Refactoring murphyif for multiresource mappings 47/31947/3
authorJanos Kovacs <jankovac503@gmail.com>
Tue, 2 Dec 2014 09:43:50 +0000 (11:43 +0200)
committerJaska Uimonen <jaska.uimonen@helsinki.fi>
Mon, 15 Dec 2014 10:56:07 +0000 (12:56 +0200)
This will allow to create multiple resource sets to
1 stream mappings and also multiple streams to 1 resource
set mappings. Of cource the classic 1to1 mapping is still
possible. This feature is needed because xwalk creates
multiple resource sets but mixes audio into 1 stream
inside xwalk.

Change-Id: I4c85f96c91c381d41306f7f9fcec1807e7d44dc0

murphy/Makefile.am
murphy/discover.c
murphy/module-murphy-ivi.c
murphy/murphyif.c
murphy/node.c
murphy/node.h
murphy/resource.c [new file with mode: 0644]
murphy/resource.h [new file with mode: 0644]
murphy/userdata.h

index 12da03f..fc947c1 100644 (file)
@@ -36,6 +36,7 @@ module_murphy_ivi_la_SOURCES = \
                        utils.c \
                        scripting.c \
                        extapi.c \
+                       resource.c \
                        murphyif.c
 
 configdir = $(sysconfdir)/pulse
index fd779a9..43d895f 100644 (file)
@@ -498,6 +498,8 @@ void pa_discover_add_sink(struct userdata *u, pa_sink *sink, bool route)
 
             if (make_rset)
                 pa_murphyif_create_resource_set(u, node, resdef);
+            else
+                node->rset.grant = 1;
         }
 
         if (route) {
@@ -667,6 +669,8 @@ void pa_discover_add_source(struct userdata *u, pa_source *source)
 
             if (make_rset)
                 pa_murphyif_create_resource_set(u, node, resdef);
+            else
+                node->rset.grant = 1;
 
             pa_fader_apply_volume_limits(u, node->stamp);
         }
@@ -816,7 +820,7 @@ void pa_discover_register_sink_input(struct userdata *u, pa_sink_input *sinp)
     data.amid      = AM_ID_INVALID;
     data.paname    = (char *)name;
     data.paidx     = sinp->index;
