/**************************************************************************** (c) SYSTEC electronic GmbH, D-07973 Greiz, August-Bebel-Str. 29 www.systec-electronic.com Project: openPOWERLINK Description: source file for NMT-MN-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: EplNmtMnu.c,v $ $Author: D.Krueger $ $Revision: 1.18 $ $Date: 2008/11/19 09:52:24 $ $State: Exp $ Build Environment: GCC V3.4 ------------------------------------------------------------------------- Revision History: 2006/06/09 k.t.: start of the implementation ****************************************************************************/ #include "user/EplNmtMnu.h" #include "user/EplTimeru.h" #include "user/EplIdentu.h" #include "user/EplStatusu.h" #include "user/EplObdu.h" #include "user/EplDlluCal.h" #include "Benchmark.h" #if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0) #if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_OBDU)) == 0) && (EPL_OBD_USE_KERNEL == FALSE) #error "EPL NmtMnu module needs EPL module OBDU or OBDK!" #endif //=========================================================================// // // // P R I V A T E 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_NMTMNU_DBG_POST_TRACE_VALUE(Event_p, uiNodeId_p, wErrorCode_p) \ TGT_DBG_POST_TRACE_VALUE((kEplEventSinkNmtMnu << 28) | (Event_p << 24) \ | (uiNodeId_p << 16) | wErrorCode_p) // defines for flags in node info structure #define EPL_NMTMNU_NODE_FLAG_ISOCHRON 0x0001 // CN is being accessed isochronously #define EPL_NMTMNU_NODE_FLAG_NOT_SCANNED 0x0002 // CN was not scanned once -> decrement SignalCounter and reset flag #define EPL_NMTMNU_NODE_FLAG_HALTED 0x0004 // boot process for this CN is halted #define EPL_NMTMNU_NODE_FLAG_NMT_CMD_ISSUED 0x0008 // NMT command was just issued, wrong NMT states will be tolerated #define EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ 0x0300 // counter for StatusRequest timer handle #define EPL_NMTMNU_NODE_FLAG_COUNT_LONGER 0x0C00 // counter for longer timeouts timer handle #define EPL_NMTMNU_NODE_FLAG_INC_STATREQ 0x0100 // increment for StatusRequest timer handle #define EPL_NMTMNU_NODE_FLAG_INC_LONGER 0x0400 // increment for longer timeouts timer handle // These counters will be incremented at every timer start // and copied to timerarg. When the timer event occures // both will be compared and if unequal the timer event // will be discarded, because it is an old one. // defines for timer arguments to draw a distinction between serveral events #define EPL_NMTMNU_TIMERARG_NODE_MASK 0x000000FFL // mask that contains the node-ID #define EPL_NMTMNU_TIMERARG_IDENTREQ 0x00010000L // timer event is for IdentRequest #define EPL_NMTMNU_TIMERARG_STATREQ 0x00020000L // timer event is for StatusRequest #define EPL_NMTMNU_TIMERARG_LONGER 0x00040000L // timer event is for longer timeouts #define EPL_NMTMNU_TIMERARG_STATE_MON 0x00080000L // timer event for StatusRequest to monitor execution of NMT state changes #define EPL_NMTMNU_TIMERARG_COUNT_SR 0x00000300L // counter for StatusRequest #define EPL_NMTMNU_TIMERARG_COUNT_LO 0x00000C00L // counter for longer timeouts // The counters must have the same position as in the node flags above. #define EPL_NMTMNU_SET_FLAGS_TIMERARG_STATREQ(pNodeInfo_p, uiNodeId_p, TimerArg_p) \ pNodeInfo_p->m_wFlags = \ ((pNodeInfo_p->m_wFlags + EPL_NMTMNU_NODE_FLAG_INC_STATREQ) \ & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) \ | (pNodeInfo_p->m_wFlags & ~EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ); \ TimerArg_p.m_ulArg = EPL_NMTMNU_TIMERARG_STATREQ | uiNodeId_p | \ (pNodeInfo_p->m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ); \ TimerArg_p.m_EventSink = kEplEventSinkNmtMnu; #define EPL_NMTMNU_SET_FLAGS_TIMERARG_IDENTREQ(pNodeInfo_p, uiNodeId_p, TimerArg_p) \ pNodeInfo_p->m_wFlags = \ ((pNodeInfo_p->m_wFlags + EPL_NMTMNU_NODE_FLAG_INC_STATREQ) \ & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) \ | (pNodeInfo_p->m_wFlags & ~EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ); \ TimerArg_p.m_ulArg = EPL_NMTMNU_TIMERARG_IDENTREQ | uiNodeId_p | \ (pNodeInfo_p->m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ); \ TimerArg_p.m_EventSink = kEplEventSinkNmtMnu; #define EPL_NMTMNU_SET_FLAGS_TIMERARG_LONGER(pNodeInfo_p, uiNodeId_p, TimerArg_p) \ pNodeInfo_p->m_wFlags = \ ((pNodeInfo_p->m_wFlags + EPL_NMTMNU_NODE_FLAG_INC_LONGER) \ & EPL_NMTMNU_NODE_FLAG_COUNT_LONGER) \ | (pNodeInfo_p->m_wFlags & ~EPL_NMTMNU_NODE_FLAG_COUNT_LONGER); \ TimerArg_p.m_ulArg = EPL_NMTMNU_TIMERARG_LONGER | uiNodeId_p | \ (pNodeInfo_p->m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_LONGER); \ TimerArg_p.m_EventSink = kEplEventSinkNmtMnu; #define EPL_NMTMNU_SET_FLAGS_TIMERARG_STATE_MON(pNodeInfo_p, uiNodeId_p, TimerArg_p) \ pNodeInfo_p->m_wFlags = \ ((pNodeInfo_p->m_wFlags + EPL_NMTMNU_NODE_FLAG_INC_STATREQ) \ & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) \ | (pNodeInfo_p->m_wFlags & ~EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ); \ TimerArg_p.m_ulArg = EPL_NMTMNU_TIMERARG_STATE_MON | uiNodeId_p | \ (pNodeInfo_p->m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ); \ TimerArg_p.m_EventSink = kEplEventSinkNmtMnu; // defines for global flags #define EPL_NMTMNU_FLAG_HALTED 0x0001 // boot process is halted #define EPL_NMTMNU_FLAG_APP_INFORMED 0x0002 // application was informed about possible NMT state change // return pointer to node info structure for specified node ID // d.k. may be replaced by special (hash) function if node ID array is smaller than 254 #define EPL_NMTMNU_GET_NODEINFO(uiNodeId_p) (&EplNmtMnuInstance_g.m_aNodeInfo[uiNodeId_p - 1]) //--------------------------------------------------------------------------- // local types //--------------------------------------------------------------------------- typedef enum { kEplNmtMnuIntNodeEventNoIdentResponse = 0x00, kEplNmtMnuIntNodeEventIdentResponse = 0x01, kEplNmtMnuIntNodeEventBoot = 0x02, kEplNmtMnuIntNodeEventExecReset = 0x03, kEplNmtMnuIntNodeEventConfigured = 0x04, kEplNmtMnuIntNodeEventNoStatusResponse = 0x05, kEplNmtMnuIntNodeEventStatusResponse = 0x06, kEplNmtMnuIntNodeEventHeartbeat = 0x07, kEplNmtMnuIntNodeEventNmtCmdSent = 0x08, kEplNmtMnuIntNodeEventTimerIdentReq = 0x09, kEplNmtMnuIntNodeEventTimerStatReq = 0x0A, kEplNmtMnuIntNodeEventTimerStateMon = 0x0B, kEplNmtMnuIntNodeEventTimerLonger = 0x0C, kEplNmtMnuIntNodeEventError = 0x0D, } tEplNmtMnuIntNodeEvent; typedef enum { kEplNmtMnuNodeStateUnknown = 0x00, kEplNmtMnuNodeStateIdentified = 0x01, kEplNmtMnuNodeStateResetConf = 0x02, // CN reset after configuration update kEplNmtMnuNodeStateConfigured = 0x03, // BootStep1 completed kEplNmtMnuNodeStateReadyToOp = 0x04, // BootStep2 completed kEplNmtMnuNodeStateComChecked = 0x05, // Communication checked successfully kEplNmtMnuNodeStateOperational = 0x06, // CN is in NMT state OPERATIONAL } tEplNmtMnuNodeState; typedef struct { tEplTimerHdl m_TimerHdlStatReq; // timer to delay StatusRequests and IdentRequests tEplTimerHdl m_TimerHdlLonger; // 2nd timer for NMT command EnableReadyToOp and CheckCommunication tEplNmtMnuNodeState m_NodeState; // internal node state (kind of sub state of NMT state) DWORD m_dwNodeCfg; // subindex from 0x1F81 WORD m_wFlags; // flags: CN is being accessed isochronously } tEplNmtMnuNodeInfo; typedef struct { tEplNmtMnuNodeInfo m_aNodeInfo[EPL_NMT_MAX_NODE_ID]; tEplTimerHdl m_TimerHdlNmtState; // timeout for stay in NMT state unsigned int m_uiMandatorySlaveCount; unsigned int m_uiSignalSlaveCount; unsigned long m_ulStatusRequestDelay; // in [ms] (object 0x1006 * EPL_C_NMT_STATREQ_CYCLE) unsigned long m_ulTimeoutReadyToOp; // in [ms] (object 0x1F89/5) unsigned long m_ulTimeoutCheckCom; // in [ms] (object 0x1006 * MultiplexedCycleCount) WORD m_wFlags; // global flags DWORD m_dwNmtStartup; // object 0x1F80 NMT_StartUp_U32 tEplNmtMnuCbNodeEvent m_pfnCbNodeEvent; tEplNmtMnuCbBootEvent m_pfnCbBootEvent; } tEplNmtMnuInstance; //--------------------------------------------------------------------------- // local vars //--------------------------------------------------------------------------- static tEplNmtMnuInstance EplNmtMnuInstance_g; //--------------------------------------------------------------------------- // local function prototypes //--------------------------------------------------------------------------- static tEplKernel PUBLIC EplNmtMnuCbNmtRequest(tEplFrameInfo * pFrameInfo_p); static tEplKernel PUBLIC EplNmtMnuCbIdentResponse(unsigned int uiNodeId_p, tEplIdentResponse * pIdentResponse_p); static tEplKernel PUBLIC EplNmtMnuCbStatusResponse(unsigned int uiNodeId_p, tEplStatusResponse * pStatusResponse_p); static tEplKernel EplNmtMnuCheckNmtState(unsigned int uiNodeId_p, tEplNmtMnuNodeInfo * pNodeInfo_p, tEplNmtState NodeNmtState_p, WORD wErrorCode_p, tEplNmtState LocalNmtState_p); static tEplKernel EplNmtMnuStartBootStep1(void); static tEplKernel EplNmtMnuStartBootStep2(void); static tEplKernel EplNmtMnuStartCheckCom(void); static tEplKernel EplNmtMnuNodeBootStep2(unsigned int uiNodeId_p, tEplNmtMnuNodeInfo * pNodeInfo_p); static tEplKernel EplNmtMnuNodeCheckCom(unsigned int uiNodeId_p, tEplNmtMnuNodeInfo * pNodeInfo_p); static tEplKernel EplNmtMnuStartNodes(void); static tEplKernel EplNmtMnuProcessInternalEvent(unsigned int uiNodeId_p, tEplNmtState NodeNmtState_p, WORD wErrorCode_p, tEplNmtMnuIntNodeEvent NodeEvent_p); static tEplKernel EplNmtMnuReset(void); //=========================================================================// // // // P U B L I C F U N C T I O N S // // // //=========================================================================// //--------------------------------------------------------------------------- // // Function: EplNmtMnuInit // // Description: init first instance of the module // // // // Parameters: // // // Returns: tEplKernel = errorcode // // // State: // //--------------------------------------------------------------------------- tEplKernel EplNmtMnuInit(tEplNmtMnuCbNodeEvent pfnCbNodeEvent_p, tEplNmtMnuCbBootEvent pfnCbBootEvent_p) { tEplKernel Ret; Ret = EplNmtMnuAddInstance(pfnCbNodeEvent_p, pfnCbBootEvent_p); return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuAddInstance // // Description: init other instances of the module // // // // Parameters: // // // Returns: tEplKernel = errorcode // // // State: // //--------------------------------------------------------------------------- tEplKernel EplNmtMnuAddInstance(tEplNmtMnuCbNodeEvent pfnCbNodeEvent_p, tEplNmtMnuCbBootEvent pfnCbBootEvent_p) { tEplKernel Ret; Ret = kEplSuccessful; // reset instance structure EPL_MEMSET(&EplNmtMnuInstance_g, 0, sizeof(EplNmtMnuInstance_g)); if ((pfnCbNodeEvent_p == NULL) || (pfnCbBootEvent_p == NULL)) { Ret = kEplNmtInvalidParam; goto Exit; } EplNmtMnuInstance_g.m_pfnCbNodeEvent = pfnCbNodeEvent_p; EplNmtMnuInstance_g.m_pfnCbBootEvent = pfnCbBootEvent_p; // initialize StatusRequest delay EplNmtMnuInstance_g.m_ulStatusRequestDelay = 5000L; // register NmtMnResponse callback function Ret = EplDlluCalRegAsndService(kEplDllAsndNmtRequest, EplNmtMnuCbNmtRequest, kEplDllAsndFilterLocal); Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuDelInstance // // Description: delete instance // // // // Parameters: // // // Returns: tEplKernel = errorcode // // // State: // //--------------------------------------------------------------------------- tEplKernel EplNmtMnuDelInstance() { tEplKernel Ret; Ret = kEplSuccessful; // deregister NmtMnResponse callback function Ret = EplDlluCalRegAsndService(kEplDllAsndNmtRequest, NULL, kEplDllAsndFilterNone); Ret = EplNmtMnuReset(); return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuSendNmtCommandEx // // Description: sends the specified NMT command to the specified node. // // Parameters: uiNodeId_p = node ID to which the NMT command will be sent // NmtCommand_p = NMT command // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- tEplKernel EplNmtMnuSendNmtCommandEx(unsigned int uiNodeId_p, tEplNmtCommand NmtCommand_p, void *pNmtCommandData_p, unsigned int uiDataSize_p) { tEplKernel Ret = kEplSuccessful; tEplFrameInfo FrameInfo; BYTE abBuffer[EPL_C_DLL_MINSIZE_NMTCMDEXT]; tEplFrame *pFrame = (tEplFrame *) abBuffer; BOOL fSoftDeleteNode = FALSE; if ((uiNodeId_p == 0) || (uiNodeId_p > EPL_C_ADR_BROADCAST)) { // invalid node ID specified Ret = kEplInvalidNodeId; goto Exit; } if ((pNmtCommandData_p != NULL) && (uiDataSize_p > (EPL_C_DLL_MINSIZE_NMTCMDEXT - EPL_C_DLL_MINSIZE_NMTCMD))) { Ret = kEplNmtInvalidParam; goto Exit; } // $$$ d.k. may be check in future versions if the caller wants to perform prohibited state transitions // the CN should not perform these transitions, but the expected NMT state will be changed and never fullfilled. // build frame EPL_MEMSET(pFrame, 0x00, sizeof(abBuffer)); AmiSetByteToLe(&pFrame->m_le_bDstNodeId, (BYTE) uiNodeId_p); AmiSetByteToLe(&pFrame->m_Data.m_Asnd.m_le_bServiceId, (BYTE) kEplDllAsndNmtCommand); AmiSetByteToLe(&pFrame->m_Data.m_Asnd.m_Payload.m_NmtCommandService. m_le_bNmtCommandId, (BYTE) NmtCommand_p); if ((pNmtCommandData_p != NULL) && (uiDataSize_p > 0)) { // copy command data to frame EPL_MEMCPY(&pFrame->m_Data.m_Asnd.m_Payload.m_NmtCommandService. m_le_abNmtCommandData[0], pNmtCommandData_p, uiDataSize_p); } // build info structure FrameInfo.m_NetTime.m_dwNanoSec = 0; FrameInfo.m_NetTime.m_dwSec = 0; FrameInfo.m_pFrame = pFrame; FrameInfo.m_uiFrameSize = sizeof(abBuffer); // send NMT-Request #if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_DLLU)) != 0) Ret = EplDlluCalAsyncSend(&FrameInfo, // pointer to frameinfo kEplDllAsyncReqPrioNmt); // priority #endif if (Ret != kEplSuccessful) { goto Exit; } EPL_DBGLVL_NMTMN_TRACE2("NMTCmd(%02X->%02X)\n", NmtCommand_p, uiNodeId_p); switch (NmtCommand_p) { case kEplNmtCmdStartNode: case kEplNmtCmdEnterPreOperational2: case kEplNmtCmdEnableReadyToOperate: { // nothing left to do, // because any further processing is done // when the NMT command is actually sent goto Exit; } case kEplNmtCmdStopNode: { fSoftDeleteNode = TRUE; break; } case kEplNmtCmdResetNode: case kEplNmtCmdResetCommunication: case kEplNmtCmdResetConfiguration: case kEplNmtCmdSwReset: { break; } default: goto Exit; } // remove CN from isochronous phase; // This must be done here and not when NMT command is actually sent // because it will be too late and may cause unwanted errors if (uiNodeId_p != EPL_C_ADR_BROADCAST) { if (fSoftDeleteNode == FALSE) { // remove CN immediately from isochronous phase Ret = EplDlluCalDeleteNode(uiNodeId_p); } else { // remove CN from isochronous phase softly Ret = EplDlluCalSoftDeleteNode(uiNodeId_p); } } else { // do it for all active CNs for (uiNodeId_p = 1; uiNodeId_p <= tabentries(EplNmtMnuInstance_g.m_aNodeInfo); uiNodeId_p++) { if ((EPL_NMTMNU_GET_NODEINFO(uiNodeId_p)-> m_dwNodeCfg & (EPL_NODEASSIGN_NODE_IS_CN | EPL_NODEASSIGN_NODE_EXISTS)) != 0) { if (fSoftDeleteNode == FALSE) { // remove CN immediately from isochronous phase Ret = EplDlluCalDeleteNode(uiNodeId_p); } else { // remove CN from isochronous phase softly Ret = EplDlluCalSoftDeleteNode (uiNodeId_p); } } } } Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuSendNmtCommand // // Description: sends the specified NMT command to the specified node. // // Parameters: uiNodeId_p = node ID to which the NMT command will be sent // NmtCommand_p = NMT command // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- tEplKernel EplNmtMnuSendNmtCommand(unsigned int uiNodeId_p, tEplNmtCommand NmtCommand_p) { tEplKernel Ret = kEplSuccessful; Ret = EplNmtMnuSendNmtCommandEx(uiNodeId_p, NmtCommand_p, NULL, 0); //Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuTriggerStateChange // // Description: triggers the specified node command for the specified node. // // Parameters: uiNodeId_p = node ID for which the node command will be executed // NodeCommand_p = node command // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- tEplKernel EplNmtMnuTriggerStateChange(unsigned int uiNodeId_p, tEplNmtNodeCommand NodeCommand_p) { tEplKernel Ret = kEplSuccessful; tEplNmtMnuIntNodeEvent NodeEvent; tEplObdSize ObdSize; BYTE bNmtState; WORD wErrorCode = EPL_E_NO_ERROR; if ((uiNodeId_p == 0) || (uiNodeId_p >= EPL_C_ADR_BROADCAST)) { Ret = kEplInvalidNodeId; goto Exit; } switch (NodeCommand_p) { case kEplNmtNodeCommandBoot: { NodeEvent = kEplNmtMnuIntNodeEventBoot; break; } case kEplNmtNodeCommandConfOk: { NodeEvent = kEplNmtMnuIntNodeEventConfigured; break; } case kEplNmtNodeCommandConfErr: { NodeEvent = kEplNmtMnuIntNodeEventError; wErrorCode = EPL_E_NMT_BPO1_CF_VERIFY; break; } case kEplNmtNodeCommandConfReset: { NodeEvent = kEplNmtMnuIntNodeEventExecReset; break; } default: { // invalid node command goto Exit; } } // fetch current NMT state ObdSize = 1; Ret = EplObduReadEntry(0x1F8E, uiNodeId_p, &bNmtState, &ObdSize); if (Ret != kEplSuccessful) { goto Exit; } Ret = EplNmtMnuProcessInternalEvent(uiNodeId_p, (tEplNmtState) (bNmtState | EPL_NMT_TYPE_CS), wErrorCode, NodeEvent); Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuCbNmtStateChange // // Description: callback function for NMT state changes // // Parameters: NmtStateChange_p = NMT state change event // // Returns: tEplKernel = error code // // // State: // //--------------------------------------------------------------------------- tEplKernel PUBLIC EplNmtMnuCbNmtStateChange(tEplEventNmtStateChange NmtStateChange_p) { tEplKernel Ret = kEplSuccessful; // do work which must be done in that state switch (NmtStateChange_p.m_NewNmtState) { // EPL stack is not running /* case kEplNmtGsOff: break; // first init of the hardware case kEplNmtGsInitialising: break; // init of the manufacturer-specific profile area and the // standardised device profile area case kEplNmtGsResetApplication: { break; } // init of the communication profile area case kEplNmtGsResetCommunication: { break; } */ // build the configuration with infos from OD case kEplNmtGsResetConfiguration: { DWORD dwTimeout; tEplObdSize ObdSize; // read object 0x1F80 NMT_StartUp_U32 ObdSize = 4; Ret = EplObduReadEntry(0x1F80, 0, &EplNmtMnuInstance_g. m_dwNmtStartup, &ObdSize); if (Ret != kEplSuccessful) { break; } // compute StatusReqDelay = object 0x1006 * EPL_C_NMT_STATREQ_CYCLE ObdSize = sizeof(dwTimeout); Ret = EplObduReadEntry(0x1006, 0, &dwTimeout, &ObdSize); if (Ret != kEplSuccessful) { break; } if (dwTimeout != 0L) { EplNmtMnuInstance_g.m_ulStatusRequestDelay = dwTimeout * EPL_C_NMT_STATREQ_CYCLE / 1000L; if (EplNmtMnuInstance_g. m_ulStatusRequestDelay == 0L) { EplNmtMnuInstance_g.m_ulStatusRequestDelay = 1L; // at least 1 ms } // $$$ fetch and use MultiplexedCycleCount from OD EplNmtMnuInstance_g.m_ulTimeoutCheckCom = dwTimeout * EPL_C_NMT_STATREQ_CYCLE / 1000L; if (EplNmtMnuInstance_g.m_ulTimeoutCheckCom == 0L) { EplNmtMnuInstance_g.m_ulTimeoutCheckCom = 1L; // at least 1 ms } } // fetch ReadyToOp Timeout from OD ObdSize = sizeof(dwTimeout); Ret = EplObduReadEntry(0x1F89, 5, &dwTimeout, &ObdSize); if (Ret != kEplSuccessful) { break; } if (dwTimeout != 0L) { // convert [us] to [ms] dwTimeout /= 1000L; if (dwTimeout == 0L) { dwTimeout = 1L; // at least 1 ms } EplNmtMnuInstance_g.m_ulTimeoutReadyToOp = dwTimeout; } else { EplNmtMnuInstance_g.m_ulTimeoutReadyToOp = 0L; } break; } /* //----------------------------------------------------------- // CN part of the state machine // node liste for EPL-Frames and check timeout case kEplNmtCsNotActive: { break; } // node process only async frames case kEplNmtCsPreOperational1: { break; } // node process isochronus and asynchronus frames case kEplNmtCsPreOperational2: { break; } // node should be configured und application is ready case kEplNmtCsReadyToOperate: { break; } // normal work state case kEplNmtCsOperational: { break; } // node stopped by MN // -> only process asynchronus frames case kEplNmtCsStopped: { break; } // no EPL cycle // -> normal ethernet communication case kEplNmtCsBasicEthernet: { break; } */ //----------------------------------------------------------- // MN part of the state machine // node listens for EPL-Frames and check timeout case kEplNmtMsNotActive: { break; } // node processes only async frames case kEplNmtMsPreOperational1: { DWORD dwTimeout; tEplTimerArg TimerArg; tEplObdSize ObdSize; tEplEvent Event; // clear global flags, e.g. reenable boot process EplNmtMnuInstance_g.m_wFlags = 0; // reset IdentResponses and running IdentRequests and StatusRequests Ret = EplIdentuReset(); Ret = EplStatusuReset(); // reset timers Ret = EplNmtMnuReset(); // 2008/11/18 d.k. reset internal node info is not necessary, // because timer flags are important and other // things are reset by EplNmtMnuStartBootStep1(). /* EPL_MEMSET(EplNmtMnuInstance_g.m_aNodeInfo, 0, sizeof (EplNmtMnuInstance_g.m_aNodeInfo)); */ // inform DLL about NMT state change, // so that it can clear the asynchonous queues and start the reduced cycle Event.m_EventSink = kEplEventSinkDllk; Event.m_EventType = kEplEventTypeDllkStartReducedCycle; EPL_MEMSET(&Event.m_NetTime, 0x00, sizeof(Event.m_NetTime)); Event.m_pArg = NULL; Event.m_uiSize = 0; Ret = EplEventuPost(&Event); if (Ret != kEplSuccessful) { break; } // reset all nodes // d.k.: skip this step if was just done before, e.g. because of a ResetNode command from a diagnostic node if (NmtStateChange_p.m_NmtEvent == kEplNmtEventTimerMsPreOp1) { BENCHMARK_MOD_07_TOGGLE(9); EPL_NMTMNU_DBG_POST_TRACE_VALUE(0, EPL_C_ADR_BROADCAST, kEplNmtCmdResetNode); Ret = EplNmtMnuSendNmtCommand(EPL_C_ADR_BROADCAST, kEplNmtCmdResetNode); if (Ret != kEplSuccessful) { break; } } // start network scan Ret = EplNmtMnuStartBootStep1(); // start timer for 0x1F89/2 MNTimeoutPreOp1_U32 ObdSize = sizeof(dwTimeout); Ret = EplObduReadEntry(0x1F89, 2, &dwTimeout, &ObdSize); if (Ret != kEplSuccessful) { break; } if (dwTimeout != 0L) { dwTimeout /= 1000L; if (dwTimeout == 0L) { dwTimeout = 1L; // at least 1 ms } TimerArg.m_EventSink = kEplEventSinkNmtMnu; TimerArg.m_ulArg = 0; Ret = EplTimeruModifyTimerMs(&EplNmtMnuInstance_g. m_TimerHdlNmtState, dwTimeout, TimerArg); } break; } // node processes isochronous and asynchronous frames case kEplNmtMsPreOperational2: { // add identified CNs to isochronous phase // send EnableReadyToOp to all identified CNs Ret = EplNmtMnuStartBootStep2(); // wait for NMT state change of CNs break; } // node should be configured und application is ready case kEplNmtMsReadyToOperate: { // check if PRes of CNs are OK // d.k. that means wait CycleLength * MultiplexCycleCount (i.e. start timer) // because Dllk checks PRes of CNs automatically in ReadyToOp Ret = EplNmtMnuStartCheckCom(); break; } // normal work state case kEplNmtMsOperational: { // send StartNode to CNs // wait for NMT state change of CNs Ret = EplNmtMnuStartNodes(); break; } // no EPL cycle // -> normal ethernet communication case kEplNmtMsBasicEthernet: { break; } default: { // TRACE0("EplNmtMnuCbNmtStateChange(): unhandled NMT state\n"); } } return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuCbCheckEvent // // Description: callback funktion for NMT events before they are actually executed. // The EPL API layer must forward NMT events from NmtCnu module. // This module will reject some NMT commands while MN. // // Parameters: NmtEvent_p = outstanding NMT event for approval // // Returns: tEplKernel = error code // kEplReject = reject the NMT event // // State: // //--------------------------------------------------------------------------- tEplKernel PUBLIC EplNmtMnuCbCheckEvent(tEplNmtEvent NmtEvent_p) { tEplKernel Ret = kEplSuccessful; return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtuProcessEvent // // Description: processes events from event queue // // Parameters: pEvent_p = pointer to event // // Returns: tEplKernel = errorcode // // State: // //--------------------------------------------------------------------------- EPLDLLEXPORT tEplKernel PUBLIC EplNmtMnuProcessEvent(tEplEvent * pEvent_p) { tEplKernel Ret; Ret = kEplSuccessful; // process event switch (pEvent_p->m_EventType) { // timer event case kEplEventTypeTimer: { tEplTimerEventArg *pTimerEventArg = (tEplTimerEventArg *) pEvent_p->m_pArg; unsigned int uiNodeId; uiNodeId = (unsigned int)(pTimerEventArg-> m_ulArg & EPL_NMTMNU_TIMERARG_NODE_MASK); if (uiNodeId != 0) { tEplObdSize ObdSize; BYTE bNmtState; tEplNmtMnuNodeInfo *pNodeInfo; pNodeInfo = EPL_NMTMNU_GET_NODEINFO(uiNodeId); ObdSize = 1; Ret = EplObduReadEntry(0x1F8E, uiNodeId, &bNmtState, &ObdSize); if (Ret != kEplSuccessful) { break; } if ((pTimerEventArg-> m_ulArg & EPL_NMTMNU_TIMERARG_IDENTREQ) != 0L) { if ((pNodeInfo-> m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) != (pTimerEventArg->m_ulArg & EPL_NMTMNU_TIMERARG_COUNT_SR)) { // this is an old (already deleted or modified) timer // but not the current timer // so discard it EPL_NMTMNU_DBG_POST_TRACE_VALUE (kEplNmtMnuIntNodeEventTimerIdentReq, uiNodeId, ((pNodeInfo-> m_NodeState << 8) | 0xFF)); break; } /* EPL_NMTMNU_DBG_POST_TRACE_VALUE(kEplNmtMnuIntNodeEventTimerIdentReq, uiNodeId, ((pNodeInfo->m_NodeState << 8) | 0x80 | ((pNodeInfo->m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) >> 6) | ((pTimerEventArg->m_ulArg & EPL_NMTMNU_TIMERARG_COUNT_SR) >> 8))); */ Ret = EplNmtMnuProcessInternalEvent (uiNodeId, (tEplNmtState) (bNmtState | EPL_NMT_TYPE_CS), EPL_E_NO_ERROR, kEplNmtMnuIntNodeEventTimerIdentReq); } else if ((pTimerEventArg-> m_ulArg & EPL_NMTMNU_TIMERARG_STATREQ) != 0L) { if ((pNodeInfo-> m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) != (pTimerEventArg->m_ulArg & EPL_NMTMNU_TIMERARG_COUNT_SR)) { // this is an old (already deleted or modified) timer // but not the current timer // so discard it EPL_NMTMNU_DBG_POST_TRACE_VALUE (kEplNmtMnuIntNodeEventTimerStatReq, uiNodeId, ((pNodeInfo-> m_NodeState << 8) | 0xFF)); break; } /* EPL_NMTMNU_DBG_POST_TRACE_VALUE(kEplNmtMnuIntNodeEventTimerStatReq, uiNodeId, ((pNodeInfo->m_NodeState << 8) | 0x80 | ((pNodeInfo->m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) >> 6) | ((pTimerEventArg->m_ulArg & EPL_NMTMNU_TIMERARG_COUNT_SR) >> 8))); */ Ret = EplNmtMnuProcessInternalEvent (uiNodeId, (tEplNmtState) (bNmtState | EPL_NMT_TYPE_CS), EPL_E_NO_ERROR, kEplNmtMnuIntNodeEventTimerStatReq); } else if ((pTimerEventArg-> m_ulArg & EPL_NMTMNU_TIMERARG_STATE_MON) != 0L) { if ((pNodeInfo-> m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) != (pTimerEventArg->m_ulArg & EPL_NMTMNU_TIMERARG_COUNT_SR)) { // this is an old (already deleted or modified) timer // but not the current timer // so discard it EPL_NMTMNU_DBG_POST_TRACE_VALUE (kEplNmtMnuIntNodeEventTimerStateMon, uiNodeId, ((pNodeInfo-> m_NodeState << 8) | 0xFF)); break; } /* EPL_NMTMNU_DBG_POST_TRACE_VALUE(kEplNmtMnuIntNodeEventTimerStatReq, uiNodeId, ((pNodeInfo->m_NodeState << 8) | 0x80 | ((pNodeInfo->m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) >> 6) | ((pTimerEventArg->m_ulArg & EPL_NMTMNU_TIMERARG_COUNT_SR) >> 8))); */ Ret = EplNmtMnuProcessInternalEvent (uiNodeId, (tEplNmtState) (bNmtState | EPL_NMT_TYPE_CS), EPL_E_NO_ERROR, kEplNmtMnuIntNodeEventTimerStateMon); } else if ((pTimerEventArg-> m_ulArg & EPL_NMTMNU_TIMERARG_LONGER) != 0L) { if ((pNodeInfo-> m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_LONGER) != (pTimerEventArg->m_ulArg & EPL_NMTMNU_TIMERARG_COUNT_LO)) { // this is an old (already deleted or modified) timer // but not the current timer // so discard it EPL_NMTMNU_DBG_POST_TRACE_VALUE (kEplNmtMnuIntNodeEventTimerLonger, uiNodeId, ((pNodeInfo-> m_NodeState << 8) | 0xFF)); break; } /* EPL_NMTMNU_DBG_POST_TRACE_VALUE(kEplNmtMnuIntNodeEventTimerLonger, uiNodeId, ((pNodeInfo->m_NodeState << 8) | 0x80 | ((pNodeInfo->m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_LONGER) >> 6) | ((pTimerEventArg->m_ulArg & EPL_NMTMNU_TIMERARG_COUNT_LO) >> 8))); */ Ret = EplNmtMnuProcessInternalEvent (uiNodeId, (tEplNmtState) (bNmtState | EPL_NMT_TYPE_CS), EPL_E_NO_ERROR, kEplNmtMnuIntNodeEventTimerLonger); } } else { // global timer event } break; } case kEplEventTypeHeartbeat: { tEplHeartbeatEvent *pHeartbeatEvent = (tEplHeartbeatEvent *) pEvent_p->m_pArg; Ret = EplNmtMnuProcessInternalEvent(pHeartbeatEvent-> m_uiNodeId, pHeartbeatEvent-> m_NmtState, pHeartbeatEvent-> m_wErrorCode, kEplNmtMnuIntNodeEventHeartbeat); break; } case kEplEventTypeNmtMnuNmtCmdSent: { tEplFrame *pFrame = (tEplFrame *) pEvent_p->m_pArg; unsigned int uiNodeId; tEplNmtCommand NmtCommand; BYTE bNmtState; uiNodeId = AmiGetByteFromLe(&pFrame->m_le_bDstNodeId); NmtCommand = (tEplNmtCommand) AmiGetByteFromLe(&pFrame->m_Data. m_Asnd.m_Payload. m_NmtCommandService. m_le_bNmtCommandId); switch (NmtCommand) { case kEplNmtCmdStartNode: bNmtState = (BYTE) (kEplNmtCsOperational & 0xFF); break; case kEplNmtCmdStopNode: bNmtState = (BYTE) (kEplNmtCsStopped & 0xFF); break; case kEplNmtCmdEnterPreOperational2: bNmtState = (BYTE) (kEplNmtCsPreOperational2 & 0xFF); break; case kEplNmtCmdEnableReadyToOperate: // d.k. do not change expected node state, because of DS 1.0.0 7.3.1.2.1 Plain NMT State Command // and because node may not change NMT state within EPL_C_NMT_STATE_TOLERANCE bNmtState = (BYTE) (kEplNmtCsPreOperational2 & 0xFF); break; case kEplNmtCmdResetNode: case kEplNmtCmdResetCommunication: case kEplNmtCmdResetConfiguration: case kEplNmtCmdSwReset: bNmtState = (BYTE) (kEplNmtCsNotActive & 0xFF); // EplNmtMnuProcessInternalEvent() sets internal node state to kEplNmtMnuNodeStateUnknown // after next unresponded IdentRequest/StatusRequest break; default: goto Exit; } // process as internal event which update expected NMT state in OD if (uiNodeId != EPL_C_ADR_BROADCAST) { Ret = EplNmtMnuProcessInternalEvent(uiNodeId, (tEplNmtState) (bNmtState | EPL_NMT_TYPE_CS), 0, kEplNmtMnuIntNodeEventNmtCmdSent); } else { // process internal event for all active nodes (except myself) for (uiNodeId = 1; uiNodeId <= tabentries(EplNmtMnuInstance_g. m_aNodeInfo); uiNodeId++) { if ((EPL_NMTMNU_GET_NODEINFO(uiNodeId)-> m_dwNodeCfg & (EPL_NODEASSIGN_NODE_IS_CN | EPL_NODEASSIGN_NODE_EXISTS)) != 0) { Ret = EplNmtMnuProcessInternalEvent (uiNodeId, (tEplNmtState) (bNmtState | EPL_NMT_TYPE_CS), 0, kEplNmtMnuIntNodeEventNmtCmdSent); if (Ret != kEplSuccessful) { goto Exit; } } } } break; } default: { Ret = kEplNmtInvalidEvent; } } Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuGetRunningTimerStatReq // // Description: returns a bit field with running StatReq timers // just for debugging purposes // // Parameters: (none) // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- tEplKernel PUBLIC EplNmtMnuGetDiagnosticInfo(unsigned int *puiMandatorySlaveCount_p, unsigned int *puiSignalSlaveCount_p, WORD * pwFlags_p) { tEplKernel Ret = kEplSuccessful; if ((puiMandatorySlaveCount_p == NULL) || (puiSignalSlaveCount_p == NULL) || (pwFlags_p == NULL)) { Ret = kEplNmtInvalidParam; goto Exit; } *puiMandatorySlaveCount_p = EplNmtMnuInstance_g.m_uiMandatorySlaveCount; *puiSignalSlaveCount_p = EplNmtMnuInstance_g.m_uiSignalSlaveCount; *pwFlags_p = EplNmtMnuInstance_g.m_wFlags; Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuGetRunningTimerStatReq // // Description: returns a bit field with running StatReq timers // just for debugging purposes // // Parameters: (none) // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- /* DWORD EplNmtMnuGetRunningTimerStatReq(void) { tEplKernel Ret = kEplSuccessful; unsigned int uiIndex; tEplNmtMnuNodeInfo* pNodeInfo; pNodeInfo = EplNmtMnuInstance_g.m_aNodeInfo; for (uiIndex = 1; uiIndex <= tabentries(EplNmtMnuInstance_g.m_aNodeInfo); uiIndex++, pNodeInfo++) { if (pNodeInfo->m_NodeState == kEplNmtMnuNodeStateConfigured) { // reset flag "scanned once" pNodeInfo->m_wFlags &= ~EPL_NMTMNU_NODE_FLAG_SCANNED; Ret = EplNmtMnuNodeBootStep2(uiIndex, pNodeInfo); if (Ret != kEplSuccessful) { goto Exit; } EplNmtMnuInstance_g.m_uiSignalSlaveCount++; // signal slave counter shall be decremented if StatusRequest was sent once to a CN // mandatory slave counter shall be decremented if mandatory CN is ReadyToOp } } Exit: return Ret; } */ //=========================================================================// // // // P R I V A T E F U N C T I O N S // // // //=========================================================================// //--------------------------------------------------------------------------- // // Function: EplNmtMnuCbNmtRequest // // Description: callback funktion for NmtRequest // // Parameters: pFrameInfo_p = Frame with the NmtRequest // // Returns: tEplKernel = error code // // // State: // //--------------------------------------------------------------------------- static tEplKernel PUBLIC EplNmtMnuCbNmtRequest(tEplFrameInfo * pFrameInfo_p) { tEplKernel Ret = kEplSuccessful; // $$$ perform NMTRequest return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuCbIdentResponse // // Description: callback funktion for IdentResponse // // Parameters: uiNodeId_p = node ID for which IdentReponse was received // pIdentResponse_p = pointer to IdentResponse // is NULL if node did not answer // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- static tEplKernel PUBLIC EplNmtMnuCbIdentResponse(unsigned int uiNodeId_p, tEplIdentResponse * pIdentResponse_p) { tEplKernel Ret = kEplSuccessful; if (pIdentResponse_p == NULL) { // node did not answer Ret = EplNmtMnuProcessInternalEvent(uiNodeId_p, kEplNmtCsNotActive, EPL_E_NMT_NO_IDENT_RES, // was EPL_E_NO_ERROR kEplNmtMnuIntNodeEventNoIdentResponse); } else { // node answered IdentRequest tEplObdSize ObdSize; DWORD dwDevType; WORD wErrorCode = EPL_E_NO_ERROR; tEplNmtState NmtState = (tEplNmtState) (AmiGetByteFromLe (&pIdentResponse_p-> m_le_bNmtStatus) | EPL_NMT_TYPE_CS); // check IdentResponse $$$ move to ProcessIntern, because this function may be called also if CN // check DeviceType (0x1F84) ObdSize = 4; Ret = EplObduReadEntry(0x1F84, uiNodeId_p, &dwDevType, &ObdSize); if (Ret != kEplSuccessful) { goto Exit; } if (dwDevType != 0L) { // actually compare it with DeviceType from IdentResponse if (AmiGetDwordFromLe(&pIdentResponse_p->m_le_dwDeviceType) != dwDevType) { // wrong DeviceType NmtState = kEplNmtCsNotActive; wErrorCode = EPL_E_NMT_BPO1_DEVICE_TYPE; } } Ret = EplNmtMnuProcessInternalEvent(uiNodeId_p, NmtState, wErrorCode, kEplNmtMnuIntNodeEventIdentResponse); } Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuCbStatusResponse // // Description: callback funktion for StatusResponse // // Parameters: uiNodeId_p = node ID for which IdentReponse was received // pIdentResponse_p = pointer to IdentResponse // is NULL if node did not answer // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- static tEplKernel PUBLIC EplNmtMnuCbStatusResponse(unsigned int uiNodeId_p, tEplStatusResponse * pStatusResponse_p) { tEplKernel Ret = kEplSuccessful; if (pStatusResponse_p == NULL) { // node did not answer Ret = EplNmtMnuProcessInternalEvent(uiNodeId_p, kEplNmtCsNotActive, EPL_E_NMT_NO_STATUS_RES, // was EPL_E_NO_ERROR kEplNmtMnuIntNodeEventNoStatusResponse); } else { // node answered StatusRequest Ret = EplNmtMnuProcessInternalEvent(uiNodeId_p, (tEplNmtState) (AmiGetByteFromLe (&pStatusResponse_p-> m_le_bNmtStatus) | EPL_NMT_TYPE_CS), EPL_E_NO_ERROR, kEplNmtMnuIntNodeEventStatusResponse); } return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuStartBootStep1 // // Description: starts BootStep1 // // Parameters: (none) // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- static tEplKernel EplNmtMnuStartBootStep1(void) { tEplKernel Ret = kEplSuccessful; unsigned int uiSubIndex; unsigned int uiLocalNodeId; DWORD dwNodeCfg; tEplObdSize ObdSize; // $$$ d.k.: save current time for 0x1F89/2 MNTimeoutPreOp1_U32 // start network scan EplNmtMnuInstance_g.m_uiMandatorySlaveCount = 0; EplNmtMnuInstance_g.m_uiSignalSlaveCount = 0; // check 0x1F81 uiLocalNodeId = EplObduGetNodeId(); for (uiSubIndex = 1; uiSubIndex <= 254; uiSubIndex++) { ObdSize = 4; Ret = EplObduReadEntry(0x1F81, uiSubIndex, &dwNodeCfg, &ObdSize); if (Ret != kEplSuccessful) { goto Exit; } if (uiSubIndex != uiLocalNodeId) { // reset flags "not scanned" and "isochronous" EPL_NMTMNU_GET_NODEINFO(uiSubIndex)->m_wFlags &= ~(EPL_NMTMNU_NODE_FLAG_ISOCHRON | EPL_NMTMNU_NODE_FLAG_NOT_SCANNED); if (uiSubIndex == EPL_C_ADR_DIAG_DEF_NODE_ID) { // diagnostic node must be scanned by MN in any case dwNodeCfg |= (EPL_NODEASSIGN_NODE_IS_CN | EPL_NODEASSIGN_NODE_EXISTS); // and it must be isochronously accessed dwNodeCfg &= ~EPL_NODEASSIGN_ASYNCONLY_NODE; } // save node config in local node info structure EPL_NMTMNU_GET_NODEINFO(uiSubIndex)->m_dwNodeCfg = dwNodeCfg; EPL_NMTMNU_GET_NODEINFO(uiSubIndex)->m_NodeState = kEplNmtMnuNodeStateUnknown; if ((dwNodeCfg & (EPL_NODEASSIGN_NODE_IS_CN | EPL_NODEASSIGN_NODE_EXISTS)) != 0) { // node is configured as CN // identify the node Ret = EplIdentuRequestIdentResponse(uiSubIndex, EplNmtMnuCbIdentResponse); if (Ret != kEplSuccessful) { goto Exit; } // set flag "not scanned" EPL_NMTMNU_GET_NODEINFO(uiSubIndex)->m_wFlags |= EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; EplNmtMnuInstance_g.m_uiSignalSlaveCount++; // signal slave counter shall be decremented if IdentRequest was sent once to a CN if ((dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0) { // node is a mandatory CN EplNmtMnuInstance_g. m_uiMandatorySlaveCount++; // mandatory slave counter shall be decremented if mandatory CN was configured successfully } } } else { // subindex of MN if ((dwNodeCfg & (EPL_NODEASSIGN_MN_PRES | EPL_NODEASSIGN_NODE_EXISTS)) != 0) { // MN shall send PRes tEplDllNodeInfo DllNodeInfo; EPL_MEMSET(&DllNodeInfo, 0, sizeof(DllNodeInfo)); DllNodeInfo.m_uiNodeId = uiLocalNodeId; Ret = EplDlluCalAddNode(&DllNodeInfo); } } } Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuStartBootStep2 // // Description: starts BootStep2. // That means add nodes to isochronous phase and send // NMT EnableReadyToOp. // // Parameters: (none) // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- static tEplKernel EplNmtMnuStartBootStep2(void) { tEplKernel Ret = kEplSuccessful; unsigned int uiIndex; tEplNmtMnuNodeInfo *pNodeInfo; if ((EplNmtMnuInstance_g.m_wFlags & EPL_NMTMNU_FLAG_HALTED) == 0) { // boot process is not halted // add nodes to isochronous phase and send NMT EnableReadyToOp EplNmtMnuInstance_g.m_uiMandatorySlaveCount = 0; EplNmtMnuInstance_g.m_uiSignalSlaveCount = 0; // reset flag that application was informed about possible state change EplNmtMnuInstance_g.m_wFlags &= ~EPL_NMTMNU_FLAG_APP_INFORMED; pNodeInfo = EplNmtMnuInstance_g.m_aNodeInfo; for (uiIndex = 1; uiIndex <= tabentries(EplNmtMnuInstance_g.m_aNodeInfo); uiIndex++, pNodeInfo++) { if (pNodeInfo->m_NodeState == kEplNmtMnuNodeStateConfigured) { Ret = EplNmtMnuNodeBootStep2(uiIndex, pNodeInfo); if (Ret != kEplSuccessful) { goto Exit; } // set flag "not scanned" pNodeInfo->m_wFlags |= EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; EplNmtMnuInstance_g.m_uiSignalSlaveCount++; // signal slave counter shall be decremented if StatusRequest was sent once to a CN if ((pNodeInfo->m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0) { // node is a mandatory CN EplNmtMnuInstance_g. m_uiMandatorySlaveCount++; } // mandatory slave counter shall be decremented if mandatory CN is ReadyToOp } } } Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuNodeBootStep2 // // Description: starts BootStep2 for the specified node. // This means the CN is added to isochronous phase if not // async-only and it gets the NMT command EnableReadyToOp. // The CN must be in node state Configured, when it enters // BootStep2. When BootStep2 finishes, the CN is in node state // ReadyToOp. // If TimeoutReadyToOp in object 0x1F89/5 is configured, // TimerHdlLonger will be started with this timeout. // // Parameters: uiNodeId_p = node ID // pNodeInfo_p = pointer to internal node info structure // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- static tEplKernel EplNmtMnuNodeBootStep2(unsigned int uiNodeId_p, tEplNmtMnuNodeInfo * pNodeInfo_p) { tEplKernel Ret = kEplSuccessful; tEplDllNodeInfo DllNodeInfo; DWORD dwNodeCfg; tEplObdSize ObdSize; tEplTimerArg TimerArg; dwNodeCfg = pNodeInfo_p->m_dwNodeCfg; if ((dwNodeCfg & EPL_NODEASSIGN_ASYNCONLY_NODE) == 0) { // add node to isochronous phase DllNodeInfo.m_uiNodeId = uiNodeId_p; ObdSize = 4; Ret = EplObduReadEntry(0x1F92, uiNodeId_p, &DllNodeInfo.m_dwPresTimeout, &ObdSize); if (Ret != kEplSuccessful) { goto Exit; } ObdSize = 2; Ret = EplObduReadEntry(0x1F8B, uiNodeId_p, &DllNodeInfo.m_wPreqPayloadLimit, &ObdSize); if (Ret != kEplSuccessful) { goto Exit; } ObdSize = 2; Ret = EplObduReadEntry(0x1F8D, uiNodeId_p, &DllNodeInfo.m_wPresPayloadLimit, &ObdSize); if (Ret != kEplSuccessful) { goto Exit; } pNodeInfo_p->m_wFlags |= EPL_NMTMNU_NODE_FLAG_ISOCHRON; Ret = EplDlluCalAddNode(&DllNodeInfo); if (Ret != kEplSuccessful) { goto Exit; } } EPL_NMTMNU_DBG_POST_TRACE_VALUE(0, uiNodeId_p, kEplNmtCmdEnableReadyToOperate); Ret = EplNmtMnuSendNmtCommand(uiNodeId_p, kEplNmtCmdEnableReadyToOperate); if (Ret != kEplSuccessful) { goto Exit; } if (EplNmtMnuInstance_g.m_ulTimeoutReadyToOp != 0L) { // start timer // when the timer expires the CN must be ReadyToOp EPL_NMTMNU_SET_FLAGS_TIMERARG_LONGER(pNodeInfo_p, uiNodeId_p, TimerArg); // TimerArg.m_EventSink = kEplEventSinkNmtMnu; // TimerArg.m_ulArg = EPL_NMTMNU_TIMERARG_LONGER | uiNodeId_p; Ret = EplTimeruModifyTimerMs(&pNodeInfo_p->m_TimerHdlLonger, EplNmtMnuInstance_g. m_ulTimeoutReadyToOp, TimerArg); } Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuStartCheckCom // // Description: starts CheckCommunication // // Parameters: (none) // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- static tEplKernel EplNmtMnuStartCheckCom(void) { tEplKernel Ret = kEplSuccessful; unsigned int uiIndex; tEplNmtMnuNodeInfo *pNodeInfo; if ((EplNmtMnuInstance_g.m_wFlags & EPL_NMTMNU_FLAG_HALTED) == 0) { // boot process is not halted // wait some time and check that no communication error occurs EplNmtMnuInstance_g.m_uiMandatorySlaveCount = 0; EplNmtMnuInstance_g.m_uiSignalSlaveCount = 0; // reset flag that application was informed about possible state change EplNmtMnuInstance_g.m_wFlags &= ~EPL_NMTMNU_FLAG_APP_INFORMED; pNodeInfo = EplNmtMnuInstance_g.m_aNodeInfo; for (uiIndex = 1; uiIndex <= tabentries(EplNmtMnuInstance_g.m_aNodeInfo); uiIndex++, pNodeInfo++) { if (pNodeInfo->m_NodeState == kEplNmtMnuNodeStateReadyToOp) { Ret = EplNmtMnuNodeCheckCom(uiIndex, pNodeInfo); if (Ret == kEplReject) { // timer was started // wait until it expires if ((pNodeInfo->m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0) { // node is a mandatory CN EplNmtMnuInstance_g. m_uiMandatorySlaveCount++; } } else if (Ret != kEplSuccessful) { goto Exit; } // set flag "not scanned" pNodeInfo->m_wFlags |= EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; EplNmtMnuInstance_g.m_uiSignalSlaveCount++; // signal slave counter shall be decremented if timeout elapsed and regardless of an error // mandatory slave counter shall be decremented if timeout elapsed and no error occured } } } Ret = kEplSuccessful; Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuNodeCheckCom // // Description: checks communication of the specified node. // That means wait some time and if no error occured everything // is OK. // // Parameters: uiNodeId_p = node ID // pNodeInfo_p = pointer to internal node info structure // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- static tEplKernel EplNmtMnuNodeCheckCom(unsigned int uiNodeId_p, tEplNmtMnuNodeInfo * pNodeInfo_p) { tEplKernel Ret = kEplSuccessful; DWORD dwNodeCfg; tEplTimerArg TimerArg; dwNodeCfg = pNodeInfo_p->m_dwNodeCfg; if (((dwNodeCfg & EPL_NODEASSIGN_ASYNCONLY_NODE) == 0) && (EplNmtMnuInstance_g.m_ulTimeoutCheckCom != 0L)) { // CN is not async-only and timeout for CheckCom was set // check communication, // that means wait some time and if no error occured everything is OK; // start timer (when the timer expires the CN must be still ReadyToOp) EPL_NMTMNU_SET_FLAGS_TIMERARG_LONGER(pNodeInfo_p, uiNodeId_p, TimerArg); // TimerArg.m_EventSink = kEplEventSinkNmtMnu; // TimerArg.m_ulArg = EPL_NMTMNU_TIMERARG_LONGER | uiNodeId_p; Ret = EplTimeruModifyTimerMs(&pNodeInfo_p->m_TimerHdlLonger, EplNmtMnuInstance_g. m_ulTimeoutCheckCom, TimerArg); // update mandatory slave counter, because timer was started if (Ret == kEplSuccessful) { Ret = kEplReject; } } else { // timer was not started // assume everything is OK pNodeInfo_p->m_NodeState = kEplNmtMnuNodeStateComChecked; } //Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuStartNodes // // Description: really starts all nodes which are ReadyToOp and CheckCom did not fail // // Parameters: (none) // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- static tEplKernel EplNmtMnuStartNodes(void) { tEplKernel Ret = kEplSuccessful; unsigned int uiIndex; tEplNmtMnuNodeInfo *pNodeInfo; if ((EplNmtMnuInstance_g.m_wFlags & EPL_NMTMNU_FLAG_HALTED) == 0) { // boot process is not halted // send NMT command Start Node EplNmtMnuInstance_g.m_uiMandatorySlaveCount = 0; EplNmtMnuInstance_g.m_uiSignalSlaveCount = 0; // reset flag that application was informed about possible state change EplNmtMnuInstance_g.m_wFlags &= ~EPL_NMTMNU_FLAG_APP_INFORMED; pNodeInfo = EplNmtMnuInstance_g.m_aNodeInfo; for (uiIndex = 1; uiIndex <= tabentries(EplNmtMnuInstance_g.m_aNodeInfo); uiIndex++, pNodeInfo++) { if (pNodeInfo->m_NodeState == kEplNmtMnuNodeStateComChecked) { if ((EplNmtMnuInstance_g. m_dwNmtStartup & EPL_NMTST_STARTALLNODES) == 0) { EPL_NMTMNU_DBG_POST_TRACE_VALUE(0, uiIndex, kEplNmtCmdStartNode); Ret = EplNmtMnuSendNmtCommand(uiIndex, kEplNmtCmdStartNode); if (Ret != kEplSuccessful) { goto Exit; } } if ((pNodeInfo->m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0) { // node is a mandatory CN EplNmtMnuInstance_g. m_uiMandatorySlaveCount++; } // set flag "not scanned" pNodeInfo->m_wFlags |= EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; EplNmtMnuInstance_g.m_uiSignalSlaveCount++; // signal slave counter shall be decremented if StatusRequest was sent once to a CN // mandatory slave counter shall be decremented if mandatory CN is OPERATIONAL } } // $$$ inform application if EPL_NMTST_NO_STARTNODE is set if ((EplNmtMnuInstance_g. m_dwNmtStartup & EPL_NMTST_STARTALLNODES) != 0) { EPL_NMTMNU_DBG_POST_TRACE_VALUE(0, EPL_C_ADR_BROADCAST, kEplNmtCmdStartNode); Ret = EplNmtMnuSendNmtCommand(EPL_C_ADR_BROADCAST, kEplNmtCmdStartNode); if (Ret != kEplSuccessful) { goto Exit; } } } Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuProcessInternalEvent // // Description: processes internal node events // // Parameters: uiNodeId_p = node ID // NodeNmtState_p = NMT state of CN // NodeEvent_p = occured events // // Returns: tEplKernel = error code // // // State: // //--------------------------------------------------------------------------- static tEplKernel EplNmtMnuProcessInternalEvent(unsigned int uiNodeId_p, tEplNmtState NodeNmtState_p, WORD wErrorCode_p, tEplNmtMnuIntNodeEvent NodeEvent_p) { tEplKernel Ret = kEplSuccessful; tEplNmtState NmtState; tEplNmtMnuNodeInfo *pNodeInfo; tEplTimerArg TimerArg; pNodeInfo = EPL_NMTMNU_GET_NODEINFO(uiNodeId_p); NmtState = EplNmtuGetNmtState(); if (NmtState <= kEplNmtMsNotActive) { // MN is not active goto Exit; } switch (NodeEvent_p) { case kEplNmtMnuIntNodeEventIdentResponse: { BYTE bNmtState; EPL_NMTMNU_DBG_POST_TRACE_VALUE(NodeEvent_p, uiNodeId_p, pNodeInfo->m_NodeState); if (pNodeInfo->m_NodeState != kEplNmtMnuNodeStateResetConf) { pNodeInfo->m_NodeState = kEplNmtMnuNodeStateIdentified; } // reset flags ISOCHRON and NMT_CMD_ISSUED pNodeInfo->m_wFlags &= ~(EPL_NMTMNU_NODE_FLAG_ISOCHRON | EPL_NMTMNU_NODE_FLAG_NMT_CMD_ISSUED); if ((NmtState == kEplNmtMsPreOperational1) && ((pNodeInfo-> m_wFlags & EPL_NMTMNU_NODE_FLAG_NOT_SCANNED) != 0)) { // decrement only signal slave count EplNmtMnuInstance_g.m_uiSignalSlaveCount--; pNodeInfo->m_wFlags &= ~EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; } // update object 0x1F8F NMT_MNNodeExpState_AU8 to PreOp1 (even if local state >= PreOp2) bNmtState = (BYTE) (kEplNmtCsPreOperational1 & 0xFF); Ret = EplObduWriteEntry(0x1F8F, uiNodeId_p, &bNmtState, 1); // check NMT state of CN Ret = EplNmtMnuCheckNmtState(uiNodeId_p, pNodeInfo, NodeNmtState_p, wErrorCode_p, NmtState); if (Ret != kEplSuccessful) { if (Ret == kEplReject) { Ret = kEplSuccessful; } break; } // request StatusResponse immediately, // because we want a fast boot-up of CNs Ret = EplStatusuRequestStatusResponse(uiNodeId_p, EplNmtMnuCbStatusResponse); if (Ret != kEplSuccessful) { EPL_NMTMNU_DBG_POST_TRACE_VALUE(NodeEvent_p, uiNodeId_p, Ret); if (Ret == kEplInvalidOperation) { // the only situation when this should happen is, when // StatusResponse was already requested from within // the StatReq timer event. // so ignore this error. Ret = kEplSuccessful; } else { break; } } if (pNodeInfo->m_NodeState != kEplNmtMnuNodeStateResetConf) { // inform application Ret = EplNmtMnuInstance_g. m_pfnCbNodeEvent(uiNodeId_p, kEplNmtNodeEventFound, NodeNmtState_p, EPL_E_NO_ERROR, (pNodeInfo-> m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0); if (Ret == kEplReject) { // interrupt boot process on user request EPL_NMTMNU_DBG_POST_TRACE_VALUE (NodeEvent_p, uiNodeId_p, ((pNodeInfo->m_NodeState << 8) | Ret)); Ret = kEplSuccessful; break; } else if (Ret != kEplSuccessful) { EPL_NMTMNU_DBG_POST_TRACE_VALUE (NodeEvent_p, uiNodeId_p, ((pNodeInfo->m_NodeState << 8) | Ret)); break; } } // continue BootStep1 } case kEplNmtMnuIntNodeEventBoot: { // $$$ check identification (vendor ID, product code, revision no, serial no) if (pNodeInfo->m_NodeState == kEplNmtMnuNodeStateIdentified) { // $$$ check software // check/start configuration // inform application Ret = EplNmtMnuInstance_g. m_pfnCbNodeEvent(uiNodeId_p, kEplNmtNodeEventCheckConf, NodeNmtState_p, EPL_E_NO_ERROR, (pNodeInfo-> m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0); if (Ret == kEplReject) { // interrupt boot process on user request EPL_NMTMNU_DBG_POST_TRACE_VALUE (kEplNmtMnuIntNodeEventBoot, uiNodeId_p, ((pNodeInfo->m_NodeState << 8) | Ret)); Ret = kEplSuccessful; break; } else if (Ret != kEplSuccessful) { EPL_NMTMNU_DBG_POST_TRACE_VALUE (kEplNmtMnuIntNodeEventBoot, uiNodeId_p, ((pNodeInfo->m_NodeState << 8) | Ret)); break; } } else if (pNodeInfo->m_NodeState != kEplNmtMnuNodeStateResetConf) { // wrong CN state // ignore event break; } // $$$ d.k.: currently we assume configuration is OK // continue BootStep1 } case kEplNmtMnuIntNodeEventConfigured: { if ((pNodeInfo->m_NodeState != kEplNmtMnuNodeStateIdentified) && (pNodeInfo->m_NodeState != kEplNmtMnuNodeStateResetConf)) { // wrong CN state // ignore event break; } pNodeInfo->m_NodeState = kEplNmtMnuNodeStateConfigured; if (NmtState == kEplNmtMsPreOperational1) { if ((pNodeInfo->m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0) { // decrement mandatory CN counter EplNmtMnuInstance_g. m_uiMandatorySlaveCount--; } } else { // put optional node to next step (BootStep2) Ret = EplNmtMnuNodeBootStep2(uiNodeId_p, pNodeInfo); } break; } case kEplNmtMnuIntNodeEventNoIdentResponse: { if ((NmtState == kEplNmtMsPreOperational1) && ((pNodeInfo-> m_wFlags & EPL_NMTMNU_NODE_FLAG_NOT_SCANNED) != 0)) { // decrement only signal slave count EplNmtMnuInstance_g.m_uiSignalSlaveCount--; pNodeInfo->m_wFlags &= ~EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; } if (pNodeInfo->m_NodeState != kEplNmtMnuNodeStateResetConf) { pNodeInfo->m_NodeState = kEplNmtMnuNodeStateUnknown; } // $$$ d.k. check start time for 0x1F89/2 MNTimeoutPreOp1_U32 // $$$ d.k. check individual timeout 0x1F89/6 MNIdentificationTimeout_U32 // if mandatory node and timeout elapsed -> halt boot procedure // trigger IdentRequest again (if >= PreOp2, after delay) if (NmtState >= kEplNmtMsPreOperational2) { // start timer EPL_NMTMNU_SET_FLAGS_TIMERARG_IDENTREQ (pNodeInfo, uiNodeId_p, TimerArg); // TimerArg.m_EventSink = kEplEventSinkNmtMnu; // TimerArg.m_ulArg = EPL_NMTMNU_TIMERARG_IDENTREQ | uiNodeId_p; /* EPL_NMTMNU_DBG_POST_TRACE_VALUE(kEplNmtMnuIntNodeEventNoIdentResponse, uiNodeId_p, ((pNodeInfo->m_NodeState << 8) | 0x80 | ((pNodeInfo->m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) >> 6) | ((TimerArg.m_ulArg & EPL_NMTMNU_TIMERARG_COUNT_SR) >> 8))); */ Ret = EplTimeruModifyTimerMs(&pNodeInfo-> m_TimerHdlStatReq, EplNmtMnuInstance_g. m_ulStatusRequestDelay, TimerArg); } else { // trigger IdentRequest immediately Ret = EplIdentuRequestIdentResponse(uiNodeId_p, EplNmtMnuCbIdentResponse); } break; } case kEplNmtMnuIntNodeEventStatusResponse: { if ((NmtState >= kEplNmtMsPreOperational2) && ((pNodeInfo-> m_wFlags & EPL_NMTMNU_NODE_FLAG_NOT_SCANNED) != 0)) { // decrement only signal slave count if checked once for ReadyToOp, CheckCom, Operational EplNmtMnuInstance_g.m_uiSignalSlaveCount--; pNodeInfo->m_wFlags &= ~EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; } // check NMT state of CN Ret = EplNmtMnuCheckNmtState(uiNodeId_p, pNodeInfo, NodeNmtState_p, wErrorCode_p, NmtState); if (Ret != kEplSuccessful) { if (Ret == kEplReject) { Ret = kEplSuccessful; } break; } if (NmtState == kEplNmtMsPreOperational1) { // request next StatusResponse immediately Ret = EplStatusuRequestStatusResponse(uiNodeId_p, EplNmtMnuCbStatusResponse); if (Ret != kEplSuccessful) { EPL_NMTMNU_DBG_POST_TRACE_VALUE (NodeEvent_p, uiNodeId_p, Ret); } } else if ((pNodeInfo->m_wFlags & EPL_NMTMNU_NODE_FLAG_ISOCHRON) == 0) { // start timer // not isochronously accessed CN (e.g. async-only or stopped CN) EPL_NMTMNU_SET_FLAGS_TIMERARG_STATREQ(pNodeInfo, uiNodeId_p, TimerArg); // TimerArg.m_EventSink = kEplEventSinkNmtMnu; // TimerArg.m_ulArg = EPL_NMTMNU_TIMERARG_STATREQ | uiNodeId_p; /* EPL_NMTMNU_DBG_POST_TRACE_VALUE(kEplNmtMnuIntNodeEventStatusResponse, uiNodeId_p, ((pNodeInfo->m_NodeState << 8) | 0x80 | ((pNodeInfo->m_wFlags & EPL_NMTMNU_NODE_FLAG_COUNT_STATREQ) >> 6) | ((TimerArg.m_ulArg & EPL_NMTMNU_TIMERARG_COUNT_SR) >> 8))); */ Ret = EplTimeruModifyTimerMs(&pNodeInfo-> m_TimerHdlStatReq, EplNmtMnuInstance_g. m_ulStatusRequestDelay, TimerArg); } break; } case kEplNmtMnuIntNodeEventNoStatusResponse: { // function CheckNmtState sets node state to unknown if necessary /* if ((NmtState >= kEplNmtMsPreOperational2) && ((pNodeInfo->m_wFlags & EPL_NMTMNU_NODE_FLAG_NOT_SCANNED) != 0)) { // decrement only signal slave count if checked once for ReadyToOp, CheckCom, Operational EplNmtMnuInstance_g.m_uiSignalSlaveCount--; pNodeInfo->m_wFlags &= ~EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; } */ // check NMT state of CN Ret = EplNmtMnuCheckNmtState(uiNodeId_p, pNodeInfo, NodeNmtState_p, wErrorCode_p, NmtState); if (Ret != kEplSuccessful) { if (Ret == kEplReject) { Ret = kEplSuccessful; } break; } break; } case kEplNmtMnuIntNodeEventError: { // currently only issued on kEplNmtNodeCommandConfErr if (pNodeInfo->m_NodeState != kEplNmtMnuNodeStateIdentified) { // wrong CN state // ignore event break; } // check NMT state of CN Ret = EplNmtMnuCheckNmtState(uiNodeId_p, pNodeInfo, kEplNmtCsNotActive, wErrorCode_p, NmtState); if (Ret != kEplSuccessful) { if (Ret == kEplReject) { Ret = kEplSuccessful; } break; } break; } case kEplNmtMnuIntNodeEventExecReset: { if (pNodeInfo->m_NodeState != kEplNmtMnuNodeStateIdentified) { // wrong CN state // ignore event break; } pNodeInfo->m_NodeState = kEplNmtMnuNodeStateResetConf; EPL_NMTMNU_DBG_POST_TRACE_VALUE(NodeEvent_p, uiNodeId_p, (((NodeNmtState_p & 0xFF) << 8) | kEplNmtCmdResetConfiguration)); // send NMT reset configuration to CN for activation of configuration Ret = EplNmtMnuSendNmtCommand(uiNodeId_p, kEplNmtCmdResetConfiguration); break; } case kEplNmtMnuIntNodeEventHeartbeat: { /* if ((NmtState >= kEplNmtMsPreOperational2) && ((pNodeInfo->m_wFlags & EPL_NMTMNU_NODE_FLAG_NOT_SCANNED) != 0)) { // decrement only signal slave count if checked once for ReadyToOp, CheckCom, Operational EplNmtMnuInstance_g.m_uiSignalSlaveCount--; pNodeInfo->m_wFlags &= ~EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; } */ // check NMT state of CN Ret = EplNmtMnuCheckNmtState(uiNodeId_p, pNodeInfo, NodeNmtState_p, wErrorCode_p, NmtState); if (Ret != kEplSuccessful) { if (Ret == kEplReject) { Ret = kEplSuccessful; } break; } break; } case kEplNmtMnuIntNodeEventTimerIdentReq: { EPL_DBGLVL_NMTMN_TRACE1 ("TimerStatReq->IdentReq(%02X)\n", uiNodeId_p); // trigger IdentRequest again Ret = EplIdentuRequestIdentResponse(uiNodeId_p, EplNmtMnuCbIdentResponse); if (Ret != kEplSuccessful) { EPL_NMTMNU_DBG_POST_TRACE_VALUE(NodeEvent_p, uiNodeId_p, (((NodeNmtState_p & 0xFF) << 8) | Ret)); if (Ret == kEplInvalidOperation) { // this can happen because of a bug in EplTimeruLinuxKernel.c // so ignore this error. Ret = kEplSuccessful; } } break; } case kEplNmtMnuIntNodeEventTimerStateMon: { // reset NMT state change flag // because from now on the CN must have the correct NMT state pNodeInfo->m_wFlags &= ~EPL_NMTMNU_NODE_FLAG_NMT_CMD_ISSUED; // continue with normal StatReq processing } case kEplNmtMnuIntNodeEventTimerStatReq: { EPL_DBGLVL_NMTMN_TRACE1("TimerStatReq->StatReq(%02X)\n", uiNodeId_p); // request next StatusResponse Ret = EplStatusuRequestStatusResponse(uiNodeId_p, EplNmtMnuCbStatusResponse); if (Ret != kEplSuccessful) { EPL_NMTMNU_DBG_POST_TRACE_VALUE(NodeEvent_p, uiNodeId_p, (((NodeNmtState_p & 0xFF) << 8) | Ret)); if (Ret == kEplInvalidOperation) { // the only situation when this should happen is, when // StatusResponse was already requested while processing // event IdentResponse. // so ignore this error. Ret = kEplSuccessful; } } break; } case kEplNmtMnuIntNodeEventTimerLonger: { switch (pNodeInfo->m_NodeState) { case kEplNmtMnuNodeStateConfigured: { // node should be ReadyToOp but it is not // check NMT state which shall be intentionally wrong, so that ERROR_TREATMENT will be started Ret = EplNmtMnuCheckNmtState(uiNodeId_p, pNodeInfo, kEplNmtCsNotActive, EPL_E_NMT_BPO2, NmtState); if (Ret != kEplSuccessful) { if (Ret == kEplReject) { Ret = kEplSuccessful; } break; } break; } case kEplNmtMnuNodeStateReadyToOp: { // CheckCom finished successfully pNodeInfo->m_NodeState = kEplNmtMnuNodeStateComChecked; if ((pNodeInfo-> m_wFlags & EPL_NMTMNU_NODE_FLAG_NOT_SCANNED) != 0) { // decrement only signal slave count if checked once for ReadyToOp, CheckCom, Operational EplNmtMnuInstance_g. m_uiSignalSlaveCount--; pNodeInfo->m_wFlags &= ~EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; } if ((pNodeInfo-> m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0) { // decrement mandatory slave counter EplNmtMnuInstance_g. m_uiMandatorySlaveCount--; } if (NmtState != kEplNmtMsReadyToOperate) { EPL_NMTMNU_DBG_POST_TRACE_VALUE (NodeEvent_p, uiNodeId_p, (((NodeNmtState_p & 0xFF) << 8) | kEplNmtCmdStartNode)); // start optional CN Ret = EplNmtMnuSendNmtCommand (uiNodeId_p, kEplNmtCmdStartNode); } break; } default: { break; } } break; } case kEplNmtMnuIntNodeEventNmtCmdSent: { BYTE bNmtState; // update expected NMT state with the one that results // from the sent NMT command bNmtState = (BYTE) (NodeNmtState_p & 0xFF); // write object 0x1F8F NMT_MNNodeExpState_AU8 Ret = EplObduWriteEntry(0x1F8F, uiNodeId_p, &bNmtState, 1); if (Ret != kEplSuccessful) { goto Exit; } if (NodeNmtState_p == kEplNmtCsNotActive) { // restart processing with IdentRequest EPL_NMTMNU_SET_FLAGS_TIMERARG_IDENTREQ (pNodeInfo, uiNodeId_p, TimerArg); } else { // monitor NMT state change with StatusRequest after // the corresponding delay; // until then wrong NMT states will be ignored EPL_NMTMNU_SET_FLAGS_TIMERARG_STATE_MON (pNodeInfo, uiNodeId_p, TimerArg); // set NMT state change flag pNodeInfo->m_wFlags |= EPL_NMTMNU_NODE_FLAG_NMT_CMD_ISSUED; } Ret = EplTimeruModifyTimerMs(&pNodeInfo-> m_TimerHdlStatReq, EplNmtMnuInstance_g. m_ulStatusRequestDelay, TimerArg); // finish processing, because NmtState_p is the expected and not the current state goto Exit; } default: { break; } } // check if network is ready to change local NMT state and this was not done before if ((EplNmtMnuInstance_g.m_wFlags & (EPL_NMTMNU_FLAG_HALTED | EPL_NMTMNU_FLAG_APP_INFORMED)) == 0) { // boot process is not halted switch (NmtState) { case kEplNmtMsPreOperational1: { if ((EplNmtMnuInstance_g.m_uiSignalSlaveCount == 0) && (EplNmtMnuInstance_g.m_uiMandatorySlaveCount == 0)) { // all optional CNs scanned once and all mandatory CNs configured successfully EplNmtMnuInstance_g.m_wFlags |= EPL_NMTMNU_FLAG_APP_INFORMED; // inform application Ret = EplNmtMnuInstance_g. m_pfnCbBootEvent (kEplNmtBootEventBootStep1Finish, NmtState, EPL_E_NO_ERROR); if (Ret != kEplSuccessful) { if (Ret == kEplReject) { // wait for application Ret = kEplSuccessful; } break; } // enter PreOp2 Ret = EplNmtuNmtEvent (kEplNmtEventAllMandatoryCNIdent); } break; } case kEplNmtMsPreOperational2: { if ((EplNmtMnuInstance_g.m_uiSignalSlaveCount == 0) && (EplNmtMnuInstance_g.m_uiMandatorySlaveCount == 0)) { // all optional CNs checked once for ReadyToOp and all mandatory CNs are ReadyToOp EplNmtMnuInstance_g.m_wFlags |= EPL_NMTMNU_FLAG_APP_INFORMED; // inform application Ret = EplNmtMnuInstance_g. m_pfnCbBootEvent (kEplNmtBootEventBootStep2Finish, NmtState, EPL_E_NO_ERROR); if (Ret != kEplSuccessful) { if (Ret == kEplReject) { // wait for application Ret = kEplSuccessful; } break; } // enter ReadyToOp Ret = EplNmtuNmtEvent (kEplNmtEventEnterReadyToOperate); } break; } case kEplNmtMsReadyToOperate: { if ((EplNmtMnuInstance_g.m_uiSignalSlaveCount == 0) && (EplNmtMnuInstance_g.m_uiMandatorySlaveCount == 0)) { // all CNs checked for errorless communication EplNmtMnuInstance_g.m_wFlags |= EPL_NMTMNU_FLAG_APP_INFORMED; // inform application Ret = EplNmtMnuInstance_g. m_pfnCbBootEvent (kEplNmtBootEventCheckComFinish, NmtState, EPL_E_NO_ERROR); if (Ret != kEplSuccessful) { if (Ret == kEplReject) { // wait for application Ret = kEplSuccessful; } break; } // enter Operational Ret = EplNmtuNmtEvent (kEplNmtEventEnterMsOperational); } break; } case kEplNmtMsOperational: { if ((EplNmtMnuInstance_g.m_uiSignalSlaveCount == 0) && (EplNmtMnuInstance_g.m_uiMandatorySlaveCount == 0)) { // all optional CNs scanned once and all mandatory CNs are OPERATIONAL EplNmtMnuInstance_g.m_wFlags |= EPL_NMTMNU_FLAG_APP_INFORMED; // inform application Ret = EplNmtMnuInstance_g. m_pfnCbBootEvent (kEplNmtBootEventOperational, NmtState, EPL_E_NO_ERROR); if (Ret != kEplSuccessful) { if (Ret == kEplReject) { // ignore error code Ret = kEplSuccessful; } break; } } break; } default: { break; } } } Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuCheckNmtState // // Description: checks the NMT state, i.e. evaluates it with object 0x1F8F // NMT_MNNodeExpState_AU8 and updates object 0x1F8E // NMT_MNNodeCurrState_AU8. // It manipulates m_NodeState in internal node info structure. // // Parameters: uiNodeId_p = node ID // NodeNmtState_p = NMT state of CN // // Returns: tEplKernel = error code // kEplReject = CN was in wrong state and has been reset // // State: // //--------------------------------------------------------------------------- static tEplKernel EplNmtMnuCheckNmtState(unsigned int uiNodeId_p, tEplNmtMnuNodeInfo * pNodeInfo_p, tEplNmtState NodeNmtState_p, WORD wErrorCode_p, tEplNmtState LocalNmtState_p) { tEplKernel Ret = kEplSuccessful; tEplObdSize ObdSize; BYTE bNmtState; BYTE bNmtStatePrev; tEplNmtState ExpNmtState; ObdSize = 1; // read object 0x1F8F NMT_MNNodeExpState_AU8 Ret = EplObduReadEntry(0x1F8F, uiNodeId_p, &bNmtState, &ObdSize); if (Ret != kEplSuccessful) { goto Exit; } // compute expected NMT state ExpNmtState = (tEplNmtState) (bNmtState | EPL_NMT_TYPE_CS); // compute BYTE of current NMT state bNmtState = ((BYTE) NodeNmtState_p & 0xFF); if (ExpNmtState == kEplNmtCsNotActive) { // ignore the current state, because the CN shall be not active Ret = kEplReject; goto Exit; } else if ((ExpNmtState == kEplNmtCsPreOperational2) && (NodeNmtState_p == kEplNmtCsReadyToOperate)) { // CN switched to ReadyToOp // delete timer for timeout handling Ret = EplTimeruDeleteTimer(&pNodeInfo_p->m_TimerHdlLonger); if (Ret != kEplSuccessful) { goto Exit; } pNodeInfo_p->m_NodeState = kEplNmtMnuNodeStateReadyToOp; // update object 0x1F8F NMT_MNNodeExpState_AU8 to ReadyToOp Ret = EplObduWriteEntry(0x1F8F, uiNodeId_p, &bNmtState, 1); if (Ret != kEplSuccessful) { goto Exit; } if ((pNodeInfo_p->m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0) { // node is a mandatory CN -> decrement counter EplNmtMnuInstance_g.m_uiMandatorySlaveCount--; } if (LocalNmtState_p >= kEplNmtMsReadyToOperate) { // start procedure CheckCommunication for this node Ret = EplNmtMnuNodeCheckCom(uiNodeId_p, pNodeInfo_p); if (Ret != kEplSuccessful) { goto Exit; } if ((LocalNmtState_p == kEplNmtMsOperational) && (pNodeInfo_p->m_NodeState == kEplNmtMnuNodeStateComChecked)) { EPL_NMTMNU_DBG_POST_TRACE_VALUE(0, uiNodeId_p, (((NodeNmtState_p & 0xFF) << 8) | kEplNmtCmdStartNode)); // immediately start optional CN, because communication is always OK (e.g. async-only CN) Ret = EplNmtMnuSendNmtCommand(uiNodeId_p, kEplNmtCmdStartNode); if (Ret != kEplSuccessful) { goto Exit; } } } } else if ((ExpNmtState == kEplNmtCsReadyToOperate) && (NodeNmtState_p == kEplNmtCsOperational)) { // CN switched to OPERATIONAL pNodeInfo_p->m_NodeState = kEplNmtMnuNodeStateOperational; if ((pNodeInfo_p->m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0) { // node is a mandatory CN -> decrement counter EplNmtMnuInstance_g.m_uiMandatorySlaveCount--; } } else if ((ExpNmtState != NodeNmtState_p) && !((ExpNmtState == kEplNmtCsPreOperational1) && (NodeNmtState_p == kEplNmtCsPreOperational2))) { // CN is not in expected NMT state (without the exceptions above) WORD wbeErrorCode; if ((pNodeInfo_p-> m_wFlags & EPL_NMTMNU_NODE_FLAG_NOT_SCANNED) != 0) { // decrement only signal slave count if checked once EplNmtMnuInstance_g.m_uiSignalSlaveCount--; pNodeInfo_p->m_wFlags &= ~EPL_NMTMNU_NODE_FLAG_NOT_SCANNED; } if (pNodeInfo_p->m_NodeState == kEplNmtMnuNodeStateUnknown) { // CN is already in state unknown, which means that it got // NMT reset command earlier goto Exit; } // -> CN is in wrong NMT state pNodeInfo_p->m_NodeState = kEplNmtMnuNodeStateUnknown; if (wErrorCode_p == 0) { // assume wrong NMT state error if ((pNodeInfo_p->m_wFlags & EPL_NMTMNU_NODE_FLAG_NMT_CMD_ISSUED) != 0) { // NMT command has been just issued; // ignore wrong NMT state until timer expires; // other errors like LOSS_PRES_TH are still processed goto Exit; } wErrorCode_p = EPL_E_NMT_WRONG_STATE; } BENCHMARK_MOD_07_TOGGLE(9); // $$$ start ERROR_TREATMENT and inform application Ret = EplNmtMnuInstance_g.m_pfnCbNodeEvent(uiNodeId_p, kEplNmtNodeEventError, NodeNmtState_p, wErrorCode_p, (pNodeInfo_p-> m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0); if (Ret != kEplSuccessful) { goto Exit; } EPL_NMTMNU_DBG_POST_TRACE_VALUE(0, uiNodeId_p, (((NodeNmtState_p & 0xFF) << 8) | kEplNmtCmdResetNode)); // reset CN // store error code in NMT command data for diagnostic purpose AmiSetWordToLe(&wbeErrorCode, wErrorCode_p); Ret = EplNmtMnuSendNmtCommandEx(uiNodeId_p, kEplNmtCmdResetNode, &wbeErrorCode, sizeof(wbeErrorCode)); if (Ret == kEplSuccessful) { Ret = kEplReject; } goto Exit; } // check if NMT_MNNodeCurrState_AU8 has to be changed ObdSize = 1; Ret = EplObduReadEntry(0x1F8E, uiNodeId_p, &bNmtStatePrev, &ObdSize); if (Ret != kEplSuccessful) { goto Exit; } if (bNmtState != bNmtStatePrev) { // update object 0x1F8E NMT_MNNodeCurrState_AU8 Ret = EplObduWriteEntry(0x1F8E, uiNodeId_p, &bNmtState, 1); if (Ret != kEplSuccessful) { goto Exit; } Ret = EplNmtMnuInstance_g.m_pfnCbNodeEvent(uiNodeId_p, kEplNmtNodeEventNmtState, NodeNmtState_p, wErrorCode_p, (pNodeInfo_p-> m_dwNodeCfg & EPL_NODEASSIGN_MANDATORY_CN) != 0); if (Ret != kEplSuccessful) { goto Exit; } } Exit: return Ret; } //--------------------------------------------------------------------------- // // Function: EplNmtMnuReset // // Description: reset internal structures, e.g. timers // // Parameters: void // // Returns: tEplKernel = error code // // State: // //--------------------------------------------------------------------------- static tEplKernel EplNmtMnuReset(void) { tEplKernel Ret; int iIndex; Ret = EplTimeruDeleteTimer(&EplNmtMnuInstance_g.m_TimerHdlNmtState); for (iIndex = 1; iIndex <= tabentries(EplNmtMnuInstance_g.m_aNodeInfo); iIndex++) { // delete timer handles Ret = EplTimeruDeleteTimer(&EPL_NMTMNU_GET_NODEINFO(iIndex)-> m_TimerHdlStatReq); Ret = EplTimeruDeleteTimer(&EPL_NMTMNU_GET_NODEINFO(iIndex)-> m_TimerHdlLonger); } return Ret; } #endif // #if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_NMT_MN)) != 0) // EOF