资讯动态

【LMP】规范精讲[13]: 参数的户口本、公钥的集装箱与出厂的兜底值

发布时间:2026/11/6 5:16:29 来源:尧图企业网站定制
一条链路上飞过的每个数字都有名有姓有单位64 字节的公钥得拆成四节 16 字节的车厢才能上路而所有谈判开始之前设备兜里都揣着五个出厂默认值。这篇把 LMP 收尾的三大件一次讲透。目录一、户口本八十多个参数的登记制度1.1 五栏登记表和一条总则1.2 按家族逛字典1.3 两条贯穿全表的位规则二、集装箱16 字节的封装货运三、出厂设置五个兜底值四、罚则字典错误码的展开五、一段小彩蛋六、收个尾七、测验前面十几篇把 LMP 的过程性内容都过完了建链、认证、加密、功控、AFH、嗅探、多时隙、角色切换。今天换一个视角不讲流程讲流程里流动的东西本身——参数怎么定义、大块头数据怎么封装、谈判之前大家的共识是什么。这三块内容在规范里属于那种平时不显眼、出问题时必回头查的硬底子。我把它们分别叫作户口本、集装箱和出厂设置。老规矩全程用两份手头的空口抓包做伴读一份是音乐场景的链路全过程一份是语音通话场景中间夹着 eSCO 建立与拆除。一、户口本八十多个参数的登记制度1.1 五栏登记表和一条总则参数定义部分是一张巨大的表八十多个参数挨个登记每个条目五栏参数名、长度字节、类型、单位、详细说明。像户口本一样姓名、籍贯、出生日期、身份信息一个都不能少。表的开头有一句总则先把没有明确强制范围mandatory range的参数统一打了包Where no mandatory range is given, the mandatory range consists of all valid values that are not reserved for future use.翻译过来就是凡是我没画线的除了标着保留给未来使用的值其余全都在合法范围内。这句话看着不起眼实际是给实现者的定心丸——你收到的值只要不是保留值就不能说人家非法。保留值在表里大量出现各种 2-255: reserved for future use它们就是各参数的地雷区。1.2 按家族逛字典八十多个条目顺着字母排读起来枯燥但按功能分家就清爽了。下面挑每家的核心讲。AFH 家族跳频地图的两套账本。这家有信道分类AFH_Channel_Classification和信道地图AFH_Channel_Map两个 10 字节的参数容易混。分类是情报账本地图是执行账本。分类参数是 40 个 2 比特元素第 n 个元素描述信道 2n 和 2n1 的好坏0 未知、1 好、3 坏唯独第 39 个元素只管 78 号信道一个——因为 79 个信道两两配对最后一对只剩一个。地图参数是 80 个 1 比特元素第 n 个直接决定信道 n 用不用第 79 号元素保留。从设备靠分类账本上报情报Central 汇总后改写地图账本跳频序列随之改道。地图不是发出去立刻生效的要等一个切换时刻AFH_Instant。这个参数 4 字节存的是 Central 时钟的 27:1 位Bits 27:1 of the Centrals clock value at the time of switching hop sequences. Only even values are valid.只取 27:1 位、只允许偶数——这两个细节后面统一讲先记住它不是随便填的数。同家的最大/最小间隔参数限定了 0x0640 到 0xBB80 槽也就是 1 到 30 秒同样只收偶数。时钟家族对表专用。时钟偏移Clock_Offset是 15 位无符号数单位 1.25 毫秒计算式是 (从设备 CLKN16-2 减 Central 的 CLKN16-2) 再模 2 的 15 次方。这个值量出来存着下次 page 同一个设备时能直接拿去预测跳频相位省下几百毫秒的搜索时间。我的音乐抓包里建链后就有一笔 0x5644 的记录通话抓包里是 0x206A——不同链路、不同晶振偏移自然不同但都规规矩矩落在 15 位容器里。同一家族还有粗调时钟五件套Clk_Adj_前缀和槽偏移Slot_Offset0 到 1249 微秒。粗调那组是为角色切换场景准备的新 Central 要把自己的表针拨到和 piconet 对齐调整量以微秒计Clk_Adj_Offset 是有符号数正负 624 微秒以内调整时刻照例只收偶数。声码器一家之主Air_Mode。语音链路的载荷格式就这一个字节定终身0 是 µ-law 对数律、1 是 A-law 对数律、2 是 CVSD、3 是透明数据。欧洲电话用 A-law北美用 µ-law蓝牙默认 CVSD透明模式则把编解码完全交给上层——我的通话抓包里 eSCO 协商的结果就是透明数据说明语音帧原样穿透编解码在应用层做。速率位域一个字节管两件事。数据速率Data_Rate参数是位域设计的教科书案例。基础速率模式下bit0 决定用不用 FEC0 用 1 不用bit1-2 是包长偏好1/3/5 槽增强速率模式下bit3-4 选速率1 是 2 Mb/s、2 是 3 Mb/sbit5-6 再来一组包长偏好bit7 保留。两套业务塞进一个字节互不干扰。空口上它是这样的音乐抓包里有一条速率偏好基础速率选了带 FEC 的包DM 系列增强速率选 3 Mb/s 加 5 槽包。对照位域拆开就是 bit00、bit3-42、bit5-63一个字节表达了三种倾向。链路质量双指标Drift 和 Jitter。漂移是时钟长期偏离的百万分比抖动是短期波动的微秒数各占一个字节。谁测谁报——接收方实测自己这端的时钟质量随定时精度上报给对端让对端决定时钟补偿策略和轮询提前量。我的抓包里两份日志都报了 250 ppm 的漂移和 10 微秒的抖动其中一个场景还先后出现 0 微秒和 10 微秒两次上报——同一条链路测量时刻不同结果就不同这不是异常恰恰说明这两个值是活的测量值而非写死的配置。eSCO 家族语音链路的谈判筹码。这家人口最多句柄、逻辑传输地址1 到 7、包类型0x07 是 EV3、0x26 是 2-EV3、0x37 是 3-EV3EV4/EV5 及 2-EV5/3-EV5 各有编号还有一个方向敏感的 0x00——Central 发是 POLL、从设备发是 NULL。真正的重头是三个数TeSCO 是语音帧间隔4 到 254 槽只收偶数W eSCO 是重传窗口宽度0 到 254 槽只收偶数Packet_Length 是载荷长度。谈判不是一次成交的。还价状态Negotiation_State字段记录了卖家的四种态度0 发起谈判、1 你给的参数能用但我有更好的、2 会造成保留槽冲突、3 会造成时延冲突、4 压根不支持。注意 2 和 3——这两个不是笼统的拒绝而是精确的拒绝理由直接对应后面要讲的错误码 0x34 和时延违规场景。为了保底互通规范还专门列了一张强制参数范围表接收方必须接受表内任何一个组合可以额外接受更大范围的值。单槽包 EV3 的 TeSCO 固定 62-EV3 放宽到 6-12 的偶数3-EV3 到 6-18三槽包 EV4/EV5/2-EV5/3-EV5 统一 16W eSCO 单槽包只许 0、2、4三槽包只许 0 和 6载荷长度两个方向都是 10×TeSCO÷2声码器模式单槽包至少支持 A-law/µ-law/CVSD/透明中的一种三槽包只强制透明模式。这张表就是谈判桌上的法律底线——你可以说不但不能对表内的组合说不。嗅探家族低功耗三兄弟。TSniff 是嗅探周期偶数且不得超过监督超时乘 0.999强制范围 0x0006 到 0x0540Sniff_Attempt 是每个周期开头必须醒着收的槽数1 到 TSniff÷2Sniff_Timeout 是听到自己的地址后继续守候的槽数0 到 0x0028。三者的关系像闹钟周期多久响一次、醒了先听多长、有动静再守多久。我的音乐抓包里协商出 T800 槽500 毫秒、Att4、超时 1从设备每半秒醒 4 个槽绝大多数时间在睡觉。监督超时与切换时刻链路的生死簿。监督超时Supervision_Timeout只许 0无限等待或 0x0190 到 0xFFFF。抓包里两次都是 8000 槽换算 0.625 毫秒一槽正好 5 秒。切换时刻Switch_Instant和 AFH 的即时值同构Central 时钟的 27:1 位只收偶数。安全与身份钥匙的尺寸学问。密钥长度Key_Size范围 7 到 16 字节抓包里顶格 16。广播加密的密钥长度掩码Key_Size_Mask是 16 个比特位图第 n 位代表 n 字节长度是否支持0 到 6 位必须为零——因为 7 字节以下压根不许存在。设备名相关的三个参数Name_Length、Name_Offset、Name_Fragment把最长 248 字节的名字切成 14 字节的分片运输Name_Fragment 的类型标注 utf8s{14z}14 个 UTF-8 字符加零结尾。IO 能力四选一仅显示、显示加确认、仅键盘、无输入无输出——简单配对时靠它决定走数字比较还是万能钥匙流程。功率状态字三种调制三张脸。功率控制响应Power_Adj_Rsp是 4 个 2 比特元素组成的字节分别汇报 GFSK、π/4-DQPSK、8DPSK 三种调制下的功率状态0 不支持、1 变了一档未到边界、2 已到最大、3 已到最小。我的音乐抓包里建链第一件事就是功率顶格状态字三种调制全是 max power随后几个来回的升降状态字如实变成 changed one step。一个字节回答三个问题位域的效率体现得淋漓尽致。SAM 子图家族和杂项。子图索引SAM_Index强制支持 0、1、2 和 0xFF子图类型参数是 56 个 2 比特字段排进 14 字节每个字段描述一个槽的收发可用性只发、只收、全开、全关TSAM_SM 是子图长度2 到 56偶数保持时长Hold_Time限 0x0014 到 0x8000 且不得超过监督超时的 0.999 倍轮询间隔Poll_Interval强制范围 0x0006 到 0x1000时控标志Timing_Control_Flags里只有 bit1 有意义区分初始化方式 1 和 2。这一圈逛下来字典的主体就都在手里了。1.3 两条贯穿全表的位规则上面反复出现两个短语only even values are valid和 bits N:M。这不是装饰性注释全表有十几处参数受它们管辖规范特意在表后面用两段话把规则说死。第一段偶数条款If a parameter is described as only even values are valid and a device receives an LMP PDU with an odd value for this parameter field, the PDU should be rejected with an error code of Invalid LMP Parameters (0x1E).为什么这么多参数要偶数因为蓝牙的槽成对出现625 微秒一槽收发交替所有涉及槽边界的时刻量——切换时刻、各类即时值、各种周期——都天然落在偶数槽号上。发奇数等于宣称自己要在半个槽的中间做事物理上就不成立。规范甚至贴心地把位域编码方式都设计好了即时值类参数存的是时钟的 27:1 位最低位在源头就被丢掉想发出奇数都难。第二段位提取规则Where a parameter is described as bits N:M of some other value V, the parameter value in the PDU shall be V shifted right by M bits. Where the space available for the parameter is greater than that needed (N-M1 bits), the parameter value still occupies the entire space and the remaining bits shall be zero.规则本身三句话右移 M 位、装满整个容器、多余位补零。规范还给了个例子值得原样看For example, if a 16 bit parameter is equal to 0xDEAD and bits 10:6 are stored in a uint8, the resulting parameter will be 0x1A.0xDEAD 是 1101 1110 1010 1101取第 10 到第 6 位是 11010装进 8 位容器左补三个零得 0001 1010 即 0x1A。我把这两条规则连同这个例子画成了一张图用 C 代码把这两条规则落地就十几行的事/* 位提取bits N:M右移 M 位高位补零 */ static inline uint32_t lmp_extract_bits(uint32_t v, uint8_t n, uint8_t m) { return (v m) ((1u (n - m 1)) - 1u); } /* 偶数校验违反即整条 PDU 拒绝错误码 0x1E */ int lmp_check_even(uint32_t v, uint8_t *err) { if (v 1u) { /* 奇数 非法 */ *err 0x1E; /* Invalid LMP Parameters */ return -1; } return 0; } /* 角色切换时刻的装载CLK 取 27:1天然偶数 */ uint32_t lmp_make_switch_instant(uint32_t clk27_0) { return clk27_0 1; /* 最低位丢弃奇数不可能出现 */ }对照真实空口看一眼最后那行代码的效果通话抓包里那次角色切换28 位本地时钟是 0x00E7EF0二进制末位是 0右移一位得 0x0073F78——抓包工具显示的切换时刻正是这个数。发出去的值天然是偶数接收方左移一位还原两边对齐在同一个槽边界上。二、集装箱16 字节的封装货运LMP 单条 PDU 的载荷上限是 16 字节这在大多数场景够用——参数表里最长的 Name_Fragment 也才 14 字节。但安全流程里有大块头椭圆曲线公钥。P-192 的公钥 48 字节P-256 的公钥 64 字节塞不进一条 PDU怎么办规范的答案是封装encapsulation机制货运集装箱思路先发一条头单报报名号——什么货、多长然后分批把 16 字节一节的车厢发过去接收方拼箱验收。头单就是表里那三个参数主类型Encap_Major_Type、次类型Encap_Minor_Type、载荷长度Encap_Payload_Length。目前启用的组合只有两组主类型 1 加次类型 1 是 P-192 公钥48 字节字节 23-0 装 X 坐标、47-24 装 Y 坐标小端序主类型 1 加次类型 2 是 P-256 公钥64 字节字节 31-0 装 X 坐标、63-32 装 Y 坐标同样小端。注意坐标系是先低后高、每个坐标内部又按小端排——两层字节序嵌套实现时最容易在这里翻车。其余所有主次类型组合全部保留也就是说这套集装箱目前只运公钥这一种货。有个细节值得单独说Encap_Data 参数的定义注明 MSBs zero padded when data is less than 16 bytes——最后一节车厢如果装不满高位补零不许短箱发运。接收方按头单里登记的总长度取数多的零位自然被截掉。分块运输的代码也简单两段函数管进出两 directions#define ENCAP_P192 0x01 #define ENCAP_P256 0x02 #define ENCAP_FRAG 16 /* 发送侧64 字节公钥拆四节 */ void encap_send_pubkey(const uint8_t key[64], uint8_t minor_type) { lmp_send_encap_header(1, minor_type, 64); /* 先报名号 */ for (int off 0; off 64; off ENCAP_FRAG) lmp_send_encap_data(key[off]); /* 4 节 16 字节车厢 */ } /* 接收侧按长度拼箱末节高位零填充已被长度自然截断 */ int encap_recv(uint8_t minor_type, const uint8_t *frags, size_t nfrags, uint8_t *out) { size_t total (minor_type ENCAP_P192) ? 48 : 64; for (size_t i 0; i nfrags; i) memcpy(out[i * ENCAP_FRAG], frags[i * ENCAP_FRAG], ENCAP_FRAG); return (nfrags * ENCAP_FRAG total) ? 0 : -1; }在空口上这套机制表现为一条头单加连续四条 16 字节载荷一气呵成。简单配对走安全建立流程时公钥交换全靠它扛。三、出厂设置五个兜底值谈判开始之前大家的共识是什么规范用一句祈使句开篇Devices shall use these values before anything else has been negotiated.在一切协商发生之前设备必须按默认值行事。默认值表一共五个AFH_Mode 关闭——建链初期老老实实全 79 信道跳频信道地图还不存在谈不上裁剪AFH_Reporting_Mode 关闭——从设备的情报上报通道默认关闭等 Central 想要情报再打开Drift 250 ppm——一个偏保守的时钟质量假设绝大多数消费级晶振都比它好用保守值意味着轮询提前量留足不会错过包Jitter 10 微秒——同理宁可多等不可错过Max_Slots 1——最保守的包长建链第一毫秒谁都不知道对端能不能收 3 槽 5 槽大包先按单槽起步。这五个默认值挑得极有讲究两个 AFH 相关的管跳频两个时钟相关的管同步一个包长管传输。全是建链初期最容易踩空的领域。默认值的本质是互操作兜底——两个从未谋面的设备在说第一句话之前就知道对方会以什么姿态行事。四、罚则字典错误码的展开参数表里 Error_Code 一栏只有一句 See [Vol 1] Part F真正的错误码字典在规范第一卷的控制器错误码附录里。跟 LMP 参数直接相关的一共七个值得逐个过一遍。0x19 Unknown LMP PDU控制器收到不认识的 LMP 操作码。版本错配的典型症状——老设备收到新版本才有的 PDU只能这么回答。0x1E Invalid LMP Parameters参数级违法法定触发条件有四条原文列举得很清楚——PDU 长度非法该偶不偶超出规定范围两个以上参数互相矛盾。前文偶数条款的罚则就是它。这四条几乎覆盖了参数表的所有违规形态是七个错误码里出场率最高的一个。0x22 LMP Response Timeout事务在超时窗口内没有等到响应。前面讲事务模型时提过的计时器到点就报这个。0x23 LMP Error Transaction Collision两个设备同时发起同一个事务撞车了。规范对碰撞的处理是各退一步重来这个错误码就是撞车时的鸣笛。0x24 LMP PDU Not Allowed操作码本身没毛病、参数也没毛病但当前状态机不允许这个 PDU 出现。状态敏感型拒绝。0x30 Parameter Out of Mandatory Range请求的参数值超出强制范围且接收方不接受。注意它和 0x1E 的分工——0x1E 罚的是无争议的违法奇数、越界、矛盾0x30 罚的是表内合法但超出强制范围、接收方又不愿意迁就的值。eSCO 谈判里对方还价状态 4不支持就可能伴随它出现。0x34 Reserved Slot Violation本次同步链路谈判被终止还价状态正是保留槽冲突。当 eSCO 的参数组合会侵犯已存在的保留槽时谈判以这个错误码收场。这七个码不是抽象概念我在通话抓包里就逮到一个现行。eSCO 链路建立期间Central 尝试发起嗅探协商T144 槽即 90 毫秒、Att4从设备直接拒绝理由正是 LMP PDU Not Allowed0x24——参数本身挑不出毛病但 eSCO 占着保留槽这个嗅探组合此刻不被状态机允许。时间线如下七秒后 eSCO 拆除但那次嗅探协商没有重试——发起方显然判断这个场景下不值得再试。一个错误码背后的完整故事参数合法性与链路状态是两回事前者由参数表管辖后者由状态机管辖0x24 就是这两套管辖权的分界线。五、一段小彩蛋规范的附录 A 有一张参数更名对照表把旧版小写名称air mode、data rate、clock offset和新版驼峰名称Air_Mode、Data_Rate、Clock_Offset一一对应。如果你读的是十年前的代码或旧版规范参数名对不上号时可以来这里考古。名字改了语义没变——户口本换了封面人还是那些人。六、收个尾三件套讲完了串起来回味一下。户口本参数定义回答了链路上每个数字的身份问题多长、什么类型、什么单位、边界在哪集装箱封装机制解决了 16 字节天花板与大块头数据之间的矛盾头单加车厢的货运模式简洁而完备出厂设置默认值则是在一切谈判之前铺设的互操作底线五个参数覆盖跳频、同步、包长三个最敏感的领域。如果只能记住三句话我建议是这三句偶数条款管住所有槽边界参数违反即 0x1E位提取规则右移 M 位、补零装满管住所有位域参数0xDEAD 取 10:6 位得 0x1A默认值管住谈判开始前的一切行为Drift 250、Jitter 10、Max_Slots 1、Poll_Interval 40、AFH 全关。这三句话撑起了 LMP 参数世界的半壁江山。至此 LMP 主体内容全部讲完。回头看这十几篇从 PDU 格式和事务模型起笔经特性协商、安全握手、连接控制到今天的三件套收尾一条 ACL 链路从生到死的所有管理动作都在射程之内了。七、测验Q1LMP 参数表中大量参数标注 only even values are valid背后的原因是什么接收方收到奇数值应如何处理这套约束在编码层面是怎么保证的答原因在于蓝牙的时隙成对出现625 微秒一槽、收发交替所有涉及槽边界的量各类即时值、周期、间隔在物理上都只能落在偶数槽号上发送奇数值等于宣称在半个槽中间做事物理上不成立。处理方式规范写得明确整条 PDU 以 0x1E Invalid LMP Parameters 拒绝而不是只忽略该字段——参数级违法污染的是整条报文的可信度。编码层面的保证体现在位域设计即时值类参数存的是时钟的 27:1 位如 Switch_Instant、AFH_Instant装载时右移一位把最低位丢掉发出的值天然是偶数接收方左移一位还原。源头设计消灭了非法值比收端校验更可靠两者叠加构成双保险。Q2LMP 单条 PDU 载荷上限 16 字节P-256 公钥 64 字节是怎么传过去的封装头三个字段各起什么作用字节序有什么讲究答靠 LMP 封装机制货运集装箱模式先发一条头单封装头声明主类型、次类型和载荷总长度然后把 64 字节公钥拆成四节、每节 16 字节逐条发送最后一节装不满则高位补零接收方按头单登记的总长度拼箱截断。主类型目前只有 1公钥类次类型 1 是 P-192、2 是 P-256其余组合全部保留载荷长度向接收方预告车厢总数和拼箱长度。字节序有双层结构整体布局是 X 坐标在前P-256 为字节 31-0、Y 坐标在后字节 63-32每个坐标内部又按小端序排布实现时两层都不能搞错。这套机制目前只运公钥一种货简单配对的 DH 密钥交换全靠它完成。Q3错误码 0x1E、0x30、0x24 都能拒绝一条 LMP PDU三者的区别是什么eSCO 谈判中对方报还价状态为保留槽冲突时对应哪个错误码答三者管辖权不同。0x1E Invalid LMP Parameters 罚参数本身违法法定触发条件四条PDU 长度非法、该偶不偶、超出规定范围、参数互相矛盾——是参数级的执法。0x30 Parameter Out of Mandatory Range 罚的是值在理论上合法但超出强制范围且接收方不接受的情况本质是谈判破裂而非报文违法多见于 eSCO 参数还价。0x24 LMP PDU Not Allowed 罚的是时机不对——操作码合法、参数合法但当前链路状态不允许这个 PDU是状态机级的执法比如 eSCO 占用保留槽期间的某些嗅探协商就会被它拒绝。还价状态 2保留槽冲突对应的终止错误码是 0x34 Reserved Slot Violation谈判以它收场还价状态 4不支持则更接近 0x30 的语义。区分这三个码的实战意义收到 0x1E 该查自己的参数打包收到 0x30 该调整请求值收到 0x24 该等状态变化再重试。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