From d1798ea6a82c46b43e4caf27148189f34cc44ca5 Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Wed, 1 Jul 2020 11:25:34 +0300 Subject: WIP: Quectel Delta Radio Firmware Upgrade support - libmts-io implementation Initial implementation of the "Apply delta firmware" step. Requires cleanup. --- src/MTS_IO_QuectelRadio.cpp | 179 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) (limited to 'src/MTS_IO_QuectelRadio.cpp') diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index 2ed11b0..25d2a30 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -576,7 +577,34 @@ ICellularRadio::CODE QuectelRadio::removeDeltaFirmwareFile() { } ICellularRadio::CODE QuectelRadio::applyDeltaFirmwareFile(ICellularRadio::UpdateCb& stepCb) { - return ERROR; // not implemented + ICellularRadio::CODE rc; + std::string sCmd; + + // Send "AT+QFOTADL" command to start the upgrade. OK responce follows shortly. + sCmd = "AT+QFOTADL=\""; + sCmd += VALUE_MTS_DELTA_PATH; + sCmd += "\""; + + rc = sendBasicCommand(sCmd, 1000); + + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: failed to apply the firmware")); + } + return rc; + } + + rc = waitApplyDeltaFirmware(stepCb); + if (rc != SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO Error: failed to apply the firmware")); + } + //return rc; + } + + // TODO: Check the new firmware version + + return rc; } ICellularRadio::CODE QuectelRadio::getServiceDomain(ICellularRadio::SERVICEDOMAIN& sd) { @@ -951,6 +979,155 @@ ICellularRadio::CODE QuectelRadio::readChunk(int fd, char* pChunk, size_t dChunk return rc; } +ICellularRadio::CODE QuectelRadio::waitApplyDeltaFirmware(ICellularRadio::UpdateCb& stepCb) { + const uint32_t duDetachTimeout = 10000; // wait for 10 seconds for the radio to detach + const uint32_t duAttachTimeout = 30000; // wait for 30 seconds for the radio to attach + const int dMaxAttempts = 5; // the radio makes 5 attempts to update the firmware + + CODE rc = FAILURE; + std::string sResponse; + + for (int i = 0; i < dMaxAttempts; i++) { + + printInfo("Waiting for the radio to enter recovery mode"); + if(stepCb) { + stepCb(Json::Value("FUMO Info: waiting for the radio to enter recovery mode")); + } + + // Wait for the radio to detach from the USB bus + MTS::Thread::sleep(duDetachTimeout); + + // It's now detached. Try to reconnect + if (!resetConnection(duAttachTimeout)) { + printError("Can't connect to the radio in %d ms", (duAttachTimeout)); + + if(stepCb) { + stepCb(Json::Value("FUMO error: failed to connect to the radio in recovery mode")); + } + break; + } + + // It's now back on the bus. Wait for the URC messages. + rc = waitFotaFinish(stepCb); + if (rc == ERROR) { + // unrecoverable error + break; + } + + if (rc != SUCCESS) { + // attempt failed, radio reboots and starts its next attempt + continue; + } + + // attempt finished + break; + } + + // Wait for the radio to finish update and reboot + rc = waitNewFirmware(stepCb); + return rc; +} + +ICellularRadio::CODE QuectelRadio::waitFotaFinish(ICellularRadio::UpdateCb& stepCb) { + const uint32_t duUrcTimeout = 4 * 60 * 1000; // wait for 4 minutes for the next URC message + const std::string sFotaUrcPrefix = "+QIND: \"FOTA\""; // prefix for the URC notification messages + const std::string sFotaUrcStart = "\"START\""; + const std::string sFotaUrcProgress = "\"UPDATING\""; + const std::string sFotaUrcEnd = "\"END\""; + const std::vector vFotaBailStrings{ sFotaUrcPrefix }; + + CODE rc = ERROR; + std::string sResponse; + + while (true) { // breaks on "FOTA","END" + sResponse = sendCommand("", vFotaBailStrings, duUrcTimeout, 0x00); + printTrace("Radio response: [%s]", sResponse.c_str()); + + if (sResponse.find(sFotaUrcPrefix) == std::string::npos) { + printError("No URC messages from the radio in %d ms", duUrcTimeout); + + if(stepCb) { + stepCb(Json::Value("FUMO error: timeout, radio is not responding")); + } + + break; + } + + const auto vParts = MTS::Text::split(MTS::Text::trim(sResponse), ',', 3); + const std::string& sStage = vParts[1]; + if (sStage == sFotaUrcEnd) { + // FOTA finished + printTrace("Got FOTA END message"); + const std::string& sCode = vParts[2]; + + if (sCode == "0") { + // finished successfully + rc = SUCCESS; + break; + } + // attempt failed, the radio attempts to recover + rc = FAILURE; + break; + } else if (sStage == sFotaUrcStart) { + printTrace("Got FOTA START message"); + } else if (sStage == sFotaUrcProgress) { + printTrace("Got FOTA progress message"); + const std::string& sPercents = vParts[2]; + printInfo("FOTA progress: [%s]", sPercents.c_str()); + } else { + printInfo("FOTA unexpected URC code: [%s]", sStage.c_str()); + } + } + + return rc; +} + +ICellularRadio::CODE QuectelRadio::waitNewFirmware(ICellularRadio::UpdateCb& stepCb) { + MTS::Timer oTimer; + oTimer.start(); + std::string sFirmware; + CODE rc = FAILURE; + + while (oTimer.getSeconds() < (5 * 60)) { // 5 minutes + + MTS::Thread::sleep(10000); + + if (getFirmware(sFirmware) != SUCCESS) { + // The radio is probably unavailable + resetConnection(100); + continue; + } + +#if 0 + if (sFirmware == m_sFw && sFirmwareBuild == m_sFwBuild) { + // Have the same firmware. The radio resets several time + // before the firmware is actually get upgraded. So keep polling. + continue; + } +#endif + + // The firmware numbers have changed + printTrace("Current firmware: %s", sFirmware.c_str()); + rc = SUCCESS; + break; + + } + oTimer.stop(); + + if (rc == SUCCESS) { + if(stepCb) { + stepCb(Json::Value("FUMO done: radio firmware applied successfully")); + } + } + else { + if(stepCb) { + stepCb(Json::Value("FUMO error: radio firmware has not been updated")); + } + } + + return rc; +} + ICellularRadio::CODE QuectelRadio::startFileUpload(const std::string& sTargetFilename, size_t nBytes) { const std::vector vBailStrings{ ICellularRadio::RSP_CONNECT, ICellularRadio::RSP_ERROR }; const int dTimeout = 1000; //ms -- cgit v1.2.3