From d1d82eb179af6b14862b52b27697a9a409b75a07 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Mon, 24 Mar 2014 08:42:13 +0100 Subject: [PATCH 1/5] The UDP socket API code --- core/net/ip/udp-socket.c | 199 +++++++++++++++++++++++++++++++++++++++ core/net/ip/udp-socket.h | 193 +++++++++++++++++++++++++++++++++++++ 2 files changed, 392 insertions(+) create mode 100644 core/net/ip/udp-socket.c create mode 100644 core/net/ip/udp-socket.h diff --git a/core/net/ip/udp-socket.c b/core/net/ip/udp-socket.c new file mode 100644 index 000000000..700306ca1 --- /dev/null +++ b/core/net/ip/udp-socket.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2012-2014, Thingsquare, http://www.thingsquare.com/. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "contiki-net.h" +#include "udp-socket.h" + +#include + +PROCESS(udp_socket_process, "UDP socket process"); + +static uint8_t buf[UIP_BUFSIZE]; + +#define UIP_IP_BUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN]) + + +/*---------------------------------------------------------------------------*/ +static void +init(void) +{ + static uint8_t inited = 0; + if(!inited) { + inited = 1; + process_start(&udp_socket_process, NULL); + } +} +/*---------------------------------------------------------------------------*/ +int +udp_socket_register(struct udp_socket *c, + void *ptr, + udp_socket_input_callback_t input_callback) +{ + init(); + + if(c == NULL) { + return -1; + } + c->ptr = ptr; + c->input_callback = input_callback; + + c->p = PROCESS_CURRENT(); + PROCESS_CONTEXT_BEGIN(&udp_socket_process); + c->udp_conn = udp_new(NULL, 0, c); + PROCESS_CONTEXT_END(); + + if(c->udp_conn == NULL) { + return -1; + } + return 1; +} +/*---------------------------------------------------------------------------*/ +int +udp_socket_close(struct udp_socket *c) +{ + if(c == NULL) { + return -1; + } + if(c->udp_conn != NULL) { + uip_udp_remove(c->udp_conn); + return 1; + } + return -1; +} +/*---------------------------------------------------------------------------*/ +int +udp_socket_bind(struct udp_socket *c, + uint16_t local_port) +{ + if(c == NULL || c->udp_conn == NULL) { + return -1; + } + udp_bind(c->udp_conn, UIP_HTONS(local_port)); + + return 1; +} +/*---------------------------------------------------------------------------*/ +int +udp_socket_connect(struct udp_socket *c, + uip_ipaddr_t *remote_addr, + uint16_t remote_port) +{ + if(c == NULL || c->udp_conn == NULL) { + return -1; + } + + if(remote_addr != NULL) { + uip_ipaddr_copy(&c->udp_conn->ripaddr, remote_addr); + } + c->udp_conn->rport = UIP_HTONS(remote_port); + return 1; +} +/*---------------------------------------------------------------------------*/ +int +udp_socket_send(struct udp_socket *c, + const void *data, uint16_t datalen) +{ + if(c == NULL || c->udp_conn == NULL) { + return -1; + } + + uip_udp_packet_send(c->udp_conn, data, datalen); + return datalen; +} +/*---------------------------------------------------------------------------*/ +int +udp_socket_sendto(struct udp_socket *c, + const void *data, uint16_t datalen, + const uip_ipaddr_t *to, + uint16_t port) +{ + if(c == NULL || c->udp_conn == NULL) { + return -1; + } + + if(c->udp_conn != NULL) { + uip_udp_packet_sendto(c->udp_conn, data, datalen, + to, UIP_HTONS(port)); + return datalen; + } + return -1; +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(udp_socket_process, ev, data) +{ + struct udp_socket *c; + PROCESS_BEGIN(); + + while(1) { + PROCESS_WAIT_EVENT(); + if(ev == tcpip_event) { + + /* An appstate pointer is passed to use from the IP stack + through the 'data' pointer. We registered this appstate when + we did the udp_new() call in udp_socket_register() as the + struct udp_socket pointer. So we extract this + pointer and use it when calling the reception callback. */ + c = (struct udp_socket *)data; + + /* Defensive coding: although the appstate *should* be non-null + here, we make sure to avoid the program crashing on us. */ + if(c != NULL) { + + /* If we were called because of incoming data, we should call + the reception callback. */ + if(uip_newdata()) { + /* Copy the data from the uIP data buffer into our own + buffer to avoid the uIP buffer being messed with by the + callee. */ + memcpy(buf, uip_appdata, uip_datalen()); + + /* Call the client process. We use the PROCESS_CONTEXT + mechanism to temporarily switch process context to the + client process. */ + if(c->input_callback != NULL) { + PROCESS_CONTEXT_BEGIN(c->p); + c->input_callback(c, c->ptr, + &(UIP_IP_BUF->srcipaddr), + UIP_HTONS(UIP_IP_BUF->srcport), + &(UIP_IP_BUF->destipaddr), + UIP_HTONS(UIP_IP_BUF->destport), + buf, uip_datalen()); + PROCESS_CONTEXT_END(); + } + } + } + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/core/net/ip/udp-socket.h b/core/net/ip/udp-socket.h new file mode 100644 index 000000000..0498bf892 --- /dev/null +++ b/core/net/ip/udp-socket.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2012-2014, Thingsquare, http://www.thingsquare.com/. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef UDP_SOCKET_H +#define UDP_SOCKET_H + +#include "net/ip/uip.h" + +struct udp_socket; + +/** + * \brief A UDP socket callback function + * \param c A pointer to the struct udp_socket that received the data + * \param ptr An opaque pointer that was specified when the UDP socket was registered with udp_socket_register() + * \param source_addr The IP address from which the datagram was sent + * \param source_port The UDP port number, in host byte order, from which the datagram was sent + * \param dest_addr The IP address that this datagram was sent to + * \param dest_port The UDP port number, in host byte order, that the datagram was sent to + * \param data A pointer to the data contents of the UDP datagram + * \param datalen The length of the data being pointed to by the data pointer + * + * Each UDP socket has a callback function that is + * registered as part of the call to + * udp_socket_register(). The callback function gets + * called every time a UDP packet is received. + */ +typedef void (* udp_socket_input_callback_t)(struct udp_socket *c, + void *ptr, + const uip_ipaddr_t *source_addr, + uint16_t source_port, + const uip_ipaddr_t *dest_addr, + uint16_t dest_port, + const uint8_t *data, + uint16_t datalen); + +struct udp_socket { + udp_socket_input_callback_t input_callback; + void *ptr; + + struct process *p; + + struct uip_udp_conn *udp_conn; + +}; + +/** + * \brief Register a UDP socket + * \param c A pointer to the struct udp_socket that should be registered + * \param ptr An opaque pointer that will be passed to callbacks + * \param receive_callback A function pointer to the callback function that will be called when data arrives + * \retval -1 The registration failed + * \retval 1 The registration succeeded + * + * This function registers the UDP socket with the + * system. A UDP socket must be registered before any data + * can be sent or received over the socket. + * + * The caller must allocate memory for the struct + * udp_socket that is to be registered. + * + * A UDP socket can begin to receive data by calling + * udp_socket_bind(). + * + */ +int udp_socket_register(struct udp_socket *c, + void *ptr, + udp_socket_input_callback_t receive_callback); + +/** + * \brief Bind a UDP socket to a local port + * \param c A pointer to the struct udp_socket that should be bound to a local port + * \param local_port The UDP port number, in host byte order, to bind the UDP socket to + * \retval -1 Binding the UDP socket to the local port failed + * \retval 1 Binding the UDP socket to the local port succeeded + * + * This function binds the UDP socket to a local port so + * that it will begin to receive data that arrives on the + * specified port. A UDP socket will receive data + * addressed to the specified port number on any IP + * address of the host. + * + * A UDP socket that is bound to a local port will use + * this port number as a source port in outgoing UDP + * messages. + * + */ +int udp_socket_bind(struct udp_socket *c, + uint16_t local_port); + +/** + * \brief Bind a UDP socket to a remote address and port + * \param c A pointer to the struct udp_socket that should be connected + * \param remote_addr The IP address of the remote host, or NULL if the UDP socket should only be connected to a specific port + * \param remote_port The UDP port number, in host byte order, to which the UDP socket should be connected + * \retval -1 Connecting the UDP socket failed + * \retval 1 Connecting the UDP socket succeeded + * + * This function connects the UDP socket to a specific + * remote port and optional remote IP address. When a UDP + * socket is connected to a remote port and address, it + * will only receive packets that are sent from the remote + * port and address. When sending data over a connected + * UDP socket, the data will be sent to the connected + * remote address. + * + * A UDP socket can be connected to a remote port, but not + * a remote IP address, by providing a NULL parameter as + * the remote_addr parameter. This lets the UDP socket + * receive data from any IP address on the specified port. + * + */ +int udp_socket_connect(struct udp_socket *c, + uip_ipaddr_t *remote_addr, + uint16_t remote_port); +/** + * \brief Send data on a UDP socket + * \param c A pointer to the struct udp_socket on which the data should be sent + * \param data A pointer to the data that should be sent + * \param datalen The length of the data to be sent + * \return The number of bytes sent, or -1 if an error occurred + * + * This function sends data over a UDP socket. The UDP + * socket must have been connected to a remote address and + * port with udp_socket_connect(). + * + */ +int udp_socket_send(struct udp_socket *c, + const void *data, uint16_t datalen); + +/** + * \brief Send data on a UDP socket to a specific address and port + * \param c A pointer to the struct udp_socket on which the data should be sent + * \param data A pointer to the data that should be sent + * \param datalen The length of the data to be sent + * \param addr The IP address to which the data should be sent + * \param port The UDP port number, in host byte order, to which the data should be sent + * \return The number of bytes sent, or -1 if an error occurred + * + * This function sends data over a UDP socket to a + * specific address and port. + * + * The UDP socket does not have to be connected to use + * this function. + * + */ +int udp_socket_sendto(struct udp_socket *c, + const void *data, uint16_t datalen, + const uip_ipaddr_t *addr, uint16_t port); + +/** + * \brief Close a UDP socket + * \param c A pointer to the struct udp_socket to be closed + * \retval -1 If closing the UDP socket failed + * \retval 1 If closing the UDP socket succeeded + * + * This function closes a UDP socket that has previously + * been registered with udp_socket_register(). All + * registered UDP sockets must be closed before exiting + * the process that registered them, or undefined behavior + * may occur. + * + */ +int udp_socket_close(struct udp_socket *c); + +#endif /* UDP_SOCKET_H */ From d53ba5aa3c053c521fac57de24c37a3e0d4d86a2 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Mon, 24 Mar 2014 08:42:28 +0100 Subject: [PATCH 2/5] The TCP socket API code --- core/net/ip/tcp-socket.c | 365 +++++++++++++++++++++++++++++++++++++++ core/net/ip/tcp-socket.h | 268 ++++++++++++++++++++++++++++ 2 files changed, 633 insertions(+) create mode 100644 core/net/ip/tcp-socket.c create mode 100644 core/net/ip/tcp-socket.h diff --git a/core/net/ip/tcp-socket.c b/core/net/ip/tcp-socket.c new file mode 100644 index 000000000..f914ae206 --- /dev/null +++ b/core/net/ip/tcp-socket.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2012-2014, Thingsquare, http://www.thingsquare.com/. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "contiki.h" +#include "contiki-net.h" + +#include "lib/list.h" + +#include "tcp-socket.h" + +#include +#include + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +LIST(socketlist); +/*---------------------------------------------------------------------------*/ +PROCESS(tcp_socket_process, "TCP socket process"); +/*---------------------------------------------------------------------------*/ +static void +call_event(struct tcp_socket *s, tcp_socket_event_t event) +{ + if(s != NULL && s->event_callback != NULL) { + s->event_callback(s, s->ptr, event); + } +} +/*---------------------------------------------------------------------------*/ +static void +senddata(struct tcp_socket *s) +{ + int len; + + if(s->output_data_len > 0) { + len = MIN(s->output_data_len, uip_mss()); + s->output_data_send_nxt = len; + uip_send(s->output_data_ptr, len); + } +} +/*---------------------------------------------------------------------------*/ +static void +acked(struct tcp_socket *s) +{ + if(s->output_data_len > 0) { + /* Copy the data in the outputbuf down and update outputbufptr and + outputbuf_lastsent */ + + if(s->output_data_send_nxt > 0) { + memcpy(&s->output_data_ptr[0], + &s->output_data_ptr[s->output_data_send_nxt], + s->output_data_maxlen - s->output_data_send_nxt); + } + if(s->output_data_len < s->output_data_send_nxt) { + printf("tcp: acked assertion failed s->output_data_len (%d) < s->output_data_send_nxt (%d)\n", + s->output_data_len, + s->output_data_send_nxt); + } + s->output_data_len -= s->output_data_send_nxt; + s->output_data_send_nxt = 0; + + call_event(s, TCP_SOCKET_DATA_SENT); + } +} +/*---------------------------------------------------------------------------*/ +static void +newdata(struct tcp_socket *s) +{ + uint16_t len, copylen, bytesleft; + uint8_t *dataptr; + len = uip_datalen(); + dataptr = uip_appdata; + + /* We have a segment with data coming in. We copy as much data as + possible into the input buffer and call the input callback + function. The input callback returns the number of bytes that + should be retained in the buffer, or zero if all data should be + consumed. If there is data to be retained, the highest bytes of + data are copied down into the input buffer. */ + do { + copylen = MIN(len, s->input_data_maxlen); + memcpy(s->input_data_ptr, dataptr, copylen); + if(s->input_callback) { + bytesleft = s->input_callback(s, s->ptr, + s->input_data_ptr, copylen); + } else { + bytesleft = 0; + } + if(bytesleft > 0) { + printf("tcp: newdata, bytesleft > 0 (%d) not implemented\n", bytesleft); + } + dataptr += copylen; + len -= copylen; + + } while(len > 0); +} +/*---------------------------------------------------------------------------*/ +static void +relisten(struct tcp_socket *s) +{ + if(s != NULL && s->listen_port != 0) { + s->flags |= TCP_SOCKET_FLAGS_LISTENING; + } +} +/*---------------------------------------------------------------------------*/ +static void +appcall(void *state) +{ + struct tcp_socket *s = state; + + if(uip_connected()) { + /* Check if this connection originated in a local listen + socket. We do this by checking the state pointer - if NULL, + this is an incoming listen connection. If so, we need to + connect the socket to the uip_conn and call the event + function. */ + if(s == NULL) { + for(s = list_head(socketlist); + s != NULL; + s = list_item_next(s)) { + if((s->flags & TCP_SOCKET_FLAGS_LISTENING) != 0 && + s->listen_port != 0 && + s->listen_port == uip_htons(uip_conn->lport)) { + s->flags &= ~TCP_SOCKET_FLAGS_LISTENING; + tcp_markconn(uip_conn, s); + call_event(s, TCP_SOCKET_CONNECTED); + break; + } + } + } else { + call_event(s, TCP_SOCKET_CONNECTED); + } + + if(s == NULL) { + uip_abort(); + } else { + if(uip_newdata()) { + newdata(s); + } + senddata(s); + } + return; + } + + if(uip_timedout()) { + call_event(s, TCP_SOCKET_TIMEDOUT); + relisten(s); + } + + if(uip_aborted()) { + call_event(s, TCP_SOCKET_ABORTED); + relisten(s); + } + + if(s == NULL) { + uip_abort(); + return; + } + + if(uip_acked()) { + acked(s); + } + if(uip_newdata()) { + newdata(s); + } + + if(uip_rexmit() || + uip_newdata() || + uip_acked()) { + senddata(s); + } else if(uip_poll()) { + senddata(s); + } + + if(s->output_data_len == 0 && s->flags & TCP_SOCKET_FLAGS_CLOSING) { + s->flags &= ~TCP_SOCKET_FLAGS_CLOSING; + uip_close(); + tcp_markconn(uip_conn, NULL); + call_event(s, TCP_SOCKET_CLOSED); + relisten(s); + } + + if(uip_closed()) { + tcp_markconn(uip_conn, NULL); + call_event(s, TCP_SOCKET_CLOSED); + relisten(s); + } +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(tcp_socket_process, ev, data) +{ + PROCESS_BEGIN(); + while(1) { + PROCESS_WAIT_EVENT(); + + if(ev == tcpip_event) { + appcall(data); + } + } + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +static void +init(void) +{ + static uint8_t inited = 0; + if(!inited) { + list_init(socketlist); + process_start(&tcp_socket_process, NULL); + inited = 1; + } +} +/*---------------------------------------------------------------------------*/ +int +tcp_socket_register(struct tcp_socket *s, void *ptr, + uint8_t *input_databuf, int input_databuf_len, + uint8_t *output_databuf, int output_databuf_len, + tcp_socket_data_callback_t input_callback, + tcp_socket_event_callback_t event_callback) +{ + + init(); + + if(s == NULL) { + return -1; + } + s->ptr = ptr; + s->input_data_ptr = input_databuf; + s->input_data_maxlen = input_databuf_len; + s->output_data_ptr = output_databuf; + s->output_data_maxlen = output_databuf_len; + s->input_callback = input_callback; + s->event_callback = event_callback; + list_add(socketlist, s); + + s->listen_port = 0; + s->flags = TCP_SOCKET_FLAGS_NONE; + return 1; +} +/*---------------------------------------------------------------------------*/ +int +tcp_socket_connect(struct tcp_socket *s, + uip_ipaddr_t *ipaddr, + uint16_t port) +{ + if(s == NULL) { + return -1; + } + PROCESS_CONTEXT_BEGIN(&tcp_socket_process); + s->c = tcp_connect(ipaddr, uip_htons(port), s); + PROCESS_CONTEXT_END(); + if(s->c == NULL) { + return -1; + } else { + return 1; + } +} +/*---------------------------------------------------------------------------*/ +int +tcp_socket_listen(struct tcp_socket *s, + uint16_t port) +{ + if(s == NULL) { + return -1; + } + + s->listen_port = port; + PROCESS_CONTEXT_BEGIN(&tcp_socket_process); + tcp_listen(uip_htons(port)); + PROCESS_CONTEXT_END(); + s->flags |= TCP_SOCKET_FLAGS_LISTENING; + return 1; +} +/*---------------------------------------------------------------------------*/ +int +tcp_socket_unlisten(struct tcp_socket *s) +{ + if(s == NULL) { + return -1; + } + + PROCESS_CONTEXT_BEGIN(&tcp_socket_process); + tcp_unlisten(uip_htons(s->listen_port)); + PROCESS_CONTEXT_END(); + s->listen_port = 0; + s->flags &= ~TCP_SOCKET_FLAGS_LISTENING; + return 1; +} +/*---------------------------------------------------------------------------*/ +int +tcp_socket_send(struct tcp_socket *s, + const uint8_t *data, int datalen) +{ + int len; + + if(s == NULL) { + return -1; + } + + len = MIN(datalen, s->output_data_maxlen - s->output_data_len); + + memcpy(&s->output_data_ptr[s->output_data_len], data, len); + s->output_data_len += len; + return len; +} +/*---------------------------------------------------------------------------*/ +int +tcp_socket_send_str(struct tcp_socket *s, + const char *str) +{ + return tcp_socket_send(s, (const uint8_t *)str, strlen(str)); +} +/*---------------------------------------------------------------------------*/ +int +tcp_socket_close(struct tcp_socket *s) +{ + if(s == NULL) { + return -1; + } + + s->flags |= TCP_SOCKET_FLAGS_CLOSING; + return 1; +} +/*---------------------------------------------------------------------------*/ +int +tcp_socket_unregister(struct tcp_socket *s) +{ + if(s == NULL) { + return -1; + } + + tcp_socket_unlisten(s); + if(s->c != NULL) { + tcp_attach(s->c, NULL); + } + list_remove(socketlist, s); + return 1; +} +/*---------------------------------------------------------------------------*/ diff --git a/core/net/ip/tcp-socket.h b/core/net/ip/tcp-socket.h new file mode 100644 index 000000000..aea678049 --- /dev/null +++ b/core/net/ip/tcp-socket.h @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2012-2014, Thingsquare, http://www.thingsquare.com/. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TCP_SOCKET_H +#define TCP_SOCKET_H + +struct tcp_socket; + +typedef enum { + TCP_SOCKET_CONNECTED, + TCP_SOCKET_CLOSED, + TCP_SOCKET_TIMEDOUT, + TCP_SOCKET_ABORTED, + TCP_SOCKET_DATA_SENT +} tcp_socket_event_t; + +/** + * \brief TCP data callback function + * \param s A pointer to a TCP socket + * \param ptr A user-defined pointer + * \param input_data_ptr A pointer to the incoming data + * \param input_data_len The length of the incoming data + * \return The function should return the number of bytes to leave in the input buffer + * + * The TCP socket input callback function gets + * called whenever there is new data on the socket. The + * function can choose to either consume the data + * directly, or leave it in the buffer for later. The + * function must return the amount of data to leave in the + * buffer. I.e., if the callback function consumes all + * incoming data, it should return 0. + */ +typedef int (* tcp_socket_data_callback_t)(struct tcp_socket *s, + void *ptr, + const uint8_t *input_data_ptr, + int input_data_len); + + +/** + * \brief TCP event callback function + * \param s A pointer to a TCP socket + * \param ptr A user-defined pointer + * \param event The event number + * + * The TCP socket event callback function gets + * called whenever there is an event on a socket, such as + * the socket getting connected or closed. + */ +typedef void (* tcp_socket_event_callback_t)(struct tcp_socket *s, + void *ptr, + tcp_socket_event_t event); + +struct tcp_socket { + struct tcp_socket *next; + + tcp_socket_data_callback_t input_callback; + tcp_socket_event_callback_t event_callback; + void *ptr; + + struct process *p; + + uint8_t *input_data_ptr; + uint8_t *output_data_ptr; + + uint16_t input_data_maxlen; + uint16_t input_data_len; + uint16_t output_data_maxlen; + uint16_t output_data_len; + uint16_t output_data_send_nxt; + + uint8_t flags; + uint16_t listen_port; + struct uip_conn *c; +}; + +enum { + TCP_SOCKET_FLAGS_NONE = 0x00, + TCP_SOCKET_FLAGS_LISTENING = 0x01, + TCP_SOCKET_FLAGS_CLOSING = 0x02, +}; + +/** + * \brief Register a TCP socket + * \param s A pointer to a TCP socket + * \param ptr A user-defined pointer that will be sent to callbacks for this socket + * \param input_databuf A pointer to a memory area this socket will use for input data + * \param input_databuf_len The size of the input data buffer + * \param output_databuf A pointer to a memory area this socket will use for outgoing data + * \param output_databuf_len The size of the output data buffer + * \param data_callback A pointer to the data callback function for this socket + * \param event_callback A pointer to the event callback function for this socket + * \retval -1 If an error occurs + * \retval 1 If the operation succeeds. + * + * This function registers a TCP socket. The function sets + * up the output and input buffers for the socket and + * callback pointers. + * + * TCP sockets use input and output buffers for incoming + * and outgoing data. The memory for these buffers must be + * allocated by the caller. The size of the buffers + * determine the amount of data that can be received and + * sent, and the principle is that the application that + * sets up the TCP socket will know roughly how large + * these buffers should be. The rule of thumb is that the + * input buffer should be large enough to hold the largest + * application layer message that the application will + * receive and the output buffer should be large enough to + * hold the largest application layer message the + * application will send. + * + * TCP throttles incoming data so that if the input buffer + * is filled, the connection will halt until the + * application has read out the data from the input + * buffer. + * + */ +int tcp_socket_register(struct tcp_socket *s, void *ptr, + uint8_t *input_databuf, int input_databuf_len, + uint8_t *output_databuf, int output_databuf_len, + tcp_socket_data_callback_t data_callback, + tcp_socket_event_callback_t event_callback); + +/** + * \brief Connect a TCP socket to a remote host + * \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register() + * \param ipaddr The IP address of the remote host + * \param port The TCP port number, in host byte order, of the remote host + * \retval -1 If an error occurs + * \retval 1 If the operation succeeds. + * + * This function connects a TCP socket to a remote host. + * + * When the socket has connected, the event callback will + * get called with the TCP_SOCKET_CONNECTED event. If the + * remote host does not accept the connection, the + * TCP_SOCKET_ABORTED will be sent to the callback. If the + * connection times out before conecting to the remote + * host, the TCP_SOCKET_TIMEDOUT event is sent to the + * callback. + * + */ +int tcp_socket_connect(struct tcp_socket *s, + uip_ipaddr_t *ipaddr, + uint16_t port); + +/** + * \brief Start listening on a specific port + * \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register() + * \param port The TCP port number, in host byte order, of the remote host + * \retval -1 If an error occurs + * \retval 1 If the operation succeeds. + * + * This function causes the TCP socket to start listening + * on the given TCP port. + * + * Several sockets can listen on the same port. If a + * remote host connects to the port, one of the listening + * sockets will get connected and the event callback will + * be called with the TCP_SOCKET_CONNECTED event. When the + * connection closes, the socket will go back to listening + * for new connections. + * + */ +int tcp_socket_listen(struct tcp_socket *s, + uint16_t port); + +/** + * \brief Stop listening for new connections + * \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register() + * \retval -1 If an error occurs + * \retval 1 If the operation succeeds. + * + * This function causes a listening TCP socket to stop + * listen. The socket must previously been put into listen + * mode with tcp_socket_listen(). + * + */ +int tcp_socket_unlisten(struct tcp_socket *s); + +/** + * \brief Send data on a connected TCP socket + * \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register() + * \param dataptr A pointer to the data to be sent + * \param datalen The length of the data to be sent + * \retval -1 If an error occurs + * \return The number of bytes that were successfully sent + * + * This function sends data over a connected TCP + * socket. The data is placed in the output buffer and + * sent to the remote host as soon as possiblce. When the + * data has been acknowledged by the remote host, the + * event callback is sent with the TCP_SOCKET_DATA_SENT + * event. + */ +int tcp_socket_send(struct tcp_socket *s, + const uint8_t *dataptr, + int datalen); + +/** + * \brief Send a string on a connected TCP socket + * \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register() + * \param strptr A pointer to the string to be sent + * \retval -1 If an error occurs + * \return The number of bytes that were successfully sent + * + * This is a convenience function for sending strings on a + * TCP socket. The function calls tcp_socket_send() to + * send the string. + */ +int tcp_socket_send_str(struct tcp_socket *s, + const char *strptr); + +/** + * \brief Close a connected TCP socket + * \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register() + * \retval -1 If an error occurs + * \retval 1 If the operation succeeds. + * + * This function closes a connected TCP socket. When the + * socket has been successfully closed, the event callback + * is called with the TCP_SOCKET_CLOSED event. + * + */ +int tcp_socket_close(struct tcp_socket *s); + +/** + * \brief Unregister a registered socket + * \param s A pointer to a TCP socket that must have been previously registered with tcp_socket_register() + * \retval -1 If an error occurs + * \retval 1 If the operation succeeds. + * + * This function unregisters a previously registered + * socket. This must be done if the process will be + * unloaded from memory. If the TCP socket is connected, + * the connection will be reset. + * + */ +int tcp_socket_unregister(struct tcp_socket *s); +#endif /* TCP_SOCKET_H */ From ca5652d98106f0eea770e60b7057aba7fb2478cf Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Mon, 24 Mar 2014 08:45:41 +0100 Subject: [PATCH 3/5] TCP socket server example --- examples/tcp-socket/Makefile | 5 ++ examples/tcp-socket/tcp-server.c | 119 +++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 examples/tcp-socket/Makefile create mode 100644 examples/tcp-socket/tcp-server.c diff --git a/examples/tcp-socket/Makefile b/examples/tcp-socket/Makefile new file mode 100644 index 000000000..544963f05 --- /dev/null +++ b/examples/tcp-socket/Makefile @@ -0,0 +1,5 @@ +all: tcp-server + +CONTIKI=../.. + +include $(CONTIKI)/Makefile.include diff --git a/examples/tcp-socket/tcp-server.c b/examples/tcp-socket/tcp-server.c new file mode 100644 index 000000000..bb5650514 --- /dev/null +++ b/examples/tcp-socket/tcp-server.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2012, Thingsquare, http://www.thingsquare.com/. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "contiki.h" + +#include +#include +#include + +#define SERVER_PORT 80 + +static struct tcp_socket socket; + +#define INPUTBUFSIZE 400 +static uint8_t inputbuf[INPUTBUFSIZE]; + +#define OUTPUTBUFSIZE 400 +static uint8_t outputbuf[OUTPUTBUFSIZE]; + +PROCESS(tcp_server_process, "TCP echo process"); +AUTOSTART_PROCESSES(&tcp_server_process); +static uint8_t get_received; +static int bytes_to_send; +/*---------------------------------------------------------------------------*/ +static int +input(struct tcp_socket *s, void *ptr, + const uint8_t *inputptr, int inputdatalen) +{ + printf("input %d bytes '%s'\n", inputdatalen, inputptr); + if(!get_received) { + /* See if we have a full GET request in the buffer. */ + if(strncmp((char *)inputptr, "GET /", 5) == 0 && + atoi((char *)&inputptr[5]) != 0) { + bytes_to_send = atoi((char *)&inputptr[5]); + printf("bytes_to_send %d\n", bytes_to_send); + return 0; + } + printf("inputptr '%.*s'\n", inputdatalen, inputptr); + /* Return the number of data bytes we received, to keep them all + in the buffer. */ + return inputdatalen; + } else { + /* Discard everything */ + return 0; /* all data consumed */ + } +} +/*---------------------------------------------------------------------------*/ +static void +event(struct tcp_socket *s, void *ptr, + tcp_socket_event_t ev) +{ + printf("event %d\n", ev); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(tcp_server_process, ev, data) +{ + PROCESS_BEGIN(); + + tcp_socket_register(&socket, NULL, + inputbuf, sizeof(inputbuf), + outputbuf, sizeof(outputbuf), + input, event); + tcp_socket_listen(&socket, SERVER_PORT); + + printf("Listening on %d\n", SERVER_PORT); + while(1) { + PROCESS_PAUSE(); + + if(bytes_to_send > 0) { + /* Send header */ + printf("sending header\n"); + tcp_socket_send_str(&socket, "HTTP/1.0 200 ok\r\nServer: Contiki tcp-socket example\r\n\r\n"); + + /* Send data */ + printf("sending data\n"); + while(bytes_to_send > 0) { + PROCESS_PAUSE(); + int len, tosend; +#define MIN(a,b) ((a)<(b)?(a):(b)) + tosend = MIN(bytes_to_send, sizeof(outputbuf)); + len = tcp_socket_send(&socket, "", tosend); + bytes_to_send -= len; + } + + tcp_socket_close(&socket); + } + + } + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ From 367eed96f4d393d51b144b807af93d0f797fe12e Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Wed, 9 Apr 2014 21:56:29 +0200 Subject: [PATCH 4/5] Add a compile test for the tcp-socket example --- examples/tcp-socket/tcp-server.c | 4 ++-- regression-tests/01-compile-base/Makefile | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/tcp-socket/tcp-server.c b/examples/tcp-socket/tcp-server.c index bb5650514..f2e776784 100644 --- a/examples/tcp-socket/tcp-server.c +++ b/examples/tcp-socket/tcp-server.c @@ -29,7 +29,7 @@ * */ -#include "contiki.h" +#include "contiki-net.h" #include #include @@ -106,7 +106,7 @@ PROCESS_THREAD(tcp_server_process, ev, data) int len, tosend; #define MIN(a,b) ((a)<(b)?(a):(b)) tosend = MIN(bytes_to_send, sizeof(outputbuf)); - len = tcp_socket_send(&socket, "", tosend); + len = tcp_socket_send(&socket, (uint8_t *)"", tosend); bytes_to_send -= len; } diff --git a/regression-tests/01-compile-base/Makefile b/regression-tests/01-compile-base/Makefile index 1252097b5..57fda92b0 100644 --- a/regression-tests/01-compile-base/Makefile +++ b/regression-tests/01-compile-base/Makefile @@ -27,6 +27,7 @@ sky-ip/sky \ sky-shell/sky \ sky-shell-exec/sky \ sky-shell-webserver/sky \ +tcp-socket/minimal-net \ telnet-server/minimal-net \ webserver/minimal-net \ webserver-ipv6/exp5438 \ From 1075effa22c4e73ee1dde46374b4f948e4bc1659 Mon Sep 17 00:00:00 2001 From: Adam Dunkels Date: Wed, 9 Apr 2014 22:00:47 +0200 Subject: [PATCH 5/5] Include udp-socket.h and tcp-socket.h by default in all applications that include contiki-net.h --- core/contiki-net.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/contiki-net.h b/core/contiki-net.h index 453b5a468..d783b35b2 100644 --- a/core/contiki-net.h +++ b/core/contiki-net.h @@ -54,6 +54,9 @@ #include "net/ip/psock.h" +#include "net/ip/udp-socket.h" +#include "net/ip/tcp-socket.h" + #include "net/rime/rime.h" #include "net/netstack.h"