/* =========================================================================
 *  Copyright 2019-2022 NXP
 *
 *  NXP Confidential. This software is owned or controlled by NXP and may only
 *  be used strictly in accordance with the applicable license terms. By
 *  expressly accepting such terms or by downloading, installing, activating
 *  and/or otherwise using the software, you are agreeing that you have read,
 *  and that you agree to comply with and are bound by, such license terms. If
 *  you do not agree to be bound by the applicable license terms, then you may
 *  not retain, install, activate or otherwise use the software.
 *  <<< PFE Restricted Software >>>
 *
 * ========================================================================= */

/**
 * @addtogroup  dxgr_FCI
 * @{
 *
 * @file		fci_core_autosar.c
 * @brief		The AUTOSAR-specific FCI core component. Full description can be
 * 				found within the fci_core.h.
 *
 */

/*==================================================================================================
*                              SOURCE FILE VERSION INFORMATION
==================================================================================================*/
#define PFE_SRC_VERSION_CHECK 43100440

#include "pfe_cfg.h"
#include "oal.h"
#include "fci.h"
#include "fci_internal.h"
#include "fci_core.h"
#include "fifo.h"

#ifdef PFE_CFG_FCI_ENABLE

/*
 *	AUTOSAR-specific FCI client representation type (fci_core_client_t)
 */
struct __fci_core_client_tag
{
	bool_t initialized;
	fifo_t *msg_fifo;
	oal_mutex_t fifo_lock;

	/*
	 * This should be a generic endpoint identifier applicable for linux/qnx/asr/...
	 */
};

/*
 *	AUTOSAR-specific FCI core representation type (fci_core_t)
 */
struct __fci_core_tag
{
	fci_core_client_t client;
};

typedef struct
{
    fifo_t instance;
    __attribute__((aligned(HAL_CACHE_LINE_SIZE))) void *data[FCI_CFG_MSG_FIFO_DEPTH];
} fci_fifo_t;

#define ETH_43_PFE_START_SEC_VAR_CLEARED_UNSPECIFIED
#include "Eth_43_PFE_MemMap.h"

/* FCI FIFO */
static fci_fifo_t fci_fifo;

#define ETH_43_PFE_STOP_SEC_VAR_CLEARED_UNSPECIFIED
#include "Eth_43_PFE_MemMap.h"

#ifdef PFE_CFG_TARGET_OS_AUTOSAR
#define ETH_43_PFE_START_SEC_CODE
#include "Eth_43_PFE_MemMap.h"
#endif /* PFE_CFG_TARGET_OS_AUTOSAR */

/*
 	 Create FCI core instance
*/
errno_t fci_core_init(const char_t *const id)
{
	fci_core_t *core;
	errno_t ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
	if (unlikely(NULL == id))
	{
		NXP_LOG_ERROR("NULL argument received\n");
		ret = EINVAL;
	}
	else
#else
	(void)id;
#endif /* PFE_CFG_NULL_ARG_CHECK */
	{
		if(__context.core != NULL)
		{
			NXP_LOG_ERROR("FCI_CORE has already been initialized\n");
			ret = EINVAL;
		}
		else
		{

			core = oal_mm_malloc(sizeof(fci_core_t));
			if (NULL == core)
			{
				ret = ENOMEM;
			}
			else
			{
				(void)memset(core, 0, sizeof(fci_core_t));

				core->client.initialized = FALSE;
				core->client.msg_fifo = NULL;

                core->client.fifo_lock = PFE_FCI_FIFO_MUTEX;
				(void)oal_mutex_init(&core->client.fifo_lock);
                core->client.msg_fifo = fifo_create(FCI_CFG_MSG_FIFO_DEPTH, &fci_fifo.instance, fci_fifo.data);
                if (NULL == core->client.msg_fifo)
                {
                    NXP_LOG_ERROR("Can't create FIFO for messages from driver to DCI client\n");
                    ret = ENOMEM;
                }
                else
                {
                    core->client.initialized = TRUE;
                    __context.core = core;
                    ret = EOK;
                }
			}
		}
	}

	return ret;
}

/*
 	 Destroy FCI core
*/
void fci_core_fini(void)
{
	fci_core_t *core = (fci_core_t *)__context.core;
	fci_msg_t msg;
	errno_t ret;

	(void)memset(&msg, 0, sizeof(msg));
	msg.type = FCI_MSG_CMD;
	msg.msg_cmd.code = FPP_CMD_ENDPOINT_SHUTDOWN;

	if (NULL != core)
	{
		ret = fci_core_client_send_broadcast(&msg, NULL);
		/*	Notify listeners */
		if (EOK != ret)
		{
			NXP_LOG_ERROR("fci_core_client_send_broadcast() failed: %d\n", ret);
		}

		(void)oal_mutex_destroy(&core->client.fifo_lock);

        fifo_destroy(core->client.msg_fifo);
		oal_mm_free(core);
		core = NULL;
	}
}

