router: fix for bluetooth disappearing causing assert in combine
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / discover.c
1 #include <stdio.h>
2
3 #include <pulsecore/pulsecore-config.h>
4
5 #include <pulsecore/hashmap.h>
6 #include <pulsecore/idxset.h>
7 #include <pulsecore/client.h>
8 #include <pulsecore/core-util.h>
9 #include <pulsecore/log.h>
10 #include <pulsecore/card.h>
11 #include <pulsecore/device-port.h>
12 #include <pulsecore/sink-input.h>
13 #include <pulsecore/source-output.h>
14 #include <pulsecore/strbuf.h>
15
16
17 #include "discover.h"
18 #include "node.h"
19 #include "audiomgr.h"
20 #include "router.h"
21 #include "multiplex.h"
22 #include "classify.h"
23 #include "utils.h"
24
25 #define MAX_CARD_TARGET   4
26 #define MAX_NAME_LENGTH   256
27
28 typedef struct {
29     struct userdata *u;
30     uint32_t index;
31 } card_check_t;
32
33 static const char combine_pattern[] = "Simultaneous output on ";
34
35 static void handle_alsa_card(struct userdata *, pa_card *);
36 static void handle_bluetooth_card(struct userdata *, pa_card *);
37
38 static void handle_udev_loaded_card(struct userdata *, pa_card *,
39                                     mir_node *, char *);
40 static void handle_card_ports(struct userdata *, mir_node *,
41                               pa_card *, pa_card_profile *);
42
43 static mir_node *create_node(struct userdata *, mir_node *, pa_bool_t *);
44 static void destroy_node(struct userdata *, mir_node *);
45
46 static void parse_profile_name(pa_card_profile *,
47                                char **, char **, char *, int);
48
49 static char *node_key_from_card(struct userdata *, mir_direction,
50                                 void *, char *, size_t);
51
52 static pa_sink *make_output_prerouting(struct userdata *, mir_node *,
53                                        pa_channel_map *, mir_node **);
54
55 static mir_node_type get_stream_routing_class(pa_proplist *);
56
57
58 static void schedule_deferred_routing(struct userdata *);
59 static void schedule_card_check(struct userdata *, pa_card *);
60
61
62 static void pa_hashmap_node_free(void *node, void *u)
63 {
64     mir_node_destroy(u, node);
65 }
66
67
68 struct pa_discover *pa_discover_init(struct userdata *u)
69 {
70     pa_discover *discover = pa_xnew0(pa_discover, 1);
71
72     discover->chmin = 1;
73     discover->chmax = 2;
74     discover->selected = TRUE;
75
76     discover->nodes.byname = pa_hashmap_new(pa_idxset_string_hash_func,
77                                             pa_idxset_string_compare_func);
78     discover->nodes.byptr  = pa_hashmap_new(pa_idxset_trivial_hash_func,
79                                             pa_idxset_trivial_compare_func);
80     return discover;
81 }
82
83 void pa_discover_done(struct userdata *u)
84 {
85     pa_discover *discover;
86
87     if (u && (discover = u->discover)) {
88         pa_hashmap_free(discover->nodes.byname, pa_hashmap_node_free,u);
89         pa_hashmap_free(discover->nodes.byptr, NULL,NULL);
90         pa_xfree(discover);
91         u->discover = NULL;
92     }
93 }
94
95 void pa_discover_domain_up(struct userdata *u)
96 {
97     pa_discover *discover;
98     mir_node    *node;
99     void        *state;
100
101     pa_assert(u);
102     pa_assert_se((discover = u->discover));
103
104     PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
105         node->amid = AM_ID_INVALID;
106
107         if (node->visible && node->available)
108             pa_audiomgr_register_node(u, node);
109     }
110 }
111
112 void pa_discover_domain_down(struct userdata *u)
113 {
114 }
115
116 void pa_discover_add_card(struct userdata *u, pa_card *card)
117 {
118     const char *bus;
119
120     pa_assert(u);
121     pa_assert(card);
122
123     if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS))) {
124         pa_log_debug("ignoring card '%s' due to lack of '%s' property",
125                      pa_utils_get_card_name(card), PA_PROP_DEVICE_BUS);
126         return;
127     }
128
129     if (pa_streq(bus, "pci") || pa_streq(bus, "usb")) {
130         handle_alsa_card(u, card);
131         return;
132     }
133     else if (pa_streq(bus, "bluetooth")) {
134         handle_bluetooth_card(u, card);
135         return;
136     }
137
138     pa_log_debug("ignoring card '%s' due to unsupported bus type '%s'",
139                  pa_utils_get_card_name(card), bus);
140 }
141
142 void pa_discover_remove_card(struct userdata *u, pa_card *card)
143 {
144     pa_discover *discover;
145     mir_node    *node;
146     void        *state;
147
148     pa_assert(u);
149     pa_assert(card);
150     pa_assert_se((discover = u->discover));
151
152     PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
153         if (node->implement == mir_device &&
154             node->pacard.index == card->index)
155         {
156             destroy_node(u, node);
157         }
158     }
159 }
160
161 void pa_discover_profile_changed(struct userdata *u, pa_card *card)
162 {
163     pa_card_profile *prof;
164     pa_discover     *discover;
165     const char      *bus;
166     pa_bool_t        pci;
167     pa_bool_t        usb;
168     pa_bool_t        bluetooth;
169     uint32_t         stamp;
170     mir_node        *node;
171     void            *state;
172     
173     pa_assert(u);
174     pa_assert(card);
175     pa_assert_se((discover = u->discover));
176
177
178     if ((bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS)) == NULL) {
179         pa_log_debug("ignoring profile change on card '%s' due to lack of '%s'"
180                      "property", pa_utils_get_card_name(card),
181                      PA_PROP_DEVICE_BUS);
182         return;
183     }
184
185     pci = pa_streq(bus, "pci");
186     usb = pa_streq(bus, "usb");
187     bluetooth = pa_streq(bus, "bluetooth");
188
189     if (!pci && !usb && !bluetooth) {
190         pa_log_debug("ignoring profile change on card '%s' due to unsupported "
191                      "bus type '%s'", pa_utils_get_card_name(card), bus);
192     }
193
194     if (bluetooth) {
195         pa_assert_se((prof = card->active_profile));
196
197         pa_log_debug("bluetooth profile changed to '%s' on card '%s'",
198                      prof->name, card->name);
199         
200         if (!prof->n_sinks && !prof->n_sources) {
201             /* switched of but not unloaded yet */
202             PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
203                 if (node->implement == mir_device &&
204                     node->pacard.index == card->index)
205                 {
206                     node->available = FALSE;
207                 }
208             }
209         }
210     }
211     else {
212         pa_log_debug("alsa profile changed to '%s' on card '%s'",
213                      card->active_profile->name, card->name);
214
215         stamp = pa_utils_get_stamp();
216  
217         handle_alsa_card(u, card);
218
219         PA_HASHMAP_FOREACH(node, discover->nodes.byname, state) {
220             if (node->implement == mir_device &&
221                 node->pacard.index == card->index &&
222                 node->stamp < stamp)
223             {
224                 destroy_node(u, node);
225             }
226         }
227     }
228
229 }
230
231 void pa_discover_add_sink(struct userdata *u, pa_sink *sink, pa_bool_t route)
232 {
233     pa_module      *module;
234     pa_discover    *discover;
235     mir_node       *node;
236     pa_card        *card;
237     char           *key;
238     mir_node_type   type;
239     mir_node        data;
240     char            buf[256];
241
242     pa_assert(u);
243     pa_assert(sink);
244     pa_assert_se((discover = u->discover));
245
246     module = sink->module;
247
248     if ((card = sink->card)) {
249         if (!(key = node_key_from_card(u, mir_output, sink, buf, sizeof(buf))))
250             return;
251         if (!(node = pa_discover_find_node(u, key))) {
252             pa_log_debug("can't find node for sink (key '%s')", key);
253             return;
254         }
255         pa_log_debug("node for '%s' found (key %s). Updating with sink data",
256                      node->paname, node->key);
257         node->paidx = sink->index;
258         pa_hashmap_put(discover->nodes.byptr, sink, node);
259
260         type = node->type;
261
262         if (route) {
263             if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
264                 mir_router_make_routing(u);
265             else {
266                 if (!u->state.profile)
267                     schedule_deferred_routing(u);
268             }
269         }
270     }
271     else if (!module || !pa_streq(module->name, "module-combine-sink")) {
272         memset(&data, 0, sizeof(data));
273         data.key = pa_xstrdup(sink->name);
274         data.direction = mir_output;
275         data.implement = mir_device;
276         data.channels  = sink->channel_map.channels;
277
278         if (sink == pa_utils_get_null_sink(u)) {
279             data.visible = FALSE;
280             data.available = TRUE;
281             data.type = mir_null;
282             data.amname = pa_xstrdup("Silent");
283             data.amid = AM_ID_INVALID;
284             data.paname = pa_xstrdup(sink->name);
285             data.paidx = sink->index;
286         }
287         else {
288             pa_xfree(data.key); /* for now */
289             pa_log_info("currently we do not support statically loaded sinks");
290             return;
291         }
292
293         create_node(u, &data, NULL);
294     }
295 }
296
297
298 void pa_discover_remove_sink(struct userdata *u, pa_sink *sink)
299 {
300     pa_discover    *discover;
301     mir_node       *node;
302     char           *name;
303     mir_node_type   type;
304
305     pa_assert(u);
306     pa_assert(sink);
307     pa_assert_se((discover = u->discover));
308
309     name = pa_utils_get_sink_name(sink);
310
311     if (!(node = pa_hashmap_get(discover->nodes.byptr, sink)))
312         pa_log_debug("can't find node for sink (name '%s')", name);
313     else {
314         pa_log_debug("node found for '%s'. Reseting sink data", name);
315         node->paidx = PA_IDXSET_INVALID;
316         pa_hashmap_remove(discover->nodes.byptr, sink);
317
318         type = node->type;
319
320         if (sink->card) {
321             if (type != mir_bluetooth_a2dp && type != mir_bluetooth_sco)
322                 node->available = FALSE;
323             else {
324                 if (!u->state.profile)
325                     schedule_deferred_routing(u);
326             }
327         }
328         else {
329             pa_log_info("currently we do not support statically loaded sinks");
330         }
331     }
332 }
333
334
335 void pa_discover_add_source(struct userdata *u, pa_source *source)
336 {
337     pa_discover    *discover;
338     mir_node       *node;
339     pa_card        *card;
340     char           *key;
341     char            buf[256];
342
343     pa_assert(u);
344     pa_assert(source);
345     pa_assert_se((discover = u->discover));
346
347     if ((card = source->card)) {
348         if (!(key = node_key_from_card(u, mir_output,source, buf,sizeof(buf))))
349             return;
350         if (!(node = pa_discover_find_node(u, key))) {
351             pa_log_debug("can't find node for source (key '%s')", key);
352             return;
353         }
354         pa_log_debug("node for '%s' found. Updating with source data",
355                      node->amname);
356         node->paidx = source->index;
357         pa_hashmap_put(discover->nodes.byptr, source, node);
358     }
359 }
360
361
362 void pa_discover_remove_source(struct userdata *u, pa_source *source)
363 {
364     pa_discover    *discover;
365     mir_node       *node;
366     char           *name;
367
368     pa_assert(u);
369     pa_assert(source);
370     pa_assert_se((discover = u->discover));
371
372     name = pa_utils_get_source_name(source);
373
374     if (!(node = pa_hashmap_get(discover->nodes.byptr, source)))
375         pa_log_debug("can't find node for source (name '%s')", name);
376     else {
377         pa_log_debug("node found. Reseting source data");
378         node->paidx = PA_IDXSET_INVALID;
379         pa_hashmap_remove(discover->nodes.byptr, source);
380     }
381 }
382
383
384 void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
385 {
386     pa_core       *core;
387     pa_discover   *discover;
388     pa_proplist   *pl;
389     char          *name;
390     const char    *media;
391     mir_node_type  type;
392     mir_node       data;
393     mir_node      *node;
394     mir_node      *target;
395     char           key[256];
396     pa_sink       *sink;
397
398     pa_assert(u);
399     pa_assert(sinp);
400     pa_assert_se((core = u->core));
401     pa_assert_se((discover = u->discover));
402     pa_assert_se((pl = sinp->proplist));
403     
404     if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)) &&
405         !strncmp(media, combine_pattern, sizeof(combine_pattern)-1))
406     {
407         pa_log_debug("Seems to be a combine stream. Nothing to do ...");
408         return;
409     }
410
411     name = pa_utils_get_sink_input_name(sinp);
412
413     pa_log_debug("registering stream '%s'", name);
414
415     if (!(type = pa_classify_guess_stream_node_type(pl))) {
416         pa_log_debug("cant find stream class for '%s'. "
417                      "Leaving it alone", name);
418         return;
419     }
420
421     pa_utils_set_stream_routing_properties(pl, type, NULL);
422
423     snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
424
425     memset(&data, 0, sizeof(data));
426     data.key       = key;
427     data.direction = mir_input;
428     data.implement = mir_stream;
429     data.channels  = sinp->channel_map.channels;
430     data.type      = type;
431     data.visible   = TRUE;
432     data.available = TRUE;
433     data.amname    = name;
434     data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
435     data.amid      = AM_ID_INVALID;
436     data.paname    = name;
437     data.paidx     = sinp->index;
438
439     /*
440      * here we can't guess whether the application requested an explicit
441      * route by sepcifying the target sink @ stream creation time.
442      *
443      * the brute force solution: we make a default route for this stream
444      * possibly overwiriting the orginal app request :(
445      */
446     /* this will set data.mux */
447     sink = make_output_prerouting(u, &data, &sinp->channel_map, &target);
448
449     node = create_node(u, &data, NULL);
450     pa_assert(node);
451     pa_hashmap_put(discover->nodes.byptr, sinp, node);
452
453     if (sink && target) {
454         pa_log_debug("move stream to sink %u (%s)", sink->index, sink->name);
455
456         if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
457             pa_log("failed to route '%s' => '%s'",node->amname,target->amname);
458         else {
459             pa_log_debug("register route '%s' => '%s'",
460                          node->amname, target->amname);
461             /* FIXME: and actually do it ... */
462         }
463     }
464 }
465
466 void pa_discover_preroute_sink_input(struct userdata *u,
467                                      pa_sink_input_new_data *data)
468 {
469     pa_core       *core;
470     pa_module     *module;
471     pa_proplist   *pl;
472     pa_discover   *discover;
473     mir_node_type  type;
474     mir_node       fake;
475     pa_sink       *sink;
476     
477     pa_assert(u);
478     pa_assert(data);
479     pa_assert_se((core = u->core));
480     pa_assert_se((discover = u->discover));
481     pa_assert_se((pl = data->proplist));
482     pa_assert_se((module = data->module));
483
484     if (pa_streq(module->name, "module-combine-sink"))
485         type = mir_node_type_unknown;
486     else {
487         type = pa_classify_guess_stream_node_type(pl);
488         pa_utils_set_stream_routing_properties(pl, type, data->sink);
489     }
490
491     if (!data->sink) {
492         memset(&fake, 0, sizeof(fake));
493         fake.direction = mir_input;
494         fake.implement = mir_stream;
495         fake.channels  = data->channel_map.channels;
496         fake.type      = type;
497         fake.visible   = TRUE;
498         fake.available = TRUE;
499         fake.amname    = "<preroute>";
500
501         if ((sink = make_output_prerouting(u,&fake,&data->channel_map,NULL))) {
502             if (!pa_sink_input_new_data_set_sink(data, sink, FALSE))
503                 pa_log("can't set sink %d for new sink-input", sink->index);
504         }
505     }
506 }
507
508
509 void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
510 {
511     pa_core        *core;
512     pa_sink        *s;
513     pa_sink_input  *csinp;
514     pa_proplist    *pl;
515     pa_discover    *discover;
516     mir_node        data;
517     mir_node       *node;
518     mir_node       *snod;
519     char           *name;
520     const char     *media;
521     mir_node_type   type;
522     char            key[256];
523     pa_bool_t       created;
524
525     pa_assert(u);
526     pa_assert(sinp);
527     pa_assert_se((core = u->core));
528     pa_assert_se((discover = u->discover));
529     pa_assert_se((pl = sinp->proplist));
530
531
532     if ((media = pa_proplist_gets(sinp->proplist, PA_PROP_MEDIA_NAME)) &&
533         !strncmp(media, combine_pattern, sizeof(combine_pattern)-1))
534     {
535         pa_log_debug("New stream is a combine stream. Nothing to do ...");
536         return;
537     }
538
539     name = pa_utils_get_sink_input_name(sinp);
540
541     pa_log_debug("dealing with new stream '%s'", name);
542
543     if ((type = get_stream_routing_class(pl)) == mir_node_type_unknown) {
544         if (!(type = pa_classify_guess_stream_node_type(pl))) {
545             pa_log_debug("cant find stream class for '%s'. "
546                          "Leaving it alone", name);
547             return;
548         }
549
550         pa_utils_set_stream_routing_properties(pl, type, NULL);
551
552         /* if needed, make some post-routing here */
553     }
554
555     /* we need to add this to main hashmap as that is used for loop
556        through on all nodes. */
557     snprintf(key, sizeof(key), "stream_input.%d", sinp->index);
558
559     memset(&data, 0, sizeof(data));
560     data.key       = key;
561     data.direction = mir_input;
562     data.implement = mir_stream;
563     data.channels  = sinp->channel_map.channels;
564     data.type      = type;
565     data.visible   = TRUE;
566     data.available = TRUE;
567     data.amname    = name;
568     data.amdescr   = (char *)pa_proplist_gets(pl, PA_PROP_MEDIA_NAME);
569     data.amid      = AM_ID_INVALID;
570     data.paname    = name;
571     data.paidx     = sinp->index;
572     data.mux       = pa_multiplex_find(u->multiplex, sinp->sink->index);
573
574     node = create_node(u, &data, &created);
575
576     pa_assert(node);
577
578     if (!created) {
579         pa_log("%s: confused with stream. '%s' did exists",
580                __FILE__, node->amname);
581         return;
582     }
583
584     pa_hashmap_put(discover->nodes.byptr, sinp, node);
585
586     if (!data.mux)
587         s = sinp->sink;
588     else {
589         csinp = pa_idxset_get_by_index(core->sink_inputs,
590                                        data.mux->defstream_index);
591         s = csinp ? csinp->sink : NULL;
592     }
593        
594     if (s)
595         pa_log_debug("routing target candidate is %u (%s)", s->index, s->name);
596
597     if (!s || !(snod = pa_hashmap_get(discover->nodes.byptr, s)))
598         pa_log_debug("can't figure out where this stream is routed");
599     else {
600         pa_log_debug("register route '%s' => '%s'",
601                      node->amname, snod->amname);
602         /* FIXME: and actually do it ... */
603     }
604 }
605
606
607 void pa_discover_remove_sink_input(struct userdata *u, pa_sink_input *sinp)
608 {
609     pa_discover    *discover;
610     mir_node       *node;
611     mir_node       *sinknod;
612     char           *name;
613
614     pa_assert(u);
615     pa_assert(sinp);
616     pa_assert_se((discover = u->discover));
617
618     name = pa_utils_get_sink_input_name(sinp);
619
620     if (!(node = pa_hashmap_remove(discover->nodes.byptr, sinp)))
621         pa_log_debug("can't find node for sink-input (name '%s')", name);
622     else {
623         pa_log_debug("node found for '%s'. After clearing the route "
624                      "it will be destroyed", name);
625
626         if (!(sinknod = pa_hashmap_get(discover->nodes.byptr, sinp->sink)))
627             pa_log_debug("can't figure out where this stream is routed");
628         else {
629             pa_log_debug("clear route '%s' => '%s'",
630                          node->amname, sinknod->amname);
631
632             /* FIXME: and actually do it ... */
633         }
634
635         destroy_node(u, node);
636     }
637 }
638
639
640 mir_node *pa_discover_find_node(struct userdata *u, const char *key)
641 {
642     pa_discover *discover;
643     mir_node    *node;
644
645     pa_assert(u);
646     pa_assert_se((discover = u->discover));
647
648     if (key)
649         node = pa_hashmap_get(discover->nodes.byname, key);
650     else
651         node = NULL;
652
653     return node;
654 }
655
656
657 static void handle_alsa_card(struct userdata *u, pa_card *card)
658 {
659     mir_node         data;
660     const char      *udd;
661     char            *cnam;
662     char            *cid;
663
664     memset(&data, 0, sizeof(data));
665     data.visible = TRUE;
666     data.amid = AM_ID_INVALID;
667     data.implement = mir_device;
668     data.paidx = PA_IDXSET_INVALID;
669     data.stamp = pa_utils_get_stamp();
670
671     cnam = pa_utils_get_card_name(card);
672     udd  = pa_proplist_gets(card->proplist, "module-udev-detect.discovered");
673
674     if (udd && pa_streq(udd, "1")) {
675         /* udev loaded alsa card */
676         if (!strncmp(cnam, "alsa_card.", 10)) {
677             cid = cnam + 10;
678             handle_udev_loaded_card(u, card, &data, cid);
679             return;
680         }
681     }
682     else {
683         /* statically loaded pci or usb card */
684     }
685
686     pa_log_debug("ignoring unrecognized pci card '%s'", cnam);
687 }
688
689
690 static void handle_bluetooth_card(struct userdata *u, pa_card *card)
691 {
692     pa_discover     *discover;
693     pa_card_profile *prof;
694     mir_node         data;
695     char            *cnam;
696     char            *cid;
697     const char      *cdescr;
698     void            *state;
699     char             paname[MAX_NAME_LENGTH+1];
700     char             amname[MAX_NAME_LENGTH+1];
701     char             key[MAX_NAME_LENGTH+1];
702     
703
704     pa_assert_se((discover = u->discover));
705
706     cdescr = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION);
707
708
709     memset(paname, 0, sizeof(paname));
710     memset(amname, 0, sizeof(amname));
711     memset(key   , 0, sizeof(key)   );
712
713     memset(&data, 0, sizeof(data));
714     data.key = key;
715     data.visible = TRUE;
716     data.amid = AM_ID_INVALID;
717     data.implement = mir_device;
718     data.paidx = PA_IDXSET_INVALID;
719     data.paname = paname;
720     data.amname = amname;
721     data.amdescr = (char *)cdescr;
722     data.pacard.index = card->index;
723     data.stamp = pa_utils_get_stamp();
724
725     cnam = pa_utils_get_card_name(card);
726
727     if (!strncmp(cnam, "bluez_card.", 11)) { 
728         cid = cnam + 11;
729
730         PA_HASHMAP_FOREACH(prof, card->profiles, state) {
731             data.available = TRUE;
732             data.pacard.profile = prof->name;
733
734             if (prof->n_sinks > 0) {
735                 data.direction = mir_output;
736                 data.channels = prof->max_sink_channels; 
737                 data.amname = amname;
738                 amname[0] = '\0';
739                 snprintf(paname, sizeof(paname), "bluez_sink.%s", cid);
740                 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
741                 pa_classify_node_by_card(&data, card, prof, NULL);
742                 create_node(u, &data, NULL);
743             }
744
745             if (prof->n_sources > 0) {
746                 data.direction = mir_input;
747                 data.channels = prof->max_source_channels; 
748                 data.amname = amname;
749                 amname[0] = '\0';
750                 snprintf(paname, sizeof(paname), "bluez_source.%s", cid);
751                 snprintf(key, sizeof(key), "%s@%s", paname, prof->name);
752                 pa_classify_node_by_card(&data, card, prof, NULL);
753                 create_node(u, &data, NULL);
754             }
755         }
756
757         if (!(prof = card->active_profile))
758             pa_log("card '%s' has no active profile", card->name);
759         else {
760             pa_log_debug("card '%s' default profile '%s'",
761                          card->name, prof->name);
762         }
763
764         schedule_card_check(u, card);
765     }
766 }
767
768
769 static void handle_udev_loaded_card(struct userdata *u, pa_card *card,
770                                     mir_node *data, char *cardid)
771 {
772     pa_discover      *discover;
773     pa_card_profile  *prof;
774     pa_card_profile  *active;
775     void             *state;
776     const char       *alsanam;
777     char             *sid;
778     char             *sinks[MAX_CARD_TARGET+1];
779     char             *sources[MAX_CARD_TARGET+1];
780     char              buf[MAX_NAME_LENGTH+1];
781     char              paname[MAX_NAME_LENGTH+1];
782     char              amname[MAX_NAME_LENGTH+1];
783     int               i;
784
785     pa_assert(card);
786     pa_assert(card->profiles);
787     pa_assert_se((discover = u->discover));
788
789     alsanam = pa_proplist_gets(card->proplist, "alsa.card_name");
790
791     memset(amname, 0, sizeof(amname));
792
793     data->paname  = paname;
794     data->amname  = amname;
795     data->amdescr = (char *)alsanam;
796
797     data->pacard.index = card->index;
798
799     active = card->active_profile;
800
801     PA_HASHMAP_FOREACH(prof, card->profiles, state) {
802         /* filtering: deal with sepected profiles if requested so */
803         if (discover->selected && (!active || (active && prof != active)))
804             continue;
805
806         /* filtering: skip the 'off' profiles */
807         if (!prof->n_sinks && !prof->n_sources)
808             continue;
809
810         /* filtering: consider sinks with suitable amount channels */
811         if (prof->n_sinks && 
812             (prof->max_sink_channels < discover->chmin ||
813              prof->max_sink_channels  > discover->chmax  ))
814             continue;
815         
816         /* filtering: consider sources with suitable amount channels */
817         if (prof->n_sources &&
818             (prof->max_source_channels <  discover->chmin ||
819              prof->max_source_channels >  discover->chmax   ))
820             continue;
821
822         data->pacard.profile = prof->name;
823
824         parse_profile_name(prof, sinks,sources, buf,sizeof(buf));
825         
826         data->direction = mir_output;
827         data->channels = prof->max_sink_channels;
828         for (i = 0;  (sid = sinks[i]);  i++) {
829             snprintf(paname, sizeof(paname), "alsa_output.%s.%s", cardid, sid);
830             handle_card_ports(u, data, card, prof);
831         }
832
833         data->direction = mir_input;
834         data->channels = prof->max_source_channels;
835         for (i = 0;  (sid = sources[i]);  i++) {
836             snprintf(paname, sizeof(paname), "alsa_input.%s.%s", cardid, sid);
837             handle_card_ports(u, data, card, prof);
838         }        
839     }
840 }
841
842
843 static void handle_card_ports(struct userdata *u, mir_node *data,
844                               pa_card *card, pa_card_profile *prof)
845 {
846     mir_node       *node = NULL;
847     pa_bool_t       have_ports = FALSE;
848     char           *amname = data->amname;
849     pa_device_port *port;
850     void           *state;
851     pa_bool_t       created;
852     char            key[MAX_NAME_LENGTH+1];
853
854     pa_assert(u);
855     pa_assert(data);
856     pa_assert(card);
857     pa_assert(prof);
858
859     if (card->ports) {        
860         PA_HASHMAP_FOREACH(port, card->ports, state) {
861             /*
862              * if this port did not belong to any profile 
863              * (ie. prof->profiles == NULL) we assume that this port
864              * does works with all the profiles
865              */
866             if (port->profiles && pa_hashmap_get(port->profiles, prof->name) &&
867                 ((port->is_input && data->direction == mir_input)||
868                  (port->is_output && data->direction == mir_output)))
869             {
870                 have_ports = TRUE;
871
872                 /* make constrain if node != NULL and add node to it */
873
874                 amname[0] = '\0';
875                 snprintf(key, sizeof(key), "%s@%s", data->paname, port->name);
876
877                 data->key       = key;
878                 data->available = (port->available != PA_PORT_AVAILABLE_NO);
879                 data->type      = 0;
880                 data->amname    = amname;
881                 data->paport    = port->name;
882
883                 pa_classify_node_by_card(data, card, prof, port);
884
885                 node = create_node(u, data, &created);
886
887                 if (created)
888                     ; /* if constrain != NULL add the node to it */
889                 else {
890                     node->stamp = data->stamp;
891                 }
892             }
893         }
894     }
895     
896     if (!have_ports) {
897         data->key = data->paname;
898         data->available = TRUE;
899
900         pa_classify_node_by_card(data, card, prof, NULL);
901
902         node = create_node(u, data, &created);
903
904         if (!created)
905             node->stamp = data->stamp;
906     }
907
908     data->amname = amname;
909     *amname = '\0';
910 }
911
912
913 static mir_node *create_node(struct userdata *u, mir_node *data, 
914                              pa_bool_t *created_ret)
915 {
916     pa_discover *discover;
917     mir_node    *node;
918     pa_bool_t    created;
919     char         buf[2048];
920
921     pa_assert(u);
922     pa_assert(data);
923     pa_assert(data->key);
924     pa_assert(data->paname);
925     pa_assert_se((discover = u->discover));
926     
927     if ((node = pa_hashmap_get(discover->nodes.byname, data->key)))
928         created = FALSE;
929     else {
930         created = TRUE;
931         
932         node = mir_node_create(u, data);
933         pa_hashmap_put(discover->nodes.byname, node->key, node);
934         
935         mir_node_print(node, buf, sizeof(buf));
936         pa_log_debug("new node:\n%s", buf);
937         
938         pa_audiomgr_register_node(u, node);
939     }
940     
941     if (created_ret)
942         *created_ret = created;
943     
944     return node;
945 }
946
947 static void destroy_node(struct userdata *u, mir_node *node)
948 {
949     pa_discover *discover;
950     mir_node    *removed;
951
952     pa_assert(u);
953     pa_assert_se((discover = u->discover));
954
955     if (node) {
956         removed = pa_hashmap_remove(discover->nodes.byname, node->key);
957
958         if (node != removed) {
959             if (removed)
960                 pa_log("%s: confused with data structures: key mismatch. "
961                        " attempted to destroy '%s'; actually destroyed '%s'",
962                        __FILE__, node->key, removed->key);
963             else
964                 pa_log("%s: confused with data structures: node '%s' "
965                        "is not in the hash table", __FILE__, node->key);
966             return;
967         }
968
969         pa_log_debug("destroying node: %s / '%s'", node->key, node->amname);
970
971         if (node->implement == mir_stream) {
972             if (node->direction == mir_input) {
973                 if (node->mux) {
974                     pa_log_debug("removing multiplexer"); 
975                 }
976             }
977         }
978         
979         pa_audiomgr_unregister_node(u, node);
980
981         pa_multiplex_destroy(u->multiplex, u->core, node->mux);
982         
983         mir_node_destroy(u, node);
984     }    
985 }
986
987 static char *get_name(char **string_ptr, int offs)
988 {
989     char c, *name, *end;
990
991     name = *string_ptr + offs;
992
993     for (end = name;  (c = *end);   end++) {
994         if (c == '+') {
995             *end++ = '\0';
996             break;
997         }
998     }
999
1000     *string_ptr = end;
1001
1002     return name;
1003
1004
1005 static void parse_profile_name(pa_card_profile *prof,
1006                                char           **sinks,
1007                                char           **sources,
1008                                char            *buf,
1009                                int              buflen)
1010 {
1011     char *p = buf;
1012     int   i = 0;
1013     int   j = 0;
1014
1015     pa_assert(prof->name);
1016
1017     strncpy(buf, prof->name, buflen);
1018     buf[buflen-1] = '\0';
1019
1020     memset(sinks, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
1021     memset(sources, 0, sizeof(char *) * (MAX_CARD_TARGET+1));
1022
1023     do {
1024         if (!strncmp(p, "output:", 7)) {
1025             if (i >= MAX_CARD_TARGET) {
1026                 pa_log_debug("number of outputs exeeds the maximum %d in "
1027                              "profile name '%s'", MAX_CARD_TARGET, prof->name);
1028                 return;
1029             }
1030             sinks[i++] = get_name(&p, 7);
1031         } 
1032         else if (!strncmp(p, "input:", 6)) {
1033             if (j >= MAX_CARD_TARGET) {
1034                 pa_log_debug("number of inputs exeeds the maximum %d in "
1035                              "profile name '%s'", MAX_CARD_TARGET, prof->name);
1036                 return;
1037             }
1038             sources[j++] = get_name(&p, 6);            
1039         }
1040         else {
1041             pa_log("%s: failed to parse profile name '%s'",
1042                    __FILE__, prof->name);
1043             return;
1044         }
1045     } while (*p);
1046 }
1047
1048
1049 static char *node_key_from_card(struct userdata *u, mir_direction direction,
1050                                 void *data, char *buf, size_t len)
1051 {
1052     pa_card         *card;
1053     pa_card_profile *profile;
1054     pa_device_port  *port;
1055     const char      *bus;
1056     pa_bool_t        pci;
1057     pa_bool_t        usb;
1058     pa_bool_t        bluetooth;
1059     char            *type;
1060     char            *name;
1061     const char      *profile_name;
1062     char            *key;
1063
1064     pa_assert(u);
1065     pa_assert(data);
1066     pa_assert(direction == mir_input || direction == mir_output);
1067
1068     if (direction == mir_input) {
1069         pa_sink *sink = data;
1070         type  = "sink";
1071         name  = pa_utils_get_sink_name(sink);
1072         card  = sink->card;
1073         port  = sink->active_port;
1074     }
1075     else {
1076         pa_source *source = data;
1077         type = "source";
1078         name = pa_utils_get_source_name(source);
1079         card = source->card;
1080         port = source->active_port;
1081     }
1082
1083     if (!card)
1084         return NULL;
1085         
1086     pa_assert_se((profile = card->active_profile));
1087
1088     if (!u->state.profile)
1089         profile_name = profile->name;
1090     else {
1091         pa_log_debug("state.profile is not null. '%s' supresses '%s'",
1092                      u->state.profile, profile->name);
1093         profile_name = u->state.profile;
1094     }
1095         
1096
1097     if (!(bus = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_BUS))) {
1098         pa_log_debug("ignoring %s '%s' due to lack of '%s' property "
1099                      "on its card", type, name, PA_PROP_DEVICE_BUS);
1100         return NULL;
1101     }
1102     
1103     pci = pa_streq(bus, "pci");
1104     usb = pa_streq(bus, "usb");
1105     bluetooth = pa_streq(bus, "bluetooth");
1106     
1107     if (!pci && !usb && !bluetooth) {
1108         pa_log_debug("ignoring %s '%s' due to unsupported bus type '%s' "
1109                      "of its card", type, name, bus);
1110         return NULL;
1111     }
1112     
1113     if (bluetooth) {
1114         key = buf;
1115         snprintf(buf, len, "%s@%s", name, profile_name);
1116     }
1117     else {
1118         if (!port)
1119             key = name;
1120         else {
1121             key = buf;
1122             snprintf(buf, len, "%s@%s", name, port->name);
1123         }
1124     }
1125
1126     return key;
1127 }
1128
1129 static pa_sink *make_output_prerouting(struct userdata *u,
1130                                        mir_node        *data,
1131                                        pa_channel_map  *chmap,
1132                                        mir_node       **target_ret)
1133 {
1134     pa_core    *core;
1135     mir_node   *target;
1136     pa_sink    *sink = NULL;
1137
1138     pa_assert(u);
1139     pa_assert(data);
1140     pa_assert(chmap);
1141     pa_assert_se((core = u->core));
1142
1143     
1144     
1145     target = mir_router_make_prerouting(u, data);
1146
1147     if (!target)
1148         pa_log("there is no default route for the stream '%s'", data->amname);
1149     else if (target->paidx == PA_IDXSET_INVALID)
1150         pa_log("can't route to default '%s': no sink", target->amname);
1151     else {
1152         if (!(sink = pa_idxset_get_by_index(core->sinks, target->paidx)))
1153             pa_log("can't route to default '%s': sink is gone",target->amname);
1154         else {
1155             if (pa_classify_multiplex_stream(data)) {
1156                 data->mux = pa_multiplex_create(u->multiplex, core,
1157                                                 sink->index, chmap, NULL,
1158                                                 data->type);
1159                 if (data->mux) {
1160                     sink = pa_idxset_get_by_index(core->sinks,
1161                                                   data->mux->sink_index);
1162                     pa_assert(sink);
1163                 }
1164             }
1165         }
1166     }
1167
1168     if (target_ret)
1169         *target_ret = target;
1170
1171     return sink;
1172 }
1173
1174
1175 static mir_node_type get_stream_routing_class(pa_proplist *pl)
1176 {
1177     const char    *clid;
1178     mir_node_type  type;
1179     char          *e;
1180
1181     pa_assert(pl);
1182
1183     if ((clid = pa_proplist_gets(pl, PA_PROP_ROUTING_CLASS_ID))) {
1184         type = strtol(clid, &e, 10);
1185
1186         if (!*e) {
1187             if (type >= mir_application_class_begin &&
1188                 type <  mir_application_class_end)
1189             {
1190                 return type;
1191             }
1192         }                
1193     }
1194
1195     return mir_node_type_unknown;
1196 }
1197
1198
1199
1200 static void deferred_routing_cb(pa_mainloop_api *m, void *d)
1201 {
1202     struct userdata *u = d;
1203
1204     (void)m;
1205
1206     pa_assert(u);
1207
1208     pa_log_debug("deferred routing starts");
1209
1210     mir_router_make_routing(u);
1211 }
1212
1213
1214 static void schedule_deferred_routing(struct userdata *u)
1215 {
1216     pa_core *core;
1217
1218     pa_assert(u);
1219     pa_assert_se((core = u->core));
1220
1221     pa_log_debug("scheduling deferred routing");
1222
1223     pa_mainloop_api_once(core->mainloop, deferred_routing_cb, u);
1224 }
1225
1226
1227 static void card_check_cb(pa_mainloop_api *m, void *d)
1228 {
1229     card_check_t *cc = d;
1230     struct userdata *u;
1231     pa_core *core;
1232     pa_card *card;
1233     pa_sink *sink;
1234     pa_source *source;
1235     int n_sink, n_source;
1236     uint32_t idx;
1237
1238     (void)m;
1239
1240     pa_assert(cc);
1241     pa_assert((u = cc->u));
1242     pa_assert((core = u->core));
1243
1244     pa_log_debug("card check starts");
1245
1246     if (!(card = pa_idxset_get_by_index(core->cards, cc->index)))
1247         pa_log_debug("card %u is gone", cc->index);
1248     else {
1249         n_sink = n_source = 0;
1250
1251         PA_IDXSET_FOREACH(sink, core->sinks, idx) {
1252             if ((sink->card) && sink->card->index == card->index)
1253                 n_sink++;
1254         }
1255
1256         PA_IDXSET_FOREACH(source, core->sources, idx) {
1257             if ((source->card) && source->card->index == card->index)
1258                 n_sink++;
1259         }
1260
1261         if (n_sink || n_source) {
1262             pa_log_debug("found %u sinks and %u sources belonging to "
1263                          "'%s' card", n_sink, n_source, card->name);
1264             pa_log_debug("nothing to do");
1265         }
1266         else {
1267             pa_log_debug("card '%s' has no sinks/sources. Do routing ...",
1268                          card->name);
1269             mir_router_make_routing(u);
1270         }
1271     }
1272     
1273     pa_xfree(cc);
1274 }
1275
1276
1277 static void schedule_card_check(struct userdata *u, pa_card *card)
1278 {
1279     pa_core *core;
1280     card_check_t *cc;
1281
1282     pa_assert(u);
1283     pa_assert(card);
1284     pa_assert_se((core = u->core));
1285
1286     pa_log_debug("scheduling card check");
1287
1288     cc = pa_xnew0(card_check_t, 1);
1289     cc->u = u;
1290     cc->index = card->index;
1291
1292     pa_mainloop_api_once(core->mainloop, card_check_cb, cc);
1293 }
1294
1295                                   
1296 /*
1297  * Local Variables:
1298  * c-basic-offset: 4
1299  * indent-tabs-mode: nil
1300  * End:
1301  *
1302  */