1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-14 17:13:50 +01:00

gnrc/netapi: add notify message API

The netapi notify API enables protocol-independent, cross-layer
notification events.
Lower layers in the network stack can use this to inform upper
layers of network events.
This commit is contained in:
Elena Frank 2025-12-09 14:17:48 +01:00
parent 1d0a275d17
commit 19a4ba04d1
10 changed files with 269 additions and 38 deletions

View File

@ -288,6 +288,7 @@
#include "net/netopt.h" #include "net/netopt.h"
#include "net/gnrc/netapi.h" #include "net/gnrc/netapi.h"
#include "net/gnrc/netapi/notify.h"
#include "net/gnrc/netreg.h" #include "net/gnrc/netreg.h"
#include "net/gnrc/nettype.h" #include "net/gnrc/nettype.h"
#include "net/gnrc/netif.h" #include "net/gnrc/netif.h"

View File

@ -60,6 +60,7 @@
#include "thread.h" #include "thread.h"
#include "net/netopt.h" #include "net/netopt.h"
#include "net/gnrc/netapi/notify.h"
#include "net/gnrc/nettype.h" #include "net/gnrc/nettype.h"
#include "net/gnrc/pkt.h" #include "net/gnrc/pkt.h"
@ -88,10 +89,17 @@ extern "C" {
#define GNRC_NETAPI_MSG_TYPE_GET (0x0204) #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) #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) * @brief Data structure to be send for setting (@ref GNRC_NETAPI_MSG_TYPE_SET)
* and getting (@ref GNRC_NETAPI_MSG_TYPE_GET) options * 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); 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 * @brief Shortcut function for sending @ref GNRC_NETAPI_MSG_TYPE_RCV messages
* *

View File

@ -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 <elena.frank@tu-dresden.de>
*/
#include <stdint.h>
#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
/** @} */

View File

@ -330,7 +330,8 @@ static inline void gnrc_netreg_entry_init_cb(gnrc_netreg_entry_t *entry,
* @brief Registers a thread to the registry. * @brief Registers a thread to the registry.
* *
* @details The semantics are: Thread gnrc_netreg_entry_t::pid is interested in * @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 * @param[in] type Type of the protocol. Must not be < GNRC_NETTYPE_UNDEF or
* >= GNRC_NETTYPE_NUMOF. * >= GNRC_NETTYPE_NUMOF.

View File

@ -120,6 +120,20 @@ typedef enum {
#endif #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 * @name Testing

View File

@ -43,6 +43,9 @@ endif
ifneq (,$(filter gnrc_netapi,$(USEMODULE))) ifneq (,$(filter gnrc_netapi,$(USEMODULE)))
DIRS += netapi DIRS += netapi
endif endif
ifneq (,$(filter gnrc_netapi_notify,$(USEMODULE)))
DIRS += netapi/notify
endif
ifneq (,$(filter gnrc_netif gnrc_netif_%,$(USEMODULE))) ifneq (,$(filter gnrc_netif gnrc_netif_%,$(USEMODULE)))
DIRS += netif DIRS += netif
endif endif

View File

@ -55,7 +55,7 @@ ifneq (,$(filter gnrc_uhcpc,$(USEMODULE)))
USEMODULE += fmt USEMODULE += fmt
endif 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 USEMODULE += gnrc
endif endif
@ -460,6 +460,10 @@ ifneq (,$(filter gnrc,$(USEMODULE)))
endif endif
endif endif
ifneq (,$(filter gnrc_netapi_notify,$(USEMODULE)))
USEMODULE += sema_inv
endif
ifneq (,$(filter gnrc_pktbuf, $(USEMODULE))) ifneq (,$(filter gnrc_pktbuf, $(USEMODULE)))
ifeq (,$(filter gnrc_pktbuf_%, $(USEMODULE))) ifeq (,$(filter gnrc_pktbuf_%, $(USEMODULE)))
USEMODULE += gnrc_pktbuf_static USEMODULE += gnrc_pktbuf_static

View File

@ -24,8 +24,10 @@
#include "mbox.h" #include "mbox.h"
#include "msg.h" #include "msg.h"
#include "net/gnrc/netreg.h" #include "net/gnrc/netreg.h"
#include "net/gnrc/netapi/notify.h"
#include "net/gnrc/pktbuf.h" #include "net/gnrc/pktbuf.h"
#include "net/gnrc/netapi.h" #include "net/gnrc/netapi.h"
#include "sema_inv.h"
#define ENABLE_DEBUG 0 #define ENABLE_DEBUG 0
#include "debug.h" #include "debug.h"
@ -82,6 +84,42 @@ static inline int _snd_rcv_mbox(mbox_t *mbox, uint16_t type, gnrc_pktsnip_t *pkt
} }
#endif #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, int gnrc_netapi_dispatch(gnrc_nettype_t type, uint32_t demux_ctx,
uint16_t cmd, gnrc_pktsnip_t *pkt) 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); gnrc_pktbuf_hold(pkt, numof - 1);
while (sendto) { while (sendto) {
#if defined(MODULE_GNRC_NETAPI_MBOX) || defined(MODULE_GNRC_NETAPI_CALLBACKS) int status = _dispatch_single(sendto, cmd, pkt);
uint32_t status = 0; if (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) {
gnrc_pktbuf_release_error(pkt, status); 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); sendto = gnrc_netreg_getnext(sendto);
} }
} }
@ -141,3 +146,48 @@ int gnrc_netapi_dispatch(gnrc_nettype_t type, uint32_t demux_ctx,
return numof; 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(&notify.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, &notify);
if (status < 0) {
numof--;
/* Decrease semaphore counter manually when sending failed. */
sema_inv_post(&notify.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(&notify.ack);
}
return numof;
}

View File

@ -0,0 +1,3 @@
MODULE = gnrc_netapi_notify
include $(RIOTBASE)/Makefile.base

View File

@ -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 <elena.frank@tu-dresden.de>
*/
#include <errno.h>
#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(&notify->ack);
return res;
}
/** @} */