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