4、HCI事件分析:Disconnection Complete Event、Connection Update Complete Event、LE Meta Event
好,咱们进入第四章。前面几章我们把GATT连接建立的过程捋了一遍,从广播、扫描到连接请求,每一步都有对应的HCI事件。那连接建立之后呢?断开、参数更新、以及各种LE专属的事件,同样需要你读懂HCI日志。
我个人习惯,拿到一份蓝牙抓包日志,先看三样东西:连接怎么断的、连接参数有没有变过、以及有没有LE Meta事件夹在中间。这三样看明白了,80%的断连问题就能定位。
4.1 Disconnection Complete Event —— 断连的“验尸报告”
这个事件,说白了就是告诉你连接已经结束了。不管是本地主动断、对端断、还是因为链路超时被Controller强制断,最终都会触发这个事件。
它的结构其实很简单,但里面的Reason字段是诊断的关键。我见过不少工程师,看到断连事件就慌,其实只要把Reason查一下手册,问题就清楚了一半。
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| Event Code | 1 | 0x05 |
| Parameter Total Length | 1 | 后面参数的总长度 |
| Status | 1 | 0x00表示成功断开 |
| Connection Handle | 2 | 哪个连接断了 |
| Reason | 1 | 断开原因码,重点看这个 |
Reason字段常见的值,我列一下:
- 0x08 —— Connection Timeout。链路超时,最常见。对端没在规定时间内回复包,Controller就自己断了。
- 0x13 —— Remote User Terminated Connection。对端主动断的,比如手机端点了断开按钮。
- 0x16 —— Connection Terminated by Local Host。本地主动断的,比如你的设备调了
DisconnectAPI。 - 0x3E —— Connection Failed to be Established。连接建立失败,不是断开,但也会触发这个事件。
举个例子,你看到这样一段HCI日志:
> HCI Event: Disconnection Complete (0x05) Plen 4
Status: Success (0x00)
Connection Handle: 0x0001
Reason: Connection Timeout (0x08)
嗯,Reason是0x08。这时候我会先问自己:是射频环境差,还是设备太忙没空回包? 这两个原因的处理方式完全不同。
4.2 Connection Update Complete Event —— 连接参数的“变更记录”
连接建立之后,双方可以协商调整连接参数,比如连接间隔、延迟、超时时间。每次参数更新成功,Controller都会上报Connection Update Complete Event。
这个事件的结构:
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| Event Code | 1 | 0x13 |
| Parameter Total Length | 1 | 后面参数的总长度 |
| Status | 1 | 0x00表示更新成功 |
| Connection Handle | 2 | 哪个连接的参数变了 |
| Connection Interval | 2 | 新的连接间隔(单位:1.25ms) |
| Connection Latency | 2 | 新的从机延迟 |
| Supervision Timeout | 2 | 新的超时时间(单位:10ms) |
这里有个坑,我踩过。有一次设备连上后频繁断连,看日志发现Connection Update Complete Event的Status是0x00,但连接间隔被更新成了一个非常小的值。小间隔意味着设备要频繁收发数据,功耗高,而且容易丢包。后来发现是手机端主动发了一个不合理的参数更新请求,设备端没做校验就接受了。
你想想看,如果连接参数被改得过于激进,设备可能撑不住。所以每次看到Connection Update Complete Event,我都要确认一下新参数是否合理。
4.3 LE Meta Event —— 所有LE专属事件的“容器”
这个事件比较特殊。它本身不是一个具体的事件,而是一个容器。所有LE相关的子事件,都会被打包在LE Meta Event里上报。
它的结构:
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| Event Code | 1 | 0x3E |
| Parameter Total Length | 1 | 后面参数的总长度 |
| Subevent Code | 1 | 子事件类型 |
| Subevent Parameters | 可变 | 子事件的具体参数 |
常见的子事件码:
- 0x01 —— LE Connection Complete。连接建立成功。
- 0x02 —— LE Advertising Report。收到广播包。
- 0x03 —— LE Connection Update Complete。连接参数更新完成(注意,这个和上面说的Connection Update Complete Event是两回事,这个是LE专属的)。
- 0x05 —— LE Read Remote Used Features Complete。读取对端支持的特性。
- 0x0A —— LE Data Length Change。数据长度更新。
- 0x0C —— LE PHY Update Complete。PHY参数更新完成。
我举个例子,你看到这样一段日志:
> HCI Event: LE Meta Event (0x3E) Plen 12
Subevent: LE Connection Complete (0x01)
Status: Success (0x00)
Connection Handle: 0x0001
Role: Peripheral (0x01)
Peer Address: 11:22:33:44:55:66
Connection Interval: 30 (37.50 ms)
Connection Latency: 0
Supervision Timeout: 500 (5000 ms)
这就是一个典型的LE连接建立成功事件。注意看Role字段,它告诉你设备是Central还是Peripheral。我曾经在调试时发现设备角色搞反了,导致连接一直失败,就是靠这个字段定位的。
4.4 三者关系与诊断流程
这三个事件在连接生命周期中的位置,我画了一张图:
这张图把三个事件在连接生命周期中的位置说清楚了。你抓包时,按照这个顺序去排查,基本不会漏。
4.5 实战:一次断连问题的诊断过程
我记得有一次,客户反馈设备连接后几分钟就断。我拿到HCI日志,先看Disconnection Complete Event:
> HCI Event: Disconnection Complete (0x05) Plen 4
Status: Success (0x00)
Connection Handle: 0x0001
Reason: Connection Timeout (0x08)
嗯,超时。然后我往前翻,看有没有Connection Update Complete Event:
> HCI Event: Connection Update Complete (0x13) Plen 6
Status: Success (0x00)
Connection Handle: 0x0001
Connection Interval: 6 (7.50 ms)
Connection Latency: 0
Supervision Timeout: 100 (1000 ms)
发现问题了!连接间隔被更新成了7.5ms,但超时时间只有1000ms。这么小的间隔,设备要频繁收发数据,一旦CPU忙不过来,丢一个包就可能触发超时。
再往前看LE Meta Event,发现是手机端发起了参数更新请求,设备端没有做校验就接受了。
你看,三个事件串起来,问题就清楚了。单独看任何一个事件,都只能看到表象。
4.6 小结
这一章我们聊了三个关键HCI事件:
- Disconnection Complete Event —— 断连的“验尸报告”,Reason字段是核心。
- Connection Update Complete Event —— 连接参数的“变更记录”,注意参数合理性。
- LE Meta Event —— 所有LE专属事件的“容器”,看到0x3E就要往里看子事件。
这三个事件,是诊断连接问题的三把钥匙。下次你抓包时,不妨先找这三个事件,把连接的生命周期还原出来。嗯,你会发现,很多问题其实没那么复杂。