BLE Host层L2CAP -- 配置参数选项

1. 为什么要聊 L2CAP 配置参数?

写过蓝牙协议栈或抓过 HCI 包的同学对 L2CAP_CONFIGURATION_REQ / RSP 一定不陌生:两个设备通过 L2CAP_CONNECT_REQ 建立面向连接的信道(Channel)之后,并不会立刻传数据,而是要进入 CONFIG 状态,互相"讨价还价"一番------你我各自能收多大的包?要不要重传?要不要校验?QoS 怎么保证?

这些"讨价还价"的内容,就是配置参数选项(Configuration Parameter Options)。协议把它们定义成一个一个的"选项"(Option),封装在信令报文里传输。

一句话理解:连接建立相当于"握手",配置过程相当于"签合同",而本文讲的选项,就是合同里的一个个条款。

L2CAP 信道建立的整体状态机是:CLOSED → WAIT_CONNECT(RSP) → CONFIG → OPEN,配置选项只在 CONFIG 阶段登场,但其中一部分参数(如 MTU、模式)在后续的 reconfigure 时也可能再次出现。

2. 配置选项的通用格式:TLV 三件套

所有配置选项都遵循统一的 Type-Length-Value 结构:

|---------------|-----------|-----------------------------------|
| 字段 | 大小 | 说明 |
| Option Type | 1 字节 | 标识要配置哪个参数 |
| Option Length | 1 字节 | Option Data 的字节数(可以没有数据,Length=0) |
| Option Data | 0~255 字节 | 具体参数值,含义由 Type 决定 |

2.1 一个非常巧妙的前向兼容设计

Option Type 的最高位(MSB)是一个"善意的开关",专门处理"老设备遇到新选项"的场景:

  • Type = 0x00 ~ 0x7F(最高位为 0) :收到不认识的选项 → 必须拒绝整个配置请求;
  • Type = 0x80 ~ 0xFF(最高位为 1) :收到不认识的选项 → 默默跳过,继续处理下一个。

💡 通俗解读:0x7F 以下的选项是"关键条款",不认识就不能签字(拒签整份合同);0x80 以上的选项是"附加条款",不认识就当没看见,合同照签。这样新版本协议即使加了新选项,也不会导致新旧设备之间完全无法建连。这也是为什么大家常说的"hint option(提示选项)"都把最高位置 1。

3. 7 个配置选项逐个击破

Core 5.4 中 L2CAP 经典信道(ACL-U / LE-U 上的面向连接信道)共定义了 7 个配置选项,先上一张总览表:

|--------------------------------|------|--------|--------------|------------------------------|
| 选项 | Type | Length | 可协商性 | 作用一句话 |
| MTU(最大传输单元) | 0x01 | 2 | ❌ 信息性参数 | 告诉对方"我最大能收多大的 SDU" |
| Flush Timeout | 0x02 | 2 | ✅ 可协商 | 基带层重传多久后放弃(BR/EDR) |
| QoS(服务质量) | 0x03 | 22 | ✅ 可协商 | 经典流规范:令牌桶限速 |
| Retransmission & Flow Control | 0x04 | 9 | 部分(Mode 可协商) | 选工作模式:Basic/ERTM/Streaming 等 |
| FCS(帧校验序列) | 0x05 | 1 | ❌ 不可协商 | S/I 帧要不要带 16 位 CRC |
| Extended Flow Specification | 0x06 | 16 | ❌ 不可协商 | 增强流规范,取代 QoS + Flush Timeout |
| Extended Window Size | 0x07 | 2 | ❌ 不可协商 | ERTM 模式下把窗口从 63 扩到 16383 |

3.1 MTU ------ 最大传输单元(Type 0x01)

格式:

核心要点:

  1. MTU 是"信息性参数",不是协商出来的值。双方各报各的,互不约束------我告诉你我最多能收多大的 SDU,你爱发多大是你的事,只要别超过我的上限。
  2. 最小 MTU 要求 :ACL-U 逻辑链路上必须支持 48 字节 ,LE-U 上必须支持 23 字节 ;默认值 672 字节。
  3. 双向 MTU 独立 :信道上行和下行的 MTU 由各自的 CONFIGURATION_REQ 分别确定,天然不对称。
  4. 配置规则:
  • 对方报的 MTU ≥ 信道最小 MTU → 必须接受;
  • 对方报的 MTU < 最小值 → 可以拒绝(用 "Failure -- Unacceptable Parameters" 结果码回复,并在响应中带上自己能接受的 MTU 值);
  • 响应方在正响应中应带上"实际使用的 MTU"(= 请求值与自身能力的较小者)。

