From 26f6e69e9d5c7049d42559259933482b35559889 Mon Sep 17 00:00:00 2001
From: Serhii Kostiuk <serhii.o.kostiuk@globallogic.com>
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 <serhii.o.kostiuk@globallogic.com>
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 <filename>.
+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 <serhii.o.kostiuk@globallogic.com>
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<std::string> 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 <mts/MTS_Thread.h>
 #include <mts/MTS_Text.h>
 
+#include <unistd.h>
+
 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 <filename>.
 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<char, FILE_CHUNK_SIZE> 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<const uint8_t*>(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<size_t>(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<size_t>(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<std::string> 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 <serhii.o.kostiuk@globallogic.com>
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 <mts/MTS_Logger.h>
 #include <mts/MTS_Thread.h>
 #include <mts/MTS_Text.h>
+#include <mts/MTS_Timer.h>
 
 #include <unistd.h>
 
@@ -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<std::string> 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<std::string> 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 <serhii.o.kostiuk@globallogic.com>
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 <serhii.o.kostiuk@globallogic.com>
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 <serhii.o.kostiuk@globallogic.com>
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<std::string> 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 <serhii.o.kostiuk@globallogic.com>
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<const uint8_t*>(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 <serhii.o.kostiuk@globallogic.com>
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 <serhii.o.kostiuk@globallogic.com>
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


From 39ecabe5d948575f6924fedd3946968c8783dda1 Mon Sep 17 00:00:00 2001
From: Serhii Kostiuk <serhii.o.kostiuk@globallogic.com>
Date: Fri, 10 Jul 2020 12:22:19 +0300
Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io
 implementation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Small fixes and improvements.

1. Fixed casing for the "FUMO Error: timeout, radio is not responding" message.

   All error messages passed to the status callback shall start with "FUMO Error:"

2. Removed extra carriage return for the "abortFileUpload" command.

   Passing "+++\r" instead of the "+++" to the radio works for Quectel but cause
   issues for Telit.

   At the same time Quectel specificly requires to
   "Do not input any character within 1s after “+++” has been inputted."
   in their FILE manual. Possibly, "any character" includes carriage return.

3. Fixed a typo: "lenght" -> "length"
---
 src/MTS_IO_QuectelRadio.cpp | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

(limited to 'src')

diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp
index 7aeed96..22374ee 100644
--- a/src/MTS_IO_QuectelRadio.cpp
+++ b/src/MTS_IO_QuectelRadio.cpp
@@ -833,18 +833,18 @@ ICellularRadio::CODE QuectelRadio::setCellularMode(CELLULAR_MODES networks) {
 }
 
 ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTargetFilename, ICellularRadio::UpdateCb& stepCb) {
-    size_t dPayloadLenght;
+    size_t dPayloadLength;
     size_t nChunks;
     CODE rc;
 
-    rc = getFileSize(fd, dPayloadLenght, nChunks);
+    rc = getFileSize(fd, dPayloadLength, nChunks);
     if (rc != SUCCESS) {
         return rc;
     }
-    printTrace("File size: %d bytes and %d chunks", dPayloadLenght, nChunks);
+    printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks);
     printTrace("Starting file upload...");
 
-    rc = startFileUpload(sTargetFilename, dPayloadLenght);
+    rc = startFileUpload(sTargetFilename, dPayloadLength);
     if (rc != SUCCESS) {
         return rc;
     }
@@ -854,21 +854,21 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget
 
     uint16_t dChecksum = 0;
     size_t nChunksPerCent = (nChunks / 100) + 1;
-    size_t nFragmentLenght = 0;
+    size_t nFragmentLength = 0;
     std::array<char, FILE_CHUNK_SIZE> vBuffer;
 
     for (size_t iChunk = 1; iChunk < (nChunks + 1); iChunk++) {
 
-        rc = readChunk(fd, vBuffer.data(), vBuffer.size(), nFragmentLenght);
+        rc = readChunk(fd, vBuffer.data(), vBuffer.size(), nFragmentLength);
         if (rc != SUCCESS) {
             break;
         }
 
         // we got our fragment, calculate checksum and flush the data
-        uint16_t dFragmentChecksum = getQuectelChecksum(vBuffer.data(), nFragmentLenght);
+        uint16_t dFragmentChecksum = getQuectelChecksum(vBuffer.data(), nFragmentLength);
         updateQuectelChecksum(dChecksum, dFragmentChecksum);
 
-        rc = sendData(vBuffer.data(), nFragmentLenght);
+        rc = sendData(vBuffer.data(), nFragmentLength);
         if (rc != SUCCESS) {
             break;
         }
@@ -889,7 +889,7 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget
 
     printTrace("Waiting for acknoledge from the radio");
     std::string sExpectedResult = "+QFUPL: ";
-    sExpectedResult += MTS::Text::format(dPayloadLenght);
+    sExpectedResult += MTS::Text::format(dPayloadLength);
     sExpectedResult += ",";
     sExpectedResult += MTS::Text::toLowerCase(MTS::Text::formatHex(dChecksum));
 
@@ -1074,7 +1074,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");
+            callNextStep(stepCb, "FUMO Error: timeout, radio is not responding");
             rc = ERROR;
             break;
         }
@@ -1176,5 +1176,5 @@ ICellularRadio::CODE QuectelRadio::abortFileUpload() {
      * 3) Do not input any character within 1s after “+++” has been inputted.
      */
     sleep(1);
-    return sendBasicCommand(CMD_ABORT_UPLOAD, 2000);
+    return sendBasicCommand(CMD_ABORT_UPLOAD, 2000, 0x00);
 }
-- 
cgit v1.2.3


From e09f54c6b6fe35dc022fcea66935f5269866bff1 Mon Sep 17 00:00:00 2001
From: Andrii Pientsov <andrii.pientsov@globallogic.com>
Date: Fri, 10 Jul 2020 15:40:48 +0300
Subject: MTX-3404 mPower Oct20: Delta Radio Firmware Upgrade - L4E1 -
 libmts-io support

---
 src/MTS_IO_LE910C4EURadio.cpp |   4 +
 src/MTS_IO_TelitRadio.cpp     | 383 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 387 insertions(+)

(limited to 'src')

diff --git a/src/MTS_IO_LE910C4EURadio.cpp b/src/MTS_IO_LE910C4EURadio.cpp
index 029a921..4cf146b 100644
--- a/src/MTS_IO_LE910C4EURadio.cpp
+++ b/src/MTS_IO_LE910C4EURadio.cpp
@@ -29,3 +29,7 @@ LE910C4EURadio::LE910C4EURadio(const std::string& sPort)
 {
 
 }
+
+TelitRadio::FOTA_GROUP LE910C4EURadio::getFotaGroup() {
+    return VALUE_GROUP_B;
+}
diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp
index be31b03..967baae 100644
--- a/src/MTS_IO_TelitRadio.cpp
+++ b/src/MTS_IO_TelitRadio.cpp
@@ -24,8 +24,14 @@
 #include <mts/MTS_Thread.h>
 #include <mts/MTS_Text.h>
 
+#include <unistd.h>
+
 using namespace MTS::IO;
 
+const size_t TelitRadio::FILE_CHUNK_SIZE = 1024;
+const std::string TelitRadio::CMD_ABORT_UPLOAD = "+++";
+
+
 TelitRadio::TelitRadio(const std::string& sName, const std::string& sRadioPort)
 : CellularRadio(sName, sRadioPort)
 {
@@ -730,3 +736,380 @@ ICellularRadio::CODE TelitRadio::setCellularMode(CELLULAR_MODES networks) {
     }
     return SUCCESS;
 }
+
+ICellularRadio::CODE TelitRadio::updateFumoLocal(int fd, ICellularRadio::UpdateCb& stepCb) {
+    CODE rc = FAILURE;
+    FOTA_GROUP group = getFotaGroup();
+
+    if (group == VALUE_UNKNOWN) {
+        printError("Delta firmware upgrade is not supported for this type of radio modem");
+        callNextStep(stepCb, "FUMO Error: delta firmware upgrade is not supported for this type of radio modem");
+        return rc;
+    }
+
+    rc = fumoLocalInject(fd, stepCb);
+    if (rc != SUCCESS) {
+        return rc;
+    }
+
+    rc = fumoLocalApply(stepCb);
+
+    return rc;
+}
+
+ICellularRadio::CODE TelitRadio::fumoLocalInject(int fd, ICellularRadio::UpdateCb& stepCb) {
+    CODE rc = FAILURE;
+    FOTA_GROUP group = getFotaGroup();
+
+    do {
+        callNextStep(stepCb, "FUMO Info: downloading the firmware");
+
+        if ((group == VALUE_GROUP_A) || (group == VALUE_GROUP_B) || (group == VALUE_GROUP_D)) {
+            rc = uploadFile(fd, stepCb);
+            if (rc != SUCCESS) {
+                printError("Failed to inject the delta file.");
+                callNextStep(stepCb, "FUMO Error: failed to download the firmware file");
+                break;
+            }
+        } else if (group == VALUE_GROUP_C) {
+            //TODO Not Implemented
+            printError("Failed to inject the delta file.");
+            callNextStep(stepCb, "FUMO Error: not implemented");
+            break;
+        }
+
+        callNextStep(stepCb, "FUMO Info: firmware downloaded successfully");
+
+    } while (false);
+
+    return rc;
+}
+
+ICellularRadio::CODE TelitRadio::fumoLocalApply(ICellularRadio::UpdateCb& stepCb) {
+    ICellularRadio::CODE rc;
+    std::string sCmd;
+    FOTA_GROUP group = getFotaGroup();
+
+    rc = getTelitFirmware(m_sTelitFirmware);
+    if (rc != SUCCESS) {
+        callNextStep(stepCb, "FUMO Error: Failed to obtain current firmware version");
+        return rc;
+    }
+    printInfo("Current firmware version: %s", m_sTelitFirmware.c_str());
+
+    if ((group == VALUE_GROUP_A) || (group == VALUE_GROUP_D)) {
+        // Send "AT#OTAUP=0,0" command to start the upgrade. OK response follows shortly.
+        sCmd  = "AT#OTAUP=0,0";
+    } else if ((group == VALUE_GROUP_B) || (group == VALUE_GROUP_C)) {
+        // Send "AT#OTAUP=2" command to start the upgrade. OK response follows shortly.
+        sCmd  = "AT#OTAUP=2";
+    }
+
+    rc = sendBasicCommand(sCmd, 2000);
+
+    if (rc != SUCCESS) {
+        printError("FUMO failed, OK not received from the radio");
+        callNextStep(stepCb, "FUMO Error: failed to apply the firmware");
+        return rc;
+    }
+
+    const uint32_t duDetachTimeout = 10000;  // wait for 10 seconds for the radio to detach
+    const uint32_t duAttachTimeout = 300000;  // wait for 300 seconds for the radio to attach
+
+    do {
+        printInfo("Waiting for the radio to enter recovery mode");
+        callNextStep(stepCb, "FUMO Info: waiting for the radio to enter recovery mode");
+
+        // Wait for the radio to detach from the USB bus
+        MTS::Thread::sleep(duDetachTimeout);
+
+        printInfo("Firmware update in progress");
+        callNextStep(stepCb, "FUMO Info: firmware update in progress");
+
+        // It's now detached. Try to reconnect
+        if (!resetConnection(duAttachTimeout)) {
+            printError("Can't connect to the radio in %d ms", (duAttachTimeout));
+            callNextStep(stepCb, "FUMO Error: unable to obtain radio after reset");
+            rc = ERROR;
+            break;
+        }
+
+        // Wait for the radio to finish update and reboot
+        printTrace("Waiting for the radio to come up");
+        callNextStep(stepCb, "FUMO Info: waiting for the radio to enter normal mode");
+        rc = fumoWaitRadioBooted(stepCb);
+
+        if (rc != SUCCESS) {
+            break;
+        }
+
+        rc = fumoCheckNewFirmware(stepCb);
+
+    } while (false);
+
+
+    if (rc == SUCCESS) {
+        printInfo("Radio firmware applied successfully");
+        callNextStep(stepCb, "FUMO Done: radio firmware applied successfully");
+    } else {
+        printError("Radio firmware has not been updated");
+        callNextStep(stepCb, "FUMO Error: radio firmware has not been updated");
+    }
+
+    return rc;
+}
+
+TelitRadio::FOTA_GROUP TelitRadio::getFotaGroup() {
+    return VALUE_UNKNOWN;
+}
+
+ICellularRadio::CODE TelitRadio::uploadFile(int fd, ICellularRadio::UpdateCb& stepCb) {
+    size_t dPayloadLength;
+    size_t nChunks;
+    CODE rc;
+
+    rc = getFileSize(fd, dPayloadLength, nChunks);
+    if (rc != SUCCESS) {
+        return rc;
+    }
+    printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks);
+    printTrace("Starting file upload...");
+
+    rc = startFileUpload();
+    if (rc != SUCCESS) {
+        return rc;
+    }
+
+    printTrace("File upload started.");
+    callNextStep(stepCb, "FILE Info: Started file upload");
+
+    size_t nChunksPerCent = (nChunks / 100) + 1;
+    size_t nFragmentLength = 0;
+    std::array<char, FILE_CHUNK_SIZE> vBuffer;
+
+    for (size_t iChunk = 1; iChunk < (nChunks + 1); iChunk++) {
+
+        rc = readChunk(fd, vBuffer.data(), vBuffer.size(), nFragmentLength);
+        if (rc != SUCCESS) {
+            break;
+        }
+
+        rc = sendData(vBuffer.data(), nFragmentLength);
+        if (rc != SUCCESS) {
+            break;
+        }
+
+        if (stepCb && ((iChunk % nChunksPerCent) == 0)) {
+            size_t dPercentsCompleted = iChunk / nChunksPerCent;
+            callNextStep(stepCb, "FILE Info: Uploaded " + MTS::Text::format(dPercentsCompleted) + "%");
+        }
+    }
+
+    if (rc != SUCCESS) {
+        callNextStep(stepCb, "FILE Error: Upload failed due to internal error");
+    } else {
+        callNextStep(stepCb, "FILE Info: Upload finished successfully");
+    }
+
+    // send +++
+    abortFileUpload();
+    return rc;
+}
+
+ICellularRadio::CODE TelitRadio::getFileSize(int fd, size_t& nBytes, size_t& nChunks) {
+    CODE rc = FAILURE;
+
+    do {
+        ssize_t dScrollPos;
+        dScrollPos = lseek(fd, 0, SEEK_SET);
+        if (dScrollPos < 0) {
+            printError("Failed to seek to the start of the file: %d", errno);
+            break;
+        }
+
+        dScrollPos = lseek(fd, 0, SEEK_END);
+        if (dScrollPos < 0) {
+            printError("Failed to determine the file size: %d", errno);
+            break;
+        }
+
+        nBytes = static_cast<size_t>(dScrollPos);
+        nChunks = (nBytes + FILE_CHUNK_SIZE - 1) / FILE_CHUNK_SIZE;  // round up
+
+        rc = SUCCESS;
+
+    } while (false);
+
+    lseek(fd, 0, SEEK_SET);
+    return rc;
+}
+
+ICellularRadio::CODE TelitRadio::readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes) {
+    size_t nUsedBuffer = 0;
+    CODE rc = FAILURE;
+
+    while (true) {
+
+        if (nUsedBuffer > dChunkSize) {
+            printError("Internal pointer error, abort upload: %d", nUsedBuffer);
+            rc = ERROR;
+            break;
+        }
+
+        if (nUsedBuffer == dChunkSize) {
+            // full chunk received
+            rc = SUCCESS;
+            nReadBytes = dChunkSize;
+            break;
+        }
+
+        char* pData = pChunk + nUsedBuffer;
+        size_t nFreeBuffer = dChunkSize - nUsedBuffer;
+
+        ssize_t dReadCount = read(fd, pData, nFreeBuffer);
+        if (dReadCount < 0) {
+            printError("Failed to read from the source file: %d", errno);
+            rc = ERROR;
+            break;
+        }
+
+        size_t duReadCount = static_cast<size_t>(dReadCount);
+        if (duReadCount == 0) {
+            // EOF. Return what was already read
+            nReadBytes = nUsedBuffer;
+            rc = SUCCESS;
+            break;
+        }
+
+        nUsedBuffer += duReadCount;
+
+    }
+
+    return rc;
+}
+
+ICellularRadio::CODE TelitRadio::startFileUpload() {
+    const std::vector<std::string> vBailStrings{ ICellularRadio::RSP_CONNECT, ICellularRadio::RSP_ERROR };
+    const int dTimeout = 1000; //ms
+    std::string sCommand, sResult;
+
+    sCommand = "AT#OTAUPW";
+
+    sResult = sendCommand(sCommand, vBailStrings, dTimeout);
+    if (sResult.find(ICellularRadio::RSP_CONNECT) == std::string::npos) {
+        printError("Radio is not ready to accept the file: [%s]", sResult.c_str());
+        return FAILURE;
+    }
+
+    return SUCCESS;
+}
+
+ICellularRadio::CODE TelitRadio::abortFileUpload() {
+    /*
+     * To prevent the “+++” from being mistaken for data, the following sequence should be followed:
+     * 1) Do not input any character within 1s or longer before inputting “+++”.
+     * 2) Input “+++” within 1s, and no other characters can be inputted during the time.
+     * 3) Do not input any character within 1s after “+++” has been inputted.
+     */
+	sleep(1);
+	return sendBasicCommand(CMD_ABORT_UPLOAD, 2000, 0x00);
+}
+
+ICellularRadio::CODE TelitRadio::getTelitFirmware(std::string& sFirmware) {
+    /*
+	 * Currently, AT+CGMR command is used to determine the radio firmware version.
+	 *
+	 * AT+CGMR
+     *   M0F.670006
+	 *
+	 * Perhaps in the future we will use AT#SWPKGV command.
+	 *
+	 * AT#SWPKGV
+	 *   25.20.676-P0F.670690
+	 *   M0F.670006
+	 *   P0F.670690
+	 *   A0F.670006
+	 */
+
+    printTrace("%s| Get Telit-specific firmware version", getName().c_str());
+    sFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
+    std::string sCmd("AT+CGMR");
+    std::string sResult = sendCommand(sCmd);
+    size_t pos = sResult.find(ICellularRadio::RSP_OK);
+    if (pos == std::string::npos) {
+        printWarning("%s| Unable to get firmware from radio using command [%s]", getName().c_str(), sCmd.c_str());
+        return FAILURE;
+    }
+
+    sFirmware = MTS::Text::trim(sResult.substr(0, pos));
+    if(sFirmware.size() == 0) {
+        printWarning("%s| Unable to get firmware from radio using command [%s]", getName().c_str(), sCmd.c_str());
+        return FAILURE;
+    }
+
+    return SUCCESS;
+}
+
+ICellularRadio::CODE TelitRadio::fumoWaitRadioBooted(ICellularRadio::UpdateCb& stepCb) {
+    const uint32_t duUrcTimeout = 60 * 1000;       // wait for 1 minutes for the next URC message
+    const std::string sFotaUrcPrefix = "#OTAEV:";  // prefix for the URC notification messages
+    const std::string sFotaUrcEndSuccess = "Module Upgraded To New Fw";
+    const std::string sFotaUrcEndFailed = "OTA Fw Upgrade Failed";
+    const std::vector<std::string> vFotaBailStrings{ sFotaUrcPrefix };
+
+    CODE rc = FAILURE;
+    std::string sResponse;
+
+    while (true) {
+
+        sResponse = sendCommand("", vFotaBailStrings, duUrcTimeout, 0x00);
+        printTrace("Radio response: [%s]", sResponse.c_str());
+
+        if (sResponse.find(sFotaUrcPrefix) == std::string::npos) {
+            printError("No URC messages from the radio in %d ms", duUrcTimeout);
+            callNextStep(stepCb, "FUMO Error: timeout, radio is not responding");
+            rc = ERROR;
+            break;
+        }
+
+        if (sResponse.find(sFotaUrcEndSuccess) != std::string::npos) {
+            printTrace("Radio module upgraded to new firmware");
+            callNextStep(stepCb, "FUMO Info: radio module upgraded to new firmware");
+            rc = SUCCESS;
+            break;
+        }
+
+        if (sResponse.find(sFotaUrcEndFailed) != std::string::npos) {
+            printTrace("Radio module firmware upgrade failed");
+            callNextStep(stepCb, "FUMO Error: firmware upgrade failed");
+            rc = ERROR;
+            break;
+        }
+
+    }
+
+    return rc;
+}
+
+ICellularRadio::CODE TelitRadio::fumoCheckNewFirmware(ICellularRadio::UpdateCb& stepCb) {
+    CODE rc = SUCCESS;
+    std::string sTelitFirmware;
+
+    rc = getTelitFirmware(sTelitFirmware);
+
+    if (rc != SUCCESS) {
+        callNextStep(stepCb, "FUMO Error: Failed to obtain current firmware version");
+        return rc;
+    }
+
+    printInfo("Firmware version before the upgrade: %s", m_sTelitFirmware.c_str());
+    printInfo("Current firmware version: %s", sTelitFirmware.c_str());
+
+    if (sTelitFirmware == m_sTelitFirmware) {
+        // Radio will not reset anymore, firmware version left the same, not updated
+        printError("Radio firmware version not changed after upgrade");
+        rc = FAILURE;
+    }
+
+    return rc;
+}
-- 
cgit v1.2.3


