HCI 功能规范【5.2. Data and parameter formats】
这部分内容属于HCI data formats下面的Data and parameter formats主要讲的是HCI Command、HCI Event 里面的数据和参数到底按照什么格式排列、如何表示、如何解析。它不是讲某一个具体命令而是在讲整个 HCI 层通用的数据格式规则。后面你看任何 HCI Command / HCI Event都要用这些规则去理解字段。一、这部分整体想表达什么HCI 中的数据不是随便摆放的而是有统一规则的。主要包括数值默认用二进制和十六进制表示多字节参数默认使用小端格式 little-endianHCI Command / HCI Event 中参数的顺序默认按照规范表格中列出的顺序排列数组参数有统一的表示方式大部分参数长度是固定的但有些参数是可变长度bit string 中最低位 bit 的方向有明确规定保留值、未知值不能导致 Host 或 Controller 崩溃除非特别说明Controller 应支持参数的所有有效取值。二、关键信息 1默认使用 little-endian 小端格式原文说All values are in binary and hexadecimal little-endian formats unless otherwise noted.意思是除非规范特别说明否则 HCI 里面的多字节数值都按照小端格式传输。小端格式的意思是低有效字节先传高有效字节后传。例如一个 16-bit 数值0x1234在 HCI 数据包中实际传输顺序是34 12再比如一个 32-bit 数值0x12345678实际传输顺序是78 56 34 12这个规则非常重要因为 HCI Command、HCI Event、ACL Data 里经常有 2 字节、4 字节字段。例如Connection_Handle Conn_Interval Supervision_Timeout Opcode Company_Identifier这些字段在原始日志中看到的字节顺序通常都要按小端方式还原。三、关键信息 2参数顺序默认按规范表格顺序排列原文说Unless noted otherwise, the order of parameters in an HCI Command packet or HCI Event packet is the order the parameters are listed in the command or event.意思是除非特别说明否则 HCI Command packet 或 HCI Event packet 里面的参数排列顺序就是规范中表格列出来的顺序。例如某个 HCI Command 规范中列出参数为Parameter_A Parameter_B Parameter_C那么 HCI packet 中参数顺序就是Parameter_A Parameter_B Parameter_C不是随便排序也不是按照参数大小排序。这个规则对于解析 HCI 日志很重要。比如你在 btsnoop 日志中看到一串 HCI Command 参数01 00 20 08 00 10 00 10 00 00 00要想解析它就必须回到对应 HCI Command 的参数表中按表格顺序一个字段一个字段切出来。四、关键信息 3数组参数的表示方式原文中提到数组参数使用类似下面的表示方式ParameterA[i]如果一个命令或事件中有多组数组参数比如ParameterA[i] ParameterB[i]那么默认排列顺序是ParameterA[0], ParameterB[0], ParameterA[1], ParameterB[1], ParameterA[2], ParameterB[2], ... ParameterA[n], ParameterB[n]除非规范特别说明否则不是这样排列ParameterA[0], ParameterA[1], ParameterA[2], ParameterB[0], ParameterB[1], ParameterB[2]这个细节很重要。因为 HCI Event 中经常会出现“多个报告”“多个句柄”“多个返回项”的情况。例如 LE 扫描报告中可能一次 Event 里面包含多个 Advertising Report。每个 report 里面都有自己的地址类型、地址、数据长度、广播数据、RSSI 等字段。它的逻辑更接近Report[0] 的字段集合 Report[1] 的字段集合 Report[2] 的字段集合而不是把所有地址先放一起、所有 RSSI 再放一起。五、关键信息 4数组参数的描述通常只描述单个元素原文说The description of an arrayed parameter will actually describe a single element of the array.意思是当规范描述数组参数时它通常描述的是数组中的一个元素而不是整个数组。例如规范中写Connection_Handle[i]它的描述可能是Connection handle for the connection.这个描述实际上是说每一个 Connection_Handle 元素的含义而不是说整个数组的含义。所以看数组参数时要理解成Connection_Handle[0] Connection_Handle[1] Connection_Handle[2] ...每个元素都遵循这个描述。六、关键信息 5多字节参数的最低有效字节先发送原文说for multi-octet parameters the rightmost, Least Significant Octet is transmitted first意思是对于多字节参数最右边的最低有效字节先传输。例如0x1234右边的0x34是最低有效字节所以先传34 12再比如0x0001传输顺序是01 00这和前面 little-endian 是同一个核心规则只是这里说得更具体多字节字段按最低有效字节优先传输。在解析 HCI 日志时千万不要直接把原始字节顺序当成数值顺序。例如日志中看到18 00它表示的不是0x1800而是0x0018七、关键信息 6大多数参数是固定长度但也有可变长度参数原文说大多数非数组参数以及数组参数中的每个元素长度都是固定的通常是整数个 octets。这里的 octet 可以理解为1 octet 1 byte 8 bit例如Status: 1 octet Connection_Handle: 2 octets BD_ADDR: 6 octets RSSI: 1 octet但是有些参数是可变长度的。如果某个参数长度可变规范会在具体命令或事件中说明并且通常会用另一个参数来表示它的长度。典型结构是Data_Length Data其中Data_Length 表示后面 Data 字段有多少字节 Data 是真正的可变长度数据例如 BLE 广播数据中经常有Advertising_Data_Length Advertising_Data如果Advertising_Data_Length 0x0A就表示后面的Advertising_Data实际有效长度是 10 字节。八、关键信息 7可变长度参数会影响整个 HCI packet 的最大长度原文中还特别补充了一点如果一个 command 或 event 里面有数组参数或者有可变长度参数那么决定长度的字段可能会导致整个 packet 太大超过 HCI Command / Event packet 允许的最大长度。所以有效最大值可能小于规范里单独写的最大值。这个意思可以这样理解某个字段理论上最大允许填 255但是整个 HCI packet 本身有最大长度限制。如果其他字段已经占用了很多空间那么这个字段实际能使用的长度可能就没有 255 了。例如HCI Event packet 最大只能容纳一定长度 ↓ Event 里面有多个 Report ↓ 每个 Report 又有可变长度数据 ↓ 那么 Number_of_Reports 和每个 Report 的 Data_Length 就不能无限大所以实际解析时不能只看单个字段的理论最大值还要考虑整个 packet 的长度限制。九、关键信息 8bit string 的低位 bit 在右边原文说Where bit strings are specified, the low order bit is the right hand bit, e.g. 0 is the low order bit in 0b10.意思是当规范使用 bit string 表示字段时最低位 bit 在右边。例如0b10右边这个0是最低位 bit也就是 bit 0。左边这个1是 bit 1。也就是说0b10 bit 1 1 bit 0 0这个规则在看各种 bit mask、feature mask、flag 字段时很重要。例如某个字段是0b00000101它表示bit 0 1 bit 2 1 其他 bit 0而不是从左边开始数。十、关键信息 90xXX / N 0xXX 表示一类值不只是一个具体值原文说如果一个参数值被描述成0xXX或者N 0xXX其中X的数量合适那么这个描述适用于所有可能的值除非另有单独说明。这个主要是在说明规范写法。例如规范中可能写0x00 to 0xFF或者用0xXX表示某一类十六进制值。如果文档没有为每个值单独写描述那就说明这些值按同一规则理解。十一、关键信息 10未知值、保留值应被忽略不能导致系统停止工作原文说Parameter values or opcodes that an implementation does not know how to interpret shall be ignored ... The Host or Controller shall not stop functioning because of receiving a reserved value.意思是如果 Host 或 Controller 收到了自己不认识的参数值或 opcode应当忽略它并且用正确的信令完成当前操作。更重要的是不能因为收到 reserved value就直接停止工作或崩溃。这体现了蓝牙规范的兼容性设计。因为蓝牙协议会不断演进新版本规范可能定义新的 opcode、新的参数值、新的 feature bit。旧设备不认识这些值很正常。正确做法是不认识 ↓ 忽略或按规范返回合适错误 ↓ 继续正常工作而不是不认识 ↓ 协议栈异常 ↓ 蓝牙功能崩溃这个规则对于 SDK、蓝牙模块、App 解析日志也有启发解析到未知字段时不要轻易认为数据一定错误也不要让程序崩溃应该保留兼容处理。十二、关键信息 11Controller 默认应支持参数的所有有效取值原文最后说除非有特别说明比如Mandatory Range或者某个值属于可选功能但当前设备不支持否则 Controller 应支持所有参数的有效值。这里要注意valid values 和 reserved values 不一样。valid values规范定义的有效值 reserved values保留值当前不能作为有效值使用Controller 应该支持所有有效值但不需要支持 reserved values。例如某个参数规范定义0x00: Disable 0x01: Enable 0x02-0xFF: Reserved for future use那么 Controller 必须支持0x00 0x01但不需要支持0x02-0xFF如果收到 reserved value应该按规范要求处理不能当成正常有效值。十三、这部分对你看 HCI Command / Event 的实际帮助以后你看任何 HCI Command 或 HCI Event可以按下面这个顺序理解1. 先看 Opcode / Event Code确定这是哪个命令或事件 2. 再看参数表按规范列出的顺序解析参数 3. 遇到多字节字段按 little-endian 还原 4. 遇到数组字段按 ParameterA[0], ParameterB[0], ParameterA[1], ParameterB[1] 这种方式理解 5. 遇到可变长度字段先看 Length 字段再截取后面的数据 6. 遇到 bit mask从右边开始数 bit 0 7. 遇到 reserved 或 unknown value不要直接认为协议栈必须崩溃而是看规范要求如何忽略或返回错误十四、总结这部分内容讲的是 HCI 数据和参数的通用格式规则。它的核心不是某一个具体命令而是告诉你后续所有 HCI Command、HCI Event 的字段都应该按照这些规则理解。最重要的几个点是1. HCI 中多字节参数默认使用 little-endian。 2. 参数顺序默认按照规范表格中的顺序排列。 3. 数组参数默认按一组一组元素交错排列。 4. 大多数参数长度固定少数参数长度可变。 5. 可变长度字段通常由另一个 Length 参数决定。 6. bit string 的 bit 0 在最右边。 7. unknown / reserved value 不能导致 Host 或 Controller 停止工作。 8. Controller 应支持所有规范定义的有效参数值。这部分是读 HCI 章节的基础规则。只要理解了这些规则后面解析 HCI Command、HCI Event、btsnoop 日志时就不会只看到一串十六进制字节而是能按字段顺序和大小端规则把它还原成真正的协议含义。

相关新闻