5. ERTM / Streaming 模式下,MTU 不允许重配置为更小的值(只许变大方,不许变小气)。

💡 通俗类比:MTU 像是你家门口的收货门宽度------你告诉快递员"我家门最宽能过 1.5 米的家具",快递员(对端)就知道发多大的包裹你才塞得进去。至于你家往外发的家具多大,那是另一扇门的事。

3.2 Flush Timeout ------ 刷新超时(Type 0x02)

这个选项只用于 BR/EDR ,告诉对方"我在基带层打算重传到什么程度"。它是一个可协商的参数,单位为毫秒:

|------------------|-------------------------------------------------------------|
| 取值 | 含义 |
| 0x0001 | 不做基带级重传(最小轮询间隔 1.25ms) |
| 0x0002 ~ 0xFFFE | 基带实际使用的 Flush Timeout |
| 0xFFFF | 无限重传 ,直到物理链路断开 ------ 即所谓的 "可靠信道"(reliable channel) |

要点:

  • 默认值 0xFFFF(无限重传);
  • 该值对同一 ACL 逻辑传输上的所有信道 生效,但可以通过 HCI ACL 数据包里的 Packet_Boundary Flag(PBF) 标记单个 L2CAP 包为"不可自动刷新",实现逐包覆盖;
  • 如果使用了扩展流规范(Extended Flow Spec,见 3.6),本选项禁止使用------功能被更强的替代了。

💡 通俗类比:可靠信道(0xFFFF)像"死磕型快递",丢了就一直重发,直到送到为止;小的 Flush Timeout 像"时效型快递",超过 X 毫秒没送到就直接扔掉旧包裹发新的,追求新鲜度而非完整性------语音通话就是典型场景,迟到的语音包毫无价值。

3.3 QoS ------ 经典服务质量选项(Type 0x03)

Length = 22 字节,携带一份类似 RFC 1363 的流规范(Flow Specification):

Service Type(服务类型),默认 Best Effort:

|------|-------------|------------------------------|
| 值 | 类型 | 含义 |
| 0x00 | No Traffic | 该方向上没有数据要发 |
| 0x01 | Best Effort | 尽力而为,协议强制要求支持的最低档,不给任何保证 |
| 0x02 | Guaranteed | 保证型服务,参数才有实际约束力 |

五个流量参数(Best Effort 时都可当作参考值,Guaranteed 时才有强制力):

|-------------------|----|------|---------------------------------------|------------|
| 参数 | 大小 | 单位 | 含义 | 默认值("不关心") |
| Token Rate | 4B | 字节/秒 | 应用层平均发送速率;0xFFFFFFFF = 野通配,要最大带宽 | 0x00000000 |
| Token Bucket Size | 4B | 字节 | 允许的"突发"上限(桶越大越能一波猛发);0xFFFFFFFF = 最大桶 | 0x00000000 |
| Peak Bandwidth | 4B | 字节/秒 | 限制背靠背发包的最高速度 | 0x00000000 |
| Access Latency | 4B | 微秒 | SDU 从上层到空中接口的最大可接受延迟 | 0xFFFFFFFF |
| Delay Variation | 4B | 微秒 | 最大延迟与最小延迟之差(抖动),纯信息性参数 | 0xFFFFFFFF |

令牌桶的工作原理

复制代码
            ┌────────────────────────────────────────────┐
            │   令牌以 Token Rate 的速率持续生成          │
            │                                            │
            │   ○ ○ ○ ○ ○ ○ ──────►  桶(容量 = Token     │
            │  (发令牌)           Bucket Size,存满即止)│
            └───────────────────┬────────────────────────┘
                                │ 发数据时消耗令牌
                                ▼
   应用发数据 ──► 【桶里令牌够?】──是──► 正常发送(受 Peak Bandwidth 限速)
                        │
                        否
                        ▼
               非符合性流量(non-conformant)
               QoS 保证不再适用(可能丢包/延迟)

