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
71 /* connection format */
97 pa_hashmap *nodes; /**< nodes ie. sinks and sources */
98 pa_hashmap *conns; /**< connections */
99 routes_t defrts; /**< default routes */
103 static bool find_default_route(struct userdata *, mir_node *,
106 static void *node_hash(mir_direction, uint16_t);
107 static void *conn_hash(uint16_t);
110 struct pa_audiomgr *pa_audiomgr_init(struct userdata *u)
112 /* pa_module *m = u->module; */
117 am = pa_xnew0(pa_audiomgr, 1);
119 am->domain.id = AM_ID_INVALID;
120 am->domain.state = DS_DOWN;
121 am->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
122 pa_idxset_trivial_compare_func);
123 am->conns = pa_hashmap_new(pa_idxset_trivial_hash_func,
124 pa_idxset_trivial_compare_func);
128 void pa_audiomgr_done(struct userdata *u)
132 if (u && (am = u->audiomgr)) {
133 if (u->routerif && am->domain.id != AM_ID_INVALID)
134 pa_routerif_unregister_domain(u, am->domain.id);
136 pa_hashmap_free(am->nodes);
137 pa_hashmap_free(am->conns);
138 pa_xfree((void *)am->domain.name);
145 void pa_audiomgr_register_domain(struct userdata *u)
148 am_domainreg_data *dr;
151 pa_assert_se((am = u->audiomgr));
153 dr = pa_xnew0(am_domainreg_data, 1);
156 dr->name = AUDIOMGR_DOMAIN; /* AM domain name */
157 dr->bus_name = AUDIOMGR_NODE; /* AM internal bus name. */
158 dr->node_name = AUDIOMGR_NODE; /* node name on AM's internal bus */
160 dr->complete = false;
163 pa_routerif_register_domain(u, dr);
166 void pa_audiomgr_domain_registered(struct userdata *u,
169 am_domainreg_data *dr)
175 pa_assert_se((am = u->audiomgr));
178 am->domain.name = pa_xstrdup(dr->name);
180 am->domain.state = state;
182 pa_log_debug("start domain registration for '%s' domain", dr->name);
184 pa_discover_domain_up(u);
186 pa_log_debug("domain registration for '%s' domain is complete", dr->name);
188 pa_routerif_domain_complete(u, id);
194 void pa_audiomgr_unregister_domain(struct userdata *u, bool send_state)
202 pa_assert_se((am = u->audiomgr));
204 pa_log_debug("unregistering domain '%s'", am->domain.name);
206 while ((node = pa_hashmap_iterate(am->nodes, &state, &key))) {
207 pa_log_debug(" unregistering '%s' (%p/%p)", node->amname, key,node);
208 node->amid = AM_ID_INVALID;
209 pa_hashmap_remove(am->nodes, key);
212 am->domain.id = AM_ID_INVALID;
213 am->domain.state = DS_DOWN;
216 void fill_am_data_and_register(struct userdata *u, mir_node *node, pa_audiomgr *am)
222 rd = pa_xnew0(am_nodereg_data, 1);
223 rd->key = pa_xstrdup(node->key);
224 rd->name = pa_xstrdup(node->amname);
225 rd->domain = am->domain.id;
229 rd->visible = node->visible;
230 rd->avail.status = AS_AVAILABLE;
231 rd->avail.reason = 0;
234 if (node->direction == mir_input) {
235 rd->interrupt = IS_OFF;
236 method = audiomgr_register_source;
239 rd->mute = MS_UNMUTED;
240 method = audiomgr_register_sink;
243 success = pa_routerif_register_node(u, method, rd);
246 pa_log_debug("initiate registration node '%s' (%p)"
247 "to audio manager", rd->name, node);
250 pa_log("%s: failed to register node '%s' (%p)"
251 "to audio manager", __FILE__, rd->name, node);
257 void pa_audiomgr_register_node(struct userdata *u, mir_node *node)
259 static const char *classes_to_register[] = {
270 const char *class_to_register;
274 pa_assert_se((am = u->audiomgr));
276 if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN) {
277 pa_log_debug("skip registering nodes while the domain is down");
281 for (i = 0; (class_to_register = classes_to_register[i]); i++) {
282 if (!strcmp(node->amname, class_to_register)) {
283 if (node->direction == mir_input || node->direction == mir_output){
284 fill_am_data_and_register(u, node, am);
289 } /* for classes_to_register */
291 /* ok, register also the gateways */
292 if (strncmp(node->amname, "gw", 2) == 0) {
293 if (node->direction == mir_input || node->direction == mir_output){
294 fill_am_data_and_register(u, node, am);
299 pa_log_debug("skip registration of node '%s' (%p): "
300 "not known by audio manager", node->amname, node);
303 void pa_audiomgr_node_registered(struct userdata *u,
316 pa_assert_se((am = u->audiomgr));
318 if (!(node = pa_discover_find_node_by_key(u, rd->key)))
319 pa_log("%s: can't find node with key '%s'", __FILE__, rd->key);
323 key = node_hash(node->direction, id);
325 pa_log_debug("registering node '%s' (%p/%p)",
326 node->amname, key, node);
328 pa_hashmap_put(am->nodes, key, node);
330 /* we don't want implicit connections register and confuse */
331 /* audio manager. Implicit connections are handled by */
332 /* creating a resource through murphy */
334 if (find_default_route(u, node, &cd))
335 pa_routerif_register_implicit_connection(u, &cd);
339 pa_xfree((void *)rd->key);
340 pa_xfree((void *)rd->name);
341 pa_xfree((void *)rd);
344 void pa_audiomgr_unregister_node(struct userdata *u, mir_node *node)
347 am_nodeunreg_data *ud;
354 pa_assert_se((am = u->audiomgr));
356 if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN)
357 pa_log_debug("skip unregistering nodes while the domain is down");
358 else if (node->amid == AM_ID_INVALID)
359 pa_log_debug("node '%s' was not registered", node->amname);
360 else if (node->direction == mir_input || node->direction == mir_output) {
361 ud = pa_xnew0(am_nodeunreg_data, 1);
363 ud->name = pa_xstrdup(node->amname);
365 key = node_hash(node->direction, node->amid);
366 removed = pa_hashmap_remove(am->nodes, key);
368 if (node != removed) {
370 pa_log("%s: confused with data structures: key mismatch. "
371 "attempted to remove '%s' (%p/%p); "
372 "actually removed '%s' (%p/%p)", __FILE__,
373 node->amname, key, node, removed->amname,
374 node_hash(removed->direction, removed->amid), removed);
376 pa_log("%s: confused with data structures: node %u (%p)"
377 "is not in the hash table", __FILE__, node->amid, node);
381 if (node->direction == mir_input)
382 method = audiomgr_deregister_source;
384 method = audiomgr_deregister_sink;
387 success = pa_routerif_unregister_node(u, method, ud);
390 pa_log_debug("sucessfully unregistered node '%s' (%p/%p)"
391 "from audio manager", node->amname, key, node);
394 pa_log("%s: failed to unregister node '%s' (%p)"
395 "from audio manager", __FILE__, node->amname, node);
400 void pa_audiomgr_node_unregistered(struct userdata *u,
401 am_nodeunreg_data *ud)
405 /* can't do too much here anyways,
406 since the node is gone already */
408 pa_xfree((void *)ud->name);
409 pa_xfree((void *)ud);
413 void pa_audiomgr_delete_default_routes(struct userdata *u)
419 pa_assert_se((am = u->audiomgr));
421 defrts = &am->defrts;
426 void pa_audiomgr_add_default_route(struct userdata *u,
438 pa_assert_se((am = u->audiomgr));
440 defrts = &am->defrts;
442 if (from->paidx == PA_IDXSET_INVALID || to->paidx == PA_IDXSET_INVALID) {
443 pa_log_debug("ignoring default route %s => %s: incomplete "
444 "input or output", from->amname, to->amname);
447 pa_log_debug("adding default route %s => %s", from->amname,to->amname);
449 if (defrts->nlink >= defrts->maxlink) {
450 defrts->maxlink += 16;
452 size = sizeof(link_t) * defrts->maxlink;
453 defrts->links = realloc(defrts->links, size);
454 pa_assert(defrts->links);
457 link = defrts->links + defrts->nlink++;
459 link->fromidx = from->index;
460 link->toidx = to->index;
461 link->channels = from->channels < to->channels ?
462 from->channels : to->channels;
466 void pa_audiomgr_send_default_routes(struct userdata *u)
468 #define MAX_DEFAULT_ROUTES 128
475 am_connect_data cds[MAX_DEFAULT_ROUTES];
480 pa_assert_se((am = u->audiomgr));
482 defrts = &am->defrts;
484 pa_assert(defrts->nlink < MAX_DEFAULT_ROUTES);
486 for (ncd = 0; ncd < defrts->nlink; ncd++) {
487 link = defrts->links + ncd;
490 if (!(from = mir_node_find_by_index(u, link->fromidx)) ||
491 !(to = mir_node_find_by_index(u, link->toidx)) )
493 pa_log_debug("will not send default route: node not found");
497 if (from->amid == AM_ID_INVALID || to->amid == AM_ID_INVALID) {
498 pa_log_debug("wil not send default route: invalid audiomgr ID");
504 cd->source = from->amid;
506 cd->format = link->channels >= 2 ? CF_STEREO : CF_MONO;
509 /* we don't want implicit connections register and confuse */
510 /* audio manager. Implicit connections are handled by */
511 /* creating a resource through murphy */
514 pa_routerif_register_implicit_connections(u, ncd, cds);
517 #undef MAX_DEFAULT_ROUTES
520 void pa_audiomgr_connect(struct userdata *u, am_connect_data *cd)
524 mir_connection *conn;
526 mir_node *from = NULL;
529 bool autoconn = false;
533 pa_assert_se((am = u->audiomgr));
535 if (cd->format == CF_AUTO) {
537 pa_log_debug("automatic connection request received");
540 if (autoconn == false) {
541 if ((from = pa_hashmap_get(am->nodes, node_hash(mir_input, cd->source))) &&
542 (to = pa_hashmap_get(am->nodes, node_hash(mir_output, cd->sink))))
544 cid = cd->connection;
546 pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
548 if (!(conn = mir_router_add_explicit_route(u, cid, from, to)))
549 err = E_NOT_POSSIBLE;
551 pa_log_debug("registering connection (%u/%p)",
552 cd->connection, conn);
553 pa_hashmap_put(am->conns, conn_hash(cid), conn);
557 pa_log_debug("failed to connect: can't find node for %s %u",
558 from ? "sink" : "source", from ? cd->sink : cd->source);
559 err = E_NON_EXISTENT;
563 memset(&ad, 0, sizeof(ad));
564 ad.handle = cd->handle;
565 ad.param1 = cd->connection;
568 pa_routerif_acknowledge(u, audiomgr_connect_ack, &ad);
571 void pa_audiomgr_disconnect(struct userdata *u, am_connect_data *cd)
574 mir_connection *conn;
581 pa_assert_se((am = u->audiomgr));
583 cid = cd->connection;
585 if ((conn = pa_hashmap_remove(am->conns, conn_hash(cid))))
586 mir_router_remove_explicit_route(u, conn);
588 pa_log_debug("failed to disconnect: can't find connection %u", cid);
589 err = E_NON_EXISTENT;
592 memset(&ad, 0, sizeof(ad));
593 ad.handle = cd->handle;
594 ad.param1 = cd->connection;
597 pa_routerif_acknowledge(u, audiomgr_disconnect_ack, &ad);
600 static bool find_default_route(struct userdata *u,
613 pa_assert_se((am = u->audiomgr));
615 defrts = &am->defrts;
617 memset(cd, 0, sizeof(am_connect_data));
619 for (i = 0; i < defrts->nlink; i++) {
620 link = defrts->links + i;
622 cd->format = link->channels >= 2 ? CF_STEREO : CF_MONO;
624 if (node->direction == mir_input && link->fromidx == node->index) {
625 return (pair = mir_node_find_by_index(u, link->toidx)) &&
626 (cd->source = node->amid) != AM_ID_INVALID &&
627 (cd->sink = pair->amid) != AM_ID_INVALID;
630 if (node->direction == mir_output && link->toidx == node->index) {
631 return (pair = mir_node_find_by_index(u, link->fromidx)) &&
632 (cd->source = pair->amid) != AM_ID_INVALID &&
633 (cd->sink = node->amid) != AM_ID_INVALID;
640 static void *node_hash(mir_direction direction, uint16_t amid)
642 return NULL + ((uint32_t)direction << 16 | (uint32_t)amid);
645 static void *conn_hash(uint16_t connid)
647 return NULL + (uint32_t)connid;
655 * indent-tabs-mode: nil