Zephyr ESP32 wifi驱动简析

本文简要分析zephyr上esp32的wifi驱动实现。

分析Zephyr ESP32 WIFI驱动的实现可以更为清晰的掌握esp32 wifi在zephyr上的使用,本文主要分析esp32的wifi驱动如何被集成进Zephyr的驱动,并不涉及esp32 wifi驱动本身API的说明。

框架 链接到标题

目前ESP32 wifi在zephyr上的实现框架如下图 1 esp_private: esp提供的wifi驱动,不开源,属于zephyr的外部module,其API头文件在modules\hal\espressif\components\esp_wifi\include\esp_private内

2 adapter esp提供的zephyr wifi适配层,对esp_private进行封装专门为zephyr用,属于zephyr的外部module,其代码放在modules\hal\espressif\zephyr\adapter\src\wifi

3 esp_wifi_drv: Zephyr中的esp32 wifi驱动,调用adapter,和L2 ethernet进行对接。明明是wifi,不封装为L2 wifi, 而封装为L2 ethernet,这可能是目前zephyr对L2 wifi的抽象还不完备,目前只支持offload wifi。 这部分是后文的主要分析内容,代码在zephyr\drivers\wifi\esp32\src

  1. L2 ethernet Zephyr L2 ethernet,提供ethernet初始化/配置/收发功能, 代码在zephyr\subsys\net\l2\ethernet,本文不做分析

esp_wifi_drv 链接到标题

zephyr的esp32 wifi驱动可以分为初始化,收,发三部分来分析:

初始化 链接到标题

主要是完成L2的初始化,注册入device初始化函数eth_esp32_dev_init和iface的初始化函数eth_esp32_init已经L2的发送函数eth_esp32_send

static const struct ethernet_api eth_esp32_apis = {
	.iface_api.init	= eth_esp32_init,
	.send =  eth_esp32_send,
};

NET_DEVICE_DT_INST_DEFINE(0,
		eth_esp32_dev_init, NULL,
		&eth_data, NULL, CONFIG_ETH_INIT_PRIORITY,
		&eth_esp32_apis, ETHERNET_L2,
		NET_L2_GET_CTX_TYPE(ETHERNET_L2), NET_ETH_MTU);

使用NET_DEVICE_DT_INST_DEFINE注册后,在系统启动时kernel的POST_KERNEL阶段调用eth_esp32_dev_init,在net初始化阶段调用eth_esp32_init.

eth_esp32_dev_init代码如下,主要是调用hal中提供的一系列初始化和启动函数,让wifi启动,值得注意的是当CONFIG_ESP32_WIFI_STA_AUTO=y时,zephyr驱动会自动去帮你用配置好的CONFIG_ESP32_WIFI_SSID和CONFIG_ESP32_WIFI_PASSWORD去连接Wifi。如果没有配置,就需要在应用代码中直接调用esp hal的API进行连接,另外就是zephyr目前并没有将esp32 wifi的scan/connect/disconnect做到L2 WIFI内进行管理,可以参考Zephyr网络管理模块分析-注册请求机制, 这边部分也需要在应用中直接调用esp hal的API进行管理。

static int eth_esp32_dev_init(const struct device *dev)
{
	esp_timer_init();
	esp_event_init();

	wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT();
	esp_err_t ret = esp_wifi_init(&config);

	ret |= esp_supplicant_init();
	ret |= esp_wifi_start();

    //安装配置进行WIFI连接
	if (IS_ENABLED(CONFIG_ESP32_WIFI_STA_AUTO)) {
		wifi_config_t wifi_config = {
			.sta = {
				.ssid = CONFIG_ESP32_WIFI_SSID,
				.password = CONFIG_ESP32_WIFI_PASSWORD,
			},
		};

		ret = esp_wifi_set_mode(WIFI_MODE_STA);
		ret |= esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
		ret |= esp_wifi_connect();
	}

	if (ret != ESP_OK) {
		LOG_ERR("Connect failed");
	}

	return ret;
}

网络初始化, 完成ethernet iface注册,并注册数据接收callback,

