utils.c \
scripting.c \
extapi.c \
+ resource.c \
murphyif.c
configdir = $(sysconfdir)/pulse
if (make_rset)
pa_murphyif_create_resource_set(u, node, resdef);
+ else
+ node->rset.grant = 1;
}
if (route) {
if (make_rset)
pa_murphyif_create_resource_set(u, node, resdef);
+ else
+ node->rset.grant = 1;
pa_fader_apply_volume_limits(u, node->stamp);
}
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
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);
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);
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
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);
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);
}
#include "scripting.h"
#include "extapi.h"
#include "murphyif.h"
+#include "resource.h"
#include "classify.h"
#ifndef DEFAULT_CONFIG_DIR
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;
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;
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;
}
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);
#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))
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
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 *);
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);
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 *);
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)
return NULL;
}
#endif
-#ifdef WITH_RESOURCES
-#endif
murphyif = pa_xnew0(pa_murphyif, 1);
dif = &murphyif->domctl;
#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
}
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
#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)) {
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);
( 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));
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 {
rsetid, node->amname);
}
- pa_xfree(node->rsetid);
+ pa_xfree(node->rset.id);
node->localrset = false;
- node->rsetid = NULL;
+ node->rset.id = NULL;
}
}
}
{
#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;
}
}
{
#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
}
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;
( 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));
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;
}
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;
}
}
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);
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;
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;
}
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 {
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");
}
}
-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
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);
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
char *profile;
};
+struct pa_node_rset {
+ char *id; /**< resource set id, if any */
+ uint32_t grant; /**< permission to play/render etc */
+};
/**
* @brief routing endpoint
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 */
};
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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:
+ *
+ */
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;
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;
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
pa_extapi *extapi;
pa_native_protocol *protocol;
pa_murphyif *murphyif;
+ pa_resource *resource;
bool enable_multiplex;
};