diff options
| -rw-r--r-- | include/mts/MTS_IO_LE910C4EURadio.h | 1 | ||||
| -rw-r--r-- | include/mts/MTS_IO_TelitRadio.h | 50 | ||||
| -rw-r--r-- | src/MTS_IO_LE910C4EURadio.cpp | 4 | ||||
| -rw-r--r-- | src/MTS_IO_TelitRadio.cpp | 383 | 
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; +} | 
