wifi test
Dependencies: X_NUCLEO_IKS01A2 mbed-http
easy-connect/wnc14a2a-driver/WNC14A2AInterface/WNC14A2AInterface.cpp
- Committer:
- JMF
- Date:
- 2018-09-05
- Revision:
- 0:24d3eb812fd4
File content as of revision 0:24d3eb812fd4:
/**
* 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:
//jmf 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));
}