-    data.rsetid    = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
+    data.rset.id   = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
 
     /*
      * here we can't guess whether the application requested an explicit
@@ -1080,7 +1084,7 @@ void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
         data.paidx     = sinp->index;
         data.mux       = pa_multiplex_find_by_sink(u->multiplex,
                                                    sinp->sink->index);
-        data.rsetid    = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
+        data.rset.id   = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
         node = create_node(u, &data, &created);
 
         pa_assert(node);
@@ -1091,10 +1095,14 @@ void pa_discover_add_sink_input(struct userdata *u, pa_sink_input *sinp)
             return;
         }
 
-        if (node->rsetid)
+        if (node->rset.id)
             pa_murphyif_add_node(u, node);
-        else if (resdef)
-            pa_murphyif_create_resource_set(u, node, resdef);
+        else {
+            if (resdef)
+                pa_murphyif_create_resource_set(u, node, resdef);
+            else
+                node->rset.grant = 1;
+        }
 
         pa_discover_add_node_to_ptr_hash(u, sinp, node);
 
@@ -1237,7 +1245,7 @@ void pa_discover_register_source_output(struct userdata  *u,
     data.amid      = AM_ID_INVALID;
     data.paname    = name;
     data.paidx     = sout->index;
-    data.rsetid    = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
+    data.rset.id   = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
 
     /*
      * here we can't guess whether the application requested an explicit
@@ -1427,7 +1435,7 @@ void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
         data.amid      = AM_ID_INVALID;
         data.paname    = name;
         data.paidx     = sout->index;
-        data.rsetid    = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
+        data.rset.id   = pa_utils_get_rsetid(pl, idbuf, sizeof(idbuf));
 
         node = create_node(u, &data, &created);
 
@@ -1439,10 +1447,14 @@ void pa_discover_add_source_output(struct userdata *u, pa_source_output *sout)
             return;
         }
 
-        if (node->rsetid)
+        if (node->rset.id)
             pa_murphyif_add_node(u, node);
-        else if (resdef)
-            pa_murphyif_create_resource_set(u, node, resdef);
+        else {
+            if (resdef)
+                pa_murphyif_create_resource_set(u, node, resdef);
+            else
+                node->rset.grant = 1;
+        }
 
         pa_discover_add_node_to_ptr_hash(u, sout, node);
     }
index b0be782..111ba16 100644 (file)
@@ -63,6 +63,7 @@
 #include "scripting.h"
 #include "extapi.h"
 #include "murphyif.h"
+#include "resource.h"
 #include "classify.h"
 
 #ifndef DEFAULT_CONFIG_DIR
@@ -166,9 +167,9 @@ int pa__init(pa_module *m) {
     char             buf[4096];
     bool             enable_multiplex = true;
 
-    
+
     pa_assert(m);
-    
+
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
         pa_log("Failed to parse module arguments.");
         goto fail;
@@ -223,6 +224,7 @@ int pa__init(pa_module *m) {
     u->config    = pa_mir_config_init(u);
     u->extapi    = pa_extapi_init(u);
     u->murphyif  = pa_murphyif_init(u, ctladdr, resaddr);
+    u->resource  = pa_resource_init(u);
 
     u->state.sink   = PA_IDXSET_INVALID;
     u->state.source = PA_IDXSET_INVALID;
@@ -248,18 +250,18 @@ int pa__init(pa_module *m) {
 
     mir_router_print_rtgroups(u, buf, sizeof(buf));
     pa_log_debug("%s", buf);
-    
+
     pa_modargs_free(ma);
-    
+
     return 0;
-    
+
  fail:
-    
+
     if (ma)
         pa_modargs_free(ma);
-    
+
     pa__done(m);
-    
+
     return -1;
 }
 
@@ -267,8 +269,9 @@ void pa__done(pa_module *m) {
     struct userdata *u;
 
     pa_assert(m);
-    
+
     if ((u = m->userdata)) {
+        pa_resource_done(u);
         pa_murphyif_done(u);
         pa_tracker_done(u);
         pa_discover_done(u);
index 95c4030..a828249 100644 (file)
@@ -53,6 +53,7 @@
 #endif
 
 #include "murphyif.h"
+#include "resource.h"
 #include "node.h"
 #include "stream-state.h"
 #include "utils.h"
 #define RESCOL_POLICY    5
 #define RESCOL_RSETNAME  6
 
-#define RSET_RELEASE     1
-#define RSET_ACQUIRE     2
 
-#define RSET_INPUT       0
-#define RSET_OUTPUT      1
 
 #define PUSH_VALUE(msg, tag, typ, val) \
     mrp_msg_append(msg, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
@@ -142,11 +139,6 @@ typedef struct {
         uint32_t request;
         uint32_t reply;
     }                seqno;
-    struct {
-        pa_hashmap *rsetid;
-        pa_hashmap *pid;
-        unsigned    nres[2];
-    }                nodes;
     PA_LLIST_HEAD(resource_attribute, attrs);
     PA_LLIST_HEAD(resource_request, reqs);
 #endif
@@ -161,35 +153,6 @@ struct pa_murphyif {
     resource_interface resource;
 };
 
-#ifdef WITH_RESOURCES
-typedef struct {
-    const char *hashkey;
-    bool        dead;
-    const char *id;
-    bool        autorel;
-    int         state;
-    bool        grant;
-    const char *policy;
-    const char *name;
-    const char *pid;
-} 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;
-    bool        type[2];
-    uint32_t    updid;
-} rset_hash;
-
-#endif
-
 
 #ifdef WITH_DOMCTL
 static void domctl_connect_notify(mrp_domctl_t *,int,int,const char *,void *);
@@ -202,7 +165,6 @@ static void       resource_attribute_destroy(resource_interface *,
                                              resource_attribute *);
 static int        resource_transport_connect(resource_interface *);
 static void       resource_xport_closed_evt(mrp_transport_t *, int, void *);
-
 static mrp_msg_t *resource_create_request(uint32_t, mrp_resproto_request_t);
 static bool       resource_send_message(resource_interface *, mrp_msg_t *,
                                         uint32_t, uint16_t, uint32_t);
@@ -213,7 +175,6 @@ static bool       resource_set_destroy_node(struct userdata *, uint32_t);
 static bool       resource_set_destroy_all(struct userdata *);
 static void       resource_set_notification(struct userdata *, const char *,
                                             int, mrp_domctl_value_t **);
-static void       resource_set_enforce_policy(struct userdata *, rset_hash *);
 
 static bool  resource_push_attributes(mrp_msg_t *, resource_interface *,
                                            pa_proplist *);
@@ -247,34 +208,8 @@ static void connect_attempt(pa_mainloop_api *, pa_time_event *,
 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 *,
-                                   int, 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,
                               const char *res_addr)
@@ -290,8 +225,6 @@ pa_murphyif *pa_murphyif_init(struct userdata *u,
         return NULL;
     }
 #endif
-#ifdef WITH_RESOURCES
-#endif
 
     murphyif = pa_xnew0(pa_murphyif, 1);
     dif = &murphyif->domctl;
@@ -302,8 +235,6 @@ pa_murphyif *pa_murphyif_init(struct userdata *u,
 #endif
 
     dif->addr = pa_xstrdup(ctl_addr ? ctl_addr:MRP_DEFAULT_DOMCTL_ADDRESS);
-#ifdef WITH_DOMCTL
-#endif
 
     rif->addr = pa_xstrdup(res_addr ? res_addr:RESPROTO_DEFAULT_ADDRESS);
 #ifdef WITH_RESOURCES
@@ -328,10 +259,6 @@ pa_murphyif *pa_murphyif_init(struct userdata *u,
     }
 
     rif->seqno.request = 1;
-    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
@@ -348,9 +275,6 @@ 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)) {
@@ -391,25 +315,6 @@ void pa_murphyif_done(struct userdata *u)
 
         resource_transport_destroy(murphyif);
 
-        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);
-        pa_hashmap_free(rif->nodes.pid);
-
         PA_LLIST_FOREACH_SAFE(attr, a, rif->attrs)
             resource_attribute_destroy(rif, attr);
 
@@ -631,7 +536,7 @@ void pa_murphyif_create_resource_set(struct userdata *u,
               ( 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(!node->rset.id);
 
     pa_assert_se((core = u->core));
 
@@ -665,22 +570,17 @@ void pa_murphyif_destroy_resource_set(struct userdata *u, mir_node *node)
     pa_assert(node);
     pa_assert_se((murphyif = u->murphyif));
 
-    if (node->localrset && node->rsetid) {
+    if (node->localrset && node->rset.id) {
 
         pa_murphyif_delete_node(u, node);
 
-        rsetid = strtoul(node->rsetid, &e, 10);
+        rsetid = strtoul(node->rset.id, &e, 10);
 
-        if (e == node->rsetid || *e) {
+        if (e == node->rset.id || *e) {
             pa_log("can't destroy resource set: invalid rsetid '%s'",
-                   node->rsetid);
+                   node->rset.id);
         }
         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("sent resource set %u destruction request", rsetid);
             else {
@@ -688,10 +588,10 @@ void pa_murphyif_destroy_resource_set(struct userdata *u, mir_node *node)
                        rsetid, node->amname);
             }
 
-            pa_xfree(node->rsetid);
+            pa_xfree(node->rset.id);
 
             node->localrset = false;
-            node->rsetid = NULL;
+            node->rset.id = NULL;
         }
     }
 }
@@ -700,63 +600,36 @@ int pa_murphyif_add_node(struct userdata *u, mir_node *node)
 {
 #ifdef WITH_RESOURCES
     pa_murphyif *murphyif;
-    const char *pid;
-    rset_data *rset;
-    rset_hash *rh;
+    char *id;
+    char *name;
     int type;
-    char buf[64];
 
     pa_assert(u);
     pa_assert(node);
 
     pa_assert_se((murphyif = u->murphyif));
 
-    if (!node->rsetid) {
+    if (!node->rset.id) {
         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_streq(node->rset.id, PA_RESOURCE_SET_ID_PID)) {
     }
     else {
-        type = (node->direction == mir_input) ? RSET_INPUT : RSET_OUTPUT;
-
-        if ((rh = rset_hashmap_put(u, node->rsetid, type, node))) {
-            rset = rh->rset;
+        if (node->rset.id[0] == '#') {
+            name = node->rset.id;
+            id = NULL;
+        }
+        else {
+            name = NULL;
+            id = node->rset.id;
+        }
 
-            pa_log_debug("enforce policies on node %u '%s' hashkey:%s "
-                         "autorel:%s state:%s grant:%s policy:%s",
-                         node->paidx, node->amname,
-                         rset->hashkey, rset->autorel ? "yes":"no",
-                         rset->state == RSET_ACQUIRE ? "acquire":"release",
-                         rset->grant ? "yes":"no", rset->policy);
 
-            node_enforce_resource_policy(u, node, rset);
+        if (pa_resource_stream_update(u, name, id, node) == 0) {
+            type = (node->direction == mir_input) ?
+                       PA_RESOURCE_PLAYBACK : PA_RESOURCE_RECORDING;
+            pa_resource_enforce_policies(u, type);
             return 0;
         }
     }
@@ -771,29 +644,17 @@ void pa_murphyif_delete_node(struct userdata *u, mir_node *node)
 {
 #ifdef WITH_RESOURCES
     pa_murphyif *murphyif;
-    const char *pid;
 
     pa_assert(u);
     pa_assert(node);
 
     pa_assert_se((murphyif = u->murphyif));
 
-    if (node->rsetid) {
-        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);
-                }
-            }
+    if (node->rset.id) {
+        if (pa_streq(node->rset.id, PA_RESOURCE_SET_ID_PID)) {
         }
         else {
-            if (rset_hashmap_remove(u, node->rsetid, node) < 0) {
-                pa_log("failed to remove node '%s' from rset hash", node->amname);
-            }
+            pa_resource_stream_remove(u, node);
         }
     }
 #endif
@@ -1020,9 +881,9 @@ static bool resource_send_message(resource_interface *rif,
 }
 
 static bool resource_set_create_node(struct userdata *u,
-                                          mir_node *node,
-                                          pa_nodeset_resdef *resdef,
-                                          bool acquire)
+                                     mir_node *node,
+                                     pa_nodeset_resdef *resdef,
+                                     bool acquire)
 {
     pa_core *core;
     pa_murphyif *murphyif;
@@ -1052,7 +913,7 @@ static bool resource_set_create_node(struct userdata *u,
               ( 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(!node->rset.id);
 
     pa_assert_se((core = u->core));
 
@@ -1149,7 +1010,7 @@ static bool resource_set_create_all(struct userdata *u)
         if ((node->implement == mir_stream && !node->loop) ||
             (node->implement == mir_device &&  node->loop)   )
         {
-            if (!node->rsetid) {
+            if (!node->rset.id) {
                 node->localrset = resource_set_create_node(u, node, NULL, false);
                 success &= node->localrset;
             }
@@ -1212,21 +1073,21 @@ static bool resource_set_destroy_all(struct userdata *u)
         if (node->implement == mir_stream && node->localrset) {
             pa_log_debug("destroying resource set for '%s'", node->amname);
 
-            if (rif->connected && node->rsetid) {
-                rsetid = strtoul(node->rsetid, &e, 10);
+            if (rif->connected && node->rset.id) {
+                rsetid = strtoul(node->rset.id, &e, 10);
 
-                if (e == node->rsetid || *e)
+                if (e == node->rset.id || *e)
                     success = false;
                 else {
-                    rset_hashmap_remove(u, node->rsetid, node);
+                    pa_resource_rset_remove(u, NULL, node->rset.id);
                     success &= resource_set_destroy_node(u, rsetid);
                 }
             }
 
-            pa_xfree(node->rsetid);
+            pa_xfree(node->rset.id);
 
             node->localrset = false;
-            node->rsetid = NULL;
+            node->rset.id = NULL;
         }
     }
 
@@ -1254,13 +1115,8 @@ static void resource_set_notification(struct userdata *u,
     mrp_domctl_value_t *crsetname;
     char rsetid[32];
     char name[256];
-    const char *pid;
-    mir_node *node, **nodes;
-    rset_hash *rh;
-    rset_data rset, *rs;
-    size_t i, size;
+    pa_resource_rset_data rset;
     unsigned nrset;
-    void *it;
 
     pa_assert(u);
     pa_assert(table);
@@ -1269,9 +1125,9 @@ static void resource_set_notification(struct userdata *u,
     rif = &murphyif->resource;
 
     if (pa_streq(table, rif->inpres.tblnam))
-        type = RSET_INPUT;
+        type = PA_RESOURCE_PLAYBACK;
     else if (pa_streq(table, rif->outres.tblnam))
-        type = RSET_OUTPUT;
+        type = PA_RESOURCE_RECORDING;
     else {
         pa_log_debug("ignoring unregistered table '%s'", table);
         return;
@@ -1309,23 +1165,22 @@ static void resource_set_notification(struct userdata *u,
         else
             name[0] = 0;
 
-        pid = cpid->str;
-
         memset(&rset, 0, sizeof(rset));
         rset.id      = rsetid;
         rset.autorel = cautorel->s32;
         rset.state   = cstate->s32;
-        rset.grant   = cgrant->s32;
-        rset.policy  = cpolicy->str;
         rset.name    = name;
-        rset.pid     = pid;
+        rset.pid     = (char *)cpid->str;
+
+        rset.policy[type] = (char *)cpolicy->str;
+        rset.grant[type]  = cgrant->s32;
 
         if (cautorel->s32 < 0 || cautorel->s32 > 1) {
             pa_log_debug("invalid autorel %d in table '%s'",
                          cautorel->s32, table);
             continue;
         }
-        if (rset.state != RSET_RELEASE && rset.state != RSET_ACQUIRE) {
+        if (rset.state != PA_RESOURCE_RELEASE && rset.state != PA_RESOURCE_ACQUIRE) {
             pa_log_debug("invalid state %d in table '%s'", rset.state, table);
             continue;
         }
@@ -1333,121 +1188,29 @@ static void resource_set_notification(struct userdata *u,
             pa_log_debug("invalid grant %d in table '%s'", cgrant->s32, table);
             continue;
         }
-        if (!rset.policy) {
+        if (!rset.policy[type]) {
             pa_log_debug("invalid 'policy' string in table '%s'", table);
             continue;
         }
 
-        if (rset.name[0] == '#') {
-            rset.hashkey = rset.name;
-
-            if (!(rh = rset_hashmap_put(u, rset.hashkey, type, NULL))) {
-                pa_log_debug("can't add to hashmap '%s' resource set",
-                             rset.hashkey);
-                continue;
-            }
-        }
-        else {
-            rset.hashkey = rset.id;
-
-            if (!(rh = rset_hashmap_get(u, rset.hashkey))) {
-                if (!pid) {
-                    pa_log_debug("can't find node for resource set %s "
-                                 "(pid in resource set unknown)", rset.id);
-                    continue;
-                }
-
-                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 (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 "
-                                       "resource 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;
-                }
-            }
-        }
-
-        rset_data_update(rh->rset, &rset);
-
-        rh->updid = updid;
-        nrset++;
-
-        resource_set_enforce_policy(u, rh);
+        pa_resource_rset_update(u, rset.name, rset.id, type, &rset, updid);
 
     } /* for each row */
 
