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

internal/types/Socket.cpp

Committer:
dan_ackme
Date:
2014-08-11
Revision:
6:8a87a59d0d21
Parent:
1:6ec9998427ad
Child:
16:7f1d6d359787

File content as of revision 6:8a87a59d0d21:

/*
 * Copyright 2014, ACKme Networks
 * All Rights Reserved.
 *
 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of ACKme Networks;
 * the contents of this file may not be disclosed to third parties, copied
 * or duplicated in any form, in whole or in part, without the prior
 * written permission of ACKme Networks.
 */

#include <stdarg.h>
#include "Wiconnect.h"
#include "internal/common.h"
#include "StringUtil.h"


#define CHECK_CONNECTED() if(!isConnected()) return WICONNECT_NOT_CONNECTED


/*************************************************************************************************/
Socket::Socket(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
        }
    }

    init(SOCKET_INVALID_HANDLE, SOCKET_TYPE_UNKNOWN, NULL, 0, 0);
}


/*************************************************************************************************/
WiconnectResult Socket::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;
}

/*************************************************************************************************/
Socket::~Socket()
{
    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 Socket::isConnected()
{
    return connected;
}

/*************************************************************************************************/
SocketType Socket::getType()
{
    return type;
}

/*************************************************************************************************/
const char* Socket::getHost()
{
    return host;
}

/*************************************************************************************************/
uint16_t Socket::getLocalPort()
{
    return localPort;
}

/*************************************************************************************************/
uint16_t Socket::getRemotePort()
{
    return remotePort;
}

/*************************************************************************************************/
uint8_t Socket::getHandle()
{
    return handle;
}


/*************************************************************************************************/
WiconnectResult Socket::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 Socket::poll(bool *rxDataAvailablePtr)
{
    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 == 2)
            {
                connected = false;
            }
            else if(status == 1)
            {
                *rxDataAvailablePtr = true;
            }
        }
    }

    CHECK_CLEANUP_COMMAND();

    return result;
}

/*************************************************************************************************/
WiconnectResult Socket::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 Socket::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 Socket::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-2)))
    {
        *bytesRead = wiconnect->getLastCommandResponseLength();
    }

    CHECK_CLEANUP_COMMAND();

    return result;
}

/*************************************************************************************************/
WiconnectResult Socket::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 Socket::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 Socket::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 Socket::puts(const char *s, bool flush)
{
    const int len = strlen(s);
    return write(s, len, flush);
}

/*************************************************************************************************/
WiconnectResult Socket::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 Socket::flushTxBuffer()
{
    WiconnectResult result = WICONNECT_SUCCESS;

    CHECK_CONNECTED();
    CHECK_OTHER_COMMAND_EXECUTING();

    if(txBuffer.bytesPending > 0)
    {
        result = wiconnect->sendCommand(ReaderFunc(this, &Socket::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 Socket::clearRxBuffer()
{
    rxBuffer.bytesPending = 0;
    rxBuffer.ptr = rxBuffer.buffer;
}

/*************************************************************************************************/
uint8_t* Socket::getTxBuffer()
{
    return txBuffer.buffer;
}
/*************************************************************************************************/
int Socket::getTxBufferSize()
{
    return txBuffer.size;
}
/*************************************************************************************************/
int Socket::getTxBufferBytesPending()
{
    return txBuffer.bytesPending;
}
/*************************************************************************************************/
uint8_t* Socket::getRxBuffer()
{
    return rxBuffer.buffer;
}
/*************************************************************************************************/
int Socket::getRxBufferSize()
{
    return rxBuffer.size;
}
/*************************************************************************************************/
int Socket::getRxBufferBytesPending()
{
    return rxBuffer.bytesPending;
}


/*************************************************************************************************/
WiconnectResult Socket::writeDataCallback(void *user, void *data, int maxReadSize, int *bytesRead)
{
    if(txBuffer.bytesPending == 0)
    {
        *bytesRead = EOF;
    }
    else
    {
        int bytesToWrite = MIN(maxReadSize, txBuffer.bytesPending);
        memcpy(data, txBuffer.ptr, bytesToWrite);
        txBuffer.ptr += bytesToWrite;
        txBuffer.bytesPending -= bytesToWrite;
        *bytesRead = bytesToWrite;
    }

    return WICONNECT_SUCCESS;
}