/****************************************************************************

  (c) SYSTEC electronic GmbH, D-07973 Greiz, August-Bebel-Str. 29
      www.systec-electronic.com

  Project:      openPOWERLINK

  Description:  source file for kernel DLL Communication Abstraction Layer module

  License:

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in the
       documentation and/or other materials provided with the distribution.

    3. Neither the name of SYSTEC electronic GmbH nor the names of its
       contributors may be used to endorse or promote products derived
       from this software without prior written permission. For written
       permission, please contact info@systec-electronic.com.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.

    Severability Clause:

        If a provision of this License is or becomes illegal, invalid or
        unenforceable in any jurisdiction, that shall not affect:
        1. the validity or enforceability in that jurisdiction of any other
           provision of this License; or
        2. the validity or enforceability in other jurisdictions of that or
           any other provision of this License.

  -------------------------------------------------------------------------

                $RCSfile: EplDllkCal.c,v $

                $Author: D.Krueger $

                $Revision: 1.7 $  $Date: 2008/11/13 17:13:09 $

                $State: Exp $

                Build Environment:
                    GCC V3.4

  -------------------------------------------------------------------------

  Revision History:

  2006/06/15 d.k.:   start of the implementation, version 1.00

****************************************************************************/

#include "kernel/EplDllkCal.h"
#include "kernel/EplDllk.h"
#include "kernel/EplEventk.h"

#include "EplDllCal.h"
#ifndef EPL_NO_FIFO
#include "SharedBuff.h"
#endif

#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_DLLK)) != 0)
/***************************************************************************/
/*                                                                         */
/*                                                                         */
/*          G L O B A L   D E F I N I T I O N S                            */
/*                                                                         */
/*                                                                         */
/***************************************************************************/

//---------------------------------------------------------------------------
// const defines
//---------------------------------------------------------------------------

#ifndef min
#define min(a,b)            (((a) < (b)) ? (a) : (b))
#endif

//---------------------------------------------------------------------------
// local types
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// modul globale vars
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// local function prototypes
//---------------------------------------------------------------------------

/***************************************************************************/
/*                                                                         */
/*                                                                         */
/*          C L A S S  EplDllkCal                                          */
/*                                                                         */
/*                                                                         */
/***************************************************************************/
//
// Description:
//
//
/***************************************************************************/

//=========================================================================//
//                                                                         //
//          P R I V A T E   D E F I N I T I O N S                          //
//                                                                         //
//=========================================================================//

//---------------------------------------------------------------------------
// const defines
//---------------------------------------------------------------------------

#define EPL_DLLKCAL_MAX_QUEUES  5	// CnGenReq, CnNmtReq, {MnGenReq, MnNmtReq}, MnIdentReq, MnStatusReq

//---------------------------------------------------------------------------
// local types
//---------------------------------------------------------------------------

typedef struct {
#ifndef EPL_NO_FIFO
//    tShbInstance    m_ShbInstanceRx;      // FIFO for Rx ASnd frames
	tShbInstance m_ShbInstanceTxNmt;	// FIFO for Tx frames with NMT request priority
	tShbInstance m_ShbInstanceTxGen;	// FIFO for Tx frames with generic priority
#else
	unsigned int m_uiFrameSizeNmt;
	u8 m_abFrameNmt[1500];
	unsigned int m_uiFrameSizeGen;
	u8 m_abFrameGen[1500];
#endif

	tEplDllkCalStatistics m_Statistics;

#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
	// IdentRequest queue with CN node IDs
	unsigned int m_auiQueueIdentReq[EPL_D_NMT_MaxCNNumber_U8 + 1];	// 1 entry is reserved to distinguish between full and empty
	unsigned int m_uiWriteIdentReq;
	unsigned int m_uiReadIdentReq;

	// StatusRequest queue with CN node IDs
	unsigned int m_auiQueueStatusReq[EPL_D_NMT_MaxCNNumber_U8 + 1];	// 1 entry is reserved to distinguish between full and empty
	unsigned int m_uiWriteStatusReq;
	unsigned int m_uiReadStatusReq;

	unsigned int m_auiQueueCnRequests[254 * 2];
	// first 254 entries represent the generic requests of the corresponding node
	// second 254 entries represent the NMT requests of the corresponding node
	unsigned int m_uiNextQueueCnRequest;
	unsigned int m_uiNextRequestQueue;
#endif

} tEplDllkCalInstance;

//---------------------------------------------------------------------------
// local vars
//---------------------------------------------------------------------------

// if no dynamic memory allocation shall be used
// define structures statically
static tEplDllkCalInstance EplDllkCalInstance_g;

//---------------------------------------------------------------------------
// local function prototypes
//---------------------------------------------------------------------------

