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. --- src/MTS_IO_CellularRadio.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src') 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. --- src/MTS_IO_QuectelRadio.cpp | 82 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) (limited to 'src') 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. --- src/MTS_IO_CellularRadio.cpp | 17 ++++ src/MTS_IO_ICellularRadio.cpp | 2 +- src/MTS_IO_QuectelRadio.cpp | 206 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 223 insertions(+), 2 deletions(-) (limited to 'src') 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. --- src/MTS_IO_QuectelRadio.cpp | 179 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) (limited to 'src') 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 --- src/MTS_IO_CellularRadio.cpp | 18 ++++--- src/MTS_IO_QuectelRadio.cpp | 121 +++++++++++++++++++++---------------------- 2 files changed, 71 insertions(+), 68 deletions(-) (limited to 'src') 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. --- src/MTS_IO_QuectelRadio.cpp | 68 ++++++++++++++------------------------------- 1 file changed, 21 insertions(+), 47 deletions(-) (limited to 'src') 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(-) (limited to 'src') 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. --- src/MTS_IO_QuectelRadio.cpp | 52 +++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 13 deletions(-) (limited to 'src') 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(-) (limited to 'src') 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(-) (limited to 'src') 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