尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

多路 Sniff 模式协商与 Deep 模式窗口对齐全解析

多路 Sniff 模式协商与 Deep 模式窗口对齐全解析 单链路的 Sniff 入门不难难的是一条设备上同时跑多条 Sniff 链路——多路窗口怎么协商、怎么错峰、进入 deep 省电后怎么保证窗口不漂、不重叠。这是 Multi-Central / Multi-Peripheral 场景下调度器设计的核心难题。本文拆解Sniff 协商的完整流程、建立用的全套参数、以及 deep 模式下多路窗口对齐的设计思路。TL;DR多路 Sniff 的本质一个设备Master 或 Slave同时维护 N 条 ACL 链路每条链路有独立的 D_SNIFF / T_SNIFF 锚点调度器必须让 N 个窗口错峰且不漂。协商流程LMP_sniff协商基础参数D_SNIFF / T_SNIFF / Attempt / TimeoutLMP_sniff_subrating协商 deep 模式的子速率Max Latency / Min Timeout。Deep 模式的窗口对齐精髓锚点 D_SNIFF 永远不变deep 只是把实际唤醒周期放大为 T_SNIFF × N双方用同一个锚点独立推算天然对齐。多路错峰的关键把多条链路的 T_SNIFF 设计成倍数关系让窗口在时域上天然不重叠配合调度器的优先级仲裁兜底。时钟漂移是最大敌人deep 模式下休眠久、漂移累积大必须靠每次收包更新 clk_offset 持续校准。目录多路 Sniff 的场景与挑战Sniff 协商全流程建立用的全套参数Deep 模式子速率与窗口对齐多路窗口对齐的调度设计实战坑与调优抓包验证总结1. 多路 Sniff 的场景与挑战1.1 什么是多路 Sniff单路 Sniff 是一条 ACL 链路进入省电模式。多路 Sniff 是同一个设备同时维护多条进入 Sniff 的 ACL 链路。典型场景场景拓扑多路压力Multi-Central一个中央设备连 3 个外围3 条 ACL 都进 Sniff中央设备作为 Master要错峰调度 3 个 Sniff 窗口Central Peripheral设备同时是链路 A 的 Master、链路 B 的 Slave既要推算自己的 Slave 窗口又要给别人的 Slave 排窗口TWS 主耳主耳对手机是 SlaveSniff对副耳是 MasterTPT两条链路的 Sniff 节奏必须对齐否则副耳监听丢包1.2 多路带来的三个新问题单路 Sniff 只要对齐一条链路的窗口多路则多了三个维度的问题问题 1窗口重叠调度冲突 链路 A: ┃窗┃──────┃窗┃──────┃窗┃ 链路 B: ──────┃窗┃──────┃窗┃── ↑ 如果 A/B 窗口撞在一起同一个 RF 前端无法同时服务两条链路 问题 2功耗峰值 三条链路同时唤醒 → 瞬时电流叠加 → 电源纹波 → 影响 RF 性能 问题 3deep 模式下漂移放大 T_SNIFF 拉大后休眠时间变长clk_offset 漂移累积 窗口对不上的概率随休眠时长指数上升核心结论多路 Sniff 的难点不在协商协议已定义而在调度器如何把 N 个窗口在时域上排开并让它们在 deep 模式下依然对齐。2. Sniff 协商全流程2.1 协商的参与者与方向Sniff 由Master 发起协商LMP 规范里LMP_sniff方向是 M→S但从设备可以主动请求HCI 层的HCI_Sniff_Mode命令Controller 内部再转成 LMP_sniff。HCI 层: Host → Controller: HCI_Sniff_Mode(handle, max_interval, min_interval, attempt, timeout) LMP 层: Master → Slave: LMP_sniff(D_sniff, T_sniff, attempt, timeout) Slave → Master: LMP_sniff_accepted / LMP_not_accepted2.2 LMP_sniff 协商时序主设备Master 从设备Slave │ │ │ ① 决定进 Sniff │ ├── LMP_sniff ──────────────────────────→ │ │ D_sniff 1000主设备 CLKN 锚点 │ │ T_sniff 32周期时隙 │ │ Attempt 4强制监听时隙 │ │ Timeout 16延伸监听时隙 │ │ │ │ ② 从设备评估参数是否可接受 │ │ ←────────────── LMP_sniff_accepted ──────┤ │ 或 LMP_not_accepted 拒绝 │ │ │ │ ③ 双方在 D_sniff 时隙开始 Sniff │ │ 窗口 k D_sniff k·T_sniff │ │ │2.3 D_sniff 是怎么算出来的D_sniff 是主设备 CLKN 的一个未来时隙号主设备选取时要保证从设备有足够时间处理 LMP 命令D_sniff 当前主设备 CLKN 安全余量通常 8~16 时隙 主设备当前 CLKN 1000 D_sniff 1000 16 1016 从设备本地换算: D_sniff_local 1016 - clk_offset关键D_sniff 必须是未来的时隙否则协商完成后窗口已经过期。余量要覆盖 LMP 传输 从设备处理的时隙数。2.4 退出 SniffLMP_unsniff需要回到 Active 时如开始大流量传输Master 发LMP_unsniffLMP_unsniff 与 LMP_sniff 不同——它不需要协商新参数 只需要双方约定一个退出时刻Instant到期回到 Active 全时监听。 退出 Sniff 后从设备恢复每个时隙监听功耗回升。3. 建立用的全套参数3.1 基础 Sniff 四参数LMP_sniff参数字段含义范围对功耗/延迟的影响D_sniffD_sniffSniff 锚点主设备 CLKN 时隙号未来时隙决定窗口的绝对位置用于多路对齐T_sniffT_sniff嗅探周期时隙数0x00040x40002.5ms10.24s越大越省电、延迟越高Sniff_AttemptAttempt强制监听时隙数1~255越大越稳、越耗电Sniff_TimeoutTimeout延伸监听时隙数0~255越大有数据时越久才休眠3.2 时钟偏移 clk_offset虽然不是 LMP_sniff 的参数但是窗口对齐的隐含前提参数来源作用clk_offsetLMP_clk_offset_req/rsp连接建立时从设备换算主设备 CLKN 到本地时钟从设备本地时钟 主设备 CLKN - clk_offset 窗口 k 的从设备本地时刻 (D_sniff k·T_sniff) - clk_offset3.3 Sniff Subrating 三参数deep 模式Deep 省电通过LMP_sniff_subrating_req/rsp协商核心参数是参数字段含义Max LatencyMax_Latency最大可容忍延迟时隙从设备据此计算子速率 NMin Remote TimeoutMin_Remote_Timeout最小远端对端TimeoutMin Local TimeoutMin_Local_Timeout最小本地 Timeout子速率 N 的计算从设备根据 Max Latency 和基础 T_sniff算出实际唤醒周期 T_sniff × N使实际周期不超过 Max Latency实际周期 T_sniff × N ≤ Max_Latency N floor(Max_Latency / T_sniff) 示例: T_sniff 8 时隙 (5ms), Max_Latency 800 时隙 (500ms) N floor(800 / 8) 100 实际周期 8 × 100 800 时隙 (500ms)3.4 参数全景表一张表记住所有参数┌─────────────────────────────────────────────────────────┐ │ Sniff 参数全景 │ ├─────────────────────────────────────────────────────────┤ │ 基础 SniffLMP_sniff: │ │ D_sniff —— 锚点绝对位置多路对齐的基准 │ │ T_sniff —— 周期唤醒频率 │ │ Attempt —— 强制监听窗口宽度 │ │ Timeout —— 延伸监听窗口宽度 │ │ │ │ 隐含前提: │ │ clk_offset —— 主从时钟偏移窗口换算的前提 │ │ │ │ Deep 模式LMP_sniff_subrating: │ │ Max_Latency —— 最大容忍延迟 → 决定子速率 N │ │ Min_Remote_Timeout —— 最小对端 Timeout │ │ Min_Local_Timeout —— 最小本地 Timeout │ └─────────────────────────────────────────────────────────┘4. Deep 模式子速率与窗口对齐4.1 什么是 deep 模式Deep 模式 深度省电 实际唤醒周期被大幅拉长。有两种方式方式机制特点大 T_sniff直接协商很大的 T_sniff如 1s简单但改参数要重新协商Sniff Subrating保持 T_sniff 不变用 N 放大实际周期推荐切换快、锚点不变实际项目里deep 模式几乎都用Subrating——因为它保留了基础 T_sniff只是跳过部分窗口锚点不动切换瞬时完成。4.2 Deep 模式窗口对齐的精髓锚点不变Subrating 最精妙的设计是deep 模式不改变 D_sniff 和 T_sniff只引入 N 跳过窗口。基础 Sniff (D_sniff100, T_sniff8): 窗口: 100 108 116 124 132 140 148 156 ... │←8→│←8→│←8→│←8→│←8→│←8→│←8→│ Deep 模式 (N4, 实际周期 8×4 32): 窗口: 100 132 164 ... │←────── 32 ──────→│←────── 32 ──────→│ 每 4 个基础窗口才唤醒一次为什么锚点不变就能对齐双方都从同一个 D_sniff 开始推算实际窗口 D_sniff k·(T_sniff × N)k 0,1,2…因为 D_sniff 是同一个值双方的推算结果天然一致不需要任何额外的同步信令——这是 Subrating 的核心价值4.3 从设备侧的 deep 窗口推算typedefstruct{uint32_td_sniff;// 基础 Sniff 锚点主设备 CLKNuint32_tt_sniff;// 基础周期时隙uint32_tattempt;// 窗口宽度uint32_tsubrating_n;// 子速率 Ndeep 模式下 N 1int32_tclk_offset;// 主从时钟偏移}sniff_deep_state_t;// 计算下一个实际唤醒窗口deep 模式uint32_tcalc_next_deep_window(sniff_deep_state_t*s,uint32_tcur_local_clk){uint32_td_sniff_locals-d_sniff-s-clk_offset;uint32_tactual_periods-t_sniff*s-subrating_n;// 实际周期uint32_telapsedcur_local_clk-d_sniff_local;uint32_tactual_idxelapsed/actual_period;// 下一个实际窗口 锚点 (idx1) × 实际周期returnd_sniff_local(actual_idx1)*actual_period;}对比普通 Sniff 的周期是t_sniffdeep 模式的周期是t_sniff * subrating_n。锚点d_sniff始终不变——这是 deep 模式窗口对齐的根本。4.4 Deep 模式下的时钟漂移问题deep 模式休眠时间变长clk_offset 漂移累积更大。这是 deep 模式窗口对齐最大的坑模式实际周期±20ppm 晶振漂移普通 Sniff25ms1μs可忽略Deep (N40)1s40μs需留余量Deep (N400)10s400μs接近 1 时隙必须校准应对策略每次收包更新 clk_offset只要通信不断偏移持续校准扩大 Attempt 窗口deep 模式下 Attempt 要覆盖漂移累积粗时钟唤醒 精时钟锁定用低功耗粗时钟提前唤醒用精确时钟锁定窗口推荐 Attempt 时隙数 ceil(2 · 实际周期秒数 · ppm · 1e-6 / 625e-6) 1 deep 模式实际周期 1s, ppm 20: Attempt ceil(2 · 1 · 20e-6 / 625e-6) 1 ceil(0.064) 1 2 时隙5. 多路窗口对齐的调度设计5.1 调度器要解决的核心问题多路 Sniff 下调度器Planner/Slice要保证目标说明窗口不重叠N 条链路的 Sniff 窗口在时域上错开RF 前端不同时服务两个窗口优先级仲裁窗口意外重叠时高优先级链路如 eSCO 通话优先功耗平滑避免多条链路同时唤醒造成电流尖峰deep 模式对齐进入 deep 后多路窗口依然错峰5.2 核心手段一T_sniff 倍数关系最有效的错峰手段是让多条链路的 T_sniff 成倍数关系窗口天然不重叠链路 A: T_sniff 8 → 窗口: 100, 108, 116, 124, ... 链路 B: T_sniff 16 → 窗口: 104, 120, 136, ... 链路 C: T_sniff 32 → 窗口: 112, 144, ... 错峰效果每 32 时隙一个大周期: 100 104 108 112 116 120 124 ... A B A C A B A ...设计原则各链路 T_sniff 都是最小 T_sniff 的整数倍D_sniff 错开一个最小窗口宽度的偏移这样窗口在时域上天然排开调度器只需处理意外重叠5.3 核心手段二Planner Slice 两级调度对应简历里的 Schedule 调度系统两级规划┌─────────────────────────────────────────────┐ │ Planner长周期规划ms~s 级 │ │ - 规划各链路 Sniff 窗口的宏观节奏 │ │ - 处理 deep 模式的子速率 N │ │ - 计算各链路下一个窗口时刻 │ └──────────────────┬──────────────────────────┘ │ 下发时隙计划 ┌──────────────────▼──────────────────────────┐ │ Slice短周期切片时隙级 │ │ - 每个时隙决定这个时隙服务哪条链路 │ │ - 窗口重叠时按优先级仲裁 │ │ - eSCO ACL 无连接广播 │ └─────────────────────────────────────────────┘// Slice 层每时隙的仲裁逻辑typedefstruct{uint8_tlink_id;uint8_tpriority;// eSCO3, ACL高2, ACL低1bool is_sniff_window;// 是否处于该链路的 Sniff 窗口}slice_decision_t;slice_decision_tslice_arbitrate(scheduler_t*sched,uint32_tcur_clk){slice_decision_tbest{.priority0,.is_sniff_windowfalse};for(inti0;isched-link_count;i){link_t*linksched-links[i];// 判断当前时隙是否是该链路的 Sniff 窗口if(in_sniff_window(link,cur_clk)){if(link-prioritybest.priority){best.link_idi;best.prioritylink-priority;best.is_sniff_windowtrue;}}}returnbest;// 返回最高优先级的链路}5.4 核心手段三多路 deep 模式的锚点规划进入 deep 模式后多路窗口的错峰依然靠锚点规划原则 1所有链路的 D_sniff 保持相对错峰 链路 A 的 D_sniff 100 链路 B 的 D_sniff 100 4错开一个最小窗口 链路 C 的 D_sniff 100 8 原则 2deep 模式的 N 也成倍数关系 链路 A: T8, N4 → 实际周期 32 链路 B: T16, N4 → 实际周期 64 链路 C: T32, N4 → 实际周期 128 原则 3锚点不变是前提 无论 N 怎么变D_sniff 永远不动错峰关系永不破坏5.5 多路对齐的完整时序示例三条链路进入 deep 模式后的对齐效果 链路 A (T8, N4, 实际周期 32): ┃窗┃───────────────────────────────┃窗┃─────────── 100 132 链路 B (T16, N4, 实际周期 64): ──────┃窗┃─────────────────────────────────────── 104 链路 C (T32, N4, 实际周期 128): ────────────┃窗┃───────────────────────────────── 112 总览128 时隙 80ms 一个超级周期: 100 104 112 A B C ┃窗┃ ┃窗┃ ┃窗┃ ...三个窗口完美错峰无重叠6. 实战坑与调优6.1 坑 1D_sniff 设得太近导致协商后窗口过期症状协商成功后第一个窗口就丢包 根因D_sniff 余量不足LMP 传输 从设备处理耗时超过了余量 修复D_sniff 当前 CLKN 至少 16 时隙10ms6.2 坑 2deep 模式切换后多路窗口突然重叠症状从普通 Sniff 切到 deep 模式后两条链路窗口撞在一起 根因切换 N 时没有重新检查多路错峰关系 修复切换 N 前先验证新实际周期下各链路窗口是否仍错峰 若会重叠优先调整 N 而非 D_sniff锚点不动是铁律6.3 坑 3deep 模式长休眠后窗口漂移失准症状deep 模式跑几分钟后从设备偶尔收不到主设备的包 根因长时间无通信clk_offset 漂移累积窗口偏移超过 Attempt 覆盖范围 修复① 扩大 Attempt ② 主设备周期性发 POLL 包强制校准 ③ 用粗时钟提前唤醒6.4 多路 Sniff 调优清单场景T_sniffNAttempt说明多路待机中央连 3 外设8 时隙402实际周期 200ms窗口错峰多路 一路活跃活跃路 T8/N1其余 T16/N8—2活跃路快响应休眠路 deepTWS 主耳手机链路 T40/N40TPT 自主调度—4副耳监听必须对齐手机链路7. 抓包验证用 HCI snoop 抓包验证多路 Sniff 对齐7.1 看协商抓包过滤 LMP 命令 LMP_sniff → 看 D_sniff / T_sniff / Attempt / Timeout LMP_sniff_subrating_req → 看 Max_Latency推断 N LMP_sniff_subrating_rsp → 确认协商结果7.2 看对齐关键验证点 1. 多条链路的 ACL 数据包是否在时间轴上错峰出现 2. deep 切换后数据包间隔是否 T_sniff × N 3. 是否有连续的 POLL/NULL 包说明窗口失准在互相探测7.3 看漂移长时间抓包观察数据包的实际间隔 vs 协商周期 间隔 协商周期 ± 漂移量 漂移量持续增大 → clk_offset 未更新窗口在漂8. 总结多路 Sniff 的设计可以浓缩成三句话协商LMP_sniff定锚点和周期LMP_sniff_subrating定 deep 模式的子速率锚点 D_sniff 永远是窗口对齐的基准。对齐deep 模式不改变 D_sniff只放大实际周期为 T_sniff × N双方从同一锚点独立推算天然对齐无需额外同步。多路错峰让 T_sniff 成倍数关系 D_sniff 错开最小窗口配合调度器的优先级仲裁保证 N 个窗口在时域上不重叠、不漂。最核心的一句话锚点不变是 deep 模式窗口对齐的根倍数错峰是多路 Sniff 不冲突的根。
返回列表