Files
linear-Slide/Middleware/CANopenNode/301/CO_SDOclient.c

1778 lines
76 KiB
C
Raw Normal View History

/*
* CANopen Service Data Object - client.
*
* @file CO_SDOclient.c
* @ingroup CO_SDOclient
* @author Janez Paternoster
* @author Matej Severkar
* @copyright 2020 Janez Paternoster
*
* This file is part of <https://github.com/CANopenNode/CANopenNode>, a CANopen Stack.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the License.
*/
#include "301/CO_SDOclient.h"
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_ENABLE) != 0
/* verify configuration */
#if CO_CONFIG_SDO_CLI_BUFFER_SIZE < 7U
#error CO_CONFIG_SDO_CLI_BUFFER_SIZE must be set to 7 or more.
#endif
#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ENABLE) == 0
#error CO_CONFIG_FIFO_ENABLE must be enabled.
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) == 0
#error CO_CONFIG_SDO_CLI_SEGMENTED must be enabled.
#endif
#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_ALT_READ) == 0
#error CO_CONFIG_FIFO_ALT_READ must be enabled.
#endif
#if ((CO_CONFIG_FIFO)&CO_CONFIG_FIFO_CRC16_CCITT) == 0
#error CO_CONFIG_FIFO_CRC16_CCITT must be enabled.
#endif
#endif
/* default 'protocol switch threshold' size for block transfer */
#ifndef CO_CONFIG_SDO_CLI_PST
#define CO_CONFIG_SDO_CLI_PST 21U
#endif
/*
* Read received message from CAN module.
*
* Function will be called (by CAN receive interrupt) every time, when CAN message with correct identifier
* will be received. For more information and description of parameters see file CO_driver.h.
*/
static void
CO_SDOclient_receive(void* object, void* msg) {
CO_SDOclient_t* SDO_C = (CO_SDOclient_t*)object;
uint8_t DLC = CO_CANrxMsg_readDLC(msg);
const uint8_t* data = CO_CANrxMsg_readData(msg);
/* Ignore messages in idle state and messages with wrong length. Ignore
* message also if previous message was not processed yet and not abort */
if ((SDO_C->state != CO_SDO_ST_IDLE) && (DLC == 8U) && (!CO_FLAG_READ(SDO_C->CANrxNew) || (data[0] == 0x80U))) {
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
bool_t state_not_upload_blk_sublock_sreq = (SDO_C->state != CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ);
bool_t state_not_upload_blk_sublock_crsp = (SDO_C->state != CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP);
if ((data[0] == 0x80U) /* abort from server */
|| (state_not_upload_blk_sublock_sreq && state_not_upload_blk_sublock_crsp)) {
#endif
/* copy data and set 'new message' flag */
(void)memcpy((void*)&SDO_C->CANrxData[0], (const void*)&data[0], 8);
CO_FLAG_SET(SDO_C->CANrxNew);
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0
/* Optional signal to RTOS, which can resume task, which handles
* SDO client processing. */
if (SDO_C->pFunctSignal != NULL) {
SDO_C->pFunctSignal(SDO_C->functSignalObject);
}
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
} else if (SDO_C->state == CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) {
/* block upload, copy data directly */
CO_SDO_state_t state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ;
uint8_t seqno = data[0] & 0x7FU;
SDO_C->timeoutTimer = 0;
SDO_C->block_timeoutTimer = 0;
/* verify if sequence number is correct */
if ((seqno <= SDO_C->block_blksize) && (seqno == (SDO_C->block_seqno + 1U))) {
SDO_C->block_seqno = seqno;
/* is this the last segment? */
if ((data[0] & 0x80U) != 0U) {
/* copy data to temporary buffer, because we don't know the number of bytes not containing data */
(void)memcpy((void*)&SDO_C->block_dataUploadLast[0], (const void*)&data[1], 7);
SDO_C->finished = true;
state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP;
} else {
/* Copy data. There is always enough space in fifo buffer,
* because block_blksize was calculated before */
(void)CO_fifo_write(&SDO_C->bufFifo, &data[1], 7, &SDO_C->block_crc);
SDO_C->sizeTran += 7U;
/* all segments in sub-block has been transferred */
if (seqno == SDO_C->block_blksize) {
state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP;
}
}
}
/* If message is duplicate or sequence didn't start yet, ignore it. Otherwise seqno is wrong,
* so break sub-block. Data after last good seqno will be re-transmitted. */
else if ((seqno != SDO_C->block_seqno) && (SDO_C->block_seqno != 0U)) {
state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP;
#ifdef CO_DEBUG_SDO_CLIENT
char msg[80];
sprintf(msg, "sub-block, rx WRONG: sequno=%02X, previous=%02X", seqno, SDO_C->block_seqno);
CO_DEBUG_SDO_CLIENT(msg);
#endif
} else {
/* MISRA C 2004 14.10 */
#ifdef CO_DEBUG_SDO_CLIENT
char msg[80];
sprintf(msg, "sub-block, rx ignored: sequno=%02X, expected=%02X", seqno, SDO_C->block_seqno + 1);
CO_DEBUG_SDO_CLIENT(msg);
#endif
}
/* Is exit from sub-block receive state? */
if (state != CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) {
/* Processing will continue in another thread, so make memory
* barrier here with CO_FLAG_CLEAR() call. */
CO_FLAG_CLEAR(SDO_C->CANrxNew);
SDO_C->state = state;
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0
/* Optional signal to RTOS, which can resume task, which handles SDO client processing. */
if (SDO_C->pFunctSignal != NULL) {
SDO_C->pFunctSignal(SDO_C->functSignalObject);
}
#endif
}
} else { /* MISRA C 2004 14.10 */
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */
}
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0
/*
* Custom function for writing OD object _SDO client parameter_
*
* For more information see file CO_ODinterface.h, OD_IO_t.
*/
static ODR_t
OD_write_1280(OD_stream_t* stream, const void* buf, OD_size_t count, OD_size_t* countWritten) {
/* "count" is already verified in *_init() function */
if ((stream == NULL) || (buf == NULL) || (countWritten == NULL)) {
return ODR_DEV_INCOMPAT;
}
CO_SDOclient_t* SDO_C = (CO_SDOclient_t*)stream->object;
switch (stream->subIndex) {
case 0: /* Highest sub-index supported */ return ODR_READONLY; break;
case 1: { /* COB-ID client -> server */
uint32_t COB_ID = CO_getUint32(buf);
uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU);
uint16_t CAN_ID_cur = (uint16_t)(SDO_C->COB_IDClientToServer & 0x7FFU);
bool_t valid = (COB_ID & 0x80000000U) == 0U;
/* SDO client must not be valid when changing COB_ID */
if (((COB_ID & 0x3FFFF800U) != 0U) || (valid && SDO_C->valid && (CAN_ID != CAN_ID_cur))
|| (valid && CO_IS_RESTRICTED_CAN_ID(CAN_ID))) {
return ODR_INVALID_VALUE;
}
(void)CO_SDOclient_setup(SDO_C, COB_ID, SDO_C->COB_IDServerToClient, SDO_C->nodeIDOfTheSDOServer);
break;
}
case 2: { /* COB-ID server -> client */
uint32_t COB_ID = CO_getUint32(buf);
uint16_t CAN_ID = (uint16_t)(COB_ID & 0x7FFU);
uint16_t CAN_ID_cur = (uint16_t)(SDO_C->COB_IDServerToClient & 0x7FFU);
bool_t valid = (COB_ID & 0x80000000U) == 0U;
/* SDO client must not be valid when changing COB_ID */
if (((COB_ID & 0x3FFFF800U) != 0U) || (valid && SDO_C->valid && (CAN_ID != CAN_ID_cur))
|| (valid && CO_IS_RESTRICTED_CAN_ID(CAN_ID))) {
return ODR_INVALID_VALUE;
}
(void)CO_SDOclient_setup(SDO_C, SDO_C->COB_IDClientToServer, COB_ID, SDO_C->nodeIDOfTheSDOServer);
break;
}
case 3: { /* Node-ID of the SDO server */
uint8_t nodeId = CO_getUint8(buf);
if (nodeId > 127U) {
return ODR_INVALID_VALUE;
}
SDO_C->nodeIDOfTheSDOServer = nodeId;
break;
}
default: return ODR_SUB_NOT_EXIST; break;
}
/* write value to the original location in the Object Dictionary */
return OD_writeOriginal(stream, buf, count, countWritten);
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_FLAG_OD_DYNAMIC */
CO_ReturnError_t
CO_SDOclient_init(CO_SDOclient_t* SDO_C, OD_t* OD, OD_entry_t* OD_1280_SDOcliPar, uint8_t nodeId,
CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx,
uint32_t* errInfo) {
bool_t index_SDOcliPar_min = (OD_getIndex(OD_1280_SDOcliPar) < (uint16_t)(OD_H1280_SDO_CLIENT_1_PARAM));
bool_t index_SDOcliPar_max = (OD_getIndex(OD_1280_SDOcliPar) > ((uint16_t)(OD_H1280_SDO_CLIENT_1_PARAM) + 0x7FU));
/* verify arguments */
if ((SDO_C == NULL) || (OD_1280_SDOcliPar == NULL) || index_SDOcliPar_min || index_SDOcliPar_max
|| (CANdevRx == NULL) || (CANdevTx == NULL)) {
return CO_ERROR_ILLEGAL_ARGUMENT;
}
/* Configure object variables */
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0
SDO_C->OD = OD;
SDO_C->nodeId = nodeId;
#endif
SDO_C->CANdevRx = CANdevRx;
SDO_C->CANdevRxIdx = CANdevRxIdx;
SDO_C->CANdevTx = CANdevTx;
SDO_C->CANdevTxIdx = CANdevTxIdx;
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0
SDO_C->pFunctSignal = NULL;
SDO_C->functSignalObject = NULL;
#endif
/* prepare circular fifo buffer */
CO_fifo_init(&SDO_C->bufFifo, SDO_C->buf, CO_CONFIG_SDO_CLI_BUFFER_SIZE + 1U);
/* Get parameters from Object Dictionary (initial values) */
uint8_t maxSubIndex, nodeIDOfTheSDOServer;
uint32_t COB_IDClientToServer, COB_IDServerToClient;
ODR_t odRet0 = OD_get_u8(OD_1280_SDOcliPar, 0, &maxSubIndex, true);
ODR_t odRet1 = OD_get_u32(OD_1280_SDOcliPar, 1, &COB_IDClientToServer, true);
ODR_t odRet2 = OD_get_u32(OD_1280_SDOcliPar, 2, &COB_IDServerToClient, true);
ODR_t odRet3 = OD_get_u8(OD_1280_SDOcliPar, 3, &nodeIDOfTheSDOServer, true);
if ((odRet0 != ODR_OK) || (maxSubIndex != 3U) || (odRet1 != ODR_OK) || (odRet2 != ODR_OK) || (odRet3 != ODR_OK)) {
if (errInfo != NULL) {
*errInfo = OD_getIndex(OD_1280_SDOcliPar);
}
return CO_ERROR_OD_PARAMETERS;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0
SDO_C->OD_1280_extension.object = SDO_C;
SDO_C->OD_1280_extension.read = OD_readOriginal;
SDO_C->OD_1280_extension.write = OD_write_1280;
ODR_t odRetE = OD_extension_init(OD_1280_SDOcliPar, &SDO_C->OD_1280_extension);
if (odRetE != ODR_OK) {
if (errInfo != NULL) {
*errInfo = OD_getIndex(OD_1280_SDOcliPar);
}
return CO_ERROR_OD_PARAMETERS;
}
/* set to zero to make sure CO_SDOclient_setup() will reconfigure CAN */
SDO_C->COB_IDClientToServer = 0;
SDO_C->COB_IDServerToClient = 0;
#endif
CO_SDO_return_t cliSetupRet = CO_SDOclient_setup(SDO_C, COB_IDClientToServer, COB_IDServerToClient,
nodeIDOfTheSDOServer);
if (cliSetupRet != CO_SDO_RT_ok_communicationEnd) {
return CO_ERROR_ILLEGAL_ARGUMENT;
}
return CO_ERROR_NO;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0
void
CO_SDOclient_initCallbackPre(CO_SDOclient_t* SDOclient, void* object, void (*pFunctSignal)(void* object)) {
if (SDOclient != NULL) {
SDOclient->functSignalObject = object;
SDOclient->pFunctSignal = pFunctSignal;
}
}
#endif
#if (((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0) && defined CO_BIG_ENDIAN
static inline void
reverseBytes(void* start, OD_size_t size) {
uint8_t* lo = (uint8_t*)start;
uint8_t* hi = (uint8_t*)start + size - 1;
uint8_t swap;
while (lo < hi) {
swap = *lo;
*lo++ = *hi;
*hi-- = swap;
}
}
#endif
CO_SDO_return_t
CO_SDOclient_setup(CO_SDOclient_t* SDO_C, uint32_t COB_IDClientToServer, uint32_t COB_IDServerToClient,
uint8_t nodeIDOfTheSDOServer) {
/* verify parameters */
if (SDO_C == NULL) {
return CO_SDO_RT_wrongArguments;
}
/* Configure object variables */
SDO_C->state = CO_SDO_ST_IDLE;
CO_FLAG_CLEAR(SDO_C->CANrxNew);
SDO_C->nodeIDOfTheSDOServer = nodeIDOfTheSDOServer;
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_OD_DYNAMIC) != 0
/* proceed only, if parameters change */
if ((COB_IDClientToServer == SDO_C->COB_IDClientToServer)
&& (COB_IDServerToClient == SDO_C->COB_IDServerToClient)) {
return CO_SDO_RT_ok_communicationEnd;
}
/* store variables */
SDO_C->COB_IDClientToServer = COB_IDClientToServer;
SDO_C->COB_IDServerToClient = COB_IDServerToClient;
#endif
/* verify valid bit */
uint16_t CanIdC2S = ((COB_IDClientToServer & 0x80000000UL) == 0U) ? (uint16_t)(COB_IDClientToServer & 0x7FFU) : 0U;
uint16_t CanIdS2C = ((COB_IDServerToClient & 0x80000000UL) == 0U) ? (uint16_t)(COB_IDServerToClient & 0x7FFU) : 0U;
if ((CanIdC2S != 0U) && (CanIdS2C != 0U)) {
SDO_C->valid = true;
} else {
CanIdC2S = 0;
CanIdS2C = 0;
SDO_C->valid = false;
}
/* configure SDO client CAN reception */
CO_ReturnError_t ret = CO_CANrxBufferInit(SDO_C->CANdevRx, SDO_C->CANdevRxIdx, CanIdS2C, 0x7FF, false, (void*)SDO_C,
CO_SDOclient_receive);
/* configure SDO client CAN transmission */
SDO_C->CANtxBuff = CO_CANtxBufferInit(SDO_C->CANdevTx, SDO_C->CANdevTxIdx, CanIdC2S, false, 8, false);
if ((ret != CO_ERROR_NO) || (SDO_C->CANtxBuff == NULL)) {
SDO_C->valid = false;
return CO_SDO_RT_wrongArguments;
}
return CO_SDO_RT_ok_communicationEnd;
}
/******************************************************************************
* DOWNLOAD *
******************************************************************************/
CO_SDO_return_t
CO_SDOclientDownloadInitiate(CO_SDOclient_t* SDO_C, uint16_t index, uint8_t subIndex, size_t sizeIndicated,
uint16_t SDOtimeoutTime_ms, bool_t blockEnable) {
/* verify parameters */
if ((SDO_C == NULL) || !SDO_C->valid) {
return CO_SDO_RT_wrongArguments;
}
/* save parameters */
SDO_C->index = index;
SDO_C->subIndex = subIndex;
SDO_C->sizeInd = sizeIndicated;
SDO_C->sizeTran = 0;
SDO_C->finished = false;
SDO_C->SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 1000U;
SDO_C->timeoutTimer = 0;
CO_fifo_reset(&SDO_C->bufFifo);
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0
/* if node-ID of the SDO server is the same as node-ID of this node, then transfer data within this node */
if ((SDO_C->OD != NULL) && (SDO_C->nodeId != 0U) && (SDO_C->nodeIDOfTheSDOServer == SDO_C->nodeId)) {
SDO_C->OD_IO.write = NULL;
SDO_C->state = CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER;
} else
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
if (blockEnable && ((sizeIndicated == 0U) || (sizeIndicated > (size_t)(CO_CONFIG_SDO_CLI_PST)))) {
SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ;
} else
#endif
{
SDO_C->state = CO_SDO_ST_DOWNLOAD_INITIATE_REQ;
}
CO_FLAG_CLEAR(SDO_C->CANrxNew);
return CO_SDO_RT_ok_communicationEnd;
}
void
CO_SDOclientDownloadInitSize(CO_SDOclient_t* SDO_C, size_t sizeIndicated) {
if (SDO_C != NULL) {
SDO_C->sizeInd = sizeIndicated;
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
if ((SDO_C->state == CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ) && (sizeIndicated > 0U)
&& (sizeIndicated <= (size_t)(CO_CONFIG_SDO_CLI_PST))) {
SDO_C->state = CO_SDO_ST_DOWNLOAD_INITIATE_REQ;
}
#endif
}
}
size_t
CO_SDOclientDownloadBufWrite(CO_SDOclient_t* SDO_C, const uint8_t* buf, size_t count) {
size_t ret = 0;
if ((SDO_C != NULL) && (buf != NULL)) {
ret = CO_fifo_write(&SDO_C->bufFifo, buf, count, NULL);
}
return ret;
}
CO_SDO_return_t
CO_SDOclientDownload(CO_SDOclient_t* SDO_C, uint32_t timeDifference_us, bool_t send_abort, bool_t bufferPartial,
CO_SDO_abortCode_t* SDOabortCode, size_t* sizeTransferred, uint32_t* timerNext_us) {
(void)timerNext_us;
(void)bufferPartial; /* may be unused */
CO_SDO_return_t ret = CO_SDO_RT_waitingResponse;
CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE;
if ((SDO_C == NULL) || !SDO_C->valid) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_wrongArguments;
} else if (SDO_C->state == CO_SDO_ST_IDLE) {
ret = CO_SDO_RT_ok_communicationEnd;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0
/* Transfer data locally */
else if ((SDO_C->state == CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER) && !send_abort) {
/* search object dictionary in first pass */
if (SDO_C->OD_IO.write == NULL) {
ODR_t odRet;
odRet = OD_getSub(OD_find(SDO_C->OD, SDO_C->index), SDO_C->subIndex, &SDO_C->OD_IO, false);
if (odRet != ODR_OK) {
abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet);
ret = CO_SDO_RT_endedWithClientAbort;
} else if ((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_RW) == 0U) {
abortCode = CO_SDO_AB_UNSUPPORTED_ACCESS;
ret = CO_SDO_RT_endedWithClientAbort;
} else if ((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_W) == 0U) {
abortCode = CO_SDO_AB_READONLY;
ret = CO_SDO_RT_endedWithClientAbort;
} else if (SDO_C->OD_IO.write == NULL) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_endedWithClientAbort;
} else { /* MISRA C 2004 14.10 */
}
}
/* write data, in several passes if necessary */
if (SDO_C->OD_IO.write != NULL) {
size_t count = CO_fifo_getOccupied(&SDO_C->bufFifo);
uint8_t buf[CO_CONFIG_SDO_CLI_BUFFER_SIZE + 2U];
(void)CO_fifo_read(&SDO_C->bufFifo, buf, count, NULL);
SDO_C->sizeTran += count;
/* error: no data */
if ((count == 0U) || (count > CO_CONFIG_SDO_CLI_BUFFER_SIZE)) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* verify if sizeTran is too large */
else if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) {
SDO_C->sizeTran -= count;
abortCode = CO_SDO_AB_DATA_LONG;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* Verify sizeTran is too small in last segment of data */
else if (!bufferPartial && (SDO_C->sizeInd > 0U) && (SDO_C->sizeTran < SDO_C->sizeInd)) {
abortCode = CO_SDO_AB_DATA_SHORT;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* is the last segment of data? */
else if (!bufferPartial) {
#ifdef CO_BIG_ENDIAN
/* swap int16_t .. uint64_t data if necessary */
if ((SDO_C->OD_IO.stream.attribute & ODA_MB) != 0) {
reverseBytes(buf, count);
}
#endif
OD_size_t sizeInOd = SDO_C->OD_IO.stream.dataLength;
/* If dataType is string, then size of data downloaded may be shorter than size of
* OD data buffer. If so, add two zero bytes to terminate (unicode) string. Shorten
* also OD data size, (temporary, send info about EOF into OD_IO.write) */
if (((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_STR) != 0U)
&& ((sizeInOd == 0U) || (SDO_C->sizeTran < sizeInOd))) {
buf[count] = 0;
count++;
SDO_C->sizeTran++;
if ((sizeInOd == 0U) || (sizeInOd > SDO_C->sizeTran)) {
buf[count] = 0;
count++;
SDO_C->sizeTran++;
}
SDO_C->OD_IO.stream.dataLength = (OD_size_t)SDO_C->sizeTran;
}
/* Indicate OD data size, if necessary. Used for EOF check. */
else if (sizeInOd == 0U) {
SDO_C->OD_IO.stream.dataLength = (OD_size_t)SDO_C->sizeTran;
}
/* Verify if size of data downloaded matches data size in OD. */
else if (SDO_C->sizeTran != sizeInOd) {
abortCode = (SDO_C->sizeTran > sizeInOd) ? CO_SDO_AB_DATA_LONG : CO_SDO_AB_DATA_SHORT;
ret = CO_SDO_RT_endedWithClientAbort;
} else { /* MISRA C 2004 14.10 */
}
} else { /* MISRA C 2004 14.10 */
}
if (abortCode == CO_SDO_AB_NONE) {
OD_size_t countWritten = 0;
ODR_t odRet;
/* write data to Object Dictionary */
CO_LOCK_OD(SDO_C->CANdevTx);
odRet = SDO_C->OD_IO.write(&SDO_C->OD_IO.stream, buf, (OD_size_t)count, &countWritten);
CO_UNLOCK_OD(SDO_C->CANdevTx);
/* verify for errors in write */
if ((odRet != ODR_OK) && (odRet != ODR_PARTIAL)) {
abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet);
ret = CO_SDO_RT_endedWithServerAbort;
}
/* error if OD variable was written completely, but SDO download still has data */
else if (bufferPartial && (odRet == ODR_OK)) {
abortCode = CO_SDO_AB_DATA_LONG;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* is end of transfer? */
else if (!bufferPartial) {
/* error if OD variable was not written completely, but SDO download finished */
if (odRet == ODR_PARTIAL) {
abortCode = CO_SDO_AB_DATA_SHORT;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* data transfer finished successfully */
else {
ret = CO_SDO_RT_ok_communicationEnd;
}
} else {
ret = CO_SDO_RT_waitingLocalTransfer;
}
}
}
if (ret != CO_SDO_RT_waitingLocalTransfer) {
SDO_C->state = CO_SDO_ST_IDLE;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0
/* Inform OS to call this function again without delay. */
else if (timerNext_us != NULL) {
*timerNext_us = 0;
} else { /* MISRA C 2004 14.10 */
}
#endif
}
#endif /* CO_CONFIG_SDO_CLI_LOCAL */
/* CAN data received */
else if (CO_FLAG_READ(SDO_C->CANrxNew)) {
/* is SDO abort */
if (SDO_C->CANrxData[0] == 0x80U) {
uint32_t code;
(void)memcpy((void*)(&code), (const void*)(&SDO_C->CANrxData[4]), sizeof(code));
abortCode = (CO_SDO_abortCode_t)CO_SWAP_32(code);
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_endedWithServerAbort;
} else if (send_abort) {
abortCode = (SDOabortCode != NULL) ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT;
SDO_C->state = CO_SDO_ST_ABORT;
} else {
switch (SDO_C->state) {
case CO_SDO_ST_DOWNLOAD_INITIATE_RSP: {
if (SDO_C->CANrxData[0] == 0x60U) {
/* verify index and subindex */
uint16_t index;
uint8_t subindex;
index = ((uint16_t)SDO_C->CANrxData[2]) << 8;
index |= SDO_C->CANrxData[1];
subindex = SDO_C->CANrxData[3];
if ((index != SDO_C->index) || (subindex != SDO_C->subIndex)) {
abortCode = CO_SDO_AB_PRAM_INCOMPAT;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0
if (SDO_C->finished) {
/* expedited transfer */
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_ok_communicationEnd;
} else {
/* segmented transfer - prepare the first segment */
SDO_C->toggle = 0x00;
SDO_C->state = CO_SDO_ST_DOWNLOAD_SEGMENT_REQ;
}
#else
/* expedited transfer */
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_ok_communicationEnd;
#endif
} else {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
}
break;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0
case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP: {
if ((SDO_C->CANrxData[0] & 0xEFU) == 0x20U) {
/* verify and alternate toggle bit */
uint8_t toggle = SDO_C->CANrxData[0] & 0x10U;
if (toggle != SDO_C->toggle) {
abortCode = CO_SDO_AB_TOGGLE_BIT;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
SDO_C->toggle = (toggle == 0x00U) ? 0x10U : 0x00U;
/* is end of transfer? */
if (SDO_C->finished) {
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_ok_communicationEnd;
} else {
SDO_C->state = CO_SDO_ST_DOWNLOAD_SEGMENT_REQ;
}
} else {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
}
break;
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_SEGMENTED */
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP: {
if ((SDO_C->CANrxData[0] & 0xFBU) == 0xA0U) {
/* verify index and subindex */
uint16_t index;
uint8_t subindex;
index = ((uint16_t)SDO_C->CANrxData[2]) << 8;
index |= SDO_C->CANrxData[1];
subindex = SDO_C->CANrxData[3];
if ((index != SDO_C->index) || (subindex != SDO_C->subIndex)) {
abortCode = CO_SDO_AB_PRAM_INCOMPAT;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
SDO_C->block_crc = 0;
SDO_C->block_blksize = SDO_C->CANrxData[4];
if ((SDO_C->block_blksize < 1U) || (SDO_C->block_blksize > 127U)) {
SDO_C->block_blksize = 127;
}
SDO_C->block_seqno = 0;
(void)CO_fifo_altBegin(&SDO_C->bufFifo, 0);
SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ;
} else {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
}
break;
}
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP: {
if (SDO_C->CANrxData[0] == 0xA2U) {
/* check number of segments */
if (SDO_C->CANrxData[1] < SDO_C->block_seqno) {
/* NOT all segments transferred successfully. Re-transmit data after erroneous segment. */
size_t cntFailed = (size_t)(SDO_C->block_seqno) - (size_t)(SDO_C->CANrxData[1]);
cntFailed = (cntFailed * 7U) - SDO_C->block_noData;
SDO_C->sizeTran -= cntFailed;
(void)CO_fifo_altBegin(&SDO_C->bufFifo, (size_t)SDO_C->CANrxData[1] * 7U);
SDO_C->finished = false;
} else if (SDO_C->CANrxData[1] > SDO_C->block_seqno) {
/* something strange from server, break transmission */
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
break;
} else { /* MISRA C 2004 14.10 */
}
/* confirm successfully transmitted data */
CO_fifo_altFinish(&SDO_C->bufFifo, &SDO_C->block_crc);
if (SDO_C->finished) {
SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_END_REQ;
} else {
SDO_C->block_blksize = SDO_C->CANrxData[2];
SDO_C->block_seqno = 0;
(void)CO_fifo_altBegin(&SDO_C->bufFifo, 0);
SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ;
}
} else {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
}
break;
}
case CO_SDO_ST_DOWNLOAD_BLK_END_RSP: {
if (SDO_C->CANrxData[0] == 0xA1U) {
/* SDO block download successfully transferred */
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_ok_communicationEnd;
} else {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
}
break;
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) == 0
case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP:
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) == 0
case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_END_RSP:
#endif
case CO_SDO_ST_IDLE:
case CO_SDO_ST_ABORT:
case CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER:
case CO_SDO_ST_DOWNLOAD_INITIATE_REQ:
case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ:
case CO_SDO_ST_UPLOAD_LOCAL_TRANSFER:
case CO_SDO_ST_UPLOAD_INITIATE_REQ:
case CO_SDO_ST_UPLOAD_INITIATE_RSP:
case CO_SDO_ST_UPLOAD_SEGMENT_REQ:
case CO_SDO_ST_UPLOAD_SEGMENT_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_END_REQ:
case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ:
case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP:
case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2:
case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ:
case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP:
case CO_SDO_ST_UPLOAD_BLK_END_SREQ:
case CO_SDO_ST_UPLOAD_BLK_END_CRSP:
default: {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
}
}
SDO_C->timeoutTimer = 0;
timeDifference_us = 0;
CO_FLAG_CLEAR(SDO_C->CANrxNew);
} else if (send_abort) {
abortCode = (SDOabortCode != NULL) ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT;
SDO_C->state = CO_SDO_ST_ABORT;
} else { /* MISRA C 2004 14.10 */
}
/* Timeout timers and transmit bufferFull flag */
if (ret == CO_SDO_RT_waitingResponse) {
if (SDO_C->timeoutTimer < SDO_C->SDOtimeoutTime_us) {
SDO_C->timeoutTimer += timeDifference_us;
}
if (SDO_C->timeoutTimer >= SDO_C->SDOtimeoutTime_us) {
abortCode = CO_SDO_AB_TIMEOUT;
SDO_C->state = CO_SDO_ST_ABORT;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0
else if (timerNext_us != NULL) {
/* check again after timeout time elapsed */
uint32_t diff = SDO_C->SDOtimeoutTime_us - SDO_C->timeoutTimer;
if (*timerNext_us > diff) {
*timerNext_us = diff;
}
} else { /* MISRA C 2004 14.10 */
}
#endif
if (SDO_C->CANtxBuff->bufferFull) {
ret = CO_SDO_RT_transmittBufferFull;
}
}
/* Transmit CAN data */
if (ret == CO_SDO_RT_waitingResponse) {
size_t count;
(void)memset((void*)&SDO_C->CANtxBuff->data[0], 0, 8);
switch (SDO_C->state) {
case CO_SDO_ST_DOWNLOAD_INITIATE_REQ: {
SDO_C->CANtxBuff->data[0] = 0x20;
SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index;
SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8);
SDO_C->CANtxBuff->data[3] = SDO_C->subIndex;
/* get count of data bytes to transfer */
count = CO_fifo_getOccupied(&SDO_C->bufFifo);
/* is expedited transfer, <= 4bytes of data */
if (((SDO_C->sizeInd == 0U) && (count <= 4U)) || ((SDO_C->sizeInd > 0U) && (SDO_C->sizeInd <= 4U))) {
SDO_C->CANtxBuff->data[0] |= 0x02U;
/* verify length, indicate data size */
if ((count == 0U) || ((SDO_C->sizeInd > 0U) && (SDO_C->sizeInd != count))) {
SDO_C->state = CO_SDO_ST_IDLE;
abortCode = CO_SDO_AB_TYPE_MISMATCH;
ret = CO_SDO_RT_endedWithClientAbort;
break;
}
if (SDO_C->sizeInd > 0U) {
SDO_C->CANtxBuff->data[0] |= (uint8_t)(0x01U | ((4U - count) << 2));
}
/* copy data */
(void)CO_fifo_read(&SDO_C->bufFifo, &SDO_C->CANtxBuff->data[4], count, NULL);
SDO_C->sizeTran = count;
SDO_C->finished = true;
} else {
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0
/* segmented transfer, indicate data size */
if (SDO_C->sizeInd > 0U) {
uint32_t size = CO_SWAP_32((uint32_t)SDO_C->sizeInd);
SDO_C->CANtxBuff->data[0] |= 0x01U;
(void)memcpy((void*)(&SDO_C->CANtxBuff->data[4]), (const void*)(&size), sizeof(size));
}
#else
SDO_C->state = CO_SDO_ST_IDLE;
abortCode = CO_SDO_AB_UNSUPPORTED_ACCESS;
ret = CO_SDO_RT_endedWithClientAbort;
break;
#endif
}
/* reset timeout timer and send message */
SDO_C->timeoutTimer = 0;
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
SDO_C->state = CO_SDO_ST_DOWNLOAD_INITIATE_RSP;
break;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0
case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ: {
/* fill data bytes */
count = CO_fifo_read(&SDO_C->bufFifo, &SDO_C->CANtxBuff->data[1], 7, NULL);
/* verify if sizeTran is too large */
SDO_C->sizeTran += count;
if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) {
SDO_C->sizeTran -= count;
abortCode = CO_SDO_AB_DATA_LONG;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
/* SDO command specifier */
SDO_C->CANtxBuff->data[0] = (uint8_t)(SDO_C->toggle | ((7U - count) << 1));
/* is end of transfer? Verify also sizeTran */
if ((CO_fifo_getOccupied(&SDO_C->bufFifo) == 0U) && !bufferPartial) {
if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran < SDO_C->sizeInd)) {
abortCode = CO_SDO_AB_DATA_SHORT;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
SDO_C->CANtxBuff->data[0] |= 0x01U;
SDO_C->finished = true;
}
/* reset timeout timer and send message */
SDO_C->timeoutTimer = 0;
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
SDO_C->state = CO_SDO_ST_DOWNLOAD_SEGMENT_RSP;
break;
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_SEGMENTED */
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ: {
SDO_C->CANtxBuff->data[0] = 0xC4;
SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index;
SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8);
SDO_C->CANtxBuff->data[3] = SDO_C->subIndex;
/* indicate data size */
if (SDO_C->sizeInd > 0U) {
uint32_t size = CO_SWAP_32((uint32_t)SDO_C->sizeInd);
SDO_C->CANtxBuff->data[0] |= 0x02U;
(void)memcpy((void*)(&SDO_C->CANtxBuff->data[4]), (const void*)(&size), sizeof(size));
}
/* reset timeout timer and send message */
SDO_C->timeoutTimer = 0;
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP;
break;
}
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ: {
if ((CO_fifo_altGetOccupied(&SDO_C->bufFifo) < 7U) && bufferPartial) {
/* wait until data are refilled */
break;
}
SDO_C->block_seqno++;
SDO_C->CANtxBuff->data[0] = SDO_C->block_seqno;
/* get up to 7 data bytes */
count = CO_fifo_altRead(&SDO_C->bufFifo, &SDO_C->CANtxBuff->data[1], 7);
SDO_C->block_noData = (uint8_t)(7U - count);
/* verify if sizeTran is too large */
SDO_C->sizeTran += count;
if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) {
SDO_C->sizeTran -= count;
abortCode = CO_SDO_AB_DATA_LONG;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
/* is end of transfer? Verify also sizeTran */
if ((CO_fifo_altGetOccupied(&SDO_C->bufFifo) == 0U) && !bufferPartial) {
if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran < SDO_C->sizeInd)) {
abortCode = CO_SDO_AB_DATA_SHORT;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
SDO_C->CANtxBuff->data[0] |= 0x80U;
SDO_C->finished = true;
SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP;
}
/* are all segments in current block transferred? */
else if (SDO_C->block_seqno >= SDO_C->block_blksize) {
SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0
else {
/* Inform OS to call this function again without delay. */
if (timerNext_us != NULL) {
*timerNext_us = 0;
}
}
#endif
/* reset timeout timer and send message */
SDO_C->timeoutTimer = 0;
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
break;
}
case CO_SDO_ST_DOWNLOAD_BLK_END_REQ: {
SDO_C->CANtxBuff->data[0] = (uint8_t)(0xC1U | (SDO_C->block_noData << 2));
SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->block_crc;
SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->block_crc >> 8);
/* reset timeout timer and send message */
SDO_C->timeoutTimer = 0;
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
SDO_C->state = CO_SDO_ST_DOWNLOAD_BLK_END_RSP;
break;
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) == 0
case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ:
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) == 0
case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_END_REQ:
#endif
case CO_SDO_ST_IDLE:
case CO_SDO_ST_ABORT:
case CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER:
case CO_SDO_ST_DOWNLOAD_INITIATE_RSP:
case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP:
case CO_SDO_ST_UPLOAD_LOCAL_TRANSFER:
case CO_SDO_ST_UPLOAD_INITIATE_REQ:
case CO_SDO_ST_UPLOAD_INITIATE_RSP:
case CO_SDO_ST_UPLOAD_SEGMENT_REQ:
case CO_SDO_ST_UPLOAD_SEGMENT_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_END_RSP:
case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ:
case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP:
case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2:
case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ:
case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP:
case CO_SDO_ST_UPLOAD_BLK_END_SREQ:
case CO_SDO_ST_UPLOAD_BLK_END_CRSP:
default: {
/* none */
break;
}
}
}
if (ret == CO_SDO_RT_waitingResponse) {
if (SDO_C->state == CO_SDO_ST_ABORT) {
uint32_t code = CO_SWAP_32((uint32_t)abortCode);
/* Send SDO abort message */
SDO_C->CANtxBuff->data[0] = 0x80;
SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index;
SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8);
SDO_C->CANtxBuff->data[3] = SDO_C->subIndex;
(void)memcpy((void*)(&SDO_C->CANtxBuff->data[4]), (const void*)(&code), sizeof(code));
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_endedWithClientAbort;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
else if (SDO_C->state == CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ) {
ret = CO_SDO_RT_blockDownldInProgress;
} else { /* MISRA C 2004 14.10 */
}
#endif
}
if (sizeTransferred != NULL) {
*sizeTransferred = SDO_C->sizeTran;
}
if (SDOabortCode != NULL) {
*SDOabortCode = abortCode;
}
return ret;
}
/******************************************************************************
* UPLOAD *
******************************************************************************/
CO_SDO_return_t
CO_SDOclientUploadInitiate(CO_SDOclient_t* SDO_C, uint16_t index, uint8_t subIndex, uint16_t SDOtimeoutTime_ms,
bool_t blockEnable) {
/* verify parameters */
if ((SDO_C == NULL) || !SDO_C->valid) {
return CO_SDO_RT_wrongArguments;
}
/* save parameters */
SDO_C->index = index;
SDO_C->subIndex = subIndex;
SDO_C->sizeInd = 0;
SDO_C->sizeTran = 0;
SDO_C->finished = false;
CO_fifo_reset(&SDO_C->bufFifo);
SDO_C->SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 1000U;
SDO_C->timeoutTimer = 0;
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
SDO_C->block_SDOtimeoutTime_us = (uint32_t)SDOtimeoutTime_ms * 700U;
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0
/* if node-ID of the SDO server is the same as node-ID of this node, then transfer data within this node */
if (((SDO_C->OD != NULL) && (SDO_C->nodeId != 0U)) && (SDO_C->nodeIDOfTheSDOServer == SDO_C->nodeId)) {
SDO_C->OD_IO.read = NULL;
SDO_C->state = CO_SDO_ST_UPLOAD_LOCAL_TRANSFER;
} else
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
if (blockEnable) {
SDO_C->state = CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ;
} else
#endif
{
SDO_C->state = CO_SDO_ST_UPLOAD_INITIATE_REQ;
}
CO_FLAG_CLEAR(SDO_C->CANrxNew);
return CO_SDO_RT_ok_communicationEnd;
}
CO_SDO_return_t
CO_SDOclientUpload(CO_SDOclient_t* SDO_C, uint32_t timeDifference_us, bool_t send_abort,
CO_SDO_abortCode_t* SDOabortCode, size_t* sizeIndicated, size_t* sizeTransferred,
uint32_t* timerNext_us) {
(void)timerNext_us; /* may be unused */
CO_SDO_return_t ret = CO_SDO_RT_waitingResponse;
CO_SDO_abortCode_t abortCode = CO_SDO_AB_NONE;
if ((SDO_C == NULL) || !SDO_C->valid) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_wrongArguments;
} else if (SDO_C->state == CO_SDO_ST_IDLE) {
ret = CO_SDO_RT_ok_communicationEnd;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_LOCAL) != 0
/* Transfer data locally */
else if ((SDO_C->state == CO_SDO_ST_UPLOAD_LOCAL_TRANSFER) && !send_abort) {
/* search object dictionary in first pass */
if (SDO_C->OD_IO.read == NULL) {
ODR_t odRet;
odRet = OD_getSub(OD_find(SDO_C->OD, SDO_C->index), SDO_C->subIndex, &SDO_C->OD_IO, false);
if (odRet != ODR_OK) {
abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet);
ret = CO_SDO_RT_endedWithClientAbort;
} else if ((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_RW) == 0U) {
abortCode = CO_SDO_AB_UNSUPPORTED_ACCESS;
ret = CO_SDO_RT_endedWithClientAbort;
} else if ((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_SDO_R) == 0U) {
abortCode = CO_SDO_AB_WRITEONLY;
ret = CO_SDO_RT_endedWithClientAbort;
} else if (SDO_C->OD_IO.read == NULL) {
abortCode = CO_SDO_AB_DEVICE_INCOMPAT;
ret = CO_SDO_RT_endedWithClientAbort;
} else { /* MISRA C 2004 14.10 */
}
}
size_t countFifo = CO_fifo_getSpace(&SDO_C->bufFifo);
/* skip copying if buffer full */
if (countFifo == 0U) {
ret = CO_SDO_RT_uploadDataBufferFull;
}
/* read data, in several passes if necessary */
else if (SDO_C->OD_IO.read != NULL) {
/* Get size of data in Object Dictionary. If size is not indicated
* use maximum SDO client buffer size. Prepare temp buffer. */
OD_size_t countData = SDO_C->OD_IO.stream.dataLength;
OD_size_t countBuf = ((countData > 0U) && (countData <= countFifo)) ? countData : (OD_size_t)countFifo;
OD_size_t countRd = 0;
uint8_t buf[CO_CONFIG_SDO_CLI_BUFFER_SIZE + 1U];
ODR_t odRet;
/* load data from OD variable into the buffer */
CO_LOCK_OD(SDO_C->CANdevTx);
odRet = SDO_C->OD_IO.read(&SDO_C->OD_IO.stream, buf, countBuf, &countRd);
CO_UNLOCK_OD(SDO_C->CANdevTx);
if ((odRet != ODR_OK) && (odRet != ODR_PARTIAL)) {
abortCode = (CO_SDO_abortCode_t)OD_getSDOabCode(odRet);
ret = CO_SDO_RT_endedWithServerAbort;
} else {
/* if data is string, send only data up to null termination */
if ((countRd > 0U) && (countRd <= CO_CONFIG_SDO_CLI_BUFFER_SIZE)
&& ((SDO_C->OD_IO.stream.attribute & (OD_attr_t)ODA_STR) != 0U)) {
buf[countRd] = 0; /* (buf is one byte larger) */
OD_size_t countStr = (OD_size_t)strlen((char*)buf);
if (countStr == 0U) {
countStr = 1; /* no zero length */
}
if (countStr < countRd) {
/* string terminator found, finish read, shorten data */
countRd = countStr;
odRet = ODR_OK;
SDO_C->OD_IO.stream.dataLength = (OD_size_t)SDO_C->sizeTran + countRd;
}
}
(void)CO_fifo_write(&SDO_C->bufFifo, buf, countRd, NULL);
SDO_C->sizeTran += countRd;
/* verify if size of data uploaded is too large */
SDO_C->sizeInd = SDO_C->OD_IO.stream.dataLength;
if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) {
abortCode = CO_SDO_AB_DATA_LONG;
ret = CO_SDO_RT_endedWithClientAbort;
}
/* If no more segments to be upload, finish */
else if (odRet == ODR_OK) {
/* verify size of data uploaded */
if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran < SDO_C->sizeInd)) {
abortCode = CO_SDO_AB_DATA_SHORT;
ret = CO_SDO_RT_endedWithClientAbort;
} else {
ret = CO_SDO_RT_ok_communicationEnd;
}
} else {
ret = CO_SDO_RT_waitingLocalTransfer;
}
}
} else { /* MISRA C 2004 14.10 */
}
if ((ret != CO_SDO_RT_uploadDataBufferFull) && (ret != CO_SDO_RT_waitingLocalTransfer)) {
SDO_C->state = CO_SDO_ST_IDLE;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0
/* Inform OS to call this function again without delay. */
else if (timerNext_us != NULL) {
*timerNext_us = 0;
} else { /* MISRA C 2004 14.10 */
}
#endif
}
#endif /* CO_CONFIG_SDO_CLI_LOCAL */
/* CAN data received */
else if (CO_FLAG_READ(SDO_C->CANrxNew)) {
/* is SDO abort */
if (SDO_C->CANrxData[0] == 0x80U) {
uint32_t code;
(void)memcpy((void*)(&code), (const void*)(&SDO_C->CANrxData[4]), sizeof(code));
abortCode = (CO_SDO_abortCode_t)CO_SWAP_32(code);
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_endedWithServerAbort;
} else if (send_abort) {
abortCode = (SDOabortCode != NULL) ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT;
SDO_C->state = CO_SDO_ST_ABORT;
} else {
switch (SDO_C->state) {
case CO_SDO_ST_UPLOAD_INITIATE_RSP: {
if ((SDO_C->CANrxData[0] & 0xF0U) == 0x40U) {
/* verify index and subindex */
uint16_t index;
uint8_t subindex;
index = ((uint16_t)SDO_C->CANrxData[2]) << 8;
index |= SDO_C->CANrxData[1];
subindex = SDO_C->CANrxData[3];
if ((index != SDO_C->index) || (subindex != SDO_C->subIndex)) {
abortCode = CO_SDO_AB_PRAM_INCOMPAT;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
if ((SDO_C->CANrxData[0] & 0x02U) != 0U) {
/* Expedited transfer */
size_t count = 4;
/* is size indicated? */
if ((SDO_C->CANrxData[0] & 0x01U) != 0U) {
count -= (((size_t)SDO_C->CANrxData[0]) >> 2) & 0x03U;
}
/* copy data, indicate size and finish */
(void)CO_fifo_write(&SDO_C->bufFifo, &SDO_C->CANrxData[4], count, NULL);
SDO_C->sizeTran = count;
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_ok_communicationEnd;
} else {
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0
/* segmented transfer, is size indicated? */
if ((SDO_C->CANrxData[0] & 0x01U) != 0U) {
uint32_t size;
(void)memcpy((void*)(&size), (void*)(&SDO_C->CANrxData[4]), sizeof(size));
SDO_C->sizeInd = CO_SWAP_32(size);
}
SDO_C->toggle = 0x00;
SDO_C->state = CO_SDO_ST_UPLOAD_SEGMENT_REQ;
#else
abortCode = CO_SDO_AB_UNSUPPORTED_ACCESS;
SDO_C->state = CO_SDO_ST_ABORT;
#endif
}
} else {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
}
break;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0
case CO_SDO_ST_UPLOAD_SEGMENT_RSP: {
if ((SDO_C->CANrxData[0] & 0xE0U) == 0x00U) {
size_t count, countWr;
/* verify and alternate toggle bit */
uint8_t toggle = SDO_C->CANrxData[0] & 0x10U;
if (toggle != SDO_C->toggle) {
abortCode = CO_SDO_AB_TOGGLE_BIT;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
SDO_C->toggle = (toggle == 0x00U) ? 0x10U : 0x00U;
/* get data size and write data to the buffer */
count = (size_t)(7U) - (((size_t)(SDO_C->CANrxData[0]) >> 1) & 0x07U);
countWr = CO_fifo_write(&SDO_C->bufFifo, &SDO_C->CANrxData[1], count, NULL);
SDO_C->sizeTran += countWr;
/* verify, if there was not enough space in fifo buffer */
if (countWr != count) {
abortCode = CO_SDO_AB_OUT_OF_MEM;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
/* verify if size of data uploaded is too large */
if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) {
abortCode = CO_SDO_AB_DATA_LONG;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
/* If no more segments to be upload, finish */
if ((SDO_C->CANrxData[0] & 0x01U) != 0U) {
/* verify size of data uploaded */
if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran < SDO_C->sizeInd)) {
abortCode = CO_SDO_AB_DATA_SHORT;
SDO_C->state = CO_SDO_ST_ABORT;
} else {
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_ok_communicationEnd;
}
} else {
SDO_C->state = CO_SDO_ST_UPLOAD_SEGMENT_REQ;
}
} else {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
}
break;
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_SEGMENTED */
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP: {
if ((SDO_C->CANrxData[0] & 0xF9U) == 0xC0U) {
uint16_t index;
uint8_t subindex;
/* get server CRC support info and data size */
if ((SDO_C->CANrxData[0] & 0x04U) != 0U) {
SDO_C->block_crcEnabled = true;
} else {
SDO_C->block_crcEnabled = false;
}
if ((SDO_C->CANrxData[0] & 0x02U) != 0U) {
uint32_t size;
(void)memcpy((void*)(&size), (const void*)(&SDO_C->CANrxData[4]), sizeof(size));
SDO_C->sizeInd = CO_SWAP_32(size);
}
/* verify index and subindex */
index = ((uint16_t)SDO_C->CANrxData[2]) << 8;
index |= SDO_C->CANrxData[1];
subindex = SDO_C->CANrxData[3];
if ((index != SDO_C->index) || (subindex != SDO_C->subIndex)) {
abortCode = CO_SDO_AB_PRAM_INCOMPAT;
SDO_C->state = CO_SDO_ST_ABORT;
} else {
SDO_C->state = CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2;
}
}
/* switch to regular transfer, CO_SDO_ST_UPLOAD_INITIATE_RSP */
else if ((SDO_C->CANrxData[0] & 0xF0U) == 0x40U) {
/* verify index and subindex */
uint16_t index;
uint8_t subindex;
index = ((uint16_t)SDO_C->CANrxData[2]) << 8;
index |= SDO_C->CANrxData[1];
subindex = SDO_C->CANrxData[3];
if ((index != SDO_C->index) || (subindex != SDO_C->subIndex)) {
abortCode = CO_SDO_AB_PRAM_INCOMPAT;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
if ((SDO_C->CANrxData[0] & 0x02U) != 0U) {
/* Expedited transfer */
size_t count = 4;
/* is size indicated? */
if ((SDO_C->CANrxData[0] & 0x01U) != 0U) {
count -= ((size_t)(SDO_C->CANrxData[0]) >> 2) & 0x03U;
}
/* copy data, indicate size and finish */
(void)CO_fifo_write(&SDO_C->bufFifo, &SDO_C->CANrxData[4], count, NULL);
SDO_C->sizeTran = count;
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_ok_communicationEnd;
} else {
/* segmented transfer, is size indicated? */
if ((SDO_C->CANrxData[0] & 0x01U) != 0U) {
uint32_t size;
(void)memcpy((void*)(&size), (const void*)(&SDO_C->CANrxData[4]), sizeof(size));
SDO_C->sizeInd = CO_SWAP_32(size);
}
SDO_C->toggle = 0x00;
SDO_C->state = CO_SDO_ST_UPLOAD_SEGMENT_REQ;
}
} else {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
}
break;
}
case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ: {
/* data are copied directly in the receive function */
break;
}
case CO_SDO_ST_UPLOAD_BLK_END_SREQ: {
if ((SDO_C->CANrxData[0] & 0xE3U) == 0xC1U) {
/* Get number of data bytes in last segment, that do not
* contain data. Then copy remaining data into fifo */
uint8_t noData = ((SDO_C->CANrxData[0] >> 2) & 0x07U);
(void)CO_fifo_write(&SDO_C->bufFifo, &SDO_C->block_dataUploadLast[0], (size_t)(7U) - noData,
&SDO_C->block_crc);
SDO_C->sizeTran += (size_t)(7U) - noData;
/* verify length */
if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran != SDO_C->sizeInd)) {
abortCode = (SDO_C->sizeTran > SDO_C->sizeInd) ? CO_SDO_AB_DATA_LONG : CO_SDO_AB_DATA_SHORT;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
/* verify CRC */
if (SDO_C->block_crcEnabled) {
uint16_t crcServer;
crcServer = ((uint16_t)SDO_C->CANrxData[2]) << 8;
crcServer |= SDO_C->CANrxData[1];
if (crcServer != SDO_C->block_crc) {
abortCode = CO_SDO_AB_CRC;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
}
SDO_C->state = CO_SDO_ST_UPLOAD_BLK_END_CRSP;
} else {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
}
break;
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) == 0
case CO_SDO_ST_UPLOAD_SEGMENT_RSP:
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) == 0
case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP:
case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ:
case CO_SDO_ST_UPLOAD_BLK_END_SREQ:
#endif
case CO_SDO_ST_IDLE:
case CO_SDO_ST_ABORT:
case CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER:
case CO_SDO_ST_DOWNLOAD_INITIATE_REQ:
case CO_SDO_ST_DOWNLOAD_INITIATE_RSP:
case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ:
case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP:
case CO_SDO_ST_UPLOAD_LOCAL_TRANSFER:
case CO_SDO_ST_UPLOAD_INITIATE_REQ:
case CO_SDO_ST_UPLOAD_SEGMENT_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_END_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_END_RSP:
case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ:
case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2:
case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP:
case CO_SDO_ST_UPLOAD_BLK_END_CRSP:
default: {
abortCode = CO_SDO_AB_CMD;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
}
}
SDO_C->timeoutTimer = 0;
timeDifference_us = 0;
CO_FLAG_CLEAR(SDO_C->CANrxNew);
} else if (send_abort) {
abortCode = (SDOabortCode != NULL) ? *SDOabortCode : CO_SDO_AB_DEVICE_INCOMPAT;
SDO_C->state = CO_SDO_ST_ABORT;
} else { /* MISRA C 2004 14.10 */
}
/* Timeout timers and transmit bufferFull flag */
if (ret == CO_SDO_RT_waitingResponse) {
if (SDO_C->timeoutTimer < SDO_C->SDOtimeoutTime_us) {
SDO_C->timeoutTimer += timeDifference_us;
}
if (SDO_C->timeoutTimer >= SDO_C->SDOtimeoutTime_us) {
bool_t state_upload_seg_req = (SDO_C->state == CO_SDO_ST_UPLOAD_SEGMENT_REQ);
bool_t state_upload_blk_sublock_crsp = (SDO_C->state == CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP);
if (state_upload_seg_req || state_upload_blk_sublock_crsp) {
/* application didn't empty buffer */
abortCode = CO_SDO_AB_GENERAL;
} else {
abortCode = CO_SDO_AB_TIMEOUT;
}
SDO_C->state = CO_SDO_ST_ABORT;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0
else if (timerNext_us != NULL) {
/* check again after timeout time elapsed */
uint32_t diff = SDO_C->SDOtimeoutTime_us - SDO_C->timeoutTimer;
if (*timerNext_us > diff) {
*timerNext_us = diff;
}
} else { /* MISRA C 2004 14.10 */
}
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
/* Timeout for sub-block reception */
if (SDO_C->state == CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) {
if (SDO_C->block_timeoutTimer < SDO_C->block_SDOtimeoutTime_us) {
SDO_C->block_timeoutTimer += timeDifference_us;
}
if (SDO_C->block_timeoutTimer >= SDO_C->block_SDOtimeoutTime_us) {
/* SDO_C->state will change, processing will continue in this
* thread. Make memory barrier here with CO_FLAG_CLEAR() call. */
SDO_C->state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP;
CO_FLAG_CLEAR(SDO_C->CANrxNew);
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0
else if (timerNext_us != NULL) {
/* check again after timeout time elapsed */
uint32_t diff = SDO_C->block_SDOtimeoutTime_us - SDO_C->block_timeoutTimer;
if (*timerNext_us > diff) {
*timerNext_us = diff;
}
} else { /* MISRA C 2004 14.10 */
}
#endif
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */
if (SDO_C->CANtxBuff->bufferFull) {
ret = CO_SDO_RT_transmittBufferFull;
}
}
/* Transmit CAN data */
if (ret == CO_SDO_RT_waitingResponse) {
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
size_t count;
#endif
(void)memset((void*)&SDO_C->CANtxBuff->data[0], 0, 8);
switch (SDO_C->state) {
case CO_SDO_ST_UPLOAD_INITIATE_REQ: {
SDO_C->CANtxBuff->data[0] = 0x40;
SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index;
SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8);
SDO_C->CANtxBuff->data[3] = SDO_C->subIndex;
/* reset timeout timer and send message */
SDO_C->timeoutTimer = 0;
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
SDO_C->state = CO_SDO_ST_UPLOAD_INITIATE_RSP;
break;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) != 0
case CO_SDO_ST_UPLOAD_SEGMENT_REQ: {
/* verify, if there is enough space in data buffer */
if (CO_fifo_getSpace(&SDO_C->bufFifo) < 7U) {
ret = CO_SDO_RT_uploadDataBufferFull;
break;
}
SDO_C->CANtxBuff->data[0] = 0x60U | SDO_C->toggle;
/* reset timeout timer and send message */
SDO_C->timeoutTimer = 0;
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
SDO_C->state = CO_SDO_ST_UPLOAD_SEGMENT_RSP;
break;
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_SEGMENTED */
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ: {
SDO_C->CANtxBuff->data[0] = 0xA4;
SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index;
SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8);
SDO_C->CANtxBuff->data[3] = SDO_C->subIndex;
/* calculate number of block segments from free buffer space */
count = CO_fifo_getSpace(&SDO_C->bufFifo) / 7U;
if (count > 127U) {
count = 127;
} else if (count == 0U) {
abortCode = CO_SDO_AB_OUT_OF_MEM;
SDO_C->state = CO_SDO_ST_ABORT;
break;
} else { /* MISRA C 2004 14.10 */
}
SDO_C->block_blksize = (uint8_t)count;
SDO_C->CANtxBuff->data[4] = SDO_C->block_blksize;
SDO_C->CANtxBuff->data[5] = CO_CONFIG_SDO_CLI_PST;
/* reset timeout timer and send message */
SDO_C->timeoutTimer = 0;
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
SDO_C->state = CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP;
break;
}
case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2: {
SDO_C->CANtxBuff->data[0] = 0xA3;
/* reset timeout timers, seqno and send message */
SDO_C->timeoutTimer = 0;
SDO_C->block_timeoutTimer = 0;
SDO_C->block_seqno = 0;
SDO_C->block_crc = 0;
/* Block segments will be received in different thread. Make memory
* barrier here with CO_FLAG_CLEAR() call. */
SDO_C->state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ;
CO_FLAG_CLEAR(SDO_C->CANrxNew);
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
break;
}
case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP: {
SDO_C->CANtxBuff->data[0] = 0xA2;
SDO_C->CANtxBuff->data[1] = SDO_C->block_seqno;
#ifdef CO_DEBUG_SDO_CLIENT
bool_t transferShort = SDO_C->block_seqno != SDO_C->block_blksize;
uint8_t seqnoStart = SDO_C->block_seqno;
#endif
/* Is last segment? */
if (SDO_C->finished) {
SDO_C->state = CO_SDO_ST_UPLOAD_BLK_END_SREQ;
} else {
/* verify if size of data uploaded is too large */
if ((SDO_C->sizeInd > 0U) && (SDO_C->sizeTran > SDO_C->sizeInd)) {
abortCode = CO_SDO_AB_DATA_LONG;
SDO_C->state = CO_SDO_ST_ABORT;
break;
}
/* calculate number of block segments from free buffer space */
count = CO_fifo_getSpace(&SDO_C->bufFifo) / 7U;
if (count >= 127U) {
count = 127;
} else if (CO_fifo_getOccupied(&SDO_C->bufFifo) > 0U) {
/* application must empty data buffer first */
ret = CO_SDO_RT_uploadDataBufferFull;
#ifdef CO_DEBUG_SDO_CLIENT
if (transferShort) {
char msg[80];
sprintf(msg, "sub-block, uploadDataBufferFull: sequno=%02X", seqnoStart);
CO_DEBUG_SDO_CLIENT(msg);
}
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_FLAG_TIMERNEXT) != 0
/* Inform OS to call this function again without delay. */
if (timerNext_us != NULL) {
*timerNext_us = 0;
}
#endif
break;
} else { /* MISRA C 2004 14.10 */
}
SDO_C->block_blksize = (uint8_t)count;
SDO_C->block_seqno = 0;
/* Block segments will be received in different thread. Make
* memory barrier here with CO_FLAG_CLEAR() call. */
SDO_C->state = CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ;
CO_FLAG_CLEAR(SDO_C->CANrxNew);
}
SDO_C->CANtxBuff->data[2] = SDO_C->block_blksize;
/* reset block_timeoutTimer, but not SDO_C->timeoutTimer */
SDO_C->block_timeoutTimer = 0;
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
#ifdef CO_DEBUG_SDO_CLIENT
if (transferShort && !SDO_C->finished) {
char msg[80];
sprintf(msg, "sub-block restarted: sequnoPrev=%02X, blksize=%02X", seqnoStart,
SDO_C->block_blksize);
CO_DEBUG_SDO_CLIENT(msg);
}
#endif
break;
}
case CO_SDO_ST_UPLOAD_BLK_END_CRSP: {
SDO_C->CANtxBuff->data[0] = 0xA1;
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_ok_communicationEnd;
break;
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_BLOCK */
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_SEGMENTED) == 0
case CO_SDO_ST_UPLOAD_SEGMENT_REQ:
#endif
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) == 0
case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ:
case CO_SDO_ST_UPLOAD_BLK_INITIATE_REQ2:
case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_CRSP:
case CO_SDO_ST_UPLOAD_BLK_END_CRSP:
#endif
case CO_SDO_ST_IDLE:
case CO_SDO_ST_ABORT:
case CO_SDO_ST_DOWNLOAD_LOCAL_TRANSFER:
case CO_SDO_ST_DOWNLOAD_INITIATE_REQ:
case CO_SDO_ST_DOWNLOAD_INITIATE_RSP:
case CO_SDO_ST_DOWNLOAD_SEGMENT_REQ:
case CO_SDO_ST_DOWNLOAD_SEGMENT_RSP:
case CO_SDO_ST_UPLOAD_LOCAL_TRANSFER:
case CO_SDO_ST_UPLOAD_INITIATE_RSP:
case CO_SDO_ST_UPLOAD_SEGMENT_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_INITIATE_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_SUBBLOCK_RSP:
case CO_SDO_ST_DOWNLOAD_BLK_END_REQ:
case CO_SDO_ST_DOWNLOAD_BLK_END_RSP:
case CO_SDO_ST_UPLOAD_BLK_INITIATE_RSP:
case CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ:
case CO_SDO_ST_UPLOAD_BLK_END_SREQ:
default: {
/* none */
break;
}
}
}
if (ret == CO_SDO_RT_waitingResponse) {
if (SDO_C->state == CO_SDO_ST_ABORT) {
uint32_t code = CO_SWAP_32((uint32_t)abortCode);
/* Send SDO abort message */
SDO_C->CANtxBuff->data[0] = 0x80;
SDO_C->CANtxBuff->data[1] = (uint8_t)SDO_C->index;
SDO_C->CANtxBuff->data[2] = (uint8_t)(SDO_C->index >> 8);
SDO_C->CANtxBuff->data[3] = SDO_C->subIndex;
(void)memcpy((void*)(&SDO_C->CANtxBuff->data[4]), (const void*)(&code), sizeof(code));
(void)CO_CANsend(SDO_C->CANdevTx, SDO_C->CANtxBuff);
SDO_C->state = CO_SDO_ST_IDLE;
ret = CO_SDO_RT_endedWithClientAbort;
}
#if ((CO_CONFIG_SDO_CLI)&CO_CONFIG_SDO_CLI_BLOCK) != 0
else if (SDO_C->state == CO_SDO_ST_UPLOAD_BLK_SUBBLOCK_SREQ) {
ret = CO_SDO_RT_blockUploadInProgress;
} else { /* MISRA C 2004 14.10 */
}
#endif
}
if (sizeIndicated != NULL) {
*sizeIndicated = SDO_C->sizeInd;
}
if (sizeTransferred != NULL) {
*sizeTransferred = SDO_C->sizeTran;
}
if (SDOabortCode != NULL) {
*SDOabortCode = abortCode;
}
return ret;
}
size_t
CO_SDOclientUploadBufRead(CO_SDOclient_t* SDO_C, uint8_t* buf, size_t count) {
size_t ret = 0;
if ((SDO_C != NULL) && (buf != NULL)) {
ret = CO_fifo_read(&SDO_C->bufFifo, buf, count, NULL);
}
return ret;
}
void
CO_SDOclientClose(CO_SDOclient_t* SDO_C) {
if (SDO_C != NULL) {
SDO_C->state = CO_SDO_ST_IDLE;
}
}
#endif /* (CO_CONFIG_SDO_CLI) & CO_CONFIG_SDO_CLI_ENABLE */