86860c8b551aaef6fbfff9f7cba1e550d713721a
[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     char               idbuf[512];
782
783     pa_assert(u);
784     pa_assert(sinp);
785     pa_assert_se((core = u->core));
786     pa_assert_se((discover = u->discover));
787     pa_assert_se((pl = sinp->proplist));
788
789     if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME))) {
790         if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
791             pa_log_debug("Seems to be a combine stream. Nothing to do ...");
792             return;
793         }
794         if (!strncmp(media, loopback_outpatrn, sizeof(loopback_outpatrn)-1)) {
795             pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
796             return;
797         }
798     }
799
800     name = pa_utils_get_sink_input_name(sinp);
801
802     pa_log_debug("registering input stream '%s'", name);
803
804     if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
805         pa_log_debug("cant find stream class for '%s'. "
806                      "Leaving it alone", name);
807         return;
808     }
809
810     pa_utils_set_stream_routing_properties(pl, type, NULL);
811
812     snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
813
814     memset(&data, 0, sizeof(data));
815     data.key       = key;
816     data.direction = mir_input;
817     data.implement = mir_stream;
818     data.channels  = sinp->channel_map.channels;
819     data.type      = type;
820     data.zone      = pa_utils_get_zone(sinp->proplist);
821     data.visible   = true;
822     data.available = true;
823     data.amname    = get_stream_amname(type, name, pl);
824     data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
825     data.amid      = AM_ID_INVALID;
826     data.paname    = name;
827     data.paidx     = sinp->index;
828     data.rsetid    = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
829
830     /*
831      * here we can't guess whether the application requested an explicit
832      * route by sepcifying the target sink @ stream creation time.
833      *
834      * the brute force solution: we make a default route for this stream
835      * possibly overwiriting the orginal app request :(
836      */
837     /* this will set data.mux */
838     role = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_ROLE);
839     sink = make_output_prerouting(u, &data, &sinp->channel_map, role, &target);
840
841     node = create_node(u, &data, NULL);
842     pa_assert(node);
843     pa_discover_add_node_to_ptr_hash(u, sinp, node);
844
845     if (sink && target) {
846         pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
847
848         if (pa_sink_input_move_to(sinp, sink, false) < 0)
849             pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
850         else
851             pa_audiomgr_add_default_route(u, node, target);
852     }
853 }
854
855 bool pa_discover_preroute_sink_input(struct userdata *u,
856                                      pa_sink_input_new_data *data)
857 {
858     pa_core           *core;
859     pa_module         *m;
860     pa_proplist       *pl;
861     pa_discover       *discover;
862     pa_multiplex      *multiplex;
863     mir_node           fake;
864     pa_sink           *sink;
865     pa_sink_input     *sinp;
866     const char        *mnam;
867     const char        *role;
868     mir_node_type      type;
869     mir_node          *node;
870     pa_muxnode        *mux;
871     pa_nodeset_resdef *resdef;
872     bool               loopback;
873     bool               remap = false;
874
875     pa_assert(u);
876     pa_assert(data);
877     pa_assert_se((core = u->core));
878     pa_assert_se((discover = u->discover));
879     pa_assert_se((multiplex = u->multiplex));
880     pa_assert_se((pl = data->proplist));
881
882     mnam = (m = data->module) ? m->name : "";
883
884     if (pa_streq(mnam, "module-combine-sink")) {
885         loopback = false;
886         type = mir_node_type_unknown;
887
888         if (!(mux  = pa_multiplex_find_by_module(multiplex, m)) ||
889             !(sink = pa_idxset_get_by_index(core->sinks, mux->sink_index)) ||
890             !(sinp = pa_idxset_first(sink->inputs, NULL)) ||
891             !(type = pa_utils_get_stream_class(sinp->proplist)))
892         {
893             pa_log_debug("can't figure out the type of multiplex stream");
894         }
895         else {
896             pa_utils_set_stream_routing_properties(data->proplist, type, NULL);
897         }
898     }
899     else {
900         loopback = pa_streq(mnam, "module-loopback");
901         remap = false;
902
903         if (loopback) {
904             if (!(node = pa_utils_get_node_from_data(u, mir_input, data))) {
905                 pa_log_debug("can't find loopback node for sink-input");
906                 return true;
907             }
908
909             if (node->direction == mir_output) {
910                 pa_log_debug("refuse to preroute loopback sink-input "
911                              "(current route: sink %u @ %p)", data->sink ?
912                              data->sink->index : PA_IDXSET_INVALID,data->sink);
913                 return true;
914             }
915
916             data->sink = NULL;
917
918             type = pa_classify_guess_stream_node_type(u, pl, NULL);
919         }
920         else {
921             remap = pa_streq(mnam, "module-remap-sink");
922             type = pa_classify_guess_stream_node_type(u, pl, &resdef);
923
924             pa_utils_set_resource_properties(pl, resdef);
925
926             if (pa_stream_state_start_corked(u, data, resdef)) {
927                 pa_log_debug("start corked");
928             }
929         }
930
931         pa_utils_set_stream_routing_properties(pl, type, data->sink);
932     }
933
934     memset(&fake, 0, sizeof(fake));
935     fake.direction = mir_input;
936     fake.implement = mir_stream;
937     fake.type      = type;
938
939     if (!data->sink) {
940         fake.channels  = data->channel_map.channels;
941         fake.zone      = pa_utils_get_zone(data->proplist);
942         fake.visible   = true;
943         fake.available = true;
944         fake.amname    = "<preroute sink-input>";
945         fake.amid      = AM_ID_INVALID;
946         fake.paidx     = PA_IDXSET_INVALID;
947
948         role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
949         sink = make_output_prerouting(u, &fake, &data->channel_map, role,NULL);
950
951         if (sink) {
952 #if 0
953             if (fake.mux && !(data->flags & PA_SINK_INPUT_START_CORKED)) {
954                 data->flags |= PA_SINK_INPUT_START_CORKED;
955                 schedule_stream_uncorking(u, sink);
956             }
957 #endif
958
959             if (pa_sink_input_new_data_set_sink(data, sink, false))
960                 pa_log_debug("set sink %u for new sink-input", sink->index);
961             else {
962                 pa_log("can't set sink %u for new sink-input", sink->index);
963                                                       /* copes wit NULL mux */
964                 pa_multiplex_destroy(u->multiplex, core, fake.mux);
965                 return false;
966             }
967         }
968     }
969
970     if (remap) {
971         /* no ramp needed */
972         return true;
973     }
974     if (loopback && data->sink && data->sink->module) {
975         /* no ramp needed */
976         if (pa_streq(data->sink->module->name, "module-combine-sink"))
977             return true;
978     }
979
980     if (pa_classify_ramping_stream(&fake)) {
981         pa_log_debug("set sink-input ramp-muted");
982         data->flags |= PA_SINK_INPUT_START_RAMP_MUTED;
983     }
984
985     return true;
986 }
987
988
989 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
990 {
991     pa_core           *core;
992     pa_sink           *s;
993     pa_sink_input     *csinp;
994     pa_proplist       *pl;
995     pa_discover       *discover;
996     pa_multiplex      *multiplex;
997     mir_node           data;
998     mir_node          *node;
999     mir_node          *snod;
1000     char              *name;
1001     const char        *media;
1002     mir_node_type      type;
1003     char               key[256];
1004     bool               created;
1005     pa_muxnode        *mux;
1006     pa_nodeset_resdef *resdef;
1007     pa_nodeset_resdef  rdbuf;
1008     char               idbuf[512];
1009
1010     pa_assert(u);
1011     pa_assert(sinp);
1012     pa_assert_se((core = u->core));
1013     pa_assert_se((discover = u->discover));
1014     pa_assert_se((multiplex = u->multiplex));
1015     pa_assert_se((pl = sinp->proplist));
1016
1017     resdef = NULL;
1018
1019     if (!(media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)))
1020         media = "<unknown>";
1021
1022     if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
1023         if (!pa_utils_stream_has_default_route(sinp->proplist) ||
1024             !(mux = pa_multiplex_find_by_module(multiplex, sinp->module)) ||
1025             mux->defstream_index != PA_IDXSET_INVALID)
1026         {
1027             pa_log_debug("New stream is a combine stream. Nothing to do ...");
1028         }
1029         else {
1030             pa_log_debug("New stream is a combine stream. Setting as default");
1031             mux->defstream_index = sinp->index;
1032             mir_router_make_routing(u);
1033         }
1034         return;
1035     } else if (!strncmp(media, loopback_outpatrn,sizeof(loopback_outpatrn)-1)){
1036         pa_log_debug("New stream is a loopback output stream");
1037
1038         if ((node = pa_utils_get_node_from_stream(u, mir_input, sinp))) {
1039             if (node->direction == mir_input)
1040                 pa_log_debug("loopback stream node '%s' found", node->amname);
1041             else {
1042                 pa_log_debug("ignoring it");
1043                 return;
1044             }
1045         }
1046         else {
1047             pa_log_debug("can't find node for the loopback stream");
1048             return;
1049         }
1050
1051         s = sinp->sink;
1052     }
1053     else {
1054         name = pa_utils_get_sink_input_name(sinp);
1055
1056         pa_log_debug("dealing with new input stream '%s'", name);
1057
1058         if ((type = get_stream_routing_class(pl)))
1059             resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1060         else {
1061             if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1062                 pa_log_debug("cant find stream class for '%s'. "
1063                              "Leaving it alone", name);
1064                 return;
1065             }
1066
1067             pa_utils_set_stream_routing_properties(pl, type, NULL);
1068
1069             /* if needed, make some post-routing here */
1070         }
1071
1072         /* we need to add this to main hashmap as that is used for loop
1073            through on all nodes. */
1074         snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
1075
1076         memset(&data, 0, sizeof(data));
1077         data.key       = key;
1078         data.direction = mir_input;
1079         data.implement = mir_stream;
1080         data.channels  = sinp->channel_map.channels;
1081         data.type      = type;
1082         data.zone      = pa_utils_get_zone(pl);
1083         data.visible   = true;
1084         data.available = true;
1085         data.amname    = get_stream_amname(type, name, pl);
1086         data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1087         data.amid      = AM_ID_INVALID;
1088         data.paname    = name;
1089         data.paidx     = sinp->index;
1090         data.mux       = pa_multiplex_find_by_sink(u->multiplex,
1091                                                    sinp->sink->index);
1092         data.rsetid    = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
1093         node = create_node(u, &data, &created);
1094
1095         pa_assert(node);
1096
1097         if (!created) {
1098             pa_log("%s: confused with stream. '%s' did exists",
1099                    __FILE__, node->amname);
1100             return;
1101         }
1102
1103         if (node->rsetid)
1104             pa_murphyif_add_node(u, node);
1105         else if (resdef)
1106             pa_murphyif_create_resource_set(u, node, resdef);
1107
1108         pa_discover_add_node_to_ptr_hash(u, sinp, node);
1109
1110         if (!data.mux)
1111             s = sinp->sink;
1112         else {
1113             csinp = pa_idxset_get_by_index(core->sink_inputs,
1114                                            data.mux->defstream_index);
1115             s = csinp ? csinp->sink : NULL;
1116
1117             if ((sinp->flags & PA_SINK_INPUT_START_RAMP_MUTED)) {
1118                 pa_log_debug("ramp '%s' to 100%", media);
1119                 pa_fader_ramp_volume(u, sinp, PA_VOLUME_NORM);
1120             }
1121         }
1122     }
1123
1124     if (s)
1125         pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1126
1127     if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1128         pa_log_debug("can't figure out where this stream is routed");
1129     else {
1130         pa_log_debug("register route '%s' => '%s'",
1131                      node->amname, snod->amname);
1132
1133         if (pa_utils_stream_has_default_route(sinp->proplist))
1134             pa_audiomgr_add_default_route(u, node, snod);
1135
1136         /* FIXME: register explicit routes */
1137         /* else pa_audiomgr_add/register_explicit_route() */
1138
1139
1140         pa_fader_apply_volume_limits(u, pa_utils_get_stamp());
1141     }
1142 }
1143
1144
1145 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
1146 {
1147     pa_discover    *discover;
1148     mir_node       *node;
1149     mir_node       *sinknod;
1150     char           *name;
1151     bool       had_properties = false;
1152
1153     pa_assert(u);
1154     pa_assert(sinp);
1155     pa_assert_se((discover = u->discover));
1156
1157     name = pa_utils_get_sink_input_name(sinp);
1158
1159     pa_log_debug("sink-input '%s' going to be destroyed", name);
1160
1161     if (sinp->proplist)
1162         had_properties = pa_utils_unset_stream_routing_properties(sinp->proplist);
1163
1164     if (!(node = pa_discover_remove_node_from_ptr_hash(u, sinp))) {
1165         if (!pa_multiplex_sink_input_remove(u->multiplex, sinp))
1166             pa_log_debug("nothing to do for sink-input (name '%s')", name);
1167     }
1168     else {
1169         pa_log_debug("node found for '%s'. After clearing routes "
1170                      "it will be destroyed", name);
1171
1172         if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
1173             pa_log_debug("can't figure out where this stream is routed");
1174         else {
1175             pa_log_debug("clear route '%s' => '%s'",
1176                          node->amname, sinknod->amname);
1177
1178             /* FIXME: and actually do it ... */
1179
1180         }
1181
1182         destroy_node(u, node);
1183     }
1184
1185     if (node || had_properties)
1186         mir_router_make_routing(u);
1187 }
1188
1189
1190 void pa_discover_register_source_output(struct userdata  *u,
1191                                         pa_source_output *sout)
1192 {
1193     pa_core           *core;
1194     pa_discover       *discover;
1195     pa_proplist       *pl;
1196     char              *name;
1197     const char        *media;
1198     mir_node_type      type;
1199     mir_node           data;
1200     mir_node          *node;
1201     mir_node          *target;
1202     char               key[256];
1203     pa_source         *source;
1204     const char        *role;
1205     pa_nodeset_resdef *resdef;
1206     char               idbuf[512];
1207
1208     pa_assert(u);
1209     pa_assert(sout);
1210     pa_assert_se((core = u->core));
1211     pa_assert_se((discover = u->discover));
1212     pa_assert_se((pl = sout->proplist));
1213
1214     if ((media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME))) {
1215         if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1216             pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
1217             return;
1218         }
1219     }
1220
1221     name = pa_utils_get_source_output_name(sout);
1222
1223     pa_log_debug("registering output stream '%s'", name);
1224
1225     if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1226         pa_log_debug("cant find stream class for '%s'. "
1227                      "Leaving it alone", name);
1228         return;
1229     }
1230
1231     pa_utils_set_stream_routing_properties(pl, type, NULL);
1232
1233     snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1234
1235     memset(&data, 0, sizeof(data));
1236     data.key       = key;
1237     data.direction = mir_output;
1238     data.implement = mir_stream;
1239     data.channels  = sout->channel_map.channels;
1240     data.type      = type;
1241     data.zone      = pa_utils_get_zone(sout->proplist);
1242     data.visible   = true;
1243     data.available = true;
1244     data.amname    = name;
1245     data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1246     data.amid      = AM_ID_INVALID;
1247     data.paname    = name;
1248     data.paidx     = sout->index;
1249     data.rsetid    = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
1250
1251     /*
1252      * here we can't guess whether the application requested an explicit
1253      * route by sepcifying the target source @ stream creation time.
1254      *
1255      * the brute force solution: we make a default route for this stream
1256      * possibly overwiriting the orginal app request :(
1257      */
1258     role   = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_ROLE);
1259     source = make_input_prerouting(u, &data, role, &target);
1260
1261     node = create_node(u, &data, NULL);
1262     pa_assert(node);
1263     pa_discover_add_node_to_ptr_hash(u, sout, node);
1264
1265     if (source && target) {
1266         pa_log_debug("move stream to source %u (%s)",
1267                      source->index, source->name);
1268
1269         if (pa_source_output_move_to(sout, source, false) < 0)
1270             pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
1271         else {
1272             pa_log_debug("register route '%s' => '%s'",
1273                          node->amname, target->amname);
1274             /* FIXME: and actually do it ... */
1275         }
1276     }
1277 }
1278
1279 bool pa_discover_preroute_source_output(struct userdata *u,
1280                                         pa_source_output_new_data *data)
1281 {
1282     pa_core           *core;
1283     pa_module         *m;
1284     pa_proplist       *pl;
1285     pa_discover       *discover;
1286     mir_node           fake;
1287     pa_source         *source;
1288     const char        *mnam;
1289     const char        *role;
1290     mir_node_type      type;
1291     mir_node          *node;
1292     pa_nodeset_resdef *resdef;
1293
1294     pa_assert(u);
1295     pa_assert(data);
1296     pa_assert_se((core = u->core));
1297     pa_assert_se((discover = u->discover));
1298     pa_assert_se((pl = data->proplist));
1299
1300     mnam = (m = data->module) ? m->name : "";
1301
1302     if (pa_streq(mnam, "module-loopback")) {
1303         if (!(node = pa_utils_get_node_from_data(u, mir_output, data))) {
1304             pa_log_debug("can't find loopback node for source-output");
1305             return true;
1306         }
1307
1308         if (node->direction == mir_input) {
1309             pa_log_debug("refuse to preroute loopback source-output "
1310                          "(current route: source %u @ %p)", data->source ?
1311                          data->source->index : PA_IDXSET_INVALID,data->source);
1312             return true;
1313         }
1314
1315         data->source = NULL;
1316
1317         type = pa_classify_guess_stream_node_type(u, pl, NULL);
1318     }
1319     else {
1320         type = pa_classify_guess_stream_node_type(u, pl, &resdef);
1321
1322         pa_utils_set_resource_properties(pl, resdef);
1323     }
1324
1325     pa_utils_set_stream_routing_properties(pl, type, data->source);
1326
1327     if (!data->source) {
1328         memset(&fake, 0, sizeof(fake));
1329         fake.direction = mir_output;
1330         fake.implement = mir_stream;
1331         fake.channels  = data->channel_map.channels;
1332         fake.type      = type;
1333         fake.zone      = pa_utils_get_zone(data->proplist);
1334         fake.visible   = true;
1335         fake.available = true;
1336         fake.amname    = "<preroute source-output>";
1337
1338         role   = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
1339         source = make_input_prerouting(u, &fake, role, NULL);
1340
1341         if (source) {
1342             if (pa_source_output_new_data_set_source(data, source, false)) {
1343                 pa_log_debug("set source %u for new source-output",
1344                              source->index);
1345             }
1346             else {
1347                 pa_log("can't set source %u for new source-output",
1348                        source->index);
1349             }
1350         }
1351     }
1352
1353     return true;
1354 }
1355
1356
1357 void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
1358 {
1359     pa_core           *core;
1360     pa_source         *s;
1361     pa_proplist       *pl;
1362     pa_discover       *discover;
1363     mir_node           data;
1364     mir_node          *node;
1365     mir_node          *snod;
1366     char              *name;
1367     const char        *media;
1368     mir_node_type      type;
1369     char               key[256];
1370     bool               created;
1371     pa_nodeset_resdef *resdef;
1372     pa_nodeset_resdef  rdbuf;
1373     char               idbuf[512];
1374
1375     pa_assert(u);
1376     pa_assert(sout);
1377     pa_assert_se((core = u->core));
1378     pa_assert_se((discover = u->discover));
1379     pa_assert_se((pl = sout->proplist));
1380
1381     resdef = NULL;
1382
1383     if (!(media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME)))
1384         media = "<unknown>";
1385
1386     if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1387         pa_log_debug("New stream is a loopback input stream");
1388
1389         if ((node = pa_utils_get_node_from_stream(u, mir_output, sout))) {
1390             if (node->direction == mir_output)
1391                 pa_log_debug("loopback stream node '%s' found", node->amname);
1392             else {
1393                 pa_log_debug("ignoring it");
1394                 return;
1395             }
1396         }
1397         else {
1398             pa_log_debug("can't find node for the loopback stream");
1399             return;
1400         }
1401     }
1402     else {
1403         name = pa_utils_get_source_output_name(sout);
1404
1405         pa_log_debug("dealing with new output stream '%s'", name);
1406
1407         if ((type = get_stream_routing_class(pl)))
1408             resdef = pa_utils_get_resource_properties(pl, &rdbuf);
1409         else {
1410             if (!(type = pa_classify_guess_stream_node_type(u, pl, &resdef))) {
1411                 pa_log_debug("cant find stream class for '%s'. "
1412                              "Leaving it alone", name);
1413                 return;
1414             }
1415
1416             pa_utils_set_stream_routing_properties(pl, type, NULL);
1417
1418             /* if needed, make some post-routing here */
1419         }
1420
1421         /* we need to add this to main hashmap as that is used for loop
1422            through on all nodes. */
1423         snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1424
1425         memset(&data, 0, sizeof(data));
1426         data.key       = key;
1427         data.direction = mir_output;
1428         data.implement = mir_stream;
1429         data.channels  = sout->channel_map.channels;
1430         data.type      = type;
1431         data.zone      = pa_utils_get_zone(pl);
1432         data.visible   = true;
1433         data.available = true;
1434         data.amname    = name;
1435         data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1436         data.amid      = AM_ID_INVALID;
1437         data.paname    = name;
1438         data.paidx     = sout->index;
1439         data.rsetid    = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
1440
1441         node = create_node(u, &data, &created);
1442
1443         pa_assert(node);
1444
1445         if (!created) {
1446             pa_log("%s: confused with stream. '%s' did exists",
1447                    __FILE__, node->amname);
1448             return;
1449         }
1450
1451         if (node->rsetid)
1452             pa_murphyif_add_node(u, node);
1453         else if (resdef)
1454             pa_murphyif_create_resource_set(u, node, resdef);
1455
1456         pa_discover_add_node_to_ptr_hash(u, sout, node);
1457     }
1458
1459     if ((s = sout->source))
1460         pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1461
1462     if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1463         pa_log_debug("can't figure out where this stream is routed");
1464     else {
1465         pa_log_debug("register route '%s' => '%s'",
1466                      snod->amname, node->amname);
1467         pa_audiomgr_add_default_route(u, node, snod);
1468     }
1469 }
1470
1471
1472 void pa_discover_remove_source_output(struct userdata  *u,
1473                                       pa_source_output *sout)
1474 {
1475     pa_discover    *discover;
1476     mir_node       *node;
1477     mir_node       *srcnod;
1478     char           *name;
1479
1480     pa_assert(u);
1481     pa_assert(sout);
1482     pa_assert_se((discover = u->discover));
1483
1484     name = pa_utils_get_source_output_name(sout);
1485
1486     pa_log_debug("source-output '%s' going to be destroyed", name);
1487
1488     if (!(node = pa_discover_remove_node_from_ptr_hash(u, sout)))
1489         pa_log_debug("can't find node for source-output (name '%s')", name);
1490     else {
1491         pa_log_debug("node found for '%s'. After clearing routes "
1492                      "it will be destroyed", name);
1493
1494         if (!(srcnod = pa_hashmap_get(discover->nodes.byptr, sout->source)))
1495             pa_log_debug("can't figure out where this stream is routed");
1496         else {
1497             pa_log_debug("clear route '%s' => '%s'",
1498                          node->amname, srcnod->amname);
1499
1500             /* FIXME: and actually do it ... */
1501
1502         }
1503
1504         destroy_node(u, node);
1505
1506         mir_router_make_routing(u);
1507     }
1508 }
1509
1510
1511 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
1512 {
1513     pa_discover *discover;
1514     mir_node    *node;
1515
1516     pa_assert(u);
1517     pa_assert_se((discover = u->discover));
1518
1519     if (key)
1520         node = pa_hashmap_get(discover->nodes.byname, key);
1521     else
1522         node = NULL;
1523
1524     return node;
1525 }
1526
1527 mir_node *pa_discover_find_node_by_ptr(struct userdata *u, void *ptr)
1528 {
1529     pa_discover *discover;
1530     mir_node    *node;
1531
1532     pa_assert(u);
1533     pa_assert_se((discover = u->discover));
1534
1535     if (ptr)
1536         node = pa_hashmap_get(discover->nodes.byptr, ptr);
1537     else
1538         node = NULL;
1539
1540     return node;
1541 }
1542
1543 void pa_discover_add_node_to_ptr_hash(struct userdata *u,
1544                                       void *ptr,
1545                                       mir_node *node)
1546 {
1547     pa_discover *discover;
1548
1549     pa_assert(u);
1550     pa_assert(ptr);
1551     pa_assert(node);
1552     pa_assert_se((discover = u->discover));
1553
1554     pa_hashmap_put(discover->nodes.byptr, ptr, node);
1555 }
1556
1557 mir_node *pa_discover_remove_node_from_ptr_hash(struct userdata *u, void *ptr)
1558 {
1559     pa_discover *discover;
1560
1561     pa_assert(u);
1562     pa_assert(ptr);
1563     pa_assert_se((discover = u->discover));
1564
1565     return pa_hashmap_remove(discover->nodes.byptr, ptr);
1566 }
1567
1568
1569 static void handle_alsa_card(struct userdata *u, pa_card *card)
1570 {
1571     mir_node    data;
1572     const char *udd;
1573     char       *cnam;
1574     char       *cid;
1575
1576     memset(&data, 0, sizeof(data));
1577     data.zone = pa_utils_get_zone(card->proplist);
1578     data.visible = true;
1579     data.amid = AM_ID_INVALID;
1580     data.implement = mir_device;
1581     data.paidx = PA_IDXSET_INVALID;
1582     data.stamp = pa_utils_get_stamp();
1583
1584     cnam = pa_utils_get_card_name(card);
1585     udd  = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
1586
1587     if (udd && pa_streq(udd, "1")) {
1588         /* udev loaded alsa card */
1589         if (!strncmp(cnam, "alsa_card.", 10)) {
1590             cid = cnam + 10;
1591             handle_udev_loaded_card(u, card, &data, cid);
1592             return;
1593         }
1594     }
1595     else {
1596         /* statically loaded pci or usb card */
1597     }
1598
1599     pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
1600 }
1601
1602
1603 #if 0
1604 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1605 {
1606     pa_discover     *discover;
1607     pa_card_profile *prof;
1608     mir_node         data;
1609     mir_node        *node;
1610     mir_constr_def  *cd;
1611     char            *cnam;
1612     char            *cid;
1613     const char      *cdescr;
1614     void            *state;
1615     char             paname[MAX_NAME_LENGTH+1];
1616     char             amname[MAX_NAME_LENGTH+1];
1617     char             key[MAX_NAME_LENGTH+1];
1618
1619     pa_assert_se((discover = u->discover));
1620
1621     cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1622
1623
1624     memset(paname, 0, sizeof(paname));
1625     memset(amname, 0, sizeof(amname));
1626     memset(key   , 0, sizeof(key)   );
1627
1628     memset(&data, 0, sizeof(data));
1629     data.key = key;
1630     data.visible = true;
1631     data.amid = AM_ID_INVALID;
1632     data.implement = mir_device;
1633     data.paidx = PA_IDXSET_INVALID;
1634     data.paname = paname;
1635     data.amname = amname;
1636     data.amdescr = (char *)cdescr;
1637     data.pacard.index = card->index;
1638     data.stamp = pa_utils_get_stamp();
1639
1640     cnam = pa_utils_get_card_name(card);
1641
1642     if (!strncmp(cnam, "bluez_card.", 11)) {
1643         cid = cnam + 11;
1644
1645         pa_assert(card->ports);
1646
1647         cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1648
1649         PA_HASHMAP_FOREACH(prof, card->profiles, cstate) {
1650             data.available = false;
1651             data.pacard.profile = prof->name;
1652
1653             if (prof->n_sinks > 0) {
1654                 data.direction = mir_output;
1655                 data.channels = prof->max_sink_channels;
1656                 data.amname = amname;
1657                 amname[0] = '\0';
1658                 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1659                 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1660                 pa_classify_node_by_card(&data, card, prof, NULL);
1661                 node = create_node(u, &data, NULL);
1662                 mir_constrain_add_node(u, cd, node);
1663             }
1664
1665             if (prof->n_sources > 0) {
1666                 data.direction = mir_input;
1667                 data.channels = prof->max_source_channels;
1668                 data.amname = amname;
1669                 amname[0] = '\0';
1670                 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1671                 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1672                 pa_classify_node_by_card(&data, card, prof, NULL);
1673                 node = create_node(u, &data, NULL);
1674                 mir_constrain_add_node(u, cd, node);
1675             }
1676         }
1677
1678         if (!(prof = card->active_profile))
1679             pa_log("card '%s' has no active profile", card->name);
1680         else {
1681             pa_log_debug("card '%s' default profile '%s'",
1682                          card->name, prof->name);
1683         }
1684
1685         schedule_card_check(u, card);
1686     }
1687 }
1688 #endif
1689
1690
1691 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1692 {
1693     pa_discover     *discover;
1694     pa_card_profile *prof;
1695     pa_device_port  *port;
1696     mir_node         data;
1697     mir_node        *node;
1698     mir_constr_def  *cd;
1699     char            *cnam;
1700     char            *cid;
1701     const char      *cdescr;
1702     void            *state0, *state1;
1703     char             paname[MAX_NAME_LENGTH+1];
1704     char             amname[MAX_NAME_LENGTH+1];
1705     char             key[MAX_NAME_LENGTH+1];
1706     int              len;
1707     bool        input;
1708     bool        output;
1709
1710     pa_assert_se((discover = u->discover));
1711
1712     cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1713
1714
1715     memset(paname, 0, sizeof(paname));
1716     memset(amname, 0, sizeof(amname));
1717     memset(key   , 0, sizeof(key)   );
1718
1719     memset(&data, 0, sizeof(data));
1720     data.key = key;
1721     data.zone = pa_utils_get_zone(card->proplist);
1722     data.visible = true;
1723     data.amid = AM_ID_INVALID;
1724     data.implement = mir_device;
1725     data.paidx = PA_IDXSET_INVALID;
1726     data.paname = paname;
1727     data.amname = amname;
1728     data.amdescr = (char *)cdescr;
1729     data.pacard.index = card->index;
1730     data.stamp = pa_utils_get_stamp();
1731
1732     cnam = pa_utils_get_card_name(card);
1733
1734     if (!strncmp(cnam, "bluez_card.", 11)) {
1735         cid = cnam + 11;
1736
1737         pa_assert(card->ports);
1738
1739         cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1740
1741         PA_HASHMAP_FOREACH(port, card->ports, state0) {
1742             pa_assert(port->profiles);
1743
1744
1745             input = output = true;
1746             len = strlen(port->name);
1747             if (len >= 6 && !strcmp("-input", port->name + (len-6)))
1748                 output = false;
1749             else if (len >= 7 && !strcmp("-output", port->name + (len-7)))
1750                 input  = false;
1751
1752
1753             PA_HASHMAP_FOREACH(prof, port->profiles, state1) {
1754                 data.pacard.profile = prof->name;
1755                 data.available = get_bluetooth_port_availability(&data, port);
1756
1757                 if (output && prof->n_sinks > 0) {
1758                     data.direction = mir_output;
1759                     data.channels = prof->max_sink_channels;
1760                     data.amname = amname;
1761                     amname[0] = '\0';
1762                     snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1763                     snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1764                     pa_classify_node_by_card(&data, card, prof, NULL);
1765                     node = create_node(u, &data, NULL);
1766                     mir_constrain_add_node(u, cd, node);
1767                     pa_utils_set_port_properties(port, node);
1768                 }
1769
1770                 if (input && prof->n_sources > 0) {
1771                     data.direction = mir_input;
1772                     data.channels = prof->max_source_channels;
1773                     data.amname = amname;
1774                     amname[0] = '\0';
1775                     snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1776                     snprintf(key, sizeof(key), "%s@%s.%s", paname, port->name, prof->name);
1777                     pa_classify_node_by_card(&data, card, prof, NULL);
1778                     node = create_node(u, &data, NULL);
1779                     mir_constrain_add_node(u, cd, node);
1780                     pa_utils_set_port_properties(port, node);
1781                 }
1782             }
1783         }
1784
1785         if (!(prof = card->active_profile))
1786             pa_log("card '%s' has no active profile", card->name);
1787         else {
1788             pa_log_debug("card '%s' default profile '%s'",
1789                          card->name, prof->name);
1790         }
1791
1792         schedule_card_check(u, card);
1793     }
1794 }
1795
1796 static bool get_bluetooth_port_availability(mir_node *node,
1797                                                  pa_device_port *port)
1798 {
1799     bool available = false;
1800     const char *prof;
1801
1802     pa_assert(node);
1803     pa_assert(port);
1804
1805     if ((prof = node->pacard.profile)) {
1806         if (!strcmp(prof, "hfgw")        ||
1807             !strcmp(prof, "a2dp_source") ||
1808             !strcmp(prof, "a2dp_sink"))
1809             available = (port->available != PA_AVAILABLE_NO);
1810         else
1811             available = true;
1812     }
1813
1814     return available;
1815 }
1816
1817 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
1818                                     mir_node *data, char *cardid)
1819 {
1820     pa_discover      *discover;
1821     pa_card_profile  *prof;
1822     pa_card_profile  *active;
1823     void             *state;
1824     const char       *alsanam;
1825     char             *sid;
1826     char             *sinks[MAX_CARD_TARGET+1];
1827     char             *sources[MAX_CARD_TARGET+1];
1828     char              buf[MAX_NAME_LENGTH+1];
1829     char              paname[MAX_NAME_LENGTH+1];
1830     char              amname[MAX_NAME_LENGTH+1];
1831     int               i;
1832
1833     pa_assert(card);
1834     pa_assert(card->profiles);
1835     pa_assert_se((discover = u->discover));
1836
1837     alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
1838
1839     memset(amname, 0, sizeof(amname));
1840
1841     data->paname  = paname;
1842     data->amname  = amname;
1843     data->amdescr = (char *)alsanam;
1844
1845     data->pacard.index = card->index;
1846
1847     active = card->active_profile;
1848
1849     PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1850         /* filtering: deal with selected profiles if requested so */
1851         if (discover->selected && (!active || (active && prof != active)))
1852             continue;
1853
1854         /* filtering: skip the 'off' profiles */
1855         if (!prof->n_sinks && !prof->n_sources)
1856             continue;
1857
1858         /* filtering: consider sinks with suitable amount channels */
1859         if (prof->n_sinks &&
1860             (prof->max_sink_channels < discover->chmin ||
1861              prof->max_sink_channels  > discover->chmax  ))
1862             continue;
1863
1864         /* filtering: consider sources with suitable amount channels */
1865         if (prof->n_sources &&
1866             (prof->max_source_channels <  discover->chmin ||
1867              prof->max_source_channels >  discover->chmax   ))
1868             continue;
1869
1870         data->pacard.profile = prof->name;
1871
1872         parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
1873
1874         data->direction = mir_output;
1875         data->channels = prof->max_sink_channels;
1876         for (i = 0;  (sid = sinks[i]);  i++) {
1877             snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
1878             handle_card_ports(u, data, card, prof);
1879         }
1880
1881         data->direction = mir_input;
1882         data->channels = prof->max_source_channels;
1883         for (i = 0;  (sid = sources[i]);  i++) {
1884             snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
1885             handle_card_ports(u, data, card, prof);
1886         }
1887     }
1888 }
1889
1890
1891 static void handle_card_ports(struct userdata *u, mir_node *data,
1892                               pa_card *card, pa_card_profile *prof)
1893 {
1894     mir_node       *node = NULL;
1895     bool       have_ports = false;
1896     mir_constr_def *cd = NULL;
1897     char           *amname = data->amname;
1898     pa_device_port *port;
1899     void           *state;
1900     bool       created;
1901     char            key[MAX_NAME_LENGTH+1];
1902
1903     pa_assert(u);
1904     pa_assert(data);
1905     pa_assert(card);
1906     pa_assert(prof);
1907
1908     if (card->ports) {
1909         PA_HASHMAP_FOREACH(port, card->ports, state) {
1910             /*
1911              * if this port did not belong to any profile
1912              * (ie. prof->profiles == NULL) we assume that this port
1913              * does works with all the profiles
1914              */
1915             if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
1916                 ((port->direction == PA_DIRECTION_INPUT && data->direction == mir_input)||
1917                  (port->direction == PA_DIRECTION_OUTPUT && data->direction == mir_output)))
1918             {
1919                 have_ports = true;
1920
1921                 amname[0] = '\0';
1922                 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
1923
1924                 data->key       = key;
1925                 data->available = (port->available != PA_AVAILABLE_NO);
1926                 data->type      = 0;
1927                 data->amname    = amname;
1928                 data->paport    = port->name;
1929
1930                 pa_classify_node_by_card(data, card, prof, port);
1931
1932                 node = create_node(u, data, &created);
1933
1934                 if (!created)
1935                     node->stamp = data->stamp;
1936                 else {
1937                     cd = mir_constrain_create(u, "port", mir_constrain_port,
1938                                               data->paname);
1939                     mir_constrain_add_node(u, cd, node);
1940                 }
1941             }
1942         }
1943     }
1944
1945     if (!have_ports) {
1946         data->key = data->paname;
1947         data->available = true;
1948
1949         pa_classify_node_by_card(data, card, prof, NULL);
1950
1951         node = create_node(u, data, &created);
1952
1953         if (!created)
1954             node->stamp = data->stamp;
1955     }
1956
1957     data->amname = amname;
1958     *amname = '\0';
1959 }
1960
1961
1962 static mir_node *create_node(struct userdata *u, mir_node *data,
1963                              bool *created_ret)
1964 {
1965     pa_discover *discover;
1966     mir_node    *node;
1967     bool    created;
1968     char         buf[2048];
1969
1970     pa_assert(u);
1971     pa_assert(data);
1972     pa_assert(data->key);
1973     pa_assert(data->paname);
1974     pa_assert_se((discover = u->discover));
1975
1976     if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
1977         created = false;
1978     else {
1979         created = true;
1980
1981         node = mir_node_create(u, data);
1982         pa_hashmap_put(discover->nodes.byname, node->key, node);
1983
1984         mir_node_print(node, buf, sizeof(buf));
1985         pa_log_debug("new node:\n%s", buf);
1986
1987         if (node->available)
1988             pa_audiomgr_register_node(u, node);
1989     }
1990
1991     if (created_ret)
1992         *created_ret = created;
1993
1994     return node;
1995 }
1996
1997 static void destroy_node(struct userdata *u, mir_node *node)
1998 {
1999     pa_discover *discover;
2000     mir_node    *removed;
2001
2002     pa_assert(u);
2003     pa_assert_se((discover = u->discover));
2004
2005     if (node) {
2006         removed = pa_hashmap_remove(discover->nodes.byname, node->key);
2007
2008         if (node != removed) {
2009             if (removed)
2010                 pa_log("%s: confused with data structures: key mismatch. "
2011                        " attempted to destroy '%s'; actually destroyed '%s'",
2012                        __FILE__, node->key, removed->key);
2013             else
2014                 pa_log("%s: confused with data structures: node '%s' "
2015                        "is not in the hash table", __FILE__, node->key);
2016             return;
2017         }
2018
2019         pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
2020
2021         if (node->implement == mir_stream) {
2022             if (node->direction == mir_input) {
2023                 if (node->mux) {
2024                     pa_log_debug("removing multiplexer");
2025                 }
2026             }
2027         }
2028
2029         pa_audiomgr_unregister_node(u, node);
2030
2031         extapi_signal_node_change(u);
2032
2033         mir_constrain_remove_node(u, node);
2034
2035         pa_loopback_destroy(u->loopback, u->core, node->loop);
2036         pa_multiplex_destroy(u->multiplex, u->core, node->mux);
2037
2038         mir_node_destroy(u, node);
2039     }
2040 }
2041
2042 static bool update_node_availability(struct userdata *u,
2043                                           mir_node *node,
2044                                           bool available)
2045 {
2046     pa_assert(u);
2047     pa_assert(node);
2048
2049     if ((!available &&  node->available) ||
2050         ( available && !node->available)  )
2051     {
2052         node->available = available;
2053
2054         if (available)
2055             pa_audiomgr_register_node(u, node);
2056         else
2057             pa_audiomgr_unregister_node(u, node);
2058
2059         extapi_signal_node_change(u);
2060
2061         return true; /* routing needed */
2062     }
2063
2064     return false;
2065 }
2066
2067 static bool update_node_availability_by_device(struct userdata *u,
2068                                                     mir_direction direction,
2069                                                     void *data,
2070                                                     pa_device_port *port,
2071                                                     bool available)
2072 {
2073     mir_node *node;
2074     char     *key;
2075     char      buf[256];
2076
2077     pa_assert(u);
2078     pa_assert(data);
2079     pa_assert(port);
2080     pa_assert(direction == mir_input || direction == mir_output);
2081
2082     if ((key = node_key(u, direction, data, port, buf, sizeof(buf)))) {
2083         if (!(node = pa_discover_find_node_by_key(u, key)))
2084             pa_log_debug("      can't find node (key '%s')", key);
2085         else {
2086             pa_log_debug("      node for '%s' found (key %s)",
2087                          node->paname, node->key);
2088
2089             return update_node_availability(u, node, available);
2090         }
2091     }
2092
2093     return false; /* no routing needed */
2094 }
2095
2096 static char *get_name(char **string_ptr, int offs)
2097 {
2098     char c, *name, *end;
2099
2100     name = *string_ptr + offs;
2101
2102     for (end = name;  (c = *end);   end++) {
2103         if (c == '+') {
2104             *end++ = '\0';
2105             break;
2106         }
2107     }
2108
2109     *string_ptr = end;
2110
2111     return name;
2112 }
2113
2114 static void parse_profile_name(pa_card_profile *prof,
2115                                char           **sinks,
2116                                char           **sources,
2117                                char            *buf,
2118                                int              buflen)
2119 {
2120     char *p = buf;
2121     int   i = 0;
2122     int   j = 0;
2123
2124     pa_assert(prof->name);
2125
2126     strncpy(buf, prof->name, buflen);
2127     buf[buflen-1] = '\0';
2128
2129     memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2130     memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
2131
2132     do {
2133         if (!strncmp(p, "output:", 7)) {
2134             if (i >= MAX_CARD_TARGET) {
2135                 pa_log_debug("number of outputs exeeds the maximum %d in "
2136                              "profile name '%s'", MAX_CARD_TARGET, prof->name);
2137                 return;
2138             }
2139             sinks[i++] = get_name(&p, 7);
2140         }
2141         else if (!strncmp(p, "input:", 6)) {
2142             if (j >= MAX_CARD_TARGET) {
2143                 pa_log_debug("number of inputs exeeds the maximum %d in "
2144                              "profile name '%s'", MAX_CARD_TARGET, prof->name);
2145                 return;
2146             }
2147             sources[j++] = get_name(&p, 6);
2148         }
2149         else {
2150             pa_log("%s: failed to parse profile name '%s'",
2151                    __FILE__, prof->name);
2152             return;
2153         }
2154     } while (*p);
2155 }
2156
2157
2158 static char *node_key(struct userdata *u, mir_direction direction,
2159                       void *data, pa_device_port *port, char *buf, size_t len)
2160 {
2161     pa_card         *card;
2162     pa_card_profile *profile;
2163     const char      *bus;
2164     bool        pci;
2165     bool        usb;
2166     bool        bluetooth;
2167     bool        platform;
2168     char            *type;
2169     char            *name;
2170     const char      *profile_name;
2171     char            *key;
2172
2173     pa_assert(u);
2174     pa_assert(data);
2175     pa_assert(buf);
2176     pa_assert(direction == mir_input || direction == mir_output);
2177
2178     if (direction == mir_output) {
2179         pa_sink *sink = data;
2180         type = "sink";
2181         name = pa_utils_get_sink_name(sink);
2182         card = sink->card;
2183         if (!port)
2184             port = sink->active_port;
2185     }
2186     else {
2187         pa_source *source = data;
2188         type = "source";
2189         name = pa_utils_get_source_name(source);
2190         card = source->card;
2191         if (!port)
2192             port = source->active_port;
2193     }
2194
2195     if (!card)
2196         return NULL;
2197
2198     pa_assert_se((profile = card->active_profile));
2199
2200     if (!u->state.profile)
2201         profile_name = profile->name;
2202     else {
2203         pa_log_debug("state.profile is not null. '%s' supresses '%s'",
2204                      u->state.profile, profile->name);
2205         profile_name = u->state.profile;
2206     }
2207
2208
2209     if (!(bus = pa_utils_get_card_bus(card))) {
2210         pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
2211                      "on its card", type, name, PA_PROP_DEVICE_BUS);
2212         return NULL;
2213     }
2214
2215     pci = pa_streq(bus, "pci");
2216     usb = pa_streq(bus, "usb");
2217     platform = pa_streq(bus, "platform");
2218     bluetooth = pa_streq(bus, "bluetooth");
2219
2220     if (!pci && !usb && !bluetooth && !platform) {
2221         pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
2222                      "of its card", type, name, bus);
2223         return NULL;
2224     }
2225
2226     if (bluetooth) {
2227         if (!port)
2228             key = NULL;
2229         else {
2230             key = buf;
2231             snprintf(buf, len, "%s@%s.%s", name, port->name, profile_name);
2232         }
2233     }
2234     else {
2235         if (!port)
2236             key = name;
2237         else {
2238             key = buf;
2239             snprintf(buf, len, "%s@%s", name, port->name);
2240         }
2241     }
2242
2243     return key;
2244 }
2245
2246 static pa_sink *make_output_prerouting(struct userdata *u,
2247                                        mir_node        *data,
2248                                        pa_channel_map  *chmap,
2249                                        const char      *media_role,
2250                                        mir_node       **target_ret)
2251 {
2252     pa_core    *core;
2253     mir_node   *target;
2254     pa_sink    *sink = NULL;
2255
2256     pa_assert(u);
2257     pa_assert(data);
2258     pa_assert(chmap);
2259     pa_assert_se((core = u->core));
2260
2261
2262     target = mir_router_make_prerouting(u, data);
2263
2264     if (!target)
2265         pa_log("there is no default route for the stream '%s'", data->amname);
2266     else if (target->paidx == PA_IDXSET_INVALID)
2267         pa_log("can't route to default '%s': no sink", target->amname);
2268     else {
2269         if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
2270             pa_log("no route to default '%s': sink is gone", target->amname);
2271         else {
2272             if (u->enable_multiplex == true) {
2273                 if (pa_classify_multiplex_stream(data)) {
2274                     data->mux = pa_multiplex_create(u->multiplex, core,
2275                                                     sink->index, chmap, NULL,
2276                                                     media_role, data->type);
2277                     if (data->mux) {
2278                         sink = pa_idxset_get_by_index(core->sinks,
2279                                                       data->mux->sink_index);
2280                         pa_assert(sink);
2281                     }
2282                 }
2283             }
2284         }
2285     }
2286
2287     if (target_ret)
2288         *target_ret = target;
2289
2290     return sink;
2291 }
2292
2293
2294 static pa_source *make_input_prerouting(struct userdata *u,
2295                                         mir_node        *data,
2296                                         const char      *media_role,
2297                                         mir_node       **target_ret)
2298 {
2299     pa_core    *core;
2300     mir_node   *target;
2301     pa_source  *source = NULL;
2302
2303     pa_assert(u);
2304     pa_assert(data);
2305     pa_assert_se((core = u->core));
2306
2307     target = mir_router_make_prerouting(u, data);
2308
2309     if (!target)
2310         pa_log("there is no default route for the stream '%s'", data->amname);
2311     else if (target->paidx == PA_IDXSET_INVALID)
2312         pa_log("can't route to default '%s': no source", target->amname);
2313     else {
2314         if (!(source = pa_idxset_get_by_index(core->sources, target->paidx)))
2315             pa_log("no route to default '%s': source is gone",target->amname);
2316     }
2317
2318     if (target_ret)
2319         *target_ret = target;
2320
2321     return source;
2322 }
2323
2324 static mir_node_type get_stream_routing_class(pa_proplist *pl)
2325 {
2326     mir_node_type t;
2327
2328     pa_assert(pl);
2329
2330     t = pa_utils_get_stream_class(pl);
2331
2332     if (t >= mir_application_class_begin && t <  mir_application_class_end)
2333         return t;
2334
2335     return mir_node_type_unknown;
2336 }
2337
2338 static char *get_stream_amname(mir_node_type type, char *name, pa_proplist *pl)
2339 {
2340     const char *appid;
2341
2342     switch (type) {
2343
2344     case mir_radio:
2345         return "radio";
2346
2347     case mir_player:
2348     case mir_game:
2349     case mir_browser:
2350     case mir_camera:
2351         appid = pa_utils_get_appid(pl);
2352
2353         if (!strcmp(appid, "threaded-ml")         ||
2354             !strcmp(appid, "WebProcess")          ||
2355             !strcmp(appid,"wrt_launchpad_daemon")  )
2356         {
2357             return "wrtApplication";
2358         }
2359         return "icoApplication";
2360
2361     case mir_navigator:
2362         return "navigator";
2363
2364     case mir_phone:
2365         return "phone";
2366
2367     default:
2368         return name;
2369     }
2370 }
2371
2372
2373 static void set_bluetooth_profile(struct userdata *u,
2374                                   pa_card *card,
2375                                   pa_direction_t direction)
2376 {
2377     pa_core *core;
2378     pa_device_port *port;
2379     pa_card_profile *prof, *make_active;
2380     void *state0, *state1;
2381     bool port_available;
2382     bool switch_off;
2383     int nport;
2384
2385     pa_assert(u);
2386     pa_assert(card);
2387     pa_assert_se((core = u->core));
2388
2389     make_active = NULL;
2390     switch_off = false;
2391     nport = 0;
2392
2393     pa_log_debug("which profile to make active:");
2394
2395     PA_HASHMAP_FOREACH(prof, card->profiles, state0) {
2396         if (!prof->n_sinks && !prof->n_sources) {
2397             if (!make_active) {
2398                 pa_log_debug("   considering %s", prof->name);
2399                 make_active = prof;
2400                 switch_off = true;
2401             }
2402         }
2403         else {
2404             port_available = false;
2405
2406             PA_HASHMAP_FOREACH(port, card->ports, state1) {
2407                 if ((direction & port->direction) &&
2408                     pa_hashmap_get(port->profiles, prof->name))
2409                 {
2410                     port_available = (port->available != PA_AVAILABLE_NO);
2411                     break;
2412                 }
2413             }
2414
2415             if (!port_available)
2416                 pa_log_debug("   ruling out %s (port not available)", prof->name);
2417             else if (prof->available != PA_AVAILABLE_YES)
2418                 pa_log_debug("   ruling out %s (profile not available)", prof->name);
2419             else {
2420                 nport++;
2421
2422                 if (((direction & PA_DIRECTION_INPUT)  && prof->n_sources > 0) ||
2423                     ((direction & PA_DIRECTION_OUTPUT) && prof->n_sinks   > 0)   ) {
2424                     if (make_active && prof->priority < make_active->priority)
2425                         pa_log_debug("   ruling out %s (low priority)", prof->name);
2426                     else {
2427                         pa_log_debug("   considering %s", prof->name);
2428                         make_active = prof;
2429                     }
2430                 }
2431                 else {
2432                     pa_log_debug("   ruling out %s (direction)", prof->name);
2433                 }
2434             }
2435         }
2436     }
2437
2438     if (!make_active)
2439         pa_log_debug("No suitable profile found. Frustrated and do nothing");
2440     else {
2441         if (make_active == card->active_profile)
2442             pa_log_debug("Profile %s already set. Do nothing", make_active->name);
2443         else {
2444             if (switch_off && nport) {
2445                 pa_log_debug("Do not switch to %s as active ports are existing "
2446                              "to the other direction", make_active->name);
2447             }
2448             else {
2449                 pa_log_debug("Set profile %s", make_active->name);
2450
2451                 if ((prof = pa_hashmap_get(card->profiles, make_active->name)) != NULL &&
2452                     pa_card_set_profile(card, prof, false) < 0) {
2453                     pa_log_debug("Failed to change profile to %s",
2454                                  make_active->name);
2455                 }
2456             }
2457         }
2458     }
2459 }
2460
2461 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
2462 {
2463     struct userdata *u = d;
2464
2465     (void)m;
2466
2467     pa_assert(u);
2468
2469     pa_log_debug("deferred routing starts");
2470
2471     mir_router_make_routing(u);
2472 }
2473
2474
2475 static void schedule_deferred_routing(struct userdata *u)
2476 {
2477     pa_core *core;
2478
2479     pa_assert(u);
2480     pa_assert_se((core = u->core));
2481
2482     pa_log_debug("scheduling deferred routing");
2483
2484     pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
2485 }
2486
2487
2488 static void card_check_cb(pa_mainloop_api *m, void *d)
2489 {
2490     card_check_t *cc = d;
2491     struct userdata *u;
2492     pa_core *core;
2493     pa_card *card;
2494     pa_sink *sink;
2495     pa_source *source;
2496     int n_sink, n_source;
2497     uint32_t idx;
2498
2499     (void)m;
2500
2501     pa_assert(cc);
2502     pa_assert((u = cc->u));
2503     pa_assert((core = u->core));
2504
2505     pa_log_debug("card check starts");
2506
2507     if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
2508         pa_log_debug("card %u is gone", cc->index);
2509     else {
2510         n_sink = n_source = 0;
2511
2512         PA_IDXSET_FOREACH(sink, core->sinks, idx) {
2513             if ((sink->card) && sink->card->index == card->index)
2514                 n_sink++;
2515         }
2516
2517         PA_IDXSET_FOREACH(source, core->sources, idx) {
2518             if ((source->card) && source->card->index == card->index)
2519                 n_sink++;
2520         }
2521
2522         if (n_sink || n_source) {
2523             pa_log_debug("found %u sinks and %u sources belonging to "
2524                          "'%s' card", n_sink, n_source, card->name);
2525             pa_log_debug("nothing to do");
2526         }
2527         else {
2528             pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
2529                          card->name);
2530             mir_router_make_routing(u);
2531         }
2532     }
2533
2534     pa_xfree(cc);
2535 }
2536
2537
2538 static void schedule_card_check(struct userdata *u, pa_card *card)
2539 {
2540     pa_core *core;
2541     card_check_t *cc;
2542
2543     pa_assert(u);
2544     pa_assert(card);
2545     pa_assert_se((core = u->core));
2546
2547     pa_log_debug("scheduling card check");
2548
2549     cc = pa_xnew0(card_check_t, 1);
2550     cc->u = u;
2551     cc->index = card->index;
2552
2553     pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
2554 }
2555
2556
2557 static void source_cleanup_cb(pa_mainloop_api *m, void *d)
2558 {
2559     source_cleanup_t *sc = d;
2560     struct userdata *u;
2561     pa_core *core;
2562
2563     (void)m;
2564
2565     pa_assert(sc);
2566     pa_assert((u = sc->u));
2567     pa_assert((core = u->core));
2568
2569     pa_log_debug("source cleanup starts");
2570
2571     pa_loopback_destroy(u->loopback, u->core, sc->loop);
2572     pa_multiplex_destroy(u->multiplex, u->core, sc->mux);
2573
2574     pa_log_debug("source cleanup ends");
2575
2576     pa_xfree(sc);
2577 }
2578
2579
2580 static void schedule_source_cleanup(struct userdata *u, mir_node *node)
2581 {
2582     pa_core *core;
2583     source_cleanup_t *sc;
2584
2585     pa_assert(u);
2586     pa_assert(node);
2587     pa_assert_se((core = u->core));
2588
2589     pa_log_debug("scheduling source cleanup");
2590
2591     sc = pa_xnew0(source_cleanup_t, 1);
2592     sc->u = u;
2593     sc->mux = node->mux;
2594     sc->loop = node->loop;
2595
2596     node->mux = NULL;
2597     node->loop = NULL;
2598
2599     pa_mainloop_api_once(core->mainloop, source_cleanup_cb, sc);
2600 }
2601
2602
2603 #if 0
2604 static void stream_uncork_cb(pa_mainloop_api *m, void *d)
2605 {
2606     stream_uncork_t *suc = d;
2607     struct userdata *u;
2608     pa_core *core;
2609     pa_sink *sink;
2610     pa_sink_input *sinp;
2611     uint32_t index;
2612
2613     (void)m;
2614
2615     pa_assert(suc);
2616     pa_assert((u = suc->u));
2617     pa_assert((core = u->core));
2618
2619     pa_log_debug("start uncorking stream");
2620
2621     if (!(sink = pa_idxset_get_by_index(core->sinks, suc->index))) {
2622         pa_log_debug("sink.%d gone", suc->index);
2623         goto out;
2624     }
2625
2626     if (!(sinp = pa_idxset_first(core->sink_inputs, &index))) {
2627         pa_log_debug("sink_input is gone");
2628         goto out;
2629     }
2630
2631     pa_sink_input_cork(sinp, false);
2632
2633     pa_log_debug("stream.%u uncorked", sinp->index);
2634
2635  out:
2636
2637     pa_xfree(suc);
2638 }
2639
2640
2641 static void schedule_stream_uncorking(struct userdata *u, pa_sink *sink)
2642 {
2643     pa_core *core;
2644     stream_uncork_t *suc;
2645
2646     pa_assert(u);
2647     pa_assert(sink);
2648     pa_assert_se((core = u->core));
2649
2650     pa_log_debug("scheduling stream uncorking");
2651
2652     suc = pa_xnew0(stream_uncork_t, 1);
2653     suc->u = u;
2654     suc->index = sink->index;
2655
2656     pa_mainloop_api_once(core->mainloop, stream_uncork_cb, suc);
2657 }
2658 #endif
2659
2660 /*
2661  * Local Variables:
2662  * c-basic-offset: 4
2663  * indent-tabs-mode: nil
2664  * End:
2665  *
2666  */