-    if (nrset < rif->nodes.nres[type]) {
-        pa_log_debug("some of the resource sets were not updated => "
-                     "find the %u resource sets that need to be deleted",
-                     pa_hashmap_size(rif->nodes.rsetid) - nrset);
+    pa_log("*** nrset=%u pa_resource_get_number_of_resources()=%u",
+           nrset, pa_resource_get_number_of_resources(u, type));
 
-        PA_HASHMAP_FOREACH(rh, rif->nodes.rsetid, it) {
-            if (rh->type[type] && rh->updid != updid) {
-                pa_log_debug("'%s' was not updated => assume it's gone "
-                             "and delete it", rh->rset->hashkey);
+    // if (nrset != pa_resource_get_number_of_resources(u, type))
+        pa_resource_purge(u, updid, type);
 
-                rh->rset->dead = true;
-                rh->rset->grant = false;
-
-                resource_set_enforce_policy(u, rh);
-            }
-        }
-    }
+    pa_resource_enforce_policies(u, type);
 }
 
-static void resource_set_enforce_policy(struct userdata *u, rset_hash *rh) {
-    size_t i, size;
-    mir_node *node, **nodes;
-
-    /* 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' hashkey=%s "
-                     "autorel:%s state:%s grant:%s pid:%s policy:%s",
-                     i, node->amname, rh->rset->hashkey,
-                     rh->rset->autorel ? "yes":"no",
-                     rh->rset->state == RSET_ACQUIRE ? "acquire":"release",
-                     rh->rset->grant ? "yes":"no",
-                     rh->rset->pid, rh->rset->policy);
-
-        node_enforce_resource_policy(u, node, rh->rset);
-    }
-}
 
 
 static bool resource_push_attributes(mrp_msg_t *msg,
-                                          resource_interface *rif,
-                                          pa_proplist *proplist)
+                                     resource_interface *rif,
+                                     pa_proplist *proplist)
 {
     resource_attribute *attr;
     union {
@@ -1624,7 +1387,7 @@ static void resource_set_create_response(struct userdata *u, mir_node *node,
         return;
     }
 
-    node->rsetid = pa_sprintf_malloc("%d", rsetid);
+    node->rset.id = pa_sprintf_malloc("%d", rsetid);
 
     if (pa_murphyif_add_node(u, node) == 0) {
         pa_log_debug("resource set was successfully created");
@@ -1905,464 +1668,8 @@ 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;
-    pa_proplist *pl;
-    rset_hash *rh;
-    resource_interface *rif;
-    int type;
-
-    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;
-    type = (node->direction == mir_input) ? RSET_INPUT : RSET_OUTPUT;
-
-    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, type, 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 && rset->autorel)
-            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)
-{
-    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)
-{
-    pa_assert(dst);
-    pa_assert(dst->id);
-    pa_assert(src);
-    pa_assert(src->id);
-    pa_assert(src->policy);
-
-    pa_assert(pa_streq(src->hashkey, dst->hashkey));
-
-    pa_xfree((void *)dst->id);
-    pa_xfree((void *)dst->policy);
-    pa_xfree((void *)dst->name);
-    pa_xfree((void *)dst->pid);
-
-    dst->id      = pa_xstrdup(src->id);
-    dst->autorel = src->autorel;
-    dst->state   = src->state;
-    dst->grant   = src->grant;
-    dst->policy  = pa_xstrdup(src->policy);
-    dst->name    = pa_xstrdup(src->name);
-    dst->pid     = pa_xstrdup(src->pid);
-}
-
-
-static void rset_data_free(rset_data *rset)
-{
-    if (rset) {
-        pa_xfree((void *)rset->hashkey);
-        pa_xfree((void *)rset->id);
-        pa_xfree((void *)rset->policy);
-        pa_xfree((void *)rset->name);
-        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, (void *)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, (void *)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, (void *)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,
-                                   int type,
-                                   mir_node *node)
-{
-    pa_murphyif *murphyif;
-    resource_interface *rif;
-    rset_hash *rh;
-    rset_data *rset;
-    size_t i, size;
-
-    pa_assert(u);
-    pa_assert(rsetid);
-    pa_assert(type == RSET_INPUT || type == RSET_OUTPUT);
-    pa_assert_se((murphyif = u->murphyif));
-
-    rif = &murphyif->resource;
-
-    if ((rh = pa_hashmap_get(rif->nodes.rsetid, rsetid))) {
-        if (rh->rset->dead) {
-            pa_log_debug("attempt to add dead rset '%s' to hashmap",
-                         rh->rset->hashkey);
-            return NULL;
-        }
-
-        if (node) {
-            for (i = 0;  i < rh->nnode;  i++) {
-                if (rh->nodes[i] == node)
-                    return NULL;
-            }
-
-            i = rh->nnode++;
-            size = sizeof(mir_node *) * (rh->nnode + 1);
-            rh->nodes = pa_xrealloc(rh->nodes, size);
-        }
-    }
-    else {
-        rset = pa_xnew0(rset_data, 1);
-
-        rset->hashkey = pa_xstrdup(rsetid);
-        rset->dead    = false;
-        rset->id      = pa_xstrdup("unknown");
-        rset->policy  = pa_xstrdup("unknown");
-        rset->name    = pa_xstrdup("unknown");
-
-        rh = pa_xnew0(rset_hash, 1);
-
-        rh->nnode = node ? 1 : 0;
-        rh->nodes = pa_xnew0(mir_node *, rh->nnode + 1);
-        rh->rset  = rset;
-
-        pa_hashmap_put(rif->nodes.rsetid, (void *)rh->rset->hashkey, rh);
-
-        rif->nodes.nres[type]++;
-
-        i = 0;
-    }
-
-    if (node) {
-        rh->nodes[i+0] = node;
-        rh->nodes[i+1] = NULL;
-    }
-
-    rh->type[type] = true;
-
-    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;
-    rset_data *rset;
-    int type;
-    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))) {
-        pa_assert_se((rset = rh->rset));
-
-        for (i = 0;  i < rh->nnode;  i++) {
-            if (node == rh->nodes[i]) {
-                if (rh->nnode <= 1 && (rset->hashkey[0] != '#' || rset->dead)){
-                    pa_hashmap_remove(rif->nodes.rsetid, rsetid);
-                    rset_hashmap_free(rh, NULL);
-
-                    type = (node->direction == mir_input) ?
-                               RSET_INPUT : RSET_OUTPUT;
-
-                    if (rif->nodes.nres[type] > 0)
-                        rif->nodes.nres[type]--;
-                }
-                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
index 27b4819..2a6eb9f 100644 (file)
@@ -314,26 +314,27 @@ mir_node *mir_node_create(struct userdata *u, mir_node *data)
 
     pa_idxset_put(ns->nodes, node, &node->index);
 
-    node->key       = pa_xstrdup(data->key);
-    node->direction = data->direction;
-    node->implement = data->implement;
-    node->channels  = data->channels;
-    node->location  = data->location;
-    node->privacy   = data->privacy;
-    node->type      = data->type;
-    node->zone      = pa_xstrdup(data->zone);
-    node->visible   = data->visible;
-    node->available = data->available;
-    node->amname    = data->amname ? data->amname : data->paname;
-    node->amdescr   = data->amdescr ? data->amdescr : "";
-    node->amid      = data->amid;
-    node->paname    = data->paname;
-    node->paidx     = data->paidx;
-    node->mux       = data->mux;
-    node->loop      = data->loop;
-    node->stamp     = data->stamp;
-    node->rsetid    = data->rsetid ? pa_xstrdup(data->rsetid) : NULL;
-    node->scripting = pa_scripting_node_create(u, node);
+    node->key        = pa_xstrdup(data->key);
+    node->direction  = data->direction;
+    node->implement  = data->implement;
+    node->channels   = data->channels;
+    node->location   = data->location;
+    node->privacy    = data->privacy;
+    node->type       = data->type;
+    node->zone       = pa_xstrdup(data->zone);
+    node->visible    = data->visible;
+    node->available  = data->available;
+    node->amname     = pa_xstrdup(data->amname ? data->amname : data->paname);
+    node->amdescr    = pa_xstrdup(data->amdescr ? data->amdescr : "");
+    node->amid       = data->amid;
+    node->paname     = pa_xstrdup(data->paname);
+    node->paidx      = data->paidx;
+    node->mux        = data->mux;
+    node->loop       = data->loop;
+    node->stamp      = data->stamp;
+    node->rset.id    = data->rset.id ? pa_xstrdup(data->rset.id) : NULL;
+    node->rset.grant = data->rset.grant;
+    node->scripting  = pa_scripting_node_create(u, node);
     MIR_DLIST_INIT(node->rtentries);
     MIR_DLIST_INIT(node->rtprilist);
     MIR_DLIST_INIT(node->constrains);
@@ -438,7 +439,8 @@ int mir_node_print(mir_node *node, char *buf, int len)
     PRINT("   mux           : %s"  ,  mux);
     PRINT("   loop          : %s"  ,  loop);
     PRINT("   constrain     : %s"  ,  constr);
-    PRINT("   rsetid        : '%s'",  node->rsetid ? node->rsetid : "");
+    PRINT("   rset.id       : '%s'",  node->rset.id ? node->rset.id : "");
+    PRINT("   rset.grant    : %u"  ,  node->rset.grant);
     PRINT("   stamp         : %u"  ,  node->stamp);
 
 #undef PRINT
index a42b0e3..2b2402a 100644 (file)
@@ -50,6 +50,10 @@ struct pa_node_card {
     char     *profile;
 };
 
+struct pa_node_rset {
+    char     *id;               /**< resource set id, if any */
+    uint32_t  grant;            /**< permission to play/render etc */
+};
 
 /**
  * @brief routing endpoint
@@ -90,7 +94,7 @@ struct mir_node {
                                                                    pa_router)*/
     mir_dlist      constrains;/**< listhead of constrains */
     mir_vlim       vlim;      /**< volume limit */
