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