/*
* iso_tp.c - contains iso-tp routines
*
* Copyright (c) 2018-2024 emotas embedded communication GmbH
*-------------------------------------------------------------------------------
* $Id: iso_tp.c 53810 2024-05-17 12:55:23Z boe $
*
*
*-------------------------------------------------------------------------------
*
*
*/


/******************************************************************************/
/**
* \brief iso-tp implementation - iso 15765-2
*
* \file iso_tp.c
* contains iso-tp routines
*
* \section i Introduction
* The ISO-TP Stack of emotas is a software library
* that provides all communication services
* depending on ISO-15765-2-ISO-TP_2024
* 
* At the moment, the user interface allowed only 16-bit buffers,
* so the maximal data transfer is limited to 65565 bytes.
*
* The main features are:
*	- well-defined interface between driver and iso-tp stack
*	- ANSI-C conform
*	- MISRA checked
*	- easy-to-handle Application Programming Interface
*	- configurable and scalable
*
* This reference manual describes the functions
* for the API to evaluate the received data
* and to use the iso-tp services in the network.
*
* Configuration and features settings are supported
* by the graphical configuration tool ISO-TP DeviceDesigner.
*
* \section g General
* The iso-tp stack uses only data pointers from the application, so the
* application has to ensure that all the data pointers are valid and protected
* during runtime.
*
* \section u Using ISO TP in an application
* At startup, some initialization functions are necessary:
* 	- codrvHardwareInit()	- generic, CAN related hardware initialization
* 	- codrvCanInit()	- initialize CAN driver (Classical Mode)
* 	- codrvCanfdInit()	- initialize CAN driver (FD-Mode)
* 	- coCanOpenStackInit()	- initialize CANopen and ISO TP functionality
* 	- codrvTimerSetup()	- initialize hardware timer
* 	- codrvCanEnable()	- start CAN communication
*
* For the CANopen/J1939 functionality,
* the central function
* coCommTask() or j1939CommTask()
* has to be called in case of
*	- new CAN message was received
*	- timer period has been ellapsed.
*
* Therefore signal handlers should be used
* or a cyclic call of the function coCommTask() is necessary.
* For operating systems (like LINUX) the function
* codrvWaitForEvent()
* can be used to wait for events.
* <br>All CANopen functionality is handled inside this function.
*
* The start of CANopen services are also possible.
*
* \section c Indication functions
* Indication functions inform application about CAN and ISO-TP service events.
* <br>To receive an indication,
* the application has to register a function
* by the apropriate service register function like isoTpEventRegister_CLIENT().
* <br>Every time the event occures,
* the registered indication function is called.
n*/

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

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

#ifdef ISOTP_SUPPORTED
#include <co_datatype.h>
#include <co_drv.h>
#include <co_timer.h>
#include <co_memcpy.h>
#include <iso_tp.h>
#include "ico_cobhandler.h"
#include "ico_queue.h"
#include "ico_event.h"
#include "iiso_tp.h"

/* constant definitions
------------------------------------------------------------------------------*/
#ifndef ISOTP_MAX_DATA_SIZE
#define ISOTP_MAX_DATA_SIZE (4u * 1024u)
#endif /* ISOTP_MAX_DATA_SIZE */

#ifndef ISOTP_MAX_BLOCK_SIZE
# define ISOTP_MAX_BLOCK_SIZE	20u
#endif /* ISOTP_MAX_BLOCK_SIZE */

#ifndef ISOTP_BLOCK_TIMEOUT
# define ISOTP_BLOCK_TIMEOUT	2u
#endif /* ISOTP_BLOCK_TIMEOUT */

#define ISOTP_FRAME_MASK			0xf0u
#define ISOTP_FRAME_SINGLE			0x00u
#define ISOTP_FRAME_FIRST			0x10u
#define ISOTP_FRAME_CONSECUTIVE		0x20u
#define ISOTP_FRAME_FLOW_CONTROL	0x30u

#define ISOTP_PRIORITY	(0x6ul << 26)

#ifndef ISOTP_FILL_BYTE
# define ISOTP_FILL_BYTE 0xccu
#endif /* ISOTP_FILL_BYTE */

#ifndef ISOTP_TIMER_N_CR
# define ISOTP_TIMER_N_CR 1000ul  /**< time until reception of the next Consecutive Frame N_PDU  (in ms) */
#endif /* ISOTP_TIMER_N_CR */

#ifndef ISOTP_TIMER_N_BS
# define ISOTP_TIMER_N_BS 1000ul  /**< time until reception of the next FlowControl N_PDU  (in ms) */
#endif /* ISOTP_TIMER_N_BS */

/* local defined data types
------------------------------------------------------------------------------*/
typedef struct  {
	CO_EVENT_T			event;
	CO_TIMER_T			recTimer;
	CO_TIMER_T			sendTimer;
	COB_REFERENZ_T			trCob;
	COB_REFERENZ_T			recCob;
	ISOTP_STATE_T			state;
	UNSIGNED16				expectedSize;
	UNSIGNED16				transferedSize;
#ifdef ISOTP_EVENT_SERVER_DATA_SPLIT
	UNSIGNED16				splitTransSize;
#endif /* ISOTP_EVENT_SERVER_DATA_SPLIT */
	UNSIGNED16				recMaxSize;
	UNSIGNED16				blockCnt;
	UNSIGNED8				*pRecData;
	UNSIGNED8				*pData;
	UNSIGNED8				seqNbr;
	UNSIGNED8				localAddr;
	UNSIGNED8				remoteAddr;
	UNSIGNED8				sendCycle;
	UNSIGNED8				recTimeout;
	ISO_TP_ADDRESS_T		type;
	ISO_TP_FORMAT_T			formatType;
#ifdef ISOTP_CLIENT_CONNECTION_CNT
	ISOTP_MAP_FCT			mapFct;
	void					*fctPara;
#endif /* ISOTP_CLIENT_CONNECTION_CNT */
} ISO_TP_CONN_T;


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

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

/* list of local defined functions
------------------------------------------------------------------------------*/
static void reqFlowControl(CO_LINE_DECL, ISO_TP_CONN_T *pIsoTp);
static void recDataMsg(CO_LINE_DECL, ISO_TP_CONN_T *pIsoTp,
		const CO_REC_DATA_T	*pRecData);
static void sendDataMsg(CO_LINE_DECL, void *pData);

static void recFlowControl(CO_LINE_DECL, ISO_TP_CONN_T *pIsoTp,
		const CO_REC_DATA_T *pRecData);

static RET_T serverUserHandler(CO_LINE_DECL, ISO_TP_CONN_T *pIsoTp);
static RET_T clientUserHandler(CO_LINE_DECL, ISO_TP_CONN_T *pIsoTp, RET_T result);
static RET_T clientTxAckUserHandler(CO_LINE_DECL, ISO_TP_CONN_T* pIsoTp,
		UNSIGNED8* pData, UNSIGNED16 size);

static ISO_TP_CONN_T *getChannel(CO_LINE_DECL, UNSIGNED16	idx);
static ISO_TP_CONN_T *searchChannelRemoteAddr(CO_LINE_DECL, UNSIGNED8 addr);
static ISO_TP_CONN_T *searchChannelLocalAddr(CO_LINE_DECL, UNSIGNED8 addr);
static ISO_TP_CONN_T *searchChannelCobRef(CO_LINE_DECL, UNSIGNED16 cobRef);


static void serverChannelTimeout(CO_LINE_DECL, void *pData);
static void clientChannelTimeout(CO_LINE_DECL, void *pData);

static UNSIGNED32 calculateId(UNSIGNED8 destAddr, UNSIGNED8 srcAddr,
		ISO_TP_ADDRESS_T addrType);

#ifdef ISOTP_CLIENT_CONNECTION_CNT
static void transmitMapFct(ISO_TP_CONN_T *pIsoTp, void *pDst,
		UNSIGNED16 srcOffset, UNSIGNED16 size);
static RET_T clientChannelReset(CO_LINE_DECL, ISO_TP_CONN_T *pIsoTp);
#endif /* ISOTP_CLIENT_CONNECTION_CNT */
static RET_T serverChannelReset(CO_LINE_DECL, ISO_TP_CONN_T *pIsoTp);

static ISO_TP_CONN_T *getAlternativeServerChannel(CO_LINE_DECL, ISO_TP_CONN_T *pIsoTp);

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

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

/* local defined variables
------------------------------------------------------------------------------*/
#ifdef ISOTP_SERVER_CONNECTION_CNT
static ISO_TP_CONN_T isotpChannel[ISOTP_SERVER_CONNECTION_CNT];
static UNSIGNED16 isotpChannelCnt[CO_LINE_ARRAY_DECL] = {0u};
static UNSIGNED16	isotpLineCnt[CO_LINE_ARRAY_DECL];
static UNSIGNED16	isotpLineOffs[CO_LINE_ARRAY_DECL];

