/*! * * Copyright (c) 2023 Diality Inc. - All Rights Reserved. * * \copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file MsgLink.h * \author (last) Phil Braica * \date (last) 23-Jan-2023 * \author (original) Phil Braica * \date (original) 23-Jan-2023 * */ #ifndef MSG_LINK_H_ #define MSG_LINK_H_ #include "EventWait.h" #include "UpdateProtocol.h" #include "Logger.h" #include "CanInterface.h" #include #ifdef __cplusplus extern "C" { #endif // Send call. extern bool sendPacketFromUi(uint16 msgId, uint8* pData, uint32 length); #ifdef __cplusplus } #endif namespace SwUpdate { /*! * Heuristic tuned value, typically it is 2x or more the wire * rate for most CAN systems. * * /note This is a tunable heuristic. */ const float BandwidthFactor = 2.0f; /*! * The total amount of payload bytes to round trip data + response. */ const uint32 TotalRoundTripDataSizeBytes = (sizeof(SwUpdateDataBuffer) + sizeof(SwUpdateResponse)); /*! * For each 8 bytes of payload, 114 wire bits are * used (header + payload + tail). */ const float BytesToCanBits = 114.0f / 8.0f; /*! * Pause after a NACK before sending reset and retry. */ const std::chrono::duration g_ms_pause(20); /*! * This is 2x the line speed transfer of a * message relative to kbps of the line in milliseconds. * * Example: * (sizeof(SwUpdateDataBuffer) + sizeof(SwUpdateResponse))/8) = 34. * 34 packets * 114 bits per packet = 3,876 bits. * / 250kpbs = 15.504 ms if full bandwidth for 250kbps. * * 2 for collisions/normal stuff = * Expected round trip within 31 ms else we should retry. * */ const float RetryKbpsScaleFactor = TotalRoundTripDataSizeBytes * BytesToCanBits * BandwidthFactor; /*! * \brief Send a command and wait for response. * * Typical use is wait on bool sendOk(...), and if it failed then * the effort timed out for retries. * * The numberRetries() call checks for too much cumulative failures * which indicates the bus is improperly being used by other nodes * while attempting the software update or cyber attack. */ class MsgLink { public: /*! * \brief Constructor. */ MsgLink(float kbps) : _ew((uint32)(60 + (RetryKbpsScaleFactor / kbps))), _expecting(false), _retries(0), _expectedId(0), _msgIdSlot(0), _wasData(false), _dataBuf({0,0,0,0, {0}}) { } /*! * \brief Virtual destructor. */ virtual ~MsgLink() { _expecting = false; } /*! * \brief Get the number of message retries. */ uint32 numberRetries() { return _retries; } /*! * \brief Reset the number of retries. */ void resetRetries() { _retries = 0; } /*! * \brief Abort waiting. */ void abort() { _expecting = false; _ew.fireEvent(); } /*! * \brief Send a message, wait as needed for retry/response. * * \param msgId The message * \param msg The message bytes, already with security. * \param maxEffort Maximum number of 1ms retries. * * \return True on success, False on retry failed or timeout. */ bool sendOk( uint16 msgId, uint8 * msg, uint16 maxEffort) { // We are expecting data. _expecting = true; _wasData = (msgId == SwUpdateCanMsgIds::DataBufferId_HD_FW) || (msgId == SwUpdateCanMsgIds::DataBufferId_HD_FPGA) || (msgId == SwUpdateCanMsgIds::DataBufferId_DG_FW) || (msgId == SwUpdateCanMsgIds::DataBufferId_DG_FPGA); // Either command or data. if data use ResponseId, // if a command and verify or version use VerifyId else ResponseId. _expectedId = _wasData ? SwUpdateCanMsgIds::ResponseId : SwUpdate_getCmd(msg[1]) == SwUpdateCmdEnum::Verify ? SwUpdateCanMsgIds::VerifyId : SwUpdate_getCmd(msg[1]) == SwUpdateCmdEnum::Version ? SwUpdateCanMsgIds::VerifyId : ResponseId; _msgIdSlot = msg[0]; // For ack/nack matching. // Sending a command (8 bytes) or data (256). uint32 length = _wasData ? sizeof(SwUpdateDataBuffer) : sizeof(SwUpdateCommand); if (_wasData) { memcpy(&_dataBuf, msg, sizeof(SwUpdateDataBuffer)); } //const uint32 bytes_per_packet = 8; for (uint32 ii = 0; ii < maxEffort; ii++) { // No longer expecting! if (!_expecting) { break; } _ew.re_arm(); // Call indirectly to make this more testable. this->sendPacket(msgId, msg, length); // Wait to either retry or success. if (_ew.wait()) { _expecting = false; return true; } _retries++; // If we're still expecting then we do a retry. // Before retry send a command that should clear us. if (_expecting && _wasData) { SwUpdateCommand resync; resync.id = _msgIdSlot + 1; SwUpdateTargetEnum target = (SwUpdateTargetEnum)(((SwUpdateDataBuffer *)msg)->kind); resync.cmd = SwUpdate_FormCommand(target, SwUpdateCmdEnum::Resync); resync.rand = 0; SwUpdate_createSecurity((uint8 *)&resync, sizeof(SwUpdateCommand)); // Either we could wait for the retry ACK or not. // For now do not, its unclear what would be ideal and either ought to work. #if 0 uint16 old_expectedId = _expectedId; uint16 old_msgIdSlot = _msgIdSlot; bool oldWasData = _wasData; _expectedId = SwUpdateCanMsgIds::ResponseId; _msgIdSlot = resync.id; _wasData = false; // Sleep, arm, send. std::this_thread::sleep_for(g_ms_pause); _ew.re_arm(); this->sendPacket(SwUpdateCanMsgIds::CommandId, (uint8*)&resync, 8); _ew.wait(); // Reset state and then sleep a bit. _expectedId = old_expectedId; _msgIdSlot = old_msgIdSlot; _wasData = oldWasData; std::this_thread::sleep_for(g_ms_pause); #else std::this_thread::sleep_for(g_ms_pause); this->sendPacket(SwUpdateCanMsgIds::CommandId, (uint8*)&resync, 8); std::this_thread::sleep_for(g_ms_pause); #endif } } _expecting = false; // swUpdate_log("MsgLink::sendOk failed."); return false; } /*! * \brief Send a packet. * * \param mstId Messge Id. * \param pData Message data. * \param length Message length. */ void sendPacket(uint16 msgId, uint8* pData, uint32 length) { _CanInterface.sendSWUpdateMsg(msgId, pData, length); } /*! * \brief Receive and check if matched. * * \param msgId Can message ID. * \param pData Data pointer. * * \return True if we received the expected message. */ bool checkReceived(uint16 msgId, uint8 * pData) { bool rv = false; if (_expecting) { if (_wasData) { rv = SwUpdate_checkSecurityForData(pData, (uint8*)&_dataBuf); } else { if ((SwUpdate_verifySecurity(pData, sizeof(SwUpdateResponse))) && (msgId == _expectedId)) { // msgId resp->id is ack | rv // is resp match // F * * | T Its a verify/version // T F * | F Not for this thread // T T F | F For the thread not an ack. // T T T | T For this thread and an ack. if (msgId == SwUpdateCanMsgIds::ResponseId) { SwUpdateResponse *resp = (SwUpdateResponse *)pData; rv = ((resp->id == _msgIdSlot) && (resp->ackNack == 1)); } else { rv = true; } } } // It's good? fire event. if (rv) { _ew.fireEvent(); } } return rv; } protected: EventWait _ew; ///< Wait for time or event. bool _expecting; ///< Expecting data. uint32 _retries; ///< Total number of retries. uint16 _expectedId; ///< Expected message Id. uint8 _msgIdSlot; ///< Message slot. bool _wasData; ///< Was data. SwUpdateDataBuffer _dataBuf; ///< Data buffer. }; } // namespace SwUpdate #endif // MSG_LINK_H_