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