Linux内核蓝牙驱动L2CAP层工程实践与调试 - 第1节
1.1 L2CAP 在蓝牙协议栈中的位置
逻辑链路控制与适配协议(Logical Link Control and Adaptation Protocol,L2CAP)位于蓝牙协议栈的数据链路层,是 Host 端协议栈的核心中间件。它向上承载 ATT、SMP、RFCOMM、BNEP 等上层协议,向下通过 HCI 与 Controller 的 Link Manager 和 Baseband 通信。
L2CAP 是蓝牙协议栈中唯一同时处理分段重组、协议复用和服务质量的层,理解它的位置是调试蓝牙驱动的第一步。
1.2 L2CAP 核心功能
1.2.1 分段与重组(Segmentation & Reassembly)
上层协议(如 RFCOMM)发送的 SDU(Service Data Unit)可能远大于底层 Controller 的 MTU。L2CAP 负责将 SDU 切分为多个 PDU(Protocol Data Unit),通过 HCI 逐帧发送;接收端将 PDU 重组为完整的 SDU 再递交给上层。
| 概念 | 说明 | 典型值 |
|---|---|---|
| SDU | 上层协议的数据单元(如 ATT 命令、RFCOMM 帧) | ATT:≤ 512 字节;RFCOMM:≤ 65535 字节 |
| PDU | L2CAP 在 HCI 信道上发送的协议数据单元 | BR/EDR:≤ 1021 字节;LE:≤ 251 字节 |
| 分段 | 将 SDU 拆成多个 PDU,每个 PDU 携带 L2CAP 头部 | 头部 4 字节(长度 + CID) |
| 重组 | 接收端根据 PDU 顺序和长度字段恢复 SDU | 依赖 Sequence Number(仅增强模式) |
1.2.2 协议复用(Protocol Multiplexing)
L2CAP 使用 信道标识符(CID) 区分不同的上层协议实例。每个 CID 唯一标识一条逻辑信道,上层协议通过注册 CID 实现复用。Linux 内核中 CID 分配规则如下:
| CID 范围 | 用途 | 典型协议 |
|---|---|---|
| 0x0001 | 信令信道(固定) | L2CAP 连接/配置命令 |
| 0x0004 | ATT 信道(固定) | ATT 协议 |
| 0x0005 | LE 信令信道(固定) | LE L2CAP 信令 |
| 0x0006 | SMP 信道(固定) | SMP 协议 |
| 0x0040 – 0x007F | 动态分配(BR/EDR) | RFCOMM、BNEP |
| 0x0080 – 0x00BF | 动态分配(LE) | LE 面向连接信道 |
调试时遇到“unknown CID”错误,通常是因为上层协议未正确注册 CID,或对端使用了不兼容的 CID 分配策略。
1.2.3 服务质量(Quality of Service, QoS)
L2CAP 支持通过 QoS 选项 协商信道参数,包括:
- Flush Timeout:数据包在 Controller 中等待发送的最大时间(BR/EDR)。
- Service Type:Best Effort / Guaranteed。
- Token Rate & Token Bucket Size:流量整形参数(仅 BR/EDR)。
- Latency & Delay Variation:延迟与抖动约束(LE 连接参数)。
Linux 内核中 QoS 参数通过 struct l2cap_qos 结构体传递:
struct l2cap_qos {
__u8 qos_flags; /* 标志位 */
__u8 service_type; /* 服务类型 */
__u32 token_rate; /* 令牌速率 */
__u32 token_bucket_size; /* 令牌桶大小 */
__u32 peak_bandwidth; /* 峰值带宽 */
__u32 latency; /* 延迟 */
__u32 delay_variation; /* 抖动 */
};
1.3 L2CAP 与 HCI / 上层协议的关系
1.3.1 L2CAP 与 HCI 的接口
L2CAP 通过 HCI 的 ACL 数据信道 与 Controller 通信。每个 ACL 数据包包含一个 HCI 头部(4 字节)和 L2CAP PDU。Linux 内核中数据流路径如下:
- 上层协议调用
l2cap_chan_send()发送 SDU。 - L2CAP 层将 SDU 分段为 PDU,调用
hci_send_acl()下发。 - HCI 层将 PDU 封装为 HCI ACL 数据包,通过 UART/USB/SDIO 发送给 Controller。
- 接收方向:Controller 上报 HCI ACL 数据包 → HCI 层解析 →
l2cap_recv_acldata()重组 → 递交给上层。
1.3.2 L2CAP 与上层协议的接口
上层协议通过 L2CAP 信道(Channel) 与 L2CAP 层交互。Linux 内核使用 struct l2cap_chan 表示一条逻辑信道,关键字段包括:
| 字段 | 类型 | 说明 |
|---|---|---|
| scid / dcid | __u16 | 本地/远端 CID |
| psm | __u16 | 协议服务复用器(如 RFCOMM = 0x0003) |
| mode | __u8 | 信道模式(Basic / ERTM / Streaming) |
| imtu / omtu | __u16 | 入/出方向 MTU |
| ops | struct l2cap_chan_ops * | 回调函数(recv、send、state_change 等) |
上层协议必须实现
l2cap_chan_ops 中的 recv 回调,否则 L2CAP 层无法将重组后的 SDU 递交给上层。这是驱动开发中最常见的遗漏点。1.4 L2CAP 信道模式对比
L2CAP 支持三种信道模式,不同模式对分段重组和重传策略有显著影响:
| 模式 | 重传 | 流控 | 分段重组 | 典型应用 |
|---|---|---|---|---|
| Basic (L2CAP_MODE_BASIC) | 无 | 无 | 仅按长度字段重组 | ATT、SMP |
| ERTM (L2CAP_MODE_ERTM) | 选择性重传(SREJ) | 基于窗口 | 使用 Sequence Number | RFCOMM、BNEP |
| Streaming (L2CAP_MODE_STREAMING) | 无 | 无 | 使用 Sequence Number | 音视频传输 |
调试 ERTM 模式时,重点关注 TxWindow 和 RetransmissionTimeout 参数。Linux 内核默认 TxWindow = 32,若对端窗口较小,可能导致频繁的 SREJ 重传。
1.5 本章小结
- L2CAP 位于蓝牙协议栈的数据链路层,是 Host 端协议栈的中间枢纽。
- 核心功能包括分段重组(SDU ↔ PDU)、协议复用(CID 机制)和服务质量(QoS 协商)。
- L2CAP 通过 HCI ACL 数据信道与 Controller 通信,通过
struct l2cap_chan与上层协议交互。 - Linux 内核中 L2CAP 实现集中在
net/bluetooth/l2cap_core.c,关键函数包括l2cap_chan_send()、l2cap_recv_acldata()和l2cap_segment()。 - 信道模式(Basic / ERTM / Streaming)决定了分段重组和重传策略,调试时需根据模式选择不同的排查方向。