blob: 4374fd26f4f9a56c3f2503ef84b3af302f0292b3 [file] [log] [blame]
/******************************************************************************
*
* <:copyright-BRCM:2016:DUAL/GPL:standard
*
* Copyright (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.
*
* :>
*
*****************************************************************************/
/**
* @file bal_api.c
* @brief The BAL Public API
*
* @addtogroup api
*/
/*@{*/
/* Project includes */
#include "bal_api.h"
#include "bal_msg.h"
#include "bal_api_worker.h"
#include "bal_obj_msg_pack_unpack.h"
#ifdef BAL_MONOLITHIC
#include <bal_worker.h>
#endif
#ifdef ENABLE_LOG
#include <bcm_dev_log.h>
/*
* @brief The logging device id for the BAL public API
*/
dev_log_id log_id_public_api;
#endif
/*
* @brief The global mgmt queues
*
* These are the queues through which the BAL API communicates with
* the core, and vice versa.
*/
static bcmos_msg_queue balapi_rsp_queue;
static bcmos_msg_queue balapi_to_core_queue;
bcmos_msg_queue *p_balapi_rsp_queue;
bcmos_msg_queue *p_balapi_to_core_queue;
static bcmos_mutex balapi_lock;
static uint32_t balapi_exchange_id;
static STAILQ_HEAD(pending_req_list, bcmos_msg) pending_req_list;
static bcmos_task api_rsp_rx_thread;
static int _bal_ipc_api_rx_handler(long data);
/* Rx polling timeout (us) */
#define BAL_API_RX_POLL_TIMEOUT 100000
/*****************************************************************************
* Initialize the BAL Public API internal data structures
*****************************************************************************/
bcmos_errno bcmbal_api_init(const char *balapi_mgmt_ip_port,
const char *core_mgmt_ip_port)
{
bcmos_errno ret = BCM_ERR_OK;
bcmos_msg_queue_parm msg_q_p = {};
bcmos_task_parm task_p = {};
#ifdef ENABLE_LOG
log_id_public_api = bcm_dev_log_id_register("BAL_API", DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
BUG_ON(log_id_public_api == DEV_LOG_INVALID_ID);
#endif
do
{
STAILQ_INIT(&pending_req_list);
ret = bcmos_mutex_create(&balapi_lock, 0, "bal_api");
if (BCM_ERR_OK != ret)
{
BCM_LOG(ERROR, log_id_public_api, "Couldn't create BAL API protection mutex\n");
break;
}
/* Create BAL API RX management queue - the BAL Public API
* exchanges management messages with the BAL core using this queue.
*/
msg_q_p.name = "balapi_mgmt_q";
if (NULL != balapi_mgmt_ip_port)
{
msg_q_p.local_ep_address = balapi_mgmt_ip_port;
msg_q_p.remote_ep_address = NULL;
msg_q_p.ep_type = BCMOS_MSG_QUEUE_EP_UDP_SOCKET;
}
else
{
msg_q_p.ep_type = BCMOS_MSG_QUEUE_EP_LOCAL;
}
ret = bcmos_msg_queue_create(&balapi_rsp_queue, &msg_q_p);
if (BCM_ERR_OK != ret)
{
BCM_LOG(ERROR, log_id_public_api, "Couldn't create BAL API mgmt queue\n");
ret = BCM_ERR_INTERNAL;
break;
}
p_balapi_rsp_queue = &balapi_rsp_queue;
#ifdef BAL_MONOLITHIC
if (NULL == balapi_mgmt_ip_port)
p_bal_core_to_api_queue = p_balapi_rsp_queue;
#endif
/* Create queue for sending API requests to the core. Only do it if API and core interact via UDP
*/
if (NULL != core_mgmt_ip_port)
{
msg_q_p.name = "balapi_to_core_q";
msg_q_p.local_ep_address = NULL;
msg_q_p.remote_ep_address = core_mgmt_ip_port;
msg_q_p.ep_type = BCMOS_MSG_QUEUE_EP_UDP_SOCKET;
ret = bcmos_msg_queue_create(&balapi_to_core_queue, &msg_q_p);
if (BCM_ERR_OK != ret)
{
BCM_LOG(ERROR, log_id_public_api, "Couldn't create BAL API mgmt queue\n");
ret = BCM_ERR_INTERNAL;
break;
}
p_balapi_to_core_queue = &balapi_to_core_queue;
}
/* Create BAL API RX thread */
task_p.name = "ipc_api_rsp_rx_thread";
task_p.priority = TASK_PRIORITY_IPC_RX;
task_p.handler = _bal_ipc_api_rx_handler;
task_p.data = (long)p_balapi_rsp_queue;
ret = bcmos_task_create(&api_rsp_rx_thread, &task_p);
if (ret)
{
BCM_LOG(ERROR, log_id_public_api, "Couldn't create BAL API response RX thread\n");
break;
}
/*
* Initialize the BAL Public API backend worker and rx threads. These
* threads are used to receive asynchronous indications from the core.
*/
enable_bal_api_indications(balapi_mgmt_ip_port);
}
while(0);
return ret;
}
/*****************************************************************************
* Un-initialize the BAL Public API internal data structures
*****************************************************************************/
bcmos_errno bcmbal_api_finish(void)
{
bcmos_task_destroy(&api_rsp_rx_thread);
bcmos_msg_queue_destroy(&balapi_rsp_queue);
if (p_balapi_to_core_queue == &balapi_to_core_queue)
bcmos_msg_queue_destroy(&balapi_to_core_queue);
bal_api_indications_finish();
return BCM_ERR_OK;
}
/* Find pending request matching response */
static bal_comm_msg_hdr *_bal_api_get_request_by_ex_id(uint32_t ex_id)
{
bcmos_msg *req_msg, *tmp_msg;
bal_comm_msg_hdr *comm_hdr = NULL;
STAILQ_FOREACH_SAFE(req_msg, &pending_req_list, next, tmp_msg)
{
comm_hdr = bcmbal_bal_hdr_get_by_bcmos_hdr(req_msg);
if (comm_hdr->ex_id == ex_id)
{
STAILQ_REMOVE(&pending_req_list, req_msg, bcmos_msg, next);
break;
}
}
return req_msg ? comm_hdr : NULL;
}
/* Check if any pending request timed out */
static void _bal_api_check_req_timeout(void)
{
bcmos_msg *req_msg, *tmp_msg;
bal_comm_msg_hdr *comm_hdr;
bcmbal_obj *req_obj;
uint32_t ts = bcmos_timestamp();
STAILQ_FOREACH_SAFE(req_msg, &pending_req_list, next, tmp_msg)
{
comm_hdr = bcmbal_bal_hdr_get_by_bcmos_hdr(req_msg);
if (ts - comm_hdr->timestamp >= BCMBAL_MSG_TIMEOUT_1_SEC)
{
STAILQ_REMOVE(&pending_req_list, req_msg, bcmos_msg, next);
req_obj = (bcmbal_obj *)bcmbal_payload_ptr_get(comm_hdr);
/* Release pending API call */
req_obj->status = BCM_ERR_TIMEOUT;
BCM_LOG(DEBUG, log_id_public_api, "Timing out request %p\n", comm_hdr);
bcmos_sem_post(&comm_hdr->sem);
}
}
}
/* API response handler */
static int _bal_ipc_api_rx_handler(long data)
{
static uint32_t last_timeout_check_ts;
bcmos_msg_queue *rxq = (bcmos_msg_queue *)data;
bcmos_task *my_task = bcmos_task_current();
bcmos_msg *msg;
bcmos_errno ret = BCM_ERR_OK;
uint32_t ex_id;
bal_comm_msg_hdr *req;
bcmbal_obj *req_obj;
last_timeout_check_ts = bcmos_timestamp();
while (!my_task->destroy_request)
{
/* Wait for response */
msg = NULL;
req = NULL;
ret = bcmos_msg_recv(rxq, BAL_API_RX_POLL_TIMEOUT, &msg);
if(BCM_ERR_OK != ret && BCM_ERR_TIMEOUT != ret)
{
BCM_LOG(ERROR, log_id_public_api,
"error during bcmos_msg_recv (error:%s)\n", bcmos_strerror(ret));
}
/* Peek exchange id */
if (msg)
{
ret = bcmbal_bal_msg_peek_ex_id(msg, &ex_id);
if(BCM_ERR_OK != ret)
{
BCM_LOG(ERROR, log_id_public_api,
"bad message. Can't find exchange id (error:%s)\n", bcmos_strerror(ret));
bcmos_msg_free(msg);
msg = NULL;
}
else
{
BCM_LOG(DEBUG, log_id_public_api, "Received message with ex_id=%u\n", ex_id);
}
}
/* Now find pending request and also check if any pending request(s) timed out */
bcmos_mutex_lock(&balapi_lock);
if (msg)
{
req = _bal_api_get_request_by_ex_id(ex_id);
if (NULL == req)
{
BCM_LOG(ERROR, log_id_public_api,
"Request with ex_id=%u is not found. Probably expired. Response discarded\n", ex_id);
}
else
{
BCM_LOG(DEBUG, log_id_public_api, "Found request %p\n", req);
}
}
if (bcmos_timestamp() - last_timeout_check_ts >= BCMBAL_MSG_TIMEOUT_1_SEC)
{
_bal_api_check_req_timeout();
last_timeout_check_ts = bcmos_timestamp();
}
bcmos_mutex_unlock(&balapi_lock);
/* Got a message. Now unpack it */
if (req)
{
req_obj = (bcmbal_obj *)bcmbal_payload_ptr_get(req);
ret = bcmbal_obj_msg_unpack(msg, &req);
if (BCM_ERR_OK != ret)
{
BCM_LOG(ERROR, log_id_public_api, "Error during message unpack: %s\n", bcmos_strerror(ret));
req_obj->status = ret;
}
/* Release pending API */
BCM_LOG(DEBUG, log_id_public_api, "Posting request semaphore for %p\n", req);
bcmos_sem_post(&req->sem);
}
if (msg)
bcmos_msg_free(msg); /* release packed message. It is no longer needed */
}
my_task->destroyed = BCMOS_TRUE;
return (BCM_ERR_OK == ret) ? 0 : -EINVAL;
}
static bcmbal_mgmt_oper_id _bcmbal_obj_to_oper_id(const bcmbal_obj *objinfo)
{
if (objinfo->group == BCMBAL_MGT_GROUP_STAT)
{
return BCMBAL_MGMT_OPER_ID_GET_STATS;
}
else if ((objinfo->type & BCMBAL_OBJ_MSG_TYPE_GET))
{
return BCMBAL_MGMT_OPER_ID_GET;
}
else if ((objinfo->type & BCMBAL_OBJ_MSG_TYPE_CLEAR))
{
return BCMBAL_MGMT_OPER_ID_CLEAR;
}
else
{
return BCMBAL_MGMT_OPER_ID_SET;
}
}
#define BALAPI_OPER_TIMEOUT (30000000) /* 30 seconds */
static bcmos_errno _bcmbal_oper(bcmbal_obj *objinfo)
{
bal_comm_msg_hdr *comm_hdr = bcmbal_bal_hdr_get(objinfo);
bcmos_msg *os_msg;
bcmos_errno ret = BCM_ERR_OK;
/*
* Send the message to the core for processing
*/
/* Parameter checks */
BUG_ON(NULL == objinfo);
/* Check the magic number to be sure that the object has been properly initialized */
if(BCMBAL_OBJ_INIT_VAL != objinfo->obj_init_val)
{
BCM_LOG(ERROR, log_id_public_api,
"Object has not been initialized: must use a BCMBAL INIT macro on the object before calling the BAL API\n");
return BCM_ERR_PARM;
}
ret = bcmos_sem_create(&comm_hdr->sem, 0, 0, "api_req");
if (ret)
{
BCM_LOG(ERROR, log_id_public_api, "Can't create semaphore: %s\n", bcmos_strerror(ret));
/* return here. We don't want to destroy semaphore that wasn't created */
return ret;
}
do
{
bcmbal_msg_hdr_set(objinfo,
BCMBAL_MGMT_MSG,
BAL_MSG_TYPE_REQ,
BAL_SUBSYSTEM_PUBLIC_API,
objinfo->obj_type,
_bcmbal_obj_to_oper_id(objinfo),
0);
BCM_LOG(DEBUG, log_id_public_api, "about to send %p\n", objinfo);
bcmos_mutex_lock(&balapi_lock);
bcmbal_ex_id_set(objinfo, ++balapi_exchange_id);
os_msg = bcmbal_bcmos_hdr_get(objinfo);
STAILQ_INSERT_TAIL(&pending_req_list, os_msg, next);
bcmos_mutex_unlock(&balapi_lock);
if (BCM_ERR_OK != (ret = bcmbal_msg_send(p_balapi_to_core_queue, objinfo, BCMOS_MSG_SEND_NO_FREE_ON_ERROR)))
{
BCM_LOG(ERROR, log_id_public_api, "message send failed with error: %s\n", bcmos_strerror(ret));
break;
}
BCM_LOG(DEBUG, log_id_public_api, "REQ message sent to core\n");
/*
* We've sent the message to the core, now, wait for the response (or timeout)
*/
ret = bcmos_sem_wait(&comm_hdr->sem, BALAPI_OPER_TIMEOUT);
if (BCM_ERR_OK != ret)
{
BCM_LOG(ERROR, log_id_public_api, "rsp message receive for failed with error: %s\n",
bcmos_strerror(ret));
break;
}
BCM_LOG(DEBUG, log_id_public_api, "RSP message received from core\n");
if (BCM_ERR_OK != ret)
{
BCM_LOG(ERROR, log_id_public_api, "failed to process rsp msg\n");
break;
}
if(BCM_ERR_OK != objinfo->status)
{
BCM_LOG(ERROR, log_id_public_api, "remote message command status is: %s\n",
bcmos_strerror(objinfo->status));
}
/*
* Pass the command status received from the core back to the caller
*/
ret = objinfo->status;
}
while(0);
bcmos_sem_destroy(&comm_hdr->sem);
return ret;
}
/*****************************************************************************
* BAL Public API Set (or modify) command.
*****************************************************************************/
bcmos_errno bcmbal_cfg_set(bcmbal_cfg *objinfo)
{
bcmos_errno ret = BCM_ERR_OK;
BCM_LOG(INFO, log_id_public_api, "BAL PUBLIC API - BCMBAL_SET\n");
if(BCMBAL_OBJ_ID_PACKET == objinfo->hdr.obj_type)
{
BCM_LOG(ERROR, log_id_public_api, "unsupported object id detected %d\n", objinfo->hdr.obj_type);
ret = BCM_ERR_NOT_SUPPORTED;
}
else
{
objinfo->hdr.type = BCMBAL_OBJ_MSG_TYPE_SET;
ret = _bcmbal_oper(&objinfo->hdr);
}
return ret;
}
#define BCMBAL_MAX_PROXY_PACKET_SIZE (1600)
/*****************************************************************************
* BAL Public API Packet Send function.
*****************************************************************************/
bcmos_errno bcmbal_pkt_send(bcmbal_dest dest,
const char *packet_to_send,
uint16_t packet_len)
{
/* Convert the user packet into a BAL object */
bcmbal_packet_cfg *p_packet_obj;
bcmbal_packet_key key;
bcmbal_u8_list_u32 pkt;
bcmos_errno ret;
BCM_LOG(INFO, log_id_public_api, "BAL PUBLIC API - BCMBAL_SEND to %s\n",
(BCMBAL_DEST_TYPE_NNI == dest.type) ? "NNI" : "SUB-TERM");
if(BCMBAL_MAX_PROXY_PACKET_SIZE < packet_len)
{
BCM_LOG(ERROR, log_id_public_api, "user packet length (%d) cannot be greater than %d\n",
packet_len,
BCMBAL_MAX_PROXY_PACKET_SIZE);
return BCM_ERR_PARM;
}
BCM_LOG(INFO, log_id_public_api, "user packet first 8 bytes %02X%02X%02X%02X%02X%02X%02X%02X\n",
packet_to_send[0], packet_to_send[1], packet_to_send[2], packet_to_send[3],
packet_to_send[4], packet_to_send[5], packet_to_send[6], packet_to_send[7]);
/* Set up the object key */
key.packet_send_dest = dest;
/* Allocate room for the packet object including the user packet */
p_packet_obj = bcmos_calloc(sizeof(bcmbal_packet_cfg) + packet_len);
BCMBAL_CFG_INIT(p_packet_obj, packet, key);
/* Now fill in user packet data into the object */
pkt.len = packet_len;
pkt.val = (uint8_t *)&p_packet_obj[1];
memcpy(pkt.val, packet_to_send, packet_len);
BCMBAL_CFG_PROP_SET(p_packet_obj, packet, pkt, pkt);
p_packet_obj->hdr.hdr.type = BCMBAL_OBJ_MSG_TYPE_SET; /* internally packet SEND is modeled as a config SET */
ret = _bcmbal_oper(&(p_packet_obj->hdr.hdr));
bcmos_free(p_packet_obj);
return ret;
}
/*****************************************************************************
* BAL Public API Get command.
*****************************************************************************/
bcmos_errno bcmbal_cfg_get(bcmbal_cfg *objinfo)
{
bcmos_errno ret = BCM_ERR_OK;
BCM_LOG(DEBUG, log_id_public_api, "BAL PUBLIC API - BCMBAL_GET\n");
if(BCMBAL_OBJ_ID_PACKET == objinfo->hdr.obj_type)
{
BCM_LOG(ERROR, log_id_public_api, "unsupported object id detected %d\n", objinfo->hdr.obj_type);
ret = BCM_ERR_NOT_SUPPORTED;
}
else
{
objinfo->hdr.type = BCMBAL_OBJ_MSG_TYPE_GET;
ret = _bcmbal_oper(&(objinfo->hdr));
}
return ret;
}
/*****************************************************************************
* BAL Public API Clear command.
*****************************************************************************/
bcmos_errno bcmbal_cfg_clear(bcmbal_cfg *objinfo)
{
bcmos_errno ret = BCM_ERR_OK;
BCM_LOG(INFO, log_id_public_api, "BAL PUBLIC API - BCMBAL_CLEAR\n");
if(BCMBAL_OBJ_ID_PACKET == objinfo->hdr.obj_type)
{
BCM_LOG(ERROR, log_id_public_api, "unsupported object id detected %d\n", objinfo->hdr.obj_type);
ret = BCM_ERR_NOT_SUPPORTED;
}
else
{
objinfo->hdr.type = BCMBAL_OBJ_MSG_TYPE_CLEAR;
ret = _bcmbal_oper(&objinfo->hdr);
}
return ret;
}
/*****************************************************************************
* @brief BAL Public API Get Stats command.
*****************************************************************************/
bcmos_errno bcmbal_stat_get(bcmbal_stat *objinfo)
{
/* Parameter checks */
BUG_ON(NULL == objinfo);
/*
* @todo Finish the stats function
*/
BCM_LOG(ERROR, log_id_public_api, "bal get stats API not supported\n");
return BCM_ERR_NOT_SUPPORTED;
}
/*****************************************************************************
* BAL Public API indication subscription.
*****************************************************************************/
bcmos_errno bcmbal_subscribe_ind(bcmbal_cb_cfg *cb_cfg)
{
bcmos_errno ret = BCM_ERR_OK;
/*
* The indication subscription function
*/
BCM_LOG(DEBUG, log_id_public_api, "BAL indication subscription for type: %s (%d)\n",
bcmbal_objtype_str(cb_cfg->obj_type), cb_cfg->obj_type);
ret = _manage_api_ind_listener(IND_CB_SUBSCRIBE, cb_cfg);
return ret;
}
/*****************************************************************************
* BAL Public API indication un-subscription.
*****************************************************************************/
bcmos_errno bcmbal_unsubscribe_ind(bcmbal_cb_cfg *cb_cfg)
{
bcmos_errno ret = BCM_ERR_OK;
BUG_ON(NULL == cb_cfg);
/*
* The indication subscription function
*/
BCM_LOG(DEBUG, log_id_public_api, "BAL indication un-subscription for type: %s (%d)\n",
bcmbal_objtype_str(cb_cfg->obj_type), cb_cfg->obj_type);
ret = _manage_api_ind_listener(IND_CB_UNSUBSCRIBE, cb_cfg);
return ret;
}
/*****************************************************************************/
/**
* @brief A function to get the string representation of the interface type
*
* @param int_type The interface type to get
*
* @returns const char * A pointer to a string containing the interface type,
* or "INVALID", if not a valid type.
*
*****************************************************************************/
const char *bcmbal_get_interface_type_str(bcmbal_intf_type int_type)
{
const char *type_str;
switch (int_type)
{
case(BCMBAL_INTF_TYPE_PON):
{
type_str = "pon";
}
break;
case(BCMBAL_INTF_TYPE_NNI):
{
type_str = "nni";
}
break;
default:
{
type_str = "INVALID";
}
break;
}
return type_str;
}
/*@}*/