155 lines
6.6 KiB
ReStructuredText
155 lines
6.6 KiB
ReStructuredText
.. _l2_and_drivers:
|
|
|
|
L2 Stack and Drivers
|
|
####################
|
|
|
|
The L2 stack is designed to hide the whole networking link-layer part
|
|
and the related device drivers from the higher IP stack. This is made
|
|
through a unique object known as the "network interface object":
|
|
:c:type:`struct net_if` declared in :file:`include/net/net_if.h`.
|
|
|
|
The IP layer is unaware of implementation details beyond the net_if
|
|
object and the generic API provided by the L2 layer in
|
|
:file:`include/net/net_l2.h` as :c:type:`struct 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, and a dummy one, which is a generic 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 even a driver for a specific L2
|
|
layer, one needs to understand how the IP layer interacts with it and
|
|
how the L2 layer is supposed to behave. The generic L2 API has 3
|
|
functions:
|
|
|
|
- recv: All device drivers, once they receive a packet which they put
|
|
into a :c:type:`struct net_pkt`, will push this buffer to the IP
|
|
core stack via :c:func:`net_recv_data()`. At this point, the IP core
|
|
stack does not know what to do with it. Instead, it passes the
|
|
buffer along to the L2 stack's recv() 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. The
|
|
recv() function will return NET_DROP in case of an erroneous packet,
|
|
NET_OK if the packet was fully consumed by the L2, or NET_CONTINUE
|
|
if the IP stack should then handle it as an IP packet.
|
|
|
|
- reserve: Prior to creating any network buffer content, the Zephyr
|
|
core stack needs to know how much dedicated buffer space is needed
|
|
for the L2 layer (for example, space for the link layer header). This
|
|
reserve function returns the number of bytes needed.
|
|
|
|
- send: Similar to recv, the IP core stack will call this function to
|
|
actually send a packet. All relevant link-layer content will be
|
|
generated and added by this function. As for recv, send returns a
|
|
verdict and can decide to drop the packet via NET_DROP if something
|
|
wrong happened, or will return NET_OK.
|
|
|
|
Network Device drivers
|
|
**********************
|
|
|
|
Network device drivers fully follows Zephyr device driver model as a
|
|
basis. Please refer to :ref:`device_drivers`.
|
|
|
|
There are, however, two differences:
|
|
|
|
- the driver_api pointer must point to a valid :c:type:`struct
|
|
net_if_api` pointer.
|
|
|
|
- The network device driver must use ``NET_DEVICE_INIT_INSTANCE()``. This
|
|
macro will call the DEVICE_AND_API_INIT() macro, and also
|
|
instantiate a unique :c:type:`struct net_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
|
|
packet. The rest is really hardware dependent and thus does not need
|
|
to be detailed here.
|
|
|
|
Ethernet device driver
|
|
======================
|
|
|
|
On reception, it is up to the device driver to fill-in the buffer with
|
|
as many data fragments as required. The buffer itself is a
|
|
:c:type:`struct net_pkt` and should be allocated through
|
|
:c:func:`net_pkt_get_reserve_rx(0)`. Then all fragments will be
|
|
allocated through :c:func:`net_pkt_get_reserve_data(0)`. Of course
|
|
the amount of required fragments depends on the size of the received
|
|
packet and on the size of a fragment, which is given by
|
|
:option:`CONFIG_NET_BUF_DATA_SIZE`.
|
|
|
|
Note that it is not up to the device driver to decide on the
|
|
link-layer space to be reserved in the buffer. Hence the 0 given as
|
|
parameter here. The Ethernet L2 layer will update such information
|
|
once the packet's Ethernet header has been successfully parsed.
|
|
|
|
In case :c:func:`net_recv_data()` call fails, it will be up to the
|
|
device driver to unreference the buffer via
|
|
:c:func:`net_pkt_unref()`.
|
|
|
|
On sending, it is up to the device driver to send the buffer all at
|
|
once, with all the fragments.
|
|
|
|
In case of a fully successful packet transmission only, the device
|
|
driver must unreference the buffer via :c:func:`net_pkt_unref()`.
|
|
|
|
Each Ethernet device driver will need, in the end, to call
|
|
``NET_DEVICE_INIT_INSTANCE()`` like this:
|
|
|
|
.. code-block:: c
|
|
|
|
NET_DEVICE_INIT_INSTANCE(...,
|
|
CONFIG_ETH_INIT_PRIORITY
|
|
&the_valid_net_if_api_instance,
|
|
ETHERNET_L2,
|
|
NET_L2_GET_CTX_TYPE(ETHERNET_L2), 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: :c:type:`struct
|
|
ieee802154_radio_api`, which overloads :c:type:`struct
|
|
net_if_api`. This is because 802.15.4 L2 needs more from the device
|
|
driver than just send and recv functions. This dedicated API is
|
|
declared in :file:`include/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 particular. IEEE 802.15.4 sends
|
|
relatively small frames, 127 bytes all inclusive: frame header,
|
|
payload and frame checksum. Buffer fragments are meant to fit such
|
|
frame size limitation. But a buffer containing an IPv6/UDP packet
|
|
might have more than one fragment. In the Ethernet device driver, it
|
|
is up to the driver to handle all fragments. IEEE 802.15.4 drivers
|
|
handle only one fragment at a time. This is why the :c:type:`struct
|
|
ieee802154_radio_api` requires a tx function pointer which differs
|
|
from the :c:type:`struct net_if_api` send function pointer.
|
|
Instead, the IEEE 802.15.4 L2, provides a generic
|
|
:c:func:`ieee802154_radio_send()` meant to be given as
|
|
:c:type:`struct net_if` send function. It turn, the implementation
|
|
of :c:func:`ieee802154_radio_send()` will ensure the same behavior:
|
|
sending one fragment at a time through :c:type:`struct
|
|
ieee802154_radio_api` tx function, and unreferencing the buffer
|
|
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:
|
|
|
|
.. code-block:: c
|
|
|
|
NET_DEVICE_INIT_INSTANCE(...,
|
|
the_device_init_prio,
|
|
&the_valid_ieee802154_radio_api_instance,
|
|
IEEE802154_L2,
|
|
NET_L2_GET_CTX_TYPE(IEEE802154_L2), 125);
|