Untitled diff

Created Diff never expires
93 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
692 lines
101 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
690 lines
/*
/*
******************************************************************************
******************************************************************************
* Copyright (c) 2015 Particle Industries, Inc. All rights reserved.
* Copyright (c) 2015 Particle Industries, Inc. All rights reserved.
*
*
* This library is free software; you can redistribute it and/or
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
* version 3 of the License, or (at your option) any later version.
*
*
* This library is distributed in the hope that it will be useful,
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* Lesser General Public License for more details.
*
*
* You should have received a copy of the GNU Lesser General Public
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
******************************************************************************
******************************************************************************
*/
*/


#ifndef HAL_CELLULAR_EXCLUDE
#ifndef HAL_CELLULAR_EXCLUDE


/* Includes -----------------------------------------------------------------*/
/* Includes -----------------------------------------------------------------*/
#include <stdio.h>
#include <stdio.h>
#include <stdint.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdarg.h>
#include <string.h>
#include <string.h>


#include "mdm_hal.h"
#include "mdm_hal.h"
#include "timer_hal.h"
#include "timer_hal.h"
#include "delay_hal.h"
#include "delay_hal.h"
#include "pinmap_hal.h"
#include "pinmap_hal.h"
#include "pinmap_impl.h"
#include "pinmap_impl.h"
#include "gpio_hal.h"
#include "gpio_hal.h"
#include "mdmapn_hal.h"
#include "mdmapn_hal.h"
#include "stm32f2xx.h"
#include "stm32f2xx.h"
#include "dns_client.h"
#include "service_debug.h"
#include "service_debug.h"
#include "bytes2hexbuf.h"
#include "hex_to_bytes.h"
#include "concurrent_hal.h"
#include "concurrent_hal.h"
#include <mutex>
#include <mutex>
#include "net_hal.h"
#include "net_hal.h"
#include <limits>
#include <limits>


std::recursive_mutex mdm_mutex;
std::recursive_mutex mdm_mutex;


/* Private typedef ----------------------------------------------------------*/
/* Private typedef ----------------------------------------------------------*/


/* Private define -----------------------------------------------------------*/
/* Private define -----------------------------------------------------------*/
/* Private macro ------------------------------------------------------------*/
/* Private macro ------------------------------------------------------------*/


#define PROFILE "0" //!< this is the psd profile used
#define PROFILE "0" //!< this is the psd profile used
#define MAX_SIZE 1024 //!< max expected messages (used with RX)
#define MAX_SIZE 1024 //!< max expected messages (used with RX)
#define USO_MAX_WRITE 1024 //!< maximum number of bytes to write to socket (used with TX)
#define USO_MAX_WRITE 1024 //!< maximum number of bytes to write to socket (used with TX)

// ID of the PDP context used to configure the default EPS bearer when registering in an LTE network
// Note: There are no PDP contexts in LTE, SARA-R4 uses this naming for the sake of simplicity
#define PDP_CONTEXT 1

// Enable hex mode for socket commands. SARA-R410M-01B has a bug which causes truncation of
// data read from a socket if the data contains a null byte
// #define SOCKET_HEX_MODE

// Timeout for socket write operations
#define SOCKET_WRITE_TIMEOUT 30000
// Timeout for +COPS command
#define COPS_TIMEOUT (3 * 60 * 1000)

// num sockets
// num sockets
#define NUMSOCKETS ((int)(sizeof(_sockets)/sizeof(*_sockets)))
#define NUMSOCKETS ((int)(sizeof(_sockets)/sizeof(*_sockets)))
//! test if it is a socket is ok to use
//! test if it is a socket is ok to use
#define ISSOCKET(s) (((s) >= 0) && ((s) < NUMSOCKETS) && (_sockets[s].handle != MDM_SOCKET_ERROR))
#define ISSOCKET(s) (((s) >= 0) && ((s) < NUMSOCKETS) && (_sockets[s].handle != MDM_SOCKET_ERROR))
//! check for timeout
//! check for timeout
#define TIMEOUT(t, ms) ((ms != TIMEOUT_BLOCKING) && ((HAL_Timer_Get_Milli_Seconds() - t) > ms))
#define TIMEOUT(t, ms) ((ms != TIMEOUT_BLOCKING) && ((HAL_Timer_Get_Milli_Seconds() - t) > ms))
//! registration ok check helper
//! registration ok check helper
#define REG_OK(r) ((r == REG_HOME) || (r == REG_ROAMING))
#define REG_OK(r) ((r == REG_HOME) || (r == REG_ROAMING))
//! registration done check helper (no need to poll further)
//! registration done check helper (no need to poll further)
#define REG_DONE(r) ((r == REG_HOME) || (r == REG_ROAMING) || (r == REG_DENIED))
#define REG_DONE(r) ((r == REG_HOME) || (r == REG_ROAMING) || (r == REG_DENIED))
//! helper to make sure that lock unlock pair is always balanced
//! helper to make sure that lock unlock pair is always balanced
#define LOCK() std::lock_guard<std::recursive_mutex> __mdm_guard(mdm_mutex);
#define LOCK() std::lock_guard<std::recursive_mutex> __mdm_guard(mdm_mutex);
//! helper to make sure that lock unlock pair is always balanced
//! helper to make sure that lock unlock pair is always balanced
#define UNLOCK()
#define UNLOCK()


static volatile uint32_t gprs_timeout_start;
static volatile uint32_t gprs_timeout_start;
static volatile uint32_t gprs_timeout_duration;
static volatile uint32_t gprs_timeout_duration;


inline void ARM_GPRS_TIMEOUT(uint32_t dur) {
inline void ARM_GPRS_TIMEOUT(uint32_t dur) {
gprs_timeout_start = HAL_Timer_Get_Milli_Seconds();
gprs_timeout_start = HAL_Timer_Get_Milli_Seconds();
gprs_timeout_duration = dur;
gprs_timeout_duration = dur;
DEBUG("GPRS WD Set %d",(dur));
DEBUG("GPRS WD Set %d",(dur));
}
}
inline bool IS_GPRS_TIMEOUT() {
inline bool IS_GPRS_TIMEOUT() {
return gprs_timeout_duration && ((HAL_Timer_Get_Milli_Seconds()-gprs_timeout_start)>gprs_timeout_duration);
return gprs_timeout_duration && ((HAL_Timer_Get_Milli_Seconds()-gprs_timeout_start)>gprs_timeout_duration);
}
}


