/* =========================================================================
 *  Copyright (C) 2007 Mindspeed Technologies, Inc.
 *  Copyright 2017-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 >>>
 *
 * ========================================================================= */

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

#include "pfe_cfg.h"
#include "pfe_platform_cfg.h"
#include "fpp.h"
#include "fci_msg.h"
#include "libfci.h"
#include "fci.h"
#include "fci_internal.h"
#include "oal.h"

#ifdef PFE_CFG_FCI_ENABLE

#ifndef EOK
#define EOK 0
#endif

/*
* Debug macros
*/
#define FCILIB_PRINT
#define FCILIB_ERR    1
#define FCILIB_INIT   0
#define FCILIB_OPEN   1
#define FCILIB_CLOSE  1
#define FCILIB_WRITE  0
#define FCILIB_READ   0
#define FCILIB_DUMP   0
#define FCILIB_CATCH  0
#define FCILIB_REG_CB 0

#if (defined(FCILIB_PRINT) && defined(NXP_LOG_ENABLED))
#define FCILIB_PRINTF(type, ...) do {if(type) NXP_LOG_INFO(__VA_ARGS__);} while(0);
#else
#define FCILIB_PRINTF(type, ...) do {} while(0);
#endif

struct fci_hdr
{
	uint16_t fcode;
	uint16_t len;
} __attribute__((packed));

struct __fci_client_tag
{
	fci_mcast_groups_t group;
	fci_cb_retval_t (*event_cb)(unsigned short fcode, unsigned short len, unsigned short *payload);
};

#define ETH_43_PFE_START_SEC_CODE
#include "Eth_43_PFE_MemMap.h"

static FCI_CLIENT *fci_create_client(fci_mcast_groups_t group);
static int32_t fci_destroy_client(FCI_CLIENT *client);
static int32_t fci_command(FCI_CLIENT *client, uint16_t fcode, const uint16_t *cmd_buf, uint16_t cmd_len, uint16_t *rep_buf, uint16_t *rep_len);

/****************************** PUBLICS FUNCTIONS ********************************/
FCI_CLIENT *fci_open(fci_client_type_t client_type, fci_mcast_groups_t group)
{
	FCI_CLIENT *new_client = NULL;

	/* Create client according to the requested socket type */
	switch(client_type)
	{
		case FCI_CLIENT_DEFAULT:
		{
			FCILIB_PRINTF(FCILIB_OPEN, "fci_open:%d client type FCILIB_FF_CLIENT with group %d\n", __LINE__, group);
			new_client = fci_create_client(group);
			break;
		}

		default:
		{
			FCILIB_PRINTF(FCILIB_ERR, "LIB_FCI: fci_open():%d client type %d not supported\n", __LINE__, client_type);
			new_client = NULL;
		}

		break;
	}

	/* Unique ID used to identify this client */
	return new_client;
}

int fci_register_cb(FCI_CLIENT *client, fci_cb_retval_t (*event_cb)(unsigned short fcode, unsigned short len, unsigned short *payload))
{
	FCILIB_PRINTF(FCILIB_REG_CB, "fci_register_cb()\n")
	if (client != NULL)
	{
		client->event_cb = event_cb;
		return EOK;
	}
	else
	{
		return -EINVAL;
	}
}

int fci_close(FCI_CLIENT *client)
{
	int32_t rc;

	FCILIB_PRINTF(FCILIB_CLOSE, "fci_close()\n");

	/* Unregister FCI client */
	if (client == NULL)
	{
		return -1;
	}
	rc = fci_register_cb(client, NULL);
	if(EOK != rc)
	{
		FCILIB_PRINTF(FCILIB_ERR, "fci_close: fci_register_cb failed with %d!\n", (int_t)rc);
	}
	rc = fci_destroy_client(client);
	if (rc < 0)
	{
		FCILIB_PRINTF(FCILIB_ERR, "fci_close: fci_destroy_client failed !\n");

		return rc;
	}
	return 0;
}

int fci_cmd(FCI_CLIENT *client, unsigned short fcode, unsigned short *cmd_buf, unsigned short cmd_len, unsigned short *rep_buf, unsigned short *rep_len)
{
	FCILIB_PRINTF(FCILIB_WRITE, "fci_cmd: send fcode %#x length %d\n", fcode, cmd_len);

	return fci_command(client, fcode, cmd_buf, cmd_len, rep_buf, rep_len);
}

int fci_write(FCI_CLIENT *client, unsigned short fcode, unsigned short cmd_len, unsigned short *cmd_buf)
{
	FCILIB_PRINTF(FCILIB_WRITE, "fci_write: send fcode %#x length %d\n", fcode, cmd_len);

	return fci_command(client, fcode, cmd_buf, cmd_len, NULL, NULL);
}

int fci_query(FCI_CLIENT *client, unsigned short fcode, unsigned short cmd_len, unsigned short *cmd_buf, unsigned short *rep_len, unsigned short *rep_buf)
{
	FCILIB_PRINTF(FCILIB_WRITE, "fci_query: send fcode %#x length %d\n", fcode, cmd_len);

	return fci_command(client, fcode, cmd_buf, cmd_len, rep_buf, rep_len);
}

