WNC14A2A LTE Interface

Dependencies:   WncControllerK64F

Fork of WNC14A2AInterface by Avnet

WNC14A2AInterface.cpp

Committer:
JMF
Date:
2017-04-19
Revision:
7:9340bc9e8d64
Parent:
6:7fd9e590c4e7
Child:
9:23587afd52fe

File content as of revision 7:9340bc9e8d64:


#include "WNC14A2AInterface.h"
#include <Thread.h>
#include <string> 

/** WNC14A2AInterface class
 *  Implementation of the NetworkInterface for the AT&T IoT Starter Kit 
 *  based on the WNC 14A2A LTE Data Module
 */

#define WNC14A2A_MISC_TIMEOUT 3000
#define WNC14A2A_RESTART_TIMEOUT 10000
#define WNC14A2A_COMMUNICATION_TIMEOUT 100
#define READ_EVERYMS    500

/////////////////////////////////////////////////////
// NXP GPIO Pins that are used to initialize the WNC Shield
/////////////////////////////////////////////////////
DigitalOut  mdm_uart2_rx_boot_mode_sel(PTC17);  // on powerup, 0 = boot mode, 1 = normal boot
DigitalOut  mdm_power_on(PTB9);                 // 0 = modem on, 1 = modem off (hold high for >5 seconds to cycle modem)
DigitalOut  mdm_wakeup_in(PTC2);                // 0 = let modem sleep, 1 = keep modem awake -- Note: pulled high on shield
DigitalOut  mdm_reset(PTC12);                   // active high
DigitalOut  shield_3v3_1v8_sig_trans_ena(PTC4); // 0 = disabled (all signals high impedence, 1 = translation active
DigitalOut  mdm_uart1_cts(PTD0);

// Define pin associations for the controller class to use be careful to 
//  keep the order of the pins in the initialization list.

using namespace WncControllerK64F_fk;       // namespace for the controller class use

WncGpioPinListK64F wncPinList = { 
    &mdm_uart2_rx_boot_mode_sel,
    &mdm_power_on,
    &mdm_wakeup_in,
    &mdm_reset,
    &shield_3v3_1v8_sig_trans_ena,
    &mdm_uart1_cts
};

Thread smsThread;
static Mutex _pwnc_mutex;

static WNCSOCKET _sockets[WNC14A2A_SOCKET_COUNT];
BufferedSerial mdmUart(PTD3,PTD2,1024,1);       //UART for WNC Module

//-------------------------------------------------------------------------
//
// Class constructor.  May be invoked with or without the APN and/or pointer
// to a debug output.  After the constructor has completed, the user can 
// check _errors to determine if any errors occured during instanciation.
// _errors = 0 when no errors occured
//           1 when power-on error occured
//           2 when settng the APN error occured
//           4 when unable to get the network configuration
//           8 when allocating a new WncControllerK64F object failed
//          NSAPI_ERROR_UNSUPPORTED when attempting to create a second object
//

WNC14A2AInterface::WNC14A2AInterface(WNCDebug *dbg) : 
 m_wncpoweredup(0),
 _pwnc(NULL),
 m_active_socket(-1),
 m_smsmoning(0)
{
    _errors = NSAPI_ERROR_OK;
    
    m_debug=false;

    if( _pwnc ) { //can only have a single instance of class
        _errors =  NSAPI_ERROR_UNSUPPORTED;
        return;
        }
    for( int i=0; i<WNC14A2A_SOCKET_COUNT; i++ ) {
        _sockets[i].socket = i;
        _sockets[i].addr = NULL;
        _sockets[i].opened=false;
        _sockets[i].proto=NSAPI_TCP;
        }

    
    memset(_mac_address,0x00,sizeof(_mac_address));

    _debugUart = dbg;
    if( dbg != NULL ) {
        dbg->printf("Adding DEBUG output\n");
        _pwnc = new WncControllerK64F(&wncPinList, &mdmUart, dbg);
        m_debug=true;
        if( _pwnc ) 
            _pwnc->enableDebug(1,1);
        }
    else 
        _pwnc = new WncControllerK64F_fk::WncControllerK64F(&wncPinList, &mdmUart, NULL);
        
    if( !_pwnc ) {
        debugOutput(_debugUart,(char*)", FAILED!\n");
        _errors = NSAPI_ERROR_DEVICE_ERROR;
        }
    else
        debugOutput(_debugUart, (char*)"\n");
}

