diff options
author | Jeff Hatch <jhatch@multitech.com> | 2021-06-02 14:55:50 -0500 |
---|---|---|
committer | Jeff Hatch <jhatch@multitech.com> | 2021-06-02 14:55:50 -0500 |
commit | 8eb97c149a08f6ec9d938be53868ee426895bf0e (patch) | |
tree | fed63fa776088659855a9ae3b2c7f5d221ec3a25 /src/MTS_IO_CellularRadio.cpp | |
parent | bcd5443bcfff3e17ee120c305bbfd0ce2c812b21 (diff) | |
parent | bc2875140ba6a91eaeab8e9626a212986dcf5d4d (diff) | |
download | libmts-io-1.0.26.tar.gz libmts-io-1.0.26.tar.bz2 libmts-io-1.0.26.zip |
Merge branch 'sk/GP-1111-carrier-detection' into 'master'
1.0.26
[GP-1111] mPower R. Apr 2021: +CEMODE shall be set to CEMODE=2
See merge request !40
Diffstat (limited to 'src/MTS_IO_CellularRadio.cpp')
-rw-r--r-- | src/MTS_IO_CellularRadio.cpp | 278 |
1 files changed, 249 insertions, 29 deletions
diff --git a/src/MTS_IO_CellularRadio.cpp b/src/MTS_IO_CellularRadio.cpp index c2fcfad..1850a60 100644 --- a/src/MTS_IO_CellularRadio.cpp +++ b/src/MTS_IO_CellularRadio.cpp @@ -425,26 +425,21 @@ ICellularRadio::CODE CellularRadio::getType(std::string& sType) { ICellularRadio::CODE CellularRadio::getCarrier(std::string& sCarrier) { printTrace("%s| Get Carrier", m_sName.c_str()); - if(m_sCarrier == "") { - Json::Value jData; - if(getNetworkStatus(jData) == SUCCESS) { - if(jData.isMember(ICellularRadio::KEY_MCC) && jData.isMember(ICellularRadio::KEY_MNC)) { - std::string sMcc = jData[ICellularRadio::KEY_MCC].asString(); - std::string sMnc = jData[ICellularRadio::KEY_MNC].asString(); - Json::Value jLookup = MccMncTable::getInstance()->lookup(sMcc, sMnc); - printTrace("%s| MCC-MNC Lookup: [%s][%s][%s]", m_sName.c_str(), - sMcc.c_str(), sMnc.c_str(), jLookup.toStyledString().c_str()); - if(jLookup.isMember(ICellularRadio::KEY_CARRIER)) { - m_sCarrier = jLookup[ICellularRadio::KEY_CARRIER].asString(); - } else { - printWarning("%s| MCC-MNC Lookup did not contain carrier", m_sName.c_str()); - return FAILURE; - } + if (m_sCarrier == "") { + std::string sMcc; + std::string sMnc; + if (getSimMccMnc(sMcc, sMnc) == CODE::SUCCESS) { + Json::Value jLookup = MccMncTable::getInstance()->lookup(sMcc, sMnc); + printTrace("%s| MCC-MNC Lookup: [%s][%s][%s]", m_sName.c_str(), + sMcc.c_str(), sMnc.c_str(), jLookup.toStyledString().c_str()); + if (jLookup.isMember(ICellularRadio::KEY_CARRIER)) { + m_sCarrier = jLookup[ICellularRadio::KEY_CARRIER].asString(); } else { - printWarning("%s| Network Status did no contain MCC or MNC", m_sName.c_str()); + printWarning("%s| MCC-MNC Lookup did not contain carrier", m_sName.c_str()); return FAILURE; } } else { + printWarning("%s| SIM did no contain MCC or MNC", m_sName.c_str()); return FAILURE; } } @@ -935,24 +930,49 @@ ICellularRadio::CODE CellularRadio::unlockSimCard(const Json::Value& jArgs) { } ICellularRadio::CODE CellularRadio::getSimCarrierCode(std::string& sCarrierCode) { - std::string sIccid; CODE rc; printTrace("%s| Get carrier code from the SIM card installed", m_sName.c_str()); - rc = getIccid(sIccid); - if (rc != SUCCESS) { - printError("%s| Unable to determine SIM carrier: Failed to fetch SIM identifier", m_sName.c_str()); - return rc; - } + do { + // Try to detect based on the ICCID + std::string sIccid; - printTrace("%s| Fetched ICCID: [%s]", m_sName.c_str(), sIccid.c_str()); + rc = getIccid(sIccid); + if (rc != SUCCESS) { + printError("%s| Unable to determine SIM carrier: Failed to fetch SIM identifier", m_sName.c_str()); + break; + } - rc = getSimCarrierCode(sIccid, sCarrierCode); - if (rc != SUCCESS) { - printError("%s| Unable to determine SIM carrier: Unable to extract carrier from the SIM identifier", m_sName.c_str()); - return rc; - } + printTrace("%s| Fetched ICCID: [%s]", m_sName.c_str(), sIccid.c_str()); + + rc = getSimCarrierCode(sIccid, sCarrierCode); + if (rc != SUCCESS) { + printError("%s| Unable to determine SIM carrier: Unable to extract carrier from the SIM identifier", m_sName.c_str()); + break; + } + + if (sCarrierCode != VALUE_UNKNOWN) { + rc = SUCCESS; + break; + } + + // Fallback to the MCC/MNC detection + std::string sMcc; + std::string sMnc; + + rc = getSimMccMnc(sMcc, sMnc); + if (rc != SUCCESS) { + printError("%s| Unable to determine SIM carrier: Failed to fetch MCC/MNC from the SIM", m_sName.c_str()); + break; + } + + rc = getSimCarrierCode(sMcc, sMnc, sCarrierCode); + if (rc != SUCCESS) { + printError("%s| Unable to determine SIM carrier: Unable to extract carrier from MCC/MNC of the SIM", m_sName.c_str()); + break; + } + } while(false); printTrace("%s| Detected carrier code: [%s]", m_sName.c_str(), sCarrierCode.c_str()); return rc; @@ -970,13 +990,213 @@ ICellularRadio::CODE CellularRadio::getSimCarrierCode(const std::string& sIccid, sCarrierCode = VALUE_CARRIER_CODE_ATT; } else { // All other carriers for which ICCID prefixes are not defined - printWarning("%s| Carrier is unknown for this SIM ID: [%s]", m_sName.c_str(), sIccid.c_str()); + printWarning("%s| Carrier code is unknown for this SIM ID: [%s]", m_sName.c_str(), sIccid.c_str()); sCarrierCode = VALUE_UNKNOWN; } return SUCCESS; // no error cases for now } +ICellularRadio::CODE CellularRadio::getSimCarrierCode(const std::string& sMcc, const std::string& sMnc, std::string& sCarrierCode) { + const Json::Value& jLookup = MccMncTable::getInstance()->lookup(sMcc, sMnc); + + do { + printTrace("%s| MCC-MNC Lookup: [%s][%s][%s]", m_sName.c_str(), + sMcc.c_str(), sMnc.c_str(), jLookup.toStyledString().c_str()); + + if (jLookup.isNull()) { + printWarning("%s| Carrier code is unknown for this MCC/NNC combination: [%s][%s]", m_sName.c_str(), sMcc.c_str(), sMnc.c_str()); + sCarrierCode = VALUE_UNKNOWN; + break; + } + + if (jLookup["carrierCode"].asString().empty()) { + printWarning("%s| Carrier code is unknown for this MCC/MNC combination: [%s][%s]", m_sName.c_str(), sMcc.c_str(), sMnc.c_str()); + sCarrierCode = VALUE_UNKNOWN; + break; + } + + sCarrierCode = jLookup["carrierCode"].asString(); + printTrace("%s| Detected carrier code by MCC/MNC: [%s]", m_sName.c_str(), sCarrierCode.c_str()); + } while (false); + + return CODE::SUCCESS; +} + +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()); |