Netdev - Network Device Driver API

This is a generic low-level network driver interface. More...

Detailed Description

This is a generic low-level network driver interface.


This interface provides a uniform API for network stacks to interact with network device drivers. This interface is designed in a way, that it is completely agnostic to the used network stack. This way, device drivers for network devices (e.g. IEEE802.15.4 radios, Ethernet devices, ...) have to implemented once and can be used with any supported network stack in RIOT.

The functions provided by the interface cover three major parts:

  1. sending and receiving of actual network data
  2. network device configuration through reading and setting device parameters
  3. event handling

The Interrupt Context Problem

Network devices are typically connected to the host CPU via some sort of bus, most commonly via SPI. This type of connection has the disadvantage, that the bus is not used by the network device alone, but it may be shared with other devices. This makes it necessary to synchronize access to the bus to prevent bus access collisions.

To illustrate this behavior, let's look at a typical error situation, that leads to a very hard to find and debug latent failure: say we have two devices A and B on the same SPI bus. Our CPU is now transferring a chunk of 100 bytes to device A. After 20 bytes were transferred, device B triggers an external interrupt on the host CPU. The interrupt handling now typically requires the reading of some sort of status register on the 'triggering' device, device B in this case. So what would happen here, is that the device driver for device B would initiate a new SPI transfer on the already used bus to read B's status register -> BAM.

The peripheral drivers for shared buses (i.e. SPI and I2C) implement access synchronization using mutexes, which are locked and unlocked in the driver's require and release functions. The problem is now, that this type of synchronization does only work in thread context, but not in interrupt context. With reasonable effort and resource usage, we have no means of synchronizing the bus access also in interrupt context.

The solution to this problem as implemented by this interface is not to call any function that interacts with a device directly from interrupt context. Unfortunately this requires some added complexity for synchronization efforts between thread and interrupt context to be able to handle device events (i.e. external interrupts). See section Events for more information.

Context requirements

The netdev interface expects the network device drivers to run in thread context (see section above). The interface was however designed in a way, to allow more than one device driver to be serviced in the same thread.

The key design element for netdev is, that device drivers implementing this interface are not able to run stand-alone in a thread, but need some bootstrapping code. This bootstrapping code can be anything from a simple msg_receive() loop (as done for the GNRC adaption) to a complete network stack that works without messaging entirely but is build on function call interfaces.

Sending and Receiving

Sending data using the netdev interface is straight forward: simply call the drivers send() function, passing it the data that should be sent. The caller of the send() function (e.g. a network stack) must hereby make sure, that the data is in the correct format expected by the specific network device driver. Typically, the data needs to contain a pre-filled link layer header as e.g. an IEEE802.15.4 or Ethernet header.

Receiving data using the netdev interface requires typically four steps:

  1. wait for a NETDEV_EVENT_RX_COMPLETE event
  2. call the recv() function with buf := NULL and len := 0 to get the size of the received data
  3. allocate a large enough buffer in some way
  4. call the recv() function a second time, passing the buffer and reading the received data into this buffer

This receive sequence can of course be simplified by skipping steps 2 and 3 when using fixed sized pre-allocated buffers or similar means. *

The send() and recv() functions must never be called from interrupt context.

Device Configuration

The netdev interface covers a wide variety of network devices, which differ to some extend in their configuration parameters (e.g. radios vs. wired interfaces, channel selection vs. link status detection). To cover this variety, netdev provides a generic configuration interface by exposing simple get() and set() functions. These are based on a globally defined and extendable list of options as defined in netopt.h.

Every device driver can choose the options which it supports for reading and/or writing from this list. If an option is not supported by the device driver, the driver simply returns -ENOTSUP.

The get() and set() functions must never be called from interrupt context.


Network devices typically signal events by triggering external interrupts on certain dedicated GPIO pins (in case of external devices), or signal them by triggering internal interrupts directly (in case of register mapped devices). As stated above, we are not allowed to do any kind of interaction with our network device that involves bus access when in interrupt mode. To circumvent this, the

  1. an interrupt is triggered
  2. the drivers interrupt routine calls the registered netdev->event_callback() function with event:= NETDEV_EVENT_ISR as argument
  3. the netdev->event_callback() (as it is implemented by the 'user' code) notifies the thread that hosts the device driver. This can be done in many ways, e.g. by using messaging, mutexes, thread flags and more
  4. the hosting thread is scheduled and calls the netdev interfaces isr() function
  5. now the driver can actual start to handle the interrupt, by e.g. reading status registers and triggering any subsequent actions like signaling a NETDEV_EVENT_RX_COMPLETE

