This is a generic low-level network driver interface. More...
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:
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.
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 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:
buf := NULL
and len := 0
to get the size of the received dataThis receive sequence can of course be simplified by skipping steps 2 and 3 when using fixed sized pre-allocated buffers or similar means. *
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
.
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
event:=
NETDEV_EVENT_ISR as argumentnetdev
interfaces isr() functionThe 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 following example illustrates a receive sequence triggered by an external interrupt:
event:=
NETDEV_EVENT_ISR (from Interrupt Service Routine) which wakes up event handlerevent:=
NETDEV_EVENT_RX_COMPLETEModules | |
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 | |
Wi-Fi drivers | |
netdev BLE mode | |
BLE adaption of netdev. | |
Files | |
file | netdev.h |
Definitions low-level network driver interface. | |
file | layer.h |
Netdev layer helper functions. | |
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... | |
Macros | |
#define | NETDEV_INDEX_ANY (0xFF) |
Will match any device index. | |
#define | CONFIG_NETDEV_REGISTER_SIGNAL 0 |
Call netdev_register_signal when the netdev device is registered. | |
Typedefs | |
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. | |
typedef struct netdev_driver | netdev_driver_t |
Structure to hold driver interface -> function mapping. | |
Enumerations | |
enum | netdev_event_t { NETDEV_EVENT_ISR , NETDEV_EVENT_RX_STARTED , NETDEV_EVENT_RX_COMPLETE , NETDEV_EVENT_TX_STARTED , NETDEV_EVENT_TX_COMPLETE , NETDEV_EVENT_TX_COMPLETE_DATA_PENDING , NETDEV_EVENT_TX_NOACK , NETDEV_EVENT_TX_MEDIUM_BUSY , NETDEV_EVENT_LINK_UP , NETDEV_EVENT_LINK_DOWN , NETDEV_EVENT_TX_TIMEOUT , NETDEV_EVENT_RX_TIMEOUT , NETDEV_EVENT_CRC_ERROR , NETDEV_EVENT_FHSS_CHANGE_CHANNEL , NETDEV_EVENT_CAD_DONE } |
Possible event types that are send from the device driver to the upper layer. More... | |
Functions | |
void | netdev_register_signal (struct netdev *dev, netdev_type_t type, uint8_t index) |
Signal that the netdev_register function registered the device. | |
static void | netdev_register (struct netdev *dev, netdev_type_t type, uint8_t index) |
Register a device with netdev. | |
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. | |
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. | |
static void | netdev_trigger_event_isr (netdev_t *netdev) |
Informs netdev there was an interrupt request from the network device. | |
Network device types | |
| |
enum | { NETDEV_TYPE_UNKNOWN , NETDEV_TYPE_TEST , NETDEV_TYPE_RAW , NETDEV_TYPE_ETHERNET , NETDEV_TYPE_IEEE802154 , NETDEV_TYPE_BLE , NETDEV_TYPE_CC110X , NETDEV_TYPE_LORA , NETDEV_TYPE_NRFMIN , NETDEV_TYPE_NRF24L01P_NG , NETDEV_TYPE_SLIP , NETDEV_TYPE_ESP_NOW } |
enum | netdev_type_t { NETDEV_ANY = 0 , NETDEV_AT86RF215 , NETDEV_AT86RF2XX , NETDEV_CC2538 , NETDEV_DOSE , NETDEV_ENC28J60 , NETDEV_KW41ZRF , NETDEV_MRF24J40 , NETDEV_NRF802154 , NETDEV_STM32_ETH , NETDEV_CC110X , NETDEV_SX127X , NETDEV_SAM0_ETH , NETDEV_ESP_NOW , NETDEV_NRF24L01P_NG , NETDEV_SOCKET_ZEP , NETDEV_SX126X , NETDEV_SX1280 , NETDEV_CC2420 , NETDEV_ETHOS , NETDEV_SLIPDEV , NETDEV_TAP , NETDEV_W5100 , NETDEV_ENCX24J600 , NETDEV_ATWINC15X0 , NETDEV_KW2XRF , NETDEV_ESP_ETH , NETDEV_ESP_WIFI , NETDEV_CDC_ECM , NETDEV_TINYUSB , NETDEV_W5500 } |
Driver types for netdev. More... | |
#define CONFIG_NETDEV_REGISTER_SIGNAL 0 |
Call netdev_register_signal when the netdev device is registered.
#define NETDEV_INDEX_ANY (0xFF) |
typedef struct netdev_driver 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).
typedef void(* netdev_event_cb_t) (netdev_t *dev, netdev_event_t event) |
enum netdev_event_t |
Possible event types that are send from the device driver to the upper layer.
Enumerator | |
---|---|
NETDEV_EVENT_ISR | driver needs it's ISR handled |
NETDEV_EVENT_RX_STARTED | started to receive a frame |
NETDEV_EVENT_RX_COMPLETE | finished receiving a frame |
NETDEV_EVENT_TX_STARTED | started to transfer a frame |
NETDEV_EVENT_TX_COMPLETE | transfer frame complete |
NETDEV_EVENT_TX_COMPLETE_DATA_PENDING | transfer frame complete and data pending flag
|
NETDEV_EVENT_TX_NOACK | ACK requested but not received.
|
NETDEV_EVENT_TX_MEDIUM_BUSY | couldn't transfer frame
|
NETDEV_EVENT_LINK_UP | link established |
NETDEV_EVENT_LINK_DOWN | link gone |
NETDEV_EVENT_TX_TIMEOUT | timeout when sending |
NETDEV_EVENT_RX_TIMEOUT | timeout when receiving |
NETDEV_EVENT_CRC_ERROR | wrong CRC |
NETDEV_EVENT_FHSS_CHANGE_CHANNEL | channel changed |
NETDEV_EVENT_CAD_DONE | channel activity detection done |
enum netdev_type_t |
|
inlinestatic |
void netdev_register_signal | ( | struct netdev * | dev, |
netdev_type_t | type, | ||
uint8_t | index | ||
) |
Signal that the netdev_register function registered the device.
This function is called right after @ref netdev_register registered the device.
[in] | dev | pointer to the device descriptor |
[in] | type | the driver used for the netdev |
[in] | index | the index in the config struct |
|
inlinestatic |