# ifdef ISOTP_EVENT_DYNAMIC_SERVER
static ISOTP_EVENT_SERVER_T	isotpServerTable[ISOTP_EVENT_DYNAMIC_SERVER];
static UNSIGNED16	isotpServerTableCnt = {0u};
# endif /* ISOTP_EVENT_DYNAMIC_SERVER */

# ifdef ISOTP_EVENT_DYNAMIC_SSPLIT
static ISOTP_EVENT_SERVER_SPLIT_T isotpServerSplitTable[ISOTP_EVENT_DYNAMIC_SSPLIT];
static UNSIGNED16 isotpServSplitTableCnt = {0u};
# endif /* ISOTP_EVENT_DYNAMIC_SSPLIT */
#endif /* ISOTP_SERVER_CONNECTION_CNT */


#ifdef ISOTP_CLIENT_CONNECTION_CNT
# ifdef ISOTP_EVENT_DYNAMIC_CLIENT
static ISOTP_EVENT_CLIENT_T	isotpClientTable[ISOTP_EVENT_DYNAMIC_CLIENT];
static UNSIGNED16 isotpClientTableCnt = {0u};
# endif /* ISOTP_EVENT_DYNAMIC_CLIENT */

# ifdef ISOTP_EVENT_DYNAMIC_CLIENT_TXACK
static ISOTP_EVENT_CLIENT_TXACK_T	isotpClientTxAckTable[ISOTP_EVENT_DYNAMIC_CLIENT_TXACK];
static UNSIGNED16 isotpClientTxAckTableCnt = {0u};
# endif /* ISOTP_EVENT_DYNAMIC_CLIENT_TXACK */
#endif /* ISOTP_CLIENT_CONNECTION_CNT */

static ISO_TP_ADDRESS_T currentType[CO_LINE_ARRAY_DECL] = {ISOTP_ADDRESS_UNKNOWN};



/* implementation
------------------------------------------------------------------------------*/

#ifdef ISOTP_CLIENT_CONNECTION_CNT
/***************************************************************************/
/**
* \internal
*
* \brief defaultMapFct - default mapping function
*
*/
static void transmitMapFct(
		ISO_TP_CONN_T	*pIsoTp,
		void *pDest,
		UNSIGNED16 srcOffset,
		UNSIGNED16 size
	)
{

	if (pIsoTp->mapFct == NULL)  {
		(void)coreMemcpy(pDest, &pIsoTp->pData[srcOffset], size, 0u);
	} else {
		pIsoTp->mapFct(pDest, srcOffset, size, pIsoTp->fctPara);
	}
}


/***************************************************************************/
/**
* \brief isoTpSendReq - send isotp request
*
* This function transmits the given data to the destAddr.
* The isotp channel has to be initialized before.
* The data len is limited to 65565 bytes at the moment.
*
* If pData is NULL, then the mapping function has to be given.
*
* Please note - the data has to be valid until transfer was finished.
*
* \return RET_T
*
*/
RET_T isoTpSendReq(
		CO_LINE_TYPE	CO_LINE,			/**< can line */
		UNSIGNED8	destAddr,		/**< destination address */
		UNSIGNED8	*pData,			/**< pointer to transmit data */
		UNSIGNED16	dataLen,		/**< length of transmit data */
		ISOTP_MAP_FCT mapFct,		/**< pointer for mapping function if pData=NULL */
		void 		*mapFctPara,	/**< pointer for mapping function parameter */
		UNSIGNED8	flags			/**< flags for inhibit/indicaton */
	)
{
RET_T			retVal;
ISO_TP_CONN_T	*pIsoTp = NULL;
UNSIGNED8		trData[CO_CAN_MAX_DATA_LEN];
UNSIGNED16		trDataMaxLen;
UNSIGNED8		extFormatOffs = 0u;
UNSIGNED8		offs;

	if ((pData == NULL) && (mapFct == NULL))  {
		return(RET_HARDWARE_ERROR);
	}

	pIsoTp = searchChannelRemoteAddr(CO_LINE, destAddr);
	if (pIsoTp == NULL)  {
		return(RET_EVENT_NO_RESSOURCE);
	}

	if (pIsoTp->state != ISOTP_STATE_FREE)  {
		return(RET_SERVICE_BUSY);
	}

	/* extended format needs 2 bytes */
	if (pIsoTp->formatType == ISOTP_FORMAT_EXTENDED)  {
		extFormatOffs = 1u;
	}

	if (pIsoTp->type == ISOTP_ADDRESS_FUNC)  {
		if (dataLen > (CO_CAN_MAX_DATA_LEN - (1u + extFormatOffs)))  {
			return(RET_DATA_TYPE_MISMATCH);
		}
	}

	if (pData == NULL)  {
		pIsoTp->mapFct = mapFct;
		pIsoTp->fctPara = mapFctPara;
	} else {
		pIsoTp->mapFct = NULL;
		pIsoTp->pData = pData;
	}

	/* fill complete buffer with fillbyte */
	MEMSET(trData, ISOTP_FILL_BYTE, CO_CAN_MAX_DATA_LEN);

	/* extended addressing - first byte is dest address */
	if (pIsoTp->formatType == ISOTP_FORMAT_EXTENDED)  {
		trData[0] = destAddr;
	}

	offs = 1u + extFormatOffs;
	trDataMaxLen = CO_CAN_CC_MAX_DATA_LEN;

	if (dataLen > (trDataMaxLen - offs))  {
		/* segmented first frame */
		pIsoTp->expectedSize = dataLen;
		pIsoTp->seqNbr = 0u;

		if (dataLen > ((4u * 1024u) -1u))  {
			/* at the moment, we don't use 32 bit function parameter... */
			UNSIGNED32	extDataLen = (UNSIGNED32)dataLen;
			trData[extFormatOffs] = 0u;
			trData[1u + extFormatOffs] = 0u;
			trData[2u + extFormatOffs] = (extDataLen >> 24) & 0xffu;
			trData[3u + extFormatOffs] = (extDataLen >> 16) & 0xffu;
			trData[4u + extFormatOffs] = (extDataLen >> 8) & 0xffu;
			trData[5u + extFormatOffs] = extDataLen & 0xffu;

			transmitMapFct(pIsoTp, &trData[6u + extFormatOffs], 0u,
				trDataMaxLen - (6u + extFormatOffs));

			pIsoTp->transferedSize = trDataMaxLen - (6u + extFormatOffs);
		} else {
			/* data len low */
			trData[1u + extFormatOffs] = (UNSIGNED8)(dataLen & 0xffu);
			dataLen >>= 8u;
			/* data len high */
			trData[extFormatOffs] = (UNSIGNED8)(dataLen & 0xffu);

			transmitMapFct(pIsoTp, &trData[2u + extFormatOffs], 0u,
				trDataMaxLen - (2u + extFormatOffs));

			pIsoTp->transferedSize = trDataMaxLen - (2u + extFormatOffs);
		}

		/* set first frame */
		trData[extFormatOffs] |= ISOTP_FRAME_FIRST;

		(void)icoCobSetLen(CO_LINE, pIsoTp->trCob, trDataMaxLen);
		retVal = icoTransmitMessage(CO_LINE, pIsoTp->trCob, &trData[0], 0u);

		pIsoTp->state = ISOTP_STATE_CLIENT_WAIT_CTS;
		(void)coTimerStart(CO_LINE, &pIsoTp->recTimer, ISOTP_TIMER_N_BS * 1000ul,
				clientChannelTimeout, (void *)pIsoTp, CO_TIMER_ATTR_ROUNDUP);
	} else {
		/* single frame */
		trData[extFormatOffs] = ISOTP_FRAME_SINGLE;

		/* save data len */
		trData[extFormatOffs] |= (UNSIGNED8)dataLen & 0x0fu;

		transmitMapFct(pIsoTp, &trData[offs], 0u, dataLen);

		retVal = icoTransmitMessage(CO_LINE, pIsoTp->trCob, &trData[0], flags);

		(void)clientChannelReset(CO_LINE, pIsoTp);
		(void)clientUserHandler(CO_LINE, pIsoTp, retVal);
	}

	return(retVal);
}


