BAL and Maple Release 2.2

Signed-off-by: Shad Ansari <developer@Carbon.local>
diff --git a/bal_release/src/common/db_engine/Makefile b/bal_release/src/common/db_engine/Makefile
new file mode 100644
index 0000000..23b64f6
--- /dev/null
+++ b/bal_release/src/common/db_engine/Makefile
@@ -0,0 +1,36 @@
+###############################################################################
+#
+#  <:copyright-BRCM:2016:DUAL/GPL:standard
+#  
+#     Copyright (c) 2016 Broadcom
+#     All Rights Reserved
+#  
+#  Unless you and Broadcom execute a separate written software license
+#  agreement governing use of this software, this software is licensed
+#  to you under the terms of the GNU General Public License version 2
+#  (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+#  with the following added to such license:
+#  
+#     As a special exception, the copyright holders of this software give
+#     you permission to link this software with independent modules, and
+#     to copy and distribute the resulting executable under terms of your
+#     choice, provided that you also meet, for each linked independent
+#     module, the terms and conditions of the license of that module.
+#     An independent module is a module which is not derived from this
+#     software.  The special exception does not apply to any modifications
+#     of the software.
+#  
+#  Not withstanding the above, under no circumstances may you combine
+#  this software in any way with any other Broadcom software provided
+#  under a license other than the GPL, without Broadcom's express prior
+#  written consent.
+#  
+#  :>
+#
+###############################################################################
+# Data Base engine
+#
+MOD_NAME = db
+MOD_TYPE = lib
+
+srcs = bcm_db_engine.c
diff --git a/bal_release/src/common/db_engine/bcm_db_engine.c b/bal_release/src/common/db_engine/bcm_db_engine.c
new file mode 100644
index 0000000..dc4f19a
--- /dev/null
+++ b/bal_release/src/common/db_engine/bcm_db_engine.c
@@ -0,0 +1,1606 @@
+/******************************************************************************
+ *
+ *  <:copyright-BRCM:2016:DUAL/GPL:standard
+ *  
+ *     Copyright (c) 2016 Broadcom
+ *     All Rights Reserved
+ *  
+ *  Unless you and Broadcom execute a separate written software license
+ *  agreement governing use of this software, this software is licensed
+ *  to you under the terms of the GNU General Public License version 2
+ *  (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+ *  with the following added to such license:
+ *  
+ *     As a special exception, the copyright holders of this software give
+ *     you permission to link this software with independent modules, and
+ *     to copy and distribute the resulting executable under terms of your
+ *     choice, provided that you also meet, for each linked independent
+ *     module, the terms and conditions of the license of that module.
+ *     An independent module is a module which is not derived from this
+ *     software.  The special exception does not apply to any modifications
+ *     of the software.
+ *  
+ *  Not withstanding the above, under no circumstances may you combine
+ *  this software in any way with any other Broadcom software provided
+ *  under a license other than the GPL, without Broadcom's express prior
+ *  written consent.
+ *  
+ *  :>
+ *
+ *****************************************************************************/
+ 
+/*
+ * Data Base Engine
+ *
+ * Proprietary and confidential.
+ */
+#include <bcmos_system.h>
+#include <bcm_db_engine.h>
+
+/* DB trace level */
+bcmos_trace_level bcmdb_trace_level = BCMOS_TRACE_LEVEL_ERROR;
+
+#define BCMDB_TRACE(level, fmt, args...)        \
+    do {                                        \
+        if (level <= bcmdb_trace_level)     \
+             bcmos_trace(level, "%s#%d> " fmt, __FUNCTION__, __LINE__, ## args);    \
+    } while (0)
+
+/* Print error trace conditionally, depending on the current trace level */
+#define BCMDB_TRACE_ERROR(fmt, args...)        \
+    BCMDB_TRACE(BCMOS_TRACE_LEVEL_ERROR, "DB ERR: %s#%d> " fmt, __FUNCTION__, __LINE__, ## args)
+
+/* Print warning trace conditionally, depending on the current trace level */
+#define BCMDB_TRACE_WARNING(fmt, args...)        \
+    BCMDB_TRACE(BCMOS_TRACE_LEVEL_WARNING, "DB WARN: %s#%d> " fmt, __FUNCTION__, __LINE__, ## args)
+
+/* Print info trace conditionally, depending on the current trace level */
+#define BCMDB_TRACE_INFO(fmt, args...)        \
+    BCMDB_TRACE(BCMOS_TRACE_LEVEL_INFO, "DB INFO: %s#%d> " fmt, __FUNCTION__, __LINE__, ## args)
+
+/* Enable debug prints */
+#define BCMDB_DEBUG
+
+#ifdef BCMDB_DEBUG
+
+/* Print debug trace conditionally, depending on the current trace level */
+#define BCMDB_TRACE_DEBUG(fmt, args...)        \
+    BCMDB_TRACE(BCMOS_TRACE_LEVEL_DEBUG, "DB DBG: %s#%d> " fmt, __FUNCTION__, __LINE__, ## args)
+
+#else
+
+#define BCMDB_TRACE_DEBUG(fmt, args...)
+
+#endif
+
+
+
+/** Data base entry
+ * \ingroup bcmdb
+ */
+struct bcmdb_entry
+{
+    void *data;       /* Set or record */
+    uint8_t flags;
+#define BCMDB_FLAG_VALID    0x01  /**< Entry is valid */
+#define BCMDB_FLAG_RECORD   0x02  /**< Record */
+#define BCMDB_FLAG_SOS      0x10  /**< Set of sets */
+    uint8_t read_count;
+    uint8_t write_pending;
+    uint8_t ffu;
+};
+
+/** Set/Record change notification
+ * \ingroup bcmdb
+ */
+typedef struct bcmdb_notify
+{
+    struct bcmdb_notify *next;
+    bcmdb_notify_cb cb;
+    long cb_priv;
+} bcmdb_notify;
+
+/** Data Base Set control block
+ * \ingroup bcmdb
+ */
+struct bcmdb_set
+{
+    bcmdb_entry entry;        /**< Common fields for set and record */
+    char *name;                 /**< Set name */
+    bcmdb_set *parent;        /**< Set parent */
+    bcmdb_key my_key;         /**< Key in the parent set */
+    int max_entries;            /**< Max number of elements in the set. -1=inlimited */
+    int num_entries;            /**< Current number of elements in the set */
+    int entry_size;             /**< Set entry size. */
+    int magic;                  /**< Magic number */
+#define BCMDB_MAGIC_ACTIVE_SET         (('a'<<24) | ('s'<<16) | ('e'<<8) | 't')
+#define BCMDB_MAGIC_FREE_SET           (('f'<<24) | ('s'<<16) | ('e'<<8) | 't')
+
+    /** Get next element */
+    bcmdb_key (*entry_get_next)(const bcmdb_set *this, bcmdb_key cur);
+
+    /** Add new entry. returns 0 if ok */
+    int (*entry_new)(bcmdb_set *this, bcmdb_key key, const void *data);
+
+    /** Delete entry */
+    int (*entry_delete)(bcmdb_set *this, bcmdb_entry *entry);
+
+    /*
+     * Handle – key mapping
+     */
+
+    /** Convert entry handle to entry key */
+    bcmdb_key (*handle_to_key)(const bcmdb_set *this, const bcmdb_entry *entry);
+
+    /** Convert entry key to entry handle */
+    bcmdb_entry *(*key_to_handle)(const bcmdb_set *this, bcmdb_key key);
+
+    /*
+     * Set/Record locking
+     * entry is handle of the set or record to be locked/unlocked
+     */
+
+    /** Lock the whole set for reading */
+    void (*lock_set_read)(bcmdb_set *set);
+
+    /** Unlock set locked for reading */
+    void (*unlock_set_read)(bcmdb_set *set);
+
+    /** Lock the whole set for update */
+    long (*lock_set_modify)(bcmdb_set *set);
+
+    /** Unlock set locked for update */
+    void (*unlock_set_modify)(bcmdb_set *set, long fastlock_flags);
+
+    /** Lock the set recursively for update (SoS only) */
+    void (*lock_set_recursively_modify)(bcmdb_set *set);
+
+    /** Unlock recursively set locked for update (SoS only)  */
+    void (*unlock_set_recursively_modify)(bcmdb_set *set);
+
+    /** Lock record for reading */
+    void *(*lock_record_read)(bcmdb_set *set, bcmdb_key key);
+
+    /** Release read lock */
+    void (*unlock_record_read)(bcmdb_set *set, bcmdb_key key);
+
+    /** Lock record for modification. */
+    void *(*lock_record_write)(bcmdb_set *set, bcmdb_key key, int is_deletion);
+
+    /** Release modify lock */
+    void (*unlock_record_write)(bcmdb_set *set, int is_deletion, int is_cancellation);
+
+    /** Format function that converts record data to human-readable form */
+    bcmdb_format_cb format;
+
+    /** Update notification mechanism.\n
+     * Note that notification functions are called before the actual update of the data base, so that
+     * there is an option to abort the update if needed.
+     */
+    bcmdb_notify *notify_list_head;
+
+    /** Shadow data record */
+    void *shadow_data;
+
+    /** holds the pointer to a write-locked entry */
+    bcmdb_entry *write_locked_entry ;
+
+    /** mutex that prevents multiple simultaneous updates */
+    bcmos_mutex mutex_update;
+
+    /** semaphore that holds update while there are unfinished reads */
+    bcmos_sem sem_wait_read_to_finish;
+
+    /** fastlock */
+    bcmos_fastlock fastlock;
+};
+
+
+/**< Data base record
+ * \ingroup bcmdb
+ */
+struct bcmdb_record
+{
+    struct bcmdb_entry e;       /**< Entry - common part for set and record */
+};
+
+/*
+ * DB backend callbacks
+ */
+
+/*
+ * Array-based backend
+ */
+
+/** Get next element */
+static bcmdb_key _bcmdb_array_entry_get_next(const bcmdb_set *this, bcmdb_key key)
+{
+    BUG_ON(!this->entry.data);
+
+    if (key < 0)
+        key = 0;
+    else
+        ++key;
+
+    while((unsigned)key<this->max_entries)
+    {
+        bcmdb_entry *entry = (bcmdb_entry *)this->entry.data + key;
+        if ((entry->flags & BCMDB_FLAG_VALID))
+            break;
+        ++key;
+    }
+
+    if ((unsigned)key >= this->max_entries)
+        return BCMDB_KEY_NO_MORE;  /* no more */
+
+    return key;
+}
+
+/*
+ * Handle – key mapping
+ */
+
+/** Convert entry handle to entry key */
+static inline bcmdb_key _bcmdb_array_handle_to_key(const bcmdb_set *this, const bcmdb_entry *entry)
+{
+    bcmdb_key key;
+
+    BUG_ON(!this);
+    BUG_ON(!entry);
+    BUG_ON(!this->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!this->entry.data);
+
+    key = entry - (bcmdb_entry *)this->entry.data;
+    if ((unsigned)key >= this->max_entries)
+        return BCMDB_KEY_INVAL;
+
+    return key;
+}
+
+
+/** Convert entry key to entry handle */
+static inline bcmdb_entry *_bcmdb_array_key_to_handle(const bcmdb_set *this, bcmdb_key key)
+{
+    BUG_ON(!this);
+    BUG_ON(!this->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!this->entry.data);
+
+    if ((unsigned long)key >= this->max_entries)
+        return NULL;
+
+    return (bcmdb_entry *)this->entry.data + key;
+}
+
+
+/** sem-based set read-lock */
+static void bcmdb_set_lock_read_sem(bcmdb_set *set)
+{
+    bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+}
+
+
+/** sem-based set read-unlock */
+static void bcmdb_set_unlock_read_sem(bcmdb_set *set)
+{
+    bcmos_mutex_unlock(&set->mutex_update);
+}
+
+
+/** sem-based set modify-lock */
+static long bcmdb_set_lock_modify_sem(bcmdb_set *set)
+{
+    bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+    return 0;
+}
+
+
+/** sem-based set modify-unlock */
+static void bcmdb_set_unlock_modify_sem(bcmdb_set *set, long fastlock_flags)
+{
+    bcmos_mutex_unlock(&set->mutex_update);
+}
+
+
+/** helper function which recursively locks all subsets of SoS
+ *  for modify */
+static void bcmdb_recursive_subsets_lock_modify(bcmdb_set *sos)
+{
+    int key;
+    int left_entries = sos->num_entries;
+    bcmdb_entry *entry;
+    bcmdb_set *subset;
+
+    BUG_ON(!(sos->entry.flags & BCMDB_FLAG_SOS));
+
+    for (key = 0; key < sos->max_entries; ++key)
+    {
+        entry = sos->key_to_handle(sos, key);
+
+        if ((entry->flags & BCMDB_FLAG_VALID))
+        {
+            subset = (bcmdb_set *)entry->data;
+            subset->lock_set_recursively_modify(subset);
+
+            --left_entries;
+            /* if we handled all subsets we can break the "for" */
+            if (left_entries==0)
+            {
+                break;
+            }
+        }
+    }
+}
+
+
+/** helper function which recursively unlocks all subsets of SoS
+ *  for modify */
+static void bcmdb_recursive_subsets_unlock_modify(bcmdb_set *sos)
+{
+    int key;
+    int left_entries = sos->num_entries;
+    bcmdb_entry *entry;
+    bcmdb_set *subset;
+
+    BUG_ON(!(sos->entry.flags & BCMDB_FLAG_SOS));
+
+    for (key = 0; key < sos->max_entries; ++key)
+    {
+        entry = sos->key_to_handle(sos, key);
+
+        if ((entry->flags & BCMDB_FLAG_VALID))
+        {
+            subset = (bcmdb_set *)entry->data;
+            subset->unlock_set_recursively_modify(subset);
+
+            --left_entries;
+            /* if we handled all subsets we can break the "for" */
+            if (left_entries==0)
+            {
+                break;
+            }
+        }
+    }
+}
+
+
+/** sem-based set modify-lock recursively */
+static void bcmdb_set_lock_recursively_modify_sem(bcmdb_set *set)
+{
+    BCMDB_TRACE_DEBUG("lock set modify recursively: %s\n", set->name);
+    bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+    BCMDB_TRACE_DEBUG("mutex was locked\n");
+
+    if ((set->entry.flags & BCMDB_FLAG_SOS))
+    {
+        BCMDB_TRACE_DEBUG("going to lock the subsets\n");
+        bcmdb_recursive_subsets_lock_modify(set);
+    }
+}
+
+
+/** sem-based set modify-unlock recursively */
+static void bcmdb_set_unlock_recursively_modify_sem(bcmdb_set *set)
+{
+    BCMDB_TRACE_DEBUG("unlock set modify recursively: %s\n", set->name);
+
+    if ((set->entry.flags & BCMDB_FLAG_SOS))
+    {
+        BCMDB_TRACE_DEBUG("going to unlock the subsets\n");
+        bcmdb_recursive_subsets_unlock_modify(set);
+    }
+
+    bcmos_mutex_unlock(&set->mutex_update);
+    BCMDB_TRACE_DEBUG("mutex was unlocked\n");
+}
+
+
+/** NB-read-sem-write policy: set read lock */
+static void bcmdb_set_lock_read__nb_read_sem_write(bcmdb_set *set)
+{
+    long flags;
+
+    BCMDB_TRACE_DEBUG("lock set read: %s\n", set->name);
+    flags = bcmos_fastlock_lock(&set->fastlock);
+    ++set->entry.read_count;
+    BCMDB_TRACE_DEBUG("read_count is now: %u\n", set->entry.read_count);
+    bcmos_fastlock_unlock(&set->fastlock, flags);
+}
+
+
+/** NB-read-sem-write policy: set read unlock */
+static void bcmdb_set_unlock_read__nb_read_sem_write(bcmdb_set *set)
+{
+    long flags;
+
+    BCMDB_TRACE_DEBUG("unlock set read: %s\n", set->name);
+
+    flags = bcmos_fastlock_lock(&set->fastlock);
+    BUG_ON(!set->entry.read_count);
+    if (!(--set->entry.read_count) &&
+        set->entry.write_pending)
+    {
+        BCMDB_TRACE_DEBUG("going to wake pending writer\n");
+
+        set->entry.write_pending = 0;
+        bcmos_fastlock_unlock(&set->fastlock, flags);
+        bcmos_sem_post(&set->sem_wait_read_to_finish);
+    }
+    else
+    {
+        bcmos_fastlock_unlock(&set->fastlock, flags);
+    }
+}
+
+
+/** NB-read-sem-write policy: set modify lock */
+static long bcmdb_set_lock_modify__nb_read_sem_write(bcmdb_set *set)
+{
+    long flags;
+
+    BCMDB_TRACE_DEBUG("lock set modify: %s\n", set->name);
+    bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+    BCMDB_TRACE_DEBUG("mutex was locked\n");
+
+    while(1)
+    {
+        flags = bcmos_fastlock_lock(&set->fastlock);
+        if (!set->entry.read_count)
+            break;
+        /* Wait until read is completed */
+        set->entry.write_pending = 1;
+        bcmos_fastlock_unlock(&set->fastlock, flags);
+        bcmos_sem_wait(&set->sem_wait_read_to_finish, BCMOS_WAIT_FOREVER);
+    }
+    /* At this point fastlock is taken and there are no pending reads */
+
+    BCMDB_TRACE_DEBUG("fastlock is taken, no active reads\n");
+
+    return flags;
+}
+
+
+/** NB-read-sem-write policy: set modify unlock */
+static void bcmdb_set_unlock_modify__nb_read_sem_write(bcmdb_set *set, long fastlock_flags)
+{
+    BCMDB_TRACE_DEBUG("unlock set modify: %s\n", set->name);
+    bcmos_fastlock_unlock(&set->fastlock, fastlock_flags);
+    bcmos_mutex_unlock(&set->mutex_update);
+    BCMDB_TRACE_DEBUG("mutex was unlocked\n");
+}
+
+
+/** sem-read/sem-write policy: lock entry */
+static void *bcmdb_sem_read_sem_write_lock(bcmdb_set *set, bcmdb_key key, int is_deletion)
+{
+    bcmdb_entry *entry;
+    bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+
+    if (is_deletion)
+    {
+        /* there is nothing to return in deletion case */
+        return NULL;
+    }
+
+    entry = set->key_to_handle(set, key);
+    if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+    {
+        bcmos_mutex_unlock(&set->mutex_update);
+        return NULL;
+    }
+    return entry->data;
+}
+
+/** sem-read/sem-write policy: unlock entry */
+static void bcmdb_sem_read_sem_write_unlock(bcmdb_set *set)
+{
+    bcmos_mutex_unlock(&set->mutex_update);
+}
+
+/** sem-read/sem-write policy: lock entry for reading */
+static void *bcmdb_sem_read_sem_write_lock_read(bcmdb_set *set, bcmdb_key key)
+{
+    return bcmdb_sem_read_sem_write_lock(set, key, 0) ;
+}
+
+/** sem-read/sem-write policy: unlock entry for reading */
+static void bcmdb_sem_read_sem_write_unlock_read(bcmdb_set *set, bcmdb_key key)
+{
+    bcmdb_sem_read_sem_write_unlock(set);
+}
+
+/** sem-read/sem-write policy: lock entry for writing */
+static void *bcmdb_sem_read_sem_write_lock_write(bcmdb_set *set, bcmdb_key key, int is_deletion)
+{
+    return bcmdb_sem_read_sem_write_lock(set, key, is_deletion) ;
+}
+
+/** sem-read/sem-write policy: unlock entry for writing */
+static void bcmdb_sem_read_sem_write_unlock_write(bcmdb_set *set, int is_deletion, int is_cancellation)
+{
+    bcmdb_sem_read_sem_write_unlock(set);
+}
+
+/** non-blocking-read/shadow write policy: Lock entry for reading */
+static void *bcmdb_nb_read_shadow_write_lock_read(bcmdb_set *set, bcmdb_key key)
+{
+    bcmdb_entry *entry;
+    long flags;
+
+    flags = bcmos_fastlock_lock(&set->fastlock);
+    entry = set->key_to_handle(set, key);
+    if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+    {
+        bcmos_fastlock_unlock(&set->fastlock, flags);
+        return NULL;
+    }
+    ++entry->read_count;
+
+    BCMDB_TRACE_DEBUG("lock record read: %s, key=%d new_read_count=%d\n", set->name, key, entry->read_count);
+
+    bcmos_fastlock_unlock(&set->fastlock, flags);
+    return entry->data;
+}
+
+
+/** non-blocking-read/shadow write policy: Unlock entry for reading */
+static void bcmdb_nb_read_shadow_write_unlock_read(bcmdb_set *set, bcmdb_key key)
+{
+    bcmdb_entry *entry=set->key_to_handle(set, key);
+    long flags;
+    BUG_ON(!entry);
+
+    BCMDB_TRACE_DEBUG("unlock record read: %s, key=%d\n", set->name, key);
+
+    flags = bcmos_fastlock_lock(&set->fastlock);
+    /* If write is pending - finish it and release write lock */
+    BUG_ON(!entry->read_count);
+    if (!(--entry->read_count) && set->entry.write_pending)
+    {
+        BCMDB_TRACE_DEBUG("going to wake pending writer\n");
+
+        /* Write was pending. Release write task */
+        set->entry.write_pending = 0;
+        bcmos_fastlock_unlock(&set->fastlock, flags);
+        bcmos_sem_post(&set->sem_wait_read_to_finish);
+    }
+    else
+    {
+        BCMDB_TRACE_DEBUG("read_count is now: %u\n", entry->read_count);
+        bcmos_fastlock_unlock(&set->fastlock, flags);
+    }
+}
+
+/** non-blocking-read/shadow write policy: Lock entry for
+ *  writing/deletion.
+ *  returned value of NULL means error only in case that
+ *  is_deletion is 0 */
+static void *bcmdb_nb_read_shadow_write_lock_write(bcmdb_set *set, bcmdb_key key, int is_deletion)
+{
+    bcmdb_entry *entry;
+
+    BCMDB_TRACE_DEBUG("lock record write: %s, key=%d\n", set->name, key);
+
+    bcmos_mutex_lock(&set->mutex_update, BCMOS_WAIT_FOREVER);
+    BCMDB_TRACE_DEBUG("mutex was locked\n");
+
+    if (is_deletion)
+    {
+        /* there is nothing to return in deletion case */
+        return NULL;
+    }
+
+    /* this check is needed since mutex_update is task-aware.
+       it is not allowed for a task to lock for writing a 2nd record before unlocking the first one. */
+    if (set->write_locked_entry)
+    {
+        BCMDB_TRACE_ERROR("there is already an entry locked for writing\n");
+        bcmos_mutex_unlock(&set->mutex_update);
+        return NULL;
+    }
+
+    entry = set->key_to_handle(set, key);
+    if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+    {
+        bcmos_mutex_unlock(&set->mutex_update);
+        return NULL;
+    }
+    /* Copy data to shadow entry */
+    memcpy(set->shadow_data, entry->data, set->entry_size);
+    set->write_locked_entry = entry;
+    return set->shadow_data;
+}
+
+/** non-blocking-read/shadow write policy: Unlock entry for
+ *  writing/deletion */
+static void bcmdb_nb_read_shadow_write_unlock_write(bcmdb_set *set, int is_deletion, int is_cancellation)
+{
+    bcmdb_entry *entry = set->write_locked_entry;
+    long flags;
+    void *old_data;
+
+    /* no entry is locked */
+    BUG_ON(!entry);
+
+    BCMDB_TRACE_DEBUG("unlock record write: %s\n", set->name);
+
+    /* cancellation: no need to update the entry from the shadow (or to delete the entry in case of deletion). */
+    if (is_cancellation)
+    {
+        set->write_locked_entry = NULL;
+        bcmos_mutex_unlock(&set->mutex_update);
+        return;
+    }
+
+    while(1)
+    {
+        flags = bcmos_fastlock_lock(&set->fastlock);
+        /* Wait until neither record nor set are locked for reading */
+        if (!entry->read_count && !set->entry.read_count)
+            break;
+
+        /* Read lock is active. wait */
+
+        BCMDB_TRACE_DEBUG("read lock is active. going to sleep.\n");
+
+        set->entry.write_pending = 1;
+        bcmos_fastlock_unlock(&set->fastlock, flags);
+        bcmos_sem_wait(&set->sem_wait_read_to_finish, BCMOS_WAIT_FOREVER);
+    }
+
+    /* At this point there is no read lock and fastlock is taken. */
+
+    BCMDB_TRACE_DEBUG("fastlock is taken, no active reads\n");
+
+    if (is_deletion)
+    {
+        /* delete the entry */
+        set->entry_delete(set, entry);
+    }
+    else
+    {
+        /* Exchange record data with shadow and release all locks. */
+        old_data = entry->data;
+        entry->data = set->shadow_data;
+        set->shadow_data = old_data;
+        set->write_locked_entry = NULL;
+    }
+
+    bcmos_fastlock_unlock(&set->fastlock, flags);
+    bcmos_mutex_unlock(&set->mutex_update);
+    BCMDB_TRACE_DEBUG("mutex was unlocked\n");
+}
+
+/** none policy: set read-lock */
+static inline void bcmdb_set_lock_read_dummy(bcmdb_set *set)
+{
+}
+
+/** none policy: set read-unlock */
+static inline void bcmdb_set_unlock_read_dummy(bcmdb_set *set)
+{
+}
+
+/** none policy: set modify-lock */
+static inline long bcmdb_set_lock_modify_dummy(bcmdb_set *set)
+{
+    return 0;
+}
+
+/** none policy: set modify-unlock */
+static inline void bcmdb_set_unlock_modify_dummy(bcmdb_set *set, long fastlock_flags)
+{
+}
+
+/** none policy: set modify-lock recursively  */
+static void bcmdb_set_lock_recursively_modify_dummy(bcmdb_set *set)
+{
+}
+
+/** none policy: set modify-unlock recursively  */
+static void bcmdb_set_unlock_recursively_modify_dummy(bcmdb_set *set)
+{
+}
+
+/** none policy: record lock */
+static inline void *bcmdb_dummy_lock(bcmdb_set *set, bcmdb_key key, int is_deletion)
+{
+    bcmdb_entry *entry;
+
+    /* there is nothing to return in deletion case */
+    if (is_deletion)
+        return NULL;
+
+    entry = set->key_to_handle(set, key);
+    if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+        return NULL;
+    return entry->data;
+}
+
+/** none policy: record unlock */
+static inline void bcmdb_dummy_unlock(bcmdb_set *set)
+{
+}
+
+/** none policy: record lock for reading */
+static inline void *bcmdb_dummy_lock_read(bcmdb_set *set, bcmdb_key key)
+{
+    return bcmdb_dummy_lock(set, key, 0);
+}
+
+/** none policy: record unlock for reading */
+static inline void bcmdb_dummy_unlock_read(bcmdb_set *set, bcmdb_key key)
+{
+    bcmdb_dummy_unlock(set);
+}
+
+/** none policy: record lock for writing */
+static inline void *bcmdb_dummy_lock_write(bcmdb_set *set, bcmdb_key key, int is_deletion)
+{
+    return bcmdb_dummy_lock(set, key, is_deletion);
+}
+
+/** none policy: record unlock for writing */
+static inline void bcmdb_dummy_unlock_write(bcmdb_set *set, int is_deletion, int is_cancellation)
+{
+    bcmdb_dummy_unlock(set);
+}
+
+
+
+/** Add new sub-set. returns 0 if ok
+ * data contains new set handle
+ */
+static int bcmdb_set_new(bcmdb_set *this, bcmdb_key key, const void *data)
+{
+    /* Although this callback takes "const void *" parameter,
+     * it is just for compatibility fith SoR->new_entry interface.
+     * For SoS this parameter is not constant on application level
+     * (see bcmdb_set_add())
+     */
+    bcmdb_entry *entry = this->key_to_handle(this, key);
+    bcmdb_set *new_set = (bcmdb_set *)(long)data;
+
+    if (!entry)
+        return BCM_ERR_PARM;
+    if ((entry->flags & BCMDB_FLAG_VALID))
+        return BCM_ERR_ALREADY;
+    ++this->num_entries;
+    entry->data = (void *)(long)data;
+    entry->flags |= BCMDB_FLAG_VALID;
+    new_set->my_key = key;
+    new_set->parent = this;
+    return 0;
+}
+
+/** Add new record. returns 0 if ok
+ * data contains record data pointer
+ */
+static int bcmdb_record_new(bcmdb_set *this, bcmdb_key key, const void *data)
+{
+    bcmdb_record *record = (bcmdb_record *)this->key_to_handle(this, key);
+    if (!record || !record->e.data)
+        return BCM_ERR_PARM;
+    if ((record->e.flags & BCMDB_FLAG_VALID))
+        return BCM_ERR_ALREADY;
+    ++this->num_entries;
+    memcpy(record->e.data, data, this->entry_size);
+    record->e.flags |= BCMDB_FLAG_VALID;
+    return 0;
+}
+
+/** Delete entry */
+static int bcmdb_entry_delete(bcmdb_set *this, bcmdb_entry *entry)
+{
+    if (!entry)
+        return BCM_ERR_PARM;
+    if (!(entry->flags & BCMDB_FLAG_VALID))
+        return BCM_ERR_ALREADY;
+    entry->flags &= ~BCMDB_FLAG_VALID;
+    --this->num_entries;
+    return 0;
+}
+
+
+/*
+ * External APIs
+ */
+
+
+/** Initialize data base engine
+ *
+ * \return
+ *      0   - OK\n
+ *      <0  - error code
+ * \ingroup bcmdb
+ */
+int bcmdb_module_init(void)
+{
+    return 0;
+}
+
+
+/** Make set-of-sets control block.
+ *
+ * Helper function that creates a set of sets with reasonable defaults for all callbacks and fields.
+ * Once created, the set control block can be tuned before adding the new set to its parent set.
+ * \param[in]   init            set parameters
+ * \param[out]  new_set         set control block
+ * \return
+ *      0   - OK\n
+ *      <0  - error code
+ */
+int bcmdb_make_set_of_sets(const bcmdb_sos_init *init, bcmdb_set **new_set)
+{
+    bcmdb_set *sos;
+    bcmdb_entry *entries;
+    int rc;
+
+    /* Parameter check */
+    if (!init || !init->name || !new_set)
+        return BCM_ERR_PARM;
+    if ((init->backend_type == BCMDB_BACKEND_ARRAY) && !init->max_entries)
+        return BCM_ERR_PARM;
+
+    /* Allocate set control block and set records */
+    sos = bcmos_calloc(sizeof(bcmdb_set) + strlen(init->name) + 1);
+    if (!sos)
+        return BCM_ERR_NOMEM;
+    sos->name = (char *)(sos + 1);
+    strcpy(sos->name, init->name);
+    sos->entry_size = sizeof(bcmdb_set);
+    sos->max_entries = init->max_entries;
+    sos->my_key = BCMDB_KEY_INVAL;
+    sos->entry.flags = BCMDB_FLAG_SOS;
+    sos->magic = BCMDB_MAGIC_ACTIVE_SET;
+
+    /* Set backend callbacks */
+    switch(init->backend_type)
+    {
+        case BCMDB_BACKEND_ARRAY:
+            entries = bcmos_calloc(sizeof(bcmdb_set)*init->max_entries);
+            if (!entries)
+            {
+                bcmos_free(sos);
+                return BCM_ERR_NOMEM;
+            }
+            sos->entry.data = entries;
+            sos->entry_get_next = _bcmdb_array_entry_get_next;
+            sos->handle_to_key = _bcmdb_array_handle_to_key;
+            sos->key_to_handle = _bcmdb_array_key_to_handle;
+            sos->entry_new = bcmdb_set_new;
+            sos->entry_delete = bcmdb_entry_delete;
+            break;
+
+        default:
+            printf("Only array-based DB backend is supported\n");
+            bcmos_free(sos);
+            return BCM_ERR_NOT_SUPPORTED;
+    }
+
+    /* Set locking callbacks. SoS locking policy is always SEMAPHORE */
+
+    /* in SoS, locking for read is same as for write (and is done recursively). */
+    sos->lock_set_read = bcmdb_set_lock_recursively_modify_sem;
+    sos->unlock_set_read = bcmdb_set_unlock_recursively_modify_sem;
+    sos->lock_set_modify = bcmdb_set_lock_modify_sem;
+    sos->unlock_set_modify = bcmdb_set_unlock_modify_sem;
+    sos->lock_set_recursively_modify = bcmdb_set_lock_recursively_modify_sem ;
+    sos->unlock_set_recursively_modify = bcmdb_set_unlock_recursively_modify_sem ;
+
+    /* create mutex_update */
+    rc = bcmos_mutex_create(&sos->mutex_update, init->os_flags);
+    if (rc)
+    {
+        bcmos_free(entries);
+        bcmos_free(sos);
+        return BCM_ERR_NOMEM;
+    }
+
+    bcmos_fastlock_init(&sos->fastlock, init->os_flags);
+
+    *new_set = sos;
+
+    return 0;
+}
+
+
+
+
+/** Make set-of-records control block.
+ *
+ * Helper function that creates a set of records with reasonable defaults for all callbacks and fields.
+ * Once created, the set control block can be tuned before adding the new set to its parent set.
+ * \param[in]   init            set parameters
+ * \param[in]   alloc_records   true (1) - allocate memory for all records.
+ * \param[out]  new_set         set control block
+ * \return
+ *      0   - OK\n
+ *      <0  - error code
+ */
+int bcmdb_make_set_of_records(const bcmdb_sor_init *init, int alloc_records, bcmdb_set **new_set)
+{
+    bcmdb_set *sor;
+    bcmdb_entry *entries = NULL;
+    void *data = NULL;
+    int i;
+    int rc ;
+
+    /* Parameter check */
+    if (!init || !init->name)
+        return BCM_ERR_PARM;
+    if ((init->backend_type == BCMDB_BACKEND_ARRAY) && !init->max_entries)
+        return BCM_ERR_PARM;
+    if (!init->record_size)
+        return BCM_ERR_PARM;
+
+    /* Allocate set control block and set records */
+    sor = bcmos_calloc(sizeof(bcmdb_set) + strlen(init->name) + 1);
+    if (!sor)
+        return BCM_ERR_NOMEM;
+    sor->name = (char *)(sor + 1);
+    strcpy(sor->name, init->name);
+    sor->entry_size = init->record_size;
+    sor->max_entries = init->max_entries;
+    sor->my_key = BCMDB_KEY_INVAL;
+    sor->magic = BCMDB_MAGIC_ACTIVE_SET;
+    sor->format = init->format;
+
+    /* Set backend callbacks */
+    switch(init->backend_type)
+    {
+        case BCMDB_BACKEND_ARRAY:
+            entries = bcmos_calloc(sizeof(bcmdb_entry)*init->max_entries);
+            if (!entries)
+            {
+                bcmos_free(sor);
+                return BCM_ERR_NOMEM;
+            }
+            sor->entry.data = entries;
+            sor->entry_get_next = _bcmdb_array_entry_get_next;
+            sor->handle_to_key = _bcmdb_array_handle_to_key;
+            sor->key_to_handle = _bcmdb_array_key_to_handle;
+            sor->entry_new = bcmdb_record_new;
+            sor->entry_delete = bcmdb_entry_delete;
+
+            /* Preallocate data */
+            if (alloc_records)
+            {
+                int size = init->max_entries * init->record_size;
+                if (init->lock_policy == BCMDB_LOCK_NB_READ_SHADOW_WRITE)
+                    size += init->record_size; /* room for shadow entry */
+                /* Allocate data + 1 extra for shadow area */
+                data = bcmos_calloc(size);
+                if (!data)
+                {
+                    bcmos_free(entries);
+                    bcmos_free(sor);
+                    return BCM_ERR_NOMEM;
+                }
+                for(i=0; i<init->max_entries; i++)
+                {
+                    bcmdb_entry *entry = (bcmdb_entry *)sor->entry.data + i;
+                    entry->data = (void *)((long)data + i * init->record_size);
+                }
+                if (init->lock_policy == BCMDB_LOCK_NB_READ_SHADOW_WRITE)
+                {
+                    sor->shadow_data = (void *)((long)data + i * init->record_size);
+                }
+            }
+
+            /* Initialize records */
+            for(i=0; i<init->max_entries; i++)
+            {
+                bcmdb_entry *entry = (bcmdb_entry *)sor->entry.data + i;
+                entry->flags = BCMDB_FLAG_RECORD;
+            }
+            break;
+
+        default:
+            printf("Only array-based DB backend is supported\n");
+            bcmos_free(sor);
+            return BCM_ERR_NOT_SUPPORTED;
+    }
+
+    /* Set locking callbacks based on locking policy */
+    switch(init->lock_policy)
+    {
+        case BCMDB_LOCK_SEM_READ_SEM_WRITE:
+            sor->lock_record_write = bcmdb_sem_read_sem_write_lock_write;
+            sor->lock_record_read = bcmdb_sem_read_sem_write_lock_read;
+            sor->unlock_record_write = bcmdb_sem_read_sem_write_unlock_write;
+            sor->unlock_record_read = bcmdb_sem_read_sem_write_unlock_read;
+            sor->lock_set_read = bcmdb_set_lock_read_sem;
+            sor->unlock_set_read = bcmdb_set_unlock_read_sem;
+            sor->lock_set_modify = bcmdb_set_lock_modify_sem;
+            sor->unlock_set_modify = bcmdb_set_unlock_modify_sem;
+            sor->lock_set_recursively_modify = bcmdb_set_lock_recursively_modify_sem ;
+            sor->unlock_set_recursively_modify = bcmdb_set_unlock_recursively_modify_sem ;
+            break;
+
+        case BCMDB_LOCK_NONE:
+        case BCMDB_LOCK_OTHER:
+            sor->lock_record_write = bcmdb_dummy_lock_write;
+            sor->lock_record_read = bcmdb_dummy_lock_read;
+            sor->unlock_record_write = bcmdb_dummy_unlock_write;
+            sor->unlock_record_read = bcmdb_dummy_unlock_read;
+            sor->lock_set_read = bcmdb_set_lock_read_dummy;
+            sor->unlock_set_read = bcmdb_set_unlock_read_dummy;
+            sor->lock_set_modify = bcmdb_set_lock_modify_dummy;
+            sor->unlock_set_modify = bcmdb_set_unlock_modify_dummy;
+            sor->lock_set_recursively_modify = bcmdb_set_lock_recursively_modify_dummy ;
+            sor->unlock_set_recursively_modify = bcmdb_set_unlock_recursively_modify_dummy ;
+            break;
+
+        case BCMDB_LOCK_NB_READ_SHADOW_WRITE:
+            sor->lock_record_write = bcmdb_nb_read_shadow_write_lock_write;
+            sor->lock_record_read = bcmdb_nb_read_shadow_write_lock_read;
+            sor->unlock_record_write = bcmdb_nb_read_shadow_write_unlock_write;
+            sor->unlock_record_read = bcmdb_nb_read_shadow_write_unlock_read;
+            sor->lock_set_read = bcmdb_set_lock_read__nb_read_sem_write;
+            sor->unlock_set_read = bcmdb_set_unlock_read__nb_read_sem_write;
+            sor->lock_set_modify = bcmdb_set_lock_modify__nb_read_sem_write;
+            sor->unlock_set_modify = bcmdb_set_unlock_modify__nb_read_sem_write;
+            sor->lock_set_recursively_modify = bcmdb_set_lock_recursively_modify_sem ;
+            sor->unlock_set_recursively_modify = bcmdb_set_unlock_recursively_modify_sem ;
+            break;
+
+        default:
+            printf("Lock policy %d is not supported\n", init->lock_policy);
+            if (data)
+                bcmos_free(data);
+            if (entries)
+                bcmos_free(entries);
+            bcmos_free(sor);
+            return BCM_ERR_NOT_SUPPORTED;
+    }
+
+    /* create mutex_update */
+    rc = bcmos_mutex_create(&sor->mutex_update, init->os_flags);
+    if (rc)
+    {
+        if (data)
+            bcmos_free(data);
+        if (entries)
+            bcmos_free(entries);
+        bcmos_free(sor);
+        return BCM_ERR_NOMEM;
+    }
+
+    /* create sem_wait_read_to_finish. it is initialized to be taken */
+    rc = bcmos_sem_create(&sor->sem_wait_read_to_finish, 0, init->os_flags);
+    if (rc)
+    {
+        /* no point to check here the error code of bcmos_mutex_destroy */
+        bcmos_mutex_destroy(&sor->mutex_update);
+        if (data)
+            bcmos_free(data);
+        if (entries)
+            bcmos_free(entries);
+        bcmos_free(sor);
+        return BCM_ERR_NOMEM;
+    }
+
+    bcmos_fastlock_init(&sor->fastlock, init->os_flags);
+
+    *new_set = sor;
+
+    return 0;
+}
+
+
+/** Lock data set for reading. When set is locked - it can't be
+ *  modified.
+ *
+ * \param[in]   set             data base set to be locked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_lock_read(bcmdb_set *set)
+{
+    BUG_ON(!set);
+    BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!set->lock_set_read);
+    set->lock_set_read(set);
+}
+
+
+/** Release data set lock
+ *
+ * Unlock set locked by \ref bcmdb_set_lock_read
+ *
+ * \param[in]   set             data base set to be unlocked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_unlock_read(bcmdb_set *set)
+{
+    BUG_ON(!set);
+    BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!set->unlock_set_read);
+    set->unlock_set_read(set);
+}
+
+
+/** Lock data set for modify. If the set is SoS, the locking
+ *  will be recursive.
+ *
+ * \param[in]   set             data base set to be locked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_lock_modify(bcmdb_set *set)
+{
+    BUG_ON(!set);
+    BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!set->lock_set_recursively_modify);
+    set->lock_set_recursively_modify(set);
+}
+
+
+/** Release data set lock
+ *
+ * Unlock set locked by \ref bcmdb_set_lock_modify
+ *
+ * \param[in]   set             data base set to be unlocked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_unlock_modify(bcmdb_set *set)
+{
+    BUG_ON(!set);
+    BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!set->unlock_set_recursively_modify);
+    set->unlock_set_recursively_modify(set);
+}
+
+
+/** Add set to the parent set with specific key.
+ *
+ * The function adds set to the parent set creating data base hierarchy.
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ * \param[in]   sos             parent set of sets
+ * \param[in]   key             key to add new set at
+ * \param[in]   new_set         set control block
+ * \return
+ *      =0  - OK\n
+ *      <0  - error code
+ * \ingroup bcmdb
+ */
+int bcmdb_set_add(bcmdb_set *sos, bcmdb_key key, bcmdb_set *new_set)
+{
+    int rc;
+    long fastlock_flags;
+    BUG_ON(!sos);
+    BUG_ON(!sos->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!(sos->entry.flags & BCMDB_FLAG_SOS));
+    BUG_ON(!new_set);
+    BUG_ON(!new_set->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!new_set->my_key == BCMDB_KEY_INVAL);
+    fastlock_flags = sos->lock_set_modify(sos);
+    rc = sos->entry_new(sos, key, new_set);
+    sos->unlock_set_modify(sos, fastlock_flags);
+    return rc;
+}
+
+
+/** Get set handle given its key.
+ *
+ * \param[in]   sos             parent set of sets
+ * \param[in]   key             set key.
+ * \return
+ *      !=0 - set handle
+ *      NULL- doesn't exist
+ * \ingroup bcmdb
+ */
+bcmdb_set *bcmdb_set_handle(const bcmdb_set *sos, bcmdb_key key)
+{
+    bcmdb_entry *entry;
+    BUG_ON(!sos);
+    BUG_ON(!sos->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!(sos->entry.flags & BCMDB_FLAG_SOS));
+    entry = sos->key_to_handle(sos, key);
+    if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+        return NULL;
+    return (bcmdb_set *)entry->data;
+}
+
+
+/** Get set key given its handle.
+ *
+ * \param[in]   set             set handle
+ * \param[in]   key             set key.
+ * \return
+ *      !=BCMDB_KEY_INVAL - set key
+ *      BCMDB_KEY_INVAL - error
+ * \ingroup bcmdb
+ */
+bcmdb_key bcmdb_set_key(const bcmdb_set *set)
+{
+    BUG_ON(!set);
+    BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+    return set->my_key;
+}
+
+
+/** Get set name
+ *
+ * \param[in]   set             set handle
+ * \return set name
+ * \ingroup bcmdb
+ */
+const char *bcmdb_set_name(const bcmdb_set *set)
+{
+    BUG_ON(!set);
+    BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+    return set->name;
+}
+
+
+/** Get number of records in the set.
+ *
+ * \param[in]   set             set handle
+ * \return number of active records in the set
+ * \ingroup bcmdb
+ */
+int bcmdb_set_num_records(const bcmdb_set *set)
+{
+    BUG_ON(!set);
+    BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+    return set->num_entries;
+}
+
+
+/** Get entry size
+ *
+ * \param[in]   set             set handle
+ * \return set entry size
+ * \ingroup bcmdb
+ */
+int bcmdb_set_entry_size(const bcmdb_set *set)
+{
+    BUG_ON(!set);
+    BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+    return set->entry_size;
+}
+
+
+/** Add record to the parent set with specific key.
+ *
+ * The function creates a new record and adds it to the parent set with specific key.
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             key to add new set at
+ * \param[in]   data            record data. Data size is defined at parent SOR registration time.
+ * \param[out]  p_record        new record handle
+ * \return
+ *      =0  - OK\n
+ *      <0  - error code
+ * \ingroup bcmdb
+ */
+int bcmdb_record_add(bcmdb_set *sor, bcmdb_key key, const void *data)
+{
+    int rc;
+    long fastlock_flags;
+
+    BUG_ON(!sor);
+    BUG_ON(!data);
+    BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+
+    fastlock_flags = sor->lock_set_modify(sor);
+    rc=sor->entry_new(sor, key, data);
+    sor->unlock_set_modify(sor, fastlock_flags);
+    return rc;
+}
+
+
+/** Delete record from the parent SoR given the record key.
+ *
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key.
+ */
+void bcmdb_record_delete(bcmdb_set *sor, bcmdb_key key)
+{
+    BUG_ON(!sor);
+    BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+
+    sor->lock_record_write(sor, key, 1);
+    sor->unlock_record_write(sor, 1, 0);
+}
+
+
+/** Get record data pointer without locking.
+ *
+ * The function returns pointer to data structure stored in data base record.\n
+ * Attention! The caller is required to aquire read or write lock - as appropriate
+ * before calling this function and release the lock when processing is finished.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \return
+ *      data pointer. NULL if there is no record matching the key.
+ * \ingroup bcmdb
+ */
+void *bcmdb_record_getraw_nolock(bcmdb_set *sor, bcmdb_key key)
+{
+    bcmdb_entry *entry;
+    BUG_ON(!sor);
+    BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+    entry = sor->key_to_handle(sor, key);
+    if (!entry || !(entry->flags & BCMDB_FLAG_VALID))
+        return NULL;
+    return entry->data;
+}
+
+
+/** Lock record for reading and return record data pointer.
+ *
+ * The function aquires read-lock and returns pointer to data structure stored in data base record.\n
+ * read-lock must be released separately when the pointer is no longer in use.
+ * Note that the default record-read lock is non-blocking and counting.
+ * That means that multiple processes cam read-lock the same record without blocking.
+ * The function is low-level. It is recommended to use macro \ref bcmdb_record_get_read instead.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \return
+ *      data pointer. NULL if there is no record matching the key.
+ * \ingroup bcmdb
+ */
+const void *bcmdb_record_getraw_read(bcmdb_set *sor, bcmdb_key key)
+{
+    BUG_ON(!sor);
+    BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+    return sor->lock_record_read(sor, key);
+}
+
+
+/** Unlock record locked for reading.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_record_unlock_read(bcmdb_set *sor, bcmdb_key key)
+{
+    BUG_ON(!sor);
+    BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+    sor->unlock_record_read(sor, key);
+}
+
+
+/** Read record data into user area.
+ *
+ * The function aquires read-lock, reads data into user area and releases read-lock.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \param[in]   offset          offset in data record
+ * \param[in]   size            data size. Note that offset+size must be <= record_size
+ * \param[in]   data            data pointer.
+ * \return
+ *      =0-OK\n
+ *      <0-error code
+ * \ingroup bcmdb
+ */
+int bcmdb_record_read(bcmdb_set *sor, bcmdb_key key, int offset, int size, void *data)
+{
+    const void *d = bcmdb_record_getraw_read(sor, key);
+    if (!d)
+        return BCM_ERR_PARM;
+    if ((unsigned)offset + (unsigned)size > sor->entry_size)
+    {
+        bcmdb_record_unlock_read(sor, key);
+        return BCM_ERR_PARM;
+    }
+    memcpy(data, (const char *)d+(unsigned)offset, (unsigned)size);
+    bcmdb_record_unlock_read(sor, key);
+    return 0;
+}
+
+
+/** Lock record for writing and return record data pointer.
+ *
+ * The function aquires write-lock and returns pointer to data structure stored in data base record.\n
+ * write-lock must be released separately when the pointer is no longer in use.
+ * The function is low-level. It is recommended to use macro \ref bcmdb_record_get_write instead.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \return
+ *      data pointer. NULL if there is no record matching the key.
+ * \ingroup bcmdb
+ */
+void *bcmdb_record_getraw_write(bcmdb_set *sor, bcmdb_key key)
+{
+    BUG_ON(!sor);
+    BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+    return sor->lock_record_write(sor, key, 0);
+}
+
+
+/** Unlock record locked for writing.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   is_cancellation TRUE=cancel transaction
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_record_unlock_write(bcmdb_set *sor, int is_cancellation)
+{
+    BUG_ON(!sor);
+    BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!(sor->entry.flags & BCMDB_FLAG_SOS)==0);
+    sor->unlock_record_write(sor, 0, is_cancellation);
+}
+
+
+/** Write record data.
+ *
+ * The function aquires modify-lock, replaces data stored in data base record
+ * and releses the lock.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \param[in]   offset          offset in data record
+ * \param[in]   size            data size. Note that offset+size must be <= record_size
+ * \param[in]   data            data pointer.
+ * \return
+ *      =0-OK\n
+ *      <0-error code
+ * \ingroup bcmdb
+ */
+int bcmdb_record_write(bcmdb_set *sor, bcmdb_key key, int offset, int size, const void *data)
+{
+    void *d=bcmdb_record_getraw_write(sor, key);
+    if (!d)
+        return BCM_ERR_PARM;
+    if ((unsigned)offset + (unsigned)size > sor->entry_size)
+    {
+        bcmdb_record_unlock_write(sor, 0);
+        return BCM_ERR_PARM;
+    }
+    memcpy((char *)d+(unsigned)offset, data, (unsigned)size);
+    bcmdb_record_unlock_write(sor, 0);
+    return 0;
+}
+
+
+/** Register notification function to get informed
+ * when data base set is modified.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   cb              callback function pointer
+ * \param[in]   cb_priv         private data that should be passed to the callback
+ * \return
+ *      =0  - OK\n
+ *      <0  - error code
+ * \ingroup bcmdb
+ */
+int bcmdb_set_notify_register(bcmdb_set *sor, bcmdb_notify_cb cb, long cb_priv)
+{
+    bcmdb_notify *nf_new, *nf, *nf_prev = NULL;
+
+    BUG_ON(!sor);
+    BUG_ON(!sor->magic == BCMDB_MAGIC_ACTIVE_SET);
+    BUG_ON(!cb);
+
+    nf_new = bcmos_calloc(sizeof(bcmdb_notify));
+    if (!nf_new)
+        return BCM_ERR_NOMEM;
+    nf_new->cb = cb;
+    nf_new->cb_priv = cb_priv;
+
+    /* Add to set's notification list */
+    nf = sor->notify_list_head;
+    while(nf)
+    {
+        nf_prev = nf;
+        nf = nf->next;
+    }
+    if (nf_prev)
+        nf_prev->next = nf_new;
+    else
+        sor->notify_list_head = nf_new;
+
+    return 0;
+}
+
+
+/** Data base iterator
+ *
+ * \param[in]   set             data base set
+ * \param[in]   prev            last entry. BCMDB_KEY_ANY=start from the beginning
+ * \return  data base entry key following prev or BCMDB_KEY_NO_MORE if end is reached.\n
+ *          BCMDB_KEY_INVAL is reqturned if prev key is invalid
+ * \ingroup bcmdb
+ */
+bcmdb_key bcmdb_set_iterate(const bcmdb_set *set, bcmdb_key prev)
+{
+    BUG_ON(!set);
+    BUG_ON(!set->magic == BCMDB_MAGIC_ACTIVE_SET);
+    return set->entry_get_next(set, prev);
+}
+
+
+/* Print database structure */
+static void _bcmdb_print_structure(const bcmdb_set *set, int level)
+{
+    int i;
+
+    if (!set)
+        return;
+
+    /* Indentation */
+    for(i=0; i<level; i++)
+        printf("\t");
+
+    if ((set->entry.flags & BCMDB_FLAG_SOS))
+    {
+        bcmdb_key key = bcmdb_set_iterate(set, BCMDB_KEY_ANY);
+        printf("%-16s SoS max_entries=%d entries=%d\n", set->name, set->max_entries, set->num_entries);
+        while(key >= 0)
+        {
+            _bcmdb_print_structure(bcmdb_set_handle(set, key), level+1);
+            key = bcmdb_set_iterate(set, key);
+        }
+    }
+    else
+    {
+        printf("%-16s SoR max_entries=%d entries=%d record_size=%d total_size=%d\n",
+               set->name, set->max_entries, set->num_entries, set->entry_size,
+               set->entry_size*set->max_entries);
+    }
+}
+
+
+/** Print database structure.
+ *
+ * \param[in]   set             root set
+ * \ingroup bcmdb
+ */
+void bcmdb_set_print_structure(const bcmdb_set *set)
+{
+    _bcmdb_print_structure(set, 0);
+}
+
+
+/** Format record for printing.
+ *
+ * The function converts record data to human-readible format.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \param[out]  buffer          output buffer
+ * \param[in]   size            buffer size
+ * \return
+ *      >=0-amount of data placed in the buffer\n
+ *      <0-error code
+ */
+int bcmdb_record_read_formatted(bcmdb_set *sor, bcmdb_key key, char *buffer, int size)
+{
+    const void *data;
+    int len;
+    if (!buffer || !size)
+        return BCM_ERR_PARM;
+    if (!sor->format)
+        return BCM_ERR_NOT_SUPPORTED;
+    *buffer=0;
+    data = bcmdb_record_getraw_read(sor, key);
+    if (!data)
+        return 0;
+    len = sor->format(data, buffer, size);
+    bcmdb_record_unlock_read(sor, key);
+    return len;
+}
diff --git a/bal_release/src/common/db_engine/bcm_db_engine.h b/bal_release/src/common/db_engine/bcm_db_engine.h
new file mode 100644
index 0000000..8d77022
--- /dev/null
+++ b/bal_release/src/common/db_engine/bcm_db_engine.h
@@ -0,0 +1,622 @@
+/******************************************************************************
+ *
+ *  <:copyright-BRCM:2016:DUAL/GPL:standard
+ *  
+ *     Copyright (c) 2016 Broadcom
+ *     All Rights Reserved
+ *  
+ *  Unless you and Broadcom execute a separate written software license
+ *  agreement governing use of this software, this software is licensed
+ *  to you under the terms of the GNU General Public License version 2
+ *  (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+ *  with the following added to such license:
+ *  
+ *     As a special exception, the copyright holders of this software give
+ *     you permission to link this software with independent modules, and
+ *     to copy and distribute the resulting executable under terms of your
+ *     choice, provided that you also meet, for each linked independent
+ *     module, the terms and conditions of the license of that module.
+ *     An independent module is a module which is not derived from this
+ *     software.  The special exception does not apply to any modifications
+ *     of the software.
+ *  
+ *  Not withstanding the above, under no circumstances may you combine
+ *  this software in any way with any other Broadcom software provided
+ *  under a license other than the GPL, without Broadcom's express prior
+ *  written consent.
+ *  
+ *  :>
+ *
+ *****************************************************************************/
+ 
+/*
+ * bcm_db_engine.c
+ *
+ * Data base engine
+ */
+#ifndef BCMDB_ENGINE_H
+
+#define BCMDB_ENGINE_H
+
+/**  \defgroup bcmdb_e Data Base Engine Module
+Hierarchical data base is built from 3 types of objects:
+- set: consists of control info and dynamic array of other sets OR of records of the same kind.
+    - Mixing subsets and records in the same set is not supported.
+    - Sets are objects that perform operations like access, locking, adding or removing elements,
+      etc., via methods that can differ for every set.
+    - Set elements are addressed using a single key.
+    - Most sets are internally organized as arrays. However, other organizations (e.g., lists, hash tables)
+      are also possible because each set can have different set of methods for element access.
+- record: is a container for storing information.
+    - Record consists of control info and a structure containing fields.
+    - Record is the smallest DB element that has a handle and can be individually locked.
+    - Record size is fixed at time when the set containing records is created.
+- field: is a convenience element.
+    - DB API includes field access macros for convenience and traceability.
+      Apart from that, record layout is transparent to the DB engine.
+    - DB user has an option of accessing record fields directly (as C structure fields), without using DB API
+ @{
+*/
+
+#define bcmdb_error_print(rc,format,args...)       BCMOS_TRACE_ERR("status:%s :" format, bcmos_strerror(rc), ##args)
+
+/** Data base backend type
+ */
+typedef enum
+{
+    BCMDB_BACKEND_ARRAY,    /**< Array-based backend */
+    BCMDB_BACKEND_HASH,     /**< Hash-based backend */
+    BCMDB_BACKEND_OTHER     /**< User-defined backend */
+} bcmdb_backend_type;
+
+
+/** Data locking policy
+ */
+typedef enum
+{
+    BCMDB_LOCK_NONE,                /**< No record-level locking. Can be used for records containing independent fields */
+    BCMDB_LOCK_NB_READ_SHADOW_WRITE,/**< Non-blocking read, write using shadow area (default) */
+    BCMDB_LOCK_SEM_READ_SEM_WRITE,  /**< Strong locking. Both read and write locks use semaphores */
+    BCMDB_LOCK_OTHER                /**< User-defined locking policy */
+} bcmdb_lock_policy;
+
+
+/** Data base key
+ * Valid values >= 0
+ */
+typedef int bcmdb_key;
+
+
+/** Any key
+ */
+#define BCMDB_KEY_ANY       (-1)
+
+/** Invalid key
+ */
+#define BCMDB_KEY_INVAL     (-2)
+
+/** No more records
+ */
+#define BCMDB_KEY_NO_MORE   (-3)
+
+
+ /** Data Base Set control block */
+typedef struct bcmdb_set bcmdb_set;
+
+/** Data Base Record control block */
+typedef struct bcmdb_record bcmdb_record;
+
+/** Data Base Set or Record */
+typedef struct bcmdb_entry bcmdb_entry;
+
+
+/** Data base operations for notifications.
+ */
+typedef enum
+{
+    BCMDB_OPER_ADD,         /**< Entry has been added */
+    BCMDB_OPER_DELETE,      /**< Entry has been deleted */
+    BCMDB_OPER_UPDATE       /**< Entry has been modified */
+} bcmdb_oper_t;
+
+
+/** Data base update notification callback.
+ */
+typedef void (*bcmdb_notify_cb)(bcmdb_set *set, bcmdb_key key, bcmdb_oper_t oper, void *new_data);
+
+
+/** Format callback. Used by bcmdb_record_read_formatted to convert record data to human-readible format */
+typedef int (*bcmdb_format_cb)(const void *data, char *buffer, int buffer_size);
+
+
+/** Set-of-Sets init structure.
+ */
+typedef struct bcmdb_sos_init
+{
+    const char *name;                   /**< Set name */
+    bcmdb_backend_type backend_type;    /**< Backend type */
+    uint32_t max_entries;               /**< Max number of entries. 0=unlimited (not supported for array backend) */
+    uint32_t os_flags;                  /**< OS flags. Control whether set can be accessed by multiple cores. See bcmos_mutex_create() */
+} bcmdb_sos_init;
+
+
+/** Set-of-Records init structure.
+ */
+typedef struct bcmdb_sor_init
+{
+    const char *name;                   /**< Set name */
+    bcmdb_backend_type backend_type;    /**< Backend type */
+    bcmdb_lock_policy lock_policy;      /**< Set locking policy */
+    uint32_t max_entries;               /**< Max number of entries. 0=unlimited (not supported for array backend) */
+    uint32_t record_size;               /**< Record size > 0 */
+    bcmdb_format_cb format;             /**< callback that converts record data to human-readable form */
+    uint32_t os_flags;                  /**< OS flags. Control whether set can be accessed by multiple cores. See bcmos_mutex_create() */
+} bcmdb_sor_init;
+
+
+/** Initialize data base engine
+ * 
+ * \return
+ *      0   - OK\n
+ *      <0  - error code
+ */
+int bcmdb_module_init(void);
+
+
+/** Make set-of-sets control block.
+ * 
+ * Helper function that creates a set of sets with reasonable defaults for all callbacks and fields.
+ * Once created, the set control block can be tuned before adding the new set to its parent set.
+ * \param[in]   init            set parameters
+ * \param[out]  new_set         set control block
+ * \return
+ *      0   - OK\n
+ *      <0  - error code
+ */ 
+int bcmdb_make_set_of_sets(const bcmdb_sos_init *init, bcmdb_set **new_set);
+
+
+/** Make set-of-sets control block macro.
+ *
+ * Calls \ref bcmdb_make_set_of_sets.
+ * Prints error message and jumps to error_label in case of failure.
+ * For parameter description see \ref bcmdb_make_set_of_sets
+ */
+#define BCMDB_MAKE_SOS(_name,_backend,_max_entries,_p_handle,_rc,_error_label) \
+({\
+    bcmdb_sos_init _init = { .name=_name, .max_entries=_max_entries, .backend_type=_backend};\
+    _rc = bcmdb_make_set_of_sets(&_init, _p_handle);\
+    if (_rc)\
+    {\
+        bcmdb_error_print(_rc, "failed to create set %s.\n", _name);\
+        goto _error_label;\
+    }\
+})
+
+
+/** Make set-of-records control block.
+ *
+ * Helper function that creates a set of records with reasonable defaults for all callbacks and fields.
+ * Once created, the set control block can be tuned before adding the new set to its parent set.
+ * \param[in]   init            set parameters
+ * \param[in]   alloc_records   true (1) - allocate memory for all records.
+ * \param[out]  new_set         set control block
+ * \return
+ *      0   - OK\n
+ *      <0  - error code
+ */ 
+int bcmdb_make_set_of_records(const bcmdb_sor_init *init, int alloc_records, bcmdb_set **new_set);
+
+
+/** Make set-of-records control block macro.
+ *
+ * Calls \ref bcmdb_make_set_of_records.
+ * Prints error message and jumps to error_label in case of failure.
+ * For parameter description see \ref bcmdb_make_set_of_records
+ */
+#define BCMDB_MAKE_SOR(_name,_backend,_lock,_max_entries,_rec_size,_is_alloc,_format,_p_handle,_rc,_error_label) \
+({\
+    bcmdb_sor_init _init = { .name=_name, .max_entries=_max_entries, .backend_type=_backend,\
+                               .lock_policy=_lock, .record_size=_rec_size,.format=_format};\
+    _rc = bcmdb_make_set_of_records(&_init,_is_alloc,_p_handle);\
+    if (_rc)\
+    {\
+        bcmdb_error_print(_rc, "failed to create record set %s.\n", _name);\
+        goto _error_label;\
+    }\
+})
+
+
+/** Lock data set. When set is locked - it can't be modified.
+ * 
+ * \param[in]   set             data base set to be locked
+ * 
+ */
+void bcmdb_set_lock_read(bcmdb_set *set);
+
+
+/** Release data set lock
+ *
+ * Unlock set locked by \ref bcmdb_set_lock_read
+ *
+ * \param[in]   set             data base set to be unlocked
+ */
+void bcmdb_set_unlock_read(bcmdb_set *set);
+
+
+/** Lock data set for modify. If the set is SoS, the locking
+ *  will be recursive.
+ *
+ * \param[in]   set             data base set to be locked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_lock_modify(bcmdb_set *set);
+
+
+/** Release data set lock
+ *
+ * Unlock set locked by \ref bcmdb_set_lock_modify
+ *
+ * \param[in]   set             data base set to be unlocked
+ *
+ * \ingroup bcmdb
+ */
+void bcmdb_set_unlock_modify(bcmdb_set *set);
+
+
+/** Add set to the parent set.
+ *
+ * The function adds set to the parent set creating data base hierarchy.
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ * \param[in]   sos             parent set of sets
+ * \param[in]   key             key to add new set at
+ * \param[in]   new_set         set control block
+ * \return
+ *      =0  - OK\n
+ *      <0  - error code
+ */
+int bcmdb_set_add(bcmdb_set *sos, bcmdb_key key, bcmdb_set *new_set);
+
+
+/** Add set to the parent set with specific key macro.
+ *
+ * Calls \ref bcmdb_set_add.
+ * Prints error message and jumps to error_label in case of failure.
+ * For parameter description see \ref bcmdb_set_add
+ */
+#define BCMDB_SET_ADD(_parent,_key,_set,_rc,_error_label) \
+({\
+    _rc = bcmdb_set_add(_parent,_key,_set);\
+    if (_rc)\
+    {\
+        bcmdb_error_print(_rc, "failed to add set %s to %s.\n", bcmdb_set_name(_set), bcmdb_set_name(_parent));\
+        goto _error_label;\
+    }\
+})
+
+
+/** Get set handle given its key.
+ *
+ * \param[in]   sos             parent set of sets
+ * \param[in]   key             set key.
+ * \return
+ *      !=0 - set handle
+ *      NULL- doesn't exist
+ */
+bcmdb_set *bcmdb_set_handle(const bcmdb_set *sos, bcmdb_key key);
+
+
+/** Get set key given its handle.
+ *
+ * \param[in]   set             set handle
+ * \return
+ *      !=BCMDB_KEY_INVAL - set key\n
+ *      BCMDB_KEY_INVAL - error
+ */
+bcmdb_key bcmdb_set_key(const bcmdb_set *set);
+
+
+/** Get set name
+ * 
+ * \param[in]   set             set handle
+ * \return set name
+ */
+const char *bcmdb_set_name(const bcmdb_set *set);
+
+
+/** Get number of records in the set.
+ *
+ * \param[in]   set             set handle
+ * \return number of active records in the set
+ */
+int bcmdb_set_num_records(const bcmdb_set *set);
+
+
+/** Get entry size
+ *
+ * \param[in]   set             set handle
+ * \return set entry size
+ */
+int bcmdb_set_entry_size(const bcmdb_set *set);
+
+
+/** Add record to the parent set.
+ *
+ * The function creates a new record and adds it to the parent set with specific key.
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \param[in]   data            record data. Data size is defined at parent SOR registration time.
+ * \return
+ *      =0  - OK\n
+ *      <0  - error code
+ */
+int bcmdb_record_add(bcmdb_set *sor, bcmdb_key key, const void *data);
+
+
+/** Delete record from the parent SoR given the record key.
+ * 
+ * The function automatically acquires modify lock and releases it
+ * in the end of operation.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key.
+ */
+void bcmdb_record_delete(bcmdb_set *sor, bcmdb_key key);
+
+
+/** Get record data pointer without locking.
+ *
+ * The function returns pointer to data structure stored in data base record.\n
+ * Attention! The caller is required to aquire read or write lock - as appropriate
+ * before calling this function and release the lock when processing is finished.
+ * The function is low-level. It is recommended to use \ref bcmdb_record_get_nolock instead.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \return
+ *      data pointer. NULL if there is no record matching the key.
+ */
+void *bcmdb_record_getraw_nolock(bcmdb_set *sor, bcmdb_key key);
+
+
+/** Get record data pointer without locking.
+ *
+ * The function returns pointer to data structure stored in data base record.\n
+ * Attention! The caller is required to aquire read or write lock - as appropriate
+ * before calling this function and release the lock when processing is finished.
+ *
+ * \param[in]   _sor            parent set of records
+ * \param[in]   _key            record key
+ * \param[in]   _record_type    underlying data type.
+ * \return
+ *      data pointer casted to the underlying data type\n
+ *      NULL if there is no record matching the key.
+ */
+#define bcmdb_record_get_nolock(_sor, _key, _record_type)      \
+    ({ \
+        assert(sizeof(_record_type)==bcmdb_set_entry_size(_sor)); \
+        (_record_type *)bcmdb_record_getraw_nolock(_sor, _key); \
+     })
+
+
+/** Lock record for reading and return record data pointer.
+ *
+ * The function aquires read-lock and returns pointer to data structure stored in data base record.\n
+ * read-lock must be released separately when the pointer is no longer in use.
+ * Note that the default record-read lock is non-blocking and counting.
+ * That means that multiple processes can read-lock the same record without blocking.
+ * The function is low-level. It is recommended to use macro \ref bcmdb_record_get_read instead.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \return
+ *      data pointer. NULL if there is no record matching the key.
+ */
+const void *bcmdb_record_getraw_read(bcmdb_set *sor, bcmdb_key key);
+
+
+/** Lock record for reading and return record data pointer.
+ *
+ * The macro returns pointer to data structure stored in data base record.\n
+ * The read-lock must be released separately when the pointer is no longer in use.
+ * Note that the default record-read lock is non-blocking and counting.
+ * That means that multiple processes can read-lock the same record without blocking.
+ *
+ * \param[in]   _sor            parent set of records
+ * \param[in]   _key            record key
+ * \param[in]   _record_type    underlying data type.
+ * \return
+ *      data pointer casted to the underlying data type
+ */
+#define bcmdb_record_get_read(_sor, _key, _record_type) \
+    ({ \
+        assert(sizeof(_record_type)==bcmdb_set_entry_size(_sor));  \
+        (const _record_type *)bcmdb_record_getraw_read(_sor, _key);\
+     })
+
+
+/** Unlock record locked for reading.
+ *
+ * This function must be called after \ref bcmdb_record_get_read or
+ * \ref bcmdb_record_getraw_read. Following bcmdb_record_read_unlock
+ * call pointer returned by \ref bcmdb_record_get_read becomes invalid.
+ * 
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * 
+ */
+void bcmdb_record_unlock_read(bcmdb_set *sor, bcmdb_key key);
+
+
+/** Read record data into user area.
+ *
+ * The function aquires read-lock, reads data into user area and releases read-lock.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \param[in]   offset          offset in data record
+ * \param[in]   size            data size. Note that offset+size must be <= record_size
+ * \param[in]   data            data pointer.
+ * \return
+ *      =0-OK\n
+ *      <0-error code
+ */
+int bcmdb_record_read(bcmdb_set *sor, bcmdb_key key, int offset, int size, void *data);
+
+
+/** Get record field.
+ *
+ * The macro returns record field value.
+ *
+ * \param[in]   _sor            parent set of records
+ * \param[in]   _key            record key
+ * \param[in]   _record_type    type of the underlying data structure.
+ * \param[in]   _field_name     data structure field name
+ * \param[out]  _p_field_value  pointer of variable where data structure field value should be returned
+ * \return
+ *      =0-OK\n
+ *      <0-error code
+ */
+#define bcmdb_record_read_field(_sor, _key, _record_type, _field_name, _p_field_value) \
+    bcmdb_record_read(_sor, _key, offsetof(_record_type, _field_name), \
+                        sizeof(*(_p_field_value)), _p_field_value);
+
+
+/** Lock record for writing and return record data pointer.
+ *
+ * The function aquires write-lock and returns pointer to data structure stored in data base record.\n
+ * write-lock must be released separately when the pointer is no longer in use.
+ * The function is low-level. It is recommended to use macro \ref bcmdb_record_get_write instead.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \return
+ *      data pointer. NULL if there is no record matching the key.
+ */
+void *bcmdb_record_getraw_write(bcmdb_set *sor, bcmdb_key key);
+
+
+/** Lock record for writing and return record data pointer.
+ *
+ * The function aquires write-lock and returns pointer to data structure stored in data base record.\n
+ * write-lock must be released separately when the pointer is no longer in use.
+ *
+ * \param[in]   _sor            parent set of records
+ * \param[in]   _key            record key
+ * \param[in]   _record_type    underlying data type.
+ * \return
+ *      data pointer casted to the underlying data type
+ */
+#define bcmdb_record_get_write(_sor, _key, _record_type)      \
+    ({ \
+        assert(sizeof(_record_type)==bcmdb_set_entry_size(_sor));   \
+        (_record_type *)bcmdb_record_getraw_write(_sor, _key);\
+     })
+
+
+/** Unlock record locked for writing.
+ *
+ * This function must be called after \ref bcmdb_record_get_write or
+ * \ref bcmdb_record_getraw_write. Following bcmdb_record_unlock_write
+ * call pointer returned by \ref bcmdb_record_get_write becomes invalid.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   is_cancellation TRUE=cancel transaction
+ *
+ */
+void bcmdb_record_unlock_write(bcmdb_set *sor, int is_cancellation);
+
+
+
+/** Write record data.
+ *
+ * The function aquires modify-lock, replaces data stored in data base record
+ * and releses the lock.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \param[in]   offset          offset in data record
+ * \param[in]   size            data size. Note that offset+size must be <= record_size
+ * \param[in]   data            data pointer.
+ * \return
+ *      =0-OK\n
+ *      <0-error code
+ */
+int bcmdb_record_write(bcmdb_set *sor, bcmdb_key key, int offset, int size, const void *data);
+
+
+/** Write record field.
+ *
+ * The macro updates record field value.\n
+ * The macro aquires and releases record-modify lock.
+ *
+ * \param[in]   _sor            parent set of records
+ * \param[in]   _key            record key
+ * \param[in]   _record_type    type of the underlying data structure.
+ * \param[in]   _field_name     data structure field name
+ * \param[in]   _field_value    field value
+ * \return
+ *      =0-OK\n
+ *      <0-error code
+ */
+#define bcmdb_record_write_field(_sor, _key, _record_type, _field_name, _field_value) \
+    ({ \
+        typeof(((_record_type *)0)->_field_name) _f = _field_value;\
+        bcmdb_record_write(_sor, _key, offsetof(_record_type, _field_name), sizeof(_f), &_f);\
+    });
+
+
+/** Register notification function to get informed
+ * when data base set is modified.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   cb              callback function pointer
+ * \param[in]   cb_priv         private data that should be passed to the callback
+ * \return
+ *      =0  - OK\n
+ *      <0  - error code
+ */
+int bcmdb_set_notify_register(bcmdb_set *sor, bcmdb_notify_cb cb, long cb_priv);
+
+
+/** Data base iterator
+ *
+ * \param[in]   set             data base set
+ * \param[in]   prev            last entry. BCMDB_KEY_ANY=start from the beginning
+ * \return  data base entry key following prev or BCMDB_KEY_NO_MORE if end is reached.\n
+ *          BCMDB_KEY_INVAL is reqturned if prev key is invalid
+ */
+bcmdb_key bcmdb_set_iterate(const bcmdb_set *set, bcmdb_key prev);
+
+
+/** Print database structure.
+ *
+ * \param[in]   set             root set
+ */
+void bcmdb_set_print_structure(const bcmdb_set *set);
+
+
+/** Format record for printing.
+ *
+ * The function converts record data to human-readable format.
+ *
+ * \param[in]   sor             parent set of records
+ * \param[in]   key             record key
+ * \param[out]  buffer          output buffer
+ * \param[in]   size            buffer size
+ * \return
+ *      >=0-amount of data placed in the buffer\n
+ *      <0-error code
+ */
+int bcmdb_record_read_formatted(bcmdb_set *sor, bcmdb_key key, char *buffer, int size);
+
+
+/** @} end of bcmdb_e group */
+
+
+#endif /* #ifndef BCMDB_ENGINE_H */
+
diff --git a/bal_release/src/common/db_engine/unitest.c b/bal_release/src/common/db_engine/unitest.c
new file mode 100644
index 0000000..ca638ce
--- /dev/null
+++ b/bal_release/src/common/db_engine/unitest.c
@@ -0,0 +1,490 @@
+/******************************************************************************
+ *
+ *  <:copyright-BRCM:2016:DUAL/GPL:standard
+ *  
+ *     Copyright (c) 2016 Broadcom
+ *     All Rights Reserved
+ *  
+ *  Unless you and Broadcom execute a separate written software license
+ *  agreement governing use of this software, this software is licensed
+ *  to you under the terms of the GNU General Public License version 2
+ *  (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+ *  with the following added to such license:
+ *  
+ *     As a special exception, the copyright holders of this software give
+ *     you permission to link this software with independent modules, and
+ *     to copy and distribute the resulting executable under terms of your
+ *     choice, provided that you also meet, for each linked independent
+ *     module, the terms and conditions of the license of that module.
+ *     An independent module is a module which is not derived from this
+ *     software.  The special exception does not apply to any modifications
+ *     of the software.
+ *  
+ *  Not withstanding the above, under no circumstances may you combine
+ *  this software in any way with any other Broadcom software provided
+ *  under a license other than the GPL, without Broadcom's express prior
+ *  written consent.
+ *  
+ *  :>
+ *
+ *****************************************************************************/
+ 
+/*
+ * unitest.c
+ *
+ *  Created on: 2013-12-10
+ *      Author: swallace
+ */
+
+#include "bcmos_system.h"
+#include "bcm_db_engine.h"
+
+/* EPON LLID data structure subset */
+typedef enum 
+    {
+    /* Free entry in the LLID management table, available for assignment on an
+       MPCP register request. */
+    unassigned_llid,
+    /* Locked out waiting on a timer to release the LLID */
+    not_registered_llid,
+    /* Waiting for permission to register from host */
+    ignored_llid,
+    /* LLID has been assigned to an ONU MAC but not registered. Intermediate 
+       state before the ONU returns a registration ack. */
+    wait_reg_ack_llid,
+    /* OLT link is in-service; user traffic is allowed */
+    inservice_llid,
+    wait_no_reports_llid,
+    /* The following state only applies to multicast/flood links */
+    in_service_mcast_llid,
+    /* We want a "Reserved" state for things like Optical monitoring */
+    reserved_llid,
+    /* We have detected a rogue ONU on this LLID - don't use it! */
+    quarantined_llid,
+    llid_state_count,
+    } epon_olt_llid_state;
+
+static char *get_llid_state_string(epon_olt_llid_state llid_state)
+{
+    static char *llid_state_strings[]= {
+        "unassigned",
+        "not_registered",
+        "ignored",
+        "wait_reg_ack",
+        "inservice",
+        "wait_no_reports",
+        "in_service_mcast",
+        "reserved",
+        "quarantined"
+        };
+
+    return llid_state_strings[(uint16_t)(llid_state)];
+}
+
+#define MAX_LINKS_PER_PORT  512
+#define MAX_PORTS_PER_HALF_CHIP 8 
+#define MAX_LINKS_PER_HALF_CHIP ((MAX_LINKS_PER_PORT)*(MAX_PORTS_PER_HALF_CHIP))
+
+typedef uint8_t core_epon;
+typedef uint16_t hw_link_index;
+
+typedef struct epon_db_olt_llid_rec
+    {
+    epon_olt_llid_state state;
+    core_epon epon;
+    hw_link_index index;
+    } epon_db_olt_llid_rec;
+
+
+typedef struct epon_msg_olt_llid_rec
+{
+    uint16_t index;
+    epon_db_olt_llid_rec llid_rec;
+} epon_msg_olt_llid_rec;
+
+typedef enum
+    {
+    enabled,
+    disabled,
+    } epon_olt_port_state;
+
+static char *get_port_state_string(epon_olt_port_state port_state)
+{
+    static char *port_state_strings[]= {
+        "enabled",
+        "disabled",
+        };
+
+    return port_state_strings[(uint16_t)(port_state)];
+}
+
+
+typedef struct epon_db_olt_port_rec
+    {
+    epon_olt_port_state state;
+    } epon_db_olt_port_rec;
+
+typedef struct epon_msg_olt_port_rec
+{
+    uint16_t index;
+    epon_db_olt_port_rec port_rec;
+} epon_msg_olt_port_rec;
+
+typedef enum
+{
+    epon_olt_link_record,
+    epon_olt_port_record,
+    num_db_tables,
+} db_tables;
+
+
+/*  Master database handle  */
+static bcmdb_set *db_sos_set;
+
+static bcmdb_set* epon_get_db_handle(void)
+{
+    return db_sos_set;
+}
+
+#define LINK_REC_DB() bcmdb_set *db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_link_record)
+
+#define PORT_REC_DB bcmdb_set *db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_port_record)
+
+/* Database test messages -   */
+typedef enum 
+    {
+    update_link_db = 20,
+    update_port_db = 21,
+    dump_db   = 30,
+    } dbtest_msgid;
+
+static inline const epon_db_olt_llid_rec *epon_olt_get_llid_rec_read(uint16_t index)
+    { 
+    LINK_REC_DB();
+    return bcmdb_record_get_read(db_set, index, epon_db_olt_llid_rec);
+    };
+
+static inline void epon_db_olt_unlock_llid_rec(uint16_t index)
+    {
+    LINK_REC_DB();
+    bcmdb_record_unlock_read(db_set, index);
+    }
+
+
+#define OltGetLlidRecWrite(index) \
+    ({ \
+    LINK_REC_DB();                                                  \
+    bcmdb_record_get_write(db_set, index, epon_db_olt_llid_rec);\
+     })
+
+#define OltCommitLlidRec(index) \
+    ({ \
+    LINK_REC_DB();                                  \
+    bcmdb_record_unlock_write(db_set, BCMOS_FALSE);\
+     })
+
+
+static void ut_dump_db(void)
+{
+    uint16_t index;
+    bcmdb_set *db_set;
+
+    db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_port_record);
+    for (index = 0; index < MAX_PORTS_PER_HALF_CHIP; index++)
+    {
+        const epon_db_olt_port_rec *port_rec;
+        port_rec = bcmdb_record_get_read(db_set, index, epon_db_olt_port_rec);
+        BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, 
+                    "Record %d, state %s\n", index, 
+                    get_port_state_string(port_rec->state));
+        bcmdb_record_unlock_read(db_set, index);
+    }
+
+    for (index = 0; index < MAX_LINKS_PER_HALF_CHIP; index++)
+    {
+        const epon_db_olt_llid_rec *llid_rec;
+        llid_rec = epon_olt_get_llid_rec_read(index);
+        if (llid_rec->state != unassigned_llid)
+        {
+            BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, 
+                    "Record %d, port %d, state %s\n", llid_rec->index, 
+                    llid_rec->epon, get_llid_state_string(llid_rec->state));
+        }
+        epon_db_olt_unlock_llid_rec(index);
+    }
+
+
+}
+
+static void ut_update_link_db(epon_msg_olt_llid_rec *rec)
+{
+    epon_db_olt_llid_rec *llid_rec;
+    llid_rec=OltGetLlidRecWrite(rec->index);
+    llid_rec->state=rec->llid_rec.state;
+
+    OltCommitLlidRec(index);
+}
+
+static void ut_update_port_db(epon_msg_olt_port_rec *rec)
+{
+    bcmdb_set *db_set;
+    epon_db_olt_port_rec *port_rec;
+
+    db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_port_record);
+    port_rec = bcmdb_record_get_write(db_set, rec->index, epon_db_olt_port_rec);
+
+    port_rec->state=rec->port_rec.state;
+
+    bcmdb_record_unlock_write(db_set, BCMOS_FALSE);
+}
+
+
+static void ut_msg_handler(dbtest_msgid id, void *data)
+    {
+    switch (id)
+        {
+        case update_link_db:
+            ut_update_link_db((epon_msg_olt_llid_rec*)data);
+            break;
+
+        case update_port_db:
+            ut_update_port_db((epon_msg_olt_port_rec*)data);
+            break;
+
+        case dump_db:
+            ut_dump_db();
+            break;
+
+        default:
+            break;
+        }
+
+    }
+
+/* Database engine unit test functions */
+static uint16_t epon_db_data_init(void)
+   {
+    uint16_t index;
+    bcmdb_set *db_set;
+    int rc = 0;
+
+    db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_link_record);
+    for ( index = 0; 
+         (index < MAX_LINKS_PER_HALF_CHIP) && (rc >= 0) && (db_set != NULL) ; 
+          index++)
+        {
+        epon_db_olt_llid_rec llid_rec;
+
+        llid_rec.state = unassigned_llid;
+        llid_rec.epon = index/MAX_LINKS_PER_PORT;
+        llid_rec.index = index;
+        rc = bcmdb_record_add(db_set, index, (void *)&llid_rec);
+        }
+
+    db_set = bcmdb_set_handle(epon_get_db_handle(), epon_olt_port_record);
+    for ( index = 0;
+         (index < MAX_PORTS_PER_HALF_CHIP) && (rc >= 0) && (db_set != NULL) ; 
+          index++)
+        {
+        epon_db_olt_port_rec port_rec;
+        port_rec.state = disabled;
+        rc = bcmdb_record_add(db_set, index, (void *)&port_rec);
+        }
+    return rc;
+    }
+
+static int epon_db_instance_init(void)
+    {
+    bcmdb_sos_init db_sos_inst;
+    bcmdb_sor_init db_sor_inst;
+    const char* db_name = "EPON STACK";
+    const char* db_llid_name = "EPON LINK REC";
+    const char* db_eport_name = "EPON PORT REC";
+    bcmdb_set *db_sor_set;
+    int rc;
+
+    db_sos_inst.name = db_name;
+    db_sos_inst.backend_type = BCMDB_BACKEND_ARRAY;
+    db_sos_inst.max_entries = num_db_tables; 
+    rc = bcmdb_make_set_of_sets(&db_sos_inst, &db_sos_set);
+
+    if (rc >= 0)
+        {
+        db_sor_inst.name = db_llid_name;
+        db_sor_inst.backend_type = BCMDB_BACKEND_ARRAY;
+        db_sor_inst.lock_policy = BCMDB_LOCK_NB_READ_SHADOW_WRITE;
+        db_sor_inst.max_entries = MAX_LINKS_PER_HALF_CHIP; 
+        db_sor_inst.record_size = sizeof(epon_db_olt_llid_rec);
+        db_sor_inst.format = NULL;
+        bcmdb_make_set_of_records(&db_sor_inst, BCMOS_TRUE, &db_sor_set);
+        
+        rc = bcmdb_set_add(epon_get_db_handle(), 
+                           epon_olt_link_record, db_sor_set);
+        }
+    if (rc >= 0)
+        {
+        db_sor_inst.name = db_eport_name;
+        db_sor_inst.backend_type = BCMDB_BACKEND_ARRAY;
+        db_sor_inst.lock_policy = BCMDB_LOCK_NB_READ_SHADOW_WRITE;
+        db_sor_inst.max_entries = MAX_PORTS_PER_HALF_CHIP; 
+        db_sor_inst.record_size = sizeof(epon_db_olt_port_rec);
+        db_sor_inst.format = NULL;
+        rc = bcmdb_make_set_of_records(&db_sor_inst, BCMOS_TRUE, &db_sor_set);
+        }
+
+    rc = bcmdb_set_add(epon_get_db_handle(), epon_olt_port_record, db_sor_set);
+
+    BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "database creation returnd %d\n", rc);
+
+    if (rc >= 0)
+        {
+        rc = epon_db_data_init();
+        }
+    return rc;
+    }
+
+/* Thread handlers - so that the DB accesses can be tested across multiple
+   threads. */
+static int task1_handler(long data)
+{
+    bcmos_msg_queue *q = (bcmos_msg_queue *)data;
+    bcmos_msg *msg;
+
+    BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "traditional task handler\n");
+
+    while (1)
+    {
+        BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "Waiting for message\n");
+
+        bcmos_msg_recv(q, BCMOS_WAIT_FOREVER, &msg);
+        BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, 
+                    "Received message ID %d, data %p\n",
+                    msg->type, msg->data);
+    
+        ut_msg_handler(msg->type, msg->data);
+        bcmos_usleep(100000);
+    }
+
+    return 0;
+}
+
+
+static bcmos_errno mod1_init(long data)
+{
+    BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "%ld\n", data);
+    return BCM_ERR_OK;
+}
+
+static void mod_msg_handler(bcmos_module_id module_id, bcmos_msg *msg)
+{
+    BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "module %d  msg %d  data %p\n", 
+                module_id, msg->type, msg->data);
+
+    ut_msg_handler(msg->type, msg->data);
+}
+
+
+
+/* Unit test function -  */
+int main(int argc, char *argv[])
+{
+    bcmos_task_parm tp = {};
+    bcmos_msg_queue_parm qp = {};
+    bcmos_module_parm mp = {};
+    bcmos_msg msg1 = {};
+    bcmos_msg msg2 = {};
+
+    bcmos_task t1;
+    bcmos_task t2;
+    bcmos_msg_queue q1;
+    bcmos_errno rc;
+    epon_msg_olt_llid_rec link_rec1, link_rec2;
+    epon_msg_olt_port_rec port_msg1, port_msg2;
+
+    bcmos_init();
+    bcmos_trace_level_set(BCMOS_TRACE_LEVEL_DEBUG);
+
+    if (epon_db_instance_init() < 0)
+        {
+        BCMOS_TRACE(BCMOS_TRACE_LEVEL_ERROR,
+                    "Could not instantiate a Database\n");
+        return BCM_ERR_NOMEM;
+        }
+    
+    BCMOS_TRACE(BCMOS_TRACE_LEVEL_INFO, "Database set %p\n", 
+                bcmdb_set_handle(db_sos_set, epon_olt_link_record));
+
+    /* Create message queue */
+    qp.name = "msg queue1";
+    qp.size = 16;
+    qp.high_wm = 14;
+    qp.low_wm = 12;
+    rc = bcmos_msg_queue_create(&q1, &qp);
+
+    /* Create a couple of threads */
+    tp.name = "task1";
+    tp.handler = task1_handler;
+    tp.data = (long)&q1;
+    rc = bcmos_task_create(&t1, &tp);
+
+    tp.name = "task2";
+    tp.handler = NULL;
+    tp.data = 0;
+    rc = bcmos_task_create(&t2, &tp);
+
+    /* Register a module */
+    mp.qparm.name = "module1";
+    mp.qparm.size = 16;
+    mp.init = mod1_init;
+    bcmos_module_create(BCMOS_MODULE_ID_TEST1, &t2, &mp);
+    
+    /* Wait some */
+    bcmos_usleep(2000000);
+
+    /*  Send a message to update the DB - enable a port*/
+    port_msg1.index=5;
+    port_msg1.port_rec.state=enabled;
+    msg1.type = update_port_db;
+    msg1.data = &port_msg1;
+    bcmos_msg_send(&q1, &msg1, BCMOS_MSG_SEND_NO_FREE_ON_ERROR);
+
+    /*  Send a message to update the DB - enable a port*/
+    port_msg2.index=3;
+    port_msg2.port_rec.state=enabled;
+    msg2.type = update_port_db;
+    msg2.data = &port_msg2;
+    bcmos_msg_send(&q1, &msg2, BCMOS_MSG_SEND_NO_FREE_ON_ERROR);
+
+    /* Wait some */
+    bcmos_usleep(2000000);
+
+    /*  Send a message to update the DB - put a link In Service*/
+    link_rec1.index=14;
+    link_rec1.llid_rec.state=inservice_llid;
+    msg1.type = update_link_db;
+    msg1.data = &link_rec1;
+    bcmos_msg_send(&q1, &msg1, BCMOS_MSG_SEND_NO_FREE_ON_ERROR);
+
+    /*  Send a message to update the DB - quarantine a link */
+    link_rec2.index=22;
+    link_rec2.llid_rec.state=quarantined_llid;
+    msg2.type = update_link_db;
+    msg2.data = &link_rec2;
+    msg2.handler = mod_msg_handler;
+    bcmos_msg_send_to_module(BCMOS_MODULE_ID_TEST1, &msg2, BCMOS_MSG_SEND_NO_FREE_ON_ERROR);
+
+    /* Wait some */
+    bcmos_usleep(2000000);
+
+    /* Send a message to dump the DB */
+    msg1.type = dump_db;
+    msg1.handler = mod_msg_handler;
+    msg1.data = NULL;
+    bcmos_msg_send_to_module(BCMOS_MODULE_ID_TEST1, &msg1, BCMOS_MSG_SEND_NO_FREE_ON_ERROR);
+
+
+    /* Wait some */
+    bcmos_usleep(2000000);
+
+    return rc;
+}