/*
* co_nmtmaster.c - contains NMT master services
*
* Copyright (c) 2014-2023 emotas embedded communication GmbH
*-------------------------------------------------------------------
* $Id: co_nmtmaster.c 51538 2023-12-14 16:48:10Z ro $
*
*
*-------------------------------------------------------------------
*
*
*/

/********************************************************************/
/**
* \brief NMT master services
*
* \file co_nmtmaster.c
* contains NMT master services
*
*/

/* header of standard C - libraries
---------------------------------------------------------------------------*/
#include <string.h>

/* header of project specific types
---------------------------------------------------------------------------*/
#include <gen_define.h>

#ifdef CO_NMT_MASTER

#include <co_datatype.h>
#include <co_drv.h>
#include <co_timer.h>
#include <co_odaccess.h>
#include <co_nmt.h>
#include "ico_cobhandler.h"
#include "ico_event.h"
#include "ico_queue.h"
#include "ico_indication.h"
#include "ico_nmt.h"

/* constant definitions
---------------------------------------------------------------------------*/

/* local defined data types
---------------------------------------------------------------------------*/

/* list of external used functions, if not in headers
---------------------------------------------------------------------------*/

/* list of global defined functions
---------------------------------------------------------------------------*/

/* list of local defined functions
---------------------------------------------------------------------------*/
static void startNmtReq(CO_LINE_DECL, void *ptr);
#ifdef CO_BOOTUP_MANAGER
static UNSIGNED32 *getNmtSlaveAssignment(CO_LINE_DECL, UNSIGNED8 idx);
#endif /* CO_BOOTUP_MANAGER */

/* external variables
---------------------------------------------------------------------------*/

/* global variables
---------------------------------------------------------------------------*/

/* local defined variables
---------------------------------------------------------------------------*/
static UNSIGNED16	nmtInhibit[CO_LINE_ARRAY_DECL] = { 0u };
static UNSIGNED32	nmtStartup[CO_LINE_ARRAY_DECL] = { 0u };
static CO_NMT_MASTER_STATE_T	nmtMasterState[CO_LINE_ARRAY_DECL];
static CO_NMT_STATE_T	requestNmt[CO_LINE_ARRAY_DECL];
static UNSIGNED8	requestNode[CO_LINE_ARRAY_DECL];
static CO_EVENT_T	reQuestNmtEvent[CO_LINE_ARRAY_DECL];
#ifdef CO_BOOTUP_MANAGER
static UNSIGNED32	nmtSlaveAssignment[CO_SLAVE_ASSIGNMENT_CNT];
static UNSIGNED8	nmtSlaveAssignLineCnt[CO_MULTI_LINE_CNT];
static UNSIGNED8	nmtSlaveAssignLineOffs[CO_MULTI_LINE_CNT];
#endif /* CO_BOOTUP_MANAGER */




