/* * CANopen LSS Slave protocol. * * @file CO_LSSslave.c * @ingroup CO_LSS * @author Martin Wagner * @copyright 2017 - 2020 Neuberger Gebaeudeautomation GmbH * * * This file is part of CANopenNode, an opensource CANopen Stack. * Project home page is . * For more information on CANopen see . * * 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 "CANopen.h" #include "CO_LSSslave.h" #if CO_NO_LSS_SERVER == 1 /* * Helper function - Handle service "switch state global" */ static void CO_LSSslave_serviceSwitchStateGlobal( CO_LSSslave_t *LSSslave, CO_LSS_cs_t service, const CO_CANrxMsg_t *msg) { uint8_t mode = msg->data[1]; switch (mode) { case CO_LSS_STATE_WAITING: LSSslave->lssState = CO_LSS_STATE_WAITING; CO_memset((uint8_t*)&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect)); break; case CO_LSS_STATE_CONFIGURATION: LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; break; default: break; } } /* * Helper function - Handle service "switch state selective" */ static void CO_LSSslave_serviceSwitchStateSelective( CO_LSSslave_t *LSSslave, CO_LSS_cs_t service, const CO_CANrxMsg_t *msg) { uint32_t value; CO_memcpySwap4(&value, &msg->data[1]); if(LSSslave->lssState != CO_LSS_STATE_WAITING) { return; } switch (service) { case CO_LSS_SWITCH_STATE_SEL_VENDOR: LSSslave->lssSelect.identity.vendorID = value; break; case CO_LSS_SWITCH_STATE_SEL_PRODUCT: LSSslave->lssSelect.identity.productCode = value; break; case CO_LSS_SWITCH_STATE_SEL_REV: LSSslave->lssSelect.identity.revisionNumber = value; break; case CO_LSS_SWITCH_STATE_SEL_SERIAL: LSSslave->lssSelect.identity.serialNumber = value; if (CO_LSS_ADDRESS_EQUAL(LSSslave->lssAddress, LSSslave->lssSelect)) { LSSslave->lssState = CO_LSS_STATE_CONFIGURATION; /* send confirmation */ LSSslave->TXbuff->data[0] = CO_LSS_SWITCH_STATE_SEL; CO_memset(&LSSslave->TXbuff->data[1], 0, 7); CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); } break; default: break; } } /* * Helper function - Handle service "configure" * * values inside message have different meaning, depending on the selected * configuration type */ static void CO_LSSslave_serviceConfig( CO_LSSslave_t *LSSslave, CO_LSS_cs_t service, const CO_CANrxMsg_t *msg) { uint8_t nid; uint8_t tableSelector; uint8_t tableIndex; uint8_t errorCode; if(LSSslave->lssState != CO_LSS_STATE_CONFIGURATION) { return; } switch (service) { case CO_LSS_CFG_NODE_ID: nid = msg->data[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] = CO_LSS_CFG_NODE_ID; LSSslave->TXbuff->data[1] = errorCode; /* we do not use spec-error, always 0 */ CO_memset(&LSSslave->TXbuff->data[2], 0, 6); CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); break; case CO_LSS_CFG_BIT_TIMING: if (LSSslave->pFunctLSScheckBitRate == NULL) { /* setting bit timing is not supported. Drop request */ break; } tableSelector = msg->data[1]; tableIndex = msg->data[2]; errorCode = CO_LSS_CFG_BIT_TIMING_OK; if (tableSelector==0 && 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_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] = CO_LSS_CFG_BIT_TIMING; LSSslave->TXbuff->data[1] = errorCode; /* we do not use spec-error, always 0 */ CO_memset(&LSSslave->TXbuff->data[2], 0, 6); CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); 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; CO_memcpySwap2(&delay, &msg->data[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->functLSScfgStore, LSSslave->pendingNodeID, LSSslave->pendingBitRate); if (!result) { errorCode = CO_LSS_CFG_STORE_FAILED; } } /* send confirmation */ LSSslave->TXbuff->data[0] = CO_LSS_CFG_STORE; LSSslave->TXbuff->data[1] = errorCode; /* we do not use spec-error, always 0 */ CO_memset(&LSSslave->TXbuff->data[2], 0, 6); CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); break; default: break; } } /* * Helper function - Handle service "inquire" */ static void CO_LSSslave_serviceInquire( CO_LSSslave_t *LSSslave, CO_LSS_cs_t service, const CO_CANrxMsg_t *msg) { uint32_t value; if(LSSslave->lssState != CO_LSS_STATE_CONFIGURATION) { return; } switch (service) { case CO_LSS_INQUIRE_VENDOR: value = LSSslave->lssAddress.identity.vendorID; break; case CO_LSS_INQUIRE_PRODUCT: value = LSSslave->lssAddress.identity.productCode; break; case CO_LSS_INQUIRE_REV: value = LSSslave->lssAddress.identity.revisionNumber; break; case CO_LSS_INQUIRE_SERIAL: value = LSSslave->lssAddress.identity.serialNumber; break; case CO_LSS_INQUIRE_NODE_ID: value = (uint32_t)LSSslave->activeNodeID; break; default: return; } /* send response */ LSSslave->TXbuff->data[0] = service; CO_memcpySwap4(&LSSslave->TXbuff->data[1], &value); CO_memset(&LSSslave->TXbuff->data[5], 0, 4); CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); } /* * Helper function - Handle service "identify" */ static void CO_LSSslave_serviceIdent( CO_LSSslave_t *LSSslave, CO_LSS_cs_t service, const CO_CANrxMsg_t *msg) { uint32_t idNumber; uint8_t bitCheck; uint8_t lssSub; uint8_t lssNext; bool_t ack; if (LSSslave->lssState != CO_LSS_STATE_WAITING) { /* fastscan is only allowed in waiting state */ return; } if (service != CO_LSS_IDENT_FASTSCAN) { /* we only support "fastscan" identification */ return; } if (LSSslave->pendingNodeID!=CO_LSS_NODE_ID_ASSIGNMENT || LSSslave->activeNodeID!=CO_LSS_NODE_ID_ASSIGNMENT) { /* fastscan is only active on unconfigured nodes */ return; } CO_memcpySwap4(&idNumber, &msg->data[1]); bitCheck = msg->data[5]; lssSub = msg->data[6]; lssNext = msg->data[7]; 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 */ return; } ack = false; if (bitCheck == CO_LSS_FASTSCAN_CONFIRM) { /* Confirm, Reset */ ack = true; LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID; CO_memset((uint8_t*)&LSSslave->lssFastscan, 0, sizeof(LSSslave->lssFastscan)); } else if (LSSslave->fastscanPos == lssSub) { uint32_t mask = 0xFFFFFFFF << bitCheck; if ((LSSslave->lssAddress.addr[lssSub] & mask) == (idNumber & mask)) { /* all requested bits match */ ack = true; LSSslave->fastscanPos = lssNext; if (bitCheck==0 && lssNextlssState = CO_LSS_STATE_CONFIGURATION; } } } if (ack) { LSSslave->TXbuff->data[0] = CO_LSS_IDENT_SLAVE; CO_memset(&LSSslave->TXbuff->data[1], 0, 7); CO_CANsend(LSSslave->CANdevTx, LSSslave->TXbuff); } } /* * 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, const CO_CANrxMsg_t *msg) { CO_LSSslave_t *LSSslave; LSSslave = (CO_LSSslave_t*)object; /* this is the correct pointer type of the first argument */ if(msg->DLC == 8){ CO_LSS_cs_t cs = msg->data[0]; if (CO_LSS_CS_SERVICE_IS_SWITCH_GLOBAL(cs)) { CO_LSSslave_serviceSwitchStateGlobal(LSSslave, cs, msg); } else if (CO_LSS_CS_SERVICE_IS_SWITCH_STATE_SELECTIVE(cs)) { CO_LSSslave_serviceSwitchStateSelective(LSSslave, cs, msg); } else if (CO_LSS_CS_SERVICE_IS_CONFIG(cs)) { CO_LSSslave_serviceConfig(LSSslave, cs, msg); } else if (CO_LSS_CS_SERVICE_IS_INQUIRE(cs)) { CO_LSSslave_serviceInquire(LSSslave, cs, msg); } else if (CO_LSS_CS_SERVICE_IS_IDENT(cs)) { CO_LSSslave_serviceIdent(LSSslave, cs, msg); } else { /* No Ack -> Unsupported commands are dropped */ } } } /******************************************************************************/ 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, uint32_t CANidLssMaster, CO_CANmodule_t *CANdevTx, uint16_t CANdevTxIdx, uint32_t CANidLssSlave) { /* verify arguments */ if (LSSslave==NULL || CANdevRx==NULL || CANdevTx==NULL || !CO_LSS_NODE_ID_VALID(pendingNodeID)) { return CO_ERROR_ILLEGAL_ARGUMENT; } /* check LSS address for plausibility. As a bare minimum, the vendor * ID and serial number must be set */ if (lssAddress.identity.vendorID==0 || lssAddress.identity.serialNumber==0) { return CO_ERROR_ILLEGAL_ARGUMENT; } CO_memcpy((uint8_t*)&LSSslave->lssAddress, (uint8_t*)&lssAddress, sizeof(LSSslave->lssAddress)); LSSslave->lssState = CO_LSS_STATE_WAITING; CO_memset((uint8_t*)&LSSslave->lssSelect, 0, sizeof(LSSslave->lssSelect)); CO_memset((uint8_t*)&LSSslave->lssFastscan, 0, sizeof(LSSslave->lssFastscan)); LSSslave->fastscanPos = CO_LSS_FASTSCAN_VENDOR_ID; LSSslave->pendingBitRate = pendingBitRate; LSSslave->pendingNodeID = pendingNodeID; LSSslave->activeNodeID = CO_LSS_NODE_ID_ASSIGNMENT; LSSslave->pFunctLSScheckBitRate = NULL; LSSslave->functLSScheckBitRateObject = NULL; LSSslave->pFunctLSSactivateBitRate = NULL; LSSslave->functLSSactivateBitRateObject = NULL; LSSslave->pFunctLSScfgStore = NULL; LSSslave->functLSScfgStore = NULL; /* configure LSS CAN Master message reception */ CO_CANrxBufferInit( CANdevRx, /* CAN device */ CANdevRxIdx, /* rx buffer index */ CANidLssMaster, /* CAN identifier */ 0x7FF, /* mask */ 0, /* rtr */ (void*)LSSslave, /* object passed to receive function */ CO_LSSslave_receive); /* this function will process received message */ /* configure LSS CAN Slave response message transmission */ LSSslave->CANdevTx = CANdevTx; LSSslave->TXbuff = CO_CANtxBufferInit( CANdevTx, /* CAN device */ CANdevTxIdx, /* index of specific buffer inside CAN module */ CANidLssSlave, /* CAN identifier */ 0, /* rtr */ 8, /* number of data bytes */ 0); /* synchronous message flag bit */ return CO_ERROR_NO; } /******************************************************************************/ void CO_LSSslave_initCheckBitRateCallback( 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_initActivateBitRateCallback( 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_initCfgStoreCallback( CO_LSSslave_t *LSSslave, void *object, bool_t (*pFunctLSScfgStore)(void *object, uint8_t id, uint16_t bitRate)) { if(LSSslave != NULL){ LSSslave->functLSScfgStore = object; LSSslave->pFunctLSScfgStore = pFunctLSScfgStore; } } /******************************************************************************/ void CO_LSSslave_process( CO_LSSslave_t *LSSslave, uint16_t activeBitRate, uint8_t activeNodeId, uint16_t *pendingBitRate, uint8_t *pendingNodeId) { LSSslave->activeNodeID = activeNodeId; *pendingBitRate = LSSslave->pendingBitRate; *pendingNodeId = LSSslave->pendingNodeID; } /******************************************************************************/ CO_LSS_state_t CO_LSSslave_getState( CO_LSSslave_t *LSSslave) { if(LSSslave != NULL){ return LSSslave->lssState; } return CO_LSS_STATE_WAITING; } /******************************************************************************/ bool_t CO_LSSslave_LEDprocess( CO_LSSslave_t *LSSslave, uint16_t timeDifference_ms, bool_t *LEDon) { static uint16_t ms50 = 0; static int8_t flash1, flash2; if (LSSslave == NULL || LEDon == NULL) return false; ms50 += timeDifference_ms; if(ms50 >= 50) { ms50 -= 50; /* 4 cycles on, 50 cycles off */ if(++flash1 >= 4) flash1 = -50; /* 4 cycles on, 4 cycles off, 4 cycles on, 50 cycles off */ switch(++flash2){ case 4: flash2 = -104; break; case -100: flash2 = 100; break; case 104: flash2 = -50; break; } } if (LSSslave->lssState == CO_LSS_STATE_CONFIGURATION) { *LEDon = (flash2 >= 0); return true; } else if (LSSslave->activeNodeID == CO_LSS_NODE_ID_ASSIGNMENT) { *LEDon = (flash1 >= 0); return true; } return false; } #endif