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