Zephyr ESP32 蓝牙驱动简析

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

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

本文主要分析esp32的蓝牙驱动如何被集成进Zephyr的驱动,并不涉及esp32 蓝牙驱动本身API的说明。

架构

Zephyr的esp32蓝牙驱动架构非常清晰简单,就是Zephyr调用esp32 VHCI接口基于esp32提供的VHCI进行蓝牙数据通信,和esp32自身采用的Bluedroid->VHCI架构一样,只是在Zephyr上VHCI上是自己的蓝牙协议栈。如下图,左边是esp-idf内的价格,右边是zephyr的架构,颜色相同的部分等同,更多请见文末参考

实现

Zephyr的蓝牙驱动也可以分为初始化,收,发来分析

初始化

SYS_INIT注册的初始化函数bt_esp32_init将在系统启动的POST_KERNEL阶段被调用,在bt_esp32_init中使用bt_hci_driver_register将bt_hci_driver注册入host,蓝牙在APPLICATION阶段被初始化,会调用bt_esp32_open

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
static const struct bt_hci_driver drv = {
.name = "BT ESP32",
.open = bt_esp32_open,
.send = bt_esp32_send,
.bus = BT_HCI_DRIVER_BUS_IPM,
#if defined(CONFIG_BT_DRIVER_QUIRK_NO_AUTO_DLE)
.quirks = BT_QUIRK_NO_AUTO_DLE,
#endif
};

static int bt_esp32_init(const struct device *unused)
{
ARG_UNUSED(unused);
//注册hci driver
bt_hci_driver_register(&drv);

return 0;
}

//注册驱动初始化函数
SYS_INIT(bt_esp32_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);

//APPLICATION阶段被调用
static int bt_esp32_open(void)
{
int err;

err = bt_esp32_ble_init();
if (err) {
return err;
}

BT_DBG("ESP32 BT started");

return 0;
}

static int bt_esp32_ble_init(void)
{
int ret;

///调用esp的蓝牙驱动初始化
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();

#ifdef CONFIG_BT_BREDR
esp_bt_mode_t mode = ESP_BT_MODE_BTDM;
#else
esp_bt_mode_t mode = ESP_BT_MODE_BLE;
#endif

ret = esp_bt_controller_init(&bt_cfg);
if (ret) {
BT_ERR("Bluetooth controller init failed %d", ret);
return ret;
}

ret = esp_bt_controller_enable(mode);
if (ret) {
BT_ERR("Bluetooth controller enable failed: %d", ret);
return ret;
}

//注册接收数据callback
esp_vhci_host_register_callback(&vhci_host_cb);

return 0;

接收

接收数据是依靠注册的callback

1
2
3
4
static esp_vhci_host_callback_t vhci_host_cb = {
hci_esp_controller_rcv_pkt_ready,
hci_esp_host_rcv_pkt
};

当数据接收完成后调用hci_esp_controller_rcv_pkt_ready,通知没有在接收数据, 之后才能发送数据

1
2
3
4
5
static void hci_esp_controller_rcv_pkt_ready(void)
{
//通过sem通知接收已经完成,发送可以进行
k_sem_give(&hci_send_sem);
}

当esp32蓝牙驱动收到数据后调用hci_esp_host_rcv_pkt,进行数据接收处理

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
37
static int hci_esp_host_rcv_pkt(uint8_t *data, uint16_t len)
{
uint8_t pkt_indicator;
struct net_buf *buf = NULL;
size_t remaining = len;

BT_HEXDUMP_DBG(data, len, "host packet data:");

pkt_indicator = *data++;
remaining -= sizeof(pkt_indicator);
//分类别处理数据
switch (pkt_indicator) {
case HCI_EVT:
buf = bt_esp_evt_recv(data, remaining);
break;

case HCI_ACL:
buf = bt_esp_acl_recv(data, remaining);
break;

case HCI_SCO:
buf = bt_esp_iso_recv(data, remaining);
break;

default:
BT_ERR("Unknown HCI type %u", pkt_indicator);
return -1;
}

if (buf) {
BT_DBG("Calling bt_recv(%p)", buf);
//将数据送到host
bt_recv(buf);
}

return 0;
}

发送

Zephyr蓝牙Host内通过下列流程调用到bt_hci_driver_register提供的send函数bt_esp32_send

1
2
3
4
5
6
7
8
9
10
11
12
int bt_send(struct net_buf *buf)
{
BT_DBG("buf %p len %u type %u", buf, buf->len, bt_buf_get_type(buf));

bt_monitor_send(bt_monitor_opcode(buf), buf->data, buf->len);

if (IS_ENABLED(CONFIG_BT_TINYCRYPT_ECC)) {
return bt_hci_ecc_send(buf);
}

return bt_dev.drv->send(buf);
}

bt_esp32_send使用ESP32提供的VHCI将数据发送出去

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
37
38
39
40
41
42
43
44
45
static int bt_esp32_send(struct net_buf *buf)
{
int err = 0;
uint8_t pkt_indicator;

BT_DBG("buf %p type %u len %u", buf, bt_buf_get_type(buf), buf->len);
//转换类型识别
switch (bt_buf_get_type(buf)) {
case BT_BUF_ACL_OUT:
pkt_indicator = HCI_ACL;
break;
case BT_BUF_CMD:
pkt_indicator = HCI_CMD;
break;
case BT_BUF_ISO_OUT:
pkt_indicator = HCI_ISO;
break;
default:
BT_ERR("Unknown type %u", bt_buf_get_type(buf));
goto done;
}
net_buf_push_u8(buf, pkt_indicator);

BT_HEXDUMP_DBG(buf->data, buf->len, "Final HCI buffer:");

//检查vhci是否可以发送
if (!esp_vhci_host_check_send_available()) {
BT_WARN("Controller not ready to receive packets");
}

//等待接收完成,才能发送
if (k_sem_take(&hci_send_sem, HCI_BT_ESP32_TIMEOUT) == 0) {
//使用vhci进行发送
esp_vhci_host_send_packet(buf->data, buf->len);
} else {
BT_ERR("Send packet timeout error");
err = -ETIMEDOUT;
}

done:
net_buf_unref(buf);
k_sem_give(&hci_send_sem);

return err;
}

参考

https://www.espressif.com/sites/default/files/documentation/esp32_bluetooth_architecture_cn.pdf
https://docs.zephyrproject.org/latest/guides/bluetooth/bluetooth-arch.html