/*
* Copyright (C) 2018
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "app_defs.h"
#include "boot.h"
#include "common.h"
#include "le910.h"
#include "modem_iface.h"
#include "modem_iface.h"
#include "app_defs.h"
#include "at_cmd.h"
#include "settings.h"
#include "utils.h"
#include "talisker.h"
#include "cellLogicTal.h"
#include "dei_net.h"
#include "mdm_bridge.h"
#include "cell.h"
#include "cellLogic.h"
// Externs
extern UART_HandleTypeDef huart2;
extern UINT8 main_state;
extern char cellLogic_version[];
extern char cellLogic_iccid[];
extern FLOAT cellLogic_lastATRcvd;
extern FLOAT cellLogic_commChangeAge;
extern UINT32 cellLogic_localAreaCode;
extern bool cellLogic_isCommAvailable;
extern UINT32 cellLogic_mccmnc;
extern long long cellLogic_msisdn;
extern long long cellLogic_imei;
extern long long cellLogic_imsi;
extern void handleCharForCell();
extern void myHandleCharForCell(const char *whence);
extern void main_setState (UINT8 newState);
#define IMEI_HEX_LEN 16
#define SMS_NUMBER_LEN 16
#define DEFAULT_SEGMENT_LENGTH 512
#define REQUEST_SEGMENT_LENGTH 1024
#define NETWORK_TYPE_POLL 10 // Number of time intervals between network type req
//--------------------------------------------------------------------------------
// Globals
//--------------------------------------------------------------------------------
QUEUE cell_atBuffer;
UINT8 imeiBuffer[8] = {0, };
cmdDataType dataPool[MAX_NUM_AT_CMD]; // to do - meant for long udp msg move to internal flash
atCmdType atCmdPool[MAX_NUM_AT_CMD]; // to do - move to internal flash
cellOpState cellState = CELL_WAIT_INIT | CELL_WAIT_SETUP; // initial AT cmds and socket cmd not done
xferState sendState = XFER_NONE;
bool udpMsgSocketOpen = false;
bool udpDmanSocketOpen = false;
bool udpTalSocketOpen = false;
UINT32 segmentLength = DEFAULT_SEGMENT_LENGTH;
INT8 networkType = -1;
INT32 netTypePollCount = 0;
char *networkTypeStr[] = {"GPRS", "EGPRS", "WCDMA", "HSDPA", "LTE", "Unknown or unregistered"};
char operatorName[32] = {0, };
char cell_dmanServer[DMAN_REPORT_SERVER_SIZE] = {0, };
/**
* Hex representation of imei
* @public {string}
*/
char cell_imeiHex[IMEI_HEX_LEN * 2]= {0, };
char cell_imeiPrint[17]= {0, }; // for print since long long printf doesn't work
char cell_imsiPrint[16]= {0, }; // for print since long long printf doesn't work
char cell_msidnPrint[16]= {0, }; // for print since long long printf doesn't work
/**
* Store last response for parsing purposes
* @private {string}
*/
char cell_lastResponse[MAX_RESP_LEN] = {0, };
/*
*
* @public {boolean}
*/
bool cell_isRoaming = false;
/**
*
* @private {string}
*/
char cell_smsFrom[SMS_NUMBER_LEN] = {0, };
/**
*
* @public {boolean}
*/
bool cell_simStoreReady = false;
/**
* Is SIP registered yet
* @public {boolean}
*/
bool cell_netRegistered = false;
/**
* Count of consecutive retry failures
* @public {uint8_t}
*/
UINT8 retryFaultCount = 0;
/**
* Store date last AT command sent
* @private {number}
*/
FLOAT cell_lastAtSendDate = 0;
FLOAT cell_lastAtRecDate = 0;
bool hasSmsSrc = false;
char tmpStr[MAX_TMP_STR_LEN] = {0, };
//--------------------------------------------------------------------------------
// Local globals
//--------------------------------------------------------------------------------
// Set true when an AT command is issued to the modem
static bool atCmdInProgress = false;
//--------------------------------------------------------------------------------
// Begin functions
//--------------------------------------------------------------------------------
xferState getSendState(void)
{
return sendState;
}
/**
* @public
* @param {string} cmdString
*/
void cellWrite(UINT8 *str, UINT16 len) {
UINT16 ii;
for (ii = 0; ii < len; ii++) {
Mdm_Put_Char(str[ii]);
}
}
void cellSetOpState(cellOpState newOpState) {
//DBG(D_CELL,"curCellState=0x%02x newOpState=0x%02x\n", cellState, newOpState);
cellState = newOpState;
}
cellOpState cellCheckOpState() {
UINT8 initDoneFlags = CELL_GOOD_CEREG | CELL_GOOD_CGSN | CELL_GOOD_CIMI | CELL_GOOD_CCID;
if ((cellState & initDoneFlags) == initDoneFlags) {
cellState &= ~CELL_INIT_IN_PROG;
cellState |= CELL_INIT_DONE;
//DBG(D_CELL,"%s: cell init done!\n");
} else {
cellState |= CELL_INIT_IN_PROG;
}
return cellState;
}
bool cellControl(UINT8 flags) {
bool pwrStateGood = false;
if (flags & CELL_CTRL_ON) {
//
// See pinTable for all the pins the Cell use), to turn on comm module:
// 1- De-assert ID_COM_HW_SHUTDOWN
// 2- Assert ID_COM_POWER
// 3- Assert,wait and then de-assert ID_COM_POWER_KEY
// (optional: Check comm module power By reading ID_COM_POWER_MON)
//
DBG(D_CELL,"Cell control: Turn on cell at %.3f\n", utilsAge());
Mdm_Hw_Shdn_Inactive();
Mdm_Pwr_Enable();
HAL_Delay(1000);
Mdm_On_Off_Active();
HAL_Delay(5500);
App_WDog_Refresh();
HAL_Delay(5000);
Mdm_On_Off_Inactive();
cellReadyHandler();
} else if (flags & CELL_CTRL_OFF) { // Disable - Turn off cell power
DBG(D_CELL,"Cell control: Turn off cell at %.3f\n", utilsAge());
printf("Toggling modem ON/OFF...\n");
Mdm_On_Off_Active();
HAL_Delay(3500);
Mdm_On_Off_Inactive();
printf("Toggling modem ON/OFF done\n");
Refresh_IWDog();
HAL_Delay(5000);
printf("Removing power...\n");
Mdm_Pwr_Disable();
printf("10000 ms delay...\n");
Refresh_IWDog();
{
int i=10;
while (i--) {
printf("Modem power down %d...\n", i
);
Mdm_Hw_Shdn_Active();
HAL_Delay(250);
Mdm_Hw_Shdn_Inactive();
HAL_Delay(750);
Refresh_IWDog();
}
}
} else if (flags & CELL_CTRL_CHK_PWR) { // Check cell power
if (Mdm_Pwr_Mon() == 0) {
pwrStateGood = true;
}
DBG(D_CELL,"Cell control: pwrStateGood = %d\n", pwrStateGood);
return pwrStateGood;
} else if (flags & CELL_CTRL_RESET) { // inband cmd reset cell
char tmp[12];
sprintf((char*)tmp
, "%s%s", AT_RESET
, RETURN
);
DBG(D_CELL,"Cell control: Reset cell (%s)\n", (char*)tmp);
cellWrite
((UINT8
*)tmp
, strlen((char*)tmp
));
}
return false;
}
/**
* Convenience method for finding str2 in str1
* @private
* @param {Uint8Array} data
* @param {number} dataStart
* @param {number} dataLength
* @param {string} str
* @returns {boolean}
*/
bool cell_match(UINT8 *data, UINT8 startIdx, UINT32 len, char *s) {
UINT32 i
, here
= startIdx
, n
= strlen(s
);
if (len < startIdx + n) {
return false;
}
// compare to the integer value of a single character at position i in the String
for (i = 0; i < n; i++) {
if (data[i + here] != (UINT8)s[i]) {
//printf("BAD Compare %s: i=%d 1st=%d 2nd[0]=%d\n", s, i, data[i + startIdx]);
return false;
}
}
return true;
}
/**
* Configure UDP messaging port.
* @private
* @param {number} connId
* @param {string} serverIp
* @param {number} port
*/
void cell_configUdp(UINT8 connId, char *serverIp, UINT32 port) {
cell_openSocket(connId, serverIp, port);
}
// Handle all AT cmd response WITHOUT +<cmd> prefix
void cell_processGoodCmdResp(char *lastCmd, UINT8 *lastResp) {
//DBG(D_CELL, "%s: lastCmd=%s lastResp=%s\n", __FUNCTION__, (char*)lastCmd, (char*)lastResp);
if (strncmp(lastCmd
, AT_SMS
, 7) == 0) {
UINT8 i;
DBG(D_CELL, "%s: lastCmd=%s lastResp=%s\n", __FUNCTION__, (char*)lastCmd, (char*)lastResp);
for (i
= 0; i
< strlen(lastResp
); i
++) {
DBG(D_CELL, "%02d ", (UINT8)lastResp[i]);
}
} else if (strcmp(lastCmd
, CGSN
) == 0) {
// Product Serial Number
#if defined(PLATFORM_TALISKER_PRO) || defined(PLATFORM_TALISKER_OBD)
Mdm_Bridge_Set_IMEI(lastResp);
#endif
cellLogic_imei = parseInt(lastResp, 10);
strcpy((char*)cell_imeiPrint
, (char*)lastResp
);
utils_put8BE((UINT8*)imeiBuffer, 0, cellLogic_imei);
utoh((UINT8*)imeiBuffer, 0, IMEI_HEX_LEN-1, (char*)cell_imeiHex);
cellState |= CELL_GOOD_CGSN;
DBG(D_CELL, "cell_imeiHex:\n");
Dump_Hex_Data(cell_imeiHex, IMEI_HEX_LEN);
} else if (strcmp(lastCmd
, CIMI
) == 0) {
// Message Receiving Behavior - Get IMSI
cellState |= CELL_GOOD_CIMI;
strcpy((char*)cell_imsiPrint
,(char*)lastResp
);
cellLogic_imsi = parseInt((char*)lastResp, 10);
//DBG(D_CELL, "cell imsi=%lld\n", cellLogic_imsi);
} else if (strcmp(lastCmd
, CGMR
) == 0) {
// Get Device Software Rev, remove \r\n in resp
strcpy((char*)cellLogic_version
, (char*)lastResp
);
remChar((char*)cellLogic_version, (char)13);
DBG(D_CELL, "cell sw ver = %s\n", (char*)cellLogic_version);
//Dump_Hex_Data((UINT8*)cellLogic_version, 16);
} else if (strncmp(lastCmd
, AT_SOC_CFGEXT
, 10) == 0) { // last setup cmd done, clear wait state
if (cellCheckOpState() & CELL_WAIT_SETUP) {
cellOpState s = cellCheckOpState();
s &= ~(CELL_WAIT_SETUP);
cellSetOpState(s);
DBG(D_CELL, "Cleared WAIT_SETUP state=0x%02x --> handleComm\n", s);
cellLogic_handleComm();
#if defined(PLATFORM_TALISKER_PRO) || defined(PLATFORM_TALISKER_SPORT) || defined(PLATFORM_TALISKER_OBD)
// request time from Tal server
if (!testPuiLoc) { // only request time if using back-end Tal server
cell_configUdp(UDP_TALISKER_SOC, settings[MESSAGES_REPORT_SERVER].u.ptr,
settings[MESSAGES_REPORT_PORT].u.v.iVal);
HAL_Delay(100);
cellLogicSendTalTimeReq();
}
#endif
}
} else if (strncmp(lastResp
, AT_MTUSIZE
, 11) == 0) {
segmentLength
= (UINT32
)strtol((char*)&lastResp
[11], NULL
, 10);
if (segmentLength > MAX_SEGMENT_LENGTH) {
segmentLength = MAX_SEGMENT_LENGTH;
}
DBG(D_CELL, "MTUSIZE Response received: %lu\n", segmentLength);
}
}
#define MAX_C_SNAC_RETRIES 0 // do not do retries here!
/**
* Check AT Command queue for unsent messages and send them.
* Also print out last command with status.
* @privatcountste
* @param {string=} handleReceive ("OK", "ERROR", or NULL)
*/
bool cell_sendNextAtCommand (char* handleReceive)
{
static int retry_count = 0;
bool rtn = false;
bool sendFirst = false;
bool hasSent = false;
INT8 idx = 0;
// Is there anything in the "cell_atBuffer" queue
if (cell_atBuffer.count > 0) {
idx = cell_atBuffer.front;
hasSent = cell_atBuffer.s.arr[idx].sent;
if (!hasSent) {
sendState = 0; // should keep -- safety knows no season!
retry_count = 0;
}
}
// If already sent p1, send p2 right away since p2 only depends on ">" from the cell
// For all other cases (new cmd) allow 3 seconds for response, resend if no resp
sendFirst = ((utilsAge() - cell_lastAtSendDate) > 3); // seconds
if (sendState & XFER_SENT_P1) {
if (sendFirst) // didn't get resp, already sent p1, clear the flag to retry p1
{
sendState &= ~XFER_SENT_P1;
}
else // already sent p1, just wait for prompt ">" to send p2, send it now
{
sendFirst = true;
}
}
else if (sendState & XFER_SENT_P2) {
if (sendFirst) // didn't get resp, already sent p2, clear flag retry sending p1
{
sendState &= ~XFER_SENT_P2;
retry_count++;
printf("%s, retry %d\n", __func__
, retry_count
); //debug
DBG(D_CELL, "%s, retry %d\n", __func__, retry_count);
#define DO_NOt_USE_INFINITE_RETRIES
#ifdef DO_NOt_USE_INFINITE_RETRIES
if(retry_count > MAX_C_SNAC_RETRIES)
{
idx = deQ(&cell_atBuffer); // Upper levels must retry
if(idx >= 0) {
atCmdType *lastCmd = &cell_atBuffer.s.arr[idx];
lastCmd->type = 0;
lastCmd->sent = 0;
printf("%s, retry count %d exceeded!\n", __func__
, retry_count
);
retry_count = 0;
} else {
printf("%s, AT queue unexpectedly empty! (1)\n",__func__
);
}
}
#endif // DO_NOt_USE_INFINITE_RETRIES
}
}
//DBG(D_CELL, "%s: lastReceived=%f\n", __FUNCTION__, cell_lastAtRecDate);
//if (handleReceive)
// DBG(D_CELL, "%s: Received=%s\n", __FUNCTION__, handleReceive);
//DBG(D_CELL, "hasSent=%d sendFirst=%d utilsAge=%f lastSentDate=%f\n",
// (INT32)hasSent, sendFirst, utilsAge(), cell_lastAtSendDate);
if (hasSent)
{
// check if anything received
// NULL --> nothing received, if
// OK -->
// ERROR -->
if (handleReceive != NULL)
{
// NOT NULL so handleReceive is expected to be: OK or ERROR
idx = deQ(&cell_atBuffer); // Only take out of the Q when get response
atCmdInProgress = false;
if(idx < 0) {
printf("%s, AT queue unexpectedly empty! (2)\n",__func__
);
return;
}
atCmdType *lastCmd = &cell_atBuffer.s.arr[idx];
memset((char*)tmpStr
, 0, sizeof(tmpStr
));
if ((lastCmd->type == AT_CMD_UDP_MESSAGE) || (lastCmd->type == AT_CMD_UDP_DMAN))
{
sprintf(tmpStr
, "%s -> UDP\n", handleReceive
);
}
else if (lastCmd->type == AT_CMD_SMS)
{
sprintf(tmpStr
, "%s -> SMS=%s, %s\n", handleReceive
, (char*)lastCmd
->cmd
, (char*)lastCmd
->extra
);
sendState &= ~XFER_SENT_P2; // Got resp, clear p2 flag
}
else
{
if (strstr(lastCmd
->cmd
, CSQ
) != NULL
&& strstr(lastCmd
->cmd
, CCLK
) != NULL
)
{
DBG(D_CELL, "%s -> %s%s\n", handleReceive,
(((strstr((char*)lastCmd
->cmd
, "AT")) == 0) ? "AT+" : ""),
(char*)lastCmd->cmd);
}
if (strcmp(handleReceive
, "OK") == 0)
{
cell_processGoodCmdResp((char*)lastCmd->cmd, (char*)cell_lastResponse);
}
}
// Zero out the cmd struct that was done
//DBG(D_CELL, "Done - zero-out last cmd\n");
lastCmd->type = 0;
lastCmd->sent = 0;
memset(lastCmd
->cmd
, 0, sizeof(cmdDataType
));
sendFirst = true;
}
else
{
// NULL --> nothing received
// Has sent the cmd but no resp back and the time allowed
// to get resp hasn't expired. So, not sending anything
if(sendState & XFER_SENT_P1)
{
sendFirst = true;
}
}
}
else
{
// cmd has not been sent
sendFirst = true;
}
if (sendFirst && cell_atBuffer.count > 0)
{
idx = cell_atBuffer.front;
atCmdType *atCommand = &cell_atBuffer.s.arr[idx];
atCommand->sent = true;
cell_lastAtSendDate = utilsAge();
if ((atCommand->type == AT_CMD_UDP_MESSAGE) || (atCommand->type == AT_CMD_UDP_DMAN))
{
char msg[6];
UINT16 cmdLen = atCommand->cmdLen; // already include 2-byte Fletcherchksum + 8-byte IMEI
UINT8 connId = UDP_MSG_SOC; // default to pui msg server
if (atCommand->type == AT_CMD_UDP_DMAN)
{
connId = UDP_DMAN_SOC;
}
else
{
#if defined(PLATFORM_TALISKER_PRO) || defined(PLATFORM_TALISKER_SPORT) || defined(PLATFORM_TALISKER_OBD)
if (!testPuiLoc) {
connId = UDP_TALISKER_SOC;
}
#endif
}
// Send data in command mode (see chip's AT command spec for details)
// at#ssendext=<connId>,<num bytes>,<carrage return>
// TO DO: make this part cell-chip-specific
if (!(sendState & XFER_SENT_P1))
{
char sendStr[32];
// Tell the cell how many bytes to send (cmdLen + 2byte checksum + 16byte imei)
memset((char*)sendStr
, 0, 32);
sprintf((char*)sendStr
, "%s%d,%d%s", AT_SEND
, connId
, cmdLen
, RETURN
);
//printf("Sendp1: %s\n", (char*)sendStr);
cellWrite
((UINT8
*)sendStr
, strlen((char*)sendStr
));
sendState |= XFER_SENT_P1;
waitCount(1); // gives it 10ms to respond
rtn = true;
}
else
{
if (sendState & XFER_SENT_P1)
{
cellWrite((UINT8*)atCommand->cmd, cmdLen);
sendState &= ~XFER_SENT_P1;
sendState |= XFER_SENT_P2;
//DBG(D_CELL, "Sendp2: cmdLen=%d time=%.3f\n", cmdLen, utilsAge());
Dump_Hex_Data((UINT8*)atCommand->cmd, cmdLen);
}
}
}
else if (atCommand->type == AT_CMD_SMS)
{
// Send SMS messages (see cell AT command spec for details)
// TO DO: make this part cell-chip-specific
memset((char*)tmpStr
, 0, sizeof(tmpStr
));
if (strlen(atCommand
->extra
) != 0)
{
if (!(sendState & XFER_SENT_P1))
{
// ATcmd + destAddr + Carriage Return
sprintf((char*)tmpStr
, "%s%s%s", AT_SMS
, (char*)atCommand
->cmd
, RETURN
);
//DBG(D_CELL, "SMSp1: len=%d\n", strlen(tmpStr));
//Dump_Hex_Data((UINT8*)tmpStr,strlen(tmpStr));
cellWrite
((UINT8
*)tmpStr
, strlen((char*)tmpStr
));
sendState |= XFER_SENT_P1;
waitCount(1); // gives it 10ms to respond
rtn = true;
}
else // must get it by now, more check?
{
if (sendState & XFER_SENT_P1)
{
memset((char*)tmpStr
, 0, MAX_TMP_STR_LEN
);
sprintf((char*)tmpStr
, "%s%s", (char*)atCommand
->extra
, CTRL_Z
);
cellWrite
((UINT8
*)tmpStr
, strlen((char*)tmpStr
));
//DBG(D_CELL, "SMSp2: len=%d\n", strlen((char*)atCommand->extra));
//Dump_Hex_Data((UINT8*)tmpStr,strlen((char*)atCommand->extra));
sendState &= ~XFER_SENT_P1;
sendState |= XFER_SENT_P2;
}
}
} else {
// So, if the atCommand->type is AT_CMD_SMS BUT
// there is nothing in the extra string
// WE DO NOTHING????
// Just leave the command at the front of the queue FOREVER?
//
// NO!!! log an error and dump the command!!!!
int idx = deQ(&cell_atBuffer); // Upper levels must retry
atCmdInProgress = false;
if(idx < 0) {
printf("%s, AT queue unexpectedly empty! (3)\n",__func__
);
return;
}
atCmdType *lastCmd = &cell_atBuffer.s.arr[idx];
lastCmd->type = 0;
lastCmd->sent = 0;
printf("%s, ERROR -- SMS extra is empty -- cmd ignored!\n",__func__
);
}
}
else
{
// To support "sendat" with AT# command, use "sendat,AT#<cmdname>"
memset((char*)tmpStr
, 0, sizeof(tmpStr
));
if (((strstr((char*)atCommand
->cmd
, "AT") == NULL
) &&
(strstr((char*)atCommand
->cmd
, "at") == NULL
)) ||
(strstr((char*)atCommand
->cmd
, "CGDCONT") != NULL
)) // pdpcontext cmd from settings
{
sprintf((char*)tmpStr
, "AT+%s%s", (char*)atCommand
->cmd
, RETURN
);
}
else
{
sprintf((char*)tmpStr
, "%s%s", (char*)atCommand
->cmd
, RETURN
);
}
cellWrite
((UINT8
*)tmpStr
, strlen((char*)tmpStr
));
}
}
return rtn;
}
static UINT8 atFaultCount = 0; // count of consecutive failed at commands
static UINT8 mdmCycleCount = 0; // count of consecutive modem cycles
static UINT8 mdmShdnCount = 0; // count of consecutive shutdown cycles
static UINT8 mdmPwrCount = 0; // count of consecutive power cycles
static UINT8 sktFaultCount = 0; // count of consecutive socket faults
static UINT8 sktCycleCount = 0; // count of consecutive modem cycles
static UINT8 sktShdnCount = 0; // count of consecutive shutdown cycles
static UINT8 sktPwrCount = 0; // count of consecutive power cycles
/**
* Clear ALL of the AT command fault counters.
*/
void clrMdmFaults(void) {
atFaultCount = 0;
mdmCycleCount = 0;
mdmShdnCount = 0;
mdmPwrCount = 0;
}
/**
* Pulse the modem cycle pin with the specified pulse width
*/
static void mdmCyclePulse(UINT32 duration)
{
Mdm_On_Off_Active();
HAL_Delay(duration);
Mdm_On_Off_Inactive();
HAL_Delay(1000);
}
/**
* Check for modem faults -- take various modem recovery actions:
* restarting (cycling), resetting, pulsing power, and
* ultimately resetting the system.
*/
void monitorModemFaults(void)
{
if(atFaultCount >= MAX_AT_FAULTS) {
mdmCycleCount++;
atFaultCount = 0;
if(mdmCycleCount < MAX_MODEM_CYCLES) {
printf("MODEM AT cmd fault count reached maximum!\n");
mdmCyclePulse(3000);
mdmCyclePulse(5000);
} else {
mdmShdnCount++;
mdmCycleCount = 0;
if(mdmShdnCount < MAX_MODEM_SHDNS) {
printf("MODEM cycle count reached maximum!\n");
Mdm_Hw_Shdn_Active();
HAL_Delay(1000);
Mdm_Hw_Shdn_Inactive();
} else {
mdmPwrCount++;
mdmShdnCount = 0;
if(mdmPwrCount < MAX_MODEM_PWRS) {
printf("MODEM shutdown cycle count reached maximum!\n");
Mdm_Pwr_Disable();
HAL_Delay(1000);
Mdm_Pwr_Enable();
} else {
printf("MODEM power cycle count reached maximum\n");
printf("RESET THE SYSTEM -- need to add code here!\n");
cellControl(CELL_CTRL_OFF);
HAL_NVIC_SystemReset();
}
}
}
}
}
/**
* Increment the socket fault counter
* This counter is used by: monitorSocketFaults() to take
* various recovery actions.
*/
void incSocketFault(void)
{
sktFaultCount++;
}
/**
* Clear ALL of the socket fault counters.
*/
void clrSocketFaults(void)
{
sktFaultCount = 0;
sktCycleCount = 0;
sktShdnCount = 0;
sktPwrCount = 0;
}
/**
* Monitor socket faults -- take various modem recovery actions:
* restarting (cycling), resetting, pulsing power, and
* ultimately resetting the system.
*/
void monitorSocketFaults(void)
{
if(sktFaultCount >= MAX_SOCKET_FAULTS) {
sktCycleCount++;
sktFaultCount = 0;
if(sktCycleCount < MAX_MODEM_CYCLES) {
printf("SOCKET fault count reached maximum!\n");
mdmCyclePulse(3000);
mdmCyclePulse(5000);
} else {
sktShdnCount++;
sktCycleCount = 0;
if(sktShdnCount < MAX_MODEM_SHDNS) {
printf("SOCKET cycle count reached maximum!\n");
Mdm_Hw_Shdn_Active();
HAL_Delay(1000);
Mdm_Hw_Shdn_Inactive();
} else {
sktPwrCount++;
sktShdnCount = 0;
if(sktPwrCount < MAX_MODEM_PWRS) {
printf("SOCKET shutdown cycle count reached maximum!\n");
Mdm_Pwr_Disable();
HAL_Delay(1000);
Mdm_Pwr_Enable();
} else {
printf("SOCKET power cycle count reached maximum\n");
printf("RESET THE SYSTEM -- need to add code here!\n");
cellControl(CELL_CTRL_OFF);
HAL_NVIC_SystemReset();
}
}
}
}
}
/**
* Send AT command to module and add to trace.
*/
void cell_sendAtCmd(char *cmd, UINT8 type, UINT16 len, char *extra, char *whence)
{
#define SHOW_AT_CMDS
#ifdef SHOW_AT_CMDS
{
static UINT8 firstTime= 1;
if(firstTime) {
UINT32 nowms = (UINT32)getSystemTime();
int ii;
printf("%s(%d.%03d)[%s], %d: ", __func__
, nowms
/ 1000,
nowms % 1000, whence, type);
//printf("%s(%d)[%s], %d: ", __func__, nowms, whence, type);
for(ii=0;ii<len;ii++) {
UINT8 chr = cmd[ii];
if((chr
< 0x20) || (chr
> 0x7F)) printf("\\%02X",chr
);
}
if(extra
!= NULL
) printf("extra: %s\n",extra
);
}
}
#endif // SHOW_AT_CMDS
//DBG(D_CELL, "%s type %d len=%d\n", __FUNCTION__, type, len);
if (cmd) {
// force comm wakeup to send at message
if (main_state == STATE_SLEEPING) {
main_setState(STATE_STOPPED);
}
// check cellState before turning it on since no flow control CTS/RTS
//cellControl(CELL_CTRL_ON);
// Get end slot of the cmd queue
INT8 rear = enQ(&cell_atBuffer);
if (rear >= 0) {
atCmdType *atCmd = (atCmdType*)&cell_atBuffer.s.arr[rear];
// Fill out ATcmd struct
if (atCmd) {
atCmd->sent = false;
memcpy((char*)atCmd
->cmd
, cmd
, len
);
atCmd->cmdLen = len;
if (type)
atCmd->type = type;
else
atCmd->type = AT_CMD_CUSTOM;
if (extra) {
memset(atCmd
->extra
, 0, sizeof(atCmd
->extra
));
}
} else {
DBG(D_CELL, "No resource for AT cmd (%s)\n", cmd);
printf("No resource for AT cmd (%s)\n", cmd
);
return;
}
//DBG(D_CELL, "NewCmd: addr=0x%x Qrear=%d Qfront=%d Qcount=%d cmdType=%d len=%d extra=%s\n\n",
// (UINT32)atCmd, cell_atBuffer.rear, cell_atBuffer.front, cell_atBuffer.count,
// atCmd->type, atCmd->cmdLen, atCmd->extra);
//Dump_Hex_Data((UINT8*)atCmd->cmd, atCmd->cmdLen);
}
atCmdInProgress = true;
if(cell_sendNextAtCommand(NULL)) myHandleCharForCell(__func__);
//#define NO_csNAC_RETRY // csNAC: cell_sendNextAtCommand
#ifdef NO_csNAC_RETRY
while(atCmdInProgress) handleCharForCell();
#else // NO_csNAC_RETRY
{
UINT32 startTime = HAL_GetTick();
int atRetryCount = AT_MAX_RETRIES;
while(atCmdInProgress) {
if((HAL_GetTick() - startTime) > AT_MAX_TIME) {
if(--atRetryCount > 0) {
startTime = HAL_GetTick();
cell_sendNextAtCommand(NULL); // re-issue the AT command
} else {
int items= howQ(&cell_atBuffer);
printf("\n\nAT cmd: %s(%s) FAILED!\n\n",whence
,cmd
);
if(atFaultCount < MAX_AT_FAULTS) atFaultCount++;
if(items != 1) {
printf("UNEXPECTED queue'd 'AT' cmds! (%d > 1)\n",items
);
}
deQ(&cell_atBuffer);
break;
}
}
handleCharForCell();
}
}
if(!atCmdInProgress) clrMdmFaults();
atCmdInProgress = false;
#endif // NO_csNAC_RETRY
}
}
/**
* @public
* @param {string} cmdString (multiple AT cmd separated by ;)
* NOTE: string may not exceed 256 bytes.
*/
void cell_sendCmds(char *cmdString) {
int ii;
int start = 0;
int len = 0;
printf("%s, cmdString: %s\n",__func__
,cmdString
); //debug
for(ii=0;ii<256;ii++) {
char chr= cmdString[ii];
if((chr == 0) || (chr == ';')) {
if(len > 0) {
cell_sendAtCmd(&cmdString[start], 0, len, 0, __func__);
}
if(chr == 0) break;
len = 0;
start = ii + 1;
} else {
len++;
}
}
}
/**
* Send an SMS.
*
* @param {string} to
* @param {string} txt
*/
void cell_sendSms(char *to, char *txt) {
char *ptr = txt;
UINT16 index
= 0, txtLen
= strlen(txt
);
DBG(D_CELL, "%s: tLen=%d TO=%s TEXT=%s\n", __FUNCTION__, txtLen, to, txt);
do {
ptr += index;
cell_sendAtCmd
(to
, AT_CMD_SMS
, (strlen(to
) + strlen((char*)AT_SMS
)), ptr
, __func__
);
index += MAX_SMS_LEN;
} while (index < txtLen);
}
/**
* Called to get new RSSI and Clock data from Comm Module.
*/
void cell_updateInfo(void) {
if (cell_simStoreReady) {
cell_sendAtCmd(CSQ, 0, 3, NULL, __func__);
cell_sendAtCmd(CCLK_QUERRY, 0, 5, NULL, __func__);
cell_sendAtCmd("COPS?", 0, 5, NULL, __func__);
if (cellLogic_msisdn == 0) {
cell_sendAtCmd(CNUM, 0, 4, NULL, __func__);
}
cell_sendAtCmd("CREG?", 0, 5, NULL, __func__);
if (networkType == -1 || cellLogic_mccmnc == 0 || netTypePollCount-- <= 0) {
netTypePollCount = NETWORK_TYPE_POLL;
cell_sendAtCmd(AT_RFSTS, 0, 8, NULL, __func__);
cell_sendAtCmd(AT_PSNT, 0, 8, NULL, __func__);
}
}
}
// Init the pre-allocated pool of AT cmd
// Allow Max 32 outstanding cmds
void initAtCmdPool(void) {
UINT8 i;
memset((char*)&atCmdPool
[0],0, sizeof(atCmdPool
));
memset((char*)&dataPool
[0],0, sizeof(dataPool
));
DBG(D_CELL, "Init atCmdPool at 0x%x dataPool at 0x%x\n",
(UINT32)&atCmdPool[0], (UINT32)&dataPool[0]);
for (i = 0; i < MAX_NUM_AT_CMD; i++) {
atCmdPool[i].cmdLen = 0;
atCmdPool[i].inUse = false;
atCmdPool[i].cmd = (cmdDataType*)&dataPool[i];
}
}
void cell_enableNetworkRegReport(void) {
// Check SIM type and issue CREG or CEREG accordingly
// NOTE: AT+CEREG AT command returns the EPS registration status.
// For now, check board and infer SIM type
// TODO: find out how to differentiate SIM type on the same board
//
#if defined(PLATFORM_TALISKER_PRO) || defined(PLATFORM_TALISKER_SPORT) || defined(PLATFORM_TALISKER_OBD)
//////////////////////////// Required to detect Network Detach
cell_sendCmds("CEREG=2");
cell_sendCmds("CGREG=2");
cell_sendCmds("CREG=2");
////////////////////////////////////////////////////////////////
#else
cell_sendCmds("CEREG=2"); // FJ2000 uses AT&T/Verizon SIM which supports CEREG
#endif
HAL_Delay(100);
cell_sendCmds("AT#MTUSIZE=1024"); // Possible values of MTU size, only if cell fw >= 20.00.524
// 0 = Default MTU size used by network operator
}
void cellReadyHandler(void) {
//DBG(D_CELL, "%s\n", __FUNCTION__);
if (cell_simStoreReady) {
DBG(D_CELL, "Send cmds in Q\n");
if(cell_sendNextAtCommand(NULL)) myHandleCharForCell(__func__);
} else {
cell_simStoreReady = true;
DBG(D_CELL, "Set cell_simStoreReady, send initial ATcmds at %.3f\n", utilsAge());
cell_enableNetworkRegReport();
HAL_Delay(100);
cell_sendCmds("CGSN;CGMR;CNUM;CIMI;CMGF=1;CCID;CSQ");
HAL_Delay(100);
cell_sendCmds("CGEREP=2");
}
}
void initCell(void) {
// Create queue for AT cmd
initAtCmdPool();
initCellLogic();
initQ(QTYPE_AT, &cell_atBuffer, MAX_NUM_AT_CMD, (void*)&atCmdPool[0]);
cell_setDmanServer(settings[DMAN_REPORT_SERVER].u.ptr);
}
/**
* Cause Comm Module to wake.
*/
void cell_wake(void) {
if (settings[POWER_SAVINGS_MODE].u.v.iVal & PWR_MODE_COMM_TURN_OFF) {
bool pwrStateGood = false;
cellLogic_commChangeAge = utilsAge();
cellControl(CELL_CTRL_ON); // no sleep/reset, just power on/off - turn on 5ms from now?
pwrStateGood = cellControl(CELL_CTRL_CHK_PWR); // no sleep/reset, just power on/off - turn on 5ms from now?
DBG(D_CELL, "%s pwrStateGood = %d\n", __FUNCTION__, pwrStateGood);
}
}
/**
* Comm module to sleep if appropriate
*/
void cell_sleep(void) {
if (settings[POWER_SAVINGS_MODE].u.v.iVal & PWR_MODE_COMM_LOW) {
// comm sleep RXM
if (utilsAge() - cellLogic_lastATRcvd > 3) {
//DBG(D_CELL, "Sleep: Turn cell low at %.3f\n", utilsAge());
//cellControl(CELL_CTRL_OFF); //no sleep/reset, just off?
}
}
if (settings[POWER_SAVINGS_MODE].u.v.iVal & PWR_MODE_COMM_TURN_OFF) {
// comm sleep OFF
DBG(D_CELL, "Sleep: Turn cell off at %.3f\n", utilsAge());
cellControl(CELL_CTRL_OFF);
cell_setIsRegistered(false);
cell_simStoreReady = false;
}
}
/**
* Power off/on the Comm Module.
*/
void cell_reset(void) {
DBG(D_CELL, "%s at %.3f\n", __FUNCTION__, utilsAge());
cell_setIsRegistered(false);
cell_simStoreReady = false;
cellLogic_commChangeAge = utilsAge();
cellControl(CELL_CTRL_OFF);
cellControl(CELL_CTRL_ON);
}
/**
* Get the length of a DMAN update segment
*/
UINT32 cell_segmentLength(void) {
//DBG(D_CELL, "%s at %.3f\n", __FUNCTION__, utilsAge());
return segmentLength;
}
/**
* Properly initialize modem based upon carrier
*
* @param {boolean} force
*/
void cell_handleDataSetup (bool force) {
char tmp[64];
//DBG(D_CELL, "%s\n", __FUNCTION__);
// Wait for cell/gps good
if (!(cellCheckOpState() & CELL_INIT_DONE)) {
DBG(D_CELL, "Cell Init not done, defer dataSetup cellState=0x%x\n",
cellCheckOpState());
return; // Wait state is cleared when the ATcmd to setup returns
}
if (strlen((char*)cellLogic_iccid
) != 0) {
if (force || (!cellLogic_isCommAvailable && cell_getIsRegistered() && cell_simStoreReady)) {
cell_sendCmds(settings[CUSTOM_INIT_AT_CMDS].u.ptr); // define PDP context
UINT8 activePDP = 1; // default - check SIM mfg for PDPcontext: Verizon = 3, others = 1
char *strPtr = NULL;
if (strncmp((char*)&cellLogic_iccid
[2], "148", 3) == NULL
) {
activePDP = 3;
cell_sendAtCmd("CNMI=3,2,0,0", 0, 12, NULL, __func__); // buffer unsolicited result code
} else {
cell_sendAtCmd("CNMI=2,2,0,0", 0, 12, NULL, __func__); // hw ring line 1s when sms is received
}
UINT8 socketToMsgServer = UDP_MSG_SOC; // default to pui msg server
#if defined(PLATFORM_TALISKER_PRO) || defined(PLATFORM_TALISKER_SPORT) || defined(PLATFORM_TALISKER_OBD)
if (!testPuiLoc) {
socketToMsgServer = UDP_TALISKER_SOC;
}
#endif
cell_closeSocket(UDP_DMAN_SOC);
cell_closeSocket(socketToMsgServer);
cellLogic_handleCommChange(true);
// <PDPcontext>,1 (means activate)
memset((char*)tmp
, 0, sizeof(tmp
));
sprintf((char*)tmp
, "%s%d,1", AT_PDPACT
, activePDP
);
cell_sendAtCmd
((char*)tmp
, 0, strlen((char*)tmp
), NULL
, __func__
);
// socket 1,<PDPcontext>, packetSize 1500
memset((char*)tmp
, 0, sizeof(tmp
));
sprintf((char*)tmp
, "%s%d,%d,1500,0,600,50", AT_SOC_CFG
, socketToMsgServer
, activePDP
);
cell_sendAtCmd
((char*)tmp
, 0, strlen((char*)tmp
), NULL
, __func__
);
// socket 2,<PDPcontext>, packetSize 1500
memset((char*)tmp
, 0, sizeof(tmp
));
sprintf((char*)tmp
, "%s%d,%d,1500,0,600,50", AT_SOC_CFG
, UDP_DMAN_SOC
, activePDP
);
cell_sendAtCmd
((char*)tmp
, 0, strlen((char*)tmp
), NULL
, __func__
);
// socket 1,
// 2=Data view: SRING: <connId>,<receivedDataLen>,<data>
// 1= Receive data in hex mode (0= Receive data in text mode - normal mode)
// 0, 0 = no keep alive, no listen auto-resp
// 1= Send data with #SSEND in hex mode
memset((char*)tmp
, 0, sizeof(tmp
));
sprintf((char*)tmp
, "%s%d,2,0,0,0,1", AT_SOC_CFGEXT
, socketToMsgServer
);
cell_sendAtCmd
((char*)tmp
, 0, strlen((char*)tmp
), NULL
, __func__
);
memset((char*)tmp
, 0, sizeof(tmp
));
sprintf((char*)tmp
, "%s%d,2,0,0,0,1", AT_SOC_CFGEXT
, UDP_DMAN_SOC
);
cell_sendAtCmd
((char*)tmp
, 0, strlen((char*)tmp
), NULL
, __func__
);
}
} else {
DBG(D_CELL, "%s do nothing cell iccid=%s\n", __FUNCTION__, cellLogic_iccid);
}
}
void cell_closeSocket(UINT8 connId) {
char tmp[40];
DBG(D_CELL, "%s: %d\n", __FUNCTION__, connId);
sprintf((char*)tmp
, "%s%d", AT_SOC_SH
, connId
);
cell_sendAtCmd
((char*)tmp
, 0, strlen((char*)tmp
), NULL
, __func__
);
if (connId == UDP_DMAN_SOC)
udpDmanSocketOpen = false;
else if (connId == UDP_MSG_SOC)
udpMsgSocketOpen = false;
else
udpTalSocketOpen = false;
}
void cell_openSocket(UINT8 connId, char *serverIp, UINT32 port) {
char tmp[40];
UINT8 localPort;
// If already opened, return
if (((connId == UDP_DMAN_SOC) && udpDmanSocketOpen) ||
((connId == UDP_MSG_SOC) && udpMsgSocketOpen) ||
((connId == UDP_TALISKER_SOC) && udpTalSocketOpen))
return;
if (connId == UDP_MSG_SOC)
localPort = 198; // arbitrary
else if (connId == UDP_DMAN_SOC)
localPort = 199; // arbitrary
else
localPort = 200; // arbitrary for connection to Talisker server
DBG(D_CELL, "%s: %d port %d\n", __FUNCTION__, connId, port);
memset((char*)tmp
, 0, sizeof(tmp
));
sprintf((char*)tmp
, "%s%d,1,%d,%s,0,%d,1", AT_SOC_OPEN
, connId
, port
, serverIp
, localPort
);
DBG(D_CELL, "%s\n",tmp);
cell_sendAtCmd
(tmp
, 0, strlen((char*)tmp
), NULL
, __func__
);
if (connId == UDP_DMAN_SOC)
udpDmanSocketOpen = true;
else if (connId == UDP_MSG_SOC)
udpMsgSocketOpen = true;
else
udpTalSocketOpen = true;
}
/**
* Send a UDP packet. Where type = UDP Server, either Location or PASS
*
* @param {Uint8Array} data
* @param {number} type
*/
void cell_sendUdp(UINT8 *data, UINT8 type, UINT32 len) {
UINT8 socketToMsgServer = UDP_MSG_SOC; // default to pui msg server
#if defined(PLATFORM_TALISKER_PRO) || defined(PLATFORM_TALISKER_SPORT) || defined(PLATFORM_TALISKER_OBD)
if (!testPuiLoc) {
socketToMsgServer = UDP_TALISKER_SOC;
}
#endif
if (type == AT_CMD_UDP_DMAN) {
if (!udpDmanSocketOpen)
cell_configUdp(UDP_DMAN_SOC, settings[DMAN_REPORT_SERVER].u.ptr,
settings[DMAN_REPORT_PORT].u.v.iVal);
}
else if (type == AT_CMD_UDP_MESSAGE) {
if (!udpMsgSocketOpen)
cell_configUdp(socketToMsgServer, settings[MESSAGES_REPORT_SERVER].u.ptr,
settings[MESSAGES_REPORT_PORT].u.v.iVal);
}
HAL_Delay(100);
// Append the payload to Fletcher+IMEI here to support legacy pui msg with Talisker
char calc[2] = {0, 0};
memset((char*)tmpStr
, 0, MAX_TMP_STR_LEN
);
memcpy((char*)tmpStr
+2, (char*)imeiBuffer
, 8); // imeiHex string
memcpy((char*)(tmpStr
+2+8), (char*)data
, len
); // udp data
calcFletcher((char*)(tmpStr+2), len+8, (char*)calc);
memcpy((char*)tmpStr
, (char*)calc
, 2); // insert Fletcher checksum
// Now if it is loc msg, and it is Talisker, wrap the Talisker header/CRC
UINT16 totalLen = len + 2 + 8; // 2-byte Fletcherchksum + 8-byte IMEI
#if defined(PLATFORM_TALISKER_PRO) || defined(PLATFORM_TALISKER_SPORT) || defined(PLATFORM_TALISKER_OBD)
if (type == AT_CMD_UDP_MESSAGE) {
cell_sendAtCmd((char*)data, type, len, NULL, __func__);
} else {
cell_sendAtCmd((char*)tmpStr, type, totalLen, NULL, __func__);
}
#else
cell_sendAtCmd((char*)tmpStr, type, totalLen, NULL, __func__);
#endif
}
//
// Looking for all SRING and process the data associated with them
//
void cellParseData(UINT8 *data, UINT16 length) {
char *ptr = NULL;
UINT16 dataLen = length;
printf("debug Parse, len=%d:\n", length
);
Dump_Hex_Data((UINT8*)data, 20);
ptr = data;
while (ptr) {
UINT16 readLen = 0;
UINT8 socketId;
char *dataPtr, *tok1, *tok2;
findToken(1, ptr+7, dataLen, &tok1, ',');
findToken(2, ptr+7, dataLen, &tok2, ',');
socketId = *tok1 - 0x30;
readLen
= (UINT16
)strtol(tok2
, NULL
, 10);
DBG(D_CELL, "SRING dataLen=%d socket=%d readLen=%d\n", dataLen, socketId, readLen);
//Dump_Hex_Data((UINT8*)data, 256);
if (readLen > 0) {
//
// Get the 3rd token which is data bcause we set srMode=2 in SCFGEXT
// which present the data as SRING: <socketId>,<readLen>,<data>
//
if (findToken(3, ptr, dataLen, &dataPtr, ',')) {
if (socketId == UDP_MSG_SOC) {
#if defined(PLATFORM_TALISKER_PRO) || defined(PLATFORM_TALISKER_OBD)
if (Send_Msg(Get_Dei_Net_Q(), dataPtr, readLen) < 0) {
printf("%s() Send_Msg() %d bytes failed\n", __func__
, readLen
);
}
#endif
} else {
if ((socketId == UDP_DMAN_SOC) && (readLen >= MAX_SEGMENT_LENGTH)) {
cellLogic_handleData((UINT8*)dataPtr, readLen);
return; // can't have more data
} else
cellLogic_handleData((UINT8*)dataPtr, readLen);
}
} else {
printf("Parse error, can't find data in SRING\n");
break;
}
} else {
printf("Error: dataLen= 0 for SRING\n");
break;
}
ptr
= strstr((char*)dataPtr
+readLen
, "SRING"); // find next SRING
dataLen = length - (14 + readLen); // SRING: x,yyyy,<readLen>
} // while
}
// Data comes from USARTx (hw platform dependent)
void cellHandler(UINT8 *data, UINT16 length) {
data[length] = 0;
// Check data comming in from serial port
static char cmd[256] = {0, };
FLOAT lastDate = 0;
FLOAT cell_lastAtRecDate = 0;
if (utilsAge() == lastDate) {
DBG(D_CELL, "Set system clock - NO OP\n");
// M=16, N=336, P=4, Q=7, latency=2, clkDiv1=2, clkDiv2=4
// See "Supported STM32F401xx devices" in targetlibs/stm32f4/lib/system_stm32f4xx.c
//sysClockType c = {16, 336, 4, 7, 2, 2, 4};
//setSystemClock(&c);
}
lastDate = utilsAge();
cellLogic_lastATRcvd = utilsAge();
// Prompt
if (length == 2) {
//printf("0x%x i0x%x\n", data[0], data[1]);
//if(cell_sendNextAtCommand(NULL)) myHandleCharForCell(__func__); // this is recursive!
cell_sendNextAtCommand(NULL); // this is not recursive!
} else if (length > 2) { // ignore lines with just \r\n
char values[4*MAX_TOKEN_SIZE] = {0,}; // 16-byte each val, allow 4 tokens
char *sringPtr;
// UDP data from cell
if ((sringPtr
= strstr((char*)data
, "SRING")) != NULL
) {
#ifdef REMOVE_REDUNDANT_OK_ERROR_CHECK
// If there's ok or error mixed in process them first
char *str
= strstr((char*)data
, "OK");
if (str != NULL) {
printf("<< OK found with SRING >>\n"); //debug
if(str < sringPtr) {
if(cell_sendNextAtCommand("OK")) myHandleCharForCell(__func__);
// AND sellParseData() will need to process
// any chars after the SRING block (if any)
}
} else {
char *str
= strstr((char*)data
, "ERROR");
if (str != NULL) {
printf("<< ERROR found with SRING >>\n"); //debug
if(str < sringPtr) {
if(cell_sendNextAtCommand("ERROR")) myHandleCharForCell(__func__);
}
}
}
#endif
cellParseData(sringPtr, length);
/*
NOW, IF cellParseData() IS MODIFIED TO RETURN A POINTER TO
THE REMAINDER OF data (IF ANY) THEN THE FOLLOWING } else {
CAN BE USED TO PROCESS IT. (NOTE -- THIS IGNORES THE
POSSIBILITY OF 2 OR MORE SRINGS IN THE BUFFER!!! -- there
should be only one unless the first SRING is damaged.)
*/
} else { // command response
if (cell_match(data, 0, length, "OK")) {
//#define ENABLE_RECURSION
#ifdef ENABLE_RECURSION
if(cell_sendNextAtCommand("OK")) myHandleCharForCell(__func__); // this is recursive!
#else
(void)cell_sendNextAtCommand("OK"); // this is not recursive!
#endif // ENABLE_RECURSION
} else if (cell_match(data, 0, length, "ERROR") || cell_match(data, 5, length, "ERROR")) {
#ifdef ENABLE_RECURSION
if(cell_sendNextAtCommand("ERROR")) myHandleCharForCell(__func__); // this is recursive!
#else
(void)cell_sendNextAtCommand("ERROR"); // this is not recursive!
#endif // ENABLE_RECURSION
} else {
char *dummy = NULL;
// strip off 2 CR and 1 LF
memset((char*)&cmd
[0], 0, sizeof(cmd
));
arrToStr(data, 0, (UINT8)(length - 3), &cmd[0]);
if (!cell_match(data, 1, length, CCLK) && !cell_match(data, 1, length, CSQ) &&
!cell_match(data, 3, length, CCLK) && !cell_match(data, 3, length, CSQ)) {
strncpy(&cell_lastResponse
[0], &cmd
[0], MAX_RESP_LEN
);
//printf("-> %s\n", (char*)&cmd[0]);
Dump_Hex_Data(data, length);
//printf("cellHandler debug:\n");
//Dump_Hex_Data((UINT8*)&cmd[0], length);
}
if (cell_match(data, 1, length, CMT)) {
char smsFromTemp[SMS_NUMBER_LEN+1]; // extra char for the "+"
// LE910: +CMT: "xxx","","18/03/08,16:45:08-32" - xxx is the src phone#
getStrTokens(&cmd[6], (char*)&values[0], ",");
if (getSubStrInQuote((char*)smsFromTemp, (char*)&values[0])) {
strncpy((char*)cell_smsFrom
, (char*)(smsFromTemp
+1), sizeof(cell_smsFrom
)-1);
if (strlen((char*)cell_smsFrom
) > 0) {
hasSmsSrc = true;
}
DBG(D_CELL, "Got CMT/SMS from %s\n", (char*)cell_smsFrom);
}
} else if (hasSmsSrc && !(sendState & XFER_SENT_P2)) {
DBG(D_CELL, "SMS text:\n");
hasSmsSrc = false;
Dump_Hex_Data
((UINT8
*)cmd
, strlen((char*)cmd
));
cellLogic_handleSMS((char*)cmd, (char*)cell_smsFrom);
memset((char*)cell_smsFrom
, 0, sizeof(cell_smsFrom
));
} else if (cell_match(data, 1, length, CCID)) {
cellState |= CELL_GOOD_CCID;
getSubStr(cellLogic_iccid, (char*)cmd, 7, length); // check
DBG(D_CELL, " cellLogic_ccid=%s\n", (char*)cellLogic_iccid);
cell_handleDataSetup(false);
// Sometimes data from cell is mangled, prefixed by some other cmd resp
// so, can't count from a fixed beginging, just look for the string
} else if (((dummy
= strstr((char*)data
, "+CREG")) != NULL
)) {
UINT8 state, cnt;
UINT8 baseIdx;
// cell returns <mode>,<stat>[,<Lac>,<Ci>[,<Act>]] split into multiple arrays
cnt = getStrTokens(dummy+7, (char*)&values[0], ",");
if (!(cnt
== 1 && strlen(&values
[0]) == 2 && values
[0] == 0x0d && values
[1] == 0x0a)) {
if (cnt == 1 || (cnt > 1 && values[1*MAX_TOKEN_SIZE] == '"')) {
// CREG requested with AT+CREG?
baseIdx = 0;
} else {
// CREG async report from AT+CREG=2
baseIdx = 1;
}
state
= (UINT32
)strtol(&values
[baseIdx
*MAX_TOKEN_SIZE
], NULL
, 10);
DBG(D_CELL, "Got CREG state=%d cnt=%d base=%d\n", state, cnt, baseIdx);
bool prevRegState = cell_getIsRegistered();
if (state == 5) { // 5 = registered, roaming
cell_isRoaming = true;
} else {
cell_isRoaming = false;
}
if((state == 1) || cell_isRoaming) { // 1= registerd home network
cell_setIsRegistered(true);
} else {
cell_setIsRegistered(false);
}
if (cnt > (baseIdx+3)) {
char str[16];
if (getSubStrInQuote((char*)str, (char*)&values[(baseIdx+1)*MAX_TOKEN_SIZE])) {
cellLogic_localAreaCode
= (UINT32
)strtol((char*)str
, NULL
, 16);
}
if (getSubStrInQuote((char*)str, (char*)&values[(baseIdx+2)*MAX_TOKEN_SIZE])) {
cellLogic_setCellId
((UINT32
)strtol((char*)str
, NULL
, 16));
}
DBG(D_CELL, "cellId=%lx AreadCode=%lx\n", cellLogic_getCellId(), cellLogic_localAreaCode);
}
if (cell_getIsRegistered() != prevRegState) {
// A change in the registration state
if (cell_getIsRegistered() && cell_getIsRegistered() != prevRegState) { // registered or roaming
cellState |= CELL_GOOD_CEREG;
cell_handleDataSetup(false);
} else {
cellLogic_handleCommChange(false);
}
}
}
} else if (cell_match(data, 1, length, CSQ)) {
INT8 value;
UINT8 tmp[4];
getSubStr((char*)tmp, (char*)cmd, 6, 7); // +CSQ: 28,4 (which is rssi,ber)
value
= (INT8
)strtol((char*)tmp
, NULL
, 10);
if (value < 32) {
value = -113 + value * 2;
} else {
value = -115;
}
if (cellLogic_isCommAvailable) {
cellLogic_setRssi(value);
} else {
cellLogic_setRssi(-115);
}
//DBG(D_CELL, " got CSQ - cell value=%d rssi=%d isCommAVail=%d\n",
// value, cellLogic_rssi, cellLogic_isCommAvailable);
// the last init cmd, if setup was defered, go it now
if (cellCheckOpState() & CELL_WAIT_SETUP)
cell_handleDataSetup(false);
} else if (cell_match(data, 1, length, "CNUM")) { // +CNUM: "","12345678..",123
char cnum[MAX_TOKEN_SIZE];
INT16 idx = 0;
getStrTokens((char*)cmd, (char*)values, ",");
memset(cnum
, 0, MAX_TOKEN_SIZE
);
if (getSubStrInQuote(cnum, (char*)&values[1*MAX_TOKEN_SIZE])) {
if ((idx = getCharIdx(cnum, '+')) != -1) {
cellLogic_msisdn = parseInt(cnum+idx+1, 10);
} else {
cellLogic_msisdn = parseInt(cnum, 10);
}
strcpy((char*)cell_msidnPrint
, cnum
);
DBG(D_CELL, "Got CNUM msisdn=%s\n", cell_msidnPrint);
}
} else if (cell_match(data, 1, length, "RFSTS")) { // #RFSTS: "310 410",...
// RFSTS (Read current network status) provides MCC, MNC and Network Name
char mccmnc[MAX_TOKEN_SIZE];
if (getSubStrInQuote(mccmnc, cmd)) {
remChar(mccmnc, ' ');
cellLogic_mccmnc = parseInt(mccmnc, 10);
DBG(D_CELL, "Got mccmnc=%lu\n", cellLogic_mccmnc);
}
#if 0
// Get the operator name (string)
char *tok1, *tok2;
if (findToken(17, (char *)(data+7), length, &tok1, ',')
&& findToken(18, (char *)(data+7), length, &tok2, ',')) {
tok2--; // before the delimeter
if (*tok1 == '"') {
tok1++; // remove leading quote
tok2--; // remove trailing quote
}
INT16 size = (tok2 - tok1);
if (size > 32) {
size = 32; // limit operator name length
}
operatorName[size] = 0;
DBG(D_CELL, "Got operator=%s\n", operatorName);
} else {
DBG(D_CELL, "No operator name\n");
}
#endif
} else if (cell_match(data, 1, length, "PSNT")) {
// PSTN (Read packet service network type)
getStrTokens((char*)cmd, (char*)values, ",");
networkType = parseInt(&values[1*MAX_TOKEN_SIZE], 10);
DBG(D_CELL, "Got network type=%d\n", networkType);
} else if (cell_match(data, 1, length, "CGEV")) {
getStrTokens((char*)cmd, (char*)values, ",");
DBG(D_CELL, "Got CGEV\n");
cellLogic_setCgEvent(&values[0]);
} else if (cell_match(data, 1, length, "COPS")) {
memset(operatorName
, 0, MAX_TOKEN_SIZE
);
getStrTokens((char*)cmd, (char*)values, ",");
if (getSubStrInQuote(operatorName, (char*)&values[2*MAX_TOKEN_SIZE])) {
DBG(D_CELL, "COPS: Operator=%s\n", operatorName);
} else {
DBG(D_CELL, "COPS: No operator name\n");
}
} else if (cell_match(data, 1, length, CCLK)) {
//printf("app/cell.c::%s, have time: %s\n",__func__,cmd); //debug
// For Talisker, sync time with Tal server, not using cell time
// String. Format is "yy/MM/dd,hh:mm:ss-zz",
// where characters indicate year (two last digits),
// month, day, hour, minutes, seconds and time zone
// (indicates the difference, expressed in quarters
// of an hour, between the local time and GMT;
// range -96...+96). E.g. 6th of May 1994, 22:10:00
// GMT+2 hours equals to "94/05/06,22:10:00+08"
char tmp[24];
char *cclk = (char*)tmp;
char c[3];
INT32 year;
c[0] = cclk[0]; c[1] = cclk[1]; c[2] = '\0';
year
= (INT32
)strtol((char*)c
, NULL
, 10);
year += 2000;
//DBG(D_CELL, "clockstr=%s year=%d\n", (char*)tmp, year);
if (year > 2016 && year < 2030) {
INT32 zoneOffset = 0;
structDate d;
zoneOffset
= (INT32
)strtol((char*)&cclk
[17], NULL
, 10) * 15 * 60;
d.year = year;
c[0] = cclk[3]; c[1] = cclk[4];
d.
month = (UINT8
)strtol((char*)c
, NULL
, 10);
c[0] = cclk[6]; c[1] = cclk[7];
d.
day = (UINT8
)strtol((char*)c
, NULL
, 10);
c[0] = cclk[9]; c[1] = cclk[10];
d.
hour = (UINT8
)strtol((char*)c
, NULL
, 10);
c[0] = cclk[12]; c[1] = cclk[13];
d.
min = (UINT8
)strtol((char*)c
, NULL
, 10);
c[0] = cclk[15]; c[1] = cclk[16];
d.
sec = (UINT8
)strtol((char*)c
, NULL
, 10);
d.ms = 0;
UINT32 nowInSec = utilsGetSecFromDate(&d);
//DBG(D_CELL, "clockstr=%s yr=%d mo=%d day=%d hr=%d min=%d sec=%d zone=%ld nowInSec=%u\n",
//cclk, d.year, d.month, d.day, d.hour, d.min, d.sec, zoneOffset, nowInSec);
cellLogic_handleClock(nowInSec - zoneOffset);
}
}
if (!(cellState & CELL_INIT_DONE))
cellCheckOpState();
}
}
}
}
void cell_setIsRegistered(bool isRegistered) {
cell_netRegistered = isRegistered;
}
bool cell_getIsRegistered() {
return cell_netRegistered;
}
void cell_setIsSimStoreReady(bool isReady) {
cell_simStoreReady = isReady;
}
/*
* Get the network status. These are the status values:
* -1 have not read status from modem yet
* 0 GPRS network
* 1 EGPRS network
* 2 WCDMA network
* 3 HSDPA network
* 4 LTE network
* 5 unknown or not registered (reported by modem)
*/
INT8 cell_networkType() {
return networkType;
}
char *cell_networkTypeStr() {
if (networkType == -1) {
return "Unknown";
}
return networkTypeStr[networkType];
}
char *cell_operatorName() {
return operatorName;
}
#ifdef ENABLE_OPEN_CLOSE_RD_WR_PARADIGM
NOTE: UNDER CONSTRUCTION
/*
>>Packet Data Protocol (PDP; e.g., IP, X.25, FrameRelay) context
>>AT+CGDCONT AT commands sets the PDP context parameters such as PDP type
(IP, IPV6, PPP, X.25 etc), APN, data compression, header compression etc.
at+cgdcont=1,"IP","a1.korem2m.com","",0,0 ------> Set APN for context 1
>>AT#SCFG AT command is used to configure the socket.
>>AT#SCFG=<connId>,<cid>,<pktSz>,<maxTo>,<connTo>,<txTo>
at#scfg=X,1,1500,0,600,50 ------> X is 1 or 2 (Check what format Diep implemented)
>>At#SCFEXT is used to configure extended parameters of the socket.
>>AT#SCFGEXT=<conned>,<srMode>,<recvDataMode>,<keepalive>,[,<ListenAutoRsp>[,<sendDataMode>]]
at#scfgext=X,2,0,0 ------> X is socket 1 or 2 (Check what format Diep implemented)
>>AT#SGACT command is used to activate (stat= 1) or deactivate (stat= 0) specific PDP context.
>>AT#SGACT=<cid>,<stat>[,<userId>,<pwd>]
at#sgact=1,1 ------> Open data session for context 1
>>AT#SD AT command opens remote connection via socket.
>>AT#SD=<connId>,<txProt>,<rPort>,<IPaddr>[,<closureType>[,<lPort>[,<connMode>]]]
at#sd=X,1,<remote port>,"<remote ip>",0,1234,1 ------> X is socket 1 or 2
>>This command is used to close a socket. {This command is used to close a socket: 1..6}
>>Note: socket cannot be closed in states “resolving DNS” and “connecting” (see AT#SS command)
at#sh=X ------> Close socket X
>>AT#SGACT command is used to activate (stat= 1) or deactivate (stat= 0) specific PDP context.
>>AT#SGACT=<cid>,<stat>[,<userId>,<pwd>]
at#sgact=1,0 ------> Close data session (see this is the same number as in APN 1)
>>AT#SHDN AT command causes the device to detach from the netwrok and shutdown.
at#shdn ------> Turn off modem
------> Remove power from modem
------> Wait 15 secs
------> Enable modem power
------> Reinit
*/
#define CTX_OK 0
#define CTX_NOT_OPEN -1
#define CTX_ALREADY_OPEN -2
#define CTX_NO_REG -3
#define CTX_BAD_CID -4
#define CTX_ACTIVATION_FAILED -5
#define CTX_BAD_HANDLE -6
#define MAX_MDM_CONTEXTS 1
typedef struct {
INT8 handle;
UINT8 cid;
} mdmContext_t;
static mdmContext_t mdmh[MAX_MDM_CONTEXTS];
// Opens a context using the specified apn # (cid: context id )
// i.e. obtains an ip address used to communicate with other internet sites
// Returns:
// >= 0 -- context handle (there is only 1 for now and it is always 0)
// < 0 -- error code
//
int openContext(int cid)
{
if (mdmh[0].handle >= 0)
return CTX_ALREADY_OPEN; // already open
}
if (!cell_getIsRegistered()) {
return CTX_NO_REG; // NOT registered (out of coverage?)
}
if ((cid != 1) && (cid != 3)) {
return CTX_BAD_CID; // invalid context id specified
}
// >>AT#SGACT command is used to activate (stat= 1) or deactivate (stat= 0) specific PDP context.
// >>AT#SGACT=<cid>,<stat>[,<userId>,<pwd>]
// at#sgact=1,1 ------> Open data session for context 1
// cell_sendAtCmd(tmp, 0, strlen((char*)tmp), NULL);
//cell_sendAtCmd(tmp, 0, strlen((char*)tmp), NULL, __func__); //debug
return CTX_ACTIVATION_FAILED; // context activation failed
}
// Closes the specified context
//
int closeContext(int handle)
{
if (handle != 0) {
return CTX_BAD_HANDLE; // invalid handle
}
if (mdmh[handle].handle < 0) {
return CTX_NOT_OPEN; // not open
}
// >>AT#SGACT command is used to activate (stat= 1) or deactivate (stat= 0) specific PDP context.
// >>AT#SGACT=<cid>,<stat>[,<userId>,<pwd>]
// at#sgact=1,0 ------> Close data session (see this is the same number as in APN 1)
mdmh[handle].handle = -1;
return CTX_OK;
}
//void cell_closeSocket(UINT8 connId) {
//void cell_openSocket(UINT8 connId, char *serverIp, UINT32 port) {
// Opens a socket using the specified ip and port
// (What apnNum is this associated with?????)
// Returns:
int openSocket(UINT32 ip,UINT16 port)
{
return -1;
}
// Closes the specified socket
//
void closeSocket(int handle)
{
}
// Do we want to allow timeouts to be specified for readSocket() and writeSocket()???????????????
// Writes a UDP packet to the specified socket
// Returns:
// < 0 -- error
// = 0 -- ERROR, this should NEVER happen!
// > 0 -- # of bytes written
int writeSocket(int handle,UINT8 *udp,int len)
{
return -1;
}
// Reads a UDP packet from the specified socket
// Returns:
// < 0 -- error
// = 0 -- no data available
// > 0 -- # of bytes in the UDP packet
//
int readSocket(int handle,UINT8 *udp,int maxLen)
{
// How do we initiate a UDP read from the back end?????????????????????????
return 0; // nothing available
}
#endif // ENABLE_OPEN_CLOSE_RD_WR_PARADIGM
/*
* Set the DMAN server.
*/
void cell_setDmanServer(char *server) {
strcpy(cell_dmanServer
, server
);
}