scripting: support for zone based routing in application classes
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / murphyif.c
index cd90c3e..9c36cc3 100644 (file)
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <string.h>
+#include <alloca.h>
 #include <errno.h>
 
 #include <pulse/utf8.h>
@@ -54,6 +55,7 @@
 #include "murphyif.h"
 #include "node.h"
 #include "stream-state.h"
+#include "utils.h"
 
 #ifdef WITH_RESOURCES
 #define INVALID_ID      (~(uint32_t)0)
@@ -135,7 +137,10 @@ typedef struct {
         uint32_t request;
         uint32_t reply;
     }                seqno;
-    pa_hashmap      *nodes;
+    struct {
+        pa_hashmap *rsetid;
+        pa_hashmap *pid;
+    }                nodes;
     PA_LLIST_HEAD(resource_attribute, attrs);
     PA_LLIST_HEAD(resource_request, reqs);
 #endif
@@ -148,9 +153,31 @@ struct pa_murphyif {
 #endif
     domctl_interface domctl;
     resource_interface resource;
-    pa_hashmap *nodes;
 };
 
+#ifdef WITH_RESOURCES
+typedef struct {
+    const char *id;
+    pa_bool_t   autorel;
+    int         state;
+    pa_bool_t   grant;
+    const char *policy;
+} rset_data;
+
+typedef struct {
+    char       *pid;
+    mir_node   *node;
+    rset_data  *rset;
+} pid_hash;
+
+typedef struct {
+    size_t      nnode;
+    mir_node  **nodes;
+    rset_data  *rset;
+} rset_hash;
+
+#endif
+
 
 #ifdef WITH_DOMCTL
 static void domctl_connect_notify(mrp_domctl_t *,int,int,const char *,void *);
@@ -202,8 +229,33 @@ static void connect_attempt(pa_mainloop_api *, pa_time_event *,
                              const struct timeval *, void *);
 static void schedule_connect(struct userdata *, resource_interface *);
 static void cancel_schedule(struct userdata *, resource_interface *);
+
+static rset_hash *node_put_rset(struct userdata *, mir_node *, rset_data *);
+static void node_enforce_resource_policy(struct userdata *, mir_node *,
+                                         rset_data *);
+static rset_data *rset_data_dup(rset_data *);
+static void rset_data_copy(rset_data *, rset_data *);
+static void rset_data_update(rset_data *, rset_data *);
+static void rset_data_free(rset_data *);
+
+static void        pid_hashmap_free(void *, void *);
+static int         pid_hashmap_put(struct userdata *, const char *,
+                                   mir_node *, rset_data *);
+static mir_node   *pid_hashmap_get_node(struct userdata *, const char *);
+static rset_data  *pid_hashmap_get_rset(struct userdata *, const char *);
+static mir_node   *pid_hashmap_remove_node(struct userdata *, const char *);
+static rset_data  *pid_hashmap_remove_rset(struct userdata *, const char *);
+
+static void       rset_hashmap_free(void *, void *);
+static rset_hash *rset_hashmap_put(struct userdata *, const char *, mir_node *);
+static rset_hash *rset_hashmap_get(struct userdata *u, const char *rsetid);
+static int        rset_hashmap_remove(struct userdata *,const char *,mir_node*);
+
 #endif
 
+static pa_proplist *get_node_proplist(struct userdata *, mir_node *);
+static const char *get_node_pid(struct userdata *, mir_node *);
+
 
 pa_murphyif *pa_murphyif_init(struct userdata *u,
                               const char *ctl_addr,
@@ -258,14 +310,14 @@ pa_murphyif *pa_murphyif_init(struct userdata *u,
     }    
 
     rif->seqno.request = 1;
-    rif->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
-                                pa_idxset_trivial_compare_func);
+    rif->nodes.rsetid = pa_hashmap_new(pa_idxset_string_hash_func,
+                                       pa_idxset_string_compare_func);
+    rif->nodes.pid = pa_hashmap_new(pa_idxset_string_hash_func,
+                                    pa_idxset_string_compare_func);
     PA_LLIST_HEAD_INIT(resource_attribute, rif->attrs);
     PA_LLIST_HEAD_INIT(resource_request, rif->reqs);
 #endif
 
-    murphyif->nodes = pa_hashmap_new(pa_idxset_string_hash_func,
-                                     pa_idxset_string_compare_func);
     return murphyif;
 }
 
