discovery: add support for stream output (i.e. source-ouput in PA terms)
[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
22 #include <pulsecore/pulsecore-config.h>
23
24 #include <pulsecore/hashmap.h>
25 #include <pulsecore/idxset.h>
26 #include <pulsecore/client.h>
27 #include <pulsecore/core-util.h>
28 #include <pulsecore/log.h>
29 #include <pulsecore/card.h>
30 #include <pulsecore/device-port.h>
31 #include <pulsecore/sink-input.h>
32 #include <pulsecore/source-output.h>
33 #include <pulsecore/strbuf.h>
34
35
36 #include "discover.h"
37 #include "node.h"
38 #include "audiomgr.h"
39 #include "router.h"
40 #include "constrain.h"
41 #include "multiplex.h"
42 #include "loopback.h"
43 #include "fader.h"
44 #include "classify.h"
45 #include "utils.h"
46
47 #define MAX_CARD_TARGET   4
48 #define MAX_NAME_LENGTH   256
49
50 #define ACTIVE_PORT       NULL
51
52 /* Bluetooth service class */
53 #define BIT(x)    (1U << (x))
54
55 #define BT_SERVICE_MASK          0xffe
56 #define BT_SERVICE_INFORMATION   BIT(23) /**< WEB-server, WAP-server, etc */
57 #define BT_SERVICE_TELEPHONY     BIT(22) /**< Modem, Headset, etc*/
58 #define BT_SERVICE_AUDIO         BIT(21) /**< Speaker, Microphone, Headset */
59 #define BT_SERVICE_OBJECT_XFER   BIT(20) /**< v-Inbox, v-Folder, etc */
60 #define BT_SERVICE_CAPTURING     BIT(19) /**< Scanner, Microphone, etc */
61 #define BT_SERVICE_RENDERING     BIT(18) /**< Printing, Speaker, etc */
62 #define BT_SERVICE_NETWORKING    BIT(17) /**< LAN, Ad hoc, etc */
63 #define BT_SERVICE_POSITIONING   BIT(16) /**< Location identification */
64
65
66 typedef struct {
67     struct userdata *u;
68     uint32_t index;
69 } card_check_t;
70
71 typedef struct {
72     struct userdata *u;
73     pa_muxnode *mux;
74     pa_loopnode *loop;
75 } source_cleanup_t;
76
77 typedef struct {
78     struct userdata *u;
79     uint32_t index;
80 } stream_uncork_t;
81
82 static const char combine_pattern[]   = "Simultaneous output on ";
83 static const char loopback_outpatrn[] = "Loopback from ";
84 static const char loopback_inpatrn[]  = "Loopback to ";
85
86 static void handle_alsa_card(struct userdata *, pa_card *);
87 static void handle_bluetooth_card(struct userdata *, pa_card *);
88
89 static void handle_udev_loaded_card(struct userdata *, pa_card *,
90                                     mir_node *, char *);
91 static void handle_card_ports(struct userdata *, mir_node *,
92                               pa_card *, pa_card_profile *);
93
94 static mir_node *create_node(struct userdata *, mir_node *, pa_bool_t *);
95 static void destroy_node(struct userdata *, mir_node *);
96 static pa_bool_t update_node_availability(struct userdata *, mir_direction,
97                                           void *, pa_device_port *, pa_bool_t);
98
99 static void parse_profile_name(pa_card_profile *,
100                                char **, char **, char *, int);
101
102 static char *node_key(struct userdata *, mir_direction,
103                       void *, pa_device_port *, char *, size_t);
104
105 static pa_sink *make_output_prerouting(struct userdata *, mir_node *,
106                                        pa_channel_map *, const char *,
107                                        mir_node **);
108 static pa_source *make_input_prerouting(struct userdata *, mir_node *,
109                                         const char *, mir_node **);
110
111 static mir_node_type get_stream_routing_class(pa_proplist *);
112
113
114 static void schedule_deferred_routing(struct userdata *);
115 static void schedule_card_check(struct userdata *, pa_card *);
116 static void schedule_source_cleanup(struct userdata *, mir_node *);
117 static void schedule_stream_uncorking(struct userdata *, pa_sink *);
118
119 static void pa_hashmap_node_free(void *node, void *u)
120 {
121     mir_node_destroy(u, node);
122 }
123
124
125 struct pa_discover *pa_discover_init(struct userdata *u)
126 {
127     pa_discover *discover = pa_xnew0(pa_discover, 1);
128
129     discover->chmin = 1;
130     discover->chmax = 2;
131     discover->selected = TRUE;
132
133     discover->nodes.byname = pa_hashmap_new(pa_idxset_string_hash_func,
134                                             pa_idxset_string_compare_func);
135     discover->nodes.byptr  = pa_hashmap_new(pa_idxset_trivial_hash_func,
136                                             pa_idxset_trivial_compare_func);
137     return discover;
138 }
139
140 void pa_discover_done(struct userdata *u)
141 {
142     pa_discover *discover;
143
144     if (u && (discover = u->discover)) {
145         pa_hashmap_free(discover->nodes.byname, pa_hashmap_node_free,u);
146         pa_hashmap_free(discover->nodes.byptr, NULL,NULL);
147         pa_xfree(discover);
148         u->discover = NULL;
149     }
150 }
151
152 void pa_discover_domain_up(struct userdata *u)
153 {
154     pa_discover *discover;
155     mir_node    *node;
156     void        *state;
157
158     pa_assert(u);
159     pa_assert_se((discover = u->discover));
160
161     PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
162         node->amid = AM_ID_INVALID;
163
164         if (node->visible && node->available)
165             pa_audiomgr_register_node(u, node);
166     }
167 }
168
169 void pa_discover_domain_down(struct userdata *u)
170 {
171 }
172
173 void pa_discover_add_card(struct userdata *u, pa_card *card)
174 {
175     const char *bus;
176
177     pa_assert(u);
178     pa_assert(card);
179
180     if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS))) {
181         pa_log_debug("ignoring card '%s' due to lack of '%s' property",
182                      pa_utils_get_card_name(card), PA_PROP_DEVICE_BUS);
183         return;
184     }
185
186     if (pa_streq(bus, "pci") || pa_streq(bus, "usb")) {
187         handle_alsa_card(u, card);
188         return;
189     }
190     else if (pa_streq(bus, "bluetooth")) {
191         handle_bluetooth_card(u, card);
192         return;
193     }
194
195     pa_log_debug("ignoring card '%s' due to unsupported bus type '%s'",
196                  pa_utils_get_card_name(card), bus);
197 }
198
199 void pa_discover_remove_card(struct userdata *u, pa_card *card)
200 {
201     const char  *bus;
202     pa_discover *discover;
203     mir_node    *node;
204     void        *state;
205
206     pa_assert(u);
207     pa_assert(card);
208     pa_assert_se((discover = u->discover));
209
210     if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS)))
211         bus = "<unknown>";
212
213
214     PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
215         if (node->implement == mir_device &&
216             node->pacard.index == card->index)
217         {
218             if (pa_streq(bus, "pci") || pa_streq(bus, "usb"))
219                 mir_constrain_destroy(u, node->paname);
220
221             destroy_node(u, node);
222         }
223     }
224
225     if (pa_streq(bus, "bluetooth"))
226         mir_constrain_destroy(u, card->name);
227 }
228
229 void pa_discover_profile_changed(struct userdata *u, pa_card *card)
230 {
231     pa_core         *core;
232     pa_card_profile *prof;
233     pa_sink         *sink;
234     pa_source       *source;
235     pa_discover     *discover;
236     const char      *bus;
237     pa_bool_t        pci;
238     pa_bool_t        usb;
239     pa_bool_t        bluetooth;
240     uint32_t         stamp;
241     mir_node        *node;
242     void            *state;
243     uint32_t         index;
244     
245     pa_assert(u);
246     pa_assert(card);
247     pa_assert_se((core = u->core));
248     pa_assert_se((discover = u->discover));
249
250
251     if ((bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS)) == NULL) {
252         pa_log_debug("ignoring profile change on card '%s' due to lack of '%s'"
253                      "property", pa_utils_get_card_name(card),
254                      PA_PROP_DEVICE_BUS);
255         return;
256     }
257
258     pci = pa_streq(bus, "pci");
259     usb = pa_streq(bus, "usb");
260     bluetooth = pa_streq(bus, "bluetooth");
261
262     if (!pci && !usb && !bluetooth) {
263         pa_log_debug("ignoring profile change on card '%s' due to unsupported "
264                      "bus type '%s'", pa_utils_get_card_name(card), bus);
265         u->state.sink = u->state.source = PA_IDXSET_INVALID;
266         return;
267     }
268
269     if ((index = u->state.sink) != PA_IDXSET_INVALID) {
270         if ((sink = pa_idxset_get_by_index(core->sinks, index)))
271             pa_discover_add_sink(u, sink, TRUE);
272         else
273             pa_log_debug("sink.%u is gone", index);
274         u->state.sink = PA_IDXSET_INVALID;
275     }
276
277     if ((index = u->state.source) != PA_IDXSET_INVALID) {
278         if ((source = pa_idxset_get_by_index(core->sources, index)))
279             pa_discover_add_source(u, source);
280         else
281             pa_log_debug("source.%u is gone", index);
282         u->state.source = PA_IDXSET_INVALID;
283     }
284
285     if (bluetooth) {
286         pa_assert_se((prof = card->active_profile));
287
288         pa_log_debug("bluetooth profile changed to '%s' on card '%s'",
289                      prof->name, card->name);
290         
291         if (!prof->n_sinks && !prof->n_sources) {
292             /* switched of but not unloaded yet */
293             PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
294                 if (node->implement == mir_device &&
295                     node->pacard.index == card->index)
296                 {
297                     node->available = FALSE;
298                 }
299             }
300         }
301     }
302     else {
303         pa_log_debug("alsa profile changed to '%s' on card '%s'",
304                      card->active_profile->name, card->name);
305
306         stamp = pa_utils_get_stamp();
307  
308         handle_alsa_card(u, card);
309
310         PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
311             if (node->implement == mir_device &&
312                 node->pacard.index == card->index &&
313                 node->stamp < stamp)
314             {
315                 destroy_node(u, node);
316             }
317         }
318     }
319
320 }
321
322 void pa_discover_port_available_changed(struct userdata *u,
323                                         pa_device_port *port)
324 {
325     pa_core    *core;
326     pa_sink    *sink;
327     pa_source  *source;
328     uint32_t    idx;
329     pa_bool_t   available;
330     const char *state;
331     pa_bool_t   route;
332
333     pa_assert(u);
334     pa_assert(port);
335     pa_assert_se((core = u->core));
336
337     switch (port->available) {
338     case PA_PORT_AVAILABLE_NO:    state = "not ";  available = FALSE;   break;
339     case PA_PORT_AVAILABLE_YES:   state = "";      available = TRUE;    break;
340     default:                      /*       do nothing              */   return;
341     }
342
343     pa_log_debug("port '%s' availabilty changed to %savailable. Updating",
344                  port->name, state);
345
346     route = FALSE;
347
348     if (port->is_output) {
349         PA_IDXSET_FOREACH(sink, core->sinks, idx) {
350             if (sink->ports) {
351                 if (port == pa_hashmap_get(sink->ports, port->name)) {
352                     pa_log_debug("   sink '%s'", sink->name);
353                     route |= update_node_availability(u, mir_output,sink,port,
354                                                       available);
355                 }
356             }
357         }
358     }
359
360     if (port->is_input) {
361         PA_IDXSET_FOREACH(source, core->sources, idx) {
362             if (source->ports) {
363                 if (port == pa_hashmap_get(source->ports, port->name)) {
364                     pa_log_debug("   source '%s'", source->name);
365                     route |= update_node_availability(u, mir_input,source,port,
366                                                       available);
367                 }
368             }
369         }
370     }
371
372     if (route)
373         mir_router_make_routing(u);
374 }
375
376 void pa_discover_add_sink(struct userdata *u, pa_sink *sink, pa_bool_t route)
377 {
378     pa_core        *core;
379     pa_discover    *discover;
380     pa_module      *module;
381     mir_node       *node;
382     pa_card        *card;
383     char           *key;
384     char            kbf[256];
385     char            nbf[2048];
386     const char     *loopback_role;
387     pa_source      *ns;
388     mir_node        data;
389     mir_node_type   type;
390
391     pa_assert(u);
392     pa_assert(sink);
393     pa_assert_se((core = u->core));
394     pa_assert_se((discover = u->discover));
395
396     module = sink->module;
397
398     if ((card = sink->card)) {
399         if (!(key = node_key(u, mir_output,sink,ACTIVE_PORT, kbf,sizeof(kbf))))
400             return;
401         if (!(node = pa_discover_find_node_by_key(u, key))) {
402             if (u->state.profile)
403                 pa_log_debug("can't find node for sink (key '%s')", key);
404             else
405                 u->state.sink = sink->index;
406             return;
407         }
408         pa_log_debug("node for '%s' found (key %s). Updating with sink data",
409                      node->paname, node->key);
410         node->paidx = sink->index;
411         node->available = TRUE;
412         pa_discover_add_node_to_ptr_hash(u, sink, node);
413
414         if ((loopback_role = pa_classify_loopback_stream(node))) {
415             if (!(ns = pa_utils_get_null_source(u))) {
416                 pa_log("Can't load loopback module: no initial null source");
417                 return;
418             }
419             node->loop = pa_loopback_create(u->loopback, core, node->index,
420                                             ns->index, sink->index,
421                                             loopback_role);
422
423             mir_node_print(node, nbf, sizeof(nbf));
424             pa_log_debug("updated node:\n%s", nbf);
425         }
426
427         if (route) {
428             type = node->type;
429
430             if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
431                 mir_router_make_routing(u);
432             else {
433                 if (!u->state.profile)
434                     schedule_deferred_routing(u);
435             }
436         }
437     }
438     else if (!module || !pa_streq(module->name, "module-combine-sink")) {
439         memset(&data, 0, sizeof(data));
440         data.key = pa_xstrdup(sink->name);
441         data.direction = mir_output;
442         data.implement = mir_device;
443         data.channels  = sink->channel_map.channels;
444
445         if (sink == pa_utils_get_null_sink(u)) {
446             data.visible = FALSE;
447             data.available = TRUE;
448             data.type = mir_null;
449             data.amname = pa_xstrdup("Silent");
450             data.amid = AM_ID_INVALID;
451             data.paname = pa_xstrdup(sink->name);
452             data.paidx = sink->index;
453         }
454         else {
455             pa_xfree(data.key); /* for now */
456             pa_log_info("currently we do not support "
457                         "statically loaded sinks");
458             return;
459         }
460
461         create_node(u, &data, NULL);
462     }
463 }
464
465
466 void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
467 {
468     pa_discover    *discover;
469     mir_node       *node;
470     char           *name;
471     mir_node_type   type;
472
473     pa_assert(u);
474     pa_assert(sink);
475     pa_assert_se((discover = u->discover));
476
477     name = pa_utils_get_sink_name(sink);
478
479     if (!(node = pa_hashmap_get(discover->nodes.byptr, sink)))
480         pa_log_debug("can't find node for sink (name '%s')", name);
481     else {
482         pa_log_debug("node found for '%s'. Reseting sink data", name);
483         schedule_source_cleanup(u, node);
484         node->paidx = PA_IDXSET_INVALID;
485         pa_hashmap_remove(discover->nodes.byptr, sink);
486
487         type = node->type;
488
489         if (sink->card) {
490             if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
491                 node->available = FALSE;
492             else {
493                 if (!u->state.profile)
494                     schedule_deferred_routing(u);                
495             }
496         }
497         else {
498             pa_log_info("currently we do not support statically loaded sinks");
499         }
500     }
501 }
502
503
504 void pa_discover_add_source(struct userdata *u, pa_source *source)
505 {
506     pa_core        *core;
507     pa_discover    *discover;
508     mir_node       *node;
509     pa_card        *card;
510     char           *key;
511     char            kbf[256];
512     char            nbf[2048];
513     const char     *loopback_role;
514     uint32_t        sink_index;
515     pa_sink        *ns;
516     mir_node        data;
517
518     pa_assert(u);
519     pa_assert(source);
520     pa_assert_se((core = u->core));
521     pa_assert_se((discover = u->discover));
522
523     if ((card = source->card)) {
524         if (!(key = node_key(u,mir_input,source,ACTIVE_PORT,kbf,sizeof(kbf))))
525             return;
526         if (!(node = pa_discover_find_node_by_key(u, key))) {
527             if (u->state.profile)
528                 pa_log_debug("can't find node for source (key '%s')", key);
529             else
530                 u->state.source = source->index;
531             return;
532         }
533         pa_log_debug("node for '%s' found. Updating with source data",
534                      node->amname);
535         node->paidx = source->index;
536         node->available = TRUE;
537         pa_discover_add_node_to_ptr_hash(u, source, node);
538         if ((loopback_role = pa_classify_loopback_stream(node))) {
539             if (!(ns = pa_utils_get_null_sink(u))) {
540                 pa_log("Can't load loopback module: no initial null sink");
541                 return;
542             }
543             node->loop = pa_loopback_create(u->loopback, core, node->index,
544                                             source->index, ns->index,
545                                             loopback_role);
546             if (node->loop) {
547                 sink_index = pa_loopback_get_sink_index(core, node->loop);
548                 node->mux = pa_multiplex_find(u->multiplex, sink_index);
549             }
550
551             mir_node_print(node, nbf, sizeof(nbf));
552             pa_log_debug("updated node:\n%s", nbf);
553         }
554     }
555     else {
556         memset(&data, 0, sizeof(data));
557         data.key = pa_xstrdup(source->name);
558         data.direction = mir_input;
559         data.implement = mir_device;
560         data.channels  = source->channel_map.channels;
561
562         if (source == pa_utils_get_null_source(u)) {
563             data.visible = FALSE;
564             data.available = TRUE;
565             data.type = mir_null;
566             data.amname = pa_xstrdup("Silent");
567             data.amid = AM_ID_INVALID;
568             data.paname = pa_xstrdup(source->name);
569             data.paidx = source->index;
570         }
571         else {
572             pa_xfree(data.key); /* for now */
573             pa_log_info("currently we do not support "
574                         "statically loaded sources");
575             return;
576         }
577
578         create_node(u, &data, NULL);
579     }
580 }
581
582
583 void pa_discover_remove_source(struct userdata *u, pa_source *source)
584 {
585     pa_discover    *discover;
586     mir_node       *node;
587     char           *name;
588     mir_node_type   type;
589
590     pa_assert(u);
591     pa_assert(source);
592     pa_assert_se((discover = u->discover));
593
594     name = pa_utils_get_source_name(source);
595
596     if (!(node = pa_hashmap_get(discover->nodes.byptr, source)))
597         pa_log_debug("can't find node for source (name '%s')", name);
598     else {
599         pa_log_debug("node found. Reseting source data");
600         schedule_source_cleanup(u, node);
601         node->paidx = PA_IDXSET_INVALID;
602         pa_hashmap_remove(discover->nodes.byptr, source);
603
604         type = node->type;
605
606         if (source->card) {
607             if (type != mir_bluetooth_sco)
608                 node->available = FALSE;
609             else {
610                 if (!u->state.profile)
611                     schedule_deferred_routing(u);
612             }
613         }
614         else {
615             pa_log_info("currently we do not support statically "
616                         "loaded sources");
617         }
618     }
619 }
620
621
622 void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
623 {
624     pa_core       *core;
625     pa_discover   *discover;
626     pa_proplist   *pl;
627     char          *name;
628     const char    *media;
629     mir_node_type  type;
630     mir_node       data;
631     mir_node      *node;
632     mir_node      *target;
633     char           key[256];
634     pa_sink       *sink;
635     const char    *role;
636
637     pa_assert(u);
638     pa_assert(sinp);
639     pa_assert_se((core = u->core));
640     pa_assert_se((discover = u->discover));
641     pa_assert_se((pl = sinp->proplist));
642     
643     if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME))) {
644         if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
645             pa_log_debug("Seems to be a combine stream. Nothing to do ...");
646             return;
647         }
648         if (!strncmp(media, loopback_outpatrn, sizeof(loopback_outpatrn)-1)) {
649             pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
650             return;
651         }
652     }
653
654     name = pa_utils_get_sink_input_name(sinp);
655
656     pa_log_debug("registering input stream '%s'", name);
657
658     if (!(type = pa_classify_guess_stream_node_type(pl))) {
659         pa_log_debug("cant find stream class for '%s'. "
660                      "Leaving it alone", name);
661         return;
662     }
663
664     pa_utils_set_stream_routing_properties(pl, type, NULL);
665
666     snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
667
668     memset(&data, 0, sizeof(data));
669     data.key       = key;
670     data.direction = mir_input;
671     data.implement = mir_stream;
672     data.channels  = sinp->channel_map.channels;
673     data.type      = type;
674     data.visible   = TRUE;
675     data.available = TRUE;
676     data.amname    = name;
677     data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
678     data.amid      = AM_ID_INVALID;
679     data.paname    = name;
680     data.paidx     = sinp->index;
681
682     /*
683      * here we can't guess whether the application requested an explicit
684      * route by sepcifying the target sink @ stream creation time.
685      *
686      * the brute force solution: we make a default route for this stream
687      * possibly overwiriting the orginal app request :(
688      */
689     /* this will set data.mux */
690     role = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_ROLE);
691     sink = make_output_prerouting(u, &data, &sinp->channel_map, role, &target);
692
693     node = create_node(u, &data, NULL);
694     pa_assert(node);
695     pa_discover_add_node_to_ptr_hash(u, sinp, node);
696
697     if (sink && target) {
698         pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
699
700         if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
701             pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
702         else {
703             pa_log_debug("register route '%s' => '%s'",
704                          node->amname, target->amname);
705             /* FIXME: and actually do it ... */
706         }
707     }
708 }
709
710 void pa_discover_preroute_sink_input(struct userdata *u,
711                                      pa_sink_input_new_data *data)
712 {
713     pa_core       *core;
714     pa_module     *m;
715     pa_proplist   *pl;
716     pa_discover   *discover;
717     mir_node       fake;
718     pa_sink       *sink;
719     const char    *mnam;
720     const char    *role;
721     mir_node_type  type;
722     mir_node      *node;
723     
724     pa_assert(u);
725     pa_assert(data);
726     pa_assert_se((core = u->core));
727     pa_assert_se((discover = u->discover));
728     pa_assert_se((pl = data->proplist));
729
730     mnam = (m = data->module) ? m->name : "";
731
732     if (pa_streq(mnam, "module-combine-sink"))
733         type = mir_node_type_unknown;
734     else {
735         if (pa_streq(mnam, "module-loopback")) {
736
737             if (!(node = pa_utils_get_node_from_data(u, mir_input, data))) {
738                 pa_log_debug("can't find loopback node for sink-input");
739                 return;
740             }
741
742             if (node->direction == mir_output) {
743                 pa_log_debug("refuse to preroute loopback sink-input "
744                              "(current route: sink %u @ %p)", data->sink ?
745                              data->sink->index : PA_IDXSET_INVALID, sink);
746                 return;
747             }
748
749             data->sink = NULL;
750         }
751         type = pa_classify_guess_stream_node_type(pl);
752         pa_utils_set_stream_routing_properties(pl, type, data->sink);
753     }
754
755     if (!data->sink) {
756         memset(&fake, 0, sizeof(fake));
757         fake.direction = mir_input;
758         fake.implement = mir_stream;
759         fake.channels  = data->channel_map.channels;
760         fake.type      = type;
761         fake.visible   = TRUE;
762         fake.available = TRUE;
763         fake.amname    = "<preroute sink-input>";
764
765         role = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
766         sink = make_output_prerouting(u, &fake, &data->channel_map, role,NULL);
767
768         if (sink) {
769 #if 0
770             if (fake.mux && !(data->flags & PA_SINK_INPUT_START_CORKED)) {
771                 data->flags |= PA_SINK_INPUT_START_CORKED;
772                 schedule_stream_uncorking(u, sink);
773             }
774 #endif
775
776             if (pa_sink_input_new_data_set_sink(data, sink, FALSE))
777                 pa_log_debug("set sink %u for new sink-input", sink->index);
778             else 
779                 pa_log("can't set sink %u for new sink-input", sink->index);
780         }
781     }
782 }
783
784
785 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
786 {
787     pa_core        *core;
788     pa_sink        *s;
789     pa_sink_input  *csinp;
790     pa_proplist    *pl;
791     pa_discover    *discover;
792     mir_node        data;
793     mir_node       *node;
794     mir_node       *snod;
795     char           *name;
796     const char     *media;
797     mir_node_type   type;
798     char            key[256];
799     pa_bool_t       created;
800
801     pa_assert(u);
802     pa_assert(sinp);
803     pa_assert_se((core = u->core));
804     pa_assert_se((discover = u->discover));
805     pa_assert_se((pl = sinp->proplist));
806
807     if (!(media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)))
808         media = "<unknown>";
809
810     if (!strncmp(media, combine_pattern, sizeof(combine_pattern)-1)) {
811         pa_log_debug("New stream is a combine stream. Nothing to do ...");
812         return;
813     } else if (!strncmp(media, loopback_outpatrn,sizeof(loopback_outpatrn)-1)){
814         pa_log_debug("New stream is a loopback output stream");
815
816         if ((node = pa_utils_get_node_from_stream(u, mir_input, sinp))) {
817             if (node->direction == mir_input)
818                 pa_log_debug("loopback stream node '%s' found", node->amname);
819             else {
820                 pa_log_debug("ignoring it");
821                 return;
822             }
823         }
824         else {
825             pa_log_debug("can't find node for the loopback stream");
826             return;
827         }
828
829         s = sinp->sink;
830     }
831     else {
832         name = pa_utils_get_sink_input_name(sinp);
833
834         pa_log_debug("dealing with new input stream '%s'", name);
835         
836         if ((type = get_stream_routing_class(pl)) == mir_node_type_unknown) {
837             if (!(type = pa_classify_guess_stream_node_type(pl))) {
838                 pa_log_debug("cant find stream class for '%s'. "
839                              "Leaving it alone", name);
840                 return;
841             }
842             
843             pa_utils_set_stream_routing_properties(pl, type, NULL);
844             
845             /* if needed, make some post-routing here */
846         }
847         
848         /* we need to add this to main hashmap as that is used for loop
849            through on all nodes. */
850         snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
851         
852         memset(&data, 0, sizeof(data));
853         data.key       = key;
854         data.direction = mir_input;
855         data.implement = mir_stream;
856         data.channels  = sinp->channel_map.channels;
857         data.type      = type;
858         data.visible   = TRUE;
859         data.available = TRUE;
860         data.amname    = name;
861         data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
862         data.amid      = AM_ID_INVALID;
863         data.paname    = name;
864         data.paidx     = sinp->index;
865         data.mux       = pa_multiplex_find(u->multiplex, sinp->sink->index);
866         
867         node = create_node(u, &data, &created);
868
869         pa_assert(node);
870
871         if (!created) {
872             pa_log("%s: confused with stream. '%s' did exists",
873                    __FILE__, node->amname);
874             return;
875         }
876
877         pa_discover_add_node_to_ptr_hash(u, sinp, node);
878         
879         if (!data.mux)
880             s = sinp->sink;
881         else {
882             csinp = pa_idxset_get_by_index(core->sink_inputs,
883                                            data.mux->defstream_index);
884             s = csinp ? csinp->sink : NULL;
885         }
886     }
887        
888     if (s)
889         pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
890
891     if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
892         pa_log_debug("can't figure out where this stream is routed");
893     else {
894         pa_log_debug("register route '%s' => '%s'",
895                      node->amname, snod->amname);
896         /* FIXME: and actually do it ... */
897
898         pa_fader_apply_volume_limits(u, pa_utils_get_stamp());
899     }
900 }
901
902
903 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
904 {
905     pa_discover    *discover;
906     mir_node       *node;
907     mir_node       *sinknod;
908     char           *name;
909     pa_loopnode    *loop;
910
911     pa_assert(u);
912     pa_assert(sinp);
913     pa_assert_se((discover = u->discover));
914
915     name = pa_utils_get_sink_input_name(sinp);
916
917     pa_log("sink-input '%s' going to be destroyed", name);
918
919     if (!(node = pa_discover_remove_node_from_ptr_hash(u, sinp)))
920         pa_log_debug("can't find node for sink-input (name '%s')", name);
921     else {
922         pa_log_debug("node found for '%s'. After clearing routes "
923                      "it will be destroyed", name);
924
925         if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
926             pa_log_debug("can't figure out where this stream is routed");
927         else {
928             pa_log_debug("clear route '%s' => '%s'",
929                          node->amname, sinknod->amname);
930             
931             /* FIXME: and actually do it ... */
932             
933         }
934             
935         destroy_node(u, node);
936         
937         mir_router_make_routing(u);
938     }
939 }
940
941
942 void pa_discover_register_source_output(struct userdata  *u,
943                                         pa_source_output *sout)
944 {
945     pa_core       *core;
946     pa_discover   *discover;
947     pa_proplist   *pl;
948     char          *name;
949     const char    *media;
950     mir_node_type  type;
951     mir_node       data;
952     mir_node      *node;
953     mir_node      *target;
954     char           key[256];
955     pa_source     *source;
956     const char    *role;
957
958     pa_assert(u);
959     pa_assert(sout);
960     pa_assert_se((core = u->core));
961     pa_assert_se((discover = u->discover));
962     pa_assert_se((pl = sout->proplist));
963     
964     if ((media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME))) {
965         if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
966             pa_log_debug("Seems to be a loopback stream. Nothing to do ...");
967             return;
968         }
969     }
970
971     name = pa_utils_get_source_output_name(sout);
972
973     pa_log_debug("registering output stream '%s'", name);
974
975     if (!(type = pa_classify_guess_stream_node_type(pl))) {
976         pa_log_debug("cant find stream class for '%s'. "
977                      "Leaving it alone", name);
978         return;
979     }
980
981     pa_utils_set_stream_routing_properties(pl, type, NULL);
982
983     snprintf(key, sizeof(key), "stream_output.%d", sout->index);
984
985     memset(&data, 0, sizeof(data));
986     data.key       = key;
987     data.direction = mir_output;
988     data.implement = mir_stream;
989     data.channels  = sout->channel_map.channels;
990     data.type      = type;
991     data.visible   = TRUE;
992     data.available = TRUE;
993     data.amname    = name;
994     data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
995     data.amid      = AM_ID_INVALID;
996     data.paname    = name;
997     data.paidx     = sout->index;
998
999     /*
1000      * here we can't guess whether the application requested an explicit
1001      * route by sepcifying the target source @ stream creation time.
1002      *
1003      * the brute force solution: we make a default route for this stream
1004      * possibly overwiriting the orginal app request :(
1005      */
1006     role   = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_ROLE);
1007     source = make_input_prerouting(u, &data, role, &target);
1008
1009     node = create_node(u, &data, NULL);
1010     pa_assert(node);
1011     pa_discover_add_node_to_ptr_hash(u, sout, node);
1012
1013     if (source && target) {
1014         pa_log_debug("move stream to source %u (%s)",
1015                      source->index, source->name);
1016
1017         if (pa_source_output_move_to(sout, source, FALSE) < 0)
1018             pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
1019         else {
1020             pa_log_debug("register route '%s' => '%s'",
1021                          node->amname, target->amname);
1022             /* FIXME: and actually do it ... */
1023         }
1024     }
1025 }
1026
1027 void pa_discover_preroute_source_output(struct userdata *u,
1028                                         pa_source_output_new_data *data)
1029 {
1030     pa_core       *core;
1031     pa_module     *m;
1032     pa_proplist   *pl;
1033     pa_discover   *discover;
1034     mir_node       fake;
1035     pa_source     *source;
1036     const char    *mnam;
1037     const char    *role;
1038     mir_node_type  type;
1039     mir_node      *node;
1040     
1041     pa_assert(u);
1042     pa_assert(data);
1043     pa_assert_se((core = u->core));
1044     pa_assert_se((discover = u->discover));
1045     pa_assert_se((pl = data->proplist));
1046
1047     mnam = (m = data->module) ? m->name : "";
1048
1049     if (pa_streq(mnam, "module-loopback")) {
1050         if (!(node = pa_utils_get_node_from_data(u, mir_output, data))) {
1051             pa_log_debug("can't find loopback node for source-output");
1052             return;
1053         }
1054
1055         if (node->direction == mir_input) {
1056             pa_log_debug("refuse to preroute loopback source-output "
1057                          "(current route: source %u @ %p)", data->source ?
1058                          data->source->index : PA_IDXSET_INVALID, source);
1059             return;
1060         }
1061
1062         data->source = NULL;
1063     }
1064     type = pa_classify_guess_stream_node_type(pl);
1065     pa_utils_set_stream_routing_properties(pl, type, data->source);
1066
1067     if (!data->source) {
1068         memset(&fake, 0, sizeof(fake));
1069         fake.direction = mir_output;
1070         fake.implement = mir_stream;
1071         fake.channels  = data->channel_map.channels;
1072         fake.type      = type;
1073         fake.visible   = TRUE;
1074         fake.available = TRUE;
1075         fake.amname    = "<preroute source-output>";
1076
1077         role   = pa_proplist_gets(data->proplist, PA_PROP_MEDIA_ROLE);
1078         source = make_input_prerouting(u, &fake, role, NULL);
1079
1080         if (source) {
1081             if (pa_source_output_new_data_set_source(data, source, FALSE))
1082                 pa_log_debug("set source %u for new source-output");
1083             else {
1084                 pa_log("can't set source %u for new source-output",
1085                        source->index);
1086             }
1087         }
1088     }
1089 }
1090
1091
1092 void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
1093 {
1094     pa_core        *core;
1095     pa_source      *s;
1096     pa_proplist    *pl;
1097     pa_discover    *discover;
1098     mir_node        data;
1099     mir_node       *node;
1100     mir_node       *snod;
1101     char           *name;
1102     const char     *media;
1103     mir_node_type   type;
1104     char            key[256];
1105     pa_bool_t       created;
1106
1107     pa_assert(u);
1108     pa_assert(sout);
1109     pa_assert_se((core = u->core));
1110     pa_assert_se((discover = u->discover));
1111     pa_assert_se((pl = sout->proplist));
1112
1113     if (!(media = pa_proplist_gets(sout->proplist, PA_PROP_MEDIA_NAME)))
1114         media = "<unknown>";
1115
1116     if (!strncmp(media, loopback_inpatrn, sizeof(loopback_inpatrn)-1)) {
1117         pa_log_debug("New stream is a loopback input stream");
1118
1119         if ((node = pa_utils_get_node_from_stream(u, mir_output, sout))) {
1120             if (node->direction == mir_output)
1121                 pa_log_debug("loopback stream node '%s' found", node->amname);
1122             else {
1123                 pa_log_debug("ignoring it");
1124                 return;
1125             }
1126         }
1127         else {
1128             pa_log_debug("can't find node for the loopback stream");
1129             return;
1130         }
1131     }
1132     else {
1133         name = pa_utils_get_source_output_name(sout);
1134
1135         pa_log_debug("dealing with new output stream '%s'", name);
1136         
1137         if ((type = get_stream_routing_class(pl)) == mir_node_type_unknown) {
1138             if (!(type = pa_classify_guess_stream_node_type(pl))) {
1139                 pa_log_debug("cant find stream class for '%s'. "
1140                              "Leaving it alone", name);
1141                 return;
1142             }
1143             
1144             pa_utils_set_stream_routing_properties(pl, type, NULL);
1145             
1146             /* if needed, make some post-routing here */
1147         }
1148         
1149         /* we need to add this to main hashmap as that is used for loop
1150            through on all nodes. */
1151         snprintf(key, sizeof(key), "stream_output.%d", sout->index);
1152         
1153         memset(&data, 0, sizeof(data));
1154         data.key       = key;
1155         data.direction = mir_output;
1156         data.implement = mir_stream;
1157         data.channels  = sout->channel_map.channels;
1158         data.type      = type;
1159         data.visible   = TRUE;
1160         data.available = TRUE;
1161         data.amname    = name;
1162         data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
1163         data.amid      = AM_ID_INVALID;
1164         data.paname    = name;
1165         data.paidx     = sout->index;
1166         
1167         node = create_node(u, &data, &created);
1168
1169         pa_assert(node);
1170
1171         if (!created) {
1172             pa_log("%s: confused with stream. '%s' did exists",
1173                    __FILE__, node->amname);
1174             return;
1175         }
1176
1177         pa_discover_add_node_to_ptr_hash(u, sout, node);        
1178     }
1179
1180     if ((s = sout->source))
1181         pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
1182
1183     if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
1184         pa_log_debug("can't figure out where this stream is routed");
1185     else {
1186         pa_log_debug("register route '%s' => '%s'",
1187                      snod->amname, node->amname);
1188         /* FIXME: and actually do it ... */
1189
1190     }
1191 }
1192
1193
1194 void pa_discover_remove_source_output(struct userdata  *u,
1195                                       pa_source_output *sout)
1196 {
1197     pa_discover    *discover;
1198     mir_node       *node;
1199     mir_node       *srcnod;
1200     char           *name;
1201     pa_loopnode    *loop;
1202
1203     pa_assert(u);
1204     pa_assert(sout);
1205     pa_assert_se((discover = u->discover));
1206
1207     name = pa_utils_get_source_output_name(sout);
1208
1209     pa_log("source-output '%s' going to be destroyed", name);
1210
1211     if (!(node = pa_discover_remove_node_from_ptr_hash(u, sout)))
1212         pa_log_debug("can't find node for source-output (name '%s')", name);
1213     else {
1214         pa_log_debug("node found for '%s'. After clearing routes "
1215                      "it will be destroyed", name);
1216
1217         if (!(srcnod = pa_hashmap_get(discover->nodes.byptr, sout->source)))
1218             pa_log_debug("can't figure out where this stream is routed");
1219         else {
1220             pa_log_debug("clear route '%s' => '%s'",
1221                          node->amname, srcnod->amname);
1222             
1223             /* FIXME: and actually do it ... */
1224             
1225         }
1226             
1227         destroy_node(u, node);
1228         
1229         mir_router_make_routing(u);
1230     }
1231 }
1232
1233
1234 mir_node *pa_discover_find_node_by_key(struct userdata *u, const char *key)
1235 {
1236     pa_discover *discover;
1237     mir_node    *node;
1238
1239     pa_assert(u);
1240     pa_assert_se((discover = u->discover));
1241
1242     if (key)
1243         node = pa_hashmap_get(discover->nodes.byname, key);
1244     else
1245         node = NULL;
1246
1247     return node;
1248 }
1249
1250 mir_node *pa_discover_find_node_by_ptr(struct userdata *u, void *ptr)
1251 {
1252     pa_discover *discover;
1253     mir_node    *node;
1254     
1255     pa_assert(u);
1256     pa_assert_se((discover = u->discover));
1257     
1258     if (ptr)
1259         node = pa_hashmap_get(discover->nodes.byptr, ptr);
1260     else
1261         node = NULL;
1262
1263     return node;
1264 }
1265
1266 void pa_discover_add_node_to_ptr_hash(struct userdata *u,
1267                                       void *ptr,
1268                                       mir_node *node)
1269 {
1270     pa_discover *discover;
1271
1272     pa_assert(u);
1273     pa_assert(ptr);
1274     pa_assert(node);
1275     pa_assert_se((discover = u->discover));
1276
1277     pa_hashmap_put(discover->nodes.byptr, ptr, node);
1278 }
1279
1280 mir_node *pa_discover_remove_node_from_ptr_hash(struct userdata *u, void *ptr)
1281 {
1282     pa_discover *discover;
1283
1284     pa_assert(u);
1285     pa_assert(ptr);
1286     pa_assert_se((discover = u->discover));
1287
1288     return pa_hashmap_remove(discover->nodes.byptr, ptr);
1289 }
1290
1291
1292 static void handle_alsa_card(struct userdata *u, pa_card *card)
1293 {
1294     mir_node         data;
1295     const char      *udd;
1296     char            *cnam;
1297     char            *cid;
1298
1299     memset(&data, 0, sizeof(data));
1300     data.visible = TRUE;
1301     data.amid = AM_ID_INVALID;
1302     data.implement = mir_device;
1303     data.paidx = PA_IDXSET_INVALID;
1304     data.stamp = pa_utils_get_stamp();
1305
1306     cnam = pa_utils_get_card_name(card);
1307     udd  = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
1308
1309     if (udd && pa_streq(udd, "1")) {
1310         /* udev loaded alsa card */
1311         if (!strncmp(cnam, "alsa_card.", 10)) {
1312             cid = cnam + 10;
1313             handle_udev_loaded_card(u, card, &data, cid);
1314             return;
1315         }
1316     }
1317     else {
1318         /* statically loaded pci or usb card */
1319     }
1320
1321     pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
1322 }
1323
1324
1325 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
1326 {
1327     pa_discover     *discover;
1328     pa_card_profile *prof;
1329     mir_node         data;
1330     mir_node        *node;
1331     mir_constr_def  *cd;
1332     char            *cnam;
1333     char            *cid;
1334     const char      *cdescr;
1335     void            *state;
1336     char             paname[MAX_NAME_LENGTH+1];
1337     char             amname[MAX_NAME_LENGTH+1];
1338     char             key[MAX_NAME_LENGTH+1];
1339     
1340
1341     pa_assert_se((discover = u->discover));
1342
1343     cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
1344
1345
1346     memset(paname, 0, sizeof(paname));
1347     memset(amname, 0, sizeof(amname));
1348     memset(key   , 0, sizeof(key)   );
1349
1350     memset(&data, 0, sizeof(data));
1351     data.key = key;
1352     data.visible = TRUE;
1353     data.amid = AM_ID_INVALID;
1354     data.implement = mir_device;
1355     data.paidx = PA_IDXSET_INVALID;
1356     data.paname = paname;
1357     data.amname = amname;
1358     data.amdescr = (char *)cdescr;
1359     data.pacard.index = card->index;
1360     data.stamp = pa_utils_get_stamp();
1361
1362     cnam = pa_utils_get_card_name(card);
1363
1364     if (!strncmp(cnam, "bluez_card.", 11)) { 
1365         cid = cnam + 11;
1366
1367         cd = mir_constrain_create(u, "profile", mir_constrain_profile, cnam);
1368
1369         PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1370             data.available = TRUE;
1371             data.pacard.profile = prof->name;
1372
1373             if (prof->n_sinks > 0) {
1374                 data.direction = mir_output;
1375                 data.channels = prof->max_sink_channels; 
1376                 data.amname = amname;
1377                 amname[0] = '\0';
1378                 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
1379                 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1380                 pa_classify_node_by_card(&data, card, prof, NULL);
1381                 node = create_node(u, &data, NULL);
1382 #if 0
1383                 cd = mir_constrain_create(u, "profile", mir_constrain_profile,
1384                                           paname);
1385 #endif
1386                 mir_constrain_add_node(u, cd, node);
1387             }
1388
1389             if (prof->n_sources > 0) {
1390                 data.direction = mir_input;
1391                 data.channels = prof->max_source_channels; 
1392                 data.amname = amname;
1393                 amname[0] = '\0';
1394                 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
1395                 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
1396                 pa_classify_node_by_card(&data, card, prof, NULL);
1397                 node = create_node(u, &data, NULL);
1398 #if 0
1399                 cd = mir_constrain_create(u, "profile", mir_constrain_profile,
1400                                           paname);
1401 #endif
1402                 mir_constrain_add_node(u, cd, node);
1403             }
1404         }
1405
1406         if (!(prof = card->active_profile))
1407             pa_log("card '%s' has no active profile", card->name);
1408         else {
1409             pa_log_debug("card '%s' default profile '%s'",
1410                          card->name, prof->name);
1411         }
1412
1413         schedule_card_check(u, card);
1414     }
1415 }
1416
1417
1418 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
1419                                     mir_node *data, char *cardid)
1420 {
1421     pa_discover      *discover;
1422     pa_card_profile  *prof;
1423     pa_card_profile  *active;
1424     void             *state;
1425     const char       *alsanam;
1426     char             *sid;
1427     char             *sinks[MAX_CARD_TARGET+1];
1428     char             *sources[MAX_CARD_TARGET+1];
1429     char              buf[MAX_NAME_LENGTH+1];
1430     char              paname[MAX_NAME_LENGTH+1];
1431     char              amname[MAX_NAME_LENGTH+1];
1432     int               i;
1433
1434     pa_assert(card);
1435     pa_assert(card->profiles);
1436     pa_assert_se((discover = u->discover));
1437
1438     alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
1439
1440     memset(amname, 0, sizeof(amname));
1441
1442     data->paname  = paname;
1443     data->amname  = amname;
1444     data->amdescr = (char *)alsanam;
1445
1446     data->pacard.index = card->index;
1447
1448     active = card->active_profile;
1449
1450     PA_HASHMAP_FOREACH(prof, card->profiles, state) {
1451         /* filtering: deal with selected profiles if requested so */
1452         if (discover->selected && (!active || (active && prof != active)))
1453             continue;
1454
1455         /* filtering: skip the 'off' profiles */
1456         if (!prof->n_sinks && !prof->n_sources)
1457             continue;
1458
1459         /* filtering: consider sinks with suitable amount channels */
1460         if (prof->n_sinks && 
1461             (prof->max_sink_channels < discover->chmin ||
1462              prof->max_sink_channels  > discover->chmax  ))
1463             continue;
1464         
1465         /* filtering: consider sources with suitable amount channels */
1466         if (prof->n_sources &&
1467             (prof->max_source_channels <  discover->chmin ||
1468              prof->max_source_channels >  discover->chmax   ))
1469             continue;
1470
1471         data->pacard.profile = prof->name;
1472
1473         parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
1474         
1475         data->direction = mir_output;
1476         data->channels = prof->max_sink_channels;
1477         for (i = 0;  (sid = sinks[i]);  i++) {
1478             snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
1479             handle_card_ports(u, data, card, prof);
1480         }
1481
1482         data->direction = mir_input;
1483         data->channels = prof->max_source_channels;
1484         for (i = 0;  (sid = sources[i]);  i++) {
1485             snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
1486             handle_card_ports(u, data, card, prof);
1487         }        
1488     }
1489 }
1490
1491
1492 static void handle_card_ports(struct userdata *u, mir_node *data,
1493                               pa_card *card, pa_card_profile *prof)
1494 {
1495     mir_node       *node = NULL;
1496     pa_bool_t       have_ports = FALSE;
1497     mir_constr_def *cd = NULL;
1498     char           *amname = data->amname;
1499     pa_device_port *port;
1500     void           *state;
1501     pa_bool_t       created;
1502     char            key[MAX_NAME_LENGTH+1];
1503
1504     pa_assert(u);
1505     pa_assert(data);
1506     pa_assert(card);
1507     pa_assert(prof);
1508
1509     if (card->ports) {        
1510         PA_HASHMAP_FOREACH(port, card->ports, state) {
1511             /*
1512              * if this port did not belong to any profile 
1513              * (ie. prof->profiles == NULL) we assume that this port
1514              * does works with all the profiles
1515              */
1516             if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
1517                 ((port->is_input && data->direction == mir_input)||
1518                  (port->is_output && data->direction == mir_output)))
1519             {
1520                 have_ports = TRUE;
1521
1522                 amname[0] = '\0';
1523                 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
1524
1525                 data->key       = key;
1526                 data->available = (port->available != PA_PORT_AVAILABLE_NO);
1527                 data->type      = 0;
1528                 data->amname    = amname;
1529                 data->paport    = port->name;
1530
1531                 pa_classify_node_by_card(data, card, prof, port);
1532
1533                 node = create_node(u, data, &created);
1534
1535                 if (!created)
1536                     node->stamp = data->stamp;
1537                 else {
1538                     cd = mir_constrain_create(u, "port", mir_constrain_port,
1539                                               data->paname);
1540                     mir_constrain_add_node(u, cd, node);
1541                 }
1542             }
1543         }
1544     }
1545     
1546     if (!have_ports) {
1547         data->key = data->paname;
1548         data->available = TRUE;
1549
1550         pa_classify_node_by_card(data, card, prof, NULL);
1551
1552         node = create_node(u, data, &created);
1553
1554         if (!created)
1555             node->stamp = data->stamp;
1556     }
1557
1558     data->amname = amname;
1559     *amname = '\0';
1560 }
1561
1562
1563 static mir_node *create_node(struct userdata *u, mir_node *data, 
1564                              pa_bool_t *created_ret)
1565 {
1566     pa_discover *discover;
1567     mir_node    *node;
1568     pa_bool_t    created;
1569     char         buf[2048];
1570
1571     pa_assert(u);
1572     pa_assert(data);
1573     pa_assert(data->key);
1574     pa_assert(data->paname);
1575     pa_assert_se((discover = u->discover));
1576     
1577     if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
1578         created = FALSE;
1579     else {
1580         created = TRUE;
1581         
1582         node = mir_node_create(u, data);
1583         pa_hashmap_put(discover->nodes.byname, node->key, node);
1584         
1585         mir_node_print(node, buf, sizeof(buf));
1586         pa_log_debug("new node:\n%s", buf);
1587         
1588         if (node->available)
1589             pa_audiomgr_register_node(u, node);
1590     }
1591     
1592     if (created_ret)
1593         *created_ret = created;
1594     
1595     return node;
1596 }
1597
1598 static void destroy_node(struct userdata *u, mir_node *node)
1599 {
1600     pa_discover *discover;
1601     mir_node    *removed;
1602
1603     pa_assert(u);
1604     pa_assert_se((discover = u->discover));
1605
1606     if (node) {
1607         removed = pa_hashmap_remove(discover->nodes.byname, node->key);
1608
1609         if (node != removed) {
1610             if (removed)
1611                 pa_log("%s: confused with data structures: key mismatch. "
1612                        " attempted to destroy '%s'; actually destroyed '%s'",
1613                        __FILE__, node->key, removed->key);
1614             else
1615                 pa_log("%s: confused with data structures: node '%s' "
1616                        "is not in the hash table", __FILE__, node->key);
1617             return;
1618         }
1619
1620         pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
1621
1622         if (node->implement == mir_stream) {
1623             if (node->direction == mir_input) {
1624                 if (node->mux) {
1625                     pa_log_debug("removing multiplexer"); 
1626                 }
1627             }
1628         }
1629         
1630         pa_audiomgr_unregister_node(u, node);
1631
1632         mir_constrain_remove_node(u, node);
1633
1634         pa_loopback_destroy(u->loopback, u->core, node->loop);
1635         pa_multiplex_destroy(u->multiplex, u->core, node->mux);
1636         
1637         mir_node_destroy(u, node);
1638     }    
1639 }
1640
1641 static pa_bool_t update_node_availability(struct userdata *u,
1642                                           mir_direction direction,
1643                                           void *data, pa_device_port *port,
1644                                           pa_bool_t available)
1645 {
1646     mir_node *node;
1647     char     *key;
1648     char      buf[256];
1649
1650     pa_assert(u);
1651     pa_assert(data);
1652     pa_assert(port);
1653     pa_assert(direction == mir_input || direction == mir_output);
1654
1655     if ((key = node_key(u, direction, data, port, buf, sizeof(buf)))) {
1656         if (!(node = pa_discover_find_node_by_key(u, key)))
1657             pa_log_debug("      can't find node for sink (key '%s')", key);
1658         else {
1659             pa_log_debug("      node for '%s' found (key %s)",
1660                          node->paname, node->key);
1661             if ((!available &&  node->available) ||
1662                 ( available && !node->available)  )
1663             {
1664                 node->available = available;
1665
1666                 if (available)
1667                     pa_audiomgr_register_node(u, node);
1668                 else
1669                     pa_audiomgr_unregister_node(u, node);
1670
1671                 return TRUE; /* routing needed */
1672             }
1673         }
1674     }
1675
1676     return FALSE; /* no routing needed */
1677 }
1678
1679 static char *get_name(char **string_ptr, int offs)
1680 {
1681     char c, *name, *end;
1682
1683     name = *string_ptr + offs;
1684
1685     for (end = name;  (c = *end);   end++) {
1686         if (c == '+') {
1687             *end++ = '\0';
1688             break;
1689         }
1690     }
1691
1692     *string_ptr = end;
1693
1694     return name;
1695
1696
1697 static void parse_profile_name(pa_card_profile *prof,
1698                                char           **sinks,
1699                                char           **sources,
1700                                char            *buf,
1701                                int              buflen)
1702 {
1703     char *p = buf;
1704     int   i = 0;
1705     int   j = 0;
1706
1707     pa_assert(prof->name);
1708
1709     strncpy(buf, prof->name, buflen);
1710     buf[buflen-1] = '\0';
1711
1712     memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
1713     memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
1714
1715     do {
1716         if (!strncmp(p, "output:", 7)) {
1717             if (i >= MAX_CARD_TARGET) {
1718                 pa_log_debug("number of outputs exeeds the maximum %d in "
1719                              "profile name '%s'", MAX_CARD_TARGET, prof->name);
1720                 return;
1721             }
1722             sinks[i++] = get_name(&p, 7);
1723         } 
1724         else if (!strncmp(p, "input:", 6)) {
1725             if (j >= MAX_CARD_TARGET) {
1726                 pa_log_debug("number of inputs exeeds the maximum %d in "
1727                              "profile name '%s'", MAX_CARD_TARGET, prof->name);
1728                 return;
1729             }
1730             sources[j++] = get_name(&p, 6);            
1731         }
1732         else {
1733             pa_log("%s: failed to parse profile name '%s'",
1734                    __FILE__, prof->name);
1735             return;
1736         }
1737     } while (*p);
1738 }
1739
1740
1741 static char *node_key(struct userdata *u, mir_direction direction,
1742                       void *data, pa_device_port *port, char *buf, size_t len)
1743 {
1744     pa_card         *card;
1745     pa_card_profile *profile;
1746     const char      *bus;
1747     pa_bool_t        pci;
1748     pa_bool_t        usb;
1749     pa_bool_t        bluetooth;
1750     char            *type;
1751     char            *name;
1752     const char      *profile_name;
1753     char            *key;
1754
1755     pa_assert(u);
1756     pa_assert(data);
1757     pa_assert(direction == mir_input || direction == mir_output);
1758
1759     if (direction == mir_input) {
1760         pa_sink *sink = data;
1761         type = "sink";
1762         name = pa_utils_get_sink_name(sink);
1763         card = sink->card;
1764         if (!port)
1765             port = sink->active_port;
1766     }
1767     else {
1768         pa_source *source = data;
1769         type = "source";
1770         name = pa_utils_get_source_name(source);
1771         card = source->card;
1772         if (!port)
1773             port = source->active_port;
1774     }
1775
1776     if (!card)
1777         return NULL;
1778         
1779     pa_assert_se((profile = card->active_profile));
1780
1781     if (!u->state.profile)
1782         profile_name = profile->name;
1783     else {
1784         pa_log_debug("state.profile is not null. '%s' supresses '%s'",
1785                      u->state.profile, profile->name);
1786         profile_name = u->state.profile;
1787     }
1788         
1789
1790     if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS))) {
1791         pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
1792                      "on its card", type, name, PA_PROP_DEVICE_BUS);
1793         return NULL;
1794     }
1795     
1796     pci = pa_streq(bus, "pci");
1797     usb = pa_streq(bus, "usb");
1798     bluetooth = pa_streq(bus, "bluetooth");
1799     
1800     if (!pci && !usb && !bluetooth) {
1801         pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
1802                      "of its card", type, name, bus);
1803         return NULL;
1804     }
1805     
1806     if (bluetooth) {
1807         key = buf;
1808         snprintf(buf, len, "%s@%s", name, profile_name);
1809     }
1810     else {
1811         if (!port)
1812             key = name;
1813         else {
1814             key = buf;
1815             snprintf(buf, len, "%s@%s", name, port->name);
1816         }
1817     }
1818
1819     return key;
1820 }
1821
1822 static pa_sink *make_output_prerouting(struct userdata *u,
1823                                        mir_node        *data,
1824                                        pa_channel_map  *chmap,
1825                                        const char      *media_role,
1826                                        mir_node       **target_ret)
1827 {
1828     pa_core    *core;
1829     mir_node   *target;
1830     pa_sink    *sink = NULL;
1831
1832     pa_assert(u);
1833     pa_assert(data);
1834     pa_assert(chmap);
1835     pa_assert_se((core = u->core));
1836
1837         
1838     target = mir_router_make_prerouting(u, data);
1839
1840     if (!target)
1841         pa_log("there is no default route for the stream '%s'", data->amname);
1842     else if (target->paidx == PA_IDXSET_INVALID)
1843         pa_log("can't route to default '%s': no sink", target->amname);
1844     else {
1845         if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
1846             pa_log("no route to default '%s': sink is gone", target->amname);
1847         else {
1848             if (pa_classify_multiplex_stream(data)) {
1849                 data->mux = pa_multiplex_create(u->multiplex, core,
1850                                                 sink->index, chmap, NULL,
1851                                                 media_role, data->type);
1852                 if (data->mux) {
1853                     sink = pa_idxset_get_by_index(core->sinks,
1854                                                   data->mux->sink_index);
1855                     pa_assert(sink);
1856                 }
1857             }
1858         }
1859     }
1860
1861     if (target_ret)
1862         *target_ret = target;
1863
1864     return sink;
1865 }
1866
1867
1868 static pa_source *make_input_prerouting(struct userdata *u,
1869                                         mir_node        *data,
1870                                         const char      *media_role,
1871                                         mir_node       **target_ret)
1872 {
1873     pa_core    *core;
1874     mir_node   *target;
1875     pa_source  *source = NULL;
1876
1877     pa_assert(u);
1878     pa_assert(data);
1879     pa_assert_se((core = u->core));
1880         
1881     target = mir_router_make_prerouting(u, data);
1882
1883     if (!target)
1884         pa_log("there is no default route for the stream '%s'", data->amname);
1885     else if (target->paidx == PA_IDXSET_INVALID)
1886         pa_log("can't route to default '%s': no source", target->amname);
1887     else {
1888         if (!(source = pa_idxset_get_by_index(core->sources, target->paidx)))
1889             pa_log("no route to default '%s': source is gone",target->amname);
1890     }
1891
1892     if (target_ret)
1893         *target_ret = target;
1894
1895     return source;
1896 }
1897
1898 static mir_node_type get_stream_routing_class(pa_proplist *pl)
1899 {
1900     const char    *clid;
1901     mir_node_type  type;
1902     char          *e;
1903
1904     pa_assert(pl);
1905
1906     if ((clid = pa_proplist_gets(pl, PA_PROP_ROUTING_CLASS_ID))) {
1907         type = strtol(clid, &e, 10);
1908
1909         if (!*e) {
1910             if (type >= mir_application_class_begin &&
1911                 type <  mir_application_class_end)
1912             {
1913                 return type;
1914             }
1915         }                
1916     }
1917
1918     return mir_node_type_unknown;
1919 }
1920
1921
1922
1923 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
1924 {
1925     struct userdata *u = d;
1926
1927     (void)m;
1928
1929     pa_assert(u);
1930
1931     pa_log_debug("deferred routing starts");
1932
1933     mir_router_make_routing(u);
1934 }
1935
1936
1937 static void schedule_deferred_routing(struct userdata *u)
1938 {
1939     pa_core *core;
1940
1941     pa_assert(u);
1942     pa_assert_se((core = u->core));
1943
1944     pa_log_debug("scheduling deferred routing");
1945
1946     pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
1947 }
1948
1949
1950 static void card_check_cb(pa_mainloop_api *m, void *d)
1951 {
1952     card_check_t *cc = d;
1953     struct userdata *u;
1954     pa_core *core;
1955     pa_card *card;
1956     pa_sink *sink;
1957     pa_source *source;
1958     int n_sink, n_source;
1959     uint32_t idx;
1960
1961     (void)m;
1962
1963     pa_assert(cc);
1964     pa_assert((u = cc->u));
1965     pa_assert((core = u->core));
1966
1967     pa_log_debug("card check starts");
1968
1969     if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
1970         pa_log_debug("card %u is gone", cc->index);
1971     else {
1972         n_sink = n_source = 0;
1973
1974         PA_IDXSET_FOREACH(sink, core->sinks, idx) {
1975             if ((sink->card) && sink->card->index == card->index)
1976                 n_sink++;
1977         }
1978
1979         PA_IDXSET_FOREACH(source, core->sources, idx) {
1980             if ((source->card) && source->card->index == card->index)
1981                 n_sink++;
1982         }
1983
1984         if (n_sink || n_source) {
1985             pa_log_debug("found %u sinks and %u sources belonging to "
1986                          "'%s' card", n_sink, n_source, card->name);
1987             pa_log_debug("nothing to do");
1988         }
1989         else {
1990             pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
1991                          card->name);
1992             mir_router_make_routing(u);
1993         }
1994     }
1995     
1996     pa_xfree(cc);
1997 }
1998
1999
2000 static void schedule_card_check(struct userdata *u, pa_card *card)
2001 {
2002     pa_core *core;
2003     card_check_t *cc;
2004
2005     pa_assert(u);
2006     pa_assert(card);
2007     pa_assert_se((core = u->core));
2008
2009     pa_log_debug("scheduling card check");
2010
2011     cc = pa_xnew0(card_check_t, 1);
2012     cc->u = u;
2013     cc->index = card->index;
2014
2015     pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
2016 }
2017
2018                                   
2019 static void source_cleanup_cb(pa_mainloop_api *m, void *d)
2020 {
2021     source_cleanup_t *sc = d;
2022     struct userdata *u;
2023     pa_core *core;
2024
2025     (void)m;
2026
2027     pa_assert(sc);
2028     pa_assert((u = sc->u));
2029     pa_assert((core = u->core));
2030
2031     pa_log_debug("source cleanup starts");
2032
2033     pa_loopback_destroy(u->loopback, u->core, sc->loop);
2034     pa_multiplex_destroy(u->multiplex, u->core, sc->mux);
2035
2036     pa_log_debug("source cleanup ends");
2037     
2038     pa_xfree(sc);
2039 }
2040
2041
2042 static void schedule_source_cleanup(struct userdata *u, mir_node *node)
2043 {
2044     pa_core *core;
2045     source_cleanup_t *sc;
2046
2047     pa_assert(u);
2048     pa_assert(node);
2049     pa_assert_se((core = u->core));
2050
2051     pa_log_debug("scheduling source cleanup");
2052
2053     sc = pa_xnew0(source_cleanup_t, 1);
2054     sc->u = u;
2055     sc->mux = node->mux;
2056     sc->loop = node->loop;
2057
2058     node->mux = NULL;
2059     node->loop = NULL;
2060
2061     pa_mainloop_api_once(core->mainloop, source_cleanup_cb, sc);
2062 }
2063
2064
2065 static void stream_uncork_cb(pa_mainloop_api *m, void *d)
2066 {
2067     stream_uncork_t *suc = d;
2068     struct userdata *u;
2069     pa_core *core;
2070     pa_sink *sink;
2071     pa_sink_input *sinp;
2072     uint32_t index;
2073
2074     (void)m;
2075
2076     pa_assert(suc);
2077     pa_assert((u = suc->u));
2078     pa_assert((core = u->core));
2079
2080     pa_log_debug("start uncorking stream");
2081
2082     if (!(sink = pa_idxset_get_by_index(core->sinks, suc->index))) {
2083         pa_log_debug("sink.%d gone", suc->index);
2084         goto out;
2085     }
2086
2087     if (!(sinp = pa_idxset_first(core->sink_inputs, &index))) {
2088         pa_log_debug("sink_input is gone");
2089         goto out;
2090     }
2091
2092     pa_sink_input_cork(sinp, FALSE);
2093
2094     pa_log_debug("stream.%u uncorked", sinp->index);
2095
2096  out:
2097     
2098     pa_xfree(suc);
2099 }
2100
2101
2102 static void schedule_stream_uncorking(struct userdata *u, pa_sink *sink)
2103 {
2104     pa_core *core;
2105     stream_uncork_t *suc;
2106
2107     pa_assert(u);
2108     pa_assert(sink);
2109     pa_assert_se((core = u->core));
2110
2111     pa_log_debug("scheduling stream uncorking");
2112
2113     suc = pa_xnew0(stream_uncork_t, 1);
2114     suc->u = u;
2115     suc->index = sink->index;
2116
2117     pa_mainloop_api_once(core->mainloop, stream_uncork_cb, suc);
2118 }
2119
2120
2121 /*
2122  * Local Variables:
2123  * c-basic-offset: 4
2124  * indent-tabs-mode: nil
2125  * End:
2126  *
2127  */