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;
+}