diff --git a/sys/include/net/gnrc.h b/sys/include/net/gnrc.h index c970eb9638..99b3985c21 100644 --- a/sys/include/net/gnrc.h +++ b/sys/include/net/gnrc.h @@ -288,6 +288,7 @@ #include "net/netopt.h" #include "net/gnrc/netapi.h" +#include "net/gnrc/netapi/notify.h" #include "net/gnrc/netreg.h" #include "net/gnrc/nettype.h" #include "net/gnrc/netif.h" diff --git a/sys/include/net/gnrc/netapi.h b/sys/include/net/gnrc/netapi.h index ea8157e408..6c68b39368 100644 --- a/sys/include/net/gnrc/netapi.h +++ b/sys/include/net/gnrc/netapi.h @@ -60,6 +60,7 @@ #include "thread.h" #include "net/netopt.h" +#include "net/gnrc/netapi/notify.h" #include "net/gnrc/nettype.h" #include "net/gnrc/pkt.h" @@ -88,10 +89,17 @@ extern "C" { #define GNRC_NETAPI_MSG_TYPE_GET (0x0204) /** - * @brief @ref core_msg type for replying to get and set option messages + * @brief @ref core_msg type for replying to get/set option, and notify messages */ #define GNRC_NETAPI_MSG_TYPE_ACK (0x0205) +/** + * @brief @ref core_msg type for sending a general event notifications to one or more subscribers + * + * This sends back a @ref GNRC_NETAPI_MSG_TYPE_ACK, so it should be used with `msg_send_receive()`. + */ +#define GNRC_NETAPI_MSG_TYPE_NOTIFY (0x0207) + /** * @brief Data structure to be send for setting (@ref GNRC_NETAPI_MSG_TYPE_SET) * and getting (@ref GNRC_NETAPI_MSG_TYPE_GET) options @@ -183,6 +191,24 @@ static inline int gnrc_netapi_dispatch_send(gnrc_nettype_t type, uint32_t demux_ return gnrc_netapi_dispatch(type, demux_ctx, GNRC_NETAPI_MSG_TYPE_SND, pkt); } +/** + * @brief Sends a @ref GNRC_NETAPI_MSG_TYPE_NOTIFY command to all subscribers to + * (@p type, @p demux_ctx). + * + * @note This blocks the sender until all registered subscribers have ack'ed the notification + * or sending has failed. The @p data can be freed after the function returned. + * + * @param[in] type Type of the targeted network module. + * @param[in] demux_ctx Demultiplexing context for @p type. + * @param[in] event Notification event type. + * @param[in] data Data associated with the event. + * @param[in] data_len Size of @p data. + * + * @return Number of subscribers to (@p type, @p demux_ctx). + */ +int gnrc_netapi_notify(gnrc_nettype_t type, uint32_t demux_ctx, netapi_notify_t event, + void *data, size_t data_len); + /** * @brief Shortcut function for sending @ref GNRC_NETAPI_MSG_TYPE_RCV messages * diff --git a/sys/include/net/gnrc/netapi/notify.h b/sys/include/net/gnrc/netapi/notify.h new file mode 100644 index 0000000000..c7c6c1e1d0 --- /dev/null +++ b/sys/include/net/gnrc/netapi/notify.h @@ -0,0 +1,91 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @defgroup net_gnrc_netapi_notify Notification events for network APIs. + * @ingroup net_gnrc_netapi + * @brief Network event notification types. + * @{ + * + * @file + * @brief Network event notification type definitions. + * + * @details The purpose of this API is to allow lower layers in the network + * stack to inform higher layers of general, protocol-independent + * network events. + * It can be used through the @ref net_gnrc_netapi to notify any + * other network interfaces that have registered themselves through + * @ref net_gnrc_netreg for the corresponding @ref net_gnrc_nettype. + * + * @note Notification events are associated with type-dependent event data. + * Receivers should not access the data directly, but instead use the + * the dedicated API functions to copy the data from the sender's. + * If the data is read manually, the receiver is responsible for + * calling @ref gnrc_netapi_notify_ack to unblock the sender. + * + * @author Elena Frank + */ + +#include +#ifdef __cplusplus +extern "C" { +#endif + +#include "net/gnrc/netif/conf.h" +#include "sema_inv.h" + +/** + * @brief Definition of notification event types in the network stack. + * + * @note Expand at will. + * If event data is associated with the type, a helper function must also + * be added for parsing the data and calling @ref gnrc_netapi_notify_ack. + */ +typedef enum { + PLACEHOLDER +} netapi_notify_t; + +/** + * @brief Data structure to be sent for netapi notification events. + */ +typedef struct { + netapi_notify_t event; /**< the type of event */ + void *_data; /**< associated event data. */ + uint16_t _data_len; /**< size of the event data */ + sema_inv_t ack; /**< inverse semaphore for collecting ack's */ +} gnrc_netapi_notify_t; + +/** + * @brief Acknowledge that a notify event was received and its data read. + * + * @param[in] ack Pointer to semaphore that is used to collect ack's. + */ +static inline void gnrc_netapi_notify_ack(sema_inv_t *ack) +{ + sema_inv_post(ack); +} + +/** + * @brief Copy the connection event data associated with a @ref gnrc_netapi_notify_t event. + * + * @note This will call @ref gnrc_netapi_notify_ack. + * + * @param[in] notify Pointer to the received notify event. + * @param[in] data_len Size of the expected data type. + * @param[out] data Connection data received in the @p notify event. + * + * @retval Size of the data type on success. + * @retval -EINVAL if the data in @p notify is invalid or doesn't match the expected + * data length. + */ +int gnrc_netapi_notify_copy_event_data(gnrc_netapi_notify_t *notify, uint8_t data_len, void *data); + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/sys/include/net/gnrc/netreg.h b/sys/include/net/gnrc/netreg.h index ad48958850..58a41a7ec6 100644 --- a/sys/include/net/gnrc/netreg.h +++ b/sys/include/net/gnrc/netreg.h @@ -330,7 +330,8 @@ static inline void gnrc_netreg_entry_init_cb(gnrc_netreg_entry_t *entry, * @brief Registers a thread to the registry. * * @details The semantics are: Thread gnrc_netreg_entry_t::pid is interested in - * packets of protocol @p type with context gnrc_netreg_entry_t::demux_ctx. + * packets or network events of protocol @p type with context + * gnrc_netreg_entry_t::demux_ctx. * * @param[in] type Type of the protocol. Must not be < GNRC_NETTYPE_UNDEF or * >= GNRC_NETTYPE_NUMOF. diff --git a/sys/include/net/gnrc/nettype.h b/sys/include/net/gnrc/nettype.h index 85ca4fccb1..b054a0dace 100644 --- a/sys/include/net/gnrc/nettype.h +++ b/sys/include/net/gnrc/nettype.h @@ -120,6 +120,20 @@ typedef enum { #endif /** @} */ + /** + * @{ + * @name Cross-layer network events. + * + * @brief Subscription types for protocol-independent events. + * They are used by lower-layers in the network stack to published + * information about the network state. + * + * @details See @ref net_gnrc_netapi_notify. + * } + */ + + /** @} */ + /** * @{ * @name Testing diff --git a/sys/net/gnrc/Makefile b/sys/net/gnrc/Makefile index ee7772b21d..182a8a1566 100644 --- a/sys/net/gnrc/Makefile +++ b/sys/net/gnrc/Makefile @@ -43,6 +43,9 @@ endif ifneq (,$(filter gnrc_netapi,$(USEMODULE))) DIRS += netapi endif +ifneq (,$(filter gnrc_netapi_notify,$(USEMODULE))) + DIRS += netapi/notify +endif ifneq (,$(filter gnrc_netif gnrc_netif_%,$(USEMODULE))) DIRS += netif endif diff --git a/sys/net/gnrc/Makefile.dep b/sys/net/gnrc/Makefile.dep index 09c4fccbdd..8b306dc9c8 100644 --- a/sys/net/gnrc/Makefile.dep +++ b/sys/net/gnrc/Makefile.dep @@ -55,7 +55,7 @@ ifneq (,$(filter gnrc_uhcpc,$(USEMODULE))) USEMODULE += fmt endif -ifneq (,$(filter gnrc_%,$(filter-out gnrc_lorawan gnrc_lorawan_1_1 gnrc_netapi gnrc_netreg gnrc_netif% gnrc_pkt%,$(USEMODULE)))) +ifneq (,$(filter gnrc_%,$(filter-out gnrc_lorawan gnrc_lorawan_1_1 gnrc_netapi gnrc_netapi_notify gnrc_netreg gnrc_netif% gnrc_pkt%,$(USEMODULE)))) USEMODULE += gnrc endif @@ -460,6 +460,10 @@ ifneq (,$(filter gnrc,$(USEMODULE))) endif endif +ifneq (,$(filter gnrc_netapi_notify,$(USEMODULE))) + USEMODULE += sema_inv +endif + ifneq (,$(filter gnrc_pktbuf, $(USEMODULE))) ifeq (,$(filter gnrc_pktbuf_%, $(USEMODULE))) USEMODULE += gnrc_pktbuf_static diff --git a/sys/net/gnrc/netapi/gnrc_netapi.c b/sys/net/gnrc/netapi/gnrc_netapi.c index a8cecb285b..680712e32e 100644 --- a/sys/net/gnrc/netapi/gnrc_netapi.c +++ b/sys/net/gnrc/netapi/gnrc_netapi.c @@ -24,8 +24,10 @@ #include "mbox.h" #include "msg.h" #include "net/gnrc/netreg.h" +#include "net/gnrc/netapi/notify.h" #include "net/gnrc/pktbuf.h" #include "net/gnrc/netapi.h" +#include "sema_inv.h" #define ENABLE_DEBUG 0 #include "debug.h" @@ -82,6 +84,42 @@ static inline int _snd_rcv_mbox(mbox_t *mbox, uint16_t type, gnrc_pktsnip_t *pkt } #endif +static int _dispatch_single(gnrc_netreg_entry_t *sendto, uint16_t cmd, void *data) +{ + int status = 0; + +#if defined(MODULE_GNRC_NETAPI_MBOX) || defined(MODULE_GNRC_NETAPI_CALLBACKS) + switch (sendto->type) { + case GNRC_NETREG_TYPE_DEFAULT: + if (_gnrc_netapi_send_recv(sendto->target.pid, data, cmd) < 1) { + /* unable to dispatch packet */ + status = -EIO; + } + break; +# ifdef MODULE_GNRC_NETAPI_MBOX + case GNRC_NETREG_TYPE_MBOX: + if (_snd_rcv_mbox(sendto->target.mbox, cmd, data) < 1) { + /* unable to dispatch packet */ + status = -EIO; + } + break; +# endif +# ifdef MODULE_GNRC_NETAPI_CALLBACKS + case GNRC_NETREG_TYPE_CB: + sendto->target.cbd->cb(cmd, data, sendto->target.cbd->ctx); + break; +# endif + default: + /* unknown dispatch type */ + status = -ECANCELED; + break; + } +#else + status = _gnrc_netapi_send_recv(sendto->target.pid, data, cmd); +#endif + return status; +} + int gnrc_netapi_dispatch(gnrc_nettype_t type, uint32_t demux_ctx, uint16_t cmd, gnrc_pktsnip_t *pkt) { @@ -96,43 +134,10 @@ int gnrc_netapi_dispatch(gnrc_nettype_t type, uint32_t demux_ctx, gnrc_pktbuf_hold(pkt, numof - 1); while (sendto) { -#if defined(MODULE_GNRC_NETAPI_MBOX) || defined(MODULE_GNRC_NETAPI_CALLBACKS) - uint32_t status = 0; - switch (sendto->type) { - case GNRC_NETREG_TYPE_DEFAULT: - if (_gnrc_netapi_send_recv(sendto->target.pid, pkt, - cmd) < 1) { - /* unable to dispatch packet */ - status = EIO; - } - break; -#ifdef MODULE_GNRC_NETAPI_MBOX - case GNRC_NETREG_TYPE_MBOX: - if (_snd_rcv_mbox(sendto->target.mbox, cmd, pkt) < 1) { - /* unable to dispatch packet */ - status = EIO; - } - break; -#endif -#ifdef MODULE_GNRC_NETAPI_CALLBACKS - case GNRC_NETREG_TYPE_CB: - sendto->target.cbd->cb(cmd, pkt, sendto->target.cbd->ctx); - break; -#endif - default: - /* unknown dispatch type */ - status = ECANCELED; - break; - } - if (status != 0) { + int status = _dispatch_single(sendto, cmd, pkt); + if (status < 0) { gnrc_pktbuf_release_error(pkt, status); } -#else - if (_gnrc_netapi_send_recv(sendto->target.pid, pkt, cmd) < 1) { - /* unable to dispatch packet */ - gnrc_pktbuf_release_error(pkt, EIO); - } -#endif sendto = gnrc_netreg_getnext(sendto); } } @@ -141,3 +146,48 @@ int gnrc_netapi_dispatch(gnrc_nettype_t type, uint32_t demux_ctx, return numof; } + +int gnrc_netapi_notify(gnrc_nettype_t type, uint32_t demux_ctx, netapi_notify_t event, + void *data, size_t data_len) +{ + gnrc_netreg_acquire_shared(); + + int numof = gnrc_netreg_num(type, demux_ctx); + if (numof <= 0) { + gnrc_netreg_release_shared(); + return numof; + } + + gnrc_netapi_notify_t notify = { + .event = event, + ._data = data, + ._data_len = data_len, + }; + + /* Use inverse semaphore to count the acknowledgments from the receivers. */ + sema_inv_init(¬ify.ack, numof); + + /* Look up the registered threads for this message type. */ + gnrc_netreg_entry_t *sendto = gnrc_netreg_lookup(type, demux_ctx); + + /* Dispatch to all registered threads. */ + while (sendto) { + int status = _dispatch_single(sendto, GNRC_NETAPI_MSG_TYPE_NOTIFY, ¬ify); + if (status < 0) { + numof--; + /* Decrease semaphore counter manually when sending failed. */ + sema_inv_post(¬ify.ack); + } + sendto = gnrc_netreg_getnext(sendto); + } + + gnrc_netreg_release_shared(); + + if (data != NULL) { + /* Wait for ACK from all receivers to ensure that the data was read and + * that there won't be any dangling pointers after the function returned. */ + sema_inv_wait(¬ify.ack); + } + + return numof; +} diff --git a/sys/net/gnrc/netapi/notify/Makefile b/sys/net/gnrc/netapi/notify/Makefile new file mode 100644 index 0000000000..7b4541bc39 --- /dev/null +++ b/sys/net/gnrc/netapi/notify/Makefile @@ -0,0 +1,3 @@ +MODULE = gnrc_netapi_notify + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c b/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c new file mode 100644 index 0000000000..a9a21b9c4d --- /dev/null +++ b/sys/net/gnrc/netapi/notify/gnrc_netapi_notify.c @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2025 TU Dresden + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @{ + * @ingroup net_gnrc_netapi_notify + * @file + * @brief Helper functions to extract data from netapi notify events. + * + * @author Elena Frank + */ + +#include + +#include "net/gnrc/netapi/notify.h" + +int gnrc_netapi_notify_copy_event_data(gnrc_netapi_notify_t *notify, uint8_t data_len, void *data) +{ + (void) data_len; + (void) data; + + int res; + + switch (notify->event) { + default: + res = -EINVAL; + break; + } + + /* Acknowledge the read data */ + gnrc_netapi_notify_ack(¬ify->ack); + + return res; +} + +/** @} */