static void eth_esp32_init(struct net_if *iface)
{
	const struct device *dev = net_if_get_device(iface);
	struct esp32_wifi_runtime *dev_data = DEV_DATA(dev);

	dev_data->iface = iface;
	esp32_wifi_iface = iface;

    //从ESP32读出MAC地址,设置给zephyr的iface
	/* Start interface when we are actually connected with WiFi network */
	net_if_flag_set(iface, NET_IF_NO_AUTO_START);
	esp_read_mac(dev_data->mac_addr, ESP_MAC_WIFI_STA);

	/* Assign link local address. */
	net_if_set_link_addr(iface,
			dev_data->mac_addr, 6, NET_LINK_ETHERNET);

    //进行ethernet初始化
	ethernet_init(iface);

    //注册接收数据的callback,当hal esp32 wifi驱动收到网络封包后会调用eth_esp32_rx
	esp_wifi_internal_reg_rxcb(ESP_IF_WIFI_STA, eth_esp32_rx);
}

数据接收 链接到标题

前面的代码可以看到注册的callback是eth_esp32_rx,hal esp32 wifi驱动收到网络封包后会调用eth_esp32_rx,eth_esp32_rx会将网络封包直接转发给IP层

static esp_err_t eth_esp32_rx(void *buffer, uint16_t len, void *eb)
{
	struct net_pkt *pkt;

	if (esp32_wifi_iface == NULL) {
		LOG_ERR("network interface unavailable");
		return ESP_FAIL;
	}
    //为封包分配内存
	pkt = net_pkt_rx_alloc_with_buffer(esp32_wifi_iface, len,
			AF_UNSPEC, 0, K_NO_WAIT);
	if (!pkt) {
		LOG_ERR("Failed to get net buffer");
		return ESP_FAIL;
	}

    //将封包数据从驱动搬运到pkt内
	if (net_pkt_write(pkt, buffer, len) < 0) {
		LOG_ERR("Failed to write pkt");
		goto pkt_unref;
	}

    //将封包抓发给IP层
	if (net_recv_data(esp32_wifi_iface, pkt) < 0) {
		LOG_ERR("Failed to push received data");
		goto pkt_unref;
	}

    //通知esp驱动封包数据已经使用完
	esp_wifi_internal_free_rx_buffer(eb);
	return ESP_OK;

pkt_unref:
	net_pkt_unref(pkt);
	return ESP_FAIL;
}

数据发送 链接到标题

数据发送的API在初始化时将eth_esp32_send注册进ethernet_api的send, IP层在呼叫L2的send时会找到ethernet_send进行发送,ethernet_send调用就是eth_esp32_send

static int ethernet_send(struct net_if *iface, struct net_pkt *pkt)
{
    ...
    //这里api->send就是注册的eth_esp32_send
    ret = net_l2_send(api->send, net_if_get_device(iface), iface, pkt);
    ...
}

static inline int net_l2_send(net_l2_send_t send_fn,
			      const struct device *dev,
			      struct net_if *iface,
			      struct net_pkt *pkt)
{
	net_capture_pkt(iface, pkt);

	return send_fn(dev, pkt);
}

static int eth_esp32_send(const struct device *dev, struct net_pkt *pkt)
{
	const int pkt_len = net_pkt_get_len(pkt);

    //找到frame
	/* Read the packet payload */
	if (net_pkt_read(pkt, DEV_DATA(dev)->frame_buf, pkt_len) < 0) {
		return -EIO;
	}

    //使用hal esp32 wifi进行发送
	/* Enqueue packet for transmission */
	esp_wifi_internal_tx(ESP_IF_WIFI_STA, (void *)DEV_DATA(dev)->frame_buf, pkt_len);

	LOG_DBG("pkt sent %p len %d", pkt, pkt_len);

	return 0;
}

待确认 链接到标题

Wifi的帧结构是802.11, 其帧结构和ethernet不一样,现在直接将hal esp32 wifi和zephyr ethernet对接,应该是esp做了相应的转换,具体如何,待确认。