/***************************************************************************/
/**
*
* \brief coNmtStateReq - request NMT state change
*
* Request NMT state change for the given node 1..127.
* If \e node == 0, the NMT request is sent to all nodes.
* If the NMT sending master should enter the same NMT state
* as the addressed node
* the \e master flag has to be set to CO_TRUE.
* This is true for \e node == 0 too. 
*
* If \e node == the masters own nodeId, the requested state is only valid for the own node.
*
* If the inhibit time is set (see object 0x102a, NMT inhibit time),
* NMT command is not sent if time hasn't been elapsed.
*
* \return RET_T
*
*/
RET_T coNmtStateReq(
		CO_LINE_TYPE	CO_LINE,		/**< can line */
		UNSIGNED8		node,			/**< node */
		CO_NMT_STATE_T	reqState,		/**< new requested state */
		BOOL_T			master			/**< valid for master */
	)
{
UNSIGNED8		trData[CO_CAN_MAX_DATA_LEN];	/* data */
COB_REFERENZ_T	cobRef;
CO_REC_DATA_T	nmtRec;
UNSIGNED8		cmd;
RET_T			retVal;

	/* check for master mode */
	if (nmtMasterState[CO_LINE] == CO_NMT_MASTER_STATE_SLAVE)  {
		return(RET_SERVICE_NOT_INITIALIZED);
	}

	switch (reqState)  {
		case CO_NMT_STATE_OPERATIONAL:
			cmd = 1u;
			break;
		case CO_NMT_STATE_STOPPED:
			cmd = 2u;
			break;
		case CO_NMT_STATE_PREOP:
			cmd = 128u;
			break;
		case CO_NMT_STATE_RESET_NODE:
			cmd = 129u;
			break;
		case CO_NMT_STATE_RESET_COMM:
			cmd = 130u;
			break;
		case CO_NMT_STATE_UNKNOWN:
		default:
			return(RET_INVALID_PARAMETER);
	}

	cobRef = icoNmtGetCob(CO_LINE);

	/* own node requested ? */
	if (node == coNmtGetNodeId(CO_LINE)) {
		master = CO_TRUE;
	} else {
		trData[0] = cmd;
		trData[1] = (node & 0x7Fu);
		retVal = icoTransmitMessage(CO_LINE, cobRef, &trData[0], 0u);
		if (retVal != RET_OK)  {
			return(retVal);
		}
	}

	if (master == CO_TRUE)  {
		/* simulate command for own node */
		nmtRec.msg.canId = 0u;
		nmtRec.msg.len = 2u;
		nmtRec.msg.data[0] = cmd;
		nmtRec.msg.data[1] = coNmtGetNodeId(CO_LINE);
		icoNmtMsgHandler(CO_LINE, &nmtRec);
	}

	return(RET_OK);
}


/***************************************************************************/
/**
*
* \brief icoNmtInhibitActive - check if inhibit is active
*
* \return inhibit state
*
*/
BOOL_T coNmtInhibitActive(
		CO_LINE_TYPE	CO_LINE		/**< can line */
	)
{
COB_REFERENZ_T	cobRef;
BOOL_T	state;

	cobRef = icoNmtGetCob(CO_LINE);
	state = icoQueueInhibitActive(CO_LINE, cobRef);
	
	return(state);
}




/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
/**
* \internal
*
* \brief icoNmtGetObjectAddr - get Nmt object address
*
* \return none
*
*/
void *icoNmtGetObjectAddr(
		CO_LINE_TYPE	CO_LINE,	/* can line */
		UNSIGNED16	index,			/* index */
		UNSIGNED8	subIndex		/* subindex */
	)
{
void	*pAddr = NULL;

	switch (index)  {
		case 0x102au:
			pAddr = (void *)&nmtInhibit[CO_LINE];
			break;
		case 0x1f80u:
			pAddr = (void *)&nmtStartup[CO_LINE];
			break;

#ifdef CO_BOOTUP_MANAGER
		case 0x1f81u:
			pAddr = getNmtSlaveAssignment(CO_LINE, subIndex - 1u);
			break;
#endif /* CO_BOOTUP_MANAGER */

		case 0x1f82u:
			pAddr = (void *)&requestNmt[CO_LINE];
#ifdef CO_HB_CONSUMER_CNT
			requestNmt[CO_LINE] = coNmtGetRemoteNodeState(CO_LINE, subIndex);
#else /* CO_HB_CONSUMER_CNT */
			requestNmt[CO_LINE] = CO_NMT_STATE_UNKNOWN;
			(void)subIndex;
#endif /* CO_HB_CONSUMER_CNT */
			break;

		default:
			break;
	}

	return(pAddr);
}


