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,
23 #include <pulsecore/pulsecore-config.h>
25 #include <pulsecore/hashmap.h>
26 #include <pulsecore/idxset.h>
27 #include <pulsecore/client.h>
28 #include <pulsecore/core-util.h>
29 #include <pulsecore/log.h>
30 #include <pulsecore/card.h>
31 #include <pulsecore/device-port.h>
32 #include <pulsecore/sink-input.h>
33 #include <pulsecore/source-output.h>
34 #include <pulsecore/strbuf.h>
41 #include "constrain.h"
42 #include "multiplex.h"
48 #include "stream-state.h"
51 #define MAX_CARD_TARGET 4
52 #define MAX_NAME_LENGTH 256
54 #define ACTIVE_PORT NULL
56 /* Bluetooth service class */
57 #define BIT(x) (1U << (x))
59 #define BT_SERVICE_MASK 0xffe
60 #define BT_SERVICE_INFORMATION BIT(23) /**< WEB-server, WAP-server, etc */
61 #define BT_SERVICE_TELEPHONY BIT(22) /**< Modem, Headset, etc*/
62 #define BT_SERVICE_AUDIO BIT(21) /**< Speaker, Microphone, Headset */
63 #define BT_SERVICE_OBJECT_XFER BIT(20) /**< v-Inbox, v-Folder, etc */
64 #define BT_SERVICE_CAPTURING BIT(19) /**< Scanner, Microphone, etc */
65 #define BT_SERVICE_RENDERING BIT(18) /**< Printing, Speaker, etc */
66 #define BT_SERVICE_NETWORKING BIT(17) /**< LAN, Ad hoc, etc */
67 #define BT_SERVICE_POSITIONING BIT(16) /**< Location identification */
86 static const char combine_pattern[] = "Simultaneous output on ";
87 static const char loopback_outpatrn[] = "Loopback from ";
88 static const char loopback_inpatrn[] = "Loopback to ";
90 static void handle_alsa_card(struct userdata *, pa_card *);
91 static void handle_bluetooth_card(struct userdata *, pa_card *);
92 static bool get_bluetooth_port_availability(mir_node *, pa_device_port *);
94 static void handle_udev_loaded_card(struct userdata *, pa_card *,
96 static void handle_card_ports(struct userdata *, mir_node *,
97 pa_card *, pa_card_profile *);
99 static mir_node *create_node(struct userdata *, mir_node *, bool *);
100 static void destroy_node(struct userdata *, mir_node *);
101 static bool update_node_availability(struct userdata *, mir_node *,
103 static bool update_node_availability_by_device(struct userdata *,
105 void *, pa_device_port *,
108 static void parse_profile_name(pa_card_profile *,
109 char **, char **, char *, int);
111 static char *node_key(struct userdata *, mir_direction,
112 void *, pa_device_port *, char *, size_t);
114 static pa_sink *make_output_prerouting(struct userdata *, mir_node *,
115 pa_channel_map *, const char *,
117 static pa_source *make_input_prerouting(struct userdata *, mir_node *,
118 const char *, mir_node **);
120 static mir_node_type get_stream_routing_class(pa_proplist *);
122 static void set_bluetooth_profile(struct userdata *, pa_card *, pa_direction_t);
125 static void schedule_deferred_routing(struct userdata *);
126 static void schedule_card_check(struct userdata *, pa_card *);
127 static void schedule_source_cleanup(struct userdata *, mir_node *);
129 static void schedule_stream_uncorking(struct userdata *, pa_sink *);
132 static void pa_hashmap_node_free(void *node, void *u)
134 mir_node_destroy(u, node);
138 struct pa_discover *pa_discover_init(struct userdata *u)
140 pa_discover *discover = pa_xnew0(pa_discover, 1);
144 discover->selected = true;
146 discover->nodes.byname = pa_hashmap_new(pa_idxset_string_hash_func,
147 pa_idxset_string_compare_func);
148 discover->nodes.byptr = pa_hashmap_new(pa_idxset_trivial_hash_func,
149 pa_idxset_trivial_compare_func);
153 void pa_discover_done(struct userdata *u)
155 pa_discover *discover;
159 if (u && (discover = u->discover)) {
160 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
161 mir_node_destroy(u, node);
163 pa_hashmap_free(discover->nodes.byname);
164 pa_hashmap_free(discover->nodes.byptr);
170 void pa_discover_domain_up(struct userdata *u)
172 pa_discover *discover;
177 pa_assert_se((discover = u->discover));
179 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
180 node->amid = AM_ID_INVALID;
182 if (node->visible && node->available) {
183 pa_audiomgr_register_node(u, node);
184 extapi_signal_node_change(u);
189 void pa_discover_domain_down(struct userdata *u)
193 void pa_discover_add_card(struct userdata *u, pa_card *card)
200 if (!(bus = pa_utils_get_card_bus(card))) {
201 pa_log_debug("ignoring card '%s' due to lack of '%s' property",
202 pa_utils_get_card_name(card), PA_PROP_DEVICE_BUS);
206 if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform")) {
207 handle_alsa_card(u, card);
210 else if (pa_streq(bus, "bluetooth")) {
211 handle_bluetooth_card(u, card);
215 pa_log_debug("ignoring card '%s' due to unsupported bus type '%s'",
216 pa_utils_get_card_name(card), bus);
219 void pa_discover_remove_card(struct userdata *u, pa_card *card)
222 pa_discover *discover;
228 pa_assert_se((discover = u->discover));
230 if (!(bus = pa_utils_get_card_bus(card)))
234 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
235 if (node->implement == mir_device &&
236 node->pacard.index == card->index)
238 if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform"))
239 mir_constrain_destroy(u, node->paname);
241 destroy_node(u, node);
245 if (pa_streq(bus, "bluetooth"))
246 mir_constrain_destroy(u, card->name);
249 void pa_discover_profile_changed(struct userdata *u, pa_card *card)
252 pa_card_profile *prof;
255 pa_discover *discover;
269 pa_assert_se((core = u->core));
270 pa_assert_se((discover = u->discover));
273 if ((bus = pa_utils_get_card_bus(card)) == NULL) {
274 pa_log_debug("ignoring profile change on card '%s' due to lack of '%s'"
275 "property", pa_utils_get_card_name(card),
280 pci = pa_streq(bus, "pci");
281 usb = pa_streq(bus, "usb");
282 bluetooth = pa_streq(bus, "bluetooth");
283 platform = pa_streq(bus, "platform");
285 if (!pci && !usb && !bluetooth && !platform) {
286 pa_log_debug("ignoring profile change on card '%s' due to unsupported "
287 "bus type '%s'", pa_utils_get_card_name(card), bus);
288 u->state.sink = u->state.source = PA_IDXSET_INVALID;
292 if ((index = u->state.sink) != PA_IDXSET_INVALID) {
293 if ((sink = pa_idxset_get_by_index(core->sinks, index)))
294 pa_discover_add_sink(u, sink, true);
296 pa_log_debug("sink.%u is gone", index);
297 u->state.sink = PA_IDXSET_INVALID;
300 if ((index = u->state.source) != PA_IDXSET_INVALID) {
301 if ((source = pa_idxset_get_by_index(core->sources, index)))
302 pa_discover_add_source(u, source);
304 pa_log_debug("source.%u is gone", index);
305 u->state.source = PA_IDXSET_INVALID;
309 pa_assert_se((prof = card->active_profile));
311 pa_log_debug("bluetooth profile changed to '%s' on card '%s'",
312 prof->name, card->name);
314 if (!prof->n_sinks && !prof->n_sources) {
315 /* switched off but not unloaded yet */
316 need_routing = false;
318 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
319 if (node->implement == mir_device &&
320 node->pacard.index == card->index)
322 if (node->type != mir_bluetooth_a2dp &&
323 node->type != mir_bluetooth_sco)
325 if (node->available) {
326 node->available = false;
334 schedule_deferred_routing(u);
338 pa_log_debug("alsa profile changed to '%s' on card '%s'",
339 card->active_profile->name, card->name);
341 stamp = pa_utils_get_stamp();
343 handle_alsa_card(u, card);
345 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
346 if (node->implement == mir_device &&
347 node->pacard.index == card->index &&
350 destroy_node(u, node);
357 void pa_discover_port_available_changed(struct userdata *u,
358 pa_device_port *port)
369 pa_direction_t direction;
374 pa_assert_se((core = u->core));
376 switch (port->available) {
377 case PA_AVAILABLE_NO: state = "not available"; break;
378 case PA_AVAILABLE_YES: state = "available"; break;
379 default: state = "unknown"; break;
382 pa_log_debug("port '%s' availabilty changed to %s. Updating",
390 while ((node = pa_utils_get_node_from_port(u, port, &iter))) {
392 available = get_bluetooth_port_availability(node, port);
393 route |= update_node_availability(u, node, available);
394 direction |= (node->direction == mir_input) ? PA_DIRECTION_INPUT : PA_DIRECTION_OUTPUT;
398 set_bluetooth_profile(u, port->card, direction);
400 switch (port->available) {
401 case PA_AVAILABLE_NO: available = false; break;
402 case PA_AVAILABLE_YES: available = true; break;
403 default: /* do nothing */ return;
406 if (port->direction == PA_DIRECTION_OUTPUT) {
407 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
409 if (port == pa_hashmap_get(sink->ports, port->name)) {
410 pa_log_debug(" sink '%s'", sink->name);
411 route |= update_node_availability_by_device(
420 if (port->direction == PA_DIRECTION_INPUT) {
421 PA_IDXSET_FOREACH(source, core->sources, idx) {
423 if (port == pa_hashmap_get(source->ports, port->name)) {
424 pa_log_debug(" source '%s'", source->name);
425 route |= update_node_availability_by_device(
436 mir_router_make_routing(u);
439 void pa_discover_add_sink(struct userdata *u, pa_sink *sink, bool route)
441 static pa_nodeset_resdef def_resdef = {0, {0, 0}};
444 pa_discover *discover;
451 const char *loopback_role;
453 pa_nodeset_resdef *resdef;
455 pa_nodeset_resdef rdbuf;
463 pa_assert_se((core = u->core));
464 pa_assert_se((discover = u->discover));
466 module = sink->module;
468 if ((card = sink->card)) {
469 if (!(key = node_key(u, mir_output,sink,ACTIVE_PORT, kbf,sizeof(kbf))))
471 if (!(node = pa_discover_find_node_by_key(u, key))) {
472 if (u->state.profile)
473 pa_log_debug("can't find node for sink (key '%s')", key);
475 u->state.sink = sink->index;
478 pa_log_debug("node for '%s' found (key %s). Updating with sink data",
479 node->paname, node->key);
480 node->paidx = sink->index;
481 node->available = true;
482 pa_discover_add_node_to_ptr_hash(u, sink, node);
484 if ((loopback_role = pa_classify_loopback_stream(node))) {
485 if (!(ns = pa_utils_get_null_source(u))) {
486 pa_log("Can't load loopback module: no initial null source");
490 map = pa_nodeset_get_map_by_role(u, loopback_role);
491 make_rset = (map && map->resdef);
492 resdef = make_rset ? map->resdef : &def_resdef;
494 node->loop = pa_loopback_create(u->loopback, core,
495 PA_LOOPBACK_SINK, node->index,
496 ns->index, sink->index,
500 resdef->flags.audio);
502 mir_node_print(node, nbf, sizeof(nbf));
503 pa_log_debug("updated node:\n%s", nbf);
506 pa_murphyif_create_resource_set(u, node, resdef);
512 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
513 mir_router_make_routing(u);
515 if (!u->state.profile)
516 schedule_deferred_routing(u);
520 else if (!module || !pa_streq(module->name, "module-combine-sink")) {
523 memset(&data, 0, sizeof(data));
524 data.key = pa_xstrdup(sink->name);
525 data.direction = mir_output;
526 data.implement = mir_device;
527 data.channels = sink->channel_map.channels;
528 data.available = true;
529 data.paidx = sink->index;
531 if (sink == pa_utils_get_null_sink(u)) {
532 data.visible = false;
533 data.type = mir_null;
534 data.amname = pa_xstrdup("Silent");
535 data.amid = AM_ID_INVALID;
536 data.paname = pa_xstrdup(sink->name);
538 else if (pa_classify_node_by_property(&data, sink->proplist)) {
539 data.privacy = mir_public;
541 data.amname = pa_xstrdup(mir_node_type_str(data.type));
542 data.amid = AM_ID_INVALID;
543 data.paname = pa_xstrdup(sink->name);
548 pa_xfree(data.key); /* for now */
549 pa_log_info("currently we do not support statically loaded "
550 "sinks without " PA_PROP_NODE_TYPE " property");
554 node = create_node(u, &data, NULL);
557 pa_discover_add_node_to_ptr_hash(u, sink, node);
562 void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
564 pa_discover *discover;
571 pa_assert_se((discover = u->discover));
573 name = pa_utils_get_sink_name(sink);
575 if (!(node = pa_hashmap_get(discover->nodes.byptr, sink)))
576 pa_log_debug("can't find node for sink (name '%s')", name);
578 pa_log_debug("node found for '%s'. Reseting sink data", name);
579 pa_murphyif_destroy_resource_set(u, node);
580 schedule_source_cleanup(u, node);
581 node->paidx = PA_IDXSET_INVALID;
582 pa_hashmap_remove(discover->nodes.byptr, sink);
587 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
588 node->available = false;
590 if (!u->state.profile)
591 schedule_deferred_routing(u);
595 pa_log_info("currently we do not support statically loaded sinks");
601 void pa_discover_add_source(struct userdata *u, pa_source *source)
603 static pa_nodeset_resdef def_resdef = {0, {0, 0}};
606 pa_discover *discover;
612 const char *loopback_role;
614 pa_nodeset_resdef *resdef;
622 pa_assert_se((core = u->core));
623 pa_assert_se((discover = u->discover));
625 if ((card = source->card)) {
626 if (!(key = node_key(u,mir_input,source,ACTIVE_PORT,kbf,sizeof(kbf))))
628 if (!(node = pa_discover_find_node_by_key(u, key))) {
629 if (u->state.profile)
630 pa_log_debug("can't find node for source (key '%s')", key);
632 u->state.source = source->index;
635 pa_log_debug("node for '%s' found. Updating with source data",
637 node->paidx = source->index;
638 node->available = true;
639 pa_discover_add_node_to_ptr_hash(u, source, node);
640 if ((loopback_role = pa_classify_loopback_stream(node))) {
641 if (!(ns = pa_utils_get_null_sink(u))) {
642 pa_log("Can't load loopback module: no initial null sink");
646 map = pa_nodeset_get_map_by_role(u, loopback_role);
647 make_rset = (map && map->resdef);
648 resdef = make_rset ? map->resdef : &def_resdef;
650 node->loop = pa_loopback_create(u->loopback, core,
651 PA_LOOPBACK_SOURCE, node->index,
652 source->index, ns->index,
656 resdef->flags.audio);
658 sink_index = pa_loopback_get_sink_index(core, node->loop);
659 node->mux = pa_multiplex_find_by_sink(u->multiplex,sink_index);
662 mir_node_print(node, nbf, sizeof(nbf));
663 pa_log_debug("updated node:\n%s", nbf);
666 pa_murphyif_create_resource_set(u, node, resdef);
668 pa_fader_apply_volume_limits(u, node->stamp);
672 memset(&data, 0, sizeof(data));
673 data.key = pa_xstrdup(source->name);
674 data.direction = mir_input;
675 data.implement = mir_device;
676 data.channels = source->channel_map.channels;
677 data.available = true;
679 if (source == pa_utils_get_null_source(u)) {
680 data.visible = false;
681 data.type = mir_null;
682 data.amname = pa_xstrdup("Silent");
683 data.amid = AM_ID_INVALID;
684 data.paname = pa_xstrdup(source->name);
685 data.paidx = source->index;
687 else if (pa_classify_node_by_property(&data, source->proplist)) {
689 data.amname = pa_xstrdup(mir_node_type_str(data.type));
690 data.amid = AM_ID_INVALID;
691 data.paname = pa_xstrdup(source->name);
694 pa_xfree(data.key); /* for now */
695 pa_log_info("currently we do not support statically loaded "
696 "sources without " PA_PROP_NODE_TYPE " property");
700 create_node(u, &data, NULL);
705 void pa_discover_remove_source(struct userdata *u, pa_source *source)
707 pa_discover *discover;
714 pa_assert_se((discover = u->discover));
716 name = pa_utils_get_source_name(source);
718 if (!(node = pa_hashmap_get(discover->nodes.byptr, source)))
719 pa_log_debug("can't find node for source (name '%s')", name);
721 pa_log_debug("node found. Reseting source data");
722 pa_murphyif_destroy_resource_set(u, node);
723 schedule_source_cleanup(u, node);
724 node->paidx = PA_IDXSET_INVALID;
725 pa_hashmap_remove(discover->nodes.byptr, source);
730 if (type != mir_bluetooth_sco)
731 node->available = false;
733 if (!u->state.profile)
734 schedule_deferred_routing(u);
738 pa_log_info("currently we do not support statically "
745 void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
748 pa_discover *discover;
759 pa_nodeset_resdef *resdef;
763 pa_assert_se((core = u->core));
764 pa_assert_se((discover = u->discover));
765 pa_assert_se((pl = sinp->proplist));
767 if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME))) {
768 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
769 pa_log_debug("Seems to be a combine stream. Nothing to do ...");
772 if (!strncmp(media, loopback_outpatrn, sizeof(loopback_outpatrn)-1)) {
773 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
778 name = pa_utils_get_sink_input_name(sinp);
780 pa_log_debug("registering input stream '%s'", name);
782 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
783 pa_log_debug("cant find stream class for '%s'. "
784 "Leaving it alone", name);
788 pa_utils_set_stream_routing_properties(pl, type, NULL);
790 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
792 memset(&data, 0, sizeof(data));
794 data.direction = mir_input;
795 data.implement = mir_stream;
796 data.channels = sinp->channel_map.channels;
798 data.zone = pa_utils_get_zone(sinp->proplist);
800 data.available = true;
802 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
803 data.amid = AM_ID_INVALID;
805 data.paidx = sinp->index;
806 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
809 * here we can't guess whether the application requested an explicit
810 * route by sepcifying the target sink @ stream creation time.
812 * the brute force solution: we make a default route for this stream
813 * possibly overwiriting the orginal app request :(
815 /* this will set data.mux */
816 role = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_ROLE);
817 sink = make_output_prerouting(u, &data, &sinp->channel_map, role, &target);
819 node = create_node(u, &data, NULL);
821 pa_discover_add_node_to_ptr_hash(u, sinp, node);
823 if (sink && target) {
824 pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
826 if (pa_sink_input_move_to(sinp, sink, false) < 0)
827 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
829 pa_audiomgr_add_default_route(u, node, target);
833 bool pa_discover_preroute_sink_input(struct userdata *u,
834 pa_sink_input_new_data *data)
839 pa_discover *discover;
840 pa_multiplex *multiplex;
849 pa_nodeset_resdef *resdef;
855 pa_assert_se((core = u->core));
856 pa_assert_se((discover = u->discover));
857 pa_assert_se((multiplex = u->multiplex));
858 pa_assert_se((pl = data->proplist));
860 mnam = (m = data->module) ? m->name : "";
862 if (pa_streq(mnam, "module-combine-sink")) {
864 type = mir_node_type_unknown;
866 if (!(mux = pa_multiplex_find_by_module(multiplex, m)) ||
867 !(sink = pa_idxset_get_by_index(core->sinks, mux->sink_index)) ||
868 !(sinp = pa_idxset_first(sink->inputs, NULL)) ||
869 !(type = pa_utils_get_stream_class(sinp->proplist)))
871 pa_log_debug("can't figure out the type of multiplex stream");
874 pa_utils_set_stream_routing_properties(data->proplist, type, NULL);
878 loopback = pa_streq(mnam, "module-loopback");
882 if (!(node = pa_utils_get_node_from_data(u, mir_input, data))) {
883 pa_log_debug("can't find loopback node for sink-input");
887 if (node->direction == mir_output) {
888 pa_log_debug("refuse to preroute loopback sink-input "
889 "(current route: sink %u @ %p)", data->sink ?
890 data->sink->index : PA_IDXSET_INVALID,data->sink);
896 type = pa_classify_guess_stream_node_type(u, pl, NULL);
899 remap = pa_streq(mnam, "module-remap-sink");
900 type = pa_classify_guess_stream_node_type(u, pl, &resdef);
902 pa_utils_set_resource_properties(pl, resdef);
904 if (pa_stream_state_start_corked(u, data, resdef)) {
905 pa_log_debug("start corked");
909 pa_utils_set_stream_routing_properties(pl, type, data->sink);
912 memset(&fake, 0, sizeof(fake));
913 fake.direction = mir_input;
914 fake.implement = mir_stream;
918 fake.channels = data->channel_map.channels;
919 fake.zone = pa_utils_get_zone(data->proplist);
921 fake.available = true;
922 fake.amname = "<preroute sink-input>";
923 fake.amid = AM_ID_INVALID;
924 fake.paidx = PA_IDXSET_INVALID;
926 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
927 sink = make_output_prerouting(u, &fake, &data->channel_map, role,NULL);
931 if (fake.mux && !(data->flags & PA_SINK_INPUT_START_CORKED)) {
932 data->flags |= PA_SINK_INPUT_START_CORKED;
933 schedule_stream_uncorking(u, sink);
937 if (pa_sink_input_new_data_set_sink(data, sink, false))
938 pa_log_debug("set sink %u for new sink-input", sink->index);
940 pa_log("can't set sink %u for new sink-input", sink->index);
941 /* copes wit NULL mux */
942 pa_multiplex_destroy(u->multiplex, core, fake.mux);
952 if (loopback && data->sink && data->sink->module) {
954 if (pa_streq(data->sink->module->name, "module-combine-sink"))
958 if (pa_classify_ramping_stream(&fake)) {
959 pa_log_debug("set sink-input ramp-muted");
960 data->flags |= PA_SINK_INPUT_START_RAMP_MUTED;
967 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
971 pa_sink_input *csinp;
973 pa_discover *discover;
974 pa_multiplex *multiplex;
984 pa_nodeset_resdef *resdef;
985 pa_nodeset_resdef rdbuf;
989 pa_assert_se((core = u->core));
990 pa_assert_se((discover = u->discover));
991 pa_assert_se((multiplex = u->multiplex));
992 pa_assert_se((pl = sinp->proplist));
996 if (!(media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)))
999 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
1000 if (!pa_utils_stream_has_default_route(sinp->proplist) ||
1001 !(mux = pa_multiplex_find_by_module(multiplex, sinp->module)) ||
1002 mux->defstream_index != PA_IDXSET_INVALID)
1004 pa_log_debug("New stream is a combine stream. Nothing to do ...");
1007 pa_log_debug("New stream is a combine stream. Setting as default");
1008 mux->defstream_index = sinp->index;
1009 mir_router_make_routing(u);
1012 } else if (!strncmp(media, loopback_outpatrn,sizeof(loopback_outpatrn)-1)){
1013 pa_log_debug("New stream is a loopback output stream");
1015 if ((node = pa_utils_get_node_from_stream(u, mir_input, sinp))) {
1016 if (node->direction == mir_input)
1017 pa_log_debug("loopback stream node '%s' found", node->amname);
1019 pa_log_debug("ignoring it");
1024 pa_log_debug("can't find node for the loopback stream");
1031 name = pa_utils_get_sink_input_name(sinp);
1033 pa_log_debug("dealing with new input stream '%s'", name);
1035 if ((type = get_stream_routing_class(pl)))
1036 resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1038 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1039 pa_log_debug("cant find stream class for '%s'. "
1040 "Leaving it alone", name);
1044 pa_utils_set_stream_routing_properties(pl, type, NULL);
1046 /* if needed, make some post-routing here */
1049 /* we need to add this to main hashmap as that is used for loop
1050 through on all nodes. */
1051 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
1053 memset(&data, 0, sizeof(data));
1055 data.direction = mir_input;
1056 data.implement = mir_stream;
1057 data.channels = sinp->channel_map.channels;
1059 data.zone = pa_utils_get_zone(pl);
1060 data.visible = true;
1061 data.available = true;
1063 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1064 data.amid = AM_ID_INVALID;
1066 data.paidx = sinp->index;
1067 data.mux = pa_multiplex_find_by_sink(u->multiplex,
1069 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1070 node = create_node(u, &data, &created);
1075 pa_log("%s: confused with stream. '%s' did exists",
1076 __FILE__, node->amname);
1081 pa_murphyif_add_node(u, node);
1083 pa_murphyif_create_resource_set(u, node, resdef);
1085 pa_discover_add_node_to_ptr_hash(u, sinp, node);
1090 csinp = pa_idxset_get_by_index(core->sink_inputs,
1091 data.mux->defstream_index);
1092 s = csinp ? csinp->sink : NULL;
1094 if ((sinp->flags & PA_SINK_INPUT_START_RAMP_MUTED)) {
1095 pa_log_debug("ramp '%s' to 100%", media);
1096 pa_fader_ramp_volume(u, sinp, PA_VOLUME_NORM);
1102 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1104 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1105 pa_log_debug("can't figure out where this stream is routed");
1107 pa_log_debug("register route '%s' => '%s'",
1108 node->amname, snod->amname);
1110 if (pa_utils_stream_has_default_route(sinp->proplist))
1111 pa_audiomgr_add_default_route(u, node, snod);
1113 /* FIXME: register explicit routes */
1114 /* else pa_audiomgr_add/register_explicit_route() */
1117 pa_fader_apply_volume_limits(u, pa_utils_get_stamp());
1122 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
1124 pa_discover *discover;
1128 bool had_properties;
1132 pa_assert_se((discover = u->discover));
1134 name = pa_utils_get_sink_input_name(sinp);
1136 pa_log_debug("sink-input '%s' going to be destroyed", name);
1138 had_properties = pa_utils_unset_stream_routing_properties(sinp->proplist);
1140 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sinp))) {
1141 if (!pa_multiplex_sink_input_remove(u->multiplex, sinp))
1142 pa_log_debug("nothing to do for sink-input (name '%s')", name);
1145 pa_log_debug("node found for '%s'. After clearing routes "
1146 "it will be destroyed", name);
1148 if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
1149 pa_log_debug("can't figure out where this stream is routed");
1151 pa_log_debug("clear route '%s' => '%s'",
1152 node->amname, sinknod->amname);
1154 /* FIXME: and actually do it ... */
1158 destroy_node(u, node);
1161 if (node || had_properties)
1162 mir_router_make_routing(u);
1166 void pa_discover_register_source_output(struct userdata *u,
1167 pa_source_output *sout)
1170 pa_discover *discover;
1181 pa_nodeset_resdef *resdef;
1185 pa_assert_se((core = u->core));
1186 pa_assert_se((discover = u->discover));
1187 pa_assert_se((pl = sout->proplist));
1189 if ((media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME))) {
1190 if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1191 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
1196 name = pa_utils_get_source_output_name(sout);
1198 pa_log_debug("registering output stream '%s'", name);
1200 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1201 pa_log_debug("cant find stream class for '%s'. "
1202 "Leaving it alone", name);
1206 pa_utils_set_stream_routing_properties(pl, type, NULL);
1208 snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1210 memset(&data, 0, sizeof(data));
1212 data.direction = mir_output;
1213 data.implement = mir_stream;
1214 data.channels = sout->channel_map.channels;
1216 data.zone = pa_utils_get_zone(sout->proplist);
1217 data.visible = true;
1218 data.available = true;
1220 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1221 data.amid = AM_ID_INVALID;
1223 data.paidx = sout->index;
1224 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1227 * here we can't guess whether the application requested an explicit
1228 * route by sepcifying the target source @ stream creation time.
1230 * the brute force solution: we make a default route for this stream
1231 * possibly overwiriting the orginal app request :(
1233 role = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_ROLE);
1234 source = make_input_prerouting(u, &data, role, &target);
1236 node = create_node(u, &data, NULL);
1238 pa_discover_add_node_to_ptr_hash(u, sout, node);
1240 if (source && target) {
1241 pa_log_debug("move stream to source %u (%s)",
1242 source->index, source->name);
1244 if (pa_source_output_move_to(sout, source, false) < 0)
1245 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
1247 pa_log_debug("register route '%s' => '%s'",
1248 node->amname, target->amname);
1249 /* FIXME: and actually do it ... */
1254 bool pa_discover_preroute_source_output(struct userdata *u,
1255 pa_source_output_new_data *data)
1260 pa_discover *discover;
1267 pa_nodeset_resdef *resdef;
1271 pa_assert_se((core = u->core));
1272 pa_assert_se((discover = u->discover));
1273 pa_assert_se((pl = data->proplist));
1275 mnam = (m = data->module) ? m->name : "";
1277 if (pa_streq(mnam, "module-loopback")) {
1278 if (!(node = pa_utils_get_node_from_data(u, mir_output, data))) {
1279 pa_log_debug("can't find loopback node for source-output");
1283 if (node->direction == mir_input) {
1284 pa_log_debug("refuse to preroute loopback source-output "
1285 "(current route: source %u @ %p)", data->source ?
1286 data->source->index : PA_IDXSET_INVALID,data->source);
1290 data->source = NULL;
1292 type = pa_classify_guess_stream_node_type(u, pl, NULL);
1295 type = pa_classify_guess_stream_node_type(u, pl, &resdef);
1297 pa_utils_set_resource_properties(pl, resdef);
1300 pa_utils_set_stream_routing_properties(pl, type, data->source);
1302 if (!data->source) {
1303 memset(&fake, 0, sizeof(fake));
1304 fake.direction = mir_output;
1305 fake.implement = mir_stream;
1306 fake.channels = data->channel_map.channels;
1308 fake.zone = pa_utils_get_zone(data->proplist);
1309 fake.visible = true;
1310 fake.available = true;
1311 fake.amname = "<preroute source-output>";
1313 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
1314 source = make_input_prerouting(u, &fake, role, NULL);
1317 if (pa_source_output_new_data_set_source(data, source, false)) {
1318 pa_log_debug("set source %u for new source-output",
1322 pa_log("can't set source %u for new source-output",
1332 void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
1337 pa_discover *discover;
1346 pa_nodeset_resdef *resdef;
1347 pa_nodeset_resdef rdbuf;
1351 pa_assert_se((core = u->core));
1352 pa_assert_se((discover = u->discover));
1353 pa_assert_se((pl = sout->proplist));
1357 if (!(media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME)))
1358 media = "<unknown>";
1360 if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1361 pa_log_debug("New stream is a loopback input stream");
1363 if ((node = pa_utils_get_node_from_stream(u, mir_output, sout))) {
1364 if (node->direction == mir_output)
1365 pa_log_debug("loopback stream node '%s' found", node->amname);
1367 pa_log_debug("ignoring it");
1372 pa_log_debug("can't find node for the loopback stream");
1377 name = pa_utils_get_source_output_name(sout);
1379 pa_log_debug("dealing with new output stream '%s'", name);
1381 if ((type = get_stream_routing_class(pl)))
1382 resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1384 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1385 pa_log_debug("cant find stream class for '%s'. "
1386 "Leaving it alone", name);
1390 pa_utils_set_stream_routing_properties(pl, type, NULL);
1392 /* if needed, make some post-routing here */
1395 /* we need to add this to main hashmap as that is used for loop
1396 through on all nodes. */
1397 snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1399 memset(&data, 0, sizeof(data));
1401 data.direction = mir_output;
1402 data.implement = mir_stream;
1403 data.channels = sout->channel_map.channels;
1405 data.zone = pa_utils_get_zone(pl);
1406 data.visible = true;
1407 data.available = true;
1409 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1410 data.amid = AM_ID_INVALID;
1412 data.paidx = sout->index;
1413 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1415 node = create_node(u, &data, &created);
1420 pa_log("%s: confused with stream. '%s' did exists",
1421 __FILE__, node->amname);
1426 pa_murphyif_add_node(u, node);
1428 pa_murphyif_create_resource_set(u, node, resdef);
1430 pa_discover_add_node_to_ptr_hash(u, sout, node);
1433 if ((s = sout->source))
1434 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1436 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1437 pa_log_debug("can't figure out where this stream is routed");
1439 pa_log_debug("register route '%s' => '%s'",
1440 snod->amname, node->amname);
1441 pa_audiomgr_add_default_route(u, node, snod);
1446 void pa_discover_remove_source_output(struct userdata *u,
1447 pa_source_output *sout)
1449 pa_discover *discover;
1456 pa_assert_se((discover = u->discover));
1458 name = pa_utils_get_source_output_name(sout);
1460 pa_log_debug("source-output '%s' going to be destroyed", name);
1462 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sout)))
1463 pa_log_debug("can't find node for source-output (name '%s')", name);
1465 pa_log_debug("node found for '%s'. After clearing routes "
1466 "it will be destroyed", name);
1468 if (!(srcnod = pa_hashmap_get(discover->nodes.byptr, sout->source)))
1469 pa_log_debug("can't figure out where this stream is routed");
1471 pa_log_debug("clear route '%s' => '%s'",
1472 node->amname, srcnod->amname);
1474 /* FIXME: and actually do it ... */
1478 destroy_node(u, node);
1480 mir_router_make_routing(u);
1485 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
1487 pa_discover *discover;
1491 pa_assert_se((discover = u->discover));
1494 node = pa_hashmap_get(discover->nodes.byname, key);
1501 mir_node *pa_discover_find_node_by_ptr(struct userdata *u, void *ptr)
1503 pa_discover *discover;
1507 pa_assert_se((discover = u->discover));
1510 node = pa_hashmap_get(discover->nodes.byptr, ptr);
1517 void pa_discover_add_node_to_ptr_hash(struct userdata *u,
1521 pa_discover *discover;
1526 pa_assert_se((discover = u->discover));
1528 pa_hashmap_put(discover->nodes.byptr, ptr, node);
1531 mir_node *pa_discover_remove_node_from_ptr_hash(struct userdata *u, void *ptr)
1533 pa_discover *discover;
1537 pa_assert_se((discover = u->discover));
1539 return pa_hashmap_remove(discover->nodes.byptr, ptr);
1543 static void handle_alsa_card(struct userdata *u, pa_card *card)
1550 memset(&data, 0, sizeof(data));
1551 data.zone = pa_utils_get_zone(card->proplist);
1552 data.visible = true;
1553 data.amid = AM_ID_INVALID;
1554 data.implement = mir_device;
1555 data.paidx = PA_IDXSET_INVALID;
1556 data.stamp = pa_utils_get_stamp();
1558 cnam = pa_utils_get_card_name(card);
1559 udd = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
1561 if (udd && pa_streq(udd, "1")) {
1562 /* udev loaded alsa card */
1563 if (!strncmp(cnam, "alsa_card.", 10)) {
1565 handle_udev_loaded_card(u, card, &data, cid);
1570 /* statically loaded pci or usb card */
1573 pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
1578 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1580 pa_discover *discover;
1581 pa_card_profile *prof;
1589 char paname[MAX_NAME_LENGTH+1];
1590 char amname[MAX_NAME_LENGTH+1];
1591 char key[MAX_NAME_LENGTH+1];
1593 pa_assert_se((discover = u->discover));
1595 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1598 memset(paname, 0, sizeof(paname));
1599 memset(amname, 0, sizeof(amname));
1600 memset(key , 0, sizeof(key) );
1602 memset(&data, 0, sizeof(data));
1604 data.visible = true;
1605 data.amid = AM_ID_INVALID;
1606 data.implement = mir_device;
1607 data.paidx = PA_IDXSET_INVALID;
1608 data.paname = paname;
1609 data.amname = amname;
1610 data.amdescr = (char *)cdescr;
1611 data.pacard.index = card->index;
1612 data.stamp = pa_utils_get_stamp();
1614 cnam = pa_utils_get_card_name(card);
1616 if (!strncmp(cnam, "bluez_card.", 11)) {
1619 pa_assert(card->ports);
1621 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1623 PA_HASHMAP_FOREACH(prof, card->profiles, cstate) {
1624 data.available = false;
1625 data.pacard.profile = prof->name;
1627 if (prof->n_sinks > 0) {
1628 data.direction = mir_output;
1629 data.channels = prof->max_sink_channels;
1630 data.amname = amname;
1632 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1633 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1634 pa_classify_node_by_card(&data, card, prof, NULL);
1635 node = create_node(u, &data, NULL);
1636 mir_constrain_add_node(u, cd, node);
1639 if (prof->n_sources > 0) {
1640 data.direction = mir_input;
1641 data.channels = prof->max_source_channels;
1642 data.amname = amname;
1644 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1645 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1646 pa_classify_node_by_card(&data, card, prof, NULL);
1647 node = create_node(u, &data, NULL);
1648 mir_constrain_add_node(u, cd, node);
1652 if (!(prof = card->active_profile))
1653 pa_log("card '%s' has no active profile", card->name);
1655 pa_log_debug("card '%s' default profile '%s'",
1656 card->name, prof->name);
1659 schedule_card_check(u, card);
1665 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1667 pa_discover *discover;
1668 pa_card_profile *prof;
1669 pa_device_port *port;
1676 void *state0, *state1;
1677 char paname[MAX_NAME_LENGTH+1];
1678 char amname[MAX_NAME_LENGTH+1];
1679 char key[MAX_NAME_LENGTH+1];
1684 pa_assert_se((discover = u->discover));
1686 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1689 memset(paname, 0, sizeof(paname));
1690 memset(amname, 0, sizeof(amname));
1691 memset(key , 0, sizeof(key) );
1693 memset(&data, 0, sizeof(data));
1695 data.zone = pa_utils_get_zone(card->proplist);
1696 data.visible = true;
1697 data.amid = AM_ID_INVALID;
1698 data.implement = mir_device;
1699 data.paidx = PA_IDXSET_INVALID;
1700 data.paname = paname;
1701 data.amname = amname;
1702 data.amdescr = (char *)cdescr;
1703 data.pacard.index = card->index;
1704 data.stamp = pa_utils_get_stamp();
1706 cnam = pa_utils_get_card_name(card);
1708 if (!strncmp(cnam, "bluez_card.", 11)) {
1711 pa_assert(card->ports);
1713 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1715 PA_HASHMAP_FOREACH(port, card->ports, state0) {
1716 pa_assert(port->profiles);
1719 input = output = true;
1720 len = strlen(port->name);
1721 if (len >= 6 && !strcmp("-input", port->name + (len-6)))
1723 else if (len >= 7 && !strcmp("-output", port->name + (len-7)))
1727 PA_HASHMAP_FOREACH(prof, port->profiles, state1) {
1728 data.pacard.profile = prof->name;
1729 data.available = get_bluetooth_port_availability(&data, port);
1731 if (output && prof->n_sinks > 0) {
1732 data.direction = mir_output;
1733 data.channels = prof->max_sink_channels;
1734 data.amname = amname;
1736 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1737 snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1738 pa_classify_node_by_card(&data, card, prof, NULL);
1739 node = create_node(u, &data, NULL);
1740 mir_constrain_add_node(u, cd, node);
1741 pa_utils_set_port_properties(port, node);
1744 if (input && prof->n_sources > 0) {
1745 data.direction = mir_input;
1746 data.channels = prof->max_source_channels;
1747 data.amname = amname;
1749 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1750 snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1751 pa_classify_node_by_card(&data, card, prof, NULL);
1752 node = create_node(u, &data, NULL);
1753 mir_constrain_add_node(u, cd, node);
1754 pa_utils_set_port_properties(port, node);
1759 if (!(prof = card->active_profile))
1760 pa_log("card '%s' has no active profile", card->name);
1762 pa_log_debug("card '%s' default profile '%s'",
1763 card->name, prof->name);
1766 schedule_card_check(u, card);
1770 static bool get_bluetooth_port_availability(mir_node *node,
1771 pa_device_port *port)
1773 bool available = false;
1779 if ((prof = node->pacard.profile)) {
1780 if (!strcmp(prof, "hfgw") ||
1781 !strcmp(prof, "a2dp_source") ||
1782 !strcmp(prof, "a2dp_sink"))
1783 available = (port->available != PA_AVAILABLE_NO);
1791 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
1792 mir_node *data, char *cardid)
1794 pa_discover *discover;
1795 pa_card_profile *prof;
1796 pa_card_profile *active;
1798 const char *alsanam;
1800 char *sinks[MAX_CARD_TARGET+1];
1801 char *sources[MAX_CARD_TARGET+1];
1802 char buf[MAX_NAME_LENGTH+1];
1803 char paname[MAX_NAME_LENGTH+1];
1804 char amname[MAX_NAME_LENGTH+1];
1808 pa_assert(card->profiles);
1809 pa_assert_se((discover = u->discover));
1811 alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
1813 memset(amname, 0, sizeof(amname));
1815 data->paname = paname;
1816 data->amname = amname;
1817 data->amdescr = (char *)alsanam;
1819 data->pacard.index = card->index;
1821 active = card->active_profile;
1823 PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1824 /* filtering: deal with selected profiles if requested so */
1825 if (discover->selected && (!active || (active && prof != active)))
1828 /* filtering: skip the 'off' profiles */
1829 if (!prof->n_sinks && !prof->n_sources)
1832 /* filtering: consider sinks with suitable amount channels */
1833 if (prof->n_sinks &&
1834 (prof->max_sink_channels < discover->chmin ||
1835 prof->max_sink_channels > discover->chmax ))
1838 /* filtering: consider sources with suitable amount channels */
1839 if (prof->n_sources &&
1840 (prof->max_source_channels < discover->chmin ||
1841 prof->max_source_channels > discover->chmax ))
1844 data->pacard.profile = prof->name;
1846 parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
1848 data->direction = mir_output;
1849 data->channels = prof->max_sink_channels;
1850 for (i = 0; (sid = sinks[i]); i++) {
1851 snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
1852 handle_card_ports(u, data, card, prof);
1855 data->direction = mir_input;
1856 data->channels = prof->max_source_channels;
1857 for (i = 0; (sid = sources[i]); i++) {
1858 snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
1859 handle_card_ports(u, data, card, prof);
1865 static void handle_card_ports(struct userdata *u, mir_node *data,
1866 pa_card *card, pa_card_profile *prof)
1868 mir_node *node = NULL;
1869 bool have_ports = false;
1870 mir_constr_def *cd = NULL;
1871 char *amname = data->amname;
1872 pa_device_port *port;
1875 char key[MAX_NAME_LENGTH+1];
1883 PA_HASHMAP_FOREACH(port, card->ports, state) {
1885 * if this port did not belong to any profile
1886 * (ie. prof->profiles == NULL) we assume that this port
1887 * does works with all the profiles
1889 if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
1890 ((port->direction == PA_DIRECTION_INPUT && data->direction == mir_input)||
1891 (port->direction == PA_DIRECTION_OUTPUT && data->direction == mir_output)))
1896 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
1899 data->available = (port->available != PA_AVAILABLE_NO);
1901 data->amname = amname;
1902 data->paport = port->name;
1904 pa_classify_node_by_card(data, card, prof, port);
1906 node = create_node(u, data, &created);
1909 node->stamp = data->stamp;
1911 cd = mir_constrain_create(u, "port", mir_constrain_port,
1913 mir_constrain_add_node(u, cd, node);
1920 data->key = data->paname;
1921 data->available = true;
1923 pa_classify_node_by_card(data, card, prof, NULL);
1925 node = create_node(u, data, &created);
1928 node->stamp = data->stamp;
1931 data->amname = amname;
1936 static mir_node *create_node(struct userdata *u, mir_node *data,
1939 pa_discover *discover;
1946 pa_assert(data->key);
1947 pa_assert(data->paname);
1948 pa_assert_se((discover = u->discover));
1950 if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
1955 node = mir_node_create(u, data);
1956 pa_hashmap_put(discover->nodes.byname, node->key, node);
1958 mir_node_print(node, buf, sizeof(buf));
1959 pa_log_debug("new node:\n%s", buf);
1961 if (node->available)
1962 pa_audiomgr_register_node(u, node);
1966 *created_ret = created;
1971 static void destroy_node(struct userdata *u, mir_node *node)
1973 pa_discover *discover;
1977 pa_assert_se((discover = u->discover));
1980 removed = pa_hashmap_remove(discover->nodes.byname, node->key);
1982 if (node != removed) {
1984 pa_log("%s: confused with data structures: key mismatch. "
1985 " attempted to destroy '%s'; actually destroyed '%s'",
1986 __FILE__, node->key, removed->key);
1988 pa_log("%s: confused with data structures: node '%s' "
1989 "is not in the hash table", __FILE__, node->key);
1993 pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
1995 if (node->implement == mir_stream) {
1996 if (node->direction == mir_input) {
1998 pa_log_debug("removing multiplexer");
2003 pa_audiomgr_unregister_node(u, node);
2005 extapi_signal_node_change(u);
2007 mir_constrain_remove_node(u, node);
2009 pa_loopback_destroy(u->loopback, u->core, node->loop);
2010 pa_multiplex_destroy(u->multiplex, u->core, node->mux);
2012 mir_node_destroy(u, node);
2016 static bool update_node_availability(struct userdata *u,
2023 if ((!available && node->available) ||
2024 ( available && !node->available) )
2026 node->available = available;
2029 pa_audiomgr_register_node(u, node);
2031 pa_audiomgr_unregister_node(u, node);
2033 extapi_signal_node_change(u);
2035 return true; /* routing needed */
2041 static bool update_node_availability_by_device(struct userdata *u,
2042 mir_direction direction,
2044 pa_device_port *port,
2054 pa_assert(direction == mir_input || direction == mir_output);
2056 if ((key = node_key(u, direction, data, port, buf, sizeof(buf)))) {
2057 if (!(node = pa_discover_find_node_by_key(u, key)))
2058 pa_log_debug(" can't find node (key '%s')", key);
2060 pa_log_debug(" node for '%s' found (key %s)",
2061 node->paname, node->key);
2063 return update_node_availability(u, node, available);
2067 return false; /* no routing needed */
2070 static char *get_name(char **string_ptr, int offs)
2072 char c, *name, *end;
2074 name = *string_ptr + offs;
2076 for (end = name; (c = *end); end++) {
2088 static void parse_profile_name(pa_card_profile *prof,
2098 pa_assert(prof->name);
2100 strncpy(buf, prof->name, buflen);
2101 buf[buflen-1] = '\0';
2103 memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2104 memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2107 if (!strncmp(p, "output:", 7)) {
2108 if (i >= MAX_CARD_TARGET) {
2109 pa_log_debug("number of outputs exeeds the maximum %d in "
2110 "profile name '%s'", MAX_CARD_TARGET, prof->name);
2113 sinks[i++] = get_name(&p, 7);
2115 else if (!strncmp(p, "input:", 6)) {
2116 if (j >= MAX_CARD_TARGET) {
2117 pa_log_debug("number of inputs exeeds the maximum %d in "
2118 "profile name '%s'", MAX_CARD_TARGET, prof->name);
2121 sources[j++] = get_name(&p, 6);
2124 pa_log("%s: failed to parse profile name '%s'",
2125 __FILE__, prof->name);
2132 static char *node_key(struct userdata *u, mir_direction direction,
2133 void *data, pa_device_port *port, char *buf, size_t len)
2136 pa_card_profile *profile;
2144 const char *profile_name;
2150 pa_assert(direction == mir_input || direction == mir_output);
2152 if (direction == mir_output) {
2153 pa_sink *sink = data;
2155 name = pa_utils_get_sink_name(sink);
2158 port = sink->active_port;
2161 pa_source *source = data;
2163 name = pa_utils_get_source_name(source);
2164 card = source->card;
2166 port = source->active_port;
2172 pa_assert_se((profile = card->active_profile));
2174 if (!u->state.profile)
2175 profile_name = profile->name;
2177 pa_log_debug("state.profile is not null. '%s' supresses '%s'",
2178 u->state.profile, profile->name);
2179 profile_name = u->state.profile;
2183 if (!(bus = pa_utils_get_card_bus(card))) {
2184 pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
2185 "on its card", type, name, PA_PROP_DEVICE_BUS);
2189 pci = pa_streq(bus, "pci");
2190 usb = pa_streq(bus, "usb");
2191 platform = pa_streq(bus, "platform");
2192 bluetooth = pa_streq(bus, "bluetooth");
2194 if (!pci && !usb && !bluetooth && !platform) {
2195 pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
2196 "of its card", type, name, bus);
2205 snprintf(buf, len, "%s@%s.%s", name, port->name, profile_name);
2213 snprintf(buf, len, "%s@%s", name, port->name);
2220 static pa_sink *make_output_prerouting(struct userdata *u,
2222 pa_channel_map *chmap,
2223 const char *media_role,
2224 mir_node **target_ret)
2228 pa_sink *sink = NULL;
2233 pa_assert_se((core = u->core));
2236 target = mir_router_make_prerouting(u, data);
2239 pa_log("there is no default route for the stream '%s'", data->amname);
2240 else if (target->paidx == PA_IDXSET_INVALID)
2241 pa_log("can't route to default '%s': no sink", target->amname);
2243 if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
2244 pa_log("no route to default '%s': sink is gone", target->amname);
2246 if (pa_classify_multiplex_stream(data)) {
2247 data->mux = pa_multiplex_create(u->multiplex, core,
2248 sink->index, chmap, NULL,
2249 media_role, data->type);
2251 sink = pa_idxset_get_by_index(core->sinks,
2252 data->mux->sink_index);
2260 *target_ret = target;
2266 static pa_source *make_input_prerouting(struct userdata *u,
2268 const char *media_role,
2269 mir_node **target_ret)
2273 pa_source *source = NULL;
2277 pa_assert_se((core = u->core));
2279 target = mir_router_make_prerouting(u, data);
2282 pa_log("there is no default route for the stream '%s'", data->amname);
2283 else if (target->paidx == PA_IDXSET_INVALID)
2284 pa_log("can't route to default '%s': no source", target->amname);
2286 if (!(source = pa_idxset_get_by_index(core->sources, target->paidx)))
2287 pa_log("no route to default '%s': source is gone",target->amname);
2291 *target_ret = target;
2296 static mir_node_type get_stream_routing_class(pa_proplist *pl)
2302 t = pa_utils_get_stream_class(pl);
2304 if (t >= mir_application_class_begin && t < mir_application_class_end)
2307 return mir_node_type_unknown;
2311 static void set_bluetooth_profile(struct userdata *u,
2313 pa_direction_t direction)
2316 pa_device_port *port;
2317 pa_card_profile *prof, *make_active;
2318 void *state0, *state1;
2319 bool port_available;
2325 pa_assert_se((core = u->core));
2331 pa_log_debug("which profile to make active:");
2333 PA_HASHMAP_FOREACH(prof, card->profiles, state0) {
2334 if (!prof->n_sinks && !prof->n_sources) {
2336 pa_log_debug(" considering %s", prof->name);
2342 port_available = false;
2344 PA_HASHMAP_FOREACH(port, card->ports, state1) {
2345 if ((direction & port->direction) &&
2346 pa_hashmap_get(port->profiles, prof->name))
2348 port_available = (port->available != PA_AVAILABLE_NO);
2353 if (!port_available)
2354 pa_log_debug(" ruling out %s (port not available)", prof->name);
2355 else if (prof->available != PA_AVAILABLE_YES)
2356 pa_log_debug(" ruling out %s (profile not available)", prof->name);
2360 if (((direction & PA_DIRECTION_INPUT) && prof->n_sources > 0) ||
2361 ((direction & PA_DIRECTION_OUTPUT) && prof->n_sinks > 0) ) {
2362 if (make_active && prof->priority < make_active->priority)
2363 pa_log_debug(" ruling out %s (low priority)", prof->name);
2365 pa_log_debug(" considering %s", prof->name);
2370 pa_log_debug(" ruling out %s (direction)", prof->name);
2377 pa_log_debug("No suitable profile found. Frustrated and do nothing");
2379 if (make_active == card->active_profile)
2380 pa_log_debug("Profile %s already set. Do nothing", make_active->name);
2382 if (switch_off && nport) {
2383 pa_log_debug("Do not switch to %s as active ports are existing "
2384 "to the other direction", make_active->name);
2387 pa_log_debug("Set profile %s", make_active->name);
2389 if ((prof = pa_hashmap_get(card->profiles, make_active->name)) != NULL &&
2390 pa_card_set_profile(card, prof, false) < 0) {
2391 pa_log_debug("Failed to change profile to %s",
2399 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
2401 struct userdata *u = d;
2407 pa_log_debug("deferred routing starts");
2409 mir_router_make_routing(u);
2413 static void schedule_deferred_routing(struct userdata *u)
2418 pa_assert_se((core = u->core));
2420 pa_log_debug("scheduling deferred routing");
2422 pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
2426 static void card_check_cb(pa_mainloop_api *m, void *d)
2428 card_check_t *cc = d;
2434 int n_sink, n_source;
2440 pa_assert((u = cc->u));
2441 pa_assert((core = u->core));
2443 pa_log_debug("card check starts");
2445 if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
2446 pa_log_debug("card %u is gone", cc->index);
2448 n_sink = n_source = 0;
2450 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
2451 if ((sink->card) && sink->card->index == card->index)
2455 PA_IDXSET_FOREACH(source, core->sources, idx) {
2456 if ((source->card) && source->card->index == card->index)
2460 if (n_sink || n_source) {
2461 pa_log_debug("found %u sinks and %u sources belonging to "
2462 "'%s' card", n_sink, n_source, card->name);
2463 pa_log_debug("nothing to do");
2466 pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
2468 mir_router_make_routing(u);
2476 static void schedule_card_check(struct userdata *u, pa_card *card)
2483 pa_assert_se((core = u->core));
2485 pa_log_debug("scheduling card check");
2487 cc = pa_xnew0(card_check_t, 1);
2489 cc->index = card->index;
2491 pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
2495 static void source_cleanup_cb(pa_mainloop_api *m, void *d)
2497 source_cleanup_t *sc = d;
2504 pa_assert((u = sc->u));
2505 pa_assert((core = u->core));
2507 pa_log_debug("source cleanup starts");
2509 pa_loopback_destroy(u->loopback, u->core, sc->loop);
2510 pa_multiplex_destroy(u->multiplex, u->core, sc->mux);
2512 pa_log_debug("source cleanup ends");
2518 static void schedule_source_cleanup(struct userdata *u, mir_node *node)
2521 source_cleanup_t *sc;
2525 pa_assert_se((core = u->core));
2527 pa_log_debug("scheduling source cleanup");
2529 sc = pa_xnew0(source_cleanup_t, 1);
2531 sc->mux = node->mux;
2532 sc->loop = node->loop;
2537 pa_mainloop_api_once(core->mainloop, source_cleanup_cb, sc);
2542 static void stream_uncork_cb(pa_mainloop_api *m, void *d)
2544 stream_uncork_t *suc = d;
2548 pa_sink_input *sinp;
2554 pa_assert((u = suc->u));
2555 pa_assert((core = u->core));
2557 pa_log_debug("start uncorking stream");
2559 if (!(sink = pa_idxset_get_by_index(core->sinks, suc->index))) {
2560 pa_log_debug("sink.%d gone", suc->index);
2564 if (!(sinp = pa_idxset_first(core->sink_inputs, &index))) {
2565 pa_log_debug("sink_input is gone");
2569 pa_sink_input_cork(sinp, false);
2571 pa_log_debug("stream.%u uncorked", sinp->index);
2579 static void schedule_stream_uncorking(struct userdata *u, pa_sink *sink)
2582 stream_uncork_t *suc;
2586 pa_assert_se((core = u->core));
2588 pa_log_debug("scheduling stream uncorking");
2590 suc = pa_xnew0(stream_uncork_t, 1);
2592 suc->index = sink->index;
2594 pa_mainloop_api_once(core->mainloop, stream_uncork_cb, suc);
2601 * indent-tabs-mode: nil