From ccd41677d998134ff501a8d6ac3a154dcaca9321 Mon Sep 17 00:00:00 2001
From: Andrii Pientsov <andrii.pientsov@globallogic.com>
Date: Mon, 13 Jul 2020 15:29:38 +0300
Subject: MTX-3404 Code Review

---
 src/MTS_IO_CellularRadio.cpp |  72 ++++++++++++++++++++++
 src/MTS_IO_QuectelRadio.cpp  |  75 +----------------------
 src/MTS_IO_TelitRadio.cpp    | 138 ++++++++++---------------------------------
 3 files changed, 104 insertions(+), 181 deletions(-)

(limited to 'src')

diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp
index bb7cd40..0acf607 100644
--- a/src/MTS_IO_CellularRadio.cpp
+++ b/src/MTS_IO_CellularRadio.cpp
@@ -1262,3 +1262,75 @@ const char *CellularRadio::RadioBandMap::getRadioBandName(const std::string &cha
 
     return band;
 }
+
+ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes, size_t& nChunks) {
+    CODE rc = FAILURE;
+
+    do {
+        ssize_t dScrollPos;
+        dScrollPos = lseek(fd, 0, SEEK_SET);
+        if (dScrollPos < 0) {
+            printError("Failed to seek to the start of the file: %d", errno);
+            break;
+        }
+
+        dScrollPos = lseek(fd, 0, SEEK_END);
+        if (dScrollPos < 0) {
+            printError("Failed to determine the file size: %d", errno);
+            break;
+        }
+
+        nBytes = static_cast<size_t>(dScrollPos);
+        nChunks = (nBytes + FILE_CHUNK_SIZE - 1) / FILE_CHUNK_SIZE;  // round up
+
+        rc = SUCCESS;
+
+    } while (false);
+
+    lseek(fd, 0, SEEK_SET);
+    return rc;
+}
+
+ICellularRadio::CODE CellularRadio::readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes) {
+    size_t nUsedBuffer = 0;
+    CODE rc = FAILURE;
+
+    while (true) {
+
+        if (nUsedBuffer > dChunkSize) {
+            printError("Internal pointer error, abort upload: %d", nUsedBuffer);
+            rc = ERROR;
+            break;
+        }
+
+        if (nUsedBuffer == dChunkSize) {
+            // full chunk received
+            rc = SUCCESS;
+            nReadBytes = dChunkSize;
+            break;
+        }
+
+        char* pData = pChunk + nUsedBuffer;
+        size_t nFreeBuffer = dChunkSize - nUsedBuffer;
+
+        ssize_t dReadCount = read(fd, pData, nFreeBuffer);
+        if (dReadCount < 0) {
+            printError("Failed to read from the source file: %d", errno);
+            rc = ERROR;
+            break;
+        }
+
+        size_t duReadCount = static_cast<size_t>(dReadCount);
+        if (duReadCount == 0) {
+            // EOF. Return what was already read
+            nReadBytes = nUsedBuffer;
+            rc = SUCCESS;
+            break;
+        }
+
+        nUsedBuffer += duReadCount;
+
+    }
+
+    return rc;
+}
diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp
index 22374ee..bb81621 100644
--- a/src/MTS_IO_QuectelRadio.cpp
+++ b/src/MTS_IO_QuectelRadio.cpp
@@ -31,7 +31,6 @@
 
 using namespace MTS::IO;
 
