If you need urgent consulting help click here
L2 Layer Management
Overview
The L2 stack is designed to hide the whole networking link-layer part
and the related device drivers from the upper network stack. This is made
through a net_if
declared in
include/zephyr/net/net_if.h.
The upper layers are unaware of implementation details beyond the net_if
object and the generic API provided by the L2 layer in
include/zephyr/net/net_l2.h as net_l2
.
Only the L2 layer can talk to the device driver, linked to the net_if object. The L2 layer dictates the API provided by the device driver, specific for that device, and optimized for working together.
Currently, there are L2 layers for Ethernet, IEEE 802.15.4 Soft-MAC, Bluetooth IPSP, CANBUS, OpenThread, Wi-Fi, and a dummy layer example that can be used as a template for writing a new one.
L2 layer API
In order to create an L2 layer, or a driver for a specific L2 layer, one needs to understand how the L3 layer interacts with it and how the L2 layer is supposed to behave. See also network stack architecture for more details. The generic L2 API has these functions:
recv()
: All device drivers, once they receive a packet which they put into anet_pkt
, will push this buffer to the network stack vianet_recv_data()
. At this point, the network stack does not know what to do with it. Instead, it passes the buffer along to the L2 stack’srecv()
function for handling. The L2 stack does what it needs to do with the packet, for example, parsing the link layer header, or handling link-layer only packets. Therecv()
function will returnNET_DROP
in case of an erroneous packet,NET_OK
if the packet was fully consumed by the L2, orNET_CONTINUE
if the network stack should then handle it.send()
: Similar to receive function, the network stack will call this function to actually send a network packet. All relevant link-layer content will be generated and added by this function. Thesend()
function returns the number of bytes sent, or a negative error code if there was a failure sending the network packet.enable()
: This function is used to enable/disable traffic over a network interface. The function returns<0
if error and>=0
if no error.get_flags()
: This function will return the capabilities of an L2 driver, for example whether the L2 supports multicast or promiscuous mode.
Network Device drivers
Network device drivers fully follows Zephyr device driver model as a basis. Please refer to Device Driver Model.
There are, however, two differences:
The driver_api pointer must point to a valid
net_if_api
pointer.The network device driver must use
NET_DEVICE_INIT_INSTANCE()
orETH_NET_DEVICE_INIT()
for Ethernet devices. These macros will call theDEVICE_DEFINE()
macro, and also instantiate a uniquenet_if
related to the created device driver instance.
Implementing a network device driver depends on the L2 stack it belongs to: Ethernet, IEEE 802.15.4, etc. In the next section, we will describe how a device driver should behave when receiving or sending a network packet. The rest is hardware dependent and is not detailed here.
Ethernet device driver
On reception, it is up to the device driver to fill-in the network packet with
as many data buffers as required. The network packet itself is a
net_pkt
and should be allocated through
net_pkt_rx_alloc_with_buffer()
. Then all data buffers will be
automatically allocated and filled by net_pkt_write()
.
After all the network data has been received, the device driver needs to
call net_recv_data()
. If that call fails, it will be up to the
device driver to unreference the buffer via net_pkt_unref()
.
On sending, the device driver send function will be called, and it is up to the device driver to send the network packet all at once, with all the buffers.
Each Ethernet device driver will need, in the end, to call
ETH_NET_DEVICE_INIT()
like this:
ETH_NET_DEVICE_INIT(..., CONFIG_ETH_INIT_PRIORITY,
&the_valid_net_if_api_instance, 1500);
IEEE 802.15.4 device driver
Device drivers for IEEE 802.15.4 L2 work basically the same as for
Ethernet. What has been described above, especially for recv()
, applies
here as well. There are two specific differences however:
It requires a dedicated device driver API:
ieee802154_radio_api
, which overloadsnet_if_api
. This is because 802.15.4 L2 needs more from the device driver than justsend()
andrecv()
functions. This dedicated API is declared in include/zephyr/net/ieee802154_radio.h. Each and every IEEE 802.15.4 device driver must provide a valid pointer on such relevantly filled-in API structure.Sending a packet is slightly different than in Ethernet. IEEE 802.15.4 sends relatively small frames, 127 bytes all inclusive: frame header, payload and frame checksum. Buffers are meant to fit such frame size limitation. But a buffer containing an IPv6/UDP packet might have more than one fragment. IEEE 802.15.4 drivers handle only one buffer at a time. This is why the
ieee802154_radio_api
requires a tx function pointer which differs from thenet_if_api
send function pointer. Instead, the IEEE 802.15.4 L2, provides a genericieee802154_radio_send()
meant to be given asnet_if
send function. It turn, the implementation ofieee802154_radio_send()
will ensure the same behavior: sending one buffer at a time throughieee802154_radio_api
tx function, and unreferencing the network packet only when all the transmission were successful.
Each IEEE 802.15.4 device driver, in the end, will need to call
NET_DEVICE_INIT_INSTANCE()
that way:
NET_DEVICE_INIT_INSTANCE(...,
the_device_init_prio,
&the_valid_ieee802154_radio_api_instance,
IEEE802154_L2,
NET_L2_GET_CTX_TYPE(IEEE802154_L2), 125);
API Reference
- group net_l2
Network Layer 2 abstraction layer.
Enums
-
struct net_l2
- #include <net_l2.h>
Network L2 structure.
Used to provide an interface to lower network stack.
Public Members
-
enum net_verdict (*recv)(struct net_if *iface, struct net_pkt *pkt)
This function is used by net core to get iface’s L2 layer parsing what’s relevant to itself.
-
int (*send)(struct net_if *iface, struct net_pkt *pkt)
This function is used by net core to push a packet to lower layer (interface’s L2), which in turn might work on the packet relevantly. (adding proper header etc…) Returns a negative error code, or the number of bytes sent otherwise.
-
int (*enable)(struct net_if *iface, bool state)
This function is used to enable/disable traffic over a network interface. The function returns <0 if error and >=0 if no error.
-
enum net_l2_flags (*get_flags)(struct net_if *iface)
Return L2 flags for the network interface.
-
enum net_verdict (*recv)(struct net_if *iface, struct net_pkt *pkt)
-
struct net_l2