/*-------------------------------------------------------------------------
 * standard destructor... free up allocated memory
 */

WNC14A2AInterface::~WNC14A2AInterface()
{
    delete _pwnc;  //free the existing WncControllerK64F object
}

nsapi_error_t WNC14A2AInterface::connect() 
{
    debugOutput(_debugUart,(char*)"+CALLED connect(void)\n");
    return connect(NULL,NULL,NULL);
}

/*-------------------------------------------------------------------------
 * This call powers up the WNC module and connects to the user specified APN.  If no APN is provided
 * a default one of 'm2m.com.attz' will be used (the NA APN)
 *
 * Input: *apn is the APN string to use
 *        *username - NOT CURRENTLY USED
 *        *password - NOT CURRENTLY USED
 *
 * Output: none
 *
 * Return: nsapi_error_t
 */
nsapi_error_t WNC14A2AInterface::connect(const char *apn, const char *username, const char *password) 
{
    debugOutput(_debugUart,(char*)"+ENTER connect(apn,user,pass)\n");
    if( !_pwnc )
        return (_errors=NSAPI_ERROR_NO_CONNECTION);

    if (!apn)
        apn = "m2m.com.attz";

    if (!m_wncpoweredup) {
        debugOutput(_debugUart,(char*)"+call powerWncOn using '%s'\n",apn);
        _pwnc_mutex.lock();
        m_wncpoweredup=_pwnc->powerWncOn(apn,40);
        _pwnc_mutex.unlock();
        _errors = m_wncpoweredup? 1:0;
        }
    else { //we've already called powerWncOn and set the APN, so just set the APN 
        debugOutput(_debugUart,(char*)"+already powered on, set APN to: %s\n",apn);
        _pwnc_mutex.lock();
        _errors = _pwnc->setApnName(apn)? 1:0;
        _pwnc_mutex.unlock();
        }

    _pwnc_mutex.lock();
    _errors |= _pwnc->getWncNetworkingStats(&myNetStats)? 2:0;
    _pwnc_mutex.unlock();

    debugOutput(_debugUart,(char*)"+EXIT connect %02X\n",_errors);
    return (!_errors)? NSAPI_ERROR_NO_CONNECTION : NSAPI_ERROR_OK;
}

/*--------------------------------------------------------------------------
 * This function calls the WNC to retrieve the device (this WNC) connected IP 
 * address once we are connected to the APN.
 *
 * Inputs: NONE.
 *
 * Output: none.
 *
 * Return: pointer to the IP string or NULL 
 */
const char *WNC14A2AInterface::get_ip_address()
{
    const char *ptr=NULL; 

    _pwnc_mutex.lock();
    if ( _pwnc->getWncNetworkingStats(&myNetStats) ) {
        CHK_WNCFE(( _pwnc->getWncStatus() == FATAL_FLAG ), null);
        ptr = &myNetStats.ip[0];
    }
    _pwnc_mutex.unlock();
    _errors=NSAPI_ERROR_NO_CONNECTION;
    return ptr;
}

#if 0
/*--------------------------------------------------------------------------
 * This function calls the WNC to retrieve the currently connected IP address
 * it will be a bogus 192.168.0.1 if we are not connected to anyone
 *
 * Inputs: NONE.
 *
 * Output: none.
 *
 * Return: pointer to the IP string or NULL 
 */
const char *WNC14A2AInterface::get_ip_address()
{
    static char *ptr=NULL, ipAddrStr[25];
    debugOutput(_debugUart,(char*)"+ENTER get_ip_address()\n");

    memset(ipAddrStr, 0x00, sizeof(ipAddrStr));
    _pwnc_mutex.lock();

    if( _pwnc && m_active_socket != -1 )
        if( _pwnc->getIpAddr(m_active_socket, ipAddrStr) )
          ptr=ipAddrStr;
    _pwnc_mutex.unlock();
    _errors=NSAPI_ERROR_NO_CONNECTION;
    return ptr;
}
#endif

