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

2.1 BlueZ 架构概览

BlueZ 是 Linux 内核官方蓝牙协议栈,自 Linux 2.4.6 起被纳入主线。它采用分层设计,将蓝牙协议栈划分为内核空间与用户空间两部分。内核空间负责时间敏感、与硬件紧密相关的底层协议;用户空间则承载高层协议、配置管理和应用接口。

架构要点: BlueZ 的分层边界以 HCI 层为界,HCI 以下(控制器固件、UART/USB 传输)由内核驱动管理,HCI 以上(L2CAP、RFCOMM、SCO、BNEP)在内核中实现,而 GAP、GATT、AVDTP 等 Profile 则在用户空间通过 D-Bus 与内核交互。

2.1.1 整体分层结构

BlueZ 架构分层图 用户空间 (User Space) bluetoothd (核心守护进程) | gatttool | hcitool | btmon D-Bus 接口 | BlueZ 库 (libbluetooth) 内核空间 (Kernel Space) bluetooth.ko 核心层 / 套接字接口 l2cap_core.c L2CAP 协议层 hci_core.c HCI 核心层 hci_uart.ko hci_usb.ko hci_sdio.ko 蓝牙控制器硬件 (Bluetooth Controller / Firmware)

2.1.2 模块职责划分

模块 内核对象 核心职责 关键文件
蓝牙核心层 bluetooth.ko 提供套接字接口 (AF_BLUETOOTH)、设备管理、协议注册框架 af_bluetooth.c, hci_core.c, mgmt.c
HCI 核心层 bluetooth.ko HCI 命令/事件/数据包分发、控制器状态机、ACL/SCO 数据通道 hci_core.c, hci_event.c, hci_sock.c
HCI 传输层 hci_uart.ko / hci_usb.ko 物理传输适配 (UART/USB/SDIO),HCI 帧封装与解封装 hci_ldisc.c, hci_usb.c, hci_sdio.c
L2CAP 层 bluetooth.ko 分段重组、协议多路复用、面向连接/无连接信道管理、MTU 协商 l2cap_core.c, l2cap_sock.c
工程提示: 在调试 L2CAP 问题时,经常需要同时查看 l2cap_core.c 中的信道状态机、hci_core.c 中的 ACL 数据包调度,以及传输层驱动的缓冲区管理。单一模块的日志往往不足以定位问题。

2.2 内核蓝牙核心层 (bluetooth.ko)

bluetooth.ko 是所有蓝牙协议栈内核模块的依赖基础。它不直接与硬件通信,而是提供协议注册、套接字抽象、设备生命周期管理三大基础设施。

2.2.1 核心层关键数据结构

  • struct bt_sock: 蓝牙套接字的通用表示,内嵌 struct sock,包含协议类型、信道指针、安全等级等字段。
  • struct hci_dev: 蓝牙控制器的内核抽象,包含设备状态、命令队列、ACL/SCO 数据队列、传输层回调函数指针。
  • struct l2cap_chan: L2CAP 信道的核心结构体,包含状态、SCID/DCID、MTU、分段重组缓冲区、QoS 参数。
// 核心层套接字创建流程 (简化)
static int bt_sock_create(struct net *net, struct socket *sock, int proto, int kern)
{
    struct bt_sock *sk;
    struct sock *sk_sock;

    // 1. 分配蓝牙套接字结构
    sk = bt_sk(sock_alloc(net, AF_BLUETOOTH, GFP_KERNEL));
    if (!sk)
        return -ENOMEM;

    // 2. 根据协议类型初始化 (L2CAP/RFCOMM/SCO)
    switch (proto) {
    case BTPROTO_L2CAP:
        sk_sock = l2cap_sock_alloc(net, sk, proto, GFP_KERNEL);
        break;
    case BTPROTO_RFCOMM:
        // ...
    }

    // 3. 绑定协议操作表
    sock->ops = &bt_sock_ops;
    return 0;
}

2.3 HCI 层 (hci_uart / hci_usb)

HCI 层是内核蓝牙协议栈与蓝牙控制器之间的桥梁。它分为 HCI 核心层和 HCI 传输层两部分。核心层处理协议逻辑,传输层处理物理介质差异。

2.3.1 HCI 传输层对比

传输类型 驱动模块 典型场景 关键挑战
UART (H4/H5/BCSP) hci_uart.ko 嵌入式设备、IoT 模组 (CC2564, CSR) 波特率匹配、流控、丢包恢复、H5 重传机制
USB hci_usb.ko PC 蓝牙适配器、开发板 USB 蓝牙 URB 管理、批量传输调度、固件加载
SDIO hci_sdio.ko 手机/平板内置蓝牙 中断处理、电源管理、高速传输

2.3.2 HCI 数据流路径

HCI 数据流路径 (发送方向) L2CAP / 上层协议 hci_core.c hci_uart.ko hci_usb.ko hci_sdio.ko 蓝牙控制器 (Controller)
调试陷阱: HCI 传输层驱动中的缓冲区管理错误(如 UART 驱动的 tty 翻转缓冲区溢出)会导致 ACL 数据包截断,进而引发 L2CAP 层 CRC 校验失败或分段重组超时。这类问题在日志中常表现为 "ACL data RX error" 或 "L2CAP reassembly timeout"。

