blob: 9ca9d2ef0ad1c87152167dc53cc67db97418e30a [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 <bcmtr_interface.h>
#include <bcmtr_header.h>
#include <bcmolt_api.h>
#include <bcm_api_cli_helpers.h>
#include <bcmtr_debug.h>
#include <bcmcli_session.h>
#include "bcmolt_remote_cli.h"
#ifdef ENABLE_LOG
#include "bcm_dev_log.h"
#endif
#define REMOTE_CLI_BUFFER_MAX 65635 /* Max reassembled packet plus header */
#define REMOTE_CLI_MESSAGE_QUEUE_DEPTH BCMOS_MSG_POOL_DEFAULT_SIZE
typedef enum
{
REMOTE_CLI_OPCODE_CLI_COMMAND,
REMOTE_CLI_OPCODE_INDICATION,
REMOTE_CLI_OPCODE_PROXY_RX
} remote_cli_opcode;
/* Statistics */
typedef struct
{
unsigned long rx_requests;
unsigned long tx_responses;
unsigned long indications;
unsigned long proxy_rxs;
unsigned long unpack_errors;
unsigned long pack_errors;
unsigned long correlation_errors;
unsigned long tx_socket_errors;
unsigned long rx_socket_errors;
unsigned long message_errors;
} remote_cli_stats;
typedef struct
{
bcmos_task output_task;
bcmos_task socket_task;
struct sockaddr_in client;
uint16_t port;
int client_socket;
bcmos_bool is_running;
dev_log_id log_id;
bcmos_bool output_pending;
uint8_t input_buffer[REMOTE_CLI_BUFFER_MAX];
char *input_str;
uint8_t output_buffer[REMOTE_CLI_BUFFER_MAX];
uint16_t output_current_char;
bcmos_mutex output_lock;
int current_corr_tag;
bcmcli_session *session;
remote_cli_stats stats;
} remote_cli_control_block;
static remote_cli_control_block remote_cli_data[BCMTR_MAX_OLTS];
static bcmos_errno remote_cli_stats_cmd(bcmcli_session *session, const bcmcli_cmd_parm parm[], uint16_t nparms)
{
int device = (int)parm[0].value.number;
if (device >= BCMTR_MAX_OLTS)
{
return BCM_ERR_RANGE;
}
bcmcli_print(session, "%-16s: %lu\n", "rx_requests", remote_cli_data[device].stats.rx_requests);
bcmcli_print(session, "%-16s: %lu\n", "tx_responses", remote_cli_data[device].stats.tx_responses);
bcmcli_print(session, "%-16s: %lu\n", "indications", remote_cli_data[device].stats.indications);
bcmcli_print(session, "%-16s: %lu\n", "proxy_rxs", remote_cli_data[device].stats.proxy_rxs);
bcmcli_print(session, "%-16s: %lu\n", "unpack_errors", remote_cli_data[device].stats.unpack_errors);
bcmcli_print(session, "%-16s: %lu\n", "pack_errors", remote_cli_data[device].stats.pack_errors);
bcmcli_print(session, "%-16s: %lu\n", "tx_socket_errors", remote_cli_data[device].stats.tx_socket_errors);
bcmcli_print(session, "%-16s: %lu\n", "rx_socket_errors", remote_cli_data[device].stats.rx_socket_errors);
bcmcli_print(session, "%-16s: %lu\n", "message_errors", remote_cli_data[device].stats.message_errors);
memset(&remote_cli_data[device].stats, 0, sizeof(remote_cli_stats));
return BCM_ERR_OK;
}
static void send_output_packets(bcmolt_devid device, remote_cli_opcode opcode, uint16_t corr_tag)
{
bcmolt_buf buf = {};
static uint8_t scratch_buffer[REMOTE_CLI_BUFFER_MAX];
ssize_t len;
bcmos_bool ok = BCMOS_TRUE;
if (remote_cli_data[device].output_current_char == 0)
{
return;
}
bcmolt_buf_init(&buf, REMOTE_CLI_BUFFER_MAX, scratch_buffer, BCMOLT_BUF_ENDIAN_FIXED);
ok = ok && bcmolt_buf_write_u8(&buf, (uint8_t)opcode);
ok = ok && bcmolt_buf_write_u16_be(&buf, corr_tag);
ok = ok && bcmolt_buf_write(
&buf,
remote_cli_data[device].output_buffer,
remote_cli_data[device].output_current_char);
if (!ok)
{
++remote_cli_data[device].stats.pack_errors;
return;
}
/* Send to client */
len = sendto(
remote_cli_data[device].client_socket,
buf.start,
bcmolt_buf_get_used(&buf),
0,
(struct sockaddr *)&remote_cli_data[device].client,
sizeof(remote_cli_data[device].client));
if (len <= 0)
{
++remote_cli_data[device].stats.tx_socket_errors;
}
}
static void process_cli_input(bcmolt_devid device)
{
bcmos_mutex_lock(&remote_cli_data[device].output_lock);
remote_cli_data[device].output_current_char = 0;
memset(remote_cli_data[device].output_buffer, 0, sizeof(remote_cli_data[device].output_buffer));
bcmcli_parse(remote_cli_data[device].session, remote_cli_data[device].input_str);
bcmos_mutex_unlock(&remote_cli_data[device].output_lock);
}
static int remote_cli_session_write_cb(bcmcli_session *session, const char *buf, uint32_t size)
{
int i;
bcmolt_devid device = *((bcmolt_devid *)bcmcli_session_user_priv(session));
bcmos_mutex_lock(&remote_cli_data[device].output_lock);
for (i = 0; i < size; ++i)
{
remote_cli_data[device].output_buffer[remote_cli_data[device].output_current_char] = buf[i];
++remote_cli_data[device].output_current_char;
}
bcmos_mutex_unlock(&remote_cli_data[device].output_lock);
return size;
}
static bcmos_errno remote_cli_open_session(bcmolt_devid device)
{
bcmos_errno rc;
bcmcli_session_parm sess_parm = {};
sess_parm.access_right = BCMCLI_ACCESS_DEBUG;
sess_parm.write = remote_cli_session_write_cb;
sess_parm.user_priv = bcmos_calloc(sizeof(device));
sess_parm.line_edit_mode = BCMCLI_LINE_EDIT_DISABLE;
BUG_ON(sess_parm.user_priv == NULL);
*((bcmolt_devid *)sess_parm.user_priv) = device;
rc = bcmcli_session_open(&sess_parm, &remote_cli_data[device].session);
if (rc != BCM_ERR_OK)
{
perror("Can't open session");
#ifdef ENABLE_LOG
BCM_LOG(
ERROR,
remote_cli_data[device].log_id,
"Can't open session\n");
#endif
shutdown(remote_cli_data[device].client_socket, SHUT_RDWR);
}
return rc;
}
static bcmos_errno remote_cli_open_socket(bcmolt_devid device)
{
struct sockaddr_in sa = {};
/* Start listening on port */
remote_cli_data[device].client_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (remote_cli_data[device].client_socket < 0)
{
perror("Can't create socket");
#ifdef ENABLE_LOG
BCM_LOG(
ERROR,
remote_cli_data[device].log_id,
"Can't create socket\n");
#endif
return BCM_ERR_INTERNAL;
}
/* Bind local */
sa.sin_family = AF_INET;
sa.sin_port = (in_port_t)htons(remote_cli_data[device].port);
sa.sin_addr.s_addr = INADDR_ANY;
if (bind(remote_cli_data[device].client_socket, (struct sockaddr*)&sa, sizeof(sa)) == -1)
{
perror("Can't bind to socket");
#ifdef ENABLE_LOG
BCM_LOG(
ERROR,
remote_cli_data[device].log_id,
"Can't bind socket to port %u\n",
remote_cli_data[device].port);
#endif
shutdown(remote_cli_data[device].client_socket, SHUT_RDWR);
return BCM_ERR_INTERNAL;
}
return BCM_ERR_OK;
}
static void remote_cli_indication_cb(bcmos_module_id module_id, bcmos_msg *msg)
{
bcmolt_devid device = (bcmolt_devid)(module_id - BCMOS_MODULE_ID_REMOTE_CLI_DEV0);
bcmolt_msg *ind = msg->data;
bcmos_mutex_lock(&remote_cli_data[device].output_lock);
remote_cli_data[device].output_current_char = 0;
memset(remote_cli_data[device].output_buffer, 0, sizeof(remote_cli_data[device].output_buffer));
apicli_msg_dump(remote_cli_data[device].session, ind);
if (ind->group == BCMOLT_MGT_GROUP_AUTO)
{
send_output_packets(device, REMOTE_CLI_OPCODE_INDICATION, ind->corr_tag);
++remote_cli_data[device].stats.indications;
}
else if (ind->group == BCMOLT_MGT_GROUP_PROXY_RX)
{
send_output_packets(device, REMOTE_CLI_OPCODE_PROXY_RX, ind->corr_tag);
++remote_cli_data[device].stats.proxy_rxs;
}
else
{
#ifdef ENABLE_LOG
BCM_LOG(
ERROR,
remote_cli_data[device].log_id,
"Unknown group type %u\n",
ind->group);
#endif
}
bcmos_mutex_unlock(&remote_cli_data[device].output_lock);
bcmolt_msg_free(ind);
}
static void remote_cli_output_cb(bcmos_module_id module_id, bcmos_msg *msg)
{
bcmolt_devid device = (bcmolt_devid)(module_id - BCMOS_MODULE_ID_REMOTE_CLI_DEV0);
process_cli_input(device);
send_output_packets(device, REMOTE_CLI_OPCODE_CLI_COMMAND, remote_cli_data[device].current_corr_tag);
remote_cli_data[device].output_pending = BCMOS_FALSE;
++remote_cli_data[device].stats.tx_responses;
}
static int remote_cli_socket_task(long data)
{
bcmolt_devid device = (bcmolt_devid)data;
struct sockaddr_in sender;
socklen_t sendsize = sizeof(sender);
bcmolt_buf buf;
ssize_t len;
uint8_t opcode = 0;
uint16_t corr_tag = 0;
bcmos_bool ok = BCMOS_TRUE;
bcmos_msg output_msg = { .handler = remote_cli_output_cb };
bcmos_errno rc;
rc = remote_cli_open_socket(device);
BCMOS_CHECK_RETURN(rc != BCM_ERR_OK, rc, -1);
rc = remote_cli_open_session(device);
BCMOS_CHECK_RETURN(rc != BCM_ERR_OK, rc, -1);
while (remote_cli_data[device].is_running)
{
if (remote_cli_data[device].output_pending)
{
bcmos_usleep(1000);
continue;
}
remote_cli_data[device].current_corr_tag = -1;
memset(remote_cli_data[device].input_buffer, 0, sizeof(remote_cli_data[device].input_buffer));
memset(&sender, 0, sizeof(sender));
len = recvfrom(
remote_cli_data[device].client_socket,
remote_cli_data[device].input_buffer,
sizeof(remote_cli_data[device].input_buffer),
0,
(struct sockaddr *)&sender,
&sendsize);
if (len < 0)
{
bcmos_usleep(1000000);
continue;
}
++remote_cli_data[device].stats.rx_requests;
if ((remote_cli_data[device].client.sin_addr.s_addr != sender.sin_addr.s_addr) ||
(remote_cli_data[device].client.sin_port != sender.sin_port))
{
remote_cli_data[device].client = sender;
}
/* Unpack received message */
bcmolt_buf_init(&buf, (uint32_t)len, remote_cli_data[device].input_buffer, BCMOLT_BUF_ENDIAN_FIXED);
ok = ok && bcmolt_buf_read_u8(&buf, &opcode);
ok = ok && bcmolt_buf_read_u16_be(&buf, &corr_tag);
if (!ok)
{
++remote_cli_data[device].stats.unpack_errors;
continue;
}
if ((remote_cli_opcode)opcode != REMOTE_CLI_OPCODE_CLI_COMMAND)
{
++remote_cli_data[device].stats.unpack_errors;
continue;
}
if ((remote_cli_data[device].current_corr_tag != corr_tag) && (remote_cli_data[device].current_corr_tag >= 0))
{
++remote_cli_data[device].stats.correlation_errors;
continue;
}
remote_cli_data[device].input_str = (char *)bcmolt_buf_snap_get(&buf);
remote_cli_data[device].input_str[bcmolt_buf_get_remaining_size(&buf)] = '\0';
remote_cli_data[device].current_corr_tag = corr_tag;
remote_cli_data[device].output_pending = BCMOS_TRUE;
rc = bcmos_msg_send_to_module(
bcmos_module_id_for_device(BCMOS_MODULE_ID_REMOTE_CLI_DEV0, device),
&output_msg,
BCMOS_MSG_SEND_AUTO_FREE);
if (rc != BCM_ERR_OK)
{
++remote_cli_data[device].stats.message_errors;
}
}
bcmos_free(bcmcli_session_user_priv(remote_cli_data[device].session));
bcmcli_session_close(remote_cli_data[device].session);
shutdown(remote_cli_data[device].client_socket, SHUT_RDWR);
remote_cli_data[device].socket_task.destroyed = BCMOS_TRUE;
return 0;
}
static bcmos_errno remote_cli_start(bcmolt_devid device, uint32_t remote_cli_port)
{
bcmos_errno rc;
bcmos_task_parm output_task_params =
{
.name = "remote_cli_indication",
.priority = TASK_PRIORITY_TRANSPORT_REMOTE_CLI
};
bcmos_task_parm socket_task_params =
{
.name = "remote_cli_main",
.handler = remote_cli_socket_task,
.priority = TASK_PRIORITY_TRANSPORT_REMOTE_CLI,
.data = (long)device
};
bcmos_module_parm module_params =
{
.qparm = { .name = "remote_cli", .size = REMOTE_CLI_MESSAGE_QUEUE_DEPTH }
};
remote_cli_data[device].port = remote_cli_port;
remote_cli_data[device].current_corr_tag = -1;
remote_cli_data[device].is_running = BCMOS_TRUE;
/* Create thread listening for incoming APIs */
rc = bcmos_task_create(&remote_cli_data[device].socket_task, &socket_task_params);
BUG_ON(BCM_ERR_OK != rc);
rc = bcmos_task_create(&remote_cli_data[device].output_task, &output_task_params);
BUG_ON(BCM_ERR_OK != rc);
bcmos_mutex_create(&remote_cli_data[device].output_lock, 0, "remote_cli");
rc = bcmos_module_create(
bcmos_module_id_for_device(BCMOS_MODULE_ID_REMOTE_CLI_DEV0, device),
&remote_cli_data[device].output_task,
&module_params);
BUG_ON(BCM_ERR_OK != rc);
#ifdef ENABLE_LOG
BCM_LOG(
INFO,
remote_cli_data[device].log_id,
"BCM68620 remote cli for device %d is listening on port %u\n",
(int)device,
remote_cli_data[device].port);
#endif
return BCM_ERR_OK;
}
/* Auto / proxy message handler */
void bcmolt_remote_cli_auto_rx_cb(bcmolt_devid device, bcmolt_msg *msg)
{
bcmos_errno err;
bcmolt_msg *ind_clone = NULL;
if (device >= BCMTR_MAX_OLTS)
{
return;
}
if (remote_cli_data[device].is_running)
{
err = bcmolt_msg_clone(&ind_clone, msg);
if (err != BCM_ERR_OK)
{
#ifdef ENABLE_LOG
BCM_LOG(ERROR, remote_cli_data[device].log_id, "Indication clone failed: %s\n", bcmos_strerror(err));
#endif
return;
}
ind_clone->os_msg.handler = remote_cli_indication_cb;
ind_clone->os_msg.data = ind_clone;
err = bcmos_msg_send_to_module(
bcmos_module_id_for_device(BCMOS_MODULE_ID_REMOTE_CLI_DEV0, device),
&ind_clone->os_msg,
0);
if (err != BCM_ERR_OK)
{
++remote_cli_data[device].stats.message_errors;
}
}
}
bcmos_errno bcmolt_remote_cli_init(bcmcli_entry *root, bcmolt_devid device, uint32_t remote_cli_port)
{
bcmcli_entry *dir;
if (device >= BCMTR_MAX_OLTS)
{
return BCM_ERR_PARM;
}
if (remote_cli_data[device].is_running)
{
return BCM_ERR_ALREADY;
}
BCM_MEMZERO_STRUCT(&remote_cli_data[device]);
#ifdef ENABLE_LOG
{
char log_id[32];
snprintf(log_id, sizeof(log_id) - 1, "remote_cli_%d", (int)device);
remote_cli_data[device].log_id =
bcm_dev_log_id_register(log_id, DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
}
#else
remote_cli_data[device].log_id = DEV_LOG_INVALID_ID;
#endif
dir = bcmcli_dir_add(root, "remote_cli", "Remote CLI", BCMCLI_ACCESS_GUEST, NULL);
if (!dir)
{
#ifdef ENABLE_LOG
BCM_LOG(ERROR, remote_cli_data[device].log_id, "Can't create remote CLI directory\n");
#endif
BUG();
}
BCMCLI_MAKE_CMD(dir, "stat", "Remote CLI statistics", remote_cli_stats_cmd,
BCMCLI_MAKE_PARM_RANGE(
"device",
"Device index",
BCMCLI_PARM_NUMBER,
BCMCLI_PARM_FLAG_OPTIONAL,
0,
BCMTR_MAX_OLTS - 1));
return remote_cli_start(device, remote_cli_port);
}
void bcmolt_remote_cli_stop(void)
{
int i;
bcmos_bool was_running[BCMTR_MAX_OLTS] = {};
for (i = 0; i < BCMTR_MAX_OLTS; i++)
{
was_running[i] = remote_cli_data[i].is_running;
if (was_running[i])
{
shutdown(remote_cli_data[i].client_socket, SHUT_RDWR);
remote_cli_data[i].is_running = BCMOS_FALSE;
}
}
for (i = 0; i < BCMTR_MAX_OLTS; i++)
{
if (!was_running[i])
{
continue;
}
while (!remote_cli_data[i].socket_task.destroyed)
{
bcmos_usleep(10000);
}
bcmos_task_destroy(&remote_cli_data[i].socket_task);
bcmos_module_destroy(bcmos_module_id_for_device(BCMOS_MODULE_ID_REMOTE_CLI_DEV0, (bcmolt_devid)i));
bcmos_task_destroy(&remote_cli_data[i].output_task);
bcmos_mutex_destroy(&remote_cli_data[i].output_lock);
}
}