/* * co_sdoblockclient.c - contains sdo block routines for client * * Copyright (c) 2013-2023 emotas embedded communication GmbH *------------------------------------------------------------------- * $Id: co_sdoblockclient.c 51912 2024-01-19 13:30:38Z boe $ * * *------------------------------------------------------------------- * * */ /********************************************************************/ /** * \brief sdo block routines * * \file co_sdoblockclient.c * contains sdo block transfer routines for client * */ /* header of standard C - libraries ---------------------------------------------------------------------------*/ #include /* header of project specific types ---------------------------------------------------------------------------*/ #include #ifdef CO_SDO_CLIENT_CNT # ifdef CO_SDO_BLOCK #include #include #include #include #include #include #include #include "ico_cobhandler.h" #include "ico_queue.h" #include "ico_event.h" #include "ico_odaccess.h" #include "ico_indication.h" #ifdef CO_SDO_NETWORKING # include "ico_sdoserver.h" #endif /* CO_SDO_NETWORKING */ #include "ico_nmt.h" #include "ico_sdo.h" #include "ico_sdoclient.h" /* constant definitions ---------------------------------------------------------------------------*/ /* local defined data types ---------------------------------------------------------------------------*/ /* list of external used functions, if not in headers ---------------------------------------------------------------------------*/ /* list of global defined functions ---------------------------------------------------------------------------*/ /* list of local defined functions ---------------------------------------------------------------------------*/ static void sdoClientReadBlockconfirmation(CO_SDO_CLIENT_T *pSdo); static void sdoClientWriteBlockEnd(CO_SDO_CLIENT_T *pSdo); static void sdoClientWriteBlock(void *pData); static void sdoClientWriteBlockCont(CO_SDO_CLIENT_T *pSdo); /* external variables ---------------------------------------------------------------------------*/ /* global variables ---------------------------------------------------------------------------*/ /* local defined variables ---------------------------------------------------------------------------*/ /***************************************************************************/ /** * \internal * * \brief icoSdoClientReadBlockInit * * * \return none * */ void icoSdoClientReadBlockInit( CO_SDO_CLIENT_T *pSdo, /* pointer to SDO */ const CO_CAN_REC_MSG_T *pRecData /* pointer to receive data */ ) { UNSIGNED8 trData[CO_CAN_MAX_DATA_LEN]; /* data */ UNSIGNED32 len = 0u; if (pSdo->state != CO_SDO_CLIENT_STATE_BLK_UL_INIT) { /* ignore message */ return; } /* delete timer */ (void)coTimerStop(&pSdo->timer); #ifdef CO_SDO_BLOCK_CRC /* use CRC ? */ if ((pRecData->data[0] & CO_SDO_SCS_BLOCK_UL_CRC) != 0u) { pSdo->blockCrcUsed = CO_TRUE; pSdo->blockCrc = 0u; } else { pSdo->blockCrcUsed = CO_FALSE; } #endif /* CO_SDO_BLOCK_CRC */ /* size indicated ? */ if ((pRecData->data[0] & CO_SDO_SCS_BLOCK_UL_SIZE) != 0u) { /* segmented transfer, len in byte 4..7 */ (void)coreMemcpyPack(&len, &pRecData->data[4], 4u, 0u, CORE_DTYPE_U32, 0u); } else { /* unspecified data length */ len = pSdo->para.size; } /* received len > as internal buffer size */ if (len > pSdo->para.size) { /* send abort */ icoSdoClientAbort(pSdo, RET_DATA_TYPE_MISMATCH); return; } trData[0] = CO_SDO_CCS_BLOCK_UPLOAD | CO_SDO_CCS_BLOCK_SC_UL_BLK; MEMSET(&trData[1], 0, 7u); /* transmit request */ (void)icoTransmitMessage(pSdo->trCob, &trData[0], 0u); /* start timer */ (void)coTimerStart(&pSdo->timer, pSdo->timeOutVal, icoSdoClientTimeOut, pSdo, CO_TIMER_ATTR_ROUNDUP); /*lint !e960 */ /* Derogation MisraC2004 R.16.9 function identifier used without '&' * or parenthesized parameter */ pSdo->para.size = len; pSdo->restSize = len; pSdo->seqNr = 1u; pSdo->state = CO_SDO_CLIENT_STATE_BLK_UL_BLK; } /***************************************************************************/ /** * \internal * * \brief icoSdoClientReadBlock * * * \return none * */ void icoSdoClientReadBlock( CO_SDO_CLIENT_T *pSdo, /* pointer to SDO */ const CO_CAN_REC_MSG_T *pRecData /* pointer to receive data */ ) { UNSIGNED32 len; UNSIGNED32 packOffset = 0u; CORE_DATA_TYPE_T dType; /* delete timer */ (void)coTimerStop(&pSdo->timer); /* check for next correct block number */ if ((pRecData->data[0] & (UNSIGNED8)~(UNSIGNED8)CO_SDO_SCS_BLOCK_UL_LAST) == pSdo->seqNr) { /* block cnt reached ? */ if (pSdo->seqNr <= pSdo->blockSize) { pSdo->seqNr++; } /* last message ? */ if ((pRecData->data[0] & CO_SDO_SCS_BLOCK_UL_LAST) != 0u) { /* yes */ /* save data temporary until block end was received */ MEMCPY(&pSdo->saveBlockData[0], &pRecData->data[1], 7u); /* send response to server */ sdoClientReadBlockconfirmation(pSdo); /* continue with block end sequence */ pSdo->state = CO_SDO_CLIENT_STATE_BLK_UL_END; /* start timer */ (void)coTimerStart(&pSdo->timer, pSdo->timeOutVal, icoSdoClientTimeOut, pSdo, CO_TIMER_ATTR_ROUNDUP); /*lint !e960 */ /* Derogation MisraC2004 R.16.9 function identifier used without '&' * or parenthesized parameter */ return; } /* save data */ if (pSdo->restSize > 7u) { len = 7u; } else { len = pSdo->restSize; } /* received len > as internal buffer size */ if (len > pSdo->restSize) { /* send abort */ icoSdoClientAbort(pSdo, RET_DATA_TYPE_MISMATCH); return; } #ifdef CO_CPU_DSP if (pSdo->domain == CO_TRUE) { packOffset = pSdo->para.size % CO_CPU_DSP_BYTESIZE; } #endif /* CO_CPU_DSP */ if (pSdo->numeric != 0u) { dType = CORE_DTYPE_U8; } else { dType = CORE_DTYPE_NON_NUMERIC; } (void)coreMemcpyPack(pSdo->pData, &pRecData->data[1], len, packOffset, dType, 0u); pSdo->pData += len; pSdo->restSize -= len; #ifdef CO_SDO_BLOCK_CRC if (pSdo->blockCrcUsed == CO_TRUE) { pSdo->blockCrc = icoSdoCrcCalc(pSdo->blockCrc, &pRecData->data[1], len); } #endif /* CO_SDO_BLOCK_CRC */ } /* block cnt reached ? */ if ((pRecData->data[0] & (UNSIGNED8)~(UNSIGNED8)CO_SDO_SCS_BLOCK_UL_LAST) == pSdo->blockSize) { /* send response to server */ sdoClientReadBlockconfirmation(pSdo); } /* start timer */ (void)coTimerStart(&pSdo->timer, pSdo->timeOutVal, icoSdoClientTimeOut, pSdo, CO_TIMER_ATTR_ROUNDUP); /*lint !e960 */ /* Derogation MisraC2004 R.16.9 function identifier used without '&' * or parenthesized parameter */ } /***************************************************************************/ /** * \internal * * \brief sdoClientReadBlockConfirmation * * * \return none * */ static void sdoClientReadBlockconfirmation( CO_SDO_CLIENT_T *pSdo ) { UNSIGNED8 trData[CO_CAN_MAX_DATA_LEN]; /* data */ trData[0] = CO_SDO_CCS_BLOCK_UPLOAD | CO_SDO_CCS_BLOCK_SC_UL_CON; trData[1] = pSdo->seqNr - 1u; trData[2] = pSdo->blockSize; MEMSET(&trData[3], 0, 5u); /* transmit request */ (void)icoTransmitMessage(pSdo->trCob, &trData[0], 0u); if ((pSdo->seqNr - 1u) == pSdo->blockSize) { pSdo->seqNr = 1u; } } /***************************************************************************/ /** * \internal * * \brief icoSdoClientReadBlockEnd * * * \return none * */ void icoSdoClientReadBlockEnd( CO_SDO_CLIENT_T *pSdo, /* pointer to SDO */ const CO_CAN_REC_MSG_T *pRecData /* pointer to receive data */ ) { UNSIGNED8 trData[CO_CAN_MAX_DATA_LEN]; /* data */ UNSIGNED32 packOffset = 0u; UNSIGNED8 len; #ifdef CO_SDO_BLOCK_CRC UNSIGNED16 crc; #endif /* CO_SDO_BLOCK_CRC */ CORE_DATA_TYPE_T dType; /* delete timer */ (void)coTimerStop(&pSdo->timer); /* valid data from last transfer */ len = 7u - ((pRecData->data[0] >> 2) & 0x7u); #ifdef CO_CPU_DSP if (pSdo->domain == CO_TRUE) { packOffset = pSdo->para.size % CO_CPU_DSP_BYTESIZE; } #endif /* CO_DSP32 */ if (pSdo->numeric != 0u) { dType = CORE_DTYPE_U8; } else { dType = CORE_DTYPE_NON_NUMERIC; } (void)coreMemcpyPack(pSdo->pData, &pSdo->saveBlockData[0], (UNSIGNED32)len, packOffset, dType, 0u); #ifdef CO_SDO_BLOCK_CRC if (pSdo->blockCrcUsed == CO_TRUE) { pSdo->blockCrc = icoSdoCrcCalc(pSdo->blockCrc, &pSdo->saveBlockData[0], (UNSIGNED32)len); crc = pRecData->data[1] | (UNSIGNED16)((UNSIGNED16)pRecData->data[2] << 8u); /* printf("crc %x %x\n", pSdo->blockCrc, crc); */ if (pSdo->blockCrc != crc) { /* wrong CRC, abort */ icoSdoClientAbort(pSdo, RET_SDO_CRC_ERROR); return; } } #endif /* CO_SDO_BLOCK_CRC */ trData[0] = CO_SDO_CCS_BLOCK_UPLOAD | CO_SDO_CCS_BLOCK_SC_UL_END; MEMSET(&trData[1], 0, 7u); /* transmit request */ (void)icoTransmitMessage(pSdo->trCob, &trData[0], 0u); pSdo->state = CO_SDO_CLIENT_STATE_FREE; icoSdoClientUserInd(pSdo, CO_SDO_CLIENT_STATE_BLK_UL_END, 0u); } /***************************************************************************/ /** * \internal * * \brief icoSdoClientWriteBlockInit * * * \return none * */ void icoSdoClientWriteBlockInit( CO_SDO_CLIENT_T *pSdo, /* pointer to SDO */ const CO_CAN_REC_MSG_T *pRecData /* pointer to receive data */ ) { if (pSdo->state != CO_SDO_CLIENT_STATE_BLK_DL_INIT) { /* ignore message */ return; } /* delete timer */ (void)coTimerStop(&pSdo->timer); #ifdef CO_SDO_BLOCK_CRC /* use CRC ? */ if ((pRecData->data[0] & CO_SDO_SCS_BLOCK_DL_CRC) != 0u) { pSdo->blockCrcUsed = CO_TRUE; #ifdef CO_EVENT_CSDO_DOMAIN_WRITE /* calculate CRC for domain indication only for first block */ if ((pSdo->split == CO_TRUE) && (pSdo->pFunction != NULL)) { pSdo->blockCrc = icoSdoCrcCalc(0u, pSdo->pData, pSdo->msgCnt * 7); } else #endif /* CO_EVENT_CSDO_DOMAIN_WRITE */ { pSdo->blockCrc = icoSdoCrcCalc(0u, pSdo->pData, pSdo->para.size); } } else { pSdo->blockCrcUsed = CO_FALSE; } #endif /* CO_SDO_BLOCK_CRC */ pSdo->blockSize = pRecData->data[4]; if ((pSdo->blockSize == 0u) || (pSdo->blockSize > 127u)) { icoSdoClientAbort(pSdo, RET_SDO_WRONG_BLOCKSIZE); return; } pSdo->seqNr = 1u; sdoClientWriteBlock(pSdo); } /***************************************************************************/ /** * \internal * * \brief sdoClientWriteBlock * * * \return none * */ static void sdoClientWriteBlock( void *pData ) { CO_SDO_CLIENT_T *pSdo = pData; UNSIGNED8 trData[CO_CAN_MAX_DATA_LEN]; /* data */ UNSIGNED32 size; UNSIGNED32 packOffset = 0u; RET_T retVal; CORE_DATA_TYPE_T dType; trData[0] = pSdo->seqNr; MEMSET(&trData[1], 0, 7u); /* last transfer ? */ if ((pSdo->para.size - pSdo->restSize) > 7u) { size = 7u; } else { /* last transfer */ size = pSdo->para.size - pSdo->restSize; trData[0] |= CO_SDO_SCS_BLOCK_DL_LAST; pSdo->state = CO_SDO_CLIENT_STATE_BLK_DL_ACQ; } # ifdef CO_CPU_DSP if (pSdo->domain == CO_TRUE) { packOffset = pSdo->restSize % CO_CPU_DSP_BYTESIZE; } # endif /* CO_CPU_DSP */ /* get data */ if (pSdo->numeric != 0u) { dType = CORE_DTYPE_U32; } else { dType = CORE_DTYPE_NON_NUMERIC; } coreMemcpyUnpack(&trData[1], pSdo->pData, (UNSIGNED32)size, packOffset, dType, 0u); /* transmit message */ retVal = icoTransmitMessage(pSdo->trCob, &trData[0], 0u); if (retVal == RET_DRV_TRANS_BUFFER_FULL) { /* transmit next message */ (void)icoEventStart(&pSdo->blockEvent, sdoClientWriteBlock, (void *)pSdo); /*lint !e960 */ /* Derogation MisraC2004 R.16.9 function identifier used without '&' * or parenthesized parameter */ return; } /* apply size information */ pSdo->restSize += size; pSdo->pData += size; /* block count reached or last block ? */ if ((pSdo->seqNr >= pSdo->blockSize) || (pSdo->restSize == pSdo->para.size)) { /* await acq from server */ pSdo->state = CO_SDO_CLIENT_STATE_BLK_DL_ACQ; /* start timer */ (void)coTimerStart(&pSdo->timer, pSdo->timeOutVal, icoSdoClientTimeOut, pSdo, CO_TIMER_ATTR_ROUNDUP); /*lint !e960 */ /* Derogation MisraC2004 R.16.9 function identifier used without '&' * or parenthesized parameter */ } else { sdoClientWriteBlockCont(pSdo); } } /******************************************************************************/ /** * \internal * * \brief sdoClientWriteBlockCont * * */ static void sdoClientWriteBlockCont( CO_SDO_CLIENT_T *pSdo ) { #ifdef CO_EVENT_CSDO_DOMAIN_WRITE RET_T retVal; /* splitted domain transfer ? */ if (pSdo->split == CO_TRUE) { /* size reached or last segment */ if (((pSdo->restSize % (7u * pSdo->msgCnt)) == 0u) /*|| ((pRecData->data[0] & CO_SDO_CCS_CONT_BIT) != 0u)*/) { /* domain indication */ if (pSdo->pFunction != NULL) { retVal = pSdo->pFunction(pSdo->para.sdoNr, pSdo->para.index, pSdo->para.subIndex, pSdo->restSize, pSdo->pFctData); /* set pData to start of buffer */ pSdo->pData = pSdo->pDomainData; # ifdef CO_SDO_BLOCK_CRC /* use CRC ? */ if (pSdo->blockCrcUsed == CO_TRUE) { /* calculate CRC for next block */ UNSIGNED32 size; size = pSdo->para.size - pSdo->restSize; if (size > (pSdo->msgCnt * 7u)) { size = pSdo->msgCnt * 7u; } pSdo->blockCrc = icoSdoCrcCalc(pSdo->blockCrc, pSdo->pData, size); } # endif /* CO_SDO_BLOCK_CRC */ # ifdef CO_SDO_CLIENT_DOMAIN_SPLIT_INDICATION /* split indication ? */ if (retVal == RET_SDO_SPLIT_INDICATION) { /* stop timeout timer */ (void)coTimerStop(&pSdo->timer); pSdo->splitIndication = CO_TRUE; return; } pSdo->splitIndication = CO_FALSE; # endif /* CO_SDO_CLIENT_DOMAIN_SPLIT_INDICATION */ if (retVal != RET_OK) { icoSdoClientAbort(pSdo, retVal); return; } } } } #endif /* CO_EVENT_CSDO_DOMAIN_WRITE */ pSdo->seqNr++; /* transmit next message */ (void)icoEventStart(&pSdo->blockEvent, sdoClientWriteBlock, (void *)pSdo); /*lint !e960 */ /* Derogation MisraC2004 R.16.9 function identifier used without '&' * or parenthesized parameter */ } /******************************************************************************/ /** * \internal * * \brief icoSdoClientDomainWriteBlockIndCont - continue SDO Client Domain Write indication * * */ void icoSdoClientDomainWriteBlockIndCont( CO_SDO_CLIENT_T *pSdo ) { pSdo->seqNr++; sdoClientWriteBlock(pSdo); } /***************************************************************************/ /** * \internal * * \brief icoSdoClientWriteBlockAcq * * * \return none * */ void icoSdoClientWriteBlockAcq( CO_SDO_CLIENT_T *pSdo, const CO_CAN_REC_MSG_T *pRecData ) { UNSIGNED8 blk; UNSIGNED32 size; if (pSdo->state != CO_SDO_CLIENT_STATE_BLK_DL_ACQ) { /* ignore message */ return; } /* delete timer */ (void)coTimerStop(&pSdo->timer); /* set new blocksize */ pSdo->blockSize = pRecData->data[2]; if ((pSdo->blockSize < 1u) || (pSdo->blockSize > 127u)) { icoSdoClientAbort(pSdo, RET_SDO_WRONG_BLOCKSIZE); return; } /* check last seqNr */ if (pRecData->data[1] == pSdo->seqNr) { /* ok, start next block */ /* all data transfered ? */ if ((pSdo->para.size - pSdo->restSize) == 0u) { /* send block end */ sdoClientWriteBlockEnd(pSdo); } else { /* start next block */ pSdo->seqNr = 0u; sdoClientWriteBlockCont(pSdo); } } else { /* wrong sequence number ? */ if (pRecData->data[1] > 127u) { icoSdoClientAbort(pSdo, RET_SDO_WRONG_SEQ_NR); return; } /* start with last correct received block + 1 */ pSdo->seqNr = pRecData->data[1] + 1u; /* calculate blocks to be repeated */ blk = pSdo->blockSize - pRecData->data[1]; if (pSdo->para.size == pSdo->restSize) { /* all blocks already sent - calculate last block byte cnt */ /* size of last block */ size = (pSdo->para.size % (pSdo->blockSize * 7ul)) - (pRecData->data[1] * 7ul); } else { /* not all blocks sent */ size = blk * 7ul; } pSdo->pData -= size; pSdo->restSize -= size; /* start next block */ sdoClientWriteBlock(pSdo); } } /***************************************************************************/ /** * \internal * * \brief sdoClientWriteBlockEnd * * * \return none * */ static void sdoClientWriteBlockEnd( CO_SDO_CLIENT_T *pSdo ) { UNSIGNED8 size; UNSIGNED8 trData[CO_CAN_MAX_DATA_LEN]; /* data */ trData[0] = CO_SDO_CCS_BLOCK_DOWNLOAD | CO_SDO_CCS_BLOCK_CS_DL_END; MEMSET(&trData[1], 0, 7u); # ifdef CO_SDO_BLOCK_CRC if (pSdo->blockCrcUsed == CO_TRUE) { /* BLOCK_TEST D6 Start */ /* pSdo->blockCrc = 0; */ /* BLOCK_TEST D6 End */ trData[1] = (UNSIGNED8)(pSdo->blockCrc & 0xffu); trData[2] = (UNSIGNED8)((pSdo->blockCrc >> 8) & 0xffu); } # endif /* CO_SDO_BLOCK_CRC */ /* calculate the unused byte in the last segment */ size = (UNSIGNED8)(pSdo->para.size % 7u); if (size != 0u) { trData[0] |= (UNSIGNED8)((7u - size) << 2); } /* transmit answer */ (void)icoTransmitMessage(pSdo->trCob, &trData[0], 0u); pSdo->state = CO_SDO_CLIENT_STATE_BLK_DL_END; /* start timer */ (void)coTimerStart(&pSdo->timer, pSdo->timeOutVal, icoSdoClientTimeOut, pSdo, CO_TIMER_ATTR_ROUNDUP); /*lint !e960 */ /* Derogation MisraC2004 R.16.9 function identifier used without '&' * or parenthesized parameter */ } # endif /* CO_SDO_BLOCK_CLIENT */ #endif /* CO_SDO_CLIENT_CNT */