HCI 功能规范【4.1. Host to Controller data flow control】

这部分是 HCI flow control 的开头,主要讲的是:

Host 和 Controller 之间传输数据时,需要有流控机制,避免 Controller 的数据缓冲区被 Host 发来的数据撑爆。

这里的 flow control 不是蓝牙空口上的流控,而是 Host 和 Controller 之间 HCI 层的数据流控


1. 这部分整体想表达什么

HCI 数据传输不是 Host 想发多少就发多少。

因为 Controller 内部有数据 buffer,例如用于暂存:

复制代码
HCI ACL Data Packet
HCI ISO Data Packet
BR/EDR 数据
LE 数据

如果 Host 连续向 Controller 发送大量数据,而 Controller 来不及通过无线链路发出去,就会导致 Controller buffer 溢出。

所以 HCI 需要 flow control。

可以理解为:

复制代码
Host
  ↓ 发送 HCI Data Packet
Controller
  ↓ 暂存在 Controller buffer
  ↓ 再通过无线链路发给 remote device

如果远端设备没有及时响应、连接间隔较长、空口吞吐受限、Controller buffer 很小,Host 还一直往 Controller 塞数据,就会出问题。


2. 这里讲的是 Host → Controller 方向的数据流控

原文第一句说:

Flow control for data shall be used in the direction from the Host to the Controller...

意思是:

数据流控必须用于 Host 到 Controller 这个方向。

也就是:

复制代码
Host → Controller

为什么这个方向必须做流控?

因为 Host 发送数据给 Controller 后,Controller 需要先把数据放进自己的 buffer,再等待合适的连接事件、空口时机、链路资源,把数据发给对端设备。

如果 Controller buffer 满了,Host 还继续发,Controller 就处理不过来。

所以 Host 必须知道:

复制代码
Controller 现在还有多少 buffer 可以用?
我还能不能继续发 ACL Data?
哪些数据包已经发送完成并释放 buffer?

3. Flow control 主要是为了避免 Controller data buffers 溢出

原文说:

to avoid overflowing the Controller data buffers

意思是:

为了避免 Controller 的数据缓冲区溢出。

这里的 Controller data buffers 可以理解为 Controller 内部用于暂存 HCI 数据包的队列。

例如 BLE GATT 写数据时,Host 最终可能通过 HCI ACL Data Packet 把 ATT 数据交给 Controller。

流程大概是:

复制代码
App 写入数据
  ↓
GATT / ATT
  ↓
L2CAP
  ↓
HCI ACL Data
  ↓
Controller buffer
  ↓
Link Layer 在连接事件中发出去
  ↓
对端设备

如果 App 或 Host 层发得太快,而空口发送能力有限,Controller buffer 就可能被填满。

这就是为什么 HCI flow control 很重要。


4. 什么是 "data destined for a remote device"

原文说:

with data destined for a remote device

意思是:

这些数据最终是要发给远端设备的。

也就是说,Host 发给 Controller 的数据不是停在 Controller 里,而是要通过 Controller 发给另一个蓝牙设备。

例如:

复制代码
手机 Host → 手机 Controller → 空口 → 蓝牙模块

或者:

复制代码
MCU Host → 蓝牙芯片 Controller → 空口 → 手机

这里的 remote device 就是对端蓝牙设备。

所以 HCI flow control 管的是:

复制代码
Host 交给 Controller、准备发往对端设备的数据

典型就是 ACL 数据。


5. using a Connection_Handle 是什么意思

原文中提到:

using a Connection_Handle

意思是:

这些数据是和某一条连接关联的,通常会带有 Connection_Handle

在 HCI ACL Data Packet 中,会有一个连接句柄,用来表示这包数据属于哪一条连接。

例如一个 Central 同时连接了多个 BLE 外设:

复制代码
Connection_Handle = 0x0001 → 设备 A
Connection_Handle = 0x0002 → 设备 B
Connection_Handle = 0x0003 → 设备 C

Host 向 Controller 发送 ACL Data 时,需要告诉 Controller:

复制代码
这包数据要发到哪一条连接上

