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,
22 #include <pulsecore/pulsecore-config.h>
24 #include <pulsecore/hashmap.h>
25 #include <pulsecore/idxset.h>
26 #include <pulsecore/client.h>
27 #include <pulsecore/core-util.h>
28 #include <pulsecore/log.h>
29 #include <pulsecore/card.h>
30 #include <pulsecore/device-port.h>
31 #include <pulsecore/sink-input.h>
32 #include <pulsecore/source-output.h>
33 #include <pulsecore/strbuf.h>
40 #include "constrain.h"
41 #include "multiplex.h"
46 #define MAX_CARD_TARGET 4
47 #define MAX_NAME_LENGTH 256
49 #define ACTIVE_PORT NULL
57 static const char combine_pattern[] = "Simultaneous output on ";
59 static void handle_alsa_card(struct userdata *, pa_card *);
60 static void handle_bluetooth_card(struct userdata *, pa_card *);
62 static void handle_udev_loaded_card(struct userdata *, pa_card *,
64 static void handle_card_ports(struct userdata *, mir_node *,
65 pa_card *, pa_card_profile *);
67 static mir_node *create_node(struct userdata *, mir_node *, pa_bool_t *);
68 static void destroy_node(struct userdata *, mir_node *);
69 static pa_bool_t update_node_availability(struct userdata *, mir_direction,
70 void *, pa_device_port *, pa_bool_t);
72 static void parse_profile_name(pa_card_profile *,
73 char **, char **, char *, int);
75 static char *node_key(struct userdata *, mir_direction,
76 void *, pa_device_port *, char *, size_t);
78 static pa_sink *make_output_prerouting(struct userdata *, mir_node *,
79 pa_channel_map *, mir_node **);
81 static mir_node_type get_stream_routing_class(pa_proplist *);
84 static void schedule_deferred_routing(struct userdata *);
85 static void schedule_card_check(struct userdata *, pa_card *);
88 static void pa_hashmap_node_free(void *node, void *u)
90 mir_node_destroy(u, node);
94 struct pa_discover *pa_discover_init(struct userdata *u)
96 pa_discover *discover = pa_xnew0(pa_discover, 1);
100 discover->selected = TRUE;
102 discover->nodes.byname = pa_hashmap_new(pa_idxset_string_hash_func,
103 pa_idxset_string_compare_func);
104 discover->nodes.byptr = pa_hashmap_new(pa_idxset_trivial_hash_func,
105 pa_idxset_trivial_compare_func);
109 void pa_discover_done(struct userdata *u)
111 pa_discover *discover;
113 if (u && (discover = u->discover)) {
114 pa_hashmap_free(discover->nodes.byname, pa_hashmap_node_free,u);
115 pa_hashmap_free(discover->nodes.byptr, NULL,NULL);
121 void pa_discover_domain_up(struct userdata *u)
123 pa_discover *discover;
128 pa_assert_se((discover = u->discover));
130 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
131 node->amid = AM_ID_INVALID;
133 if (node->visible && node->available)
134 pa_audiomgr_register_node(u, node);
138 void pa_discover_domain_down(struct userdata *u)
142 void pa_discover_add_card(struct userdata *u, pa_card *card)
149 if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS))) {
150 pa_log_debug("ignoring card '%s' due to lack of '%s' property",
151 pa_utils_get_card_name(card), PA_PROP_DEVICE_BUS);
155 if (pa_streq(bus, "pci") || pa_streq(bus, "usb")) {
156 handle_alsa_card(u, card);
159 else if (pa_streq(bus, "bluetooth")) {
160 handle_bluetooth_card(u, card);
164 pa_log_debug("ignoring card '%s' due to unsupported bus type '%s'",
165 pa_utils_get_card_name(card), bus);
168 void pa_discover_remove_card(struct userdata *u, pa_card *card)
171 pa_discover *discover;
177 pa_assert_se((discover = u->discover));
179 if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS)))
183 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
184 if (node->implement == mir_device &&
185 node->pacard.index == card->index)
187 if (pa_streq(bus, "pci") || pa_streq(bus, "usb"))
188 mir_constrain_destroy(u, node->paname);
190 destroy_node(u, node);
194 if (pa_streq(bus, "bluetooth"))
195 mir_constrain_destroy(u, card->name);
198 void pa_discover_profile_changed(struct userdata *u, pa_card *card)
200 pa_card_profile *prof;
201 pa_discover *discover;
212 pa_assert_se((discover = u->discover));
215 if ((bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS)) == NULL) {
216 pa_log_debug("ignoring profile change on card '%s' due to lack of '%s'"
217 "property", pa_utils_get_card_name(card),
222 pci = pa_streq(bus, "pci");
223 usb = pa_streq(bus, "usb");
224 bluetooth = pa_streq(bus, "bluetooth");
226 if (!pci && !usb && !bluetooth) {
227 pa_log_debug("ignoring profile change on card '%s' due to unsupported "
228 "bus type '%s'", pa_utils_get_card_name(card), bus);
232 pa_assert_se((prof = card->active_profile));
234 pa_log_debug("bluetooth profile changed to '%s' on card '%s'",
235 prof->name, card->name);
237 if (!prof->n_sinks && !prof->n_sources) {
238 /* switched of but not unloaded yet */
239 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
240 if (node->implement == mir_device &&
241 node->pacard.index == card->index)
243 node->available = FALSE;
249 pa_log_debug("alsa profile changed to '%s' on card '%s'",
250 card->active_profile->name, card->name);
252 stamp = pa_utils_get_stamp();
254 handle_alsa_card(u, card);
256 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
257 if (node->implement == mir_device &&
258 node->pacard.index == card->index &&
261 destroy_node(u, node);
268 void pa_discover_port_available_changed(struct userdata *u,
269 pa_device_port *port)
281 pa_assert_se((core = u->core));
283 switch (port->available) {
284 case PA_PORT_AVAILABLE_NO: state = "not "; available = FALSE; break;
285 case PA_PORT_AVAILABLE_YES: state = ""; available = TRUE; break;
286 default: /* do nothing */ return;
289 pa_log_debug("port '%s' availabilty changed to %savailable. Updating",
294 if (port->is_output) {
295 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
297 if (port == pa_hashmap_get(sink->ports, port->name)) {
298 pa_log_debug(" sink '%s'", sink->name);
299 route |= update_node_availability(u, mir_output,sink,port,
306 if (port->is_input) {
307 PA_IDXSET_FOREACH(source, core->sources, idx) {
309 if (port == pa_hashmap_get(source->ports, port->name)) {
310 pa_log_debug(" source '%s'", source->name);
311 route |= update_node_availability(u, mir_input,source,port,
319 mir_router_make_routing(u);
322 void pa_discover_add_sink(struct userdata *u, pa_sink *sink, pa_bool_t route)
325 pa_discover *discover;
335 pa_assert_se((discover = u->discover));
337 module = sink->module;
339 if ((card = sink->card)) {
340 if (!(key = node_key(u, mir_output,sink,ACTIVE_PORT, buf,sizeof(buf))))
342 if (!(node = pa_discover_find_node_by_key(u, key))) {
343 pa_log_debug("can't find node for sink (key '%s')", key);
346 pa_log_debug("node for '%s' found (key %s). Updating with sink data",
347 node->paname, node->key);
348 node->paidx = sink->index;
349 pa_discover_add_node_to_ptr_hash(u, sink, node);
354 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
355 mir_router_make_routing(u);
357 if (!u->state.profile)
358 schedule_deferred_routing(u);
362 else if (!module || !pa_streq(module->name, "module-combine-sink")) {
363 memset(&data, 0, sizeof(data));
364 data.key = pa_xstrdup(sink->name);
365 data.direction = mir_output;
366 data.implement = mir_device;
367 data.channels = sink->channel_map.channels;
369 if (sink == pa_utils_get_null_sink(u)) {
370 data.visible = FALSE;
371 data.available = TRUE;
372 data.type = mir_null;
373 data.amname = pa_xstrdup("Silent");
374 data.amid = AM_ID_INVALID;
375 data.paname = pa_xstrdup(sink->name);
376 data.paidx = sink->index;
379 pa_xfree(data.key); /* for now */
380 pa_log_info("currently we do not support statically loaded sinks");
384 create_node(u, &data, NULL);
389 void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
391 pa_discover *discover;
398 pa_assert_se((discover = u->discover));
400 name = pa_utils_get_sink_name(sink);
402 if (!(node = pa_hashmap_get(discover->nodes.byptr, sink)))
403 pa_log_debug("can't find node for sink (name '%s')", name);
405 pa_log_debug("node found for '%s'. Reseting sink data", name);
406 node->paidx = PA_IDXSET_INVALID;
407 pa_hashmap_remove(discover->nodes.byptr, sink);
412 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
413 node->available = FALSE;
415 if (!u->state.profile)
416 schedule_deferred_routing(u);
420 pa_log_info("currently we do not support statically loaded sinks");
426 void pa_discover_add_source(struct userdata *u, pa_source *source)
428 pa_discover *discover;
436 pa_assert_se((discover = u->discover));
438 if ((card = source->card)) {
439 if (!(key = node_key(u,mir_input,source,ACTIVE_PORT,buf,sizeof(buf))))
441 if (!(node = pa_discover_find_node_by_key(u, key))) {
442 pa_log_debug("can't find node for source (key '%s')", key);
445 pa_log_debug("node for '%s' found. Updating with source data",
447 node->paidx = source->index;
448 pa_discover_add_node_to_ptr_hash(u, source, node);
453 void pa_discover_remove_source(struct userdata *u, pa_source *source)
455 pa_discover *discover;
461 pa_assert_se((discover = u->discover));
463 name = pa_utils_get_source_name(source);
465 if (!(node = pa_hashmap_get(discover->nodes.byptr, source)))
466 pa_log_debug("can't find node for source (name '%s')", name);
468 pa_log_debug("node found. Reseting source data");
469 node->paidx = PA_IDXSET_INVALID;
470 pa_hashmap_remove(discover->nodes.byptr, source);
475 void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
478 pa_discover *discover;
491 pa_assert_se((core = u->core));
492 pa_assert_se((discover = u->discover));
493 pa_assert_se((pl = sinp->proplist));
495 if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)) &&
496 !strncmp(media, combine_pattern, sizeof(combine_pattern)-1))
498 pa_log_debug("Seems to be a combine stream. Nothing to do ...");
502 name = pa_utils_get_sink_input_name(sinp);
504 pa_log_debug("registering stream '%s'", name);
506 if (!(type = pa_classify_guess_stream_node_type(pl))) {
507 pa_log_debug("cant find stream class for '%s'. "
508 "Leaving it alone", name);
512 pa_utils_set_stream_routing_properties(pl, type, NULL);
514 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
516 memset(&data, 0, sizeof(data));
518 data.direction = mir_input;
519 data.implement = mir_stream;
520 data.channels = sinp->channel_map.channels;
523 data.available = TRUE;
525 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
526 data.amid = AM_ID_INVALID;
528 data.paidx = sinp->index;
531 * here we can't guess whether the application requested an explicit
532 * route by sepcifying the target sink @ stream creation time.
534 * the brute force solution: we make a default route for this stream
535 * possibly overwiriting the orginal app request :(
537 /* this will set data.mux */
538 sink = make_output_prerouting(u, &data, &sinp->channel_map, &target);
540 node = create_node(u, &data, NULL);
542 pa_discover_add_node_to_ptr_hash(u, sinp, node);
544 if (sink && target) {
545 pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
547 if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
548 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
550 pa_log_debug("register route '%s' => '%s'",
551 node->amname, target->amname);
552 /* FIXME: and actually do it ... */
557 void pa_discover_preroute_sink_input(struct userdata *u,
558 pa_sink_input_new_data *data)
563 pa_discover *discover;
570 pa_assert_se((core = u->core));
571 pa_assert_se((discover = u->discover));
572 pa_assert_se((pl = data->proplist));
574 if ((m = data->module) && pa_streq(m->name, "module-combine-sink"))
575 type = mir_node_type_unknown;
577 type = pa_classify_guess_stream_node_type(pl);
578 pa_utils_set_stream_routing_properties(pl, type, data->sink);
582 memset(&fake, 0, sizeof(fake));
583 fake.direction = mir_input;
584 fake.implement = mir_stream;
585 fake.channels = data->channel_map.channels;
588 fake.available = TRUE;
589 fake.amname = "<preroute>";
591 if ((sink = make_output_prerouting(u,&fake,&data->channel_map,NULL))) {
592 if (!pa_sink_input_new_data_set_sink(data, sink, FALSE))
593 pa_log("can't set sink %d for new sink-input", sink->index);
599 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
603 pa_sink_input *csinp;
605 pa_discover *discover;
617 pa_assert_se((core = u->core));
618 pa_assert_se((discover = u->discover));
619 pa_assert_se((pl = sinp->proplist));
622 if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)) &&
623 !strncmp(media, combine_pattern, sizeof(combine_pattern)-1))
625 pa_log_debug("New stream is a combine stream. Nothing to do ...");
629 name = pa_utils_get_sink_input_name(sinp);
631 pa_log_debug("dealing with new stream '%s'", name);
633 if ((type = get_stream_routing_class(pl)) == mir_node_type_unknown) {
634 if (!(type = pa_classify_guess_stream_node_type(pl))) {
635 pa_log_debug("cant find stream class for '%s'. "
636 "Leaving it alone", name);
640 pa_utils_set_stream_routing_properties(pl, type, NULL);
642 /* if needed, make some post-routing here */
645 /* we need to add this to main hashmap as that is used for loop
646 through on all nodes. */
647 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
649 memset(&data, 0, sizeof(data));
651 data.direction = mir_input;
652 data.implement = mir_stream;
653 data.channels = sinp->channel_map.channels;
656 data.available = TRUE;
658 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
659 data.amid = AM_ID_INVALID;
661 data.paidx = sinp->index;
662 data.mux = pa_multiplex_find(u->multiplex, sinp->sink->index);
664 node = create_node(u, &data, &created);
669 pa_log("%s: confused with stream. '%s' did exists",
670 __FILE__, node->amname);
674 pa_discover_add_node_to_ptr_hash(u, sinp, node);
679 csinp = pa_idxset_get_by_index(core->sink_inputs,
680 data.mux->defstream_index);
681 s = csinp ? csinp->sink : NULL;
685 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
687 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
688 pa_log_debug("can't figure out where this stream is routed");
690 pa_log_debug("register route '%s' => '%s'",
691 node->amname, snod->amname);
692 /* FIXME: and actually do it ... */
695 pa_fader_apply_volume_limits(u, pa_utils_get_stamp());
700 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
702 pa_discover *discover;
709 pa_assert_se((discover = u->discover));
711 name = pa_utils_get_sink_input_name(sinp);
713 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sinp)))
714 pa_log_debug("can't find node for sink-input (name '%s')", name);
716 pa_log_debug("node found for '%s'. After clearing the route "
717 "it will be destroyed", name);
719 if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
720 pa_log_debug("can't figure out where this stream is routed");
722 pa_log_debug("clear route '%s' => '%s'",
723 node->amname, sinknod->amname);
725 /* FIXME: and actually do it ... */
729 destroy_node(u, node);
731 mir_router_make_routing(u);
736 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
738 pa_discover *discover;
742 pa_assert_se((discover = u->discover));
745 node = pa_hashmap_get(discover->nodes.byname, key);
752 mir_node *pa_discover_find_node_by_ptr(struct userdata *u, void *ptr)
754 pa_discover *discover;
758 pa_assert_se((discover = u->discover));
761 node = pa_hashmap_get(discover->nodes.byptr, ptr);
768 void pa_discover_add_node_to_ptr_hash(struct userdata *u,
772 pa_discover *discover;
777 pa_assert_se((discover = u->discover));
779 pa_hashmap_put(discover->nodes.byptr, ptr, node);
782 mir_node *pa_discover_remove_node_from_ptr_hash(struct userdata *u, void *ptr)
784 pa_discover *discover;
788 pa_assert_se((discover = u->discover));
790 return pa_hashmap_remove(discover->nodes.byptr, ptr);
794 static void handle_alsa_card(struct userdata *u, pa_card *card)
801 memset(&data, 0, sizeof(data));
803 data.amid = AM_ID_INVALID;
804 data.implement = mir_device;
805 data.paidx = PA_IDXSET_INVALID;
806 data.stamp = pa_utils_get_stamp();
808 cnam = pa_utils_get_card_name(card);
809 udd = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
811 if (udd && pa_streq(udd, "1")) {
812 /* udev loaded alsa card */
813 if (!strncmp(cnam, "alsa_card.", 10)) {
815 handle_udev_loaded_card(u, card, &data, cid);
820 /* statically loaded pci or usb card */
823 pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
827 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
829 pa_discover *discover;
830 pa_card_profile *prof;
838 char paname[MAX_NAME_LENGTH+1];
839 char amname[MAX_NAME_LENGTH+1];
840 char key[MAX_NAME_LENGTH+1];
843 pa_assert_se((discover = u->discover));
845 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
848 memset(paname, 0, sizeof(paname));
849 memset(amname, 0, sizeof(amname));
850 memset(key , 0, sizeof(key) );
852 memset(&data, 0, sizeof(data));
855 data.amid = AM_ID_INVALID;
856 data.implement = mir_device;
857 data.paidx = PA_IDXSET_INVALID;
858 data.paname = paname;
859 data.amname = amname;
860 data.amdescr = (char *)cdescr;
861 data.pacard.index = card->index;
862 data.stamp = pa_utils_get_stamp();
864 cnam = pa_utils_get_card_name(card);
866 if (!strncmp(cnam, "bluez_card.", 11)) {
869 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
871 PA_HASHMAP_FOREACH(prof, card->profiles, state) {
872 data.available = TRUE;
873 data.pacard.profile = prof->name;
875 if (prof->n_sinks > 0) {
876 data.direction = mir_output;
877 data.channels = prof->max_sink_channels;
878 data.amname = amname;
880 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
881 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
882 pa_classify_node_by_card(&data, card, prof, NULL);
883 node = create_node(u, &data, NULL);
884 cd = mir_constrain_create(u, "profile", mir_constrain_profile,
886 mir_constrain_add_node(u, cd, node);
889 if (prof->n_sources > 0) {
890 data.direction = mir_input;
891 data.channels = prof->max_source_channels;
892 data.amname = amname;
894 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
895 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
896 pa_classify_node_by_card(&data, card, prof, NULL);
897 node = create_node(u, &data, NULL);
898 mir_constrain_add_node(u, cd, node);
902 if (!(prof = card->active_profile))
903 pa_log("card '%s' has no active profile", card->name);
905 pa_log_debug("card '%s' default profile '%s'",
906 card->name, prof->name);
909 schedule_card_check(u, card);
914 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
915 mir_node *data, char *cardid)
917 pa_discover *discover;
918 pa_card_profile *prof;
919 pa_card_profile *active;
923 char *sinks[MAX_CARD_TARGET+1];
924 char *sources[MAX_CARD_TARGET+1];
925 char buf[MAX_NAME_LENGTH+1];
926 char paname[MAX_NAME_LENGTH+1];
927 char amname[MAX_NAME_LENGTH+1];
931 pa_assert(card->profiles);
932 pa_assert_se((discover = u->discover));
934 alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
936 memset(amname, 0, sizeof(amname));
938 data->paname = paname;
939 data->amname = amname;
940 data->amdescr = (char *)alsanam;
942 data->pacard.index = card->index;
944 active = card->active_profile;
946 PA_HASHMAP_FOREACH(prof, card->profiles, state) {
947 /* filtering: deal with selected profiles if requested so */
948 if (discover->selected && (!active || (active && prof != active)))
951 /* filtering: skip the 'off' profiles */
952 if (!prof->n_sinks && !prof->n_sources)
955 /* filtering: consider sinks with suitable amount channels */
957 (prof->max_sink_channels < discover->chmin ||
958 prof->max_sink_channels > discover->chmax ))
961 /* filtering: consider sources with suitable amount channels */
962 if (prof->n_sources &&
963 (prof->max_source_channels < discover->chmin ||
964 prof->max_source_channels > discover->chmax ))
967 data->pacard.profile = prof->name;
969 parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
971 data->direction = mir_output;
972 data->channels = prof->max_sink_channels;
973 for (i = 0; (sid = sinks[i]); i++) {
974 snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
975 handle_card_ports(u, data, card, prof);
978 data->direction = mir_input;
979 data->channels = prof->max_source_channels;
980 for (i = 0; (sid = sources[i]); i++) {
981 snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
982 handle_card_ports(u, data, card, prof);
988 static void handle_card_ports(struct userdata *u, mir_node *data,
989 pa_card *card, pa_card_profile *prof)
991 mir_node *node = NULL;
992 pa_bool_t have_ports = FALSE;
993 mir_constr_def *cd = NULL;
994 char *amname = data->amname;
995 pa_device_port *port;
998 char key[MAX_NAME_LENGTH+1];
1006 PA_HASHMAP_FOREACH(port, card->ports, state) {
1008 * if this port did not belong to any profile
1009 * (ie. prof->profiles == NULL) we assume that this port
1010 * does works with all the profiles
1012 if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
1013 ((port->is_input && data->direction == mir_input)||
1014 (port->is_output && data->direction == mir_output)))
1019 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
1022 data->available = (port->available != PA_PORT_AVAILABLE_NO);
1024 data->amname = amname;
1025 data->paport = port->name;
1027 pa_classify_node_by_card(data, card, prof, port);
1029 node = create_node(u, data, &created);
1032 node->stamp = data->stamp;
1034 cd = mir_constrain_create(u, "port", mir_constrain_port,
1036 mir_constrain_add_node(u, cd, node);
1043 data->key = data->paname;
1044 data->available = TRUE;
1046 pa_classify_node_by_card(data, card, prof, NULL);
1048 node = create_node(u, data, &created);
1051 node->stamp = data->stamp;
1054 data->amname = amname;
1059 static mir_node *create_node(struct userdata *u, mir_node *data,
1060 pa_bool_t *created_ret)
1062 pa_discover *discover;
1069 pa_assert(data->key);
1070 pa_assert(data->paname);
1071 pa_assert_se((discover = u->discover));
1073 if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
1078 node = mir_node_create(u, data);
1079 pa_hashmap_put(discover->nodes.byname, node->key, node);
1081 mir_node_print(node, buf, sizeof(buf));
1082 pa_log_debug("new node:\n%s", buf);
1084 if (node->available)
1085 pa_audiomgr_register_node(u, node);
1089 *created_ret = created;
1094 static void destroy_node(struct userdata *u, mir_node *node)
1096 pa_discover *discover;
1100 pa_assert_se((discover = u->discover));
1103 removed = pa_hashmap_remove(discover->nodes.byname, node->key);
1105 if (node != removed) {
1107 pa_log("%s: confused with data structures: key mismatch. "
1108 " attempted to destroy '%s'; actually destroyed '%s'",
1109 __FILE__, node->key, removed->key);
1111 pa_log("%s: confused with data structures: node '%s' "
1112 "is not in the hash table", __FILE__, node->key);
1116 pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
1118 if (node->implement == mir_stream) {
1119 if (node->direction == mir_input) {
1121 pa_log_debug("removing multiplexer");
1126 pa_audiomgr_unregister_node(u, node);
1128 mir_constrain_remove_node(u, node);
1130 pa_multiplex_destroy(u->multiplex, u->core, node->mux);
1132 mir_node_destroy(u, node);
1136 static pa_bool_t update_node_availability(struct userdata *u,
1137 mir_direction direction,
1138 void *data, pa_device_port *port,
1139 pa_bool_t available)
1148 pa_assert(direction == mir_input || direction == mir_output);
1150 if ((key = node_key(u, direction, data, port, buf, sizeof(buf)))) {
1151 if (!(node = pa_discover_find_node_by_key(u, key)))
1152 pa_log_debug(" can't find node for sink (key '%s')", key);
1154 pa_log_debug(" node for '%s' found (key %s)",
1155 node->paname, node->key);
1156 if ((!available && node->available) ||
1157 ( available && !node->available) )
1159 node->available = available;
1162 pa_audiomgr_register_node(u, node);
1164 pa_audiomgr_unregister_node(u, node);
1166 return TRUE; /* routing needed */
1171 return FALSE; /* no routing needed */
1174 static char *get_name(char **string_ptr, int offs)
1176 char c, *name, *end;
1178 name = *string_ptr + offs;
1180 for (end = name; (c = *end); end++) {
1192 static void parse_profile_name(pa_card_profile *prof,
1202 pa_assert(prof->name);
1204 strncpy(buf, prof->name, buflen);
1205 buf[buflen-1] = '\0';
1207 memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
1208 memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
1211 if (!strncmp(p, "output:", 7)) {
1212 if (i >= MAX_CARD_TARGET) {
1213 pa_log_debug("number of outputs exeeds the maximum %d in "
1214 "profile name '%s'", MAX_CARD_TARGET, prof->name);
1217 sinks[i++] = get_name(&p, 7);
1219 else if (!strncmp(p, "input:", 6)) {
1220 if (j >= MAX_CARD_TARGET) {
1221 pa_log_debug("number of inputs exeeds the maximum %d in "
1222 "profile name '%s'", MAX_CARD_TARGET, prof->name);
1225 sources[j++] = get_name(&p, 6);
1228 pa_log("%s: failed to parse profile name '%s'",
1229 __FILE__, prof->name);
1236 static char *node_key(struct userdata *u, mir_direction direction,
1237 void *data, pa_device_port *port, char *buf, size_t len)
1240 pa_card_profile *profile;
1244 pa_bool_t bluetooth;
1247 const char *profile_name;
1252 pa_assert(direction == mir_input || direction == mir_output);
1254 if (direction == mir_input) {
1255 pa_sink *sink = data;
1257 name = pa_utils_get_sink_name(sink);
1260 port = sink->active_port;
1263 pa_source *source = data;
1265 name = pa_utils_get_source_name(source);
1266 card = source->card;
1268 port = source->active_port;
1274 pa_assert_se((profile = card->active_profile));
1276 if (!u->state.profile)
1277 profile_name = profile->name;
1279 pa_log_debug("state.profile is not null. '%s' supresses '%s'",
1280 u->state.profile, profile->name);
1281 profile_name = u->state.profile;
1285 if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS))) {
1286 pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
1287 "on its card", type, name, PA_PROP_DEVICE_BUS);
1291 pci = pa_streq(bus, "pci");
1292 usb = pa_streq(bus, "usb");
1293 bluetooth = pa_streq(bus, "bluetooth");
1295 if (!pci && !usb && !bluetooth) {
1296 pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
1297 "of its card", type, name, bus);
1303 snprintf(buf, len, "%s@%s", name, profile_name);
1310 snprintf(buf, len, "%s@%s", name, port->name);
1317 static pa_sink *make_output_prerouting(struct userdata *u,
1319 pa_channel_map *chmap,
1320 mir_node **target_ret)
1324 pa_sink *sink = NULL;
1329 pa_assert_se((core = u->core));
1333 target = mir_router_make_prerouting(u, data);
1336 pa_log("there is no default route for the stream '%s'", data->amname);
1337 else if (target->paidx == PA_IDXSET_INVALID)
1338 pa_log("can't route to default '%s': no sink", target->amname);
1340 if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
1341 pa_log("can't route to default '%s': sink is gone",target->amname);
1343 if (pa_classify_multiplex_stream(data)) {
1344 data->mux = pa_multiplex_create(u->multiplex, core,
1345 sink->index, chmap, NULL,
1348 sink = pa_idxset_get_by_index(core->sinks,
1349 data->mux->sink_index);
1357 *target_ret = target;
1363 static mir_node_type get_stream_routing_class(pa_proplist *pl)
1371 if ((clid = pa_proplist_gets(pl, PA_PROP_ROUTING_CLASS_ID))) {
1372 type = strtol(clid, &e, 10);
1375 if (type >= mir_application_class_begin &&
1376 type < mir_application_class_end)
1383 return mir_node_type_unknown;
1388 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
1390 struct userdata *u = d;
1396 pa_log_debug("deferred routing starts");
1398 mir_router_make_routing(u);
1402 static void schedule_deferred_routing(struct userdata *u)
1407 pa_assert_se((core = u->core));
1409 pa_log_debug("scheduling deferred routing");
1411 pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
1415 static void card_check_cb(pa_mainloop_api *m, void *d)
1417 card_check_t *cc = d;
1423 int n_sink, n_source;
1429 pa_assert((u = cc->u));
1430 pa_assert((core = u->core));
1432 pa_log_debug("card check starts");
1434 if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
1435 pa_log_debug("card %u is gone", cc->index);
1437 n_sink = n_source = 0;
1439 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
1440 if ((sink->card) && sink->card->index == card->index)
1444 PA_IDXSET_FOREACH(source, core->sources, idx) {
1445 if ((source->card) && source->card->index == card->index)
1449 if (n_sink || n_source) {
1450 pa_log_debug("found %u sinks and %u sources belonging to "
1451 "'%s' card", n_sink, n_source, card->name);
1452 pa_log_debug("nothing to do");
1455 pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
1457 mir_router_make_routing(u);
1465 static void schedule_card_check(struct userdata *u, pa_card *card)
1472 pa_assert_se((core = u->core));
1474 pa_log_debug("scheduling card check");
1476 cc = pa_xnew0(card_check_t, 1);
1478 cc->index = card->index;
1480 pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
1487 * indent-tabs-mode: nil