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

TcpServer.cpp

Committer:
aymangrais
Date:
2015-09-28
Revision:
42:8ffb253b09e7
Parent:
29:b6af04b77a56
Child:
40:4b4306f3d829

File content as of revision 42:8ffb253b09e7:

/**
 * 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 "Wiconnect.h"
#include "internal/common.h"
#include "api/StringUtil.h"

#ifdef WICONNECT_GPIO_IRQ_ENABLED
#include "api/types/SocketIrqHandlerMap.h"
#endif

#define TCP_SERVER_MONITOR_PERIOD 250 //ms



static WiconnectResult parseIpPortStr(char *str, uint32_t *ipAddress, uint16_t *port);




/*************************************************************************************************/
WiconnectResult SocketInterface::tcpListen(uint16_t listeningPort, int maxClients GPIO_IRQ_ARG)
{
    WiconnectResult result = WICONNECT_ERROR;

    enum
    {
        FS_SET_MAX_CLIENTS,
        FS_SET_DATA_GPIO,
        FS_START_SERVER,
    };

    CHECK_OTHER_COMMAND_EXECUTING();

    if(wiconnect->internalProcessingState == FS_SET_MAX_CLIENTS)
    {
        if(WICONNECT_SUCCEEDED(result, wiconnect->sendCommand("set tcp.server.max_clients", maxClients)))
        {
            wiconnect->internalProcessingState = FS_SET_DATA_GPIO;
        }
        else if(result == WICONNECT_CMD_RESPONSE_ERROR)
        {
            // if there was a module error, then the wiconnect version probably doesn't support this option
            // just continue to the next state
            wiconnect->internalProcessingState = FS_SET_DATA_GPIO;
        }
    }

    if(wiconnect->internalProcessingState == FS_SET_DATA_GPIO)
    {
#ifdef WICONNECT_GPIO_IRQ_ENABLED
        if(irqPin == PIN_NC)
        {
            wiconnect->internalProcessingState = FS_START_SERVER;
        }
        else
        {
            PinToGpioMapper mapper = wiconnect->pinToGpioMapper;
            if(mapper == NULL)
            {
                return WICONNECT_PINNAME_TO_GPIO_MAPPER_NULL;
            }
            int8_t gpio = mapper(irqPin);
            if(gpio == -1)
            {
                return WICONNECT_PINNAME_TO_GPIO_NO_MAPPING;
            }
            else if(!irqHandlers.pinIsRegistered(irqPin))
            {
                return WICONNECT_NOT_FOUND;
            }
            else if(WICONNECT_SUCCEEDED(result, wiconnect->sendCommand("set tcp.server.data_gpio %d", gpio)))
            {
                wiconnect->internalProcessingState = FS_START_SERVER;
            }
        }
#else
        wiconnect->internalProcessingState = FS_START_SERVER;
#endif
    }

    if(wiconnect->internalProcessingState == FS_START_SERVER)
    {
        if(WICONNECT_SUCCEEDED(result, wiconnect->sendCommand("tcps start %d", listeningPort)))
        {
//#ifdef WICONNECT_ASYNC_TIMER_ENABLED
//            if(clientConnectedCallback.isValid() && !wiconnect->nonBlocking)
//            {
//                serverClientConnectedCallback = clientConnectedCallback;
//                serverMonitorTimer.start(this, &SocketInterface::serverClientMonitor, TCP_SERVER_MONITOR_PERIOD);
//            }
//#endif
        }
    }

    CHECK_CLEANUP_COMMAND();

    return result;
}

