diff --git a/pkg/emb6/Makefile.dep b/pkg/emb6/Makefile.dep index 652cc1063f2b08891cef735e4563ea75a2969889..9d1c709d44e205d4e8d39f1d4458d02fd723e1ee 100644 --- a/pkg/emb6/Makefile.dep +++ b/pkg/emb6/Makefile.dep @@ -2,6 +2,11 @@ ifneq (,$(filter emb6_conn_udp,$(USEMODULE))) USEMODULE += emb6_sock endif +ifneq (,$(filter emb6_sock_%,$(USEMODULE))) + USEMODULE += core_mbox + USEMODULE += emb6_sock +endif + ifneq (,$(filter emb6_%,$(USEMODULE))) USEMODULE += emb6 endif @@ -19,4 +24,5 @@ ifneq (,$(filter emb6,$(USEMODULE))) USEMODULE += emb6_rpl USEMODULE += emb6_sicslowpan USEMODULE += emb6_utils + USEMODULE += xtimer endif diff --git a/pkg/emb6/Makefile.include b/pkg/emb6/Makefile.include index 5b15adedeb47ac54025e285b61a13e4742da3b5c..34308ae713287b28d43203b0beb1190200e7fe7c 100644 --- a/pkg/emb6/Makefile.include +++ b/pkg/emb6/Makefile.include @@ -65,6 +65,11 @@ ifneq (,$(filter emb6_sock,$(USEMODULE))) INCLUDES += -I$(EMB6_DIR)/emb6/inc/tport endif +ifneq (,$(filter emb6_sock_udp,$(USEMODULE))) + DIRS += $(EMB6_CONTRIB)/sock/udp + CFLAGS += -DSOCK_HAS_IPV6 +endif + ifneq (,$(filter emb6_utils,$(USEMODULE))) DIRS += $(EMB6_DIR)/utils/src INCLUDES += -I$(EMB6_DIR)/utils/inc diff --git a/pkg/emb6/contrib/sock/udp/Makefile b/pkg/emb6/contrib/sock/udp/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d38d86d7ac97cc4d6a4757f454ef83eff38c3d0b --- /dev/null +++ b/pkg/emb6/contrib/sock/udp/Makefile @@ -0,0 +1,3 @@ +MODULE := emb6_sock_udp + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/emb6/contrib/sock/udp/emb6_sock_udp.c b/pkg/emb6/contrib/sock/udp/emb6_sock_udp.c new file mode 100644 index 0000000000000000000000000000000000000000..e981444c1827e9a402913be5a31d4c35fc875f54 --- /dev/null +++ b/pkg/emb6/contrib/sock/udp/emb6_sock_udp.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2016 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders <m.lenders@fu-berlin.de> + */ + +#include <assert.h> +#include <errno.h> +#include <stdbool.h> + +#include "byteorder.h" +#include "evproc.h" +#include "msg.h" +#include "mutex.h" +#include "net/af.h" +#include "net/sock/udp.h" +#include "net/ipv6/hdr.h" +#include "sched.h" +#include "uip.h" +#include "xtimer.h" + +#define _MSG_TYPE_CLOSE (0x4123) +#define _MSG_TYPE_TIMEOUT (0x4124) +#define _MSG_TYPE_RCV (0x4125) + +/* struct to describe a sendto command for emb6 thread */ +typedef struct { + mutex_t block; + struct udp_socket *sock; + const sock_udp_ep_t *remote; + int res; + const void *data; + size_t len; +} _send_cmd_t; + +extern uint16_t uip_slen; + +static bool send_registered = false; + +static void _timeout_callback(void *arg); +static void _input_callback(struct udp_socket *c, void *ptr, + const uip_ipaddr_t *src_addr, uint16_t src_port, + const uip_ipaddr_t *dst_addr, uint16_t dst_port, + const uint8_t *data, uint16_t datalen); +static void _output_callback(c_event_t c_event, p_data_t p_data); + +static int _reg(struct udp_socket *c, void *ptr, udp_socket_input_callback_t cb, + const sock_udp_ep_t *local, const sock_udp_ep_t *remote) +{ + if (((local != NULL) && (local->family != AF_INET6)) || + ((remote != NULL) && (remote->family != AF_INET6))) { + return -EAFNOSUPPORT; + } + if (udp_socket_register(c, ptr, cb) < 0) { + return -ENOMEM; + } + if (local != NULL) { + if (udp_socket_bind(c, local->port) < 0) { + udp_socket_close(c); + return -EADDRINUSE; + } + } + if (remote != NULL) { + /* check of return value not necessary, since neither c nor + * c->udp_conn is NULL (only error case) at this point */ + udp_socket_connect(c, (uip_ipaddr_t *)&remote->addr, remote->port); + } + return 0; +} + +int sock_udp_create(sock_udp_t *sock, const sock_udp_ep_t *local, + const sock_udp_ep_t *remote, uint16_t flags) +{ + int res; + + (void)flags; + assert((sock != NULL)); + assert((local == NULL) || (local->port != 0)); + assert((remote == NULL) || (remote->port != 0)); + if (sock->sock.input_callback != NULL) { + sock_udp_close(sock); + } + mutex_init(&sock->mutex); + mutex_lock(&sock->mutex); + mbox_init(&sock->mbox, sock->mbox_queue, SOCK_MBOX_SIZE); + atomic_flag_clear(&sock->receivers); + if ((res = _reg(&sock->sock, sock, _input_callback, local, remote)) < 0) { + sock->sock.input_callback = NULL; + } + mutex_unlock(&sock->mutex); + return res; +} + +void sock_udp_close(sock_udp_t *sock) +{ + assert(sock != NULL); + if (sock->sock.input_callback != NULL) { + while (atomic_fetch_sub(&sock->receivers, 1) > 0) { + msg_t msg = { .type = _MSG_TYPE_CLOSE }; + mbox_put(&sock->mbox, &msg); + } + mutex_lock(&sock->mutex); + udp_socket_close(&sock->sock); + sock->sock.input_callback = NULL; + mutex_unlock(&sock->mutex); + } +} + +int sock_udp_get_local(sock_udp_t *sock, sock_udp_ep_t *ep) +{ + assert((sock != NULL) && (ep != NULL)); + if ((sock->sock.input_callback != NULL) && + (sock->sock.udp_conn->lport != 0)) { + mutex_lock(&sock->mutex); + /* local UDP endpoints do not have addresses in emb6 */ + memset(&ep->addr, 0, sizeof(ipv6_addr_t)); + ep->port = ntohs(sock->sock.udp_conn->lport); + mutex_unlock(&sock->mutex); + return sizeof(ipv6_addr_t); + } + return -EADDRNOTAVAIL; +} + +int sock_udp_get_remote(sock_udp_t *sock, sock_udp_ep_t *ep) +{ + assert((sock != NULL) && (ep != NULL)); + if ((sock->sock.input_callback != NULL) && + (sock->sock.udp_conn->rport != 0)) { + mutex_lock(&sock->mutex); + memcpy(&ep->addr, &sock->sock.udp_conn->ripaddr, sizeof(ipv6_addr_t)); + ep->port = ntohs(sock->sock.udp_conn->rport); + mutex_unlock(&sock->mutex); + return sizeof(ipv6_addr_t); + } + return -ENOTCONN; +} + +int sock_udp_recv(sock_udp_t *sock, void *data, size_t max_len, + uint32_t timeout, sock_udp_ep_t *remote) +{ + xtimer_t timeout_timer; + int blocking = BLOCKING; + int res = -EIO; + msg_t msg; + + assert((sock != NULL) && (data != NULL) && (max_len > 0)); + if (sock->sock.input_callback == NULL) { + return -EADDRNOTAVAIL; + } + if (timeout == 0) { + blocking = NON_BLOCKING; + } + else if (timeout != SOCK_NO_TIMEOUT) { + timeout_timer.callback = _timeout_callback; + timeout_timer.arg = &sock->mbox; + xtimer_set(&timeout_timer, timeout); + } + atomic_fetch_add(&sock->receivers, 1); + if (_mbox_get(&sock->mbox, &msg, blocking) == 0) { + /* do not need to remove xtimer, since we only get here in non-blocking + * mode (timeout > 0) */ + return -EAGAIN; + } + switch (msg.type) { + case _MSG_TYPE_CLOSE: + res = -EADDRNOTAVAIL; + break; + case _MSG_TYPE_TIMEOUT: + res = -ETIMEDOUT; + break; + case _MSG_TYPE_RCV: + mutex_lock(&sock->mutex); + if (max_len < sock->recv_info.datalen) { + res = -ENOBUFS; + mutex_unlock(&sock->mutex); + break; + } + memcpy(data, sock->recv_info.data, sock->recv_info.datalen); + if (remote != NULL) { + remote->family = AF_INET6; + remote->netif = SOCK_ADDR_ANY_NETIF; + memcpy(&remote->addr, &sock->recv_info.src, sizeof(ipv6_addr_t)); + remote->port = sock->recv_info.src_port; + } + res = (int)sock->recv_info.datalen; + mutex_unlock(&sock->mutex); + break; + } + atomic_fetch_sub(&sock->receivers, 1); + return res; +} + +int sock_udp_send(sock_udp_t *sock, const void *data, size_t len, + const sock_udp_ep_t *remote) +{ + struct udp_socket tmp; + _send_cmd_t send_cmd = { .block = MUTEX_INIT, + .remote = remote, + .data = data, + .len = len }; + + assert((sock != NULL) || (remote != NULL)); + assert((len == 0) || (data != NULL)); /* (len != 0) => (data != NULL) */ + /* we want the send in the uip thread (which udp_socket_send does not offer) + * so we need to do it manually */ + if (!send_registered) { + if (evproc_regCallback(EVENT_TYPE_CONN_SEND, _output_callback) != E_SUCCESS) { + return -ENOMEM; + } + else { + send_registered = true; + } + } + if ((len > (UIP_BUFSIZE - (UIP_LLH_LEN + UIP_IPUDPH_LEN))) || + (len > UINT16_MAX)) { + return -ENOMEM; + } + if (remote != NULL) { + if (remote->family != AF_INET6) { + return -EAFNOSUPPORT; + } + if (remote->port == 0) { + return -EINVAL; + } + send_cmd.remote = remote; + } + else if (sock->sock.udp_conn->rport == 0) { + return -ENOTCONN; + } + /* cppcheck-supress nullPointerRedundantCheck + * remote == NULL implies that sock != NULL (see assert at start of + * function) * that's why it is okay in the if-statement above to check + * sock->... without checking (sock != NULL) first => this check afterwards + * isn't redundant */ + if (sock == NULL) { + int res; + if ((res = _reg(&tmp, NULL, NULL, NULL, NULL)) < 0) { + return res; + } + send_cmd.sock = &tmp; + } + else { + send_cmd.sock = &sock->sock; + } + mutex_lock(&send_cmd.block); + /* change to emb6 thread context */ + if (evproc_putEvent(E_EVPROC_TAIL, EVENT_TYPE_CONN_SEND, &send_cmd) == E_SUCCESS) { + /* block thread until data was sent */ + mutex_lock(&send_cmd.block); + } + else { + /* most likely error: event queue was full */ + send_cmd.res = -ENOMEM; + } + if (send_cmd.sock == &tmp) { + udp_socket_close(&tmp); + } + mutex_unlock(&send_cmd.block); + + return send_cmd.res; +} + +static void _timeout_callback(void *arg) +{ + msg_t msg = { .type = _MSG_TYPE_TIMEOUT }; + mbox_t *mbox = arg; + + /* should be safe, because otherwise if mbox were filled this callback is + * senseless */ + mbox_try_put(mbox, &msg); +} + +static void _input_callback(struct udp_socket *c, void *ptr, + const uip_ipaddr_t *src_addr, uint16_t src_port, + const uip_ipaddr_t *dst_addr, uint16_t dst_port, + const uint8_t *data, uint16_t datalen) +{ + msg_t msg = { .type = _MSG_TYPE_RCV }; + sock_udp_t *sock = ptr; + + (void)dst_addr; + (void)dst_port; + mutex_lock(&sock->mutex); + sock->recv_info.src_port = src_port; + sock->recv_info.src = (const ipv6_addr_t *)src_addr; + sock->recv_info.data = data; + sock->recv_info.datalen = datalen - sizeof(ipv6_hdr_t); + mutex_unlock(&sock->mutex); + mbox_put(&sock->mbox, &msg); +} + +static void _output_callback(c_event_t c_event, p_data_t p_data) +{ + + if ((c_event != EVENT_TYPE_CONN_SEND) || (p_data == NULL)) { + return; + } + + _send_cmd_t *send_cmd = (_send_cmd_t *)p_data; + + if (send_cmd->remote != NULL) { + /* send_cmd->len was previously checked */ + send_cmd->res = udp_socket_sendto(send_cmd->sock, send_cmd->data, + (uint16_t)send_cmd->len, + (uip_ipaddr_t *)&send_cmd->remote->addr, + send_cmd->remote->port); + } + else { + /* send_cmd->len was previously checked */ + send_cmd->res = udp_socket_send(send_cmd->sock, send_cmd->data, + (uint16_t)send_cmd->len); + } + send_cmd->res = (send_cmd->res < 0) ? -EHOSTUNREACH : send_cmd->res; + /* notify notify waiting thread */ + mutex_unlock(&send_cmd->block); +} +/** @} */ diff --git a/pkg/emb6/include/sock_types.h b/pkg/emb6/include/sock_types.h new file mode 100644 index 0000000000000000000000000000000000000000..6ce812b6c4409e25efe183cb6fb09ac897bf162c --- /dev/null +++ b/pkg/emb6/include/sock_types.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup pkg_emb6_sock emb6-specific implementation of the sock API + * @ingroup pkg_emb6 + * @brief + * @{ + * + * @file + * @brief + * + * @author Martine Lenders <mlenders@inf.fu-berlin.de> + */ +#ifndef SOCK_TYPES_H_ +#define SOCK_TYPES_H_ + +#include <stdatomic.h> + +#include "mbox.h" +#include "mutex.h" +#include "net/ipv6/addr.h" + +#include "uip.h" +#include "udp-socket.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SOCK_MBOX_SIZE +#define SOCK_MBOX_SIZE (2) +#endif + +#ifndef SOCK_HAS_IPV6 +#error "emb6 only runs with IPv6 support" +#endif + +/** + * @brief @ref net_sock_udp definition for emb6 + */ +struct sock_udp { + struct udp_socket sock; /**< emb6 internal socket */ + mutex_t mutex; /**< mutex for the connection */ + mbox_t mbox; /**< mbox for receiving */ + msg_t mbox_queue[SOCK_MBOX_SIZE]; /**< queue for mbox */ + atomic_int receivers; /**< current number of recv calls */ + struct { + const ipv6_addr_t *src; /**< source address */ + const void *data; /**< data of received packet */ + size_t datalen; /**< length of received packet data */ + uint16_t src_port; /**< source port */ + } recv_info; /**< info on received packet */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SOCK_TYPES_H_ */ +/** @} */ diff --git a/tests/emb6/Makefile b/tests/emb6/Makefile index 18e3052f06284ed3e205d854e474cfad8a7f14aa..8f1632ed06bac30015ee16818de28a854570deb0 100644 --- a/tests/emb6/Makefile +++ b/tests/emb6/Makefile @@ -5,6 +5,9 @@ include ../Makefile.tests_common FEATURES_REQUIRED = periph_gpio periph_spi # for at86rf231 +# MSP-430 doesn't support C11's atomic functionality yet +BOARD_BLACKLIST := msb-430 msb-430h telosb wsn430-v1_3b wsn430-v1_4 z1 + BOARD_INSUFFICIENT_MEMORY := msb-430 msb-430h nucleo32-l031 nucleo32-f031 \ nucleo32-f042 nucleo-l053 stm32f0discovery telosb \ weio wsn430-v1_3b wsn430-v1_4 z1 @@ -12,7 +15,7 @@ BOARD_INSUFFICIENT_MEMORY := msb-430 msb-430h nucleo32-l031 nucleo32-f031 \ USEPKG += emb6 USEMODULE += emb6_router -USEMODULE += emb6_conn_udp +USEMODULE += emb6_sock_udp USEMODULE += ipv6_addr USEMODULE += shell USEMODULE += shell_commands diff --git a/tests/emb6/common.h b/tests/emb6/common.h index 0e8a419659b16b46654aed6f376f9a15a9b97969..8e873cd17a33d8acae8ace8d45f5cb38f39a5324 100644 --- a/tests/emb6/common.h +++ b/tests/emb6/common.h @@ -11,7 +11,7 @@ * @{ * * @file - * @brief Definitions for tests/lwip/ + * @brief Definitions for tests/emb6/ * * @author Martine Lenders <mlenders@inf.fu-berlin.de> */ @@ -29,7 +29,7 @@ extern "C" { * @brief Application configuration * @{ */ -#define CONN_INBUF_SIZE (256) +#define SOCK_INBUF_SIZE (256) #define SERVER_MSG_QUEUE_SIZE (8) #define SERVER_BUFFER_SIZE (64) /** @@ -58,7 +58,7 @@ size_t hex2ints(uint8_t *out, const char *in); */ int ping_cmd(int argc, char **argv); -#ifdef MODULE_CONN_UDP +#ifdef MODULE_EMB6_SOCK_UDP /** * @brief UDP IP shell command * diff --git a/tests/emb6/main.c b/tests/emb6/main.c index 9f833acc5ad3652677b3a81d7bda35bc48ece424..06452f550cf8c176f893bed88897ff0e3250dca5 100644 --- a/tests/emb6/main.c +++ b/tests/emb6/main.c @@ -78,15 +78,14 @@ static void *_emb6_thread(void *args) static const shell_command_t shell_commands[] = { { "ping6", "Send pings and receive pongs", ping_cmd }, -#ifdef MODULE_CONN_UDP +#ifdef MODULE_EMB6_SOCK_UDP { "udp", "Send UDP messages and listen for messages on UDP port", udp_cmd }, #endif { "ifconfig", "Shows assigned IPv6 addresses", ifconfig }, { NULL, NULL, NULL } }; -static char line_buf[SHELL_DEFAULT_BUFSIZE]; -char conn_inbuf[CONN_INBUF_SIZE]; +static char line_buf[SHELL_DEFAULT_BUFSIZE]; int main(void) { diff --git a/tests/emb6/udp.c b/tests/emb6/udp.c index 748110cc6eef71389d8f387fdc75283d10ec66e2..f1ba76aa0fdabac7b6264923c6e15f6e1b15af01 100644 --- a/tests/emb6/udp.c +++ b/tests/emb6/udp.c @@ -25,29 +25,26 @@ #include "common.h" #include "od.h" #include "net/af.h" -#include "net/conn/udp.h" +#include "net/sock/udp.h" #include "net/ipv6.h" #include "thread.h" #include "xtimer.h" -#ifdef MODULE_CONN_UDP -static char conn_inbuf[CONN_INBUF_SIZE]; +#ifdef MODULE_EMB6_SOCK_UDP +static char sock_inbuf[SOCK_INBUF_SIZE]; static bool server_running; -static conn_udp_t server_conn; +static sock_udp_t server_sock; static char server_stack[THREAD_STACKSIZE_DEFAULT]; -static msg_t server_msg_queue[SERVER_MSG_QUEUE_SIZE]; static void *_server_thread(void *args) { - ipv6_addr_t server_addr = IPV6_ADDR_UNSPECIFIED; - uint16_t port; int res; - msg_init_queue(server_msg_queue, SERVER_MSG_QUEUE_SIZE); /* parse port */ - port = (uint16_t)atoi((char *)args); - if ((res = conn_udp_create(&server_conn, &server_addr, - sizeof(server_addr), AF_INET6, port)) < 0) { + uint16_t port = atoi(args); + const sock_udp_ep_t server_addr = { .family = AF_INET6, + .port = port }; + if ((res = sock_udp_create(&server_sock, &server_addr, NULL, 0)) < 0) { printf("Unable to open UDP server on port %" PRIu16 " (error code %d)\n", port, -res); return NULL; @@ -55,12 +52,10 @@ static void *_server_thread(void *args) server_running = true; printf("Success: started UDP server on port %" PRIu16 "\n", port); while (1) { - int res; - ipv6_addr_t src; - size_t src_len = sizeof(ipv6_addr_t); - uint16_t sport; - if ((res = conn_udp_recvfrom(&server_conn, conn_inbuf, sizeof(conn_inbuf), &src, - &src_len, &sport)) < 0) { + sock_udp_ep_t client_addr; + + if ((res = sock_udp_recv(&server_sock, sock_inbuf, sizeof(sock_inbuf), + SOCK_NO_TIMEOUT, &client_addr)) < 0) { puts("Error on receive"); } else if (res == 0) { @@ -68,9 +63,10 @@ static void *_server_thread(void *args) } else { char addrstr[IPV6_ADDR_MAX_STR_LEN]; - printf("Received from [%s]:%" PRIu16 ":\n", ipv6_addr_to_str(addrstr, &src, - sizeof(addrstr)), sport); - od_hex_dump(conn_inbuf, res, 0); + printf("Received from [%s]:%" PRIu16 ":\n", + ipv6_addr_to_str(addrstr, (ipv6_addr_t *)&client_addr.addr, + sizeof(addrstr)), client_addr.port); + od_hex_dump(sock_inbuf, res, 0); } } return NULL; @@ -79,27 +75,25 @@ static void *_server_thread(void *args) static int udp_send(char *addr_str, char *port_str, char *data, unsigned int num, unsigned int delay) { - ipv6_addr_t src = IPV6_ADDR_UNSPECIFIED, dst; - uint16_t port; uint8_t byte_data[strlen(data) / 2]; + sock_udp_ep_t dst = { .family = AF_INET6 }; size_t data_len; /* parse destination address */ - if (ipv6_addr_from_str(&dst, addr_str) == NULL) { + if (ipv6_addr_from_str((ipv6_addr_t *)&dst.addr, addr_str) == NULL) { puts("Error: unable to parse destination address"); return 1; } /* parse port */ - port = (uint16_t)atoi(port_str); + dst.port = atoi(port_str); data_len = hex2ints(byte_data, data); for (unsigned int i = 0; i < num; i++) { - if (conn_udp_sendto(byte_data, data_len, &src, sizeof(src), (struct sockaddr *)&dst, - sizeof(dst), AF_INET6, port, port) < 0) { + if (sock_udp_send(NULL, byte_data, data_len, &dst) < 0) { puts("could not send"); } else { printf("Success: send %u byte to [%s]:%" PRIu16 ")\n", - (unsigned)data_len, addr_str, port); + (unsigned)data_len, addr_str, dst.port); } xtimer_usleep(delay); }