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
WiconnectSocket.cpp
- Committer:
- dan_ackme
- Date:
- 2015-02-23
- Revision:
- 40:4b4306f3d829
- Parent:
- 29:b6af04b77a56
- Child:
- 41:66beaca0fd1a
File content as of revision 40:4b4306f3d829:
/** * 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_) { do { result = close(); } while(result == WICONNECT_PROCESSING); 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; }