/*************************************************************************************************/
WiconnectResult SocketInterface::tcpAccept(WiconnectSocket &socket, uint32_t timeoutMs)
{
    TimeoutTimer timer;

    do
    {
        uint8_t handle;
        uint16_t local, remote;
        uint32_t ipAddress;
        WiconnectResult result;

        if(WICONNECT_SUCCEEDED(result, pollForServerClient(&handle, &local, &remote, &ipAddress)))
        {
            char ipBuffer[17];
            if(WICONNECT_FAILED(result, socket.init(handle, SOCKET_TYPE_TCP, Wiconnect::ipToStr(ipAddress, ipBuffer), remote, local)))
            {
                return result;
            }
            serverConnectedClientList |= (1 << handle);
            return WICONNECT_SUCCESS;
        }
        else if(!(result == WICONNECT_PROCESSING || result == WICONNECT_NOT_FOUND))
        {
            return result;
        }

    } while(timeoutMs == WICONNECT_WAIT_FOREVER || !timer.timedOut(timeoutMs));

    return WICONNECT_TIMEOUT;
}

/*************************************************************************************************/
WiconnectResult SocketInterface::tcpServerStop(void)
{
    WiconnectResult result = WICONNECT_ERROR;

    CHECK_OTHER_COMMAND_EXECUTING();

    result = wiconnect->sendCommand("tcps stop");

    CHECK_CLEANUP_COMMAND();

    return result;
}

/*************************************************************************************************/
WiconnectResult SocketInterface::pollForServerClient(uint8_t *handlePtr, uint16_t *localPort, uint16_t *remotePort, uint32_t *ipAddress)
{
    WiconnectResult result;

    CHECK_OTHER_COMMAND_EXECUTING();

    if(WICONNECT_SUCCEEDED(result, wiconnect->sendCommand("list")))
    {
        uint32_t connectedClients = 0;
        char *line, *savedLine;
        result = WICONNECT_NOT_FOUND;

        for(savedLine = wiconnect->internalBuffer; (line = StringUtil::strtok_r(savedLine, "\r\n", &savedLine)) != NULL;)
        {
            char *toks[4], *savedTok;

            if(*line != '#')
            {
                continue;
            }
            savedTok = line + 2;

            for(int i = 0; i < 4 && (toks[i] = StringUtil::strtok_r(savedTok, " ", &savedTok)) != NULL; ++i)
            {
                if(toks[i] == NULL)
                {
                    result = WICONNECT_RESPONSE_PARSE_ERROR;
                    goto exit;
                }
            }

            if(strcmp(toks[1], "TCPS") != 0)
            {
                continue;
            }

            uint8_t handle = (uint8_t)(*toks[0] - '0');
            if(handle >= WICONNECT_MAX_SOCKETS)
            {
                result = WICONNECT_RESPONSE_PARSE_ERROR;
                goto exit;
            }
            const uint32_t handleMask = (1 << handle);
            connectedClients |= handleMask;

            if(result == WICONNECT_SUCCESS)
            {
                continue;
            }
            else if(serverConnectedClientList & handleMask)
            {
                continue;
            }

            result = WICONNECT_SUCCESS;

            if(handlePtr != NULL)
            {
                *handlePtr = handle;
                parseIpPortStr(toks[2], NULL, localPort);
                parseIpPortStr(toks[3], ipAddress, remotePort);
            }
        }

        uint32_t mask = 1;
        for(int i = 0; i < WICONNECT_MAX_SOCKETS; ++i, mask <<= 1)
        {
            if(!(connectedClients & mask))
            {
                serverConnectedClientList &= ~mask;
            }
        }
    }


exit:
    CHECK_CLEANUP_COMMAND();

    return result;
}

/*************************************************************************************************/
static WiconnectResult parseIpPortStr(char *str, uint32_t *ipAddress, uint16_t *port)
{
    char *colon = strchr(str, ':');
    if(colon == NULL)
    {
        return WICONNECT_RESPONSE_PARSE_ERROR;
    }
    *colon++ = 0;

    if(ipAddress != NULL && !Wiconnect::strToIp(str, ipAddress))
    {
        return WICONNECT_RESPONSE_PARSE_ERROR;
    }
    else if(!StringUtil::strToUint16(colon, port))
    {
        return WICONNECT_RESPONSE_PARSE_ERROR;
    }

    return WICONNECT_SUCCESS;
}