/***************************************************************************/
/**
* \brief isoTpGetTxMaxPayloadSize - get maximum payload size
*
* This function returns the maximum payload size which can be send with
* the given ISO-TP frame type.
*
* \return RET_T
*
*/
RET_T isoTpGetTxMaxPayloadSize(
		CO_LINE_TYPE	CO_LINE,			/**< can line */
		UNSIGNED8	destAddr,			/**< destination address */
		ISO_TP_FRAME_TYPE_T frameType,	/**< frame type */
		UNSIGNED32	*pSize				/**< pointer for result */
	)
{
ISO_TP_CONN_T	*pIsoTp = NULL;
UNSIGNED8		extFormatOffs = 0u;

	if (pSize == NULL)  {
		return(RET_INVALID_PARAMETER);
	}

	pIsoTp = searchChannelRemoteAddr(CO_LINE, destAddr);
	if (pIsoTp == NULL)  {
		return(RET_EVENT_NO_RESSOURCE);
	}

	if (pIsoTp->formatType == ISOTP_FORMAT_EXTENDED)  {
		extFormatOffs = 1u;
	}

	if (frameType == ISOTP_FRAME_TYPE_SF)  {
		*pSize = CO_CAN_MAX_DATA_LEN - 1u;
		*pSize -= extFormatOffs;
	} else {
		return(RET_INVALID_PARAMETER);
	}

	return(RET_OK);
}


/***************************************************************************/
/**
* \brief isoTpAbortTransfer - abort client transfer
*
* This function aborts a running tranfser to the destAddr.
*
*
*/
void isoTpAbortTransfer(
		CO_LINE_TYPE	CO_LINE,			/**< can line */
		UNSIGNED8	destAddr		/**< destination address */
	)
{
ISO_TP_CONN_T	*pIsoTp = NULL;

	pIsoTp = searchChannelRemoteAddr(CO_LINE, destAddr);
	if (pIsoTp == NULL)  {
		return;
	}

	(void)clientChannelReset(CO_LINE, pIsoTp);
}
#endif /* ISOTP_CLIENT_CONNECTION_CNT */


/***************************************************************************/
/**
* \brief isoTpSetRecDataPtr - set ISO-TP receive pointer
*
* This function sets the adress and the size of a receive connection.<br>
* At the initialization time, there are no receive buffers.
* This has to be done by this function.
*
* \return RET_T
*
*/
RET_T isoTpSetRecDataPtr(
		CO_LINE_TYPE		CO_LINE,		/**< can line */
		ISO_TP_DATAPTR_T direction,			/**< direction */
		UNSIGNED8	destAddr,				/**< destination address */
		UNSIGNED8	*pRecData,				/**< pointer for receive data */
		UNSIGNED16	size					/**< max len of data */
	)
{
ISO_TP_CONN_T	*pIsoTp = NULL;

	if (size > ISOTP_MAX_DATA_SIZE)  {
		return(RET_INVALID_PARAMETER);
	}

	if (direction == ISOTP_DATAPTR_SERVER)  {
#ifdef ISOTP_SERVER_CONNECTION_CNT
		pIsoTp = searchChannelLocalAddr(CO_LINE, destAddr);
	} else {
#else /* ISOTP_SERVER_CONNECTION_CNT */
		return(RET_INVALID_PARAMETER);
#endif /* ISOTP_SERVER_CONNECTION_CNT */
	}

	if (pIsoTp == NULL)  {
		return(RET_EVENT_NO_RESSOURCE);
	}

	pIsoTp->pRecData = pRecData;
	pIsoTp->recMaxSize = size;

	return(RET_OK);
}


#ifdef ISOTP_SERVER_CONNECTION_CNT
/******************************************************************************/
/**
*  \internal
*
* \brief isoTpMessageHandler - handles all iso-tp messages
*
*
*/
void iisoTpMessageHandler(
		CO_LINE_TYPE			CO_LINE,		/* can line */
		const CO_REC_DATA_T	*pRecData		/* pointer to received data */
	)
{
ISO_TP_CONN_T *pIsoTp;
UNSIGNED32	respId;
UNSIGNED16	trDataMaxLen;
UNSIGNED8	command;
UNSIGNED8	extFormatOffs = 0u;
UNSIGNED8	dstAddr = 0u;
UNSIGNED8	offs;

	/* check max service index */
	if (pRecData->spec >= isotpChannelCnt[CO_LINE])  {
		return;
	}
	pIsoTp = getChannel(CO_LINE, pRecData->spec);

	/* extended format ?*/
	if (pIsoTp->formatType == ISOTP_FORMAT_EXTENDED)  {
		if (pRecData->msg.data[0u] != pIsoTp->localAddr)  {
			/* look for alternative channel (functional or physical address mode) */
			pIsoTp = getAlternativeServerChannel(CO_LINE, pIsoTp);
			if (pIsoTp == NULL)  {
				/* nothing found */
				return;
			}
			/* check for correct local address */
			if (pRecData->msg.data[0u] != pIsoTp->localAddr)  {
				return;
			}
		}

		extFormatOffs = 1u;
	}

	/* normal fixed ? */
	if (pIsoTp->formatType == ISOTP_FORMAT_NORMAL_FIXED)  {
		/* check local address */
		dstAddr = (pRecData->msg.canId >> 8) & 0xffu;
		if ((dstAddr != pIsoTp->localAddr) && (dstAddr != 0xff))  {
			return;
		}
		pIsoTp->remoteAddr = pRecData->msg.canId & 0xffu;
	}


	/* the command is the most left nibble */
	command = pRecData->msg.data[extFormatOffs] & ISOTP_FRAME_MASK;

	trDataMaxLen = CO_CAN_CC_MAX_DATA_LEN;

	switch (command)  {
	case ISOTP_FRAME_SINGLE:
		if (pIsoTp->pRecData == NULL)  {
			return;
		}

		/* if segmented transfer is in progess, abort it */
		if (pIsoTp->state == ISOTP_STATE_SERVER_WAIT_DATA)  {
			serverChannelReset(CO_LINE, pIsoTp);
		}

		/* ignore messages when receive is active */
		if (pIsoTp->state != ISOTP_STATE_FREE)  {
			return;
		}

		{
			offs = extFormatOffs;
		}

		if (pRecData->msg.data[offs] > (trDataMaxLen - (1u + offs)))  {
			return;
		}

		/* check for correct dlc value */
		if (pRecData->msg.len < (pRecData->msg.data[offs] + offs + 1u))  {
			return;
		}

		/* ensure iso-tp len is != 0 */
		if (pRecData->msg.data[offs] == 0u)  {
			return;
		}

		if (pIsoTp->recMaxSize >= pRecData->msg.data[offs])  {
			MEMCPY(pIsoTp->pRecData, &(pRecData->msg.data[1u + offs]),
				pRecData->msg.data[offs]);
			pIsoTp->transferedSize = pRecData->msg.data[offs];
			pIsoTp->expectedSize = pRecData->msg.data[offs];
			/* call user indication */
			serverUserHandler(CO_LINE, pIsoTp);
		}
		break;

	case ISOTP_FRAME_FIRST:
		if (pIsoTp->type == ISOTP_ADDRESS_FUNC)  {
			return;
		}

		/* if segmented transfer is in progess, abort it */
		if (pIsoTp->state == ISOTP_STATE_SERVER_WAIT_DATA)  {
			serverChannelReset(CO_LINE, pIsoTp);
		}

		/* ignore messages when receive is already active */
		if (pIsoTp->state != ISOTP_STATE_FREE)  {
			return;
		}

		/* ignore messages with len < CAN-frame len */
		if (pRecData->msg.len != CO_CAN_MAX_DATA_LEN) {
			return;
		} 

		pIsoTp->expectedSize = pRecData->msg.data[extFormatOffs] & 0x0fu;
		pIsoTp->expectedSize = pIsoTp->expectedSize << 8u;
		pIsoTp->expectedSize |= pRecData->msg.data[extFormatOffs + 1u];

		/* > 4095 bytes - 32bit size info */
		if (pIsoTp->expectedSize == 0u)  {
			UNSIGNED32 expectedSize = 0u;
			expectedSize = pRecData->msg.data[extFormatOffs + 2u];
			expectedSize <<= 8;
			expectedSize |= pRecData->msg.data[extFormatOffs + 3u];
			expectedSize <<= 8;
			expectedSize |= pRecData->msg.data[extFormatOffs + 4u];
			expectedSize <<= 8;
			expectedSize |= pRecData->msg.data[extFormatOffs + 5u];
			pIsoTp->expectedSize = (UNSIGNED16)(expectedSize & 0xffffu);

			offs = 6u + extFormatOffs;
		} else
		{
			offs = 2u + extFormatOffs;
		}

		/* if size == 0 -> return */
		if (pIsoTp->expectedSize == 0u)  {
			return;
		}

		/* buffer size ok ? */
		if (pIsoTp->expectedSize > pIsoTp->recMaxSize)  {
			/* ignore in case we cannot handle the size */
			return;
		}

		pIsoTp->transferedSize = trDataMaxLen - offs;
		MEMCPY(pIsoTp->pRecData, &(pRecData->msg.data[offs]),
				trDataMaxLen - offs);

		pIsoTp->seqNbr = 1u;
		pIsoTp->blockCnt = 0u;

#ifdef ISOTP_EVENT_SERVER_DATA_SPLIT
		pIsoTp->splitTransSize = 6u;
#endif /* ISOTP_EVENT_SERVER_DATA_SPLIT */

		pIsoTp->state = ISOTP_STATE_SERVER_WAIT_DATA;

		/* reconfigure COB-ID for response (flow control) already */
		if (pIsoTp->formatType == ISOTP_FORMAT_NORMAL_FIXED)  {
			respId = calculateId(pIsoTp->remoteAddr, dstAddr, pIsoTp->type);
			(void)icoCobSet(CO_LINE, pIsoTp->trCob, respId, CO_COB_RTR_NONE,
				(UNSIGNED8)trDataMaxLen);
		}

		/* call flow control request */
		reqFlowControl(CO_LINE, pIsoTp);
		break;

	case ISOTP_FRAME_CONSECUTIVE:
		recDataMsg(CO_LINE, pIsoTp, pRecData);
		break;

	case ISOTP_FRAME_FLOW_CONTROL:
		recFlowControl(CO_LINE, pIsoTp, pRecData);
		break;

	default:
		break;
	}
}
#endif /* ISOTP_SERVER_CONNECTION_CNT */


