blob: cbbe9734f1b2eb23d40d52818f04d6bc43c58cc5 [file] [log] [blame]
/*
<: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;
}