Jim Flynn
/
aws-iot-device-sdk-mbed-c
Changes to enabled on-line compiler
Diff: platform/ezconnect/BG96-driver/BG96Interface.cpp
- Revision:
- 0:082731ede69f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/BG96Interface.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,957 @@ +/** +* copyright (c) 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 BG96Interface.cpp +* @brief BG96 NetworkInterfaceAPI implementation for Mbed OS v5.x +* +* @author James Flynn +* +* @date 1-April-2018 +*/ + +#include <ctype.h> +#include "mbed.h" +#include "BG96.h" +#include "BG96Interface.h" + +// +// The driver uses a simple/basic state machine to manage receiving and transmitting +// data. These are the states that are used +// +#define READ_INIT 10 //initialize for a read state +#define READ_START 11 //start reading from the BG96 +#define READ_ACTIVE 12 //a read is on-going/active +#define READ_DOCB 13 //need to perform a call-back on the socket +#define DATA_AVAILABLE 14 //indicates that rx data is available + +#define TX_IDLE 20 //indicates that no data is bing TX'd +#define TX_STARTING 21 //indicates that TX data is starting +#define TX_ACTIVE 22 //indicates that TX data is being sent +#define TX_COMPLETE 23 //all TX data has been sent +#define TX_DOCB 24 //indicatew we need to exeucte the call-back + +#define BG96_READ_TIMEOUTMS 30000 //read timeout in MS +#define EQ_FREQ 50 //frequency in ms to check for Tx/Rx data +#define EQ_FREQ_SLOW 2000 //frequency in ms to check when in slow monitor mode + +#define EVENT_COMPLETE 0 //signals when a TX/RX event is complete +#define EVENT_GETMORE 0x01 //signals when we need additional TX/RX data + +// +// The following are only used when debug is eabled. +// + +#if MBED_CONF_APP_BG96_DEBUG == true +#define debugOutput(...) _dbOut(__VA_ARGS__) +#define debugDump_arry(...) _dbDump_arry(__VA_ARGS__) + +#define dbgIO_lock dbgout_mutex.lock(); //used to prevent stdio output over-writes +#define dbgIO_unlock dbgout_mutex.unlock(); //when executing--including BG96 debug output + +/** functions to output debug data--------------------------- +* +* @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 BG96Interface::_dbDump_arry( const uint8_t* data, unsigned int size ) +{ + unsigned int i, k; + + dbgIO_lock; + if( g_debug & DBGMSG_ARRY ) { + for (i=0; i<size; i+=16) { + printf("[BG96 Driver]:0x%04X: ",i); + for (k=0; k<16; k++) { + if( (i+k)<size ) + printf("%02X ", data[i+k]); + else + printf(" "); + } + printf(" "); + for (k=0; k<16; k++) { + if( (i+k)<size ) + printf("%c", isprint(data[i+k])? data[i+k]:'.'); + } + printf("\n\r"); + } + } + dbgIO_unlock; +} + +void BG96Interface::_dbOut(int who, const char* format, ...) +{ + char buffer[256]; + dbgIO_lock; + if( who & (g_debug & (DBGMSG_DRV|DBGMSG_EQ)) ) { + va_list args; + va_start (args, format); + printf("[BG96 Driver]: "); + vsnprintf(buffer, sizeof(buffer), format, args); + printf("%s",buffer); + printf("\n"); + va_end (args); + } + dbgIO_unlock; +} + +#else + +#define dbgIO_lock +#define dbgIO_unlock +#define debugOutput(...) {/* __VA_ARGS__ */} +#define debugDump_arry(...) {/* __VA_ARGS__ */} + +#endif //MBED_CONF_APP_BG96_DEBUG == true + + +/** -------------------------------------------------------- +* @brief BG96Interface constructor +* @param none +* @retval none +*/ +BG96Interface::BG96Interface(void) : + g_isInitialized(NSAPI_ERROR_NO_CONNECTION), + g_bg96_queue_id(-1), + scheduled_events(0), + _BG96(false) +{ + for( int i=0; i<BG96_SOCKET_COUNT; i++ ) { + g_sock[i].id = -1; + g_sock[i].disTO = false; + g_sock[i].connected = false; + g_socRx[i].m_rx_state = READ_START; + g_socRx[i].m_rx_disTO = false; + g_socTx[i].m_tx_state = TX_IDLE; + } + #if MBED_CONF_APP_BG96_DEBUG == true + g_debug=0; + #endif +} + +/** ---------------------------------------------------------- +* @brief BG96Interface destructor +* @param none +* @retval none +*/ +BG96Interface::~BG96Interface() +{ +} + +/** ---------------------------------------------------------- +* @brief network connect +* connects to Access Point, can be called with no argument +* or arguments. If none, use default APN. +* @param ap: Access Point Name (APN) Name String +* pass_word: Password String for AP +* username: username to use for AP +* @retval NSAPI Error Type +*/ +nsapi_error_t BG96Interface::connect(void) +{ + nsapi_error_t ret = NSAPI_ERROR_OK; + debugOutput(DBGMSG_DRV,"BG96Interface::connect(void) ENTER."); + if( g_isInitialized == NSAPI_ERROR_NO_CONNECTION ) + ret = connect(DEFAULT_APN, NULL, NULL); + + return (ret == NSAPI_ERROR_NO_CONNECTION); +} + +nsapi_error_t BG96Interface::connect(const char *apn, const char *username, const char *password) +{ + Timer t; + bool ok=false; + + debugOutput(DBGMSG_DRV,"BG96Interface::connect(%s,%s,%s) ENTER",apn,username,password); + + if( g_isInitialized == NSAPI_ERROR_IS_CONNECTED ) + ok = disconnect(); + + t.start(); + dbgIO_lock; + while(t.read_ms() < BG96_MISC_TIMEOUT && !ok) + ok = _BG96.startup(); + dbgIO_unlock; + + if( ok && g_bg96_queue_id == -1) + g_bg96_queue_id = _bg96_monitor.start(callback(&_bg96_queue, &EventQueue::dispatch_forever)); + + debugOutput(DBGMSG_DRV,"BG96Interface::connect EXIT"); + + return ok? set_credentials(apn, username, password) : NSAPI_ERROR_DEVICE_ERROR; +} + +/** Set the cellular network credentials -------------------- +* +* @param apn Optional, APN of network +* @param user Optional, username --not used-- +* @param pass Optional, password --not used-- +* @return nsapi_error_t +*/ +nsapi_error_t BG96Interface::set_credentials(const char *apn, const char *username, const char *password) +{ + debugOutput(DBGMSG_DRV,"BG96Interface::set_credentials ENTER/EXIT, APN=%s, USER=%s, PASS=%s",apn,username,password); + g_isInitialized = (_BG96.connect((char*)apn, (char*)username, (char*)password)==true)? NSAPI_ERROR_NO_CONNECTION : NSAPI_ERROR_IS_CONNECTED; + + return g_isInitialized; +} + +/**---------------------------------------------------------- +* @brief network disconnect +* disconnects from APN +* @param none +* @return nsapi_error_t +*/ +int BG96Interface::disconnect(void) +{ + nsapi_error_t ret; + + debugOutput(DBGMSG_DRV,"BG96Interface::disconnect ENTER"); + _bg96_queue.cancel(g_bg96_queue_id); + g_bg96_queue_id = -1; + g_isInitialized = NSAPI_ERROR_NO_CONNECTION; + dbgIO_lock; + ret = _BG96.disconnect(); + dbgIO_unlock; + debugOutput(DBGMSG_DRV,"BG96Interface::disconnect EXIT"); + return ret? NSAPI_ERROR_OK:NSAPI_ERROR_DEVICE_ERROR; +} + +/**---------------------------------------------------------- +* @brief Get the local IP address +* @param none +* @retval Null-terminated representation of the local IP address +* or null if not yet connected +*/ +const char *BG96Interface::get_ip_address() +{ + static char ip[25]; + debugOutput(DBGMSG_DRV,"BG96Interface::get_ip_address ENTER"); + dbgIO_lock; + const char* ptr = _BG96.getIPAddress(ip); + dbgIO_unlock; + + debugOutput(DBGMSG_DRV,"BG96Interface::get_ip_address EXIT"); + return ptr; +} + +/**---------------------------------------------------------- +* @brief Get the MAC address +* @param none +* @retval Null-terminated representation of the MAC address +* or null if not yet connected +*/ +const char *BG96Interface::get_mac_address() +{ + static char mac[25]; + debugOutput(DBGMSG_DRV,"BG96Interface::get_mac_address ENTER"); + dbgIO_lock; + const char* ptr = _BG96.getMACAddress(mac); + dbgIO_unlock; + debugOutput(DBGMSG_DRV,"BG96Interface::get_mac_address EXIT"); + return ptr; +} + +/**---------------------------------------------------------- +* @brief Get Module Firmware Information +* @param none +* @retval Null-terminated representation of the MAC address +* or null if error +*/ +const char* BG96Interface::getRevision(void) +{ + static char str[40]; + dbgIO_lock; + const char* ptr = _BG96.getRev(str); + dbgIO_unlock; + return ptr; +} + +/**---------------------------------------------------------- +* @brief attach function/callback to the socket +* Not used +* @param handle: Pointer to handle +* callback: callback function pointer +* data: pointer to data +* @retval none +*/ +void BG96Interface::socket_attach(void *handle, void (*callback)(void *), void *data) +{ + BG96SOCKET *sock = (BG96SOCKET*)handle; + debugOutput(DBGMSG_DRV,"ENTER/EXIT socket_attach(), socket %d attached",sock->id); + sock->_callback = callback; + sock->_data = data; +} + + +/**---------------------------------------------------------- +* @brief bind to a port number and address +* @param handle: Pointer to socket handle +* proto: address to bind to +* @return nsapi_error_t +*/ +int BG96Interface::socket_bind(void *handle, const SocketAddress &address) +{ + debugOutput(DBGMSG_DRV,"BG96Interface::socket_bind ENTER/EXIT"); + return socket_listen(handle, 1); +} + +/**---------------------------------------------------------- +* @brief start listening on a port and address +* @param handle: Pointer to handle +* backlog: not used (always value is 1) +* @return nsapi_error_t +*/ +int BG96Interface::socket_listen(void *handle, int backlog) +{ + BG96SOCKET *socket = (BG96SOCKET *)handle; + nsapi_error_t ret = NSAPI_ERROR_OK; + + backlog = backlog; //avoid unused error from compiler + debugOutput(DBGMSG_DRV,"BG96Interface::socket_listen, socket %d listening %s ENTER", + socket->id, socket->connected? "YES":"NO"); + if( !socket->connected ) { + socket->disTO = true; + _eq_schedule(); + } + else + ret = NSAPI_ERROR_NO_CONNECTION; + + debugOutput(DBGMSG_DRV,"BG96Interface::socket_listen EXIT"); + return ret; +} + +/**---------------------------------------------------------- +* @brief Set the socket options +* Not used +* @param handle: Pointer to handle +* level: SOL_SOCKET +* optname: option name +* optval: pointer to option value +* optlen: option length +* @return nsapi_error_t +*/ +int BG96Interface::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + + debugOutput(DBGMSG_DRV,"BG96Interface::setsockopt ENTER/EXIT"); + if (!optlen || !sock) { + return NSAPI_ERROR_PARAMETER; + } + + if (level == NSAPI_SOCKET && sock->proto == NSAPI_TCP) { + switch (optname) { + case NSAPI_REUSEADDR: + case NSAPI_KEEPIDLE: + case NSAPI_KEEPINTVL: + case NSAPI_LINGER: + case NSAPI_SNDBUF: + case NSAPI_ADD_MEMBERSHIP: + case NSAPI_DROP_MEMBERSHIP: + case NSAPI_KEEPALIVE: + return NSAPI_ERROR_UNSUPPORTED; + + case NSAPI_RCVBUF: + if (optlen == sizeof(void *)) { + sock->dptr_last = (void*)optval; + sock->dptr_size = (unsigned)optlen; + return NSAPI_ERROR_OK; + } + return NSAPI_ERROR_PARAMETER; + } + } + return NSAPI_ERROR_UNSUPPORTED; +} + +/**---------------------------------------------------------- +* @brief Get the socket options +* Not used +* @param handle: Pointer to handle +* level: SOL_SOCKET +* optname: option name +* optval: pointer to option value +* optlen: pointer to option length +* @return nsapi_error_t +*/ +int BG96Interface::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + + debugOutput(DBGMSG_DRV,"BG96Interface::getsockopt ENTER/EXIT"); + if (!optval || !optlen || !sock) { + return NSAPI_ERROR_PARAMETER; + } + + if (level == NSAPI_SOCKET && sock->proto == NSAPI_TCP) { + switch (optname) { + case NSAPI_REUSEADDR: + case NSAPI_KEEPALIVE: + case NSAPI_KEEPIDLE: + case NSAPI_KEEPINTVL: + case NSAPI_LINGER: + case NSAPI_SNDBUF: + case NSAPI_ADD_MEMBERSHIP: + case NSAPI_DROP_MEMBERSHIP: + return NSAPI_ERROR_UNSUPPORTED; + + case NSAPI_RCVBUF: + optval = sock->dptr_last; + *optlen = sock->dptr_size; + return NSAPI_ERROR_OK; + } + } + return NSAPI_ERROR_UNSUPPORTED; +} + +/**---------------------------------------------------------- +* @brief helpe function to set debug levels. Only enabled +* if debug flag set during compilation +* @param int = value to set debug flag to +* @retval none +*/ +void BG96Interface::doDebug( int v ) +{ + #if MBED_CONF_APP_BG96_DEBUG == true + gvupdate_mutex.lock(); + _BG96.doDebug(v); + g_debug= v; + gvupdate_mutex.unlock(); + debugOutput(DBGMSG_DRV,"SET debug flag to 0x%02X",v); + #endif +} + +/**---------------------------------------------------------- +* @brief open a socket handle +* @param handle: Pointer to handle +* proto: TCP/UDP protocol +* @return nsapi_error_t +*/ +int BG96Interface::socket_open(void **handle, nsapi_protocol_t proto) +{ + int i; + nsapi_error_t ret=NSAPI_ERROR_OK; + + debugOutput(DBGMSG_DRV,"ENTER socket_open(), protocol=%s", (proto==NSAPI_TCP)?"TCP":"UDP"); + gvupdate_mutex.lock(); + for( i=0; i<BG96_SOCKET_COUNT; i++ ) //find the next available socket... + if( g_sock[i].id == -1 ) + break; + + if( i == BG96_SOCKET_COUNT ) { + ret = NSAPI_ERROR_NO_SOCKET; + debugOutput(DBGMSG_DRV,"EXIT socket_open; NO SOCKET AVAILABLE (%d)",i); + } + else{ + debugOutput(DBGMSG_DRV,"socket_open using socket %d", i); + + g_socTx[i].m_tx_state = TX_IDLE; + g_socRx[i].m_rx_state = READ_START; + + g_sock[i].id = i; + g_sock[i].disTO = false; + g_sock[i].proto = proto; + g_sock[i].connected = false; + g_sock[i]._callback = NULL; + g_sock[i]._data = NULL; + *handle = &g_sock[i]; + debugOutput(DBGMSG_DRV,"EXIT socket_open; Socket=%d, protocol =%s", + i, (g_sock[i].proto==NSAPI_UDP)?"UDP":"TCP"); + } + gvupdate_mutex.unlock(); + + return ret; +} + +/**---------------------------------------------------------- +* @brief close a socket +* @param handle: Pointer to handle +* @return nsapi_error_t +*/ +int BG96Interface::socket_close(void *handle) +{ + BG96SOCKET *sock = (BG96SOCKET*)handle; + nsapi_error_t ret =NSAPI_ERROR_DEVICE_ERROR; + RXEVENT *rxsock; + TXEVENT *txsock; + int i = sock->id; + + debugOutput(DBGMSG_DRV,"ENTER socket_close(); Socket=%d", i); + + if(i >= 0) { + txrx_mutex.lock(); + txsock = &g_socTx[i]; + rxsock = &g_socRx[i]; + + txsock->m_tx_state = TX_IDLE; + rxsock->m_rx_state = READ_START; + + dbgIO_lock; + if( sock->connected ) + _BG96.close(sock->id); + dbgIO_unlock; + + sock->id = -1; + sock->disTO = false; + sock->proto = NSAPI_TCP; + sock->connected= false; + sock->_callback= NULL; + sock->_data = NULL; + ret = NSAPI_ERROR_OK; + txrx_mutex.unlock(); + debugOutput(DBGMSG_DRV,"EXIT socket_close(), socket %d - success",i); + } + else + debugOutput(DBGMSG_DRV,"EXIT socket_close() - fail"); + return ret; +} + +/**---------------------------------------------------------- +* @brief accept connections from remote sockets +* @param handle: Pointer to handle of client socket (connecting) +* proto: handle of server socket which will accept connections +* @return nsapi_error_t +*/ +int BG96Interface::socket_accept(nsapi_socket_t server,nsapi_socket_t *handle, SocketAddress *address) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +/**---------------------------------------------------------- +* @brief connect to a remote socket +* @param handle: Pointer to socket handle +* addr: Address to connect to +* @return nsapi_error_t +*/ +int BG96Interface::socket_connect(void *handle, const SocketAddress &addr) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + nsapi_error_t ret=NSAPI_ERROR_OK; + const char proto = (sock->proto == NSAPI_UDP) ? 'u' : 't'; + bool k; + int cnt; + + + debugOutput(DBGMSG_DRV,"ENTER socket_connect(); Socket=%d; IP=%s; PORT=%d;", + sock->id, addr.get_ip_address(), addr.get_port()); + dbgIO_lock; + for( k=true, cnt=0; cnt<3 && k; cnt++ ) { + k = !_BG96.open(proto, sock->id, addr.get_ip_address(), addr.get_port()); + if( k ) + _BG96.close(sock->id); + } + dbgIO_unlock; + + if( cnt<3 ) { + sock->addr = addr; + sock->connected = true; + + if( sock->_callback != NULL ) + sock->_callback(sock->_data); + } + else + ret = NSAPI_ERROR_DEVICE_ERROR; + + debugOutput(DBGMSG_DRV,"EXIT socket_connect(), Socket %d",sock->id); + return ret; +} + +/**---------------------------------------------------------- +* @brief return the address of this object +* @param none +* @retval pointer to this class object +*/ +NetworkStack *BG96Interface::get_stack() +{ + return this; +} + +/**---------------------------------------------------------- +* @brief return IP address after looking up the URL name +* @param name = URL string +* address = address to store IP in +* version = not used +* @return nsapi_error_t +*/ +nsapi_error_t BG96Interface::gethostbyname(const char* name, SocketAddress *address, nsapi_version_t version) +{ + char ipstr[25]; + bool ok; + nsapi_error_t ret=NSAPI_ERROR_OK; + + debugOutput(DBGMSG_DRV,"ENTER gethostbyname(); IP=%s; PORT=%d; URL=%s;", + address->get_ip_address(), address->get_port(), name); + + dbgIO_lock; + ok=_BG96.resolveUrl(name,ipstr); + dbgIO_unlock; + + if( !ok ) { + ret = NSAPI_ERROR_DEVICE_ERROR; + debugOutput(DBGMSG_DRV,"EXIT gethostbyname() -- failed to get DNS"); + } + else{ + address->set_ip_address(ipstr); + debugOutput(DBGMSG_DRV,"EXIT gethostbyname(); IP=%s; PORT=%d; URL=%s;", + address->get_ip_address(), address->get_port(), name); + } + return ret; +} + +/**---------------------------------------------------------- +* @brief send data to a udp socket +* @param handle: Pointer to handle +* addr: address of udp socket +* data: pointer to data +* size: size of data +* @retval no of bytes sent +*/ +int BG96Interface::socket_sendto(void *handle, const SocketAddress &addr, const void *data, unsigned size) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + int err=NSAPI_ERROR_OK; + + if (!sock->connected) + err = socket_connect(sock, addr); + + if( err != NSAPI_ERROR_OK ) + return err; + else + return socket_send(sock, data, size); +} + + +/**---------------------------------------------------------- +* @brief write to a socket +* @param handle: Pointer to handle +* data: pointer to data +* size: size of data +* @retval no of bytes sent +*/ +int BG96Interface::socket_send(void *handle, const void *data, unsigned size) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + TXEVENT *txsock; + + debugOutput(DBGMSG_DRV,"ENTER socket_send(),socket %d, send %d bytes",sock->id,size); + + if( size < 1 || data == NULL ) // should never happen but have seen it + return 0; + + txrx_mutex.lock(); + txsock = &g_socTx[sock->id]; + + switch( txsock->m_tx_state ) { + case TX_IDLE: + txsock->m_tx_socketID = sock->id; + txsock->m_tx_state = TX_STARTING; + 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 = sock->_callback; + txsock->m_tx_cb_data = sock->_data; + debugDump_arry((const uint8_t*)data,size); + + if( txsock->m_tx_req_size > BG96::BG96_BUFF_SIZE ) + txsock->m_tx_req_size= BG96::BG96_BUFF_SIZE; + + if( tx_event(txsock) != EVENT_COMPLETE ) { //if we didn't sent all the data, schedule background send the rest + debugOutput(DBGMSG_DRV,"Schedule TX event for socket %d",sock->id); + txsock->m_tx_state = TX_ACTIVE; + _eq_schedule(); + txrx_mutex.unlock(); + return NSAPI_ERROR_WOULD_BLOCK; + } + // fall through + + if( txsock->m_tx_state == TX_DOCB ) { + debugOutput(DBGMSG_DRV,"Call socket %d TX call-back",sock->id); + txsock->m_tx_state = TX_COMPLETE; + txsock->m_tx_callback( txsock->m_tx_cb_data ); + } + + // fall through + + case TX_COMPLETE: + debugOutput(DBGMSG_DRV,"EXIT socket_send(), socket %d, sent %d bytes", txsock->m_tx_socketID,txsock->m_tx_total_sent); + txsock->m_tx_state = TX_IDLE; + txrx_mutex.unlock(); + return txsock->m_tx_total_sent; + + case TX_ACTIVE: + case TX_STARTING: + debugOutput(DBGMSG_DRV,"EXIT socket_send(), TX_ACTIVE/TX_STARTING"); + txrx_mutex.unlock(); + return NSAPI_ERROR_WOULD_BLOCK; + + case TX_DOCB: + default: + debugOutput(DBGMSG_DRV,"EXIT socket_send(), NSAPI_ERROR_DEVICE_ERROR"); + txrx_mutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } +} + +/**---------------------------------------------------------- +* @brief receive data on a udp socket +* @param handle: Pointer to handle +* addr: address of udp socket +* data: pointer to data +* size: size of data +* @retval no of bytes read +*/ +int BG96Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + + if (!sock->connected) + return NSAPI_ERROR_NO_CONNECTION; + *addr = sock->addr; + return socket_recv(sock, data, size); +} + +/**---------------------------------------------------------- +* @brief receive data on a socket +* @param handle: Pointer to socket handle +* data: pointer to data +* size: size of data +* @retval no of bytes read +*/ +int BG96Interface::socket_recv(void *handle, void *data, unsigned size) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + RXEVENT *rxsock; + + if( size < 1 || data == NULL ) // should never happen + return 0; + + txrx_mutex.lock(); + rxsock = &g_socRx[sock->id]; + debugOutput(DBGMSG_DRV,"ENTER socket_recv(), socket %d, request %d bytes",sock->id, size); + + switch( rxsock->m_rx_state ) { + case READ_START: //need to start a read sequence of events + rxsock->m_rx_disTO = sock->disTO; + rxsock->m_rx_socketID = sock->id; + rxsock->m_rx_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 > BG96::BG96_BUFF_SIZE) + rxsock->m_rx_req_size= BG96::BG96_BUFF_SIZE; + + rxsock->m_rx_callback = sock->_callback; + rxsock->m_rx_cb_data = sock->_data; + // fall through + if( rx_event(rxsock) != EVENT_COMPLETE ){ + rxsock->m_rx_state = READ_ACTIVE; + _eq_schedule(); + debugOutput(DBGMSG_DRV,"EXIT socket_recv, scheduled read of socket %d.", sock->id); + txrx_mutex.unlock(); + return NSAPI_ERROR_WOULD_BLOCK; + } + + //got data, fall thru and finish. no need to schedule the background task + if( rxsock->m_rx_state == READ_DOCB ) { + debugOutput(DBGMSG_DRV,"Call socket %d RX call-back",sock->id); + rxsock->m_rx_state = DATA_AVAILABLE; + rxsock->m_rx_callback( rxsock->m_rx_cb_data ); + } + + // fall through + + case DATA_AVAILABLE: + debugOutput(DBGMSG_DRV,"EXIT socket_recv(),socket %d, return %d bytes",sock->id, rxsock->m_rx_return_cnt); + debugDump_arry((const uint8_t*)data,rxsock->m_rx_return_cnt); + rxsock->m_rx_state = READ_START; + txrx_mutex.unlock(); + return rxsock->m_rx_return_cnt; + + case READ_ACTIVE: + case READ_INIT: + debugOutput(DBGMSG_DRV,"EXIT socket_recv(), socket id %d, READ_ACTIVE/INIT", sock->id); + txrx_mutex.unlock(); + return NSAPI_ERROR_WOULD_BLOCK; + + case READ_DOCB: + default: + debugOutput(DBGMSG_DRV,"EXIT socket_recv(), NSAPI_ERROR_DEVICE_ERROR"); + txrx_mutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } +} + +/**---------------------------------------------------------- +* @brief check for and retrieve data user requested. Time out +* after TO period unless socket has TO disabled. +* @param pointer to an RXEVENT +* @retval 1 if need to schedule another check, 0 if data received or Timed Out +*/ +int BG96Interface::rx_event(RXEVENT *ptr) +{ + debugOutput(DBGMSG_EQ,"ENTER rx_event() for socket id %d, size=%d", ptr->m_rx_socketID, ptr->m_rx_req_size); + dbgIO_lock; + int cnt = _BG96.recv(ptr->m_rx_socketID, ptr->m_rx_dptr, ptr->m_rx_req_size); + dbgIO_unlock; + + if( cnt == NSAPI_ERROR_DEVICE_ERROR ) { + debugOutput(DBGMSG_EQ,"EXIT rx_event(), error reading socket %d", ptr->m_rx_socketID); + ptr->m_rx_timer=0; + return EVENT_GETMORE; + } + + if( cnt>0 ) { //got data, return it to the caller + debugOutput(DBGMSG_EQ,"EXIT rx_event(), socket %d received %d bytes", ptr->m_rx_socketID, cnt); + ptr->m_rx_return_cnt += cnt; + ptr->m_rx_state = DATA_AVAILABLE; + if( ptr->m_rx_callback != NULL ) + ptr->m_rx_state = READ_DOCB; + return EVENT_COMPLETE; + } + + if( ++ptr->m_rx_timer > (BG96_READ_TIMEOUTMS/EQ_FREQ) && !ptr->m_rx_disTO ) { //timed out waiting, return 0 to caller + debugOutput(DBGMSG_EQ,"EXIT rx_event(), socket id %d, rx data TIME-OUT!",ptr->m_rx_socketID); + ptr->m_rx_state = DATA_AVAILABLE; + ptr->m_rx_return_cnt = 0; + if( ptr->m_rx_callback != NULL ) + ptr->m_rx_state = READ_DOCB; + return EVENT_COMPLETE; + } + + debugOutput(DBGMSG_EQ,"EXIT rx_event(), socket id %d, sechedule for more.", + ptr->m_rx_socketID); + return EVENT_GETMORE; +} + +/**---------------------------------------------------------- +* @brief send data, if more data than BG96 can handle at one +* send as much as possible, and schedule another event +* @param pointer to TXEVENT structure +* @retval 1 if need to schedule another event, 0 if data sent +*/ +int BG96Interface::tx_event(TXEVENT *ptr) +{ + debugOutput(DBGMSG_EQ,"ENTER tx_event(), socket id %d",ptr->m_tx_socketID); + + dbgIO_lock; + bool done =_BG96.send(ptr->m_tx_socketID, ptr->m_tx_dptr, ptr->m_tx_req_size); + dbgIO_unlock; + + if( done ) + ptr->m_tx_total_sent += ptr->m_tx_req_size; + else{ + debugOutput(DBGMSG_EQ,"EXIT tx_event(), socket id %d, sent no data!",ptr->m_tx_socketID); + return EVENT_GETMORE; + } + + 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 > BG96::BG96_BUFF_SIZE) + ptr->m_tx_req_size= BG96::BG96_BUFF_SIZE; + + debugOutput(DBGMSG_EQ,"EXIT tx_event(), need to send %d more bytes.",ptr->m_tx_req_size); + return EVENT_GETMORE; + } + debugOutput(DBGMSG_EQ,"EXIT tx_event, socket id %d, sent %d bytes",ptr->m_tx_socketID,ptr->m_tx_total_sent); + ptr->m_tx_state = TX_COMPLETE; + if( ptr->m_tx_callback != NULL ) + ptr->m_tx_state = TX_DOCB; + + return EVENT_COMPLETE; +} + + +/**---------------------------------------------------------- +* @brief periodic event(EventQueu thread) to check for RX and TX data. If checking for RX data with TO disabled +* slow down event checking after a while. +* @param none +* @retval none +*/ +void BG96Interface::g_eq_event(void) +{ + int done = txrx_mutex.trylock(); + bool goSlow = false; + + if( scheduled_events > 0 ) + scheduled_events--; + + if( !done ) { + _eq_schedule(); + return; + } + + done = EVENT_COMPLETE; + for( unsigned int i=0; i<BG96_SOCKET_COUNT; i++ ) { + if( g_socRx[i].m_rx_state == READ_ACTIVE || g_socRx[i].m_rx_disTO) { + done |= rx_event(&g_socRx[i]); + goSlow |= ( g_socRx[i].m_rx_timer > ((BG96_READ_TIMEOUTMS/EQ_FREQ)*(EQ_FREQ_SLOW/EQ_FREQ)) ); + + if( goSlow ) + g_socRx[i].m_rx_timer = (BG96_READ_TIMEOUTMS/EQ_FREQ)*(EQ_FREQ_SLOW/EQ_FREQ); + } + + if( g_socTx[i].m_tx_state == TX_ACTIVE ) { + goSlow = false; + done |= tx_event(&g_socTx[i]); + } + } + + for( unsigned int i=0; i<BG96_SOCKET_COUNT; i++ ) { + if( g_socRx[i].m_rx_state == READ_DOCB ) { + debugOutput(DBGMSG_EQ,"Call socket %d RX call-back",i); + g_socRx[i].m_rx_state = DATA_AVAILABLE; + g_socRx[i].m_rx_callback( g_socRx[i].m_rx_cb_data ); + } + + if( g_socTx[i].m_tx_state == TX_DOCB ) { + debugOutput(DBGMSG_EQ,"Call socket %d TX call-back",i); + g_socTx[i].m_tx_state = TX_COMPLETE; + g_socTx[i].m_tx_callback( g_socTx[i].m_tx_cb_data ); + } + } + + if( done != EVENT_COMPLETE ) + _eq_schedule(); + + debugOutput(DBGMSG_EQ, "EXIT eq_event, queue=%d\n", scheduled_events); + txrx_mutex.unlock(); +} + + +void BG96Interface::_eq_schedule(void) +{ + if( scheduled_events < BG96_SOCKET_COUNT ) { + scheduled_events++; + _bg96_queue.call_in(EQ_FREQ,mbed::Callback<void()>((BG96Interface*)this,&BG96Interface::g_eq_event)); + } +} +