From b713413c521d871246f04e9d820613984e7ac2f2 Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Thu, 31 Oct 2019 15:33:28 +0100 Subject: [PATCH 1/2] gnrc_sock: provide asynchronous event implementation --- Makefile.dep | 5 +++ makefiles/pseudomodules.inc.mk | 1 + sys/Makefile.include | 4 +++ sys/net/gnrc/sock/gnrc_sock.c | 27 ++++++++++++++++ sys/net/gnrc/sock/include/sock_types.h | 43 ++++++++++++++++++++++++-- sys/net/gnrc/sock/ip/gnrc_sock_ip.c | 19 ++++++++++++ sys/net/gnrc/sock/udp/gnrc_sock_udp.c | 19 ++++++++++++ 7 files changed, 116 insertions(+), 2 deletions(-) diff --git a/Makefile.dep b/Makefile.dep index c3301ba5a3..65e843ab63 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -105,6 +105,11 @@ ifneq (,$(filter gnrc_sock_%,$(USEMODULE))) USEMODULE += gnrc_sock endif +ifneq (,$(filter gnrc_sock_async,$(USEMODULE))) + USEMODULE += sock_async + USEMODULE += gnrc_netapi_callbacks +endif + ifneq (,$(filter gnrc_sock_ip,$(USEMODULE))) USEMODULE += sock_ip endif diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index b11af0f939..f8309ad820 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -38,6 +38,7 @@ PSEUDOMODULES += gnrc_sixlowpan_iphc_nhc PSEUDOMODULES += gnrc_sixlowpan_nd_border_router PSEUDOMODULES += gnrc_sixlowpan_router PSEUDOMODULES += gnrc_sixlowpan_router_default +PSEUDOMODULES += gnrc_sock_async PSEUDOMODULES += gnrc_sock_check_reuse PSEUDOMODULES += gnrc_txtsnd PSEUDOMODULES += i2c_scan diff --git a/sys/Makefile.include b/sys/Makefile.include index cde195f739..cb51be94cb 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -13,6 +13,10 @@ ifneq (,$(filter gnrc_sock,$(USEMODULE))) endif endif +ifneq (,$(filter gnrc_sock_async,$(USEMODULE))) + CFLAGS += -DSOCK_HAS_ASYNC +endif + ifneq (,$(filter posix_headers,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/sys/posix/include endif diff --git a/sys/net/gnrc/sock/gnrc_sock.c b/sys/net/gnrc/sock/gnrc_sock.c index 6f7bd9a2f2..62c7373433 100644 --- a/sys/net/gnrc/sock/gnrc_sock.c +++ b/sys/net/gnrc/sock/gnrc_sock.c @@ -15,6 +15,7 @@ #include +#include "log.h" #include "net/af.h" #include "net/ipv6/hdr.h" #include "net/gnrc/ipv6.h" @@ -44,10 +45,36 @@ static void _callback_put(void *arg) } #endif +#ifdef SOCK_HAS_ASYNC +static void _netapi_cb(uint16_t cmd, gnrc_pktsnip_t *pkt, void *ctx) +{ + if (cmd == GNRC_NETAPI_MSG_TYPE_RCV) { + msg_t msg = { .type = GNRC_NETAPI_MSG_TYPE_RCV, + .content = { .ptr = pkt } }; + gnrc_sock_reg_t *reg = ctx; + + if (mbox_try_put(®->mbox, &msg) < 1) { + LOG_WARNING("gnrc_sock: dropped message to %p (was full)\n", + (void *)®->mbox); + } + if (reg->async_cb.generic) { + reg->async_cb.generic(reg, SOCK_ASYNC_MSG_RECV); + } + } +} +#endif /* SOCK_HAS_ASYNC */ + void gnrc_sock_create(gnrc_sock_reg_t *reg, gnrc_nettype_t type, uint32_t demux_ctx) { mbox_init(®->mbox, reg->mbox_queue, SOCK_MBOX_SIZE); +#ifdef SOCK_HAS_ASYNC + reg->async_cb.generic = NULL; + reg->netreg_cb.cb = _netapi_cb; + reg->netreg_cb.ctx = reg; + gnrc_netreg_entry_init_cb(®->entry, demux_ctx, ®->netreg_cb); +#else /* SOCK_HAS_ASYNC */ gnrc_netreg_entry_init_mbox(®->entry, demux_ctx, ®->mbox); +#endif /* SOCK_HAS_ASYNC */ gnrc_netreg_register(type, ®->entry); } diff --git a/sys/net/gnrc/sock/include/sock_types.h b/sys/net/gnrc/sock/include/sock_types.h index 82e446d414..59d25aee2d 100644 --- a/sys/net/gnrc/sock/include/sock_types.h +++ b/sys/net/gnrc/sock/include/sock_types.h @@ -29,6 +29,9 @@ #include "net/af.h" #include "net/gnrc.h" #include "net/gnrc/netreg.h" +#ifdef SOCK_HAS_ASYNC +#include "net/sock/async.h" +#endif #include "net/sock/ip.h" #include "net/sock/udp.h" @@ -40,18 +43,54 @@ extern "C" { #define SOCK_MBOX_SIZE (8) /**< Size for gnrc_sock_reg_t::mbox_queue */ #endif +/** + * @brief Forward declaration + * @internal + */ +typedef struct gnrc_sock_reg gnrc_sock_reg_t; + +#ifdef SOCK_HAS_ASYNC +/** + * @brief Event callback for @ref gnrc_sock_reg_t + * @internal + */ +typedef void (*gnrc_sock_reg_cb_t)(gnrc_sock_reg_t *sock, + sock_async_flags_t flags); +#endif /* SOCK_HAS_ASYNC */ + /** * @brief sock @ref net_gnrc_netreg info * @internal */ -typedef struct gnrc_sock_reg { +struct gnrc_sock_reg { #ifdef MODULE_GNRC_SOCK_CHECK_REUSE struct gnrc_sock_reg *next; /**< list-like for internal storage */ #endif gnrc_netreg_entry_t entry; /**< @ref net_gnrc_netreg entry for mbox */ mbox_t mbox; /**< @ref core_mbox target for the sock */ msg_t mbox_queue[SOCK_MBOX_SIZE]; /**< queue for gnrc_sock_reg_t::mbox */ -} gnrc_sock_reg_t; +#ifdef SOCK_HAS_ASYNC + gnrc_netreg_entry_cbd_t netreg_cb; /**< netreg callback */ + /** + * @brief asynchronous upper layer callback + * + * @note All have void return value and a (sock pointer, sock_async_flags_t) + * pair, so casting between these function pointers is okay. + */ + union { + gnrc_sock_reg_cb_t generic; /**< generic version */ +#ifdef MODULE_SOCK_IP + sock_ip_cb_t ip; /**< IP version */ +#endif +#ifdef MODULE_SOCK_UDP + sock_udp_cb_t udp; /**< UDP version */ +#endif + } async_cb; +#ifdef SOCK_HAS_ASYNC_CTX + sock_async_ctx_t async_ctx; /**< asynchronous event context */ +#endif +#endif /* SOCK_HAS_ASYNC */ +}; /** * @brief Raw IP sock type diff --git a/sys/net/gnrc/sock/ip/gnrc_sock_ip.c b/sys/net/gnrc/sock/ip/gnrc_sock_ip.c index 548e0d710f..44719ff777 100644 --- a/sys/net/gnrc/sock/ip/gnrc_sock_ip.c +++ b/sys/net/gnrc/sock/ip/gnrc_sock_ip.c @@ -195,7 +195,26 @@ ssize_t sock_ip_send(sock_ip_t *sock, const void *data, size_t len, if (res <= 0) { return res; } +#ifdef SOCK_HAS_ASYNC + if ((sock != NULL) && (sock->reg.async_cb.ip)) { + sock->reg.async_cb.ip(sock, SOCK_ASYNC_MSG_SENT); + } +#endif /* SOCK_HAS_ASYNC */ return res; } +#ifdef SOCK_HAS_ASYNC +void sock_ip_set_cb(sock_ip_t *sock, sock_ip_cb_t cb) +{ + sock->reg.async_cb.ip = cb; +} + +#ifdef SOCK_HAS_ASYNC_CTX +sock_async_ctx_t *sock_ip_get_async_ctx(sock_ip_t *sock) +{ + return &sock->reg.async_ctx; +} +#endif /* SOCK_HAS_ASYNC_CTX */ +#endif /* SOCK_HAS_ASYNC */ + /** @} */ diff --git a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c index 6268befe2c..72eba92c6a 100644 --- a/sys/net/gnrc/sock/udp/gnrc_sock_udp.c +++ b/sys/net/gnrc/sock/udp/gnrc_sock_udp.c @@ -317,7 +317,26 @@ ssize_t sock_udp_send(sock_udp_t *sock, const void *data, size_t len, if (res > 0) { res -= sizeof(udp_hdr_t); } +#ifdef SOCK_HAS_ASYNC + if ((sock != NULL) && (sock->reg.async_cb.udp)) { + sock->reg.async_cb.udp(sock, SOCK_ASYNC_MSG_SENT); + } +#endif /* SOCK_HAS_ASYNC */ return res; } +#ifdef SOCK_HAS_ASYNC +void sock_udp_set_cb(sock_udp_t *sock, sock_udp_cb_t cb) +{ + sock->reg.async_cb.udp = cb; +} + +#ifdef SOCK_HAS_ASYNC_CTX +sock_async_ctx_t *sock_udp_get_async_ctx(sock_udp_t *sock) +{ + return &sock->reg.async_ctx; +} +#endif /* SOCK_HAS_ASYNC_CTX */ +#endif /* SOCK_HAS_ASYNC */ + /** @} */ From 23428ab77566e4cb773d8bf32440e11e6df12d3e Mon Sep 17 00:00:00 2001 From: "Martine S. Lenders" Date: Thu, 31 Oct 2019 17:32:43 +0100 Subject: [PATCH 2/2] tests: provide tests for gnrc_sock_async --- tests/gnrc_sock_async/Makefile | 16 +++ tests/gnrc_sock_async/Makefile.ci | 10 ++ tests/gnrc_sock_async/main.c | 160 ++++++++++++++++++++++++++ tests/gnrc_sock_async/tests/01-run.py | 27 +++++ 4 files changed, 213 insertions(+) create mode 100644 tests/gnrc_sock_async/Makefile create mode 100644 tests/gnrc_sock_async/Makefile.ci create mode 100644 tests/gnrc_sock_async/main.c create mode 100755 tests/gnrc_sock_async/tests/01-run.py diff --git a/tests/gnrc_sock_async/Makefile b/tests/gnrc_sock_async/Makefile new file mode 100644 index 0000000000..54832029a7 --- /dev/null +++ b/tests/gnrc_sock_async/Makefile @@ -0,0 +1,16 @@ +include ../Makefile.tests_common + +USEMODULE += auto_init_gnrc_netif +USEMODULE += gnrc_ipv6_hdr +USEMODULE += gnrc_pktdump +USEMODULE += gnrc_sock_async +USEMODULE += gnrc_sock_ip +USEMODULE += gnrc_sock_udp +USEMODULE += od +USEMODULE += xtimer + +CFLAGS += -DSOCK_HAS_IPV6 -DGNRC_PKTBUF_SIZE=200 +# mock IPv6 gnrc_nettype +CFLAGS += -DTEST_SUITES -DGNRC_NETTYPE_IPV6=GNRC_NETTYPE_TEST + +include $(RIOTBASE)/Makefile.include diff --git a/tests/gnrc_sock_async/Makefile.ci b/tests/gnrc_sock_async/Makefile.ci new file mode 100644 index 0000000000..73c8498472 --- /dev/null +++ b/tests/gnrc_sock_async/Makefile.ci @@ -0,0 +1,10 @@ +BOARD_INSUFFICIENT_MEMORY := \ + arduino-duemilanove \ + arduino-leonardo \ + arduino-nano \ + arduino-uno \ + atmega328p \ + nucleo-f031k6 \ + nucleo-f042k6 \ + stm32f030f4-demo \ + # diff --git a/tests/gnrc_sock_async/main.c b/tests/gnrc_sock_async/main.c new file mode 100644 index 0000000000..470e2144ce --- /dev/null +++ b/tests/gnrc_sock_async/main.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2019 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. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * + * @author Martine Lenders + * + * @} + */ + +#include +#include "net/ipv6/addr.h" +#include "net/ipv6/hdr.h" +#include "net/sock/ip.h" +#include "net/sock/udp.h" +#include "net/gnrc.h" +#include "net/gnrc/ipv6/hdr.h" +#include "net/gnrc/pktdump.h" +#include "net/gnrc/udp.h" +#include "net/protnum.h" +#include "od.h" +#include "thread.h" + +#define RECV_BUFFER_SIZE (128) + +#define TEST_PORT (38664U) +#define TEST_LOCAL { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } +#define TEST_REMOTE { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } +#define TEST_PAYLOAD { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef } + +static const uint8_t _test_local[] = TEST_LOCAL; +static const uint8_t _test_remote[] = TEST_REMOTE; +static const uint8_t _test_payload[] = TEST_PAYLOAD; + +static gnrc_netreg_entry_t _pktdump; + +static char _addr_str[IPV6_ADDR_MAX_STR_LEN]; +static uint8_t _buffer[128]; +static sock_ip_t _ip_sock; +static sock_udp_t _udp_sock; + +/* module is not compiled in, so provide this function for the test */ +ipv6_hdr_t *gnrc_ipv6_get_header(gnrc_pktsnip_t *pkt) +{ + gnrc_pktsnip_t *tmp = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); + if (tmp == NULL) { + return NULL; + } + + assert(tmp->data != NULL); + assert(tmp->size >= sizeof(ipv6_hdr_t)); + assert(ipv6_hdr_is(tmp->data)); + + return ((ipv6_hdr_t*) tmp->data); +} + +static void _recv_udp(sock_udp_t *sock, sock_async_flags_t flags) +{ + printf("UDP event triggered: %04X\n", flags); + if (flags & SOCK_ASYNC_MSG_RECV) { + sock_udp_ep_t remote; + ssize_t res; + + if ((res = sock_udp_recv(sock, _buffer, sizeof(_buffer), 0, + &remote)) >= 0) { + printf("Received UDP packet from [%s]:%u:\n", + ipv6_addr_to_str(_addr_str, (ipv6_addr_t *)remote.addr.ipv6, + sizeof(_addr_str)), + remote.port); + od_hex_dump(_buffer, res, OD_WIDTH_DEFAULT); + } + } + if (flags & SOCK_ASYNC_MSG_SENT) { + puts("UDP message successfully sent"); + } +} + +static void _recv_ip(sock_ip_t *sock, sock_async_flags_t flags) +{ + printf("IP event triggered: %04X\n", flags); + if (flags & SOCK_ASYNC_MSG_RECV) { + sock_ip_ep_t remote; + ssize_t res; + + if ((res = sock_ip_recv(sock, _buffer, sizeof(_buffer), 0, + &remote)) >= 0) { + printf("Received IP packet from [%s]:\n", + ipv6_addr_to_str(_addr_str, (ipv6_addr_t *)remote.addr.ipv6, + sizeof(_addr_str))); + od_hex_dump(_buffer, res, OD_WIDTH_DEFAULT); + } + } + if (flags & SOCK_ASYNC_MSG_SENT) { + puts("IP message successfully sent"); + } +} + +int main(void) +{ + gnrc_pktsnip_t *pkt; + sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + sock_udp_ep_t remote = SOCK_IPV6_EP_ANY; + + /* register for IPv6 to have a target */ + gnrc_netreg_entry_init_pid(&_pktdump, GNRC_NETREG_DEMUX_CTX_ALL, + gnrc_pktdump_pid); + gnrc_netreg_register(GNRC_NETTYPE_IPV6, &_pktdump); + + local.port = TEST_PORT; + sock_udp_create(&_udp_sock, &local, NULL, 0); + sock_ip_create(&_ip_sock, (sock_ip_ep_t *)&local, NULL, PROTNUM_UDP, 0); + + + /* XXX don't do it like this in production and use a proper `sock_async` + * frontend! This is just for testing. */ + sock_udp_set_cb(&_udp_sock, _recv_udp); + sock_ip_set_cb(&_ip_sock, _recv_ip); + memcpy(remote.addr.ipv6, _test_remote, sizeof(_test_remote)); + remote.port = TEST_PORT - 1; + + sock_udp_send(&_udp_sock, _test_payload, sizeof(_test_payload), &remote); + sock_ip_send(&_ip_sock, _test_payload, sizeof(_test_payload), + PROTNUM_RESERVED, (sock_ip_ep_t *)&remote); + + /* create packet to inject for reception */ + pkt = gnrc_netif_hdr_build(NULL, 0, NULL, 0); + assert(pkt != NULL); + memset(pkt->data, 0, pkt->size); + pkt = gnrc_ipv6_hdr_build(pkt, (ipv6_addr_t *)&_test_remote, + (ipv6_addr_t *)&_test_local); + assert(pkt != NULL); + /* module is not compiled in, so set header type manually */ + pkt->type = GNRC_NETTYPE_IPV6; + pkt = gnrc_udp_hdr_build(pkt, TEST_PORT - 1, TEST_PORT); + assert(pkt != NULL); + pkt = gnrc_pktbuf_add(pkt, _test_payload, sizeof(_test_payload), + GNRC_NETTYPE_UNDEF); + assert(pkt != NULL); + /* we dispatch twice, so hold one time */ + gnrc_pktbuf_hold(pkt, 1); + + /* trigger receive on UDP sock */ + gnrc_netapi_dispatch_receive(GNRC_NETTYPE_UDP, TEST_PORT, pkt); + /* trigger receive on IP sock */ + gnrc_netapi_dispatch_receive(GNRC_NETTYPE_IPV6, PROTNUM_UDP, pkt); + return 0; +} + +/** @} */ diff --git a/tests/gnrc_sock_async/tests/01-run.py b/tests/gnrc_sock_async/tests/01-run.py new file mode 100755 index 0000000000..f08f753780 --- /dev/null +++ b/tests/gnrc_sock_async/tests/01-run.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2019 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. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect_exact("UDP event triggered: 0020") + child.expect_exact("UDP message successfully sent") + child.expect_exact("IP event triggered: 0020") + child.expect_exact("IP message successfully sent") + child.expect_exact("UDP event triggered: 0010") + child.expect_exact("Received UDP packet from [fe80::2]:38663:") + child.expect_exact("00000000 01 23 45 67 89 AB CD EF") + child.expect_exact("IP event triggered: 0010") + child.expect_exact("Received IP packet from [fe80::2]:") + child.expect_exact("00000000 01 23 45 67 89 AB CD EF") + + +if __name__ == "__main__": + sys.exit(run(testfunc))