/* * CANopen LSS Slave protocol. * * @file CO_LSSslave.c * @ingroup CO_LSS * @author Martin Wagner * @author Janez Paternoster * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH * * * This file is part of , 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 #include "305/CO_LSSslave.h" #if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE) != 0 /* 'bit' must be unsigned or additional range check must be added: bit>=CO_LSS_FASTSCAN_BIT0 */ #define CO_LSS_FASTSCAN_BITCHECK_VALID(bit) ((bit <= CO_LSS_FASTSCAN_BIT31) || (bit == CO_LSS_FASTSCAN_CONFIRM)) /* 'index' must be unsigned or additional range check must be added: index>=CO_LSS_FASTSCAN_VENDOR_ID */ #define CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(index) (index <= CO_LSS_FASTSCAN_SERIAL) /* 'index' must be unsigned or additional range check must be added: index>=CO_LSS_BIT_TIMING_1000 */ #define CO_LSS_BIT_TIMING_VALID(index) ((index != 5U) && (index <= CO_LSS_BIT_TIMING_AUTO)) /* * 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_LSSslave_receive(void* object, void* msg) { CO_LSSslave_t* LSSslave = (CO_LSSslave_t*)object; uint8_t DLC = CO_CANrxMsg_readDLC(msg); if ((DLC == 8U) && !CO_FLAG_READ(LSSslave->sendResponse)) { bool_t request_LSSslave_process = false; const uint8_t* data = CO_CANrxMsg_readData(msg); uint8_t cs = data[0]; if (cs == CO_LSS_SWITCH_STATE_GLOBAL) { uint8_t mode = data[1]; switch (mode) { case CO_LSS_STATE_WAITING: if ((LSSslave->lssState == CO_LSS_STATE_CONFIGURATION) && (LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT) && (*LSSslave->pendingNodeID != CO_LSS_NODE_ID_ASSIGNMENT)) { /* Slave process function will request NMT Reset comm. */ LSSslave->service = cs; request_LSSslave_process = true; } LSSslave->lssState = CO_LSS_STATE_WAITING; (void)memset(&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect)); break; case CO_LSS_STATE_CONFIGURATION: LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; break; default: /* none */ break; } } else if (LSSslave->lssState == CO_LSS_STATE_WAITING) { switch (cs) { case CO_LSS_SWITCH_STATE_SEL_VENDOR: { uint32_t valSw; (void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw)); LSSslave->lssSelect.identity.vendorID = CO_SWAP_32(valSw); break; } case CO_LSS_SWITCH_STATE_SEL_PRODUCT: { uint32_t valSw; (void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw)); LSSslave->lssSelect.identity.productCode = CO_SWAP_32(valSw); break; } case CO_LSS_SWITCH_STATE_SEL_REV: { uint32_t valSw; (void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw)); LSSslave->lssSelect.identity.revisionNumber = CO_SWAP_32(valSw); break; } case CO_LSS_SWITCH_STATE_SEL_SERIAL: { uint32_t valSw; (void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw)); LSSslave->lssSelect.identity.serialNumber = CO_SWAP_32(valSw); if (CO_LSS_ADDRESS_EQUAL(LSSslave->lssAddress, LSSslave->lssSelect)) { LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; LSSslave->service = cs; request_LSSslave_process = true; } break; } case CO_LSS_IDENT_FASTSCAN: { /* fastscan is only active on unconfigured nodes */ if ((*LSSslave->pendingNodeID == CO_LSS_NODE_ID_ASSIGNMENT) && (LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT)) { uint8_t bitCheck = data[5]; uint8_t lssSub = data[6]; uint8_t lssNext = data[7]; uint32_t valSw; uint32_t idNumber; bool_t ack; if (!CO_LSS_FASTSCAN_BITCHECK_VALID(bitCheck) || !CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssSub) || !CO_LSS_FASTSCAN_LSS_SUB_NEXT_VALID(lssNext)) { /* Invalid request */ break; } (void)memcpy((void*)(&valSw), (const void*)(&data[1]), sizeof(valSw)); idNumber = CO_SWAP_32(valSw); ack = false; if (bitCheck == CO_LSS_FASTSCAN_CONFIRM) { /* Confirm, Reset */ ack = true; LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID; (void)memset(&LSSslave->lssFastscan, 0, sizeof(LSSslave->lssFastscan)); } else if (LSSslave->fastscanPos == lssSub) { uint32_t mask = 0xFFFFFFFFU << bitCheck; if ((LSSslave->lssAddress.addr[lssSub] & mask) == (idNumber & mask)) { /* all requested bits match */ ack = true; LSSslave->fastscanPos = lssNext; if ((bitCheck == 0U) && (lssNext < lssSub)) { /* complete match, enter configuration state */ LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; } } } else { /* MISRA C 2004 14.10 */ } if (ack) { #if ((CO_CONFIG_LSS)&CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND) != 0 LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE; (void)memset(&LSSslave->TXbuff->data[1], 0, sizeof(LSSslave->TXbuff->data) - 1U); (void)CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); #else LSSslave->service = cs; request_LSSslave_process = true; #endif } } break; } default: { /* none */ break; } } } else { /* LSSslave->lssState == CO_LSS_STATE_CONFIGURATION */ (void)memcpy((void*)(&LSSslave->CANdata[0]), (const void*)(&data[0]), sizeof(LSSslave->CANdata)); LSSslave->service = cs; request_LSSslave_process = true; } if (request_LSSslave_process) { CO_FLAG_SET(LSSslave->sendResponse); #if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 /* Optional signal to RTOS, which can resume task, which handles further processing. */ if (LSSslave->pFunctSignalPre != NULL) { LSSslave->pFunctSignalPre(LSSslave->functSignalObjectPre); } #endif } } } CO_ReturnError_t CO_LSSslave_init(CO_LSSslave_t* LSSslave, CO_LSS_address_t* lssAddress, uint16_t* pendingBitRate, uint8_t* pendingNodeID, CO_CANmodule_t* CANdevRx, uint16_t CANdevRxIdx, uint16_t CANidLssMaster, CO_CANmodule_t* CANdevTx, uint16_t CANdevTxIdx, uint16_t CANidLssSlave) { CO_ReturnError_t ret = CO_ERROR_NO; /* verify arguments */ if ((LSSslave == NULL) || (pendingBitRate == NULL) || (pendingNodeID == NULL) || (CANdevRx == NULL) || (CANdevTx == NULL) || !CO_LSS_NODE_ID_VALID(*pendingNodeID)) { return CO_ERROR_ILLEGAL_ARGUMENT; } /* Application must make sure that lssAddress is filled with data. */ /* clear the object */ (void)memset(LSSslave, 0, sizeof(CO_LSSslave_t)); /* Configure object variables */ (void)memcpy(&LSSslave->lssAddress, lssAddress, sizeof(LSSslave->lssAddress)); LSSslave->lssState = CO_LSS_STATE_WAITING; LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID; LSSslave->pendingBitRate = pendingBitRate; LSSslave->pendingNodeID = pendingNodeID; LSSslave->activeNodeID = *pendingNodeID; CO_FLAG_CLEAR(LSSslave->sendResponse); /* configure LSS CAN Master message reception */ ret = CO_CANrxBufferInit(CANdevRx, CANdevRxIdx, CANidLssMaster, 0x7FF, false, (void*)LSSslave, CO_LSSslave_receive); /* configure LSS CAN Slave response message transmission */ LSSslave->CANdevTx = CANdevTx; LSSslave->TXbuff = CO_CANtxBufferInit(CANdevTx, CANdevTxIdx, CANidLssSlave, false, 8, false); if (LSSslave->TXbuff == NULL) { ret = CO_ERROR_ILLEGAL_ARGUMENT; } return ret; } #if ((CO_CONFIG_LSS)&CO_CONFIG_FLAG_CALLBACK_PRE) != 0 void CO_LSSslave_initCallbackPre(CO_LSSslave_t* LSSslave, void* object, void (*pFunctSignalPre)(void* object)) { if (LSSslave != NULL) { LSSslave->functSignalObjectPre = object; LSSslave->pFunctSignalPre = pFunctSignalPre; } } #endif void CO_LSSslave_initCkBitRateCall(CO_LSSslave_t* LSSslave, void* object, bool_t (*pFunctLSScheckBitRate)(void* object, uint16_t bitRate)) { if (LSSslave != NULL) { LSSslave->functLSScheckBitRateObject = object; LSSslave->pFunctLSScheckBitRate = pFunctLSScheckBitRate; } } void CO_LSSslave_initActBitRateCall(CO_LSSslave_t* LSSslave, void* object, void (*pFunctLSSactivateBitRate)(void* object, uint16_t delay)) { if (LSSslave != NULL) { LSSslave->functLSSactivateBitRateObject = object; LSSslave->pFunctLSSactivateBitRate = pFunctLSSactivateBitRate; } } void CO_LSSslave_initCfgStoreCall(CO_LSSslave_t* LSSslave, void* object, bool_t (*pFunctLSScfgStore)(void* object, uint8_t id, uint16_t bitRate)) { if (LSSslave != NULL) { LSSslave->functLSScfgStoreObject = object; LSSslave->pFunctLSScfgStore = pFunctLSScfgStore; } } bool_t CO_LSSslave_process(CO_LSSslave_t* LSSslave) { bool_t resetCommunication = false; if (CO_FLAG_READ(LSSslave->sendResponse)) { uint8_t nid; uint8_t errorCode; uint8_t errorCodeManuf; uint8_t tableSelector; uint8_t tableIndex; bool_t CANsend = false; uint32_t valSw; (void)memset(&LSSslave->TXbuff->data[0], 0, sizeof(LSSslave->TXbuff->data)); switch (LSSslave->service) { case CO_LSS_SWITCH_STATE_GLOBAL: { /* Node-Id was unconfigured before, now it is configured, * enter the NMT Reset communication autonomously. */ resetCommunication = true; break; } case CO_LSS_SWITCH_STATE_SEL_SERIAL: { LSSslave->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL; CANsend = true; break; } case CO_LSS_CFG_NODE_ID: { nid = LSSslave->CANdata[1]; errorCode = CO_LSS_CFG_NODE_ID_OK; if (CO_LSS_NODE_ID_VALID(nid)) { *LSSslave->pendingNodeID = nid; } else { errorCode = CO_LSS_CFG_NODE_ID_OUT_OF_RANGE; } /* send confirmation */ LSSslave->TXbuff->data[0] = LSSslave->service; LSSslave->TXbuff->data[1] = errorCode; /* we do not use spec-error, always 0 */ CANsend = true; break; } case CO_LSS_CFG_BIT_TIMING: { if (LSSslave->pFunctLSScheckBitRate == NULL) { /* setting bit timing is not supported. Drop request */ break; } tableSelector = LSSslave->CANdata[1]; tableIndex = LSSslave->CANdata[2]; errorCode = CO_LSS_CFG_BIT_TIMING_OK; errorCodeManuf = CO_LSS_CFG_BIT_TIMING_OK; if ((tableSelector == 0U) && CO_LSS_BIT_TIMING_VALID(tableIndex)) { uint16_t bit = CO_LSS_bitTimingTableLookup[tableIndex]; bool_t bit_rate_supported = LSSslave->pFunctLSScheckBitRate(LSSslave->functLSScheckBitRateObject, bit); if (bit_rate_supported) { *LSSslave->pendingBitRate = bit; } else { errorCode = CO_LSS_CFG_BIT_TIMING_MANUFACTURER; errorCodeManuf = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE; } } else { /* we currently only support CiA301 bit timing table */ errorCode = CO_LSS_CFG_BIT_TIMING_OUT_OF_RANGE; } /* send confirmation */ LSSslave->TXbuff->data[0] = LSSslave->service; LSSslave->TXbuff->data[1] = errorCode; LSSslave->TXbuff->data[2] = errorCodeManuf; CANsend = true; break; } case CO_LSS_CFG_ACTIVATE_BIT_TIMING: { if (LSSslave->pFunctLSScheckBitRate == NULL) { /* setting bit timing is not supported. Drop request */ break; } /* notify application */ if (LSSslave->pFunctLSSactivateBitRate != NULL) { uint16_t delay = ((uint16_t)LSSslave->CANdata[2]) << 8; delay |= LSSslave->CANdata[1]; LSSslave->pFunctLSSactivateBitRate(LSSslave->functLSSactivateBitRateObject, delay); } break; } case CO_LSS_CFG_STORE: { errorCode = CO_LSS_CFG_STORE_OK; if (LSSslave->pFunctLSScfgStore == NULL) { /* storing is not supported. Reply error */ errorCode = CO_LSS_CFG_STORE_NOT_SUPPORTED; } else { bool_t result; /* Store "pending" to "persistent" */ result = LSSslave->pFunctLSScfgStore(LSSslave->functLSScfgStoreObject, *LSSslave->pendingNodeID, *LSSslave->pendingBitRate); if (!result) { errorCode = CO_LSS_CFG_STORE_FAILED; } } /* send confirmation */ LSSslave->TXbuff->data[0] = LSSslave->service; LSSslave->TXbuff->data[1] = errorCode; /* we do not use spec-error, always 0 */ CANsend = true; break; } case CO_LSS_INQUIRE_VENDOR: { LSSslave->TXbuff->data[0] = LSSslave->service; valSw = CO_SWAP_32(LSSslave->lssAddress.identity.vendorID); (void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw)); CANsend = true; break; } case CO_LSS_INQUIRE_PRODUCT: { LSSslave->TXbuff->data[0] = LSSslave->service; valSw = CO_SWAP_32(LSSslave->lssAddress.identity.productCode); (void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw)); CANsend = true; break; } case CO_LSS_INQUIRE_REV: { LSSslave->TXbuff->data[0] = LSSslave->service; valSw = CO_SWAP_32(LSSslave->lssAddress.identity.revisionNumber); (void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw)); CANsend = true; break; } case CO_LSS_INQUIRE_SERIAL: { LSSslave->TXbuff->data[0] = LSSslave->service; valSw = CO_SWAP_32(LSSslave->lssAddress.identity.serialNumber); (void)memcpy((void*)(&LSSslave->TXbuff->data[1]), (const void*)(&valSw), sizeof(valSw)); CANsend = true; break; } case CO_LSS_INQUIRE_NODE_ID: { LSSslave->TXbuff->data[0] = LSSslave->service; LSSslave->TXbuff->data[1] = LSSslave->activeNodeID; CANsend = true; break; } case CO_LSS_IDENT_FASTSCAN: { LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE; CANsend = true; break; } default: { /* none */ break; } } if (CANsend) { (void)CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); } CO_FLAG_CLEAR(LSSslave->sendResponse); } return resetCommunication; } #endif /* (CO_CONFIG_LSS) & CO_CONFIG_LSS_SLAVE */