//=========================================================================//
//                                                                         //
//          P U B L I C   F U N C T I O N S                                //
//                                                                         //
//=========================================================================//

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalAddInstance()
//
// Description: add and initialize new instance of DLL CAL module
//
// Parameters:  none
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalAddInstance(void)
{
	tEplKernel Ret = kEplSuccessful;
#ifndef EPL_NO_FIFO
	tShbError ShbError;
	unsigned int fShbNewCreated;

/*    ShbError = ShbCirAllocBuffer (EPL_DLLCAL_BUFFER_SIZE_RX, EPL_DLLCAL_BUFFER_ID_RX,
        &EplDllkCalInstance_g.m_ShbInstanceRx, &fShbNewCreated);
    // returns kShbOk, kShbOpenMismatch, kShbOutOfMem or kShbInvalidArg

    if (ShbError != kShbOk)
    {
        Ret = kEplNoResource;
    }
*/
	ShbError =
	    ShbCirAllocBuffer(EPL_DLLCAL_BUFFER_SIZE_TX_NMT,
			      EPL_DLLCAL_BUFFER_ID_TX_NMT,
			      &EplDllkCalInstance_g.m_ShbInstanceTxNmt,
			      &fShbNewCreated);
	// returns kShbOk, kShbOpenMismatch, kShbOutOfMem or kShbInvalidArg

	if (ShbError != kShbOk) {
		Ret = kEplNoResource;
	}

/*    ShbError = ShbCirSetSignalHandlerNewData (EplDllkCalInstance_g.m_ShbInstanceTxNmt, EplDllkCalTxNmtSignalHandler, kShbPriorityNormal);
    // returns kShbOk, kShbAlreadySignaling or kShbInvalidArg

    if (ShbError != kShbOk)
    {
        Ret = kEplNoResource;
    }
*/
	ShbError =
	    ShbCirAllocBuffer(EPL_DLLCAL_BUFFER_SIZE_TX_GEN,
			      EPL_DLLCAL_BUFFER_ID_TX_GEN,
			      &EplDllkCalInstance_g.m_ShbInstanceTxGen,
			      &fShbNewCreated);
	// returns kShbOk, kShbOpenMismatch, kShbOutOfMem or kShbInvalidArg

	if (ShbError != kShbOk) {
		Ret = kEplNoResource;
	}

/*    ShbError = ShbCirSetSignalHandlerNewData (EplDllkCalInstance_g.m_ShbInstanceTxGen, EplDllkCalTxGenSignalHandler, kShbPriorityNormal);
    // returns kShbOk, kShbAlreadySignaling or kShbInvalidArg

    if (ShbError != kShbOk)
    {
        Ret = kEplNoResource;
    }
*/
#else
	EplDllkCalInstance_g.m_uiFrameSizeNmt = 0;
	EplDllkCalInstance_g.m_uiFrameSizeGen = 0;
#endif

	return Ret;
}

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalDelInstance()
//
// Description: deletes instance of DLL CAL module
//
// Parameters:  none
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalDelInstance(void)
{
	tEplKernel Ret = kEplSuccessful;
#ifndef EPL_NO_FIFO
	tShbError ShbError;

/*    ShbError = ShbCirReleaseBuffer (EplDllkCalInstance_g.m_ShbInstanceRx);
    if (ShbError != kShbOk)
    {
        Ret = kEplNoResource;
    }
    EplDllkCalInstance_g.m_ShbInstanceRx = NULL;
*/
	ShbError = ShbCirReleaseBuffer(EplDllkCalInstance_g.m_ShbInstanceTxNmt);
	if (ShbError != kShbOk) {
		Ret = kEplNoResource;
	}
	EplDllkCalInstance_g.m_ShbInstanceTxNmt = NULL;

	ShbError = ShbCirReleaseBuffer(EplDllkCalInstance_g.m_ShbInstanceTxGen);
	if (ShbError != kShbOk) {
		Ret = kEplNoResource;
	}
	EplDllkCalInstance_g.m_ShbInstanceTxGen = NULL;

#else
	EplDllkCalInstance_g.m_uiFrameSizeNmt = 0;
	EplDllkCalInstance_g.m_uiFrameSizeGen = 0;
#endif

	return Ret;
}

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalProcess
//
// Description: process the passed configuration
//
// Parameters:  pEvent_p                = event containing configuration options
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalProcess(tEplEvent * pEvent_p)
{
	tEplKernel Ret = kEplSuccessful;

	switch (pEvent_p->m_EventType) {
	case kEplEventTypeDllkServFilter:
		{
			tEplDllCalAsndServiceIdFilter *pServFilter;

			pServFilter =
			    (tEplDllCalAsndServiceIdFilter *) pEvent_p->m_pArg;
			Ret =
			    EplDllkSetAsndServiceIdFilter(pServFilter->
							  m_ServiceId,
							  pServFilter->
							  m_Filter);
			break;
		}

#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
	case kEplEventTypeDllkIssueReq:
		{
			tEplDllCalIssueRequest *pIssueReq;

			pIssueReq = (tEplDllCalIssueRequest *) pEvent_p->m_pArg;
			Ret =
			    EplDllkCalIssueRequest(pIssueReq->m_Service,
						   pIssueReq->m_uiNodeId,
						   pIssueReq->m_bSoaFlag1);
			break;
		}

	case kEplEventTypeDllkAddNode:
		{
			tEplDllNodeInfo *pNodeInfo;

			pNodeInfo = (tEplDllNodeInfo *) pEvent_p->m_pArg;
			Ret = EplDllkAddNode(pNodeInfo);
			break;
		}

	case kEplEventTypeDllkDelNode:
		{
			unsigned int *puiNodeId;

			puiNodeId = (unsigned int *)pEvent_p->m_pArg;
			Ret = EplDllkDeleteNode(*puiNodeId);
			break;
		}

	case kEplEventTypeDllkSoftDelNode:
		{
			unsigned int *puiNodeId;

			puiNodeId = (unsigned int *)pEvent_p->m_pArg;
			Ret = EplDllkSoftDeleteNode(*puiNodeId);
			break;
		}
#endif

	case kEplEventTypeDllkIdentity:
		{
			tEplDllIdentParam *pIdentParam;

			pIdentParam = (tEplDllIdentParam *) pEvent_p->m_pArg;
			if (pIdentParam->m_uiSizeOfStruct > pEvent_p->m_uiSize) {
				pIdentParam->m_uiSizeOfStruct =
				    pEvent_p->m_uiSize;
			}
			Ret = EplDllkSetIdentity(pIdentParam);
			break;
		}

	case kEplEventTypeDllkConfig:
		{
			tEplDllConfigParam *pConfigParam;

			pConfigParam = (tEplDllConfigParam *) pEvent_p->m_pArg;
			if (pConfigParam->m_uiSizeOfStruct > pEvent_p->m_uiSize) {
				pConfigParam->m_uiSizeOfStruct =
				    pEvent_p->m_uiSize;
			}
			Ret = EplDllkConfig(pConfigParam);
			break;
		}

	default:
		break;
	}

//Exit:
	return Ret;
}

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalAsyncGetTxCount()
//
// Description: returns count of Tx frames of FIFO with highest priority
//
// Parameters:  none
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalAsyncGetTxCount(tEplDllAsyncReqPriority * pPriority_p,
				     unsigned int *puiCount_p)
{
	tEplKernel Ret = kEplSuccessful;
#ifndef EPL_NO_FIFO
	tShbError ShbError;
	unsigned long ulFrameCount;

	// get frame count of Tx FIFO with NMT request priority
	ShbError =
	    ShbCirGetReadBlockCount(EplDllkCalInstance_g.m_ShbInstanceTxNmt,
				    &ulFrameCount);
	// returns kShbOk, kShbInvalidArg

	// error handling
	if (ShbError != kShbOk) {
		Ret = kEplNoResource;
		goto Exit;
	}

	if (ulFrameCount >
	    EplDllkCalInstance_g.m_Statistics.m_ulMaxTxFrameCountNmt) {
		EplDllkCalInstance_g.m_Statistics.m_ulMaxTxFrameCountNmt =
		    ulFrameCount;
	}

	if (ulFrameCount != 0) {	// NMT requests are in queue
		*pPriority_p = kEplDllAsyncReqPrioNmt;
		*puiCount_p = (unsigned int)ulFrameCount;
		goto Exit;
	}
	// get frame count of Tx FIFO with generic priority
	ShbError =
	    ShbCirGetReadBlockCount(EplDllkCalInstance_g.m_ShbInstanceTxGen,
				    &ulFrameCount);
	// returns kShbOk, kShbInvalidArg

	// error handling
	if (ShbError != kShbOk) {
		Ret = kEplNoResource;
		goto Exit;
	}

	if (ulFrameCount >
	    EplDllkCalInstance_g.m_Statistics.m_ulMaxTxFrameCountGen) {
		EplDllkCalInstance_g.m_Statistics.m_ulMaxTxFrameCountGen =
		    ulFrameCount;
	}

	*pPriority_p = kEplDllAsyncReqPrioGeneric;
	*puiCount_p = (unsigned int)ulFrameCount;

      Exit:
#else
	if (EplDllkCalInstance_g.m_uiFrameSizeNmt > 0) {
		*pPriority_p = kEplDllAsyncReqPrioNmt;
		*puiCount_p = 1;
	} else if (EplDllkCalInstance_g.m_uiFrameSizeGen > 0) {
		*pPriority_p = kEplDllAsyncReqPrioGeneric;
		*puiCount_p = 1;
	} else {
		*pPriority_p = kEplDllAsyncReqPrioGeneric;
		*puiCount_p = 0;
	}
#endif

	return Ret;
}

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalAsyncGetTxFrame()
//
// Description: returns Tx frames from FIFO with specified priority
//
// Parameters:  pFrame_p                = IN: pointer to buffer
//              puiFrameSize_p          = IN: max size of buffer
//                                        OUT: actual size of frame
//              Priority_p              = IN: priority
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalAsyncGetTxFrame(void *pFrame_p,
				     unsigned int *puiFrameSize_p,
				     tEplDllAsyncReqPriority Priority_p)
{
	tEplKernel Ret = kEplSuccessful;
#ifndef EPL_NO_FIFO
	tShbError ShbError;
	unsigned long ulFrameSize;

	switch (Priority_p) {
	case kEplDllAsyncReqPrioNmt:	// NMT request priority
		ShbError =
		    ShbCirReadDataBlock(EplDllkCalInstance_g.m_ShbInstanceTxNmt,
					(u8 *) pFrame_p, *puiFrameSize_p,
					&ulFrameSize);
		// returns kShbOk, kShbDataTruncated, kShbInvalidArg, kShbNoReadableData
		break;

	default:		// generic priority
		ShbError =
		    ShbCirReadDataBlock(EplDllkCalInstance_g.m_ShbInstanceTxGen,
					(u8 *) pFrame_p, *puiFrameSize_p,
					&ulFrameSize);
		// returns kShbOk, kShbDataTruncated, kShbInvalidArg, kShbNoReadableData
		break;

	}

	// error handling
	if (ShbError != kShbOk) {
		if (ShbError == kShbNoReadableData) {
			Ret = kEplDllAsyncTxBufferEmpty;
		} else {	// other error
			Ret = kEplNoResource;
		}
		goto Exit;
	}

	*puiFrameSize_p = (unsigned int)ulFrameSize;

      Exit:
#else
	switch (Priority_p) {
	case kEplDllAsyncReqPrioNmt:	// NMT request priority
		*puiFrameSize_p =
		    min(*puiFrameSize_p, EplDllkCalInstance_g.m_uiFrameSizeNmt);
		EPL_MEMCPY(pFrame_p, EplDllkCalInstance_g.m_abFrameNmt,
			   *puiFrameSize_p);
		EplDllkCalInstance_g.m_uiFrameSizeNmt = 0;
		break;

	default:		// generic priority
		*puiFrameSize_p =
		    min(*puiFrameSize_p, EplDllkCalInstance_g.m_uiFrameSizeGen);
		EPL_MEMCPY(pFrame_p, EplDllkCalInstance_g.m_abFrameGen,
			   *puiFrameSize_p);
		EplDllkCalInstance_g.m_uiFrameSizeGen = 0;
		break;
	}

#endif

	return Ret;
}

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalAsyncFrameReceived()
//
// Description: passes ASnd frame to receive FIFO.
//              It will be called only for frames with registered AsndServiceIds.
//
// Parameters:  none
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalAsyncFrameReceived(tEplFrameInfo * pFrameInfo_p)
{
	tEplKernel Ret = kEplSuccessful;
	tEplEvent Event;

	Event.m_EventSink = kEplEventSinkDlluCal;
	Event.m_EventType = kEplEventTypeAsndRx;
	Event.m_pArg = pFrameInfo_p->m_pFrame;
	Event.m_uiSize = pFrameInfo_p->m_uiFrameSize;
	// pass NetTime of frame to userspace
	Event.m_NetTime = pFrameInfo_p->m_NetTime;

	Ret = EplEventkPost(&Event);
	if (Ret != kEplSuccessful) {
		EplDllkCalInstance_g.m_Statistics.m_ulCurRxFrameCount++;
	} else {
		EplDllkCalInstance_g.m_Statistics.m_ulMaxRxFrameCount++;
	}

	return Ret;
}

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalAsyncSend()
//
// Description: puts the given frame into the transmit FIFO with the specified
//              priority.
//
// Parameters:  pFrameInfo_p            = frame info structure
//              Priority_p              = priority
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalAsyncSend(tEplFrameInfo * pFrameInfo_p,
			       tEplDllAsyncReqPriority Priority_p)
{
	tEplKernel Ret = kEplSuccessful;
	tEplEvent Event;
#ifndef EPL_NO_FIFO
	tShbError ShbError;

	switch (Priority_p) {
	case kEplDllAsyncReqPrioNmt:	// NMT request priority
		ShbError =
		    ShbCirWriteDataBlock(EplDllkCalInstance_g.
					 m_ShbInstanceTxNmt,
					 pFrameInfo_p->m_pFrame,
					 pFrameInfo_p->m_uiFrameSize);
		// returns kShbOk, kShbExceedDataSizeLimit, kShbBufferFull, kShbInvalidArg
		break;

	default:		// generic priority
		ShbError =
		    ShbCirWriteDataBlock(EplDllkCalInstance_g.
					 m_ShbInstanceTxGen,
					 pFrameInfo_p->m_pFrame,
					 pFrameInfo_p->m_uiFrameSize);
		// returns kShbOk, kShbExceedDataSizeLimit, kShbBufferFull, kShbInvalidArg
		break;

	}

	// error handling
	switch (ShbError) {
	case kShbOk:
		break;

	case kShbExceedDataSizeLimit:
		Ret = kEplDllAsyncTxBufferFull;
		break;

	case kShbBufferFull:
		Ret = kEplDllAsyncTxBufferFull;
		break;

	case kShbInvalidArg:
	default:
		Ret = kEplNoResource;
		break;
	}

#else

	switch (Priority_p) {
	case kEplDllAsyncReqPrioNmt:	// NMT request priority
		if (EplDllkCalInstance_g.m_uiFrameSizeNmt == 0) {
			EPL_MEMCPY(EplDllkCalInstance_g.m_abFrameNmt,
				   pFrameInfo_p->m_pFrame,
				   pFrameInfo_p->m_uiFrameSize);
			EplDllkCalInstance_g.m_uiFrameSizeNmt =
			    pFrameInfo_p->m_uiFrameSize;
		} else {
			Ret = kEplDllAsyncTxBufferFull;
			goto Exit;
		}
		break;

	default:		// generic priority
		if (EplDllkCalInstance_g.m_uiFrameSizeGen == 0) {
			EPL_MEMCPY(EplDllkCalInstance_g.m_abFrameGen,
				   pFrameInfo_p->m_pFrame,
				   pFrameInfo_p->m_uiFrameSize);
			EplDllkCalInstance_g.m_uiFrameSizeGen =
			    pFrameInfo_p->m_uiFrameSize;
		} else {
			Ret = kEplDllAsyncTxBufferFull;
			goto Exit;
		}
		break;
	}

#endif

	// post event to DLL
	Event.m_EventSink = kEplEventSinkDllk;
	Event.m_EventType = kEplEventTypeDllkFillTx;
	EPL_MEMSET(&Event.m_NetTime, 0x00, sizeof(Event.m_NetTime));
	Event.m_pArg = &Priority_p;
	Event.m_uiSize = sizeof(Priority_p);
	Ret = EplEventkPost(&Event);

#ifdef EPL_NO_FIFO
      Exit:
#endif

	return Ret;
}

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalAsyncClearBuffer()
//
// Description: clears the transmit buffer
//
// Parameters:  (none)
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalAsyncClearBuffer(void)
{
	tEplKernel Ret = kEplSuccessful;
#ifndef EPL_NO_FIFO
	tShbError ShbError;

	ShbError =
	    ShbCirResetBuffer(EplDllkCalInstance_g.m_ShbInstanceTxNmt, 1000,
			      NULL);
	ShbError =
	    ShbCirResetBuffer(EplDllkCalInstance_g.m_ShbInstanceTxGen, 1000,
			      NULL);

#else
	EplDllkCalInstance_g.m_uiFrameSizeNmt = 0;
	EplDllkCalInstance_g.m_uiFrameSizeGen = 0;
#endif

//    EPL_MEMSET(&EplDllkCalInstance_g.m_Statistics, 0, sizeof (tEplDllkCalStatistics));
	return Ret;
}

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalAsyncClearQueues()
//
// Description: clears the transmit buffer
//
// Parameters:  (none)
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
tEplKernel EplDllkCalAsyncClearQueues(void)
{
	tEplKernel Ret = kEplSuccessful;

	// clear MN asynchronous queues
	EplDllkCalInstance_g.m_uiNextQueueCnRequest = 0;
	EplDllkCalInstance_g.m_uiNextRequestQueue = 0;
	EplDllkCalInstance_g.m_uiReadIdentReq = 0;
	EplDllkCalInstance_g.m_uiWriteIdentReq = 0;
	EplDllkCalInstance_g.m_uiReadStatusReq = 0;
	EplDllkCalInstance_g.m_uiWriteStatusReq = 0;

	return Ret;
}
#endif

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalGetStatistics()
//
// Description: returns statistics of the asynchronous queues.
//
// Parameters:  ppStatistics            = statistics structure
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalGetStatistics(tEplDllkCalStatistics ** ppStatistics)
{
	tEplKernel Ret = kEplSuccessful;
#ifndef EPL_NO_FIFO
	tShbError ShbError;

	ShbError =
	    ShbCirGetReadBlockCount(EplDllkCalInstance_g.m_ShbInstanceTxNmt,
				    &EplDllkCalInstance_g.m_Statistics.
				    m_ulCurTxFrameCountNmt);
	ShbError =
	    ShbCirGetReadBlockCount(EplDllkCalInstance_g.m_ShbInstanceTxGen,
				    &EplDllkCalInstance_g.m_Statistics.
				    m_ulCurTxFrameCountGen);
//    ShbError = ShbCirGetReadBlockCount (EplDllkCalInstance_g.m_ShbInstanceRx, &EplDllkCalInstance_g.m_Statistics.m_ulCurRxFrameCount);

#else
	if (EplDllkCalInstance_g.m_uiFrameSizeNmt > 0) {
		EplDllkCalInstance_g.m_Statistics.m_ulCurTxFrameCountNmt = 1;
	} else {
		EplDllkCalInstance_g.m_Statistics.m_ulCurTxFrameCountNmt = 0;
	}
	if (EplDllkCalInstance_g.m_uiFrameSizeGen > 0) {
		EplDllkCalInstance_g.m_Statistics.m_ulCurTxFrameCountGen = 1;
	} else {
		EplDllkCalInstance_g.m_Statistics.m_ulCurTxFrameCountGen = 0;
	}
#endif

	*ppStatistics = &EplDllkCalInstance_g.m_Statistics;
	return Ret;
}