Controller 才知道应该把数据放到哪条链路对应的队列里。

所以 flow control 通常也和连接句柄有关,因为 Controller 需要知道哪些连接上的数据包已经处理完成。


6. Packet based flow control 是默认机制

原文说:

Packet based flow control is the default for BR/EDR traffic and the only option for LE traffic.

意思是:

基于 Packet 数量的流控,是 BR/EDR 流量的默认方式,也是 LE 流量的唯一方式。

这句话非常关键。


6.1 什么是 Packet based flow control

Packet based flow control 可以理解为:

按"包的数量"来进行流控。

Controller 会告诉 Host:

复制代码
我最多能接收多少个 HCI Data Packet

Host 发送数据包后,要减少可用额度。

Controller 处理完一部分数据后,会通过事件告诉 Host:

复制代码
哪些连接上的多少个包已经完成了
你可以继续发送新的包了

典型相关事件是:

复制代码
HCI_Number_Of_Completed_Packets event

这个事件的作用就是告诉 Host:

复制代码
某个 Connection_Handle 上,Controller 已经完成了多少个 HCI Data Packet
对应 buffer 已经释放
Host 可以继续发新的数据包

6.2 为什么 LE 只能用 Packet based flow control

对于 BLE 来说,规范明确说:

复制代码
Packet based flow control is the only option for LE traffic.

也就是说,LE 数据流控只能按包数量来做。

这就意味着,Host 不能无限制地往 Controller 发送 LE ACL Data Packet。

Host 必须根据 Controller 的 buffer 能力和完成事件来控制发送节奏。


7. 这部分不是在讲 GATT 层流控

这里容易和 GATT/ATT 的流控混淆。

HCI flow control 是:

复制代码
Host ↔ Controller 之间的流控

不是:

复制代码
App ↔ GATT 之间的流控

也不是:

复制代码
ATT Write Request / Write Command 的应用层确认机制

例如 BLE 发送数据时,可能有多层"限速":

复制代码
App 层:一次发送多少业务数据
GATT/ATT 层:Write Request 还是 Write Without Response
L2CAP 层:分片、重组
HCI 层:Controller buffer 流控
Link Layer 层:连接事件里能发多少个空口包

这一节讲的是其中的 HCI 层。


8. Controller → Host 方向的流控

原文说:

Flow control for data moving from the Controller to the Host may be in accordance with Section 4.2.

意思是:

Controller 到 Host 方向的数据流控,可以按照 4.2 节的规则来处理。

也就是说:

复制代码
Controller → Host

这个方向也可能需要流控,但这里的语气是 may,不像 Host → Controller 那样强调必须使用。

为什么?

因为 Controller 向 Host 上报数据时,Host 侧通常资源更充足,或者由系统驱动处理接收队列。但在某些传输方式或实现中,也需要避免 Host 接收不过来,所以可以使用 Controller 到 Host 方向的流控机制。


9. Command flow control 是另一件事

最后一句说:

Command flow control is covered in Section 4.4 and Section 4.5.

意思是:

命令流控在 4.4 和 4.5 节中讲。

这里要区分两种流控:

复制代码
Data flow control
Command flow control

9.1 Data flow control

Data flow control 管的是 HCI 数据包,比如:

复制代码
HCI ACL Data Packet
HCI ISO Data Packet

例如 BLE GATT 通信最终会走 HCI ACL Data Packet。

所以这里关心的是:

复制代码
Host 能不能继续往 Controller 发数据包
Controller buffer 有没有空位
已经完成了多少个数据包

9.2 Command flow control

Command flow control 管的是 HCI Command。

例如:

复制代码
LE Set Advertising Parameters
LE Set Advertising Data
LE Set Scan Enable
LE Create Connection
Disconnect
Read RSSI

HCI Command 也不能无限制乱发。

Controller 通常会通过:

复制代码
Num_HCI_Command_Packets

告诉 Host 当前还能接收多少个 HCI Command。

这个字段经常出现在:

复制代码
HCI_Command_Complete event
HCI_Command_Status event

所以命令流控和数据流控不是一回事。


10. 结合 BLE 数据发送理解

