blob: ce0eb33f4c41129c8d2fa35bc62e7c85cc9fbe7d [file] [log] [blame]
/****************************************************************************
(c) SYSTEC electronic GmbH, D-07973 Greiz, August-Bebel-Str. 29
www.systec-electronic.com
Project: openPOWERLINK
Description: source file for SDO Command Layer module
License:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of SYSTEC electronic GmbH nor the names of its
contributors may be used to endorse or promote products derived
from this software without prior written permission. For written
permission, please contact info@systec-electronic.com.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Severability Clause:
If a provision of this License is or becomes illegal, invalid or
unenforceable in any jurisdiction, that shall not affect:
1. the validity or enforceability in that jurisdiction of any other
provision of this License; or
2. the validity or enforceability in other jurisdictions of that or
any other provision of this License.
-------------------------------------------------------------------------
$RCSfile: EplSdoComu.c,v $
$Author: D.Krueger $
$Revision: 1.14 $ $Date: 2008/10/17 15:32:32 $
$State: Exp $
Build Environment:
GCC V3.4
-------------------------------------------------------------------------
Revision History:
2006/06/26 k.t.: start of the implementation
****************************************************************************/
#include "user/EplSdoComu.h"
#if ((((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) == 0) &&\
(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) == 0) )
#error 'ERROR: At least SDO Server or SDO Client should be activate!'
#endif
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
#if (((EPL_MODULE_INTEGRATION) & (EPL_MODULE_OBDU)) == 0) && (EPL_OBD_USE_KERNEL == FALSE)
#error 'ERROR: SDO Server needs OBDu module!'
#endif
#endif
/***************************************************************************/
/* */
/* */
/* G L O B A L D E F I N I T I O N S */
/* */
/* */
/***************************************************************************/
//---------------------------------------------------------------------------
// const defines
//---------------------------------------------------------------------------
#ifndef EPL_MAX_SDO_COM_CON
#define EPL_MAX_SDO_COM_CON 5
#endif
//---------------------------------------------------------------------------
// local types
//---------------------------------------------------------------------------
// intern events
typedef enum {
kEplSdoComConEventSendFirst = 0x00, // first frame to send
kEplSdoComConEventRec = 0x01, // frame received
kEplSdoComConEventConEstablished = 0x02, // connection established
kEplSdoComConEventConClosed = 0x03, // connection closed
kEplSdoComConEventAckReceived = 0x04, // acknowledge received by lower layer
// -> continue sending
kEplSdoComConEventFrameSended = 0x05, // lower has send a frame
kEplSdoComConEventInitError = 0x06, // error duringinitialisiation
// of the connection
kEplSdoComConEventTimeout = 0x07 // timeout in lower layer
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
,
kEplSdoComConEventInitCon = 0x08, // init connection (only client)
kEplSdoComConEventAbort = 0x09 // abort sdo transfer (only client)
#endif
} tEplSdoComConEvent;
typedef enum {
kEplSdoComSendTypeReq = 0x00, // send a request
kEplSdoComSendTypeAckRes = 0x01, // send a resonse without data
kEplSdoComSendTypeRes = 0x02, // send response with data
kEplSdoComSendTypeAbort = 0x03 // send abort
} tEplSdoComSendType;
// state of the state maschine
typedef enum {
// General State
kEplSdoComStateIdle = 0x00, // idle state
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
// Server States
kEplSdoComStateServerSegmTrans = 0x01, // send following frames
#endif
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
// Client States
kEplSdoComStateClientWaitInit = 0x10, // wait for init connection
// on lower layer
kEplSdoComStateClientConnected = 0x11, // connection established
kEplSdoComStateClientSegmTrans = 0x12 // send following frames
#endif
} tEplSdoComState;
// control structure for transaction
typedef struct {
tEplSdoSeqConHdl m_SdoSeqConHdl; // if != 0 -> entry used
tEplSdoComState m_SdoComState;
BYTE m_bTransactionId;
unsigned int m_uiNodeId; // NodeId of the target
// -> needed to reinit connection
// after timeout
tEplSdoTransType m_SdoTransType; // Auto, Expedited, Segmented
tEplSdoServiceType m_SdoServiceType; // WriteByIndex, ReadByIndex
tEplSdoType m_SdoProtType; // protocol layer: Auto, Udp, Asnd, Pdo
BYTE *m_pData; // pointer to data
unsigned int m_uiTransSize; // number of bytes
// to transfer
unsigned int m_uiTransferredByte; // number of bytes
// already transferred
tEplSdoFinishedCb m_pfnTransferFinished; // callback function of the
// application
// -> called in the end of
// the SDO transfer
void *m_pUserArg; // user definable argument pointer
DWORD m_dwLastAbortCode; // save the last abort code
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
// only for client
unsigned int m_uiTargetIndex; // index to access
unsigned int m_uiTargetSubIndex; // subiondex to access
// for future use
unsigned int m_uiTimeout; // timeout for this connection
#endif
} tEplSdoComCon;
// instance table
typedef struct {
tEplSdoComCon m_SdoComCon[EPL_MAX_SDO_COM_CON];
#if defined(WIN32) || defined(_WIN32)
LPCRITICAL_SECTION m_pCriticalSection;
CRITICAL_SECTION m_CriticalSection;
#endif
} tEplSdoComInstance;
//---------------------------------------------------------------------------
// modul globale vars
//---------------------------------------------------------------------------
static tEplSdoComInstance SdoComInstance_g;
//---------------------------------------------------------------------------
// local function prototypes
//---------------------------------------------------------------------------
tEplKernel PUBLIC EplSdoComReceiveCb(tEplSdoSeqConHdl SdoSeqConHdl_p,
tEplAsySdoCom * pAsySdoCom_p,
unsigned int uiDataSize_p);
tEplKernel PUBLIC EplSdoComConCb(tEplSdoSeqConHdl SdoSeqConHdl_p,
tEplAsySdoConState AsySdoConState_p);
static tEplKernel EplSdoComSearchConIntern(tEplSdoSeqConHdl SdoSeqConHdl_p,
tEplSdoComConEvent SdoComConEvent_p,
tEplAsySdoCom * pAsySdoCom_p);
static tEplKernel EplSdoComProcessIntern(tEplSdoComConHdl SdoComCon_p,
tEplSdoComConEvent SdoComConEvent_p,
tEplAsySdoCom * pAsySdoCom_p);
static tEplKernel EplSdoComTransferFinished(tEplSdoComConHdl SdoComCon_p,
tEplSdoComCon * pSdoComCon_p,
tEplSdoComConState
SdoComConState_p);
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
static tEplKernel EplSdoComServerInitReadByIndex(tEplSdoComCon * pSdoComCon_p,
tEplAsySdoCom * pAsySdoCom_p);
static tEplKernel EplSdoComServerSendFrameIntern(tEplSdoComCon * pSdoComCon_p,
unsigned int uiIndex_p,
unsigned int uiSubIndex_p,
tEplSdoComSendType SendType_p);
static tEplKernel EplSdoComServerInitWriteByIndex(tEplSdoComCon * pSdoComCon_p,
tEplAsySdoCom * pAsySdoCom_p);
#endif
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
static tEplKernel EplSdoComClientSend(tEplSdoComCon * pSdoComCon_p);
static tEplKernel EplSdoComClientProcessFrame(tEplSdoComConHdl SdoComCon_p,
tEplAsySdoCom * pAsySdoCom_p);
static tEplKernel EplSdoComClientSendAbort(tEplSdoComCon * pSdoComCon_p,
DWORD dwAbortCode_p);
#endif
/***************************************************************************/
/* */
/* */
/* C L A S S <SDO Command Layer> */
/* */
/* */
/***************************************************************************/
//
// Description: SDO Command layer Modul
//
//
/***************************************************************************/
//=========================================================================//
// //
// P U B L I C F U N C T I O N S //
// //
//=========================================================================//
//---------------------------------------------------------------------------
//
// Function: EplSdoComInit
//
// Description: Init first instance of the module
//
//
//
// Parameters:
//
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel PUBLIC EplSdoComInit(void)
{
tEplKernel Ret;
Ret = EplSdoComAddInstance();
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplSdoComAddInstance
//
// Description: Init additional instance of the module
//
//
//
// Parameters:
//
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel PUBLIC EplSdoComAddInstance(void)
{
tEplKernel Ret;
Ret = kEplSuccessful;
// init controll structure
EPL_MEMSET(&SdoComInstance_g, 0x00, sizeof(SdoComInstance_g));
// init instance of lower layer
Ret = EplSdoAsySeqAddInstance(EplSdoComReceiveCb, EplSdoComConCb);
if (Ret != kEplSuccessful) {
goto Exit;
}
#if defined(WIN32) || defined(_WIN32)
// create critical section for process function
SdoComInstance_g.m_pCriticalSection =
&SdoComInstance_g.m_CriticalSection;
InitializeCriticalSection(SdoComInstance_g.m_pCriticalSection);
#endif
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplSdoComDelInstance
//
// Description: delete instance of the module
//
//
//
// Parameters:
//
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel PUBLIC EplSdoComDelInstance(void)
{
tEplKernel Ret;
Ret = kEplSuccessful;
#if defined(WIN32) || defined(_WIN32)
// delete critical section for process function
DeleteCriticalSection(SdoComInstance_g.m_pCriticalSection);
#endif
Ret = EplSdoAsySeqDelInstance();
if (Ret != kEplSuccessful) {
goto Exit;
}
Exit:
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplSdoComDefineCon
//
// Description: function defines a SDO connection to another node
// -> init lower layer and returns a handle for the connection.
// Two client connections to the same node via the same protocol
// are not allowed. If this function detects such a situation
// it will return kEplSdoComHandleExists and the handle of
// the existing connection in pSdoComConHdl_p.
// Using of existing server connections is possible.
//
// Parameters: pSdoComConHdl_p = pointer to the buffer of the handle
// uiTargetNodeId_p = NodeId of the targetnode
// ProtType_p = type of protocol to use for connection
//
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
tEplKernel PUBLIC EplSdoComDefineCon(tEplSdoComConHdl * pSdoComConHdl_p,
unsigned int uiTargetNodeId_p,
tEplSdoType ProtType_p)
{
tEplKernel Ret;
unsigned int uiCount;
unsigned int uiFreeHdl;
tEplSdoComCon *pSdoComCon;
// check Parameter
ASSERT(pSdoComConHdl_p != NULL);
// check NodeId
if ((uiTargetNodeId_p == EPL_C_ADR_INVALID)
|| (uiTargetNodeId_p >= EPL_C_ADR_BROADCAST)) {
Ret = kEplInvalidNodeId;
}
// search free control structure
pSdoComCon = &SdoComInstance_g.m_SdoComCon[0];
uiCount = 0;
uiFreeHdl = EPL_MAX_SDO_COM_CON;
while (uiCount < EPL_MAX_SDO_COM_CON) {
if (pSdoComCon->m_SdoSeqConHdl == 0) { // free entry
uiFreeHdl = uiCount;
} else if ((pSdoComCon->m_uiNodeId == uiTargetNodeId_p)
&& (pSdoComCon->m_SdoProtType == ProtType_p)) { // existing client connection with same node ID and same protocol type
*pSdoComConHdl_p = uiCount;
Ret = kEplSdoComHandleExists;
goto Exit;
}
uiCount++;
pSdoComCon++;
}
if (uiFreeHdl == EPL_MAX_SDO_COM_CON) {
Ret = kEplSdoComNoFreeHandle;
goto Exit;
}
pSdoComCon = &SdoComInstance_g.m_SdoComCon[uiFreeHdl];
// save handle for application
*pSdoComConHdl_p = uiFreeHdl;
// save parameters
pSdoComCon->m_SdoProtType = ProtType_p;
pSdoComCon->m_uiNodeId = uiTargetNodeId_p;
// set Transaction Id
pSdoComCon->m_bTransactionId = 0;
// check protocol
switch (ProtType_p) {
// udp
case kEplSdoTypeUdp:
{
// call connection int function of lower layer
Ret = EplSdoAsySeqInitCon(&pSdoComCon->m_SdoSeqConHdl,
pSdoComCon->m_uiNodeId,
kEplSdoTypeUdp);
if (Ret != kEplSuccessful) {
goto Exit;
}
break;
}
// Asend
case kEplSdoTypeAsnd:
{
// call connection int function of lower layer
Ret = EplSdoAsySeqInitCon(&pSdoComCon->m_SdoSeqConHdl,
pSdoComCon->m_uiNodeId,
kEplSdoTypeAsnd);
if (Ret != kEplSuccessful) {
goto Exit;
}
break;
}
// Pdo -> not supported
case kEplSdoTypePdo:
default:
{
Ret = kEplSdoComUnsupportedProt;
goto Exit;
}
} // end of switch(m_ProtType_p)
// call process function
Ret = EplSdoComProcessIntern(uiFreeHdl,
kEplSdoComConEventInitCon, NULL);
Exit:
return Ret;
}
#endif
//---------------------------------------------------------------------------
//
// Function: EplSdoComInitTransferByIndex
//
// Description: function init SDO Transfer for a defined connection
//
//
//
// Parameters: SdoComTransParam_p = Structure with parameters for connection
//
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
tEplKernel PUBLIC EplSdoComInitTransferByIndex(tEplSdoComTransParamByIndex *
pSdoComTransParam_p)
{
tEplKernel Ret;
tEplSdoComCon *pSdoComCon;
// check parameter
if ((pSdoComTransParam_p->m_uiSubindex >= 0xFF)
|| (pSdoComTransParam_p->m_uiIndex == 0)
|| (pSdoComTransParam_p->m_uiIndex > 0xFFFF)
|| (pSdoComTransParam_p->m_pData == NULL)
|| (pSdoComTransParam_p->m_uiDataSize == 0)) {
Ret = kEplSdoComInvalidParam;
goto Exit;
}
if (pSdoComTransParam_p->m_SdoComConHdl >= EPL_MAX_SDO_COM_CON) {
Ret = kEplSdoComInvalidHandle;
goto Exit;
}
// get pointer to control structure of connection
pSdoComCon =
&SdoComInstance_g.m_SdoComCon[pSdoComTransParam_p->m_SdoComConHdl];
// check if handle ok
if (pSdoComCon->m_SdoSeqConHdl == 0) {
Ret = kEplSdoComInvalidHandle;
goto Exit;
}
// check if command layer is idle
if ((pSdoComCon->m_uiTransferredByte + pSdoComCon->m_uiTransSize) > 0) { // handle is not idle
Ret = kEplSdoComHandleBusy;
goto Exit;
}
// save parameter
// callback function for end of transfer
pSdoComCon->m_pfnTransferFinished =
pSdoComTransParam_p->m_pfnSdoFinishedCb;
pSdoComCon->m_pUserArg = pSdoComTransParam_p->m_pUserArg;
// set type of SDO command
if (pSdoComTransParam_p->m_SdoAccessType == kEplSdoAccessTypeRead) {
pSdoComCon->m_SdoServiceType = kEplSdoServiceReadByIndex;
} else {
pSdoComCon->m_SdoServiceType = kEplSdoServiceWriteByIndex;
}
// save pointer to data
pSdoComCon->m_pData = pSdoComTransParam_p->m_pData;
// maximal bytes to transfer
pSdoComCon->m_uiTransSize = pSdoComTransParam_p->m_uiDataSize;
// bytes already transfered
pSdoComCon->m_uiTransferredByte = 0;
// reset parts of control structure
pSdoComCon->m_dwLastAbortCode = 0;
pSdoComCon->m_SdoTransType = kEplSdoTransAuto;
// save timeout
//pSdoComCon->m_uiTimeout = SdoComTransParam_p.m_uiTimeout;
// save index and subindex
pSdoComCon->m_uiTargetIndex = pSdoComTransParam_p->m_uiIndex;
pSdoComCon->m_uiTargetSubIndex = pSdoComTransParam_p->m_uiSubindex;
// call process function
Ret = EplSdoComProcessIntern(pSdoComTransParam_p->m_SdoComConHdl, kEplSdoComConEventSendFirst, // event to start transfer
NULL);
Exit:
return Ret;
}
#endif
//---------------------------------------------------------------------------
//
// Function: EplSdoComUndefineCon
//
// Description: function undefine a SDO connection
//
//
//
// Parameters: SdoComConHdl_p = handle for the connection
//
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
tEplKernel PUBLIC EplSdoComUndefineCon(tEplSdoComConHdl SdoComConHdl_p)
{
tEplKernel Ret;
tEplSdoComCon *pSdoComCon;
Ret = kEplSuccessful;
if (SdoComConHdl_p >= EPL_MAX_SDO_COM_CON) {
Ret = kEplSdoComInvalidHandle;
goto Exit;
}
// get pointer to control structure
pSdoComCon = &SdoComInstance_g.m_SdoComCon[SdoComConHdl_p];
// $$$ d.k. abort a running transfer before closing the sequence layer
if (((pSdoComCon->m_SdoSeqConHdl & ~EPL_SDO_SEQ_HANDLE_MASK) !=
EPL_SDO_SEQ_INVALID_HDL)
&& (pSdoComCon->m_SdoSeqConHdl != 0)) {
// close connection in lower layer
switch (pSdoComCon->m_SdoProtType) {
case kEplSdoTypeAsnd:
case kEplSdoTypeUdp:
{
Ret =
EplSdoAsySeqDelCon(pSdoComCon->
m_SdoSeqConHdl);
break;
}
case kEplSdoTypePdo:
case kEplSdoTypeAuto:
default:
{
Ret = kEplSdoComUnsupportedProt;
goto Exit;
}
} // end of switch(pSdoComCon->m_SdoProtType)
}
// clean controll structure
EPL_MEMSET(pSdoComCon, 0x00, sizeof(tEplSdoComCon));
Exit:
return Ret;
}
#endif
//---------------------------------------------------------------------------
//
// Function: EplSdoComGetState
//
// Description: function returns the state fo the connection
//
//
//
// Parameters: SdoComConHdl_p = handle for the connection
// pSdoComFinished_p = pointer to structur for sdo state
//
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
tEplKernel PUBLIC EplSdoComGetState(tEplSdoComConHdl SdoComConHdl_p,
tEplSdoComFinished * pSdoComFinished_p)
{
tEplKernel Ret;
tEplSdoComCon *pSdoComCon;
Ret = kEplSuccessful;
if (SdoComConHdl_p >= EPL_MAX_SDO_COM_CON) {
Ret = kEplSdoComInvalidHandle;
goto Exit;
}
// get pointer to control structure
pSdoComCon = &SdoComInstance_g.m_SdoComCon[SdoComConHdl_p];
// check if handle ok
if (pSdoComCon->m_SdoSeqConHdl == 0) {
Ret = kEplSdoComInvalidHandle;
goto Exit;
}
pSdoComFinished_p->m_pUserArg = pSdoComCon->m_pUserArg;
pSdoComFinished_p->m_uiNodeId = pSdoComCon->m_uiNodeId;
pSdoComFinished_p->m_uiTargetIndex = pSdoComCon->m_uiTargetIndex;
pSdoComFinished_p->m_uiTargetSubIndex = pSdoComCon->m_uiTargetSubIndex;
pSdoComFinished_p->m_uiTransferredByte =
pSdoComCon->m_uiTransferredByte;
pSdoComFinished_p->m_dwAbortCode = pSdoComCon->m_dwLastAbortCode;
pSdoComFinished_p->m_SdoComConHdl = SdoComConHdl_p;
if (pSdoComCon->m_SdoServiceType == kEplSdoServiceWriteByIndex) {
pSdoComFinished_p->m_SdoAccessType = kEplSdoAccessTypeWrite;
} else {
pSdoComFinished_p->m_SdoAccessType = kEplSdoAccessTypeRead;
}
if (pSdoComCon->m_dwLastAbortCode != 0) { // sdo abort
pSdoComFinished_p->m_SdoComConState =
kEplSdoComTransferRxAborted;
// delete abort code
pSdoComCon->m_dwLastAbortCode = 0;
} else if ((pSdoComCon->m_SdoSeqConHdl & ~EPL_SDO_SEQ_HANDLE_MASK) == EPL_SDO_SEQ_INVALID_HDL) { // check state
pSdoComFinished_p->m_SdoComConState =
kEplSdoComTransferLowerLayerAbort;
} else if (pSdoComCon->m_SdoComState == kEplSdoComStateClientWaitInit) {
// finished
pSdoComFinished_p->m_SdoComConState =
kEplSdoComTransferNotActive;
} else if (pSdoComCon->m_uiTransSize == 0) { // finished
pSdoComFinished_p->m_SdoComConState =
kEplSdoComTransferFinished;
}
Exit:
return Ret;
}
#endif
//---------------------------------------------------------------------------
//
// Function: EplSdoComSdoAbort
//
// Description: function abort a sdo transfer
//
//
//
// Parameters: SdoComConHdl_p = handle for the connection
// dwAbortCode_p = abort code
//
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
tEplKernel PUBLIC EplSdoComSdoAbort(tEplSdoComConHdl SdoComConHdl_p,
DWORD dwAbortCode_p)
{
tEplKernel Ret;
tEplSdoComCon *pSdoComCon;
if (SdoComConHdl_p >= EPL_MAX_SDO_COM_CON) {
Ret = kEplSdoComInvalidHandle;
goto Exit;
}
// get pointer to control structure of connection
pSdoComCon = &SdoComInstance_g.m_SdoComCon[SdoComConHdl_p];
// check if handle ok
if (pSdoComCon->m_SdoSeqConHdl == 0) {
Ret = kEplSdoComInvalidHandle;
goto Exit;
}
// save pointer to abort code
pSdoComCon->m_pData = (BYTE *) & dwAbortCode_p;
Ret = EplSdoComProcessIntern(SdoComConHdl_p,
kEplSdoComConEventAbort,
(tEplAsySdoCom *) NULL);
Exit:
return Ret;
}
#endif
//=========================================================================//
// //
// P R I V A T E F U N C T I O N S //
// //
//=========================================================================//
//---------------------------------------------------------------------------
//
// Function: EplSdoComReceiveCb
//
// Description: callback function for SDO Sequence Layer
// -> indicates new data
//
//
//
// Parameters: SdoSeqConHdl_p = Handle for connection
// pAsySdoCom_p = pointer to data
// uiDataSize_p = size of data ($$$ not used yet, but it should)
//
//
// Returns:
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel PUBLIC EplSdoComReceiveCb(tEplSdoSeqConHdl SdoSeqConHdl_p,
tEplAsySdoCom * pAsySdoCom_p,
unsigned int uiDataSize_p)
{
tEplKernel Ret;
// search connection internally
Ret = EplSdoComSearchConIntern(SdoSeqConHdl_p,
kEplSdoComConEventRec, pAsySdoCom_p);
EPL_DBGLVL_SDO_TRACE3
("EplSdoComReceiveCb SdoSeqConHdl: 0x%X, First Byte of pAsySdoCom_p: 0x%02X, uiDataSize_p: 0x%04X\n",
SdoSeqConHdl_p, (WORD) pAsySdoCom_p->m_le_abCommandData[0],
uiDataSize_p);
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplSdoComConCb
//
// Description: callback function called by SDO Sequence Layer to inform
// command layer about state change of connection
//
//
//
// Parameters: SdoSeqConHdl_p = Handle of the connection
// AsySdoConState_p = Event of the connection
//
//
// Returns: tEplKernel = Errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
tEplKernel PUBLIC EplSdoComConCb(tEplSdoSeqConHdl SdoSeqConHdl_p,
tEplAsySdoConState AsySdoConState_p)
{
tEplKernel Ret;
tEplSdoComConEvent SdoComConEvent = kEplSdoComConEventSendFirst;
Ret = kEplSuccessful;
// check state
switch (AsySdoConState_p) {
case kAsySdoConStateConnected:
{
EPL_DBGLVL_SDO_TRACE0("Connection established\n");
SdoComConEvent = kEplSdoComConEventConEstablished;
// start transmission if needed
break;
}
case kAsySdoConStateInitError:
{
EPL_DBGLVL_SDO_TRACE0("Error during initialisation\n");
SdoComConEvent = kEplSdoComConEventInitError;
// inform app about error and close sequence layer handle
break;
}
case kAsySdoConStateConClosed:
{
EPL_DBGLVL_SDO_TRACE0("Connection closed\n");
SdoComConEvent = kEplSdoComConEventConClosed;
// close sequence layer handle
break;
}
case kAsySdoConStateAckReceived:
{
EPL_DBGLVL_SDO_TRACE0("Acknowlage received\n");
SdoComConEvent = kEplSdoComConEventAckReceived;
// continue transmission
break;
}
case kAsySdoConStateFrameSended:
{
EPL_DBGLVL_SDO_TRACE0("One Frame sent\n");
SdoComConEvent = kEplSdoComConEventFrameSended;
// to continue transmission
break;
}
case kAsySdoConStateTimeout:
{
EPL_DBGLVL_SDO_TRACE0("Timeout\n");
SdoComConEvent = kEplSdoComConEventTimeout;
// close sequence layer handle
break;
}
} // end of switch(AsySdoConState_p)
Ret = EplSdoComSearchConIntern(SdoSeqConHdl_p,
SdoComConEvent, (tEplAsySdoCom *) NULL);
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplSdoComSearchConIntern
//
// Description: search a Sdo Sequence Layer connection handle in the
// control structure of the Command Layer
//
// Parameters: SdoSeqConHdl_p = Handle to search
// SdoComConEvent_p = event to process
// pAsySdoCom_p = pointer to received frame
//
// Returns: tEplKernel
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel EplSdoComSearchConIntern(tEplSdoSeqConHdl SdoSeqConHdl_p,
tEplSdoComConEvent SdoComConEvent_p,
tEplAsySdoCom * pAsySdoCom_p)
{
tEplKernel Ret;
tEplSdoComCon *pSdoComCon;
tEplSdoComConHdl HdlCount;
tEplSdoComConHdl HdlFree;
Ret = kEplSdoComNotResponsible;
// get pointer to first element of the array
pSdoComCon = &SdoComInstance_g.m_SdoComCon[0];
HdlCount = 0;
HdlFree = 0xFFFF;
while (HdlCount < EPL_MAX_SDO_COM_CON) {
if (pSdoComCon->m_SdoSeqConHdl == SdoSeqConHdl_p) { // matching command layer handle found
Ret = EplSdoComProcessIntern(HdlCount,
SdoComConEvent_p,
pAsySdoCom_p);
} else if ((pSdoComCon->m_SdoSeqConHdl == 0)
&& (HdlFree == 0xFFFF)) {
HdlFree = HdlCount;
}
pSdoComCon++;
HdlCount++;
}
if (Ret == kEplSdoComNotResponsible) { // no responsible command layer handle found
if (HdlFree == 0xFFFF) { // no free handle
// delete connection immediately
// 2008/04/14 m.u./d.k. This connection actually does not exist.
// pSdoComCon is invalid.
// Ret = EplSdoAsySeqDelCon(pSdoComCon->m_SdoSeqConHdl);
Ret = kEplSdoComNoFreeHandle;
} else { // create new handle
HdlCount = HdlFree;
pSdoComCon = &SdoComInstance_g.m_SdoComCon[HdlCount];
pSdoComCon->m_SdoSeqConHdl = SdoSeqConHdl_p;
Ret = EplSdoComProcessIntern(HdlCount,
SdoComConEvent_p,
pAsySdoCom_p);
}
}
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplSdoComProcessIntern
//
// Description: search a Sdo Sequence Layer connection handle in the
// control structer of the Command Layer
//
//
//
// Parameters: SdoComCon_p = index of control structure of connection
// SdoComConEvent_p = event to process
// pAsySdoCom_p = pointer to received frame
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel EplSdoComProcessIntern(tEplSdoComConHdl SdoComCon_p,
tEplSdoComConEvent SdoComConEvent_p,
tEplAsySdoCom * pAsySdoCom_p)
{
tEplKernel Ret;
tEplSdoComCon *pSdoComCon;
BYTE bFlag;
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
DWORD dwAbortCode;
unsigned int uiSize;
#endif
#if defined(WIN32) || defined(_WIN32)
// enter critical section for process function
EnterCriticalSection(SdoComInstance_g.m_pCriticalSection);
EPL_DBGLVL_SDO_TRACE0
("\n\tEnterCiticalSection EplSdoComProcessIntern\n\n");
#endif
Ret = kEplSuccessful;
// get pointer to control structure
pSdoComCon = &SdoComInstance_g.m_SdoComCon[SdoComCon_p];
// process state maschine
switch (pSdoComCon->m_SdoComState) {
// idle state
case kEplSdoComStateIdle:
{
// check events
switch (SdoComConEvent_p) {
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
// init con for client
case kEplSdoComConEventInitCon:
{
// call of the init function already
// processed in EplSdoComDefineCon()
// only change state to kEplSdoComStateClientWaitInit
pSdoComCon->m_SdoComState =
kEplSdoComStateClientWaitInit;
break;
}
#endif
// int con for server
case kEplSdoComConEventRec:
{
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
// check if init of an transfer and no SDO abort
if ((pAsySdoCom_p->m_le_bFlags & 0x80) == 0) { // SDO request
if ((pAsySdoCom_p->m_le_bFlags & 0x40) == 0) { // no SDO abort
// save tansaction id
pSdoComCon->
m_bTransactionId =
AmiGetByteFromLe
(&pAsySdoCom_p->
m_le_bTransactionId);
// check command
switch (pAsySdoCom_p->
m_le_bCommandId)
{
case kEplSdoServiceNIL:
{ // simply acknowlegde NIL command on sequence layer
Ret =
EplSdoAsySeqSendData
(pSdoComCon->
m_SdoSeqConHdl,
0,
(tEplFrame
*)
NULL);
break;
}
case kEplSdoServiceReadByIndex:
{ // read by index
// search entry an start transfer
EplSdoComServerInitReadByIndex
(pSdoComCon,
pAsySdoCom_p);
// check next state
if (pSdoComCon->m_uiTransSize == 0) { // ready -> stay idle
pSdoComCon->
m_SdoComState
=
kEplSdoComStateIdle;
// reset abort code
pSdoComCon->
m_dwLastAbortCode
=
0;
} else { // segmented transfer
pSdoComCon->
m_SdoComState
=
kEplSdoComStateServerSegmTrans;
}
break;
}
case kEplSdoServiceWriteByIndex:
{
// search entry an start write
EplSdoComServerInitWriteByIndex
(pSdoComCon,
pAsySdoCom_p);
// check next state
if (pSdoComCon->m_uiTransSize == 0) { // already -> stay idle
pSdoComCon->
m_SdoComState
=
kEplSdoComStateIdle;
// reset abort code
pSdoComCon->
m_dwLastAbortCode
=
0;
} else { // segmented transfer
pSdoComCon->
m_SdoComState
=
kEplSdoComStateServerSegmTrans;
}
break;
}
default:
{
// unsupported command
// -> abort senden
dwAbortCode
=
EPL_SDOAC_UNKNOWN_COMMAND_SPECIFIER;
// send abort
pSdoComCon->
m_pData
=
(BYTE
*)
&
dwAbortCode;
Ret =
EplSdoComServerSendFrameIntern
(pSdoComCon,
0,
0,
kEplSdoComSendTypeAbort);
}
} // end of switch(pAsySdoCom_p->m_le_bCommandId)
}
} else { // this command layer handle is not responsible
// (wrong direction or wrong transaction ID)
Ret = kEplSdoComNotResponsible;
goto Exit;
}
#endif // end of #if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
break;
}
// connection closed
case kEplSdoComConEventInitError:
case kEplSdoComConEventTimeout:
case kEplSdoComConEventConClosed:
{
Ret =
EplSdoAsySeqDelCon(pSdoComCon->
m_SdoSeqConHdl);
// clean control structure
EPL_MEMSET(pSdoComCon, 0x00,
sizeof(tEplSdoComCon));
break;
}
default:
// d.k. do nothing
break;
} // end of switch(SdoComConEvent_p)
break;
}
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
//-------------------------------------------------------------------------
// SDO Server part
// segmented transfer
case kEplSdoComStateServerSegmTrans:
{
// check events
switch (SdoComConEvent_p) {
// send next frame
case kEplSdoComConEventAckReceived:
case kEplSdoComConEventFrameSended:
{
// check if it is a read
if (pSdoComCon->m_SdoServiceType ==
kEplSdoServiceReadByIndex) {
// send next frame
EplSdoComServerSendFrameIntern
(pSdoComCon, 0, 0,
kEplSdoComSendTypeRes);
// if all send -> back to idle
if (pSdoComCon->m_uiTransSize == 0) { // back to idle
pSdoComCon->
m_SdoComState =
kEplSdoComStateIdle;
// reset abort code
pSdoComCon->
m_dwLastAbortCode =
0;
}
}
break;
}
// process next frame
case kEplSdoComConEventRec:
{
// check if the frame is a SDO response and has the right transaction ID
bFlag =
AmiGetByteFromLe(&pAsySdoCom_p->
m_le_bFlags);
if (((bFlag & 0x80) != 0)
&&
(AmiGetByteFromLe
(&pAsySdoCom_p->
m_le_bTransactionId) ==
pSdoComCon->m_bTransactionId)) {
// check if it is a abort
if ((bFlag & 0x40) != 0) { // SDO abort
// clear control structure
pSdoComCon->
m_uiTransSize = 0;
pSdoComCon->
m_uiTransferredByte
= 0;
// change state
pSdoComCon->
m_SdoComState =
kEplSdoComStateIdle;
// reset abort code
pSdoComCon->
m_dwLastAbortCode =
0;
// d.k.: do not execute anything further on this command
break;
}
// check if it is a write
if (pSdoComCon->
m_SdoServiceType ==
kEplSdoServiceWriteByIndex)
{
// write data to OD
uiSize =
AmiGetWordFromLe
(&pAsySdoCom_p->
m_le_wSegmentSize);
if (pSdoComCon->
m_dwLastAbortCode ==
0) {
EPL_MEMCPY
(pSdoComCon->
m_pData,
&pAsySdoCom_p->
m_le_abCommandData
[0],
uiSize);
}
// update counter
pSdoComCon->
m_uiTransferredByte
+= uiSize;
pSdoComCon->
m_uiTransSize -=
uiSize;
// update pointer
if (pSdoComCon->
m_dwLastAbortCode ==
0) {
( /*(BYTE*) */
pSdoComCon->
m_pData) +=
uiSize;
}
// check end of transfer
if ((pAsySdoCom_p->m_le_bFlags & 0x30) == 0x30) { // transfer ready
pSdoComCon->
m_uiTransSize
= 0;
if (pSdoComCon->
m_dwLastAbortCode
== 0) {
// send response
// send next frame
EplSdoComServerSendFrameIntern
(pSdoComCon,
0,
0,
kEplSdoComSendTypeRes);
// if all send -> back to idle
if (pSdoComCon->m_uiTransSize == 0) { // back to idle
pSdoComCon->
m_SdoComState
=
kEplSdoComStateIdle;
// reset abort code
pSdoComCon->
m_dwLastAbortCode
=
0;
}
} else { // send dabort code
// send abort
pSdoComCon->
m_pData
=
(BYTE
*)
&
pSdoComCon->
m_dwLastAbortCode;
Ret =
EplSdoComServerSendFrameIntern
(pSdoComCon,
0,
0,
kEplSdoComSendTypeAbort);
// reset abort code
pSdoComCon->
m_dwLastAbortCode
= 0;
}
} else {
// send acknowledge without any Command layer data
Ret =
EplSdoAsySeqSendData
(pSdoComCon->
m_SdoSeqConHdl,
0,
(tEplFrame
*) NULL);
}
}
} else { // this command layer handle is not responsible
// (wrong direction or wrong transaction ID)
Ret = kEplSdoComNotResponsible;
goto Exit;
}
break;
}
// connection closed
case kEplSdoComConEventInitError:
case kEplSdoComConEventTimeout:
case kEplSdoComConEventConClosed:
{
Ret =
EplSdoAsySeqDelCon(pSdoComCon->
m_SdoSeqConHdl);
// clean control structure
EPL_MEMSET(pSdoComCon, 0x00,
sizeof(tEplSdoComCon));
break;
}
default:
// d.k. do nothing
break;
} // end of switch(SdoComConEvent_p)
break;
}
#endif // endif of #if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
//-------------------------------------------------------------------------
// SDO Client part
// wait for finish of establishing connection
case kEplSdoComStateClientWaitInit:
{
// if connection handle is invalid reinit connection
// d.k.: this will be done only on new events (i.e. InitTransfer)
if ((pSdoComCon->
m_SdoSeqConHdl & ~EPL_SDO_SEQ_HANDLE_MASK) ==
EPL_SDO_SEQ_INVALID_HDL) {
// check kind of connection to reinit
// check protocol
switch (pSdoComCon->m_SdoProtType) {
// udp
case kEplSdoTypeUdp:
{
// call connection int function of lower layer
Ret =
EplSdoAsySeqInitCon
(&pSdoComCon->
m_SdoSeqConHdl,
pSdoComCon->m_uiNodeId,
kEplSdoTypeUdp);
if (Ret != kEplSuccessful) {
goto Exit;
}
break;
}
// Asend -> not supported
case kEplSdoTypeAsnd:
{
// call connection int function of lower layer
Ret =
EplSdoAsySeqInitCon
(&pSdoComCon->
m_SdoSeqConHdl,
pSdoComCon->m_uiNodeId,
kEplSdoTypeAsnd);
if (Ret != kEplSuccessful) {
goto Exit;
}
break;
}
// Pdo -> not supported
case kEplSdoTypePdo:
default:
{
Ret = kEplSdoComUnsupportedProt;
goto Exit;
}
} // end of switch(m_ProtType_p)
// d.k.: reset transaction ID, because new sequence layer connection was initialized
// $$$ d.k. is this really necessary?
//pSdoComCon->m_bTransactionId = 0;
}
// check events
switch (SdoComConEvent_p) {
// connection established
case kEplSdoComConEventConEstablished:
{
//send first frame if needed
if ((pSdoComCon->m_uiTransSize > 0)
&& (pSdoComCon->m_uiTargetIndex != 0)) { // start SDO transfer
Ret =
EplSdoComClientSend
(pSdoComCon);
if (Ret != kEplSuccessful) {
goto Exit;
}
// check if segemted transfer
if (pSdoComCon->
m_SdoTransType ==
kEplSdoTransSegmented) {
pSdoComCon->
m_SdoComState =
kEplSdoComStateClientSegmTrans;
goto Exit;
}
}
// goto state kEplSdoComStateClientConnected
pSdoComCon->m_SdoComState =
kEplSdoComStateClientConnected;
goto Exit;
}
case kEplSdoComConEventSendFirst:
{
// infos for transfer already saved by function EplSdoComInitTransferByIndex
break;
}
case kEplSdoComConEventConClosed:
case kEplSdoComConEventInitError:
case kEplSdoComConEventTimeout:
{
// close sequence layer handle
Ret =
EplSdoAsySeqDelCon(pSdoComCon->
m_SdoSeqConHdl);
pSdoComCon->m_SdoSeqConHdl |=
EPL_SDO_SEQ_INVALID_HDL;
// call callback function
if (SdoComConEvent_p ==
kEplSdoComConEventTimeout) {
pSdoComCon->m_dwLastAbortCode =
EPL_SDOAC_TIME_OUT;
} else {
pSdoComCon->m_dwLastAbortCode =
0;
}
Ret =
EplSdoComTransferFinished
(SdoComCon_p, pSdoComCon,
kEplSdoComTransferLowerLayerAbort);
// d.k.: do not clean control structure
break;
}
default:
// d.k. do nothing
break;
} // end of switch(SdoComConEvent_p)
break;
}
// connected
case kEplSdoComStateClientConnected:
{
// check events
switch (SdoComConEvent_p) {
// send a frame
case kEplSdoComConEventSendFirst:
case kEplSdoComConEventAckReceived:
case kEplSdoComConEventFrameSended:
{
Ret = EplSdoComClientSend(pSdoComCon);
if (Ret != kEplSuccessful) {
goto Exit;
}
// check if read transfer finished
if ((pSdoComCon->m_uiTransSize == 0)
&& (pSdoComCon->
m_uiTransferredByte != 0)
&& (pSdoComCon->m_SdoServiceType ==
kEplSdoServiceReadByIndex)) {
// inc transaction id
pSdoComCon->m_bTransactionId++;
// call callback of application
pSdoComCon->m_dwLastAbortCode =
0;
Ret =
EplSdoComTransferFinished
(SdoComCon_p, pSdoComCon,
kEplSdoComTransferFinished);
goto Exit;
}
// check if segemted transfer
if (pSdoComCon->m_SdoTransType ==
kEplSdoTransSegmented) {
pSdoComCon->m_SdoComState =
kEplSdoComStateClientSegmTrans;
goto Exit;
}
break;
}
// frame received
case kEplSdoComConEventRec:
{
// check if the frame is a SDO response and has the right transaction ID
bFlag =
AmiGetByteFromLe(&pAsySdoCom_p->
m_le_bFlags);
if (((bFlag & 0x80) != 0)
&&
(AmiGetByteFromLe
(&pAsySdoCom_p->
m_le_bTransactionId) ==
pSdoComCon->m_bTransactionId)) {
// check if abort or not
if ((bFlag & 0x40) != 0) {
// send acknowledge without any Command layer data
Ret =
EplSdoAsySeqSendData
(pSdoComCon->
m_SdoSeqConHdl, 0,
(tEplFrame *)
NULL);
// inc transaction id
pSdoComCon->
m_bTransactionId++;
// save abort code
pSdoComCon->
m_dwLastAbortCode =
AmiGetDwordFromLe
(&pAsySdoCom_p->
m_le_abCommandData
[0]);
// call callback of application
Ret =
EplSdoComTransferFinished
(SdoComCon_p,
pSdoComCon,
kEplSdoComTransferRxAborted);
goto Exit;
} else { // normal frame received
// check frame
Ret =
EplSdoComClientProcessFrame
(SdoComCon_p,
pAsySdoCom_p);
// check if transfer ready
if (pSdoComCon->
m_uiTransSize ==
0) {
// send acknowledge without any Command layer data
Ret =
EplSdoAsySeqSendData
(pSdoComCon->
m_SdoSeqConHdl,
0,
(tEplFrame
*) NULL);
// inc transaction id
pSdoComCon->
m_bTransactionId++;
// call callback of application
pSdoComCon->
m_dwLastAbortCode
= 0;
Ret =
EplSdoComTransferFinished
(SdoComCon_p,
pSdoComCon,
kEplSdoComTransferFinished);
goto Exit;
}
}
} else { // this command layer handle is not responsible
// (wrong direction or wrong transaction ID)
Ret = kEplSdoComNotResponsible;
goto Exit;
}
break;
}
// connection closed event go back to kEplSdoComStateClientWaitInit
case kEplSdoComConEventConClosed:
{ // connection closed by communication partner
// close sequence layer handle
Ret =
EplSdoAsySeqDelCon(pSdoComCon->
m_SdoSeqConHdl);
// set handle to invalid and enter kEplSdoComStateClientWaitInit
pSdoComCon->m_SdoSeqConHdl |=
EPL_SDO_SEQ_INVALID_HDL;
// change state
pSdoComCon->m_SdoComState =
kEplSdoComStateClientWaitInit;
// call callback of application
pSdoComCon->m_dwLastAbortCode = 0;
Ret =
EplSdoComTransferFinished
(SdoComCon_p, pSdoComCon,
kEplSdoComTransferLowerLayerAbort);
goto Exit;
break;
}
// abort to send from higher layer
case kEplSdoComConEventAbort:
{
EplSdoComClientSendAbort(pSdoComCon,
*((DWORD *)
pSdoComCon->
m_pData));
// inc transaction id
pSdoComCon->m_bTransactionId++;
// call callback of application
pSdoComCon->m_dwLastAbortCode =
*((DWORD *) pSdoComCon->m_pData);
Ret =
EplSdoComTransferFinished
(SdoComCon_p, pSdoComCon,
kEplSdoComTransferTxAborted);
break;
}
case kEplSdoComConEventInitError:
case kEplSdoComConEventTimeout:
{
// close sequence layer handle
Ret =
EplSdoAsySeqDelCon(pSdoComCon->
m_SdoSeqConHdl);
pSdoComCon->m_SdoSeqConHdl |=
EPL_SDO_SEQ_INVALID_HDL;
// change state
pSdoComCon->m_SdoComState =
kEplSdoComStateClientWaitInit;
// call callback of application
pSdoComCon->m_dwLastAbortCode =
EPL_SDOAC_TIME_OUT;
Ret =
EplSdoComTransferFinished
(SdoComCon_p, pSdoComCon,
kEplSdoComTransferLowerLayerAbort);
}
default:
// d.k. do nothing
break;
} // end of switch(SdoComConEvent_p)
break;
}
// process segmented transfer
case kEplSdoComStateClientSegmTrans:
{
// check events
switch (SdoComConEvent_p) {
// sned a frame
case kEplSdoComConEventSendFirst:
case kEplSdoComConEventAckReceived:
case kEplSdoComConEventFrameSended:
{
Ret = EplSdoComClientSend(pSdoComCon);
if (Ret != kEplSuccessful) {
goto Exit;
}
// check if read transfer finished
if ((pSdoComCon->m_uiTransSize == 0)
&& (pSdoComCon->m_SdoServiceType ==
kEplSdoServiceReadByIndex)) {
// inc transaction id
pSdoComCon->m_bTransactionId++;
// change state
pSdoComCon->m_SdoComState =
kEplSdoComStateClientConnected;
// call callback of application
pSdoComCon->m_dwLastAbortCode =
0;
Ret =
EplSdoComTransferFinished
(SdoComCon_p, pSdoComCon,
kEplSdoComTransferFinished);
goto Exit;
}
break;
}
// frame received
case kEplSdoComConEventRec:
{
// check if the frame is a response
bFlag =
AmiGetByteFromLe(&pAsySdoCom_p->
m_le_bFlags);
if (((bFlag & 0x80) != 0)
&&
(AmiGetByteFromLe
(&pAsySdoCom_p->
m_le_bTransactionId) ==
pSdoComCon->m_bTransactionId)) {
// check if abort or not
if ((bFlag & 0x40) != 0) {
// send acknowledge without any Command layer data
Ret =
EplSdoAsySeqSendData
(pSdoComCon->
m_SdoSeqConHdl, 0,
(tEplFrame *)
NULL);
// inc transaction id
pSdoComCon->
m_bTransactionId++;
// change state
pSdoComCon->
m_SdoComState =
kEplSdoComStateClientConnected;
// save abort code
pSdoComCon->
m_dwLastAbortCode =
AmiGetDwordFromLe
(&pAsySdoCom_p->
m_le_abCommandData
[0]);
// call callback of application
Ret =
EplSdoComTransferFinished
(SdoComCon_p,
pSdoComCon,
kEplSdoComTransferRxAborted);
goto Exit;
} else { // normal frame received
// check frame
Ret =
EplSdoComClientProcessFrame
(SdoComCon_p,
pAsySdoCom_p);
// check if transfer ready
if (pSdoComCon->
m_uiTransSize ==
0) {
// send acknowledge without any Command layer data
Ret =
EplSdoAsySeqSendData
(pSdoComCon->
m_SdoSeqConHdl,
0,
(tEplFrame
*) NULL);
// inc transaction id
pSdoComCon->
m_bTransactionId++;
// change state
pSdoComCon->
m_SdoComState
=
kEplSdoComStateClientConnected;
// call callback of application
pSdoComCon->
m_dwLastAbortCode
= 0;
Ret =
EplSdoComTransferFinished
(SdoComCon_p,
pSdoComCon,
kEplSdoComTransferFinished);
}
}
}
break;
}
// connection closed event go back to kEplSdoComStateClientWaitInit
case kEplSdoComConEventConClosed:
{ // connection closed by communication partner
// close sequence layer handle
Ret =
EplSdoAsySeqDelCon(pSdoComCon->
m_SdoSeqConHdl);
// set handle to invalid and enter kEplSdoComStateClientWaitInit
pSdoComCon->m_SdoSeqConHdl |=
EPL_SDO_SEQ_INVALID_HDL;
// change state
pSdoComCon->m_SdoComState =
kEplSdoComStateClientWaitInit;
// inc transaction id
pSdoComCon->m_bTransactionId++;
// call callback of application
pSdoComCon->m_dwLastAbortCode = 0;
Ret =
EplSdoComTransferFinished
(SdoComCon_p, pSdoComCon,
kEplSdoComTransferFinished);
break;
}
// abort to send from higher layer
case kEplSdoComConEventAbort:
{
EplSdoComClientSendAbort(pSdoComCon,
*((DWORD *)
pSdoComCon->
m_pData));
// inc transaction id
pSdoComCon->m_bTransactionId++;
// change state
pSdoComCon->m_SdoComState =
kEplSdoComStateClientConnected;
// call callback of application
pSdoComCon->m_dwLastAbortCode =
*((DWORD *) pSdoComCon->m_pData);
Ret =
EplSdoComTransferFinished
(SdoComCon_p, pSdoComCon,
kEplSdoComTransferTxAborted);
break;
}
case kEplSdoComConEventInitError:
case kEplSdoComConEventTimeout:
{
// close sequence layer handle
Ret =
EplSdoAsySeqDelCon(pSdoComCon->
m_SdoSeqConHdl);
pSdoComCon->m_SdoSeqConHdl |=
EPL_SDO_SEQ_INVALID_HDL;
// change state
pSdoComCon->m_SdoComState =
kEplSdoComStateClientWaitInit;
// call callback of application
pSdoComCon->m_dwLastAbortCode =
EPL_SDOAC_TIME_OUT;
Ret =
EplSdoComTransferFinished
(SdoComCon_p, pSdoComCon,
kEplSdoComTransferLowerLayerAbort);
}
default:
// d.k. do nothing
break;
} // end of switch(SdoComConEvent_p)
break;
}
#endif // endo of #if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
} // end of switch(pSdoComCon->m_SdoComState)
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
Exit:
#endif
#if defined(WIN32) || defined(_WIN32)
// leave critical section for process function
EPL_DBGLVL_SDO_TRACE0
("\n\tLeaveCriticalSection EplSdoComProcessIntern\n\n");
LeaveCriticalSection(SdoComInstance_g.m_pCriticalSection);
#endif
return Ret;
}
//---------------------------------------------------------------------------
//
// Function: EplSdoComServerInitReadByIndex
//
// Description: function start the processing of an read by index command
//
//
//
// Parameters: pSdoComCon_p = pointer to control structure of connection
// pAsySdoCom_p = pointer to received frame
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
static tEplKernel EplSdoComServerInitReadByIndex(tEplSdoComCon * pSdoComCon_p,
tEplAsySdoCom * pAsySdoCom_p)
{
tEplKernel Ret;
unsigned int uiIndex;
unsigned int uiSubindex;
tEplObdSize EntrySize;
tEplObdAccess AccessType;
DWORD dwAbortCode;
dwAbortCode = 0;
// a init of a read could not be a segmented transfer
// -> no variable part of header
// get index and subindex
uiIndex = AmiGetWordFromLe(&pAsySdoCom_p->m_le_abCommandData[0]);
uiSubindex = AmiGetByteFromLe(&pAsySdoCom_p->m_le_abCommandData[2]);
// check accesstype of entry
// existens of entry
//#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_OBDU)) != 0)
Ret = EplObduGetAccessType(uiIndex, uiSubindex, &AccessType);
/*#else
Ret = kEplObdSubindexNotExist;
AccessType = 0;
#endif*/
if (Ret == kEplObdSubindexNotExist) { // subentry doesn't exist
dwAbortCode = EPL_SDOAC_SUB_INDEX_NOT_EXIST;
// send abort
pSdoComCon_p->m_pData = (BYTE *) & dwAbortCode;
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
uiIndex,
uiSubindex,
kEplSdoComSendTypeAbort);
goto Exit;
} else if (Ret != kEplSuccessful) { // entry doesn't exist
dwAbortCode = EPL_SDOAC_OBJECT_NOT_EXIST;
// send abort
pSdoComCon_p->m_pData = (BYTE *) & dwAbortCode;
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
uiIndex,
uiSubindex,
kEplSdoComSendTypeAbort);
goto Exit;
}
// compare accesstype must be read or const
if (((AccessType & kEplObdAccRead) == 0)
&& ((AccessType & kEplObdAccConst) == 0)) {
if ((AccessType & kEplObdAccWrite) != 0) {
// entry read a write only object
dwAbortCode = EPL_SDOAC_READ_TO_WRITE_ONLY_OBJ;
} else {
dwAbortCode = EPL_SDOAC_UNSUPPORTED_ACCESS;
}
// send abort
pSdoComCon_p->m_pData = (BYTE *) & dwAbortCode;
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
uiIndex,
uiSubindex,
kEplSdoComSendTypeAbort);
goto Exit;
}
// save service
pSdoComCon_p->m_SdoServiceType = kEplSdoServiceReadByIndex;
// get size of object to see iof segmented or expedited transfer
//#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_OBDU)) != 0)
EntrySize = EplObduGetDataSize(uiIndex, uiSubindex);
/*#else
EntrySize = 0;
#endif*/
if (EntrySize > EPL_SDO_MAX_PAYLOAD) { // segmented transfer
pSdoComCon_p->m_SdoTransType = kEplSdoTransSegmented;
// get pointer to object-entry data
//#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_OBDU)) != 0)
pSdoComCon_p->m_pData =
EplObduGetObjectDataPtr(uiIndex, uiSubindex);
//#endif
} else { // expedited transfer
pSdoComCon_p->m_SdoTransType = kEplSdoTransExpedited;
}
pSdoComCon_p->m_uiTransSize = EntrySize;
pSdoComCon_p->m_uiTransferredByte = 0;
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
uiIndex,
uiSubindex, kEplSdoComSendTypeRes);
if (Ret != kEplSuccessful) {
// error -> abort
dwAbortCode = EPL_SDOAC_GENERAL_ERROR;
// send abort
pSdoComCon_p->m_pData = (BYTE *) & dwAbortCode;
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
uiIndex,
uiSubindex,
kEplSdoComSendTypeAbort);
goto Exit;
}
Exit:
return Ret;
}
#endif
//---------------------------------------------------------------------------
//
// Function: EplSdoComServerSendFrameIntern();
//
// Description: function creats and send a frame for server
//
//
//
// Parameters: pSdoComCon_p = pointer to control structure of connection
// uiIndex_p = index to send if expedited transfer else 0
// uiSubIndex_p = subindex to send if expedited transfer else 0
// SendType_p = to of frame to send
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
static tEplKernel EplSdoComServerSendFrameIntern(tEplSdoComCon * pSdoComCon_p,
unsigned int uiIndex_p,
unsigned int uiSubIndex_p,
tEplSdoComSendType SendType_p)
{
tEplKernel Ret;
BYTE abFrame[EPL_MAX_SDO_FRAME_SIZE];
tEplFrame *pFrame;
tEplAsySdoCom *pCommandFrame;
unsigned int uiSizeOfFrame;
BYTE bFlag;
Ret = kEplSuccessful;
pFrame = (tEplFrame *) & abFrame[0];
EPL_MEMSET(&abFrame[0], 0x00, sizeof(abFrame));
// build generic part of frame
// get pointer to command layerpart of frame
pCommandFrame =
&pFrame->m_Data.m_Asnd.m_Payload.m_SdoSequenceFrame.
m_le_abSdoSeqPayload;
AmiSetByteToLe(&pCommandFrame->m_le_bCommandId,
pSdoComCon_p->m_SdoServiceType);
AmiSetByteToLe(&pCommandFrame->m_le_bTransactionId,
pSdoComCon_p->m_bTransactionId);
// set size to header size
uiSizeOfFrame = 8;
// check SendType
switch (SendType_p) {
// requestframe to send
case kEplSdoComSendTypeReq:
{
// nothing to do for server
//-> error
Ret = kEplSdoComInvalidSendType;
break;
}
// response without data to send
case kEplSdoComSendTypeAckRes:
{
// set response flag
AmiSetByteToLe(&pCommandFrame->m_le_bFlags, 0x80);
// send frame
Ret = EplSdoAsySeqSendData(pSdoComCon_p->m_SdoSeqConHdl,
uiSizeOfFrame, pFrame);
break;
}
// responsframe to send
case kEplSdoComSendTypeRes:
{
// set response flag
bFlag = AmiGetByteFromLe(&pCommandFrame->m_le_bFlags);
bFlag |= 0x80;
AmiSetByteToLe(&pCommandFrame->m_le_bFlags, bFlag);
// check type of resonse
if (pSdoComCon_p->m_SdoTransType == kEplSdoTransExpedited) { // Expedited transfer
// copy data in frame
//#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_OBDU)) != 0)
Ret = EplObduReadEntryToLe(uiIndex_p,
uiSubIndex_p,
&pCommandFrame->
m_le_abCommandData
[0],
(tEplObdSize *) &
pSdoComCon_p->
m_uiTransSize);
if (Ret != kEplSuccessful) {
goto Exit;
}
//#endif
// set size of frame
AmiSetWordToLe(&pCommandFrame->
m_le_wSegmentSize,
(WORD) pSdoComCon_p->
m_uiTransSize);
// correct byte-counter
uiSizeOfFrame += pSdoComCon_p->m_uiTransSize;
pSdoComCon_p->m_uiTransferredByte +=
pSdoComCon_p->m_uiTransSize;
pSdoComCon_p->m_uiTransSize = 0;
// send frame
uiSizeOfFrame += pSdoComCon_p->m_uiTransSize;
Ret =
EplSdoAsySeqSendData(pSdoComCon_p->
m_SdoSeqConHdl,
uiSizeOfFrame, pFrame);
} else if (pSdoComCon_p->m_SdoTransType == kEplSdoTransSegmented) { // segmented transfer
// distinguish between init, segment and complete
if (pSdoComCon_p->m_uiTransferredByte == 0) { // init
// set init flag
bFlag =
AmiGetByteFromLe(&pCommandFrame->
m_le_bFlags);
bFlag |= 0x10;
AmiSetByteToLe(&pCommandFrame->
m_le_bFlags, bFlag);
// init variable header
AmiSetDwordToLe(&pCommandFrame->
m_le_abCommandData[0],
pSdoComCon_p->
m_uiTransSize);
// copy data in frame
EPL_MEMCPY(&pCommandFrame->
m_le_abCommandData[4],
pSdoComCon_p->m_pData,
(EPL_SDO_MAX_PAYLOAD - 4));
// correct byte-counter
pSdoComCon_p->m_uiTransSize -=
(EPL_SDO_MAX_PAYLOAD - 4);
pSdoComCon_p->m_uiTransferredByte +=
(EPL_SDO_MAX_PAYLOAD - 4);
// move data pointer
pSdoComCon_p->m_pData +=
(EPL_SDO_MAX_PAYLOAD - 4);
// set segment size
AmiSetWordToLe(&pCommandFrame->
m_le_wSegmentSize,
(EPL_SDO_MAX_PAYLOAD -
4));
// send frame
uiSizeOfFrame += EPL_SDO_MAX_PAYLOAD;
Ret =
EplSdoAsySeqSendData(pSdoComCon_p->
m_SdoSeqConHdl,
uiSizeOfFrame,
pFrame);
} else
if ((pSdoComCon_p->m_uiTransferredByte > 0)
&& (pSdoComCon_p->m_uiTransSize > EPL_SDO_MAX_PAYLOAD)) { // segment
// set segment flag
bFlag =
AmiGetByteFromLe(&pCommandFrame->
m_le_bFlags);
bFlag |= 0x20;
AmiSetByteToLe(&pCommandFrame->
m_le_bFlags, bFlag);
// copy data in frame
EPL_MEMCPY(&pCommandFrame->
m_le_abCommandData[0],
pSdoComCon_p->m_pData,
EPL_SDO_MAX_PAYLOAD);
// correct byte-counter
pSdoComCon_p->m_uiTransSize -=
EPL_SDO_MAX_PAYLOAD;
pSdoComCon_p->m_uiTransferredByte +=
EPL_SDO_MAX_PAYLOAD;
// move data pointer
pSdoComCon_p->m_pData +=
EPL_SDO_MAX_PAYLOAD;
// set segment size
AmiSetWordToLe(&pCommandFrame->
m_le_wSegmentSize,
EPL_SDO_MAX_PAYLOAD);
// send frame
uiSizeOfFrame += EPL_SDO_MAX_PAYLOAD;
Ret =
EplSdoAsySeqSendData(pSdoComCon_p->
m_SdoSeqConHdl,
uiSizeOfFrame,
pFrame);
} else {
if ((pSdoComCon_p->m_uiTransSize == 0)
&& (pSdoComCon_p->
m_SdoServiceType !=
kEplSdoServiceWriteByIndex)) {
goto Exit;
}
// complete
// set segment complete flag
bFlag =
AmiGetByteFromLe(&pCommandFrame->
m_le_bFlags);
bFlag |= 0x30;
AmiSetByteToLe(&pCommandFrame->
m_le_bFlags, bFlag);
// copy data in frame
EPL_MEMCPY(&pCommandFrame->
m_le_abCommandData[0],
pSdoComCon_p->m_pData,
pSdoComCon_p->m_uiTransSize);
// correct byte-counter
pSdoComCon_p->m_uiTransferredByte +=
pSdoComCon_p->m_uiTransSize;
// move data pointer
pSdoComCon_p->m_pData +=
pSdoComCon_p->m_uiTransSize;
// set segment size
AmiSetWordToLe(&pCommandFrame->
m_le_wSegmentSize,
(WORD) pSdoComCon_p->
m_uiTransSize);
// send frame
uiSizeOfFrame +=
pSdoComCon_p->m_uiTransSize;
pSdoComCon_p->m_uiTransSize = 0;
Ret =
EplSdoAsySeqSendData(pSdoComCon_p->
m_SdoSeqConHdl,
uiSizeOfFrame,
pFrame);
}
}
break;
}
// abort to send
case kEplSdoComSendTypeAbort:
{
// set response and abort flag
bFlag = AmiGetByteFromLe(&pCommandFrame->m_le_bFlags);
bFlag |= 0xC0;
AmiSetByteToLe(&pCommandFrame->m_le_bFlags, bFlag);
// copy abortcode to frame
AmiSetDwordToLe(&pCommandFrame->m_le_abCommandData[0],
*((DWORD *) pSdoComCon_p->m_pData));
// set size of segment
AmiSetWordToLe(&pCommandFrame->m_le_wSegmentSize,
sizeof(DWORD));
// update counter
pSdoComCon_p->m_uiTransferredByte = sizeof(DWORD);
pSdoComCon_p->m_uiTransSize = 0;
// calc framesize
uiSizeOfFrame += sizeof(DWORD);
Ret = EplSdoAsySeqSendData(pSdoComCon_p->m_SdoSeqConHdl,
uiSizeOfFrame, pFrame);
break;
}
} // end of switch(SendType_p)
Exit:
return Ret;
}
#endif
//---------------------------------------------------------------------------
//
// Function: EplSdoComServerInitWriteByIndex
//
// Description: function start the processing of an write by index command
//
//
//
// Parameters: pSdoComCon_p = pointer to control structure of connection
// pAsySdoCom_p = pointer to received frame
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOS)) != 0)
static tEplKernel EplSdoComServerInitWriteByIndex(tEplSdoComCon * pSdoComCon_p,
tEplAsySdoCom * pAsySdoCom_p)
{
tEplKernel Ret = kEplSuccessful;
unsigned int uiIndex;
unsigned int uiSubindex;
unsigned int uiBytesToTransfer;
tEplObdSize EntrySize;
tEplObdAccess AccessType;
DWORD dwAbortCode;
BYTE *pbSrcData;
dwAbortCode = 0;
// a init of a write
// -> variable part of header possible
// check if expedited or segmented transfer
if ((pAsySdoCom_p->m_le_bFlags & 0x30) == 0x10) { // initiate segmented transfer
pSdoComCon_p->m_SdoTransType = kEplSdoTransSegmented;
// get index and subindex
uiIndex =
AmiGetWordFromLe(&pAsySdoCom_p->m_le_abCommandData[4]);
uiSubindex =
AmiGetByteFromLe(&pAsySdoCom_p->m_le_abCommandData[6]);
// get source-pointer for copy
pbSrcData = &pAsySdoCom_p->m_le_abCommandData[8];
// save size
pSdoComCon_p->m_uiTransSize =
AmiGetDwordFromLe(&pAsySdoCom_p->m_le_abCommandData[0]);
} else if ((pAsySdoCom_p->m_le_bFlags & 0x30) == 0x00) { // expedited transfer
pSdoComCon_p->m_SdoTransType = kEplSdoTransExpedited;
// get index and subindex
uiIndex =
AmiGetWordFromLe(&pAsySdoCom_p->m_le_abCommandData[0]);
uiSubindex =
AmiGetByteFromLe(&pAsySdoCom_p->m_le_abCommandData[2]);
// get source-pointer for copy
pbSrcData = &pAsySdoCom_p->m_le_abCommandData[4];
// save size
pSdoComCon_p->m_uiTransSize =
AmiGetWordFromLe(&pAsySdoCom_p->m_le_wSegmentSize);
// subtract header
pSdoComCon_p->m_uiTransSize -= 4;
} else {
// just ignore any other transfer type
goto Exit;
}
// check accesstype of entry
// existens of entry
//#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_OBDU)) != 0)
Ret = EplObduGetAccessType(uiIndex, uiSubindex, &AccessType);
/*#else
Ret = kEplObdSubindexNotExist;
AccessType = 0;
#endif*/
if (Ret == kEplObdSubindexNotExist) { // subentry doesn't exist
pSdoComCon_p->m_dwLastAbortCode = EPL_SDOAC_SUB_INDEX_NOT_EXIST;
// send abort
// d.k. This is wrong: k.t. not needed send abort on end of write
/*pSdoComCon_p->m_pData = (BYTE*)pSdoComCon_p->m_dwLastAbortCode;
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
uiIndex,
uiSubindex,
kEplSdoComSendTypeAbort); */
goto Abort;
} else if (Ret != kEplSuccessful) { // entry doesn't exist
pSdoComCon_p->m_dwLastAbortCode = EPL_SDOAC_OBJECT_NOT_EXIST;
// send abort
// d.k. This is wrong: k.t. not needed send abort on end of write
/*
pSdoComCon_p->m_pData = (BYTE*)&dwAbortCode;
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
uiIndex,
uiSubindex,
kEplSdoComSendTypeAbort); */
goto Abort;
}
// compare accesstype must be read
if ((AccessType & kEplObdAccWrite) == 0) {
if ((AccessType & kEplObdAccRead) != 0) {
// entry write a read only object
pSdoComCon_p->m_dwLastAbortCode =
EPL_SDOAC_WRITE_TO_READ_ONLY_OBJ;
} else {
pSdoComCon_p->m_dwLastAbortCode =
EPL_SDOAC_UNSUPPORTED_ACCESS;
}
// send abort
// d.k. This is wrong: k.t. not needed send abort on end of write
/*pSdoComCon_p->m_pData = (BYTE*)&dwAbortCode;
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
uiIndex,
uiSubindex,
kEplSdoComSendTypeAbort); */
goto Abort;
}
// save service
pSdoComCon_p->m_SdoServiceType = kEplSdoServiceWriteByIndex;
pSdoComCon_p->m_uiTransferredByte = 0;
// write data to OD
if (pSdoComCon_p->m_SdoTransType == kEplSdoTransExpedited) { // expedited transfer
// size checking is done by EplObduWriteEntryFromLe()
//#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_OBDU)) != 0)
Ret = EplObduWriteEntryFromLe(uiIndex,
uiSubindex,
pbSrcData,
pSdoComCon_p->m_uiTransSize);
switch (Ret) {
case kEplSuccessful:
{
break;
}
case kEplObdAccessViolation:
{
pSdoComCon_p->m_dwLastAbortCode =
EPL_SDOAC_UNSUPPORTED_ACCESS;
// send abort
goto Abort;
}
case kEplObdValueLengthError:
{
pSdoComCon_p->m_dwLastAbortCode =
EPL_SDOAC_DATA_TYPE_LENGTH_NOT_MATCH;
// send abort
goto Abort;
}
case kEplObdValueTooHigh:
{
pSdoComCon_p->m_dwLastAbortCode =
EPL_SDOAC_VALUE_RANGE_TOO_HIGH;
// send abort
goto Abort;
}
case kEplObdValueTooLow:
{
pSdoComCon_p->m_dwLastAbortCode =
EPL_SDOAC_VALUE_RANGE_TOO_LOW;
// send abort
goto Abort;
}
default:
{
pSdoComCon_p->m_dwLastAbortCode =
EPL_SDOAC_GENERAL_ERROR;
// send abort
goto Abort;
}
}
//#endif
// send command acknowledge
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
0,
0,
kEplSdoComSendTypeAckRes);
pSdoComCon_p->m_uiTransSize = 0;
goto Exit;
} else {
// get size of the object to check if it fits
// because we directly write to the destination memory
// d.k. no one calls the user OD callback function
//#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_OBDU)) != 0)
EntrySize = EplObduGetDataSize(uiIndex, uiSubindex);
/*#else
EntrySize = 0;
#endif */
if (EntrySize < pSdoComCon_p->m_uiTransSize) { // parameter too big
pSdoComCon_p->m_dwLastAbortCode =
EPL_SDOAC_DATA_TYPE_LENGTH_TOO_HIGH;
// send abort
// d.k. This is wrong: k.t. not needed send abort on end of write
/*pSdoComCon_p->m_pData = (BYTE*)&dwAbortCode;
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
uiIndex,
uiSubindex,
kEplSdoComSendTypeAbort); */
goto Abort;
}
uiBytesToTransfer =
AmiGetWordFromLe(&pAsySdoCom_p->m_le_wSegmentSize);
// eleminate header (Command header (8) + variable part (4) + Command header (4))
uiBytesToTransfer -= 16;
// get pointer to object entry
//#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_OBDU)) != 0)
pSdoComCon_p->m_pData = EplObduGetObjectDataPtr(uiIndex,
uiSubindex);
//#endif
if (pSdoComCon_p->m_pData == NULL) {
pSdoComCon_p->m_dwLastAbortCode =
EPL_SDOAC_GENERAL_ERROR;
// send abort
// d.k. This is wrong: k.t. not needed send abort on end of write
/* pSdoComCon_p->m_pData = (BYTE*)&pSdoComCon_p->m_dwLastAbortCode;
Ret = EplSdoComServerSendFrameIntern(pSdoComCon_p,
uiIndex,
uiSubindex,
kEplSdoComSendTypeAbort);*/
goto Abort;
}
// copy data
EPL_MEMCPY(pSdoComCon_p->m_pData, pbSrcData, uiBytesToTransfer);
// update internal counter
pSdoComCon_p->m_uiTransferredByte = uiBytesToTransfer;
pSdoComCon_p->m_uiTransSize -= uiBytesToTransfer;
// update target pointer
( /*(BYTE*) */ pSdoComCon_p->m_pData) += uiBytesToTransfer;
// send acknowledge without any Command layer data
Ret = EplSdoAsySeqSendData(pSdoComCon_p->m_SdoSeqConHdl,
0, (tEplFrame *) NULL);
goto Exit;
}
Abort:
if (pSdoComCon_p->m_dwLastAbortCode != 0) {
// send abort
pSdoComCon_p->m_pData =
(BYTE *) & pSdoComCon_p->m_dwLastAbortCode;
Ret =
EplSdoComServerSendFrameIntern(pSdoComCon_p, uiIndex,
uiSubindex,
kEplSdoComSendTypeAbort);
// reset abort code
pSdoComCon_p->m_dwLastAbortCode = 0;
pSdoComCon_p->m_uiTransSize = 0;
goto Exit;
}
Exit:
return Ret;
}
#endif
//---------------------------------------------------------------------------
//
// Function: EplSdoComClientSend
//
// Description: function starts an sdo transfer an send all further frames
//
//
//
// Parameters: pSdoComCon_p = pointer to control structure of connection
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
static tEplKernel EplSdoComClientSend(tEplSdoComCon * pSdoComCon_p)
{
tEplKernel Ret;
BYTE abFrame[EPL_MAX_SDO_FRAME_SIZE];
tEplFrame *pFrame;
tEplAsySdoCom *pCommandFrame;
unsigned int uiSizeOfFrame;
BYTE bFlags;
BYTE *pbPayload;
Ret = kEplSuccessful;
pFrame = (tEplFrame *) & abFrame[0];
EPL_MEMSET(&abFrame[0], 0x00, sizeof(abFrame));
// build generic part of frame
// get pointer to command layerpart of frame
pCommandFrame =
&pFrame->m_Data.m_Asnd.m_Payload.m_SdoSequenceFrame.
m_le_abSdoSeqPayload;
AmiSetByteToLe(&pCommandFrame->m_le_bCommandId,
pSdoComCon_p->m_SdoServiceType);
AmiSetByteToLe(&pCommandFrame->m_le_bTransactionId,
pSdoComCon_p->m_bTransactionId);
// set size constant part of header
uiSizeOfFrame = 8;
// check if first frame to send -> command header needed
if (pSdoComCon_p->m_uiTransSize > 0) {
if (pSdoComCon_p->m_uiTransferredByte == 0) { // start SDO transfer
// check if segmented or expedited transfer
// only for write commands
switch (pSdoComCon_p->m_SdoServiceType) {
case kEplSdoServiceReadByIndex:
{ // first frame of read access always expedited
pSdoComCon_p->m_SdoTransType =
kEplSdoTransExpedited;
pbPayload =
&pCommandFrame->
m_le_abCommandData[0];
// fill rest of header
AmiSetWordToLe(&pCommandFrame->
m_le_wSegmentSize, 4);
// create command header
AmiSetWordToLe(pbPayload,
(WORD) pSdoComCon_p->
m_uiTargetIndex);
pbPayload += 2;
AmiSetByteToLe(pbPayload,
(BYTE) pSdoComCon_p->
m_uiTargetSubIndex);
// calc size
uiSizeOfFrame += 4;
// set pSdoComCon_p->m_uiTransferredByte to one
pSdoComCon_p->m_uiTransferredByte = 1;
break;
}
case kEplSdoServiceWriteByIndex:
{
if (pSdoComCon_p->m_uiTransSize > EPL_SDO_MAX_PAYLOAD) { // segmented transfer
// -> variable part of header needed
// save that transfer is segmented
pSdoComCon_p->m_SdoTransType =
kEplSdoTransSegmented;
// fill variable part of header
AmiSetDwordToLe(&pCommandFrame->
m_le_abCommandData
[0],
pSdoComCon_p->
m_uiTransSize);
// set pointer to real payload
pbPayload =
&pCommandFrame->
m_le_abCommandData[4];
// fill rest of header
AmiSetWordToLe(&pCommandFrame->
m_le_wSegmentSize,
EPL_SDO_MAX_PAYLOAD);
bFlags = 0x10;
AmiSetByteToLe(&pCommandFrame->
m_le_bFlags,
bFlags);
// create command header
AmiSetWordToLe(pbPayload,
(WORD)
pSdoComCon_p->
m_uiTargetIndex);
pbPayload += 2;
AmiSetByteToLe(pbPayload,
(BYTE)
pSdoComCon_p->
m_uiTargetSubIndex);
// on byte for reserved
pbPayload += 2;
// calc size
uiSizeOfFrame +=
EPL_SDO_MAX_PAYLOAD;
// copy payload
EPL_MEMCPY(pbPayload,
pSdoComCon_p->
m_pData,
(EPL_SDO_MAX_PAYLOAD
- 8));
pSdoComCon_p->m_pData +=
(EPL_SDO_MAX_PAYLOAD - 8);
// correct intern counter
pSdoComCon_p->m_uiTransSize -=
(EPL_SDO_MAX_PAYLOAD - 8);
pSdoComCon_p->
m_uiTransferredByte =
(EPL_SDO_MAX_PAYLOAD - 8);
} else { // expedited trandsfer
// save that transfer is expedited
pSdoComCon_p->m_SdoTransType =
kEplSdoTransExpedited;
pbPayload =
&pCommandFrame->
m_le_abCommandData[0];
// create command header
AmiSetWordToLe(pbPayload,
(WORD)
pSdoComCon_p->
m_uiTargetIndex);
pbPayload += 2;
AmiSetByteToLe(pbPayload,
(BYTE)
pSdoComCon_p->
m_uiTargetSubIndex);
// + 2 -> one byte for subindex and one byte reserved
pbPayload += 2;
// copy data
EPL_MEMCPY(pbPayload,
pSdoComCon_p->
m_pData,
pSdoComCon_p->
m_uiTransSize);
// calc size
uiSizeOfFrame +=
(4 +
pSdoComCon_p->
m_uiTransSize);
// fill rest of header
AmiSetWordToLe(&pCommandFrame->
m_le_wSegmentSize,
(WORD) (4 +
pSdoComCon_p->
m_uiTransSize));
pSdoComCon_p->
m_uiTransferredByte =
pSdoComCon_p->m_uiTransSize;
pSdoComCon_p->m_uiTransSize = 0;
}
break;
}
case kEplSdoServiceNIL:
default:
// invalid service requested
Ret = kEplSdoComInvalidServiceType;
goto Exit;
} // end of switch(pSdoComCon_p->m_SdoServiceType)
} else // (pSdoComCon_p->m_uiTransferredByte > 0)
{ // continue SDO transfer
switch (pSdoComCon_p->m_SdoServiceType) {
// for expedited read is nothing to do
// -> server sends data
case kEplSdoServiceWriteByIndex:
{ // send next frame
if (pSdoComCon_p->m_SdoTransType ==
kEplSdoTransSegmented) {
if (pSdoComCon_p->m_uiTransSize > EPL_SDO_MAX_PAYLOAD) { // next segment
pbPayload =
&pCommandFrame->
m_le_abCommandData
[0];
// fill rest of header
AmiSetWordToLe
(&pCommandFrame->
m_le_wSegmentSize,
EPL_SDO_MAX_PAYLOAD);
bFlags = 0x20;
AmiSetByteToLe
(&pCommandFrame->
m_le_bFlags,
bFlags);
// copy data
EPL_MEMCPY(pbPayload,
pSdoComCon_p->
m_pData,
EPL_SDO_MAX_PAYLOAD);
pSdoComCon_p->m_pData +=
EPL_SDO_MAX_PAYLOAD;
// correct intern counter
pSdoComCon_p->
m_uiTransSize -=
EPL_SDO_MAX_PAYLOAD;
pSdoComCon_p->
m_uiTransferredByte
=
EPL_SDO_MAX_PAYLOAD;
// calc size
uiSizeOfFrame +=
EPL_SDO_MAX_PAYLOAD;
} else { // end of transfer
pbPayload =
&pCommandFrame->
m_le_abCommandData
[0];
// fill rest of header
AmiSetWordToLe
(&pCommandFrame->
m_le_wSegmentSize,
(WORD)
pSdoComCon_p->
m_uiTransSize);
bFlags = 0x30;
AmiSetByteToLe
(&pCommandFrame->
m_le_bFlags,
bFlags);
// copy data
EPL_MEMCPY(pbPayload,
pSdoComCon_p->
m_pData,
pSdoComCon_p->
m_uiTransSize);
pSdoComCon_p->m_pData +=
pSdoComCon_p->
m_uiTransSize;
// calc size
uiSizeOfFrame +=
pSdoComCon_p->
m_uiTransSize;
// correct intern counter
pSdoComCon_p->
m_uiTransSize = 0;
pSdoComCon_p->
m_uiTransferredByte
=
pSdoComCon_p->
m_uiTransSize;
}
} else {
goto Exit;
}
break;
}
default:
{
goto Exit;
}
} // end of switch(pSdoComCon_p->m_SdoServiceType)
}
} else {
goto Exit;
}
// call send function of lower layer
switch (pSdoComCon_p->m_SdoProtType) {
case kEplSdoTypeAsnd:
case kEplSdoTypeUdp:
{
Ret = EplSdoAsySeqSendData(pSdoComCon_p->m_SdoSeqConHdl,
uiSizeOfFrame, pFrame);
break;
}
default:
{
Ret = kEplSdoComUnsupportedProt;
}
} // end of switch(pSdoComCon_p->m_SdoProtType)
Exit:
return Ret;
}
#endif
//---------------------------------------------------------------------------
//
// Function: EplSdoComClientProcessFrame
//
// Description: function process a received frame
//
//
//
// Parameters: SdoComCon_p = connection handle
// pAsySdoCom_p = pointer to frame to process
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
static tEplKernel EplSdoComClientProcessFrame(tEplSdoComConHdl SdoComCon_p,
tEplAsySdoCom * pAsySdoCom_p)
{
tEplKernel Ret;
BYTE bBuffer;
unsigned int uiBuffer;
unsigned int uiDataSize;
unsigned long ulBuffer;
tEplSdoComCon *pSdoComCon;
Ret = kEplSuccessful;
// get pointer to control structure
pSdoComCon = &SdoComInstance_g.m_SdoComCon[SdoComCon_p];
// check if transaction Id fit
bBuffer = AmiGetByteFromLe(&pAsySdoCom_p->m_le_bTransactionId);
if (pSdoComCon->m_bTransactionId != bBuffer) {
// incorrect transaction id
// if running transfer
if ((pSdoComCon->m_uiTransferredByte != 0)
&& (pSdoComCon->m_uiTransSize != 0)) {
pSdoComCon->m_dwLastAbortCode = EPL_SDOAC_GENERAL_ERROR;
// -> send abort
EplSdoComClientSendAbort(pSdoComCon,
pSdoComCon->m_dwLastAbortCode);
// call callback of application
Ret =
EplSdoComTransferFinished(SdoComCon_p, pSdoComCon,
kEplSdoComTransferTxAborted);
}
} else { // check if correct command
bBuffer = AmiGetByteFromLe(&pAsySdoCom_p->m_le_bCommandId);
if (pSdoComCon->m_SdoServiceType != bBuffer) {
// incorrect command
// if running transfer
if ((pSdoComCon->m_uiTransferredByte != 0)
&& (pSdoComCon->m_uiTransSize != 0)) {
pSdoComCon->m_dwLastAbortCode =
EPL_SDOAC_GENERAL_ERROR;
// -> send abort
EplSdoComClientSendAbort(pSdoComCon,
pSdoComCon->
m_dwLastAbortCode);
// call callback of application
Ret =
EplSdoComTransferFinished(SdoComCon_p,
pSdoComCon,
kEplSdoComTransferTxAborted);
}
} else { // switch on command
switch (pSdoComCon->m_SdoServiceType) {
case kEplSdoServiceWriteByIndex:
{ // check if confirmation from server
// nothing more to do
break;
}
case kEplSdoServiceReadByIndex:
{ // check if it is an segmented or an expedited transfer
bBuffer =
AmiGetByteFromLe(&pAsySdoCom_p->
m_le_bFlags);
// mask uninteressting bits
bBuffer &= 0x30;
switch (bBuffer) {
// expedited transfer
case 0x00:
{
// check size of buffer
uiBuffer =
AmiGetWordFromLe
(&pAsySdoCom_p->
m_le_wSegmentSize);
if (uiBuffer > pSdoComCon->m_uiTransSize) { // buffer provided by the application is to small
// copy only a part
uiDataSize =
pSdoComCon->
m_uiTransSize;
} else { // buffer fits
uiDataSize =
uiBuffer;
}
// copy data
EPL_MEMCPY(pSdoComCon->
m_pData,
&pAsySdoCom_p->
m_le_abCommandData
[0],
uiDataSize);
// correct counter
pSdoComCon->
m_uiTransSize = 0;
pSdoComCon->
m_uiTransferredByte
= uiDataSize;
break;
}
// start of a segmented transfer
case 0x10:
{ // get total size of transfer
ulBuffer =
AmiGetDwordFromLe
(&pAsySdoCom_p->
m_le_abCommandData
[0]);
if (ulBuffer <= pSdoComCon->m_uiTransSize) { // buffer fit
pSdoComCon->
m_uiTransSize
=
(unsigned
int)
ulBuffer;
} else { // buffer to small
// send abort
pSdoComCon->
m_dwLastAbortCode
=
EPL_SDOAC_DATA_TYPE_LENGTH_TOO_HIGH;
// -> send abort
EplSdoComClientSendAbort
(pSdoComCon,
pSdoComCon->
m_dwLastAbortCode);
// call callback of application
Ret =
EplSdoComTransferFinished
(SdoComCon_p,
pSdoComCon,
kEplSdoComTransferRxAborted);
goto Exit;
}
// get segment size
// check size of buffer
uiBuffer =
AmiGetWordFromLe
(&pAsySdoCom_p->
m_le_wSegmentSize);
// subtract size of vaiable header from datasize
uiBuffer -= 4;
// copy data
EPL_MEMCPY(pSdoComCon->
m_pData,
&pAsySdoCom_p->
m_le_abCommandData
[4],
uiBuffer);
// correct counter an pointer
pSdoComCon->m_pData +=
uiBuffer;
pSdoComCon->
m_uiTransferredByte
+= uiBuffer;
pSdoComCon->
m_uiTransSize -=
uiBuffer;
break;
}
// segment
case 0x20:
{
// get segment size
// check size of buffer
uiBuffer =
AmiGetWordFromLe
(&pAsySdoCom_p->
m_le_wSegmentSize);
// check if data to copy fit to buffer
if (uiBuffer >= pSdoComCon->m_uiTransSize) { // to much data
uiBuffer =
(pSdoComCon->
m_uiTransSize
- 1);
}
// copy data
EPL_MEMCPY(pSdoComCon->
m_pData,
&pAsySdoCom_p->
m_le_abCommandData
[0],
uiBuffer);
// correct counter an pointer
pSdoComCon->m_pData +=
uiBuffer;
pSdoComCon->
m_uiTransferredByte
+= uiBuffer;
pSdoComCon->
m_uiTransSize -=
uiBuffer;
break;
}
// last segment
case 0x30:
{
// get segment size
// check size of buffer
uiBuffer =
AmiGetWordFromLe
(&pAsySdoCom_p->
m_le_wSegmentSize);
// check if data to copy fit to buffer
if (uiBuffer > pSdoComCon->m_uiTransSize) { // to much data
uiBuffer =
(pSdoComCon->
m_uiTransSize
- 1);
}
// copy data
EPL_MEMCPY(pSdoComCon->
m_pData,
&pAsySdoCom_p->
m_le_abCommandData
[0],
uiBuffer);
// correct counter an pointer
pSdoComCon->m_pData +=
uiBuffer;
pSdoComCon->
m_uiTransferredByte
+= uiBuffer;
pSdoComCon->
m_uiTransSize = 0;
break;
}
} // end of switch(bBuffer & 0x30)
break;
}
case kEplSdoServiceNIL:
default:
// invalid service requested
// $$$ d.k. What should we do?
break;
} // end of switch(pSdoComCon->m_SdoServiceType)
}
}
Exit:
return Ret;
}
#endif
//---------------------------------------------------------------------------
//
// Function: EplSdoComClientSendAbort
//
// Description: function send a abort message
//
//
//
// Parameters: pSdoComCon_p = pointer to control structure of connection
// dwAbortCode_p = Sdo abort code
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
#if(((EPL_MODULE_INTEGRATION) & (EPL_MODULE_SDOC)) != 0)
static tEplKernel EplSdoComClientSendAbort(tEplSdoComCon * pSdoComCon_p,
DWORD dwAbortCode_p)
{
tEplKernel Ret;
BYTE abFrame[EPL_MAX_SDO_FRAME_SIZE];
tEplFrame *pFrame;
tEplAsySdoCom *pCommandFrame;
unsigned int uiSizeOfFrame;
Ret = kEplSuccessful;
pFrame = (tEplFrame *) & abFrame[0];
EPL_MEMSET(&abFrame[0], 0x00, sizeof(abFrame));
// build generic part of frame
// get pointer to command layerpart of frame
pCommandFrame =
&pFrame->m_Data.m_Asnd.m_Payload.m_SdoSequenceFrame.
m_le_abSdoSeqPayload;
AmiSetByteToLe(&pCommandFrame->m_le_bCommandId,
pSdoComCon_p->m_SdoServiceType);
AmiSetByteToLe(&pCommandFrame->m_le_bTransactionId,
pSdoComCon_p->m_bTransactionId);
uiSizeOfFrame = 8;
// set response and abort flag
pCommandFrame->m_le_bFlags |= 0x40;
// copy abortcode to frame
AmiSetDwordToLe(&pCommandFrame->m_le_abCommandData[0], dwAbortCode_p);
// set size of segment
AmiSetWordToLe(&pCommandFrame->m_le_wSegmentSize, sizeof(DWORD));
// update counter
pSdoComCon_p->m_uiTransferredByte = sizeof(DWORD);
pSdoComCon_p->m_uiTransSize = 0;
// calc framesize
uiSizeOfFrame += sizeof(DWORD);
// save abort code
pSdoComCon_p->m_dwLastAbortCode = dwAbortCode_p;
// call send function of lower layer
switch (pSdoComCon_p->m_SdoProtType) {
case kEplSdoTypeAsnd:
case kEplSdoTypeUdp:
{
Ret = EplSdoAsySeqSendData(pSdoComCon_p->m_SdoSeqConHdl,
uiSizeOfFrame, pFrame);
break;
}
default:
{
Ret = kEplSdoComUnsupportedProt;
}
} // end of switch(pSdoComCon_p->m_SdoProtType)
return Ret;
}
#endif
//---------------------------------------------------------------------------
//
// Function: EplSdoComTransferFinished
//
// Description: calls callback function of application if available
// and clears entry in control structure
//
// Parameters: pSdoComCon_p = pointer to control structure of connection
// SdoComConState_p = state of SDO transfer
//
// Returns: tEplKernel = errorcode
//
//
// State:
//
//---------------------------------------------------------------------------
static tEplKernel EplSdoComTransferFinished(tEplSdoComConHdl SdoComCon_p,
tEplSdoComCon * pSdoComCon_p,
tEplSdoComConState SdoComConState_p)
{
tEplKernel Ret;
Ret = kEplSuccessful;
if (pSdoComCon_p->m_pfnTransferFinished != NULL) {
tEplSdoFinishedCb pfnTransferFinished;
tEplSdoComFinished SdoComFinished;
SdoComFinished.m_pUserArg = pSdoComCon_p->m_pUserArg;
SdoComFinished.m_uiNodeId = pSdoComCon_p->m_uiNodeId;
SdoComFinished.m_uiTargetIndex = pSdoComCon_p->m_uiTargetIndex;
SdoComFinished.m_uiTargetSubIndex =
pSdoComCon_p->m_uiTargetSubIndex;
SdoComFinished.m_uiTransferredByte =
pSdoComCon_p->m_uiTransferredByte;
SdoComFinished.m_dwAbortCode = pSdoComCon_p->m_dwLastAbortCode;
SdoComFinished.m_SdoComConHdl = SdoComCon_p;
SdoComFinished.m_SdoComConState = SdoComConState_p;
if (pSdoComCon_p->m_SdoServiceType ==
kEplSdoServiceWriteByIndex) {
SdoComFinished.m_SdoAccessType = kEplSdoAccessTypeWrite;
} else {
SdoComFinished.m_SdoAccessType = kEplSdoAccessTypeRead;
}
// reset transfer state so this handle is not busy anymore
pSdoComCon_p->m_uiTransferredByte = 0;
pSdoComCon_p->m_uiTransSize = 0;
pfnTransferFinished = pSdoComCon_p->m_pfnTransferFinished;
// delete function pointer to inform application only once for each transfer
pSdoComCon_p->m_pfnTransferFinished = NULL;
// call application's callback function
pfnTransferFinished(&SdoComFinished);
}
return Ret;
}
// EOF