2 * module-murphy-ivi -- PulseAudio module for providing audio routing support
3 * Copyright (c) 2012, Intel Corporation.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
22 #include <sys/types.h>
25 #include <pulsecore/pulsecore-config.h>
27 #include <pulse/def.h>
29 #include <pulsecore/core-util.h>
38 #define AUDIOMGR_DOMAIN "PULSE"
39 #define AUDIOMGR_NODE "pulsePlugin"
42 * these must match their counterpart
43 * in audiomanagertypes.h
47 #define DS_CONTROLLED 1
53 #define IS_INTERRUPTED 2
55 /* availability status */
56 #define AS_AVAILABLE 1
57 #define AS_UNAVAILABLE 2
59 /* availability reason */
61 #define AR_SAMEMEDIA 2
63 #define AR_TEMPERATURE 4
65 #define AR_ERRORMEDIA 6
81 pa_hashmap *nodes; /**< nodes ie. sinks and sources */
82 pa_hashmap *conns; /**< connections */
86 static void *node_hash(mir_direction, uint16_t);
87 static void *conn_hash(uint16_t);
90 struct pa_audiomgr *pa_audiomgr_init(struct userdata *u)
92 /* pa_module *m = u->module; */
97 am = pa_xnew0(pa_audiomgr, 1);
99 am->domain.id = AM_ID_INVALID;
100 am->domain.state = DS_DOWN;
101 am->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
102 pa_idxset_trivial_compare_func);
103 am->conns = pa_hashmap_new(pa_idxset_trivial_hash_func,
104 pa_idxset_trivial_compare_func);
108 void pa_audiomgr_done(struct userdata *u)
112 if (u && (am = u->audiomgr)) {
113 if (u->routerif && am->domain.id != AM_ID_INVALID)
114 pa_routerif_unregister_domain(u, am->domain.id);
116 pa_hashmap_free(am->nodes);
117 pa_hashmap_free(am->conns);
118 pa_xfree((void *)am->domain.name);
125 void pa_audiomgr_register_domain(struct userdata *u)
128 am_domainreg_data *dr;
131 pa_assert_se((am = u->audiomgr));
133 dr = pa_xnew0(am_domainreg_data, 1);
136 dr->name = AUDIOMGR_DOMAIN; /* AM domain name */
137 dr->bus_name = AUDIOMGR_NODE; /* AM internal bus name. */
138 dr->node_name = AUDIOMGR_NODE; /* node name on AM's internal bus */
140 dr->complete = false;
143 pa_routerif_register_domain(u, dr);
146 void pa_audiomgr_domain_registered(struct userdata *u,
149 am_domainreg_data *dr)
155 pa_assert_se((am = u->audiomgr));
158 am->domain.name = pa_xstrdup(dr->name);
160 am->domain.state = state;
162 pa_log_debug("start domain registration for '%s' domain", dr->name);
164 pa_discover_domain_up(u);
166 pa_log_debug("domain registration for '%s' domain is complete", dr->name);
168 pa_routerif_domain_complete(u, id);
174 void pa_audiomgr_unregister_domain(struct userdata *u, bool send_state)
182 pa_assert_se((am = u->audiomgr));
184 pa_log_debug("unregistering domain '%s'", am->domain.name);
186 while ((node = pa_hashmap_iterate(am->nodes, &state, &key))) {
187 pa_log_debug(" unregistering '%s' (%p/%p)", node->amname, key,node);
188 node->amid = AM_ID_INVALID;
189 pa_hashmap_remove(am->nodes, key);
192 am->domain.id = AM_ID_INVALID;
193 am->domain.state = DS_DOWN;
197 void pa_audiomgr_register_node(struct userdata *u, mir_node *node)
205 pa_assert_se((am = u->audiomgr));
207 if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN)
208 pa_log_debug("skip registering nodes while the domain is down");
210 if (node->direction == mir_input || node->direction == mir_output) {
211 rd = pa_xnew0(am_nodereg_data, 1);
212 rd->key = pa_xstrdup(node->key);
213 rd->name = pa_xstrdup(node->amname);
214 rd->domain = am->domain.id;
217 rd->visible = node->visible;
218 rd->avail.status = AS_AVAILABLE;
219 rd->avail.reason = 0;
222 if (node->direction == mir_input) {
223 rd->interrupt = IS_OFF;
224 method = audiomgr_register_source;
227 rd->mute = MS_UNMUTED;
228 method = audiomgr_register_sink;
231 success = pa_routerif_register_node(u, method, rd);
234 pa_log_debug("initiate registration node '%s' (%p)"
235 "to audio manager", rd->name, node);
238 pa_log("%s: failed to register node '%s' (%p)"
239 "to audio manager", __FILE__, rd->name, node);
245 void pa_audiomgr_node_registered(struct userdata *u,
257 pa_assert_se((am = u->audiomgr));
259 if (!(node = pa_discover_find_node_by_key(u, rd->key)))
260 pa_log("%s: can't find node with key '%s'", __FILE__, rd->key);
264 key = node_hash(node->direction, id);
266 pa_log_debug("registering node '%s' (%p/%p)",
267 node->amname, key, node);
269 pa_hashmap_put(am->nodes, key, node);
272 pa_xfree((void *)rd->key);
273 pa_xfree((void *)rd->name);
274 pa_xfree((void *)rd);
277 void pa_audiomgr_unregister_node(struct userdata *u, mir_node *node)
280 am_nodeunreg_data *ud;
287 pa_assert_se((am = u->audiomgr));
289 if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN)
290 pa_log_debug("skip unregistering nodes while the domain is down");
291 else if (node->amid == AM_ID_INVALID)
292 pa_log_debug("node '%s' was not registered", node->amname);
293 else if (node->direction == mir_input || node->direction == mir_output) {
294 ud = pa_xnew0(am_nodeunreg_data, 1);
296 ud->name = pa_xstrdup(node->amname);
298 key = node_hash(node->direction, node->amid);
299 removed = pa_hashmap_remove(am->nodes, key);
301 if (node != removed) {
303 pa_log("%s: confused with data structures: key mismatch. "
304 "attempted to remove '%s' (%p/%p); "
305 "actually removed '%s' (%p/%p)", __FILE__,
306 node->amname, key, node, removed->amname,
307 node_hash(removed->direction, removed->amid), removed);
309 pa_log("%s: confused with data structures: node %u (%p)"
310 "is not in the hash table", __FILE__, node->amid, node);
314 if (node->direction == mir_input)
315 method = audiomgr_deregister_source;
317 method = audiomgr_deregister_sink;
320 success = pa_routerif_unregister_node(u, method, ud);
323 pa_log_debug("sucessfully unregistered node '%s' (%p/%p)"
324 "from audio manager", node->amname, key, node);
327 pa_log("%s: failed to unregister node '%s' (%p)"
328 "from audio manager", __FILE__, node->amname, node);
333 void pa_audiomgr_node_unregistered(struct userdata *u,
334 am_nodeunreg_data *ud)
338 /* can't do too much here anyways,
339 since the node is gone already */
341 pa_xfree((void *)ud->name);
342 pa_xfree((void *)ud);
346 void pa_audiomgr_connect(struct userdata *u, am_connect_data *cd)
350 mir_connection *conn;
352 mir_node *from = NULL;
358 pa_assert_se((am = u->audiomgr));
360 if ((from = pa_hashmap_get(am->nodes, node_hash(mir_input, cd->source))) &&
361 (to = pa_hashmap_get(am->nodes, node_hash(mir_output, cd->sink))))
363 cid = cd->connection;
365 pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
367 if (!(conn = mir_router_add_explicit_route(u, cid, from, to)))
368 err = E_NOT_POSSIBLE;
370 pa_log_debug("registering connection (%u/%p)",
371 cd->connection, conn);
372 pa_hashmap_put(am->conns, conn_hash(cid), conn);
376 pa_log_debug("failed to connect: can't find node for %s %u",
377 from ? "sink" : "source", from ? cd->sink : cd->source);
378 err = E_NON_EXISTENT;
381 memset(&ad, 0, sizeof(ad));
382 ad.handle = cd->handle;
383 ad.param1 = cd->connection;
386 pa_routerif_acknowledge(u, audiomgr_connect_ack, &ad);
389 void pa_audiomgr_disconnect(struct userdata *u, am_connect_data *cd)
392 mir_connection *conn;
399 pa_assert_se((am = u->audiomgr));
401 cid = cd->connection;
403 if ((conn = pa_hashmap_remove(am->conns, conn_hash(cid))))
404 mir_router_remove_explicit_route(u, conn);
406 pa_log_debug("failed to disconnect: can't find connection %u", cid);
407 err = E_NON_EXISTENT;
410 memset(&ad, 0, sizeof(ad));
411 ad.handle = cd->handle;
412 ad.param1 = cd->connection;
415 pa_routerif_acknowledge(u, audiomgr_disconnect, &ad);
418 static void *node_hash(mir_direction direction, uint16_t amid)
420 return NULL + ((uint32_t)direction << 16 | (uint32_t)amid);
423 static void *conn_hash(uint16_t connid)
425 return NULL + (uint32_t)connid;
433 * indent-tabs-mode: nil