The way that is used for waking up the hosting thread and telling is to call the isr() function is completely up to the netdev external code and can be done in many ways (e.g. sending messages, # setting thread flags, unlocking mutexes, etc.).

Any event that is not of type NETDEV_EVENT_ISR is expected to be triggered from thread context. This enables the code that sits on top of netdev to perform the necessary actions right away, as for example reading the received data from the network device or similar.

The netdev_event_cb_t function runs in interrupt context when called for NETDEV_EVENT_ISR, but it must run in thread context for all other events.


The following example illustrates a receive sequence triggered by an external interrupt:

  1. frame arrives for device
  2. The driver previously registered an ISR for handling received frames. This ISR then calls netdev->event_callback() with event:=NETDEV_EVENT_ISR (from Interrupt Service Routine) which wakes up event handler
  3. event handler calls netdev->driver->isr() (from thread context)
  4. netdev->driver->isr() calls netdev->event_callback() with event:=NETDEV_EVENT_RX_COMPLETE
  5. netdev->event_callback() uses netdev->driver->recv() to fetch frame
RX event example


 802.15.4 radio drivers
 Ethernet drivers
 IEEE802.15.4 SubMAC netdev layer
 This module defines implements the netdev API on top of the IEEE 802.15.4 radio HAL.
 LoRa drivers
 netdev BLE mode
 BLE adaption of netdev.


file  layer.h
 Netdev layer helper functions.
file  netdev.h
 Definitions low-level network driver interface.

Data Structures

struct  netdev_radio_rx_info
 Received frame status information for most radios. More...
struct  netdev
 Structure to hold driver state. More...
struct  netdev_driver
 Structure to hold driver interface -> function mapping. More...


#define NETDEV_INDEX_ANY   (0xFF)
 Will match any device index.


typedef struct netdev netdev_t
 Forward declaration for netdev struct.
typedef void(* netdev_event_cb_t) (netdev_t *dev, netdev_event_t event)
 Event callback for signaling event to upper layers. More...
typedef struct netdev_driver netdev_driver_t
 Structure to hold driver interface -> function mapping. More...


enum  netdev_event_t {
 Possible event types that are send from the device driver to the upper layer. More...


static void netdev_register (struct netdev *dev, netdev_type_t type, uint8_t index)
 Register a device with netdev. More...
static int netdev_get_notsup (netdev_t *dev, netopt_t opt, void *value, size_t max_len)
 Convenience function for declaring get() as not supported in general. More...
static int netdev_set_notsup (netdev_t *dev, netopt_t opt, const void *value, size_t value_len)
 Convenience function for declaring set() as not supported in general. More...
static void netdev_trigger_event_isr (netdev_t *netdev)
 Informs netdev there was an interrupt request from the network device. More...

Network device types

When implementing a new type that is able to carry IPv6, have a look if you need to update Stack-independent helpers for IPv6 over X as well.
enum  {
enum  netdev_type_t {
 Driver types for netdev. More...

Typedef Documentation

◆ netdev_driver_t

Structure to hold driver interface -> function mapping.

The send/receive functions expect/return a full ethernet frame (dst mac, src mac, ethertype, payload, no checksum).

◆ netdev_event_cb_t

typedef void(* netdev_event_cb_t) (netdev_t *dev, netdev_event_t event)

Event callback for signaling event to upper layers.

[in]typetype of the event

Definition at line 294 of file netdev.h.

Enumeration Type Documentation

◆ netdev_event_t

Possible event types that are send from the device driver to the upper layer.


driver needs it's ISR handled


started to receive a frame


finished receiving a frame


started to transfer a frame


transfer frame complete


transfer frame complete and data pending flag

Issue an NETDEV_EVENT_TX_COMPLETE event instead and pass the data pending info in netdev_driver_t::confirm_send via the info parameter

ACK requested but not received.

Issue an NETDEV_EVENT_TX_COMPLETE event instead and return -ECOMM in netdev_driver_t::confirm_send. Via the info parameter additional details about the error can be passed

couldn't transfer frame

Issue an NETDEV_EVENT_TX_COMPLETE event instead and return -EBUSY in netdev_driver_t::confirm_send.

link established


link gone


timeout when sending


timeout when receiving


wrong CRC


channel changed


channel activity detection done

Definition at line 235 of file netdev.h.

◆ netdev_type_t

Driver types for netdev.

New entries must be added at the bottom of the list because the values need to remain constant to generate stable L2 addresses.

Will match any device type.

Definition at line 304 of file netdev.h.

Function Documentation

◆ netdev_get_notsup()

static int netdev_get_notsup ( netdev_t dev,
netopt_t  opt,
void *  value,
size_t  max_len 

Convenience function for declaring get() as not supported in general.

always returns -ENOTSUP

Definition at line 575 of file netdev.h.

◆ netdev_register()

static void netdev_register ( struct netdev dev,
netdev_type_t  type,
uint8_t  index 

Register a device with netdev.

Must by called by the driver's setup function.

[out]devthe new netdev
[in]typethe driver used for the netdev
[in]indexthe index in the config struct

Definition at line 367 of file netdev.h.

◆ netdev_set_notsup()

static int netdev_set_notsup ( netdev_t dev,
netopt_t  opt,
const void *  value,
size_t  value_len 

Convenience function for declaring set() as not supported in general.

always returns -ENOTSUP

Definition at line 595 of file netdev.h.

◆ netdev_trigger_event_isr()

static void netdev_trigger_event_isr ( netdev_t netdev)

Informs netdev there was an interrupt request from the network device.

This function calls netdev_t::event_callback with NETDEV_EVENT_ISR event.

netdevnetdev instance of the device associated to the interrupt.

Definition at line 613 of file netdev.h.