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")) {
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"))
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;
268 pa_assert_se((core = u->core));
269 pa_assert_se((discover = u->discover));
272 if ((bus = pa_utils_get_card_bus(card)) == NULL) {
273 pa_log_debug("ignoring profile change on card '%s' due to lack of '%s'"
274 "property", pa_utils_get_card_name(card),
279 pci = pa_streq(bus, "pci");
280 usb = pa_streq(bus, "usb");
281 bluetooth = pa_streq(bus, "bluetooth");
283 if (!pci && !usb && !bluetooth) {
284 pa_log_debug("ignoring profile change on card '%s' due to unsupported "
285 "bus type '%s'", pa_utils_get_card_name(card), bus);
286 u->state.sink = u->state.source = PA_IDXSET_INVALID;
290 if ((index = u->state.sink) != PA_IDXSET_INVALID) {
291 if ((sink = pa_idxset_get_by_index(core->sinks, index)))
292 pa_discover_add_sink(u, sink, true);
294 pa_log_debug("sink.%u is gone", index);
295 u->state.sink = PA_IDXSET_INVALID;
298 if ((index = u->state.source) != PA_IDXSET_INVALID) {
299 if ((source = pa_idxset_get_by_index(core->sources, index)))
300 pa_discover_add_source(u, source);
302 pa_log_debug("source.%u is gone", index);
303 u->state.source = PA_IDXSET_INVALID;
307 pa_assert_se((prof = card->active_profile));
309 pa_log_debug("bluetooth profile changed to '%s' on card '%s'",
310 prof->name, card->name);
312 if (!prof->n_sinks && !prof->n_sources) {
313 /* switched off but not unloaded yet */
314 need_routing = false;
316 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
317 if (node->implement == mir_device &&
318 node->pacard.index == card->index)
320 if (node->type != mir_bluetooth_a2dp &&
321 node->type != mir_bluetooth_sco)
323 if (node->available) {
324 node->available = false;
332 schedule_deferred_routing(u);
336 pa_log_debug("alsa profile changed to '%s' on card '%s'",
337 card->active_profile->name, card->name);
339 stamp = pa_utils_get_stamp();
341 handle_alsa_card(u, card);
343 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
344 if (node->implement == mir_device &&
345 node->pacard.index == card->index &&
348 destroy_node(u, node);
355 void pa_discover_port_available_changed(struct userdata *u,
356 pa_device_port *port)
367 pa_direction_t direction;
372 pa_assert_se((core = u->core));
374 switch (port->available) {
375 case PA_AVAILABLE_NO: state = "not available"; break;
376 case PA_AVAILABLE_YES: state = "available"; break;
377 default: state = "unknown"; break;
380 pa_log_debug("port '%s' availabilty changed to %s. Updating",
388 while ((node = pa_utils_get_node_from_port(u, port, &iter))) {
390 available = get_bluetooth_port_availability(node, port);
391 route |= update_node_availability(u, node, available);
392 direction |= (node->direction == mir_input) ? PA_DIRECTION_INPUT : PA_DIRECTION_OUTPUT;
396 set_bluetooth_profile(u, port->card, direction);
398 switch (port->available) {
399 case PA_AVAILABLE_NO: available = false; break;
400 case PA_AVAILABLE_YES: available = true; break;
401 default: /* do nothing */ return;
404 if (port->direction == PA_DIRECTION_OUTPUT) {
405 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
407 if (port == pa_hashmap_get(sink->ports, port->name)) {
408 pa_log_debug(" sink '%s'", sink->name);
409 route |= update_node_availability_by_device(
418 if (port->direction == PA_DIRECTION_INPUT) {
419 PA_IDXSET_FOREACH(source, core->sources, idx) {
421 if (port == pa_hashmap_get(source->ports, port->name)) {
422 pa_log_debug(" source '%s'", source->name);
423 route |= update_node_availability_by_device(
434 mir_router_make_routing(u);
437 void pa_discover_add_sink(struct userdata *u, pa_sink *sink, bool route)
439 static pa_nodeset_resdef def_resdef = {0, {0, 0}};
442 pa_discover *discover;
449 const char *loopback_role;
451 pa_nodeset_resdef *resdef;
453 pa_nodeset_resdef rdbuf;
461 pa_assert_se((core = u->core));
462 pa_assert_se((discover = u->discover));
464 module = sink->module;
466 if ((card = sink->card)) {
467 if (!(key = node_key(u, mir_output,sink,ACTIVE_PORT, kbf,sizeof(kbf))))
469 if (!(node = pa_discover_find_node_by_key(u, key))) {
470 if (u->state.profile)
471 pa_log_debug("can't find node for sink (key '%s')", key);
473 u->state.sink = sink->index;
476 pa_log_debug("node for '%s' found (key %s). Updating with sink data",
477 node->paname, node->key);
478 node->paidx = sink->index;
479 node->available = true;
480 pa_discover_add_node_to_ptr_hash(u, sink, node);
482 if ((loopback_role = pa_classify_loopback_stream(node))) {
483 if (!(ns = pa_utils_get_null_source(u))) {
484 pa_log("Can't load loopback module: no initial null source");
488 map = pa_nodeset_get_map_by_role(u, loopback_role);
489 make_rset = (map && map->resdef);
490 resdef = make_rset ? map->resdef : &def_resdef;
492 node->loop = pa_loopback_create(u->loopback, core,
493 PA_LOOPBACK_SINK, node->index,
494 ns->index, sink->index,
498 resdef->flags.audio);
500 mir_node_print(node, nbf, sizeof(nbf));
501 pa_log_debug("updated node:\n%s", nbf);
504 pa_murphyif_create_resource_set(u, node, resdef);
510 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
511 mir_router_make_routing(u);
513 if (!u->state.profile)
514 schedule_deferred_routing(u);
518 else if (!module || !pa_streq(module->name, "module-combine-sink")) {
521 memset(&data, 0, sizeof(data));
522 data.key = pa_xstrdup(sink->name);
523 data.direction = mir_output;
524 data.implement = mir_device;
525 data.channels = sink->channel_map.channels;
526 data.available = true;
527 data.paidx = sink->index;
529 if (sink == pa_utils_get_null_sink(u)) {
530 data.visible = false;
531 data.type = mir_null;
532 data.amname = pa_xstrdup("Silent");
533 data.amid = AM_ID_INVALID;
534 data.paname = pa_xstrdup(sink->name);
536 else if (pa_classify_node_by_property(&data, sink->proplist)) {
537 data.privacy = mir_public;
539 data.amname = pa_xstrdup(mir_node_type_str(data.type));
540 data.amid = AM_ID_INVALID;
541 data.paname = pa_xstrdup(sink->name);
546 pa_xfree(data.key); /* for now */
547 pa_log_info("currently we do not support statically loaded "
548 "sinks without " PA_PROP_NODE_TYPE " property");
552 node = create_node(u, &data, NULL);
555 pa_discover_add_node_to_ptr_hash(u, sink, node);
560 void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
562 pa_discover *discover;
569 pa_assert_se((discover = u->discover));
571 name = pa_utils_get_sink_name(sink);
573 if (!(node = pa_hashmap_get(discover->nodes.byptr, sink)))
574 pa_log_debug("can't find node for sink (name '%s')", name);
576 pa_log_debug("node found for '%s'. Reseting sink data", name);
577 pa_murphyif_destroy_resource_set(u, node);
578 schedule_source_cleanup(u, node);
579 node->paidx = PA_IDXSET_INVALID;
580 pa_hashmap_remove(discover->nodes.byptr, sink);
585 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
586 node->available = false;
588 if (!u->state.profile)
589 schedule_deferred_routing(u);
593 pa_log_info("currently we do not support statically loaded sinks");
599 void pa_discover_add_source(struct userdata *u, pa_source *source)
601 static pa_nodeset_resdef def_resdef = {0, {0, 0}};
604 pa_discover *discover;
610 const char *loopback_role;
612 pa_nodeset_resdef *resdef;
620 pa_assert_se((core = u->core));
621 pa_assert_se((discover = u->discover));
623 if ((card = source->card)) {
624 if (!(key = node_key(u,mir_input,source,ACTIVE_PORT,kbf,sizeof(kbf))))
626 if (!(node = pa_discover_find_node_by_key(u, key))) {
627 if (u->state.profile)
628 pa_log_debug("can't find node for source (key '%s')", key);
630 u->state.source = source->index;
633 pa_log_debug("node for '%s' found. Updating with source data",
635 node->paidx = source->index;
636 node->available = true;
637 pa_discover_add_node_to_ptr_hash(u, source, node);
638 if ((loopback_role = pa_classify_loopback_stream(node))) {
639 if (!(ns = pa_utils_get_null_sink(u))) {
640 pa_log("Can't load loopback module: no initial null sink");
644 map = pa_nodeset_get_map_by_role(u, loopback_role);
645 make_rset = (map && map->resdef);
646 resdef = make_rset ? map->resdef : &def_resdef;
648 node->loop = pa_loopback_create(u->loopback, core,
649 PA_LOOPBACK_SOURCE, node->index,
650 source->index, ns->index,
654 resdef->flags.audio);
656 sink_index = pa_loopback_get_sink_index(core, node->loop);
657 node->mux = pa_multiplex_find_by_sink(u->multiplex,sink_index);
660 mir_node_print(node, nbf, sizeof(nbf));
661 pa_log_debug("updated node:\n%s", nbf);
664 pa_murphyif_create_resource_set(u, node, resdef);
666 pa_fader_apply_volume_limits(u, node->stamp);
670 memset(&data, 0, sizeof(data));
671 data.key = pa_xstrdup(source->name);
672 data.direction = mir_input;
673 data.implement = mir_device;
674 data.channels = source->channel_map.channels;
675 data.available = true;
677 if (source == pa_utils_get_null_source(u)) {
678 data.visible = false;
679 data.type = mir_null;
680 data.amname = pa_xstrdup("Silent");
681 data.amid = AM_ID_INVALID;
682 data.paname = pa_xstrdup(source->name);
683 data.paidx = source->index;
685 else if (pa_classify_node_by_property(&data, source->proplist)) {
687 data.amname = pa_xstrdup(mir_node_type_str(data.type));
688 data.amid = AM_ID_INVALID;
689 data.paname = pa_xstrdup(source->name);
692 pa_xfree(data.key); /* for now */
693 pa_log_info("currently we do not support statically loaded "
694 "sources without " PA_PROP_NODE_TYPE " property");
698 create_node(u, &data, NULL);
703 void pa_discover_remove_source(struct userdata *u, pa_source *source)
705 pa_discover *discover;
712 pa_assert_se((discover = u->discover));
714 name = pa_utils_get_source_name(source);
716 if (!(node = pa_hashmap_get(discover->nodes.byptr, source)))
717 pa_log_debug("can't find node for source (name '%s')", name);
719 pa_log_debug("node found. Reseting source data");
720 pa_murphyif_destroy_resource_set(u, node);
721 schedule_source_cleanup(u, node);
722 node->paidx = PA_IDXSET_INVALID;
723 pa_hashmap_remove(discover->nodes.byptr, source);
728 if (type != mir_bluetooth_sco)
729 node->available = false;
731 if (!u->state.profile)
732 schedule_deferred_routing(u);
736 pa_log_info("currently we do not support statically "
743 void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
746 pa_discover *discover;
757 pa_nodeset_resdef *resdef;
761 pa_assert_se((core = u->core));
762 pa_assert_se((discover = u->discover));
763 pa_assert_se((pl = sinp->proplist));
765 if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME))) {
766 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
767 pa_log_debug("Seems to be a combine stream. Nothing to do ...");
770 if (!strncmp(media, loopback_outpatrn, sizeof(loopback_outpatrn)-1)) {
771 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
776 name = pa_utils_get_sink_input_name(sinp);
778 pa_log_debug("registering input stream '%s'", name);
780 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
781 pa_log_debug("cant find stream class for '%s'. "
782 "Leaving it alone", name);
786 pa_utils_set_stream_routing_properties(pl, type, NULL);
788 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
790 memset(&data, 0, sizeof(data));
792 data.direction = mir_input;
793 data.implement = mir_stream;
794 data.channels = sinp->channel_map.channels;
796 data.zone = pa_utils_get_zone(sinp->proplist);
798 data.available = true;
800 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
801 data.amid = AM_ID_INVALID;
803 data.paidx = sinp->index;
804 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
807 * here we can't guess whether the application requested an explicit
808 * route by sepcifying the target sink @ stream creation time.
810 * the brute force solution: we make a default route for this stream
811 * possibly overwiriting the orginal app request :(
813 /* this will set data.mux */
814 role = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_ROLE);
815 sink = make_output_prerouting(u, &data, &sinp->channel_map, role, &target);
817 node = create_node(u, &data, NULL);
819 pa_discover_add_node_to_ptr_hash(u, sinp, node);
821 if (sink && target) {
822 pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
824 if (pa_sink_input_move_to(sinp, sink, false) < 0)
825 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
827 pa_audiomgr_add_default_route(u, node, target);
831 bool pa_discover_preroute_sink_input(struct userdata *u,
832 pa_sink_input_new_data *data)
837 pa_discover *discover;
838 pa_multiplex *multiplex;
847 pa_nodeset_resdef *resdef;
853 pa_assert_se((core = u->core));
854 pa_assert_se((discover = u->discover));
855 pa_assert_se((multiplex = u->multiplex));
856 pa_assert_se((pl = data->proplist));
858 mnam = (m = data->module) ? m->name : "";
860 if (pa_streq(mnam, "module-combine-sink")) {
862 type = mir_node_type_unknown;
864 if (!(mux = pa_multiplex_find_by_module(multiplex, m)) ||
865 !(sink = pa_idxset_get_by_index(core->sinks, mux->sink_index)) ||
866 !(sinp = pa_idxset_first(sink->inputs, NULL)) ||
867 !(type = pa_utils_get_stream_class(sinp->proplist)))
869 pa_log_debug("can't figure out the type of multiplex stream");
872 pa_utils_set_stream_routing_properties(data->proplist, type, NULL);
876 loopback = pa_streq(mnam, "module-loopback");
880 if (!(node = pa_utils_get_node_from_data(u, mir_input, data))) {
881 pa_log_debug("can't find loopback node for sink-input");
885 if (node->direction == mir_output) {
886 pa_log_debug("refuse to preroute loopback sink-input "
887 "(current route: sink %u @ %p)", data->sink ?
888 data->sink->index : PA_IDXSET_INVALID,data->sink);
894 type = pa_classify_guess_stream_node_type(u, pl, NULL);
897 remap = pa_streq(mnam, "module-remap-sink");
898 type = pa_classify_guess_stream_node_type(u, pl, &resdef);
900 pa_utils_set_resource_properties(pl, resdef);
902 if (pa_stream_state_start_corked(u, data, resdef)) {
903 pa_log_debug("start corked");
907 pa_utils_set_stream_routing_properties(pl, type, data->sink);
910 memset(&fake, 0, sizeof(fake));
911 fake.direction = mir_input;
912 fake.implement = mir_stream;
916 fake.channels = data->channel_map.channels;
917 fake.zone = pa_utils_get_zone(data->proplist);
919 fake.available = true;
920 fake.amname = "<preroute sink-input>";
921 fake.amid = AM_ID_INVALID;
922 fake.paidx = PA_IDXSET_INVALID;
924 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
925 sink = make_output_prerouting(u, &fake, &data->channel_map, role,NULL);
929 if (fake.mux && !(data->flags & PA_SINK_INPUT_START_CORKED)) {
930 data->flags |= PA_SINK_INPUT_START_CORKED;
931 schedule_stream_uncorking(u, sink);
935 if (pa_sink_input_new_data_set_sink(data, sink, false))
936 pa_log_debug("set sink %u for new sink-input", sink->index);
938 pa_log("can't set sink %u for new sink-input", sink->index);
939 /* copes wit NULL mux */
940 pa_multiplex_destroy(u->multiplex, core, fake.mux);
950 if (loopback && data->sink && data->sink->module) {
952 if (pa_streq(data->sink->module->name, "module-combine-sink"))
956 if (pa_classify_ramping_stream(&fake)) {
957 pa_log_debug("set sink-input ramp-muted");
958 data->flags |= PA_SINK_INPUT_START_RAMP_MUTED;
965 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
969 pa_sink_input *csinp;
971 pa_discover *discover;
972 pa_multiplex *multiplex;
982 pa_nodeset_resdef *resdef;
983 pa_nodeset_resdef rdbuf;
987 pa_assert_se((core = u->core));
988 pa_assert_se((discover = u->discover));
989 pa_assert_se((multiplex = u->multiplex));
990 pa_assert_se((pl = sinp->proplist));
994 if (!(media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)))
997 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
998 if (!pa_utils_stream_has_default_route(sinp->proplist) ||
999 !(mux = pa_multiplex_find_by_module(multiplex, sinp->module)) ||
1000 mux->defstream_index != PA_IDXSET_INVALID)
1002 pa_log_debug("New stream is a combine stream. Nothing to do ...");
1005 pa_log_debug("New stream is a combine stream. Setting as default");
1006 mux->defstream_index = sinp->index;
1007 mir_router_make_routing(u);
1010 } else if (!strncmp(media, loopback_outpatrn,sizeof(loopback_outpatrn)-1)){
1011 pa_log_debug("New stream is a loopback output stream");
1013 if ((node = pa_utils_get_node_from_stream(u, mir_input, sinp))) {
1014 if (node->direction == mir_input)
1015 pa_log_debug("loopback stream node '%s' found", node->amname);
1017 pa_log_debug("ignoring it");
1022 pa_log_debug("can't find node for the loopback stream");
1029 name = pa_utils_get_sink_input_name(sinp);
1031 pa_log_debug("dealing with new input stream '%s'", name);
1033 if ((type = get_stream_routing_class(pl)))
1034 resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1036 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1037 pa_log_debug("cant find stream class for '%s'. "
1038 "Leaving it alone", name);
1042 pa_utils_set_stream_routing_properties(pl, type, NULL);
1044 /* if needed, make some post-routing here */
1047 /* we need to add this to main hashmap as that is used for loop
1048 through on all nodes. */
1049 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
1051 memset(&data, 0, sizeof(data));
1053 data.direction = mir_input;
1054 data.implement = mir_stream;
1055 data.channels = sinp->channel_map.channels;
1057 data.zone = pa_utils_get_zone(pl);
1058 data.visible = true;
1059 data.available = true;
1061 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1062 data.amid = AM_ID_INVALID;
1064 data.paidx = sinp->index;
1065 data.mux = pa_multiplex_find_by_sink(u->multiplex,
1067 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1068 node = create_node(u, &data, &created);
1073 pa_log("%s: confused with stream. '%s' did exists",
1074 __FILE__, node->amname);
1079 pa_murphyif_add_node(u, node);
1081 pa_murphyif_create_resource_set(u, node, resdef);
1083 pa_discover_add_node_to_ptr_hash(u, sinp, node);
1088 csinp = pa_idxset_get_by_index(core->sink_inputs,
1089 data.mux->defstream_index);
1090 s = csinp ? csinp->sink : NULL;
1092 if ((sinp->flags & PA_SINK_INPUT_START_RAMP_MUTED)) {
1093 pa_log_debug("ramp '%s' to 100%", media);
1094 pa_fader_ramp_volume(u, sinp, PA_VOLUME_NORM);
1100 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1102 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1103 pa_log_debug("can't figure out where this stream is routed");
1105 pa_log_debug("register route '%s' => '%s'",
1106 node->amname, snod->amname);
1108 if (pa_utils_stream_has_default_route(sinp->proplist))
1109 pa_audiomgr_add_default_route(u, node, snod);
1111 /* FIXME: register explicit routes */
1112 /* else pa_audiomgr_add/register_explicit_route() */
1115 pa_fader_apply_volume_limits(u, pa_utils_get_stamp());
1120 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
1122 pa_discover *discover;
1126 bool had_properties;
1130 pa_assert_se((discover = u->discover));
1132 name = pa_utils_get_sink_input_name(sinp);
1134 pa_log_debug("sink-input '%s' going to be destroyed", name);
1136 had_properties = pa_utils_unset_stream_routing_properties(sinp->proplist);
1138 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sinp))) {
1139 if (!pa_multiplex_sink_input_remove(u->multiplex, sinp))
1140 pa_log_debug("nothing to do for sink-input (name '%s')", name);
1143 pa_log_debug("node found for '%s'. After clearing routes "
1144 "it will be destroyed", name);
1146 if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
1147 pa_log_debug("can't figure out where this stream is routed");
1149 pa_log_debug("clear route '%s' => '%s'",
1150 node->amname, sinknod->amname);
1152 /* FIXME: and actually do it ... */
1156 destroy_node(u, node);
1159 if (node || had_properties)
1160 mir_router_make_routing(u);
1164 void pa_discover_register_source_output(struct userdata *u,
1165 pa_source_output *sout)
1168 pa_discover *discover;
1179 pa_nodeset_resdef *resdef;
1183 pa_assert_se((core = u->core));
1184 pa_assert_se((discover = u->discover));
1185 pa_assert_se((pl = sout->proplist));
1187 if ((media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME))) {
1188 if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1189 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
1194 name = pa_utils_get_source_output_name(sout);
1196 pa_log_debug("registering output stream '%s'", name);
1198 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1199 pa_log_debug("cant find stream class for '%s'. "
1200 "Leaving it alone", name);
1204 pa_utils_set_stream_routing_properties(pl, type, NULL);
1206 snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1208 memset(&data, 0, sizeof(data));
1210 data.direction = mir_output;
1211 data.implement = mir_stream;
1212 data.channels = sout->channel_map.channels;
1214 data.zone = pa_utils_get_zone(sout->proplist);
1215 data.visible = true;
1216 data.available = true;
1218 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1219 data.amid = AM_ID_INVALID;
1221 data.paidx = sout->index;
1222 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1225 * here we can't guess whether the application requested an explicit
1226 * route by sepcifying the target source @ stream creation time.
1228 * the brute force solution: we make a default route for this stream
1229 * possibly overwiriting the orginal app request :(
1231 role = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_ROLE);
1232 source = make_input_prerouting(u, &data, role, &target);
1234 node = create_node(u, &data, NULL);
1236 pa_discover_add_node_to_ptr_hash(u, sout, node);
1238 if (source && target) {
1239 pa_log_debug("move stream to source %u (%s)",
1240 source->index, source->name);
1242 if (pa_source_output_move_to(sout, source, false) < 0)
1243 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
1245 pa_log_debug("register route '%s' => '%s'",
1246 node->amname, target->amname);
1247 /* FIXME: and actually do it ... */
1252 bool pa_discover_preroute_source_output(struct userdata *u,
1253 pa_source_output_new_data *data)
1258 pa_discover *discover;
1265 pa_nodeset_resdef *resdef;
1269 pa_assert_se((core = u->core));
1270 pa_assert_se((discover = u->discover));
1271 pa_assert_se((pl = data->proplist));
1273 mnam = (m = data->module) ? m->name : "";
1275 if (pa_streq(mnam, "module-loopback")) {
1276 if (!(node = pa_utils_get_node_from_data(u, mir_output, data))) {
1277 pa_log_debug("can't find loopback node for source-output");
1281 if (node->direction == mir_input) {
1282 pa_log_debug("refuse to preroute loopback source-output "
1283 "(current route: source %u @ %p)", data->source ?
1284 data->source->index : PA_IDXSET_INVALID,data->source);
1288 data->source = NULL;
1290 type = pa_classify_guess_stream_node_type(u, pl, NULL);
1293 type = pa_classify_guess_stream_node_type(u, pl, &resdef);
1295 pa_utils_set_resource_properties(pl, resdef);
1298 pa_utils_set_stream_routing_properties(pl, type, data->source);
1300 if (!data->source) {
1301 memset(&fake, 0, sizeof(fake));
1302 fake.direction = mir_output;
1303 fake.implement = mir_stream;
1304 fake.channels = data->channel_map.channels;
1306 fake.zone = pa_utils_get_zone(data->proplist);
1307 fake.visible = true;
1308 fake.available = true;
1309 fake.amname = "<preroute source-output>";
1311 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
1312 source = make_input_prerouting(u, &fake, role, NULL);
1315 if (pa_source_output_new_data_set_source(data, source, false)) {
1316 pa_log_debug("set source %u for new source-output",
1320 pa_log("can't set source %u for new source-output",
1330 void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
1335 pa_discover *discover;
1344 pa_nodeset_resdef *resdef;
1345 pa_nodeset_resdef rdbuf;
1349 pa_assert_se((core = u->core));
1350 pa_assert_se((discover = u->discover));
1351 pa_assert_se((pl = sout->proplist));
1355 if (!(media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME)))
1356 media = "<unknown>";
1358 if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1359 pa_log_debug("New stream is a loopback input stream");
1361 if ((node = pa_utils_get_node_from_stream(u, mir_output, sout))) {
1362 if (node->direction == mir_output)
1363 pa_log_debug("loopback stream node '%s' found", node->amname);
1365 pa_log_debug("ignoring it");
1370 pa_log_debug("can't find node for the loopback stream");
1375 name = pa_utils_get_source_output_name(sout);
1377 pa_log_debug("dealing with new output stream '%s'", name);
1379 if ((type = get_stream_routing_class(pl)))
1380 resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1382 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1383 pa_log_debug("cant find stream class for '%s'. "
1384 "Leaving it alone", name);
1388 pa_utils_set_stream_routing_properties(pl, type, NULL);
1390 /* if needed, make some post-routing here */
1393 /* we need to add this to main hashmap as that is used for loop
1394 through on all nodes. */
1395 snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1397 memset(&data, 0, sizeof(data));
1399 data.direction = mir_output;
1400 data.implement = mir_stream;
1401 data.channels = sout->channel_map.channels;
1403 data.zone = pa_utils_get_zone(pl);
1404 data.visible = true;
1405 data.available = true;
1407 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1408 data.amid = AM_ID_INVALID;
1410 data.paidx = sout->index;
1411 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1413 node = create_node(u, &data, &created);
1418 pa_log("%s: confused with stream. '%s' did exists",
1419 __FILE__, node->amname);
1424 pa_murphyif_add_node(u, node);
1426 pa_murphyif_create_resource_set(u, node, resdef);
1428 pa_discover_add_node_to_ptr_hash(u, sout, node);
1431 if ((s = sout->source))
1432 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1434 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1435 pa_log_debug("can't figure out where this stream is routed");
1437 pa_log_debug("register route '%s' => '%s'",
1438 snod->amname, node->amname);
1439 pa_audiomgr_add_default_route(u, node, snod);
1444 void pa_discover_remove_source_output(struct userdata *u,
1445 pa_source_output *sout)
1447 pa_discover *discover;
1454 pa_assert_se((discover = u->discover));
1456 name = pa_utils_get_source_output_name(sout);
1458 pa_log_debug("source-output '%s' going to be destroyed", name);
1460 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sout)))
1461 pa_log_debug("can't find node for source-output (name '%s')", name);
1463 pa_log_debug("node found for '%s'. After clearing routes "
1464 "it will be destroyed", name);
1466 if (!(srcnod = pa_hashmap_get(discover->nodes.byptr, sout->source)))
1467 pa_log_debug("can't figure out where this stream is routed");
1469 pa_log_debug("clear route '%s' => '%s'",
1470 node->amname, srcnod->amname);
1472 /* FIXME: and actually do it ... */
1476 destroy_node(u, node);
1478 mir_router_make_routing(u);
1483 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
1485 pa_discover *discover;
1489 pa_assert_se((discover = u->discover));
1492 node = pa_hashmap_get(discover->nodes.byname, key);
1499 mir_node *pa_discover_find_node_by_ptr(struct userdata *u, void *ptr)
1501 pa_discover *discover;
1505 pa_assert_se((discover = u->discover));
1508 node = pa_hashmap_get(discover->nodes.byptr, ptr);
1515 void pa_discover_add_node_to_ptr_hash(struct userdata *u,
1519 pa_discover *discover;
1524 pa_assert_se((discover = u->discover));
1526 pa_hashmap_put(discover->nodes.byptr, ptr, node);
1529 mir_node *pa_discover_remove_node_from_ptr_hash(struct userdata *u, void *ptr)
1531 pa_discover *discover;
1535 pa_assert_se((discover = u->discover));
1537 return pa_hashmap_remove(discover->nodes.byptr, ptr);
1541 static void handle_alsa_card(struct userdata *u, pa_card *card)
1548 memset(&data, 0, sizeof(data));
1549 data.zone = pa_utils_get_zone(card->proplist);
1550 data.visible = true;
1551 data.amid = AM_ID_INVALID;
1552 data.implement = mir_device;
1553 data.paidx = PA_IDXSET_INVALID;
1554 data.stamp = pa_utils_get_stamp();
1556 cnam = pa_utils_get_card_name(card);
1557 udd = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
1559 if (udd && pa_streq(udd, "1")) {
1560 /* udev loaded alsa card */
1561 if (!strncmp(cnam, "alsa_card.", 10)) {
1563 handle_udev_loaded_card(u, card, &data, cid);
1568 /* statically loaded pci or usb card */
1571 pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
1576 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1578 pa_discover *discover;
1579 pa_card_profile *prof;
1587 char paname[MAX_NAME_LENGTH+1];
1588 char amname[MAX_NAME_LENGTH+1];
1589 char key[MAX_NAME_LENGTH+1];
1591 pa_assert_se((discover = u->discover));
1593 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1596 memset(paname, 0, sizeof(paname));
1597 memset(amname, 0, sizeof(amname));
1598 memset(key , 0, sizeof(key) );
1600 memset(&data, 0, sizeof(data));
1602 data.visible = true;
1603 data.amid = AM_ID_INVALID;
1604 data.implement = mir_device;
1605 data.paidx = PA_IDXSET_INVALID;
1606 data.paname = paname;
1607 data.amname = amname;
1608 data.amdescr = (char *)cdescr;
1609 data.pacard.index = card->index;
1610 data.stamp = pa_utils_get_stamp();
1612 cnam = pa_utils_get_card_name(card);
1614 if (!strncmp(cnam, "bluez_card.", 11)) {
1617 pa_assert(card->ports);
1619 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1621 PA_HASHMAP_FOREACH(prof, card->profiles, cstate) {
1622 data.available = false;
1623 data.pacard.profile = prof->name;
1625 if (prof->n_sinks > 0) {
1626 data.direction = mir_output;
1627 data.channels = prof->max_sink_channels;
1628 data.amname = amname;
1630 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1631 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1632 pa_classify_node_by_card(&data, card, prof, NULL);
1633 node = create_node(u, &data, NULL);
1634 mir_constrain_add_node(u, cd, node);
1637 if (prof->n_sources > 0) {
1638 data.direction = mir_input;
1639 data.channels = prof->max_source_channels;
1640 data.amname = amname;
1642 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1643 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1644 pa_classify_node_by_card(&data, card, prof, NULL);
1645 node = create_node(u, &data, NULL);
1646 mir_constrain_add_node(u, cd, node);
1650 if (!(prof = card->active_profile))
1651 pa_log("card '%s' has no active profile", card->name);
1653 pa_log_debug("card '%s' default profile '%s'",
1654 card->name, prof->name);
1657 schedule_card_check(u, card);
1663 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1665 pa_discover *discover;
1666 pa_card_profile *prof;
1667 pa_device_port *port;
1674 void *state0, *state1;
1675 char paname[MAX_NAME_LENGTH+1];
1676 char amname[MAX_NAME_LENGTH+1];
1677 char key[MAX_NAME_LENGTH+1];
1682 pa_assert_se((discover = u->discover));
1684 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1687 memset(paname, 0, sizeof(paname));
1688 memset(amname, 0, sizeof(amname));
1689 memset(key , 0, sizeof(key) );
1691 memset(&data, 0, sizeof(data));
1693 data.zone = pa_utils_get_zone(card->proplist);
1694 data.visible = true;
1695 data.amid = AM_ID_INVALID;
1696 data.implement = mir_device;
1697 data.paidx = PA_IDXSET_INVALID;
1698 data.paname = paname;
1699 data.amname = amname;
1700 data.amdescr = (char *)cdescr;
1701 data.pacard.index = card->index;
1702 data.stamp = pa_utils_get_stamp();
1704 cnam = pa_utils_get_card_name(card);
1706 if (!strncmp(cnam, "bluez_card.", 11)) {
1709 pa_assert(card->ports);
1711 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1713 PA_HASHMAP_FOREACH(port, card->ports, state0) {
1714 pa_assert(port->profiles);
1717 input = output = true;
1718 len = strlen(port->name);
1719 if (len >= 6 && !strcmp("-input", port->name + (len-6)))
1721 else if (len >= 7 && !strcmp("-output", port->name + (len-7)))
1725 PA_HASHMAP_FOREACH(prof, port->profiles, state1) {
1726 data.pacard.profile = prof->name;
1727 data.available = get_bluetooth_port_availability(&data, port);
1729 if (output && prof->n_sinks > 0) {
1730 data.direction = mir_output;
1731 data.channels = prof->max_sink_channels;
1732 data.amname = amname;
1734 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1735 snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1736 pa_classify_node_by_card(&data, card, prof, NULL);
1737 node = create_node(u, &data, NULL);
1738 mir_constrain_add_node(u, cd, node);
1739 pa_utils_set_port_properties(port, node);
1742 if (input && prof->n_sources > 0) {
1743 data.direction = mir_input;
1744 data.channels = prof->max_source_channels;
1745 data.amname = amname;
1747 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1748 snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1749 pa_classify_node_by_card(&data, card, prof, NULL);
1750 node = create_node(u, &data, NULL);
1751 mir_constrain_add_node(u, cd, node);
1752 pa_utils_set_port_properties(port, node);
1757 if (!(prof = card->active_profile))
1758 pa_log("card '%s' has no active profile", card->name);
1760 pa_log_debug("card '%s' default profile '%s'",
1761 card->name, prof->name);
1764 schedule_card_check(u, card);
1768 static bool get_bluetooth_port_availability(mir_node *node,
1769 pa_device_port *port)
1771 bool available = false;
1777 if ((prof = node->pacard.profile)) {
1778 if (!strcmp(prof, "hfgw") ||
1779 !strcmp(prof, "a2dp_source") ||
1780 !strcmp(prof, "a2dp_sink"))
1781 available = (port->available != PA_AVAILABLE_NO);
1789 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
1790 mir_node *data, char *cardid)
1792 pa_discover *discover;
1793 pa_card_profile *prof;
1794 pa_card_profile *active;
1796 const char *alsanam;
1798 char *sinks[MAX_CARD_TARGET+1];
1799 char *sources[MAX_CARD_TARGET+1];
1800 char buf[MAX_NAME_LENGTH+1];
1801 char paname[MAX_NAME_LENGTH+1];
1802 char amname[MAX_NAME_LENGTH+1];
1806 pa_assert(card->profiles);
1807 pa_assert_se((discover = u->discover));
1809 alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
1811 memset(amname, 0, sizeof(amname));
1813 data->paname = paname;
1814 data->amname = amname;
1815 data->amdescr = (char *)alsanam;
1817 data->pacard.index = card->index;
1819 active = card->active_profile;
1821 PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1822 /* filtering: deal with selected profiles if requested so */
1823 if (discover->selected && (!active || (active && prof != active)))
1826 /* filtering: skip the 'off' profiles */
1827 if (!prof->n_sinks && !prof->n_sources)
1830 /* filtering: consider sinks with suitable amount channels */
1831 if (prof->n_sinks &&
1832 (prof->max_sink_channels < discover->chmin ||
1833 prof->max_sink_channels > discover->chmax ))
1836 /* filtering: consider sources with suitable amount channels */
1837 if (prof->n_sources &&
1838 (prof->max_source_channels < discover->chmin ||
1839 prof->max_source_channels > discover->chmax ))
1842 data->pacard.profile = prof->name;
1844 parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
1846 data->direction = mir_output;
1847 data->channels = prof->max_sink_channels;
1848 for (i = 0; (sid = sinks[i]); i++) {
1849 snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
1850 handle_card_ports(u, data, card, prof);
1853 data->direction = mir_input;
1854 data->channels = prof->max_source_channels;
1855 for (i = 0; (sid = sources[i]); i++) {
1856 snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
1857 handle_card_ports(u, data, card, prof);
1863 static void handle_card_ports(struct userdata *u, mir_node *data,
1864 pa_card *card, pa_card_profile *prof)
1866 mir_node *node = NULL;
1867 bool have_ports = false;
1868 mir_constr_def *cd = NULL;
1869 char *amname = data->amname;
1870 pa_device_port *port;
1873 char key[MAX_NAME_LENGTH+1];
1881 PA_HASHMAP_FOREACH(port, card->ports, state) {
1883 * if this port did not belong to any profile
1884 * (ie. prof->profiles == NULL) we assume that this port
1885 * does works with all the profiles
1887 if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
1888 ((port->direction == PA_DIRECTION_INPUT && data->direction == mir_input)||
1889 (port->direction == PA_DIRECTION_OUTPUT && data->direction == mir_output)))
1894 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
1897 data->available = (port->available != PA_AVAILABLE_NO);
1899 data->amname = amname;
1900 data->paport = port->name;
1902 pa_classify_node_by_card(data, card, prof, port);
1904 node = create_node(u, data, &created);
1907 node->stamp = data->stamp;
1909 cd = mir_constrain_create(u, "port", mir_constrain_port,
1911 mir_constrain_add_node(u, cd, node);
1918 data->key = data->paname;
1919 data->available = true;
1921 pa_classify_node_by_card(data, card, prof, NULL);
1923 node = create_node(u, data, &created);
1926 node->stamp = data->stamp;
1929 data->amname = amname;
1934 static mir_node *create_node(struct userdata *u, mir_node *data,
1937 pa_discover *discover;
1944 pa_assert(data->key);
1945 pa_assert(data->paname);
1946 pa_assert_se((discover = u->discover));
1948 if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
1953 node = mir_node_create(u, data);
1954 pa_hashmap_put(discover->nodes.byname, node->key, node);
1956 mir_node_print(node, buf, sizeof(buf));
1957 pa_log_debug("new node:\n%s", buf);
1959 if (node->available)
1960 pa_audiomgr_register_node(u, node);
1964 *created_ret = created;
1969 static void destroy_node(struct userdata *u, mir_node *node)
1971 pa_discover *discover;
1975 pa_assert_se((discover = u->discover));
1978 removed = pa_hashmap_remove(discover->nodes.byname, node->key);
1980 if (node != removed) {
1982 pa_log("%s: confused with data structures: key mismatch. "
1983 " attempted to destroy '%s'; actually destroyed '%s'",
1984 __FILE__, node->key, removed->key);
1986 pa_log("%s: confused with data structures: node '%s' "
1987 "is not in the hash table", __FILE__, node->key);
1991 pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
1993 if (node->implement == mir_stream) {
1994 if (node->direction == mir_input) {
1996 pa_log_debug("removing multiplexer");
2001 pa_audiomgr_unregister_node(u, node);
2003 extapi_signal_node_change(u);
2005 mir_constrain_remove_node(u, node);
2007 pa_loopback_destroy(u->loopback, u->core, node->loop);
2008 pa_multiplex_destroy(u->multiplex, u->core, node->mux);
2010 mir_node_destroy(u, node);
2014 static bool update_node_availability(struct userdata *u,
2021 if ((!available && node->available) ||
2022 ( available && !node->available) )
2024 node->available = available;
2027 pa_audiomgr_register_node(u, node);
2029 pa_audiomgr_unregister_node(u, node);
2031 extapi_signal_node_change(u);
2033 return true; /* routing needed */
2039 static bool update_node_availability_by_device(struct userdata *u,
2040 mir_direction direction,
2042 pa_device_port *port,
2052 pa_assert(direction == mir_input || direction == mir_output);
2054 if ((key = node_key(u, direction, data, port, buf, sizeof(buf)))) {
2055 if (!(node = pa_discover_find_node_by_key(u, key)))
2056 pa_log_debug(" can't find node (key '%s')", key);
2058 pa_log_debug(" node for '%s' found (key %s)",
2059 node->paname, node->key);
2061 return update_node_availability(u, node, available);
2065 return false; /* no routing needed */
2068 static char *get_name(char **string_ptr, int offs)
2070 char c, *name, *end;
2072 name = *string_ptr + offs;
2074 for (end = name; (c = *end); end++) {
2086 static void parse_profile_name(pa_card_profile *prof,
2096 pa_assert(prof->name);
2098 strncpy(buf, prof->name, buflen);
2099 buf[buflen-1] = '\0';
2101 memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2102 memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2105 if (!strncmp(p, "output:", 7)) {
2106 if (i >= MAX_CARD_TARGET) {
2107 pa_log_debug("number of outputs exeeds the maximum %d in "
2108 "profile name '%s'", MAX_CARD_TARGET, prof->name);
2111 sinks[i++] = get_name(&p, 7);
2113 else if (!strncmp(p, "input:", 6)) {
2114 if (j >= MAX_CARD_TARGET) {
2115 pa_log_debug("number of inputs exeeds the maximum %d in "
2116 "profile name '%s'", MAX_CARD_TARGET, prof->name);
2119 sources[j++] = get_name(&p, 6);
2122 pa_log("%s: failed to parse profile name '%s'",
2123 __FILE__, prof->name);
2130 static char *node_key(struct userdata *u, mir_direction direction,
2131 void *data, pa_device_port *port, char *buf, size_t len)
2134 pa_card_profile *profile;
2141 const char *profile_name;
2147 pa_assert(direction == mir_input || direction == mir_output);
2149 if (direction == mir_output) {
2150 pa_sink *sink = data;
2152 name = pa_utils_get_sink_name(sink);
2155 port = sink->active_port;
2158 pa_source *source = data;
2160 name = pa_utils_get_source_name(source);
2161 card = source->card;
2163 port = source->active_port;
2169 pa_assert_se((profile = card->active_profile));
2171 if (!u->state.profile)
2172 profile_name = profile->name;
2174 pa_log_debug("state.profile is not null. '%s' supresses '%s'",
2175 u->state.profile, profile->name);
2176 profile_name = u->state.profile;
2180 if (!(bus = pa_utils_get_card_bus(card))) {
2181 pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
2182 "on its card", type, name, PA_PROP_DEVICE_BUS);
2186 pci = pa_streq(bus, "pci");
2187 usb = pa_streq(bus, "usb");
2188 bluetooth = pa_streq(bus, "bluetooth");
2190 if (!pci && !usb && !bluetooth) {
2191 pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
2192 "of its card", type, name, bus);
2201 snprintf(buf, len, "%s@%s.%s", name, port->name, profile_name);
2209 snprintf(buf, len, "%s@%s", name, port->name);
2216 static pa_sink *make_output_prerouting(struct userdata *u,
2218 pa_channel_map *chmap,
2219 const char *media_role,
2220 mir_node **target_ret)
2224 pa_sink *sink = NULL;
2229 pa_assert_se((core = u->core));
2232 target = mir_router_make_prerouting(u, data);
2235 pa_log("there is no default route for the stream '%s'", data->amname);
2236 else if (target->paidx == PA_IDXSET_INVALID)
2237 pa_log("can't route to default '%s': no sink", target->amname);
2239 if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
2240 pa_log("no route to default '%s': sink is gone", target->amname);
2242 if (pa_classify_multiplex_stream(data)) {
2243 data->mux = pa_multiplex_create(u->multiplex, core,
2244 sink->index, chmap, NULL,
2245 media_role, data->type);
2247 sink = pa_idxset_get_by_index(core->sinks,
2248 data->mux->sink_index);
2256 *target_ret = target;
2262 static pa_source *make_input_prerouting(struct userdata *u,
2264 const char *media_role,
2265 mir_node **target_ret)
2269 pa_source *source = NULL;
2273 pa_assert_se((core = u->core));
2275 target = mir_router_make_prerouting(u, data);
2278 pa_log("there is no default route for the stream '%s'", data->amname);
2279 else if (target->paidx == PA_IDXSET_INVALID)
2280 pa_log("can't route to default '%s': no source", target->amname);
2282 if (!(source = pa_idxset_get_by_index(core->sources, target->paidx)))
2283 pa_log("no route to default '%s': source is gone",target->amname);
2287 *target_ret = target;
2292 static mir_node_type get_stream_routing_class(pa_proplist *pl)
2298 t = pa_utils_get_stream_class(pl);
2300 if (t >= mir_application_class_begin && t < mir_application_class_end)
2303 return mir_node_type_unknown;
2307 static void set_bluetooth_profile(struct userdata *u,
2309 pa_direction_t direction)
2312 pa_device_port *port;
2313 pa_card_profile *prof, *make_active;
2314 void *state0, *state1;
2315 bool port_available;
2321 pa_assert_se((core = u->core));
2327 pa_log_debug("which profile to make active:");
2329 PA_HASHMAP_FOREACH(prof, card->profiles, state0) {
2330 if (!prof->n_sinks && !prof->n_sources) {
2332 pa_log_debug(" considering %s", prof->name);
2338 port_available = false;
2340 PA_HASHMAP_FOREACH(port, card->ports, state1) {
2341 if ((direction & port->direction) &&
2342 pa_hashmap_get(port->profiles, prof->name))
2344 port_available = (port->available != PA_AVAILABLE_NO);
2349 if (!port_available)
2350 pa_log_debug(" ruling out %s (port not available)", prof->name);
2351 else if (prof->available != PA_AVAILABLE_YES)
2352 pa_log_debug(" ruling out %s (profile not available)", prof->name);
2356 if (((direction & PA_DIRECTION_INPUT) && prof->n_sources > 0) ||
2357 ((direction & PA_DIRECTION_OUTPUT) && prof->n_sinks > 0) ) {
2358 if (make_active && prof->priority < make_active->priority)
2359 pa_log_debug(" ruling out %s (low priority)", prof->name);
2361 pa_log_debug(" considering %s", prof->name);
2366 pa_log_debug(" ruling out %s (direction)", prof->name);
2373 pa_log_debug("No suitable profile found. Frustrated and do nothing");
2375 if (make_active == card->active_profile)
2376 pa_log_debug("Profile %s already set. Do nothing", make_active->name);
2378 if (switch_off && nport) {
2379 pa_log_debug("Do not switch to %s as active ports are existing "
2380 "to the other direction", make_active->name);
2383 pa_log_debug("Set profile %s", make_active->name);
2385 if ((prof = pa_hashmap_get(card->profiles, make_active->name)) != NULL &&
2386 pa_card_set_profile(card, prof, false) < 0) {
2387 pa_log_debug("Failed to change profile to %s",
2395 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
2397 struct userdata *u = d;
2403 pa_log_debug("deferred routing starts");
2405 mir_router_make_routing(u);
2409 static void schedule_deferred_routing(struct userdata *u)
2414 pa_assert_se((core = u->core));
2416 pa_log_debug("scheduling deferred routing");
2418 pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
2422 static void card_check_cb(pa_mainloop_api *m, void *d)
2424 card_check_t *cc = d;
2430 int n_sink, n_source;
2436 pa_assert((u = cc->u));
2437 pa_assert((core = u->core));
2439 pa_log_debug("card check starts");
2441 if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
2442 pa_log_debug("card %u is gone", cc->index);
2444 n_sink = n_source = 0;
2446 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
2447 if ((sink->card) && sink->card->index == card->index)
2451 PA_IDXSET_FOREACH(source, core->sources, idx) {
2452 if ((source->card) && source->card->index == card->index)
2456 if (n_sink || n_source) {
2457 pa_log_debug("found %u sinks and %u sources belonging to "
2458 "'%s' card", n_sink, n_source, card->name);
2459 pa_log_debug("nothing to do");
2462 pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
2464 mir_router_make_routing(u);
2472 static void schedule_card_check(struct userdata *u, pa_card *card)
2479 pa_assert_se((core = u->core));
2481 pa_log_debug("scheduling card check");
2483 cc = pa_xnew0(card_check_t, 1);
2485 cc->index = card->index;
2487 pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
2491 static void source_cleanup_cb(pa_mainloop_api *m, void *d)
2493 source_cleanup_t *sc = d;
2500 pa_assert((u = sc->u));
2501 pa_assert((core = u->core));
2503 pa_log_debug("source cleanup starts");
2505 pa_loopback_destroy(u->loopback, u->core, sc->loop);
2506 pa_multiplex_destroy(u->multiplex, u->core, sc->mux);
2508 pa_log_debug("source cleanup ends");
2514 static void schedule_source_cleanup(struct userdata *u, mir_node *node)
2517 source_cleanup_t *sc;
2521 pa_assert_se((core = u->core));
2523 pa_log_debug("scheduling source cleanup");
2525 sc = pa_xnew0(source_cleanup_t, 1);
2527 sc->mux = node->mux;
2528 sc->loop = node->loop;
2533 pa_mainloop_api_once(core->mainloop, source_cleanup_cb, sc);
2538 static void stream_uncork_cb(pa_mainloop_api *m, void *d)
2540 stream_uncork_t *suc = d;
2544 pa_sink_input *sinp;
2550 pa_assert((u = suc->u));
2551 pa_assert((core = u->core));
2553 pa_log_debug("start uncorking stream");
2555 if (!(sink = pa_idxset_get_by_index(core->sinks, suc->index))) {
2556 pa_log_debug("sink.%d gone", suc->index);
2560 if (!(sinp = pa_idxset_first(core->sink_inputs, &index))) {
2561 pa_log_debug("sink_input is gone");
2565 pa_sink_input_cork(sinp, false);
2567 pa_log_debug("stream.%u uncorked", sinp->index);
2575 static void schedule_stream_uncorking(struct userdata *u, pa_sink *sink)
2578 stream_uncork_t *suc;
2582 pa_assert_se((core = u->core));
2584 pa_log_debug("scheduling stream uncorking");
2586 suc = pa_xnew0(stream_uncork_t, 1);
2588 suc->index = sink->index;
2590 pa_mainloop_api_once(core->mainloop, stream_uncork_cb, suc);
2597 * indent-tabs-mode: nil