inline void CLR_GPRS_TIMEOUT() {
inline void CLR_GPRS_TIMEOUT() {
gprs_timeout_duration = 0;
gprs_timeout_duration = 0;
DEBUG("GPRS WD Cleared, was %d", gprs_timeout_duration);
DEBUG("GPRS WD Cleared, was %d", gprs_timeout_duration);
}
}


#ifdef MDM_DEBUG
#ifdef MDM_DEBUG
#if 0 // colored terminal output using ANSI escape sequences
#if 0 // colored terminal output using ANSI escape sequences
#define COL(c) "\033[" c
#define COL(c) "\033[" c
#else
#else
#define COL(c) ""
#define COL(c) ""
#endif
#endif
#define DEF COL("39m")
#define DEF COL("39m")
#define BLA COL("30m")
#define BLA COL("30m")
#define RED COL("31m")
#define RED COL("31m")
#define GRE COL("32m")
#define GRE COL("32m")
#define YEL COL("33m")
#define YEL COL("33m")
#define BLU COL("34m")
#define BLU COL("34m")
#define MAG COL("35m")
#define MAG COL("35m")
#define CYA COL("36m")
#define CYA COL("36m")
#define WHY COL("37m")
#define WHY COL("37m")


void dumpAtCmd(const char* buf, int len)
void dumpAtCmd(const char* buf, int len)
{
{
DEBUG_D(" %3d \"", len);
DEBUG_D(" %3d \"", len);
while (len --) {
while (len --) {
char ch = *buf++;
char ch = *buf++;
if ((ch > 0x1F) && (ch < 0x7F)) { // is printable
if ((ch > 0x1F) && (ch < 0x7F)) { // is printable
if (ch == '%') DEBUG_D("%%");
if (ch == '%') DEBUG_D("%%");
else if (ch == '"') DEBUG_D("\\\"");
else if (ch == '"') DEBUG_D("\\\"");
else if (ch == '\\') DEBUG_D("\\\\");
else if (ch == '\\') DEBUG_D("\\\\");
else DEBUG_D("%c", ch);
else DEBUG_D("%c", ch);
} else {
} else {
if (ch == '\a') DEBUG_D("\\a"); // BEL (0x07)
if (ch == '\a') DEBUG_D("\\a"); // BEL (0x07)
else if (ch == '\b') DEBUG_D("\\b"); // Backspace (0x08)
else if (ch == '\b') DEBUG_D("\\b"); // Backspace (0x08)
else if (ch == '\t') DEBUG_D("\\t"); // Horizontal Tab (0x09)
else if (ch == '\t') DEBUG_D("\\t"); // Horizontal Tab (0x09)
else if (ch == '\n') DEBUG_D("\\n"); // Linefeed (0x0A)
else if (ch == '\n') DEBUG_D("\\n"); // Linefeed (0x0A)
else if (ch == '\v') DEBUG_D("\\v"); // Vertical Tab (0x0B)
else if (ch == '\v') DEBUG_D("\\v"); // Vertical Tab (0x0B)
else if (ch == '\f') DEBUG_D("\\f"); // Formfeed (0x0C)
else if (ch == '\f') DEBUG_D("\\f"); // Formfeed (0x0C)
else if (ch == '\r') DEBUG_D("\\r"); // Carriage Return (0x0D)
else if (ch == '\r') DEBUG_D("\\r"); // Carriage Return (0x0D)
else DEBUG_D("\\x%02x", (unsigned char)ch);
else DEBUG_D("\\x%02x", (unsigned char)ch);
}
}
}
}
DEBUG_D("\"\r\n");
DEBUG_D("\"\r\n");
}
}


void MDMParser::_debugPrint(int level, const char* color, const char* format, ...)
void MDMParser::_debugPrint(int level, const char* color, const char* format, ...)
{
{
if (_debugLevel >= level)
if (_debugLevel >= level)
{
{
va_list args;
va_start (args, format);
if (color) DEBUG_D(color);
if (color) DEBUG_D(color);
DEBUG_D(format, args);
va_list args;
va_start(args, format);
log_printf_v(LOG_LEVEL_TRACE, LOG_THIS_CATEGORY(), nullptr, format, args);
va_end(args);
if (color) DEBUG_D(DEF);
if (color) DEBUG_D(DEF);
va_end (args);
DEBUG_D("\r\n");
DEBUG_D("\r\n");
}
}
}
}
// Warning: Do not use these for anything other than constant char messages,

// they will yield incorrect values for integers. Use DEBUG_D() instead.
#define MDM_ERROR(_fmt, ...) do {_debugPrint(0, RED, _fmt, ##__VA_ARGS__);}while(0)
#define MDM_ERROR(...) do {_debugPrint(0, RED, __VA_ARGS__);}while(0)
#define MDM_INFO(_fmt, ...) do {_debugPrint(1, GRE, _fmt, ##__VA_ARGS__);}while(0)
#define MDM_INFO(...) do {_debugPrint(1, GRE, __VA_ARGS__);}while(0)
#define MDM_TRACE(_fmt, ...) do {_debugPrint(2, DEF, _fmt, ##__VA_ARGS__);}while(0)
#define MDM_TRACE(...) do {_debugPrint(2, DEF, __VA_ARGS__);}while(0)
#define MDM_TEST(_fmt, ...) do {_debugPrint(3, CYA, _fmt, ##__VA_ARGS__);}while(0)
#define MDM_TEST(...) do {_debugPrint(3, CYA, __VA_ARGS__);}while(0)


#else
#else


#define MDM_ERROR(...) // no tracing
#define MDM_ERROR(...) // no tracing
#define MDM_TEST(...) // no tracing
#define MDM_TEST(...) // no tracing
#define MDM_INFO(...) // no tracing
#define MDM_INFO(...) // no tracing
#define MDM_TRACE(...) // no tracing
#define MDM_TRACE(...) // no tracing


#endif
#endif


/* Private variables --------------------------------------------------------*/
/* Private variables --------------------------------------------------------*/


MDMParser* MDMParser::inst;
MDMParser* MDMParser::inst;


/* Extern variables ---------------------------------------------------------*/
/* Extern variables ---------------------------------------------------------*/


/* Private function prototypes ----------------------------------------------*/
/* Private function prototypes ----------------------------------------------*/


