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 *,
95 mir_node *, const char *);
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 const 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 const char *get_stream_amname(mir_node_type, const 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 struct pa_discover *pa_discover_init(struct userdata *u)
135 pa_discover *discover = pa_xnew0(pa_discover, 1);
139 discover->selected = true;
141 discover->nodes.byname = pa_hashmap_new(pa_idxset_string_hash_func,
142 pa_idxset_string_compare_func);
143 discover->nodes.byptr = pa_hashmap_new(pa_idxset_trivial_hash_func,
144 pa_idxset_trivial_compare_func);
148 void pa_discover_done(struct userdata *u)
150 pa_discover *discover;
154 if (u && (discover = u->discover)) {
155 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
156 mir_node_destroy(u, node);
158 pa_hashmap_free(discover->nodes.byname);
159 pa_hashmap_free(discover->nodes.byptr);
165 void pa_discover_domain_up(struct userdata *u)
167 pa_discover *discover;
172 pa_assert_se((discover = u->discover));
174 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
175 node->amid = AM_ID_INVALID;
177 if ((node->visible && node->available) ||
178 (node->type == mir_gateway_sink ||
179 node->type == mir_gateway_source)) {
180 pa_audiomgr_register_node(u, node);
181 extapi_signal_node_change(u);
186 void pa_discover_domain_down(struct userdata *u)
190 void pa_discover_add_card(struct userdata *u, pa_card *card)
197 if (!(bus = pa_utils_get_card_bus(card))) {
198 pa_log_debug("ignoring card '%s' due to lack of '%s' property",
199 pa_utils_get_card_name(card), PA_PROP_DEVICE_BUS);
203 if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform")) {
204 handle_alsa_card(u, card);
207 else if (pa_streq(bus, "bluetooth")) {
208 handle_bluetooth_card(u, card);
212 pa_log_debug("ignoring card '%s' due to unsupported bus type '%s'",
213 pa_utils_get_card_name(card), bus);
216 void pa_discover_remove_card(struct userdata *u, pa_card *card)
219 pa_discover *discover;
225 pa_assert_se((discover = u->discover));
227 if (!(bus = pa_utils_get_card_bus(card)))
230 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
231 if (node->implement == mir_device &&
232 node->pacard.index == card->index)
234 if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform"))
235 mir_constrain_destroy(u, node->paname);
237 destroy_node(u, node);
241 if (pa_streq(bus, "bluetooth"))
242 mir_constrain_destroy(u, card->name);
245 void pa_discover_profile_changed(struct userdata *u, pa_card *card)
248 pa_card_profile *prof;
251 pa_discover *discover;
265 pa_assert_se((core = u->core));
266 pa_assert_se((discover = u->discover));
268 if ((bus = pa_utils_get_card_bus(card)) == NULL) {
269 pa_log_debug("ignoring profile change on card '%s' due to lack of '%s'"
270 "property", pa_utils_get_card_name(card),
275 pci = pa_streq(bus, "pci");
276 usb = pa_streq(bus, "usb");
277 bluetooth = pa_streq(bus, "bluetooth");
278 platform = pa_streq(bus, "platform");
280 if (!pci && !usb && !bluetooth && !platform) {
281 pa_log_debug("ignoring profile change on card '%s' due to unsupported "
282 "bus type '%s'", pa_utils_get_card_name(card), bus);
283 u->state.sink = u->state.source = PA_IDXSET_INVALID;
287 if ((index = u->state.sink) != PA_IDXSET_INVALID) {
288 if ((sink = pa_idxset_get_by_index(core->sinks, index)))
289 pa_discover_add_sink(u, sink, true);
291 pa_log_debug("sink.%u is gone", index);
292 u->state.sink = PA_IDXSET_INVALID;
295 if ((index = u->state.source) != PA_IDXSET_INVALID) {
296 if ((source = pa_idxset_get_by_index(core->sources, index)))
297 pa_discover_add_source(u, source);
299 pa_log_debug("source.%u is gone", index);
300 u->state.source = PA_IDXSET_INVALID;
304 pa_assert_se((prof = card->active_profile));
306 pa_log_debug("bluetooth profile changed to '%s' on card '%s'",
307 prof->name, card->name);
309 if (!prof->n_sinks && !prof->n_sources) {
310 /* switched off but not unloaded yet */
311 need_routing = false;
313 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
314 if (node->implement == mir_device &&
315 node->pacard.index == card->index)
317 if (node->type != mir_bluetooth_a2dp &&
318 node->type != mir_bluetooth_sco)
320 if (node->available) {
321 node->available = false;
329 schedule_deferred_routing(u);
333 pa_log_debug("alsa profile changed to '%s' on card '%s'",
334 card->active_profile->name, card->name);
336 stamp = pa_utils_get_stamp();
338 handle_alsa_card(u, card);
340 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
341 if (node->implement == mir_device &&
342 node->pacard.index == card->index &&
345 destroy_node(u, node);
352 void pa_discover_port_available_changed(struct userdata *u,
353 pa_device_port *port)
364 pa_direction_t direction;
369 pa_assert_se((core = u->core));
371 switch (port->available) {
372 case PA_AVAILABLE_NO: state = "not available"; break;
373 case PA_AVAILABLE_YES: state = "available"; break;
374 default: state = "unknown"; break;
377 pa_log_debug("port '%s' availabilty changed to %s. Updating",
385 while ((node = pa_utils_get_node_from_port(u, port, &iter))) {
387 available = get_bluetooth_port_availability(node, port);
388 route |= update_node_availability(u, node, available);
389 direction |= (node->direction == mir_input) ? PA_DIRECTION_INPUT : PA_DIRECTION_OUTPUT;
393 set_bluetooth_profile(u, port->card, direction);
395 switch (port->available) {
396 case PA_AVAILABLE_NO: available = false; break;
397 case PA_AVAILABLE_YES: available = true; break;
398 default: /* do nothing */ return;
401 if (port->direction == PA_DIRECTION_OUTPUT) {
402 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
404 if (port == pa_hashmap_get(sink->ports, port->name)) {
405 pa_log_debug(" sink '%s'", sink->name);
406 route |= update_node_availability_by_device(
415 if (port->direction == PA_DIRECTION_INPUT) {
416 PA_IDXSET_FOREACH(source, core->sources, idx) {
418 if (port == pa_hashmap_get(source->ports, port->name)) {
419 pa_log_debug(" source '%s'", source->name);
420 route |= update_node_availability_by_device(
431 mir_router_make_routing(u);
434 void pa_discover_add_sink(struct userdata *u, pa_sink *sink, bool route)
436 static pa_nodeset_resdef def_resdef = {0, {0, 0}};
439 pa_discover *discover;
446 const char *loopback_role;
448 pa_nodeset_resdef *resdef;
457 pa_assert_se((core = u->core));
458 pa_assert_se((discover = u->discover));
460 module = sink->module;
462 if ((card = sink->card)) {
463 if (!(key = node_key(u, mir_output,sink,ACTIVE_PORT, kbf,sizeof(kbf))))
465 if (!(node = pa_discover_find_node_by_key(u, key))) {
466 if (u->state.profile)
467 pa_log_debug("can't find node for sink (key '%s')", key);
469 u->state.sink = sink->index;
472 pa_log_debug("node for '%s' found (key %s). Updating with sink data",
473 node->paname, node->key);
474 node->paidx = sink->index;
475 node->available = true;
476 pa_discover_add_node_to_ptr_hash(u, sink, node);
478 if ((loopback_role = pa_classify_loopback_stream(node))) {
479 if (!(ns = pa_utils_get_null_source(u))) {
480 pa_log("Can't load loopback module: no initial null source");
484 map = pa_nodeset_get_map_by_role(u, loopback_role);
485 make_rset = (map && map->resdef);
486 resdef = make_rset ? map->resdef : &def_resdef;
488 node->loop = pa_loopback_create(u->loopback, core,
489 PA_LOOPBACK_SINK, node->index,
490 ns->index, sink->index,
494 resdef->flags.audio);
496 mir_node_print(node, nbf, sizeof(nbf));
497 pa_log_debug("updated node:\n%s", nbf);
500 pa_murphyif_create_resource_set(u, node, resdef);
502 node->rset.grant = 1;
508 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
509 mir_router_make_routing(u);
511 if (!u->state.profile)
512 schedule_deferred_routing(u);
516 else if (!module || !pa_streq(module->name, "module-combine-sink")) {
519 memset(&data, 0, sizeof(data));
520 data.key = pa_xstrdup(sink->name);
521 data.direction = mir_output;
522 data.implement = mir_device;
523 data.channels = sink->channel_map.channels;
524 data.available = true;
525 data.paidx = sink->index;
527 if (sink == pa_utils_get_null_sink(u)) {
528 data.visible = false;
529 data.type = mir_null;
530 data.amname = "Silent";
531 data.amid = AM_ID_INVALID;
532 data.paname = pa_xstrdup(sink->name);
534 else if (pa_classify_node_by_property(&data, sink->proplist)) {
535 if (data.type == mir_gateway_sink) {
536 data.privacy = mir_private;
537 data.visible = false;
538 data.amname = sink->name;
539 data.amid = AM_ID_INVALID;
540 data.paname = pa_xstrdup(sink->name);
543 data.privacy = mir_public;
545 data.amname = mir_node_type_str(data.type);
546 data.amid = AM_ID_INVALID;
547 data.paname = pa_xstrdup(sink->name);
553 pa_xfree(data.key); /* for now */
554 pa_log_info("currently we do not support statically loaded "
555 "sinks without " PA_PROP_NODE_TYPE " property");
559 node = create_node(u, &data, NULL);
562 pa_discover_add_node_to_ptr_hash(u, sink, node);
567 void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
569 pa_discover *discover;
576 pa_assert_se((discover = u->discover));
578 name = pa_utils_get_sink_name(sink);
580 if (!(node = pa_hashmap_get(discover->nodes.byptr, sink)))
581 pa_log_debug("can't find node for sink (name '%s')", name);
583 pa_log_debug("node found for '%s'. Reseting sink data", name);
584 pa_murphyif_destroy_resource_set(u, node);
585 schedule_source_cleanup(u, node);
586 node->paidx = PA_IDXSET_INVALID;
587 pa_hashmap_remove(discover->nodes.byptr, sink);
592 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
593 node->available = false;
595 if (!u->state.profile)
596 schedule_deferred_routing(u);
600 pa_log_info("currently we do not support statically loaded sinks");
606 void pa_discover_add_source(struct userdata *u, pa_source *source)
608 static pa_nodeset_resdef def_resdef = {0, {0, 0}};
611 pa_discover *discover;
617 const char *loopback_role;
619 pa_nodeset_resdef *resdef;
627 pa_assert_se((core = u->core));
628 pa_assert_se((discover = u->discover));
630 if ((card = source->card)) {
631 if (!(key = node_key(u,mir_input,source,ACTIVE_PORT,kbf,sizeof(kbf))))
633 if (!(node = pa_discover_find_node_by_key(u, key))) {
634 if (u->state.profile)
635 pa_log_debug("can't find node for source (key '%s')", key);
637 u->state.source = source->index;
640 pa_log_debug("node for '%s' found. Updating with source data",
642 node->paidx = source->index;
643 node->available = true;
644 pa_discover_add_node_to_ptr_hash(u, source, node);
645 if ((loopback_role = pa_classify_loopback_stream(node))) {
646 if (!(ns = pa_utils_get_null_sink(u))) {
647 pa_log("Can't load loopback module: no initial null sink");
651 map = pa_nodeset_get_map_by_role(u, loopback_role);
652 make_rset = (map && map->resdef);
653 resdef = make_rset ? map->resdef : &def_resdef;
655 node->loop = pa_loopback_create(u->loopback, core,
656 PA_LOOPBACK_SOURCE, node->index,
657 source->index, ns->index,
661 resdef->flags.audio);
663 sink_index = pa_loopback_get_sink_index(core, node->loop);
664 node->mux = pa_multiplex_find_by_sink(u->multiplex,sink_index);
667 mir_node_print(node, nbf, sizeof(nbf));
668 pa_log_debug("updated node:\n%s", nbf);
671 pa_murphyif_create_resource_set(u, node, resdef);
673 node->rset.grant = 1;
675 pa_fader_apply_volume_limits(u, node->stamp);
679 memset(&data, 0, sizeof(data));
680 data.key = pa_xstrdup(source->name);
681 data.direction = mir_input;
682 data.implement = mir_device;
683 data.channels = source->channel_map.channels;
684 data.available = true;
686 if (source == pa_utils_get_null_source(u)) {
687 data.visible = false;
688 data.type = mir_null;
689 data.amname = "Silent";
690 data.amid = AM_ID_INVALID;
691 data.paname = pa_xstrdup(source->name);
692 data.paidx = source->index;
694 else if (pa_classify_node_by_property(&data, source->proplist)) {
695 if (data.type == mir_gateway_source) {
696 data.privacy = mir_private;
697 data.visible = false;
698 data.amname = source->name;
699 data.amid = AM_ID_INVALID;
700 data.paname = source->name;
703 data.privacy = mir_public;
705 data.amname = mir_node_type_str(data.type);
706 data.amid = AM_ID_INVALID;
707 data.paname = source->name;
711 pa_xfree(data.key); /* for now */
712 pa_log_info("currently we do not support statically loaded "
713 "sources without " PA_PROP_NODE_TYPE " property");
717 create_node(u, &data, NULL);
721 void pa_discover_remove_source(struct userdata *u, pa_source *source)
723 pa_discover *discover;
730 pa_assert_se((discover = u->discover));
732 name = pa_utils_get_source_name(source);
734 if (!(node = pa_hashmap_get(discover->nodes.byptr, source)))
735 pa_log_debug("can't find node for source (name '%s')", name);
737 pa_log_debug("node found. Reseting source data");
738 pa_murphyif_destroy_resource_set(u, node);
739 schedule_source_cleanup(u, node);
740 node->paidx = PA_IDXSET_INVALID;
741 pa_hashmap_remove(discover->nodes.byptr, source);
746 if (type != mir_bluetooth_sco)
747 node->available = false;
749 if (!u->state.profile)
750 schedule_deferred_routing(u);
754 pa_log_info("currently we do not support statically "
761 void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
764 pa_discover *discover;
775 pa_nodeset_resdef *resdef;
780 pa_assert_se((core = u->core));
781 pa_assert_se((discover = u->discover));
782 pa_assert_se((pl = sinp->proplist));
784 if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME))) {
785 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
786 pa_log_debug("Seems to be a combine stream. Nothing to do ...");
789 if (!strncmp(media, loopback_outpatrn, sizeof(loopback_outpatrn)-1)) {
790 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
795 name = pa_utils_get_sink_input_name(sinp);
797 pa_log_debug("registering input stream '%s'", name);
799 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
800 pa_log_debug("cant find stream class for '%s'. "
801 "Leaving it alone", name);
805 pa_utils_set_stream_routing_properties(pl, type, NULL);
807 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
809 memset(&data, 0, sizeof(data));
811 data.direction = mir_input;
812 data.implement = mir_stream;
813 data.channels = sinp->channel_map.channels;
815 data.zone = pa_utils_get_zone(sinp->proplist);
817 data.available = true;
818 data.amname = get_stream_amname(type, name, pl);
819 data.amdescr = pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
820 data.amid = AM_ID_INVALID;
821 data.paname = (char *)name;
822 data.paidx = sinp->index;
823 data.rset.id = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
826 * here we can't guess whether the application requested an explicit
827 * route by sepcifying the target sink @ stream creation time.
829 * the brute force solution: we make a default route for this stream
830 * possibly overwiriting the orginal app request :(
832 /* this will set data.mux */
833 role = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_ROLE);
834 sink = make_output_prerouting(u, &data, &sinp->channel_map, role, &target);
836 node = create_node(u, &data, NULL);
838 pa_discover_add_node_to_ptr_hash(u, sinp, node);
840 if (sink && target) {
841 pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
843 if (pa_sink_input_move_to(sinp, sink, false) < 0)
844 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
846 pa_audiomgr_add_default_route(u, node, target);
850 bool pa_discover_preroute_sink_input(struct userdata *u,
851 pa_sink_input_new_data *data)
856 pa_discover *discover;
857 pa_multiplex *multiplex;
866 pa_nodeset_resdef *resdef;
872 pa_assert_se((core = u->core));
873 pa_assert_se((discover = u->discover));
874 pa_assert_se((multiplex = u->multiplex));
875 pa_assert_se((pl = data->proplist));
877 mnam = (m = data->module) ? m->name : "";
879 if (pa_streq(mnam, "module-combine-sink")) {
881 type = mir_node_type_unknown;
883 if (!(mux = pa_multiplex_find_by_module(multiplex, m)) ||
884 !(sink = pa_idxset_get_by_index(core->sinks, mux->sink_index)) ||
885 !(sinp = pa_idxset_first(sink->inputs, NULL)) ||
886 !(type = pa_utils_get_stream_class(sinp->proplist)))
888 pa_log_debug("can't figure out the type of multiplex stream");
891 pa_utils_set_stream_routing_properties(data->proplist, type, NULL);
895 loopback = pa_streq(mnam, "module-loopback");
899 if (!(node = pa_utils_get_node_from_data(u, mir_input, data))) {
900 pa_log_debug("can't find loopback node for sink-input");
904 if (node->direction == mir_output) {
905 pa_log_debug("refuse to preroute loopback sink-input "
906 "(current route: sink %u @ %p)", data->sink ?
907 data->sink->index : PA_IDXSET_INVALID,(void *)data->sink);
913 type = pa_classify_guess_stream_node_type(u, pl, NULL);
916 remap = pa_streq(mnam, "module-remap-sink");
917 type = pa_classify_guess_stream_node_type(u, pl, &resdef);
919 pa_utils_set_resource_properties(pl, resdef);
921 if (pa_stream_state_start_corked(u, data, resdef)) {
922 pa_log_debug("start corked");
926 pa_utils_set_stream_routing_properties(pl, type, data->sink);
929 memset(&fake, 0, sizeof(fake));
930 fake.direction = mir_input;
931 fake.implement = mir_stream;
935 fake.channels = data->channel_map.channels;
936 fake.zone = pa_utils_get_zone(data->proplist);
938 fake.available = true;
939 fake.amname = "<preroute sink-input>";
940 fake.amid = AM_ID_INVALID;
941 fake.paidx = PA_IDXSET_INVALID;
943 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
944 sink = make_output_prerouting(u, &fake, &data->channel_map, role,NULL);
948 if (fake.mux && !(data->flags & PA_SINK_INPUT_START_CORKED)) {
949 data->flags |= PA_SINK_INPUT_START_CORKED;
950 schedule_stream_uncorking(u, sink);
954 if (pa_sink_input_new_data_set_sink(data, sink, false))
955 pa_log_debug("set sink %u for new sink-input", sink->index);
957 pa_log("can't set sink %u for new sink-input", sink->index);
958 /* copes wit NULL mux */
959 pa_multiplex_destroy(u->multiplex, core, fake.mux);
969 if (loopback && data->sink && data->sink->module) {
971 if (pa_streq(data->sink->module->name, "module-combine-sink"))
975 if (pa_classify_ramping_stream(&fake)) {
976 pa_log_debug("set sink-input ramp-muted");
977 data->flags |= PA_SINK_INPUT_START_RAMP_MUTED;
984 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
988 pa_sink_input *csinp;
990 pa_discover *discover;
991 pa_multiplex *multiplex;
1001 pa_nodeset_resdef *resdef;
1002 pa_nodeset_resdef rdbuf;
1007 pa_assert_se((core = u->core));
1008 pa_assert_se((discover = u->discover));
1009 pa_assert_se((multiplex = u->multiplex));
1010 pa_assert_se((pl = sinp->proplist));
1014 if (!(media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)))
1015 media = "<unknown>";
1017 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
1018 if (!pa_utils_stream_has_default_route(sinp->proplist) ||
1019 !(mux = pa_multiplex_find_by_module(multiplex, sinp->module)) ||
1020 mux->defstream_index != PA_IDXSET_INVALID)
1022 pa_log_debug("New stream is a combine stream. Nothing to do ...");
1025 pa_log_debug("New stream is a combine stream. Setting as default");
1026 mux->defstream_index = sinp->index;
1027 mir_router_make_routing(u);
1030 } else if (!strncmp(media, loopback_outpatrn,sizeof(loopback_outpatrn)-1)){
1031 pa_log_debug("New stream is a loopback output stream");
1033 if ((node = pa_utils_get_node_from_stream(u, mir_input, sinp))) {
1034 if (node->direction == mir_input)
1035 pa_log_debug("loopback stream node '%s' found", node->amname);
1037 pa_log_debug("ignoring it");
1042 pa_log_debug("can't find node for the loopback stream");
1049 name = pa_utils_get_sink_input_name(sinp);
1051 pa_log_debug("dealing with new input stream '%s'", name);
1053 if ((type = get_stream_routing_class(pl)))
1054 resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1056 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1057 pa_log_debug("cant find stream class for '%s'. "
1058 "Leaving it alone", name);
1062 pa_utils_set_stream_routing_properties(pl, type, NULL);
1064 /* if needed, make some post-routing here */
1067 /* we need to add this to main hashmap as that is used for loop
1068 through on all nodes. */
1069 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
1071 memset(&data, 0, sizeof(data));
1073 data.direction = mir_input;
1074 data.implement = mir_stream;
1075 data.channels = sinp->channel_map.channels;
1077 data.zone = pa_utils_get_zone(pl);
1078 data.visible = true;
1079 data.available = true;
1080 data.amname = get_stream_amname(type, name, pl);
1081 data.amdescr = pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1082 data.amid = AM_ID_INVALID;
1083 data.paname = (char *)name;
1084 data.paidx = sinp->index;
1085 data.mux = pa_multiplex_find_by_sink(u->multiplex,
1087 data.rset.id = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
1088 node = create_node(u, &data, &created);
1093 pa_log("%s: confused with stream. '%s' did exists",
1094 __FILE__, node->amname);
1099 pa_murphyif_add_node(u, node);
1102 pa_murphyif_create_resource_set(u, node, resdef);
1104 node->rset.grant = 1;
1107 pa_discover_add_node_to_ptr_hash(u, sinp, node);
1112 csinp = pa_idxset_get_by_index(core->sink_inputs,
1113 data.mux->defstream_index);
1114 s = csinp ? csinp->sink : NULL;
1116 if ((sinp->flags & PA_SINK_INPUT_START_RAMP_MUTED)) {
1117 pa_log_debug("ramp '%s' to 100%%", media);
1118 pa_fader_ramp_volume(u, sinp, PA_VOLUME_NORM);
1124 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1126 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1127 pa_log_debug("can't figure out where this stream is routed");
1129 pa_log_debug("register route '%s' => '%s'",
1130 node->amname, snod->amname);
1132 if (pa_utils_stream_has_default_route(sinp->proplist))
1133 pa_audiomgr_add_default_route(u, node, snod);
1135 /* FIXME: register explicit routes */
1136 /* else pa_audiomgr_add/register_explicit_route() */
1139 pa_fader_apply_volume_limits(u, pa_utils_get_stamp());
1144 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
1146 pa_discover *discover;
1150 bool had_properties = false;
1154 pa_assert_se((discover = u->discover));
1156 name = pa_utils_get_sink_input_name(sinp);
1158 pa_log_debug("sink-input '%s' going to be destroyed", name);
1161 had_properties = pa_utils_unset_stream_routing_properties(sinp->proplist);
1163 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sinp))) {
1164 if (!pa_multiplex_sink_input_remove(u->multiplex, sinp))
1165 pa_log_debug("nothing to do for sink-input (name '%s')", name);
1168 pa_log_debug("node found for '%s'. After clearing routes "
1169 "it will be destroyed", name);
1171 if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
1172 pa_log_debug("can't figure out where this stream is routed");
1174 pa_log_debug("clear route '%s' => '%s'",
1175 node->amname, sinknod->amname);
1177 /* FIXME: and actually do it ... */
1181 destroy_node(u, node);
1184 if (node || had_properties)
1185 mir_router_make_routing(u);
1189 void pa_discover_register_source_output(struct userdata *u,
1190 pa_source_output *sout)
1193 pa_discover *discover;
1204 pa_nodeset_resdef *resdef;
1209 pa_assert_se((core = u->core));
1210 pa_assert_se((discover = u->discover));
1211 pa_assert_se((pl = sout->proplist));
1213 if ((media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME))) {
1214 if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1215 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
1220 name = pa_utils_get_source_output_name(sout);
1222 pa_log_debug("registering output stream '%s'", name);
1224 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1225 pa_log_debug("cant find stream class for '%s'. "
1226 "Leaving it alone", name);
1230 pa_utils_set_stream_routing_properties(pl, type, NULL);
1232 snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1234 memset(&data, 0, sizeof(data));
1236 data.direction = mir_output;
1237 data.implement = mir_stream;
1238 data.channels = sout->channel_map.channels;
1240 data.zone = pa_utils_get_zone(sout->proplist);
1241 data.visible = true;
1242 data.available = true;
1244 data.amdescr = pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1245 data.amid = AM_ID_INVALID;
1247 data.paidx = sout->index;
1248 data.rset.id = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
1251 * here we can't guess whether the application requested an explicit
1252 * route by sepcifying the target source @ stream creation time.
1254 * the brute force solution: we make a default route for this stream
1255 * possibly overwiriting the orginal app request :(
1257 role = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_ROLE);
1258 source = make_input_prerouting(u, &data, role, &target);
1260 node = create_node(u, &data, NULL);
1262 pa_discover_add_node_to_ptr_hash(u, sout, node);
1264 if (source && target) {
1265 pa_log_debug("move stream to source %u (%s)",
1266 source->index, source->name);
1268 if (pa_source_output_move_to(sout, source, false) < 0)
1269 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
1271 pa_log_debug("register route '%s' => '%s'",
1272 node->amname, target->amname);
1273 /* FIXME: and actually do it ... */
1278 bool pa_discover_preroute_source_output(struct userdata *u,
1279 pa_source_output_new_data *data)
1284 pa_discover *discover;
1291 pa_nodeset_resdef *resdef;
1295 pa_assert_se((core = u->core));
1296 pa_assert_se((discover = u->discover));
1297 pa_assert_se((pl = data->proplist));
1299 mnam = (m = data->module) ? m->name : "";
1301 if (pa_streq(mnam, "module-loopback")) {
1302 if (!(node = pa_utils_get_node_from_data(u, mir_output, data))) {
1303 pa_log_debug("can't find loopback node for source-output");
1307 if (node->direction == mir_input) {
1308 pa_log_debug("refuse to preroute loopback source-output "
1309 "(current route: source %u @ %p)", data->source ?
1310 data->source->index : PA_IDXSET_INVALID,(void *)data->source);
1314 data->source = NULL;
1316 type = pa_classify_guess_stream_node_type(u, pl, NULL);
1319 type = pa_classify_guess_stream_node_type(u, pl, &resdef);
1321 pa_utils_set_resource_properties(pl, resdef);
1324 pa_utils_set_stream_routing_properties(pl, type, data->source);
1326 if (!data->source) {
1327 memset(&fake, 0, sizeof(fake));
1328 fake.direction = mir_output;
1329 fake.implement = mir_stream;
1330 fake.channels = data->channel_map.channels;
1332 fake.zone = pa_utils_get_zone(data->proplist);
1333 fake.visible = true;
1334 fake.available = true;
1335 fake.amname = "<preroute source-output>";
1337 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
1338 source = make_input_prerouting(u, &fake, role, NULL);
1341 if (pa_source_output_new_data_set_source(data, source, false)) {
1342 pa_log_debug("set source %u for new source-output",
1346 pa_log("can't set source %u for new source-output",
1356 void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
1361 pa_discover *discover;
1370 pa_nodeset_resdef *resdef;
1371 pa_nodeset_resdef rdbuf;
1376 pa_assert_se((core = u->core));
1377 pa_assert_se((discover = u->discover));
1378 pa_assert_se((pl = sout->proplist));
1382 if (!(media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME)))
1383 media = "<unknown>";
1385 if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1386 pa_log_debug("New stream is a loopback input stream");
1388 if ((node = pa_utils_get_node_from_stream(u, mir_output, sout))) {
1389 if (node->direction == mir_output)
1390 pa_log_debug("loopback stream node '%s' found", node->amname);
1392 pa_log_debug("ignoring it");
1397 pa_log_debug("can't find node for the loopback stream");
1402 name = pa_utils_get_source_output_name(sout);
1404 pa_log_debug("dealing with new output stream '%s'", name);
1406 if ((type = get_stream_routing_class(pl)))
1407 resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1409 if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1410 pa_log_debug("cant find stream class for '%s'. "
1411 "Leaving it alone", name);
1415 pa_utils_set_stream_routing_properties(pl, type, NULL);
1417 /* if needed, make some post-routing here */
1420 /* we need to add this to main hashmap as that is used for loop
1421 through on all nodes. */
1422 snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1424 memset(&data, 0, sizeof(data));
1426 data.direction = mir_output;
1427 data.implement = mir_stream;
1428 data.channels = sout->channel_map.channels;
1430 data.zone = pa_utils_get_zone(pl);
1431 data.visible = true;
1432 data.available = true;
1434 data.amdescr = pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1435 data.amid = AM_ID_INVALID;
1437 data.paidx = sout->index;
1438 data.rset.id = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
1440 node = create_node(u, &data, &created);
1445 pa_log("%s: confused with stream. '%s' did exists",
1446 __FILE__, node->amname);
1451 pa_murphyif_add_node(u, node);
1454 pa_murphyif_create_resource_set(u, node, resdef);
1456 node->rset.grant = 1;
1459 pa_discover_add_node_to_ptr_hash(u, sout, node);
1462 if ((s = sout->source))
1463 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1465 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1466 pa_log_debug("can't figure out where this stream is routed");
1468 pa_log_debug("register route '%s' => '%s'",
1469 snod->amname, node->amname);
1470 pa_audiomgr_add_default_route(u, node, snod);
1475 void pa_discover_remove_source_output(struct userdata *u,
1476 pa_source_output *sout)
1478 pa_discover *discover;
1485 pa_assert_se((discover = u->discover));
1487 name = pa_utils_get_source_output_name(sout);
1489 pa_log_debug("source-output '%s' going to be destroyed", name);
1491 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sout)))
1492 pa_log_debug("can't find node for source-output (name '%s')", name);
1494 pa_log_debug("node found for '%s'. After clearing routes "
1495 "it will be destroyed", name);
1497 if (!(srcnod = pa_hashmap_get(discover->nodes.byptr, sout->source)))
1498 pa_log_debug("can't figure out where this stream is routed");
1500 pa_log_debug("clear route '%s' => '%s'",
1501 node->amname, srcnod->amname);
1503 /* FIXME: and actually do it ... */
1507 destroy_node(u, node);
1509 mir_router_make_routing(u);
1514 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
1516 pa_discover *discover;
1520 pa_assert_se((discover = u->discover));
1523 node = pa_hashmap_get(discover->nodes.byname, key);
1530 mir_node *pa_discover_find_node_by_ptr(struct userdata *u, void *ptr)
1532 pa_discover *discover;
1536 pa_assert_se((discover = u->discover));
1539 node = pa_hashmap_get(discover->nodes.byptr, ptr);
1546 void pa_discover_add_node_to_ptr_hash(struct userdata *u,
1550 pa_discover *discover;
1555 pa_assert_se((discover = u->discover));
1557 pa_hashmap_put(discover->nodes.byptr, ptr, node);
1560 mir_node *pa_discover_remove_node_from_ptr_hash(struct userdata *u, void *ptr)
1562 pa_discover *discover;
1566 pa_assert_se((discover = u->discover));
1568 return pa_hashmap_remove(discover->nodes.byptr, ptr);
1572 static void handle_alsa_card(struct userdata *u, pa_card *card)
1579 memset(&data, 0, sizeof(data));
1580 data.zone = pa_utils_get_zone(card->proplist);
1581 data.visible = true;
1582 data.amid = AM_ID_INVALID;
1583 data.implement = mir_device;
1584 data.paidx = PA_IDXSET_INVALID;
1585 data.stamp = pa_utils_get_stamp();
1587 cnam = pa_utils_get_card_name(card);
1588 udd = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
1590 if (udd && pa_streq(udd, "1")) {
1591 /* udev loaded alsa card */
1592 if (!strncmp(cnam, "alsa_card.", 10)) {
1594 handle_udev_loaded_card(u, card, &data, cid);
1599 /* statically loaded pci or usb card */
1602 pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
1607 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1609 pa_discover *discover;
1610 pa_card_profile *prof;
1618 char paname[MAX_NAME_LENGTH+1];
1619 char amname[MAX_NAME_LENGTH+1];
1620 char key[MAX_NAME_LENGTH+1];
1622 pa_assert_se((discover = u->discover));
1624 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1627 memset(paname, 0, sizeof(paname));
1628 memset(amname, 0, sizeof(amname));
1629 memset(key , 0, sizeof(key) );
1631 memset(&data, 0, sizeof(data));
1633 data.visible = true;
1634 data.amid = AM_ID_INVALID;
1635 data.implement = mir_device;
1636 data.paidx = PA_IDXSET_INVALID;
1637 data.paname = paname;
1638 data.amname = amname;
1639 data.amdescr = (char *)cdescr;
1640 data.pacard.index = card->index;
1641 data.stamp = pa_utils_get_stamp();
1643 cnam = pa_utils_get_card_name(card);
1645 if (!strncmp(cnam, "bluez_card.", 11)) {
1648 pa_assert(card->ports);
1650 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1652 PA_HASHMAP_FOREACH(prof, card->profiles, cstate) {
1653 data.available = false;
1654 data.pacard.profile = prof->name;
1656 if (prof->n_sinks > 0) {
1657 data.direction = mir_output;
1658 data.channels = prof->max_sink_channels;
1659 data.amname = amname;
1661 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1662 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1663 pa_classify_node_by_card(&data, card, prof, NULL);
1664 node = create_node(u, &data, NULL);
1665 mir_constrain_add_node(u, cd, node);
1668 if (prof->n_sources > 0) {
1669 data.direction = mir_input;
1670 data.channels = prof->max_source_channels;
1671 data.amname = amname;
1673 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1674 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1675 pa_classify_node_by_card(&data, card, prof, NULL);
1676 node = create_node(u, &data, NULL);
1677 mir_constrain_add_node(u, cd, node);
1681 if (!(prof = card->active_profile))
1682 pa_log("card '%s' has no active profile", card->name);
1684 pa_log_debug("card '%s' default profile '%s'",
1685 card->name, prof->name);
1688 schedule_card_check(u, card);
1694 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1696 pa_discover *discover;
1697 pa_card_profile *prof;
1698 pa_device_port *port;
1705 void *state0, *state1;
1706 char paname[MAX_NAME_LENGTH+1];
1707 char amname[MAX_NAME_LENGTH+1];
1708 char key[MAX_NAME_LENGTH+1];
1713 pa_assert_se((discover = u->discover));
1715 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1718 memset(paname, 0, sizeof(paname));
1719 memset(amname, 0, sizeof(amname));
1720 memset(key , 0, sizeof(key) );
1722 memset(&data, 0, sizeof(data));
1724 data.zone = pa_utils_get_zone(card->proplist);
1725 data.visible = true;
1726 data.amid = AM_ID_INVALID;
1727 data.implement = mir_device;
1728 data.paidx = PA_IDXSET_INVALID;
1729 data.paname = paname;
1730 data.amname = amname;
1731 data.amdescr = (char *)cdescr;
1732 data.pacard.index = card->index;
1733 data.stamp = pa_utils_get_stamp();
1735 cnam = pa_utils_get_card_name(card);
1737 if (!strncmp(cnam, "bluez_card.", 11)) {
1740 pa_assert(card->ports);
1742 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1744 PA_HASHMAP_FOREACH(port, card->ports, state0) {
1745 pa_assert(port->profiles);
1748 input = output = true;
1749 len = strlen(port->name);
1750 if (len >= 6 && !strcmp("-input", port->name + (len-6)))
1752 else if (len >= 7 && !strcmp("-output", port->name + (len-7)))
1756 PA_HASHMAP_FOREACH(prof, port->profiles, state1) {
1757 data.pacard.profile = prof->name;
1758 data.available = get_bluetooth_port_availability(&data, port);
1760 if (output && prof->n_sinks > 0) {
1761 data.direction = mir_output;
1762 data.channels = prof->max_sink_channels;
1763 data.amname = amname;
1765 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1766 snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1767 pa_classify_node_by_card(&data, card, prof, NULL);
1768 node = create_node(u, &data, NULL);
1769 mir_constrain_add_node(u, cd, node);
1770 pa_utils_set_port_properties(port, node);
1773 if (input && prof->n_sources > 0) {
1774 data.direction = mir_input;
1775 data.channels = prof->max_source_channels;
1776 data.amname = amname;
1778 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1779 snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1780 pa_classify_node_by_card(&data, card, prof, NULL);
1781 node = create_node(u, &data, NULL);
1782 mir_constrain_add_node(u, cd, node);
1783 pa_utils_set_port_properties(port, node);
1788 if (!(prof = card->active_profile))
1789 pa_log("card '%s' has no active profile", card->name);
1791 pa_log_debug("card '%s' default profile '%s'",
1792 card->name, prof->name);
1795 schedule_card_check(u, card);
1799 static bool get_bluetooth_port_availability(mir_node *node,
1800 pa_device_port *port)
1802 bool available = false;
1808 if ((prof = node->pacard.profile)) {
1809 if (!strcmp(prof, "hfgw") ||
1810 !strcmp(prof, "a2dp_source") ||
1811 !strcmp(prof, "a2dp_sink"))
1812 available = (port->available != PA_AVAILABLE_NO);
1820 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
1821 mir_node *data, const char *cardid)
1823 pa_discover *discover;
1824 pa_card_profile *prof;
1825 pa_card_profile *active;
1827 const char *alsanam;
1829 char *sinks[MAX_CARD_TARGET+1];
1830 char *sources[MAX_CARD_TARGET+1];
1831 char buf[MAX_NAME_LENGTH+1];
1832 char paname[MAX_NAME_LENGTH+1];
1833 char amname[MAX_NAME_LENGTH+1];
1837 pa_assert(card->profiles);
1838 pa_assert_se((discover = u->discover));
1840 alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
1842 memset(amname, 0, sizeof(amname));
1844 data->paname = paname;
1845 data->amname = amname;
1846 data->amdescr = (char *)alsanam;
1848 data->pacard.index = card->index;
1850 active = card->active_profile;
1852 PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1853 /* filtering: deal with selected profiles if requested so */
1854 if (discover->selected && (!active || (active && prof != active)))
1857 /* filtering: skip the 'off' profiles */
1858 if (!prof->n_sinks && !prof->n_sources)
1861 /* filtering: consider sinks with suitable amount channels */
1862 if (prof->n_sinks &&
1863 (prof->max_sink_channels < discover->chmin ||
1864 prof->max_sink_channels > discover->chmax ))
1867 /* filtering: consider sources with suitable amount channels */
1868 if (prof->n_sources &&
1869 (prof->max_source_channels < discover->chmin ||
1870 prof->max_source_channels > discover->chmax ))
1873 data->pacard.profile = prof->name;
1875 parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
1877 data->direction = mir_output;
1878 data->channels = prof->max_sink_channels;
1879 for (i = 0; (sid = sinks[i]); i++) {
1880 snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
1881 handle_card_ports(u, data, card, prof);
1884 data->direction = mir_input;
1885 data->channels = prof->max_source_channels;
1886 for (i = 0; (sid = sources[i]); i++) {
1887 snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
1888 handle_card_ports(u, data, card, prof);
1894 static void handle_card_ports(struct userdata *u, mir_node *data,
1895 pa_card *card, pa_card_profile *prof)
1897 mir_node *node = NULL;
1898 bool have_ports = false;
1899 mir_constr_def *cd = NULL;
1900 const char *amname = data->amname;
1901 pa_device_port *port;
1904 char key[MAX_NAME_LENGTH+1];
1912 PA_HASHMAP_FOREACH(port, card->ports, state) {
1914 * if this port did not belong to any profile
1915 * (ie. prof->profiles == NULL) we assume that this port
1916 * does works with all the profiles
1918 if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
1919 ((port->direction == PA_DIRECTION_INPUT && data->direction == mir_input)||
1920 (port->direction == PA_DIRECTION_OUTPUT && data->direction == mir_output)))
1925 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
1928 data->available = (port->available != PA_AVAILABLE_NO);
1930 data->amname = amname;
1931 data->paport = port->name;
1933 pa_classify_node_by_card(data, card, prof, port);
1935 node = create_node(u, data, &created);
1938 node->stamp = data->stamp;
1940 cd = mir_constrain_create(u, "port", mir_constrain_port,
1942 mir_constrain_add_node(u, cd, node);
1949 data->key = pa_xstrdup(data->paname);
1950 data->available = true;
1952 pa_classify_node_by_card(data, card, prof, NULL);
1954 node = create_node(u, data, &created);
1957 node->stamp = data->stamp;
1961 data->amname = amname;
1965 static mir_node *create_node(struct userdata *u, mir_node *data,
1968 pa_discover *discover;
1975 pa_assert(data->key);
1976 pa_assert(data->paname);
1977 pa_assert_se((discover = u->discover));
1979 if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
1984 node = mir_node_create(u, data);
1985 pa_hashmap_put(discover->nodes.byname, node->key, node);
1987 mir_node_print(node, buf, sizeof(buf));
1988 pa_log_debug("new node:\n%s", buf);
1990 if (node->available)
1991 pa_audiomgr_register_node(u, node);
1995 *created_ret = created;
2000 static void destroy_node(struct userdata *u, mir_node *node)
2002 pa_discover *discover;
2006 pa_assert_se((discover = u->discover));
2009 removed = pa_hashmap_remove(discover->nodes.byname, node->key);
2011 if (node != removed) {
2013 pa_log("%s: confused with data structures: key mismatch. "
2014 " attempted to destroy '%s'; actually destroyed '%s'",
2015 __FILE__, node->key, removed->key);
2017 pa_log("%s: confused with data structures: node '%s' "
2018 "is not in the hash table", __FILE__, node->key);
2022 pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
2024 if (node->implement == mir_stream) {
2025 if (node->direction == mir_input) {
2027 pa_log_debug("removing multiplexer");
2032 pa_audiomgr_unregister_node(u, node);
2034 extapi_signal_node_change(u);
2036 mir_constrain_remove_node(u, node);
2038 pa_loopback_destroy(u->loopback, u->core, node->loop);
2039 pa_multiplex_destroy(u->multiplex, u->core, node->mux);
2041 mir_node_destroy(u, node);
2045 static bool update_node_availability(struct userdata *u,
2052 if ((!available && node->available) ||
2053 ( available && !node->available) )
2055 node->available = available;
2058 pa_audiomgr_register_node(u, node);
2060 pa_audiomgr_unregister_node(u, node);
2062 extapi_signal_node_change(u);
2064 return true; /* routing needed */
2070 static bool update_node_availability_by_device(struct userdata *u,
2071 mir_direction direction,
2073 pa_device_port *port,
2083 pa_assert(direction == mir_input || direction == mir_output);
2085 if ((key = node_key(u, direction, data, port, buf, sizeof(buf)))) {
2086 if (!(node = pa_discover_find_node_by_key(u, key)))
2087 pa_log_debug(" can't find node (key '%s')", key);
2089 pa_log_debug(" node for '%s' found (key %s)",
2090 node->paname, node->key);
2092 return update_node_availability(u, node, available);
2096 return false; /* no routing needed */
2099 static char *get_name(char **string_ptr, int offs)
2101 char c, *name, *end;
2103 name = *string_ptr + offs;
2105 for (end = name; (c = *end); end++) {
2117 static void parse_profile_name(pa_card_profile *prof,
2127 pa_assert(prof->name);
2129 strncpy(buf, prof->name, (size_t)buflen);
2130 buf[buflen-1] = '\0';
2132 memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2133 memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2136 if (!strncmp(p, "output:", 7)) {
2137 if (i >= MAX_CARD_TARGET) {
2138 pa_log_debug("number of outputs exeeds the maximum %d in "
2139 "profile name '%s'", MAX_CARD_TARGET, prof->name);
2142 sinks[i++] = get_name(&p, 7);
2144 else if (!strncmp(p, "input:", 6)) {
2145 if (j >= MAX_CARD_TARGET) {
2146 pa_log_debug("number of inputs exeeds the maximum %d in "
2147 "profile name '%s'", MAX_CARD_TARGET, prof->name);
2150 sources[j++] = get_name(&p, 6);
2153 pa_log("%s: failed to parse profile name '%s'",
2154 __FILE__, prof->name);
2161 static const char *node_key(struct userdata *u, mir_direction direction,
2162 void *data, pa_device_port *port, char *buf, size_t len)
2165 pa_card_profile *profile;
2173 const char *profile_name;
2179 pa_assert(direction == mir_input || direction == mir_output);
2181 if (direction == mir_output) {
2182 pa_sink *sink = data;
2183 type = pa_xstrdup("sink");
2184 name = pa_utils_get_sink_name(sink);
2187 port = sink->active_port;
2190 pa_source *source = data;
2191 type = pa_xstrdup("source");
2192 name = pa_utils_get_source_name(source);
2193 card = source->card;
2195 port = source->active_port;
2201 pa_assert_se((profile = card->active_profile));
2203 if (!u->state.profile)
2204 profile_name = profile->name;
2206 pa_log_debug("state.profile is not null. '%s' supresses '%s'",
2207 u->state.profile, profile->name);
2208 profile_name = u->state.profile;
2212 if (!(bus = pa_utils_get_card_bus(card))) {
2213 pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
2214 "on its card", type, name, PA_PROP_DEVICE_BUS);
2218 pci = pa_streq(bus, "pci");
2219 usb = pa_streq(bus, "usb");
2220 platform = pa_streq(bus, "platform");
2221 bluetooth = pa_streq(bus, "bluetooth");
2223 if (!pci && !usb && !bluetooth && !platform) {
2224 pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
2225 "of its card", type, name, bus);
2234 snprintf(buf, len, "%s@%s.%s", name, port->name, profile_name);
2242 snprintf(buf, len, "%s@%s", name, port->name);
2249 static pa_sink *make_output_prerouting(struct userdata *u,
2251 pa_channel_map *chmap,
2252 const char *media_role,
2253 mir_node **target_ret)
2257 pa_sink *sink = NULL;
2262 pa_assert_se((core = u->core));
2265 target = mir_router_make_prerouting(u, data);
2268 pa_log("there is no default route for the stream '%s'", data->amname);
2269 else if (target->paidx == PA_IDXSET_INVALID)
2270 pa_log("can't route to default '%s': no sink", target->amname);
2272 if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
2273 pa_log("no route to default '%s': sink is gone", target->amname);
2275 if (u->enable_multiplex == true) {
2276 if (pa_classify_multiplex_stream(data)) {
2277 data->mux = pa_multiplex_create(u->multiplex, core,
2278 sink->index, chmap, NULL,
2279 media_role, data->type);
2281 sink = pa_idxset_get_by_index(core->sinks,
2282 data->mux->sink_index);
2291 *target_ret = target;
2297 static pa_source *make_input_prerouting(struct userdata *u,
2299 const char *media_role,
2300 mir_node **target_ret)
2304 pa_source *source = NULL;
2308 pa_assert_se((core = u->core));
2310 target = mir_router_make_prerouting(u, data);
2313 pa_log("there is no default route for the stream '%s'", data->amname);
2314 else if (target->paidx == PA_IDXSET_INVALID)
2315 pa_log("can't route to default '%s': no source", target->amname);
2317 if (!(source = pa_idxset_get_by_index(core->sources, target->paidx)))
2318 pa_log("no route to default '%s': source is gone",target->amname);
2322 *target_ret = target;
2327 static mir_node_type get_stream_routing_class(pa_proplist *pl)
2333 t = pa_utils_get_stream_class(pl);
2335 if (t >= mir_application_class_begin && t < mir_application_class_end)
2338 return mir_node_type_unknown;
2341 static const char *get_stream_amname(mir_node_type type, const char *name, pa_proplist *pl)
2348 return pa_xstrdup("radio");
2354 appid = pa_utils_get_appid(pl);
2356 if (!strcmp(appid, "threaded-ml") ||
2357 !strcmp(appid, "WebProcess") ||
2358 !strcmp(appid,"wrt_launchpad_daemon") )
2360 return "wrtApplication";
2362 return "icoApplication";
2376 static void set_bluetooth_profile(struct userdata *u,
2378 pa_direction_t direction)
2381 pa_device_port *port;
2382 pa_card_profile *prof, *make_active;
2383 void *state0, *state1;
2384 bool port_available;
2390 pa_assert_se((core = u->core));
2396 pa_log_debug("which profile to make active:");
2398 PA_HASHMAP_FOREACH(prof, card->profiles, state0) {
2399 if (!prof->n_sinks && !prof->n_sources) {
2401 pa_log_debug(" considering %s", prof->name);
2407 port_available = false;
2409 PA_HASHMAP_FOREACH(port, card->ports, state1) {
2410 if ((direction & port->direction) &&
2411 pa_hashmap_get(port->profiles, prof->name))
2413 port_available = (port->available != PA_AVAILABLE_NO);
2418 if (!port_available)
2419 pa_log_debug(" ruling out %s (port not available)", prof->name);
2420 else if (prof->available != PA_AVAILABLE_YES)
2421 pa_log_debug(" ruling out %s (profile not available)", prof->name);
2425 if (((direction & PA_DIRECTION_INPUT) && prof->n_sources > 0) ||
2426 ((direction & PA_DIRECTION_OUTPUT) && prof->n_sinks > 0) ) {
2427 if (make_active && prof->priority < make_active->priority)
2428 pa_log_debug(" ruling out %s (low priority)", prof->name);
2430 pa_log_debug(" considering %s", prof->name);
2435 pa_log_debug(" ruling out %s (direction)", prof->name);
2442 pa_log_debug("No suitable profile found. Frustrated and do nothing");
2444 if (make_active == card->active_profile)
2445 pa_log_debug("Profile %s already set. Do nothing", make_active->name);
2447 if (switch_off && nport) {
2448 pa_log_debug("Do not switch to %s as active ports are existing "
2449 "to the other direction", make_active->name);
2452 pa_log_debug("Set profile %s", make_active->name);
2454 if ((prof = pa_hashmap_get(card->profiles, make_active->name)) != NULL &&
2455 pa_card_set_profile(card, prof, false) < 0) {
2456 pa_log_debug("Failed to change profile to %s",
2464 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
2466 struct userdata *u = d;
2472 pa_log_debug("deferred routing starts");
2474 mir_router_make_routing(u);
2478 static void schedule_deferred_routing(struct userdata *u)
2483 pa_assert_se((core = u->core));
2485 pa_log_debug("scheduling deferred routing");
2487 pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
2491 static void card_check_cb(pa_mainloop_api *m, void *d)
2493 card_check_t *cc = d;
2499 int n_sink, n_source;
2505 pa_assert((u = cc->u));
2506 pa_assert((core = u->core));
2508 pa_log_debug("card check starts");
2510 if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
2511 pa_log_debug("card %u is gone", cc->index);
2513 n_sink = n_source = 0;
2515 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
2516 if ((sink->card) && sink->card->index == card->index)
2520 PA_IDXSET_FOREACH(source, core->sources, idx) {
2521 if ((source->card) && source->card->index == card->index)
2525 if (n_sink || n_source) {
2526 pa_log_debug("found %u sinks and %u sources belonging to "
2527 "'%s' card", n_sink, n_source, card->name);
2528 pa_log_debug("nothing to do");
2531 pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
2533 mir_router_make_routing(u);
2541 static void schedule_card_check(struct userdata *u, pa_card *card)
2548 pa_assert_se((core = u->core));
2550 pa_log_debug("scheduling card check");
2552 cc = pa_xnew0(card_check_t, 1);
2554 cc->index = card->index;
2556 pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
2560 static void source_cleanup_cb(pa_mainloop_api *m, void *d)
2562 source_cleanup_t *sc = d;
2569 pa_assert((u = sc->u));
2570 pa_assert((core = u->core));
2572 pa_log_debug("source cleanup starts");
2574 pa_loopback_destroy(u->loopback, u->core, sc->loop);
2575 pa_multiplex_destroy(u->multiplex, u->core, sc->mux);
2577 pa_log_debug("source cleanup ends");
2583 static void schedule_source_cleanup(struct userdata *u, mir_node *node)
2586 source_cleanup_t *sc;
2590 pa_assert_se((core = u->core));
2592 pa_log_debug("scheduling source cleanup");
2594 sc = pa_xnew0(source_cleanup_t, 1);
2596 sc->mux = node->mux;
2597 sc->loop = node->loop;
2602 pa_mainloop_api_once(core->mainloop, source_cleanup_cb, sc);
2607 static void stream_uncork_cb(pa_mainloop_api *m, void *d)
2609 stream_uncork_t *suc = d;
2613 pa_sink_input *sinp;
2619 pa_assert((u = suc->u));
2620 pa_assert((core = u->core));
2622 pa_log_debug("start uncorking stream");
2624 if (!(sink = pa_idxset_get_by_index(core->sinks, suc->index))) {
2625 pa_log_debug("sink.%d gone", suc->index);
2629 if (!(sinp = pa_idxset_first(core->sink_inputs, &index))) {
2630 pa_log_debug("sink_input is gone");
2634 pa_sink_input_cork(sinp, false);
2636 pa_log_debug("stream.%u uncorked", sinp->index);
2644 static void schedule_stream_uncorking(struct userdata *u, pa_sink *sink)
2647 stream_uncork_t *suc;
2651 pa_assert_se((core = u->core));
2653 pa_log_debug("scheduling stream uncorking");
2655 suc = pa_xnew0(stream_uncork_t, 1);
2657 suc->index = sink->index;
2659 pa_mainloop_api_once(core->mainloop, stream_uncork_cb, suc);
2666 * indent-tabs-mode: nil