diff options
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()); | 
