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
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.
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.
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,
27 agl_router *agl_router_init (struct userdata *u)
32 num_classes = agl_application_class_end;
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);
42 AGL_DLIST_INIT (router->nodlist);
43 AGL_DLIST_INIT (router->connlist);
48 void agl_router_done (struct userdata *u)
52 agl_connection *conn, *c;
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);
65 PA_HASHMAP_FOREACH(rtg, router->rtgroups.input, state) {
66 rtgroup_destroy(u, rtg);
68 PA_HASHMAP_FOREACH(rtg, router->rtgroups.output, state) {
69 rtgroup_destroy(u, rtg);
71 pa_hashmap_free (router->rtgroups.input);
72 pa_hashmap_free (router->rtgroups.output);
74 for (i = 0; i < AGL_ZONE_MAX; i++) {
75 if ((map = router->classmap.input[i]))
77 if ((map = router->classmap.output[i]))
81 pa_xfree (router->priormap);
88 bool agl_router_default_accept (struct userdata *u, agl_rtgroup *rtg, agl_node *node)
94 bool agl_router_phone_accept (struct userdata *u, agl_rtgroup *rtg, agl_node *node)
100 int agl_router_default_compare (struct userdata *u, agl_rtgroup *rtg, agl_node *n1, agl_node *n2)
106 int agl_router_phone_compare (struct userdata *u, agl_rtgroup *rtg, agl_node *n1, agl_node *n2)
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)
116 agl_nodeset *nodeset;
121 pa_assert (type == agl_input || type == agl_output);
123 pa_assert_se (router = u->router);
124 pa_assert_se (nodeset = u->nodeset);
126 if (type == agl_input)
127 table = router->rtgroups.input;
129 table = router->rtgroups.output;
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);
153 pa_hashmap_put (table, rtg->name, rtg);
155 pa_log_debug ("routing group '%s' created", name);
160 void agl_router_destroy_rtgroup (struct userdata *u, agl_direction type, const char *name)
168 pa_assert_se (router = u->router);
170 if (type == agl_input)
171 table = router->rtgroups.input;
173 table = router->rtgroups.output;
176 rtg = pa_hashmap_remove (table, name);
178 pa_log_debug ("can't destroy routing group '%s': not found", name);
180 //rtgroup_destroy (u, rtg);
181 pa_log_debug ("routing group '%s' destroyed", name);
185 bool agl_router_assign_class_to_rtgroup (struct userdata *u, agl_node_type class, uint32_t zone, agl_direction type, const char *name)
189 agl_rtgroup ***classmap;
190 agl_rtgroup **zonemap;
191 const char *classname;
192 const char *direction;
197 pa_assert (zone < AGL_ZONE_MAX);
198 pa_assert (type == agl_input || type == agl_output);
200 pa_assert_se (router = u->router);
202 if (type == agl_input) {
203 rtable = router->rtgroups.input;
204 classmap = router->classmap.input;
206 rtable = router->rtgroups.output;
207 classmap = router->classmap.output;
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);
217 classname = agl_node_type_str (class); /* "Player", "Radio"... */
218 direction = agl_node_direction_str (type); /* "input", "output" */
220 rtg = pa_hashmap_get (rtable, name);
222 pa_log_debug ("Cannot assign class to routing group '%s': "
223 "router group not found", name);
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;
233 zonemap[class] = rtg;
235 /* try to get zone name for logging, if fails, only print id number */
236 rzone = agl_zoneset_get_zone_by_index (u, zone);
238 pa_log_debug ("class '%s'@'%s' assigned to routing group '%s'",
239 classname, rzone->name, name);
241 pa_log_debug ("class '%s'@zone%d assigned to routing group '%s'",
242 classname, zone, name);
248 void agl_router_assign_class_priority (struct userdata *u, agl_node_type class, int priority)
254 pa_assert_se (router = u->router);
255 pa_assert_se (priormap = router->priormap);
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;
264 void agl_router_register_node (struct userdata *u, agl_node *node)
271 pa_assert_se (router = u->router);
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));
278 implement_default_route (u, node, rtg->node, agl_utils_new_stamp ());
280 implement_default_route (u, node, NULL, agl_utils_new_stamp ());
282 rtg = pa_hashmap_get (router->rtgroups.output, agl_node_type_str (node->type));
284 implement_default_route (u, rtg->node, node, agl_utils_new_stamp ());
286 implement_default_route (u, NULL, node, agl_utils_new_stamp ());
290 void agl_router_unregister_node (struct userdata *u, agl_node *node)
295 remove_routes (u, node, NULL, agl_utils_new_stamp ());
298 agl_node *agl_router_make_prerouting (struct userdata *u, agl_node *data)
302 static bool done_prerouting;
304 agl_node *start, *end;
308 pa_assert_se (router = u->router);
309 pa_assert_se (data->implement == agl_stream);
311 //priority = node_priority (u, data);
313 done_prerouting = false;
315 stamp = agl_utils_new_stamp ();
317 //make_explicit_routes (u, stamp);
319 //pa_audiomgr_delete_default_routes(u);
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 */
326 /*if (priority >= node_priority (u, start)) {
327 target = find_default_route (u, data, stamp);
329 implement_preroute (u, data, target, stamp);
331 done_prerouting = true;
334 if (start->stamp >= stamp)
337 //end = find_default_route (u, start, stamp);
339 // implement_default_route(u, start, end, stamp);
342 if (!done_prerouting) {
343 pa_log_debug ("Prerouting failed, trying to find default route as last resort");
345 //target = find_default_route (u, data, stamp);
347 // implement_preroute (u, data, target, stamp);
353 void agl_router_make_routing (struct userdata *u)
356 static bool ongoing_routing; /* true while we are actively routing */
358 agl_node *start, *end;
361 pa_assert_se (router = u->router);
363 if (ongoing_routing) /* already routing, canceling */
365 ongoing_routing = true;
366 stamp = agl_utils_new_stamp ();
368 pa_log_debug("stamp for routing: %d", stamp);
370 // make_explicit_routes (u, stamp);
372 // pa_audiomgr_delete_default_routes (u);
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 */
379 if (start->stamp >= stamp)
382 end = find_default_route (u, start, stamp);
384 implement_default_route (u, start, end, stamp);
387 // pa_audiomgr_send_default_routes (u);
389 ongoing_routing = false;
392 void implement_default_route (struct userdata *u,
393 agl_node *start, agl_node *end,
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);
400 agl_switch_setup_link (u, end, start, false);
404 agl_node *find_default_route (struct userdata *u, agl_node *start, uint32_t stamp)
411 void remove_routes (struct userdata *u, agl_node *start, agl_node *end, uint32_t stamp)
413 if (start->direction == agl_input) {
414 agl_switch_teardown_link (u, start, end);
416 agl_switch_teardown_link (u, end, start);