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, ¤t_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_ */