Linux内核蓝牙驱动L2CAP层工程实践与调试 - 第1节

1.1 L2CAP 在蓝牙协议栈中的位置

逻辑链路控制与适配协议(Logical Link Control and Adaptation Protocol,L2CAP)位于蓝牙协议栈的数据链路层,是 Host 端协议栈的核心中间件。它向上承载 ATT、SMP、RFCOMM、BNEP 等上层协议,向下通过 HCI 与 Controller 的 Link Manager 和 Baseband 通信。

L2CAP 是蓝牙协议栈中唯一同时处理分段重组、协议复用和服务质量的层,理解它的位置是调试蓝牙驱动的第一步。
蓝牙协议栈分层架构(L2CAP 位置) 应用层(Profiles) ATT SMP RFCOMM BNEP L2CAP 层 分段重组 · 协议复用 · QoS HCI(主机-控制器接口) Link Manager / Baseband 蓝牙射频(2.4 GHz) L2CAP 是上层协议与 HCI 之间的桥梁,所有逻辑信道都必须经过 L2CAP 复用/解复用 Linux 内核中 L2CAP 实现位于 net/bluetooth/l2cap_core.c

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(仅增强模式)
L2CAP 分段重组流程 上层 SDU(1500 字节) L2CAP 分段 PDU #1 PDU #2 PDU #3 L2CAP 重组 完整 SDU 分段时每个 PDU 携带 4 字节 L2CAP 头部(长度 + 信道 ID),接收端根据长度字段判断 SDU 边界 增强模式(ERTM)下使用 Sequence Number 保证有序重组,基本模式(Basic)依赖 HCI 顺序

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 内核中数据流路径如下:

  1. 上层协议调用 l2cap_chan_send() 发送 SDU。
  2. L2CAP 层将 SDU 分段为 PDU,调用 hci_send_acl() 下发。
  3. HCI 层将 PDU 封装为 HCI ACL 数据包,通过 UART/USB/SDIO 发送给 Controller。
  4. 接收方向:Controller 上报 HCI ACL 数据包 → HCI 层解析 → l2cap_recv_acldata() 重组 → 递交给上层。
L2CAP 与 HCI 数据流 上层协议 L2CAP 层 HCI 层 蓝牙 Controller HCI ACL 数据包格式(发送方向) | HCI 头部(4 字节) | L2CAP 头部(4 字节) | L2CAP 载荷(SDU 分段) | Linux 内核关键函数调用链 l2cap_chan_send() → l2cap_segment() → hci_send_acl() → hci_queue_acl() 接收:hci_rx_work() → hci_acldata_packet() → l2cap_recv_acldata() → l2cap_reassemble()

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)决定了分段重组和重传策略,调试时需根据模式选择不同的排查方向。