Contiki-NG
tcp-socket.c
1 /*
2  * Copyright (c) 2012-2014, Thingsquare, http://www.thingsquare.com/.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31 
32 #define DEBUG DEBUG_NONE
33 #include "net/ipv6/uip-debug.h"
34 
35 #include "contiki.h"
36 #include "sys/cc.h"
37 #include "contiki-net.h"
38 
39 #include "lib/list.h"
40 
41 #include "tcp-socket.h"
42 
43 #include <string.h>
44 
45 static void relisten(struct tcp_socket *s);
46 
47 LIST(socketlist);
48 /*---------------------------------------------------------------------------*/
49 PROCESS(tcp_socket_process, "TCP socket process");
50 /*---------------------------------------------------------------------------*/
51 static void
52 call_event(struct tcp_socket *s, tcp_socket_event_t event)
53 {
54  if(s != NULL && s->event_callback != NULL) {
55  s->event_callback(s, s->ptr, event);
56  }
57 }
58 /*---------------------------------------------------------------------------*/
59 static void
60 senddata(struct tcp_socket *s)
61 {
62  int len = MIN(s->output_data_max_seg, uip_mss());
63 
64  if(s->output_senddata_len > 0) {
65  len = MIN(s->output_senddata_len, len);
66  s->output_data_send_nxt = len;
67  uip_send(s->output_data_ptr, len);
68  }
69 }
70 /*---------------------------------------------------------------------------*/
71 static void
72 acked(struct tcp_socket *s)
73 {
74  if(s->output_senddata_len > 0) {
75  /* Copy the data in the outputbuf down and update outputbufptr and
76  outputbuf_lastsent */
77 
78  if(s->output_data_send_nxt > 0) {
79  memmove(&s->output_data_ptr[0],
80  &s->output_data_ptr[s->output_data_send_nxt],
81  s->output_data_maxlen - s->output_data_send_nxt);
82  }
83  if(s->output_data_len < s->output_data_send_nxt) {
84  PRINTF("tcp: acked assertion failed s->output_data_len (%d) < s->output_data_send_nxt (%d)\n",
85  s->output_data_len,
86  s->output_data_send_nxt);
87  tcp_markconn(uip_conn, NULL);
88  uip_abort();
89  call_event(s, TCP_SOCKET_ABORTED);
90  relisten(s);
91  return;
92  }
93  s->output_data_len -= s->output_data_send_nxt;
94  s->output_senddata_len = s->output_data_len;
95  s->output_data_send_nxt = 0;
96 
97  call_event(s, TCP_SOCKET_DATA_SENT);
98  }
99 }
100 /*---------------------------------------------------------------------------*/
101 static void
102 newdata(struct tcp_socket *s)
103 {
104  uint16_t len, copylen, bytesleft;
105  uint8_t *dataptr;
106  len = uip_datalen();
107  dataptr = uip_appdata;
108 
109  /* We have a segment with data coming in. We copy as much data as
110  possible into the input buffer and call the input callback
111  function. The input callback returns the number of bytes that
112  should be retained in the buffer, or zero if all data should be
113  consumed. If there is data to be retained, the highest bytes of
114  data are copied down into the input buffer. */
115  do {
116  copylen = MIN(len, s->input_data_maxlen);
117  memcpy(s->input_data_ptr, dataptr, copylen);
118  if(s->input_callback) {
119  bytesleft = s->input_callback(s, s->ptr,
120  s->input_data_ptr, copylen);
121  } else {
122  bytesleft = 0;
123  }
124  if(bytesleft > 0) {
125  PRINTF("tcp: newdata, bytesleft > 0 (%d) not implemented\n", bytesleft);
126  }
127  dataptr += copylen;
128  len -= copylen;
129 
130  } while(len > 0);
131 }
132 /*---------------------------------------------------------------------------*/
133 static void
134 relisten(struct tcp_socket *s)
135 {
136  if(s != NULL && s->listen_port != 0) {
137  s->flags |= TCP_SOCKET_FLAGS_LISTENING;
138  }
139 }
140 /*---------------------------------------------------------------------------*/
141 static void
142 appcall(void *state)
143 {
144  struct tcp_socket *s = state;
145 
146  if(s != NULL && s->c != NULL && s->c != uip_conn) {
147  /* Safe-guard: this should not happen, as the incoming event relates to
148  * a previous connection */
149  return;
150  }
151  if(uip_connected()) {
152  /* Check if this connection originated in a local listen
153  socket. We do this by checking the state pointer - if NULL,
154  this is an incoming listen connection. If so, we need to
155  connect the socket to the uip_conn and call the event
156  function. */
157  if(s == NULL) {
158  for(s = list_head(socketlist);
159  s != NULL;
160  s = list_item_next(s)) {
161  if((s->flags & TCP_SOCKET_FLAGS_LISTENING) != 0 &&
162  s->listen_port != 0 &&
163  s->listen_port == uip_htons(uip_conn->lport)) {
164  s->flags &= ~TCP_SOCKET_FLAGS_LISTENING;
165  s->output_data_max_seg = uip_mss();
166  tcp_markconn(uip_conn, s);
167  call_event(s, TCP_SOCKET_CONNECTED);
168  break;
169  }
170  }
171  } else {
172  s->output_data_max_seg = uip_mss();
173  call_event(s, TCP_SOCKET_CONNECTED);
174  }
175 
176  if(s == NULL) {
177  uip_abort();
178  } else {
179  if(uip_newdata()) {
180  newdata(s);
181  }
182  senddata(s);
183  }
184  return;
185  }
186 
187  if(uip_timedout()) {
188  call_event(s, TCP_SOCKET_TIMEDOUT);
189  relisten(s);
190  }
191 
192  if(uip_aborted()) {
193  tcp_markconn(uip_conn, NULL);
194  call_event(s, TCP_SOCKET_ABORTED);
195  relisten(s);
196 
197  }
198 
199  if(s == NULL) {
200  uip_abort();
201  return;
202  }
203 
204  if(uip_acked()) {
205  acked(s);
206  }
207  if(uip_newdata()) {
208  newdata(s);
209  }
210 
211  if(uip_rexmit() ||
212  uip_newdata() ||
213  uip_acked()) {
214  senddata(s);
215  } else if(uip_poll()) {
216  senddata(s);
217  }
218 
219  if(s->output_data_len == 0 && s->flags & TCP_SOCKET_FLAGS_CLOSING) {
220  s->flags &= ~TCP_SOCKET_FLAGS_CLOSING;
221  uip_close();
222  s->c = NULL;
223  tcp_markconn(uip_conn, NULL);
224  s->c = NULL;
225  /*call_event(s, TCP_SOCKET_CLOSED);*/
226  relisten(s);
227  }
228 
229  if(uip_closed()) {
230  tcp_markconn(uip_conn, NULL);
231  s->c = NULL;
232  call_event(s, TCP_SOCKET_CLOSED);
233  relisten(s);
234  }
235 }
236 /*---------------------------------------------------------------------------*/
237 PROCESS_THREAD(tcp_socket_process, ev, data)
238 {
239  PROCESS_BEGIN();
240  while(1) {
242 
243  if(ev == tcpip_event) {
244  appcall(data);
245  }
246  }
247  PROCESS_END();
248 }
249 /*---------------------------------------------------------------------------*/
250 static void
251 init(void)
252 {
253  static uint8_t inited = 0;
254  if(!inited) {
255  list_init(socketlist);
256  process_start(&tcp_socket_process, NULL);
257  inited = 1;
258  }
259 }
260 /*---------------------------------------------------------------------------*/
261 int
262 tcp_socket_register(struct tcp_socket *s, void *ptr,
263  uint8_t *input_databuf, int input_databuf_len,
264  uint8_t *output_databuf, int output_databuf_len,
265  tcp_socket_data_callback_t input_callback,
266  tcp_socket_event_callback_t event_callback)
267 {
268 
269  init();
270 
271  if(s == NULL) {
272  return -1;
273  }
274  s->ptr = ptr;
275  s->input_data_ptr = input_databuf;
276  s->input_data_maxlen = input_databuf_len;
277  s->output_data_len = 0;
278  s->output_data_ptr = output_databuf;
279  s->output_data_maxlen = output_databuf_len;
280  s->input_callback = input_callback;
281  s->event_callback = event_callback;
282  list_add(socketlist, s);
283 
284  s->listen_port = 0;
285  s->flags = TCP_SOCKET_FLAGS_NONE;
286  return 1;
287 }
288 /*---------------------------------------------------------------------------*/
289 int
290 tcp_socket_connect(struct tcp_socket *s,
291  const uip_ipaddr_t *ipaddr,
292  uint16_t port)
293 {
294  if(s == NULL) {
295  return -1;
296  }
297  if(s->c != NULL) {
298  tcp_markconn(s->c, NULL);
299  }
300  PROCESS_CONTEXT_BEGIN(&tcp_socket_process);
301  s->c = tcp_connect(ipaddr, uip_htons(port), s);
303  if(s->c == NULL) {
304  return -1;
305  } else {
306  return 1;
307  }
308 }
309 /*---------------------------------------------------------------------------*/
310 int
311 tcp_socket_listen(struct tcp_socket *s,
312  uint16_t port)
313 {
314  if(s == NULL) {
315  return -1;
316  }
317 
318  s->listen_port = port;
319  PROCESS_CONTEXT_BEGIN(&tcp_socket_process);
320  tcp_listen(uip_htons(port));
322  s->flags |= TCP_SOCKET_FLAGS_LISTENING;
323  return 1;
324 }
325 /*---------------------------------------------------------------------------*/
326 int
327 tcp_socket_unlisten(struct tcp_socket *s)
328 {
329  if(s == NULL) {
330  return -1;
331  }
332 
333  PROCESS_CONTEXT_BEGIN(&tcp_socket_process);
334  tcp_unlisten(uip_htons(s->listen_port));
336  s->listen_port = 0;
337  s->flags &= ~TCP_SOCKET_FLAGS_LISTENING;
338  return 1;
339 }
340 /*---------------------------------------------------------------------------*/
341 int
342 tcp_socket_send(struct tcp_socket *s,
343  const uint8_t *data, int datalen)
344 {
345  int len;
346 
347  if(s == NULL) {
348  return -1;
349  }
350 
351  len = MIN(datalen, s->output_data_maxlen - s->output_data_len);
352 
353  memmove(&s->output_data_ptr[s->output_data_len], data, len);
354  s->output_data_len += len;
355 
356  if(s->output_senddata_len == 0) {
357  s->output_senddata_len = s->output_data_len;
358  }
359 
360  tcpip_poll_tcp(s->c);
361 
362  return len;
363 }
364 /*---------------------------------------------------------------------------*/
365 int
366 tcp_socket_send_str(struct tcp_socket *s,
367  const char *str)
368 {
369  return tcp_socket_send(s, (const uint8_t *)str, strlen(str));
370 }
371 /*---------------------------------------------------------------------------*/
372 int
373 tcp_socket_close(struct tcp_socket *s)
374 {
375  if(s == NULL) {
376  return -1;
377  }
378 
379  s->flags |= TCP_SOCKET_FLAGS_CLOSING;
380  return 1;
381 }
382 /*---------------------------------------------------------------------------*/
383 int
384 tcp_socket_unregister(struct tcp_socket *s)
385 {
386  if(s == NULL) {
387  return -1;
388  }
389 
390  tcp_socket_unlisten(s);
391  if(s->c != NULL) {
392  tcp_attach(s->c, NULL);
393  }
394  list_remove(socketlist, s);
395  return 1;
396 }
397 /*---------------------------------------------------------------------------*/
398 int
399 tcp_socket_max_sendlen(struct tcp_socket *s)
400 {
401  return s->output_data_maxlen - s->output_data_len;
402 }
403 /*---------------------------------------------------------------------------*/
404 int
405 tcp_socket_queuelen(struct tcp_socket *s)
406 {
407  return s->output_data_len;
408 }
409 /*---------------------------------------------------------------------------*/
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Definition: uip-nd6.c:116
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
#define uip_close()
Close the current connection.
Definition: uip.h:659
Representation of a uIP TCP connection.
Definition: uip.h:1324
#define PROCESS_CONTEXT_END(p)
End a context switch.
Definition: process.h:440
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition: process.h:141
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:749
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:62
void tcp_attach(struct uip_conn *conn, void *appstate)
Attach a TCP connection to the current process.
Definition: tcpip.c:247
void tcpip_poll_tcp(struct uip_conn *conn)
Cause a specified TCP connection to be polled.
Definition: tcpip.c:758
#define uip_mss()
Get the current maximum segment size that can be sent on the current connection.
Definition: uip.h:826
A set of debugging macros for the IP stack
static void newdata(void)
Definition: resolv.c:717
uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
Definition: uip6.c:2334
void tcp_unlisten(uint16_t port)
Close a listening TCP port.
Definition: tcpip.c:211
#define uip_abort()
Abort the current connection.
Definition: uip.h:670
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:726
Linked list manipulation routines.
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:82
uint16_t lport
The local TCP port, in network byte order.
Definition: uip.h:1327
#define uip_acked()
Has previously sent data been acknowledged?
Definition: uip.h:737
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:142
#define uip_aborted()
Has the connection been aborted by the other end?
Definition: uip.h:769
void list_init(list_t list)
Initialize a list.
Definition: list.c:65
void uip_send(const void *data, int len)
Send data on the current connection.
Definition: uip6.c:2346
#define LIST(name)
Declare a linked list.
Definition: list.h:89
#define PROCESS_CONTEXT_BEGIN(p)
Switch context to another process.
Definition: process.h:426
void tcp_listen(uint16_t port)
Open a TCP port.
Definition: tcpip.c:229
#define uip_timedout()
Has the connection timed out?
Definition: uip.h:779
#define uip_rexmit()
Do we need to retransmit previously data?
Definition: uip.h:791
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:759
Default definitions of C compiler quirk work-arounds.
#define uip_poll()
Is the connection being polled by uIP?
Definition: uip.h:805
PROCESS_THREAD(cc2538_rf_process, ev, data)
Implementation of the cc2538 RF driver process.
Definition: cc2538-rf.c:1107
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:237
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:322
struct uip_conn * tcp_connect(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
Open a TCP connection to the specified IP address and port.
void * uip_appdata
Pointer to the application data in the packet buffer.
Definition: uip6.c:148
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:639
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99