routing: explicit routing in module-murphy-ivi
authorJanos Kovacs <jankovac503@gmail.com>
Mon, 28 May 2012 08:56:31 +0000 (11:56 +0300)
committerJanos Kovacs <jankovac503@gmail.com>
Mon, 28 May 2012 08:56:31 +0000 (11:56 +0300)
* in case of multiplexed streams it logs it without actually doing it
* counterpart in combine not implemented yet
* integration with combine is missing

16 files changed:
murphy/audiomgr.c
murphy/classify.c
murphy/discover.c
murphy/module-murphy-ivi.c
murphy/multiplex.c
murphy/multiplex.h
murphy/node.c
murphy/node.h
murphy/router.c
murphy/router.h
murphy/switch.c
murphy/switch.h
murphy/tracker.c
murphy/userdata.h
murphy/utils.c
murphy/utils.h

index b234358..98b0a61 100644 (file)
@@ -14,7 +14,6 @@
 #include "node.h"
 #include "discover.h"
 #include "router.h"
-#include "switch.h"             /* later we should talk just the router */
 #include "dbusif.h"
 
 #define AUDIOMGR_DOMAIN   "PULSE"
@@ -60,11 +59,13 @@ typedef struct {
 
 struct pa_audiomgr {
     domain_t      domain;
-    pa_hashmap   *nodes;
+    pa_hashmap   *nodes;        /**< nodes ie. sinks and sources */
+    pa_hashmap   *conns;        /**< connections */
 };
 
 
-static void *hash_key(mir_direction, uint16_t);
+static void *node_hash(mir_direction, uint16_t);
+static void *conn_hash(uint16_t);
 
 
 struct pa_audiomgr *pa_audiomgr_init(struct userdata *u)
@@ -80,6 +81,8 @@ struct pa_audiomgr *pa_audiomgr_init(struct userdata *u)
     am->domain.state = DS_DOWN;
     am->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
                                      pa_idxset_trivial_compare_func);
+    am->conns = pa_hashmap_new(pa_idxset_trivial_hash_func,
+                               pa_idxset_trivial_compare_func);
     return am;
 }
 
@@ -92,6 +95,7 @@ void pa_audiomgr_done(struct userdata *u)
             pa_policy_dbusif_unregister_domain(u, am->domain.id);
 
         pa_hashmap_free(am->nodes, NULL,NULL);
+        pa_hashmap_free(am->conns, NULL,NULL);
         pa_xfree((void *)am->domain.name);
         pa_xfree(am);
         u->audiomgr = NULL;