2.4 L2CAP 层 (l2cap_core.c)

L2CAP (Logical Link Control and Adaptation Protocol) 是蓝牙协议栈中承上启下的关键层。它位于 HCI 之上,为上层协议(RFCOMM、BNEP、ATT、AVDTP)提供逻辑信道抽象。

2.4.1 L2CAP 核心功能

  • 协议多路复用: 通过信道标识符 (CID) 区分同一 ACL 链路上的不同上层协议。
  • 分段与重组 (SAR): 将上层大包分割为 HCI ACL 数据包发送,接收端重组还原。
  • MTU 协商: 连接建立时交换双方 MTU,避免数据包超限。
  • 流控与重传: 增强型 L2CAP 支持 ERTM 模式,提供选择性重传和丢包检测。

2.4.2 L2CAP 信道状态机

L2CAP 信道状态机 (面向连接信道) CLOSED CONNECT CONFIG OPEN DISCONNECT L2CAP_ConnectReq L2CAP_ConnectRsp 配置完成 配置完成 DisconnectReq 断开完成 → CLOSED

2.4.3 L2CAP 数据包结构

// L2CAP 基本帧头 (Basic L2CAP Header)
struct l2cap_hdr {
    __le16     len;        // 数据域长度 (不包括帧头本身)
    __le16     cid;        // 信道标识符 (Channel ID)
} __packed;

// L2CAP 面向连接信道帧 (B-Frame)
struct l2cap_conn_req {
    __le16     psm;        // 协议服务多路复用器
    __le16     scid;       // 源信道标识符
} __packed;

// L2CAP 配置请求帧
struct l2cap_conf_req {
    __le16     dcid;       // 目的信道标识符
    __le16     flags;      // 标志位
    __u8       data[];     // 配置选项 (MTU, Flush Timeout, QoS 等)
} __packed;

2.5 模块间数据流

理解数据流是调试 L2CAP 问题的基础。以下以一次典型的 L2CAP 数据发送为例,展示数据在各模块间的传递路径。

2.5.1 发送数据流 (上层 → 控制器)

  1. 上层协议调用: RFCOMM/BNEP 通过 l2cap_chan_send() 将数据提交给 L2CAP 层。
  2. L2CAP 分段: l2cap_segment() 根据 MTU 将 SDU 分割为多个 PDU,每个 PDU 添加 L2CAP 帧头。
  3. HCI 封装: l2cap_send_frame() 调用 hci_send_acl(),将 PDU 封装为 HCI ACL 数据包。
  4. 传输层发送: HCI 核心层将 ACL 数据包放入传输队列,由 hci_uart_send_frame() 或 hci_usb_send_frame() 发送到控制器。

2.5.2 接收数据流 (控制器 → 上层)

  1. 传输层接收: 控制器通过 UART/USB 中断上报 HCI ACL 数据包。
  2. HCI 核心分发: hci_acldata_packet() 根据连接句柄找到对应的 HCI 连接,将数据提交给 L2CAP 层。
  3. L2CAP 重组: l2cap_recv_frame() 根据 CID 找到信道,将分片数据重组为完整的 SDU。
  4. 上层交付: 重组完成后,通过 chan->ops->recv() 回调将数据交付给上层协议。
L2CAP 数据流 (发送与接收) 发送路径 (TX) 上层 → l2cap_chan_send() → l2cap_segment() → hci_send_acl() → hci_uart_send_frame() → 控制器 接收路径 (RX) 控制器 → hci_acldata_packet() → l2cap_recv_frame() → 重组 → chan->ops->recv() → 上层 关键调试点:分段重组缓冲区、MTU 匹配、CID 路由
工程实践要点:
  • 调试 L2CAP 发送问题时,优先检查 l2cap_segment() 中的 MTU 计算和 hci_send_acl() 的返回状态。
  • 调试接收问题时,关注 l2cap_recv_frame() 中的重组超时逻辑(L2CAP_RECV_TIMEOUT),该超时通常为 60 秒。
  • 使用 btmon 或 hcidump 抓取 HCI 日志,可以清晰看到 L2CAP 帧在 HCI 层之上的封装细节。

2.6 本章小结

知识点 核心内容 调试关注点
BlueZ 架构 用户空间 (bluetoothd) + 内核空间 (bluetooth.ko) + 控制器固件 D-Bus 接口错误、内核与用户空间版本不匹配
bluetooth.ko 套接字接口、协议注册、设备管理 协议注册失败、套接字创建错误
HCI 层 核心层 (hci_core.c) + 传输层 (hci_uart/hci_usb) 传输层缓冲区溢出、HCI 命令超时
L2CAP 层 信道管理、分段重组、MTU 协商、状态机 重组超时、CID 冲突、MTU 协商失败
数据流 上层 → L2CAP → HCI 核心 → HCI 传输 → 控制器 分段边界、ACL 数据包丢失、传输层重传