其他要点:

  • 只支持 Best Effort 是强制底线,其他都是可选实现;
  • 请求里写 "do not care"(默认值)的参数,响应里也必须原样回 "do not care";
  • Guaranteed 模式下不允许使用默认值,且响应必须把通配参数填成具体值;Token Bucket Size 还必须 ≤ MTU。

💡 通俗类比:Token Rate 是"平均月薪",Token Bucket Size 是"你的信用卡额度"(平时省着花,急用可以一波透支),Peak Bandwidth 是"单日消费上限",Access Latency 是"从下单到送达最长等多久"。

3.4 Retransmission and Flow Control Option ------ 重传与流控(Type 0x04)

Length = 9 字节,这是内容最丰富、最容易踩坑的选项:

(1)Mode ------ 信道工作模式(唯一的可协商参数)

|------|----------------------------------------|--------------------------|
| 值 | 模式 | 说明 |
| 0x00 | Basic Mode | 默认模式,最原始的透传,其余 6 个参数全部忽略 |
| 0x01 | Retransmission Mode | 重传模式(老协议遗留,仅为兼容旧设备保留) |
| 0x02 | Flow Control Mode | 流控模式(同上,仅向后兼容) |
| 0x03 | Enhanced Retransmission Mode(ERTM) | 增强重传模式,实际工程中最常用的可靠模式 |
| 0x04 | Streaming Mode | 流模式,低延迟、不重传 |

选择建议(协议原文):

  • 需要可靠信道 → 用 ERTM;
  • 设置了有限的 Flush Timeout → 用 Streaming;
  • Retransmission / Flow Control 模式只在对方不支持 ERTM/Streaming 时出于兼容目的使用;
  • 使用 ERTM/Streaming 前必须确认对端已在 Extended Features Mask (通过 L2CAP_INFORMATION_RSP 交互)里声明支持,否则不能发。
(2)其余 5 个参数一览

|------------------------|----|-----------------------------------------------------------------|-----------------------------------------------------------------------------------|
| 参数 | 大小 | 作用 | 备注 |
| TxWindow Size | 1B | 发送窗口:Flow Control / Retransmission 模式取值 132,**ERTM 取值 163** | ERTM 下是信息性参数,表示最多收多少个 I-frame 不回 ACK;窗口越大信道利用率越高 |
| MaxTransmit | 1B | 同一帧最多尝试发送次数 | ERTM 下控制 I-frame 与 P=1 的 S-frame 重传次数;ERTM 下 0 = 无限重传,Retransmission 模式下禁止为 0 |
| Retransmission Timeout | 2B | 重传定时器初值(ms) | ERTM 下用于检测 I-frame 丢失并触发错误恢复 |
| Monitor Timeout | 2B | 监视定时器(ms) | ERTM 下用于检测 P=1 的 S-frame 丢失;链路空闲时周期性发 S-frame 保活 |
| MPS | 2B | 最大 PDU 载荷 | 各自声明;正响应中必须 ≤ 请求值 |

注意 MTU 与 MPS 的区别:MTU 限制的是上层 SDU(整包)的大小,MPS 限制的是每个 L2CAP PDU 分片(载荷)的大小。一个大的 SDU 会拆成多个 MPS 大小的 PDU 发送。

(3)模式冲突怎么办?------ State 1 / State 2 优先级仲裁

ERTM 和 Streaming 是双向必须一致的模式(不像老的重传/流控模式可以两方向配不同的模式)。当两边想用的模式对不上时,协议定义了两种状态:

  • State 1:设备有偏好模式,但愿意妥协;
  • State 2:上层(Profile)强制要求某种模式,不许妥协。

State 1 下的优先级(从高到低):

复制代码
Basic Mode  >  Enhanced Retransmission  >  Streaming  >  其他

⚠️ 是不是很反直觉?Basic 反而优先级最高。因为 Basic 是人人必会的模式,选它兼容性最稳。

State 1 的协商流程:

复制代码
本地设备(想用 ERTM)                远端设备(偏好 Basic, State 1)
          │                                        │
          │──── CONFIGURATION_REQ (Mode=ERTM) ────►│
          │                                        │ Basic 优先级最高,拒绝
          │◄──── CONFIGURATION_RSP (Negative,      │
          │       建议改用 Basic) ─────────────────│
          │                                        │
          │  收到负响应,重新提议 Basic              │
          │──── CONFIGURATION_REQ (Mode=Basic) ───►│
          │                                        │ 通过!
          │◄──── CONFIGURATION_RSP (Positive) ─────│
          │                                        │
          │        (反过来远端主动发配置时同理)      │