/******************************************************************************/
#ifdef ISOTP_CLIENT_CONNECTION_CNT
RET_T iisoTpClientTxAck(
		CO_LINE_TYPE			CO_LINE,		/* can line */
		COB_REFERENZ_T cobRef,
		CO_CAN_TR_MSG_T* pMsg
	)
{
ISO_TP_CONN_T* pIsoTp = NULL;
UNSIGNED8	extFormatOffs = 0u;

	pIsoTp = searchChannelCobRef(CO_LINE, cobRef);
	if (pIsoTp == NULL)  {
		return(RET_EVENT_NO_RESSOURCE);
	}

	/* extended format ?*/
	if (pIsoTp->formatType == ISOTP_FORMAT_EXTENDED)  {
		extFormatOffs = 1u;
	}

	if (pMsg->data[extFormatOffs] > (CO_CAN_MAX_DATA_LEN - 1u))  {
		return(RET_DATA_TYPE_MISMATCH);
	}

	(void)clientTxAckUserHandler(CO_LINE, pIsoTp, &pMsg->data[extFormatOffs + 1u], pMsg->data[extFormatOffs]);

	return(RET_OK);
}
#endif /* ISOTP_CLIENT_CONNECTION_CNT */


/******************************************************************************/
/**
*  \internal
*
* \brief recDataMsg - handles all received data messages
*
*
*/
static void recDataMsg(
		CO_LINE_TYPE	CO_LINE,
		ISO_TP_CONN_T *pIsoTp,
		const CO_REC_DATA_T	*pRecData
	)
{
#ifdef ISOTP_EVENT_SERVER_DATA_SPLIT
UNSIGNED16 splitCnt;
#endif /* ISOTP_EVENT_SERVER_DATA_SPLIT */
UNSIGNED8	extFormatOffs = 0u;
UNSIGNED16	recDataLen;		/* len of data (without header data) */

	/* check correct state */
	if (pIsoTp->state != ISOTP_STATE_SERVER_WAIT_DATA)  {
		return;
	}

	(void)coTimerStop(CO_LINE, &pIsoTp->recTimer);

	/* extended format ?*/
	if (pIsoTp->formatType == ISOTP_FORMAT_EXTENDED)  {
		extFormatOffs = 1u;
	}

	if ((pRecData->msg.data[extFormatOffs] & 0x0fu) != pIsoTp->seqNbr)  {
		pIsoTp->state = ISOTP_STATE_FREE;
		return;
	}

	pIsoTp->seqNbr++;
	pIsoTp->seqNbr &= 0x0fu;

	pIsoTp->blockCnt++;

	recDataLen = pRecData->msg.len - (1u + extFormatOffs);

	/* data len have to be max - except last message */
	{
		if ((pRecData->msg.len != CO_CAN_CC_MAX_DATA_LEN)
		 && ((pIsoTp->expectedSize - pIsoTp->transferedSize) > recDataLen))  {
			return;
		}
	}


#ifdef ISOTP_EVENT_SERVER_DATA_SPLIT
	if ((pIsoTp->splitTransSize + (7u + extFormatOffs)) >= ISOTP_SERVER_DATA_SPLIT_CNT)  {

		splitCnt = (pIsoTp->splitTransSize + (7u + extFormatOffs)) - ISOTP_SERVER_DATA_SPLIT_CNT;
		splitCnt = (CO_CAN_MAX_DATA_LEN - (1u + extFormatOffs)) - splitCnt;

		MEMCPY(&pIsoTp->pRecData[pIsoTp->splitTransSize], &pRecData->msg.data[1u + extFormatOffs], splitCnt);
		pIsoTp->splitTransSize += splitCnt;
		pIsoTp->transferedSize += splitCnt;
		(void)serverUserHandler(CO_LINE, pIsoTp);
		pIsoTp->splitTransSize = 0u;

		splitCnt = (CO_CAN_MAX_DATA_LEN - (1u + extFormatOffs)) - splitCnt;
		MEMCPY(&pIsoTp->pRecData[pIsoTp->splitTransSize], &pRecData->msg.data[CO_CAN_MAX_DATA_LEN - splitCnt], splitCnt);
		pIsoTp->splitTransSize += splitCnt;
		pIsoTp->transferedSize += splitCnt;

	} else {
		MEMCPY(&pIsoTp->pRecData[pIsoTp->splitTransSize], &pRecData->msg.data[1u], CO_CAN_MAX_DATA_LEN - 1u);
		pIsoTp->transferedSize += (CO_CAN_MAX_DATA_LEN - 1u);
		pIsoTp->splitTransSize += (CO_CAN_MAX_DATA_LEN - 1u);
	}

	if (pIsoTp->transferedSize >= pIsoTp->expectedSize)  {
		pIsoTp->splitTransSize = pIsoTp->expectedSize % ISOTP_SERVER_DATA_SPLIT_CNT;
	}
#else /* ISOTP_EVENT_SERVER_DATA_SPLIT */
	if ((pIsoTp->expectedSize - pIsoTp->transferedSize) > recDataLen)  {
		MEMCPY(&pIsoTp->pRecData[pIsoTp->transferedSize], &pRecData->msg.data[1u + extFormatOffs], recDataLen);
		pIsoTp->transferedSize += recDataLen;
	} else {
		MEMCPY(&pIsoTp->pRecData[pIsoTp->transferedSize], &pRecData->msg.data[1u + extFormatOffs],
				pIsoTp->expectedSize - pIsoTp->transferedSize);
		pIsoTp->transferedSize += (pIsoTp->expectedSize - pIsoTp->transferedSize);
	}
#endif /* ISOTP_EVENT_SERVER_DATA_SPLIT */

	if (pIsoTp->transferedSize >= pIsoTp->expectedSize)  {

		pIsoTp->state = ISOTP_STATE_FREE;

		pIsoTp->transferedSize = pIsoTp->expectedSize;
		/* call user indication */
		(void)serverUserHandler(CO_LINE, pIsoTp);
		return;
	}
	if (pIsoTp->blockCnt >= ISOTP_MAX_BLOCK_SIZE)  {
		reqFlowControl(CO_LINE, pIsoTp);
		pIsoTp->blockCnt = 0u;
	} else {
		(void)coTimerStart(CO_LINE, &pIsoTp->recTimer, ISOTP_TIMER_N_CR * 1000ul,
				serverChannelTimeout, (void *)pIsoTp, CO_TIMER_ATTR_ROUNDUP);
	}
}


/******************************************************************************/
/**
*  \internal
*
* \brief reqFlowControl - sends flow control message
*
*
*/
static void reqFlowControl(
		CO_LINE_TYPE	CO_LINE,
		ISO_TP_CONN_T *pIsoTp
	)
{
UNSIGNED8	trData[CO_CAN_MAX_DATA_LEN];
UNSIGNED8	extFormatOffs = 0u;

	MEMSET(trData, ISOTP_FILL_BYTE, CO_CAN_MAX_DATA_LEN);

	/* extended format ?*/
	if (pIsoTp->formatType == ISOTP_FORMAT_EXTENDED)  {
		trData[0u] = pIsoTp->remoteAddr;
		extFormatOffs = 1u;
	}

	trData[extFormatOffs] = ISOTP_FRAME_FLOW_CONTROL;
	if (pIsoTp->expectedSize > pIsoTp->recMaxSize)  {
		trData[extFormatOffs] |= (UNSIGNED8)2u;
	}

	trData[extFormatOffs + 1u] = ISOTP_MAX_BLOCK_SIZE;
	trData[extFormatOffs + 2u] = ISOTP_BLOCK_TIMEOUT;


	(void)icoTransmitMessage(CO_LINE, pIsoTp->trCob, &trData[0], 0u);

	(void)coTimerStart(CO_LINE, &pIsoTp->recTimer, ISOTP_TIMER_N_CR * 1000ul,
			serverChannelTimeout, (void *)pIsoTp, CO_TIMER_ATTR_ROUNDUP);
}


