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