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 */
104 static bool find_default_route(struct userdata *, mir_node *,
108 static void *node_hash(mir_direction, uint16_t);
109 static void *conn_hash(uint16_t);
110 static void fill_am_data_and_register(struct userdata *, mir_node *, pa_audiomgr *);
112 struct pa_audiomgr *pa_audiomgr_init(struct userdata *u)
114 /* pa_module *m = u->module; */
119 am = pa_xnew0(pa_audiomgr, 1);
121 am->domain.id = AM_ID_INVALID;
122 am->domain.state = DS_DOWN;
123 am->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
124 pa_idxset_trivial_compare_func);
125 am->conns = pa_hashmap_new(pa_idxset_trivial_hash_func,
126 pa_idxset_trivial_compare_func);
130 void pa_audiomgr_done(struct userdata *u)
134 if (u && (am = u->audiomgr)) {
135 if (u->routerif && am->domain.id != AM_ID_INVALID)
136 pa_routerif_unregister_domain(u, am->domain.id);
138 pa_hashmap_free(am->nodes);
139 pa_hashmap_free(am->conns);
140 pa_xfree((void *)am->domain.name);
147 void pa_audiomgr_register_domain(struct userdata *u)
150 am_domainreg_data *dr;
153 pa_assert_se((am = u->audiomgr));
155 dr = pa_xnew0(am_domainreg_data, 1);
158 dr->name = AUDIOMGR_DOMAIN; /* AM domain name */
159 dr->bus_name = AUDIOMGR_NODE; /* AM internal bus name. */
160 dr->node_name = AUDIOMGR_NODE; /* node name on AM's internal bus */
162 dr->complete = false;
165 pa_routerif_register_domain(u, dr);
168 void pa_audiomgr_domain_registered(struct userdata *u,
171 am_domainreg_data *dr)
177 pa_assert_se((am = u->audiomgr));
180 am->domain.name = pa_xstrdup(dr->name);
182 am->domain.state = state;
184 pa_log_debug("start domain registration for '%s' domain", dr->name);
186 pa_discover_domain_up(u);
188 pa_log_debug("domain registration for '%s' domain is complete", dr->name);
190 pa_routerif_domain_complete(u, id);
196 void pa_audiomgr_unregister_domain(struct userdata *u, bool send_state)
204 pa_assert_se((am = u->audiomgr));
206 pa_log_debug("unregistering domain '%s'", am->domain.name);
208 while ((node = pa_hashmap_iterate(am->nodes, &state, &key))) {
209 pa_log_debug(" unregistering '%s' (%p/%p)", node->amname, (void *)key, (void *)node);
210 node->amid = AM_ID_INVALID;
211 pa_hashmap_remove(am->nodes, key);
214 am->domain.id = AM_ID_INVALID;
215 am->domain.state = DS_DOWN;
218 void fill_am_data_and_register(struct userdata *u, mir_node *node, pa_audiomgr *am)
224 rd = pa_xnew0(am_nodereg_data, 1);
225 rd->key = pa_xstrdup(node->key);
226 rd->name = pa_xstrdup(node->amname);
227 rd->domain = am->domain.id;
231 rd->visible = node->visible;
232 rd->avail.status = AS_AVAILABLE;
233 rd->avail.reason = 0;
236 if (node->direction == mir_input) {
237 rd->interrupt = IS_OFF;
238 method = audiomgr_register_source;
241 rd->mute = MS_UNMUTED;
242 method = audiomgr_register_sink;
245 success = pa_routerif_register_node(u, method, rd);
248 pa_log_debug("initiate registration node '%s' (%p)"
249 "to audio manager", rd->name, (void *)node);
252 pa_log("%s: failed to register node '%s' (%p)"
253 "to audio manager", __FILE__, rd->name, (void *)node);
259 void pa_audiomgr_register_node(struct userdata *u, mir_node *node)
261 static const char *classes_to_register[] = {
271 const char *class_to_register;
275 pa_assert_se((am = u->audiomgr));
277 if (am->domain.state == DS_DOWN || am->domain.state == DS_RUNDOWN) {
278 pa_log_debug("skip registering nodes while the domain is down");
282 for (i = 0; (class_to_register = classes_to_register[i]); i++) {
283 if (!strcmp(node->amname, class_to_register)) {
284 if (node->direction == mir_input || node->direction == mir_output){
285 fill_am_data_and_register(u, node, am);
290 } /* for classes_to_register */
292 /* ok, register also the gateways */
293 if (strncmp(node->amname, "gw", 2) == 0) {
294 if (node->direction == mir_input || node->direction == mir_output){
295 fill_am_data_and_register(u, node, am);
300 pa_log_debug("skip registration of node '%s' (%p): "
301 "not known by audio manager", node->amname, (void *)node);
304 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, (void *)key, (void *)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, (void *)key, (void *)node, removed->amname,
374 (void *)node_hash(removed->direction, removed->amid),
377 pa_log("%s: confused with data structures: node %u (%p)"
378 "is not in the hash table", __FILE__, node->amid, (void *)node);
382 if (node->direction == mir_input)
383 method = audiomgr_deregister_source;
385 method = audiomgr_deregister_sink;
388 success = pa_routerif_unregister_node(u, method, ud);
391 pa_log_debug("sucessfully unregistered node '%s' (%p/%p)"
392 "from audio manager", node->amname,
393 (void *)key, (void *)node);
396 pa_log("%s: failed to unregister node '%s' (%p)"
397 "from audio manager", __FILE__, node->amname, (void *)node);
402 void pa_audiomgr_node_unregistered(struct userdata *u,
403 am_nodeunreg_data *ud)
407 /* can't do too much here anyways,
408 since the node is gone already */
410 pa_xfree((void *)ud->name);
411 pa_xfree((void *)ud);
415 void pa_audiomgr_delete_default_routes(struct userdata *u)
421 pa_assert_se((am = u->audiomgr));
423 defrts = &am->defrts;
428 void pa_audiomgr_add_default_route(struct userdata *u,
440 pa_assert_se((am = u->audiomgr));
442 defrts = &am->defrts;
444 if (from->paidx == PA_IDXSET_INVALID || to->paidx == PA_IDXSET_INVALID) {
445 pa_log_debug("ignoring default route %s => %s: incomplete "
446 "input or output", from->amname, to->amname);
449 pa_log_debug("adding default route %s => %s", from->amname,to->amname);
451 if (defrts->nlink >= defrts->maxlink) {
452 defrts->maxlink += 16;
454 size = sizeof(link_t) * (size_t)defrts->maxlink;
455 defrts->links = realloc(defrts->links, size);
456 pa_assert(defrts->links);
459 link = defrts->links + defrts->nlink++;
461 link->fromidx = (uint16_t)from->index;
462 link->toidx = (uint16_t)to->index;
463 link->channels = from->channels < to->channels ?
464 from->channels : to->channels;
468 void pa_audiomgr_send_default_routes(struct userdata *u)
470 #define MAX_DEFAULT_ROUTES 128
477 am_connect_data cds[MAX_DEFAULT_ROUTES];
482 pa_assert_se((am = u->audiomgr));
484 defrts = &am->defrts;
486 pa_assert(defrts->nlink < MAX_DEFAULT_ROUTES);
488 for (ncd = 0; ncd < defrts->nlink; ncd++) {
489 link = defrts->links + ncd;
492 if (!(from = mir_node_find_by_index(u, link->fromidx)) ||
493 !(to = mir_node_find_by_index(u, link->toidx)) )
495 pa_log_debug("will not send default route: node not found");
499 if (from->amid == AM_ID_INVALID || to->amid == AM_ID_INVALID) {
500 pa_log_debug("wil not send default route: invalid audiomgr ID");
506 cd->source = from->amid;
508 cd->format = link->channels >= 2 ? CF_STEREO : CF_MONO;
511 /* we don't want implicit connections register and confuse */
512 /* audio manager. Implicit connections are handled by */
513 /* creating a resource through murphy */
516 pa_routerif_register_implicit_connections(u, ncd, cds);
519 #undef MAX_DEFAULT_ROUTES
522 void pa_audiomgr_connect(struct userdata *u, am_connect_data *cd)
526 mir_connection *conn;
528 mir_node *from = NULL;
531 bool autoconn = false;
535 pa_assert_se((am = u->audiomgr));
537 if (cd->format == CF_AUTO) {
539 pa_log_debug("automatic connection request received");
542 if (autoconn == false) {
543 if ((from = pa_hashmap_get(am->nodes, node_hash(mir_input, cd->source))) &&
544 (to = pa_hashmap_get(am->nodes, node_hash(mir_output, cd->sink))))
546 cid = cd->connection;
548 pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
550 if (!(conn = mir_router_add_explicit_route(u, cid, from, to)))
551 err = E_NOT_POSSIBLE;
553 pa_log_debug("registering connection (%u/%p)",
554 cd->connection, (void *)conn);
555 pa_hashmap_put(am->conns, conn_hash(cid), conn);
559 pa_log_debug("failed to connect: can't find node for %s %u",
560 from ? "sink" : "source", from ? cd->sink : cd->source);
561 err = E_NON_EXISTENT;
565 memset(&ad, 0, sizeof(ad));
566 ad.handle = cd->handle;
567 ad.param1 = cd->connection;
568 ad.error = (am_uint16_t)err;
570 pa_routerif_acknowledge(u, audiomgr_connect_ack, &ad);
573 void pa_audiomgr_disconnect(struct userdata *u, am_connect_data *cd)
576 mir_connection *conn;
583 pa_assert_se((am = u->audiomgr));
585 cid = cd->connection;
587 if ((conn = pa_hashmap_remove(am->conns, conn_hash(cid))))
588 mir_router_remove_explicit_route(u, conn);
590 pa_log_debug("failed to disconnect: can't find connection %u", cid);
591 err = E_NON_EXISTENT;
594 memset(&ad, 0, sizeof(ad));
595 ad.handle = cd->handle;
596 ad.param1 = cd->connection;
597 ad.error = (am_uint16_t)err;
599 pa_routerif_acknowledge(u, audiomgr_disconnect_ack, &ad);
603 static bool find_default_route(struct userdata *u,
616 pa_assert_se((am = u->audiomgr));
618 defrts = &am->defrts;
620 memset(cd, 0, sizeof(am_connect_data));
622 for (i = 0; i < defrts->nlink; i++) {
623 link = defrts->links + i;
625 cd->format = link->channels >= 2 ? CF_STEREO : CF_MONO;
627 if (node->direction == mir_input && link->fromidx == node->index) {
628 return (pair = mir_node_find_by_index(u, link->toidx)) &&
629 (cd->source = node->amid) != AM_ID_INVALID &&
630 (cd->sink = pair->amid) != AM_ID_INVALID;
633 if (node->direction == mir_output && link->toidx == node->index) {
634 return (pair = mir_node_find_by_index(u, link->fromidx)) &&
635 (cd->source = pair->amid) != AM_ID_INVALID &&
636 (cd->sink = node->amid) != AM_ID_INVALID;
644 static void *node_hash(mir_direction direction, uint16_t amid)
646 return (char *)NULL + ((uint32_t)direction << 16 | (uint32_t)amid);
649 static void *conn_hash(uint16_t connid)
651 return (char *)NULL + (uint32_t)connid;
659 * indent-tabs-mode: nil