State 2 的规则非常简单粗暴 :双方模式必须完全一致,谈不拢就直接断开信道,没有第二次机会。

还有一个工程细节:信道进入 OPEN 状态之后再重配置(reconfigure) ,ERTM/Streaming 下只允许改 Retransmission Timeout 和 Monitor Timeout,其他参数一律不许改。

3.5 FCS ------ 帧校验序列(Type 0x05)

|------|-------------------------|
| 值 | 含义 |
| 0x00 | No FCS ------ S/I 帧不带校验 |
| 0x01 | 16-bit FCS(默认) |

要点:

  • 不可协商;只在 ERTM 或 Streaming 模式下有效,其他模式下禁用;
  • 前提同样是对端在 Extended Features Mask 里声明支持 FCS 选项;
  • "No FCS" 成立的条件非常苛刻 :必须双方都在 CONFIGURATION_REQ 里显式发送 FCS Type=0x00。只要有一方没发 FCS 选项、或发的不是 0x00,就用默认值 0x01(带 FCS)。

💡 通俗类比:FCS 像快递包裹上的"易碎封条"。默认贴封条(16 位校验),除非你我都白纸黑字确认"不需要封条",否则一律照贴。

3.6 Extended Flow Specification ------ 扩展流规范(Type 0x06)

|------------------------|----|----|----------------------------------------------------------|
| 参数 | 大小 | 单位 | 说明 |
| Identifier | 1B | - | 流规范的唯一标识(物理链路范围内唯一);Best Effort 固定为 0x01 |
| Service Type | 1B | - | No Traffic / Best Effort(默认)/ Guaranteed |
| Maximum SDU Size | 2B | 字节 | 应用提交的最大 SDU;0xFFFF = 未知 |
| SDU Inter-arrival Time | 4B | 微秒 | 相邻 SDU 的产生间隔;0xFFFFFFFF = 未知 |
| Access Latency | 4B | 微秒 | 空口连续发送机会的最大间隔 |
| Flush Timeout | 4B | 微秒 | SDU 各分片在 L2CAP/Controller 中存活的最长时间;0xFFFFFFFF = 永不丢弃(可靠) |

核心亮点与规则:

  1. 一旦双方都声明支持 Extended Flow Spec,所有信道都必须用它,且 QoS 选项和 Flush Timeout 选项同时禁用(新老不能混用);
  2. Guaranteed 类型细分三档:
  • 保证带宽:只要速率,不管延迟(Access Latency = 0xFFFFFFFF);
  • 保证延迟:只要延迟,不管速率(SDU Inter-arrival = 0xFFFFFFFF),典型如 HID 中断通道、AVRCP;
  • 带宽 + 延迟双保证:典型如音视频流(A2DP);
  1. 一个信道不许一方向 Best Effort、另一方向 Guaranteed------这样配直接断链;但 "No Traffic + Best Effort" / "No Traffic + Guaranteed" 的组合是合法的;
  2. 配置完成后 Service Type 不允许通过重配置改变;
  3. 几个经验值:Access Latency 要扣除 HCI 传输延迟(如应用要求 20ms、HCI 延迟 5ms,就填 15ms);Flush Timeout 应大于 Access Latency,并考虑接收端去抖动缓冲的容量;Flush Timeout 填 0x00000000 表示发了就扔、绝不重传,适合游戏手柄这种"宁可要新鲜数据也不要旧数据"的场景。

3.7 Extended Window Size ------ 扩展窗口大小(Type 0x07)

ERTM 模式下 TxWindow 最大只有 63,吞吐量受限。这个选项把窗口扩到 15 位(0x0001~0x3FFF,即最多 16383 帧):

|------------------|----------|----------------|
| 取值 | ERTM | Streaming |
| 0x0000 | ❌ 无效 | ✅ 有效(启用扩展控制字段) |
| 0x0001 ~ 0x3FFF | ✅ 有效窗口大小 | ❌ 无效 |

