From 1afb3c7bb338eba0410f85be8e5eaf192a7c4857 Mon Sep 17 00:00:00 2001
From: Serhii Kostiuk <serhii.o.kostiuk@globallogic.com>
Date: Tue, 4 Aug 2020 10:20:54 +0300
Subject: Quectel EG25-G Delta Radio Firmware Upgrade support - libmts-io
 implementation

During testing with L4G1 device I discovered some inconsistencies in behaviour between
EG25-G device and EG95 devices (EG95-NA and EG95-E).

EG25-G device that I have on hands does not allow to perform downgrades using delta images.
When there is an attempt to apply a downgrade delta image it behaves as the following:

- radio prints `OK`
- radio prints `+QIND: "FOTA",502`
- radio is not rebooted

While EG95 radios always reboot at least once to apply the firmware image. Even if it is not valid.

Also I noticed that detach from the serial bus may take more than 10 seconds in some rare cases.
Thus we need to wait not a fixed amount of time but until the radio actually detaches from the bus.

This commit attempts to address the findings described above.
---
 src/MTS_IO_QuectelRadio.cpp | 49 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 45 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp
index 601674b..7d205e7 100644
--- a/src/MTS_IO_QuectelRadio.cpp
+++ b/src/MTS_IO_QuectelRadio.cpp
@@ -608,8 +608,12 @@ ICellularRadio::CODE QuectelRadio::fumoLocalCleanup() {
 }
 
 ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& stepCb) {
+    const std::string sFotaUrcPrefix = "+QIND: \"FOTA\"";  // prefix for URC notification messages
+    const std::vector<std::string> vFotaBailStrings{ sFotaUrcPrefix };
+
     ICellularRadio::CODE rc;
     std::string sCmd;
+    std::string sResponse;
 
     rc = getVendorFirmware(m_sQuectelFirmware);
     if (rc != SUCCESS) {
@@ -631,8 +635,8 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step
         return rc;
     }
 
-    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 uint32_t duDetachTimeout = 15000;  // wait up to 15 seconds for the radio to detach
+    const uint32_t duAttachTimeout = 30000;  // wait up to 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++) {
@@ -640,8 +644,22 @@ ICellularRadio::CODE QuectelRadio::fumoLocalApply(ICellularRadio::UpdateCb& step
         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);
+        // Wait for new FOTA responses. Exits preliminary if radio detached.
+        sResponse = sendCommand("", vFotaBailStrings, duDetachTimeout, 0x00);
+        printTrace("Radio response: [%s]", sResponse.c_str());
+
+        if (i == 0 && sResponse.find(sFotaUrcPrefix) != std::string::npos) {
+            // FOTA responce code received before radio detach, image can't be applied.
+            // EG25-G radio prints a message in +QIND: "FOTA",ERR_CODE format in this case.
+            std::string sErrorCode = getFumoEarlyErrorCode(sResponse);
+
+            printError("Preliminary termination of FUMO procedure: [%s]", sErrorCode.c_str());
+            callNextStep(stepCb, "FUMO Error: radio returned error code " + sErrorCode);
+
+            // We got a response from the radio but FOTA failed
+            rc = FAILURE;
+            break;
+        }
 
         // It's now detached. Try to reconnect
         if (!resetConnection(duAttachTimeout)) {
@@ -1116,6 +1134,29 @@ ICellularRadio::CODE QuectelRadio::fumoWaitNewFirmware(ICellularRadio::UpdateCb&
     return rc;
 }
 
+std::string QuectelRadio::getFumoEarlyErrorCode(const std::string& sRadioInput) {
+    const std::string sFotaPrefix = "+QIND: \"FOTA\",";
+    const char cLineEnd = ICellularRadio::CR;
+
+    // sRadioInput may contain several lines. We need to find the line with '+QIND: "FOTA",' in it
+    auto pLineStart = sRadioInput.find(sFotaPrefix);
+
+    if (pLineStart == std::string::npos) {
+        // FOTA line not found at all
+        return "-1";
+    }
+
+    // Parse the error code
+    auto pErrorStart = pLineStart + sFotaPrefix.length();
+    auto pLineEnd = sRadioInput.find(cLineEnd, pErrorStart);
+
+    // Filter the error code
+    std::string sSubString = sRadioInput.substr(pErrorStart, pLineEnd - pErrorStart);
+    std::string sResult = MTS::Text::trim(sSubString);
+
+    return sResult;
+}
+
 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