From 121f4d98bd4d74629809697ab66001ebcbac76ce Mon Sep 17 00:00:00 2001 From: Daniele Basile Date: Thu, 19 Jan 2012 19:03:00 +0100 Subject: [PATCH] Add first implementation of sockect with kfile interface. --- bertos/net/tcp_socket.c | 214 ++++++++++++++++++++++++++++++++++++++++ bertos/net/tcp_socket.h | 76 ++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 bertos/net/tcp_socket.c create mode 100644 bertos/net/tcp_socket.h diff --git a/bertos/net/tcp_socket.c b/bertos/net/tcp_socket.c new file mode 100644 index 00000000..1924a604 --- /dev/null +++ b/bertos/net/tcp_socket.c @@ -0,0 +1,214 @@ +/** + * \file + * + * + * \brief TCP sockect with kfile interface. + * + * \author Luca Ottaviano + * \author Daniele Basile + */ + +#include "tcp_socket.h" + +#define LOG_LEVEL LOG_LVL_WARN +#define LOG_FORMAT LOG_FMT_TERSE +#include +#include + +#include +#include +#include +#include + +static int tcpConnect(TcpSocket *socket) +{ + socket->sock = netconn_new(NETCONN_TCP); + ASSERT(socket->sock); + + if(netconn_bind(socket->sock, socket->local_addr, socket->port) != ERR_OK) + { + LOG_ERR("Connection error\n"); + goto error; + } + + if(netconn_connect(socket->sock, socket->remote_addr, socket->port) != 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->local_addr->addr)); + return 0; + +error: + netconn_delete(socket->sock); + socket->sock = NULL; + return -1; +} + + +static bool 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; + } + + // Connect to our peer peer + if (tcpConnect(socket) < 0) + { + LOG_ERR("Reconnect error!\n"); + socket->error |= ERR_TCP_NOTCONN; + return false; + } + + LOG_INFO("Reconnecting DONE!\n"); + + return true; +} + +static int tcpsocket_close(KFile *fd) +{ + TcpSocket *socket = TCPSOCKET_CAST(fd); + int ret = netconn_delete(socket->sock); + socket->sock = NULL; + + if (ret) + { + LOG_ERR("Close error\n"); + socket->error |= ERR_CONN_CLOSE; + return EOF; + } + return 0; +} + +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) + { + if (!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); + + socket->error |= ERR_RECV_DATA; + return recv_len; + } + + // Do we have an EOF condition? If so, bailout. + if (recv_len == 0 && len != 0) + { + 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; + } + len -= recv_len; + } + + return recv_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; + } + + result = netconn_write(socket->sock, buf, len, NETCONN_COPY); + if (result != 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; + } + return 0; + } + + return len; +} + +static int tcpsocket_error(KFile *fd) +{ + TcpSocket *socket = TCPSOCKET_CAST(fd); + return socket->error; +} + +static void tcpsocket_clearerr(KFile *fd) +{ + TcpSocket *socket = TCPSOCKET_CAST(fd); + socket->error = 0; +} + +void tcpsocket_init(TcpSocket *socket, struct ip_addr *local_addr, struct ip_addr *remote_addr, uint16_t port) +{ + socket->sock = NULL; + socket->local_addr = local_addr; + socket->remote_addr = remote_addr; + socket->port = port; + + socket->fd._type = KFT_TCPSOCKET; + socket->fd.read = tcpsocket_read; + socket->fd.error = tcpsocket_error; + socket->fd.close = tcpsocket_close; + socket->fd.write = tcpsocket_write; + socket->fd.clearerr = tcpsocket_clearerr; + +} diff --git a/bertos/net/tcp_socket.h b/bertos/net/tcp_socket.h new file mode 100644 index 00000000..095321ee --- /dev/null +++ b/bertos/net/tcp_socket.h @@ -0,0 +1,76 @@ +/** + * \file + * + * + * \brief TCP sockect with kfile interface. + * + * \author Luca Ottaviano + * \author Daniele Basile + */ + +#ifndef NET_TCP_SOCKET_H +#define NET_TCP_SOCKET_H + +#include + +#include + +#include +#include + +#define ERR_TCP_NOTCONN BV(0); +#define ERR_CONN_RESET BV(1); +#define ERR_CONN_CLOSE BV(2); +#define ERR_RECV_DATA BV(3); + + +typedef struct TcpSocket +{ + KFile fd; + struct netconn *sock; + struct ip_addr *local_addr; + struct ip_addr *remote_addr; + uint16_t port; + uint16_t error; +} TcpSocket; + +#define KFT_TCPSOCKET MAKE_ID('T', 'S', 'C', 'K') + +INLINE TcpSocket *TCPSOCKET_CAST(KFile *fd) +{ + ASSERT(fd->_type == KFT_TCPSOCKET); + return (TcpSocket *)fd; +} + + +void tcpsocket_init(TcpSocket *socket, struct ip_addr *local_addr, struct ip_addr *remote_addr, uint16_t port); + +#endif /* NET_TCP_SOCKET_H */ -- 2.25.1