4. HCI协议详解
HCI(Host Controller Interface)是蓝牙协议栈中主机(Host)与控制器(Controller)之间的通信桥梁。理解 HCI 数据包的结构是进行蓝牙驱动调试和日志分析的基础。本章将深入剖析 HCI 的四种核心数据包类型及其格式。
4.1 HCI Packet 类型概览
HCI 定义了四种不同的数据包类型,每种类型通过特定的 Packet Indicator 字节进行区分。主机通过 UART、USB 或 SDIO 等物理总线发送这些数据包。
| Packet 类型 | 指示符 (Byte) | 方向 | 用途 |
|---|---|---|---|
| HCI Command | 0x01 |
Host → Controller | 主机下发指令,如扫描、连接、配置参数 |
| HCI ACL Data | 0x02 |
双向 | 承载面向连接的异步数据(如 A2DP 音频、ATT 协议) |
| HCI SCO Data | 0x03 |
双向 | 承载同步面向连接数据(如传统语音通话) |
| HCI Event | 0x04 |
Controller → Host | 控制器向主机报告事件,如命令完成、连接建立、断开 |
调试要点:在 HCI 日志(如 btsnoop、hcidump)中,第一个字节就是 Packet Indicator。例如,看到
01 开头,立即知道这是一个 Command 包。
4.2 HCI Command 格式详解
HCI Command 是主机驱动控制器行为的核心手段。每个 Command 包由 OpCode、Parameter Total Length 和 Parameters 三部分组成。
+--------+--------+--------+--------+--------+--------+--------+
| 0x01 | OpCode (2B) | Length (1B) | Parameters (0-255B) |
+--------+--------+--------+--------+--------+--------+--------+
| OpCode Group | OpCode Cmd | |
| (OGF) | (OCF) | |
+--------------+-------------+ +
- OpCode (2 字节):由高 6 位的 OGF(OpCode Group Field)和低 10 位的 OCF(OpCode Command Field)组成。OGF 用于分组命令类型(如链路控制、控制器配置),OCF 标识具体命令。
- Parameter Total Length (1 字节):指示 Parameters 字段的字节数,不包含 OpCode 和 Length 本身。
- Parameters (可变):命令特有的参数,长度由 Length 字段指定。
示例:HCI_Reset 命令的 OpCode 为
0x0C03(OGF=0x03, OCF=0x0003),无参数,因此 Length=0。日志中表现为:01 03 0C 00。
4.3 HCI Event 格式详解
HCI Event 是控制器向主机异步报告状态变化的机制。每个 Event 包由 Event Code、Parameter Total Length 和 Parameters 组成。
+--------+--------+--------+--------+--------+--------+--------+
| 0x04 | Event Code(1B)| Length (1B) | Parameters (0-255B) |
+--------+--------+--------+--------+--------+--------+--------+
- Event Code (1 字节):标识事件类型。例如
0x0E表示 Command Complete,0x0F表示 Command Status。 - Parameter Total Length (1 字节):Parameters 字段的字节数。
- Parameters (可变):事件特有的数据,如命令返回的 Status、连接句柄等。
常见陷阱:Command Complete 事件(0x0E)的 Parameters 中第一个字节是 Num_HCI_Command_Packets,表示控制器还能接收多少个 Command。如果该值为 0,主机必须等待,不能继续下发命令。
4.4 HCI ACL 与 SCO 数据包格式
ACL 和 SCO 数据包用于传输实际的应用数据。它们的头部结构相似,但用途不同。
4.4.1 HCI ACL Data Packet
+--------+--------+--------+--------+--------+--------+--------+
| 0x02 | Handle (2B) | PB Flag | BC Flag | Data Total Length |
+--------+--------+--------+--------+--------+--------+--------+
| Connection Handle (12b) | Flags (4b) | Length (2B) |
+-------------------------+------------+---------------+
- Connection Handle (12 位):标识该数据包属于哪个 ACL 连接。
- PB Flag (2 位):Packet Boundary Flag,用于分段重组。例如
10表示数据包起始,01表示中间或末尾片段。 - BC Flag (2 位):Broadcast Flag,用于广播包。
- Data Total Length (2 字节):后续数据的长度。
4.4.2 HCI SCO Data Packet
+--------+--------+--------+--------+--------+--------+--------+
| 0x03 | Handle (2B) | Reserved | Data Total Length (1B) |
+--------+--------+--------+--------+--------+--------+--------+
| Connection Handle (12b) | Flags (4b) | Length (1B) |
+-------------------------+------------+---------------+
- Connection Handle (12 位):标识 SCO 连接。
- Flags (4 位):通常保留或用于指示 SCO 状态。
- Data Total Length (1 字节):SCO 数据长度通常较小(如 60 字节),因此只用 1 字节表示。
4.5 HCI 数据包交互流程(SVG)
下图展示了主机与控制器之间通过 HCI 数据包进行交互的典型流程:
4.6 关键事件与命令对照表
在调试过程中,以下命令和事件组合最为常见:
| 操作 | HCI Command (OpCode) | 对应 HCI Event | 调试关注点 |
|---|---|---|---|
| 重置控制器 | 0x0C03 (Reset) |
0x0E (Command Complete) |
检查 Status 是否为 0x00 |
| 查询设备 | 0x0401 (Inquiry) |
0x02 (Inquiry Result) / 0x01 (Inquiry Complete) |
观察返回的设备地址和 RSSI |
| 创建连接 | 0x0405 (Create Connection) |
0x0F (Command Status) → 0x03 (Connection Complete) |
确认连接句柄和链路类型 |
| 断开连接 | 0x0406 (Disconnect) |
0x05 (Disconnection Complete) |
分析 Reason 字段(如 0x13 表示远程用户终止) |
| 读取本地版本 | 0x1001 (Read Local Version Info) |
0x0E (Command Complete) |
获取 HCI 版本、LMP 版本、制造商信息 |
4.7 实战:解析一段 HCI 日志
以下是一段真实的 HCI 日志片段(十六进制),我们逐字节解析:
01 03 0C 00
04 0E 04 01 03 0C 00
- 第一行:
01→ HCI Command 包03 0C→ OpCode(小端序),实际为0x0C03,即 HCI_Reset00→ Parameter Total Length = 0,无参数
- 第二行:
04→ HCI Event 包0E→ Event Code = Command Complete04→ 参数长度 = 4 字节01→ Num_HCI_Command_Packets = 1(可以继续发命令)03 0C→ 对应命令的 OpCode00→ Status = Success
日志分析技巧:使用
hcidump -X 或 Wireshark 的蓝牙嗅探功能,可以自动解析这些字节。但手动解析能帮助你深入理解协议栈的底层行为,尤其在驱动开发阶段。