| /* |
| <: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; |
| } |