MDMParser::MDMParser(void)
MDMParser::MDMParser(void)
{
{
inst = this;
inst = this;
memset(&_dev, 0, sizeof(_dev));
memset(&_dev, 0, sizeof(_dev));
memset(&_net, 0, sizeof(_net));
memset(&_net, 0, sizeof(_net));
_net.lac = 0xFFFF;
_net.lac = 0xFFFF;
_net.ci = 0xFFFFFFFF;
_net.ci = 0xFFFFFFFF;
_ip = NOIP;
_ip = NOIP;
_init = false;
_init = false;
_pwr = false;
_pwr = false;
_activated = false;
_activated = false;
_attached = false;
_attached = false;
_attached_urc = false; // updated by GPRS detached/attached URC,
_attached_urc = false; // updated by GPRS detached/attached URC,
// used to notify system of prolonged GPRS detach.
// used to notify system of prolonged GPRS detach.
_power_mode = 1; // default power mode is AT+UPSV=1
_power_mode = 1; // default power mode is AT+UPSV=1
_cancel_all_operations = false;
_cancel_all_operations = false;
sms_cb = NULL;
sms_cb = NULL;
memset(_sockets, 0, sizeof(_sockets));
memset(_sockets, 0, sizeof(_sockets));
for (int socket = 0; socket < NUMSOCKETS; socket ++)
for (int socket = 0; socket < NUMSOCKETS; socket ++)
_sockets[socket].handle = MDM_SOCKET_ERROR;
_sockets[socket].handle = MDM_SOCKET_ERROR;
#ifdef MDM_DEBUG
#ifdef MDM_DEBUG
_debugLevel = 3;
_debugLevel = 3;
_debugTime = HAL_Timer_Get_Milli_Seconds();
_debugTime = HAL_Timer_Get_Milli_Seconds();
#endif
#endif
}
}


void MDMParser::setPowerMode(int mode) {
void MDMParser::setPowerMode(int mode) {
_power_mode = mode;
_power_mode = mode;
}
}


void MDMParser::cancel(void) {
void MDMParser::cancel(void) {
MDM_INFO("\r\n[ Modem::cancel ] = = = = = = = = = = = = = = =");
MDM_INFO("\r\n[ Modem::cancel ] = = = = = = = = = = = = = = =");
_cancel_all_operations = true;
_cancel_all_operations = true;
}
}


void MDMParser::resume(void) {
void MDMParser::resume(void) {
MDM_INFO("\r\n[ Modem::resume ] = = = = = = = = = = = = = = =");
MDM_INFO("\r\n[ Modem::resume ] = = = = = = = = = = = = = = =");
_cancel_all_operations = false;
_cancel_all_operations = false;
}
}


void MDMParser::setSMSreceivedHandler(_CELLULAR_SMS_CB cb, void* data) {
void MDMParser::setSMSreceivedHandler(_CELLULAR_SMS_CB cb, void* data) {
sms_cb = cb;
sms_cb = cb;
sms_data = data;
sms_data = data;
}
}


void MDMParser::SMSreceived(int index) {
void MDMParser::SMSreceived(int index) {
sms_cb(sms_data, index); // call the SMS callback with the index of the new SMS
sms_cb(sms_data, index); // call the SMS callback with the index of the new SMS
}
}


int MDMParser::send(const char* buf, int len)
int MDMParser::send(const char* buf, int len)
{
{
#ifdef MDM_DEBUG
#ifdef MDM_DEBUG
if (_debugLevel >= 3) {
if (_debugLevel >= 3) {
DEBUG_D("%10.3f AT send ", (HAL_Timer_Get_Milli_Seconds()-_debugTime)*0.001);
DEBUG_D("%10.3f AT send ", (HAL_Timer_Get_Milli_Seconds()-_debugTime)*0.001);
dumpAtCmd(buf,len);
dumpAtCmd(buf,len);
}
}
#endif
#endif
return _send(buf, len);
return _send(buf, len);
}
}


int MDMParser::sendFormated(const char* format, ...) {
int MDMParser::sendFormated(const char* format, ...) {
va_list args;
va_list args;
va_start(args, format);
va_start(args, format);
const int ret = sendFormattedWithArgs(format, args);
const int ret = sendFormattedWithArgs(format, args);
va_end(args);
va_end(args);
return ret;
return ret;
}
}


int MDMParser::sendFormattedWithArgs(const char* format, va_list args) {
int MDMParser::sendFormattedWithArgs(const char* format, va_list args) {
if (_cancel_all_operations) {
if (_cancel_all_operations) {
return 0;
return 0;
}
}
va_list argsCopy;
va_list argsCopy;
va_copy(argsCopy, args);
va_copy(argsCopy, args);
char buf[128];
char buf[128];
int n = vsnprintf(buf, sizeof(buf), format, args);
int n = vsnprintf(buf, sizeof(buf), format, args);
if (n >= 0) {
if (n >= 0) {
if ((size_t)n < sizeof(buf)) {
if ((size_t)n < sizeof(buf)) {
n = send(buf, n);
n = send(buf, n);
} else {
} else {
char buf[n + 1]; // Use larger buffer
char buf[n + 1]; // Use larger buffer
n = vsnprintf(buf, sizeof(buf), format, argsCopy);
n = vsnprintf(buf, sizeof(buf), format, argsCopy);
if (n >= 0) {
if (n >= 0) {
n = send(buf, n);
n = send(buf, n);
}
}
}
}
}
}
va_end(argsCopy);
va_end(argsCopy);
return n;
return n;
}
}


