BAL and Maple Release 2.2

Signed-off-by: Shad Ansari <developer@Carbon.local>
diff --git a/bcm68620_release/release/host_driver/transport/Makefile b/bcm68620_release/release/host_driver/transport/Makefile
new file mode 100644
index 0000000..3fd7dda
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/Makefile
@@ -0,0 +1,47 @@
+# Transport
+#
+MOD_NAME = transport
+MOD_TYPE = lib
+MOD_DEPS = utils cli model common_api tr_plugin dev_log
+
+ifeq ("$(RELEASE_BUILD)", "y")
+    MOD_DEPS += api_cli
+    TR_PERF_TEST = n
+else
+    MOD_DEPS += api_cli_helpers
+endif
+
+ifeq ("$(SUBSYSTEM)", "host")
+    MOD_DEPS += device_selector
+endif
+
+ifeq ("$(OS_KERNEL)", "linux")
+   MOD_DEPS += dev_log_linux
+endif
+
+srcs = bcmtr_transport.c bcmtr_config.c bcmtr_debug.c
+
+ifeq ("$(ENABLE_CLI)", "y")
+	srcs += bcmtr_transport_cli.c bcmtr_debug_cli.c
+else
+    TR_PERF_TEST = n
+endif
+
+CONFIG_TRANSPORT_RAW ?= n
+CONFIG_TRANSPORT_UDP ?= y
+
+# Extra configuration
+ifeq ("$(CONFIG_TRANSPORT_RAW)", "y")
+	MOD_DEPS += tr_plugin_mux
+	CONFIG_TRANSPORT_UDP = n
+endif
+ifeq ("$(CONFIG_TRANSPORT_UDP)", "y")
+	MOD_DEPS += tr_plugin_udp
+endif
+
+TR_PERF_TEST ?= y
+ifeq ("$(TR_PERF_TEST)", "y")
+    EXTRA_DEFINES += -DBCMTR_PERFTEST
+    EXTRA_INCLUDES += -I$(SRC_DIR)/test
+    srcs += test/bcmtr_perftest.c
+endif
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_config.c b/bcm68620_release/release/host_driver/transport/bcmtr_config.c
new file mode 100644
index 0000000..4fc4749
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_config.c
@@ -0,0 +1,82 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#include "bcmtr_internal.h"
+#include "bcmtr_interface.h"
+#include "bcmtr_plugin.h"
+
+
+#ifdef BCMTR_UDP_SUPPORT
+
+uint32_t bcmtr_host_ip = BCMTR_TR_UDP_HOST_IP;
+uint16_t bcmtr_host_udp_port = BCMTR_TR_UDP_HOST_PORT;
+uint32_t bcmtr_olt_ip[BCMTR_MAX_OLTS];
+uint16_t bcmtr_olt_udp_port[BCMTR_MAX_OLTS];
+
+#endif
+
+/* Fetch configuration
+ * set-up transport driver plugin
+ * Eventually this function can read from config file or device control driver.
+ * For now - hardcode
+ */
+bcmos_errno bcmtr_cfg_get(bcmolt_devid device, bcmtr_cfg *cfg, bcmtr_driver *driver)
+{
+    bcmos_errno rc = BCM_ERR_NOT_SUPPORTED;
+
+    cfg->max_retries = BCMTR_MAX_RETRIES;
+    cfg->msg_timeout = BCMTR_MSG_TIMEOUT;
+    cfg->max_fragments = BCMTR_MAX_FRAGMENTS;
+    cfg->max_requests = BCMTR_MAX_REQUESTS;
+    cfg->max_autos = BCMTR_MAX_AUTOS;
+    cfg->max_mtu = BCMTR_MAX_MTU_SIZE;
+    cfg->msg_wait_timeout = BCMTR_MSG_WAIT_MS;
+    cfg->msg_ready_timeout = BCMTR_MSG_READY_MS;
+    cfg->plugin_cfg.type = BCMTR_TR_TYPE;
+    cfg->rx_thread_priority = TASK_PRIORITY_TRANSPORT_RX;
+
+#ifdef BCMTR_UDP_SUPPORT
+#ifdef BCM_SUBSYSTEM_HOST
+    cfg->plugin_cfg.x.udp.my_ip = bcmtr_host_ip;
+    cfg->plugin_cfg.x.udp.remote_ip = bcmtr_olt_ip[device];
+    cfg->plugin_cfg.x.udp.my_port = bcmtr_host_udp_port + device;
+    cfg->plugin_cfg.x.udp.remote_port = bcmtr_olt_udp_port[device];
+#else
+    cfg->plugin_cfg.x.udp.my_ip = bcmtr_olt_ip[device];
+    cfg->plugin_cfg.x.udp.remote_ip = bcmtr_host_ip;
+    cfg->plugin_cfg.x.udp.my_port = bcmtr_olt_udp_port[device];
+    cfg->plugin_cfg.x.udp.remote_port = bcmtr_host_udp_port + device;
+#endif
+#endif
+
+    rc = bcmtr_plugin_init(&cfg->plugin_cfg, driver);
+
+    return rc;
+}
+
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_debug.c b/bcm68620_release/release/host_driver/transport/bcmtr_debug.c
new file mode 100644
index 0000000..889630c
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_debug.c
@@ -0,0 +1,876 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#include <bcmos_system.h>
+#include <bcmtr_debug.h>
+#include <bcmcli_session.h>
+#include <bcm_api_cli_helpers.h>
+#include <bcmolt_math.h>
+
+#ifdef ENABLE_LOG
+#include <bcm_dev_log.h>
+#endif
+
+#ifdef ENABLE_CLI
+#include <bcmtr_debug_cli.h>
+#endif
+
+/* Messages are recorded in the following format:
+ * uint32_t - event type
+ * uint32_t - timestamp
+ * uint32_t - message data size
+ * <message data> - padded to the nearest 4 bytes
+ * uint32_t - total size - total capture entry size, including control info
+ */
+
+/* Overhead size: entry header + uint32_t suffix */
+#define BCMTR_CAPTURE_OVERHEAD_SIZE  (sizeof(bcmtr_capture_entry) + sizeof(uint32_t))
+
+#define BCMTR_CAPTURE_ENTRY_FIELDS  (sizeof(bcmtr_capture_entry) / sizeof(uint32_t))
+
+/* Maximum total number of characters for a message dump */
+#define BCMTR_MAX_MSG_DUMP_STR_SIZE 4096
+
+/* Capture control block */
+typedef struct
+{
+    bcmtr_capture_parm parm;            /* Capture configuration */
+    bcmos_bool active;
+
+    uint8_t *start;
+    uint8_t *end;
+    uint8_t *cur;
+    uint32_t size;                      /* Buffer size */
+    uint32_t used;                      /* Bytes used */
+    uint32_t wa;                        /* Number of times buffer wrapped around */
+    uint32_t events;                    /* Number of capture events */
+} bcmtr_capture_buf;
+
+static bcmtr_capture_buf capture_buf[BCMTR_MAX_OLTS];
+
+/* CLI session where to dump */
+static bcmcli_session *bcmtr_cld_session;
+
+#ifdef ENABLE_LOG
+/* Logger used for BCMTR_CLD_LOG */
+static dev_log_id bcmtr_cld_log_id;
+#endif
+
+/* Global variable: per msg_id CLD level */
+bcmtr_cld_type bcmtr_cld_active_level[BCMTR_MAX_OLTS][BCMOLT_GROUP_ID__NUM_OF];
+
+/* Create a dummy CLI session so we can print to a buffer internally before printing to the real CLI.
+ * This way, the output can't be interrupted by another print. */
+static char bcmtr_cld_scratch_buf[BCMTR_MAX_MSG_DUMP_STR_SIZE];
+static uint32_t bcmtr_cld_scratch_pos = 0;
+static bcmcli_session *bcmtr_cld_scratch_session;
+
+/*
+ * Internal functions
+ */
+
+/* CLI session print callback for the scratch buffer */
+static int bcmtr_log_cli_print(bcmcli_session *session, const char *buf, uint32_t size)
+{
+    size = MIN(size, BCMTR_MAX_MSG_DUMP_STR_SIZE - bcmtr_cld_scratch_pos);
+    if (size > 0)
+    {
+        memcpy(&bcmtr_cld_scratch_buf[bcmtr_cld_scratch_pos], buf, size);
+    }
+    bcmtr_cld_scratch_pos += size;
+    return size;
+}
+
+/* Get message event name */
+static inline const char *bcmtr_cld_event_name(bcmtr_cld_event_type ev)
+{
+    static const char *ev_name[] = {
+        [BCMTR_CLD_EV_SEND]     = "tx",
+        [BCMTR_CLD_EV_RESEND]   = "re-tx",
+        [BCMTR_CLD_EV_RECV]     = "rx",
+        [BCMTR_CLD_EV_RECV_DISCARD] = "rx-discard",
+        [BCMTR_CLD_EV_TIMEOUT] = "timeout"
+    };
+    return ev_name[ev];
+}
+
+/* Store data in capture buffer */
+static inline void _bcmtr_capture_store(bcmtr_capture_buf *tb, const void *data, uint32_t size)
+{
+    int32_t left = (int32_t)(tb->end - tb->cur);
+    if (left >= (int32_t)size)
+    {
+        memcpy(tb->cur, data, size);
+        tb->cur += size;
+    }
+    else
+    {
+        memcpy(tb->cur, data, left);
+        memcpy(tb->start, (const uint8_t *)data + left, size - left);
+        tb->cur = tb->start + size - left;
+        ++tb->wa;
+    }
+    tb->used += size;
+    if (tb->used > tb->size)
+        tb->used = tb->size;
+}
+
+
+/* Get capture entry size and start pointer given pointer right after the entry */
+static void _bcmtr_capture_get_prev(bcmtr_capture_buf *tb, uint8_t *ptr, uint32_t *prev_size, uint8_t **prev_ptr)
+{
+    uint32_t size;
+    uint8_t *prev;
+
+    if (ptr == tb->start)
+        ptr = tb->end;
+    size = *(((uint32_t *)(long)ptr) - 1);
+    BUG_ON(!size || size > 0xffff || (size & 0x3));
+    prev = ptr - size;
+    if (prev < tb->start)
+        prev = tb->end - (size - (ptr - tb->start));
+    *prev_size = size;
+    *prev_ptr = prev;
+}
+
+/* Get number of complete messages stored in capture buffer */
+static uint32_t _bcmtr_capture_nmsgs(bcmtr_capture_buf *tb)
+{
+    uint32_t n = 0;
+    uint32_t prev_length;
+    uint8_t *prev_start = tb->cur;
+
+    if (!tb->used)
+        return 0;
+
+    /* Unwind capture buffer backward */
+    while (n < tb->events)
+    {
+        uint8_t *prev = prev_start;
+        _bcmtr_capture_get_prev(tb, prev, &prev_length, &prev_start);
+        if (prev_start >= prev)
+            break;
+        ++n;
+    }
+
+    /* If buffer has wrapped around - continue unwinding */
+    if (tb->wa)
+    {
+        while (prev_start >= tb->cur)
+        {
+            _bcmtr_capture_get_prev(tb, prev_start, &prev_length, &prev_start);
+            ++n;
+        }
+    }
+
+    return n;
+}
+
+static inline void _bcmtr_capture_wrap(uint8_t **cur, bcmtr_capture_buf *tb)
+{
+    if (*cur > tb->end)
+    {
+        *cur = tb->start + (*cur - tb->end);
+    }
+}
+static void _bcmtr_capture_unwind(bcmtr_capture_buf *tb, uint8_t **start, uint32_t *count)
+{
+    uint32_t prev_length;
+    uint8_t *prev_start;
+    uint32_t n = 0;
+    uint8_t *cur_hdr = NULL;
+
+    prev_start = tb->cur;
+    while (n < tb->events)
+    {
+        uint8_t *prev = prev_start;
+        _bcmtr_capture_get_prev(tb, prev, &prev_length, &prev_start);
+        if (prev_start >= prev)
+            break;
+        ++n;
+        cur_hdr = prev_start;
+    }
+
+    /* If buffer has wrapped around - continue unwinding */
+    if (tb->wa)
+    {
+        while (prev_start >= tb->cur)
+        {
+            cur_hdr = prev_start;
+            _bcmtr_capture_get_prev(tb, prev_start, &prev_length, &prev_start);
+            ++n;
+        }
+    }
+
+    *start = cur_hdr;
+    *count = n;
+}
+
+static inline uint32_t _bcmtr_capture_msg_size_get(uint8_t *buf)
+{
+    /* WARNING: do NOT access any members of bcmtr_capture_entry other than msg_size (the first member) as they may
+       have been wrapped to the beginning of the buffer. */
+    return ((bcmtr_capture_entry *)(long)buf)->msg_size;
+}
+
+static void _bcmtr_capture_copy(
+    bcmtr_capture_buf *tb,
+    uint8_t **dst,
+    uint8_t *src,
+    uint32_t to_copy,
+    uint32_t *remaining)
+{
+    uint32_t left;
+
+    left = tb->end - src;
+    if (left < to_copy)
+    {
+        memcpy(*dst, src, left);
+        memcpy((*dst) + left, tb->start, to_copy - left);
+    }
+    else
+    {
+        memcpy(*dst, src, to_copy);
+    }
+    (*dst) += to_copy;
+    (*remaining) -= to_copy;
+}
+
+static void _bcmtr_capture_copy_bounded(
+    bcmtr_capture_buf *tb,
+    uint8_t **dst,
+    uint8_t *src,
+    uint32_t to_copy,
+    uint32_t *remaining,
+    uint32_t bound)
+{
+    if (bound < to_copy)
+    {
+        src += to_copy - bound;
+        to_copy = bound;
+    }
+    if ((*remaining) < to_copy)
+    {
+        to_copy = *remaining;
+    }
+    _bcmtr_capture_copy(tb, dst, src, to_copy, remaining);
+}
+
+/* Set capture, log, debug for selected messages */
+bcmos_errno bcmtr_cld_level_set(bcmolt_devid device, const bcmtr_cld_filter *filter, bcmtr_cld_type cld_level)
+{
+    bcmolt_group_id msg_id;
+    bcmos_errno rc;
+
+    if ((unsigned)device >= BCMTR_MAX_OLTS || !filter)
+    {
+        return BCM_ERR_PARM;
+    }
+
+    /* Handle wildcard object */
+    if (filter->object == BCMOLT_OBJECT_ANY)
+    {
+        bcmtr_cld_filter f = *filter;
+
+        for (f.object = 0; f.object <= BCMOLT_OBJ_ID__NUM_OF; f.object++)
+        {
+            bcmtr_cld_level_set(device, &f, cld_level);
+        }
+        return BCM_ERR_OK;
+    }
+
+    /* Handle wildcard group */
+    if (filter->group == BCMOLT_MGT_GROUP_ANY)
+    {
+        bcmtr_cld_filter f = *filter;
+
+        f.subgroup = BCMOLT_SUBGROUP_ANY;
+        for (f.group = BCMOLT_MGT_GROUP_CFG; f.group <= BCMOLT_MGT_GROUP__NUM_OF; f.group++)
+        {
+            bcmtr_cld_level_set(device, &f, cld_level);
+        }
+        return BCM_ERR_OK;
+    }
+
+    /* Handle wildcard subgroup */
+    if (filter->group == BCMOLT_MGT_GROUP_ANY || filter->subgroup == BCMOLT_SUBGROUP_ANY)
+    {
+        bcmtr_cld_filter f = *filter;
+
+        f.subgroup = 0;
+        for (f.subgroup = 0;
+            bcmolt_group_id_combine(f.object, f.group, f.subgroup, &msg_id) == BCM_ERR_OK;
+            f.subgroup++)
+        {
+            bcmtr_cld_level_set(device, &f, cld_level);
+        }
+        return BCM_ERR_OK;
+    }
+
+    /* If we are here - it is not a wildcard */
+    rc = bcmolt_group_id_combine(filter->object, filter->group, filter->subgroup, &msg_id);
+    if (rc)
+        return rc;
+
+    BUG_ON((unsigned)msg_id >= BCMOLT_GROUP_ID__NUM_OF);
+    bcmtr_cld_active_level[device][msg_id] = cld_level;
+    return BCM_ERR_OK;
+}
+
+/* Get capture, log, debug for selected message */
+bcmos_errno bcmtr_cld_level_get(bcmolt_devid device, const bcmtr_cld_filter *filter, bcmtr_cld_type *cld_level)
+{
+    bcmolt_group_id msg_id;
+    bcmos_errno rc;
+    if ((unsigned)device >= BCMTR_MAX_OLTS || !filter)
+    {
+        return BCM_ERR_PARM;
+    }
+    rc = bcmolt_group_id_combine(filter->object, filter->group, filter->subgroup, &msg_id);
+    if (rc)
+        return rc;
+    BUG_ON((unsigned)msg_id >= BCMOLT_GROUP_ID__NUM_OF);
+    *cld_level = bcmtr_cld_active_level[device][msg_id];
+    return BCM_ERR_OK;
+}
+
+/** Initialize capture */
+bcmos_errno bcmtr_capture_init(bcmolt_devid olt, const bcmtr_capture_parm *parm)
+{
+    bcmtr_capture_buf *tb;
+
+    if (olt >= BCMTR_MAX_OLTS || !parm)
+        return BCM_ERR_PARM;
+    if (capture_buf[olt].start)
+    {
+        bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: already initialized\n", olt);
+        return BCM_ERR_PARM;
+    }
+
+    if (parm->size < BCMTR_CAPTURE_MIN_BUF_SIZE)
+    {
+        bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: capture buffer is too small (%u < %d)\n",
+            olt, parm->size, BCMTR_CAPTURE_MIN_BUF_SIZE);
+        return BCM_ERR_PARM;
+    }
+
+    tb = &capture_buf[olt];
+    tb->size = parm->size & ~0x3;
+    /* User-supplied or dynamically allocated buffer ? */
+    if (parm->ptr)
+        tb->start = parm->ptr;
+    else
+    {
+        tb->start = bcmos_alloc(parm->size);
+        if (!tb->start)
+        {
+            bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: can't allocate capture buffer\n", olt);
+            tb->size = 0;
+            return BCM_ERR_NOMEM;
+        }
+    }
+    tb->end = (void *)((long)tb->start + parm->size);
+    tb->cur = tb->start;
+    tb->used = tb->wa = 0;
+    tb->active = parm->activate;
+    tb->parm = *parm;
+
+    return BCM_ERR_OK;
+}
+
+/** Destroy capture buffer */
+void bcmtr_capture_destroy(bcmolt_devid olt)
+{
+    bcmtr_capture_buf *tb;
+
+    if (olt >= BCMTR_MAX_OLTS)
+        return;
+    tb = &capture_buf[olt];
+    tb->active = BCMOS_FALSE;
+    if (tb->start && !tb->parm.ptr)
+        bcmos_free(tb->start);
+    memset(tb, 0, sizeof(*tb));
+}
+
+/** Get capture recording info */
+bcmos_errno bcmtr_capture_info_get(bcmolt_devid olt, bcmtr_capture_info *info)
+{
+    bcmtr_capture_buf *tb;
+
+    if (olt >= BCMTR_MAX_OLTS || !info)
+        return BCM_ERR_PARM;
+    tb = &capture_buf[olt];
+    if (tb->active)
+    {
+        bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: must stop first\n", olt);
+        return BCM_ERR_PARM;
+    }
+    info->size = tb->size;
+    info->used = tb->used;
+    info->wa   = tb->wa;
+    info->msgs = _bcmtr_capture_nmsgs(tb);
+    info->lost = tb->events - info->msgs;
+    return BCM_ERR_OK;
+}
+
+bcmos_errno bcmtr_capture_size_get(bcmolt_devid olt, uint32_t *size)
+{
+    bcmtr_capture_buf *tb;
+    uint32_t n = 0;
+    uint8_t *cur_hdr = NULL;
+    uint32_t i;
+
+    *size = 0;
+    if (olt >= BCMTR_MAX_OLTS)
+    {
+        return BCM_ERR_PARM;
+    }
+    tb = &capture_buf[olt];
+    if (!tb->start)
+    {
+        bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: not initialized\n", olt);
+        return BCM_ERR_PARM;
+    }
+    if (tb->active)
+    {
+        bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: must stop first\n", olt);
+        return BCM_ERR_PARM;
+    }
+
+    if (!tb->used)
+    {
+        return BCM_ERR_OK;
+    }
+
+    /* Unwind capture buffer backward to get to the 1st recorded message */
+    _bcmtr_capture_unwind(tb, &cur_hdr, &n);
+
+    /* "first" points to the 1st recorded entry and "n" contains number of messages.
+     * Now go forward and copy to the user buffer
+     */
+    BUG_ON(!cur_hdr);
+    for (i = 0; i < n; i++)
+    {
+        uint32_t msg_size = _bcmtr_capture_msg_size_get(cur_hdr) + sizeof(bcmtr_capture_entry);
+        uint32_t rounded_size = BCMOS_ROUND_UP(msg_size, sizeof(uint32_t));
+
+        (*size) += msg_size;
+
+        /* Move to the next entry in capture buffer */
+        cur_hdr += rounded_size + sizeof(uint32_t);
+        _bcmtr_capture_wrap(&cur_hdr, tb);
+    }
+
+    return BCM_ERR_OK;
+}
+
+bcmos_errno bcmtr_capture_read(bcmolt_devid olt, uint8_t *buf, uint32_t offset, uint32_t *length)
+{
+    bcmtr_capture_buf *tb;
+    uint32_t n = 0;
+    uint8_t *cur_hdr = NULL;
+    uint32_t cur_offset = 0;
+    uint32_t i;
+
+    if (olt >= BCMTR_MAX_OLTS || !buf)
+    {
+        return BCM_ERR_PARM;
+    }
+    tb = &capture_buf[olt];
+    if (!tb->start)
+    {
+        bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: not initialized\n", olt);
+        return BCM_ERR_PARM;
+    }
+    if (tb->active)
+    {
+        bcmcli_session_print(bcmtr_cld_session, "TRACE/%d: must stop first\n", olt);
+        return BCM_ERR_PARM;
+    }
+
+    if (!tb->used)
+    {
+        return BCM_ERR_OK;
+    }
+
+    /* Unwind capture buffer backward to get to the 1st recorded message */
+    _bcmtr_capture_unwind(tb, &cur_hdr, &n);
+
+    /* "first" points to the 1st recorded entry and "n" contains number of messages.
+     * Now go forward and copy to the user buffer
+     */
+    BUG_ON(!cur_hdr);
+    for (i = 0; (i < n) && ((*length) > 0); i++)
+    {
+        uint32_t msg_size = _bcmtr_capture_msg_size_get(cur_hdr);
+        uint32_t rounded_size = BCMOS_ROUND_UP(msg_size, sizeof(uint32_t));
+
+        cur_offset += sizeof(bcmtr_capture_entry);
+        if (cur_offset > offset)
+        {
+            uint32_t temp[BCMTR_CAPTURE_ENTRY_FIELDS];
+            uint8_t j;
+
+            for (j = 0; j < BCMTR_CAPTURE_ENTRY_FIELDS; j++)
+            {
+                temp[j] = BCMOLT_BUF_ENDIAN_CPU_TO_BUF(U32, *((uint32_t*)(long)cur_hdr));
+                cur_hdr += sizeof(uint32_t);
+                _bcmtr_capture_wrap(&cur_hdr, tb);
+            }
+
+            _bcmtr_capture_copy_bounded(
+                tb,
+                &buf,
+                (uint8_t*)temp,
+                sizeof(bcmtr_capture_entry),
+                length,
+                cur_offset - offset);
+        }
+        else
+        {
+            cur_hdr += sizeof(bcmtr_capture_entry);
+        }
+
+        cur_offset += msg_size;
+        if (cur_offset > offset)
+        {
+            _bcmtr_capture_copy_bounded(
+                tb,
+                &buf,
+                cur_hdr,
+                msg_size,
+                length,
+                cur_offset - offset);
+        }
+
+        /* Move to the next entry in capture buffer */
+        cur_hdr += rounded_size + sizeof(uint32_t);
+        _bcmtr_capture_wrap(&cur_hdr, tb);
+    }
+
+    return BCM_ERR_OK;
+}
+
+bcmos_bool bcmtr_capture_entry_get_next(bcmolt_buf *buf, bcmtr_capture_entry *hdr, uint8_t **msg)
+{
+    bcmos_bool valid;
+
+    BUG_ON(buf == NULL);
+    BUG_ON(hdr == NULL);
+
+    valid = bcmtr_capture_entry_unpack(buf, hdr);
+    if (msg != NULL)
+    {
+        *msg = bcmolt_buf_snap_get(buf);
+    }
+    return valid && bcmolt_buf_skip(buf, hdr->msg_size);
+}
+
+/** Decode and dump capture recording */
+bcmos_errno bcmtr_capture_dump(bcmcli_session *session, bcmolt_devid olt, uint32_t *nmsgs)
+{
+    bcmtr_capture_entry hdr;
+    uint8_t *msg_start;
+    bcmolt_buf buf;
+    uint8_t *data;
+    uint32_t length;
+    uint32_t remaining;
+    bcmos_errno rc;
+
+    rc = bcmtr_capture_size_get(olt, &length);
+    BCMOS_CHECK_RETURN_ERROR(BCM_ERR_OK != rc, rc);
+
+    /* Allocate temp buffer and read data into it */
+    data = bcmos_calloc(length);
+    if (data == NULL)
+    {
+        bcmcli_session_print(session, "TRACE/%d: no memory\n", olt);
+        return BCM_ERR_NOMEM;
+    }
+
+    remaining = length;
+    rc = bcmtr_capture_read(olt, data, 0, &remaining);
+    if (BCM_ERR_OK != rc)
+    {
+        bcmos_free(data);
+        return rc;
+    }
+
+    /* Dump */
+    bcmolt_buf_init(&buf, length - remaining, data, BCMOLT_BUF_ENDIAN_FIXED);
+    while (bcmtr_capture_entry_get_next(&buf, &hdr, &msg_start))
+    {
+        bcmolt_buf msg_buf;
+        bcmolt_msg *msg = NULL;
+        bcmos_errno err;
+
+        bcmcli_session_print(session, "\n%08x %s:\n", hdr.timestamp, bcmtr_cld_event_name(hdr.event));
+        bcmolt_buf_init(&msg_buf, hdr.msg_size, msg_start, BCMOLT_BUF_ENDIAN_FIXED);
+        err = bcmolt_msg_unpack(&msg_buf, &msg);
+        if (BCM_ERR_OK == err)
+        {
+            (void)apicli_msg_dump(session, msg);
+            bcmolt_msg_free(msg);
+        }
+        else
+        {
+            bcmcli_session_hexdump(session, msg_start, 0, hdr.msg_size, NULL);
+        }
+    }
+
+    bcmos_free(data);
+
+    *nmsgs = _bcmtr_capture_nmsgs(&capture_buf[olt]);
+
+    return BCM_ERR_OK;
+}
+
+/** (Re)start / Suspend capture recording */
+bcmos_errno bcmtr_capture_start_stop(bcmolt_devid olt, bcmos_bool start)
+{
+    if (olt >= BCMTR_MAX_OLTS)
+        return BCM_ERR_PARM;
+    if (!capture_buf[olt].start && start)
+    {
+        bcmcli_session_print(bcmtr_cld_session,
+            "TRACE/%d: Can't start recording - must initialize first\n", olt);
+        return BCM_ERR_PARM;
+    }
+    capture_buf[olt].active = start;
+    return BCM_ERR_OK;
+}
+
+bcmos_bool bcmtr_capture_is_active(bcmolt_devid olt)
+{
+    return capture_buf[olt].active;
+}
+
+bcmos_bool bcmtr_capture_entry_unpack(bcmolt_buf *buf, bcmtr_capture_entry *entry)
+{
+    return
+        bcmolt_buf_read_u32(buf, &entry->msg_size) &&
+        bcmolt_buf_read_u32(buf, &entry->event) &&
+        bcmolt_buf_read_u32(buf, &entry->timestamp);
+}
+
+/* Notify message to capture module - called from the transport layer */
+static void bcmtr_capture_notify(bcmolt_devid device, const bcmtr_hdr *trhdr,
+    bcmtr_cld_event_type ev, uint32_t ts,
+    const void *packed, uint32_t packed_length, bcmolt_msg *msg)
+{
+    bcmtr_capture_buf *tb;
+    bcmtr_capture_entry hdr;
+    uint32_t rounded_size;
+
+    tb = &capture_buf[device];
+
+    /* Sanity */
+    if (!packed)
+        return;
+    hdr.msg_size = packed_length;
+    /* Enable & overflow checks */
+    if (!tb->active)
+        return;
+    if (tb->parm.stop_on_full && (tb->used + hdr.msg_size + BCMTR_CAPTURE_OVERHEAD_SIZE > tb->size))
+        return;
+    hdr.timestamp = ts;
+    hdr.event = ev;
+    rounded_size = BCMOS_ROUND_UP(hdr.msg_size, sizeof(uint32_t));
+    _bcmtr_capture_store(tb, &hdr, sizeof(hdr));
+    _bcmtr_capture_store(tb, packed, rounded_size); /* overflow by up to 3 bytes; is this safe? */
+    rounded_size += sizeof(bcmtr_capture_entry) + sizeof(uint32_t);
+    _bcmtr_capture_store(tb, &rounded_size, sizeof(rounded_size));
+    ++tb->events;
+}
+
+/* Notify message to logger */
+static void bcmtr_log_notify(bcmolt_devid device, const bcmtr_hdr *hdr,
+    bcmtr_cld_event_type ev, uint32_t ts,
+    const void *packed, uint32_t packed_length, bcmolt_msg *msg)
+{
+#ifdef ENABLE_LOG
+    bcmos_errno err;
+    bcmolt_obj_id obj;
+    bcmolt_mgt_group group;
+    uint16_t subgroup;
+    const char *obj_name;
+    const char *subgroup_name;
+    const char *dummy_str;
+
+    err = bcmolt_group_id_split(hdr->msg_id, &obj, &group, &subgroup);
+    BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,);
+
+    err = api_cli_object_name(obj, &obj_name, &dummy_str);
+    BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,);
+
+    err = api_cli_object_subgroup_name(obj, group, subgroup, &subgroup_name, &dummy_str);
+    BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,);
+
+    /* log with the header but without file/line number (file/line number isn't helpful here). */
+    bcm_dev_log_log(
+        bcmtr_cld_log_id,
+        DEV_LOG_LEVEL_INFO,
+        BCM_LOG_FLAG_NONE,
+        "%s %s: corr_tag=%u instance=%d obj=%s group=%s subgrp=%s org_ts=%u err=%s\n",
+        bcmtr_cld_event_name(ev),
+        hdr->dir == BCMOLT_MSG_DIR_RESPONSE ? "response" : "request",
+        hdr->corr_tag,
+        hdr->instance,
+        obj_name,
+        apicli_mgt_group_to_str(group),
+        subgroup_name,
+        ts,
+        msg ? bcmos_strerror(msg->err) : "N/A");
+#endif
+}
+
+/* Dump message header and/or body */
+static void bcmtr_dump_notify(
+    bcmolt_devid device,
+    const bcmtr_hdr *hdr,
+    bcmtr_cld_event_type ev,
+    uint32_t ts,
+    const void *packed,
+    uint32_t packed_length,
+    bcmolt_msg *msg)
+{
+    bcmos_errno err;
+    bcmtr_cld_type val = bcmtr_cld_active_level[device][hdr->msg_id];
+    bcmolt_obj_id obj;
+    bcmolt_mgt_group group;
+    uint16_t subgroup;
+    const char *obj_name;
+    const char *subgroup_name;
+    const char *dummy_str;
+
+    err = bcmolt_group_id_split(hdr->msg_id, &obj, &group, &subgroup);
+    BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,);
+
+    err = api_cli_object_name(obj, &obj_name, &dummy_str);
+    BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,);
+
+    err = api_cli_object_subgroup_name(obj, group, subgroup, &subgroup_name, &dummy_str);
+    BCMOS_CHECK_RETURN(err != BCM_ERR_OK, err,);
+
+    /* always dump the message header to the scratch CLI session */
+    bcmcli_session_print(
+        bcmtr_cld_scratch_session,
+        "[-- CLD: %s %s: corr_tag=%u instance=%d msg_id=%d obj=%s(%d) group=%s(%d) subgrp=%s(%d)",
+        bcmtr_cld_event_name(ev),
+        hdr->dir == BCMOLT_MSG_DIR_RESPONSE ? "response" : "request",
+        hdr->corr_tag,
+        hdr->instance,
+        hdr->msg_id,
+        obj_name, obj,
+        apicli_mgt_group_to_str(group), group,
+        subgroup_name, subgroup);
+
+    if (hdr->more_fragments || (hdr->frag_number != 0))
+    {
+        bcmcli_session_print(
+            bcmtr_cld_scratch_session,
+            " more_fragments=%d fragment_number=%u",
+            hdr->more_fragments,
+            hdr->frag_number);
+    }
+
+    bcmcli_session_print(bcmtr_cld_scratch_session, " --]\n");
+
+    /* if configured for a full message dump, write the message data to the scratch session */
+    if ((val & BCMTR_CLD_DUMP) == BCMTR_CLD_DUMP)
+    {
+        if (msg != NULL)
+        {
+            bcmcli_session_print(bcmtr_cld_scratch_session, "[-- CLD Message Dump Start --]\n");
+            apicli_msg_dump(bcmtr_cld_scratch_session, msg);
+        }
+        else
+        {
+            bcmcli_session_print(bcmtr_cld_scratch_session, "[-- CLD Message Hex Dump Start --]\n");
+            bcmcli_session_hexdump(bcmtr_cld_scratch_session, packed, 0, packed_length, NULL);
+        }
+        bcmcli_session_print(bcmtr_cld_scratch_session, "[-- CLD Message Dump End --]\n");
+    }
+
+    /* Write the scratch session's buffer to the real CLI and reset it */
+    bcmcli_session_write(bcmtr_cld_session, bcmtr_cld_scratch_buf, bcmtr_cld_scratch_pos);
+    bcmtr_cld_scratch_pos = 0;
+}
+
+/* Notify capture, log, debug */
+void bcmtr_cld_notify(bcmolt_devid device, const bcmtr_hdr *hdr,
+    bcmtr_cld_event_type ev, uint32_t ts, const uint8_t *packed, uint32_t packed_length,
+    bcmolt_msg *msg)
+{
+    bcmtr_cld_type val = bcmtr_cld_active_level[device][hdr->msg_id];
+
+    if ((val & BCMTR_CLD_CAPTURE))
+        bcmtr_capture_notify(device, hdr, ev, ts, packed, packed_length, msg);
+    if ((val & BCMTR_CLD_LOG))
+        bcmtr_log_notify(device, hdr, ev, ts, packed, packed_length, msg);
+    if ((val & BCMTR_CLD_DUMP))
+        bcmtr_dump_notify(device, hdr, ev, ts, packed, packed_length, msg);
+}
+
+
+bcmos_errno bcmtr_cld_init(bcmcli_session *session)
+{
+    bcmos_errno err;
+    bcmcli_session_parm scratch_session_parm = { .write = bcmtr_log_cli_print };
+
+    err = bcmcli_session_open_user(&scratch_session_parm, &bcmtr_cld_scratch_session);
+    BCMOS_CHECK_RETURN_ERROR(err != BCM_ERR_OK, err);
+
+    bcmtr_cld_session = session;
+
+#ifdef ENABLE_LOG
+    bcmtr_cld_log_id = bcm_dev_log_id_register("cld", DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
+#endif
+
+#ifdef ENABLE_CLI
+    err = bcmtr_cld_cli_init();
+    BCMOS_CHECK_RETURN_ERROR(err != BCM_ERR_OK, err);
+#endif
+
+    return BCM_ERR_OK;
+}
+
+/** Clean up transport capture, log, debug service
+ */
+void bcmtr_cld_exit(void)
+{
+#ifdef ENABLE_CLI
+    bcmtr_cld_cli_exit();
+#endif
+    if (bcmtr_cld_scratch_session)
+    {
+        bcmcli_session_close(bcmtr_cld_scratch_session);
+        bcmtr_cld_scratch_session = NULL;
+    }
+}
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_debug.h b/bcm68620_release/release/host_driver/transport/bcmtr_debug.h
new file mode 100644
index 0000000..4690748
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_debug.h
@@ -0,0 +1,257 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#ifndef BCMTR_DEBUG_H_
+#define BCMTR_DEBUG_H_
+
+#include <bcm_config.h>
+#include <bcmolt_msg_pack.h>
+#include <bcmtr_header.h>
+#include <bcmolt_msg.h>
+#include <bcmcli.h>
+
+/* CLD stands for capture,log,debug */
+
+/** Capture, log, debug type */
+typedef enum
+{
+    BCMTR_CLD_NONE      = 0,            /**< Off */
+    BCMTR_CLD_CAPTURE   = 0x01,         /**< Message capture */
+    BCMTR_CLD_LOG       = 0x02,         /**< Message logging */
+    BCMTR_CLD_DUMP_HDR  = 0x04,         /**< Message header dump */
+    BCMTR_CLD_DUMP      = 0x0C,         /**< Message header and body dump */
+} bcmtr_cld_type;
+
+/** Event type */
+typedef enum
+{
+    BCMTR_CLD_EV_SEND,                  /**< Send message */
+    BCMTR_CLD_EV_RESEND,                /**< Retransmit message */
+    BCMTR_CLD_EV_RECV,                  /**< Receive message */
+    BCMTR_CLD_EV_RECV_DISCARD,          /**< Receive message discarded */
+    BCMTR_CLD_EV_TIMEOUT,               /**< Request timed out waiting for response */
+} bcmtr_cld_event_type;
+
+/** Capture, log, debug filter */
+typedef struct
+{
+    bcmolt_mgt_group group;     /**< Message group. Can be BCMOLT_MGT_GROUP_ANY */
+    bcmolt_obj_id object;       /**< Object. Can be BCMOLT_OBJECT_ANY */
+    uint16_t subgroup;          /**< Message subgroup. Can be BCMOLT_SUBGROUP_ANY */
+} bcmtr_cld_filter;
+
+/** Initialize transport capture,log, debug service
+ * \param[in]   parent  Parent CLI directory. Can be NULL
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_cld_init(bcmcli_session *session);
+
+/** Clean up transport capture, log, debug service
+ */
+void bcmtr_cld_exit(void);
+
+/** Set capture, log, debug for selected messages
+ * \param[in]   device          Device id
+ * \param[in]   filter          Message filter
+ * \param[in]   cld_level       Capture, log, debug level. Can be a combination of BCMTR_CLD_.. constants
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_cld_level_set(bcmolt_devid device, const bcmtr_cld_filter *filter, bcmtr_cld_type cld_level);
+
+/** Get capture, log, debug for selected message
+ * \param[in]   device          Device id
+ * \param[in]   filter          Message filter. Wildcards are not allowed.
+ * \param[out]  cld_level       Capture, log, debug level
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_cld_level_get(bcmolt_devid device, const bcmtr_cld_filter *filter, bcmtr_cld_type *cld_level);
+
+/*
+ * Message capture functions
+ */
+
+/**
+ * Message capture facility allows capturing messages between the host
+ * and OLT at real time. The recorded trace can be dumped in
+ * human readable format or "played back" in order to reproduce
+ * the configuration under test.
+ * @{
+ */
+
+/** Minimal trace buffer size (bytes) */
+#define BCMTR_CAPTURE_MIN_BUF_SIZE          (16*1024)
+
+/** Capture buffer configuration parameters */
+typedef struct bcmtr_capture_parm
+{
+    uint32_t size;              /**< Capture buffer size. Must be at least \ref BCMTR_CAPTURE_MIN_BUF_SIZE bytes */
+    void *ptr;                  /**< Optional capture buffer pointer. Allocated automatically if NULL */
+    bcmos_bool stop_on_full;    /**< TRUE-stop recording when buffer is full. FALSE=circular buffer */
+    bcmos_bool activate;        /**< Auto-activate capture immediately after init */
+} bcmtr_capture_parm;
+
+/** Capture info record */
+typedef struct bcmtr_capture_info
+{
+    uint32_t size;              /**< Capture buffer size */
+    uint32_t used;              /**< Used bytes */
+    uint32_t wa;                /**< Number of times capture buffer wrapped around */
+    uint32_t msgs;              /**< Number of complete messages stored in the capture buffer */
+    uint32_t lost;              /**< Number of messages lost due to buffer overflow */
+} bcmtr_capture_info;
+
+/* Capture entry header
+ *
+ * Followed by msg_size bytes of message data
+ */
+typedef struct
+{
+    uint32_t msg_size;          /**< Message size (bytes); MUST be the first member */
+    uint32_t event;             /**< bcmtr_cld_event_type event */
+    uint32_t timestamp;         /**< Message timestamp (us) */
+    /* Followed by message data padded to the nearest 4 bytes */
+} bcmtr_capture_entry;
+
+/** Initialize capture buffer
+ *
+ * This function must be called first.
+ * \param[in]   olt     Olt index
+ * \param[in]   cfg     Capture configuration parameters
+ * \return 0=success or error code
+ */
+bcmos_errno bcmtr_capture_init(bcmolt_devid olt, const bcmtr_capture_parm *parm);
+
+/** Destroy capture buffer
+ *
+ * Destroy buffer initialized by bcmtr_capture_Init().
+ * \param[in]   olt     Olt index
+ * Following this call all recording is lost.
+ */
+void bcmtr_capture_destroy(bcmolt_devid olt);
+
+/** Get capture recording info
+ *
+ * \param[in]   olt     Olt index
+ * \param[out]  info    Capture information
+ * \return 0=success or error code
+ */
+bcmos_errno bcmtr_capture_info_get(bcmolt_devid olt, bcmtr_capture_info *info);
+
+/** Get the number of readable bytes in the capture buffer
+ *
+ * \param[in]   olt             OLT index
+ * \param[out]  size            Number of readable bytes
+ */
+bcmos_errno bcmtr_capture_size_get(bcmolt_devid olt, uint32_t *size);
+
+/** Read portion of capture
+ *
+ * This function reads recorded data into a user buffer starting at the specified offset from the beginning of the
+ * capture up to the specified length. Applications can use this function in order to save capture into file or dump it.
+ *
+ * \param[in]   olt             OLT index
+ * \param[out]  buf             User buffer
+ * \param[in]   offset          Buffer offset to start read (bytes)
+ * \param[in/out]  length       in: Maximum number of bytes to read into the user buffer
+ *                              out: Remaining unused bytes in user buffer
+ * \return 0=success or error code
+ */
+bcmos_errno bcmtr_capture_read(bcmolt_devid olt, uint8_t *buf, uint32_t offset, uint32_t *length);
+
+/** Advance buffer to next capture entry
+ *
+ * Helper function used to scan buffer read by bcmtr_capture_read()
+ *
+ * \param[in/out]   buf         Capture buffer
+ * \param[out]      hdr         Capture entry header
+ * \param[out]      msg         Pointer to packed message
+ */
+bcmos_bool bcmtr_capture_entry_get_next(bcmolt_buf *buf, bcmtr_capture_entry *hdr, uint8_t **msg);
+
+/** Decode and dump capture recording
+ *
+ * This function interprets and dumps capture recording.
+ *
+ * \param[in]   session CLI session
+ * \param[in]   olt     OLT index
+ * \param[out]  nmsgs   Number of messages dumped
+ * \return 0=success or error code
+ */
+bcmos_errno bcmtr_capture_dump(bcmcli_session *session, bcmolt_devid olt, uint32_t *nmsgs);
+
+/** (Re)start / Suspend capture recording
+ *
+ * \param[in]   olt     OLT index
+ * \param[in]   start   TRUE=start, FALSE=suspend
+ * \return 0=success or error code
+ */
+bcmos_errno bcmtr_capture_start_stop(bcmolt_devid olt, bcmos_bool start);
+
+/** Are we actively capturing?
+ *
+ * \param[in]   olt     OLT index
+ * \return TRUE=actively capturing, FALSE=NOT capturing
+ */
+bcmos_bool bcmtr_capture_is_active(bcmolt_devid olt);
+
+/** Convert entry to platform specific format
+ *
+ * \param[in/out]   buf     Capture buffer
+ * \param[out]      entry   Capture entry header
+ *
+ * \return TRUE if success, FALSE otherwise
+ */
+bcmos_bool bcmtr_capture_entry_unpack(bcmolt_buf *buf, bcmtr_capture_entry *entry);
+
+/*
+ * Internal functions - called from transport level
+ */
+
+/* Per-OLT, per-command combined trace,log,debug level */
+extern bcmtr_cld_type bcmtr_cld_active_level[BCMTR_MAX_OLTS][BCMOLT_GROUP_ID__NUM_OF];
+
+/* Notify message.
+ * Called by transport layer
+ */
+void bcmtr_cld_notify(bcmolt_devid device, const bcmtr_hdr *hdr,
+    bcmtr_cld_event_type ev, uint32_t ts, const uint8_t *packed, uint32_t packed_length,
+    bcmolt_msg *msg);
+
+/* Check if CLD is enabled for the message and record if yes
+ * Called by transport layer
+ */
+#define BCMTR_CLD_CHECK_NOTIFY(_device, _hdr, _ev, _ts, _packed, _packed_length, _msg) \
+    do {\
+        if (bcmtr_cld_active_level[_device][(_hdr)->msg_id]) \
+        { \
+            bcmtr_cld_notify(_device, _hdr, _ev, _ts, _packed, _packed_length, _msg); \
+        } \
+    } while (0)
+
+#endif /* BCMTR_DEBUG_H_ */
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_debug_cli.c b/bcm68620_release/release/host_driver/transport/bcmtr_debug_cli.c
new file mode 100644
index 0000000..6db7d82
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_debug_cli.c
@@ -0,0 +1,440 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#include <bcmos_system.h>
+#include <bcmtr_debug.h>
+#include <bcmtr_debug_cli.h>
+#include <bcm_api_cli_helpers.h>
+#ifdef BCM_SUBSYSTEM_HOST
+#include <bcmolt_dev_selector.h>
+#endif
+
+#define BCMTR_CLD_CAST_DISCARD_CONST(p, type)       (type)((long)(p))
+
+static bcmcli_entry *cld_cli_dir;
+
+static bcmos_errno _bcmtr_cld_cli_create(void);
+static void _bcmtr_cld_cli_destroy(void);
+
+/*
+ * CLI handlers
+ */
+
+static bcmos_errno _bcmtr_cld_cli_setget_level(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    bcmcli_number obj = bcmcli_parm_get(session, "object")->value.number;
+    bcmcli_cmd_parm *grp_parm = bcmcli_parm_get(session, "group");
+    bcmcli_number group = grp_parm ? grp_parm->value.number : -1;
+    bcmcli_cmd_parm *subgrp_parm = bcmcli_parm_get(session, "subgroup");
+    bcmcli_number subgroup = subgrp_parm ? subgrp_parm->value.number : -1;
+    bcmtr_cld_type level = bcmcli_parm_get(session, "level")->value.number;
+    bcmtr_cld_filter filter = {};
+    bcmos_errno rc;
+
+    if (obj == -1 || !bcmcli_parm_is_set(session, bcmcli_parm_get(session, "object")))
+    {
+        filter.object = BCMOLT_OBJECT_ANY;
+    }
+    else
+    {
+        filter.object = obj;
+    }
+
+    if (group == -1 || !grp_parm || !bcmcli_parm_is_set(session, grp_parm))
+    {
+        filter.group = BCMOLT_MGT_GROUP_ANY;
+    }
+    else
+    {
+        filter.group = group;
+    }
+
+    if (subgroup == -1 || !subgrp_parm || !bcmcli_parm_is_set(session, subgrp_parm))
+    {
+        filter.subgroup = BCMOLT_SUBGROUP_ANY;
+    }
+    else
+    {
+        filter.subgroup = subgroup;
+    }
+
+    /* Get or set level ? */
+    if (bcmcli_parm_is_set(session, bcmcli_parm_get(session, "level")))
+    {
+        rc = bcmtr_cld_level_set(current_device, &filter, level);
+    }
+    else
+    {
+        rc = bcmtr_cld_level_get(current_device, &filter, &level);
+        if (rc == BCM_ERR_OK)
+        {
+            bcmcli_session_print(session, "capture:%s log:%s dump:%s\n",
+                (level & BCMTR_CLD_CAPTURE) ? "on" : "off",
+                (level & BCMTR_CLD_LOG) ? "on" : "off",
+                ((level & BCMTR_CLD_DUMP) == BCMTR_CLD_DUMP) ? "all" :
+                    (level & BCMTR_CLD_DUMP_HDR) ? "headers" : "off");
+        }
+    }
+
+    return rc;
+}
+
+static bcmos_errno _bcmtr_cld_cli_capture_init(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    bcmcli_number size = bcmcli_parm_get(session, "size")->value.number;
+    bcmos_bool stop_on_full = bcmcli_parm_get(session, "stop_on_full")->value.number;
+    bcmos_bool autostart = bcmcli_parm_get(session, "autostart")->value.number;
+    bcmtr_capture_parm p = {};
+
+    p.size = size;
+    p.stop_on_full = stop_on_full;
+    p.activate = autostart;
+
+    return bcmtr_capture_init(current_device, &p);
+}
+
+static bcmos_errno _bcmtr_cld_cli_capture_delete(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    bcmtr_capture_destroy(current_device);
+    return BCM_ERR_OK;
+}
+
+static bcmos_errno _bcmtr_cld_cli_capture_start(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    return bcmtr_capture_start_stop(current_device, BCMOS_TRUE);
+}
+
+static bcmos_errno _bcmtr_cld_cli_capture_stop(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    return bcmtr_capture_start_stop(current_device, BCMOS_FALSE);
+}
+
+static bcmos_errno _bcmtr_cld_cli_capture_dump(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    uint32_t nmsgs;
+    bcmos_errno rc;
+
+    rc = bcmtr_capture_dump(session, current_device, &nmsgs);
+    if (!rc)
+    {
+        bcmcli_session_print(session, "Dumped %u messages\n", nmsgs);
+    }
+
+    return BCM_ERR_OK;
+}
+
+static bcmos_errno _bcmtr_cld_cli_capture_info(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    bcmtr_capture_info info;
+    bcmos_errno rc;
+
+    rc = bcmtr_capture_info_get(current_device, &info);
+    if (!rc)
+    {
+        bcmcli_session_print(session, "Buffer: size=%u used=%u. Events: recorded=%u lost=%d. Wraps around:%u\n",
+            info.size, info.used, info.msgs, info.lost, info.wa);
+    }
+
+    return BCM_ERR_OK;
+}
+
+
+#ifdef BCM_SUBSYSTEM_HOST
+/* Current device change indication */
+static void _bcmtr_cld_device_change_ind(bcmcli_session *session, bcmolt_devid dev)
+{
+    bcmcli_entry *cur_dir = bcmcli_dir_get(session);
+    bcmos_bool is_cld_cur_dir = (cur_dir && cur_dir == cld_cli_dir);
+    bcmos_errno rc;
+
+    /* Destroy and re-create CLD CLI directory */
+    _bcmtr_cld_cli_destroy();
+    rc = _bcmtr_cld_cli_create();
+
+    /* Restore current CLI directory to CLD */
+    if (!rc && is_cld_cur_dir)
+        bcmcli_dir_set(session, cld_cli_dir);
+}
+#endif
+
+/* allocate memory for name and description and copy to to parm */
+static bcmos_errno _bcmtr_cld_copy_parm_name(bcmcli_cmd_parm *parm, const char *name, const char *descr)
+{
+    parm->name = bcmos_alloc(strlen(name) + 1);
+    parm->description = bcmos_alloc(strlen(descr) + 1);
+    if ((parm->name == NULL) || (parm->description == NULL))
+    {
+        /* Successful allocation if any will be released by common cleanup
+         * along with the rest of dynamic parameter fields */
+        return BCM_ERR_NOMEM;
+    }
+    strcpy(BCMTR_CLD_CAST_DISCARD_CONST(parm->name, void *), name);
+    strcpy(BCMTR_CLD_CAST_DISCARD_CONST(parm->description, void *), descr);
+    return BCM_ERR_OK;
+}
+
+/* Free "level" command parameters. both name and description are allocated dynamically */
+static void _bcmtr_cld_free_level_parms(bcmcli_cmd_parm *parms)
+{
+    bcmcli_cmd_parm *p = parms;
+
+    while (p->name)
+    {
+        if (p->type == BCMCLI_PARM_ENUM && p->enum_table)
+        {
+            if ((p->flags & BCMCLI_PARM_FLAG_SELECTOR))
+            {
+                bcmcli_enum_val *e = p->enum_table;
+                while(e->name)
+                {
+                    if (e->parms)
+                    {
+                        _bcmtr_cld_free_level_parms(e->parms);
+                    }
+                    ++e;
+                }
+            }
+            bcmos_free(p->enum_table);
+        }
+        if (p->description)
+            bcmos_free(BCMTR_CLD_CAST_DISCARD_CONST(p->description, void *));
+        if (p->name)
+            bcmos_free(BCMTR_CLD_CAST_DISCARD_CONST(p->name, void *));
+
+        ++p;
+    }
+    bcmos_free(parms);
+}
+
+/* Add "level" command */
+static bcmos_errno _bcmtr_cld_add_level_cmd(bcmcli_entry *dir)
+{
+    /* object:            *all, object list
+     * group:   selector: *all, group list
+     * subgroup:          *all, subgroup list
+     * level:   bitmask
+     */
+    bcmcli_cmd_extra_parm cmd_extras = { .free_parms = _bcmtr_cld_free_level_parms };
+    bcmcli_enum_val *obj_selector;
+    bcmcli_cmd_parm *cmd_parms;
+    static const char *all_name = "*all";
+    bcmolt_system_mode current_system_mode;
+    int n_obj = 0;
+    bcmolt_obj_id o;
+    bcmos_errno rc = BCM_ERR_NOMEM;
+
+    /* Allocate top level parameters: object selector, level, terminator */
+    cmd_parms = bcmos_calloc(sizeof(bcmcli_cmd_parm) * 3);
+    if (!cmd_parms)
+    {
+        return BCM_ERR_NOMEM;
+    }
+
+    /* Allocate object enum table. 2 extra entries for *all and terminator */
+    obj_selector = bcmos_calloc(sizeof(bcmcli_enum_val) * (BCMOLT_OBJ_ID__NUM_OF + 2));
+    if (!obj_selector)
+    {
+        goto cleanup;
+    }
+
+    /* Fill up parameters */
+    rc = _bcmtr_cld_copy_parm_name(&cmd_parms[0], "object", "Object Name");
+    cmd_parms[0].type = BCMCLI_PARM_ENUM;
+    cmd_parms[0].flags = BCMCLI_PARM_FLAG_SELECTOR;
+    cmd_parms[0].enum_table = obj_selector;
+
+    rc = rc ? rc : _bcmtr_cld_copy_parm_name(&cmd_parms[1], "level", "Level bitmask: 1=capture,2=log,4=print hdr,8=print body");
+    cmd_parms[1].type = BCMCLI_PARM_DECIMAL;
+    cmd_parms[1].flags = BCMCLI_PARM_FLAG_OPTIONAL;
+
+    /* obj_selector[0] is reserved for *all */
+    obj_selector[0].name = all_name;
+    obj_selector[0].val = -1;
+
+    /* Go over objects */
+    bcmolt_system_mode_get(current_device, &current_system_mode);
+    for (o = 0; o < BCMOLT_OBJ_ID__NUM_OF; o++)
+    {
+        bcmcli_enum_val *grp_selector;
+        bcmcli_cmd_parm *grp_parm;
+        bcmolt_mgt_group g;
+        int n_grp = 0;
+
+        if (!bcmolt_object_is_supported(current_system_mode, o))
+            continue;
+
+        /* Allocate group parameter and selector */
+        ++n_obj;
+        obj_selector[n_obj].val = o;
+        rc = api_cli_object_name(o, &obj_selector[n_obj].name, NULL);
+        if (rc != BCM_ERR_OK)
+            goto cleanup;
+
+        obj_selector[n_obj].parms = grp_parm = bcmos_calloc(sizeof(bcmcli_cmd_parm)*2);
+        if (!grp_parm)
+            goto cleanup;
+        rc = _bcmtr_cld_copy_parm_name(grp_parm, "group", "Message group");
+        grp_parm->type = BCMCLI_PARM_ENUM;
+        grp_parm->flags = BCMCLI_PARM_FLAG_SELECTOR;
+        grp_parm->enum_table = grp_selector = bcmos_calloc(sizeof(bcmcli_enum_val) * (BCMOLT_MGT_GROUP__NUM_OF + 1));
+        if (!grp_selector)
+            goto cleanup;
+
+        /* Selector 0 is reserved for *all */
+        grp_selector[0].name = all_name;
+        grp_selector[0].val = -1;
+
+        /* Go over groups */
+        for (g = 1; g < BCMOLT_MGT_GROUP__NUM_OF; g++)
+        {
+            uint16_t subgroup_count = api_cli_get_subgroup_count(o, g);
+            bcmcli_enum_val *subgrp_table;
+            bcmcli_cmd_parm *subgrp_parm;
+            uint16_t s;
+            int sg_table_idx;
+
+            if (subgroup_count == 0)
+                continue;
+
+            ++n_grp;
+            grp_selector[n_grp].name = apicli_mgt_group_to_str(g);
+            grp_selector[n_grp].val = g;
+
+            /* Skip subgroup selector for groups that don't support it */
+            if (g != BCMOLT_MGT_GROUP_AUTO      &&
+                g != BCMOLT_MGT_GROUP_OPER      &&
+                g != BCMOLT_MGT_GROUP_PROXY     &&
+                g != BCMOLT_MGT_GROUP_PROXY_RX)
+            {
+                continue;
+            }
+
+            grp_selector[n_grp].parms = subgrp_parm = bcmos_calloc(sizeof(bcmcli_cmd_parm)*2);
+            if (!subgrp_parm)
+                goto cleanup;
+            rc = rc ? rc : _bcmtr_cld_copy_parm_name(subgrp_parm, "subgroup", "Message sub-group");
+            subgrp_parm->type = BCMCLI_PARM_ENUM;
+            subgrp_parm->enum_table = subgrp_table = bcmos_calloc(sizeof(bcmcli_enum_val) * (subgroup_count + 2));
+            if (!subgrp_table)
+                goto cleanup;
+            subgrp_table[0].name = all_name;
+            subgrp_table[0].val = -1;
+
+            sg_table_idx = 1;
+            for (s = 0; s < subgroup_count; s++)
+            {
+                if (api_cli_object_subgroup_name(o, g, s, &subgrp_table[sg_table_idx].name, NULL) == BCM_ERR_OK)
+                {
+                    subgrp_table[sg_table_idx].val = s;
+                    ++sg_table_idx;
+                }
+            }
+        }
+    }
+
+    /* Finally add command */
+    rc = bcmcli_cmd_add(dir, "level", _bcmtr_cld_cli_setget_level, "Set/get cld level", BCMCLI_ACCESS_ADMIN,
+        &cmd_extras, cmd_parms);
+    if (rc)
+        goto cleanup;
+    return BCM_ERR_OK;
+
+cleanup:
+    _bcmtr_cld_free_level_parms(cmd_parms);
+    return rc;
+}
+
+/* Create CLI commands */
+static bcmos_errno _bcmtr_cld_cli_create(void)
+{
+    bcmcli_entry *dir;
+    bcmos_errno err;
+
+    dir = bcmcli_dir_add(NULL, "cld", "Transport Capture, Log, Debug", BCMCLI_ACCESS_ADMIN, NULL);
+    BCMOS_CHECK_RETURN_ERROR(dir == NULL, BCM_ERR_NOMEM);
+
+    err = _bcmtr_cld_add_level_cmd(dir);
+    if (err)
+        return err;
+
+    BCMCLI_MAKE_CMD(dir, "init", "Initialize capture buffer", _bcmtr_cld_cli_capture_init,
+        BCMCLI_MAKE_PARM("size", "Buffer size (bytes)", BCMCLI_PARM_UDECIMAL, 0),
+        BCMCLI_MAKE_PARM_ENUM("stop_on_full", "Stop capture when buffer is full (yes) or wrap-around (no)",
+            bcmcli_enum_bool_table, 0),
+        BCMCLI_MAKE_PARM_ENUM("autostart", "Autostart", bcmcli_enum_bool_table, 0) );
+
+    BCMCLI_MAKE_CMD_NOPARM(dir, "delete", "Destroy buffer", _bcmtr_cld_cli_capture_delete);
+
+    BCMCLI_MAKE_CMD_NOPARM(dir, "start", "Start capture", _bcmtr_cld_cli_capture_start);
+
+    BCMCLI_MAKE_CMD_NOPARM(dir, "stop", "Stop capture", _bcmtr_cld_cli_capture_stop);
+
+    BCMCLI_MAKE_CMD_NOPARM(dir, "dump", "Dump capture buffer", _bcmtr_cld_cli_capture_dump);
+
+    BCMCLI_MAKE_CMD_NOPARM(dir, "info", "Capture info", _bcmtr_cld_cli_capture_info);
+
+    cld_cli_dir = dir;
+
+    return BCM_ERR_OK;
+
+}
+
+/* Destroy CLI commands */
+static void _bcmtr_cld_cli_destroy(void)
+{
+    if (cld_cli_dir)
+    {
+        bcmcli_token_destroy(cld_cli_dir);
+        cld_cli_dir = NULL;
+    }
+}
+
+
+/** Initialize CLD CLI commands
+ * \returns BCM_ERR_OK (0) if successful
+ */
+bcmos_errno bcmtr_cld_cli_init(void)
+{
+    bcmos_errno err = BCM_ERR_OK;
+
+#ifdef BCM_SUBSYSTEM_HOST
+    /* Subscribe for device change indication */
+    err = bcmolt_dev_sel_ind_register(_bcmtr_cld_device_change_ind);
+#endif
+
+    err = err ? err : _bcmtr_cld_cli_create();
+
+    return err;
+}
+
+/** Clean-up CLD CLI commands */
+void bcmtr_cld_cli_exit(void)
+{
+    _bcmtr_cld_cli_destroy();
+}
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_debug_cli.h b/bcm68620_release/release/host_driver/transport/bcmtr_debug_cli.h
new file mode 100644
index 0000000..46b7e20
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_debug_cli.h
@@ -0,0 +1,41 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#ifndef BCMTR_DEBUG_CLI_H_
+#define BCMTR_DEBUG_CLI_H_
+
+/** Initialize CLD CLI commands
+ * \returns BCM_ERR_OK (0) if successful
+ */
+bcmos_errno bcmtr_cld_cli_init(void);
+
+/** Clean-up CLD CLI commands */
+void bcmtr_cld_cli_exit(void);
+
+#endif /* BCMTR_DEBUG_CLI_H_ */
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_header.h b/bcm68620_release/release/host_driver/transport/bcmtr_header.h
new file mode 100644
index 0000000..a662535
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_header.h
@@ -0,0 +1,142 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#ifndef BCMTR_HEADER_H_
+#define BCMTR_HEADER_H_
+
+#include <bcmolt_buf.h>
+#include <bcmolt_msg_pack.h>
+
+/** Endianess of numbers written to the transport buffer.
+ * Currently bcmolt_buf service doesn't support setting
+ * endianness dynamically. Transport buffer direction must be the same
+ * as selected at compile time in bcmolt_buf.h
+ */
+#define BCMTR_BUF_ENDIAN        BCMOLT_BUF_ENDIAN_FIXED
+
+#define BCMTR_ENDIAN_CPU_TO_BUF_U32(n) BCMOLT_BUF_ENDIAN_CPU_TO_BUF(U32, n)
+#define BCMTR_ENDIAN_BUF_TO_CPU_U32(n) BCMOLT_BUF_ENDIAN_BUF_TO_CPU(U32, n)
+
+/** Transport header.
+ * Inserted in each packet transmitted on the line
+ */
+typedef struct bcmtr_hdr
+{
+    bcmolt_msg_dir dir;         /**< Message direction: request/response */
+    bcmos_bool more_fragments;  /**< TRUE=more fragments to follow */
+    bcmos_bool auto_proxy_reg;  /**< TRUE=message is auto / proxy registration */
+    bcmos_bool auto_proxy_unreg;/**< TRUE=message is auto / proxy un-registration */
+    uint8_t instance;           /**< Message instance */
+    bcmolt_group_id msg_id;     /**< Message id: object+group+subgroup */
+    uint16_t corr_tag;          /**< correlation tag */
+    uint16_t frag_number;       /**< fragment number */
+} bcmtr_hdr;
+
+#define BCMTR_HDR_SIZE  8
+
+/* Shifts and widths of transport header fields */
+#define BCMTR_HDR_DIR_S         31
+#define BCMTR_HDR_DIR_W         1
+#define BCMTR_HDR_REG_S         26
+#define BCMTR_HDR_REG_W         1
+#define BCMTR_HDR_UNREG_S       25
+#define BCMTR_HDR_UNREG_W       1
+#define BCMTR_HDR_MORE_FRAGS_S  24
+#define BCMTR_HDR_MORE_FRAGS_W  1
+#define BCMTR_HDR_INSTANCE_S    16
+#define BCMTR_HDR_INSTANCE_W    8
+#define BCMTR_HDR_MSG_ID_S      0
+#define BCMTR_HDR_MSG_ID_W      16
+#define BCMTR_HDR_CORR_TAG_S    16
+#define BCMTR_HDR_CORR_TAG_W    16
+#define BCMTR_HDR_FRAG_S        0
+#define BCMTR_HDR_FRAG_W        16
+
+/* Pack transport header
+ *
+ * \param[in]   hdr             Unpacked transport header
+ * \param[out]  packed_hdr      Packed header
+ */
+static inline void bcmtr_header_pack(const bcmtr_hdr *hdr, uint8_t *packed_hdr)
+{
+    uint32_t w[2];
+
+    w[0] = (hdr->dir << BCMTR_HDR_DIR_S)                |
+        (hdr->more_fragments << BCMTR_HDR_MORE_FRAGS_S) |
+        (hdr->instance << BCMTR_HDR_INSTANCE_S)         |
+        (hdr->auto_proxy_reg << BCMTR_HDR_REG_S)        |
+        (hdr->auto_proxy_unreg << BCMTR_HDR_UNREG_S)    |
+        hdr->msg_id;
+    w[1] = (hdr->corr_tag << BCMTR_HDR_CORR_TAG_S) | hdr->frag_number;
+    w[0] = BCMTR_ENDIAN_CPU_TO_BUF_U32(w[0]);
+    w[1] = BCMTR_ENDIAN_CPU_TO_BUF_U32(w[1]);
+    memcpy(packed_hdr, w, BCMTR_HDR_SIZE);
+}
+
+/* Unpack transport header
+ *
+ * \param[in]   packed_hdr      Packed header
+ * \param[out]  hdr             Unpacked transport header
+ * Buffer current pointer is incremented by the header side.
+ */
+static inline void bcmtr_header_unpack(const uint8_t *packed_hdr, bcmtr_hdr *hdr)
+{
+    uint32_t w[2];
+    memcpy(w, packed_hdr, BCMTR_HDR_SIZE);
+    w[0] = BCMTR_ENDIAN_BUF_TO_CPU_U32(w[0]);
+    w[1] = BCMTR_ENDIAN_BUF_TO_CPU_U32(w[1]);
+    hdr->dir = BCM_FIELD_GET(w[0], BCMTR_HDR_DIR);
+    hdr->more_fragments = BCM_FIELD_GET(w[0], BCMTR_HDR_MORE_FRAGS);
+    hdr->instance = BCM_FIELD_GET(w[0], BCMTR_HDR_INSTANCE);
+    hdr->msg_id = BCM_FIELD_GET(w[0], BCMTR_HDR_MSG_ID);
+    hdr->auto_proxy_reg = BCM_FIELD_GET(w[0], BCMTR_HDR_REG);
+    hdr->auto_proxy_unreg = BCM_FIELD_GET(w[0], BCMTR_HDR_UNREG);
+    hdr->corr_tag = BCM_FIELD_GET(w[1], BCMTR_HDR_CORR_TAG);
+    hdr->frag_number = BCM_FIELD_GET(w[1], BCMTR_HDR_FRAG);
+}
+
+/* Fill transport header
+ */
+static inline bcmos_errno bcmtr_header_fill(const bcmolt_msg *msg, bcmtr_hdr *hdr)
+{
+    bcmos_errno err;
+
+    hdr->dir = msg->dir;
+    hdr->more_fragments = BCMOS_FALSE;
+    err = bcmolt_group_id_combine(msg->obj_type, msg->group, msg->subgroup, &hdr->msg_id);
+    if (err)
+        return err;
+    hdr->instance = bcmolt_msg_instance(msg);
+    hdr->corr_tag = msg->corr_tag;
+    hdr->frag_number = 0;
+    return BCM_ERR_OK;
+}
+
+
+#endif /* BCMTR_HEADER_H_ */
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_interface.h b/bcm68620_release/release/host_driver/transport/bcmtr_interface.h
new file mode 100644
index 0000000..b66c8cb
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_interface.h
@@ -0,0 +1,241 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#ifndef BCMTR_INTERFACE_H_
+#define BCMTR_INTERFACE_H_
+
+#include <bcmos_system.h>
+#include <bcmolt_model_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Transport statistics */
+typedef struct bcmtr_stat
+{
+    uint32_t msg_sent;                  /**< Messages sent */
+    uint32_t msg_resp_received;         /**< Valid responses received */
+    uint32_t msg_req_auto_received;     /**< Request or Autonomous message received */
+    uint32_t msg_req_timeout;           /**< Number of requests that timed out */
+    uint32_t msg_reass_timeout;         /**< Number of messages discarded due to reassemble timeout */
+    uint32_t msg_no_req;                /**< Number of responses discarded because there was no matching request */
+    uint32_t msg_no_mem;                /**< Number of memory allocation failures */
+    uint32_t msg_comm_err;              /**< Messages discarded because of communication error */
+    uint32_t msg_ready_timeout;         /**< Responses that have been reported to application but not peaked up */
+    uint32_t msg_too_many_req;          /**< Number of requests discarded because there were too many outstanding requests */
+    uint32_t msg_too_many_auto;         /**< Number of autonomous messages discarded because there were too many being reassembled */
+    uint32_t not_connected;             /**< Number of TX messages discarded because connection was lost */
+    uint32_t frag_received;             /**< Valid fragments received */
+    uint32_t frag_invalid;              /**< Fragments discarded */
+    uint32_t pack_errors;               /**< Message pack errors */
+    uint32_t unpack_errors;             /**< Message unpack errors */
+    uint32_t no_rx_handler;             /**< Message discarded because there was no rx handler */
+} bcmtr_stat;
+
+/** Transport message control block */
+typedef struct bcmtr_msg bcmtr_msg;
+
+/** Channel id */
+typedef uint8_t bcmtr_channel_id;
+
+/** Send flags */
+typedef enum
+{
+    BCMTR_SEND_FLAGS_NONE               = 0,            /**< None */
+    BCMTR_SEND_FLAGS_CALL               = 0x0001,       /**< Request/Response sequence */
+
+    BCMTR_SEND_FLAGS_PRI_NORMAL         = 0,            /**< Normal priority */
+    BCMTR_SEND_FLAGS_PRI_HI             = 0x0010,       /**< High priority */
+
+    BCMTR_SEND_FLAGS_LONG_WAIT          = 0x0100,       /**< Enable long wait until there is room in tx queue */
+    BCMTR_SEND_FLAGS_SHORT_WAIT         = 0x0200,       /**< Enable short wait until there is room in tx queue */
+    BCMTR_SEND_FLAGS_DO_NOT_WAIT        = 0x0400,       /**< Drop packet if TX q is full */
+
+} bcmtr_send_flags;
+
+#define BCMTR_SEND_FLAGS_WAIT_MASK (BCMTR_SEND_FLAGS_LONG_WAIT | BCMTR_SEND_FLAGS_SHORT_WAIT | BCMTR_SEND_FLAGS_DO_NOT_WAIT)
+
+/** Transport handler registration parameters */
+typedef struct bcmtr_handler_parm
+{
+    uint8_t instance;           /**< Instance (i.e, link) */
+    bcmolt_mgt_group group;     /**< Message group */
+    bcmolt_obj_id object;       /**< Object. Can be BCMOLT_OBJECT_ANY */
+    uint16_t subgroup;          /**< Message subgroup. Can be BCMOLT_SUBGROUP_ANY */
+    f_bcmolt_msg_handler app_cb;/**< Message handler */
+    bcmolt_auto_flags flags;    /**< Flags. app_cb is called in context of transport task
+                                     or app module, depending on the flags */
+    bcmos_module_id module;     /**< Target module id. Relevant only if flags == BCMOLT_AUTO_FLAGS_DISPATCH.
+                                     BCMOS_MODULE_ID_NONE is replaced by the module calling registration function */
+} bcmtr_handler_parm;
+
+/** Initialize transport library.
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_init(void);
+
+/** Release resources used by transport library.
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_exit(void);
+
+/** Send message. Do not wait for response
+ *
+ * Set-up connection if necessary, pack message and send it to the remote side.
+ * This function is intended for proxy and autonomous messages.
+ *
+ * \param[in]   device  OLT device index
+ * \param[in]   msg     Application message to be sent
+ * \param[in]   flags   flags (request/auto or reply)
+ *
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_send(bcmolt_devid device, bcmolt_msg *msg, bcmtr_send_flags flags);
+
+/** Send response message
+ *
+ * Set-up connection if necessary, pack message and send it to the remote side.
+ * This function is intended for proxy and autonomous messages.
+ *
+ * \param[in]   device  OLT device index
+ * \param[in]   msg     Application message to be sent
+ * \param[in]   flags   flags (request/auto or reply)
+ *
+ * \returns BCM_ERR_OK or error code
+ */
+static inline bcmos_errno bcmtr_send_response(bcmolt_devid device, bcmolt_msg *msg)
+{
+    msg->dir = BCMOLT_MSG_DIR_RESPONSE;
+    return bcmtr_send(device, msg, BCMTR_SEND_FLAGS_CALL | BCMTR_SEND_FLAGS_SHORT_WAIT);
+}
+
+/** Send request and wait for reply
+ *
+ * Set-up connection if necessary, pack message and send it to the remote side.
+ * Wait for reply or timeout, unpack the reply and return.
+ *
+ * \param[in]        device  OLT device index
+ * \param[in, out]   msg     Application message
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_call(bcmolt_devid device, bcmolt_msg *msg);
+
+/** Register message handler
+ *
+ * \param[in]   device          OLT device index
+ * \param[in]   parm            Registration parameters
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_msg_handler_register(bcmolt_devid device, const bcmtr_handler_parm *parm);
+
+/** Unregister message handler
+ *
+ * \param[in]   device          OLT device index
+ * \param[in]   parm            Registration parameters
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_msg_handler_unregister(bcmolt_devid device, const bcmtr_handler_parm *parm);
+
+/** Get registration info
+ *
+ * \param[in]           device          OLT device index
+ * \param[in,out]       parm            Registration parameters.
+ *                                      instance, group, object, subgroup must be set
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_msg_handler_register_get(bcmolt_devid device, bcmtr_handler_parm *parm);
+
+/** Get transport statistics
+ *
+ * \param[in]   device          OLT device index
+ * \param[out]  stat            Statistics
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_stat_get(bcmolt_devid device, bcmtr_stat *stat);
+
+/* Query whether or not the device is currently connected */
+bcmos_errno bcmtr_is_connected(bcmolt_devid device, bcmos_bool *is_connected);
+
+/* Connect device */
+bcmos_errno bcmtr_connect(bcmolt_devid device);
+
+/* Disconnect device */
+bcmos_errno bcmtr_disconnect(bcmolt_devid device);
+
+/* Low-level disconnect that breaks "physical" connection, but doesn't destroy connection structure and registrations */
+bcmos_errno bcmtr_driver_disconnect(bcmolt_devid device);
+
+/* Repair/reconnect the driver-level connection for an already-connected device */
+bcmos_errno bcmtr_driver_reconnect(bcmolt_devid device);
+
+/* "dropped because of tx_queue overflow" indication */
+typedef void (*F_bcmtr_tx_overflow)(bcmolt_devid device, bcmtr_send_flags send_flags);
+
+/* Register for notification that transmit failed because
+ * tx_queue was full
+ * \param[in]   device          OLT device index
+ * \param[in]   cb              Callback to be called. NULL=unregister
+ * \returns 0=OK or error code < 0
+ */
+bcmos_errno bcmtr_tx_overflow_cb_register(bcmolt_devid device, F_bcmtr_tx_overflow cb);
+
+/*
+ * The following functions are useful for proxy daemon.
+ * They provide direct interface to transport plugin
+ */
+
+/* Connect device in raw mode
+ * Receive task is NOT created
+ * \param[in]   device          OLT device index
+ * \parm[out]   headroom        Headroom that should be reserved in buffer when transmitting
+ * \returns 0=OK or error code < 0
+ */
+bcmos_errno bcmtr_proxy_connect(bcmolt_devid device, uint32_t *headroom);
+
+/* Send data to device
+ * \param[in]   device          OLT device index
+ * \param[in]   txb             Transmit buffer
+ * \returns 0=OK or error code < 0
+ */
+bcmos_errno bcmtr_proxy_send(bcmolt_devid device, bcmolt_buf *tx_buf);
+
+/* Receive data from device
+ * \param[in]   device          OLT device index
+ * \param[in]   rxb             Receive buffer
+ * \returns 0=OK, BCM_ERR_TIMEOUT-timeout and no message, other errors <0
+ */
+bcmos_errno bcmtr_proxy_receive(bcmolt_devid device, bcmolt_buf *rx_buf);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BCMTR_INTERFACE_H_ */
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_internal.h b/bcm68620_release/release/host_driver/transport/bcmtr_internal.h
new file mode 100644
index 0000000..3b03e29
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_internal.h
@@ -0,0 +1,151 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#ifndef BCMTR_INTERNAL_H_
+#define BCMTR_INTERNAL_H_
+
+#include <bcmos_system.h>
+#include <bcmolt_buf.h>
+#include <bcmolt_msg_pack.h>
+#include <bcmtr_interface.h>
+#include <bcmtr_plugin.h>
+#include <bcm_config.h>
+
+#include "bcmtr_header.h"
+
+/** Transport configuration parameters */
+typedef struct
+{
+    bcmtr_plugin_cfg plugin_cfg;        /**< Transport plugin configuration parameters */
+
+    /** Limits and timeouts.
+     * If not set, the appropriate BCM_TR_default defined in bcmConfig.h is used
+     */
+    uint32_t max_retries;       /**< Max number of request retransmissions */
+    uint32_t msg_timeout;       /**< Max time to wait for response or next message part (ms) */
+    uint32_t max_requests;      /**< Max number of outstanding requests */
+    uint32_t max_autos;         /**< Max number of multi-part autonomous messages */
+    uint32_t max_fragments;     /**< Max number of multi-part message fragments */
+    uint32_t msg_wait_timeout;  /**< Message waiting timeout (ms) */
+    uint32_t msg_ready_timeout; /**< Time that transaction is kept after notifying application that it finished (ms) */
+    uint32_t max_mtu;           /**< Max MTU size (bytes) */
+
+    int rx_thread_priority;     /**< Receive thread priority. If set -1, Rx thread is NOT created */
+} bcmtr_cfg;
+
+/** Transport connection control block */
+typedef struct bcmtr_conn bcmtr_conn;
+
+/** Reassemble block */
+typedef struct bcmtr_reass
+{
+    bcmolt_buf *fragments;              /** Array of bcmtr_cfg.max_fragments */
+    uint32_t num_fragments;             /** Number of fragments filled in fragments array */
+    uint32_t max_fragment;              /** Max fragment number. Set when last fragment is received */
+    uint32_t total_size;                /** Total size of received fragments */
+} bcmtr_reass;
+
+/** Transport header list head */
+typedef TAILQ_HEAD(bcmtr_msg_list, bcmtr_msg) bcmtr_msg_list;
+
+/** Transport transaction control block */
+struct bcmtr_msg
+{
+    bcmolt_buf tx_buf;                  /**< Transmit buffer info (request only) */
+    bcmolt_buf rx_buf;                  /**< Receive buffer info (response or autonomous) */
+    uint32_t timestamp;                 /**< Message timestamp. Tx or last Rx */
+    uint16_t tx_count;                  /**< Number of times message was transmitted */
+    bcmtr_hdr hdr;                      /**< Transport header */
+    TAILQ_ENTRY(bcmtr_msg) l;           /**< Transport message list entry */
+    bcmtr_reass *segm;                  /**< Segmentation block */
+    bcmtr_reass *reass;                 /**< Reassemble block */
+    bcmolt_msg *msg;                    /**< Message reference */
+    bcmolt_subchannel subch;            /**< Sub-channel via which message was received */
+    bcmos_errno err;                    /**< Transport status */
+
+    /* Transport header is cleared up to this point when released.
+     * "err" field is the last in the section that gets cleared.
+     * Do not move fields below above "err"!
+     */
+#define BCMTR_HDR_CLEAR_SIZE   (offsetof(bcmtr_msg, err) + sizeof(bcmos_errno))
+    bcmtr_msg_list *free_list;          /**< Free list head reference */
+    bcmtr_conn *conn;                   /**< Connection back reference */
+    bcmos_sem sem;                      /**< "wait for response" semaphore */
+    bcmos_msg ipc_hdr;                  /**< IPC message header */
+};
+
+/** Message handler context */
+typedef struct bcmtr_handler
+{
+    f_bcmolt_msg_handler app_cb;        /**< Application callback function */
+    bcmolt_auto_flags flags;            /**< Registration flag: call in context of transport rx thread or app module */
+    bcmos_module_id module;             /**< Optional module to dispatch received message to */
+} bcmtr_handler;
+
+/** Transport connection control block */
+struct bcmtr_conn
+{
+    char name[16];                      /**< Transport name */
+    bcmolt_devid device;                /**< Device index */
+    bcmtr_channel_id channel;           /**< Channels served by this transport */
+    bcmtr_cfg cfg;                      /**< Transport configuration parameters */
+    bcmtr_driver driver;                /**< Transport driver */
+    bcmtr_plugin_channel drv_priv;      /**< Plugin driver private data */
+    bcmos_task rx_thread;               /**< RX thread handle */
+    bcmos_mutex mutex;                  /**< Mutex protecting the transport structure */
+    uint32_t last_timeout_check;        /**< Last timeout check timestamp */
+    uint32_t timeout_check_period;      /**< Time-out check period */
+    bcmtr_msg_list msg_list;            /**< Message list head */
+    bcmtr_msg_list free_req_list;       /**< Free request block list */
+    bcmtr_msg_list free_auto_list;      /**< Free autonomous block list */
+    bcmtr_msg *tmsg_array;              /**< Pre-allocated array of transport headers */
+    bcmtr_stat stat;                    /**< Statistics */
+    bcmos_bool connected;               /**< Transport state */
+    uint16_t num_requests;              /**< Number of outstanding requests */
+    uint16_t num_auto;                  /**< Number of autonomous messages being reassembled */
+    uint16_t corr_tag;                  /**< Last used correlation tag */
+    bcmos_bool rx_thread_created;       /**< TRUE=RX thread was created */
+    int kill_request;                   /**< Transport thread is commanded to die */
+    int kill_done;                      /**< Transport thread is dead */
+};
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bcmos_errno bcmtr_cfg_get(bcmolt_devid device, bcmtr_cfg *cfg, bcmtr_driver *driver);
+
+bcmos_errno _bcmtr_conn_get(bcmolt_devid device, bcmtr_conn **conn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BCMTR_INTERNAL_H_ */
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_transport.c b/bcm68620_release/release/host_driver/transport/bcmtr_transport.c
new file mode 100644
index 0000000..029158e
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_transport.c
@@ -0,0 +1,1713 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#include <bcmos_system.h>
+#include <bcmtr_interface.h>
+#include <bcmtr_debug.h>
+
+#include "bcmtr_header.h"
+#include "bcmtr_internal.h"
+
+typedef struct
+{
+    bcmtr_conn *conn; /**< Connection dynamic info (allocated on connect) */
+    bcmtr_handler msg_handler[BCMOLT_GROUP_ID__NUM_OF][BCMTR_MAX_INSTANCES];
+    F_bcmtr_tx_overflow overflow_cb;    /**< Callback to be called in case of tx drop because of queue overflow */
+} bcmtr_conn_info;
+
+static bcmtr_conn_info conn_info[BCMTR_MAX_OLTS]; /* Store connection info separately per OLT */
+
+static bcmos_errno _bcmtr_connect(bcmolt_devid device, bcmtr_conn **conn, bcmos_bool raw_mode);
+
+static bcmos_mutex conn_lock;
+
+/* Get existing connection. If none - setup new.
+ * If raw_mode is TRUE, the connection is intended for use by raw interface
+ * bcmtr_proxy_xx(). In this case RX task is not created
+ */
+static bcmos_errno _bcmtr_conn_get_any(bcmolt_devid device, bcmtr_conn **conn, bcmos_bool is_raw)
+{
+    bcmos_errno err;
+    if (device >= BCMTR_MAX_OLTS)
+    {
+        return BCM_ERR_RANGE;
+    }
+    *conn = conn_info[device].conn;
+    if (*conn)
+    {
+        return BCM_ERR_OK;
+    }
+    err = _bcmtr_connect(device, &conn_info[device].conn, is_raw);
+    *conn = conn_info[device].conn;
+    return err;
+}
+
+/* Get existing connection. If none - setup new */
+bcmos_errno _bcmtr_conn_get(bcmolt_devid device, bcmtr_conn **conn)
+{
+    return _bcmtr_conn_get_any(device, conn, BCMOS_FALSE);
+}
+
+/* Free reassemble block */
+static void _bcmtr_reass_block_free(bcmtr_reass **prb)
+{
+    bcmtr_reass *reass = *prb;
+    int i;
+
+    for (i=0; i<reass->num_fragments; i++)
+    {
+        bcmolt_buf_free(&reass->fragments[i]);
+    }
+    bcmos_free(reass);
+    *prb = NULL;
+}
+
+/* Free transport header. Called under transport lock */
+static void _bcmtr_tmsg_free(bcmtr_msg *tmsg, bcmtr_msg_list *cur_list)
+{
+    /* Remove from the list it is in, if any */
+    if (cur_list)
+        TAILQ_REMOVE(cur_list, tmsg, l);
+
+    bcmolt_buf_free(&tmsg->tx_buf);
+    bcmolt_buf_free(&tmsg->rx_buf);
+    if (tmsg->reass)
+        _bcmtr_reass_block_free(&tmsg->reass);
+    memset(tmsg, 0, BCMTR_HDR_CLEAR_SIZE);
+
+    /* Request-response or autonomous ? */
+    TAILQ_INSERT_TAIL(tmsg->free_list, tmsg, l);
+}
+
+/* Unpack message. *unpacked is set=NULL in case of error */
+static inline bcmos_errno _bcmtr_msg_unpack(bcmtr_conn *conn, bcmolt_buf *buf, bcmtr_hdr *hdr, uint32_t ts, bcmolt_msg **msg)
+{
+    int16_t err;
+    uint8_t *packed = buf->curr;
+    uint32_t packed_length = bcmolt_buf_get_remaining_size(buf);
+
+    /* Unpack */
+    BUG_ON(!buf->start);
+    err = bcmolt_msg_unpack(buf, msg);
+    if (err < 0)
+    {
+        BCMTR_CLD_CHECK_NOTIFY(conn->device, hdr, BCMTR_CLD_EV_RECV_DISCARD, ts, packed, packed_length, NULL);
+        ++conn->stat.unpack_errors;
+        return err;
+    }
+
+    BCMTR_CLD_CHECK_NOTIFY(conn->device, hdr, BCMTR_CLD_EV_RECV, ts, packed, packed_length, *msg);
+
+    return BCM_ERR_OK;
+}
+
+/* Transport IPC release callback */
+static void _bcmtr_ipc_msg_release(bcmos_msg *m)
+{
+    BCMOS_TRACE_ERR("We shouldn't be here!\n");
+}
+
+/* Transport IPC message handler.
+ * Called in context of the target module as part
+ * of dispatching message to the user task.
+ * It unpacks message, releases transport header and calls user callback
+ */
+static void _bcmtr_ipc_msg_handler(bcmos_module_id module_id, bcmos_msg *m)
+{
+    bcmos_errno err;
+
+    bcmtr_msg *tmsg = m->data;
+    bcmtr_conn *conn = tmsg->conn;
+    bcmolt_msg *msg = NULL;
+
+    /* Unpack */
+    err = _bcmtr_msg_unpack(conn, &tmsg->rx_buf, &tmsg->hdr, tmsg->timestamp, &msg);
+    if (err != BCM_ERR_OK)
+    {
+        BCMOS_TRACE_ERR(
+            "Unpack error for module %d. Error %s (%d)\n",
+            module_id,
+            bcmos_strerror(err),
+            err);
+        msg = NULL;
+    }
+
+    if (msg != NULL)
+    {
+        bcmtr_handler *h = &conn_info[conn->device].msg_handler[tmsg->hdr.msg_id][tmsg->hdr.instance];
+        msg->subch = tmsg->subch;
+        if (h->app_cb)
+        {
+            msg->corr_tag = tmsg->hdr.corr_tag;
+            h->app_cb(conn->device, msg);
+        }
+        else
+        {
+            bcmolt_msg_free(msg);
+            ++conn->stat.no_rx_handler;
+        }
+    }
+
+    /* Release transport header under conn lock */
+    bcmos_mutex_lock(&conn->mutex);
+    _bcmtr_tmsg_free(tmsg, NULL);
+    bcmos_mutex_unlock(&conn->mutex);
+}
+
+/* Init IPC header in transport message */
+static void _bcmtr_tmsg_ipc_init(bcmtr_msg *tmsg)
+{
+    tmsg->ipc_hdr.start = tmsg->ipc_hdr.data = tmsg;
+    tmsg->ipc_hdr.type = BCMOS_MSG_ID_INTERNAL_IPC;
+    tmsg->ipc_hdr.release = _bcmtr_ipc_msg_release;
+    tmsg->ipc_hdr.handler = _bcmtr_ipc_msg_handler;
+}
+
+/* Pre-allocate transport header array, put all blocks on free lists */
+static int _bcmtr_tmsg_list_alloc(bcmtr_conn *conn)
+{
+    int n_hdr, i;
+    bcmtr_msg *tmsg;
+
+    n_hdr = conn->cfg.max_requests + conn->cfg.max_autos;
+    conn->tmsg_array = bcmos_calloc(sizeof(bcmtr_msg) * n_hdr);
+    if (!conn->tmsg_array)
+        return BCM_ERR_NOMEM;
+
+    tmsg = conn->tmsg_array;
+    for (i=0; i<conn->cfg.max_requests; i++, tmsg++)
+    {
+        bcmos_errno rc;
+
+        TAILQ_INSERT_TAIL(&conn->free_req_list, tmsg, l);
+        tmsg->free_list = &conn->free_req_list;
+        rc = bcmos_sem_create(&tmsg->sem, 0, 0, NULL);
+        if (rc != BCM_ERR_OK)
+            return rc;
+        _bcmtr_tmsg_ipc_init(tmsg);
+        tmsg->conn = conn;
+    }
+    for (i=0; i<conn->cfg.max_autos; i++, tmsg++)
+    {
+        TAILQ_INSERT_TAIL(&conn->free_auto_list, tmsg, l);
+        tmsg->free_list = &conn->free_auto_list;
+        _bcmtr_tmsg_ipc_init(tmsg);
+        tmsg->conn = conn;
+    }
+    return BCM_ERR_OK;
+}
+
+/* Cleanup function - free transport headers */
+static void _bcmtr_tmsg_list_free(bcmtr_conn *conn)
+{
+    bcmtr_msg *tmsg, *tmp_tmsg;
+
+    if (!conn->tmsg_array)
+        return;
+
+    TAILQ_FOREACH_SAFE(tmsg, &conn->msg_list, l, tmp_tmsg)
+    {
+        bcmolt_msg *msg = tmsg->msg;
+        /* Release waiting task if request-response */
+        if (msg && tmsg->err == BCM_ERR_IN_PROGRESS)
+        {
+            msg->err = BCM_ERR_COMM_FAIL;
+            bcmos_sem_post(&tmsg->sem);
+        }
+    }
+    TAILQ_FOREACH_SAFE(tmsg, &conn->free_req_list, l, tmp_tmsg)
+    {
+        bcmos_sem_destroy(&tmsg->sem);
+    }
+    bcmos_free(conn->tmsg_array);
+}
+
+/* Allocate transport header from the given free list.
+ * Must be called under lock
+ */
+static inline bcmtr_msg *_bcmtr_msg_get_free(bcmtr_msg_list *free_list)
+{
+    bcmtr_msg *tmsg = TAILQ_FIRST(free_list);
+    if (tmsg)
+        TAILQ_REMOVE(free_list, tmsg, l);
+    return tmsg;
+}
+
+/* Find message by correlation tag
+ * Called under lock
+ */
+static bcmtr_msg *_bcmtr_msg_get_by_corr_tag(const bcmtr_conn *conn, const bcmtr_hdr *hdr)
+{
+    bcmtr_msg *tmsg;
+
+    TAILQ_FOREACH(tmsg, &conn->msg_list, l)
+    {
+        if (tmsg->hdr.corr_tag==hdr->corr_tag && tmsg->hdr.msg_id==hdr->msg_id && tmsg->err == BCM_ERR_IN_PROGRESS)
+            break;
+    }
+    return tmsg;
+}
+
+/* Message reassembler. Returns TRUE if message reassembling is completed */
+static bcmos_bool _bcmtr_reassemble(bcmtr_conn *conn, bcmtr_msg *tmsg, bcmolt_buf *buf)
+{
+    bcmtr_hdr *hdr = &tmsg->hdr;
+    uint16_t frag_num = hdr->frag_number;
+    bcmos_bool done = BCMOS_FALSE;
+    bcmos_bool is_last;
+
+    is_last = !hdr->more_fragments;
+
+    /* Single-buffer message ? */
+    if (is_last && !frag_num)
+    {
+        tmsg->rx_buf = *buf;
+        tmsg->err = BCM_ERR_OK;
+        buf->start = NULL;
+        return BCMOS_TRUE;
+    }
+
+    /*
+     * Multi-part message
+     */
+
+    /* Discard if invalid fragment number or duplicate */
+    if (frag_num >= conn->cfg.max_fragments ||
+        (tmsg->reass && tmsg->reass->fragments[frag_num].start) )
+    {
+        bcmolt_buf_free(buf);
+        /* If last out-of range fragment was received report it.
+         * We want to avoid request retransmission in this case */
+        if (frag_num >= conn->cfg.max_fragments)
+        {
+            tmsg->err = BCM_ERR_TOO_MANY_FRAGS;
+            return is_last;
+        }
+        ++conn->stat.frag_invalid;
+        return BCMOS_FALSE;
+    }
+
+    /* Allocate reassembly buffer if not done yet and store fragment */
+    if (!tmsg->reass)
+    {
+        tmsg->reass = bcmos_calloc(sizeof(bcmtr_reass) + conn->cfg.max_fragments * sizeof(bcmolt_buf));
+        if (!tmsg->reass)
+        {
+            tmsg->err = BCM_ERR_NOMEM;
+            ++conn->stat.msg_no_mem;
+            bcmolt_buf_free(buf);
+            return BCMOS_FALSE;
+        }
+        tmsg->reass->fragments = (bcmolt_buf *)((long)tmsg->reass + sizeof(bcmtr_reass));
+    }
+    tmsg->reass->total_size += bcmolt_buf_get_remaining_size(buf);
+    tmsg->reass->fragments[frag_num] = *buf;
+    buf->start = NULL;
+    tmsg->reass->num_fragments++;
+    if (is_last)
+        tmsg->reass->max_fragment = frag_num;
+    done = (tmsg->reass->max_fragment && (tmsg->reass->num_fragments > tmsg->reass->max_fragment));
+    ++conn->stat.frag_received;
+
+    /* Reassemble if done */
+    if (done)
+    {
+        /* Allocate big flat buffer */
+        if (bcmolt_buf_alloc(&tmsg->rx_buf, tmsg->reass->total_size, BCMTR_BUF_ENDIAN) == BCM_ERR_OK)
+        {
+            int i;
+            uint8_t *body = tmsg->rx_buf.start;
+
+            for (i=0; i<tmsg->reass->num_fragments; i++)
+            {
+                uint32_t frag_size = bcmolt_buf_get_remaining_size(&tmsg->reass->fragments[i]);
+                BUG_ON(!tmsg->reass->fragments[i].curr);
+                memcpy(body, tmsg->reass->fragments[i].curr, frag_size);
+                body += frag_size;
+            }
+            tmsg->err = BCM_ERR_OK;
+        }
+        else
+        {
+            /* Reassembly buffer allocation failed */
+            tmsg->err = BCM_ERR_NOMEM;
+        }
+    }
+    else
+    {
+        /* More fragments expected. Update timestamp to prolong timing out */
+        tmsg->timestamp = bcmos_timestamp();
+    }
+
+    return done;
+}
+
+/* Notify application that message is ready */
+static inline void _bcmtr_notify_ready(bcmtr_conn *conn, bcmtr_msg *tmsg)
+{
+    bcmos_sem_post(&tmsg->sem);
+}
+
+/* Notify rx request/response message
+ * called under connection lock
+ */
+static inline void _bcmtr_notify_rx_response(bcmtr_conn *conn, bcmtr_msg *tmsg)
+{
+    ++conn->stat.msg_resp_received;
+
+    /* Now unlock and notify application. Autonomous handler is only called if message is OK.
+       The lock has been taken in _bcmtr_rx_packet */
+    bcmos_mutex_unlock(&conn->mutex);
+
+    /* Release waiting application. It will take care of unpacking */
+    _bcmtr_notify_ready(conn, tmsg);
+}
+
+/* Notify rx autonomous message
+ * called under connection lock
+ */
+static inline void _bcmtr_notify_rx_req_auto(bcmtr_conn *conn, bcmtr_msg *tmsg)
+{
+    bcmolt_buf rx_buf;
+    bcmolt_msg *msg = NULL;
+    bcmolt_group_id msg_id = tmsg->hdr.msg_id;
+    bcmtr_handler *h;
+    uint16_t corr_tag;
+    bcmtr_hdr hdr = tmsg->hdr;
+    uint32_t ts = tmsg->timestamp;
+    bcmolt_subchannel subch = tmsg->subch;
+    bcmos_errno err;
+
+    if (msg_id >= BCMOLT_GROUP_ID__NUM_OF)
+    {
+        BCMOS_TRACE_ERR("Unexpected msg group id: %u\n", tmsg->hdr.msg_id);
+        _bcmtr_tmsg_free(tmsg, NULL);
+        bcmos_mutex_unlock(&conn->mutex);
+        return;
+    }
+    if (tmsg->hdr.instance >= BCMTR_MAX_INSTANCES)
+    {
+        BCMOS_TRACE_ERR("Unexpected instance id: %u\n", tmsg->hdr.instance);
+        _bcmtr_tmsg_free(tmsg, NULL);
+        bcmos_mutex_unlock(&conn->mutex);
+        return;
+    }
+    h = &conn_info[conn->device].msg_handler[tmsg->hdr.msg_id][tmsg->hdr.instance];
+    BUG_ON(!h->app_cb);
+    ++conn->stat.msg_req_auto_received;
+
+    /* If dispatch is required - do it.
+     * The message will be unpacked in the context of the receiver
+     */
+    if ((h->flags & BCMOLT_AUTO_FLAGS_DISPATCH))
+    {
+        err = bcmos_msg_send_to_module(h->module, &tmsg->ipc_hdr, 0);
+        if (err)
+        {
+            BCMOS_TRACE_ERR("Can't deliver message to module %d. Error %s(%d)\n",
+                h->module, bcmos_strerror(err), err);
+            _bcmtr_tmsg_free(tmsg, NULL);
+        }
+        bcmos_mutex_unlock(&conn->mutex);
+        return;
+    }
+
+    /* No dispatch. Unpacking in the context of rx thread */
+    corr_tag = tmsg->hdr.corr_tag;
+    /* Make sure that rx_buf is not released by the following _bcmtr_msg_free.
+     * It is needed for unpack and will be released separately later. */
+    rx_buf = tmsg->rx_buf;
+    tmsg->rx_buf.start = NULL;
+    _bcmtr_tmsg_free(tmsg, NULL);
+
+    /* Release connection lock taken in _bcmtr_rx_packet */
+    bcmos_mutex_unlock(&conn->mutex);
+
+    /* Unpack and deliver */
+    _bcmtr_msg_unpack(conn, &rx_buf, &hdr, ts, &msg);
+
+    bcmolt_buf_free(&rx_buf);
+
+    if (msg)
+    {
+        msg->corr_tag = corr_tag;
+        msg->subch = subch;
+        h->app_cb(conn->device, msg);
+    }
+}
+
+/* Handle rx data. Returns number of messages that was identified and reassembled. Can be 0 or 1 */
+static int _bcmtr_rx_packet(bcmtr_conn *conn, bcmolt_subchannel subch, bcmolt_buf *buf)
+{
+    bcmtr_hdr hdr;
+    bcmtr_msg *tmsg;
+    bcmos_bool msg_done;
+    bcmos_bool is_response;
+
+    /* Transport lock. This lock is needed to
+     * - allocate/release transport header
+     * - update statistics
+     */
+    bcmos_mutex_lock(&conn->mutex);
+
+    /* If some data was received - handle it */
+    if (buf->len < BCMTR_HDR_SIZE)
+    {
+        /* Message is too short */
+        ++conn->stat.msg_comm_err;
+        goto rx_free_buf_and_error_exit;
+    }
+
+    if (NULL == buf->curr)
+    {
+        BCMOS_TRACE_ERR("Invalid buffer received!\n");
+        goto rx_done;
+    }
+
+    bcmtr_header_unpack(buf->curr, &hdr);
+    bcmolt_buf_skip(buf, BCMTR_HDR_SIZE);
+    is_response = (hdr.dir == BCMOLT_MSG_DIR_RESPONSE);
+
+    /* Find transport header. If not found - allocate new for autonomous message */
+    tmsg = _bcmtr_msg_get_by_corr_tag(conn, &hdr);
+    if (!tmsg)
+    {
+        if (!is_response)
+        {
+            /* Allocate new transport block */
+            tmsg = _bcmtr_msg_get_free(&conn->free_auto_list);
+            if (!tmsg)
+            {
+                ++conn->stat.msg_too_many_auto;
+                goto rx_free_buf_and_error_exit;
+            }
+            tmsg->err = BCM_ERR_IN_PROGRESS;
+            TAILQ_INSERT_TAIL(&conn->msg_list, tmsg, l);
+        }
+        else
+        {
+            /* Response, but no request - discard */
+            ++conn->stat.msg_no_req;
+            BCMTR_CLD_CHECK_NOTIFY(conn->device, &hdr, BCMTR_CLD_EV_RECV_DISCARD,
+                bcmos_timestamp(), buf->curr, bcmolt_buf_get_remaining_size(buf), NULL);
+            goto rx_free_buf_and_error_exit;
+        }
+    }
+
+    /* Reassemble. "buf" should not be used following this call */
+    tmsg->hdr = hdr;
+    tmsg->subch = subch;
+    msg_done = _bcmtr_reassemble(conn, tmsg, buf);
+
+    /* If expects more parts - nothing more to do here */
+    if (!msg_done)
+        goto rx_done;
+
+    if (tmsg->err && !is_response)
+    {
+        _bcmtr_tmsg_free(tmsg, &conn->msg_list);
+        goto rx_done;
+    }
+
+    /* Done with the message. Get it out of pending message queue to avoid race condition
+     * when timeout happens while the message is in flight to the destination task.
+     */
+    TAILQ_REMOVE(&conn->msg_list, tmsg, l);
+
+    /* Notify rx. conn->mutex is still taken. It will be released
+       inside _bcmtr_notify_rx_response(), _bcmtr_notify_rx_req_auto() */
+    tmsg->timestamp = bcmos_timestamp();
+    if (is_response)
+    {
+        _bcmtr_notify_rx_response(conn, tmsg);
+    }
+    else
+    {
+        _bcmtr_notify_rx_req_auto(conn, tmsg);
+    }
+
+    return 1;
+
+    /* Error return */
+rx_free_buf_and_error_exit:
+    bcmos_mutex_unlock(&conn->mutex);
+    bcmolt_buf_free(buf);
+    return 0;
+
+    /* return without a buffer */
+rx_done:
+    bcmos_mutex_unlock(&conn->mutex);
+    return 0;
+}
+
+/*
+ * Low-level fragment and send function.
+ * It allocates a series of buffers up to MAX_MTU size, copies original data into them and sends.
+ * The original buffer stays intact - in case it should be retransmitted
+ */
+static bcmos_errno _bcmtr_fragment_and_send(bcmtr_conn *conn, bcmtr_msg *tmsg, bcmtr_send_flags flags)
+{
+    uint32_t frag_number = 0;
+    bcmos_errno err = BCM_ERR_OK;
+    uint32_t data_offset = conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE;
+    uint32_t data_len = bcmolt_buf_get_used(&tmsg->tx_buf) - data_offset;
+    uint8_t *data = tmsg->tx_buf.start + data_offset;
+
+    do
+    {
+        uint32_t send_len = data_len + BCMTR_HDR_SIZE;
+        bcmolt_buf frag;
+
+        if (send_len > conn->cfg.max_mtu)
+        {
+            send_len = conn->cfg.max_mtu;
+            tmsg->hdr.more_fragments = BCMOS_TRUE;
+        }
+        else
+        {
+            tmsg->hdr.more_fragments = BCMOS_FALSE;
+        }
+
+        err = bcmolt_buf_alloc(&frag, send_len + conn->cfg.plugin_cfg.headroom, BCMTR_BUF_ENDIAN);
+        if (err)
+            break;
+
+        tmsg->hdr.frag_number = frag_number++;
+
+        /* Pack correlation tag, command and length */
+        bcmtr_header_pack(&tmsg->hdr, frag.start + conn->cfg.plugin_cfg.headroom);
+        bcmolt_buf_skip(&frag, data_offset);
+        if (bcmolt_buf_write(&frag, data, send_len - BCMTR_HDR_SIZE))
+        {
+            /* Send using customer-provided driver */
+            err = conn->driver.send(conn->drv_priv, tmsg->msg->subch, &frag, flags);
+        }
+        else
+        {
+            err = BCM_ERR_OVERFLOW;
+        }
+        bcmolt_buf_free(&frag);
+        if (err)
+        {
+            break;
+        }
+
+        data_len -= (send_len - BCMTR_HDR_SIZE);
+        data += (send_len - BCMTR_HDR_SIZE);
+
+    } while (data_len);
+
+    return err;
+}
+
+/* Check for time-outs. returns number of messages timed out */
+static int _bcmtr_check_timeout(bcmtr_conn *conn)
+{
+    bcmtr_msg *tmsg, *tmp;
+    uint32_t now;
+    int nmsg = 0;
+    bcmos_errno err;
+
+    /* Transport lock */
+    bcmos_mutex_lock(&conn->mutex);
+
+    now = bcmos_timestamp();
+    TAILQ_FOREACH_SAFE(tmsg, &conn->msg_list, l, tmp)
+    {
+        bcmolt_msg *msg = tmsg->msg;
+
+        if (now - tmsg->timestamp <= conn->cfg.msg_timeout)
+            continue;
+
+        /* Retransmit ? */
+        if (msg && tmsg->tx_count <= conn->cfg.max_retries)
+        {
+            tmsg->timestamp = bcmos_timestamp();
+            BCMTR_CLD_CHECK_NOTIFY(
+                conn->device,
+                &tmsg->hdr,
+                BCMTR_CLD_EV_RESEND,
+                tmsg->timestamp,
+                tmsg->tx_buf.start + conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE,
+                tmsg->tx_buf.len - (conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE),
+                msg);
+
+            /* Fragment and send or send directly, depending on message length */
+            if (bcmolt_buf_get_used(&tmsg->tx_buf) > conn->cfg.max_mtu)
+            {
+                err = _bcmtr_fragment_and_send(conn, tmsg, BCMTR_SEND_FLAGS_PRI_NORMAL);
+            }
+            else
+            {
+                err = conn->driver.send(conn->drv_priv, msg->subch, &tmsg->tx_buf, BCMTR_SEND_FLAGS_PRI_NORMAL);
+            }
+            if (err)
+            {
+                ++conn->stat.msg_comm_err;
+            }
+            ++tmsg->tx_count;
+            continue;
+        }
+
+        /* Giving up */
+        /* Release waiting task if request-response - unless it has already been done */
+        if (msg)
+        {
+            if (tmsg->err == BCM_ERR_IN_PROGRESS)
+            {
+                tmsg->err = BCM_ERR_TIMEOUT;
+            }
+            BCMTR_CLD_CHECK_NOTIFY(
+                conn->device,
+                &tmsg->hdr,
+                BCMTR_CLD_EV_TIMEOUT,
+                bcmos_timestamp(),
+                tmsg->tx_buf.start + conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE,
+                tmsg->tx_buf.len - (conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE),
+                msg);
+            TAILQ_REMOVE(&conn->msg_list, tmsg, l);
+            _bcmtr_notify_ready(conn, tmsg);
+            ++conn->stat.msg_req_timeout;
+        }
+        else
+        {
+            _bcmtr_tmsg_free(tmsg, &conn->msg_list);
+            ++conn->stat.msg_reass_timeout;
+        }
+        ++nmsg;
+    }
+    conn->last_timeout_check = bcmos_timestamp();
+
+    /* Release transport lock */
+    bcmos_mutex_unlock(&conn->mutex);
+
+    return nmsg;
+}
+
+/* Check for receive and timeouts */
+static int _bcmtr_rx_poll(bcmtr_conn *conn, int *pnmsg)
+{
+    bcmolt_buf buf;
+    int nmsg = 0, nmsg_prev;
+    bcmos_errno rc = BCM_ERR_OK;
+    bcmolt_subchannel subch;
+
+    do
+    {
+        nmsg_prev = nmsg;
+
+        /* Plugin driver's recv - get pending rx packet is any.
+         * The function is not allowed to block for more then BCMTR_MSG_TIMEOUT ms
+         */
+        rc = conn->driver.recv(conn->drv_priv, &subch, &buf);
+        if (rc != BCM_ERR_OK)
+        {
+            if (rc == BCM_ERR_NOMEM)
+            {
+                ++conn->stat.msg_no_mem;
+            }
+        }
+        else
+        {
+            nmsg += _bcmtr_rx_packet(conn, subch, &buf);
+        }
+
+        /* Check for timeouts if any */
+        if (bcmos_timestamp() - conn->last_timeout_check > conn->timeout_check_period)
+        {
+            /* Check requests waiting for acknowledge and multy-part messages for timeout.
+             * Timed-out requests are retransmitted.
+             */
+            nmsg += _bcmtr_check_timeout(conn);
+        }
+    } while(nmsg_prev != nmsg);
+
+    *pnmsg = nmsg;
+
+    return rc;
+}
+
+/* Rx thread handler */
+static int _bcmtr_rx_thread_handler(long arg)
+{
+    bcmtr_conn *conn = (bcmtr_conn *)arg;
+    int nmsgs;
+    int rc;
+
+    while(!conn->kill_request)
+    {
+        rc = _bcmtr_rx_poll(conn, &nmsgs);
+        if (rc == BCM_ERR_COMM_FAIL)
+            bcmos_usleep(1000);
+    }
+    conn->kill_done = 1;
+
+    return 0;
+}
+
+/*
+ * Internal transport interface
+ */
+
+
+/** Default message handler - discard */
+static void _bcmtr_dft_msg_handler(bcmolt_devid olt, bcmolt_msg *msg)
+{
+    bcmtr_conn *conn = conn_info[olt].conn;
+
+    /* ToDo: log */
+
+    if (conn)
+        ++conn->stat.no_rx_handler;
+
+    bcmolt_msg_free(msg);
+}
+
+static bcmos_errno _bcmtr_create_rx_thread(bcmtr_conn *conn, bcmos_bool raw_mode)
+{
+    bcmos_errno err = BCM_ERR_OK;
+
+    if (conn->cfg.rx_thread_priority >= 0 && !raw_mode)
+    {
+        bcmos_task_parm parm = {
+            .priority = conn->cfg.rx_thread_priority,
+            .stack_size = BCMTR_RX_THREAD_STACK,
+            .handler = _bcmtr_rx_thread_handler,
+            .name = conn->name,
+            .data = (long)conn
+        };
+        conn->kill_request = BCMOS_FALSE;
+        conn->kill_done = BCMOS_FALSE;
+        err = bcmos_task_create(&conn->rx_thread, &parm);
+        if (err == BCM_ERR_OK)
+            conn->rx_thread_created = BCMOS_TRUE;
+    }
+
+    return err;
+}
+
+static void _bcmtr_destroy_rx_thread(bcmtr_conn *conn)
+{
+    /* Kill rx thread if any */
+    if (conn->rx_thread_created)
+    {
+        conn->kill_request = 1;
+        while(!conn->kill_done)
+            bcmos_usleep(1000);
+        bcmos_task_destroy(&conn->rx_thread);
+    }
+}
+
+static bcmos_errno _bcmtr_connect(bcmolt_devid device, bcmtr_conn **pconn, bcmos_bool raw_mode)
+{
+    bcmtr_conn *conn;
+    bcmos_errno err = BCM_ERR_OK;
+
+    /* Init OS abstraction - just in case */
+    err = bcmos_init();
+    if (err != BCM_ERR_OK && err != BCM_ERR_ALREADY)
+    {
+        return err;
+    }
+
+    bcmos_mutex_lock(&conn_lock);
+
+    /* Allocate */
+    conn = bcmos_calloc(sizeof(*conn));
+    if (!conn)
+    {
+        bcmos_mutex_unlock(&conn_lock);
+        return BCM_ERR_NOMEM;
+    }
+
+    /* Get configuration */
+    err = bcmtr_cfg_get(device, &conn->cfg, &conn->driver);
+    if (err)
+    {
+        bcmos_mutex_unlock(&conn_lock);
+        bcmos_free(conn);
+        return err;
+    }
+
+    snprintf(conn->name, sizeof(conn->name), "tr_rx%u", device);
+    TAILQ_INIT(&conn->free_req_list);
+    TAILQ_INIT(&conn->free_auto_list);
+    TAILQ_INIT(&conn->msg_list);
+    bcmos_mutex_create(&conn->mutex, 0, NULL);
+
+    /* Convert timeouts to us */
+    conn->cfg.msg_timeout *= 1000;
+    conn->cfg.msg_ready_timeout *= 1000;
+    conn->cfg.msg_wait_timeout *= 1000;
+
+    /* Set defaults */
+    conn->timeout_check_period = conn->cfg.msg_wait_timeout;
+    conn->last_timeout_check = bcmos_timestamp();
+
+    /* Allocate and initialize transport blocks and put onto free request and autonomous lists */
+    err = _bcmtr_tmsg_list_alloc(conn);
+
+    /* Open/connect on driver level */
+    err = err ? err : conn->driver.open(device, &conn->cfg.plugin_cfg, &conn->drv_priv);
+    if (err)
+    {
+        bcmos_mutex_destroy(&conn->mutex);
+        goto cleanup;
+    }
+
+    conn->connected = BCMOS_TRUE;
+
+    /* Create rx thread if necessary */
+    err = _bcmtr_create_rx_thread(conn, raw_mode);
+    if (err)
+    {
+        conn->driver.close(conn->drv_priv);
+        bcmos_mutex_destroy(&conn->mutex);
+        goto cleanup;
+    }
+    conn->device = device;
+
+    *pconn = conn;
+    bcmos_mutex_unlock(&conn_lock);
+
+    return BCM_ERR_OK;
+
+cleanup:
+    _bcmtr_tmsg_list_free(conn);
+    bcmos_free(conn);
+    bcmos_mutex_unlock(&conn_lock);
+    return err;
+}
+
+/** Query whether or not the device is currently connected */
+bcmos_errno bcmtr_is_connected(bcmolt_devid device, bcmos_bool *is_connected)
+{
+    bcmtr_conn *conn;
+    if (device >= BCMTR_MAX_OLTS)
+    {
+        return BCM_ERR_RANGE;
+    }
+    conn = conn_info[device].conn;
+    *is_connected = (conn != NULL && conn->connected);
+    return BCM_ERR_OK;
+}
+
+/** Open transport channel */
+bcmos_errno bcmtr_connect(bcmolt_devid device)
+{
+    bcmtr_conn *conn;
+    return _bcmtr_conn_get(device, &conn);
+}
+
+/** Close transport channel */
+bcmos_errno bcmtr_disconnect(bcmolt_devid device)
+{
+    bcmtr_conn *conn;
+
+    if (device >= BCMTR_MAX_OLTS)
+    {
+        return BCM_ERR_RANGE;
+    }
+    bcmos_mutex_lock(&conn_lock);
+    conn = conn_info[device].conn;
+    if (!conn)
+    {
+        bcmos_mutex_unlock(&conn_lock);
+        return BCM_ERR_NOT_CONNECTED;
+    }
+    conn_info[device].conn = NULL;
+
+    /* Kill rx thread if any */
+    _bcmtr_destroy_rx_thread(conn);
+
+    bcmos_mutex_lock(&conn->mutex);
+    /* Close connection */
+    if (conn->driver.close)
+    {
+        conn->driver.close(conn->drv_priv);
+    }
+
+    /* Release all pending messages */
+    bcmos_usleep(100000);
+    _bcmtr_tmsg_list_free(conn);
+
+    bcmos_mutex_unlock(&conn->mutex);
+    bcmos_mutex_destroy(&conn->mutex);
+    bcmos_free(conn);
+
+    bcmos_mutex_unlock(&conn_lock);
+
+    return BCM_ERR_OK;
+}
+
+/* Low-level disconnect that breaks "physical" connection, but doesn't destroy connection structure and registrations */
+bcmos_errno bcmtr_driver_disconnect(bcmolt_devid device)
+{
+    bcmtr_conn *conn;
+    bcmos_errno err = BCM_ERR_OK;
+
+    if (device >= BCMTR_MAX_OLTS)
+    {
+        return BCM_ERR_RANGE;
+    }
+
+    bcmos_mutex_lock(&conn_lock);
+
+    conn = conn_info[device].conn;
+    if (conn == NULL || !conn->connected)
+    {
+        bcmos_mutex_unlock(&conn_lock);
+        return BCM_ERR_NOT_CONNECTED;
+    }
+
+    _bcmtr_destroy_rx_thread(conn);
+
+    bcmos_mutex_lock(&conn->mutex);
+
+    /* Close driver connection */
+    if (conn->driver.close != NULL)
+    {
+        err = conn->driver.close(conn->drv_priv);
+        if (err != BCM_ERR_OK)
+        {
+            BCMOS_TRACE_ERR("Failed to close transport driver: %s (%d)\n", bcmos_strerror(err), err);
+        }
+    }
+
+    conn->connected = BCMOS_FALSE;
+
+    bcmos_mutex_unlock(&conn->mutex);
+
+    bcmos_mutex_unlock(&conn_lock);
+
+    return err;
+}
+
+/* Repair/reconnect the driver-level connection for an already-connected device */
+bcmos_errno bcmtr_driver_reconnect(bcmolt_devid device)
+{
+    bcmtr_conn *conn;
+    bcmos_errno err;
+
+    if (device >= BCMTR_MAX_OLTS)
+    {
+        return BCM_ERR_RANGE;
+    }
+    bcmos_mutex_lock(&conn_lock);
+    conn = conn_info[device].conn;
+    if (conn == NULL)
+    {
+        bcmos_mutex_unlock(&conn_lock);
+        return BCM_ERR_NOT_CONNECTED;
+    }
+
+    if (conn->connected)
+    {
+        bcmtr_driver_disconnect(device);
+    }
+    bcmos_mutex_lock(&conn->mutex);
+
+    /* Re-open driver connection */
+    err = conn->driver.open(device, &conn->cfg.plugin_cfg, &conn->drv_priv);
+    if (err != BCM_ERR_OK)
+    {
+        BCMOS_TRACE_ERR("Failed to re-open transport driver: %s (%d)\n", bcmos_strerror(err), err);
+    }
+
+    err = _bcmtr_create_rx_thread(conn, BCMOS_FALSE);
+    if (err != BCM_ERR_OK)
+    {
+        BCMOS_TRACE_ERR("Failed to create RX transport task: %s (%d)\n", bcmos_strerror(err), err);
+        conn->driver.close(conn->drv_priv);
+    }
+    conn->connected = BCMOS_TRUE;
+
+    bcmos_mutex_unlock(&conn->mutex);
+
+    bcmos_mutex_unlock(&conn_lock);
+
+    return err;
+}
+
+/* Register for notification that transmit failed because tx_queue was full */
+bcmos_errno bcmtr_tx_overflow_cb_register(bcmolt_devid device, F_bcmtr_tx_overflow cb)
+{
+    if (device >= BCMTR_MAX_OLTS)
+    {
+        return BCM_ERR_RANGE;
+    }
+
+    conn_info[device].overflow_cb = cb;
+    return BCM_ERR_OK;
+}
+
+/* Send message.
+ * Internal function. Called under connection lock
+ */
+static bcmos_errno _bcmtr_send(bcmtr_conn *conn, bcmolt_msg *msg, bcmolt_buf *tx_buf, bcmtr_send_flags flags, bcmtr_msg **ptmsg)
+{
+    bcmtr_msg *tmsg;
+    bcmos_errno err;
+
+    if (!conn->connected)
+    {
+        ++conn->stat.not_connected;
+        return BCM_ERR_NOT_CONNECTED;
+    }
+
+    /* Allocate message transport header */
+    tmsg = _bcmtr_msg_get_free(&conn->free_req_list);
+    if (!tmsg)
+    {
+        ++conn->stat.msg_no_mem;
+        return BCM_ERR_TOO_MANY_REQS;
+    }
+    tmsg->msg = msg;
+
+    /* Fill transport header */
+    err = bcmtr_header_fill(tmsg->msg, &tmsg->hdr);
+    if (err)
+        return err;
+
+    tmsg->err = BCM_ERR_IN_PROGRESS;
+    tmsg->tx_count = 1;
+
+    /* Save transmit buffer. It will be released together with transport header */
+    tmsg->tx_buf = *tx_buf;
+
+    tmsg->timestamp = bcmos_timestamp();
+
+    if (bcmolt_buf_get_used(tx_buf) > conn->cfg.max_mtu)
+    {
+        err = _bcmtr_fragment_and_send(conn, tmsg, flags);
+    }
+    else
+    {
+        /* Pack correlation tag, command and length */
+        bcmtr_header_pack(&tmsg->hdr, tmsg->tx_buf.start + conn->cfg.plugin_cfg.headroom);
+
+        /* Send using customer-provided driver */
+        err = conn->driver.send(conn->drv_priv, msg->subch, &tmsg->tx_buf, flags);
+    }
+    BCMTR_CLD_CHECK_NOTIFY(
+        conn->device,
+        &tmsg->hdr,
+        BCMTR_CLD_EV_SEND,
+        tmsg->timestamp,
+        tmsg->tx_buf.start + conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE,
+        tmsg->tx_buf.len - (conn->cfg.plugin_cfg.headroom + BCMTR_HDR_SIZE),
+        msg);
+    if (err != BCM_ERR_OK)
+    {
+        ++conn->stat.msg_comm_err;
+        goto cleanup;
+    }
+
+    tx_buf->start = NULL; /* Ownership passed to tmsg */
+    ++conn->stat.msg_sent;
+
+    if (ptmsg)
+    {
+        *ptmsg = tmsg;
+    }
+    else
+    {
+        _bcmtr_tmsg_free(tmsg, NULL);
+    }
+
+    return BCM_ERR_OK;
+
+    /* error */
+cleanup:
+    tmsg->tx_buf.start = NULL; /* prevent tx buffer de-allocation */
+    _bcmtr_tmsg_free(tmsg, NULL);
+    return err;
+}
+
+
+/* Allocate tx buffer and pack */
+static bcmos_errno _bcmtr_pack(const bcmtr_conn *conn, bcmolt_msg *msg, bcmolt_buf *buf)
+{
+    int32_t len = bcmolt_msg_get_packed_length(msg);
+    uint32_t headroom = conn->cfg.plugin_cfg.headroom;
+    bcmos_errno err;
+
+    if (len < 0)
+        return (bcmos_errno)len;
+
+    /* Reallocate if too big */
+    len += BCMTR_HDR_SIZE + headroom;
+    if (buf->start)
+    {
+        if (buf->len < len)
+        {
+            /* ToDo: reallocate */
+            return BCM_ERR_OVERFLOW;
+        }
+        else
+        {
+            bcmolt_buf_init(buf, len, buf->start, BCMTR_BUF_ENDIAN);
+        }
+    }
+    else
+    {
+        err = bcmolt_buf_alloc(buf, len, BCMTR_BUF_ENDIAN);
+        if (err)
+        {
+            return err;
+        }
+    }
+
+    /* Reserve room for header */
+    buf->curr = buf->start + BCMTR_HDR_SIZE + headroom;
+
+    /* Pack */
+    err = bcmolt_msg_pack(msg, buf);
+
+    return err;
+}
+
+/*
+ * External message interface
+ */
+
+/* Send message. Don't expect response. */
+bcmos_errno bcmtr_send(bcmolt_devid device, bcmolt_msg *msg, bcmtr_send_flags flags)
+{
+    bcmtr_conn *conn;
+    bcmos_errno err;
+    bcmolt_buf tx_buf = {};
+
+    err = _bcmtr_conn_get(device, &conn);
+    if (err)
+        return err;
+
+    /* Allocate transport buffer and pack */
+    err = _bcmtr_pack(conn, msg, &tx_buf);
+    if (err)
+    {
+        bcmolt_buf_free(&tx_buf);
+        return err;
+    }
+
+    bcmos_mutex_lock(&conn->mutex);
+    err = _bcmtr_send(conn, msg, &tx_buf, flags, NULL);
+    bcmos_mutex_unlock(&conn->mutex);
+    if (err)
+    {
+        bcmolt_buf_free(&tx_buf);
+        if (err == BCM_ERR_QUEUE_FULL && conn_info[device].overflow_cb)
+            conn_info[device].overflow_cb(conn->device, flags);
+    }
+
+    return err;
+}
+
+static bcmos_errno bcmtr_call_err(bcmolt_msg *msg, bcmos_errno err, const char *err_text)
+{
+    msg->dir = BCMOLT_MSG_DIR_RESPONSE;
+    msg->err = err;
+    if (err_text != NULL)
+    {
+        strncpy(msg->err_text, err_text, BCMOLT_MAX_ERR_TEXT_LENGTH-1);
+        msg->err_text[BCMOLT_MAX_ERR_TEXT_LENGTH-1] = 0;
+    }
+    return err;
+}
+
+/* Send message and wait for response */
+bcmos_errno bcmtr_call(bcmolt_devid device, bcmolt_msg *msg)
+{
+    static uint32_t corr_tag = 0;
+    bcmos_task *task;
+    bcmtr_msg *tmsg = NULL;
+    bcmtr_conn *conn;
+    bcmolt_buf tx_buf = {};
+    bcmos_errno err;
+    uint8_t instance;
+
+    msg->err = BCM_ERR_OK;
+    msg->dir = BCMOLT_MSG_DIR_REQUEST;
+    msg->corr_tag = ++corr_tag;
+
+    err = _bcmtr_conn_get(device, &conn);
+    if (err)
+    {
+        return bcmtr_call_err(msg, err, NULL);
+    }
+
+    /* prevent sleeping in RX thread (this would cause it to never wake up) */
+    task = bcmos_task_current();
+    if (task == &conn->rx_thread)
+    {
+        return bcmtr_call_err(msg, BCM_ERR_COMM_FAIL, "Cannot call API functions from PCI RX thread");
+    }
+
+    instance = bcmolt_msg_instance(msg);
+    if (instance >= BCMTR_MAX_INSTANCES)
+    {
+        return bcmtr_call_err(msg, BCM_ERR_KEY_RANGE, "Invalid PON index");
+    }
+
+    /* Allocate transport buffer and pack */
+    err = _bcmtr_pack(conn, msg, &tx_buf);
+    if (err)
+    {
+        bcmolt_buf_free(&tx_buf);
+        return bcmtr_call_err(msg, err, NULL);
+    }
+
+    /* transmit request under connection lock */
+    bcmos_mutex_lock(&conn->mutex);
+    err = _bcmtr_send(conn, msg, &tx_buf, BCMTR_SEND_FLAGS_CALL, &tmsg);
+    if (!tmsg)
+    {
+        bcmos_mutex_unlock(&conn->mutex);
+        bcmolt_buf_free(&tx_buf);
+        return bcmtr_call_err(msg, err, NULL);
+    }
+    TAILQ_INSERT_TAIL(&conn->msg_list, tmsg, l);
+    bcmos_mutex_unlock(&conn->mutex);
+
+    /* Wait for response or timeout.
+     * Message timeout is enforced by audit rather than semaphore timeout option
+     */
+    bcmos_sem_wait(&tmsg->sem, BCMOS_WAIT_FOREVER);
+
+    /* Connection could've been killed while we are waiting here.
+     * It is indicated by COMM_FAILURE in msg->err.
+     * In this case transport header (tmsg) is already released
+     */
+    if (msg->err == BCM_ERR_COMM_FAIL)
+    {
+        return bcmtr_call_err(msg, msg->err, NULL);
+    }
+
+    err = tmsg->err;
+    if (!err)
+    {
+        err = _bcmtr_msg_unpack(conn, &tmsg->rx_buf, &tmsg->hdr, tmsg->timestamp, &msg);
+    }
+
+    /* Take connection lock again in order to release transport header safely */
+    bcmos_mutex_lock(&conn->mutex);
+    _bcmtr_tmsg_free(tmsg, NULL);
+    bcmos_mutex_unlock(&conn->mutex);
+
+    return bcmtr_call_err(msg, err ? err : msg->err, NULL);
+}
+
+
+#ifdef BCM_SUBSYSTEM_HOST
+/* Send (un)registration info to the mux */
+static bcmos_errno _bcmtr_send_to_mux(bcmtr_conn *conn, bcmtr_hdr *hdr)
+{
+    bcmolt_buf buf;
+    uint8_t packed_hdr[BCMTR_HDR_SIZE];
+    bcmos_errno err;
+
+    err = bcmolt_buf_alloc(&buf, BCMTR_HDR_SIZE + conn->cfg.plugin_cfg.headroom, BCMTR_BUF_ENDIAN);
+    if (err)
+    {
+        return err;
+    }
+    bcmolt_buf_skip(&buf, conn->cfg.plugin_cfg.headroom);
+    bcmtr_header_pack(hdr, packed_hdr);
+    if (bcmolt_buf_write(&buf, packed_hdr, BCMTR_HDR_SIZE))
+        err = conn->driver.send(conn->drv_priv, 0, &buf, BCMTR_SEND_FLAGS_PRI_NORMAL);
+    else
+        err = BCM_ERR_OVERFLOW;
+    bcmolt_buf_free(&buf);
+    return err;
+}
+#endif
+
+/** Register message handler
+ *
+ * \param[in]   device          OLT device index
+ * \param[in]   parm            Registration parameters
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_msg_handler_register(bcmolt_devid device, const bcmtr_handler_parm *parm)
+{
+    bcmtr_conn *conn;
+    bcmos_errno err = BCM_ERR_OK;
+    bcmolt_group_id msg_id;
+    bcmtr_handler *h;
+
+    if (device >= BCMTR_MAX_OLTS || !parm || !parm->app_cb || parm->instance >= BCMTR_MAX_INSTANCES)
+    {
+        return BCM_ERR_PARM;
+    }
+
+    if ((unsigned)parm->object >= BCMOLT_OBJ_ID__NUM_OF)
+    {
+        bcmtr_handler_parm p1 = *parm;
+
+        for (p1.object = 0; p1.object < BCMOLT_OBJ_ID__NUM_OF && !err; p1.object++)
+        {
+            err = bcmtr_msg_handler_register(device, &p1);
+            /* Ignore RANGE error that indicates that the object being iterated doesn't have this group */
+            /* Ignore ALREADY error that indicates that registration is already present for specific message and was skipped */
+            if ((err == BCM_ERR_RANGE) || (err == BCM_ERR_ALREADY))
+            {
+                err = BCM_ERR_OK;
+            }
+        }
+        return err;
+    }
+
+    if ((unsigned)parm->subgroup == BCMOLT_SUBGROUP_ANY)
+    {
+        bcmtr_handler_parm p1 = *parm;
+
+        for (p1.subgroup = 0;
+            bcmolt_group_id_combine(p1.object, p1.group, p1.subgroup, &msg_id) == BCM_ERR_OK &&
+                (err == BCM_ERR_OK || err == BCM_ERR_ALREADY);
+            p1.subgroup++)
+        {
+            err = bcmtr_msg_handler_register(device, &p1);
+        }
+        if (err == BCM_ERR_ALREADY)
+        {
+            err = BCM_ERR_OK;
+        }
+        return err;
+    }
+
+    /* Specific object/group/subgroup */
+    err = bcmolt_group_id_combine(parm->object, parm->group, parm->subgroup, &msg_id);
+    if (err)
+        return err;
+    h = &conn_info[device].msg_handler[msg_id][parm->instance];
+
+    /* Refuse new registration if already registered */
+    if (h->app_cb != _bcmtr_dft_msg_handler)
+    {
+        return BCM_ERR_ALREADY;
+    }
+
+    h->app_cb = parm->app_cb;
+    h->flags = parm->flags;
+    if ((parm->flags & BCMOLT_AUTO_FLAGS_DISPATCH))
+    {
+        if (parm->module != BCMOS_MODULE_ID_NONE)
+        {
+            h->module = parm->module;
+        }
+        else
+        {
+            h->module = bcmos_module_current();
+        }
+    }
+    else
+    {
+        h->module = BCMOS_MODULE_ID_NONE;
+    }
+
+#ifdef BCM_SUBSYSTEM_HOST
+    /* On the host, automatically connect on message handler registration */
+    err = _bcmtr_conn_get(device, &conn);
+    if (err)
+        return err;
+
+    /* Registration with tr-mux is per device, per-instance, per-object */
+    if (!parm->subgroup)
+    {
+        /* Send registration info to the mux driver. It is just a header */
+        bcmtr_hdr hdr;
+        memset(&hdr, 0, sizeof(hdr));
+        hdr.msg_id = msg_id;
+        hdr.auto_proxy_reg = 1;
+        hdr.instance = parm->instance;
+        err = _bcmtr_send_to_mux(conn, &hdr);
+        if (err)
+        {
+            bcmtr_msg_handler_unregister(device, parm);
+        }
+    }
+#else
+    (void)conn;
+#endif
+
+    return err;
+}
+
+/* Unregister autonomous message handler */
+bcmos_errno bcmtr_msg_handler_unregister(bcmolt_devid device, const bcmtr_handler_parm *parm)
+{
+    bcmtr_conn *conn;
+    bcmos_errno err = BCM_ERR_OK;
+    bcmolt_group_id msg_id;
+    bcmtr_handler *h;
+
+    if (device >= BCMTR_MAX_OLTS || !parm || parm->instance >= BCMTR_MAX_INSTANCES)
+    {
+        return BCM_ERR_PARM;
+    }
+
+    if ((unsigned)parm->object >= BCMOLT_OBJ_ID__NUM_OF)
+    {
+        bcmtr_handler_parm p1 = *parm;
+        for (p1.object = 0; p1.object < BCMOLT_OBJ_ID__NUM_OF && !err; p1.object++)
+        {
+            err = bcmtr_msg_handler_unregister(device, &p1);
+            /* Ignore RANGE error that indicates that the object being iterated doesn't have this group */
+            if (err == BCM_ERR_RANGE)
+            {
+                err = BCM_ERR_OK;
+            }
+        }
+        return err;
+    }
+
+    if ((unsigned)parm->subgroup == BCMOLT_SUBGROUP_ANY)
+    {
+        bcmtr_handler_parm p1 = *parm;
+
+        for (p1.subgroup = 0;
+            bcmolt_group_id_combine(p1.object, p1.group, p1.subgroup, &msg_id) == BCM_ERR_OK && !err;
+            p1.subgroup++)
+        {
+            err = bcmtr_msg_handler_unregister(device, &p1);
+        }
+        return err;
+    }
+
+    err = bcmolt_group_id_combine(parm->object, parm->group, parm->subgroup, &msg_id);
+    if (err)
+        return err;
+
+    h = &conn_info[device].msg_handler[msg_id][parm->instance];
+    h->app_cb = _bcmtr_dft_msg_handler;
+    h->flags = BCMOLT_AUTO_FLAGS_NONE;
+    h->module = BCMOS_MODULE_ID_NONE;
+
+#ifdef BCM_SUBSYSTEM_HOST
+    /* On the host, automatically connect on message handler (de)registration */
+    err = _bcmtr_conn_get(device, &conn);
+    if (err)
+        return err;
+
+    /* Registration with tr-mux is per device, per-instance, per-object */
+    if (!parm->subgroup)
+    {
+        /* Send un-registration info to the mux driver. It is just a header */
+        bcmtr_hdr hdr;
+        memset(&hdr, 0, sizeof(hdr));
+        hdr.msg_id = msg_id;
+        hdr.auto_proxy_unreg = 1;
+        hdr.instance = parm->instance;
+        err = _bcmtr_send_to_mux(conn, &hdr);
+    }
+#else
+    (void)conn;
+#endif
+
+    return err;
+}
+
+/** Get registration info
+ *
+ * \param[in]           device          OLT device index
+ * \param[in,out]       parm            Registration parameters.
+ *                                      instance, group, object, subgroup must be set
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_msg_handler_register_get(bcmolt_devid device, bcmtr_handler_parm *parm)
+{
+    bcmos_errno err;
+    bcmolt_group_id msg_id;
+    bcmtr_handler *h;
+
+    if (device >= BCMTR_MAX_OLTS ||
+        !parm ||
+        parm->instance >= BCMTR_MAX_INSTANCES ||
+        (unsigned)parm->object >= BCMOLT_OBJ_ID__NUM_OF ||
+        (unsigned)parm->subgroup == BCMOLT_SUBGROUP_ANY)
+    {
+        return BCM_ERR_PARM;
+    }
+
+    err = bcmolt_group_id_combine(parm->object, parm->group, parm->subgroup, &msg_id);
+    if (err)
+        return err;
+
+    h = &conn_info[device].msg_handler[msg_id][parm->instance];
+    parm->app_cb = (h->app_cb == _bcmtr_dft_msg_handler) ? NULL : h->app_cb;
+    parm->flags = h->flags;
+    parm->module = h->module;
+
+    return BCM_ERR_OK;
+}
+
+/* Get transport statistics */
+bcmos_errno bcmtr_stat_get(bcmolt_devid device, bcmtr_stat *stat)
+{
+    bcmtr_conn *conn;
+    bcmos_errno err;
+
+    if (!stat)
+    {
+        return BCM_ERR_PARM;
+    }
+    err = _bcmtr_conn_get(device, &conn);
+    if (err)
+    {
+        return err;
+    }
+    bcmos_mutex_lock(&conn->mutex);
+    *stat = conn->stat;
+    memset(&conn->stat, 0, sizeof(conn->stat));
+    bcmos_mutex_unlock(&conn->mutex);
+
+    return BCM_ERR_OK;
+}
+
+#if defined(SIMULATION_BUILD) && defined(LINUX_USER_SPACE) && defined(BCM_SUBSYSTEM_EMBEDDED) && defined(BCMTR_UDP_SUPPORT)
+static int _bcmtr_assign_random_port(void)
+{
+    int port;
+
+    srand(bcmos_timestamp());
+    port = rand() % 50000;
+    if (port < 20000)
+    {
+        port += 20000;
+    }
+
+    return port;
+}
+#endif
+
+/* Connect device in raw mode. Receive task is NOT created */
+bcmos_errno bcmtr_proxy_connect(bcmolt_devid device, uint32_t *headroom)
+{
+    bcmtr_conn *conn;
+    bcmos_errno rc;
+
+    rc = _bcmtr_conn_get_any(device, &conn, BCMOS_TRUE);
+    if (!rc)
+    {
+        *headroom = conn->cfg.plugin_cfg.headroom;
+    }
+    return rc;
+}
+
+/* Send data to device in raw mode */
+bcmos_errno bcmtr_proxy_send(bcmolt_devid device, bcmolt_buf *tx_buf)
+{
+    bcmtr_conn *conn;
+    bcmos_errno rc;
+    rc = _bcmtr_conn_get_any(device, &conn, BCMOS_TRUE);
+    if (rc)
+        return rc;
+    rc = conn->driver.send(conn->drv_priv, 0, tx_buf, BCMTR_SEND_FLAGS_NONE);
+    return rc;
+}
+
+/* Receive data from device in raw mode */
+bcmos_errno bcmtr_proxy_receive(bcmolt_devid device, bcmolt_buf *rx_buf)
+{
+    bcmtr_conn *conn;
+    bcmolt_subchannel subch;
+    bcmos_errno rc;
+    rc = _bcmtr_conn_get_any(device, &conn, BCMOS_TRUE);
+    if (rc)
+        return rc;
+    rc = conn->driver.recv(conn->drv_priv, &subch, rx_buf);
+    return rc;
+}
+
+/** Initialize transport library.
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_init(void)
+{
+    bcmos_errno err = BCM_ERR_OK;
+    bcmolt_devid device;
+
+    bcmos_printf("bcmtr_init: init transport library\n");
+
+#if defined(BCMTR_UDP_SUPPORT)
+
+    /* Set defaults and add configuration command */
+    if (!bcmtr_host_ip)
+        bcmtr_host_ip = BCMTR_TR_UDP_HOST_IP;
+    if (!bcmtr_host_udp_port)
+        bcmtr_host_udp_port = BCMTR_TR_UDP_HOST_PORT;
+    for (device = 0; device < BCMTR_MAX_OLTS; device++)
+    {
+        if (!bcmtr_olt_ip[device])
+            bcmtr_olt_ip[device] = BCMTR_TR_UDP_OLT_IP + device;
+        if (!bcmtr_olt_udp_port[device])
+            bcmtr_olt_udp_port[device] = BCMTR_TR_UDP_OLT_PORT;
+    }
+
+    /* A hack to allow multiple simulations run on the same PC */
+#if defined(SIMULATION_BUILD) && defined(LINUX_USER_SPACE) && defined(BCM_SUBSYSTEM_EMBEDDED)
+    {
+        bcmtr_olt_udp_port[0] = _bcmtr_assign_random_port();
+    }
+#endif
+#endif
+
+    /* Initialize handlers */
+    for (device = 0; device < BCMTR_MAX_OLTS; device++)
+    {
+        bcmolt_group_id group;
+        for (group = 0; group < BCMOLT_GROUP_ID__NUM_OF; group++)
+        {
+            int inst;
+            for (inst = 0; inst < BCMTR_MAX_INSTANCES; inst++)
+            {
+                conn_info[device].msg_handler[group][inst].app_cb = _bcmtr_dft_msg_handler;
+            }
+        }
+    }
+
+    bcmos_mutex_create(&conn_lock, 0, NULL);
+
+    return err;
+}
+
+/** Release resources used by transport library.
+ * \returns BCM_ERR_OK or error code
+ */
+bcmos_errno bcmtr_exit(void)
+{
+    int i;
+
+    for (i = 0; i < BCMTR_MAX_OLTS; i++)
+        bcmtr_disconnect(i);
+
+    bcmos_mutex_destroy(&conn_lock);
+
+    return BCM_ERR_OK;
+}
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_transport_cli.c b/bcm68620_release/release/host_driver/transport/bcmtr_transport_cli.c
new file mode 100644
index 0000000..e6d007a
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_transport_cli.c
@@ -0,0 +1,307 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#include <bcmos_system.h>
+#include <bcmcli.h>
+#include <bcmtr_interface.h>
+#include <bcmtr_internal.h>
+#include <bcmtr_transport_cli.h>
+#include <bcm_api_cli_helpers.h>
+#ifdef BCMTR_PERFTEST
+#include <test/bcmtr_perftest.h>
+#endif
+
+#ifdef BCMTR_PLUGIN_TASK_FORWARDER
+extern uint32_t raw_tx_drop_count;
+#endif
+
+/*
+ * CLI support
+ */
+static bcmcli_entry *trcli_dir;
+
+#ifdef BCMTR_UDP_SUPPORT
+
+/* Display/set host IP+port
+   BCMCLI_MAKE_PARM("ip_addr", "IP address", BCMCLI_PARM_IP, BCMCLI_PARM_FLAG_OPTIONAL),
+   BCMCLI_MAKE_PARM_RANGE("udp_port", "UDP port", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_OPTIONAL,
+   */
+static bcmos_errno _bcmtr_cli_host_udp(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    if (!nparms)
+    {
+        bcmcli_session_print(session, "Host Address:port = %d.%d.%d.%d:%d\n",
+            (bcmtr_host_ip >> 24) & 0xff, (bcmtr_host_ip >> 16) & 0xff,
+            (bcmtr_host_ip >> 8) & 0xff, bcmtr_host_ip & 0xff, bcmtr_host_udp_port);
+    }
+    else
+    {
+        if (bcmcli_parm_is_set(session, &parm[0]))
+        {
+            bcmtr_host_ip = parm[0].value.number;
+        }
+        if (bcmcli_parm_is_set(session, &parm[1]))
+        {
+            bcmtr_host_udp_port = parm[1].value.number;
+        }
+    }
+
+    return BCM_ERR_OK;
+}
+
+/* Display/Set OLT IP+port
+   BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+   BCMCLI_MAKE_PARM("ip_addr", "IP address", BCMCLI_PARM_IP, BCMCLI_PARM_FLAG_OPTIONAL),
+   BCMCLI_MAKE_PARM_RANGE("udp_port", "UDP port", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_OPTIONAL,
+   */
+static bcmos_errno _bcmtr_cli_olt_udp(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    bcmolt_devid device = (bcmolt_devid)parm[0].value.number;
+
+    if (nparms == 1)
+    {
+        bcmcli_session_print(session, "OLT[%d] Address:port = %d.%d.%d.%d:%d\n",
+            device,
+            (bcmtr_olt_ip[device] >> 24) & 0xff, (bcmtr_olt_ip[device] >> 16) & 0xff,
+            (bcmtr_olt_ip[device] >> 8) & 0xff, bcmtr_olt_ip[device] & 0xff,
+            bcmtr_olt_udp_port[device]);
+    }
+    else
+    {
+        if (bcmcli_parm_is_set(session, &parm[1]))
+        {
+            bcmtr_olt_ip[device] = parm[1].value.number;
+        }
+        if (bcmcli_parm_is_set(session, &parm[2]))
+        {
+            bcmtr_olt_udp_port[device] = parm[2].value.number;
+        }
+    }
+
+    return BCM_ERR_OK;
+}
+
+#endif /* #ifdef BCMTR_UDP_SUPPORT */
+
+/* Connect
+   BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+   */
+static bcmos_errno _bcmtr_cli_connect(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    bcmolt_devid device = (bcmolt_devid)parm[0].value.number;
+    return bcmtr_connect(device);
+}
+
+/* Disconnect
+   BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+   */
+static bcmos_errno _bcmtr_cli_disconnect(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    bcmolt_devid device = (bcmolt_devid)parm[0].value.number;
+    return bcmtr_disconnect(device);
+}
+
+/* Reconnect
+   BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+   */
+static bcmos_errno _bcmtr_cli_reconnect(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    bcmolt_devid device = (bcmolt_devid)parm[0].value.number;
+    return bcmtr_driver_reconnect(device);
+}
+
+/* Stat
+   BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+   */
+static bcmos_errno _bcmtr_cli_stat(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    bcmolt_devid device = (bcmolt_devid)parm[0].value.number;
+    bcmtr_stat stat;
+    bcmos_errno err;
+#ifdef BCMTR_PLUGIN_TASK_FORWARDER
+    static uint32_t prev_raw_tx_drop_count;
+    uint32_t new_raw_tx_drop_count = raw_tx_drop_count;
+#endif
+
+    err = bcmtr_stat_get(device, &stat);
+    if (err)
+        return err;
+
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_sent", stat.msg_sent);
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_resp_received", stat.msg_resp_received);
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_req_auto_received", stat.msg_req_auto_received);
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_req_timeout", stat.msg_req_timeout);
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_reass_timeout", stat.msg_reass_timeout);
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_no_req", stat.msg_no_req);
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_no_mem", stat.msg_no_mem);
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_comm_err", stat.msg_comm_err);
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_ready_timeout", stat.msg_ready_timeout);
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_too_many_req", stat.msg_too_many_req);
+    bcmcli_session_print(session, "%-24s : %d\n", "msg_too_many_auto", stat.msg_too_many_auto);
+    bcmcli_session_print(session, "%-24s : %d\n", "frag_received", stat.frag_received);
+    bcmcli_session_print(session, "%-24s : %d\n", "frag_invalid", stat.frag_invalid);
+    bcmcli_session_print(session, "%-24s : %d\n", "unpack_errors", stat.unpack_errors);
+    bcmcli_session_print(session, "%-24s : %d\n", "pack_errors", stat.pack_errors);
+    bcmcli_session_print(session, "%-24s : %d\n", "no_rx_handler", stat.no_rx_handler);
+    bcmcli_session_print(session, "%-24s : %d\n", "not_connected", stat.not_connected);
+#ifdef BCMTR_PLUGIN_TASK_FORWARDER
+    bcmcli_session_print(session, "%-24s : %d\n", "raw_tx_drop", new_raw_tx_drop_count - prev_raw_tx_drop_count);
+    prev_raw_tx_drop_count = new_raw_tx_drop_count;
+#endif
+    return BCM_ERR_OK;
+}
+
+/* Register
+   BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+   */
+static bcmos_errno _bcmtr_cli_register_info(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+    bcmolt_devid device = (bcmolt_devid)parm[0].value.number;
+    bcmtr_handler_parm p =
+    {
+        .instance = (uint8_t)parm[1].value.number,
+        .group = (bcmolt_mgt_group)parm[2].value.number,
+    };
+
+    if (p.instance >= BCMTR_MAX_INSTANCES)
+    {
+        return BCM_ERR_PARM;
+    }
+
+    for (p.object = BCMOLT_OBJ_ID_DEVICE; p.object < BCMOLT_OBJ_ID__NUM_OF; p.object++)
+    {
+        bcmolt_group_id group_id;
+        const char *obj_name, *obj_descr;
+        const char *sub_name, *sub_descr;
+
+        if (api_cli_object_name(p.object, &obj_name, &obj_descr) != BCM_ERR_OK)
+            continue;
+
+        for (p.subgroup = 0; bcmtr_msg_handler_register_get(device, &p) == BCM_ERR_OK; p.subgroup++)
+        {
+            bcmolt_group_id_combine(p.object, p.group, p.subgroup, &group_id);
+            api_cli_object_subgroup_name(p.object, p.group, p.subgroup, &sub_name, &sub_descr);
+
+            bcmcli_session_print(session, "%s - %s : msg_id=%u ", obj_name, sub_name, group_id);
+            if (p.app_cb)
+            {
+                bcmcli_session_print(session, "module:%d func:%p\n", p.module, p.app_cb);
+            }
+            else
+            {
+                bcmcli_session_print(session, "NONE\n");
+            }
+        }
+    }
+    return BCM_ERR_OK;
+}
+
+
+bcmos_errno bcmtr_cli_init(void)
+{
+    if ((trcli_dir=bcmcli_dir_find(NULL, "transport")) != NULL)
+        return BCM_ERR_ALREADY;
+    trcli_dir = bcmcli_dir_add(NULL, "transport", "Maple Transport", BCMCLI_ACCESS_GUEST, NULL);
+    if (!trcli_dir)
+    {
+        printf("Can't create transport directory\n");
+        return BCM_ERR_INTERNAL;
+    }
+
+#ifdef BCMTR_UDP_SUPPORT
+
+    BCMCLI_MAKE_CMD(trcli_dir, "host_udp", "Host IP and UDP port parameters", _bcmtr_cli_host_udp,
+        BCMCLI_MAKE_PARM("ip_addr", "IP address", BCMCLI_PARM_IP, BCMCLI_PARM_FLAG_OPTIONAL),
+        BCMCLI_MAKE_PARM_RANGE("udp_port", "UDP port", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_OPTIONAL,
+        0, 0xffff));
+    BCMCLI_MAKE_CMD(trcli_dir, "olt_udp", "OLT IP and UDP port parameters", _bcmtr_cli_olt_udp,
+        BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+        0, BCMTR_MAX_OLTS-1),
+        BCMCLI_MAKE_PARM("ip_addr", "IP address", BCMCLI_PARM_IP, BCMCLI_PARM_FLAG_OPTIONAL),
+        BCMCLI_MAKE_PARM_RANGE("udp_port", "UDP port", BCMCLI_PARM_NUMBER, BCMCLI_PARM_FLAG_OPTIONAL,
+        0, 0xffff));
+#endif
+
+    BCMCLI_MAKE_CMD(trcli_dir, "connect", "Connect", _bcmtr_cli_connect,
+        BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+        0, BCMTR_MAX_OLTS-1));
+
+    BCMCLI_MAKE_CMD(trcli_dir, "disconnect", "Disconnect", _bcmtr_cli_disconnect,
+        BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+        0, BCMTR_MAX_OLTS-1));
+
+    BCMCLI_MAKE_CMD(trcli_dir, "reconnect", "Reconnect", _bcmtr_cli_reconnect,
+        BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+        0, BCMTR_MAX_OLTS-1));
+
+    BCMCLI_MAKE_CMD(trcli_dir, "stat", "Transport statistics", _bcmtr_cli_stat,
+        BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+        0, BCMTR_MAX_OLTS-1));
+
+    {
+        static bcmcli_enum_val groups[] =
+        {
+#ifdef BCM_SUBSYSTEM_HOST
+            { .name = "auto", .val = BCMOLT_MGT_GROUP_CFG },
+            { .name = "proxy_rx", .val = BCMOLT_MGT_GROUP_PROXY_RX },
+#else
+            { .name = "cfg", .val = BCMOLT_MGT_GROUP_CFG },
+            { .name = "stat", .val = BCMOLT_MGT_GROUP_STAT },
+            { .name = "stat_cfg", .val = BCMOLT_MGT_GROUP_STAT_CFG },
+            { .name = "oper", .val = BCMOLT_MGT_GROUP_OPER },
+            { .name = "auto_cfg", .val = BCMOLT_MGT_GROUP_AUTO_CFG },
+            { .name = "proxy", .val = BCMOLT_MGT_GROUP_PROXY },
+#endif
+            { .name = NULL }
+
+        };
+        BCMCLI_MAKE_CMD(trcli_dir, "registration", "Transport RX registration info", _bcmtr_cli_register_info,
+            BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0,
+            0, BCMTR_MAX_OLTS-1),
+            BCMCLI_MAKE_PARM_RANGE("pon_ni", "PON NI", BCMCLI_PARM_DECIMAL, 0,
+            0, BCMTR_MAX_INSTANCES-1),
+            BCMCLI_MAKE_PARM_ENUM("group", "Management group", groups, 0) );
+    }
+
+    /* Performance testing directory */
+#ifdef BCMTR_PERFTEST
+    bcmtr_test_init();
+#endif
+
+    return BCM_ERR_OK;
+}
+
+void bcmtr_cli_exit(void)
+{
+    if (trcli_dir)
+    {
+        bcmcli_token_destroy(trcli_dir);
+        trcli_dir = NULL;
+    }
+}
diff --git a/bcm68620_release/release/host_driver/transport/bcmtr_transport_cli.h b/bcm68620_release/release/host_driver/transport/bcmtr_transport_cli.h
new file mode 100644
index 0000000..3ebffaa
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/bcmtr_transport_cli.h
@@ -0,0 +1,41 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#ifndef BCMTR_TRANSPORT_CLI_H_
+#define BCMTR_TRANSPORT_CLI_H_
+
+#include <bcmos_system.h>
+
+#ifdef ENABLE_CLI
+bcmos_errno bcmtr_cli_init(void);
+void bcmtr_cli_exit(void);
+#endif
+
+#endif
+
diff --git a/bcm68620_release/release/host_driver/transport/inband/Makefile b/bcm68620_release/release/host_driver/transport/inband/Makefile
new file mode 100644
index 0000000..31c6c00
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/inband/Makefile
@@ -0,0 +1,10 @@
+# Inband Transport
+#
+
+MOD_NAME = inband_driver
+MOD_TYPE = lib
+MOD_DEFS = -DIN_BAND
+
+USE_LINT = yes
+
+srcs = bcmtr_inband.c
diff --git a/bcm68620_release/release/host_driver/transport/inband/bcmtr_inband.c b/bcm68620_release/release/host_driver/transport/inband/bcmtr_inband.c
new file mode 100644
index 0000000..bab77ca
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/inband/bcmtr_inband.c
@@ -0,0 +1,276 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+/*
+ * bcmtr_inband.c - In-band transport driver
+ */
+#include <bcmos_system.h>
+#include <bcmtr_inband.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+typedef struct device_info
+{
+    bcmos_ipv4_address remote_ip;
+    uint16_t remote_port;
+    uint8_t channel;
+    int tr_udp_sock;
+} device_info;
+
+static device_info dev_info[BCMTR_MAX_OLTS];
+
+#define DEBUG_ONLY 0
+
+/** Initialize in-band transport driver
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_ib_init(void)
+{
+    return BCM_ERR_OK;
+}
+
+/** Cleanup in-band transport driver
+ * \returns: 0 in case of success or error code < 0
+ */
+void bcmtr_ib_exit(void)
+{
+    int i;
+
+    for (i=0; i<BCMTR_MAX_OLTS; i++)
+    {
+        bcmtr_ib_disconnect(i);
+    }
+}
+
+/* Receive packet */
+static bcmos_errno _bcmtr_ib_rx(uint32_t device, int s, bcmos_buf **buf, uint8_t *ch)
+{
+    struct sockaddr_in sa;
+#if 0
+    struct iovec iov = {.iov_len = BCMTR_MAX_MTU_SIZE};
+    struct msghdr msg = {
+        .msg_iov = &iov, .msg_iovlen = 1,
+        .msg_name = &sa, .msg_namelen = sizeof(sa)
+    };
+#endif
+    bcmos_buf *b;
+    ssize_t len;
+
+    b = bcmos_buf_alloc(BCMTR_MAX_MTU_SIZE);
+    if (!b)
+    {
+        bcmos_printf("%s: Failed to allocate buffer\n", __FUNCTION__);
+        return BCM_ERR_NOMEM;
+    }
+#if 0
+    iov.iov_base = bcmos_buf_data(b);
+#endif
+    memset(&sa, 0, sizeof(sa));
+    len = recv(s, bcmos_buf_data(b), BCMTR_MAX_MTU_SIZE, 0);
+    if (len <= 0)
+    {
+        bcmos_printf("%s: recvmsg() --> %d\n", __FUNCTION__, len);
+        bcmos_buf_free(b);
+        return BCM_ERR_COMM_FAIL;
+    }
+
+    *ch = dev_info[device].channel;
+    dev_info[device].channel = 0;
+    bcmos_buf_length_set(b, (int)len);
+    *buf = b;
+
+#if DEBUG_ONLY
+    bcmos_printf("\nReceived to %d bytes from channel %d/%d port", len, *ch, sa.sin_port);
+
+    {
+        int ii = 0;
+        unsigned char *c = bcmos_buf_data(b);
+        int size = (int)len;
+
+        for (ii=0; ii<size; ii++)
+        {
+            if (0==(ii%16)) bcmos_printf("\n");
+            if (0==(ii%8)) bcmos_printf("  ");
+            bcmos_printf(" %02x", *c);
+            c++;
+        }
+        bcmos_printf("\n");
+    }
+#endif
+
+    return BCM_ERR_OK;
+}
+
+/** Connect to maple device */
+bcmos_errno bcmtr_ib_connect(uint8_t device, bcmos_ipv4_address ip_address, uint16_t udp_port)
+{
+    int r = 0;
+    int s = 0;
+    struct sockaddr_in sa;
+
+    /*Check for valid device*/
+    BUG_ON((unsigned)device >= BCMTR_MAX_OLTS);
+
+    if (dev_info[device].tr_udp_sock)
+        return BCM_ERR_ALREADY;
+
+    dev_info[device].remote_ip = ip_address;
+    dev_info[device].remote_port = udp_port;
+
+    /*check that ip address and port have been initialized*/
+    if(dev_info[device].remote_port == 0  || dev_info[device].remote_ip.u32 == 0)
+    {
+        bcmos_printf("%s: IP address and port not initialized. Error %d\n", __FUNCTION__, r);
+        return BCM_ERR_RANGE;
+    }
+
+    /* Create socket */
+    s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (!s)
+    {
+        bcmos_printf("%s: Failed to create socket. Error %d\n", __FUNCTION__, s);
+        return BCM_ERR_COMM_FAIL;
+    }
+
+    /* Connect to remote */
+    memset(&sa, 0, sizeof(sa));
+    sa.sin_family = AF_INET;
+    sa.sin_port = (in_port_t)htons(dev_info[device].remote_port);
+    sa.sin_addr.s_addr = (in_addr_t)htonl(dev_info[device].remote_ip.u32);
+    r = connect(s, (struct sockaddr*)&sa, sizeof(sa));
+    if (r)
+    {
+        bcmos_printf("%s: Failed to connect socket. Error %d\n", __FUNCTION__, r);
+        close(s);
+        return BCM_ERR_COMM_FAIL;
+    }
+
+    {
+        socklen_t slen;
+        if (getsockname(s, (struct sockaddr *)&sa, &slen) < 0)
+        {
+        bcmos_printf("%s: Connected socket invalid. Error %d\n", __FUNCTION__, r);
+        close(s);
+        return BCM_ERR_COMM_FAIL;
+        }
+
+        bcmos_printf("%s: device %d: socket (port %d) connected to %d.%d.%d.%d:%d\n",
+                     __FUNCTION__, device, ntohs((uint16_t)sa.sin_port),
+                     (int)(dev_info[device].remote_ip.u32 >> 24), (int)((dev_info[device].remote_ip.u32 >> 16) & 0xff),
+                     (int)((dev_info[device].remote_ip.u32 >> 8) & 0xff), (int)(dev_info[device].remote_ip.u32 & 0xff),
+                     (int)dev_info[device].remote_port);
+    }
+
+    dev_info[device].tr_udp_sock = s;
+
+    return BCM_ERR_OK;
+}
+
+/** Disconnect. All buffers are released
+ * \param[in]   device          Maple device index
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_ib_disconnect(uint8_t device)
+{
+    BUG_ON((unsigned)device >= BCMTR_MAX_OLTS);
+
+    bcmos_printf("%s: disconnecting %d..", __FUNCTION__, device);
+    if (dev_info[device].tr_udp_sock)
+    {
+        int s = dev_info[device].tr_udp_sock;
+        dev_info[device].tr_udp_sock = 0;
+        close(s);
+    }
+    bcmos_printf("done\n");
+    return BCM_ERR_OK;
+}
+
+/** Send packet to device via in-band interface
+ * \param[in]   device  Mapole device index
+ * \param[in]   channel Logical channel
+ * \param[in]   buf     Buffer to be transmitted
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_ib_send(uint8_t device, uint8_t channel, bcmos_buf *buf)
+{
+    int buflen = bcmos_buf_length(buf);
+    struct iovec iov = { .iov_base = bcmos_buf_data(buf), .iov_len = buflen };
+    struct msghdr msg = {
+        .msg_iov = &iov, .msg_iovlen = 1, .msg_flags=MSG_DONTWAIT,
+        .msg_name = NULL, .msg_namelen = 0
+    };
+    ssize_t len;
+
+    dev_info[device].channel = channel;
+
+    len = sendmsg(dev_info[device].tr_udp_sock, &msg, 0);
+    if ((int)len < buflen)
+    {
+        bcmos_printf("%s: sendmsg(%u) --> %d\n", __FUNCTION__, buflen, len);
+        bcmos_buf_free(buf);
+        return BCM_ERR_NOT_CONNECTED;
+    }
+
+#if 1
+    {
+        int ii = 0;
+        unsigned char *c = bcmos_buf_data(buf);
+        struct sockaddr_in sa;
+        socklen_t slen;
+
+        for (ii=0; ii<(int)len; ii++)
+        {
+            if (0==(ii%16)) bcmos_printf("\n");
+            if (0==(ii%8)) bcmos_printf("  ");
+            bcmos_printf(" %02x", *c);
+            c++;
+        }
+
+        if (getsockname(dev_info[device].tr_udp_sock, (struct sockaddr *)&sa, &slen) < 0)
+        {
+            bcmos_printf("%s: Connected socket invalid\n", __FUNCTION__);
+            return BCM_ERR_COMM_FAIL;
+        }
+        bcmos_printf("\nSent %d bytes to channel %d from port %d\t", len, channel, sa.sin_port);
+    }
+#endif
+
+    return BCM_ERR_OK;
+}
+
+/* Receive packet from device */
+bcmos_errno bcmtr_ib_receive(uint32_t device, uint8_t *channel, bcmos_buf **buf)
+{
+    if (device >= BCMTR_MAX_OLTS || !  dev_info[device].tr_udp_sock)
+    {
+        return BCM_ERR_PARM;
+    }
+
+    return _bcmtr_ib_rx(device, dev_info[device].tr_udp_sock, buf, channel);
+}
diff --git a/bcm68620_release/release/host_driver/transport/inband/bcmtr_inband.h b/bcm68620_release/release/host_driver/transport/inband/bcmtr_inband.h
new file mode 100644
index 0000000..10f3d7d
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/inband/bcmtr_inband.h
@@ -0,0 +1,74 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+/*
+ * bcmtr_inband.h - Inband host-maple communication interface
+ */
+
+#ifndef _BCMTR_INBAND_H_
+#define _BCMTR_INBAND_H_
+
+
+/** Initialize in-band transport driver
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_ib_init(void);
+
+
+/** Cleanup in-band transport driver
+ * \returns: 0 in case of success or error code < 0
+ */
+void bcmtr_ib_exit(void);
+
+/** Connect
+ * \param[in]  device          Maple device index
+ * \param[in]  ip_address      Maple IP address
+ * \param[in]  udp_port        Maple UDP port
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_ib_connect(uint8_t device, bcmos_ipv4_address ip_address, uint16_t udp_port);
+
+/** Disconnect. All buffers are released
+ * \param[in]   device          Maple device index
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_ib_disconnect(uint8_t device);
+
+/** Send packet to device via in-band interface
+ * \param[in]   device  Mapole device index
+ * \param[in]   channel Logical channel
+ * \param[in]   buf     Buffer to be transmitted
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_ib_send(uint8_t device, uint8_t channel, bcmos_buf *buf);
+
+/* Receive packet from device */
+bcmos_errno bcmtr_ib_receive(uint32_t device, uint8_t *channel, bcmos_buf **buf);
+
+#endif /* _BCMTR_INBAND_H_ */
diff --git a/bcm68620_release/release/host_driver/transport/mux/Makefile b/bcm68620_release/release/host_driver/transport/mux/Makefile
new file mode 100644
index 0000000..8a45a97
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/mux/Makefile
@@ -0,0 +1,35 @@
+# PCIe transport
+# Consists of 2 components
+# - low-level PCIe driver (send/rx functions)
+# - multiplexer
+#
+MOD_NAME = trmux
+MOD_DEPS = transport model pcie pcie_mod ll_pcie fld pcie_sw_queue
+
+ifeq ("$(RELEASE_BUILD)", "y")
+    MOD_DEPS += common_api
+else
+    MOD_DEPS += api
+endif
+
+ifeq ("$(RAW_TRANSPORT_VIA_UDP)", "y")
+MOD_DEPS += trpcie
+endif
+
+ifeq ("$(OS_KERNEL)", "linux")
+MOD_TYPE = linux_lib
+else
+MOD_TYPE = lib
+endif
+
+# If called by linux kernel builder - add include paths manually.
+# It is not elegant, but we'll not have many linux modules
+ifneq ("$(KBUILD_SRC)", "")
+	-include $(OUT_DIR_BASE)/Makefile.config.$(MOD_NAME)
+endif
+
+srcs = bcmolt_tr_mux.c 
+
+USE_LINT = yes
+
+
diff --git a/bcm68620_release/release/host_driver/transport/mux/bcmolt_tr_mux.c b/bcm68620_release/release/host_driver/transport/mux/bcmolt_tr_mux.c
new file mode 100644
index 0000000..b73e650
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/mux/bcmolt_tr_mux.c
@@ -0,0 +1,1092 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+/*
+ * bcmtr_mux.c
+ *
+ * Transport Multiplexer
+ * - PCIe/in-band channel multiplexer
+ * - locally/remotely - terminated message multiplexer
+ * - autonomous messages de-multiplexer
+ */
+#include <bcmos_system.h>
+#include <bcmolt_msg.h>
+#include <bcmolt_buf.h>
+#include <bcmolt_msg_pack.h>
+#include <bcmtr_header.h>
+#include <bcmolt_tr_mux.h>
+#ifdef IN_BAND
+#include <bcmtr_inband.h>
+#else
+#include <bcmtr_pcie.h>
+#include <bcmolt_llpcie.h>
+#include <bcmolt_fld.h>
+#include <bcm_fld_common.h>
+#include <bcmtr_pcie_sw_queue.h>
+#endif
+#include <bcmolt_api.h>
+#include <bcmolt_model_types.h>
+#include <bcmolt_model_ids.h>
+
+#define BCMTRMUX_MAX_TX_DELAY   500     /* max transmit delay (us) */
+#define BCMTRMUX_MAX_RX_DELAY   5000    /* max receive delay (us) */
+
+#ifdef __KERNEL__
+#define BCMTRMUX_LOG printk
+#else
+#define BCMTRMUX_LOG BCMOS_TRACE_INFO
+#endif
+
+/* Channel registration info */
+typedef struct
+{
+    f_bcmtr_rx_handler rx;
+    void *data;
+} bcmtrmux_rx_info;
+
+typedef struct
+{
+    f_bcmtr_local_rx_handler rx;
+    void *data;
+} bcmtrmux_local_rx_info;
+
+/* Registration arrays */
+static bcmtrmux_rx_info bcmtrmux_rx_info_array[BCMTR_MAX_OLTS][BCMTRMUX_MAX_CHANNELS];
+static bcmtrmux_local_rx_info bcmtrmux_local_rx_info_array[BCMTR_MAX_OLTS];
+static bcmtrmux_channel bcmtrmux_auto_channels[BCMTR_MAX_OLTS][BCMTR_MAX_INSTANCES][BCMOLT_OBJ_ID__NUM_OF];
+static bcmtrmux_channel bcmtrmux_proxy_channels[BCMTR_MAX_OLTS][BCMTR_MAX_INSTANCES][BCMOLT_OBJ_ID__NUM_OF];
+static bcmos_bool bcmtr_mux_connected[BCMTR_MAX_OLTS];
+static bcmos_bool bcmtr_mux_terminated[BCMTR_MAX_OLTS];
+static bcmos_bool bcmtrmux_dev_ctrl_intercept[BCMTR_MAX_OLTS][BCMOLT_GROUP_ID__NUM_OF];
+
+#ifndef IN_BAND
+
+/* Number of registered high-priority channels */
+static bcmtrmux_channel num_urgent_channels[BCMTR_MAX_OLTS];
+
+/* Receive semaphore */
+static bcmos_sem bcmtrmux_rx_lock[BCMTR_MAX_OLTS];
+
+#endif
+
+/* Statistics */
+static bcmtrmux_stat bcmtrmux_stat_array[BCMTR_MAX_OLTS];
+
+/* Receive tasks */
+static bcmos_task bcmtrmux_rx_task[BCMTR_MAX_OLTS];
+
+/* Registration protection */
+static bcmos_fastlock bcmtrmux_lock;
+
+static void bcmtrmux_rx_auto_proxy(bcmolt_devid device, bcmos_buf *buf, bcmtrmux_channel channel, void *data);
+static void bcmtrmux_rx_local(bcmolt_devid device, bcmos_buf *buf, bcmtrmux_channel channel, void *data);
+
+static bcmtrmux_msg_filter_cb_t bcmtrmux_msg_filter_cb;
+
+/* discard packet for which there is no registration */
+static void bcmtrmux_rx_discard(bcmolt_devid device, bcmos_buf *buf, bcmtrmux_channel channel, void *data)
+{
+    uint32_t *counter = (uint32_t *)data;
+    ++(*counter);
+    bcmos_buf_free(buf);
+}
+
+/* discard unpacked message for which there is no registration */
+static void bcmtrmux_local_rx_discard(bcmolt_devid device, bcmolt_msg *msg, void *data)
+{
+    uint32_t *counter = (uint32_t *)data;
+    ++(*counter);
+    bcmolt_msg_free(msg);
+}
+
+/* Find free channel. returns channel id >= 0 or BCMTRMUX_MAX_CHANNELS if no free channels */
+static int bcmtrmux_channel_get_free(bcmolt_devid device)
+{
+    int i;
+    /* Start from BCMTRMUX_CHANNEL_FIRST_FREE. Channel 0 is reserved for indications/proxy , Channel 1 is for Keep Alive*/
+    for (i=BCMTRMUX_CHANNEL_FIRST_FREE; i<BCMTRMUX_FIRST_URGENT_CHANNEL; i++)
+    {
+        if (bcmtrmux_rx_info_array[device][i].rx == bcmtrmux_rx_discard)
+            break;
+    }
+    return i;
+}
+
+#ifndef IN_BAND
+/* PCIe Receive handler */
+static int _bcmtrmux_pcie_rx_handler(long device)
+{
+    bcmos_errno rc;
+    bcmos_buf *buf;
+    uint32_t nbuf[BCMTR_PCIE_PRTY__NUM_OF] = {};
+    uint8_t ch;
+
+    BUG_ON(device >= BCMTR_MAX_OLTS);
+
+    BCMTRMUX_LOG("rx_task(%ld) - started\n", device);
+
+    /* Enable rx interrupt */
+
+    while (1)
+    {
+        bcmtr_pcie_rxint_enable(device);
+        bcmos_sem_wait(&bcmtrmux_rx_lock[device], BCMTRMUX_MAX_RX_DELAY);
+
+        if (!bcmtr_mux_connected[device])
+        {
+            break;
+        }
+        /* Wait for receive */
+        do
+        {
+            buf = NULL;
+            rc = bcmtr_swq_receive(device, BCMTR_PCIE_PRTY_NORMAL, &ch, &buf);
+            if (rc)
+            {
+                /* If DMA is not shared with urgent service, poll RXQ here,
+                 * don't wait for interrupt
+                 */
+                if (rc == BCM_ERR_QUEUE_EMPTY && !num_urgent_channels[device])
+                {
+                    bcmtr_pcie_rxint_disable(device);
+                    bcmtr_swq_rx_poll(device, nbuf);
+                    bcmtrmux_stat_array[device].rx_poll_normal += nbuf[BCMTR_PCIE_PRTY_NORMAL];
+                }
+                /* Wait for interrupt or semaphore timeout */
+                break;
+            }
+            bcmtrmux_rx_from_line((bcmolt_devid)device, (bcmtrmux_channel)ch, buf);
+        } while (bcmtr_mux_connected[device]);
+    }
+    BCMTRMUX_LOG("rx_task(%ld) - terminated\n", device);
+    bcmtr_mux_terminated[device] = BCMOS_TRUE;
+
+    return 0;
+}
+
+static void bcmtrmux_rx_irq(bcmolt_devid device)
+{
+    bcmos_buf *buf;
+    uint8_t ch;
+    uint32_t nbuf[BCMTR_PCIE_PRTY__NUM_OF] = {};
+
+    bcmtr_pcie_rxint_disable(device);
+    bcmtr_pcie_rxint_clear(device);
+    bcmtr_swq_rx_poll(device, nbuf);
+
+    /* Got some packets. Deliver high priority from interrupt level */
+    if (nbuf[BCMTR_PCIE_PRTY_URGENT])
+    {
+        bcmtrmux_stat_array[device].rx_poll_urgent += nbuf[BCMTR_PCIE_PRTY_URGENT];
+        while (bcmtr_swq_receive(device, BCMTR_PCIE_PRTY_URGENT, &ch, &buf) == BCM_ERR_OK)
+        {
+            bcmtrmux_rx_from_line(device, ch, buf);
+        }
+    }
+
+    /* Check normal priority */
+    if (nbuf[BCMTR_PCIE_PRTY_NORMAL])
+    {
+        bcmtrmux_stat_array[device].rx_poll_normal += nbuf[BCMTR_PCIE_PRTY_NORMAL];
+        bcmos_sem_post(&bcmtrmux_rx_lock[device]);
+    }
+
+    /* Enable rx interrupt */
+    bcmtr_pcie_rxint_enable(device);
+
+    return;
+}
+
+/* Tx confirmation interrupt handler.
+ * Used only if there are urgent channels
+ */
+static void bcmtrmux_tx_irq(bcmolt_devid device)
+{
+    /* Disable and clear transmit completion interrupts */
+    bcmtr_pcie_txint_disable(device);
+    bcmtr_pcie_txint_clear(device);
+    /* Submit buffers pending in sw queue to the h/w queue */
+    bcmtr_swq_tx_submit(device);
+    /* Re-enable tx completion interrupt */
+    bcmtr_pcie_txint_enable(device);
+    return;
+}
+
+/* Init PCIE transport */
+static bcmos_errno bcmtrmux_pcie_init(bcmolt_devid device, uint32_t txq_size, uint32_t rxq_size)
+{
+    uint32_t pcie_cookie[BCMOS_ROUND_UP(PCIE_OPAQUE_DATA_SIZE, sizeof(uint32_t))/sizeof(uint32_t)];
+    bcmtr_pcie_pre_connect_cfg cfg;
+    bcm_ll_dev_info ll_info;
+    int niter = 0;
+    bcmos_errno err;
+    bcmos_bool status;
+
+#ifndef SIMULATION_BUILD
+    err = bcm_ll_pcie_query(device, &ll_info);
+    if (err)
+    {
+        BCMOS_TRACE_RETURN(err, "bcm_ll_pcie_query() failed\n");
+    }
+#endif
+
+    BCMTRMUX_LOG("Waiting for BCM68620 application\n");
+    bcm_fld_set_rings_size(device, txq_size, rxq_size);
+    status = bcm_fld_test_device_bootrecord_flag(device);
+
+    /* Wait for embedded handshake. BCMTR_PCIE_START_TIMEOUT is in ms */
+    for (niter = 0;
+         niter < BCMTR_PCIE_START_TIMEOUT / 10 && !(status = bcm_fld_test_device_bootrecord_flag(device));
+         niter++)
+    {
+        bcmos_usleep(10000);
+    }
+    if (!status)
+    {
+        BCMOS_TRACE_RETURN(BCM_ERR_IO, "BCM68620 application timeout\n");
+    }
+
+    err = bcm_fld_get_device_bootrecord(device, pcie_cookie);
+    if (err != BCM_ERR_OK)
+    {
+        BCMOS_TRACE_RETURN(err, "bcm_fld_get_device_bootrecord() failed\n");
+    }
+
+    /* set host prm bit - indicate host ready to send/receive DMA */
+    bcm_fld_clear_device_bootrecord_flag(device);
+
+    cfg.txq_size = txq_size;             /* Transmit queue size */
+    cfg.rxq_size = rxq_size;             /* Receive queue size */
+    cfg.max_mtu = BCMTR_MAX_MTU_SIZE;    /* Max MTU size */
+    cfg.pcie_reg_base = ll_info.soc_regs_base;
+    cfg.ddr_win_base = ll_info.soc_ddr_base;
+    cfg.rx_irq = ll_info.irq;
+
+    err = bcmtr_pcie_pre_connect(device, &cfg, (bcmtr_pcie_opaque_data *)pcie_cookie);
+    if (err)
+    {
+        BCMOS_TRACE_RETURN(err, "bcmtr_pcie_pre_connect() failed\n");
+    }
+
+    err = bcmtr_pcie_connect(device,(bcmtr_pcie_opaque_data *)pcie_cookie);
+    if (err)
+    {
+        BCMOS_TRACE_RETURN(err, "bcmtr_pcie_connect() failed\n");
+    }
+
+    bcm_fld_set_host_bootrecord_flag(device);
+
+    /* Wait for embedded handshake. BCMTR_PCIE_CONNECT_TIMEOUT is in ms */
+    for (niter = 0;
+         niter < BCMTR_PCIE_CONNECT_TIMEOUT / 10 && (status = bcm_fld_test_host_bootrecord_flag(device));
+         niter++)
+    {
+        bcmos_usleep(10000);
+    }
+    if (status)
+    {
+        BCMOS_TRACE_RETURN(BCM_ERR_IO, "BCM68620 connect timeout\n");
+    }
+
+    BCMTRMUX_LOG("PCI transport: initialized\n");
+
+    return BCM_ERR_OK;
+}
+
+#else /* #ifndef IN_BAND */
+
+/* IN-BAND receive handler */
+static int _bcmtrmux_ib_rx_handler(long device)
+{
+    bcmos_errno rc;
+    bcmos_buf *buf;
+    uint8_t ch;
+
+    BUG_ON(device >= BCMTR_MAX_OLTS);
+
+    BCMTRMUX_LOG("rx_task(%ld) - started\n", device);
+
+    while (bcmtr_mux_connected[device])
+    {
+        /* Wait for receive */
+        buf = NULL;
+        rc = bcmtr_ib_receive(device, &ch, &buf);
+        if (rc == BCM_ERR_OK)
+        {
+            bcmtrmux_rx_from_line((bcmolt_devid)device, (bcmtrmux_channel)ch, buf);
+        }
+    }
+
+    BCMTRMUX_LOG("rx_task(%ld) - terminated\n", device);
+    bcmtr_mux_terminated[device] = BCMOS_TRUE;
+
+    return 0;
+}
+
+static bcmos_errno bcmtrmux_ib_connect(bcmolt_devid device,  bcmos_ipv4_address ip_address, uint16_t udp_port)
+{
+    bcmos_errno rc;
+
+    rc = bcmtr_ib_connect((uint8_t)device, ip_address, udp_port);
+    if (rc)
+    {
+        BCMTRMUX_LOG("%s: Failed to connect. Error %d\n", __FUNCTION__, rc);
+        return rc;
+    }
+    return rc;
+}
+
+#endif /* #ifndef IN_BAND */
+
+
+/** Initialize mux service
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_init(bcmtrmux_msg_filter_cb_t msg_filter_cb)
+{
+    static bcmos_bool initialized = BCMOS_FALSE;
+    int i, j;
+    bcmos_errno rc;
+
+    bcmtrmux_msg_filter_cb = msg_filter_cb;
+
+    BCMTRMUX_LOG("Initialising transport MUX subsystem\n");
+    if (initialized)
+    {
+        return BCM_ERR_ALREADY;
+    }
+
+    for (i=0; i<BCMTR_MAX_OLTS; i++)
+    {
+        for (j=0; j<BCMTRMUX_MAX_CHANNELS; j++)
+        {
+            bcmtrmux_rx_info_array[i][j].rx = bcmtrmux_rx_discard;
+            bcmtrmux_rx_info_array[i][j].data = &bcmtrmux_stat_array[i].rx_disc_remote;
+        }
+        bcmtrmux_rx_info_array[i][BCMTRMUX_CHANNEL_AUTO_PROXY].rx = bcmtrmux_rx_auto_proxy;
+        bcmtrmux_rx_info_array[i][BCMTRMUX_CHANNEL_AUTO_PROXY].data = NULL;
+        bcmtrmux_rx_info_array[i][BCMTRMUX_CHANNEL_DEV_CONTROL].rx = bcmtrmux_rx_local;
+        bcmtrmux_rx_info_array[i][BCMTRMUX_CHANNEL_DEV_CONTROL].data = NULL;
+        for (j=0; j<BCMTR_MAX_INSTANCES; j++)
+        {
+            bcmolt_obj_id k;
+            for (k=0; k<BCMOLT_OBJ_ID__NUM_OF; k++)
+            {
+                bcmtrmux_auto_channels[i][j][k] = BCMTRMUX_MAX_CHANNELS;
+                bcmtrmux_proxy_channels[i][j][k] = BCMTRMUX_MAX_CHANNELS;
+            }
+        }
+        bcmtrmux_local_rx_info_array[i].rx = bcmtrmux_local_rx_discard;
+        bcmtrmux_local_rx_info_array[i].data = &bcmtrmux_stat_array[i].tx_disc_local;
+    }
+
+    bcmos_fastlock_init(&bcmtrmux_lock, 0);
+
+    /*Don't initialize at this time for User Space dev ctrl,
+      don't have enough information*/
+#ifndef IN_BAND
+    rc = bcmtr_pcie_init(BCMTR_MAX_OLTS);
+    if (rc)
+    {
+        BCMOS_TRACE_RETURN(rc, "bcmtr_pcie_init() failed\n");
+    }
+
+    rc = bcmtr_swq_init();
+    if (rc)
+    {
+        BCMOS_TRACE_RETURN(rc, "bcmtr_swq_init() failed\n");
+    }
+
+    /* Register rx callback in PCIe driver */
+    bcmtr_pcie_rx_irq_cblk_register(bcmtrmux_rx_irq);
+    bcmtr_pcie_tx_irq_cblk_register(bcmtrmux_tx_irq);
+#else
+
+    rc = bcmtr_ib_init();
+    if (rc)
+    {
+        BCMOS_TRACE_RETURN(rc, "bcmtr_ib_init() failed\n");
+    }
+
+#endif /* #ifndef IN_BAND */
+
+    BCMTRMUX_LOG("Transport MUX init done\n");
+
+    return rc;
+}
+
+/** Notify mux driver that low-level transport connection is ready
+ * \returns: 0 in case of success or error code < 0
+ */
+#ifdef IN_BAND
+bcmos_errno bcmtrmux_connect(bcmolt_devid device,  bcmos_ipv4_address ip_address, uint16_t udp_port)
+#else
+bcmos_errno bcmtrmux_connect(bcmolt_devid device, uint32_t txq_size, uint32_t rxq_size)
+#endif
+{
+    static char task_name[BCMTR_MAX_OLTS][16];
+    bcmos_task_parm taskp = {};
+    bcmos_errno rc;
+
+    if (bcmtr_mux_connected[device])
+    {
+        return BCM_ERR_ALREADY;
+    }
+
+    snprintf(task_name[device], sizeof(task_name[device]), "bcmtr_rx%d", device);
+    taskp.data = (long)device;
+    taskp.name = task_name[device];
+
+#ifdef IN_BAND
+    rc = bcmtrmux_ib_connect(device, ip_address, udp_port);
+    if (rc)
+    {
+        BCMTRMUX_LOG("%s: Failed to init inband device. Error %d\n", __FUNCTION__, rc);
+        return rc;
+    }
+    taskp.handler = _bcmtrmux_ib_rx_handler;
+#else
+    rc = bcmos_sem_create(&bcmtrmux_rx_lock[device], 0, 0, NULL);
+    if (rc)
+    {
+        BCMTRMUX_LOG("%s: Failed to create rx lock. Error %d\n", __FUNCTION__, rc);
+        return rc;
+    }
+
+    /* Initialize low-level PCIe transport */
+    rc = bcmtrmux_pcie_init(device, txq_size, rxq_size);
+    if (rc)
+    {
+        bcmos_sem_destroy(&bcmtrmux_rx_lock[device]);
+        BCMTRMUX_LOG("%s: Failed to init low-level PCIe transport. Error %d\n", __FUNCTION__, rc);
+        return rc;
+    }
+    taskp.handler = _bcmtrmux_pcie_rx_handler;
+
+    rc = bcmtr_swq_device_init(device);
+    if (rc)
+    {
+        bcmos_sem_destroy(&bcmtrmux_rx_lock[device]);
+        BCMTRMUX_LOG("%s: Failed to init pcie_swq. Error %d\n", __FUNCTION__, rc);
+        return rc;
+    }
+
+#endif
+    bcmtr_mux_connected[device] = BCMOS_TRUE;
+    bcmtr_mux_terminated[device] = BCMOS_FALSE;
+
+    rc = bcmos_task_create(&bcmtrmux_rx_task[device], &taskp);
+    if (rc)
+    {
+#ifndef IN_BAND
+        bcmos_sem_destroy(&bcmtrmux_rx_lock[device]);
+#endif
+        bcmtr_mux_connected[device] = BCMOS_FALSE;
+        BCMTRMUX_LOG("%s: Failed to create rx task. Error %d\n", __FUNCTION__, rc);
+        return rc;
+    }
+
+    return BCM_ERR_OK;
+}
+
+/** Notify mux driver that low-level transport connection is disconnected
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_disconnect(bcmolt_devid device)
+{
+    if (!bcmtr_mux_connected[device])
+    {
+        return BCM_ERR_ALREADY;
+    }
+    bcmtr_mux_connected[device] = BCMOS_FALSE;
+#ifdef IN_BAND
+    bcmtr_ib_disconnect((uint8_t)device);
+#else
+    bcmos_sem_post(&bcmtrmux_rx_lock[device]);
+#endif
+    while (!bcmtr_mux_terminated[device])
+    {
+        bcmos_usleep(10000);
+    }
+    bcmos_task_destroy(&bcmtrmux_rx_task[device]);
+#ifndef IN_BAND
+    bcmos_sem_destroy(&bcmtrmux_rx_lock[device]);
+    bcmtr_swq_device_exit(device);
+    bcmtr_pcie_disconnect((uint8_t)device);
+#endif
+    return BCM_ERR_OK;
+}
+
+/** Cleanup and exit
+ */
+void bcmtrmux_exit(void)
+{
+    int i;
+
+    BCMTRMUX_LOG("Cleaning up transport MUX subsystem\n");
+#ifndef IN_BAND
+    bcmtr_swq_exit();
+    bcmtr_pcie_rx_irq_cblk_unregister();
+    bcmtr_pcie_tx_irq_cblk_unregister();
+#endif
+    /* kill receive tasks */
+    for (i=0; i<BCMTR_MAX_OLTS; i++)
+    {
+        bcmtrmux_disconnect((bcmolt_devid)i);
+    }
+#ifdef IN_BAND
+    bcmtr_ib_exit();
+#else
+    bcmtr_pcie_exit();
+#endif
+    BCMTRMUX_LOG("Transport MUX cleanup done\n");
+}
+
+/** Register PCIe channel owner */
+bcmos_errno bcmtrmux_channel_register(bcmolt_devid device, bcmtrmux_channel *channel,
+    f_bcmtr_rx_handler rx, void *data)
+{
+    bcmtrmux_channel ch;
+    long flags;
+
+    if ((unsigned)device >= BCMTR_MAX_OLTS || !channel || !rx)
+    {
+        return BCM_ERR_PARM;
+    }
+    ch = *channel;
+
+    flags = bcmos_fastlock_lock(&bcmtrmux_lock);
+
+    if (ch == BCMTRMUX_CHANNEL_AUTO_ASSIGN)
+    {
+        /* Auto-assign free channel */
+        ch = (bcmtrmux_channel)bcmtrmux_channel_get_free(device);
+        if (ch >= BCMTRMUX_MAX_CHANNELS)
+        {
+            bcmos_fastlock_unlock(&bcmtrmux_lock, flags);
+            return BCM_ERR_NORES;
+        }
+    }
+
+    /* Make sure that channel is valid and not busy */
+    if ((unsigned)ch >= BCMTRMUX_MAX_CHANNELS)
+    {
+        bcmos_fastlock_unlock(&bcmtrmux_lock, flags);
+        return BCM_ERR_PARM;
+    }
+    if (bcmtrmux_rx_info_array[device][ch].rx != bcmtrmux_rx_discard)
+    {
+        bcmos_fastlock_unlock(&bcmtrmux_lock, flags);
+        return BCM_ERR_ALREADY;
+    }
+
+    /* Assign channel */
+    bcmtrmux_rx_info_array[device][ch].rx = rx;
+    bcmtrmux_rx_info_array[device][ch].data = data;
+
+#ifndef IN_BAND
+    /* Urgent channels are not supported for IN-BAND management */
+    if (ch >= BCMTRMUX_FIRST_URGENT_CHANNEL)
+    {
+        /* We use transmit confirmation interrupt to kick transmission
+         * if PCI bus is shared between high and low-priority channels
+         */
+        if (!num_urgent_channels[device])
+            bcmtr_pcie_txint_enable(device);
+        ++num_urgent_channels[device];
+    }
+#endif
+
+    bcmos_fastlock_unlock(&bcmtrmux_lock, flags);
+
+    *channel = ch;
+
+    return BCM_ERR_OK;
+}
+
+
+/** Release PCIe channel allocated by bcmtrmux_channel_register()
+ *
+ * \param[in]   device  Maple device index
+ * \param[in]   channel
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_channel_unregister(bcmolt_devid device, bcmtrmux_channel channel)
+{
+    long flags;
+
+    if ((unsigned)device >= BCMTR_MAX_OLTS || (unsigned)channel >= BCMTRMUX_MAX_CHANNELS)
+    {
+        return BCM_ERR_PARM;
+    }
+
+    flags = bcmos_fastlock_lock(&bcmtrmux_lock);
+
+    if (bcmtrmux_rx_info_array[device][channel].rx == bcmtrmux_rx_discard)
+    {
+        bcmos_fastlock_unlock(&bcmtrmux_lock, flags);
+        return BCM_ERR_NOENT;
+    }
+
+    bcmtrmux_rx_info_array[device][channel].rx = bcmtrmux_rx_discard;
+    bcmtrmux_rx_info_array[device][channel].data = &bcmtrmux_stat_array[device].rx_disc_remote;
+
+#ifndef IN_BAND
+    /* Urgent channels are not supported for IN-BAND management */
+    if (channel >= BCMTRMUX_FIRST_URGENT_CHANNEL)
+    {
+        --num_urgent_channels[device];
+        /* If PCI bus is not shared between normal and urgent channels,
+         * transmit confirmation mechanism is not needed
+         */
+        if (!num_urgent_channels[device])
+            bcmtr_pcie_txint_disable(device);
+    }
+#endif
+
+    bcmos_fastlock_unlock(&bcmtrmux_lock, flags);
+
+    return BCM_ERR_OK;
+}
+
+/*
+ * Local termination handler
+ */
+
+/* Register local termination handler. */
+bcmos_errno bcmtrmux_local_handler_register(bcmolt_devid device, f_bcmtr_local_rx_handler rx, void *data)
+{
+    long flags;
+
+    if ((unsigned)device >= BCMTR_MAX_OLTS || !rx)
+    {
+        return BCM_ERR_PARM;
+    }
+    flags = bcmos_fastlock_lock(&bcmtrmux_lock);
+    if (bcmtrmux_local_rx_info_array[device].rx != bcmtrmux_local_rx_discard)
+    {
+        bcmos_fastlock_unlock(&bcmtrmux_lock, flags);
+        return BCM_ERR_ALREADY;
+    }
+    bcmtrmux_local_rx_info_array[device].rx = rx;
+    bcmtrmux_local_rx_info_array[device].data = data;
+    bcmos_fastlock_unlock(&bcmtrmux_lock, flags);
+    return BCM_ERR_OK;
+}
+
+
+/* Unregister local termination handler registered by bcmtrmux_local_handler_register() */
+bcmos_errno bcmtrmux_local_handler_unregister(bcmolt_devid device)
+{
+    long flags;
+
+    if ((unsigned)device >= BCMTR_MAX_OLTS)
+    {
+        return BCM_ERR_PARM;
+    }
+    flags = bcmos_fastlock_lock(&bcmtrmux_lock);
+    if (bcmtrmux_local_rx_info_array[device].rx == bcmtrmux_local_rx_discard)
+    {
+        bcmos_fastlock_unlock(&bcmtrmux_lock, flags);
+        return BCM_ERR_NOENT;
+    }
+    bcmtrmux_local_rx_info_array[device].data = &bcmtrmux_stat_array[device].tx_disc_local;
+    bcmtrmux_local_rx_info_array[device].rx = bcmtrmux_local_rx_discard;
+    bcmos_fastlock_unlock(&bcmtrmux_lock, flags);
+    return BCM_ERR_OK;
+}
+
+/* Deliver message to local destination */
+static bcmos_bool bcmtrmux_deliver_to_local(bcmolt_devid device, bcmtrmux_channel channel, bcmos_buf *buf, bcmtr_hdr *hdr)
+{
+    bcmtrmux_local_rx_info *rx_info;
+    bcmolt_msg *msg = NULL;
+    bcmolt_buf ubuf;
+    bcmos_errno err;
+
+    /* Unpack */
+    bcmolt_buf_init(&ubuf, bcmos_buf_length(buf), bcmos_buf_data(buf), BCMTR_BUF_ENDIAN);
+    bcmolt_buf_skip(&ubuf, BCMTR_HDR_SIZE);
+    err = bcmolt_msg_unpack(&ubuf, &msg);
+    bcmos_buf_free(buf);
+    if (err < 0)
+    {
+        BCMOS_TRACE_ERR("Message unpack error %s (%d)\n", bcmos_strerror(err), err);
+        ++bcmtrmux_stat_array[device].tx_disc_local;
+        return BCMOS_TRUE;
+    }
+    msg->corr_tag = hdr->corr_tag;
+    msg->subch = (bcmolt_subchannel)channel;
+    ++bcmtrmux_stat_array[device].tx_local;
+
+    rx_info = &bcmtrmux_local_rx_info_array[device];
+    rx_info->rx(device, msg, rx_info->data);
+    return BCMOS_TRUE;
+}
+
+/* send to line with repetitive attempts if pcie buffer is full */
+static void bcmtrmux_send_to_line(bcmolt_devid device, bcmtrmux_channel channel, bcmos_buf *buf)
+{
+    bcmos_errno rc;
+
+#ifdef IN_BAND
+    rc = bcmtr_ib_send((uint8_t)device, (uint8_t)channel, buf);
+#else
+    rc = bcmtr_swq_send((uint8_t)device, channel, buf);
+#endif
+    if (rc != BCM_ERR_OK)
+    {
+        /* Failed */
+        ++bcmtrmux_stat_array[device].tx_disc_remote;
+        bcmos_buf_free(buf);
+    }
+}
+
+/* Receive message from host application */
+void bcmtrmux_rx_from_host(bcmolt_devid device, bcmtrmux_channel channel, bcmos_buf *buf)
+{
+    bcmtr_hdr hdr;
+    bcmolt_obj_id obj;
+    bcmolt_mgt_group group;
+    uint16_t subgroup;
+    bcmos_errno rc;
+
+    /* Validate parameters */
+    BUG_ON((unsigned)device >= BCMTR_MAX_OLTS);
+    BUG_ON((unsigned)channel >= BCMTRMUX_MAX_CHANNELS);
+
+    /* Peek in transport header. It contains enough info to decide what to do with the message */
+    bcmtr_header_unpack(bcmos_buf_data(buf), &hdr);
+
+    rc = bcmolt_group_id_split(hdr.msg_id, &obj, &group, &subgroup);
+    if (rc)
+    {
+        BCMOS_TRACE_ERR("Can't decode group_id %u. Error %s (%d)\n", hdr.msg_id, bcmos_strerror(rc), rc);
+        ++bcmtrmux_stat_array[device].tx_disc_remote;
+        bcmos_buf_free(buf);
+        return;
+    }
+
+    /* Filter auto/proxy (un)registration.
+     * This message is terminated here.
+     */
+    if (hdr.auto_proxy_reg || hdr.auto_proxy_unreg)
+    {
+        bcmtrmux_channel *p_ch = (group == BCMOLT_MGT_GROUP_AUTO) ?
+            &bcmtrmux_auto_channels[device][hdr.instance][obj] : &bcmtrmux_proxy_channels[device][hdr.instance][obj];
+
+        bcmos_buf_free(buf);
+
+        /* Sanity check */
+        if (hdr.instance >= BCMTR_MAX_INSTANCES || obj >= BCMOLT_OBJ_ID__NUM_OF)
+        {
+            BCMOS_TRACE_ERR("Instance %u or object %d is insane\n", hdr.instance, obj);
+            return;
+        }
+
+        /* Do not override bcmolt_dev_ctrl filters */
+        if (*p_ch != BCMTRMUX_CHANNEL_DEV_CONTROL)
+        {
+            *p_ch = hdr.auto_proxy_reg ? channel : BCMTRMUX_MAX_CHANNELS;
+        }
+
+        return;
+    }
+
+    /* Filter message that should go to local destination (device control) */
+    if (bcmtrmux_msg_filter_cb && bcmtrmux_msg_filter_cb(device, obj, group, subgroup) == BCMTRMUX_DEST_LOCAL)
+    {
+        if (bcmtrmux_deliver_to_local(device, channel, buf, &hdr) == BCMOS_TRUE)
+            return;
+    }
+
+    /* Handle Remote message */
+    ++bcmtrmux_stat_array[device].tx_remote;
+    bcmtrmux_send_to_line(device, channel, buf);
+}
+
+
+/* Receive packet from the line or local control process.
+ * Parameters are expected to be checked beforehand.
+ * The function de-muxes
+ * - replies based on channel
+ * - autonomous/proxy messages based on registration info
+ */
+static void bcmtrmux_rx(bcmolt_devid device, bcmtrmux_channel channel, bcmos_buf *buf)
+{
+    bcmtrmux_rx_info *rx_info;
+    rx_info = &bcmtrmux_rx_info_array[device][channel];
+    rx_info->rx(device, buf, channel, rx_info->data);
+}
+
+
+/* Receive packet from PCIe interface */
+void bcmtrmux_rx_from_line(bcmolt_devid device, bcmtrmux_channel channel, bcmos_buf *buf)
+{
+    BUG_ON((unsigned)device >= BCMTR_MAX_OLTS);
+
+    if ((unsigned)channel >= BCMTRMUX_MAX_CHANNELS)
+    {
+        ++bcmtrmux_stat_array[device].rx_disc_inv_ch;
+        bcmos_buf_free(buf);
+        return;
+    }
+
+    ++bcmtrmux_stat_array[device].rx_remote;
+
+    bcmtrmux_rx(device, channel, buf);
+}
+
+/* Handle message received via Auto/Proxy channel */
+static void bcmtrmux_rx_auto_proxy(bcmolt_devid device, bcmos_buf *buf, bcmtrmux_channel channel, void *data)
+{
+    bcmtr_hdr hdr;
+    bcmolt_obj_id obj;
+    bcmolt_mgt_group group;
+    uint16_t subgroup;
+    bcmos_errno rc;
+
+    /* Peek in transport header. It contains enough info to decide what to do with the message */
+    bcmtr_header_unpack(bcmos_buf_data(buf), &hdr);
+
+    rc = bcmolt_group_id_split(hdr.msg_id, &obj, &group, &subgroup);
+    if (rc)
+    {
+        BCMOS_TRACE_ERR("Can't decode group_id %u. Error %s (%d)\n", hdr.msg_id, bcmos_strerror(rc), rc);
+        ++bcmtrmux_stat_array[device].rx_disc_auto;
+        bcmos_buf_free(buf);
+        return;
+    }
+
+    /* Sanity check */
+    if (hdr.instance >= BCMTR_MAX_INSTANCES || obj >= BCMOLT_OBJ_ID__NUM_OF)
+    {
+        BCMOS_TRACE_ERR("Instance %u or object %d is insane\n", hdr.instance, obj);
+        ++bcmtrmux_stat_array[device].rx_disc_auto;
+        bcmos_buf_free(buf);
+        return;
+    }
+
+    /* Dispatch based on object id */
+    /* Handle dev_ctrl intercept */
+    if (bcmtrmux_dev_ctrl_intercept[device][hdr.msg_id])
+    {
+        channel = BCMTRMUX_CHANNEL_DEV_CONTROL;
+    }
+    else
+    {
+        channel = (group == BCMOLT_MGT_GROUP_AUTO) ?
+            bcmtrmux_auto_channels[device][hdr.instance][obj] : bcmtrmux_proxy_channels[device][hdr.instance][obj];
+    }
+
+    /* If no registration - discard */
+    if (channel >= BCMTRMUX_MAX_CHANNELS)
+    {
+        ++bcmtrmux_stat_array[device].rx_disc_auto;
+        bcmos_buf_free(buf);
+        return;
+    }
+    bcmtrmux_rx(device, channel, buf);
+}
+
+/* Handle message received via DEV_CONTROL channel */
+static void bcmtrmux_rx_local(bcmolt_devid device, bcmos_buf *buf, bcmtrmux_channel channel, void *data)
+{
+    bcmtrmux_local_rx_info *rx_info;
+    bcmtr_hdr hdr;
+    bcmolt_buf ubuf;
+    bcmolt_msg *msg = NULL;
+    bcmos_errno err;
+
+    bcmtr_header_unpack(bcmos_buf_data(buf), &hdr);
+
+    bcmolt_buf_init(&ubuf, bcmos_buf_length(buf), bcmos_buf_data(buf), BCMTR_BUF_ENDIAN);
+    bcmolt_buf_skip(&ubuf, BCMTR_HDR_SIZE);
+    err = bcmolt_msg_unpack(&ubuf, &msg);
+    bcmos_buf_free(buf);
+    if (err < 0)
+    {
+        BCMOS_TRACE_ERR("Message unpack error %s (%d)\n", bcmos_strerror(err), err);
+        ++bcmtrmux_stat_array[device].rx_disc_remote;
+        return;
+    }
+
+    msg->corr_tag = hdr.corr_tag;
+    msg->subch = (bcmolt_subchannel)channel;
+    ++bcmtrmux_stat_array[device].rx_local;
+
+    rx_info = &bcmtrmux_local_rx_info_array[device];
+    rx_info->rx(device, msg, rx_info->data);
+}
+
+static bcmos_errno bcmtrmux_msg_pack(bcmolt_devid device, bcmolt_msg *msg, bcmos_buf **p_buf)
+{
+    int32_t len = bcmolt_msg_get_packed_length(msg);
+    bcmos_buf *buf;
+    bcmolt_buf ubuf;
+    bcmos_errno rc = BCM_ERR_OK;
+    bcmtr_hdr thdr = {};
+
+    if (len < 0)
+    {
+        BCMOS_TRACE_ERR("Can't calculate packet length. Error %s (%d)\n", bcmos_strerror(rc), len);
+        return BCM_ERR_PARM;
+    }
+    rc = bcmtr_header_fill(msg, &thdr);
+    if (rc)
+    {
+        BCMOS_TRACE_ERR("Can't create transport header. Error %s (%d)\n", bcmos_strerror(rc), rc);
+        return BCM_ERR_PARM;
+    }
+
+    len += BCMTR_HDR_SIZE;
+    buf = bcmos_buf_alloc(len);
+    if (!buf)
+    {
+        BCMOS_TRACE_ERR("Can't allocate packet buffer\n");
+        return BCM_ERR_NOMEM;
+    }
+    bcmolt_buf_init(&ubuf, len, bcmos_buf_data(buf), BCMTR_BUF_ENDIAN);
+    bcmolt_buf_skip(&ubuf, BCMTR_HDR_SIZE);
+
+    /* Pack transport header */
+    bcmtr_header_pack(&thdr, ubuf.start);
+
+    /* Pack message */
+    rc = bcmolt_msg_pack(msg, &ubuf);
+    if (rc)
+    {
+        BCMOS_TRACE_ERR("Message pack failed. Error %s (%d)\n", bcmos_strerror(rc), rc);
+        bcmos_buf_free(buf);
+        return BCM_ERR_PARM;
+    }
+    bcmos_buf_length_set(buf, len);
+
+    *p_buf = buf;
+
+    return BCM_ERR_OK;
+}
+
+/* Send packet from local control process to the host application */
+bcmos_errno bcmtrmux_control_to_host(bcmolt_devid device, bcmolt_msg *msg)
+{
+    bcmtrmux_channel channel = (bcmtrmux_channel)msg->subch;
+    bcmos_buf *buf;
+    bcmos_errno rc;
+
+    BUG_ON((unsigned)channel >= BCMTRMUX_MAX_CHANNELS);
+    BUG_ON((unsigned)device >= BCMTR_MAX_OLTS);
+
+    ++bcmtrmux_stat_array[device].control_to_host;
+
+    rc = bcmtrmux_msg_pack(device, msg, &buf);
+    if (rc)
+    {
+        return rc;
+    }
+    bcmtrmux_rx(device, channel, buf);
+    return BCM_ERR_OK;
+}
+
+/* Send packet from local control process to the embedded system */
+bcmos_errno bcmtrmux_control_to_line(bcmolt_devid device, bcmolt_msg *msg)
+{
+    bcmos_buf *buf;
+    bcmos_errno err;
+
+    BUG_ON((unsigned)device >= BCMTR_MAX_OLTS);
+
+    ++bcmtrmux_stat_array[device].control_to_line;
+
+    err = bcmtrmux_msg_pack(device, msg, &buf);
+    if (err)
+    {
+        return err;
+    }
+
+    bcmtrmux_send_to_line(device, BCMTRMUX_CHANNEL_DEV_CONTROL, buf);
+
+    return err;
+}
+
+/* Register message for intercept by bcmolt_dev_ctrl */
+bcmos_errno bcmtrmux_control_auto_intercept_filter(bcmolt_devid device,  bcmolt_obj_id object, uint16_t subgroup)
+{
+    bcmos_errno err;
+    bcmolt_group_id msg_id;
+
+    if ((unsigned)device >= BCMTR_MAX_OLTS)
+    {
+        return BCM_ERR_PARM;
+    }
+    err = bcmolt_group_id_combine(object, BCMOLT_MGT_GROUP_AUTO, subgroup, &msg_id);
+    if (err)
+    {
+        BCMOS_TRACE_ERR("Can't identify operation %d for object %d. Error %s (%d)\n",
+            (int)subgroup, (int)object, bcmos_strerror(err), err);
+        return err;
+    }
+    bcmtrmux_dev_ctrl_intercept[device][msg_id] = BCMOS_TRUE;
+    return BCM_ERR_OK;
+}
+
+
+/** Get transport mux statistics.
+ *
+ * \param[in]   device  Maple device index
+ * \param[out]  stat    Statistics
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_stat_get(bcmolt_devid device, bcmtrmux_stat *stat)
+{
+    if ((unsigned)device >= BCMTR_MAX_OLTS || !stat)
+    {
+        return BCM_ERR_PARM;
+    }
+    *stat = bcmtrmux_stat_array[device];
+    return BCM_ERR_OK;
+}
+
+#ifdef __KERNEL__
+
+EXPORT_SYMBOL(bcmtrmux_init);
+EXPORT_SYMBOL(bcmtrmux_connect);
+EXPORT_SYMBOL(bcmtrmux_disconnect);
+EXPORT_SYMBOL(bcmtrmux_channel_register);
+EXPORT_SYMBOL(bcmtrmux_channel_unregister);
+EXPORT_SYMBOL(bcmtrmux_local_handler_register);
+EXPORT_SYMBOL(bcmtrmux_local_handler_unregister);
+EXPORT_SYMBOL(bcmtrmux_rx_from_host);
+EXPORT_SYMBOL(bcmtrmux_rx_from_line);
+EXPORT_SYMBOL(bcmtrmux_control_to_host);
+EXPORT_SYMBOL(bcmtrmux_control_to_line);
+EXPORT_SYMBOL(bcmtrmux_control_auto_intercept_filter);
+EXPORT_SYMBOL(bcmtrmux_stat_get);
+
+#endif
diff --git a/bcm68620_release/release/host_driver/transport/mux/bcmolt_tr_mux.h b/bcm68620_release/release/host_driver/transport/mux/bcmolt_tr_mux.h
new file mode 100644
index 0000000..f837375
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/mux/bcmolt_tr_mux.h
@@ -0,0 +1,252 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+
+#ifndef BCMOLT_TR_MUX_H_
+#define BCMOLT_TR_MUX_H_
+
+/** \defgroup tr_mux Transport Multiplexer
+ * - PCIe channel multiplexer
+ * - locally/remotely - terminated message multiplexer
+ * - autonomous messages de-multiplexer
+ * @{
+ */
+
+#include <bcmolt_msg.h>
+#ifndef IN_BAND
+#include <bcmtr_pcie_sw_queue.h>
+#endif
+
+
+/* Message destination: local or remote */
+typedef enum
+{
+    BCMTRMUX_DEST_LOCAL,
+    BCMTRMUX_DEST_REMOTE,
+} bcmtrmux_msg_dest;
+
+typedef bcmtrmux_msg_dest (*bcmtrmux_msg_filter_cb_t)(bcmolt_devid, bcmolt_obj_id obj, bcmolt_mgt_group group, uint16_t subgroup);
+
+/** Initialize mux service
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_init(bcmtrmux_msg_filter_cb_t msg_filter_cb);
+
+/** Notify mux driver that low-level transport connection is ready
+ * \returns: 0 in case of success or error code < 0
+ */
+#ifdef IN_BAND 
+bcmos_errno bcmtrmux_connect(bcmolt_devid device,  bcmos_ipv4_address ip_address, uint16_t udp_port);
+#else
+bcmos_errno bcmtrmux_connect(bcmolt_devid device, uint32_t txq_size, uint32_t rxq_size);
+#endif
+
+/** Notify mux driver that low-level transport connection is disconnected
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_disconnect(bcmolt_devid device);
+
+/** Cleanup and exit
+ */
+void bcmtrmux_exit(void);
+
+/*
+ * PCIe channel multiplexer
+ */
+
+/** Logical channel id */
+typedef int bcmtrmux_channel;
+
+/** Mux statistics */
+typedef struct bcmtrmux_stat
+{
+    uint32_t tx_remote;         /**< Attempted transmit to device (incuding discards) */
+    uint32_t tx_local;          /**< Attempted "transmit" to device control */
+    uint32_t tx_disc_remote;    /**< Discarded when transmitting to remote device (not ready or queue overflow) */
+    uint32_t tx_disc_local;     /**< Couldn't deliver to device control. No handler */
+    uint32_t rx_remote;         /**< Received from device */
+    uint32_t rx_local;          /**< Message from the line intercepted by device control */
+    uint32_t rx_auto;           /**< Received autonomous messages */
+    uint32_t control_to_host;   /**< Sent by device control to the host */
+    uint32_t control_to_line;   /**< Sent by device control to line */
+    uint32_t rx_disc_remote;    /**< Discarded packets received from device. Can't demux channel */
+    uint32_t rx_disc_auto;      /**< Discarded autonomous messages. Can't demux auto message */
+    uint32_t rx_disc_local;     /**< Discarded packets received from device control. Can't demux channel */
+    uint32_t rx_disc_inv_ch;    /**< Discarded because channel is invalid */
+    uint32_t rx_poll_urgent;    /**< Urgent buffers counted by bcmtr_sw_queue_rx_poll() */
+    uint32_t rx_poll_normal;    /**< Normal buffers counted by bcmtr_sw_queue_rx_poll() */
+} bcmtrmux_stat;
+
+
+/** Max number of channels per device */
+#define BCMTRMUX_MAX_CHANNELS   32
+
+/** First urgent channel */
+#ifndef IN_BAND
+#define BCMTRMUX_FIRST_URGENT_CHANNEL BCMTR_SWQ_FIRST_URGENT_CHANNEL
+#if BCMTRMUX_FIRST_URGENT_CHANNEL >= BCMTRMUX_MAX_CHANNELS
+    #error BCMTRMUX_FIRST_URGENT_CHANNEL and BCMTRMUX_MAX_CHANNELS settings are inconsistent
+#endif
+
+#else
+
+#define BCMTRMUX_FIRST_URGENT_CHANNEL (BCMTRMUX_MAX_CHANNELS - 1)        /* Urgent channel is not supported for INBAND. */
+
+#endif
+
+
+/** Auto-assign channel */
+#define BCMTRMUX_CHANNEL_AUTO_ASSIGN   (-1)
+
+/** Channel id reserved for autonomous/proxy messages */
+#define BCMTRMUX_CHANNEL_AUTO_PROXY     0
+
+/** Channel id reserved for device control */
+#define BCMTRMUX_CHANNEL_DEV_CONTROL    1
+
+/** First channel id that is not reserved */
+#define BCMTRMUX_CHANNEL_FIRST_FREE     (BCMTRMUX_CHANNEL_DEV_CONTROL + 1)
+
+/** Receive message handler */
+typedef void (*f_bcmtr_rx_handler)(bcmolt_devid device, bcmos_buf *buf, bcmtrmux_channel channel, void *data);
+
+/** Local receive message handler */
+typedef void (*f_bcmtr_local_rx_handler)(bcmolt_devid device, bcmolt_msg *msg, void *data);
+
+/** Register PCIe channel owner
+ *
+ * \param[in]           device  Maple device index
+ * \param[in,out]       channel Logical PCIe channel id.
+ *                              If BCMTRMUX_CHANNEL_AUTO_ASSIGN, unused channel is allocated internally.
+ * \param[in]           rx      Receive callback that should be used for packets received over channel
+ * \param[in]           data    Data to be passed to receive callback
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_channel_register(bcmolt_devid device, bcmtrmux_channel *channel,
+    f_bcmtr_rx_handler rx, void *data);
+
+
+/** Release PCIe channel allocated by bcmtrmux_channel_register()
+ *
+ * \param[in]   device  Maple device index
+ * \param[in]   channel
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_channel_unregister(bcmolt_devid device, bcmtrmux_channel channel);
+
+
+/** Receive message from host application
+ *
+ * This function is called for messages transmitted from the host.
+ * Message can be locally or remotely terminated
+ * \param[in]   device  Maple device index
+ * \param[in]   channel Logical channel
+ * \param[in]   buf     Buffer to be transmitted
+ */
+void bcmtrmux_rx_from_host(bcmolt_devid device, bcmtrmux_channel channel, bcmos_buf *buf);
+
+/** Receive packet from PCIe interface
+ *
+ * \param[in]   device  Maple device index
+ * \param[in]   channel Logical channel
+ * \param[in]   buf     Buffer to be forwarded to application.
+ *                      It is de-allocated automatically
+ */
+void bcmtrmux_rx_from_line(bcmolt_devid device, bcmtrmux_channel channel, bcmos_buf *buf);
+
+/** Send packet from local control process to the host application
+ *
+ * \param[in]   device  Maple device index
+ * \param[in]   msg     Message to be sent to host application.
+ *                      Attention! It is responsibility of the caller to release the message
+ *                      after return from this function - if necessary.
+ *                      The reason for this is that it is expected that often msg will be allocated on stack.
+ *                      It is also responsibility of the caller to make sure that msg->subch is set correctly
+ *                      - preserved for request/response exchange
+ *                      - set = BCMTRMUX_CHANNEL_AUTO_PROXY for autonomous indications
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_control_to_host(bcmolt_devid device, bcmolt_msg *msg);
+
+/** Send packet from local control process to the embedded system
+ *
+ * \param[in]   device  Maple device index
+ * \param[in]   msg     Message to be sent to the embedded system.
+ *                      Attention! It is responsibility of the caller to release the message
+ *                      after return from this function - if necessary.
+ *                      The reason for this is that it is expected that often msg will be allocated on stack.
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_control_to_line(bcmolt_devid device, bcmolt_msg *msg);
+
+/** Register message for intercept by dev_ctrl
+ *
+ * Matched message receive from the line will be intercepted and
+ * delivered to callback registered using bcmtrmux_local_handler_register()
+ *
+ * \param[in]   device   Maple device index
+ * \param[in]   object   Object for which message is to be intercepted
+ * \param[in]   subgroup Message subgroup
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_control_auto_intercept_filter(bcmolt_devid device,  bcmolt_obj_id object, uint16_t subgroup);
+
+/*
+ * Local termination handler
+ */
+
+/** Register local termination handler.
+ *
+ * This handler is called for messages transmitted by host application
+ * that should be terminated locally by device control driver and
+ * for messages received from the line that match bcmtrmux_control_auto_intercept_filter()
+ *
+ * \param[in]   device  Maple device index
+ * \param[in]   rx      Receive callback that should be used for locally-terminated packets.
+ *                      It must release the buffer when no longer needed
+ * \param[in]   data    Data to be passed to receive callback
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_local_handler_register(bcmolt_devid device, f_bcmtr_local_rx_handler rx, void *data);
+
+/** Unregister local termination handler registered by bcmtrmux_local_handler_register()
+ *
+ * \param[in]   device  Maple device index
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_local_handler_unregister(bcmolt_devid device);
+
+/** Get transport mux statistics.
+ *
+ * \param[in]   device  Maple device index
+ * \param[out]  stat    Statistics
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtrmux_stat_get(bcmolt_devid device, bcmtrmux_stat *stat);
+
+#endif
diff --git a/bcm68620_release/release/host_driver/transport/mux/daemon/Makefile b/bcm68620_release/release/host_driver/transport/mux/daemon/Makefile
new file mode 100644
index 0000000..f3f9fc3
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/mux/daemon/Makefile
@@ -0,0 +1,27 @@
+# In Band transport
+# Consists of 2 components
+# - low-level driver (send/rx functions)
+# - multiplexer
+#
+ifneq ("$(SIMULATION_BUILD)", "y")
+MOD_NAME = trmux_daemon
+
+MOD_DEPS = transport model trmux inband_driver
+
+MOD_DEFS += -DIN_BAND
+
+ifeq ("$(RELEASE_BUILD)", "y")
+    MOD_DEPS += common_api
+else
+    MOD_DEPS += api
+endif
+
+MOD_TYPE = lib
+
+srcs = ../bcmolt_tr_mux.c 
+
+USE_LINT = yes
+
+endif
+
+
diff --git a/bcm68620_release/release/host_driver/transport/pcie_sw_queue/Makefile b/bcm68620_release/release/host_driver/transport/pcie_sw_queue/Makefile
new file mode 100644
index 0000000..1e36d57
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/pcie_sw_queue/Makefile
@@ -0,0 +1,21 @@
+# PCIe Software Queue driver
+# - Software layer on top of low level PCIe driver that adds support
+#   for s/w queue and priority
+#
+MOD_NAME = pcie_sw_queue
+
+MOD_DEPS = pcie
+
+ifeq ("$(OS_KERNEL)", "linux")
+MOD_TYPE = linux_lib
+else
+MOD_TYPE = lib
+endif
+
+ifneq ("$(RAW_TRANSPORT_VIA_UDP)", "y")
+    srcs = bcmtr_pcie_sw_queue.c
+endif
+
+USE_LINT = yes
+
+
diff --git a/bcm68620_release/release/host_driver/transport/pcie_sw_queue/bcmtr_pcie_sw_queue.c b/bcm68620_release/release/host_driver/transport/pcie_sw_queue/bcmtr_pcie_sw_queue.c
new file mode 100644
index 0000000..624101e
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/pcie_sw_queue/bcmtr_pcie_sw_queue.c
@@ -0,0 +1,389 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+   Broadcom Proprietary and Confidential.(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.
+
+:>
+ */
+#include <bcmtr_pcie.h>
+#include <bcmolt_tr_pcie_specific.h>
+#include "bcmtr_pcie_sw_queue.h"
+
+/*
+ * bcm_pcie_sw_queue.c
+ * Software layer on top of low level PCIe driver that adds support
+ * for s/w queue and priority
+ */
+typedef struct
+{
+    bcmos_buf_queue txq;           /* Transmit queue */
+    bcmos_buf_queue rxq;           /* Receive queue */
+    uint32_t max_hwq_size;
+    uint32_t max_swq_size;
+    bcmtr_swq_rx_cb rx_cb;         /* Optional rx_cb for callback-based RX buffer delivery */
+} pcie_swq;
+
+static pcie_swq swq_info[BCMTR_MAX_OLTS][BCMTR_PCIE_PRTY__NUM_OF];
+static uint32_t hwq_occupancy[BCMTR_MAX_OLTS];  /* Number of unacknowledged buffers in hw tx queue */
+static bcmos_bool swq_initialized[BCMTR_MAX_OLTS];
+static bcmos_fastlock tx_lock[BCMTR_MAX_OLTS];
+static bcmos_fastlock rx_lock[BCMTR_MAX_OLTS];
+
+#define BCMTR_SWQ_GET_RETURN_IF_ERROR(device,prty,swq)   \
+    do {                                        \
+        if (device >= BCMTR_MAX_OLTS)           \
+            return BCM_ERR_PARM;                \
+        swq = &swq_info[device][prty];          \
+    } while (0)
+
+static inline long _bcmtr_swq_tx_lock(uint8_t device)
+{
+    return bcmos_fastlock_lock(&tx_lock[device]);
+}
+
+static inline void _bcmtr_swq_tx_unlock(uint8_t device, long flags)
+{
+    bcmos_fastlock_unlock(&tx_lock[device], flags);
+}
+
+static inline long _bcmtr_swq_rx_lock(uint8_t device)
+{
+    return bcmos_fastlock_lock(&rx_lock[device]);
+}
+
+static inline void _bcmtr_swq_rx_unlock(uint8_t device, long flags)
+{
+    bcmos_fastlock_unlock(&rx_lock[device], flags);
+}
+
+/** Tx done callback.
+ * Must be called under tx_lock
+ */
+static void _bcmtr_swq_tx_done_cb(uint8_t device, bcmos_buf *buf)
+{
+    BUG_ON(!hwq_occupancy[device]);
+    --hwq_occupancy[device];
+    bcmos_buf_free(buf);
+}
+
+/* Initialize PCI software queue module */
+bcmos_errno bcmtr_swq_init(void)
+{
+    return bcmtr_pcie_tx_done_cblk_register(_bcmtr_swq_tx_done_cb);
+}
+
+/* Cleanup software queue module
+ */
+void bcmtr_swq_exit(void)
+{
+    int i;
+
+    /* Unregister from bcmtr_pcie driver */
+    bcmtr_pcie_tx_done_cblk_unregister();
+
+    for (i = 0; i < BCMTR_MAX_OLTS; i++)
+        bcmtr_swq_device_exit(i);
+}
+
+/* Initialize PCI software queue module */
+bcmos_errno bcmtr_swq_device_init(uint8_t device)
+{
+    bcmtr_pcie_prty prty;
+
+    if (device >= BCMTR_MAX_OLTS)
+        return BCM_ERR_PARM;
+
+    if (swq_initialized[device])
+        return BCM_ERR_ALREADY;
+
+    bcmos_fastlock_init(&tx_lock[device], 0);
+    bcmos_fastlock_init(&rx_lock[device], 0);
+    for (prty = 0; prty < BCMTR_PCIE_PRTY__NUM_OF; prty++)
+    {
+        pcie_swq *swq = &swq_info[device][prty];
+        bcmos_buf_queue_init(&swq->txq);
+        bcmos_buf_queue_init(&swq->rxq);
+        swq->rx_cb = NULL;
+        swq->max_hwq_size = swq->max_swq_size = 0;
+    }
+    swq_initialized[device] = BCMOS_TRUE;
+
+    return BCM_ERR_OK;
+}
+
+/* Cleanup software queue module */
+void bcmtr_swq_device_exit(uint8_t device)
+{
+    bcmtr_pcie_prty prty;
+
+    if (!swq_initialized[device])
+        return;
+
+    for (prty = 0; prty < BCMTR_PCIE_PRTY__NUM_OF; prty++)
+    {
+        pcie_swq *swq = &swq_info[device][prty];
+        bcmos_buf *buf;
+
+        while ((buf=bcmos_buf_queue_get(&swq->txq)))
+            bcmos_buf_free(buf);
+        while ((buf=bcmos_buf_queue_get(&swq->rxq)))
+            bcmos_buf_free(buf);
+    }
+    swq_initialized[device] = BCMOS_FALSE;
+
+    return;
+}
+
+/** Send buffer to the peer
+ * \param[in]   device    Maple device index
+ * \param[in]   channel   Channel id (opaque to the bcmtr_pcie driver)
+ * \param[in]   buf       Buffer to be transferred
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_swq_send(uint8_t device, uint8_t channel, bcmos_buf *buf)
+{
+    bcmtr_pcie_prty prty = (channel >= BCMTR_SWQ_FIRST_URGENT_CHANNEL) ?
+        BCMTR_PCIE_PRTY_URGENT : BCMTR_PCIE_PRTY_NORMAL;
+    pcie_swq *swq;
+    bcmos_bool was_empty;
+    bcmos_bool hw_queue_full;
+    bcmos_errno err;
+    long flags;
+
+    BCMTR_SWQ_GET_RETURN_IF_ERROR(device, prty, swq);
+
+    /* Store channel in the buffer */
+    bcmos_buf_channel_set(buf, channel);
+
+    /* Prevent concurrent access to the queue */
+    flags = _bcmtr_swq_tx_lock(device);
+
+    /* Store q-was-empty status */
+    was_empty = bcmos_buf_queue_is_empty(&swq->txq);
+
+    /* Check if max h/w queue occupancy isn't exceeded. If it isn't and s/w queue is empty
+     * submit directly to the h/w queue.
+     */
+    hw_queue_full = (swq->max_hwq_size && hwq_occupancy[device] >= swq->max_hwq_size);
+    if (was_empty && !hw_queue_full)
+    {
+        ++hwq_occupancy[device];
+        _bcmtr_swq_tx_unlock(device, flags);
+        err = bcmtr_pcie_send(device, channel, buf);
+        if (err)
+        {
+            flags = _bcmtr_swq_tx_lock(device);
+            --hwq_occupancy[device];
+            /* If sw q is enabled, enque the buffer, otherwise, just return */
+            if (swq->max_swq_size || swq->max_hwq_size)
+            {
+                bcmos_buf_queue_put(&swq->txq, buf);
+                err = BCM_ERR_OK;
+            }
+            _bcmtr_swq_tx_unlock(device, flags);
+        }
+    }
+    else
+    {
+        bcmos_buf_queue_put(&swq->txq, buf);
+        _bcmtr_swq_tx_unlock(device, flags);
+        err = BCM_ERR_OK;
+    }
+
+
+    return err;
+}
+
+/** Receive packet from device
+ * \param[in]   device          Maple device index
+ * \param[in]   prty            Priority
+ * \param[out]  channel         message channel from the BD
+ * \param[out]  buf  pointer to network buffer containing the
+ *       received packet
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_swq_receive(uint8_t device, bcmtr_pcie_prty prty, uint8_t *channel, bcmos_buf **buf)
+{
+    pcie_swq *swq;
+    long flags;
+    bcmos_errno err;
+
+    BCMTR_SWQ_GET_RETURN_IF_ERROR(device, prty, swq);
+
+    /* Peevent concurent access to the queue */
+    flags = _bcmtr_swq_rx_lock(device);
+    *buf = bcmos_buf_queue_get(&swq->rxq);
+    if (*buf)
+    {
+        *channel = bcmos_buf_channel(*buf);
+        err = BCM_ERR_OK;
+    }
+    else
+    {
+        err = BCM_ERR_QUEUE_EMPTY;
+    }
+    _bcmtr_swq_rx_unlock(device, flags);
+
+    return err;
+}
+
+/** Configure TX queue.
+ */
+bcmos_errno bcmtr_swq_tx_queue_cfg(uint8_t device, bcmtr_pcie_prty prty, uint32_t hardq_size, uint32_t softq_size)
+{
+    pcie_swq *swq;
+
+    BCMTR_SWQ_GET_RETURN_IF_ERROR(device, prty, swq);
+
+    swq->max_hwq_size = hardq_size;
+    swq->max_swq_size = softq_size;
+
+    return BCM_ERR_OK;
+}
+
+/** Register for "data received indication"
+ * \param[in]   device          Maple device index
+ * \param[in]   prty            Priority
+ * \param[in]   cb              Callback pointer
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_swq_rx_cb_register(uint8_t device, bcmtr_pcie_prty prty, bcmtr_swq_rx_cb rx_cb)
+{
+    if (device >= BCMTR_MAX_OLTS)
+        return BCM_ERR_PARM;
+    swq_info[device][prty].rx_cb = rx_cb;
+
+    return BCM_ERR_OK;
+}
+
+/** Unregister "data received indication" callback
+ * \param[in]   device          Maple device index
+ * \param[in]   prty            Priority
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_swq_rx_cb_unregister(uint8_t device, bcmtr_pcie_prty prty)
+{
+    if (device >= BCMTR_MAX_OLTS)
+        return BCM_ERR_PARM;
+    swq_info[device][prty].rx_cb = NULL;
+
+    return BCM_ERR_OK;
+}
+
+/* Fetch data from the hw to the sw queue. */
+void bcmtr_swq_rx_poll(uint8_t device, uint32_t nbuf[])
+{
+    uint8_t channel;
+    bcmos_buf *buf;
+    int n[BCMTR_PCIE_PRTY__NUM_OF] = {};
+    long flags;
+    bcmos_errno err;
+
+    do
+    {
+        bcmtr_pcie_prty prty;
+        pcie_swq *swq;
+
+        err = bcmtr_pcie_receive(device, &channel, &buf);
+        if (err != BCM_ERR_OK)
+            break;
+        prty = (channel >= BCMTR_SWQ_FIRST_URGENT_CHANNEL) ?
+            BCMTR_PCIE_PRTY_URGENT : BCMTR_PCIE_PRTY_NORMAL;
+        /* If callback based delivery - deliver buffer now, otherwise, place on s/w queue */
+        swq = &swq_info[device][prty];
+        if (swq->rx_cb)
+        {
+            swq->rx_cb(device, channel, buf);
+        }
+        else
+        {
+            bcmos_buf_channel_set(buf, channel);
+            flags = _bcmtr_swq_rx_lock(device);
+            bcmos_buf_queue_put(&swq->rxq, buf);
+            _bcmtr_swq_rx_unlock(device, flags);
+        }
+        ++n[prty];
+    } while (BCMOS_TRUE);
+
+
+    nbuf[0] = n[0];
+    nbuf[1] = n[1];
+}
+
+/* Submit data from the sw TX queue to the h/w */
+static void _bcmtr_swq_tx_submit_prty(uint8_t device, bcmtr_pcie_prty prty)
+{
+    bcmos_errno err = BCM_ERR_OK;
+    pcie_swq *swq;
+    bcmos_buf *buf;
+    uint8_t channel;
+    bcmos_bool hw_queue_full;
+    long flags;
+
+    swq = &swq_info[device][prty];
+    do
+    {
+        flags = _bcmtr_swq_tx_lock(device);
+
+        /* Check if not over limit */
+        hw_queue_full = (swq->max_hwq_size && hwq_occupancy[device] >= swq->max_hwq_size);
+        if (hw_queue_full)
+        {
+            _bcmtr_swq_tx_unlock(device, flags);
+            break;
+        }
+
+        /* Get from s/w queue and submit to the h/w queue */
+        buf = bcmos_buf_queue_peek(&swq->rxq);
+        _bcmtr_swq_tx_unlock(device, flags);
+        if (!buf)
+            break;
+
+        channel = bcmos_buf_channel(buf);
+        err = bcmtr_pcie_send(device, channel, buf);
+        if (err != BCM_ERR_OK)
+            break;
+
+        flags = _bcmtr_swq_tx_lock(device);
+        ++hwq_occupancy[device];
+        bcmos_buf_queue_get(&swq->txq);
+        _bcmtr_swq_tx_unlock(device, flags);
+
+    } while (BCMOS_TRUE);
+}
+
+/* Submit data from the sw TX queue to the h/w */
+void bcmtr_swq_tx_submit(uint8_t device)
+{
+    if (bcmtr_pcie_tx_collect(device) > 0)
+    {
+        _bcmtr_swq_tx_submit_prty(device, BCMTR_PCIE_PRTY_URGENT);
+        _bcmtr_swq_tx_submit_prty(device, BCMTR_PCIE_PRTY_NORMAL);
+    }
+}
+
+#ifdef __KERNEL__
+EXPORT_SYMBOL(bcmtr_swq_tx_queue_cfg);
+EXPORT_SYMBOL(bcmtr_swq_send);
+#endif
diff --git a/bcm68620_release/release/host_driver/transport/pcie_sw_queue/bcmtr_pcie_sw_queue.h b/bcm68620_release/release/host_driver/transport/pcie_sw_queue/bcmtr_pcie_sw_queue.h
new file mode 100644
index 0000000..1cf6c23
--- /dev/null
+++ b/bcm68620_release/release/host_driver/transport/pcie_sw_queue/bcmtr_pcie_sw_queue.h
@@ -0,0 +1,115 @@
+/*
+ * bcm_pcie_sw_queue.h
+ *
+ *  Created on: 31 Jan 2017
+ *      Author: igort
+ */
+
+#ifndef _BCM_PCIE_SW_QUEUE_H_
+#define _BCM_PCIE_SW_QUEUE_H_
+
+#include <bcmos_system.h>
+#include <bcmtr_pcie.h>
+
+/* Message priority */
+typedef enum
+{
+    BCMTR_PCIE_PRTY_URGENT = 0,
+    BCMTR_PCIE_PRTY_NORMAL = 1,
+
+    BCMTR_PCIE_PRTY__NUM_OF
+} bcmtr_pcie_prty;
+
+/* First urgent channel id */
+#define BCMTR_SWQ_FIRST_URGENT_CHANNEL     30
+
+/** Initialize PCI software queue module
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_swq_init(void);
+
+/* Cleanup software queue module
+ */
+void bcmtr_swq_exit(void);
+
+/** Initialize PCI software queue module for a device
+ * \param[in]   device    Maple device index
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_swq_device_init(uint8_t device);
+
+/* Cleanup software queue module for a device
+ * \param[in]   device    Maple device index
+ */
+void bcmtr_swq_device_exit(uint8_t device);
+
+/** Configure TX queue.
+ * \param[in]   device      Maple device index
+ * \param[in]   prty        Priority
+ * \param[in]   hardq_size  Max number of buffers that can be submitted to the h/w queue.
+ *              0=unlimited.
+ * \param[in]   softq_size  Max number of buffers to be queued. 0=unlimited.
+ * \returns: 0 in case of success or error code < 0
+ * BCM_ERR_OVERFLOW - max_softq_size buffer is waiting to be handling by the receiver
+ */
+bcmos_errno bcmtr_swq_tx_queue_cfg(uint8_t device, bcmtr_pcie_prty prty, uint32_t hardq_size, uint32_t softq_size);
+
+/** Send buffer to the peer
+ * \param[in]   device    Maple device index
+ * \param[in]   prty      Priority
+ * \param[in]   channel   Channel id
+ * \param[in]   buf       Buffer to be transferred
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_swq_send(uint8_t device, uint8_t channel, bcmos_buf *buf);
+
+/** Receive packet from device
+ * \param[in]   device          Maple device index
+ * \param[in]   prty            Priority
+ * \param[out]  channel         message channel from the BD
+ * \param[out]  buf  pointer to network buffer containing the
+ *       received packet
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_swq_receive(uint8_t device, bcmtr_pcie_prty prty, uint8_t *channel, bcmos_buf **buf);
+
+/** data_received callback enables callback-based receive buffer delivery
+ * instead of / in addition to bcmtr_swq_receive() function.
+ * This callback takes buffer ownership
+ */
+typedef void (*bcmtr_swq_rx_cb)(uint8_t device, uint8_t channel, bcmos_buf *buf);
+
+/** Register for "data received" indication"
+ * \param[in]   device          Maple device index
+ * \param[in]   prty            Priority
+ * \param[in]   cb              Callback pointer
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_swq_rx_cb_register(uint8_t device, bcmtr_pcie_prty prty, bcmtr_swq_rx_cb rx_cb);
+
+/** Unregister "data received indication" callback
+ * \param[in]   device          Maple device index
+ * \param[in]   prty            Priority
+ * \returns: 0 in case of success or error code < 0
+ */
+bcmos_errno bcmtr_swq_rx_cb_unregister(uint8_t device, bcmtr_pcie_prty prty);
+
+/** Fetch data from the hw to the sw queue.
+ *
+ * It is expected that this function should be triggered by RX interrupt
+ *
+ * \param[in]   device          Maple device index
+ * \param[out]  nbuf            Per priority array with number of buffers
+ */
+void bcmtr_swq_rx_poll(uint8_t device, uint32_t nbuf[]);
+
+/** Submit data from the sw TX queue to the h/w
+ *
+ * It is expected that this function should be triggered by TX
+ * completion interrupt
+ *
+ * \param[in]   device          Maple device index
+ */
+void bcmtr_swq_tx_submit(uint8_t device);
+
+#endif /* _BCM_PCIE_SW_QUEUE_H_ */