Netdev - Network Device Driver API¶
This is a generic low-level network driver interface.
About¶
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:
- sending and receiving of actual network data
- network device configuration through reading and setting device parameters
- 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 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.h::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 netdev.h::netdev_driver::send
function, passing it the data that should be sent. The caller of the netdev.h::netdev_driver::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:
- wait for a netdev.h::netdev_event_t::NETDEV_EVENT_RX_COMPLETE event
- call the
netdev.h::netdev_driver::recv
function withbuf := NULL
andlen := 0
to get the size of the received data - allocate a large enough buffer in some way
- call the
netdev.h::netdev_driver::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. *
Note
The netdev.h::netdev_driver::send
and netdev.h::netdev_driver::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 netdev.h::netdev_driver::get
and netdev.h::netdev_driver::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
.
Note
The netdev.h::netdev_driver::get
and netdev.h::netdev_driver::set
functions must never be called from interrupt context.
Events¶
-
enum
@143
¶ - NETDEV_TYPE_UNKNOWN
- NETDEV_TYPE_RAW
- NETDEV_TYPE_ETHERNET
- NETDEV_TYPE_IEEE802154
- NETDEV_TYPE_BLE
- NETDEV_TYPE_CC110X
- NETDEV_TYPE_LORA
- NETDEV_TYPE_NRFMIN
- NETDEV_TYPE_SLIP
- NETDEV_TYPE_ESP_NOW
-
enum
netdev_event_t
¶ - NETDEV_EVENT_ISR
- driver needs it’s ISR handled
- NETDEV_EVENT_RX_STARTED
- started to receive a packet
- NETDEV_EVENT_RX_COMPLETE
- finished receiving a packet
- NETDEV_EVENT_TX_STARTED
- started to transfer a packet
- NETDEV_EVENT_TX_COMPLETE
- transfer packet complete
- NETDEV_EVENT_TX_COMPLETE_DATA_PENDING
- transfer packet complete and data pending flag
- NETDEV_EVENT_TX_NOACK
- ACK requested but not received.
- NETDEV_EVENT_TX_MEDIUM_BUSY
- couldn’t transfer packet
- 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
-
void(*
netdev_event_cb_t
()¶ Event callback for signaling event to upper layers.
Parameters
type: type of the event
-
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).
-
int
netdev_get_notsup
(netdev.h::netdev_t
* dev,netopt.h::netopt_t
opt, void * value,msp430_types.h::size_t
max_len)¶ Convenience function for declaring get() as not supported in general.
Parameters
dev: ignored opt: ignored value: ignored max_len: ignored Return values
- always returns
-ENOTSUP
- always returns
-
int
netdev_set_notsup
(netdev.h::netdev_t
* dev,netopt.h::netopt_t
opt, const void * value,msp430_types.h::size_t
value_len)¶ Convenience function for declaring set() as not supported in general.
Parameters
dev: ignored opt: ignored value: ignored value_len: ignored Return values
- always returns
-ENOTSUP
- always returns
-
struct
netdev_radio_rx_info
¶ Received packet status information for most radios.
May be different for certain radios.
-
int16_t
rssi
¶ RSSI of a received packet in dBm.
-
uint8_t
lqi
¶ LQI of a received packet.
-
int16_t
-
struct
netdev
¶ Structure to hold driver state.
Supposed to be extended by driver implementations. The extended structure should contain all variable driver state.
Contains a field
context
which is not used by the drivers, but supposed to be used by upper layers to store reference information.-
const struct netdev_driver *
driver
¶ ptr to that driver’s interface.
-
netdev.h::netdev_event_cb_t
event_callback
¶ callback for device events
-
void *
context
¶ ptr to network stack context
-
const struct netdev_driver *
-
struct
netdev_driver
¶ 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).
-
int(*
send
()¶ Send frame.
Parameters
dev: Network device descriptor. Must not be NULL. iolist: io vector list to send Return values
- negative errno on error
- number of bytes sent
-
int(*
recv
()¶ Get a received frame.
Supposed to be called from
netdev.h::netdev::event_callback
If
buf
== NULL andlen
== 0, returns the packet size without dropping it. Ifbuf
== NULL andlen
> 0, drops the packet and returns the packet size.Parameters
dev: network device descriptor. Must not be NULL. buf: buffer to write into or NULL to return the packet size. len: maximum number of bytes to read. If buf
is NULL the currently buffered packet is dropped whenlen
> 0. Must not be 0 whenbuf
!= NULL.info: status information for the received packet. Might be of different type for different netdev devices. May be NULL if not needed or applicable. Return values
-ENOBUFS
if supplied buffer is too small- number of bytes read if buf != NULL
- packet size if buf == NULL
-
int(*
init
()¶ the driver’s initialization function
Parameters
dev: network device descriptor. Must not be NULL. Return values
< 0
on error- 0 on success
-
void(*
isr
()¶ a driver’s user-space ISR handler
This function will be called from a network stack’s loop when being notified by netdev_isr.
It is supposed to call
netdev.h::netdev::event_callback
for each occurring event.See receive packet flow description for details.
Parameters
dev: network device descriptor. Must not be NULL.
-
int(*
get
()¶ Get an option value from a given network device.
Parameters
dev: network device descriptor opt: option type value: pointer to store the option’s value in max_len: maximal amount of byte that fit into value
Return values
- number of bytes written to
value
-ENOTSUP
ifopt
is not provided by the device
- number of bytes written to
-
int(*
set
()¶ Set an option value for a given network device.
Parameters
dev: network device descriptor opt: option type value: value to set value_len: the length of value
Return values
- number of bytes written to
value
-ENOTSUP
ifopt
is not configurable for the device-EINVAL
ifvalue
is an invalid value with regards toopt
- number of bytes written to
-
int(*