/*! * * 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 UiProtocol.h * \author (last) Phil Braica * \date (last) 23-Jan-2023 * \author (original) Phil Braica * \date (original) 23-Jan-2023 * */ #ifndef UI_PROTOCOL_H_ #define UI_PROTOCOL_H_ #include "IDataProvider.h" #include "IEnterBootLoader.h" #include "HalStdTypes.h" #include "MsgLink.h" #include "UpdateProtocol.h" #include "UiUpdateStatus.h" #include #include #include #include namespace SwUpdate { /*! * This class uses a thread plus a waitable condition variable * to impliment a linear procedure to update one firmware * "target". It uses "ID" slots for commands so that in theory * multiple updaters running in paralell won't conflict. * * Note that this is deliberately NOT a statemachine even * though it could be coded as one. Statemachines have overhead * waiting for events and the goal is to have virtually zero * time between events and in theory never really yielding unless * the Firmware image gets too busy. */ class UiProtocol { public: /*! * \brief Constructor. * * \param target Target to go after for update. */ UiProtocol(SwUpdateTargetEnum target); /*! * \brief Destructor (virtual). */ virtual ~UiProtocol(); /*! * \brief Start updating. * * \return True on started ok. */ virtual bool start(); /*! * \brief Abort update. * * \return True on success, false on timeout trying to stop the process. */ bool abort(); /*! * \brief Current estimated progress. * * When not "updating" it is the last known step * which can describe a failure condition for debug. * * \return Status object. */ UiUpdateStatus progress(); /*! * \brief Receive and check if matched. * * \param msgId Can message ID. * \param pData Data pointer. * * \return True if it matched. */ bool receive(uint16 msgId, uint8 * pData); /*! * \brief Get the target enum value. * * \return Target enum. */ SwUpdateTargetEnum target() { return _target; } /*! * \brief Did this complete ok? * * \return True if it has ran and completed successfully since a "start" call. */ bool completedOk() { return _completedOk; } /*! * \brief Start updating. * * \param pProvider Data provider. * \param pEnterBoot The enter bootloader object or nullpointer. * * \return True on started ok. */ void setProvider(IDataProvider* pProvider, SwUpdate::IEnterBootLoader *pEnterBoot) { std::lock_guard lockApi(_apiMutex); _pProvider = pProvider; _maxTransfer = pProvider->totalSize; _pEnterBL = pEnterBoot; } static uint8 getNextMsgSlotId(); static uint16 getNextMsgSeed(); protected: /*! * \brief Thread runs update process. */ void _run(); /** * \brief Do the update for FW / FPGA */ virtual void _doUpdate(); /*! * \brief Send command. * * \param cmd Command enum to use. * * \return True on success, else false. */ virtual bool _sendCommand(SwUpdateCmdEnum cmd); /*! * \brief Stream the data. * * \return True on completed ok. */ virtual bool _streamData(); /*! * \brief Stream the data. * * \return True on completed ok. */ virtual bool _sendSignature(); /*! * \brief Wait until we are paused or started. * * \param tillStopped Wait till stopped else wait till started. * * \return True on success, false on timeout. */ bool _wait_stop(bool tillStopped); /*! * Progress states. */ enum UpdateState { None = 0, Starting, Started, Streamed, Verified, Versioned, Completed, StartTimedOut, StreamTimedOut, FailedVerify, FailedVersion, RebootTimeout, CyberThreat, FileFailure }; /*! * The desired state. */ enum DesiredState { Idle = 0, Complete, Shutdown }; static uint16 _gRandSeed; ///< Used to "whiten" encoding. static uint8 _gIdNum; ///< Keeps multiple Updaters seperate. // Note: These would be tuned based on traffic to be as small as possible but reliable. const uint32 _retryEffort = 40; ///< Retry effort. const uint32 _maxCyberRetries = 200; ///< Max retries total all msgs. // The goal is to apply reasonable effort, and be able to report status. // Thus, there is a desired end-state goal we apply effort towards, and // we track the progress. We also track when we abort / stop what was the // last state to make any desired logging / status / debug easy. DesiredState _desired; ///< Desired state. UpdateState _state; ///< Current state. UpdateState _lastState; ///< Last state before stopping. SwUpdateTargetEnum _target; ///< Target for update. IDataProvider* _pProvider; ///< Data provider. uint32 _transfered; ///< Transfered bytes. uint32 _maxTransfer; ///< Max to transfer. uint32 _retries; ///< Number of packet retries. uint8 _lastRx[CAN_PAYLOAD_SIZE]; ///< Last RX. MsgLink _link; ///< Link. ::std::thread* _pThread; ///< Thread. ::std::mutex _apiMutex; ///< Mutex API. bool _completedOk; ///< Completed ok, false is either not started or in progress. IEnterBootLoader * _pEnterBL; ///< Enter bootloader class. }; /*! * This class inherits from UiProtocol to do all of the file transfers. */ class UiProtocolFile : public UiProtocol { public: /*! * \brief Constructor. * * \param target Target to go after for update. */ UiProtocolFile() : UiProtocol(LINUX) { ; // NOP. } /*! * \brief Destructor (virtual). */ virtual ~UiProtocolFile() { ; // NOP. } /*! * \brief Start updating. * * \param targets Data provider, destination pairs. * * \return True on started ok. */ void setup( std::vector& targets) { std::lock_guard lockApi(_apiMutex); _targets = targets; // So UI threads can get transfer status asynchronously // once we start, save off the transfer max in number of files. _maxTransfer = (uint32)targets.size(); // Copy the key info in. _desired = Complete; _lastState = None; } /*! * \brief Do the update for file. */ virtual void _doUpdate() { constexpr std::chrono::duration waitRetry(250); // In theory this is fast, so "status" is about number of files copied. bool ok = true; for (IDataProvider* p : _targets) { if (_desired == Complete) { ok = p->copyFile(); } else { break; } if (!ok) { break; } _transfered++; } _state = ok ? UpdateState::Completed : UpdateState::FileFailure; } protected: std::vector _targets; ///< File targets to copy. }; } // namespace::SwUpdate #endif // UI_PROTOCOL_H_