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