Not test for avr. Add missing include.
[bertos.git] / bertos / net / tcp_socket.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2012 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \brief TCP sockect with kfile interface.
34  *
35  * \author Luca Ottaviano <lottaviano@develer.com>
36  * \author Daniele Basile <asterix@develer.com>
37  *
38  * notest:avr
39  */
40
41 #include "tcp_socket.h"
42
43 #define LOG_LEVEL   LOG_LVL_WARN
44 #define LOG_FORMAT  LOG_FMT_TERSE
45 #include <cfg/log.h>
46 #include <cpu/byteorder.h>
47
48 #include <lwip/ip_addr.h>
49 #include <lwip/api.h>
50 #include <lwip/netif.h>
51 #include <lwip/netbuf.h>
52 #include <lwip/tcpip.h>
53
54 static int tcpConnect(TcpSocket *socket)
55 {
56         socket->sock = netconn_new(NETCONN_TCP);
57         ASSERT(socket->sock);
58
59         if(netconn_bind(socket->sock, socket->local_addr, socket->port) != ERR_OK)
60         {
61                 LOG_ERR("Connection error\n");
62                 goto error;
63         }
64
65         if(netconn_connect(socket->sock, socket->remote_addr, socket->port) != ERR_OK)
66         {
67                 LOG_ERR("Cannot create socket\n");
68                 goto error;
69         }
70
71         LOG_INFO("connected ip=%d.%d.%d.%d\n", IP_ADDR_TO_INT_TUPLE(socket->local_addr->addr));
72         return 0;
73
74 error:
75         netconn_delete(socket->sock);
76         socket->sock = NULL;
77         return -1;
78 }
79
80
81 static bool reconnect(TcpSocket *socket)
82 {
83         LOG_INFO("Reconnecting...\n");
84         // Release old socket if needed
85         if (socket->sock)
86         {
87                 if (netconn_delete(socket->sock) != ERR_OK)
88                         LOG_ERR("Error closing socket\n");
89
90                 socket->sock = NULL;
91         }
92
93         // Connect to our peer peer
94         if (tcpConnect(socket) < 0)
95         {
96                 LOG_ERR("Reconnect error!\n");
97                 socket->error |= ERR_TCP_NOTCONN;
98                 return false;
99         }
100
101         LOG_INFO("Reconnecting DONE!\n");
102
103         return true;
104 }
105
106 static int tcpsocket_close(KFile *fd)
107 {
108         TcpSocket *socket = TCPSOCKET_CAST(fd);
109         int ret = netconn_delete(socket->sock);
110         socket->sock = NULL;
111
112         if (ret)
113         {
114                 LOG_ERR("Close error\n");
115                 socket->error |= ERR_CONN_CLOSE;
116                 return EOF;
117         }
118         return 0;
119 }
120
121 static size_t tcpsocket_read(KFile *fd, void *buf, size_t len)
122 {
123         TcpSocket *socket = TCPSOCKET_CAST(fd);
124         uint16_t recv_len = 0;
125
126         // Try reconnecting if our socket isn't valid
127         if (!socket->sock)
128         {
129                 if (!reconnect(socket))
130                         return 0;
131         }
132
133         while (len)
134         {
135                 struct netbuf *rx_buf_conn = netconn_recv(socket->sock);
136                 if (rx_buf_conn)
137                 {
138                         netbuf_data(rx_buf_conn, (void **)&buf, &recv_len);
139
140                         socket->error |= ERR_RECV_DATA;
141                         return recv_len;
142                 }
143
144                 // Do we have an EOF condition? If so, bailout.
145                 if (recv_len == 0 && len != 0)
146                 {
147                         LOG_INFO("Connection reset by peer\n");
148                         socket->error = 0;
149
150                         if (tcpsocket_close(fd) == EOF)
151                                 LOG_ERR("Error closing socket, leak detected\n");
152
153                         return recv_len;
154                 }
155                 len -= recv_len;
156         }
157
158         return recv_len;
159 }
160
161 static size_t tcpsocket_write(KFile *fd, const void *buf, size_t len)
162 {
163         TcpSocket *socket = TCPSOCKET_CAST(fd);
164         ssize_t result;
165
166         // Try reconnecting if our socket isn't valid
167         if (!socket->sock)
168         {
169                 if (!reconnect(socket))
170                         return 0;
171         }
172
173         result = netconn_write(socket->sock, buf, len, NETCONN_COPY);
174         if (result != ERR_OK)
175         {
176                 LOG_ERR("While writing %d\n", result);
177                 if (result == ERR_RST)
178                 {
179                         LOG_INFO("Connection close\n");
180
181                         if (tcpsocket_close(fd) == EOF)
182                                 LOG_ERR("Error closing socket, leak detected\n");
183                         return 0;
184                 }
185                 return 0;
186         }
187
188         return len;
189 }
190
191 static int tcpsocket_error(KFile *fd)
192 {
193         TcpSocket *socket = TCPSOCKET_CAST(fd);
194         return socket->error;
195 }
196
197 static void tcpsocket_clearerr(KFile *fd)
198 {
199         TcpSocket *socket = TCPSOCKET_CAST(fd);
200         socket->error = 0;
201 }
202
203 void tcpsocket_init(TcpSocket *socket, struct ip_addr *local_addr, struct ip_addr *remote_addr, uint16_t port)
204 {
205         socket->sock = NULL;
206         socket->local_addr = local_addr;
207         socket->remote_addr = remote_addr;
208         socket->port = port;
209
210         socket->fd._type = KFT_TCPSOCKET;
211         socket->fd.read = tcpsocket_read;
212         socket->fd.error = tcpsocket_error;
213         socket->fd.close = tcpsocket_close;
214         socket->fd.write = tcpsocket_write;
215         socket->fd.clearerr = tcpsocket_clearerr;
216
217 }