BAL and Maple Release 2.2
Signed-off-by: Shad Ansari <developer@Carbon.local>
diff --git a/bal_release/src/lib/libbalapicli/bal_api_cli.c b/bal_release/src/lib/libbalapicli/bal_api_cli.c
new file mode 100644
index 0000000..cbbe973
--- /dev/null
+++ b/bal_release/src/lib/libbalapicli/bal_api_cli.c
@@ -0,0 +1,1059 @@
+/*
+<:copyright-BRCM:2016:DUAL/GPL:standard
+
+ Broadcom Proprietary and Confidential.(c) 2016 Broadcom
+ All Rights Reserved
+
+Unless you and Broadcom execute a separate written software license
+agreement governing use of this software, this software is licensed
+to you under the terms of the GNU General Public License version 2
+(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+with the following added to such license:
+
+ As a special exception, the copyright holders of this software give
+ you permission to link this software with independent modules, and
+ to copy and distribute the resulting executable under terms of your
+ choice, provided that you also meet, for each linked independent
+ module, the terms and conditions of the license of that module.
+ An independent module is a module which is not derived from this
+ software. The special exception does not apply to any modifications
+ of the software.
+
+Not withstanding the above, under no circumstances may you combine
+this software in any way with any other Broadcom software provided
+under a license other than the GPL, without Broadcom's express prior
+written consent.
+
+:>
+ */
+
+#include <bcmos_system.h>
+#include <bcmcli.h>
+#include <bal_api.h>
+#include "bal_api_cli.h"
+#include "bal_api_cli_helpers.h"
+#include "bal_api_cli_handlers.h"
+
+#define BCMBAL_APICLI_CAST_DISCARD_CONST(p, type) (type)((long)(p))
+
+/* bool enum table */
+static bcmcli_enum_val bool_enum[] =
+{
+ { .name = "yes", .val = 1 },
+ { .name = "no", .val = 0 },
+ BCMCLI_ENUM_LAST
+};
+
+static bcmbal_apicli_type_descr bool_type_descr = {
+ .name = "bool",
+ .descr = "Boolean",
+ .base_type = BCMBAL_APICLI_BASE_TYPE_ID_ENUM,
+ .size = sizeof(bcmos_bool),
+ .x = {.e = bool_enum}
+};
+
+/* parameter data */
+typedef struct
+{
+ const bcmbal_apicli_prop_descr *prop; /* property */
+ const bcmbal_apicli_field_descr *field; /* field or NULL */
+ const bcmbal_apicli_field_descr *array_fd; /* array field descriptor or NULL */
+ uint16_t offset; /* offset from the beginning of the property */
+ uint16_t array_fd_offset; /* offset of array_fd from the beginning of the property */
+ bcmbal_mgt_group group; /* management group */
+} bcmbal_apicli_parm_data;
+
+typedef enum
+{
+ BCMBAL_APICLI_FLAGS_NONE = 0,
+ BCMBAL_APICLI_FLAGS_IGNORE_FIELDS = 1 << 0
+} bcmbal_apicli_flags;
+
+/* Current session */
+static bcmcli_session *current_session;
+
+/*
+ * helpers
+ */
+
+/* calculate number of fields in type */
+static uint32_t bcmbal_apicli_get_num_fields_in_type(const bcmbal_apicli_type_descr *td)
+{
+ uint16_t f;
+ uint32_t nf = 0;
+
+
+ switch (td->base_type)
+ {
+ case BCMBAL_APICLI_BASE_TYPE_ID_STRUCT:
+ {
+ if (!td->x.s.num_fields)
+ return 0;
+ BUG_ON(!td->x.s.fields);
+ for (f = 0; f < td->x.s.num_fields; f++)
+ {
+ nf += bcmbal_apicli_get_num_fields_in_type(td->x.s.fields[f].type);
+ }
+ break;
+ }
+
+ case BCMBAL_APICLI_BASE_TYPE_ID_UNION:
+ {
+ /* Union. Count only common fields */
+ nf = td->x.u.num_common_fields;
+ break;
+ }
+
+ case BCMBAL_APICLI_BASE_TYPE_ID_ARR_FIXED:
+ {
+ nf = bcmbal_apicli_get_num_fields_in_type(td->x.arr_fixed.elem_type);
+ break;
+ }
+
+ case BCMBAL_APICLI_BASE_TYPE_ID_ARR_DYN:
+ {
+ nf = bcmbal_apicli_get_num_fields_in_type(td->x.arr_dyn.elem_type);
+ break;
+ }
+
+ default:
+ {
+ nf = 1;
+ break;
+ }
+ }
+
+ return nf;
+}
+
+/* calculate number of property fields for given object+group+subgroup+access. simple property=single field */
+static bcmos_errno bcmbal_apicli_get_num_fields_in_group(bcmbal_obj_id o, bcmbal_mgt_group group, uint16_t subgroup,
+ bcmbal_apicli_prop_access_id access_level, uint32_t *nfields)
+{
+ uint32_t nf = 0;
+ int i;
+ bcmos_errno rc = BCM_ERR_OK;
+
+ for (i = 0; rc != BCM_ERR_RANGE; i++)
+ {
+ const bcmbal_apicli_prop_descr *pd;
+ rc = bcmbal_apicli_object_property(o, group, subgroup, i, &pd);
+ if (rc == BCM_ERR_OK && (pd->access & access_level))
+ {
+ /* Calculate number of fields if write access. Count only properties for read access */
+ if ((access_level & BCMBAL_APICLI_PROP_ACCESS_ID_W) != 0)
+ {
+ BUG_ON(!pd->type);
+ nf += bcmbal_apicli_get_num_fields_in_type(pd->type);
+ }
+ else
+ {
+ ++nf;
+ }
+ }
+ }
+ *nfields = nf;
+
+ return BCM_ERR_OK;
+}
+
+/*
+ * Command handlers
+ */
+
+static bcmos_errno bcmbal_apicli_objects_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ int rc;
+ bcmbal_obj_id o;
+ const char *name, *descr;
+
+ bcmcli_print(session, "System Object Types:\n");
+ bcmcli_print(session, "=======================================\n");
+ bcmcli_print(session, "Id Name Description\n");
+ bcmcli_print(session, "=======================================\n");
+ for (o = 0; o < BCMBAL_OBJ_ID__NUM_OF; o++)
+ {
+ rc = bcmbal_apicli_object_name(o, &name, &descr);
+ if (!rc)
+ bcmcli_print(session, "%.4d %-22s %s\n", o, name, descr);
+ }
+
+ return 0;
+}
+
+static bcmos_errno bcmbal_apicli_set_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ return bcmbal_apicli_call(BCMBAL_MGT_GROUP_CFG, BCMBAL_OBJ_MSG_TYPE_SET, session);
+}
+
+static bcmos_errno bcmbal_apicli_get_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ return bcmbal_apicli_call(BCMBAL_MGT_GROUP_CFG, BCMBAL_OBJ_MSG_TYPE_GET, session);
+}
+
+static bcmos_errno bcmbal_apicli_clear_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ return bcmbal_apicli_call(BCMBAL_MGT_GROUP_CFG, BCMBAL_OBJ_MSG_TYPE_CLEAR, session);
+}
+
+static bcmos_errno bcmbal_apicli_stat_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ return bcmbal_apicli_call(BCMBAL_MGT_GROUP_STAT, BCMBAL_OBJ_MSG_TYPE_GET, session);
+}
+
+/*
+ * Init-time helpers
+ */
+
+/* map to CLI type */
+static bcmos_errno bcmbal_apicli_map_type(
+ const bcmbal_apicli_type_descr *td,
+ const bcmbal_apicli_type_descr *array_td,
+ bcmcli_cmd_parm *cmd_parm)
+{
+ bcmbal_apicli_parm_data *parm_data = cmd_parm->user_data;
+ bcmos_errno rc = BCM_ERR_OK;
+
+ /* Map type */
+ switch(td->base_type)
+ {
+ case BCMBAL_APICLI_BASE_TYPE_ID_SNUM:
+ cmd_parm->type = BCMCLI_PARM_NUMBER;
+ break;
+ case BCMBAL_APICLI_BASE_TYPE_ID_UNUM:
+ cmd_parm->type = BCMCLI_PARM_UNUMBER;
+ break;
+ case BCMBAL_APICLI_BASE_TYPE_ID_UNUM_HEX:
+ cmd_parm->type = BCMCLI_PARM_HEX;
+ break;
+ case BCMBAL_APICLI_BASE_TYPE_ID_BOOL:
+ cmd_parm->type = BCMCLI_PARM_ENUM;
+ cmd_parm->enum_table = bool_enum;
+ break;
+ case BCMBAL_APICLI_BASE_TYPE_ID_FLOAT:
+ cmd_parm->type = td->size == sizeof(double) ? BCMCLI_PARM_DOUBLE : BCMCLI_PARM_FLOAT;
+ break;
+ case BCMBAL_APICLI_BASE_TYPE_ID_STRING:
+ cmd_parm->type = BCMCLI_PARM_STRING;
+ break;
+ case BCMBAL_APICLI_BASE_TYPE_ID_IPV4:
+ cmd_parm->type = BCMCLI_PARM_IP;
+ break;
+ case BCMBAL_APICLI_BASE_TYPE_ID_MAC:
+ cmd_parm->type = BCMCLI_PARM_MAC;
+ break;
+ case BCMBAL_APICLI_BASE_TYPE_ID_ENUM:
+ cmd_parm->type = BCMCLI_PARM_ENUM;
+ cmd_parm->enum_table = td->x.e;
+ break;
+ case BCMBAL_APICLI_BASE_TYPE_ID_ENUM_MASK:
+ cmd_parm->type = BCMCLI_PARM_ENUM_MASK;
+ cmd_parm->enum_table = td->x.e;
+ break;
+ default:
+ bcmcli_print(current_session, "*** can't map type %s (%d)\n", td->name, (int)td->base_type);
+ rc = BCM_ERR_NOT_SUPPORTED;
+ break;
+ }
+
+ /* Map uint8_t array to buffer if it is independent (not structure field) */
+ if (array_td &&
+ td->size == 1 &&
+ (td->base_type == BCMBAL_APICLI_BASE_TYPE_ID_UNUM || td->base_type == BCMBAL_APICLI_BASE_TYPE_ID_UNUM_HEX) &&
+ (parm_data->array_fd == parm_data->field || !parm_data->field))
+ {
+ cmd_parm->type = BCMCLI_PARM_BUFFER;
+ }
+
+ return rc;
+}
+
+/* allocate memory for name and description and copy to to parm */
+static bcmos_errno bcmbal_apicli_copy_parm_name(bcmcli_cmd_parm *parm, const char *name, const char *descr)
+{
+ parm->name = bcmos_alloc(strlen(name) + 1);
+ parm->description = bcmos_alloc(strlen(descr) + 1);
+ if ((parm->name == NULL) || (parm->description == NULL))
+ {
+ /* Successful allocation if any will be released by common cleanup
+ * along with the rest of dynamic parameter fields */
+ return BCM_ERR_NOMEM;
+ }
+ strcpy(BCMBAL_APICLI_CAST_DISCARD_CONST(parm->name, void *), name);
+ strcpy(BCMBAL_APICLI_CAST_DISCARD_CONST(parm->description, void *), descr);
+ return BCM_ERR_OK;
+}
+
+/* populate single parameter */
+static int bcmbal_apicli_populate_parm1(
+ const bcmbal_apicli_prop_descr *pd,
+ const bcmbal_apicli_field_descr *fd,
+ const bcmbal_apicli_type_descr *td,
+ const bcmbal_apicli_field_descr *array_fd,
+ uint32_t offset,
+ uint32_t array_fd_offset,
+ bcmcli_cmd_parm *cmd_parm,
+ uint32_t cmd_flags,
+ char *name,
+ char *help)
+{
+ bcmbal_apicli_parm_data *parm_data = cmd_parm->user_data;
+ int rc;
+
+ parm_data->prop = pd;
+ parm_data->field = fd;
+ parm_data->offset = offset;
+ parm_data->array_fd = array_fd;
+ parm_data->array_fd_offset = array_fd_offset;
+
+ rc = bcmbal_apicli_copy_parm_name(cmd_parm, name, help);
+ if (rc)
+ {
+ return rc;
+ }
+ cmd_parm->flags = cmd_flags;
+ if (td->min_val != td->max_val || td->min_val)
+ {
+ cmd_parm->flags |= BCMCLI_PARM_FLAG_RANGE;
+ cmd_parm->low_val = td->min_val;
+ cmd_parm->hi_val = td->max_val;
+ }
+ rc = bcmbal_apicli_map_type(td, array_fd ? array_fd->type : NULL, cmd_parm);
+ if (rc < 0)
+ {
+ return rc;
+ }
+
+ /* Arrays require more work.
+ * - Calculate size. Known for fixed arrays, hard-coded max for dynamic
+ * - Allocate either buffer or array of values based on CLI parameter type
+ * - Calculate offset from the beginning of array entry
+ */
+ if (array_fd)
+ {
+ uint32_t array_size;
+
+ if (array_fd->type->base_type == BCMBAL_APICLI_BASE_TYPE_ID_ARR_FIXED)
+ {
+ array_size = array_fd->type->x.arr_fixed.size;
+ }
+ else
+ {
+ array_size = array_fd->type->x.arr_dyn.max_size;
+ }
+ if (!array_size)
+ {
+ bcmcli_print(current_session, "*** Error in %s array descriptor. Size is not set.\n", array_fd->name);
+ return BCM_ERR_INTERNAL;
+ }
+ if (cmd_parm->type == BCMCLI_PARM_BUFFER)
+ {
+ rc = bcmbal_buf_alloc(&cmd_parm->value.buffer, array_size);
+ if (rc)
+ {
+ return rc;
+ }
+ }
+ else
+ {
+ cmd_parm->values = bcmos_calloc(sizeof(bcmcli_parm_value) * array_size);
+ if (!cmd_parm->values)
+ {
+ return BCM_ERR_NOMEM;
+ }
+ cmd_parm->max_array_size = array_size;
+ }
+ }
+
+ return 1;
+}
+
+
+/* populate name buf and help buf */
+static void bcmbal_apicli_populate_name_help(const bcmbal_apicli_field_descr *fld, char *name_buf0, char *help_buf0,
+ char *name_buf, char *help_buf)
+{
+ name_buf[0] = 0;
+ help_buf[0] = 0;
+ bcmcli_strncpy(name_buf, name_buf0, BCMBAL_APICLI_MAX_PARM_NAME_LENGTH);
+ if (strlen(name_buf))
+ bcmcli_strncat(name_buf, ".", BCMBAL_APICLI_MAX_PARM_NAME_LENGTH);
+ bcmcli_strncat(name_buf, fld->cli_name ? fld->cli_name : fld->name, BCMBAL_APICLI_MAX_PARM_NAME_LENGTH);
+ bcmcli_strncpy(help_buf, help_buf0, BCMBAL_APICLI_MAX_PARM_HELP_LENGTH);
+ bcmcli_strncat(help_buf, " - ", BCMBAL_APICLI_MAX_PARM_HELP_LENGTH);
+ bcmcli_strncat(help_buf, fld->descr ? fld->descr : fld->name, BCMBAL_APICLI_MAX_PARM_HELP_LENGTH);
+}
+
+/* Allocate CLI parameter array. Set up parm->data */
+static bcmcli_cmd_parm *bcmbal_apicli_parm_alloc(int nparms)
+{
+ uint32_t size;
+ bcmcli_cmd_parm *parms;
+ bcmbal_apicli_parm_data *parm_data;
+ int i;
+
+ /* Allocate parameter table and populate it */
+ size = (sizeof(bcmcli_cmd_parm) + sizeof(bcmbal_apicli_parm_data)) * (nparms + 1);
+ parms = bcmos_calloc(size);
+ if (!parms)
+ return NULL;
+
+ /* Associate parameter_data structs with parameters */
+ parm_data = (bcmbal_apicli_parm_data *)(parms + nparms + 1);
+ for (i = 0; i < nparms; i++)
+ {
+ parms[i].user_data = &parm_data[i];
+ }
+ return parms;
+}
+
+/* clone enum table */
+static bcmcli_enum_val *bcmbal_apicli_clone_enum_table(bcmcli_cmd_parm *parm)
+{
+ bcmcli_enum_val *org_table = parm->enum_table;
+ bcmcli_enum_val *val = org_table;
+ bcmcli_enum_val *clone_table = org_table;
+ int i, n;
+
+ BUG_ON(parm->type != BCMCLI_PARM_ENUM);
+ while (val && val->name)
+ {
+ ++val;
+ }
+ n = val - org_table;
+
+ clone_table = bcmos_calloc(sizeof(bcmcli_enum_val) * (n + 1));
+ if (!clone_table)
+ {
+ return NULL;
+ }
+ for (i = 0; i < n; i++)
+ {
+ clone_table[i].name = org_table[i].name;
+ clone_table[i].val = org_table[i].val;
+ }
+ return clone_table;
+}
+
+
+/* populate CLI parameter(s) from a single property. Can be multiple parameters
+ * if property contains multiple fields.
+ * Returns number of parameters populated >= 0 or error < 0
+ */
+static int bcmbal_apicli_populate_parms_from_property(const bcmbal_apicli_prop_descr *pd,
+ const bcmbal_apicli_field_descr *fd, const bcmbal_apicli_field_descr *array_fd, uint32_t offset,
+ uint32_t array_fd_offset, bcmcli_cmd_parm *parms, bcmbal_apicli_prop_access_id access_level, uint32_t cmd_flags,
+ char *name_buf0, char *help_buf0)
+{
+ const bcmbal_apicli_type_descr *td = fd ? fd->type : pd->type;
+ uint32_t nf = 0;
+ char name_buf[BCMBAL_APICLI_MAX_PARM_NAME_LENGTH];
+ char help_buf[BCMBAL_APICLI_MAX_PARM_HELP_LENGTH];
+ int rc = 0;
+
+ /* presence masks are not set directly, they are calculated based on other fields */
+ if (fd != NULL && (fd->flags & BCMBAL_APICLI_FIELD_DESCR_FLAGS_PRESENCE_MASK) != 0)
+ {
+ return BCM_ERR_READ_ONLY;
+ }
+
+ /* At top level take name from property */
+ if (td == pd->type)
+ {
+ /* In case there's a global prefix */
+ char *top_name_buf = name_buf0;
+ uint32_t top_name_buf_len = BCMBAL_APICLI_MAX_PARM_NAME_LENGTH;
+ uint32_t prefix_len = strlen(name_buf0);
+ if (prefix_len > 0)
+ {
+ top_name_buf += prefix_len;
+ top_name_buf_len -= prefix_len;
+ }
+
+ bcmcli_strncpy(top_name_buf, pd->cli_name ? pd->cli_name : pd->name, top_name_buf_len);
+ bcmcli_strncpy(help_buf0, pd->descr ? pd->descr : pd->name, BCMBAL_APICLI_MAX_PARM_HELP_LENGTH);
+ }
+
+ /* For read access we only mark whether read property or not. It is not field-by-field operation */
+ if (access_level == BCMBAL_APICLI_PROP_ACCESS_ID_R)
+ {
+ td = &bool_type_descr;
+ }
+
+ /* In case of arrays we should
+ * - check that there is no array in array. It is not supported
+ * - store array type descriptor FFU and replace the "current" type descriptor with element type
+ * - reset offset because for array fields it should be calculated from array base rather than property
+ */
+ if (td->base_type == BCMBAL_APICLI_BASE_TYPE_ID_ARR_DYN || td->base_type == BCMBAL_APICLI_BASE_TYPE_ID_ARR_FIXED)
+ {
+ if (array_fd)
+ {
+ bcmcli_print(
+ current_session,
+ "*** %s in %s: arrays-in-arrays are not supported\n",
+ pd->name,
+ array_fd->name);
+ return BCM_ERR_NOT_SUPPORTED;
+ }
+ /* store array type and fetch element type */
+ array_fd = fd ? fd : (const bcmbal_apicli_field_descr *)pd;
+ if (td->base_type == BCMBAL_APICLI_BASE_TYPE_ID_ARR_DYN)
+ {
+ td = td->x.arr_dyn.elem_type;
+ }
+ else
+ {
+ td = td->x.arr_fixed.elem_type;
+ }
+ array_fd_offset = offset;
+ offset = 0;
+ }
+
+ if (td->base_type == BCMBAL_APICLI_BASE_TYPE_ID_STRUCT)
+ {
+ uint16_t f;
+ if (!td->x.s.num_fields)
+ return 0;
+ BUG_ON(!td->x.s.fields);
+ for (f = 0; f < td->x.s.num_fields; f++)
+ {
+ const bcmbal_apicli_field_descr *fld = &td->x.s.fields[f];
+ bcmbal_apicli_populate_name_help(fld, name_buf0, help_buf0, name_buf, help_buf);
+ rc = bcmbal_apicli_populate_parms_from_property(pd, fld, array_fd, offset+fld->offset,
+ array_fd_offset, &parms[nf], access_level, cmd_flags, name_buf, help_buf);
+ if (rc > 0)
+ nf += rc;
+ }
+ }
+ else if (td->base_type == BCMBAL_APICLI_BASE_TYPE_ID_UNION)
+ {
+ /* Union */
+ uint16_t f;
+ const bcmbal_apicli_field_descr *fld;
+ bcmcli_cmd_parm *sel_parm;
+ bcmbal_apicli_parm_data *sel_data;
+ bcmcli_enum_val *e;
+
+ if (!td->x.u.num_common_fields)
+ return 0;
+ BUG_ON(!td->x.u.common_fields);
+
+ /* Populate parameters preceding the union selector */
+ for (f = 0; f < td->x.u.classifier_idx; f++)
+ {
+ fld = &td->x.u.common_fields[f];
+ bcmbal_apicli_populate_name_help(fld, name_buf0, help_buf0, name_buf, help_buf);
+ rc = bcmbal_apicli_populate_parms_from_property(pd, fld, array_fd,
+ offset+fld->offset, array_fd_offset, &parms[nf], access_level, cmd_flags, name_buf, help_buf);
+ if (rc > 0)
+ nf += rc;
+ }
+
+ /* Now populate parameter for selector */
+ sel_parm = &parms[nf];
+ fld = &td->x.u.common_fields[f];
+ bcmbal_apicli_populate_name_help(fld, name_buf0, help_buf0, name_buf, help_buf);
+ rc = bcmbal_apicli_populate_parms_from_property(pd, fld, array_fd,
+ offset+fld->offset, array_fd_offset, sel_parm, access_level, cmd_flags, name_buf, help_buf);
+ if (rc > 0)
+ nf += rc;
+ /* Clone enum table in order to allow modifying it */
+ if (rc >= 1)
+ {
+ sel_parm->enum_table = bcmbal_apicli_clone_enum_table(sel_parm);
+ if (!sel_parm->enum_table)
+ {
+ rc = BCM_ERR_NOMEM;
+ }
+ }
+
+ /* Now set-up selector */
+ sel_parm->flags |= BCMCLI_PARM_FLAG_SELECTOR;
+ sel_data = sel_parm->user_data;
+ e = sel_parm->enum_table;
+ while (e && e->name && rc >= 0)
+ {
+ fld = &td->x.u.union_fields[e - sel_parm->enum_table];
+ if (fld->type)
+ {
+ int np = bcmbal_apicli_get_num_fields_in_type(fld->type);
+ int i;
+
+ e->parms = bcmbal_apicli_parm_alloc(np);
+ if (!e->parms)
+ {
+ rc = BCM_ERR_NOMEM;
+ break;
+ }
+ for (i = 0; i < np; i++)
+ {
+ bcmbal_apicli_parm_data *data = e->parms[i].user_data;
+ data->group = sel_data->group;
+ }
+ /* Collapse substructure name */
+ if (fld->type->base_type == BCMBAL_APICLI_BASE_TYPE_ID_STRUCT ||
+ fld->type->base_type == BCMBAL_APICLI_BASE_TYPE_ID_UNION)
+ {
+ bcmcli_strncpy(name_buf, name_buf0, sizeof(name_buf));
+ bcmcli_strncpy(help_buf, help_buf0, sizeof(help_buf));
+ }
+ else
+ {
+ bcmbal_apicli_populate_name_help(fld, name_buf0, help_buf0, name_buf, help_buf);
+ }
+ rc = bcmbal_apicli_populate_parms_from_property(pd, fld, array_fd,
+ offset+fld->offset, array_fd_offset, e->parms, access_level, cmd_flags, name_buf, help_buf);
+ }
+ ++e;
+ }
+
+ /* Finally populate parameters following the selector parameter */
+ for (f = td->x.u.classifier_idx + 1; f < td->x.u.num_common_fields && rc >= 0; f++)
+ {
+ fld = &td->x.u.common_fields[f];
+ bcmbal_apicli_populate_name_help(fld, name_buf0, help_buf0, name_buf, help_buf);
+ rc = bcmbal_apicli_populate_parms_from_property(pd, fld, array_fd,
+ offset+fld->offset, array_fd_offset, &parms[nf], access_level, cmd_flags, name_buf, help_buf);
+ if (rc > 0)
+ nf += rc;
+ }
+ }
+ else
+ {
+ /* Finally! Simple type that maps to a single CLI parameter */
+ nf = bcmbal_apicli_populate_parm1(pd, fd, td, array_fd, offset, array_fd_offset,
+ &parms[0], cmd_flags, name_buf0, help_buf0);
+ }
+ return (rc >= 0) ? nf : rc;
+}
+
+/* populate CLI parameter table */
+static int bcmbal_apicli_populate_parms(
+ bcmbal_obj_id o,
+ bcmbal_mgt_group group,
+ uint16_t subgroup,
+ bcmbal_apicli_prop_access_id access_level,
+ bcmcli_cmd_parm *parms,
+ uint32_t cmd_flags,
+ const char *prefix)
+{
+ int nf = 0;
+ int i;
+ bcmos_errno rc = BCM_ERR_OK;
+
+ for (i = 0; rc != BCM_ERR_RANGE; i++)
+ {
+ const bcmbal_apicli_prop_descr *pd;
+ char name_buf[BCMBAL_APICLI_MAX_PARM_NAME_LENGTH] = "";
+ char help_buf[BCMBAL_APICLI_MAX_PARM_HELP_LENGTH] = "";
+
+ strncpy(name_buf, prefix, BCMBAL_APICLI_MAX_PARM_NAME_LENGTH-1);
+ name_buf[BCMBAL_APICLI_MAX_PARM_NAME_LENGTH-1] = 0;
+
+ rc = bcmbal_apicli_object_property(o, group, subgroup, i, &pd);
+ if (rc == BCM_ERR_OK && (pd->access & access_level))
+ {
+ rc = bcmbal_apicli_populate_parms_from_property(pd, NULL, NULL, 0, 0, &parms[nf],
+ access_level, cmd_flags, name_buf, help_buf);
+ if (rc > 0)
+ nf += rc;
+ }
+ }
+ return nf;
+}
+
+
+/* compact selector table. squeeze out values that don't have parameter table attached */
+static void bcmbal_apicli_compact_selector(bcmcli_enum_val *selector, int size)
+{
+ int i, j;
+
+ for (i = 0; i < size; i++)
+ {
+ if (!selector[i].parms)
+ {
+ for ( j = i + 1; j < size && !selector[j].parms; j ++)
+ ;
+ if (j < size)
+ {
+ memcpy(&selector[i], &selector[j], sizeof(bcmcli_enum_val));
+ memset(&selector[j], 0, sizeof(bcmcli_enum_val));
+ }
+ else
+ {
+ memset(&selector[i], 0, sizeof(bcmcli_enum_val));
+ }
+ }
+ }
+}
+
+/* Free CLI parameters. both name and description are allocated dynamically */
+static void bcmbal_apicli_free_parms(bcmcli_cmd_parm *parms)
+{
+ bcmcli_cmd_parm *p = parms;
+
+ while (p->name)
+ {
+ if ((p->flags & BCMCLI_PARM_FLAG_SELECTOR))
+ {
+ /* Remove selector table */
+ bcmcli_enum_val *sel = p->enum_table;
+ if (sel)
+ {
+ bcmcli_enum_val *e = sel;
+ while(e->name)
+ {
+ if (e->parms)
+ {
+ bcmbal_apicli_free_parms(e->parms);
+ }
+ ++e;
+ }
+ bcmos_free(sel);
+ }
+ }
+ if (p->description)
+ bcmos_free(BCMBAL_APICLI_CAST_DISCARD_CONST(p->description, void *));
+ if (p->name)
+ bcmos_free(BCMBAL_APICLI_CAST_DISCARD_CONST(p->name, void *));
+ if (p->max_array_size && p->values)
+ bcmos_free(p->values);
+ if (p->value.buffer.start)
+ bcmbal_buf_free(&p->value.buffer);
+
+ ++p;
+ }
+ bcmos_free(parms);
+}
+
+static uint8_t bcmbal_apicli_get_num_cmd_parms(bcmbal_mgt_group group, bcmbal_apicli_flags flags)
+{
+ if (group == BCMBAL_MGT_GROUP_STAT)
+ return 2; /* object + stat ID */
+ else
+ return 1; /* object */
+}
+
+/* Read generated info and add CLI command */
+static bcmos_errno bcmbal_apicli_add(bcmcli_entry *dir, const char *cmd_name, const char *cmd_descr,
+ bcmbal_mgt_group group, bcmbal_apicli_prop_access_id access_level, bcmcli_cmd_cb cmd_handler,
+ bcmbal_apicli_flags flags)
+{
+ bcmcli_cmd_extra_parm cmd_extras = { .free_parms = bcmbal_apicli_free_parms };
+ bcmcli_cmd_parm *cmd_parms;
+ bcmcli_enum_val *obj_selector;
+ bcmbal_obj_id o;
+ bcmos_errno rc = BCM_ERR_OK;
+ uint32_t cmd_flags = 0;
+ uint8_t num_cmd_parms = bcmbal_apicli_get_num_cmd_parms(group, flags);
+ int n_obj;
+ int i;
+
+ /* Command flags: parameters in the following groups are optional */
+ if (group == BCMBAL_MGT_GROUP_CFG || group == BCMBAL_MGT_GROUP_STAT || group == BCMBAL_MGT_GROUP_AUTO_CFG)
+ cmd_flags = BCMCLI_PARM_FLAG_OPTIONAL;
+
+ /* command parameters are:
+ * - object_name (selector)
+ * - object_key_fields
+ * - object_per_group_fields filtered by access
+ * Therefore, there is 1 top-level enum parameter (object type) with per-value parameter tables
+ * In the case of operations or proxy messages, there is also a top-level enum parameter for the oper/proxy name
+ */
+
+ /* Allocate enum table based on max number of objects. Will be compacted in the end */
+ cmd_parms = bcmos_calloc(sizeof(bcmcli_cmd_parm) * (num_cmd_parms + 1));
+ if (!cmd_parms)
+ return BCM_ERR_NOMEM;
+
+ /* Allocate enough space for all object entries as well as a terminator entry (which is left NULL) */
+ obj_selector = bcmos_calloc(sizeof(bcmcli_enum_val) * (BCMBAL_OBJ_ID__NUM_OF + 1));
+ if (!obj_selector)
+ goto nomem_cleanup;
+
+ /* Allocate parameter table */
+ n_obj = 0;
+ for (o = 0; o < BCMBAL_OBJ_ID__NUM_OF; o++)
+ {
+ uint32_t nkeyfields = 0;
+ uint32_t nfields = 0;
+ uint32_t nfilterfields = 0;
+ uint32_t size;
+ uint16_t s;
+ uint16_t subgroup_count = bcmbal_apicli_get_subgroup_count(o, group);
+ bcmcli_enum_val *sub_selector;
+
+ if (subgroup_count == 0)
+ continue;
+
+ obj_selector[n_obj].val = o;
+ rc = bcmbal_apicli_object_name(o, &obj_selector[n_obj].name, NULL);
+ if (rc)
+ continue;
+
+ /* Get number of key fields and save it */
+ if (group == BCMBAL_MGT_GROUP_AUTO_CFG)
+ {
+ nkeyfields = 0;
+ }
+ else
+ {
+ bcmbal_apicli_get_num_fields_in_group(
+ o, BCMBAL_MGT_GROUP_KEY, 0, BCMBAL_APICLI_PROP_ACCESS_ID_W, &nkeyfields);
+ }
+
+ /* Allocate subgroup enum table */
+ sub_selector = bcmos_calloc(sizeof(bcmcli_enum_val) * (subgroup_count + 1));
+ if (!sub_selector)
+ goto nomem_cleanup;
+
+ /* Allocate single subgroup command parameter */
+ size = sizeof(bcmcli_cmd_parm) * 2;
+ obj_selector[n_obj].parms = bcmos_calloc(size);
+ if (!obj_selector[n_obj].parms)
+ {
+ bcmos_free(sub_selector);
+ goto nomem_cleanup;
+ }
+
+ /* Setup single subgroup command parameter */
+ obj_selector[n_obj].parms[0].type = BCMCLI_PARM_ENUM;
+ obj_selector[n_obj].parms[0].flags = BCMCLI_PARM_FLAG_SELECTOR;
+ obj_selector[n_obj].parms[0].enum_table = sub_selector;
+ rc = bcmbal_apicli_copy_parm_name(&obj_selector[n_obj].parms[0],
+ "sub",
+ "Subgroup (specific operation / proxy msg)");
+ if (rc)
+ goto nomem_cleanup;
+
+ for (s = 0; s < subgroup_count; ++s)
+ {
+ const char *sub_name;
+ bcmcli_cmd_parm *parm_ptr;
+
+ /* Get name of specific subgroup */
+ rc = bcmbal_apicli_object_subgroup_name(o, group, s, &sub_name, NULL);
+ if (rc)
+ continue;
+
+ /* Setup entry in subgroup enum table */
+ sub_selector[s].name = sub_name;
+ sub_selector[s].val = s;
+
+ /* Get number of group fields */
+ rc = bcmbal_apicli_get_num_fields_in_group(o, group, s, access_level, &nfields);
+ if (rc)
+ continue;
+
+ if ((flags & BCMBAL_APICLI_FLAGS_IGNORE_FIELDS) != BCMBAL_APICLI_FLAGS_NONE)
+ {
+ nfilterfields = 0;
+ nfields = 0;
+ }
+
+ /* Allocate parameter table and populate it */
+ sub_selector[s].parms = bcmbal_apicli_parm_alloc(nfields + nkeyfields + nfilterfields);
+ if (!sub_selector[s].parms)
+ {
+ rc = BCM_ERR_NOMEM;
+ goto nomem_cleanup;
+ }
+ for (i = 0; i < nkeyfields + nfields + nfilterfields; i++)
+ {
+ bcmbal_apicli_parm_data *parm_data = sub_selector[s].parms[i].user_data;
+ parm_data->group = (i < nkeyfields) ? BCMBAL_MGT_GROUP_KEY : group;
+ }
+
+ parm_ptr = sub_selector[s].parms;
+ if (nkeyfields)
+ {
+ rc = bcmbal_apicli_populate_parms(
+ o, BCMBAL_MGT_GROUP_KEY, 0, BCMBAL_APICLI_PROP_ACCESS_ID_W, parm_ptr, 0, "");
+ if (rc < 0)
+ goto nomem_cleanup;
+ parm_ptr += rc;
+ }
+ if (nfilterfields)
+ {
+ rc = bcmbal_apicli_populate_parms(
+ o, group, s, BCMBAL_APICLI_PROP_ACCESS_ID_RW, parm_ptr, cmd_flags, "filter.");
+ if (rc < 0)
+ goto nomem_cleanup;
+ parm_ptr += rc;
+ }
+ if (nfields)
+ {
+ rc = bcmbal_apicli_populate_parms(o, group, s, access_level, parm_ptr, cmd_flags, "");
+ if (rc < 0)
+ goto nomem_cleanup;
+ parm_ptr += rc;
+ }
+ }
+
+ /* Compact sub_selector enum. Removes holes (values without parameter table) */
+ bcmbal_apicli_compact_selector(sub_selector, subgroup_count);
+
+ /* If the group type doesn't support subgroups, remove the subgroup param entry */
+ if (group == BCMBAL_MGT_GROUP_CFG || group == BCMBAL_MGT_GROUP_STAT || group == BCMBAL_MGT_GROUP_AUTO_CFG)
+ {
+ /* Free the memory associated with the (single) subgroup param */
+ bcmos_free(BCMBAL_APICLI_CAST_DISCARD_CONST(obj_selector[n_obj].parms[0].name, void *));
+ bcmos_free(BCMBAL_APICLI_CAST_DISCARD_CONST(obj_selector[n_obj].parms[0].description, void *));
+ bcmos_free(obj_selector[n_obj].parms);
+ /* Assign the subgroup params to the root object params */
+ obj_selector[n_obj].parms = sub_selector[0].parms;
+ bcmos_free(sub_selector);
+ }
+
+ ++n_obj; /* number of configured objects */
+ }
+
+ /* Compact obj_selector enum. Removes holes (values without parameter table) */
+ bcmbal_apicli_compact_selector(obj_selector, BCMBAL_OBJ_ID__NUM_OF);
+
+ /* Add a 'clear on read' to stats group */
+ if (group == BCMBAL_MGT_GROUP_STAT)
+ {
+ cmd_parms[0].type = BCMCLI_PARM_ENUM;
+ cmd_parms[0].enum_table = bool_enum;
+ rc = bcmbal_apicli_copy_parm_name(&cmd_parms[0], "clear", "clear on read");
+ if (rc)
+ goto nomem_cleanup;
+ }
+
+ /* We are ready to add this command */
+ cmd_parms[num_cmd_parms - 1].type = BCMCLI_PARM_ENUM;
+ cmd_parms[num_cmd_parms - 1].flags = BCMCLI_PARM_FLAG_SELECTOR;
+ cmd_parms[num_cmd_parms - 1].enum_table = obj_selector;
+ rc = bcmbal_apicli_copy_parm_name(&cmd_parms[num_cmd_parms - 1], "object", "Object Type");
+ if (rc)
+ goto nomem_cleanup;
+ rc = bcmcli_cmd_add(dir, cmd_name, cmd_handler, cmd_descr,
+ (access_level == BCMBAL_APICLI_PROP_ACCESS_ID_W) ? BCMCLI_ACCESS_ADMIN : BCMCLI_ACCESS_GUEST,
+ &cmd_extras, cmd_parms);
+ if (rc)
+ goto nomem_cleanup;
+ return 0;
+
+nomem_cleanup:
+ if (obj_selector)
+ {
+ for (o = 0; o < BCMBAL_OBJ_ID__NUM_OF; o++)
+ {
+ if (obj_selector[o].parms)
+ bcmbal_apicli_free_parms(obj_selector[o].parms);
+ }
+ bcmos_free(obj_selector);
+ }
+ bcmos_free(cmd_parms);
+ return rc;
+}
+
+static bcmcli_session *bcmbal_apicli_log;
+static FILE *bcmbal_apicli_log_file;
+
+static int bcmbal_apicli_log_write_cb(bcmcli_session *session, const char *buf, uint32_t size)
+{
+ if (bcmbal_apicli_log_file == NULL || buf == NULL)
+ return BCM_ERR_INTERNAL;
+ fwrite(buf, 1, size, bcmbal_apicli_log_file);
+ fflush(bcmbal_apicli_log_file);
+ return BCM_ERR_OK;
+}
+
+/* Enable/disable API logging
+ * BCMCLI_MAKE_PARM("file", "Log file. Use \"-\" to disable logging", BCMCLI_PARM_STRING, 0));
+ */
+static bcmos_errno bcmbal_apicli_log_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
+{
+ const char *fname = parm[0].value.string;
+ bcmcli_session_parm session_params =
+ {
+ .write = bcmbal_apicli_log_write_cb,
+ .name = "api_log"
+ };
+ bcmos_errno rc;
+ time_t start_time;
+
+ /* Close existing log session if any */
+ if (bcmbal_apicli_log)
+ {
+ bcmcli_log_set(BCMCLI_LOG_NONE, NULL);
+ bcmcli_session_close(bcmbal_apicli_log);
+ fclose(bcmbal_apicli_log_file);
+ bcmbal_apicli_log = NULL;
+ bcmbal_apicli_log_file = NULL;
+ }
+
+ if (!strcmp(fname, "-"))
+ return BCM_ERR_OK;
+
+ /* Starting a new log session */
+ bcmbal_apicli_log_file = fopen(fname, "a");
+ if (bcmbal_apicli_log_file == NULL)
+ {
+ bcmcli_print(session, "Can't open file %s for logging\n", fname);
+ return BCM_ERR_PARM;
+ }
+ rc = bcmcli_session_open_user(&session_params, &bcmbal_apicli_log);
+ if (rc)
+ {
+ fclose(bcmbal_apicli_log_file);
+ bcmbal_apicli_log_file = NULL;
+ bcmcli_print(session, "Can't open log session. Error %s\n", bcmos_strerror(rc));
+ return rc;
+ }
+ time(&start_time);
+ bcmcli_log_set(BCMCLI_LOG_C_COMMENT, bcmbal_apicli_log);
+ bcmcli_log("/* API logging session started. %s */\n", ctime(&start_time));
+ return BCM_ERR_OK;
+}
+
+static void bcmbal_apicli_find_del_cmd(bcmcli_entry *dir, const char *cmd_name)
+{
+ bcmcli_entry *cmd;
+ cmd = bcmcli_cmd_find(dir, cmd_name);
+ if (cmd)
+ {
+ bcmcli_token_destroy(cmd);
+ }
+}
+
+/* Unregisters commands and directories */
+void bcmbal_apicli_del_commands(bcmcli_session *session, bcmcli_entry *api_dir)
+{
+ bcmbal_apicli_find_del_cmd(api_dir, "set");
+ bcmbal_apicli_find_del_cmd(api_dir, "get");
+ bcmbal_apicli_find_del_cmd(api_dir, "clear");
+ bcmbal_apicli_find_del_cmd(api_dir, "stat");
+ bcmbal_apicli_find_del_cmd(api_dir, "objects");
+ bcmbal_apicli_find_del_cmd(api_dir, "log");
+}
+
+/* Registers commands and directories */
+bcmos_errno bcmbal_apicli_add_commands(bcmcli_session *session, bcmcli_entry *api_dir)
+{
+ bcmos_errno rc;
+
+ current_session = session;
+
+ /* Now generate and add commands */
+ rc = bcmbal_apicli_add(api_dir, "set", "Set object configuration", BCMBAL_MGT_GROUP_CFG,
+ BCMBAL_APICLI_PROP_ACCESS_ID_W, bcmbal_apicli_set_handler, BCMBAL_APICLI_FLAGS_NONE);
+ rc = rc ? rc : bcmbal_apicli_add(api_dir, "get", "Get object configuration", BCMBAL_MGT_GROUP_CFG,
+ BCMBAL_APICLI_PROP_ACCESS_ID_R, bcmbal_apicli_get_handler, BCMBAL_APICLI_FLAGS_NONE);
+ rc = rc ? rc : bcmbal_apicli_add(api_dir, "clear", "Clear object configuration", BCMBAL_MGT_GROUP_CFG,
+ BCMBAL_APICLI_PROP_ACCESS_ID_R, bcmbal_apicli_clear_handler, BCMBAL_APICLI_FLAGS_IGNORE_FIELDS);
+ rc = rc ? rc : bcmbal_apicli_add(api_dir, "stat", "Get statistics", BCMBAL_MGT_GROUP_STAT,
+ BCMBAL_APICLI_PROP_ACCESS_ID_R, bcmbal_apicli_stat_handler, BCMBAL_APICLI_FLAGS_NONE);
+
+ /* List all system objects */
+ rc = rc ? rc : bcmcli_cmd_add(api_dir, "objects", bcmbal_apicli_objects_handler,
+ "Object Types", BCMCLI_ACCESS_GUEST, NULL, NULL);
+
+ BCMCLI_MAKE_CMD(api_dir, "log", "Log API calls", bcmbal_apicli_log_handler,
+ BCMCLI_MAKE_PARM("file", "Log file. Use \"-\" to disable logging", BCMCLI_PARM_STRING, 0));
+
+ return rc;
+}