〇、序言
HCI (Host Controller Interface) 数据包是蓝牙通信中主机与控制器之间交换信息的基本单元。HCI 定义了主机与蓝牙控制器之间如何交换命令、数据和事件。它提供了一种标准的接口和协议,使得蓝牙设备(如手机、计算机、耳机、音响等)能够通过主机控制器接口进行通信。
蓝牙 HCI 数据包通常分为以下几类:
#define HCI_COMMAND_DATA_PACKET 0x01
#define HCI_ACL_DATA_PACKET 0x02
#define HCI_SCO_DATA_PACKET 0x03
#define HCI_EVENT_PACKET 0x04
一、HCI Command Packet(HCI命令包 0x01)
这个数据包的作用是通过命令向Controller传输操作指令,Controller根据命令执行特定操作,并返回相应的事件信息。如果是厂商特定的调试命令,Controller会进行特殊处理。
1、数据包格式(参照Core spec)
2、具体数据位详解
① Op_Code: Size: 2 Octets
Op_Code
是 命令操作码(Operation Code),它用于标识命令类型。这个字段占用 2 字节(16 位),由两个部分组成:
- OGF(Opcode Group Field):6 位,用于指定命令分组的类型。
- OCF(Opcode Command Field):10 位,表示特定命令在该命令分组下的编号。
② Parameter_Total_Length: Size: 1 Octet
Parameter_Total_Length
是 参数总长度,它指定了命令中所有参数的总长度。这个字段占用 1 字节(8 位),其值表示命令后续参数的总字节数。
注意:
- 这个字段表示的是 所有参数的字节长度总和,而不是参数的个数。
- 例如,如果命令有两个参数,第一个参数占用 2 字节,第二个参数占用 3 字节,那么
Parameter_Total_Length
的值为 5。
③ Parameter 0 - N: Size: Parameter Total Length
这个部分是 命令参数,其大小由 Parameter_Total_Length
字段指定。每个命令都有一个特定的参数集合,参数的个数和每个参数的大小根据命令类型而不同。
- Parameter 0 - N 代表命令的所有参数,具体的参数大小和格式会根据命令的类型进行定义。
- 每个命令的参数数量、大小以及每个参数的含义在蓝牙协议规范中是事先定义的。④以实际数据包举例
④ 结合实际数据包分析
我通过抓取了btsnoop(btsnoop用于记录Host与Controller的通信)来详细分析一包HCI Command Packets。
高亮部分的左边第一个字节0x01代表这一包数据类型为Command Packet。
高亮部分的从左边数第二三字节0x0c1a(Frontline这里显示低位在前高位在后,二进制为0000 1100 0001 1010),其中从高位开始算15~10Bit为OGF(Opcode Group Field),其值为0x03,通过查阅core spec得知是归属于CONTROLLER &BASEBAND COMMANDS组,这类命令是用来访问和控制蓝牙硬件的各种功能。
接着从高位开始的9~0Bit为OCF(Opcode Command Field),其值为0x001a,同样通过查阅core spec得知是Write Scan Enable Command,该命令的作用是 写入扫描使能参数,即控制设备是否启用扫描功能。
高亮部分的从左边数第四字节0x01代表Parameter的字节总长度。
高亮部分的从左边数第五字节0x02代表实际的Parameter参数值,通过上图可知0x02代表Inquiry Scan disabled,Page Scan enabled。
综上所述这条命令HCI_Write_Scan_Enable
用于设置蓝牙设备的扫描状态,禁用询问扫描(Inquiry Scan),并启用配对扫描(Page Scan)。
二、 HCI ACL Data Packet(HCI ACL数据包 0x02)
这个数据包的主要作用是进行主机与控制器之间的数据交换,具有不同的刷新策略来控制数据包的传输和处理。
1、数据包格式(参照Core spec)
2、具体数据包详解
① Handle: 12 Bits
Handle
是一个 12 位的字段,用于标识连接句柄。在不同的情景下,这个字段有不同的用途。
- Connection_Handle 用于在主控制器(Primary Controller)上发送数据包。
- Logical_Link_Handle 用于在AMP 控制器上发送数据包。
在接收端(从 AMP 控制器到主机),这个字段的最低 8 位是Physical_Link_Handle,而最高的 4 位被保留供未来使用。
② Packet_Boundary_Flag: 2 Bits
这个标志字段用于标识数据包边界的类型,特别是在分片和数据包传输中非常重要。它包含以下几种值:
- 00b:表示一个非自动刷新的数据包的开始,即更高层消息的第一部分。例如,L2CAP PDU 的开始。
- 01b:表示继续分片,即高层消息的后续分片。
- 10b:表示自动刷新的数据包的开始,即 L2CAP PDU 的开始部分。
- 11b:表示一个完整的 L2CAP PDU,并且是可自动刷新的。
根据不同的传输方向(从Host到Controller,或从Controller到Host),这些数据包的传输规则和允许的传输场景是不同的。例如,非自动刷新的数据包只能在Host到Controller的传输中使用,而自动刷新的数据包只允许从Controller到Host传输。
③ Broadcast_Flag: 2 Bits
这个标志字段用于指示是否为广播包,它的作用如下:
-
从Host到Controller:
- 00b:没有广播,仅限点对点通信(这种情况适用于 AMP)。
- 01b:活动从设备广播,即数据包会被发送给所有活动的从设备,并可能被处于 sniff 模式的从设备接收。
- 10b 和 11b:保留给未来使用。
-
从Controller到Host:
- 同样的值含义,表明广播包的类型,是否会广播到所有活动的从设备等。
注意:
- 处于sniff模式的从设备可能会接收到广播包,具体取决于它是否在相应的 sniff 时隙内接收到数据包。
④ Data_Total_Length: 2 Octets
Data_Total_Length
字段用于指示数据部分的总长度(以字节为单位),它占用 2 字节。其作用是告诉接收方数据包的长度,便于解析和处理。
⑤ Data: x Octets
LE-U 逻辑连接:是为满足低功耗且单向数据传输的需求而设计的,其数据部分的长度最大为27字节,不包括数据包的头部(即 HCI ACL Data Packet Header)。
其他连接类型(如 AMP 逻辑连接):AMP(Alternate MAC/PHY)逻辑连接上的数据包可以更大一些,因为它不受LE-U 逻辑连接数据包长度限制的影响。所有通过 AMP 逻辑连接传输的数据包都受到自动刷新定时器的影响,但最大有效载荷长度会根据设备和使用的协议而有所不同。
⑥ 结合实际数据包分析
我通过抓取了btsnoop(btsnoop用于记录Host与Controller的通信)来详细分析一包HCI ACL Data Packets。
高亮部分的左边第一个字节0x02代表这一包数据类型为ACL Data Packet。
高亮部分的左边第二三字节0x0001(Frontline这里显示低位在前 高位在后,二进制为0000 0000 0000 0001),其中从低位往高位开始算的0~11Bit为Handle,其值为0x001,代表一个有效的连接句柄。
接着从低位往高位开始算的12~13Bit为PB flag,其值为00b,这表示这是一个完整的数据包,没有分片
接着从低位往高位开始算的14~15Bit为BC flag,其值为00b,这表示这是一个点对点通信数据包,而不是广播包。
高亮部分的左边第四个字节0x000a(Frontline这里显示低位在前 高位在后),这代表后面data字段的数据长度为10个字节
高亮部分最后十个字节为有效data数据,此段data数据为L2CAP层数据,具体解析不在本文档中分析。
三、HCI Synchronous Data Packet(HCI同步数据包 0x03)
这个数据包主要用于Host和Controller之间传输同步数据。SCO (Synchronous Connection-Oriented) 和 eSCO (Extended Synchronous Connection-Oriented) 是蓝牙中的两种同步连接模式,专门用于音频和实时数据传输,它们通常用于语音传输、音频流以及其他低延迟、稳定的数据流。
(目前笔者还未接触到该数据包,如若在后续的工作内容中接触到了此类数据包,届时会来更新这一部分)
四、HCI Event Packet(HCI事件包 0x04)
HCI事件数据包是由Controller用来在事件发生时通知Host的。Host必须能够接收包含最多255个字节(octet,即字节)数据的HCI事件数据包,这里的数据量不包括HCI事件数据包的头部。
1、数据包格式(参照Core spec)
2、具体数据位详解
① Event_Code: 1 Octet
Event_Code 是一个 1 字节(8位)的字段,用于唯一标识不同类型的事件。Core spec有对于不同事件有。
- 用途:标识事件类型。
- 取值范围:0x00 到 0xFF(十六进制),其中 0xFF 保留用于供应商特定的调试事件。
② Parameter_Total_Length: 1 Octet
Parameter_Total_Length 字段表示该数据包中所有事件参数的总长度,以字节为单位。这个信息对于正确解析数据包至关重要。
- 用途:指示数据包中参数的总大小。
- 大小:1 字节(8位)。
③ Event_Parameter 0 - N: Variable Length
Event_Parameter 字段包含与特定事件相关的参数。每个事件都有其独特的参数集和参数大小,这些参数在事件定义中指定。
- 用途:包含与事件相关的数据。
- 大小:根据 Parameter_Total_Length 字段的值而变化。
- 数量:0 到 N 个,具体取决于事件类型。
④ 结合实际数据包分析
我通过抓取了btsnoop(btsnoop用于记录Host与Controller的通信)来详细分析一包HCI Envent Packet。
高亮部分的左边第一个字节0x04代表这一包数据类型为HCI Envent Packet。
高亮部分的左边第二个字节0x07,通过查阅Core spec得知这是Remote Name Request Complete事件,可以得知这是Controller通知Host远端友好名称请求完成。
高亮部分的左边第三个字节0xFF,这代表Event_Parameter后面字段的数据长度为255个字节。
高亮部分最后的255个字节,这代表Event_Parameter字段的具体内容。
此处可以简单分析一下Remote Name Request Complete事件的Event_Parameter:
- Event_Parameter里的从左边开始算第一个字节0x00,代表了回复成功的状态
- Event_Parameter里的从左边开始算第二到六个字节01 e8 fc ef 66 7c,代表了远端设备的MAC地址。(需要做一个大小端序的转换,实际MAC地址为7c:66:ef:fc:e8:01)
- Event_Parameter里的剩下的字节代表远端设备的友好名称,其为ASCII值。(转换成字符为BRAVIA VU31)
综上所述这条事件Remote Name Request Complete,用于通知Host已完成远端设备友好名称请求,远端MAC地址为7c:66:ef:fc:e8:01,远端友好名称为BRAVIA VU31。