/******************************************************************************/
/**
*  \internal
*
* \brief recFlowControl - handles received flow control
*
*
*/
static void recFlowControl(
		CO_LINE_TYPE	CO_LINE,
		ISO_TP_CONN_T	*pIsoTp,
		const CO_REC_DATA_T	*pRecData
	)
{
UNSIGNED8 command;
UNSIGNED8 extFormatOffs = 0u;

	/* extended format ?*/
	if (pIsoTp->formatType == ISOTP_FORMAT_EXTENDED)  {
		extFormatOffs = 1u;
	}

	/* check correct data len */
	if (pRecData->msg.len < (3u + extFormatOffs)) {
		return;
	}

	/* ignore unexpected flow controls */
	if (pIsoTp->state != ISOTP_STATE_CLIENT_WAIT_CTS)  {
		return;
	}

	(void)coTimerStop(CO_LINE, &pIsoTp->recTimer);

	pIsoTp->sendCycle = pRecData->msg.data[2u + extFormatOffs];
	pIsoTp->blockCnt = pRecData->msg.data[1u + extFormatOffs];

	if (pIsoTp->blockCnt == 0u)  {
		pIsoTp->blockCnt = 0xffffu;
	}

	command = pRecData->msg.data[extFormatOffs];
	command = command & 0x0fu;

	switch(command)  {
	case 0x00u: /* Clear To Send */
		pIsoTp->state = ISOTP_STATE_CLIENT_TRANSMITTING;
		if (pIsoTp->sendCycle == 0u)  {
			sendDataMsg(CO_LINE, pIsoTp);
		} else {
			(void)coTimerStart(CO_LINE, &pIsoTp->sendTimer,
					(UNSIGNED32)pIsoTp->sendCycle * 1000ul + 1000ul,
					sendDataMsg, (void *)pIsoTp, CO_TIMER_ATTR_ROUNDUP_CYCLIC);
		}
		break;

	case 0x01u: /* wait for next frame */
		/* we are supposed to wait, so restart flow control timer */
		(void)coTimerStart(CO_LINE, &pIsoTp->recTimer, ISOTP_TIMER_N_BS * 1000ul,
				clientChannelTimeout, (void *)pIsoTp, CO_TIMER_ATTR_ROUNDUP);
		break;

	case 0x02:	/* overflow at receiver side */
		(void)clientUserHandler(CO_LINE, pIsoTp, RET_ISOTP_RESULT_BUFFER_OVFL);
		(void)clientChannelReset(CO_LINE, pIsoTp);
		break;

	default:
		(void)clientUserHandler(CO_LINE, pIsoTp, RET_ISOTP_RESULT_ERROR);
		(void)clientChannelReset(CO_LINE, pIsoTp);
		break;
	}
}


/******************************************************************************/
/**
*  \internal
*
* \brief sendDataMsg - sends data message
*
*
*/
static void sendDataMsg(
		CO_LINE_TYPE	CO_LINE,
		void	*pData
	)
{
UNSIGNED8	trData[CO_CAN_MAX_DATA_LEN];
UNSIGNED16	size;
ISO_TP_CONN_T	*pIsoTp = (ISO_TP_CONN_T *)pData;
UNSIGNED8	extFormatOffs = 0u;
UNSIGNED8	maxPayLoad;

	/* extended format ?*/
	if (pIsoTp->formatType == ISOTP_FORMAT_EXTENDED)  {
		trData[0] = pIsoTp->remoteAddr;
		extFormatOffs = 1u;
	}

	maxPayLoad = CO_CAN_CC_MAX_DATA_LEN - (1u + extFormatOffs);

	size = pIsoTp->expectedSize - pIsoTp->transferedSize;
	if (size >= maxPayLoad)  {
		size = maxPayLoad;
	}

	if (size == 0u)  {
		/* nothing to send anymore */
		(void)clientChannelReset(CO_LINE, pIsoTp);
		(void)clientUserHandler(CO_LINE, pIsoTp, RET_OK);
		return;
	}

	MEMSET(&trData[1u + extFormatOffs], ISOTP_FILL_BYTE, maxPayLoad);

	pIsoTp->seqNbr++;
	pIsoTp->seqNbr &= 0x0fu;

	trData[extFormatOffs] = 0x20u;
	trData[extFormatOffs] |= pIsoTp->seqNbr;

	transmitMapFct(pIsoTp, &trData[1u + extFormatOffs], pIsoTp->transferedSize, size);

	pIsoTp->transferedSize += size;
	pIsoTp->blockCnt--;


	(void)icoTransmitMessage(CO_LINE, pIsoTp->trCob, &trData[0], 0u);

	/* cleanup if it was the last message */
	size = pIsoTp->expectedSize - pIsoTp->transferedSize;
	if (size == 0u)  {
		/* nothing to send anymore */
		(void)clientChannelReset(CO_LINE, pIsoTp);
		(void)clientUserHandler(CO_LINE, pIsoTp, RET_OK);
		return;
	}

	if ((size != 0u) && (pIsoTp->blockCnt == 0u))  {
		(void)coTimerStop(CO_LINE, &pIsoTp->sendTimer);

		pIsoTp->state = ISOTP_STATE_CLIENT_WAIT_CTS;
		(void)coTimerStart(CO_LINE, &pIsoTp->recTimer, ISOTP_TIMER_N_BS * 1000ul,
			clientChannelTimeout, (void *)pIsoTp, CO_TIMER_ATTR_ROUNDUP);
		return;
	}

	if (pIsoTp->sendCycle == 0u)  {
		icoEventStart(CO_LINE, &pIsoTp->event, sendDataMsg, pIsoTp);
	}
}


/******************************************************************************/
/**
* \internal
*
* \brief serverUserHandler - call iso-tp server user indication
*
* \return RET_T
*
*/
static RET_T serverUserHandler(
		CO_LINE_TYPE	CO_LINE,
		ISO_TP_CONN_T	*pIsoTp
	)
{
UNSIGNED16 cnt;

	/* save connection type for callback */
	currentType[CO_LINE] = pIsoTp->type;

#ifdef ISOTP_EVENT_SERVER_DATA_SPLIT
	cnt = isotpServSplitTableCnt;
	while(cnt--)  {
		isotpServerSplitTable[cnt](CO_LINE, pIsoTp->localAddr,
			&pIsoTp->pRecData[0u],
			pIsoTp->splitTransSize, pIsoTp->transferedSize);
	}
#endif /* ISOTP_EVENT_SERVER_DATA_SPLIT */


#ifdef ISOTP_EVENT_DYNAMIC_SERVER
	if (pIsoTp->transferedSize == pIsoTp->expectedSize)  {
		cnt = isotpServerTableCnt;
		while (cnt--)  {
			isotpServerTable[cnt](CO_LINE, pIsoTp->remoteAddr,
				&pIsoTp->pRecData[0u], pIsoTp->transferedSize);
		}
	}
#endif /* ISOTP_EVENT_DYNAMIC_SERVER */

	currentType[CO_LINE] = ISOTP_ADDRESS_UNKNOWN;

	(void)pIsoTp;
	(void)cnt;

	return(RET_OK);
}


/******************************************************************************/
/**
* \internal
*
* \brief clientUserHandler - call iso-tp client user indication
*
* \return none
*
*/
static RET_T clientUserHandler(
		CO_LINE_TYPE		CO_LINE,		/* can line */
		ISO_TP_CONN_T	*pIsoTp,
		RET_T			result
	)
{
#ifdef ISOTP_EVENT_DYNAMIC_CLIENT
UNSIGNED16 cnt;

	cnt = isotpClientTableCnt;
	while(cnt--)  {
		isotpClientTable[cnt](CO_LINE, pIsoTp->localAddr, &pIsoTp->pRecData[0u], result);
	}
#else /* ISOTP_EVENT_DYNAMIC_CLIENT */
	(void)pIsoTp;
	(void)result;
	(void)CO_LINE;
#endif /* ISOTP_EVENT_DYNAMIC_CLIENT */

	return(RET_OK);
}


/******************************************************************************/
/**
* \internal
*
* \brief clientTxAckUserHandler - call iso-tp client user indication
*
* \return RET_T
*
*/
static RET_T clientTxAckUserHandler(
		CO_LINE_TYPE	CO_LINE,
		ISO_TP_CONN_T* pIsoTp,
		UNSIGNED8 *pData,
		UNSIGNED16 size
	)
{
#ifdef ISOTP_EVENT_DYNAMIC_CLIENT_TXACK
UNSIGNED16 cnt;

	cnt = isotpClientTxAckTableCnt;
	while (cnt--)  {
		isotpClientTxAckTable[cnt](CO_LINE, pIsoTp->localAddr, pData, size);
	}
#else /* ISOTP_EVENT_DYNAMIC_CLIENT_TXACK */
	(void)pIsoTp;
	(void)CO_LINE;
#endif /* ISOTP_EVENT_DYNAMIC_CLIENT_TXACK */

	return(RET_OK);
}