/* -------------------------------------------------------------------------
 * Open a socket for the WNC.  This doesn't actually open the socket within
 * the WNC, it only allocates a socket device and saves the pertinet info
 * that will be required with the WNC socket is opened. The m_active_socket
 * is also updated to this socket as it is assumed this socket should be used
 * for subsequent interactions.
 *
 * Input: 
 *  - a pointer to a handle pointer.  
 *  - The type of socket this will be, either NSAPI_UDP or NSAP_TCP
 *
 * Output: *handle is updated
 *
 * Return:
 *  - socket being used if successful, -1 on failure
 */
int WNC14A2AInterface::socket_open(void **handle, nsapi_protocol_t proto) 
{
    int i;
    debugOutput(_debugUart,(char*)"+ENTER socket_open()\n");

    // search through the available sockets (WNC can only support a max amount).
    for( i=0; i<WNC14A2A_SOCKET_COUNT; i++ )
        if( !_sockets[i].opened )
            break;

    if( i == WNC14A2A_SOCKET_COUNT ) {
        _errors=NSAPI_ERROR_NO_SOCKET;
        return -1;
        }

    m_active_socket = i;           
    _sockets[i].socket = i;        //save this index to make easier later-on
    _sockets[i].url="";
    _sockets[i].opened = true;     //ok, we are using this socket now
    _sockets[i].addr = NULL;       //but we haven't opened it yet
    _sockets[i].proto = (proto == NSAPI_UDP) ? 0 : 1; //set it up for what WNC wants
    *handle = &_sockets[i];

    debugOutput(_debugUart,(char*)"+USING Socket index %d, OPEN=%s, proto =%d\nEXIT socket_open()\n",
    i, _sockets[i].opened?"YES":"NO", _sockets[i].proto);
    
    _errors = NSAPI_ERROR_OK;
    return i;
}

/*-------------------------------------------------------------------------
 * Connect a socket to a IP/PORT.  Before you can connect a socket, you must have opened
 * it.
 *
 * Input: handle - pointer to the socket to use
 *        address- the IP/Port pair that will be used
 *
 * Output: none
 *
 * return: 0 or greater on success (value is the socket ID)
 *        -1 on failure
 */
int WNC14A2AInterface::socket_connect(void *handle, const SocketAddress &address) 
{
    WNCSOCKET *wnc = (WNCSOCKET *)handle;   

    debugOutput(_debugUart,(char*)"+ENTER socket_connect()\n");
    debugOutput(_debugUart,(char*)"+IP  = %s\n+PORT= %d\n", address.get_ip_address(), address.get_port());
    
    if (!_pwnc || m_active_socket == -1) {
        _errors = NSAPI_ERROR_NO_SOCKET;
        return -1;
        }

    if( !wnc->opened ) {
        _errors = NSAPI_ERROR_NO_SOCKET;
        return -1;
        }
    m_active_socket = wnc->socket;  //in case the user is asking for a different socket
    wnc->addr = address;
                                
    //we will always connect using the URL if possible, if no url has been provided, try the IP address
    if( wnc->url.empty() ) {
        debugOutput(_debugUart,(char*)"+call openSocketIpAddr(%d,%s,%d,%d)\n",m_active_socket, 
                           address.get_ip_address(), address.get_port(), wnc->proto);
        if( !_pwnc->openSocketIpAddr(m_active_socket, address.get_ip_address(), address.get_port(), 
                                 wnc->proto, WNC14A2A_COMMUNICATION_TIMEOUT) ) {
            _errors = NSAPI_ERROR_NO_SOCKET;
            return -1;
            }
        }
     else {
        debugOutput(_debugUart,(char*)"+call openSocketUrl(%d,%s,%d,%d)\n", m_active_socket, 
                           wnc->url.c_str(), wnc->addr.get_port(), wnc->proto);
        if( !_pwnc->openSocketUrl(m_active_socket, wnc->url.c_str(), wnc->addr.get_port(), wnc->proto) ) {
            _errors = NSAPI_ERROR_NO_SOCKET;
            return -1;
            }
        }

    debugOutput(_debugUart,(char*)"+SOCKET %d CONNECTED!\n+URL=%s\n+IP=%s; PORT=%d\n+EXIT socket_connect()\n\n",m_active_socket,
                wnc->url.c_str(), wnc->addr.get_ip_address(), wnc->addr.get_port());
    return 0;
}

