summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSerhii Kostiuk <serhii.o.kostiuk@globallogic.com>2021-05-28 15:18:47 +0300
committerSerhii Kostiuk <serhii.o.kostiuk@globallogic.com>2021-05-29 12:44:04 +0300
commit51484668683c5e10bda111c08a300af1834a59d2 (patch)
tree036272b226f64e02f8ab3084458ead1492f1c45c
parent3589be0c1b394c33abac98c2de98a5c56d595eb3 (diff)
downloadlibmts-io-51484668683c5e10bda111c08a300af1834a59d2.tar.gz
libmts-io-51484668683c5e10bda111c08a300af1834a59d2.tar.bz2
libmts-io-51484668683c5e10bda111c08a300af1834a59d2.zip
[GP-1111] mPower R. Apr 2021: +CEMODE shall be set to CEMODE=2
Added an ability to read the PLMN ID (MCC/MNC combination) of the home carrier from the SIM. SIM PLMN ID is a part of the IMSI that contains identifier of the home network. PLMN ID in turn consists of 3-digit MCC (country code) and 2-or-3-digit MNC (network code). The length of the MNC is stored in the SIM Elementary File called "Administrative Data" (EFad for short). The purpose of the new functions is to extract PLMN ID from the SIM if this information is available.
-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