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