HCI 功能规范【4.3. Disconnection behavior】

这部分是 4.3 Disconnection behavior,讲的是:

当连接断开以后,Host 和 Controller 之间与这个连接相关的 HCI 数据包、buffer、flow control 计数应该如何处理。

它不是在讲断开连接的原因,也不是在讲 HCI_Disconnect command 的参数,而是在讲:

复制代码
连接已经断开以后,之前还没有完成的数据包和 buffer 怎么算?

1. 这部分整体表达了哪些知识

这一节主要表达两个方向的处理规则:

复制代码
1. Host → Controller 方向:
   连接断开后,Host 应认为之前发给 Controller、但还没确认完成的数据包已经被 flush,
   Controller 对应 buffer 已经释放。

2. Controller → Host 方向:
   如果 Controller → Host 方向也启用了流控,
   连接断开后,Controller 可以认为 Host 会释放这个连接相关的接收 buffer,
   Host 不需要再专门发送 completed packets command 通知 Controller。

也就是说,断开连接以后,这条连接上的未完成 HCI 数据包不会继续等待完成通知,而是直接按"清掉了"处理。


2. HCI_Disconnection_Complete event 是关键事件

原文第一句说:

复制代码
When the Host receives an HCI_Disconnection_Complete event...

意思是:

当 Host 收到 HCI_Disconnection_Complete event 时,Host 就知道某个连接已经断开完成。

HCI_Disconnection_Complete event 是 Controller 通知 Host:

复制代码
某个 Connection_Handle 对应的连接已经断开

这个事件里通常会带有:

复制代码
Status
Connection_Handle
Reason

其中 Connection_Handle 很重要,因为它告诉 Host:

复制代码
是哪一条连接断开了

如果 Host 同时连接多个设备,就要根据这个 handle 清理对应连接的数据状态。


3. Host 应认为未确认的 HCI Data packets 已经被 flush

原文说:

复制代码
the Host shall assume that all unacknowledged HCI Data packets that have been sent to the Controller for the returned Handle have been flushed

意思是:

Host 应当认为,所有已经发送给 Controller、但还没有收到完成通知的 HCI Data Packet,都已经被 flush 掉了。

这里的 unacknowledged HCI Data packets 指的是:

复制代码
Host 已经发给 Controller
但是 Controller 还没有通过 HCI_Number_Of_Completed_Packets event 告诉 Host completed 的数据包

例如 Host 给某个 BLE 连接发送了 10 个 HCI ACL Data Packet:

复制代码
Host → Controller:Packet 1
Host → Controller:Packet 2
Host → Controller:Packet 3
...
Host → Controller:Packet 10

Controller 已经通过 HCI_Number_Of_Completed_Packets event 报告完成了 6 个:

复制代码
Num_Completed_Packets = 6

还有 4 个没有完成通知。

这时连接断开,Host 收到:

复制代码
HCI_Disconnection_Complete event
Connection_Handle = 0x0001

那么 Host 应该认为剩下那 4 个未完成的数据包已经被 flush,不需要再等它们的 completed 通知。


4. flushed 是什么意思

这里的 flushed 可以理解为:

复制代码
被清除
被丢弃
不会继续发送
不再占用 Controller buffer

它不表示这些数据已经成功送到对端设备。

恰恰相反,连接都已经断开了,未完成的数据通常应该视为没有可靠送达。

所以这里要特别注意:

复制代码
flush ≠ 成功发送到对端
flush ≠ 对端应用层收到
flush ≠ GATT 写入成功

它只是说:

复制代码
Controller 不再保留这些数据,对应 buffer 已经释放。

5. 对应 Controller buffer 已经释放

原文接着说:

复制代码
and that the corresponding data buffers have been freed.

意思是:

Host 还应认为这些数据包对应的 Controller buffer 已经释放。

这句话是为了和前面 4.1 的 Host → Controller flow control 对上。

在 4.1 中,Host 每发送一个 HCI Data Packet,就会把 Controller 可用 buffer 计数减 1。

例如:

复制代码
Controller 总共有 8 个 LE ACL buffer
Host 发出 8 个包后,free_buffer = 0

如果其中 4 个包还没 completed,正常情况下 Host 要等:

复制代码
HCI_Number_Of_Completed_Packets event

才能把 free buffer 加回来。

但是如果连接断开了,Host 不需要再等这个事件。

Host 收到:

复制代码
HCI_Disconnection_Complete event

后,就可以认为这条连接上未完成数据包对应的 Controller buffer 已经释放。


6. Controller 不需要再通过 completed 事件通知这些包

原文说:

复制代码
A Controller does not have to notify the Host about this in an HCI_Number_Of_Completed_Packets or an HCI_Number_Of_Completed_Data_Blocks event before the disconnection event.

意思是:

Controller 不需要在断开事件之前,再专门通过 HCI_Number_Of_Completed_Packets 或 HCI_Number_Of_Completed_Data_Blocks event 通知 Host 这些数据包已经完成。

也就是说,Controller 不需要这样做:

复制代码
先上报 HCI_Number_Of_Completed_Packets
告诉 Host 剩余未完成包都完成了
然后再上报 HCI_Disconnection_Complete

规范允许它直接上报:

复制代码
HCI_Disconnection_Complete

Host 收到后自己清理这条连接上的 pending 数据和 buffer 计数。

这避免了断开连接时还要额外补很多 completed packets 事件。


7. Controller → Host 方向启用流控时的处理

第二段讲的是反方向:

复制代码
Controller → Host

也就是 4.2 中提到的 Controller 到 Host 的 flow control。

原文说:

复制代码
If flow control is also enabled in the direction from the Controller to the Host...

意思是:

如果 Controller → Host 方向也启用了流控,那么断开连接后也有类似处理。

前面 4.2 说过,Controller 向 Host 发送数据时,会消耗 Host buffer。Host 处理完成后,要用:

复制代码
HCI_Host_Number_Of_Completed_Packets command

通知 Controller:

复制代码
我这边某个 Connection_Handle 上的多少个 packet 已经处理完了,Host buffer 可以释放。

但是连接断开后,不需要再这样通知。


8. Controller 可以认为 Host 会释放这个 Handle 对应的 buffer

原文说:

复制代码
the Controller may, after it has sent an HCI_Disconnection_Complete event, assume that the Host will flush its data buffers for the sent Handle

意思是:

Controller 发送 HCI_Disconnection_Complete event 后,可以认为 Host 会清理这个 Connection_Handle 对应的数据 buffer。

也就是说,Controller 不需要继续等待 Host 对这个 handle 发送:

复制代码
HCI_Host_Number_Of_Completed_Packets command

因为这条连接已经没了,这个 handle 对应的数据包和 buffer 状态都应该被清理。


9. Host 不需要再发 HCI_Host_Number_Of_Completed_Packets command

原文最后说:

复制代码
The Host does not have to notify the Controller about this in an HCI_Host_Number_Of_Completed_Packets command.

意思是:

Host 不需要再通过 HCI_Host_Number_Of_Completed_Packets command 告诉 Controller:我已经释放了这个连接相关的 Host buffer。

这和第一段是对称的。

可以这样理解:

复制代码
Host → Controller 方向:
连接断开后,Controller 不需要再发 completed packets event。
Host 自己认为 Controller buffer 已释放。

Controller → Host 方向:
连接断开后,Host 不需要再发 host completed packets command。
Controller 自己认为 Host buffer 会释放。

10. 两个方向的断开行为对比

可以整理成这样:

方向 断开前谁发数据 谁的 buffer 被占用 断开后谁不用再通知 Host/Controller 应怎么认为
Host → Controller Host 发给 Controller Controller buffer Controller 不必再发 HCI_Number_Of_Completed_Packets Host 认为未完成包已 flush,Controller buffer 已释放
Controller → Host Controller 发给 Host Host buffer Host 不必再发 HCI_Host_Number_Of_Completed_Packets Controller 认为 Host 会清理该 handle 的 buffer

核心逻辑就是:

复制代码
连接断开后,这个 Connection_Handle 相关的 pending data 和 buffer 计数都要清掉。

11. 为什么需要这个规则

因为连接断开时,很多数据可能还在排队。

例如 BLE OTA 发送中:

