HCI 功能规范【4.4. Command flow control】

这部分是 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。
相关推荐
欢乐熊嵌入式编程18 天前
WIFI通信协议全解析18: ESP32 作为 AP 热点:打造自己的“微型路由器”(附完整实战代码)
物联网·wifi·esp32·蓝牙·wifi协议·ap热点
YF02111 个月前
Android BLE 信号强度获取与 底层原理深度解析
android·蓝牙
Refrain_zc1 个月前
无触摸屏场景下的蓝牙交互:Android 纯按键蓝牙扫描配对与 A2DP/Headset 连接
java·蓝牙
sweet丶1 个月前
iOS 蓝牙开发深入总结
ios·蓝牙
桑榆肖物1 个月前
ImprovWifi 跨平台传输层设计:把协议层做薄,把宿主层做稳
嵌入式硬件·wifi·.net·ble
liuniu08181 个月前
GAP-初始
蓝牙·gap
jiang_bluetooth1 个月前
蓝牙典型射频架构剖析
蓝牙·信号处理·射频·pa·lna
一苇以航322 个月前
LE Audio低功耗蓝牙音频详解 (三)
音视频·蓝牙·ble·le audio
jiang_bluetooth2 个月前
蓝牙6.0 Channel Sounding 分数时延估计原理详解
蓝牙·信号处理·蓝牙测距·channel sound·蓝牙6.0