
这部分是 4.2 Controller to Host data flow control,讲的是:
Controller 向 Host 发送 HCI Data Packet 时,也可能需要流控,避免 Host 侧的数据缓冲区被 Controller 发来的数据撑爆。
前面 4.1 讲的是:
Host → Controller 数据流控
这一节讲的是反方向:
Controller → Host 数据流控
1. 这部分整体表达了哪些知识
这一节主要表达了 5 个知识点:
1. Controller → Host 方向有些实现也需要流控
2. 这个方向的流控可以通过 HCI_Set_Controller_To_Host_Flow_Control command 开启或关闭
3. Host 初始化时要用 HCI_Host_Buffer_Size command 告诉 Controller:Host 自己有多少接收 buffer
4. Host 用 HCI_Host_Number_Of_Completed_Packets command 告诉 Controller:哪些 packet 已经处理完,Host buffer 可以释放
5. 同一个 Connection_Handle 上,Controller 发给 Host 的 HCI Data Packet 也必须保持顺序
可以把这一节理解为:
Controller 发数据给 Host 时,Host 也要告诉 Controller:"我现在最多能接多少包,我处理完了多少包,你可以继续发多少包"。
2. 为什么 Controller → Host 方向也需要流控
原文第一句说:
In some implementations, flow control may also be necessary in the direction from the Controller to the Host.
意思是:
在某些实现中,从 Controller 到 Host 的方向也可能需要流控。
原因很简单:Host 侧虽然通常资源比 Controller 多,但也不是无限的。
Controller 可能会向 Host 发送大量数据,例如:
对端设备发来的 ACL 数据
对端设备发来的 L2CAP/ATT/GATT 数据
BR/EDR 同步数据
多连接场景下多个设备同时上报数据
这些数据会先进入 Host 侧的 HCI buffer。
如果 Controller 发得太快,而 Host 的 HCI 层、L2CAP 层、ATT/GATT 层、系统线程处理不过来,也可能造成 Host buffer 堆积。
所以有些系统需要在这个方向也做流控。
3. HCI_Set_Controller_To_Host_Flow_Control command 的作用
原文说:
The HCI_Set_Controller_To_Host_Flow_Control command can be used to turn flow control on or off in that direction.
意思是:
HCI_Set_Controller_To_Host_Flow_Control 命令可以用来开启或关闭 Controller → Host 方向的流控。
也就是说,这个方向的流控不是天然一直开启的,而是可以由 Host 配置。
可以理解为:
Host 发命令给 Controller:
Controller → Host 方向要不要启用流控?
如果开启了,Controller 向 Host 发送数据时,就要受 Host buffer 状态限制。
如果关闭了,Controller 可以不按这一套 Host buffer 计数机制来控制发送。
4. HCI_Host_Buffer_Size command 的作用
原文说:
On initialization, the Host uses the HCI_Host_Buffer_Size command to notify the Controller about the maximum size of HCI ACL and Synchronous Data packets sent from the Controller to the Host.
意思是:
初始化时,Host 使用 HCI_Host_Buffer_Size 命令通知 Controller:Controller 发给 Host 的 HCI ACL Data Packet 和 Synchronous Data Packet,Host 最大能接收多大的包。
它还会告诉 Controller:
Host 可以存储多少个 ACL Data Packet
Host 可以存储多少个 Synchronous Data Packet
所以这个命令的作用是:
Host 把自己的接收 buffer 能力告诉 Controller
前面 4.1 是 Host 去问 Controller:
Controller,你有多少 buffer?
这一节反过来,是 Host 告诉 Controller:
Controller,我 Host 这边有多少 buffer。
5. Host Buffer Size 里包含哪些信息
这个命令大致表达几类能力:
1. Host 能接收的 HCI ACL Data Packet 最大长度
2. Host 能接收的 HCI Synchronous Data Packet 最大长度
3. Host 能缓存的 ACL Data Packet 总数量
4. Host 能缓存的 Synchronous Data Packet 总数量
可以理解为:
Host_ACL_Data_Packet_Length
Host_Synchronous_Data_Packet_Length
Host_Total_Num_ACL_Data_Packets
Host_Total_Num_Synchronous_Data_Packets
例如假设 Host 告诉 Controller:
Host_Total_Num_ACL_Data_Packets = 20
那么 Controller 就知道:
Host 最多可以同时缓存 20 个从 Controller 发过来的 ACL Data Packet
如果 Host 当前可用 buffer 用完了,Controller 就不能继续发对应类型的数据包给 Host。
6. HCI_Host_Number_Of_Completed_Packets command 的作用
原文说:
The Host uses the HCI_Host_Number_Of_Completed_Packets command in exactly the same way as the Controller uses the HCI_Number_Of_Completed_Packets event...
意思是:
Host 使用 HCI_Host_Number_Of_Completed_Packets 命令的方式,和 Controller 在 Host → Controller 流控中使用 HCI_Number_Of_Completed_Packets event 的方式完全类似。
前面 4.1 中:
Controller → Host:
HCI_Number_Of_Completed_Packets event
它告诉 Host:
Controller 已经完成了多少个 Host 发来的包,Controller buffer 已经释放。
这一节中反过来:
Host → Controller:
HCI_Host_Number_Of_Completed_Packets command
它告诉 Controller:
Host 已经处理完了多少个 Controller 发来的包,Host buffer 已经释放。
7. 两个方向的 completed packets 对比
这两个名字很像,但方向相反。
Host → Controller 数据流控:
Host 发数据给 Controller
Controller 处理完成后
Controller 通过 HCI_Number_Of_Completed_Packets event 通知 Host
Controller → Host 数据流控:
Controller 发数据给 Host
Host 处理完成后
Host 通过 HCI_Host_Number_Of_Completed_Packets command 通知 Controller
可以整理成表:
| 方向 | 谁发数据 | 谁的 buffer 被占用 | 谁通知 completed | 使用什么 |
|---|---|---|---|---|
| Host → Controller | Host | Controller buffer | Controller 通知 Host | HCI_Number_Of_Completed_Packets event |
| Controller → Host | Controller | Host buffer | Host 通知 Controller | HCI_Host_Number_Of_Completed_Packets command |
这就是这一节的核心。
8. Host 什么时候发送 HCI_Host_Number_Of_Completed_Packets
原文说:
a packet is completed when the Host is ready to free the corresponding buffer.
意思是:
当 Host 准备释放对应 buffer 时,这个 packet 就算 completed。
这里的 completed 也是 buffer 管理意义上的完成,不等于应用层业务已经处理完。
例如 Controller 收到对端 BLE 设备发来的数据,然后通过 HCI ACL Data Packet 发给 Host:
对端 BLE 设备
↓
空口
Controller
↓
HCI ACL Data Packet
Host
Host 收到后,数据可能会继续往上传:
HCI
↓
L2CAP
↓
ATT
↓
GATT
↓
App / 系统蓝牙框架
当 Host 认为底层 HCI buffer 可以释放时,就可以通过:
HCI_Host_Number_Of_Completed_Packets command
告诉 Controller:
某个 Connection_Handle 上,我已经处理完成了多少个 packet。
你可以继续往我这里发了。
9. Controller 不能在 Host free buffer 为 0 时继续发送
原文说:
The Controller shall not send an HCI Data packet to the Host when its count of the free buffer space for the corresponding link type is zero.
意思是:
当对应链路类型的 Host 可用 buffer 数量为 0 时,Controller 不得继续向 Host 发送 HCI Data Packet。
这里的"its count"可以理解为:
Controller 内部维护的 Host free buffer 计数
Controller 根据 Host 初始化时提供的 buffer size,以及 Host 后续发送的 completed packets command,维护一个"Host 当前还有多少接收能力"的计数。
流程是:
初始化时:
Host 告诉 Controller:我能接收 N 个 packet
Controller 每发送 1 个 HCI Data Packet 给 Host:
Host free buffer 计数减 1
Host 处理完成后:
Host 发送 HCI_Host_Number_Of_Completed_Packets command
Controller 收到后:
Host free buffer 计数增加
如果 Host free buffer = 0:
Controller 不能继续发
这和 4.1 的逻辑完全对称。
10. HCI_Host_Number_Of_Completed_Packets command 的特殊性
原文说:
The HCI_Host_Number_Of_Completed_Packets command is a special command for which no command flow control is used, and which can be sent anytime there is a connection or when in local loopback mode.
意思是:
HCI_Host_Number_Of_Completed_Packets 是一个特殊命令,它不受普通 HCI Command flow control 限制;只要有连接,或者处于 local loopback mode,就可以发送。
这句话很关键。
普通 HCI Command 发送时通常要受命令流控影响,比如要看 Controller 当前还能接收多少 HCI Command。
但这个命令不一样。
为什么?
因为它本身就是为了释放 flow control 额度的。
如果它也被普通命令流控卡住,就可能出现死锁:
Controller 等 Host 通知 buffer 释放
Host 想发 completed packets command
但普通 command flow control 不允许 Host 发命令
双方卡住
所以规范规定:
HCI_Host_Number_Of_Completed_Packets command 不使用 command flow control
这样 Host 可以随时告诉 Controller:
我这边 buffer 释放了,你可以继续发数据了。
11. 这个命令没有 completion event
原文说:
The command also has no event after the command has completed.
意思是:
这个命令执行完成后,没有对应的完成事件。
普通 HCI Command 通常会有:
HCI_Command_Complete event
或者:
HCI_Command_Status event
但 HCI_Host_Number_Of_Completed_Packets command 没有。
原因还是一样:它是流控内部使用的特殊命令,目的只是通知 Controller Host buffer 已释放,不需要再产生一个事件回应。
12. 为什么这样设计可以让两个方向的流控同时工作
原文说:
This makes it possible for the flow control to work in exactly the same way in both directions, and the flow of normal commands will not be disturbed.
意思是:
这样设计可以让两个方向的流控以相同方式工作,同时不会干扰普通命令的流动。
也就是说:
Host → Controller 数据流控:
Controller 用 event 通知 Host 释放 buffer
Controller → Host 数据流控:
Host 用 command 通知 Controller 释放 buffer
虽然一个是 event,一个是 command,但逻辑是对称的。
同时,因为 HCI_Host_Number_Of_Completed_Packets command 不占用普通 command flow control 额度,所以不会影响正常 HCI Command,例如:
LE Set Scan Enable
LE Create Connection
Read RSSI
Disconnect
LE Set Advertising Enable
13. 同一个 Connection_Handle 上的数据顺序要求
最后一句说:
For each individual Connection_Handle, the data shall be sent to the Host in HCI Data packets in the order in which it was provided by the Controller and shall arrive at the Host in that order.
意思是:
对于每一个单独的 Connection_Handle,Controller 发给 Host 的 HCI Data Packet 必须按照 Controller 提供的顺序发送,并且到达 Host 时也必须保持这个顺序。
这和 4.1 中 Host → Controller 的顺序要求是对称的。
例如对同一个 BLE 连接,Controller 要发给 Host:
Packet 1
Packet 2
Packet 3
Host 必须按顺序收到:
Packet 1 → Packet 2 → Packet 3
不能乱序:
Packet 2 → Packet 1 → Packet 3
这对上层 L2CAP、ATT、GATT 的数据重组非常重要。
14. 用 BLE 接收数据来理解这部分
假设 BLE 模块向手机发送 Notify 数据。
底层流程大概是:
BLE 模块
↓ 空口发送
手机 Controller
↓ HCI ACL Data Packet
手机 Host
↓ L2CAP
↓ ATT
↓ GATT
↓ App 收到 didUpdateValueForCharacteristic
如果 Controller → Host 方向启用了流控,那么初始化时 Host 会告诉 Controller:
我最多能缓存多少个 ACL Data Packet
每个 packet 最大多大
Controller 收到对端数据后,不是无限制往 Host 发,而是根据 Host buffer 计数发送。
每发一个包:
Host free buffer -= 1
Host 处理完若干包后,发送:
HCI_Host_Number_Of_Completed_Packets command
告诉 Controller:
Connection_Handle = 0x0001
Num_Completed_Packets = 5
Controller 更新:
Host free buffer += 5
然后继续向 Host 发送后续数据。
15. 和 App 层现象的关系
在 iOS / Android BLE App 中,一般看不到这些 HCI 细节,但它可能影响数据接收表现。
例如:
对端 Notify 发太快,App 层收到速度跟不上
Android 某些手机接收大吞吐时出现延迟
HCI log 里看到大量 ACL Data
系统蓝牙栈内部排队
不同手机接收吞吐差异很大
这些现象不一定全部是 HCI flow control 导致的,但 HCI flow control 是底层数据接收链路中的一个环节。
App 层看到的是:
didUpdateValueForCharacteristic
onCharacteristicChanged
但底层可能经历了:
空口包
Controller buffer
HCI ACL Data Packet
Host buffer
L2CAP
ATT
GATT
App callback
16. 4.1 和 4.2 的核心区别
可以这样对比:
4.1 Host to Controller data flow control
关注的是:
Host 发数据给 Controller 时,如何避免 Controller buffer 溢出
典型场景:
App 主动写数据给 BLE 外设
GATT Write
OTA 下发
透传发送
4.2 Controller to Host data flow control
关注的是:
Controller 发数据给 Host 时,如何避免 Host buffer 溢出
典型场景:
BLE 外设 Notify / Indicate 数据给手机
对端设备发送大量数据
Host 接收数据
17. 这部分的关键信息总结
17.1 Controller → Host 方向也可能需要流控
不是只有 Host → Controller 方向需要流控。
有些实现中,Controller 向 Host 发数据也需要根据 Host buffer 能力控制节奏。
17.2 是否启用由 HCI_Set_Controller_To_Host_Flow_Control 控制
这个命令用于开启或关闭 Controller → Host 方向的流控。
17.3 Host 初始化时用 HCI_Host_Buffer_Size 告诉 Controller 自己的接收能力
包括:
Host 能接收的 ACL Data Packet 最大长度
Host 能接收的 Synchronous Data Packet 最大长度
Host 能缓存的 ACL Data Packet 数量
Host 能缓存的 Synchronous Data Packet 数量
17.4 Host 用 HCI_Host_Number_Of_Completed_Packets 通知 Controller 释放 buffer
这个命令告诉 Controller:
某个 Connection_Handle 上,Host 已经处理完成了多少个 HCI Data Packet
Host buffer 可以释放
17.5 Controller 在 Host free buffer 为 0 时不能继续发
这是 Controller → Host 方向流控的硬规则。
Host free buffer > 0:Controller 可以发
Host free buffer = 0:Controller 不能发
17.6 HCI_Host_Number_Of_Completed_Packets 是特殊命令
它有两个特殊点:
不受普通 command flow control 限制
没有 command complete / command status 事件
这样可以避免流控机制干扰普通 HCI Command。
17.7 同一个 Connection_Handle 上的数据必须保持顺序
Controller 发给 Host 的 HCI Data Packet,在同一条连接上不能乱序。
18. 最核心的一句话
这一节可以总结为:
Controller to Host data flow control 的本质,是 Host 先告诉 Controller 自己有多少接收 buffer,Controller 每发一个 HCI Data Packet 就消耗一个 Host buffer 额度,Host 处理完数据后通过 HCI_Host_Number_Of_Completed_Packets command 把额度归还给 Controller;当 Host 可用 buffer 为 0 时,Controller 不能继续向 Host 发送数据。
和上一节 4.1 放在一起理解,就是:
Host → Controller:保护 Controller buffer
Controller → Host:保护 Host buffer
这两者都是 HCI 层的数据流控,不是 GATT 层确认机制,也不是空口层流控。