复制代码
Host 已经往 Controller 塞了很多 ACL Data Packet
Controller 还没来得及通过空口全部发出去
连接突然断开

如果没有这个规则,Host 可能还在等:

复制代码
HCI_Number_Of_Completed_Packets event

来释放 buffer 额度。

但是连接已经断开了,剩余数据不可能继续按原连接发送。

所以规范直接规定:

复制代码
收到 HCI_Disconnection_Complete 后,Host 应当认为该连接未完成包已经 flush,buffer 已释放。

这样 Host 可以清理状态,避免 flow control 计数卡死。


12. completed 和 disconnection 的关系

前面 4.1 说:

复制代码
packet completed = Controller 不再需要该 packet 对应的 buffer,并释放 buffer

通常 completed 会通过:

复制代码
HCI_Number_Of_Completed_Packets event

通知 Host。

但是 4.3 补充了一个特殊场景:

复制代码
连接断开时,不需要逐个 completed。

断开事件本身就隐含了:

复制代码
这个 Connection_Handle 上未完成的数据包已经不再保留
对应 buffer 已经释放

所以可以理解为:

复制代码
正常发送完成:通过 completed packets event 归还 buffer
连接断开:通过 disconnection complete 隐含清理 pending buffer

13. 对 BLE App 开发的实际意义

在 iOS / Android BLE 开发中,这部分能解释一些现象:

复制代码
App 正在大量写入数据
突然收到断开连接回调
之前已经调用 write 的数据,不代表都已经到达对端
底层还没完成的数据可能已经被 flush
断开以后,不应该继续等待这些数据的底层完成
应该清理发送队列和连接状态

例如 BLE OTA 时:

复制代码
App 已经发送到第 500 包
系统底层可能还有若干 HCI ACL Data Packet 排队
此时连接断开

这时不能认为:

复制代码
第 500 包之前的数据都已经被对端成功处理

更严谨的理解是:

复制代码
已经被应用层确认 / 协议层确认的数据才可靠
未确认的数据可能只是进入了本地 Host/Controller 队列
连接断开后,未完成的数据会被 flush

所以做 OTA 或大数据传输时,需要自己的业务确认机制,例如:

复制代码
分包序号
ACK
CRC
断点续传
失败重传
最终校验

不能只依赖"App 调用了 write 成功"来判断对端已经收到。


14. 这部分的关键信息总结

14.1 HCI_Disconnection_Complete event 是清理连接状态的关键事件

Host 收到这个事件后,就知道某个 Connection_Handle 对应的连接已经断开。


14.2 Host → Controller 方向,未完成数据包视为 flushed

对于这个断开的 handle:

复制代码
Host 已发给 Controller
但还没收到 completed 通知的数据包

Host 应认为它们已经被 flush。


14.3 flush 不等于发送成功

flushed 表示数据被清理、不再占用 buffer,不代表对端已经收到。


14.4 Controller buffer 视为已释放

Host 不需要继续等待这个 handle 的:

复制代码
HCI_Number_Of_Completed_Packets event

或:

复制代码
HCI_Number_Of_Completed_Data_Blocks event

来归还 buffer。


14.5 Controller 不需要在断开事件前补发 completed packets event

Controller 可以直接上报:

复制代码
HCI_Disconnection_Complete event

Host 自己根据这个事件清理相关 pending 数据。


14.6 Controller → Host 方向也类似

如果 Controller → Host 方向启用了 flow control,Controller 发送断开事件后,可以认为 Host 会清理该 handle 的 buffer。

Host 不需要再发送:

复制代码
HCI_Host_Number_Of_Completed_Packets command

15. 最核心的一句话

这一节可以总结为:

复制代码
连接断开后,Connection_Handle 对应的未完成 HCI Data Packet 都应被视为已经清理;Host 不需要继续等待 Controller 的 completed packets event,Controller 也不需要继续等待 Host 的 host completed packets command,双方都应基于 HCI_Disconnection_Complete event 清理该连接相关的 buffer 和 flow control 状态。

对 BLE 数据传输来说,最重要的理解是:

复制代码
断开连接时,底层未完成的数据会被 flush;flush 只是释放 buffer,不代表数据已经成功到达对端。