summaryrefslogtreecommitdiff
path: root/src/MTS_IO_ME910C1WWRadio.cpp
diff options
context:
space:
mode:
authorAndrii Pientsov <andrii.pientsov@globallogic.com>2020-02-17 17:05:22 +0200
committerAndrii Pientsov <andrii.pientsov@globallogic.com>2020-02-17 17:05:22 +0200
commita19364df47f14bff07f8f1ade616fe147f910838 (patch)
treed5d27a332d6ddf68a694260876a6ba4b5ad40337 /src/MTS_IO_ME910C1WWRadio.cpp
parentf3425f9f5b32060895b7cd33d337b7ac50f9e714 (diff)
downloadlibmts-io-a19364df47f14bff07f8f1ade616fe147f910838.tar.gz
libmts-io-a19364df47f14bff07f8f1ade616fe147f910838.tar.bz2
libmts-io-a19364df47f14bff07f8f1ade616fe147f910838.zip
MTX-3211 Remove the code that is used to support ME910C1-NV and ME910C1-NA
Diffstat (limited to 'src/MTS_IO_ME910C1WWRadio.cpp')
-rw-r--r--src/MTS_IO_ME910C1WWRadio.cpp593
1 files changed, 592 insertions, 1 deletions
diff --git a/src/MTS_IO_ME910C1WWRadio.cpp b/src/MTS_IO_ME910C1WWRadio.cpp
index 0469d5d..32c64b3 100644
--- a/src/MTS_IO_ME910C1WWRadio.cpp
+++ b/src/MTS_IO_ME910C1WWRadio.cpp
@@ -17,15 +17,27 @@
* along with libmts-io. If not, see <http://www.gnu.org/licenses/>.
*
*/
-
+#include <fstream>
#include <mts/MTS_Text.h>
#include <mts/MTS_Logger.h>
+#include <mts/MTS_Thread.h>
+#include <mts/MTS_Timer.h>
#include <mts/MTS_IO_ME910C1WWRadio.h>
using namespace MTS::IO;
const std::string ME910C1WWRadio::MODEL_NAME("ME910C1-WW");
+const std::string ME910C1WWRadio::KEY_FUMO_PDPID("pdpid"); // optional (default : "3")
+const std::string ME910C1WWRadio::KEY_FUMO_PDPTYPE("pdptype"); // optional (default : "IPV4V6")
+const std::string ME910C1WWRadio::KEY_FUMO_APN("apn"); // optional (default : "")
+const std::string ME910C1WWRadio::KEY_FUMO_ADDRESS("address");
+const std::string ME910C1WWRadio::KEY_FUMO_DIR("dir");
+const std::string ME910C1WWRadio::KEY_FUMO_FILE("file");
+const std::string ME910C1WWRadio::KEY_FUMO_USER("user");
+const std::string ME910C1WWRadio::KEY_FUMO_PASSWORD("password");
+const std::string ME910C1WWRadio::KEY_FUMO_DRYRUN("dryrun");
+
ME910C1WWRadio::ME910C1WWRadio(const std::string& sPort)
: ME910Radio(MODEL_NAME, sPort)
{
@@ -119,3 +131,582 @@ ICellularRadio::CODE ME910C1WWRadio::getActiveFirmware(std::string& sFwId) {
return SUCCESS;
}
+ICellularRadio::CODE ME910C1WWRadio::doGetFirmwareNumbers(std::string &sFirmware, std::string &sFirmwareBuild) {
+ ICellularRadio::CODE rc = FAILURE;
+
+ rc = getFirmware(sFirmware);
+ if (rc != SUCCESS){
+ return rc;
+ }
+
+ rc = getFirmwareBuild(sFirmwareBuild);
+ if (rc != SUCCESS){
+ return rc;
+ }
+
+ return rc;
+}
+
+ICellularRadio::CODE ME910C1WWRadio::doFumoReadConfig(const Json::Value& jArgs, Json::Value &jConfig) {
+ ICellularRadio::CODE rc = INVALID_ARGS;
+ std::string sPath;
+
+ do
+ {
+ if (!jArgs["config-file"].isString()) {
+ rc = INVALID_ARGS;
+ break;
+ }
+
+ sPath = jArgs["config-file"].asString();
+
+ std::ifstream file(sPath.c_str());
+ if (!file.is_open()) {
+ printError("Failed to open file [%s]", sPath.c_str());
+ break;
+ }
+
+ file.seekg(0, std::ios::end);
+ size_t size = file.tellg();
+ std::string buffer(size, ' ');
+ file.seekg(0);
+ file.read(&buffer[0], size);
+ file.close();
+
+ Json::Features features = Json::Features::strictMode();
+ Json::Reader reader(features);
+ if (!reader.parse(buffer, jConfig)) {
+ printError("Error parsing FOTA configuration file");
+ break;
+ }
+
+ //
+ // set default values if missing
+ //
+ if (!jConfig.isMember(KEY_FUMO_PDPID)) {
+ jConfig[KEY_FUMO_PDPID] = std::string("3");
+ }
+
+ if (!jConfig.isMember(KEY_FUMO_PDPTYPE)) {
+ jConfig[KEY_FUMO_PDPTYPE] = std::string("IPV4V6");
+ }
+
+ if (!jConfig.isMember(KEY_FUMO_APN)) {
+ jConfig[KEY_FUMO_APN] = std::string("");
+ }
+
+ //
+ // validate
+ //
+ if (!jConfig[KEY_FUMO_PDPID].isString()) {
+ printError("Error loading FOTA configuration: PDP context id is not set");
+ break;
+ }
+
+ if (jConfig[KEY_FUMO_PDPID].asString().empty()) {
+ printError("Error loading FOTA configuration: context id is empty");
+ break;
+ }
+
+ if (!jConfig[KEY_FUMO_PDPTYPE].isString()) {
+ printError("Error loading FOTA configuration: PDP type is not set");
+ break;
+ }
+
+ if (jConfig[KEY_FUMO_PDPTYPE].asString().empty()) {
+ printError("Error loading FOTA configuration: PDP type is empty");
+ break;
+ }
+
+ // Note : allow empty APN
+ if (!jConfig[KEY_FUMO_APN].isString()) {
+ printError("Error loading FOTA configuration: APN is not set");
+ break;
+ }
+
+ if (!jConfig[KEY_FUMO_ADDRESS].isString()) {
+ printError("Error loading FOTA configuration: address is not set");
+ break;
+ }
+
+ if (jConfig[KEY_FUMO_ADDRESS].asString().empty()) {
+ printError("Error loading FOTA configuration: address is empty");
+ break;
+ }
+
+ // Note: allow empty dir
+ if (!jConfig[KEY_FUMO_DIR].isString()) {
+ printError("Error loading FOTA configuration: directory is not set");
+ break;
+ }
+
+ if (!jConfig[KEY_FUMO_FILE].isString()) {
+ printError("Error loading FOTA configuration: filename is not set");
+ break;
+ }
+
+ if (jConfig[KEY_FUMO_FILE].asString().empty()) {
+ printError("Error loading FOTA configuration: filename is empty");
+ break;
+ }
+
+ // Note: allow empty username/password
+ if (!jConfig[KEY_FUMO_USER].isString()) {
+ printError("Error loading FOTA configuration: username is not set");
+ break;
+ }
+
+ if (!jConfig[KEY_FUMO_PASSWORD].isString()) {
+ printError("Error loading FOTA configuration: password is not set");
+ break;
+ }
+
+ rc = SUCCESS;
+ }
+ while(0);
+
+ return rc;
+}
+
+ICellularRadio::CODE ME910C1WWRadio::doFumoSetup(const Json::Value &jConfig, UpdateCb& stepCb) {
+ ICellularRadio::CODE rc = FAILURE;
+ std::string sCmd;
+
+ std::string sContextId = jConfig[KEY_FUMO_PDPID].asString();
+ std::string sApn = jConfig[KEY_FUMO_APN].asString();
+ std::string sPdpType = jConfig[KEY_FUMO_PDPTYPE].asString();
+
+
+ do
+ {
+ //
+ // Execution command is used to activate or deactivate either the GSM
+ // context or the specified PDP context.
+ //
+ // AT#SGACT=<cid>,<stat>[,<userId>,<pwd>]
+ //
+ sCmd = "AT#SGACT=" + sContextId + ",0";
+ rc = sendBasicCommand(sCmd);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: Failed to deactivate PDP context"));
+ }
+ break;
+ }
+
+ //
+ // Read current Firmware numbers (let it be after AT#SGACT not to confuse users)
+ //
+ rc = doGetFirmwareNumbers(m_sFw, m_sFwBuild);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: Failed to obtain current firmware version"));
+ }
+ break;
+ }
+
+ //
+ // Set command specifies PDP context parameter values for a PDP context identified by
+ // the (local) context identification parameter <cid>.
+ //
+ // AT+CGDCONT= [<cid>[,<PDP_type>[,<APN>[,<PDP_addr>[,<d_comp>[,<h_comp>[,<pd1>[,...[,pdN]]]]]]]]]
+ //
+ sCmd = "AT+CGDCONT=" + sContextId + ",\"" + sPdpType + "\"";
+ if (!sApn.empty()) {
+ sCmd += ",\"" + sApn + "\"";
+ }
+ rc = sendBasicCommand(sCmd, 1000);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: Failed to setup PDP context"));
+ }
+ break;
+ }
+
+ //
+ // Set command sets the socket configuration parameters.
+ //
+ // AT#SCFG==<connId>,<cid>,<pktSz>,<maxTo>,<connTo>,<txTo>
+ // <connId> - socket connection identifier
+ // <cid> - PDP context identifier
+ // <pktSz> - packet size to be used by the TCP/UDP/IP stack for data sending.
+ // <maxTo> - exchange timeout (or socket inactivity timeout); if there’s no
+ // data exchange within this timeout period the connection is closed (timeout value in seconds).
+ // <connTo> - connection timeout; if we can’t establish a connection to the
+ // remote within this timeout period, an error is raised timeout value in hundreds of milliseconds.
+ // <txTo> - data sending timeout; after this period data are sent also if they’re
+ // less than max packet size (timeout value in hundreds of milliseconds).
+ //
+ sCmd = "AT#SCFG=1," + sContextId + ",300,90,600,50";
+ rc = sendBasicCommand(sCmd);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: Failed to set connection configuration parameters"));
+ }
+ break;
+ }
+
+ //
+ // Activate PDP context
+ //
+ sCmd = "AT#SGACT=" + sContextId + ",1";
+ rc = sendBasicCommand(sCmd, 60 * 1000);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: Failed to activate PDP context"));
+ }
+ break;
+ }
+ }
+ while (0);
+
+ return rc;
+}
+
+ICellularRadio::CODE ME910C1WWRadio::doFumoFtp(const Json::Value &jConfig, UpdateCb& stepCb) {
+ ICellularRadio::CODE rc = FAILURE;
+ std::string sCmd;
+ std::string sResult;
+
+ //
+ // Set command sets the time-out used when opening either the FTP control
+ // channel or the FTP traffic channel.
+ //
+ // AT#FTPTO= [<tout>]
+ // <tout> - time-out in 100 ms units
+ //
+ rc = sendBasicCommand("AT#FTPTO=2400");
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: Failed to setup connection timeout"));
+ }
+ return rc;
+ }
+
+ //
+ // Execution command opens an FTP connection toward the FTP server.
+ //
+ // AT#FTPOPEN=[<server:port>,<username>,<password>[,<mode>]]
+ //
+ sCmd = "AT#FTPOPEN=";
+ sCmd += "\"" + jConfig[KEY_FUMO_ADDRESS].asString() + "\",";
+ sCmd += "\"" + jConfig[KEY_FUMO_USER].asString() + "\",";
+ sCmd += "\"" + jConfig[KEY_FUMO_PASSWORD].asString() + "\",1";
+ rc = sendBasicCommand(sCmd, 60 * 1000);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: Failed to open connection"));
+ }
+ return rc;
+ }
+
+ if (stepCb) {
+ stepCb(Json::Value("FUMO Info: connection opened"));
+ }
+
+ do
+ {
+ //
+ // Set command, issued during an FTP connection, sets the file transfer type.
+ //
+ // AT#FTPTYPE=[<type>]
+ // <type> - file transfer type:
+ // 0 - binary
+ // 1 - ascii
+ //
+ rc = sendBasicCommand("AT#FTPTYPE=0", 1000);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: failed to set file transfer type"));
+ }
+ break;
+ }
+
+ //
+ // Execution command, issued during an FTP connection, changes the
+ // working directory on FTP server.
+ //
+ // AT#FTPCWD=[<dirname>]
+ //
+ sCmd = "AT#FTPCWD=\"/";
+ if (!jConfig[KEY_FUMO_DIR].asString().empty()) {
+ sCmd += jConfig[KEY_FUMO_DIR].asString() + "/";
+ }
+ sCmd += "\"";
+ rc = sendBasicCommand(sCmd, 60 * 1000);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: failed to change working directory on the server"));
+ }
+ break;
+ }
+
+ if (stepCb) {
+ stepCb(Json::Value("FUMO Info: downloading the firmware"));
+ }
+
+ //
+ // Start FTP transfer
+ //
+ sCmd = "AT#FTPGETOTA=";
+ sCmd += "\"" + jConfig[KEY_FUMO_FILE].asString() + "\",1,1";
+ sendBasicCommand(sCmd);
+
+ //
+ // Noticed that after successful AT#FTPGETOTA the radio resets the connection.
+ // and the response code (OK, ERROR.. ) not always reach the host. Therefore
+ // we send the AT#FTPGETOTA with relatively small timeout and then poll with AT
+ // until we get valid response. After that, using AT#FTPMSG we can check the
+ // result of the last FTP command (which is AT#FTPGETOTA in our case).
+ MTS::Timer oTimer;
+
+ oTimer.start();
+
+ while (oTimer.getSeconds() < (30 * 60)) // 30 min
+ {
+ MTS::Thread::sleep(5000);
+
+ rc = sendBasicCommand("AT");
+ if (rc == SUCCESS) {
+ break;
+ }
+
+ resetConnection(1);
+ }
+ oTimer.stop();
+
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: unable to obtain radio after reset"));
+ }
+ break;
+ }
+
+ //
+ // Now check the FTP status
+ //
+ std::string sResult = sendCommand("AT#FTPMSG");
+
+ printTrace("RADIO| AT#FTPMSG result [%s]", sResult.c_str());
+
+ if (sResult.find(ICellularRadio::RSP_OK) == std::string::npos) {
+ rc = FAILURE;
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: failed to download the firmware file"));
+ }
+ break;
+ }
+ if (sResult.find("#FTPMSG: 550") != std::string::npos) {
+ // FTP(550) : File not found
+ rc = FAILURE;
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: file not found"));
+ }
+ break;
+ }
+ if (sResult.find("#FTPMSG: 226") == std::string::npos) {
+ // FTP(226) : Successfully transferred
+ rc = FAILURE;
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: failed to download the firmware file"));
+ }
+ break;
+ }
+ }
+ while (0);
+
+ //
+ // Execution command closes an FTP connection.
+ //
+ // AT#FTPCLOSE
+ //
+ ICellularRadio::CODE rcclose = sendBasicCommand("AT#FTPCLOSE", 60 * 1000);
+ if (rcclose != SUCCESS && rc == SUCCESS) {
+ if(stepCb) {
+ // Only one "FUMO Error" message should be sent
+ stepCb(Json::Value("FUMO Error: Failed to close FTP connection"));
+ }
+ rc = rcclose;
+ }
+
+ if (rc == SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Info: firmware downloaded successfully"));
+ }
+ }
+
+ return rc;
+}
+
+ICellularRadio::CODE ME910C1WWRadio::doFumoCleanup(const Json::Value &jConfig, UpdateCb& stepCb) {
+ ICellularRadio::CODE rc = FAILURE;
+ std::string sCmd;
+
+ std::string sContextId = jConfig[KEY_FUMO_PDPID].asString();
+
+ //
+ // Deactivate PDP context
+ //
+ sCmd = "AT#SGACT=" + sContextId + ",0";
+ rc = sendBasicCommand(sCmd, 10000);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: Failed to deactivate PDP context"));
+ }
+ }
+ return rc;
+}
+
+ICellularRadio::CODE ME910C1WWRadio::doFumoApplyFirmware(const Json::Value &jConfig, UpdateCb& stepCb) {
+ ICellularRadio::CODE rc = FAILURE;
+
+ if (jConfig.isMember(KEY_FUMO_DRYRUN)) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Info: applying the radio firmware"));
+ }
+ return SUCCESS;
+ }
+
+ rc = sendBasicCommand("AT#OTAUP=0", 10000);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: failed to apply the firmware"));
+ }
+ return rc;
+ }
+
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Info: applying the radio firmware"));
+ }
+
+ return rc;
+}
+
+ICellularRadio::CODE ME910C1WWRadio::doFumoWaitNewFirmware(const Json::Value &jConfig, UpdateCb& stepCb) {
+ std::string sFirmware;
+ std::string sFirmwareBuild;
+ ICellularRadio::CODE rc = FAILURE;
+
+ if (jConfig.isMember(KEY_FUMO_DRYRUN)) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO done: radio firmware applied successfully"));
+ }
+ return SUCCESS;
+ }
+
+ // The radio is expected to send "#OTAEV: Module Upgraded To New Fw" unsolicited message
+ // on success. However, for some reason, we do not see this message.
+
+ MTS::Timer oTimer;
+ oTimer.start();
+
+ while (oTimer.getSeconds() < (5 * 60)) { // 5 minutes
+
+ MTS::Thread::sleep(10000);
+
+ if (doGetFirmwareNumbers(sFirmware, sFirmwareBuild) != SUCCESS) {
+ // The radio is probably unavailable
+ resetConnection(100);
+ continue;
+ }
+
+ if (sFirmware == m_sFw && sFirmwareBuild == m_sFwBuild) {
+ // Have the same firmware. The radio resets several time
+ // before the firmware is actually get upgraded. So keep polling.
+ continue;
+ }
+
+ // The firmware numbers have changed
+ rc = SUCCESS;
+ break;
+ }
+ oTimer.stop();
+
+ if (rc == SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO done: radio firmware applied successfully"));
+ }
+ }
+ else {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO error: radio firmware has not been updated"));
+ }
+ }
+
+ return rc;
+}
+
+
+ICellularRadio::CODE ME910C1WWRadio::doFumoPerform(const Json::Value &jConfig, UpdateCb& stepCb) {
+ ICellularRadio::CODE rc = FAILURE;
+
+ UpdateCb dummyCb;
+
+ // Set the PDP context for the FOTA
+ rc = doFumoSetup(jConfig, stepCb);
+ if (rc != SUCCESS) {
+ return rc;
+ }
+
+ // Download FW over FTP
+ rc = doFumoFtp(jConfig, stepCb);
+ if (rc != SUCCESS) {
+ doFumoCleanup(jConfig, dummyCb);
+ return rc;
+ }
+
+ // Clean up before applying the FW file
+ rc = doFumoCleanup(jConfig, stepCb);
+ if (rc != SUCCESS) {
+ return rc;
+ }
+
+ // Apply the FW file
+ rc = doFumoApplyFirmware(jConfig, stepCb);
+ if (rc != SUCCESS) {
+ return rc;
+ }
+
+ rc = doFumoWaitNewFirmware(jConfig, stepCb);
+
+ return rc;
+}
+
+ICellularRadio::CODE ME910C1WWRadio::updateFumo(const Json::Value& jArgs, UpdateCb& stepCb) {
+ Json::Value jConfig(Json::objectValue);
+ ICellularRadio::CODE rc = FAILURE;
+ std::string sFwId;
+
+ do
+ {
+ rc = getActiveFirmware(sFwId);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: failed to obtain current active firmware id"));
+ }
+ break;
+ }
+
+ // For Verizon Only
+ if (sFwId != "1") {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: fumo is not supported"));
+ }
+ break;
+ }
+
+ rc = doFumoReadConfig(jArgs, jConfig);
+ if (rc != SUCCESS) {
+ if(stepCb) {
+ stepCb(Json::Value("FUMO Error: bad configuration parameters"));
+ }
+ break;
+ }
+
+ rc = doFumoPerform(jConfig, stepCb);
+ }
+ while(0);
+
+ return rc;
+}
+