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