diff options
Diffstat (limited to 'drivers/staging/epl/EplDllk.c')
-rw-r--r-- | drivers/staging/epl/EplDllk.c | 3783 |
1 files changed, 3783 insertions, 0 deletions
diff --git a/drivers/staging/epl/EplDllk.c b/drivers/staging/epl/EplDllk.c new file mode 100644 index 00000000000..dee0d3dc2f3 --- /dev/null +++ b/drivers/staging/epl/EplDllk.c @@ -0,0 +1,3783 @@ +/**************************************************************************** + + (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 + |