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