4. 蓝牙协议栈剖析:Host层、Controller层、HCI接口、关键协议(GAP、GATT、L2CAP、SM)
做蓝牙网关优化,你得先把协议栈吃透。很多人一上来就调参数、改代码,结果越改越糟。为什么?因为不知道瓶颈到底在哪一层。
蓝牙协议栈分两层:Controller 和 Host。中间有个 HCI 接口连着。我习惯把 Controller 比作「手脚」,Host 比作「大脑」。手脚负责干粗活,大脑负责想事情。
核心观点:性能优化的本质,就是让「手脚」和「大脑」配合得更默契,减少等待和空转。
4.1 整体架构:一张图看懂
先看这张图,把各层的关系理清楚。后面讲细节时你就能找到位置。
4.2 Controller 层:干粗活的
Controller 负责最底层的射频操作。它处理跳频、数据包收发、重传这些事。说白了,它只管把比特流发出去,或者收回来。
Controller 的核心是 Link Layer (LL)。LL 管理连接状态机:待机、广播、扫描、发起、已连接。每个状态切换都有严格的时序要求。
我的经验:有一次网关吞吐量上不去,查了半天发现是 LL 的 connection interval 设得太小。Controller 频繁切换上下文,反而浪费了带宽。后来调到 30ms,吞吐量直接翻倍。
Controller 还有个关键参数叫 TX Power。功率太大,干扰别人也耗电;功率太小,丢包率飙升。我建议在网关场景下,先设到 0 dBm,再根据实际覆盖范围微调。
4.3 Host 层:动脑子的
Host 层跑在主 CPU 上,处理逻辑和控制。它不直接碰射频,而是通过 HCI 给 Controller 下命令。
Host 包含四个关键协议,我一个个说。
4.3.1 GAP:管连接的
GAP (Generic Access Profile) 控制设备的角色和行为。你的网关是广播者?观察者?还是中心设备?都由 GAP 决定。
GAP 定义了四种角色:
- Broadcaster:只发广播,不连。适合信标。
- Observer:只收广播,不连。适合扫描器。
- Peripheral:可被连接。适合传感器。
- Central:主动连接。适合网关。
网关通常是 Central 角色。但要注意,Central 同时管理多个连接时,GAP 的扫描窗口和间隔直接影响发现速度。
避坑指南:我曾经把扫描窗口设得太大,结果 Controller 一直在收广播包,没时间处理已连接设备的数据。后来把扫描窗口控制在 30ms,间隔 50ms,才平衡了发现和吞吐。
4.3.2 GATT:管数据的
GATT (Generic Attribute Profile) 定义了数据怎么组织和交换。它用 Service 和 Characteristic 来描述数据。
GATT 有两种角色:
- Server:提供数据。比如传感器上报温度。
- Client:读取或写入数据。比如网关读取温度。
网关通常是 GATT Client。但有些场景下,网关也需要作为 Server,比如对外提供配置接口。
GATT 的操作有四种:Read、Write、Notify、Indicate。其中 Notify 最常用,也最容易出性能问题。
关键点:Notify 不需要应用层确认,速度快。但 Controller 层有流控,如果 Host 下发太快,数据会积压在 HCI 缓冲区。我建议在网关代码里加一个发送队列,配合 HCI 的 Number of Completed Packets 事件做流控。
4.3.3 L2CAP:管分片的
L2CAP (Logical Link Control and Adaptation Protocol) 负责把上层数据分片成适合 Controller 发送的小包,也负责把收到的分片重组回去。
L2CAP 的 MTU (Maximum Transmission Unit) 很关键。默认是 23 字节,但可以协商到更大。我见过很多网关没做 MTU 协商,一直用 23 字节,吞吐量自然上不去。
| MTU 大小 | 有效载荷 | 吞吐量(理论) | 适用场景 |
|---|---|---|---|
| 23 字节 | 20 字节 | ~10 KB/s | 传感器小数据 |
| 247 字节 | 244 字节 | ~120 KB/s | OTA 升级 |
| 512 字节 | 509 字节 | ~250 KB/s | 音频流 |
我的建议:网关启动后,第一时间和每个设备协商 MTU。设到 247 字节,性能提升立竿见影。但别设太大,否则 Controller 缓冲区容易溢出。
4.3.4 SM:管安全的
SM (Security Manager) 负责配对、加密和密钥分发。网关场景下,安全很重要,但也不能拖慢性能。
SM 有三种配对方式:
- Just Works:无用户交互,安全性最低,但最快。
- Passkey Entry:需要输入 PIN 码,安全性中等。
- Out of Band (OOB):通过其他通道交换密钥,安全性最高。
网关通常用 Just Works,因为不需要人机交互。但要注意,每次配对都会消耗几十毫秒。如果设备频繁重连,可以考虑保存长期密钥 (LTK),避免重复配对。
避坑指南:我曾经遇到一个坑:网关和设备配对后,每次重连都要重新加密。查了协议栈日志才发现,是 SM 的密钥分发没做完。后来在配对完成后主动触发一次密钥分发,问题解决。
4.4 HCI 接口:连接大脑和手脚的桥梁
HCI (Host Controller Interface) 是 Host 和 Controller 之间的通信协议。它定义了命令、事件和数据的格式。
HCI 的通信模式是:Host 发命令,Controller 回复事件。数据则通过 ACL (Asynchronous Connection-Oriented) 通道传输。
HCI 的性能瓶颈通常出现在两个地方:
- 命令发送频率:Host 不能连续发太多命令,否则 Controller 处理不过来。
- 数据缓冲区:Controller 的 ACL 缓冲区有限,Host 发太快会触发流控。
优化技巧:我习惯在 Host 侧维护一个 HCI 命令队列,每次发完命令后等待 Command Complete 或 Command Status 事件再发下一条。数据发送则根据 Number of Completed Packets 事件动态调整。
4.5 各层之间的协作流程
看一个实际例子:网关收到传感器数据。
- Controller 层:PHY 收到射频信号,LL 解包、校验、去重。
- HCI 接口:Controller 把数据通过 HCI ACL 包发给 Host。
- Host 层:L2CAP 重组分片,GATT 解析 Attribute,应用层拿到数据。
反过来,网关下发命令:
- Host 层:应用层构造数据,GATT 封装成 Attribute,L2CAP 分片。
- HCI 接口:Host 把 ACL 包发给 Controller。
- Controller 层:LL 封装成链路层包,PHY 发射出去。
每个环节都可能成为瓶颈。你想想看,如果 L2CAP 分片太大,HCI 缓冲区会满;如果 LL 的 connection interval 太长,数据延迟会高。
我的习惯:做性能优化时,我会在每一层加一个计数器。Host 侧统计 GATT 读写次数,HCI 侧统计 ACL 包数量,Controller 侧统计 LL 包数量。哪个计数器异常,就重点查哪一层。
4.6 本章小结
蓝牙协议栈的分层设计,让各层各司其职。Controller 管射频,Host 管逻辑,HCI 管通信。优化时,你得清楚每个层的职责和限制。
记住几个关键点:
- GAP 控制连接行为,扫描参数影响发现速度。
- GATT 控制数据交换,Notify 配合流控才能发挥性能。
- L2CAP 的 MTU 协商是提升吞吐量的捷径。
- SM 的安全策略要平衡性能和安全性。
- HCI 的流控机制必须遵守,否则数据会丢。