
这部分是 4.4 Command flow control,讲的是:
Host 向 Controller 发送 HCI Command 时,也需要流控。Host 不能无限制地连续发送 HCI Command,而是要根据 Controller 返回的 Num_HCI_Command_Packets 参数来判断还能不能继续发命令。
前面 4.1、4.2 讲的是 HCI Data Packet 的流控,也就是 ACL / ISO 数据包的流控。
这一节讲的是 HCI Command Packet 的流控。
1. 这部分整体表述了哪些知识
这一节主要讲了 7 个知识点:
1. Host 上电或 reset 后,最多只能先发送 1 个未完成的 HCI Command
2. Controller 通过 Command Complete / Command Status 事件告诉 Host 还能发多少个命令
3. 这个数量由 Num_HCI_Command_Packets 参数表示
4. Controller 可以允许 Host 同时发送多个 HCI Command
5. Controller 接收命令的顺序和完成命令的顺序不一定一致
6. HCI Command 有两种执行模式:快速完成型、耗时 pending 型
7. 如果连接句柄被删除,相关 pending command 的完成事件也要在断开事件前返回
核心就是一句话:
Command flow control 的本质,是 Controller 通过 Num_HCI_Command_Packets 控制 Host 当前还能发送多少个 HCI Command。
2. 上电或 Reset 后,Host 最多只能先发 1 个 HCI Command
原文第一段说:
On initial power-on, and after a reset, the Host shall send a maximum of one outstanding HCI Command packet until an HCI_Command_Complete or HCI_Command_Status event has been received.
意思是:
刚上电或者 reset 之后,Host 在收到第一个 HCI_Command_Complete 或 HCI_Command_Status 事件之前,最多只能发送 1 个 outstanding HCI Command。
这里的 outstanding HCI Command 指的是:
Host 已经发送给 Controller
但是 Controller 还没有通过 Command Complete 或 Command Status 给出响应的命令
也就是说,上电或 reset 后不能这样:
Host 连续发送 Command 1
Host 连续发送 Command 2
Host 连续发送 Command 3
而应该是:
Host 发送 Command 1
等待 Controller 返回 Command Complete 或 Command Status
再根据事件里的 Num_HCI_Command_Packets 判断还能发几个
这是为了避免 Controller 初始化阶段命令队列还没准备好时,Host 一口气塞太多命令。
3. HCI_Command_Complete 和 HCI_Command_Status 都会携带 Num_HCI_Command_Packets
原文第二段说:
The HCI_Command_Complete and HCI_Command_Status events contain a parameter called Num_HCI_Command_Packets
意思是:
HCI_Command_Complete 和 HCI_Command_Status 事件中都有一个参数,叫 Num_HCI_Command_Packets。
这个参数非常关键。
它表示:
Host 当前还允许发送多少个 HCI Command packet 给 Controller。
例如 Controller 返回:
Num_HCI_Command_Packets = 1
表示 Host 当前可以再发送 1 个 HCI Command。
如果返回:
Num_HCI_Command_Packets = 3
表示 Host 当前可以连续发送最多 3 个 HCI Command。
如果返回:
Num_HCI_Command_Packets = 0
表示 Host 暂时不能再发送 HCI Command,必须等待后续事件把这个数量恢复到大于 0。
4. Controller 可以允许多个 HCI Command 同时 pending
规范说:
The Controller may buffer one or more HCI Command packets
意思是:
Controller 可以缓存一个或多个 HCI Command packet。
也就是说,不同 Controller 的命令缓存能力可能不一样。
有的 Controller 可能只允许:
Num_HCI_Command_Packets = 1
表示一次只处理一个命令。
有的 Controller 可能允许:
Num_HCI_Command_Packets > 1
表示 Host 可以连续发送多个命令,Controller 内部排队处理。
这就是为什么 Host 不能自己假设"我可以随便发",而是必须根据 Controller 返回的 Num_HCI_Command_Packets 来控制。
5. Controller 按接收顺序开始执行,但不一定按开始顺序完成
原文说:
the Controller shall start performing the commands in the order in which they are received.
意思是:
Controller 应该按照收到命令的顺序开始执行命令。
例如 Controller 收到:
Command A
Command B
Command C
Controller 应该按顺序开始处理:
先开始 A
再开始 B
再开始 C
但是后面又说:
The Controller can start performing a command before it completes previous commands. Therefore, the commands do not always complete in the order they are started.
意思是:
Controller 可以在前一个命令还没有完成时,就开始执行后一个命令。因此,命令不一定按照开始执行的顺序完成。
这点很重要。
例如:
Command A:LE Create Connection,可能要等很久
Command B:Read Local Version Information,很快完成
Controller 可能先开始 A,然后又开始 B,但 B 先完成。
所以完成顺序可能是:
Command B 完成
Command A 后面才完成
这说明 Host 在处理 HCI 事件时,不能简单认为:
下一个 Command Complete 一定对应我最早发出去的命令
而应该根据事件里的:
Command_Opcode
判断这个完成事件对应的是哪个命令。
6. Command Opcode = 0x0000 的特殊含义
原文第三段讲了一个特殊机制:
Command Opcode set to 0x0000
一般 HCI_Command_Complete 或 HCI_Command_Status 事件会带有 Command Opcode,用来说明这个事件对应哪个命令。
但是如果:
Command Opcode = 0x0000
它不是对应 Host 发送过的某个普通命令,而是 Controller 用来告诉 Host:
我现在可以接收更多 HCI Command 了
也就是说,这是一个特殊事件。
它的作用是:
单独更新 Num_HCI_Command_Packets
例如 Controller 之前返回:
Num_HCI_Command_Packets = 0
Host 停止发送命令。
后面 Controller 可以发送一个:
HCI_Command_Complete
Command Opcode = 0x0000
Num_HCI_Command_Packets = 1
意思是:
现在我又可以接收 1 个 HCI Command 了
这不是某个具体命令完成,而是 Controller 主动恢复命令额度。
7. Num_HCI_Command_Packets = 0 时,Host 必须停止发命令
原文说:
If the Controller generates an HCI_Command_Complete or HCI_Command_Status event with Num_HCI_Command_Packets set to zero, then the Host shall stop sending commands.
意思是:
如果 Controller 生成的 Command Complete 或 Command Status 事件里,Num_HCI_Command_Packets 为 0,那么 Host 必须停止发送命令。
这就是 Command flow control 的硬规则。
可以理解为:
Num_HCI_Command_Packets > 0:Host 可以继续发送命令
Num_HCI_Command_Packets = 0:Host 必须停止发送命令
直到 Controller 后续通过 Command Complete / Command Status 事件把它改成大于 0。
8. HCI Command 的结果通过 Event 返回
原文说:
HCI commands may take different amounts of time to be completed.
Therefore, the results of commands will be reported back to the Host in the form of an event.
意思是:
HCI Command 完成所需时间可能不同,所以命令结果会通过 HCI Event 返回给 Host。
这也是 HCI 的基本交互方式:
Host 发送 HCI Command
Controller 执行命令
Controller 通过 HCI Event 返回结果
最常见的两个事件就是:
HCI_Command_Complete event
HCI_Command_Status event
9. 大多数命令完成后返回 HCI_Command_Complete
规范说:
for most HCI commands the Controller will generate the HCI_Command_Complete event when a command is completed.
意思是:
大多数 HCI Command 完成后,Controller 会返回 HCI_Command_Complete event。
例如很多读取类、配置类命令通常都是这样:
Host → Controller:HCI_Read_Local_Version_Information
Controller → Host:HCI_Command_Complete
或者 BLE 中一些配置命令:
Host → Controller:HCI_LE_Set_Advertising_Data
Controller → Host:HCI_Command_Complete
Command Complete 的含义是:
这个命令已经执行完成,结果也在这个事件里返回。
它通常包含:
Num_HCI_Command_Packets
Command_Opcode
Return_Parameters
其中 Return Parameters 里通常会有:
Status
10. 为什么需要超时机制
原文说:
For enabling the Host to detect errors on the HCI-Transport Layer, there needs to be a timeout between the transmission of the Host's command and the reception of the Controller's response...
意思是:
为了让 Host 能发现 HCI Transport Layer 上的错误,Host 发出命令后,需要设置一个等待 Controller 响应的超时时间。
也就是说,Host 不能发了命令后无限等。
如果 Controller 没有返回:
HCI_Command_Complete
或者:
HCI_Command_Status
Host 应该能判断:
HCI transport 可能出问题了
Controller 没响应
命令丢了
串口/USB/SDIO 通道异常
Controller 死机
规范建议默认超时时间是:
1 秒
不过它也说,这个时间强依赖具体 HCI Transport Layer,并且还和 Controller 内部未处理的命令队列数量有关。
所以这里不是说所有命令都必须 1 秒内完成,而是说:
Host 应该有一个检测命令响应异常的 timeout 机制。
11. HCI Command 有两种执行模式
这一节后半部分非常重要,它把 HCI Command 执行模式分成两类。
11.1 第一类:快速完成型命令
原文说:
For the first type, used by those commands which are expected to complete quickly and are carried out entirely in the local Controller...
意思是:
第一类命令是预期很快完成、并且完全在本地 Controller 内部执行的命令。
这类命令执行完后,Controller 直接返回:
HCI_Command_Complete event
例如:
读取本地版本
读取本地支持特性
设置广播数据
设置扫描参数
读取 buffer size
它们一般不需要和对端设备交互,只是在本机 Controller 内部完成。
流程是:
Host
↓ HCI Command
Controller
↓ 本地执行
Host
↑ HCI_Command_Complete event
这类命令没有明显 pending 阶段。
11.2 第二类:耗时 pending 型命令
原文说:
For the second type, used by those commands that are expected to take a significant length of time, usually because they involve interaction with a peer device...
意思是:
第二类命令预计会花比较长时间,通常是因为它们需要和对端设备交互。
这类命令不是直接返回最终结果。
它的流程是:
Host 发送 HCI Command
↓
Controller 先返回 HCI_Command_Status event
表示命令已收到,并开始处理
↓
命令进入 pending 状态
↓
Controller 和对端设备进行交互
↓
最终过程完成后,Controller 再返回一个 completion event
注意这里的 completion event 不一定叫 HCI_Command_Complete。
它可能是该命令对应的特定事件。
例如 BLE 建立连接:
Host → Controller:
HCI_LE_Create_Connection command
Controller → Host:
HCI_Command_Status event
表示命令已接受,开始建立连接
后续 Controller → Host:
HCI_LE_Connection_Complete event
表示连接过程最终完成
这里最终完成事件不是 HCI_Command_Complete,而是:
HCI_LE_Connection_Complete event
12. pending 状态是什么意思
原文说:
Between the times that these two events are generated ... this type of command is described as "pending".
意思是:
从 Controller 生成 HCI_Command_Status,到生成最终 completion event 之间,这个命令处于 pending 状态。
例如:
LE Create Connection
流程是:
Host 发送 LE Create Connection
Controller 返回 Command Status
↓
此时命令 pending
↓
Controller 正在扫描、发起连接、等待对端响应
↓
最终返回 LE Connection Complete
在 pending 期间,命令还没有最终完成。
这就是为什么很多连接相关命令不能简单看 Command Status = Success 就认为功能已经成功。
Command Status = Success 只表示:
Controller 接受了这个命令,并开始执行。
不表示:
连接已经成功建立。
真正连接成功要看后续:
HCI_LE_Connection_Complete event
并且还要看该事件里的 Status。
13. 如果第二类命令没有开始执行,会怎样
原文说:
If a command of the second type does not begin to execute ... the HCI_Command_Status event shall be returned with the appropriate error code in the Status parameter and no completion event is generated.
意思是:
如果第二类命令没有真正开始执行,比如参数错误,或者当前不允许执行,那么 Controller 会返回带错误码的 HCI_Command_Status event,并且不会再生成后续 completion event。
例如 Host 发起连接命令,但当前 Controller 状态不允许:
Host → HCI_LE_Create_Connection
Controller → HCI_Command_Status
Status = Command Disallowed
这种情况下,命令没有进入真正执行流程,因此不会再有:
HCI_LE_Connection_Complete event
所以 Host 处理第二类命令时要分清楚:
Command Status 成功:
命令已开始,后续等 completion event
Command Status 失败:
命令没有开始,不会再有 completion event
14. Connection_Handle 被删除时,pending command 怎么处理
最后一段说:
When a Connection_Handle is deleted and there are pending commands relating to that Connection_Handle...
意思是:
当某个 Connection_Handle 被删除,并且还有与这个 Connection_Handle 相关的 pending command 时,Controller 应该把这些 pending command 的完成事件返回给 Host。
这里的 Connection_Handle is deleted,通常就是连接断开后,这个 handle 不再有效。
规范要求:
每个相关 pending command 都要返回一个 completion event
每个 event 都要有非 0 的 Status
并且这些 completion event 要在连接删除事件之前返回
也就是说,如果一条连接要断开,而这条连接上还有一些未完成命令,Controller 不能直接让这些 pending command 消失。
它需要先告诉 Host:
这些命令失败了 / 没完成
然后再告诉 Host:
连接已经断开,Connection_Handle 删除了
顺序是:
pending command 的 completion event,Status 非 0
↓
HCI_Disconnection_Complete event
规范明确说:
No events for that Connection_Handle shall be sent after the event indicating the deletion of the Connection_Handle.
意思是:
表示 Connection_Handle 删除的事件之后,不能再发送这个 Connection_Handle 相关的事件。
也就是说,连接断开事件是这个 handle 的"最后通知边界"。
15. Command Complete 和 Command Status 的区别
这部分最值得重点理解的是这两个事件的区别。
15.1 HCI_Command_Complete
适合快速完成型命令。
含义是:
命令已经完成,结果在这个事件里。
流程:
Host → Command
Controller → Command Complete
例如:
HCI_LE_Set_Advertising_Data
HCI_LE_Read_Buffer_Size
HCI_Read_Local_Version_Information
15.2 HCI_Command_Status
适合耗时 pending 型命令。
含义是:
Controller 已收到命令,并告知这个命令是否开始执行。
如果 Status 成功:
命令开始执行,后面还会有 completion event。
如果 Status 失败:
命令没有开始执行,后面不会有 completion event。
流程:
Host → Command
Controller → Command Status
Controller 后续 → 特定 completion event
例如:
HCI_LE_Create_Connection
一般是:
Host → HCI_LE_Create_Connection
Controller → HCI_Command_Status
Controller → HCI_LE_Connection_Complete
16. Command flow control 和 Data flow control 的区别
前面 4.1、4.2 是数据流控:
HCI ACL Data Packet
HCI ISO Data Packet
这一节是命令流控:
HCI Command Packet
二者不要混淆。
| 类型 | 控制对象 | 典型机制 |
|---|---|---|
| Data flow control | HCI ACL / ISO Data Packet | Buffer Size + Number Of Completed Packets |
| Command flow control | HCI Command Packet | Num_HCI_Command_Packets |
可以简单理解为:
Data flow control:控制数据包别把 buffer 塞爆
Command flow control:控制命令包别把 Controller 命令队列塞爆
17. 用 BLE 广播命令理解
例如 Host 要开启 Legacy Advertising,可能发送:
HCI_LE_Set_Advertising_Parameters
HCI_LE_Set_Advertising_Data
HCI_LE_Set_Scan_Response_Data
HCI_LE_Set_Advertising_Enable
这些通常是本地 Controller 配置类命令。
每个命令完成后,Controller 返回:
HCI_Command_Complete
Host 要根据每个事件里的:
Num_HCI_Command_Packets
决定能不能继续发下一个命令。
如果 Controller 只允许一个 outstanding command,那么流程是:
Host → Set Advertising Parameters
Controller → Command Complete
Host → Set Advertising Data
Controller → Command Complete
Host → Set Scan Response Data
Controller → Command Complete
Host → Set Advertising Enable
Controller → Command Complete
如果 Controller 允许多个 outstanding command,那么 Host 可能可以连续发多个,但仍然必须受 Num_HCI_Command_Packets 限制。
18. 用 BLE 建立连接理解
例如 Host 发:
HCI_LE_Create_Connection
这是一个耗时命令,因为它涉及空口扫描、发起连接、等待对端响应。
流程通常是:
Host → HCI_LE_Create_Connection command
Controller → HCI_Command_Status event
如果 Command Status 里:
Status = 0x00 Success
只表示:
Controller 接受了这个命令,开始建立连接。
此时命令 pending。
后续才会收到:
HCI_LE_Connection_Complete event
如果这个事件里:
Status = 0x00 Success
才表示连接建立成功。
如果:
Status != 0x00
表示连接过程最终失败。
所以不能把:
Command Status = Success
误解为:
连接已经成功。
这是 HCI 学习里非常重要的点。
19. 这部分关键信息总结
19.1 Host 不能无限制发送 HCI Command
上电或 Reset 后,在收到 Command Complete / Command Status 之前,Host 最多只能发 1 个 outstanding command。
19.2 Num_HCI_Command_Packets 是命令流控的核心参数
它表示:
Host 当前还能发送多少个 HCI Command packet。
如果它为 0,Host 必须停止发送命令。
19.3 Command Opcode = 0x0000 是特殊事件
它不对应某个 Host 发出的命令,而是 Controller 用来单独通知 Host:
我现在可以接收更多 HCI Command 了。
19.4 Controller 开始执行命令的顺序和完成顺序不一定一致
Controller 按接收顺序开始执行命令,但完成顺序可能不同。
Host 要根据事件里的 Command_Opcode 判断对应哪个命令。
19.5 大多数本地快速命令返回 Command Complete
比如读取本地信息、设置本地参数等,通常完成后直接返回:
HCI_Command_Complete
19.6 耗时命令通常先返回 Command Status,再返回 completion event
比如连接相关命令,可能流程是:
HCI Command
↓
HCI_Command_Status
↓
specific completion event
19.7 Command Status 成功不等于最终操作成功
Command Status = Success 只是表示命令已经开始执行。
最终是否成功,要看后续 completion event。
19.8 Command Status 失败时,不会再有 completion event
如果命令因为参数错误、当前状态不允许等原因没有开始执行,Controller 返回错误的 Command Status,并且不会再生成后续完成事件。
19.9 Connection_Handle 删除前,相关 pending command 要先返回失败完成事件
如果某个连接断开,Controller 要先对这个 handle 上的 pending command 返回非 0 状态的完成事件,再发送表示 handle 删除的事件。
20. 最核心的一句话
这一节可以总结为:
Command flow control 的核心,是 Host 根据 Controller 在 HCI_Command_Complete / HCI_Command_Status 事件中返回的 Num_HCI_Command_Packets 来控制 HCI Command 的发送数量;快速命令通常直接返回 Command Complete,耗时命令通常先返回 Command Status 并进入 pending,最终再通过特定 completion event 告诉 Host 操作结果。
对 BLE 学习最重要的是:
不要把 Command Status = Success 理解成操作最终成功;
它很多时候只表示 Controller 接受命令并开始执行。
真正结果要看后续的 completion event。