By convention, the I/O driver has to register itself as an esp-netif driver, and thus holds a dependency on esp-netif component and is responsible for providing data path functions, post-attach callback and in most cases, also default event handlers to define network interface actions based on driver's lifecycle transitions.
The first two functions for transmitting and freeing the rx buffer are provided as callbacks, i.e., they get called from esp-netif (and its underlying TCP/IP stack) and I/O driver provides their implementation.
The receiving function on the other hand gets called from the I/O driver, so that the driver's code simply calls :cpp:func:`esp_netif_receive()` on a new data received event.
A final part of the network interface initialization consists of attaching the esp-netif instance to the I/O driver, by means of calling the following API:
It is assumed that the ``esp_netif_iodriver_handle`` is a pointer to driver's object, a struct derived from ``struct esp_netif_driver_base_s``, so that the first member of I/O driver structure must be this base structure with pointers to
So when the :cpp:func:`esp_netif_attach()` gets called from the initialization code, the post-attach callback from I/O driver's code gets executed to mutually register callbacks between esp-netif and I/O driver instances. Typically the driver is started as well in the post-attach callback. An example of a simple post-attach callback is outlined below:
I/O drivers also typically provide default definitions of lifecycle behaviour of related network interfaces based on state transitions of I/O drivers. For example *driver start*``->``*network start*, etc.
The packet data path functions for transmitting and freeing the rx buffer (defined in the I/O driver) are called from the esp-netif, specifically from its TCP/IP stack connecting layer.
Note, that ESP-IDF provides several network stack configurations for the most common network interfaces, such as for the WiFi station or Ethernet.
These configurations are defined in :component_file:`esp_netif/include/esp_netif_defaults.h` and should be sufficient for most network drivers. (In rare cases, expert users might want to define custom lwIP based interface layers; it is possible, but an explicit dependency to lwIP needs to be set)