以 BLE 发送 GATT 数据为例。

假设手机要向蓝牙模块写入大量数据:

复制代码
App
  ↓
CoreBluetooth / Android Bluetooth API
  ↓
GATT
  ↓
ATT
  ↓
L2CAP
  ↓
HCI ACL Data
  ↓
Controller
  ↓
Link Layer
  ↓
空口
  ↓
蓝牙模块

当 Host 把 ATT 数据封装成 HCI ACL Data Packet 后,并不是随便往 Controller 发。

Host 要考虑:

复制代码
Controller 支持的 ACL buffer 数量
每个 ACL buffer 的最大长度
当前还剩多少可用 buffer
Controller 已经完成了多少包

Controller 完成发送后,会通过事件释放额度:

复制代码
HCI_Number_Of_Completed_Packets event

Host 收到这个事件后,才知道可以继续往 Controller 发更多 ACL 数据。

所以 HCI flow control 直接影响 BLE 大数据传输的吞吐和稳定性。


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

11.1 HCI flow control 是 Host 和 Controller 之间的流控

它不是空口流控,也不是 GATT 应用层流控。

它解决的是:

复制代码
Host 发给 Controller 的数据太快,Controller buffer 被塞满

11.2 Host → Controller 方向必须使用数据流控

因为 Controller buffer 有限,Host 不能无限制发送 HCI Data Packet。


11.3 LE 流量只能使用 Packet based flow control

对于 BLE 来说,HCI 数据流控就是基于包数量的流控。

核心思路是:

复制代码
Controller 告诉 Host:我能接收多少包
Host 发包时消耗额度
Controller 完成包后通过事件释放额度
Host 根据释放额度继续发送

11.4 Connection_Handle 用来区分数据属于哪条连接

HCI 数据包通常带有 Connection_Handle

它告诉 Controller:

复制代码
这包数据属于哪一个蓝牙连接

多连接场景下,这一点非常重要。


11.5 Data flow control 和 Command flow control 要区分

复制代码
Data flow control:管 HCI ACL / ISO 数据包
Command flow control:管 HCI Command 命令包

不要把它们混在一起理解。


12. 对这部分的学习建议

这部分现在只需要先建立一个核心理解:

复制代码
HCI flow control 的本质,是 Host 根据 Controller 的 buffer 能力和完成事件,控制 HCI 数据包发送节奏。

后面继续看 4.x 小节时,可以重点关注:

复制代码
4.1 Host to Controller data flow control
4.2 Controller to Host data flow control
4.4 Command flow control
4.5 Command Status / Command Complete 相关机制
HCI_Number_Of_Completed_Packets event

尤其对于 BLE 数据透传、OTA、大文件发送、吞吐测试来说,HCI flow control 很关键。

因为你在 App 层看到的现象可能是:

复制代码
写太快丢包
写太快失败
吞吐上不去
连接容易断
不同手机表现不同

但底层原因之一可能就是:

复制代码
Host、Controller、Link Layer、对端设备之间的数据缓存和流控能力不同。

这部分是 4.1 Host to Controller data flow control,讲的是:

Host 向 Controller 发送 HCI Data Packet 时,Host 应该如何根据 Controller 的 buffer 情况控制发送节奏,避免 Controller 缓冲区溢出。

这部分比 4. HCI flow control 更具体,开始真正讲 Host → Controller 方向的数据流控规则。


1. 这部分整体想表达什么

Host 发给 Controller 的数据,主要是:

复制代码
HCI ACL Data Packet
HCI ISO Data Packet

这些数据最终会通过 Controller 发给对端蓝牙设备。

但是 Controller 内部 buffer 是有限的,所以 Host 不能无限制地往 Controller 发数据。

因此 Host 必须知道:

复制代码
Controller 一共有多少 buffer
每个 buffer 最大能放多大的 HCI Data Packet
当前还有多少 buffer 可用
哪些已经发送完成,可以释放 buffer

这就是 Host to Controller data flow control 要解决的问题。

它的核心逻辑是:

复制代码
初始化时,Host 读取 Controller 的 buffer 能力
        ↓