/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
#ifdef ISOTP_EVENT_DYNAMIC_SERVER
/******************************************************************************/
/**
* \brief isoTpEventRegister_SERVER - register iso-tp server event function
*
* This function registers an iso-tp write indication function.
*
* \return RET_T
*
*/
RET_T isoTpEventRegister_SERVER(
		ISOTP_EVENT_SERVER_T	pFunction	/**< pointer to function */
	)
{
	if (isotpServerTableCnt >= ISOTP_EVENT_DYNAMIC_SERVER)  {
		return(RET_EVENT_NO_RESSOURCE);
	}
	/* save function pointer */
	isotpServerTable[isotpServerTableCnt] = pFunction;
	isotpServerTableCnt++;

	return(RET_OK);
}
#endif /* ISOTP_EVENT_DYNAMIC_SERVER */


#ifdef ISOTP_EVENT_DYNAMIC_SSPLIT
/******************************************************************************/
/**
* \brief isoTpEventRegister_SERVER - register iso-tp server event function
*
* This function registers an iso-tp write indication function.
*
* \return RET_T
*
*/
RET_T isoTpEventRegister_SERVER_SPLIT(
		ISOTP_EVENT_SERVER_SPLIT_T	pFunction	/**< pointer to function */
	)
{
	if (isotpServSplitTableCnt >= ISOTP_EVENT_DYNAMIC_SSPLIT)  {
		return(RET_EVENT_NO_RESSOURCE);
	}
	/* save function pointer */
	isotpServerSplitTable[isotpServSplitTableCnt] = pFunction;
	isotpServSplitTableCnt++;

	return(RET_OK);
}
#endif /* ISOTP_EVENT_DYNAMIC_SSPLIT */


#ifdef ISOTP_EVENT_DYNAMIC_CLIENT
/******************************************************************************/
/**
* \brief isoTpEventRegister_CLIENT - register iso-tp client event function
*
* This function registers an iso-tp client indication function.
*
* \return RET_T
*
*/
RET_T isoTpEventRegister_CLIENT(
		ISOTP_EVENT_CLIENT_T	pFunction	/**< pointer to function */
	)
{
	if (isotpClientTableCnt >= ISOTP_EVENT_DYNAMIC_CLIENT)  {
		return(RET_EVENT_NO_RESSOURCE);
	}
	/* save function pointer */
	isotpClientTable[isotpClientTableCnt] = pFunction;
	isotpClientTableCnt++;

	return(RET_OK);
}
#endif /* ISOTP_EVENT_DYNAMIC_CLIENT */


#ifdef ISOTP_EVENT_DYNAMIC_CLIENT_TXACK
/******************************************************************************/
/**
* \brief isoTpEventRegister_CLIENT - register iso-tp client event function
*
* This function registers an iso-tp client indication function.
*
* \return RET_T
*
*/
RET_T isoTpEventRegister_CLIENT_TXACK(
		ISOTP_EVENT_CLIENT_TXACK_T	pFunction	/**< pointer to function */
	)
{
	if (isotpClientTxAckTableCnt >= ISOTP_EVENT_DYNAMIC_CLIENT_TXACK)  {
		return(RET_EVENT_NO_RESSOURCE);
	}
	/* save function pointer */
	isotpClientTxAckTable[isotpClientTxAckTableCnt] = pFunction;
	isotpClientTxAckTableCnt++;

	return(RET_OK);
}
#endif /* ISOTP_EVENT_DYNAMIC_CLIENT_TXACK */


/******************************************************************************/
/**
* \brief isoTpSetIds - change request and response CAN-IDs
*
* This function changes the isotp client and server IDs
* for request and response
* depending on the given destination address.
*
* If a transfer is in progess or remoteAddr wasn't found,
* the function returns an error.
*
* This function can only be used for normal fixed address mode.
*
* \return RET_T
*
*/
RET_T isoTpSetIds(
		CO_LINE_TYPE	CO_LINE,			/**< can line */
		UNSIGNED8 remoteAddr,			/**< remote address */
		UNSIGNED32 reqId,				/**< request CAN id */
		UNSIGNED32 respId				/**< response CAN id */
	)
{
ISO_TP_CONN_T *pIsoTp;
RET_T	retVal;
UNSIGNED8	localAddr;

	/* get client channel */
	pIsoTp = searchChannelRemoteAddr(CO_LINE, remoteAddr);
	if (pIsoTp == NULL)  {
		return(RET_INVALID_PARAMETER);
	}

	/* if transfer in progress ? */
	if (pIsoTp->state != ISOTP_STATE_FREE)  {
		return(RET_SERVICE_BUSY);
	}

	/* only ISOTP_FORMAT_NORMAL and ISOTP_FORMAT_EXTENDED use respId and reqId*/
	if ((pIsoTp->formatType != ISOTP_FORMAT_NORMAL)
	 && (pIsoTp->formatType != ISOTP_FORMAT_EXTENDED))  {
		return(RET_INVALID_PARAMETER);
	}

	retVal = icoCobSet(CO_LINE, pIsoTp->recCob, reqId, CO_COB_RTR_NONE,
							CO_CAN_MAX_DATA_LEN);
	if (retVal != RET_OK)  {
		return(retVal);
	}
	retVal = icoCobSet(CO_LINE, pIsoTp->trCob, respId, CO_COB_RTR_NONE,
							CO_CAN_MAX_DATA_LEN);
	if (retVal != RET_OK)  {
		return(retVal);
	}

	localAddr = pIsoTp->localAddr;

	/* get server channel */
	pIsoTp = searchChannelLocalAddr(CO_LINE, localAddr);
	if (pIsoTp == NULL)  {
		return(RET_INVALID_PARAMETER);
	}

	/* if transfer in progress ? */
	if (pIsoTp->state != ISOTP_STATE_FREE)  {
		return(RET_SERVICE_BUSY);
	}

	/* only ISOTP_FORMAT_NORMAL use respId and reqId */
	if (pIsoTp->formatType != ISOTP_FORMAT_NORMAL)  {
		return(RET_INVALID_PARAMETER);
	}

	retVal = icoCobSet(CO_LINE, pIsoTp->recCob, reqId, CO_COB_RTR_NONE,
						CO_CAN_MAX_DATA_LEN);
	if (retVal != RET_OK)  {
		return(retVal);
	}
	retVal = icoCobSet(CO_LINE, pIsoTp->trCob, respId, CO_COB_RTR_NONE,
						CO_CAN_MAX_DATA_LEN);
	if (retVal != RET_OK)  {
		return(retVal);
	}
	return(RET_OK);
}


/******************************************************************************/
/*
* \brief calculateId - calculate can-ids
*
*/
static UNSIGNED32 calculateId(
		UNSIGNED8 destAddr,				/* destination address */
		UNSIGNED8 srcAddr,				/* source address */
		ISO_TP_ADDRESS_T addrType		/* address type */
	)
{
UNSIGNED32	id;

	id = CO_COB_29BIT | ISOTP_PRIORITY | (destAddr << 8) | srcAddr;
	if (addrType == ISOTP_ADDRESS_PHYS)  {
		id  |= 0xDA0000ul;
	} else
	if (addrType == ISOTP_ADDRESS_FUNC)  {
		id  |= 0xDB0000ul;
	} else {
		id = 0u;
	}

	return(id);
}