int MDMParser::waitFinalResp(_CALLBACKPTR cb /* = NULL*/,
int MDMParser::waitFinalResp(_CALLBACKPTR cb /* = NULL*/,
void* param /* = NULL*/,
void* param /* = NULL*/,
system_tick_t timeout_ms /*= 5000*/)
system_tick_t timeout_ms /*= 5000*/)
{
{
if (_cancel_all_operations) return WAIT;
if (_cancel_all_operations) return WAIT;


// If we went from a GPRS attached state to detached via URC,
// If we went from a GPRS attached state to detached via URC,
// a WDT was set and now expired. Notify system of disconnect.
// a WDT was set and now expired. Notify system of disconnect.
if (IS_GPRS_TIMEOUT()) {
if (IS_GPRS_TIMEOUT()) {
_ip = NOIP;
_ip = NOIP;
_attached = false;
_attached = false;
CLR_GPRS_TIMEOUT();
CLR_GPRS_TIMEOUT();
// HAL_NET_notify_dhcp(false);
// HAL_NET_notify_dhcp(false);
HAL_NET_notify_disconnected();
HAL_NET_notify_disconnected();
}
}


char buf[MAX_SIZE + 64 /* add some more space for framing */];
char buf[MAX_SIZE + 64 /* add some more space for framing */];
system_tick_t start = HAL_Timer_Get_Milli_Seconds();
system_tick_t start = HAL_Timer_Get_Milli_Seconds();
do {
do {
int ret = getLine(buf, sizeof(buf));
int ret = getLine(buf, sizeof(buf));
#ifdef MDM_DEBUG
#ifdef MDM_DEBUG
if ((_debugLevel >= 3) && (ret != WAIT) && (ret != NOT_FOUND))
if ((_debugLevel >= 3) && (ret != WAIT) && (ret != NOT_FOUND))
{
{
int len = LENGTH(ret);
int len = LENGTH(ret);
int type = TYPE(ret);
int type = TYPE(ret);
const char* s = (type == TYPE_UNKNOWN)? YEL "UNK" DEF :
const char* s = (type == TYPE_UNKNOWN)? YEL "UNK" DEF :
(type == TYPE_TEXT) ? MAG "TXT" DEF :
(type == TYPE_TEXT) ? MAG "TXT" DEF :
(type == TYPE_OK ) ? GRE "OK " DEF :
(type == TYPE_OK ) ? GRE "OK " DEF :
(type == TYPE_ERROR) ? RED "ERR" DEF :
(type == TYPE_ERROR) ? RED "ERR" DEF :
(type == TYPE_ABORTED) ? RED "ABT" DEF :
(type == TYPE_ABORTED) ? RED "ABT" DEF :
(type == TYPE_PLUS) ? CYA " + " DEF :
(type == TYPE_PLUS) ? CYA " + " DEF :
(type == TYPE_PROMPT) ? BLU " > " DEF :
(type == TYPE_PROMPT) ? BLU " > " DEF :
"..." ;
"..." ;
DEBUG_D("%10.3f AT read %s", (HAL_Timer_Get_Milli_Seconds()-_debugTime)*0.001, s);
DEBUG_D("%10.3f AT read %s", (HAL_Timer_Get_Milli_Seconds()-_debugTime)*0.001, s);
dumpAtCmd(buf, len);
dumpAtCmd(buf, len);
(void)s;
(void)s;
}
}
#endif
#endif
if ((ret != WAIT) && (ret != NOT_FOUND))
if ((ret != WAIT) && (ret != NOT_FOUND))
{
{
int type = TYPE(ret);
int type = TYPE(ret);
// handle unsolicited commands here
// handle unsolicited commands here
if (type == TYPE_PLUS) {
if (type == TYPE_PLUS) {
const char* cmd = buf+3;
const char* cmd = buf+3;
int a, b, c, d, r;
int a, b, c, d, r;
char s[32];
char s[32];


// SMS Command ---------------------------------
// SMS Command ---------------------------------
// +CNMI: <mem>,<index>
// +CNMI: <mem>,<index>
if (sscanf(cmd, "CMTI: \"%*[^\"]\",%d", &a) == 1) {
if (sscanf(cmd, "CMTI: \"%*[^\"]\",%d", &a) == 1) {
DEBUG_D("New SMS at index %d\r\n", a);
DEBUG_D("New SMS at index %d\r\n", a);
if (sms_cb) SMSreceived(a);
if (sms_cb) SMSreceived(a);
}
}
else if ((sscanf(cmd, "CIEV: 9,%d", &a) == 1)) {
else if ((sscanf(cmd, "CIEV: 9,%d", &a) == 1)) {
DEBUG_D("CIEV matched: 9,%d\r\n", a);
DEBUG_D("CIEV matched: 9,%d\r\n", a);
// Wait until the system is attached before attempting to act on GPRS detach
// Wait until the system is attached before attempting to act on GPRS detach
if (_attached) {
if (_attached) {
_attached_urc = (a==2)?1:0;
_attached_urc = (a==2)?1:0;
if (!_attached_urc) ARM_GPRS_TIMEOUT(15*1000); // If detached, set WDT
if (!_attached_urc) ARM_GPRS_TIMEOUT(15*1000); // If detached, set WDT
else CLR_GPRS_TIMEOUT(); // else if re-attached clear WDT.
else CLR_GPRS_TIMEOUT(); // else if re-attached clear WDT.
}
}
// Socket Specific Command ---------------------------------
// Socket Specific Command ---------------------------------
// +USORD: <socket>,<length>
// +USORD: <socket>,<length>
} else if ((sscanf(cmd, "USORD: %d,%d", &a, &b) == 2)) {
} else if ((sscanf(cmd, "USORD: %d,%d", &a, &b) == 2)) {
int socket = _findSocket(a);
int socket = _findSocket(a);
DEBUG_D("Socket %d: handle %d has %d bytes pending\r\n", socket, a, b);
DEBUG_D("Socket %d: handle %d has %d bytes pending\r\n", socket, a, b);
if (socket != MDM_SOCKET_ERROR)
if (socket != MDM_SOCKET_ERROR)
_sockets[socket].pending = b;
_sockets[socket].pending = b;
// +UUSORD: <socket>,<length>
// +UUSORD: <socket>,<length>
} else if ((sscanf(cmd, "UUSORD: %d,%d", &a, &b) == 2)) {
} else if ((sscanf(cmd, "UUSORD: %d,%d", &a, &b) == 2)) {
int socket = _findSocket(a);
int socket = _findSocket(a);
DEBUG_D("Socket %d: handle %d has %d bytes pending\r\n", socket, a, b);
DEBUG_D("Socket %d: handle %d has %d bytes pending\r\n", socket, a, b);
if (socket != MDM_SOCKET_ERROR)
if (socket != MDM_SOCKET_ERROR)
_sockets[socket].pending = b;
_sockets[socket].pending = b;
// +USORF: <socket>,<length>
// +USORF: <socket>,<length>
} else if ((sscanf(cmd, "USORF: %d,%d", &a, &b) == 2)) {
} else if ((sscanf(cmd, "USORF: %d,%d", &a, &b) == 2)) {
int socket = _findSocket(a);
int socket = _findSocket(a);
DEBUG_D("Socket %d: handle %d has %d bytes pending\r\n", socket, a, b);
DEBUG_D("Socket %d: handle %d has %d bytes pending\r\n", socket, a, b);
if (socket != MDM_SOCKET_ERROR)
if (socket != MDM_SOCKET_ERROR)
_sockets[socket].pending = b;
_sockets[socket].pending = b;
// +UUSORF: <socket>,<length>
// +UUSORF: <socket>,<length>
} else if ((sscanf(cmd, "UUSORF: %d,%d", &a, &b) == 2)) {
} else if ((sscanf(cmd, "UUSORF: %d,%d", &a, &b) == 2)) {
int socket = _findSocket(a);
int socket = _findSocket(a);
DEBUG_D("Socket %d: handle %d has %d bytes pending\r\n", socket, a, b);
DEBUG_D("Socket %d: handle %d has %d bytes pending\r\n", socket, a, b);
if (socket != MDM_SOCKET_ERROR)
if (socket != MDM_SOCKET_ERROR)
_sockets[socket].pending = b;
_sockets[socket].pending = b;
// +UUSOCL: <socket>
// +UUSOCL: <socket>
} else if ((sscanf(cmd, "UUSOCL: %d", &a) == 1)) {
} else if ((sscanf(cmd, "UUSOCL: %d", &a) == 1)) {
int socket = _findSocket(a);
int socket = _findSocket(a);
DEBUG_D("Socket %d: handle %d closed by remote host\r\n", socket, a);
DEBUG_D("Socket %d: handle %d closed by remote host\r\n", socket, a);
if (socket != MDM_SOCKET_ERROR) {
if (socket != MDM_SOCKET_ERROR) {
_socketFree(socket);
_socketFree(socket);
}
}
}
}


// GSM/UMTS Specific -------------------------------------------
// GSM/UMTS Specific -------------------------------------------
// +UUPSDD: <profile_id>
// +UUPSDD: <profile_id>
if (sscanf(cmd, "UUPSDD: %s", s) == 1) {
if (sscanf(cmd, "UUPSDD: %s", s) == 1) {
DEBUG_D("UUPSDD: %s matched\r\n", PROFILE);
DEBUG_D("UUPSDD: %s matched\r\n", PROFILE);
if ( !strcmp(s, PROFILE) ) {
if ( !strcmp(s, PROFILE) ) {
_ip = NOIP;
_ip = NOIP;
_attached = false;
_attached = false;
DEBUG("PDP context deactivated remotely!\r\n");
DEBUG("PDP context deactivated remotely!\r\n");
// PDP context was remotely deactivated via URC,
// PDP context was remotely deactivated via URC,
// Notify system of disconnect.
// Notify system of disconnect.
HAL_NET_notify_dhcp(false);
HAL_NET_notify_dhcp(false);
}
}
} else {
} else {
// +CREG|CGREG: <n>,<stat>[,<lac>,<ci>[,AcT[,<rac>]]] // reply to AT+CREG|AT+CGREG
// +CREG|CGREG: <n>,<stat>[,<lac>,<ci>[,AcT[,<rac>]]] // reply to AT+CREG|AT+CGREG
// +CREG|CGREG: <stat>[,<lac>,<ci>[,AcT[,<rac>]]] // URC
// +CREG|CGREG: <stat>[,<lac>,<ci>[,AcT[,<rac>]]] // URC
b = (int)0xFFFF; c = (int)0xFFFFFFFF; d = -1;
b = (int)0xFFFF; c = (int)0xFFFFFFFF; d = -1;
r = sscanf(cmd, "%s %*d,%d,\"%x\",\"%x\",%d",s,&a,&b,&c,&d);
r = sscanf(cmd, "%s %*d,%d,\"%x\",\"%x\",%d",s,&a,&b,&c,&d);
if (r <= 1)
if (r <= 1)
r = sscanf(cmd, "%s %d,\"%x\",\"%x\",%d",s,&a,&b,&c,&d);
r = sscanf(cmd, "%s %d,\"%x\",\"%x\",%d",s,&a,&b,&c,&d);
if (r >= 2) {
if (r >= 2) {
Reg *reg = !strcmp(s, "CREG:") ? &_net.csd :
Reg *reg = !strcmp(s, "CREG:") ? &_net.csd :
!strcmp(s, "CGREG:") ? &_net.psd : NULL;
!strcmp(s, "CGREG:") ? &_net.psd :
!strcmp(s, "CEREG:") ? &_net.eps : NULL;
if (reg) {
if (reg) {
// network status
// network status
if (a == 0) *reg = REG_NONE; // 0: not registered, home network
if (a == 0) *reg = REG_NONE; // 0: not registered, home network
else if (a == 1) *reg = REG_HOME; // 1: registered, home network
else if (a == 1) *reg = REG_HOME; // 1: registered, home network
else if (a == 2) *reg = REG_NONE; // 2: not registered, but MT is currently searching a new operator to register to
else if (a == 2) *reg = REG_NONE; // 2: not registered, but MT is currently searching a new operator to register to
else if (a == 3) *reg = REG_DENIED; // 3: registration denied
else if (a == 3) *reg = REG_DENIED; // 3: registration denied
else if (a == 4) *reg = REG_UNKNOWN; // 4: unknown
else if (a == 4) *reg = REG_UNKNOWN; // 4: unknown
else if (a == 5) *reg = REG_ROAMING; // 5: registered, roaming
else if (a == 5) *reg = REG_ROAMING; // 5: registered, roaming
if ((r >= 3) && (b != (int)0xFFFF)) _net.lac = b; // location area code
if ((r >= 3) && (b != (int)0xFFFF)) _net.lac = b; // location area code
if ((r >= 4) && (c != (int)0xFFFFFFFF)) _net.ci = c; // cell ID
if ((r >= 4) && (c != (int)0xFFFFFFFF)) _net.ci = c; // cell ID
// access technology
// access technology
if (r >= 5) {
if (r >= 5) {
if (d == 0) _net.act = ACT_GSM; // 0: GSM
if (d == 0) _net.act = ACT_GSM; // 0: GSM
else if (d == 1) _net.act = ACT_GSM; // 1: GSM COMPACT
else if (d == 1) _net.act = ACT_GSM; // 1: GSM COMPACT
else if (d == 2) _net.act = ACT_UTRAN; // 2: UTRAN
else if (d == 2) _net.act = ACT_UTRAN; // 2: UTRAN
else if (d == 3) _net.act = ACT_EDGE; // 3: GSM with EDGE availability
else if (d == 3) _net.act = ACT_EDGE; // 3: GSM with EDGE availability
else if (d == 4) _net.act = ACT_UTRAN; // 4: UTRAN with HSDPA availability
else if (d == 4) _net.act = ACT_UTRAN; // 4: UTRAN with HSDPA availability
else if (d == 5) _net.act = ACT_UTRAN; // 5: UTRAN with HSUPA availability
else if (d == 5) _net.act = ACT_UTRAN; // 5: UTRAN with HSUPA availability
else if (d == 6) _net.act = ACT_UTRAN; // 6: UTRAN with HSDPA and HSUPA availability
else if (d == 6) _net.act = ACT_UTRAN; // 6: UTRAN with HSDPA and HSUPA availability
else if (d == 7) _net.act = ACT_LTE; // 7: LTE
else if (d == 8) _net.act = ACT_LTE_CAT_M1; // 8: LTE Cat M1
else if (d == 9) _net.act = ACT_LTE_CAT_NB1; // 9: LTE Cat NB1
}
}
}
}
}
}
}
}
} // end ==TYPE_PLUS
} // end ==TYPE_PLUS
if (cb) {
if (cb) {
int len = LENGTH(ret);
int len = LENGTH(ret);
int ret = cb(type, buf, len, param);
int ret = cb(type, buf, len, param);
if (WAIT != ret)
if (WAIT != ret)
return ret;
return ret;
}
}
if (type == TYPE_OK)
if (type == TYPE_OK)
return RESP_OK;
return RESP_OK;
if (type == TYPE_ERROR)
if (type == TYPE_ERROR)
return RESP_ERROR;
return RESP_ERROR;
if (type == TYPE_PROMPT)
if (type == TYPE_PROMPT)
return RESP_PROMPT;
return RESP_PROMPT;
if (type == TYPE_ABORTED)
if (type == TYPE_ABORTED)
return RESP_ABORTED; // This means the current command was ABORTED, so retry your command if critical.
return RESP_ABORTED; // This means the current command was ABORTED, so retry your command if critical.
}
}
// relax a bit
// relax a bit
HAL_Delay_Milliseconds(10);
HAL_Delay_Milliseconds(10);
}
}
while (!TIMEOUT(start, timeout_ms) && !_cancel_all_operations);
while (!TIMEOUT(start, timeout_ms) && !_cancel_all_operations);


return WAIT;
return WAIT;
}
}


