gam-resource-manager: initial experimental/demo plugin implementation.
authorJanos Kovacs <jankovac503@gmail.com>
Sun, 4 May 2014 12:06:23 +0000 (15:06 +0300)
committerKrisztian Litkey <krisztian.litkey@intel.com>
Thu, 8 Jan 2015 16:37:16 +0000 (18:37 +0200)
GAM resource manager is a decision-tree based resource backend
implementation.

Change-Id: Ia353e92ddb9d17bf50882b60dad0001e7c2eb0fc

21 files changed:
src/Makefile.am
src/daemon/sample-config/resource.cfg
src/plugins/gam-resource-manager/backend.c [new file with mode: 0644]
src/plugins/gam-resource-manager/backend.h [new file with mode: 0644]
src/plugins/gam-resource-manager/c5-decision-tree.c [new file with mode: 0644]
src/plugins/gam-resource-manager/c5-decision-tree.h [new file with mode: 0644]
src/plugins/gam-resource-manager/decision-maker.c [new file with mode: 0644]
src/plugins/gam-resource-manager/decision-maker.h [new file with mode: 0644]
src/plugins/gam-resource-manager/decision-test.c [new file with mode: 0644]
src/plugins/gam-resource-manager/decision-tree.c [new file with mode: 0644]
src/plugins/gam-resource-manager/decision-tree.h [new file with mode: 0644]
src/plugins/gam-resource-manager/pattern-generator.c [new file with mode: 0644]
src/plugins/gam-resource-manager/pattern-generator.h [new file with mode: 0644]
src/plugins/gam-resource-manager/plugin-gam-resource-manager.c [new file with mode: 0644]
src/plugins/gam-resource-manager/plugin-gam-resource-manager.h [new file with mode: 0644]
src/plugins/gam-resource-manager/sink.c [new file with mode: 0644]
src/plugins/gam-resource-manager/sink.h [new file with mode: 0644]
src/plugins/gam-resource-manager/source.c [new file with mode: 0644]
src/plugins/gam-resource-manager/source.h [new file with mode: 0644]
src/plugins/gam-resource-manager/usecase.c [new file with mode: 0644]
src/plugins/gam-resource-manager/usecase.h [new file with mode: 0644]

index 54a6517..f6c2b3a 100644 (file)
@@ -1350,6 +1350,108 @@ plugin_ivi_resource_manager_la_LDFLAGS = -module -avoid-version
 plugin_ivi_resource_manager_la_LIBADD  = $(PLUGIN_IVI_RESOURCE_MANAGER_LIBS)
 
 plugin_LTLIBRARIES += plugin-ivi-resource-manager.la
+
+#
+# GAM support:
+# - decision-tree library
+# - gam-resource-manager plugin
+# - pattern generator and decision-test
+#
+
+# decision-tree library
+if BUILD_RESOURCES
+DECISION_TREE_LIBRARY = libmurphy-decision-tree.la
+
+lib_LTLIBRARIES += $(DECISION_TREE_LIBRARY)
+
+libmurphy_decision_tree_ladir =                                             \
+               $(includedir)/murphy/decision-tree
+
+libmurphy_decision_tree_la_HEADERS =                                        \
+               plugins/gam-resource-manager/decision-tree.h                \
+               plugins/gam-resource-manager/c5-decision-tree.h
+libmurphy_decision_tree_la_SOURCES =                                        \
+               plugins/gam-resource-manager/decision-tree.c                \
+               plugins/gam-resource-manager/c5-decision-tree.c
+libmurphy_decision_tree_la_CFLAGS =                                        \
+               $(AM_CFLAGS)
+libmurphy_decision_tree_la_LIBADD =                                         \
+               libmurphy-common.la
+
+libmurphy_decision_tree_la_DEPENDENCIES = linker-script.decision_tree       \
+       $(filter %.la, $(libmurphy_decision_tree_la_LIBADD))
+
+# debug file:line-function mapping generation
+decision-tree-func-info.c: $(libmurphy_decision_tree_la_REGULAR_SOURCES)
+       $(QUIET_GEN)$(top_builddir)/build-aux/gen-debug-table -o $@ $^
+
+clean-func-infos::
+       -rm decision-tree-func-info.c
+
+# decision-tree linker script generation
+linker-script.decision_tree: $(libmurphy_decision_tree_la_HEADERS)
+       $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+               -c "$(libmurphy_decision_tree_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+       -rm -f linker-script.decision_tree
+
+generate-linker-scripts: linker-script.decision_tree
+
+
+# gam-resource-manager plugin
+PLUGIN_GAM_RESOURCE_MANAGER_REGULAR_SOURCES =                              \
+               plugins/gam-resource-manager/plugin-gam-resource-manager.c  \
+               plugins/gam-resource-manager/backend.c                      \
+               plugins/gam-resource-manager/source.c                       \
+               plugins/gam-resource-manager/sink.c                         \
+               plugins/gam-resource-manager/usecase.c
+PLUGIN_GAM_RESOURCE_MANAGER_SOURCES =                                      \
+               $(PLUGIN_GAM_RESOURCE_MANAGER_REGULAR_SOURCES)              \
+                plugin-gam-resource-manager-func-info.c
+PLUGIN_GAM_RESOURCE_MANAGER_CFLAGS =                                       \
+               $(LUA_CFLAGS)
+
+PLUGIN_GAM_RESOURCE_MANAGER_LIBS =                                         \
+               libmurphy-common.la                                         \
+               libmurphy-decision-tree.la                                  \
+               $(RESOURCE_LIBRARY)                                         \
+               $(LUA_LIBS)
+
+plugin_gam_resource_manager_la_SOURCES = $(PLUGIN_GAM_RESOURCE_MANAGER_SOURCES)
+plugin_gam_resource_manager_la_CFLAGS  = $(PLUGIN_GAM_RESOURCE_MANAGER_CFLAGS)\
+                                        $(MURPHY_CFLAGS) $(AM_CFLAGS)        \
+                                        $(JSON_CFLAGS)
+plugin_gam_resource_manager_la_LDFLAGS = -module -avoid-version
+plugin_gam_resource_manager_la_LIBADD  = $(PLUGIN_GAM_RESOURCE_MANAGER_LIBS)
+
+plugin_LTLIBRARIES += plugin-gam-resource-manager.la
+
+# debug file:line-function mapping generation
+plugin-gam-resource-manager-func-info.c: $(PLUGIN_GAM_RESOURCE_MANAGER_REGULAR_SOURCES)
+       $(QUIET_GEN)$(top_builddir)/build-aux/gen-debug-table -o $@ $^
+
+clean-func-infos::
+       -rm plugin-gam-resource-manager-func-info.c
+
+# pattern-generator
+bin_PROGRAMS += pattern-generator
+
+pattern_generator_SOURCES =                              \
+       plugins/gam-resource-manager/pattern-generator.c \
+       plugins/gam-resource-manager/decision-maker.c
+pattern_generator_CFLAGS  = $(AM_CFLAGS)
+pattern_generator_LDADD   = libmurphy-common.la
+
+# decision-test
+bin_PROGRAMS += decision-test
+
+decision_test_SOURCES = plugins/gam-resource-manager/decision-test.c
+decision_test_CFLAGS  = $(AM_CFLAGS)
+decision_test_LDADD   =                                  \
+               libbreedline-murphy.la libbreedline.la   \
+               libmurphy-common.la                      \
+               libmurphy-decision-tree.la
 endif
 
 