/*-------------------------------------------------------------------------
 * Perform a URL name resolve, update the IP/Port pair, and nsapi_version. nsapi_version
 * could be either NSAPI_IPv4 or NSAPI_IPv6 but this functional is hard coded ti NSAPI_IPv4
 * for now. The currently active socket is used for the resolution.  
 *
 * Input: name - the URL to resolve
 *
 * Output: address - the IP/PORT pair this URL resolves to
 *         version - always assumed to be NSAPI_IPv4  currently
 *
 * Return: nsapi_error_t
 */

nsapi_error_t WNC14A2AInterface::gethostbyname(const char* name, SocketAddress *address, nsapi_version_t version)
{
    nsapi_error_t ret = NSAPI_ERROR_OK;
    char ipAddrStr[25];
    int  t_socket = 0;  //use a temporary socket place holder

    debugOutput(_debugUart,(char*)"+ENTER gethostbyname()()\n+CURRENTLY:\n");
    debugOutput(_debugUart,(char*)"+URL = %s\n+IP  = %s\n+PORT= %d\n", name, address->get_ip_address(), address->get_port());
    memset(ipAddrStr,0x00,sizeof(ipAddrStr));
    
    if (!_pwnc) 
        return (_errors = NSAPI_ERROR_NO_SOCKET);
        
    if (m_active_socket != -1)      //we might have been called before a socket was opened
        t_socket = m_active_socket; //if so, do nothing with the active socket index

    //Execute DNS query.  
    if( !_pwnc->resolveUrl(t_socket, name) )  
        return (_errors = NSAPI_ERROR_DEVICE_ERROR);

    //Now, get the IP address that the URL was resolved to
    if( !_pwnc->getIpAddr(t_socket, ipAddrStr) )
        return (_errors = NSAPI_ERROR_DEVICE_ERROR);

    address->set_ip_address(ipAddrStr);
    debugOutput(_debugUart,(char*)"+resolveUrl returned IP=%s\n",ipAddrStr);
    if( t_socket == m_active_socket ) {
        _sockets[m_active_socket].url=name;
        _sockets[m_active_socket].addr.set_ip_address(ipAddrStr);
        }

    debugOutput(_debugUart,(char*)"+EXIT gethostbyname()\n+URL = %s\n+IP  = %s\n+PORT= %d\n\n", 
                _sockets[m_active_socket].url.c_str(), address->get_ip_address(), 
                address->get_port());
    _errors = ret;
    return ret;
}
 
/*-------------------------------------------------------------------------
 * using the specified socket, send the data.
 *
 * Input: handle of the socket to use
 *        pointer to the data to send
 *        amount of data being sent
 *
 * Output: none
 *
 * Return: number of bytes that was sent
 */
int WNC14A2AInterface::socket_send(void *handle, const void *data, unsigned size) 
{
    WNCSOCKET *wnc = (WNCSOCKET *)handle;
    int r = -1;
    debugOutput(_debugUart,(char*)"+ENTER socket_send()\n");

    if (!_pwnc || m_active_socket == -1) {
        _errors = NSAPI_ERROR_NO_SOCKET;
        return 0;
        }
    else
        m_active_socket = wnc->socket; //just in case sending to a socket that wasn't last used

    debugOutput(_debugUart,(char*)"+SOCKET %d is %s, URL=%s, IP=%s, PORT=%d\n",m_active_socket,
        (char*)wnc->opened?"OPEN":"CLOSED",wnc->url.c_str(),wnc->addr.get_ip_address(),wnc->addr.get_port());
    debugOutput(_debugUart,(char*)"+WRITE [%s] (%d bytes) to socket #%d\n",data,size,wnc->socket);

    _pwnc_mutex.lock();
    if( _pwnc->write(m_active_socket, (const uint8_t*)data, size) ) 
       r = size;
    else
       debugOutput(_debugUart,(char*)"+ERROR: write to socket %d failed!\n",m_active_socket);
    _pwnc_mutex.unlock();

    debugOutput(_debugUart,(char*)"+EXIT socket_send(), successful: %d\n\n",r);
    return r;
}  

