# ESP-NETIF 开发者指南

[English]

如 ESP-NETIF 架构 所示，ESP-NETIF 实际上是 I/O 驱动和 TCP/IP 协议栈之间的中介。本指南用以说明如何自定义这二者，即如何实现 ESP-NETIF 自定义 I/O 驱动程序 和 ESP-NETIF 自定义 TCP/IP 协议栈。

# ESP-NETIF 自定义 I/O 驱动程序

本节概述了如何配置具有 ESP-NETIF 连接功能的新 I/O 驱动程序。

通常情况下，I/O 驱动程序须注册为 ESP-NETIF 驱动程序。因此，它依赖于 ESP-NETIF 组件，并负责提供数据路径函数、后附回调函数，并在多数情况下用于设置默认事件处理程序，根据驱动程序的生命周期转换来定义网络接口操作。

## 数据包 Input/Output

根据 ESP-NETIF 架构 章节提供的图表可以看出，须定义以下三个数据路径函数 API 以连接 ESP-NETIF：

- `esp_netif_transmit()`

- `esp_netif_free_rx_buffer()`

- `esp_netif_receive()`

前两个函数可以传输和释放 RX 缓冲区，用作回调。它们由 ESP-NETIF（及其底层 TCP/IP 堆栈）调用，并由 I/O 驱动实现。

另一方面，接收函数由 I/O 驱动程序调用，因此驱动的代码只需在接收到新数据时调用 `esp_netif_receive()` 函数。

## 后附回调

网络接口初始化的最后一步是调用以下 API，将 ESP-NETIF 实例附加到 I/O 驱动程序上：

    esp_err_t esp_netif_attach(esp_netif_t *esp_netif, esp_netif_iodriver_handle driver_handle);

假设 `esp_netif_iodriver_handle` 是指向驱动程序对象的指针，该对象是从 `struct esp_netif_driver_base_s` 衍生的结构体，那么 I/O 驱动结构体的第一个成员必须是此基础结构，并指向：

- 后附函数回调

- 相关的 ESP-NETIF 实例

因此，I/O 驱动程序须创建以下结构体的实例：

    typedef struct my_netif_driver_s {
            esp_netif_driver_base_t base;           /*!< 保留基本结构体作为 esp-netif 驱动 */
            driver_impl             *h;             /*!< 驱动实现 */
        } my_netif_driver_t;

此实例中包含 `my_netif_driver_t::base.post_attach` 的真实值和实际的驱动处理程序 `my_netif_driver_t::h`。

从初始化代码调用 `esp_netif_attach()` 时，将执行 I/O 驱动程序代码的后附回调，以在 ESP-NETIF 和 I/O 驱动程序实例之间相互注册回调。通常，后附回调中也会启动驱动程序。以下为一个简单的后附回调示例：

    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;
    }

## 默认处理程序

I/O 驱动程序通常还会根据 I/O 驱动程序的状态转换，为相关网络接口的生命周期行为提供默认定义，例如 driver start `->` network start 等。

以下是此类默认处理程序的一个示例：

    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;
    }

### 网络堆栈连接

用于传输和释放 RX 缓冲区的数据路径函数（在 I/O 驱动中定义）由 ESP-NETIF 的 TCP/IP 堆栈连接层调用。

注意，ESP-IDF 为最常见的网络接口（如 Wi-Fi station 或以太网）提供了几种网络堆栈配置。这些配置在 esp_netif/include/esp_netif_defaults.h 中定义，能够满足大多数网络驱动程序的需求。

有时可能需要定义一个基于 lwIP 的自定义接口，例如当您需要使用特定的数据包池更新 esp_netif/lwip/netif/wlanif.c 文件时。在这种情况下，您需要显式定义对 lwIP 的依赖关系，并包含 esp_netif/include/lwip/esp_netif_net_stack.h 文件以获取相关的 lwIP 配置结构体。

### ESP-NETIF 自定义 TCP/IP 协议栈

若实现了 BSD API，则可以借助 ESP-IDF 使用自定义的 TCP/IP 协议栈。除了使用一般性的 ESP-NETIF 功能，还可以支持自定义的 TCP/IP 协议栈，这样应用代码就能和使用 lwIP 时保持一致。

要想实现这一点，请在 ESP-NETIF 组件配置菜单中选择 `ESP_NETIF_PROVIDE_CUSTOM_IMPLEMENTATION`。此选项将禁用 ESP-NETIF 功能的 lwIP 实现，只提供带有数据声明和 API 声明的头文件，因此要在自定义组件中提供必要的实现。有关以上功能的 dummy 实现示例，请参考 esp_netif/loopback/esp_netif_loopback.c。

也可以不借助 lwIP 构建 ESP-IDF，请参考 components/esp_netif_stack/README.md。

### API 参考

以下参考 API 概述了这些网络堆栈和 ESP-NETIF 的交互：

## Header File

- components/esp_netif/include/esp_netif_net_stack.h

- This header file can be included with:

  >     #include "esp_netif_net_stack.h"

- This header file is a part of the API provided by the `esp_netif` component. To declare that your component depends on `esp_netif`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_netif

  > or

  >     PRIV_REQUIRES esp_netif

## Functions

esp_netif_t *esp_netif_get_handle_from_netif_impl(void *dev)

  Returns esp-netif handle.

  参数

    dev -- [in] opaque ptr to network interface of specific TCP/IP stack

  返回

    handle to related esp-netif instance

void *esp_netif_get_netif_impl(esp_netif_t *esp_netif)

  Returns network stack specific implementation handle.

  参数

    esp_netif -- [in] Handle to esp-netif instance

  返回

    handle to related network stack netif handle

esp_err_t esp_netif_set_link_speed(esp_netif_t *esp_netif, uint32_t speed)

  Set link-speed for the specified network interface.

  参数

    - esp_netif -- [in] Handle to esp-netif instance

    - speed -- [in] Link speed in bit/s

  返回

    ESP_OK on success

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.

  参数

    - esp_netif -- [in] Handle to esp-netif instance

    - data -- [in] Data to be transmitted

    - len -- [in] Length of the data frame

  返回

    ESP_OK on success, an error passed from the I/O driver otherwise

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.

  参数

    - esp_netif -- [in] Handle to esp-netif instance

    - data -- [in] Data to be transmitted

    - len -- [in] Length of the data frame

    - netstack_buf -- [in] net stack buffer

  返回

    ESP_OK on success, an error passed from the I/O driver otherwise

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)

  参数

    - esp_netif -- [in] Handle to esp-netif instance

    - buffer -- [in] Rx buffer pointer

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。