/***************************************************************************/
/**
* \internal
*
* \brief icoNmtObjChanged - Nmt object changed
*
*
* \return RET_T
*
*/
RET_T icoNmtObjChanged(
		CO_LINE_TYPE	CO_LINE,	/* can line */
		UNSIGNED16	index,			/* index */
		UNSIGNED8	subIndex		/* subindex */
	)
{
COB_REFERENZ_T	cobRef;
RET_T	retVal = RET_OK;

	switch (index)  {
		case 0x102a:
			/* get nmt cob */
			cobRef = icoNmtGetCob(CO_LINE);

			/* setup inhibit time (given in 100 usec) */
			retVal = icoCobSetInhibit(CO_LINE, cobRef, nmtInhibit[CO_LINE]);
			break;
		case 0x1f82:
			switch((UNSIGNED16)requestNmt[CO_LINE]) {
				case CO_NMT_REQ_STATE_RESET_NODE:
					requestNmt[CO_LINE] = CO_NMT_STATE_RESET_NODE;
					break;
				case CO_NMT_REQ_STATE_RESET_COMM:
					requestNmt[CO_LINE] = CO_NMT_STATE_RESET_COMM;
					break;
				default:
					break;
			}
			requestNode[CO_LINE] = subIndex;
			retVal = icoEventStart(CO_LINE, &reQuestNmtEvent[CO_LINE],
				startNmtReq, NULL); /*lint !e960 */
			/* Derogation MisraC2004 R.16.9 function identifier used without '&'
			 * or parenthesized parameter */
			break;
		default:
			break;
	}
	return(retVal);
}


/***************************************************************************/
/**
* \internal
*
* \brief startNmtReq - eventfunction for NMT requests
*
* \return none
*
*/
static void startNmtReq(
		CO_LINE_TYPE	CO_LINE,		/* can line */
		void *ptr
	)
{
(void)ptr;

	(void)coNmtStateReq(CO_LINE, requestNode[CO_LINE],
		requestNmt[CO_LINE], CO_FALSE);
}


/***************************************************************************/
/**
* \internal
*
* \brief icoNmtSetDefaultValue - set predefined connection set
*
* \return none
*
*/
void icoNmtSetDefaultValue(
		CO_LINE_TYPE	CO_LINE		/* can line */
	)
{
RET_T	retVal;
UNSIGNED16	u16;
UNSIGNED32	u32;
#ifdef CO_BOOTUP_MANAGER
UNSIGNED8	i;
UNSIGNED8	subIndex;
#endif /* CO_BOOTUP_MANAGER */

	/* setup reset values */
	retVal = coOdGetDefaultVal_u16(CO_LINE, 0x102au, 0u, &u16);
	if (retVal == RET_OK)  {
		nmtInhibit[CO_LINE] = u16;
	}

	retVal = coOdGetDefaultVal_u32(CO_LINE, 0x1f80u, 0u, &u32);
	if (retVal == RET_OK)  {
		nmtStartup[CO_LINE] = u32;
	}

#ifdef CO_BOOTUP_MANAGER
	for (i = 0u; i < nmtSlaveAssignLineCnt[CO_LINE]; i++)  {
		subIndex = i + 1u;
		retVal = coOdGetDefaultVal_u32(CO_LINE, 0x1f81u, subIndex, &u32);
		if (retVal == RET_OK)  {
			*getNmtSlaveAssignment(CO_LINE, i) = u32;
		}
	}
#endif /* CO_BOOTUP_MANAGER */
}


/***************************************************************************/
/**
* \internal
*
* \brief icoNmtMasterGetStartupObj - get startup object 0x1f80
*
* \return none
*
*/
UNSIGNED32	icoNmtMasterGetStartupObj(
		CO_LINE_TYPE	CO_LINE		/* can line */
	)
{
	return(nmtStartup[CO_LINE]);
}


#ifdef CO_BOOTUP_MANAGER
/***************************************************************************/
/**
* \internal
*
* \brief icoNmtMasterGetSlaveAssignment - get object 0x1f81
*
* \return none
*
*/
UNSIGNED32 icoNmtMasterGetSlaveAssignment(
		CO_LINE_TYPE	CO_LINE,		/* can line */
		UNSIGNED8		idx				/* subindex - 1 */
	)
{
	return(nmtSlaveAssignment[idx + nmtSlaveAssignLineOffs[CO_LINE]]);
}


