Changes to enabled on-line compiler

Revision:
0:082731ede69f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/platform/ezconnect/wnc14a2a-driver/WNC14A2AInterface/WNC14A2AInterface.cpp	Wed May 30 20:59:51 2018 +0000
@@ -0,0 +1,957 @@
+/**
+* copyright (c) 2017-2018, James Flynn
+* SPDX-License-Identifier: Apache-2.0
+*/
+
+/* 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+ 
+/**
+*   @file   WNC14A2AInterface.cpp
+*   @brief  WNC14A2A implementation of NetworkInterfaceAPI for Mbed OS
+*
+*   @author James Flynn
+*
+*   @date   1-Feb-2018
+*/
+
+#include "WNC14A2AInterface.h"
+#include <Thread.h>
+#include "mbed_events.h"
+#include "WNCIO.h"
+
+#include <string> 
+#include <ctype.h>
+
+#define WNC_DEBUG   0           //1=enable the WNC startup debug output
+                                //0=disable the WNC startup debug output
+#define STOP_ON_FE  1           //1=hang forever if a fatal error occurs
+                                //0=simply return failed response for all socket calls
+#define DISPLAY_FE  1           //1 to display the fatal error when it occurs
+                                //0 to NOT display the fatal error
+#define RESETON_FE  0           //1 to cause the MCU to reset on fatal error
+                                //0 to NOT reset the MCU
+
+/** Error Handling macros & data
+*   @brief  The macros CHK_WNCFE is used to check if a fatal error has occured. If it has
+*           then execute the action specified: fail, void, null, resume
+*
+*    CHK_WNCFE( condition-to-check, fail|void|null|resume )
+*
+*     'fail'   if you want FATAL_WNC_ERROR to be called.  
+*     'void'   if you want to execute a void return
+*     'null'   if you want to execute a null return
+*     'resume' if you simply want to resume program execution
+*
+*  There are several settings that control how FATAL_WNC_ERROR behaves:
+*      1) RESETON_FE determines if the system will reset or hang.
+*      2) DISPLAY_FE determine if an error message is generated or not
+*
+*  The DISPLAY_FE setting determines if a failure message is displayed. 
+*  If set to 1, user sees this messageo:
+*
+*      WNC FAILED @ source-file-name:source-file-line-number
+*
+*  if not set, nothing is displayed.
+*/
+
+#define FATAL_FLAG  WncController::WNC_NO_RESPONSE
+#define WNC_GOOD    WncController::WNC_ON
+
+#define RETfail return -1
+#define RETvoid return
+#define RETnull return NULL
+#define RETresume   
+
+#define DORET(x) RET##x
+
+#define TOSTR(x) #x
+#define INTSTR(x) TOSTR(x)
+#define FATAL_STR (char*)(__FILE__ ":" INTSTR(__LINE__))
+
+#if RESETON_FE == 1   //reset on fatal error
+#define MCURESET     ((*((volatile unsigned long *)0xE000ED0CU))=(unsigned long)((0x5fa<<16) | 0x04L))
+#define RSTMSG       "RESET MCU! "
+#else
+#define MCURESET
+#define RSTMSG       ""
+#endif
+
+#if DISPLAY_FE == 1  //display fatal error message
+#define PFE     {if(_debugUart)_debugUart->printf((char*)RSTMSG "\r\n>>WNC FAILED @ %s\r\n", FATAL_STR);}
+#else
+#define PFE
+#endif
+
+#if STOP_ON_FE == 1  //halt cpu on fatal error
+#define FATAL_WNC_ERROR(v)  {_fatal_err_loc=FATAL_STR;PFE;MCURESET;while(1);}
+#else
+#define FATAL_WNC_ERROR(v)  {_fatal_err_loc=FATAL_STR;PFE;DORET(v);}
+#endif
+
+#define CHK_WNCFE(x,y)    if( x ){FATAL_WNC_ERROR(y);}
+
+//
+// Define different levels of debug output
+//
+#define DBGMSG_DRV	               0x04                     //driver enter/exit info
+#define DBGMSG_EQ	               0x08                     //driver event queue info
+#define DBGMSG_SMS	               0x10                     //driver SMS info
+#define DBGMSG_ARRY                    0x20                     //dump driver arrays
+
+#define WNC14A2A_READ_TIMEOUTMS        4000                     //duration to read no data to receive in MS
+#define WNC14A2A_COMMUNICATION_TIMEOUT 100                      //how long (ms) to wait for a WNC14A2A connect response
+#define WNC_BUFF_SIZE                  1500                     //total number of bytes in a single WNC call
+#define UART_BUFF_SIZE                 4096                     //size of our internal uart buffer.. define in *.json file
+
+#define EQ_FREQ                        250                      //frequency in ms to check for Tx/Rx data
+#define EQ_FREQ_SLOW                   2000                     //frequency in ms to check when in slow monitor mode
+
+//
+// The WNC device does not generate interrutps on received data, so this software polls 
+// for data availablility.  To implement a non-blocking mode, simulate interrupts using
+// mbed OS Event Queues.  These Constants are used to manage the Rx/Tx states.
+//
+#define READ_INIT                      10
+#define READ_START                     11
+#define READ_ACTIVE                    12
+#define DATA_AVAILABLE                 13
+#define TX_IDLE                        20
+#define TX_STARTING                    21
+#define TX_ACTIVE                      22
+#define TX_COMPLETE                    23
+
+#if MBED_CONF_APP_WNC_DEBUG == true
+#define debugOutput(...)      WNC14A2AInterface::_dbOut(__VA_ARGS__)
+#define debugDump_arry(...)   WNC14A2AInterface::_dbDump_arry(__VA_ARGS__)
+#else
+#define debugOutput(...)      {/* __VA_ARGS__ */}
+#define debugDump_arry(...)   {/* __VA_ARGS__ */}
+#endif
+
+/*   Constructor
+*
+*  @brief    May be invoked with or without the debug pointer.
+*  @note     After the constructor has completed, call check 
+*  m_errors to determine if any errors occured. Possible values:
+*           NSAPI_ERROR_UNSUPPORTED 
+*           NSAPI_ERROR_DEVICE_ERROR
+*/
+WNC14A2AInterface::WNC14A2AInterface(WNCDebug *dbg) : 
+ m_wncpoweredup(0),
+ m_debug(0),
+ m_pwnc(NULL),
+ m_errors(NSAPI_ERROR_OK),
+ m_smsmoning(0),
+ _active_socket(0),
+ mdmUart(MBED_CONF_WNC14A2A_LIBRARY_WNC_TXD,MBED_CONF_WNC14A2A_LIBRARY_WNC_RXD,115200),
+ wnc_io(&mdmUart)
+{
+    _debugUart = dbg;           
+    memset(_mac_address,0x00,sizeof(_mac_address));
+    memset(_socTxS,0x00,sizeof(_socTxS));
+    memset(_socRxS,0x00,sizeof(_socRxS));
+    for( unsigned int i=0; i<WNC14A2A_SOCKET_COUNT; i++ ) {
+        _sockets[i].socket = i;
+        _sockets[i].addr = NULL;
+        _sockets[i].opened=false;
+
+        _sockets[i].connected=false;
+        _sockets[i].proto=1;
+        _socRxS[i].m_rx_socket=i;
+        _socTxS[i].m_tx_socket=i;
+        }
+}
+
+//! Standard destructor
+WNC14A2AInterface::~WNC14A2AInterface()
+{
+    if( m_pwnc )
+        delete m_pwnc;  //free the existing WncControllerK64F object
+}
+
+// - - - - - - - 
+// SMS Functions
+// - - - - - - - 
+
+char* WNC14A2AInterface::getSMSnbr( void ) 
+{
+    char * ret=NULL;
+    string iccid_str;
+    static string msisdn_str;
+
+    if( !m_pwnc ) {
+        m_errors=NSAPI_ERROR_DEVICE_ERROR;
+        return NULL;
+        }
+    CHK_WNCFE(( m_pwnc->getWncStatus() == FATAL_FLAG ), null);
+
+    _pwnc_mutex.lock();
+    if( !m_pwnc->getICCID(&iccid_str) ) {
+        _pwnc_mutex.unlock();
+        return ret;
+        }
+ 
+    if( m_pwnc->convertICCIDtoMSISDN(iccid_str, &msisdn_str) )
+         ret = (char*)msisdn_str.c_str();    
+    _pwnc_mutex.unlock();
+    return ret;
+}
+
+void WNC14A2AInterface::sms_attach(void (*callback)(IOTSMS *))
+{
+    debugOutput("ENTER/EXIT sms_attach()");
+    _sms_cb = callback;
+}
+
+void WNC14A2AInterface::sms_start(void)
+{
+    _pwnc_mutex.lock();                     
+    m_pwnc->deleteSMSTextFromMem('*');       
+    _pwnc_mutex.unlock();
+}
+
+void WNC14A2AInterface::sms_listen(uint16_t pp)
+{
+    debugOutput("ENTER sms_listen(%d)",pp);
+
+    if( m_smsmoning )
+        m_smsmoning = false;
+    if( pp < 1)
+        pp = 30;
+
+    debugOutput("setup sms_listen event queue");
+    _smsThread.start(callback(&sms_queue,&EventQueue::dispatch_forever));
+
+    sms_start();
+    sms_queue.call_every(pp*1000, mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::handle_sms_event));
+
+    m_smsmoning = true;
+    debugOutput("EXIT sms_listen()");
+}
+
+void WNC14A2AInterface::handle_sms_event()
+{
+    int msgs_available;
+    debugOutput("ENTER handle_sms_event()");
+
+    if ( _sms_cb && m_smsmoning ) {
+        _pwnc_mutex.lock();
+        msgs_available = m_pwnc->readUnreadSMSText(&m_smsmsgs, true);
+        _pwnc_mutex.unlock();
+        if( msgs_available ) {
+            debugOutput("Have %d unread texts present",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("EXIT handle_sms_event");
+}
+
+int WNC14A2AInterface::getSMS(IOTSMS **pmsg) 
+{
+    int msgs_available=0;
+
+    debugOutput("ENTER getSMS()");
+    if( !m_pwnc ) 
+        m_errors=NSAPI_ERROR_DEVICE_ERROR;
+    else{
+        CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail);
+        _pwnc_mutex.lock();
+        msgs_available = m_pwnc->readUnreadSMSText(&m_smsmsgs, true);
+        _pwnc_mutex.unlock();
+        }
+
+    if( msgs_available ) {
+        debugOutput("Have %d unread texts present",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];
+            }
+        msgs_available = m_smsmsgs.msgCount;
+        }
+    debugOutput("EXIT getSMS");
+    return msgs_available;
+}
+
+
+int WNC14A2AInterface::sendIOTSms(const string& number, const string& message) 
+{
+    debugOutput("ENTER sendIOTSms(%s,%s)",number.c_str(), message.c_str());
+
+    if( !m_pwnc ) 
+        return (m_errors=NSAPI_ERROR_DEVICE_ERROR);
+    CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail);
+
+    _pwnc_mutex.lock();
+    int i =  m_pwnc->sendSMSText((char*)number.c_str(), message.c_str());
+    _pwnc_mutex.unlock();
+
+    debugOutput("EXIT sendIOTSms(%s,%s)",number.c_str(), message.c_str());
+    return i;
+}
+
+
+// - - - - - - - - - - -
+// WNC Control Functions
+// - - - - - - - - - - -
+
+nsapi_error_t WNC14A2AInterface::connect()   //can be called with no arguments or with arguments
+{
+    debugOutput("ENTER connect(void)");
+    return connect(NULL,NULL,NULL);
+}
+
+nsapi_error_t WNC14A2AInterface::connect(const char *apn, const char *username, const char *password) 
+{
+    //
+    // GPIO Pins used to initialize the WNC parts on the Avnet WNC Shield
+    //
+    // on powerup, 0 = boot mode, 1 = normal boot
+    // 0=let modem sleep, 1=keep modem awake -- Note: pulled high on shield
+    // active high
+    // 0 = disabled (all signals high impedence, 1 = translation active
+    // WNC doesn't utilize RTS/CTS but the pin is connected
+
+    static DigitalOut  mdm_uart2_rx_boot_mode_sel(MBED_CONF_WNC14A2A_LIBRARY_WNC_RX_BOOT_SEL);
+    static DigitalOut  mdm_power_on(MBED_CONF_WNC14A2A_LIBRARY_WNC_POWER_ON);
+    static DigitalOut  mdm_wakeup_in(MBED_CONF_WNC14A2A_LIBRARY_WNC_WAKEUP);
+    static DigitalOut  mdm_reset(MBED_CONF_WNC14A2A_LIBRARY_WNC_RESET);
+    static DigitalOut  shield_3v3_1v8_sig_trans_ena(MBED_CONF_WNC14A2A_LIBRARY_WNC_LVLTRANSLATOR);
+    static DigitalOut  mdm_uart1_cts(MBED_CONF_WNC14A2A_LIBRARY_WNC_CTS);
+
+    //! associations for the controller class to use. Order of pins is critical.
+    static WncControllerK64F_fk::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
+    };
+
+    debugOutput("ENTER connect(apn,user,pass)");
+
+    if( m_pwnc == NULL ) {
+        m_pwnc = new WncControllerK64F_fk::WncControllerK64F(&wncPinList, &wnc_io, _debugUart);
+        if( !m_pwnc ) {
+            debugOutput("FAILED to open WncControllerK64!");
+            m_errors = NSAPI_ERROR_DEVICE_ERROR;
+            return NSAPI_ERROR_NO_MEMORY;
+            }
+        CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail);
+        #if MBED_CONF_APP_WNC_DEBUG == true
+        m_pwnc->enableDebug( (MBED_CONF_APP_WNC_DEBUG_SETTING&1), (MBED_CONF_APP_WNC_DEBUG_SETTING&2) );
+        #endif
+        }
+
+    _eqThread.start(callback(&wnc_queue,&EventQueue::dispatch_forever));
+
+    if (!apn)
+        apn = "m2m.com.attz";
+
+    _pwnc_mutex.lock();
+    if (!m_wncpoweredup) {
+        debugOutput("call powerWncOn(%s,40)",apn);
+        m_wncpoweredup=m_pwnc->powerWncOn(apn,40);
+        m_errors = m_wncpoweredup? 1:0;
+        }
+    else {          //powerWncOn already called, set a new APN
+        debugOutput("set APN=%s",apn);
+        m_errors = m_pwnc->setApnName(apn)? 1:0;
+        }
+
+    m_errors |= m_pwnc->getWncNetworkingStats(&myNetStats)? 2:0;
+    _pwnc_mutex.unlock();
+
+    debugOutput("EXIT connect (%02X)",m_errors);
+    return (!m_errors)? NSAPI_ERROR_NO_CONNECTION : NSAPI_ERROR_OK;
+}
+
+const char* WNC14A2AInterface::getWNCRev(void)
+{
+    if( m_pwnc ) {
+        const char * str = m_pwnc->getFirmRev();
+        return &str[12];
+        }
+    else
+        return NULL;
+}
+
+
+const char *WNC14A2AInterface::get_ip_address()
+{
+    const char *ptr=NULL; 
+
+    if( !m_pwnc ) {
+        m_errors=NSAPI_ERROR_DEVICE_ERROR;
+        return ptr;
+        }
+    CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), null);
+
+    _pwnc_mutex.lock();
+    if ( m_pwnc->getWncNetworkingStats(&myNetStats) ) {
+        _pwnc_mutex.unlock();
+        ptr = &myNetStats.ip[0];
+        }
+    else{
+        _pwnc_mutex.unlock();
+        m_errors=NSAPI_ERROR_NO_CONNECTION;
+        }
+    return ptr;
+}
+ 
+const char *WNC14A2AInterface::get_mac_address()
+{
+    string mac, str;
+    debugOutput("ENTER get_mac_address()");
+
+    if( m_pwnc ) {
+        CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), null);
+        _pwnc_mutex.lock();
+        if( m_pwnc->getICCID(&str) ) {
+            _pwnc_mutex.unlock();
+            mac = str.substr(3,20);
+            mac[2]=mac[5]=mac[8]=mac[11]=mac[14]=':';
+            strncpy(_mac_address, mac.c_str(), mac.length());
+            debugOutput("EXIT get_mac_address() - %s",_mac_address);
+            return _mac_address;
+            }
+        _pwnc_mutex.unlock();
+        }
+    debugOutput("EXIT get_mac_address() - NULL");
+    return NULL;
+}
+
+NetworkStack *WNC14A2AInterface::get_stack() {
+    debugOutput("ENTER/EXIT get_stack()");
+    return this;
+}
+
+nsapi_error_t WNC14A2AInterface::disconnect() 
+{
+    debugOutput("ENTER/EXIT disconnect()");
+    return NSAPI_ERROR_OK;
+}
+
+nsapi_error_t WNC14A2AInterface::set_credentials(const char *apn, const char *username, const char *password) 
+{
+
+    m_errors=NSAPI_ERROR_OK;
+    debugOutput("ENTER set_credentials()");
+
+    if( !m_pwnc ) 
+        return (m_errors=NSAPI_ERROR_DEVICE_ERROR);
+    CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail);
+        
+    if( !apn )
+        return (m_errors=NSAPI_ERROR_PARAMETER);
+
+    _pwnc_mutex.lock();
+    if( !m_pwnc->setApnName(apn) )
+        m_errors=NSAPI_ERROR_DEVICE_ERROR;
+    _pwnc_mutex.unlock();
+    debugOutput("EXIT set_credentials()");
+    return m_errors;
+}
+
+bool WNC14A2AInterface::registered()
+{
+    debugOutput("ENTER registered()");
+    m_errors=NSAPI_ERROR_OK;
+
+    if( !m_pwnc ) {
+        return (m_errors=NSAPI_ERROR_DEVICE_ERROR);
+        }
+    CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail);
+
+    _pwnc_mutex.lock();
+    if ( m_pwnc->getWncStatus() != WNC_GOOD )
+        m_errors=NSAPI_ERROR_NO_CONNECTION;
+    _pwnc_mutex.unlock();
+
+    debugOutput("EXIT registered()");
+    return (m_errors==NSAPI_ERROR_OK);
+}
+
+
+void WNC14A2AInterface::doDebug( int v )
+{
+    #if MBED_CONF_APP_WNC_DEBUG == true
+    m_debug= v;
+    debugOutput("SET debug flag to 0x%02X",v);
+    #endif
+}
+
+/** function to dump a user provided array.
+*
+* @author James Flynn
+* @param  data    pointer to the data array to dump
+* @param  size    number of bytes to dump
+* @return void
+* @date 1-Feb-2018
+*/
+void WNC14A2AInterface::_dbDump_arry( const uint8_t* data, unsigned int size )
+{
+    #if MBED_CONF_APP_WNC_DEBUG == true
+    char buffer[256];
+    unsigned int i, k;
+
+    if( _debugUart != NULL && (m_debug & DBGMSG_ARRY) ) {
+        for (i=0; i<size; i+=16) {
+            sprintf(buffer,"[WNC Driver]:0x%04X: ",i);
+            _debugUart->puts(buffer);
+            for (k=0; k<16; k++) {
+                sprintf(buffer, "%02X ", data[i+k]);
+                _debugUart->puts(buffer);
+                }
+            _debugUart->puts(" -- ");
+            for (k=0; k<16; k++) {
+                sprintf(buffer, "%2c", isprint(data[i+k])? data[i+k]:'.');
+                _debugUart->puts(buffer);
+                }
+            _debugUart->puts("\n\r");
+            }
+        }
+    #endif
+}
+
+void WNC14A2AInterface::_dbOut(const char *format, ...) 
+{
+    #if MBED_CONF_APP_WNC_DEBUG == true
+    char buffer[256];
+
+    sprintf(buffer,"[WNC Driver]: ");
+    if( _debugUart != NULL && (m_debug & (DBGMSG_DRV|DBGMSG_EQ|DBGMSG_SMS)) ) {
+        va_list args;
+        va_start (args, format);
+        _debugUart->puts(buffer);
+        if( m_debug & DBGMSG_DRV )
+            vsnprintf(buffer, sizeof(buffer), format, args);
+        if( m_debug & DBGMSG_EQ )
+            vsnprintf(buffer, sizeof(buffer), format, args);
+        if( m_debug & DBGMSG_SMS )
+            vsnprintf(buffer, sizeof(buffer), format, args);
+        _debugUart->puts(buffer);
+        _debugUart->putc('\n');
+        va_end (args);
+        }
+    #endif
+}
+
+// - - - - - - - - - - - - - - -
+// WNC Socket Based operatioins
+// - - - - - - - - - - - - - - -
+
+nsapi_error_t WNC14A2AInterface::gethostbyname(const char* name, SocketAddress *address, nsapi_version_t version)
+{
+    nsapi_error_t ret = NSAPI_ERROR_OK;
+    char ipAddrStr[25];
+
+    debugOutput("ENTER gethostbyname(); IP=%s; PORT=%d; URL=%s;", address->get_ip_address(), address->get_port(), name);
+
+    if( !m_pwnc ) 
+        return (m_errors=NSAPI_ERROR_DEVICE_ERROR);
+    CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail);
+
+    memset(ipAddrStr,0x00,sizeof(ipAddrStr));
+
+    //Execute DNS query.  
+    _pwnc_mutex.lock();
+    if( !m_pwnc->resolveUrl(_active_socket, name) )  
+        ret = m_errors = NSAPI_ERROR_DEVICE_ERROR;
+
+    //Get IP address that the URL was resolved to
+    if( !m_pwnc->getIpAddr(_active_socket, ipAddrStr) )
+        ret = m_errors = NSAPI_ERROR_DEVICE_ERROR;
+    _pwnc_mutex.unlock();
+
+    address->set_ip_address(ipAddrStr);
+
+    debugOutput("EXIT gethostbyname(); IP=%s; PORT=%d; URL=%s;", address->get_ip_address(), address->get_port(), name);
+    return (m_errors = ret);
+}
+
+int WNC14A2AInterface::socket_open(void **handle, nsapi_protocol_t proto) 
+{
+    unsigned int i;
+
+    debugOutput("ENTER socket_open()");
+
+    //find the next available socket...
+    for( i=0; i<WNC14A2A_SOCKET_COUNT; i++ )
+        if( !_sockets[i].opened )
+            break;
+
+    if( i == WNC14A2A_SOCKET_COUNT ) {
+        m_errors=NSAPI_ERROR_NO_SOCKET;
+        return -1;
+        }
+
+    _sockets[i].socket = i;            //save index later
+    _sockets[i].opened = true;         
+    _sockets[i].connected=false;
+    _sockets[i].proto = (proto==NSAPI_UDP)?0:1;
+    _sockets[i]._callback = NULL;
+    _sockets[i]._cb_data = NULL;         
+
+    _socRxS[i].m_rx_wnc_state = READ_START;
+    _socRxS[i].m_rx_disTO = false;
+    _socTxS[i].m_tx_wnc_state = TX_IDLE;
+
+    *handle = &_sockets[i];
+    
+    debugOutput("EXIT socket_open; Socket=%d, OPEN=%s, protocol =%s",
+                i, _sockets[i].opened?"YES":"NO", (!_sockets[i].proto)?"UDP":"TCP");
+    
+    return (m_errors = NSAPI_ERROR_OK);
+}
+
+int WNC14A2AInterface::socket_connect(void *handle, const SocketAddress &address) 
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;   
+    int err = 0;
+
+    debugOutput("ENTER socket_connect(); Socket=%d; IP=%s; PORT=%d;", wnc->socket, address.get_ip_address(), address.get_port());
+
+    if (!wnc->opened ) 
+        return (m_errors = NSAPI_ERROR_NO_SOCKET);
+
+    wnc->addr = address;
+                                
+    _pwnc_mutex.lock();
+    if( wnc->url.empty() ) 
+        err = !m_pwnc->openSocketIpAddr(wnc->socket, address.get_ip_address(), address.get_port(), wnc->proto, WNC14A2A_COMMUNICATION_TIMEOUT);
+     else 
+        err = !m_pwnc->openSocketUrl(wnc->socket, wnc->url.c_str(), wnc->addr.get_port(), wnc->proto);
+    _pwnc_mutex.unlock();
+
+    if( !err ) {
+        wnc->connected = true;
+        _socRxS[wnc->socket].m_rx_wnc_state = READ_START;
+        _socTxS[wnc->socket].m_tx_wnc_state = TX_IDLE;
+
+        if( wnc->_callback != NULL ) 
+            wnc->_callback( wnc->_cb_data );
+        }
+
+    return err;
+}
+
+int WNC14A2AInterface::socket_close(void *handle)
+{
+    WNCSOCKET *wnc = (WNCSOCKET*)handle;
+    RXEVENT *rxsock;
+    TXEVENT *txsock;
+    bool err = false;
+
+    debugOutput("ENTER socket_close()");
+
+    rxsock = &_socRxS[wnc->socket];
+    txsock = &_socTxS[wnc->socket];
+
+    txsock->m_tx_wnc_state = TX_IDLE;               //reset TX state
+    if( rxsock->m_rx_wnc_state != READ_START ) {    //reset RX state
+        rxsock->m_rx_disTO=false;
+        while( rxsock->m_rx_wnc_state !=  DATA_AVAILABLE ) 
+            wait(1);  //someone called close while a read was happening
+        }
+
+    _pwnc_mutex.lock();
+    if( !m_pwnc->closeSocket(wnc->socket) ) {
+        m_errors = NSAPI_ERROR_DEVICE_ERROR;
+        err = true;
+        }
+    _pwnc_mutex.unlock();
+
+    if( !err ) {
+        wnc->opened   = false;       //no longer in use
+        wnc->addr     = NULL;        //not open
+        wnc->connected= false;
+        wnc->proto    = 1;           //assume TCP for now
+        m_errors      = NSAPI_ERROR_OK;
+        wnc->_cb_data = NULL;
+        wnc->_callback= NULL;
+        }
+
+    debugOutput("EXIT socket_close()");
+    return err;
+}
+
+
+void WNC14A2AInterface::socket_attach(void *handle, void (*callback)(void *), void *data)
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+
+    debugOutput("ENTER/EXIT socket_attach()");
+    wnc->_callback = callback;
+    wnc->_cb_data = data;
+}
+
+int WNC14A2AInterface::socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size)
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+    
+    debugOutput("ENTER socket_sendto()");
+    
+    if (!wnc->connected) {
+       int err = socket_connect(wnc, address);
+       if (err < 0) 
+           return err;
+       }
+    wnc->addr = address;
+
+    debugOutput("EXIT socket_sendto()");
+    return socket_send(wnc, data, size);
+}
+
+int WNC14A2AInterface::socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size)
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+    debugOutput("ENTER socket_recvfrom()");
+
+    if (!wnc->connected) {
+       debugOutput("need to open a WNC socket first");
+       int err = socket_connect(wnc, *address);
+       if (err < 0) 
+           return err;
+       }
+
+    int ret = socket_recv(wnc, (char *)buffer, size);
+    if (ret >= 0 && address) 
+        *address = wnc->addr;
+    debugOutput("EXIT socket_recvfrom()");
+    return ret;
+}
+
+
+int inline WNC14A2AInterface::socket_accept(nsapi_socket_t server, nsapi_socket_t *handle, SocketAddress *address) 
+{
+    debugOutput("ENTER/EXIT socket_accept() -- not supported");
+    m_errors = NSAPI_ERROR_UNSUPPORTED;
+    return -1;
+}
+
+int inline WNC14A2AInterface::socket_bind(void *handle, const SocketAddress &address) 
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+
+    debugOutput("ENTER/EXIT socket_bind(), use address '%s', port %d", address.get_ip_address(),address.get_port());
+    _socRxS[wnc->socket].m_rx_disTO=true;  //for us, simply disable the Rx timeout to keep monitoring for data
+    return (m_errors = NSAPI_ERROR_OK);
+}
+
+
+int inline WNC14A2AInterface::socket_listen(void *handle, int backlog)
+{
+   debugOutput("ENTER/EXIT socket_listen() -- not supported");
+    m_errors = NSAPI_ERROR_UNSUPPORTED;
+    return -1;
+}
+
+
+int WNC14A2AInterface::socket_send(void *handle, const void *data, unsigned size) 
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+    TXEVENT *txsock;
+
+    debugOutput("ENTER socket_send() send %d bytes",size);
+    txsock = &_socTxS[wnc->socket];
+
+    if( size < 1 || data == NULL )  // should never happen but have seen it
+        return 0; 
+
+    switch( txsock->m_tx_wnc_state ) {
+        case TX_IDLE:
+            txsock->m_tx_wnc_state = TX_STARTING;
+            debugDump_arry((const uint8_t*)data,size);
+            txsock->m_tx_dptr      = (uint8_t*)data;
+            txsock->m_tx_orig_size = size;
+            txsock->m_tx_req_size  = (uint32_t)size;
+            txsock->m_tx_total_sent= 0;
+            txsock->m_tx_callback  = wnc->_callback;
+            txsock->m_tx_cb_data   = wnc->_cb_data;
+
+            if( txsock->m_tx_req_size > UART_BUFF_SIZE ) 
+                txsock->m_tx_req_size= UART_BUFF_SIZE;
+
+            if( !tx_event(txsock) ) {   //if we didn't sent all the data at once, continue in background
+                txsock->m_tx_wnc_state = TX_ACTIVE;
+                wnc_queue.call_in(EQ_FREQ,mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::wnc_eq_event));
+                return NSAPI_ERROR_WOULD_BLOCK;
+                }
+            // fall through 
+
+        case TX_COMPLETE:
+            debugOutput("EXIT socket_send(), sent %d bytes", txsock->m_tx_total_sent);
+            txsock->m_tx_wnc_state = TX_IDLE;
+            return txsock->m_tx_total_sent;
+
+        case TX_ACTIVE:
+        case TX_STARTING:
+            return NSAPI_ERROR_WOULD_BLOCK;
+
+        default:
+            debugOutput("EXIT socket_send(), NSAPI_ERROR_DEVICE_ERROR");
+            return (m_errors = NSAPI_ERROR_DEVICE_ERROR);
+        }
+}
+
+int WNC14A2AInterface::tx_event(TXEVENT *ptr)
+{
+    debugOutput("ENTER tx_event(), socket %d",ptr->m_tx_socket);
+
+    _pwnc_mutex.lock();
+    if( m_pwnc->write(ptr->m_tx_socket, ptr->m_tx_dptr, ptr->m_tx_req_size) ) 
+        ptr->m_tx_total_sent += ptr->m_tx_req_size;
+    _pwnc_mutex.unlock();
+    
+    if( ptr->m_tx_total_sent < ptr->m_tx_orig_size ) {
+        ptr->m_tx_dptr += ptr->m_tx_req_size;
+        ptr->m_tx_req_size = ptr->m_tx_orig_size-ptr->m_tx_total_sent;
+
+        if( ptr->m_tx_req_size > UART_BUFF_SIZE) 
+            ptr->m_tx_req_size= UART_BUFF_SIZE;
+
+        debugOutput("EXIT tx_event(), send %d more bytes.",ptr->m_tx_req_size);
+        return 0;
+        }
+    debugOutput("EXIT tx_event, socket %d, data sent",ptr->m_tx_socket);
+    ptr->m_tx_wnc_state = TX_COMPLETE;
+    if( ptr->m_tx_callback != NULL ) 
+        ptr->m_tx_callback( ptr->m_tx_cb_data );
+    ptr->m_tx_cb_data = NULL; 
+    ptr->m_tx_callback = NULL;
+
+    return 1;
+}
+
+int WNC14A2AInterface::socket_recv(void *handle, void *data, unsigned size) 
+{
+    WNCSOCKET *wnc = (WNCSOCKET *)handle;
+    RXEVENT *rxsock;
+
+    rxsock = &_socRxS[wnc->socket];
+    debugOutput("ENTER socket_recv(), socket %d, request %d bytes",wnc->socket, size);
+
+    if( size < 1 || data == NULL ) { // should never happen
+        return 0; 
+        }
+
+    switch( rxsock->m_rx_wnc_state ) {
+        case READ_START:  //need to start a read sequence of events
+            rxsock->m_rx_wnc_state= READ_INIT;
+            rxsock->m_rx_dptr     = (uint8_t*)data;
+            rxsock->m_rx_req_size = (uint32_t)size;
+            rxsock->m_rx_total_cnt= 0;
+            rxsock->m_rx_timer    = 0;
+            rxsock->m_rx_return_cnt=0;
+
+            if( rxsock->m_rx_req_size > WNC_BUFF_SIZE) 
+                rxsock->m_rx_req_size= WNC_BUFF_SIZE;
+                
+            rxsock->m_rx_callback = wnc->_callback;
+            rxsock->m_rx_cb_data  = wnc->_cb_data;
+
+            if( !rx_event(rxsock) ){
+                rxsock->m_rx_wnc_state = READ_ACTIVE;
+                wnc_queue.call_in(EQ_FREQ,mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::wnc_eq_event));
+                return NSAPI_ERROR_WOULD_BLOCK;
+                }
+            // fall through 
+
+        case DATA_AVAILABLE:
+            debugOutput("EXIT socket_recv(),socket %d, return %d bytes",wnc->socket, rxsock->m_rx_return_cnt);
+            debugDump_arry((const uint8_t*)data,rxsock->m_rx_return_cnt);
+            rxsock->m_rx_wnc_state = READ_START;
+            return rxsock->m_rx_return_cnt;
+
+        case READ_ACTIVE:
+        case READ_INIT:
+            rxsock->m_rx_timer    = 0;  //reset the time-out timer
+            return NSAPI_ERROR_WOULD_BLOCK;
+
+        default:
+            debugOutput("EXIT socket_recv(), NSAPI_ERROR_DEVICE_ERROR");
+            return (m_errors = NSAPI_ERROR_DEVICE_ERROR);
+        }
+}
+
+
+int WNC14A2AInterface::rx_event(RXEVENT *ptr)
+{
+    debugOutput("ENTER rx_event() for socket %d", ptr->m_rx_socket);
+    _pwnc_mutex.lock();
+    int cnt = m_pwnc->read(ptr->m_rx_socket, ptr->m_rx_dptr,  ptr->m_rx_req_size);
+    _pwnc_mutex.unlock();
+
+    if( cnt ) {  //got data, return it to the caller
+        debugOutput("data received on socket %d, cnt=%d", ptr->m_rx_socket,cnt);
+        ptr->m_rx_wnc_state = DATA_AVAILABLE;
+        ptr->m_rx_return_cnt = cnt;
+        if( ptr->m_rx_callback != NULL ) 
+            ptr->m_rx_callback( ptr->m_rx_cb_data );
+        ptr->m_rx_cb_data = NULL; 
+        ptr->m_rx_callback = NULL;
+        return 1;
+        }
+    if( ++ptr->m_rx_timer > (WNC14A2A_READ_TIMEOUTMS/EQ_FREQ) && !ptr->m_rx_disTO ) {  //timed out waiting, return 0 to caller
+        debugOutput("EXIT rx_event(), rx data TIME-OUT!");
+        ptr->m_rx_wnc_state = DATA_AVAILABLE;
+        ptr->m_rx_return_cnt = 0;
+        if( ptr->m_rx_callback != NULL ) 
+            ptr->m_rx_callback( ptr->m_rx_cb_data );
+        ptr->m_rx_cb_data = NULL; 
+        ptr->m_rx_callback = NULL;
+        return 1;
+        }
+
+    debugOutput("EXIT rx_event(), socket %d, sechedule for more data.",ptr->m_rx_socket);
+    return 0;
+}
+
+void WNC14A2AInterface::wnc_eq_event()
+{
+    int done = 1;
+    bool goSlow = true;
+
+    for( unsigned int i=0; i<WNC14A2A_SOCKET_COUNT; i++ ) {
+        if( _socRxS[i].m_rx_wnc_state == READ_ACTIVE || _socRxS[i].m_rx_disTO) {
+            done &= rx_event(&_socRxS[i]);
+            goSlow &= ( _socRxS[i].m_rx_timer > ((WNC14A2A_READ_TIMEOUTMS/EQ_FREQ)*(EQ_FREQ_SLOW/EQ_FREQ)) );
+
+            if( goSlow ) 
+                _socRxS[i].m_rx_timer = (WNC14A2A_READ_TIMEOUTMS/EQ_FREQ)*(EQ_FREQ_SLOW/EQ_FREQ);
+            }
+
+        if( _socTxS[i].m_tx_wnc_state == TX_ACTIVE ) {
+            goSlow = false;
+            debugOutput("CALL TX_event() for socket %d", i);
+            done &= tx_event(&_socTxS[i]);
+            }
+        }
+
+    if( !done )  
+        wnc_queue.call_in((goSlow?EQ_FREQ_SLOW:EQ_FREQ),mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::wnc_eq_event));
+}