BAL and Maple Release 2.2

Signed-off-by: Shad Ansari <developer@Carbon.local>
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);