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: WiconnectSocket.cpp
- Revision:
- 29:b6af04b77a56
- Child:
- 40:4b4306f3d829
diff -r 3c52f578708a -r b6af04b77a56 WiconnectSocket.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WiconnectSocket.cpp Mon Oct 27 13:42:26 2014 -0700 @@ -0,0 +1,615 @@ +/** + * 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 "api/StringUtil.h" + + +#define CHECK_CONNECTED() if(!isConnected()) return WICONNECT_NOT_CONNECTED + + +WiconnectResult readerCallback(void *user, void *data, int maxReadSize, int *bytesRead); + + + +/*************************************************************************************************/ +WiconnectSocket::WiconnectSocket(int rxBufferLen_, void *rxBuffer_, int txBufferLen_, void *txBuffer_) +{ + wiconnect = Wiconnect::getInstance(); + + memset((void*)&txBuffer, 0, sizeof(Buffer)); + memset((void*)&rxBuffer, 0, sizeof(Buffer)); + + txBuffer.size = txBufferLen_; + txBuffer.buffer = (uint8_t*)txBuffer_; + + rxBuffer.size = rxBufferLen_; + 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(wiconnect, "must specify buffer", 0); +#endif + } + txBuffer.size -= 4; + } + + 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(wiconnect, "must specify buffer", 0); +#endif + } + rxBuffer.size -= 4; + } + + handle = SOCKET_INVALID_HANDLE; + type = SOCKET_TYPE_UNKNOWN; + remotePort = 0; + localPort = 0; + connected = false; + enableAutoClose = 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; + enableAutoClose = false; + + rxBuffer.ptr = rxBuffer.buffer; + rxBuffer.bytesPending = 0; + txBuffer.ptr = txBuffer.buffer; + txBuffer.bytesPending = 0; + + 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(); + + result = wiconnect->sendCommand("close %d", handle); + + if(result != WICONNECT_PROCESSING) + { + connected = false; + wiconnect->socketClosedCallback(this); + } + + CHECK_CLEANUP_COMMAND(); + + return result; +} + +/*************************************************************************************************/ +WiconnectResult WiconnectSocket::poll(bool *rxDataAvailablePtr, bool autoClose) +{ + WiconnectResult result; + int32_t status; + bool autoClosed = false; + + *rxDataAvailablePtr = false; + + CHECK_CONNECTED(); + + if(rxBuffer.size > 0 && WICONNECT_IS_IDLE()) + { + if(rxBuffer.ptr < &rxBuffer.buffer[rxBuffer.bytesPending]) + { + *rxDataAvailablePtr = true; + return WICONNECT_SUCCESS; + } + else if(read() == WICONNECT_SUCCESS && (rxBuffer.ptr < &rxBuffer.buffer[rxBuffer.bytesPending])) + { + *rxDataAvailablePtr = true; + return WICONNECT_SUCCESS; + } + } + + CHECK_OTHER_COMMAND_EXECUTING(); + + if(WICONNECT_SUCCEEDED(result, wiconnect->sendCommand("poll %d", handle))) + { + if(!WICONNECT_FAILED(result, wiconnect->responseToInt32(&status))) + { + if(status > 0) + { + if(status == 2 && enableAutoClose) + { + autoClosed = autoClose; + *rxDataAvailablePtr = false; + } + else + { + *rxDataAvailablePtr = true; + } + } + } + } + + CHECK_CLEANUP_COMMAND(); + + // if we auto-closed, then block until everything is cleaned-up + if(autoClosed) + { + while(WICONNECT_IS_PROCESSING(result, close())) + { + } + } + + 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) + { + 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 *bytesReadPtr) +{ + WiconnectResult result; + + CHECK_CONNECTED(); + + if(rxBuffer.size > 0) + { + uint16_t bytesToRead = 0; + const uint16_t bufferedBytes = (&rxBuffer.buffer[rxBuffer.bytesPending] - rxBuffer.ptr); + if(bufferedBytes > 0) + { + bytesToRead = MIN(bufferedBytes, maxLength); + memcpy(buffer, rxBuffer.ptr, bytesToRead); + rxBuffer.ptr += bytesToRead; + *bytesReadPtr = bytesToRead; + } + if(rxBuffer.ptr >= &rxBuffer.buffer[rxBuffer.bytesPending]) + { + clearRxBuffer(); + } + if(bytesToRead > 0) + { + return WICONNECT_SUCCESS; + } + } + + CHECK_OTHER_COMMAND_EXECUTING(); + + if(WICONNECT_SUCCEEDED(result, wiconnect->sendCommand((char*)buffer, maxLength, "read %d %d", handle, maxLength))) + { + const uint16_t bytesRead = wiconnect->getLastCommandResponseLength(); + enableAutoClose = (bytesRead == 0); + *bytesReadPtr = bytesRead; + } + + 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; + } + + if(rxBuffer.ptr >= &rxBuffer.buffer[rxBuffer.bytesPending]) + { + clearRxBuffer(); + } + + if(rxBuffer.bytesPending < rxBuffer.size) + { + const int bytesToRead = rxBuffer.size - rxBuffer.bytesPending; + char* ptr = (char*)&rxBuffer.buffer[rxBuffer.bytesPending]; + + CHECK_OTHER_COMMAND_EXECUTING(); + + loop: + if(bytesToRead > 0 && WICONNECT_SUCCEEDED(result, wiconnect->sendCommand(ptr, bytesToRead, "read %d %d", handle, bytesToRead))) + { + const uint16_t bytesRead = wiconnect->getLastCommandResponseLength(); + enableAutoClose = (bytesRead == 0); + rxBuffer.bytesPending += bytesRead; + } + + // if still processing and in non-blocking mode, + // then this api call must block until the command completes + if(result == WICONNECT_PROCESSING && wiconnect->nonBlocking) + { + goto loop; + } + + CHECK_CLEANUP_COMMAND(); + } + + 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; + } + + 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(); + return WICONNECT_ERROR; + } +} + +/*************************************************************************************************/ +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(); + + if(txBuffer.size == 0) + { + CHECK_OTHER_COMMAND_EXECUTING(); + } + + if(txBuffer.bytesPending > 0) + { + loop: + result = wiconnect->sendCommand(ReaderFunc(readerCallback), &this->txBuffer, "write %u %u", handle, txBuffer.bytesPending); + + // if still processing and in non-blocking mode and using a txtBuffer, + // then this api call must block until the command completes + if(result == WICONNECT_PROCESSING && wiconnect->nonBlocking && txBuffer.size > 0) + { + goto loop; + } + } + + if(txBuffer.size == 0) + { + 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 readerCallback(void *user, void *data, int maxReadSize, int *bytesRead) +{ + Buffer *txBuffer = (Buffer*)user; + + 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; +} +