/*
* Copyright (C) 2019 by Multi-Tech Systems
*
* This file is part of libmts-io.
*
* libmts-io is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* libmts-io is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libmts-io. If not, see .
*
*/
#include "mts/MTS_IO_CellularRadio.h"
#include
#include
#include
#include
#include
#include
#include
using namespace MTS::IO;
namespace {
typedef struct
{
const char *name;
int32_t low;
int32_t high;
} *pNameRangeMap, nameRangeMap;
const unsigned int NUM_GSM_BANDS = 7;
const unsigned int NUM_WCDMA_BANDS = 6;
const unsigned int NUM_LTE_BANDS = 42;
// http://niviuk.free.fr/gsm_band.php
const nameRangeMap GSMband[] =
{
{"GSM 450", 259, 293}, {"GSM 480", 306, 340},
{"GSM 750", 438, 511}, {"GSM 850", 128, 251},
{"GSM 900 P", 1, 124}, {"GSM 900 E/R", 955, 1023},
{"GSM DCS 1800/1900", 512, 885},
};
// http://niviuk.free.fr/umts_band.php
const nameRangeMap WCDMAband[] =
{
{"BAND I", 10592, 10838}, {"BAND II", 9662, 9938},
{"BAND III", 1162, 1513}, {"BAND IV", 1537, 1738},
{"BAND V", 4357, 4458}, {"BAND VI", 4387, 4413}
};
// http://niviuk.free.fr/lte_band.php
const nameRangeMap EULTRAband[] =
{
{"EUTRAN BAND1", 0, 599}, {"EUTRAN BAND2", 600, 1199},
{"EUTRAN BAND3", 1200, 1949}, {"EUTRAN BAND4", 1950, 2399},
{"EUTRAN BAND5", 2400, 2649}, {"EUTRAN BAND6", 2650, 2749},
{"EUTRAN BAND7", 2750, 3449}, {"EUTRAN BAND8", 3450, 3799},
{"EUTRAN BAND9", 3800, 4149}, {"EUTRAN BAND10", 4150, 4749},
{"EUTRAN BAND11", 4750, 4999}, {"EUTRAN BAND12", 5000, 5179},
{"EUTRAN BAND13", 5180, 5279}, {"EUTRAN BAND14", 5280, 5379},
{"EUTRAN BAND17", 5730, 5849}, {"EUTRAN BAND18", 5850, 5999},
{"EUTRAN BAND19", 6000, 6149}, {"EUTRAN BAND20", 6150, 6449},
{"EUTRAN BAND21", 6450, 6525}, {"EUTRAN BAND22", 6600, 7399},
{"EUTRAN BAND23", 7500, 7699}, {"EUTRAN BAND24", 7700, 8039},
{"EUTRAN BAND25", 8040, 8689}, {"EUTRAN BAND26", 8690, 9039},
{"EUTRAN BAND27", 9040, 9209}, {"EUTRAN BAND28", 9210, 9659},
{"EUTRAN BAND29", 9660, 9769}, {"EUTRAN BAND30", 9770, 9869},
{"EUTRAN BAND31", 9870, 9919}, {"EUTRAN BAND32", 9920, 10359},
{"EUTRAN BAND33", 36000, 36199}, {"EUTRAN BAND34", 36200, 36349},
{"EUTRAN BAND35", 36350, 36949}, {"EUTRAN BAND36", 36950, 37549},
{"EUTRAN BAND37", 37550, 37749}, {"EUTRAN BAND38", 37750, 38249},
{"EUTRAN BAND39", 38250, 38649}, {"EUTRAN BAND40", 38650, 39649},
{"EUTRAN BAND41", 39650, 41589}, {"EUTRAN BAND42", 41590, 43589},
{"EUTRAN BAND43", 43590, 45589}, {"EUTRAN BAND44", 45590, 46589}
};
}
CellularRadio::CellularRadio(const std::string& sName, const std::string& sRadioPort)
: m_sName(sName)
, m_sRadioPort(sRadioPort)
, m_bEchoEnabled(false)
, m_bEnableEchoOnClose(false)
{
m_apIo.reset(new MTS::IO::SerialConnection(
MTS::IO::SerialConnection::Builder(m_sRadioPort)
.baudRate(115200)
.useLockFile()
.build()));
}
CellularRadio::~CellularRadio() {
shutdown();
m_apIo.reset();
}
bool CellularRadio::initialize(uint32_t iTimeoutMillis) {
if(!m_apIo->open(iTimeoutMillis)) {
printError("%s| Failed to open radio port [%s]", m_sName.c_str(), m_sRadioPort.c_str());
return false;
}
bool bEnabled;
ICellularRadio::CODE eCode = getEcho(bEnabled);
if(eCode == SUCCESS && bEnabled) {
printDebug("%s| Disabling 'echo'", m_sName.c_str());
setEcho(false);
m_bEnableEchoOnClose = true;
}
return true;
}
bool CellularRadio::resetConnection(uint32_t iTimeoutMillis) {
//Close Current Connection
if(!m_apIo.isNull()) {
m_apIo->close();
}
m_apIo.reset(new MTS::IO::SerialConnection(
MTS::IO::SerialConnection::Builder(m_sRadioPort)
.baudRate(115200)
.useLockFile()
.build()));
//Try to obtain the device port over the given period of time
MTS::Timer oTimer;
oTimer.start();
uint64_t iCurrentTime = 0;
while(iCurrentTime < iTimeoutMillis) {
if(!m_apIo->open(iTimeoutMillis - iCurrentTime)) {
printWarning("%s| Failed to re-open radio port [%s]", m_sName.c_str(), m_sRadioPort.c_str());
} else {
printInfo("%s| Successfully re-opened radio port [%s]", m_sName.c_str(), m_sRadioPort.c_str());
printDebug("%s| Recovering 'echo' after connection reset", m_sName.c_str()); // see CellularRadio::initialize
setEcho(m_bEchoEnabled);
break;
}
::usleep(500000); //500 millis
iCurrentTime = oTimer.getMillis();
}
oTimer.stop();
return !m_apIo->isClosed();
}
void CellularRadio::shutdown() {
if(!m_apIo.isNull()) {
if(m_bEnableEchoOnClose) {
printDebug("%s| Enabling 'echo'", m_sName.c_str());
setEcho(true);
m_bEnableEchoOnClose = false;
}
m_apIo->close();
}
}
const std::string& CellularRadio::getName() const {
return m_sName;
}
ICellularRadio::CODE CellularRadio::getFirmware(std::string& sFirmware) {
printTrace("%s| Get Firmware", m_sName.c_str());
sFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
std::string sCmd("AT+CGMR");
std::string sResult = sendCommand(sCmd);
size_t pos = sResult.find(ICellularRadio::RSP_OK);
if (pos == std::string::npos) {
printWarning("%s| Unable to get firmware from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
sFirmware = MTS::Text::trim(sResult.substr(0, pos));
if(sFirmware.size() == 0) {
printWarning("%s| Unable to get firmware from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
m_sFirmware = sFirmware;
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::getFirmwareBuild(std::string& sFirmwareBuild) {
sFirmwareBuild = ICellularRadio::VALUE_NOT_SUPPORTED;
return FAILURE;
}
ICellularRadio::CODE CellularRadio::getVendorFirmware(std::string& sVendorFirmware) {
sVendorFirmware = ICellularRadio::VALUE_NOT_SUPPORTED;
return FAILURE;
}
ICellularRadio::CODE CellularRadio::getHardware(std::string& sHardware) {
printTrace("%s| Get Hardware", m_sName.c_str());
sHardware = ICellularRadio::VALUE_NOT_SUPPORTED;
if(m_sFirmware.size() == 0) {
getFirmware(m_sFirmware);
}
if(getHardwareVersionFromFirmware(m_sFirmware, sHardware)) {
return SUCCESS;
}
return FAILURE;
}
ICellularRadio::CODE CellularRadio::getManufacturer(std::string& sManufacturer) {
printTrace("%s| Get Manufacturer", m_sName.c_str());
sManufacturer = ICellularRadio::VALUE_NOT_SUPPORTED;
std::string sCmd("AT+GMI");
std::string sResult = sendCommand(sCmd);
size_t pos = sResult.find(ICellularRadio::RSP_OK);
if (pos == std::string::npos) {
printWarning("%s| Unable to get manufacturer from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
sManufacturer = MTS::Text::trim(sResult.substr(0, pos));
if(sManufacturer.size() == 0) {
printWarning("%s| Unable to get manufacturer from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::getImei(std::string& sImei) {
printTrace("%s| Get IMEI", m_sName.c_str());
sImei = ICellularRadio::VALUE_NOT_SUPPORTED;
// AT+CGSN execution can take up to 300ms according to the Quectel datasheet. Setting timeout to 500ms just for sure.
std::string sCmd("AT+CGSN");
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 500);
size_t pos = sResult.find(ICellularRadio::RSP_OK);
if (pos == std::string::npos) {
printWarning("%s| Unable to get IMEI from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
sImei = MTS::Text::trim(sResult.substr(0, pos));
if(sImei.size() == 0) {
printWarning("%s| Unable to get IMEI from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::getMeid(std::string& sMeid) {
printTrace("%s| Get MEID", m_sName.c_str());
return getImei(sMeid);
}
ICellularRadio::CODE CellularRadio::getImsi(std::string& sImsi) {
printTrace("%s| Get IMSI", m_sName.c_str());
sImsi = ICellularRadio::VALUE_NOT_SUPPORTED;
// AT+CIMI execution can take up to 300ms according to the Quectel datasheet. Setting timeout to 500ms just for sure.
std::string sCmd("AT+CIMI");
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 500);
size_t pos = sResult.find(ICellularRadio::RSP_OK);
if (pos == std::string::npos) {
printWarning("%s| Unable to get IMSI from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
sImsi = MTS::Text::trim(sResult.substr(0, pos));
if(sImsi.size() == 0) {
printWarning("%s| Unable to get IMSI from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::getSimStatus(std::string& sSimStatus) {
printTrace("%s| Get SIM Status", getName().c_str());
sSimStatus = ICellularRadio::VALUE_UNKNOWN;
return FAILURE;
}
ICellularRadio::CODE CellularRadio::getSimStatusSummary(Json::Value& jData) {
bool bIsSimInserted = false;
bool bIsSimLocked = true;
int iAttemptsPin = 0;
int iAttemptsPuk = 0;
std::string sSimLockStatus;
ICellularRadio::CODE retCode;
do {
retCode = getIsSimInserted(bIsSimInserted);
if (retCode != SUCCESS) {
break;
}
if (!bIsSimInserted) {
// There is not much left to do. Return one field only.
jData[KEY_IS_SIM_INSERTED] = bIsSimInserted;
break;
}
// The following code assumes that the SIM card is inserted
retCode = getSimLockStatus(sSimLockStatus);
if (retCode != SUCCESS) {
/* IN:4033:
*
* On some devices #SIMDET reports "inserted" but +CPIN? returns ERROR when there is
* no SIM card in the slot. It's also the case when only plastic holder is inserted
* instead of the SIM itself.
*
* Interpret this error as "SIM card not detected" for such cases.
*/
jData[KEY_IS_SIM_INSERTED] = false;
retCode = SUCCESS;
break;
}
bIsSimLocked = (sSimLockStatus != "READY"); // SIM PIN, SIM PUK or other values
retCode = getSimLockAttempts(iAttemptsPin, iAttemptsPuk);
if (retCode != SUCCESS) {
break;
}
// Everything fetched successfully. Populate the jData object
jData[KEY_IS_SIM_INSERTED] = bIsSimInserted;
jData[KEY_IS_SIM_LOCKED] = bIsSimLocked;
jData[KEY_SIM_LOCK_STATUS] = sSimLockStatus;
jData[KEY_ATTEMPTS_PIN] = iAttemptsPin;
jData[KEY_ATTEMPTS_PUK] = iAttemptsPuk;
} while (false);
return retCode;
}
ICellularRadio::CODE CellularRadio::getLac(std::string& sLac) {
Json::Value jData;
printTrace("%s| Get LAC", m_sName.c_str());
sLac = ICellularRadio::VALUE_NOT_SUPPORTED;
if(getNetworkStatus(jData) == SUCCESS) {
if(jData.isMember(ICellularRadio::KEY_LAC)) {
sLac = jData[ICellularRadio::KEY_LAC].asString();
return SUCCESS;
}
}
return FAILURE;
}
ICellularRadio::CODE CellularRadio::getMdn(std::string& sMdn) {
printTrace("%s| Get MDN", m_sName.c_str());
sMdn = ICellularRadio::VALUE_NOT_SUPPORTED;
// AT+CNUM execution can take up to 300ms according to the Quectel datasheet. Setting timeout to 500ms just for sure.
std::string sCmd("AT+CNUM");
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 500);
size_t end = sResult.find(ICellularRadio::RSP_OK);
if (end == std::string::npos) {
printWarning("%s| Unable to get MDN from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
size_t start = sResult.find("CNUM:");
if(start != std::string::npos) {
start += sizeof("CNUM:");
std::vector vParts = MTS::Text::split(sResult.substr(start, end - start), ',');
if(vParts.size() < 3) {
printWarning("%s| Unable to parse MDN from response [%s]", m_sName.c_str(), sResult.c_str());
return FAILURE;
}
sMdn = MTS::Text::strip(vParts[1], '"');
if(sMdn.size() == 0) {
printWarning("%s| Unable to get MDN from radio using command [%s]. MDN may not be set.", m_sName.c_str(), sCmd.c_str());
}
} else {
sMdn = "";
printWarning("%s| Unable to get MDN from radio using command [%s]. MDN may not be set.", m_sName.c_str(), sCmd.c_str());
}
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::getMsid(std::string& sMsid) {
printTrace("%s| Get MSID", m_sName.c_str());
sMsid = "";
std::string sImsi;
if(getImsi(sImsi) == SUCCESS) {
if(sImsi.size() >= 10) {
sMsid = sImsi.substr(sImsi.size() - 10);
printTrace("IMSI: [%s] MEID [%s]", sImsi.c_str(), sMsid.c_str());
return SUCCESS;
}
}
printWarning("%s| Unable to get MSID from radio", m_sName.c_str());
return FAILURE;
}
ICellularRadio::CODE CellularRadio::getType(std::string& sType) {
printTrace("%s| Get Type", m_sName.c_str());
sType = ICellularRadio::VALUE_NOT_SUPPORTED;
return FAILURE;
}
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;
}
} else {
printWarning("%s| Network Status did no contain MCC or MNC", m_sName.c_str());
return FAILURE;
}
} else {
return FAILURE;
}
}
sCarrier = m_sCarrier;
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::getTower(std::string& sTower) {
Json::Value jData;
printTrace("%s| Get Tower", m_sName.c_str());
sTower = ICellularRadio::VALUE_NOT_SUPPORTED;
if(getNetworkStatus(jData) == SUCCESS) {
if(jData.isMember(ICellularRadio::KEY_CID)) {
sTower = jData[ICellularRadio::KEY_CID].asString();
return SUCCESS;
}
}
return FAILURE;
}
ICellularRadio::CODE CellularRadio::getTime(std::string& sDate, std::string& sTime, std::string& sTimeZone) {
Json::Value jData;
printTrace("%s| Get Time", m_sName.c_str());
sDate = "";
sTime = "";
sTimeZone = "";
std::string sCmd("AT+CCLK?");
std::string sResult = sendCommand(sCmd);
size_t end = sResult.find(ICellularRadio::RSP_OK);
if (end == std::string::npos) {
printWarning("%s| Unable to get Time from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
size_t start = sResult.find("CCLK: ");
if(start != std::string::npos) {
start += sizeof("CCLK: ");
std::string sValue = MTS::Text::trim(sResult.substr(start, end - start));
sValue = MTS::Text::strip(sValue, '"');
std::vector vParts = MTS::Text::split(sValue, ',');
if(vParts.size() != 2) {
printWarning("%s| Unable to parse Date from response [%s]", m_sName.c_str(), sResult.c_str());
return FAILURE;
}
std::vector vDateParts = MTS::Text::split(vParts[0], '/');
if(vDateParts.size() != 3) {
printWarning("%s| Unable to parse Date from response [%s]", m_sName.c_str(), sResult.c_str());
return FAILURE;
}
//The Date format is YY/MM/DD -> Change to MM/DD/YY
sDate = vDateParts[1] + "/" + vDateParts[2] + "/" + vDateParts[0];
vParts = MTS::Text::split(vParts[1], '-');
if(vParts.size() != 2) {
printWarning("%s| Unable to parse Time from response [%s]", m_sName.c_str(), sResult.c_str());
return FAILURE;
}
sTime = vParts[0];
int32_t iZoneUnits; //the difference, expressed in quarters of an hour, between the local time and GMT
if(!MTS::Text::parse(iZoneUnits, MTS::Text::strip(vParts[1], '+'))) {
printWarning("%s| Unable to parse Time Zone from response [%s]", m_sName.c_str(), sResult.c_str());
return FAILURE;
}
int32_t iZone = iZoneUnits/4; //Divide by 4 to get hours difference
int32_t iZonePartial = (iZoneUnits % 4) * 15; //Remainder in minutes
std::string sPlusSign = "+";
if(iZonePartial < 0) {
//Remove negative sign from partial and clear plus sign component
iZonePartial *= -1;
sPlusSign = "";
}
std::stringstream ss;
ss << sPlusSign << iZone;
if(iZonePartial != 0) {
ss << ":" << iZonePartial;
}
sTimeZone = ss.str();
return SUCCESS;
} else {
printWarning("%s| Unable to get Time from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
}
return FAILURE;
}
ICellularRadio::CODE CellularRadio::getRoaming(bool& bRoaming) {
Json::Value jData;
printTrace("%s| Get Roaming", m_sName.c_str());
bRoaming = false;
REGISTRATION eReg;
if(getRegistration(eReg) == SUCCESS) {
bRoaming = (eReg == ROAMING);
return SUCCESS;
}
return FAILURE;
}
ICellularRadio::CODE CellularRadio::getSignalStrength(int32_t& rssi) {
printTrace("%s| Get Signal Strength", m_sName.c_str());
// AT+CSQ execution can take up to 300ms according to the Quectel datasheet. Setting timeout to 500ms just for sure.
std::string sCmd("AT+CSQ");
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 500);
if (sResult.find("+CSQ: ") == std::string::npos) {
printDebug("%s| Signal Strength command returned unexpected response: [%s]", m_sName.c_str(), sResult.c_str());
return FAILURE;
}
size_t start = sResult.find(':');
size_t stop = sResult.find(',', start);
if(start == std::string::npos || stop == std::string::npos) {
printDebug("%s| Signal Strength command returned malformed response: [%s]", m_sName.c_str(), sResult.c_str());
return FAILURE;
}
std::string signal = sResult.substr(start + 2, stop - start - 2);
sscanf(signal.c_str(), "%d", &rssi);
printDebug("%s| Signal Strength: [%d]", m_sName.c_str(), rssi);
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::getModemLocation(std::string&) {
printTrace("%s|CellularRadio getModemLocation - not supported", m_sName.c_str());
return FAILURE;
}
ICellularRadio::CODE CellularRadio::getEcho(bool& bEnabled) {
printTrace("%s| Echo Test", m_sName.c_str());
std::string sResult = sendCommand("AT");
if(sResult.size() == 0) {
return NO_RESPONSE;
}
if(sResult.find("AT") != std::string::npos) {
bEnabled = true;
} else {
bEnabled = false;
}
m_bEchoEnabled = bEnabled;
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::setEcho(bool bEnabled) {
ICellularRadio::CODE eCode = FAILURE;
if(bEnabled) {
eCode = sendBasicCommand("ATE1");
m_bEchoEnabled = (eCode == SUCCESS ) ? true : m_bEchoEnabled;
} else {
eCode = sendBasicCommand("ATE0");
m_bEchoEnabled = (eCode == SUCCESS ) ? false : m_bEchoEnabled;
}
return eCode;
}
ICellularRadio::CODE CellularRadio::getStaticInformation(Json::Value& jData) {
printTrace("%s| Get Static Information", m_sName.c_str());
printTrace("%s| Static Information:\n%s\n", m_sName.c_str(), jData.toStyledString().c_str());
return FAILURE;
}
// Get the LAC for the LTE radio that's not in the #RFSTS or +QENG response
std::string CellularRadio::queryLteLac() {
std::string CGREGstring;
std::string originalCGREG;
std::string result;
CGREGstring = queryCGREGstring();
if (CGREGstring == ICellularRadio::RSP_ERROR) {
originalCGREG = "0";
} else {
originalCGREG = CGREGstring.at(CGREGstring.find(",") - 1); //Position right before first comma ("+CGREG: 0,1")
}
// Temporarily set CGREG=2 to get more info
setCGREG("2");
CGREGstring = queryCGREGstring();
if (CGREGstring == ICellularRadio::RSP_ERROR) {
result = ICellularRadio::VALUE_UNKNOWN;
} else {
size_t start = CGREGstring.find(":") + 1; //Position right after "+CGREG:"
std::vector vParts = MTS::Text::split(MTS::Text::trim(CGREGstring.substr(start)), ",");
if(vParts.size() < 3) {
result = ICellularRadio::VALUE_UNAVAILABLE;
} else {
result = MTS::Text::strip(vParts[2], '"');
}
}
setCGREG(originalCGREG);
return result;
}
void CellularRadio::setCGREG(std::string value) {
std::string sCmd("AT+CGREG=" + value);
std::string cmdResult(sendCommand(sCmd));
if (cmdResult.find("OK") == std::string::npos) {
printDebug("%s| AT+CGREG=%s returned unexpected response: [%s][%s]", m_sName.c_str(), value.c_str(), sCmd.c_str(), cmdResult.c_str());
}
}
std::string CellularRadio::queryCGREGstring() {
std::string sCmd("AT+CGREG?");
std::string cmdResult(sendCommand(sCmd));
if (cmdResult.find("+CGREG:") == std::string::npos) {
printDebug("%s| AT+CGREG? returned unexpected response: [%s][%s]", m_sName.c_str(), sCmd.c_str(), cmdResult.c_str());
return ICellularRadio::RSP_ERROR;
}
return cmdResult;
}
void CellularRadio::getCommonNetworkStats(Json::Value& jData) {
bool bRoaming = false;
if(getRoaming(bRoaming) == SUCCESS) {
jData[ICellularRadio::KEY_ROAMING] = bRoaming;
}
int32_t iRssi;
if(getSignalStrength(iRssi) == SUCCESS) {
jData[ICellularRadio::KEY_RSSI] = iRssi;
int32_t dBm;
if(!jData.isMember(ICellularRadio::KEY_RSSIDBM) && convertSignalStrengthTodBm(iRssi, dBm) == SUCCESS) {
//Add RSSI in dBm format
jData[ICellularRadio::KEY_RSSIDBM] = MTS::Text::format(dBm);
}
}
std::string sService;
if(getService(sService) == SUCCESS) {
jData[ICellularRadio::KEY_SERVICE] = sService;
}
std::string sDate, sTime, sTimeZone;
if(getTime(sDate, sTime, sTimeZone) == SUCCESS) {
jData[ICellularRadio::KEY_DATETIME] = sDate + " " + sTime + " GMT" + sTimeZone;
}
std::string sNetworkReg;
REGISTRATION eReg;
if (getRegistration(eReg) == SUCCESS) {
if (convertRegistrationToString(eReg, sNetworkReg) == SUCCESS) {
jData[ICellularRadio::KEY_NETWORK_REG] = sNetworkReg;
}
}
std::string sCurrentCellMode;
CELLULAR_MODES eModes;
if (getCellularMode(eModes) == SUCCESS) {
if (convertCellModesToString(eModes, sCurrentCellMode) == SUCCESS) {
jData[ICellularRadio::KEY_CELL_MODE] = sCurrentCellMode;
}
}
}
ICellularRadio::CODE CellularRadio::getSimLockStatus(std::string& sData)
{
printTrace("%s| Get SIM lock status", m_sName.c_str());
// SIM card may introduce a delay to AT+CPIN? execution. Setting timeout to 1s as set in PPP chat patches.
std::string sCmd("AT+CPIN?");
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 1000);
const std::string sPrefix = "+CPIN: ";
size_t start = sResult.find(sPrefix);
size_t end = sResult.rfind(ICellularRadio::RSP_OK);
if (start == std::string::npos || end == std::string::npos) {
printWarning("%s| Unable to get SIM lock status from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
start += sPrefix.size();
sData = MTS::Text::trim(sResult.substr(start, end-start));
if(sData.size() == 0) {
printWarning("%s| Unable to get SIM lock status from radio using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
return SUCCESS;
}
void CellularRadio::initMipProfile(Json::Value& jData) {
jData[ICellularRadio::KEY_MIP_ID] = 0;
jData[ICellularRadio::KEY_MIP_ENABLED] = false;
jData[ICellularRadio::KEY_MIP_NAI] = ICellularRadio::VALUE_UNKNOWN;
jData[ICellularRadio::KEY_MIP_HOMEADDRESS] = ICellularRadio::VALUE_UNKNOWN;
jData[ICellularRadio::KEY_MIP_PRIMARYHA] = ICellularRadio::VALUE_UNKNOWN;
jData[ICellularRadio::KEY_MIP_SECONDARYHA] = ICellularRadio::VALUE_UNKNOWN;
jData[ICellularRadio::KEY_MIP_MNAAASPI] = ICellularRadio::VALUE_UNKNOWN;
jData[ICellularRadio::KEY_MIP_MNHASPI] = ICellularRadio::VALUE_UNKNOWN;
jData[ICellularRadio::KEY_MIP_MNAAASS] = false;
jData[ICellularRadio::KEY_MIP_MNHASS] = false;
}
const std::vector CellularRadio::getRegistrationCommands() {
std::string sType;
convertModelToType(getName(), sType);
if (sType == VALUE_TYPE_LTE) {
return { "CREG", "CGREG", "CEREG" };
} else {
return { "CREG", "CGREG" };
}
}
ICellularRadio::REGISTRATION CellularRadio::parseRegResponse(std::string sResult) {
size_t start = sResult.find(',');
size_t stop = sResult.find(' ', start);
std::string sRegStat = sResult.substr(start + 1, stop - start - 1);
int32_t value;
sscanf(sRegStat.c_str(), "%d", &value);
return (ICellularRadio::REGISTRATION)value;
}
ICellularRadio::CODE CellularRadio::getRegistration(REGISTRATION& eRegistration, const std::string& sType) {
std::string sCmd = "AT+" + sType + "?";
std::string sResp = "+" + sType + ": ";
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 5000);
if (sResult.find(sResp) == std::string::npos) {
if(sResult.size() == 0) {
printDebug("%s| Registration command returned no response", m_sName.c_str());
return NO_RESPONSE;
}
printDebug("%s| Registration command returned unexpected response: [%s]", m_sName.c_str(), sResult.c_str());
return FAILURE;
}
eRegistration = parseRegResponse(sResult);
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::getRegistration(REGISTRATION& eRegistration) {
/* REGISTRATION_PRIORITY:
* REGISTERED = 0
* ROAMING = 1
* DENIED = 2
* SEARCHING = 3
* NOT_REGISTERED = 4
* UNKNOWN = 5
*/
uint8_t uRegPriority[] = { 4, 0, 3, 2, 5, 1 };
ICellularRadio::CODE ret = ERROR;
REGISTRATION eReg = UNKNOWN;
eRegistration = UNKNOWN;
// We need to check CREG, CGREG, and CEREG for possible success.
// Depending on the radio, carrier, roaming, sim card, cellular mode and some other factors the registration
// will come back differently depending on the type of connection that is possible.
const auto & commands = getRegistrationCommands();
for (const auto & cmd : commands) {
ret = getRegistration(eReg, cmd);
if (ret != SUCCESS) {
break;
}
if (eReg == REGISTERED || eReg == ROAMING) {
eRegistration = eReg;
break;
}
eRegistration = (uRegPriority[eRegistration] > uRegPriority[eReg]) ? eReg : eRegistration;
}
return ret;
}
ICellularRadio::CODE CellularRadio::getCellularMode(CELLULAR_MODES &networks) {
networks = CELLULAR_MODE_NA;
std::string cmdResult = sendCommand("AT+COPS?");
if (cmdResult.find(ICellularRadio::RSP_OK) == std::string::npos) {
printError("%s| AT+COPS returned unexpected response: AT+COPS? [%s]", getName().c_str(), cmdResult.c_str());
return FAILURE;
}
size_t cursor = 0;
const std::vector &reply = MTS::Text::split(MTS::Text::getLine(MTS::Text::trim(cmdResult), cursor, cursor), ',');
uint8_t op;
if (reply.size() < 4 || !MTS::Text::parse(op, reply[3])) {
printError("%s| AT+COPS Error parsing reply [AT+COPS?][%s]", getName().c_str(), cmdResult.c_str());
return FAILURE;
}
switch (op) {
case 0: // GSM
case 1: // GSM Compact
case 3: // GSM w/EGPRS
networks = CELLULAR_MODE_2G;
break;
case 2: // UTRAN
case 4: // UTRAN w/HSDPA
case 5: // UTRAN w/HSUPA
case 6: // UTRAN w/HSDPA and HSUPA
networks = CELLULAR_MODE_3G;
break;
case 7: // E-UTRAN, LTE
case 8: // CAT M1, EC-GSM-IoT (A/Gb mode), LTE
case 9: // NB IoT, E-UTRAN (NB-S1 mode), LTE
networks = CELLULAR_MODE_4G;
break;
default:
printError("%s| AT+COPS unknown Radio Access Technology [AT+COPS?][%s]", getName().c_str(), cmdResult.c_str());
return FAILURE;
}
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::convertRegistrationToString(REGISTRATION eRegistration, std::string& sRegistration) {
ICellularRadio::CODE eCode = FAILURE;
switch (eRegistration) {
case NOT_REGISTERED: sRegistration = ICellularRadio::VALUE_NOT_REGISTERED; eCode = SUCCESS; break;
case REGISTERED: sRegistration = ICellularRadio::VALUE_REGISTERED; eCode = SUCCESS; break;
case SEARCHING: sRegistration = ICellularRadio::VALUE_SEARCHING; eCode = SUCCESS; break;
case DENIED: sRegistration = ICellularRadio::VALUE_DENIED; eCode = SUCCESS; break;
case UNKNOWN: sRegistration = ICellularRadio::VALUE_UNKNOWN; eCode = SUCCESS; break;
case ROAMING: sRegistration = ICellularRadio::VALUE_ROAMING; eCode = SUCCESS; break;
}
return eCode;
}
ICellularRadio::CODE CellularRadio::convertCellModesToString(ICellularRadio::CELLULAR_MODES eCellModes, std::string& sCellModes) {
std::string sResult;
if (eCellModes & CELLULAR_MODE_2G) {
sResult += "2g,";
}
if (eCellModes & CELLULAR_MODE_3G) {
sResult += "3g,";
}
if (eCellModes & CELLULAR_MODE_4G) {
sResult += "4g,";
}
if (eCellModes & CELLULAR_MODE_5G) {
sResult += "5g,";
}
if (!sResult.empty()) {
sResult.pop_back(); // remove trailing comma
}
sCellModes = sResult;
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::unlockSimCard(const Json::Value& jArgs) {
printTrace("%s| Unlock the SIM card using PIN code", m_sName.c_str());
if(!jArgs["pin"].isString()) {
return INVALID_ARGS;
}
std::string sCmd = "AT+CPIN=" + jArgs["pin"].asString();
std::string sResult = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, 5000);
size_t pos = sResult.find(ICellularRadio::RSP_OK);
if (pos == std::string::npos) {
printWarning("%s| Failed to unlock the SIM card using command [%s]", m_sName.c_str(), sCmd.c_str());
return FAILURE;
}
return SUCCESS;
}
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;
}
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());
return rc;
}
printTrace("%s| Detected carrier code: [%s]", m_sName.c_str(), sCarrierCode.c_str());
return rc;
}
ICellularRadio::CODE CellularRadio::getSimCarrierCode(const std::string& sIccid, std::string& sCarrierCode) {
const char* ICCID_PREFIX_VZW = "891480";
const char* ICCID_PREFIX_ATT = "8901410";
if (sIccid.find(ICCID_PREFIX_VZW) == 0) {
printTrace("%s| Verizon SIM detected", m_sName.c_str());
sCarrierCode = VALUE_CARRIER_CODE_VERIZON;
} else if (sIccid.find(ICCID_PREFIX_ATT) == 0) {
printTrace("%s| AT&T SIM detected", m_sName.c_str());
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());
sCarrierCode = VALUE_UNKNOWN;
}
return SUCCESS; // no error cases for now
}
ICellularRadio::CODE CellularRadio::validateMsl(const Json::Value&) {
printTrace("%s| Validate MSL", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMsid(const Json::Value&) {
printTrace("%s| Set MSID", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::getMipProfile(Json::Value&) {
printTrace("%s| Get MIP Active Profile", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMipActiveProfile(const Json::Value&) {
printTrace("%s| Set MIP Active Profile", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMipNai(const Json::Value&) {
printTrace("%s| Set MIP NAI", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMipHomeIp(const Json::Value&) {
printTrace("%s| Set MIP Home IP", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMipPrimaryHa(const Json::Value&) {
printTrace("%s| Set MIP Primary HA", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMipSecondaryHa(const Json::Value&) {
printTrace("%s| Set MIP Secondary HA", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMipMnAaaSpi(const Json::Value&) {
printTrace("%s| Set MIP MN-AAA SPI", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMipMnHaSpi(const Json::Value&) {
printTrace("%s| Set MIP MN-HA SPI", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMipRevTun(const Json::Value&) {
printTrace("%s| Set MIP Rev Tun", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMipMnAaaSs(const Json::Value&) {
printTrace("%s| Set MIP MN-AAA SS", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setMipMnHaSs(const Json::Value&) {
printTrace("%s| Set MIP MN-HA SS", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::updateDc(const Json::Value&, UpdateCb&) {
printTrace("%s| Update Device Configuration", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::updatePrl(const Json::Value&, UpdateCb&) {
printTrace("%s| Update Preferred Roaming List", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::updateFumo(const Json::Value&, UpdateCb&) {
printTrace("%s| Update Firmware Update Management Object", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::updateFumoLocal(int, ICellularRadio::UpdateCb&) {
printTrace("%s| Update Local Firmware Update Management Object", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::fumoLocalInject(int, ICellularRadio::UpdateCb&) {
printTrace("%s| Inject Delta Firmware Image File: not applicable", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::fumoLocalApply(ICellularRadio::UpdateCb&) {
printTrace("%s| Apply Delta Firmware Image File: not applicable", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::fumoLocalCleanup() {
printTrace("%s| Cleanup Delta Firmware Image File: not applicable", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::resetHfa(const Json::Value&, UpdateCb&) {
printTrace("%s| HFA Reset", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::activate(const Json::Value&, UpdateCb&) {
printTrace("%s| Activation", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::startOmaDm(ICellularRadio::UpdateCb&) {
printTrace("%s| Start OMA DM procedure: not applicable", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::setActiveFirmware(const Json::Value&) {
printTrace("%s| Set Active Firmware Image Number: not applicable", m_sName.c_str());
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::getActiveFirmware(std::string& sFwId) {
printTrace("%s| Get Active Firmware Image Number: not applicable", m_sName.c_str());
sFwId = ICellularRadio::VALUE_NOT_SUPPORTED;
return NOT_APPLICABLE;
}
ICellularRadio::CODE CellularRadio::sendBasicCommand(const std::string& sCmd, int32_t iTimeoutMillis, const char& ESC) {
std::string response = sendCommand(sCmd, DEFAULT_BAIL_STRINGS, iTimeoutMillis, ESC);
if (response.size() == 0) {
return NO_RESPONSE;
} else if (response.find(ICellularRadio::RSP_OK) != std::string::npos) {
return SUCCESS;
} else if (response.find(ICellularRadio::RSP_ERROR) != std::string::npos) {
return ERROR;
} else {
return FAILURE;
}
}
std::string CellularRadio::sendCommand(const std::string& sCmd, const std::vector& vBail, int32_t timeoutMillis, const char& ESC) {
return ICellularRadio::sendCommand(m_apIo, sCmd, vBail, timeoutMillis, ESC);
}
std::string CellularRadio::sendCommand(const std::string& sCmd, MTS::IO::CellularRadio::IsNeedMoreData& isNeedMoreData, int32_t timeoutMillis, const char& ESC) {
return ICellularRadio::sendCommand(m_apIo, sCmd, isNeedMoreData, timeoutMillis, ESC);
}
std::string CellularRadio::waitResponse(const std::vector& vBail, int32_t timeoutMillis) {
return ICellularRadio::waitResponse(m_apIo, vBail, timeoutMillis);
}
std::string CellularRadio::waitResponse(ICellularRadio::IsNeedMoreData& isNeedMoreData, int32_t timeoutMillis) {
return ICellularRadio::waitResponse(m_apIo, isNeedMoreData, timeoutMillis);
}
ICellularRadio::CODE CellularRadio::sendData(const char* pData, size_t nBytes) {
if(m_apIo.isNull()) {
printError("RADIO| IO is not set in sendData");
return ERROR;
}
// This limit comes from the Connection::write implementation. Otherwise we can get overflows.
const size_t maxInt32 = INT32_MAX; // iSize parameter type in Connection::write
const size_t maxUInt32 = UINT32_MAX; // return value type in Connection::write
const size_t nSizeLimit = std::min(maxInt32, maxUInt32);
if (nBytes > nSizeLimit) {
printError("RADIO| Chunks larger than %d bytes are not supported", nSizeLimit);
return INVALID_ARGS;
}
// Now we can ignore conversion and overflow warnings emitted by compiler.
int32_t iResult;
iResult = m_apIo->write(pData, nBytes);
if(iResult != static_cast(nBytes)) {
printError("RADIO| Failed to send data to radio");
return ERROR;
}
return SUCCESS;
}
bool CellularRadio::splitAndAssign(const std::string& sLine, const std::string& sKey, Json::Value& jParent, const std::string& sJsonKey, Json::ValueType eType) {
std::vector vParts = MTS::Text::split(sLine, ":", 2);
if(vParts.size() == 2 && vParts[0] == sKey) {
if(eType == Json::ValueType::stringValue) {
jParent[sJsonKey] = MTS::Text::trim(vParts[1]);
} else if (eType == Json::ValueType::intValue || eType == Json::ValueType::uintValue) {
//TODO:
printWarning("%s| Unable to parse requested type from line [%s]", getName().c_str(), sKey.c_str(), sLine.c_str());
return false;
} else if(eType == Json::ValueType::realValue) {
//TODO:
printWarning("%s| Unable to parse requested type from line [%s]", getName().c_str(), sKey.c_str(), sLine.c_str());
return false;
} else if(eType == Json::ValueType::booleanValue) {
//TODO:
printWarning("%s| Unable to parse requested type from line [%s]", getName().c_str(), sKey.c_str(), sLine.c_str());
return false;
} else {
printWarning("%s| Unable to parse requested type from line [%s]", getName().c_str(), sKey.c_str(), sLine.c_str());
return false;
}
} else {
printWarning("%s| Unable to parse %s from line [%s]", getName().c_str(), sKey.c_str(), sLine.c_str());
return false;
}
return true;
}
bool CellularRadio::getCarrierFromFirmware(const std::string& sFirmware, std::string& sCarrier) {
// Assuming that this function is not supported by the modem until overriden.
return false;
}
bool CellularRadio::getHardwareVersionFromFirmware(const std::string& sFirmware, std::string& sHardware) {
// Assuming that this function is not supported by the modem until overriden.
return false;
}
const char *CellularRadio::RadioBandMap::getLTEBand(const int32_t channel)
{
for (unsigned int ii = 0; ii < NUM_LTE_BANDS; ii++)
{
if (EULTRAband[ii].low <= channel && EULTRAband[ii].high >= channel)
{
return EULTRAband[ii].name;
}
}
return ICellularRadio::VALUE_UNKNOWN;
}
const char *CellularRadio::RadioBandMap::getCDMABand(const int channel)
{
for (unsigned int ii = 0; ii < NUM_WCDMA_BANDS; ii++)
{
if (WCDMAband[ii].low <= channel && WCDMAband[ii].high >= channel)
{
return WCDMAband[ii].name;
}
}
return ICellularRadio::VALUE_UNKNOWN;
}
const char *CellularRadio::RadioBandMap::getGSMBand(const int channel)
{
for (unsigned int ii = 0; ii < NUM_GSM_BANDS; ii++)
{
if (GSMband[ii].low <= channel && GSMband[ii].high >= channel)
{
return GSMband[ii].name;
}
}
return ICellularRadio::VALUE_UNKNOWN;
}
const char *CellularRadio::RadioBandMap::getRadioBandName()
{
const char *band = ICellularRadio::VALUE_UNKNOWN;
if (m_sRadioType == ICellularRadio::VALUE_TYPE_LTE)
{
band = getLTEBand(m_iChannel);
}
else if (m_sRadioType == ICellularRadio::VALUE_TYPE_CDMA)
{
band = getCDMABand(m_iChannel);
}
else if (m_sRadioType == ICellularRadio::VALUE_TYPE_GSM)
{
band = getGSMBand(m_iChannel);
}
return band;
}
const char *CellularRadio::RadioBandMap::getRadioBandName(const std::string &channel, const std::string &radioType)
{
const char *band = ICellularRadio::VALUE_UNKNOWN;
int32_t chan = strtol(channel.c_str(), NULL, 10);
if (radioType == ICellularRadio::VALUE_TYPE_LTE)
{
band = getLTEBand(chan);
}
else if (radioType == ICellularRadio::VALUE_TYPE_CDMA)
{
band = getCDMABand(chan);
}
else if (radioType == ICellularRadio::VALUE_TYPE_GSM)
{
band = getGSMBand(chan);
}
return band;
}
ICellularRadio::CODE CellularRadio::getFileSize(int fd, size_t& nBytes) {
CODE rc = FAILURE;
do {
struct stat fileStatus;
// On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
if (fstat(fd, &fileStatus) < 0) {
printError("Failed to determine file size: %d", errno);
break;
}
if (fileStatus.st_size < 0) {
printError("Failed to determine file size, file size is negative: %d", fileStatus.st_size);
break;
}
nBytes = static_cast(fileStatus.st_size);
rc = SUCCESS;
} while (false);
lseek(fd, 0, SEEK_SET);
return rc;
}
ICellularRadio::CODE CellularRadio::sizeToChunks(const size_t nBytes, const size_t chunkSize, size_t& nChunks) {
nChunks = (nBytes + chunkSize - 1) / chunkSize;
return SUCCESS;
}
ICellularRadio::CODE CellularRadio::readChunk(int fd, char* pChunk, size_t dChunkSize, size_t& nReadBytes) {
size_t nUsedBuffer = 0;
CODE rc = FAILURE;
while (true) {
if (nUsedBuffer > dChunkSize) {
printError("Internal pointer error, abort upload: %d", nUsedBuffer);
rc = ERROR;
break;
}
if (nUsedBuffer == dChunkSize) {
// full chunk received
rc = SUCCESS;
nReadBytes = dChunkSize;
break;
}
char* pData = pChunk + nUsedBuffer;
size_t nFreeBuffer = dChunkSize - nUsedBuffer;
ssize_t dReadCount = read(fd, pData, nFreeBuffer);
if (dReadCount < 0) {
printError("Failed to read from the source file: %d", errno);
rc = ERROR;
break;
}
size_t duReadCount = static_cast(dReadCount);
if (duReadCount == 0) {
// EOF. Return what was already read
nReadBytes = nUsedBuffer;
rc = SUCCESS;
break;
}
nUsedBuffer += duReadCount;
}
return rc;
}