BAL and Maple Release 2.2
Signed-off-by: Shad Ansari <developer@Carbon.local>
diff --git a/bal_release/src/lib/libbalapi/bal_api.c b/bal_release/src/lib/libbalapi/bal_api.c
new file mode 100644
index 0000000..4374fd2
--- /dev/null
+++ b/bal_release/src/lib/libbalapi/bal_api.c
@@ -0,0 +1,658 @@
+/******************************************************************************
+ *
+ * <: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;
+}
+
+
+/*@}*/