Contiki 3.x
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 #include "contiki.h"
33 #include "sys/cc.h"
34 #include "contiki-net.h"
35 
36 #include "lib/list.h"
37 
38 #include "tcp-socket.h"
39 
40 #include <stdio.h>
41 #include <string.h>
42 
43 
44 static void relisten(struct tcp_socket *s);
45 
46 LIST(socketlist);
47 /*---------------------------------------------------------------------------*/
48 PROCESS(tcp_socket_process, "TCP socket process");
49 /*---------------------------------------------------------------------------*/
50 static void
51 call_event(struct tcp_socket *s, tcp_socket_event_t event)
52 {
53  if(s != NULL && s->event_callback != NULL) {
54  s->event_callback(s, s->ptr, event);
55  }
56 }
57 /*---------------------------------------------------------------------------*/
58 static void
59 senddata(struct tcp_socket *s)
60 {
61  int len = MIN(s->output_data_max_seg, uip_mss());
62 
63  if(s->output_senddata_len > 0) {
64  len = MIN(s->output_senddata_len, len);
65  s->output_data_send_nxt = len;
66  uip_send(s->output_data_ptr, len);
67  }
68 }
69 /*---------------------------------------------------------------------------*/
70 static void
71 acked(struct tcp_socket *s)
72 {
73  if(s->output_senddata_len > 0) {
74  /* Copy the data in the outputbuf down and update outputbufptr and
75  outputbuf_lastsent */
76 
77  if(s->output_data_send_nxt > 0) {
78  memcpy(&s->output_data_ptr[0],
79  &s->output_data_ptr[s->output_data_send_nxt],
80  s->output_data_maxlen - s->output_data_send_nxt);
81  }
82  if(s->output_data_len < s->output_data_send_nxt) {
83  printf("tcp: acked assertion failed s->output_data_len (%d) < s->output_data_send_nxt (%d)\n",
84  s->output_data_len,
85  s->output_data_send_nxt);
86  tcp_markconn(uip_conn, NULL);
87  uip_abort();
88  call_event(s, TCP_SOCKET_ABORTED);
89  relisten(s);
90  return;
91  }
92  s->output_data_len -= s->output_data_send_nxt;
93  s->output_senddata_len = s->output_data_len;
94  s->output_data_send_nxt = 0;
95 
96  call_event(s, TCP_SOCKET_DATA_SENT);
97  }
98 }
99 /*---------------------------------------------------------------------------*/
100 static void
101 newdata(struct tcp_socket *s)
102 {
103  uint16_t len, copylen, bytesleft;
104  uint8_t *dataptr;
105  len = uip_datalen();
106  dataptr = uip_appdata;
107 
108  /* We have a segment with data coming in. We copy as much data as
109  possible into the input buffer and call the input callback
110  function. The input callback returns the number of bytes that
111  should be retained in the buffer, or zero if all data should be
112  consumed. If there is data to be retained, the highest bytes of
113  data are copied down into the input buffer. */
114  do {
115  copylen = MIN(len, s->input_data_maxlen);
116  memcpy(s->input_data_ptr, dataptr, copylen);
117  if(s->input_callback) {
118  bytesleft = s->input_callback(s, s->ptr,
119  s->input_data_ptr, copylen);
120  } else {
121  bytesleft = 0;
122  }
123  if(bytesleft > 0) {
124  printf("tcp: newdata, bytesleft > 0 (%d) not implemented\n", bytesleft);
125  }
126  dataptr += copylen;
127  len -= copylen;
128 
129  } while(len > 0);
130 }
131 /*---------------------------------------------------------------------------*/
132 static void
133 relisten(struct tcp_socket *s)
134 {
135  if(s != NULL && s->listen_port != 0) {
136  s->flags |= TCP_SOCKET_FLAGS_LISTENING;
137  }
138 }
139 /*---------------------------------------------------------------------------*/
140 static void
141 appcall(void *state)
142 {
143  struct tcp_socket *s = state;
144 
145  if(s != NULL && s->c != NULL && s->c != uip_conn) {
146  /* Safe-guard: this should not happen, as the incoming event relates to
147  * a previous connection */
148  return;
149  }
150  if(uip_connected()) {
151  /* Check if this connection originated in a local listen
152  socket. We do this by checking the state pointer - if NULL,
153  this is an incoming listen connection. If so, we need to
154  connect the socket to the uip_conn and call the event
155  function. */
156  if(s == NULL) {
157  for(s = list_head(socketlist);
158  s != NULL;
159  s = list_item_next(s)) {
160  if((s->flags & TCP_SOCKET_FLAGS_LISTENING) != 0 &&
161  s->listen_port != 0 &&
162  s->listen_port == uip_htons(uip_conn->lport)) {
163  s->flags &= ~TCP_SOCKET_FLAGS_LISTENING;
164  s->output_data_max_seg = uip_mss();
165  tcp_markconn(uip_conn, s);
166  call_event(s, TCP_SOCKET_CONNECTED);
167  break;
168  }
169  }
170  } else {
171  s->output_data_max_seg = uip_mss();
172  call_event(s, TCP_SOCKET_CONNECTED);
173  }
174 
175  if(s == NULL) {
176  uip_abort();
177  } else {
178  if(uip_newdata()) {
179  newdata(s);
180  }
181  senddata(s);
182  }
183  return;
184  }
185 
186  if(uip_timedout()) {
187  call_event(s, TCP_SOCKET_TIMEDOUT);
188  relisten(s);
189  }
190 
191  if(uip_aborted()) {
192  tcp_markconn(uip_conn, NULL);
193  call_event(s, TCP_SOCKET_ABORTED);
194  relisten(s);
195 
196  }
197 
198  if(s == NULL) {
199  uip_abort();
200  return;
201  }
202 
203  if(uip_acked()) {
204  acked(s);
205  }
206  if(uip_newdata()) {
207  newdata(s);
208  }
209 
210  if(uip_rexmit() ||
211  uip_newdata() ||
212  uip_acked()) {
213  senddata(s);
214  } else if(uip_poll()) {
215  senddata(s);
216  }
217 
218  if(s->output_data_len == 0 && s->flags & TCP_SOCKET_FLAGS_CLOSING) {
219  s->flags &= ~TCP_SOCKET_FLAGS_CLOSING;
220  uip_close();
221  s->c = NULL;
222  tcp_markconn(uip_conn, NULL);
223  s->c = NULL;
224  /*call_event(s, TCP_SOCKET_CLOSED);*/
225  relisten(s);
226  }
227 
228  if(uip_closed()) {
229  tcp_markconn(uip_conn, NULL);
230  s->c = NULL;
231  call_event(s, TCP_SOCKET_CLOSED);
232  relisten(s);
233  }
234 }
235 /*---------------------------------------------------------------------------*/
236 PROCESS_THREAD(tcp_socket_process, ev, data)
237 {
238  PROCESS_BEGIN();
239  while(1) {
241 
242  if(ev == tcpip_event) {
243  appcall(data);
244  }
245  }
246  PROCESS_END();
247 }
248 /*---------------------------------------------------------------------------*/
249 static void
250 init(void)
251 {
252  static uint8_t inited = 0;
253  if(!inited) {
254  list_init(socketlist);
255  process_start(&tcp_socket_process, NULL);
256  inited = 1;
257  }
258 }
259 /*---------------------------------------------------------------------------*/
260 int
261 tcp_socket_register(struct tcp_socket *s, void *ptr,
262  uint8_t *input_databuf, int input_databuf_len,
263  uint8_t *output_databuf, int output_databuf_len,
264  tcp_socket_data_callback_t input_callback,
265  tcp_socket_event_callback_t event_callback)
266 {
267 
268  init();
269 
270  if(s == NULL) {
271  return -1;
272  }
273  s->ptr = ptr;
274  s->input_data_ptr = input_databuf;
275  s->input_data_maxlen = input_databuf_len;
276  s->output_data_len = 0;
277  s->output_data_ptr = output_databuf;
278  s->output_data_maxlen = output_databuf_len;
279  s->input_callback = input_callback;
280  s->event_callback = event_callback;
281  list_add(socketlist, s);
282 
283  s->listen_port = 0;
284  s->flags = TCP_SOCKET_FLAGS_NONE;
285  return 1;
286 }
287 /*---------------------------------------------------------------------------*/
288 int
289 tcp_socket_connect(struct tcp_socket *s,
290  const uip_ipaddr_t *ipaddr,
291  uint16_t port)
292 {
293  if(s == NULL) {
294  return -1;
295  }
296  if(s->c != NULL) {
297  tcp_markconn(s->c, NULL);
298  }
299  PROCESS_CONTEXT_BEGIN(&tcp_socket_process);
300  s->c = tcp_connect(ipaddr, uip_htons(port), s);
302  if(s->c == NULL) {
303  return -1;
304  } else {
305  return 1;
306  }
307 }
308 /*---------------------------------------------------------------------------*/
309 int
310 tcp_socket_listen(struct tcp_socket *s,
311  uint16_t port)
312 {
313  if(s == NULL) {
314  return -1;
315  }
316 
317  s->listen_port = port;
318  PROCESS_CONTEXT_BEGIN(&tcp_socket_process);
319  tcp_listen(uip_htons(port));
321  s->flags |= TCP_SOCKET_FLAGS_LISTENING;
322  return 1;
323 }
324 /*---------------------------------------------------------------------------*/
325 int
326 tcp_socket_unlisten(struct tcp_socket *s)
327 {
328  if(s == NULL) {
329  return -1;
330  }
331 
332  PROCESS_CONTEXT_BEGIN(&tcp_socket_process);
333  tcp_unlisten(uip_htons(s->listen_port));
335  s->listen_port = 0;
336  s->flags &= ~TCP_SOCKET_FLAGS_LISTENING;
337  return 1;
338 }
339 /*---------------------------------------------------------------------------*/
340 int
341 tcp_socket_send(struct tcp_socket *s,
342  const uint8_t *data, int datalen)
343 {
344  int len;
345 
346  if(s == NULL) {
347  return -1;
348  }
349 
350  len = MIN(datalen, s->output_data_maxlen - s->output_data_len);
351 
352  memcpy(&s->output_data_ptr[s->output_data_len], data, len);
353  s->output_data_len += len;
354 
355  if(s->output_senddata_len == 0) {
356  s->output_senddata_len = s->output_data_len;
357  }
358 
359  return len;
360 }
361 /*---------------------------------------------------------------------------*/
362 int
363 tcp_socket_send_str(struct tcp_socket *s,
364  const char *str)
365 {
366  return tcp_socket_send(s, (const uint8_t *)str, strlen(str));
367 }
368 /*---------------------------------------------------------------------------*/
369 int
370 tcp_socket_close(struct tcp_socket *s)
371 {
372  if(s == NULL) {
373  return -1;
374  }
375 
376  s->flags |= TCP_SOCKET_FLAGS_CLOSING;
377  return 1;
378 }
379 /*---------------------------------------------------------------------------*/
380 int
381 tcp_socket_unregister(struct tcp_socket *s)
382 {
383  if(s == NULL) {
384  return -1;
385  }
386 
387  tcp_socket_unlisten(s);
388  if(s->c != NULL) {
389  tcp_attach(s->c, NULL);
390  }
391  list_remove(socketlist, s);
392  return 1;
393 }
394 /*---------------------------------------------------------------------------*/
395 int
396 tcp_socket_max_sendlen(struct tcp_socket *s)
397 {
398  return s->output_data_maxlen - s->output_data_len;
399 }
400 /*---------------------------------------------------------------------------*/
#define uip_acked()
Has previously sent data been acknowledged?
Definition: uip.h:750
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Definition: uip-nd6.c:129
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Definition: list.c:240
Representation of a uIP TCP connection.
Definition: uip.h:1353
#define uip_abort()
Abort the current connection.
Definition: uip.h:683
#define LIST(name)
Declare a linked list.
Definition: list.h:86
static void newdata(void)
Definition: resolv.c:787
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:80
CCIF 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.
#define PROCESS_CONTEXT_END(p)
End a context switch.
Definition: process.h:440
Default definitions of C compiler quirk work-arounds.
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:772
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
void tcp_listen(uint16_t port)
Open a TCP port.
Definition: tcpip.c:261
void * list_item_next(void *item)
Get the next item following this item.
Definition: list.c:325
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74
#define uip_aborted()
Has the connection been aborted by the other end?
Definition: uip.h:782
void list_init(list_t list)
Initialize a list.
Definition: list.c:66
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer...
Definition: uip.h:652
#define PROCESS_CONTEXT_BEGIN(p)
Switch context to another process.
Definition: process.h:426
void * list_head(list_t list)
Get a pointer to the first element of a list.
Definition: list.c:83
void tcp_unlisten(uint16_t port)
Close a listening TCP port.
Definition: tcpip.c:243
#define NULL
The null pointer.
CCIF void uip_send(const void *data, int len)
Send data on the current connection.
Definition: uip.c:1960
void tcp_attach(struct uip_conn *conn, void *appstate)
Attach a TCP connection to the current process.
Definition: tcpip.c:279
uint16_t lport
The local TCP port, in network byte order.
Definition: uip.h:1356
#define uip_mss()
Get the current maximum segment size that can be sent on the current connection.
Definition: uip.h:839
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:762
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition: list.c:143
Linked list manipulation routines.
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
#define uip_rexmit()
Do we need to retransmit previously data?
Definition: uip.h:804
#define uip_close()
Close the current connection.
Definition: uip.h:672
#define uip_poll()
Is the connection being polled by uIP?
Definition: uip.h:818
#define uip_timedout()
Has the connection timed out?
Definition: uip.h:792
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition: process.h:141
#define uip_newdata()
Is new incoming data available?
Definition: uip.h:739
CCIF uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
Definition: uip.c:1948
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120