blob: 0c5a8f7e8f0070766b763039ff6040c331330e97 [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.
:>
*/
/*
* bcm_embedded_cli.c - embedded CLI access from the host
*/
#include <bcmos_system.h>
#include <bcmolt_embedded_cli.h>
#include <bcmolt_model_types.h>
#include <bcmolt_api.h>
#include <bcmtr_interface.h>
#include <bcmcli.h>
#define EMBEDDED_CLI_BUFFER_SIZE 2048
#define EMBEDDED_CLI_TIMER_DELAY_US 100000
/* Embedded CLI client context */
typedef struct
{
bcmolt_devid device;
bcmos_task task;
bcmos_timer timer;
bcmcli_session *session;
uint8_t inbuf[EMBEDDED_CLI_BUFFER_SIZE];
uint32_t inbuf_length;
} embedded_cli_context;
static embedded_cli_context *cli_context;
static bcmos_timer_rc embedded_cli_timer_handler(bcmos_timer *timer, long data)
{
/* FFU: this timer is for future line editing support */
return BCMOS_TIMER_OK;
}
static void embedded_cli_output_msg_handler(bcmolt_devid olt, bcmolt_msg *msg)
{
bcmolt_debug_cli_input *cli_msg = (bcmolt_debug_cli_input *)msg;
if (cli_context && cli_msg->data.data.val)
{
/* print directly because output can contain ESC sequences */
uint8_t *val = cli_msg->data.data.val;
while (*val)
{
bcmos_putchar(*(val++));
}
}
bcmolt_msg_free(msg);
}
static void embedded_cli_send_input(embedded_cli_context *context)
{
bcmolt_debug_key key = {};
bcmolt_debug_cli_input msg;
bcmolt_u8_list_u32 data = { context->inbuf_length, context->inbuf };
if (!context->inbuf_length)
{
return;
}
BCMOLT_OPER_INIT(&msg, debug, cli_input, key);
BCMOLT_OPER_PROP_SET(&msg, debug, cli_input, data, data);
bcmolt_oper_submit(context->device, &msg.hdr);
context->inbuf_length = 0;
}
/* Initialize embedded CLI module */
bcmos_errno bcm_embedded_cli_enter(bcmcli_session *session, bcmolt_devid device)
{
static char *embedded_cli_task_name = "embedded_cli";
bcmos_task_parm taskp =
{
.name = embedded_cli_task_name,
.priority = TASK_PRIORITY_CLI,
.data = device
};
bcmos_module_parm modulep = {};
bcmos_timer_parm timerp =
{
.owner = BCMOS_MODULE_ID_CLI_OVER_PCIE,
.handler = embedded_cli_timer_handler,
.data = device
};
bcmtr_handler_parm msgp =
{
.object = BCMOLT_OBJ_ID_DEBUG,
.group = BCMOLT_MGT_GROUP_AUTO,
.subgroup = BCMOLT_DEBUG_AUTO_ID_CLI_OUTPUT,
.module = BCMOS_MODULE_ID_CLI_OVER_PCIE,
.app_cb = embedded_cli_output_msg_handler,
.flags = BCMOLT_AUTO_FLAGS_DISPATCH
};
bcmtr_handler_parm old_msgp =
{
.object = BCMOLT_OBJ_ID_DEBUG,
.group = BCMOLT_MGT_GROUP_AUTO,
.subgroup = BCMOLT_DEBUG_AUTO_ID_CLI_OUTPUT,
};
int c;
bcmos_errno rc;
bcmos_bool raw_mode = BCMOS_FALSE;
/* Only 1 instance is supported */
if (cli_context)
{
BCMOS_TRACE_RETURN(BCM_ERR_ALREADY, "embedded_cli client is already active for device %u\n",
cli_context->device);
}
cli_context = bcmos_calloc(sizeof(*cli_context));
if (!cli_context)
{
BCMOS_TRACE_RETURN(BCM_ERR_NOMEM, "can't allocate embedded CLI context\n");
}
cli_context->session = session;
cli_context->device = device;
/* Create input/output task & module */
rc = bcmos_task_create(&cli_context->task, &taskp);
if (rc)
{
BCMOS_TRACE_ERR("Can't create embedded cli task: %s\n", bcmos_strerror(rc));
goto cleanup1;
}
modulep.data = (long)cli_context;
rc = bcmos_module_create(BCMOS_MODULE_ID_CLI_OVER_PCIE, &cli_context->task, &modulep);
if (rc)
{
BCMOS_TRACE_ERR("Can't create embedded cli module: %s\n", bcmos_strerror(rc));
goto cleanup2;
}
timerp.data = (long)cli_context;
rc = bcmos_timer_create(&cli_context->timer, &timerp);
if (rc)
{
BCMOS_TRACE_ERR("Can't create embedded cli timer: %s\n", bcmos_strerror(rc));
goto cleanup3;
}
/* Query existing indication in order to be able to restore it */
bcmtr_msg_handler_register_get(device, &old_msgp);
/* Unregister old handler */
bcmtr_msg_handler_unregister(device, &old_msgp);
/* Register for cli_output indication */
rc = bcmtr_msg_handler_register(device, &msgp);
if (rc)
{
BCMOS_TRACE_ERR("Can't register for cli_output indication: %s\n", bcmos_strerror(rc));
goto cleanup4;
}
bcmcli_session_print(session, "Device %u: Entering embedded CLI. Type %c to terminate\n",
device, BCM_EMBEDDED_CONSOLE_EXIT_CHAR);
/* Try to enter raw mode if line editing is requested */
rc = bcmcli_session_raw_mode_set(session, BCMOS_TRUE);
if (rc)
{
bcmcli_session_print(session, "Can't enable RAW input mode. Line editing is disabled\n");
}
else
{
raw_mode = BCMOS_TRUE;
}
/* No we read input and push it into input buffer */
if (raw_mode)
{
c = bcmos_getchar();
while (c >= 0 && c != BCM_EMBEDDED_CONSOLE_EXIT_CHAR)
{
cli_context->inbuf[cli_context->inbuf_length++] = c;
embedded_cli_send_input(cli_context);
c = bcmos_getchar();
}
bcmcli_session_raw_mode_set(session, BCMOS_FALSE);
}
else
{
/* Line mode */
char *buf;
buf = bcmcli_session_gets(session, (char *)cli_context->inbuf, sizeof(cli_context->inbuf));
while (buf && buf[0] != BCM_EMBEDDED_CONSOLE_EXIT_CHAR)
{
cli_context->inbuf_length = strlen(buf);
if (!cli_context->inbuf_length)
continue;
if (buf[cli_context->inbuf_length-1] != '\n' && buf[cli_context->inbuf_length-1] != '\r' &&
cli_context->inbuf_length < sizeof(cli_context->inbuf) - 1)
{
buf[cli_context->inbuf_length++] = '\n';
}
embedded_cli_send_input(cli_context);
buf = bcmcli_session_gets(session, (char *)cli_context->inbuf, sizeof(cli_context->inbuf));
}
}
bcmos_timer_stop(&cli_context->timer);
bcmcli_session_print(session, "Device %u: Embedded CLI terminated\n", device);
bcmtr_msg_handler_unregister(device, &msgp);
if (old_msgp.app_cb)
rc = bcmtr_msg_handler_register(device, &old_msgp);
cleanup4:
bcmos_timer_destroy(&cli_context->timer);
cleanup3:
bcmos_module_destroy(BCMOS_MODULE_ID_CLI_OVER_PCIE);
cleanup2:
bcmos_task_destroy(&cli_context->task);
cleanup1:
bcmos_free(cli_context);
cli_context = NULL;
return rc;
}
static bcmos_errno bcm_embedded_cli_command(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
bcmolt_devid device = parm[0].value.number;
return bcm_embedded_cli_enter(session, device);
}
/* Initialize embedded CLI module */
bcmos_errno bcm_embedded_cli_init(void)
{
BCMCLI_MAKE_CMD(NULL, "embedded", "Embedded CLI", bcm_embedded_cli_command,
BCMCLI_MAKE_PARM_RANGE("device", "Device index", BCMCLI_PARM_DECIMAL, 0, 0, BCMTR_MAX_OLTS-1));
return BCM_ERR_OK;
}
/* Cleanup embedded CVLI module */
bcmos_errno bcm_embedded_cli_exit(void)
{
return BCM_ERR_OK;
}