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"
47 #define MAX_CARD_TARGET 4
48 #define MAX_NAME_LENGTH 256
50 #define ACTIVE_PORT NULL
52 /* Bluetooth service class */
53 #define BIT(x) (1U << (x))
55 #define BT_SERVICE_MASK 0xffe
56 #define BT_SERVICE_INFORMATION BIT(23) /**< WEB-server, WAP-server, etc */
57 #define BT_SERVICE_TELEPHONY BIT(22) /**< Modem, Headset, etc*/
58 #define BT_SERVICE_AUDIO BIT(21) /**< Speaker, Microphone, Headset */
59 #define BT_SERVICE_OBJECT_XFER BIT(20) /**< v-Inbox, v-Folder, etc */
60 #define BT_SERVICE_CAPTURING BIT(19) /**< Scanner, Microphone, etc */
61 #define BT_SERVICE_RENDERING BIT(18) /**< Printing, Speaker, etc */
62 #define BT_SERVICE_NETWORKING BIT(17) /**< LAN, Ad hoc, etc */
63 #define BT_SERVICE_POSITIONING BIT(16) /**< Location identification */
82 static const char combine_pattern[] = "Simultaneous output on ";
83 static const char loopback_pattern[] = "Loopback from ";
85 static void handle_alsa_card(struct userdata *, pa_card *);
86 static void handle_bluetooth_card(struct userdata *, pa_card *);
88 static void handle_udev_loaded_card(struct userdata *, pa_card *,
90 static void handle_card_ports(struct userdata *, mir_node *,
91 pa_card *, pa_card_profile *);
93 static mir_node *create_node(struct userdata *, mir_node *, pa_bool_t *);
94 static void destroy_node(struct userdata *, mir_node *);
95 static pa_bool_t update_node_availability(struct userdata *, mir_direction,
96 void *, pa_device_port *, pa_bool_t);
98 static void parse_profile_name(pa_card_profile *,
99 char **, char **, char *, int);
101 static char *node_key(struct userdata *, mir_direction,
102 void *, pa_device_port *, char *, size_t);
104 static pa_sink *make_output_prerouting(struct userdata *, mir_node *,
105 pa_channel_map *, const char *,
108 static mir_node_type get_stream_routing_class(pa_proplist *);
111 static void schedule_deferred_routing(struct userdata *);
112 static void schedule_card_check(struct userdata *, pa_card *);
113 static void schedule_source_cleanup(struct userdata *, mir_node *);
114 static void schedule_stream_uncorking(struct userdata *, pa_sink *);
116 static void pa_hashmap_node_free(void *node, void *u)
118 mir_node_destroy(u, node);
122 struct pa_discover *pa_discover_init(struct userdata *u)
124 pa_discover *discover = pa_xnew0(pa_discover, 1);
128 discover->selected = TRUE;
130 discover->nodes.byname = pa_hashmap_new(pa_idxset_string_hash_func,
131 pa_idxset_string_compare_func);
132 discover->nodes.byptr = pa_hashmap_new(pa_idxset_trivial_hash_func,
133 pa_idxset_trivial_compare_func);
137 void pa_discover_done(struct userdata *u)
139 pa_discover *discover;
141 if (u && (discover = u->discover)) {
142 pa_hashmap_free(discover->nodes.byname, pa_hashmap_node_free,u);
143 pa_hashmap_free(discover->nodes.byptr, NULL,NULL);
149 void pa_discover_domain_up(struct userdata *u)
151 pa_discover *discover;
156 pa_assert_se((discover = u->discover));
158 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
159 node->amid = AM_ID_INVALID;
161 if (node->visible && node->available)
162 pa_audiomgr_register_node(u, node);
166 void pa_discover_domain_down(struct userdata *u)
170 void pa_discover_add_card(struct userdata *u, pa_card *card)
177 if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS))) {
178 pa_log_debug("ignoring card '%s' due to lack of '%s' property",
179 pa_utils_get_card_name(card), PA_PROP_DEVICE_BUS);
183 if (pa_streq(bus, "pci") || pa_streq(bus, "usb")) {
184 handle_alsa_card(u, card);
187 else if (pa_streq(bus, "bluetooth")) {
188 handle_bluetooth_card(u, card);
192 pa_log_debug("ignoring card '%s' due to unsupported bus type '%s'",
193 pa_utils_get_card_name(card), bus);
196 void pa_discover_remove_card(struct userdata *u, pa_card *card)
199 pa_discover *discover;
205 pa_assert_se((discover = u->discover));
207 if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS)))
211 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
212 if (node->implement == mir_device &&
213 node->pacard.index == card->index)
215 if (pa_streq(bus, "pci") || pa_streq(bus, "usb"))
216 mir_constrain_destroy(u, node->paname);
218 destroy_node(u, node);
222 if (pa_streq(bus, "bluetooth"))
223 mir_constrain_destroy(u, card->name);
226 void pa_discover_profile_changed(struct userdata *u, pa_card *card)
229 pa_card_profile *prof;
232 pa_discover *discover;
244 pa_assert_se((core = u->core));
245 pa_assert_se((discover = u->discover));
248 if ((bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS)) == NULL) {
249 pa_log_debug("ignoring profile change on card '%s' due to lack of '%s'"
250 "property", pa_utils_get_card_name(card),
255 pci = pa_streq(bus, "pci");
256 usb = pa_streq(bus, "usb");
257 bluetooth = pa_streq(bus, "bluetooth");
259 if (!pci && !usb && !bluetooth) {
260 pa_log_debug("ignoring profile change on card '%s' due to unsupported "
261 "bus type '%s'", pa_utils_get_card_name(card), bus);
262 u->state.sink = u->state.source = PA_IDXSET_INVALID;
266 if ((index = u->state.sink) != PA_IDXSET_INVALID) {
267 if ((sink = pa_idxset_get_by_index(core->sinks, index)))
268 pa_discover_add_sink(u, sink, TRUE);
270 pa_log_debug("sink.%u is gone", index);
271 u->state.sink = PA_IDXSET_INVALID;
274 if ((index = u->state.source) != PA_IDXSET_INVALID) {
275 if ((source = pa_idxset_get_by_index(core->sources, index)))
276 pa_discover_add_source(u, source);
278 pa_log_debug("source.%u is gone", index);
279 u->state.source = PA_IDXSET_INVALID;
283 pa_assert_se((prof = card->active_profile));
285 pa_log_debug("bluetooth profile changed to '%s' on card '%s'",
286 prof->name, card->name);
288 if (!prof->n_sinks && !prof->n_sources) {
289 /* switched of but not unloaded yet */
290 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
291 if (node->implement == mir_device &&
292 node->pacard.index == card->index)
294 node->available = FALSE;
300 pa_log_debug("alsa profile changed to '%s' on card '%s'",
301 card->active_profile->name, card->name);
303 stamp = pa_utils_get_stamp();
305 handle_alsa_card(u, card);
307 PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
308 if (node->implement == mir_device &&
309 node->pacard.index == card->index &&
312 destroy_node(u, node);
319 void pa_discover_port_available_changed(struct userdata *u,
320 pa_device_port *port)
332 pa_assert_se((core = u->core));
334 switch (port->available) {
335 case PA_PORT_AVAILABLE_NO: state = "not "; available = FALSE; break;
336 case PA_PORT_AVAILABLE_YES: state = ""; available = TRUE; break;
337 default: /* do nothing */ return;
340 pa_log_debug("port '%s' availabilty changed to %savailable. Updating",
345 if (port->is_output) {
346 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
348 if (port == pa_hashmap_get(sink->ports, port->name)) {
349 pa_log_debug(" sink '%s'", sink->name);
350 route |= update_node_availability(u, mir_output,sink,port,
357 if (port->is_input) {
358 PA_IDXSET_FOREACH(source, core->sources, idx) {
360 if (port == pa_hashmap_get(source->ports, port->name)) {
361 pa_log_debug(" source '%s'", source->name);
362 route |= update_node_availability(u, mir_input,source,port,
370 mir_router_make_routing(u);
373 void pa_discover_add_sink(struct userdata *u, pa_sink *sink, pa_bool_t route)
376 pa_discover *discover;
383 const char *loopback_role;
390 pa_assert_se((core = u->core));
391 pa_assert_se((discover = u->discover));
393 module = sink->module;
395 if ((card = sink->card)) {
396 if (!(key = node_key(u, mir_output,sink,ACTIVE_PORT, kbf,sizeof(kbf))))
398 if (!(node = pa_discover_find_node_by_key(u, key))) {
399 if (u->state.profile)
400 pa_log_debug("can't find node for sink (key '%s')", key);
402 u->state.sink = sink->index;
405 pa_log_debug("node for '%s' found (key %s). Updating with sink data",
406 node->paname, node->key);
407 node->paidx = sink->index;
408 pa_discover_add_node_to_ptr_hash(u, sink, node);
410 if ((loopback_role = pa_classify_loopback_stream(node))) {
411 if (!(ns = pa_utils_get_null_source(u))) {
412 pa_log("Can't load loopback module: no initial null source");
415 node->loop = pa_loopback_create(u->loopback, core, node->index,
416 ns->index, sink->index,
419 mir_node_print(node, nbf, sizeof(nbf));
420 pa_log_debug("updated node:\n%s", nbf);
426 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
427 mir_router_make_routing(u);
429 if (!u->state.profile)
430 schedule_deferred_routing(u);
434 else if (!module || !pa_streq(module->name, "module-combine-sink")) {
435 memset(&data, 0, sizeof(data));
436 data.key = pa_xstrdup(sink->name);
437 data.direction = mir_output;
438 data.implement = mir_device;
439 data.channels = sink->channel_map.channels;
441 if (sink == pa_utils_get_null_sink(u)) {
442 data.visible = FALSE;
443 data.available = TRUE;
444 data.type = mir_null;
445 data.amname = pa_xstrdup("Silent");
446 data.amid = AM_ID_INVALID;
447 data.paname = pa_xstrdup(sink->name);
448 data.paidx = sink->index;
451 pa_xfree(data.key); /* for now */
452 pa_log_info("currently we do not support "
453 "statically loaded sinks");
457 create_node(u, &data, NULL);
462 void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
464 pa_discover *discover;
471 pa_assert_se((discover = u->discover));
473 name = pa_utils_get_sink_name(sink);
475 if (!(node = pa_hashmap_get(discover->nodes.byptr, sink)))
476 pa_log_debug("can't find node for sink (name '%s')", name);
478 pa_log_debug("node found for '%s'. Reseting sink data", name);
479 node->paidx = PA_IDXSET_INVALID;
480 pa_hashmap_remove(discover->nodes.byptr, sink);
485 if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
486 node->available = FALSE;
488 if (!u->state.profile)
489 schedule_deferred_routing(u);
493 pa_log_info("currently we do not support statically loaded sinks");
499 void pa_discover_add_source(struct userdata *u, pa_source *source)
502 pa_discover *discover;
508 const char *loopback_role;
515 pa_assert_se((core = u->core));
516 pa_assert_se((discover = u->discover));
518 if ((card = source->card)) {
519 if (!(key = node_key(u,mir_input,source,ACTIVE_PORT,kbf,sizeof(kbf))))
521 if (!(node = pa_discover_find_node_by_key(u, key))) {
522 if (u->state.profile)
523 pa_log_debug("can't find node for source (key '%s')", key);
525 u->state.source = source->index;
528 pa_log_debug("node for '%s' found. Updating with source data",
530 node->paidx = source->index;
531 pa_discover_add_node_to_ptr_hash(u, source, node);
532 if ((loopback_role = pa_classify_loopback_stream(node))) {
533 if (!(ns = pa_utils_get_null_sink(u))) {
534 pa_log("Can't load loopback module: no initial null sink");
537 node->loop = pa_loopback_create(u->loopback, core, node->index,
538 source->index, ns->index,
541 sink_index = pa_loopback_get_sink_index(core, node->loop);
542 node->mux = pa_multiplex_find(u->multiplex, sink_index);
545 mir_node_print(node, nbf, sizeof(nbf));
546 pa_log_debug("updated node:\n%s", nbf);
550 memset(&data, 0, sizeof(data));
551 data.key = pa_xstrdup(source->name);
552 data.direction = mir_input;
553 data.implement = mir_device;
554 data.channels = source->channel_map.channels;
556 if (source == pa_utils_get_null_source(u)) {
557 data.visible = FALSE;
558 data.available = TRUE;
559 data.type = mir_null;
560 data.amname = pa_xstrdup("Silent");
561 data.amid = AM_ID_INVALID;
562 data.paname = pa_xstrdup(source->name);
563 data.paidx = source->index;
566 pa_xfree(data.key); /* for now */
567 pa_log_info("currently we do not support "
568 "statically loaded sources");
572 create_node(u, &data, NULL);
577 void pa_discover_remove_source(struct userdata *u, pa_source *source)
579 pa_discover *discover;
585 pa_assert_se((discover = u->discover));
587 name = pa_utils_get_source_name(source);
589 if (!(node = pa_hashmap_get(discover->nodes.byptr, source)))
590 pa_log_debug("can't find node for source (name '%s')", name);
592 pa_log_debug("node found. Reseting source data");
593 schedule_source_cleanup(u, node);
594 node->paidx = PA_IDXSET_INVALID;
595 pa_hashmap_remove(discover->nodes.byptr, source);
600 void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
603 pa_discover *discover;
617 pa_assert_se((core = u->core));
618 pa_assert_se((discover = u->discover));
619 pa_assert_se((pl = sinp->proplist));
621 if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME))) {
622 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
623 pa_log_debug("Seems to be a combine stream. Nothing to do ...");
626 if (!strncmp(media, loopback_pattern, sizeof(loopback_pattern)-1)) {
627 pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
632 name = pa_utils_get_sink_input_name(sinp);
634 pa_log_debug("registering stream '%s'", name);
636 if (!(type = pa_classify_guess_stream_node_type(pl))) {
637 pa_log_debug("cant find stream class for '%s'. "
638 "Leaving it alone", name);
642 pa_utils_set_stream_routing_properties(pl, type, NULL);
644 snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
646 memset(&data, 0, sizeof(data));
648 data.direction = mir_input;
649 data.implement = mir_stream;
650 data.channels = sinp->channel_map.channels;
653 data.available = TRUE;
655 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
656 data.amid = AM_ID_INVALID;
658 data.paidx = sinp->index;
661 * here we can't guess whether the application requested an explicit
662 * route by sepcifying the target sink @ stream creation time.
664 * the brute force solution: we make a default route for this stream
665 * possibly overwiriting the orginal app request :(
667 /* this will set data.mux */
668 role = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_ROLE);
669 sink = make_output_prerouting(u, &data, &sinp->channel_map, role, &target);
671 node = create_node(u, &data, NULL);
673 pa_discover_add_node_to_ptr_hash(u, sinp, node);
675 if (sink && target) {
676 pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
678 if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
679 pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
681 pa_log_debug("register route '%s' => '%s'",
682 node->amname, target->amname);
683 /* FIXME: and actually do it ... */
688 void pa_discover_preroute_sink_input(struct userdata *u,
689 pa_sink_input_new_data *data)
694 pa_discover *discover;
703 pa_assert_se((core = u->core));
704 pa_assert_se((discover = u->discover));
705 pa_assert_se((pl = data->proplist));
707 mnam = (m = data->module) ? m->name : "";
709 if (pa_streq(mnam, "module-combine-sink"))
710 type = mir_node_type_unknown;
712 if (pa_streq(mnam, "module-loopback"))
714 type = pa_classify_guess_stream_node_type(pl);
715 pa_utils_set_stream_routing_properties(pl, type, data->sink);
719 memset(&fake, 0, sizeof(fake));
720 fake.direction = mir_input;
721 fake.implement = mir_stream;
722 fake.channels = data->channel_map.channels;
725 fake.available = TRUE;
726 fake.amname = "<preroute sink-input>";
728 role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
729 sink = make_output_prerouting(u, &fake, &data->channel_map, role,NULL);
733 if (fake.mux && !(data->flags & PA_SINK_INPUT_START_CORKED)) {
734 data->flags |= PA_SINK_INPUT_START_CORKED;
735 schedule_stream_uncorking(u, sink);
739 if (!pa_sink_input_new_data_set_sink(data, sink, FALSE))
740 pa_log("can't set sink %d for new sink-input", sink->index);
746 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
750 pa_sink_input *csinp;
752 pa_discover *discover;
764 pa_assert_se((core = u->core));
765 pa_assert_se((discover = u->discover));
766 pa_assert_se((pl = sinp->proplist));
768 if (!(media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)))
771 if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
772 pa_log_debug("New stream is a combine stream. Nothing to do ...");
774 } else if (!strncmp(media, loopback_pattern, sizeof(loopback_pattern)-1)) {
775 pa_log_debug("New stream is a loopback output stream");
777 if ((node = pa_utils_get_node_from_stream(u, sinp))) {
778 if (node->direction == mir_input)
779 pa_log_debug("loopback stream node '%s' found", node->amname);
781 pa_log_debug("ignoring it");
786 pa_log_debug("can't find node for the loopback stream");
793 name = pa_utils_get_sink_input_name(sinp);
795 pa_log_debug("dealing with new stream '%s'", name);
797 if ((type = get_stream_routing_class(pl)) == mir_node_type_unknown) {
798 if (!(type = pa_classify_guess_stream_node_type(pl))) {
799 pa_log_debug("cant find stream class for '%s'. "
800 "Leaving it alone", name);
804 pa_utils_set_stream_routing_properties(pl, type, NULL);
806 /* if needed, make some post-routing here */
809 /* we need to add this to main hashmap as that is used for loop
810 through on all nodes. */
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;
820 data.available = TRUE;
822 data.amdescr = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
823 data.amid = AM_ID_INVALID;
825 data.paidx = sinp->index;
826 data.mux = pa_multiplex_find(u->multiplex, sinp->sink->index);
828 node = create_node(u, &data, &created);
833 pa_log("%s: confused with stream. '%s' did exists",
834 __FILE__, node->amname);
838 pa_discover_add_node_to_ptr_hash(u, sinp, node);
843 csinp = pa_idxset_get_by_index(core->sink_inputs,
844 data.mux->defstream_index);
845 s = csinp ? csinp->sink : NULL;
850 pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
852 if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
853 pa_log_debug("can't figure out where this stream is routed");
855 pa_log_debug("register route '%s' => '%s'",
856 node->amname, snod->amname);
857 /* FIXME: and actually do it ... */
859 pa_fader_apply_volume_limits(u, pa_utils_get_stamp());
864 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
866 pa_discover *discover;
874 pa_assert_se((discover = u->discover));
876 name = pa_utils_get_sink_input_name(sinp);
878 pa_log("sink-input '%s' going to be destroyed", name);
880 if (!(node = pa_discover_remove_node_from_ptr_hash(u, sinp)))
881 pa_log_debug("can't find node for sink-input (name '%s')", name);
883 pa_log_debug("node found for '%s'. After clearing routes "
884 "it will be destroyed", name);
886 if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
887 pa_log_debug("can't figure out where this stream is routed");
889 pa_log_debug("clear route '%s' => '%s'",
890 node->amname, sinknod->amname);
892 /* FIXME: and actually do it ... */
896 destroy_node(u, node);
898 mir_router_make_routing(u);
903 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
905 pa_discover *discover;
909 pa_assert_se((discover = u->discover));
912 node = pa_hashmap_get(discover->nodes.byname, key);
919 mir_node *pa_discover_find_node_by_ptr(struct userdata *u, void *ptr)
921 pa_discover *discover;
925 pa_assert_se((discover = u->discover));
928 node = pa_hashmap_get(discover->nodes.byptr, ptr);
935 void pa_discover_add_node_to_ptr_hash(struct userdata *u,
939 pa_discover *discover;
944 pa_assert_se((discover = u->discover));
946 pa_hashmap_put(discover->nodes.byptr, ptr, node);
949 mir_node *pa_discover_remove_node_from_ptr_hash(struct userdata *u, void *ptr)
951 pa_discover *discover;
955 pa_assert_se((discover = u->discover));
957 return pa_hashmap_remove(discover->nodes.byptr, ptr);
961 static void handle_alsa_card(struct userdata *u, pa_card *card)
968 memset(&data, 0, sizeof(data));
970 data.amid = AM_ID_INVALID;
971 data.implement = mir_device;
972 data.paidx = PA_IDXSET_INVALID;
973 data.stamp = pa_utils_get_stamp();
975 cnam = pa_utils_get_card_name(card);
976 udd = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
978 if (udd && pa_streq(udd, "1")) {
979 /* udev loaded alsa card */
980 if (!strncmp(cnam, "alsa_card.", 10)) {
982 handle_udev_loaded_card(u, card, &data, cid);
987 /* statically loaded pci or usb card */
990 pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
994 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
996 pa_discover *discover;
997 pa_card_profile *prof;
1005 char paname[MAX_NAME_LENGTH+1];
1006 char amname[MAX_NAME_LENGTH+1];
1007 char key[MAX_NAME_LENGTH+1];
1010 pa_assert_se((discover = u->discover));
1012 cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1015 memset(paname, 0, sizeof(paname));
1016 memset(amname, 0, sizeof(amname));
1017 memset(key , 0, sizeof(key) );
1019 memset(&data, 0, sizeof(data));
1021 data.visible = TRUE;
1022 data.amid = AM_ID_INVALID;
1023 data.implement = mir_device;
1024 data.paidx = PA_IDXSET_INVALID;
1025 data.paname = paname;
1026 data.amname = amname;
1027 data.amdescr = (char *)cdescr;
1028 data.pacard.index = card->index;
1029 data.stamp = pa_utils_get_stamp();
1031 cnam = pa_utils_get_card_name(card);
1033 if (!strncmp(cnam, "bluez_card.", 11)) {
1036 cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1038 PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1039 data.available = TRUE;
1040 data.pacard.profile = prof->name;
1042 if (prof->n_sinks > 0) {
1043 data.direction = mir_output;
1044 data.channels = prof->max_sink_channels;
1045 data.amname = amname;
1047 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1048 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1049 pa_classify_node_by_card(&data, card, prof, NULL);
1050 node = create_node(u, &data, NULL);
1051 cd = mir_constrain_create(u, "profile", mir_constrain_profile,
1053 mir_constrain_add_node(u, cd, node);
1056 if (prof->n_sources > 0) {
1057 data.direction = mir_input;
1058 data.channels = prof->max_source_channels;
1059 data.amname = amname;
1061 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1062 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1063 pa_classify_node_by_card(&data, card, prof, NULL);
1064 node = create_node(u, &data, NULL);
1065 mir_constrain_add_node(u, cd, node);
1069 if (!(prof = card->active_profile))
1070 pa_log("card '%s' has no active profile", card->name);
1072 pa_log_debug("card '%s' default profile '%s'",
1073 card->name, prof->name);
1076 schedule_card_check(u, card);
1081 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
1082 mir_node *data, char *cardid)
1084 pa_discover *discover;
1085 pa_card_profile *prof;
1086 pa_card_profile *active;
1088 const char *alsanam;
1090 char *sinks[MAX_CARD_TARGET+1];
1091 char *sources[MAX_CARD_TARGET+1];
1092 char buf[MAX_NAME_LENGTH+1];
1093 char paname[MAX_NAME_LENGTH+1];
1094 char amname[MAX_NAME_LENGTH+1];
1098 pa_assert(card->profiles);
1099 pa_assert_se((discover = u->discover));
1101 alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
1103 memset(amname, 0, sizeof(amname));
1105 data->paname = paname;
1106 data->amname = amname;
1107 data->amdescr = (char *)alsanam;
1109 data->pacard.index = card->index;
1111 active = card->active_profile;
1113 PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1114 /* filtering: deal with selected profiles if requested so */
1115 if (discover->selected && (!active || (active && prof != active)))
1118 /* filtering: skip the 'off' profiles */
1119 if (!prof->n_sinks && !prof->n_sources)
1122 /* filtering: consider sinks with suitable amount channels */
1123 if (prof->n_sinks &&
1124 (prof->max_sink_channels < discover->chmin ||
1125 prof->max_sink_channels > discover->chmax ))
1128 /* filtering: consider sources with suitable amount channels */
1129 if (prof->n_sources &&
1130 (prof->max_source_channels < discover->chmin ||
1131 prof->max_source_channels > discover->chmax ))
1134 data->pacard.profile = prof->name;
1136 parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
1138 data->direction = mir_output;
1139 data->channels = prof->max_sink_channels;
1140 for (i = 0; (sid = sinks[i]); i++) {
1141 snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
1142 handle_card_ports(u, data, card, prof);
1145 data->direction = mir_input;
1146 data->channels = prof->max_source_channels;
1147 for (i = 0; (sid = sources[i]); i++) {
1148 snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
1149 handle_card_ports(u, data, card, prof);
1155 static void handle_card_ports(struct userdata *u, mir_node *data,
1156 pa_card *card, pa_card_profile *prof)
1158 mir_node *node = NULL;
1159 pa_bool_t have_ports = FALSE;
1160 mir_constr_def *cd = NULL;
1161 char *amname = data->amname;
1162 pa_device_port *port;
1165 char key[MAX_NAME_LENGTH+1];
1173 PA_HASHMAP_FOREACH(port, card->ports, state) {
1175 * if this port did not belong to any profile
1176 * (ie. prof->profiles == NULL) we assume that this port
1177 * does works with all the profiles
1179 if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
1180 ((port->is_input && data->direction == mir_input)||
1181 (port->is_output && data->direction == mir_output)))
1186 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
1189 data->available = (port->available != PA_PORT_AVAILABLE_NO);
1191 data->amname = amname;
1192 data->paport = port->name;
1194 pa_classify_node_by_card(data, card, prof, port);
1196 node = create_node(u, data, &created);
1199 node->stamp = data->stamp;
1201 cd = mir_constrain_create(u, "port", mir_constrain_port,
1203 mir_constrain_add_node(u, cd, node);
1210 data->key = data->paname;
1211 data->available = TRUE;
1213 pa_classify_node_by_card(data, card, prof, NULL);
1215 node = create_node(u, data, &created);
1218 node->stamp = data->stamp;
1221 data->amname = amname;
1226 static mir_node *create_node(struct userdata *u, mir_node *data,
1227 pa_bool_t *created_ret)
1229 pa_discover *discover;
1236 pa_assert(data->key);
1237 pa_assert(data->paname);
1238 pa_assert_se((discover = u->discover));
1240 if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
1245 node = mir_node_create(u, data);
1246 pa_hashmap_put(discover->nodes.byname, node->key, node);
1248 mir_node_print(node, buf, sizeof(buf));
1249 pa_log_debug("new node:\n%s", buf);
1251 if (node->available)
1252 pa_audiomgr_register_node(u, node);
1256 *created_ret = created;
1261 static void destroy_node(struct userdata *u, mir_node *node)
1263 pa_discover *discover;
1267 pa_assert_se((discover = u->discover));
1270 removed = pa_hashmap_remove(discover->nodes.byname, node->key);
1272 if (node != removed) {
1274 pa_log("%s: confused with data structures: key mismatch. "
1275 " attempted to destroy '%s'; actually destroyed '%s'",
1276 __FILE__, node->key, removed->key);
1278 pa_log("%s: confused with data structures: node '%s' "
1279 "is not in the hash table", __FILE__, node->key);
1283 pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
1285 if (node->implement == mir_stream) {
1286 if (node->direction == mir_input) {
1288 pa_log_debug("removing multiplexer");
1293 pa_audiomgr_unregister_node(u, node);
1295 mir_constrain_remove_node(u, node);
1297 pa_loopback_destroy(u->loopback, u->core, node->loop);
1298 pa_multiplex_destroy(u->multiplex, u->core, node->mux);
1300 mir_node_destroy(u, node);
1304 static pa_bool_t update_node_availability(struct userdata *u,
1305 mir_direction direction,
1306 void *data, pa_device_port *port,
1307 pa_bool_t available)
1316 pa_assert(direction == mir_input || direction == mir_output);
1318 if ((key = node_key(u, direction, data, port, buf, sizeof(buf)))) {
1319 if (!(node = pa_discover_find_node_by_key(u, key)))
1320 pa_log_debug(" can't find node for sink (key '%s')", key);
1322 pa_log_debug(" node for '%s' found (key %s)",
1323 node->paname, node->key);
1324 if ((!available && node->available) ||
1325 ( available && !node->available) )
1327 node->available = available;
1330 pa_audiomgr_register_node(u, node);
1332 pa_audiomgr_unregister_node(u, node);
1334 return TRUE; /* routing needed */
1339 return FALSE; /* no routing needed */
1342 static char *get_name(char **string_ptr, int offs)
1344 char c, *name, *end;
1346 name = *string_ptr + offs;
1348 for (end = name; (c = *end); end++) {
1360 static void parse_profile_name(pa_card_profile *prof,
1370 pa_assert(prof->name);
1372 strncpy(buf, prof->name, buflen);
1373 buf[buflen-1] = '\0';
1375 memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
1376 memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
1379 if (!strncmp(p, "output:", 7)) {
1380 if (i >= MAX_CARD_TARGET) {
1381 pa_log_debug("number of outputs exeeds the maximum %d in "
1382 "profile name '%s'", MAX_CARD_TARGET, prof->name);
1385 sinks[i++] = get_name(&p, 7);
1387 else if (!strncmp(p, "input:", 6)) {
1388 if (j >= MAX_CARD_TARGET) {
1389 pa_log_debug("number of inputs exeeds the maximum %d in "
1390 "profile name '%s'", MAX_CARD_TARGET, prof->name);
1393 sources[j++] = get_name(&p, 6);
1396 pa_log("%s: failed to parse profile name '%s'",
1397 __FILE__, prof->name);
1404 static char *node_key(struct userdata *u, mir_direction direction,
1405 void *data, pa_device_port *port, char *buf, size_t len)
1408 pa_card_profile *profile;
1412 pa_bool_t bluetooth;
1415 const char *profile_name;
1420 pa_assert(direction == mir_input || direction == mir_output);
1422 if (direction == mir_input) {
1423 pa_sink *sink = data;
1425 name = pa_utils_get_sink_name(sink);
1428 port = sink->active_port;
1431 pa_source *source = data;
1433 name = pa_utils_get_source_name(source);
1434 card = source->card;
1436 port = source->active_port;
1442 pa_assert_se((profile = card->active_profile));
1444 if (!u->state.profile)
1445 profile_name = profile->name;
1447 pa_log_debug("state.profile is not null. '%s' supresses '%s'",
1448 u->state.profile, profile->name);
1449 profile_name = u->state.profile;
1453 if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS))) {
1454 pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
1455 "on its card", type, name, PA_PROP_DEVICE_BUS);
1459 pci = pa_streq(bus, "pci");
1460 usb = pa_streq(bus, "usb");
1461 bluetooth = pa_streq(bus, "bluetooth");
1463 if (!pci && !usb && !bluetooth) {
1464 pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
1465 "of its card", type, name, bus);
1471 snprintf(buf, len, "%s@%s", name, profile_name);
1478 snprintf(buf, len, "%s@%s", name, port->name);
1485 static pa_sink *make_output_prerouting(struct userdata *u,
1487 pa_channel_map *chmap,
1488 const char *media_role,
1489 mir_node **target_ret)
1493 pa_sink *sink = NULL;
1498 pa_assert_se((core = u->core));
1501 target = mir_router_make_prerouting(u, data);
1504 pa_log("there is no default route for the stream '%s'", data->amname);
1505 else if (target->paidx == PA_IDXSET_INVALID)
1506 pa_log("can't route to default '%s': no sink", target->amname);
1508 if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
1509 pa_log("can't route to default '%s': sink is gone",target->amname);
1511 if (pa_classify_multiplex_stream(data)) {
1512 data->mux = pa_multiplex_create(u->multiplex, core,
1513 sink->index, chmap, NULL,
1514 media_role, data->type);
1516 sink = pa_idxset_get_by_index(core->sinks,
1517 data->mux->sink_index);
1525 *target_ret = target;
1531 static mir_node_type get_stream_routing_class(pa_proplist *pl)
1539 if ((clid = pa_proplist_gets(pl, PA_PROP_ROUTING_CLASS_ID))) {
1540 type = strtol(clid, &e, 10);
1543 if (type >= mir_application_class_begin &&
1544 type < mir_application_class_end)
1551 return mir_node_type_unknown;
1556 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
1558 struct userdata *u = d;
1564 pa_log_debug("deferred routing starts");
1566 mir_router_make_routing(u);
1570 static void schedule_deferred_routing(struct userdata *u)
1575 pa_assert_se((core = u->core));
1577 pa_log_debug("scheduling deferred routing");
1579 pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
1583 static void card_check_cb(pa_mainloop_api *m, void *d)
1585 card_check_t *cc = d;
1591 int n_sink, n_source;
1597 pa_assert((u = cc->u));
1598 pa_assert((core = u->core));
1600 pa_log_debug("card check starts");
1602 if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
1603 pa_log_debug("card %u is gone", cc->index);
1605 n_sink = n_source = 0;
1607 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
1608 if ((sink->card) && sink->card->index == card->index)
1612 PA_IDXSET_FOREACH(source, core->sources, idx) {
1613 if ((source->card) && source->card->index == card->index)
1617 if (n_sink || n_source) {
1618 pa_log_debug("found %u sinks and %u sources belonging to "
1619 "'%s' card", n_sink, n_source, card->name);
1620 pa_log_debug("nothing to do");
1623 pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
1625 mir_router_make_routing(u);
1633 static void schedule_card_check(struct userdata *u, pa_card *card)
1640 pa_assert_se((core = u->core));
1642 pa_log_debug("scheduling card check");
1644 cc = pa_xnew0(card_check_t, 1);
1646 cc->index = card->index;
1648 pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
1652 static void source_cleanup_cb(pa_mainloop_api *m, void *d)
1654 source_cleanup_t *sc = d;
1661 pa_assert((u = sc->u));
1662 pa_assert((core = u->core));
1664 pa_log_debug("source cleanup starts");
1666 pa_loopback_destroy(u->loopback, u->core, sc->loop);
1667 pa_multiplex_destroy(u->multiplex, u->core, sc->mux);
1669 pa_log_debug("source cleanup ends");
1675 static void schedule_source_cleanup(struct userdata *u, mir_node *node)
1678 source_cleanup_t *sc;
1682 pa_assert_se((core = u->core));
1684 pa_log_debug("scheduling source cleanup");
1686 sc = pa_xnew0(source_cleanup_t, 1);
1688 sc->mux = node->mux;
1689 sc->loop = node->loop;
1694 pa_mainloop_api_once(core->mainloop, source_cleanup_cb, sc);
1698 static void stream_uncork_cb(pa_mainloop_api *m, void *d)
1700 stream_uncork_t *suc = d;
1704 pa_sink_input *sinp;
1710 pa_assert((u = suc->u));
1711 pa_assert((core = u->core));
1713 pa_log_debug("start uncorking stream");
1715 if (!(sink = pa_idxset_get_by_index(core->sinks, suc->index))) {
1716 pa_log_debug("sink.%d gone", suc->index);
1720 if (!(sinp = pa_idxset_first(core->sink_inputs, &index))) {
1721 pa_log_debug("sink_input is gone");
1725 pa_sink_input_cork(sinp, FALSE);
1727 pa_log_debug("stream.%u uncorked", sinp->index);
1735 static void schedule_stream_uncorking(struct userdata *u, pa_sink *sink)
1738 stream_uncork_t *suc;
1742 pa_assert_se((core = u->core));
1744 pa_log_debug("scheduling stream uncorking");
1746 suc = pa_xnew0(stream_uncork_t, 1);
1748 suc->index = sink->index;
1750 pa_mainloop_api_once(core->mainloop, stream_uncork_cb, suc);
1757 * indent-tabs-mode: nil