BAL and Maple Release 2.2

Signed-off-by: Shad Ansari <developer@Carbon.local>
diff --git a/bcm68620_release/release/host_reference/cli/Makefile b/bcm68620_release/release/host_reference/cli/Makefile
new file mode 100644
index 0000000..f6974c1
--- /dev/null
+++ b/bcm68620_release/release/host_reference/cli/Makefile
@@ -0,0 +1,26 @@
+# CLI engine
+#
+MOD_NAME = cli
+MOD_TYPE = lib
+MOD_DEPS = utils
+
+srcs = bcmcli_session.c
+ifeq ("$(ENABLE_CLI)", "y")
+    srcs += bcmcli.c
+    MOD_DEFS = -DENABLE_CLI
+
+    # Enable line editing by default. Can be overwritten in make command line
+    CONFIG_LIBEDIT	?= n
+    CONFIG_LINENOISE ?= y
+    
+    # Extra configuration
+    ifeq ("$(CONFIG_LIBEDIT)", "y")
+    	MOD_DEFS += -DCONFIG_LIBEDIT -DCONFIG_EDITLINE
+    	MOD_LIBS += -ledit -ltermcap
+    	CONFIG_LINENOISE = n
+    endif
+    ifeq ("$(CONFIG_LINENOISE)", "y")
+    	MOD_DEPS += linenoise
+    endif
+endif
+
diff --git a/bcm68620_release/release/host_reference/cli/bcmcli.c b/bcm68620_release/release/host_reference/cli/bcmcli.c
new file mode 100644
index 0000000..9190e5d
--- /dev/null
+++ b/bcm68620_release/release/host_reference/cli/bcmcli.c
@@ -0,0 +1,2835 @@
+/*
+<: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.
+
+:>
+ */
+
+
+/*******************************************************************
+ * bcmcli.c
+ *
+ * CLI engine
+ *
+ *******************************************************************/
+#include <bcmos_system.h>
+#define BCMCLI_INTERNAL
+#include <bcmcli.h>
+#include <bcmos_types.h>
+
+#define BCMCLI_INBUF_LEN          2048
+#define BCMCLI_MAX_QUAL_NAME_LENGTH 256
+#define BCMCLI_MAX_PARMS          128
+#define BCMCLI_UP_STR             ".."
+#define BCMCLI_ROOT_STR           "/"
+#define BCMCLI_COMMENT_CHAR       '#'
+#define BCMCLI_HELP_CHAR          '?'
+#define BCMCLI_ARRAY_DELIM_CHAR   ','
+#define BCMCLI_ROOT_HELP          "root directory"
+#define BCMCLI_MAX_PARM_VAL_LEN   256
+#define BCMCLI_ENUM_MASK_DEL_CHAR '+'
+#define BCMCLI_HELP_BUFFER_SIZE   16384
+
+#define BCMCLI_EQUAL_CHAR         '='
+
+
+typedef enum { BCMCLI_ENTRY_DIR, BCMCLI_ENTRY_CMD } bcmcli_entry_selector;
+
+/* External table - boolean values */
+bcmcli_enum_val bcmcli_enum_bool_table[] = {
+    { .name="true", .val = 1 },
+    { .name="yes", .val = 1 },
+    { .name="on", .val = 1 },
+    { .name="false", .val = 0 },
+    { .name="no", .val = 0 },
+    { .name="off", .val = 0 },
+    BCMCLI_ENUM_LAST
+};
+
+/* Monitor token structure */
+struct bcmcli_entry
+{
+    struct bcmcli_entry  *next;
+    char *name;                                  /* Command/directory name */
+    char *help;                                  /* Command/directory help */
+    bcmcli_entry_selector sel;                   /* Entry selector */
+    char *alias;                                 /* Alias */
+    uint16_t alias_len;                          /* Alias length */
+    struct bcmcli_entry *parent;                 /* Parent directory */
+    bcmcli_access_right access_right;
+
+    union {
+        struct
+        {
+            struct bcmcli_entry *first;          /* First entry in directory */
+            bcmcli_dir_extra_parm extras;        /* Optional extras */
+        } dir;
+        struct
+        {
+            bcmcli_cmd_cb cmd_cb;                /* Command callback */
+            bcmcli_cmd_parm *parms;              /* Command parameters */
+            bcmcli_cmd_extra_parm extras;        /* Optional extras */
+            uint16_t num_parms;
+        } cmd;
+    } u;
+};
+
+
+/* Token types */
+typedef enum
+{
+    BCMCLI_TOKEN_EMPTY,
+    BCMCLI_TOKEN_UP,
+    BCMCLI_TOKEN_ROOT,
+    BCMCLI_TOKEN_BREAK,
+    BCMCLI_TOKEN_HELP,
+    BCMCLI_TOKEN_NAME,
+    BCMCLI_TOKEN_VALUE,
+} bcmcli_token_type;
+
+/* Parameter value set descriptor */
+typedef union bcmcli_parm_value_status
+{
+    bcmos_bool  value_set;
+    bcmos_bool *values_set;
+} bcmcli_parm_value_status;
+
+/* CLI session data */
+typedef struct bcmcli_session_data
+{
+    bcmcli_entry *curdir;
+    bcmcli_entry *curcmd;
+    bcmcli_cmd_parm cmd_parms[BCMCLI_MAX_PARMS];
+    bcmcli_parm_value_status value_status[BCMCLI_MAX_PARMS];
+    bcmcli_session *session;
+    uint16_t num_parms;
+    char *p_inbuf;
+    int stop_monitor;
+    char inbuf[BCMCLI_INBUF_LEN];
+} bcmcli_session_extras;
+
+/* Name, value pairs */
+typedef struct bcmcli_name_value
+{
+    bcmcli_token_type type;
+    const char *name;
+    const char *value;
+} bcmcli_name_value;
+
+static bcmcli_entry           *_bcmcli_root_dir;
+static bcmcli_session_extras  *_bcmcli_root_session;
+static bcmcli_log_mode         _bcmcli_log_mode;
+static bcmcli_session         *_bcmcli_log_session;
+
+#define BCMCLI_MIN_NAME_LENGTH_FOR_ALIAS   3
+#define BCMCLI_ROOT_NAME       "/"
+
+/* Internal functions */
+static void        _bcmcli_alloc_root(const bcmcli_session_parm *parm);
+static void        _bcmcli_display_dir(bcmcli_session_extras *mon_session, bcmcli_entry *p_dir );
+static bcmcli_token_type _bcmcli_get_word(bcmcli_session_extras *session, char **inbuf, char **p_word);
+static bcmcli_token_type _bcmcli_analyze_token( const char *name );
+static int         _bcmcli_parse_parms( bcmcli_session_extras *mon_session, bcmcli_entry *p_token,
+    bcmcli_name_value *pairs, int npairs);
+static int _bcmcli_extend_parms( bcmcli_session_extras *mon_session, bcmcli_name_value *pairs,
+    int npairs, bcmos_bool last_is_space, char *insert_str, uint32_t insert_size);
+static bcmcli_entry *_bcmcli_search_token( bcmcli_entry *p_dir, const char *name );
+static void        _bcmcli_help_dir( bcmcli_session_extras *mon_session, bcmcli_entry *p_dir );
+static void        _bcmcli_help_entry(bcmcli_session_extras *mon_session, bcmcli_entry *p_token,
+    bcmcli_name_value *pairs, int npairs, bcmos_bool suppress_err_print);
+static void        _bcmcli_help_populated_cmd(bcmcli_session_extras *mon_session, bcmcli_entry *p_token,
+    const char *partial_match, bcmos_bool suppress_assigned);
+static void        _bcmcli_choose_alias( bcmcli_entry *p_dir, bcmcli_entry *p_new_token );
+static bcmcli_cmd_parm *_bcmcli_find_named_parm(bcmcli_session_extras *mon_session, const char *name);
+static char       *_bcmcli_strlwr( char *s );
+static int         _bcmcli_stricmp( const char *s1, const char *s2, int len );
+static bcmos_errno _bcmcli_dft_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val);
+static const char *_bcmcli_get_type_name(const bcmcli_cmd_parm *parm);
+static void        _bcmcli_dft_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size);
+static bcmos_errno _bcmcli_enum_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val);
+static void        _bcmcli_enum_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size);
+static bcmos_errno _bcmcli_enum_mask_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val);
+static void        _bcmcli_enum_mask_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size);
+static bcmos_errno _bcmcli_buffer_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val);
+static const char *_bcmcli_qualified_name( bcmcli_entry *token, char *buffer, int size);
+static bcmos_errno _bcmcli_split(bcmcli_session_extras *mon_session, bcmcli_name_value **pairs, int *npairs);
+static void        _bcmcli_assign_callbacks(bcmcli_cmd_parm *parm);
+static void        _bcmcli_log_cmd(const char *cmd);
+static void        _bcmcli_log_rc(bcmos_errno rc);
+static void        _bcmcli_free_session_value_status(bcmcli_session_extras *mon_session);
+
+static inline bcmcli_session_extras *_bcmcli_session_data(bcmcli_session *session)
+{
+    if (!session)
+        return _bcmcli_root_session;
+    return bcmcli_session_data(session);
+}
+
+/** Add subdirectory to the parent directory
+ *
+ * \param[in]   parent          Parent directory handle. NULL=root
+ * \param[in]   name            Directory name
+ * \param[in]   help            Help string
+ * \param[in]   access_right    Access rights
+ * \param[in]   extras          Optional directory descriptor. Mustn't be allocated on the stack.
+ * \return      new directory handle or NULL in case of failure
+ */
+bcmcli_entry *bcmcli_dir_add(bcmcli_entry *parent, const char *name,
+                             const char *help, bcmcli_access_right access_right,
+                             const bcmcli_dir_extra_parm *extras)
+{
+    bcmcli_entry *p_dir;
+    bcmcli_entry **p_e;
+
+    assert(name);
+    assert(help);
+    if (!name || !help)
+        return NULL;
+
+    if (!_bcmcli_root_dir)
+    {
+        _bcmcli_alloc_root(NULL);
+        if (!_bcmcli_root_dir)
+            return NULL;
+    }
+
+    if (!parent)
+        parent = _bcmcli_root_dir;
+
+    p_dir=(bcmcli_entry *)bcmos_calloc( sizeof(bcmcli_entry) + strlen(name) + strlen(help) + 2 );
+    if ( !p_dir )
+        return NULL;
+
+    p_dir->name = (char *)(p_dir + 1);
+    strcpy( p_dir->name, name);
+    p_dir->help = p_dir->name + strlen(name) + 1;
+    strcpy(p_dir->help, help);
+    p_dir->sel = BCMCLI_ENTRY_DIR;
+    _bcmcli_choose_alias( parent, p_dir );
+    p_dir->access_right = access_right;
+    if (extras)
+        p_dir->u.dir.extras = *extras;
+
+    /* Add new directory to the parent's list */
+    p_dir->parent = parent;
+    p_e = &(parent->u.dir.first);
+    while (*p_e)
+        p_e = &((*p_e)->next);
+    *p_e = p_dir;
+
+    return p_dir;
+}
+
+static bcmcli_entry * find_entry_in_dir( bcmcli_entry *dir, const char *name,
+        bcmcli_entry_selector type, uint16_t recursive_search)
+{
+    bcmcli_entry *p1, *p;
+
+    if ( !dir )
+    {
+        dir = _bcmcli_root_dir;
+        if (!dir)
+            return NULL;
+    }
+    p = dir->u.dir.first;
+    while (p)
+    {
+        if ( !_bcmcli_stricmp(p->name, name, -1) && type == p->sel )
+            return p;
+        if ( recursive_search && p->sel == BCMCLI_ENTRY_DIR )
+        {
+            p1 = find_entry_in_dir(p, name , type, 1 );
+            if ( p1 )
+                return p1;
+        }
+        p = p->next;
+    }
+    return NULL;
+}
+
+
+/* Scan directory tree and look for directory with name starts from
+ * root directory with name root_name
+ */
+bcmcli_entry *bcmcli_dir_find(bcmcli_entry *parent, const char  *name)
+{
+    if ( !parent )
+        parent = _bcmcli_root_dir;
+    return find_entry_in_dir(parent, name, BCMCLI_ENTRY_DIR, 0 );
+}
+
+
+/* Scan directory tree and look for command named "name". */
+bcmcli_entry *bcmcli_cmd_find(bcmcli_entry *parent, const char *name )
+{
+    if ( !parent )
+        parent = _bcmcli_root_dir;
+    return find_entry_in_dir(parent, name, BCMCLI_ENTRY_CMD, 0 );
+}
+
+
+/** Add CLI command
+ *
+ * \param[in]   dir             Handle of directory to add command to. NULL=root
+ * \param[in]   name            Command name
+ * \param[in]   cmd_cb          Command handler
+ * \param[in]   help            Help string
+ * \param[in]   access_right    Access rights
+ * \param[in]   extras          Optional extras
+ * \param[in]   parms           Optional parameters array. Must not be allocated on the stack!
+ *                              If parms!=NULL, the last parameter in the array must have name==NULL.
+ * \return
+ *      0   =OK\n
+ *      <0  =error code
+ */
+bcmos_errno bcmcli_cmd_add(bcmcli_entry *dir, const char *name, bcmcli_cmd_cb cmd_cb,
+    const char *help, bcmcli_access_right access_right,
+    const bcmcli_cmd_extra_parm *extras, bcmcli_cmd_parm parms[])
+{
+    bcmcli_entry *p_token;
+    bcmcli_entry **p_e;
+    uint16_t       i;
+    bcmcli_cmd_parm *parm = parms;
+
+    assert(name);
+    assert(help);
+    assert(cmd_cb);
+    if (!name || !cmd_cb || !help)
+        return BCM_ERR_PARM;
+
+    if (!_bcmcli_root_dir)
+    {
+        _bcmcli_alloc_root(NULL);
+        if (!_bcmcli_root_dir)
+            return BCM_ERR_NOMEM;
+    }
+
+    if (!dir)
+        dir = _bcmcli_root_dir;
+
+    p_token=(bcmcli_entry *)bcmos_calloc( sizeof(bcmcli_entry) + strlen(name) + strlen(help) + 2 );
+    if ( !p_token )
+        return BCM_ERR_NOMEM;
+
+    /* Copy name */
+    p_token->name = (char *)(p_token + 1);
+    strcpy( p_token->name, name );
+    p_token->help = p_token->name + strlen(name) + 1;
+    strcpy(p_token->help, help);
+    p_token->sel = BCMCLI_ENTRY_CMD;
+    p_token->u.cmd.cmd_cb = cmd_cb;
+    p_token->u.cmd.parms = parms;
+    if (extras)
+        p_token->u.cmd.extras = *extras;
+    p_token->access_right = access_right;
+
+    /* Convert name to lower case and choose alias */
+    _bcmcli_choose_alias(dir, p_token );
+
+
+    /* Check parameters */
+    for (i = 0; i < BCMCLI_MAX_PARMS && parms && parms[i].name; i++)
+    {
+        parm = &parms[i];
+        /* User-defined parameter must have a scan_cb callback for text->value conversion */
+        if ((parm->type==BCMCLI_PARM_USERDEF) && !parm->scan_cb)
+        {
+            bcmos_printf("MON: %s> scan_cb callback must be set for user-defined parameter %s\n", name, parm->name);
+            goto cmd_add_error;
+        }
+        if (parm->type==BCMCLI_PARM_ENUM || parm->type==BCMCLI_PARM_ENUM_MASK)
+        {
+            if (!parm->enum_table)
+            {
+                bcmos_printf("MON: %s> value table must be set in low_val for enum parameter %s\n", name, parm->name);
+                goto cmd_add_error;
+            }
+
+            /* Check default value if any */
+            if ((parm->flags & BCMCLI_PARM_FLAG_DEFVAL))
+            {
+                if (_bcmcli_enum_mask_scan_cb(parm, &parm->value, parm->value.string) < 0)
+                {
+                    bcmos_printf("MON: %s> default value %s doesn't match any value of enum parameter %s\n", name, parm->value.string, parm->name);
+                    goto cmd_add_error;
+                }
+            }
+            else if ((parm->flags & BCMCLI_PARM_FLAG_OPTIONAL))
+            {
+                /* Optional enum parameters are initialized by their 1st value by default.
+                 * All other parameters are initialized to 0.
+                 */
+                bcmcli_enum_val *values=parm->enum_table;
+                parm->value.enum_val = values[0].val;
+            }
+
+            /* All values of enum mask parameters mast be complementary bits */
+            if (parm->type==BCMCLI_PARM_ENUM_MASK)
+            {
+                long all_mask = 0;
+                bcmcli_enum_val *values;
+                for (values=parm->enum_table; values->name; ++values)
+                    all_mask |= values->val;
+
+                for (values=parm->enum_table; values->name; ++values)
+                {
+                    if ((all_mask & values->val) != values->val)
+                    {
+                        bcmos_printf("MON: %s> enum_table values of enum_mask parameters must be complementary bits\n", name, parm->name);
+                        goto cmd_add_error;
+                    }
+                    all_mask &= ~values->val;
+                }
+            }
+        }
+        else if (parm->type==BCMCLI_PARM_BUFFER)
+        {
+            if (!parm->value.buffer.start || !parm->value.buffer.len)
+            {
+                bcmos_printf("MON: %s> value.buffer.start is not set for BUFFER parameter %s\n", name, parm->name);
+                goto cmd_add_error;
+            }
+            if (parm->max_array_size)
+            {
+                bcmos_printf("MON: %s> BUFFER arrays are not supported %s\n", name, parm->name);
+                goto cmd_add_error;
+            }
+        }
+        if (parm->max_array_size)
+        {
+            if (!parm->values)
+            {
+                bcmos_printf("MON: %s> parm->values must be set for parameter-array %s\n", name, parm->name);
+                goto cmd_add_error;
+            }
+        }
+        _bcmcli_assign_callbacks(parm);
+    }
+    if ((i == BCMCLI_MAX_PARMS) && parms[i].name[0])
+    {
+        bcmos_printf("MON: %s> too many parameters\n", name);
+        goto cmd_add_error;
+    }
+    p_token->u.cmd.num_parms = i;
+
+    /* Add token to the directory */
+    p_token->parent = dir;
+    p_e = &(dir->u.dir.first);
+    while (*p_e)
+        p_e = &((*p_e)->next);
+    *p_e = p_token;
+
+    return 0;
+
+cmd_add_error:
+    bcmos_free( p_token );
+    return BCM_ERR_PARM;
+}
+
+
+/** Destroy token (command or directory)
+ * \param[in]   token           Directory or command token. NULL=root
+ */
+void bcmcli_token_destroy(bcmcli_entry *token)
+{
+    if (!token)
+    {
+        if (!_bcmcli_root_dir)
+            return;
+        token = _bcmcli_root_dir;
+    }
+    /* Remove from parent's list */
+    if (token->parent)
+    {
+        bcmcli_entry **p_e;
+        p_e = &(token->parent->u.dir.first);
+        while (*p_e)
+        {
+            if (*p_e == token)
+            {
+                *p_e = token->next;
+                break;
+            }
+            p_e = &((*p_e)->next);
+        }
+    }
+
+    /* Remove all directory entries */
+    if (token->sel == BCMCLI_ENTRY_DIR)
+    {
+        bcmcli_entry *e = token->u.dir.first;
+        while ((e = token->u.dir.first))
+            bcmcli_token_destroy(e);
+    }
+    else if (token->u.cmd.extras.free_parms)
+            token->u.cmd.extras.free_parms(token->u.cmd.parms);
+
+    /* Release the token */
+    bcmos_free(token);
+
+    if (token == _bcmcli_root_dir)
+    {
+        _bcmcli_root_dir = NULL;
+        if (_bcmcli_root_session)
+        {
+            bcmcli_session_close(_bcmcli_root_session->session);
+            _bcmcli_root_session = NULL;
+        }
+    }
+}
+
+/** Open monitor session
+ *
+ * Monitor supports multiple simultaneous sessions with different
+ * access rights.
+ * Note that there already is a default session with full administrative rights,
+ * that takes input from stdin and outputs to stdout.
+ * \param[in]   parm        Session parameters. Must not be allocated on the stack.
+ * \param[out]  p_session   Session handle
+ * \return
+ *      0   =OK\n
+ *      <0  =error code
+ */
+bcmos_errno bcmcli_session_open(const bcmcli_session_parm *parm, bcmcli_session **p_session)
+{
+    bcmcli_session *session;
+    bcmcli_session_extras *mon_session;
+    bcmcli_session_parm session_parms;
+    int rc;
+
+    assert(p_session);
+    if (!p_session)
+        return BCM_ERR_PARM;
+
+    if (!_bcmcli_root_dir)
+    {
+        _bcmcli_alloc_root(parm);
+        if (!_bcmcli_root_dir)
+            return BCM_ERR_NOMEM;
+    }
+    if (parm)
+        session_parms = *parm;
+    else
+    {
+        memset(&session_parms, 0, sizeof(session_parms));
+        session_parms.name = "unnamed";
+    }
+
+    /* Open comm session */
+    session_parms.extra_size = sizeof(bcmcli_session_extras);
+    rc = bcmcli_session_open_user(&session_parms, &session);
+    if (rc)
+        return rc;
+    mon_session = _bcmcli_session_data(session);
+    mon_session->curdir = _bcmcli_root_dir;
+    mon_session->session = session;
+
+    *p_session = session;
+
+    return 0;
+}
+
+#define BCMCLI_PARSE_RETURN(ret) \
+    do { \
+        rc = ret;   \
+        goto bcmcli_parse_out; \
+    } while (0)
+
+/* Parse a single command. Stop on ';' or EOL */
+static bcmos_errno bcmcli_parse_command(bcmcli_session *session)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    bcmcli_entry  *p_token;
+    bcmcli_name_value *pairs = NULL;
+    int stop_parsing = 0;
+    int npairs;
+    int i;
+    char *cmd_line;
+    bcmos_errno rc = BCM_ERR_OK;
+
+    session = mon_session->session;
+
+    /* Make a copy of command line - for logging */
+    cmd_line = bcmos_alloc(strlen(mon_session->p_inbuf) + 1);
+    if (!cmd_line)
+        return BCM_ERR_NOMEM;
+    strcpy(cmd_line, mon_session->p_inbuf);
+
+    /* Split string to name/value pairs */
+    rc = _bcmcli_split(mon_session, &pairs, &npairs);
+    if (rc)
+    {
+        if (rc == BCM_ERR_NOENT)
+            rc = BCM_ERR_OK;
+        BCMCLI_PARSE_RETURN(rc);
+    }
+
+    /* Interpret empty string as "display directory" */
+    if ( !npairs )
+    {
+        _bcmcli_display_dir(mon_session, mon_session->curdir );
+        BCMCLI_PARSE_RETURN(BCM_ERR_OK);
+    }
+
+    /* Identify parameters */
+    for (i=0; i<npairs && !rc && !stop_parsing; i++)
+    {
+        switch (pairs[i].type)
+        {
+        case BCMCLI_TOKEN_NAME:
+        case BCMCLI_TOKEN_VALUE:
+            /* Identify command. The 1st pair can't contain name, only value */
+            if (pairs[i].name)
+            {
+                bcmcli_session_print(session, "**ERR: %s is unexpected\n", pairs[i].name);
+                BCMCLI_PARSE_RETURN(BCM_ERR_PARM);
+            }
+            p_token = _bcmcli_search_token(mon_session->curdir, pairs[i].value);
+            if (p_token == NULL)
+            {
+                bcmcli_session_print(session, "**ERR: %s is unexpected\n", pairs[i].value);
+                BCMCLI_PARSE_RETURN(BCM_ERR_PARM);
+            }
+            /* Directory or command ? */
+            if (p_token->sel == BCMCLI_ENTRY_DIR)
+            {
+                mon_session->curdir = p_token;
+                _bcmcli_display_dir(mon_session, mon_session->curdir );
+            }
+            else
+            {
+                /* Function token */
+                mon_session->curcmd = p_token;
+                if (_bcmcli_parse_parms(mon_session, p_token, &pairs[i+1], npairs-i-1) < 0)
+                {
+                    _bcmcli_help_entry(mon_session, p_token, &pairs[i+1], npairs-i-1, BCMOS_TRUE);
+                    rc = BCM_ERR_PARM;
+                }
+                else
+                {
+                    _bcmcli_log_cmd(cmd_line);
+                    rc = p_token->u.cmd.cmd_cb(session, mon_session->cmd_parms, npairs-i-1 );
+                    if (rc)
+                    {
+                        char buffer[BCMCLI_MAX_QUAL_NAME_LENGTH];
+                        bcmcli_session_print(session, "MON: %s> failed with error code %s(%d)\n",
+                            _bcmcli_qualified_name(p_token, buffer, sizeof(buffer)),
+                                         bcmos_strerror(rc), rc);
+                    }
+                    _bcmcli_log_rc(rc);
+                    _bcmcli_free_session_value_status(mon_session);
+                }
+                stop_parsing = 1;
+            }
+            break;
+
+        case BCMCLI_TOKEN_UP: /* Go to upper directory */
+            if (mon_session->curdir->parent)
+                mon_session->curdir = mon_session->curdir->parent;
+            _bcmcli_display_dir(mon_session, mon_session->curdir );
+            break;
+
+        case BCMCLI_TOKEN_ROOT: /* Go to the root directory */
+            mon_session->curdir = _bcmcli_root_dir;
+            _bcmcli_display_dir(mon_session, mon_session->curdir );
+            break;
+
+        case BCMCLI_TOKEN_HELP: /* Display help */
+            if (i < npairs-1 &&
+                ((p_token = _bcmcli_search_token( mon_session->curdir, pairs[i+1].value)) != NULL ))
+            {
+                _bcmcli_help_entry(mon_session, p_token, &pairs[i+2], npairs-i-2, BCMOS_FALSE);
+            }
+            else
+            {
+                _bcmcli_help_dir(mon_session, mon_session->curdir);
+            }
+            stop_parsing = 1;
+            break;
+
+        default:
+            stop_parsing = 1;
+            break;
+        }
+    }
+
+bcmcli_parse_out:
+    if (pairs)
+        bcmos_free(pairs);
+    if (cmd_line)
+        bcmos_free(cmd_line);
+    return rc;
+
+}
+
+/** Context extension */
+bcmos_errno bcmcli_extend(bcmcli_session *session, char *input_str, char *insert_str, uint32_t insert_size)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    bcmcli_entry  *p_token;
+    bcmcli_name_value *pairs;
+    bcmos_bool last_is_space;
+    int npairs;
+    bcmos_errno rc = BCM_ERR_OK;
+
+    if (!mon_session || !mon_session->curdir || !input_str)
+        return BCM_ERR_PARM;
+
+    insert_str[0] = 0;
+    mon_session->p_inbuf = input_str;
+
+    last_is_space = strlen(input_str) && (input_str[strlen(input_str) - 1] == ' ');
+
+    /* Split string to name/value pairs */
+    rc = _bcmcli_split(mon_session, &pairs, &npairs);
+    if (rc)
+        return rc;
+
+    /* empty list - display list of commands */
+    if ( !npairs )
+    {
+        _bcmcli_display_dir(mon_session, mon_session->curdir );
+        BCMCLI_PARSE_RETURN(0);
+    }
+
+    /* Identify parameters */
+    switch (pairs[0].type)
+    {
+    case BCMCLI_TOKEN_NAME:
+    case BCMCLI_TOKEN_VALUE:
+        /* Identify command. The 1st pair can't contain name, only value */
+        if (pairs[0].name ||
+            !(p_token = _bcmcli_search_token(mon_session->curdir, pairs[0].value)))
+        {
+            _bcmcli_display_dir(mon_session, mon_session->curdir );
+            BCMCLI_PARSE_RETURN(BCM_ERR_PARM);
+        }
+
+        /* Directory or command ? */
+        if (p_token->sel != BCMCLI_ENTRY_CMD)
+            BCMCLI_PARSE_RETURN(BCM_ERR_OK);
+
+        /* Function token */
+        mon_session->curcmd = p_token;
+        rc = _bcmcli_extend_parms(mon_session, &pairs[1], npairs-1, last_is_space, insert_str, insert_size);
+        break;
+
+    default:
+        break;
+    }
+
+bcmcli_parse_out:
+    bcmos_free(pairs);
+    return rc;
+}
+
+/** Parse and execute input string.
+ * input_string can contain multiple commands delimited by ';'
+ *
+ * \param[in]   session         Session handle
+ * \param[in]   input_string    String to be parsed. May consist of multiple ';'-delimited commands
+ * \return
+ *      =0  - OK \n
+ *      BCM_ERR_PARM - parsing error\n
+ *      other - return code - as returned from command handler.
+ *            It is recommended to return -EINTR to interrupt monitor loop.
+ */
+bcmos_errno bcmcli_parse(bcmcli_session *session, char* input_string)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    uint32_t input_len;
+    int rc = 0;
+
+    if (!mon_session || !mon_session->curdir || !input_string)
+        return BCM_ERR_PARM;
+    input_len = strlen(input_string);
+    if (!input_len)
+        return 0;
+
+    /* cut CR, LF if any */
+    while (input_len && (input_string[input_len-1]=='\n' || input_string[input_len-1]=='\r'))
+        input_string[--input_len]=0;
+
+    mon_session->p_inbuf = input_string;
+    mon_session->stop_monitor = 0;
+
+    do {
+        rc = bcmcli_parse_command(session);
+    } while (mon_session->p_inbuf && mon_session->p_inbuf[0] && !mon_session->stop_monitor && !rc);
+
+    return rc;
+}
+
+/** Read input and parse iteratively until EOF or bcmcli_is_stopped()
+ *
+ * \param[in]   session         Session handle
+ * \return
+ *      =0  - OK \n
+ */
+bcmos_errno bcmcli_driver(bcmcli_session *session)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+
+    session = mon_session->session;
+    mon_session->stop_monitor = 0;
+    while (!bcmcli_is_stopped(session) &&
+          bcmcli_session_gets(session, mon_session->inbuf, sizeof(mon_session->inbuf)-1))
+    {
+        /* Session could've been stopped while in "gets". Check again and proceed if active */
+        if (!bcmcli_is_stopped(session))
+            bcmcli_parse(session, mon_session->inbuf);
+    }
+
+    return BCM_ERR_OK;
+}
+
+
+/* Stop monitor driver */
+void bcmcli_stop( bcmcli_session *session )
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    assert(mon_session);
+    mon_session->stop_monitor = 1;
+}
+
+/** Returns 1 if monitor session is stopped
+ * \param[in]   session         Session handle
+ * \returns 1 if monitor session stopped by bcmcli_stop()\n
+ * 0 otherwise
+ */
+bcmos_bool bcmcli_is_stopped(bcmcli_session *session)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    return mon_session->stop_monitor;
+}
+
+
+/** Get parameter number given its name.
+ * The function is intended for use by command handlers
+ * \param[in]       session         Session handle
+ * \param[in,out]   parm_name       Parameter name
+ * \return
+ *  >=0 - parameter number\n
+ *  <0  - parameter with this name doesn't exist
+ */
+int bcmcli_parm_number(bcmcli_session *session, const char *parm_name)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    int i;
+    if (!parm_name || !mon_session || !mon_session->curcmd)
+        return BCM_ERR_PARM;
+    for(i=0;
+        mon_session->cmd_parms[i].name &&
+            _bcmcli_stricmp( parm_name, mon_session->cmd_parms[i].name, -1);
+        i++)
+        ;
+    if (!mon_session->cmd_parms[i].name)
+        return BCM_ERR_PARM;
+    return i;
+}
+
+
+/** Get parameter by name
+ * The function is intended for use by command handlers
+ * \param[in]       session         Session handle
+ * \param[in]       parm_name       Parameter name
+ * \return
+ * parameter pointer or NULL if not found
+ */
+bcmcli_cmd_parm *bcmcli_parm_get(bcmcli_session *session, const char *parm_name)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    int nparm = bcmcli_parm_number(session, parm_name);
+    if (nparm < 0)
+    {
+        return NULL;
+    }
+    return &mon_session->cmd_parms[nparm];
+}
+
+
+/** Check if parameter is set
+ * \param[in]       session         Session handle
+ * \param[in]       parm_number     Parameter number
+ * \return
+ *  1 if parameter is set\n
+ *  0 if parameter is not set or parm_number is invalid
+ */
+bcmos_errno bcmcli_parm_check(bcmcli_session *session, int parm_number)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+
+    if (parm_number < 0 || !mon_session || !mon_session->curcmd)
+        return BCM_ERR_PARM;
+    if (parm_number >= mon_session->num_parms)
+        return BCM_ERR_PARM;
+    if (!(mon_session->cmd_parms[parm_number].flags & BCMCLI_PARM_FLAG_ASSIGNED))
+        return BCM_ERR_NOENT;
+    return BCM_ERR_OK;
+}
+
+
+/** Get enum's string value given its internal value
+ * \param[in]       session         Session handle
+ * \param[in]       parm_number     Parameter number
+ * \param[in]       value           Internal value
+ * \return
+ *      enum string value or NULL if parameter is not enum or
+ *      internal value is invalid
+ */
+const char *bcmcli_enum_parm_stringval(bcmcli_session *session, int parm_number, long value)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    int i;
+    bcmcli_enum_val *values;
+    if (parm_number < 0 || !mon_session || !mon_session->curcmd)
+        return NULL;
+    for(i=0; i<parm_number && mon_session->cmd_parms[i].name; i++)
+        ;
+    if (i < parm_number)
+        return NULL;
+    if (mon_session->cmd_parms[parm_number].type != BCMCLI_PARM_ENUM)
+        return NULL;
+    values = mon_session->cmd_parms[parm_number].enum_table;
+    return bcmcli_enum_stringval(values, value);
+}
+
+
+/* Get current directory handle */
+bcmcli_entry *bcmcli_dir_get(bcmcli_session *session)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    if (!mon_session)
+        return NULL;
+    return mon_session->curdir;
+}
+
+/* Set current directory */
+bcmos_errno bcmcli_dir_set(bcmcli_session *session, bcmcli_entry *dir)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    assert(mon_session);
+    if (!mon_session)
+        return BCM_ERR_PARM;
+    /* Check access rights */
+    if (!dir)
+        dir = _bcmcli_root_dir;
+    if (dir->access_right > bcmcli_session_access_right(mon_session->session))
+        return BCM_ERR_PERM;
+    mon_session->curdir = dir;
+    return 0;
+}
+
+/** Get token name
+ * \param[in]   token           Directory or command token
+ * \return      directory token name
+ */
+const char *bcmcli_token_name(bcmcli_entry *token)
+{
+    if (!token)
+        return NULL;
+    return token->name;
+}
+
+bcmcli_cmd_parm *bcmcli_find_named_parm(bcmcli_session *session, const char *name)
+{
+    bcmcli_cmd_parm * cmd_parm;
+
+    if ( !session || !name || *name=='\0')
+        return NULL;
+
+    cmd_parm = _bcmcli_find_named_parm(_bcmcli_session_data(session), name);
+    if(cmd_parm && (cmd_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED))
+    {
+        return cmd_parm;
+    }
+
+    return NULL;
+}
+
+/* Return TRUE if parameter value is set */
+bcmos_bool bcmcli_parm_value_is_set(bcmcli_session *session, bcmcli_cmd_parm *parm, uint32_t value_index)
+{
+    bcmcli_session_extras *mon_session=_bcmcli_session_data(session);
+    uint32_t parm_index = parm - mon_session->cmd_parms;
+
+    if (!mon_session)
+    {
+        bcmcli_print(NULL, "MON> Session %p is invalid\n", session);
+        return BCMOS_FALSE;
+    }
+
+    parm_index = parm - mon_session->cmd_parms;
+    if (parm_index >= BCMCLI_MAX_PARMS)
+    {
+        bcmcli_print(session, "MON> Parameter %p is invalid\n", parm);
+        return BCMOS_FALSE;
+    }
+
+    if (!parm->array_size)
+        return mon_session->value_status[parm_index].value_set;
+
+    if (value_index >= parm->array_size)
+        return BCMOS_FALSE;
+
+    return mon_session->value_status[parm_index].values_set[value_index];
+}
+
+bcmcli_cmd_parm *bcmcli_find_parm_by_prefix(bcmcli_session *session, const char *prefix)
+{
+    bcmcli_cmd_parm *cmd_parm;
+
+    if (session == NULL || prefix == NULL)
+    {
+        return NULL;
+    }
+
+    cmd_parm = _bcmcli_session_data(session)->cmd_parms;
+    while (cmd_parm->name != NULL)
+    {
+        if ((_bcmcli_stricmp(prefix, cmd_parm->name, strlen(prefix)) == 0) &&
+            ((cmd_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED) != 0))
+        {
+            return cmd_parm;
+        }
+        ++cmd_parm;
+    }
+
+    return NULL;
+}
+
+/** Print CLI parameter
+ * \param[in]       session         Session handle
+ * \param[in]       parm            Parameter
+ */
+void bcmcli_parm_print(bcmcli_session *session, const bcmcli_cmd_parm *parm)
+{
+    char buf[BCMCLI_MAX_PARM_VAL_LEN] = "";
+    if (parm->type != BCMCLI_PARM_BUFFER)
+    {
+        parm->format_cb(parm, parm->value, buf, sizeof(buf));
+        bcmcli_print(session, "%s\n", buf);
+    }
+    else
+    {
+        if (parm->value.buffer.len == 0)
+        {
+            bcmcli_print(session, "-\n");
+        }
+        else
+        {
+            bcmcli_print(session, "\n");
+            bcmcli_session_hexdump(session, parm->value.buffer.start, 0,
+                bcmolt_buf_get_used(&parm->value.buffer), NULL);
+        }
+    }
+}
+
+/** Enable / disable CLI command logging
+ * \param[in]   mode    Logging flags
+ * \param[in]   log     Log session. Must be set if mode != BCMCLI_CMD_LOG_NONE
+ * \return 0=OK or error <0
+ */
+bcmos_errno bcmcli_log_set(bcmcli_log_mode mode, bcmcli_session *log)
+{
+    if (mode != BCMCLI_LOG_NONE && log == NULL)
+    {
+        BCMOS_TRACE_ERR("log session must be set\n");
+        return BCM_ERR_PARM;
+    }
+    if (mode == BCMCLI_LOG_NONE)
+    {
+        _bcmcli_log_session = NULL;
+    }
+    else
+    {
+        _bcmcli_log_session = log;
+    }
+    _bcmcli_log_mode = mode;
+    return BCM_ERR_OK;
+}
+
+/** Write string to CLI log.
+ * The function is ignored if CLI logging is not enabled using bcmcli_log_set()
+ * \param[in]   format  printf-like format followed by arguments
+ */
+void bcmcli_log(const char *format, ...)
+{
+    va_list ap;
+    if (!_bcmcli_log_session)
+        return;
+    va_start(ap, format);
+    bcmcli_session_vprint(_bcmcli_log_session, format, ap);
+    va_end(ap);
+}
+
+/*********************************************************/
+/* Internal functions                                    */
+/*********************************************************/
+
+static void _bcmcli_log_cmd(const char *cmd)
+{
+    switch (_bcmcli_log_mode)
+    {
+    case BCMCLI_LOG_CLI:
+        bcmcli_log("%s\n", cmd);
+        break;
+    case BCMCLI_LOG_C_COMMENT:
+        bcmcli_log("/* %s */\n", cmd);
+        break;
+    default:
+        break;
+    }
+}
+
+static void _bcmcli_log_rc(bcmos_errno rc)
+{
+    switch (_bcmcli_log_mode)
+    {
+    case BCMCLI_LOG_CLI:
+        bcmcli_log("# CLI command completed: %s (%d)\n", bcmos_strerror(rc), rc);
+        break;
+    case BCMCLI_LOG_C_COMMENT:
+        bcmcli_log("/* CLI command completed: %s (%d) */\n", bcmos_strerror(rc), rc);
+        break;
+    default:
+        break;
+    }
+
+}
+
+static bcmcli_session *_bcmcli_help_session_open(bcmcli_session *main_session)
+{
+    bcmolt_string *help_scratch;
+    bcmcli_session *help_session;
+    bcmos_errno err;
+
+    bcmolt_string_create(&help_scratch, BCMCLI_HELP_BUFFER_SIZE);
+    err = bcmcli_session_open_string(&help_session, help_scratch);
+    if (err)
+    {
+        bcmcli_session_print(main_session, "CLI: can't create help session. Error %s\n", bcmos_strerror(err));
+        return NULL;
+    }
+
+    return help_session;
+}
+
+static void _bcmcli_help_session_print_and_close(bcmcli_session *main_session, bcmcli_session *help_session)
+{
+    bcmolt_string *str = bcmcli_session_user_priv(help_session);
+
+    bcmcli_session_print(main_session, "%s", bcmolt_string_get(str));
+    bcmcli_session_close(help_session);
+    bcmolt_string_destroy(str);
+}
+
+#ifdef CONFIG_LINENOISE
+static bcmos_errno _bcmcli_line_edit_cmd(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms)
+{
+    if (n_parms > 0)
+    {
+        if ((parm[0].flags & BCMCLI_PARM_FLAG_ASSIGNED))
+            linenoiseSetDumbTerminal(session->ln_session, ! parm[0].value.number);
+        if ((parm[1].flags & BCMCLI_PARM_FLAG_ASSIGNED))
+            linenoiseSetMultiLine(session->ln_session, parm[1].value.number);
+    }
+    else
+    {
+        int dumb = linenoiseGetDumbTerminal(session->ln_session);
+        int multiline = linenoiseGetMultiLine(session->ln_session);
+        bcmcli_session_print(session, "Line editing: %s  Multiline: %s\n",
+            dumb ? "off" : "on", multiline ? "on" : "off");
+    }
+    return BCM_ERR_OK;
+}
+#endif
+
+/* Allocate root directory and default session */
+static void _bcmcli_alloc_root(const bcmcli_session_parm *first_session_parm)
+{
+    bcmcli_session_parm session_parms;
+    bcmcli_session *session;
+    int rc;
+
+    /* The very first call. Allocate root structure */
+    if ((_bcmcli_root_dir=(bcmcli_entry *)bcmos_calloc(sizeof(bcmcli_entry) + strlen(BCMCLI_ROOT_HELP) + 2 )) == NULL)
+        return;
+    _bcmcli_root_dir->name = (char *)(_bcmcli_root_dir + 1);
+    _bcmcli_root_dir->help = (char *)(_bcmcli_root_dir->name + 1);
+    strcpy(_bcmcli_root_dir->help, BCMCLI_ROOT_HELP);
+    _bcmcli_root_dir->sel = BCMCLI_ENTRY_DIR;
+    _bcmcli_root_dir->access_right = BCMCLI_ACCESS_GUEST;
+
+    memset(&session_parms, 0, sizeof(session_parms));
+    session_parms.access_right = BCMCLI_ACCESS_ADMIN;
+    session_parms.extra_size = sizeof(bcmcli_session_extras);
+    session_parms.name = "monroot";
+    if (first_session_parm)
+    {
+        session_parms.line_edit_mode = first_session_parm->line_edit_mode;
+    }
+    rc = bcmcli_session_open(&session_parms, &session);
+    if (rc)
+    {
+        bcmos_free(_bcmcli_root_dir);
+        _bcmcli_root_dir = NULL;
+        _bcmcli_root_session = NULL;
+        return;
+    }
+    _bcmcli_root_session = _bcmcli_session_data(session);
+    _bcmcli_root_session->session = session;
+    _bcmcli_root_session->curdir = _bcmcli_root_dir;
+
+    /* Add command to disable/enable line editing */
+#ifdef CONFIG_LINENOISE
+    if (session_parms.line_edit_mode != BCMCLI_LINE_EDIT_DISABLE)
+    {
+        BCMCLI_MAKE_CMD(NULL, "~", "Enable/disable/query line editing", _bcmcli_line_edit_cmd,
+            BCMCLI_MAKE_PARM_ENUM("enable", "Enable line editing", bcmcli_enum_bool_table, BCMCLI_PARM_FLAG_OPTIONAL),
+            BCMCLI_MAKE_PARM_ENUM("multiline", "Enable multiline mode", bcmcli_enum_bool_table,
+                BCMCLI_PARM_FLAG_OPTIONAL));
+    }
+#endif
+}
+
+/* Display directory */
+static void _bcmcli_display_dir(bcmcli_session_extras *mon_session, bcmcli_entry *p_dir)
+{
+    bcmcli_session *session = mon_session->session;
+    bcmcli_entry *p_token;
+    bcmcli_entry *prev=NULL;
+    bcmcli_session *help_session = _bcmcli_help_session_open(session);
+
+    if (!help_session)
+        return;
+
+    bcmcli_session_print(help_session, "%s%s> ", (p_dir==_bcmcli_root_dir)?"":".../", p_dir->name );
+    p_token = p_dir->u.dir.first;
+    while ( p_token )
+    {
+        if (p_token->access_right <= bcmcli_session_access_right(session))
+        {
+            if (prev)
+                bcmcli_session_print(help_session, ", ");
+            bcmcli_session_print(help_session, "%s", p_token->name );
+            if (p_token->sel == BCMCLI_ENTRY_DIR )
+                bcmcli_session_print(help_session, "/");
+            prev = p_token;
+        }
+        p_token = p_token->next;
+    }
+    bcmcli_session_print(help_session, "\n");
+    _bcmcli_help_session_print_and_close(session, help_session);
+}
+
+
+/* Is character that can be used in a single token ? */
+static inline int _bcmcli_is_special_char(char c)
+{
+    if (!c)
+        return 0;
+    return (c == BCMCLI_HELP_CHAR || c == BCMCLI_COMMENT_CHAR || c == BCMCLI_EQUAL_CHAR);
+}
+
+/* Make a preliminary analizis of <name> token.
+ *   Returns a token type (Empty, Up, Root, Break, Name)
+ */
+static bcmcli_token_type _bcmcli_analyze_token( const char *name )
+{
+    if (!name[0] || name[0]==';')
+        return BCMCLI_TOKEN_EMPTY;
+
+    if (*name == BCMCLI_COMMENT_CHAR)
+        return BCMCLI_TOKEN_BREAK;
+
+    if (!strcmp(name, BCMCLI_UP_STR))
+        return BCMCLI_TOKEN_UP;
+
+    if (!strcmp(name, BCMCLI_ROOT_STR))
+        return BCMCLI_TOKEN_ROOT;
+
+    if (*name == BCMCLI_HELP_CHAR)
+        return BCMCLI_TOKEN_HELP;
+
+    return BCMCLI_TOKEN_VALUE;
+
+}
+
+
+/* isspace wrapper */
+static inline int _bcmcli_isspace(char c)
+{
+    return isspace((int)c);
+}
+
+/* Cut the first word from <p_inbuf>.
+ * - Return pointer to start of the word in p_word
+ * - 0 terminator is inserted in the end of the word
+ * - session->p_inbuf is updated to point after the word
+ * Returns token type
+ */
+static bcmcli_token_type _bcmcli_get_word(bcmcli_session_extras *mon_session, char **buf, char **p_word)
+{
+    bcmcli_token_type token_type;
+    char *p_inbuf = *buf;
+    char next_char = 0;
+    bcmos_bool quoted_string = BCMOS_FALSE;
+
+    /* Skip leading blanks */
+    while (*p_inbuf && (_bcmcli_isspace(*p_inbuf) || (*p_inbuf==',')))
+        ++p_inbuf;
+
+    *buf = p_inbuf;
+    if (! *p_inbuf)
+        return BCMCLI_TOKEN_EMPTY;
+    if (*p_inbuf == ';')
+    {
+        *p_inbuf = 0;
+        *buf = ++p_inbuf;
+        return BCMCLI_TOKEN_EMPTY;
+    }
+
+    /* Quoted string ? */
+    if (*p_inbuf == '"')
+    {
+        quoted_string = BCMOS_TRUE;
+        *p_word = ++p_inbuf;
+        while ( *p_inbuf && *p_inbuf!='"' )
+            ++p_inbuf;
+        if (*p_inbuf != '"')
+        {
+            bcmcli_session_print(mon_session->session, "MON: unterminated string %s\n", *p_word);
+            return BCMCLI_TOKEN_EMPTY;
+        }
+        if (*p_inbuf)
+            *(p_inbuf++) = 0;
+    }
+    else
+    {
+        *p_word = p_inbuf;
+        if (!_bcmcli_is_special_char(*p_inbuf))
+        {
+            do ++p_inbuf;
+            while (*p_inbuf && !_bcmcli_isspace(*p_inbuf) && *p_inbuf!=';' && !_bcmcli_is_special_char(*p_inbuf));
+            /* Skip trailing spaces */
+            while (*p_inbuf && _bcmcli_isspace(*p_inbuf))
+                *(p_inbuf++) = 0;
+            next_char = *p_inbuf;
+            if (next_char == BCMCLI_EQUAL_CHAR)
+                *(p_inbuf++) = 0;
+        }
+        else
+        {
+            ++p_inbuf;
+        }
+    }
+    *buf = p_inbuf;
+    token_type   = _bcmcli_analyze_token( *p_word );
+    if (token_type == BCMCLI_TOKEN_VALUE && next_char == BCMCLI_EQUAL_CHAR)
+        token_type = BCMCLI_TOKEN_NAME;
+    if ((token_type == BCMCLI_TOKEN_EMPTY) && quoted_string)
+        token_type = BCMCLI_TOKEN_VALUE;
+    return token_type;
+}
+
+/* Split string to [name=]value pairs */
+static bcmos_errno _bcmcli_split(bcmcli_session_extras *mon_session, bcmcli_name_value **p_pairs, int *p_npairs)
+{
+    bcmcli_name_value *pairs;
+    char *tmp_buf, *tmp_buf_org;
+    char *word;
+    bcmcli_token_type token_type, prev_type=BCMCLI_TOKEN_EMPTY;
+    int n = 0;
+
+    /* Make a copy of input buffer */
+    tmp_buf_org = tmp_buf = bcmos_alloc(strlen(mon_session->p_inbuf) + 1);
+    if (!tmp_buf)
+        return BCM_ERR_NOMEM;
+    strcpy(tmp_buf, mon_session->p_inbuf);
+
+    /* Calculate number of pairs first */
+    token_type = _bcmcli_get_word(mon_session, &tmp_buf, &word);
+    while (token_type != BCMCLI_TOKEN_EMPTY && token_type != BCMCLI_TOKEN_BREAK)
+    {
+        /* Skip =value */
+        if (!(prev_type == BCMCLI_TOKEN_NAME && token_type == BCMCLI_TOKEN_VALUE))
+            ++n;
+        prev_type = token_type;
+        token_type = _bcmcli_get_word(mon_session, &tmp_buf, &word);
+    }
+    bcmos_free(tmp_buf_org);
+    *p_npairs = n;
+    if (!n)
+    {
+        *p_pairs = NULL;
+        /* Cut input string in order to prevent infinite loop in the parser if the string
+         * is not empty (e.g., contains spaces) */
+        *mon_session->p_inbuf = 0;
+        if (token_type == BCMCLI_TOKEN_BREAK)
+        {
+            return BCM_ERR_NOENT;
+        }
+        return 0;
+    }
+
+    *p_pairs = pairs = bcmos_calloc(n * sizeof(bcmcli_name_value));
+    if (! pairs)
+        return BCM_ERR_NOMEM;
+
+    /* Now scan the original string and set names and values */
+    token_type = _bcmcli_get_word(mon_session, &mon_session->p_inbuf, &word);
+    prev_type=BCMCLI_TOKEN_EMPTY;
+    --pairs; /* it is going to be pre-incremented */
+    while (token_type != BCMCLI_TOKEN_EMPTY && token_type != BCMCLI_TOKEN_BREAK)
+    {
+        if (!(prev_type == BCMCLI_TOKEN_NAME && token_type == BCMCLI_TOKEN_VALUE))
+            ++pairs;
+        pairs->type = token_type;
+        if (token_type == BCMCLI_TOKEN_NAME)
+        {
+            pairs->name = word;
+        }
+        else
+        {
+            pairs->value = word;
+        }
+        prev_type = token_type;
+        token_type = _bcmcli_get_word(mon_session, &mon_session->p_inbuf, &word);
+    }
+    return 0;
+}
+
+/* Find parameter by name */
+static bcmcli_cmd_parm *_bcmcli_find_named_parm(bcmcli_session_extras *mon_session, const char *name)
+{
+    bcmcli_cmd_parm *cmd_parm = mon_session->cmd_parms;
+
+    while (cmd_parm->name)
+    {
+        if (!_bcmcli_stricmp(name, cmd_parm->name, -1))
+            break;
+        ++cmd_parm;
+    }
+
+    if (!cmd_parm->name)
+        return NULL;
+
+    return cmd_parm;
+}
+
+/* Extend session parameter table based on selector value */
+static bcmos_errno _bcmcli_extend_parm_table(bcmcli_session_extras *mon_session,
+    bcmcli_cmd_parm *selector, const char *value)
+{
+    bcmcli_enum_val *values=selector->enum_table;
+    bcmcli_cmd_parm *parms;
+    bcmcli_cmd_parm *session_parm;
+    int nsel = selector - mon_session->cmd_parms;
+    bcmcli_parm_value_status *val_status;
+    int nparms;
+
+    while (values->name)
+    {
+        if (!_bcmcli_stricmp(values->name, value, -1))
+            break;
+        ++values;
+    }
+    if (!values->name)
+        return BCM_ERR_INTERNAL;
+
+    /* Calculate number of parameters in selected table */
+    parms = values->parms;
+    while (parms && parms->name)
+    {
+        ++parms;
+    }
+    nparms = parms - values->parms;
+
+    if (mon_session->num_parms + nparms >= BCMCLI_MAX_PARMS)
+    {
+        bcmcli_session_print(mon_session->session, "MON: %s> Can's process selector %s. Too many parameters\n",
+            mon_session->curcmd->name, selector->name);
+        return BCM_ERR_OVERFLOW;
+    }
+
+    /* Shift session parameters making room for the new table */
+    if (selector != &mon_session->cmd_parms[mon_session->num_parms-1])
+    {
+        memmove(selector + nparms + 1, selector + 1,
+            (&mon_session->cmd_parms[mon_session->num_parms-1] - selector) * sizeof(bcmcli_cmd_parm));
+        memmove(&mon_session->value_status[nsel + nparms + 1], &mon_session->value_status[nsel + 1],
+            (mon_session->num_parms - nsel) * sizeof(bcmcli_parm_value_status));
+    }
+
+    /* Finally insert selector's table */
+    parms = values->parms;
+    session_parm = selector+1;
+    val_status = &mon_session->value_status[nsel + 1];
+    while (parms && parms->name)
+    {
+        *session_parm = *parms;
+        _bcmcli_assign_callbacks(session_parm);
+
+        if (parms->max_array_size)
+        {
+            val_status->values_set = bcmos_calloc(sizeof(bcmos_bool) * parms->max_array_size);
+            if (!val_status->values_set)
+            {
+                bcmcli_session_print(mon_session->session, "MON: > Couldn't allocate value status array for %s\n",
+                    parms->name);
+                return BCM_ERR_NOMEM;
+            }
+        }
+        else
+        {
+            val_status->value_set = BCMOS_FALSE;
+        }
+
+
+        ++parms;
+        ++session_parm;
+        ++val_status;
+    }
+    mon_session->num_parms += nparms;
+
+    return BCM_ERR_OK;
+}
+
+/* Parse a single parameter value (scalar value or array element) */
+static bcmos_errno _bcmcli_parse_1value(bcmcli_session_extras *mon_session, bcmcli_entry *cmd,
+    bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_value,
+    int val_len, bcmos_bool suppress_err_print)
+{
+    bcmos_errno rc;
+
+    if (val_len >= 0)
+    {
+        /* We are dealing with array element. string_value is comma rather than
+         * 0-terminated. Copy it aside.
+         */
+        char val_copy[val_len + 1];
+        strncpy(val_copy, string_value, val_len);
+        val_copy[val_len] = 0;
+        rc = parm->scan_cb(parm, value, val_copy);
+    }
+    else
+    {
+        rc = parm->scan_cb(parm, value, string_value);
+    }
+    if (rc)
+    {
+        if (!suppress_err_print)
+        {
+            bcmcli_session_print(mon_session->session, "MON: %s> <%s>: value %s is invalid\n",
+                cmd->name, parm->name, string_value);
+        }
+    }
+    return rc;
+}
+
+
+/* Parse parameter value, including array value (comma-delimited list of element values) */
+static bcmos_errno _bcmcli_parse_value(bcmcli_session_extras *mon_session, bcmcli_entry *cmd,
+    bcmcli_cmd_parm *parm, const char *string_value, bcmos_bool suppress_err_print)
+{
+    bcmos_errno rc = BCM_ERR_OK;
+    uint32_t parm_index = parm - mon_session->cmd_parms;
+
+    BUG_ON(parm_index >= BCMCLI_MAX_PARMS);
+
+    if (parm->max_array_size)
+    {
+        uint32_t i = 0;
+
+        /* Empty array? */
+        if (_bcmcli_stricmp(string_value, BCMCLI_ARRAY_EMPTY, -1))
+        {
+            /* array element values are comma-delimited */
+            for (i = 0; i < parm->max_array_size && string_value && *string_value && !rc; i++)
+            {
+                const char *pcomma;
+                int val_len;
+
+                pcomma = strchr(string_value, BCMCLI_ARRAY_DELIM_CHAR);
+                if (pcomma)
+                {
+                    val_len = pcomma - string_value;
+                }
+                else
+                {
+                    val_len = -1; /* to the end of string */
+                }
+                /* No value ? */
+                if (_bcmcli_stricmp(string_value, BCMCLI_PARM_NO_VALUE, val_len))
+                {
+                    rc = _bcmcli_parse_1value(mon_session, cmd,
+                        parm, &parm->values[i], string_value, val_len, suppress_err_print);
+                    mon_session->value_status[parm_index].values_set[i] = (rc == BCM_ERR_OK);
+                }
+                string_value = pcomma ? pcomma + 1 : NULL;
+            }
+            /* If all parsed values were ok, but we have more values than array size - it is an error */
+            if (string_value && *string_value && !rc)
+            {
+                rc = BCM_ERR_TOO_MANY;
+                if (!suppress_err_print)
+                {
+                    bcmcli_session_print(mon_session->session, "MON: %s> <%s>: too many values. %s is invalid\n",
+                        cmd->name, parm->name, string_value);
+                }
+            }
+        }
+
+        parm->array_size = i;
+    }
+    else
+    {
+        if (_bcmcli_stricmp(string_value, BCMCLI_PARM_NO_VALUE, strlen(string_value)))
+        {
+            rc = _bcmcli_parse_1value(mon_session, cmd,
+                parm, &parm->value, string_value, -1, suppress_err_print);
+            mon_session->value_status[parm_index].value_set = (rc == BCM_ERR_OK);
+        }
+    }
+
+    return rc;
+}
+
+/* Release value status arrays */
+static void _bcmcli_free_session_value_status(bcmcli_session_extras *mon_session)
+{
+    bcmcli_cmd_parm *parms=mon_session->cmd_parms;
+    int i;
+
+    for (i = 0; i < BCMCLI_MAX_PARMS; i++)
+    {
+        if (parms[i].max_array_size)
+        {
+            if (mon_session->value_status[i].values_set != NULL)
+            {
+                bcmos_free(mon_session->value_status[i].values_set);
+                mon_session->value_status[i].values_set = NULL;
+            }
+        }
+        else
+        {
+            mon_session->value_status[i].value_set = BCMOS_FALSE;
+        }
+    }
+}
+
+/* Populate session parameters. Apply selectors */
+static bcmos_errno _bcmcli_populate_parms(bcmcli_session_extras *mon_session, bcmcli_entry *cmd,
+    bcmcli_name_value *pairs, int npairs, bcmos_bool suppress_err_print, int *last)
+{
+    const char *parm_value;
+    int positional=1;
+    bcmcli_cmd_parm *parms=mon_session->cmd_parms;
+    bcmcli_cmd_parm *cur_parm;
+    int rc;
+    int i;
+
+    /* Mark all parameters as don't having an explicit value */
+    memset(&parms[0], 0, sizeof(mon_session->cmd_parms));
+    memcpy(&parms[0], cmd->u.cmd.parms, sizeof(bcmcli_cmd_parm)*cmd->u.cmd.num_parms);
+    /* Clear array buffers */
+    for (i = 0; i < cmd->u.cmd.num_parms; i++)
+    {
+        if (parms[i].max_array_size)
+        {
+            BUG_ON(!parms[i].values);
+            memset(parms[i].values, 0, sizeof(bcmcli_parm_value) * parms[i].max_array_size);
+            mon_session->value_status[i].values_set = bcmos_calloc(sizeof(bcmos_bool) * parms[i].max_array_size);
+            if (!mon_session->value_status[i].values_set)
+            {
+                bcmcli_session_print(mon_session->session, "MON: %s> Couldn't allocate value status array for %s\n",
+                    cmd->name, pairs[i].name);
+                return BCM_ERR_NOMEM;
+            }
+        }
+        else
+        {
+            mon_session->value_status[i].value_set = BCMOS_FALSE;
+        }
+    }
+    mon_session->curcmd = cmd;
+    mon_session->num_parms = cmd->u.cmd.num_parms;
+    if (last)
+        *last = 0;
+    /* Build a format string */
+    for (i=0; i<npairs && pairs[i].type != BCMCLI_TOKEN_BREAK; i++)
+    {
+        parm_value = pairs[i].value;
+        if (last)
+            *last = i;
+        cur_parm = NULL;
+        /* Named parameter ? */
+        if (pairs[i].name)
+        {
+            if ( (cmd->u.cmd.extras.flags & BCMCLI_CMD_FLAG_NO_NAME_PARMS) )
+            {
+                if (!suppress_err_print)
+                {
+                    bcmcli_session_print(mon_session->session, "MON: %s> Doesn't support named parameters. %s is unexpected\n",
+                        cmd->name, pairs[i].name);
+                }
+                return BCM_ERR_PARM;
+            }
+            positional = 0; /* No more positional parameters */
+            /* Check name */
+            cur_parm = _bcmcli_find_named_parm(mon_session, pairs[i].name);
+            if (!cur_parm)
+            {
+                if (!suppress_err_print)
+                {
+                    bcmcli_session_print(mon_session->session, "MON: %s> parameter <%s> doesn't exist\n",
+                        cmd->name, pairs[i].name);
+                }
+                return BCM_ERR_PARM;
+            }
+            if (!parm_value)
+            {
+                if (!suppress_err_print)
+                {
+                    bcmcli_session_print(mon_session->session, "MON: %s> <%s>: value is missing\n",
+                        cmd->name, cur_parm->name);
+                }
+                return BCM_ERR_PARM;
+            }
+        }
+        else
+        {
+            /* it can still be named ENUM parameter (without =value). In this case the 1st
+             * enum value is assumed. Check it
+             */
+            if (parm_value && (cur_parm = _bcmcli_find_named_parm(mon_session, parm_value)) &&
+                (cur_parm->type == BCMCLI_PARM_ENUM))
+            {
+                pairs[i].name = parm_value;
+                pairs[i].value = parm_value = cur_parm->enum_table->name;
+                positional = 0; /* No more positional parameters */
+            }
+            else
+            {
+                if (!positional)
+                {
+                    if (!suppress_err_print)
+                        bcmcli_session_print(mon_session->session, "MON: %s> Expected named parameter. Got %s\n", cmd->name, parm_value);
+                    return BCM_ERR_PARM;
+                }
+                cur_parm = &parms[i];
+            }
+            if (!cur_parm->name)
+            {
+                if (!suppress_err_print)
+                    bcmcli_session_print(mon_session->session, "MON: %s> Too many parameters. %s is unexpected\n", cmd->name, parm_value);
+                return BCM_ERR_PARM;
+            }
+        }
+
+        if (cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED)
+        {
+            if (!suppress_err_print)
+            {
+                bcmcli_session_print(mon_session->session, "MON: %s> Attempt to assign parameter %s more than once\n",
+                    cmd->name, cur_parm->name);
+            }
+            return BCM_ERR_PARM;
+        }
+
+        if (parm_value)
+        {
+            if (cur_parm->type == BCMCLI_PARM_STRING)
+                cur_parm->value.string = parm_value;
+            else
+            {
+                rc = _bcmcli_parse_value(mon_session, cmd, cur_parm, parm_value, suppress_err_print);
+                if (rc)
+                    return rc;
+
+                /* For parameter-selector extend list of parameters accordingly */
+                if (cur_parm->flags & BCMCLI_PARM_FLAG_SELECTOR)
+                {
+                    rc = _bcmcli_extend_parm_table(mon_session, cur_parm, parm_value);
+                    if (rc)
+                        return rc;
+                }
+            }
+            cur_parm->flags |= BCMCLI_PARM_FLAG_ASSIGNED;
+        }
+    }
+    return BCM_ERR_OK;
+}
+
+/* Parse p_inbuf string based on parameter descriptions in <p_token>.
+ *   Fill parameter values in <p_token>.
+ *   Returns the number of parameters filled or BCM_ERR_PARM
+ *   To Do: add a option of one-by-one user input of missing parameters.
+ */
+static int _bcmcli_parse_parms( bcmcli_session_extras *mon_session, bcmcli_entry *cmd, bcmcli_name_value *pairs, int npairs)
+{
+    bcmcli_cmd_parm *parms=mon_session->cmd_parms;
+    int rc;
+    int i;
+
+    /* Populate parameter table */
+    rc = _bcmcli_populate_parms(mon_session, cmd, pairs, npairs, BCMOS_FALSE, NULL);
+    if (rc)
+        goto err_return;
+
+
+    rc = BCM_ERR_PARM;
+
+    /* Make sure that parameters are OK. Check range, process default values */
+    for (i=0; i<mon_session->num_parms; i++)
+    {
+        bcmcli_cmd_parm *cur_parm = &parms[i];
+
+        if (!(cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED))
+        {
+            if ((cur_parm->flags & BCMCLI_PARM_FLAG_DEFVAL))
+            {
+                cur_parm->flags |= BCMCLI_PARM_FLAG_ASSIGNED;
+            }
+            else if (!(cur_parm->flags & BCMCLI_PARM_FLAG_OPTIONAL) )
+            {
+                /* Mandatory parameter missing */
+                bcmcli_session_print(mon_session->session, "MON: %s> Mandatory parameter <%s> is missing\n", cmd->name, parms[i].name);
+                goto err_return;
+            }
+        }
+
+        /* Check value */
+        if ((cur_parm->flags & BCMCLI_PARM_FLAG_RANGE))
+        {
+            if ((cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED))
+            {
+                if (cur_parm->array_size)
+                {
+                    uint32_t j;
+
+                    for (j = 0; j < cur_parm->array_size; j++)
+                    {
+                        if (((cur_parm->values[j].number < cur_parm->low_val) ||
+                            (cur_parm->values[j].number > cur_parm->hi_val)))
+                        {
+                            bcmcli_session_print(mon_session->session, "MON: %s> <%s>: %ld out of range (%ld, %ld)\n",
+                                cmd->name, cur_parm->name, cur_parm->values[j].number, cur_parm->low_val, cur_parm->hi_val);
+                            goto err_return;
+                        }
+                    }
+                }
+                else if (((cur_parm->value.number < cur_parm->low_val) ||
+                    (cur_parm->value.number > cur_parm->hi_val)))
+                {
+                    bcmcli_session_print(mon_session->session, "MON: %s> <%s>: %ld out of range (%ld, %ld)\n",
+                        cmd->name, cur_parm->name, cur_parm->value.number, cur_parm->low_val, cur_parm->hi_val);
+                    goto err_return;
+                }
+            }
+        }
+    }
+
+    return BCM_ERR_OK;
+
+err_return:
+    _bcmcli_free_session_value_status(mon_session);
+    return rc;
+}
+
+/* insert value skipping partial match trhat is already present */
+static void _bcmcli_insert(const char *partial_match, const char *insert_val1,
+    const char *insert_val2, char *insert_str, uint32_t insert_size)
+{
+    if (partial_match)
+        insert_val1 += strlen(partial_match);
+    bcmcli_strncpy(insert_str, insert_val1, insert_size);
+    if (insert_val2)
+        bcmcli_strncat(insert_str, insert_val2, insert_size);
+}
+
+static void _bcmcli_update_longest_match(char *longest_match, const char *name)
+{
+    uint32_t nlen = strlen(name);
+    uint32_t lmlen = strlen(longest_match);
+
+    if (nlen < lmlen)
+    {
+        lmlen = nlen;
+    }
+    while (lmlen && memcmp(longest_match, name, lmlen))
+    {
+        --lmlen;
+    }
+    longest_match[lmlen] = 0;
+}
+
+
+/* extend value.
+ * If !enum - do nothing
+ * If more than 1 matching value - display them
+ * If no matching value - do nothing
+ * If 1 matching value - insert
+ */
+static void _bcmcli_extend_value(bcmcli_session_extras *mon_session, bcmcli_cmd_parm *parm,
+    const char *partial_value, char *insert_str, uint32_t insert_size)
+{
+    int nmatch = 0;
+    bcmcli_enum_val *vals = parm->enum_table;
+    char longest_match[BCMCLI_MAX_SEARCH_SUBSTR_LENGTH]="";
+
+    if ((parm->type != BCMCLI_PARM_ENUM && parm->type != BCMCLI_PARM_ENUM_MASK) || !vals)
+        return;
+
+    /* If enum mask, partial value can be a sum of values. Skip past the last '+' sign */
+    if (parm->type == BCMCLI_PARM_ENUM_MASK && partial_value)
+    {
+        char *pdel = strrchr(partial_value, BCMCLI_ENUM_MASK_DEL_CHAR);
+        if (pdel)
+            partial_value = pdel + 1;
+    }
+
+    while (vals->name)
+    {
+        if (!partial_value || !strncmp(vals->name, partial_value, strlen(partial_value)))
+        {
+            if (!nmatch)
+            {
+                bcmcli_strncpy(longest_match, vals->name, sizeof(longest_match));
+            }
+            else
+            {
+                _bcmcli_update_longest_match(longest_match, vals->name);
+            }
+            ++nmatch;
+        }
+        ++vals;
+    }
+    if (!nmatch)
+        return;
+    if (nmatch == 1)
+    {
+        _bcmcli_insert(partial_value, longest_match, " ", insert_str, insert_size);
+        return;
+    }
+    /* display all matching values */
+    _bcmcli_insert(partial_value, longest_match, "", insert_str, insert_size);
+    bcmcli_session_print(mon_session->session, "\n");
+    vals = parm->enum_table;
+    while (vals->name)
+    {
+        if (!partial_value || !strncmp(vals->name, partial_value, strlen(partial_value)))
+            bcmcli_session_print(mon_session->session, " %s", vals->name);
+        ++vals;
+    }
+    bcmcli_session_print(mon_session->session, "\n");
+}
+
+/* calculate number of matching parameter names */
+static int _bcmcli_num_matching_names(bcmcli_session_extras *mon_session, const char *partial_value, int *first_match)
+{
+    int i;
+    int nmatch = 0;
+
+    *first_match = -1;
+    for (i = 0; i < mon_session->num_parms; i++)
+    {
+        uint32_t flags = mon_session->cmd_parms[i].flags;
+        if ((flags & BCMCLI_PARM_FLAG_ASSIGNED))
+            continue;
+        if (partial_value && strncmp(mon_session->cmd_parms[i].name, partial_value, strlen(partial_value)))
+            continue;
+        if (*first_match == -1)
+            *first_match = i;
+        ++nmatch;
+    }
+    return nmatch;
+}
+
+/* calculate longest matching string.
+ * returns number of matching parameters
+ */
+static int _bcmcli_longest_match(bcmcli_session_extras *mon_session, const char *partial_value,
+    char *longest_match, uint32_t longest_match_size, int *first_match)
+{
+    int nmatch0 = _bcmcli_num_matching_names(mon_session, partial_value, first_match);
+    int nmatch;
+    const char *match_name;
+
+    if (!nmatch0)
+        return nmatch0;
+    match_name = mon_session->cmd_parms[*first_match].name;
+    if (nmatch0 == 1)
+    {
+        bcmcli_strncpy(longest_match, match_name, longest_match_size);
+        return nmatch0;
+    }
+    bcmcli_strncpy(longest_match, match_name, longest_match_size);
+    nmatch = _bcmcli_num_matching_names(mon_session, longest_match, first_match);
+    while (nmatch != nmatch0)
+    {
+        longest_match[strlen(longest_match)-1] = 0;
+        nmatch = _bcmcli_num_matching_names(mon_session, longest_match, first_match);
+    }
+    return nmatch0;
+}
+
+/* display/insert unset matching names
+ * If more than 1 matching value - display them
+ * If no matching value - do nothing
+ * If 1 matching value - insert
+ */
+static void _bcmcli_extend_name(bcmcli_session_extras *mon_session, const char *partial_value,
+    char *insert_str, uint32_t insert_size)
+{
+    char longest_match[BCMCLI_MAX_SEARCH_SUBSTR_LENGTH]="";
+    int first_match;
+    int nmatch = _bcmcli_longest_match(mon_session, partial_value, longest_match,
+        sizeof(longest_match), &first_match);
+
+    if (!nmatch)
+        return;
+    if (!partial_value || strcmp(partial_value, longest_match))
+        _bcmcli_insert(partial_value, longest_match, (nmatch == 1) ? "=" : "", insert_str, insert_size);
+    else
+        _bcmcli_help_populated_cmd(mon_session, mon_session->curcmd, partial_value, BCMOS_TRUE);
+}
+
+static int _bcmcli_extend_parms( bcmcli_session_extras *mon_session, bcmcli_name_value *pairs,
+    int npairs, bcmos_bool last_is_space, char *insert_str, uint32_t insert_size)
+{
+    bcmos_errno rc;
+    int last = 0;
+    bcmcli_cmd_parm *help_parm = NULL;
+    int i;
+
+    rc = _bcmcli_populate_parms(mon_session, mon_session->curcmd, pairs, npairs, BCMOS_TRUE, &last);
+    if (!rc)
+    {
+        /* So far so good */
+        /* If there is unset mandatory parameter - insert its name.
+         * Otherwise, display list of unset parameters
+         */
+        /* Find mandatory parameter that is still unassigned */
+        for (i = 0; i < mon_session->num_parms; i++)
+        {
+            uint32_t flags = mon_session->cmd_parms[i].flags;
+            if (!(flags & (BCMCLI_PARM_FLAG_OPTIONAL | BCMCLI_PARM_FLAG_DEFVAL | BCMCLI_PARM_FLAG_ASSIGNED)))
+            {
+                help_parm = &mon_session->cmd_parms[i];
+                break;
+            }
+        }
+        if (help_parm)
+        {
+            if (!last_is_space)
+                bcmcli_strncpy(insert_str, " ", insert_size);
+            bcmcli_strncat(insert_str, help_parm->name, insert_size);
+            bcmcli_strncat(insert_str, "=", insert_size);
+        }
+        else if (last < mon_session->num_parms)
+            _bcmcli_help_populated_cmd(mon_session, mon_session->curcmd, NULL, BCMOS_TRUE);
+    }
+    else
+    {
+        /* Parsing failed. See what stopped at */
+        if (last < mon_session->num_parms)
+        {
+            bcmcli_name_value *last_pair;
+
+            last_pair = &pairs[last];
+            if (last_pair->name)
+            {
+                /* Try to identify by name */
+                help_parm = _bcmcli_find_named_parm(mon_session, last_pair->name ? last_pair->name : last_pair->value);
+            }
+            if (help_parm)
+            {
+                /* Looking for values */
+                _bcmcli_extend_value(mon_session, help_parm, last_pair->value, insert_str, insert_size);
+            }
+            else
+            {
+                /* Looking for partial name */
+                _bcmcli_extend_name(mon_session, last_pair->name ? last_pair->name : last_pair->value,
+                    insert_str, insert_size);
+            }
+        }
+    }
+    _bcmcli_free_session_value_status(mon_session);
+
+    return BCM_ERR_OK;
+}
+
+/* Identify token in the given directory */
+static bcmcli_entry *_bcmcli_search_token1( bcmcli_entry *p_dir, const char **p_name, int name_len )
+{
+    bcmcli_entry *p_token = NULL;
+    const char *name = *p_name;
+    bcmcli_token_type type=_bcmcli_analyze_token(name);
+
+    /* Name can be qualified */
+    if (type == BCMCLI_TOKEN_VALUE && !strncmp(name, BCMCLI_UP_STR, name_len))
+        type = BCMCLI_TOKEN_UP;
+
+    switch(type)
+    {
+        case BCMCLI_TOKEN_ROOT:
+            p_token = _bcmcli_root_dir;
+            *p_name = name + strlen(BCMCLI_ROOT_STR);
+            break;
+        case BCMCLI_TOKEN_UP:
+            if (p_dir->parent)
+                p_token = p_dir->parent;
+            else
+                p_token = p_dir;
+            *p_name = name + strlen(BCMCLI_UP_STR) + 1;
+            break;
+        case BCMCLI_TOKEN_NAME:
+        case BCMCLI_TOKEN_VALUE:
+            /* Check alias */
+            p_token = p_dir->u.dir.first;
+            while ( p_token )
+            {
+                if (p_token->alias &&
+                        (name_len == p_token->alias_len) &&
+                        !_bcmcli_stricmp(p_token->alias, name, p_token->alias_len))
+                    break;
+                p_token = p_token->next;
+            }
+            if (!p_token)
+            {
+                bcmcli_entry *partial_match = NULL;
+                /* Check name */
+                p_token = p_dir->u.dir.first;
+                while( p_token )
+                {
+                    if (!_bcmcli_stricmp(p_token->name, name, name_len))
+                    {
+                        if (name_len == strlen(p_token->name))
+                            break;
+                        if (!partial_match)
+                            partial_match = p_token;
+                    }
+                    p_token = p_token->next;
+                }
+                if (!p_token)
+                    p_token = partial_match;
+            }
+            *p_name = name + name_len + 1;
+            break;
+        default:
+            break;
+    }
+
+    return p_token;
+}
+
+
+/* Search a token by name in the current directory.
+ * The name can be qualified (contain path)
+ */
+static bcmcli_entry *_bcmcli_search_token( bcmcli_entry *p_dir, const char *name )
+{
+    bcmcli_entry *p_token;
+    const char *name0 = name;
+    const char *p_slash;
+
+    if (!name[0])
+        return p_dir;
+
+    /* Check if name is qualified */
+    do
+    {
+        p_slash = strchr(name, '/');
+        if (p_slash)
+        {
+            if (p_slash == name0)
+            {
+                p_dir = p_token = _bcmcli_root_dir;
+                name = p_slash + 1;
+            }
+            else
+            {
+                p_token = _bcmcli_search_token1(p_dir, &name, p_slash - name);
+                if (p_token && (p_token->sel == BCMCLI_ENTRY_DIR))
+                    p_dir = p_token;
+            }
+        }
+        else
+        {
+            p_token = _bcmcli_search_token1(p_dir, &name, strlen(name));
+        }
+    } while (p_slash && p_token && *name);
+
+    return p_token;
+}
+
+
+
+/* Display help for each entry in the current directory */
+static void  _bcmcli_help_dir(bcmcli_session_extras *mon_session, bcmcli_entry *p_dir)
+{
+    bcmcli_session *help_session = _bcmcli_help_session_open(mon_session->session);
+    bcmcli_entry *p_token;
+    char buffer[BCMCLI_MAX_QUAL_NAME_LENGTH];
+
+    _bcmcli_qualified_name(p_dir, buffer, sizeof(buffer));
+    bcmcli_session_print(help_session, "Directory %s/ - %s\n", buffer, p_dir->help);
+    bcmcli_session_print(help_session, "Commands:\n");
+
+    p_token = p_dir->u.dir.first;
+    while ( p_token )
+    {
+        if (bcmcli_session_access_right(help_session) >= p_token->access_right)
+        {
+            if (p_token->sel == BCMCLI_ENTRY_DIR)
+                bcmcli_session_print(help_session, "\t%s/:  %s directory\n", p_token->name, p_token->help );
+            else
+            {
+                char *peol = strchr(p_token->help, '\n');
+                int help_len = peol ? peol - p_token->help : (int)strlen(p_token->help);
+                bcmcli_session_print(help_session, "\t%s(%d parms): %.*s\n",
+                            p_token->name, p_token->u.cmd.num_parms, help_len, p_token->help );
+            }
+        }
+        p_token = p_token->next;
+    }
+    bcmcli_session_print(help_session, "Type ? <name> for command help, \"/\"-root, \"..\"-upper\n" );
+    _bcmcli_help_session_print_and_close(mon_session->session, help_session);
+}
+
+
+/* Display help a token */
+static void _bcmcli_help_populated_cmd(bcmcli_session_extras *mon_session, bcmcli_entry *p_token,
+    const char *partial_match, bcmos_bool suppress_assigned)
+{
+    char tmp[80];
+    char bra, ket;
+    uint16_t i;
+
+    if (suppress_assigned)
+        bcmcli_session_print(mon_session->session, "\n");
+    for ( i=0; i<mon_session->num_parms; i++ )
+    {
+        bcmcli_cmd_parm *cur_parm = &mon_session->cmd_parms[i];
+        if (suppress_assigned && (cur_parm->flags & BCMCLI_PARM_FLAG_ASSIGNED))
+            continue;
+        if (partial_match && memcmp(partial_match, cur_parm->name, strlen(partial_match)))
+            continue;
+
+        if ((cur_parm->flags & BCMCLI_PARM_FLAG_OPTIONAL))
+        {
+            bra = '[';
+            ket=']';
+        }
+        else
+        {
+            bra = '<';
+            ket='>';
+        }
+        bcmcli_session_print(mon_session->session, "\t%c%s(%s)", bra, cur_parm->name, _bcmcli_get_type_name(cur_parm) );
+        if (cur_parm->max_array_size || cur_parm->type == BCMCLI_PARM_BUFFER)
+        {
+            uint32_t num_entries = (cur_parm->type == BCMCLI_PARM_BUFFER) ? cur_parm->value.buffer.len : cur_parm->max_array_size;
+            bcmcli_session_print(mon_session->session, "[%u]", num_entries);
+        }
+        if (cur_parm->type == BCMCLI_PARM_ENUM || cur_parm->type == BCMCLI_PARM_ENUM_MASK)
+        {
+            bcmcli_enum_val *values=cur_parm->enum_table;
+            bcmcli_session_print(mon_session->session, " {");
+            while (values->name)
+            {
+                if (values!=cur_parm->enum_table)
+                    bcmcli_session_print(mon_session->session, ", ");
+                bcmcli_session_print(mon_session->session, "%s", values->name);
+                ++values;
+            }
+            bcmcli_session_print(mon_session->session, "}");
+        }
+        if ((cur_parm->flags & BCMCLI_PARM_FLAG_DEFVAL))
+        {
+            bcmcli_session_print(mon_session->session, "=");
+            cur_parm->format_cb(cur_parm, cur_parm->value, tmp, sizeof(tmp));
+            bcmcli_session_print(mon_session->session, "%s", tmp);
+        }
+        if ((cur_parm->flags & BCMCLI_PARM_FLAG_RANGE))
+        {
+            bcmcli_parm_value low_val = { .number = cur_parm->low_val };
+            bcmcli_parm_value hi_val = { .number = cur_parm->hi_val };
+
+            bcmcli_session_print(mon_session->session, " (");
+            cur_parm->format_cb(cur_parm, low_val, tmp, sizeof(tmp));
+            bcmcli_session_print(mon_session->session, "%s..", tmp);
+            cur_parm->format_cb(cur_parm, hi_val, tmp, sizeof(tmp));
+            bcmcli_session_print(mon_session->session, "%s)", tmp);
+        }
+        bcmcli_session_print(mon_session->session, "%c ", ket);
+        bcmcli_session_print(mon_session->session, "- %s\n", cur_parm->description);
+    }
+
+    /* Print extra help if command has unresolved selector */
+    if (mon_session->num_parms &&
+        (mon_session->cmd_parms[mon_session->num_parms-1].flags & BCMCLI_PARM_FLAG_SELECTOR) &&
+        !(mon_session->cmd_parms[mon_session->num_parms-1].flags & BCMCLI_PARM_FLAG_ASSIGNED))
+    {
+        const char *sel_name = mon_session->cmd_parms[mon_session->num_parms-1].name;
+        bcmcli_session_print(mon_session->session, "Add %s=%s_value to see %s-specific parameters\n",
+            sel_name, sel_name, sel_name);
+    }
+    bcmcli_session_print(mon_session->session, "\n");
+}
+
+
+/* Display help a token */
+static void _bcmcli_help_entry(bcmcli_session_extras *mon_session, bcmcli_entry *p_token,
+    bcmcli_name_value *pairs, int npairs, bcmos_bool suppress_err_print)
+{
+    char buffer[BCMCLI_MAX_QUAL_NAME_LENGTH];
+
+    if (p_token->sel == BCMCLI_ENTRY_DIR)
+    {
+        _bcmcli_help_dir(mon_session, p_token);
+        return;
+    }
+
+    /* Populate parameter table */
+    _bcmcli_populate_parms(mon_session, p_token, pairs, npairs, suppress_err_print, NULL);
+
+    _bcmcli_qualified_name(p_token, buffer, sizeof(buffer));
+    bcmcli_session_print(mon_session->session, "%s: \t%s\n", buffer, p_token->help );
+    if (p_token->u.cmd.num_parms)
+        bcmcli_session_print(mon_session->session, "Parameters:\n");
+    _bcmcli_help_populated_cmd(mon_session, p_token, NULL, BCMOS_FALSE);
+    _bcmcli_free_session_value_status(mon_session);
+}
+
+
+/* Choose unique alias for <name> in <p_dir> */
+/* Currently only single-character aliases are supported */
+static void __bcmcli_chooseAlias(bcmcli_entry *p_dir, bcmcli_entry *p_new_token, int from)
+{
+    bcmcli_entry *p_token;
+    int         i;
+    char        c;
+
+    _bcmcli_strlwr( p_new_token->name );
+    i = from;
+    while ( p_new_token->name[i] )
+    {
+        c = p_new_token->name[i];
+        p_token = p_dir->u.dir.first;
+
+        while ( p_token )
+        {
+            if (p_token->alias &&
+                    (tolower( *p_token->alias ) == c) )
+                break;
+            if (strlen(p_token->name)<=2 && tolower(p_token->name[0])==c)
+                break;
+            p_token = p_token->next;
+        }
+        if (p_token)
+            ++i;
+        else
+        {
+            p_new_token->name[i] = toupper( c );
+            p_new_token->alias   = &p_new_token->name[i];
+            p_new_token->alias_len = 1;
+            break;
+        }
+    }
+}
+
+/* isupper wrapper */
+static inline int _bcmcli_isupper(char c)
+{
+    return isupper((int)c);
+}
+
+static void _bcmcli_choose_alias(bcmcli_entry *p_dir, bcmcli_entry *p_new_token)
+{
+    int i=0;
+    p_new_token->alias_len = 0;
+    p_new_token->alias = NULL;
+    /* Don't try to alias something short */
+    if (strlen(p_new_token->name) < BCMCLI_MIN_NAME_LENGTH_FOR_ALIAS)
+        return;
+    /* Try pre-set alias 1st */
+    while ( p_new_token->name[i] )
+    {
+        if (_bcmcli_isupper(p_new_token->name[i]))
+            break;
+        i++;
+    }
+    if (p_new_token->name[i])
+        __bcmcli_chooseAlias(p_dir, p_new_token, i);
+    if (p_new_token->alias != &p_new_token->name[i])
+        __bcmcli_chooseAlias(p_dir, p_new_token, 0);
+}
+
+
+/* Convert string s to lower case. Return pointer to s */
+static char  * _bcmcli_strlwr( char *s )
+{
+    char  *s0=s;
+
+    while ( *s )
+    {
+        *s = tolower( *s );
+        ++s;
+    }
+
+    return s0;
+}
+
+
+/* Compare strings case incensitive */
+static int _bcmcli_stricmp(const char *s1, const char *s2, int len)
+{
+    int  i;
+
+    for ( i=0; (i<len || len<0); i++ )
+    {
+        if (tolower( s1[i])  != tolower( s2[i] ))
+            return 1;
+        if (!s1[i])
+            break;
+    }
+
+    return 0;
+}
+
+static const char *_bcmcli_get_type_name(const bcmcli_cmd_parm *parm)
+{
+    bcmcli_parm_type type = parm->type;
+    static const char *type_name[] = {
+        [BCMCLI_PARM_DECIMAL]    = "decimal",
+        [BCMCLI_PARM_DECIMAL64]  = "decimal64",
+        [BCMCLI_PARM_UDECIMAL]   = "udecimal",
+        [BCMCLI_PARM_UDECIMAL64] = "udecimal64",
+        [BCMCLI_PARM_HEX]        = "hex",
+        [BCMCLI_PARM_HEX64]      = "hex64",
+        [BCMCLI_PARM_NUMBER]     = "number",
+        [BCMCLI_PARM_NUMBER64]   = "number64",
+        [BCMCLI_PARM_UNUMBER]    = "unumber",
+        [BCMCLI_PARM_UNUMBER64]  = "unumber64",
+        [BCMCLI_PARM_FLOAT]      = "float",
+        [BCMCLI_PARM_DOUBLE]     = "double",
+        [BCMCLI_PARM_ENUM]       = "enum",
+        [BCMCLI_PARM_ENUM_MASK]  = "enum_mask",
+        [BCMCLI_PARM_STRING]     = "string",
+        [BCMCLI_PARM_IP]         = "IP",
+        [BCMCLI_PARM_IPV6]       = "IPv6",
+        [BCMCLI_PARM_MAC]        = "MAC",
+        [BCMCLI_PARM_BUFFER]     = "buffer",
+        [BCMCLI_PARM_USERDEF]    = "userdef",
+    };
+    static const char *undefined = "undefined";
+    static const char *selector = "selector";
+    if (type > BCMCLI_PARM_USERDEF || !type_name[type])
+        return undefined;
+    if (type == BCMCLI_PARM_ENUM && (parm->flags & BCMCLI_PARM_FLAG_SELECTOR))
+        return selector;
+    return type_name[type];
+}
+
+/* Assign default callbacks */
+static void _bcmcli_assign_callbacks(bcmcli_cmd_parm *parm)
+{
+    if (parm->type == BCMCLI_PARM_ENUM)
+    {
+        parm->scan_cb = _bcmcli_enum_scan_cb;
+        parm->format_cb = _bcmcli_enum_format_cb;
+    }
+    else if (parm->type == BCMCLI_PARM_ENUM_MASK)
+    {
+        parm->scan_cb = _bcmcli_enum_mask_scan_cb;
+        parm->format_cb = _bcmcli_enum_mask_format_cb;
+    }
+    else if (parm->type == BCMCLI_PARM_BUFFER)
+    {
+        if (!parm->scan_cb)
+            parm->scan_cb = _bcmcli_buffer_scan_cb;
+        if (!parm->format_cb)
+            parm->format_cb = _bcmcli_dft_format_cb;
+    }
+    else
+    {
+        if (!parm->scan_cb)
+            parm->scan_cb = _bcmcli_dft_scan_cb;
+        if (!parm->format_cb)
+            parm->format_cb = _bcmcli_dft_format_cb;
+    }
+}
+
+
+/* Convert hex-string to binary data.
+ * Returns: converted length >=0 or error < 0
+ */
+static int _bcmcli_strhex(const char *src, uint8_t *dst, uint16_t dst_len)
+{
+    uint16_t src_len = (uint16_t)strlen( src );
+    uint16_t i = src_len, j, shift = 0;
+
+    if ( !dst || !dst_len || (src_len > 2*dst_len) || (src_len%2) )
+    {
+        return BCM_ERR_PARM;
+    }
+
+    /* Calculate hex buffer length and fill it up from right-to-left
+     * in order to start the process from LS nibble
+     */
+    dst_len = src_len / 2;
+    memset(dst, 0, dst_len);
+    j = dst_len-1;
+    do
+    {
+        int c = src[--i];
+
+        if ( (c>='0') && (c<='9') )
+        {
+            c = c - '0';
+        }
+        else if ( (c>='a') && (c<='f') )
+        {
+            c = 0xA + c - 'a';
+        }
+        else if ( (c>='A') && (c<='F') )
+        {
+            c = 0xA + c - 'A';
+        }
+        else
+        {
+            return BCM_ERR_PARM;
+        }
+
+        dst[j] |= (uint8_t)(c<<shift); /* shift can have 1 of 2 values: 0 and 4 */
+
+        j     -= shift>>2;              /* move to the next byte if we've just filled the ms nibble */
+        shift ^= 4;                     /* alternate nibbles */
+
+    } while ( i );
+
+    return dst_len;
+}
+
+/* Default function for string->value conversion.
+ * Returns 0 if OK
+ */
+static bcmos_errno _bcmcli_dft_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val)
+{
+    char *p_end = NULL;
+    int n;
+
+    if (parm->type == BCMCLI_PARM_UDECIMAL ||
+        parm->type == BCMCLI_PARM_UDECIMAL64 ||
+        parm->type == BCMCLI_PARM_UNUMBER ||
+        parm->type == BCMCLI_PARM_UNUMBER64)
+    {
+        /* strtoul returns OK even when parsing a negative number */
+        if (string_val[0] == '-')
+        {
+            return BCM_ERR_PARM;
+        }
+    }
+
+    switch(parm->type)
+    {
+        case BCMCLI_PARM_DECIMAL:
+            value->number = strtol(string_val, &p_end, 10);
+            break;
+        case BCMCLI_PARM_UDECIMAL:
+            value->unumber = strtoul(string_val, &p_end, 10);
+            break;
+        case BCMCLI_PARM_DECIMAL64:
+            value->number64 = strtoll(string_val, &p_end, 10);
+            break;
+        case BCMCLI_PARM_UDECIMAL64:
+            value->unumber64 = strtoull(string_val, &p_end, 10);
+            break;
+        case BCMCLI_PARM_HEX:
+            value->unumber = strtoul(string_val, &p_end, 16);
+            break;
+        case BCMCLI_PARM_HEX64:
+            value->unumber64 = strtoull(string_val, &p_end, 16);
+            break;
+        case BCMCLI_PARM_NUMBER:
+            value->number = strtol(string_val, &p_end, 0);
+            break;
+        case BCMCLI_PARM_UNUMBER:
+            value->unumber = strtoul(string_val, &p_end, 0);
+            break;
+        case BCMCLI_PARM_NUMBER64:
+            value->number64 = strtoll(string_val, &p_end, 0);
+            break;
+        case BCMCLI_PARM_UNUMBER64:
+            value->unumber64 = strtoull(string_val, &p_end, 0);
+            break;
+        case BCMCLI_PARM_FLOAT:
+        case BCMCLI_PARM_DOUBLE:
+            value->d = strtod(string_val, &p_end);
+            break;
+        case BCMCLI_PARM_MAC:
+        {
+            unsigned m0, m1, m2, m3, m4, m5;
+            n = sscanf(string_val, "%02x:%02x:%02x:%02x:%02x:%02x",
+                &m0, &m1, &m2, &m3, &m4, &m5);
+            if (n != 6)
+            {
+                n = sscanf(string_val, "%02x%02x%02x%02x%02x%02x",
+                    &m0, &m1, &m2, &m3, &m4, &m5);
+            }
+            if (n != 6)
+                return BCM_ERR_PARM;
+            if (m0 > 255 || m1 > 255 || m2 > 255 || m3 > 255 || m4 > 255 || m5 > 255)
+                return BCM_ERR_PARM;
+            value->mac.u8[0] = m0;
+            value->mac.u8[1] = m1;
+            value->mac.u8[2] = m2;
+            value->mac.u8[3] = m3;
+            value->mac.u8[4] = m4;
+            value->mac.u8[5] = m5;
+            break;
+        }
+        case BCMCLI_PARM_IP:
+        {
+            int n1, n2, n3, n4;
+            n = sscanf(string_val, "%d.%d.%d.%d", &n1, &n2, &n3, &n4);
+            if (n != 4)
+                return BCM_ERR_PARM;
+            if ((unsigned)n1 > 255 || (unsigned)n2 > 255 || (unsigned)n3 > 255 || (unsigned)n4 > 255)
+                return BCM_ERR_PARM;
+            value->unumber = (n1 << 24) | (n2 << 16) | (n3 << 8) | n4;
+            break;
+        }
+
+        default:
+            return BCM_ERR_PARM;
+    }
+    if (p_end && *p_end)
+        return BCM_ERR_PARM;
+    return BCM_ERR_OK;
+}
+
+static void _bcmcli_dft_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size)
+{
+    switch(parm->type)
+    {
+        case BCMCLI_PARM_DECIMAL:
+            snprintf(buffer, size, "%ld", value.number);
+            break;
+        case BCMCLI_PARM_UDECIMAL:
+            snprintf(buffer, size, "%lu", value.unumber);
+            break;
+        case BCMCLI_PARM_DECIMAL64:
+            snprintf(buffer, size, "%lld", value.number64);
+            break;
+        case BCMCLI_PARM_UDECIMAL64:
+            snprintf(buffer, size, "%llu", value.unumber64);
+            break;
+        case BCMCLI_PARM_HEX:
+            snprintf(buffer, size, "0x%lx", value.unumber);
+            break;
+        case BCMCLI_PARM_HEX64:
+            snprintf(buffer, size, "0x%llx", value.unumber64);
+            break;
+        case BCMCLI_PARM_NUMBER:
+            snprintf(buffer, size, "%ld", value.number);
+            break;
+        case BCMCLI_PARM_NUMBER64:
+            snprintf(buffer, size, "%lld", value.number64);
+            break;
+        case BCMCLI_PARM_UNUMBER:
+            snprintf(buffer, size, "%lu", value.unumber);
+            break;
+        case BCMCLI_PARM_UNUMBER64:
+            snprintf(buffer, size, "%llu", value.unumber64);
+            break;
+        case BCMCLI_PARM_FLOAT:
+        case BCMCLI_PARM_DOUBLE:
+            snprintf(buffer, size, "%f", value.d);
+            break;
+        case BCMCLI_PARM_STRING:
+            snprintf(buffer, size, "%s", value.string);
+            break;
+        case BCMCLI_PARM_MAC:
+            snprintf(buffer, size, "%02x:%02x:%02x:%02x:%02x:%02x",
+                parm->value.mac.u8[0], parm->value.mac.u8[1], parm->value.mac.u8[2],
+                parm->value.mac.u8[3], parm->value.mac.u8[4], parm->value.mac.u8[5]);
+            break;
+        case BCMCLI_PARM_IP:
+            snprintf(buffer, size, "%d.%d.%d.%d",
+                (int)((parm->value.unumber >> 24) & 0xff), (int)((parm->value.unumber >> 16) & 0xff),
+                (int)((parm->value.unumber >> 8) & 0xff), (int)(parm->value.unumber & 0xff));
+            break;
+
+        default:
+            bcmcli_strncpy(buffer, "*unknown*", size);
+    }
+}
+
+static bcmos_errno _bcmcli_enum_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val)
+{
+    bcmcli_enum_val *values=parm->enum_table;
+    while (values->name)
+    {
+        if (!_bcmcli_stricmp(values->name, string_val, -1))
+        {
+            value->enum_val = values->val;
+            return BCM_ERR_OK;
+        }
+        ++values;
+    }
+    return BCM_ERR_PARM;
+}
+
+static void _bcmcli_enum_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size)
+{
+    bcmcli_enum_val *values=parm->enum_table;
+    while (values->name)
+    {
+        if (values->val == value.enum_val)
+            break;
+        ++values;
+    }
+    if (values->name)
+        strncpy(buffer, values->name, size);
+    else
+        strncpy(buffer, "*invalid*", size);
+}
+
+static bcmos_errno _bcmcli_enum_mask_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val)
+{
+    bcmcli_parm_value val1;
+    char *del;
+    bcmos_errno err;
+
+    value->number = 0;
+
+    /* string_val is a combination of enum values separated by BCMCLI_ENUM_MASK_DEL_STR */
+    del = strchr(string_val, BCMCLI_ENUM_MASK_DEL_CHAR);
+    while (del)
+    {
+        char single_val[64];
+        if (del - string_val >= sizeof(single_val))
+            return BCM_ERR_OVERFLOW;
+        memcpy(single_val, string_val, del - string_val);
+        single_val[del - string_val] = 0;
+        err = _bcmcli_enum_scan_cb(parm, &val1, single_val);
+        if (err)
+            return err;
+        value->enum_val |= val1.enum_val;
+        string_val = del+1;
+        del = strchr(string_val, BCMCLI_ENUM_MASK_DEL_CHAR);
+    }
+    err = _bcmcli_enum_scan_cb(parm, &val1, string_val);
+    if (err)
+        return err;
+    value->number |= val1.enum_val;
+    return BCM_ERR_OK;
+}
+
+static void _bcmcli_enum_mask_format_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value value, char *buffer, int size)
+{
+    bcmcli_enum_val *values=parm->enum_table;
+    const char *none = NULL;
+    *buffer = 0;
+    while (values->name)
+    {
+        if (values->val == 0)
+        {
+            none = values->name;
+        }
+        if ((values->val & value.enum_val) != 0)
+        {
+            if (*buffer)
+                strncat(buffer, BCMCLI_ENUM_MASK_DEL_STR, size - strlen(buffer));
+            strncat(buffer, values->name, size - strlen(buffer));
+        }
+        ++values;
+    }
+    if (! *buffer)
+        strncpy(buffer, NULL != none ? none : "0", size);
+}
+
+static bcmos_errno _bcmcli_buffer_scan_cb(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value, const char *string_val)
+{
+    int n;
+
+    if (!value->buffer.start)
+        return BCM_ERR_PARM;
+    value->buffer.curr = value->buffer.start;
+    if (strcmp(string_val, "-") == 0)
+    {
+        return BCM_ERR_OK;
+    }
+    n = _bcmcli_strhex(string_val, value->buffer.start, value->buffer.len);
+    if (n < 0)
+        return n;
+    bcmolt_buf_skip(&value->buffer, n);
+
+    return BCM_ERR_OK;
+}
+
+static const char *_bcmcli_qualified_name(bcmcli_entry *token, char *buffer, int size )
+{
+    bcmcli_entry *parent = token->parent;
+    char qual_name[BCMCLI_MAX_QUAL_NAME_LENGTH];
+    *buffer=0;
+    while (parent)
+    {
+        bcmcli_strncpy(qual_name, parent->name, sizeof(qual_name));
+        if (parent->parent)
+            bcmcli_strncat(qual_name, "/", sizeof(qual_name));
+        bcmcli_strncat(qual_name, buffer, sizeof(qual_name));
+        bcmcli_strncpy(buffer, qual_name, size);
+        parent = parent->parent;
+    }
+    size -= strlen(buffer);
+    bcmcli_strncat(buffer, token->name, size);
+    return buffer;
+}
+
+/*
+ * Exports
+ */
+EXPORT_SYMBOL(bcmcli_dir_add);
+EXPORT_SYMBOL(bcmcli_dir_find);
+EXPORT_SYMBOL(bcmcli_token_name);
+EXPORT_SYMBOL(bcmcli_cmd_add);
+EXPORT_SYMBOL(bcmcli_session_open);
+EXPORT_SYMBOL(bcmcli_session_close);
+EXPORT_SYMBOL(bcmcli_parse);
+EXPORT_SYMBOL(bcmcli_stop);
+EXPORT_SYMBOL(bcmcli_is_stopped);
+EXPORT_SYMBOL(bcmcli_dir_get);
+EXPORT_SYMBOL(bcmcli_dir_set);
+EXPORT_SYMBOL(bcmcli_parm_number);
+EXPORT_SYMBOL(bcmcli_parm_is_set);
+EXPORT_SYMBOL(bcmcli_enum_parm_stringval);
+EXPORT_SYMBOL(bcmcli_token_destroy);
+EXPORT_SYMBOL(bcmcli_enum_bool_table);
diff --git a/bcm68620_release/release/host_reference/cli/bcmcli.h b/bcm68620_release/release/host_reference/cli/bcmcli.h
new file mode 100644
index 0000000..334e27c
--- /dev/null
+++ b/bcm68620_release/release/host_reference/cli/bcmcli.h
@@ -0,0 +1,677 @@
+/*
+<: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.
+
+:>
+ */
+
+
+/*******************************************************************
+ * bcmcli.h
+ *
+ * CLI engine
+ *
+ *******************************************************************/
+
+#ifndef BCMCLI_H
+
+#define BCMCLI_H
+
+#include <bcmos_system.h>
+#include <bcmcli_session.h>
+#include <bcmolt_buf.h>
+
+/** \defgroup bcm_cli Broadcom CLI Engine
+ * Broadcom CLI engine is used for all configuration and status monitoring.\n
+ * It doesn't have built-in scripting capabilities (logical expressions, loops),
+ * but can be used in combination with any available scripting language.\n
+ * Broadcom CLI supports the following features:\n
+ * - parameter number and type validation (simplifies command handlers development)
+ * - parameter value range checking
+ * - mandatory and optional parameters
+ * - positional and named parameters
+ * - parameters with default values
+ * - enum parameters can have arbitrary values
+ * - automatic command help generation
+ * - automatic or user-defined command shortcuts
+ * - command handlers return completion status to enable scripting
+ * - multiple sessions
+ * - session access rights
+ * - extendible. Supports user-defined parameter types
+ * - relatively low stack usage
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define BCMCLI_MAX_SEARCH_SUBSTR_LENGTH 80
+
+#define BCMCLI_ARRAY_EMPTY        "-"
+#define BCMCLI_PARM_NO_VALUE      "_"
+#define BCMCLI_ENUM_MASK_DEL_STR  "+"
+
+/** Monitor entry handle
+ */
+typedef struct bcmcli_entry bcmcli_entry;
+
+/* if BCMCLI_PARM_USERIO flag is set:
+   low_val: t_userscanf_f function
+   high_val: t_userprintf_f function
+*/
+
+/** Function parameter structure */
+typedef struct bcmcli_cmd_parm bcmcli_cmd_parm;
+
+/** Parameter type */
+typedef enum
+{
+    BCMCLI_PARM_NONE,
+    BCMCLI_PARM_DECIMAL,         /**< Decimal number */
+    BCMCLI_PARM_DECIMAL64,       /**< Signed 64-bit decimal */
+    BCMCLI_PARM_UDECIMAL,        /**< Unsigned decimal number */
+    BCMCLI_PARM_UDECIMAL64,      /**< Unsigned 64-bit decimal number */
+    BCMCLI_PARM_HEX,             /**< Hexadecimal number */
+    BCMCLI_PARM_HEX64,           /**< 64-bit hexadecimal number */
+    BCMCLI_PARM_NUMBER,          /**< Decimal number or hex number prefixed by 0x */
+    BCMCLI_PARM_NUMBER64,        /**< 64bit decimal number or hex number prefixed by 0x */
+    BCMCLI_PARM_UNUMBER,         /**< Unsigned decimal number or hex number prefixed by 0x */
+    BCMCLI_PARM_UNUMBER64,       /**< Unsigned 64bit decimal number or hex number prefixed by 0x */
+    BCMCLI_PARM_FLOAT,           /**< IEEE 32-bit floating-point number */
+    BCMCLI_PARM_DOUBLE,          /**< IEEE 64-bit floating point number */
+    BCMCLI_PARM_STRING,          /**< String */
+    BCMCLI_PARM_ENUM,            /**< Enumeration */
+    BCMCLI_PARM_ENUM_MASK,       /**< Bitmask created from enumeration values */
+    BCMCLI_PARM_IP,              /**< IP address n.n.n.n */
+    BCMCLI_PARM_IPV6,            /**< IPv6 address */
+    BCMCLI_PARM_MAC,             /**< MAC address xx:xx:xx:xx:xx:xx */
+    BCMCLI_PARM_BUFFER,          /**< Byte array */
+
+    BCMCLI_PARM_USERDEF          /**< User-defined parameter. User must provide scan_cb */
+} bcmcli_parm_type;
+
+/** Numeric type used for storing enum values. */
+typedef long bcmcli_enum_number;
+
+/** Enum attribute value.
+ *
+ *  Enum values is an array of bcmcli_enum_val terminated by element with name==NULL
+ *
+ */
+typedef struct bcmcli_enum_val
+{
+    const char *name;           /**< Enum symbolic name */
+    bcmcli_enum_number val;     /**< Enum internal value */
+    bcmcli_cmd_parm *parms;     /**< Extension parameter table for enum-selector */
+} bcmcli_enum_val;
+#define BCMCLI_MAX_ENUM_VALUES   128     /**< Max number of enum values */
+#define BCMCLI_ENUM_LAST     { NULL, 0}  /**< Last entry in enum table */
+
+/** Boolean values (true/false, yes/no, on/off)
+ *
+ */
+extern bcmcli_enum_val bcmcli_enum_bool_table[];
+
+/* Monitor data types */
+typedef long bcmcli_number;      /**< Type underlying BCMCLI_PARM_NUMBER, BCMCLI_PARM_DECIMAL */
+typedef long bcmcli_unumber;     /**< Type underlying BCMCLI_PARM_HEX, BCMCLI_PARM_UDECIMAL */
+typedef long bcmcli_number64;    /**< Type underlying BCMCLI_PARM_NUMBER64, BCMCLI_PARM_DECIMAL64 */
+typedef long bcmcli_unumber64;   /**< Type underlying BCMCLI_PARM_HEX64, BCMCLI_PARM_UDECIMAL64 */
+
+/** Parameter value */
+typedef union bcmcli_parm_value
+{
+    long number;                    /**< Signed number */
+    unsigned long unumber;          /**< Unsigned number */
+    long long number64;             /**< Signed 64-bit number */
+    unsigned long long unumber64;   /**< Unsigned 64-bit number */
+    const char *string;             /**< 0-terminated string */
+    double d;                       /**< Double-precision floating point number */
+    bcmos_mac_address mac;          /**< MAC address */
+    bcmolt_buf buffer;              /**< Buffer: used for BCMCLI_PARM_BUFFER */
+    bcmcli_enum_number enum_val;    /**< Enum value (number) */
+}  bcmcli_parm_value;
+
+/** User-defined scan function.
+ * The function is used for parsing user-defined parameter types
+ * Returns: 0-ok, <=error
+ *
+ */
+typedef bcmos_errno (*bcmcli_scan_cb)(const bcmcli_cmd_parm *parm, bcmcli_parm_value *value,
+    const char *string_val);
+
+/** User-defined print function.
+ * The function is used for printing user-defined parameter types
+ *
+ */
+typedef void (*bcmcli_format_cb)(const bcmcli_cmd_parm *parm, bcmcli_parm_value value,
+    char *buffer, int size);
+
+
+/** Function parameter structure */
+struct bcmcli_cmd_parm
+{
+   const char *name;            /**< Parameter name. Shouldn't be allocated on stack! */
+   const char *description;     /**< Parameter description. Shouldn't be allocated on stack! */
+   bcmcli_parm_type type;       /**< Parameter type */
+   uint8_t flags;               /**< Combination of BCMCLI_PARM_xx flags */
+#define BCMCLI_PARM_FLAG_NONE       0x00 /**< For use instead of magic number 0 when no flags apply */
+#define BCMCLI_PARM_FLAG_OPTIONAL   0x01 /**< Parameter is optional */
+#define BCMCLI_PARM_FLAG_DEFVAL     0x02 /**< Default value is set */
+#define BCMCLI_PARM_FLAG_RANGE      0x04 /**< Range is set */
+#define BCMCLI_PARM_FLAG_EOL        0x20 /**< String from the current parser position till EOL */
+#define BCMCLI_PARM_FLAG_SELECTOR   0x40 /**< Parameter selects other parameters */
+#define BCMCLI_PARM_FLAG_ASSIGNED   0x80 /**< Internal flag: parameter is assigned */
+
+   bcmcli_number low_val;       /**< Low val for range checking */
+   bcmcli_number hi_val;        /**< Hi val for range checking */
+   bcmcli_parm_value value;     /**< Value */
+   bcmcli_enum_val *enum_table; /**< Table containing { enum_name, enum_value } pairs */
+   bcmcli_scan_cb scan_cb;      /**< User-defined scan function for BCMCLI_PARM_USERDEF parameter type */
+   bcmcli_format_cb format_cb;  /**< User-defined format function for BCMCLI_PARM_USERDEF parameter type */
+   uint32_t max_array_size;     /**< Max array size for array-parameter */
+   uint32_t array_size;         /**< Actual array size for array-parameter */
+   bcmcli_parm_value *values;   /**< Array values */
+   void *user_data;             /**< User data - passed transparently to command handler */
+};
+
+/** Command parameter list terminator */
+#define BCMCLI_PARM_LIST_TERMINATOR  { .name=NULL, .type=BCMCLI_PARM_NONE }
+
+/** Helper macro: make simple parameter
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _type     Parameter type
+ * \param[in] _flags    Parameter flags
+ */
+#define BCMCLI_MAKE_PARM(_name, _descr, _type, _flags) \
+    { .name=(_name), .description=(_descr), .type=(_type), .flags=(_flags) }
+
+/** Helper macro: make simple parameter
+ * \param[in] _name       Parameter name
+ * \param[in] _descr      Parameter description
+ * \param[in] _type       Parameter type
+ * \param[in] _flags      Parameter flags
+ * \param[in] _size       Max array size
+ * \param[in] _values     Array values buffer
+ */
+#define BCMCLI_MAKE_PARM_ARRAY(_name, _descr, _type, _flags, _size, _values) \
+    { .name=(_name), .description=(_descr), .type=(_type), .flags=(_flags),\
+        .max_array_size=(_size), .values=(_values) }
+
+/** Helper macro: make simple parameter for arrays of enums
+ * \param[in] _name       Parameter name
+ * \param[in] _descr      Parameter description
+ * \param[in] _type       Parameter type
+ * \param[in] _flags      Parameter flags
+ * \param[in] _size       Max array size
+ * \param[in] _values     Array values buffer
+ * \param[in] _enum_table An array of enums that may be in the array
+ */
+#define BCMCLI_MAKE_PARM_ENUM_ARRAY(_name, _descr, _type, _flags, _size, _values, _enum_table) \
+    { .name=(_name), .description=(_descr), .type=(_type), .flags=(_flags),\
+        .max_array_size=(_size), .values=(_values), .enum_table=(_enum_table) }
+
+/** Helper macro: make range parameter
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _type     Parameter type
+ * \param[in] _flags    Parameter flags
+ * \param[in] _min      Min value
+ * \param[in] _max      Max value
+ */
+#define BCMCLI_MAKE_PARM_RANGE(_name, _descr, _type, _flags, _min, _max) \
+    { .name=(_name), .description=(_descr), .type=(_type), .flags=(_flags) | BCMCLI_PARM_FLAG_RANGE, \
+        .low_val=(_min), .hi_val=(_max) }
+
+/** Helper macro: make range parameter for arrays with range
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _type     Parameter type
+ * \param[in] _flags    Parameter flags
+ * \param[in] _size     Max array size
+ * \param[in] _values   Array values buffer
+ * \param[in] _min      Min value
+ * \param[in] _max      Max value
+ */
+#define BCMCLI_MAKE_PARM_ARRAY_RANGE(_name, _descr, _type, _flags, _size, _values, _min, _max) \
+    { .name=(_name), .description=(_descr), .type=(_type), .flags=(_flags) | BCMCLI_PARM_FLAG_RANGE,\
+         .max_array_size=(_size), .values=(_values), .low_val=(_min), .hi_val=(_max) }
+
+/** Helper macro: make parameter with default value
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _type     Parameter type
+ * \param[in] _flags    Parameter flags
+ * \param[in] _dft      Default value
+ */
+#define BCMCLI_MAKE_PARM_DEFVAL(_name, _descr, _type, _flags, _dft) \
+    { .name=(_name), .description=(_descr), .type=(_type), .flags=(_flags) | BCMCLI_PARM_FLAG_DEFVAL, \
+        .value = {_dft} }
+
+/** Helper macro: make range parameter with default value
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _type     Parameter type
+ * \param[in] _flags    Parameter flags
+ * \param[in] _min      Min value
+ * \param[in] _max      Max value
+ * \param[in] _dft      Default value
+ */
+#define BCMCLI_MAKE_PARM_RANGE_DEFVAL(_name, _descr, _type, _flags, _min, _max, _dft) \
+    { .name=(_name), .description=(_descr), .type=(_type), \
+        .flags=(_flags) | BCMCLI_PARM_FLAG_RANGE | BCMCLI_PARM_FLAG_DEFVAL, \
+        .low_val=(_min), .hi_val=(_max), .value = {_dft} }
+
+/** Helper macro: make enum parameter
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _values   Enum values table
+ * \param[in] _flags    Parameter flags
+ */
+#define BCMCLI_MAKE_PARM_ENUM(_name, _descr, _values, _flags) \
+    { .name=(_name), .description=(_descr), .type=BCMCLI_PARM_ENUM, .flags=(_flags), .enum_table=(_values)}
+
+/** Helper macro: make enum parameter with default value
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _values   Enum values table
+ * \param[in] _flags    Parameter flags
+ * \param[in] _dft      Default value
+ */
+#define BCMCLI_MAKE_PARM_ENUM_DEFVAL(_name, _descr, _values, _flags, _dft) \
+    { .name=(_name), .description=(_descr), .type=BCMCLI_PARM_ENUM, .flags=(_flags) | BCMCLI_PARM_FLAG_DEFVAL,\
+        .enum_table=(_values), .value={.string=_dft} }
+
+/** Helper macro: make enum mask parameter
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _values   Enum values table
+ * \param[in] _flags    Parameter flags
+ */
+#define BCMCLI_MAKE_PARM_ENUM_MASK(_name, _descr, _values, _flags) \
+    { .name=(_name), .description=(_descr), .type=BCMCLI_PARM_ENUM_MASK, .flags=(_flags), .enum_table=(_values)}
+
+/** Helper macro: make enum_mask parameter with default value
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _values   Enum values table
+ * \param[in] _flags    Parameter flags
+ * \param[in] _dft      Default value
+ */
+#define BCMCLI_MAKE_PARM_ENUM_MASK_DEFVAL(_name, _descr, _values, _flags, _dft) \
+    { .name=(_name), .description=(_descr), .type=BCMCLI_PARM_ENUM_MASK, .flags=(_flags) | BCMCLI_PARM_FLAG_DEFVAL,\
+        .enum_table=(_values), .value={.string=_dft} }
+
+/** Helper macro: make enum-selector parameter
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _values   Selector values table
+ * \param[in] _flags    Parameter flags
+ */
+#define BCMCLI_MAKE_PARM_SELECTOR(_name, _descr, _values, _flags) \
+    { .name=(_name), .description=(_descr), .type=BCMCLI_PARM_ENUM, .flags=(_flags) | BCMCLI_PARM_FLAG_SELECTOR,\
+        .enum_table=(_values) }
+
+/** Helper macro: make buffer parameter
+ * \param[in] _name     Parameter name
+ * \param[in] _descr    Parameter description
+ * \param[in] _flags    Parameter flags
+ * \param[in] _buf      Memory buffer associated with the parameter
+ * \param[in] _size     Buffer size
+ */
+#define BCMCLI_MAKE_PARM_BUFFER(_name, _descr, _flags, _buf, _size) \
+    { .name=(_name), .description=(_descr), .type=BCMCLI_PARM_BUFFER, \
+        .flags=(_flags), .value.buffer = {.start = _buf, .curr = _buf, .len = _size} }
+
+/** Register command without parameters helper */
+#define BCMCLI_MAKE_CMD_NOPARM(dir, cmd, help, cb) \
+{\
+    bcmos_errno bcmcli_cmd_add_err = bcmcli_cmd_add(dir, cmd, cb, help, BCMCLI_ACCESS_ADMIN, NULL, NULL);\
+    BUG_ON(BCM_ERR_OK != bcmcli_cmd_add_err);\
+}
+
+/** Register command helper */
+#define BCMCLI_MAKE_CMD(dir, cmd, help, cb, parms...)                           \
+{                                                                               \
+    static bcmcli_cmd_parm cmd_parms[]={                                        \
+        parms,                                                                  \
+        BCMCLI_PARM_LIST_TERMINATOR                                             \
+    };                                                                          \
+    bcmos_errno bcmcli_cmd_add_err = bcmcli_cmd_add(dir, cmd, cb, help, BCMCLI_ACCESS_ADMIN, NULL, cmd_parms);   \
+    BUG_ON(BCM_ERR_OK != bcmcli_cmd_add_err);\
+}
+
+/** Optional custom directory handlers */
+typedef void (*bcmcli_dir_enter_leave_cb)(bcmcli_session *session, bcmcli_entry *dir, int is_enter);
+
+/** Optional command or directory help callback
+ * \param[in]   session     Session handle
+ * \param[in]   h           Command or directory handle
+ * \param[in]   parms       Parameter(s) - the rest of the command string.
+ *                          Can be used for example to get help on individual parameters
+ */
+typedef void (*bcmcli_help_cb)(bcmcli_session *session, bcmcli_entry *h, const char *parms);
+
+
+/** Extra parameters of monitor directory.
+ * See \ref bcmcli_dir_add
+ *
+ */
+typedef struct bcmcli_dir_extra_parm
+{
+    void *user_priv;            /**< private data passed to enter_leave_cb */
+    bcmcli_dir_enter_leave_cb enter_leave_cb; /**< callback function to be called when session enters/leavs the directory */
+    bcmcli_help_cb help_cb;     /**< Help function called to print directory help instead of the automatic help */
+} bcmcli_dir_extra_parm;
+
+
+/** Extra parameters of monitor command.
+ * See \ref bcmcli_cmd_add
+ *
+ */
+typedef struct bcmcli_cmd_extra_parm
+{
+    bcmcli_help_cb help_cb;     /**< Optional help callback. Can be used for more sophisticated help, e.g., help for specific parameters */
+    uint32_t flags;             /**< Command flags */
+#define BCMCLI_CMD_FLAG_NO_NAME_PARMS   0x00000001 /**< No named parms. Positional only. Can be useful if parameter value can contain ',' */
+    void (*free_parms)(bcmcli_cmd_parm *parms);    /* Optional user-defined free */
+} bcmcli_cmd_extra_parm;
+
+
+/** Monitor command handler prototype */
+typedef bcmos_errno (*bcmcli_cmd_cb)(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t n_parms);
+
+/** CLI command logging mode */
+typedef enum
+{
+    BCMCLI_LOG_NONE,                /**< Disable logging */
+    BCMCLI_LOG_CLI,                 /**< Log commands as is and rc as CLI comment*/
+    BCMCLI_LOG_C_COMMENT            /**< Log as C comments */
+} bcmcli_log_mode;
+
+/** Add subdirectory to the parent directory
+ *
+ * \param[in]   parent          Parent directory handle. NULL=root
+ * \param[in]   name            Directory name
+ * \param[in]   help            Help string
+ * \param[in]   access_right    Access rights
+ * \param[in]   extras          Optional directory descriptor. Mustn't be allocated on the stack.
+ * \return      new directory handle or NULL in case of failure
+ */
+bcmcli_entry *bcmcli_dir_add(bcmcli_entry *parent, const char *name,
+                             const char *help, bcmcli_access_right access_right,
+                             const bcmcli_dir_extra_parm *extras);
+
+
+/** Scan directory tree and look for directory named "name".
+ *
+ * \param[in]   parent          Directory sub-tree root. NULL=root
+ * \param[in]   name            Name of directory to be found
+ * \return      directory handle if found or NULL if not found
+ */
+bcmcli_entry *bcmcli_dir_find(bcmcli_entry *parent, const char *name );
+
+
+/** Scan directory tree and look for command named "name".
+ *
+ * \param[in]   parent          Directory sub-tree root. NULL=root
+ * \param[in]   name            Name of command to be found
+ * \return      command handle if found or NULL if not found
+ */
+bcmcli_entry *bcmcli_cmd_find(bcmcli_entry *parent, const char *name );
+
+
+/** Get token name
+ * \param[in]   token           Directory or command token
+ * \return      directory token name
+ */
+const char *bcmcli_token_name(bcmcli_entry *token);
+
+/** Find the CLI parameter with the specified name (case insensitive).
+ * \param[in]   session  CLI session
+ * \param[in]   name     Parameter name
+ * \return      The CLI parameter that was found, or NULL if not found
+ */
+bcmcli_cmd_parm *bcmcli_find_named_parm(bcmcli_session *session, const char *name);
+
+/** Find the first CLI parameter whose name starts with the specified string (case insensitive).
+ * \param[in]   session  CLI session
+ * \param[in]   prefix   Parameter name prefix
+ * \return      The CLI parameter that was found, or NULL if not found
+ */
+bcmcli_cmd_parm *bcmcli_find_parm_by_prefix(bcmcli_session *session, const char *prefix);
+
+/** Query parameter value status.
+ * The function can be used for scalar and array parameters
+ * \param[in]   session         CLI session
+ * \param[in]   parm            Parameter from the array passed to the CLI command handler
+ *                              or returned by bcmcli_find_named_parm()
+ * \param[in]   value_index     value_index - for array parameters
+ * \return      BCMOS_TRUE if the parameter value is set, BCMOS_FALSE otherwise
+ */
+bcmos_bool bcmcli_parm_value_is_set(bcmcli_session *session, bcmcli_cmd_parm *parm, uint32_t value_index);
+
+/** Add CLI command
+ *
+ * \param[in]   dir             Handle of directory to add command to. NULL=root
+ * \param[in]   name            Command name
+ * \param[in]   cmd_cb          Command handler
+ * \param[in]   help            Help string
+ * \param[in]   access_right    Access rights
+ * \param[in]   extras          Optional extras
+ * \param[in]   parms           Optional parameters array. Must not be allocated on the stack!
+ *                              If parms!=NULL, the last parameter in the array must have name==NULL.
+ * \return
+ *      0   =OK\n
+ *      <0  =error code
+ */
+bcmos_errno bcmcli_cmd_add(bcmcli_entry *dir, const char *name, bcmcli_cmd_cb cmd_cb,
+                  const char *help, bcmcli_access_right access_right,
+                  const bcmcli_cmd_extra_parm *extras, bcmcli_cmd_parm parms[]);
+
+
+/** Destroy token (command or directory)
+ * \param[in]   token           Directory or command token. NULL=root
+ */
+void bcmcli_token_destroy(bcmcli_entry *token);
+
+/** Parse and execute input string.
+ * input_string can contain multiple commands delimited by ';'
+ *
+ * \param[in]   session         Session handle
+ * \param[in]   input_string    String to be parsed
+ * \return
+ *      =0  - OK \n
+ *      -EINVAL - parsing error\n
+ *      other - return code - as returned from command handler.
+ *            It is recommended to return -EINTR to interrupt monitor loop.
+ */
+bcmos_errno bcmcli_parse(bcmcli_session *session, char *input_string);
+
+/** Read input and parse iteratively until EOF or bcmcli_is_stopped()
+ *
+ * \param[in]   session         Session handle
+ * \return
+ *      =0  - OK \n
+ */
+bcmos_errno bcmcli_driver(bcmcli_session *session);
+
+/** Stop monitor driver.
+ * The function stops \ref bcmcli_driver
+ * \param[in]   session         Session handle
+ */
+void bcmcli_stop(bcmcli_session *session);
+
+/** Returns 1 if monitor session is stopped
+ * \param[in]   session         Session handle
+ * \returns 1 if monitor session stopped by bcmcli_stop()\n
+ * 0 otherwise
+ */
+bcmos_bool bcmcli_is_stopped(bcmcli_session *session);
+
+/** Get current directory for the session,
+ * \param[in]   session         Session handle
+ * \return      The current directory handle
+ */
+bcmcli_entry *bcmcli_dir_get(bcmcli_session *session );
+
+/** Set current directory for the session.
+ * \param[in]   session         Session handle
+ * \param[in]   dir             Directory that should become current
+ * \return
+ *      =0  - OK
+ *      <0  - error
+ */
+bcmos_errno bcmcli_dir_set(bcmcli_session *session, bcmcli_entry *dir);
+
+/** Get parameter number given its name.
+ * The function is intended for use by command handlers
+ * \param[in]       session         Session handle
+ * \param[in]       parm_name       Parameter name
+ * \return
+ *  >=0 - parameter number\n
+ *  <0  - parameter with this name doesn't exist
+ */
+int bcmcli_parm_number(bcmcli_session *session, const char *parm_name);
+
+/** Get parameter by name
+ * The function is intended for use by command handlers
+ * \param[in]       session         Session handle
+ * \param[in]       parm_name       Parameter name
+ * \return
+ * parameter pointer or NULL if not found
+ */
+bcmcli_cmd_parm *bcmcli_parm_get(bcmcli_session *session, const char *parm_name);
+
+/** Check if parameter is set
+ * The function is intended for use by command handlers
+ * \param[in]       session         Session handle
+ * \param[in]       parm            Parameter name
+ * \return
+ * TRUE if parameter is set, FALSE otherwise
+ */
+static inline bcmos_bool bcmcli_parm_is_set(bcmcli_session *session, const bcmcli_cmd_parm *parm)
+{
+    return (parm->flags & BCMCLI_PARM_FLAG_ASSIGNED) ? BCMOS_TRUE : BCMOS_FALSE;
+}
+
+/** Check if parameter is set
+ * \param[in]       session         Session handle
+ * \param[in]       parm_number     Parameter number
+ * \return
+ *  0 if parameter is set\n
+ *  BCM_ERR_NOENT if parameter is not set
+ *  BCM_ERR_PARM if parm_number is invalid
+ */
+bcmos_errno bcmcli_parm_check(bcmcli_session *session, int parm_number);
+
+
+/** Get enum's string value given its internal value
+ * \param[in]       table           Enum table
+ * \param[in]       value           Internal value
+ * \return
+ *      enum string value or NULL if internal value is invalid
+ */
+static inline const char *bcmcli_enum_stringval(const bcmcli_enum_val table[], long value)
+{
+    while(table->name)
+    {
+        if (table->val==value)
+            return table->name;
+        ++table;
+    }
+    return NULL;
+}
+
+
+/** Get enum's parameter string value given its internal value
+ * \param[in]       session         Session handle
+ * \param[in]       parm_number     Parameter number
+ * \param[in]       value           Internal value
+ * \return
+ *      enum string value or NULL if parameter is not enum or
+ *      internal value is invalid
+ */
+const char *bcmcli_enum_parm_stringval(bcmcli_session *session, int parm_number, long value);
+
+
+/** Print CLI parameter value
+ * \param[in]       session         Session handle
+ * \param[in]       parm            Parameter
+ */
+void bcmcli_parm_print(bcmcli_session *session, const bcmcli_cmd_parm *parm);
+
+
+/** strncpy flavour that always add 0 terminator
+ * \param[in]       dst             Destination string
+ * \param[in]       src             Source string
+ * \param[in]       dst_size        Destination buffer size
+ * \return dst
+ */
+static inline char *bcmcli_strncpy(char *dst, const char *src, uint32_t dst_size)
+{
+    strncpy(dst, src, dst_size-1);
+    dst[dst_size-1] = 0;
+    return dst;
+}
+
+
+/** strncat flavour that limits size of destination buffer
+ * \param[in]       dst             Destination string
+ * \param[in]       src             Source string
+ * \param[in]       dst_size        Destination buffer size
+ * \return dst
+ */
+static inline char *bcmcli_strncat(char *dst, const char *src, uint32_t dst_size)
+{
+    uint32_t dst_len = strlen(dst);
+    return strncat(dst, src, dst_size-dst_len-1);
+}
+
+/* Redefine bcmcli_session_print --> bcmcli_print */
+#define bcmcli_print bcmcli_session_print
+
+/** Enable / disable CLI command logging
+ * \param[in]   mode    Logging flags
+ * \param[in]   log     Log session. Must be set if mode != BCMCLI_CMD_LOG_NONE
+ * \return 0=OK or error <0
+ */
+bcmos_errno bcmcli_log_set(bcmcli_log_mode mode, bcmcli_session *log);
+
+/** Write string to CLI log.
+ * The function is ignored if CLI logging is not enabled using bcmcli_log_set()
+ * \param[in]   format  printf-like format followed by arguments
+ */
+void bcmcli_log(const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+/** @} end bcm_cli group */
+
+#endif /* #ifndef BCM_CLI_H */
diff --git a/bcm68620_release/release/host_reference/cli/bcmcli_server.c b/bcm68620_release/release/host_reference/cli/bcmcli_server.c
new file mode 100644
index 0000000..160d98d
--- /dev/null
+++ b/bcm68620_release/release/host_reference/cli/bcmcli_server.c
@@ -0,0 +1,546 @@
+/*
+<: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.
+
+:>
+ */
+
+
+/*******************************************************************
+ * bcmcli_server.c
+ *
+ * CLI engine - remote shell support
+ *
+ * This module is a back-end of remote shell support.
+ * - multiple servers
+ * - domain and TCP-based connections
+ * - session access level - per server
+ *******************************************************************/
+
+#include <bcmcli_server.h>
+
+typedef struct bcmclis_server bcmclis_server_t;
+
+/* Server connection
+ */
+typedef struct bcmclis_conn
+{
+    struct bcmclis_conn *next;
+    bcmclis_server_t *server;
+    const char *address; /* client address */
+    int sock;             /* transport socket */
+    bdmf_task rx_thread;
+    bcmcli_session *session;
+    uint32_t bytes_sent;
+    uint32_t bytes_received;
+    bdmf_task conn_thread;
+} bcmclis_conn_t;
+
+/* Server control bdmfock
+ */
+struct bcmclis_server
+{
+    bcmclis_server_t *next;
+    bcmclis_conn_t *conn_list;
+    int sock;             /* listening socket */
+    bcmclis_parm_t parms;
+    int id;
+    int nconns;
+    bcmos_fastlock lock;
+    bdmf_task listen_thread;
+};
+
+/* socaddr variants */
+typedef union
+{
+    struct sockaddr sa;
+    struct sockaddr_un domain_sa;
+    struct sockaddr_in tcp_sa;
+} sockaddr_any;
+
+static bcmclis_server_t *bcmclis_servers;
+static int bcmclis_server_id;
+
+static bcmclis_server_t *bcmclis_id_to_server(int hs, bcmclis_server_t **prev)
+{
+    bcmclis_server_t *s=bcmclis_servers;
+    if (prev)
+        *prev = NULL;
+    while(s)
+    {
+        if (s->id == hs)
+            break;
+        if (prev)
+            *prev = s;
+        s = s->next;
+    }
+    return s;
+}
+
+/* Parse address helper */
+static int bcmclis_parse_address(const bcmclis_parm_t *parms, int *protocol, sockaddr_any *sa, int *len)
+{
+    switch(parms->transport)
+    {
+    case BCMCLI_TRANSPORT_DOMAIN_SOCKET:
+    {
+        *protocol = AF_UNIX;
+        sa->domain_sa.sun_family = AF_UNIX;  /* local is declared before socket() ^ */
+        strcpy(sa->domain_sa.sun_path, parms->address);
+        *len = strlen(sa->domain_sa.sun_path) + sizeof(sa->domain_sa.sun_family);
+        break;
+    }
+    case BCMCLI_TRANSPORT_TCP_SOCKET:
+    {
+        *protocol = AF_INET;
+        sa->tcp_sa.sin_family = AF_INET;
+        sa->tcp_sa.sin_port = htons(atoi(parms->address));
+        sa->tcp_sa.sin_addr.s_addr = INADDR_ANY;
+        *len = sizeof(sa->tcp_sa);
+        break;
+    }
+    default:
+        return BCM_ERR_PARM;
+    }
+    return 0;
+}
+
+
+/* disconnect client and clear resources */
+static void bcmclis_disconnect(bcmclis_conn_t *conn)
+{
+    bcmclis_server_t *s=conn->server;
+    bcmclis_conn_t *c=s->conn_list, *prev=NULL;
+
+    bcmos_fastlock_lock(&s->lock);
+    while(c && c!=conn)
+    {
+        prev = c;
+        c = c->next;
+    }
+    BUG_ON(!c);
+    if (prev)
+        prev->next = c->next;
+    else
+        s->conn_list = c->next;
+    --s->nconns;
+    bcmos_fastlock_unlock(&s->lock);
+    bcmcli_session_close(c->session);
+    close(c->sock);
+    bdmf_task_destroy(c->rx_thread);
+    bcmos_free(c);
+}
+
+/*
+ * Session callbacks
+ */
+
+/** Session's output function.
+ * returns the number of bytes written or <0 if error
+ */
+static int bcmclis_cb_sess_write(void *user_priv, const void *buf, uint32_t size)
+{
+    bcmclis_conn_t *c=user_priv;
+    int rc;
+
+    rc = send(c->sock, buf, size, 0);
+    /* disconnect if IO error */
+    if (rc < size)
+        bcmclis_disconnect(c);
+    else
+        c->bytes_sent += rc;
+    return rc;
+}
+
+#define CHAR_EOT 0x04
+
+/** Session's input function.
+ * returns the number of bytes read or <0 if error
+ */
+static char *bcmclis_read_line(bcmclis_conn_t *c, char *buf, uint32_t size)
+{
+    int i;
+    int rc;
+    int len=0;
+
+    for(i=0; i<size-1; i++)
+    {
+        char ch;
+        rc = recv(c->sock, &ch, 1, MSG_WAITALL);
+        if (rc <= 0)
+            break;
+        if (ch == '\r')
+            continue;
+        if (ch == CHAR_EOT)
+            break;
+        buf[len++] = ch;
+        if (ch == '\n')
+            break;
+    }
+    c->bytes_received += i;
+    buf[len] = 0;
+    return (len ? buf : NULL);
+}
+
+/* Receive handler */
+static int bcmclis_rx_thread_handler(void *arg)
+{
+    char buf[512];
+    bcmclis_conn_t *c=arg;
+
+    while(!bcmcli_is_stopped(c->session) &&
+          bcmclis_read_line(c, buf, sizeof(buf)))
+    {
+        bcmcli_parse(c->session, buf);
+    }
+    bcmclis_disconnect(c);
+    return 0;
+}
+
+/* New client connection indication */
+static void bcmclis_connect(bcmclis_server_t *s, char *addr, int sock)
+{
+    bcmclis_conn_t *c;
+    bcmcli_session_parm sess_parm;
+    int rc;
+
+    if (s->parms.max_clients && s->nconns >= s->parms.max_clients)
+    {
+        bcmos_printf("bdmfmons: server %s: refused connection because max number has been reached\n", s->parms.address);
+        close(sock);
+        return;
+    }
+
+    c = bcmos_calloc(sizeof(*c) + strlen(addr) + 1);
+    if (!c)
+        goto cleanup;
+    c->address = (char *)c + sizeof(*c);
+    strcpy((char *)c->address, addr);
+    c->server = s;
+    c->sock = sock;
+
+    /* create new management session */
+    memset(&sess_parm, 0, sizeof(sess_parm));
+    sess_parm.access_right = s->parms.access;
+    sess_parm.write = bcmclis_cb_sess_write;
+    sess_parm.user_priv = c;
+    rc = bcmcli_session_open(&sess_parm, &c->session);
+    if (rc)
+        goto cleanup;
+
+    /* wait for receive in a separate thread */
+    rc = bdmf_task_create("bcmclis_rx",
+                    BDMFSYS_DEFAULT_TASK_PRIORITY,
+                    BDMFSYS_DEFAULT_TASK_STACK,
+                    bcmclis_rx_thread_handler, c,
+                    &c->rx_thread);
+    if (rc)
+        goto cleanup;
+
+    bcmos_fastlock_lock(&s->lock);
+    c->next = s->conn_list;
+    s->conn_list = c;
+    ++s->nconns;
+    bcmos_fastlock_unlock(&s->lock);
+
+    return;
+
+cleanup:
+    close(sock);
+    if (c->session)
+        bcmcli_session_close(c->session);
+    if (c)
+        bcmos_free(c);
+}
+
+/* Receive handler */
+static int bcmclis_listen_thread_handler(void *arg)
+{
+    bcmclis_server_t *s=arg;
+    sockaddr_any addr;
+    socklen_t len;
+    int sock;
+
+    while(1)
+    {
+        char caddr[64];
+        len = sizeof(addr);
+        sock = accept(s->sock, &addr.sa, &len);
+        if (sock < 0)
+        {
+            perror("accept");
+            break;
+        }
+        if (s->parms.transport==BCMCLI_TRANSPORT_DOMAIN_SOCKET)
+            strncpy(caddr, s->parms.address, sizeof(caddr)-1);
+        else
+        {
+            snprintf(caddr, sizeof(caddr)-1, "%s:%d",
+                inet_ntoa(addr.tcp_sa.sin_addr), ntohs(addr.tcp_sa.sin_port));
+        }
+        bcmclis_connect(s, caddr, sock);
+    }
+    return 0;
+}
+
+/*
+ * External API
+ */
+
+/** Create shell server.
+ * Immediately after creation server is ready to accept client connections
+ * \param[in]   parms   Server parameters
+ * \param[out]  hs      Server handle
+ * \return  0 - OK\n
+ *         <0 - error code
+ */
+bcmos_errno bcmclis_server_create(const bcmclis_parm_t *parms, int *hs)
+{
+    bcmclis_server_t *s;
+    int protocol;
+    sockaddr_any sa;
+    int len;
+    int rc;
+
+    if (!parms || !hs || !parms->address)
+        return BCM_ERR_PARM;
+
+    /* parse address */
+    if (bcmclis_parse_address(parms, &protocol, &sa, &len))
+        return BCM_ERR_PARM;
+
+    /* allocate server structure */
+    s = bcmos_calloc(sizeof(bcmclis_server_t)+strlen(parms->address)+1);
+    if (!s)
+        return BCM_ERR_NOMEM;
+    s->parms = *parms;
+    s->parms.address = (char *)s + sizeof(*s);
+    strcpy(s->parms.address, parms->address);
+    s->id = ++bcmclis_server_id;
+    bcmos_fastlock_init(&s->lock);
+
+    /* create socket and start listening */
+    s->sock = socket(protocol, SOCK_STREAM, 0);
+    if ((s->sock < 0) ||
+        (bind(s->sock, &sa.sa, len) < 0) ||
+        (listen(s->sock, 1) < 0))
+    {
+        perror("socket/bind/listen");
+        close(s->sock);
+        bcmos_free(s);
+        return BCM_ERR_PARM;
+    }
+
+    /* wait for connection(s) in a separate thread */
+    rc = bdmf_task_create("bcmclis_listen",
+                    BDMFSYS_DEFAULT_TASK_PRIORITY,
+                    BDMFSYS_DEFAULT_TASK_STACK,
+                    bcmclis_listen_thread_handler, s,
+                    &s->listen_thread);
+    if (rc)
+    {
+        close(s->sock);
+        bcmos_free(s);
+        return rc;
+    }
+
+    /* all good */
+    s->next = bcmclis_servers;
+    bcmclis_servers = s;
+    *hs = s->id;
+
+    return 0;
+}
+
+/** Destroy shell server.
+ * All client connections if any are closed
+ * \param[in]   hs      Server handle
+ * \return  0 - OK\n
+ *         <0 - error code
+ */
+bcmos_errno bcmclis_server_destroy(int hs)
+{
+    bcmclis_server_t *prev;
+    bcmclis_server_t *s = bcmclis_id_to_server(hs, &prev);
+    bcmclis_conn_t *c;
+    if (!s)
+        return BCM_ERR_NOENT;
+
+    bdmf_task_destroy(s->listen_thread);
+    close(s->sock);
+
+    /* disconnect all clients */
+    while((c = s->conn_list))
+        bcmclis_disconnect(c);
+
+    /* destroy server */
+    bcmos_fastlock_lock(&s->lock);
+    if (prev)
+        prev->next = s->next;
+    else
+        bcmclis_servers = s->next;
+    bcmos_fastlock_unlock(&s->lock);
+
+    bcmos_free(s);
+    return 0;
+}
+
+/*
+ * Shell command handlers
+ */
+
+static bcmcli_enum_val transport_type_enum_tabdmfe[] = {
+    { .name="domain_socket", .val=BCMCLI_TRANSPORT_DOMAIN_SOCKET},
+    { .name="tcp_socket", .val=BCMCLI_TRANSPORT_TCP_SOCKET},
+    BCMCLI_ENUM_LAST
+};
+
+static bcmcli_enum_val access_type_enum_tabdmfe[] = {
+    { .name="guest", .val=BCMCLI_ACCESS_GUEST},
+    { .name="admin", .val=BCMCLI_ACCESS_ADMIN},
+    { .name="debug", .val=BCMCLI_ACCESS_DEBUG},
+    BCMCLI_ENUM_LAST
+};
+
+/* Create remote shell server
+    BCMCLI_MAKE_PARM_ENUM("transport", "Transport type", transport_type_enum_tabdmfe, 0),
+    BCMCLI_MAKE_PARM("address", "Bind address", BCMCLI_PARM_STRING, 0),
+    BCMCLI_MAKE_PARM_ENUM("access", "Access level", access_type_enum_tabdmfe, 0),
+    BCMCLI_MAKE_PARM_DEFVAL("max_clients", "Max clients. 0=default", BCMCLI_PARM_NUMBER, 0, 0),
+*/
+static int bcmclis_mon_create(bcmcli_session *session, const bcmcli_cmd_parm parm[],  uint16_t n_parms)
+{
+    bcmclis_transport_type_t transport = (bcmclis_transport_type_t)parm[0].value.number;
+    char *address = (char *)parm[1].value.number;
+    bcmcli_access_right access = (bcmcli_access_right)parm[2].value.number;
+    int max_clients = (int)parm[3].value.number;
+    bcmclis_parm_t parms;
+    int hs;
+    int rc;
+
+    memset(&parms, 0, sizeof(parms));
+    parms.transport = transport;
+    parms.access = access;
+    parms.address = address;
+    parms.max_clients = max_clients;
+    rc = bcmclis_server_create(&parms, &hs);
+    if (rc)
+        bcmcli_session_print(session, "bcmclis_server_create() failed with rc=%d - %s\n",
+                        rc, bcmos_strerror(rc));
+    else
+        bcmcli_session_print(session, "Remote shell server created. Server id %d\n", hs);
+    return rc;
+}
+
+/* Destroy remote shell server
+    BCMCLI_MAKE_PARM("server_id", "Server id", BCMCLI_PARM_NUMBER, 0),
+*/
+static int bcmclis_mon_destroy(bcmcli_session *session, const bcmcli_cmd_parm parm[],  uint16_t n_parms)
+{
+    int hs = (int)parm[0].value.number;
+    int rc;
+    rc = bcmclis_server_destroy(hs);
+    bcmcli_session_print(session, "Remote shell server %d destroyed. rc=%d - %s\n",
+        hs, rc, bcmos_strerror(rc));
+    return rc;
+}
+
+/* Show remote shell servers
+*/
+static int bcmclis_mon_show(bcmcli_session *session, const bcmcli_cmd_parm parm[],  uint16_t n_parms)
+{
+    bcmclis_server_t *s=bcmclis_servers;
+    bcmclis_conn_t *c;
+    while(s)
+    {
+        bcmcli_session_print(session, "Remote server %d at %s\n", s->id, s->parms.address);
+        c = s->conn_list;
+        while(c)
+        {
+            bcmcli_session_print(session, "\t - %s. bytes sent:%d received:%d\n",
+                c->address, c->bytes_sent, c->bytes_received);
+            c = c->next;
+        }
+        s = s->next;
+    }
+    return 0;
+}
+
+/* Create shell_server directory in root_dir
+   Returns the "shell_server" directory handle
+*/
+bcmcli_entry *bcmclis_server_mon_init(bcmcli_entry *root_dir)
+{
+    bcmcli_entry *shell_dir;
+
+    if ((shell_dir=bcmcli_dir_find(NULL, "shell_server"))!=NULL)
+        return NULL;
+
+    shell_dir = bcmcli_dir_add(root_dir, "shell_server",
+                             "Remote Shell",
+                             BCMCLI_ACCESS_GUEST, NULL);
+
+    {
+        static bcmcli_cmd_parm parms[]={
+            BCMCLI_MAKE_PARM_ENUM("transport", "Transport type", transport_type_enum_tabdmfe, 0),
+            BCMCLI_MAKE_PARM("address", "Bind address: domain_socket address or TCP port", BCMCLI_PARM_STRING, 0),
+            BCMCLI_MAKE_PARM_ENUM("access", "Access level", access_type_enum_tabdmfe, 0),
+            BCMCLI_MAKE_PARM_DEFVAL("max_clients", "Max clients. 0=default", BCMCLI_PARM_NUMBER, 0, 0),
+            BCMCLI_PARM_LIST_TERMINATOR
+        };
+        bcmcli_cmd_add(shell_dir, "create", bcmclis_mon_create,
+                      "Create remote shell server",
+                      BCMCLI_ACCESS_ADMIN, NULL, parms);
+    }
+
+    {
+        static bcmcli_cmd_parm parms[]={
+            BCMCLI_MAKE_PARM("server_id", "Server id", BCMCLI_PARM_NUMBER, 0),
+            BCMCLI_PARM_LIST_TERMINATOR
+        };
+        bcmcli_cmd_add(shell_dir, "destroy", bcmclis_mon_destroy,
+                      "Destroy remote shell server",
+                      BCMCLI_ACCESS_ADMIN, NULL, parms);
+    }
+
+    {
+        bcmcli_cmd_add(shell_dir, "show", bcmclis_mon_show,
+                      "Show remote shell servers",
+                      BCMCLI_ACCESS_GUEST, NULL, NULL);
+    }
+
+    return shell_dir;
+}
+
+/* Destroy shell_server directory
+*/
+void bcmclis_server_mon_destroy(void)
+{
+    bcmcli_entry *shell_dir;
+    shell_dir=bcmcli_dir_find(NULL, "shell_server");
+    if (shell_dir)
+        bcmcli_token_destroy(shell_dir);
+}
+
diff --git a/bcm68620_release/release/host_reference/cli/bcmcli_server.h b/bcm68620_release/release/host_reference/cli/bcmcli_server.h
new file mode 100644
index 0000000..66e74e5
--- /dev/null
+++ b/bcm68620_release/release/host_reference/cli/bcmcli_server.h
@@ -0,0 +1,97 @@
+/*
+<: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.
+
+:>
+ */
+
+
+/*******************************************************************
+ * -mon_server.h
+ *
+ * BL framework - remote shell support
+ *
+ * This module is a back-end of remote shell support.
+ * - multiple servers
+ * - domain and TCP-based connections
+ * - session access level - per server
+ *******************************************************************/
+
+#ifndef BCMCLI_SERVER_H_
+#define BCMCLI_SERVER_H_
+
+#include <bcmos_system.h>
+#include <bcmcli_session.h>
+#include <bcmcli.h>
+
+/** Shell server transport type
+ */
+typedef enum {
+    BCMCLI_TRANSPORT_DOMAIN_SOCKET,
+    BCMCLI_TRANSPORT_TCP_SOCKET,
+
+    BCMCLI_TRANSPORT__NUMBER_OF
+} bcmclis_transport_type_t;
+
+/** Shell server parameters
+ */
+typedef struct bcmclis_parm
+{
+    bcmcli_access_right access;         /**< Access rights */
+    bcmclis_transport_type_t transport; /**< Transport type */
+    char *address;                      /**< Address in string form: domain socket file in local FS; port for TCP socket */
+    int max_clients;                    /**< Max number of clients */
+} bcmclis_parm_t;
+
+
+/** Create shell server.
+ * Immediately after creation server is ready to accept client connections
+ * \param[in]   parms   Server parameters
+ * \param[out]  hs      Server handle
+ * \return  0 - OK\n
+ *         <0 - error code
+ */
+bcmos_errno bcmclis_server_create(const bcmclis_parm_t *parms, int *hs);
+
+/** Destroy shell server.
+ * All client connections if any are closed
+ * \param[in]   hs      Server handle
+ * \return  0 - OK\n
+ *         <0 - error code
+ */
+bcmos_errno bcmclis_server_destroy(int hs);
+
+
+/* Create shell_server directory in root_dir
+   Returns the "shell_server" directory handle
+*/
+bcmcli_entry *bcmclis_server_mon_init(bcmcli_entry *root_dir);
+
+
+/* Destroy shell_server directory
+*/
+void bcmclis_server_mon_destroy(void);
+
+#endif /* BCMCLI_SERVER_H_ */
diff --git a/bcm68620_release/release/host_reference/cli/bcmcli_session.c b/bcm68620_release/release/host_reference/cli/bcmcli_session.c
new file mode 100644
index 0000000..519eaa8
--- /dev/null
+++ b/bcm68620_release/release/host_reference/cli/bcmcli_session.c
@@ -0,0 +1,587 @@
+/*
+<: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.
+
+:>
+ */
+
+
+/*******************************************************************
+ * bcmcli_session.c
+ *
+ * CLI engine - session management
+ *
+ *******************************************************************/
+
+#include <bcmos_system.h>
+#include <bcmolt_utils.h>
+
+#define BCMCLI_INTERNAL
+#include <bcmcli_session.h>
+
+static bcmos_fastlock session_lock;
+static bcmcli_session *session_list;
+static int session_module_initialized;
+
+/*
+ * Internal functions
+ */
+
+static void _bcmcli_session_update_prompt(bcmcli_session *session)
+{
+    if (!session)
+        return;
+
+    if (session->parms.get_prompt)
+    {
+        session->parms.get_prompt(session, session->prompt_buf, BCMCLI_MAX_PROMPT_LEN);
+        session->prompt_buf[BCMCLI_MAX_PROMPT_LEN - 1] = '\0';
+    }
+    else
+    {
+        session->prompt_buf[0] = '\0';
+    }
+}
+
+static char *_bcmcli_session_gets(bcmcli_session *session, char *buffer, uint32_t size)
+{
+    const char *line = NULL;
+    _bcmcli_session_update_prompt(session);
+#ifdef CONFIG_LIBEDIT
+    if (session && session->el && session->history)
+    {
+        int line_len;
+        line = (el_gets(session->el, &line_len));
+        if (!line)
+            return NULL;
+        if (line_len > size)
+        {
+            bcmos_printf("%s: buffer is too short %u - got %d. Truncated\n",
+                        __FUNCTION__, size, line_len);
+        }
+        strncpy(buffer, line, size);
+        if (*line && *line != '\n' && *line != '#')
+            history(session->history, &session->histevent, H_ENTER, line);
+    }
+    else
+#endif
+#ifdef CONFIG_LINENOISE
+    if (session && session->ln_session)
+    {
+        char *ln_line = linenoise(session->ln_session, session->prompt_buf, buffer, size);
+        if (ln_line)
+        {
+            if (strlen(ln_line))
+            {
+                linenoiseHistoryAdd(session->ln_session, ln_line); /* Add to the history. */
+            }
+            else
+            {
+                strncpy(buffer, "\n", size-1);
+            }
+            buffer[size-1] = 0;
+            line = buffer;
+        }
+    }
+    else
+#endif
+    {
+        bcmcli_session_print(session, "%s", session->prompt_buf);
+        if (session && session->parms.gets)
+            line = session->parms.gets(session, buffer, size);
+        else
+            line = fgets(buffer, size, stdin);
+    }
+    return line ? buffer : NULL;
+}
+
+#ifdef CONFIG_LIBEDIT
+
+static char *_bcmcli_editline_prompt(EditLine *e)
+{
+    bcmcli_session *session = NULL;
+    el_get(e, EL_CLIENTDATA, &session);
+    BUG_ON(session == NULL || session->magic != BCMCLI_SESSION_MAGIC);
+    _bcmcli_session_update_prompt(session);
+    return session->prompt_buf;
+}
+
+static int _bcmcli_editline_cfn(EditLine *el, char *c)
+{
+    bcmcli_session *session = NULL;
+    char insert_buf[80];
+    int c1;
+    bcmos_errno rc;
+
+    el_get(el, EL_CLIENTDATA, &session);
+    BUG_ON(session == NULL || session->magic != BCMCLI_SESSION_MAGIC);
+    c1 = session->parms.get_char(session);
+
+    /* ToDo: handle \t parameter extension */
+    while (c1 > 0 && c1 == '\t')
+    {
+
+        const LineInfo *li = el_line(el);
+        char *line = bcmos_alloc(li->cursor - li->buffer + 1);
+        if (!line)
+            continue;
+        memcpy(line, li->buffer, li->cursor - li->buffer);
+        line[li->cursor - li->buffer] = 0;
+        rc = bcmcli_extend(session, line, insert_buf, sizeof(insert_buf));
+        bcmos_free(line);
+        if (rc)
+        {
+            c1 = session->parms.get_char(session);
+            continue;
+        }
+        el_insertstr(el, insert_buf);
+        printf("\r");
+        el_set(el, EL_REFRESH, NULL);
+        c1 = session->parms.get_char(session);
+    }
+    if (c1 < 0)
+        return -1;
+    *c = c1;
+    return 1;
+}
+#endif
+
+/* linenoise line editing library: completion support */
+#ifdef CONFIG_LINENOISE
+
+static int _bcmcli_linenoise_read_char(long fd_in, char *c)
+{
+    bcmcli_session *session = (bcmcli_session *)fd_in;
+    int c1;
+    c1 = session->parms.get_char(session);
+    if (c1 < 0)
+    {
+        return -1;
+    }
+    *c = c1;
+    return 1;
+}
+
+static int _bcmcli_linenoise_write(long fd_out, const char *buf, size_t len)
+{
+    bcmcli_session *session = (bcmcli_session *)fd_out;
+    /* Use a shortcut for len==1 - which is char-by-char input.
+       bcmos_printf("%*s", buf, 1) misbehaves on vxw platform,
+       possibly because it is too slow.
+    */
+    if (len == 1 && !session->parms.write)
+    {
+        bcmos_putchar(buf[0]);
+        return 1;
+    }
+    return bcmcli_session_write(session, buf, len);
+}
+
+static int _bcmcli_linenoise_tab(linenoiseSession *ln_session, const char *buf, int pos)
+{
+    bcmcli_session *session = NULL;
+    char *line;
+    char insert_buf[80]="";
+    bcmos_errno rc;
+    int len;
+
+    session = linenoiseSessionData(ln_session);
+    BUG_ON(session == NULL || session->magic != BCMCLI_SESSION_MAGIC);
+    line = bcmos_alloc(strlen(buf)+1);
+    if (!line)
+        return 0;
+    strcpy(line, buf);
+    rc = bcmcli_extend(session, line, insert_buf, sizeof(insert_buf));
+    bcmos_free(line);
+    if (rc || !strlen(insert_buf))
+        return 0;
+
+    len = strlen(buf);
+    line = bcmos_alloc(strlen(buf)+strlen(insert_buf)+1);
+    if (!line)
+        return 0;
+    if (pos >=0 && pos < len)
+    {
+        strncpy(line, buf, pos);
+        line[pos] = 0;
+        strcat(line, insert_buf);
+        strcat(line, &buf[pos]);
+        pos += strlen(insert_buf);
+    }
+    else
+    {
+        strcpy(line, buf);
+        strcat(line, insert_buf);
+        pos = strlen(line);
+    }
+    linenoiseSetBuffer(ln_session, line, pos);
+    bcmos_free(line);
+    return 1;
+}
+
+#endif
+
+/* Default getc function */
+static int _bcmcli_session_get_char(bcmcli_session *session)
+{
+    return bcmos_getchar();
+}
+
+/** Initialize session management module
+ * \return
+ *      0   =OK\n
+ *      <0  =error code
+ */
+static void bcmcli_session_module_init(void)
+{
+    bcmos_fastlock_init(&session_lock, 0);
+    session_module_initialized = 1;
+}
+
+/** Open management session */
+int bcmcli_session_open_user(const bcmcli_session_parm *parm, bcmcli_session **p_session)
+{
+    bcmcli_session *session;
+    bcmcli_session **p_last_next;
+    const char *name;
+    char *name_clone;
+    long flags;
+    int size;
+
+    if (!p_session || !parm)
+        return BCM_ERR_PARM;
+#ifndef CONFIG_EDITLINE
+    if (parm->line_edit_mode == BCMCLI_LINE_EDIT_ENABLE)
+    {
+        bcmos_trace(BCMOS_TRACE_LEVEL_ERROR, "Line editing feature is not compiled in. define CONFIG_EDITLINE\n");
+        return BCM_ERR_NOT_SUPPORTED;
+    }
+#endif
+    if (!session_module_initialized)
+        bcmcli_session_module_init();
+    name = parm->name;
+    if (!name)
+        name = "*unnamed*";
+    size = sizeof(bcmcli_session) + strlen(name) + 1 + parm->extra_size;
+    session=bcmos_calloc(size);
+    if (!session)
+        return BCM_ERR_NOMEM;
+    session->parms = *parm;
+    name_clone = (char *)session + sizeof(bcmcli_session) + parm->extra_size;
+    strcpy(name_clone, name);
+    session->parms.name = name_clone;
+    if (!session->parms.get_char)
+        session->parms.get_char = _bcmcli_session_get_char;
+
+#ifdef CONFIG_LIBEDIT
+    if (!parm->gets && (parm->line_edit_mode == BCMCLI_LINE_EDIT_ENABLE ||
+        parm->line_edit_mode == BCMCLI_LINE_EDIT_DEFAULT))
+    {
+        /* Initialize editline library */
+        session->el = el_init(session->parms.name, stdin, stdout, stderr);
+        session->history = history_init();
+        if (session->el && session->history)
+        {
+            el_set(session->el, EL_EDITOR, "emacs");
+            el_set(session->el, EL_PROMPT, &_bcmcli_editline_prompt);
+            el_set(session->el, EL_TERMINAL, "xterm");
+            el_set(session->el, EL_GETCFN, _bcmcli_editline_cfn);
+            el_set(session->el, EL_CLIENTDATA, session);
+            history(session->history, &session->histevent, H_SETSIZE, 800);
+            el_set(session->el, EL_HIST, history, session->history);
+        }
+        else
+        {
+            bcmos_trace(BCMOS_TRACE_LEVEL_ERROR, "Can't initialize editline library\n");
+            bcmos_free(session);
+            return BCM_ERR_INTERNAL;
+        }
+    }
+#endif
+
+#ifdef CONFIG_LINENOISE
+    /* Set the completion callback. This will be called every time the
+     * user uses the <tab> key. */
+    if (!parm->gets && (parm->line_edit_mode == BCMCLI_LINE_EDIT_ENABLE ||
+                        parm->line_edit_mode == BCMCLI_LINE_EDIT_DEFAULT))
+    {
+        linenoiseSessionIO io={.fd_in=(long)session, .fd_out=(long)session,
+            .read_char=_bcmcli_linenoise_read_char,
+            .write=_bcmcli_linenoise_write
+        };
+        if (linenoiseSessionOpen(&io, session, &session->ln_session))
+        {
+            bcmos_trace(BCMOS_TRACE_LEVEL_ERROR, "Can't create linenoise session\n");
+            bcmos_free(session);
+            return BCM_ERR_INTERNAL;
+        }
+        linenoiseSetCompletionCallback(session->ln_session, _bcmcli_linenoise_tab);
+    }
+#endif
+
+    session->magic = BCMCLI_SESSION_MAGIC;
+
+    flags = bcmos_fastlock_lock(&session_lock);
+    p_last_next = &session_list;
+    while(*p_last_next)
+        p_last_next = &((*p_last_next)->next);
+    *p_last_next = session;
+    bcmos_fastlock_unlock(&session_lock, flags);
+
+    *p_session = session;
+
+    return 0;
+}
+
+static int bcmcli_session_string_write(bcmcli_session *session, const char *buf, uint32_t size)
+{
+    bcmolt_string *str = bcmcli_session_user_priv(session);
+    return bcmolt_string_copy(str, buf, size);
+}
+
+bcmos_errno bcmcli_session_open_string(bcmcli_session **session, bcmolt_string *str)
+{
+    bcmcli_session_parm sp = { .user_priv = str, .write = bcmcli_session_string_write };
+
+    return bcmcli_session_open_user(&sp, session);
+}
+
+/** Close management session.
+ * \param[in]   session         Session handle
+ */
+void bcmcli_session_close(bcmcli_session *session)
+{
+    long flags;
+
+    BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
+    flags = bcmos_fastlock_lock(&session_lock);
+    if (session==session_list)
+        session_list = session->next;
+    else
+    {
+        bcmcli_session *prev = session_list;
+        while (prev && prev->next != session)
+            prev = prev->next;
+        if (!prev)
+        {
+            bcmos_fastlock_unlock(&session_lock, flags);
+            bcmos_trace(BCMOS_TRACE_LEVEL_ERROR, "%s: can't find session\n", __FUNCTION__);
+            return;
+        }
+        prev->next = session->next;
+    }
+    bcmos_fastlock_unlock(&session_lock, flags);
+
+#ifdef CONFIG_LIBEDIT
+    if (session->history)
+        history_end(session->history);
+    if (session->el)
+        el_end(session->el);
+#endif
+#ifdef CONFIG_LINENOISE
+    if (session->ln_session)
+        linenoiseSessionClose(session->ln_session);
+#endif
+    session->magic = BCMCLI_SESSION_MAGIC_DEL;
+    bcmos_free(session);
+
+}
+
+/** Configure RAW input mode
+ *
+ * \param[in]       session         Session handle
+ * \param[in]       is_raw          TRUE=enable raw mode, FALSE=disable raw mode
+ * \return
+ *      =0  - OK \n
+ *      BCM_ERR_NOT_SUPPORTED - raw mode is not supported\n
+ */
+bcmos_errno bcmcli_session_raw_mode_set(bcmcli_session *session, bcmos_bool is_raw)
+{
+#ifdef CONFIG_LINENOISE
+    int rc;
+    if (session->parms.gets)
+        return BCM_ERR_NOT_SUPPORTED;
+    rc = linenoiseSetRaw(session->ln_session, is_raw);
+    return (rc == 0) ? BCM_ERR_OK : BCM_ERR_NOT_SUPPORTED;
+#else
+    return BCM_ERR_NOT_SUPPORTED;
+#endif
+}
+
+/** Default write callback function
+ * write to stdout
+ */
+static int _bcmcli_session_write(bcmcli_session *session, const char *buf, uint32_t size)
+{
+    return bcmos_printf("%.*s", size, buf);
+}
+
+
+/** Write function.
+ * Write buffer to the current session.
+ * \param[in]   session         Session handle. NULL=use stdout
+ * \param[in]   buffer          output buffer
+ * \param[in]   size            number of bytes to be written
+ * \return
+ *  >=0 - number of bytes written\n
+ *  <0  - output error
+ */
+int bcmcli_session_write(bcmcli_session *session, const char *buf, uint32_t size)
+{
+    int (*write_cb)(bcmcli_session *session, const char *buf, uint32_t size);
+    if (session && session->parms.write)
+    {
+        BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
+        write_cb = session->parms.write;
+    }
+    else
+        write_cb = _bcmcli_session_write;
+    return write_cb(session, buf, size);
+}
+
+
+/** Read line
+ * \param[in]       session         Session handle. NULL=use default
+ * \param[in,out]   buf             input buffer
+ * \param[in]       size            buf size
+ * \return
+ *      buf if successful
+ *      NULL if EOF or error
+ */
+char *bcmcli_session_gets(bcmcli_session *session, char *buf, uint32_t size)
+{
+    return _bcmcli_session_gets(session, buf, size);
+}
+
+
+/** Print function.
+ * Prints in the context of current session.
+ * \param[in]   session         Session handle. NULL=use stdout
+ * \param[in]   format          print format - as in printf
+ * \param[in]   ap              parameters list. Undefined after the call
+ */
+void bcmcli_session_vprint(bcmcli_session *session, const char *format, va_list ap)
+{
+    if (session && session->parms.write)
+    {
+        BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
+        vsnprintf(session->outbuf, sizeof(session->outbuf), format, ap);
+        bcmcli_session_write(session, session->outbuf, strlen(session->outbuf));
+    }
+    else
+        bcmos_vprintf(format, ap);
+}
+
+
+/** Print function.
+ * Prints in the context of current session.
+ * \param[in]   session         Session handle. NULL=use stdout
+ * \param[in]   format          print format - as in printf
+ */
+void bcmcli_session_print(bcmcli_session *session, const char *format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    bcmcli_session_vprint(session, format, ap);
+    va_end(ap);
+}
+
+/** Get user_priv provoded in session partameters when it was registered
+ * \param[in]       session         Session handle. NULL=use stdin
+ * \return usr_priv value
+ */
+void *bcmcli_session_user_priv(bcmcli_session *session)
+{
+    if (!session)
+        return NULL;
+    BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
+    return session->parms.user_priv;
+}
+
+
+/** Get extra data associated with the session
+ * \param[in]       session         Session handle. NULL=default session
+ * \return extra_data pointer or NULL if there is no extra data
+ */
+void *bcmcli_session_data(bcmcli_session *session)
+{
+    if (!session)
+        return NULL;
+    BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
+    if (session->parms.extra_size <= 0)
+        return NULL;
+    return (char *)session + sizeof(*session);
+}
+
+
+/** Get session namedata
+ * \param[in]       session         Session handle. NULL=default session
+ * \return session name
+ */
+const char *bcmcli_session_name(bcmcli_session *session)
+{
+    if (!session)
+        return NULL;
+    BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
+    return session->parms.name;
+}
+
+
+/** Get session access righte
+ * \param[in]       session         Session handle. NULL=default debug session
+ * \return session access right
+ */
+bcmcli_access_right bcmcli_session_access_right(bcmcli_session *session)
+{
+    if (!session)
+        return BCMCLI_ACCESS_DEBUG;
+    BUG_ON(session->magic != BCMCLI_SESSION_MAGIC);
+    return session->parms.access_right;
+}
+
+/** Print buffer in hexadecimal format
+ * \param[in]   session         Session handle. NULL=use stdout
+ * \param[in]   buffer          Buffer address
+ * \param[in]   offset          Start offset in the buffer
+ * \param[in]   count           Number of bytes to dump
+ * \param[in]   indent          Optional indentation string
+ */
+void bcmcli_session_hexdump(bcmcli_session *session, const void *buffer, uint32_t offset, uint32_t count, const char *indent)
+{
+    bcmos_hexdump((bcmos_msg_print_cb)bcmcli_session_print, session, buffer, offset, count, indent);
+}
+
+/*
+ * Exports
+ */
+EXPORT_SYMBOL(bcmcli_session_open);
+EXPORT_SYMBOL(bcmcli_session_close);
+EXPORT_SYMBOL(bcmcli_session_write);
+EXPORT_SYMBOL(bcmcli_session_vprint);
+EXPORT_SYMBOL(bcmcli_session_print);
+EXPORT_SYMBOL(bcmcli_session_access_right);
+EXPORT_SYMBOL(bcmcli_session_data);
+EXPORT_SYMBOL(bcmcli_session_name);
+EXPORT_SYMBOL(bcmcli_session_hexdump);
diff --git a/bcm68620_release/release/host_reference/cli/bcmcli_session.h b/bcm68620_release/release/host_reference/cli/bcmcli_session.h
new file mode 100644
index 0000000..30d873d
--- /dev/null
+++ b/bcm68620_release/release/host_reference/cli/bcmcli_session.h
@@ -0,0 +1,309 @@
+/*
+<: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.
+
+:>
+ */
+
+
+/*******************************************************************
+ * bcmcli_session.h
+ *
+ * BCM CLI engine - session management
+ *
+ *******************************************************************/
+
+#ifndef BCMCLI_SESSION_H
+
+#define BCMCLI_SESSION_H
+
+#include <bcmos_system.h>
+#include <stdarg.h>
+#include "bcmolt_string.h"
+
+/** \defgroup bcmcli_session Management Session Control
+ *
+ * APIs in this header file allow to create/destroy management sessions.
+ * Management session is characterized by its access level and also
+ * input/output functions.
+ * Management sessions allow managed entities in the system to communicate
+ * with local or remote managers (e.g., local or remote shell or NMS)
+ * @{
+ */
+
+/** Access rights */
+typedef enum
+{
+    BCMCLI_ACCESS_GUEST,     /**< Guest. Doesn't have access to commands and directories registered with ADMIN rights */
+    BCMCLI_ACCESS_ADMIN,     /**< Administrator: full access */
+    BCMCLI_ACCESS_DEBUG,     /**< Administrator: full access + extended debug features */
+} bcmcli_access_right;
+
+/** Line edit mode */
+typedef enum
+{
+    BCMCLI_LINE_EDIT_DEFAULT,/**< Enable line editing and history if CONFIG_EDITLINE is defined, disable otherwise */
+    BCMCLI_LINE_EDIT_ENABLE, /**< Enable line editing. Requires CONFIG_EDITLINE define and libedit-dev library */
+    BCMCLI_LINE_EDIT_DISABLE,/**< Disable line editing and history */
+} bcmcli_line_edit_mode;
+
+
+/** Management session handle
+ */
+typedef struct bcmcli_session bcmcli_session;
+
+
+/** Session parameters structure.
+ * See \ref bcmcli_session_open
+ */
+typedef struct bcmcli_session_parm
+{
+    const char *name;       /**< Session name */
+    void *user_priv;        /**< Private user's data */
+
+    /** Session's output function. NULL=use write(stdout)
+     * returns the number of bytes written or <0 if error
+     */
+    int (*write)(bcmcli_session *session, const char *buf, uint32_t size);
+
+    /** Session line input function. NULL=use default(stdin[+line edit) */
+    char *(*gets)(bcmcli_session *session, char *buf, uint32_t size);
+
+    /** Session char input function. NULL=use bcmos_getchar() */
+    int (*get_char)(bcmcli_session *session);
+
+    /** Fill the specified buffer with the prompt for this session (NULL-terminated). NULL = no prompt. */
+    void (*get_prompt)(bcmcli_session *session, char *buf, uint32_t max_len);
+
+    /** Access rights */
+    bcmcli_access_right access_right;
+
+    /** Line editing mode */
+    bcmcli_line_edit_mode line_edit_mode;
+
+    /** Extra data size to be allocated along with session control block.
+     * The extra data is accessible using bcmcli_session_data().
+     * Please note that if session is created using bcmcli_session_open(),
+     * extra_size is reserved.
+     * It can only be used for user context allocation if session is created
+     * using bcmcli_session_open_user()
+     */
+    uint32_t extra_size;
+} bcmcli_session_parm;
+
+
+/** Open monitor session
+ *
+ * Monitor supports multiple simultaneous sessions with different
+ * access rights.
+ * Note that there already is a default session with full administrative rights,
+ * that takes input from stdin and outputs to stdout.
+ *
+ * Please don't use parm.extra_size. This field is reserved.
+ *
+ * \param[in]   parm        Session parameters. Must not be allocated on the stack.
+ * \param[out]  p_session   Session handle
+ * \return
+ *      0   =OK\n
+ *      <0  =error code
+ */
+bcmos_errno bcmcli_session_open(const bcmcli_session_parm *parm, bcmcli_session **p_session);
+
+
+/** Close monitor session.
+ * \param[in]   session         Session handle
+ */
+void bcmcli_session_close(bcmcli_session *session );
+
+
+/** Write function.
+ * Write buffer to the current session.
+ * \param[in]   session         Session handle. NULL=use stdout
+ * \param[in]   buf             output buffer
+ * \param[in]   size            number of bytes to be written
+ * \return
+ *  >=0 - number of bytes written\n
+ *  <0  - output error
+ */
+int bcmcli_session_write(bcmcli_session *session, const char *buf, uint32_t size);
+
+
+/** Read line
+ * \param[in]       session         Session handle. NULL=use default
+ * \param[in,out]   buf             input buffer
+ * \param[in]       size            buf size
+ * \return
+ *      buf if successful
+ *      NULL if EOF or error
+ */
+char *bcmcli_session_gets(bcmcli_session *session, char *buf, uint32_t size);
+
+
+/** Print function.
+ * Prints in the context of current session.
+ * \param[in]   session         Session handle. NULL=use stdout
+ * \param[in]   format          print format - as in printf
+ */
+void bcmcli_session_print(bcmcli_session *session, const char *format, ...)
+#ifndef BCMCLI_SESSION_DISABLE_FORMAT_CHECK
+__attribute__((format(printf, 2, 3)))
+#endif
+;
+
+
+/** Print function.
+ * Prints in the context of current session.
+ * \param[in]   session         Session handle. NULL=use stdout
+ * \param[in]   format          print format - as in printf
+ * \param[in]   ap              parameters list. Undefined after the call
+ */
+void bcmcli_session_vprint(bcmcli_session *session, const char *format, va_list ap);
+
+/** Print buffer in hexadecimal format
+ * \param[in]   session         Session handle. NULL=use stdout
+ * \param[in]   buffer          Buffer address
+ * \param[in]   offset          Start offset in the buffer
+ * \param[in]   count           Number of bytes to dump
+ * \param[in]   indent          Optional indentation string
+ */
+void bcmcli_session_hexdump(bcmcli_session *session, const void *buffer, uint32_t offset, uint32_t count, const char *indent);
+
+/** Get extra data associated with the session
+ * \param[in]       session         Session handle. NULL=default session
+ * \return extra_data pointer or NULL if there is no extra data
+ */
+void *bcmcli_session_data(bcmcli_session *session);
+
+
+/** Get user_priv provided in session parameters when it was registered
+ * \param[in]       session         Session handle. NULL=default session
+ * \return usr_priv value
+ */
+void *bcmcli_session_user_priv(bcmcli_session *session);
+
+
+/** Get session name
+ * \param[in]       session         Session handle. NULL=use stdin
+ * \return session name
+ */
+const char *bcmcli_session_name(bcmcli_session *session);
+
+
+/** Get session access rights
+ * \param[in]       session         Session handle. NULL=default debug session
+ * \return session access right
+ */
+bcmcli_access_right bcmcli_session_access_right(bcmcli_session *session);
+
+/** @} end of bcmcli_session group */
+
+/** Low-level interface for when session is used outside CLI
+ *
+ * \param[in]   parm        Session parameters. Must not be allocated on the stack.
+ * \param[out]  p_session   Session handle
+ * \return
+ *      0   =OK\n
+ *      <0  =error code
+ */
+int bcmcli_session_open_user(const bcmcli_session_parm *parm, bcmcli_session **p_session);
+
+/** Open a session that prints to the specified string
+ */
+bcmos_errno bcmcli_session_open_string(bcmcli_session **session, bcmolt_string *str);
+
+/** Configure RAW input mode
+ *
+ * \param[in]       session         Session handle
+ * \param[in]       is_raw          TRUE=enable raw mode, FALSE=disable raw mode
+ * \return
+ *      =0  - OK \n
+ *      BCM_ERR_NOT_SUPPORTED - raw mode is not supported\n
+ */
+bcmos_errno bcmcli_session_raw_mode_set(bcmcli_session *session, bcmos_bool is_raw);
+
+/** Context extension
+ *
+ * - if no command - display list of command or extend command
+ * - if prev char is " "
+ *      - if positional and next parm is enum - show/extends list of matching values
+ *      - else - show/extend list of unused parameters
+ *   else
+ *      - if entering value and enum - show/extends list of matching values
+ *      - else - show/extend list of matching unused parameters
+ *
+ * \param[in]       session         Session handle
+ * \param[in]       input_string    String to be parsed
+ * \param[out]      insert_str      String to insert at cursor position
+ * \param[in]       insert_size     Insert buffer size
+ * \return
+ *      =0  - OK \n
+ *      BCM_ERR_PARM - parsing error\n
+ */
+bcmos_errno bcmcli_extend(bcmcli_session *session, char *input_string,
+    char *insert_str, uint32_t insert_size);
+
+
+#ifdef BCMCLI_INTERNAL
+
+#define BCMCLI_SESSION_OUTBUF_LEN 2048
+#define BCMCLI_MAX_PROMPT_LEN 8
+
+/* editline functionality */
+/* If libedit is included - it takes precedence */
+#ifdef CONFIG_LIBEDIT
+#include <histedit.h>
+#undef CONFIG_LINENOISE
+#endif /* #ifdef CONFIG_LIBEDIT */
+
+#ifdef CONFIG_LINENOISE
+#include <linenoise.h>
+#endif
+
+/* Management session structure */
+struct bcmcli_session
+{
+    bcmcli_session *next;
+    bcmcli_session_parm parms;
+    uint32_t magic;
+#define BCMCLI_SESSION_MAGIC            (('s'<<24)|('e'<<16)|('s'<<8)|'s')
+#define BCMCLI_SESSION_MAGIC_DEL        (('s'<<24)|('e'<<16)|('s'<<8)|'~')
+
+    /* Line editing and history support */
+#ifdef CONFIG_LIBEDIT
+    EditLine *el;
+    History *history;
+    HistEvent histevent;
+#endif
+#ifdef CONFIG_LINENOISE
+    linenoiseSession *ln_session;
+#endif
+    char outbuf[BCMCLI_SESSION_OUTBUF_LEN];
+    char prompt_buf[BCMCLI_MAX_PROMPT_LEN];
+
+    /* Followed by session data */
+};
+#endif
+
+#endif /* #ifndef BCMCLI_SESSION_H */