int MDMParser::sendCommandWithArgs(const char* fmt, va_list args, _CALLBACKPTR cb, void* param, system_tick_t timeout)
int MDMParser::sendCommandWithArgs(const char* fmt, va_list args, _CALLBACKPTR cb, void* param, system_tick_t timeout)
{
{
LOCK();
LOCK();
sendFormattedWithArgs(fmt, args);
sendFormattedWithArgs(fmt, args);
const int ret = waitFinalResp(cb, param, timeout);
const int ret = waitFinalResp(cb, param, timeout);
UNLOCK();
UNLOCK();
return ret;
return ret;
}
}


void MDMParser::lock()
void MDMParser::lock()
{
{
mdm_mutex.lock();
mdm_mutex.lock();
}
}


void MDMParser::unlock()
void MDMParser::unlock()
{
{
mdm_mutex.unlock();
mdm_mutex.unlock();
}
}


int MDMParser::_cbString(int type, const char* buf, int len, char* str)
int MDMParser::_cbString(int type, const char* buf, int len, char* str)
{
{
if (str && (type == TYPE_UNKNOWN)) {
if (str && (type == TYPE_UNKNOWN)) {
if (sscanf(buf, "\r\n%s\r\n", str) == 1)
if (sscanf(buf, "\r\n%s\r\n", str) == 1)
/*nothing*/;
/*nothing*/;
}
}
return WAIT;
return WAIT;
}
}