/*-------------------------------------------------------------------------
 * Called to receive data.  
 *
 * Input: handle to the socket we want to read from
 *
 * Output: data we receive is placed into the data buffer
 *         size is the size of the buffer
 *
 * Returns: The number of bytes received or -1 if an error occured
 */
int WNC14A2AInterface::socket_recv(void *handle, void *data, unsigned size) 
{
    WNCSOCKET *wnc = (WNCSOCKET *)handle;
    size_t done, cnt;
    Timer t;

    debugOutput(_debugUart,(char*)"+ENTER socket_recv(); read up to %d bytes\n",size);

    memset(data,0x00,size);  
    if (!_pwnc || m_active_socket == -1) {
        _errors = NSAPI_ERROR_NO_SOCKET;
        return -1;
        }
    else
        m_active_socket = wnc->socket; //just in case sending to a socket that wasn't last used

    t.start();
    do {
        if( !(t.read_ms() % READ_EVERYMS) )
          cnt = done = _pwnc->read(m_active_socket, (uint8_t *)data, (uint32_t) size);
        done = (cnt || (t.read_ms() > WNC14A2A_MISC_TIMEOUT))? 1:0;
        }
    while( !done );
    t.stop();
    
    if( _pwnc->getWncStatus() != WNC_GOOD ) {
        _errors = NSAPI_ERROR_DEVICE_ERROR;
        return -1;
        }

    debugOutput(_debugUart,(char*)"+EXIT socket_recv(), ret=%d\n",cnt);
    return cnt;
}

/*-------------------------------------------------------------------------
 * Close a socket 
 *
 * Input: the handle to the socket to close
 *
 * Output: none
 *
 * Return: -1 on error, otherwise 0
 */
int WNC14A2AInterface::socket_close(void *handle)
{
    WNCSOCKET *wnc = (WNCSOCKET*)handle;
    debugOutput(_debugUart,(char*)"+CALLED socket_close()\n");

    if (!_pwnc || m_active_socket == -1) {
        _errors = NSAPI_ERROR_NO_SOCKET;
        return -1;
        }
    else
        m_active_socket = wnc->socket; //just in case sending to a socket that wasn't last used
    
    if( !_pwnc->closeSocket(m_active_socket) ) {
        _errors = NSAPI_ERROR_DEVICE_ERROR;
        return -1;
        }

    wnc->opened = false;     //no longer in use
    wnc->addr = NULL;        //not open
    wnc->proto = 0;  //assume TCP for now
    _errors = NSAPI_ERROR_OK;
    return 0;
}

/*-------------------------------------------------------------------------
 * return the MAC for this device.  Because there is no MAC Ethernet 
 * address to return, this function returns a bogus MAC address created 
 * from the ICCD on the SIM that is being used.
 *
 * Input: none
 *
 * Output: none
 *
 * Return: MAC string containing "NN:NN:NN:NN:NN:NN" or NULL
 */
const char *WNC14A2AInterface::get_mac_address()
{
    string mac, str;
    debugOutput(_debugUart,(char*)"+ENTER get_mac_address()\n");

    if( _pwnc->getICCID(&str) ) {
        CHK_WNCFE((_pwnc->getWncStatus()==FATAL_FLAG), null);
        mac = str.substr(3,20);
        mac[2]=mac[5]=mac[8]=mac[11]=mac[14]=':';
        strncpy(_mac_address, mac.c_str(), mac.length());
        return _mac_address;
    }
    return NULL;
}

/*-------------------------------------------------------------------------
 * return a pointer to the current WNC14A2AInterface
 */
NetworkStack *WNC14A2AInterface::get_stack() {
    debugOutput(_debugUart,(char*)"+CALLED get_stack()\n");
    return this;
}

/*-------------------------------------------------------------------------
 * Disconnnect from the 14A2A, but we can not do that
 * so just return saying everything is ok.
 */