要点:

  • 不可协商,且前提是对端在 Extended Features Mask 中声明支持;
  • Basic 模式下收到该选项必须忽略;
  • 一旦任一方向配置成功,双方所有 S/I 帧都必须使用扩展控制字段(Extended Control Field,16 位序号)------即使只有单方向配置了该选项;
  • 单方向配置成功后,反方向的最大窗口沿用 Retransmission & Flow Control 选项里的旧值。

4. 汇总

复制代码
                     L2CAP 配置参数选项
                                   │
        ┌──────────────┬───────────┼───────────┬──────────────┬──────────────┐
        ▼              ▼           ▼           ▼              ▼              ▼
   0x01 MTU       0x02 Flush   0x03 QoS    0x04 RTX&FC     0x05 FCS      0x06 Ext Flow Spec
   收包能力        超时放弃      令牌桶流控   模式选择         帧校验         增强流规范
   (信息性)       (可协商)     (可协商)    ┌─────────┐     (不可协商)     (不可协商)
                                             │Mode:    │
                                             │Basic/   │      0x07 Ext Window
                                             │ERTM/    │      ERTM窗口扩到
                                             │Streaming│      16383 帧
                                             └─────────┘
   ◄──────────── BR/EDR 专属 ◄──┘                     ◄── 需 ERTM/Streaming 模式配合 ──►

再给一张速查表,开发时直接对着填参数:

|------------------|----------------------------------------------------------------|
| 开发场景 | 推荐配置 |
| 普通数据传输(如 SPP 透传) | MTU 0x01 + ERTM(0x04,Mode=0x03),大吞吐再加 0x07 扩窗口 |
| 音频流(A2DP 场景思路) | 0x06 Extended Flow Spec(Guaranteed),Flush Timeout 按缓冲容量设 |
| 游戏手柄 / 遥感类低延迟 | Streaming Mode(0x04, Mode=0x04)+ FCS=0x00 或 Flush Timeout=0 |
| 兼容老设备 | Basic Mode(一切默认即可),老的重传/流控模式仅做兼容 |
| LE 设备(ATT/GATT) | 不用本节选项!LE 固定信道直接用默认 MTU=23;LE Credit Based 信道在连接信令里直接带 MTU/MPS |

5. 写在最后:3 个最容易踩的坑

  1. MTU/MPS/SDU 三者的关系:MTU 管 SDU(一整包应用数据),MPS 管 PDU(线上的分片)。收端缓冲按 SDU 算,线上帧长按 MPS 算。抓包时看到一包大 SDU 被拆成多个小 PDU,就是 MPS 在起作用。
  2. Mode 优先级反直觉:State 1 下 Basic 模式优先级最高。别以为"我提议了高级模式对方就该听我的"------对方完全可以按协议把你打回 Basic,这不算 bug。
  3. 选项前提条件链 :ERTM / Streaming / FCS / Ext Window 这四个选项都依赖对端在 Extended Features Mask 中先声明支持,配置流程开始前先通过 L2CAP_INFORMATION_REQ/RSP(Feature Mask)摸清对方底细,能省掉大量无效协商。
相关推荐
幻智星科技2 小时前
纯FPGA网络通信实现微秒级确定性延迟
网络协议·fpga开发·硬件架构
码林鼠4 小时前
flutter网络http请求入门教程-dio
网络协议·flutter·http
金瑞铭5 小时前
RFID称重货架-科研实验室试剂精准管控
物联网·rfid·iot
数字新视界5 小时前
信创机房动环监控的核心功能与智能运维管理实践
大数据·物联网·信息可视化·信息与通信·一体化机柜
蓝策电子6 小时前
地下停车场室内定位系统部署指南:蓝牙AoA技术落地实践
物联网
zxanz16 小时前
免费 SSL 证书的隐性成本:续期压力、稳定性与商业风险评估
网络·网络协议·ssl
zzu123zsw7 小时前
面对多 RPC 扇入超时崩溃:深入 asyncTool 核心 DAG 编排与线程池隔离防护
java·网络·spring boot·网络协议·rpc·并发编程
福娃筱欢7 小时前
金仓数据库配置SSL双向认证
数据库·网络协议·ssl
Quanqiucard7 小时前
四轮巡检机器人物联网卡怎么选?5 条硬指标 + 4 重干扰
物联网·机器人