Connection Manager library for u-blox cellular modules. It manages the modem for keeping data connection always active.

Dependents:   C027_demo_ConnMan

CNManager.cpp

Committer:
msinig
Date:
2016-01-21
Revision:
1:29ad1d1ac1f9
Parent:
0:86284a262735

File content as of revision 1:29ad1d1ac1f9:

#include "CNManager.h"

#include "CNData.h"
#include "CNUtil.h"
#include "CNLib.h"
#include "CNReg.h"

//! Manager internal state
typedef enum {
    MNG_NOT_INIT = 0,       //!< not initiated
    MNG_RADIO_OFF,          //!< radio is off
    MNG_RADIO_ON,           //!< radio is on
    MNG_IDLE,               //!< idle
    MNG_DATA_UP,            //!< data connection is active
    MNG_ERROR_RESET,        //!< reset
    MNG_ERROR_STUCK         //!< unrecoverable error
} MngState;

static MngState state = MNG_NOT_INIT;    //!< Manager state
static bool moduleOn;                    //!< Module power requested power status
static bool dataOn;
static CNLib* cnLib;                      //!< Pointer to CN library
static char simPin[10];                   //!< Sim Pin
static int errorCounter;                  //!< Error counter
static evMngHandler mngHandler;         //!< Function point handler
static void* paramDataHandler;            //!< Pointer to pass to the handler    
static int respError;

static CNResp radioSwitchOn(CNLib*& lib);
static CNResp radioSwitchOff(CNLib*& lib);
static CNResp simInit(CNLib* lib, char* simPin);

bool cnInit(bool powerOn /*=true*/, bool dataEnabled /*=true*/, bool roomingEnabled /*=true*/){
    mngHandler = NULL;
    paramDataHandler = NULL;
    state = MNG_RADIO_OFF;
    moduleOn = powerOn;
    errorCounter = 0;
    respError = 0;
    cnRegInit();
    cnDataInit();
    dataOn = dataEnabled;
    cnRegSetRoaming(roomingEnabled);
    return true;
}

void cnRegHandler(evMngHandler handler, void* param/*=NULL*/)
{
     mngHandler = handler;     
     paramDataHandler = param;
}

MDMSerial* cnGetMDM(){
    return (MDMSerial*) cnLib;
}

void cnSetPower(bool on){
    moduleOn = on;
}

void cnSetDataEnabled(bool hasDataConnToBeEnabled){
     TRACE("%s enter \r\n", __FUNCTION__); 
    dataOn = hasDataConnToBeEnabled;
    cnDataEnable(dataOn);
}

void cnSetRoamingOn(bool isRoomingEnabled){
    cnRegSetRoaming(isRoomingEnabled);
}

void cnSetApn(const char *apn,const  char* username/*= NULL*/,const  char* password/*= NULL*/){
    cnDataSetupApn(apn, username, password);
}

void cnSetSimPin(const char* pin){
    if (pin)
        strncpy(simPin,pin, sizeof(simPin)); 
}

bool cnIsDataUp(){
    return (state == MNG_DATA_UP)? true : false;
}

bool cnSetDebug(int level){
     if (!setUtilDebugLevel(level))
         return false;
     return true;
}

int cnLoop(){
    int ret;
    MngState oldState = state;

    TRACE("%s enter \r\n", __FUNCTION__);   
    //check if init has been called
    if (state == MNG_NOT_INIT){
        ERROR("%s Module has not been initiated\r\n", __FUNCTION__);
        return false;
    }
    //switch on
    if (state == MNG_RADIO_OFF && moduleOn ){
        INFO("%s: Switching ON radio \r\n", __FUNCTION__);
        cnDataEnable(dataOn);
        ret = radioSwitchOn(cnLib);
        state = (ret == RES_OK) ? MNG_RADIO_ON : MNG_ERROR_RESET;
        
    }
    //switch off
    if (state != MNG_RADIO_OFF && !moduleOn){
        INFO("%s: Switching OFF radio \r\n", __FUNCTION__);
        if (state == MNG_DATA_UP)
            //disable data
            cnDataEnable(false);
        else if (state == MNG_IDLE){
            //switching off
            cnDataReset();
            cnRegReset();
            radioSwitchOff(cnLib);
            state = MNG_RADIO_OFF;
        }
    }
    //sim initialization
    if (state == MNG_RADIO_ON ){
        ret = simInit(cnLib, simPin);
        state = (ret == RES_OK) ? MNG_IDLE : MNG_ERROR_RESET;
        if (state == MNG_IDLE && mngHandler)
            mngHandler(MNG_EV_IDLE, paramDataHandler);
    }
    //cycle the registration and data service
    if (state == MNG_IDLE ||  state == MNG_DATA_UP){
        RegStatus regStatus;
        int res;
        DataConnStatus dataStatus;
        //Tick the registration service
        res = cnRegLoop(cnLib, &regStatus);        
        if (res != RES_OK && ++respError > MEX_RESP_ERROR)
            state = MNG_ERROR_RESET;            
        //Tick Data service
        cnDataLoop(cnLib, regStatus, &dataStatus);
        //manage data status
        if (dataStatus == DATA_IS_CONNECTED){
            state = MNG_DATA_UP;            
            if (mngHandler) mngHandler(MNG_EV_DATA_UP, paramDataHandler);
        } else if (dataStatus == DATA_IS_DISCONNECTED){
            state = MNG_IDLE;
            if (mngHandler) mngHandler(MNG_EV_DATA_DOWN, paramDataHandler);
        }
    }
    //error management
    if (state == MNG_ERROR_RESET){
        //block state machine when errors reach a max value counter
        if (++errorCounter > MAX_ERROR_RETRIES)
            state = MNG_ERROR_STUCK;
        else{
            //otherwise start reset procedure
            ERROR("%s: State machine is in Error state, doing Reset.\r\n", __FUNCTION__);
            respError=0;
            cnDataReset();
            cnRegReset();
            radioSwitchOff(cnLib);
            state = MNG_RADIO_OFF;
        }
    }
    //when the state is stuck stay here forever
    if (state == MNG_ERROR_STUCK){
        ERROR("%s: State machine is in Blocked state, please change setting.\r\n", __FUNCTION__);
        return false;
    }
    //dump info
    if (getUtilDebugLevel() > 1 && oldState != state){
        INFO("CNManager Dump\r\n");
        const char* txtState[] = { "Not Initiated", "Radio is Off", "Radio is On", \
        "Idle", "Connected", "Reset Error", "Blocked Error"};
        if (state < sizeof(txtState)/sizeof(*txtState))
            INFO("  Internal State: %s\r\n", txtState[state]);
    }
    return true;
}