int MDMParser::_cbInt(int type, const char* buf, int len, int* val)
int MDMParser::_cbInt(int type, const char* buf, int len, int* val)
{
{
if (val && (type == TYPE_UNKNOWN)) {
if (val && (type == TYPE_UNKNOWN)) {
if (sscanf(buf, "\r\n%d\r\n", val) == 1)
if (sscanf(buf, "\r\n%d\r\n", val) == 1)
/*nothing*/;
/*nothing*/;
}
}
return WAIT;
return WAIT;
}
}


// ----------------------------------------------------------------
// ----------------------------------------------------------------


bool MDMParser::connect(
bool MDMParser::connect(
const char* simpin,
const char* apn, const char* username,
const char* apn, const char* username,
const char* password, Auth auth)
const char* password, Auth auth)
{
{
bool ok = powerOn(simpin);
bool ok = registerNet(apn);
if (!ok)
/*
return false;
ok = init();
#ifdef MDM_DEBUG
#ifdef MDM_DEBUG
if (_debugLevel >= 1) dumpDevStatus(&_dev);
if (_debugLevel >= 1) {
dumpNetStatus(&_net);
}
#endif
#endif
if (!ok)
*/
if (!ok) {
return false;
return false;
ok = registerNet();
}
ok = pdp(apn);
/*
#ifdef MDM_DEBUG
#ifdef MDM_DEBUG
if (_debugLevel >= 1) dumpNetStatus(&_net);
if (_debugLevel >= 1) {
dumpNetStatus(&_net);
}
#endif
#endif
if (!ok)
*/
if (!ok) {
return false;
return false;
MDM_IP ip = join(apn,username,password,auth);
}
const MDM_IP ip = join(apn, username, password, auth);
/*
#ifdef MDM_DEBUG
#ifdef MDM_DEBUG
if (_debugLevel >= 1) dumpIp(ip);
if (_debugLevel >= 1) {
dumpIp(ip);
}
#endif
#endif
if (ip == NOIP)
*/
if (ip == NOIP) {
return false;
}
HAL_NET_notify_connected();
HAL_NET_notify_dhcp(true);
return true;
}

bool MDMParser::disconnect() {
if (!deactivate()) {
return false;
}
if (!detach()) {
return false;
return false;
}
HAL_NET_notify_disconnected();
return true;
return true;
}
}


void MDMParser::reset(void)
void MDMParser::reset(void)
{
{
MDM_INFO("[ Modem reset ]");
MDM_INFO("[ Modem reset ]");
unsigned delay = 100;
if (_dev.dev == DEV_UNKNOWN || _dev.dev == DEV_SARA_R410) {
delay = 10000; // SARA-R4: 10s
}
HAL_GPIO_Write(RESET_UC, 0);
HAL_GPIO_Write(RESET_UC, 0);
HAL_Delay_Milliseconds(100);
HAL_Delay_Milliseconds(delay);
HAL_GPIO_Write(RESET_UC, 1);
HAL_GPIO_Write(RESET_UC, 1);
}
}


