summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSerhii Kostiuk <serhii.o.kostiuk@globallogic.com>2020-07-01 11:25:34 +0300
committerSerhii Kostiuk <serhii.o.kostiuk@globallogic.com>2020-07-03 16:17:39 +0300
commitd1798ea6a82c46b43e4caf27148189f34cc44ca5 (patch)
tree4f389b34388d6d3889e743f1e156864cf3cfa68a
parent69d7eb13bdda92a92b381f58a8ba9ec6fc29888f (diff)
downloadlibmts-io-d1798ea6a82c46b43e4caf27148189f34cc44ca5.tar.gz
libmts-io-d1798ea6a82c46b43e4caf27148189f34cc44ca5.tar.bz2
libmts-io-d1798ea6a82c46b43e4caf27148189f34cc44ca5.zip
WIP: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation
Initial implementation of the "Apply delta firmware" step. Requires cleanup.
-rw-r--r--include/mts/MTS_IO_QuectelRadio.h3
-rw-r--r--src/MTS_IO_QuectelRadio.cpp179
2 files changed, 181 insertions, 1 deletions
diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h
index 506b21b..cb455d8 100644
--- a/include/mts/MTS_IO_QuectelRadio.h
+++ b/include/mts/MTS_IO_QuectelRadio.h
@@ -76,6 +76,9 @@ namespace MTS {
static inline uint16_t bytesToUint16(uint8_t high, uint8_t low);
static CODE getFileSize(int fd, size_t& nBytes, size_t& nFileChunks);
static CODE readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes);
+ CODE waitApplyDeltaFirmware(UpdateCb& stepCb);
+ CODE waitFotaFinish(UpdateCb& stepCb);
+ CODE waitNewFirmware(UpdateCb& stepCb);
};
}
}
diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp
index 2ed11b0..25d2a30 100644
--- a/src/MTS_IO_QuectelRadio.cpp
+++ b/src/MTS_IO_QuectelRadio.cpp
@@ -25,6 +25,7 @@
#include <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