#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalIssueRequest()
//
// Description: issues a StatusRequest or a IdentRequest to the specified node.
//
// Parameters:  Service_p               = request service ID
//              uiNodeId_p              = node ID
//              bSoaFlag1_p             = flag1 for this node (transmit in SoA and PReq)
//                                        If 0xFF this flag is ignored.
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalIssueRequest(tEplDllReqServiceId Service_p,
				  unsigned int uiNodeId_p, u8 bSoaFlag1_p)
{
	tEplKernel Ret = kEplSuccessful;

	if (bSoaFlag1_p != 0xFF) {
		Ret = EplDllkSetFlag1OfNode(uiNodeId_p, bSoaFlag1_p);
		if (Ret != kEplSuccessful) {
			goto Exit;
		}
	}
	// add node to appropriate request queue
	switch (Service_p) {
	case kEplDllReqServiceIdent:
		{
			if (((EplDllkCalInstance_g.m_uiWriteIdentReq +
			      1) %
			     tabentries(EplDllkCalInstance_g.
					m_auiQueueIdentReq))
			    == EplDllkCalInstance_g.m_uiReadIdentReq) {	// queue is full
				Ret = kEplDllAsyncTxBufferFull;
				goto Exit;
			}
			EplDllkCalInstance_g.
			    m_auiQueueIdentReq[EplDllkCalInstance_g.
					       m_uiWriteIdentReq] = uiNodeId_p;
			EplDllkCalInstance_g.m_uiWriteIdentReq =
			    (EplDllkCalInstance_g.m_uiWriteIdentReq +
			     1) %
			    tabentries(EplDllkCalInstance_g.m_auiQueueIdentReq);
			break;
		}

	case kEplDllReqServiceStatus:
		{
			if (((EplDllkCalInstance_g.m_uiWriteStatusReq +
			      1) %
			     tabentries(EplDllkCalInstance_g.
					m_auiQueueStatusReq))
			    == EplDllkCalInstance_g.m_uiReadStatusReq) {	// queue is full
				Ret = kEplDllAsyncTxBufferFull;
				goto Exit;
			}
			EplDllkCalInstance_g.
			    m_auiQueueStatusReq[EplDllkCalInstance_g.
						m_uiWriteStatusReq] =
			    uiNodeId_p;
			EplDllkCalInstance_g.m_uiWriteStatusReq =
			    (EplDllkCalInstance_g.m_uiWriteStatusReq +
			     1) %
			    tabentries(EplDllkCalInstance_g.
				       m_auiQueueStatusReq);
			break;
		}

	default:
		{
			Ret = kEplDllInvalidParam;
			goto Exit;
		}
	}

      Exit:
	return Ret;
}

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalAsyncGetSoaRequest()
//
// Description: returns next request for SoA. This function is called by DLLk module.
//
// Parameters:  pReqServiceId_p         = pointer to request service ID
//                                        IN: available request for MN NMT or generic request queue (Flag2.PR)
//                                            or kEplDllReqServiceNo if queues are empty
//                                        OUT: next request
//              puiNodeId_p             = OUT: pointer to node ID of next request
//                                             = EPL_C_ADR_INVALID, if request is self addressed
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalAsyncGetSoaRequest(tEplDllReqServiceId * pReqServiceId_p,
					unsigned int *puiNodeId_p)
{
	tEplKernel Ret = kEplSuccessful;
	unsigned int uiCount;

//    *pReqServiceId_p = kEplDllReqServiceNo;

	for (uiCount = EPL_DLLKCAL_MAX_QUEUES; uiCount > 0; uiCount--) {
		switch (EplDllkCalInstance_g.m_uiNextRequestQueue) {
		case 0:
			{	// CnGenReq
				for (;
				     EplDllkCalInstance_g.
				     m_uiNextQueueCnRequest <
				     (tabentries
				      (EplDllkCalInstance_g.
				       m_auiQueueCnRequests) / 2);
				     EplDllkCalInstance_g.
				     m_uiNextQueueCnRequest++) {
					if (EplDllkCalInstance_g.m_auiQueueCnRequests[EplDllkCalInstance_g.m_uiNextQueueCnRequest] > 0) {	// non empty queue found
						// remove one request from queue
						EplDllkCalInstance_g.
						    m_auiQueueCnRequests
						    [EplDllkCalInstance_g.
						     m_uiNextQueueCnRequest]--;
						*puiNodeId_p =
						    EplDllkCalInstance_g.
						    m_uiNextQueueCnRequest + 1;
						*pReqServiceId_p =
						    kEplDllReqServiceUnspecified;
						EplDllkCalInstance_g.
						    m_uiNextQueueCnRequest++;
						if (EplDllkCalInstance_g.m_uiNextQueueCnRequest >= (tabentries(EplDllkCalInstance_g.m_auiQueueCnRequests) / 2)) {	// last node reached
							// continue with CnNmtReq queue at next SoA
							EplDllkCalInstance_g.
							    m_uiNextRequestQueue
							    = 1;
						}
						goto Exit;
					}
				}
				// all CnGenReq queues are empty -> continue with CnNmtReq queue
				EplDllkCalInstance_g.m_uiNextRequestQueue = 1;
				break;
			}

		case 1:
			{	// CnNmtReq
				for (;
				     EplDllkCalInstance_g.
				     m_uiNextQueueCnRequest <
				     tabentries(EplDllkCalInstance_g.
						m_auiQueueCnRequests);
				     EplDllkCalInstance_g.
				     m_uiNextQueueCnRequest++) {
					if (EplDllkCalInstance_g.m_auiQueueCnRequests[EplDllkCalInstance_g.m_uiNextQueueCnRequest] > 0) {	// non empty queue found
						// remove one request from queue
						EplDllkCalInstance_g.
						    m_auiQueueCnRequests
						    [EplDllkCalInstance_g.
						     m_uiNextQueueCnRequest]--;
						*puiNodeId_p =
						    EplDllkCalInstance_g.
						    m_uiNextQueueCnRequest + 1 -
						    (tabentries
						     (EplDllkCalInstance_g.
						      m_auiQueueCnRequests) /
						     2);
						*pReqServiceId_p =
						    kEplDllReqServiceNmtRequest;
						EplDllkCalInstance_g.
						    m_uiNextQueueCnRequest++;
						if (EplDllkCalInstance_g.m_uiNextQueueCnRequest > tabentries(EplDllkCalInstance_g.m_auiQueueCnRequests)) {	// last node reached
							// restart CnGenReq queue
							EplDllkCalInstance_g.
							    m_uiNextQueueCnRequest
							    = 0;
							// continue with MnGenReq queue at next SoA
							EplDllkCalInstance_g.
							    m_uiNextRequestQueue
							    = 2;
						}
						goto Exit;
					}
				}
				// restart CnGenReq queue
				EplDllkCalInstance_g.m_uiNextQueueCnRequest = 0;
				// all CnNmtReq queues are empty -> continue with MnGenReq queue
				EplDllkCalInstance_g.m_uiNextRequestQueue = 2;
				break;
			}

		case 2:
			{	// MnNmtReq and MnGenReq
				// next queue will be MnIdentReq queue
				EplDllkCalInstance_g.m_uiNextRequestQueue = 3;
				if (*pReqServiceId_p != kEplDllReqServiceNo) {
					*puiNodeId_p = EPL_C_ADR_INVALID;	// DLLk must exchange this with the actual node ID
					goto Exit;
				}
				break;
			}

		case 3:
			{	// MnIdentReq
				// next queue will be MnStatusReq queue
				EplDllkCalInstance_g.m_uiNextRequestQueue = 4;
				if (EplDllkCalInstance_g.m_uiReadIdentReq != EplDllkCalInstance_g.m_uiWriteIdentReq) {	// queue is not empty
					*puiNodeId_p =
					    EplDllkCalInstance_g.
					    m_auiQueueIdentReq
					    [EplDllkCalInstance_g.
					     m_uiReadIdentReq];
					EplDllkCalInstance_g.m_uiReadIdentReq =
					    (EplDllkCalInstance_g.
					     m_uiReadIdentReq +
					     1) %
					    tabentries(EplDllkCalInstance_g.
						       m_auiQueueIdentReq);
					*pReqServiceId_p =
					    kEplDllReqServiceIdent;
					goto Exit;
				}
				break;
			}

		case 4:
			{	// MnStatusReq
				// next queue will be CnGenReq queue
				EplDllkCalInstance_g.m_uiNextRequestQueue = 0;
				if (EplDllkCalInstance_g.m_uiReadStatusReq != EplDllkCalInstance_g.m_uiWriteStatusReq) {	// queue is not empty
					*puiNodeId_p =
					    EplDllkCalInstance_g.
					    m_auiQueueStatusReq
					    [EplDllkCalInstance_g.
					     m_uiReadStatusReq];
					EplDllkCalInstance_g.m_uiReadStatusReq =
					    (EplDllkCalInstance_g.
					     m_uiReadStatusReq +
					     1) %
					    tabentries(EplDllkCalInstance_g.
						       m_auiQueueStatusReq);
					*pReqServiceId_p =
					    kEplDllReqServiceStatus;
					goto Exit;
				}
				break;
			}

		}
	}

      Exit:
	return Ret;
}

