Fork for fixes

Revision:
9:a156d3de5647
Child:
10:e4ddab81e6a8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TcpClient.cpp	Tue Aug 27 15:01:10 2019 +0000
@@ -0,0 +1,702 @@
+/*
+ UIPClient.cpp - Arduino implementation of a UIP wrapper class.
+ Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
+ All rights reserved.
+
+ Modified (ported to mbed) by Zoltan Hudak <hudakz@inbox.com>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+extern "C"
+{
+#include "utility/uip-conf.h"
+#include "utility/uip.h"
+#include "utility/uip_arp.h"
+#include "string.h"
+}
+#include "UipEthernet.h"
+#include "TcpClient.h"
+#include "DnsClient.h"
+
+#define UIP_TCP_PHYH_LEN    UIP_LLH_LEN + UIP_IPTCPH_LEN
+
+uip_userdata_t TcpClient::all_data[UIP_CONNS];
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+TcpClient::TcpClient() :
+    data(NULL)
+{ }
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+TcpClient::TcpClient(uip_userdata_t* conn_data) :
+    data(conn_data)
+{ }
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int TcpClient::connect(IpAddress ip, uint16_t port)
+{
+    stop();
+
+    uip_ipaddr_t    ipaddr;
+    uip_ip_addr(ipaddr, ip);
+
+    struct uip_conn*    conn = uip_connect(&ipaddr, htons(port));
+    if (conn)
+    {
+#if UIP_CONNECT_TIMEOUT > 0
+        int32_t timeout = time(NULL) + UIP_CONNECT_TIMEOUT;
+#endif
+        while ((conn->tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) {
+            UipEthernet::ethernet->tick();
+            if ((conn->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED) {
+                data = (uip_userdata_t*)conn->appstate;
+#ifdef UIPETHERNET_DEBUG_CLIENT
+                printf("connected, state: %d, first packet in: %d\r\n", data->state, data->packets_in[0]);
+#endif
+                return 1;
+            }
+
+#if UIP_CONNECT_TIMEOUT > 0
+            if (((int32_t) (time(NULL) - timeout)) > 0) {
+                conn->tcpstateflags = UIP_CLOSED;
+                break;
+            }
+#endif
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int TcpClient::connect(const char* host, uint16_t port)
+{
+    // Look up the host first
+    int         ret = 0;
+#if UIP_UDP
+    DnsClient   dns;
+    IpAddress   remote_addr;
+
+    dns.begin(UipEthernet::dnsServerAddress);
+    ret = dns.getHostByName(host, remote_addr);
+    if (ret == 1) {
+        return connect(remote_addr, port);
+    }
+#endif
+    return ret;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void TcpClient::stop()
+{
+    if (data && data->state)
+    {
+#ifdef UIPETHERNET_DEBUG_CLIENT
+        printf("before stop(), with data\r\n");
+        _dumpAllData();
+#endif
+        _flushBlocks(&data->packets_in[0]);
+        if (data->state & UIP_CLIENT_REMOTECLOSED) {
+            data->state = 0;
+        }
+        else {
+            data->state |= UIP_CLIENT_CLOSE;
+        }
+
+#ifdef UIPETHERNET_DEBUG_CLIENT
+        printf("after stop()\r\n");
+        _dumpAllData();
+#endif
+    }
+
+#ifdef UIPETHERNET_DEBUG_CLIENT
+    else {
+        printf("stop(), data: NULL\r\n");
+    }
+#endif
+    data = NULL;
+    UipEthernet::ethernet->tick();
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint8_t TcpClient::connected()
+{
+    return(data && (data->packets_in[0] != NOBLOCK || (data->state & UIP_CLIENT_CONNECTED))) ? 1 : 0;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+bool TcpClient::operator==(const TcpClient& rhs)
+{
+    return data && rhs.data && (data == rhs.data);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+TcpClient::operator bool()
+{
+    UipEthernet::ethernet->tick();
+    return data && (!(data->state & UIP_CLIENT_REMOTECLOSED) || data->packets_in[0] != NOBLOCK);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+size_t TcpClient::write(uint8_t c)
+{
+    return _write(data, &c, 1);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+size_t TcpClient::write(const uint8_t* buf, size_t size)
+{
+    return _write(data, buf, size);
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+size_t TcpClient::_write(uip_userdata_t* data, const uint8_t* buf, size_t size)
+{
+    int         remain = size;
+    uint16_t    written;
+#if UIP_ATTEMPTS_ON_WRITE > 0
+    uint16_t    attempts = UIP_ATTEMPTS_ON_WRITE;
+#endif
+    repeat : UipEthernet::ethernet->tick();
+    if (data && !(data->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))) {
+        uint8_t p = _currentBlock(&data->packets_out[0]);
+        if (data->packets_out[p] == NOBLOCK)
+        {
+newpacket:
+            data->packets_out[p] = UipEthernet::ethernet->phy.allocBlock(UIP_SOCKET_DATALEN);
+            if (data->packets_out[p] == NOBLOCK)
+            {
+#if UIP_ATTEMPTS_ON_WRITE > 0
+                if (--attempts > 0)
+#endif
+#if UIP_ATTEMPTS_ON_WRITE != 0
+                    goto repeat;
+#endif
+                goto ready;
+            }
+
+            data->out_pos = 0;
+        }
+
+#ifdef UIPETHERNET_DEBUG_CLIENT
+        printf
+        (
+            "UIPClient.write: writePacket(%d) pos: %d, buf[%d-%d]\r\n",
+            u->packets_out[p],
+            u->out_pos,
+            size - remain,
+            remain
+        );
+#endif
+        written = UipEthernet::ethernet->phy.writePacket
+            (
+                data->packets_out[p],
+                data->out_pos,
+                (uint8_t*)buf + size - remain,
+                remain
+            );
+        remain -= written;
+        data->out_pos += written;
+        if (remain > 0) {
+            if (p == UIP_SOCKET_NUMPACKETS - 1)
+            {
+#if UIP_ATTEMPTS_ON_WRITE > 0
+                if (--attempts > 0)
+#endif
+#if UIP_ATTEMPTS_ON_WRITE != 0
+                    goto repeat;
+#endif
+                goto ready;
+            }
+
+            p++;
+            goto newpacket;
+        }
+
+ready:
+        data->pollTimer.start();
+        return size - remain;
+    }
+
+    return -1;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int TcpClient::available()
+{
+    if (*this)
+        return _available(data);
+    return 0;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int TcpClient::_available(uip_userdata_t* u)
+{
+    int len = 0;
+    for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) {
+        len += UipEthernet::ethernet->phy.blockSize(u->packets_in[i]);
+    }
+
+    return len;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int TcpClient::read(uint8_t* buf, size_t size)
+{
+    if (*this) {
+        uint16_t    remain = size;
+        if (data->packets_in[0] == NOBLOCK)
+            return 0;
+
+        uint16_t    read;
+        do {
+            read = UipEthernet::ethernet->phy.readPacket(data->packets_in[0], 0, buf + size - remain, remain);
+            if (read == UipEthernet::ethernet->phy.blockSize(data->packets_in[0])) {
+                remain -= read;
+                _eatBlock(&data->packets_in[0]);
+                if
+                (
+                    uip_stopped(&uip_conns[data->state & UIP_CLIENT_SOCKETS]) &&
+                    !(data->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))
+                ) data->state |= UIP_CLIENT_RESTART;
+                if (data->packets_in[0] == NOBLOCK) {
+                    if (data->state & UIP_CLIENT_REMOTECLOSED) {
+                        data->state = 0;
+                        data = NULL;
+                    }
+
+                    return size - remain;
+                }
+            }
+            else {
+                UipEthernet::ethernet->phy.resizeBlock(data->packets_in[0], read);
+                break;
+            }
+        } while (remain > 0);
+        return size;
+    }
+
+    return -1;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int TcpClient::read()
+{
+    static uint8_t  c;
+    if (read(&c, 1) < 0)
+        return -1;
+    return c;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int TcpClient::peek()
+{
+    static uint8_t  c;
+    if (*this) {
+        if (data->packets_in[0] != NOBLOCK) {
+            UipEthernet::ethernet->phy.readPacket(data->packets_in[0], 0, &c, 1);
+            return c;
+        }
+    }
+
+    return -1;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void TcpClient::flush()
+{
+    if (*this) {
+        _flushBlocks(&data->packets_in[0]);
+    }
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void TcpClient::close()
+{
+    delete this;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void uipclient_appcall()
+{
+    uint16_t            send_len = 0;
+    uip_userdata_t*     u = (uip_userdata_t*)uip_conn->appstate;
+    if (!u && uip_connected())
+    {
+#ifdef UIPETHERNET_DEBUG_CLIENT
+        printf("UIPClient uip_connected\r\n");
+        UIPClient::_dumpAllData();
+#endif
+        u = (uip_userdata_t*)TcpClient::_allocateData();
+        if (u) {
+            uip_conn->appstate = u;
+#ifdef UIPETHERNET_DEBUG_CLIENT
+            printf("UIPClient allocated state: %d", u->state);
+#endif
+        }
+
+#ifdef UIPETHERNET_DEBUG_CLIENT
+        else
+            printf("UIPClient allocation failed\r\n");
+#endif
+    }
+
+    if (u) {
+        if (uip_newdata())
+        {
+#ifdef UIPETHERNET_DEBUG_CLIENT
+            printf("UIPClient uip_newdata, uip_len: %d\r\n", uip_len);
+#endif
+            if (uip_len && !(u->state & (UIP_CLIENT_CLOSE | UIP_CLIENT_REMOTECLOSED))) {
+                for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) {
+                    if (u->packets_in[i] == NOBLOCK) {
+                        u->packets_in[i] = UipEthernet::ethernet->phy.allocBlock(uip_len);
+                        if (u->packets_in[i] != NOBLOCK) {
+                            UipEthernet::ethernet->phy.copyPacket
+                                (
+                                    u->packets_in[i],
+                                    0,
+                                    UipEthernet::inPacket,
+                                    ((uint8_t*)uip_appdata) - uip_buf,
+                                    uip_len
+                                );
+                            if (i == UIP_SOCKET_NUMPACKETS - 1)
+                                uip_stop();
+                            goto finish_newdata;
+                        }
+                    }
+                }
+
+                UipEthernet::packetState &= ~UIPETHERNET_FREEPACKET;
+                uip_stop();
+            }
+        }
+
+finish_newdata:
+        if (u->state & UIP_CLIENT_RESTART) {
+            u->state &= ~UIP_CLIENT_RESTART;
+            uip_restart();
+        }
+
+        // If the connection has been closed, save received but unread data.
+        if (uip_closed() || uip_timedout())
+        {
+#ifdef UIPETHERNET_DEBUG_CLIENT
+            printf("UIPClient uip_closed\r\n");
+            UIPClient::_dumpAllData();
+#endif
+            // drop outgoing packets not sent yet:
+
+            TcpClient::_flushBlocks(&u->packets_out[0]);
+            if (u->packets_in[0] != NOBLOCK) {
+                ((uip_userdata_closed_t*)u)->lport = uip_conn->lport;
+                u->state |= UIP_CLIENT_REMOTECLOSED;
+            }
+            else
+                u->state = 0;
+
+            // disassociate appdata.
+#ifdef UIPETHERNET_DEBUG_CLIENT
+            printf("after UIPClient uip_closed\r\n");
+            UIPClient::_dumpAllData();
+#endif
+            uip_conn->appstate = NULL;
+            goto finish;
+        }
+
+        if (uip_acked())
+        {
+#ifdef UIPETHERNET_DEBUG_CLIENT
+            printf("UIPClient uip_acked\r\n");
+#endif
+            TcpClient::_eatBlock(&u->packets_out[0]);
+        }
+
+        if (uip_poll() || uip_rexmit())
+        {
+#ifdef UIPETHERNET_DEBUG_CLIENT
+            //printf("UIPClient uip_poll\r\n");
+#endif
+            if (u->packets_out[0] != NOBLOCK) {
+                if (u->packets_out[1] == NOBLOCK) {
+                    send_len = u->out_pos;
+                    if (send_len > 0) {
+                        UipEthernet::ethernet->phy.resizeBlock(u->packets_out[0], 0, send_len);
+                    }
+                }
+                else
+                    send_len = UipEthernet::ethernet->phy.blockSize(u->packets_out[0]);
+                if (send_len > 0) {
+                    UipEthernet::uipHeaderLen = ((uint8_t*)uip_appdata) - uip_buf;
+                    UipEthernet::uipPacket = UipEthernet::ethernet->phy.allocBlock(UipEthernet::uipHeaderLen + send_len);
+                    if (UipEthernet::uipPacket != NOBLOCK) {
+                        UipEthernet::ethernet->phy.copyPacket
+                            (
+                                UipEthernet::uipPacket,
+                                UipEthernet::uipHeaderLen,
+                                u->packets_out[0],
+                                0,
+                                send_len
+                            );
+                        UipEthernet::packetState |= UIPETHERNET_SENDPACKET;
+                    }
+                }
+
+                goto finish;
+            }
+        }
+
+        // don't close connection unless all outgoing packets are sent
+        if (u->state & UIP_CLIENT_CLOSE)
+        {
+#ifdef UIPETHERNET_DEBUG_CLIENT
+            printf("UIPClient state UIP_CLIENT_CLOSE\r\n");
+            UIPClient::_dumpAllData();
+#endif
+            if (u->packets_out[0] == NOBLOCK) {
+                u->state = 0;
+                uip_conn->appstate = NULL;
+                uip_close();
+#ifdef UIPETHERNET_DEBUG_CLIENT
+                printf("no blocks out -> free userdata\r\n");
+                UIPClient::_dumpAllData();
+#endif
+            }
+            else {
+                uip_stop();
+#ifdef UIPETHERNET_DEBUG_CLIENT
+                printf("blocks outstanding transfer -> uip_stop()\r\n");
+#endif
+            }
+        }
+    }
+
+finish:
+    uip_send(uip_appdata, send_len);
+    uip_len = send_len;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uip_userdata_t* TcpClient::_allocateData()
+{
+    for (uint8_t sock = 0; sock < UIP_CONNS; sock++) {
+        uip_userdata_t*     data = &TcpClient::all_data[sock];
+        if (!data->state) {
+            data->pollTimer.reset();
+            data->state = sock | UIP_CLIENT_CONNECTED;
+            memset(data->packets_in, 0, sizeof(data->packets_in) / sizeof(data->packets_in[0]));
+            memset(&data->packets_out, 0, sizeof(data->packets_out) / sizeof(data->packets_out[0]));
+            data->out_pos = 0;
+            return data;
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+uint8_t TcpClient::_currentBlock(memhandle* block)
+{
+    for (uint8_t i = 1; i < UIP_SOCKET_NUMPACKETS; i++) {
+        if (block[i] == NOBLOCK)
+            return i - 1;
+    }
+
+    return UIP_SOCKET_NUMPACKETS - 1;
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void TcpClient::_eatBlock(memhandle* block)
+{
+#ifdef UIPETHERNET_DEBUG_CLIENT
+    memhandle*  start = block;
+    printf("eatblock(%d): ", *block);
+    for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) {
+        printf("%d ", start[i]);
+    }
+
+    printf("-> ");
+#endif
+    UipEthernet::ethernet->phy.freeBlock(block[0]);
+    for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS - 1; i++) {
+        block[i] = block[i + 1];
+    }
+
+    block[UIP_SOCKET_NUMPACKETS - 1] = NOBLOCK;
+#ifdef UIPETHERNET_DEBUG_CLIENT
+    for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) {
+        printf("%d ", start[i]);
+    }
+
+    printf("\r\n");
+#endif
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void TcpClient::_flushBlocks(memhandle* block)
+{
+    for (uint8_t i = 0; i < UIP_SOCKET_NUMPACKETS; i++) {
+        UipEthernet::ethernet->phy.freeBlock(block[i]);
+        block[i] = NOBLOCK;
+    }
+}
+
+#ifdef UIPETHERNET_DEBUG_CLIENT
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+void UIPClient::_dumpAllData()
+{
+    for (uint8_t i = 0; i < UIP_CONNS; i++) {
+        printf("UIPClient::all_data[%d], state:%d packets_in: ", i, all_data[i].state);
+        for (uint8_t j = 0; j < UIP_SOCKET_NUMPACKETS; j++) {
+            printf("%d ", all_data[i].packets_in[j]);
+        }
+
+        printf("\r\n");
+        if (all_data[i].state & UIP_CLIENT_REMOTECLOSED) {
+            printf("state remote closed, local port: %d", htons(((uip_userdata_closed_t *) (&all_data[i]))->lport));
+            printf("\r\n");
+        }
+        else {
+            printf("packets_out: ");
+            for (uint8_t j = 0; j < UIP_SOCKET_NUMPACKETS; j++) {
+                printf("%d ", all_data[i].packets_out[j]);
+            }
+
+            printf("\r\n");
+            printf("out_pos: %d\r\n", all_data[i].out_pos);
+        }
+    }
+}
+#endif