static UNSIGNED32 *getNmtSlaveAssignment(
		CO_LINE_TYPE	CO_LINE,		/* can line */
		UNSIGNED8		idx
	)
{
UNSIGNED32 *sa;

	sa = &nmtSlaveAssignment[idx + nmtSlaveAssignLineOffs[CO_LINE]];

	return(sa);
}
#endif /* CO_BOOTUP_MANAGER */

/***************************************************************************/
/**
* \internal
*
* \brief icoNmtMasterSetState - set master state
*
* \return none
*
*/
void icoNmtMasterSetState(
		CO_LINE_TYPE	CO_LINE,		/* can line */
		CO_NMT_MASTER_STATE_T mState
	)
{
COB_REFERENZ_T	cobRef;

	nmtMasterState[CO_LINE] = mState;

	/* change cob type NMT */
	cobRef = icoNmtGetCob(CO_LINE);
	if (nmtMasterState[CO_LINE] == CO_NMT_MASTER_STATE_SLAVE)  {
		(void)icoCobChangeType(CO_LINE, cobRef, CO_COB_TYPE_RECEIVE);
	} else {
		(void)icoCobChangeType(CO_LINE, cobRef, CO_COB_TYPE_TRANSMIT);
	}
}


/***************************************************************************/
/**
* \internal
*
* \brief icoNmtMasterGetState - get nmt master state
*
* \return none
*
*/
CO_NMT_MASTER_STATE_T icoNmtMasterGetState(
		CO_LINE_TYPE	CO_LINE		/* can line */
	)
{
	return(nmtMasterState[CO_LINE]);
}


/***************************************************************************/
/**
*
* \brief coNmtNodeIsMaster - detect if node is master
*
* \retval CO_TRUE - node is master
* \retval CO_FALSE - node is not master
*
*/
BOOL_T coNmtNodeIsMaster(
		CO_LINE_TYPE	CO_LINE		/* can line */
	)
{
	if (nmtMasterState[CO_LINE] == CO_NMT_MASTER_STATE_MASTER)  {
		return(CO_TRUE);
	} else {
		return(CO_FALSE);
	}
}


/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
/***************************************************************************/
/**
* \internal
*
* \brief icoNmtMasterVarInit - init nmt variables
*
* \return none
*
*/
void icoNmtMasterVarInit(
		UNSIGNED8	*pSlaveAssign
	)
{
CO_LINE_TYPE	CO_LINE;
#ifdef CO_BOOTUP_MANAGER
UNSIGNED16		offs = 0u;
#endif /* CO_BOOTUP_MANAGER */
(void)pSlaveAssign;

	for (CO_LINE = 0; CO_LINE < CO_MULTI_LINE_CNT; CO_LINE++) 
	{
		nmtInhibit[CO_LINE] = 0u;

#ifdef CO_BOOTUP_MANAGER
		nmtSlaveAssignLineCnt[CO_LINE] = pSlaveAssign[CO_LINE];
		nmtSlaveAssignLineOffs[CO_LINE] = (UNSIGNED8)offs;
		offs += pSlaveAssign[CO_LINE];
#endif /* CO_BOOTUP_MANAGER */
	}
}


/***************************************************************************/
/**
* \internal
*
* \brief icoNmtMasterReset - NMT master reset
*
*
* \return void
*
*/
void icoNmtMasterReset(
		CO_LINE_TYPE	CO_LINE		/* can line */
	)
{
COB_REFERENZ_T	cobRef;

	/* get nmt cob */
	cobRef = icoNmtGetCob(CO_LINE);

	/* setup inhibit time (given in 100 usec) */
	(void)icoCobSetInhibit(CO_LINE, cobRef, nmtInhibit[CO_LINE]);
}

#endif /* CO_NMT_MASTER */