nsapi_error_t WNC14A2AInterface::disconnect() 
{
    debugOutput(_debugUart,(char*)"+CALLED disconnect()\n");
    return NSAPI_ERROR_OK;
}

/*-------------------------------------------------------------------------
 * allow the user to change the APN. The API takes username and password
 * but they are not used.
 *
 * Input: apn string
 *        username - not used
 *        password - not used
 *
 * Output: none
 *
 * Return: nsapi_error_t 
 */
nsapi_error_t WNC14A2AInterface::set_credentials(const char *apn, const char *username, const char *password) 
{
    debugOutput(_debugUart,(char*)"+ENTER set_credentials()\n");
    if( !_pwnc ) 
        return (_errors=NSAPI_ERROR_NO_CONNECTION);
        
    if( !apn )
        return (_errors=NSAPI_ERROR_PARAMETER);

    if( !_pwnc->setApnName(apn) )
        return (_errors=NSAPI_ERROR_DEVICE_ERROR);

    return (_errors=NSAPI_ERROR_OK);
}

/*-------------------------------------------------------------------------
 * Register a callback on state change of the socket.FROM NetworkStack
 *  @param handle       Socket handle
 *  @param callback     Function to call on state change
 *  @param data         Argument to pass to callback
 *  @note Callback may be called in an interrupt context.
 */
void WNC14A2AInterface::socket_attach(void *handle, void (*callback)(void *), void *data)
{
    debugOutput(_debugUart,(char*)"+CALLED socket_attach()\n");
}

/*-------------------------------------------------------------------------
 * check to see if we are currently regisered with the network.
 *
 * Input: none
 *
 * Output: none
 *
 * Return: ture if we are registerd, false if not or an error occured
 */
bool WNC14A2AInterface::registered()
{
    debugOutput(_debugUart,(char*)"+ENTER registered()\n");
    if( !_pwnc ) {
        _errors=NSAPI_ERROR_NO_CONNECTION;
        return false;
        }

    if ( _pwnc->getWncStatus() == WNC_GOOD ){
        _errors=NSAPI_ERROR_OK;
        return true;
        }
    _errors=NSAPI_ERROR_NO_CONNECTION;
    return false;
}

/*-------------------------------------------------------------------------
 * doDebug is just a handy way to allow a developer to set different levels
 * of debug for the WNC14A2A device.
 *
 * Input:  a Bitfield of -
 *   basic debug   = 0x01
 *   more debug    = 0x02
 *   network debug = 0x04
 *   all debug     = 0x07
 *
 * Output: none
 *
 * Returns: void
 */
void WNC14A2AInterface::doDebug( int v )
{
    if( !_pwnc )
        _errors = NSAPI_ERROR_DEVICE_ERROR;
    else
        _pwnc->enableDebug( (v&1), (v&2) );

    m_debug=(v&4);
    debugOutput(_debugUart,(char*)"+SETTING debug flag to 0x%02X\n",v);
}

/*-------------------------------------------------------------------------
 * Simple function to allow for writing debug messages.  It always 
 * checks to see if debug has been enabled or not before it
 * outputs the message.
 *
 * Input: The debug uart pointer followed by format string and vars
 *
 * Output: none
 *
 * Return: void
 */
void WNC14A2AInterface::debugOutput(WNCDebug *dbgOut, char * format, ...) 
{
    if( dbgOut && m_debug ) {
        char buffer[256];
        va_list args;
        va_start (args, format);
        vsnprintf(buffer, sizeof(buffer), format, args);
        dbgOut->puts(buffer);
        va_end (args);
        }
}


////////////////////////////////////////////////////////////////////
//  UDP methods
///////////////////////////////////////////////////////////////////

//-------------------------------------------------------------------------
//sends data to a UDP socket
int WNC14A2AInterface::socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size)
{
    WNCSOCKET *wnc = (WNCSOCKET *)handle;
    
    debugOutput(_debugUart,(char*)"+CALLED socket_sendto()\n");
    CHK_WNCFE(( _pwnc->getWncStatus() == FATAL_FLAG ), fail);
    if (!wnc->opened) {
       int err = socket_connect(wnc, address);
       if (err < 0) 
           return err;
       }
    wnc->addr = address;

    return socket_send(wnc, data, size);
}