index 07b6890..9bf36d4 100644 (file)
@@ -25,6 +25,15 @@ try_load('resource-wrt', {
 -- load the IVI resource manager if it is available
 load_if_exists('ivi-resource-manager')
 
+-- load the GAM resource manager if it is available
+load_if_exists('gam-resource-manager', {
+    config_dir = '/home/jko/Sources/protos/gam-poc/state-machine',
+    decision_names = 'gam-wrtApplication-4',
+    max_active = 4
+})
+
+
+
 --
 -- define application classes
 --
@@ -93,7 +102,9 @@ zone {
 --
 -- define resource classes
 --
-if not m:plugin_loaded('ivi-resource-manager') then
+if not m:plugin_loaded('ivi-resource-manager') and
+   not m:plugin_loaded('gam-resource-manager')
+then
    resource.class {
         name = "audio_playback",
         shareable = true,
@@ -105,15 +116,17 @@ if not m:plugin_loaded('ivi-resource-manager') then
    }
 end
 
-resource.class {
-     name = "audio_recording",
-     shareable = false,
-     attributes = {
-         role = { mdb.string, "music", "rw" },
-         pid = { mdb.string, "<unknown>", "rw" },
-         policy = { mdb.string, "relaxed", "rw" }
-     }
-}
+if not m:plugin_loaded('gam-resource-manager') then
+   resource.class {
+        name = "audio_recording",
+        shareable = false,
+        attributes = {
+           role = { mdb.string, "music", "rw" },
+            pid = { mdb.string, "<unknown>", "rw" },
+            policy = { mdb.string, "relaxed", "rw" }
+        }
+   }
+end
 
 resource.class {
      name = "video_playback",
diff --git a/src/plugins/gam-resource-manager/backend.c b/src/plugins/gam-resource-manager/backend.c
new file mode 100644 (file)
index 0000000..f49800f
--- /dev/null
@@ -0,0 +1,1228 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common.h>
+
+#include <murphy-db/mqi.h>
+
+#include <murphy/resource/config-api.h>
+#include <murphy/resource/manager-api.h>
+#include <murphy/resource/client-api.h>
+
+#include "backend.h"
+#include "source.h"
+#include "sink.h"
+#include "usecase.h"
+
+#define ANY_ZONE  (~((uint32_t)0))
+
+
+#define ATTRIBUTE(n,t,v)    {n, MRP_RESOURCE_RW, mqi_##t, {.t=v}}
+#define ATTR_END            {NULL, 0, 0, {.string=NULL}}
+
+
+typedef enum   decision_value_e  decision_value_t;
+typedef enum   state_value_e     state_value_t;
+typedef struct resource_s        resource_t;
+typedef struct resource_type_s   resource_type_t;
+typedef struct decision_s        decision_t;
+typedef struct state_s           state_t;
+
+enum decision_value_e {
+    STATE_ERROR = -1,
+
+    STATE_STOP = 0,
+    STATE_PAUSE,
+    STATE_PLAY,
+
+    STATE_MAX
+};
+
+enum state_value_e {
+    DECISION_ERROR = -1,
+
+    DECISION_TEARDOWN = 0,
+    DECISION_DISCONNECTED,
+    DECISION_CONNECTED,
+    DECISION_SUSPENDED,
+
+    DECISION_MAX
+};
+
+struct resource_type_s {
+    int id;
+    const char *name;
+    uint32_t resid;
+};
+
+struct mrp_resmgr_backend_s {
+    mrp_resmgr_t *resmgr;
+    resource_type_t types[MRP_RESMGR_RESOURCE_TYPE_MAX];
+    struct {
+        mrp_htbl_t *by_pointer;
+        mrp_htbl_t *by_connid;
+    } resources;
+};
+
+struct decision_s {
+    int32_t new;
+    int32_t current;
+};
+
+struct state_s {
+    int32_t new;
+    int32_t current;
+};
+
+struct mrp_resmgr_resource_s {
+    const char *name;
+    mrp_resmgr_backend_t *backend;
+    mrp_resource_t *res;
+    resource_type_t *type;
+    mrp_resmgr_source_t *source;
+    mrp_resmgr_sink_t *sink;
+    mrp_list_hook_t source_link;
+    uint32_t zoneid;
+    uint32_t connid;
+    uint32_t connno;
+    decision_t decision;
+    state_t state;
+};
+
+
+static void make_resource_definition(mrp_resmgr_backend_t *, int, const char*);
+static resource_type_t *find_resource_definition_by_id(mrp_resmgr_backend_t *,
+                                                       int);
+
+static int hash_compare(const void *, const void *);
+static uint32_t ptr_hash_function(const void *);
+static uint32_t id_hash_function(const void *);
+
+//static const char *get_resource_appid(mrp_resource_t *);
+static int32_t get_resource_sourceid(mrp_resource_t *);
+static int32_t get_resource_sinkid(mrp_resource_t *);
+static int32_t get_resource_connid(mrp_resource_t *);
+static int32_t get_resource_connno(mrp_resource_t *);
+static int32_t get_resource_stamp(mrp_resource_t *);
+
+static bool set_resource_source_and_sink(mrp_resource_t *,
+                                         mrp_resmgr_source_t *,
+                                         mrp_resmgr_sink_t *);
+static bool set_resource_stamp(mrp_resource_t *, int32_t);
+static bool set_resource_decision(mrp_resource_t *, int32_t);
+
+
+static void resource_create(mrp_resmgr_backend_t *, mrp_application_class_t *,
+                            mrp_zone_t *, mrp_resource_t *);
+static void resource_destroy(mrp_resmgr_backend_t *, mrp_zone_t *,
+                             mrp_resource_t *);
+static bool resource_acquire(mrp_resmgr_backend_t *, mrp_zone_t *,
+                             mrp_resource_t *);
+static bool resource_release(mrp_resmgr_backend_t *, mrp_zone_t *,
+                             mrp_resource_t *);
+
+static bool resource_register_by_id(mrp_resmgr_backend_t *,
+                                    mrp_resmgr_resource_t *);
+static mrp_resmgr_resource_t *resource_lookup_by_pointer(mrp_resmgr_backend_t*,
+                                                         mrp_resource_t *);
+
+static size_t resource_print_name(mrp_resmgr_source_t *,uint32_t,char *,size_t);
+
+
+static void make_decisions(mrp_resmgr_backend_t *);
+static void commit_decisions(mrp_resmgr_backend_t *);
+static size_t print_decision(mrp_resmgr_resource_t *, char *, size_t);
+static size_t print_commit(mrp_resmgr_resource_t *, char *, size_t);
+
+static void backend_notify(mrp_resource_event_t, mrp_zone_t *,
+                           mrp_application_class_t *, mrp_resource_t *, void*);
+static void backend_init(mrp_zone_t *, void *);
+static bool backend_allocate(mrp_zone_t *, mrp_resource_t *, void *);
+static void backend_free(mrp_zone_t *, mrp_resource_t *, void *);
+static bool backend_advice(mrp_zone_t *, mrp_resource_t *, void *);
+static void backend_commit(mrp_zone_t *, void *);
+
+
+
+#define APPID_ATTRIDX      0
+#define ROLE_ATTRIDX       1
+#define PID_ATTRIDX        2
+#define POLICY_ATTRIDX     3
+#define SRCNAM_ATTRIDX     4
+#define SRCID_ATTRIDX      5
+#define SINKNAM_ATTRIDX    6
+#define SINKID_ATTRIDX     7
+#define CONNID_ATTRIDX     8
+#define CONNNO_ATTRIDX     9
+#define STAMP_ATTRIDX     10
+#define DECISION_ATTRIDX  11
+
+#define ATTR_MAX          12
+
+static mrp_attr_def_t audio_attrs[] = {
+    ATTRIBUTE( "appid"      , string ,  "<undefined>" ),
+    ATTRIBUTE( "role"       , string ,  "music"       ),
+    ATTRIBUTE( "pid"        , string ,  "<unknown>"   ),
+    ATTRIBUTE( "policy"     , string ,  "relaxed"     ),
+    ATTRIBUTE( "source_name", string ,  "<undefined>" ),
+    ATTRIBUTE( "source_id"  , integer,  0             ),
+    ATTRIBUTE( "sink_name"  , string ,  "<undefined>" ),
+    ATTRIBUTE( "sink_id"    , integer,  0             ),
+    ATTRIBUTE( "connid"     , integer,  0             ),
+    ATTRIBUTE( "connno"     , integer,  0             ),
+    ATTRIBUTE( "stamp"      , integer,  0             ),
+    ATTRIBUTE( "decision"   , string ,  "<not yet>"   ),
+    ATTR_END
+};
+
+static mrp_resource_mgr_ftbl_t playback_ftbl = {
+    backend_notify,             /* notify   */
+    backend_init,               /* init     */
+    backend_allocate,           /* allocate */
+    backend_free,               /* free     */
+    backend_advice,             /* advice   */
+    NULL                        /* commit   */
+};
+
+static mrp_resource_mgr_ftbl_t recording_ftbl = {
+    backend_notify,             /* notify   */
+    NULL,                       /* init     */
+    backend_allocate,           /* allocate */
+    backend_free,               /* free     */
+    backend_advice,             /* advice   */
+    backend_commit              /* commit   */
+};
+
+static mrp_resource_mgr_ftbl_t *backend_ftbl[MRP_RESMGR_RESOURCE_TYPE_MAX] = {
+    [ MRP_RESMGR_RESOURCE_TYPE_PLAYBACK  ] = &playback_ftbl ,
+    [ MRP_RESMGR_RESOURCE_TYPE_RECORDING ] = &recording_ftbl,
+};
+
+
+static const char *decision_names[DECISION_MAX + 1] = {
+    [ DECISION_TEARDOWN     ] = "teardown"    ,
+    [ DECISION_DISCONNECTED ] = "disconnected",
+    [ DECISION_CONNECTED    ] = "connected"   ,
+    [ DECISION_SUSPENDED    ] = "suspended"   ,
+};
+
+static const char *state_names[STATE_MAX + 1] = {
+    [ STATE_STOP  ] = "stop" ,
+    [ STATE_PAUSE ] = "pause",
+    [ STATE_PLAY  ] = "play" ,
+};
+
+static state_value_t decision2state[DECISION_MAX] = {
+    [ DECISION_TEARDOWN     ] = STATE_STOP ,
+    [ DECISION_DISCONNECTED ] = STATE_STOP ,
+    [ DECISION_CONNECTED    ] = STATE_PLAY ,
+    [ DECISION_SUSPENDED    ] = STATE_PAUSE,
+};
+
+
+mrp_resmgr_backend_t *mrp_resmgr_backend_create(mrp_resmgr_t *resmgr)
+{
+    mrp_resmgr_backend_t *backend;
+    mrp_htbl_config_t pcfg, icfg;
+
+    MRP_ASSERT(resmgr, "invalid argument");
+
+    if ((backend = mrp_allocz(sizeof(mrp_resmgr_backend_t)))) {
+        pcfg.nentry = MRP_RESMGR_RESOURCE_MAX;
+        pcfg.comp = hash_compare;
+        pcfg.hash = ptr_hash_function;
+        pcfg.free = NULL;
+        pcfg.nbucket = MRP_RESMGR_RESOURCE_BUCKETS;
+
+        icfg.nentry = MRP_RESMGR_RESOURCE_MAX;
+        icfg.comp = hash_compare;
+        icfg.hash = id_hash_function;
+        icfg.free = NULL;
+        icfg.nbucket = MRP_RESMGR_RESOURCE_BUCKETS;
+
+        backend->resmgr = resmgr;
+        backend->resources.by_pointer = mrp_htbl_create(&pcfg);
+        backend->resources.by_connid = mrp_htbl_create(&icfg);
+
+        make_resource_definition(backend, MRP_RESMGR_RESOURCE_TYPE_PLAYBACK,
+                                 MRP_RESMGR_PLAYBACK_RESOURCE);
+        make_resource_definition(backend, MRP_RESMGR_RESOURCE_TYPE_RECORDING,
+                                 MRP_RESMGR_RECORDING_RESOURCE);
+    }
+
+    return backend;
+}
+
+void mrp_resmgr_backend_destroy(mrp_resmgr_backend_t *backend)
+{
+    if (backend) {
+        mrp_free(backend);
+    }
+}
+
+const char **mrp_resmgr_backend_get_decision_names(void)
+{
+    return decision_names;
+}
+
+uint32_t mrp_resmgr_backend_get_resource_connid(mrp_resmgr_resource_t *ar)
+{
+    MRP_ASSERT(ar, "invalid argument");
+
+    return ar->connid;
+}
+
+uint32_t mrp_resmgr_backend_get_resource_connno(mrp_resmgr_resource_t *ar)
+{
+    MRP_ASSERT(ar, "invalid argument");
+
+    return ar->connno;
+}
+
+int32_t mrp_resmgr_backend_get_resource_state(mrp_resmgr_resource_t *ar)
+{
+    MRP_ASSERT(ar, "invalid argument");
+
+    if (get_resource_stamp(ar->res) == 0)
+        return 0;
+
+    return ar->state.current;
+}
+
+int32_t mrp_resmgr_backend_get_resource_decision_id(mrp_resmgr_resource_t *ar)
+{
+    MRP_ASSERT(ar, "invalid argument");
+
+    if (get_resource_stamp(ar->res) == 0)
+        return 0;
+
+    return mrp_resmgr_sink_get_decision_id(ar->sink, ar->source);
+}
+
+
+uint32_t mrp_resmgr_backend_get_attribute_index(const char *name,
+                                                mqi_data_type_t type)
+{
+    mrp_attr_def_t *attrd;
+    uint32_t idx;
+
+    if (name) {
+        for (idx = 0;  (attrd = audio_attrs + idx)->name;  idx++) {
+            if (!strcmp(name, attrd->name)) {
+                if (attrd->type == type)
+                    return idx;
+                break;
+            }
+        } /* for attrd */
+    }
+
+    return MRP_RESMGR_RESOURCE_FIELD_INVALID;
+}
+
+
+int32_t mrp_resmgr_backend_get_integer_attribute(mrp_resmgr_resource_t *ar,
+                                                 uint32_t idx)
+{
+    mrp_attr_t attr;
+
+    if (mrp_resource_read_attribute(ar->res, idx, &attr)) {
+        if (attr.type == mqi_integer)
+            return attr.value.integer;
+    }
+
+    return 0;
+}
+
+const char *mrp_resmgr_backend_get_string_attribute(mrp_resmgr_resource_t *ar,
+                                                    uint32_t idx)
+{
+    mrp_attr_t attr;
+
+    if (mrp_resource_read_attribute(ar->res, idx, &attr)) {
+        if (attr.type == mqi_string)
+            return attr.value.string;
+    }
+
+    return "";
+}
+
+
+mrp_resmgr_resource_t *mrp_resmgr_backend_resource_list_entry(
+                                                   mrp_list_hook_t *entry)
+{
+    MRP_ASSERT(entry, "invalid argument");
+
+    return mrp_list_entry(entry, mrp_resmgr_resource_t, source_link);
+}
+
+
+
+
+
+
+#if 0
+int mrp_resmgr_backend_print(mrp_resmgr_backend_t *backend,
+                           uint32_t zoneid,
+                           char *buf, int len)
+{
+#define PRINT(...)                              \
+    do {                                        \
+        p += snprintf(p, e-p, __VA_ARGS__);     \
+        if (p >= e)                             \
+            return p - buf;                     \
+    } while (0)
+
+    char *p, *e;
+    uint32_t grantid;
+    mrp_list_hook_t *resources, *rentry, *rn;
+    mrp_resmgr_resource_t *ar;
+    mrp_attr_t a;
+    size_t i;
+    char disable[256];
+    char requisite[1024];
+
+    MRP_ASSERT(backend && buf && len > 0, "invalid argument");
+
+    e = (p = buf) + len;
+    *p = 0;
+
+    if (zoneid < MRP_ZONE_MAX) {
+        resources = backend->zones + zoneid;
+        grantid = backend->grantids[zoneid];
+    }
+    else {
+        resources = NULL;
+        grantid = 0;
+    }
+
+    PRINT("      Resource '%s' - grantid:%u\n",
+          MRP_SYSCTL_AUDIO_RESOURCE, grantid);
+
+    if (!resources || mrp_list_empty(resources))
+        PRINT("         No resources\n");
+    else {
+        mrp_list_foreach_back(resources, rentry, rn) {
+            ar = mrp_list_entry(rentry, mrp_resmgr_resource_t, link);
+
+            mrp_resmgr_disable_print(ar->disable, disable,
+                                     sizeof(disable));
+            mrp_application_requisite_print(ar->requisite, requisite,
+                                            sizeof(requisite));
+
+            PRINT("            "
+                  "key:0x%08x %s %s grantid:%u requisite:%s disable:%s",
+                  ar->key,
+                  ar->interrupt ? "interrupt" : "base",
+                  ar->acquire ? "acquire":"release",
+                  ar->grantid,
+                  requisite,
+                  disable);
+
+            for (i = 0;  i < MRP_ARRAY_SIZE(audio_attrs) - 1;  i++) {
+                if ((mrp_resource_read_attribute(ar->res, i, &a))) {
+                    PRINT(" %s:", a.name);
+
+                    switch (a.type) {
+                    case mqi_string:   PRINT("'%s'",a.value.string); break;
+                    case mqi_integer:  PRINT("%d",a.value.integer);  break;
+                    case mqi_unsignd:  PRINT("%u",a.value.unsignd);  break;
+                    case mqi_floating: PRINT("%lf",a.value.floating);break;
+                    default:           PRINT("<unsupported type>");  break;
+                    }
+                }
+            }
+
+            PRINT("\n");
+        } /* mrp_list_foreach_back - resources */
+    }
+
+    return p - buf;
+}
+#endif
+
+static void make_resource_definition(mrp_resmgr_backend_t *backend,
+                                     int id,
+                                     const char *name)
+{
+    resource_type_t *type = backend->types + id;
+
+    MRP_ASSERT(backend && id >= 0 && id < MRP_RESMGR_RESOURCE_TYPE_MAX,
+               "invalid attribute");
+
+    type->id = id;
+    type->name = mrp_strdup(name);
+    type->resid = mrp_resource_definition_create(type->name, true, /* share */
+                                                 audio_attrs, backend_ftbl[id],
+                                                 backend);
+
+    mrp_lua_resclass_create_from_c(type->resid);
+}
+
+static resource_type_t *find_resource_definition_by_id(
+                                             mrp_resmgr_backend_t *backend,
+                                             int id)
+{
+    resource_type_t *type;
+    int i;
+
+    MRP_ASSERT(backend, "invalid argument");
+
+    for (i = 0;  i < MRP_RESMGR_RESOURCE_TYPE_MAX;  i++) {
+        type = backend->types + i;
+
+        if (type->id == id)
+            return type;
+    }
+
+    return NULL;
+}
+
+static int hash_compare(const void *key1, const void *key2)
+{
+    if (key1 < key2)
+        return -1;
+    if (key1 > key2)
+        return 1;
+    return 0;
+}
+
+static uint32_t ptr_hash_function(const void *key)
+{
+    return (uint32_t)(((size_t)key >> 4) & 0xffffffff);
+}
+
+static uint32_t id_hash_function(const void *key)
+{
+    return (uint32_t)(key - (const void *)0);
+}
+
+
+#if 0
+static const char *get_resource_appid(mrp_resource_t *res)
+{
+    mrp_attr_t attr;
+    const char *appid;
+
+    if (!mrp_resource_read_attribute(res, APPID_ATTRIDX, &attr) ||
+        attr.type != mqi_string || !(appid = attr.value.string)  )
+        appid = NULL;
+
+    return appid;
+}
+#endif
+
+static int32_t get_resource_sourceid(mrp_resource_t *res)
+{
+    mrp_attr_t attr;
+
+    if (!mrp_resource_read_attribute(res, SRCID_ATTRIDX, &attr) ||
+        attr.type != mqi_integer)
+    {
+        return 0;
+    }
+
+    return attr.value.integer;
+}
+
+static int32_t get_resource_sinkid(mrp_resource_t *res)
+{
+    mrp_attr_t attr;
+
+    if (!mrp_resource_read_attribute(res, SINKID_ATTRIDX, &attr) ||
+        attr.type != mqi_integer)
+    {
+        return 0;
+    }
+
+    return attr.value.integer;
+}
+
+
+static int32_t get_resource_connid(mrp_resource_t *res)
+{
+    mrp_attr_t attr;
+
+    if (!mrp_resource_read_attribute(res, CONNID_ATTRIDX, &attr) ||
+        attr.type != mqi_integer)
+    {
+        return 0;
+    }
+
+    return attr.value.integer;
+}
+
+static int32_t get_resource_connno(mrp_resource_t *res)
+{
+    mrp_attr_t attr;
+
+    if (!mrp_resource_read_attribute(res, CONNNO_ATTRIDX, &attr) ||
+        attr.type != mqi_integer)
+    {
+        return 0;
+    }
+
+    return attr.value.integer;
+}
+
+static int32_t get_resource_stamp(mrp_resource_t *res)
+{
+    mrp_attr_t attr;
+
+    if (!mrp_resource_read_attribute(res, STAMP_ATTRIDX, &attr) ||
+        attr.type != mqi_integer)
+    {
+        return 0;
+    }
+
+    return attr.value.integer;
+}
+
+static bool set_resource_source_and_sink(mrp_resource_t *res,
+                                         mrp_resmgr_source_t *source,
+                                         mrp_resmgr_sink_t *sink)
+{
+    const char *source_name;
+    const char *sink_name;
+    mrp_attr_t attrs[ATTR_MAX+1];
+
+    memset(attrs, 0, sizeof(attrs));
+
+    if (!mrp_resource_read_all_attributes(res, ATTR_MAX+1, attrs))
+        return false;
+
+    if (source && (source_name = mrp_resmgr_source_get_name(source)))
+        attrs[SRCNAM_ATTRIDX].value.string = source_name;
+
+    if (sink && (sink_name = mrp_resmgr_sink_get_name(sink)))
+        attrs[SINKNAM_ATTRIDX].value.string = sink_name;
+
+    if (mrp_resource_write_attributes(res, attrs) < 0)
+        return false;
+
+    return true;
+}
+
+
+static bool set_resource_stamp(mrp_resource_t *res, int32_t stamp)
+{
+    mrp_attr_t attrs[ATTR_MAX+1];
+
+    memset(attrs, 0, sizeof(attrs));
+
+    if (!mrp_resource_read_all_attributes(res, ATTR_MAX+1, attrs))
+        return false;
+
+    attrs[STAMP_ATTRIDX].value.integer = stamp;
+
+    if (mrp_resource_write_attributes(res, attrs) < 0)
+        return false;
+
+    return true;
+}
+
+static bool set_resource_decision(mrp_resource_t *res, int32_t decision)
+{
+    const char *decision_name;
+    mrp_attr_t attrs[ATTR_MAX+1];
+
+    if (decision < 0 || decision >= DECISION_MAX)
+        decision_name = "<error>";
+    else
+        decision_name = decision_names[decision];
+
+    memset(attrs, 0, sizeof(attrs));
+
+    if (!mrp_resource_read_all_attributes(res, ATTR_MAX+1, attrs))
+        return false;
+
+    attrs[DECISION_ATTRIDX].value.string = decision_name;
+
+    if (mrp_resource_write_attributes(res, attrs) < 0)
+        return false;
+
+    return true;
+}
+
+
+static void resource_create(mrp_resmgr_backend_t *backend,
+                            mrp_application_class_t *ac,
+                            mrp_zone_t *zone,
+                            mrp_resource_t *res)
+{
+    mrp_resmgr_t *resmgr;
+    mrp_resmgr_resource_t *ar;
+    uint32_t zoneid;
+    uint32_t resid;
+    resource_type_t *type;
+    uint32_t srcid, sinkid;
+    mrp_resmgr_source_t *src;
+    mrp_resmgr_sink_t *sink;
+    int32_t connid;
+    int32_t connno;
+    char name[256];
+
+    MRP_UNUSED(ac);
+    MRP_UNUSED(zone);
+
+    MRP_ASSERT(backend && backend->resmgr && res, "invalid argument");
+
+    resmgr = backend->resmgr;
+    zoneid = mrp_zone_get_id(zone);
+    resid  = mrp_resource_get_id(res);
+    type   = find_resource_definition_by_id(backend, resid);
+    srcid  = get_resource_sourceid(res);
+    sinkid = get_resource_sinkid(res);
+    src    = (srcid  > 0) ? mrp_resmgr_source_find_by_id(resmgr, srcid):NULL;
+    sink   = (sinkid > 0) ? mrp_resmgr_sink_find_by_gam_id(resmgr,sinkid):NULL;
+    connid = get_resource_connid(res);
+    connno = get_resource_connno(res);
+
+    if (!type)
+        return;
+
+    resource_print_name(src, connno, name, sizeof(name));
+
+
+    if (!(ar = mrp_allocz(sizeof(mrp_resmgr_resource_t))))
+        return;
+
+    ar->name    = mrp_strdup(name);
+    ar->backend = backend;
+    ar->res     = res;
+    ar->type    = type;
+    ar->source  = src;
+    ar->sink    = sink;
+    ar->zoneid  = zoneid;
+    ar->connid  = connid;
+    ar->connno  = connno;
+
+    mrp_list_init(&ar->source_link);
+
+    if (!mrp_htbl_insert(backend->resources.by_pointer, res, ar)) {
+        mrp_log_error("gam-resource-manager: can't add resource %s"
+                      "to hash (by pointer)", ar->name);
+        mrp_free(ar);
+        return;
+    }
+
+    resource_register_by_id(backend, ar);
+
+    set_resource_source_and_sink(ar->res, ar->source, ar->sink);
+}
+
+static void resource_destroy(mrp_resmgr_backend_t *backend,
+                             mrp_zone_t *zone,
+                             mrp_resource_t *res)
+{
+    mrp_resmgr_usecase_t *usecase;
+    mrp_resmgr_resource_t *ar;
+    mrp_htbl_t *hash;
+
+    MRP_ASSERT(backend && backend->resmgr && zone && res,"invalid argument");
+
+    if (!(hash = backend->resources.by_pointer) ||
+        !(ar = mrp_htbl_remove(hash, res, false)))
+    {
+        mrp_debug("failed to destroy audio resource: can't find it");
+        return;
+    }
+
+    mrp_debug("%s resource '%s' going to be destroyed",
+              ar->type->name, ar->name);
+
+    if (ar->connid > 0 && (hash = backend->resources.by_connid)) {
+        if (ar != mrp_htbl_remove(hash, NULL + ar->connid, false)) {
+            mrp_log_error("gam-resource-manager: confused with data "
+                          "structures when attempting to remove "
+                          "resource '%s' from ID hash", ar->name);
+        }
+    }
+
+    mrp_list_delete(&ar->source_link);
+
+    mrp_free((void *)ar->name);
+
+    mrp_free(ar);
+
+    usecase = mrp_resmgr_get_usecase(backend->resmgr);
+    mrp_resmgr_usecase_update(usecase);
+}
+
+static bool resource_acquire(mrp_resmgr_backend_t *backend,
+                             mrp_zone_t *zone,
+                             mrp_resource_t *res)
+{
+    static int32_t stamp;
+
+    mrp_resmgr_resource_t *ar;
+
+    MRP_ASSERT(backend && zone && res, "invalid argument");
+
+    if (!(ar = resource_lookup_by_pointer(backend, res))) {
+        mrp_debug("failed to acquire audio resource: can't find it");
+        return false;
+    }
+
+    if (!set_resource_stamp(ar->res, ++stamp)) {
+        mrp_log_error("gam-resource-manager: failed to set 'stamp' "
+                      "property for '%s' when acquiring", ar->name);
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool resource_release(mrp_resmgr_backend_t *backend,
+                             mrp_zone_t *zone,
+                             mrp_resource_t *res)
+{
+    mrp_resmgr_resource_t *ar;
+
+    MRP_ASSERT(backend && zone && res, "invalid argument");
+
+    if (!(ar = resource_lookup_by_pointer(backend, res))) {
+        mrp_debug("failed to release audio resource: can't find it");
+        return false;
+    }
+
+    if (!set_resource_stamp(ar->res, 0)) {
+        mrp_log_error("gam-resource-manager: failed to set 'stamp' "
+                      "property for '%s' when releasing", ar->name);
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool resource_register_by_id(mrp_resmgr_backend_t *backend,
+                                    mrp_resmgr_resource_t *ar)
+{
+    if (ar->connid < 1)
+        return false;
+
+    if (!mrp_htbl_insert(backend->resources.by_connid, NULL + ar->connid, ar)) {
+        mrp_log_error("gam-resource-manager: can't add resource '%s'"
+                      "to hash (by connid)", ar->name);
+        return false;
+    }
+
+    if (!mrp_resmgr_source_add_resource(ar->source, &ar->source_link)) {
+        mrp_log_error("gam-resource-manager: can't add resource '%s'"
+                      "to source", ar->name);
+        return false;
+    }
+
+    return true;
+}
+
+static mrp_resmgr_resource_t *resource_lookup_by_pointer(mrp_resmgr_backend_t *backend,
+                                                         mrp_resource_t *res)
+{
+    mrp_htbl_t *htbl;
+
+    if (!backend || !(htbl = backend->resources.by_pointer) || !res)
+        return NULL;
+
+    return mrp_htbl_lookup(htbl, res);
+}
+
+static size_t resource_print_name(mrp_resmgr_source_t *src,
+                                  uint32_t connno,
+                                  char *name,
+                                  size_t len)
+{
+    size_t ret;
+
+    if (!src)
+        ret = snprintf(name, len, "<invalid>");
+    else if (connno < 2)
+        ret = snprintf(name, len, "%s", mrp_resmgr_source_get_name(src));
+    else {
+        ret = snprintf(name, len, "%s%d", mrp_resmgr_source_get_name(src),
+                       connno);
+    }
+
+    return ret;
+}
+
+
+static int decision_cb(void *key, void *object, void *user_data)
+{
+    mrp_resmgr_backend_t  *backend = (mrp_resmgr_backend_t *)user_data;
+    mrp_resmgr_resource_t *ar = (mrp_resmgr_resource_t *)object;
+    mrp_resmgr_t *resmgr;
+    mrp_resmgr_usecase_t *usecase;
+    bool sink_available, source_available;
+    int32_t decision_new, state_new;
+    mrp_attr_t attrs[ATTR_MAX+1];
+    uint16_t src_id, sink_id;
+    uint32_t connid;
+    mrp_resmgr_source_t *src;
+    bool need_update;
+    char buf[512];
+
+    MRP_UNUSED(key);
+    MRP_UNUSED(user_data);
+
+    MRP_ASSERT(ar && ar->backend == backend, "confused with data structures");
+
+    if (!ar->sink || !ar->source || !ar->connno) {
+        memset(attrs, 0, sizeof(attrs));
+        need_update = false;
+
+        if (mrp_resource_read_all_attributes(ar->res, ATTR_MAX+1, attrs)) {
+            src_id = attrs[SRCID_ATTRIDX].value.integer;
+            sink_id = attrs[SINKID_ATTRIDX].value.integer;
+            connid = attrs[CONNID_ATTRIDX].value.integer;
+
+            if (!ar->source && src_id > 0) {
+                src = mrp_resmgr_source_find_by_id(backend->resmgr, src_id);
+
+                if (src && mrp_resmgr_source_add_resource(src,&ar->source_link)) {
+                    resource_print_name(src, ar->connno, buf, sizeof(buf));
+                    mrp_free(ar->name);
+
+                    ar->name = mrp_strdup(buf);
+                    ar->source = src;
+
+                    mrp_debug("update resource %s source to '%s'",
+                              ar->name, mrp_resmgr_source_get_name(src));
+
+                    need_update = true;
+                }
+            }
+
+            if (!ar->sink && sink_id > 0) {
+                ar->sink = mrp_resmgr_sink_find_by_gam_id(backend->resmgr,
+                                                          sink_id);
+                mrp_debug("update resource %s sink to '%s'",
+                          ar->name, mrp_resmgr_sink_get_name(ar->sink));
+
+                need_update = true;
+            }
+
+            if (!ar->connid && connid > 0) {
+                ar->connid = connid;
+                mrp_debug("update resource %s connid to %u",
+                          ar->name, connid);
+
+                need_update = true;
+            }
+
+            if (need_update) {
+                resmgr = backend->resmgr;
+
+                if ((usecase = mrp_resmgr_get_usecase(resmgr)))
+                    mrp_resmgr_usecase_update(usecase);
+            }
+        }
+    }
+
+    source_available = mrp_resmgr_source_get_availability(ar->source);
+    sink_available = mrp_resmgr_sink_get_availability(ar->sink);
+
+    if (!source_available || !sink_available)
+        decision_new = DECISION_DISCONNECTED; /* or teardown ? */
+    else if (ar->connno != 0 || ar->connid < 1)
+        decision_new = DECISION_DISCONNECTED;
+    else
+        decision_new = mrp_resmgr_source_make_decision(ar->source);
+
+    if (decision_new < 0 || decision_new >= DECISION_MAX)
+        decision_new = ar->decision.current;
+
+    if (decision_new < 0 || decision_new >= DECISION_MAX)
+        decision_new = 0;
+
+    state_new = decision2state[decision_new];
+
+    ar->decision.new = decision_new;
+    ar->state.new = state_new;
+
+    print_decision(ar, buf, sizeof(buf));
+    mrp_debug("   %s", buf);
+
+    return MRP_HTBL_ITER_MORE;
+}
+
+static void make_decisions(mrp_resmgr_backend_t *backend)
+{
+    mrp_resmgr_usecase_t *usecase;
+
+    usecase = mrp_resmgr_get_usecase(backend->resmgr);
+    mrp_resmgr_usecase_update(usecase);
+
+    mrp_htbl_foreach(backend->resources.by_pointer, decision_cb, backend);
+}
+
+static int commit_cb(void *key, void *object, void *user_data)
+{
+    mrp_resmgr_backend_t  *backend = (mrp_resmgr_backend_t *)user_data;
+    mrp_resmgr_resource_t *ar = (mrp_resmgr_resource_t *)object;
+    char buf[256];
+
+    MRP_UNUSED(key);
+    MRP_UNUSED(user_data);
+
+    MRP_ASSERT(ar && ar->backend == backend, "confused with data structures");
+
+    if (ar->source) {
+        print_commit(ar, buf, sizeof(buf));
+        mrp_debug("   %s", buf);
+
+        ar->decision.current = ar->decision.new;
+        ar->state.current = ar->state.new;
+
+        set_resource_decision(ar->res, ar->decision.current);
+    }
+
+    return MRP_HTBL_ITER_MORE;
+}
+
+static void commit_decisions(mrp_resmgr_backend_t *backend)
+{
+    mrp_resmgr_usecase_t *usecase;
+
+    mrp_htbl_foreach(backend->resources.by_pointer, commit_cb, backend);
+
+    usecase = mrp_resmgr_get_usecase(backend->resmgr);
+    mrp_resmgr_usecase_update(usecase);
+}
+
+
+static size_t print_decision(mrp_resmgr_resource_t *ar, char *buf, size_t len)
+{
+    char name [256];
+    char decision[256];
+    char state[256];
+
+    snprintf(name, sizeof(name), "%s:", ar->name);
+
+    if (ar->decision.new == ar->decision.current) {
+        snprintf(decision, sizeof(decision), "%s (no change)",
+                 decision_names[ar->decision.new]);
+    }
+    else {
+        snprintf(decision, sizeof(decision), "%s => %s",
+                 decision_names[ar->decision.current],
+                 decision_names[ar->decision.new]);
+    }
+
+    if (ar->state.new == ar->state.current)
+        state[0] = 0;
+    else {
+        snprintf(state, sizeof(state), "%s => %s",
+                 state_names[ar->state.current],
+                 state_names[ar->state.new]);
+    }
+
+    return snprintf(buf, len, "%-24s %-28s %s", name, decision, state);
+}
+
+
+static size_t print_commit(mrp_resmgr_resource_t *ar, char *buf, size_t len)
+{
+    const char *decision;
+    const char *state;
+    char name[256];
+
+    snprintf(name, sizeof(name), "%s:", ar->name);
+
+    decision = decision_names[ar->decision.new];
+    state = state_names[ar->state.new];
+
+    return snprintf(buf, len, "%-24s %-12s %s", name, decision, state);
+}
+
+
+static void backend_notify(mrp_resource_event_t event,
+                           mrp_zone_t *zone,
+                           mrp_application_class_t *ac,
+                           mrp_resource_t *res,
+                           void *userdata)
+{
+    mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
+    const char *zonename = mrp_zone_get_name(zone);
+
+    MRP_ASSERT(zone && ac && res && backend, "invalid argument");
+
+    switch (event) {
+
+    case MRP_RESOURCE_EVENT_CREATED:
+        mrp_debug("audio resource in zone '%s' created", zonename);
+        resource_create(backend, ac, zone, res);
+        break;
+
+    case MRP_RESOURCE_EVENT_DESTROYED:
+        mrp_debug("audio resource in zone '%s' destroyed", zonename);
+        resource_destroy(backend, zone, res);
+        break;
+
+    case MRP_RESOURCE_EVENT_ACQUIRE:
+        mrp_debug("audio resource in zone '%s' is acquiring", zonename);
+        resource_acquire(backend, zone, res);
+        break;
+
+    case MRP_RESOURCE_EVENT_RELEASE:
+        mrp_debug("audio resource in zone '%s' is released", zonename);
+        resource_release(backend, zone, res);
+        break;
+
+    default:
+        mrp_log_error("gam-resource-manager: invalid event %d at audio "
+                      "notification (zone '%s')", event, zonename);
+        break;
+    }
+}
+
+static void backend_init(mrp_zone_t *zone, void *userdata)
+{
+    mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
+    // uint32_t zoneid;
+    const char *zonename;
+
+    MRP_ASSERT(zone && backend && backend->resmgr, "invalid argument");
+
+    // zoneid   = mrp_zone_get_id(zone);
+    zonename = mrp_zone_get_name(zone);
+
+    if (!zonename)
+        zonename = "<unknown>";
+
+    mrp_debug("audio init in zone '%s'", zonename);
+
+    make_decisions(backend);
+}
+
+static bool backend_allocate(mrp_zone_t *zone,
+                            mrp_resource_t *res,
+                            void *userdata)
+{
+    mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
+    // uint32_t zoneid;
+    const char *zonename;
+    mrp_resmgr_resource_t *ar;
+    bool allocated;
+
+    MRP_ASSERT(zone && res && backend && backend->resmgr, "invalid argument");
+
+    // zoneid = mrp_zone_get_id(zone);
+
+    if (!(zonename = mrp_zone_get_name(zone)))
+        zonename = "<unknown>";
+
+    if ((ar = resource_lookup_by_pointer(backend, res))) {
+        allocated = (ar->state.new == STATE_PLAY);
+
+        mrp_debug("%s allocation for '%s' in zone '%s' %s",
+                  ar->type->name, ar->name, zonename,
+                  allocated ? "succeeded":"failed");
+
+        return allocated;
+    }
+
+    mrp_log_error("gam-resource-manager: attempt to allocate untracked "
+                  "resource in zone '%s'", zonename);
+
+    return FALSE;
+}
+
+static void backend_free(mrp_zone_t *zone, mrp_resource_t *res, void *userdata)
+{
+    mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
+    const char *zonename;
+    mrp_resmgr_resource_t *ar;
+
+    MRP_ASSERT(zone && res && backend, "invalid argument");
+
+    if (!(zonename = mrp_zone_get_name(zone)))
+        zonename = "<unknown>";
+
+    if ((ar = resource_lookup_by_pointer(backend, res))) {
+        ar->decision.new = DECISION_DISCONNECTED;
+        ar->state.new = decision2state[ar->decision.new];
+
+        mrp_debug("free %s of '%s' in zone '%s'",
+                  ar->type->name, ar->name, zonename);
+
+        return;
+    }
+
+    mrp_log_error("gam-resource-manager: attempt to free untracked "
+                  "resource in zone '%s'", zonename);
+}
+
+static bool backend_advice(mrp_zone_t *zone,mrp_resource_t *res,void *userdata)
+{
+#if 1
+    MRP_UNUSED(zone);
+    MRP_UNUSED(res);
+    MRP_UNUSED(userdata);
+#else
+    mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
+    const char *zonename;
+    const char *appid;
+
+    MRP_ASSERT(zone && res && backend, "invalid argument");
+
+    if (!(zonename = mrp_zone_get_name(zone)))
+        zonename = "<unknown>";
+    if (!(appid = get_resource_appid(res)))
+        appid = "<unknown>";
+
+    mrp_debug("audio advice for '%s' in zone '%s'", appid, zonename);
+#endif
+
+    return TRUE;
+}
+
+static void backend_commit(mrp_zone_t *zone, void *userdata)
+{
+    mrp_resmgr_backend_t *backend = (mrp_resmgr_backend_t *)userdata;
+    const char *zonename;
+    // uint32_t zoneid;
+
+    MRP_ASSERT(zone && backend && backend->resmgr, "invalid argument");
+
+    // zoneid  = mrp_zone_get_id(zone);
+
+    if (!(zonename = mrp_zone_get_name(zone)))
+        zonename = "<unknown>";
+
+    mrp_debug("audio commit in zone '%s'", zonename);
+
+    commit_decisions(backend);
+}
diff --git a/src/plugins/gam-resource-manager/backend.h b/src/plugins/gam-resource-manager/backend.h
new file mode 100644 (file)
index 0000000..ff3af49
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_GAM_RESOURCE_MANAGER_BACKEND_H__
+#define __MURPHY_GAM_RESOURCE_MANAGER_BACKEND_H__
+
+#include <sys/types.h>
+
+#include <murphy/common/hashtbl.h>
+#include <murphy/resource/data-types.h>
+
+#include "plugin-gam-resource-manager.h"
+
+/* positive values are considered to be resource attribute indices */
+#define MRP_RESMGR_RESOURCE_FIELD_STATE        -1
+#define MRP_RESMGR_RESOURCE_FIELD_DECISION_ID  -2
+
+#define MRP_RESMGR_RESOURCE_FIELD_INVALID (~(uint32_t)0)
+
+
+mrp_resmgr_backend_t *mrp_resmgr_backend_create(mrp_resmgr_t *resmgr);
+void mrp_resmgr_backend_destroy(mrp_resmgr_backend_t *backend);
+const char **mrp_resmgr_backend_get_decision_names(void);
+
+uint32_t mrp_resmgr_backend_get_resource_connid(mrp_resmgr_resource_t *ar);
+uint32_t mrp_resmgr_backend_get_resource_connno(mrp_resmgr_resource_t *ar);
+int32_t mrp_resmgr_backend_get_resource_state(mrp_resmgr_resource_t *ar);
+int32_t mrp_resmgr_backend_get_resource_decision_id(mrp_resmgr_resource_t *ar);
+
+mrp_resmgr_resource_t *mrp_resmgr_backend_resource_list_entry(
+                                                   mrp_list_hook_t *entry);
+
+uint32_t mrp_resmgr_backend_get_attribute_index(const char *name,
+                                                mqi_data_type_t type);
+int32_t mrp_resmgr_backend_get_integer_attribute(mrp_resmgr_resource_t *ar,
+                                                 uint32_t idx);
+const char *mrp_resmgr_backend_get_string_attribute(mrp_resmgr_resource_t *ar,
+                                                    uint32_t idx);
+
+#if 0
+int mrp_resmgr_backend_print(mrp_resmgr_backend_t *backend, uint32_t zoneid,
+                             char *buf, int len);
+#endif
+
+#endif /* __MURPHY_GAM_RESOURCE_MANAGER_BACKEND_H__ */
diff --git a/src/plugins/gam-resource-manager/c5-decision-tree.c b/src/plugins/gam-resource-manager/c5-decision-tree.c
new file mode 100644 (file)
index 0000000..15407e1
--- /dev/null
@@ -0,0 +1,1447 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <murphy/common.h>
+
+#include "c5-decision-tree.h"
+
+
+#define ATTRIBUTE_MAX  32
+#define ENUM_MAX       16384
+#define ENUM_BUCKETS   16
+
+
+
+typedef enum   state_e              state_t;
+typedef struct conf_iter_s          conf_iter_t;
+typedef struct attr_value_iter_s    attr_value_iter_t;
+typedef struct value_list_item_s    value_list_item_t;
+typedef struct value_list_s         value_list_t;
+
+enum state_e {
+    START = 0,
+    NAME,
+    VALUE,
+    END
+};
+
+
+struct conf_iter_s {
+    mrp_decision_conf_t *conf;
+    int nattr;
+    mrp_decision_attr_t *attrs[0];
+};
+
+
+struct attr_value_iter_s {
+    mrp_decision_attr_t *attr;
+    int ndesc;
+    mrp_decision_attr_value_desc_t descs[0];
+};
+
+struct value_list_item_s {
+    const char *name;
+    int32_t value;
+};
+
+struct value_list_s {
+    int size;
+    value_list_item_t *items;
+};
+
+static bool conf_finish(mrp_decision_conf_t *, const char *);
+static conf_iter_t *conf_iterator(mrp_decision_conf_t *);
+
+static mrp_decision_attr_t *attr_create(mrp_decision_conf_t*,const char*,int);
+static void attr_destroy(void *, void *);
+static void attr_add_value(mrp_decision_attr_t *, const char *);
+static attr_value_iter_t *attr_value_iterator(mrp_decision_attr_t *);
+static size_t attr_print(mrp_decision_attr_t *, bool, char *, size_t);
+
+static void value_destroy(void *, void *);
+
+static bool tree_parse(mrp_decision_conf_t *, FILE *, size_t *, char *,
+                       mrp_decision_node_t **, int);
+static bool tree_parse_terminal_node(mrp_decision_conf_t *, FILE *, size_t *,
+                                     char *, bool, mrp_decision_node_t **, int);
+static bool tree_parse_test_node(mrp_decision_conf_t *, FILE *, size_t *,
+                                 char *, int, mrp_decision_node_t **, int);
+
+
+
+static bool property(char **, char **, char **);
+static bool list_item(char **, char **);
+static bool identifier(char **, char **, char *);
+static bool whitespace(char **);
+static bool quoted(char **, char **);
+
+static size_t print_node(mrp_decision_conf_t *, mrp_decision_node_t *,
+                         conf_iter_t *, char *, size_t, char *);
+static size_t print_bitmask(mrp_decision_attr_t *, mrp_decision_bitmask_t,
+                            char *, size_t);
+
+
+
+mrp_decision_conf_t *mrp_decision_conf_create_from_file(const char *stem)
+{
+    FILE *f;
+    char filnam[1024];
+    char *buf, *p;
+    size_t n;
+    ssize_t linlen;
+    state_t state;
+    char decision[256];
+    char *name;
+    char *value;
+    char sep;
+    size_t lineno;
+    mrp_decision_conf_t *conf;
+    mrp_decision_attr_t *attr;
+    mrp_htbl_config_t aconf;
+    int id;
+
+    if (!stem || !stem[0])
+        return false;
+
+    snprintf(filnam, sizeof(filnam), "%s.names", stem);
+    if (!(f = fopen(filnam, "r"))) {
+        mrp_log_error("gam-resource-manager: failed to open file '%s': %s",
+                      filnam, strerror(errno));
+        return NULL;
+    }
+
+    if (!(conf = mrp_allocz(sizeof(mrp_decision_conf_t)))) {
+        mrp_log_error("gam-resource-manager: can't allocate memory for "
+                      "'%s' decision configuration", stem);
+        return NULL;
+    }
+    else {
+        aconf.nentry  = ATTRIBUTE_MAX;
+        aconf.comp    = mrp_string_comp;
+        aconf.hash    = mrp_string_hash;
+        aconf.free    = attr_destroy;
+        aconf.nbucket = ATTRIBUTE_MAX;
+
+        conf->stem  = mrp_strdup(stem);
+        conf->nattr = 0;
+        conf->attrs = mrp_htbl_create(&aconf);
+
+        if (!conf->attrs) {
+            mrp_log_error("gam-resource-manager: failed to create attribute "
+                          "hash for '%s' decision configuration", stem);
+            mrp_decision_conf_destroy(conf);
+            return NULL;
+        }
+    }
+
+    state = START;
+    buf = NULL;
+    lineno = 0;
+    attr = NULL;
+    id = 0;
+    decision[0] = 0;
+
+    while ((linlen = getline(&buf, &n, f)) >= 0) {
+        lineno++;
+        p = buf;
+
+        whitespace(&p);
+
+        while (*p) {
+            switch (state) {
+
+            case START:
+                if (!identifier(&p, &name, &sep) || sep != '.')
+                    goto failed;
+                mrp_debug("decision = '%s'", name);
+                snprintf(decision, sizeof(decision), "%s", name);
+                state = NAME;
+                whitespace(&p);
+                break;
+
+            case NAME:
+                if (!identifier(&p, &name, &sep) || sep != ':')
+                    goto failed;
+                mrp_debug("name = '%s'", name);
+                if (!(attr = attr_create(conf, name, id++)))
+                    goto failed;
+                state = VALUE;
+                whitespace(&p);
+                break;
+
+            case VALUE:
+                if (!identifier(&p, &value, &sep) ||
+                    (sep != ',' && sep != '.' && sep != 0))
+                    goto failed;
+                mrp_debug("value = '%s'", value);
+                if (!strcmp(name, "continuous") && sep != '.')
+                    goto failed;
+                attr_add_value(attr, value);
+                if (sep == '.') {
+                    state = NAME;
+                    attr = NULL;
+                }
+                whitespace(&p);
+                break;
+
+            default:
+                break;
+            }
+        }
+
+        free(buf);
+        buf = NULL;
+    } /* while getline */
+
+    if (linlen < 0 && !feof(f)) {
+        mrp_log_error("gam-resource-manager: error during reading "
+                      "'%s' file: %s", filnam, strerror(errno));
+        mrp_decision_conf_destroy(conf);
+        return NULL;
+    }
+
+    fclose(f);
+
+    if (!conf_finish(conf, decision)) {
+        mrp_decision_conf_destroy(conf);
+        return NULL;
+    }
+
+    mrp_log_info("mrp-resource-manager: successfully loaded "
+                 "decision configuration from file '%s'", filnam);
+
+    return conf;
+
+  failed:
+    mrp_log_error("gam-resource-manager: error in file '%s' line %lu",
+                  filnam, lineno);
+    free(buf);
+    fclose(f);
+    mrp_decision_conf_destroy(conf);
+    return NULL;
+}
+
+void mrp_decision_conf_destroy(mrp_decision_conf_t *conf)
+{
+    if (conf) {
+        mrp_log_info("mrp-resource-manager: going to unload "
+                     "decision configuration '%s'", conf->stem);
+
+        if (conf->decision_attr && conf->decision_names)
+            mrp_free(conf->decision_names);
+
+        mrp_htbl_destroy(conf->attrs, TRUE);
+
+        mrp_free((void *)conf->stem);
+        mrp_free((void *)conf);
+    }
+}
+
+bool mrp_decision_set_attr_offset(mrp_decision_conf_t *conf,
+                                  const char *attr_name,
+                                  size_t offset)
+{
+    mrp_decision_attr_t *attr;
+
+    if (!conf || !attr_name)
+        return false;
+
+    if (!(attr = mrp_htbl_lookup(conf->attrs, (void *)attr_name)))
+        return false;
+
+    attr->offset = offset;
+
+    return true;
+}
+
+ssize_t mrp_decision_attr_list(mrp_decision_conf_t *conf,
+                               const char **buf, size_t len)
+{
+    conf_iter_t *it;
+    int i,j,n;
+
+    if (!conf || !buf || len < 1)
+        return -1;
+
+    if ((int)len < conf->nattr)
+        return -1;
+
+    if (!(it = conf_iterator(conf)))
+        return -1;
+
+    for (i = j = 0, n = it->nattr;  i < n;  i++) {
+        if (conf->decision_attr != it->attrs[i])
+            buf[j++] = it->attrs[i]->name;
+    }
+    buf[j] = NULL;
+
+    mrp_free(it);
+
+    return j;
+}
+
+ssize_t mrp_decision_attr_value_list(mrp_decision_conf_t *conf,
+                                     const char *attr_name,
+                                     mrp_decision_attr_value_desc_t *buf,
+                                     size_t len)
+{
+    mrp_decision_attr_t *attr;
+    attr_value_iter_t *it;
+    int buf_len;
+    int actual_len;
+    size_t size;
+
+    if (!conf || !attr_name || !buf || (buf_len = len) < 1)
+        return -1;
+
+    if (!(attr = mrp_htbl_lookup(conf->attrs, (void *)attr_name)))
+        return -1;
+
+    if (buf_len < attr->nvalue)
+        return -1;
+
+    if (!(it = attr_value_iterator(attr)))
+        return -1;
+
+    actual_len = it->ndesc;
+
+    size = sizeof(mrp_decision_attr_value_desc_t) * actual_len;
+    memcpy(buf, it->descs, size);
+
+    if (buf_len > actual_len) {
+        size = sizeof(mrp_decision_attr_value_desc_t) * (buf_len - it->ndesc);
+        memset(buf + it->ndesc, 0, size);
+    }
+
+    mrp_free(it);
+
+    return actual_len;
+}
+
+
+
+const char *mrp_decision_name(mrp_decision_conf_t *conf, int32_t decision)
+{
+    if (!conf || decision < 0 || decision > conf->decision_attr->nvalue)
+        return "<invalid>";
+
+    return conf->decision_names[decision];
+}
+
+
+int32_t mrp_decision_value_max(mrp_decision_conf_t *conf)
+{
+    if (!conf || !conf->decision_attr)
+        return 0;
+
+    return conf->decision_attr->nvalue;
+}
+
+int32_t mrp_decision_get_integer_attr_value(mrp_decision_conf_t *conf,
+                                            const char *attr_name,
+                                            const char *value_name,
+                                            bool *error)
+{
+    mrp_decision_attr_t *attr;
+    mrp_decision_value_t *value;
+
+    if (error)
+        *error = true;
+
+    if (!conf || !attr_name || !value_name)
+        return 0;
+
+    if (!(attr = mrp_htbl_lookup(conf->attrs, (void *)attr_name)))
+        return -1;
+
+    if (attr->value_type != MRP_DECISION_VALUE_INTEGER)
+        return -1;
+
+    if (!(value = mrp_htbl_lookup(attr->values, (void *)value_name)))
+        return -1;
+
+    if (error)
+        *error = false;
+
+    return value->integer;
+}
+
+
+const char *mrp_decision_get_integer_attr_name(mrp_decision_conf_t *conf,
+                                               const char *attr_name,
+                                               int32_t value,
+                                               bool *error)
+{
+    mrp_decision_attr_t *attr;
+    attr_value_iter_t *it;
+    const char *value_name;
+    int i;
+
+    if (error)
+        *error = true;
+
+    if (!conf || !attr_name)
+        return "<error>";
+
+    if (!(attr = mrp_htbl_lookup(conf->attrs, (void *)attr_name)))
+        return "<unknown attribute>";
+
+    if (attr->value_type != MRP_DECISION_VALUE_INTEGER)
+        return "<not integer attribute>";
+
+    if (!(it = attr_value_iterator(attr)))
+        return "<error>";
+
+    for (i = 0, value_name = "<unknown value>";   i < it->ndesc;   i++) {
+        if (it->descs[i].value == value) {
+            value_name = it->descs[i].name;
+            break;
+        }
+    }
+
+    if (i < it->ndesc && error)
+        *error = false;
+
+    return value_name;
+}
+
+
+size_t mrp_decision_conf_print(mrp_decision_conf_t *conf, char *buf,size_t len)
+{
+    conf_iter_t *it;
+    mrp_decision_attr_t *attr;
+    int i;
+    char *p, *e;
+
+    e = (p = buf) + len;
+
+    if (buf) {
+        if (!(it = conf_iterator(conf)))
+            p += snprintf(p, e-p, "<error>");
+        else {
+            p += snprintf(p, e-p, "attributes for '%s'\n", conf->stem);
+
+            for (i = 0;  i < it->nattr;  i++) {
+                attr = it->attrs[i];
+                p += attr_print(attr, (attr == conf->decision_attr), p, e-p);
+            }
+
+            mrp_free(it);
+        }
+    }
+
+    return p - buf;
+}
+
+
+static bool conf_finish(mrp_decision_conf_t *conf,
+                        const char *decision_attr_name)
+{
+    mrp_decision_attr_t *attr;
+    attr_value_iter_t *it;
+    mrp_decision_attr_value_desc_t *dsc;
+    size_t size;
+    const char **tbl;
+    int i, n;
+
+    if (!(attr = mrp_htbl_lookup(conf->attrs, (void *)decision_attr_name))) {
+        mrp_log_error("gam-resoure-manager: can't find decision attribute "
+                      "'%s' for '%s'", decision_attr_name, conf->stem);
+        return false;
+    }
+
+    if (attr->nvalue < 1) {
+        mrp_log_error("gam-resource-manager: attribute '%s' in '%s' is "
+                      "not suitable for decisions", attr->name, conf->stem);
+        return false;
+    }
+
+    n = attr->nvalue;
+    size = sizeof(const char *) * n;
+
+    if (!(it = attr_value_iterator(attr)) || !(tbl = mrp_allocz(size))) {
+        mrp_free(it);
+        mrp_log_error("gam-resource-manager: can't allocate memory to "
+                      "finalize '%s' decision", conf->stem);
+        return false;
+    }
+
+    for (i = 0;  i < n;  i++) {
+        dsc = it->descs + i;
+
+        if (i != dsc->value) {
+            mrp_log_error("gam-resource-manager: internal error: decision "
+                          "values of '%s' are non-continous for '%s' decisions",
+                          attr->name, conf->stem);
+            mrp_free(it);
+            mrp_free(tbl);
+            return false;
+        }
+
+        tbl[i] = dsc->name;
+    };
+
+    mrp_free(it);
+
+    conf->decision_attr = attr;
+    conf->decision_names = tbl;
+
+    return true;
+}
+
+
+static int conf_iter_cb(void *key, void *object, void *user_data)
+{
+    conf_iter_t *it = (conf_iter_t *)user_data;
+    mrp_decision_attr_t *attr = (mrp_decision_attr_t *)object;
+
+    MRP_UNUSED(key);
+
+    if (it->nattr >= it->conf->nattr) {
+        mrp_log_error("gam-resource-manager: detected inconsitency while "
+                      "iterating configuration of '%s'", it->conf->stem);
+    }
+    else {
+        it->attrs[it->nattr++] = attr;
+    }
+
+    return MRP_HTBL_ITER_MORE;
+}
+
+static conf_iter_t *conf_iterator(mrp_decision_conf_t *conf)
+{
+    conf_iter_t *it;
+    size_t size;
+    mrp_decision_attr_t *tmp;
+    int i,j;
+
+    if (!conf)
+        it = NULL;
+    else {
+        size = sizeof(conf_iter_t) + (sizeof(void *) * conf->nattr);
+
+        if ((it = mrp_allocz(size))) {
+            it->conf = conf;
+
+            mrp_htbl_foreach(conf->attrs, conf_iter_cb, it);
+
+            for (i = 0;   i < it->nattr - 1;   i++) {
+                for (j = i + 1;   j < it->nattr;   j++) {
+                    if (it->attrs[i]->id > it->attrs[j]->id) {
+                        tmp = it->attrs[i];
+                        it->attrs[i] = it->attrs[j];
+                        it->attrs[j] = tmp;
+                    }
+                }
+            }
+        }
+    }
+
+    return it;
+}
+
+
+static mrp_decision_attr_t *attr_create(mrp_decision_conf_t *conf,
+                                        const char *name, int id)
+{
+    mrp_decision_attr_t *attr;
+
+    if (!conf || !name)
+        attr = NULL;
+    else {
+        if ((attr = mrp_allocz(sizeof(mrp_decision_attr_t)))) {
+            attr->name       = mrp_strdup(name);
+            attr->id         = id;
+            attr->attr_type  = MRP_DECISION_ATTR_CONTINUOUS;
+            attr->value_type = MRP_DECISION_VALUE_INTEGER;
+            attr->nvalue     = 0;
+            attr->values     = NULL;
+        }
+
+        if (!mrp_htbl_insert(conf->attrs, (void *)attr->name, attr)) {
+            attr_destroy((void *)attr->name, (void *)attr);
+            attr = NULL;
+        }
+
+        conf->nattr++;
+    }
+
+    return attr;
+}
+
+static void attr_destroy(void *key, void *object)
+{
+    mrp_decision_attr_t *attr = (mrp_decision_attr_t *)object;
+
+    MRP_UNUSED(key);
+
+    if (attr->values)
+        mrp_htbl_destroy(attr->values, TRUE);
+
+    mrp_free((void *)attr->name);
+    mrp_free(object);
+}
+
+static void attr_add_value(mrp_decision_attr_t *attr, const char *name)
+{
+    mrp_htbl_config_t vconf;
+    mrp_decision_value_t *value;
+    char *key = NULL;
+
+    if (attr && name) {
+        if (attr->attr_type == MRP_DECISION_ATTR_CONTINUOUS) {
+            vconf.nentry  = ENUM_MAX;
+            vconf.comp    = mrp_string_comp;
+            vconf.hash    = mrp_string_hash;
+            vconf.free    = value_destroy;
+            vconf.nbucket = ENUM_BUCKETS;
+
+            attr->attr_type = MRP_DECISION_ATTR_ENUM;
+            attr->nvalue    = 0;
+            attr->values    = mrp_htbl_create(&vconf);
+        }
+
+        if (!(key = mrp_strdup(name)) || !(value = mrp_allocz(sizeof(*value))))
+            mrp_free((void *)key);
+        else {
+            value->integer = attr->nvalue++;
+            mrp_htbl_insert(attr->values, key, value);
+        }
+    }
+}
+
+static int attr_value_iter_cb(void *key, void *object, void *user_data)
+{
+    attr_value_iter_t *it = (attr_value_iter_t *)user_data;
+    const char *name = (const char *)key;
+    mrp_decision_value_t *value = (mrp_decision_value_t *)object;
+    mrp_decision_attr_value_desc_t *desc;
+
+    if (it->ndesc >= it->attr->nvalue) {
+        mrp_log_error("gam-resource-manager: detected inconsitency while "
+                      "iterating decision attribute '%s' for '%s'",
+                      name, it->attr->name);
+    }
+    else {
+        desc = it->descs + it->ndesc++;
+        desc->name = name;
+        desc->value = value->integer;
+    }
+
+    return MRP_HTBL_ITER_MORE;
+}
+
+static attr_value_iter_t *attr_value_iterator(mrp_decision_attr_t *attr)
+{
+    attr_value_iter_t *it;
+    mrp_decision_attr_value_desc_t tmp;
+    size_t size;
+    int i,j;
+
+    if (!attr)
+        it = NULL;
+    else {
+        size = sizeof(*it) +
+               (sizeof(mrp_decision_attr_value_desc_t) * attr->nvalue);
+
+        if ((it = mrp_allocz(size))) {
+            it->attr = attr;
+            it->ndesc = 0;
+            mrp_htbl_foreach(attr->values, attr_value_iter_cb, it);
+
+            for (i = 0;   i < it->ndesc - 1;   i++) {
+                for (j = i + 1;  j < it->ndesc;   j++) {
+                    if (it->descs[i].value > it->descs[j].value) {
+                        tmp = it->descs[i];
+                        it->descs[i] = it->descs[j];
+                        it->descs[j] = tmp;
+                    }
+                }
+            }
+        }
+    }
+
+    return it;
+}
+
+static size_t attr_print(mrp_decision_attr_t *attr, bool decision,
+                         char *buf, size_t len)
+{
+#define PRINT(args...) \
+    do { if (p < e) p += snprintf(p, e-p, args); } while (0)
+
+    attr_value_iter_t *it;
+    mrp_decision_attr_value_desc_t *dsc;
+    char *p, *e;
+    const char *sep;
+    char nambuf[256];
+    int i;
+
+    e = (p = buf) + len;
+
+    if (attr && p < e) {
+        snprintf(nambuf, sizeof(nambuf), "%2d %s:", attr->id, attr->name);
+        PRINT(" %c %-24s @%03lu  ", decision ? '*':' ', nambuf, attr->offset);
+
+        switch (attr->attr_type) {
+
+        case MRP_DECISION_ATTR_ENUM:
+            if (!(it = attr_value_iterator(attr)))
+                PRINT("<error>\n");
+            else {
+                PRINT("{");
+                for (i = 0;   i < it->ndesc;   i++) {
+                    dsc = it->descs + i;
+                    sep = (i == 0) ?  ""   : ((i % 10) ?
+                                      ", " :
+                                      ",\n                                  ");
+                    PRINT("%s[%d (%s)]", sep, dsc->value, dsc->name);
+                }
+                PRINT("}\n");
+
+                mrp_free(it);
+            }
+            break;
+
+        case MRP_DECISION_ATTR_CONTINUOUS:
+            PRINT("continuous\n");
+            break;
+
+        default:
+            PRINT("<unsupported attribute type %d>\n", attr->attr_type);
+            break;
+        }
+    }
+
+    return p - buf;
+
+#undef PRINT
+}
+
+static void value_destroy(void *key, void *object)
+{
+    mrp_free(key);
+    mrp_free(object);
+}
+
+
+mrp_decision_node_t *mrp_decision_tree_create_from_file(
+                                             mrp_decision_conf_t *conf,
+                                             const char *stem)
+{
+    FILE *f;
+    char filnam[1024];
+    char *buf, *p;
+    size_t n;
+    ssize_t linlen;
+    size_t lineno;
+    mrp_decision_node_t *root, *node;
+    mrp_decision_value_type_t vtype;
+    char *name, *value;
+
+    if (!stem)
+        stem = conf->stem;
+
+    snprintf(filnam, sizeof(filnam), "%s.tree", stem);
+    if (!(f = fopen(filnam, "r"))) {
+        printf("failed to open file '%s': %s\n", filnam, strerror(errno));
+        return NULL;
+    }
+
+    vtype = conf->decision_attr->value_type;
+
+    if (!(root = mrp_decision_tree_create(stem, vtype))) {
+        mrp_log_error("gam-resource-manager: failed to create "
+                      "decision tree for '%s'", stem);
+        return NULL;
+    }
+
+    lineno = 0;
+    buf = NULL;
+    node = NULL;
+
+    while ((linlen = getline(&buf, &n, f)) >= 0) {
+        lineno++;
+        p = buf;
+
+        whitespace(&p);
+
+        if (!strncmp(p, "type", 4)) {
+            if (!(tree_parse(conf, f, &lineno, buf, &node, 0)))
+                goto failed;
+            if (!(mrp_decision_add_node_to_root(root, node)))
+                goto failed;
+            node = NULL;
+        }
+        else if (property(&p, &name, &value)) {
+            if (!strcmp(name, "id")) {
+                mrp_debug("id: %s", value);
+            }
+            else if (!strcmp(name, "entries")) {
+                mrp_debug("entries: %s", value);
+            }
+            else {
+                goto parse_error;
+            }
+        }
+
+        free(buf);
+        buf = NULL;
+    }
+
+    if (linlen < 0 && !feof(f)) {
+        mrp_log_error("gam-resource-manager: error during reading "
+                      "'%s' file: %s", filnam, strerror(errno));
+        goto failed;
+    }
+
+    fclose(f);
+
+    mrp_log_info("mrp-resource-manager: successfully loaded "
+                 "decision tree from file '%s'", filnam);
+
+    return root;
+
+ parse_error:
+    mrp_log_error("gam-resource-manager: error in file '%s' line %lu",
+                  filnam, lineno);
+ failed:
+    mrp_log_error("gam-resource-manager: failed to parse '%s' file",
+                  filnam);
+    free(buf);
+    fclose(f);
+    mrp_decision_tree_destroy(root);
+    mrp_decision_tree_destroy(node);
+    return NULL;
+}
+
+static const char *indent(int depth)
+{
+    static char buf[1024];
+    size_t l = depth * 3;
+    memset(buf, ' ', l);
+    buf[l] = 0;
+    return buf;
+}
+
+static bool tree_parse(mrp_decision_conf_t *conf,
+                       FILE *f, size_t *lineno,
+                       char *buf,
+                       mrp_decision_node_t **node,
+                       int depth)
+{
+    char *p = buf;
+    char *name, *value, *e;
+    int type;
+
+    if (!property(&p, &name, &value) || strcmp(name, "type"))
+        return false;
+
+    type = strtol(value, &e, 10);
+
+    if (e == value || *e)
+        return false;
+
+    switch (type) {
+    case 0:
+        return tree_parse_terminal_node(conf, f, lineno, p, false, node, depth);
+
+    case 1:
+    case 2:
+    case 3:
+        return tree_parse_test_node(conf, f, lineno, p, type, node, depth);
+
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+static bool tree_parse_terminal_node(mrp_decision_conf_t *conf,
+                                     FILE *f, size_t *lineno,
+                                     char *buf,
+                                     bool need_empty,
+                                     mrp_decision_node_t **node,
+                                     int depth)
+{
+    char *p = buf;
+    char *name, *value;
+    mrp_decision_value_t *vptr;
+    char *decision_name;
+    int32_t decision;
+    bool has_decision;
+    bool has_cases;
+
+    MRP_UNUSED(f);
+    MRP_UNUSED(lineno);
+    MRP_UNUSED(depth);
+
+    has_decision = false;
+    has_cases = false;
+
+    while (*p) {
+        if (!property(&p, &name, &value))
+            break;
+
+        if (!strcmp(name, "class")) {
+            if (!(vptr = mrp_htbl_lookup(conf->decision_attr->values, value)))
+                return false;
+            else {
+                decision_name = value;
+                decision = vptr->integer;
+                has_decision = true;
+            }
+        }
+        if (!strcmp(name, "freq")) {
+            has_cases = true;
+        }
+
+        whitespace(&p);
+    }
+
+    if (has_decision) {
+        if (!need_empty && node) {
+            mrp_debug("%sterminal: %d/%s", indent(depth),
+                  decision, decision_name);
+            if (!(*node = mrp_decision_create_terminal_node(vptr)))
+                return false;
+            return true;
+        }
+        if (need_empty && !has_cases)
+            return true;
+    }
+
+    return false;
+}
+
+static bool tree_parse_test_node(mrp_decision_conf_t *conf,
+                                 FILE *f, size_t *lineno,
+                                 char *buf,
+                                 int type,
+                                 mrp_decision_node_t **node,
+                                 int depth)
+{
+    mrp_decision_node_t *child;
+    char *p;
+    char *name, *value;
+    mrp_decision_attr_t *attr;
+    int nbr;
+    char *e;
+    size_t n;
+    char *buf2;
+    int listidx;
+    value_list_t *lists, *l;
+    value_list_item_t *iv;
+    mrp_decision_value_t *av;
+    mrp_decision_value_type_t testval_type;
+    mrp_decision_value_t testval;
+    mrp_decision_condition_t testcond;
+    int i,j,k;
+    attr_value_iter_t *ait;
+    char valbuf[4096], *q;
+    size_t size;
+    bool ok, success;
+    char dbgbuf[256];
+
+    p = buf;
+    child = NULL;
+    attr = NULL;
+    nbr  = -1;
+    listidx = 0;
+    lists = NULL;
+    buf2 = NULL;
+    ait = NULL;
+    success = false;
+
+    testcond = MRP_DECISION_EQ;
+    testval_type = MRP_DECISION_VALUE_UNKNOWN;
+    memset(&testval, 0, sizeof(testval));
+
+    while (*p) {
+        if (!property(&p, &name, &value))
+            break;
+
+        if (!strcmp(name, "att")) {
+            if (attr)
+                goto finish_parsing;
+
+            if (!(attr = mrp_htbl_lookup(conf->attrs, value)))
+                goto finish_parsing;
+        }
+        else if (!strcmp(name, "forks")) {
+            if (nbr >= 0)
+                goto finish_parsing;
+
+            nbr = strtol(value, &e, 10);
+
+            if (*e || e == value || nbr <= 0 || nbr > 100)
+                goto finish_parsing;
+
+            if (type == 3)
+                lists = mrp_allocz(sizeof(*lists) * nbr);
+        }
+        else if (!strcmp(name, "elts")) {
+            if (!attr || !lists || listidx >= nbr)
+                goto finish_parsing;
+
+            l = lists + listidx++;
+            l->items = mrp_allocz(sizeof(l->items[0]) * attr->nvalue);
+
+            do {
+                if (l->size >= attr->nvalue)
+                    goto finish_parsing;
+                if (!(av = mrp_htbl_lookup(attr->values, value)))
+                    goto finish_parsing;
+
+                iv = l->items + l->size++;
+                iv->name = value;
+                iv->value = av->integer;
+
+            } while (list_item(&p, &value));
+        }
+
+        whitespace(&p);
+
+    } /* while property */
+
+    if (attr && nbr > 0) {
+        if (type == 1) {
+            if (getline(&buf2, &n, f) < 0)
+                goto finish_parsing;
+
+            if (!tree_parse_terminal_node(conf, f,lineno,buf2, true, NULL, 0))
+                goto finish_parsing;
+
+            free(buf2);
+            buf2 = NULL;
+            nbr--;
+            if (!(ait = attr_value_iterator(attr)))
+                goto finish_parsing;
+        }
+
+        mrp_debug("%stest/%d: '%s'", indent(depth), nbr, attr->name);
+
+        if (!(*node = mrp_decision_create_test_node()))
+            goto finish_parsing;
+
+        for (i=0, buf2=NULL;  i < nbr && getline(&buf2,&n,f) >= 0;   i++) {
+            (*lineno)++;
+
+            switch (type) {
+
+            case 1:
+                testval_type = MRP_DECISION_VALUE_INTEGER;
+                testval.integer = ait->descs[i].value;
+                testcond = MRP_DECISION_EQ;
+                snprintf(valbuf, sizeof(valbuf), "%s", ait->descs[i].name);
+                break;
+
+            case 2:
+                break;
+
+            case 3:
+                testval_type = MRP_DECISION_VALUE_UNKNOWN;
+                l = lists + i;
+                if (l->size == 1) {
+                    testcond = MRP_DECISION_EQ;
+                    testval_type = MRP_DECISION_VALUE_INTEGER;
+                    testval.integer = l->items[0].value;
+                    snprintf(valbuf, sizeof(valbuf), "%s", l->items[0].name);
+                }
+                else {
+                    testcond = MRP_DECISION_IN;
+                    if (l->size <= (int)MRP_DECISION_BITMASK_WIDTH) {
+                        testval_type = MRP_DECISION_VALUE_BITMASK;
+                        testval.bitmask = 0;
+                    }
+                    else {
+                        testval_type = MRP_DECISION_ARRAY |
+                            MRP_DECISION_VALUE_INTEGER;
+                        testval.array.size = l->size;
+                        size = sizeof(mrp_decision_value_t) * testval.array.size;
+                        testval.array.values = mrp_allocz(size);
+                    }
+                    e = (q = valbuf) + sizeof(valbuf);
+                    for (j = 0;   j < l->size;   j++) {
+                        if (q < e) {
+                            q += snprintf(q, e-q, "%s%s",
+                                          j?",":"", l->items[j].name);
+                        }
+                        k = l->items[j].value;
+                        if (testval_type == MRP_DECISION_VALUE_BITMASK)
+                            testval.bitmask |= MRP_DECISION_BIT(k);
+                        else
+                            testval.array.values[j].integer = k;
+                    }
+                }
+                break;
+
+            default:
+                goto finish_parsing;
+            }
+
+            switch (testval_type) {
+            case MRP_DECISION_VALUE_BITMASK:
+                snprintf(dbgbuf, sizeof(dbgbuf), " 0x%x", testval.bitmask);
+                break;
+            case MRP_DECISION_VALUE_INTEGER:
+                snprintf(dbgbuf, sizeof(dbgbuf), " %d", testval.integer);
+                break;
+            default:
+                dbgbuf[0] = 0;
+            }
+            mrp_debug("%s%s %s '%s'%s", indent(depth+1),
+                      mrp_decision_condition_str(testcond),
+                      mrp_decision_value_type_str(testval_type),
+                      valbuf, dbgbuf);
+
+            if (!tree_parse(conf, f, lineno, buf2, &child, depth+2))
+                goto finish_parsing;
+
+            ok = mrp_decision_add_branch_to_test_node(*node, testcond, attr->id,
+                                                      testval_type, &testval,
+                                                      attr->offset, child);
+            if (!ok)
+                goto finish_parsing;
+
+            child = NULL;
+            if ((testval_type & MRP_DECISION_ARRAY))
+                mrp_free(testval.array.values);
+            testval_type = MRP_DECISION_VALUE_UNKNOWN;
+
+            free(buf2);
+            buf2 = NULL;
+        }
+
+        success = true;
+    }
+
+ finish_parsing:
+    if (lists) {
+        for (i = 0; i < nbr; i++)
+            free(lists[i].items);
+        mrp_free(lists);
+    }
+    if ((testval_type & MRP_DECISION_ARRAY))
+        mrp_free(testval.array.values);
+    mrp_decision_tree_destroy(child);
+    mrp_free(ait);
+    free(buf2);
+
+    return success;
+}
+
+
+
+static bool property(char **buf, char **name, char **value)
+{
+    char *p = *buf;
+    char term;
+
+    if (identifier(&p, name, &term) &&
+        term == '=' &&
+        quoted(&p, value))
+    {
+        whitespace(&p);
+        *buf = p;
+        return true;
+    }
+
+    return false;
+}
+
+static bool list_item(char **buf, char **name)
+{
+    char *p = *buf;
+
+    whitespace(&p);
+
+    if (*p++ != ',')
+        return false;
+
+    if (quoted(&p, name)) {
+        *buf = p;
+        return true;
+    }
+
+    return false;
+}
+
+
+static bool identifier(char **buf, char **id, char *term)
+{
+    char *p, *q, c;
+
+    q = *buf;
+
+    whitespace(&q);
+
+    for (p = q;  (c = *p);  p++) {
+        if (!isalnum(c))
+            break;
+    }
+
+    if (p == q)
+        return false;
+
+    whitespace(&p);
+    if ((*term = *p))
+        *p++ = 0;
+
+    *buf = p;
+    *id = q;
+    return true;
+}
+
+
+static bool whitespace(char **buf)
+{
+    char *p, c;
+
+    for (p = *buf;  (c = *p);  p++) {
+        if (c != ' ' && c != '\t' && c != '\n')
+            break;
+    }
+
+    *buf = p;
+    return true;
+}
+
+static bool quoted(char **buf, char **string)
+{
+    char *p, *q, c;
+
+    q = *buf;
+
+    whitespace(&q);
+
+    if (*q++ != '"')
+        return false;
+
+    for (p = q; (c = *p);  p++) {
+        if (c < 0x20)
+            return -1;
+        if (c == '"' && p[-1] != '\\') {
+            *p++ = 0;
+            *buf = p;
+            *string = q;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+size_t mrp_decision_tree_print(mrp_decision_conf_t *conf,
+                               mrp_decision_node_t *node,
+                               char *buf, size_t len)
+{
+    conf_iter_t *cit;
+    char *p, *e;
+
+    e = (p = buf) + len;
+
+    if (conf && node && buf && len > 0) {
+        if (!(cit = conf_iterator(conf)))
+            p += snprintf(p, e-p, "<error>\n");
+        else {
+            p += print_node(conf, node, cit, p, e-p, NULL);
+            p += snprintf(p, e-p, "\n");
+            mrp_free(cit);
+        }
+    }
+
+    return p - buf;
+}
+
+
+static size_t print_node(mrp_decision_conf_t *conf,
+                         mrp_decision_node_t *node,
+                         conf_iter_t *cit,
+                         char *buf, size_t len,
+                         char *indent)
+{
+#define PRINT(_args...)      \
+    do {if (p < e) p += snprintf(p,e-p, _args);} while(0)
+#define PRINT_VALUE(_t,_v)   \
+    do {if (p < e) p += mrp_decision_value_print(_t, _v, p,e-p);} while(0)
+#define PRINT_BITMASK(_a,_m) \
+    do {if (p < e) p += print_bitmask(_a, _m, p, e-p);} while(0)
+#define PRINT_NODE(_p,_i)    \
+    do {if (p < e) p += print_node(conf, (_p)->node, cit, p,e-p, _i);} while(0)
+
+    static char indent_buf[4096];
+    static char *indent_end = indent_buf + sizeof(indent_buf);
+
+    mrp_decision_root_node_t *root;
+    mrp_decision_test_node_t *test;
+    mrp_decision_terminal_node_t *term;
+    mrp_decision_branch_t *branch;
+    mrp_decision_attr_t *attr;
+    const char *attr_name;
+    int32_t value_idx;
+    attr_value_iter_t *ait;
+    char *p, *e, *new_indent;
+    int32_t decision;
+    size_t i;
+
+    if (!node || !buf)
+        return 0;
+
+    if (!indent) {
+        indent_buf[0] = 0;
+        indent = indent_buf;
+    }
+
+    e = (p = buf) + len;
+
+    switch (node->type) {
+
+    case MRP_DECISION_ROOT_NODE:
+        root = &node->root;
+        PRINT("root of %s (decision type: %s)", root->name,
+              mrp_decision_value_type_str(root->decision_value_type));
+        new_indent  = indent;
+        new_indent += snprintf(indent, indent_end-indent, "\n");
+        PRINT_NODE(root, new_indent);
+        *indent = 0;
+        break;
+
+    case MRP_DECISION_TEST_NODE:
+        test = &node->test;
+
+        for (i = 0;  i < test->nbranch;  i++) {
+            branch = test->branches + i;
+            if (branch->value_id < 0 || branch->value_id >= cit->nattr)
+                PRINT("%s:...<invalid attribute>", indent_buf);
+            else {
+                attr = cit->attrs[branch->value_id];
+                attr_name = attr ? attr->name : "<invalid attribute>";
+                PRINT("%s:...%s %s ", indent_buf, attr_name,
+                      mrp_decision_condition_str(branch->condition));
+
+                if (branch->value_type != MRP_DECISION_VALUE_INTEGER) {
+                    PRINT_VALUE(branch->value_type, &branch->value);
+                    if (branch->value_type == MRP_DECISION_VALUE_BITMASK) {
+                        PRINT(" (");
+                        PRINT_BITMASK(attr, branch->value.bitmask);
+                        PRINT(")");
+                    }
+                }
+                else {
+                    value_idx = branch->value.integer;
+                    if (value_idx < 0 || value_idx >= attr->nvalue ||
+                        !(ait = attr_value_iterator(attr)))
+                        PRINT("<invalid attribute value>");
+                    else {
+                        PRINT("%d (%s)", value_idx, ait->descs[value_idx].name);
+                        mrp_free(ait);
+                    }
+                }
+            }
+            new_indent  = indent;
+            new_indent += snprintf(new_indent, indent_end-indent, "%c   ",
+                                   i == test->nbranch-1 ? ' ':':');
+            PRINT_NODE(branch, new_indent);
+            *indent = 0;
+        }
+
+        break;
+
+    case MRP_DECISION_TERMINAL_NODE:
+        term = &node->terminal;
+        decision = term->decision.integer;
+        if (decision < 0 || decision >= conf->decision_attr->nvalue)
+            PRINT(" => decision <invalid value>");
+        else
+            PRINT(" => %s", conf->decision_names[decision]);
+        break;
+
+    default:
+        PRINT("%s<unknown node type %d>\n", indent_buf, node->type);
+        break;
+    }
+
+    return p - buf;
+
+#undef PRINT_VALUE
+#undef PRINT
+}
+
+
+static size_t print_bitmask(mrp_decision_attr_t *attr,
+                            mrp_decision_bitmask_t bitmask,
+                            char *buf, size_t len)
+{
+    attr_value_iter_t *it;
+    mrp_decision_bitmask_t m;
+    char *p, *e;
+    int i,j;
+    char *sep;
+
+    if (!attr || !buf || len < 1)
+        return 0;
+
+    if (!(it = attr_value_iterator(attr)))
+        return 0;
+
+    e = (p = buf) + len;
+
+    if (!(m = bitmask))
+        p += snprintf(p, e-p, "<empty>");
+    else {
+        for (i = 0, sep = "";   m && i < it->ndesc && p < e;  i++, m >>= 1) {
+            if ((m & 1)) {
+                if (it->descs[i].value == i)
+                    j = i;
+                else {
+                    for (j = 0;  j < it->ndesc;  j++) {
+                        if (it->descs[j].value == i)
+                            break;
+                    }
+                }
+                if (j < 0 || j >= it->ndesc)
+                    p += snprintf(p, e-p, "%s<unknown value %d>", sep, j);
+                else
+                    p += snprintf(p, e-p, "%s%s", sep, it->descs[j].name);
+                sep = ",";
+            }
+        }
+    }
+
+    mrp_free(it);
+
+    return p - buf;
+}
diff --git a/src/plugins/gam-resource-manager/c5-decision-tree.h b/src/plugins/gam-resource-manager/c5-decision-tree.h
new file mode 100644 (file)
index 0000000..14c1c9d
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MRP_C5_DECISION_TREE_H__
+#define __MRP_C5_DECISION_TREE_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <murphy/common/hashtbl.h>
+
+#include "decision-tree.h"
+
+typedef enum  mrp_decision_attr_type_e         mrp_decision_attr_type_t;
+typedef struct mrp_decision_attr_s             mrp_decision_attr_t;
+typedef struct mrp_decision_conf_s             mrp_decision_conf_t;
+typedef struct mrp_decision_attr_value_desc_s  mrp_decision_attr_value_desc_t;
+
+enum mrp_decision_attr_type_e {
+    MRP_DECISION_ATTR_UNKNOWN = 0,
+    MRP_DECISION_ATTR_ENUM,
+    MRP_DECISION_ATTR_CONTINUOUS,
+};
+
+struct mrp_decision_attr_s {
+    const char *name;
+    int id;
+    mrp_decision_attr_type_t attr_type;
+    mrp_decision_value_type_t value_type;
+    size_t offset;
+    int nvalue;
+    mrp_htbl_t *values;
+};
+
+
+struct mrp_decision_conf_s {
+    const char *stem;
+    int nattr;
+    mrp_htbl_t *attrs;
+    mrp_decision_attr_t *decision_attr;
+    const char **decision_names;
+};
+
+
+struct mrp_decision_attr_value_desc_s {
+    const char *name;
+    int32_t value;
+};
+
+
+mrp_decision_conf_t *mrp_decision_conf_create_from_file(const char *stem);
+void mrp_decision_conf_destroy(mrp_decision_conf_t *conf);
+
+bool mrp_decision_set_attr_offset(mrp_decision_conf_t *conf,
+                                  const char *attr_name, size_t offset);
+
+ssize_t mrp_decision_attr_list(mrp_decision_conf_t *conf,
+                               const char **buf, size_t len);
+ssize_t mrp_decision_attr_value_list(mrp_decision_conf_t *conf,
+                                     const char *attr_name,
+                                     mrp_decision_attr_value_desc_t *buf,
+                                     size_t len);
+
+const char *mrp_decision_name(mrp_decision_conf_t *conf, int32_t decision);
+int32_t mrp_decision_value_max(mrp_decision_conf_t *conf);
+
+int32_t mrp_decision_get_integer_attr_value(mrp_decision_conf_t *conf,
+                                            const char *attr_name,
+                                            const char *value_name,
+                                            bool *error);
+const char *mrp_decision_get_integer_attr_name(mrp_decision_conf_t *conf,
+                                               const char *attr_name,
+                                               int32_t value,
+                                               bool *error);
+
+size_t mrp_decision_conf_print(mrp_decision_conf_t *conf,char *buf,size_t len);
+
+mrp_decision_node_t *mrp_decision_tree_create_from_file(
+                                               mrp_decision_conf_t *conf,
+                                               const char *stem);
+
+size_t mrp_decision_tree_print(mrp_decision_conf_t *conf,
+                               mrp_decision_node_t *node,
+                               char *buf, size_t len);
+
+#endif /* __MRP_C5_DECISION_TREE_H__ */
diff --git a/src/plugins/gam-resource-manager/decision-maker.c b/src/plugins/gam-resource-manager/decision-maker.c
new file mode 100644 (file)
index 0000000..10bd29a
--- /dev/null
@@ -0,0 +1,264 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "decision-maker.h"
+
+#define CONN_STATE_MASK                         \
+    (((decision_t)1 << CONN_STATE_BITS) - 1)
+
+#define KEY(_priority,_stamp)             \
+    ((_stamp) ? ((((_priority) & 0xff) << 8) |   \
+                 ((_stamp) & 0xff)            )  \
+              :  0)
+
+typedef uint32_t list_key_t;
+
+typedef struct {
+    list_key_t key;
+    entry_t entry;
+    state_t new_state;
+} list_t;
+
+
+static const char *conn_state_names[CONN_STATE_MAX] = {
+    [teardown]     = "teardown",
+    [disconnected] = "disconnected",
+    [connected]    = "connected",
+    [suspended]    = "suspended",
+};
+
+
+static uint32_t source_priority [SOURCE_MAX] = {
+    [wrtApplication] = 1,
+    [icoApplication] = 1,
+    [phoneSource]    = 3,
+    [radio]          = 1,
+    [microphone]     = 3,
+    [navigator]      = 2,
+};
+
+static bool mutually_exclusive[SOURCE_MAX][SOURCE_MAX] = {
+    [wrtApplication][icoApplication] = true,
+    [wrtApplication][radio]          = true,
+
+    [icoApplication][wrtApplication] = true,
+    [icoApplication][radio]          = true,
+
+    [radio][wrtApplication]          = true,
+    [radio][icoApplication]          = true,
+};
+
+static decision_t set_decision_for_source(source_t source, conn_state_t state)
+{
+    decision_t decision;
+
+    if (source < 0 || source >= SOURCE_MAX)
+        decision =  0;
+    else
+        decision = (((decision_t)state) & CONN_STATE_MASK) <<
+                                                (source * CONN_STATE_BITS);
+    return decision;
+}
+
+#if 0
+static conn_state_t get_decision_for_source(source_t source, decision_t decision)
+{
+    conn_state_t state;
+
+    if (source < 0 || source >= SOURCE_MAX)
+        state = 0;
+    else
+        state = (decision >> (source * CONN_STATE_BITS)) & CONN_STATE_MASK;
+
+    return state;
+}
+#endif
+
+static void sort_list(list_t *list)
+{
+    list_t tmp;
+    int i,j;
+
+    for (i = 0;  i < SOURCE_MAX-1; i++) {
+        for (j = i+1; j < SOURCE_MAX; j++) {
+            if (list[j].key > list[i].key) {
+                tmp = list[j];
+                list[j] = list[i];
+                list[i] = tmp;
+            }
+        }
+    }
+}
+
+
+static void build_sorted_list(entry_t *usecase, list_t *list)
+{
+    list_t *l;
+    stamp_t stamp;
+    uint32_t priority;
+    int i;
+
+    for (i = 0;  i < SOURCE_MAX;  i++) {
+        stamp = ENTRY_STAMP(usecase[i]);
+        priority = source_priority[i];
+
+        l = list + i;
+
+        l->key = KEY(priority, stamp);
+        l->entry = usecase[i];
+    }
+
+    sort_list(list);
+}
+
+static bool routing_conflict(list_t *list, int i)
+{
+    entry_t entry = list[i].entry;
+    entry_t e;
+    int j;
+
+    if (ENTRY_STAMP(entry) > 0) {
+        for (j = 0; j < i;  j++) {
+            e = ENTRY_CONNECTION(list[j].entry);
+
+            if (route_conflicts(entry, e))
+                return true;
+        }
+    }
+
+    return false;
+}
+
+static bool rule_conflict(list_t *list, int i)
+{
+    entry_t  entry  = list[i].entry;
+    stamp_t  stamp  = ENTRY_STAMP(entry);
+    conn_t   conn   = ENTRY_CONNECTION(entry);
+    source_t source = CONNECTION_SOURCE(conn);
+    entry_t  e;
+    conn_t   c;
+    source_t s;
+    int j;
+
+    if (stamp > 0) {
+        for (j = 0;  j < i;  j++) {
+            e = list[j].entry;
+            c = ENTRY_CONNECTION(e);
+            s = CONNECTION_SOURCE(c);
+
+            if (ENTRY_STAMP(e) > 0 && list[j].new_state == play) {
+                if (mutually_exclusive[source][s])
+                    return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+
+
+static conn_state_t determine_connection_state(list_t *list, int i)
+{
+    entry_t entry;
+    conn_t conn;
+    sink_t sink;
+    conn_state_t ct;
+
+    entry = list[i].entry;
+    ct = teardown;
+
+    if (ENTRY_STAMP(entry) > 0) {
+        conn  = ENTRY_CONNECTION(entry);
+        sink  = CONNECTION_SINK(conn);
+
+        if (sink == noroute)
+            ct = disconnected;
+        else if (routing_conflict(list, i))
+            ct = disconnected;
+        else if (rule_conflict(list, i))
+            ct = suspended;
+        else
+            ct = connected;
+
+    }
+
+    return ct;
+}
+
+
+decision_t make_decision(entry_t *usecase, source_t source, int max_active)
+{
+    decision_t decision;
+    list_t list[SOURCE_MAX];
+    entry_t entry;
+    conn_t conn;
+    source_t idx;
+    stamp_t stamp;
+    conn_state_t ct;
+    int active;
+    int i;
+
+    build_sorted_list(usecase, list);
+    decision = 0;
+    active = 0;
+
+    for (i = 0;  i < SOURCE_MAX;  i++) {
+        entry = list[i].entry;
+        stamp = ENTRY_STAMP(entry);
+        conn  = ENTRY_CONNECTION(entry);
+        idx   = CONNECTION_SOURCE(conn);
+
+        if (active >= max_active || !stamp)
+            ct = teardown;
+        else {
+            ct = determine_connection_state(list, i);
+            active++;
+        }
+
+        if (source == allSources)
+            decision |= set_decision_for_source(idx, ct);
+        else if (source == idx) {
+            decision = ct;
+            break;
+        }
+
+        list[i].new_state = (ct == connected) ? play : stop;
+    } /* for */
+
+#if 0
+    for (i=0; i<SOURCE_MAX; i++) {
+        entry = list[i].entry;
+        conn = ENTRY_CONNECTION(entry);
+        printf("[0x%x %d %s -> %s %s] ",
+               list[i].key,
+               ENTRY_STAMP(entry),
+               get_source_name(CONNECTION_SOURCE(conn)),
+               get_sink_name(CONNECTION_SINK(conn)),
+               ENTRY_STATE(entry) ? "play":"stop");
+    }
+    printf("=> %u\n", decision);
+#endif
+
+    return decision;
+}
+
+size_t print_decision(decision_t decision, source_t source, char *buf, size_t len)
+{
+    size_t l;
+
+    if (source == allSources)
+        l = snprintf(buf, len, "%u", decision);
+    else
+        l = snprintf(buf, len, "%s", get_conn_state_name(decision));
+
+    return l;
+}
+
+const char *get_conn_state_name(conn_state_t ct)
+{
+    if (ct < 0 || ct >= CONN_STATE_MAX)
+        return "<unknown>";
+    return conn_state_names[ct];
+}
diff --git a/src/plugins/gam-resource-manager/decision-maker.h b/src/plugins/gam-resource-manager/decision-maker.h
new file mode 100644 (file)
index 0000000..9175950
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MRP_DECISION_MAKER_H__
+#define __MRP_DECISION_MAKER_H__
+
+#include "pattern-generator.h"
+
+#define CONN_STATE_BITS 2
+
+typedef enum {
+    teardown = 0,               /* 0 */
+    disconnected,               /* 1 */
+    connected,                  /* 2 */
+    suspended,                  /* 3 */
+
+    CONN_STATE_MAX /* expected to be 4 */
+} conn_state_t;
+
+
+typedef uint32_t decision_t;
+
+decision_t make_decision(entry_t *usecase, source_t source, int max_active);
+size_t print_decision(decision_t decision, source_t source,
+                      char *buf, size_t len);
+
+const char *get_conn_state_name(conn_state_t ct);
+
+
+#endif /* __MRP_DECISION_MAKER_H__ */
diff --git a/src/plugins/gam-resource-manager/decision-test.c b/src/plugins/gam-resource-manager/decision-test.c
new file mode 100644 (file)
index 0000000..6a51507
--- /dev/null
@@ -0,0 +1,1112 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <ctype.h>
+
+#include <murphy/common.h>
+#include <breedline/breedline-murphy.h>
+
+#include "decision-tree.h"
+#include "c5-decision-tree.h"
+
+#define INVALID_OFFSET      (~(size_t)0)
+
+typedef struct {
+    int32_t route;
+    int32_t stamp;
+    int32_t state;
+} source_attr_t;
+
+typedef struct {
+    source_attr_t   wrtApplication;
+    source_attr_t   icoApplication;
+    source_attr_t   phone;
+    source_attr_t   radio;
+    source_attr_t   microphone;
+    source_attr_t   navigator;
+} usecase_vector_t;
+
+typedef struct {
+    const char *name;
+    size_t offset;
+} offset_def_t;
+
+typedef struct {
+    const char *name;
+    size_t stamp_offset;
+    size_t route_offset;
+    size_t state_offset;
+    mrp_decision_conf_t *conf;
+    mrp_decision_tree_t *tree;
+    struct {
+        int32_t value;
+        const char *str;
+        bool valid;
+        int32_t stamp;
+        int32_t state;
+    } decision;
+} source_t;
+
+typedef struct {
+    const char *prognam;
+    int max_active;
+    mrp_mainloop_t *ml;
+    brl_t *brl;
+    int nsource;
+    source_t *sources;
+    mrp_decision_conf_t *default_conf;
+    usecase_vector_t usecase;
+} user_data_t;
+
+
+#define OFFSET_DEF(_src,_attr,_fld)  \
+    { # _src #_attr, MRP_OFFSET(usecase_vector_t, _src._fld) }
+#define LIST_END                     \
+    { NULL, 0 }
+#define SOURCE(_src)                 \
+    OFFSET_DEF(_src, Route, route),  \
+    OFFSET_DEF(_src, Stamp, stamp),  \
+    OFFSET_DEF(_src, State, state)
+
+static offset_def_t offset_defs[] = {
+    SOURCE( wrtApplication ),
+    SOURCE( icoApplication ),
+    SOURCE( phone          ),
+    SOURCE( radio          ),
+    SOURCE( microphone     ),
+    SOURCE( navigator      ),
+    LIST_END
+};
+
+#undef SOURCE
+#undef LIST_END
+#undef OFFSET_DEF
+
+
+static bool whitespace(char **buf)
+{
+    char *p, *q, c;
+
+    for (p = q = *buf;   (c = *p);   p++) {
+        if (c != ' ' && c != '\t' && c != '\n')
+            break;
+    }
+
+    *buf = p;
+
+    return *p == 0 || p > q;
+}
+
+static bool identifier(char **buf, char *id, size_t len)
+{
+    char *p = *buf;
+    char *q, *e, c;
+
+    whitespace(&p);
+
+    if (!isalpha(*(q = p)))
+        return false;
+
+    while ((c = *p)) {
+        if (c == ' ' || c == '\t' || c == '\n')
+            break;
+        if (!isalnum(c) && c != '_' && c != '-')
+            return false;
+        p++;
+    }
+
+    if (p > q && (p-q) < ((int)len-1)) {
+        e = p;
+
+        if (whitespace(&p)) {
+            strncpy(id, q, (e-q));
+            id[(e-q)] = 0;
+
+            *buf = p;
+
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static bool integer(char **buf, int32_t *integer, char *string, size_t len)
+{
+    char *p = *buf;
+    char *e;
+    size_t l;
+
+    whitespace(&p);
+
+
+    *integer = strtol(p, &e, 10);
+
+    if (e > p) {
+        l = e - p;
+
+        if (whitespace(&e)) {
+            if (string && len > 0 && len >= l) {
+                strncpy(string, p, l);
+                string[l] = 0;
+            }
+
+            *buf = e;
+
+            return true;
+        }
+    }
+
+    return false;
+}
+
+
+static bool token(char **buf, const char *tkn)
+{
+    char *p = *buf;
+    size_t l = strlen(tkn);
+
+    whitespace(&p);
+
+    if (!strncmp(p, tkn, l)) {
+        p += l;
+        if (whitespace(&p)) {
+            *buf = p;
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static bool end_of_line(char **buf)
+{
+    char *p = *buf;
+
+    if (whitespace(&p) && *p == 0) {
+        *buf = p;
+        return true;
+    }
+
+    return false;
+}
+
+static source_t *find_source(user_data_t *ud, const char *srcnam)
+{
+    source_t *src;
+    int i;
+
+    for (i = 0;  i < ud->nsource;  i++) {
+        src = ud->sources + i;
+
+        if (!strcmp(srcnam, src->name))
+            return(src);
+    }
+
+    return NULL;
+}
+
+static size_t find_offset(user_data_t *ud, const char *attrnam)
+{
+    offset_def_t *d;
+
+    MRP_UNUSED(ud);
+
+    for (d = offset_defs;  (d->name);  d++) {
+        if (!strcmp(attrnam, d->name))
+            return d->offset;
+    }
+
+    return INVALID_OFFSET;
+}
+
+
+static void show_conf(user_data_t *ud, const char *srcnam)
+{
+    source_t *src;
+    char buf[32768];
+
+    brl_hide_prompt(ud->brl);
+
+    if (!(src = find_source(ud, srcnam)))
+        printf("don't know anything about '%s' source\n", srcnam);
+    else {
+        mrp_decision_conf_print(src->conf, buf, sizeof(buf));
+        printf("\n%s\n", buf);
+    }
+
+    brl_show_prompt(ud->brl);
+}
+
+static void show_tree(user_data_t *ud, const char *srcnam)
+{
+    source_t *src;
+    char buf[32768];
+
+    brl_hide_prompt(ud->brl);
+
+    if (!(src = find_source(ud, srcnam)))
+        printf("don't know anything about '%s' source\n", srcnam);
+    else {
+        mrp_decision_tree_print(src->conf, src->tree, buf, sizeof(buf));
+        printf("\n%s\n", buf);
+    }
+
+    brl_show_prompt(ud->brl);
+}
+
+static void show_usecase(user_data_t *ud)
+{
+    mrp_decision_conf_t *conf;
+    usecase_vector_t *usecase;
+    offset_def_t *d;
+    int32_t value;
+    const char *vnam;
+    char buf[256];
+
+    brl_hide_prompt(ud->brl);
+
+    if (!(conf = ud->default_conf))
+        printf("\ncan't find any valid source configuration\n\n");
+    else {
+        printf("\n");
+
+        usecase = &ud->usecase;
+
+        for (d = offset_defs;  (d->name);   d++) {
+            if ((d - offset_defs) && !((d - offset_defs) % 3))
+                printf("\n");
+
+            value = *(int32_t *)((char *)usecase + d->offset);
+            vnam = mrp_decision_get_integer_attr_name(conf, d->name,value, NULL);
+
+            snprintf(buf, sizeof(buf), "%s:", d->name);
+            printf("%-24s %d (%s)\n", buf, value, vnam);
+        }
+
+        printf("\n");
+    }
+
+    brl_show_prompt(ud->brl);
+}
+
+
+static void show_sources(user_data_t *ud)
+{
+    int i;
+
+    brl_hide_prompt(ud->brl);
+
+    printf("\n");
+
+    if (ud->nsource <= 0)
+        printf("<no source available>\n");
+    else {
+        for (i = 0;  i < ud->nsource;  i++)
+            printf("%s\n", ud->sources[i].name);
+    }
+
+    printf("\n");
+
+    brl_show_prompt(ud->brl);
+}
+
+
+static void show_help(user_data_t *ud)
+{
+    brl_hide_prompt(ud->brl);
+    printf("\nAvailable commands:\n"
+           "    show usecase\n"
+           "    show sources\n"
+           "    show <source> {conf|tree}\n"
+           "    set <source> {route|stamp|state} <value>\n"
+           "    set * {route|state} <value>\n"
+           "    decide\n"
+           "    shift stamps <value>\n"
+           "    reset stamps\n"
+           "    normalise\n"
+           "    apply\n"
+           "    exit\n\n");
+
+    brl_show_prompt(ud->brl);
+}
+
+static void show_cmd(user_data_t *ud, const char *cmd)
+{
+    char *p = (char *)cmd;
+    char srcnam[256];
+
+    if (whitespace(&p)) {
+        if (token       (&p, "usecase") &&
+            end_of_line (&p           )  )
+        {
+            show_usecase(ud);
+            return;
+        }
+        else if (token       (&p, "sources") &&
+                 end_of_line (&p           )  )
+        {
+            show_sources(ud);
+            return;
+        }
+        else if (identifier  (&p, srcnam, sizeof(srcnam))) {
+            if (token       (&p, "conf") &&
+                end_of_line (&p        )  )
+            {
+                show_conf(ud, srcnam);
+                return;
+            }
+            else if (token       (&p, "tree") &&
+                     end_of_line (&p        )  )
+            {
+                show_tree(ud, srcnam);
+                return;
+            }
+        }
+    }
+
+    brl_hide_prompt(ud->brl);
+    printf("\nSyntax error. syntax of 'show' command:\n"
+           "    show usecase\n"
+           "    show sources\n"
+           "    show <source> {conf|tree}\n\n");
+    brl_show_prompt(ud->brl);
+}
+
+
+static void set_usecase_attribute(user_data_t *ud, const char *srcnam,
+                                  const char *attrnam, const char *attrval)
+{
+    source_t *src;
+    int32_t value;
+    size_t offset;
+    char fullnam[256];
+    bool error;
+
+    brl_hide_prompt(ud->brl);
+
+    if (!(src = find_source(ud, srcnam)))
+        printf("don't know anything about '%s' source\n", srcnam);
+    else {
+        snprintf(fullnam, sizeof(fullnam), "%s%s", srcnam, attrnam);
+        value = mrp_decision_get_integer_attr_value(src->conf, fullnam,
+                                                    attrval, &error);
+        offset = find_offset(ud, fullnam);
+
+        if (error || offset == INVALID_OFFSET)
+            printf("invalid attribute '%s' or value '%s'\n", fullnam, attrval);
+        else
+            *(int32_t *)((char *)&ud->usecase + offset) = value;
+    }
+
+    brl_show_prompt(ud->brl);
+}
+
+static void set_cmd(user_data_t *ud, const char *cmd)
+{
+    char *p = (char *)cmd;
+    char srcnam[256];
+    char attrval[256];
+    int32_t stamp;
+    int i;
+
+
+    if (whitespace (&p)) {
+        if (token(&p, "*")) {
+            if (token       (&p, "route"                 ) &&
+                identifier  (&p, attrval, sizeof(attrval)) &&
+                end_of_line (&p                          )  )
+            {
+                for (i = 0;  i < ud->nsource;  i++) {
+                    set_usecase_attribute(ud, ud->sources[i].name,
+                                          "Route", attrval);
+                }
+                return;
+            }
+            if (token       (&p, "state"                 ) &&
+                identifier  (&p, attrval, sizeof(attrval)) &&
+                end_of_line (&p                          )  )
+            {
+                for (i = 0;  i < ud->nsource;  i++) {
+                    set_usecase_attribute(ud, ud->sources[i].name,
+                                          "State", attrval);
+                }
+                return;
+            }
+        }
+        if (identifier  (&p, srcnam,  sizeof(srcnam))) {
+            if (token       (&p, "route"                 ) &&
+                identifier  (&p, attrval, sizeof(attrval)) &&
+                end_of_line (&p                          )  )
+            {
+                set_usecase_attribute(ud, srcnam, "Route", attrval);
+                return;
+            }
+            if (token       (&p, "stamp"                         ) &&
+                integer     (&p, &stamp, attrval, sizeof(attrval)) &&
+                end_of_line (&p                                  )  )
+            {
+                set_usecase_attribute(ud, srcnam, "Stamp", attrval);
+                return;
+            }
+            if (token       (&p, "state"                 ) &&
+                identifier  (&p, attrval, sizeof(attrval)) &&
+                end_of_line (&p                          )  )
+            {
+                set_usecase_attribute(ud, srcnam, "State", attrval);
+                return;
+            }
+        }
+    }
+
+    brl_hide_prompt(ud->brl);
+    printf("\nSyntax error. syntax of 'set' command:\n"
+           "    set <source> {route|stamp|state} <value>\n"
+           "    set * {route|state} <value>\n\n");
+    brl_show_prompt(ud->brl);
+}
+
+
+static void decide(user_data_t *ud)
+{
+    mrp_decision_value_type_t type;
+    mrp_decision_value_t *decision;
+    source_t *src;
+    mrp_decision_conf_t *conf;
+    int32_t stop, pause, play;
+    size_t offs;
+    char buf[256];
+    int i;
+
+
+    for (i = 0;  i < ud->nsource;  i++) {
+        src  = ud->sources + i;
+        conf = src->conf;
+
+        snprintf(buf, sizeof(buf), "%sState", src->name);
+
+        stop  = mrp_decision_get_integer_attr_value(conf, buf, "stop",  NULL);
+        pause = mrp_decision_get_integer_attr_value(conf, buf, "pause", NULL);
+        play  = mrp_decision_get_integer_attr_value(conf, buf, "play",  NULL);
+
+        if (!mrp_decision_make(src->tree, &ud->usecase, &type, &decision)) {
+            src->decision.value = 0;
+            src->decision.str = "<failed>";
+            src->decision.valid = false;
+        }
+        else {
+            src->decision.value = decision->integer;
+            src->decision.str = mrp_decision_name(src->conf, decision->integer);
+            src->decision.valid = true;
+
+            offs = src->stamp_offset;
+            src->decision.stamp = *(int32_t *)((char *)&ud->usecase + offs);
+
+            if (!strcmp(src->decision.str, "disconnected"))
+                src->decision.state = stop;
+            else if (!strcmp(src->decision.str, "connected"))
+                src->decision.state = play;
+            else if (!strcmp(src->decision.str, "suspended"))
+                src->decision.state = pause;
+            else {
+                src->decision.stamp = 0;
+                src->decision.state = stop;
+            }
+        }
+    } /* for  */
+}
+
+static void decide_cmd(user_data_t *ud, const char *cmd)
+{
+    source_t *src;
+    bool stamp_error;
+    size_t ioffs, joffs;
+    int32_t istamp, jstamp, stamp_max;
+    uint32_t stamp_mask, full_mask;
+    int32_t new_stamp, new_state;
+    int32_t old_stamp, old_state;
+    char nambuf[256];
+    char valbuf[256];
+    char statebuf[256];
+    char stampbuf[256];
+    const char *old_name, *new_name;
+    int i,j;
+    bool ok;
+
+    MRP_UNUSED(cmd);
+
+    brl_hide_prompt(ud->brl);
+
+
+    if (ud->nsource < 1)
+        printf("\nthere are no valid sources\n\n");
+    else {
+        printf("\n");
+
+        stamp_error = false;
+        stamp_mask = 0;
+        stamp_max  = 0;
+
+        for (i = 0;    i < ud->nsource;    i++) {
+            ioffs = ud->sources[i].stamp_offset;
+            istamp = *(int32_t *)((char *)&ud->usecase + ioffs);
+
+            if (istamp > 0) {
+                if (istamp > stamp_max)
+                    stamp_max = istamp;
+
+                stamp_mask |= (uint32_t)1 << (istamp - 1);
+            }
+
+            for (j = i + 1;  j < ud->nsource;  j++) {
+                joffs = ud->sources[j].stamp_offset;
+                jstamp = *(int32_t *)((char *)&ud->usecase + joffs);
+
+                if (istamp > 0 && istamp == jstamp) {
+                    printf("stamp %d occurs multiple times\n", istamp);
+                    stamp_error = true;
+                }
+            }
+        }
+
+        if (stamp_max > 0) {
+            full_mask = ((uint32_t)1 << stamp_max) - 1;
+
+            if (stamp_mask != full_mask) {
+                printf("stamps are not continuous\n");
+                stamp_error = true;
+            }
+        }
+
+
+        if (!stamp_error) {
+            decide(ud);
+
+            for (i = 0;  i < ud->nsource;  i++) {
+                src = ud->sources + i;
+
+                old_stamp = *(int32_t*)((char*)&ud->usecase+src->stamp_offset);
+                old_state = *(int32_t*)((char*)&ud->usecase+src->state_offset);
+
+                new_state = src->decision.state;
+                new_stamp = src->decision.stamp;
+
+                statebuf[0] = 0;
+                stampbuf[0] = 0;
+
+                if (src->decision.valid && old_state != new_state) {
+                    snprintf(nambuf, sizeof(nambuf), "%sState", src->name);
+                    old_name = mrp_decision_get_integer_attr_name(src->conf,
+                                                                  nambuf,
+                                                                  old_state,
+                                                                  &ok);
+                    new_name = mrp_decision_get_integer_attr_name(src->conf,
+                                                                  nambuf,
+                                                                  new_state,
+                                                                  &ok);
+                    snprintf(statebuf, sizeof(statebuf), "%d (%s) => %d (%s)",
+                             old_state, old_name, new_state, new_name);
+                }
+
+                if (src->decision.valid && old_stamp != new_stamp) {
+                    snprintf(stampbuf, sizeof(stampbuf),"%d => %d",
+                             old_stamp, new_stamp);
+                }
+
+                snprintf(nambuf, sizeof(nambuf), "%s:",
+                         src->name);
+                snprintf(valbuf, sizeof(valbuf), "%d (%s)",
+                         src->decision.value, src->decision.str);
+
+                printf("%-24s %-18s %-24s %s\n",
+                       nambuf, valbuf, statebuf, stampbuf);
+            }
+        }
+
+        printf("\n");
+    }
+
+    brl_show_prompt(ud->brl);
+}
+
+
+static void shift_cmd(user_data_t *ud, const char *cmd)
+{
+    char *p = (char *)cmd;
+    int32_t shift;
+    size_t stamp_offset;
+    int32_t *stamp_ptr;
+    int32_t stamp;
+    int i;
+
+    if (ud->max_active < 2) {
+        brl_hide_prompt(ud->brl);
+        printf("\nshift is not allowed if maximum 1 stream can be active\n\n");
+        brl_show_prompt(ud->brl);
+    }
+    else {
+        if (whitespace  (&p                ) &&
+            token       (&p, "stamps"      ) &&
+            integer     (&p, &shift, NULL,0) &&
+            end_of_line (&p                )  )
+        {
+            if (shift  > -ud->max_active &&
+                shift !=  0              &&
+                shift  <  ud->max_active  )
+            {
+                for (i = 0;  i < ud->nsource;  i++) {
+                    stamp_offset = ud->sources[i].stamp_offset;
+                    stamp_ptr = (int32_t*)((char*)&ud->usecase + stamp_offset);
+
+                    if ((stamp = *stamp_ptr) > 0) {
+                        stamp += shift;
+
+                        if (stamp < 0 || stamp > ud->max_active)
+                            stamp = 0;
+
+                        *stamp_ptr = stamp;
+                    }
+                } /* for */
+
+                show_usecase(ud);
+
+                return;
+            }
+        }
+    }
+
+    brl_hide_prompt(ud->brl);
+    printf("\nSyntax error. syntax of 'shift' command:\n"
+           "    shift stamps <value>\n"
+           "where\n"
+           "    <value> should be in the range of [%-d:-1] or [1:%d]\n\n",
+           -(ud->max_active - 1), (ud->max_active - 1));
+    brl_show_prompt(ud->brl);
+}
+
+static void reset_cmd(user_data_t *ud, const char *cmd)
+{
+    char *p = (char *)cmd;
+    size_t stamp_offset;
+    int32_t *stamp_ptr;
+    int i;
+
+    if (whitespace  (&p                ) &&
+        token       (&p, "stamps"      ) &&
+        end_of_line (&p                )  )
+    {
+        for (i = 0;  i < ud->nsource;  i++) {
+            stamp_offset = ud->sources[i].stamp_offset;
+            stamp_ptr = (int32_t*)((char*)&ud->usecase + stamp_offset);
+
+            *stamp_ptr = 0;
+        }
+
+        show_usecase(ud);
+
+        return;
+    }
+
+    brl_hide_prompt(ud->brl);
+    printf("\nSyntax error. syntax of 'reset' command:\n"
+           "    reset stamps\n\n");
+    brl_show_prompt(ud->brl);
+}
+
+static void normalise(user_data_t *ud)
+{
+    source_t *src;
+    size_t route_offset, stamp_offset, state_offset;
+    int32_t *route_ptr, *stamp_ptr, *state_ptr;
+    int nptr;
+    int32_t *ptrs[ud->nsource];
+    int32_t *tmp;
+    int i, j;
+
+    for (nptr = 0, i = 0;  i < ud->nsource;  i++) {
+        src = ud->sources + i;
+
+        route_offset = src->route_offset;
+        stamp_offset = src->stamp_offset;
+        state_offset = src->state_offset;
+
+        route_ptr = (int32_t *)((char *)&ud->usecase + route_offset);
+        stamp_ptr = (int32_t *)((char *)&ud->usecase + stamp_offset);
+        state_ptr = (int32_t *)((char *)&ud->usecase + state_offset);
+
+        if (*stamp_ptr > 0)
+            ptrs[nptr++] = stamp_ptr;
+        else {
+            *route_ptr = 0;
+            *stamp_ptr = 0;
+            *state_ptr = 0;
+        }
+    }
+
+    for (i = 0;   i < nptr-1;   i++) {
+        for (j = i + 1;  j < nptr;  j++) {
+            if (*(ptrs[i]) > *(ptrs[j])) {
+                tmp = ptrs[i];
+                ptrs[i] = ptrs[j];
+                ptrs[j] = tmp;
+            }
+        }
+    }
+
+    for (i = 0;  i < nptr;  i++)
+        *(ptrs[i]) = i+1;
+}
+
+static void normalise_cmd(user_data_t *ud, const char *cmd)
+{
+    char *p = (char *)cmd;
+
+    if (end_of_line (&p)) {
+        normalise(ud);
+
+        show_usecase(ud);
+
+        return;
+    }
+
+    brl_hide_prompt(ud->brl);
+    printf("\nSyntax error. syntax of 'normalise' command:\n"
+           "    normalise\n\n");
+    brl_show_prompt(ud->brl);
+}
+
+static void apply_cmd(user_data_t *ud, const char *cmd)
+{
+    char *p = (char *)cmd;
+    source_t *src;
+    int32_t *stamp_ptr, *state_ptr;
+    int i, n;
+
+    if (end_of_line (&p)) {
+        for (n = i = 0;  i < ud->nsource;  i++) {
+            src = ud->sources + i;
+
+            if (src->decision.valid) {
+                src->decision.valid = false;
+
+                stamp_ptr = (int32_t*)((char*)&ud->usecase + src->stamp_offset);
+                state_ptr = (int32_t*)((char*)&ud->usecase + src->state_offset);
+
+                if (*stamp_ptr != src->decision.stamp) {
+                    *stamp_ptr = src->decision.stamp;
+                    n++;
+                }
+
+                if (*state_ptr != src->decision.state) {
+                    *state_ptr = src->decision.state;
+                    n++;
+                }
+            }
+        }
+
+        if (n == 0) {
+            brl_hide_prompt(ud->brl);
+            printf("\nnothing has been changed\n\n");
+            brl_show_prompt(ud->brl);
+        }
+        else {
+            normalise(ud);
+            show_usecase(ud);
+        }
+
+        return;
+    }
+
+    brl_hide_prompt(ud->brl);
+    printf("\nSyntax error. syntax of 'apply' command:\n"
+           "    apply\n\n");
+    brl_show_prompt(ud->brl);
+}
+
+static void input_handler(brl_t *brl, const char *input, void *user_data)
+{
+    user_data_t *ud = (user_data_t *)user_data;
+
+    MRP_UNUSED(brl);
+
+    brl_add_history(ud->brl, input);
+
+    if (input == NULL || !strcmp(input, "exit")) {
+        mrp_mainloop_quit(ud->ml, 0);
+        return;
+    }
+
+    if (strlen(input) == 0)
+        return;
+
+    if (!strcmp(input, "help")) {
+        show_help(ud);
+    }
+    else if (!strncmp(input, "set", 3)) {
+        set_cmd(ud, input+3);
+    }
+    else if (!strncmp(input, "show", 4)) {
+        show_cmd(ud, input+4);
+    }
+    else if (!strncmp(input, "decide", 6)) {
+        decide_cmd(ud, input+4);
+    }
+    else if (!strncmp(input, "shift", 5)) {
+        shift_cmd(ud, input+5);
+    }
+    else if (!strncmp(input, "reset", 5)) {
+        reset_cmd(ud, input+5);
+    }
+    else if (!strncmp(input, "normalise", 9)) {
+        normalise_cmd(ud, input+9);
+    }
+    else if (!strncmp(input, "apply", 5)) {
+        apply_cmd(ud, input+5);
+    }
+    else {
+        show_help(ud);
+    }
+}
+
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+    mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+
+    MRP_UNUSED(user_data);
+
+    switch (signum) {
+    case SIGINT:
+        printf("Got SIGINT\n");
+        if (ml != NULL)
+            mrp_mainloop_quit(ml, 0);
+        else
+            exit(0);
+        break;
+    }
+}
+
+static void input_create(user_data_t *ud, const char *prompt)
+{
+    if (!(ud->ml = mrp_mainloop_create())) {
+        printf("failed to create mainloop\n");
+        exit(1);
+    }
+
+    mrp_add_sighandler(ud->ml, SIGINT, signal_handler, NULL);
+
+    ud->brl = brl_create_with_murphy(fileno(stdin), prompt, ud->ml,
+                                     input_handler, ud);
+    if (!ud->brl) {
+        printf("failed to create breedline\n");
+        exit(1);
+    }
+
+    brl_show_prompt(ud->brl);
+}
+
+static void input_destroy(user_data_t *ud)
+{
+    if (ud) {
+        if (ud->brl)
+            brl_destroy(ud->brl);
+        if (ud->ml)
+            mrp_mainloop_destroy(ud->ml);
+    }
+}
+
+static void input_process(user_data_t *ud)
+{
+    if (ud && ud->ml)
+        mrp_mainloop_run(ud->ml);
+}
+
+static void sources_create(user_data_t *ud, const char *prefix)
+{
+    mrp_decision_conf_t *conf;
+    mrp_decision_tree_t *tree;
+    offset_def_t *d1, *d2;
+    source_t *src;
+    char srcnam[256];
+    size_t srcnamlen;
+    char stem[1024];
+    size_t offset;
+    char *p;
+    size_t dlen;
+    int idx;
+    size_t size;
+    int i;
+    int errcnt;
+
+    memset(srcnam, 0, sizeof(srcnam));
+
+    conf = NULL;
+    tree = NULL;
+
+    for (srcnamlen = 0, d1 = offset_defs;   (d1->name);    d1++) {
+
+        offset = d1->offset;
+
+        if ((p = strstr(d1->name, "Route"))) {
+            dlen = p - d1->name;
+
+            if (dlen >= sizeof(srcnam) - 1) {
+                printf("skipping source with too long name\n");
+                continue;
+            }
+
+            if (srcnamlen == dlen && !strncmp(srcnam, d1->name, dlen))
+                continue;
+
+            /* seems to be new source name */
+            srcnamlen = dlen;
+
+            strncpy(srcnam, d1->name, dlen);
+            srcnam[dlen] = 0;
+
+            if (prefix) {
+                snprintf(stem, sizeof(stem), "%s-%s-%d",
+                         prefix, srcnam, ud->max_active);
+            }
+            else {
+                snprintf(stem, sizeof(stem), "%s-%d",
+                         srcnam, ud->max_active);
+            }
+
+
+            if (!(conf = mrp_decision_conf_create_from_file(stem)))
+                goto failed;
+
+            for (errcnt = 0, d2 = offset_defs;  d2->name;  d2++) {
+                if (!mrp_decision_set_attr_offset(conf, d2->name, d2->offset)) {
+                    errcnt++;
+                    printf("failed to set offset of '%s' for source %s\n",
+                           d2->name, srcnam);
+                }
+            }
+
+            if (errcnt > 0)
+                goto failed;
+
+            if (!(tree = mrp_decision_tree_create_from_file(conf, NULL)))
+                goto failed;
+
+            idx = ud->nsource++;
+            size = sizeof(source_t) * ud->nsource;
+
+            if (!(ud->sources = mrp_realloc(ud->sources, size))) {
+                printf("failed to (re)allocate memory for %s source", srcnam);
+                ud->nsource = 0;
+                goto failed;
+            }
+
+            src = ud->sources + idx;
+
+            memset(src, 0, sizeof(source_t));
+            src->name = mrp_strdup(srcnam);
+            src->route_offset = offset;
+            src->conf = conf;
+            src->tree = tree;
+
+            if (!ud->default_conf)
+                ud->default_conf = conf;
+
+            conf = NULL;
+            tree = NULL;
+
+            continue;
+
+        failed:
+            if (tree) {
+                mrp_decision_tree_destroy(tree);
+                tree = NULL;
+            }
+            if (conf) {
+                mrp_decision_conf_destroy(conf);
+                conf = NULL;
+            }
+        } /* if Route */
+        else if ((p = strstr(d1->name, "Stamp"))) {
+            dlen = p - d1->name;
+
+            for (i = 0;  i < ud->nsource;  i++) {
+                src = ud->sources + i;
+                if (!strncmp(src->name, d1->name, dlen)) {
+                    src->stamp_offset = offset;
+                    break;
+                }
+            }
+        } /* if Stamp */
+        else if ((p = strstr(d1->name, "State"))) {
+            dlen = p - d1->name;
+
+            for (i = 0;  i < ud->nsource;  i++) {
+                src = ud->sources + i;
+                if (!strncmp(src->name, d1->name, dlen)) {
+                    src->state_offset = offset;
+                    break;
+                }
+            }
+        }
+    } /* for */
+}
+
+
+int main(int argc, char **argv)
+{
+    user_data_t ud;
+    const char *prefix;
+    int max_active;
+    const char *prompt;
+
+    MRP_UNUSED(argc);
+
+    prompt = "decision-tester";
+    prefix = "gam";
+    max_active = 4;
+
+    memset(&ud, 0, sizeof(ud));
+    ud.prognam = basename(argv[0]);
+    ud.max_active = max_active;
+
+    sources_create(&ud, prefix);
+    input_create(&ud, prompt);
+
+    input_process(&ud);
+
+    printf("Exiting now ...\n");
+
+    input_destroy(&ud);
+
+    return 0;
+}
diff --git a/src/plugins/gam-resource-manager/decision-tree.c b/src/plugins/gam-resource-manager/decision-tree.c
new file mode 100644 (file)
index 0000000..facf307
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <murphy/common.h>
+#include <breedline/breedline-murphy.h>
+
+#include "decision-tree.h"
+
+
+static mrp_decision_value_t *traverse_tree(mrp_decision_node_t *, void *);
+static bool test_condition(mrp_decision_branch_t *, void *);
+
+static void copy_value(mrp_decision_value_type_t, mrp_decision_value_t *,
+                       mrp_decision_value_t *);
+static void destroy_value(mrp_decision_value_type_t,
+                          mrp_decision_value_t *);
+
+
+mrp_decision_tree_t *mrp_decision_tree_create(const char *name,
+                                mrp_decision_value_type_t decision_value_type)
+{
+    mrp_decision_node_t *tree;
+
+    if (!name || decision_value_type < 0 ||
+        decision_value_type >= MRP_DECISION_VALUE_BASIC_TYPE_MAX)
+    {
+        return NULL;
+    }
+
+    if ((tree = mrp_allocz(sizeof(mrp_decision_root_node_t)))) {
+        tree->root.type = MRP_DECISION_ROOT_NODE;
+        tree->root.name = mrp_strdup(name);
+        tree->root.decision_value_type = decision_value_type;
+        tree->root.node = NULL;
+    }
+
+    return tree;
+}
+
+
+void mrp_decision_tree_destroy(mrp_decision_tree_t *node)
+{
+    mrp_decision_branch_t *branch;
+    size_t i;
+
+    if (node) {
+        switch (node->type) {
+
+        case MRP_DECISION_ROOT_NODE:
+            mrp_decision_tree_destroy(node->root.node);
+            mrp_free((void *)node->root.name);
+            break;
+
+        case MRP_DECISION_TEST_NODE:
+            for (i = 0;  i < node->test.nbranch;  i++) {
+                branch = node->test.branches + i;
+
+                destroy_value(branch->value_type, &branch->value);
+                mrp_decision_tree_destroy(branch->node);
+            }
+            mrp_free(node->test.branches);
+            break;
+
+        case MRP_DECISION_TERMINAL_NODE:
+            break;
+
+        default:
+            return;
+        }
+
+        mrp_free(node);
+    }
+}
+
+mrp_decision_node_t *
+mrp_decision_create_terminal_node(mrp_decision_value_t *decision)
+{
+    mrp_decision_node_t *node;
+
+    if (!decision)
+        return NULL;
+
+    if ((node = mrp_allocz(sizeof(mrp_decision_terminal_node_t)))) {
+        node->terminal.type = MRP_DECISION_TERMINAL_NODE;
+        node->terminal.decision = *decision;
+    }
+
+    return node;
+}
+
+
+mrp_decision_node_t *mrp_decision_create_test_node(void)
+{
+    mrp_decision_node_t *node;
+
+    if ((node = mrp_allocz(sizeof(mrp_decision_test_node_t)))) {
+        node->terminal.type = MRP_DECISION_TEST_NODE;
+    }
+
+    return node;
+}
+
+bool mrp_decision_add_node_to_root(mrp_decision_node_t *root,
+                                   mrp_decision_node_t *node)
+{
+    if (!root || !node)
+        return false;
+
+    if (root->type != MRP_DECISION_ROOT_NODE || root->root.node)
+        return false;
+
+    root->root.node = node;
+
+    return true;
+}
+
+bool mrp_decision_add_branch_to_test_node(mrp_decision_node_t *test_node,
+                                          mrp_decision_condition_t condition,
+                                          int value_id,
+                                          mrp_decision_value_type_t value_type,
+                                          mrp_decision_value_t *value,
+                                          size_t offset,
+                                          mrp_decision_node_t *node)
+{
+    mrp_decision_branch_t *branch;
+    size_t size;
+    size_t i;
+
+    if (!test_node || test_node->type != MRP_DECISION_TEST_NODE ||
+        !value || !node)
+    {
+        return false;
+    }
+
+    i = test_node->test.nbranch++;
+    size = sizeof(mrp_decision_branch_t) * test_node->test.nbranch;
+    test_node->test.branches = mrp_realloc(test_node->test.branches, size);
+
+    branch = test_node->test.branches + i;
+    memset(branch, 0, sizeof(mrp_decision_branch_t));
+
+    branch->condition = condition;
+    branch->value_id = value_id;
+    branch->value_type = value_type;
+    branch->offset = offset;
+    branch->node = node;
+
+    copy_value(value_type, value, &branch->value);
+
+    return true;
+}
+
+
+
+bool mrp_decision_make(mrp_decision_tree_t *node, void *input,
+                       mrp_decision_value_type_t *decision_value_type,
+                       mrp_decision_value_t **decision_value)
+{
+    if (!node || node->type != MRP_DECISION_ROOT_NODE ||
+        !input || !decision_value)
+    {
+        return false;
+    }
+
+    if (decision_value_type)
+        *decision_value_type = MRP_DECISION_VALUE_UNKNOWN;
+
+    if ((*decision_value = traverse_tree(node->root.node, input))) {
+        if (decision_value_type)
+            *decision_value_type = node->root.decision_value_type;
+
+        return true;
+    }
+
+    return false;
+}
+
+
+const char *mrp_decision_value_type_str(mrp_decision_value_type_t type)
+{
+    switch (type) {
+    case MRP_DECISION_VALUE_INTEGER:
+        return "integer";
+    case MRP_DECISION_VALUE_UNSIGND:
+        return "unsigned";
+    case MRP_DECISION_VALUE_FLOATING:
+        return "floating";
+    case MRP_DECISION_VALUE_STRING:
+        return "string";
+    case MRP_DECISION_VALUE_BITMASK:
+        return "bitmask";
+    case MRP_DECISION_ARRAY_INTEGER:
+        return "integer array";
+    case MRP_DECISION_ARRAY_UNSIGND:
+        return "unsigned array";
+    case MRP_DECISION_ARRAY_FLOATING:
+        return "floating array";
+    case MRP_DECISION_ARRAY_STRING:
+        return "string array";
+    default:
+        return "<unknown value type>";
+    }
+}
+
+const char *mrp_decision_condition_str(mrp_decision_condition_t cond)
+{
+    switch (cond) {
+    case MRP_DECISION_GT:   return ">";
+    case MRP_DECISION_GE:   return ">=";
+    case MRP_DECISION_EQ:   return "=";
+    case MRP_DECISION_LE:   return "<=";
+    case MRP_DECISION_LT:   return "<";
+    case MRP_DECISION_IN:   return "in";
+    default:                return "<unknown condition>";
+    }
+}
+
+size_t mrp_decision_value_print(mrp_decision_value_type_t type,
+                                mrp_decision_value_t *value,
+                                char *buf, size_t len)
+{
+#define ELLIPSIS        " ... "
+#define PRINT(args...)  do {if (p<h) p += snprintf(p, h-p, args);    } while(0)
+#define PRINT_ELLIPSIS  do {if (p<e) p += snprintf(p, e-p, ELLIPSIS);} while(0)
+#define PRINT_LISTSTART do {if (p<e) p += snprintf(p, e-p, "[");     } while(0)
+#define PRINT_LISTEND   do {if (p<e) p += snprintf(p, e-p, "]");     } while(0)
+
+
+    mrp_decision_value_type_t basic_type;
+    mrp_decision_value_t *v;
+    char *p, *e, *h;
+    size_t i, n;
+
+    if (len < sizeof(ELLIPSIS) + 3)
+        return 0;
+
+    h = (e = (p = buf) + len) - (sizeof(ELLIPSIS) + 1);
+
+    if (!(type & MRP_DECISION_ARRAY)) {
+        switch (type) {
+        case MRP_DECISION_VALUE_INTEGER:  PRINT("%d",  value->integer ); break;
+        case MRP_DECISION_VALUE_UNSIGND:  PRINT("%u",  value->unsignd ); break;
+        case MRP_DECISION_VALUE_FLOATING: PRINT("%lf", value->floating); break;
+        case MRP_DECISION_VALUE_STRING:   PRINT("'%s'",value->string  ); break;
+        case MRP_DECISION_VALUE_BITMASK:  PRINT("0x%x",value->bitmask ); break;
+        default:                          PRINT("<unknown value type>"); break;
+        }
+    }
+    else {
+        basic_type = MRP_DECISION_VALUE_BASIC(type);
+        n = value->array.size;
+        v = value->array.values;
+
+        PRINT_LISTSTART;
+
+        for (i = 0;   i < n;   i++) {
+            if (p >= h) {
+                PRINT_ELLIPSIS;
+                break;
+            }
+
+            if (i > 0)
+                PRINT(", ");
+
+            p += mrp_decision_value_print(basic_type, v+i, p, e-p);
+        }
+
+        PRINT_LISTEND;
+    }
+
+    return p - buf;
+
+#undef PRINT_LISTEND
+#undef PRINT_LISTSTART
+#undef PRINT_ELLIPSIS
+#undef PRINT
+#undef ELLIPSIS
+}
+
+
+static mrp_decision_value_t *traverse_tree(mrp_decision_node_t *node,
+                                           void *input)
+{
+    mrp_decision_branch_t *branch;
+    size_t i, n;
+
+    if (node && input) {
+        switch (node->type) {
+
+        case MRP_DECISION_TERMINAL_NODE:
+            return &node->terminal.decision;
+            break;
+
+        case MRP_DECISION_TEST_NODE:
+            for (i = 0, n = node->test.nbranch, branch = node->test.branches;
+                 i < n;
+                 i++, branch++)
+            {
+                if (test_condition(branch, input))
+                    return traverse_tree(branch->node, input);
+            }
+            return NULL;
+
+        default:
+            return NULL;
+        }
+    }
+
+    return NULL;
+}
+
+
+static bool test_condition(mrp_decision_branch_t *branch, void *input)
+{
+#define INPUT_VALUE(_t) (*(_t *)(((char *)input) + branch->offset))
+
+    uint32_t bitidx;
+    mrp_decision_bitmask_t bit;
+    mrp_decision_value_t *value;
+    int32_t integer;
+    uint32_t unsignd;
+    double floating;
+    const char *string;
+    size_t i;
+
+    if (!branch || !input)
+        return false;
+
+    switch (branch->condition) {
+
+    case MRP_DECISION_GT:
+        switch (branch->value_type) {
+        case MRP_DECISION_VALUE_INTEGER:
+            return INPUT_VALUE(int32_t) > branch->value.integer;
+        case MRP_DECISION_VALUE_UNSIGND:
+            return INPUT_VALUE(uint32_t) > branch->value.unsignd;
+        case MRP_DECISION_VALUE_FLOATING:
+            return INPUT_VALUE(double) > branch->value.floating;
+        default:
+            return false;
+        }
+
+    case MRP_DECISION_GE:
+        switch (branch->value_type) {
+        case MRP_DECISION_VALUE_INTEGER:
+            return INPUT_VALUE(int32_t) >= branch->value.integer;
+        case MRP_DECISION_VALUE_UNSIGND:
+            return INPUT_VALUE(uint32_t) >= branch->value.unsignd;
+        case MRP_DECISION_VALUE_FLOATING:
+            return INPUT_VALUE(double) >= branch->value.floating;
+        default:
+            return false;
+        }
+
+    case MRP_DECISION_EQ:
+        switch (branch->value_type) {
+        case MRP_DECISION_VALUE_INTEGER:
+            return INPUT_VALUE(int32_t) == branch->value.integer;
+        case MRP_DECISION_VALUE_UNSIGND:
+            return INPUT_VALUE(uint32_t) == branch->value.unsignd;
+        case MRP_DECISION_VALUE_FLOATING:
+            return INPUT_VALUE(double) == branch->value.floating;
+        case MRP_DECISION_VALUE_STRING:
+            return strcmp(INPUT_VALUE(char *), branch->value.string);
+        default:
+            return false;
+        }
+
+    case MRP_DECISION_LE:
+        switch (branch->value_type) {
+        case MRP_DECISION_VALUE_INTEGER:
+            return INPUT_VALUE(int32_t) <= branch->value.integer;
+        case MRP_DECISION_VALUE_UNSIGND:
+            return INPUT_VALUE(uint32_t) <= branch->value.unsignd;
+        case MRP_DECISION_VALUE_FLOATING:
+            return INPUT_VALUE(double) <= branch->value.floating;
+        default:
+            return false;
+        }
+
+    case MRP_DECISION_LT:
+        switch (branch->value_type) {
+        case MRP_DECISION_VALUE_INTEGER:
+            return INPUT_VALUE(int32_t) < branch->value.integer;
+        case MRP_DECISION_VALUE_UNSIGND:
+            return INPUT_VALUE(uint32_t) < branch->value.unsignd;
+        case MRP_DECISION_VALUE_FLOATING:
+            return INPUT_VALUE(double) < branch->value.floating;
+        default:
+            return false;
+        }
+
+    case MRP_DECISION_IN:
+        if ((branch->value_type & MRP_DECISION_ARRAY)) {
+
+            integer = 0;
+            unsignd = 0;
+            floating = 0;
+            string = "";
+
+            switch ((branch->value_type & 0xff)) {
+            case MRP_DECISION_VALUE_INTEGER:
+                integer = INPUT_VALUE(int32_t);
+                break;
+            case MRP_DECISION_VALUE_UNSIGND:
+                unsignd = INPUT_VALUE(uint32_t);
+                break;
+            case MRP_DECISION_VALUE_FLOATING:
+                floating = INPUT_VALUE(double);
+                break;
+            case MRP_DECISION_VALUE_STRING:
+                string = INPUT_VALUE(char *);
+                break;
+            default:
+                return false;
+            }
+            for (i = 0;    i < branch->value.array.size;    i++) {
+                value = branch->value.array.values + i;
+                switch ((branch->value_type & 0xff)) {
+                case MRP_DECISION_VALUE_INTEGER:
+                    if (integer == value->integer)
+                        return true;
+                    break;
+                case MRP_DECISION_VALUE_UNSIGND:
+                    if (unsignd == value->unsignd)
+                        return true;
+                    break;
+                case MRP_DECISION_VALUE_FLOATING:
+                    if (floating == value->floating)
+                        return true;
+                    break;
+                case MRP_DECISION_VALUE_STRING:
+                    if (!strcmp(string, value->string))
+                        return true;
+                    break;
+                default:
+                    return false;
+                }
+            } /* for i */
+            return false;
+        }
+        else {
+            if (branch->value_type == MRP_DECISION_VALUE_BITMASK) {
+                bitidx = INPUT_VALUE(int32_t);
+
+                if (bitidx >= MRP_DECISION_BITMASK_WIDTH)
+                    return false;
+
+                bit = MRP_DECISION_BIT(bitidx);
+
+                return (bit & branch->value.bitmask) ? true : false;
+            }
+        }
+        return false;
+    }
+
+    return false;
+}
+
+static void copy_value(mrp_decision_value_type_t type,
+                       mrp_decision_value_t *src,
+                       mrp_decision_value_t *dst)
+{
+    mrp_decision_value_type_t basic_type;
+    mrp_decision_value_t *src_values;
+    mrp_decision_value_t *dst_values;
+    size_t i, n;
+
+    if (!(type & MRP_DECISION_ARRAY)) {
+        if (type == MRP_DECISION_VALUE_STRING)
+            dst->string = mrp_strdup(src->string);
+        else
+            *dst = *src;
+    }
+    else {
+        n = src->array.size;
+
+        dst->array.size = n;
+        dst->array.values = mrp_allocz(sizeof(dst->array.values[0]) * n);
+
+        basic_type = MRP_DECISION_VALUE_BASIC(type);
+        src_values = src->array.values;
+        dst_values = dst->array.values;
+
+        for (i = 0;  i < n;  i++)
+            copy_value(basic_type, src_values + i, dst_values + i);
+    }
+}
+
+
+static void destroy_value(mrp_decision_value_type_t type,
+                          mrp_decision_value_t *value)
+{
+    mrp_decision_value_type_t basic_type;
+    size_t i;
+
+    if (!(type & MRP_DECISION_ARRAY)) {
+        if (type == MRP_DECISION_VALUE_STRING)
+            mrp_free((void *)value->string);
+    }
+    else {
+        basic_type = MRP_DECISION_VALUE_BASIC(type);
+
+        for (i = 0;   i < value->array.size;   i++)
+            destroy_value(basic_type, value->array.values + i);
+    }
+}
diff --git a/src/plugins/gam-resource-manager/decision-tree.h b/src/plugins/gam-resource-manager/decision-tree.h
new file mode 100644 (file)
index 0000000..dac13ec
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MRP_DECISION_TREE_H__
+#define __MRP_DECISION_TREE_H__
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <murphy/common/macros.h>
+
+#define MRP_DECISION_BITMASK_WIDTH    (sizeof(mrp_decision_bitmask_t) * 8)
+#define MRP_DECISION_BIT(b)           ((mrp_decision_bitmask_t)1 << (b))
+#define MRP_DECISION_VALUE_BASIC(v)   ((v) & 0xff)
+
+typedef enum mrp_decision_value_type_e       mrp_decision_value_type_t;
+typedef enum mrp_decision_node_type_e        mrp_decision_node_type_t;
+typedef enum mrp_decision_condition_e        mrp_decision_condition_t;
+
+typedef union  mrp_decision_value_u          mrp_decision_value_t;
+typedef struct mrp_decision_branch_s         mrp_decision_branch_t;
+typedef struct mrp_decision_root_node_s      mrp_decision_root_node_t;
+typedef struct mrp_decision_test_node_s      mrp_decision_test_node_t;
+typedef struct mrp_decision_terminal_node_s  mrp_decision_terminal_node_t;
+typedef union  mrp_decision_node_u           mrp_decision_node_t;
+typedef union  mrp_decision_node_u           mrp_decision_tree_t;
+
+typedef uint32_t mrp_decision_bitmask_t;
+
+enum mrp_decision_value_type_e {
+    MRP_DECISION_VALUE_UNKNOWN = -1,
+    MRP_DECISION_VALUE_INTEGER = 0,
+    MRP_DECISION_VALUE_UNSIGND,
+    MRP_DECISION_VALUE_FLOATING,
+    MRP_DECISION_VALUE_STRING,
+    MRP_DECISION_VALUE_BASIC_TYPE_MAX,
+
+    MRP_DECISION_VALUE_BITMASK = MRP_DECISION_VALUE_BASIC_TYPE_MAX,
+
+    MRP_DECISION_ARRAY = 0x100,
+
+    MRP_DECISION_ARRAY_INTEGER  = (MRP_DECISION_ARRAY |
+                                   MRP_DECISION_VALUE_INTEGER),
+    MRP_DECISION_ARRAY_UNSIGND  = (MRP_DECISION_ARRAY |
+                                   MRP_DECISION_VALUE_UNSIGND),
+    MRP_DECISION_ARRAY_FLOATING = (MRP_DECISION_ARRAY |
+                                   MRP_DECISION_VALUE_FLOATING),
+    MRP_DECISION_ARRAY_STRING   = (MRP_DECISION_ARRAY |
+                                   MRP_DECISION_VALUE_STRING),
+};
+
+union mrp_decision_value_u {
+    int32_t integer;
+    uint32_t unsignd;
+    double floating;
+    mrp_decision_bitmask_t bitmask;
+    const char *string;
+    struct {
+        size_t size;
+        mrp_decision_value_t *values;
+    } array;
+};
+
+enum mrp_decision_condition_e {
+    MRP_DECISION_GT,
+    MRP_DECISION_GE,
+    MRP_DECISION_LE,
+    MRP_DECISION_EQ,
+    MRP_DECISION_LT,
+    MRP_DECISION_IN,
+};
+
+struct mrp_decision_branch_s {
+    mrp_decision_condition_t condition;
+    int value_id;
+    mrp_decision_value_type_t value_type;
+    mrp_decision_value_t value;
+    size_t offset;
+    mrp_decision_node_t *node;
+};
+
+enum mrp_decision_node_type_e {
+    MRP_DECISION_UNKNOWN_NODE = 0,
+    MRP_DECISION_ROOT_NODE,
+    MRP_DECISION_TEST_NODE,
+    MRP_DECISION_TERMINAL_NODE
+};
+
+struct mrp_decision_root_node_s {
+    mrp_decision_node_type_t type;
+    const char *name;
+    mrp_decision_value_type_t decision_value_type;
+    mrp_decision_node_t *node;
+};
+
+struct mrp_decision_test_node_s {
+    mrp_decision_node_type_t type;
+    size_t nbranch;
+    mrp_decision_branch_t *branches;
+};
+
+struct mrp_decision_terminal_node_s {
+    mrp_decision_node_type_t type;
+    mrp_decision_value_t decision;
+};
+
+union mrp_decision_node_u {
+    mrp_decision_node_type_t type;
+    mrp_decision_root_node_t root;
+    mrp_decision_test_node_t test;
+    mrp_decision_terminal_node_t terminal;
+};
+
+
+mrp_decision_tree_t *mrp_decision_tree_create(const char *name,
+                                mrp_decision_value_type_t decision_value_type);
+void mrp_decision_tree_destroy(mrp_decision_tree_t *tree);
+
+mrp_decision_node_t *mrp_decision_create_terminal_node(
+                                   mrp_decision_value_t *decision);
+mrp_decision_node_t *mrp_decision_create_test_node(void);
+
+bool mrp_decision_add_node_to_root(mrp_decision_tree_t *tree,
+                                   mrp_decision_node_t *node);
+bool mrp_decision_add_branch_to_test_node(mrp_decision_node_t *test_node,
+                                          mrp_decision_condition_t condition,
+                                          int value_id,
+                                          mrp_decision_value_type_t value_type,
+                                          mrp_decision_value_t *value,
+                                          size_t offset,
+                                          mrp_decision_node_t *node);
+
+
+bool mrp_decision_make(mrp_decision_tree_t *tree, void *input,
+                       mrp_decision_value_type_t *decision_value_type,
+                       mrp_decision_value_t **decision_value);
+
+
+const char *mrp_decision_value_type_str(mrp_decision_value_type_t type);
+const char *mrp_decision_condition_str(mrp_decision_condition_t cond);
+
+size_t mrp_decision_value_print(mrp_decision_value_type_t type,
+                                mrp_decision_value_t *value,
+                                char *buf, size_t len);
+
+
+#endif  /* __MRP_DECISION_TREE_H__ */
diff --git a/src/plugins/gam-resource-manager/pattern-generator.c b/src/plugins/gam-resource-manager/pattern-generator.c
new file mode 100644 (file)
index 0000000..733d767
--- /dev/null
@@ -0,0 +1,706 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <libgen.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "pattern-generator.h"
+#include "decision-maker.h"
+
+#define CONNECTION(_source, _sink)  \
+    (((conn_t)((_source) & 0xff) << 8) | ((conn_t)((_sink) & 0xff)))
+
+#define INVALID_ENTRY (~(entry_t)0)
+#define ENTRY(_source, _sink, _state, _stamp) \
+    (((entry_t)(CONNECTION(_source, _sink) << 16)) |  \
+     (((entry_t)(_stamp) & 0xff) << 8) | \
+     (((entry_t)(_state)) & 0x0f))
+
+
+typedef struct {
+        sink_t sink;
+        conn_t conflict[SOURCE_MAX + 1];
+} conn_def_t [SINK_MAX];
+
+typedef struct {
+    int bits;
+    int8_t bitseq[SOURCE_MAX];
+} stamp_pattern_t;
+
+typedef struct {
+    int pattern;
+    int active;
+    uint32_t sequence;
+} index_t;
+
+
+static const char *source_names[SOURCE_MAX] = {
+    "wrtApplication",
+    "icoApplication",
+    "phone",
+    "radio",
+    "microphone",
+    "navigator",
+};
+
+static const char *sink_names[SINK_MAX] = {
+    "noroute",
+    "phone",
+    "btHeadset",
+    "usbHeadset",
+    "speakers",
+    "wiredHeadset",
+    "voiceRecognition",
+};
+
+static const char *state_names[STATE_MAX] = {
+    "stop",
+    "pause",
+    "play",
+};
+
+
+static conn_def_t conn_defs[SOURCE_MAX] = {
+    [wrtApplication]       = {
+        [noroute]          = { noroute,           {} },
+        [btHeadset]        = { btHeadset,         {} },
+        [usbHeadset]       = { usbHeadset,        {} },
+        [speakers]         = { speakers,          { CONNECTION(wrtApplication, wiredHeadset) }},
+        [wiredHeadset]     = { wiredHeadset,      { CONNECTION(wrtApplication, speakers)     }},
+    },
+    [icoApplication] = {
+        [noroute]          = { noroute,           {} },
+        [btHeadset]        = { btHeadset,         {} },
+        [usbHeadset]       = { usbHeadset,        {} },
+        [speakers]         = { speakers,          { CONNECTION(icoApplication, wiredHeadset),
+                                                    CONNECTION(phoneSource, speakers),
+                                                    CONNECTION(phoneSource, wiredHeadset) }},
+        [wiredHeadset]     = { wiredHeadset,      { CONNECTION(icoApplication, speakers),
+                                                    CONNECTION(phoneSource, speakers),
+                                                    CONNECTION(phoneSource, wiredHeadset) }},
+    },
+    [phoneSource]    = {
+        [noroute]          = { noroute,           {} },
+        [btHeadset]        = { btHeadset,         { CONNECTION(phoneSource, usbHeadset),
+                                                    CONNECTION(phoneSource, speakers),
+                                                    CONNECTION(phoneSource, wiredHeadset) }},
+        [usbHeadset]       = { usbHeadset,        { CONNECTION(phoneSource, btHeadset),
+                                                    CONNECTION(phoneSource, speakers),
+                                                    CONNECTION(phoneSource, wiredHeadset) }},
+        [speakers]         = { speakers,          { CONNECTION(phoneSource, btHeadset),
+                                                    CONNECTION(phoneSource, usbHeadset),
+                                                    CONNECTION(icoApplication, speakers),
+                                                    CONNECTION(icoApplication, wiredHeadset),
+                                                    CONNECTION(phoneSource, wiredHeadset) }},
+        [wiredHeadset]     = { wiredHeadset,      { CONNECTION(phoneSource, btHeadset),
+                                                    CONNECTION(phoneSource, usbHeadset),
+                                                    CONNECTION(icoApplication, speakers),
+                                                    CONNECTION(icoApplication, wiredHeadset),
+                                                    CONNECTION(phoneSource, speakers) }},
+    },
+    [radio]          = {
+        [noroute]          = { noroute,          {} },
+        [btHeadset]        = { btHeadset,        { CONNECTION(radio, usbHeadset) }},
+        [usbHeadset]       = { usbHeadset,       { CONNECTION(radio, btHeadset) }},
+        [speakers]         = { speakers,         {} },
+        [wiredHeadset]     = { wiredHeadset,     {} },
+    },
+    [microphone]     = {
+        [noroute]          = { noroute,          {} },
+        [phoneSink]        = { phoneSink,        { CONNECTION(microphone, voiceRecognition) }},
+        [voiceRecognition] = { voiceRecognition, { CONNECTION(microphone, phoneSink) }}
+    },
+    [navigator]      = {
+        [noroute]          = { noroute,          {} },
+        [speakers]         = { speakers,         { CONNECTION(navigator, wiredHeadset) }},
+        [wiredHeadset]     = { wiredHeadset,     { CONNECTION(navigator, speakers) }}
+    },
+};
+
+static uint32_t factorial[9] = { 0, 1, 2, 6, 24, 120, 720, 5040, 40320 };
+
+static int npattern[SOURCE_MAX+1];
+static stamp_pattern_t patterns[SOURCE_MAX+1][1024];
+static int nsequence[SOURCE_MAX+1];
+static uint8_t *sequences[SOURCE_MAX+1];
+static int nindex[SOURCE_MAX+1];
+static index_t indices[SOURCE_MAX+1][100000];
+
+#define DECISION_SPACE_DIMENSION (1 << (SOURCE_MAX * CONN_STATE_BITS))
+static bool decisions[DECISION_SPACE_DIMENSION];
+static uint32_t pattern_counter;
+static uint32_t decision_counter;
+
+static FILE *data;
+static FILE *names;
+
+
+static sink_t next_sink(source_t source, sink_t sink)
+{
+    while (++sink < SINK_MAX) {
+        if (conn_defs[source][sink].sink == sink) {
+            break;
+        }
+    }
+    return sink;
+}
+
+static bool next_entry(entry_t *usecase, source_t source, stamp_t stamp, entry_t *cursor)
+{
+    entry_t entry, new_entry, new_cursor;
+    conn_t conn;
+    sink_t sink;
+    state_t state;
+
+    if (source < 0 || source >= SOURCE_MAX)
+        return false;
+
+    if ((entry = *cursor) == INVALID_ENTRY)
+        return false;
+
+    if (stamp == 0) {
+        new_entry = ENTRY(source, noroute, stop, 0);
+        new_cursor = INVALID_ENTRY;
+    }
+    else {
+        conn = ENTRY_CONNECTION(entry);
+        state = ENTRY_STATE(entry);
+        sink = CONNECTION_SINK(conn);
+
+        for (;;) {
+            if (state >= STATE_MAX) {
+                *cursor = INVALID_ENTRY;
+                return false;
+            }
+
+            if (sink >= SINK_MAX) {
+                state++;
+                sink = 0;
+                continue;
+            }
+
+            if (sink == noroute && state != stop) {
+                sink = next_sink(source, sink);
+                continue;
+            }
+
+            new_entry = ENTRY(source, sink, state, stamp);
+
+            sink = next_sink(source, sink);
+            new_cursor = ENTRY(source, sink, state, stamp);
+
+            break;
+        }
+    }
+
+    *cursor = new_cursor;
+    usecase[source] = new_entry;
+
+    return true;
+}
+
+
+static void populate_active_patterns(void)
+{
+#define BIT(b) ((int)1 << (b))
+
+    stamp_pattern_t *p;
+    int n;
+    int i,j,k;
+    int cnt;
+    int8_t bitseq[SOURCE_MAX];
+
+    for (i = 1;  i < SOURCE_MAX;  i++) {
+        for (n = 0, k = 0;  n < BIT(SOURCE_MAX); n++) {
+            memset(bitseq, -1, sizeof(bitseq));
+            for (j = 0, cnt = 0;  j < SOURCE_MAX;  j++) {
+                if (n & BIT(j))
+                    bitseq[j] = cnt++;
+            }
+            if (cnt <= i) {
+                p = &patterns[i][k++];
+                p->bits = cnt;
+                memcpy(p->bitseq, bitseq, sizeof(bitseq));
+            }
+        }
+        npattern[i] = k;
+    }
+
+#undef BIT
+}
+
+static void populate_sequences(void)
+{
+    static uint32_t max[9] = { 0, 1, 4, 27, 256, 3125, 46656, 823543, 16777216 };
+    static uint8_t seqs[(SOURCE_MAX +1) * (40320 *SOURCE_MAX)];
+
+    uint8_t *sp;
+    uint32_t i, j, k, n, v, s;
+    bool valid;
+
+    nsequence[0] = 1;
+    sequences[0] = seqs;
+
+    for (i = 1, sp = seqs + 1;  i <= SOURCE_MAX;  i++) {
+        sequences[i] = sp;
+
+        for (n = 0;  n < max[i];  n++) {
+            for (j = 0, v = n, valid = true;    j < i && valid;   j++, v /= i) {
+                sp[j] = s = (v % i) + 1;
+                for (k = 0;  k < j;  k++) {
+                    if (s == sp[k]) {
+                        valid = false;
+                        break;
+                    }
+                }
+            }
+            if (valid)
+                sp += (SOURCE_MAX + 1);
+        }
+
+        nsequence[i] = (sp - sequences[i]) / (SOURCE_MAX + 1);
+    }
+}
+
+static uint32_t seqno_max(int max_active)
+{
+    if (max_active > 8 || max_active > SOURCE_MAX)
+        return 0;
+
+   return nindex[max_active];
+}
+
+
+static void populate_indices(void)
+{
+    stamp_pattern_t *pattern;
+    int i,j;
+    int active;
+    uint32_t cnt, combinations, k;
+    index_t *idx;
+
+    for (i = 1;  i <= SOURCE_MAX;  i++) {
+        idx = &indices[i][0];
+        for (cnt = 0, j = 0;   j < npattern[i];   j++) {
+            pattern = &patterns[i][j];
+            active = pattern->bits;
+            combinations = factorial[pattern->bits];
+            cnt += combinations;
+            for (k = 0;  k < combinations; k++, idx++) {
+                idx->pattern = j;
+                idx->active = active;
+                idx->sequence = k;
+            }
+        }
+        nindex[i] = cnt;
+    }
+}
+
+
+static stamp_t source_stamp(uint32_t seqno, source_t source, int max_active)
+{
+    stamp_pattern_t *pattern;
+    int active;
+    int8_t *bitseq;
+    index_t *idx;
+    uint8_t *seq;
+
+    if (max_active <= 0)
+        return 0;
+
+    if (max_active > SOURCE_MAX)
+        max_active = SOURCE_MAX;
+
+    if (seqno >= seqno_max(max_active))
+        return 0;
+
+    idx = &indices[max_active][seqno];
+
+    pattern = &patterns[max_active][idx->pattern];
+    active  = pattern->bits;
+    bitseq  = pattern->bitseq;
+
+    if (bitseq[source] < 0)
+        return 0;
+
+    seq = sequences[active] + (idx->sequence * (SOURCE_MAX + 1));
+
+    return seq[bitseq[source]];
+}
+
+static bool valid_usecase(entry_t *usecase)
+{
+    entry_t entry;
+    stamp_t stamp;
+    state_t state;
+    conn_t conn;
+    sink_t sink;
+    int i;
+
+    for (i = 0; i < SOURCE_MAX;  i++) {
+        entry = usecase[i];
+        stamp = ENTRY_STAMP(entry);
+        state = ENTRY_STATE(entry);
+        conn  = ENTRY_CONNECTION(entry);
+        sink  = CONNECTION_SINK(conn);
+
+        if (!stamp && (sink != noroute || state != stop))
+            return false;
+    }
+
+    return true;
+}
+
+static void generate_usecases(int max_active,
+                              source_t source,
+                              entry_t *usecase,
+                              entry_t *cursor,
+                              stamp_t *stamp,
+                              uint32_t seqno,
+                              source_t idx)
+{
+    decision_t decision;
+    char ubuf[256];
+    char dbuf[64];
+
+    if (idx == SOURCE_MAX) {
+        if (valid_usecase(usecase)) {
+            decision = make_decision(usecase, source, max_active);
+
+            print_usecase(usecase, ubuf,sizeof(ubuf));
+            print_decision(decision, source, dbuf,sizeof(dbuf));
+
+            fprintf(data, "%s %s\n", ubuf, dbuf);
+
+            decisions[decision] = true;
+            pattern_counter++;
+        }
+    }
+    else {
+        while (next_entry(usecase, idx, stamp[idx], &cursor[idx])) {
+            cursor[idx+1] = 0;
+            generate_usecases(max_active, source, usecase,
+                              cursor, stamp, seqno, idx+1);
+        }
+    }
+}
+
+
+static size_t print_entry(entry_t entry, char *buf, size_t len)
+{
+    conn_t conn;
+    sink_t sink;
+    stamp_t stamp;
+    state_t state;
+    const char *sink_name, *state_name;
+
+    conn  = ENTRY_CONNECTION(entry);
+    state = ENTRY_STATE(entry);
+    stamp = ENTRY_STAMP(entry);
+    sink  = CONNECTION_SINK(conn);
+
+    sink_name = (sink >= 0 && sink < SINK_MAX) ?
+                  sink_names[sink] : "<unknown>";
+    state_name = (state >= 0 && state < STATE_MAX) ?
+                  state_names[state] : "<unknown>";
+
+    return snprintf(buf, len, "%s,%u,%s, ", sink_name, stamp, state_name);
+}
+
+
+void initialize_pattern_generator(const char *stem,
+                                  int max_active,
+                                  source_t source)
+{
+    const char *srcnam;
+    char data_file[1024];
+    char names_file[1024];
+
+    if (source == allSources) {
+        snprintf(data_file, sizeof(data_file), "%s-%d.data",
+                 stem, max_active);
+        snprintf(names_file, sizeof(names_file), "%s-%d.names",
+                 stem, max_active);
+    }
+    else {
+        srcnam = get_source_name(source);
+        snprintf(data_file, sizeof(data_file), "%s-%s-%d.data",
+                 stem, srcnam, max_active);
+        snprintf(names_file, sizeof(names_file), "%s-%s-%d.names",
+                 stem, srcnam, max_active);
+    }
+
+    if (!(data = fopen(data_file, "w+"))) {
+        printf("failed to open file '%s': %s\n", data_file, strerror(errno));
+        exit(errno);
+    }
+
+    if (!(names = fopen(names_file, "w+"))) {
+        printf("failed to open file '%s': %s\n", names_file, strerror(errno));
+        exit(errno);
+    }
+
+    printf("populating '%s' and '%s' files\n", data_file, names_file);
+
+    populate_active_patterns();
+    populate_sequences();
+    populate_indices();
+}
+
+void generate_patterns(int max_active, int source)
+{
+    uint32_t seqno;
+    entry_t cursor[SOURCE_MAX];
+    stamp_t stamp[SOURCE_MAX];
+    usecase_t usecase;
+    source_t idx;
+
+    for (seqno = 0;  seqno < seqno_max(max_active); seqno++) {
+        memset(cursor, 0, sizeof(cursor));
+        memset(usecase, 0, sizeof(usecase));
+
+        for (idx = 0;  idx < SOURCE_MAX;  idx++) {
+            stamp[idx] = source_stamp(seqno, idx, max_active);
+        }
+
+        generate_usecases(max_active, source, usecase, cursor, stamp, seqno, 0);
+    }
+}
+
+void generate_names(int max_active, int source)
+{
+    const char *srcnam;
+    sink_t sink;
+    char feature[256];
+    int  i,j;
+    char *sep;
+
+
+    fprintf(names, "decision.\n\n");
+
+    for (i = 0;  i < SOURCE_MAX;  i++) {
+        if ((srcnam = source_names[i])) {
+            /* route */
+            snprintf(feature, sizeof(feature), "%sRoute:", srcnam);
+            fprintf(names, "%-24s noroute", feature);
+            for (j = 1;  j < SINK_MAX;  j++) {
+                if ((sink = conn_defs[i][j].sink) != noroute)
+                    fprintf(names, ", %s", sink_names[j]);
+            }
+            fprintf(names, ".\n");
+
+            /* stamp */
+            snprintf(feature, sizeof(feature), "%sStamp:", srcnam);
+            if (1) {
+                fprintf(names, "%-24s 0", feature);
+                for (j = 0; j < max_active; j++)
+                    fprintf(names, ", %d", j+1);
+                fprintf(names, ".\n");
+            }
+            else {
+                fprintf(names, "%-24s continuous.\n", feature);
+            }
+
+            /* state */
+            snprintf(feature, sizeof(feature), "%sState:", srcnam);
+            fprintf(names, "%-24s stop, pause, play.\n\n", feature);
+        }
+    }
+
+    snprintf(feature, sizeof(feature), "decision:");
+    fprintf(names, "%-24s ", feature);
+
+    if (source == allSources) {
+        for (i = j = 0, sep = "";   i < DECISION_SPACE_DIMENSION;   i++) {
+            if (decisions[i]) {
+                decision_counter++;
+                if (j && !(j % 10))
+                    sep = ",\n                         ";
+                fprintf(names, "%s%d", sep, i);
+                sep = ", ";
+                j++;
+            }
+        }
+    }
+    else {
+        for (i = 0, sep = "";   i < CONN_STATE_MAX;  i++) {
+            if (decisions[i]) {
+                decision_counter++;
+                fprintf(names, "%s%s", sep, get_conn_state_name(i));
+                sep = ", ";
+            }
+        }
+    }
+
+    fprintf(names, ".\n");
+}
+
+
+size_t print_usecase(entry_t *usecase, char *buf, size_t len)
+{
+    source_t source;
+    char *p, *e;
+
+    e = (p = buf) + len;
+
+    for (source = 0;  source < SOURCE_MAX && p < e;  source++)
+        p += print_entry(usecase[source], p, e-p);
+
+    return p - buf;
+}
+
+
+bool route_conflicts(entry_t entry, conn_t conn)
+{
+    conn_t c;
+    source_t source;
+    sink_t sink;
+    conn_t *conflict;
+    int i;
+
+    if (CONNECTION_SINK(conn) == noroute)
+        return false;
+
+    c      = ENTRY_CONNECTION(entry);
+    source = CONNECTION_SOURCE(c);
+    sink   = CONNECTION_SINK(c);
+
+    conflict = conn_defs[source][sink].conflict;
+
+    for (i = 0;  conflict[i];  i++) {
+        if (conn == conflict[i])
+            return true;
+    }
+
+    return false;
+}
+
+const char *get_source_name(source_t source)
+{
+    if (source == allSources)
+        return "allSources";
+    if (source < 0 || source >= SOURCE_MAX)
+        return "<unknown source>";
+    return source_names[source];
+}
+
+const char *get_sink_name(sink_t sink)
+{
+    if (sink == allSinks)
+        return "allSinks";
+    if (sink < 0 || sink >= SINK_MAX)
+        return "<unknown sink>";
+    return sink_names[sink];
+}
+
+static void usage(int argc, char **argv, int status)
+{
+    (void)argc;
+
+    printf("Usage: %s stem max_active_sources [source]\n"
+           "\twhere\n"
+           "\t   stem is the name for .data and .names files\n"
+           "\t   range for max_active_sources (1 - %d)\n"
+           "\t   the source the decisions are done for [optional]\n",
+           basename(argv[0]), SOURCE_MAX);
+
+    if (status)
+        exit(status);
+}
+
+double get_time_stamp(void)
+{
+    struct timeval tv;
+
+    if (gettimeofday(&tv, NULL) < 0) {
+        printf("failed to get time stamp: %s\n", strerror(errno));
+        exit(errno);
+    }
+
+    return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
+}
+
+int main(int argc, char **argv)
+{
+    char *stem;
+    int max_active;
+    int source;
+    char *e;
+    double start, end;
+
+    if (argc > 4 || argc < 3)
+        usage(argc, argv, EINVAL);
+
+    stem = argv[1];
+    max_active = strtol(argv[2], &e, 10);
+
+    if (max_active < 1 || max_active >= SOURCE_MAX || *e || e == argv[2])
+        usage(argc, argv, EINVAL);
+
+    if (argc == 3)
+        source = allSources;
+    else {
+        if (isdigit(argv[3][0])) {
+            source = strtol(argv[3], &e, 10);
+
+            if (*e || e == argv[3])
+                source = SOURCE_MAX;
+        }
+        else if (isalpha(argv[3][0])) {
+            for (source = 0;  source < SOURCE_MAX;  source++) {
+                if (!strcmp(get_source_name(source), argv[3]))
+                    break;
+            }
+        }
+        else {
+            source = SOURCE_MAX;
+        }
+    }
+
+    if (source < -1 || source >= SOURCE_MAX)
+        usage(argc, argv, EINVAL);
+
+    start = get_time_stamp();
+
+    initialize_pattern_generator(stem, max_active, source);
+    generate_patterns(max_active, source);
+    generate_names(max_active, source);
+
+    end = get_time_stamp();
+
+    printf("generated %u patterns with %u different decisions "
+           "in %.3lf seconds\n",
+           pattern_counter, decision_counter, end - start);
+
+    return 0;
+}
diff --git a/src/plugins/gam-resource-manager/pattern-generator.h b/src/plugins/gam-resource-manager/pattern-generator.h
new file mode 100644 (file)
index 0000000..e33b960
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MRP_PATTERN_GENERATOR_H__
+#define __MRP_PATTERN_GENERATOR_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+
+typedef enum {
+    allSources = -1,
+    wrtApplication = 0,
+    icoApplication,
+    phoneSource,
+    radio,
+    microphone,
+    navigator,
+    SOURCE_MAX
+} source_t;
+
+typedef enum {
+    allSinks = -1,
+    noroute = 0,
+    phoneSink,
+    btHeadset,
+    usbHeadset,
+    speakers,
+    wiredHeadset,
+    voiceRecognition,
+    SINK_MAX
+} sink_t;
+
+typedef enum {
+    stop = 0,
+    pause,
+    play,
+    STATE_MAX
+} state_t;
+
+
+#define CONNECTION_SOURCE(_conn) \
+    (((_conn) >> 8) & 0xff)
+#define CONNECTION_SINK(_conn) \
+    ((_conn) & 0xff)
+
+typedef uint16_t conn_t;
+
+typedef uint8_t stamp_t;        /* SOURCE_MAX must be smaller than 8 */
+
+#define ENTRY_CONNECTION(_entry) \
+    ((conn_t)(((_entry) >> 16) & 0xffff))
+#define ENTRY_STATE(_entry) \
+    ((state_t)(_entry) & 0x0f)
+#define ENTRY_STAMP(_entry) \
+    ((stamp_t)(((_entry) >> 8) & 0xff))
+
+typedef uint32_t entry_t;
+
+typedef entry_t usecase_t [SOURCE_MAX];
+
+
+void initialize_pattern_generator(const char *stemy, int max_active,
+                                  source_t source);
+void generate_patterns(int max_active, int source);
+void generate_names(int max_active, int source);
+
+size_t print_usecase(entry_t *usecase, char *buf, size_t len);
+
+bool route_conflicts(entry_t entry, conn_t conn);
+
+const char *get_source_name(source_t source);
+const char *get_sink_name(sink_t sink);
+
+
+#endif /* __MRP_PATTERN_GENERATOR_H__ */
diff --git a/src/plugins/gam-resource-manager/plugin-gam-resource-manager.c b/src/plugins/gam-resource-manager/plugin-gam-resource-manager.c
new file mode 100644 (file)
index 0000000..eed6941
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common.h>
+#include <murphy/common/debug.h>
+#include <murphy/core/plugin.h>
+#include <murphy/core/console.h>
+#include <murphy/core/event.h>
+#include <murphy/core/context.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+#include <murphy-db/mqi.h>
+
+#include <murphy/resource/config-api.h>
+#include <murphy/resource/manager-api.h>
+#include <murphy/resource/client-api.h>
+#include <murphy/resource/protocol.h>
+
+#include "plugin-gam-resource-manager.h"
+#include "backend.h"
+#include "source.h"
+#include "sink.h"
+#include "usecase.h"
+
+typedef struct dependency_s   dependency_t;
+
+enum {
+    CONFDIR,
+    PREFIX,
+    DECISION_NAMES,
+    MAX_ACTIVE,
+};
+
+struct dependency_s {
+    const char *db_table_name;
+    mrp_resmgr_dependency_cb_t callback;
+    bool changed;
+};
+
+struct mrp_resmgr_s {
+    mrp_plugin_t *plugin;
+
+    mrp_resmgr_config_t *config;
+    mrp_event_watch_t *w;
+    mrp_resmgr_backend_t *backend;
+    mrp_resmgr_sources_t *sources;
+    mrp_resmgr_sinks_t *sinks;
+    mrp_resmgr_usecase_t *usecase;
+
+    size_t ndepend;
+    dependency_t *depends;
+};
+
+static int resource_update_cb(mrp_scriptlet_t *, mrp_context_tbl_t *);
+static void add_depenedencies_to_resolver(mrp_resmgr_t *);
+
+static void print_resources_cb(mrp_console_t *, void *, int, char **);
+static void print_usecase_cb(mrp_console_t *, void *, int, char **);
+
+static mrp_resmgr_config_t *config_create(mrp_plugin_t *);
+static void config_destroy(mrp_resmgr_config_t *);
+
+static void event_cb(mrp_event_watch_t *, int, mrp_msg_t *, void *);
+static int subscribe_events(mrp_resmgr_t *);
+static void unsubscribe_events(mrp_resmgr_t *);
+
+
+
+MRP_CONSOLE_GROUP(manager_group, "gam", NULL, NULL, {
+        MRP_TOKENIZED_CMD("resources", print_resources_cb, FALSE,
+                          "resources", "prints managed resources",
+                          "prints  the resources managed by "
+                          "gam-resource-manager."),
+        MRP_TOKENIZED_CMD("usecase", print_usecase_cb, FALSE,
+                          "usecase", "prints the usecase values",
+                          "prints  the usecase values that are used "
+                          "for making routing/playback right decisions "
+                          "by gam-resource-manager."),
+});
+
+static mrp_resmgr_t *resmgr_data;
+
+mrp_resmgr_config_t *mrp_resmgr_get_config(mrp_resmgr_t *resmgr)
+{
+    return resmgr->config;
+}
+
+mrp_resmgr_backend_t *mrp_resmgr_get_backend(mrp_resmgr_t *resmgr)
+{
+    return resmgr->backend;
+}
+
+
+mrp_resmgr_sources_t *mrp_resmgr_get_sources(mrp_resmgr_t *resmgr)
+{
+    return resmgr->sources;
+}
+
+mrp_resmgr_sinks_t *mrp_resmgr_get_sinks(mrp_resmgr_t *resmgr)
+{
+    return resmgr->sinks;
+}
+
+mrp_resmgr_usecase_t *mrp_resmgr_get_usecase(mrp_resmgr_t *resmgr)
+{
+    return resmgr->usecase;
+}
+
+
+
+void mrp_resmgr_register_dependency(mrp_resmgr_t *resmgr,
+                                    const char *db_table_name,
+                                    mrp_resmgr_dependency_cb_t callback)
+{
+    size_t size;
+    char dependency[512];
+    int idx;
+
+    MRP_ASSERT(resmgr && db_table_name, "invalid argument");
+
+    snprintf(dependency, sizeof(dependency), "$%s", db_table_name);
+
+    idx = resmgr->ndepend++;
+    size = resmgr->ndepend * sizeof(dependency_t);
+
+    if (!(resmgr->depends = mrp_realloc(resmgr->depends, size)) ||
+        !(resmgr->depends[idx].db_table_name = mrp_strdup(dependency)))
+    {
+        mrp_log_error("gam-resource-manager: failed to allocate memory "
+                      "for resource dependencies");
+        resmgr->ndepend = 0;
+        resmgr->depends = NULL;
+        return;
+    }
+
+    resmgr->depends[idx].callback = callback;
+}
+
+
+
+static int resource_update_cb(mrp_scriptlet_t *script, mrp_context_tbl_t *ctbl)
+{
+    mrp_resmgr_t *resmgr = (mrp_resmgr_t *)script->data;
+    uint32_t zoneid;
+    size_t i;
+    bool recalc;
+
+    MRP_UNUSED(ctbl);
+
+    MRP_ASSERT(resmgr, "invalid argument");
+
+    printf("### %s() called\n", __FUNCTION__);
+
+    for (recalc = false, i = 0;    i < resmgr->ndepend;    i++)
+        recalc |= resmgr->depends[i].callback(resmgr);
+
+    if (recalc) {
+        printf("=> recalc resource allocations!\n");
+
+        for (zoneid = 0;  zoneid < mrp_zone_count();  zoneid++)
+            mrp_resource_owner_recalc(zoneid);
+    }
+
+    return TRUE;
+}
+
+static void add_depenedencies_to_resolver(mrp_resmgr_t *resmgr)
+{
+    static const char *target = "_gam_resources";
+    static mrp_interpreter_t resource_updater = {
+        { NULL, NULL },
+        "gam_resource_updater",
+        NULL,
+        NULL,
+        NULL,
+        resource_update_cb,
+        NULL
+    };
+
+    mrp_plugin_t *plugin;
+    mrp_context_t *ctx;
+    mrp_resolver_t *resolver;
+    char buf[2048];
+    const char *deps[256];
+    size_t i,ndep;
+    char *p, *e;
+    int ok;
+
+    MRP_ASSERT(resmgr, "invalid argument");
+
+    plugin = resmgr->plugin;
+
+    if (!(ctx = plugin->ctx) || !(resolver = ctx->r))
+        return;
+
+    if (!(ndep = resmgr->ndepend) || !resmgr->depends)
+        return;
+
+    MRP_ASSERT(ndep < MRP_ARRAY_SIZE(deps), "dependency overflow");
+
+    for (e = (p = buf) + sizeof(buf), i = 0;     i < ndep;     i++) {
+        deps[i] = resmgr->depends[i].db_table_name;
+
+        if (p < e)
+            p += snprintf(p, e-p, " %s", resmgr->depends[i].db_table_name);
+    }
+
+    printf("%s:%s\n\tresource_recalc()\n\n", target, buf);
+
+    ok = mrp_resolver_add_prepared_target(resolver, target, deps, ndep,
+                                          &resource_updater, NULL, resmgr);
+    if (!ok) {
+        mrp_log_error("gam-resource-manager: failed to install "
+                      "resolver target '%s'", target);
+    }
+}
+
+
+static void print_resources_cb(mrp_console_t *c, void *user_data,
+                               int argc, char **argv)
+{
+    MRP_UNUSED(c);
+    MRP_UNUSED(user_data);
+    MRP_UNUSED(argc);
+    MRP_UNUSED(argv);
+
+    printf("Resources managed by gam-resource-manager:\n");
+
+    printf("\n");
+}
+
+static void print_usecase_cb(mrp_console_t *c, void *user_data,
+                             int argc, char **argv)
+{
+    mrp_resmgr_t *resmgr;
+    char buf[16384];
+
+    MRP_UNUSED(c);
+    MRP_UNUSED(user_data);
+    MRP_UNUSED(argc);
+    MRP_UNUSED(argv);
+
+    if ((resmgr = resmgr_data)) {
+        mrp_usecase_print(resmgr->usecase, buf, sizeof(buf));
+        printf("Current usecase:\n%s\n", buf);
+    }
+}
+
+
+static mrp_resmgr_config_t *config_create(mrp_plugin_t *plugin)
+{
+    mrp_plugin_arg_t    *args = plugin->args;
+    mrp_resmgr_config_t *config;
+
+    if ((config = mrp_allocz(sizeof(mrp_resmgr_config_t)))) {
+        config->confdir    = mrp_strdup(args[CONFDIR].str);
+        config->prefix     = mrp_strdup(args[PREFIX].str);
+        config->confnams   = mrp_strdup(args[DECISION_NAMES].str);
+        config->max_active = args[MAX_ACTIVE].i32;
+    }
+
+    return config;
+}
+
+static void config_destroy(mrp_resmgr_config_t *config)
+{
+    if (config) {
+        mrp_free((void *)config->confdir);
+        mrp_free((void *)config->prefix);
+        mrp_free(config);
+    }
+}
+
+
+static void event_cb(mrp_event_watch_t *w, int id, mrp_msg_t *event_data,
+                     void *user_data)
+{
+    mrp_plugin_t      *plugin   = (mrp_plugin_t *)user_data;
+#if 0
+    mrp_plugin_arg_t  *args     = plugin->args;
+#endif
+    mrp_resmgr_t      *resmgr   = (mrp_resmgr_t *)plugin->data;
+    const char        *event    = mrp_get_event_name(id);
+    uint16_t           tag_inst = MRP_PLUGIN_TAG_INSTANCE;
+    uint16_t           tag_name = MRP_PLUGIN_TAG_PLUGIN;
+    const char        *inst;
+    const char        *name;
+    int                success;
+
+    MRP_UNUSED(w);
+
+    mrp_log_info("%s: got event 0x%x (%s):", plugin->instance, id, event);
+
+    if (resmgr && event) {
+        if (!strcmp(event, MRP_PLUGIN_EVENT_STARTED)) {
+            success = mrp_msg_get(event_data,
+                                  MRP_MSG_TAG_STRING(tag_inst, &inst),
+                                  MRP_MSG_TAG_STRING(tag_name, &name),
+                                  MRP_MSG_END);
+            if (success) {
+                if (!strcmp(inst, plugin->instance)) {
+                    /* initialize here */
+                }
+            }
+        } /* if PLUGIN_STARTED */
+    }
+}
+
+static int subscribe_events(mrp_resmgr_t *resmgr)
+{
+    mrp_plugin_t *plugin = resmgr->plugin;
+    mrp_event_mask_t events;
+
+    mrp_set_named_events(&events,
+                         MRP_PLUGIN_EVENT_LOADED,
+                         MRP_PLUGIN_EVENT_STARTED,
+                         MRP_PLUGIN_EVENT_FAILED,
+                         MRP_PLUGIN_EVENT_STOPPING,
+                         MRP_PLUGIN_EVENT_STOPPED,
+                         MRP_PLUGIN_EVENT_UNLOADED,
+                         NULL);
+
+    resmgr->w = mrp_add_event_watch(&events, event_cb, plugin);
+
+    return (resmgr->w != NULL);
+}
+
+
+static void unsubscribe_events(mrp_resmgr_t *resmgr)
+{
+    if (resmgr->w) {
+        mrp_del_event_watch(resmgr->w);
+        resmgr->w = NULL;
+    }
+}
+
+
+
+static int manager_init(mrp_plugin_t *plugin)
+{
+    mrp_resmgr_t *resmgr;
+
+    mrp_log_info("%s() called for GAM resource manager instance '%s'...",
+                 __FUNCTION__, plugin->instance);
+
+
+    if (!(resmgr = mrp_allocz(sizeof(*resmgr)))) {
+        mrp_log_error("Failed to allocate private data for GAM resource "
+                      "manager plugin instance %s.", plugin->instance);
+        return FALSE;
+    }
+
+    resmgr->plugin = plugin;
+    resmgr->config = config_create(plugin);
+    resmgr->backend = mrp_resmgr_backend_create(resmgr);
+    resmgr->sources = mrp_resmgr_sources_create(resmgr);
+    resmgr->sinks = mrp_resmgr_sinks_create(resmgr);
+    resmgr->usecase = mrp_resmgr_usecase_create(resmgr);
+
+    plugin->data = resmgr;
+    resmgr_data = resmgr;
+
+    subscribe_events(resmgr);
+    mqi_open();
+    add_depenedencies_to_resolver(resmgr);
+
+    /*******************************/
+    mrp_resmgr_sink_add(resmgr, "speakers"        , 0, NULL);
+    mrp_resmgr_sink_add(resmgr, "wiredHeadset"    , 0, NULL);
+    mrp_resmgr_sink_add(resmgr, "usbHeadset"      , 0, NULL);
+    mrp_resmgr_sink_add(resmgr, "btHeadset"       , 0, NULL);
+    mrp_resmgr_sink_add(resmgr, "voiceRecognition", 0, NULL);
+
+    mrp_resmgr_source_add(resmgr, "wrtApplication", 0);
+    mrp_resmgr_source_add(resmgr, "icoApplication", 0);
+    mrp_resmgr_source_add(resmgr, "phone"         , 0);
+    mrp_resmgr_source_add(resmgr, "radio"         , 0);
+    mrp_resmgr_source_add(resmgr, "microphone"    , 0);
+    mrp_resmgr_source_add(resmgr, "navigator"     , 0);
+    /*******************************/
+
+    return TRUE;
+}
+
+
+static void manager_exit(mrp_plugin_t *plugin)
+{
+    mrp_resmgr_t *resmgr;
+    size_t i;
+
+    mrp_log_info("%s() called for GAM resource manager instance '%s'...",
+                 __FUNCTION__, plugin->instance);
+
+    if ((resmgr = plugin->data) && resmgr_data == resmgr) {
+        unsubscribe_events(resmgr);
+
+        for (i = 0;  i < resmgr->ndepend;  i++)
+            mrp_free((void *)resmgr->depends[i].db_table_name);
+        mrp_free((void *)resmgr->depends);
+
+        mrp_resmgr_backend_destroy(resmgr->backend);
+        mrp_resmgr_sources_destroy(resmgr->sources);
+        mrp_resmgr_sinks_destroy(resmgr->sinks);
+        mrp_resmgr_usecase_destroy(resmgr->usecase);
+        config_destroy(resmgr->config);
+
+        mrp_free(resmgr);
+
+        resmgr_data = NULL;
+    }
+}
+
+
+#define MANAGER_DESCRIPTION "Plugin to implement GAM resources"
+#define MANAGER_HELP        "Maybe later ..."
+#define MANAGER_VERSION      MRP_VERSION_INT(0, 0, 1)
+#define MANAGER_AUTHORS     "Janos Kovacs <jankovac503@gmail.com>"
+
+#define STRING_ARG(_id,_n,_d) MRP_PLUGIN_ARGIDX(_id, STRING, _n, _d)
+#define INTEGER_ARG(_id,_n,_d) MRP_PLUGIN_ARGIDX(_id, INT32, _n, _d)
+
+static mrp_plugin_arg_t arg_defs[] = {
+    STRING_ARG  (CONFDIR       , "config_dir"    , MRP_RESMGR_DEFAULT_CONFDIR),
+    STRING_ARG  (PREFIX        , "prefix"        , MRP_RESMGR_DEFAULT_PREFIX ),
+    STRING_ARG  (DECISION_NAMES, "decision_names", MRP_RESMGR_DEFAULT_NAMES  ),
+    INTEGER_ARG (MAX_ACTIVE    , "max_active"    , 1                         ),
+};
+
+#undef INTEGER_ARG
+#undef STRING_ARG
+
+MURPHY_REGISTER_PLUGIN("gam-resource-manager",
+                       MANAGER_VERSION,
+                       MANAGER_DESCRIPTION,
+                       MANAGER_AUTHORS,
+                       MANAGER_HELP,
+                       MRP_SINGLETON,
+                       manager_init,
+                       manager_exit,
+                       arg_defs, MRP_ARRAY_SIZE(arg_defs),
+                       NULL, 0,
+                       NULL, 0,
+                       &manager_group);
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
diff --git a/src/plugins/gam-resource-manager/plugin-gam-resource-manager.h b/src/plugins/gam-resource-manager/plugin-gam-resource-manager.h
new file mode 100644 (file)
index 0000000..12e3530
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_GAM_RESOURCE_MANAGER_H__
+#define __MURPHY_GAM_RESOURCE_MANAGER_H__
+
+#include <murphy/resource/data-types.h>
+
+#define MRP_RESMGR_PLAYBACK_RESOURCE        "audio_playback"
+#define MRP_RESMGR_RECORDING_RESOURCE       "audio_recording"
+
+#define MRP_RESMGR_SOURCE_STATE_TABLE       "audio_manager_sources"
+#define MRP_RESMGR_SINK_STATE_TABLE         "audio_manager_sinks"
+
+#define MRP_RESMGR_DEFAULT_CONFDIR          "/etc/murphy/gam_config"
+#define MRP_RESMGR_DEFAULT_PREFIX           "gam"
+#define MRP_RESMGR_DEFAULT_NAMES            MRP_RESMGR_DEFAULT_PREFIX
+#define MRP_RESMGR_RESOURCE_MAX             256
+#define MRP_RESMGR_SOURCE_MAX               64
+#define MRP_RESMGR_SINK_MAX                 64
+#define MRP_RESMGR_USECASE_SIZE_MAX         128
+
+#define MRP_RESMGR_RESOURCE_BUCKETS         (MRP_RESMGR_RESOURCE_MAX / 4)
+#define MRP_RESMGR_SOURCE_BUCKETS           (MRP_RESMGR_SOURCE_MAX / 4)
+#define MRP_RESMGR_SINK_BUCKETS             (MRP_RESMGR_SINK_MAX / 4)
+#define MRP_RESMGR_USECASE_SIZE_BUCKETS     (MRP_RESMGR_USECASE_SIZE_MAX / 4)
+
+#define MRP_RESMGR_RESOURCE_TYPE_PLAYBACK   0
+#define MRP_RESMGR_RESOURCE_TYPE_RECORDING  1
+#define MRP_RESMGR_RESOURCE_TYPE_MAX        2
+
+typedef struct mrp_resmgr_s                 mrp_resmgr_t;
+typedef struct mrp_resmgr_config_s          mrp_resmgr_config_t;
+typedef struct mrp_resmgr_backend_s         mrp_resmgr_backend_t;
+typedef struct mrp_resmgr_resource_s        mrp_resmgr_resource_t;
+typedef struct mrp_resmgr_sources_s         mrp_resmgr_sources_t;
+typedef struct mrp_resmgr_source_s          mrp_resmgr_source_t;
+typedef struct mrp_resmgr_sinks_s           mrp_resmgr_sinks_t;
+typedef struct mrp_resmgr_sink_s            mrp_resmgr_sink_t;
+typedef struct mrp_resmgr_usecase_s         mrp_resmgr_usecase_t;
+
+typedef bool (*mrp_resmgr_dependency_cb_t)(mrp_resmgr_t *);
+
+struct mrp_resmgr_config_s {
+    const char *confdir;
+    const char *prefix;
+    const char *confnams;
+    int max_active;
+};
+
+void mrp_resmgr_register_dependency(mrp_resmgr_t *resmgr,
+                                    const char *db_table_name,
+                                    mrp_resmgr_dependency_cb_t callback);
+
+mrp_resmgr_config_t *mrp_resmgr_get_config(mrp_resmgr_t *resmgr);
+mrp_resmgr_backend_t *mrp_resmgr_get_backend(mrp_resmgr_t *resmgr);
+mrp_resmgr_sources_t *mrp_resmgr_get_sources(mrp_resmgr_t *resmgr);
+mrp_resmgr_sinks_t *mrp_resmgr_get_sinks(mrp_resmgr_t *resmgr);
+mrp_resmgr_usecase_t *mrp_resmgr_get_usecase(mrp_resmgr_t *resmgr);
+
+#endif  /* __MURPHY_GAM_RESOURCE_MANAGER_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
diff --git a/src/plugins/gam-resource-manager/sink.c b/src/plugins/gam-resource-manager/sink.c
new file mode 100644 (file)
index 0000000..979dd52
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common.h>
+
+#include <murphy-db/mqi.h>
+
+#include "sink.h"
+#include "source.h"
+#include "c5-decision-tree.h"
+
+#define COLUMN_MAX 4
+
+typedef struct gam_sink_s     gam_sink_t;
+typedef struct table_s        table_t;
+typedef struct row_s          row_t;
+typedef struct colum_def_s    column_def_t;
+
+struct table_s {
+    mqi_handle_t handle;
+    mqi_column_desc_t cols[COLUMN_MAX+1];
+};
+
+struct row_s {
+    int32_t id;
+    const char *name;
+    int32_t available;
+    int32_t visible;
+};
+
+struct colum_def_s {
+    char *name;
+    mqi_data_type_t type;
+    int offset;
+};
+
+struct mrp_resmgr_sinks_s {
+    mrp_resmgr_t *resmgr;
+    struct {
+        mrp_htbl_t *by_name;
+        mrp_htbl_t *by_id;
+    } lookup;
+    table_t state_table;
+};
+
+struct gam_sink_s {
+    const char *name;
+    uint16_t id;
+};
+
+
+struct mrp_resmgr_sink_s {
+    mrp_resmgr_sinks_t *sinks;
+    gam_sink_t gam_sink;
+    mrp_htbl_t *decision_ids;
+    bool available;
+};
+
+static bool register_gam_id(mrp_resmgr_sink_t *, uint16_t);
+
+static void sink_free(void *, void *);
+
+static int hash_compare(const void *, const void *);
+static uint32_t id_hash_function(const void *);
+static uint32_t ptr_hash_function(const void *);
+
+static bool sink_status_changed_cb(mrp_resmgr_t *);
+static mqi_handle_t get_table_handle(mrp_resmgr_sinks_t *);
+
+
+
+mrp_resmgr_sinks_t *mrp_resmgr_sinks_create(mrp_resmgr_t *resmgr)
+{
+    mrp_resmgr_sinks_t *sinks;
+    mrp_htbl_config_t ncfg, icfg;
+
+    MRP_ASSERT(resmgr, "invalid argument");
+
+    memset(&ncfg, 0, sizeof(ncfg));
+    ncfg.nentry = MRP_RESMGR_SINK_MAX;
+    ncfg.comp = mrp_string_comp;
+    ncfg.hash = mrp_string_hash;
+    ncfg.free = sink_free;
+    ncfg.nbucket = MRP_RESMGR_SINK_BUCKETS;
+
+    memset(&icfg, 0, sizeof(icfg));
+    icfg.nentry = MRP_RESMGR_SINK_MAX;
+    icfg.comp = hash_compare;
+    icfg.hash = id_hash_function;
+    icfg.free = NULL;
+    icfg.nbucket = MRP_RESMGR_SINK_BUCKETS;
+
+    if ((sinks = mrp_allocz(sizeof(mrp_resmgr_sinks_t)))) {
+        sinks->resmgr = resmgr;
+        sinks->lookup.by_name = mrp_htbl_create(&ncfg);
+        sinks->lookup.by_id = mrp_htbl_create(&icfg);
+        sinks->state_table.handle = MQI_HANDLE_INVALID;
+
+        mrp_resmgr_register_dependency(resmgr, MRP_RESMGR_SINK_STATE_TABLE,
+                                       sink_status_changed_cb);
+    }
+
+    return sinks;
+}
+
+void mrp_resmgr_sinks_destroy(mrp_resmgr_sinks_t *sinks)
+{
+    if (sinks) {
+        mrp_htbl_destroy(sinks->lookup.by_id, false);
+        mrp_htbl_destroy(sinks->lookup.by_name, true);
+        mrp_free(sinks);
+    }
+}
+
+mrp_resmgr_sink_t *mrp_resmgr_sink_add(mrp_resmgr_t *resmgr,
+                                       const char *name,
+                                       int32_t id,
+                                       mrp_resmgr_source_t *source)
+{
+    mrp_resmgr_config_t *config;
+    mrp_resmgr_sinks_t *sinks;
+    mrp_resmgr_sink_t *sink;
+    char *name_dup;
+    mrp_htbl_config_t cfg;
+
+    MRP_ASSERT(resmgr && name, "invalid argument");
+
+    config = mrp_resmgr_get_config(resmgr);
+    sinks = mrp_resmgr_get_sinks(resmgr);
+
+    MRP_ASSERT(config && sinks, "internal error");
+
+    if (!(sink = mrp_resmgr_sink_find_by_name(resmgr, name))) {
+
+        /* new sink */
+        memset(&cfg, 0, sizeof(cfg));
+        cfg.nentry = MRP_RESMGR_SOURCE_MAX;
+        cfg.comp = hash_compare;
+        cfg.hash = ptr_hash_function;
+        cfg.free = NULL;
+        cfg.nbucket = MRP_RESMGR_SOURCE_BUCKETS;
+
+        if ((sink = mrp_allocz(sizeof(mrp_resmgr_sink_t))) &&
+            (name_dup = mrp_strdup(name)))
+        {
+            sink->sinks = sinks;
+            sink->gam_sink.name = name_dup;
+            sink->decision_ids = mrp_htbl_create(&cfg);
+
+            if (!mrp_htbl_insert(sinks->lookup.by_name, name_dup, sink)) {
+                mrp_log_error("gam-resource-manager: attempt to add sink "
+                              "'%s' multiple times", name_dup);
+
+                sink_free((void *)sink->gam_sink.name, (void *)sink);
+                sink = NULL;
+            }
+        }
+    }
+
+    if (sink) {
+        if (!source) {
+            /* id is a gam sink ID */
+            register_gam_id(sink, id);
+        }
+        else {
+            /* id is the enumeration of the source in the decision conf */
+            if (!(mrp_htbl_insert(sink->decision_ids, source, NULL + (id+1)))){
+                mrp_log_error("gam-resource-manager: attempt to add id %d "
+                              "multiple time for source '%s'",
+                              id, mrp_resmgr_source_get_name(source));
+            }
+        }
+    }
+
+    return sink;
+}
+
+mrp_resmgr_sink_t *mrp_resmgr_sink_find_by_name(mrp_resmgr_t *resmgr,
+                                                const char *name)
+{
+    mrp_resmgr_sinks_t *sinks;
+
+    MRP_ASSERT(resmgr && name, "invalid argument");
+
+    if (!(sinks = mrp_resmgr_get_sinks(resmgr)) || !(sinks->lookup.by_name))
+        return NULL;
+
+    return mrp_htbl_lookup(sinks->lookup.by_name, (void *)name);
+}
+
+
+mrp_resmgr_sink_t *mrp_resmgr_sink_find_by_gam_id(mrp_resmgr_t *resmgr,
+                                                  uint16_t gam_id)
+{
+    mrp_resmgr_sinks_t *sinks;
+
+    MRP_ASSERT(resmgr, "invalid argument");
+
+    if (!(sinks = mrp_resmgr_get_sinks(resmgr)) || !(sinks->lookup.by_id))
+        return NULL;
+
+    return mrp_htbl_lookup(sinks->lookup.by_id, NULL + gam_id);
+}
+
+
+const char *mrp_resmgr_sink_get_name(mrp_resmgr_sink_t *sink)
+{
+    MRP_ASSERT(sink, "invalid argument");
+
+    return sink->gam_sink.name;
+}
+
+bool mrp_resmgr_sink_get_availability(mrp_resmgr_sink_t *sink)
+{
+    if (!sink)
+        return false;
+
+    return sink->available;
+}
+
+
+int32_t mrp_resmgr_sink_get_decision_id(mrp_resmgr_sink_t *sink,
+                                        mrp_resmgr_source_t *source)
+{
+    void *void_id;
+
+    if (!sink || !source)
+        return -1;
+
+    if (!(void_id = mrp_htbl_lookup(sink->decision_ids, source)))
+        return -1;
+
+    return (void_id - NULL) - 1;
+}
+
+static bool register_gam_id(mrp_resmgr_sink_t *sink, uint16_t gam_id)
+{
+    mrp_resmgr_sinks_t *sinks;
+
+    if (!(sinks = sink->sinks))
+        return false;
+
+    if (!gam_id || gam_id == sink->gam_sink.id)
+        return true;
+
+    if (sink->gam_sink.id) {
+        mrp_log_error("gam-resource-manager: attempt to reset "
+                      "gam ID of '%s'", sink->gam_sink.name);
+        return false;
+    }
+
+    if (!mrp_htbl_insert(sinks->lookup.by_id, NULL+gam_id, sink)) {
+        mrp_log_error("gam-resource-manager: attempt to add "
+                      "sink %d multiple times", gam_id);
+        return false;
+    }
+
+    sink->gam_sink.id = gam_id;
+
+    mrp_debug("assign id %d to sink '%s'", gam_id, sink->gam_sink.name);
+
+
+    return true;
+}
+
+static void sink_free(void *key, void *object)
+{
+    mrp_resmgr_sink_t *sink = (mrp_resmgr_sink_t *)object;
+
+    MRP_ASSERT(key && object, "internal error");
+    MRP_ASSERT(sink->gam_sink.name == (const char *)key, "corrupt data");
+
+    mrp_htbl_destroy(sink->decision_ids, false);
+
+    mrp_free((void *)sink->gam_sink.name);
+
+    free(sink);
+}
+
+static int hash_compare(const void *key1, const void *key2)
+{
+    if (key1 < key2)
+        return -1;
+    if (key1 > key2)
+        return 1;
+    return 0;
+}
+
+static uint32_t id_hash_function(const void *key)
+{
+    return (uint32_t)(key - (const void *)0);
+}
+
+static uint32_t ptr_hash_function(const void *key)
+{
+    return (uint32_t)(((size_t)key >> 4) & 0xffffffff);
+}
+
+static bool sink_status_changed_cb(mrp_resmgr_t *resmgr)
+{
+    mrp_resmgr_sinks_t *sinks;
+    mrp_resmgr_sink_t *sink;
+    mqi_handle_t h;
+    int i, n;
+    row_t rows[MRP_RESMGR_SOURCE_MAX];
+    row_t *r;
+    int change_count;
+
+    MRP_ASSERT(resmgr, "invalid argument");
+
+    printf("### %s() called\n", __FUNCTION__);
+
+    if (!(sinks = mrp_resmgr_get_sinks(resmgr)) ||
+        !sinks->lookup.by_name)
+        return false;
+
+    if ((h = get_table_handle(sinks)) == MQI_HANDLE_INVALID) {
+        mrp_log_error("gam-resource-manager: can't update status changes: "
+                      "database error");
+        return false;
+    }
+
+    memset(rows, 0, sizeof(rows));
+
+    if ((n = MQI_SELECT(sinks->state_table.cols, h, MQI_ALL, rows)) < 0) {
+        mrp_log_error("gam-resource-manager: select on table '%s' failed: %s",
+                      MRP_RESMGR_SINK_STATE_TABLE, strerror(errno));
+        return false;
+    }
+
+    if (n == 0)
+        return false;
+
+    for (change_count = i = 0;  i < n;  i++) {
+        r = rows + i;
+
+        if (!r->visible)
+            continue;
+
+        if ((sink = mrp_htbl_lookup(sinks->lookup.by_name, (void *)r->name))) {
+            register_gam_id(sink, r->id);
+
+            if (( r->available && !sink->available) ||
+                (!r->available &&  sink->available)  )
+            {
+                sink->available = r->available;
+                change_count++;
+
+                mrp_debug("%s become %savailable", sink->gam_sink.name,
+                          sink->available ? "":"un");
+            }
+        }
+    }
+
+    return change_count > 0;
+}
+
+
+static mqi_handle_t get_table_handle(mrp_resmgr_sinks_t *sinks)
+{
+#define COLUMN(_n, _t)  { # _n, mqi_ ## _t, MQI_OFFSET(row_t, _n) }
+
+    static column_def_t col_defs[COLUMN_MAX] = {
+        COLUMN( id       , integer ),
+        COLUMN( name     , string  ),
+        COLUMN( available, integer ),
+        COLUMN( visible  , integer ),
+    };
+
+    mqi_handle_t h;
+    column_def_t *def;
+    mqi_column_desc_t *desc;
+    int i;
+
+    if (sinks->state_table.handle == MQI_HANDLE_INVALID) {
+        h = mqi_get_table_handle(MRP_RESMGR_SINK_STATE_TABLE);
+
+        if (h != MQI_HANDLE_INVALID) {
+            sinks->state_table.handle = h;
+
+            for (i = 0;  i < COLUMN_MAX;  i++) {
+                def = col_defs + i;
+                desc = sinks->state_table.cols + i;
+
+                if ((desc->cindex = mqi_get_column_index(h, def->name)) < 0) {
+                    mrp_log_error("gam-resource-manager: can't find column "
+                                  "'%s' in table '%s'", def->name,
+                                  MRP_RESMGR_SINK_STATE_TABLE);
+                    sinks->state_table.handle = MQI_HANDLE_INVALID;
+                    break;
+                }
+
+                desc->offset = def->offset;
+            }
+
+            desc = sinks->state_table.cols + i;
+            desc->cindex = -1;
+            desc->offset = -1;
+        }
+
+    }
+
+    return sinks->state_table.handle;
+
+#undef COLUMN
+}
diff --git a/src/plugins/gam-resource-manager/sink.h b/src/plugins/gam-resource-manager/sink.h
new file mode 100644 (file)
index 0000000..ff62cbe
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_GAM_RESOURCE_MANAGER_SINK_H__
+#define __MURPHY_GAM_RESOURCE_MANAGER_SINK_H__
+
+#include "plugin-gam-resource-manager.h"
+
+
+mrp_resmgr_sinks_t *mrp_resmgr_sinks_create(mrp_resmgr_t *resmgr);
+void mrp_resmgr_sinks_destroy(mrp_resmgr_sinks_t *sinks);
+
+mrp_resmgr_sink_t *mrp_resmgr_sink_find_by_name(mrp_resmgr_t *resmgr,
+                                                const char *name);
+mrp_resmgr_sink_t *mrp_resmgr_sink_find_by_gam_id(mrp_resmgr_t *resmgr,
+                                                  uint16_t gam_id);
+
+const char *mrp_resmgr_sink_get_name(mrp_resmgr_sink_t *sink);
+bool mrp_resmgr_sink_get_availability(mrp_resmgr_sink_t *sink);
+int32_t mrp_resmgr_sink_get_decision_id(mrp_resmgr_sink_t *sink,
+                                        mrp_resmgr_source_t *source);
+
+mrp_resmgr_sink_t *mrp_resmgr_sink_add(mrp_resmgr_t *resmgr,
+                                       const char *gam_name,
+                                       int32_t id,
+                                       mrp_resmgr_source_t *source);
+
+
+#endif /* __MURPHY_GAM_RESOURCE_MANAGER_SINK_H__ */
diff --git a/src/plugins/gam-resource-manager/source.c b/src/plugins/gam-resource-manager/source.c
new file mode 100644 (file)
index 0000000..287e7b7
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common.h>
+
+#include <murphy-db/mqi.h>
+
+#include "source.h"
+#include "usecase.h"
+#include "backend.h"
+
+#define COLUMN_MAX 4
+
+typedef struct gam_source_s   gam_source_t;
+typedef struct table_s        table_t;
+typedef struct row_s          row_t;
+typedef struct colum_def_s    column_def_t;
+
+struct table_s {
+    mqi_handle_t handle;
+    mqi_column_desc_t cols[COLUMN_MAX+1];
+};
+
+struct row_s {
+    int32_t id;
+    const char *name;
+    int32_t available;
+    int32_t visible;
+};
+
+struct colum_def_s {
+    char *name;
+    mqi_data_type_t type;
+    int offset;
+};
+
+
+struct mrp_resmgr_sources_s {
+    mrp_resmgr_t *resmgr;
+    struct {
+        mrp_htbl_t *by_name;
+        mrp_htbl_t *by_id;
+    } lookup;
+    mrp_decision_conf_t *decision_conf;
+    table_t state_table;
+};
+
+struct gam_source_s {
+    const char *name;
+    uint16_t id;
+};
+
+
+struct mrp_resmgr_source_s {
+    mrp_resmgr_sources_t *sources;
+    gam_source_t gam_source;
+    mrp_list_hook_t resources;
+    mrp_decision_tree_t *decision_tree;
+    bool available;
+};
+
+
+static bool register_gam_id(mrp_resmgr_source_t *, uint16_t);
+
+static mrp_decision_conf_t *load_decision_conf(const char *);
+static bool decision_values_match(mrp_decision_conf_t *);
+
+static void source_free(void *, void *);
+static int id_hash_compare(const void *, const void *);
+static uint32_t id_hash_function(const void *);
+
+static bool source_status_changed_cb(mrp_resmgr_t *);
+static mqi_handle_t get_table_handle(mrp_resmgr_sources_t *);
+
+
+mrp_resmgr_sources_t *mrp_resmgr_sources_create(mrp_resmgr_t *resmgr)
+{
+    mrp_resmgr_config_t *config;
+    mrp_resmgr_sources_t *sources;
+    mrp_htbl_config_t ncfg, icfg;
+    char stem[1024];
+
+    MRP_ASSERT(resmgr, "invalid argument");
+
+    config = mrp_resmgr_get_config(resmgr);
+    snprintf(stem, sizeof(stem), "%s/%s", config->confdir, config->confnams);
+
+    memset(&ncfg, 0, sizeof(ncfg));
+    ncfg.nentry = MRP_RESMGR_SOURCE_MAX;
+    ncfg.comp = mrp_string_comp;
+    ncfg.hash = mrp_string_hash;
+    ncfg.free = source_free;
+    ncfg.nbucket = MRP_RESMGR_SOURCE_BUCKETS;
+
+    memset(&icfg, 0, sizeof(icfg));
+    icfg.nentry = MRP_RESMGR_SOURCE_MAX;
+    icfg.comp = id_hash_compare;
+    icfg.hash = id_hash_function;
+    icfg.free = NULL;
+    icfg.nbucket = MRP_RESMGR_SOURCE_BUCKETS;
+
+    if ((sources = mrp_allocz(sizeof(mrp_resmgr_sources_t)))) {
+        sources->resmgr = resmgr;
+        sources->lookup.by_name = mrp_htbl_create(&ncfg);
+        sources->lookup.by_id = mrp_htbl_create(&icfg);
+        sources->decision_conf = load_decision_conf(stem);
+        sources->state_table.handle = MQI_HANDLE_INVALID;
+
+        mrp_resmgr_register_dependency(resmgr, MRP_RESMGR_SOURCE_STATE_TABLE,
+                                       source_status_changed_cb);
+    }
+
+    return sources;
+}
+
+
+void mrp_resmgr_sources_destroy(mrp_resmgr_sources_t *sources)
+{
+    if (sources) {
+        mrp_decision_conf_destroy(sources->decision_conf);
+
+        mrp_htbl_destroy(sources->lookup.by_id, false);
+        mrp_htbl_destroy(sources->lookup.by_name, true);
+
+        mrp_free(sources);
+    }
+}
+
+mrp_decision_conf_t *mrp_resmgr_sources_get_decision_conf(mrp_resmgr_t *resmgr)
+{
+    mrp_resmgr_sources_t *sources;
+
+    if (!resmgr || !(sources = mrp_resmgr_get_sources(resmgr)))
+        return NULL;
+
+    return sources->decision_conf;
+}
+
+mrp_resmgr_source_t *mrp_resmgr_source_add(mrp_resmgr_t *resmgr,
+                                           const char *gam_name,
+                                           uint16_t gam_id)
+{
+#define ATTR_MAX   MRP_RESMGR_USECASE_SIZE_MAX
+
+    mrp_resmgr_config_t *config;
+    mrp_resmgr_sources_t *sources;
+    mrp_resmgr_usecase_t *usecase;
+    mrp_resmgr_source_t *src;
+    char *gam_name_dup;
+    mrp_decision_conf_t *dc;
+    ssize_t nattr, i;
+    const char *attrs[ATTR_MAX];
+    ssize_t offs;
+    char stem[1024];
+
+    MRP_ASSERT(resmgr && gam_name, "invalid argument");
+
+    config = mrp_resmgr_get_config(resmgr);
+    sources = mrp_resmgr_get_sources(resmgr);
+    usecase = mrp_resmgr_get_usecase(resmgr);
+
+    MRP_ASSERT(config && sources && usecase, "internal error");
+
+    if (!(src = mrp_resmgr_source_find_by_name(resmgr, gam_name))) {
+        snprintf(stem, sizeof(stem), "%s/%s-%s-%d", config->confdir,
+                 config->prefix, gam_name, config->max_active);
+
+        dc = sources->decision_conf;
+        nattr = mrp_decision_attr_list(dc, attrs, ATTR_MAX);
+
+        if (dc && nattr > 0 &&
+            (src = mrp_allocz(sizeof(mrp_resmgr_source_t))) &&
+            (gam_name_dup = mrp_strdup(gam_name)))
+        {
+            src->sources = sources;
+            src->gam_source.name = gam_name_dup;
+            src->gam_source.id = gam_id;
+            mrp_list_init(&src->resources);
+
+            if (!mrp_htbl_insert(sources->lookup.by_name, gam_name_dup, src)) {
+                mrp_log_error("gam-resource-manager: attempt to add source "
+                              "'%s' multiple times", gam_name_dup);
+                goto failed;
+            }
+
+            for (i = 0;  i < nattr;  i++) {
+                offs = mrp_resmgr_usecase_add_attribute(usecase,src,attrs[i]);
+
+                if (offs < 0) {
+                    mrp_log_error("gam-resource-manager: failed to add "
+                                  "attribute '%s' to source '%s'",
+                                  attrs[i], gam_name);
+                }
+                else {
+                    if (!mrp_decision_set_attr_offset(dc, attrs[i], offs)) {
+                        mrp_log_error("gam-resource-manager: failed to set "
+                                      "offset for attribute '%s' in source "
+                                      "'%s'", attrs[i], gam_name);
+                    }
+                }
+            }
+
+            src->decision_tree = mrp_decision_tree_create_from_file(dc, stem);
+
+            if (!src->decision_tree) {
+                mrp_log_error("gam-resource-manager: source '%s' "
+                              "is nonfunctional", gam_name);
+            }
+        }
+    }
+
+    if (src) {
+        register_gam_id(src, gam_id);
+    }
+
+    return src;
+
+ failed:
+    if (src)
+        source_free((void *)src->gam_source.name, (void *)src);
+    return NULL;
+
+#undef ATTR_MAX
+}
+
+mrp_resmgr_source_t *mrp_resmgr_source_find_by_name(mrp_resmgr_t *resmgr,
+                                                    const char *gam_name)
+{
+    mrp_resmgr_sources_t *sources;
+
+    MRP_ASSERT(resmgr && gam_name, "invalid argument");
+
+    if (!(sources = mrp_resmgr_get_sources(resmgr)) ||
+        !(sources->lookup.by_name))
+    {
+        return NULL;
+    }
+
+    return mrp_htbl_lookup(sources->lookup.by_name, (void *)gam_name);
+}
+
+
+mrp_resmgr_source_t *mrp_resmgr_source_find_by_id(mrp_resmgr_t *resmgr,
+                                                  uint16_t gam_id)
+{
+    mrp_resmgr_sources_t *sources;
+
+    MRP_ASSERT(resmgr, "invalid argument");
+
+    if (!(sources = mrp_resmgr_get_sources(resmgr)) ||
+        !(sources->lookup.by_id))
+    {
+        return NULL;
+    }
+
+    return mrp_htbl_lookup(sources->lookup.by_id, NULL + gam_id);
+}
+
+
+
+
+const char *mrp_resmgr_source_get_name(mrp_resmgr_source_t *src)
+{
+    if (!src)
+        return "<invalid>";
+
+    return src->gam_source.name;
+}
+
+bool mrp_resmgr_source_get_availability(mrp_resmgr_source_t *src)
+{
+    if (!src)
+        return false;
+
+    return src->available;
+}
+
+
+mrp_resmgr_resource_t *mrp_resmgr_source_get_resource(mrp_resmgr_source_t *src,
+                                                      uint32_t connno)
+{
+    mrp_list_hook_t *entry, *n;
+    mrp_resmgr_resource_t *ar;
+
+    MRP_ASSERT(src, "invalid argument");
+
+    mrp_list_foreach(&src->resources, entry, n) {
+        ar = mrp_resmgr_backend_resource_list_entry(entry);
+        if (connno == mrp_resmgr_backend_get_resource_connno(ar))
+            return ar;
+    }
+
+    return NULL;
+}
+
+bool mrp_resmgr_source_add_resource(mrp_resmgr_source_t *src,
+                                    mrp_list_hook_t *ar_source_link)
+{
+    mrp_resmgr_resource_t *ar, *ar2;
+    uint32_t connno, connno2;
+    mrp_list_hook_t *entry, *n;
+    mrp_list_hook_t *insert_after;
+    bool success;
+
+    MRP_ASSERT(src && ar_source_link, "invalid argument");
+
+    ar = mrp_resmgr_backend_resource_list_entry(ar_source_link);
+    connno = mrp_resmgr_backend_get_resource_connno(ar);
+
+    success = true;
+    insert_after = &src->resources;
+
+    mrp_list_foreach(&src->resources, entry, n) {
+        ar2 = mrp_resmgr_backend_resource_list_entry(entry);
+        connno2 = mrp_resmgr_backend_get_resource_connno(ar2);
+
+        if (connno2 == connno) {
+            success = false;
+            break;
+        }
+
+        if (connno2 < connno)
+            insert_after = entry;
+    }
+
+    if (success)
+        mrp_list_insert_after(insert_after, ar_source_link);
+
+    return success;
+}
+
+int32_t mrp_resmgr_source_make_decision(mrp_resmgr_source_t *src)
+{
+    mrp_resmgr_t *resmgr;
+    mrp_resmgr_sources_t *sources;
+    mrp_resmgr_usecase_t *usecase;
+    void *input;
+    mrp_decision_value_type_t type;
+    mrp_decision_value_t *value;
+    int32_t decision;
+
+    MRP_ASSERT(src && src->sources && src->sources->resmgr,"invalid argument");
+
+    sources = src->sources;
+    resmgr  = sources->resmgr;
+    usecase = mrp_resmgr_get_usecase(resmgr);
+    input   = mrp_resmgr_usecase_get_decision_input(usecase);
+
+    if (!mrp_decision_make(src->decision_tree, input, &type, &value))
+        decision = -1;
+    else
+        decision = value->integer;
+
+    return decision;
+}
+
+static bool register_gam_id(mrp_resmgr_source_t *src, uint16_t gam_id)
+{
+    mrp_resmgr_sources_t *sources;
+
+    if (!(sources = src->sources))
+        return false;
+
+    if (!gam_id || gam_id == src->gam_source.id)
+        return true;
+
+    if (src->gam_source.id) {
+        mrp_log_error("gam-resource-manager: attempt to reset "
+                      "gam ID of '%s'", src->gam_source.name);
+        return false;
+    }
+
+    if (!mrp_htbl_insert(sources->lookup.by_id,  NULL+gam_id, src)) {
+        mrp_log_error("gam-resource-manager: attempt to add source "
+                      "%d multiple times", gam_id);
+        return false;
+    }
+
+    src->gam_source.id = gam_id;
+
+    mrp_debug("assign id %d to source '%s'", gam_id, src->gam_source.name);
+
+    return true;
+}
+
+static mrp_decision_conf_t *load_decision_conf(const char *stem)
+{
+    mrp_decision_conf_t *conf;
+
+    if (!(conf = mrp_decision_conf_create_from_file(stem))) {
+        mrp_log_error("gam-resource-manager: can't load "
+                      "decision conf file %s.names", stem);
+    }
+    else if (!decision_values_match(conf)) {
+        mrp_log_error("gam-resource-manager: decision values in conf file "
+                      "%s.name do not match their builtin counterpart", stem);
+
+        mrp_decision_conf_destroy(conf);
+        conf = NULL;
+    }
+
+    return conf;
+}
+
+static bool decision_values_match(mrp_decision_conf_t *conf)
+{
+    const char **names = mrp_resmgr_backend_get_decision_names();
+    int32_t i;
+
+    if (!conf)
+        return false;
+
+    for (i = 0;  names[i];  i++) {
+        if (strcmp(names[i], mrp_decision_name(conf, i)))
+            return false;
+    }
+
+    return (i > 0 && i == mrp_decision_value_max(conf));
+}
+
+static void source_free(void *key, void *object)
+{
+    mrp_resmgr_source_t *src = (mrp_resmgr_source_t *)object;
+
+    MRP_ASSERT(key && object, "internal error");
+    MRP_ASSERT(src->gam_source.name == (const char *)key, "corrupt data");
+    MRP_ASSERT(mrp_list_empty(&src->resources), "resource list not empty");
+
+    mrp_decision_tree_destroy(src->decision_tree);
+
+    mrp_free((void *)src->gam_source.name);
+
+    mrp_free(src);
+}
+
+static int id_hash_compare(const void *key1, const void *key2)
+{
+    if (key1 < key2)
+        return -1;
+    if (key1 > key2)
+        return 1;
+    return 0;
+}
+
+static uint32_t id_hash_function(const void *key)
+{
+    return (uint32_t)(key - (const void *)0);
+}
+
+
+static bool source_status_changed_cb(mrp_resmgr_t *resmgr)
+{
+    mrp_resmgr_sources_t *sources;
+    mrp_resmgr_source_t *src;
+    mqi_handle_t h;
+    int i, n;
+    row_t rows[MRP_RESMGR_SOURCE_MAX];
+    row_t *r;
+    int change_count;
+
+    MRP_ASSERT(resmgr, "invalid argument");
+
+    printf("### %s() called\n", __FUNCTION__);
+
+    if (!(sources = mrp_resmgr_get_sources(resmgr)) ||
+        !sources->lookup.by_name)
+        return false;
+
+    if ((h = get_table_handle(sources)) == MQI_HANDLE_INVALID) {
+        mrp_log_error("gam-resource-manager: can't update status changes: "
+                      "database error");
+        return false;
+    }
+
+    memset(rows, 0, sizeof(rows));
+
+    if ((n = MQI_SELECT(sources->state_table.cols, h, MQI_ALL, rows)) < 0) {
+        mrp_log_error("gam-resource-manager: select on table '%s' failed: %s",
+                      MRP_RESMGR_SOURCE_STATE_TABLE, strerror(errno));
+        return false;
+    }
+
+    if (n == 0)
+        return false;
+
+    for (change_count = i = 0;  i < n;  i++) {
+        r = rows + i;
+
+        if (!r->visible)
+            continue;
+
+        if ((src = mrp_htbl_lookup(sources->lookup.by_name, (void *)r->name))){
+            register_gam_id(src, r->id);
+
+            if (( r->available && !src->available) ||
+                (!r->available &&  src->available)  )
+            {
+                src->available = r->available;
+                change_count++;
+
+                mrp_debug("%s become %savailable", src->gam_source.name,
+                          src->available ? "":"un");
+            }
+        }
+    }
+
+    return change_count > 0;
+}
+
+static mqi_handle_t get_table_handle(mrp_resmgr_sources_t *sources)
+{
+#define COLUMN(_n, _t)  { # _n, mqi_ ## _t, MQI_OFFSET(row_t, _n) }
+
+    static column_def_t col_defs[COLUMN_MAX] = {
+        COLUMN( id       , integer ),
+        COLUMN( name     , string  ),
+        COLUMN( available, integer ),
+        COLUMN( visible  , integer ),
+    };
+
+    mqi_handle_t h;
+    column_def_t *def;
+    mqi_column_desc_t *desc;
+    int i;
+
+    if (sources->state_table.handle == MQI_HANDLE_INVALID) {
+        h = mqi_get_table_handle(MRP_RESMGR_SOURCE_STATE_TABLE);
+
+        if (h != MQI_HANDLE_INVALID) {
+            sources->state_table.handle = h;
+
+            for (i = 0;  i < COLUMN_MAX;  i++) {
+                def = col_defs + i;
+                desc = sources->state_table.cols + i;
+
+                if ((desc->cindex = mqi_get_column_index(h, def->name)) < 0) {
+                    mrp_log_error("gam-resource-manager: can't find column "
+                                  "'%s' in table '%s'", def->name,
+                                  MRP_RESMGR_SOURCE_STATE_TABLE);
+                    sources->state_table.handle = MQI_HANDLE_INVALID;
+                    break;
+                }
+
+                desc->offset = def->offset;
+            }
+
+            desc = sources->state_table.cols + i;
+            desc->cindex = -1;
+            desc->offset = -1;
+        }
+
+    }
+
+    return sources->state_table.handle;
+
+#undef COLUMN
+}
diff --git a/src/plugins/gam-resource-manager/source.h b/src/plugins/gam-resource-manager/source.h
new file mode 100644 (file)
index 0000000..44fc205
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_GAM_RESOURCE_MANAGER_SOURCE_H__
+#define __MURPHY_GAM_RESOURCE_MANAGER_SOURCE_H__
+
+#include "plugin-gam-resource-manager.h"
+#include "c5-decision-tree.h"
+
+
+mrp_resmgr_sources_t *mrp_resmgr_sources_create(mrp_resmgr_t *resmgr);
+void mrp_resmgr_sources_destroy(mrp_resmgr_sources_t *sources);
+
+mrp_decision_conf_t *mrp_resmgr_sources_get_decision_conf(mrp_resmgr_t *resmgr);
+
+mrp_resmgr_source_t *mrp_resmgr_source_add(mrp_resmgr_t *resmgr,
+                                           const char *gam_name,
+                                           uint16_t gam_id);
+mrp_resmgr_source_t *mrp_resmgr_source_find_by_name(mrp_resmgr_t *resmgr,
+                                           const char *gam_name);
+mrp_resmgr_source_t *mrp_resmgr_source_find_by_id(mrp_resmgr_t *resmgr,
+                                           uint16_t gam_id);
+
+const char *mrp_resmgr_source_get_name(mrp_resmgr_source_t *src);
+bool mrp_resmgr_source_get_availability(mrp_resmgr_source_t *src);
+
+mrp_resmgr_resource_t *mrp_resmgr_source_get_resource(mrp_resmgr_source_t *src,
+                                           uint32_t connno);
+
+bool mrp_resmgr_source_add_resource(mrp_resmgr_source_t *src,
+                                           mrp_list_hook_t *ar_source_link);
+int32_t mrp_resmgr_source_make_decision(mrp_resmgr_source_t *src);
+
+
+#endif /* __MURPHY_GAM_RESOURCE_MANAGER_SOURCE_H__ */
diff --git a/src/plugins/gam-resource-manager/usecase.c b/src/plugins/gam-resource-manager/usecase.c
new file mode 100644 (file)
index 0000000..8d11b3b
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <murphy/common.h>
+
+#include "usecase.h"
+#include "source.h"
+#include "sink.h"
+#include "backend.h"
+#include "c5-decision-tree.h"
+
+typedef enum   attr_type_e        attr_type_t;
+
+typedef struct update_s           update_t;
+typedef struct attr_def_s         attr_def_t;
+typedef struct entry_iterator_s   entry_iterator_t;
+typedef struct entry_descr_s      entry_descr_t;
+
+struct mrp_resmgr_usecase_s {
+    mrp_resmgr_t *resmgr;
+    size_t size;
+    int32_t *vector;
+    uint32_t nentry;
+    mrp_htbl_t *entries;
+    int nupdate;
+    update_t *updates;
+    int nstamp;
+    uint32_t *stamps;
+};
+
+
+struct update_s {
+    mrp_resmgr_source_t *source;
+    uint32_t connno;
+    int32_t attr_idx;
+    uint32_t vector_idx;
+    bool stamp;
+};
+
+enum attr_type_e {
+    REGULAR = 0,
+    STAMP,
+    ROUTE
+};
+
+struct attr_def_s {
+    const char *suffix;
+    size_t len;
+    const char *name;
+    int32_t idx;
+    attr_type_t type;
+};
+
+struct entry_descr_s {
+    const char *name;
+    int update_idx;
+};
+
+struct entry_iterator_s {
+    int maxentry;
+    int nentry;
+    entry_descr_t descs[0];
+};
+
+
+#define ATTR(_s,_n,_id,_b)    { _s,sizeof(_s)-1,_n,_id,_b }
+#define ATTR_END              { NULL, 0, NULL, 0, false }
+
+static attr_def_t attr_defs[] = {
+    ATTR( "Route", "sink_id", MRP_RESMGR_RESOURCE_FIELD_DECISION_ID, ROUTE   ),
+    ATTR( "Stamp", "stamp"  ,                    0                 , STAMP   ),
+    ATTR( "State", "state"  , MRP_RESMGR_RESOURCE_FIELD_STATE      , REGULAR ),
+    ATTR_END
+};
+
+#undef ATTR_END
+#undef ATTR
+
+
+
+static void hash_free(void *, void *);
+
+bool parse_name(const char *, const char **, uint32_t *, attr_def_t **,
+                char *, int);
+
+static uint32_t add_update_to_usecase(mrp_resmgr_usecase_t *, int32_t,
+                                      uint32_t, attr_type_t);
+static uint32_t add_entry_to_vector(mrp_resmgr_usecase_t *);
+static void add_stamp_to_usecase(mrp_resmgr_usecase_t *, uint32_t);
+static void register_sink_ids(mrp_resmgr_usecase_t *, const char *,
+                              mrp_resmgr_source_t *);
+
+static void normalise_stamps(int32_t *vector, int nstamp, uint32_t *stamps);
+
+
+static entry_iterator_t *entry_iterator(mrp_resmgr_usecase_t *usecase);
+
+
+
+mrp_resmgr_usecase_t *mrp_resmgr_usecase_create(mrp_resmgr_t *resmgr)
+{
+    mrp_resmgr_usecase_t *usecase;
+    mrp_htbl_config_t cfg;
+    MRP_ASSERT(resmgr, "invalid argument");
+
+    cfg.nentry = MRP_RESMGR_USECASE_SIZE_MAX;
+    cfg.comp = mrp_string_comp;
+    cfg.hash = mrp_string_hash;
+    cfg.free = hash_free;
+    cfg.nbucket = MRP_RESMGR_USECASE_SIZE_BUCKETS;
+
+    if ((usecase = mrp_allocz(sizeof(mrp_resmgr_usecase_t)))) {
+        usecase->resmgr = resmgr;
+        usecase->size = 0;
+        usecase->vector = NULL;
+        usecase->entries = mrp_htbl_create(&cfg);
+    }
+
+    return usecase;
+}
+
+void mrp_resmgr_usecase_destroy(mrp_resmgr_usecase_t *usecase)
+{
+    if (usecase) {
+        mrp_htbl_destroy(usecase->entries, true);
+        mrp_free(usecase->updates);
+        mrp_free(usecase->vector);
+        mrp_free(usecase);
+    }
+}
+
+
+ssize_t mrp_resmgr_usecase_add_attribute(mrp_resmgr_usecase_t *usecase,
+                                         mrp_resmgr_source_t *source,
+                                         const char *name)
+{
+    const char *srcnam;
+    uint32_t connno;
+    attr_def_t *atd;
+    ssize_t offs;
+    void *void_idx;
+    int32_t attr_idx;
+    uint32_t idx;
+    uint32_t vect_idx;
+    uint32_t upd_idx;
+    update_t *upd;
+    char buf[256];
+
+    MRP_ASSERT(usecase && source && name, "invalid attribute");
+
+    offs = -1;
+    atd = NULL;
+
+    if (parse_name(name, &srcnam, &connno, &atd, buf,sizeof(buf))) {
+
+        if (!(void_idx = mrp_htbl_lookup(usecase->entries, (void *)name))) {
+            if ((attr_idx = atd->idx) >= 0) {
+                idx = mrp_resmgr_backend_get_attribute_index(atd->name,
+                                                             mqi_integer);
+                if (idx != MRP_RESMGR_RESOURCE_FIELD_INVALID)
+                    attr_idx = idx;
+                else {
+                    mrp_log_error("gam-resource-manager: can't obtain index "
+                                  "for resource attribute '%s'", atd->name);
+                    goto out;
+                }
+            }
+
+            vect_idx = add_entry_to_vector(usecase);
+            upd_idx = add_update_to_usecase(usecase, attr_idx, vect_idx,
+                                            atd->type);
+            upd = usecase->updates + upd_idx;
+            void_idx = NULL + (upd_idx + 1);
+
+            if (!mrp_htbl_insert(usecase->entries,mrp_strdup(name),void_idx)) {
+                mrp_log_error("gam-resource-manager: failed to insert into "
+                              "hashtable usecase attribute '%s'", name);
+            }
+
+            if (atd->type == STAMP)
+                add_stamp_to_usecase(usecase, vect_idx);
+        }
+        else {
+            upd_idx = (void_idx - NULL) - 1;
+            upd = usecase->updates + upd_idx;
+
+            MRP_ASSERT(usecase->updates && (int)upd_idx < usecase->nupdate,
+                       "corrupt data structures");
+
+            vect_idx = upd->vector_idx;
+        }
+
+        if (!strcmp(srcnam, mrp_resmgr_source_get_name(source))) {
+            upd->source = source;
+            upd->connno = connno;
+        }
+
+        if (atd && atd->type == ROUTE)
+            register_sink_ids(usecase, name, source);
+
+        offs = sizeof(usecase->vector[0]) * vect_idx;
+    }
+
+ out:
+    return offs;
+}
+
+
+
+
+void mrp_resmgr_usecase_update(mrp_resmgr_usecase_t *usecase)
+{
+    update_t *upd;
+    mrp_resmgr_resource_t *ar;
+    int32_t *val;
+    int32_t idx;
+    int i;
+
+    MRP_ASSERT(usecase, "invalid argument");
+
+    for (i =0;  i < usecase->nupdate;  i++) {
+        upd = usecase->updates + i;
+        val = usecase->vector + upd->vector_idx;
+
+        if (!(ar = mrp_resmgr_source_get_resource(upd->source, upd->connno)))
+            *val = 0;
+        else {
+            if ((idx = upd->attr_idx) >= 0)
+                *val = mrp_resmgr_backend_get_integer_attribute(ar, idx);
+            else {
+                switch (idx) {
+                case MRP_RESMGR_RESOURCE_FIELD_STATE:
+                    *val = mrp_resmgr_backend_get_resource_state(ar);
+                    break;
+                case MRP_RESMGR_RESOURCE_FIELD_DECISION_ID:
+                    *val = mrp_resmgr_backend_get_resource_decision_id(ar);
+                    break;
+                default:
+                    *val = 0;
+                    break;
+                }
+            }
+        }
+    }
+
+    normalise_stamps(usecase->vector, usecase->nstamp, usecase->stamps);
+}
+
+void *mrp_resmgr_usecase_get_decision_input(mrp_resmgr_usecase_t *usecase)
+{
+    if (!usecase)
+        return NULL;
+
+    return (void *)usecase->vector;
+}
+
+
+size_t mrp_usecase_print(mrp_resmgr_usecase_t *usecase, char *buf, size_t len)
+{
+#define PRINT(args...) do { if (p<e) p += snprintf(p, e-p, args); } while(0)
+
+    mrp_decision_conf_t *conf;
+    entry_iterator_t *it;
+    entry_descr_t *desc;
+    const char *name;
+    update_t *upd;
+    int32_t value;
+    const char *value_str;
+    char *p, *e;
+    char nbuf[256];
+    bool er;
+    int i;
+
+    conf = mrp_resmgr_sources_get_decision_conf(usecase->resmgr);
+
+    snprintf(buf, len, "<no entries>");
+
+    e = (p = buf) + len;
+
+    if (usecase && buf && len > 0 && (it = entry_iterator(usecase))) {
+        for (i = 0;  i < it->nentry;  i++) {
+            desc  = it->descs + i;
+            name  = desc->name;
+            upd   = usecase->updates + desc->update_idx;
+            value = usecase->vector[upd->vector_idx];
+
+            snprintf(nbuf, sizeof(nbuf), "%s:", name);
+
+            value_str = mrp_decision_get_integer_attr_name(conf,name,value,&er);
+
+            PRINT("%-24s %s (%d)\n", nbuf, value_str, value);
+        }
+
+        mrp_free(it);
+    }
+
+    return p - buf;
+
+#undef PRINT
+}
+
+
+static void hash_free(void *key, void *object)
+{
+    MRP_UNUSED(object);
+    mrp_free(key);
+}
+
+/*
+ * the syntax of a regular attribute name is as follows:
+ *
+ *   - source name, eg. navigator
+ *
+ *   - optional digits specifying the connection number
+ *     For the first connection, no digits are presents.
+ *     Numbering of subsequent connections start from 2.
+ *
+ *   - suffix that actually defines the type of the attribute
+ *     suffix names and the associated type definitions are
+ *     in the filed_defs[] array.
+ */
+bool parse_name(const char *name, const char **source, uint32_t *connno,
+                attr_def_t **attrdef, char *buf, int len)
+{
+    attr_def_t *d;
+    char *suffix;
+    char *p;
+    uint32_t n;
+    int l;
+
+    MRP_ASSERT(name && source && connno && attrdef && buf && len > 0,
+               "invalid arument");
+
+    *source  = NULL;
+    *connno  = 0;
+    *attrdef = NULL;
+
+    if (len < (l = strlen(name)) + 1)
+        return false;
+
+    memcpy(buf, name, l+1);
+    buf[l] = 0;
+
+
+    for (d = attr_defs;  d->suffix;  d++) {
+        if ((suffix = strstr(buf, d->suffix)) && !suffix[d->len]) {
+            *suffix = 0;
+
+            for(n = 0, p = suffix - 1;  p >= buf && isdigit(*p);  p++)
+                ;
+            if (++p < suffix) {
+                if ((n = strtoul(p, NULL, 10)) < 2)
+                    return false;
+                *p = 0;
+            }
+
+            if (p <= buf)
+                return false;
+
+            *source  = buf;
+            *connno  = n;
+            *attrdef = d;
+
+            return true;
+        }
+    }
+
+    return false;
+}
+
+
+static uint32_t add_update_to_usecase(mrp_resmgr_usecase_t *usecase,
+                                      int32_t attr_idx,
+                                      uint32_t vector_idx,
+                                      attr_type_t type)
+{
+    uint32_t i = usecase->nupdate++;
+    size_t size = sizeof(update_t) * usecase->nupdate;
+    update_t *u;
+
+    usecase->updates = mrp_realloc(usecase->updates, size);
+
+    MRP_ASSERT(usecase->updates, "can't allocate memory");
+
+    u = usecase->updates + i;
+    u->source     = NULL;
+    u->connno     = 0;
+    u->attr_idx   = attr_idx;
+    u->vector_idx = vector_idx;
+    u->stamp      = (type == STAMP);
+
+    return i;
+}
+
+static uint32_t add_entry_to_vector(mrp_resmgr_usecase_t *usecase)
+{
+    uint32_t idx = usecase->nentry++;
+    size_t size = sizeof(int32_t) * usecase->nentry;
+
+    usecase->vector = mrp_realloc(usecase->vector, size);
+
+    MRP_ASSERT(usecase->vector, "can't allocate memory");
+
+    usecase->vector[idx] = 0;
+
+    return idx;
+}
+
+static void add_stamp_to_usecase(mrp_resmgr_usecase_t *usecase,
+                                 uint32_t vector_idx)
+{
+    uint32_t idx = usecase->nstamp++;
+    size_t size = sizeof(uint32_t) * usecase->nstamp;
+
+    usecase->stamps = mrp_realloc(usecase->stamps, size);
+
+    MRP_ASSERT(usecase->stamps, "can't allocate memory");
+
+    usecase->stamps[idx] = vector_idx;
+}
+
+
+static void register_sink_ids(mrp_resmgr_usecase_t *usecase,
+                              const char *attrnam,
+                              mrp_resmgr_source_t *source)
+{
+    mrp_resmgr_t *resmgr;
+    mrp_decision_conf_t *conf;
+    ssize_t i, nvalue;
+    mrp_decision_attr_value_desc_t values[MRP_RESMGR_SINK_MAX];
+    mrp_decision_attr_value_desc_t *dsc;
+
+    resmgr = usecase->resmgr;
+
+    if (source) {
+        conf = mrp_resmgr_sources_get_decision_conf(usecase->resmgr);
+        nvalue = mrp_decision_attr_value_list(conf, attrnam, values,
+                                              MRP_RESMGR_SINK_MAX);
+        for (i = 0;  i < nvalue;  i++) {
+            dsc = values + i;
+
+            if (!mrp_resmgr_sink_add(resmgr, dsc->name, dsc->value, source)) {
+                mrp_log_error("gam-resource-manager: failed to add sink '%s' "
+                              "with decision id %d", dsc->name, dsc->value);
+            }
+        }
+    }
+}
+
+static void normalise_stamps(int32_t *vector, int nstamp, uint32_t *stamps)
+{
+    int sort[MRP_RESMGR_USECASE_SIZE_MAX];
+    int32_t val, tmp;
+    int i,j,n;
+
+    if (nstamp > 0 && nstamp < MRP_RESMGR_USECASE_SIZE_MAX) {
+        for (n = i = 0;  i < nstamp;  i++) {
+            j = stamps[i];
+
+            MRP_ASSERT(j >= 0 && j < MRP_RESMGR_USECASE_SIZE_MAX,
+                       "invalid stamp index");
+
+            if (!(val = vector[j]))
+                continue;
+
+            if (val < 0)
+                vector[j] = 0;
+            else
+                sort[n++] = j;
+        }
+
+        for (i = 0;  i < n-1;  i++) {
+            for (j = i + 1;  j < n;  j++) {
+                if (vector[sort[i]] > vector[sort[j]]) {
+                    tmp = sort[i];
+                    sort[i] = sort[j];
+                    sort[j] = tmp;
+                }
+            }
+        }
+
+        for (i = 0;  i < n;  i++)
+            vector[sort[i]] = i + 1;
+    }
+}
+
+static int entry_iterator_cb(void *key, void *object, void *user_data)
+{
+    entry_iterator_t *it = (entry_iterator_t *)user_data;
+    const char *name = (const char *)key;
+    int update_idx = (object - NULL) - 1;
+    entry_descr_t *desc;
+
+    if (it->nentry < it->maxentry) {
+        desc = it->descs + it->nentry++;
+        desc->name = name;
+        desc->update_idx = update_idx;
+    }
+
+    return MRP_HTBL_ITER_MORE;
+}
+
+static entry_iterator_t *entry_iterator(mrp_resmgr_usecase_t *usecase)
+{
+    entry_iterator_t *it;
+    size_t size;
+    entry_descr_t tmp;
+    int i,j;
+
+
+    if (!usecase)
+        it = NULL;
+    else {
+        size = sizeof(*it) + (sizeof(entry_descr_t) * usecase->nentry);
+
+        if ((it = mrp_allocz(size))) {
+            it->maxentry = usecase->nentry;
+            mrp_htbl_foreach(usecase->entries, entry_iterator_cb, it);
+        }
+
+        for (i = 0;  i < it->nentry - 1;  i++) {
+            for (j = i + 1;  j < it->nentry;  j++) {
+                if (it->descs[i].update_idx > it->descs[j].update_idx) {
+                    tmp = it->descs[i];
+                    it->descs[i] = it->descs[j];
+                    it->descs[j] = tmp;
+                }
+            }
+        }
+    }
+
+    return it;
+}
diff --git a/src/plugins/gam-resource-manager/usecase.h b/src/plugins/gam-resource-manager/usecase.h
new file mode 100644 (file)
index 0000000..15166d4
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *  * Neither the name of Intel Corporation nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_GAM_RESOURCE_MANAGER_USECASE_H__
+#define __MURPHY_GAM_RESOURCE_MANAGER_USECASE_H__
+
+#include "plugin-gam-resource-manager.h"
+
+mrp_resmgr_usecase_t *mrp_resmgr_usecase_create(mrp_resmgr_t *resmgr);
+void mrp_resmgr_usecase_destroy(mrp_resmgr_usecase_t *usecase);
+
+ssize_t mrp_resmgr_usecase_add_attribute(mrp_resmgr_usecase_t *usecase,
+                                         mrp_resmgr_source_t *source,
+                                         const char *name);
+
+void mrp_resmgr_usecase_update(mrp_resmgr_usecase_t *usecase);
+void *mrp_resmgr_usecase_get_decision_input(mrp_resmgr_usecase_t *usecase);
+
+
+size_t mrp_usecase_print(mrp_resmgr_usecase_t *usecase, char *buf, size_t len);
+
+
+
+#endif /* __MURPHY_GAM_RESOURCE_MANAGER_USECASE_H__ */