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 *);
121 static char *get_stream_amname(mir_node_type, char *, pa_proplist *);
123 static void set_bluetooth_profile(struct userdata *, pa_card *, pa_direction_t);
126 static void schedule_deferred_routing(struct userdata *);
127 static void schedule_card_check(struct userdata *, pa_card *);
128 static void schedule_source_cleanup(struct userdata *, mir_node *);
130 static void schedule_stream_uncorking(struct userdata *, pa_sink *);
133 static void pa_hashmap_node_free(void *node, void *u)
135 mir_node_destroy(u, node);
139 struct pa_discover *pa_discover_init(struct userdata *u)
141 pa_discover *discover = pa_xnew0(pa_discover, 1);
145 discover->selected = true;
147 discover->nodes.byname = pa_hashmap_new(pa_idxset_string_hash_func,
148 pa_idxset_string_compare_func);
149 discover->nodes.byptr = pa_hashmap_new(pa_idxset_trivial_hash_func,
150 pa_idxset_trivial_compare_func);
154 void pa_discover_done(struct userdata *u)
156 pa_discover *discover;
160 if (u && (discover = u->discover)) {
161 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
162 mir_node_destroy(u, node);
164 pa_hashmap_free(discover->nodes.byname);
165 pa_hashmap_free(discover->nodes.byptr);
171 void pa_discover_domain_up(struct userdata *u)
173 pa_discover *discover;
178 pa_assert_se((discover = u->discover));
180 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
181 node->amid = AM_ID_INVALID;
183 if ((node->visible && node->available) ||
184 (node->type == mir_gateway_sink ||
185 node->type == mir_gateway_source)) {
186 pa_audiomgr_register_node(u, node);
187 extapi_signal_node_change(u);
192 void pa_discover_domain_down(struct userdata *u)
196 void pa_discover_add_card(struct userdata *u, pa_card *card)
203 if (!(bus = pa_utils_get_card_bus(card))) {
204 pa_log_debug("ignoring card '%s' due to lack of '%s' property",
205 pa_utils_get_card_name(card), PA_PROP_DEVICE_BUS);
209 if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform")) {
210 handle_alsa_card(u, card);
213 else if (pa_streq(bus, "bluetooth")) {
214 handle_bluetooth_card(u, card);
218 pa_log_debug("ignoring card '%s' due to unsupported bus type '%s'",
219 pa_utils_get_card_name(card), bus);
222 void pa_discover_remove_card(struct userdata *u, pa_card *card)
225 pa_discover *discover;
231 pa_assert_se((discover = u->discover));
233 if (!(bus = pa_utils_get_card_bus(card)))
237 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
238 if (node->implement == mir_device &&
239 node->pacard.index == card->index)
241 if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform"))
242 mir_constrain_destroy(u, node->paname);
244 destroy_node(u, node);
248 if (pa_streq(bus, "bluetooth"))
249 mir_constrain_destroy(u, card->name);
252 void pa_discover_profile_changed(struct userdata *u, pa_card *card)
255 pa_card_profile *prof;
258 pa_discover *discover;
272 pa_assert_se((core = u->core));
273 pa_assert_se((discover = u->discover));
276 if ((bus = pa_utils_get_card_bus(card)) == NULL) {
277 pa_log_debug("ignoring profile change on card '%s' due to lack of '%s'"
278 "property", pa_utils_get_card_name(card),
283 pci = pa_streq(bus, "pci");
284 usb = pa_streq(bus, "usb");
285 bluetooth = pa_streq(bus, "bluetooth");
286 platform = pa_streq(bus, "platform");
288 if (!pci && !usb && !bluetooth && !platform) {
289 pa_log_debug("ignoring profile change on card '%s' due to unsupported "
290 "bus type '%s'", pa_utils_get_card_name(card), bus);
291 u->state.sink = u->state.source = PA_IDXSET_INVALID;
295 if ((index = u->state.sink) != PA_IDXSET_INVALID) {
296 if ((sink = pa_idxset_get_by_index(core->sinks, index)))
297 pa_discover_add_sink(u, sink, true);
299 pa_log_debug("sink.%u is gone", index);
300 u->state.sink = PA_IDXSET_INVALID;
303 if ((index = u->state.source) != PA_IDXSET_INVALID) {
304 if ((source = pa_idxset_get_by_index(core->sources, index)))
305 pa_discover_add_source(u, source);
307 pa_log_debug("source.%u is gone", index);
308 u->state.source = PA_IDXSET_INVALID;
312 pa_assert_se((prof = card->active_profile));
314 pa_log_debug("bluetooth profile changed to '%s' on card '%s'",
315 prof->name, card->name);
317 if (!prof->n_sinks && !prof->n_sources) {
318 /* switched off but not unloaded yet */
319 need_routing = false;
321 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
322 if (node->implement == mir_device &&
323 node->pacard.index == card->index)
325 if (node->type != mir_bluetooth_a2dp &&
326 node->type != mir_bluetooth_sco)
328 if (node->available) {
329 node->available = false;
337 schedule_deferred_routing(u);
341 pa_log_debug("alsa profile changed to '%s' on card '%s'",
342 card->active_profile->name, card->name);
344 stamp = pa_utils_get_stamp();
346 handle_alsa_card(u, card);
348 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
349 if (node->implement == mir_device &&
350 node->pacard.index == card->index &&
353 destroy_node(u, node);
360 void pa_discover_port_available_changed(struct userdata *u,
361 pa_device_port *port)
372 pa_direction_t direction;
377 pa_assert_se((core = u->core));
379 switch (port->available) {
380 case PA_AVAILABLE_NO: state = "not available"; break;
381 case PA_AVAILABLE_YES: state = "available"; break;
382 default: state = "unknown"; break;
385 pa_log_debug("port '%s' availabilty changed to %s. Updating",
393 while ((node = pa_utils_get_node_from_port(u, port, &iter))) {
395 available = get_bluetooth_port_availability(node, port);
396 route |= update_node_availability(u, node, available);
397 direction |= (node->direction == mir_input) ? PA_DIRECTION_INPUT : PA_DIRECTION_OUTPUT;
401 set_bluetooth_profile(u, port->card, direction);
403 switch (port->available) {
404 case PA_AVAILABLE_NO: available = false; break;
405 case PA_AVAILABLE_YES: available = true; break;
406 default: /* do nothing */ return;
409 if (port->direction == PA_DIRECTION_OUTPUT) {
410 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
412 if (port == pa_hashmap_get(sink->ports, port->name)) {
413 pa_log_debug(" sink '%s'", sink->name);
414 route |= update_node_availability_by_device(
423 if (port->direction == PA_DIRECTION_INPUT) {
424 PA_IDXSET_FOREACH(source, core->sources, idx) {
426 if (port == pa_hashmap_get(source->ports, port->name)) {
427 pa_log_debug(" source '%s'", source->name);
428 route |= update_node_availability_by_device(
439 mir_router_make_routing(u);
442 void pa_discover_add_sink(struct userdata *u, pa_sink *sink, bool route)
444 static pa_nodeset_resdef def_resdef = {0, {0, 0}};
447 pa_discover *discover;
454 const char *loopback_role;
456 pa_nodeset_resdef *resdef;
458 pa_nodeset_resdef rdbuf;
466 pa_assert_se((core = u->core));
467 pa_assert_se((discover = u->discover));
469 module = sink->module;
471 if ((card = sink->card)) {
472 if (!(key = node_key(u, mir_output,sink,ACTIVE_PORT, kbf,sizeof(kbf))))
474 if (!(node = pa_discover_find_node_by_key(u, key))) {
475 if (u->state.profile)
476 pa_log_debug("can't find node for sink (key '%s')", key);
478 u->state.sink = sink->index;
481 pa_log_debug("node for '%s' found (key %s). Updating with sink data",
482 node->paname, node->key);
483 node->paidx = sink->index;
484 node->available = true;
485 pa_discover_add_node_to_ptr_hash(u, sink, node);
487 if ((loopback_role = pa_classify_loopback_stream(node))) {
488 if (!(ns = pa_utils_get_null_source(u))) {
489 pa_log("Can't load loopback module: no initial null source");
493 map = pa_nodeset_get_map_by_role(u, loopback_role);
494 make_rset = (map && map->resdef);
495 resdef = make_rset ? map->resdef : &def_resdef;
497 node->loop = pa_loopback_create(u->loopback, core,
498 PA_LOOPBACK_SINK, node->index,
499 ns->index, sink->index,
503 resdef->flags.audio);
505 mir_node_print(node, nbf, sizeof(nbf));
506 pa_log_debug("updated node:\n%s", nbf);
509 pa_murphyif_create_resource_set(u, node, resdef);
515 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
516 mir_router_make_routing(u);
518 if (!u->state.profile)
519 schedule_deferred_routing(u);
523 else if (!module || !pa_streq(module->name, "module-combine-sink")) {
526 memset(&data, 0, sizeof(data));
527 data.key = pa_xstrdup(sink->name);
528 data.direction = mir_output;
529 data.implement = mir_device;
530 data.channels = sink->channel_map.channels;
531 data.available = true;
532 data.paidx = sink->index;
534 if (sink == pa_utils_get_null_sink(u)) {
535 data.visible = false;
536 data.type = mir_null;
537 data.amname = pa_xstrdup("Silent");
538 data.amid = AM_ID_INVALID;
539 data.paname = pa_xstrdup(sink->name);
541 else if (pa_classify_node_by_property(&data, sink->proplist)) {
542 if (data.type == mir_gateway_sink) {
543 data.privacy = mir_private;
544 data.visible = false;
545 data.amname = pa_xstrdup(sink->name);
546 data.amid = AM_ID_INVALID;
547 data.paname = pa_xstrdup(sink->name);
550 data.privacy = mir_public;
552 data.amname = pa_xstrdup(mir_node_type_str(data.type));
553 data.amid = AM_ID_INVALID;
554 data.paname = pa_xstrdup(sink->name);
560 pa_xfree(data.key); /* for now */
561 pa_log_info("currently we do not support statically loaded "
562 "sinks without " PA_PROP_NODE_TYPE " property");
566 node = create_node(u, &data, NULL);
569 pa_discover_add_node_to_ptr_hash(u, sink, node);
574 void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
576 pa_discover *discover;
583 pa_assert_se((discover = u->discover));
585 name = pa_utils_get_sink_name(sink);
587 if (!(node = pa_hashmap_get(discover->nodes.byptr, sink)))
588 pa_log_debug("can't find node for sink (name '%s')", name);
590 pa_log_debug("node found for '%s'. Reseting sink data", name);
591 pa_murphyif_destroy_resource_set(u, node);
592 schedule_source_cleanup(u, node);
593 node->paidx = PA_IDXSET_INVALID;
594 pa_hashmap_remove(discover->nodes.byptr, sink);
599 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
600 node->available = false;
602 if (!u->state.profile)
603 schedule_deferred_routing(u);
607 pa_log_info("currently we do not support statically loaded sinks");
613 void pa_discover_add_source(struct userdata *u, pa_source *source)
615 static pa_nodeset_resdef def_resdef = {0, {0, 0}};
618 pa_discover *discover;
624 const char *loopback_role;
626 pa_nodeset_resdef *resdef;
634 pa_assert_se((core = u->core));
635 pa_assert_se((discover = u->discover));
637 if ((card = source->card)) {
638 if (!(key = node_key(u,mir_input,source,ACTIVE_PORT,kbf,sizeof(kbf))))
640 if (!(node = pa_discover_find_node_by_key(u, key))) {
641 if (u->state.profile)
642 pa_log_debug("can't find node for source (key '%s')", key);
644 u->state.source = source->index;
647 pa_log_debug("node for '%s' found. Updating with source data",
649 node->paidx = source->index;
650 node->available = true;
651 pa_discover_add_node_to_ptr_hash(u, source, node);
652 if ((loopback_role = pa_classify_loopback_stream(node))) {
653 if (!(ns = pa_utils_get_null_sink(u))) {
654 pa_log("Can't load loopback module: no initial null sink");
658 map = pa_nodeset_get_map_by_role(u, loopback_role);
659 make_rset = (map && map->resdef);
660 resdef = make_rset ? map->resdef : &def_resdef;
662 node->loop = pa_loopback_create(u->loopback, core,
663 PA_LOOPBACK_SOURCE, node->index,
664 source->index, ns->index,
668 resdef->flags.audio);
670 sink_index = pa_loopback_get_sink_index(core, node->loop);
671 node->mux = pa_multiplex_find_by_sink(u->multiplex,sink_index);
674 mir_node_print(node, nbf, sizeof(nbf));
675 pa_log_debug("updated node:\n%s", nbf);
678 pa_murphyif_create_resource_set(u, node, resdef);
680 pa_fader_apply_volume_limits(u, node->stamp);
684 memset(&data, 0, sizeof(data));
685 data.key = pa_xstrdup(source->name);
686 data.direction = mir_input;
687 data.implement = mir_device;
688 data.channels = source->channel_map.channels;
689 data.available = true;
691 if (source == pa_utils_get_null_source(u)) {
692 data.visible = false;
693 data.type = mir_null;
694 data.amname = pa_xstrdup("Silent");
695 data.amid = AM_ID_INVALID;
696 data.paname = pa_xstrdup(source->name);
697 data.paidx = source->index;
699 else if (pa_classify_node_by_property(&data, source->proplist)) {
700 if (data.type == mir_gateway_source) {
701 data.privacy = mir_private;
702 data.visible = false;
703 data.amname = pa_xstrdup(source->name);
704 data.amid = AM_ID_INVALID;
705 data.paname = pa_xstrdup(source->name);
708 data.privacy = mir_public;
710 data.amname = pa_xstrdup(mir_node_type_str(data.type));
711 data.amid = AM_ID_INVALID;
712 data.paname = pa_xstrdup(source->name);
716 pa_xfree(data.key); /* for now */
717 pa_log_info("currently we do not support statically loaded "
718 "sources without " PA_PROP_NODE_TYPE " property");
722 create_node(u, &data, NULL);
726 void pa_discover_remove_source(struct userdata *u, pa_source *source)
728 pa_discover *discover;
735 pa_assert_se((discover = u->discover));
737 name = pa_utils_get_source_name(source);
739 if (!(node = pa_hashmap_get(discover->nodes.byptr, source)))
740 pa_log_debug("can't find node for source (name '%s')", name);
742 pa_log_debug("node found. Reseting source data");
743 pa_murphyif_destroy_resource_set(u, node);
744 schedule_source_cleanup(u, node);
745 node->paidx = PA_IDXSET_INVALID;
746 pa_hashmap_remove(discover->nodes.byptr, source);
751 if (type != mir_bluetooth_sco)
752 node->available = false;
754 if (!u->state.profile)
755 schedule_deferred_routing(u);
759 pa_log_info("currently we do not support statically "
766 void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
769 pa_discover *discover;
780 pa_nodeset_resdef *resdef;
784 pa_assert_se((core = u->core));
785 pa_assert_se((discover = u->discover));
786 pa_assert_se((pl = sinp->proplist));
788 if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME))) {
789 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
790 pa_log_debug("Seems to be a combine stream. Nothing to do ...");
793 if (!strncmp(media, loopback_outpatrn, sizeof(loopback_outpatrn)-1)) {
794 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
799 name = pa_utils_get_sink_input_name(sinp);
801 pa_log_debug("registering input stream '%s'", name);
803 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
804 pa_log_debug("cant find stream class for '%s'. "
805 "Leaving it alone", name);
809 pa_utils_set_stream_routing_properties(pl, type, NULL);
811 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
813 memset(&data, 0, sizeof(data));
815 data.direction = mir_input;
816 data.implement = mir_stream;
817 data.channels = sinp->channel_map.channels;
819 data.zone = pa_utils_get_zone(sinp->proplist);
821 data.available = true;
822 data.amname = get_stream_amname(type, name, pl);
823 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
824 data.amid = AM_ID_INVALID;
826 data.paidx = sinp->index;
827 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
830 * here we can't guess whether the application requested an explicit
831 * route by sepcifying the target sink @ stream creation time.
833 * the brute force solution: we make a default route for this stream
834 * possibly overwiriting the orginal app request :(
836 /* this will set data.mux */
837 role = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_ROLE);
838 sink = make_output_prerouting(u, &data, &sinp->channel_map, role, &target);
840 node = create_node(u, &data, NULL);
842 pa_discover_add_node_to_ptr_hash(u, sinp, node);
844 if (sink && target) {
845 pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
847 if (pa_sink_input_move_to(sinp, sink, false) < 0)
848 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
850 pa_audiomgr_add_default_route(u, node, target);
854 bool pa_discover_preroute_sink_input(struct userdata *u,
855 pa_sink_input_new_data *data)
860 pa_discover *discover;
861 pa_multiplex *multiplex;
870 pa_nodeset_resdef *resdef;
876 pa_assert_se((core = u->core));
877 pa_assert_se((discover = u->discover));
878 pa_assert_se((multiplex = u->multiplex));
879 pa_assert_se((pl = data->proplist));
881 mnam = (m = data->module) ? m->name : "";
883 if (pa_streq(mnam, "module-combine-sink")) {
885 type = mir_node_type_unknown;
887 if (!(mux = pa_multiplex_find_by_module(multiplex, m)) ||
888 !(sink = pa_idxset_get_by_index(core->sinks, mux->sink_index)) ||
889 !(sinp = pa_idxset_first(sink->inputs, NULL)) ||
890 !(type = pa_utils_get_stream_class(sinp->proplist)))
892 pa_log_debug("can't figure out the type of multiplex stream");
895 pa_utils_set_stream_routing_properties(data->proplist, type, NULL);
899 loopback = pa_streq(mnam, "module-loopback");
903 if (!(node = pa_utils_get_node_from_data(u, mir_input, data))) {
904 pa_log_debug("can't find loopback node for sink-input");
908 if (node->direction == mir_output) {
909 pa_log_debug("refuse to preroute loopback sink-input "
910 "(current route: sink %u @ %p)", data->sink ?
911 data->sink->index : PA_IDXSET_INVALID,data->sink);
917 type = pa_classify_guess_stream_node_type(u, pl, NULL);
920 remap = pa_streq(mnam, "module-remap-sink");
921 type = pa_classify_guess_stream_node_type(u, pl, &resdef);
923 pa_utils_set_resource_properties(pl, resdef);
925 if (pa_stream_state_start_corked(u, data, resdef)) {
926 pa_log_debug("start corked");
930 pa_utils_set_stream_routing_properties(pl, type, data->sink);
933 memset(&fake, 0, sizeof(fake));
934 fake.direction = mir_input;
935 fake.implement = mir_stream;
939 fake.channels = data->channel_map.channels;
940 fake.zone = pa_utils_get_zone(data->proplist);
942 fake.available = true;
943 fake.amname = "<preroute sink-input>";
944 fake.amid = AM_ID_INVALID;
945 fake.paidx = PA_IDXSET_INVALID;
947 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
948 sink = make_output_prerouting(u, &fake, &data->channel_map, role,NULL);
952 if (fake.mux && !(data->flags & PA_SINK_INPUT_START_CORKED)) {
953 data->flags |= PA_SINK_INPUT_START_CORKED;
954 schedule_stream_uncorking(u, sink);
958 if (pa_sink_input_new_data_set_sink(data, sink, false))
959 pa_log_debug("set sink %u for new sink-input", sink->index);
961 pa_log("can't set sink %u for new sink-input", sink->index);
962 /* copes wit NULL mux */
963 pa_multiplex_destroy(u->multiplex, core, fake.mux);
973 if (loopback && data->sink && data->sink->module) {
975 if (pa_streq(data->sink->module->name, "module-combine-sink"))
979 if (pa_classify_ramping_stream(&fake)) {
980 pa_log_debug("set sink-input ramp-muted");
981 data->flags |= PA_SINK_INPUT_START_RAMP_MUTED;
988 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
992 pa_sink_input *csinp;
994 pa_discover *discover;
995 pa_multiplex *multiplex;
1005 pa_nodeset_resdef *resdef;
1006 pa_nodeset_resdef rdbuf;
1010 pa_assert_se((core = u->core));
1011 pa_assert_se((discover = u->discover));
1012 pa_assert_se((multiplex = u->multiplex));
1013 pa_assert_se((pl = sinp->proplist));
1017 if (!(media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)))
1018 media = "<unknown>";
1020 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
1021 if (!pa_utils_stream_has_default_route(sinp->proplist) ||
1022 !(mux = pa_multiplex_find_by_module(multiplex, sinp->module)) ||
1023 mux->defstream_index != PA_IDXSET_INVALID)
1025 pa_log_debug("New stream is a combine stream. Nothing to do ...");
1028 pa_log_debug("New stream is a combine stream. Setting as default");
1029 mux->defstream_index = sinp->index;
1030 mir_router_make_routing(u);
1033 } else if (!strncmp(media, loopback_outpatrn,sizeof(loopback_outpatrn)-1)){
1034 pa_log_debug("New stream is a loopback output stream");
1036 if ((node = pa_utils_get_node_from_stream(u, mir_input, sinp))) {
1037 if (node->direction == mir_input)
1038 pa_log_debug("loopback stream node '%s' found", node->amname);
1040 pa_log_debug("ignoring it");
1045 pa_log_debug("can't find node for the loopback stream");
1052 name = pa_utils_get_sink_input_name(sinp);
1054 pa_log_debug("dealing with new input stream '%s'", name);
1056 if ((type = get_stream_routing_class(pl)))
1057 resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1059 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1060 pa_log_debug("cant find stream class for '%s'. "
1061 "Leaving it alone", name);
1065 pa_utils_set_stream_routing_properties(pl, type, NULL);
1067 /* if needed, make some post-routing here */
1070 /* we need to add this to main hashmap as that is used for loop
1071 through on all nodes. */
1072 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
1074 memset(&data, 0, sizeof(data));
1076 data.direction = mir_input;
1077 data.implement = mir_stream;
1078 data.channels = sinp->channel_map.channels;
1080 data.zone = pa_utils_get_zone(pl);
1081 data.visible = true;
1082 data.available = true;
1083 data.amname = get_stream_amname(type, name, pl);
1084 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1085 data.amid = AM_ID_INVALID;
1087 data.paidx = sinp->index;
1088 data.mux = pa_multiplex_find_by_sink(u->multiplex,
1090 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1091 node = create_node(u, &data, &created);
1096 pa_log("%s: confused with stream. '%s' did exists",
1097 __FILE__, node->amname);
1102 pa_murphyif_add_node(u, node);
1104 pa_murphyif_create_resource_set(u, node, resdef);
1106 pa_discover_add_node_to_ptr_hash(u, sinp, node);
1111 csinp = pa_idxset_get_by_index(core->sink_inputs,
1112 data.mux->defstream_index);
1113 s = csinp ? csinp->sink : NULL;
1115 if ((sinp->flags & PA_SINK_INPUT_START_RAMP_MUTED)) {
1116 pa_log_debug("ramp '%s' to 100%", media);
1117 pa_fader_ramp_volume(u, sinp, PA_VOLUME_NORM);
1123 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1125 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1126 pa_log_debug("can't figure out where this stream is routed");
1128 pa_log_debug("register route '%s' => '%s'",
1129 node->amname, snod->amname);
1131 if (pa_utils_stream_has_default_route(sinp->proplist))
1132 pa_audiomgr_add_default_route(u, node, snod);
1134 /* FIXME: register explicit routes */
1135 /* else pa_audiomgr_add/register_explicit_route() */
1138 pa_fader_apply_volume_limits(u, pa_utils_get_stamp());
1143 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
1145 pa_discover *discover;
1149 bool had_properties = false;
1153 pa_assert_se((discover = u->discover));
1155 name = pa_utils_get_sink_input_name(sinp);
1157 pa_log_debug("sink-input '%s' going to be destroyed", name);
1160 had_properties = pa_utils_unset_stream_routing_properties(sinp->proplist);
1162 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sinp))) {
1163 if (!pa_multiplex_sink_input_remove(u->multiplex, sinp))
1164 pa_log_debug("nothing to do for sink-input (name '%s')", name);
1167 pa_log_debug("node found for '%s'. After clearing routes "
1168 "it will be destroyed", name);
1170 if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
1171 pa_log_debug("can't figure out where this stream is routed");
1173 pa_log_debug("clear route '%s' => '%s'",
1174 node->amname, sinknod->amname);
1176 /* FIXME: and actually do it ... */
1180 destroy_node(u, node);
1183 if (node || had_properties)
1184 mir_router_make_routing(u);
1188 void pa_discover_register_source_output(struct userdata *u,
1189 pa_source_output *sout)
1192 pa_discover *discover;
1203 pa_nodeset_resdef *resdef;
1207 pa_assert_se((core = u->core));
1208 pa_assert_se((discover = u->discover));
1209 pa_assert_se((pl = sout->proplist));
1211 if ((media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME))) {
1212 if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1213 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
1218 name = pa_utils_get_source_output_name(sout);
1220 pa_log_debug("registering output stream '%s'", name);
1222 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1223 pa_log_debug("cant find stream class for '%s'. "
1224 "Leaving it alone", name);
1228 pa_utils_set_stream_routing_properties(pl, type, NULL);
1230 snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1232 memset(&data, 0, sizeof(data));
1234 data.direction = mir_output;
1235 data.implement = mir_stream;
1236 data.channels = sout->channel_map.channels;
1238 data.zone = pa_utils_get_zone(sout->proplist);
1239 data.visible = true;
1240 data.available = true;
1242 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1243 data.amid = AM_ID_INVALID;
1245 data.paidx = sout->index;
1246 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1249 * here we can't guess whether the application requested an explicit
1250 * route by sepcifying the target source @ stream creation time.
1252 * the brute force solution: we make a default route for this stream
1253 * possibly overwiriting the orginal app request :(
1255 role = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_ROLE);
1256 source = make_input_prerouting(u, &data, role, &target);
1258 node = create_node(u, &data, NULL);
1260 pa_discover_add_node_to_ptr_hash(u, sout, node);
1262 if (source && target) {
1263 pa_log_debug("move stream to source %u (%s)",
1264 source->index, source->name);
1266 if (pa_source_output_move_to(sout, source, false) < 0)
1267 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
1269 pa_log_debug("register route '%s' => '%s'",
1270 node->amname, target->amname);
1271 /* FIXME: and actually do it ... */
1276 bool pa_discover_preroute_source_output(struct userdata *u,
1277 pa_source_output_new_data *data)
1282 pa_discover *discover;
1289 pa_nodeset_resdef *resdef;
1293 pa_assert_se((core = u->core));
1294 pa_assert_se((discover = u->discover));
1295 pa_assert_se((pl = data->proplist));
1297 mnam = (m = data->module) ? m->name : "";
1299 if (pa_streq(mnam, "module-loopback")) {
1300 if (!(node = pa_utils_get_node_from_data(u, mir_output, data))) {
1301 pa_log_debug("can't find loopback node for source-output");
1305 if (node->direction == mir_input) {
1306 pa_log_debug("refuse to preroute loopback source-output "
1307 "(current route: source %u @ %p)", data->source ?
1308 data->source->index : PA_IDXSET_INVALID,data->source);
1312 data->source = NULL;
1314 type = pa_classify_guess_stream_node_type(u, pl, NULL);
1317 type = pa_classify_guess_stream_node_type(u, pl, &resdef);
1319 pa_utils_set_resource_properties(pl, resdef);
1322 pa_utils_set_stream_routing_properties(pl, type, data->source);
1324 if (!data->source) {
1325 memset(&fake, 0, sizeof(fake));
1326 fake.direction = mir_output;
1327 fake.implement = mir_stream;
1328 fake.channels = data->channel_map.channels;
1330 fake.zone = pa_utils_get_zone(data->proplist);
1331 fake.visible = true;
1332 fake.available = true;
1333 fake.amname = "<preroute source-output>";
1335 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
1336 source = make_input_prerouting(u, &fake, role, NULL);
1339 if (pa_source_output_new_data_set_source(data, source, false)) {
1340 pa_log_debug("set source %u for new source-output",
1344 pa_log("can't set source %u for new source-output",
1354 void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
1359 pa_discover *discover;
1368 pa_nodeset_resdef *resdef;
1369 pa_nodeset_resdef rdbuf;
1373 pa_assert_se((core = u->core));
1374 pa_assert_se((discover = u->discover));
1375 pa_assert_se((pl = sout->proplist));
1379 if (!(media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME)))
1380 media = "<unknown>";
1382 if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1383 pa_log_debug("New stream is a loopback input stream");
1385 if ((node = pa_utils_get_node_from_stream(u, mir_output, sout))) {
1386 if (node->direction == mir_output)
1387 pa_log_debug("loopback stream node '%s' found", node->amname);
1389 pa_log_debug("ignoring it");
1394 pa_log_debug("can't find node for the loopback stream");
1399 name = pa_utils_get_source_output_name(sout);
1401 pa_log_debug("dealing with new output stream '%s'", name);
1403 if ((type = get_stream_routing_class(pl)))
1404 resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1406 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1407 pa_log_debug("cant find stream class for '%s'. "
1408 "Leaving it alone", name);
1412 pa_utils_set_stream_routing_properties(pl, type, NULL);
1414 /* if needed, make some post-routing here */
1417 /* we need to add this to main hashmap as that is used for loop
1418 through on all nodes. */
1419 snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1421 memset(&data, 0, sizeof(data));
1423 data.direction = mir_output;
1424 data.implement = mir_stream;
1425 data.channels = sout->channel_map.channels;
1427 data.zone = pa_utils_get_zone(pl);
1428 data.visible = true;
1429 data.available = true;
1431 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1432 data.amid = AM_ID_INVALID;
1434 data.paidx = sout->index;
1435 data.rsetid = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1437 node = create_node(u, &data, &created);
1442 pa_log("%s: confused with stream. '%s' did exists",
1443 __FILE__, node->amname);
1448 pa_murphyif_add_node(u, node);
1450 pa_murphyif_create_resource_set(u, node, resdef);
1452 pa_discover_add_node_to_ptr_hash(u, sout, node);
1455 if ((s = sout->source))
1456 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1458 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1459 pa_log_debug("can't figure out where this stream is routed");
1461 pa_log_debug("register route '%s' => '%s'",
1462 snod->amname, node->amname);
1463 pa_audiomgr_add_default_route(u, node, snod);
1468 void pa_discover_remove_source_output(struct userdata *u,
1469 pa_source_output *sout)
1471 pa_discover *discover;
1478 pa_assert_se((discover = u->discover));
1480 name = pa_utils_get_source_output_name(sout);
1482 pa_log_debug("source-output '%s' going to be destroyed", name);
1484 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sout)))
1485 pa_log_debug("can't find node for source-output (name '%s')", name);
1487 pa_log_debug("node found for '%s'. After clearing routes "
1488 "it will be destroyed", name);
1490 if (!(srcnod = pa_hashmap_get(discover->nodes.byptr, sout->source)))
1491 pa_log_debug("can't figure out where this stream is routed");
1493 pa_log_debug("clear route '%s' => '%s'",
1494 node->amname, srcnod->amname);
1496 /* FIXME: and actually do it ... */
1500 destroy_node(u, node);
1502 mir_router_make_routing(u);
1507 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
1509 pa_discover *discover;
1513 pa_assert_se((discover = u->discover));
1516 node = pa_hashmap_get(discover->nodes.byname, key);
1523 mir_node *pa_discover_find_node_by_ptr(struct userdata *u, void *ptr)
1525 pa_discover *discover;
1529 pa_assert_se((discover = u->discover));
1532 node = pa_hashmap_get(discover->nodes.byptr, ptr);
1539 void pa_discover_add_node_to_ptr_hash(struct userdata *u,
1543 pa_discover *discover;
1548 pa_assert_se((discover = u->discover));
1550 pa_hashmap_put(discover->nodes.byptr, ptr, node);
1553 mir_node *pa_discover_remove_node_from_ptr_hash(struct userdata *u, void *ptr)
1555 pa_discover *discover;
1559 pa_assert_se((discover = u->discover));
1561 return pa_hashmap_remove(discover->nodes.byptr, ptr);
1565 static void handle_alsa_card(struct userdata *u, pa_card *card)
1572 memset(&data, 0, sizeof(data));
1573 data.zone = pa_utils_get_zone(card->proplist);
1574 data.visible = true;
1575 data.amid = AM_ID_INVALID;
1576 data.implement = mir_device;
1577 data.paidx = PA_IDXSET_INVALID;
1578 data.stamp = pa_utils_get_stamp();
1580 cnam = pa_utils_get_card_name(card);
1581 udd = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
1583 if (udd && pa_streq(udd, "1")) {
1584 /* udev loaded alsa card */
1585 if (!strncmp(cnam, "alsa_card.", 10)) {
1587 handle_udev_loaded_card(u, card, &data, cid);
1592 /* statically loaded pci or usb card */
1595 pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
1600 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1602 pa_discover *discover;
1603 pa_card_profile *prof;
1611 char paname[MAX_NAME_LENGTH+1];
1612 char amname[MAX_NAME_LENGTH+1];
1613 char key[MAX_NAME_LENGTH+1];
1615 pa_assert_se((discover = u->discover));
1617 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1620 memset(paname, 0, sizeof(paname));
1621 memset(amname, 0, sizeof(amname));
1622 memset(key , 0, sizeof(key) );
1624 memset(&data, 0, sizeof(data));
1626 data.visible = true;
1627 data.amid = AM_ID_INVALID;
1628 data.implement = mir_device;
1629 data.paidx = PA_IDXSET_INVALID;
1630 data.paname = paname;
1631 data.amname = amname;
1632 data.amdescr = (char *)cdescr;
1633 data.pacard.index = card->index;
1634 data.stamp = pa_utils_get_stamp();
1636 cnam = pa_utils_get_card_name(card);
1638 if (!strncmp(cnam, "bluez_card.", 11)) {
1641 pa_assert(card->ports);
1643 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1645 PA_HASHMAP_FOREACH(prof, card->profiles, cstate) {
1646 data.available = false;
1647 data.pacard.profile = prof->name;
1649 if (prof->n_sinks > 0) {
1650 data.direction = mir_output;
1651 data.channels = prof->max_sink_channels;
1652 data.amname = amname;
1654 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1655 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1656 pa_classify_node_by_card(&data, card, prof, NULL);
1657 node = create_node(u, &data, NULL);
1658 mir_constrain_add_node(u, cd, node);
1661 if (prof->n_sources > 0) {
1662 data.direction = mir_input;
1663 data.channels = prof->max_source_channels;
1664 data.amname = amname;
1666 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1667 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1668 pa_classify_node_by_card(&data, card, prof, NULL);
1669 node = create_node(u, &data, NULL);
1670 mir_constrain_add_node(u, cd, node);
1674 if (!(prof = card->active_profile))
1675 pa_log("card '%s' has no active profile", card->name);
1677 pa_log_debug("card '%s' default profile '%s'",
1678 card->name, prof->name);
1681 schedule_card_check(u, card);
1687 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1689 pa_discover *discover;
1690 pa_card_profile *prof;
1691 pa_device_port *port;
1698 void *state0, *state1;
1699 char paname[MAX_NAME_LENGTH+1];
1700 char amname[MAX_NAME_LENGTH+1];
1701 char key[MAX_NAME_LENGTH+1];
1706 pa_assert_se((discover = u->discover));
1708 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1711 memset(paname, 0, sizeof(paname));
1712 memset(amname, 0, sizeof(amname));
1713 memset(key , 0, sizeof(key) );
1715 memset(&data, 0, sizeof(data));
1717 data.zone = pa_utils_get_zone(card->proplist);
1718 data.visible = true;
1719 data.amid = AM_ID_INVALID;
1720 data.implement = mir_device;
1721 data.paidx = PA_IDXSET_INVALID;
1722 data.paname = paname;
1723 data.amname = amname;
1724 data.amdescr = (char *)cdescr;
1725 data.pacard.index = card->index;
1726 data.stamp = pa_utils_get_stamp();
1728 cnam = pa_utils_get_card_name(card);
1730 if (!strncmp(cnam, "bluez_card.", 11)) {
1733 pa_assert(card->ports);
1735 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1737 PA_HASHMAP_FOREACH(port, card->ports, state0) {
1738 pa_assert(port->profiles);
1741 input = output = true;
1742 len = strlen(port->name);
1743 if (len >= 6 && !strcmp("-input", port->name + (len-6)))
1745 else if (len >= 7 && !strcmp("-output", port->name + (len-7)))
1749 PA_HASHMAP_FOREACH(prof, port->profiles, state1) {
1750 data.pacard.profile = prof->name;
1751 data.available = get_bluetooth_port_availability(&data, port);
1753 if (output && prof->n_sinks > 0) {
1754 data.direction = mir_output;
1755 data.channels = prof->max_sink_channels;
1756 data.amname = amname;
1758 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1759 snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1760 pa_classify_node_by_card(&data, card, prof, NULL);
1761 node = create_node(u, &data, NULL);
1762 mir_constrain_add_node(u, cd, node);
1763 pa_utils_set_port_properties(port, node);
1766 if (input && prof->n_sources > 0) {
1767 data.direction = mir_input;
1768 data.channels = prof->max_source_channels;
1769 data.amname = amname;
1771 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1772 snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1773 pa_classify_node_by_card(&data, card, prof, NULL);
1774 node = create_node(u, &data, NULL);
1775 mir_constrain_add_node(u, cd, node);
1776 pa_utils_set_port_properties(port, node);
1781 if (!(prof = card->active_profile))
1782 pa_log("card '%s' has no active profile", card->name);
1784 pa_log_debug("card '%s' default profile '%s'",
1785 card->name, prof->name);
1788 schedule_card_check(u, card);
1792 static bool get_bluetooth_port_availability(mir_node *node,
1793 pa_device_port *port)
1795 bool available = false;
1801 if ((prof = node->pacard.profile)) {
1802 if (!strcmp(prof, "hfgw") ||
1803 !strcmp(prof, "a2dp_source") ||
1804 !strcmp(prof, "a2dp_sink"))
1805 available = (port->available != PA_AVAILABLE_NO);
1813 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
1814 mir_node *data, char *cardid)
1816 pa_discover *discover;
1817 pa_card_profile *prof;
1818 pa_card_profile *active;
1820 const char *alsanam;
1822 char *sinks[MAX_CARD_TARGET+1];
1823 char *sources[MAX_CARD_TARGET+1];
1824 char buf[MAX_NAME_LENGTH+1];
1825 char paname[MAX_NAME_LENGTH+1];
1826 char amname[MAX_NAME_LENGTH+1];
1830 pa_assert(card->profiles);
1831 pa_assert_se((discover = u->discover));
1833 alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
1835 memset(amname, 0, sizeof(amname));
1837 data->paname = paname;
1838 data->amname = amname;
1839 data->amdescr = (char *)alsanam;
1841 data->pacard.index = card->index;
1843 active = card->active_profile;
1845 PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1846 /* filtering: deal with selected profiles if requested so */
1847 if (discover->selected && (!active || (active && prof != active)))
1850 /* filtering: skip the 'off' profiles */
1851 if (!prof->n_sinks && !prof->n_sources)
1854 /* filtering: consider sinks with suitable amount channels */
1855 if (prof->n_sinks &&
1856 (prof->max_sink_channels < discover->chmin ||
1857 prof->max_sink_channels > discover->chmax ))
1860 /* filtering: consider sources with suitable amount channels */
1861 if (prof->n_sources &&
1862 (prof->max_source_channels < discover->chmin ||
1863 prof->max_source_channels > discover->chmax ))
1866 data->pacard.profile = prof->name;
1868 parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
1870 data->direction = mir_output;
1871 data->channels = prof->max_sink_channels;
1872 for (i = 0; (sid = sinks[i]); i++) {
1873 snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
1874 handle_card_ports(u, data, card, prof);
1877 data->direction = mir_input;
1878 data->channels = prof->max_source_channels;
1879 for (i = 0; (sid = sources[i]); i++) {
1880 snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
1881 handle_card_ports(u, data, card, prof);
1887 static void handle_card_ports(struct userdata *u, mir_node *data,
1888 pa_card *card, pa_card_profile *prof)
1890 mir_node *node = NULL;
1891 bool have_ports = false;
1892 mir_constr_def *cd = NULL;
1893 char *amname = data->amname;
1894 pa_device_port *port;
1897 char key[MAX_NAME_LENGTH+1];
1905 PA_HASHMAP_FOREACH(port, card->ports, state) {
1907 * if this port did not belong to any profile
1908 * (ie. prof->profiles == NULL) we assume that this port
1909 * does works with all the profiles
1911 if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
1912 ((port->direction == PA_DIRECTION_INPUT && data->direction == mir_input)||
1913 (port->direction == PA_DIRECTION_OUTPUT && data->direction == mir_output)))
1918 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
1921 data->available = (port->available != PA_AVAILABLE_NO);
1923 data->amname = amname;
1924 data->paport = port->name;
1926 pa_classify_node_by_card(data, card, prof, port);
1928 node = create_node(u, data, &created);
1931 node->stamp = data->stamp;
1933 cd = mir_constrain_create(u, "port", mir_constrain_port,
1935 mir_constrain_add_node(u, cd, node);
1942 data->key = data->paname;
1943 data->available = true;
1945 pa_classify_node_by_card(data, card, prof, NULL);
1947 node = create_node(u, data, &created);
1950 node->stamp = data->stamp;
1953 data->amname = amname;
1958 static mir_node *create_node(struct userdata *u, mir_node *data,
1961 pa_discover *discover;
1968 pa_assert(data->key);
1969 pa_assert(data->paname);
1970 pa_assert_se((discover = u->discover));
1972 if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
1977 node = mir_node_create(u, data);
1978 pa_hashmap_put(discover->nodes.byname, node->key, node);
1980 mir_node_print(node, buf, sizeof(buf));
1981 pa_log_debug("new node:\n%s", buf);
1983 if (node->available)
1984 pa_audiomgr_register_node(u, node);
1988 *created_ret = created;
1993 static void destroy_node(struct userdata *u, mir_node *node)
1995 pa_discover *discover;
1999 pa_assert_se((discover = u->discover));
2002 removed = pa_hashmap_remove(discover->nodes.byname, node->key);
2004 if (node != removed) {
2006 pa_log("%s: confused with data structures: key mismatch. "
2007 " attempted to destroy '%s'; actually destroyed '%s'",
2008 __FILE__, node->key, removed->key);
2010 pa_log("%s: confused with data structures: node '%s' "
2011 "is not in the hash table", __FILE__, node->key);
2015 pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
2017 if (node->implement == mir_stream) {
2018 if (node->direction == mir_input) {
2020 pa_log_debug("removing multiplexer");
2025 pa_audiomgr_unregister_node(u, node);
2027 extapi_signal_node_change(u);
2029 mir_constrain_remove_node(u, node);
2031 pa_loopback_destroy(u->loopback, u->core, node->loop);
2032 pa_multiplex_destroy(u->multiplex, u->core, node->mux);
2034 mir_node_destroy(u, node);
2038 static bool update_node_availability(struct userdata *u,
2045 if ((!available && node->available) ||
2046 ( available && !node->available) )
2048 node->available = available;
2051 pa_audiomgr_register_node(u, node);
2053 pa_audiomgr_unregister_node(u, node);
2055 extapi_signal_node_change(u);
2057 return true; /* routing needed */
2063 static bool update_node_availability_by_device(struct userdata *u,
2064 mir_direction direction,
2066 pa_device_port *port,
2076 pa_assert(direction == mir_input || direction == mir_output);
2078 if ((key = node_key(u, direction, data, port, buf, sizeof(buf)))) {
2079 if (!(node = pa_discover_find_node_by_key(u, key)))
2080 pa_log_debug(" can't find node (key '%s')", key);
2082 pa_log_debug(" node for '%s' found (key %s)",
2083 node->paname, node->key);
2085 return update_node_availability(u, node, available);
2089 return false; /* no routing needed */
2092 static char *get_name(char **string_ptr, int offs)
2094 char c, *name, *end;
2096 name = *string_ptr + offs;
2098 for (end = name; (c = *end); end++) {
2110 static void parse_profile_name(pa_card_profile *prof,
2120 pa_assert(prof->name);
2122 strncpy(buf, prof->name, buflen);
2123 buf[buflen-1] = '\0';
2125 memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2126 memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2129 if (!strncmp(p, "output:", 7)) {
2130 if (i >= MAX_CARD_TARGET) {
2131 pa_log_debug("number of outputs exeeds the maximum %d in "
2132 "profile name '%s'", MAX_CARD_TARGET, prof->name);
2135 sinks[i++] = get_name(&p, 7);
2137 else if (!strncmp(p, "input:", 6)) {
2138 if (j >= MAX_CARD_TARGET) {
2139 pa_log_debug("number of inputs exeeds the maximum %d in "
2140 "profile name '%s'", MAX_CARD_TARGET, prof->name);
2143 sources[j++] = get_name(&p, 6);
2146 pa_log("%s: failed to parse profile name '%s'",
2147 __FILE__, prof->name);
2154 static char *node_key(struct userdata *u, mir_direction direction,
2155 void *data, pa_device_port *port, char *buf, size_t len)
2158 pa_card_profile *profile;
2166 const char *profile_name;
2172 pa_assert(direction == mir_input || direction == mir_output);
2174 if (direction == mir_output) {
2175 pa_sink *sink = data;
2177 name = pa_utils_get_sink_name(sink);
2180 port = sink->active_port;
2183 pa_source *source = data;
2185 name = pa_utils_get_source_name(source);
2186 card = source->card;
2188 port = source->active_port;
2194 pa_assert_se((profile = card->active_profile));
2196 if (!u->state.profile)
2197 profile_name = profile->name;
2199 pa_log_debug("state.profile is not null. '%s' supresses '%s'",
2200 u->state.profile, profile->name);
2201 profile_name = u->state.profile;
2205 if (!(bus = pa_utils_get_card_bus(card))) {
2206 pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
2207 "on its card", type, name, PA_PROP_DEVICE_BUS);
2211 pci = pa_streq(bus, "pci");
2212 usb = pa_streq(bus, "usb");
2213 platform = pa_streq(bus, "platform");
2214 bluetooth = pa_streq(bus, "bluetooth");
2216 if (!pci && !usb && !bluetooth && !platform) {
2217 pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
2218 "of its card", type, name, bus);
2227 snprintf(buf, len, "%s@%s.%s", name, port->name, profile_name);
2235 snprintf(buf, len, "%s@%s", name, port->name);
2242 static pa_sink *make_output_prerouting(struct userdata *u,
2244 pa_channel_map *chmap,
2245 const char *media_role,
2246 mir_node **target_ret)
2250 pa_sink *sink = NULL;
2255 pa_assert_se((core = u->core));
2258 target = mir_router_make_prerouting(u, data);
2261 pa_log("there is no default route for the stream '%s'", data->amname);
2262 else if (target->paidx == PA_IDXSET_INVALID)
2263 pa_log("can't route to default '%s': no sink", target->amname);
2265 if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
2266 pa_log("no route to default '%s': sink is gone", target->amname);
2268 if (u->enable_multiplex == true) {
2269 if (pa_classify_multiplex_stream(data)) {
2270 data->mux = pa_multiplex_create(u->multiplex, core,
2271 sink->index, chmap, NULL,
2272 media_role, data->type);
2274 sink = pa_idxset_get_by_index(core->sinks,
2275 data->mux->sink_index);
2284 *target_ret = target;
2290 static pa_source *make_input_prerouting(struct userdata *u,
2292 const char *media_role,
2293 mir_node **target_ret)
2297 pa_source *source = NULL;
2301 pa_assert_se((core = u->core));
2303 target = mir_router_make_prerouting(u, data);
2306 pa_log("there is no default route for the stream '%s'", data->amname);
2307 else if (target->paidx == PA_IDXSET_INVALID)
2308 pa_log("can't route to default '%s': no source", target->amname);
2310 if (!(source = pa_idxset_get_by_index(core->sources, target->paidx)))
2311 pa_log("no route to default '%s': source is gone",target->amname);
2315 *target_ret = target;
2320 static mir_node_type get_stream_routing_class(pa_proplist *pl)
2326 t = pa_utils_get_stream_class(pl);
2328 if (t >= mir_application_class_begin && t < mir_application_class_end)
2331 return mir_node_type_unknown;
2334 static char *get_stream_amname(mir_node_type type, char *name, pa_proplist *pl)
2347 appid = pa_utils_get_appid(pl);
2349 if (!strcmp(appid, "threaded-ml") ||
2350 !strcmp(appid, "WebProcess") ||
2351 !strcmp(appid,"wrt_launchpad_daemon") )
2353 return "wrtApplication";
2355 return "icoApplication";
2369 static void set_bluetooth_profile(struct userdata *u,
2371 pa_direction_t direction)
2374 pa_device_port *port;
2375 pa_card_profile *prof, *make_active;
2376 void *state0, *state1;
2377 bool port_available;
2383 pa_assert_se((core = u->core));
2389 pa_log_debug("which profile to make active:");
2391 PA_HASHMAP_FOREACH(prof, card->profiles, state0) {
2392 if (!prof->n_sinks && !prof->n_sources) {
2394 pa_log_debug(" considering %s", prof->name);
2400 port_available = false;
2402 PA_HASHMAP_FOREACH(port, card->ports, state1) {
2403 if ((direction & port->direction) &&
2404 pa_hashmap_get(port->profiles, prof->name))
2406 port_available = (port->available != PA_AVAILABLE_NO);
2411 if (!port_available)
2412 pa_log_debug(" ruling out %s (port not available)", prof->name);
2413 else if (prof->available != PA_AVAILABLE_YES)
2414 pa_log_debug(" ruling out %s (profile not available)", prof->name);
2418 if (((direction & PA_DIRECTION_INPUT) && prof->n_sources > 0) ||
2419 ((direction & PA_DIRECTION_OUTPUT) && prof->n_sinks > 0) ) {
2420 if (make_active && prof->priority < make_active->priority)
2421 pa_log_debug(" ruling out %s (low priority)", prof->name);
2423 pa_log_debug(" considering %s", prof->name);
2428 pa_log_debug(" ruling out %s (direction)", prof->name);
2435 pa_log_debug("No suitable profile found. Frustrated and do nothing");
2437 if (make_active == card->active_profile)
2438 pa_log_debug("Profile %s already set. Do nothing", make_active->name);
2440 if (switch_off && nport) {
2441 pa_log_debug("Do not switch to %s as active ports are existing "
2442 "to the other direction", make_active->name);
2445 pa_log_debug("Set profile %s", make_active->name);
2447 if ((prof = pa_hashmap_get(card->profiles, make_active->name)) != NULL &&
2448 pa_card_set_profile(card, prof, false) < 0) {
2449 pa_log_debug("Failed to change profile to %s",
2457 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
2459 struct userdata *u = d;
2465 pa_log_debug("deferred routing starts");
2467 mir_router_make_routing(u);
2471 static void schedule_deferred_routing(struct userdata *u)
2476 pa_assert_se((core = u->core));
2478 pa_log_debug("scheduling deferred routing");
2480 pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
2484 static void card_check_cb(pa_mainloop_api *m, void *d)
2486 card_check_t *cc = d;
2492 int n_sink, n_source;
2498 pa_assert((u = cc->u));
2499 pa_assert((core = u->core));
2501 pa_log_debug("card check starts");
2503 if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
2504 pa_log_debug("card %u is gone", cc->index);
2506 n_sink = n_source = 0;
2508 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
2509 if ((sink->card) && sink->card->index == card->index)
2513 PA_IDXSET_FOREACH(source, core->sources, idx) {
2514 if ((source->card) && source->card->index == card->index)
2518 if (n_sink || n_source) {
2519 pa_log_debug("found %u sinks and %u sources belonging to "
2520 "'%s' card", n_sink, n_source, card->name);
2521 pa_log_debug("nothing to do");
2524 pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
2526 mir_router_make_routing(u);
2534 static void schedule_card_check(struct userdata *u, pa_card *card)
2541 pa_assert_se((core = u->core));
2543 pa_log_debug("scheduling card check");
2545 cc = pa_xnew0(card_check_t, 1);
2547 cc->index = card->index;
2549 pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
2553 static void source_cleanup_cb(pa_mainloop_api *m, void *d)
2555 source_cleanup_t *sc = d;
2562 pa_assert((u = sc->u));
2563 pa_assert((core = u->core));
2565 pa_log_debug("source cleanup starts");
2567 pa_loopback_destroy(u->loopback, u->core, sc->loop);
2568 pa_multiplex_destroy(u->multiplex, u->core, sc->mux);
2570 pa_log_debug("source cleanup ends");
2576 static void schedule_source_cleanup(struct userdata *u, mir_node *node)
2579 source_cleanup_t *sc;
2583 pa_assert_se((core = u->core));
2585 pa_log_debug("scheduling source cleanup");
2587 sc = pa_xnew0(source_cleanup_t, 1);
2589 sc->mux = node->mux;
2590 sc->loop = node->loop;
2595 pa_mainloop_api_once(core->mainloop, source_cleanup_cb, sc);
2600 static void stream_uncork_cb(pa_mainloop_api *m, void *d)
2602 stream_uncork_t *suc = d;
2606 pa_sink_input *sinp;
2612 pa_assert((u = suc->u));
2613 pa_assert((core = u->core));
2615 pa_log_debug("start uncorking stream");
2617 if (!(sink = pa_idxset_get_by_index(core->sinks, suc->index))) {
2618 pa_log_debug("sink.%d gone", suc->index);
2622 if (!(sinp = pa_idxset_first(core->sink_inputs, &index))) {
2623 pa_log_debug("sink_input is gone");
2627 pa_sink_input_cork(sinp, false);
2629 pa_log_debug("stream.%u uncorked", sinp->index);
2637 static void schedule_stream_uncorking(struct userdata *u, pa_sink *sink)
2640 stream_uncork_t *suc;
2644 pa_assert_se((core = u->core));
2646 pa_log_debug("scheduling stream uncorking");
2648 suc = pa_xnew0(stream_uncork_t, 1);
2650 suc->index = sink->index;
2652 pa_mainloop_api_once(core->mainloop, stream_uncork_cb, suc);
2659 * indent-tabs-mode: nil