From 26f6e69e9d5c7049d42559259933482b35559889 Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Thu, 28 May 2020 14:51:31 +0300 Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Declared base interface and added stub implementation for the Delta Radio Firmware Upgrade support in libmts-io. --- include/mts/MTS_IO_CellularRadio.h | 3 +++ include/mts/MTS_IO_ICellularRadio.h | 50 +++++++++++++++++++++++++++++++++++++ src/MTS_IO_CellularRadio.cpp | 18 +++++++++++++ 3 files changed, 71 insertions(+) diff --git a/include/mts/MTS_IO_CellularRadio.h b/include/mts/MTS_IO_CellularRadio.h index 56506af..9fdb076 100644 --- a/include/mts/MTS_IO_CellularRadio.h +++ b/include/mts/MTS_IO_CellularRadio.h @@ -98,6 +98,9 @@ namespace MTS { CODE updateDc(const Json::Value& jArgs, UpdateCb& stepCb) override; CODE updatePrl(const Json::Value& jArgs, UpdateCb& stepCb) override; CODE updateFumo(const Json::Value& jArgs, UpdateCb& stepCb) override; + CODE uploadDeltaFirmwareFile(int fd, UpdateCb& stepCb) override; + CODE removeDeltaFirmwareFile() override; + CODE applyDeltaFirmwareFile(UpdateCb& stepCb) override; CODE resetHfa(const Json::Value& jArgs, UpdateCb& stepCb) override; CODE activate(const Json::Value& jArgs, UpdateCb& stepCb) override; CODE setActiveFirmware(const Json::Value& jArgs) override; diff --git a/include/mts/MTS_IO_ICellularRadio.h b/include/mts/MTS_IO_ICellularRadio.h index 58d5076..5123ba1 100644 --- a/include/mts/MTS_IO_ICellularRadio.h +++ b/include/mts/MTS_IO_ICellularRadio.h @@ -405,6 +405,56 @@ namespace MTS { */ virtual CODE updateFumo(const Json::Value& jArgs, UpdateCb& stepCb) = 0; + /** + * @brief uploadDeltaFirmwareFile - upload delta file to the radio's internal memory. + * + * This command uploads (injects) the whole delta firmware image to some place that + * can be later used by the radio to perform the Delta Radio Firmware Upgrade. + * + * This delta firmware image is NOT validated on the firmware image upload step. + * + * @param fd - file sescriptor of a file that shall be uploaded to the radio. + * @param stepCb - the callback to receive status updates during the upload. + * @return CODE::SUCCESS when the file was successfully uploaded, + * CODE::INVALID_ARGS when the file can't be opened for reading, + * CODE::NOT_APPLICABLE when not supported by this radio, + * CODE::ERROR otherwise. + */ + virtual CODE uploadDeltaFirmwareFile(int fd, UpdateCb& stepCb) = 0; + + /** + * @brief removeDeltaFirmwareFile - remove the delta file from the radio's internal memory. + * + * This command allows to remove the old delta firmware image from the radio's internal + * memory for cases when it's no longer needed. + * + * @param fd - file sescriptor of a file that shall be uploaded to the radio. + * @return CODE::SUCCESS when the file was successfully deleted, + * CODE::FAILURE when the file can't be deleted (i.e. no such file), + * CODE::NOT_APPLICABLE when not supported by this radio, + * CODE::ERROR otherwise. + */ + virtual CODE removeDeltaFirmwareFile() = 0; + + /** + * @brief applyDeltaFirmwareFile - apply the delta file that was previously uploaded. + * + * This commands initializes and tracks the progress of the delta firmware upgrade + * procedure using the delta firmware image file that was previously uploaded + * into internal radio's memory. + * + * See ICellularRadio::removeDeltaFirmwareFile to upload the file and prepare + * for the upgrade. + * + * @param fd - file sescriptor of a file that shall be uploaded to the radio. + * @param stepCb - the callback to receive status updates during the upgrade. + * @return CODE::SUCCESS when the file was successfully deleted, + * CODE::FAILURE when the file can't be deleted (i.e. no such file), + * CODE::NOT_APPLICABLE when not supported by this radio, + * CODE::ERROR otherwise. + */ + virtual CODE applyDeltaFirmwareFile(UpdateCb& stepCb) = 0; + /* * jArgs = { * "msl" : "Master Subsidy Lock (Sprint): STRING" diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp index 50fdf5c..d3bee11 100644 --- a/src/MTS_IO_CellularRadio.cpp +++ b/src/MTS_IO_CellularRadio.cpp @@ -1008,6 +1008,24 @@ ICellularRadio::CODE CellularRadio::updateFumo(const Json::Value&, UpdateCb&) { return NOT_APPLICABLE; } +ICellularRadio::CODE CellularRadio::uploadDeltaFirmwareFile(int, ICellularRadio::UpdateCb&) { + printTrace("%s| Upload Delta Firmware Upgrade File: not applicable", m_sName.c_str()); + + return NOT_APPLICABLE; +} + +ICellularRadio::CODE CellularRadio::removeDeltaFirmwareFile() { + printTrace("%s| Remove Delta Firmware Upgrade File: not applicable", m_sName.c_str()); + + return NOT_APPLICABLE; +} + +ICellularRadio::CODE CellularRadio::applyDeltaFirmwareFile(ICellularRadio::UpdateCb&) { + printTrace("%s| Apply Delta Firmware Upgrade File: not applicable", m_sName.c_str()); + + return NOT_APPLICABLE; +} + ICellularRadio::CODE CellularRadio::resetHfa(const Json::Value&, UpdateCb&) { printTrace("%s| HFA Reset", m_sName.c_str()); -- cgit v1.2.3 From 9f5a4f138b56a0a1b4e5764a69261aa4a4edaa71 Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Thu, 28 May 2020 15:55:00 +0300 Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Declared base interface and added started implementation for the QuectelRadio class. Implemented file listing (file status check) and file removal logic. --- include/mts/MTS_IO_QuectelRadio.h | 10 +++++ src/MTS_IO_QuectelRadio.cpp | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index df3b5b5..8aba437 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -42,6 +42,10 @@ namespace MTS { CODE setMdn(const Json::Value& jArgs) override; + CODE uploadDeltaFirmwareFile(int fd, UpdateCb& stepCb) override; + CODE removeDeltaFirmwareFile() override; + CODE applyDeltaFirmwareFile(UpdateCb& stepCb) override; + protected: QuectelRadio(const std::string& sName, const std::string& sRadioPort); @@ -51,7 +55,13 @@ namespace MTS { virtual CODE getServiceDomain(SERVICEDOMAIN& sd); virtual CODE convertToActiveBand(const std::string& sQuectelBand, ACTIVEBAND& band); + virtual CODE uploadFile(int fd, const std::string& sTargetFilename, UpdateCb& stepCb); + virtual CODE removeFile(const std::string& sTargetFilename); + virtual CODE checkFile(bool& bFilePresent, const std::string& sTargetFilename); + private: + static const std::string VALUE_MTS_DELTA_NAME; + static const std::string VALUE_MTS_DELTA_PATH; }; } diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index c4bdc56..d0ec310 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -26,6 +26,10 @@ using namespace MTS::IO; +// It is strongly recommended to use DOS 8.3 file name format for . +const std::string QuectelRadio::VALUE_MTS_DELTA_NAME = "mtsdelta.zip"; +const std::string QuectelRadio::VALUE_MTS_DELTA_PATH = "/data/ufs/" + QuectelRadio::VALUE_MTS_DELTA_NAME; + QuectelRadio::QuectelRadio(const std::string& sName, const std::string& sRadioPort) : CellularRadio (sName, sRadioPort) { @@ -436,6 +440,46 @@ ICellularRadio::CODE QuectelRadio::setMdn(const Json::Value& jArgs) { return NOT_APPLICABLE; } +ICellularRadio::CODE QuectelRadio::uploadDeltaFirmwareFile(int fd, ICellularRadio::UpdateCb& stepCb) { + CODE rc = FAILURE; + bool bIsFilePresent = false; + + do { + rc = checkFile(bIsFilePresent, VALUE_MTS_DELTA_NAME); + if (rc != SUCCESS) { + printError("Failed to check if the delta file was already uploaded."); + break; + } + + if (bIsFilePresent) { + rc = removeDeltaFirmwareFile(); + } + + if (rc != SUCCESS) { + printError("Failed to remove the previous delta file."); + break; + } + + rc = uploadFile(fd, VALUE_MTS_DELTA_NAME, stepCb); + if (rc != SUCCESS) { + printError("Failed to upload the delta file."); + break; + } + + } while (false); + + return rc; +} + +ICellularRadio::CODE QuectelRadio::removeDeltaFirmwareFile() { + printTrace("Removing the delta upgrade file: %s", VALUE_MTS_DELTA_NAME.c_str()); + return removeFile(VALUE_MTS_DELTA_NAME); +} + +ICellularRadio::CODE QuectelRadio::applyDeltaFirmwareFile(ICellularRadio::UpdateCb& stepCb) { + return ERROR; // not implemented +} + ICellularRadio::CODE QuectelRadio::getServiceDomain(ICellularRadio::SERVICEDOMAIN& sd) { printTrace("%s| Get Service Domain", getName().c_str()); @@ -568,3 +612,41 @@ ICellularRadio::CODE QuectelRadio::convertToActiveBand(const std::string& sQuect return SUCCESS; } + +ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTargetFilename, ICellularRadio::UpdateCb& stepCb) { + return ERROR; // not implemented +} + +ICellularRadio::CODE QuectelRadio::removeFile(const std::string& sTargetFilename) { + printTrace("Removing file [%s] from the radio memory", sTargetFilename.c_str()); + + const int dTimeout = 1000; //ms + const std::string sCmd = "AT+QFDEL=\"" + sTargetFilename + "\""; + + std::string sResult = sendCommand(sCmd, ICellularRadio::DEFAULT_BAIL_STRINGS, dTimeout); + if (sResult.find(ICellularRadio::RSP_OK) == std::string::npos) { + printError("Failed to remove file [%s]: [%s]", sTargetFilename.c_str(), sResult.c_str()); + return FAILURE; + } + + printTrace("File [%s] removed", sTargetFilename.c_str()); + return SUCCESS; +} + +ICellularRadio::CODE QuectelRadio::checkFile(bool& bIsFilePresent, const std::string& sTargetFilename) { + printTrace("Checking status of the [%s] file", sTargetFilename.c_str()); + + const int dTimeout = 1000; //ms + const std::string sCmd = "AT+QFLST"; // list all files in the UFS memory + + std::string sResult = sendCommand(sCmd, ICellularRadio::DEFAULT_BAIL_STRINGS, dTimeout); + if (sResult.rfind(ICellularRadio::RSP_OK) == std::string::npos) { + printError("Unable to list files from the radio memory: [%s]", sResult.c_str()); + return FAILURE; + } + + const std::string sExpected = "+QFLST: \"UFS:" + sTargetFilename + "\""; + bIsFilePresent = (sResult.find(sExpected) != std::string::npos); + + return SUCCESS; +} -- cgit v1.2.3 From 6e9ce61addd97809d5ea7b912332dd11a4bf7cee Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Fri, 29 May 2020 21:02:15 +0300 Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Initial implementation of the delta firmware image upload for Quectel radios. --- include/mts/MTS_IO_CellularRadio.h | 2 + include/mts/MTS_IO_ICellularRadio.h | 1 + include/mts/MTS_IO_QuectelRadio.h | 20 ++++ src/MTS_IO_CellularRadio.cpp | 17 +++ src/MTS_IO_ICellularRadio.cpp | 2 +- src/MTS_IO_QuectelRadio.cpp | 206 +++++++++++++++++++++++++++++++++++- 6 files changed, 246 insertions(+), 2 deletions(-) diff --git a/include/mts/MTS_IO_CellularRadio.h b/include/mts/MTS_IO_CellularRadio.h index 9fdb076..92df561 100644 --- a/include/mts/MTS_IO_CellularRadio.h +++ b/include/mts/MTS_IO_CellularRadio.h @@ -177,6 +177,8 @@ namespace MTS { REGISTRATION parseRegResponse(std::string sResult); CODE getRegistration(REGISTRATION& eRegistration, const std::string& sType); + virtual CODE sendData(const char* pData, size_t nBytes); + class RadioBandMap : public MTS::NonCopyable { public: RadioBandMap() diff --git a/include/mts/MTS_IO_ICellularRadio.h b/include/mts/MTS_IO_ICellularRadio.h index 5123ba1..7ad69c7 100644 --- a/include/mts/MTS_IO_ICellularRadio.h +++ b/include/mts/MTS_IO_ICellularRadio.h @@ -103,6 +103,7 @@ namespace MTS { static const char *RSP_OK; static const char *RSP_ERROR; + static const char *RSP_CONNECT; static const char *VALUE_NOT_REGISTERED; static const char *VALUE_REGISTERED; diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index 8aba437..9ccf50a 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -60,11 +60,31 @@ namespace MTS { virtual CODE checkFile(bool& bFilePresent, const std::string& sTargetFilename); private: + 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; + CODE startFileUpload(const std::string& sTargetFilename, size_t nBytes); + CODE abortFileUpload(); + + 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); }; } } +void MTS::IO::QuectelRadio::updateQuectelChecksum(uint16_t& iChecksum, uint16_t iNewFragment) { + iChecksum = iChecksum ^ iNewFragment; +} + +uint16_t MTS::IO::QuectelRadio::bytesToUint16(uint8_t high, uint8_t low) { + uint16_t comboHigh = static_cast(high << 8); // explicit cast to prevent warnings + uint16_t comboLow = low; + return (comboHigh | comboLow); +} + #endif /* MTS_IO_QUECTELRADIO_H_ */ diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp index d3bee11..26d9f43 100644 --- a/src/MTS_IO_CellularRadio.cpp +++ b/src/MTS_IO_CellularRadio.cpp @@ -1072,6 +1072,23 @@ std::string CellularRadio::sendCommand(const std::string& sCmd, MTS::IO::Cellula return ICellularRadio::sendCommand(m_apIo, sCmd, isNeedMoreData, timeoutMillis, ESC); } +ICellularRadio::CODE CellularRadio::sendData(const char* pData, size_t nBytes) { + if(m_apIo.isNull()) { + printError("RADIO| IO is not set in sendData"); + return ERROR; + } + + int32_t iResult; + iResult = m_apIo->write(pData, nBytes); + + if(iResult == -1) { + printError("RADIO| Failed to send data to radio"); + return ERROR; + } + + return SUCCESS; +} + bool CellularRadio::splitAndAssign(const std::string& sLine, const std::string& sKey, Json::Value& jParent, const std::string& sJsonKey, Json::ValueType eType) { std::vector vParts = MTS::Text::split(sLine, ":", 2); diff --git a/src/MTS_IO_ICellularRadio.cpp b/src/MTS_IO_ICellularRadio.cpp index 53c8faa..2de52bb 100644 --- a/src/MTS_IO_ICellularRadio.cpp +++ b/src/MTS_IO_ICellularRadio.cpp @@ -11,7 +11,7 @@ const char MTS::IO::ICellularRadio::CTRL_Z = 0x1A; const char *MTS::IO::ICellularRadio::RSP_ERROR = "ERROR"; const char *MTS::IO::ICellularRadio::RSP_OK = "OK"; - +const char *MTS::IO::ICellularRadio::RSP_CONNECT = "CONNECT"; const char *MTS::IO::ICellularRadio::DEFAULT_RADIO_PORT = "/dev/modem_at1"; const char *MTS::IO::ICellularRadio::DEFAULT_RADIO_DIR = "/var/run/radio/"; diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index d0ec310..8cd7194 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -24,8 +24,13 @@ #include #include +#include + 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 . const std::string QuectelRadio::VALUE_MTS_DELTA_NAME = "mtsdelta.zip"; const std::string QuectelRadio::VALUE_MTS_DELTA_PATH = "/data/ufs/" + QuectelRadio::VALUE_MTS_DELTA_NAME; @@ -614,7 +619,87 @@ ICellularRadio::CODE QuectelRadio::convertToActiveBand(const std::string& sQuect } ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTargetFilename, ICellularRadio::UpdateCb& stepCb) { - return ERROR; // not implemented + size_t dPayloadLenght; + size_t nChunks; + CODE rc; + + rc = getFileSize(fd, dPayloadLenght, nChunks); + if (rc != SUCCESS) { + return rc; + } + printTrace("File size: %d bytes and %d chunks", dPayloadLenght, nChunks); + printTrace("Starting file upload..."); + + rc = startFileUpload(sTargetFilename, dPayloadLenght); + if (rc != SUCCESS) { + return rc; + } + + printTrace("File upload started."); + if (stepCb) { + stepCb(Json::Value("FILE info: Started file upload for " + sTargetFilename)); + } + + uint16_t dChecksum = 0; + size_t nChunksPerCent = (nChunks / 100) + 1; + size_t nFragmentLenght = 0; + std::array vBuffer; + + for (size_t iChunk = 1; iChunk < (nChunks + 1); iChunk++) { + + rc = readChunk(fd, vBuffer.data(), vBuffer.size(), nFragmentLenght); + if (rc != SUCCESS) { + break; + } + + // we got our fragment, calculate checksum and flush the data + uint16_t dFragmentChecksum = getQuectelChecksum(vBuffer.data(), nFragmentLenght); + updateQuectelChecksum(dChecksum, dFragmentChecksum); + + rc = sendData(vBuffer.data(), nFragmentLenght); + if (rc != SUCCESS) { + break; + } + + if (stepCb && ((iChunk % nChunksPerCent) == 0)) { + size_t dPercentsCompleted = iChunk / nChunksPerCent; + stepCb(Json::Value("FILE info: Uploaded " + MTS::Text::format(dPercentsCompleted) + "%")); + } + + } + + if (rc != SUCCESS) { + // cancel upload and terminate + stepCb(Json::Value("FILE error: Upload failed due to internal error")); + abortFileUpload(); + return rc; + } + + printTrace("Waiting for acknoledge from the radio"); + std::string sExpectedResult = "+QFUPL: "; + sExpectedResult += MTS::Text::format(dPayloadLenght); + sExpectedResult += ","; + sExpectedResult += MTS::Text::toLowerCase(MTS::Text::formatHex(dChecksum)); + + // "send" empty string to read acknoledge string + std::string sResult = sendCommand("", DEFAULT_BAIL_STRINGS, 3000, 0x00); + + if (sResult.find(sExpectedResult) != std::string::npos) { + printDebug("Radio returned: [%s]", sResult.c_str()); + printTrace("Upload finished, checksum matched"); + } else { + printError("Upload failed: checksum mismatch. Expected: [%s], Actual: [%s]", sExpectedResult.c_str(), sResult.c_str()); + abortFileUpload(); + rc = FAILURE; + } + + if (stepCb && rc == SUCCESS) { + stepCb(Json::Value("FILE info: Upload finished successfully")); + } else if (stepCb) { + stepCb(Json::Value("FILE error: Upload failed due to internal error")); + } + + return rc; } ICellularRadio::CODE QuectelRadio::removeFile(const std::string& sTargetFilename) { @@ -650,3 +735,122 @@ ICellularRadio::CODE QuectelRadio::checkFile(bool& bIsFilePresent, const std::st return SUCCESS; } + +uint16_t QuectelRadio::getQuectelChecksum(const void* data, size_t nBytes) { + auto castData = static_cast(data); + uint16_t iChecksum = 0; + + for (size_t i = 0; i < nBytes; i += 2) { + // If the number of the characters is odd, set the last character as the high 8 bit, and the low 8 bit as 0, + // and then use an XOR operator to calculate the checksum. + uint8_t high = castData[i]; + uint8_t low = (i < nBytes) ? (castData[i+1]) : (0); + + uint16_t iFragment = bytesToUint16(high, low); + updateQuectelChecksum(iChecksum, iFragment); + } + + 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::startFileUpload(const std::string& sTargetFilename, size_t nBytes) { + const std::vector vBailStrings{ ICellularRadio::RSP_CONNECT, ICellularRadio::RSP_ERROR }; + const int dTimeout = 1000; //ms + std::string sCommand, sResult; + + sCommand = "AT+QFUPL=\""; + sCommand += sTargetFilename; + sCommand += "\","; + sCommand += MTS::Text::format(nBytes); + + 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 QuectelRadio::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); +} -- cgit v1.2.3 From d1798ea6a82c46b43e4caf27148189f34cc44ca5 Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Wed, 1 Jul 2020 11:25:34 +0300 Subject: WIP: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Initial implementation of the "Apply delta firmware" step. Requires cleanup. --- include/mts/MTS_IO_QuectelRadio.h | 3 + src/MTS_IO_QuectelRadio.cpp | 179 +++++++++++++++++++++++++++++++++++++- 2 files changed, 181 insertions(+), 1 deletion(-) diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index 506b21b..cb455d8 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -76,6 +76,9 @@ namespace MTS { 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 waitApplyDeltaFirmware(UpdateCb& stepCb); + CODE waitFotaFinish(UpdateCb& stepCb); + CODE waitNewFirmware(UpdateCb& stepCb); }; } } diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index 2ed11b0..25d2a30 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -576,7 +577,34 @@ ICellularRadio::CODE QuectelRadio::removeDeltaFirmwareFile() { } ICellularRadio::CODE QuectelRadio::applyDeltaFirmwareFile(ICellularRadio::UpdateCb& stepCb) { - return ERROR; // not implemented + ICellularRadio::CODE rc; + std::string sCmd; + + // Send "AT+QFOTADL" command to start the upgrade. OK responce follows shortly. + sCmd = "AT+QFOTADL=\""; + sCmd += VALUE_MTS_DELTA_PATH; + sCmd += "\""; + + rc = sendBasicCommand(sCmd, 1000); + + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: failed to apply the firmware")); + } + return rc; + } + + rc = waitApplyDeltaFirmware(stepCb); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: failed to apply the firmware")); + } + //return rc; + } + + // TODO: Check the new firmware version + + return rc; } ICellularRadio::CODE QuectelRadio::getServiceDomain(ICellularRadio::SERVICEDOMAIN& sd) { @@ -951,6 +979,155 @@ ICellularRadio::CODE QuectelRadio::readChunk(int fd, char* pChunk, size_t dChunk return rc; } +ICellularRadio::CODE QuectelRadio::waitApplyDeltaFirmware(ICellularRadio::UpdateCb& stepCb) { + const uint32_t duDetachTimeout = 10000; // wait for 10 seconds for the radio to detach + const uint32_t duAttachTimeout = 30000; // wait for 30 seconds for the radio to attach + const int dMaxAttempts = 5; // the radio makes 5 attempts to update the firmware + + CODE rc = FAILURE; + std::string sResponse; + + for (int i = 0; i < dMaxAttempts; i++) { + + printInfo("Waiting for the radio to enter recovery mode"); + if(stepCb) { + stepCb(Json::Value("FUMO Info: waiting for the radio to enter recovery mode")); + } + + // Wait for the radio to detach from the USB bus + MTS::Thread::sleep(duDetachTimeout); + + // It's now detached. Try to reconnect + if (!resetConnection(duAttachTimeout)) { + printError("Can't connect to the radio in %d ms", (duAttachTimeout)); + + if(stepCb) { + stepCb(Json::Value("FUMO error: failed to connect to the radio in recovery mode")); + } + break; + } + + // It's now back on the bus. Wait for the URC messages. + rc = waitFotaFinish(stepCb); + if (rc == ERROR) { + // unrecoverable error + break; + } + + if (rc != SUCCESS) { + // attempt failed, radio reboots and starts its next attempt + continue; + } + + // attempt finished + break; + } + + // Wait for the radio to finish update and reboot + rc = waitNewFirmware(stepCb); + return rc; +} + +ICellularRadio::CODE QuectelRadio::waitFotaFinish(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 + const std::string sFotaUrcStart = "\"START\""; + const std::string sFotaUrcProgress = "\"UPDATING\""; + const std::string sFotaUrcEnd = "\"END\""; + const std::vector vFotaBailStrings{ sFotaUrcPrefix }; + + CODE rc = ERROR; + std::string sResponse; + + while (true) { // breaks on "FOTA","END" + 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); + + if(stepCb) { + stepCb(Json::Value("FUMO error: timeout, radio is not responding")); + } + + break; + } + + const auto vParts = MTS::Text::split(MTS::Text::trim(sResponse), ',', 3); + const std::string& sStage = vParts[1]; + if (sStage == sFotaUrcEnd) { + // FOTA finished + printTrace("Got FOTA END message"); + const std::string& sCode = vParts[2]; + + if (sCode == "0") { + // finished successfully + rc = SUCCESS; + break; + } + // attempt failed, the radio attempts to recover + rc = FAILURE; + break; + } else if (sStage == sFotaUrcStart) { + printTrace("Got FOTA START message"); + } else if (sStage == sFotaUrcProgress) { + printTrace("Got FOTA progress message"); + const std::string& sPercents = vParts[2]; + printInfo("FOTA progress: [%s]", sPercents.c_str()); + } else { + printInfo("FOTA unexpected URC code: [%s]", sStage.c_str()); + } + } + + return rc; +} + +ICellularRadio::CODE QuectelRadio::waitNewFirmware(ICellularRadio::UpdateCb& stepCb) { + MTS::Timer oTimer; + oTimer.start(); + std::string sFirmware; + CODE rc = FAILURE; + + while (oTimer.getSeconds() < (5 * 60)) { // 5 minutes + + MTS::Thread::sleep(10000); + + if (getFirmware(sFirmware) != SUCCESS) { + // The radio is probably unavailable + resetConnection(100); + continue; + } + +#if 0 + if (sFirmware == m_sFw && sFirmwareBuild == m_sFwBuild) { + // Have the same firmware. The radio resets several time + // before the firmware is actually get upgraded. So keep polling. + continue; + } +#endif + + // The firmware numbers have changed + printTrace("Current firmware: %s", sFirmware.c_str()); + rc = SUCCESS; + break; + + } + oTimer.stop(); + + if (rc == SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO done: radio firmware applied successfully")); + } + } + else { + if(stepCb) { + stepCb(Json::Value("FUMO error: radio firmware has not been updated")); + } + } + + return rc; +} + ICellularRadio::CODE QuectelRadio::startFileUpload(const std::string& sTargetFilename, size_t nBytes) { const std::vector vBailStrings{ ICellularRadio::RSP_CONNECT, ICellularRadio::RSP_ERROR }; const int dTimeout = 1000; //ms -- cgit v1.2.3 From 2c3296b5239b5d27f5df4d61a9609ace6d70904f Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Fri, 3 Jul 2020 17:59:47 +0300 Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Started code cleanup before finishing the procedure. Renamed functions related to the delta radio firmware upgrade to follow established patterns: - uploadDeltaFirmwareFile -> fumoLocalInject - applyDeltaFirmwareFile -> fumoLocalApply - removeDeltaFirmwareFile -> fumoLocalCleanup - new function: updateFumoLocal - encapsulates all the magic for radios that may not support separate stages for the delta upload and delta apply --- include/mts/MTS_IO_CellularRadio.h | 7 ++- include/mts/MTS_IO_ICellularRadio.h | 55 ++++++++++------ include/mts/MTS_IO_QuectelRadio.h | 12 ++-- src/MTS_IO_CellularRadio.cpp | 18 ++++-- src/MTS_IO_QuectelRadio.cpp | 121 ++++++++++++++++++------------------ 5 files changed, 116 insertions(+), 97 deletions(-) diff --git a/include/mts/MTS_IO_CellularRadio.h b/include/mts/MTS_IO_CellularRadio.h index e65e7cd..e3941c3 100644 --- a/include/mts/MTS_IO_CellularRadio.h +++ b/include/mts/MTS_IO_CellularRadio.h @@ -99,9 +99,10 @@ namespace MTS { CODE updateDc(const Json::Value& jArgs, UpdateCb& stepCb) override; CODE updatePrl(const Json::Value& jArgs, UpdateCb& stepCb) override; CODE updateFumo(const Json::Value& jArgs, UpdateCb& stepCb) override; - CODE uploadDeltaFirmwareFile(int fd, UpdateCb& stepCb) override; - CODE removeDeltaFirmwareFile() override; - CODE applyDeltaFirmwareFile(UpdateCb& stepCb) override; + CODE updateFumoLocal(int fd, UpdateCb& stepCb) override; + CODE fumoLocalInject(int fd, UpdateCb& stepCb) override; + CODE fumoLocalApply(UpdateCb& stepCb) override; + CODE fumoLocalCleanup() override; CODE resetHfa(const Json::Value& jArgs, UpdateCb& stepCb) override; CODE activate(const Json::Value& jArgs, UpdateCb& stepCb) override; CODE startOmaDm(UpdateCb& stepCb) override; diff --git a/include/mts/MTS_IO_ICellularRadio.h b/include/mts/MTS_IO_ICellularRadio.h index 3259041..e1edbcd 100644 --- a/include/mts/MTS_IO_ICellularRadio.h +++ b/include/mts/MTS_IO_ICellularRadio.h @@ -429,54 +429,69 @@ namespace MTS { virtual CODE updateFumo(const Json::Value& jArgs, UpdateCb& stepCb) = 0; /** - * @brief uploadDeltaFirmwareFile - upload delta file to the radio's internal memory. + * @brief updateFumoLocal - Performs the radio firmware upgrade using local firmware image. * - * This command uploads (injects) the whole delta firmware image to some place that - * can be later used by the radio to perform the Delta Radio Firmware Upgrade. - * - * This delta firmware image is NOT validated on the firmware image upload step. + * This command uploads (injects) the whole delta firmware image to the radio, performs the + * upgrade and waits for it to complete. * - * @param fd - file sescriptor of a file that shall be uploaded to the radio. - * @param stepCb - the callback to receive status updates during the upload. - * @return CODE::SUCCESS when the file was successfully uploaded, + * @param fd - file descriptor of a file that shall be injected to the radio. + * @param stepCb - callback to receive status updates during the firmware upgrade. + * @return CODE::SUCCESS when the firmware upgrade was successful, * CODE::INVALID_ARGS when the file can't be opened for reading, + * CODE::FAILURE when upgrade failed on the radio side, * CODE::NOT_APPLICABLE when not supported by this radio, * CODE::ERROR otherwise. */ - virtual CODE uploadDeltaFirmwareFile(int fd, UpdateCb& stepCb) = 0; + virtual CODE updateFumoLocal(int fd, UpdateCb& stepCb) = 0; /** - * @brief removeDeltaFirmwareFile - remove the delta file from the radio's internal memory. + * @brief fumoLocalInject - upload delta file to the radio's internal memory. * - * This command allows to remove the old delta firmware image from the radio's internal - * memory for cases when it's no longer needed. + * This command uploads (injects) the whole delta firmware image to some place that + * can be later used by the radio to perform the Delta Radio Firmware Upgrade. + * + * This delta firmware image is NOT validated on the firmware image upload step. * * @param fd - file sescriptor of a file that shall be uploaded to the radio. - * @return CODE::SUCCESS when the file was successfully deleted, - * CODE::FAILURE when the file can't be deleted (i.e. no such file), + * @param stepCb - callback to receive status updates during the upload. + * @return CODE::SUCCESS when the file was successfully uploaded, + * CODE::INVALID_ARGS when the file can't be opened for reading, * CODE::NOT_APPLICABLE when not supported by this radio, * CODE::ERROR otherwise. */ - virtual CODE removeDeltaFirmwareFile() = 0; + virtual CODE fumoLocalInject(int fd, UpdateCb& stepCb) = 0; /** - * @brief applyDeltaFirmwareFile - apply the delta file that was previously uploaded. + * @brief fumoLocalApply - apply the delta file that was previously uploaded. * * This commands initializes and tracks the progress of the delta firmware upgrade * procedure using the delta firmware image file that was previously uploaded - * into internal radio's memory. + * into internal memory of the radio. * - * See ICellularRadio::removeDeltaFirmwareFile to upload the file and prepare + * See ICellularRadio::fumoLocalInject to upload the file and prepare * for the upgrade. * - * @param fd - file sescriptor of a file that shall be uploaded to the radio. + * @param fd - file descriptor of a file that shall be uploaded to the radio. * @param stepCb - the callback to receive status updates during the upgrade. + * @return CODE::SUCCESS when the firmware upgrade was successful, + * CODE::FAILURE when upgrade failed on the radio side, + * CODE::NOT_APPLICABLE when not supported by this radio, + * CODE::ERROR otherwise. + */ + virtual CODE fumoLocalApply(UpdateCb& stepCb) = 0; + + /** + * @brief fumoLocalCleanup - remove the delta file from the radio's internal memory. + * + * This command allows to remove the old delta firmware image from the radio's internal + * memory for cases when it's no longer needed. + * * @return CODE::SUCCESS when the file was successfully deleted, * CODE::FAILURE when the file can't be deleted (i.e. no such file), * CODE::NOT_APPLICABLE when not supported by this radio, * CODE::ERROR otherwise. */ - virtual CODE applyDeltaFirmwareFile(UpdateCb& stepCb) = 0; + virtual CODE fumoLocalCleanup() = 0; /* * jArgs = { diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index cb455d8..b805ed8 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -45,9 +45,10 @@ namespace MTS { CODE setCellularMode(CELLULAR_MODES networks) override; - CODE uploadDeltaFirmwareFile(int fd, UpdateCb& stepCb) override; - CODE removeDeltaFirmwareFile() override; - CODE applyDeltaFirmwareFile(UpdateCb& stepCb) override; + CODE updateFumoLocal(int fd, UpdateCb& stepCb) override; + CODE fumoLocalInject(int fd, UpdateCb& stepCb) override; + CODE fumoLocalCleanup() override; + CODE fumoLocalApply(UpdateCb& stepCb) override; protected: QuectelRadio(const std::string& sName, const std::string& sRadioPort); @@ -76,9 +77,8 @@ namespace MTS { 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 waitApplyDeltaFirmware(UpdateCb& stepCb); - CODE waitFotaFinish(UpdateCb& stepCb); - CODE waitNewFirmware(UpdateCb& stepCb); + CODE fumoWaitUpgradeFinished(UpdateCb& stepCb); + CODE fumoWaitNewFirmware(UpdateCb& stepCb); }; } } diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp index 55f719f..bb7cd40 100644 --- a/src/MTS_IO_CellularRadio.cpp +++ b/src/MTS_IO_CellularRadio.cpp @@ -1051,20 +1051,26 @@ ICellularRadio::CODE CellularRadio::updateFumo(const Json::Value&, UpdateCb&) { return NOT_APPLICABLE; } -ICellularRadio::CODE CellularRadio::uploadDeltaFirmwareFile(int, ICellularRadio::UpdateCb&) { - printTrace("%s| Upload Delta Firmware Upgrade File: not applicable", m_sName.c_str()); +ICellularRadio::CODE CellularRadio::updateFumoLocal(int, ICellularRadio::UpdateCb&) { + printTrace("%s| Update Local Firmware Update Management Object", m_sName.c_str()); return NOT_APPLICABLE; } -ICellularRadio::CODE CellularRadio::removeDeltaFirmwareFile() { - printTrace("%s| Remove Delta Firmware Upgrade File: not applicable", m_sName.c_str()); +ICellularRadio::CODE CellularRadio::fumoLocalInject(int, ICellularRadio::UpdateCb&) { + printTrace("%s| Inject Delta Firmware Image File: not applicable", m_sName.c_str()); return NOT_APPLICABLE; } -ICellularRadio::CODE CellularRadio::applyDeltaFirmwareFile(ICellularRadio::UpdateCb&) { - printTrace("%s| Apply Delta Firmware Upgrade File: not applicable", m_sName.c_str()); +ICellularRadio::CODE CellularRadio::fumoLocalApply(ICellularRadio::UpdateCb&) { + printTrace("%s| Apply Delta Firmware Image File: not applicable", m_sName.c_str()); + + return NOT_APPLICABLE; +} + +ICellularRadio::CODE CellularRadio::fumoLocalCleanup() { + printTrace("%s| Cleanup Delta Firmware Image File: not applicable", m_sName.c_str()); return NOT_APPLICABLE; } diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index 25d2a30..7b67431 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -540,7 +540,21 @@ ICellularRadio::CODE QuectelRadio::startOmaDm(ICellularRadio::UpdateCb& stepCb) return eCode; } -ICellularRadio::CODE QuectelRadio::uploadDeltaFirmwareFile(int fd, ICellularRadio::UpdateCb& stepCb) { +ICellularRadio::CODE QuectelRadio::updateFumoLocal(int fd, ICellularRadio::UpdateCb& stepCb) { + CODE rc; + + rc = fumoLocalInject(fd, stepCb); + if (rc != SUCCESS) { + return rc; + } + + rc = fumoLocalApply(stepCb); + (void)fumoLocalCleanup(); // try to cleanup the injected file but cleanup errors are not fatal + + return rc; +} + +ICellularRadio::CODE QuectelRadio::fumoLocalInject(int fd, ICellularRadio::UpdateCb& stepCb) { CODE rc = FAILURE; bool bIsFilePresent = false; @@ -552,7 +566,7 @@ ICellularRadio::CODE QuectelRadio::uploadDeltaFirmwareFile(int fd, ICellularRadi } if (bIsFilePresent) { - rc = removeDeltaFirmwareFile(); + rc = fumoLocalCleanup(); } if (rc != SUCCESS) { @@ -571,16 +585,16 @@ ICellularRadio::CODE QuectelRadio::uploadDeltaFirmwareFile(int fd, ICellularRadi return rc; } -ICellularRadio::CODE QuectelRadio::removeDeltaFirmwareFile() { +ICellularRadio::CODE QuectelRadio::fumoLocalCleanup() { printTrace("Removing the delta upgrade file: %s", VALUE_MTS_DELTA_NAME.c_str()); return removeFile(VALUE_MTS_DELTA_NAME); } -ICellularRadio::CODE QuectelRadio::applyDeltaFirmwareFile(ICellularRadio::UpdateCb& stepCb) { +ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& stepCb) { ICellularRadio::CODE rc; std::string sCmd; - // Send "AT+QFOTADL" command to start the upgrade. OK responce follows shortly. + // Send "AT+QFOTADL" command to start the upgrade. OK response follows shortly. sCmd = "AT+QFOTADL=\""; sCmd += VALUE_MTS_DELTA_PATH; sCmd += "\""; @@ -594,16 +608,48 @@ ICellularRadio::CODE QuectelRadio::applyDeltaFirmwareFile(ICellularRadio::Update return rc; } - rc = waitApplyDeltaFirmware(stepCb); - if (rc != SUCCESS) { + const uint32_t duDetachTimeout = 10000; // wait for 10 seconds for the radio to detach + const uint32_t duAttachTimeout = 30000; // wait for 30 seconds for the radio to attach + const int dMaxAttempts = 5; // the radio makes 5 attempts to update the firmware + + for (int i = 0; i < dMaxAttempts; i++) { + + printInfo("Waiting for the radio to enter recovery mode"); if(stepCb) { - stepCb(Json::Value("FUMO Error: failed to apply the firmware")); + stepCb(Json::Value("FUMO Info: waiting for the radio to enter recovery mode")); } - //return rc; - } - // TODO: Check the new firmware version + // Wait for the radio to detach from the USB bus + MTS::Thread::sleep(duDetachTimeout); + + // It's now detached. Try to reconnect + if (!resetConnection(duAttachTimeout)) { + printError("Can't connect to the radio in %d ms", (duAttachTimeout)); + + if(stepCb) { + stepCb(Json::Value("FUMO error: failed to connect to the radio in recovery mode")); + } + break; + } + + // It's now back on the bus. Wait for the URC messages. + rc = fumoWaitUpgradeFinished(stepCb); + if (rc == ERROR) { + // unrecoverable error + break; + } + + if (rc != SUCCESS) { + // attempt failed, radio reboots and starts its next attempt + continue; + } + + // attempt finished + break; + } + // Wait for the radio to finish update and reboot + rc = fumoWaitNewFirmware(stepCb); return rc; } @@ -979,56 +1025,7 @@ ICellularRadio::CODE QuectelRadio::readChunk(int fd, char* pChunk, size_t dChunk return rc; } -ICellularRadio::CODE QuectelRadio::waitApplyDeltaFirmware(ICellularRadio::UpdateCb& stepCb) { - const uint32_t duDetachTimeout = 10000; // wait for 10 seconds for the radio to detach - const uint32_t duAttachTimeout = 30000; // wait for 30 seconds for the radio to attach - const int dMaxAttempts = 5; // the radio makes 5 attempts to update the firmware - - CODE rc = FAILURE; - std::string sResponse; - - for (int i = 0; i < dMaxAttempts; i++) { - - printInfo("Waiting for the radio to enter recovery mode"); - if(stepCb) { - stepCb(Json::Value("FUMO Info: waiting for the radio to enter recovery mode")); - } - - // Wait for the radio to detach from the USB bus - MTS::Thread::sleep(duDetachTimeout); - - // It's now detached. Try to reconnect - if (!resetConnection(duAttachTimeout)) { - printError("Can't connect to the radio in %d ms", (duAttachTimeout)); - - if(stepCb) { - stepCb(Json::Value("FUMO error: failed to connect to the radio in recovery mode")); - } - break; - } - - // It's now back on the bus. Wait for the URC messages. - rc = waitFotaFinish(stepCb); - if (rc == ERROR) { - // unrecoverable error - break; - } - - if (rc != SUCCESS) { - // attempt failed, radio reboots and starts its next attempt - continue; - } - - // attempt finished - break; - } - - // Wait for the radio to finish update and reboot - rc = waitNewFirmware(stepCb); - return rc; -} - -ICellularRadio::CODE QuectelRadio::waitFotaFinish(ICellularRadio::UpdateCb& stepCb) { +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 const std::string sFotaUrcStart = "\"START\""; @@ -1082,7 +1079,7 @@ ICellularRadio::CODE QuectelRadio::waitFotaFinish(ICellularRadio::UpdateCb& step return rc; } -ICellularRadio::CODE QuectelRadio::waitNewFirmware(ICellularRadio::UpdateCb& stepCb) { +ICellularRadio::CODE QuectelRadio::fumoWaitNewFirmware(ICellularRadio::UpdateCb& stepCb) { MTS::Timer oTimer; oTimer.start(); std::string sFirmware; -- cgit v1.2.3 From d19829b7e9cdd3c7e3d209628044ab62cd46b907 Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Fri, 3 Jul 2020 18:53:57 +0300 Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Simplified usage of step callbacks in the QuectelRadio class. Refactored existing function to use a wrapper and preven repetitive checks. --- include/mts/MTS_IO_QuectelRadio.h | 15 +++++++++ src/MTS_IO_QuectelRadio.cpp | 68 ++++++++++++--------------------------- 2 files changed, 36 insertions(+), 47 deletions(-) diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index b805ed8..3751b5a 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -72,6 +72,9 @@ namespace MTS { CODE startFileUpload(const std::string& sTargetFilename, size_t nBytes); CODE abortFileUpload(); + static inline void callNextStep(UpdateCb& stepCb, const char* csMessage); + static inline void callNextStep(UpdateCb& stepCb, const std::string& sMessage); + 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); @@ -83,6 +86,18 @@ namespace MTS { } } +void MTS::IO::QuectelRadio::callNextStep(ICellularRadio::UpdateCb& stepCb, const char* csMessage) { + if (stepCb) { + stepCb(Json::Value(csMessage)); + } +} + +void MTS::IO::QuectelRadio::callNextStep(ICellularRadio::UpdateCb& stepCb, const std::string& sMessage) { + if (stepCb) { + stepCb(Json::Value(sMessage)); + } +} + void MTS::IO::QuectelRadio::updateQuectelChecksum(uint16_t& iChecksum, uint16_t iNewFragment) { iChecksum = iChecksum ^ iNewFragment; } diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index 7b67431..cbc2cfb 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -476,9 +476,7 @@ ICellularRadio::CODE QuectelRadio::startOmaDm(ICellularRadio::UpdateCb& stepCb) eCode = sendBasicCommand(sCmdOdmStart, iTimeoutOk); if (eCode != SUCCESS) { printError("%s| OMA DM procedure can not be started", getName().c_str()); - if (stepCb) { - stepCb(Json::Value("OMA DM Error: OMA DM can not be started")); - } + callNextStep(stepCb, "OMA DM Error: OMA DM can not be started"); break; } @@ -489,18 +487,14 @@ ICellularRadio::CODE QuectelRadio::startOmaDm(ICellularRadio::UpdateCb& stepCb) // Received something unexpected or nothing at all? if (sResponse.find(sOdmStarted) == std::string::npos) { printError("%s| OMA DM procedure failed due to timeout", getName().c_str()); - if (stepCb) { - stepCb(Json::Value("OMA DM Error: OMA DM failed due to timeout")); - } + callNextStep(stepCb, "OMA DM Error: OMA DM failed due to timeout"); eCode = FAILURE; break; } // Got "DM Started" message from the radio printTrace("%s| OMA DM started", getName().c_str()); - if (stepCb) { - stepCb(Json::Value("OMA DM Info: OMA DM started")); - } + callNextStep(stepCb, "OMA DM Info: OMA DM started"); // Wait for the "End" or "Abnormal" response sResponse = sendCommand("", vOdmFinishedStrings, iTimeoutEnd, 0x00); @@ -509,9 +503,8 @@ ICellularRadio::CODE QuectelRadio::startOmaDm(ICellularRadio::UpdateCb& stepCb) // Received "Abnormal"? if (sResponse.find(sOdmAbnormal) != std::string::npos) { printError("%s| OMA DM procedure failed due to internal error: [%s]", getName().c_str(), sResponse.c_str()); - if (stepCb) { - stepCb(Json::Value("OMA DM Error: OMA DM failed due to internal error")); - } + callNextStep(stepCb, "OMA DM Error: OMA DM failed due to internal error"); + eCode = FAILURE; break; } @@ -519,9 +512,8 @@ ICellularRadio::CODE QuectelRadio::startOmaDm(ICellularRadio::UpdateCb& stepCb) // Received something unexpected or nothing at all? if (sResponse.find(sOdmFinished) == std::string::npos) { printError("%s| OMA DM procedure failed due to timeout", getName().c_str()); - if (stepCb) { - stepCb(Json::Value("OMA DM Error: OMA DM failed due to timeout")); - } + callNextStep(stepCb, "OMA DM Error: OMA DM failed due to timeout"); + sendBasicCommand(sCmdOdmAbort, iTimeoutAbort); // abort the procedure eCode = FAILURE; break; @@ -529,9 +521,7 @@ ICellularRadio::CODE QuectelRadio::startOmaDm(ICellularRadio::UpdateCb& stepCb) // Got "DM End" message from the radio printTrace("%s| OMA DM finished", getName().c_str()); - if (stepCb) { - stepCb(Json::Value("OMA DM Info: OMA DM finished")); - } + callNextStep(stepCb, "OMA DM Info: OMA DM finished"); eCode = SUCCESS; @@ -602,9 +592,7 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step rc = sendBasicCommand(sCmd, 1000); if (rc != SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO Error: failed to apply the firmware")); - } + callNextStep(stepCb, "FUMO Error: failed to apply the firmware"); return rc; } @@ -615,9 +603,7 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step for (int i = 0; i < dMaxAttempts; i++) { printInfo("Waiting for the radio to enter recovery mode"); - if(stepCb) { - stepCb(Json::Value("FUMO Info: 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); @@ -626,9 +612,7 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step if (!resetConnection(duAttachTimeout)) { printError("Can't connect to the radio in %d ms", (duAttachTimeout)); - if(stepCb) { - stepCb(Json::Value("FUMO error: failed to connect to the radio in recovery mode")); - } + callNextStep(stepCb, "FUMO error: failed to connect to the radio in recovery mode"); break; } @@ -836,9 +820,7 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget } printTrace("File upload started."); - if (stepCb) { - stepCb(Json::Value("FILE info: Started file upload for " + sTargetFilename)); - } + callNextStep(stepCb, "FILE info: Started file upload for " + sTargetFilename); uint16_t dChecksum = 0; size_t nChunksPerCent = (nChunks / 100) + 1; @@ -863,14 +845,14 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget if (stepCb && ((iChunk % nChunksPerCent) == 0)) { size_t dPercentsCompleted = iChunk / nChunksPerCent; - stepCb(Json::Value("FILE info: Uploaded " + MTS::Text::format(dPercentsCompleted) + "%")); + callNextStep(stepCb, "FILE info: Uploaded " + MTS::Text::format(dPercentsCompleted) + "%"); } } if (rc != SUCCESS) { // cancel upload and terminate - stepCb(Json::Value("FILE error: Upload failed due to internal error")); + callNextStep(stepCb, "FILE error: Upload failed due to internal error"); abortFileUpload(); return rc; } @@ -893,10 +875,10 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget rc = FAILURE; } - if (stepCb && rc == SUCCESS) { - stepCb(Json::Value("FILE info: Upload finished successfully")); - } else if (stepCb) { - stepCb(Json::Value("FILE error: Upload failed due to internal error")); + if (rc == SUCCESS) { + callNextStep(stepCb, "FILE info: Upload finished successfully"); + } else { + callNextStep(stepCb, "FILE error: Upload failed due to internal error"); } return rc; @@ -1042,11 +1024,7 @@ ICellularRadio::CODE QuectelRadio::fumoWaitUpgradeFinished(ICellularRadio::Updat if (sResponse.find(sFotaUrcPrefix) == std::string::npos) { printError("No URC messages from the radio in %d ms", duUrcTimeout); - - if(stepCb) { - stepCb(Json::Value("FUMO error: timeout, radio is not responding")); - } - + callNextStep(stepCb, "FUMO error: timeout, radio is not responding"); break; } @@ -1112,14 +1090,10 @@ ICellularRadio::CODE QuectelRadio::fumoWaitNewFirmware(ICellularRadio::UpdateCb& oTimer.stop(); if (rc == SUCCESS) { - if(stepCb) { - stepCb(Json::Value("FUMO done: radio firmware applied successfully")); - } + callNextStep(stepCb, "FUMO done: radio firmware applied successfully"); } else { - if(stepCb) { - stepCb(Json::Value("FUMO error: radio firmware has not been updated")); - } + callNextStep(stepCb, "FUMO error: radio firmware has not been updated"); } return rc; -- cgit v1.2.3 From 075c19d664719808164a5c4ea5e5a3525fcb4702 Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Fri, 3 Jul 2020 22:03:53 +0300 Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Cleanup for the printouts and status messages. --- src/MTS_IO_QuectelRadio.cpp | 52 +++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index cbc2cfb..03593a7 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -549,9 +549,12 @@ ICellularRadio::CODE QuectelRadio::fumoLocalInject(int fd, ICellularRadio::Updat bool bIsFilePresent = false; do { + callNextStep(stepCb, "FUMO Info: downloading the firmware"); + rc = checkFile(bIsFilePresent, VALUE_MTS_DELTA_NAME); if (rc != SUCCESS) { - printError("Failed to check if the delta file was already uploaded."); + printError("Failed to check if the delta file was already download."); + callNextStep(stepCb, "FUMO Error: failed to download the firmware file"); break; } @@ -561,15 +564,19 @@ ICellularRadio::CODE QuectelRadio::fumoLocalInject(int fd, ICellularRadio::Updat if (rc != SUCCESS) { printError("Failed to remove the previous delta file."); + callNextStep(stepCb, "FUMO Error: failed to download the firmware file"); break; } rc = uploadFile(fd, VALUE_MTS_DELTA_NAME, stepCb); if (rc != SUCCESS) { - printError("Failed to upload the delta file."); + printError("Failed to inject the delta file."); + callNextStep(stepCb, "FUMO Error: failed to download the firmware file"); break; } + callNextStep(stepCb, "FUMO Info: firmware downloaded successfully"); + } while (false); return rc; @@ -592,6 +599,7 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step rc = sendBasicCommand(sCmd, 1000); if (rc != SUCCESS) { + printError("FUMO failed, OK not received from the radio"); callNextStep(stepCb, "FUMO Error: failed to apply the firmware"); return rc; } @@ -611,29 +619,44 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step // 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: failed to connect to the radio in recovery mode"); + callNextStep(stepCb, "FUMO Error: unable to obtain radio after reset"); break; } // It's now back on the bus. Wait for the URC messages. + printInfo("Applying the radio firmware"); + callNextStep(stepCb, "FUMO Info: applying the radio firmware"); rc = fumoWaitUpgradeFinished(stepCb); + if (rc == ERROR) { // unrecoverable error + callNextStep(stepCb, "FUMO Error: failed to apply the firmware, consider radio reset"); break; } if (rc != SUCCESS) { // attempt failed, radio reboots and starts its next attempt + printError("Failed to apply the firmware, attempts left: %d", (dMaxAttempts - i - 1)); + callNextStep(stepCb, "FUMO Error: failed to apply the firmware"); continue; } - // attempt finished + // 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 = fumoWaitNewFirmware(stepCb); break; } - // Wait for the radio to finish update and reboot - rc = fumoWaitNewFirmware(stepCb); + 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; } @@ -1015,7 +1038,7 @@ ICellularRadio::CODE QuectelRadio::fumoWaitUpgradeFinished(ICellularRadio::Updat const std::string sFotaUrcEnd = "\"END\""; const std::vector vFotaBailStrings{ sFotaUrcPrefix }; - CODE rc = ERROR; + CODE rc = FAILURE; std::string sResponse; while (true) { // breaks on "FOTA","END" @@ -1025,6 +1048,7 @@ ICellularRadio::CODE QuectelRadio::fumoWaitUpgradeFinished(ICellularRadio::Updat 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; } @@ -1041,7 +1065,7 @@ ICellularRadio::CODE QuectelRadio::fumoWaitUpgradeFinished(ICellularRadio::Updat break; } // attempt failed, the radio attempts to recover - rc = FAILURE; + callNextStep(stepCb, "FUMO Error: radio returned error code " + sCode); break; } else if (sStage == sFotaUrcStart) { printTrace("Got FOTA START message"); @@ -1049,6 +1073,7 @@ ICellularRadio::CODE QuectelRadio::fumoWaitUpgradeFinished(ICellularRadio::Updat printTrace("Got FOTA progress message"); const std::string& sPercents = vParts[2]; printInfo("FOTA progress: [%s]", sPercents.c_str()); + callNextStep(stepCb, "FUMO Info: firmware apply progress " + sPercents); } else { printInfo("FOTA unexpected URC code: [%s]", sStage.c_str()); } @@ -1074,6 +1099,7 @@ ICellularRadio::CODE QuectelRadio::fumoWaitNewFirmware(ICellularRadio::UpdateCb& } #if 0 + // TODO: Implement the version check if (sFirmware == m_sFw && sFirmwareBuild == m_sFwBuild) { // Have the same firmware. The radio resets several time // before the firmware is actually get upgraded. So keep polling. @@ -1089,11 +1115,9 @@ ICellularRadio::CODE QuectelRadio::fumoWaitNewFirmware(ICellularRadio::UpdateCb& } oTimer.stop(); - if (rc == SUCCESS) { - callNextStep(stepCb, "FUMO done: radio firmware applied successfully"); - } - else { - callNextStep(stepCb, "FUMO error: radio firmware has not been updated"); + if (rc != SUCCESS) { + printError("Radio is not responding"); + callNextStep(stepCb, "FUMO Error: unable to obtain radio after reset"); } return rc; -- cgit v1.2.3 From 0d5806073d2e9bbd7259c474b4079991e0c5ef13 Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Fri, 3 Jul 2020 22:59:02 +0300 Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Implemented firmware version check detection. --- include/mts/MTS_IO_QuectelRadio.h | 7 ++++++ src/MTS_IO_QuectelRadio.cpp | 52 +++++++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index 3751b5a..d8228cb 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -64,11 +64,18 @@ namespace MTS { virtual CODE checkFile(bool& bFilePresent, const std::string& sTargetFilename); private: + // 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; + // TODO: Consider asbtracting to the ICellularRadio::getFirmwareBuild + //!< Get Quectel-specific firmware version (firmware build?) + CODE getQuectelFirmware(std::string& sFirmware); + CODE startFileUpload(const std::string& sTargetFilename, size_t nBytes); CODE abortFileUpload(); diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index 03593a7..9a95046 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -591,6 +591,13 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step ICellularRadio::CODE rc; std::string sCmd; + rc = getQuectelFirmware(m_sQuectelFirmware); + if (rc != SUCCESS) { + callNextStep(stepCb, "FUMO Error: Failed to obtain current firmware version"); + return rc; + } + printInfo("Current firmware version: %s", m_sQuectelFirmware.c_str()); + // Send "AT+QFOTADL" command to start the upgrade. OK response follows shortly. sCmd = "AT+QFOTADL=\""; sCmd += VALUE_MTS_DELTA_PATH; @@ -941,6 +948,26 @@ ICellularRadio::CODE QuectelRadio::checkFile(bool& bIsFilePresent, const std::st return SUCCESS; } +ICellularRadio::CODE QuectelRadio::getQuectelFirmware(std::string& sFirmware) { + printTrace("%s| Get Quectel-specific firmware version", getName().c_str()); + sFirmware = ICellularRadio::VALUE_NOT_SUPPORTED; + std::string sCmd("AT+QGMR"); + 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; +} + uint16_t QuectelRadio::getQuectelChecksum(const void* data, size_t nBytes) { auto castData = static_cast(data); uint16_t iChecksum = 0; @@ -1085,37 +1112,36 @@ ICellularRadio::CODE QuectelRadio::fumoWaitUpgradeFinished(ICellularRadio::Updat ICellularRadio::CODE QuectelRadio::fumoWaitNewFirmware(ICellularRadio::UpdateCb& stepCb) { MTS::Timer oTimer; oTimer.start(); - std::string sFirmware; - CODE rc = FAILURE; + std::string sQuectelFirmware; + CODE rc = ERROR; while (oTimer.getSeconds() < (5 * 60)) { // 5 minutes MTS::Thread::sleep(10000); - if (getFirmware(sFirmware) != SUCCESS) { + if (getQuectelFirmware(sQuectelFirmware) != SUCCESS) { // The radio is probably unavailable resetConnection(100); continue; } -#if 0 - // TODO: Implement the version check - if (sFirmware == m_sFw && sFirmwareBuild == m_sFwBuild) { - // Have the same firmware. The radio resets several time - // before the firmware is actually get upgraded. So keep polling. - continue; + printInfo("Firmware version before the upgrade: %s", m_sQuectelFirmware.c_str()); + printInfo("Current firmware version: %s", sQuectelFirmware.c_str()); + + if (sQuectelFirmware == m_sQuectelFirmware) { + // Radio will not reset anymore, firmware version left the same, not updated + printError("Radio firmware version not changed after upgrade"); + rc = FAILURE; + break; } -#endif // The firmware numbers have changed - printTrace("Current firmware: %s", sFirmware.c_str()); rc = SUCCESS; break; - } oTimer.stop(); - if (rc != SUCCESS) { + if (rc == ERROR) { printError("Radio is not responding"); callNextStep(stepCb, "FUMO Error: unable to obtain radio after reset"); } -- cgit v1.2.3 From 02d9adc99e2bebde36ad43eb17e8b322cc1014ec Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Mon, 6 Jul 2020 09:26:03 +0300 Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Fixed format of FILE upload log messages. --- src/MTS_IO_QuectelRadio.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index 9a95046..e5e3296 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -850,7 +850,7 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget } printTrace("File upload started."); - callNextStep(stepCb, "FILE info: Started file upload for " + sTargetFilename); + callNextStep(stepCb, "FILE Info: Started file upload for " + sTargetFilename); uint16_t dChecksum = 0; size_t nChunksPerCent = (nChunks / 100) + 1; @@ -875,14 +875,14 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget if (stepCb && ((iChunk % nChunksPerCent) == 0)) { size_t dPercentsCompleted = iChunk / nChunksPerCent; - callNextStep(stepCb, "FILE info: Uploaded " + MTS::Text::format(dPercentsCompleted) + "%"); + callNextStep(stepCb, "FILE Info: Uploaded " + MTS::Text::format(dPercentsCompleted) + "%"); } } if (rc != SUCCESS) { // cancel upload and terminate - callNextStep(stepCb, "FILE error: Upload failed due to internal error"); + callNextStep(stepCb, "FILE Error: Upload failed due to internal error"); abortFileUpload(); return rc; } @@ -906,9 +906,9 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget } if (rc == SUCCESS) { - callNextStep(stepCb, "FILE info: Upload finished successfully"); + callNextStep(stepCb, "FILE Info: Upload finished successfully"); } else { - callNextStep(stepCb, "FILE error: Upload failed due to internal error"); + callNextStep(stepCb, "FILE Error: Upload failed due to internal error"); } return rc; -- cgit v1.2.3 From 63b1b15a510ff3acdc4f93bbac1ed14548e6620b Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Mon, 6 Jul 2020 10:22:45 +0300 Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Fixed message format for FUMO Done and FUMO Error messages. The original code with "FUMO done" ("done" lowercase) and "FUMO error" ("error" lowercase) was copied from the ME910C1-WW implementation. At the same time other places of the code use Title Case for "Done", "Error" and "Info" messages. This commit fixes the last bunch of messages in the QuectelRadio Delta FWU implementation to use Title Case. This simplifies handling in various scripts and other components. --- src/MTS_IO_QuectelRadio.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index e5e3296..7aeed96 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -658,10 +658,10 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step if (rc == SUCCESS) { printInfo("Radio firmware applied successfully"); - callNextStep(stepCb, "FUMO done: 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"); + callNextStep(stepCb, "FUMO Error: radio firmware has not been updated"); } return rc; -- cgit v1.2.3