//---------------------------------------------------------------------------
//
// Function:    EplDllkCalAsyncSetPendingRequests()
//
// Description: sets the pending asynchronous frame requests of the specified node.
//              This will add the node to the asynchronous request scheduler.
//
// Parameters:  uiNodeId_p              = node ID
//              AsyncReqPrio_p          = asynchronous request priority
//              uiCount_p               = count of asynchronous frames
//
// Returns:     tEplKernel              = error code
//
//
// State:
//
//---------------------------------------------------------------------------

tEplKernel EplDllkCalAsyncSetPendingRequests(unsigned int uiNodeId_p,
					     tEplDllAsyncReqPriority
					     AsyncReqPrio_p,
					     unsigned int uiCount_p)
{
	tEplKernel Ret = kEplSuccessful;

	// add node to appropriate request queue
	switch (AsyncReqPrio_p) {
	case kEplDllAsyncReqPrioNmt:
		{
			uiNodeId_p--;
			if (uiNodeId_p >=
			    (tabentries
			     (EplDllkCalInstance_g.m_auiQueueCnRequests) / 2)) {
				Ret = kEplDllInvalidParam;
				goto Exit;
			}
			uiNodeId_p +=
			    tabentries(EplDllkCalInstance_g.
				       m_auiQueueCnRequests) / 2;
			EplDllkCalInstance_g.m_auiQueueCnRequests[uiNodeId_p] =
			    uiCount_p;
			break;
		}

	default:
		{
			uiNodeId_p--;
			if (uiNodeId_p >=
			    (tabentries
			     (EplDllkCalInstance_g.m_auiQueueCnRequests) / 2)) {
				Ret = kEplDllInvalidParam;
				goto Exit;
			}
			EplDllkCalInstance_g.m_auiQueueCnRequests[uiNodeId_p] =
			    uiCount_p;
			break;
		}
	}

      Exit:
	return Ret;
}
#endif //(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)

