Host library for controlling a WiConnect enabled Wi-Fi module.
Dependents: wiconnect-ota_example wiconnect-web_setup_example wiconnect-test-console wiconnect-tcp_server_example ... more
Diff: internal/types/WiconnectSocket.cpp
- Revision:
- 17:7268f365676b
- Child:
- 21:17bb3eddcbae
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/internal/types/WiconnectSocket.cpp Sat Aug 23 05:39:17 2014 -0700 @@ -0,0 +1,511 @@ +/** + * ACKme WiConnect Host Library is licensed under the BSD licence: + * + * Copyright (c)2014 ACKme Networks. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ +#include <stdarg.h> +#include "Wiconnect.h" +#include "internal/common.h" +#include "StringUtil.h" + + +#define CHECK_CONNECTED() if(!isConnected()) return WICONNECT_NOT_CONNECTED + + +/*************************************************************************************************/ +WiconnectSocket::WiconnectSocket(int rxBufferLen_, void *rxBuffer_, int txBufferLen_, void *txBuffer_) +{ + wiconnect = Wiconnect::getInstance(); + + memset(&txBuffer, 0, sizeof(Buffer)); + memset(&rxBuffer, 0, sizeof(Buffer)); + + txBuffer.size = !wiconnect->nonBlocking ? txBufferLen_ : 0; + txBuffer.buffer = (uint8_t*)txBuffer_; + + rxBuffer.size = !wiconnect->nonBlocking ? rxBufferLen_ : 0; + rxBuffer.buffer = (uint8_t*)rxBuffer_; + + if(txBuffer.size > 0) + { + if(txBuffer_ == NULL) + { +#ifdef WICONNECT_ENABLE_MALLOC + wiconnect_assert(wiconnect, "Socket(), malloc not defined", wiconnect->_malloc != NULL); + txBuffer.buffer = (uint8_t*)wiconnect->_malloc(txBufferLen_); + wiconnect_assert(wiconnect, "Socket(), txBuffer malloc failed", txBuffer.buffer != NULL); + txBuffer.allocated = true; +#else + wiconnect_assert(0); +#endif + } + } + + if(rxBuffer.size > 0) + { + if(rxBuffer_ == NULL) + { +#ifdef WICONNECT_ENABLE_MALLOC + wiconnect_assert(wiconnect, "Socket(), malloc not defined", wiconnect->_malloc != NULL); + rxBuffer.buffer = (uint8_t*)wiconnect->_malloc(rxBufferLen_); + wiconnect_assert(wiconnect, "Socket(), rxBuffer malloc failed", rxBuffer.buffer != NULL); + rxBuffer.allocated = true; +#else + wiconnect_assert(0); +#endif + } + } + + handle = SOCKET_INVALID_HANDLE; + type = SOCKET_TYPE_UNKNOWN; + remotePort = 0; + localPort = 0; + connected = false; + host[0] = 0; +} + + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::init(uint8_t handle_, SocketType type_, const char *host_, uint16_t remotePort_, uint16_t localPort_) +{ + handle = handle_; + type = type_; + remotePort = remotePort_; + localPort = localPort_; + connected = true; + + txBuffer.ptr = txBuffer.buffer; + rxBuffer.ptr = rxBuffer.buffer; + + strncpy(host, host_, sizeof(host)-1); + + return WICONNECT_SUCCESS; +} + +/*************************************************************************************************/ +WiconnectSocket::~WiconnectSocket() +{ + while((handle != SOCKET_INVALID_HANDLE) && (close() == WICONNECT_PROCESSING)) + { + } + +#ifdef WICONNECT_ENABLE_MALLOC + if(txBuffer.allocated && txBuffer.size > 0) + { + wiconnect_assert(wiconnect, "~Socket(), free not defined", wiconnect->_free != NULL); + wiconnect->_free(txBuffer.buffer); + } + if(rxBuffer.allocated && rxBuffer.size > 0) + { + wiconnect_assert(wiconnect, "~Socket(), free not defined", wiconnect->_free != NULL); + wiconnect->_free(rxBuffer.buffer); + } +#endif +} + +/*************************************************************************************************/ +bool WiconnectSocket::isConnected() +{ + return connected; +} + +/*************************************************************************************************/ +SocketType WiconnectSocket::getType() +{ + return type; +} + +/*************************************************************************************************/ +const char* WiconnectSocket::getHost() +{ + return host; +} + +/*************************************************************************************************/ +uint16_t WiconnectSocket::getLocalPort() +{ + return localPort; +} + +/*************************************************************************************************/ +uint16_t WiconnectSocket::getRemotePort() +{ + return remotePort; +} + +/*************************************************************************************************/ +uint8_t WiconnectSocket::getHandle() +{ + return handle; +} + + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::close() +{ + WiconnectResult result; + CHECK_CONNECTED(); + CHECK_OTHER_COMMAND_EXECUTING(); + + if(WICONNECT_SUCCEEDED(result, wiconnect->sendCommand("close %d", handle))) + { + connected = false; + } + + CHECK_CLEANUP_COMMAND(); + + return result; +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::poll(bool *rxDataAvailablePtr, bool autoClose) +{ + WiconnectResult result; + int32_t status; + + CHECK_CONNECTED(); + CHECK_OTHER_COMMAND_EXECUTING(); + + *rxDataAvailablePtr = false; + + if(WICONNECT_SUCCEEDED(result, wiconnect->sendCommand("poll %d", handle))) + { + if(!WICONNECT_FAILED(result, wiconnect->responseToInt32(&status))) + { + if(status > 0) + { + *rxDataAvailablePtr = true; + if(status == 2 && autoClose) + { + connected = false; + } + } + } + } + + CHECK_CLEANUP_COMMAND(); + + return result; +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::write(int length, bool flush) +{ + CHECK_CONNECTED(); + + if( txBuffer.size == 0) + { + return WICONNECT_UNSUPPORTED; + } + else if(length > txBuffer.size) + { + return WICONNECT_OVERFLOW; + } + txBuffer.bytesPending = length; + + return flush ? flushTxBuffer() : WICONNECT_SUCCESS; +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::write(const void* buffer, int length, bool flush) +{ + WiconnectResult result = WICONNECT_SUCCESS; + CHECK_CONNECTED(); + + if(txBuffer.size > 0) + { + // NOTE: txBuffer only available in blocking mode (so no need to check if a cmd is executing) + + const uint8_t *src = (const uint8_t *)buffer; + + while(length > 0) + { + int bytesToWrite = MIN(length, txBuffer.size - txBuffer.bytesPending); + uint8_t *dst = (uint8_t*)&txBuffer.buffer[txBuffer.bytesPending]; + memcpy(dst, src, bytesToWrite); + txBuffer.bytesPending += bytesToWrite; + length -= bytesToWrite; + src += bytesToWrite; + + if((txBuffer.bytesPending >= txBuffer.size) && + WICONNECT_FAILED(result, flushTxBuffer())) + { + break; + } + } + + if(flush && txBuffer.bytesPending > 0) + { + result = flushTxBuffer(); + } + } + else + { + if(WICONNECT_IS_IDLE()) + { + txBuffer.ptr = (uint8_t*)buffer; + txBuffer.bytesPending = length; + } + + result = flushTxBuffer(); + } + + return result; +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::read(void* buffer, uint16_t maxLength, uint16_t *bytesRead) +{ + WiconnectResult result; + + CHECK_CONNECTED(); + CHECK_OTHER_COMMAND_EXECUTING(); + + if(WICONNECT_SUCCEEDED(result, wiconnect->sendCommand((char*)buffer, maxLength, "read %d %d", handle, maxLength))) + { + *bytesRead = wiconnect->getLastCommandResponseLength(); + } + + CHECK_CLEANUP_COMMAND(); + + return result; +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::read(uint8_t **bufferPtr, uint16_t *bytesReadPtr) +{ + WiconnectResult result = WICONNECT_SUCCESS; + + CHECK_CONNECTED(); + + if(rxBuffer.size == 0) + { + return WICONNECT_UNSUPPORTED; + } + else if(bufferPtr != NULL && bytesReadPtr == NULL) + { + return WICONNECT_BAD_ARG; + } + else if(rxBuffer.bytesPending < rxBuffer.size - 2) + { + const int bytesToRead = rxBuffer.size - rxBuffer.bytesPending - 2; + char* ptr = (char*)&rxBuffer.buffer[rxBuffer.bytesPending]; + if(!WICONNECT_FAILED(result, wiconnect->sendCommand(ptr, bytesToRead+2, "read %d %d", handle, bytesToRead))) + { + rxBuffer.bytesPending += wiconnect->getLastCommandResponseLength(); + } + } + + if(bufferPtr != NULL) + { + *bufferPtr = rxBuffer.buffer; + *bytesReadPtr = rxBuffer.bytesPending; + clearRxBuffer(); + } + + return result; +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::getc(uint8_t *c) +{ + WiconnectResult result; + + if(rxBuffer.size == 0) + { + return WICONNECT_UNSUPPORTED; + } + + read_data: + if(rxBuffer.bytesPending == 0 && + WICONNECT_FAILED(result, read())) + { + return result; + } + else if(rxBuffer.ptr < &rxBuffer.buffer[rxBuffer.bytesPending]) + { + *c = *rxBuffer.ptr; + ++rxBuffer.ptr; + return WICONNECT_SUCCESS; + } + else + { + clearRxBuffer(); + goto read_data; + } +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::putc(uint8_t c, bool flush) +{ + WiconnectResult result = WICONNECT_SUCCESS; + CHECK_CONNECTED(); + + if(txBuffer.size == 0) + { + return WICONNECT_UNSUPPORTED; + } + else if(txBuffer.bytesPending < txBuffer.size) + { + uint8_t *ptr = (uint8_t*)&txBuffer.buffer[txBuffer.bytesPending]; + *ptr = c; + ++txBuffer.bytesPending; + + if(flush || txBuffer.bytesPending >= txBuffer.size) + { + result = flushTxBuffer(); + } + } + else + { + result = WICONNECT_OVERFLOW; + } + + return result; +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::puts(const char *s, bool flush) +{ + const int len = strlen(s); + return write(s, len, flush); +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::printf(const char* format, ...) +{ + WiconnectResult result = WICONNECT_SUCCESS; + + CHECK_CONNECTED(); + if(txBuffer.size == 0) + { + return WICONNECT_UNSUPPORTED; + } + + const int available = txBuffer.size - txBuffer.bytesPending; + char *ptr = (char*)&txBuffer.buffer[txBuffer.bytesPending]; + va_list args; + va_start(args, format); + const int len = vsnprintf(ptr, available, format, args); + if(len > available) + { + return WICONNECT_OVERFLOW; + } + else + { + txBuffer.bytesPending += len; + } + + if(txBuffer.bytesPending >= txBuffer.size) + { + result = flushTxBuffer(); + } + + return result; +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::flushTxBuffer() +{ + WiconnectResult result = WICONNECT_SUCCESS; + + CHECK_CONNECTED(); + CHECK_OTHER_COMMAND_EXECUTING(); + + if(txBuffer.bytesPending > 0) + { + result = wiconnect->sendCommand(ReaderFunc(this, &WiconnectSocket::writeDataCallback), NULL, "write %d %d", handle, txBuffer.bytesPending); + } + + CHECK_CLEANUP_COMMAND(); + + if(result != WICONNECT_PROCESSING) + { + txBuffer.ptr = txBuffer.buffer; + txBuffer.bytesPending = 0; + } + + return result; +} + +/*************************************************************************************************/ +void WiconnectSocket::clearRxBuffer() +{ + rxBuffer.bytesPending = 0; + rxBuffer.ptr = rxBuffer.buffer; +} + +/*************************************************************************************************/ +uint8_t* WiconnectSocket::getTxBuffer() +{ + return txBuffer.buffer; +} +/*************************************************************************************************/ +int WiconnectSocket::getTxBufferSize() +{ + return txBuffer.size; +} +/*************************************************************************************************/ +int WiconnectSocket::getTxBufferBytesPending() +{ + return txBuffer.bytesPending; +} +/*************************************************************************************************/ +uint8_t* WiconnectSocket::getRxBuffer() +{ + return rxBuffer.buffer; +} +/*************************************************************************************************/ +int WiconnectSocket::getRxBufferSize() +{ + return rxBuffer.size; +} +/*************************************************************************************************/ +int WiconnectSocket::getRxBufferBytesPending() +{ + return rxBuffer.bytesPending; +} + + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::writeDataCallback(void *user, void *data, int maxReadSize, int *bytesRead) +{ + if(txBuffer.bytesPending == 0) + { + *bytesRead = EOF; + } + else + { + const int bytesToWrite = MIN(maxReadSize, txBuffer.bytesPending); + memcpy(data, txBuffer.ptr, bytesToWrite); + txBuffer.ptr += bytesToWrite; + txBuffer.bytesPending -= bytesToWrite; + *bytesRead = bytesToWrite; + } + + return WICONNECT_SUCCESS; +} +