-const size_t QuectelRadio::FILE_CHUNK_SIZE = 1024;
 const std::string QuectelRadio::CMD_ABORT_UPLOAD = "+++";
 
 // It is strongly recommended to use DOS 8.3 file name format for <filename>.
@@ -603,7 +602,7 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step
     sCmd += VALUE_MTS_DELTA_PATH;
     sCmd += "\"";
 
-    rc = sendBasicCommand(sCmd, 1000);
+    rc = sendBasicCommand(sCmd, 10000);
 
     if (rc != SUCCESS) {
         printError("FUMO failed, OK not received from the radio");
@@ -985,78 +984,6 @@ uint16_t QuectelRadio::getQuectelChecksum(const void* data, size_t nBytes) {
     return iChecksum;
 }
 
-ICellularRadio::CODE QuectelRadio::getFileSize(int fd, size_t& nBytes, size_t& nChunks) {
-    CODE rc = FAILURE;
-
-    do {
-        ssize_t dScrollPos;
-        dScrollPos = lseek(fd, 0, SEEK_SET);
-        if (dScrollPos < 0) {
-            printError("Failed to seek to the start of the file: %d", errno);
-            break;
-        }
-
-        dScrollPos = lseek(fd, 0, SEEK_END);
-        if (dScrollPos < 0) {
-            printError("Failed to determine the file size: %d", errno);
-            break;
-        }
-
-        nBytes = static_cast<size_t>(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<size_t>(dReadCount);
-        if (duReadCount == 0) {
-            // EOF. Return what was already read
-            nReadBytes = nUsedBuffer;
-            rc = SUCCESS;
-            break;
-        }
-
-        nUsedBuffer += duReadCount;
-
-    }
-
-    return rc;
-}
-
 ICellularRadio::CODE QuectelRadio::fumoWaitUpgradeFinished(ICellularRadio::UpdateCb& stepCb) {
     const uint32_t duUrcTimeout = 4 * 60 * 1000;  // wait for 4 minutes for the next URC message
     const std::string sFotaUrcPrefix = "+QIND: \"FOTA\"";  // prefix for the URC notification messages
diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp
index 967baae..86c3565 100644
--- a/src/MTS_IO_TelitRadio.cpp
+++ b/src/MTS_IO_TelitRadio.cpp
@@ -28,7 +28,6 @@
 
 using namespace MTS::IO;
 
-const size_t TelitRadio::FILE_CHUNK_SIZE = 1024;
 const std::string TelitRadio::CMD_ABORT_UPLOAD = "+++";
 
 
@@ -739,13 +738,6 @@ ICellularRadio::CODE TelitRadio::setCellularMode(CELLULAR_MODES networks) {
 
 ICellularRadio::CODE TelitRadio::updateFumoLocal(int fd, ICellularRadio::UpdateCb& stepCb) {
     CODE rc = FAILURE;
-    FOTA_GROUP group = getFotaGroup();
-
-    if (group == VALUE_UNKNOWN) {
-        printError("Delta firmware upgrade is not supported for this type of radio modem");
-        callNextStep(stepCb, "FUMO Error: delta firmware upgrade is not supported for this type of radio modem");
-        return rc;
-    }
 
     rc = fumoLocalInject(fd, stepCb);
     if (rc != SUCCESS) {
@@ -765,16 +757,22 @@ ICellularRadio::CODE TelitRadio::fumoLocalInject(int fd, ICellularRadio::UpdateC
         callNextStep(stepCb, "FUMO Info: downloading the firmware");
 
         if ((group == VALUE_GROUP_A) || (group == VALUE_GROUP_B) || (group == VALUE_GROUP_D)) {
-            rc = uploadFile(fd, stepCb);
+            rc = fumoWriteGroupsABD(fd, stepCb);
             if (rc != SUCCESS) {
                 printError("Failed to inject the delta file.");
                 callNextStep(stepCb, "FUMO Error: failed to download the firmware file");
                 break;
             }
         } else if (group == VALUE_GROUP_C) {
-            //TODO Not Implemented
+            //TODO Not Implemented TelitRadio::fumoWriteGroupC
             printError("Failed to inject the delta file.");
             callNextStep(stepCb, "FUMO Error: not implemented");
+            rc = NOT_APPLICABLE;
+            break;
+        } else {
+            printError("Delta firmware upgrade is not supported for this type of radio modem");
+            callNextStep(stepCb, "FUMO Error: delta firmware upgrade is not supported for this type of radio modem");
+            rc = NOT_APPLICABLE;
             break;
         }
 
@@ -803,9 +801,13 @@ ICellularRadio::CODE TelitRadio::fumoLocalApply(ICellularRadio::UpdateCb& stepCb
     } else if ((group == VALUE_GROUP_B) || (group == VALUE_GROUP_C)) {
         // Send "AT#OTAUP=2" command to start the upgrade. OK response follows shortly.
         sCmd  = "AT#OTAUP=2";
+    } else {
+        printError("Delta firmware upgrade is not supported for this type of radio modem");
+        callNextStep(stepCb, "FUMO Error: delta firmware upgrade is not supported for this type of radio modem");
+        return NOT_APPLICABLE;
     }
 
-    rc = sendBasicCommand(sCmd, 2000);
+    rc = sendBasicCommand(sCmd, 10000);
 
     if (rc != SUCCESS) {
         printError("FUMO failed, OK not received from the radio");
@@ -814,30 +816,15 @@ ICellularRadio::CODE TelitRadio::fumoLocalApply(ICellularRadio::UpdateCb& stepCb
     }
 
     const uint32_t duDetachTimeout = 10000;  // wait for 10 seconds for the radio to detach
-    const uint32_t duAttachTimeout = 300000;  // wait for 300 seconds for the radio to attach
 
     do {
-        printInfo("Waiting for the radio to enter recovery mode");
-        callNextStep(stepCb, "FUMO Info: waiting for the radio to enter recovery mode");
+        printInfo("Applying the radio firmware");
+        callNextStep(stepCb, "FUMO Info: applying the radio firmware");
 
         // Wait for the radio to detach from the USB bus
         MTS::Thread::sleep(duDetachTimeout);
 
-        printInfo("Firmware update in progress");
-        callNextStep(stepCb, "FUMO Info: firmware update in progress");
-
-        // It's now detached. Try to reconnect
-        if (!resetConnection(duAttachTimeout)) {
-            printError("Can't connect to the radio in %d ms", (duAttachTimeout));
-            callNextStep(stepCb, "FUMO Error: unable to obtain radio after reset");
-            rc = ERROR;
-            break;
-        }
-
-        // Wait for the radio to finish update and reboot
-        printTrace("Waiting for the radio to come up");
-        callNextStep(stepCb, "FUMO Info: waiting for the radio to enter normal mode");
-        rc = fumoWaitRadioBooted(stepCb);
+        rc = fumoWaitUpgradeFinished(stepCb);
 
         if (rc != SUCCESS) {
             break;
@@ -863,7 +850,7 @@ TelitRadio::FOTA_GROUP TelitRadio::getFotaGroup() {
     return VALUE_UNKNOWN;
 }
 
-ICellularRadio::CODE TelitRadio::uploadFile(int fd, ICellularRadio::UpdateCb& stepCb) {
+ICellularRadio::CODE TelitRadio::fumoWriteGroupsABD(int fd, ICellularRadio::UpdateCb& stepCb) {
     size_t dPayloadLength;
     size_t nChunks;
     CODE rc;
@@ -875,7 +862,7 @@ ICellularRadio::CODE TelitRadio::uploadFile(int fd, ICellularRadio::UpdateCb& st
     printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks);
     printTrace("Starting file upload...");
 
-    rc = startFileUpload();
+    rc = startWrite();
     if (rc != SUCCESS) {
         return rc;
     }
@@ -912,83 +899,12 @@ ICellularRadio::CODE TelitRadio::uploadFile(int fd, ICellularRadio::UpdateCb& st
     }
 
     // send +++
-    abortFileUpload();
-    return rc;
-}
-
-ICellularRadio::CODE TelitRadio::getFileSize(int fd, size_t& nBytes, size_t& nChunks) {
-    CODE rc = FAILURE;
-
-    do {
-        ssize_t dScrollPos;
-        dScrollPos = lseek(fd, 0, SEEK_SET);
-        if (dScrollPos < 0) {
-            printError("Failed to seek to the start of the file: %d", errno);
-            break;
-        }
-
-        dScrollPos = lseek(fd, 0, SEEK_END);
-        if (dScrollPos < 0) {
-            printError("Failed to determine the file size: %d", errno);
-            break;
-        }
-
-        nBytes = static_cast<size_t>(dScrollPos);
-        nChunks = (nBytes + FILE_CHUNK_SIZE - 1) / FILE_CHUNK_SIZE;  // round up
-
-        rc = SUCCESS;
-
-    } while (false);
-
-    lseek(fd, 0, SEEK_SET);
+    abortWrite();
     return rc;
 }
 
-ICellularRadio::CODE TelitRadio::readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes) {
-    size_t nUsedBuffer = 0;
-    CODE rc = FAILURE;
-
-    while (true) {
-
-        if (nUsedBuffer > dChunkSize) {
-            printError("Internal pointer error, abort upload: %d", nUsedBuffer);
-            rc = ERROR;
-            break;
-        }
-
-        if (nUsedBuffer == dChunkSize) {
-            // full chunk received
-            rc = SUCCESS;
-            nReadBytes = dChunkSize;
-            break;
-        }
-
-        char* pData = pChunk + nUsedBuffer;
-        size_t nFreeBuffer = dChunkSize - nUsedBuffer;
-
-        ssize_t dReadCount = read(fd, pData, nFreeBuffer);
-        if (dReadCount < 0) {
-            printError("Failed to read from the source file: %d", errno);
-            rc = ERROR;
-            break;
-        }
-
-        size_t duReadCount = static_cast<size_t>(dReadCount);
-        if (duReadCount == 0) {
-            // EOF. Return what was already read
-            nReadBytes = nUsedBuffer;
-            rc = SUCCESS;
-            break;
-        }
-
-        nUsedBuffer += duReadCount;
 
-    }
-
-    return rc;
-}
-
-ICellularRadio::CODE TelitRadio::startFileUpload() {
+ICellularRadio::CODE TelitRadio::startWrite() {
     const std::vector<std::string> vBailStrings{ ICellularRadio::RSP_CONNECT, ICellularRadio::RSP_ERROR };
     const int dTimeout = 1000; //ms
     std::string sCommand, sResult;
@@ -1004,7 +920,7 @@ ICellularRadio::CODE TelitRadio::startFileUpload() {
     return SUCCESS;
 }
 
-ICellularRadio::CODE TelitRadio::abortFileUpload() {
+ICellularRadio::CODE TelitRadio::abortWrite() {
     /*
      * To prevent the “+++” from being mistaken for data, the following sequence should be followed:
      * 1) Do not input any character within 1s or longer before inputting “+++”.
@@ -1050,8 +966,9 @@ ICellularRadio::CODE TelitRadio::getTelitFirmware(std::string& sFirmware) {
     return SUCCESS;
 }
 
-ICellularRadio::CODE TelitRadio::fumoWaitRadioBooted(ICellularRadio::UpdateCb& stepCb) {
-    const uint32_t duUrcTimeout = 60 * 1000;       // wait for 1 minutes for the next URC message
+ICellularRadio::CODE TelitRadio::fumoWaitUpgradeFinished(ICellularRadio::UpdateCb& stepCb) {
+    const uint32_t duAttachTimeout = 300000;       // wait for 300 seconds for the radio to attach
+	const uint32_t duUrcTimeout = 60 * 1000;       // wait for 1 minutes for the next URC message
     const std::string sFotaUrcPrefix = "#OTAEV:";  // prefix for the URC notification messages
     const std::string sFotaUrcEndSuccess = "Module Upgraded To New Fw";
     const std::string sFotaUrcEndFailed = "OTA Fw Upgrade Failed";
@@ -1060,6 +977,13 @@ ICellularRadio::CODE TelitRadio::fumoWaitRadioBooted(ICellularRadio::UpdateCb& s
     CODE rc = FAILURE;
     std::string sResponse;
 
+    // It's now detached. Try to reconnect
+    if (!resetConnection(duAttachTimeout)) {
+        printError("Can't connect to the radio in %d ms", (duAttachTimeout));
+        callNextStep(stepCb, "FUMO Error: unable to obtain radio after reset");
+        return ERROR;
+    }
+
     while (true) {
 
         sResponse = sendCommand("", vFotaBailStrings, duUrcTimeout, 0x00);
-- 
cgit v1.2.3


From 8186f98913c55191b5a3610d19c8580c6a86c2f4 Mon Sep 17 00:00:00 2001
From: Andrii Pientsov <andrii.pientsov@globallogic.com>
Date: Wed, 15 Jul 2020 13:49:59 +0300
Subject: MTX-3404 Code Review

---
 src/MTS_IO_CellularRadio.cpp |  8 ++++++--
 src/MTS_IO_QuectelRadio.cpp  |  9 ++++++++-
 src/MTS_IO_TelitRadio.cpp    | 39 +++++++++++++++++++++++----------------
 3 files changed, 37 insertions(+), 19 deletions(-)

(limited to 'src')

diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp
index 0acf607..c4514e6 100644
--- a/src/MTS_IO_CellularRadio.cpp
+++ b/src/MTS_IO_CellularRadio.cpp
@@ -1263,7 +1263,7 @@ const char *CellularRadio::RadioBandMap::getRadioBandName(const std::string &cha
     return band;
 }
 
-ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes, size_t& nChunks) {
+ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes) {
     CODE rc = FAILURE;
 
     do {
@@ -1281,7 +1281,6 @@ ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes, size_t&
         }
 
         nBytes = static_cast<size_t>(dScrollPos);
-        nChunks = (nBytes + FILE_CHUNK_SIZE - 1) / FILE_CHUNK_SIZE;  // round up
 
         rc = SUCCESS;
 
@@ -1291,6 +1290,11 @@ ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes, size_t&
     return rc;
 }
 
+ICellularRadio::CODE CellularRadio::sizeToChunks(const size_t nBytes, const size_t chunkSize, size_t& nChunks) {
+    nChunks = (nBytes + chunkSize - 1) / chunkSize;
+    return SUCCESS;
+}
+
 ICellularRadio::CODE CellularRadio::readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes) {
     size_t nUsedBuffer = 0;
     CODE rc = FAILURE;
diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp
index bb81621..2d1ffcd 100644
--- a/src/MTS_IO_QuectelRadio.cpp
+++ b/src/MTS_IO_QuectelRadio.cpp
@@ -31,6 +31,7 @@
 
 using namespace MTS::IO;
 
+const size_t QuectelRadio::FILE_CHUNK_SIZE = 1024;
 const std::string QuectelRadio::CMD_ABORT_UPLOAD = "+++";
 
 // It is strongly recommended to use DOS 8.3 file name format for <filename>.
@@ -836,10 +837,16 @@ ICellularRadio::CODE QuectelRadio::uploadFile(int fd, const std::string& sTarget
     size_t nChunks;
     CODE rc;
 
-    rc = getFileSize(fd, dPayloadLength, nChunks);
+    rc = getFileSize(fd, dPayloadLength);
     if (rc != SUCCESS) {
         return rc;
     }
+
+    rc = sizeToChunks(dPayloadLength, FILE_CHUNK_SIZE, nChunks);
+    if (rc != SUCCESS) {
+        return rc;
+    }
+
     printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks);
     printTrace("Starting file upload...");
 
diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp
index 86c3565..52b5664 100644
--- a/src/MTS_IO_TelitRadio.cpp
+++ b/src/MTS_IO_TelitRadio.cpp
@@ -28,6 +28,7 @@
 
 using namespace MTS::IO;
 
+const size_t TelitRadio::FILE_CHUNK_SIZE = 1024;
 const std::string TelitRadio::CMD_ABORT_UPLOAD = "+++";
 
 
@@ -855,10 +856,16 @@ ICellularRadio::CODE TelitRadio::fumoWriteGroupsABD(int fd, ICellularRadio::Upda
     size_t nChunks;
     CODE rc;
 
-    rc = getFileSize(fd, dPayloadLength, nChunks);
+    rc = getFileSize(fd, dPayloadLength);
     if (rc != SUCCESS) {
         return rc;
     }
+
+    rc = sizeToChunks(dPayloadLength, FILE_CHUNK_SIZE, nChunks);
+    if (rc != SUCCESS) {
+        return rc;
+    }
+
     printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks);
     printTrace("Starting file upload...");
 
@@ -927,25 +934,25 @@ ICellularRadio::CODE TelitRadio::abortWrite() {
      * 2) Input “+++” within 1s, and no other characters can be inputted during the time.
      * 3) Do not input any character within 1s after “+++” has been inputted.
      */
-	sleep(1);
-	return sendBasicCommand(CMD_ABORT_UPLOAD, 2000, 0x00);
+    sleep(1);
+    return sendBasicCommand(CMD_ABORT_UPLOAD, 2000, 0x00);
 }
 
 ICellularRadio::CODE TelitRadio::getTelitFirmware(std::string& sFirmware) {
     /*
-	 * Currently, AT+CGMR command is used to determine the radio firmware version.
-	 *
-	 * AT+CGMR
+     * Currently, AT+CGMR command is used to determine the radio firmware version.
+     *
+     * AT+CGMR
+     *   M0F.670006
+     *
+     * Perhaps in the future we will use AT#SWPKGV command.
+     *
+     * AT#SWPKGV
+     *   25.20.676-P0F.670690
      *   M0F.670006
-	 *
-	 * Perhaps in the future we will use AT#SWPKGV command.
-	 *
-	 * AT#SWPKGV
-	 *   25.20.676-P0F.670690
-	 *   M0F.670006
-	 *   P0F.670690
-	 *   A0F.670006
-	 */
+     *   P0F.670690
+     *   A0F.670006
+     */
 
     printTrace("%s| Get Telit-specific firmware version", getName().c_str());
     sFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
@@ -968,7 +975,7 @@ ICellularRadio::CODE TelitRadio::getTelitFirmware(std::string& sFirmware) {
 
 ICellularRadio::CODE TelitRadio::fumoWaitUpgradeFinished(ICellularRadio::UpdateCb& stepCb) {
     const uint32_t duAttachTimeout = 300000;       // wait for 300 seconds for the radio to attach
-	const uint32_t duUrcTimeout = 60 * 1000;       // wait for 1 minutes for the next URC message
+    const uint32_t duUrcTimeout = 60 * 1000;       // wait for 1 minutes for the next URC message
     const std::string sFotaUrcPrefix = "#OTAEV:";  // prefix for the URC notification messages
     const std::string sFotaUrcEndSuccess = "Module Upgraded To New Fw";
     const std::string sFotaUrcEndFailed = "OTA Fw Upgrade Failed";
-- 
cgit v1.2.3


From 1986b67d54e8e6a3f8176f5b4cb78786ac3ee496 Mon Sep 17 00:00:00 2001
From: Andrii Pientsov <andrii.pientsov@globallogic.com>
Date: Thu, 16 Jul 2020 19:23:40 +0300
Subject: Add vendor firmware version

---
 src/MTS_IO_CellularRadio.cpp  |  5 ++++
 src/MTS_IO_ICellularRadio.cpp |  1 +
 src/MTS_IO_QuectelRadio.cpp   | 12 ++++----
 src/MTS_IO_TelitRadio.cpp     | 69 ++++++++++++++++++++++++-------------------
 4 files changed, 51 insertions(+), 36 deletions(-)

(limited to 'src')

diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp
index c4514e6..19f1b7b 100644
--- a/src/MTS_IO_CellularRadio.cpp
+++ b/src/MTS_IO_CellularRadio.cpp
@@ -200,6 +200,11 @@ ICellularRadio::CODE CellularRadio::getFirmwareBuild(std::string& sFirmwareBuild
     return FAILURE;
 }
 
+ICellularRadio::CODE CellularRadio::getVendorFirmware(std::string& sVendorFirmware) {
+    sVendorFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
+    return FAILURE;
+}
+
 ICellularRadio::CODE CellularRadio::getHardware(std::string& sHardware) {
     printTrace("%s| Get Hardware", m_sName.c_str());
     sHardware = ICellularRadio::VALUE_NOT_SUPPORTED;
diff --git a/src/MTS_IO_ICellularRadio.cpp b/src/MTS_IO_ICellularRadio.cpp
index 4b58189..9dbfad1 100644
--- a/src/MTS_IO_ICellularRadio.cpp
+++ b/src/MTS_IO_ICellularRadio.cpp
@@ -33,6 +33,7 @@ const char *MTS::IO::ICellularRadio::KEY_MANUFACTURER = "manufacturer";    //!<
 const char *MTS::IO::ICellularRadio::KEY_HARDWARE = "hardware";            //!< Radio Hardware Version
 const char *MTS::IO::ICellularRadio::KEY_FIRMWARE = "firmware";            //!< Radio Firmware Version
 const char *MTS::IO::ICellularRadio::KEY_FIRMWARE_BUILD = "firmwarebuild"; //!< Radio Firmware Build
+const char *MTS::IO::ICellularRadio::KEY_VENDOR_FIRMWARE = "vendorfirmware";  //!< Radio Vendor Firmware Version
 
 const char *MTS::IO::ICellularRadio::KEY_CARRIER = "carrier";   //!< Cellular Service Provider  = Home Network
 const char *MTS::IO::ICellularRadio::VALUE_CARRIER_VERIZON = "Verizon";
diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp
index 2d1ffcd..e32d6e8 100644
--- a/src/MTS_IO_QuectelRadio.cpp
+++ b/src/MTS_IO_QuectelRadio.cpp
@@ -591,7 +591,7 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step
     ICellularRadio::CODE rc;
     std::string sCmd;
 
-    rc = getQuectelFirmware(m_sQuectelFirmware);
+    rc = getVendorFirmware(m_sQuectelFirmware);
     if (rc != SUCCESS) {
         callNextStep(stepCb, "FUMO Error: Failed to obtain current firmware version");
         return rc;
@@ -954,9 +954,9 @@ ICellularRadio::CODE QuectelRadio::checkFile(bool& bIsFilePresent, const std::st
     return SUCCESS;
 }
 
-ICellularRadio::CODE QuectelRadio::getQuectelFirmware(std::string& sFirmware) {
+ICellularRadio::CODE QuectelRadio::getVendorFirmware(std::string& sVendorFirmware) {
     printTrace("%s| Get Quectel-specific firmware version", getName().c_str());
-    sFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
+    sVendorFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
     std::string sCmd("AT+QGMR");
     std::string sResult = sendCommand(sCmd);
     size_t pos = sResult.find(ICellularRadio::RSP_OK);
@@ -965,8 +965,8 @@ ICellularRadio::CODE QuectelRadio::getQuectelFirmware(std::string& sFirmware) {
         return FAILURE;
     }
 
-    sFirmware = MTS::Text::trim(sResult.substr(0, pos));
-    if(sFirmware.size() == 0) {
+    sVendorFirmware = MTS::Text::trim(sResult.substr(0, pos));
+    if(sVendorFirmware.size() == 0) {
         printWarning("%s| Unable to get firmware from radio using command [%s]", getName().c_str(), sCmd.c_str());
         return FAILURE;
     }
@@ -1053,7 +1053,7 @@ ICellularRadio::CODE QuectelRadio::fumoWaitNewFirmware(ICellularRadio::UpdateCb&
 
         MTS::Thread::sleep(10000);
 
-        if (getQuectelFirmware(sQuectelFirmware) != SUCCESS) {
+        if (getVendorFirmware(sQuectelFirmware) != SUCCESS) {
             // The radio is probably unavailable
             resetConnection(100);
             continue;
diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp
index 52b5664..8d762be 100644
--- a/src/MTS_IO_TelitRadio.cpp
+++ b/src/MTS_IO_TelitRadio.cpp
@@ -789,7 +789,7 @@ ICellularRadio::CODE TelitRadio::fumoLocalApply(ICellularRadio::UpdateCb& stepCb
     std::string sCmd;
     FOTA_GROUP group = getFotaGroup();
 
-    rc = getTelitFirmware(m_sTelitFirmware);
+    rc = getVendorFirmware(m_sTelitFirmware);
     if (rc != SUCCESS) {
         callNextStep(stepCb, "FUMO Error: Failed to obtain current firmware version");
         return rc;
@@ -938,39 +938,48 @@ ICellularRadio::CODE TelitRadio::abortWrite() {
     return sendBasicCommand(CMD_ABORT_UPLOAD, 2000, 0x00);
 }
 
-ICellularRadio::CODE TelitRadio::getTelitFirmware(std::string& sFirmware) {
-    /*
-     * Currently, AT+CGMR command is used to determine the radio firmware version.
-     *
-     * AT+CGMR
-     *   M0F.670006
-     *
-     * Perhaps in the future we will use AT#SWPKGV command.
-     *
-     * AT#SWPKGV
-     *   25.20.676-P0F.670690
-     *   M0F.670006
-     *   P0F.670690
-     *   A0F.670006
-     */
-
+ICellularRadio::CODE TelitRadio::getVendorFirmware(std::string& sVendorFirmware) {
     printTrace("%s| Get Telit-specific firmware version", getName().c_str());
-    sFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
-    std::string sCmd("AT+CGMR");
+    ICellularRadio::CODE rc = FAILURE;
+    sVendorFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
+    std::string sFirmware;
+    std::string sFirmwareBuild;
+    std::string sCmd("AT#SWPKGV");
     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;
-    }
+    do {
 
-    return SUCCESS;
+        if (pos != std::string::npos) {
+            // Found
+            std::vector<std::string> vLine = MTS::Text::split(sResult, "\r");
+            if(vLine[1].size() == 0) {
+                printWarning("%s| Unable to get firmware from radio using command [%s]", getName().c_str(), sCmd.c_str());
+                rc = FAILURE;
+            } else {
+                sVendorFirmware = MTS::Text::trim(vLine[1]);
+                rc = SUCCESS;
+            }
+            break;
+        }
+
+        // Not Found. Then we will use "AT+CGMR" + "AT#CFVR"
+        rc = getFirmware(sFirmware);
+        if (rc != SUCCESS){
+            break;
+        }
+
+        rc = getFirmwareBuild(sFirmwareBuild);
+        if (rc != SUCCESS){
+            break;
+        }
+
+        sVendorFirmware = sFirmware + "," + sFirmwareBuild;
+
+    } while (false);
+
+
+    return rc;
 }
 
 ICellularRadio::CODE TelitRadio::fumoWaitUpgradeFinished(ICellularRadio::UpdateCb& stepCb) {
@@ -1026,7 +1035,7 @@ ICellularRadio::CODE TelitRadio::fumoCheckNewFirmware(ICellularRadio::UpdateCb&
     CODE rc = SUCCESS;
     std::string sTelitFirmware;
 
-    rc = getTelitFirmware(sTelitFirmware);
+    rc = getVendorFirmware(sTelitFirmware);
 
     if (rc != SUCCESS) {
         callNextStep(stepCb, "FUMO Error: Failed to obtain current firmware version");
-- 
cgit v1.2.3


From a05854f5d389f67001b7ecc4cda62c7320433349 Mon Sep 17 00:00:00 2001
From: Andrii Pientsov <andrii.pientsov@globallogic.com>
Date: Fri, 17 Jul 2020 10:03:48 +0300
Subject: Code review

---
 src/MTS_IO_ME910Radio.cpp | 32 --------------------------------
 src/MTS_IO_TelitRadio.cpp | 34 ++++++++++++++++++++++++++++++++--
 2 files changed, 32 insertions(+), 34 deletions(-)

(limited to 'src')

diff --git a/src/MTS_IO_ME910Radio.cpp b/src/MTS_IO_ME910Radio.cpp
index 2b181d2..ad3e7ec 100644
--- a/src/MTS_IO_ME910Radio.cpp
+++ b/src/MTS_IO_ME910Radio.cpp
@@ -33,35 +33,3 @@ ME910Radio::ME910Radio(const std::string& sME910Model, const std::string& sPort)
 ICellularRadio::CODE ME910Radio::setRxDiversity(const Json::Value& jArgs) {
         return FAILURE;
 }
-
-
-ICellularRadio::CODE ME910Radio::getFirmwareBuild(std::string& sFirmwareBuild) {
-    std::string sCmd("AT#CFVR");
-
-    std::string sResult = sendCommand(sCmd);
-
-    size_t end = sResult.find(ICellularRadio::RSP_OK);
-    if (end == std::string::npos) {
-        printWarning("%s| Unable to get firmware build number [%s]",
-                getName().c_str(),
-                sCmd.c_str());
-        return FAILURE;
-    }
-
-    size_t start = sResult.find("#CFVR:");
-    if (start == std::string::npos) {
-        printWarning("%s| Command returned unexpected response [%s]",
-                getName().c_str(),
-                sCmd.c_str());
-        return FAILURE;
-    }
-
-    start += sizeof("#CFVR:");
-
-    sFirmwareBuild = MTS::Text::trim(sResult.substr(start, end-start));
-    if(sFirmwareBuild.size() == 0) {
-        printWarning("%s| Firmware Build Version is empty", getName().c_str());
-        return FAILURE;
-    }
-    return SUCCESS;
-}
diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp
index 8d762be..599c71e 100644
--- a/src/MTS_IO_TelitRadio.cpp
+++ b/src/MTS_IO_TelitRadio.cpp
@@ -51,6 +51,36 @@ bool TelitRadio::resetRadio(uint32_t iTimeoutMillis) {
     return false;
 }
 
+ICellularRadio::CODE TelitRadio::getFirmwareBuild(std::string& sFirmwareBuild) {
+    std::string sCmd("AT#CFVR");
+
+    std::string sResult = sendCommand(sCmd);
+
+    size_t end = sResult.find(ICellularRadio::RSP_OK);
+    if (end == std::string::npos) {
+        printWarning("%s| Unable to get firmware build number [%s]",
+                getName().c_str(),
+                sCmd.c_str());
+        return FAILURE;
+    }
+
+    size_t start = sResult.find("#CFVR:");
+    if (start == std::string::npos) {
+        printWarning("%s| Command returned unexpected response [%s]",
+                getName().c_str(),
+                sCmd.c_str());
+        return FAILURE;
+    }
+
+    start += sizeof("#CFVR:");
+
+    sFirmwareBuild = MTS::Text::trim(sResult.substr(start, end-start));
+    if(sFirmwareBuild.size() == 0) {
+        printWarning("%s| Firmware Build Version is empty", getName().c_str());
+        return FAILURE;
+    }
+    return SUCCESS;
+}
 
 ICellularRadio::CODE TelitRadio::getModel(std::string& sModel) {
     printTrace("%s| Get Model", getName().c_str());
@@ -953,11 +983,11 @@ ICellularRadio::CODE TelitRadio::getVendorFirmware(std::string& sVendorFirmware)
         if (pos != std::string::npos) {
             // Found
             std::vector<std::string> vLine = MTS::Text::split(sResult, "\r");
-            if(vLine[1].size() == 0) {
+            sVendorFirmware = MTS::Text::trim(vLine[1]);
+            if(sVendorFirmware.size() == 0) {
                 printWarning("%s| Unable to get firmware from radio using command [%s]", getName().c_str(), sCmd.c_str());
                 rc = FAILURE;
             } else {
-                sVendorFirmware = MTS::Text::trim(vLine[1]);
                 rc = SUCCESS;
             }
             break;
-- 
cgit v1.2.3


From 298efe4e17d3665f9ddbb9298a37b563c60de735 Mon Sep 17 00:00:00 2001
From: Andrii Pientsov <andrii.pientsov@globallogic.com>
Date: Fri, 17 Jul 2020 10:42:16 +0300
Subject: Code review

---
 src/MTS_IO_QuectelRadio.cpp | 40 ++++++++++-----------
 src/MTS_IO_TelitRadio.cpp   | 88 ++++++++++++++++++++++-----------------------
 2 files changed, 64 insertions(+), 64 deletions(-)

(limited to 'src')

diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp
index e32d6e8..22fa92f 100644
--- a/src/MTS_IO_QuectelRadio.cpp
+++ b/src/MTS_IO_QuectelRadio.cpp
@@ -56,6 +56,26 @@ bool QuectelRadio::resetRadio(uint32_t iTimeoutMillis) {
     return false;
 }
 
+ICellularRadio::CODE QuectelRadio::getVendorFirmware(std::string& sVendorFirmware) {
+    printTrace("%s| Get Quectel-specific firmware version", getName().c_str());
+    sVendorFirmware = 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;
+    }
+
+    sVendorFirmware = MTS::Text::trim(sResult.substr(0, pos));
+    if(sVendorFirmware.size() == 0) {
+        printWarning("%s| Unable to get firmware from radio using command [%s]", getName().c_str(), sCmd.c_str());
+        return FAILURE;
+    }
+
+    return SUCCESS;
+}
+
 ICellularRadio::CODE QuectelRadio::getModel(std::string& sModel) {
     printTrace("%s| Get Model", getName().c_str());
     //Always returns SUCCESS because the model should be m_sName
@@ -954,26 +974,6 @@ ICellularRadio::CODE QuectelRadio::checkFile(bool& bIsFilePresent, const std::st
     return SUCCESS;
 }
 
-ICellularRadio::CODE QuectelRadio::getVendorFirmware(std::string& sVendorFirmware) {
-    printTrace("%s| Get Quectel-specific firmware version", getName().c_str());
-    sVendorFirmware = 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;
-    }
-
-    sVendorFirmware = MTS::Text::trim(sResult.substr(0, pos));
-    if(sVendorFirmware.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<const uint8_t*>(data);
     uint16_t iChecksum = 0;
diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp
index 599c71e..a82004c 100644
--- a/src/MTS_IO_TelitRadio.cpp
+++ b/src/MTS_IO_TelitRadio.cpp
@@ -82,6 +82,50 @@ ICellularRadio::CODE TelitRadio::getFirmwareBuild(std::string& sFirmwareBuild) {
     return SUCCESS;
 }
 
+ICellularRadio::CODE TelitRadio::getVendorFirmware(std::string& sVendorFirmware) {
+    printTrace("%s| Get Telit-specific firmware version", getName().c_str());
+    ICellularRadio::CODE rc = FAILURE;
+    sVendorFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
+    std::string sFirmware;
+    std::string sFirmwareBuild;
+    std::string sCmd("AT#SWPKGV");
+    std::string sResult = sendCommand(sCmd);
+    size_t pos = sResult.find(ICellularRadio::RSP_OK);
+
+    do {
+
+        if (pos != std::string::npos) {
+            // Found
+            std::vector<std::string> vLine = MTS::Text::split(sResult, "\r");
+            sVendorFirmware = MTS::Text::trim(vLine[1]);
+            if(sVendorFirmware.size() == 0) {
+                printWarning("%s| Unable to get firmware from radio using command [%s]", getName().c_str(), sCmd.c_str());
+                rc = FAILURE;
+            } else {
+                rc = SUCCESS;
+            }
+            break;
+        }
+
+        // Not Found. Then we will use "AT+CGMR" + "AT#CFVR"
+        rc = getFirmware(sFirmware);
+        if (rc != SUCCESS){
+            break;
+        }
+
+        rc = getFirmwareBuild(sFirmwareBuild);
+        if (rc != SUCCESS){
+            break;
+        }
+
+        sVendorFirmware = sFirmware + "," + sFirmwareBuild;
+
+    } while (false);
+
+
+    return rc;
+}
+
 ICellularRadio::CODE TelitRadio::getModel(std::string& sModel) {
     printTrace("%s| Get Model", getName().c_str());
     //Always returns SUCCESS because the model should be m_sName
@@ -968,50 +1012,6 @@ ICellularRadio::CODE TelitRadio::abortWrite() {
     return sendBasicCommand(CMD_ABORT_UPLOAD, 2000, 0x00);
 }
 
-ICellularRadio::CODE TelitRadio::getVendorFirmware(std::string& sVendorFirmware) {
-    printTrace("%s| Get Telit-specific firmware version", getName().c_str());
-    ICellularRadio::CODE rc = FAILURE;
-    sVendorFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
-    std::string sFirmware;
-    std::string sFirmwareBuild;
-    std::string sCmd("AT#SWPKGV");
-    std::string sResult = sendCommand(sCmd);
-    size_t pos = sResult.find(ICellularRadio::RSP_OK);
-
-    do {
-
-        if (pos != std::string::npos) {
-            // Found
-            std::vector<std::string> vLine = MTS::Text::split(sResult, "\r");
-            sVendorFirmware = MTS::Text::trim(vLine[1]);
-            if(sVendorFirmware.size() == 0) {
-                printWarning("%s| Unable to get firmware from radio using command [%s]", getName().c_str(), sCmd.c_str());
-                rc = FAILURE;
-            } else {
-                rc = SUCCESS;
-            }
-            break;
-        }
-
-        // Not Found. Then we will use "AT+CGMR" + "AT#CFVR"
-        rc = getFirmware(sFirmware);
-        if (rc != SUCCESS){
-            break;
-        }
-
-        rc = getFirmwareBuild(sFirmwareBuild);
-        if (rc != SUCCESS){
-            break;
-        }
-
-        sVendorFirmware = sFirmware + "," + sFirmwareBuild;
-
-    } while (false);
-
-
-    return rc;
-}
-
 ICellularRadio::CODE TelitRadio::fumoWaitUpgradeFinished(ICellularRadio::UpdateCb& stepCb) {
     const uint32_t duAttachTimeout = 300000;       // wait for 300 seconds for the radio to attach
     const uint32_t duUrcTimeout = 60 * 1000;       // wait for 1 minutes for the next URC message
-- 
cgit v1.2.3


From a9a598dab448dd0d062725e2f3a60bcf68c6fe9e Mon Sep 17 00:00:00 2001
From: Serhii Kostiuk <serhii.o.kostiuk@globallogic.com>
Date: Tue, 21 Jul 2020 18:35:33 +0300
Subject: Quectel Delta Radio Firmware Upgrade support - libmts-io
 implementation

Fixed Quectel checksum calculation algorithm to correctly handle odd file sizes.
---
 src/MTS_IO_QuectelRadio.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp
index 22fa92f..39c0601 100644
--- a/src/MTS_IO_QuectelRadio.cpp
+++ b/src/MTS_IO_QuectelRadio.cpp
@@ -981,8 +981,10 @@ uint16_t QuectelRadio::getQuectelChecksum(const void* data, size_t nBytes) {
     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.
+        bool bHasLowByte = (i + 1 < nBytes);
+
         uint8_t high = castData[i];
-        uint8_t low = (i < nBytes) ? (castData[i+1]) : (0);
+        uint8_t low = bHasLowByte ? (castData[i+1]) : (0);
 
         uint16_t iFragment = bytesToUint16(high, low);
         updateQuectelChecksum(iChecksum, iFragment);
-- 
cgit v1.2.3


From b9a5e7ab27ad313b95fe6642f08e230091efef0f Mon Sep 17 00:00:00 2001
From: Serhii Kostiuk <serhii.o.kostiuk@globallogic.com>
Date: Tue, 21 Jul 2020 20:24:00 +0300
Subject: Telit Delta Radio Firmware Upgrade support - libmts-io implementation

Changes after code review:

1. Handle cases when the number of bytes written is different from the number of bytes requested to transfer.
2. Use fstat instead of lseek to determine the firmware size.
3. Renamed TelitRadio::startWrite and TelitRadio::abortWrite functions to better represent their applicability scope.
---
 src/MTS_IO_CellularRadio.cpp | 31 +++++++++++++++++++++----------
 src/MTS_IO_TelitRadio.cpp    |  8 ++++----
 2 files changed, 25 insertions(+), 14 deletions(-)

(limited to 'src')

diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp
index 19f1b7b..49a2b32 100644
--- a/src/MTS_IO_CellularRadio.cpp
+++ b/src/MTS_IO_CellularRadio.cpp
@@ -21,6 +21,7 @@
 #include "mts/MTS_IO_CellularRadio.h"
 
 #include <unistd.h>
+#include <sys/stat.h>
 
 #include <mts/MTS_IO_MccMncTable.h>
 #include <mts/MTS_Thread.h>
@@ -1138,10 +1139,21 @@ ICellularRadio::CODE CellularRadio::sendData(const char* pData, size_t nBytes) {
         return ERROR;
     }
 
+    // This limit comes from the Connection::write implementation. Otherwise we can get overflows.
+    const size_t maxInt32 = INT32_MAX;    // iSize parameter type in Connection::write
+    const size_t maxUInt32 = UINT32_MAX;  // return value type in Connection::write
+    const size_t nSizeLimit = std::min(maxInt32, maxUInt32);
+
+    if (nBytes > nSizeLimit) {
+        printError("RADIO| Chunks larger than %d bytes are not supported", nSizeLimit);
+        return INVALID_ARGS;
+    }
+
+    // Now we can ignore conversion and overflow warnings emitted by compiler.
     int32_t iResult;
     iResult = m_apIo->write(pData, nBytes);
 
-    if(iResult == -1) {
+    if(iResult != static_cast<int32_t>(nBytes)) {
         printError("RADIO| Failed to send data to radio");
         return ERROR;
     }
@@ -1272,21 +1284,20 @@ ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes) {
     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);
+        struct stat fileStatus;
+
+        // On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
+        if (fstat(fd, &fileStatus) < 0) {
+            printError("Failed to determine file size: %d", errno);
             break;
         }
 
-        dScrollPos = lseek(fd, 0, SEEK_END);
-        if (dScrollPos < 0) {
-            printError("Failed to determine the file size: %d", errno);
+        if (fileStatus.st_size < 0) {
+            printError("Failed to determine file size, file size is negative: %d", fileStatus.st_size);
             break;
         }
 
-        nBytes = static_cast<size_t>(dScrollPos);
-
+        nBytes = static_cast<size_t>(fileStatus.st_size);
         rc = SUCCESS;
 
     } while (false);
diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp
index a82004c..8c7e174 100644
--- a/src/MTS_IO_TelitRadio.cpp
+++ b/src/MTS_IO_TelitRadio.cpp
@@ -943,7 +943,7 @@ ICellularRadio::CODE TelitRadio::fumoWriteGroupsABD(int fd, ICellularRadio::Upda
     printTrace("File size: %d bytes and %d chunks", dPayloadLength, nChunks);
     printTrace("Starting file upload...");
 
-    rc = startWrite();
+    rc = startFotaWriteABD();
     if (rc != SUCCESS) {
         return rc;
     }
@@ -980,12 +980,12 @@ ICellularRadio::CODE TelitRadio::fumoWriteGroupsABD(int fd, ICellularRadio::Upda
     }
 
     // send +++
-    abortWrite();
+    abortFotaWriteABD();
     return rc;
 }
 
 
-ICellularRadio::CODE TelitRadio::startWrite() {
+ICellularRadio::CODE TelitRadio::startFotaWriteABD() {
     const std::vector<std::string> vBailStrings{ ICellularRadio::RSP_CONNECT, ICellularRadio::RSP_ERROR };
     const int dTimeout = 1000; //ms
     std::string sCommand, sResult;
@@ -1001,7 +1001,7 @@ ICellularRadio::CODE TelitRadio::startWrite() {
     return SUCCESS;
 }
 
-ICellularRadio::CODE TelitRadio::abortWrite() {
+ICellularRadio::CODE TelitRadio::abortFotaWriteABD() {
     /*
      * 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 “+++”.
-- 
cgit v1.2.3


From beb3f36c5dcc70beed900859d6426dc74e4bccc8 Mon Sep 17 00:00:00 2001
From: Serhii Kostiuk <serhii.o.kostiuk@globallogic.com>
Date: Wed, 22 Jul 2020 09:55:55 +0300
Subject: Telit Delta Radio Firmware Upgrade support - libmts-io implementation

During testing it was discovered that the radio is detached for 4 minutes 40 seconds while
the upgrade is in progress. This was too close to the time limit of 5 minutes set before.

This commit increases attach timeout to 6 minutes to be on the safe side.
---
 src/MTS_IO_TelitRadio.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp
index 8c7e174..457dfdc 100644
--- a/src/MTS_IO_TelitRadio.cpp
+++ b/src/MTS_IO_TelitRadio.cpp
@@ -1013,7 +1013,7 @@ ICellularRadio::CODE TelitRadio::abortFotaWriteABD() {
 }
 
 ICellularRadio::CODE TelitRadio::fumoWaitUpgradeFinished(ICellularRadio::UpdateCb& stepCb) {
-    const uint32_t duAttachTimeout = 300000;       // wait for 300 seconds for the radio to attach
+    const uint32_t duAttachTimeout = 6 * 60 * 1000;// wait for 6 minutes for the radio to attach
     const uint32_t duUrcTimeout = 60 * 1000;       // wait for 1 minutes for the next URC message
     const std::string sFotaUrcPrefix = "#OTAEV:";  // prefix for the URC notification messages
     const std::string sFotaUrcEndSuccess = "Module Upgraded To New Fw";
-- 
cgit v1.2.3