routing: add loopback support to nodes
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / switch.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 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24
25 #include <pulsecore/pulsecore-config.h>
26 #include <pulsecore/core-util.h>
27 #include <pulsecore/namereg.h>
28
29 #include <pulsecore/card.h>
30 #include <pulsecore/sink.h>
31 #include <pulsecore/device-port.h>
32 #include <pulsecore/source.h>
33 #include <pulsecore/sink-input.h>
34 #include <pulsecore/source-output.h>
35
36 #include "switch.h"
37 #include "node.h"
38 #include "multiplex.h"
39 #include "discover.h"
40 #include "utils.h"
41
42 static pa_bool_t setup_explicit_link_from_stream_to_device(struct userdata *,
43                                                            mir_node *, mir_node *);
44 static pa_bool_t teardown_explicit_link_from_stream_to_device(struct userdata *,
45                                                               mir_node *, mir_node *);
46
47 static pa_bool_t setup_default_link_from_stream_to_device(struct userdata *,
48                                                           mir_node *, mir_node *);
49
50
51 static pa_sink *setup_device_output(struct userdata *, mir_node *);
52
53 static pa_bool_t set_profile(struct userdata *, mir_node *);
54 static pa_bool_t set_port(struct userdata *, mir_node *);
55
56
57 pa_bool_t mir_switch_setup_link(struct userdata *u,
58                                 mir_node *from,
59                                 mir_node *to,
60                                 pa_bool_t explicit)
61 {
62     pa_core *core;
63
64     pa_assert(u);
65     pa_assert(to);
66     pa_assert_se((core = u->core));
67     pa_assert(!from || from->direction == mir_input);
68     pa_assert(to->direction == mir_output);
69
70     if (explicit) {
71         /*
72          * links for explic routes
73          */
74         pa_assert(from);
75
76         switch (from->implement) {
77
78         case mir_stream:
79             switch (to->implement) {
80
81             case mir_stream:
82                 pa_log_debug("routing to streams is not implemented yet");
83                 break;
84
85             case mir_device:
86                 if (!setup_explicit_link_from_stream_to_device(u, from, to))
87                     return FALSE;
88                 break;
89
90             default:
91                 pa_log("%s: can't setup link: invalid sink node "
92                        "implement", __FILE__);
93                 return FALSE;
94             }
95             break;
96
97         case mir_device:
98             pa_log_debug("input device routing is not implemented yet");
99             break;
100
101         default:
102             pa_log("%s: can't setup link: invalid source node "
103                    "implement", __FILE__);
104             return FALSE;
105         }
106     }
107     else {
108         /*
109          * links for default routes
110          */
111         switch (to->implement) {
112
113         case mir_stream:
114             pa_log_debug("routing to a stream is not implemented yet");
115             break;
116
117         case mir_device:
118             if (!from) /* prerouting */
119                 return (!explicit && setup_device_output(u, to) != NULL);
120             else {
121                 switch (from->implement) {
122
123                 case mir_stream:
124                     if (!setup_default_link_from_stream_to_device(u, from, to))
125                         return FALSE;
126                     break;
127
128                 case mir_device:
129                     pa_log("%s: default device -> device route is "
130                            "not supported", __FILE__);
131                     break;
132
133                 default:
134                     pa_log("%s: can't setup link: invalid source node "
135                            "implement", __FILE__);
136                     return FALSE;
137                 }
138             }
139             break;
140
141         default:
142             pa_log("%s: can't setup link: invalid sink node "
143                    "implement", __FILE__);
144             return FALSE;
145         }
146     }
147
148     pa_log_debug("link %s => %s is established", from->amname, to->amname);
149
150     return TRUE;
151 }
152
153 pa_bool_t mir_switch_teardown_link(struct userdata *u,
154                                    mir_node        *from,
155                                    mir_node        *to)
156 {
157     pa_core *core;
158
159     pa_assert(u);
160     pa_assert(from);
161     pa_assert(to);
162     pa_assert_se((core = u->core));
163     pa_assert(from->direction == mir_input);
164     pa_assert(to->direction == mir_output);
165
166
167     switch (from->implement) {
168
169     case mir_stream:
170         switch (to->implement) {
171             
172         case mir_stream: /* stream -> stream */
173             pa_log_debug("routing to streams is not implemented yet");
174             break;
175             
176         case mir_device: /* stream -> device */
177             if (!teardown_explicit_link_from_stream_to_device(u, from, to))
178                 return FALSE;
179             break;
180             
181         default:
182             pa_log("%s: can't teardown link: invalid sink node "
183                    "implement", __FILE__);
184             return FALSE;
185         }
186         break;
187         
188     case mir_device: /* device -> stream | device->device */
189         pa_log_debug("input device routing is not implemented yet");
190         break;
191         
192     default:
193         pa_log("%s: can't teardown link: invalid source node "
194                "implement", __FILE__);
195         return FALSE;
196     }
197
198     pa_log_debug("link %s => %s is torn down", from->amname, to->amname);
199
200     return TRUE;
201 }
202
203 static pa_bool_t setup_explicit_link_from_stream_to_device(struct userdata *u,
204                                                            mir_node *from,
205                                                            mir_node *to)
206 {
207     pa_core       *core;
208     pa_sink       *sink;
209     pa_sink_input *sinp;
210     pa_muxnode    *mux;
211
212     pa_assert(u);
213     pa_assert(from);
214     pa_assert(to);
215     pa_assert((core = u->core));
216
217     if (!(sink = setup_device_output(u, to)))
218         return FALSE;
219
220     if (!set_profile(u, from) || !set_port(u, from)) {
221         pa_log("can't route from '%s'", from->amname);
222         return FALSE;
223     }
224
225     if ((mux = from->mux)) {
226         sinp = pa_idxset_get_by_index(core->sink_inputs, mux->defstream_index);
227
228         if (sinp && sinp->sink == sink) {
229             if (!pa_multiplex_remove_default_route(core, mux, TRUE))
230                 return FALSE;
231         }
232         else if (pa_multiplex_duplicate_route(core, mux, NULL, sink)) {
233             pa_log_debug("multiplex route %s => %s already exists",
234                          from->amname, to->amname);
235             return TRUE;
236         }
237         else {
238             if (!pa_multiplex_add_explicit_route(core, mux, sink, from->type))
239                 return FALSE;
240         }
241     }
242     else {
243         if ((sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
244             if (sinp->sink == sink)
245                 pa_log_debug("direct route already exists. nothing to do");
246             else {
247                 pa_log_debug("direct route: sink-input.%u -> sink.%u",
248                              sinp->index, sink->index);
249
250                 if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
251                     return FALSE;
252             }
253         }
254     }
255
256     pa_log_debug("link %s => %s is established", from->amname, to->amname);
257
258     return TRUE;
259 }
260
261
262 static pa_bool_t teardown_explicit_link_from_stream_to_device(struct userdata *u,
263                                                               mir_node *from,
264                                                               mir_node *to)
265 {
266     pa_core       *core;
267     pa_sink       *sink;
268     pa_sink_input *sinp;
269     pa_muxnode    *mux;
270
271     pa_assert(u);
272     pa_assert(from);
273     pa_assert(to);
274     pa_assert((core = u->core));
275
276     if ((mux = from->mux)) {
277         if (!(sink = pa_idxset_get_by_index(core->sinks, to->paidx))) {
278             pa_log_debug("can't find sink.%u", to->paidx);
279             return FALSE;
280         }
281
282         if (!pa_multiplex_remove_explicit_route(core, mux, sink)) {
283             pa_log_debug("can't remove multiplex route on mux %u", mux->module_index);
284             return FALSE;
285         }
286     }
287     else {
288         if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
289             pa_log_debug("can't find source.%u", from->paidx);
290             return FALSE;
291         }
292
293         if (!(sink = pa_utils_get_null_sink(u))) {
294             pa_log_debug("can't remove direct route: no null sink");
295             return FALSE;
296         }
297
298         if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
299             return FALSE;
300     }
301
302     pa_log_debug("link %s => %s is torn down", from->amname, to->amname);
303
304     return TRUE;
305 }
306
307
308 static pa_bool_t setup_default_link_from_stream_to_device(struct userdata *u,
309                                                           mir_node *from,
310                                                           mir_node *to)
311 {
312     pa_core       *core;
313     pa_sink       *sink;
314     pa_sink_input *sinp;
315     pa_muxnode    *mux;
316     int            n;
317
318     pa_assert(u);
319     pa_assert(from);
320     pa_assert(to);
321     pa_assert((core = u->core));
322
323     if (!(sink = setup_device_output(u, to)))
324         return FALSE;
325
326     if (!set_profile(u, from) || !set_port(u, from)) {
327         pa_log("can't route from '%s'", from->amname);
328         return FALSE;
329     }
330
331     if ((mux = from->mux)) {
332         if (mux->defstream_index == PA_IDXSET_INVALID) {
333             if ((n = pa_multiplex_no_of_routes(core, mux)) < 0)
334                 return FALSE;
335             else if (n > 0) {
336                 pa_log_debug("currently mux %u has no default route",
337                              mux->module_index);
338                 return TRUE;
339             }
340             sinp = NULL;
341         }
342         else {
343             sinp = pa_idxset_get_by_index(core->sink_inputs,
344                                           mux->defstream_index);
345         }
346
347         if (!sinp) {
348             /*
349              * we supposed to have a default stream but the sink-input
350              * on the combine side is not existing any more. This can
351              * happen, for instance, if the sink, where it was connected,
352              * died for some reason.
353              */
354             pa_log_debug("supposed to have a default stream on multiplex "
355                          "%u but non was found. Trying to make one",
356                          mux->module_index);
357             if (pa_multiplex_duplicate_route(core, mux, sinp, sink)) {
358                 pa_log_debug("the default stream on mux %u would be a "
359                              "duplicate to an explicit route. "
360                              "Removing it ...", mux->module_index);
361                 mux->defstream_index = PA_IDXSET_INVALID;
362                 return TRUE; /* the routing is a success */
363             }
364
365             if (!pa_multiplex_add_default_route(core, mux,sink, from->type)) {
366                 pa_log_debug("failed to add default route on mux %d",
367                              mux->module_index);
368                 mux->defstream_index = PA_IDXSET_INVALID;
369                 return FALSE;
370             }
371         }
372         else if (pa_multiplex_duplicate_route(core, mux, sinp, sink)) {
373             pa_log_debug("the default stream on mux %u would be a duplicate "
374                          "to an explicit route. Removing it ...",
375                          mux->module_index);
376             return TRUE;        /* the routing is a success */
377         }
378             
379         if (sinp) {
380             pa_log_debug("multiplex route: sink-input.%d -> (sink.%d - "
381                          "sink-input.%d) -> sink.%d", from->paidx,
382                          mux->sink_index, sinp->index, sink->index);
383         }
384         else {
385             pa_log_debug("multiplex route: sink-input.%d -> (sink.%d - "
386                          "sink-input) -> sink.%d", from->paidx,
387                          mux->sink_index, sink->index);
388         }
389
390         if (!pa_multiplex_change_default_route(core, mux, sink))
391             return FALSE;
392     }
393     else {
394         if (from->paidx == PA_IDXSET_INVALID) {
395             pa_log_debug("can't route '%s': no sink-input", to->amname);
396             return FALSE;
397         }
398
399         if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
400             pa_log_debug("can't find sink input for '%s'", from->amname);
401             return FALSE;
402         }
403
404         pa_log_debug("direct route: sink-input.%d -> sink.%d",
405                      sinp->index, sink->index);
406
407         if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
408             return FALSE;
409     }
410
411     return TRUE;
412 }
413
414 static pa_sink *setup_device_output(struct userdata *u, mir_node *node)
415 {
416     pa_core *core;
417     pa_sink *sink;
418
419     pa_assert(u);
420     pa_assert(node);
421     pa_assert_se((core = u->core));
422
423     if (!set_profile(u, node) || !set_port(u, node)) {
424         pa_log("can't route to '%s'", node->amname);
425         return NULL;
426     }
427
428     if (node->paidx == PA_IDXSET_INVALID) {
429         pa_log_debug("can't route to '%s': no sink", node->amname);
430         return NULL;
431     }
432
433     if (!(sink = pa_idxset_get_by_index(core->sinks, node->paidx))) {
434         pa_log_debug("can't route to '%s': cant find sink", node->amname);
435         return NULL;
436     }
437
438     return sink;
439 }
440
441
442 static pa_bool_t set_profile(struct userdata *u, mir_node *node)
443 {
444     pa_core         *core;
445     pa_card         *card;
446     pa_card_profile *prof;
447
448     pa_assert(u);
449     pa_assert(node);
450     pa_assert_se((core = u->core));
451
452     if (node->implement != mir_device)
453         return TRUE;
454
455     if (node->type == mir_bluetooth_a2dp ||
456         node->type == mir_bluetooth_sco)
457     {
458         card = pa_idxset_get_by_index(core->cards, node->pacard.index);
459
460         if (!card) {
461             pa_log("can't find card for '%s'", node->amname);
462             return FALSE;
463         }
464
465         pa_assert_se(prof = card->active_profile);
466     
467         if (!pa_streq(node->pacard.profile, prof->name)) {
468             pa_log_debug("changing profile '%s' => '%s'",
469                          prof->name, node->pacard.profile);
470
471             if (u->state.profile) {
472                 pa_log("nested profile setting is not allowed. won't change "
473                        "'%s' => '%s'", prof->name, node->pacard.profile);
474                 return FALSE;
475             }
476
477             u->state.profile = node->pacard.profile;
478
479             pa_card_set_profile(card, node->pacard.profile, FALSE);
480
481             u->state.profile = NULL;            
482         }
483     }
484
485     return TRUE;
486 }
487
488
489
490 static pa_bool_t set_port(struct userdata *u, mir_node *node)
491 {
492     pa_core   *core;
493     pa_sink   *sink;
494     pa_source *source;
495     pa_device_port *port;
496     mir_node  *oldnode;
497     void      *data  = NULL;
498     uint32_t   paidx = PA_IDXSET_INVALID;
499
500     pa_assert(u);
501     pa_assert(node);
502     pa_assert(node->paname);
503     pa_assert_se((core = u->core));
504
505     if (node->direction != mir_input && node->direction != mir_output)
506         return FALSE;
507
508     if (node->implement != mir_device)
509         return TRUE;
510
511     if (!node->paport)
512         return TRUE;
513
514     if (node->direction == mir_input) {
515         source = pa_namereg_get(core, node->paname, PA_NAMEREG_SOURCE);
516         
517         if (!(data = source)) {
518             pa_log("can't set port for '%s': source not found",
519                    node->paname);
520             return FALSE;
521         }
522         
523         if ((port = source->active_port) && pa_streq(node->paport, port->name))
524             return TRUE;
525
526         if (pa_source_set_port(source, node->paport, FALSE) < 0)
527             return FALSE;
528
529         paidx = source->index;
530     }
531
532     if (node->direction == mir_output) {
533         sink = pa_namereg_get(core, node->paname, PA_NAMEREG_SINK);
534         
535         if (!(data = sink)) {
536             pa_log("can't set port for '%s': sink not found",
537                    node->paname);
538             return FALSE;
539         }
540
541         if ((port = sink->active_port) && pa_streq(node->paport, port->name))
542             return TRUE;
543
544         if (pa_sink_set_port(sink, node->paport, FALSE) < 0)
545             return FALSE;
546
547         paidx = sink->index;
548     }
549
550     if ((oldnode = pa_discover_remove_node_from_ptr_hash(u, data)))
551         oldnode->paidx = PA_IDXSET_INVALID;
552
553     node->paidx = paidx;
554     pa_discover_add_node_to_ptr_hash(u, data, node);
555
556
557     return TRUE;
558 }
559
560
561
562 /*
563  * Local Variables:
564  * c-basic-offset: 4
565  * indent-tabs-mode: nil
566  * End:
567  *
568  */