From e09f54c6b6fe35dc022fcea66935f5269866bff1 Mon Sep 17 00:00:00 2001 From: Andrii Pientsov Date: Fri, 10 Jul 2020 15:40:48 +0300 Subject: MTX-3404 mPower Oct20: Delta Radio Firmware Upgrade - L4E1 - libmts-io support --- include/mts/MTS_IO_LE910C4EURadio.h | 1 + include/mts/MTS_IO_TelitRadio.h | 50 ++++- src/MTS_IO_LE910C4EURadio.cpp | 4 + src/MTS_IO_TelitRadio.cpp | 383 ++++++++++++++++++++++++++++++++++++ 4 files changed, 437 insertions(+), 1 deletion(-) diff --git a/include/mts/MTS_IO_LE910C4EURadio.h b/include/mts/MTS_IO_LE910C4EURadio.h index e872df3..a0d6baa 100644 --- a/include/mts/MTS_IO_LE910C4EURadio.h +++ b/include/mts/MTS_IO_LE910C4EURadio.h @@ -35,6 +35,7 @@ namespace MTS { virtual ~LE910C4EURadio(){}; protected: + FOTA_GROUP getFotaGroup() override; private: diff --git a/include/mts/MTS_IO_TelitRadio.h b/include/mts/MTS_IO_TelitRadio.h index bd542f1..07a95de 100644 --- a/include/mts/MTS_IO_TelitRadio.h +++ b/include/mts/MTS_IO_TelitRadio.h @@ -44,6 +44,10 @@ namespace MTS { CODE getSupportedCellularModes(CELLULAR_MODES &networks) override; CODE setCellularMode(CELLULAR_MODES networks) override; + CODE updateFumoLocal(int fd, UpdateCb& stepCb) override; + CODE fumoLocalInject(int fd, UpdateCb& stepCb) override; + CODE fumoLocalApply(UpdateCb& stepCb) override; + protected: TelitRadio(const std::string& sName, const std::string& sRadioPort); @@ -53,10 +57,54 @@ namespace MTS { CODE getIsSimInserted(bool& bData) override; CODE getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk) override; + enum FOTA_GROUP : uint8_t { + VALUE_GROUP_A = 0, + VALUE_GROUP_B, + VALUE_GROUP_C, + VALUE_GROUP_D, + VALUE_UNKNOWN + }; + + virtual FOTA_GROUP getFotaGroup(); + virtual CODE uploadFile(int fd, UpdateCb& stepCb); + private: virtual CODE getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk, const std::string& sLockStatus); ICellularRadio::CODE wdsList(std::set &wds); + + // private variable to save old firmware versions during FOTA + std::string m_sTelitFirmware; + + static const size_t FILE_CHUNK_SIZE; + static const std::string CMD_ABORT_UPLOAD; + + CODE getTelitFirmware(std::string& sFirmware); + + CODE startFileUpload(); + CODE abortFileUpload(); + + static inline void callNextStep(UpdateCb& stepCb, const char* csMessage); + static inline void callNextStep(UpdateCb& stepCb, const std::string& sMessage); + + static CODE getFileSize(int fd, size_t& nBytes, size_t& nFileChunks); + static CODE readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes); + CODE fumoWaitRadioBooted(UpdateCb& stepCb); + CODE fumoCheckNewFirmware(UpdateCb& stepCb); + }; } } -#endif + +void MTS::IO::TelitRadio::callNextStep(ICellularRadio::UpdateCb& stepCb, const char* csMessage) { + if (stepCb) { + stepCb(Json::Value(csMessage)); + } +} + +void MTS::IO::TelitRadio::callNextStep(ICellularRadio::UpdateCb& stepCb, const std::string& sMessage) { + if (stepCb) { + stepCb(Json::Value(sMessage)); + } +} + +#endif /* MTS_IO_TELITRADIO_H_ */ diff --git a/src/MTS_IO_LE910C4EURadio.cpp b/src/MTS_IO_LE910C4EURadio.cpp index 029a921..4cf146b 100644 --- a/src/MTS_IO_LE910C4EURadio.cpp +++ b/src/MTS_IO_LE910C4EURadio.cpp @@ -29,3 +29,7 @@ LE910C4EURadio::LE910C4EURadio(const std::string& sPort) { } + +TelitRadio::FOTA_GROUP LE910C4EURadio::getFotaGroup() { + return VALUE_GROUP_B; +} diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp index be31b03..967baae 100644 --- a/src/MTS_IO_TelitRadio.cpp +++ b/src/MTS_IO_TelitRadio.cpp @@ -24,8 +24,14 @@ #include #include +#include + using namespace MTS::IO; +const size_t TelitRadio::FILE_CHUNK_SIZE = 1024; +const std::string TelitRadio::CMD_ABORT_UPLOAD = "+++"; + + TelitRadio::TelitRadio(const std::string& sName, const std::string& sRadioPort) : CellularRadio(sName, sRadioPort) { @@ -730,3 +736,380 @@ ICellularRadio::CODE TelitRadio::setCellularMode(CELLULAR_MODES networks) { } return SUCCESS; } + +ICellularRadio::CODE TelitRadio::updateFumoLocal(int fd, ICellularRadio::UpdateCb& stepCb) { + CODE rc = FAILURE; + FOTA_GROUP group = getFotaGroup(); + + if (group == VALUE_UNKNOWN) { + printError("Delta firmware upgrade is not supported for this type of radio modem"); + callNextStep(stepCb, "FUMO Error: delta firmware upgrade is not supported for this type of radio modem"); + return rc; + } + + rc = fumoLocalInject(fd, stepCb); + if (rc != SUCCESS) { + return rc; + } + + rc = fumoLocalApply(stepCb); + + return rc; +} + +ICellularRadio::CODE TelitRadio::fumoLocalInject(int fd, ICellularRadio::UpdateCb& stepCb) { + CODE rc = FAILURE; + FOTA_GROUP group = getFotaGroup(); + + do { + callNextStep(stepCb, "FUMO Info: downloading the firmware"); + + if ((group == VALUE_GROUP_A) || (group == VALUE_GROUP_B) || (group == VALUE_GROUP_D)) { + rc = uploadFile(fd, stepCb); + if (rc != SUCCESS) { + printError("Failed to inject the delta file."); + callNextStep(stepCb, "FUMO Error: failed to download the firmware file"); + break; + } + } else if (group == VALUE_GROUP_C) { + //TODO Not Implemented + printError("Failed to inject the delta file."); + callNextStep(stepCb, "FUMO Error: not implemented"); + break; + } + + callNextStep(stepCb, "FUMO Info: firmware downloaded successfully"); + + } while (false); + + return rc; +} + +ICellularRadio::CODE TelitRadio::fumoLocalApply(ICellularRadio::UpdateCb& stepCb) { + ICellularRadio::CODE rc; + std::string sCmd; + FOTA_GROUP group = getFotaGroup(); + + rc = getTelitFirmware(m_sTelitFirmware); + if (rc != SUCCESS) { + callNextStep(stepCb, "FUMO Error: Failed to obtain current firmware version"); + return rc; + } + printInfo("Current firmware version: %s", m_sTelitFirmware.c_str()); + + if ((group == VALUE_GROUP_A) || (group == VALUE_GROUP_D)) { + // Send "AT#OTAUP=0,0" command to start the upgrade. OK response follows shortly. + sCmd = "AT#OTAUP=0,0"; + } else if ((group == VALUE_GROUP_B) || (group == VALUE_GROUP_C)) { + // Send "AT#OTAUP=2" command to start the upgrade. OK response follows shortly. + sCmd = "AT#OTAUP=2"; + } + + rc = sendBasicCommand(sCmd, 2000); + + if (rc != SUCCESS) { + printError("FUMO failed, OK not received from the radio"); + callNextStep(stepCb, "FUMO Error: failed to apply the firmware"); + return rc; + } + + const uint32_t duDetachTimeout = 10000; // wait for 10 seconds for the radio to detach + const uint32_t duAttachTimeout = 300000; // wait for 300 seconds for the radio to attach + + do { + printInfo("Waiting for the radio to enter recovery mode"); + callNextStep(stepCb, "FUMO Info: waiting for the radio to enter recovery mode"); + + // Wait for the radio to detach from the USB bus + MTS::Thread::sleep(duDetachTimeout); + + printInfo("Firmware update in progress"); + callNextStep(stepCb, "FUMO Info: firmware update in progress"); + + // It's now detached. Try to reconnect + if (!resetConnection(duAttachTimeout)) { + printError("Can't connect to the radio in %d ms", (duAttachTimeout)); + callNextStep(stepCb, "FUMO Error: unable to obtain radio after reset"); + rc = ERROR; + break; + } + + // Wait for the radio to finish update and reboot + printTrace("Waiting for the radio to come up"); + callNextStep(stepCb, "FUMO Info: waiting for the radio to enter normal mode"); + rc = fumoWaitRadioBooted(stepCb); + + if (rc != SUCCESS) { + break; + } + + rc = fumoCheckNewFirmware(stepCb); + + } while (false); + + + if (rc == SUCCESS) { + printInfo("Radio firmware applied successfully"); + callNextStep(stepCb, "FUMO Done: radio firmware applied successfully"); + } else { + printError("Radio firmware has not been updated"); + callNextStep(stepCb, "FUMO Error: radio firmware has not been updated"); + } + + return rc; +} + +TelitRadio::FOTA_GROUP TelitRadio::getFotaGroup() { + return VALUE_UNKNOWN; +} + +ICellularRadio::CODE TelitRadio::uploadFile(int fd, ICellularRadio::UpdateCb& stepCb) { + size_t dPayloadLength; + size_t nChunks; + CODE rc; + + rc = getFileSize(fd, dPayloadLength, nChunks); + if (rc != SUCCESS) { + return rc; + } + printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks); + printTrace("Starting file upload..."); + + rc = startFileUpload(); + if (rc != SUCCESS) { + return rc; + } + + printTrace("File upload started."); + callNextStep(stepCb, "FILE Info: Started file upload"); + + size_t nChunksPerCent = (nChunks / 100) + 1; + size_t nFragmentLength = 0; + std::array vBuffer; + + for (size_t iChunk = 1; iChunk < (nChunks + 1); iChunk++) { + + rc = readChunk(fd, vBuffer.data(), vBuffer.size(), nFragmentLength); + if (rc != SUCCESS) { + break; + } + + rc = sendData(vBuffer.data(), nFragmentLength); + if (rc != SUCCESS) { + break; + } + + if (stepCb && ((iChunk % nChunksPerCent) == 0)) { + size_t dPercentsCompleted = iChunk / nChunksPerCent; + callNextStep(stepCb, "FILE Info: Uploaded " + MTS::Text::format(dPercentsCompleted) + "%"); + } + } + + if (rc != SUCCESS) { + callNextStep(stepCb, "FILE Error: Upload failed due to internal error"); + } else { + callNextStep(stepCb, "FILE Info: Upload finished successfully"); + } + + // send +++ + abortFileUpload(); + return rc; +} + +ICellularRadio::CODE TelitRadio::getFileSize(int fd, size_t& nBytes, size_t& nChunks) { + CODE rc = FAILURE; + + do { + ssize_t dScrollPos; + dScrollPos = lseek(fd, 0, SEEK_SET); + if (dScrollPos < 0) { + printError("Failed to seek to the start of the file: %d", errno); + break; + } + + dScrollPos = lseek(fd, 0, SEEK_END); + if (dScrollPos < 0) { + printError("Failed to determine the file size: %d", errno); + break; + } + + nBytes = static_cast(dScrollPos); + nChunks = (nBytes + FILE_CHUNK_SIZE - 1) / FILE_CHUNK_SIZE; // round up + + rc = SUCCESS; + + } while (false); + + lseek(fd, 0, SEEK_SET); + return rc; +} + +ICellularRadio::CODE TelitRadio::readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes) { + size_t nUsedBuffer = 0; + CODE rc = FAILURE; + + while (true) { + + if (nUsedBuffer > dChunkSize) { + printError("Internal pointer error, abort upload: %d", nUsedBuffer); + rc = ERROR; + break; + } + + if (nUsedBuffer == dChunkSize) { + // full chunk received + rc = SUCCESS; + nReadBytes = dChunkSize; + break; + } + + char* pData = pChunk + nUsedBuffer; + size_t nFreeBuffer = dChunkSize - nUsedBuffer; + + ssize_t dReadCount = read(fd, pData, nFreeBuffer); + if (dReadCount < 0) { + printError("Failed to read from the source file: %d", errno); + rc = ERROR; + break; + } + + size_t duReadCount = static_cast(dReadCount); + if (duReadCount == 0) { + // EOF. Return what was already read + nReadBytes = nUsedBuffer; + rc = SUCCESS; + break; + } + + nUsedBuffer += duReadCount; + + } + + return rc; +} + +ICellularRadio::CODE TelitRadio::startFileUpload() { + const std::vector vBailStrings{ ICellularRadio::RSP_CONNECT, ICellularRadio::RSP_ERROR }; + const int dTimeout = 1000; //ms + std::string sCommand, sResult; + + sCommand = "AT#OTAUPW"; + + sResult = sendCommand(sCommand, vBailStrings, dTimeout); + if (sResult.find(ICellularRadio::RSP_CONNECT) == std::string::npos) { + printError("Radio is not ready to accept the file: [%s]", sResult.c_str()); + return FAILURE; + } + + return SUCCESS; +} + +ICellularRadio::CODE TelitRadio::abortFileUpload() { + /* + * To prevent the “+++” from being mistaken for data, the following sequence should be followed: + * 1) Do not input any character within 1s or longer before inputting “+++”. + * 2) Input “+++” within 1s, and no other characters can be inputted during the time. + * 3) Do not input any character within 1s after “+++” has been inputted. + */ + sleep(1); + return sendBasicCommand(CMD_ABORT_UPLOAD, 2000, 0x00); +} + +ICellularRadio::CODE TelitRadio::getTelitFirmware(std::string& sFirmware) { + /* + * Currently, AT+CGMR command is used to determine the radio firmware version. + * + * AT+CGMR + * M0F.670006 + * + * Perhaps in the future we will use AT#SWPKGV command. + * + * AT#SWPKGV + * 25.20.676-P0F.670690 + * M0F.670006 + * P0F.670690 + * A0F.670006 + */ + + printTrace("%s| Get Telit-specific firmware version", getName().c_str()); + sFirmware = ICellularRadio::VALUE_NOT_SUPPORTED; + std::string sCmd("AT+CGMR"); + std::string sResult = sendCommand(sCmd); + size_t pos = sResult.find(ICellularRadio::RSP_OK); + if (pos == std::string::npos) { + printWarning("%s| Unable to get firmware from radio using command [%s]", getName().c_str(), sCmd.c_str()); + return FAILURE; + } + + sFirmware = MTS::Text::trim(sResult.substr(0, pos)); + if(sFirmware.size() == 0) { + printWarning("%s| Unable to get firmware from radio using command [%s]", getName().c_str(), sCmd.c_str()); + return FAILURE; + } + + return SUCCESS; +} + +ICellularRadio::CODE TelitRadio::fumoWaitRadioBooted(ICellularRadio::UpdateCb& stepCb) { + const uint32_t duUrcTimeout = 60 * 1000; // wait for 1 minutes for the next URC message + const std::string sFotaUrcPrefix = "#OTAEV:"; // prefix for the URC notification messages + const std::string sFotaUrcEndSuccess = "Module Upgraded To New Fw"; + const std::string sFotaUrcEndFailed = "OTA Fw Upgrade Failed"; + const std::vector vFotaBailStrings{ sFotaUrcPrefix }; + + CODE rc = FAILURE; + std::string sResponse; + + while (true) { + + sResponse = sendCommand("", vFotaBailStrings, duUrcTimeout, 0x00); + printTrace("Radio response: [%s]", sResponse.c_str()); + + if (sResponse.find(sFotaUrcPrefix) == std::string::npos) { + printError("No URC messages from the radio in %d ms", duUrcTimeout); + callNextStep(stepCb, "FUMO Error: timeout, radio is not responding"); + rc = ERROR; + break; + } + + if (sResponse.find(sFotaUrcEndSuccess) != std::string::npos) { + printTrace("Radio module upgraded to new firmware"); + callNextStep(stepCb, "FUMO Info: radio module upgraded to new firmware"); + rc = SUCCESS; + break; + } + + if (sResponse.find(sFotaUrcEndFailed) != std::string::npos) { + printTrace("Radio module firmware upgrade failed"); + callNextStep(stepCb, "FUMO Error: firmware upgrade failed"); + rc = ERROR; + break; + } + + } + + return rc; +} + +ICellularRadio::CODE TelitRadio::fumoCheckNewFirmware(ICellularRadio::UpdateCb& stepCb) { + CODE rc = SUCCESS; + std::string sTelitFirmware; + + rc = getTelitFirmware(sTelitFirmware); + + if (rc != SUCCESS) { + callNextStep(stepCb, "FUMO Error: Failed to obtain current firmware version"); + return rc; + } + + printInfo("Firmware version before the upgrade: %s", m_sTelitFirmware.c_str()); + printInfo("Current firmware version: %s", sTelitFirmware.c_str()); + + if (sTelitFirmware == m_sTelitFirmware) { + // Radio will not reset anymore, firmware version left the same, not updated + printError("Radio firmware version not changed after upgrade"); + rc = FAILURE; + } + + return rc; +} -- cgit v1.2.3 From ccd41677d998134ff501a8d6ac3a154dcaca9321 Mon Sep 17 00:00:00 2001 From: Andrii Pientsov Date: Mon, 13 Jul 2020 15:29:38 +0300 Subject: MTX-3404 Code Review --- include/mts/MTS_IO_CellularRadio.h | 4 ++ include/mts/MTS_IO_QuectelRadio.h | 3 - include/mts/MTS_IO_TelitRadio.h | 12 ++-- src/MTS_IO_CellularRadio.cpp | 72 +++++++++++++++++++ src/MTS_IO_QuectelRadio.cpp | 75 +------------------- src/MTS_IO_TelitRadio.cpp | 138 +++++++++---------------------------- 6 files changed, 113 insertions(+), 191 deletions(-) diff --git a/include/mts/MTS_IO_CellularRadio.h b/include/mts/MTS_IO_CellularRadio.h index e3941c3..6b23986 100644 --- a/include/mts/MTS_IO_CellularRadio.h +++ b/include/mts/MTS_IO_CellularRadio.h @@ -217,6 +217,10 @@ namespace MTS { std::string m_sRadioType; }; + static const size_t FILE_CHUNK_SIZE = 1024; + static CODE getFileSize(int fd, size_t& nBytes, size_t& nFileChunks); + static CODE readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes); + private: std::string m_sName; std::string m_sRadioPort; diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index d8228cb..2121f7c 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -67,7 +67,6 @@ namespace MTS { // private variable to save old firmware versions during FOTA std::string m_sQuectelFirmware; - static const size_t FILE_CHUNK_SIZE; static const std::string CMD_ABORT_UPLOAD; static const std::string VALUE_MTS_DELTA_NAME; static const std::string VALUE_MTS_DELTA_PATH; @@ -85,8 +84,6 @@ namespace MTS { static uint16_t getQuectelChecksum(const void* data, size_t nBytes); static inline void updateQuectelChecksum(uint16_t& iChecksum, uint16_t iNewFragment); static inline uint16_t bytesToUint16(uint8_t high, uint8_t low); - static CODE getFileSize(int fd, size_t& nBytes, size_t& nFileChunks); - static CODE readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes); CODE fumoWaitUpgradeFinished(UpdateCb& stepCb); CODE fumoWaitNewFirmware(UpdateCb& stepCb); }; diff --git a/include/mts/MTS_IO_TelitRadio.h b/include/mts/MTS_IO_TelitRadio.h index 07a95de..fdf8a20 100644 --- a/include/mts/MTS_IO_TelitRadio.h +++ b/include/mts/MTS_IO_TelitRadio.h @@ -66,7 +66,8 @@ namespace MTS { }; virtual FOTA_GROUP getFotaGroup(); - virtual CODE uploadFile(int fd, UpdateCb& stepCb); + virtual CODE fumoWriteGroupsABD(int fd, UpdateCb& stepCb); + //virtual CODE fumoWriteGroupC(int fd, UpdateCb& stepCb); private: virtual CODE getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk, const std::string& sLockStatus); @@ -75,20 +76,17 @@ namespace MTS { // private variable to save old firmware versions during FOTA std::string m_sTelitFirmware; - static const size_t FILE_CHUNK_SIZE; static const std::string CMD_ABORT_UPLOAD; CODE getTelitFirmware(std::string& sFirmware); - CODE startFileUpload(); - CODE abortFileUpload(); + CODE startWrite(); + CODE abortWrite(); static inline void callNextStep(UpdateCb& stepCb, const char* csMessage); static inline void callNextStep(UpdateCb& stepCb, const std::string& sMessage); - static CODE getFileSize(int fd, size_t& nBytes, size_t& nFileChunks); - static CODE readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes); - CODE fumoWaitRadioBooted(UpdateCb& stepCb); + CODE fumoWaitUpgradeFinished(UpdateCb& stepCb); CODE fumoCheckNewFirmware(UpdateCb& stepCb); }; diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp index bb7cd40..0acf607 100644 --- a/src/MTS_IO_CellularRadio.cpp +++ b/src/MTS_IO_CellularRadio.cpp @@ -1262,3 +1262,75 @@ const char *CellularRadio::RadioBandMap::getRadioBandName(const std::string &cha return band; } + +ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes, size_t& nChunks) { + CODE rc = FAILURE; + + do { + ssize_t dScrollPos; + dScrollPos = lseek(fd, 0, SEEK_SET); + if (dScrollPos < 0) { + printError("Failed to seek to the start of the file: %d", errno); + break; + } + + dScrollPos = lseek(fd, 0, SEEK_END); + if (dScrollPos < 0) { + printError("Failed to determine the file size: %d", errno); + break; + } + + nBytes = static_cast(dScrollPos); + nChunks = (nBytes + FILE_CHUNK_SIZE - 1) / FILE_CHUNK_SIZE; // round up + + rc = SUCCESS; + + } while (false); + + lseek(fd, 0, SEEK_SET); + return rc; +} + +ICellularRadio::CODE CellularRadio::readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes) { + size_t nUsedBuffer = 0; + CODE rc = FAILURE; + + while (true) { + + if (nUsedBuffer > dChunkSize) { + printError("Internal pointer error, abort upload: %d", nUsedBuffer); + rc = ERROR; + break; + } + + if (nUsedBuffer == dChunkSize) { + // full chunk received + rc = SUCCESS; + nReadBytes = dChunkSize; + break; + } + + char* pData = pChunk + nUsedBuffer; + size_t nFreeBuffer = dChunkSize - nUsedBuffer; + + ssize_t dReadCount = read(fd, pData, nFreeBuffer); + if (dReadCount < 0) { + printError("Failed to read from the source file: %d", errno); + rc = ERROR; + break; + } + + size_t duReadCount = static_cast(dReadCount); + if (duReadCount == 0) { + // EOF. Return what was already read + nReadBytes = nUsedBuffer; + rc = SUCCESS; + break; + } + + nUsedBuffer += duReadCount; + + } + + return rc; +} diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index 22374ee..bb81621 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -31,7 +31,6 @@ using namespace MTS::IO; -const size_t QuectelRadio::FILE_CHUNK_SIZE = 1024; const std::string QuectelRadio::CMD_ABORT_UPLOAD = "+++"; // It is strongly recommended to use DOS 8.3 file name format for . @@ -603,7 +602,7 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step sCmd += VALUE_MTS_DELTA_PATH; sCmd += "\""; - rc = sendBasicCommand(sCmd, 1000); + rc = sendBasicCommand(sCmd, 10000); if (rc != SUCCESS) { printError("FUMO failed, OK not received from the radio"); @@ -985,78 +984,6 @@ uint16_t QuectelRadio::getQuectelChecksum(const void* data, size_t nBytes) { return iChecksum; } -ICellularRadio::CODE QuectelRadio::getFileSize(int fd, size_t& nBytes, size_t& nChunks) { - CODE rc = FAILURE; - - do { - ssize_t dScrollPos; - dScrollPos = lseek(fd, 0, SEEK_SET); - if (dScrollPos < 0) { - printError("Failed to seek to the start of the file: %d", errno); - break; - } - - dScrollPos = lseek(fd, 0, SEEK_END); - if (dScrollPos < 0) { - printError("Failed to determine the file size: %d", errno); - break; - } - - nBytes = static_cast(dScrollPos); - nChunks = (nBytes + FILE_CHUNK_SIZE - 1) / FILE_CHUNK_SIZE; // round up - - rc = SUCCESS; - - } while (false); - - lseek(fd, 0, SEEK_SET); - return rc; -} - -ICellularRadio::CODE QuectelRadio::readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes) { - size_t nUsedBuffer = 0; - CODE rc = FAILURE; - - while (true) { - - if (nUsedBuffer > dChunkSize) { - printError("Internal pointer error, abort upload: %d", nUsedBuffer); - rc = ERROR; - break; - } - - if (nUsedBuffer == dChunkSize) { - // full chunk received - rc = SUCCESS; - nReadBytes = dChunkSize; - break; - } - - char* pData = pChunk + nUsedBuffer; - size_t nFreeBuffer = dChunkSize - nUsedBuffer; - - ssize_t dReadCount = read(fd, pData, nFreeBuffer); - if (dReadCount < 0) { - printError("Failed to read from the source file: %d", errno); - rc = ERROR; - break; - } - - size_t duReadCount = static_cast(dReadCount); - if (duReadCount == 0) { - // EOF. Return what was already read - nReadBytes = nUsedBuffer; - rc = SUCCESS; - break; - } - - nUsedBuffer += duReadCount; - - } - - return rc; -} - ICellularRadio::CODE QuectelRadio::fumoWaitUpgradeFinished(ICellularRadio::UpdateCb& stepCb) { const uint32_t duUrcTimeout = 4 * 60 * 1000; // wait for 4 minutes for the next URC message const std::string sFotaUrcPrefix = "+QIND: \"FOTA\""; // prefix for the URC notification messages diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp index 967baae..86c3565 100644 --- a/src/MTS_IO_TelitRadio.cpp +++ b/src/MTS_IO_TelitRadio.cpp @@ -28,7 +28,6 @@ using namespace MTS::IO; -const size_t TelitRadio::FILE_CHUNK_SIZE = 1024; const std::string TelitRadio::CMD_ABORT_UPLOAD = "+++"; @@ -739,13 +738,6 @@ ICellularRadio::CODE TelitRadio::setCellularMode(CELLULAR_MODES networks) { ICellularRadio::CODE TelitRadio::updateFumoLocal(int fd, ICellularRadio::UpdateCb& stepCb) { CODE rc = FAILURE; - FOTA_GROUP group = getFotaGroup(); - - if (group == VALUE_UNKNOWN) { - printError("Delta firmware upgrade is not supported for this type of radio modem"); - callNextStep(stepCb, "FUMO Error: delta firmware upgrade is not supported for this type of radio modem"); - return rc; - } rc = fumoLocalInject(fd, stepCb); if (rc != SUCCESS) { @@ -765,16 +757,22 @@ ICellularRadio::CODE TelitRadio::fumoLocalInject(int fd, ICellularRadio::UpdateC callNextStep(stepCb, "FUMO Info: downloading the firmware"); if ((group == VALUE_GROUP_A) || (group == VALUE_GROUP_B) || (group == VALUE_GROUP_D)) { - rc = uploadFile(fd, stepCb); + rc = fumoWriteGroupsABD(fd, stepCb); if (rc != SUCCESS) { printError("Failed to inject the delta file."); callNextStep(stepCb, "FUMO Error: failed to download the firmware file"); break; } } else if (group == VALUE_GROUP_C) { - //TODO Not Implemented + //TODO Not Implemented TelitRadio::fumoWriteGroupC printError("Failed to inject the delta file."); callNextStep(stepCb, "FUMO Error: not implemented"); + rc = NOT_APPLICABLE; + break; + } else { + printError("Delta firmware upgrade is not supported for this type of radio modem"); + callNextStep(stepCb, "FUMO Error: delta firmware upgrade is not supported for this type of radio modem"); + rc = NOT_APPLICABLE; break; } @@ -803,9 +801,13 @@ ICellularRadio::CODE TelitRadio::fumoLocalApply(ICellularRadio::UpdateCb& stepCb } else if ((group == VALUE_GROUP_B) || (group == VALUE_GROUP_C)) { // Send "AT#OTAUP=2" command to start the upgrade. OK response follows shortly. sCmd = "AT#OTAUP=2"; + } else { + printError("Delta firmware upgrade is not supported for this type of radio modem"); + callNextStep(stepCb, "FUMO Error: delta firmware upgrade is not supported for this type of radio modem"); + return NOT_APPLICABLE; } - rc = sendBasicCommand(sCmd, 2000); + rc = sendBasicCommand(sCmd, 10000); if (rc != SUCCESS) { printError("FUMO failed, OK not received from the radio"); @@ -814,30 +816,15 @@ ICellularRadio::CODE TelitRadio::fumoLocalApply(ICellularRadio::UpdateCb& stepCb } const uint32_t duDetachTimeout = 10000; // wait for 10 seconds for the radio to detach - const uint32_t duAttachTimeout = 300000; // wait for 300 seconds for the radio to attach do { - printInfo("Waiting for the radio to enter recovery mode"); - callNextStep(stepCb, "FUMO Info: waiting for the radio to enter recovery mode"); + printInfo("Applying the radio firmware"); + callNextStep(stepCb, "FUMO Info: applying the radio firmware"); // Wait for the radio to detach from the USB bus MTS::Thread::sleep(duDetachTimeout); - printInfo("Firmware update in progress"); - callNextStep(stepCb, "FUMO Info: firmware update in progress"); - - // It's now detached. Try to reconnect - if (!resetConnection(duAttachTimeout)) { - printError("Can't connect to the radio in %d ms", (duAttachTimeout)); - callNextStep(stepCb, "FUMO Error: unable to obtain radio after reset"); - rc = ERROR; - break; - } - - // Wait for the radio to finish update and reboot - printTrace("Waiting for the radio to come up"); - callNextStep(stepCb, "FUMO Info: waiting for the radio to enter normal mode"); - rc = fumoWaitRadioBooted(stepCb); + rc = fumoWaitUpgradeFinished(stepCb); if (rc != SUCCESS) { break; @@ -863,7 +850,7 @@ TelitRadio::FOTA_GROUP TelitRadio::getFotaGroup() { return VALUE_UNKNOWN; } -ICellularRadio::CODE TelitRadio::uploadFile(int fd, ICellularRadio::UpdateCb& stepCb) { +ICellularRadio::CODE TelitRadio::fumoWriteGroupsABD(int fd, ICellularRadio::UpdateCb& stepCb) { size_t dPayloadLength; size_t nChunks; CODE rc; @@ -875,7 +862,7 @@ ICellularRadio::CODE TelitRadio::uploadFile(int fd, ICellularRadio::UpdateCb& st printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks); printTrace("Starting file upload..."); - rc = startFileUpload(); + rc = startWrite(); if (rc != SUCCESS) { return rc; } @@ -912,83 +899,12 @@ ICellularRadio::CODE TelitRadio::uploadFile(int fd, ICellularRadio::UpdateCb& st } // send +++ - abortFileUpload(); - return rc; -} - -ICellularRadio::CODE TelitRadio::getFileSize(int fd, size_t& nBytes, size_t& nChunks) { - CODE rc = FAILURE; - - do { - ssize_t dScrollPos; - dScrollPos = lseek(fd, 0, SEEK_SET); - if (dScrollPos < 0) { - printError("Failed to seek to the start of the file: %d", errno); - break; - } - - dScrollPos = lseek(fd, 0, SEEK_END); - if (dScrollPos < 0) { - printError("Failed to determine the file size: %d", errno); - break; - } - - nBytes = static_cast(dScrollPos); - nChunks = (nBytes + FILE_CHUNK_SIZE - 1) / FILE_CHUNK_SIZE; // round up - - rc = SUCCESS; - - } while (false); - - lseek(fd, 0, SEEK_SET); + abortWrite(); return rc; } -ICellularRadio::CODE TelitRadio::readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes) { - size_t nUsedBuffer = 0; - CODE rc = FAILURE; - - while (true) { - - if (nUsedBuffer > dChunkSize) { - printError("Internal pointer error, abort upload: %d", nUsedBuffer); - rc = ERROR; - break; - } - - if (nUsedBuffer == dChunkSize) { - // full chunk received - rc = SUCCESS; - nReadBytes = dChunkSize; - break; - } - - char* pData = pChunk + nUsedBuffer; - size_t nFreeBuffer = dChunkSize - nUsedBuffer; - - ssize_t dReadCount = read(fd, pData, nFreeBuffer); - if (dReadCount < 0) { - printError("Failed to read from the source file: %d", errno); - rc = ERROR; - break; - } - - size_t duReadCount = static_cast(dReadCount); - if (duReadCount == 0) { - // EOF. Return what was already read - nReadBytes = nUsedBuffer; - rc = SUCCESS; - break; - } - - nUsedBuffer += duReadCount; - } - - return rc; -} - -ICellularRadio::CODE TelitRadio::startFileUpload() { +ICellularRadio::CODE TelitRadio::startWrite() { const std::vector vBailStrings{ ICellularRadio::RSP_CONNECT, ICellularRadio::RSP_ERROR }; const int dTimeout = 1000; //ms std::string sCommand, sResult; @@ -1004,7 +920,7 @@ ICellularRadio::CODE TelitRadio::startFileUpload() { return SUCCESS; } -ICellularRadio::CODE TelitRadio::abortFileUpload() { +ICellularRadio::CODE TelitRadio::abortWrite() { /* * To prevent the “+++” from being mistaken for data, the following sequence should be followed: * 1) Do not input any character within 1s or longer before inputting “+++”. @@ -1050,8 +966,9 @@ ICellularRadio::CODE TelitRadio::getTelitFirmware(std::string& sFirmware) { return SUCCESS; } -ICellularRadio::CODE TelitRadio::fumoWaitRadioBooted(ICellularRadio::UpdateCb& stepCb) { - const uint32_t duUrcTimeout = 60 * 1000; // wait for 1 minutes for the next URC message +ICellularRadio::CODE TelitRadio::fumoWaitUpgradeFinished(ICellularRadio::UpdateCb& stepCb) { + const uint32_t duAttachTimeout = 300000; // wait for 300 seconds for the radio to attach + const uint32_t duUrcTimeout = 60 * 1000; // wait for 1 minutes for the next URC message const std::string sFotaUrcPrefix = "#OTAEV:"; // prefix for the URC notification messages const std::string sFotaUrcEndSuccess = "Module Upgraded To New Fw"; const std::string sFotaUrcEndFailed = "OTA Fw Upgrade Failed"; @@ -1060,6 +977,13 @@ ICellularRadio::CODE TelitRadio::fumoWaitRadioBooted(ICellularRadio::UpdateCb& s CODE rc = FAILURE; std::string sResponse; + // It's now detached. Try to reconnect + if (!resetConnection(duAttachTimeout)) { + printError("Can't connect to the radio in %d ms", (duAttachTimeout)); + callNextStep(stepCb, "FUMO Error: unable to obtain radio after reset"); + return ERROR; + } + while (true) { sResponse = sendCommand("", vFotaBailStrings, duUrcTimeout, 0x00); -- cgit v1.2.3 From 8186f98913c55191b5a3610d19c8580c6a86c2f4 Mon Sep 17 00:00:00 2001 From: Andrii Pientsov Date: Wed, 15 Jul 2020 13:49:59 +0300 Subject: MTX-3404 Code Review --- include/mts/MTS_IO_CellularRadio.h | 4 ++-- include/mts/MTS_IO_QuectelRadio.h | 1 + include/mts/MTS_IO_TelitRadio.h | 1 + src/MTS_IO_CellularRadio.cpp | 8 ++++++-- src/MTS_IO_QuectelRadio.cpp | 9 ++++++++- src/MTS_IO_TelitRadio.cpp | 39 ++++++++++++++++++++++---------------- 6 files changed, 41 insertions(+), 21 deletions(-) diff --git a/include/mts/MTS_IO_CellularRadio.h b/include/mts/MTS_IO_CellularRadio.h index 6b23986..57a4de6 100644 --- a/include/mts/MTS_IO_CellularRadio.h +++ b/include/mts/MTS_IO_CellularRadio.h @@ -217,8 +217,8 @@ namespace MTS { std::string m_sRadioType; }; - static const size_t FILE_CHUNK_SIZE = 1024; - static CODE getFileSize(int fd, size_t& nBytes, size_t& nFileChunks); + static CODE getFileSize(int fd, size_t& nBytes); + static CODE sizeToChunks(const size_t nBytes, const size_t chunkSize, size_t& nChunks); static CODE readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes); private: diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index 2121f7c..6e7372b 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -67,6 +67,7 @@ namespace MTS { // private variable to save old firmware versions during FOTA std::string m_sQuectelFirmware; + static const size_t FILE_CHUNK_SIZE; static const std::string CMD_ABORT_UPLOAD; static const std::string VALUE_MTS_DELTA_NAME; static const std::string VALUE_MTS_DELTA_PATH; diff --git a/include/mts/MTS_IO_TelitRadio.h b/include/mts/MTS_IO_TelitRadio.h index fdf8a20..a3425f9 100644 --- a/include/mts/MTS_IO_TelitRadio.h +++ b/include/mts/MTS_IO_TelitRadio.h @@ -76,6 +76,7 @@ namespace MTS { // private variable to save old firmware versions during FOTA std::string m_sTelitFirmware; + static const size_t FILE_CHUNK_SIZE; static const std::string CMD_ABORT_UPLOAD; CODE getTelitFirmware(std::string& sFirmware); diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp index 0acf607..c4514e6 100644 --- a/src/MTS_IO_CellularRadio.cpp +++ b/src/MTS_IO_CellularRadio.cpp @@ -1263,7 +1263,7 @@ const char *CellularRadio::RadioBandMap::getRadioBandName(const std::string &cha return band; } -ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes, size_t& nChunks) { +ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes) { CODE rc = FAILURE; do { @@ -1281,7 +1281,6 @@ ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes, size_t& } nBytes = static_cast(dScrollPos); - nChunks = (nBytes + FILE_CHUNK_SIZE - 1) / FILE_CHUNK_SIZE; // round up rc = SUCCESS; @@ -1291,6 +1290,11 @@ ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes, size_t& return rc; } +ICellularRadio::CODE CellularRadio::sizeToChunks(const size_t nBytes, const size_t chunkSize, size_t& nChunks) { + nChunks = (nBytes + chunkSize - 1) / chunkSize; + return SUCCESS; +} + ICellularRadio::CODE CellularRadio::readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes) { size_t nUsedBuffer = 0; CODE rc = FAILURE; diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index bb81621..2d1ffcd 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -31,6 +31,7 @@ using namespace MTS::IO; +const size_t QuectelRadio::FILE_CHUNK_SIZE = 1024; const std::string QuectelRadio::CMD_ABORT_UPLOAD = "+++"; // It is strongly recommended to use DOS 8.3 file name format for . @@ -836,10 +837,16 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget size_t nChunks; CODE rc; - rc = getFileSize(fd, dPayloadLength, nChunks); + rc = getFileSize(fd, dPayloadLength); if (rc != SUCCESS) { return rc; } + + rc = sizeToChunks(dPayloadLength, FILE_CHUNK_SIZE, nChunks); + if (rc != SUCCESS) { + return rc; + } + printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks); printTrace("Starting file upload..."); diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp index 86c3565..52b5664 100644 --- a/src/MTS_IO_TelitRadio.cpp +++ b/src/MTS_IO_TelitRadio.cpp @@ -28,6 +28,7 @@ using namespace MTS::IO; +const size_t TelitRadio::FILE_CHUNK_SIZE = 1024; const std::string TelitRadio::CMD_ABORT_UPLOAD = "+++"; @@ -855,10 +856,16 @@ ICellularRadio::CODE TelitRadio::fumoWriteGroupsABD(int fd, ICellularRadio::Upda size_t nChunks; CODE rc; - rc = getFileSize(fd, dPayloadLength, nChunks); + rc = getFileSize(fd, dPayloadLength); if (rc != SUCCESS) { return rc; } + + rc = sizeToChunks(dPayloadLength, FILE_CHUNK_SIZE, nChunks); + if (rc != SUCCESS) { + return rc; + } + printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks); printTrace("Starting file upload..."); @@ -927,25 +934,25 @@ ICellularRadio::CODE TelitRadio::abortWrite() { * 2) Input “+++” within 1s, and no other characters can be inputted during the time. * 3) Do not input any character within 1s after “+++” has been inputted. */ - sleep(1); - return sendBasicCommand(CMD_ABORT_UPLOAD, 2000, 0x00); + sleep(1); + return sendBasicCommand(CMD_ABORT_UPLOAD, 2000, 0x00); } ICellularRadio::CODE TelitRadio::getTelitFirmware(std::string& sFirmware) { /* - * Currently, AT+CGMR command is used to determine the radio firmware version. - * - * AT+CGMR + * Currently, AT+CGMR command is used to determine the radio firmware version. + * + * AT+CGMR + * M0F.670006 + * + * Perhaps in the future we will use AT#SWPKGV command. + * + * AT#SWPKGV + * 25.20.676-P0F.670690 * M0F.670006 - * - * Perhaps in the future we will use AT#SWPKGV command. - * - * AT#SWPKGV - * 25.20.676-P0F.670690 - * M0F.670006 - * P0F.670690 - * A0F.670006 - */ + * P0F.670690 + * A0F.670006 + */ printTrace("%s| Get Telit-specific firmware version", getName().c_str()); sFirmware = ICellularRadio::VALUE_NOT_SUPPORTED; @@ -968,7 +975,7 @@ ICellularRadio::CODE TelitRadio::getTelitFirmware(std::string& sFirmware) { ICellularRadio::CODE TelitRadio::fumoWaitUpgradeFinished(ICellularRadio::UpdateCb& stepCb) { const uint32_t duAttachTimeout = 300000; // wait for 300 seconds for the radio to attach - const uint32_t duUrcTimeout = 60 * 1000; // wait for 1 minutes for the next URC message + const uint32_t duUrcTimeout = 60 * 1000; // wait for 1 minutes for the next URC message const std::string sFotaUrcPrefix = "#OTAEV:"; // prefix for the URC notification messages const std::string sFotaUrcEndSuccess = "Module Upgraded To New Fw"; const std::string sFotaUrcEndFailed = "OTA Fw Upgrade Failed"; -- cgit v1.2.3