gnrc_netif: Add support for internal event loop
Enabled by the gnrc_netif_events pseudo module. Using an internal event loop within the gnrc_netif thread eliminates the risk of lost interrupts and lets ISR events always be handled before any send/receive requests from other threads are processed. The events in the event loop is also a potential hook for MAC layers and other link layer modules which may need to inject and process events before any external IPC messages are handled. Co-Authored-By: Koen Zandberg <koen@bergzand.net>
This commit is contained in:
parent
32d4b7eea5
commit
2013ac75f5
@ -195,6 +195,11 @@ ifneq (,$(filter gnrc_netif,$(USEMODULE)))
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter gnrc_netif_events,$(USEMODULE)))
|
||||||
|
USEMODULE += core_thread_flags
|
||||||
|
USEMODULE += event
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter ieee802154 nrfmin esp_now cc110x gnrc_sixloenc,$(USEMODULE)))
|
ifneq (,$(filter ieee802154 nrfmin esp_now cc110x gnrc_sixloenc,$(USEMODULE)))
|
||||||
ifneq (,$(filter gnrc_ipv6, $(USEMODULE)))
|
ifneq (,$(filter gnrc_ipv6, $(USEMODULE)))
|
||||||
USEMODULE += gnrc_sixlowpan
|
USEMODULE += gnrc_sixlowpan
|
||||||
|
|||||||
@ -31,6 +31,7 @@ PSEUDOMODULES += gnrc_netdev_default
|
|||||||
PSEUDOMODULES += gnrc_neterr
|
PSEUDOMODULES += gnrc_neterr
|
||||||
PSEUDOMODULES += gnrc_netapi_callbacks
|
PSEUDOMODULES += gnrc_netapi_callbacks
|
||||||
PSEUDOMODULES += gnrc_netapi_mbox
|
PSEUDOMODULES += gnrc_netapi_mbox
|
||||||
|
PSEUDOMODULES += gnrc_netif_events
|
||||||
PSEUDOMODULES += gnrc_pktbuf_cmd
|
PSEUDOMODULES += gnrc_pktbuf_cmd
|
||||||
PSEUDOMODULES += gnrc_netif_cmd_%
|
PSEUDOMODULES += gnrc_netif_cmd_%
|
||||||
PSEUDOMODULES += gnrc_netif_dedup
|
PSEUDOMODULES += gnrc_netif_dedup
|
||||||
|
|||||||
@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#include "kernel_types.h"
|
#include "kernel_types.h"
|
||||||
#include "msg.h"
|
#include "msg.h"
|
||||||
|
#include "event.h"
|
||||||
#include "net/ipv6/addr.h"
|
#include "net/ipv6/addr.h"
|
||||||
#include "net/gnrc/netapi.h"
|
#include "net/gnrc/netapi.h"
|
||||||
#include "net/gnrc/pkt.h"
|
#include "net/gnrc/pkt.h"
|
||||||
@ -94,6 +95,16 @@ typedef struct {
|
|||||||
* @see net_gnrc_netif_flags
|
* @see net_gnrc_netif_flags
|
||||||
*/
|
*/
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
#if IS_USED(MODULE_GNRC_NETIF_EVENTS) || IS_ACTIVE(DOXYGEN)
|
||||||
|
/**
|
||||||
|
* @brief Event queue for asynchronous events
|
||||||
|
*/
|
||||||
|
event_queue_t evq;
|
||||||
|
/**
|
||||||
|
* @brief ISR event for the network device
|
||||||
|
*/
|
||||||
|
event_t event_isr;
|
||||||
|
#endif /* MODULE_GNRC_NETIF_EVENTS */
|
||||||
#if (GNRC_NETIF_L2ADDR_MAXLEN > 0) || DOXYGEN
|
#if (GNRC_NETIF_L2ADDR_MAXLEN > 0) || DOXYGEN
|
||||||
/**
|
/**
|
||||||
* @brief The link-layer address currently used as the source address
|
* @brief The link-layer address currently used as the source address
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
#include <kernel_defines.h>
|
#include <kernel_defines.h>
|
||||||
|
|
||||||
#include "bitfield.h"
|
#include "bitfield.h"
|
||||||
|
#include "event.h"
|
||||||
#include "net/ethernet.h"
|
#include "net/ethernet.h"
|
||||||
#include "net/ipv6.h"
|
#include "net/ipv6.h"
|
||||||
#include "net/gnrc.h"
|
#include "net/gnrc.h"
|
||||||
@ -40,6 +41,16 @@
|
|||||||
#define ENABLE_DEBUG (0)
|
#define ENABLE_DEBUG (0)
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
|
#ifdef MODULE_GNRC_NETIF_EVENTS
|
||||||
|
/**
|
||||||
|
* @brief Event type used for passing netdev pointers together with the event
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
event_t super;
|
||||||
|
netdev_t *dev;
|
||||||
|
} event_netdev_t;
|
||||||
|
#endif /* MODULE_GNRC_NETIF_EVENTS */
|
||||||
|
|
||||||
static void _update_l2addr_from_dev(gnrc_netif_t *netif);
|
static void _update_l2addr_from_dev(gnrc_netif_t *netif);
|
||||||
static void _configure_netdev(netdev_t *dev);
|
static void _configure_netdev(netdev_t *dev);
|
||||||
static void *_gnrc_netif_thread(void *args);
|
static void *_gnrc_netif_thread(void *args);
|
||||||
@ -1348,6 +1359,93 @@ void gnrc_netif_default_init(gnrc_netif_t *netif)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if IS_USED(MODULE_GNRC_NETIF_EVENTS)
|
||||||
|
/**
|
||||||
|
* @brief Call the ISR handler from an event
|
||||||
|
*
|
||||||
|
* @param[in] evp pointer to the event
|
||||||
|
*/
|
||||||
|
static void _event_handler_isr(event_t *evp)
|
||||||
|
{
|
||||||
|
gnrc_netif_t *netif = container_of(evp, gnrc_netif_t, event_isr);
|
||||||
|
netif->dev->driver->isr(netif->dev);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline void _event_post(gnrc_netif_t *netif)
|
||||||
|
{
|
||||||
|
#if IS_USED(MODULE_GNRC_NETIF_EVENTS)
|
||||||
|
event_post(&netif->evq, &netif->event_isr);
|
||||||
|
#else
|
||||||
|
(void)netif;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Retrieve the netif event queue if enabled
|
||||||
|
*
|
||||||
|
* @param[in] netif gnrc_netif instance to operate on
|
||||||
|
*
|
||||||
|
* @return NULL if MODULE_GNRC_NETIF_EVENTS is not enabled
|
||||||
|
* @return gnrc_netif_t::evq if MODULE_GNRC_NETIF_EVENTS is enabled
|
||||||
|
*/
|
||||||
|
static inline event_queue_t *_get_evq(gnrc_netif_t *netif)
|
||||||
|
{
|
||||||
|
#ifdef MODULE_GNRC_NETIF_EVENTS
|
||||||
|
return &netif->evq;
|
||||||
|
#else
|
||||||
|
(void)netif;
|
||||||
|
return NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Process any pending events and wait for IPC messages
|
||||||
|
*
|
||||||
|
* This function will block until an IPC message is received. Events posted to
|
||||||
|
* the event queue will be processed while waiting for messages.
|
||||||
|
*
|
||||||
|
* @param[in] netif gnrc_netif instance to operate on
|
||||||
|
* @param[out] msg pointer to message buffer to write the first received message
|
||||||
|
*
|
||||||
|
* @return >0 if msg contains a new message
|
||||||
|
*/
|
||||||
|
static void _process_events_await_msg(gnrc_netif_t *netif, msg_t *msg)
|
||||||
|
{
|
||||||
|
if (IS_USED(MODULE_GNRC_NETIF_EVENTS)) {
|
||||||
|
while (1) {
|
||||||
|
/* Using messages for external IPC, and events for internal events */
|
||||||
|
|
||||||
|
/* First drain the queues before blocking the thread */
|
||||||
|
/* Events will be handled before messages */
|
||||||
|
DEBUG("gnrc_netif: handling events\n");
|
||||||
|
event_t *evp;
|
||||||
|
/* We can not use event_loop() or event_wait() because then we would not
|
||||||
|
* wake up when a message arrives */
|
||||||
|
event_queue_t *evq = _get_evq(netif);
|
||||||
|
while ((evp = event_get(evq))) {
|
||||||
|
DEBUG("gnrc_netif: event %p\n", (void *)evp);
|
||||||
|
if (evp->handler) {
|
||||||
|
evp->handler(evp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* non-blocking msg check */
|
||||||
|
int msg_waiting = msg_try_receive(msg);
|
||||||
|
if (msg_waiting > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DEBUG("gnrc_netif: waiting for events\n");
|
||||||
|
/* Block the thread until something interesting happens */
|
||||||
|
thread_flags_wait_any(THREAD_FLAG_MSG_WAITING | THREAD_FLAG_EVENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Only messages used for event handling */
|
||||||
|
DEBUG("gnrc_netif: waiting for incoming messages\n");
|
||||||
|
msg_receive(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void *_gnrc_netif_thread(void *args)
|
static void *_gnrc_netif_thread(void *args)
|
||||||
{
|
{
|
||||||
gnrc_netapi_opt_t *opt;
|
gnrc_netapi_opt_t *opt;
|
||||||
@ -1355,13 +1453,20 @@ static void *_gnrc_netif_thread(void *args)
|
|||||||
netdev_t *dev;
|
netdev_t *dev;
|
||||||
int res;
|
int res;
|
||||||
msg_t reply = { .type = GNRC_NETAPI_MSG_TYPE_ACK };
|
msg_t reply = { .type = GNRC_NETAPI_MSG_TYPE_ACK };
|
||||||
msg_t msg, msg_queue[CONFIG_GNRC_NETIF_MSG_QUEUE_SIZE];
|
msg_t msg_queue[CONFIG_GNRC_NETIF_MSG_QUEUE_SIZE];
|
||||||
|
|
||||||
DEBUG("gnrc_netif: starting thread %i\n", sched_active_pid);
|
DEBUG("gnrc_netif: starting thread %i\n", sched_active_pid);
|
||||||
netif = args;
|
netif = args;
|
||||||
gnrc_netif_acquire(netif);
|
gnrc_netif_acquire(netif);
|
||||||
dev = netif->dev;
|
dev = netif->dev;
|
||||||
netif->pid = sched_active_pid;
|
netif->pid = sched_active_pid;
|
||||||
|
|
||||||
|
#if IS_USED(MODULE_GNRC_NETIF_EVENTS)
|
||||||
|
netif->event_isr.handler = _event_handler_isr,
|
||||||
|
/* set up the event queue */
|
||||||
|
event_queue_init(&netif->evq);
|
||||||
|
#endif /* MODULE_GNRC_NETIF_EVENTS */
|
||||||
|
|
||||||
/* setup the link-layer's message queue */
|
/* setup the link-layer's message queue */
|
||||||
msg_init_queue(msg_queue, CONFIG_GNRC_NETIF_MSG_QUEUE_SIZE);
|
msg_init_queue(msg_queue, CONFIG_GNRC_NETIF_MSG_QUEUE_SIZE);
|
||||||
/* register the event callback with the device driver */
|
/* register the event callback with the device driver */
|
||||||
@ -1394,9 +1499,13 @@ static void *_gnrc_netif_thread(void *args)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
DEBUG("gnrc_netif: waiting for incoming messages\n");
|
msg_t msg;
|
||||||
msg_receive(&msg);
|
/* msg will be filled by _process_events_await_msg.
|
||||||
|
* The function will not return until a message has been received. */
|
||||||
|
_process_events_await_msg(netif, &msg);
|
||||||
|
|
||||||
/* dispatch netdev, MAC and gnrc_netapi messages */
|
/* dispatch netdev, MAC and gnrc_netapi messages */
|
||||||
|
DEBUG("gnrc_netif: message %u\n", (unsigned)msg.type);
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case NETDEV_MSG_TYPE_EVENT:
|
case NETDEV_MSG_TYPE_EVENT:
|
||||||
DEBUG("gnrc_netif: GNRC_NETDEV_MSG_TYPE_EVENT received\n");
|
DEBUG("gnrc_netif: GNRC_NETDEV_MSG_TYPE_EVENT received\n");
|
||||||
@ -1487,11 +1596,16 @@ static void _event_cb(netdev_t *dev, netdev_event_t event)
|
|||||||
gnrc_netif_t *netif = (gnrc_netif_t *) dev->context;
|
gnrc_netif_t *netif = (gnrc_netif_t *) dev->context;
|
||||||
|
|
||||||
if (event == NETDEV_EVENT_ISR) {
|
if (event == NETDEV_EVENT_ISR) {
|
||||||
msg_t msg = { .type = NETDEV_MSG_TYPE_EVENT,
|
if (IS_USED(MODULE_GNRC_NETIF_EVENTS)) {
|
||||||
.content = { .ptr = netif } };
|
_event_post(netif);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg_t msg = { .type = NETDEV_MSG_TYPE_EVENT,
|
||||||
|
.content = { .ptr = netif } };
|
||||||
|
|
||||||
if (msg_send(&msg, netif->pid) <= 0) {
|
if (msg_send(&msg, netif->pid) <= 0) {
|
||||||
puts("gnrc_netif: possibly lost interrupt.");
|
puts("gnrc_netif: possibly lost interrupt.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user