//receives from a UDP socket
int WNC14A2AInterface::socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size)
{
    WNCSOCKET *wnc = (WNCSOCKET *)handle;
    debugOutput(_debugUart,(char*)"+CALLED socket_recvfrom()\n");
    int ret = socket_recv(wnc, (char *)buffer, size);
    if (ret >= 0 && address) 
        *address = wnc->addr;
    return ret;
}

////////////////////////////////////////////////////////////////////
//  SMS methods
///////////////////////////////////////////////////////////////////

/*-------------------------------------------------------------------------
 * IOTSMS message don't use a phone number, they use the device ICCID.  This 
 * function returns the ICCID based number that is used for this device.
 *
 * Input: none
 * Output: none
 * Return: string containing the IOTSMS number to use
 */
char* WNC14A2AInterface::getSMSnbr( void ) 
{
    char * ret=NULL;
    string iccid_str;
    static string msisdn_str;

    if( !_pwnc ) {
        _errors=NSAPI_ERROR_NO_CONNECTION;
        return NULL;
        }

    CHK_WNCFE(( _pwnc->getWncStatus() == FATAL_FLAG ), null);

    if( !_pwnc->getICCID(&iccid_str) ) 
        return ret;
 
    CHK_WNCFE(( _pwnc->getWncStatus() == FATAL_FLAG ), null);

    if( _pwnc->convertICCIDtoMSISDN(iccid_str, &msisdn_str) )
         ret = (char*)msisdn_str.c_str();    
    return ret;
}


/*-------------------------------------------------------------------------
 * Normally the user attaches his call-back function when performing
 * the listen call which enables the SMS system, but this function
 * allows them to update the callback if desired.
 *
 * input: pointer to the function to call
 * output: none
 * return: none
 */
void WNC14A2AInterface::sms_attach(void (*callback)(IOTSMS *))
{
    debugOutput(_debugUart,(char*)"+CALLED sms_attach() called\n");
    _sms_cb = callback;
}

/*-------------------------------------------------------------------------
 * Call this to start the SMS system.  It is needed to reset the WNC 
 * internal data structures related to SMS messaging
 */
void WNC14A2AInterface::sms_start(void)
{
    _pwnc_mutex.lock();                       //delete any message currently in storage
    _pwnc->deleteSMSTextFromMem('*');       //so we are notified of new incomming messages
    _pwnc_mutex.unlock();
}

/*-------------------------------------------------------------------------
 * Initialize the IoT SMS system.  Initializing it allows the user to set a 
 * polling period to check for SMS messages, and a SMS is recevied, then 
 * a user provided function is called.  
 *
 * Input: polling period in seconds. If not specified 30 seconds is used.
 *        pointer to a users calllback function
 * Output: none
 *
 * Returns: void
 */
void WNC14A2AInterface::sms_listen(uint16_t pp)
{
    debugOutput(_debugUart,(char*)"+CALLED sms_listen(%d) called\n",pp);
    if( !_pwnc ) {
        _errors=NSAPI_ERROR_NO_CONNECTION;
        return;
        }

    CHK_WNCFE(( _pwnc->getWncStatus() == FATAL_FLAG ), fail);

    if( m_smsmoning )
        m_smsmoning = false;
    if( pp < 1)
        pp = 30;


    debugOutput(_debugUart,(char*)"+setup event queue\n");
    smsThread.start(callback(&sms_queue,&EventQueue::dispatch_forever));

    _pwnc_mutex.lock();                       //delete any message currently in storage
    _pwnc->deleteSMSTextFromMem('*');       //so we are notified of new incomming messages
    _pwnc_mutex.unlock();
    sms_queue.call_every(pp*1000, mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::handle_sms_event));

    m_smsmoning = true;
    debugOutput(_debugUart,(char*)"+EXIT sms_listen()\n");
}

/*-------------------------------------------------------------------------
 * process to check SMS messages that is called at the user specified period
 * 
 * input: none
 * output:none
 * return:void
 */