/*
	Send message to the FCI core
*/
errno_t fci_core_send(fci_msg_t *msg, fci_msg_t *rep)
{
	(void)msg;
	(void)rep;
	fci_core_t *core = (fci_core_t *)__context.core;
	errno_t ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
	if (unlikely(NULL == core))
	{
		NXP_LOG_ERROR("NULL argument received\n");
		ret = EINVAL;
	}
	else if (unlikely(FALSE == __context.fci_initialized))
	{
		NXP_LOG_ERROR("Context not initialized\n");
		ret = EPERM;
	}
	else
#else
	(void)core;
#endif /* PFE_CFG_NULL_ARG_CHECK */
	{
		NXP_LOG_ERROR("fci_core_send should not be used within MCAL implementation\n");
		ret = EINVAL;
	}
	return ret;
}

/*
	Send message to FCI client
*/
errno_t fci_core_client_send(fci_core_client_t *client, fci_msg_t *msg, fci_msg_t *rep)
{
	errno_t err;

#if defined(PFE_CFG_NULL_ARG_CHECK)
	if (unlikely(NULL == client))
	{
		NXP_LOG_ERROR("NULL argument received\n");
		err = EINVAL;
	}
	else
#else
	(void)client;
#endif /* PFE_CFG_NULL_ARG_CHECK */
	{
		/* This function is not needed in MCAL implementation */
		(void)msg;
		(void)rep;
		err = EOK;
	}

	return err;
}

/*
	Send asynchronous message to all registered FCI clients
*/
errno_t fci_core_client_send_broadcast(fci_msg_t *msg, fci_msg_t *rep)
{
	fci_core_t *core = (fci_core_t *)__context.core;
	errno_t err;
	(void)rep;

#if defined(PFE_CFG_NULL_ARG_CHECK)
	if (unlikely(NULL == core))
	{
		NXP_LOG_ERROR("NULL argument received\n");
		err = EINVAL;
	}
	else if (unlikely(FALSE == __context.fci_initialized))
	{
		NXP_LOG_ERROR("Context not initialized\n");
		err = EPERM;
	}
	else
#else
	/*	this function could be called from a callback even after the FCI module was destroyed */
	if (unlikely(NULL == core))
	{
		NXP_LOG_ERROR("NULL argument received\n");
		err = EINVAL;
	}
	else
#endif /* PFE_CFG_NULL_ARG_CHECK */
	{
		msg->type = FCI_MSG_CORE_CLIENT_BROADCAST; /* message type is changed back to FCI_MSG_CMD in core thread */

		/*	Enter critical section */
		(void)oal_mutex_lock(&core->client.fifo_lock);

		err = fifo_put(core->client.msg_fifo, msg);
		if (unlikely(EOK != err))
		{
			NXP_LOG_ERROR("Save message to FIFO failed: %d\n", err);
		}

		/*	Leave critical section */
		(void)oal_mutex_unlock(&core->client.fifo_lock);
	}

	return err;
}

/*
	Get a message from the FIFO (called by the FCI client)
	Returns NULL when the FIFO is empty, an error code on error
	or the message from the FIFO.
*/
errno_t fci_core_client_get_msg(fci_msg_t **msg)
{
	fci_core_t *core = (fci_core_t *)__context.core;
	errno_t ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
	if (unlikely(NULL == core))
	{
		NXP_LOG_ERROR("NULL argument received\n");
		ret = EINVAL;
	}
	else if (unlikely(FALSE == __context.fci_initialized))
	{
		NXP_LOG_ERROR("Context not initialized\n");
		ret = EPERM;
	}
	else
#endif /* PFE_CFG_NULL_ARG_CHECK */
	{
		/*	Enter critical section */
		(void)oal_mutex_lock(&core->client.fifo_lock);

		*msg = fifo_get(core->client.msg_fifo);

		/*	Leave critical section */
		(void)oal_mutex_unlock(&core->client.fifo_lock);
		ret = EOK;
	}

	return ret;
}

#ifdef PFE_CFG_TARGET_OS_AUTOSAR
#define ETH_43_PFE_STOP_SEC_CODE
#include "Eth_43_PFE_MemMap.h"
#endif /* PFE_CFG_TARGET_OS_AUTOSAR */

#endif /* PFE_CFG_FCI_ENABLE */
/** @}*/
