Map labels to classes, map routing groups to audio adapters
[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_compare (struct userdata *u, agl_rtgroup *rtg, agl_node *n1, agl_node *n2)
101 {
102         /* TODO */
103         return 1;
104 }
105
106 int agl_router_phone_compare (struct userdata *u, agl_rtgroup *rtg, agl_node *n1, agl_node *n2)
107 {
108         /* TODO */
109         return 1;
110 }
111
112 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_compare_t compare)
113 {
114         agl_router *router;
115         agl_rtgroup *rtg;
116         agl_nodeset *nodeset;
117         agl_node *node;
118         pa_hashmap *table;
119
120         pa_assert (u);
121         pa_assert (type == agl_input || type == agl_output);
122         pa_assert (name);
123         pa_assert_se (router = u->router);
124         pa_assert_se (nodeset = u->nodeset);
125
126         if (type == agl_input)
127                 table = router->rtgroups.input;
128         else
129                 table = router->rtgroups.output;
130         pa_assert (table);
131
132         rtg = pa_xnew0 (agl_rtgroup, 1);
133         rtg->name = pa_xstrdup (name);
134         rtg->accept = accept;
135         rtg->compare = compare;
136         AGL_DLIST_INIT(rtg->entries);
137         /* associate an agl_output node for an agl_input routing group */
138         if (type == agl_input) {
139                 node = agl_node_create (u, NULL);
140                 node->direction = agl_output;
141                 node->implement = agl_device;
142                 node->visible = true;
143                 node->available = true;
144                 node->paname = pa_xstrdup (node_desc);
145                  /* add to global nodeset */
146                 pa_idxset_put (nodeset->nodes, node, &node->index);
147                 rtg->node = node;
148         } else {
149                 rtg->node = NULL;
150         }
151         
152
153         pa_hashmap_put (table, rtg->name, rtg);
154
155         pa_log_debug ("routing group '%s' created", name);
156
157         return rtg;
158 }
159
160 void agl_router_destroy_rtgroup (struct userdata *u, agl_direction type, const char *name)
161 {
162         agl_router *router;
163         agl_rtgroup *rtg;
164         pa_hashmap *table;
165
166         pa_assert (u);
167         pa_assert (name);
168         pa_assert_se (router = u->router);
169
170         if (type == agl_input)
171                 table = router->rtgroups.input;
172         else
173                 table = router->rtgroups.output;
174         pa_assert (table);
175
176         rtg = pa_hashmap_remove (table, name);
177         if (!rtg) {
178                 pa_log_debug ("can't destroy routing group '%s': not found", name);
179         } else {
180                 //rtgroup_destroy (u, rtg);
181                 pa_log_debug ("routing group '%s' destroyed", name);
182         }
183 }
184
185 bool agl_router_assign_class_to_rtgroup (struct userdata *u, agl_node_type class, uint32_t zone, agl_direction type, const char *name)
186 {
187         agl_router *router;
188         pa_hashmap *rtable;
189         agl_rtgroup ***classmap;
190         agl_rtgroup **zonemap;
191         const char *classname;
192         const char *direction;
193         agl_rtgroup *rtg;
194         agl_zone *rzone;
195
196         pa_assert (u);
197         pa_assert (zone < AGL_ZONE_MAX);        
198         pa_assert (type == agl_input || type == agl_output);
199         pa_assert (name);
200         pa_assert_se (router = u->router);
201
202         if (type == agl_input) {
203                 rtable = router->rtgroups.input;
204                 classmap = router->classmap.input;
205         } else {
206                 rtable = router->rtgroups.output;
207                 classmap = router->classmap.output;
208         }
209
210         if (class < 0 || class >= router->maplen) {
211                 pa_log_debug ("Cannot assign class to routing group '%s': "
212                               "id %d out of range (0 - %d)",
213                               name, class, router->maplen);
214                 return false;
215         }
216
217         classname = agl_node_type_str (class); /* "Player", "Radio"... */
218         direction = agl_node_direction_str (type); /* "input", "output" */
219
220         rtg = pa_hashmap_get (rtable, name);
221         if (!rtg) {
222                 pa_log_debug ("Cannot assign class to routing group '%s': "
223                               "router group not found", name);
224                 return false;
225         }
226
227         zonemap = classmap[zone];
228         if (!zonemap) { /* THIS LOOKS LIKE A HACK TO IGNORE THE ERROR... */
229                 zonemap = pa_xnew0 (agl_rtgroup *, router->maplen);
230                 classmap[zone] = zonemap;
231         }
232
233         zonemap[class] = rtg;
234
235          /* try to get zone name for logging, if fails, only print id number */
236         rzone = agl_zoneset_get_zone_by_index (u, zone);
237         if (rzone) {
238                 pa_log_debug ("class '%s'@'%s' assigned to routing group '%s'",
239                               classname, rzone->name, name); 
240         } else {
241                 pa_log_debug ("class '%s'@zone%d assigned to routing group '%s'",
242                               classname, zone, name);
243         }
244
245         return true;
246 }
247
248 void agl_router_assign_class_priority (struct userdata *u, agl_node_type class, int priority)
249 {
250         agl_router *router;
251         int *priormap;
252
253         pa_assert (u);
254         pa_assert_se (router = u->router);
255         pa_assert_se (priormap = router->priormap);
256
257         if (class > 0 && class < router->maplen) {
258                 pa_log_debug ("assigning priority %d to class '%s'",
259                               priority, agl_node_type_str (class));
260                 priormap[class] = priority;
261         }
262 }
263
264 void agl_router_register_node (struct userdata *u, agl_node *node)
265 {
266         agl_router *router;
267         agl_rtgroup *rtg;
268
269         pa_assert (u);
270         pa_assert (node);
271         pa_assert_se (router = u->router);
272
273         /* we try to discover node routing group from the configuration, "Phone" for instance,
274          * see defaults in "config.c. Otherwise we just say NULL, a.k.a. default */
275         if (node->direction == agl_input) {
276                 rtg = pa_hashmap_get (router->rtgroups.input, agl_node_type_str (node->type));
277                 if (rtg)
278                         implement_default_route (u, node, rtg->node, agl_utils_new_stamp ());
279                 else
280                         implement_default_route (u, node, NULL, agl_utils_new_stamp ());
281         } else {
282                 rtg = pa_hashmap_get (router->rtgroups.output, agl_node_type_str (node->type));
283                 if (rtg)
284                         implement_default_route (u, rtg->node, node, agl_utils_new_stamp ());
285                 else
286                         implement_default_route (u, NULL, node, agl_utils_new_stamp ());
287         }
288 }
289
290 void agl_router_unregister_node (struct userdata *u, agl_node *node)
291 {
292         pa_assert (u);
293         pa_assert (node);
294
295         remove_routes (u, node, NULL, agl_utils_new_stamp ());
296 }
297
298 agl_node *agl_router_make_prerouting (struct userdata *u, agl_node *data)
299 {
300         agl_router *router;
301         int priority;
302         static bool done_prerouting;
303         uint32_t stamp;
304         agl_node *start, *end;
305         agl_node *target;
306
307         pa_assert (u);
308         pa_assert_se (router = u->router);
309         pa_assert_se (data->implement == agl_stream);
310
311         //priority = node_priority (u, data);
312
313         done_prerouting = false;
314         target = NULL;
315         stamp = agl_utils_new_stamp ();
316
317         //make_explicit_routes (u, stamp);
318
319         //pa_audiomgr_delete_default_routes(u);
320
321         AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) {
322                 //if ((start->implement == agl_device) &&
323                 //    (!start->loop))   /* only manage looped real devices */
324                 //      continue;
325
326                 /*if (priority >= node_priority (u, start)) {
327                         target = find_default_route (u, data, stamp);
328                         if (target)
329                                 implement_preroute (u, data, target, stamp);
330                         else
331                                 done_prerouting = true;
332                 }*/
333
334                 if (start->stamp >= stamp)
335                         continue;
336
337                 //end = find_default_route (u, start, stamp);
338                 //if (end)
339                 //      implement_default_route(u, start, end, stamp);
340         }
341
342         if (!done_prerouting) {
343                 pa_log_debug ("Prerouting failed, trying to find default route as last resort");
344
345                 //target = find_default_route (u, data, stamp);
346                 //if (target)
347                 //      implement_preroute (u, data, target, stamp);
348         }
349
350         return target;
351 }
352
353 void agl_router_make_routing (struct userdata *u)
354 {
355         agl_router *router;
356         static bool ongoing_routing;    /* true while we are actively routing */
357         uint32_t stamp;
358         agl_node *start, *end;
359
360         pa_assert (u);
361         pa_assert_se (router = u->router);
362
363         if (ongoing_routing)            /* already routing, canceling */
364                 return;
365         ongoing_routing = true;
366         stamp = agl_utils_new_stamp ();
367
368         pa_log_debug("stamp for routing: %d", stamp);
369
370         // make_explicit_routes (u, stamp);
371
372         // pa_audiomgr_delete_default_routes (u);
373
374         AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) {
375                 //if ((start->implement == agl_device) &&
376                 //   (!start->loop))    /* only manage looped real devices */
377                 //      continue;
378
379                 if (start->stamp >= stamp)
380                         continue;
381
382                 end = find_default_route (u, start, stamp);
383                 if (end)
384                         implement_default_route (u, start, end, stamp);
385         }
386
387         // pa_audiomgr_send_default_routes (u);
388
389         ongoing_routing = false;
390 }
391
392 void implement_default_route (struct userdata *u,
393                               agl_node *start, agl_node *end,
394                               uint32_t stamp)
395 {
396         if (start->direction == agl_input) {
397                 agl_switch_setup_link (u, start, end, false);
398                 //agl_volume_add_limiting_class(u, end, volume_class(start), stamp);
399         } else {
400                 agl_switch_setup_link (u, end, start, false);
401         }
402 }
403
404 agl_node *find_default_route (struct userdata *u, agl_node *start, uint32_t stamp)
405 {
406         /* TODO */
407
408         return NULL;
409 }
410
411 void remove_routes (struct userdata *u, agl_node *start, agl_node *end, uint32_t stamp)
412 {
413         if (start->direction == agl_input) {
414                 agl_switch_teardown_link (u, start, end);
415         } else {
416                 agl_switch_teardown_link (u, end, start);
417         }
418 }