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