225d9ea33dd7b8315a7f10f2b9b19fb7868540ff
[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_INFO
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->remaning_data_len = 0;
57         socket->sock = netconn_new(NETCONN_TCP);
58         ASSERT(socket->sock);
59
60         if(netconn_bind(socket->sock, socket->local_addr, socket->port) != ERR_OK)
61         {
62                 LOG_ERR("Connection error\n");
63                 goto error;
64         }
65
66         if(netconn_connect(socket->sock, socket->remote_addr, socket->port) != ERR_OK)
67         {
68                 LOG_ERR("Cannot create socket\n");
69                 goto error;
70         }
71
72         LOG_INFO("connected ip=%d.%d.%d.%d\n", IP_ADDR_TO_INT_TUPLE(socket->local_addr->addr));
73         return 0;
74
75 error:
76         netconn_delete(socket->sock);
77         socket->sock = NULL;
78         return -1;
79 }
80
81
82 static bool reconnect(TcpSocket *socket)
83 {
84         LOG_INFO("Reconnecting...\n");
85         // Release old socket if needed
86         if (socket->sock)
87         {
88                 if (netconn_delete(socket->sock) != ERR_OK)
89                         LOG_ERR("Error closing socket\n");
90
91                 socket->sock = NULL;
92         }
93
94         // Connect to our peer peer
95         if (tcpConnect(socket) < 0)
96         {
97                 LOG_ERR("Reconnect error!\n");
98                 socket->error |= ERR_TCP_NOTCONN;
99                 return false;
100         }
101
102         LOG_INFO("Reconnecting DONE!\n");
103
104         return true;
105 }
106
107 static int tcpsocket_close(KFile *fd)
108 {
109         TcpSocket *socket = TCPSOCKET_CAST(fd);
110         int ret = netconn_delete(socket->sock);
111         socket->sock = NULL;
112
113         if (ret)
114         {
115                 LOG_ERR("Close error\n");
116                 socket->error |= ERR_CONN_CLOSE;
117                 return EOF;
118         }
119         return 0;
120 }
121
122 static size_t tcpsocket_read(KFile *fd, void *buf, size_t len)
123 {
124         TcpSocket *socket = TCPSOCKET_CAST(fd);
125         char *_buf;
126         uint16_t read_len = 0;
127         uint16_t recv_data_len = 0;
128         size_t _len = 0;
129
130         if (socket->remaning_data_len == 0)
131         {
132                 LOG_INFO("No byte left.\n");
133                 if (socket->rx_buf_conn)
134                         netbuf_delete(socket->rx_buf_conn);
135         }
136         else if (socket->remaning_data_len > 0)
137         {
138                 LOG_INFO("Return stored bytes.\n");
139                 ASSERT(socket->rx_buf_conn);
140                 netbuf_data(socket->rx_buf_conn, (void **)&_buf, &recv_data_len);
141
142                 if (_buf)
143                 {
144                         ASSERT((recv_data_len - socket->remaning_data_len) > 0);
145                         _len = MIN((size_t)(socket->remaning_data_len), len);
146                         memcpy((char *)buf, &_buf[recv_data_len - socket->remaning_data_len], _len);
147
148                         socket->remaning_data_len -= _len;
149                         return _len;
150                 }
151                 else
152                 {
153                         LOG_ERR("No valid data to read\n");
154                         socket->remaning_data_len = 0;
155                         netbuf_delete(socket->rx_buf_conn);
156                         return 0;
157                 }
158         }
159
160         /* Try reconnecting if our socket isn't valid */
161         if (!socket->sock)
162         {
163                 if (!reconnect(socket))
164                         return 0;
165         }
166
167         while (len)
168         {
169                 LOG_INFO("Get bytes from socket.\n");
170                 socket->rx_buf_conn = netconn_recv(socket->sock);
171                 socket->error = netconn_err(socket->sock);
172
173                 if (socket->error != ERR_OK)
174                 {
175                         LOG_ERR("While recv %d\n", socket->error);
176                         socket->rx_buf_conn = NULL;
177                         return 0;
178                 }
179
180                 if (socket->rx_buf_conn)
181                 {
182                         netbuf_data(socket->rx_buf_conn, (void **)&_buf, &recv_data_len);
183                         if (_buf)
184                         {
185                                 socket->remaning_data_len = recv_data_len;
186                                 _len = MIN((size_t)recv_data_len, len);
187                                 memcpy(buf, _buf, _len);
188                                 socket->remaning_data_len -= _len;
189                         }
190
191                         if (socket->remaning_data_len <= 0)
192                         {
193                                 netbuf_delete(socket->rx_buf_conn);
194                                 return _len;
195                         }
196                 }
197
198                 len -= _len;
199                 read_len += _len;
200         }
201
202         return read_len;
203 }
204
205 static size_t tcpsocket_write(KFile *fd, const void *buf, size_t len)
206 {
207         TcpSocket *socket = TCPSOCKET_CAST(fd);
208
209         // Try reconnecting if our socket isn't valid
210         if (!socket->sock)
211         {
212                 if (!reconnect(socket))
213                         return 0;
214         }
215
216         int result = netconn_write(socket->sock, buf, len, NETCONN_COPY);
217         if (result != ERR_OK)
218         {
219                 LOG_ERR("While writing %d\n", result);
220                 if (result == ERR_RST)
221                 {
222                         LOG_INFO("Connection close\n");
223
224                         if (tcpsocket_close(fd) == EOF)
225                                 LOG_ERR("Error closing socket, leak detected\n");
226                         return 0;
227                 }
228                 return 0;
229         }
230
231         return len;
232 }
233
234 static int tcpsocket_error(KFile *fd)
235 {
236         TcpSocket *socket = TCPSOCKET_CAST(fd);
237         return socket->error;
238 }
239
240 static void tcpsocket_clearerr(KFile *fd)
241 {
242         TcpSocket *socket = TCPSOCKET_CAST(fd);
243         socket->error = 0;
244 }
245
246 void tcpsocket_init(TcpSocket *socket, struct ip_addr *local_addr, struct ip_addr *remote_addr, uint16_t port)
247 {
248         socket->sock = NULL;
249         socket->local_addr = local_addr;
250         socket->remote_addr = remote_addr;
251         socket->port = port;
252
253         socket->fd._type = KFT_TCPSOCKET;
254         socket->fd.read = tcpsocket_read;
255         socket->fd.error = tcpsocket_error;
256         socket->fd.close = tcpsocket_close;
257         socket->fd.write = tcpsocket_write;
258         socket->fd.clearerr = tcpsocket_clearerr;
259
260 }