Untitled diff

Created Diff never expires
/*
/*
******************************************************************************
******************************************************************************
* 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)
{
{
if (color) DEBUG_D(color);
va_list args;
va_list args;
va_start (args, format);
va_start(args, format);
if (color) DEBUG_D(color);
log_printf_v(LOG_LEVEL_TRACE, LOG_THIS_CATEGORY(), nullptr, format, args);
DEBUG_D(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