/** Switch On Radio
   \param lib pointer to library
 */
CNResp radioSwitchOn(CNLib*& lib)
{
    TRACE("%s enter \r\n", __FUNCTION__);
    lib =  new CNLib();
    if (lib == NULL){
        ERROR("%s: Error on allocating lib\r\n", __FUNCTION__);
        goto error;
    }
    lib->setDebug(getUtilDebugLevel());
    //power on the module and detect if alive
    if (!RESPOK(lib->powerOnModem())){
        ERROR("%s: Error on detecting the modem\r\n", __FUNCTION__);
        goto error;
    }
    //init generic modem
    if (!RESPOK(lib->initModem())){
        ERROR("%s: Error on init device\r\n", __FUNCTION__);
        goto error;
    }
    //handle unknown device
    if (lib->getDev()->dev == MDMParser::DEV_UNKNOWN){
        ERROR("%s: Device is unknown\r\n", __FUNCTION__);
        goto error;
    }
    return RES_OK;

error:
    return RES_ERROR;

}

/** Switch off the radio
    \param lib pointer to CN library
 */
CNResp radioSwitchOff(CNLib*& lib){
    TRACE("%s enter \r\n", __FUNCTION__);
    if (lib != NULL){
        lib->powerOff();
        delete lib;
    }
    return RES_OK;
}

/** Sim Pin initialization
     \param lib pointer to CN library
     \param simPin sim pin
 */
CNResp simEnterPin(CNLib* lib, char* simPin){
    int res;

    if (simPin == NULL){
        ERROR("%s: Sim Pin is required but not provided\r\n", __FUNCTION__);
        return RES_ERROR_STUCK;
    }
    res = lib->simInit(simPin);
    if (RESPOK(res) )
        return RES_OK;
    if (lib->getDev()->sim == MDMParser::WRONG_PIN){
        ERROR("%s: Sim pin, %s ,provided is wrong\r\n", simPin);
        return RES_ERROR_STUCK;
        }
    return RES_ERROR;
}

/** Sim initialization
     \param lib pointer to CN library
     \param simPin sim pin
 */
CNResp simInit(CNLib* lib, char* simPin)
{
    CNResp res = RES_ERROR;
    int _simWaitCounter=0;
    TRACE("CNLib::%s enter\r\n", __FUNCTION__);

    do{
        if (RESPOK(lib->simInit(NULL)))
            break;
        wait_ms(10);
    } while(_simWaitCounter++ < SIM_WAIT_MAX_CYCLE);
    INFO("Sim status is %d \r\n", lib->getDev()->sim);
    //handle sim status
    switch (lib->getDev()->sim){
      case MDMParser::SIM_READY:
          INFO("%s: Sim has been initiated correctly\r\n", __FUNCTION__);
          res = RES_OK;
          break;
      case MDMParser::SIM_PIN:
          INFO("%s: Sim Pin is requested\r\n");
          res = simEnterPin(lib, simPin);
          break;
      case MDMParser::SIM_MISSING:
          ERROR("%s: Sim has not been inserted, HALT\r\n", __FUNCTION__);
          res = RES_ERROR;
          break;
      case MDMParser::SIM_PUK:
          ERROR("%s: Sim Puk is required, HALT\r\n", __FUNCTION__);
          res = RES_ERROR_STUCK;
          break;
      case MDMParser::SIM_UNKNOWN:
          ERROR("%s: Sim Unknown state\r\n", __FUNCTION__);
          res = RES_ERROR_STUCK;
          break;
      case MDMParser::WRONG_PIN:
          ERROR("%s: Wrong sim pin provided, HALT\r\n", __FUNCTION__);
          res = RES_ERROR_STUCK;
          break;
    }
    //sim has been successfully initialized
    if (res == RES_OK){
        lib->getSimInfo();
    }
    return res;
}

void getNetStatus(MDMParser::NetStatus* net){
    if (net && cnLib)
        memcpy(net, cnLib->getNet(), sizeof(MDMParser::NetStatus));
}
void getDevStatus(MDMParser::DevStatus* dev){
    if (dev && cnLib)
        memcpy(dev, cnLib->getDev(), sizeof(MDMParser::DevStatus));
}