//=========================================================================//
//                                                                         //
//          P R I V A T E   F U N C T I O N S                              //
//                                                                         //
//=========================================================================//

//---------------------------------------------------------------------------
//  Callback handler for new data signaling
//---------------------------------------------------------------------------

#ifndef EPL_NO_FIFO
/*static void  EplDllkCalTxNmtSignalHandler (
    tShbInstance pShbRxInstance_p,
    unsigned long ulDataSize_p)
{
tEplKernel      Ret = kEplSuccessful;
tEplEvent       Event;
tEplDllAsyncReqPriority Priority;
#ifndef EPL_NO_FIFO
tShbError   ShbError;
unsigned long   ulBlockCount;

    ShbError = ShbCirGetReadBlockCount (EplDllkCalInstance_g.m_ShbInstanceTxNmt, &ulBlockCount);
    if (ulBlockCount > EplDllkCalInstance_g.m_Statistics.m_ulMaxTxFrameCountNmt)
    {
        EplDllkCalInstance_g.m_Statistics.m_ulMaxTxFrameCountNmt = ulBlockCount;
    }

#endif

    // post event to DLL
    Priority = kEplDllAsyncReqPrioNmt;
    Event.m_EventSink = kEplEventSinkDllk;
    Event.m_EventType = kEplEventTypeDllkFillTx;
    EPL_MEMSET(&Event.m_NetTime, 0x00, sizeof(Event.m_NetTime));
    Event.m_pArg = &Priority;
    Event.m_uiSize = sizeof(Priority);
    Ret = EplEventkPost(&Event);

}

static void  EplDllkCalTxGenSignalHandler (
    tShbInstance pShbRxInstance_p,
    unsigned long ulDataSize_p)
{
tEplKernel      Ret = kEplSuccessful;
tEplEvent       Event;
tEplDllAsyncReqPriority Priority;
#ifndef EPL_NO_FIFO
tShbError   ShbError;
unsigned long   ulBlockCount;

    ShbError = ShbCirGetReadBlockCount (EplDllkCalInstance_g.m_ShbInstanceTxGen, &ulBlockCount);
    if (ulBlockCount > EplDllkCalInstance_g.m_Statistics.m_ulMaxTxFrameCountGen)
    {
        EplDllkCalInstance_g.m_Statistics.m_ulMaxTxFrameCountGen = ulBlockCount;
    }

#endif

    // post event to DLL
    Priority = kEplDllAsyncReqPrioGeneric;
    Event.m_EventSink = kEplEventSinkDllk;
    Event.m_EventType = kEplEventTypeDllkFillTx;
    EPL_MEMSET(&Event.m_NetTime, 0x00, sizeof(Event.m_NetTime));
    Event.m_pArg = &Priority;
    Event.m_uiSize = sizeof(Priority);
    Ret = EplEventkPost(&Event);

}
*/
#endif

#endif // #if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_DLLK)) != 0)

// EOF
