summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrii Pientsov <andrii.pientsov@globallogic.com>2020-07-10 15:40:48 +0300
committerAndrii Pientsov <andrii.pientsov@globallogic.com>2020-07-10 15:40:48 +0300
commite09f54c6b6fe35dc022fcea66935f5269866bff1 (patch)
tree9607af6717f2663ffc31aa70eee55ba44d49f881
parent9e007df4a7e3fafff27a0c3fa11d4ba53e785985 (diff)
downloadlibmts-io-e09f54c6b6fe35dc022fcea66935f5269866bff1.tar.gz
libmts-io-e09f54c6b6fe35dc022fcea66935f5269866bff1.tar.bz2
libmts-io-e09f54c6b6fe35dc022fcea66935f5269866bff1.zip
MTX-3404 mPower Oct20: Delta Radio Firmware Upgrade - L4E1 - libmts-io support
-rw-r--r--include/mts/MTS_IO_LE910C4EURadio.h1
-rw-r--r--include/mts/MTS_IO_TelitRadio.h50
-rw-r--r--src/MTS_IO_LE910C4EURadio.cpp4
-rw-r--r--src/MTS_IO_TelitRadio.cpp383
4 files changed, 437 insertions, 1 deletions
diff --git a/include/mts/MTS_IO_LE910C4EURadio.h b/include/mts/MTS_IO_LE910C4EURadio.h
index e872df3..a0d6baa 100644
--- a/include/mts/MTS_IO_LE910C4EURadio.h
+++ b/include/mts/MTS_IO_LE910C4EURadio.h
@@ -35,6 +35,7 @@ namespace MTS {
virtual ~LE910C4EURadio(){};
protected:
+ FOTA_GROUP getFotaGroup() override;
private:
diff --git a/include/mts/MTS_IO_TelitRadio.h b/include/mts/MTS_IO_TelitRadio.h
index bd542f1..07a95de 100644
--- a/include/mts/MTS_IO_TelitRadio.h
+++ b/include/mts/MTS_IO_TelitRadio.h
@@ -44,6 +44,10 @@ namespace MTS {
CODE getSupportedCellularModes(CELLULAR_MODES &networks) override;
CODE setCellularMode(CELLULAR_MODES networks) override;
+ CODE updateFumoLocal(int fd, UpdateCb& stepCb) override;
+ CODE fumoLocalInject(int fd, UpdateCb& stepCb) override;
+ CODE fumoLocalApply(UpdateCb& stepCb) override;
+
protected:
TelitRadio(const std::string& sName, const std::string& sRadioPort);
@@ -53,10 +57,54 @@ namespace MTS {
CODE getIsSimInserted(bool& bData) override;
CODE getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk) override;
+ enum FOTA_GROUP : uint8_t {
+ VALUE_GROUP_A = 0,
+ VALUE_GROUP_B,
+ VALUE_GROUP_C,
+ VALUE_GROUP_D,
+ VALUE_UNKNOWN
+ };
+
+ virtual FOTA_GROUP getFotaGroup();
+ virtual CODE uploadFile(int fd, UpdateCb& stepCb);
+
private:
virtual CODE getSimLockAttempts(int& iAttemptsPin, int& iAttemptsPuk, const std::string& sLockStatus);
ICellularRadio::CODE wdsList(std::set<int> &wds);
+
+ // private variable to save old firmware versions during FOTA
+ std::string m_sTelitFirmware;
+
+ static const size_t FILE_CHUNK_SIZE;
+ static const std::string CMD_ABORT_UPLOAD;
+
+ CODE getTelitFirmware(std::string& sFirmware);
+
+ CODE startFileUpload();
+ CODE abortFileUpload();
+
+ static inline void callNextStep(UpdateCb& stepCb, const char* csMessage);
+ static inline void callNextStep(UpdateCb& stepCb, const std::string& sMessage);
+
+ 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 fumoWaitRadioBooted(UpdateCb& stepCb);
+ CODE fumoCheckNewFirmware(UpdateCb& stepCb);
+
};
}
}
-#endif
+
+void MTS::IO::TelitRadio::callNextStep(ICellularRadio::UpdateCb& stepCb, const char* csMessage) {
+ if (stepCb) {
+ stepCb(Json::Value(csMessage));
+ }
+}
+
+void MTS::IO::TelitRadio::callNextStep(ICellularRadio::UpdateCb& stepCb, const std::string& sMessage) {
+ if (stepCb) {
+ stepCb(Json::Value(sMessage));
+ }
+}
+
+#endif /* MTS_IO_TELITRADIO_H_ */
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;
+}