217a611257da17ae41932b19580d5acd67a8e8cc
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / discover.c
1 /*
2  * module-murphy-ivi -- PulseAudio module for providing audio routing support
3  * Copyright (c) 2012, Intel Corporation.
4  *
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.
8  *
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.
13  *
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,
17  * MA 02110-1301 USA.
18  *
19  */
20 #include <stdio.h>
21 #include <assert.h>
22
23 #include <pulsecore/pulsecore-config.h>
24
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>
35
36
37 #include "discover.h"
38 #include "node.h"
39 #include "audiomgr.h"
40 #include "router.h"
41 #include "constrain.h"
42 #include "multiplex.h"
43 #include "loopback.h"
44 #include "fader.h"
45 #include "classify.h"
46 #include "utils.h"
47 #include "extapi.h"
48 #include "stream-state.h"
49 #include "murphyif.h"
50
51 #define MAX_CARD_TARGET   4
52 #define MAX_NAME_LENGTH   256
53
54 #define ACTIVE_PORT       NULL
55
56 /* Bluetooth service class */
57 #define BIT(x)    (1U << (x))
58
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 */
68
69
70 typedef struct {
71     struct userdata *u;
72     uint32_t index;
73 } card_check_t;
74
75 typedef struct {
76     struct userdata *u;
77     pa_muxnode *mux;
78     pa_loopnode *loop;
79 } source_cleanup_t;
80
81 typedef struct {
82     struct userdata *u;
83     uint32_t index;
84 } stream_uncork_t;
85
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 ";
89
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 *);
93
94 static void handle_udev_loaded_card(struct userdata *, pa_card *,
95                                     mir_node *, char *);
96 static void handle_card_ports(struct userdata *, mir_node *,
97                               pa_card *, pa_card_profile *);
98
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 *,
102                                           bool);
103 static bool update_node_availability_by_device(struct userdata *,
104                                                     mir_direction,
105                                                     void *, pa_device_port *,
106                                                     bool);
107
108 static void parse_profile_name(pa_card_profile *,
109                                char **, char **, char *, int);
110
111 static char *node_key(struct userdata *, mir_direction,
112                       void *, pa_device_port *, char *, size_t);
113
114 static pa_sink *make_output_prerouting(struct userdata *, mir_node *,
115                                        pa_channel_map *, const char *,
116                                        mir_node **);
117 static pa_source *make_input_prerouting(struct userdata *, mir_node *,
118                                         const char *, mir_node **);
119
120 static mir_node_type get_stream_routing_class(pa_proplist *);
121
122 static void set_bluetooth_profile(struct userdata *, pa_card *, pa_direction_t);
123
124
125 static void schedule_deferred_routing(struct userdata *);
126 static void schedule_card_check(struct userdata *, pa_card *);
127 static void schedule_source_cleanup(struct userdata *, mir_node *);
128 #if 0
129 static void schedule_stream_uncorking(struct userdata *, pa_sink *);
130 #endif
131
132 static void pa_hashmap_node_free(void *node, void *u)
133 {
134     mir_node_destroy(u, node);
135 }
136
137
138 struct pa_discover *pa_discover_init(struct userdata *u)
139 {
140     pa_discover *discover = pa_xnew0(pa_discover, 1);
141
142     discover->chmin = 1;
143     discover->chmax = 2;
144     discover->selected = true;
145
146     discover->nodes.byname = pa_hashmap_new(pa_idxset_string_hash_func,
147                                             pa_idxset_string_compare_func);
148     discover->nodes.byptr  = pa_hashmap_new(pa_idxset_trivial_hash_func,
149                                             pa_idxset_trivial_compare_func);
150     return discover;
151 }
152
153 void pa_discover_done(struct userdata *u)
154 {
155     pa_discover *discover;
156     void *state;
157     mir_node *node;
158
159     if (u && (discover = u->discover)) {
160         PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
161             mir_node_destroy(u, node);
162         }
163         pa_hashmap_free(discover->nodes.byname);
164         pa_hashmap_free(discover->nodes.byptr);
165         pa_xfree(discover);
166         u->discover = NULL;
167     }
168 }
169
170 void pa_discover_domain_up(struct userdata *u)
171 {
172     pa_discover *discover;
173     mir_node    *node;
174     void        *state;
175
176     pa_assert(u);
177     pa_assert_se((discover = u->discover));
178
179     PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
180         node->amid = AM_ID_INVALID;
181
182         if (node->visible && node->available) {
183             pa_audiomgr_register_node(u, node);
184             extapi_signal_node_change(u);
185         }
186     }
187 }
188
189 void pa_discover_domain_down(struct userdata *u)
190 {
191 }
192
193 void pa_discover_add_card(struct userdata *u, pa_card *card)
194 {
195     const char *bus;
196
197     pa_assert(u);
198     pa_assert(card);
199
200     if (!(bus = pa_utils_get_card_bus(card))) {
201         pa_log_debug("ignoring card '%s' due to lack of '%s' property",
202                      pa_utils_get_card_name(card), PA_PROP_DEVICE_BUS);
203         return;
204     }
205
206     if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform")) {
207         handle_alsa_card(u, card);
208         return;
209     }
210     else if (pa_streq(bus, "bluetooth")) {
211         handle_bluetooth_card(u, card);
212         return;
213     }
214
215     pa_log_debug("ignoring card '%s' due to unsupported bus type '%s'",
216                  pa_utils_get_card_name(card), bus);
217 }
218
219 void pa_discover_remove_card(struct userdata *u, pa_card *card)
220 {
221     const char  *bus;
222     pa_discover *discover;
223     mir_node    *node;
224     void        *state;
225
226     pa_assert(u);
227     pa_assert(card);
228     pa_assert_se((discover = u->discover));
229
230     if (!(bus = pa_utils_get_card_bus(card)))
231         bus = "<unknown>";
232
233
234     PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
235         if (node->implement == mir_device &&
236             node->pacard.index == card->index)
237         {
238             if (pa_streq(bus, "pci") || pa_streq(bus, "usb") || pa_streq(bus, "platform"))
239                 mir_constrain_destroy(u, node->paname);
240
241             destroy_node(u, node);
242         }
243     }
244
245     if (pa_streq(bus, "bluetooth"))
246         mir_constrain_destroy(u, card->name);
247 }
248
249 void pa_discover_profile_changed(struct userdata *u, pa_card *card)
250 {
251     pa_core         *core;
252     pa_card_profile *prof;
253     pa_sink         *sink;
254     pa_source       *source;
255     pa_discover     *discover;
256     const char      *bus;
257     bool        pci;
258     bool        usb;
259     bool        bluetooth;
260     bool        platform;
261     uint32_t         stamp;
262     mir_node        *node;
263     void            *state;
264     uint32_t         index;
265     bool        need_routing;
266
267     pa_assert(u);
268     pa_assert(card);
269     pa_assert_se((core = u->core));
270     pa_assert_se((discover = u->discover));
271
272
273     if ((bus = pa_utils_get_card_bus(card)) == NULL) {
274         pa_log_debug("ignoring profile change on card '%s' due to lack of '%s'"
275                      "property", pa_utils_get_card_name(card),
276                      PA_PROP_DEVICE_BUS);
277         return;
278     }
279
280     pci = pa_streq(bus, "pci");
281     usb = pa_streq(bus, "usb");
282     bluetooth = pa_streq(bus, "bluetooth");
283     platform = pa_streq(bus, "platform");
284
285     if (!pci && !usb && !bluetooth && !platform) {
286         pa_log_debug("ignoring profile change on card '%s' due to unsupported "
287                      "bus type '%s'", pa_utils_get_card_name(card), bus);
288         u->state.sink = u->state.source = PA_IDXSET_INVALID;
289         return;
290     }
291
292     if ((index = u->state.sink) != PA_IDXSET_INVALID) {
293         if ((sink = pa_idxset_get_by_index(core->sinks, index)))
294             pa_discover_add_sink(u, sink, true);
295         else
296             pa_log_debug("sink.%u is gone", index);
297         u->state.sink = PA_IDXSET_INVALID;
298     }
299
300     if ((index = u->state.source) != PA_IDXSET_INVALID) {
301         if ((source = pa_idxset_get_by_index(core->sources, index)))
302             pa_discover_add_source(u, source);
303         else
304             pa_log_debug("source.%u is gone", index);
305         u->state.source = PA_IDXSET_INVALID;
306     }
307
308     if (bluetooth) {
309         pa_assert_se((prof = card->active_profile));
310
311         pa_log_debug("bluetooth profile changed to '%s' on card '%s'",
312                      prof->name, card->name);
313
314         if (!prof->n_sinks && !prof->n_sources) {
315             /* switched off but not unloaded yet */
316             need_routing = false;
317
318             PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
319                 if (node->implement == mir_device &&
320                     node->pacard.index == card->index)
321                 {
322                     if (node->type != mir_bluetooth_a2dp &&
323                         node->type != mir_bluetooth_sco)
324                     {
325                         if (node->available) {
326                             node->available = false;
327                             need_routing = true;
328                         }
329                     }
330                 }
331             }
332
333             if (need_routing)
334                 schedule_deferred_routing(u);
335         }
336     }
337     else {
338         pa_log_debug("alsa profile changed to '%s' on card '%s'",
339                      card->active_profile->name, card->name);
340
341         stamp = pa_utils_get_stamp();
342
343         handle_alsa_card(u, card);
344
345         PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
346             if (node->implement == mir_device &&
347                 node->pacard.index == card->index &&
348                 node->stamp < stamp)
349             {
350                 destroy_node(u, node);
351             }
352         }
353     }
354
355 }
356
357 void pa_discover_port_available_changed(struct userdata *u,
358                                         pa_device_port  *port)
359 {
360     pa_core       *core;
361     pa_sink       *sink;
362     pa_source     *source;
363     mir_node      *node;
364     uint32_t       idx;
365     bool      available;
366     const char    *state;
367     bool      btport;
368     bool      route;
369     pa_direction_t direction;
370     void          *iter;
371
372     pa_assert(u);
373     pa_assert(port);
374     pa_assert_se((core = u->core));
375
376     switch (port->available) {
377     case PA_AVAILABLE_NO:    state = "not available";  break;
378     case PA_AVAILABLE_YES:   state = "available";      break;
379     default:                 state = "unknown";        break;
380     }
381
382     pa_log_debug("port '%s' availabilty changed to %s. Updating",
383                  port->name, state);
384
385     btport = false;
386     route = false;
387     direction = 0;
388     iter = NULL;
389
390     while ((node = pa_utils_get_node_from_port(u, port, &iter))) {
391         btport = true;
392         available = get_bluetooth_port_availability(node, port);
393         route |= update_node_availability(u, node, available);
394         direction |= (node->direction == mir_input) ? PA_DIRECTION_INPUT : PA_DIRECTION_OUTPUT;
395     }
396
397     if (btport)
398         set_bluetooth_profile(u, port->card, direction);
399     else {
400         switch (port->available) {
401         case PA_AVAILABLE_NO:    available = false;    break;
402         case PA_AVAILABLE_YES:   available = true;     break;
403         default:                 /* do nothing */      return;
404         }
405
406         if (port->direction == PA_DIRECTION_OUTPUT) {
407             PA_IDXSET_FOREACH(sink, core->sinks, idx) {
408                 if (sink->ports) {
409                     if (port == pa_hashmap_get(sink->ports, port->name)) {
410                         pa_log_debug("   sink '%s'", sink->name);
411                         route |= update_node_availability_by_device(
412                                                      u, mir_output,
413                                                      sink, port,
414                                                      available);
415                     }
416                 }
417             }
418         }
419
420         if (port->direction == PA_DIRECTION_INPUT) {
421             PA_IDXSET_FOREACH(source, core->sources, idx) {
422                 if (source->ports) {
423                     if (port == pa_hashmap_get(source->ports, port->name)) {
424                         pa_log_debug("   source '%s'", source->name);
425                         route |= update_node_availability_by_device(
426                                                       u, mir_input,
427                                                       source, port,
428                                                       available);
429                     }
430                 }
431             }
432         }
433     }
434
435     if (route)
436         mir_router_make_routing(u);
437 }
438
439 void pa_discover_add_sink(struct userdata *u, pa_sink *sink, bool route)
440 {
441     static pa_nodeset_resdef def_resdef = {0, {0, 0}};
442
443     pa_core           *core;
444     pa_discover       *discover;
445     pa_module         *module;
446     mir_node          *node;
447     pa_card           *card;
448     char              *key;
449     char               kbf[256];
450     char               nbf[2048];
451     const char        *loopback_role;
452     pa_nodeset_map    *map;
453     pa_nodeset_resdef *resdef;
454     bool          make_rset;
455     pa_nodeset_resdef  rdbuf;
456     pa_source         *ns;
457     mir_node           data;
458     mir_node_type      type;
459     bool               add_to_hash;
460
461     pa_assert(u);
462     pa_assert(sink);
463     pa_assert_se((core = u->core));
464     pa_assert_se((discover = u->discover));
465
466     module = sink->module;
467
468     if ((card = sink->card)) {
469         if (!(key = node_key(u, mir_output,sink,ACTIVE_PORT, kbf,sizeof(kbf))))
470             return;
471         if (!(node = pa_discover_find_node_by_key(u, key))) {
472             if (u->state.profile)
473                 pa_log_debug("can't find node for sink (key '%s')", key);
474             else
475                 u->state.sink = sink->index;
476             return;
477         }
478         pa_log_debug("node for '%s' found (key %s). Updating with sink data",
479                      node->paname, node->key);
480         node->paidx = sink->index;
481         node->available = true;
482         pa_discover_add_node_to_ptr_hash(u, sink, node);
483
484         if ((loopback_role = pa_classify_loopback_stream(node))) {
485             if (!(ns = pa_utils_get_null_source(u))) {
486                 pa_log("Can't load loopback module: no initial null source");
487                 return;
488             }
489
490             map = pa_nodeset_get_map_by_role(u, loopback_role);
491             make_rset = (map && map->resdef);
492             resdef = make_rset ? map->resdef : &def_resdef;
493
494             node->loop = pa_loopback_create(u->loopback, core,
495                                             PA_LOOPBACK_SINK, node->index,
496                                             ns->index, sink->index,
497                                             loopback_role,
498                                             resdef->priority,
499                                             resdef->flags.rset,
500                                             resdef->flags.audio);
501
502             mir_node_print(node, nbf, sizeof(nbf));
503             pa_log_debug("updated node:\n%s", nbf);
504
505             if (make_rset)
506                 pa_murphyif_create_resource_set(u, node, resdef);
507         }
508
509         if (route) {
510             type = node->type;
511
512             if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
513                 mir_router_make_routing(u);
514             else {
515                 if (!u->state.profile)
516                     schedule_deferred_routing(u);
517             }
518         }
519     }
520     else if (!module || !pa_streq(module->name, "module-combine-sink")) {
521         add_to_hash = false;
522
523         memset(&data, 0, sizeof(data));
524         data.key = pa_xstrdup(sink->name);
525         data.direction = mir_output;
526         data.implement = mir_device;
527         data.channels  = sink->channel_map.channels;
528         data.available = true;
529         data.paidx     = sink->index;
530
531         if (sink == pa_utils_get_null_sink(u)) {
532             data.visible = false;
533             data.type = mir_null;
534             data.amname = pa_xstrdup("Silent");
535             data.amid = AM_ID_INVALID;
536             data.paname = pa_xstrdup(sink->name);
537         }
538         else if (pa_classify_node_by_property(&data, sink->proplist)) {
539             data.privacy = mir_public;
540             data.visible = true;
541             data.amname = pa_xstrdup(mir_node_type_str(data.type));
542             data.amid = AM_ID_INVALID;
543             data.paname = pa_xstrdup(sink->name);
544
545             add_to_hash = true;
546         }
547         else {
548             pa_xfree(data.key); /* for now */
549             pa_log_info("currently we do not support statically loaded "
550                         "sinks without " PA_PROP_NODE_TYPE " property");
551             return;
552         }
553
554         node = create_node(u, &data, NULL);
555
556         if (add_to_hash)
557             pa_discover_add_node_to_ptr_hash(u, sink, node);
558     }
559 }
560
561
562 void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
563 {
564     pa_discover    *discover;
565     mir_node       *node;
566     char           *name;
567     mir_node_type   type;
568
569     pa_assert(u);
570     pa_assert(sink);
571     pa_assert_se((discover = u->discover));
572
573     name = pa_utils_get_sink_name(sink);
574
575     if (!(node = pa_hashmap_get(discover->nodes.byptr, sink)))
576         pa_log_debug("can't find node for sink (name '%s')", name);
577     else {
578         pa_log_debug("node found for '%s'. Reseting sink data", name);
579         pa_murphyif_destroy_resource_set(u, node);
580         schedule_source_cleanup(u, node);
581         node->paidx = PA_IDXSET_INVALID;
582         pa_hashmap_remove(discover->nodes.byptr, sink);
583
584         type = node->type;
585
586         if (sink->card) {
587             if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
588                 node->available = false;
589             else {
590                 if (!u->state.profile)
591                     schedule_deferred_routing(u);
592             }
593         }
594         else {
595             pa_log_info("currently we do not support statically loaded sinks");
596         }
597     }
598 }
599
600
601 void pa_discover_add_source(struct userdata *u, pa_source *source)
602 {
603     static pa_nodeset_resdef def_resdef = {0, {0, 0}};
604
605     pa_core           *core;
606     pa_discover       *discover;
607     mir_node          *node;
608     pa_card           *card;
609     char              *key;
610     char               kbf[256];
611     char               nbf[2048];
612     const char        *loopback_role;
613     pa_nodeset_map    *map;
614     pa_nodeset_resdef *resdef;
615     bool          make_rset;
616     uint32_t           sink_index;
617     pa_sink           *ns;
618     mir_node           data;
619
620     pa_assert(u);
621     pa_assert(source);
622     pa_assert_se((core = u->core));
623     pa_assert_se((discover = u->discover));
624
625     if ((card = source->card)) {
626         if (!(key = node_key(u,mir_input,source,ACTIVE_PORT,kbf,sizeof(kbf))))
627             return;
628         if (!(node = pa_discover_find_node_by_key(u, key))) {
629             if (u->state.profile)
630                 pa_log_debug("can't find node for source (key '%s')", key);
631             else
632                 u->state.source = source->index;
633             return;
634         }
635         pa_log_debug("node for '%s' found. Updating with source data",
636                      node->amname);
637         node->paidx = source->index;
638         node->available = true;
639         pa_discover_add_node_to_ptr_hash(u, source, node);
640         if ((loopback_role = pa_classify_loopback_stream(node))) {
641             if (!(ns = pa_utils_get_null_sink(u))) {
642                 pa_log("Can't load loopback module: no initial null sink");
643                 return;
644             }
645
646             map = pa_nodeset_get_map_by_role(u, loopback_role);
647             make_rset = (map && map->resdef);
648             resdef = make_rset ? map->resdef : &def_resdef;
649
650             node->loop = pa_loopback_create(u->loopback, core,
651                                             PA_LOOPBACK_SOURCE, node->index,
652                                             source->index, ns->index,
653                                             loopback_role,
654                                             resdef->priority,
655                                             resdef->flags.rset,
656                                             resdef->flags.audio);
657             if (node->loop) {
658                 sink_index = pa_loopback_get_sink_index(core, node->loop);
659                 node->mux = pa_multiplex_find_by_sink(u->multiplex,sink_index);
660             }
661
662             mir_node_print(node, nbf, sizeof(nbf));
663             pa_log_debug("updated node:\n%s", nbf);
664
665             if (make_rset)
666                 pa_murphyif_create_resource_set(u, node, resdef);
667
668             pa_fader_apply_volume_limits(u, node->stamp);
669         }
670     }
671     else {
672         memset(&data, 0, sizeof(data));
673         data.key = pa_xstrdup(source->name);
674         data.direction = mir_input;
675         data.implement = mir_device;
676         data.channels  = source->channel_map.channels;
677         data.available = true;
678
679         if (source == pa_utils_get_null_source(u)) {
680             data.visible = false;
681             data.type = mir_null;
682             data.amname = pa_xstrdup("Silent");
683             data.amid = AM_ID_INVALID;
684             data.paname = pa_xstrdup(source->name);
685             data.paidx = source->index;
686         }
687         else if (pa_classify_node_by_property(&data, source->proplist)) {
688             data.visible = true;
689             data.amname = pa_xstrdup(mir_node_type_str(data.type));
690             data.amid   = AM_ID_INVALID;
691             data.paname = pa_xstrdup(source->name);
692         }
693         else {
694             pa_xfree(data.key); /* for now */
695             pa_log_info("currently we do not support statically loaded "
696                         "sources without " PA_PROP_NODE_TYPE " property");
697             return;
698         }
699
700         create_node(u, &data, NULL);
701     }
702 }
703
704
705 void pa_discover_remove_source(struct userdata *u, pa_source *source)
706 {
707     pa_discover    *discover;
708     mir_node       *node;
709     char           *name;
710     mir_node_type   type;
711
712     pa_assert(u);
713     pa_assert(source);
714     pa_assert_se((discover = u->discover));
715
716     name = pa_utils_get_source_name(source);
717
718     if (!(node = pa_hashmap_get(discover->nodes.byptr, source)))
719         pa_log_debug("can't find node for source (name '%s')", name);
720     else {
721         pa_log_debug("node found. Reseting source data");
722         pa_murphyif_destroy_resource_set(u, node);
723         schedule_source_cleanup(u, node);
724         node->paidx = PA_IDXSET_INVALID;
725         pa_hashmap_remove(discover->nodes.byptr, source);
726
727         type = node->type;
728
729         if (source->card) {
730             if (type != mir_bluetooth_sco)
731                 node->available = false;
732             else {
733                 if (!u->state.profile)
734                     schedule_deferred_routing(u);
735             }
736         }
737         else {
738             pa_log_info("currently we do not support statically "
739                         "loaded sources");
740         }
741     }
742 }
743
744
745 void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
746 {
747     pa_core           *core;
748     pa_discover       *discover;
749     pa_proplist       *pl;
750     char              *name;
751     const char        *media;
752     mir_node_type      type;
753     mir_node           data;
754     mir_node          *node;
755     mir_node          *target;
756     char               key[256];
757     pa_sink           *sink;
758     const char        *role;
759     pa_nodeset_resdef *resdef;
760
761     pa_assert(u);
762     pa_assert(sinp);
763     pa_assert_se((core = u->core));
764     pa_assert_se((discover = u->discover));
765     pa_assert_se((pl = sinp->proplist));
766
767     if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME))) {
768         if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
769             pa_log_debug("Seems to be a combine stream. Nothing to do ...");
770             return;
771         }
772         if (!strncmp(media, loopback_outpatrn, sizeof(loopback_outpatrn)-1)) {
773             pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
774             return;
775         }
776     }
777
778     name = pa_utils_get_sink_input_name(sinp);
779
780     pa_log_debug("registering input stream '%s'", name);
781
782     if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
783         pa_log_debug("cant find stream class for '%s'. "
784                      "Leaving it alone", name);
785         return;
786     }
787
788     pa_utils_set_stream_routing_properties(pl, type, NULL);
789
790     snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
791
792     memset(&data, 0, sizeof(data));
793     data.key       = key;
794     data.direction = mir_input;
795     data.implement = mir_stream;
796     data.channels  = sinp->channel_map.channels;
797     data.type      = type;
798     data.zone      = pa_utils_get_zone(sinp->proplist);
799     data.visible   = true;
800     data.available = true;
801     data.amname    = name;
802     data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
803     data.amid      = AM_ID_INVALID;
804     data.paname    = name;
805     data.paidx     = sinp->index;
806     data.rsetid    = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
807
808     /*
809      * here we can't guess whether the application requested an explicit
810      * route by sepcifying the target sink @ stream creation time.
811      *
812      * the brute force solution: we make a default route for this stream
813      * possibly overwiriting the orginal app request :(
814      */
815     /* this will set data.mux */
816     role = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_ROLE);
817     sink = make_output_prerouting(u, &data, &sinp->channel_map, role, &target);
818
819     node = create_node(u, &data, NULL);
820     pa_assert(node);
821     pa_discover_add_node_to_ptr_hash(u, sinp, node);
822
823     if (sink && target) {
824         pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
825
826         if (pa_sink_input_move_to(sinp, sink, false) < 0)
827             pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
828         else
829             pa_audiomgr_add_default_route(u, node, target);
830     }
831 }
832
833 bool pa_discover_preroute_sink_input(struct userdata *u,
834                                      pa_sink_input_new_data *data)
835 {
836     pa_core           *core;
837     pa_module         *m;
838     pa_proplist       *pl;
839     pa_discover       *discover;
840     pa_multiplex      *multiplex;
841     mir_node           fake;
842     pa_sink           *sink;
843     pa_sink_input     *sinp;
844     const char        *mnam;
845     const char        *role;
846     mir_node_type      type;
847     mir_node          *node;
848     pa_muxnode        *mux;
849     pa_nodeset_resdef *resdef;
850     bool               loopback;
851     bool               remap = false;
852
853     pa_assert(u);
854     pa_assert(data);
855     pa_assert_se((core = u->core));
856     pa_assert_se((discover = u->discover));
857     pa_assert_se((multiplex = u->multiplex));
858     pa_assert_se((pl = data->proplist));
859
860     mnam = (m = data->module) ? m->name : "";
861
862     if (pa_streq(mnam, "module-combine-sink")) {
863         loopback = false;
864         type = mir_node_type_unknown;
865
866         if (!(mux  = pa_multiplex_find_by_module(multiplex, m)) ||
867             !(sink = pa_idxset_get_by_index(core->sinks, mux->sink_index)) ||
868             !(sinp = pa_idxset_first(sink->inputs, NULL)) ||
869             !(type = pa_utils_get_stream_class(sinp->proplist)))
870         {
871             pa_log_debug("can't figure out the type of multiplex stream");
872         }
873         else {
874             pa_utils_set_stream_routing_properties(data->proplist, type, NULL);
875         }
876     }
877     else {
878         loopback = pa_streq(mnam, "module-loopback");
879         remap = false;
880
881         if (loopback) {
882             if (!(node = pa_utils_get_node_from_data(u, mir_input, data))) {
883                 pa_log_debug("can't find loopback node for sink-input");
884                 return true;
885             }
886
887             if (node->direction == mir_output) {
888                 pa_log_debug("refuse to preroute loopback sink-input "
889                              "(current route: sink %u @ %p)", data->sink ?
890                              data->sink->index : PA_IDXSET_INVALID,data->sink);
891                 return true;
892             }
893
894             data->sink = NULL;
895
896             type = pa_classify_guess_stream_node_type(u, pl, NULL);
897         }
898         else {
899             remap = pa_streq(mnam, "module-remap-sink");
900             type = pa_classify_guess_stream_node_type(u, pl, &resdef);
901
902             pa_utils_set_resource_properties(pl, resdef);
903
904             if (pa_stream_state_start_corked(u, data, resdef)) {
905                 pa_log_debug("start corked");
906             }
907         }
908
909         pa_utils_set_stream_routing_properties(pl, type, data->sink);
910     }
911
912     memset(&fake, 0, sizeof(fake));
913     fake.direction = mir_input;
914     fake.implement = mir_stream;
915     fake.type      = type;
916
917     if (!data->sink) {
918         fake.channels  = data->channel_map.channels;
919         fake.zone      = pa_utils_get_zone(data->proplist);
920         fake.visible   = true;
921         fake.available = true;
922         fake.amname    = "<preroute sink-input>";
923         fake.amid      = AM_ID_INVALID;
924         fake.paidx     = PA_IDXSET_INVALID;
925
926         role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
927         sink = make_output_prerouting(u, &fake, &data->channel_map, role,NULL);
928
929         if (sink) {
930 #if 0
931             if (fake.mux && !(data->flags & PA_SINK_INPUT_START_CORKED)) {
932                 data->flags |= PA_SINK_INPUT_START_CORKED;
933                 schedule_stream_uncorking(u, sink);
934             }
935 #endif
936
937             if (pa_sink_input_new_data_set_sink(data, sink, false))
938                 pa_log_debug("set sink %u for new sink-input", sink->index);
939             else {
940                 pa_log("can't set sink %u for new sink-input", sink->index);
941                                                       /* copes wit NULL mux */
942                 pa_multiplex_destroy(u->multiplex, core, fake.mux);
943                 return false;
944             }
945         }
946     }
947
948     if (remap) {
949         /* no ramp needed */
950         return true;
951     }
952     if (loopback && data->sink && data->sink->module) {
953         /* no ramp needed */
954         if (pa_streq(data->sink->module->name, "module-combine-sink"))
955             return true;
956     }
957
958     if (pa_classify_ramping_stream(&fake)) {
959         pa_log_debug("set sink-input ramp-muted");
960         data->flags |= PA_SINK_INPUT_START_RAMP_MUTED;
961     }
962
963     return true;
964 }
965
966
967 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
968 {
969     pa_core           *core;
970     pa_sink           *s;
971     pa_sink_input     *csinp;
972     pa_proplist       *pl;
973     pa_discover       *discover;
974     pa_multiplex      *multiplex;
975     mir_node           data;
976     mir_node          *node;
977     mir_node          *snod;
978     char              *name;
979     const char        *media;
980     mir_node_type      type;
981     char               key[256];
982     bool               created;
983     pa_muxnode        *mux;
984     pa_nodeset_resdef *resdef;
985     pa_nodeset_resdef  rdbuf;
986
987     pa_assert(u);
988     pa_assert(sinp);
989     pa_assert_se((core = u->core));
990     pa_assert_se((discover = u->discover));
991     pa_assert_se((multiplex = u->multiplex));
992     pa_assert_se((pl = sinp->proplist));
993
994     resdef = NULL;
995
996     if (!(media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)))
997         media = "<unknown>";
998
999     if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
1000         if (!pa_utils_stream_has_default_route(sinp->proplist) ||
1001             !(mux = pa_multiplex_find_by_module(multiplex, sinp->module)) ||
1002             mux->defstream_index != PA_IDXSET_INVALID)
1003         {
1004             pa_log_debug("New stream is a combine stream. Nothing to do ...");
1005         }
1006         else {
1007             pa_log_debug("New stream is a combine stream. Setting as default");
1008             mux->defstream_index = sinp->index;
1009             mir_router_make_routing(u);
1010         }
1011         return;
1012     } else if (!strncmp(media, loopback_outpatrn,sizeof(loopback_outpatrn)-1)){
1013         pa_log_debug("New stream is a loopback output stream");
1014
1015         if ((node = pa_utils_get_node_from_stream(u, mir_input, sinp))) {
1016             if (node->direction == mir_input)
1017                 pa_log_debug("loopback stream node '%s' found", node->amname);
1018             else {
1019                 pa_log_debug("ignoring it");
1020                 return;
1021             }
1022         }
1023         else {
1024             pa_log_debug("can't find node for the loopback stream");
1025             return;
1026         }
1027
1028         s = sinp->sink;
1029     }
1030     else {
1031         name = pa_utils_get_sink_input_name(sinp);
1032
1033         pa_log_debug("dealing with new input stream '%s'", name);
1034
1035         if ((type = get_stream_routing_class(pl)))
1036             resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1037         else {
1038             if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1039                 pa_log_debug("cant find stream class for '%s'. "
1040                              "Leaving it alone", name);
1041                 return;
1042             }
1043
1044             pa_utils_set_stream_routing_properties(pl, type, NULL);
1045
1046             /* if needed, make some post-routing here */
1047         }
1048
1049         /* we need to add this to main hashmap as that is used for loop
1050            through on all nodes. */
1051         snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
1052
1053         memset(&data, 0, sizeof(data));
1054         data.key       = key;
1055         data.direction = mir_input;
1056         data.implement = mir_stream;
1057         data.channels  = sinp->channel_map.channels;
1058         data.type      = type;
1059         data.zone      = pa_utils_get_zone(pl);
1060         data.visible   = true;
1061         data.available = true;
1062         data.amname    = name;
1063         data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1064         data.amid      = AM_ID_INVALID;
1065         data.paname    = name;
1066         data.paidx     = sinp->index;
1067         data.mux       = pa_multiplex_find_by_sink(u->multiplex,
1068                                                    sinp->sink->index);
1069         data.rsetid    = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1070         node = create_node(u, &data, &created);
1071
1072         pa_assert(node);
1073
1074         if (!created) {
1075             pa_log("%s: confused with stream. '%s' did exists",
1076                    __FILE__, node->amname);
1077             return;
1078         }
1079
1080         if (node->rsetid)
1081             pa_murphyif_add_node(u, node);
1082         else if (resdef)
1083             pa_murphyif_create_resource_set(u, node, resdef);
1084
1085         pa_discover_add_node_to_ptr_hash(u, sinp, node);
1086
1087         if (!data.mux)
1088             s = sinp->sink;
1089         else {
1090             csinp = pa_idxset_get_by_index(core->sink_inputs,
1091                                            data.mux->defstream_index);
1092             s = csinp ? csinp->sink : NULL;
1093
1094             if ((sinp->flags & PA_SINK_INPUT_START_RAMP_MUTED)) {
1095                 pa_log_debug("ramp '%s' to 100%", media);
1096                 pa_fader_ramp_volume(u, sinp, PA_VOLUME_NORM);
1097             }
1098         }
1099     }
1100
1101     if (s)
1102         pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1103
1104     if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1105         pa_log_debug("can't figure out where this stream is routed");
1106     else {
1107         pa_log_debug("register route '%s' => '%s'",
1108                      node->amname, snod->amname);
1109
1110         if (pa_utils_stream_has_default_route(sinp->proplist))
1111             pa_audiomgr_add_default_route(u, node, snod);
1112
1113         /* FIXME: register explicit routes */
1114         /* else pa_audiomgr_add/register_explicit_route() */
1115         
1116
1117         pa_fader_apply_volume_limits(u, pa_utils_get_stamp());
1118     }
1119 }
1120
1121
1122 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
1123 {
1124     pa_discover    *discover;
1125     mir_node       *node;
1126     mir_node       *sinknod;
1127     char           *name;
1128     bool       had_properties;
1129
1130     pa_assert(u);
1131     pa_assert(sinp);
1132     pa_assert_se((discover = u->discover));
1133
1134     name = pa_utils_get_sink_input_name(sinp);
1135
1136     pa_log_debug("sink-input '%s' going to be destroyed", name);
1137
1138     had_properties = pa_utils_unset_stream_routing_properties(sinp->proplist);
1139
1140     if (!(node = pa_discover_remove_node_from_ptr_hash(u, sinp))) {
1141         if (!pa_multiplex_sink_input_remove(u->multiplex, sinp))
1142             pa_log_debug("nothing to do for sink-input (name '%s')", name);
1143     }
1144     else {
1145         pa_log_debug("node found for '%s'. After clearing routes "
1146                      "it will be destroyed", name);
1147
1148         if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
1149             pa_log_debug("can't figure out where this stream is routed");
1150         else {
1151             pa_log_debug("clear route '%s' => '%s'",
1152                          node->amname, sinknod->amname);
1153
1154             /* FIXME: and actually do it ... */
1155
1156         }
1157
1158         destroy_node(u, node);
1159     }
1160
1161     if (node || had_properties)
1162         mir_router_make_routing(u);
1163 }
1164
1165
1166 void pa_discover_register_source_output(struct userdata  *u,
1167                                         pa_source_output *sout)
1168 {
1169     pa_core           *core;
1170     pa_discover       *discover;
1171     pa_proplist       *pl;
1172     char              *name;
1173     const char        *media;
1174     mir_node_type      type;
1175     mir_node           data;
1176     mir_node          *node;
1177     mir_node          *target;
1178     char               key[256];
1179     pa_source         *source;
1180     const char        *role;
1181     pa_nodeset_resdef *resdef;
1182
1183     pa_assert(u);
1184     pa_assert(sout);
1185     pa_assert_se((core = u->core));
1186     pa_assert_se((discover = u->discover));
1187     pa_assert_se((pl = sout->proplist));
1188
1189     if ((media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME))) {
1190         if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1191             pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
1192             return;
1193         }
1194     }
1195
1196     name = pa_utils_get_source_output_name(sout);
1197
1198     pa_log_debug("registering output stream '%s'", name);
1199
1200     if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1201         pa_log_debug("cant find stream class for '%s'. "
1202                      "Leaving it alone", name);
1203         return;
1204     }
1205
1206     pa_utils_set_stream_routing_properties(pl, type, NULL);
1207
1208     snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1209
1210     memset(&data, 0, sizeof(data));
1211     data.key       = key;
1212     data.direction = mir_output;
1213     data.implement = mir_stream;
1214     data.channels  = sout->channel_map.channels;
1215     data.type      = type;
1216     data.zone      = pa_utils_get_zone(sout->proplist);
1217     data.visible   = true;
1218     data.available = true;
1219     data.amname    = name;
1220     data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1221     data.amid      = AM_ID_INVALID;
1222     data.paname    = name;
1223     data.paidx     = sout->index;
1224     data.rsetid    = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1225
1226     /*
1227      * here we can't guess whether the application requested an explicit
1228      * route by sepcifying the target source @ stream creation time.
1229      *
1230      * the brute force solution: we make a default route for this stream
1231      * possibly overwiriting the orginal app request :(
1232      */
1233     role   = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_ROLE);
1234     source = make_input_prerouting(u, &data, role, &target);
1235
1236     node = create_node(u, &data, NULL);
1237     pa_assert(node);
1238     pa_discover_add_node_to_ptr_hash(u, sout, node);
1239
1240     if (source && target) {
1241         pa_log_debug("move stream to source %u (%s)",
1242                      source->index, source->name);
1243
1244         if (pa_source_output_move_to(sout, source, false) < 0)
1245             pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
1246         else {
1247             pa_log_debug("register route '%s' => '%s'",
1248                          node->amname, target->amname);
1249             /* FIXME: and actually do it ... */
1250         }
1251     }
1252 }
1253
1254 bool pa_discover_preroute_source_output(struct userdata *u,
1255                                         pa_source_output_new_data *data)
1256 {
1257     pa_core           *core;
1258     pa_module         *m;
1259     pa_proplist       *pl;
1260     pa_discover       *discover;
1261     mir_node           fake;
1262     pa_source         *source;
1263     const char        *mnam;
1264     const char        *role;
1265     mir_node_type      type;
1266     mir_node          *node;
1267     pa_nodeset_resdef *resdef;
1268
1269     pa_assert(u);
1270     pa_assert(data);
1271     pa_assert_se((core = u->core));
1272     pa_assert_se((discover = u->discover));
1273     pa_assert_se((pl = data->proplist));
1274
1275     mnam = (m = data->module) ? m->name : "";
1276
1277     if (pa_streq(mnam, "module-loopback")) {
1278         if (!(node = pa_utils_get_node_from_data(u, mir_output, data))) {
1279             pa_log_debug("can't find loopback node for source-output");
1280             return true;
1281         }
1282
1283         if (node->direction == mir_input) {
1284             pa_log_debug("refuse to preroute loopback source-output "
1285                          "(current route: source %u @ %p)", data->source ?
1286                          data->source->index : PA_IDXSET_INVALID,data->source);
1287             return true;
1288         }
1289
1290         data->source = NULL;
1291
1292         type = pa_classify_guess_stream_node_type(u, pl, NULL);
1293     }
1294     else {
1295         type = pa_classify_guess_stream_node_type(u, pl, &resdef);
1296
1297         pa_utils_set_resource_properties(pl, resdef);
1298     }
1299
1300     pa_utils_set_stream_routing_properties(pl, type, data->source);
1301
1302     if (!data->source) {
1303         memset(&fake, 0, sizeof(fake));
1304         fake.direction = mir_output;
1305         fake.implement = mir_stream;
1306         fake.channels  = data->channel_map.channels;
1307         fake.type      = type;
1308         fake.zone      = pa_utils_get_zone(data->proplist);
1309         fake.visible   = true;
1310         fake.available = true;
1311         fake.amname    = "<preroute source-output>";
1312
1313         role   = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
1314         source = make_input_prerouting(u, &fake, role, NULL);
1315
1316         if (source) {
1317             if (pa_source_output_new_data_set_source(data, source, false)) {
1318                 pa_log_debug("set source %u for new source-output",
1319                              source->index);
1320             }
1321             else {
1322                 pa_log("can't set source %u for new source-output",
1323                        source->index);
1324             }
1325         }
1326     }
1327
1328     return true;
1329 }
1330
1331
1332 void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
1333 {
1334     pa_core           *core;
1335     pa_source         *s;
1336     pa_proplist       *pl;
1337     pa_discover       *discover;
1338     mir_node           data;
1339     mir_node          *node;
1340     mir_node          *snod;
1341     char              *name;
1342     const char        *media;
1343     mir_node_type      type;
1344     char               key[256];
1345     bool          created;
1346     pa_nodeset_resdef *resdef;
1347     pa_nodeset_resdef  rdbuf;
1348
1349     pa_assert(u);
1350     pa_assert(sout);
1351     pa_assert_se((core = u->core));
1352     pa_assert_se((discover = u->discover));
1353     pa_assert_se((pl = sout->proplist));
1354
1355     resdef = NULL;
1356
1357     if (!(media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME)))
1358         media = "<unknown>";
1359
1360     if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1361         pa_log_debug("New stream is a loopback input stream");
1362
1363         if ((node = pa_utils_get_node_from_stream(u, mir_output, sout))) {
1364             if (node->direction == mir_output)
1365                 pa_log_debug("loopback stream node '%s' found", node->amname);
1366             else {
1367                 pa_log_debug("ignoring it");
1368                 return;
1369             }
1370         }
1371         else {
1372             pa_log_debug("can't find node for the loopback stream");
1373             return;
1374         }
1375     }
1376     else {
1377         name = pa_utils_get_source_output_name(sout);
1378
1379         pa_log_debug("dealing with new output stream '%s'", name);
1380
1381         if ((type = get_stream_routing_class(pl)))
1382             resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1383         else {
1384             if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1385                 pa_log_debug("cant find stream class for '%s'. "
1386                              "Leaving it alone", name);
1387                 return;
1388             }
1389
1390             pa_utils_set_stream_routing_properties(pl, type, NULL);
1391
1392             /* if needed, make some post-routing here */
1393         }
1394
1395         /* we need to add this to main hashmap as that is used for loop
1396            through on all nodes. */
1397         snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1398
1399         memset(&data, 0, sizeof(data));
1400         data.key       = key;
1401         data.direction = mir_output;
1402         data.implement = mir_stream;
1403         data.channels  = sout->channel_map.channels;
1404         data.type      = type;
1405         data.zone      = pa_utils_get_zone(pl);
1406         data.visible   = true;
1407         data.available = true;
1408         data.amname    = name;
1409         data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1410         data.amid      = AM_ID_INVALID;
1411         data.paname    = name;
1412         data.paidx     = sout->index;
1413         data.rsetid    = (char *)pa_proplist_gets(pl, PA_PROP_RESOURCE_SET_ID);
1414
1415         node = create_node(u, &data, &created);
1416
1417         pa_assert(node);
1418
1419         if (!created) {
1420             pa_log("%s: confused with stream. '%s' did exists",
1421                    __FILE__, node->amname);
1422             return;
1423         }
1424
1425         if (node->rsetid)
1426             pa_murphyif_add_node(u, node);
1427         else if (resdef)
1428             pa_murphyif_create_resource_set(u, node, resdef);
1429
1430         pa_discover_add_node_to_ptr_hash(u, sout, node);
1431     }
1432
1433     if ((s = sout->source))
1434         pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1435
1436     if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1437         pa_log_debug("can't figure out where this stream is routed");
1438     else {
1439         pa_log_debug("register route '%s' => '%s'",
1440                      snod->amname, node->amname);
1441         pa_audiomgr_add_default_route(u, node, snod);
1442     }
1443 }
1444
1445
1446 void pa_discover_remove_source_output(struct userdata  *u,
1447                                       pa_source_output *sout)
1448 {
1449     pa_discover    *discover;
1450     mir_node       *node;
1451     mir_node       *srcnod;
1452     char           *name;
1453
1454     pa_assert(u);
1455     pa_assert(sout);
1456     pa_assert_se((discover = u->discover));
1457
1458     name = pa_utils_get_source_output_name(sout);
1459
1460     pa_log_debug("source-output '%s' going to be destroyed", name);
1461
1462     if (!(node = pa_discover_remove_node_from_ptr_hash(u, sout)))
1463         pa_log_debug("can't find node for source-output (name '%s')", name);
1464     else {
1465         pa_log_debug("node found for '%s'. After clearing routes "
1466                      "it will be destroyed", name);
1467
1468         if (!(srcnod = pa_hashmap_get(discover->nodes.byptr, sout->source)))
1469             pa_log_debug("can't figure out where this stream is routed");
1470         else {
1471             pa_log_debug("clear route '%s' => '%s'",
1472                          node->amname, srcnod->amname);
1473
1474             /* FIXME: and actually do it ... */
1475
1476         }
1477
1478         destroy_node(u, node);
1479
1480         mir_router_make_routing(u);
1481     }
1482 }
1483
1484
1485 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
1486 {
1487     pa_discover *discover;
1488     mir_node    *node;
1489
1490     pa_assert(u);
1491     pa_assert_se((discover = u->discover));
1492
1493     if (key)
1494         node = pa_hashmap_get(discover->nodes.byname, key);
1495     else
1496         node = NULL;
1497
1498     return node;
1499 }
1500
1501 mir_node *pa_discover_find_node_by_ptr(struct userdata *u, void *ptr)
1502 {
1503     pa_discover *discover;
1504     mir_node    *node;
1505
1506     pa_assert(u);
1507     pa_assert_se((discover = u->discover));
1508
1509     if (ptr)
1510         node = pa_hashmap_get(discover->nodes.byptr, ptr);
1511     else
1512         node = NULL;
1513
1514     return node;
1515 }
1516
1517 void pa_discover_add_node_to_ptr_hash(struct userdata *u,
1518                                       void *ptr,
1519                                       mir_node *node)
1520 {
1521     pa_discover *discover;
1522
1523     pa_assert(u);
1524     pa_assert(ptr);
1525     pa_assert(node);
1526     pa_assert_se((discover = u->discover));
1527
1528     pa_hashmap_put(discover->nodes.byptr, ptr, node);
1529 }
1530
1531 mir_node *pa_discover_remove_node_from_ptr_hash(struct userdata *u, void *ptr)
1532 {
1533     pa_discover *discover;
1534
1535     pa_assert(u);
1536     pa_assert(ptr);
1537     pa_assert_se((discover = u->discover));
1538
1539     return pa_hashmap_remove(discover->nodes.byptr, ptr);
1540 }
1541
1542
1543 static void handle_alsa_card(struct userdata *u, pa_card *card)
1544 {
1545     mir_node    data;
1546     const char *udd;
1547     char       *cnam;
1548     char       *cid;
1549
1550     memset(&data, 0, sizeof(data));
1551     data.zone = pa_utils_get_zone(card->proplist);
1552     data.visible = true;
1553     data.amid = AM_ID_INVALID;
1554     data.implement = mir_device;
1555     data.paidx = PA_IDXSET_INVALID;
1556     data.stamp = pa_utils_get_stamp();
1557
1558     cnam = pa_utils_get_card_name(card);
1559     udd  = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
1560
1561     if (udd && pa_streq(udd, "1")) {
1562         /* udev loaded alsa card */
1563         if (!strncmp(cnam, "alsa_card.", 10)) {
1564             cid = cnam + 10;
1565             handle_udev_loaded_card(u, card, &data, cid);
1566             return;
1567         }
1568     }
1569     else {
1570         /* statically loaded pci or usb card */
1571     }
1572
1573     pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
1574 }
1575
1576
1577 #if 0
1578 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1579 {
1580     pa_discover     *discover;
1581     pa_card_profile *prof;
1582     mir_node         data;
1583     mir_node        *node;
1584     mir_constr_def  *cd;
1585     char            *cnam;
1586     char            *cid;
1587     const char      *cdescr;
1588     void            *state;
1589     char             paname[MAX_NAME_LENGTH+1];
1590     char             amname[MAX_NAME_LENGTH+1];
1591     char             key[MAX_NAME_LENGTH+1];
1592
1593     pa_assert_se((discover = u->discover));
1594
1595     cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1596
1597
1598     memset(paname, 0, sizeof(paname));
1599     memset(amname, 0, sizeof(amname));
1600     memset(key   , 0, sizeof(key)   );
1601
1602     memset(&data, 0, sizeof(data));
1603     data.key = key;
1604     data.visible = true;
1605     data.amid = AM_ID_INVALID;
1606     data.implement = mir_device;
1607     data.paidx = PA_IDXSET_INVALID;
1608     data.paname = paname;
1609     data.amname = amname;
1610     data.amdescr = (char *)cdescr;
1611     data.pacard.index = card->index;
1612     data.stamp = pa_utils_get_stamp();
1613
1614     cnam = pa_utils_get_card_name(card);
1615
1616     if (!strncmp(cnam, "bluez_card.", 11)) {
1617         cid = cnam + 11;
1618
1619         pa_assert(card->ports);
1620
1621         cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1622
1623         PA_HASHMAP_FOREACH(prof, card->profiles, cstate) {
1624             data.available = false;
1625             data.pacard.profile = prof->name;
1626
1627             if (prof->n_sinks > 0) {
1628                 data.direction = mir_output;
1629                 data.channels = prof->max_sink_channels;
1630                 data.amname = amname;
1631                 amname[0] = '\0';
1632                 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1633                 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1634                 pa_classify_node_by_card(&data, card, prof, NULL);
1635                 node = create_node(u, &data, NULL);
1636                 mir_constrain_add_node(u, cd, node);
1637             }
1638
1639             if (prof->n_sources > 0) {
1640                 data.direction = mir_input;
1641                 data.channels = prof->max_source_channels;
1642                 data.amname = amname;
1643                 amname[0] = '\0';
1644                 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1645                 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1646                 pa_classify_node_by_card(&data, card, prof, NULL);
1647                 node = create_node(u, &data, NULL);
1648                 mir_constrain_add_node(u, cd, node);
1649             }
1650         }
1651
1652         if (!(prof = card->active_profile))
1653             pa_log("card '%s' has no active profile", card->name);
1654         else {
1655             pa_log_debug("card '%s' default profile '%s'",
1656                          card->name, prof->name);
1657         }
1658
1659         schedule_card_check(u, card);
1660     }
1661 }
1662 #endif
1663
1664
1665 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1666 {
1667     pa_discover     *discover;
1668     pa_card_profile *prof;
1669     pa_device_port  *port;
1670     mir_node         data;
1671     mir_node        *node;
1672     mir_constr_def  *cd;
1673     char            *cnam;
1674     char            *cid;
1675     const char      *cdescr;
1676     void            *state0, *state1;
1677     char             paname[MAX_NAME_LENGTH+1];
1678     char             amname[MAX_NAME_LENGTH+1];
1679     char             key[MAX_NAME_LENGTH+1];
1680     int              len;
1681     bool        input;
1682     bool        output;
1683
1684     pa_assert_se((discover = u->discover));
1685
1686     cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1687
1688
1689     memset(paname, 0, sizeof(paname));
1690     memset(amname, 0, sizeof(amname));
1691     memset(key   , 0, sizeof(key)   );
1692
1693     memset(&data, 0, sizeof(data));
1694     data.key = key;
1695     data.zone = pa_utils_get_zone(card->proplist);
1696     data.visible = true;
1697     data.amid = AM_ID_INVALID;
1698     data.implement = mir_device;
1699     data.paidx = PA_IDXSET_INVALID;
1700     data.paname = paname;
1701     data.amname = amname;
1702     data.amdescr = (char *)cdescr;
1703     data.pacard.index = card->index;
1704     data.stamp = pa_utils_get_stamp();
1705
1706     cnam = pa_utils_get_card_name(card);
1707
1708     if (!strncmp(cnam, "bluez_card.", 11)) {
1709         cid = cnam + 11;
1710
1711         pa_assert(card->ports);
1712
1713         cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1714
1715         PA_HASHMAP_FOREACH(port, card->ports, state0) {
1716             pa_assert(port->profiles);
1717
1718
1719             input = output = true;
1720             len = strlen(port->name);
1721             if (len >= 6 && !strcmp("-input", port->name + (len-6)))
1722                 output = false;
1723             else if (len >= 7 && !strcmp("-output", port->name + (len-7)))
1724                 input  = false;
1725             
1726
1727             PA_HASHMAP_FOREACH(prof, port->profiles, state1) {
1728                 data.pacard.profile = prof->name;
1729                 data.available = get_bluetooth_port_availability(&data, port);
1730
1731                 if (output && prof->n_sinks > 0) {
1732                     data.direction = mir_output;
1733                     data.channels = prof->max_sink_channels;
1734                     data.amname = amname;
1735                     amname[0] = '\0';
1736                     snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1737                     snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1738                     pa_classify_node_by_card(&data, card, prof, NULL);
1739                     node = create_node(u, &data, NULL);
1740                     mir_constrain_add_node(u, cd, node);
1741                     pa_utils_set_port_properties(port, node);
1742                 }
1743
1744                 if (input && prof->n_sources > 0) {
1745                     data.direction = mir_input;
1746                     data.channels = prof->max_source_channels;
1747                     data.amname = amname;
1748                     amname[0] = '\0';
1749                     snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1750                     snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1751                     pa_classify_node_by_card(&data, card, prof, NULL);
1752                     node = create_node(u, &data, NULL);
1753                     mir_constrain_add_node(u, cd, node);
1754                     pa_utils_set_port_properties(port, node);
1755                 }
1756             }
1757         }
1758
1759         if (!(prof = card->active_profile))
1760             pa_log("card '%s' has no active profile", card->name);
1761         else {
1762             pa_log_debug("card '%s' default profile '%s'",
1763                          card->name, prof->name);
1764         }
1765
1766         schedule_card_check(u, card);
1767     }
1768 }
1769
1770 static bool get_bluetooth_port_availability(mir_node *node,
1771                                                  pa_device_port *port)
1772 {
1773     bool available = false;
1774     const char *prof;
1775
1776     pa_assert(node);
1777     pa_assert(port);
1778
1779     if ((prof = node->pacard.profile)) {
1780         if (!strcmp(prof, "hfgw")        ||
1781             !strcmp(prof, "a2dp_source") ||
1782             !strcmp(prof, "a2dp_sink"))
1783             available = (port->available != PA_AVAILABLE_NO);
1784         else
1785             available = true;
1786     }
1787
1788     return available;
1789 }
1790
1791 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
1792                                     mir_node *data, char *cardid)
1793 {
1794     pa_discover      *discover;
1795     pa_card_profile  *prof;
1796     pa_card_profile  *active;
1797     void             *state;
1798     const char       *alsanam;
1799     char             *sid;
1800     char             *sinks[MAX_CARD_TARGET+1];
1801     char             *sources[MAX_CARD_TARGET+1];
1802     char              buf[MAX_NAME_LENGTH+1];
1803     char              paname[MAX_NAME_LENGTH+1];
1804     char              amname[MAX_NAME_LENGTH+1];
1805     int               i;
1806
1807     pa_assert(card);
1808     pa_assert(card->profiles);
1809     pa_assert_se((discover = u->discover));
1810
1811     alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
1812
1813     memset(amname, 0, sizeof(amname));
1814
1815     data->paname  = paname;
1816     data->amname  = amname;
1817     data->amdescr = (char *)alsanam;
1818
1819     data->pacard.index = card->index;
1820
1821     active = card->active_profile;
1822
1823     PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1824         /* filtering: deal with selected profiles if requested so */
1825         if (discover->selected && (!active || (active && prof != active)))
1826             continue;
1827
1828         /* filtering: skip the 'off' profiles */
1829         if (!prof->n_sinks && !prof->n_sources)
1830             continue;
1831
1832         /* filtering: consider sinks with suitable amount channels */
1833         if (prof->n_sinks &&
1834             (prof->max_sink_channels < discover->chmin ||
1835              prof->max_sink_channels  > discover->chmax  ))
1836             continue;
1837
1838         /* filtering: consider sources with suitable amount channels */
1839         if (prof->n_sources &&
1840             (prof->max_source_channels <  discover->chmin ||
1841              prof->max_source_channels >  discover->chmax   ))
1842             continue;
1843
1844         data->pacard.profile = prof->name;
1845
1846         parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
1847
1848         data->direction = mir_output;
1849         data->channels = prof->max_sink_channels;
1850         for (i = 0;  (sid = sinks[i]);  i++) {
1851             snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
1852             handle_card_ports(u, data, card, prof);
1853         }
1854
1855         data->direction = mir_input;
1856         data->channels = prof->max_source_channels;
1857         for (i = 0;  (sid = sources[i]);  i++) {
1858             snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
1859             handle_card_ports(u, data, card, prof);
1860         }
1861     }
1862 }
1863
1864
1865 static void handle_card_ports(struct userdata *u, mir_node *data,
1866                               pa_card *card, pa_card_profile *prof)
1867 {
1868     mir_node       *node = NULL;
1869     bool       have_ports = false;
1870     mir_constr_def *cd = NULL;
1871     char           *amname = data->amname;
1872     pa_device_port *port;
1873     void           *state;
1874     bool       created;
1875     char            key[MAX_NAME_LENGTH+1];
1876
1877     pa_assert(u);
1878     pa_assert(data);
1879     pa_assert(card);
1880     pa_assert(prof);
1881
1882     if (card->ports) {
1883         PA_HASHMAP_FOREACH(port, card->ports, state) {
1884             /*
1885              * if this port did not belong to any profile
1886              * (ie. prof->profiles == NULL) we assume that this port
1887              * does works with all the profiles
1888              */
1889             if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
1890                 ((port->direction == PA_DIRECTION_INPUT && data->direction == mir_input)||
1891                  (port->direction == PA_DIRECTION_OUTPUT && data->direction == mir_output)))
1892             {
1893                 have_ports = true;
1894
1895                 amname[0] = '\0';
1896                 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
1897
1898                 data->key       = key;
1899                 data->available = (port->available != PA_AVAILABLE_NO);
1900                 data->type      = 0;
1901                 data->amname    = amname;
1902                 data->paport    = port->name;
1903
1904                 pa_classify_node_by_card(data, card, prof, port);
1905
1906                 node = create_node(u, data, &created);
1907
1908                 if (!created)
1909                     node->stamp = data->stamp;
1910                 else {
1911                     cd = mir_constrain_create(u, "port", mir_constrain_port,
1912                                               data->paname);
1913                     mir_constrain_add_node(u, cd, node);
1914                 }
1915             }
1916         }
1917     }
1918
1919     if (!have_ports) {
1920         data->key = data->paname;
1921         data->available = true;
1922
1923         pa_classify_node_by_card(data, card, prof, NULL);
1924
1925         node = create_node(u, data, &created);
1926
1927         if (!created)
1928             node->stamp = data->stamp;
1929     }
1930
1931     data->amname = amname;
1932     *amname = '\0';
1933 }
1934
1935
1936 static mir_node *create_node(struct userdata *u, mir_node *data,
1937                              bool *created_ret)
1938 {
1939     pa_discover *discover;
1940     mir_node    *node;
1941     bool    created;
1942     char         buf[2048];
1943
1944     pa_assert(u);
1945     pa_assert(data);
1946     pa_assert(data->key);
1947     pa_assert(data->paname);
1948     pa_assert_se((discover = u->discover));
1949
1950     if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
1951         created = false;
1952     else {
1953         created = true;
1954
1955         node = mir_node_create(u, data);
1956         pa_hashmap_put(discover->nodes.byname, node->key, node);
1957
1958         mir_node_print(node, buf, sizeof(buf));
1959         pa_log_debug("new node:\n%s", buf);
1960
1961         if (node->available)
1962             pa_audiomgr_register_node(u, node);
1963     }
1964
1965     if (created_ret)
1966         *created_ret = created;
1967
1968     return node;
1969 }
1970
1971 static void destroy_node(struct userdata *u, mir_node *node)
1972 {
1973     pa_discover *discover;
1974     mir_node    *removed;
1975
1976     pa_assert(u);
1977     pa_assert_se((discover = u->discover));
1978
1979     if (node) {
1980         removed = pa_hashmap_remove(discover->nodes.byname, node->key);
1981
1982         if (node != removed) {
1983             if (removed)
1984                 pa_log("%s: confused with data structures: key mismatch. "
1985                        " attempted to destroy '%s'; actually destroyed '%s'",
1986                        __FILE__, node->key, removed->key);
1987             else
1988                 pa_log("%s: confused with data structures: node '%s' "
1989                        "is not in the hash table", __FILE__, node->key);
1990             return;
1991         }
1992
1993         pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
1994
1995         if (node->implement == mir_stream) {
1996             if (node->direction == mir_input) {
1997                 if (node->mux) {
1998                     pa_log_debug("removing multiplexer");
1999                 }
2000             }
2001         }
2002
2003         pa_audiomgr_unregister_node(u, node);
2004
2005         extapi_signal_node_change(u);
2006
2007         mir_constrain_remove_node(u, node);
2008
2009         pa_loopback_destroy(u->loopback, u->core, node->loop);
2010         pa_multiplex_destroy(u->multiplex, u->core, node->mux);
2011
2012         mir_node_destroy(u, node);
2013     }
2014 }
2015
2016 static bool update_node_availability(struct userdata *u,
2017                                           mir_node *node,
2018                                           bool available)
2019 {
2020     pa_assert(u);
2021     pa_assert(node);
2022
2023     if ((!available &&  node->available) ||
2024         ( available && !node->available)  )
2025     {
2026         node->available = available;
2027
2028         if (available)
2029             pa_audiomgr_register_node(u, node);
2030         else
2031             pa_audiomgr_unregister_node(u, node);
2032
2033         extapi_signal_node_change(u);
2034
2035         return true; /* routing needed */
2036     }
2037
2038     return false;
2039 }
2040
2041 static bool update_node_availability_by_device(struct userdata *u,
2042                                                     mir_direction direction,
2043                                                     void *data,
2044                                                     pa_device_port *port,
2045                                                     bool available)
2046 {
2047     mir_node *node;
2048     char     *key;
2049     char      buf[256];
2050
2051     pa_assert(u);
2052     pa_assert(data);
2053     pa_assert(port);
2054     pa_assert(direction == mir_input || direction == mir_output);
2055
2056     if ((key = node_key(u, direction, data, port, buf, sizeof(buf)))) {
2057         if (!(node = pa_discover_find_node_by_key(u, key)))
2058             pa_log_debug("      can't find node (key '%s')", key);
2059         else {
2060             pa_log_debug("      node for '%s' found (key %s)",
2061                          node->paname, node->key);
2062
2063             return update_node_availability(u, node, available);
2064         }
2065     }
2066
2067     return false; /* no routing needed */
2068 }
2069
2070 static char *get_name(char **string_ptr, int offs)
2071 {
2072     char c, *name, *end;
2073
2074     name = *string_ptr + offs;
2075
2076     for (end = name;  (c = *end);   end++) {
2077         if (c == '+') {
2078             *end++ = '\0';
2079             break;
2080         }
2081     }
2082
2083     *string_ptr = end;
2084
2085     return name;
2086 }
2087
2088 static void parse_profile_name(pa_card_profile *prof,
2089                                char           **sinks,
2090                                char           **sources,
2091                                char            *buf,
2092                                int              buflen)
2093 {
2094     char *p = buf;
2095     int   i = 0;
2096     int   j = 0;
2097
2098     pa_assert(prof->name);
2099
2100     strncpy(buf, prof->name, buflen);
2101     buf[buflen-1] = '\0';
2102
2103     memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2104     memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2105
2106     do {
2107         if (!strncmp(p, "output:", 7)) {
2108             if (i >= MAX_CARD_TARGET) {
2109                 pa_log_debug("number of outputs exeeds the maximum %d in "
2110                              "profile name '%s'", MAX_CARD_TARGET, prof->name);
2111                 return;
2112             }
2113             sinks[i++] = get_name(&p, 7);
2114         }
2115         else if (!strncmp(p, "input:", 6)) {
2116             if (j >= MAX_CARD_TARGET) {
2117                 pa_log_debug("number of inputs exeeds the maximum %d in "
2118                              "profile name '%s'", MAX_CARD_TARGET, prof->name);
2119                 return;
2120             }
2121             sources[j++] = get_name(&p, 6);
2122         }
2123         else {
2124             pa_log("%s: failed to parse profile name '%s'",
2125                    __FILE__, prof->name);
2126             return;
2127         }
2128     } while (*p);
2129 }
2130
2131
2132 static char *node_key(struct userdata *u, mir_direction direction,
2133                       void *data, pa_device_port *port, char *buf, size_t len)
2134 {
2135     pa_card         *card;
2136     pa_card_profile *profile;
2137     const char      *bus;
2138     bool        pci;
2139     bool        usb;
2140     bool        bluetooth;
2141     bool        platform;
2142     char            *type;
2143     char            *name;
2144     const char      *profile_name;
2145     char            *key;
2146
2147     pa_assert(u);
2148     pa_assert(data);
2149     pa_assert(buf);
2150     pa_assert(direction == mir_input || direction == mir_output);
2151
2152     if (direction == mir_output) {
2153         pa_sink *sink = data;
2154         type = "sink";
2155         name = pa_utils_get_sink_name(sink);
2156         card = sink->card;
2157         if (!port)
2158             port = sink->active_port;
2159     }
2160     else {
2161         pa_source *source = data;
2162         type = "source";
2163         name = pa_utils_get_source_name(source);
2164         card = source->card;
2165         if (!port)
2166             port = source->active_port;
2167     }
2168
2169     if (!card)
2170         return NULL;
2171
2172     pa_assert_se((profile = card->active_profile));
2173
2174     if (!u->state.profile)
2175         profile_name = profile->name;
2176     else {
2177         pa_log_debug("state.profile is not null. '%s' supresses '%s'",
2178                      u->state.profile, profile->name);
2179         profile_name = u->state.profile;
2180     }
2181
2182
2183     if (!(bus = pa_utils_get_card_bus(card))) {
2184         pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
2185                      "on its card", type, name, PA_PROP_DEVICE_BUS);
2186         return NULL;
2187     }
2188
2189     pci = pa_streq(bus, "pci");
2190     usb = pa_streq(bus, "usb");
2191     platform = pa_streq(bus, "platform");
2192     bluetooth = pa_streq(bus, "bluetooth");
2193
2194     if (!pci && !usb && !bluetooth && !platform) {
2195         pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
2196                      "of its card", type, name, bus);
2197         return NULL;
2198     }
2199
2200     if (bluetooth) {
2201         if (!port)
2202             key = NULL;
2203         else {
2204             key = buf;
2205             snprintf(buf, len, "%s@%s.%s", name, port->name, profile_name);
2206         }
2207     }
2208     else {
2209         if (!port)
2210             key = name;
2211         else {
2212             key = buf;
2213             snprintf(buf, len, "%s@%s", name, port->name);
2214         }
2215     }
2216
2217     return key;
2218 }
2219
2220 static pa_sink *make_output_prerouting(struct userdata *u,
2221                                        mir_node        *data,
2222                                        pa_channel_map  *chmap,
2223                                        const char      *media_role,
2224                                        mir_node       **target_ret)
2225 {
2226     pa_core    *core;
2227     mir_node   *target;
2228     pa_sink    *sink = NULL;
2229
2230     pa_assert(u);
2231     pa_assert(data);
2232     pa_assert(chmap);
2233     pa_assert_se((core = u->core));
2234
2235
2236     target = mir_router_make_prerouting(u, data);
2237
2238     if (!target)
2239         pa_log("there is no default route for the stream '%s'", data->amname);
2240     else if (target->paidx == PA_IDXSET_INVALID)
2241         pa_log("can't route to default '%s': no sink", target->amname);
2242     else {
2243         if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
2244             pa_log("no route to default '%s': sink is gone", target->amname);
2245         else {
2246             if (pa_classify_multiplex_stream(data)) {
2247                 data->mux = pa_multiplex_create(u->multiplex, core,
2248                                                 sink->index, chmap, NULL,
2249                                                 media_role, data->type);
2250                 if (data->mux) {
2251                     sink = pa_idxset_get_by_index(core->sinks,
2252                                                   data->mux->sink_index);
2253                     pa_assert(sink);
2254                 }
2255             }
2256         }
2257     }
2258
2259     if (target_ret)
2260         *target_ret = target;
2261
2262     return sink;
2263 }
2264
2265
2266 static pa_source *make_input_prerouting(struct userdata *u,
2267                                         mir_node        *data,
2268                                         const char      *media_role,
2269                                         mir_node       **target_ret)
2270 {
2271     pa_core    *core;
2272     mir_node   *target;
2273     pa_source  *source = NULL;
2274
2275     pa_assert(u);
2276     pa_assert(data);
2277     pa_assert_se((core = u->core));
2278
2279     target = mir_router_make_prerouting(u, data);
2280
2281     if (!target)
2282         pa_log("there is no default route for the stream '%s'", data->amname);
2283     else if (target->paidx == PA_IDXSET_INVALID)
2284         pa_log("can't route to default '%s': no source", target->amname);
2285     else {
2286         if (!(source = pa_idxset_get_by_index(core->sources, target->paidx)))
2287             pa_log("no route to default '%s': source is gone",target->amname);
2288     }
2289
2290     if (target_ret)
2291         *target_ret = target;
2292
2293     return source;
2294 }
2295
2296 static mir_node_type get_stream_routing_class(pa_proplist *pl)
2297 {
2298     mir_node_type t;
2299
2300     pa_assert(pl);
2301
2302     t = pa_utils_get_stream_class(pl);
2303
2304     if (t >= mir_application_class_begin && t <  mir_application_class_end)
2305         return t;
2306
2307     return mir_node_type_unknown;
2308 }
2309
2310
2311 static void set_bluetooth_profile(struct userdata *u,
2312                                   pa_card *card,
2313                                   pa_direction_t direction)
2314 {
2315     pa_core *core;
2316     pa_device_port *port;
2317     pa_card_profile *prof, *make_active;
2318     void *state0, *state1;
2319     bool port_available;
2320     bool switch_off;
2321     int nport;
2322
2323     pa_assert(u);
2324     pa_assert(card);
2325     pa_assert_se((core = u->core));
2326
2327     make_active = NULL;
2328     switch_off = false;
2329     nport = 0;
2330
2331     pa_log_debug("which profile to make active:");
2332
2333     PA_HASHMAP_FOREACH(prof, card->profiles, state0) {
2334         if (!prof->n_sinks && !prof->n_sources) {
2335             if (!make_active) {
2336                 pa_log_debug("   considering %s", prof->name);
2337                 make_active = prof;
2338                 switch_off = true;
2339             }
2340         }
2341         else {
2342             port_available = false;
2343
2344             PA_HASHMAP_FOREACH(port, card->ports, state1) {
2345                 if ((direction & port->direction) &&
2346                     pa_hashmap_get(port->profiles, prof->name))
2347                 {
2348                     port_available = (port->available != PA_AVAILABLE_NO);
2349                     break;
2350                 }
2351             }
2352
2353             if (!port_available)
2354                 pa_log_debug("   ruling out %s (port not available)", prof->name);
2355             else if (prof->available != PA_AVAILABLE_YES)
2356                 pa_log_debug("   ruling out %s (profile not available)", prof->name);
2357             else {
2358                 nport++;
2359
2360                 if (((direction & PA_DIRECTION_INPUT)  && prof->n_sources > 0) ||
2361                     ((direction & PA_DIRECTION_OUTPUT) && prof->n_sinks   > 0)   ) {
2362                     if (make_active && prof->priority < make_active->priority)
2363                         pa_log_debug("   ruling out %s (low priority)", prof->name);
2364                     else {
2365                         pa_log_debug("   considering %s", prof->name);
2366                         make_active = prof;
2367                     }
2368                 }
2369                 else {
2370                     pa_log_debug("   ruling out %s (direction)", prof->name);
2371                 }
2372             }
2373         }
2374     }
2375
2376     if (!make_active)
2377         pa_log_debug("No suitable profile found. Frustrated and do nothing");
2378     else {
2379         if (make_active == card->active_profile)
2380             pa_log_debug("Profile %s already set. Do nothing", make_active->name);
2381         else {
2382             if (switch_off && nport) {
2383                 pa_log_debug("Do not switch to %s as active ports are existing "
2384                              "to the other direction", make_active->name);
2385             }
2386             else {
2387                 pa_log_debug("Set profile %s", make_active->name);
2388
2389                 if ((prof = pa_hashmap_get(card->profiles, make_active->name)) != NULL && 
2390                     pa_card_set_profile(card, prof, false) < 0) {
2391                     pa_log_debug("Failed to change profile to %s",
2392                                  make_active->name);
2393                 }
2394             }
2395         }
2396     }
2397 }
2398
2399 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
2400 {
2401     struct userdata *u = d;
2402
2403     (void)m;
2404
2405     pa_assert(u);
2406
2407     pa_log_debug("deferred routing starts");
2408
2409     mir_router_make_routing(u);
2410 }
2411
2412
2413 static void schedule_deferred_routing(struct userdata *u)
2414 {
2415     pa_core *core;
2416
2417     pa_assert(u);
2418     pa_assert_se((core = u->core));
2419
2420     pa_log_debug("scheduling deferred routing");
2421
2422     pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
2423 }
2424
2425
2426 static void card_check_cb(pa_mainloop_api *m, void *d)
2427 {
2428     card_check_t *cc = d;
2429     struct userdata *u;
2430     pa_core *core;
2431     pa_card *card;
2432     pa_sink *sink;
2433     pa_source *source;
2434     int n_sink, n_source;
2435     uint32_t idx;
2436
2437     (void)m;
2438
2439     pa_assert(cc);
2440     pa_assert((u = cc->u));
2441     pa_assert((core = u->core));
2442
2443     pa_log_debug("card check starts");
2444
2445     if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
2446         pa_log_debug("card %u is gone", cc->index);
2447     else {
2448         n_sink = n_source = 0;
2449
2450         PA_IDXSET_FOREACH(sink, core->sinks, idx) {
2451             if ((sink->card) && sink->card->index == card->index)
2452                 n_sink++;
2453         }
2454
2455         PA_IDXSET_FOREACH(source, core->sources, idx) {
2456             if ((source->card) && source->card->index == card->index)
2457                 n_sink++;
2458         }
2459
2460         if (n_sink || n_source) {
2461             pa_log_debug("found %u sinks and %u sources belonging to "
2462                          "'%s' card", n_sink, n_source, card->name);
2463             pa_log_debug("nothing to do");
2464         }
2465         else {
2466             pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
2467                          card->name);
2468             mir_router_make_routing(u);
2469         }
2470     }
2471
2472     pa_xfree(cc);
2473 }
2474
2475
2476 static void schedule_card_check(struct userdata *u, pa_card *card)
2477 {
2478     pa_core *core;
2479     card_check_t *cc;
2480
2481     pa_assert(u);
2482     pa_assert(card);
2483     pa_assert_se((core = u->core));
2484
2485     pa_log_debug("scheduling card check");
2486
2487     cc = pa_xnew0(card_check_t, 1);
2488     cc->u = u;
2489     cc->index = card->index;
2490
2491     pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
2492 }
2493
2494
2495 static void source_cleanup_cb(pa_mainloop_api *m, void *d)
2496 {
2497     source_cleanup_t *sc = d;
2498     struct userdata *u;
2499     pa_core *core;
2500
2501     (void)m;
2502
2503     pa_assert(sc);
2504     pa_assert((u = sc->u));
2505     pa_assert((core = u->core));
2506
2507     pa_log_debug("source cleanup starts");
2508
2509     pa_loopback_destroy(u->loopback, u->core, sc->loop);
2510     pa_multiplex_destroy(u->multiplex, u->core, sc->mux);
2511
2512     pa_log_debug("source cleanup ends");
2513
2514     pa_xfree(sc);
2515 }
2516
2517
2518 static void schedule_source_cleanup(struct userdata *u, mir_node *node)
2519 {
2520     pa_core *core;
2521     source_cleanup_t *sc;
2522
2523     pa_assert(u);
2524     pa_assert(node);
2525     pa_assert_se((core = u->core));
2526
2527     pa_log_debug("scheduling source cleanup");
2528
2529     sc = pa_xnew0(source_cleanup_t, 1);
2530     sc->u = u;
2531     sc->mux = node->mux;
2532     sc->loop = node->loop;
2533
2534     node->mux = NULL;
2535     node->loop = NULL;
2536
2537     pa_mainloop_api_once(core->mainloop, source_cleanup_cb, sc);
2538 }
2539
2540
2541 #if 0
2542 static void stream_uncork_cb(pa_mainloop_api *m, void *d)
2543 {
2544     stream_uncork_t *suc = d;
2545     struct userdata *u;
2546     pa_core *core;
2547     pa_sink *sink;
2548     pa_sink_input *sinp;
2549     uint32_t index;
2550
2551     (void)m;
2552
2553     pa_assert(suc);
2554     pa_assert((u = suc->u));
2555     pa_assert((core = u->core));
2556
2557     pa_log_debug("start uncorking stream");
2558
2559     if (!(sink = pa_idxset_get_by_index(core->sinks, suc->index))) {
2560         pa_log_debug("sink.%d gone", suc->index);
2561         goto out;
2562     }
2563
2564     if (!(sinp = pa_idxset_first(core->sink_inputs, &index))) {
2565         pa_log_debug("sink_input is gone");
2566         goto out;
2567     }
2568
2569     pa_sink_input_cork(sinp, false);
2570
2571     pa_log_debug("stream.%u uncorked", sinp->index);
2572
2573  out:
2574
2575     pa_xfree(suc);
2576 }
2577
2578
2579 static void schedule_stream_uncorking(struct userdata *u, pa_sink *sink)
2580 {
2581     pa_core *core;
2582     stream_uncork_t *suc;
2583
2584     pa_assert(u);
2585     pa_assert(sink);
2586     pa_assert_se((core = u->core));
2587
2588     pa_log_debug("scheduling stream uncorking");
2589
2590     suc = pa_xnew0(stream_uncork_t, 1);
2591     suc->u = u;
2592     suc->index = sink->index;
2593
2594     pa_mainloop_api_once(core->mainloop, stream_uncork_cb, suc);
2595 }
2596 #endif
2597
2598 /*
2599  * Local Variables:
2600  * c-basic-offset: 4
2601  * indent-tabs-mode: nil
2602  * End:
2603  *
2604  */