@@ -238,7 +242,7 @@ void pa_audiomgr_node_registered(struct userdata *u,
     else {
         node->amid = id;
 
-        key = hash_key(node->direction, id);
+        key = node_hash(node->direction, id);
 
         pa_log_debug("registering node '%s' (%p/%p)",
                      node->amname, key, node);
@@ -272,7 +276,7 @@ void pa_audiomgr_unregister_node(struct userdata *u, mir_node *node)
         ud->id   = node->amid;
         ud->name = pa_xstrdup(node->amname);
 
-        key = hash_key(node->direction, node->amid);
+        key = node_hash(node->direction, node->amid);
         removed = pa_hashmap_remove(am->nodes, key);
 
         if (node != removed) {
@@ -281,7 +285,7 @@ void pa_audiomgr_unregister_node(struct userdata *u, mir_node *node)
                        "attempted to remove '%s' (%p/%p); "
                        "actually removed '%s' (%p/%p)", __FILE__,
                        node->amname, key, node, removed->amname,
-                       hash_key(removed->direction, removed->amid), removed);
+                       node_hash(removed->direction, removed->amid), removed);
             else
                 pa_log("%s: confused with data structures: node %u (%p)"
                        "is not in the hash table", __FILE__, node->amid, node);
@@ -322,23 +326,32 @@ void pa_audiomgr_node_unregistered(struct userdata   *u,
 
 void pa_audiomgr_connect(struct userdata *u, am_connect_data *cd)
 {
-    pa_audiomgr *am;
-    am_ack_data  ad;
-    mir_node    *from = NULL;
-    mir_node    *to   = NULL;
-    int          err  = E_OK;
+    pa_audiomgr    *am;
+    am_ack_data     ad;
+    mir_connection *conn;
+    uint16_t        cid;
+    mir_node       *from = NULL;
+    mir_node       *to   = NULL;
+    int             err  = E_OK;
 
     pa_assert(u);
     pa_assert(cd);
     pa_assert_se((am = u->audiomgr));
 
-    /* temporary hack: instead of switching we should setup an explicit route*/
-    if ((from = pa_hashmap_get(am->nodes, hash_key(mir_input, cd->source))) &&
-        (to   = pa_hashmap_get(am->nodes, hash_key(mir_output, cd->sink))))
+    if ((from = pa_hashmap_get(am->nodes, node_hash(mir_input, cd->source))) &&
+        (to   = pa_hashmap_get(am->nodes, node_hash(mir_output, cd->sink))))
     {
+        cid = cd->connection;
+
         pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
-        if (!mir_switch_setup_link(u, from, to))
+
+        if (!(conn = mir_router_add_explicit_route(u, cid, from, to)))
             err = E_NOT_POSSIBLE;
+        else {
+            pa_log_debug("registering connection (%u/%p)",
+                         cd->connection, conn);
+            pa_hashmap_put(am->conns, conn_hash(cid), conn);
+        }
     }
     else {
         pa_log_debug("failed to connect: can't find node for %s %u",
@@ -356,14 +369,24 @@ void pa_audiomgr_connect(struct userdata *u, am_connect_data *cd)
 
 void pa_audiomgr_disconnect(struct userdata *u, am_connect_data *cd)
 {
-    am_ack_data  ad;
-    int err = E_OK;
+    pa_audiomgr    *am;
+    mir_connection *conn;
+    uint16_t        cid;
+    am_ack_data     ad;
+    int             err = E_OK;
 
     pa_assert(u);
     pa_assert(cd);
+    pa_assert_se((am = u->audiomgr));
 
-    /* temporary hack: instead this we should delete an explicit route */
-    mir_router_make_routing(u);
+    cid = cd->connection;
+
+    if ((conn = pa_hashmap_remove(am->conns, conn_hash(cid))))
+        mir_router_remove_explicit_route(u, conn);
+    else {
+        pa_log_debug("failed to disconnect: can't find connection %u", cid);
+        err = E_NON_EXISTENT;
+    }
 
     memset(&ad, 0, sizeof(ad));
     ad.handle = cd->handle;
@@ -373,11 +396,16 @@ void pa_audiomgr_disconnect(struct userdata *u, am_connect_data *cd)
     pa_policy_dbusif_acknowledge(u, AUDIOMGR_DISCONNECT_ACK, &ad);
 }
 
-static void *hash_key(mir_direction direction, uint16_t amid)
+static void *node_hash(mir_direction direction, uint16_t amid)
 {
     return NULL + ((uint32_t)direction << 16 | (uint32_t)amid);
 }
 
+static void *conn_hash(uint16_t connid)
+{
+    return NULL + (uint32_t)connid;
+}
+
 
 
 /*
index 0b6b786..d48c4d0 100644 (file)
@@ -252,6 +252,8 @@ pa_bool_t pa_classify_multiplex_stream(mir_node *node)
 
     pa_assert(node);
 
+    //return FALSE;
+
     if (node->implement == mir_stream && node->direction == mir_input) {
         class = node->type;
 
index 406041f..d0cf9f3 100644 (file)
@@ -586,10 +586,9 @@ void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
     if (!data.mux)
         s = sinp->sink;
     else {
-        if ((csinp = pa_multiplex_default_stream(core, data.mux)))
-            s = csinp->sink;
-        else
-            s = NULL;
+        csinp = pa_idxset_get_by_index(core->sink_inputs,
+                                       data.mux->defstream_index);
+        s = csinp ? csinp->sink : NULL;
     }
        
     if (s)
@@ -1141,6 +1140,8 @@ static pa_sink *make_output_prerouting(struct userdata *u,
     pa_assert(chmap);
     pa_assert_se((core = u->core));
 
+    
+    
     target = mir_router_make_prerouting(u, data);
 
     if (!target)
index 041924b..5314690 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "module-murphy-ivi-symdef.h"
 #include "userdata.h"
+#include "node.h"
 #include "tracker.h"
 #include "discover.h"
 #include "router.h"
@@ -103,6 +104,7 @@ int pa__init(pa_module *m) {
     u->core      = m->core;
     u->module    = m;
     u->nullsink  = pa_utils_create_null_sink(u, nsnam);
+    u->nodeset   = pa_nodeset_init(u);
     u->audiomgr  = pa_audiomgr_init(u);
     u->dbusif    = pa_policy_dbusif_init(u,ifnam,mrppath,mrpnam,ampath,amnam);
     u->discover  = pa_discover_init(u);
@@ -153,6 +155,7 @@ void pa__done(pa_module *m) {
         pa_audiomgr_done(u);
         pa_policy_dbusif_done(u);
         pa_mir_config_done(u);
+        pa_nodeset_done(u);
         pa_utils_destroy_null_sink(u);
 
         pa_multiplex_done(u->multiplex, u->core);
index 8e0cd0d..b4972a1 100644 (file)
@@ -83,14 +83,19 @@ pa_muxnode *pa_multiplex_create(pa_multiplex   *multiplex,
     mux = pa_xnew0(pa_muxnode, 1);
     mux->module_index = module->index;
     mux->sink_index = u->sink->index;
+    mux->defstream_index = PA_IDXSET_INVALID;
 
     PA_LLIST_PREPEND(pa_muxnode, multiplex->muxnodes, mux);
 
     if (!(o = pa_idxset_first(u->outputs, &idx)))
         pa_log("can't find default multiplexer stream");
     else {
-        if ((sinp = o->sink_input))
+        if ((sinp = o->sink_input)) {
             pa_utils_set_stream_routing_properties(sinp->proplist, type, NULL);
+            mux->defstream_index = sinp->index;
+            /* Hmm... */
+            sinp->flags &= ~(pa_sink_input_flags_t)PA_SINK_INPUT_DONT_MOVE;
+        }
     }
 
     pa_log_debug("multiplexer succesfully loaded");
@@ -130,41 +135,120 @@ pa_muxnode *pa_multiplex_find(pa_multiplex *multiplex, uint32_t sink_index)
     return NULL;
 }
 
-pa_sink_input *pa_multiplex_default_stream(pa_core *core, pa_muxnode *mux)
+
+pa_bool_t pa_multiplex_remove_default_route(pa_core *core,
+                                            pa_muxnode *mux,
+                                            pa_bool_t transfer_to_explicit)
 {
-    pa_module       *module;
-    pa_sink_input   *sinp;
+    pa_module *module;
+    pa_sink_input *sinp;
+    uint32_t idx;
     struct userdata *u;         /* combine's userdata! */
+
+    pa_assert(core);
+    pa_assert(mux);
+
+    if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
+        pa_log("module %u is gone", mux->module_index);
+    else if ((idx = mux->defstream_index) == PA_IDXSET_INVALID)
+        pa_log_debug("mux %u do not have default stream", mux->module_index);
+    else if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, idx)))
+        pa_log("can't remove default route: sink-input %u is gone", idx);
+    else {
+        pa_assert_se((u = module->userdata));
+        mux->defstream_index = PA_IDXSET_INVALID;
+
+        if (transfer_to_explicit) {
+            pa_log_debug("converting default route sink-input.%u -> sink.%u "
+                         "to explicit", sinp->index, sinp->sink->index);
+            pa_utils_set_stream_routing_method_property(sinp->proplist, TRUE);
+            return TRUE;
+        }
+        else {
+            /* call Jaska's routine */
+        }
+    }
+
+    return FALSE;
+}
+
+
+pa_bool_t pa_multiplex_add_explicit_route(pa_core    *core,
+                                          pa_muxnode *mux,
+                                          pa_sink    *sink,
+                                          int         type)
+{
+    pa_module *module;
+    pa_sink_input *sinp;
+    struct userdata *u;         /* combine's userdata! */
+
+    pa_assert(core);
+    pa_assert(mux);
+
+    if (!(module = pa_idxset_get_by_index(core->modules, mux->module_index)))
+        pa_log("module %u is gone", mux->module_index);
+    else {
+        pa_assert_se((u = module->userdata));
+
+        if (sink == u->sink) {
+            pa_log("%s: mux %d refuses to make a loopback to itself",
+                   __FILE__, mux->module_index);
+        }
+        else {
+            pa_log_debug("adding explicit route to mux %u", mux->module_index);
+            /* pa_utils_set_stream_routing_properties(sinp->proplist, type, sink); */
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+
+pa_bool_t pa_multiplex_duplicate_route(pa_core       *core,
+                                       pa_muxnode    *mux,
+                                       pa_sink_input *sinp,
+                                       pa_sink       *sink)
+{
+    pa_module       *module;
+    struct userdata *u;   /* combine's userdata! */
     struct output   *o;
     uint32_t         idx;
+    pa_sink_input   *i;
 
     pa_assert(core);
     pa_assert(mux);
-   
-    if ((module = pa_idxset_get_by_index(core->modules, mux->module_index)) &&
-        (u = module->userdata))
-    {
-        pa_log_debug("trying to find the default stream on mux %u",
-                     module->index);
-
-        PA_IDXSET_FOREACH(o, u->outputs, idx) {            
-            if (!(sinp = o->sink_input))
-                continue;
+    pa_assert(sink);
 
-            pa_log_debug("  trying sink input %u", sinp->index);
+    pa_log_debug("check for duplicate route on mux %u",
+                 mux->module_index);
 
-            if (pa_utils_stream_has_default_route(sinp->proplist)) {
-                pa_log_debug("sink input %u is the default", sinp->index);
-                return sinp;
+    if (!(module = pa_idxset_get_by_index(core->modules,mux->module_index)))
+        pa_log("module %u is gone", mux->module_index);
+    else {
+        pa_assert_se((u = module->userdata));
+
+        PA_IDXSET_FOREACH(o, u->outputs, idx) {
+            if (!(i = o->sink_input))
+                continue;
+            if (sinp && i == sinp)
+                continue;
+            if (i->sink == sink) {
+                pa_log_debug("route sink-input.%u -> sink.%u is a duplicate",
+                             i->index, sink->index);
+                return TRUE;
             }
+        }
 
-            pa_log_debug("  no");
+        if (!sinp)
+            pa_log_debug("no duplicate route found to sink.%u", sink->index);
+        else {
+            pa_log_debug("no duplicate found for route sink-input.%u -> "
+                         "sink.%u", sinp->index, sink->index);
         }
     }
-
-    pa_log_debug("could not find the default stream on mux %u", module->index);
-    
-    return NULL;
+        
+    return FALSE;
 }
 
 
@@ -180,8 +264,8 @@ int pa_multiplex_print(pa_muxnode *mux, char *buf, int len)
     if (!mux)
         p += snprintf(p, e-p, "<not set>");
     else {
-        p += snprintf(p, e-p, "module %u, sink %u",
-                      mux->module_index, mux->sink_index);
+        p += snprintf(p, e-p, "module %u, sink %u, default stream %u",
+                      mux->module_index, mux->sink_index,mux->defstream_index);
     }
     
     return p - buf;
index 15949c9..628a22c 100644 (file)
@@ -17,6 +17,7 @@ struct pa_muxnode {
     PA_LLIST_FIELDS(pa_muxnode);
     uint32_t   module_index;
     uint32_t   sink_index;
+    uint32_t   defstream_index;
 };
 
 pa_multiplex *pa_multiplex_init(void);
@@ -29,7 +30,12 @@ void pa_multiplex_destroy(pa_multiplex *, pa_core *, pa_muxnode *);
 
 pa_muxnode *pa_multiplex_find(pa_multiplex *, uint32_t);
 
-pa_sink_input *pa_multiplex_default_stream(pa_core *, pa_muxnode *);
+pa_bool_t pa_multiplex_remove_default_route(pa_core *,pa_muxnode *,pa_bool_t);
+
+pa_bool_t pa_multiplex_add_explicit_route(pa_core*, pa_muxnode*, pa_sink*,int);
+
+pa_bool_t pa_multiplex_duplicate_route(pa_core *, pa_muxnode *,
+                                       pa_sink_input *, pa_sink *);
 
 int pa_multiplex_print(pa_muxnode *, char *, int);
 
index ef76fec..e9b9bb3 100644 (file)
 #include "discover.h"
 #include "router.h"
 
+struct pa_nodeset {
+    pa_idxset *nodes;
+};
+
+
+pa_nodeset *pa_nodeset_init(struct userdata *u)
+{
+    pa_nodeset *ns;
+
+    pa_assert(u);
+
+    ns = pa_xnew0(pa_nodeset, 1);
+    ns->nodes = pa_idxset_new(pa_idxset_trivial_hash_func,
+                              pa_idxset_trivial_compare_func);
+    return ns;
+}
+
+void pa_nodeset_done(struct userdata *u)
+{
+    pa_nodeset *ns;
+
+    if (u && (ns = u->nodeset)) {
+        pa_idxset_free(ns->nodes, NULL, NULL);
+        free(ns);
+    }    
+}
+
 
 mir_node *mir_node_create(struct userdata *u, mir_node *data)
 {
+    pa_nodeset *ns;
     mir_node *node;
 
     pa_assert(u);
     pa_assert(data);
+    pa_assert_se((ns = u->nodeset));
     pa_assert(data->key);
     pa_assert(data->paname);
     
@@ -49,6 +78,8 @@ mir_node *mir_node_create(struct userdata *u, mir_node *data)
             node->paport = pa_xstrdup(data->paport);
     }
 
+    pa_idxset_put(ns->nodes, node, &node->index);
+
     mir_router_register_node(u, node);
     
     return node;
@@ -56,11 +87,16 @@ mir_node *mir_node_create(struct userdata *u, mir_node *data)
 
 void mir_node_destroy(struct userdata *u, mir_node *node)
 {
+    pa_nodeset *ns;
+
     pa_assert(u);
+    pa_assert_se((ns = u->nodeset));
 
     if (node) {
         mir_router_unregister_node(u, node);
 
+        pa_idxset_remove_by_index(ns->nodes, node->index);
+
         pa_xfree(node->key);
         pa_xfree(node->amname);
         pa_xfree(node->amdescr);
@@ -72,6 +108,19 @@ void mir_node_destroy(struct userdata *u, mir_node *node)
     }
 }
 
+mir_node *mir_node_find_by_index(struct userdata *u, uint32_t nodidx)
+{
+    pa_nodeset *ns;
+    mir_node *node;
+
+    pa_assert(u);
+    pa_assert_se((ns = u->nodeset));
+
+    node = pa_idxset_get_by_index(ns->nodes, nodidx);
+
+    return node;
+}
+
 int mir_node_print(mir_node *node, char *buf, int len)
 {
     char *p, *e;
@@ -87,6 +136,7 @@ int mir_node_print(mir_node *node, char *buf, int len)
 
 #define PRINT(f,v) if (p < e) p += snprintf(p, e-p, f "\n", v)
 
+    PRINT("   index         : %u"  ,  node->index);
     PRINT("   key           : '%s'",  node->key ? node->key : "");
     PRINT("   direction     : %s"  ,  mir_direction_str(node->direction));
     PRINT("   implement     : %s"  ,  mir_implement_str(node->implement));
index 703f6fb..8424afa 100644 (file)
@@ -9,6 +9,7 @@
 
 #define AM_ID_INVALID  65535
 
+
 enum mir_direction {
     mir_direction_unknown,
     mir_input,
@@ -87,6 +88,7 @@ struct pa_node_card {
  *          is either a sink_input or a source_output
  */
 struct mir_node {
+    uint32_t        index;    /**< index into nodeset->idxset */
     char           *key;      /**< hash key for discover lookups */
     mir_direction   direction;/**< mir_input | mir_output */
     mir_implement   implement;/**< mir_device | mir_stream */
@@ -113,9 +115,15 @@ struct mir_node {
 };
 
 
+pa_nodeset *pa_nodeset_init(struct userdata *);
+void pa_nodeset_done(struct userdata *);
+
+
 mir_node *mir_node_create(struct userdata *, mir_node *);
 void mir_node_destroy(struct userdata *, mir_node *);
 
+mir_node *mir_node_find_by_index(struct userdata *, uint32_t);
+
 int mir_node_print(mir_node *, char *, int);
 
 const char *mir_direction_str(mir_direction);
index cd3475b..3b50931 100644 (file)
@@ -17,11 +17,11 @@ static void rtgroup_destroy(struct userdata *, mir_rtgroup *);
 static int rtgroup_print(mir_rtgroup *, char *, int);
 static void rtgroup_update_module_property(struct userdata *, mir_rtgroup *);
 
-
 static void add_rtentry(struct userdata *, mir_rtgroup *, mir_node *);
 static void remove_rtentry(struct userdata *, mir_rtentry *);
 
-static mir_node *route_stream(struct userdata *, mir_node *);
+static void make_explicit_routes(struct userdata *, uint32_t);
+static mir_node *find_default_route(struct userdata *, mir_node *);
 
 
 static int uint32_cmp(uint32_t, uint32_t);
@@ -46,20 +46,27 @@ pa_router *pa_router_init(struct userdata *u)
     router->classmap = pa_xnew0(mir_rtgroup *, num_classes);
     router->priormap = pa_xnew0(int, num_classes);
     MIR_DLIST_INIT(router->nodlist);
+    MIR_DLIST_INIT(router->connlist);
     
     return router;
 }
 
 void pa_router_done(struct userdata *u)
 {
-    pa_router  *router;
-    mir_node   *e,*n;
+    pa_router      *router;
+    mir_connection *conn, *c;
+    mir_node       *e,*n;
 
     if (u && (router = u->router)) {
         MIR_DLIST_FOR_EACH_SAFE(mir_node, rtentries, e,n, &router->nodlist) {
             MIR_DLIST_UNLINK(mir_node, rtentries, e);
         }
 
+        MIR_DLIST_FOR_EACH_SAFE(mir_connection,link, conn,c,&router->connlist){
+            MIR_DLIST_UNLINK(mir_connection, link, conn);
+            pa_xfree(conn);
+        }
+
         pa_hashmap_free(router->rtgroups, pa_hashmap_rtgroup_free,u);
 
         pa_xfree(router->classmap);
@@ -215,6 +222,48 @@ void mir_router_unregister_node(struct userdata *u, mir_node *node)
     }
 }
 
+mir_connection *mir_router_add_explicit_route(struct userdata *u,
+                                              uint16_t   amid,
+                                              mir_node  *from,
+                                              mir_node  *to)
+{
+    pa_router *router;
+    mir_connection *conn;
+
+    pa_assert(u);
+    pa_assert(from);
+    pa_assert(to);
+    pa_assert_se((router = u->router));
+
+    conn = pa_xnew0(mir_connection, 1);
+    MIR_DLIST_INIT(conn->link);
+    conn->amid = amid;
+    conn->from = from->index;
+    conn->to = to->index;
+    
+    MIR_DLIST_APPEND(mir_connection, link, conn, &router->connlist);
+
+    mir_router_make_routing(u);
+
+    return conn;
+}
+
+void mir_router_remove_explicit_route(struct userdata *u, mir_connection *conn)
+{
+    pa_router *router;
+
+    pa_assert(u);
+    pa_assert(conn);
+    pa_assert_se((router = u->router));
+
+    MIR_DLIST_UNLINK(mir_connection, link, conn);
+
+    if (!conn->blocked)
+        mir_router_make_routing(u);
+
+    pa_xfree(conn);
+}
+
 int mir_router_print_rtgroups(struct userdata *u, char *buf, int len)
 {
     pa_router *router;
@@ -254,32 +303,41 @@ int mir_router_print_rtgroups(struct userdata *u, char *buf, int len)
 mir_node *mir_router_make_prerouting(struct userdata *u, mir_node *data)
 {
     pa_router     *router;
-    mir_node      *source;
-    mir_node      *sink;
+    mir_node      *from;
+    mir_node      *to;
     int            priority;
     pa_bool_t      done;
     mir_node      *target;
+    uint32_t       stamp;
 
     pa_assert(u);
     pa_assert_se((router = u->router));
+    pa_assert_se((data->implement == mir_stream));
+    pa_assert_se((data->direction == mir_input));
 
     priority = node_priority(u, data);
     done = FALSE;
     target = NULL;
+    stamp = pa_utils_new_stamp();
 
-    MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, source, &router->nodlist) {
-        if (priority >= node_priority(u, source)) {
-            if ((target = route_stream(u, data)))
-                mir_switch_setup_link(u, NULL, target);
+    make_explicit_routes(u, stamp);
+
+    MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, from, &router->nodlist) {
+        if (priority >= node_priority(u, from)) {
+            if ((target = find_default_route(u, data)))
+                mir_switch_setup_link(u, NULL, target, FALSE);
             done = TRUE;
         }
 
-        if ((sink = route_stream(u, source)))
-            mir_switch_setup_link(u, source, sink);
+        if (from->stamp >= stamp)
+            continue;
+
+        if ((to = find_default_route(u, from)))
+            mir_switch_setup_link(u, from, to, FALSE);
     }    
 
-    if (!done && (target = route_stream(u, data)))
-        mir_switch_setup_link(u, NULL, target);
+    if (!done && (target = find_default_route(u, data)))
+        mir_switch_setup_link(u, NULL, target, FALSE);
 
     return target;
 }
@@ -289,9 +347,10 @@ void mir_router_make_routing(struct userdata *u)
 {
     static pa_bool_t ongoing_routing;
 
-    pa_router     *router;
-    mir_node      *source;
-    mir_node      *sink;
+    pa_router  *router;
+    mir_node   *from;
+    mir_node   *to;
+    uint32_t    stamp;
 
     pa_assert(u);
     pa_assert_se((router = u->router));
@@ -300,10 +359,16 @@ void mir_router_make_routing(struct userdata *u)
         return;
 
     ongoing_routing = TRUE;
+    stamp = pa_utils_new_stamp();
+
+    make_explicit_routes(u, stamp);
 
-    MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, source, &router->nodlist) {
-        if ((sink = route_stream(u, source)))
-            mir_switch_setup_link(u, source, sink);
+    MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, from, &router->nodlist) {
+        if (from->stamp >= stamp)
+            continue;
+
+        if ((to = find_default_route(u, from)))
+            mir_switch_setup_link(u, from, to, FALSE);
     }    
 
     ongoing_routing = FALSE;
@@ -494,64 +559,94 @@ static void remove_rtentry(struct userdata *u, mir_rtentry *rte)
     pa_xfree(rte);
 }
 
+static void make_explicit_routes(struct userdata *u, uint32_t stamp)
+{
+    pa_router *router;
+    mir_connection *conn;
+    mir_node *from;
+    mir_node *to;
+
+    pa_assert(u);
+    pa_assert_se((router = u->router));
+
+    MIR_DLIST_FOR_EACH_BACKWARD(mir_connection,link, conn, &router->connlist) {
+        if (conn->blocked)
+            continue;
+        
+        if (!(from = mir_node_find_by_index(u, conn->from)) ||
+            !(to   = mir_node_find_by_index(u, conn->to))     )
+        {
+            pa_log_debug("ignoring explicit route %u: some of the nodes "
+                         "not found", conn->amid);
+            continue;
+        }
+
+        if (!mir_switch_setup_link(u, from, to, TRUE))
+            continue;
+
+        if (from->implement == mir_stream)
+            from->stamp = stamp;
+    }
+}
+
 
-static mir_node *route_stream(struct userdata *u, mir_node *source)
+static mir_node *find_default_route(struct userdata *u, mir_node *from)
 {
     pa_router     *router = u->router;
-    mir_node_type  class  = source->type;
-    mir_node      *sink;
+    mir_node_type  class  = from->type;
+    mir_node      *to;
     mir_rtgroup   *rtg;
     mir_rtentry   *rte;
 
 
     if (class < 0 || class > router->maplen) {
         pa_log_debug("can't route '%s': class %d is out of range (0 - %d)",
-                     source->amname, class, router->maplen);
+                     from->amname, class, router->maplen);
         return NULL;
     }
     
     if (!(rtg = router->classmap[class])) {
         pa_log_debug("node '%s' won't be routed beacuse its class '%s' "
                      "is not assigned to any router group",
-                     source->amname, mir_node_type_str(class));
+                     from->amname, mir_node_type_str(class));
         return NULL;
     }
     
     pa_log_debug("using '%s' router group when routing '%s'",
-                 rtg->name, source->amname);
+                 rtg->name, from->amname);
 
         
     MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
-        if (!(sink = rte->node)) {
+        if (!(to = rte->node)) {
             pa_log("   node was null in mir_rtentry");
             continue;
         }
         
-        if (sink->ignore) {
-            pa_log_debug("   '%s' ignored. Skipping...",sink->amname);
+        if (to->ignore) {
+            pa_log_debug("   '%s' ignored. Skipping...",to->amname);
             continue;
         }
 
-        if (!sink->available) {
-            pa_log_debug("   '%s' not available. Skipping...", sink->amname);
+        if (!to->available) {
+            pa_log_debug("   '%s' not available. Skipping...", to->amname);
             continue;
         }
 
-        if (sink->paidx == PA_IDXSET_INVALID) {
-            if (sink->type != mir_bluetooth_a2dp &&
-                sink->type != mir_bluetooth_sco)
+        if (to->paidx == PA_IDXSET_INVALID) {
+            if (to->type != mir_bluetooth_a2dp &&
+                to->type != mir_bluetooth_sco)
             {
-                pa_log_debug("   '%s' has no sink. Skipping...", sink->amname);
+                pa_log_debug("   '%s' has no to. Skipping...", to->amname);
                 continue;
             }
         }
         
-        pa_log_debug("routing '%s' => '%s'", source->amname, sink->amname);
+        pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
 
-        return sink;
+        return to;
     }
     
-    pa_log_debug("could not find route for '%s'", source->amname);
+    pa_log_debug("could not find route for '%s'", from->amname);
 
     return NULL;
 }
index 40221c5..87f2f49 100644 (file)
@@ -13,10 +13,11 @@ typedef int       (*mir_rtgroup_compare_t)(struct userdata *u,
 
 struct pa_router {
     pa_hashmap   *rtgroups;
-    int           maplen;       /**< length of the class- and priormap */
-    mir_rtgroup **classmap;     /**< to map device node types to rtgroups  */
-    int          *priormap;     /**< stream node priorities */
-    mir_dlist     nodlist;      /**< priorized list of the nodes  */
+    int           maplen;     /**< length of the class- and priormap */
+    mir_rtgroup **classmap;   /**< to map device node types to rtgroups */
+    int          *priormap;   /**< stream node priorities */
+    mir_dlist     nodlist;    /**< priorized list of the input stream nodes */
+    mir_dlist     connlist;   /**< listhead of the connections */
 };
 
 
@@ -35,6 +36,15 @@ struct mir_rtgroup {
     mir_rtgroup_compare_t compare;  /**< comparision function for ordering */
 };
 
+struct mir_connection {
+    mir_dlist     link;     /**< list of connections */
+    pa_bool_t     blocked;  /**< true if this conflicts with another route */
+    uint16_t      amid;     /**< audio manager connection id */
+    uint32_t      from;     /**< source node index */
+    uint32_t      to;       /**< destination node index */
+    uint32_t      stream;   /**< index of the sink-input to be routed */
+};
+
 
 pa_router *pa_router_init(struct userdata *);
 void pa_router_done(struct userdata *);
@@ -52,6 +62,11 @@ void mir_router_unregister_node(struct userdata *, mir_node *);
 mir_node *mir_router_make_prerouting(struct userdata *, mir_node *);
 void mir_router_make_routing(struct userdata *);
 
+mir_connection *mir_router_add_explicit_route(struct userdata *, uint16_t,
+                                              mir_node *, mir_node *);
+void mir_router_remove_explicit_route(struct userdata *, mir_connection *);
+
+
 int mir_router_print_rtgroups(struct userdata *, char *, int);
 
 pa_bool_t mir_router_default_accept(struct userdata *, mir_rtgroup *,
index 5960a32..d01a5f9 100644 (file)
 #include "multiplex.h"
 
 
-pa_bool_t set_profile(struct userdata *, mir_node *);
+static pa_bool_t explicit_link_from_stream_to_device(struct userdata *,
+                                                     mir_node *, mir_node *);
+static pa_bool_t default_link_from_stream_to_device(struct userdata *,
+                                                    mir_node *, mir_node *);
 
 
-pa_bool_t mir_switch_setup_link(struct userdata *u,mir_node *from,mir_node *to)
+static pa_sink *setup_device_output(struct userdata *, mir_node *);
+
+static pa_bool_t set_profile(struct userdata *, mir_node *);
+
+
+pa_bool_t mir_switch_setup_link(struct userdata *u,
+                                mir_node *from,
+                                mir_node *to,
+                                pa_bool_t explicit)
+{
+    pa_core *core;
+
+    pa_assert(u);
+    pa_assert(to);
+    pa_assert_se((core = u->core));
+    pa_assert(!from || from->direction == mir_input);
+    pa_assert(to->direction == mir_output);
+
+    if (explicit) {
+        /*
+         * links for explic routes
+         */
+        pa_assert(from);
+
+        switch (from->implement) {
+
+        case mir_stream:
+            switch (to->implement) {
+
+            case mir_stream:
+                pa_log_debug("routing to streams is not implemented yet");
+                break;
+
+            case mir_device:
+                if (!explicit_link_from_stream_to_device(u, from, to))
+                    return FALSE;
+                break;
+
+            default:
+                pa_log("%s: can't setup link: invalid sink node "
+                       "implement", __FILE__);
+                return FALSE;
+            }
+            break;
+
+        case mir_device:
+            pa_log_debug("input device routing is not implemented yet");
+            break;
+
+        default:
+            pa_log("%s: can't setup link: invalid source node "
+                   "implement", __FILE__);
+            return FALSE;
+        }
+    }
+    else {
+        /*
+         * links for default routes
+         */
+        switch (to->implement) {
+
+        case mir_stream:
+            pa_log_debug("routing to a stream is not implemented yet");
+            break;
+
+        case mir_device:
+            if (!from) /* prerouting */
+                return (!explicit && setup_device_output(u, to) != NULL);
+            else {
+                switch (from->implement) {
+
+                case mir_stream:
+                    if (!default_link_from_stream_to_device(u, from, to))
+                        return FALSE;
+                    break;
+
+                case mir_device:
+                    pa_log("%s: default device -> device route is "
+                           "not supported", __FILE__);
+                    break;
+
+                default:
+                    pa_log("%s: can't setup link: invalid source node "
+                           "implement", __FILE__);
+                    return FALSE;
+                }
+            }
+            break;
+
+        default:
+            pa_log("%s: can't setup link: invalid sink node "
+                   "implement", __FILE__);
+            return FALSE;
+        }
+    }
+
+    pa_log_debug("link %s => %s is established", from->amname, to->amname);
+
+    return TRUE;
+}
+
+static pa_bool_t explicit_link_from_stream_to_device(struct userdata *u,
+                                                     mir_node *from,
+                                                     mir_node *to)
 {
     pa_core       *core;
-    pa_sink_input *sinp;
     pa_sink       *sink;
+    pa_sink_input *sinp;
     pa_muxnode    *mux;
 
     pa_assert(u);
+    pa_assert(from);
     pa_assert(to);
-    pa_assert_se((core = u->core));
+    pa_assert((core = u->core));
 
-    if (!set_profile(u, to)) {
-        pa_log("can't route to '%s'", to->amname);
+    if (!(sink = setup_device_output(u, to)))
         return FALSE;
-    }
 
-    if (to->paidx == PA_IDXSET_INVALID) {
-        pa_log_debug("can't route to '%s': no sink", to->amname);
+    if (!set_profile(u, from)) {
+        pa_log("can't route from '%s'", from->amname);
         return FALSE;
     }
 
-    if (!(sink = pa_idxset_get_by_index(core->sinks, to->paidx))) {
-        pa_log_debug("can't route to '%s': cant find sink", to->amname);
-        return FALSE;
+    if ((mux = from->mux)) {
+        sinp = pa_idxset_get_by_index(core->sink_inputs, mux->defstream_index);
+
+        if (sinp && sinp->sink == sink) {
+            if (!pa_multiplex_remove_default_route(core, mux, TRUE))
+                return FALSE;
+        }
+        else if (pa_multiplex_duplicate_route(core, mux, NULL, sink)) {
+            pa_log_debug("multiplex route %s => %s already exists",
+                         from->amname, to->amname);
+            return TRUE;
+        }
+        else {
+            if (!pa_multiplex_add_explicit_route(core, mux, sink, from->type))
+                return FALSE;
+        }
+    }
+    else {
+        if ((sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
+            if (sinp->sink == sink)
+                pa_log_debug("direct route already exists. nothing to do");
+            else {
+                pa_log_debug("direct route: sink-input.%u -> sink.%u",
+                             sinp->index, sink->index);
+
+                if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
+                    return FALSE;
+            }
+        }
     }
 
-    if (!from)
-        return TRUE;
+    pa_log_debug("link %s => %s is established", from->amname, to->amname);
+
+    return TRUE;
+}
+
+
+static pa_bool_t default_link_from_stream_to_device(struct userdata *u,
+                                                    mir_node *from,
+                                                    mir_node *to)
+{
+    pa_core       *core;
+    pa_sink       *sink;
+    pa_sink_input *sinp;
+    pa_muxnode    *mux;
+
+    pa_assert(u);
+    pa_assert(from);
+    pa_assert(to);
+    pa_assert((core = u->core));
+
+    if (!(sink = setup_device_output(u, to)))
+        return FALSE;
 
     if (!set_profile(u, from)) {
         pa_log("can't route from '%s'", from->amname);
         return FALSE;
     }
 
+    if ((mux = from->mux)) {
+        sinp = pa_idxset_get_by_index(core->sink_inputs, mux->defstream_index);
 
-    if (from->implement == mir_stream && (mux = from->mux)) {
-        if (!(sinp = pa_multiplex_default_stream(core, mux))) {
-            pa_log_debug("can't find default stream on mux %u",
-                         mux->module_index); 
+        if (!sinp) {
+            pa_log_debug("no default sstream found on multiplex %u",
+                         mux->module_index);
+            mux->defstream_index = PA_IDXSET_INVALID;
             return FALSE;
         }
+        else if (pa_multiplex_duplicate_route(core, mux, sinp, sink)) {
+            pa_log_debug("default stream on mux %u would be a duplicate "
+                         "to an explicit route. Removing it ...",
+                         mux->module_index);
+            return TRUE;        /* the routing is a success */
+        }
+            
+        pa_log_debug("multiplex route: sink-input.%d -> (sink.%d - "
+                     "sink-input.%d) -> sink.%d", from->paidx,
+                     mux->sink_index, sinp->index, sink->index);
     }
     else {
         if (from->paidx == PA_IDXSET_INVALID) {
@@ -70,9 +233,12 @@ pa_bool_t mir_switch_setup_link(struct userdata *u,mir_node *from,mir_node *to)
         }
 
         if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
-            pa_log_debug("can't route '%s': cant find sink-input", to->amname);
+            pa_log_debug("can't find sink input for '%s'", from->amname);
             return FALSE;
         }
+
+        pa_log_debug("direct route: sink-input.%d -> sink.%d",
+                     sinp->index, sink->index);
     }
 
     if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
@@ -81,8 +247,35 @@ pa_bool_t mir_switch_setup_link(struct userdata *u,mir_node *from,mir_node *to)
     return TRUE;
 }
 
+static pa_sink *setup_device_output(struct userdata *u, mir_node *node)
+{
+    pa_core *core;
+    pa_sink *sink;
+
+    pa_assert(u);
+    pa_assert(node);
+    pa_assert_se((core = u->core));
+
+    if (!set_profile(u, node)) {
+        pa_log("can't route to '%s'", node->amname);
+        return NULL;
+    }
+
+    if (node->paidx == PA_IDXSET_INVALID) {
+        pa_log_debug("can't route to '%s': no sink", node->amname);
+        return NULL;
+    }
+
+    if (!(sink = pa_idxset_get_by_index(core->sinks, node->paidx))) {
+        pa_log_debug("can't route to '%s': cant find sink", node->amname);
+        return NULL;
+    }
+
+    return sink;
+}
+
 
-pa_bool_t set_profile(struct userdata *u, mir_node *node)
+static pa_bool_t set_profile(struct userdata *u, mir_node *node)
 {
     pa_core         *core;
     pa_card         *card;
@@ -129,6 +322,7 @@ pa_bool_t set_profile(struct userdata *u, mir_node *node)
 }
 
 
+
 /*
  * Local Variables:
  * c-basic-offset: 4
index b9dd484..deb4b64 100644 (file)
@@ -6,7 +6,8 @@
 #include "userdata.h"
 
 
-pa_bool_t mir_switch_setup_link(struct userdata *, mir_node *, mir_node *);
+pa_bool_t mir_switch_setup_link(struct userdata *, mir_node *, mir_node *,
+                                pa_bool_t);
 
 
 #endif  /* foomirswitchfoo */
index 4f31700..f138ce7 100644 (file)
@@ -163,7 +163,6 @@ void pa_tracker_synchronize(struct userdata *u)
     pa_assert(u);
     pa_assert_se((core = u->core));
 
-    pa_utils_new_stamp();
 
     PA_IDXSET_FOREACH(card, core->cards, index) {
         pa_discover_add_card(u, card);
@@ -200,7 +199,6 @@ static pa_hook_result_t card_put(void *hook_data,
     pa_assert(u);
     pa_assert(card);
 
-    pa_utils_new_stamp();
     pa_discover_add_card(u, card);
 
     return PA_HOOK_OK;
@@ -238,7 +236,6 @@ static pa_hook_result_t card_profile_changed(void *hook_data,
     pa_assert(u);
     pa_assert(card);
 
-    pa_utils_new_stamp();
     pa_discover_profile_changed(u, card);
 
     return PA_HOOK_OK;
@@ -255,7 +252,6 @@ static pa_hook_result_t sink_put(void *hook_data,
     pa_assert(u);
     pa_assert(sink);
 
-    pa_utils_new_stamp();
     pa_discover_add_sink(u, sink, TRUE);
 
     return PA_HOOK_OK;
@@ -315,7 +311,6 @@ static pa_hook_result_t source_put(void *hook_data,
     pa_assert(u);
     pa_assert(source);
 
-    pa_utils_new_stamp();
     pa_discover_add_source(u, source);
 
     return PA_HOOK_OK;
@@ -376,7 +371,6 @@ static pa_hook_result_t sink_input_new(void *hook_data,
     pa_assert(u);
     pa_assert(data);
 
-    pa_utils_new_stamp();
     pa_discover_preroute_sink_input(u, data);
 
     return PA_HOOK_OK;
@@ -392,7 +386,6 @@ static pa_hook_result_t sink_input_put(void *hook_data,
     pa_assert(u);
     pa_assert(sinp);
 
-    pa_utils_new_stamp();
     pa_discover_add_sink_input(u, sinp);
 
     return PA_HOOK_OK;
index 92f21a4..e6d84ef 100644 (file)
@@ -25,6 +25,7 @@ typedef struct pa_policy_dbusif      pa_policy_dbusif;
 typedef struct pa_discover           pa_discover;
 typedef struct pa_router             pa_router;
 typedef struct pa_mir_config         pa_mir_config;
+typedef struct pa_nodeset            pa_nodeset;
 typedef struct pa_node_card          pa_node_card;
 typedef struct pa_card_hooks         pa_card_hooks;
 typedef struct pa_sink_hooks         pa_sink_hooks;
@@ -39,6 +40,7 @@ typedef enum   mir_privacy           mir_privacy;
 typedef struct mir_node              mir_node;
 typedef struct mir_rtgroup           mir_rtgroup;
 typedef struct mir_rtentry           mir_rtentry;
+typedef struct mir_connection        mir_connection;
 
 typedef struct am_domainreg_data     am_domainreg_data;
 typedef struct am_nodereg_data       am_nodereg_data;
@@ -60,6 +62,7 @@ struct userdata {
     pa_core           *core;
     pa_module         *module;
     pa_null_sink      *nullsink;
+    pa_nodeset        *nodeset;
     pa_audiomgr       *audiomgr;
     pa_policy_dbusif  *dbusif;
     pa_discover       *discover;
index 2ab0e11..bc7cf55 100644 (file)
@@ -169,8 +169,19 @@ void pa_utils_set_stream_routing_properties(pa_proplist *pl,
         pa_proplist_sets(pl, PA_PROP_ROUTING_CLASS_ID  , clid  ) < 0 ||
         pa_proplist_sets(pl, PA_PROP_ROUTING_METHOD    , method) < 0  )
     {
-        pa_log("failed to set properties on sink-input. "
-               "some routing function might malfunction later on");
+        pa_log("failed to set some property on sink-input");
+    }
+}
+
+void pa_utils_set_stream_routing_method_property(pa_proplist *pl,
+                                                 pa_bool_t explicit)
+{
+    const char *method = explicit ? PA_ROUTING_EXPLICIT : PA_ROUTING_DEFAULT;
+
+    pa_assert(pl);
+    
+    if (pa_proplist_sets(pl, PA_PROP_ROUTING_METHOD, method) < 0) {
+        pa_log("failed to set routing method property on sink-input");
     }
 }
 
@@ -216,12 +227,12 @@ const char *pa_utils_file_path(const char *file, char *buf, size_t len)
 }
 
 
-const uint32_t pa_utils_new_stamp(void)
+uint32_t pa_utils_new_stamp(void)
 {
     return ++stamp;
 }
 
-const uint32_t pa_utils_get_stamp(void)
+uint32_t pa_utils_get_stamp(void)
 {
     return stamp;
 }
index 2aa2737..8a4f10e 100644 (file)
@@ -18,13 +18,14 @@ char *pa_utils_get_sink_input_name(pa_sink_input *);
 char *pa_utils_get_sink_input_name_from_data(pa_sink_input_new_data *);
 
 void  pa_utils_set_stream_routing_properties(pa_proplist *, int, pa_sink *);
+void  pa_utils_set_stream_routing_method_property(pa_proplist *, pa_bool_t);
 pa_bool_t pa_utils_stream_has_default_route(pa_proplist *);
 
 
 const char *pa_utils_file_path(const char *, char *, size_t);
 
-const uint32_t pa_utils_new_stamp(void);
-const uint32_t pa_utils_get_stamp(void);
+uint32_t pa_utils_new_stamp(void);
+uint32_t pa_utils_get_stamp(void);
 
 #endif