ESP-NETIF Custom I/O Driver¶
This section outlines implementing a new I/O driver with esp-netif connection capabilities. 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.
Packet input/output¶
As shown in the diagram, the following three API functions for the packet data path must be defined for connecting with esp-netif:
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 esp_netif_receive()
on a new data received event.
Post attach callback¶
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:
esp_err_t esp_netif_attach(esp_netif_t *esp_netif, esp_netif_iodriver_handle driver_handle);
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
- post-attach function callback 
- related esp-netif instance 
As a consequence the I/O driver has to create an instance of the struct per below:
typedef struct my_netif_driver_s {
        esp_netif_driver_base_t base;           /*!< base structure reserved as esp-netif driver */
        driver_impl             *h;             /*!< handle of driver implementation */
    } my_netif_driver_t;
with actual values of my_netif_driver_t::base.post_attach and the actual drivers handle my_netif_driver_t::h.
So when the 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:
static esp_err_t my_post_attach_start(esp_netif_t * esp_netif, void * args)
{
    my_netif_driver_t *driver = args;
    const esp_netif_driver_ifconfig_t driver_ifconfig = {
            .driver_free_rx_buffer = my_free_rx_buf,
            .transmit = my_transmit,
            .handle = driver->driver_impl
    };
    driver->base.netif = esp_netif;
    ESP_ERROR_CHECK(esp_netif_set_driver_config(esp_netif, &driver_ifconfig));
    my_driver_start(driver->driver_impl);
    return ESP_OK;
}
Default handlers¶
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.
An example of such a default handler is provided below:
esp_err_t my_driver_netif_set_default_handlers(my_netif_driver_t *driver, esp_netif_t * esp_netif)
{
    driver_set_event_handler(driver->driver_impl, esp_netif_action_start, MY_DRV_EVENT_START, esp_netif);
    driver_set_event_handler(driver->driver_impl, esp_netif_action_stop, MY_DRV_EVENT_STOP, esp_netif);
    return ESP_OK;
}
Network stack connection¶
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. The following API reference outlines these network stack interaction with the esp-netif.
Functions¶
- 
esp_netif_t *esp_netif_get_handle_from_netif_impl(void *dev)¶
- Returns esp-netif handle. - Return
- handle to related esp-netif instance 
- Parameters
- [in] dev: opaque ptr to network interface of specific TCP/IP stack
 
 
- 
void *esp_netif_get_netif_impl(esp_netif_t *esp_netif)¶
- Returns network stack specific implementation handle (if supported) - Note that it is not supported to acquire PPP netif impl pointer and this function will return NULL for esp_netif instances configured to PPP mode - Return
- handle to related network stack netif handle 
- Parameters
- [in] esp_netif: Handle to esp-netif instance
 
 
- 
esp_err_t esp_netif_transmit(esp_netif_t *esp_netif, void *data, size_t len)¶
- Outputs packets from the TCP/IP stack to the media to be transmitted. - This function gets called from network stack to output packets to IO driver. - Return
- ESP_OK on success, an error passed from the I/O driver otherwise 
- Parameters
- [in] esp_netif: Handle to esp-netif instance
- [in] data: Data to be transmitted
- [in] len: Length of the data frame
 
 
- 
esp_err_t esp_netif_transmit_wrap(esp_netif_t *esp_netif, void *data, size_t len, void *netstack_buf)¶
- Outputs packets from the TCP/IP stack to the media to be transmitted. - This function gets called from network stack to output packets to IO driver. - Return
- ESP_OK on success, an error passed from the I/O driver otherwise 
- Parameters
- [in] esp_netif: Handle to esp-netif instance
- [in] data: Data to be transmitted
- [in] len: Length of the data frame
- [in] netstack_buf: net stack buffer
 
 
- 
void esp_netif_free_rx_buffer(void *esp_netif, void *buffer)¶
- Free the rx buffer allocated by the media driver. - This function gets called from network stack when the rx buffer to be freed in IO driver context, i.e. to deallocate a buffer owned by io driver (when data packets were passed to higher levels to avoid copying) - Parameters
- [in] esp_netif: Handle to esp-netif instance
- [in] buffer: Rx buffer pointer