/******************************************************************************/
/**
* \brief isoTpSetRemoteAddr - change remote address
*
* This function changes the isotp remote address
* for the given destination channel.
*
* If a transfer is in progess or remote address wasn't found,
* the function returns an error.
*
* If newLocalAddr or newRemoteAddr is zero, the address will not be changed.
*
* \return RET_T
*
*/
RET_T isoTpSetRemoteAddr(
		CO_LINE_TYPE	CO_LINE,			/**< can line */
		UNSIGNED8 remoteAddr,				/**< current destination iso-tp address */
		UNSIGNED8 newLocalAddr,				/**< new local iso-tp address */
		UNSIGNED8 newRemoteAddr				/**< new remote address */
	)
{
ISO_TP_CONN_T *pIsoTp;
RET_T	retVal;
UNSIGNED32	canId;

	/* get client channel */
	pIsoTp = searchChannelRemoteAddr(CO_LINE, remoteAddr);
	if (pIsoTp == NULL)  {
		return(RET_INVALID_PARAMETER);
	}

	/* if transfer in progress ? */
	if (pIsoTp->state != ISOTP_STATE_FREE)  {
		return(RET_SERVICE_BUSY);
	}

	/* only ISOTP_FORMAT_NORMAL_FIXED uses dst and src address */
	if ((pIsoTp->formatType != ISOTP_FORMAT_NORMAL_FIXED)
	 && (pIsoTp->formatType != ISOTP_FORMAT_EXTENDED))  {
		return(RET_INVALID_PARAMETER);
	}

	if (newRemoteAddr != 0u)  {
		pIsoTp->remoteAddr = newRemoteAddr;
	}
	if (newLocalAddr != 0u)  {
		pIsoTp->localAddr = newLocalAddr;
	}

	if (pIsoTp->formatType == ISOTP_FORMAT_NORMAL_FIXED)  {
		canId = calculateId(pIsoTp->localAddr, pIsoTp->remoteAddr, pIsoTp->type);
		retVal = icoCobSet(CO_LINE, pIsoTp->recCob, canId, CO_COB_RTR_NONE,
					CO_CAN_MAX_DATA_LEN);
		if (retVal != RET_OK)  {
			return(retVal);
		}

		canId = calculateId(pIsoTp->remoteAddr, pIsoTp->localAddr, pIsoTp->type);
		retVal = icoCobSet(CO_LINE, pIsoTp->trCob, canId, CO_COB_RTR_NONE,
					CO_CAN_MAX_DATA_LEN);
		if (retVal != RET_OK)  {
			return(retVal);
		}
	}

	return(RET_OK);
}


/******************************************************************************/
/**
* \brief isoTpSetLocalAddr - change new local address
*
* This function changes the isotp local address
* for the given server channel.
*
* If a transfer is in progess or localAddr wasn't found,
* the function returns an error.
*
* If newLocalAddr or newRemoteAddr is zero, the address will not be changed.
*
* \return RET_T
*
*/
RET_T isoTpSetLocalAddr(
		CO_LINE_TYPE	CO_LINE,			/**< can line */
		UNSIGNED8 localAddr,				/**< current local iso-tp address */
		UNSIGNED8 newLocalAddr,				/**< new local iso-tp address */
		UNSIGNED8 newRemoteAddr				/**< new remote address */
	)
{
ISO_TP_CONN_T *pIsoTp;
RET_T	retVal;
UNSIGNED32	canId;

	/* get server channel */
	pIsoTp = searchChannelLocalAddr(CO_LINE, localAddr);
	if (pIsoTp == NULL)  {
		return(RET_INVALID_PARAMETER);
	}

	/* if transfer in progress ? */
	if (pIsoTp->state != ISOTP_STATE_FREE)  {
		return(RET_SERVICE_BUSY);
	}

	/* only ISOTP_FORMAT_NORMAL use respId and reqId */
	if ((pIsoTp->formatType != ISOTP_FORMAT_NORMAL_FIXED)
	 && (pIsoTp->formatType != ISOTP_FORMAT_EXTENDED))  {
		return(RET_INVALID_PARAMETER);
	}

	if (newRemoteAddr != 0u)  {
		pIsoTp->remoteAddr = newRemoteAddr;
	}
	if (newLocalAddr != 0u)  {
		pIsoTp->localAddr = newLocalAddr;
	}

	if (pIsoTp->formatType == ISOTP_FORMAT_NORMAL_FIXED)  {
		canId = calculateId(pIsoTp->remoteAddr, pIsoTp->localAddr, pIsoTp->type);
		retVal = icoCobSet(CO_LINE, pIsoTp->trCob, canId, CO_COB_RTR_NONE,
					CO_CAN_MAX_DATA_LEN);
		if (retVal != RET_OK)  {
			return(retVal);
		}
		canId = calculateId(pIsoTp->localAddr, pIsoTp->remoteAddr, pIsoTp->type);
		retVal = icoCobSet(CO_LINE, pIsoTp->recCob, canId, CO_COB_RTR_NONE,
					CO_CAN_MAX_DATA_LEN);
		if (retVal != RET_OK)  {
			return(retVal);
		}
	}

	return(RET_OK);
}


/******************************************************************************/
/**
* \brief isoTpInitChannel - initialize iso tp channel
*
* This function initialize an iso tp channel.
* It can be used for transmit and for receive of data.
*
* \return RET_T
*
*/
RET_T isoTpInitChannel(
		CO_LINE_TYPE	CO_LINE,			/**< can line */
		UNSIGNED32 recId,				/**< receive CAN id */
		UNSIGNED32 trId,				/**< transmit CAN id */
		UNSIGNED8 localAddr,			/**< source address */
		UNSIGNED8 destAddr,				/**< destination address */
		ISO_TP_ADDRESS_T addrType,		/**< address type */
		ISO_TP_FORMAT_T formatType,		/**< format type */
		UNSIGNED8 *pRecDataBuffer,		/**< receive data buffer */
		UNSIGNED16 recBufSize			/**< receive data buffer size */
	)
{
ISO_TP_CONN_T *pIsoTp;

	if (isotpChannelCnt[CO_LINE] >= ISOTP_SERVER_CONNECTION_CNT)  {
		return(RET_EVENT_NO_RESSOURCE);
	}

	pIsoTp = getChannel(CO_LINE, isotpChannelCnt[CO_LINE]);

	switch (formatType)  {
	case ISOTP_FORMAT_NORMAL:
		break;
	case ISOTP_FORMAT_NORMAL_FIXED:	/**< normal fixed addressing */
		break;
	case ISOTP_FORMAT_EXTENDED:		/**< extended addressing */
		break;
	case ISOTP_FORMAT_MIXED:		/**< mixed addressing */
		/* no break - currently not supported */
	default:
		return(RET_INVALID_PARAMETER);
	}

	pIsoTp->recCob = icoCobCreate(CO_LINE, CO_COB_TYPE_RECEIVE,
			CO_SERVICE_ISOTP_SERVER, isotpChannelCnt[CO_LINE]);
	if (pIsoTp->recCob == CO_INVALID_COB_REF)  {
		return(RET_NO_COB_AVAILABLE);
	}

	pIsoTp->trCob = icoCobCreate(CO_LINE, CO_COB_TYPE_TRANSMIT,
			CO_SERVICE_ISOTP_SERVER, isotpChannelCnt[CO_LINE]);
	if (pIsoTp->trCob == CO_INVALID_COB_REF)  {
		return(RET_NO_COB_AVAILABLE);
	}

	/* for ISOTP_FORMAT_NORMAL use trId and recId */
	if (formatType == ISOTP_FORMAT_NORMAL_FIXED)  {
		trId = calculateId(destAddr, localAddr, addrType);
		recId  = calculateId(localAddr, destAddr, addrType);
	}

	(void)icoCobSet(CO_LINE, pIsoTp->recCob, recId, CO_COB_RTR_NONE,
					CO_CAN_MAX_DATA_LEN);
	(void)icoCobSet(CO_LINE, pIsoTp->trCob, trId, CO_COB_RTR_NONE,
					CO_CAN_MAX_DATA_LEN);

	if (formatType == ISOTP_FORMAT_NORMAL_FIXED)  {
		(void)icoCobSetIgnore(CO_LINE, pIsoTp->recCob, 0xffffu);
	}

	pIsoTp->localAddr = localAddr;
	pIsoTp->remoteAddr = destAddr;
	pIsoTp->type = addrType;
	pIsoTp->formatType = formatType;

	pIsoTp->pRecData = pRecDataBuffer;
	pIsoTp->recMaxSize = recBufSize;

	isotpChannelCnt[CO_LINE]++;

	return(RET_OK);
}


/******************************************************************************/
/**
* \brief isoTpInit - init iso tp module
*
*
* \return RET_T
*
*/
RET_T isoTpInit(
		const UNSIGNED16	*pClientCnt,
		const UNSIGNED16	*pServerCnt
	)
{
UNSIGNED16 cnt;
CO_LINE_TYPE	CO_LINE;
UNSIGNED16		offs = 0u;

#ifdef ISOTP_EVENT_DYNAMIC_SERVER
	isotpServerTableCnt = 0u;

	for (cnt = 0u; cnt < ISOTP_EVENT_DYNAMIC_SERVER; cnt++)  {
		isotpServerTable[cnt] = NULL;
	}
#endif /* ISOTP_EVENT_DYNAMIC_SERVER */

#ifdef ISOTP_EVENT_DYNAMIC_SSPLIT
	for (cnt = 0u; cnt < ISOTP_EVENT_DYNAMIC_SSPLIT; cnt++)  {
		isotpServerSplitTable[cnt] = NULL;
	}
	isotpServSplitTableCnt = 0u;
#endif /* ISOTP_EVENT_DYNAMIC_SSPLIT */

#ifdef ISOTP_EVENT_DYNAMIC_CLIENT
	isotpClientTableCnt = 0u;

	for (cnt = 0u; cnt < ISOTP_EVENT_DYNAMIC_CLIENT; cnt++)  {
		isotpClientTable[cnt] = NULL;
	}
#endif /* ISOTP_EVENT_DYNAMIC_CLIENT */

#ifdef ISOTP_CLIENT_CONNECTION_CNT

#endif /* ISOTP_CLIENT_CONNECTION_CNT */

#ifdef ISOTP_SERVER_CONNECTION_CNT
	for (cnt = 0u; cnt < ISOTP_SERVER_CONNECTION_CNT; cnt++)  {
		isotpChannel[cnt].state = ISOTP_STATE_FREE;
	}

	offs = 0u;
	for (CO_LINE = 0; CO_LINE < CO_MULTI_LINE_CNT; CO_LINE++)
	{

		isotpChannelCnt[CO_LINE] = 0u;

		isotpLineCnt[CO_LINE] = pServerCnt[CO_LINE];
		isotpLineOffs[CO_LINE] = offs;
		offs += isotpLineCnt[CO_LINE];
	}
#else /* ISOTP_SERVER_CONNECTION_CNT */
	(void)offs;
	(void)CO_LINE;
#endif /* ISOTP_SERVER_CONNECTION_CNT */

	(void)cnt;
	(void)pClientCnt;

	return(RET_OK);
}


