Zephyr ESP32 wifi驱动简析

Creative Commons
本作品采用知识共享署名

本文简要分析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

1
2
3
4
5
6
7
8
9
10
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进行管理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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做了相应的转换,具体如何,待确认。