Host 根据 buffer 数量发送 HCI Data Packet
        ↓
Host 每发送一个包,就认为 Controller 可用 buffer 减 1
        ↓
Controller 完成若干包后,通过事件通知 Host
        ↓
Host 根据完成事件恢复可用 buffer 数量
        ↓
Host 继续发送后续数据

2. Host → Controller 数据流控有两种方式

规范中说定义了两种数据流控方式:

复制代码
packet-based flow control
data-block-based flow control

也就是:

复制代码
基于包的流控
基于数据块的流控

选择哪种流控模式,通过这个命令设置:

复制代码
HCI_Write_Flow_Control_Mode command

2.1 Packet-based flow control

这是 BLE 最重要的流控方式。

它的核心是:

复制代码
按 HCI Data Packet 的数量来计算 buffer 使用情况

例如 Controller 告诉 Host:

复制代码
我最多可以缓存 10 个 HCI ACL Data Packet

那么 Host 最多只能连续发 10 个包。

每发 1 个包,可用数量减 1。

Controller 完成 1 个包后,通过事件告诉 Host:

复制代码
某个 Connection_Handle 上完成了 1 个包

Host 再把可用数量加 1。


2.2 Data-block-based flow control

这是另一种流控方式,也称为:

复制代码
buffer management

它不是简单按包数量管理,而是按 Controller 内部的数据块 buffer 来管理。

这部分对普通 BLE 学习不是最核心。对于 LE 来说,前面规范已经说了:

复制代码
Packet based flow control is the only option for LE traffic.

所以你现在重点掌握 packet-based flow control 就够了。


3. BR/EDR/LE Controller 是否有独立 ACL buffer

截图开头讲了两种情况:

复制代码
Controller 为 BR/EDR 和 LE 实现了独立 ACL buffer
Controller 没有为 BR/EDR 和 LE 实现独立 ACL buffer

这主要是针对 双模 Controller

双模 Controller 同时支持:

复制代码
BR/EDR
LE

也就是既支持经典蓝牙,也支持 BLE。

那么 Controller 内部的 ACL buffer 可能有两种设计。


3.1 情况一:BR/EDR 和 LE 使用独立 buffer

如果 Controller 为 ACL Data 实现了独立 buffer,那么 Host 要分别管理。

规范要求:

复制代码
1. Host 使用 HCI_LE_Read_Buffer_Size command 读取 LE-U logical link 使用的 buffer。
2. Host 对每一组 buffer 使用独立的 packet-based flow control。
3. Controller 根据 ACL Data packet 里的 Connection_Handle 判断这包数据应该使用哪一组 buffer。

可以理解为:

复制代码
BR/EDR ACL buffer:一套
LE ACL buffer:一套

Host 不能把它们混在一起算。

例如:

复制代码
BR/EDR buffer 剩余 5 个
LE buffer 剩余 3 个

那么发送 LE 数据时,只能消耗 LE buffer。

发送 BR/EDR 数据时,只能消耗 BR/EDR buffer。


3.2 情况二:BR/EDR 和 LE 没有独立 buffer

如果 Controller 没有实现独立 buffer,那么所有 ACL Data 使用同一套 BR/EDR buffer management。

规范说:

复制代码
all ACL Data shall use the BR/EDR buffer management

也就是说:

复制代码
BR/EDR ACL Data
LE ACL Data

都从同一个 buffer 池里消耗。

这时 Controller 根据 Connection_Handle 来判断这包数据属于哪个 logical link。


4. Connection_Handle 在流控中有什么作用

这一节多次提到:

复制代码
Connection_Handle
Handle

原因是 HCI Data Packet 是和具体连接相关的。

例如一个 Host 同时连接多个 BLE 设备:

复制代码
Connection_Handle = 0x0001 → 设备 A
Connection_Handle = 0x0002 → 设备 B
Connection_Handle = 0x0003 → 设备 C

Host 发 HCI ACL Data Packet 时,里面会带 Connection_Handle

Controller 根据这个 handle 知道:

复制代码
这包数据属于哪条连接
这包数据应该走哪个 logical link
这包数据应该消耗哪一组 buffer