bool MDMParser::_powerOn(void)
bool MDMParser::_powerOn(void)
{
{
LOCK();
LOCK();


/* Initialize I/O */
/* Initialize I/O */
STM32_Pin_Info* PIN_MAP_PARSER = HAL_Pin_Map();
STM32_Pin_Info* PIN_MAP_PARSER = HAL_Pin_Map();
// This pin tends to stay low when floating on the output of the buffer (PWR_UB)
// This pin tends to stay low when floating on the output of the buffer (PWR_UB)
// It shouldn't hurt if it goes low temporarily on STM32 boot, but strange behavior
// It shouldn't hurt if it goes low temporarily on STM32 boot, but strange behavior
// was noticed when it was left to do whatever it wanted. By adding a 100k pull up
// was noticed when it was left to do whatever it wanted. By adding a 100k pull up
// resistor all flakey behavior has ceased (i.e., the modem had previously stopped
// resistor all flakey behavior has ceased (i.e., the modem had previously stopped
// responding to AT commands). This is how we set it HIGH before enabling the OUTPUT.
// responding to AT commands). This is how we set it HIGH before enabling the OUTPUT.
PIN_MAP_PARSER[PWR_UC].gpio_peripheral->BSRRL = PIN_MAP_PARSER[PWR_UC].gpio_pin;
PIN_MAP_PARSER[PWR_UC].gpio_peripheral->BSRRL = PIN_MAP_PARSER[PWR_UC].gpio_pin;
HAL_Pin_Mode(PWR_UC, OUTPUT);
HAL_Pin_Mode(PWR_UC, OUTPUT);
// This pin tends to stay high when floating on the output of the buffer (RESET_UB),
// This pin tends to stay high when floating on the output of the buffer (RESET_UB),
// but we need to ensure it gets set high before being set to an OUTPUT.
// but we need to ensure it gets set high before being set to an OUTPUT.
// If this pin goes LOW, the modem will be reset and all configuration will be lost.
// If this pin goes LOW, the modem will be reset and all configuration will be lost.
PIN_MAP_PARSER[RESET_UC].gpio_peripheral->BSRRL = PIN_MAP_PARSER[RESET_UC].gpio_pin;
PIN_MAP_PARSER[RESET_UC].gpio_peripheral->BSRRL = PIN_MAP_PARSER[RESET_UC].gpio_pin;
HAL_Pin_Mode(RESET_UC, OUTPUT);
HAL_Pin_Mode(RESET_UC, OUTPUT);


#if USE_USART3_HARDWARE_FLOW_CONTROL_RTS_CTS
_dev.dev = DEV_UNKNOWN;
_dev.lpm = LPM_ENABLED;
_dev.lpm = LPM_ENABLED;
#else
HAL_Pin_Mode(RTS_UC, OUTPUT);
HAL_GPIO_Write(RTS_UC, 0); // VERY IMPORTANT FOR CORRECT OPERATION W/O HW FLOW CONTROL!!
#endif


HAL_Pin_Mode(LVLOE_UC, OUTPUT);
HAL_Pin_Mode(LVLOE_UC, OUTPUT);
HAL_GPIO_Write(LVLOE_UC, 0);
HAL_GPIO_Write(LVLOE_UC, 0);


if (!_init) {
if (!_init) {
MDM_INFO("[ ElectronSerialPipe::begin ] = = = = = = = =");
MDM_INFO("[ ElectronSerialPipe::begin ] = = = = = = = =");


/* Instantiate the USART3 hardware */
// Here we initialize the UART with hardware flow control enabled, even though some of
electronMDM.begin(115200);
// the modems don't support it (SARA-R4 at the time of writing). It is assumed that the

// modem still keeps the CTS pin in a correct state even if doesn't support the CTS/RTS
/* Initialize only once */
// flow control
electronMDM.begin(115200, true /* hwFlowControl */);
_init = true;
_init = true;
}
}


MDM_INFO("\r\n[ Modem::powerOn ] = = = = = = = = = = = = = =");
MDM_INFO("\r\n[ Modem::powerOn ] = = = = = = = = = = = = = =");
bool continue_cancel = false;
bool continue_cancel = false;
bool retried_after_reset = false;
bool retried_after_reset = false;


int i = 10;
int i = 10;
while (i--) {
while (i--) {
// SARA-U2/LISA-U2 50..80us
// SARA-U2/LISA-U2 50..80us
HAL_GPIO_Write(PWR_UC, 0); HAL_Delay_Milliseconds(50);
HAL_GPIO_Write(PWR_UC, 0); HAL_Delay_Milliseconds(50);
HAL_GPIO_Write(PWR_UC, 1); HAL_Delay_Milliseconds(10);
HAL_GPIO_Write(PWR_UC, 1); HAL_Delay_Milliseconds(10);


// SARA-G35 >5ms, LISA-C2 > 150ms, LEON-G2 >5ms
// SARA-G35 >5ms, LISA-C2 > 150ms, LEON-G2 >5ms, SARA-R4 >= 150ms
HAL_GPIO_Write(PWR_UC, 0); HAL_Delay_Milliseconds(150);
HAL_GPIO_Write(PWR_UC, 0); HAL_Delay_Milliseconds(150);
HAL_GPIO_Write(PWR_UC, 1); HAL_Delay_Milliseconds(100);
HAL_GPIO_Write(PWR_UC, 1); HAL_Delay_Milliseconds(100);


// purge any messages
// purge any messages
purge();
purge();


// Save desire to cancel, but since we are already here
// Save desire to cancel, but since we are already here
// trying to power up the modem when we received a cancel
// trying to power up the modem when we received a cancel
// resume AT parser to ensure it's ready to receive
// resume AT parser to ensure it's ready to receive
// power down commands.
// power down commands.
if (_cancel_all_operations) {
if (_cancel_all_operations) {
continue_cancel = true;
continue_cancel = true;
resume(); // make sure we can talk to the modem
resume(); // make sure we can talk to the modem
}
}


// check interface
// check interface
sendFormated("AT\r\n");
sendFormated("AT\r\n");
int r = waitFinalResp(NULL,NULL,1000);
int r = waitFinalResp(NULL,NULL,1000);
if(RESP_OK == r) {
if(RESP_OK == r) {
_pwr = true;
_pwr = true;
break;
break;
}
}
else if (i==0 && !retried_after_reset) {
else if (i==0 && !retried_after_reset) {
retried_after_reset = true; // only perform reset & retry sequence once
retried_after_reset = true; // only perform reset & retry sequence once
i = 10;
i = 10;
reset();
reset();
}
}


}
}
if (i < 0) {
if (i < 0) {
MDM_ERROR("[ No Reply from Modem ]\r\n");
MDM_ERROR("[ No Reply from Modem ]\r\n");
} else {
// Determine type of the modem
sendFormated("AT+CGMM\r\n");
waitFinalResp(_cbCGMM, &_dev);
if (_dev.dev == DEV_SARA_R410) {
// SARA-R410 doesn't support hardware flow control, reinitialize the UART
electronMDM.begin(115200, false /* hwFlowControl */);
// Power saving modes defined by the +UPSV command are not supported
_dev.lpm = LPM_DISABLED;
}
}
}


if (continue_cancel) {
if (continue_cancel) {
cancel();
cancel();
goto failure;
goto failure;
}
}


// Flush any on-boot URCs that can cause syncing issues later
// Flush any on-boot URCs that can cause syncing issues later
waitFinalResp(NULL,NULL,200);
waitFinalResp(NULL,NULL,200);


// echo off
// echo off
sendFormated("AT E0\r\n");
sendFormated("AT E0\r\n");
if(RESP_OK != waitFinalResp())
if(RESP_OK != waitFinalResp())
goto failure;
goto failure;
// enable verbose error messages
// enable verbose error messages
sendFormated("AT+CMEE=2\r\n");
sendFormated("AT+CMEE=2\r\n");
if(RESP_OK != waitFinalResp())
if(RESP_OK != waitFinalResp())
goto failure;
goto failure;
// Configures sending of URCs from MT to DTE for indications
// Configures sending of URCs from MT to DTE for indications
sendFormated("AT+CMER=1,0,0,2,1\r\n");
sendFormated("AT+CMER=1,0,0,2,1\r\n");
if(RESP_OK != waitFinalResp())
if(RESP_OK != waitFinalResp())
goto failure;
goto failure;
// set baud rate
// set baud rate
sendFormated("AT+IPR=115200\r\n");
sendFormated("AT+IPR=115200\r\n");
if (RESP_OK != waitFinalResp())
if (RESP_OK != waitFinalResp())
goto failure;
goto failure;
// wait some time until baudrate is applied
// wait some time until baudrate is applied
HAL_Delay_Milliseconds(100); // SARA-G > 40ms
HAL_Delay_Milliseconds(100); // SARA-G > 40ms


UNLOCK();
UNLOCK();
return true;
return true;
failure:
failure:
UNLOCK();
UNLOCK();
return false;
return false;
}
}