void WNC14A2AInterface::handle_sms_event()
{
    int msgs_available;
    debugOutput(_debugUart,(char*)"+CALLED handle_sms_event() called\n");

    if ( _sms_cb && m_smsmoning ) {
        CHK_WNCFE((_pwnc->getWncStatus()==FATAL_FLAG), fail);
        _pwnc_mutex.lock();
        msgs_available = _pwnc->readUnreadSMSText(&m_smsmsgs, true);
        _pwnc_mutex.unlock();
        if( msgs_available ) {
            debugOutput(_debugUart,(char*)"+Have %d unread texts present\n",m_smsmsgs.msgCount);
            for( int i=0; i< m_smsmsgs.msgCount; i++ ) {
                m_MsgText.number = m_smsmsgs.e[i].number;
                m_MsgText.date = m_smsmsgs.e[i].date;
                m_MsgText.time = m_smsmsgs.e[i].time;
                m_MsgText.msg = m_smsmsgs.e[i].msg;
                _sms_cb(&m_MsgText);
                }
            }
        }
    debugOutput(_debugUart,(char*)"+EXIT handle_sms_event\n");
}


/*-------------------------------------------------------------------------
 * Check for any SMS messages that are present. If there are, then  
 * fetch them and pass to the users call-back function for processing
 *
 * input: pointer to a IOTSMS message buffer array (may be more than 1 msg)
 * output:message buffer pointer is updated
 * return: the number of messages being returned
 */
int WNC14A2AInterface::getSMS(IOTSMS **pmsg) 
{
    int msgs_available;

    debugOutput(_debugUart,(char*)"+CALLED getSMS()\n");
    CHK_WNCFE((_pwnc->getWncStatus()==FATAL_FLAG), fail);

    _pwnc_mutex.lock();
    msgs_available = _pwnc->readUnreadSMSText(&m_smsmsgs, true);
    _pwnc_mutex.unlock();

    if( msgs_available ) {
        debugOutput(_debugUart,(char*)"+Have %d unread texts present\n",m_smsmsgs.msgCount);
        for( int i=0; i< m_smsmsgs.msgCount; i++ ) {
            m_MsgText_array[i].number = m_smsmsgs.e[i].number;
            m_MsgText_array[i].date   = m_smsmsgs.e[i].date;
            m_MsgText_array[i].time   = m_smsmsgs.e[i].time;
            m_MsgText_array[i].msg    = m_smsmsgs.e[i].msg;
            pmsg[i] = (IOTSMS*)&m_MsgText_array[i];
            }
        debugOutput(_debugUart,(char*)"+DONE getting messages\n");
        msgs_available = m_smsmsgs.msgCount;
        }
    debugOutput(_debugUart,(char*)"+EXIT getSMS\n");
    return msgs_available;
}


/*-------------------------------------------------------------------------
 * send a message to the specified user number. 
 *
 * input: string containing users number
 *        string with users message
 * ouput: none
 *
 * return: true if no problems occures, false if failed to send
 */
int WNC14A2AInterface::sendIOTSms(const string& number, const string& message) 
{

    debugOutput(_debugUart,(char*)"+CALLED sendIOTSms(%s,%s)\n",number.c_str(), message.c_str());
    _pwnc_mutex.lock();
    int i =  _pwnc->sendSMSText((char*)number.c_str(), message.c_str());
    _pwnc_mutex.unlock();

    return i;
}

//
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//      NetworkStack API's that are not support in the WNC14A2A
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
//

int inline WNC14A2AInterface::socket_accept(nsapi_socket_t server, nsapi_socket_t *handle, SocketAddress *address) 
{
    debugOutput(_debugUart,(char*)"+CALLED socket_accept()\n");
    _errors = NSAPI_ERROR_UNSUPPORTED;
    return -1;
}

int inline WNC14A2AInterface::socket_bind(void *handle, const SocketAddress &address) 
{
    debugOutput(_debugUart,(char*)"+CALLED socket_bind()\n");
    _errors = NSAPI_ERROR_UNSUPPORTED;
    return -1;
}


int inline WNC14A2AInterface::socket_listen(void *handle, int backlog)
{
   debugOutput(_debugUart,(char*)"+CALLED socket_listen()\n");
    _errors = NSAPI_ERROR_UNSUPPORTED;
    return -1;
}