@@ -278,6 +330,9 @@ void pa_murphyif_done(struct userdata *u)
 #ifdef WITH_RESOURCES
     resource_attribute *attr, *a;
     resource_request *req, *r;
+    void *state;
+    rset_hash *rh;
+    pid_hash *ph;
 #endif
 
     if (u && (murphyif = u->murphyif)) {
@@ -289,7 +344,6 @@ void pa_murphyif_done(struct userdata *u)
         dif = &murphyif->domctl;
 
         mrp_domctl_destroy(dif->ctl);
-        mrp_mainloop_destroy(murphyif->ml);
 
         if (dif->ntable > 0 && dif->tables) {
             for (i = 0;  i < dif->ntable;  i++) {
@@ -319,8 +373,24 @@ void pa_murphyif_done(struct userdata *u)
 
         resource_transport_destroy(murphyif);
 
-        pa_xfree((void *)rif->atype);
-        pa_hashmap_free(rif->nodes, NULL, NULL);
+        PA_HASHMAP_FOREACH(rh, rif->nodes.rsetid, state) {
+            if (rh) {
+                pa_xfree(rh->nodes);
+                rset_data_free(rh->rset);
+                pa_xfree(rh);
+            }
+        }
+
+        PA_HASHMAP_FOREACH(ph, rif->nodes.pid, state) {
+            if (ph) {
+                pa_xfree((void *)ph->pid);
+                rset_data_free(ph->rset);
+                pa_xfree(ph);
+            }
+        }
+
+        pa_hashmap_free(rif->nodes.rsetid, NULL);
+        pa_hashmap_free(rif->nodes.pid, NULL);
 
         PA_LLIST_FOREACH_SAFE(attr, a, rif->attrs)
             resource_attribute_destroy(rif, attr);
@@ -332,8 +402,7 @@ void pa_murphyif_done(struct userdata *u)
         pa_xfree((void *)rif->inpres.name);
         pa_xfree((void *)rif->outres.name);
 #endif
-
-        pa_hashmap_free(murphyif->nodes, NULL, NULL);
+        mrp_mainloop_destroy(murphyif->ml);
 
         pa_xfree(murphyif);
     }
@@ -533,18 +602,17 @@ void pa_murphyif_create_resource_set(struct userdata *u,
     pa_core *core;
     pa_murphyif *murphyif;
     resource_interface *rif;
-    const char *class;
     int state;
 
     pa_assert(u);
     pa_assert(node);
-    pa_assert(node->implement == mir_stream);
+    pa_assert((!node->loop && node->implement == mir_stream) ||
+              ( node->loop && node->implement == mir_device)   );
     pa_assert(node->direction == mir_input || node->direction == mir_output);
     pa_assert(node->zone);
     pa_assert(!node->rsetid);
 
     pa_assert_se((core = u->core));
-    pa_assert_se((class = pa_nodeset_get_class(u, node->type)));
 
     pa_assert_se((murphyif = u->murphyif));
     rif = &murphyif->resource;
@@ -577,6 +645,9 @@ void pa_murphyif_destroy_resource_set(struct userdata *u, mir_node *node)
     pa_assert_se((murphyif = u->murphyif));
 
     if (node->localrset && node->rsetid) {
+
+        pa_murphyif_delete_node(u, node);
+
         rsetid = strtoul(node->rsetid, &e, 10);
 
         if (e == node->rsetid || *e) {
@@ -584,10 +655,15 @@ void pa_murphyif_destroy_resource_set(struct userdata *u, mir_node *node)
                    node->rsetid);
         }
         else {
+            if (rset_hashmap_remove(u, node->rsetid, node) < 0) {
+                pa_log_debug("failed to remove resource set %s from hashmap",
+                             node->rsetid);
+            }
+
             if (resource_set_destroy_node(u, rsetid))
-                pa_log_debug("resource set %u destruction request", rsetid);
+                pa_log_debug("sent resource set %u destruction request", rsetid);
             else {
-                pa_log("falied to destroy resourse set %u for node '%s'",
+                pa_log("failed to destroy resourse set %u for node '%s'",
                        rsetid, node->amname);
             }
 
@@ -596,8 +672,6 @@ void pa_murphyif_destroy_resource_set(struct userdata *u, mir_node *node)
             node->localrset = FALSE;
             node->rsetid = NULL;
         }
-
-        pa_murphyif_delete_node(u, node);
     }
 }
 
@@ -605,24 +679,64 @@ int pa_murphyif_add_node(struct userdata *u, mir_node *node)
 {
 #ifdef WITH_RESOURCES
     pa_murphyif *murphyif;
+    resource_interface *rif;
+    const char *pid;
+    rset_data *rset;
+    rset_hash *rh;
+    char buf[64];
 
     pa_assert(u);
     pa_assert(node);
-    pa_assert(node->implement == mir_stream);
 
     pa_assert_se((murphyif = u->murphyif));
 
+    rif = &murphyif->resource;
+
     if (!node->rsetid) {
-        pa_log("can't register resource set for node '%s'.: missing rsetid",
-               node->amname);
+        pa_log("can't register resource set for node %u '%s'.: missing rsetid",
+               node->paidx, node->amname);
+    }
+    else if (pa_streq(node->rsetid, PA_RESOURCE_SET_ID_PID)) {
+        if (!(pid = get_node_pid(u,node)))
+            pa_log("can't obtain PID for node '%s'", node->amname);
+        else {
+            if (pid_hashmap_put(u, pid, node, NULL) == 0)
+                return 0;
+
+            if ((rset = pid_hashmap_remove_rset(u, pid))) {
+                pa_log_debug("found resource-set %s for node '%s'",
+                             rset->id, node->amname);
+
+                if (node_put_rset(u, node, rset)) {
+                    node_enforce_resource_policy(u, node, rset);
+                    rset_data_free(rset);
+                    return 0;
+                }
+
+                pa_log("can't register resource set for node '%s': "
+                       "failed to set rsetid", node->amname);
+
+                rset_data_free(rset);
+            }
+            else {
+                pa_log("can't register resource set for node '%s': "
+                       "conflicting pid", node->amname);
+            }
+        }
     }
     else {
-        if (pa_hashmap_put(murphyif->nodes, node->rsetid, node) == 0)
+        if ((rh = rset_hashmap_put(u, node->rsetid, node))) {
+            rset = rh->rset;
+
+            pa_log_debug("enforce policies on node %u '%s' rsetid:%s autorel:%s "
+                         "state:%s grant:%s policy:%s", node->paidx, node->amname,
+                         rset->id, rset->autorel ? "yes":"no",
+                         rset->state == RSET_ACQUIRE ? "acquire":"release",
+                         rset->grant ? "yes":"no", rset->policy);
+
+            node_enforce_resource_policy(u, node, rset);
             return 0;
-        else {
-            pa_log("can't register resource set for node '%s': conflicting "
-                   "resource id '%s'", node->amname, node->rsetid);
-        } 
+        }
     }
 
     return -1;
@@ -635,41 +749,38 @@ void pa_murphyif_delete_node(struct userdata *u, mir_node *node)
 {
 #ifdef WITH_RESOURCES
     pa_murphyif *murphyif;
+    resource_interface *rif;
+    const char *pid;
     mir_node *deleted;
 
     pa_assert(u);
     pa_assert(node);
-    pa_assert(node->implement == mir_stream);
 
     pa_assert_se((murphyif = u->murphyif));
 
+    rif = &murphyif->resource;
+
     if (node->rsetid) {
-        deleted = pa_hashmap_remove(murphyif->nodes, node->rsetid);
-        pa_assert(deleted == node);
+        if (pa_streq(node->rsetid, PA_RESOURCE_SET_ID_PID)) {
+            if ((pid = get_node_pid(u, node))) {
+                if (node == pid_hashmap_get_node(u, pid))
+                    pid_hashmap_remove_node(u, pid);
+                else {
+                    pa_log("pid %s seems to have multiple resource sets. "
+                           "Refuse to delete node %u (%s) from hashmap",
+                           pid, node->index, node->amname);
+                }
+            }
+        }
+        else {
+            if (rset_hashmap_remove(u, node->rsetid, node) < 0) {
+                pa_log("failed to remove node '%s' from rset hash", node->amname);
+            }
+        }
     }
 #endif
 }
 
-mir_node *pa_murphyif_find_node(struct userdata *u, const char *rsetid)
-{
-#ifdef WITH_RESOURCES
-    pa_murphyif *murphyif;
-    mir_node *node;
-
-    pa_assert(u);
-    pa_assert_se((murphyif = u->murphyif));
-
-    if (!rsetid)
-        node = NULL;
-    else
-        node = pa_hashmap_get(murphyif->nodes, rsetid);
-
-    return node;
-#else
-    return NULL;
-#endif
-}
-
 
 #ifdef WITH_DOMCTL
 static void domctl_connect_notify(mrp_domctl_t *dc, int connected, int errcode,
@@ -902,11 +1013,15 @@ static pa_bool_t resource_set_create_node(struct userdata *u,
     uint16_t reqid;
     uint32_t seqno;
     uint32_t rset_flags;
+    const char *role;
+    pa_nodeset_map *map;
     const char *class;
+    pa_loopnode *loop;
     pa_sink_input *sinp;
     pa_source_output *sout;
     audio_resource_t *res;
     const char *resnam;
+    mir_node_type type = 0;
     uint32_t audio_flags = 0;
     uint32_t priority;
     pa_proplist *proplist = NULL;
@@ -915,23 +1030,45 @@ static pa_bool_t resource_set_create_node(struct userdata *u,
     pa_assert(u);
     pa_assert(node);
     pa_assert(node->index != PA_IDXSET_INVALID);
-    pa_assert(node->implement == mir_stream);
+    pa_assert((!node->loop && node->implement == mir_stream) ||
+              ( node->loop && node->implement == mir_device)   );
     pa_assert(node->direction == mir_input || node->direction == mir_output);
     pa_assert(node->zone);
     pa_assert(!node->rsetid);
 
     pa_assert_se((core = u->core));
-    pa_assert_se((class = pa_nodeset_get_class(u, node->type)));
 
-    if (node->direction == mir_output) {
-        if ((sout = pa_idxset_get_by_index(core->source_outputs, node->paidx)))
-            proplist = sout->proplist;
+    if ((loop = node->loop)) {
+        if (node->direction == mir_input) {
+            sout = pa_idxset_get_by_index(core->source_outputs,
+                                          loop->source_output_index);
+            if (sout)
+                proplist = sout->proplist;
+        }
+        else {
+            sinp = pa_idxset_get_by_index(core->sink_inputs,
+                                          loop->sink_input_index);
+            if (sinp)
+                proplist = sinp->proplist;
+        }
+        if (proplist && (role = pa_proplist_gets(proplist, PA_PROP_MEDIA_ROLE))) {
+            if ((map = pa_nodeset_get_map_by_role(u, role)))
+                type = map->type;
+        }
     }
     else {
-        if ((sinp = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
-            proplist = sinp->proplist;
+        if (node->direction == mir_output) {
+            if ((sout = pa_idxset_get_by_index(core->source_outputs, node->paidx)))
+                proplist = sout->proplist;
+        }
+        else {
+            if ((sinp = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
+                proplist = sinp->proplist;
+        }
+        type = node->type;
     }
 
+    pa_assert_se((class = pa_nodeset_get_class(u, type)));
     pa_assert_se((murphyif = u->murphyif));
     rif = &murphyif->resource;
 
@@ -990,9 +1127,13 @@ static pa_bool_t resource_set_create_all(struct userdata *u)
     idx = PA_IDXSET_INVALID;
 
     while ((node = pa_nodeset_iterate_nodes(u, &idx))) {
-        if (node->implement == mir_stream && !node->rsetid) {
-            node->localrset = resource_set_create_node(u, node, NULL, FALSE);
-            success &= node->localrset;
+        if ((node->implement == mir_stream && !node->loop) ||
+            (node->implement == mir_device &&  node->loop)   )
+        {
+            if (!node->rsetid) {
+                node->localrset = resource_set_create_node(u, node, NULL, FALSE);
+                success &= node->localrset;
+            }
         }
     }
 
@@ -1057,8 +1198,10 @@ static pa_bool_t resource_set_destroy_all(struct userdata *u)
 
                 if (e == node->rsetid || *e)
                     success = FALSE;
-                else
+                else {
+                    rset_hashmap_remove(u, node->rsetid, node);
                     success &= resource_set_destroy_node(u, rsetid);
+                }
             }
 
             pa_xfree(node->rsetid);
@@ -1076,6 +1219,8 @@ static void resource_set_notification(struct userdata *u,
                                       int nrow,
                                       mrp_domctl_value_t **values)
 {
+    pa_murphyif *murphyif;
+    resource_interface *rif;
     int r;
     mrp_domctl_value_t *row;
     mrp_domctl_value_t *crsetid;
@@ -1085,17 +1230,18 @@ static void resource_set_notification(struct userdata *u,
     mrp_domctl_value_t *cpid;
     mrp_domctl_value_t *cpolicy;
     char rsetid[32];
-    pa_bool_t autorel;
-    int state;
-    pa_bool_t grant;
     const char *pid;
-    const char *policy;
-    mir_node *node;
-    int req;
+    mir_node *node, **nodes;
+    rset_hash *rh;
+    rset_data rset, *rs;
+    size_t i, size;
 
     pa_assert(u);
     pa_assert(table);
 
+    pa_assert_se((murphyif = u->murphyif));
+    rif = &murphyif->resource;
+
     for (r = 0;  r < nrow;  r++) {
         row = values[r];
         crsetid  =  row + RESCOL_RSETID;
@@ -1119,53 +1265,87 @@ static void resource_set_notification(struct userdata *u,
         }
 
         snprintf(rsetid, sizeof(rsetid), "%d", crsetid->s32);
+        pid = cpid->str;
 
-        if (!(node = pa_murphyif_find_node(u, rsetid))) {
-            pa_log_debug("can't find node for resource set %s", rsetid);
-            continue;
-        }
-
-        pa_assert(node->implement == mir_stream);
+        rset.id      = rsetid;
+        rset.autorel = cautorel->s32;
+        rset.state   = cstate->s32;
+        rset.grant   = cgrant->s32;
+        rset.policy  = cpolicy->str;
 
-        autorel = cautorel->s32;
-        state   = cstate->s32;
-        grant   = cgrant->s32;
-        pid     = cpid->str;
-        policy  = cpolicy->str;
 
-        if (autorel != 0 && autorel != 1) {
-            pa_log_debug("invalid autorel %d in table '%s'", autorel, table);
+        if (rset.autorel != 0 && rset.autorel != 1) {
+            pa_log_debug("invalid autorel %d in table '%s'",
+                         rset.autorel, table);
             continue;
         }
-        if (state != RSET_RELEASE && state != RSET_ACQUIRE) {
-            pa_log_debug("invalid state %d in table '%s'", state, table);
+        if (rset.state != RSET_RELEASE && rset.state != RSET_ACQUIRE) {
+            pa_log_debug("invalid state %d in table '%s'", rset.state, table);
             continue;
         }
-        if (grant != 0 && grant != 1) {
-            pa_log_debug("invalid grant %d in table '%s'", grant, table);
+        if (rset.grant != 0 && rset.grant != 1) {
+            pa_log_debug("invalid grant %d in table '%s'", rset.grant, table);
             continue;
         }
 
-        pa_log_debug("resource notification for node '%s' autorel:%s state:%s "
-                     "grant:%s pid:%s policy:%s", node->amname,
-                     autorel ? "yes":"no",
-                     state == RSET_ACQUIRE ? "acquire":"release",
-                     grant ? "yes":"no", pid, policy);
+        if (!(rh = rset_hashmap_get(u, rset.id))) {
+            if (!pid) {
+                pa_log_debug("can't find node for resource set %s "
+                             "(pid in resource set unknown)", rset.id);
+                continue;
+            }
 
-        if (pa_streq(policy, "relaxed"))
-            req = PA_STREAM_RUN;
-        else {
-            if (state == RSET_RELEASE)
-                req = PA_STREAM_KILL;
+            if ((node = pid_hashmap_remove_node(u, pid))) {
+                pa_log_debug("found node %s for resource-set '%s'",
+                             node->amname, rset.id);
+
+                if (!(rh = node_put_rset(u, node, &rset))) {
+                    pa_log("can't register resource set for node '%s': "
+                           "failed to set rsetid", node->amname);
+                    continue;
+                }
+            }
             else {
-                if (grant)
-                    req = PA_STREAM_RUN;
-                else
-                    req = /* node->localrset ? PA_STREAM_KILL : */ PA_STREAM_BLOCK;
+                if (pid_hashmap_put(u, pid, NULL, rset_data_dup(&rset)) < 0) {
+                    if (!(rs = pid_hashmap_get_rset(u, pid)))
+                        pa_log("failed to add resource set to pid hash");
+                    else {
+                        if (!pa_streq(rs->id, rset.id)) {
+                            pa_log("process %s appears to have multiple resour"
+                                   "ce sets (%s and %s)", pid, rs->id,rset.id);
+                        }
+                        pa_log_debug("update resource-set %s data in "
+                                     "pid hash (pid %s)", rs->id, pid);
+                        rset_data_copy(rs, &rset);
+                    }
+                }
+                else {
+                    pa_log_debug("can't find node for resource set %s. "
+                                 "Beleive the stream will appear later on",
+                                 rset.id);
+                }
+
+                continue;
             }
         }
 
-        pa_stream_state_change(u, node, req);
+        rset_data_update(rh->rset, &rset);
+
+        /* we need to make a copy of this as node_enforce_resource_policy()
+           will delete/modify it */
+        size = sizeof(mir_node *) * (rh->nnode + 1);
+        nodes = alloca(size);
+        memcpy(nodes, rh->nodes, size);
+
+        for (i = 0;  (node = nodes[i]);  i++) {
+            pa_log_debug("%u: resource notification for node '%s' autorel:%s "
+                         "state:%s grant:%s pid:%s policy:%s", i,
+                         node->amname, rset.autorel ? "yes":"no",
+                         rset.state == RSET_ACQUIRE ? "acquire":"release",
+                         rset.grant ? "yes":"no", pid, rset.policy);
+
+            node_enforce_resource_policy(u, node, &rset);
+        }
     }
 }
 
@@ -1629,8 +1809,431 @@ static void cancel_schedule(struct userdata *u, resource_interface *rif)
     }
 }
 
+static rset_hash *node_put_rset(struct userdata *u, mir_node *node, rset_data *rset)
+{
+    pa_murphyif *murphyif;
+    resource_interface *rif;
+    pa_proplist *pl;
+    rset_hash *rh;
+
+    pa_assert(u);
+    pa_assert(node);
+    pa_assert(rset);
+    pa_assert(rset->id);
+
+    pa_assert(node->implement == mir_stream);
+    pa_assert(node->direction == mir_input || node->direction == mir_output);
+
+    pa_assert_se((murphyif = u->murphyif));
+    rif = &murphyif->resource;
+
+    pa_log_debug("setting rsetid %s for node %s", rset->id, node->amname);
+
+    if (node->rsetid) {
+        pa_xfree(node->rsetid);
+    }
+    node->rsetid = pa_xstrdup(rset->id);
+
+    if (!(pl = get_node_proplist(u, node))) {
+        pa_log("can't obtain property list for node %s", node->amname);
+        return NULL;
+    }
+
+    if ((pa_proplist_sets(pl, PA_PROP_RESOURCE_SET_ID, node->rsetid) < 0)) {
+        pa_log("failed to set '" PA_PROP_RESOURCE_SET_ID "' property "
+               "of '%s' node", node->amname);
+        return NULL;
+    }
+
+    if (!(rh = rset_hashmap_put(u, node->rsetid, node))) {
+        pa_log("conflicting rsetid %s for %s", node->rsetid, node->amname);
+        return NULL;
+    }
+
+    return rh;
+}
+
+static void node_enforce_resource_policy(struct userdata *u,
+                                         mir_node *node,
+                                         rset_data *rset)
+{
+    int req;
+
+    pa_assert(node);
+    pa_assert(rset);
+    pa_assert(rset->policy);
+    
+
+    if (pa_streq(rset->policy, "relaxed"))
+        req = PA_STREAM_RUN;
+    else if (pa_streq(rset->policy, "strict")) {
+        if (rset->state == RSET_RELEASE)
+            req = PA_STREAM_KILL;
+        else {
+            if (rset->grant)
+                req = PA_STREAM_RUN;
+            else
+                req = PA_STREAM_BLOCK;
+        }
+    }
+    else {
+        req = PA_STREAM_BLOCK;
+    }
+
+    pa_stream_state_change(u, node, req);
+}
+
+static rset_data *rset_data_dup(rset_data *orig)
+{
+    rset_data *dup;
+
+    pa_assert(orig);
+    pa_assert(orig->id);
+    pa_assert(orig->policy);
+
+    dup = pa_xnew0(rset_data, 1);
+
+    dup->id      = pa_xstrdup(orig->id);
+    dup->autorel = orig->autorel;
+    dup->state   = orig->state;
+    dup->grant   = orig->grant;
+    dup->policy  = pa_xstrdup(orig->policy);
+
+    return dup;
+}
+
+static void rset_data_copy(rset_data *dst, rset_data *src)
+{
+    rset_data *dup;
+
+    pa_assert(dst);
+    pa_assert(src);
+    pa_assert(src->id);
+    pa_assert(src->policy);
+
+    pa_xfree((void *)dst->id);
+    pa_xfree((void *)dst->policy);
+
+    dst->id      = pa_xstrdup(src->id);
+    dst->autorel = src->autorel;
+    dst->state   = src->state;
+    dst->grant   = src->grant;
+    dst->policy  = pa_xstrdup(src->policy);
+}
+
+
+static void rset_data_update(rset_data *dst, rset_data *src)
+{
+    rset_data *dup;
+
+    pa_assert(dst);
+    pa_assert(dst->id);
+    pa_assert(src);
+    pa_assert(src->id);
+    pa_assert(src->policy);
+
+    pa_assert_se(pa_streq(src->id, dst->id));
+
+    pa_xfree((void *)dst->policy);
+
+    dst->autorel = src->autorel;
+    dst->state   = src->state;
+    dst->grant   = src->grant;
+    dst->policy  = pa_xstrdup(src->policy);
+}
+
+
+static void rset_data_free(rset_data *rset)
+{
+    if (rset) {
+        pa_xfree((void *)rset->id);
+        pa_xfree((void *)rset->policy);
+        pa_xfree(rset);
+    }
+}
+
+static void pid_hashmap_free(void *p, void *userdata)
+{
+    pid_hash *ph = (pid_hash *)p;
+
+    (void)userdata;
+
+    if (ph) {
+        pa_xfree((void *)ph->pid);
+        rset_data_free(ph->rset);
+        pa_xfree(ph);
+    }
+}
+
+static int pid_hashmap_put(struct userdata *u, const char *pid,
+                           mir_node *node, rset_data *rset)
+{
+    pa_murphyif *murphyif;
+    resource_interface *rif;
+    pid_hash *ph;
+
+    pa_assert(u);
+    pa_assert(pid);
+    pa_assert(node || rset);
+    pa_assert_se((murphyif = u->murphyif));
+    
+    rif = &murphyif->resource;
+
+    ph = pa_xnew0(pid_hash, 1);
+    ph->pid = pa_xstrdup(pid);
+    ph->node = node;
+    ph->rset = rset;
+
+    if (pa_hashmap_put(rif->nodes.pid, ph->pid, ph) == 0)
+        return 0;
+    else
+        pid_hashmap_free(ph, NULL);
+
+    return -1;
+}
+
+static mir_node *pid_hashmap_get_node(struct userdata *u, const char *pid)
+{
+    pa_murphyif *murphyif;
+    resource_interface *rif;
+    pid_hash *ph;
+
+    pa_assert(u);
+    pa_assert(pid);
+    pa_assert(murphyif = u->murphyif);
+    
+    rif = &murphyif->resource;
+
+    if ((ph = pa_hashmap_get(rif->nodes.pid, pid)))
+        return ph->node;
+
+    return NULL;
+}
+
+static rset_data *pid_hashmap_get_rset(struct userdata *u, const char *pid)
+{
+    pa_murphyif *murphyif;
+    resource_interface *rif;
+    pid_hash *ph;
+
+    pa_assert(u);
+    pa_assert(pid);
+    pa_assert(murphyif = u->murphyif);
+    
+    rif = &murphyif->resource;
+
+    if ((ph = pa_hashmap_get(rif->nodes.pid, pid)))
+        return ph->rset;
+
+    return NULL;
+}
+
+static mir_node *pid_hashmap_remove_node(struct userdata *u, const char *pid)
+{
+    pa_murphyif *murphyif;
+    resource_interface *rif;
+    mir_node *node;
+    pid_hash *ph;
+
+    pa_assert(u);
+    pa_assert_se((murphyif = u->murphyif));
+
+    rif = &murphyif->resource;
+
+    if (!(ph = pa_hashmap_remove(rif->nodes.pid, pid)))
+        node = NULL;
+    else if (!(node = ph->node))
+        pa_hashmap_put(rif->nodes.pid, ph->pid, ph);
+    else
+        pid_hashmap_free(ph, NULL);
+
+    return node;
+}
+
+static rset_data *pid_hashmap_remove_rset(struct userdata *u, const char *pid)
+{
+    pa_murphyif *murphyif;
+    resource_interface *rif;
+    rset_data *rset;
+    pid_hash *ph;
+
+    pa_assert(u);
+    pa_assert(pid);
+
+    pa_assert_se((murphyif = u->murphyif));
+
+    rif = &murphyif->resource;
+
+    if (!(ph = pa_hashmap_remove(rif->nodes.pid, pid)))
+        rset = NULL;
+    else if (!(rset = ph->rset))
+        pa_hashmap_put(rif->nodes.pid, ph->pid, ph);
+    else {
+        ph->rset = NULL;
+        pid_hashmap_free(ph, NULL);
+    }
+
+    return rset;
+}
+
+
+static void rset_hashmap_free(void *r, void *userdata)
+{
+    rset_hash *rh = (rset_hash *)r;
+
+    (void)userdata;
+
+    if (rh) {
+        pa_xfree(rh->nodes);
+        rset_data_free(rh->rset);
+        pa_xfree(rh);
+    }
+}
+
+static rset_hash *rset_hashmap_put(struct userdata *u,
+                                   const char *rsetid,
+                                   mir_node *node)
+{
+    pa_murphyif *murphyif;
+    resource_interface *rif;
+    rset_hash *rh;
+    rset_data *rset;
+    size_t i;
+
+    pa_assert(u);
+    pa_assert(rsetid);
+    pa_assert(node);
+    pa_assert_se((murphyif = u->murphyif));
+    
+    rif = &murphyif->resource;
+
+    if ((rh = pa_hashmap_get(rif->nodes.rsetid, rsetid))) {
+        for (i = 0;  i < rh->nnode;  i++) {
+            if (rh->nodes[i] == node)
+                return NULL;
+        }
+
+        i = rh->nnode++;
+        rh->nodes = pa_xrealloc(rh->nodes, sizeof(mir_node *) * (rh->nnode+1));
+    }
+    else {
+        rset = pa_xnew0(rset_data, 1);
+
+        rset->id = pa_xstrdup(rsetid);
+        rset->policy = pa_xstrdup("unknown");
+
+        rh = pa_xnew0(rset_hash, 1);
+
+        rh->nnode = 1;
+        rh->nodes = pa_xnew0(mir_node *, 2);
+        rh->rset  = rset;
+
+        pa_hashmap_put(rif->nodes.rsetid, rh->rset->id, rh);
+
+        i = 0;
+    }
+
+
+    rh->nodes[i+0] = node;
+    rh->nodes[i+1] = NULL;
+
+    return rh;
+}
+
+static rset_hash *rset_hashmap_get(struct userdata *u, const char *rsetid)
+{
+    pa_murphyif *murphyif;
+    resource_interface *rif;
+    rset_hash *rh;
+
+    pa_assert(u);
+    pa_assert(rsetid);
+    pa_assert(murphyif = u->murphyif);
+    
+    rif = &murphyif->resource;
+
+    if ((rh = pa_hashmap_get(rif->nodes.rsetid, rsetid)))
+        return rh;
+
+    return NULL;
+}
+
+static int rset_hashmap_remove(struct userdata *u,
+                               const char *rsetid,
+                               mir_node *node)
+{
+    pa_murphyif *murphyif;
+    resource_interface *rif;
+    rset_hash *rh;
+    size_t i,j;
+
+    pa_assert(u);
+    pa_assert_se((murphyif = u->murphyif));
+
+    rif = &murphyif->resource;
+
+    if ((rh = pa_hashmap_get(rif->nodes.rsetid, rsetid))) {
+
+        for (i = 0;  i < rh->nnode;  i++) {
+            if (node == rh->nodes[i]) {
+                if (rh->nnode <= 1) {
+                    pa_hashmap_remove(rif->nodes.rsetid, rsetid);
+                    rset_hashmap_free(rh, NULL);
+                    return 0;
+                }
+                else {
+                    for (j = i;  j < rh->nnode;  j++)
+                        rh->nodes[j] = rh->nodes[j+1];
+
+                    rh->nnode--;
+
+                    return 0;
+                }
+            }
+        }
+    }
+
+    return -1;
+}
+
 #endif
 
+static pa_proplist *get_node_proplist(struct userdata *u, mir_node *node)
+{
+    pa_core *core;
+    pa_sink_input *i;
+    pa_source_output *o;
+
+    pa_assert(u);
+    pa_assert(node);
+    pa_assert_se((core = u->core));
+    
+    if (node->implement == mir_stream && node->paidx != PA_IDXSET_INVALID) {
+        if (node->direction == mir_input) {
+            if ((i = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
+                return i->proplist;
+        }
+        else if (node->direction == mir_output) {
+            if ((o = pa_idxset_get_by_index(core->source_outputs,node->paidx)))
+                return o->proplist;
+        }
+    }
+
+    return NULL;
+}
+
+static const char *get_node_pid(struct userdata *u, mir_node *node)
+{
+    pa_proplist *pl;
+
+    pa_assert(u);
+    if (node && (pl = get_node_proplist(u, node)))
+        return pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_ID);
+
+    return NULL;
+}
+
 /*
  * Local Variables:
  * c-basic-offset: 4