bool MDMParser::powerOn(const char* simpin)
bool MDMParser::powerOn(const char* simpin)
{
{
LOCK();
LOCK();
memset(&_dev, 0, sizeof(_dev));
memset(&_dev, 0, sizeof(_dev));
bool retried_after_reset = false;
bool retried_after_reset = false;


/* Power on the modem and perform basic initialization */
/* Power on the modem and perform basic initialization */
if (!_powerOn())
if (!_powerOn())
goto failure;
goto failure;


/* The ATI command is undocumented, and in practice the response
/* The ATI command is undocumented, and in practice the response
* time varies greatly. On inital power-on of the module, ATI
* time varies greatly. On inital power-on of the module, ATI
* will respond with "OK" before a device type number, which
* will respond with "OK" before a device type number, which
* requires wasting time in a for() loop to solve.
* requires wasting time in a for() loop to solve.
* Instead, use AT+CGMM and _dev.model for future use of module identification.
* Instead, use AT+CGMM and _dev.model for future use of module identification.
*
*
* identify the module
* identify the module
* sendFormated("ATI\r\n");
* sendFormated("ATI\r\n");
* if (RESP_OK != waitFinalResp(_cbATI, &_dev.dev))
* if (RESP_OK != waitFinalResp(_cbATI, &_dev.dev))
* goto failure;
* goto failure;
* if (_dev.dev == DEV_UNKNOWN)
* if (_dev.dev == DEV_UNKNOWN)
* goto failure;
* goto failure;
*/
*/


// check the sim card
// check the sim card
for (int i = 0; (i < 5) && (_dev.sim != SIM_READY) && !_cancel_all_operations; i++) {
for (int i = 0; (i < 5) && (_dev.sim != SIM_READY) && !_cancel_all_operations; i++) {
sendFormated("AT+CPIN?\r\n");
sendFormated("AT+
int ret = waitFinalResp(_cbCPIN, &_dev.sim);
// having an error here is ok (sim may still be initializing)
if ((RESP_OK != ret) && (RESP_ERROR != ret)) {
goto failure;
}
else if (i==4 && (RESP_OK != ret) && !retried_after_reset) {
retried_after_reset = true; // only perform reset & retry sequence once
i = 0;
if(!powerOff())
reset();
/* Power on the modem and perform basic initialization again */
if (!_powerOn())
goto failure;
}
// Enter PIN if needed
if (_dev.sim == SIM_PIN) {
if (!simpin) {
MDM_ERROR("SIM PIN not available\r\n");
goto failure;
}
sendFormated("AT+CPIN=%s\r\n", simpin);
if (RESP_OK != waitFinalResp(_cbCPIN, &_dev.sim))
goto failure;
} else if (_dev.sim != SIM_READY) {
// wait for up to one second while looking for slow "+CPIN: READY" URCs
waitFinalResp(_cbCPIN, &_dev.sim, 1000);
}
}
if (_dev.sim != SIM_READY) {
if (_dev.sim == SIM_MISSING) {
MDM_ERROR("SIM not inserted\r\n");
}
goto failure;
}

UNLOCK();
return true;
failure:
if (_cancel_all_operations) {
// fake out the has_credentials() function so we don't end up in listening mode
_dev.sim = SIM_READY;
// return true to prevent from entering Listening Mode
// UNLOCK();
// return true;
}
UNLOCK();
return false;
}

bool MDMParser::init(DevStatus* status)
{
LOCK();
MDM_INFO("\r\n[ Modem::init ] = = = = = = = = = = = = = = =");

// Returns the product serial number, IMEI (International Mobile Equipment Identity)
sendFormated("AT+CGSN\r\n");
i