summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mts/MTS_IO_CdmaRadio.h3
-rw-r--r--include/mts/MTS_IO_CellularRadio.h34
-rw-r--r--include/mts/MTS_IO_ICellularRadio.h15
-rw-r--r--src/MTS_IO_CdmaRadio.cpp10
-rw-r--r--src/MTS_IO_CellularRadio.cpp174
-rw-r--r--src/MTS_IO_ICellularRadio.cpp1
6 files changed, 237 insertions, 0 deletions
diff --git a/include/mts/MTS_IO_CdmaRadio.h b/include/mts/MTS_IO_CdmaRadio.h
index 7cdba02..3b23937 100644
--- a/include/mts/MTS_IO_CdmaRadio.h
+++ b/include/mts/MTS_IO_CdmaRadio.h
@@ -75,6 +75,9 @@ namespace MTS {
virtual CODE getNetworkStatus(Json::Value& jData);
+ CODE getSimMccMnc(std::string& sMccMnc) override;
+ CODE getSimMccMnc(std::string& sMcc, std::string& sMnc) override;
+
protected:
CdmaRadio(const std::string& sName, const std::string& sRadioPort);
diff --git a/include/mts/MTS_IO_CellularRadio.h b/include/mts/MTS_IO_CellularRadio.h
index 7e7454b..3783705 100644
--- a/include/mts/MTS_IO_CellularRadio.h
+++ b/include/mts/MTS_IO_CellularRadio.h
@@ -137,6 +137,9 @@ namespace MTS {
CODE setUeModeOfOperation(ICellularRadio::UE_MODES_OF_OPERATION mode) override;
CODE getUeModeOfOperation(ICellularRadio::UE_MODES_OF_OPERATION& mode) override;
+ CODE getSimMccMnc(std::string& sMccMnc) override;
+ CODE getSimMccMnc(std::string& sMcc, std::string& sMnc) override;
+
protected:
CellularRadio(const std::string& sName, const std::string& sRadioPort);
@@ -183,6 +186,37 @@ namespace MTS {
//! Get carrier code based on the SIM card ID (ICCID)
virtual CODE getSimCarrierCode(const std::string& sIccid, std::string& sCarrierCode);
+ /**
+ * @brief simAccessReadBinary - Read a string of bytes from the SIM Elementary File.
+ * Please see ETSI TS 102 221 v16.4.0 and ETSI TS 127 007 v16.6.0 for the details.
+ *
+ * @param iFiledId - identifier of the Elementary File to read from.
+ * @param iP1 - offset (ETSI TS 102 221 v16.4.0, Section 11.1.3.2).
+ * @param iP2 - offset low (ETSI TS 102 221 v16.4.0, Section 11.1.3.2).
+ * @param iLe - number of bytes to be read (ETSI TS 102 221 v16.4.0, Section 11.1.3.2).
+ * @param sResult - container for the hex-encoded result string,
+ * example for EFpnn: "004F474F21FFFFFFFFFFFFFFFFFFFFFFFF".
+ * @return CODE::SUCCESS when the read is completed successfully,
+ * CODE::NOT_APPLICABLE when the modem doesn't support this feature,
+ * CODE::NO_RESPONSE when the modem doesn't respond,
+ * CODE::ERROR when the radio returns "ERROR" (SIM card removed, SIM card locked etc),
+ * CODE::FAILURE otherwise (unexpected response, no data in SIM etc).
+ */
+ virtual CODE simAccessReadBinary(uint16_t iFiledId, uint8_t iP1, uint8_t iP2, uint8_t iLe, std::string& sResult);
+
+ /**
+ * @brief getSimMncLength - Read the length of the MNC from SIM EFad.
+ * Please see ETSI TS 131 102 V16.4.0, Section 4.2.18 for the details.
+ *
+ * @param iLength - container for the number of digits in SIM MNC.
+ * @return CODE::SUCCESS when the read is completed successfully,
+ * CODE::NOT_APPLICABLE when the modem doesn't support this feature,
+ * CODE::NO_RESPONSE when the modem doesn't respond,
+ * CODE::ERROR when the radio returns "ERROR" (SIM card removed, SIM card locked etc),
+ * CODE::FAILURE otherwise (unexpected response, no data in SIM etc).
+ */
+ virtual CODE getSimMncLength(uint8_t& iLength);
+
void initMipProfile(Json::Value& jData);
bool splitAndAssign(const std::string& sLine, const std::string& sKey, Json::Value& jParent, const std::string& sJsonKey, Json::ValueType eType = Json::ValueType::stringValue);
diff --git a/include/mts/MTS_IO_ICellularRadio.h b/include/mts/MTS_IO_ICellularRadio.h
index e638e8b..bed4dab 100644
--- a/include/mts/MTS_IO_ICellularRadio.h
+++ b/include/mts/MTS_IO_ICellularRadio.h
@@ -151,6 +151,7 @@ namespace MTS {
static const char *KEY_MSL; //!< Master Subsidy Lock
static const char *KEY_SUPPORTED_CELL_MODES; //!< Comma-separated list of all supported cellular modes (2g,3g,4g)
static const char *KEY_SIM_CARRIER_CODE; //!< Unique carrier identifier based on the SIM card information.
+ static const char *KEY_SIM_MCC_MNC; //!< MCC/MNC of the home network from the SIM.
//Network Status Data
@@ -576,6 +577,20 @@ namespace MTS {
virtual CODE setUeModeOfOperation(ICellularRadio::UE_MODES_OF_OPERATION mode) = 0;
virtual CODE getUeModeOfOperation(ICellularRadio::UE_MODES_OF_OPERATION& mode) = 0;
+
+ /**
+ * @brief getSimMccMnc - get MCC/MNC (PLMN code) of the home network from the SIM.
+ *
+ * @param sPlmnId - a string to be filled with the MCC/MNC combination,
+ * example: "310410" (for AT&T), "90118" (for WMS).
+ * @return CODE::SUCCESS when fetched successfully,
+ * CODE::NOT_APPLICABLE when the modem doesn't support this feature,
+ * CODE::NO_RESPONSE when the modem doesn't respond,
+ * CODE::ERROR when the radio returns "ERROR" (SIM card removed, SIM card locked etc),
+ * CODE::FAILURE otherwise (unexpected response, no data in SIM etc).
+ */
+ virtual CODE getSimMccMnc(std::string& sMccMnc) = 0;
+ virtual CODE getSimMccMnc(std::string& sMcc, std::string& sMnc) = 0;
};
}
}
diff --git a/src/MTS_IO_CdmaRadio.cpp b/src/MTS_IO_CdmaRadio.cpp
index f84fd34..de02055 100644
--- a/src/MTS_IO_CdmaRadio.cpp
+++ b/src/MTS_IO_CdmaRadio.cpp
@@ -1290,6 +1290,16 @@ ICellularRadio::CODE CdmaRadio::getNetworkStatus(Json::Value& jData) {
return SUCCESS;
}
+ICellularRadio::CODE CdmaRadio::getSimMccMnc(std::string&) {
+ printTrace("%s| Get MCC/MNC of the home network from the SIM: not applicable", getName().c_str());
+ return NOT_APPLICABLE;
+}
+
+ICellularRadio::CODE CdmaRadio::getSimMccMnc(std::string&, std::string&) {
+ printTrace("%s| Get MCC/MNC of the home network from the SIM: not applicable", getName().c_str());
+ return NOT_APPLICABLE;
+}
+
std::string CdmaRadio::getMeidLastSix() {
std::string sMeid;
if(getMeid(sMeid) != SUCCESS || sMeid.size() != 14) {
diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp
index c2fcfad..5b6521f 100644
--- a/src/MTS_IO_CellularRadio.cpp
+++ b/src/MTS_IO_CellularRadio.cpp
@@ -977,6 +977,180 @@ ICellularRadio::CODE CellularRadio::getSimCarrierCode(const std::string& sIccid,
return SUCCESS; // no error cases for now
}
+ICellularRadio::CODE CellularRadio::simAccessReadBinary(uint16_t iFileId, uint8_t iP1, uint8_t iP2, uint8_t iLe, std::string& sResult) {
+ printTrace("%s| Read binary from the SIM Elementary File", m_sName.c_str());
+
+ // +CRSM=176,<fileid>,<P1>,<P2>,<P3/Le>[,<data>[,<pathid>]]
+ std::string sCmd = "AT+CRSM=176,";
+ sCmd += MTS::Text::format(iFileId);
+ sCmd += ',';
+ sCmd += MTS::Text::format(iP1);
+ sCmd += ',';
+ sCmd += MTS::Text::format(iP2);
+ sCmd += ',';
+ sCmd += MTS::Text::format(iLe);
+
+ std::string sRawResponse = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 3000);
+ printTrace("%s| Raw response from the radio: [%s]", m_sName.c_str(), sRawResponse.c_str());
+
+ if (sRawResponse.empty()) {
+ printError("%s| No response from the radio in 3 seconds.", m_sName.c_str());
+ return CODE::NO_RESPONSE;
+ }
+
+ if (sRawResponse.rfind(RSP_ERROR) != std::string::npos) {
+ printError("%s| Failed to read from the SIM Elementary File: [%s]", m_sName.c_str(), sRawResponse.c_str());
+ return CODE::ERROR;
+ }
+
+ // Trim the output to remove excess whitespaces and line separators.
+ sRawResponse = MTS::Text::trim(sRawResponse);
+
+ // The response should start with "+CRSM: ".
+ const std::string sResponsePrefix = "+CRSM: ";
+ if (sRawResponse.rfind(sResponsePrefix, 0) != 0) {
+ printError("%s| Unexpected response from the radio: [%s]", m_sName.c_str(), sRawResponse.c_str());
+ return CODE::FAILURE;
+ }
+
+ // Select eveything between the prefix and the next line.
+ auto eolPos = sRawResponse.find(CR, sResponsePrefix.size());
+ sRawResponse = sRawResponse.substr(sResponsePrefix.size(), eolPos - sResponsePrefix.size());
+
+ // Split the output by commas. Example: 144,0,"00FFFF02"
+ auto vOutput = MTS::Text::split(sRawResponse, ',', 3);
+ if (vOutput.size() < 3) {
+ printError("%s| Unexpected response from the radio: [%s]", m_sName.c_str(), sRawResponse.c_str());
+ return CODE::FAILURE;
+ }
+
+ // Two unquoted integers
+ const std::string& sSw1 = vOutput[0];
+ const std::string& sSw2 = vOutput[1];
+
+ // Check if the SIM indicates any errors
+ if (sSw1 != "144" || sSw2 != "0") {
+ printError("%s| Unexpected response from the SIM: [%s]", m_sName.c_str(), sRawResponse.c_str());
+ return CODE::FAILURE;
+ }
+
+ // Quectel radios quote the third element of the output. Remove the quoting.
+ const std::string& sResponse = MTS::Text::trim(vOutput[2], '"');
+
+ sResult = sResponse;
+ return CODE::SUCCESS;
+}
+
+ICellularRadio::CODE CellularRadio::getSimMncLength(uint8_t& iLength) {
+ printTrace("%s| Get SIM MNC length", m_sName.c_str());
+
+ const int iEfadId = 0x6FAD;
+ const uint8_t iOffsetHigh = 0;
+ const uint8_t iOffsetLow = 0;
+ const uint8_t iNumBytes = 0;
+
+ CODE rc;
+ std::string sEFadContent;
+
+ rc = simAccessReadBinary(iEfadId, iOffsetLow, iOffsetHigh, iNumBytes, sEFadContent);
+ if (rc != CODE::SUCCESS) {
+ printError("%s| Failed to determine the SIM MNC length", m_sName.c_str());
+ return rc;
+ }
+
+ // length of MNC in the IMSI is stored in byte 4 of EFad (indexing from 1)
+ const uint8_t iMncLengthEfadIdx = 4;
+ const uint8_t iCharsPerByte = 2;
+ const uint8_t iMinEFadLength = iMncLengthEfadIdx * iCharsPerByte;
+
+ if (sEFadContent.size() < iMinEFadLength) {
+ printError("%s| SIM EFad does not contain an MNC length byte: [%s]", m_sName.c_str(), sEFadContent.c_str());
+ return CODE::FAILURE;
+ }
+
+ // read byte 4 of EFad (indexing from 1) with the MNC length
+ const size_t iMncStartPosition = (iMncLengthEfadIdx - 1) * iCharsPerByte;
+ const std::string sMncLength = sEFadContent.substr(iMncStartPosition, iCharsPerByte);
+ uint8_t iMncLength;
+
+ // parse hex to unsigned byte
+ if (!MTS::Text::parseHex(iMncLength, sMncLength)) {
+ printError("%s| Unexpected SIM EFad content: [%s]", m_sName.c_str(), sEFadContent.c_str());
+ return CODE::FAILURE;
+ }
+
+ // Only the lower 4 bits are used for MNC length, others are reserved for future use.
+ iMncLength &= 0x0F;
+
+ // Done
+ iLength = iMncLength;
+ printDebug("%s| Got MNC length of [%u]", m_sName.c_str(), iLength);
+
+ return CODE::SUCCESS;
+}
+
+ICellularRadio::CODE CellularRadio::getSimMccMnc(std::string& sMccMnc) {
+ printTrace("%s| Get MCC/MNC of the home network from the SIM", m_sName.c_str());
+
+ CODE rc;
+ std::string sImsi;
+ uint8_t iMncLength;
+
+ do {
+ rc = getImsi(sImsi);
+ if (rc != CODE::SUCCESS) {
+ printError("%s| Failed to get SIM IMSI", m_sName.c_str());
+ break;
+ }
+
+ if (sImsi.size() < 5) {
+ printError("%s| Unexpected IMSI value: [%s]", m_sName.c_str(), sImsi.c_str());
+ rc = CODE::FAILURE;
+ break;
+ }
+
+ rc = getSimMncLength(iMncLength);
+ if (rc != CODE::SUCCESS) {
+ printError("%s| Failed to determine the MNC length", m_sName.c_str());
+ break;
+ }
+
+ // MNC shall be 2 or 3 characters long
+ if (iMncLength < 2 || iMncLength > 3) {
+ printError("%s| Unexpected MNC length: [%u]", m_sName.c_str(), iMncLength);
+ rc = CODE::FAILURE;
+ break;
+ }
+
+ // PLMN code shall be 5 or 6 characters long
+ const size_t mncLength = 3;
+ const size_t plmnCodeLength = mncLength + iMncLength;
+
+ sMccMnc = sImsi.substr(0, plmnCodeLength);
+
+ // Done
+ printDebug("%s| Got MCC/MNC of the home network from the SIM: [%s]", m_sName.c_str(), sMccMnc.c_str());
+ rc = CODE::SUCCESS;
+ } while (false);
+
+ return rc;
+}
+
+ICellularRadio::CODE CellularRadio::getSimMccMnc(std::string& sMcc, std::string& sMnc) {
+ CODE rc;
+ std::string sPlmnCode;
+
+ rc = getSimMccMnc(sPlmnCode);
+
+ if (rc == CODE::SUCCESS) {
+ // PLMN code consists of MCC (first 3 digits) and MNC (second 2 or 3 digits)
+ sMcc = sPlmnCode.substr(0, 3);
+ sMnc = sPlmnCode.substr(3);
+ }
+
+ return rc;
+}
+
ICellularRadio::CODE CellularRadio::validateMsl(const Json::Value&) {
printTrace("%s| Validate MSL", m_sName.c_str());
diff --git a/src/MTS_IO_ICellularRadio.cpp b/src/MTS_IO_ICellularRadio.cpp
index 0483dc8..042faf1 100644
--- a/src/MTS_IO_ICellularRadio.cpp
+++ b/src/MTS_IO_ICellularRadio.cpp
@@ -52,6 +52,7 @@ const char *MTS::IO::ICellularRadio::KEY_ICCID = "iccid"; //!< Integrated
const char *MTS::IO::ICellularRadio::KEY_MSL = "msl"; //!< Master Subsidy Lock
const char *MTS::IO::ICellularRadio::KEY_SUPPORTED_CELL_MODES = "supportedCellularModes"; //!< Comma-separated list of all supported cellular modes (2g,3g,4g)
const char *MTS::IO::ICellularRadio::KEY_SIM_CARRIER_CODE = "simCarrierCode"; //!< Unique carrier identifier based on the SIM card information.
+const char *MTS::IO::ICellularRadio::KEY_SIM_MCC_MNC = "simMccMnc"; //!< MCC/MNC of the home network from the SIM.
//Dynamic Data
const char *MTS::IO::ICellularRadio::KEY_ROAMING = "roaming"; //!< Indicates whether or not using Home Network
const char *MTS::IO::ICellularRadio::KEY_DATETIME = "datetime"; //!< Date and Time from tower