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 pa_bool_t 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 *, pa_bool_t *);
100 static void destroy_node(struct userdata *, mir_node *);
101 static pa_bool_t update_node_availability(struct userdata *, mir_node *,
103 static pa_bool_t 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, NULL);
164 pa_hashmap_free(discover->nodes.byptr, NULL);
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;
264 pa_bool_t need_routing;
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_PORT_AVAILABLE_NO: state = "not available"; break;
376 case PA_PORT_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_PORT_AVAILABLE_NO: available = FALSE; break;
400 case PA_PORT_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, pa_bool_t 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;
460 pa_assert_se((core = u->core));
461 pa_assert_se((discover = u->discover));
463 module = sink->module;
465 if ((card = sink->card)) {
466 if (!(key = node_key(u, mir_output,sink,ACTIVE_PORT, kbf,sizeof(kbf))))
468 if (!(node = pa_discover_find_node_by_key(u, key))) {
469 if (u->state.profile)
470 pa_log_debug("can't find node for sink (key '%s')", key);
472 u->state.sink = sink->index;
475 pa_log_debug("node for '%s' found (key %s). Updating with sink data",
476 node->paname, node->key);
477 node->paidx = sink->index;
478 node->available = TRUE;
479 pa_discover_add_node_to_ptr_hash(u, sink, node);
481 if ((loopback_role = pa_classify_loopback_stream(node))) {
482 if (!(ns = pa_utils_get_null_source(u))) {
483 pa_log("Can't load loopback module: no initial null source");
487 map = pa_nodeset_get_map_by_role(u, loopback_role);
488 make_rset = (map && map->resdef);
489 resdef = make_rset ? map->resdef : &def_resdef;
491 node->loop = pa_loopback_create(u->loopback, core,
492 PA_LOOPBACK_SINK, node->index,
493 ns->index, sink->index,
497 resdef->flags.audio);
499 mir_node_print(node, nbf, sizeof(nbf));
500 pa_log_debug("updated node:\n%s", nbf);
503 pa_murphyif_create_resource_set(u, node, resdef);
509 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
510 mir_router_make_routing(u);
512 if (!u->state.profile)
513 schedule_deferred_routing(u);
517 else if (!module || !pa_streq(module->name, "module-combine-sink")) {
518 memset(&data, 0, sizeof(data));
519 data.key = pa_xstrdup(sink->name);
520 data.direction = mir_output;
521 data.implement = mir_device;
522 data.channels = sink->channel_map.channels;
523 data.available = TRUE;
524 data.paidx = sink->index;
526 if (sink == pa_utils_get_null_sink(u)) {
527 data.visible = FALSE;
528 data.type = mir_null;
529 data.amname = pa_xstrdup("Silent");
530 data.amid = AM_ID_INVALID;
531 data.paname = pa_xstrdup(sink->name);
533 else if (pa_classify_node_by_property(&data, sink->proplist)) {
534 data.privacy = mir_public;
536 data.amname = pa_xstrdup(mir_node_type_str(data.type));
537 data.amid = AM_ID_INVALID;
538 data.paname = pa_xstrdup(sink->name);
541 pa_xfree(data.key); /* for now */
542 pa_log_info("currently we do not support statically loaded "
543 "sinks without " PA_PROP_NODE_TYPE " property");
547 create_node(u, &data, NULL);
552 void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
554 pa_discover *discover;
561 pa_assert_se((discover = u->discover));
563 name = pa_utils_get_sink_name(sink);
565 if (!(node = pa_hashmap_get(discover->nodes.byptr, sink)))
566 pa_log_debug("can't find node for sink (name '%s')", name);
568 pa_log_debug("node found for '%s'. Reseting sink data", name);
569 pa_murphyif_destroy_resource_set(u, node);
570 schedule_source_cleanup(u, node);
571 node->paidx = PA_IDXSET_INVALID;
572 pa_hashmap_remove(discover->nodes.byptr, sink);
577 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
578 node->available = FALSE;
580 if (!u->state.profile)
581 schedule_deferred_routing(u);
585 pa_log_info("currently we do not support statically loaded sinks");
591 void pa_discover_add_source(struct userdata *u, pa_source *source)
593 static pa_nodeset_resdef def_resdef = {0, {0, 0}};
596 pa_discover *discover;
602 const char *loopback_role;
604 pa_nodeset_resdef *resdef;
612 pa_assert_se((core = u->core));
613 pa_assert_se((discover = u->discover));
615 if ((card = source->card)) {
616 if (!(key = node_key(u,mir_input,source,ACTIVE_PORT,kbf,sizeof(kbf))))
618 if (!(node = pa_discover_find_node_by_key(u, key))) {
619 if (u->state.profile)
620 pa_log_debug("can't find node for source (key '%s')", key);
622 u->state.source = source->index;
625 pa_log_debug("node for '%s' found. Updating with source data",
627 node->paidx = source->index;
628 node->available = TRUE;
629 pa_discover_add_node_to_ptr_hash(u, source, node);
630 if ((loopback_role = pa_classify_loopback_stream(node))) {
631 if (!(ns = pa_utils_get_null_sink(u))) {
632 pa_log("Can't load loopback module: no initial null sink");
636 map = pa_nodeset_get_map_by_role(u, loopback_role);
637 make_rset = (map && map->resdef);
638 resdef = make_rset ? map->resdef : &def_resdef;
640 node->loop = pa_loopback_create(u->loopback, core,
641 PA_LOOPBACK_SOURCE, node->index,
642 source->index, ns->index,
646 resdef->flags.audio);
648 sink_index = pa_loopback_get_sink_index(core, node->loop);
649 node->mux = pa_multiplex_find_by_sink(u->multiplex,sink_index);
652 mir_node_print(node, nbf, sizeof(nbf));
653 pa_log_debug("updated node:\n%s", nbf);
656 pa_murphyif_create_resource_set(u, node, resdef);
658 pa_fader_apply_volume_limits(u, node->stamp);
662 memset(&data, 0, sizeof(data));
663 data.key = pa_xstrdup(source->name);
664 data.direction = mir_input;
665 data.implement = mir_device;
666 data.channels = source->channel_map.channels;
667 data.available = TRUE;
669 if (source == pa_utils_get_null_source(u)) {
670 data.visible = FALSE;
671 data.type = mir_null;
672 data.amname = pa_xstrdup("Silent");
673 data.amid = AM_ID_INVALID;
674 data.paname = pa_xstrdup(source->name);
675 data.paidx = source->index;
677 else if (pa_classify_node_by_property(&data, source->proplist)) {
679 data.amname = pa_xstrdup(mir_node_type_str(data.type));
680 data.amid = AM_ID_INVALID;
681 data.paname = pa_xstrdup(source->name);
684 pa_xfree(data.key); /* for now */
685 pa_log_info("currently we do not support statically loaded "
686 "sources without " PA_PROP_NODE_TYPE " property");
690 create_node(u, &data, NULL);
695 void pa_discover_remove_source(struct userdata *u, pa_source *source)
697 pa_discover *discover;
704 pa_assert_se((discover = u->discover));
706 name = pa_utils_get_source_name(source);
708 if (!(node = pa_hashmap_get(discover->nodes.byptr, source)))
709 pa_log_debug("can't find node for source (name '%s')", name);
711 pa_log_debug("node found. Reseting source data");
712 pa_murphyif_destroy_resource_set(u, node);
713 schedule_source_cleanup(u, node);
714 node->paidx = PA_IDXSET_INVALID;
715 pa_hashmap_remove(discover->nodes.byptr, source);
720 if (type != mir_bluetooth_sco)
721 node->available = FALSE;
723 if (!u->state.profile)
724 schedule_deferred_routing(u);
728 pa_log_info("currently we do not support statically "
735 void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
738 pa_discover *discover;
749 pa_nodeset_resdef *resdef;
753 pa_assert_se((core = u->core));
754 pa_assert_se((discover = u->discover));
755 pa_assert_se((pl = sinp->proplist));
757 if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME))) {
758 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
759 pa_log_debug("Seems to be a combine stream. Nothing to do ...");
762 if (!strncmp(media, loopback_outpatrn, sizeof(loopback_outpatrn)-1)) {
763 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
768 name = pa_utils_get_sink_input_name(sinp);
770 pa_log_debug("registering input stream '%s'", name);
772 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
773 pa_log_debug("cant find stream class for '%s'. "
774 "Leaving it alone", name);
778 pa_utils_set_stream_routing_properties(pl, type, NULL);
780 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
782 memset(&data, 0, sizeof(data));
784 data.direction = mir_input;
785 data.implement = mir_stream;
786 data.channels = sinp->channel_map.channels;
788 data.zone = pa_utils_get_zone(sinp->proplist);
790 data.available = TRUE;
792 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
793 data.amid = AM_ID_INVALID;
795 data.paidx = sinp->index;
796 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
799 * here we can't guess whether the application requested an explicit
800 * route by sepcifying the target sink @ stream creation time.
802 * the brute force solution: we make a default route for this stream
803 * possibly overwiriting the orginal app request :(
805 /* this will set data.mux */
806 role = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_ROLE);
807 sink = make_output_prerouting(u, &data, &sinp->channel_map, role, &target);
809 node = create_node(u, &data, NULL);
811 pa_discover_add_node_to_ptr_hash(u, sinp, node);
813 if (sink && target) {
814 pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
816 if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
817 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
819 pa_log_debug("register route '%s' => '%s'",
820 node->amname, target->amname);
821 /* FIXME: and actually do it ... */
826 void pa_discover_preroute_sink_input(struct userdata *u,
827 pa_sink_input_new_data *data)
832 pa_discover *discover;
833 pa_multiplex *multiplex;
842 pa_nodeset_resdef *resdef;
847 pa_assert_se((core = u->core));
848 pa_assert_se((discover = u->discover));
849 pa_assert_se((multiplex = u->multiplex));
850 pa_assert_se((pl = data->proplist));
852 mnam = (m = data->module) ? m->name : "";
854 if (pa_streq(mnam, "module-combine-sink")) {
856 type = mir_node_type_unknown;
858 if (!(mux = pa_multiplex_find_by_module(multiplex, m)) ||
859 !(sink = pa_idxset_get_by_index(core->sinks, mux->sink_index)) ||
860 !(sinp = pa_idxset_first(sink->inputs, NULL)) ||
861 !(type = pa_utils_get_stream_class(sinp->proplist)))
863 pa_log_debug("can't figure out the type of multiplex stream");
866 pa_utils_set_stream_routing_properties(data->proplist, type, NULL);
870 loopback = pa_streq(mnam, "module-loopback");
873 if (!(node = pa_utils_get_node_from_data(u, mir_input, data))) {
874 pa_log_debug("can't find loopback node for sink-input");
878 if (node->direction == mir_output) {
879 pa_log_debug("refuse to preroute loopback sink-input "
880 "(current route: sink %u @ %p)", data->sink ?
881 data->sink->index : PA_IDXSET_INVALID,data->sink);
887 type = pa_classify_guess_stream_node_type(u, pl, NULL);
890 type = pa_classify_guess_stream_node_type(u, pl, &resdef);
892 pa_utils_set_resource_properties(pl, resdef);
894 if (pa_stream_state_start_corked(u, data, resdef)) {
895 pa_log_debug("start corked");
899 pa_utils_set_stream_routing_properties(pl, type, data->sink);
903 memset(&fake, 0, sizeof(fake));
904 fake.direction = mir_input;
905 fake.implement = mir_stream;
906 fake.channels = data->channel_map.channels;
908 fake.zone = pa_utils_get_zone(data->proplist);
910 fake.available = TRUE;
911 fake.amname = "<preroute sink-input>";
913 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
914 sink = make_output_prerouting(u, &fake, &data->channel_map, role,NULL);
918 if (fake.mux && !(data->flags & PA_SINK_INPUT_START_CORKED)) {
919 data->flags |= PA_SINK_INPUT_START_CORKED;
920 schedule_stream_uncorking(u, sink);
923 if (pa_sink_input_new_data_set_sink(data, sink, FALSE))
924 pa_log_debug("set sink %u for new sink-input", sink->index);
926 pa_log("can't set sink %u for new sink-input", sink->index);
930 if (loopback && data->sink && data->sink->module) {
931 if (pa_streq(data->sink->module->name, "module-combine-sink"))
935 pa_log_debug("set sink-input ramp-muted");
936 data->flags |= PA_SINK_INPUT_START_RAMP_MUTED;
940 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
944 pa_sink_input *csinp;
946 pa_discover *discover;
947 pa_multiplex *multiplex;
957 pa_nodeset_resdef *resdef;
958 pa_nodeset_resdef rdbuf;
962 pa_assert_se((core = u->core));
963 pa_assert_se((discover = u->discover));
964 pa_assert_se((multiplex = u->multiplex));
965 pa_assert_se((pl = sinp->proplist));
969 if (!(media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)))
972 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
973 if (!pa_utils_stream_has_default_route(sinp->proplist) ||
974 !(mux = pa_multiplex_find_by_module(multiplex, sinp->module)) ||
975 mux->defstream_index != PA_IDXSET_INVALID)
977 pa_log_debug("New stream is a combine stream. Nothing to do ...");
980 pa_log_debug("New stream is a combine stream. Setting as default");
981 mux->defstream_index = sinp->index;
982 mir_router_make_routing(u);
985 } else if (!strncmp(media, loopback_outpatrn,sizeof(loopback_outpatrn)-1)){
986 pa_log_debug("New stream is a loopback output stream");
988 if ((node = pa_utils_get_node_from_stream(u, mir_input, sinp))) {
989 if (node->direction == mir_input)
990 pa_log_debug("loopback stream node '%s' found", node->amname);
992 pa_log_debug("ignoring it");
997 pa_log_debug("can't find node for the loopback stream");
1004 name = pa_utils_get_sink_input_name(sinp);
1006 pa_log_debug("dealing with new input stream '%s'", name);
1008 if ((type = get_stream_routing_class(pl)))
1009 resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1011 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1012 pa_log_debug("cant find stream class for '%s'. "
1013 "Leaving it alone", name);
1017 pa_utils_set_stream_routing_properties(pl, type, NULL);
1019 /* if needed, make some post-routing here */
1022 /* we need to add this to main hashmap as that is used for loop
1023 through on all nodes. */
1024 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
1026 memset(&data, 0, sizeof(data));
1028 data.direction = mir_input;
1029 data.implement = mir_stream;
1030 data.channels = sinp->channel_map.channels;
1032 data.zone = pa_utils_get_zone(pl);
1033 data.visible = TRUE;
1034 data.available = TRUE;
1036 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1037 data.amid = AM_ID_INVALID;
1039 data.paidx = sinp->index;
1040 data.mux = pa_multiplex_find_by_sink(u->multiplex,
1042 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1043 node = create_node(u, &data, &created);
1048 pa_log("%s: confused with stream. '%s' did exists",
1049 __FILE__, node->amname);
1054 pa_murphyif_add_node(u, node);
1056 pa_murphyif_create_resource_set(u, node, resdef);
1058 pa_discover_add_node_to_ptr_hash(u, sinp, node);
1063 csinp = pa_idxset_get_by_index(core->sink_inputs,
1064 data.mux->defstream_index);
1065 s = csinp ? csinp->sink : NULL;
1067 if ((sinp->flags & PA_SINK_INPUT_START_RAMP_MUTED)) {
1068 pa_log_debug("ramp '%s' to 100%", media);
1069 pa_fader_ramp_volume(u, sinp, PA_VOLUME_NORM);
1075 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1077 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1078 pa_log_debug("can't figure out where this stream is routed");
1080 pa_log_debug("register route '%s' => '%s'",
1081 node->amname, snod->amname);
1082 /* FIXME: and actually do it ... */
1084 pa_fader_apply_volume_limits(u, pa_utils_get_stamp());
1089 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
1091 pa_discover *discover;
1095 pa_bool_t had_properties;
1099 pa_assert_se((discover = u->discover));
1101 name = pa_utils_get_sink_input_name(sinp);
1103 pa_log_debug("sink-input '%s' going to be destroyed", name);
1105 had_properties = pa_utils_unset_stream_routing_properties(sinp->proplist);
1107 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sinp))) {
1108 if (!pa_multiplex_sink_input_remove(u->multiplex, sinp))
1109 pa_log_debug("nothing to do for sink-input (name '%s')", name);
1112 pa_log_debug("node found for '%s'. After clearing routes "
1113 "it will be destroyed", name);
1115 if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
1116 pa_log_debug("can't figure out where this stream is routed");
1118 pa_log_debug("clear route '%s' => '%s'",
1119 node->amname, sinknod->amname);
1121 /* FIXME: and actually do it ... */
1125 destroy_node(u, node);
1128 if (node || had_properties)
1129 mir_router_make_routing(u);
1133 void pa_discover_register_source_output(struct userdata *u,
1134 pa_source_output *sout)
1137 pa_discover *discover;
1148 pa_nodeset_resdef *resdef;
1152 pa_assert_se((core = u->core));
1153 pa_assert_se((discover = u->discover));
1154 pa_assert_se((pl = sout->proplist));
1156 if ((media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME))) {
1157 if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1158 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
1163 name = pa_utils_get_source_output_name(sout);
1165 pa_log_debug("registering output stream '%s'", name);
1167 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1168 pa_log_debug("cant find stream class for '%s'. "
1169 "Leaving it alone", name);
1173 pa_utils_set_stream_routing_properties(pl, type, NULL);
1175 snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1177 memset(&data, 0, sizeof(data));
1179 data.direction = mir_output;
1180 data.implement = mir_stream;
1181 data.channels = sout->channel_map.channels;
1183 data.zone = pa_utils_get_zone(sout->proplist);
1184 data.visible = TRUE;
1185 data.available = TRUE;
1187 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1188 data.amid = AM_ID_INVALID;
1190 data.paidx = sout->index;
1191 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1194 * here we can't guess whether the application requested an explicit
1195 * route by sepcifying the target source @ stream creation time.
1197 * the brute force solution: we make a default route for this stream
1198 * possibly overwiriting the orginal app request :(
1200 role = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_ROLE);
1201 source = make_input_prerouting(u, &data, role, &target);
1203 node = create_node(u, &data, NULL);
1205 pa_discover_add_node_to_ptr_hash(u, sout, node);
1207 if (source && target) {
1208 pa_log_debug("move stream to source %u (%s)",
1209 source->index, source->name);
1211 if (pa_source_output_move_to(sout, source, FALSE) < 0)
1212 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
1214 pa_log_debug("register route '%s' => '%s'",
1215 node->amname, target->amname);
1216 /* FIXME: and actually do it ... */
1221 void pa_discover_preroute_source_output(struct userdata *u,
1222 pa_source_output_new_data *data)
1227 pa_discover *discover;
1234 pa_nodeset_resdef *resdef;
1238 pa_assert_se((core = u->core));
1239 pa_assert_se((discover = u->discover));
1240 pa_assert_se((pl = data->proplist));
1242 mnam = (m = data->module) ? m->name : "";
1244 if (pa_streq(mnam, "module-loopback")) {
1245 if (!(node = pa_utils_get_node_from_data(u, mir_output, data))) {
1246 pa_log_debug("can't find loopback node for source-output");
1250 if (node->direction == mir_input) {
1251 pa_log_debug("refuse to preroute loopback source-output "
1252 "(current route: source %u @ %p)", data->source ?
1253 data->source->index : PA_IDXSET_INVALID,data->source);
1257 data->source = NULL;
1259 type = pa_classify_guess_stream_node_type(u, pl, NULL);
1262 type = pa_classify_guess_stream_node_type(u, pl, &resdef);
1264 pa_utils_set_resource_properties(pl, resdef);
1267 pa_utils_set_stream_routing_properties(pl, type, data->source);
1269 if (!data->source) {
1270 memset(&fake, 0, sizeof(fake));
1271 fake.direction = mir_output;
1272 fake.implement = mir_stream;
1273 fake.channels = data->channel_map.channels;
1275 fake.zone = pa_utils_get_zone(data->proplist);
1276 fake.visible = TRUE;
1277 fake.available = TRUE;
1278 fake.amname = "<preroute source-output>";
1280 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
1281 source = make_input_prerouting(u, &fake, role, NULL);
1284 if (pa_source_output_new_data_set_source(data, source, FALSE)) {
1285 pa_log_debug("set source %u for new source-output",
1289 pa_log("can't set source %u for new source-output",
1297 void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
1302 pa_discover *discover;
1311 pa_nodeset_resdef *resdef;
1312 pa_nodeset_resdef rdbuf;
1316 pa_assert_se((core = u->core));
1317 pa_assert_se((discover = u->discover));
1318 pa_assert_se((pl = sout->proplist));
1322 if (!(media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME)))
1323 media = "<unknown>";
1325 if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1326 pa_log_debug("New stream is a loopback input stream");
1328 if ((node = pa_utils_get_node_from_stream(u, mir_output, sout))) {
1329 if (node->direction == mir_output)
1330 pa_log_debug("loopback stream node '%s' found", node->amname);
1332 pa_log_debug("ignoring it");
1337 pa_log_debug("can't find node for the loopback stream");
1342 name = pa_utils_get_source_output_name(sout);
1344 pa_log_debug("dealing with new output stream '%s'", name);
1346 if ((type = get_stream_routing_class(pl)))
1347 resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1349 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1350 pa_log_debug("cant find stream class for '%s'. "
1351 "Leaving it alone", name);
1355 pa_utils_set_stream_routing_properties(pl, type, NULL);
1357 /* if needed, make some post-routing here */
1360 /* we need to add this to main hashmap as that is used for loop
1361 through on all nodes. */
1362 snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1364 memset(&data, 0, sizeof(data));
1366 data.direction = mir_output;
1367 data.implement = mir_stream;
1368 data.channels = sout->channel_map.channels;
1370 data.zone = pa_utils_get_zone(pl);
1371 data.visible = TRUE;
1372 data.available = TRUE;
1374 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1375 data.amid = AM_ID_INVALID;
1377 data.paidx = sout->index;
1378 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1380 node = create_node(u, &data, &created);
1385 pa_log("%s: confused with stream. '%s' did exists",
1386 __FILE__, node->amname);
1391 pa_murphyif_add_node(u, node);
1393 pa_murphyif_create_resource_set(u, node, resdef);
1395 pa_discover_add_node_to_ptr_hash(u, sout, node);
1398 if ((s = sout->source))
1399 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1401 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1402 pa_log_debug("can't figure out where this stream is routed");
1404 pa_log_debug("register route '%s' => '%s'",
1405 snod->amname, node->amname);
1406 /* FIXME: and actually do it ... */
1412 void pa_discover_remove_source_output(struct userdata *u,
1413 pa_source_output *sout)
1415 pa_discover *discover;
1422 pa_assert_se((discover = u->discover));
1424 name = pa_utils_get_source_output_name(sout);
1426 pa_log_debug("source-output '%s' going to be destroyed", name);
1428 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sout)))
1429 pa_log_debug("can't find node for source-output (name '%s')", name);
1431 pa_log_debug("node found for '%s'. After clearing routes "
1432 "it will be destroyed", name);
1434 if (!(srcnod = pa_hashmap_get(discover->nodes.byptr, sout->source)))
1435 pa_log_debug("can't figure out where this stream is routed");
1437 pa_log_debug("clear route '%s' => '%s'",
1438 node->amname, srcnod->amname);
1440 /* FIXME: and actually do it ... */
1444 destroy_node(u, node);
1446 mir_router_make_routing(u);
1451 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
1453 pa_discover *discover;
1457 pa_assert_se((discover = u->discover));
1460 node = pa_hashmap_get(discover->nodes.byname, key);
1467 mir_node *pa_discover_find_node_by_ptr(struct userdata *u, void *ptr)
1469 pa_discover *discover;
1473 pa_assert_se((discover = u->discover));
1476 node = pa_hashmap_get(discover->nodes.byptr, ptr);
1483 void pa_discover_add_node_to_ptr_hash(struct userdata *u,
1487 pa_discover *discover;
1492 pa_assert_se((discover = u->discover));
1494 pa_hashmap_put(discover->nodes.byptr, ptr, node);
1497 mir_node *pa_discover_remove_node_from_ptr_hash(struct userdata *u, void *ptr)
1499 pa_discover *discover;
1503 pa_assert_se((discover = u->discover));
1505 return pa_hashmap_remove(discover->nodes.byptr, ptr);
1509 static void handle_alsa_card(struct userdata *u, pa_card *card)
1516 memset(&data, 0, sizeof(data));
1517 data.zone = pa_utils_get_zone(card->proplist);
1518 data.visible = TRUE;
1519 data.amid = AM_ID_INVALID;
1520 data.implement = mir_device;
1521 data.paidx = PA_IDXSET_INVALID;
1522 data.stamp = pa_utils_get_stamp();
1524 cnam = pa_utils_get_card_name(card);
1525 udd = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
1527 if (udd && pa_streq(udd, "1")) {
1528 /* udev loaded alsa card */
1529 if (!strncmp(cnam, "alsa_card.", 10)) {
1531 handle_udev_loaded_card(u, card, &data, cid);
1536 /* statically loaded pci or usb card */
1539 pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
1544 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1546 pa_discover *discover;
1547 pa_card_profile *prof;
1555 char paname[MAX_NAME_LENGTH+1];
1556 char amname[MAX_NAME_LENGTH+1];
1557 char key[MAX_NAME_LENGTH+1];
1559 pa_assert_se((discover = u->discover));
1561 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1564 memset(paname, 0, sizeof(paname));
1565 memset(amname, 0, sizeof(amname));
1566 memset(key , 0, sizeof(key) );
1568 memset(&data, 0, sizeof(data));
1570 data.visible = TRUE;
1571 data.amid = AM_ID_INVALID;
1572 data.implement = mir_device;
1573 data.paidx = PA_IDXSET_INVALID;
1574 data.paname = paname;
1575 data.amname = amname;
1576 data.amdescr = (char *)cdescr;
1577 data.pacard.index = card->index;
1578 data.stamp = pa_utils_get_stamp();
1580 cnam = pa_utils_get_card_name(card);
1582 if (!strncmp(cnam, "bluez_card.", 11)) {
1585 pa_assert(card->ports);
1587 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1589 PA_HASHMAP_FOREACH(prof, card->profiles, cstate) {
1590 data.available = FALSE;
1591 data.pacard.profile = prof->name;
1593 if (prof->n_sinks > 0) {
1594 data.direction = mir_output;
1595 data.channels = prof->max_sink_channels;
1596 data.amname = amname;
1598 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1599 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1600 pa_classify_node_by_card(&data, card, prof, NULL);
1601 node = create_node(u, &data, NULL);
1602 mir_constrain_add_node(u, cd, node);
1605 if (prof->n_sources > 0) {
1606 data.direction = mir_input;
1607 data.channels = prof->max_source_channels;
1608 data.amname = amname;
1610 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1611 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1612 pa_classify_node_by_card(&data, card, prof, NULL);
1613 node = create_node(u, &data, NULL);
1614 mir_constrain_add_node(u, cd, node);
1618 if (!(prof = card->active_profile))
1619 pa_log("card '%s' has no active profile", card->name);
1621 pa_log_debug("card '%s' default profile '%s'",
1622 card->name, prof->name);
1625 schedule_card_check(u, card);
1631 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1633 pa_discover *discover;
1634 pa_card_profile *prof;
1635 pa_device_port *port;
1642 void *state0, *state1;
1643 char paname[MAX_NAME_LENGTH+1];
1644 char amname[MAX_NAME_LENGTH+1];
1645 char key[MAX_NAME_LENGTH+1];
1650 pa_assert_se((discover = u->discover));
1652 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1655 memset(paname, 0, sizeof(paname));
1656 memset(amname, 0, sizeof(amname));
1657 memset(key , 0, sizeof(key) );
1659 memset(&data, 0, sizeof(data));
1661 data.zone = pa_utils_get_zone(card->proplist);
1662 data.visible = TRUE;
1663 data.amid = AM_ID_INVALID;
1664 data.implement = mir_device;
1665 data.paidx = PA_IDXSET_INVALID;
1666 data.paname = paname;
1667 data.amname = amname;
1668 data.amdescr = (char *)cdescr;
1669 data.pacard.index = card->index;
1670 data.stamp = pa_utils_get_stamp();
1672 cnam = pa_utils_get_card_name(card);
1674 if (!strncmp(cnam, "bluez_card.", 11)) {
1677 pa_assert(card->ports);
1679 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1681 PA_HASHMAP_FOREACH(port, card->ports, state0) {
1682 pa_assert(port->profiles);
1685 input = output = TRUE;
1686 len = strlen(port->name);
1687 if (len >= 6 && !strcmp("-input", port->name + (len-6)))
1689 else if (len >= 7 && !strcmp("-output", port->name + (len-7)))
1693 PA_HASHMAP_FOREACH(prof, port->profiles, state1) {
1694 data.pacard.profile = prof->name;
1695 data.available = get_bluetooth_port_availability(&data, port);
1697 if (output && prof->n_sinks > 0) {
1698 data.direction = mir_output;
1699 data.channels = prof->max_sink_channels;
1700 data.amname = amname;
1702 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1703 snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1704 pa_classify_node_by_card(&data, card, prof, NULL);
1705 node = create_node(u, &data, NULL);
1706 mir_constrain_add_node(u, cd, node);
1707 pa_utils_set_port_properties(port, node);
1710 if (input && prof->n_sources > 0) {
1711 data.direction = mir_input;
1712 data.channels = prof->max_source_channels;
1713 data.amname = amname;
1715 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1716 snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1717 pa_classify_node_by_card(&data, card, prof, NULL);
1718 node = create_node(u, &data, NULL);
1719 mir_constrain_add_node(u, cd, node);
1720 pa_utils_set_port_properties(port, node);
1725 if (!(prof = card->active_profile))
1726 pa_log("card '%s' has no active profile", card->name);
1728 pa_log_debug("card '%s' default profile '%s'",
1729 card->name, prof->name);
1732 schedule_card_check(u, card);
1736 static pa_bool_t get_bluetooth_port_availability(mir_node *node,
1737 pa_device_port *port)
1739 pa_bool_t available = FALSE;
1745 if ((prof = node->pacard.profile)) {
1746 if (!strcmp(prof, "hfgw") ||
1747 !strcmp(prof, "a2dp_source") ||
1748 !strcmp(prof, "a2dp_sink"))
1749 available = (port->available != PA_PORT_AVAILABLE_NO);
1757 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
1758 mir_node *data, char *cardid)
1760 pa_discover *discover;
1761 pa_card_profile *prof;
1762 pa_card_profile *active;
1764 const char *alsanam;
1766 char *sinks[MAX_CARD_TARGET+1];
1767 char *sources[MAX_CARD_TARGET+1];
1768 char buf[MAX_NAME_LENGTH+1];
1769 char paname[MAX_NAME_LENGTH+1];
1770 char amname[MAX_NAME_LENGTH+1];
1774 pa_assert(card->profiles);
1775 pa_assert_se((discover = u->discover));
1777 alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
1779 memset(amname, 0, sizeof(amname));
1781 data->paname = paname;
1782 data->amname = amname;
1783 data->amdescr = (char *)alsanam;
1785 data->pacard.index = card->index;
1787 active = card->active_profile;
1789 PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1790 /* filtering: deal with selected profiles if requested so */
1791 if (discover->selected && (!active || (active && prof != active)))
1794 /* filtering: skip the 'off' profiles */
1795 if (!prof->n_sinks && !prof->n_sources)
1798 /* filtering: consider sinks with suitable amount channels */
1799 if (prof->n_sinks &&
1800 (prof->max_sink_channels < discover->chmin ||
1801 prof->max_sink_channels > discover->chmax ))
1804 /* filtering: consider sources with suitable amount channels */
1805 if (prof->n_sources &&
1806 (prof->max_source_channels < discover->chmin ||
1807 prof->max_source_channels > discover->chmax ))
1810 data->pacard.profile = prof->name;
1812 parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
1814 data->direction = mir_output;
1815 data->channels = prof->max_sink_channels;
1816 for (i = 0; (sid = sinks[i]); i++) {
1817 snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
1818 handle_card_ports(u, data, card, prof);
1821 data->direction = mir_input;
1822 data->channels = prof->max_source_channels;
1823 for (i = 0; (sid = sources[i]); i++) {
1824 snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
1825 handle_card_ports(u, data, card, prof);
1831 static void handle_card_ports(struct userdata *u, mir_node *data,
1832 pa_card *card, pa_card_profile *prof)
1834 mir_node *node = NULL;
1835 pa_bool_t have_ports = FALSE;
1836 mir_constr_def *cd = NULL;
1837 char *amname = data->amname;
1838 pa_device_port *port;
1841 char key[MAX_NAME_LENGTH+1];
1849 PA_HASHMAP_FOREACH(port, card->ports, state) {
1851 * if this port did not belong to any profile
1852 * (ie. prof->profiles == NULL) we assume that this port
1853 * does works with all the profiles
1855 if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
1856 ((port->direction == PA_DIRECTION_INPUT && data->direction == mir_input)||
1857 (port->direction == PA_DIRECTION_OUTPUT && data->direction == mir_output)))
1862 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
1865 data->available = (port->available != PA_PORT_AVAILABLE_NO);
1867 data->amname = amname;
1868 data->paport = port->name;
1870 pa_classify_node_by_card(data, card, prof, port);
1872 node = create_node(u, data, &created);
1875 node->stamp = data->stamp;
1877 cd = mir_constrain_create(u, "port", mir_constrain_port,
1879 mir_constrain_add_node(u, cd, node);
1886 data->key = data->paname;
1887 data->available = TRUE;
1889 pa_classify_node_by_card(data, card, prof, NULL);
1891 node = create_node(u, data, &created);
1894 node->stamp = data->stamp;
1897 data->amname = amname;
1902 static mir_node *create_node(struct userdata *u, mir_node *data,
1903 pa_bool_t *created_ret)
1905 pa_discover *discover;
1912 pa_assert(data->key);
1913 pa_assert(data->paname);
1914 pa_assert_se((discover = u->discover));
1916 if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
1921 node = mir_node_create(u, data);
1922 pa_hashmap_put(discover->nodes.byname, node->key, node);
1924 mir_node_print(node, buf, sizeof(buf));
1925 pa_log_debug("new node:\n%s", buf);
1927 if (node->available)
1928 pa_audiomgr_register_node(u, node);
1932 *created_ret = created;
1937 static void destroy_node(struct userdata *u, mir_node *node)
1939 pa_discover *discover;
1943 pa_assert_se((discover = u->discover));
1946 removed = pa_hashmap_remove(discover->nodes.byname, node->key);
1948 if (node != removed) {
1950 pa_log("%s: confused with data structures: key mismatch. "
1951 " attempted to destroy '%s'; actually destroyed '%s'",
1952 __FILE__, node->key, removed->key);
1954 pa_log("%s: confused with data structures: node '%s' "
1955 "is not in the hash table", __FILE__, node->key);
1959 pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
1961 if (node->implement == mir_stream) {
1962 if (node->direction == mir_input) {
1964 pa_log_debug("removing multiplexer");
1969 pa_audiomgr_unregister_node(u, node);
1971 extapi_signal_node_change(u);
1973 mir_constrain_remove_node(u, node);
1975 pa_loopback_destroy(u->loopback, u->core, node->loop);
1976 pa_multiplex_destroy(u->multiplex, u->core, node->mux);
1978 mir_node_destroy(u, node);
1982 static pa_bool_t update_node_availability(struct userdata *u,
1984 pa_bool_t available)
1989 if ((!available && node->available) ||
1990 ( available && !node->available) )
1992 node->available = available;
1995 pa_audiomgr_register_node(u, node);
1997 pa_audiomgr_unregister_node(u, node);
1999 extapi_signal_node_change(u);
2001 return TRUE; /* routing needed */
2007 static pa_bool_t update_node_availability_by_device(struct userdata *u,
2008 mir_direction direction,
2010 pa_device_port *port,
2011 pa_bool_t available)
2020 pa_assert(direction == mir_input || direction == mir_output);
2022 if ((key = node_key(u, direction, data, port, buf, sizeof(buf)))) {
2023 if (!(node = pa_discover_find_node_by_key(u, key)))
2024 pa_log_debug(" can't find node (key '%s')", key);
2026 pa_log_debug(" node for '%s' found (key %s)",
2027 node->paname, node->key);
2029 return update_node_availability(u, node, available);
2033 return FALSE; /* no routing needed */
2036 static char *get_name(char **string_ptr, int offs)
2038 char c, *name, *end;
2040 name = *string_ptr + offs;
2042 for (end = name; (c = *end); end++) {
2054 static void parse_profile_name(pa_card_profile *prof,
2064 pa_assert(prof->name);
2066 strncpy(buf, prof->name, buflen);
2067 buf[buflen-1] = '\0';
2069 memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2070 memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2073 if (!strncmp(p, "output:", 7)) {
2074 if (i >= MAX_CARD_TARGET) {
2075 pa_log_debug("number of outputs exeeds the maximum %d in "
2076 "profile name '%s'", MAX_CARD_TARGET, prof->name);
2079 sinks[i++] = get_name(&p, 7);
2081 else if (!strncmp(p, "input:", 6)) {
2082 if (j >= MAX_CARD_TARGET) {
2083 pa_log_debug("number of inputs exeeds the maximum %d in "
2084 "profile name '%s'", MAX_CARD_TARGET, prof->name);
2087 sources[j++] = get_name(&p, 6);
2090 pa_log("%s: failed to parse profile name '%s'",
2091 __FILE__, prof->name);
2098 static char *node_key(struct userdata *u, mir_direction direction,
2099 void *data, pa_device_port *port, char *buf, size_t len)
2102 pa_card_profile *profile;
2106 pa_bool_t bluetooth;
2109 const char *profile_name;
2115 pa_assert(direction == mir_input || direction == mir_output);
2117 if (direction == mir_output) {
2118 pa_sink *sink = data;
2120 name = pa_utils_get_sink_name(sink);
2123 port = sink->active_port;
2126 pa_source *source = data;
2128 name = pa_utils_get_source_name(source);
2129 card = source->card;
2131 port = source->active_port;
2137 pa_assert_se((profile = card->active_profile));
2139 if (!u->state.profile)
2140 profile_name = profile->name;
2142 pa_log_debug("state.profile is not null. '%s' supresses '%s'",
2143 u->state.profile, profile->name);
2144 profile_name = u->state.profile;
2148 if (!(bus = pa_utils_get_card_bus(card))) {
2149 pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
2150 "on its card", type, name, PA_PROP_DEVICE_BUS);
2154 pci = pa_streq(bus, "pci");
2155 usb = pa_streq(bus, "usb");
2156 bluetooth = pa_streq(bus, "bluetooth");
2158 if (!pci && !usb && !bluetooth) {
2159 pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
2160 "of its card", type, name, bus);
2169 snprintf(buf, len, "%s@%s.%s", name, port->name, profile_name);
2177 snprintf(buf, len, "%s@%s", name, port->name);
2184 static pa_sink *make_output_prerouting(struct userdata *u,
2186 pa_channel_map *chmap,
2187 const char *media_role,
2188 mir_node **target_ret)
2192 pa_sink *sink = NULL;
2197 pa_assert_se((core = u->core));
2200 target = mir_router_make_prerouting(u, data);
2203 pa_log("there is no default route for the stream '%s'", data->amname);
2204 else if (target->paidx == PA_IDXSET_INVALID)
2205 pa_log("can't route to default '%s': no sink", target->amname);
2207 if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
2208 pa_log("no route to default '%s': sink is gone", target->amname);
2210 if (pa_classify_multiplex_stream(data)) {
2211 data->mux = pa_multiplex_create(u->multiplex, core,
2212 sink->index, chmap, NULL,
2213 media_role, data->type);
2215 sink = pa_idxset_get_by_index(core->sinks,
2216 data->mux->sink_index);
2224 *target_ret = target;
2230 static pa_source *make_input_prerouting(struct userdata *u,
2232 const char *media_role,
2233 mir_node **target_ret)
2237 pa_source *source = NULL;
2241 pa_assert_se((core = u->core));
2243 target = mir_router_make_prerouting(u, data);
2246 pa_log("there is no default route for the stream '%s'", data->amname);
2247 else if (target->paidx == PA_IDXSET_INVALID)
2248 pa_log("can't route to default '%s': no source", target->amname);
2250 if (!(source = pa_idxset_get_by_index(core->sources, target->paidx)))
2251 pa_log("no route to default '%s': source is gone",target->amname);
2255 *target_ret = target;
2260 static mir_node_type get_stream_routing_class(pa_proplist *pl)
2266 t = pa_utils_get_stream_class(pl);
2268 if (t >= mir_application_class_begin && t < mir_application_class_end)
2271 return mir_node_type_unknown;
2275 static void set_bluetooth_profile(struct userdata *u,
2277 pa_direction_t direction)
2280 pa_device_port *port;
2281 pa_card_profile *prof, *make_active;
2282 void *state0, *state1;
2283 pa_bool_t port_available;
2284 pa_bool_t switch_off;
2289 pa_assert_se((core = u->core));
2295 pa_log_debug("which profile to make active:");
2297 PA_HASHMAP_FOREACH(prof, card->profiles, state0) {
2298 if (!prof->n_sinks && !prof->n_sources) {
2300 pa_log_debug(" considering %s", prof->name);
2306 port_available = FALSE;
2308 PA_HASHMAP_FOREACH(port, card->ports, state1) {
2309 if ((direction & port->direction) &&
2310 pa_hashmap_get(port->profiles, prof->name))
2312 port_available = (port->available != PA_PORT_AVAILABLE_NO);
2317 if (!port_available)
2318 pa_log_debug(" ruling out %s (port not available)", prof->name);
2319 else if (prof->available != PA_AVAILABLE_YES)
2320 pa_log_debug(" ruling out %s (profile not available)", prof->name);
2324 if (((direction & PA_DIRECTION_INPUT) && prof->n_sources > 0) ||
2325 ((direction & PA_DIRECTION_OUTPUT) && prof->n_sinks > 0) ) {
2326 if (make_active && prof->priority < make_active->priority)
2327 pa_log_debug(" ruling out %s (low priority)", prof->name);
2329 pa_log_debug(" considering %s", prof->name);
2334 pa_log_debug(" ruling out %s (direction)", prof->name);
2341 pa_log_debug("No suitable profile found. Frustrated and do nothing");
2343 if (make_active == card->active_profile)
2344 pa_log_debug("Profile %s already set. Do nothing", make_active->name);
2346 if (switch_off && nport) {
2347 pa_log_debug("Do not switch to %s as active ports are existing "
2348 "to the other direction", make_active->name);
2351 pa_log_debug("Set profile %s", make_active->name);
2353 if (pa_card_set_profile(card, make_active->name, FALSE) < 0) {
2354 pa_log_debug("Failed to change profile to %s",
2362 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
2364 struct userdata *u = d;
2370 pa_log_debug("deferred routing starts");
2372 mir_router_make_routing(u);
2376 static void schedule_deferred_routing(struct userdata *u)
2381 pa_assert_se((core = u->core));
2383 pa_log_debug("scheduling deferred routing");
2385 pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
2389 static void card_check_cb(pa_mainloop_api *m, void *d)
2391 card_check_t *cc = d;
2397 int n_sink, n_source;
2403 pa_assert((u = cc->u));
2404 pa_assert((core = u->core));
2406 pa_log_debug("card check starts");
2408 if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
2409 pa_log_debug("card %u is gone", cc->index);
2411 n_sink = n_source = 0;
2413 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
2414 if ((sink->card) && sink->card->index == card->index)
2418 PA_IDXSET_FOREACH(source, core->sources, idx) {
2419 if ((source->card) && source->card->index == card->index)
2423 if (n_sink || n_source) {
2424 pa_log_debug("found %u sinks and %u sources belonging to "
2425 "'%s' card", n_sink, n_source, card->name);
2426 pa_log_debug("nothing to do");
2429 pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
2431 mir_router_make_routing(u);
2439 static void schedule_card_check(struct userdata *u, pa_card *card)
2446 pa_assert_se((core = u->core));
2448 pa_log_debug("scheduling card check");
2450 cc = pa_xnew0(card_check_t, 1);
2452 cc->index = card->index;
2454 pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
2458 static void source_cleanup_cb(pa_mainloop_api *m, void *d)
2460 source_cleanup_t *sc = d;
2467 pa_assert((u = sc->u));
2468 pa_assert((core = u->core));
2470 pa_log_debug("source cleanup starts");
2472 pa_loopback_destroy(u->loopback, u->core, sc->loop);
2473 pa_multiplex_destroy(u->multiplex, u->core, sc->mux);
2475 pa_log_debug("source cleanup ends");
2481 static void schedule_source_cleanup(struct userdata *u, mir_node *node)
2484 source_cleanup_t *sc;
2488 pa_assert_se((core = u->core));
2490 pa_log_debug("scheduling source cleanup");
2492 sc = pa_xnew0(source_cleanup_t, 1);
2494 sc->mux = node->mux;
2495 sc->loop = node->loop;
2500 pa_mainloop_api_once(core->mainloop, source_cleanup_cb, sc);
2505 static void stream_uncork_cb(pa_mainloop_api *m, void *d)
2507 stream_uncork_t *suc = d;
2511 pa_sink_input *sinp;
2517 pa_assert((u = suc->u));
2518 pa_assert((core = u->core));
2520 pa_log_debug("start uncorking stream");
2522 if (!(sink = pa_idxset_get_by_index(core->sinks, suc->index))) {
2523 pa_log_debug("sink.%d gone", suc->index);
2527 if (!(sinp = pa_idxset_first(core->sink_inputs, &index))) {
2528 pa_log_debug("sink_input is gone");
2532 pa_sink_input_cork(sinp, FALSE);
2534 pa_log_debug("stream.%u uncorked", sinp->index);
2542 static void schedule_stream_uncorking(struct userdata *u, pa_sink *sink)
2545 stream_uncork_t *suc;
2549 pa_assert_se((core = u->core));
2551 pa_log_debug("scheduling stream uncorking");
2553 suc = pa_xnew0(stream_uncork_t, 1);
2555 suc->index = sink->index;
2557 pa_mainloop_api_once(core->mainloop, stream_uncork_cb, suc);
2564 * indent-tabs-mode: nil