/******************************************************************************/
/**
*
* \brief isoTpGetCurrectConType - get current connection type
*
*
* \return RET_T
*
*/
ISO_TP_ADDRESS_T isoTpGetCurrectConType(
		CO_LINE_TYPE	CO_LINE		/**< can line */
	)
{
	return(currentType[CO_LINE]);
}


/******************************************************************************/
/**
* \internal
*
* \brief getChannel - get channel
*
*
* \return RET_T
*
*/
static ISO_TP_CONN_T *getChannel(
		CO_LINE_TYPE		CO_LINE,		/* can line */
		UNSIGNED16			idx
	)
{
ISO_TP_CONN_T *pIsoTp;

	pIsoTp = &isotpChannel[idx + isotpLineOffs[CO_LINE]];

	return(pIsoTp);
}


/******************************************************************************/
/**
* \internal
*
* \brief searchChannelRemoteAddr - search channel by Dst address
*
*
* \return RET_T
*
*/
static ISO_TP_CONN_T *searchChannelRemoteAddr(
		CO_LINE_TYPE		CO_LINE,		/* can line */
		UNSIGNED8			addr			/* remote address */
	)
{
ISO_TP_CONN_T *pIsoTp = NULL;
UNSIGNED16	cnt;

	for (cnt = 0u; cnt < isotpChannelCnt[CO_LINE]; cnt++)  {
		pIsoTp = getChannel(CO_LINE, cnt);
		if (pIsoTp != NULL)  {
			if (pIsoTp->remoteAddr == addr)  {
				break;
			} else {
				pIsoTp = NULL;
			}
		}
	}

	return(pIsoTp);
}


/******************************************************************************/
/**
* \internal
*
* \brief searchClientChannelCobRef - search client channel by cob reference
*
*
* \return RET_T
*
*/
static ISO_TP_CONN_T *searchChannelCobRef(
		CO_LINE_TYPE		CO_LINE,		/* can line */
		UNSIGNED16			cobRef
	)
{
ISO_TP_CONN_T *pIsoTp = NULL;
UNSIGNED16	cnt;

	for (cnt = 0u; cnt < isotpChannelCnt[CO_LINE]; cnt++)  {
		pIsoTp = getChannel(CO_LINE, cnt);
		if (pIsoTp != NULL)  {
			if (pIsoTp->trCob == cobRef)  {
				break;
			} else {
				pIsoTp = NULL;
			}
		}
	}

	return(pIsoTp);
}


#ifdef ISOTP_CLIENT_CONNECTION_CNT
/******************************************************************************/
/**
* \internal
*
* \brief clientChannelReset - reset the given isp-tp client
*
*
* \return RET_T
*
*/
static RET_T clientChannelReset(
		CO_LINE_TYPE		CO_LINE,		/* can line */
		ISO_TP_CONN_T *pIsoTp	/* pointer to iso-tp connection which should be reset */
	)
{
	if (pIsoTp == NULL)  {
		return(RET_INVALID_PARAMETER);
	}

	pIsoTp->state = ISOTP_STATE_FREE;
	pIsoTp->sendCycle = 0u;
	(void)coTimerStop(CO_LINE, &pIsoTp->sendTimer);

	return(RET_OK);
}
#endif /* ISOTP_CLIENT_CONNECTION_CNT */


#ifdef ISOTP_CLIENT_CONNECTION_CNT
/******************************************************************************/
/**
* \internal
*
* \brief clientChannelTimeout - callback function for timer
*
* If a receive message timed out this function is called on server side to
* reset the ISO-TP client channel.
*
*/
static void clientChannelTimeout(
		CO_LINE_TYPE		CO_LINE,		/* can line */
		void				*pData
	)
{
ISO_TP_CONN_T *pIsoTp;

	pIsoTp = (ISO_TP_CONN_T*)pData;

	/* inform application - error depends on current state */
	clientUserHandler(CO_LINE,	pIsoTp, RET_ISOTP_TIMEOUT_BS);

	(void)clientChannelReset(CO_LINE, pIsoTp);
}
#endif /* ISOTP_CLIENT_CONNECTION_CNT */


#ifdef ISOTP_SERVER_CONNECTION_CNT
/******************************************************************************/
/**
* \internal
*
* \brief getAlternativeServerChannel - get alternative server channel
*
* In exended adressing mode, can-ids for functional and physical adressing
* can be the same.
* Therefore we check here, if an alternative channel available.
*
* \return pointer to ISO-TP connection
*
*/
static ISO_TP_CONN_T *getAlternativeServerChannel(
		CO_LINE_TYPE		CO_LINE,		/* can line */
		ISO_TP_CONN_T 		*pIsoTp			/* current used channel */
	)
{
ISO_TP_CONN_T *pRetCon = NULL;
ISO_TP_ADDRESS_T addrType;
UNSIGNED16 idx;

	if (pIsoTp == NULL)  {
		return(NULL);
	}

	/* use alternate adress mode */
	if (pIsoTp->type == ISOTP_ADDRESS_FUNC)  {
		addrType = ISOTP_ADDRESS_PHYS;
	} else {
		addrType = ISOTP_ADDRESS_FUNC;
	}

	for (idx = 0u; idx < isotpChannelCnt[CO_LINE]; idx++)  {
		ISO_TP_CONN_T *pChan = getChannel(CO_LINE, idx);
		if ((pChan->remoteAddr == pIsoTp->remoteAddr)
		 && (pChan->type == addrType))  {
			pRetCon = pChan;
			break;
		}
	}

	return(pRetCon);
}


/******************************************************************************/
/**
* \internal
*
* \brief searchChannelLocalAddr - search server channel by local addr
*
*
* \return pointer to ISO-TP connection
*
*/
static ISO_TP_CONN_T *searchChannelLocalAddr(
		CO_LINE_TYPE		CO_LINE,		/* can line */
		UNSIGNED8			addr
	)
{
ISO_TP_CONN_T *pIsoTp = NULL;
UNSIGNED16	cnt;

	for (cnt = 0u; cnt < isotpChannelCnt[CO_LINE]; cnt++)  {
		pIsoTp = getChannel(CO_LINE, cnt);
		if (pIsoTp != NULL)  {
			if (pIsoTp->localAddr == addr)  {
				return(pIsoTp);
			}
		}
	}
	return(NULL);
}


/******************************************************************************/
/**
* \internal
*
* \brief serverChannelReset - reset the given isp-tp server
*
*
* \return RET_T
*
*/
static RET_T serverChannelReset(
		CO_LINE_TYPE		CO_LINE,		/* can line */
		ISO_TP_CONN_T *pIsoTp	/**< pointer to iso-tp connection which should be reset */
	)
{
RET_T retVal = RET_INVALID_PARAMETER;

	if (pIsoTp != NULL)  {

		pIsoTp->state = ISOTP_STATE_FREE;
		pIsoTp->recTimeout = 0u;
		(void)coTimerStop(CO_LINE, &pIsoTp->recTimer);

		retVal = RET_OK;
	}

	return(retVal);
}


/******************************************************************************/
/**
* \internal
*
* \brief serverChannelTimeout - callback function for timer
*
* If a receive message timed out this function is called on server side to
* reset the ISO-TP server channel.
*
*/
static void serverChannelTimeout(
		CO_LINE_TYPE		CO_LINE,		/* can line */
		void				*pData
	)
{
ISO_TP_CONN_T *pIsoTp;

	pIsoTp = (ISO_TP_CONN_T*)pData;
	(void)serverChannelReset(CO_LINE, pIsoTp);
}
#endif /* ISOTP_SERVER_CONNECTION_CNT */
#endif /* ISOTP_SUPPORTED */