int fci_catch(FCI_CLIENT *client)
{
	fci_msg_t *msg = NULL;
	int32_t ret;
	bool_t shall_quit = FALSE;
	fci_cb_retval_t cb_ret;

	while (FALSE == shall_quit)
	{
		/* Read message from kernel */
		ret = fci_core_client_get_msg(&msg);
		if (EOK != ret)
		{
			FCILIB_PRINTF(FCILIB_ERR, "fci_core_client_get_msg() failed: %d\n", (int_t)ret);
			return ret;
		}
		else
		{
			/*	Message FIFO is empty */
			if (NULL == msg)
			{
				/*	Terminate */
				FCILIB_PRINTF(FCILIB_CATCH," No message to receive\n");
				shall_quit = TRUE;
			}
			else
			{
				/*	Message */
				FCILIB_PRINTF(FCILIB_CATCH," Received message payload: %s\n",(char*)msg->msg_cmd.payload);
				if ((FCI_MSG_CMD == msg->type) || (FCI_MSG_CORE_CLIENT_BROADCAST == msg->type))
				{
					/*	Call registered callback */
					if (NULL != client->event_cb)
					{
						cb_ret = client->event_cb((unsigned short)msg->msg_cmd.code,
												(unsigned short)msg->msg_cmd.length,
												(unsigned short *)msg->msg_cmd.payload);

						if (FCI_CB_CONTINUE == cb_ret)
						{
							/*	Continue */
							;
						}
						else
						{
							/*	Terminate */
							shall_quit = TRUE;
						}
					}
				}
				else
				{
					FCILIB_PRINTF(FCILIB_ERR, "Unknown message received (type = 0x%x)\n", msg->type);
				}
			}
		}
	}

	return 0;
}

/*
 * @brief		fci_fd: Not supported yet
 * @param[in]	client FCI client instance to be used
 * @return		Always -1 (failure) because the function is not supported.
 * @warning		Function shall not be used.
 */
int fci_fd(FCI_CLIENT *client)
{
	(void)*client;
	FCILIB_PRINTF(FCILIB_ERR, "LIBFCI: fci_fd() not implemented\n");
	return -1;
}

/****************************** PRIVATE FUNCTIONS ********************************/
static int32_t fci_command(FCI_CLIENT *client, uint16_t fcode, const uint16_t *cmd_buf, uint16_t cmd_len, uint16_t *rep_buf, uint16_t *rep_len)
{
	fci_msg_t msg;
	fci_msg_t reply_msg;
	int32_t cmd_ret = EOK;

	/* Make sure fci client is registered*/
	if (client == NULL)
	{
		FCILIB_PRINTF(FCILIB_ERR, "LIBFCI: Client is unregistered\n");
		return EINVAL;
	}

	(void)memset(&msg, 0, sizeof(msg));
	(void)memset(&reply_msg, 0, sizeof(reply_msg));

	msg.type = FCI_MSG_CMD;
	msg.msg_cmd.code = fcode;

	if (cmd_len > 0U)
	{
		msg.msg_cmd.length = cmd_len;
		(void)memcpy(&msg.msg_cmd.payload, cmd_buf, cmd_len);
	}

	/* Local sender identification */
	msg.msg_cmd.sender = (uint32_t)PFE_CFG_LOCAL_IF;

	cmd_ret = fci_process_ipc_message(&msg, &reply_msg);

	if(cmd_ret != EOK)
	{
		/*	Command failure */
		FCILIB_PRINTF(FCILIB_ERR, "LIBFCI: Command failed with %d\n", (int_t)cmd_ret);
		return cmd_ret;
	}
	else
	{
		/*	Success, pass reply data (if any) and its length to user */
		if ((NULL != rep_buf) && (NULL != rep_len) && (4U <= reply_msg.msg_cmd.length))
		{
#if (TRUE == FCI_CFG_FORCE_LEGACY_API)
			(void)memcpy(rep_buf, reply_msg.msg_cmd.payload, reply_msg.msg_cmd.length);
			*rep_len = reply_msg.msg_cmd.length;
#else
			(void)memcpy(rep_buf, (reply_msg.msg_cmd.payload + 4U), reply_msg.msg_cmd.length - 4U);
			*rep_len = (uint16_t)(reply_msg.msg_cmd.length) - 4U;
#endif /* FCI_CFG_FORCE_LEGACY_API */
		}
		(void)memcpy(&cmd_ret, reply_msg.msg_cmd.payload, sizeof(uint16_t));

		return cmd_ret;
	}
}

static FCI_CLIENT *fci_create_client(fci_mcast_groups_t group)
{
	FCI_CLIENT *cl;

	cl = oal_mm_malloc(sizeof(FCI_CLIENT));
	if (NULL == cl)
	{
		FCILIB_PRINTF(FCILIB_ERR, "LIBFCI: fci_create_client() failed\n");
		return NULL;
	}
	cl->group = group;

	return cl;
}

static int32_t fci_destroy_client(FCI_CLIENT *client)
{
	FCILIB_PRINTF(FCILIB_CLOSE, "fci_destroy_client()\n");

	oal_mm_free(client);
	client = NULL;

	return 0;
}

#define ETH_43_PFE_STOP_SEC_CODE
#include "Eth_43_PFE_MemMap.h"

#endif /* PFE_CFG_FCI_ENABLE */
