blob: 9e22641055c61cd7066410603c73d74e99d198fa [file] [log] [blame]
/****************************************************************************
(c) SYSTEC electronic GmbH, D-07973 Greiz, August-Bebel-Str. 29
www.systec-electronic.com
Project: openPOWERLINK
Description: source file for kernel DLL 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: EplDllk.c,v $
$Author: D.Krueger $
$Revision: 1.21 $ $Date: 2008/11/13 17:13:09 $
$State: Exp $
Build Environment:
GCC V3.4
-------------------------------------------------------------------------
Revision History:
2006/06/12 d.k.: start of the implementation, version 1.00
****************************************************************************/
#include "kernel/EplDllk.h"
#include "kernel/EplDllkCal.h"
#include "kernel/EplEventk.h"
#include "kernel/EplNmtk.h"
#include "edrv.h"
#include "Benchmark.h"
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_PDOK)) != 0)
#include "kernel/EplPdok.h"
#endif
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_VETH)) != 0)
#include "kernel/VirtualEthernet.h"
#endif
//#if EPL_TIMER_USE_HIGHRES != FALSE
#include "kernel/EplTimerHighResk.h"
//#endif
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_DLLK)) != 0)
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMTK)) == 0)
#error "EPL module DLLK needs EPL module NMTK!"
#endif
#if (EPL_DLL_PRES_READY_AFTER_SOA != FALSE) && (EPL_DLL_PRES_READY_AFTER_SOC != FALSE)
#error "EPL module DLLK: select only one of EPL_DLL_PRES_READY_AFTER_SOA and EPL_DLL_PRES_READY_AFTER_SOC."
#endif
#if ((EPL_DLL_PRES_READY_AFTER_SOA != FALSE) || (EPL_DLL_PRES_READY_AFTER_SOC != FALSE)) \
&& (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) == 0)
#error "EPL module DLLK: currently, EPL_DLL_PRES_READY_AFTER_* is not supported if EPL_MODULE_NMT_MN is enabled."
#endif
#if (EDRV_FAST_TXFRAMES == FALSE) && \
((EPL_DLL_PRES_READY_AFTER_SOA != FALSE) || (EPL_DLL_PRES_READY_AFTER_SOC != FALSE))
#error "EPL module DLLK: EPL_DLL_PRES_READY_AFTER_* is enabled, but not EDRV_FAST_TXFRAMES."
#endif
/***************************************************************************/
/* */
/* */
/* G L O B A L D E F I N I T I O N S */
/* */
/* */
/***************************************************************************/
//---------------------------------------------------------------------------
// const defines
//---------------------------------------------------------------------------
// TracePoint support for realtime-debugging
#ifdef _DBG_TRACE_POINTS_
void PUBLIC TgtDbgSignalTracePoint(BYTE bTracePointNumber_p);
void PUBLIC TgtDbgPostTraceValue(DWORD dwTraceValue_p);
#define TGT_DBG_SIGNAL_TRACE_POINT(p) TgtDbgSignalTracePoint(p)
#define TGT_DBG_POST_TRACE_VALUE(v) TgtDbgPostTraceValue(v)
#else
#define TGT_DBG_SIGNAL_TRACE_POINT(p)
#define TGT_DBG_POST_TRACE_VALUE(v)
#endif
#define EPL_DLLK_DBG_POST_TRACE_VALUE(Event_p, uiNodeId_p, wErrorCode_p) \
TGT_DBG_POST_TRACE_VALUE((kEplEventSinkDllk << 28) | (Event_p << 24) \
| (uiNodeId_p << 16) | wErrorCode_p)
/***************************************************************************/
/* */
/* */
/* C L A S S EplDllk */
/* */
/* */
/***************************************************************************/
//
// Description:
//
//
/***************************************************************************/
//=========================================================================//
// //
// P R I V A T E D E F I N I T I O N S //
// //
//=========================================================================//
//---------------------------------------------------------------------------
// const defines
//---------------------------------------------------------------------------
// defines for indexes of tEplDllInstance.m_pTxFrameInfo
#define EPL_DLLK_TXFRAME_IDENTRES 0 // IdentResponse on CN / MN
#define EPL_DLLK_TXFRAME_STATUSRES 1 // StatusResponse on CN / MN
#define EPL_DLLK_TXFRAME_NMTREQ 2 // NMT Request from FIFO on CN / MN
#define EPL_DLLK_TXFRAME_NONEPL 3 // non-EPL frame from FIFO on CN / MN
#define EPL_DLLK_TXFRAME_PRES 4 // PRes on CN / MN
#define EPL_DLLK_TXFRAME_SOC 5 // SoC on MN
#define EPL_DLLK_TXFRAME_SOA 6 // SoA on MN
#define EPL_DLLK_TXFRAME_PREQ 7 // PReq on MN
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
#define EPL_DLLK_TXFRAME_COUNT (7 + EPL_D_NMT_MaxCNNumber_U8 + 2) // on MN: 7 + MaxPReq of regular CNs + 1 Diag + 1 Router
#else
#define EPL_DLLK_TXFRAME_COUNT 5 // on CN: 5
#endif
#define EPL_DLLK_BUFLEN_EMPTY 0 // buffer is empty
#define EPL_DLLK_BUFLEN_FILLING 1 // just the buffer is being filled
#define EPL_DLLK_BUFLEN_MIN 60 // minimum ethernet frame length
//---------------------------------------------------------------------------
// local types
//---------------------------------------------------------------------------
typedef enum {
kEplDllGsInit = 0x00, // MN/CN: initialisation (< PreOp2)
kEplDllCsWaitPreq = 0x01, // CN: wait for PReq frame
kEplDllCsWaitSoc = 0x02, // CN: wait for SoC frame
kEplDllCsWaitSoa = 0x03, // CN: wait for SoA frame
kEplDllMsNonCyclic = 0x04, // MN: reduced EPL cycle (PreOp1)
kEplDllMsWaitSocTrig = 0x05, // MN: wait for SoC trigger (cycle timer)
kEplDllMsWaitPreqTrig = 0x06, // MN: wait for (first) PReq trigger (WaitSoCPReq_U32)
kEplDllMsWaitPres = 0x07, // MN: wait for PRes frame from CN
kEplDllMsWaitSoaTrig = 0x08, // MN: wait for SoA trigger (PRes transmitted)
kEplDllMsWaitAsndTrig = 0x09, // MN: wait for ASnd trigger (SoA transmitted)
kEplDllMsWaitAsnd = 0x0A, // MN: wait for ASnd frame if SoA contained invitation
} tEplDllState;
typedef struct {
BYTE m_be_abSrcMac[6];
tEdrvTxBuffer *m_pTxBuffer; // Buffers for Tx-Frames
unsigned int m_uiMaxTxFrames;
BYTE m_bFlag1; // Flag 1 with EN, EC for PRes, StatusRes
BYTE m_bMnFlag1; // Flag 1 with EA, ER from PReq, SoA of MN
BYTE m_bFlag2; // Flag 2 with PR and RS for PRes, StatusRes, IdentRes
tEplDllConfigParam m_DllConfigParam;
tEplDllIdentParam m_DllIdentParam;
tEplDllState m_DllState;
tEplDllkCbAsync m_pfnCbAsync;
tEplDllAsndFilter m_aAsndFilter[EPL_DLL_MAX_ASND_SERVICE_ID];
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
tEplDllkNodeInfo *m_pFirstNodeInfo;
tEplDllkNodeInfo *m_pCurNodeInfo;
tEplDllkNodeInfo m_aNodeInfo[EPL_NMT_MAX_NODE_ID];
tEplDllReqServiceId m_LastReqServiceId;
unsigned int m_uiLastTargetNodeId;
#endif
#if EPL_TIMER_USE_HIGHRES != FALSE
tEplTimerHdl m_TimerHdlCycle; // used for EPL cycle monitoring on CN and generation on MN
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
tEplTimerHdl m_TimerHdlResponse; // used for CN response monitoring
#endif //(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
#endif
unsigned int m_uiCycleCount; // cycle counter (needed for multiplexed cycle support)
unsigned long long m_ullFrameTimeout; // frame timeout (cycle length + loss of frame tolerance)
} tEplDllkInstance;
//---------------------------------------------------------------------------
// local vars
//---------------------------------------------------------------------------
// if no dynamic memory allocation shall be used
// define structures statically
static tEplDllkInstance EplDllkInstance_g;
static tEdrvTxBuffer aEplDllkTxBuffer_l[EPL_DLLK_TXFRAME_COUNT];
//---------------------------------------------------------------------------
// local function prototypes
//---------------------------------------------------------------------------
// change DLL state on event
static tEplKernel EplDllkChangeState(tEplNmtEvent NmtEvent_p,
tEplNmtState NmtState_p);
// called from EdrvInterruptHandler()
static void EplDllkCbFrameReceived(tEdrvRxBuffer * pRxBuffer_p);
// called from EdrvInterruptHandler()
static void EplDllkCbFrameTransmitted(tEdrvTxBuffer * pTxBuffer_p);
// check frame and set missing information
static tEplKernel EplDllkCheckFrame(tEplFrame * pFrame_p,
unsigned int uiFrameSize_p);
// called by high resolution timer module to monitor EPL cycle as CN
#if EPL_TIMER_USE_HIGHRES != FALSE
static tEplKernel PUBLIC EplDllkCbCnTimer(tEplTimerEventArg * pEventArg_p);
#endif
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
// MN: returns internal node info structure
static tEplDllkNodeInfo *EplDllkGetNodeInfo(unsigned int uiNodeId_p);
// transmit SoA
static tEplKernel EplDllkMnSendSoa(tEplNmtState NmtState_p,
tEplDllState * pDllStateProposed_p,
BOOL fEnableInvitation_p);
static tEplKernel EplDllkMnSendSoc(void);
static tEplKernel EplDllkMnSendPreq(tEplNmtState NmtState_p,
tEplDllState * pDllStateProposed_p);
static tEplKernel EplDllkAsyncFrameNotReceived(tEplDllReqServiceId
ReqServiceId_p,
unsigned int uiNodeId_p);
static tEplKernel PUBLIC EplDllkCbMnTimerCycle(tEplTimerEventArg * pEventArg_p);
static tEplKernel PUBLIC EplDllkCbMnTimerResponse(tEplTimerEventArg *
pEventArg_p);
#endif
//=========================================================================//
// //
// P U B L I C F U N C T I O N S //
// //
//=========================================================================//
//---------------------------------------------------------------------------
//
// Function: EplDllkAddInstance()
//
// Description: add and initialize new instance of EPL stack
//
// Parameters: pInitParam_p = initialisation parameters like MAC address
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkAddInstance(tEplDllkInitParam * pInitParam_p)
{
tEplKernel Ret = kEplSuccessful;
unsigned int uiIndex;
tEdrvInitParam EdrvInitParam;
// reset instance structure
EPL_MEMSET(&EplDllkInstance_g, 0, sizeof(EplDllkInstance_g));
#if EPL_TIMER_USE_HIGHRES != FALSE
Ret = EplTimerHighReskInit();
if (Ret != kEplSuccessful) { // error occured while initializing high resolution timer module
goto Exit;
}
#endif
// if dynamic memory allocation available
// allocate instance structure
// allocate TPDO and RPDO table with default size
// initialize and link pointers in instance structure to frame tables
EplDllkInstance_g.m_pTxBuffer = aEplDllkTxBuffer_l;
EplDllkInstance_g.m_uiMaxTxFrames =
sizeof(aEplDllkTxBuffer_l) / sizeof(tEdrvTxBuffer);
// initialize state
EplDllkInstance_g.m_DllState = kEplDllGsInit;
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
// set up node info structure
for (uiIndex = 0; uiIndex < tabentries(EplDllkInstance_g.m_aNodeInfo);
uiIndex++) {
EplDllkInstance_g.m_aNodeInfo[uiIndex].m_uiNodeId = uiIndex + 1;
EplDllkInstance_g.m_aNodeInfo[uiIndex].m_wPresPayloadLimit =
0xFFFF;
}
#endif
// initialize Edrv
EPL_MEMCPY(EdrvInitParam.m_abMyMacAddr, pInitParam_p->m_be_abSrcMac, 6);
EdrvInitParam.m_pfnRxHandler = EplDllkCbFrameReceived;
EdrvInitParam.m_pfnTxHandler = EplDllkCbFrameTransmitted;
Ret = EdrvInit(&EdrvInitParam);
if (Ret != kEplSuccessful) { // error occured while initializing ethernet driver
goto Exit;
}
// copy local MAC address from Ethernet driver back to local instance structure
// because Ethernet driver may have read it from controller EEPROM
EPL_MEMCPY(EplDllkInstance_g.m_be_abSrcMac, EdrvInitParam.m_abMyMacAddr,
6);
EPL_MEMCPY(pInitParam_p->m_be_abSrcMac, EdrvInitParam.m_abMyMacAddr, 6);
// initialize TxBuffer array
for (uiIndex = 0; uiIndex < EplDllkInstance_g.m_uiMaxTxFrames;
uiIndex++) {
EplDllkInstance_g.m_pTxBuffer[uiIndex].m_pbBuffer = NULL;
}
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_VETH)) != 0)
Ret = VEthAddInstance(pInitParam_p);
#endif
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkDelInstance()
//
// Description: deletes an instance of EPL stack
//
// Parameters: (none)
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkDelInstance(void)
{
tEplKernel Ret = kEplSuccessful;
// reset state
EplDllkInstance_g.m_DllState = kEplDllGsInit;
#if EPL_TIMER_USE_HIGHRES != FALSE
Ret = EplTimerHighReskDelInstance();
#endif
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_VETH)) != 0)
Ret = VEthDelInstance();
#endif
Ret = EdrvShutdown();
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkCreateTxFrame
//
// Description: creates the buffer for a Tx frame and registers it to the
// ethernet driver
//
// Parameters: puiHandle_p = OUT: handle to frame buffer
// ppFrame_p = OUT: pointer to pointer of EPL frame
// puiFrameSize_p = IN/OUT: pointer to size of frame
// returned size is always equal or larger than
// requested size, if that is not possible
// an error will be returned
// MsgType_p = EPL message type
// ServiceId_p = Service ID in case of ASnd frame, otherwise
// kEplDllAsndNotDefined
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkCreateTxFrame(unsigned int *puiHandle_p,
tEplFrame ** ppFrame_p,
unsigned int *puiFrameSize_p,
tEplMsgType MsgType_p,
tEplDllAsndServiceId ServiceId_p)
{
tEplKernel Ret = kEplSuccessful;
tEplFrame *pTxFrame;
unsigned int uiHandle = EplDllkInstance_g.m_uiMaxTxFrames;
tEdrvTxBuffer *pTxBuffer = NULL;
if (MsgType_p == kEplMsgTypeAsnd) {
// search for fixed Tx buffers
if (ServiceId_p == kEplDllAsndIdentResponse) {
uiHandle = EPL_DLLK_TXFRAME_IDENTRES;
} else if (ServiceId_p == kEplDllAsndStatusResponse) {
uiHandle = EPL_DLLK_TXFRAME_STATUSRES;
} else if ((ServiceId_p == kEplDllAsndNmtRequest)
|| (ServiceId_p == kEplDllAsndNmtCommand)) {
uiHandle = EPL_DLLK_TXFRAME_NMTREQ;
}
if (uiHandle >= EplDllkInstance_g.m_uiMaxTxFrames) { // look for free entry
uiHandle = EPL_DLLK_TXFRAME_PREQ;
pTxBuffer = &EplDllkInstance_g.m_pTxBuffer[uiHandle];
for (; uiHandle < EplDllkInstance_g.m_uiMaxTxFrames;
uiHandle++, pTxBuffer++) {
if (pTxBuffer->m_pbBuffer == NULL) { // free entry found
break;
}
}
}
} else if (MsgType_p == kEplMsgTypeNonEpl) {
uiHandle = EPL_DLLK_TXFRAME_NONEPL;
} else if (MsgType_p == kEplMsgTypePres) {
uiHandle = EPL_DLLK_TXFRAME_PRES;
} else if (MsgType_p == kEplMsgTypeSoc) {
uiHandle = EPL_DLLK_TXFRAME_SOC;
} else if (MsgType_p == kEplMsgTypeSoa) {
uiHandle = EPL_DLLK_TXFRAME_SOA;
} else { // look for free entry
uiHandle = EPL_DLLK_TXFRAME_PREQ;
pTxBuffer = &EplDllkInstance_g.m_pTxBuffer[uiHandle];
for (; uiHandle < EplDllkInstance_g.m_uiMaxTxFrames;
uiHandle++, pTxBuffer++) {
if (pTxBuffer->m_pbBuffer == NULL) { // free entry found
break;
}
}
if (pTxBuffer->m_pbBuffer != NULL) {
Ret = kEplEdrvNoFreeBufEntry;
goto Exit;
}
}
// test if requested entry is free
pTxBuffer = &EplDllkInstance_g.m_pTxBuffer[uiHandle];
if (pTxBuffer->m_pbBuffer != NULL) { // entry is not free
Ret = kEplEdrvNoFreeBufEntry;
goto Exit;
}
// setup Tx buffer
pTxBuffer->m_EplMsgType = MsgType_p;
pTxBuffer->m_uiMaxBufferLen = *puiFrameSize_p;
Ret = EdrvAllocTxMsgBuffer(pTxBuffer);
if (Ret != kEplSuccessful) { // error occured while registering Tx frame
goto Exit;
}
// because buffer size may be larger than requested
// memorize real length of frame
pTxBuffer->m_uiTxMsgLen = *puiFrameSize_p;
// fill whole frame with 0
EPL_MEMSET(pTxBuffer->m_pbBuffer, 0, pTxBuffer->m_uiMaxBufferLen);
pTxFrame = (tEplFrame *) pTxBuffer->m_pbBuffer;
if (MsgType_p != kEplMsgTypeNonEpl) { // fill out Frame only if it is an EPL frame
// ethertype
AmiSetWordToBe(&pTxFrame->m_be_wEtherType,
EPL_C_DLL_ETHERTYPE_EPL);
// source node ID
AmiSetByteToLe(&pTxFrame->m_le_bSrcNodeId,
(BYTE) EplDllkInstance_g.m_DllConfigParam.
m_uiNodeId);
// source MAC address
EPL_MEMCPY(&pTxFrame->m_be_abSrcMac[0],
&EplDllkInstance_g.m_be_abSrcMac[0], 6);
switch (MsgType_p) {
case kEplMsgTypeAsnd:
// destination MAC address
AmiSetQword48ToBe(&pTxFrame->m_be_abDstMac[0],
EPL_C_DLL_MULTICAST_ASND);
// destination node ID
switch (ServiceId_p) {
case kEplDllAsndIdentResponse:
case kEplDllAsndStatusResponse:
{ // IdentResponses and StatusResponses are Broadcast
AmiSetByteToLe(&pTxFrame->
m_le_bDstNodeId,
(BYTE)
EPL_C_ADR_BROADCAST);
break;
}
default:
break;
}
// ASnd Service ID
AmiSetByteToLe(&pTxFrame->m_Data.m_Asnd.m_le_bServiceId,
ServiceId_p);
break;
case kEplMsgTypeSoc:
// destination MAC address
AmiSetQword48ToBe(&pTxFrame->m_be_abDstMac[0],
EPL_C_DLL_MULTICAST_SOC);
// destination node ID
AmiSetByteToLe(&pTxFrame->m_le_bDstNodeId,
(BYTE) EPL_C_ADR_BROADCAST);
// reset Flags
//AmiSetByteToLe(&pTxFrame->m_Data.m_Soc.m_le_bFlag1, (BYTE) 0);
//AmiSetByteToLe(&pTxFrame->m_Data.m_Soc.m_le_bFlag2, (BYTE) 0);
break;
case kEplMsgTypeSoa:
// destination MAC address
AmiSetQword48ToBe(&pTxFrame->m_be_abDstMac[0],
EPL_C_DLL_MULTICAST_SOA);
// destination node ID
AmiSetByteToLe(&pTxFrame->m_le_bDstNodeId,
(BYTE) EPL_C_ADR_BROADCAST);
// reset Flags
//AmiSetByteToLe(&pTxFrame->m_Data.m_Soa.m_le_bFlag1, (BYTE) 0);
//AmiSetByteToLe(&pTxFrame->m_Data.m_Soa.m_le_bFlag2, (BYTE) 0);
// EPL profile version
AmiSetByteToLe(&pTxFrame->m_Data.m_Soa.m_le_bEplVersion,
(BYTE) EPL_SPEC_VERSION);
break;
case kEplMsgTypePres:
// destination MAC address
AmiSetQword48ToBe(&pTxFrame->m_be_abDstMac[0],
EPL_C_DLL_MULTICAST_PRES);
// destination node ID
AmiSetByteToLe(&pTxFrame->m_le_bDstNodeId,
(BYTE) EPL_C_ADR_BROADCAST);
// reset Flags
//AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.m_le_bFlag1, (BYTE) 0);
//AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.m_le_bFlag2, (BYTE) 0);
// PDO size
//AmiSetWordToLe(&pTxFrame->m_Data.m_Pres.m_le_wSize, 0);
break;
case kEplMsgTypePreq:
// reset Flags
//AmiSetByteToLe(&pTxFrame->m_Data.m_Preq.m_le_bFlag1, (BYTE) 0);
//AmiSetByteToLe(&pTxFrame->m_Data.m_Preq.m_le_bFlag2, (BYTE) 0);
// PDO size
//AmiSetWordToLe(&pTxFrame->m_Data.m_Preq.m_le_wSize, 0);
break;
default:
break;
}
// EPL message type
AmiSetByteToLe(&pTxFrame->m_le_bMessageType, (BYTE) MsgType_p);
}
*ppFrame_p = pTxFrame;
*puiFrameSize_p = pTxBuffer->m_uiMaxBufferLen;
*puiHandle_p = uiHandle;
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkDeleteTxFrame
//
// Description: deletes the buffer for a Tx frame and frees it in the
// ethernet driver
//
// Parameters: uiHandle_p = IN: handle to frame buffer
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkDeleteTxFrame(unsigned int uiHandle_p)
{
tEplKernel Ret = kEplSuccessful;
tEdrvTxBuffer *pTxBuffer = NULL;
if (uiHandle_p >= EplDllkInstance_g.m_uiMaxTxFrames) { // handle is not valid
Ret = kEplDllIllegalHdl;
goto Exit;
}
pTxBuffer = &EplDllkInstance_g.m_pTxBuffer[uiHandle_p];
// mark buffer as free so that frame will not be send in future anymore
// $$$ d.k. What's up with running transmissions?
pTxBuffer->m_uiTxMsgLen = EPL_DLLK_BUFLEN_EMPTY;
pTxBuffer->m_pbBuffer = NULL;
// delete Tx buffer
Ret = EdrvReleaseTxMsgBuffer(pTxBuffer);
if (Ret != kEplSuccessful) { // error occured while releasing Tx frame
goto Exit;
}
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkProcess
//
// Description: process the passed event
//
// Parameters: pEvent_p = event to be processed
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkProcess(tEplEvent * pEvent_p)
{
tEplKernel Ret = kEplSuccessful;
tEplFrame *pTxFrame;
tEdrvTxBuffer *pTxBuffer;
unsigned int uiHandle;
unsigned int uiFrameSize;
BYTE abMulticastMac[6];
tEplDllAsyncReqPriority AsyncReqPriority;
unsigned int uiFrameCount;
tEplNmtState NmtState;
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_PDOK)) != 0)
tEplFrameInfo FrameInfo;
#endif
switch (pEvent_p->m_EventType) {
case kEplEventTypeDllkCreate:
{
// $$$ reset ethernet driver
NmtState = *((tEplNmtState *) pEvent_p->m_pArg);
// initialize flags for PRes and StatusRes
EplDllkInstance_g.m_bFlag1 = EPL_FRAME_FLAG1_EC;
EplDllkInstance_g.m_bMnFlag1 = 0;
EplDllkInstance_g.m_bFlag2 = 0;
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
// initialize linked node list
EplDllkInstance_g.m_pFirstNodeInfo = NULL;
#endif
// register TxFrames in Edrv
// IdentResponse
uiFrameSize = EPL_C_DLL_MINSIZE_IDENTRES;
Ret =
EplDllkCreateTxFrame(&uiHandle, &pTxFrame,
&uiFrameSize, kEplMsgTypeAsnd,
kEplDllAsndIdentResponse);
if (Ret != kEplSuccessful) { // error occured while registering Tx frame
goto Exit;
}
// EPL profile version
AmiSetByteToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_bEplProfileVersion,
(BYTE) EPL_SPEC_VERSION);
// FeatureFlags
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_dwFeatureFlags,
EplDllkInstance_g.m_DllConfigParam.
m_dwFeatureFlags);
// MTU
AmiSetWordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_wMtu,
(WORD) EplDllkInstance_g.
m_DllConfigParam.m_uiAsyncMtu);
// PollInSize
AmiSetWordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_wPollInSize,
(WORD) EplDllkInstance_g.
m_DllConfigParam.
m_uiPreqActPayloadLimit);
// PollOutSize
AmiSetWordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_wPollOutSize,
(WORD) EplDllkInstance_g.
m_DllConfigParam.
m_uiPresActPayloadLimit);
// ResponseTime / PresMaxLatency
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_dwResponseTime,
EplDllkInstance_g.m_DllConfigParam.
m_dwPresMaxLatency);
// DeviceType
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_dwDeviceType,
EplDllkInstance_g.m_DllIdentParam.
m_dwDeviceType);
// VendorId
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_dwVendorId,
EplDllkInstance_g.m_DllIdentParam.
m_dwVendorId);
// ProductCode
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_dwProductCode,
EplDllkInstance_g.m_DllIdentParam.
m_dwProductCode);
// RevisionNumber
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_dwRevisionNumber,
EplDllkInstance_g.m_DllIdentParam.
m_dwRevisionNumber);
// SerialNumber
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_dwSerialNumber,
EplDllkInstance_g.m_DllIdentParam.
m_dwSerialNumber);
// VendorSpecificExt1
AmiSetQword64ToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.
m_le_qwVendorSpecificExt1,
EplDllkInstance_g.m_DllIdentParam.
m_qwVendorSpecificExt1);
// VerifyConfigurationDate
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.
m_le_dwVerifyConfigurationDate,
EplDllkInstance_g.m_DllIdentParam.
m_dwVerifyConfigurationDate);
// VerifyConfigurationTime
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.
m_le_dwVerifyConfigurationTime,
EplDllkInstance_g.m_DllIdentParam.
m_dwVerifyConfigurationTime);
// ApplicationSwDate
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.
m_le_dwApplicationSwDate,
EplDllkInstance_g.m_DllIdentParam.
m_dwApplicationSwDate);
// ApplicationSwTime
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.
m_le_dwApplicationSwTime,
EplDllkInstance_g.m_DllIdentParam.
m_dwApplicationSwTime);
// IPAddress
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_dwIpAddress,
EplDllkInstance_g.m_DllIdentParam.
m_dwIpAddress);
// SubnetMask
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_dwSubnetMask,
EplDllkInstance_g.m_DllIdentParam.
m_dwSubnetMask);
// DefaultGateway
AmiSetDwordToLe(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_dwDefaultGateway,
EplDllkInstance_g.m_DllIdentParam.
m_dwDefaultGateway);
// HostName
EPL_MEMCPY(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_sHostname[0],
&EplDllkInstance_g.m_DllIdentParam.
m_sHostname[0],
sizeof(EplDllkInstance_g.m_DllIdentParam.
m_sHostname));
// VendorSpecificExt2
EPL_MEMCPY(&pTxFrame->m_Data.m_Asnd.m_Payload.
m_IdentResponse.m_le_abVendorSpecificExt2[0],
&EplDllkInstance_g.m_DllIdentParam.
m_abVendorSpecificExt2[0],
sizeof(EplDllkInstance_g.m_DllIdentParam.
m_abVendorSpecificExt2));
// StatusResponse
uiFrameSize = EPL_C_DLL_MINSIZE_STATUSRES;
Ret =
EplDllkCreateTxFrame(&uiHandle, &pTxFrame,
&uiFrameSize, kEplMsgTypeAsnd,
kEplDllAsndStatusResponse);
if (Ret != kEplSuccessful) { // error occured while registering Tx frame
goto Exit;
}
// PRes $$$ maybe move this to PDO module
if ((EplDllkInstance_g.m_DllConfigParam.m_fAsyncOnly ==
FALSE)
&& (EplDllkInstance_g.m_DllConfigParam.m_uiPresActPayloadLimit >= 36)) { // it is not configured as async-only CN,
// so take part in isochronous phase and register PRes frame
uiFrameSize =
EplDllkInstance_g.m_DllConfigParam.
m_uiPresActPayloadLimit + 24;
Ret =
EplDllkCreateTxFrame(&uiHandle, &pTxFrame,
&uiFrameSize,
kEplMsgTypePres,
kEplDllAsndNotDefined);
if (Ret != kEplSuccessful) { // error occured while registering Tx frame
goto Exit;
}
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_PDOK)) != 0)
// initially encode TPDO -> inform PDO module
FrameInfo.m_pFrame = pTxFrame;
FrameInfo.m_uiFrameSize = uiFrameSize;
Ret = EplPdokCbPdoTransmitted(&FrameInfo);
#endif
// reset cycle counter
EplDllkInstance_g.m_uiCycleCount = 0;
} else { // it is an async-only CN
// fool EplDllkChangeState() to think that PRes was not expected
EplDllkInstance_g.m_uiCycleCount = 1;
}
// NMT request
uiFrameSize = EPL_C_IP_MAX_MTU;
Ret =
EplDllkCreateTxFrame(&uiHandle, &pTxFrame,
&uiFrameSize, kEplMsgTypeAsnd,
kEplDllAsndNmtRequest);
if (Ret != kEplSuccessful) { // error occured while registering Tx frame
goto Exit;
}
// mark Tx buffer as empty
EplDllkInstance_g.m_pTxBuffer[uiHandle].m_uiTxMsgLen =
EPL_DLLK_BUFLEN_EMPTY;
// non-EPL frame
uiFrameSize = EPL_C_IP_MAX_MTU;
Ret =
EplDllkCreateTxFrame(&uiHandle, &pTxFrame,
&uiFrameSize,
kEplMsgTypeNonEpl,
kEplDllAsndNotDefined);
if (Ret != kEplSuccessful) { // error occured while registering Tx frame
goto Exit;
}
// mark Tx buffer as empty
EplDllkInstance_g.m_pTxBuffer[uiHandle].m_uiTxMsgLen =
EPL_DLLK_BUFLEN_EMPTY;
// register multicast MACs in ethernet driver
AmiSetQword48ToBe(&abMulticastMac[0],
EPL_C_DLL_MULTICAST_SOC);
Ret = EdrvDefineRxMacAddrEntry(abMulticastMac);
AmiSetQword48ToBe(&abMulticastMac[0],
EPL_C_DLL_MULTICAST_SOA);
Ret = EdrvDefineRxMacAddrEntry(abMulticastMac);
AmiSetQword48ToBe(&abMulticastMac[0],
EPL_C_DLL_MULTICAST_PRES);
Ret = EdrvDefineRxMacAddrEntry(abMulticastMac);
AmiSetQword48ToBe(&abMulticastMac[0],
EPL_C_DLL_MULTICAST_ASND);
Ret = EdrvDefineRxMacAddrEntry(abMulticastMac);
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
if (NmtState >= kEplNmtMsNotActive) { // local node is MN
unsigned int uiIndex;
// SoC
uiFrameSize = EPL_C_DLL_MINSIZE_SOC;
Ret =
EplDllkCreateTxFrame(&uiHandle, &pTxFrame,
&uiFrameSize,
kEplMsgTypeSoc,
kEplDllAsndNotDefined);
if (Ret != kEplSuccessful) { // error occured while registering Tx frame
goto Exit;
}
// SoA
uiFrameSize = EPL_C_DLL_MINSIZE_SOA;
Ret =
EplDllkCreateTxFrame(&uiHandle, &pTxFrame,
&uiFrameSize,
kEplMsgTypeSoa,
kEplDllAsndNotDefined);
if (Ret != kEplSuccessful) { // error occured while registering Tx frame
goto Exit;
}
for (uiIndex = 0;
uiIndex <
tabentries(EplDllkInstance_g.m_aNodeInfo);
uiIndex++) {
// EplDllkInstance_g.m_aNodeInfo[uiIndex].m_uiNodeId = uiIndex + 1;
EplDllkInstance_g.m_aNodeInfo[uiIndex].
m_wPresPayloadLimit =
(WORD) EplDllkInstance_g.
m_DllConfigParam.
m_uiIsochrRxMaxPayload;
}
// calculate cycle length
EplDllkInstance_g.m_ullFrameTimeout = 1000LL
*
((unsigned long long)EplDllkInstance_g.
m_DllConfigParam.m_dwCycleLen);
}
#endif //(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
Ret = EplDllkCalAsyncClearBuffer();
break;
}
case kEplEventTypeDllkDestroy:
{
// destroy all data structures
NmtState = *((tEplNmtState *) pEvent_p->m_pArg);
// delete Tx frames
Ret = EplDllkDeleteTxFrame(EPL_DLLK_TXFRAME_IDENTRES);
if (Ret != kEplSuccessful) { // error occured while deregistering Tx frame
goto Exit;
}
Ret = EplDllkDeleteTxFrame(EPL_DLLK_TXFRAME_STATUSRES);
if (Ret != kEplSuccessful) { // error occured while deregistering Tx frame
goto Exit;
}
Ret = EplDllkDeleteTxFrame(EPL_DLLK_TXFRAME_PRES);
if (Ret != kEplSuccessful) { // error occured while deregistering Tx frame
goto Exit;
}
Ret = EplDllkDeleteTxFrame(EPL_DLLK_TXFRAME_NMTREQ);
if (Ret != kEplSuccessful) { // error occured while deregistering Tx frame
goto Exit;
}
Ret = EplDllkDeleteTxFrame(EPL_DLLK_TXFRAME_NONEPL);
if (Ret != kEplSuccessful) { // error occured while deregistering Tx frame
goto Exit;
}
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
if (NmtState >= kEplNmtMsNotActive) { // local node was MN
unsigned int uiIndex;
Ret =
EplDllkDeleteTxFrame(EPL_DLLK_TXFRAME_SOC);
if (Ret != kEplSuccessful) { // error occured while deregistering Tx frame
goto Exit;
}
Ret =
EplDllkDeleteTxFrame(EPL_DLLK_TXFRAME_SOA);
if (Ret != kEplSuccessful) { // error occured while deregistering Tx frame
goto Exit;
}
for (uiIndex = 0;
uiIndex <
tabentries(EplDllkInstance_g.m_aNodeInfo);
uiIndex++) {
if (EplDllkInstance_g.
m_aNodeInfo[uiIndex].
m_pPreqTxBuffer != NULL) {
uiHandle =
EplDllkInstance_g.
m_aNodeInfo[uiIndex].
m_pPreqTxBuffer -
EplDllkInstance_g.
m_pTxBuffer;
EplDllkInstance_g.
m_aNodeInfo[uiIndex].
m_pPreqTxBuffer = NULL;
Ret =
EplDllkDeleteTxFrame
(uiHandle);
if (Ret != kEplSuccessful) { // error occured while deregistering Tx frame
goto Exit;
}
}
EplDllkInstance_g.m_aNodeInfo[uiIndex].
m_wPresPayloadLimit = 0xFFFF;
}
}
#endif //(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
// deregister multicast MACs in ethernet driver
AmiSetQword48ToBe(&abMulticastMac[0],
EPL_C_DLL_MULTICAST_SOC);
Ret = EdrvUndefineRxMacAddrEntry(abMulticastMac);
AmiSetQword48ToBe(&abMulticastMac[0],
EPL_C_DLL_MULTICAST_SOA);
Ret = EdrvUndefineRxMacAddrEntry(abMulticastMac);
AmiSetQword48ToBe(&abMulticastMac[0],
EPL_C_DLL_MULTICAST_PRES);
Ret = EdrvUndefineRxMacAddrEntry(abMulticastMac);
AmiSetQword48ToBe(&abMulticastMac[0],
EPL_C_DLL_MULTICAST_ASND);
Ret = EdrvUndefineRxMacAddrEntry(abMulticastMac);
// delete timer
#if EPL_TIMER_USE_HIGHRES != FALSE
Ret =
EplTimerHighReskDeleteTimer(&EplDllkInstance_g.
m_TimerHdlCycle);
#endif
break;
}
case kEplEventTypeDllkFillTx:
{
// fill TxBuffer of specified priority with new frame if empty
pTxFrame = NULL;
AsyncReqPriority =
*((tEplDllAsyncReqPriority *) pEvent_p->m_pArg);
switch (AsyncReqPriority) {
case kEplDllAsyncReqPrioNmt: // NMT request priority
{
pTxBuffer =
&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NMTREQ];
if (pTxBuffer->m_pbBuffer != NULL) { // NmtRequest does exist
// check if frame is empty and not being filled
if (pTxBuffer->m_uiTxMsgLen ==
EPL_DLLK_BUFLEN_EMPTY) {
// mark Tx buffer as filling is in process
pTxBuffer->
m_uiTxMsgLen =
EPL_DLLK_BUFLEN_FILLING;
// set max buffer size as input parameter
uiFrameSize =
pTxBuffer->
m_uiMaxBufferLen;
// copy frame from shared loop buffer to Tx buffer
Ret =
EplDllkCalAsyncGetTxFrame
(pTxBuffer->
m_pbBuffer,
&uiFrameSize,
AsyncReqPriority);
if (Ret ==
kEplSuccessful) {
pTxFrame =
(tEplFrame
*)
pTxBuffer->
m_pbBuffer;
Ret =
EplDllkCheckFrame
(pTxFrame,
uiFrameSize);
// set buffer valid
pTxBuffer->
m_uiTxMsgLen
=
uiFrameSize;
} else if (Ret == kEplDllAsyncTxBufferEmpty) { // empty Tx buffer is not a real problem
// so just ignore it
Ret =
kEplSuccessful;
// mark Tx buffer as empty
pTxBuffer->
m_uiTxMsgLen
=
EPL_DLLK_BUFLEN_EMPTY;
}
}
}
break;
}
default: // generic priority
{
pTxBuffer =
&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NONEPL];
if (pTxBuffer->m_pbBuffer != NULL) { // non-EPL frame does exist
// check if frame is empty and not being filled
if (pTxBuffer->m_uiTxMsgLen ==
EPL_DLLK_BUFLEN_EMPTY) {
// mark Tx buffer as filling is in process
pTxBuffer->
m_uiTxMsgLen =
EPL_DLLK_BUFLEN_FILLING;
// set max buffer size as input parameter
uiFrameSize =
pTxBuffer->
m_uiMaxBufferLen;
// copy frame from shared loop buffer to Tx buffer
Ret =
EplDllkCalAsyncGetTxFrame
(pTxBuffer->
m_pbBuffer,
&uiFrameSize,
AsyncReqPriority);
if (Ret ==
kEplSuccessful) {
pTxFrame =
(tEplFrame
*)
pTxBuffer->
m_pbBuffer;
Ret =
EplDllkCheckFrame
(pTxFrame,
uiFrameSize);
// set buffer valid
pTxBuffer->
m_uiTxMsgLen
=
uiFrameSize;
} else if (Ret == kEplDllAsyncTxBufferEmpty) { // empty Tx buffer is not a real problem
// so just ignore it
Ret =
kEplSuccessful;
// mark Tx buffer as empty
pTxBuffer->
m_uiTxMsgLen
=
EPL_DLLK_BUFLEN_EMPTY;
}
}
}
break;
}
}
NmtState = EplNmtkGetNmtState();
if ((NmtState == kEplNmtCsBasicEthernet) || (NmtState == kEplNmtMsBasicEthernet)) { // send frame immediately
if (pTxFrame != NULL) { // frame is present
// padding is done by Edrv or ethernet controller
Ret = EdrvSendTxMsg(pTxBuffer);
} else { // no frame moved to TxBuffer
// check if TxBuffers contain unsent frames
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NMTREQ].m_uiTxMsgLen > EPL_DLLK_BUFLEN_EMPTY) { // NMT request Tx buffer contains a frame
Ret =
EdrvSendTxMsg
(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NMTREQ]);
} else if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NONEPL].m_uiTxMsgLen > EPL_DLLK_BUFLEN_EMPTY) { // non-EPL Tx buffer contains a frame
Ret =
EdrvSendTxMsg
(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NONEPL]);
}
if (Ret == kEplInvalidOperation) { // ignore error if caused by already active transmission
Ret = kEplSuccessful;
}
}
// reset PRes flag 2
EplDllkInstance_g.m_bFlag2 = 0;
} else {
// update Flag 2 (PR, RS)
Ret =
EplDllkCalAsyncGetTxCount(&AsyncReqPriority,
&uiFrameCount);
if (AsyncReqPriority == kEplDllAsyncReqPrioNmt) { // non-empty FIFO with hightest priority is for NMT requests
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NMTREQ].m_uiTxMsgLen > EPL_DLLK_BUFLEN_EMPTY) { // NMT request Tx buffer contains a frame
// add one more frame
uiFrameCount++;
}
} else { // non-empty FIFO with highest priority is for generic frames
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NMTREQ].m_uiTxMsgLen > EPL_DLLK_BUFLEN_EMPTY) { // NMT request Tx buffer contains a frame
// use NMT request FIFO, because of higher priority
uiFrameCount = 1;
AsyncReqPriority =
kEplDllAsyncReqPrioNmt;
} else if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NONEPL].m_uiTxMsgLen > EPL_DLLK_BUFLEN_EMPTY) { // non-EPL Tx buffer contains a frame
// use NMT request FIFO, because of higher priority
// add one more frame
uiFrameCount++;
}
}
if (uiFrameCount > 7) { // limit frame request to send counter to 7
uiFrameCount = 7;
}
if (uiFrameCount > 0) {
EplDllkInstance_g.m_bFlag2 =
(BYTE) (((AsyncReqPriority <<
EPL_FRAME_FLAG2_PR_SHIFT)
& EPL_FRAME_FLAG2_PR)
| (uiFrameCount &
EPL_FRAME_FLAG2_RS));
} else {
EplDllkInstance_g.m_bFlag2 = 0;
}
}
break;
}
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
case kEplEventTypeDllkStartReducedCycle:
{
// start the reduced cycle by programming the cycle timer
// it is issued by NMT MN module, when PreOp1 is entered
// clear the asynchronous queues
Ret = EplDllkCalAsyncClearQueues();
// reset cycle counter (everytime a SoA is triggerd in PreOp1 the counter is incremented
// and when it reaches EPL_C_DLL_PREOP1_START_CYCLES the SoA may contain invitations)
EplDllkInstance_g.m_uiCycleCount = 0;
// remove any CN from isochronous phase
while (EplDllkInstance_g.m_pFirstNodeInfo != NULL) {
EplDllkDeleteNode(EplDllkInstance_g.
m_pFirstNodeInfo->m_uiNodeId);
}
// change state to NonCyclic,
// hence EplDllkChangeState() will not ignore the next call
EplDllkInstance_g.m_DllState = kEplDllMsNonCyclic;
#if EPL_TIMER_USE_HIGHRES != FALSE
if (EplDllkInstance_g.m_DllConfigParam.
m_dwAsyncSlotTimeout != 0) {
Ret =
EplTimerHighReskModifyTimerNs
(&EplDllkInstance_g.m_TimerHdlCycle,
EplDllkInstance_g.m_DllConfigParam.
m_dwAsyncSlotTimeout,
EplDllkCbMnTimerCycle, 0L, FALSE);
}
#endif
break;
}
#endif
#if EPL_DLL_PRES_READY_AFTER_SOA != FALSE
case kEplEventTypeDllkPresReady:
{
// post PRes to transmit FIFO
NmtState = EplNmtkGetNmtState();
if (NmtState != kEplNmtCsBasicEthernet) {
// Does PRes exist?
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_PRES].m_pbBuffer != NULL) { // PRes does exist
pTxFrame =
(tEplFrame *) EplDllkInstance_g.
m_pTxBuffer[EPL_DLLK_TXFRAME_PRES].
m_pbBuffer;
// update frame (NMT state, RD, RS, PR, MS, EN flags)
if (NmtState < kEplNmtCsPreOperational2) { // NMT state is not PreOp2, ReadyToOp or Op
// fake NMT state PreOp2, because PRes will be sent only in PreOp2 or greater
NmtState =
kEplNmtCsPreOperational2;
}
AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.
m_le_bNmtStatus,
(BYTE) NmtState);
AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.
m_le_bFlag2,
EplDllkInstance_g.
m_bFlag2);
if (NmtState != kEplNmtCsOperational) { // mark PDO as invalid in NMT state Op
// $$$ reset only RD flag; set other flags appropriately
AmiSetByteToLe(&pTxFrame->
m_Data.m_Pres.
m_le_bFlag1, 0);
}
// $$$ make function that updates Pres, StatusRes
// mark PRes frame as ready for transmission
Ret =
EdrvTxMsgReady(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_PRES]);
}
}
break;
}
#endif
default:
{
ASSERTMSG(FALSE,
"EplDllkProcess(): unhandled event type!\n");
}
}
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkConfig
//
// Description: configure parameters of DLL
//
// Parameters: pDllConfigParam_p = configuration parameters
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkConfig(tEplDllConfigParam * pDllConfigParam_p)
{
tEplKernel Ret = kEplSuccessful;
// d.k. check of NMT state disabled, because CycleLen is programmed at run time by MN without reset of CN
/*tEplNmtState NmtState;
NmtState = EplNmtkGetNmtState();
if (NmtState > kEplNmtGsResetConfiguration)
{ // only allowed in state DLL_GS_INIT
Ret = kEplInvalidOperation;
goto Exit;
}
*/
EPL_MEMCPY(&EplDllkInstance_g.m_DllConfigParam, pDllConfigParam_p,
(pDllConfigParam_p->m_uiSizeOfStruct <
sizeof(tEplDllConfigParam) ? pDllConfigParam_p->
m_uiSizeOfStruct : sizeof(tEplDllConfigParam)));
if ((EplDllkInstance_g.m_DllConfigParam.m_dwCycleLen != 0)
&& (EplDllkInstance_g.m_DllConfigParam.m_dwLossOfFrameTolerance != 0)) { // monitor EPL cycle, calculate frame timeout
EplDllkInstance_g.m_ullFrameTimeout = (1000LL
*
((unsigned long long)
EplDllkInstance_g.
m_DllConfigParam.
m_dwCycleLen))
+
((unsigned long long)EplDllkInstance_g.m_DllConfigParam.
m_dwLossOfFrameTolerance);
} else {
EplDllkInstance_g.m_ullFrameTimeout = 0LL;
}
if (EplDllkInstance_g.m_DllConfigParam.m_fAsyncOnly != FALSE) { // it is configured as async-only CN
// disable multiplexed cycle, that m_uiCycleCount will not be incremented spuriously on SoC
EplDllkInstance_g.m_DllConfigParam.m_uiMultiplCycleCnt = 0;
}
//Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkSetIdentity
//
// Description: configure identity of local node for IdentResponse
//
// Parameters: pDllIdentParam_p = identity
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkSetIdentity(tEplDllIdentParam * pDllIdentParam_p)
{
tEplKernel Ret = kEplSuccessful;
EPL_MEMCPY(&EplDllkInstance_g.m_DllIdentParam, pDllIdentParam_p,
(pDllIdentParam_p->m_uiSizeOfStruct <
sizeof(tEplDllIdentParam) ? pDllIdentParam_p->
m_uiSizeOfStruct : sizeof(tEplDllIdentParam)));
// $$$ if IdentResponse frame exists update it
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkRegAsyncHandler
//
// Description: registers handler for non-EPL frames
//
// Parameters: pfnDllkCbAsync_p = pointer to callback function
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkRegAsyncHandler(tEplDllkCbAsync pfnDllkCbAsync_p)
{
tEplKernel Ret = kEplSuccessful;
if (EplDllkInstance_g.m_pfnCbAsync == NULL) { // no handler registered yet
EplDllkInstance_g.m_pfnCbAsync = pfnDllkCbAsync_p;
} else { // handler already registered
Ret = kEplDllCbAsyncRegistered;
}
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkDeregAsyncHandler
//
// Description: deregisters handler for non-EPL frames
//
// Parameters: pfnDllkCbAsync_p = pointer to callback function
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkDeregAsyncHandler(tEplDllkCbAsync pfnDllkCbAsync_p)
{
tEplKernel Ret = kEplSuccessful;
if (EplDllkInstance_g.m_pfnCbAsync == pfnDllkCbAsync_p) { // same handler is registered
// deregister it
EplDllkInstance_g.m_pfnCbAsync = NULL;
} else { // wrong handler or no handler registered
Ret = kEplDllCbAsyncRegistered;
}
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkSetAsndServiceIdFilter()
//
// Description: sets the specified node ID filter for the specified
// AsndServiceId. It registers C_DLL_MULTICAST_ASND in ethernet
// driver if any AsndServiceId is open.
//
// Parameters: ServiceId_p = ASnd Service ID
// Filter_p = node ID filter
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkSetAsndServiceIdFilter(tEplDllAsndServiceId ServiceId_p,
tEplDllAsndFilter Filter_p)
{
tEplKernel Ret = kEplSuccessful;
if (ServiceId_p < tabentries(EplDllkInstance_g.m_aAsndFilter)) {
EplDllkInstance_g.m_aAsndFilter[ServiceId_p] = Filter_p;
}
return Ret;
}
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
//---------------------------------------------------------------------------
//
// Function: EplDllkSetFlag1OfNode()
//
// Description: sets Flag1 (for PReq and SoA) of the specified node ID.
//
// Parameters: uiNodeId_p = node ID
// bSoaFlag1_p = flag1
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkSetFlag1OfNode(unsigned int uiNodeId_p, BYTE bSoaFlag1_p)
{
tEplKernel Ret = kEplSuccessful;
tEplDllkNodeInfo *pNodeInfo;
pNodeInfo = EplDllkGetNodeInfo(uiNodeId_p);
if (pNodeInfo == NULL) { // no node info structure available
Ret = kEplDllNoNodeInfo;
goto Exit;
}
// store flag1 in internal node info structure
pNodeInfo->m_bSoaFlag1 = bSoaFlag1_p;
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkGetFirstNodeInfo()
//
// Description: returns first info structure of first node in isochronous phase.
// It is only useful for ErrorHandlerk module.
//
// Parameters: ppNodeInfo_p = pointer to pointer of internal node info structure
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkGetFirstNodeInfo(tEplDllkNodeInfo ** ppNodeInfo_p)
{
tEplKernel Ret = kEplSuccessful;
*ppNodeInfo_p = EplDllkInstance_g.m_pFirstNodeInfo;
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkAddNode()
//
// Description: adds the specified node to the isochronous phase.
//
// Parameters: pNodeInfo_p = pointer of node info structure
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkAddNode(tEplDllNodeInfo * pNodeInfo_p)
{
tEplKernel Ret = kEplSuccessful;
tEplDllkNodeInfo *pIntNodeInfo;
tEplDllkNodeInfo **ppIntNodeInfo;
unsigned int uiHandle;
tEplFrame *pFrame;
unsigned int uiFrameSize;
pIntNodeInfo = EplDllkGetNodeInfo(pNodeInfo_p->m_uiNodeId);
if (pIntNodeInfo == NULL) { // no node info structure available
Ret = kEplDllNoNodeInfo;
goto Exit;
}
EPL_DLLK_DBG_POST_TRACE_VALUE(kEplEventTypeDllkAddNode,
pNodeInfo_p->m_uiNodeId, 0);
// copy node configuration
pIntNodeInfo->m_dwPresTimeout = pNodeInfo_p->m_dwPresTimeout;
pIntNodeInfo->m_wPresPayloadLimit = pNodeInfo_p->m_wPresPayloadLimit;
// $$$ d.k.: actually add node only if MN. On CN it is sufficient to update the node configuration
if (pNodeInfo_p->m_uiNodeId == EplDllkInstance_g.m_DllConfigParam.m_uiNodeId) { // we shall send PRes ourself
// insert our node at the end of the list
ppIntNodeInfo = &EplDllkInstance_g.m_pFirstNodeInfo;
while ((*ppIntNodeInfo != NULL)
&& ((*ppIntNodeInfo)->m_pNextNodeInfo != NULL)) {
ppIntNodeInfo = &(*ppIntNodeInfo)->m_pNextNodeInfo;
}
if (*ppIntNodeInfo != NULL) {
if ((*ppIntNodeInfo)->m_uiNodeId == pNodeInfo_p->m_uiNodeId) { // node was already added to list
// $$$ d.k. maybe this should be an error
goto Exit;
} else { // add our node at the end of the list
ppIntNodeInfo =
&(*ppIntNodeInfo)->m_pNextNodeInfo;
}
}
// set "PReq"-TxBuffer to PRes-TxBuffer
pIntNodeInfo->m_pPreqTxBuffer =
&EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_PRES];
} else { // normal CN shall be added to isochronous phase
// insert node into list in ascending order
ppIntNodeInfo = &EplDllkInstance_g.m_pFirstNodeInfo;
while ((*ppIntNodeInfo != NULL)
&& ((*ppIntNodeInfo)->m_uiNodeId <
pNodeInfo_p->m_uiNodeId)
&& ((*ppIntNodeInfo)->m_uiNodeId !=
EplDllkInstance_g.m_DllConfigParam.m_uiNodeId)) {
ppIntNodeInfo = &(*ppIntNodeInfo)->m_pNextNodeInfo;
}
if ((*ppIntNodeInfo != NULL) && ((*ppIntNodeInfo)->m_uiNodeId == pNodeInfo_p->m_uiNodeId)) { // node was already added to list
// $$$ d.k. maybe this should be an error
goto Exit;
}
}
// initialize elements of internal node info structure
pIntNodeInfo->m_bSoaFlag1 = 0;
pIntNodeInfo->m_fSoftDelete = FALSE;
pIntNodeInfo->m_NmtState = kEplNmtCsNotActive;
if (pIntNodeInfo->m_pPreqTxBuffer == NULL) { // create TxBuffer entry
uiFrameSize = pNodeInfo_p->m_wPreqPayloadLimit + 24;
Ret =
EplDllkCreateTxFrame(&uiHandle, &pFrame, &uiFrameSize,
kEplMsgTypePreq,
kEplDllAsndNotDefined);
if (Ret != kEplSuccessful) {
goto Exit;
}
pIntNodeInfo->m_pPreqTxBuffer =
&EplDllkInstance_g.m_pTxBuffer[uiHandle];
AmiSetByteToLe(&pFrame->m_le_bDstNodeId,
(BYTE) pNodeInfo_p->m_uiNodeId);
// set up destination MAC address
EPL_MEMCPY(pFrame->m_be_abDstMac, pIntNodeInfo->m_be_abMacAddr,
6);
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_PDOK)) != 0)
{
tEplFrameInfo FrameInfo;
// initially encode TPDO -> inform PDO module
FrameInfo.m_pFrame = pFrame;
FrameInfo.m_uiFrameSize = uiFrameSize;
Ret = EplPdokCbPdoTransmitted(&FrameInfo);
}
#endif
}
pIntNodeInfo->m_ulDllErrorEvents = 0L;
// add node to list
pIntNodeInfo->m_pNextNodeInfo = *ppIntNodeInfo;
*ppIntNodeInfo = pIntNodeInfo;
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkDeleteNode()
//
// Description: removes the specified node from the isochronous phase.
//
// Parameters: uiNodeId_p = node ID
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkDeleteNode(unsigned int uiNodeId_p)
{
tEplKernel Ret = kEplSuccessful;
tEplDllkNodeInfo *pIntNodeInfo;
tEplDllkNodeInfo **ppIntNodeInfo;
unsigned int uiHandle;
pIntNodeInfo = EplDllkGetNodeInfo(uiNodeId_p);
if (pIntNodeInfo == NULL) { // no node info structure available
Ret = kEplDllNoNodeInfo;
goto Exit;
}
EPL_DLLK_DBG_POST_TRACE_VALUE(kEplEventTypeDllkDelNode, uiNodeId_p, 0);
// search node in whole list
ppIntNodeInfo = &EplDllkInstance_g.m_pFirstNodeInfo;
while ((*ppIntNodeInfo != NULL)
&& ((*ppIntNodeInfo)->m_uiNodeId != uiNodeId_p)) {
ppIntNodeInfo = &(*ppIntNodeInfo)->m_pNextNodeInfo;
}
if ((*ppIntNodeInfo == NULL) || ((*ppIntNodeInfo)->m_uiNodeId != uiNodeId_p)) { // node was not found in list
// $$$ d.k. maybe this should be an error
goto Exit;
}
// remove node from list
*ppIntNodeInfo = pIntNodeInfo->m_pNextNodeInfo;
if ((pIntNodeInfo->m_pPreqTxBuffer != NULL)
&& (pIntNodeInfo->m_pPreqTxBuffer != &EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_PRES])) { // delete TxBuffer entry
uiHandle =
pIntNodeInfo->m_pPreqTxBuffer -
EplDllkInstance_g.m_pTxBuffer;
pIntNodeInfo->m_pPreqTxBuffer = NULL;
Ret = EplDllkDeleteTxFrame(uiHandle);
/* if (Ret != kEplSuccessful)
{
goto Exit;
}*/
}
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkSoftDeleteNode()
//
// Description: removes the specified node not immediately from the isochronous phase.
// Instead the will be removed after error (late/loss PRes) without
// charging the error.
//
// Parameters: uiNodeId_p = node ID
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel EplDllkSoftDeleteNode(unsigned int uiNodeId_p)
{
tEplKernel Ret = kEplSuccessful;
tEplDllkNodeInfo *pIntNodeInfo;
pIntNodeInfo = EplDllkGetNodeInfo(uiNodeId_p);
if (pIntNodeInfo == NULL) { // no node info structure available
Ret = kEplDllNoNodeInfo;
goto Exit;
}
EPL_DLLK_DBG_POST_TRACE_VALUE(kEplEventTypeDllkSoftDelNode,
uiNodeId_p, 0);
pIntNodeInfo->m_fSoftDelete = TRUE;
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 //
// //
//=========================================================================//
//---------------------------------------------------------------------------
//
// Function: EplDllkChangeState
//
// Description: change DLL state on event and diagnose some communication errors
//
// Parameters: NmtEvent_p = DLL event (wrapped in NMT event)
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel EplDllkChangeState(tEplNmtEvent NmtEvent_p,
tEplNmtState NmtState_p)
{
tEplKernel Ret = kEplSuccessful;
tEplEvent Event;
tEplErrorHandlerkEvent DllEvent;
DllEvent.m_ulDllErrorEvents = 0;
DllEvent.m_uiNodeId = 0;
DllEvent.m_NmtState = NmtState_p;
switch (NmtState_p) {
case kEplNmtGsOff:
case kEplNmtGsInitialising:
case kEplNmtGsResetApplication:
case kEplNmtGsResetCommunication:
case kEplNmtGsResetConfiguration:
case kEplNmtCsBasicEthernet:
// enter DLL_GS_INIT
EplDllkInstance_g.m_DllState = kEplDllGsInit;
break;
case kEplNmtCsNotActive:
case kEplNmtCsPreOperational1:
// reduced EPL cycle is active
if (NmtEvent_p == kEplNmtEventDllCeSoc) { // SoC received
// enter DLL_CS_WAIT_PREQ
EplDllkInstance_g.m_DllState = kEplDllCsWaitPreq;
} else {
// enter DLL_GS_INIT
EplDllkInstance_g.m_DllState = kEplDllGsInit;
}
break;
case kEplNmtCsPreOperational2:
case kEplNmtCsReadyToOperate:
case kEplNmtCsOperational:
// full EPL cycle is active
switch (EplDllkInstance_g.m_DllState) {
case kEplDllCsWaitPreq:
switch (NmtEvent_p) {
// DLL_CT2
case kEplNmtEventDllCePreq:
// enter DLL_CS_WAIT_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_RECVD_PREQ;
EplDllkInstance_g.m_DllState = kEplDllCsWaitSoa;
break;
// DLL_CT8
case kEplNmtEventDllCeFrameTimeout:
if (NmtState_p == kEplNmtCsPreOperational2) { // ignore frame timeout in PreOp2,
// because the previously configured cycle len
// may be wrong.
// 2008/10/15 d.k. If it would not be ignored,
// we would go cyclically to PreOp1 and on next
// SoC back to PreOp2.
break;
}
// report DLL_CEV_LOSS_SOC and DLL_CEV_LOSS_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOA |
EPL_DLL_ERR_CN_LOSS_SOC;
// enter DLL_CS_WAIT_SOC
EplDllkInstance_g.m_DllState = kEplDllCsWaitSoc;
break;
case kEplNmtEventDllCeSoa:
// check if multiplexed and PReq should have been received in this cycle
// and if >= NMT_CS_READY_TO_OPERATE
if ((EplDllkInstance_g.m_uiCycleCount == 0)
&& (NmtState_p >= kEplNmtCsReadyToOperate)) { // report DLL_CEV_LOSS_OF_PREQ
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_PREQ;
}
// enter DLL_CS_WAIT_SOC
EplDllkInstance_g.m_DllState = kEplDllCsWaitSoc;
break;
// DLL_CT7
case kEplNmtEventDllCeSoc:
case kEplNmtEventDllCeAsnd:
// report DLL_CEV_LOSS_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOA;
case kEplNmtEventDllCePres:
default:
// remain in this state
break;
}
break;
case kEplDllCsWaitSoc:
switch (NmtEvent_p) {
// DLL_CT1
case kEplNmtEventDllCeSoc:
// start of cycle and isochronous phase
// enter DLL_CS_WAIT_PREQ
EplDllkInstance_g.m_DllState =
kEplDllCsWaitPreq;
break;
// DLL_CT4
// case kEplNmtEventDllCePres:
case kEplNmtEventDllCeFrameTimeout:
if (NmtState_p == kEplNmtCsPreOperational2) { // ignore frame timeout in PreOp2,
// because the previously configured cycle len
// may be wrong.
// 2008/10/15 d.k. If it would not be ignored,
// we would go cyclically to PreOp1 and on next
// SoC back to PreOp2.
break;
}
// fall through
case kEplNmtEventDllCePreq:
case kEplNmtEventDllCeSoa:
// report DLL_CEV_LOSS_SOC
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOC;
case kEplNmtEventDllCeAsnd:
default:
// remain in this state
break;
}
break;
case kEplDllCsWaitSoa:
switch (NmtEvent_p) {
case kEplNmtEventDllCeFrameTimeout:
// DLL_CT3
if (NmtState_p == kEplNmtCsPreOperational2) { // ignore frame timeout in PreOp2,
// because the previously configured cycle len
// may be wrong.
// 2008/10/15 d.k. If it would not be ignored,
// we would go cyclically to PreOp1 and on next
// SoC back to PreOp2.
break;
}
// fall through
case kEplNmtEventDllCePreq:
// report DLL_CEV_LOSS_SOC and DLL_CEV_LOSS_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOA |
EPL_DLL_ERR_CN_LOSS_SOC;
case kEplNmtEventDllCeSoa:
// enter DLL_CS_WAIT_SOC
EplDllkInstance_g.m_DllState = kEplDllCsWaitSoc;
break;
// DLL_CT9
case kEplNmtEventDllCeSoc:
// report DLL_CEV_LOSS_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOA;
// enter DLL_CS_WAIT_PREQ
EplDllkInstance_g.m_DllState =
kEplDllCsWaitPreq;
break;
// DLL_CT10
case kEplNmtEventDllCeAsnd:
// report DLL_CEV_LOSS_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOA;
case kEplNmtEventDllCePres:
default:
// remain in this state
break;
}
break;
case kEplDllGsInit:
// enter DLL_CS_WAIT_PREQ
EplDllkInstance_g.m_DllState = kEplDllCsWaitPreq;
break;
default:
break;
}
break;
case kEplNmtCsStopped:
// full EPL cycle is active, but without PReq/PRes
switch (EplDllkInstance_g.m_DllState) {
case kEplDllCsWaitPreq:
switch (NmtEvent_p) {
// DLL_CT2
case kEplNmtEventDllCePreq:
// enter DLL_CS_WAIT_SOA
EplDllkInstance_g.m_DllState = kEplDllCsWaitSoa;
break;
// DLL_CT8
case kEplNmtEventDllCeFrameTimeout:
// report DLL_CEV_LOSS_SOC and DLL_CEV_LOSS_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOA |
EPL_DLL_ERR_CN_LOSS_SOC;
case kEplNmtEventDllCeSoa:
// NMT_CS_STOPPED active
// it is Ok if no PReq was received
// enter DLL_CS_WAIT_SOC
EplDllkInstance_g.m_DllState = kEplDllCsWaitSoc;
break;
// DLL_CT7
case kEplNmtEventDllCeSoc:
case kEplNmtEventDllCeAsnd:
// report DLL_CEV_LOSS_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOA;
case kEplNmtEventDllCePres:
default:
// remain in this state
break;
}
break;
case kEplDllCsWaitSoc:
switch (NmtEvent_p) {
// DLL_CT1
case kEplNmtEventDllCeSoc:
// start of cycle and isochronous phase
// enter DLL_CS_WAIT_SOA
EplDllkInstance_g.m_DllState = kEplDllCsWaitSoa;
break;
// DLL_CT4
// case kEplNmtEventDllCePres:
case kEplNmtEventDllCePreq:
case kEplNmtEventDllCeSoa:
case kEplNmtEventDllCeFrameTimeout:
// report DLL_CEV_LOSS_SOC
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOC;
case kEplNmtEventDllCeAsnd:
default:
// remain in this state
break;
}
break;
case kEplDllCsWaitSoa:
switch (NmtEvent_p) {
// DLL_CT3
case kEplNmtEventDllCeFrameTimeout:
// report DLL_CEV_LOSS_SOC and DLL_CEV_LOSS_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOA |
EPL_DLL_ERR_CN_LOSS_SOC;
case kEplNmtEventDllCeSoa:
// enter DLL_CS_WAIT_SOC
EplDllkInstance_g.m_DllState = kEplDllCsWaitSoc;
break;
// DLL_CT9
case kEplNmtEventDllCeSoc:
// report DLL_CEV_LOSS_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOA;
// remain in DLL_CS_WAIT_SOA
break;
// DLL_CT10
case kEplNmtEventDllCeAsnd:
// report DLL_CEV_LOSS_SOA
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_CN_LOSS_SOA;
case kEplNmtEventDllCePreq:
// NMT_CS_STOPPED active and we do not expect any PReq
// so just ignore it
case kEplNmtEventDllCePres:
default:
// remain in this state
break;
}
break;
case kEplDllGsInit:
default:
// enter DLL_CS_WAIT_PREQ
EplDllkInstance_g.m_DllState = kEplDllCsWaitSoa;
break;
}
break;
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
case kEplNmtMsNotActive:
case kEplNmtMsBasicEthernet:
break;
case kEplNmtMsPreOperational1:
// reduced EPL cycle is active
if (EplDllkInstance_g.m_DllState != kEplDllMsNonCyclic) { // stop cycle timer
#if EPL_TIMER_USE_HIGHRES != FALSE
Ret =
EplTimerHighReskDeleteTimer(&EplDllkInstance_g.
m_TimerHdlCycle);
#endif
EplDllkInstance_g.m_DllState = kEplDllMsNonCyclic;
// stop further processing,
// because it will be restarted by NMT MN module
break;
}
switch (NmtEvent_p) {
case kEplNmtEventDllMeSocTrig:
case kEplNmtEventDllCeAsnd:
{ // because of reduced EPL cycle SoA shall be triggered, not SoC
tEplDllState DummyDllState;
Ret =
EplDllkAsyncFrameNotReceived
(EplDllkInstance_g.m_LastReqServiceId,
EplDllkInstance_g.m_uiLastTargetNodeId);
// go ahead and send SoA
Ret = EplDllkMnSendSoa(NmtState_p,
&DummyDllState,
(EplDllkInstance_g.
m_uiCycleCount >=
EPL_C_DLL_PREOP1_START_CYCLES));
// increment cycle counter to detect if EPL_C_DLL_PREOP1_START_CYCLES empty cycles are elapsed
EplDllkInstance_g.m_uiCycleCount++;
// reprogram timer
#if EPL_TIMER_USE_HIGHRES != FALSE
if (EplDllkInstance_g.m_DllConfigParam.
m_dwAsyncSlotTimeout != 0) {
Ret =
EplTimerHighReskModifyTimerNs
(&EplDllkInstance_g.m_TimerHdlCycle,
EplDllkInstance_g.m_DllConfigParam.
m_dwAsyncSlotTimeout,
EplDllkCbMnTimerCycle, 0L, FALSE);
}
#endif
break;
}
default:
break;
}
break;
case kEplNmtMsPreOperational2:
case kEplNmtMsReadyToOperate:
case kEplNmtMsOperational:
// full EPL cycle is active
switch (NmtEvent_p) {
case kEplNmtEventDllMeSocTrig:
{
// update cycle counter
if (EplDllkInstance_g.m_DllConfigParam.m_uiMultiplCycleCnt > 0) { // multiplexed cycle active
EplDllkInstance_g.m_uiCycleCount =
(EplDllkInstance_g.m_uiCycleCount +
1) %
EplDllkInstance_g.m_DllConfigParam.
m_uiMultiplCycleCnt;
// $$$ check multiplexed cycle restart
// -> toggle MC flag
// -> change node linked list
} else { // non-multiplexed cycle active
// start with first node in isochronous phase
EplDllkInstance_g.m_pCurNodeInfo = NULL;
}
switch (EplDllkInstance_g.m_DllState) {
case kEplDllMsNonCyclic:
{ // start continuous cycle timer
#if EPL_TIMER_USE_HIGHRES != FALSE
Ret =
EplTimerHighReskModifyTimerNs
(&EplDllkInstance_g.
m_TimerHdlCycle,
EplDllkInstance_g.
m_ullFrameTimeout,
EplDllkCbMnTimerCycle, 0L,
TRUE);
#endif
// continue with sending SoC
}
case kEplDllMsWaitAsnd:
case kEplDllMsWaitSocTrig:
{ // if m_LastReqServiceId is still valid,
// SoA was not correctly answered
// and user part has to be informed
Ret =
EplDllkAsyncFrameNotReceived
(EplDllkInstance_g.
m_LastReqServiceId,
EplDllkInstance_g.
m_uiLastTargetNodeId);
// send SoC
Ret = EplDllkMnSendSoc();
// new DLL state
EplDllkInstance_g.m_DllState =
kEplDllMsWaitPreqTrig;
// start WaitSoCPReq Timer
#if EPL_TIMER_USE_HIGHRES != FALSE
Ret =
EplTimerHighReskModifyTimerNs
(&EplDllkInstance_g.
m_TimerHdlResponse,
EplDllkInstance_g.
m_DllConfigParam.
m_dwWaitSocPreq,
EplDllkCbMnTimerResponse,
0L, FALSE);
#endif
break;
}
default:
{ // wrong DLL state / cycle time exceeded
DllEvent.m_ulDllErrorEvents |=
EPL_DLL_ERR_MN_CYCTIMEEXCEED;
EplDllkInstance_g.m_DllState =
kEplDllMsWaitSocTrig;
break;
}
}
break;
}
case kEplNmtEventDllMePresTimeout:
{
switch (EplDllkInstance_g.m_DllState) {
case kEplDllMsWaitPres:
{ // PRes not received
if (EplDllkInstance_g.m_pCurNodeInfo->m_fSoftDelete == FALSE) { // normal isochronous CN
DllEvent.
m_ulDllErrorEvents
|=
EPL_DLL_ERR_MN_CN_LOSS_PRES;
DllEvent.m_uiNodeId =
EplDllkInstance_g.
m_pCurNodeInfo->
m_uiNodeId;
} else { // CN shall be deleted softly
Event.m_EventSink =
kEplEventSinkDllkCal;
Event.m_EventType =
kEplEventTypeDllkSoftDelNode;
// $$$ d.k. set Event.m_NetTime to current time
Event.m_uiSize =
sizeof(unsigned
int);
Event.m_pArg =
&EplDllkInstance_g.
m_pCurNodeInfo->
m_uiNodeId;
Ret =
EplEventkPost
(&Event);
}
// continue with sending next PReq
}
case kEplDllMsWaitPreqTrig:
{
// send next PReq
Ret =
EplDllkMnSendPreq
(NmtState_p,
&EplDllkInstance_g.
m_DllState);
break;
}
default:
{ // wrong DLL state
break;
}
}
break;
}
case kEplNmtEventDllCePres:
{
switch (EplDllkInstance_g.m_DllState) {
case kEplDllMsWaitPres:
{ // PRes received
// send next PReq
Ret =
EplDllkMnSendPreq
(NmtState_p,
&EplDllkInstance_g.
m_DllState);
break;
}
default:
{ // wrong DLL state
break;
}
}
break;
}
case kEplNmtEventDllMeSoaTrig:
{
switch (EplDllkInstance_g.m_DllState) {
case kEplDllMsWaitSoaTrig:
{ // MN PRes sent
// send SoA
Ret =
EplDllkMnSendSoa(NmtState_p,
&EplDllkInstance_g.
m_DllState,
TRUE);
break;
}
default:
{ // wrong DLL state
break;
}
}
break;
}
case kEplNmtEventDllCeAsnd:
{ // ASnd has been received, but it may be not the requested one
/*
// report if SoA was correctly answered
Ret = EplDllkAsyncFrameNotReceived(EplDllkInstance_g.m_LastReqServiceId,
EplDllkInstance_g.m_uiLastTargetNodeId);
*/
if (EplDllkInstance_g.m_DllState ==
kEplDllMsWaitAsnd) {
EplDllkInstance_g.m_DllState =
kEplDllMsWaitSocTrig;
}
break;
}
default:
break;
}
break;
#endif //(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
default:
break;
}
if (DllEvent.m_ulDllErrorEvents != 0) { // error event set -> post it to error handler
Event.m_EventSink = kEplEventSinkErrk;
Event.m_EventType = kEplEventTypeDllError;
// $$$ d.k. set Event.m_NetTime to current time
Event.m_uiSize = sizeof(DllEvent);
Event.m_pArg = &DllEvent;
Ret = EplEventkPost(&Event);
}
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkCbFrameReceived()
//
// Description: called from EdrvInterruptHandler()
//
// Parameters: pRxBuffer_p = receive buffer structure
//
// Returns: (none)
//
//
// State:
//
//---------------------------------------------------------------------------
static void EplDllkCbFrameReceived(tEdrvRxBuffer * pRxBuffer_p)
{
tEplKernel Ret = kEplSuccessful;
tEplNmtState NmtState;
tEplNmtEvent NmtEvent = kEplNmtEventNoEvent;
tEplEvent Event;
tEplFrame *pFrame;
tEplFrame *pTxFrame;
tEdrvTxBuffer *pTxBuffer = NULL;
tEplFrameInfo FrameInfo;
tEplMsgType MsgType;
tEplDllReqServiceId ReqServiceId;
unsigned int uiAsndServiceId;
unsigned int uiNodeId;
BYTE bFlag1;
BENCHMARK_MOD_02_SET(3);
NmtState = EplNmtkGetNmtState();
if (NmtState <= kEplNmtGsResetConfiguration) {
goto Exit;
}
pFrame = (tEplFrame *) pRxBuffer_p->m_pbBuffer;
#if EDRV_EARLY_RX_INT != FALSE
switch (pRxBuffer_p->m_BufferInFrame) {
case kEdrvBufferFirstInFrame:
{
MsgType =
(tEplMsgType) AmiGetByteFromLe(&pFrame->
m_le_bMessageType);
if (MsgType == kEplMsgTypePreq) {
if (EplDllkInstance_g.m_DllState == kEplDllCsWaitPreq) { // PReq expected and actually received
// d.k.: The condition above is sufficent, because EPL cycle is active
// and no non-EPL frame shall be received in isochronous phase.
// start transmission PRes
// $$$ What if Tx buffer is invalid?
pTxBuffer =
&EplDllkInstance_g.
m_pTxBuffer[EPL_DLLK_TXFRAME_PRES];
#if (EPL_DLL_PRES_READY_AFTER_SOA != FALSE) || (EPL_DLL_PRES_READY_AFTER_SOC != FALSE)
Ret = EdrvTxMsgStart(pTxBuffer);
#else
pTxFrame =
(tEplFrame *) pTxBuffer->m_pbBuffer;
// update frame (NMT state, RD, RS, PR, MS, EN flags)
AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.
m_le_bNmtStatus,
(BYTE) NmtState);
AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.
m_le_bFlag2,
EplDllkInstance_g.
m_bFlag2);
if (NmtState != kEplNmtCsOperational) { // mark PDO as invalid in NMT state Op
// $$$ reset only RD flag; set other flags appropriately
AmiSetByteToLe(&pTxFrame->
m_Data.m_Pres.
m_le_bFlag1, 0);
}
// $$$ make function that updates Pres, StatusRes
// send PRes frame
Ret = EdrvSendTxMsg(pTxBuffer);
#endif
}
}
goto Exit;
}
case kEdrvBufferMiddleInFrame:
{
goto Exit;
}
case kEdrvBufferLastInFrame:
{
break;
}
}
#endif
FrameInfo.m_pFrame = pFrame;
FrameInfo.m_uiFrameSize = pRxBuffer_p->m_uiRxMsgLen;
FrameInfo.m_NetTime.m_dwNanoSec = pRxBuffer_p->m_NetTime.m_dwNanoSec;
FrameInfo.m_NetTime.m_dwSec = pRxBuffer_p->m_NetTime.m_dwSec;
if (AmiGetWordFromBe(&pFrame->m_be_wEtherType) != EPL_C_DLL_ETHERTYPE_EPL) { // non-EPL frame
//TRACE2("EplDllkCbFrameReceived: pfnCbAsync=0x%p SrcMAC=0x%llx\n", EplDllkInstance_g.m_pfnCbAsync, AmiGetQword48FromBe(pFrame->m_be_abSrcMac));
if (EplDllkInstance_g.m_pfnCbAsync != NULL) { // handler for async frames is registered
EplDllkInstance_g.m_pfnCbAsync(&FrameInfo);
}
goto Exit;
}
MsgType = (tEplMsgType) AmiGetByteFromLe(&pFrame->m_le_bMessageType);
switch (MsgType) {
case kEplMsgTypePreq:
{
// PReq frame
// d.k.: (we assume that this PReq frame is intended for us and don't check DstNodeId)
if (AmiGetByteFromLe(&pFrame->m_le_bDstNodeId) != EplDllkInstance_g.m_DllConfigParam.m_uiNodeId) { // this PReq is not intended for us
goto Exit;
}
NmtEvent = kEplNmtEventDllCePreq;
if (NmtState >= kEplNmtMsNotActive) { // MN is active -> wrong msg type
break;
}
#if EDRV_EARLY_RX_INT == FALSE
if (NmtState >= kEplNmtCsPreOperational2) { // respond to and process PReq frames only in PreOp2, ReadyToOp and Op
// Does PRes exist?
pTxBuffer =
&EplDllkInstance_g.
m_pTxBuffer[EPL_DLLK_TXFRAME_PRES];
if (pTxBuffer->m_pbBuffer != NULL) { // PRes does exist
#if (EPL_DLL_PRES_READY_AFTER_SOA != FALSE) || (EPL_DLL_PRES_READY_AFTER_SOC != FALSE)
EdrvTxMsgStart(pTxBuffer);
#else
pTxFrame =
(tEplFrame *) pTxBuffer->m_pbBuffer;
// update frame (NMT state, RD, RS, PR, MS, EN flags)
AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.
m_le_bNmtStatus,
(BYTE) NmtState);
AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.
m_le_bFlag2,
EplDllkInstance_g.
m_bFlag2);
bFlag1 =
AmiGetByteFromLe(&pFrame->m_Data.
m_Preq.
m_le_bFlag1);
// save EA flag
EplDllkInstance_g.m_bMnFlag1 =
(EplDllkInstance_g.
m_bMnFlag1 & ~EPL_FRAME_FLAG1_EA)
| (bFlag1 & EPL_FRAME_FLAG1_EA);
// preserve MS flag
bFlag1 &= EPL_FRAME_FLAG1_MS;
// add EN flag from Error signaling module
bFlag1 |=
EplDllkInstance_g.
m_bFlag1 & EPL_FRAME_FLAG1_EN;
if (NmtState != kEplNmtCsOperational) { // mark PDO as invalid in NMT state Op
// reset only RD flag
AmiSetByteToLe(&pTxFrame->
m_Data.m_Pres.
m_le_bFlag1,
bFlag1);
} else { // leave RD flag untouched
AmiSetByteToLe(&pTxFrame->
m_Data.m_Pres.
m_le_bFlag1,
(AmiGetByteFromLe
(&pTxFrame->
m_Data.m_Pres.
m_le_bFlag1) &
EPL_FRAME_FLAG1_RD)
| bFlag1);
}
// $$$ update EPL_DLL_PRES_READY_AFTER_* code
// send PRes frame
Ret = EdrvSendTxMsg(pTxBuffer);
if (Ret != kEplSuccessful) {
goto Exit;
}
#endif
}
#endif
// inform PDO module
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_PDOK)) != 0)
if (NmtState >= kEplNmtCsReadyToOperate) { // inform PDO module only in ReadyToOp and Op
if (NmtState != kEplNmtCsOperational) {
// reset RD flag and all other flags, but that does not matter, because they were processed above
AmiSetByteToLe(&pFrame->m_Data.
m_Preq.
m_le_bFlag1, 0);
}
// compares real frame size and PDO size
if ((unsigned
int)(AmiGetWordFromLe(&pFrame->
m_Data.
m_Preq.
m_le_wSize) +
24)
> FrameInfo.m_uiFrameSize) { // format error
tEplErrorHandlerkEvent DllEvent;
DllEvent.m_ulDllErrorEvents =
EPL_DLL_ERR_INVALID_FORMAT;
DllEvent.m_uiNodeId =
AmiGetByteFromLe(&pFrame->
m_le_bSrcNodeId);
DllEvent.m_NmtState = NmtState;
Event.m_EventSink =
kEplEventSinkErrk;
Event.m_EventType =
kEplEventTypeDllError;
Event.m_NetTime =
FrameInfo.m_NetTime;
Event.m_uiSize =
sizeof(DllEvent);
Event.m_pArg = &DllEvent;
Ret = EplEventkPost(&Event);
break;
}
// forward PReq frame as RPDO to PDO module
Ret = EplPdokCbPdoReceived(&FrameInfo);
}
#if (EPL_DLL_PRES_READY_AFTER_SOC != FALSE)
if (pTxBuffer->m_pbBuffer != NULL) { // PRes does exist
// inform PDO module about PRes after PReq
FrameInfo.m_pFrame =
(tEplFrame *) pTxBuffer->m_pbBuffer;
FrameInfo.m_uiFrameSize =
pTxBuffer->m_uiMaxBufferLen;
Ret =
EplPdokCbPdoTransmitted(&FrameInfo);
}
#endif
#endif
#if EDRV_EARLY_RX_INT == FALSE
// $$$ inform emergency protocol handling (error signaling module) about flags
}
#endif
// reset cycle counter
EplDllkInstance_g.m_uiCycleCount = 0;
break;
}
case kEplMsgTypePres:
{
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
tEplDllkNodeInfo *pIntNodeInfo;
tEplHeartbeatEvent HeartbeatEvent;
#endif
// PRes frame
NmtEvent = kEplNmtEventDllCePres;
uiNodeId = AmiGetByteFromLe(&pFrame->m_le_bSrcNodeId);
if ((NmtState >= kEplNmtCsPreOperational2)
&& (NmtState <= kEplNmtCsOperational)) { // process PRes frames only in PreOp2, ReadyToOp and Op of CN
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
pIntNodeInfo = EplDllkGetNodeInfo(uiNodeId);
if (pIntNodeInfo == NULL) { // no node info structure available
Ret = kEplDllNoNodeInfo;
goto Exit;
}
} else if (EplDllkInstance_g.m_DllState == kEplDllMsWaitPres) { // or process PRes frames in MsWaitPres
pIntNodeInfo = EplDllkInstance_g.m_pCurNodeInfo;
if ((pIntNodeInfo == NULL) || (pIntNodeInfo->m_uiNodeId != uiNodeId)) { // ignore PRes, because it is from wrong CN
// $$$ maybe post event to NmtMn module
goto Exit;
}
// forward Flag2 to asynchronous scheduler
bFlag1 =
AmiGetByteFromLe(&pFrame->m_Data.m_Asnd.
m_Payload.m_StatusResponse.
m_le_bFlag2);
Ret =
EplDllkCalAsyncSetPendingRequests(uiNodeId,
((tEplDllAsyncReqPriority) ((bFlag1 & EPL_FRAME_FLAG2_PR) >> EPL_FRAME_FLAG2_PR_SHIFT)), (bFlag1 & EPL_FRAME_FLAG2_RS));
#endif
} else { // ignore PRes, because it was received in wrong NMT state
// but execute EplDllkChangeState() and post event to NMT module
break;
}
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
{ // check NMT state of CN
HeartbeatEvent.m_wErrorCode = EPL_E_NO_ERROR;
HeartbeatEvent.m_NmtState =
(tEplNmtState) (AmiGetByteFromLe
(&pFrame->m_Data.m_Pres.
m_le_bNmtStatus) |
EPL_NMT_TYPE_CS);
if (pIntNodeInfo->m_NmtState != HeartbeatEvent.m_NmtState) { // NMT state of CN has changed -> post event to NmtMnu module
if (pIntNodeInfo->m_fSoftDelete == FALSE) { // normal isochronous CN
HeartbeatEvent.m_uiNodeId =
uiNodeId;
Event.m_EventSink =
kEplEventSinkNmtMnu;
Event.m_EventType =
kEplEventTypeHeartbeat;
Event.m_uiSize =
sizeof(HeartbeatEvent);
Event.m_pArg = &HeartbeatEvent;
} else { // CN shall be deleted softly
Event.m_EventSink =
kEplEventSinkDllkCal;
Event.m_EventType =
kEplEventTypeDllkSoftDelNode;
Event.m_uiSize =
sizeof(unsigned int);
Event.m_pArg =
&pIntNodeInfo->m_uiNodeId;
}
Event.m_NetTime = FrameInfo.m_NetTime;
Ret = EplEventkPost(&Event);
// save current NMT state of CN in internal node structure
pIntNodeInfo->m_NmtState =
HeartbeatEvent.m_NmtState;
}
}
#endif
// inform PDO module
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_PDOK)) != 0)
if ((NmtState != kEplNmtCsPreOperational2)
&& (NmtState != kEplNmtMsPreOperational2)) { // inform PDO module only in ReadyToOp and Op
// compare real frame size and PDO size?
if (((unsigned
int)(AmiGetWordFromLe(&pFrame->m_Data.
m_Pres.m_le_wSize) +
24)
> FrameInfo.m_uiFrameSize)
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
||
(AmiGetWordFromLe
(&pFrame->m_Data.m_Pres.m_le_wSize) >
pIntNodeInfo->m_wPresPayloadLimit)
#endif
) { // format error
tEplErrorHandlerkEvent DllEvent;
DllEvent.m_ulDllErrorEvents =
EPL_DLL_ERR_INVALID_FORMAT;
DllEvent.m_uiNodeId = uiNodeId;
DllEvent.m_NmtState = NmtState;
Event.m_EventSink = kEplEventSinkErrk;
Event.m_EventType =
kEplEventTypeDllError;
Event.m_NetTime = FrameInfo.m_NetTime;
Event.m_uiSize = sizeof(DllEvent);
Event.m_pArg = &DllEvent;
Ret = EplEventkPost(&Event);
break;
}
if ((NmtState != kEplNmtCsOperational)
&& (NmtState != kEplNmtMsOperational)) {
// reset RD flag and all other flags, but that does not matter, because they were processed above
AmiSetByteToLe(&pFrame->m_Data.m_Pres.
m_le_bFlag1, 0);
}
Ret = EplPdokCbPdoReceived(&FrameInfo);
}
#endif
break;
}
case kEplMsgTypeSoc:
{
// SoC frame
NmtEvent = kEplNmtEventDllCeSoc;
if (NmtState >= kEplNmtMsNotActive) { // MN is active -> wrong msg type
break;
}
#if EPL_DLL_PRES_READY_AFTER_SOC != FALSE
// post PRes to transmit FIFO of the ethernet controller, but don't start
// transmission over bus
pTxBuffer =
&EplDllkInstance_g.
m_pTxBuffer[EPL_DLLK_TXFRAME_PRES];
// Does PRes exist?
if (pTxBuffer->m_pbBuffer != NULL) { // PRes does exist
pTxFrame = (tEplFrame *) pTxBuffer->m_pbBuffer;
// update frame (NMT state, RD, RS, PR, MS, EN flags)
if (NmtState < kEplNmtCsPreOperational2) { // NMT state is not PreOp2, ReadyToOp or Op
// fake NMT state PreOp2, because PRes will be sent only in PreOp2 or greater
NmtState = kEplNmtCsPreOperational2;
}
AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.
m_le_bNmtStatus,
(BYTE) NmtState);
AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.
m_le_bFlag2,
EplDllkInstance_g.m_bFlag2);
if (NmtState != kEplNmtCsOperational) { // mark PDO as invalid in NMT state Op
// $$$ reset only RD flag; set other flags appropriately
AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.
m_le_bFlag1, 0);
}
// $$$ make function that updates Pres, StatusRes
// mark PRes frame as ready for transmission
Ret = EdrvTxMsgReady(pTxBuffer);
}
#endif
if (NmtState >= kEplNmtCsPreOperational2) { // SoC frames only in PreOp2, ReadyToOp and Op
// trigger synchronous task
Event.m_EventSink = kEplEventSinkSync;
Event.m_EventType = kEplEventTypeSync;
Event.m_uiSize = 0;
Ret = EplEventkPost(&Event);
// update cycle counter
if (EplDllkInstance_g.m_DllConfigParam.m_uiMultiplCycleCnt > 0) { // multiplexed cycle active
EplDllkInstance_g.m_uiCycleCount =
(EplDllkInstance_g.m_uiCycleCount +
1) %
EplDllkInstance_g.m_DllConfigParam.
m_uiMultiplCycleCnt;
}
}
// reprogram timer
#if EPL_TIMER_USE_HIGHRES != FALSE
if (EplDllkInstance_g.m_ullFrameTimeout != 0) {
Ret =
EplTimerHighReskModifyTimerNs
(&EplDllkInstance_g.m_TimerHdlCycle,
EplDllkInstance_g.m_ullFrameTimeout,
EplDllkCbCnTimer, 0L, FALSE);
}
#endif
break;
}
case kEplMsgTypeSoa:
{
// SoA frame
NmtEvent = kEplNmtEventDllCeSoa;
if (NmtState >= kEplNmtMsNotActive) { // MN is active -> wrong msg type
break;
}
pTxFrame = NULL;
if ((NmtState & EPL_NMT_SUPERSTATE_MASK) != EPL_NMT_CS_EPLMODE) { // do not respond, if NMT state is < PreOp1 (i.e. not EPL_MODE)
break;
}
// check TargetNodeId
uiNodeId =
AmiGetByteFromLe(&pFrame->m_Data.m_Soa.
m_le_bReqServiceTarget);
if (uiNodeId == EplDllkInstance_g.m_DllConfigParam.m_uiNodeId) { // local node is the target of the current request
// check ServiceId
ReqServiceId =
(tEplDllReqServiceId)
AmiGetByteFromLe(&pFrame->m_Data.m_Soa.
m_le_bReqServiceId);
if (ReqServiceId == kEplDllReqServiceStatus) { // StatusRequest
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_STATUSRES].m_pbBuffer != NULL) { // StatusRes does exist
pTxFrame =
(tEplFrame *)
EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_STATUSRES].
m_pbBuffer;
// update StatusRes frame (NMT state, EN, EC, RS, PR flags)
AmiSetByteToLe(&pTxFrame->
m_Data.m_Asnd.
m_Payload.
m_StatusResponse.
m_le_bNmtStatus,
(BYTE) NmtState);
AmiSetByteToLe(&pTxFrame->
m_Data.m_Asnd.
m_Payload.
m_StatusResponse.
m_le_bFlag1,
EplDllkInstance_g.
m_bFlag1);
AmiSetByteToLe(&pTxFrame->
m_Data.m_Asnd.
m_Payload.
m_StatusResponse.
m_le_bFlag2,
EplDllkInstance_g.
m_bFlag2);
// send StatusRes
Ret =
EdrvSendTxMsg
(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_STATUSRES]);
if (Ret != kEplSuccessful) {
goto Exit;
}
TGT_DBG_SIGNAL_TRACE_POINT(8);
// update error signaling
bFlag1 =
AmiGetByteFromLe(&pFrame->
m_Data.
m_Soa.
m_le_bFlag1);
if (((bFlag1 ^ EplDllkInstance_g.m_bMnFlag1) & EPL_FRAME_FLAG1_ER) != 0) { // exception reset flag was changed by MN
// assume same state for EC in next cycle (clear all other bits)
if ((bFlag1 &
EPL_FRAME_FLAG1_ER)
!= 0) {
// set EC and reset rest
EplDllkInstance_g.
m_bFlag1 =
EPL_FRAME_FLAG1_EC;
} else {
// reset complete flag 1 (including EC and EN)
EplDllkInstance_g.
m_bFlag1 =
0;
}
}
// save flag 1 from MN for Status request response cycle
EplDllkInstance_g.m_bMnFlag1 =
bFlag1;
}
} else if (ReqServiceId == kEplDllReqServiceIdent) { // IdentRequest
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_IDENTRES].m_pbBuffer != NULL) { // IdentRes does exist
pTxFrame =
(tEplFrame *)
EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_IDENTRES].
m_pbBuffer;
// update IdentRes frame (NMT state, RS, PR flags)
AmiSetByteToLe(&pTxFrame->
m_Data.m_Asnd.
m_Payload.
m_IdentResponse.
m_le_bNmtStatus,
(BYTE) NmtState);
AmiSetByteToLe(&pTxFrame->
m_Data.m_Asnd.
m_Payload.
m_IdentResponse.
m_le_bFlag2,
EplDllkInstance_g.
m_bFlag2);
// send IdentRes
Ret =
EdrvSendTxMsg
(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_IDENTRES]);
if (Ret != kEplSuccessful) {
goto Exit;
}
TGT_DBG_SIGNAL_TRACE_POINT(7);
}
} else if (ReqServiceId == kEplDllReqServiceNmtRequest) { // NmtRequest
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NMTREQ].m_pbBuffer != NULL) { // NmtRequest does exist
// check if frame is not empty and not being filled
if (EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NMTREQ].
m_uiTxMsgLen >
EPL_DLLK_BUFLEN_FILLING) {
/*if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NMTREQ].m_uiTxMsgLen < EPL_DLLK_BUFLEN_MIN)
{ // pad frame
EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NMTREQ].m_uiTxMsgLen = EPL_DLLK_BUFLEN_MIN;
} */
// memorize transmission
pTxFrame =
(tEplFrame *) 1;
// send NmtRequest
Ret =
EdrvSendTxMsg
(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NMTREQ]);
if (Ret !=
kEplSuccessful) {
goto Exit;
}
}
}
} else if (ReqServiceId == kEplDllReqServiceUnspecified) { // unspecified invite
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NONEPL].m_pbBuffer != NULL) { // non-EPL frame does exist
// check if frame is not empty and not being filled
if (EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NONEPL].
m_uiTxMsgLen >
EPL_DLLK_BUFLEN_FILLING) {
/*if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NMTREQ].m_uiTxMsgLen < EPL_DLLK_BUFLEN_MIN)
{ // pad frame
EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NMTREQ].m_uiTxMsgLen = EPL_DLLK_BUFLEN_MIN;
} */
// memorize transmission
pTxFrame =
(tEplFrame *) 1;
// send non-EPL frame
Ret =
EdrvSendTxMsg
(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NONEPL]);
if (Ret !=
kEplSuccessful) {
goto Exit;
}
}
}
} else if (ReqServiceId == kEplDllReqServiceNo) { // no async service requested -> do nothing
}
}
#if EPL_DLL_PRES_READY_AFTER_SOA != FALSE
if (pTxFrame == NULL) { // signal process function readiness of PRes frame
Event.m_EventSink = kEplEventSinkDllk;
Event.m_EventType = kEplEventTypeDllkPresReady;
Event.m_uiSize = 0;
Event.m_pArg = NULL;
Ret = EplEventkPost(&Event);
}
#endif
// inform PDO module
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_PDOK)) != 0)
// Ret = EplPdokCbSoa(&FrameInfo);
#endif
// $$$ put SrcNodeId, NMT state and NetTime as HeartbeatEvent into eventqueue
// $$$ inform emergency protocol handling about flags
break;
}
case kEplMsgTypeAsnd:
{
// ASnd frame
NmtEvent = kEplNmtEventDllCeAsnd;
// ASnd service registered?
uiAsndServiceId =
(unsigned int)AmiGetByteFromLe(&pFrame->m_Data.
m_Asnd.
m_le_bServiceId);
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
if ((EplDllkInstance_g.m_DllState >= kEplDllMsNonCyclic)
&&
((((tEplDllAsndServiceId) uiAsndServiceId) ==
kEplDllAsndStatusResponse)
|| (((tEplDllAsndServiceId) uiAsndServiceId) == kEplDllAsndIdentResponse))) { // StatusRes or IdentRes received
uiNodeId =
AmiGetByteFromLe(&pFrame->m_le_bSrcNodeId);
if ((EplDllkInstance_g.m_LastReqServiceId ==
((tEplDllReqServiceId) uiAsndServiceId))
&& (uiNodeId == EplDllkInstance_g.m_uiLastTargetNodeId)) { // mark request as responded
EplDllkInstance_g.m_LastReqServiceId =
kEplDllReqServiceNo;
}
if (((tEplDllAsndServiceId) uiAsndServiceId) == kEplDllAsndIdentResponse) { // memorize MAC address of CN for PReq
tEplDllkNodeInfo *pIntNodeInfo;
pIntNodeInfo =
EplDllkGetNodeInfo(uiNodeId);
if (pIntNodeInfo == NULL) { // no node info structure available
Ret = kEplDllNoNodeInfo;
} else {
EPL_MEMCPY(pIntNodeInfo->
m_be_abMacAddr,
pFrame->
m_be_abSrcMac, 6);
}
}
// forward Flag2 to asynchronous scheduler
bFlag1 =
AmiGetByteFromLe(&pFrame->m_Data.m_Asnd.
m_Payload.m_StatusResponse.
m_le_bFlag2);
Ret =
EplDllkCalAsyncSetPendingRequests(uiNodeId,
((tEplDllAsyncReqPriority) ((bFlag1 & EPL_FRAME_FLAG2_PR) >> EPL_FRAME_FLAG2_PR_SHIFT)), (bFlag1 & EPL_FRAME_FLAG2_RS));
}
#endif
if (uiAsndServiceId < EPL_DLL_MAX_ASND_SERVICE_ID) { // ASnd service ID is valid
if (EplDllkInstance_g.m_aAsndFilter[uiAsndServiceId] == kEplDllAsndFilterAny) { // ASnd service ID is registered
// forward frame via async receive FIFO to userspace
Ret =
EplDllkCalAsyncFrameReceived
(&FrameInfo);
} else if (EplDllkInstance_g.m_aAsndFilter[uiAsndServiceId] == kEplDllAsndFilterLocal) { // ASnd service ID is registered, but only local node ID or broadcasts
// shall be forwarded
uiNodeId =
AmiGetByteFromLe(&pFrame->
m_le_bDstNodeId);
if ((uiNodeId ==
EplDllkInstance_g.m_DllConfigParam.
m_uiNodeId)
|| (uiNodeId == EPL_C_ADR_BROADCAST)) { // ASnd frame is intended for us
// forward frame via async receive FIFO to userspace
Ret =
EplDllkCalAsyncFrameReceived
(&FrameInfo);
}
}
}
break;
}
default:
{
break;
}
}
if (NmtEvent != kEplNmtEventNoEvent) { // event for DLL and NMT state machine generated
Ret = EplDllkChangeState(NmtEvent, NmtState);
if (Ret != kEplSuccessful) {
goto Exit;
}
if ((NmtEvent != kEplNmtEventDllCeAsnd)
&& ((NmtState <= kEplNmtCsPreOperational1) || (NmtEvent != kEplNmtEventDllCePres))) { // NMT state machine is not interested in ASnd frames and PRes frames when not CsNotActive or CsPreOp1
// inform NMT module
Event.m_EventSink = kEplEventSinkNmtk;
Event.m_EventType = kEplEventTypeNmtEvent;
Event.m_uiSize = sizeof(NmtEvent);
Event.m_pArg = &NmtEvent;
Ret = EplEventkPost(&Event);
}
}
Exit:
if (Ret != kEplSuccessful) {
DWORD dwArg;
BENCHMARK_MOD_02_TOGGLE(9);
dwArg = EplDllkInstance_g.m_DllState | (NmtEvent << 8);
// Error event for API layer
Ret = EplEventkPostError(kEplEventSourceDllk,
Ret, sizeof(dwArg), &dwArg);
}
BENCHMARK_MOD_02_RESET(3);
return;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkCbFrameTransmitted()
//
// Description: called from EdrvInterruptHandler().
// It signals
//
// Parameters: pRxBuffer_p = receive buffer structure
//
// Returns: (none)
//
//
// State:
//
//---------------------------------------------------------------------------
static void EplDllkCbFrameTransmitted(tEdrvTxBuffer * pTxBuffer_p)
{
tEplKernel Ret = kEplSuccessful;
tEplEvent Event;
tEplDllAsyncReqPriority Priority;
tEplNmtState NmtState;
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_PDOK)) != 0) \
&& (EPL_DLL_PRES_READY_AFTER_SOC == FALSE)
tEplFrameInfo FrameInfo;
#endif
NmtState = EplNmtkGetNmtState();
if (NmtState <= kEplNmtGsResetConfiguration) {
goto Exit;
}
if ((pTxBuffer_p - EplDllkInstance_g.m_pTxBuffer) == EPL_DLLK_TXFRAME_NMTREQ) { // frame from NMT request FIFO sent
// mark Tx-buffer as empty
pTxBuffer_p->m_uiTxMsgLen = EPL_DLLK_BUFLEN_EMPTY;
// 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);
} else if ((pTxBuffer_p - EplDllkInstance_g.m_pTxBuffer) == EPL_DLLK_TXFRAME_NONEPL) { // frame from generic priority FIFO sent
// mark Tx-buffer as empty
pTxBuffer_p->m_uiTxMsgLen = EPL_DLLK_BUFLEN_EMPTY;
// 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);
}
#if ((((EPL_MODULE_INTEGRATION) & (EPL_MODULE_PDOK)) != 0) \
&& (EPL_DLL_PRES_READY_AFTER_SOC == FALSE)) \
|| (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
else if ((pTxBuffer_p->m_EplMsgType == kEplMsgTypePreq)
|| (pTxBuffer_p->m_EplMsgType == kEplMsgTypePres)) { // PRes resp. PReq frame sent
#if ((((EPL_MODULE_INTEGRATION) & (EPL_MODULE_PDOK)) != 0) \
&& (EPL_DLL_PRES_READY_AFTER_SOC == FALSE))
{
// inform PDO module
FrameInfo.m_pFrame =
(tEplFrame *) pTxBuffer_p->m_pbBuffer;
FrameInfo.m_uiFrameSize = pTxBuffer_p->m_uiMaxBufferLen;
Ret = EplPdokCbPdoTransmitted(&FrameInfo);
}
#endif
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
{
// if own Pres on MN, trigger SoA
if ((NmtState >= kEplNmtMsPreOperational2)
&& (pTxBuffer_p ==
&EplDllkInstance_g.
m_pTxBuffer[EPL_DLLK_TXFRAME_PRES])) {
Ret =
EplDllkChangeState(kEplNmtEventDllMeSoaTrig,
NmtState);
}
}
#endif
#if EPL_DLL_PRES_READY_AFTER_SOA != FALSE
goto Exit;
#endif
}
#endif
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
else if (pTxBuffer_p->m_EplMsgType == kEplMsgTypeSoa) { // SoA frame sent
tEplNmtEvent NmtEvent = kEplNmtEventDllMeSoaSent;
// check if we are invited
if (EplDllkInstance_g.m_uiLastTargetNodeId ==
EplDllkInstance_g.m_DllConfigParam.m_uiNodeId) {
tEplFrame *pTxFrame;
if (EplDllkInstance_g.m_LastReqServiceId == kEplDllReqServiceStatus) { // StatusRequest
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_STATUSRES].m_pbBuffer != NULL) { // StatusRes does exist
pTxFrame =
(tEplFrame *) EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_STATUSRES].
m_pbBuffer;
// update StatusRes frame (NMT state, EN, EC, RS, PR flags)
AmiSetByteToLe(&pTxFrame->m_Data.m_Asnd.
m_Payload.
m_StatusResponse.
m_le_bNmtStatus,
(BYTE) NmtState);
AmiSetByteToLe(&pTxFrame->m_Data.m_Asnd.
m_Payload.
m_StatusResponse.
m_le_bFlag1,
EplDllkInstance_g.
m_bFlag1);
AmiSetByteToLe(&pTxFrame->m_Data.m_Asnd.
m_Payload.
m_StatusResponse.
m_le_bFlag2,
EplDllkInstance_g.
m_bFlag2);
// send StatusRes
Ret =
EdrvSendTxMsg(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_STATUSRES]);
if (Ret != kEplSuccessful) {
goto Exit;
}
TGT_DBG_SIGNAL_TRACE_POINT(8);
}
} else if (EplDllkInstance_g.m_LastReqServiceId == kEplDllReqServiceIdent) { // IdentRequest
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_IDENTRES].m_pbBuffer != NULL) { // IdentRes does exist
pTxFrame =
(tEplFrame *) EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_IDENTRES].
m_pbBuffer;
// update IdentRes frame (NMT state, RS, PR flags)
AmiSetByteToLe(&pTxFrame->m_Data.m_Asnd.
m_Payload.
m_IdentResponse.
m_le_bNmtStatus,
(BYTE) NmtState);
AmiSetByteToLe(&pTxFrame->m_Data.m_Asnd.
m_Payload.
m_IdentResponse.
m_le_bFlag2,
EplDllkInstance_g.
m_bFlag2);
// send IdentRes
Ret =
EdrvSendTxMsg(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_IDENTRES]);
if (Ret != kEplSuccessful) {
goto Exit;
}
TGT_DBG_SIGNAL_TRACE_POINT(7);
}
} else if (EplDllkInstance_g.m_LastReqServiceId == kEplDllReqServiceNmtRequest) { // NmtRequest
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NMTREQ].m_pbBuffer != NULL) { // NmtRequest does exist
// check if frame is not empty and not being filled
if (EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NMTREQ].
m_uiTxMsgLen >
EPL_DLLK_BUFLEN_FILLING) {
// check if this frame is a NMT command,
// then forward this frame back to NmtMnu module,
// because it needs the time, when this frame is
// actually sent, to start the timer for monitoring
// the NMT state change.
pTxFrame =
(tEplFrame *)
EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NMTREQ].
m_pbBuffer;
if ((AmiGetByteFromLe
(&pTxFrame->
m_le_bMessageType)
== (BYTE) kEplMsgTypeAsnd)
&&
(AmiGetByteFromLe
(&pTxFrame->m_Data.m_Asnd.
m_le_bServiceId)
== (BYTE) kEplDllAsndNmtCommand)) { // post event directly to NmtMnu module
Event.m_EventSink =
kEplEventSinkNmtMnu;
Event.m_EventType =
kEplEventTypeNmtMnuNmtCmdSent;
Event.m_uiSize =
EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NMTREQ].
m_uiTxMsgLen;
Event.m_pArg = pTxFrame;
Ret =
EplEventkPost
(&Event);
}
// send NmtRequest
Ret =
EdrvSendTxMsg
(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NMTREQ]);
if (Ret != kEplSuccessful) {
goto Exit;
}
}
}
} else if (EplDllkInstance_g.m_LastReqServiceId == kEplDllReqServiceUnspecified) { // unspecified invite
if (EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_NONEPL].m_pbBuffer != NULL) { // non-EPL frame does exist
// check if frame is not empty and not being filled
if (EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NONEPL].
m_uiTxMsgLen >
EPL_DLLK_BUFLEN_FILLING) {
// send non-EPL frame
Ret =
EdrvSendTxMsg
(&EplDllkInstance_g.
m_pTxBuffer
[EPL_DLLK_TXFRAME_NONEPL]);
if (Ret != kEplSuccessful) {
goto Exit;
}
}
}
}
// ASnd frame was sent, remove the request
EplDllkInstance_g.m_LastReqServiceId =
kEplDllReqServiceNo;
}
// forward event to ErrorHandler and PDO module
Event.m_EventSink = kEplEventSinkNmtk;
Event.m_EventType = kEplEventTypeNmtEvent;
Event.m_uiSize = sizeof(NmtEvent);
Event.m_pArg = &NmtEvent;
Ret = EplEventkPost(&Event);
if (Ret != kEplSuccessful) {
goto Exit;
}
}
#endif
#if EPL_DLL_PRES_READY_AFTER_SOA != FALSE
else { // d.k.: Why that else? on CN it is entered on IdentRes and StatusRes
goto Exit;
}
// signal process function readiness of PRes frame
Event.m_EventSink = kEplEventSinkDllk;
Event.m_EventType = kEplEventTypeDllkPresReady;
Event.m_uiSize = 0;
Event.m_pArg = NULL;
Ret = EplEventkPost(&Event);
#endif
Exit:
if (Ret != kEplSuccessful) {
DWORD dwArg;
BENCHMARK_MOD_02_TOGGLE(9);
dwArg =
EplDllkInstance_g.m_DllState | (pTxBuffer_p->
m_EplMsgType << 16);
// Error event for API layer
Ret = EplEventkPostError(kEplEventSourceDllk,
Ret, sizeof(dwArg), &dwArg);
}
return;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkCheckFrame()
//
// Description: check frame and set missing information
//
// Parameters: pFrame_p = ethernet frame
// uiFrameSize_p = size of frame
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel EplDllkCheckFrame(tEplFrame * pFrame_p,
unsigned int uiFrameSize_p)
{
tEplMsgType MsgType;
WORD wEtherType;
// check frame
if (pFrame_p != NULL) {
// check SrcMAC
if (AmiGetQword48FromBe(pFrame_p->m_be_abSrcMac) == 0) {
// source MAC address
EPL_MEMCPY(&pFrame_p->m_be_abSrcMac[0],
&EplDllkInstance_g.m_be_abSrcMac[0], 6);
}
// check ethertype
wEtherType = AmiGetWordFromBe(&pFrame_p->m_be_wEtherType);
if (wEtherType == 0) {
// assume EPL frame
wEtherType = EPL_C_DLL_ETHERTYPE_EPL;
AmiSetWordToBe(&pFrame_p->m_be_wEtherType, wEtherType);
}
if (wEtherType == EPL_C_DLL_ETHERTYPE_EPL) {
// source node ID
AmiSetByteToLe(&pFrame_p->m_le_bSrcNodeId,
(BYTE) EplDllkInstance_g.
m_DllConfigParam.m_uiNodeId);
// check message type
MsgType =
AmiGetByteFromLe(&pFrame_p->m_le_bMessageType);
if (MsgType == 0) {
MsgType = kEplMsgTypeAsnd;
AmiSetByteToLe(&pFrame_p->m_le_bMessageType,
(BYTE) MsgType);
}
if (MsgType == kEplMsgTypeAsnd) {
// destination MAC address
AmiSetQword48ToBe(&pFrame_p->m_be_abDstMac[0],
EPL_C_DLL_MULTICAST_ASND);
}
}
}
return kEplSuccessful;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkCbCnTimer()
//
// Description: called by timer module. It monitors the EPL cycle when it is a CN.
//
// Parameters: pEventArg_p = timer event argument
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
#if EPL_TIMER_USE_HIGHRES != FALSE
static tEplKernel PUBLIC EplDllkCbCnTimer(tEplTimerEventArg * pEventArg_p)
{
tEplKernel Ret = kEplSuccessful;
tEplNmtState NmtState;
#if EPL_TIMER_USE_HIGHRES != FALSE
if (pEventArg_p->m_TimerHdl != EplDllkInstance_g.m_TimerHdlCycle) { // zombie callback
// just exit
goto Exit;
}
#endif
NmtState = EplNmtkGetNmtState();
if (NmtState <= kEplNmtGsResetConfiguration) {
goto Exit;
}
Ret = EplDllkChangeState(kEplNmtEventDllCeFrameTimeout, NmtState);
if (Ret != kEplSuccessful) {
goto Exit;
}
// 2008/10/15 d.k. reprogramming of timer not necessary,
// because it will be programmed, when SoC is received.
/*
// reprogram timer
#if EPL_TIMER_USE_HIGHRES != FALSE
if ((NmtState > kEplNmtCsPreOperational1)
&& (EplDllkInstance_g.m_ullFrameTimeout != 0))
{
Ret = EplTimerHighReskModifyTimerNs(&EplDllkInstance_g.m_TimerHdlCycle, EplDllkInstance_g.m_ullFrameTimeout, EplDllkCbCnTimer, 0L, FALSE);
}
#endif
*/
Exit:
if (Ret != kEplSuccessful) {
DWORD dwArg;
BENCHMARK_MOD_02_TOGGLE(9);
dwArg =
EplDllkInstance_g.
m_DllState | (kEplNmtEventDllCeFrameTimeout << 8);
// Error event for API layer
Ret = EplEventkPostError(kEplEventSourceDllk,
Ret, sizeof(dwArg), &dwArg);
}
return Ret;
}
#endif
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
//---------------------------------------------------------------------------
//
// Function: EplDllkCbMnTimerCycle()
//
// Description: called by timer module. It triggers the SoC when it is a MN.
//
// Parameters: pEventArg_p = timer event argument
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel PUBLIC EplDllkCbMnTimerCycle(tEplTimerEventArg * pEventArg_p)
{
tEplKernel Ret = kEplSuccessful;
tEplNmtState NmtState;
#if EPL_TIMER_USE_HIGHRES != FALSE
if (pEventArg_p->m_TimerHdl != EplDllkInstance_g.m_TimerHdlCycle) { // zombie callback
// just exit
goto Exit;
}
#endif
NmtState = EplNmtkGetNmtState();
if (NmtState <= kEplNmtGsResetConfiguration) {
goto Exit;
}
Ret = EplDllkChangeState(kEplNmtEventDllMeSocTrig, NmtState);
Exit:
if (Ret != kEplSuccessful) {
DWORD dwArg;
BENCHMARK_MOD_02_TOGGLE(9);
dwArg =
EplDllkInstance_g.
m_DllState | (kEplNmtEventDllMeSocTrig << 8);
// Error event for API layer
Ret = EplEventkPostError(kEplEventSourceDllk,
Ret, sizeof(dwArg), &dwArg);
}
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkCbMnTimerResponse()
//
// Description: called by timer module. It monitors the PRes timeout.
//
// Parameters: pEventArg_p = timer event argument
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel PUBLIC EplDllkCbMnTimerResponse(tEplTimerEventArg *
pEventArg_p)
{
tEplKernel Ret = kEplSuccessful;
tEplNmtState NmtState;
#if EPL_TIMER_USE_HIGHRES != FALSE
if (pEventArg_p->m_TimerHdl != EplDllkInstance_g.m_TimerHdlResponse) { // zombie callback
// just exit
goto Exit;
}
#endif
NmtState = EplNmtkGetNmtState();
if (NmtState <= kEplNmtGsResetConfiguration) {
goto Exit;
}
Ret = EplDllkChangeState(kEplNmtEventDllMePresTimeout, NmtState);
Exit:
if (Ret != kEplSuccessful) {
DWORD dwArg;
BENCHMARK_MOD_02_TOGGLE(9);
dwArg =
EplDllkInstance_g.
m_DllState | (kEplNmtEventDllMePresTimeout << 8);
// Error event for API layer
Ret = EplEventkPostError(kEplEventSourceDllk,
Ret, sizeof(dwArg), &dwArg);
}
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkGetNodeInfo()
//
// Description: returns node info structure of the specified node.
//
// Parameters: uiNodeId_p = node ID
//
// Returns: tEplDllkNodeInfo* = pointer to internal node info structure
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplDllkNodeInfo *EplDllkGetNodeInfo(unsigned int uiNodeId_p)
{
// $$$ d.k.: use hash algorithm to retrieve the appropriate node info structure
// if size of array is less than 254.
uiNodeId_p--; // node ID starts at 1 but array at 0
if (uiNodeId_p >= tabentries(EplDllkInstance_g.m_aNodeInfo)) {
return NULL;
} else {
return &EplDllkInstance_g.m_aNodeInfo[uiNodeId_p];
}
}
//---------------------------------------------------------------------------
//
// Function: EplDllkMnSendSoa()
//
// Description: it updates and transmits the SoA.
//
// Parameters: NmtState_p = current NMT state
// pDllStateProposed_p = proposed DLL state
// fEnableInvitation_p = enable invitation for asynchronous phase
// it will be disabled for EPL_C_DLL_PREOP1_START_CYCLES SoAs
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel EplDllkMnSendSoa(tEplNmtState NmtState_p,
tEplDllState * pDllStateProposed_p,
BOOL fEnableInvitation_p)
{
tEplKernel Ret = kEplSuccessful;
tEdrvTxBuffer *pTxBuffer = NULL;
tEplFrame *pTxFrame;
tEplDllkNodeInfo *pNodeInfo;
*pDllStateProposed_p = kEplDllMsNonCyclic;
pTxBuffer = &EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_SOA];
if (pTxBuffer->m_pbBuffer != NULL) { // SoA does exist
pTxFrame = (tEplFrame *) pTxBuffer->m_pbBuffer;
if (fEnableInvitation_p != FALSE) { // fetch target of asynchronous phase
if (EplDllkInstance_g.m_bFlag2 == 0) { // own queues are empty
EplDllkInstance_g.m_LastReqServiceId =
kEplDllReqServiceNo;
} else if (((tEplDllAsyncReqPriority) (EplDllkInstance_g.m_bFlag2 >> EPL_FRAME_FLAG2_PR_SHIFT)) == kEplDllAsyncReqPrioNmt) { // frames in own NMT request queue available
EplDllkInstance_g.m_LastReqServiceId =
kEplDllReqServiceNmtRequest;
} else {
EplDllkInstance_g.m_LastReqServiceId =
kEplDllReqServiceUnspecified;
}
Ret =
EplDllkCalAsyncGetSoaRequest(&EplDllkInstance_g.
m_LastReqServiceId,
&EplDllkInstance_g.
m_uiLastTargetNodeId);
if (Ret != kEplSuccessful) {
goto Exit;
}
if (EplDllkInstance_g.m_LastReqServiceId != kEplDllReqServiceNo) { // asynchronous phase will be assigned to one node
if (EplDllkInstance_g.m_uiLastTargetNodeId == EPL_C_ADR_INVALID) { // exchange invalid node ID with local node ID
EplDllkInstance_g.m_uiLastTargetNodeId =
EplDllkInstance_g.m_DllConfigParam.
m_uiNodeId;
// d.k. DLL state WaitAsndTrig is not helpful;
// so just step over to WaitSocTrig,
// because own ASnd is sent automatically in CbFrameTransmitted() after SoA.
//*pDllStateProposed_p = kEplDllMsWaitAsndTrig;
*pDllStateProposed_p =
kEplDllMsWaitSocTrig;
} else { // assignment to CN
*pDllStateProposed_p =
kEplDllMsWaitAsnd;
}
pNodeInfo =
EplDllkGetNodeInfo(EplDllkInstance_g.
m_uiLastTargetNodeId);
if (pNodeInfo == NULL) { // no node info structure available
Ret = kEplDllNoNodeInfo;
goto Exit;
}
// update frame (EA, ER flags)
AmiSetByteToLe(&pTxFrame->m_Data.m_Soa.
m_le_bFlag1,
pNodeInfo->
m_bSoaFlag1 & (EPL_FRAME_FLAG1_EA
|
EPL_FRAME_FLAG1_ER));
} else { // no assignment of asynchronous phase
*pDllStateProposed_p = kEplDllMsWaitSocTrig;
EplDllkInstance_g.m_uiLastTargetNodeId =
EPL_C_ADR_INVALID;
}
// update frame (target)
AmiSetByteToLe(&pTxFrame->m_Data.m_Soa.
m_le_bReqServiceId,
(BYTE) EplDllkInstance_g.
m_LastReqServiceId);
AmiSetByteToLe(&pTxFrame->m_Data.m_Soa.
m_le_bReqServiceTarget,
(BYTE) EplDllkInstance_g.
m_uiLastTargetNodeId);
} else { // invite nobody
// update frame (target)
AmiSetByteToLe(&pTxFrame->m_Data.m_Soa.
m_le_bReqServiceId, (BYTE) 0);
AmiSetByteToLe(&pTxFrame->m_Data.m_Soa.
m_le_bReqServiceTarget, (BYTE) 0);
}
// update frame (NMT state)
AmiSetByteToLe(&pTxFrame->m_Data.m_Soa.m_le_bNmtStatus,
(BYTE) NmtState_p);
// send SoA frame
Ret = EdrvSendTxMsg(pTxBuffer);
}
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkMnSendSoc()
//
// Description: it updates and transmits the SoA.
//
// Parameters: (none)
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel EplDllkMnSendSoc(void)
{
tEplKernel Ret = kEplSuccessful;
tEdrvTxBuffer *pTxBuffer = NULL;
tEplFrame *pTxFrame;
tEplEvent Event;
pTxBuffer = &EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_SOC];
if (pTxBuffer->m_pbBuffer != NULL) { // SoC does exist
pTxFrame = (tEplFrame *) pTxBuffer->m_pbBuffer;
// $$$ update NetTime
// send SoC frame
Ret = EdrvSendTxMsg(pTxBuffer);
if (Ret != kEplSuccessful) {
goto Exit;
}
// trigger synchronous task
Event.m_EventSink = kEplEventSinkSync;
Event.m_EventType = kEplEventTypeSync;
Event.m_uiSize = 0;
Ret = EplEventkPost(&Event);
}
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkMnSendPreq()
//
// Description: it updates and transmits the PReq for the next isochronous CN
// or own PRes if enabled.
//
// Parameters: NmtState_p = current NMT state
// pDllStateProposed_p = proposed DLL state
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel EplDllkMnSendPreq(tEplNmtState NmtState_p,
tEplDllState * pDllStateProposed_p)
{
tEplKernel Ret = kEplSuccessful;
tEdrvTxBuffer *pTxBuffer = NULL;
tEplFrame *pTxFrame;
BYTE bFlag1 = 0;
if (EplDllkInstance_g.m_pCurNodeInfo == NULL) { // start with first isochronous CN
EplDllkInstance_g.m_pCurNodeInfo =
EplDllkInstance_g.m_pFirstNodeInfo;
} else { // iterate to next isochronous CN
EplDllkInstance_g.m_pCurNodeInfo =
EplDllkInstance_g.m_pCurNodeInfo->m_pNextNodeInfo;
}
if (EplDllkInstance_g.m_pCurNodeInfo == NULL) { // last isochronous CN reached
Ret = EplDllkMnSendSoa(NmtState_p, pDllStateProposed_p, TRUE);
goto Exit;
} else {
pTxBuffer = EplDllkInstance_g.m_pCurNodeInfo->m_pPreqTxBuffer;
bFlag1 =
EplDllkInstance_g.m_pCurNodeInfo->
m_bSoaFlag1 & EPL_FRAME_FLAG1_EA;
*pDllStateProposed_p = kEplDllMsWaitPres;
// start PRes Timer
// $$$ d.k.: maybe move this call to CbFrameTransmitted(), because the time should run from there
#if EPL_TIMER_USE_HIGHRES != FALSE
Ret =
EplTimerHighReskModifyTimerNs(&EplDllkInstance_g.
m_TimerHdlResponse,
EplDllkInstance_g.
m_pCurNodeInfo->
m_dwPresTimeout,
EplDllkCbMnTimerResponse, 0L,
FALSE);
#endif
}
if (pTxBuffer == NULL) { // PReq does not exist
Ret = kEplDllTxBufNotReady;
goto Exit;
}
pTxFrame = (tEplFrame *) pTxBuffer->m_pbBuffer;
if (pTxFrame != NULL) { // PReq does exist
if (NmtState_p == kEplNmtMsOperational) { // leave RD flag untouched
bFlag1 |=
AmiGetByteFromLe(&pTxFrame->m_Data.m_Preq.
m_le_bFlag1) & EPL_FRAME_FLAG1_RD;
}
if (pTxBuffer == &EplDllkInstance_g.m_pTxBuffer[EPL_DLLK_TXFRAME_PRES]) { // PRes of MN will be sent
// update NMT state
AmiSetByteToLe(&pTxFrame->m_Data.m_Pres.m_le_bNmtStatus,
(BYTE) NmtState_p);
*pDllStateProposed_p = kEplDllMsWaitSoaTrig;
}
// $$$ d.k. set EPL_FRAME_FLAG1_MS if necessary
// update frame (Flag1)
AmiSetByteToLe(&pTxFrame->m_Data.m_Preq.m_le_bFlag1, bFlag1);
// calculate frame size from payload size
pTxBuffer->m_uiTxMsgLen =
AmiGetWordFromLe(&pTxFrame->m_Data.m_Preq.m_le_wSize) + 24;
// send PReq frame
Ret = EdrvSendTxMsg(pTxBuffer);
} else {
Ret = kEplDllTxFrameInvalid;
}
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplDllkAsyncFrameNotReceived()
//
// Description: passes empty ASnd frame to receive FIFO.
// It will be called only for frames with registered AsndServiceIds
// (only kEplDllAsndFilterAny).
//
// Parameters: none
//
// Returns: tEplKernel = error code
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel EplDllkAsyncFrameNotReceived(tEplDllReqServiceId
ReqServiceId_p,
unsigned int uiNodeId_p)
{
tEplKernel Ret = kEplSuccessful;
BYTE abBuffer[18];
tEplFrame *pFrame = (tEplFrame *) abBuffer;
tEplFrameInfo FrameInfo;
// check if previous SoA invitation was not answered
switch (ReqServiceId_p) {
case kEplDllReqServiceIdent:
case kEplDllReqServiceStatus:
// ASnd service registered?
if (EplDllkInstance_g.m_aAsndFilter[ReqServiceId_p] == kEplDllAsndFilterAny) { // ASnd service ID is registered
AmiSetByteToLe(&pFrame->m_le_bSrcNodeId,
(BYTE) uiNodeId_p);
// EPL MsgType ASnd
AmiSetByteToLe(&pFrame->m_le_bMessageType,
(BYTE) kEplMsgTypeAsnd);
// ASnd Service ID
AmiSetByteToLe(&pFrame->m_Data.m_Asnd.m_le_bServiceId,
(BYTE) ReqServiceId_p);
// create frame info structure
FrameInfo.m_pFrame = pFrame;
FrameInfo.m_uiFrameSize = 18; // empty non existing ASnd frame
// forward frame via async receive FIFO to userspace
Ret = EplDllkCalAsyncFrameReceived(&FrameInfo);
}
break;
default:
// no invitation issued or it was successfully answered or it is uninteresting
break;
}
return Ret;
}
#endif //(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0)
#endif // #if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_DLLK)) != 0)
// EOF