mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #12625 from miri64/gnrc_sock/enh/async-support
gnrc_sock: provide asynchronous event implementation
This commit is contained in:
commit
7b13781cd2
@ -110,6 +110,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
|
||||
|
@ -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 += heap_cmd
|
||||
|
@ -17,6 +17,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
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
/** @} */
|
||||
|
@ -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 */
|
||||
|
||||
/** @} */
|
||||
|
16
tests/gnrc_sock_async/Makefile
Normal file
16
tests/gnrc_sock_async/Makefile
Normal file
@ -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
|
10
tests/gnrc_sock_async/Makefile.ci
Normal file
10
tests/gnrc_sock_async/Makefile.ci
Normal file
@ -0,0 +1,10 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-leonardo \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega328p \
|
||||
nucleo-f031k6 \
|
||||
nucleo-f042k6 \
|
||||
stm32f030f4-demo \
|
||||
#
|
160
tests/gnrc_sock_async/main.c
Normal file
160
tests/gnrc_sock_async/main.c
Normal file
@ -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 <m.lenders@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
/** @} */
|
27
tests/gnrc_sock_async/tests/01-run.py
Executable file
27
tests/gnrc_sock_async/tests/01-run.py
Executable file
@ -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))
|
Loading…
Reference in New Issue
Block a user