From c04f0c7224311c2b9828e653ed4014e1ccf7a82f Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Fri, 16 Dec 2022 16:45:19 +0200 Subject: LE910: Do not exetute #RFSTS if the SIM card is PUK-locked Executing AT#RFSTS while the SIM card is locked by PIN or PUK causes a deadlock in the legacy LE910 (LAT1, LEU1, LVW2) firmware. Avoid executing AT#RFSTS if the SIM card is locked by PUK. --- src/MTS_IO_TelitRadio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp index 5db2ddf..2a07939 100644 --- a/src/MTS_IO_TelitRadio.cpp +++ b/src/MTS_IO_TelitRadio.cpp @@ -325,7 +325,7 @@ ICellularRadio::CODE TelitRadio::getNetworkStatus(Json::Value& jData) { printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str()); return SUCCESS; //return SUCCESS because getCommonNetworkStats() succeeded at top of this function } - if (sResult.find("SIM PIN") != std::string::npos) { + if (sResult.find("SIM PIN") != std::string::npos || sResult.find("SIM PUK") != std::string::npos) { printError("%s| The SIM is locked and must first be unlocked", getName().c_str()); printTrace("%s| Network Status:\n%s\n", getName().c_str(), jData.toStyledString().c_str()); return SUCCESS; //return SUCCESS because getCommonNetworkStats() succeeded at top of this function -- cgit v1.2.3 From 4b448e3c5daf34062861d0261c5a2253638b8100 Mon Sep 17 00:00:00 2001 From: Serhii Kostiuk Date: Fri, 16 Dec 2022 17:04:08 +0200 Subject: [GP-1195] Cellular debugging - add a query Define the set of debugging AT commands and a function to execute such commands. The function executes the commands one-by-one end returns raw command outputs. To be used by radio-query --diagnostics. --- include/mts/MTS_IO_CellularRadio.h | 11 +++++++ include/mts/MTS_IO_ICellularRadio.h | 21 ++++++++++++ include/mts/MTS_IO_LE910Radio.h | 2 ++ include/mts/MTS_IO_QuectelRadio.h | 2 ++ include/mts/MTS_IO_TelitRadio.h | 2 ++ src/MTS_IO_CellularRadio.cpp | 63 ++++++++++++++++++++++++++++++++++- src/MTS_IO_LE910Radio.cpp | 65 +++++++++++++++++++++++++++++++++++++ src/MTS_IO_QuectelRadio.cpp | 46 +++++++++++++++++++++++++- src/MTS_IO_TelitRadio.cpp | 32 ++++++++++++++++++ 9 files changed, 242 insertions(+), 2 deletions(-) diff --git a/include/mts/MTS_IO_CellularRadio.h b/include/mts/MTS_IO_CellularRadio.h index 60659dc..e985c21 100644 --- a/include/mts/MTS_IO_CellularRadio.h +++ b/include/mts/MTS_IO_CellularRadio.h @@ -148,6 +148,8 @@ namespace MTS { CODE getPdpContexts(Json::Value& jData) override; CODE setPdpContext(const std::string& sId, const Json::Value& jConfig) override; + CODE getDiagnostics(std::string& sReport) override; + protected: CellularRadio(const std::string& sName, const std::string& sRadioPort); @@ -267,6 +269,15 @@ namespace MTS { virtual CODE sendData(const char* pData, size_t nBytes); virtual CODE sendBasicQuery(const std::string& sCmd, const std::string& sLabel, std::string& sResult, int32_t timeoutMillis = 100, const char& ESC = ICellularRadio::CR); + /** + * @brief getDiagCommands - returns the list of Cellular Diagnostics commands for this radio. + * + * @param bIsSimReady - set to "true" if the SIM card is inserted and NOT locked by PIN or PUK. + * + * @return the list of AT commands (strings). + */ + virtual const std::vector& getDiagCommands(bool bIsSimReady = true) = 0; + class RadioBandMap : public MTS::NonCopyable { public: RadioBandMap() diff --git a/include/mts/MTS_IO_ICellularRadio.h b/include/mts/MTS_IO_ICellularRadio.h index 91ab782..0523236 100644 --- a/include/mts/MTS_IO_ICellularRadio.h +++ b/include/mts/MTS_IO_ICellularRadio.h @@ -697,6 +697,27 @@ namespace MTS { */ virtual CODE setPdpContext(const std::string& sId, const Json::Value& jConfig) = 0; + /** + * @brief Execute the diagnostics AT commands and capture their output + * + * NOTE: The list of diagnostic AT commands is radio-specific and changes + * depending on the radio model (code). + * + * NOTE: The method stops its execution if the radio repeatedly ignores + * the commands (returns no execution result for several of them). This + * is required to limit the maximum execution time of the function and + * prevent the process from occupying the radio for too long. In case + * of timeout, the report will contain the output of all commands that + * it managed to execute before the failure. + * + * @param sDiagReport - the AT commands execution report. + * + * @return CODE::SUCCESS when fetched successfully, + * CODE::NO_RESPONSE when the radio ignored several of the AT commands, + * CODE::FAILURE otherwise. + */ + virtual CODE getDiagnostics(std::string& sDiagReport) = 0; + }; } } diff --git a/include/mts/MTS_IO_LE910Radio.h b/include/mts/MTS_IO_LE910Radio.h index 2ba423d..6700ee3 100644 --- a/include/mts/MTS_IO_LE910Radio.h +++ b/include/mts/MTS_IO_LE910Radio.h @@ -50,6 +50,8 @@ namespace MTS { protected: + const std::vector& getDiagCommands(bool bIsSimReady = true) override; + private: diff --git a/include/mts/MTS_IO_QuectelRadio.h b/include/mts/MTS_IO_QuectelRadio.h index bd312b8..4315cb3 100644 --- a/include/mts/MTS_IO_QuectelRadio.h +++ b/include/mts/MTS_IO_QuectelRadio.h @@ -85,6 +85,8 @@ namespace MTS { CODE setRxDiversity(const Json::Value& jArgs) override; + const std::vector& getDiagCommands(bool bIsSimReady = true) override; + private: // private variable to save old firmware versions during FOTA std::string m_sQuectelFirmware; diff --git a/include/mts/MTS_IO_TelitRadio.h b/include/mts/MTS_IO_TelitRadio.h index a06da54..d198969 100644 --- a/include/mts/MTS_IO_TelitRadio.h +++ b/include/mts/MTS_IO_TelitRadio.h @@ -73,6 +73,8 @@ namespace MTS { virtual CODE fumoWriteGroupsABD(int fd, UpdateCb& stepCb); //virtual CODE fumoWriteGroupC(int fd, UpdateCb& stepCb); + const std::vector& getDiagCommands(bool bIsSimReady = true) override; + static bool isContainsSignChar(const std::string& str); private: diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp index 3de96ce..069d5fa 100644 --- a/src/MTS_IO_CellularRadio.cpp +++ b/src/MTS_IO_CellularRadio.cpp @@ -1868,4 +1868,65 @@ ICellularRadio::CODE CellularRadio::setPdpContext(const std::string& sId, const rc = sendBasicCommand(sCommand, dTimeout); return rc; -} \ No newline at end of file +} + +ICellularRadio::CODE CellularRadio::getDiagnostics(std::string& sReport) { + // Clear the original content before using the string as a buffer. + sReport.clear(); + + // Determine whether the SIM card is locked to select the appropriate + // list of commands for this radio. + std::string sSimLockStatus; + CODE iSimLockRet = getSimLockStatus(sSimLockStatus); + + // Re-use the SIM status detection logic from TelitRadio::getNetworkStatus. + // The SIM should be inserted and NOT locked. + bool bIsSimReady = (iSimLockRet == CODE::SUCCESS + && sSimLockStatus != "SIM PIN" + && sSimLockStatus != "SIM PUK"); + + // Determine the list of diagnostic commands required for this radio. + const auto& vCommands = getDiagCommands(bIsSimReady); + + // For all commands - set the maximum timeout to 2 seconds. + const int iMaxTimeout = 2000; + + // Allow the radio to ignore up to 2 consecutive queries before giving up. + int iMaxNoResponseCount = 2; + int iNoResponseCount = 0; + + // Execute each of the commands, add their output to the report. + for (const auto& sCmd : vCommands) { + // First line - the command to execute. + sReport.append(sCmd); + sReport.push_back(ICellularRadio::CR); + sReport.push_back(ICellularRadio::NL); + + // Execute the command. + std::string sResult = sendCommand(sCmd, ICellularRadio::DEFAULT_BAIL_STRINGS, iMaxTimeout); + + // Count the number of commands ignored by the radio. + // Normally, the radio should not ignore any of the commands, + // but radios have their own bugs. + if (sResult.empty()) { + printWarning("%s| Failed to execute the [%s] command - no response from the radio in [%d] ms", getName().c_str(), sCmd.c_str(), iMaxTimeout); + sResult = "\r\n"; + ++iNoResponseCount; + } else { + iNoResponseCount = 0; + } + + // If the radio ignored too many commands - probably it is stuck and will not + // return to operation on its own. There is no point in waiting any longer. + if (iNoResponseCount >= iMaxNoResponseCount) { + printError("%s| Failed to execute the diagnostic commands - the radio has stopped responding", getName().c_str()); + return CODE::NO_RESPONSE; + } + + // Append the command output to the report. + sReport.append(sResult); + } + + // All commands returned a non-empty output. + return CODE::SUCCESS; +} diff --git a/src/MTS_IO_LE910Radio.cpp b/src/MTS_IO_LE910Radio.cpp index ba03c5e..2f1debb 100644 --- a/src/MTS_IO_LE910Radio.cpp +++ b/src/MTS_IO_LE910Radio.cpp @@ -165,3 +165,68 @@ ICellularRadio::CODE LE910Radio::getUeModeOfOperation(ICellularRadio::UE_MODES_O return rc; } +const std::vector& LE910Radio::getDiagCommands(bool bIsSimReady) { + // Declare as static to initialize only when used, but cache the results. + const static std::vector vCommands { + // Radio model and firmware: + "AT+CGMI", "AT+CGMM", "AT+CGMR", "AT#SWPKGV", "AT#CFVR", + + // Current operator profile on the radio side: + "AT#FWSWITCH?", "AT+CGSN", + + // SIM card information: + "AT#SIMDET?", "AT#CCID", "AT+CPIN?", "AT#PCT", + + // Operating mode of the radio: + "AT+CFUN?", + + // Low-level network settings: + "AT+WS46?", "AT#RXDIV", "AT#CALLDISA?", "AT+CEMODE?", + + // Data connection configuration: + "AT+CGDCONT?", "AT#PDPAUTH?", + + // Registration and connection to the tower: + "AT+CIND?", "AT+CSQ", "AT+COPS?", "AT+CREG?", "AT+CGREG?", "AT+CEREG?", + "AT#RFSTS", "AT#PSNT?", "AT#MONI", + + // Data connection status: + "AT+CGACT?", "AT+CGCONTRDP=1", "AT+CGCONTRDP=2", "AT+CGCONTRDP=3" + }; + + const static std::vector vSimLockedCommands { + // Radio model and firmware: + "AT+CGMI", "AT+CGMM", "AT+CGMR", "AT#SWPKGV", "AT#CFVR", + + // Current operator profile on the radio side: + "AT#FWSWITCH?", "AT+CGSN", + + // SIM card information: + "AT#SIMDET?", "AT#CCID", "AT+CPIN?", "AT#PCT", + + // Operating mode of the radio: + "AT+CFUN?", + + // Low-level network settings: + "AT+WS46?", "AT#RXDIV", "AT#CALLDISA?", "AT+CEMODE?", + + // Data connection configuration: + "AT+CGDCONT?", "AT#PDPAUTH?", + + // Registration and connection to the tower. + // The same set of commands, but AT#RFSTS is replaced with a dummy command. + "AT+CIND?", "AT+CSQ", "AT+COPS?", "AT+CREG?", "AT+CGREG?", "AT+CEREG?", + "AT#RFSTS_IGNORED", "AT#PSNT?", "AT#MONI", + + // Data connection status: + "AT+CGACT?", "AT+CGCONTRDP=1", "AT+CGCONTRDP=2", "AT+CGCONTRDP=3" + }; + + // Ignore AT#RFSTS on LE910 radios (mostly legacy ones like LAT1 and LEU1) if + // the SIM card is locked by PIN or PUK. Telit Support Portal Case #5069697. + if (bIsSimReady) { + return vCommands; + } else { + return vSimLockedCommands; + } +} diff --git a/src/MTS_IO_QuectelRadio.cpp b/src/MTS_IO_QuectelRadio.cpp index ae6200e..3a5924c 100644 --- a/src/MTS_IO_QuectelRadio.cpp +++ b/src/MTS_IO_QuectelRadio.cpp @@ -1627,4 +1627,48 @@ ICellularRadio::CODE QuectelRadio::setRxDiversity(const Json::Value& jArgs) { } return SUCCESS; -} \ No newline at end of file +} + +const std::vector& QuectelRadio::getDiagCommands(bool) { + // Declare as static to initialize only when used, but cache the results. + const static std::vector vCommands { + // Radio model and firmware: + "AT+CGMI", "AT+CGMM", "AT+CGMR", "AT+QGMR", + + // Current operator profile on the radio side: + "AT+QMBNCFG=\"SELECT\"", "AT+CGSN", + + // SIM card information: + "AT+QSIMSTAT?", "AT+QCCID", "AT+CPIN?", "AT+QPINC=\"SC\"", + + // Operating mode of the radio: + "AT+CFUN?", + + // Cellular Mode (RAT selection): + "AT+QCFG=\"nwscanseq\"", "AT+QCFG=\"nwscanmode\"", + + // Cellular Diversity configuration: + "AT+QCFG=\"divctl\",\"lte\"", "AT+QCFG=\"divctl\",\"wcdma\"", "AT+QCFG=\"diversity\"", + + // Voice call support (AT&T, T-Mobile): + "AT+QNVFR=\"/nv/item_files/ims/IMS_enable\"", + "AT+QNVFR=\"/nv/item_files/modem/mmode/sms_only\"", + "AT+QNVR=5280,0", + + // UE Mode of Operation (CEMODE; AT&T): + "AT+QCFG=\"servicedomain\"", + "AT+QNVFR=\"/nv/item_files/modem/mmode/ue_usage_setting\"", + + // Data connection configuration: + "AT+CGDCONT?", "AT+QICSGP=1", "AT+QICSGP=2", "AT+QICSGP=3", + + // Registration and connection to the tower: + "AT+CIND?", "AT+CSQ", "AT+COPS?", "AT+CREG?", "AT+CGREG?", "AT+CEREG?", + "AT+QENG=\"servingcell\"", + + // Data connection status: + "AT+CGACT?", "AT+CGCONTRDP=1", "AT+CGCONTRDP=2", "AT+CGCONTRDP=3" + }; + + return vCommands; +} diff --git a/src/MTS_IO_TelitRadio.cpp b/src/MTS_IO_TelitRadio.cpp index 2a07939..4a0a8b2 100644 --- a/src/MTS_IO_TelitRadio.cpp +++ b/src/MTS_IO_TelitRadio.cpp @@ -1179,3 +1179,35 @@ bool MTS::IO::TelitRadio::isContainsSignChar(const std::string& str) { return true; } + +const std::vector& TelitRadio::getDiagCommands(bool) { + // Declare as static to initialize only when used, but cache the results. + const static std::vector vCommands { + // Radio model and firmware: + "AT+CGMI", "AT+CGMM", "AT+CGMR", "AT#SWPKGV", "AT#CFVR", + + // Current operator profile on the radio side: + "AT#FWSWITCH?", "AT+CGSN", + + // SIM card information: + "AT#SIMDET?", "AT#CCID", "AT+CPIN?", "AT#PCT", + + // Operating mode of the radio: + "AT+CFUN?", + + // Low-level network settings: + "AT+WS46?", "AT#RXDIV", "AT#CALLDISA?", "AT+CEMODE?", + + // Data connection configuration: + "AT+CGDCONT?", "AT#PDPAUTH?", + + // Registration and connection to the tower: + "AT+CIND?", "AT+CSQ", "AT+COPS?", "AT+CREG?", "AT+CGREG?", "AT+CEREG?", + "AT#RFSTS", "AT#PSNT?", "AT#MONI", + + // Data connection status: + "AT+CGACT?", "AT+CGCONTRDP=1", "AT+CGCONTRDP=2", "AT+CGCONTRDP=3" + }; + + return vCommands; +} -- cgit v1.2.3