blob: 2e2666f9f03f04ed736486821bd14be51692bae9 [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 <bcmolt_api.h>
#include <bcmolt_model_types.h>
#include <bcmos_types.h>
#include "bcm_api_cli.h"
#include "bcm_api_cli_helpers.h"
#include "bcm_api_cli_handlers.h"
#ifdef BCM_SUBSYSTEM_HOST
#include <bcmolt_dev_selector.h>
#endif
#define 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 bcmcli_type_descr bool_type_descr = {
.name = "bool",
.descr = "Boolean",
.base_type = BCMOLT_BASE_TYPE_ID_ENUM,
.size = sizeof(bcmos_bool),
.x = {.e = bool_enum}
};
/* parameter data */
typedef struct
{
const bcmcli_prop_descr *prop; /* property */
const bcmcli_field_descr *field; /* field or NULL */
const bcmcli_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 */
bcmolt_mgt_group group; /* management group */
} apicli_parm_data;
typedef enum
{
API_CLI_FLAGS_NONE = 0,
API_CLI_FLAGS_IGNORE_FIELDS = 1 << 0,
API_CLI_FLAGS_MULTI = 1 << 1
} api_cli_flags;
/* Operation: set, get, modify */
typedef enum
{
API_CLI_OPER_SET,
API_CLI_OPER_GET,
API_CLI_OPER_MODIFY,
} api_cli_oper;
/* Current session */
static bcmcli_session *current_session;
/* Current system mode */
static bcmolt_system_mode current_system_mode;
/* Parent directory */
static bcmcli_entry *api_parent_dir;
/*
* helpers
*/
/* calculate number of fields in type */
static uint32_t _api_cli_get_num_fields_in_type(const bcmcli_type_descr *td)
{
uint16_t f;
uint32_t nf = 0;
switch (td->base_type)
{
case BCMOLT_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 += _api_cli_get_num_fields_in_type(td->x.s.fields[f].type);
}
break;
}
case BCMOLT_BASE_TYPE_ID_UNION:
{
/* Union. Count only common fields */
nf = td->x.u.num_common_fields;
break;
}
case BCMOLT_BASE_TYPE_ID_ARR_FIXED:
{
nf = _api_cli_get_num_fields_in_type(td->x.arr_fixed.elem_type);
break;
}
case BCMOLT_BASE_TYPE_ID_ARR_DYN:
{
nf = _api_cli_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 _api_cli_get_num_fields_in_group(bcmolt_obj_id o, bcmolt_mgt_group group, uint16_t subgroup,
bcmolt_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 bcmcli_prop_descr *pd;
rc = api_cli_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 & BCMOLT_PROP_ACCESS_ID_W) != 0)
{
BUG_ON(!pd->type);
nf += _api_cli_get_num_fields_in_type(pd->type);
}
else
{
++nf;
}
}
}
*nfields = nf;
return BCM_ERR_OK;
}
/*
* Command handlers
*/
static bcmos_errno _apicli_objects_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
int rc;
bcmolt_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 < BCMOLT_OBJ_ID__NUM_OF; o++)
{
if (current_system_mode < BCMOLT_SYSTEM_MODE__NUM_OF && !bcmolt_object_is_supported(current_system_mode, o))
continue;
rc = api_cli_object_name(o, &name, &descr);
if (!rc)
bcmcli_print(session, "%.4d %-22s %s\n", o, name, descr);
}
return 0;
}
static bcmos_errno _apicli_set_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_CFG, BCMOLT_MSG_TYPE_SET, session);
}
static bcmos_errno _apicli_get_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_CFG, BCMOLT_MSG_TYPE_GET, session);
}
static bcmos_errno _apicli_clear_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_CFG, BCMOLT_MSG_TYPE_CLEAR, session);
}
static bcmos_errno _apicli_stat_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_STAT, BCMOLT_MSG_TYPE_GET, session);
}
static bcmos_errno _apicli_oper_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_OPER, BCMOLT_MSG_TYPE_SET, session);
}
static bcmos_errno _apicli_send_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_PROXY, BCMOLT_MSG_TYPE_SET, session);
}
static bcmos_errno _apicli_stat_cfg_set_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_STAT_CFG, BCMOLT_MSG_TYPE_SET, session);
}
static bcmos_errno _apicli_stat_cfg_get_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_STAT_CFG, BCMOLT_MSG_TYPE_GET, session);
}
static bcmos_errno _apicli_auto_cfg_set_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_AUTO_CFG, BCMOLT_MSG_TYPE_SET, session);
}
static bcmos_errno _apicli_auto_cfg_get_handler(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_AUTO_CFG, BCMOLT_MSG_TYPE_GET, session);
}
static bcmos_errno _apicli_auto_cfg_get_multi_handler(
bcmcli_session *session,
const bcmcli_cmd_parm parm[],
uint16_t nparms)
{
return bcmolt_cli_api_call(current_device, BCMOLT_MGT_GROUP_CFG, BCMOLT_MSG_TYPE_GET_MULTI, session);
}
/*
* Init-time helpers
*/
/* map to CLI type */
static bcmos_errno _api_cli_map_type(const bcmcli_type_descr *td, const bcmcli_type_descr *array_td, bcmcli_cmd_parm *cmd_parm)
{
apicli_parm_data *parm_data = cmd_parm->user_data;
bcmos_errno rc = BCM_ERR_OK;
/* Map type */
switch(td->base_type)
{
case BCMOLT_BASE_TYPE_ID_SNUM:
cmd_parm->type = BCMCLI_PARM_NUMBER;
break;
case BCMOLT_BASE_TYPE_ID_UNUM:
cmd_parm->type = BCMCLI_PARM_UNUMBER;
break;
case BCMOLT_BASE_TYPE_ID_UNUM_HEX:
cmd_parm->type = BCMCLI_PARM_HEX;
break;
case BCMOLT_BASE_TYPE_ID_BOOL:
cmd_parm->type = BCMCLI_PARM_ENUM;
cmd_parm->enum_table = bool_enum;
break;
case BCMOLT_BASE_TYPE_ID_FLOAT:
cmd_parm->type = td->size == sizeof(double) ? BCMCLI_PARM_DOUBLE : BCMCLI_PARM_FLOAT;
break;
case BCMOLT_BASE_TYPE_ID_STRING:
cmd_parm->type = BCMCLI_PARM_STRING;
break;
case BCMOLT_BASE_TYPE_ID_IPV4:
cmd_parm->type = BCMCLI_PARM_IP;
break;
case BCMOLT_BASE_TYPE_ID_MAC:
cmd_parm->type = BCMCLI_PARM_MAC;
break;
case BCMOLT_BASE_TYPE_ID_ENUM:
cmd_parm->type = BCMCLI_PARM_ENUM;
cmd_parm->enum_table = td->x.e;
break;
case BCMOLT_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 == BCMOLT_BASE_TYPE_ID_UNUM || td->base_type == BCMOLT_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 _api_cli_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(APICLI_CAST_DISCARD_CONST(parm->name, void *), name);
strcpy(APICLI_CAST_DISCARD_CONST(parm->description, void *), descr);
return BCM_ERR_OK;
}
/* populate single parameter */
static int _api_cli_populate_parm1(const bcmcli_prop_descr *pd, const bcmcli_field_descr *fd, const bcmcli_type_descr *td,
const bcmcli_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)
{
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 = _api_cli_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 = _api_cli_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 == BCMOLT_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 = bcmolt_buf_alloc(&cmd_parm->value.buffer, array_size, BCMOLT_BUF_ENDIAN_FIXED);
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 _api_cli_populate_name_help(const bcmcli_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, APICLI_MAX_PARM_NAME_LENGTH);
if (strlen(name_buf))
bcmcli_strncat(name_buf, ".", APICLI_MAX_PARM_NAME_LENGTH);
bcmcli_strncat(name_buf, fld->cli_name ? fld->cli_name : fld->name, APICLI_MAX_PARM_NAME_LENGTH);
bcmcli_strncpy(help_buf, help_buf0, APICLI_MAX_PARM_HELP_LENGTH);
bcmcli_strncat(help_buf, " - ", APICLI_MAX_PARM_HELP_LENGTH);
bcmcli_strncat(help_buf, fld->descr ? fld->descr : fld->name, APICLI_MAX_PARM_HELP_LENGTH);
}
/* Allocate CLI parameter array. Set up parm->data */
static bcmcli_cmd_parm *_api_cli_parm_alloc(int nparms)
{
uint32_t size;
bcmcli_cmd_parm *parms;
apicli_parm_data *parm_data;
int i;
/* Allocate parameter table and populate it */
size = (sizeof(bcmcli_cmd_parm) + sizeof(apicli_parm_data)) * (nparms + 1);
parms = bcmos_calloc(size);
if (!parms)
return NULL;
/* Associate parameter_data structs with parameters */
parm_data = (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 *_api_cli_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 _api_cli_populate_parms_from_property(const bcmcli_prop_descr *pd, const bcmcli_field_descr *fd,
const bcmcli_field_descr *array_fd, uint32_t offset, uint32_t array_fd_offset, bcmcli_cmd_parm *parms,
bcmolt_prop_access_id access_level, uint32_t cmd_flags, char *name_buf0, char *help_buf0)
{
const bcmcli_type_descr *td = fd ? fd->type : pd->type;
uint32_t nf = 0;
char name_buf[APICLI_MAX_PARM_NAME_LENGTH];
char help_buf[APICLI_MAX_PARM_HELP_LENGTH];
int rc = 0;
/* 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 = 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, 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 == BCMOLT_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 == BCMOLT_BASE_TYPE_ID_ARR_DYN || td->base_type == BCMOLT_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 bcmcli_field_descr *)pd;
if (td->base_type == BCMOLT_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 == BCMOLT_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 bcmcli_field_descr *fld = &td->x.s.fields[f];
_api_cli_populate_name_help(fld, name_buf0, help_buf0, name_buf, help_buf);
rc = _api_cli_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 == BCMOLT_BASE_TYPE_ID_UNION)
{
/* Union */
uint16_t f;
const bcmcli_field_descr *fld;
bcmcli_cmd_parm *sel_parm;
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];
_api_cli_populate_name_help(fld, name_buf0, help_buf0, name_buf, help_buf);
rc = _api_cli_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];
_api_cli_populate_name_help(fld, name_buf0, help_buf0, name_buf, help_buf);
rc = _api_cli_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 = _api_cli_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 = _api_cli_get_num_fields_in_type(fld->type);
int i;
e->parms = _api_cli_parm_alloc(np);
if (!e->parms)
{
rc = BCM_ERR_NOMEM;
break;
}
for (i = 0; i < np; i++)
{
apicli_parm_data *data = e->parms[i].user_data;
data->group = sel_data->group;
}
/* Collapse substructure name */
if (fld->type->base_type == BCMOLT_BASE_TYPE_ID_STRUCT ||
fld->type->base_type == BCMOLT_BASE_TYPE_ID_UNION)
{
bcmcli_strncpy(name_buf, name_buf0, sizeof(name_buf));
bcmcli_strncpy(help_buf, help_buf0, sizeof(help_buf));
}
else
{
_api_cli_populate_name_help(fld, name_buf0, help_buf0, name_buf, help_buf);
}
rc = _api_cli_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];
_api_cli_populate_name_help(fld, name_buf0, help_buf0, name_buf, help_buf);
rc = _api_cli_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 = _api_cli_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 _api_cli_populate_parms(
bcmolt_obj_id o,
bcmolt_mgt_group group,
uint16_t subgroup,
bcmolt_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 bcmcli_prop_descr *pd;
char name_buf[APICLI_MAX_PARM_NAME_LENGTH] = "";
char help_buf[APICLI_MAX_PARM_HELP_LENGTH] = "";
strncpy(name_buf, prefix, APICLI_MAX_PARM_NAME_LENGTH-1);
name_buf[APICLI_MAX_PARM_NAME_LENGTH-1] = 0;
rc = api_cli_object_property(o, group, subgroup, i, &pd);
if (rc == BCM_ERR_OK && (pd->access & access_level))
{
rc = _api_cli_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 _api_cli_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 _api_cli_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)
{
_api_cli_free_parms(e->parms);
}
++e;
}
bcmos_free(sel);
}
}
if (p->description)
bcmos_free(APICLI_CAST_DISCARD_CONST(p->description, void *));
if (p->name)
bcmos_free(APICLI_CAST_DISCARD_CONST(p->name, void *));
if (p->max_array_size && p->values)
bcmos_free(p->values);
if (p->value.buffer.start)
bcmolt_buf_free(&p->value.buffer);
++p;
}
bcmos_free(parms);
}
static uint8_t _apicli_get_num_cmd_parms(bcmolt_mgt_group group, api_cli_flags flags)
{
if (group == BCMOLT_MGT_GROUP_STAT)
return 2; /* object + stat ID */
else if (group == BCMOLT_MGT_GROUP_CFG && (flags & API_CLI_FLAGS_MULTI) != API_CLI_FLAGS_NONE)
return 3; /* object + max msgs + invert flag */
else
return 1; /* object */
}
/* Read generated info and add CLI command */
static bcmos_errno _api_cli_add(bcmcli_entry *dir, const char *cmd_name, const char *cmd_descr,
bcmolt_mgt_group group, bcmolt_prop_access_id access_level, bcmcli_cmd_cb cmd_handler, api_cli_flags flags)
{
bcmcli_cmd_extra_parm cmd_extras = { .free_parms = _api_cli_free_parms };
bcmcli_cmd_parm *cmd_parms;
bcmcli_enum_val *obj_selector;
bcmolt_obj_id o;
bcmos_errno rc = BCM_ERR_OK;
uint32_t cmd_flags = 0;
uint8_t num_cmd_parms = _apicli_get_num_cmd_parms(group, flags);
int n_obj;
int i;
/* Command flags: parameters in the following groups are optional */
if (group == BCMOLT_MGT_GROUP_CFG || group == BCMOLT_MGT_GROUP_STAT || group == BCMOLT_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) * (BCMOLT_OBJ_ID__NUM_OF + 1));
if (!obj_selector)
goto nomem_cleanup;
/* Allocate parameter table */
n_obj = 0;
for (o = 0; o < BCMOLT_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 = api_cli_get_subgroup_count(o, group);
bcmcli_enum_val *sub_selector;
if (!bcmolt_object_is_supported(current_system_mode, o) && o != BCMOLT_OBJ_ID_DEVICE)
continue;
if (subgroup_count == 0)
continue;
obj_selector[n_obj].val = o;
rc = api_cli_object_name(o, &obj_selector[n_obj].name, NULL);
if (rc)
continue;
/* Get number of key fields and save it */
if (group == BCMOLT_MGT_GROUP_AUTO_CFG)
{
nkeyfields = 0;
}
else
{
_api_cli_get_num_fields_in_group(o, BCMOLT_MGT_GROUP_KEY, 0, BCMOLT_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 = _api_cli_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 = api_cli_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 = _api_cli_get_num_fields_in_group(o, group, s, access_level, &nfields);
if (rc)
continue;
/* For multi-object GET messages, populate the filter fields just like a SET (except that all read-only
fields are also settable) */
if ((flags & API_CLI_FLAGS_MULTI) != API_CLI_FLAGS_NONE)
{
rc = _api_cli_get_num_fields_in_group(o, group, s, BCMOLT_PROP_ACCESS_ID_RW, &nfilterfields);
if (rc)
continue;
}
if ((flags & API_CLI_FLAGS_IGNORE_FIELDS) != API_CLI_FLAGS_NONE)
{
nfilterfields = 0;
nfields = 0;
}
/* Allocate parameter table and populate it */
sub_selector[s].parms = _api_cli_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++)
{
apicli_parm_data *parm_data = sub_selector[s].parms[i].user_data;
parm_data->group = (i < nkeyfields) ? BCMOLT_MGT_GROUP_KEY : group;
}
parm_ptr = sub_selector[s].parms;
if (nkeyfields)
{
rc = _api_cli_populate_parms(o, BCMOLT_MGT_GROUP_KEY, 0, BCMOLT_PROP_ACCESS_ID_W, parm_ptr, 0, "");
if (rc < 0)
goto nomem_cleanup;
parm_ptr += rc;
}
if (nfilterfields)
{
rc = _api_cli_populate_parms(o, group, s, BCMOLT_PROP_ACCESS_ID_RW, parm_ptr, cmd_flags, "filter.");
if (rc < 0)
goto nomem_cleanup;
parm_ptr += rc;
}
if (nfields)
{
rc = _api_cli_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) */
_api_cli_compact_selector(sub_selector, subgroup_count);
/* If the group type doesn't support subgroups, remove the subgroup param entry */
if (group == BCMOLT_MGT_GROUP_CFG || group == BCMOLT_MGT_GROUP_STAT || group == BCMOLT_MGT_GROUP_AUTO_CFG)
{
/* Free the memory associated with the (single) subgroup param */
bcmos_free(APICLI_CAST_DISCARD_CONST(obj_selector[n_obj].parms[0].name, void *));
bcmos_free(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) */
_api_cli_compact_selector(obj_selector, BCMOLT_OBJ_ID__NUM_OF);
/* Add a 'clear on read' to stats group */
if (group == BCMOLT_MGT_GROUP_STAT)
{
cmd_parms[0].type = BCMCLI_PARM_ENUM;
cmd_parms[0].enum_table = bool_enum;
rc = _api_cli_copy_parm_name(&cmd_parms[0], "clear", "clear on read");
if (rc)
goto nomem_cleanup;
}
/* Add 'max number of messages to read' and 'invert filter' to cfg get_multi msgs */
if (group == BCMOLT_MGT_GROUP_CFG && (flags & API_CLI_FLAGS_MULTI) != API_CLI_FLAGS_NONE)
{
cmd_parms[0].type = BCMCLI_PARM_UNUMBER;
rc = _api_cli_copy_parm_name(&cmd_parms[0], "max_msgs", "max number of API GET messages to receive per call");
if (rc)
goto nomem_cleanup;
cmd_parms[1].type = BCMCLI_PARM_ENUM;
cmd_parms[1].enum_table = bool_enum;
rc = _api_cli_copy_parm_name(&cmd_parms[1], "filter_invert", "invert filter (select objects that don't match)");
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 = _api_cli_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 == BCMOLT_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 < BCMOLT_OBJ_ID__NUM_OF; o++)
{
if (obj_selector[o].parms)
_api_cli_free_parms(obj_selector[o].parms);
}
bcmos_free(obj_selector);
}
bcmos_free(cmd_parms);
return rc;
}
#ifdef BCM_SUBSYSTEM_HOST
/* Current device change indication */
static void _api_cli_device_change_ind(bcmcli_session *session, bcmolt_devid dev)
{
api_cli_set_commands(session);
}
#ifdef LINUX_USER_SPACE
static bcmcli_session *_apicli_log;
static FILE *_apicli_log_file;
static int _apicli_log_write_cb(bcmcli_session *session, const char *buf, uint32_t size)
{
if (_apicli_log_file == NULL || buf == NULL)
return BCM_ERR_INTERNAL;
fwrite(buf, 1, size, _apicli_log_file);
fflush(_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 _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 = _apicli_log_write_cb,
.name = "api_log"
};
bcmos_errno rc;
time_t start_time;
/* Close existing log session if any */
if (_apicli_log)
{
bcmcli_log_set(BCMCLI_LOG_NONE, NULL);
bcmcli_session_close(_apicli_log);
fclose(_apicli_log_file);
_apicli_log = NULL;
_apicli_log_file = NULL;
}
if (!strcmp(fname, "-"))
return BCM_ERR_OK;
/* Starting a new log session */
_apicli_log_file = fopen(fname, "a");
if (_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, &_apicli_log);
if (rc)
{
fclose(_apicli_log_file);
_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, _apicli_log);
bcmcli_log("/* API logging session started. %s */\n", ctime(&start_time));
return BCM_ERR_OK;
}
#endif /* #ifdef LINUX_USER_SPACE */
#endif /* #ifdef BCM_SUBSYSTEM_HOST */
static void api_cli_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 */
static void api_cli_del_commands(bcmcli_session *session)
{
bcmcli_entry *dir;
dir = bcmcli_dir_find(api_parent_dir, "api");
if (!dir)
{
return;
}
api_cli_find_del_cmd(dir, "set");
api_cli_find_del_cmd(dir, "get");
api_cli_find_del_cmd(dir, "multiget");
api_cli_find_del_cmd(dir, "clear");
api_cli_find_del_cmd(dir, "modify");
api_cli_find_del_cmd(dir, "stat");
api_cli_find_del_cmd(dir, "oper");
api_cli_find_del_cmd(dir, "send");
api_cli_find_del_cmd(dir, "saset");
api_cli_find_del_cmd(dir, "saget");
api_cli_find_del_cmd(dir, "acset");
api_cli_find_del_cmd(dir, "acget");
api_cli_find_del_cmd(dir, "objects");
#ifdef BCM_SUBSYSTEM_HOST
api_cli_find_del_cmd(dir, "log");
#endif
}
/* Registers commands and directories */
static bcmos_errno api_cli_add_commands(bcmcli_session *session)
{
bcmcli_entry *api_dir;
bcmos_errno rc;
if ((api_dir = bcmcli_dir_find(api_parent_dir, "api")) == NULL)
{
api_dir = bcmcli_dir_add(api_parent_dir, "api", "Maple API", BCMCLI_ACCESS_GUEST, NULL);
if (api_dir == NULL)
{
bcmcli_session_print(session, "Can't create api directory\n");
return BCM_ERR_INTERNAL;
}
}
current_session = session;
bcmolt_system_mode_get(current_device, &current_system_mode);
/* Now generate and add commands */
rc = _api_cli_add(api_dir, "set", "Set object configuration", BCMOLT_MGT_GROUP_CFG,
BCMOLT_PROP_ACCESS_ID_W, _apicli_set_handler, API_CLI_FLAGS_NONE);
rc = rc ? rc : _api_cli_add(api_dir, "get", "Get object configuration", BCMOLT_MGT_GROUP_CFG,
BCMOLT_PROP_ACCESS_ID_R, _apicli_get_handler, API_CLI_FLAGS_NONE);
rc = rc ? rc : _api_cli_add(api_dir, "clear", "Clear object configuration", BCMOLT_MGT_GROUP_CFG,
BCMOLT_PROP_ACCESS_ID_R, _apicli_clear_handler, API_CLI_FLAGS_IGNORE_FIELDS);
rc = rc ? rc : _api_cli_add(api_dir, "stat", "Get statistics", BCMOLT_MGT_GROUP_STAT, BCMOLT_PROP_ACCESS_ID_R,
_apicli_stat_handler, API_CLI_FLAGS_NONE);
rc = rc ? rc : _api_cli_add(api_dir, "oper", "Initiate Operation", BCMOLT_MGT_GROUP_OPER,
BCMOLT_PROP_ACCESS_ID_W, _apicli_oper_handler, API_CLI_FLAGS_NONE);
rc = rc ? rc : _api_cli_add(api_dir, "send", "Send message to ONU", BCMOLT_MGT_GROUP_PROXY,
BCMOLT_PROP_ACCESS_ID_W, _apicli_send_handler, API_CLI_FLAGS_NONE);
rc = rc ? rc : _api_cli_add(api_dir, "saset", "Set statistic alarm configuration",
BCMOLT_MGT_GROUP_STAT_CFG, BCMOLT_PROP_ACCESS_ID_W, _apicli_stat_cfg_set_handler, API_CLI_FLAGS_NONE);
rc = rc ? rc : _api_cli_add(api_dir, "saget", "Get statistic alarm configuration",
BCMOLT_MGT_GROUP_STAT_CFG, BCMOLT_PROP_ACCESS_ID_R, _apicli_stat_cfg_get_handler, API_CLI_FLAGS_IGNORE_FIELDS);
rc = rc ? rc : _api_cli_add(api_dir, "acset", "Set autonomous message configuration",
BCMOLT_MGT_GROUP_AUTO_CFG, BCMOLT_PROP_ACCESS_ID_W, _apicli_auto_cfg_set_handler, API_CLI_FLAGS_NONE);
rc = rc ? rc : _api_cli_add(api_dir, "acget", "Get autonomous message configuration",
BCMOLT_MGT_GROUP_AUTO_CFG, BCMOLT_PROP_ACCESS_ID_R, _apicli_auto_cfg_get_handler, API_CLI_FLAGS_NONE);
rc = rc ? rc : _api_cli_add(api_dir, "multiget", "Get configuration for multiple objects",
BCMOLT_MGT_GROUP_CFG, BCMOLT_PROP_ACCESS_ID_R, _apicli_auto_cfg_get_multi_handler, API_CLI_FLAGS_MULTI);
/* List all system objects */
rc = rc ? rc : bcmcli_cmd_add(api_dir, "objects", _apicli_objects_handler,
"Object Types", BCMCLI_ACCESS_GUEST, NULL, NULL);
#if defined(BCM_SUBSYSTEM_HOST) && defined(LINUX_USER_SPACE)
BCMCLI_MAKE_CMD(api_dir, "log", "Log API calls", _apicli_log_handler,
BCMCLI_MAKE_PARM("file", "Log file. Use \"-\" to disable logging", BCMCLI_PARM_STRING, 0));
#endif
return rc;
}
/* Update API CLI commands for the current device */
bcmos_errno api_cli_set_commands(bcmcli_session *session)
{
bcmos_errno rc;
api_cli_del_commands(session);
rc = api_cli_add_commands(session);
return rc;
}
/* Init API CLI commands for the current device */
bcmos_errno api_cli_init(bcmcli_entry *parent_dir, bcmcli_session *session)
{
bcmos_errno rc;
api_parent_dir = parent_dir;
rc = api_cli_set_commands(session);
#ifdef BCM_SUBSYSTEM_HOST
/* Subscribe for device change indication */
rc = rc ? rc : bcmolt_dev_sel_ind_register(_api_cli_device_change_ind);
#endif
return rc;
}