.. _net_l2_interface: L2 Layer Management ################### Overview ******** 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); API Reference ************* .. doxygengroup:: net_l2 :project: Zephyr