在多连接场景下,这一点非常重要。


5. 什么叫一个 packet "completed"

截图中有一段非常关键:

复制代码
A packet is said to have completed when the Controller no longer needs the buffer space to store the data from the packet and has freed the corresponding buffer for re-use.

意思是:

当 Controller 不再需要用 buffer 保存这个 packet 的数据,并且已经释放对应 buffer 供后续复用时,这个 packet 才算 completed。

注意,这里的 completed 不一定等同于:

复制代码
对端应用层已经收到数据

也不一定等同于:

复制代码
ATT 层已经确认

它表示的是:

复制代码
Controller 侧的 HCI buffer 已经可以释放

通常发生在:

复制代码
数据已经发送出去
数据被 flush 掉
Controller 内部已经把数据转移到其他存储位置

所以这里的 completed 是 Controller buffer 管理意义上的完成


6. 同一个 Connection_Handle 上的数据顺序要求

规范最后一句说:

复制代码
For each individual Connection_Handle, the data shall be sent to the Controller in HCI Data packets in the order in which it was provided by the Host and shall arrive at the Controller in that order.

意思是:

对于每一个单独的 Connection_Handle,Host 提供的数据应该按照原始顺序发送到 Controller,并且到达 Controller 时也应该保持这个顺序。

也就是说,同一条连接上的 HCI Data Packet 不能乱序。

例如 Host 对同一个 BLE 连接发送:

复制代码
Packet 1
Packet 2
Packet 3

Controller 应该按这个顺序收到:

复制代码
Packet 1 → Packet 2 → Packet 3

不能变成:

复制代码
Packet 2 → Packet 1 → Packet 3

这对 L2CAP、ATT、GATT 层的数据重组和语义很重要。

但是不同 Connection_Handle 之间,规范这里强调的是"每个 individual Connection_Handle",也就是说,不同连接之间的数据调度可以由 Host/Controller 根据 buffer 和链路情况处理。


7. 4.1.1 Packet-based data flow control 具体讲什么

这一小节是重点。

它讲的是:

当使用 packet-based flow control 时,Host 和 Controller 怎么通过 buffer size 命令和 completed packets 事件来管理发送额度。


8. 初始化时,Host 要读取 Controller buffer size

对于支持 LE 的 Host,初始化时应发送:

复制代码
HCI_LE_Read_Buffer_Size command

这个命令用于读取 LE 传输使用的 HCI ACL Data buffer 信息。

它返回的重要参数包括:

复制代码
LE_ACL_Data_Packet_Length
Total_Num_LE_ACL_Data_Packets

可以理解为:

复制代码
LE_ACL_Data_Packet_Length:
每个 LE ACL Data Packet 最大能放多少字节数据,不包括 HCI ACL header。

Total_Num_LE_ACL_Data_Packets:
Controller 最多能同时缓存多少个 LE ACL Data Packet。

例如假设返回:

复制代码
LE_ACL_Data_Packet_Length = 251
Total_Num_LE_ACL_Data_Packets = 10

可以理解为:

复制代码
Controller 最多可以同时排队 10 个 LE ACL Data Packet
每个包最大数据长度是 251 字节

那么 Host 发送 LE ACL Data 时,就要按照这个能力控制发送。


9. 如果 Total_Num_LE_ACL_Data_Packets 返回 0 怎么办

规范里有一个特殊情况:

如果 Controller 同时支持 BR/EDR 和 LE,那么它可能在 HCI_LE_Read_Buffer_Size 里返回:

复制代码
Total_Num_LE_ACL_Data_Packets = 0

这表示:

复制代码
LE 不使用单独的 LE ACL buffer
Host 应该使用 BR/EDR 的 ACL buffer 信息来管理 LE ACL Data

这时 Host 应该再使用:

复制代码
HCI_Read_Buffer_Size command

来读取 BR/EDR buffer 相关信息。

不过规范也说:

复制代码
不支持 BR/EDR 的 Controller,不应在 Total_Num_LE_ACL_Data_Packets 返回 0。

也就是说,如果是纯 LE Controller,它不能告诉 Host:

复制代码
LE ACL buffer 数量为 0

因为纯 LE Controller 没有 BR/EDR buffer 可借用。


10. ISO 数据的 buffer size

截图里还讲到了 HCI ISO Data Packet。

如果 Host 支持通过 HCI 传输 isochronous data,那么初始化时也应发送:

复制代码
HCI_LE_Read_Buffer_Size command

这个命令的某些版本会返回 ISO 相关参数:

复制代码
ISO_Data_Packet_Length
Total_Num_ISO_Data_Packets

它们表示:

复制代码
ISO_Data_Packet_Length:
每个 HCI ISO Data Packet 的最大 buffer 大小。

Total_Num_ISO_Data_Packets:
Controller 最多可以同时缓存多少个 HCI ISO Data Packet。

注意截图中的 Note:

复制代码
ISO_Data_Packet_Length 和 Total_Num_ISO_Data_Packets 只有在使用 v2 或以上版本的 HCI_LE_Read_Buffer_Size command 时才可用。

对普通 BLE GATT 通信来说,ISO 不是重点。ISO 主要和 LE Audio、CIS/BIS 等等时数据有关。


11. BR/EDR Controller 初始化时读取 buffer size

对于 BR/EDR Controller,当启用 packet-based flow control 时,初始化时 Host 应发送:

复制代码
HCI_Read_Buffer_Size command

这个命令返回 BR/EDR 相关 buffer 信息,例如:

复制代码
HCI ACL Data Packet 最大长度
HCI Synchronous Data Packet 最大长度
Controller 可缓存的 ACL Data Packet 总数
Controller 可缓存的 Synchronous Data Packet 总数

这主要是经典蓝牙相关。

你如果主要研究 BLE,可以先知道它和 LE 的区别即可:

复制代码
LE:重点看 HCI_LE_Read_Buffer_Size
BR/EDR:重点看 HCI_Read_Buffer_Size

12. HCI_Number_Of_Completed_Packets event 的作用

这是这一节最重要的事件。

当至少存在一个连接时,Controller 使用:

复制代码
HCI_Number_Of_Completed_Packets event

来控制 Host 到 Controller 的数据流。

这个事件会告诉 Host:

复制代码
每个 Connection_Handle 上,自上一次事件以来,有多少个 HCI Data Packet 已经 completed

也就是告诉 Host:

复制代码
哪些连接上的多少个 packet 对应的 Controller buffer 已经释放

例如 Controller 上报:

复制代码
Connection_Handle = 0x0001
Num_Completed_Packets = 3

意思是:

复制代码
连接 0x0001 上有 3 个 HCI Data Packet 已经完成
Host 可以把对应 buffer 额度加回 3

如果事件中有多个 handle,则可能是:

复制代码
Connection_Handle = 0x0001,完成 3 个包
Connection_Handle = 0x0002,完成 2 个包
Connection_Handle = 0x0003,完成 1 个包

Host 根据这个事件更新自己的 buffer 计数。


13. Host 如何选择后续发往哪些 Connection_Handle

规范说:

复制代码
The Host chooses the Connection_Handles for the following HCI Data packets...

意思是:

Host 根据以下信息选择接下来要给哪些连接发送 HCI Data Packet:

复制代码
HCI_Number_Of_Completed_Packets event 返回的信息
HCI_LE_Read_Buffer_Size command 返回的信息
HCI_Read_Buffer_Size command 返回的信息

也就是说,Host 自己要做调度。

例如有三个 BLE 连接:

复制代码
设备 A 有很多数据要发
设备 B 有少量数据要发
设备 C 暂时没数据

Host 要根据各连接的数据队列、Controller buffer 空间、完成事件,决定后续先发哪个连接的数据。


14. Host 每发送一个 HCI Data Packet,就要减少可用 buffer 计数

规范中说:

复制代码
Every time it has sent an HCI Data packet, the Host shall reduce its count of the free buffer space...

意思是:

Host 每发送一个 HCI Data Packet,都必须把对应类型的 Controller 可用 buffer 计数减 1。

例如初始化时知道:

复制代码
Total_Num_LE_ACL_Data_Packets = 10

Host 当前认为可用 LE ACL buffer 是 10。

发送一个 LE ACL Data Packet 后:

复制代码
free_buffer = 9

再发一个:

复制代码
free_buffer = 8

直到:

复制代码
free_buffer = 0

此时不能继续发。

必须等待 Controller 通过 HCI_Number_Of_Completed_Packets event 释放额度。


15. Host 每发送一个 HCI ISO Data Packet,也要减少 ISO buffer 计数

对于 ISO 数据也是类似。

Host 每发送一个 HCI ISO Data Packet,就要把对应 logical link type 的 free buffer 计数减 1。

这里提到的 logical link type:

复制代码
LE-S
LE-F

这是 ISO 相关的逻辑链路类型。

普通 BLE GATT 通信可以先不深入。


16. Host 不能在 free buffer 为 0 时继续发送

这是非常关键的一条规则:

复制代码
The Host shall not send an HCI Data packet or HCI ISO Data packet to the Controller when its count of the free buffer space for the corresponding link type is zero.

意思是:

当对应链路类型的可用 buffer 数量为 0 时,Host 不得继续向 Controller 发送 HCI Data Packet 或 HCI ISO Data Packet。

这就是 flow control 的硬规则。

可以简化成:

复制代码
free_buffer > 0:可以发
free_buffer = 0:不能发,必须等 completed event

17. Controller 完成 packet 后,要发送 completed packets event

当 Controller 完成一个或多个 HCI Data Packet 或 HCI ISO Data Packet 后,应发送:

复制代码
HCI_Number_Of_Completed_Packets event

通知 Host。

并且要一直发送,直到最终报告所有 pending 的 HCI Data Packet 都已经完成。

不过发送频率是厂商自定义的:

复制代码
The frequency at which this event is sent is manufacturer specific.

也就是说,不同 Controller 可能上报频率不同。

有的可能很频繁上报,有的可能批量上报。

这会影响 Host 侧发送节奏和吞吐表现。


18. 4.1.2 Data-block-based data flow control 讲什么

这一节讲另一种流控模式:

复制代码
data-block-based data flow control

当启用这种模式时,初始化时 Host 要发送:

复制代码
HCI_Read_Data_Block_Size command

这个命令返回:

复制代码
最大 HCI ACL Data Packet 长度
Controller 可缓存的 HCI ACL Data Packet 总数

Controller 使用另一个事件来通知 Host:

复制代码
HCI_Number_Of_Completed_Data_Blocks event

它的作用类似于 packet-based 中的 HCI_Number_Of_Completed_Packets event,只是它按 data block 管理。

Host 同样要根据这个事件计算当前 buffer 使用情况。

规则也类似:

复制代码
Host 每发送一个 HCI Data Packet,可用 buffer 减 1
Host 收到 completed data blocks 事件,可用 buffer 增加
free buffer 为 0 时,Host 不能继续发送

19. Packet-based 和 Data-block-based 的区别

可以这样理解:

复制代码
Packet-based:
按 HCI Data Packet 数量进行流控。

Data-block-based:
按 Controller 内部 data block buffer 进行流控。

对于你的 BLE 学习重点来说,主要记住:

复制代码
LE traffic 只能使用 packet-based flow control。

所以 BLE GATT 数据、BLE L2CAP 数据,重点看:

复制代码
HCI_LE_Read_Buffer_Size
HCI_Number_Of_Completed_Packets
free buffer count
Connection_Handle

20. 用一个 BLE GATT 大数据发送例子理解

假设手机 Host 要给 BLE 模块发送 OTA 数据。

上层流程是:

复制代码
App
  ↓
GATT Write Without Response
  ↓
ATT
  ↓
L2CAP
  ↓
HCI ACL Data Packet
  ↓
Controller buffer
  ↓
Link Layer 空口发送
  ↓
对端 BLE 模块

Controller 初始化时告诉 Host:

复制代码
LE_ACL_Data_Packet_Length = 251
Total_Num_LE_ACL_Data_Packets = 8

Host 内部维护:

复制代码
free_buffer = 8

然后 Host 开始发送:

复制代码
发送第 1 个 HCI ACL Data Packet:free_buffer = 7
发送第 2 个 HCI ACL Data Packet:free_buffer = 6
发送第 3 个 HCI ACL Data Packet:free_buffer = 5
...
发送第 8 个 HCI ACL Data Packet:free_buffer = 0

此时 Host 不能继续往 Controller 发。

等 Controller 在某几个连接事件中把部分数据发出去后,上报:

复制代码
HCI_Number_Of_Completed_Packets event
Connection_Handle = 0x0001
Num_Completed_Packets = 4

Host 更新:

复制代码
free_buffer = 4

然后 Host 可以继续发送 4 个包。

这就是 Host → Controller 数据流控的基本逻辑。


21. 和 App 层现象的关系

在 iOS / Android App 开发中,一般直接看不到 HCI buffer 计数,但它会影响你看到的现象。

例如 BLE 透传、OTA、大文件发送中:

复制代码
writeWithoutResponse 不能无限制调用
发送太快可能失败
系统会限制写入节奏
不同手机吞吐不同
不同蓝牙芯片吞吐不同
连接间隔影响吞吐
MTU / Data Length / PHY 影响吞吐

背后除了 GATT/ATT、L2CAP、Link Layer 之外,也和 HCI flow control 有关系。

App 层虽然不能直接操作 HCI_Number_Of_Completed_Packets event,但系统蓝牙协议栈会在底层根据这些事件控制发送节奏。


22. 这部分最关键的信息总结

22.1 Host 不能无限制向 Controller 发 HCI Data Packet

Controller buffer 有限,Host 必须根据 buffer 状态发送。


22.2 Host 初始化时要读取 Controller buffer 能力

BLE 主要看:

复制代码
HCI_LE_Read_Buffer_Size

BR/EDR 主要看:

复制代码
HCI_Read_Buffer_Size

22.3 Host 每发送一个包,可用 buffer 计数减 1

Host 内部需要维护类似这样的计数:

复制代码
free_buffer_count

发送一个 HCI Data Packet 后:

复制代码
free_buffer_count -= 1

22.4 Controller 完成包后,通过事件告诉 Host

主要事件是:

复制代码
HCI_Number_Of_Completed_Packets event

它告诉 Host:

复制代码
某个 Connection_Handle 上完成了多少个 HCI Data Packet

Host 收到后增加可用 buffer 计数。


22.5 free buffer 为 0 时,Host 不得继续发送

这是流控的硬规则:

复制代码
free_buffer_count == 0

时,Host 不能继续发对应类型的 HCI Data Packet。


22.6 completed 表示 Controller buffer 可以释放,不等于对端应用层处理完成

这个 completed 是 Controller buffer 管理意义上的完成。

它不是 GATT 层确认,也不是业务层确认。


22.7 同一个 Connection_Handle 上的数据要保持顺序

同一条连接上的 HCI Data Packet 必须按 Host 提供的顺序到达 Controller。

这对上层 L2CAP / ATT 数据的正确性很重要。


22.8 BLE 重点关注 packet-based flow control

因为 LE traffic 只能使用 packet-based flow control。

所以你现在重点掌握:

复制代码
HCI_LE_Read_Buffer_Size
HCI_Number_Of_Completed_Packets
Connection_Handle
free buffer count
packet completed

23. 可以形成的核心理解

这一节可以总结成一句话:

复制代码
Host to Controller data flow control 的本质,是 Host 先读取 Controller 的数据 buffer 能力,然后每发送一个 HCI Data Packet 就消耗一个 buffer 额度,Controller 完成数据包后通过 HCI_Number_Of_Completed_Packets event 归还额度;当可用额度为 0 时,Host 不能继续发送。

对 BLE 来说,重点是:

复制代码
LE 数据走 packet-based flow control。
HCI_LE_Read_Buffer_Size 用来读取 LE buffer 能力。
HCI_Number_Of_Completed_Packets event 用来通知哪些连接上的 packet 已经完成。
Connection_Handle 用来区分数据属于哪条连接。