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