-    char          *rsetid;    /**< resource set id, if any */
+    pa_node_rset   rset;      /**< resource set info if applies */
     uint32_t       stamp;
     scripting_node *scripting;/** scripting data, if any */
 };
diff --git a/murphy/resource.c b/murphy/resource.c
new file mode 100644 (file)
index 0000000..c0dd18a
--- /dev/null
@@ -0,0 +1,970 @@
+/*
+ * module-murphy-ivi -- PulseAudio module for providing audio routing support
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
+ * MA 02110-1301 USA.
+ *
+ */
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <pulse/utf8.h>
+#include <pulsecore/pulsecore-config.h>
+#include <pulsecore/llist.h>
+#include <pulsecore/idxset.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/core-util.h>
+
+#include "resource.h"
+#include "node.h"
+#include "stream-state.h"
+
+
+struct pa_resource {
+    struct {
+        pa_hashmap *id;
+        pa_hashmap *name;
+        unsigned    nres[2];
+    } rsets;
+    struct {
+        pa_hashmap *id;
+        pa_hashmap *name;
+        pa_hashmap *node;
+    } streams;
+};
+
+
+
+struct pa_resource_rset_entry {
+    size_t                     nstream;
+    pa_resource_stream_entry **streams;
+    char                      *name;
+    char                      *id;
+    pa_resource_rset_data     *rset;
+    bool                       type[2];
+    uint32_t                   updid;
+    bool                       dead;
+};
+
+struct pa_resource_stream_entry {
+    size_t                     nrset;
+    pa_resource_rset_entry   **rsets;
+    char                      *name;
+    char                      *id;
+    mir_node                  *node;
+};
+
+static void rset_data_copy(pa_resource_rset_data *,pa_resource_rset_data *,int);
+
+static pa_resource_rset_entry *rset_entry_new(pa_resource *, const char *,
+                                              const char *);
+static void rset_entry_free(pa_resource *, pa_resource_rset_entry *);
+static int rset_entry_add_stream_link(pa_resource_rset_entry *,
+                                      pa_resource_stream_entry *);
+static int rset_entry_remove_stream_link( pa_resource_rset_entry *,
+                                          pa_resource_stream_entry *);
+static void rset_entry_is_dead(pa_resource *, pa_resource_rset_entry *);
+
+
+static pa_resource_stream_entry *stream_entry_new(pa_resource *, const char *,
+                                                  const char *, mir_node *);
+static void stream_entry_free(pa_resource *, pa_resource_stream_entry *);
+static int stream_entry_add_rset_link(pa_resource_stream_entry *,
+                                      pa_resource_rset_entry *);
+static int stream_entry_remove_rset_link(pa_resource_stream_entry *,
+                                         pa_resource_rset_entry *);
+
+static bool is_number(const char *);
+
+static void enforce_policy(struct userdata *, mir_node *,
+                           pa_resource_rset_data *, int);
+
+
+
+pa_resource *pa_resource_init(struct userdata *u)
+{
+    pa_resource *resource;
+
+    resource = pa_xnew0(pa_resource, 1);
+
+    resource->rsets.id = pa_hashmap_new(pa_idxset_string_hash_func,
+                                        pa_idxset_string_compare_func);
+    resource->rsets.name = pa_hashmap_new(pa_idxset_string_hash_func,
+                                         pa_idxset_string_compare_func);
+
+    resource->streams.id = pa_hashmap_new(pa_idxset_string_hash_func,
+                                          pa_idxset_string_compare_func);
+    resource->streams.name = pa_hashmap_new(pa_idxset_string_hash_func,
+                                            pa_idxset_string_compare_func);
+    resource->streams.node = pa_hashmap_new(pa_idxset_trivial_hash_func,
+                                            pa_idxset_trivial_compare_func);
+
+    return resource;
+}
+
+void pa_resource_done(struct userdata *u)
+{
+    pa_resource *resource;
+    pa_resource_rset_entry *re;
+    pa_resource_stream_entry *se;
+    void *state;
+
+    if (u && (resource = u->resource)) {
+        PA_HASHMAP_FOREACH(re, resource->rsets.id, state)
+            rset_entry_free(resource, re);
+
+
+        PA_HASHMAP_FOREACH(re, resource->rsets.name, state)
+            rset_entry_free(resource, re);
+
+
+        PA_HASHMAP_FOREACH(se, resource->streams.id, state)
+            stream_entry_free(resource, se);
+
+        PA_HASHMAP_FOREACH(se, resource->streams.name, state)
+            stream_entry_free(resource, se);
+
+        pa_hashmap_free(resource->rsets.id);
+        pa_hashmap_free(resource->rsets.name);
+        pa_hashmap_free(resource->streams.id);
+        pa_hashmap_free(resource->streams.name);
+        pa_hashmap_free(resource->streams.node);
+    }
+}
+
+unsigned pa_resource_get_number_of_resources(struct userdata *u, int type)
+{
+    pa_resource *resource;
+
+    pa_assert(u);
+    pa_assert_se((resource = u->resource));
+    pa_assert(type == PA_RESOURCE_RECORDING || type == PA_RESOURCE_PLAYBACK);
+
+    return resource->rsets.nres[type];
+}
+
+void pa_resource_purge(struct userdata *u, uint32_t updid, int type)
+{
+    pa_resource *resource;
+    pa_resource_rset_entry *re;
+    void *state;
+
+    pa_assert(u);
+    pa_assert_se((resource = u->resource));
+    pa_assert(type == PA_RESOURCE_RECORDING || type == PA_RESOURCE_PLAYBACK);
+
+    pa_log("purging rsets ...");
+
+    PA_HASHMAP_FOREACH(re, resource->rsets.id, state) {
+        if (re->type[type] && re->updid != updid)
+            rset_entry_is_dead(resource, re);
+    }
+}
+
+
+int pa_resource_enforce_policies(struct userdata *u, int type)
+{
+    pa_resource *resource;
+    mir_direction direction;
+    pa_resource_stream_entry *se;
+    pa_resource_rset_entry *re;
+    pa_resource_rset_data rset;
+    mir_node *node;
+    void *state;
+    bool *grant;
+    char **policy;
+    size_t i;
+
+    pa_assert(u);
+    pa_assert_se((resource = u->resource));
+    pa_assert(type == PA_RESOURCE_RECORDING || type == PA_RESOURCE_PLAYBACK);
+
+    direction = (type == PA_RESOURCE_RECORDING) ? mir_output : mir_input;
+
+    PA_HASHMAP_FOREACH(se, resource->streams.node, state) {
+        pa_assert_se((node = se->node));
+
+        if (direction == node->direction) {
+            pa_assert_se((re = se->rsets[0]));
+            pa_assert(re->rset);
+
+            if (se->nrset == 1)
+                enforce_policy(u, node, re->rset, type);
+            else {
+                grant  = &rset.grant[type];
+                policy = &rset.policy[type];
+
+                memcpy(&rset, re->rset, sizeof(rset));
+                *grant = false;
+
+                for (i = 0;  i < se->nrset;  i++) {
+                    re = se->rsets[i];
+
+                    if (!pa_streq(re->rset->policy[type], *policy))
+                     *policy = pa_xstrdup("strict");
+
+                    *grant |= re->rset->grant[type];
+                }
+
+                enforce_policy(u, node, &rset, type);
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+pa_resource_rset_data *pa_resource_rset_data_new(void)
+{
+    pa_resource_rset_data *rset;
+
+    rset = pa_xnew0(pa_resource_rset_data, 1);
+
+    rset->id   = pa_xstrdup("<unknown>");
+    rset->name = pa_xstrdup("<unknown>");
+    rset->pid  = pa_xstrdup("<unknown>");
+
+    rset->policy[PA_RESOURCE_RECORDING] = pa_xstrdup("<unknown>");
+    rset->policy[PA_RESOURCE_PLAYBACK]  = pa_xstrdup("<unknown>");
+
+    return rset;
+}
+
+
+void pa_resource_rset_data_free(pa_resource_rset_data *rset)
+{
+    if (rset) {
+        pa_xfree(rset->id);
+        pa_xfree(rset->policy[PA_RESOURCE_RECORDING]);
+        pa_xfree(rset->policy[PA_RESOURCE_PLAYBACK]);
+        pa_xfree(rset->name);
+        pa_xfree(rset->pid);
+
+        pa_xfree(rset);
+    }
+}
+
+static void rset_data_copy(pa_resource_rset_data *dst,
+                           pa_resource_rset_data *src,
+                           int type)
+{
+    pa_assert(dst);
+    pa_assert(type == PA_RESOURCE_RECORDING || type == PA_RESOURCE_PLAYBACK);
+
+    if (!src)
+        return;
+
+    if (dst->id && !pa_streq(dst->id, "<unknown>")) {
+        if (!src->id || (src->id && !pa_streq(src->id, dst->id))) {
+            pa_log_error("refuse to update rset: mismatching ids (%s vs %s)",
+                         dst->id, src->id ? src->id : "<null>");
+            return;
+        }
+    }
+
+    if (dst->name && !pa_streq(dst->name, "<unknown>")) {
+        if (!src->name || (src->name && !pa_streq(src->name, dst->name))) {
+            pa_log_error("refuse to update rset: mismatching names (%s vs %s)",
+                         dst->name, src->name ? src->name:"<null>");
+            return;
+        }
+    }
+
+    if (dst->pid && !pa_streq(dst->pid, "<unknown>")) {
+        if (!src->pid || (src->pid && !pa_streq(src->pid, dst->pid))) {
+            pa_log_error("refuse to update rset: mismatching pids (%s vs %s)",
+                         dst->pid, src->pid ? src->pid : "<null>");
+            return;
+        }
+    }
+
+    pa_xfree(dst->id);
+    pa_xfree(dst->policy[type]);
+    pa_xfree(dst->name);
+    pa_xfree(dst->pid);
+
+    dst->autorel = src->autorel;
+    dst->state   = src->state;
+    dst->id      = src->id     ?  pa_xstrdup(src->id)     : NULL;
+    dst->name    = src->name   ?  pa_xstrdup(src->name)   : NULL;
+    dst->pid     = src->pid    ?  pa_xstrdup(src->pid)    : NULL;
+
+    dst->policy[type]  = src->policy[type] ?  pa_xstrdup(src->policy[type]) : NULL;
+    dst->grant[type]   = src->grant[type];
+}
+
+
+int pa_resource_rset_update(struct userdata *u,
+                            const char *name,
+                            const char *id,
+                            int type,
+                            pa_resource_rset_data *rset,
+                            uint32_t updid)
+{
+    pa_resource *resource;
+    pa_resource_rset_entry *re, *de;
+    pa_resource_stream_entry *se;
+    bool has_name, has_id;
+
+    pa_assert(u);
+    pa_assert_se((resource = u->resource));
+    pa_assert(type == PA_RESOURCE_RECORDING || type == PA_RESOURCE_PLAYBACK);
+
+    re = NULL;
+    has_name = name && name[0] && !pa_streq(name, "<unknown>");
+    has_id = id && id[0] && !pa_streq(id, "<unknown>");
+
+    if (!has_id)
+        return -1;
+
+    if (!(re = pa_hashmap_get(resource->rsets.id, id))) {
+        if ((has_name && (re = pa_hashmap_get(resource->rsets.name, name))) &&
+            (re->id == NULL)) {
+            /* we have an incomplete rset created by a stream, ie.
+               the stream was created first */
+
+            re->id = pa_xstrdup(id);
+
+            if (pa_hashmap_put(resource->rsets.id, re->id, re) != 0) {
+                pa_log_error("failed to add rset (id='%s' name='%s') "
+                             "to id hashmap",
+                             re->id ? re->id : "<unknown>",
+                             re->name ? re->name : "<unknown>");
+                return -1;
+            }
+
+            pa_log("complete rset entry and add it to id hash (id='%s' name='%s')",
+                   re->id, re->name ? re->name : "<unknown>");
+        }
+        else {
+            /* we need to create a new rset entry */
+            if ((has_name && (se = pa_hashmap_get(resource->streams.name, name))) ||
+                (has_id   && (se = pa_hashmap_get(resource->streams.id, id))))
+            {
+                /* found a matching stream entry, ie.
+                   that stream is controlled by multiple rsets */
+                pa_assert(se->nrset > 0);
+
+                if (!(re = rset_entry_new(resource, NULL, id))) {
+                    pa_log("failed to create rset (id='%s' name='%s'): "
+                           "invalid rset id or duplicate rset",
+                           id ? id : "<unknown>", name ? name : "<unknown>");
+                    return -1;
+                }
+
+                pa_log("stream controlled by multiple rsets => created new "
+                       "rset entry (id='%s' unused name='%s')",
+                       id ? id : "<unknown>", name ? name : "<unknown>");
+
+                if (has_name) {
+                    pa_log("removing rset (name='%s') from name hash", name);
+                    if ((de = pa_hashmap_remove(resource->rsets.name, name))) {
+                        pa_xfree(de->name);
+                        de->name = NULL;
+
+                        pa_log("stream controlled by multiple rsets => removing "
+                               "first rset entry from name hash (id='%s' name='%s')",
+                           de->id ? de->id : "<unknown>", name);
+                    }
+                }
+            }
+            else {
+                /* could not find matching stream entry, ie.
+                   the rset was created first*/
+
+                if (!(re = rset_entry_new(resource, name, id))) {
+                    pa_log("failed to create rset (id='%s' name='%s'): "
+                           "invalid rset name/id or duplicate rset",
+                           id ? id : "<unknown>", name ? name : "<unknown>");
+                    return -1;
+                }
+
+                pa_log("new rset entry (id='%s' name='%s')",
+                       id ? id : "<unknown>", name ? name : "<unknown>");
+
+                if (!(se = stream_entry_new(resource, name, id, NULL))) {
+                    pa_log("failed to link rset (id='%s' name='%s') to stream: "
+                           "invalid stream id/name or duplicate stream",
+                           id ? id : "<null>", name ? name : "<null>");
+                    rset_entry_free(resource, re);
+                    return -1;
+                }
+
+                pa_log("created incomplete stream entry (id='%s' name='%s')",
+                       id ? id : "<unknown>", name ? name : "<unknown>");
+            }
+
+            pa_assert(se);
+
+            rset_entry_add_stream_link(re, se);
+            stream_entry_add_rset_link(se, re);
+        }
+    }
+
+    pa_assert(re);
+
+    if (re->dead)
+        return -1;
+
+    if (!re->type[type]) {
+        re->type[type] = true;
+        resource->rsets.nres[type]++;
+    }
+
+    rset_data_copy(re->rset, rset, type);
+    re->updid = updid;
+
+    return 0;
+}
+
+
+int pa_resource_rset_remove(struct userdata *u,
+                            const char *name,
+                            const char *id)
+{
+    pa_resource *resource;
+    pa_resource_rset_entry *re;
+
+    pa_assert(u);
+    pa_assert_se((resource = u->resource));
+
+    if ((id   && (re = pa_hashmap_get(resource->rsets.id, id))) ||
+        (name && (re = pa_hashmap_get(resource->rsets.name, name))))
+    {
+        rset_entry_is_dead(resource, re);
+        return 0;
+    }
+
+    return -1;
+}
+
+static pa_resource_rset_entry *rset_entry_new(pa_resource *resource,
+                                              const char *name,
+                                              const char *id)
+{
+    pa_resource_rset_entry *re;
+
+    pa_assert(resource);
+    pa_assert(name || id);
+
+    re = pa_xnew0(pa_resource_rset_entry, 1);
+
+    re->streams = pa_xnew0(pa_resource_stream_entry *, 1);
+    re->rset = pa_resource_rset_data_new();
+
+    if (name && !pa_streq(name, "<unknown>")) {
+        pa_xfree(re->name);
+        re->name = pa_xstrdup(name);
+
+        if (pa_hashmap_put(resource->rsets.name, re->name, re) != 0) {
+            rset_entry_free(resource, re);
+            return NULL;
+        }
+    }
+
+    if (id && !pa_streq(id, "<unknown>")) {
+        pa_xfree(re->id);
+        re->id = pa_xstrdup(id);
+
+        if (pa_hashmap_put(resource->rsets.id, re->id, re) != 0) {
+            rset_entry_free(resource, re);
+            return NULL;
+        }
+    }
+
+    return re;
+}
+
+static void rset_entry_free(pa_resource *resource, pa_resource_rset_entry *re)
+{
+    if (re) {
+        if (re->name && !pa_streq(re->name, "<unknown>"))
+            pa_hashmap_remove(resource->rsets.name, re->name);
+        if (re->id && !pa_streq(re->id, "<unknown>"))
+            pa_hashmap_remove(resource->rsets.id, re->id);
+
+        pa_xfree(re->streams);
+        pa_xfree(re->name);
+        pa_xfree(re->id);
+        pa_resource_rset_data_free(re->rset);
+
+        pa_xfree(re);
+    }
+}
+
+
+static int rset_entry_add_stream_link(pa_resource_rset_entry *re,
+                                      pa_resource_stream_entry *se)
+{
+    size_t size;
+    int i;
+
+    pa_assert(re);
+
+    if (!se)
+        return -1;
+
+    for (i = 0;  i < (int)re->nstream;  i++) {
+        if (re->streams[i] == se)
+            return -1;
+    }
+
+    re->nstream++;
+    size = sizeof(pa_resource_stream_entry *) * (re->nstream + 1);
+    re->streams = pa_xrealloc(re->streams, size);
+
+    re->streams[i+0] = se;
+    re->streams[i+1] = NULL;
+
+    return 0;
+}
+
+
+static int rset_entry_remove_stream_link( pa_resource_rset_entry *re,
+                                          pa_resource_stream_entry *se)
+{
+    size_t i,j;
+
+    pa_assert(re);
+
+    if (se) {
+        for (i = 0;  i < re->nstream;  i++) {
+            if (re->streams[i] == se) {
+                for (j = i;  j < re->nstream;  j++)
+                    re->streams[j] = re->streams[j+1];
+
+                re->nstream--;
+
+                return 0;
+            }
+        }
+    }
+
+    return -1;
+}
+
+static void rset_entry_is_dead(pa_resource *resource, pa_resource_rset_entry *re)
+{
+    pa_resource_stream_entry *se;
+    pa_resource_rset_data *rset;
+
+    pa_assert(resource);
+    pa_assert(re);
+    pa_assert(re->nstream > 0);
+    pa_assert_se((se = re->streams[0]));
+
+    if (!re->dead) {
+        if (re->type[PA_RESOURCE_RECORDING])
+            resource->rsets.nres[PA_RESOURCE_RECORDING]--;
+
+        if (re->type[PA_RESOURCE_PLAYBACK])
+            resource->rsets.nres[PA_RESOURCE_PLAYBACK]--;
+
+        if ((re->nstream == 1 && se->nrset == 1) || re->nstream > 1) {
+            pa_log_debug("rset (id='%s' name='%s') "
+                         "was not updated => mark it as 'dead' but "
+                         "keep it as long as the streams is alive",
+                         re->id ? re->id : "<unknown>",
+                         re->name ? re->name : "<unknown>");
+
+            re->dead = true;
+
+            pa_assert_se((rset = re->rset));
+            rset->grant[PA_RESOURCE_RECORDING] = false;
+            rset->grant[PA_RESOURCE_PLAYBACK]  = false;
+
+            return;
+         }
+         else {
+             pa_log_debug("rset (id='%s' name='%s') "
+                          "was not updated => remove it",
+                          re->id ? re->id : "<unknown>",
+                          re->name ? re->name : "<unknown>");
+
+             rset_entry_remove_stream_link(re, se);
+             stream_entry_remove_rset_link(se, re);
+
+             rset_entry_free(resource, re);
+         }
+    }
+}
+
+
+int pa_resource_stream_update(struct userdata *u,
+                              const char *name,
+                              const char *id,
+                              mir_node *node)
+{
+    pa_resource *resource;
+    pa_resource_stream_entry *se, *de;
+    pa_resource_rset_entry *re;
+    bool has_name, has_id;
+
+    pa_assert(u);
+    pa_assert_se((resource = u->resource));
+
+    if (!node)
+        return -1;
+
+    re = NULL;
+    has_name = name && name[0] && !pa_streq(name, "<unknown>");
+    has_id = id && id[0] && !pa_streq(id, "<unknown>");
+
+    if (!has_name && !has_id)
+        return -1;
+
+    if (!(se = pa_hashmap_get(resource->streams.node, node))) {
+        if (((has_id   && (se = pa_hashmap_get(resource->streams.id, id)))  ||
+             (has_name && (se = pa_hashmap_get(resource->streams.name, name)))) &&
+            (se->node == NULL))
+        {
+            /* we have an incomplete stream entry created by an rset, ie.
+               the rset was created first*/
+
+            se->node = node;
+
+            if (pa_hashmap_put(resource->streams.node, se->node, se) != 0) {
+                pa_log_error("failed to add stream (id='%s' name='%s') "
+                             "to node hashmap",
+                             se->id   ? se->id : "<unknown>",
+                             se->name ? se->name : "<unknown>");
+                return -1;
+            }
+
+            pa_log("complete rset entry and add it to node hash (id='%s' name='%s')",
+                   se->id ? se->id : "<unknown>", se->name ? se->name : "<unknown>");
+
+        }
+        else {
+            /* we need to create a new stream entry */
+            if ((has_name && (re = pa_hashmap_get(resource->rsets.name, name)))||
+                (has_id   && (re = pa_hashmap_get(resource->rsets.id, id))))
+            {
+                /* found a matching rset that controls multiple streams, ie.
+                   the rset was created first */
+                pa_assert(re->nstream > 0);
+
+                if (!(se = stream_entry_new(resource, NULL, NULL, node))) {
+                    pa_log("failed to create stream (id='%s' name='%s'): "
+                           "duplicate stream node",
+                           id ? id : "<unknown>", name ? name : "<unknown>");
+                    return -1;
+                }
+
+                pa_log("rset controls multiple streams => created new "
+                       "stream entry (unused id='%s' unused name='%s') "
+                       "and added to node hash only",
+                       id ? id : "<unknown>", name ? name : "<unknown>");
+
+                if (has_id) {
+                    pa_log("removing stream (id='%s') form id hash", id);
+                    if ((de = pa_hashmap_remove(resource->streams.id, id))) {
+                        pa_xfree(de->id);
+                        de->id = NULL;
+                    }
+
+                    pa_log("rset controls multiple streams => removing first "
+                           "stream entry from id hash (id='%s')", id);
+                }
+
+                if (has_name) {
+                    pa_log("removing stream (name='%s') form name hash", name);
+                    if ((de = pa_hashmap_remove(resource->streams.name, name))) {
+                        pa_xfree(de->name);
+                        de->name = NULL;
+
+                        pa_log("rset controls multiple streams => removing first "
+                               "stream entry from name hash (name='%s')", name);
+                    }
+                }
+            }
+            else {
+                /* could not find matching rset entry, ie.
+                   the stream was created first */
+
+                if (!(se = stream_entry_new(resource, name, id, node))) {
+                    pa_log("failed to create stream (id='%s' name='%s'): "
+                           "invalid stream id/name or duplicate stream",
+                           id ? id : "<unknown>", name ? name : "<unknown>");
+                    return -1;
+                }
+
+                pa_log("new stream entry (id='%s' name='%s')",
+                       id ? id : "<unknown>", name ? name : "<unknown>");
+
+                if (!(re = rset_entry_new(resource, name, id))) {
+                    pa_log("failed to link stream (id='%s' name='%s') to rset: "
+                           "invalid rset id/name or duplicate rset",
+                           id ? id : "<null>", name ? name : "<null>");
+                    stream_entry_free(resource, se);
+                    return -1;
+                }
+
+                pa_log("created incomplete rset entry (id='%s' name='%s')",
+                       id ? id : "<unknown>", name ? name : "<unknown>");
+            }
+
+            pa_assert(re);
+
+            stream_entry_add_rset_link(se, re);
+            rset_entry_add_stream_link(re, se);
+        }
+    }
+
+    pa_assert(se);
+
+    return 0;
+}
+
+int pa_resource_stream_remove(struct userdata *u, mir_node *node)
+{
+    pa_resource *resource;
+    pa_resource_stream_entry *se;
+    pa_resource_rset_entry *re;
+
+
+    pa_assert(u);
+    pa_assert_se((resource = u->resource));
+    pa_assert(node);
+
+    if (!(se = pa_hashmap_remove(resource->streams.node, node))) {
+        pa_log("failed to remove stream (name='%s'): can't find it",
+               node->amname ? node->amname : "<unknown>");
+        return -1;
+    }
+
+    se->node = NULL;
+
+    pa_log("stream removed from node hash (id='%s' name='%s')",
+           se->id ? se->id : "<unknown>", se->name ? se->name : "<unknown>");
+
+
+    pa_assert(se->nrset > 0);
+    pa_assert_se((re = se->rsets[0]));
+    pa_assert(re->nstream > 0);
+
+    if (se->nrset == 1) {
+        /* stream is controlled by a single rset */
+
+        if (re->nstream == 1) {
+            /* the rset controls only this stream */
+            pa_assert(re->streams[0] == se);
+
+            if (re->dead) {
+                pa_log("stream is dead => free both "
+                       "rset (id='%s') & stream (name='%s')",
+                       re->id ? re->id : "<unknown>",
+                       se->name ? se->name : "<unknown>");
+
+                stream_entry_free(resource, se);
+                rset_entry_free(resource, re);
+            }
+            else {
+                pa_log("preserve incomplete stream as it is the last "
+                       "stream entry for rset (id='%s' name='%s')",
+                       re->id ? re->id : "<unknown>",
+                       re->name ? re->name : "<unknown>");
+            }
+        }
+        else {
+            /* beside this stream the rset controls other streams as well
+               so it is safe to destroy this stream as the rset does not
+               become streamless */
+
+            pa_log("rset controls multiple streams => destroy stream "
+                   "(id='%s' name='%s') as the reset does not become "
+                   "'streamless'", se->id ? se->id : "<unknown>",
+                   se->name ? se->name : "<unknown>");
+
+            rset_entry_remove_stream_link(re, se);
+            stream_entry_remove_rset_link(se, re);
+
+            stream_entry_free(resource, se);
+        }
+    }
+
+    return 0;
+}
+
+
+static pa_resource_stream_entry *stream_entry_new(pa_resource *resource,
+                                                  const char *name,
+                                                  const char *id,
+                                                  mir_node *node)
+{
+    pa_resource_stream_entry *se;
+
+    pa_assert(resource);
+    pa_assert(name || id || node);
+
+    se = pa_xnew0(pa_resource_stream_entry, 1);
+
+    se->rsets = pa_xnew0(pa_resource_rset_entry *, 1);
+
+    if (name && !pa_streq(name, "<unknown>")) {
+        se->name = pa_xstrdup(name);
+
+        if (pa_hashmap_put(resource->streams.name, se->name, se) != 0) {
+            stream_entry_free(resource, se);
+            return NULL;
+        }
+    }
+
+    if (is_number(id)) {
+        if (node && is_number(node->rset.id)) {
+            if (!pa_streq(id, node->rset.id)) {
+                stream_entry_free(resource, se);
+                return NULL;
+            }
+        }
+
+        se->id = pa_xstrdup(id);
+
+        if (pa_hashmap_put(resource->streams.id, se->id, se) != 0) {
+            stream_entry_free(resource, se);
+            return NULL;
+        }
+    }
+
+    if (node) {
+        se->node = node;
+
+        if (pa_hashmap_put(resource->streams.node, se->node, se) != 0) {
+            stream_entry_free(resource, se);
+            return NULL;
+        }
+    }
+
+    return se;
+}
+
+
+static void stream_entry_free(pa_resource *resource,
+                              pa_resource_stream_entry *se)
+{
+    if (se) {
+        if (se->name)
+            pa_hashmap_remove(resource->streams.name, se->name);
+        if (se->id)
+            pa_hashmap_remove(resource->streams.id, se->id);
+        if (se->node)
+            pa_hashmap_remove(resource->streams.node, se->node);
+
+        pa_xfree(se->rsets);
+        pa_xfree(se->name);
+        pa_xfree(se->id);
+
+        pa_xfree(se);
+    }
+}
+
+static int stream_entry_add_rset_link(pa_resource_stream_entry *se,
+                                      pa_resource_rset_entry *re)
+{
+    size_t size;
+    int i;
+
+    pa_assert(se);
+
+    if (!re)
+        return -1;
+
+    for (i = 0;  i < (int)se->nrset;  i++) {
+        if (se->rsets[i] == re)
+            return -1;
+    }
+
+    se->nrset++;
+    size = sizeof(pa_resource_rset_entry *) * (se->nrset + 1);
+    se->rsets = pa_xrealloc(se->rsets, size);
+
+    se->rsets[i+0] = re;
+    se->rsets[i+1] = NULL;
+
+    return 0;
+}
+
+static int stream_entry_remove_rset_link(pa_resource_stream_entry *se,
+                                         pa_resource_rset_entry *re)
+{
+    size_t i,j;
+
+    pa_assert(se);
+
+    if (re) {
+        for (i = 0;  i < se->nrset;  i++) {
+            if (se->rsets[i] == re) {
+                for (j = i;  j < se->nrset;  j++)
+                    se->rsets[j] = se->rsets[j+1];
+
+                se->nrset--;
+
+                return 0;
+            }
+        }
+    }
+
+    return -1;
+}
+
+
+static bool is_number(const char *string)
+{
+    const char *p = string;
+
+    if (string)
+        for (p = string;  isdigit(*p);   p++) ;
+    else
+        p = "a";
+
+    return *p ? false : true;
+}
+
+static void enforce_policy(struct userdata *u,
+                           mir_node *node,
+                           pa_resource_rset_data *rset,
+                           int type)
+{
+    int req;
+    char *policy;
+
+    pa_assert(u);
+    pa_assert(node);
+    pa_assert(rset);
+    pa_assert(type == PA_RESOURCE_RECORDING || PA_RESOURCE_PLAYBACK);
+    pa_assert_se((policy = rset->policy[type]));
+
+
+    if (pa_streq(policy, "relaxed"))
+        req = PA_STREAM_RUN;
+    else if (pa_streq(policy, "strict")) {
+        if (rset->state == PA_RESOURCE_RELEASE && rset->autorel)
+            req = PA_STREAM_KILL;
+        else {
+            if (rset->grant[type])
+                req = PA_STREAM_RUN;
+            else
+                req = PA_STREAM_BLOCK;
+        }
+    }
+    else {
+        req = PA_STREAM_BLOCK;
+    }
+
+    pa_stream_state_change(u, node, req);
+}
diff --git a/murphy/resource.h b/murphy/resource.h
new file mode 100644 (file)
index 0000000..1f64a37
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * module-murphy-ivi -- PulseAudio module for providing audio routing support
+ * Copyright (c) 2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
+ * MA 02110-1301 USA.
+ *
+ */
+#ifndef fooresourcefoo
+#define fooresourcefoo
+
+#include <stdarg.h>
+
+#include "userdata.h"
+
+/* rset types */
+#define PA_RESOURCE_RECORDING  0
+#define PA_RESOURCE_PLAYBACK   1
+
+#define PA_RESOURCE_RELEASE    1
+#define PA_RESOURCE_ACQUIRE    2
+
+
+struct pa_resource_rset_data {
+    char *id;
+    bool  autorel;
+    int   state;
+    bool  grant[2];
+    char *policy[2];
+    char *name;
+    char *pid;
+};
+
+
+pa_resource *pa_resource_init(struct userdata *);
+void pa_resource_done(struct userdata *);
+unsigned pa_resource_get_number_of_resources(struct userdata *, int);
+void pa_resource_purge(struct userdata *, uint32_t, int);
+int pa_resource_enforce_policies(struct userdata *, int);
+
+pa_resource_rset_data *pa_resource_rset_data_new(void);
+void pa_resource_rset_data_free(pa_resource_rset_data *);
+
+int pa_resource_rset_update(struct userdata *, const char *, const char *, int,
+                            pa_resource_rset_data *, uint32_t);
+int pa_resource_rset_remove(struct userdata *, const char *, const char *);
+
+
+int pa_resource_stream_update(struct userdata *, const char *, const char *,
+                              mir_node *);
+int pa_resource_stream_remove(struct userdata *, mir_node *);
+
+
+
+#endif /* fooresourcefoo */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
index 263edf9..51a4527 100644 (file)
@@ -74,6 +74,7 @@ typedef struct pa_zoneset               pa_zoneset;
 typedef struct pa_nodeset               pa_nodeset;
 typedef struct pa_nodeset_resdef        pa_nodeset_resdef;
 typedef struct pa_nodeset_map           pa_nodeset_map;
+typedef struct pa_node_rset             pa_node_rset;
 typedef struct pa_node_card             pa_node_card;
 typedef struct pa_card_hooks            pa_card_hooks;
 typedef struct pa_port_hooks            pa_port_hooks;
@@ -83,6 +84,10 @@ typedef struct pa_sink_input_hooks      pa_sink_input_hooks;
 typedef struct pa_source_output_hooks   pa_source_output_hooks;
 typedef struct pa_extapi                pa_extapi;
 typedef struct pa_murphyif              pa_murphyif;
+typedef struct pa_resource               pa_resource;
+typedef struct pa_resource_rset_data     pa_resource_rset_data;
+typedef struct pa_resource_rset_entry    pa_resource_rset_entry;
+typedef struct pa_resource_stream_entry  pa_resource_stream_entry;
 
 //typedef enum   mir_direction            mir_direction;
 //typedef enum   mir_implement            mir_implement;
@@ -114,7 +119,6 @@ typedef struct am_nodeunreg_data        am_nodeunreg_data;
 typedef struct am_ack_data              am_ack_data;
 typedef struct am_connect_data          am_connect_data;
 
-
 typedef struct {
     char *profile;    /**< During profile change it contains the new profile
                            name. Otherwise it is NULL. When sink tracking
@@ -239,6 +243,7 @@ struct userdata {
     pa_extapi     *extapi;
     pa_native_protocol *protocol;
     pa_murphyif   *murphyif;
+    pa_resource   *resource;
     bool           enable_multiplex;
 };