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