*
* \author Luca Ottaviano <lottaviano@develer.com>
* \author Daniele Basile <asterix@develer.com>
+ *
+ * notest:avr
*/
#include "tcp_socket.h"
-#define LOG_LEVEL LOG_LVL_WARN
-#define LOG_FORMAT LOG_FMT_TERSE
+#include "cfg/cfg_tcpsocket.h"
+
+#define LOG_LEVEL TCPSOCKET_LOG_LEVEL
+#define LOG_FORMAT TCPSOCKET_LOG_FORMAT
#include <cfg/log.h>
#include <cpu/byteorder.h>
#include <lwip/ip_addr.h>
+#include <lwip/api.h>
#include <lwip/netif.h>
#include <lwip/netbuf.h>
#include <lwip/tcpip.h>
-static int tcpConnect(TcpSocket *socket)
+
+INLINE int close_socket(TcpSocket *socket)
{
- socket->sock = netconn_new(NETCONN_TCP);
- ASSERT(socket->sock);
+ /* Clean all previuos states */
+ netbuf_delete(socket->rx_buf_conn);
+ socket->rx_buf_conn = NULL;
+ socket->remaning_data_len = 0;
+ socket->error = 0;
- if(netconn_bind(socket->sock, socket->local_addr, socket->port) != ERR_OK)
- {
- LOG_ERR("Connection error\n");
- goto error;
- }
+ if (!socket->sock)
+ return 0;
+
+ /* Close socket if was opened */
+ socket->error = netconn_delete(socket->sock);
+ socket->sock = NULL;
- if(netconn_connect(socket->sock, socket->remote_addr, socket->port) != ERR_OK)
+ if (socket->error != ERR_OK)
{
- LOG_ERR("Cannot create socket\n");
- goto error;
+ LOG_ERR("Closing socket\n");
+ return -1;
}
- LOG_INFO("connected ip=%d.%d.%d.%d\n", IP_ADDR_TO_INT_TUPLE(socket->local_addr->addr));
return 0;
-
-error:
- netconn_delete(socket->sock);
- socket->sock = NULL;
- return -1;
}
-
-static bool reconnect(TcpSocket *socket)
+static bool tcpsocket_reconnect(TcpSocket *socket)
{
LOG_INFO("Reconnecting...\n");
- // Release old socket if needed
- if (socket->sock)
- {
- if (netconn_delete(socket->sock) != ERR_OK)
- LOG_ERR("Error closing socket\n");
- socket->sock = NULL;
+ /* Close socket if was opened */
+ close_socket(socket);
+
+ /* If we are in server mode we do nothing */
+ if (socket->handler)
+ return true;
+
+
+ /* Start with new connection */
+ socket->sock = netconn_new(NETCONN_TCP);
+ if(!socket->sock)
+ {
+ LOG_ERR("Unabe to alloc new connection\n");
+ socket->error = -1;
+ goto error;
}
- // Connect to our peer peer
- if (tcpConnect(socket) < 0)
+ socket->error = netconn_bind(socket->sock, socket->local_addr, socket->port);
+ if(socket->error != ERR_OK)
{
- LOG_ERR("Reconnect error!\n");
- socket->error |= ERR_TCP_NOTCONN;
- return false;
+ LOG_ERR("Connection error\n");
+ goto error;
}
- LOG_INFO("Reconnecting DONE!\n");
+ socket->error = netconn_connect(socket->sock, socket->remote_addr, socket->port);
+ if(socket->error != ERR_OK)
+ {
+ LOG_ERR("Cannot create socket\n");
+ goto error;
+ }
+ LOG_INFO("connected ip=%d.%d.%d.%d\n", IP_ADDR_TO_INT_TUPLE(socket->remote_addr->addr));
return true;
+
+error:
+ netconn_delete(socket->sock);
+ socket->sock = NULL;
+ return false;
}
static int tcpsocket_close(KFile *fd)
{
TcpSocket *socket = TCPSOCKET_CAST(fd);
- int ret = netconn_delete(socket->sock);
- socket->sock = NULL;
+ return close_socket(socket);
+}
- if (ret)
- {
- LOG_ERR("Close error\n");
- socket->error |= ERR_CONN_CLOSE;
- return EOF;
- }
- return 0;
+static KFile *tcpsocket_reopen(KFile *fd)
+{
+ TcpSocket *socket = TCPSOCKET_CAST(fd);
+ if (tcpsocket_reconnect(socket))
+ return fd;
+
+ return NULL;
}
+/*
+ * Read data from socket.
+ *
+ * The read return the bytes that had been received if they are less than we request too.
+ * Otherwise if the byte that we want read are less that the received bytes, we return only
+ * the requested bytes. To get the remaning bytes we need to make an others read, until the
+ * buffer is empty.
+ * When there are not any more bytes, a new read takes data from remote socket.
+ */
static size_t tcpsocket_read(KFile *fd, void *buf, size_t len)
{
TcpSocket *socket = TCPSOCKET_CAST(fd);
- uint16_t recv_len = 0;
- // Try reconnecting if our socket isn't valid
- if (!socket->sock)
+ char *data;
+ uint16_t read_len = 0;
+
+ if (socket->remaning_data_len <= 0)
+ {
+ LOG_INFO("No byte left.\n");
+ netbuf_delete(socket->rx_buf_conn);
+ }
+ else /* We had byte into buffer use that */
{
- if (!reconnect(socket))
+ LOG_INFO("Read stored bytes.\n");
+ if (!socket->rx_buf_conn)
+ {
+ LOG_ERR("Byte stored are corrupted!\n");
+ socket->remaning_data_len = 0;
+ return 0;
+ }
+ uint16_t tot_data_len = 0;
+ netbuf_data(socket->rx_buf_conn, (void **)&data, &tot_data_len);
+
+ if (data)
+ {
+ ASSERT(((int)tot_data_len - (int)socket->remaning_data_len) >= 0);
+ size_t chunk_len = MIN((size_t)(socket->remaning_data_len), len);
+ memcpy((char *)buf, &data[tot_data_len - socket->remaning_data_len], chunk_len);
+
+ socket->remaning_data_len -= chunk_len;
+ return chunk_len;
+ }
+ else
+ {
+ LOG_ERR("No valid data to read\n");
+ socket->remaning_data_len = 0;
+ netbuf_delete(socket->rx_buf_conn);
return 0;
+ }
}
+ /* Try reconnecting if our socket isn't valid */
+ if (!socket->sock && !tcpsocket_reconnect(socket))
+ return 0;
+
while (len)
{
- struct netbuf *rx_buf_conn = netconn_recv(socket->sock);
- if (rx_buf_conn)
- {
- netbuf_data(rx_buf_conn, (void **)&buf, &recv_len);
+ LOG_INFO("Get bytes from socket.\n");
+ socket->rx_buf_conn = netconn_recv(socket->sock);
- socket->error |= ERR_RECV_DATA;
- return recv_len;
+ socket->error = netconn_err(socket->sock);
+ if (socket->error != ERR_OK)
+ {
+ LOG_ERR("While recv %d\n", socket->error);
+ close_socket(socket);
+ return 0;
}
- // Do we have an EOF condition? If so, bailout.
- if (recv_len == 0 && len != 0)
+ size_t chunk_len = 0;
+ uint16_t data_len = 0;
+ if (socket->rx_buf_conn)
{
- LOG_INFO("Connection reset by peer\n");
- socket->error = 0;
-
- if (tcpsocket_close(fd) == EOF)
- LOG_ERR("Error closing socket, leak detected\n");
-
- return recv_len;
+ netbuf_data(socket->rx_buf_conn, (void **)&data, &data_len);
+
+ if (data)
+ {
+ chunk_len = MIN((size_t)data_len, len);
+ memcpy(buf, data, chunk_len);
+
+ socket->remaning_data_len = data_len - chunk_len;
+ }
+
+ if (socket->remaning_data_len <= 0)
+ {
+ netbuf_delete(socket->rx_buf_conn);
+ socket->rx_buf_conn = NULL;
+ socket->remaning_data_len = 0;
+ return chunk_len;
+ }
}
- len -= recv_len;
+
+ len -= chunk_len;
+ read_len += chunk_len;
}
- return recv_len;
+ return read_len;
}
static size_t tcpsocket_write(KFile *fd, const void *buf, size_t len)
{
TcpSocket *socket = TCPSOCKET_CAST(fd);
- ssize_t result;
- // Try reconnecting if our socket isn't valid
- if (!socket->sock)
- {
- if (!reconnect(socket))
- return 0;
- }
+ /* Try reconnecting if our socket isn't valid */
+ if (!socket->sock && !tcpsocket_reconnect(socket))
+ return 0;
- result = netconn_write(socket->sock, buf, len, NETCONN_COPY);
- if (result != ERR_OK)
+ socket->error = netconn_write(socket->sock, buf, len, NETCONN_COPY);
+ if (socket->error != ERR_OK)
{
- LOG_ERR("While writing %d\n", result);
- if (result == ERR_RST)
- {
- LOG_INFO("Connection close\n");
-
- if (tcpsocket_close(fd) == EOF)
- LOG_ERR("Error closing socket, leak detected\n");
- return 0;
- }
+ LOG_ERR("While writing %d\n", socket->error);
+ close_socket(socket);
return 0;
}
socket->error = 0;
}
+void tcpsocket_serverPoll(KFile *fd)
+{
+ TcpSocket *socket = TCPSOCKET_CAST(fd);
+
+
+ if (!socket->sock)
+ socket->sock = netconn_accept(socket->server_sock);
+
+ if (!socket->sock)
+ {
+ LOG_ERR("Unable to connect with client\n");
+ return;
+ }
+
+ socket->handler(fd);
+}
+
void tcpsocket_init(TcpSocket *socket, struct ip_addr *local_addr, struct ip_addr *remote_addr, uint16_t port)
{
- socket->sock = NULL;
+ memset(socket, 0, sizeof(TcpSocket));
+
socket->local_addr = local_addr;
socket->remote_addr = remote_addr;
socket->port = port;
socket->fd.close = tcpsocket_close;
socket->fd.write = tcpsocket_write;
socket->fd.clearerr = tcpsocket_clearerr;
+ socket->fd.reopen = tcpsocket_reopen;
}
+
+void tcpsocket_serverInit(TcpSocket *socket, struct ip_addr *local_addr, struct ip_addr *listen_addr, uint16_t port, tcphandler_t handler)
+{
+ tcpsocket_init(socket, local_addr, listen_addr, port);
+ socket->handler = handler;
+
+ socket->server_sock = netconn_new(NETCONN_TCP);
+ socket->error = netconn_bind(socket->server_sock, listen_addr, port);
+ socket->error = netconn_listen(socket->server_sock);
+ if(socket->error != ERR_OK)
+ LOG_ERR("Init server\n");
+}