2. Linux 内核蓝牙子系统架构:BlueZ 协议栈全景
BlueZ 是 Linux 官方蓝牙协议栈,自 Linux 2.4.6 起被纳入主线内核。它采用内核空间 + 用户空间双层架构,将实时性要求高的底层协议(HCI、L2CAP)放在内核态,将灵活性强的高层协议(RFCOMM、GATT)及配置文件放在用户态。这种分工既保证了蓝牙通信的实时响应,又降低了内核模块的复杂度。
架构核心原则:内核只做“数据搬运与链路管理”,用户空间负责“协议解析与业务逻辑”。
2.1 内核空间与用户空间的分工
BlueZ 将协议栈切分为两个域,通过 Socket 接口(AF_BLUETOOTH)和 D-Bus 进行跨域通信。下表列出了各层归属与职责边界:
| 域 | 包含层/模块 | 主要职责 | 通信方式 |
|---|---|---|---|
| 用户空间 | 应用程序、GATT 配置文件、RFCOMM 服务、SDP、BlueZ 工具(bluetoothd、bluetoothctl) | 高层协议解析、配置文件注册、用户交互、策略控制 | D-Bus(bluetoothd 提供 org.bluez 接口) |
| 内核空间 | HCI 层、L2CAP 层、蓝牙设备驱动(hci_uart、btusb 等) | HCI 命令/事件处理、L2CAP 信道管理、数据帧收发、硬件控制 | Socket(AF_BLUETOOTH)、Netlink |
设计考量:将 L2CAP 放在内核态是因为它需要处理实时性高的分段重组(SAR)和重传;而 RFCOMM 和 GATT 放在用户态,便于灵活升级协议版本和配置文件,无需重新编译内核。
2.2 HCI 层(Host Controller Interface)
HCI 层是蓝牙协议栈的基石,位于主机(Host)与蓝牙控制器(Controller)之间。它定义了标准的命令、事件和数据包格式,屏蔽了底层传输介质(UART、USB、SDIO)的差异。
HCI 层在内核中由 net/bluetooth/hci_core.c 实现,核心数据结构包括:
- hci_dev:代表一个蓝牙控制器,包含设备状态、缓冲队列、命令发送等待队列。
- hci_conn:代表一条 ACL 或 SCO 连接,管理连接状态与链路参数。
- hci_cb:回调函数表,供上层 L2CAP 注册接收数据回调。
关键接口:HCI 层向上层提供
hci_send_cmd()、hci_recv_frame()、hci_conn_enter_active_mode() 等核心 API。
2.3 L2CAP 层(Logical Link Control and Adaptation Protocol)
L2CAP 位于 HCI 之上,提供逻辑信道复用、分段重组(SAR)、服务质量(QoS)控制等功能。它是所有高层协议(RFCOMM、GATT、SDP)的传输基础。
| 功能 | 说明 | 内核实现文件 |
|---|---|---|
| 信道复用 | 通过 CID(Channel ID)区分不同上层协议的数据流 | net/bluetooth/l2cap_core.c |
| 分段重组 | 将大于 HCI 数据包 MTU 的 L2CAP SDU 拆分为多个 HCI ACL 数据包 | net/bluetooth/l2cap_core.c |
| 重传与流控 | 支持 ERTM(Enhanced Retransmission Mode)和 Streaming Mode | net/bluetooth/l2cap_core.c |
| 连接管理 | 处理连接请求、配置参数、断开连接等信令 | net/bluetooth/l2cap_sock.c |
常见陷阱:L2CAP 的 MTU 协商必须在连接建立阶段完成。如果上层协议(如 GATT)未正确协商 MTU,可能导致数据包被丢弃或连接异常断开。内核日志中常见的
l2cap_sock_recvmsg: sock len 0 错误往往与此相关。
2.4 RFCOMM 层(Radio Frequency Communication)
RFCOMM 是一个串口仿真协议,它在 L2CAP 之上模拟 RS-232 串行接口,使得传统串口应用(如蓝牙串口调试、拨号网络)可以无缝迁移到蓝牙上。RFCOMM 在 BlueZ 中作为内核模块实现,但会话管理和 DLC(数据链路连接)控制由用户空间的 bluetoothd 协调。
RFCOMM 的核心概念:
- DLCI(Data Link Connection Identifier):标识一条 RFCOMM 数据链路,范围 0~63。
- MSC(Modem Status Command):模拟串口控制信号(RTS/CTS/DTR/DSR)。
- PN(Parameter Negotiation):协商最大帧大小、波特率等参数。
典型应用:Android 的蓝牙串口(SPP)配置文件、Linux 的
rfcomm 工具、车载蓝牙免提(HFP)的音频/命令通道均基于 RFCOMM。
2.5 GATT 层(Generic Attribute Profile)
GATT 是 BLE(Bluetooth Low Energy)的核心配置文件,定义了属性(Attribute)的组织方式和交互流程。在 BlueZ 中,GATT 的实现分为两部分:
- 内核部分:
net/bluetooth/gatt.c提供属性数据库的底层存储和基本读写操作。 - 用户空间部分:
bluetoothd中的 GATT 插件实现服务发现、通知、指示等高层逻辑。
GATT 操作的核心流程:
- 服务发现:客户端发送
Read By Group Type请求,获取服务列表。 - 特征发现:客户端发送
Read By Type请求,获取特征声明及其属性句柄。 - 属性读写:客户端通过
Read Request/Write Request操作具体属性。 - 通知/指示:服务端主动推送数据,客户端通过 CCCD(客户端特征配置描述符)使能。
调试技巧:使用
btmon 或 hcidump 抓取 HCI 日志,可以清晰看到 GATT 的 ATT PDU 交互过程。例如 btmon -w gatt.log 导出日志后用 Wireshark 分析。
2.6 各层协作流程示例
以 BLE 设备连接并读取心率数据为例,展示各层协作:
| 步骤 | 涉及层 | 动作描述 |
|---|---|---|
| 1 | HCI | 主机发送 LE Create Connection 命令,控制器发起广播扫描与连接 |
| 2 | L2CAP | 连接建立后,L2CAP 创建 ATT 信道(CID=0x0004),协商 MTU |
| 3 | GATT | 客户端通过 ATT 信道发送 Exchange MTU Request,协商最大传输单元 |
| 4 | GATT | 客户端发送 Read By Group Type 发现心率服务 |
| 5 | GATT | 客户端发送 Read By Type 发现心率测量特征 |
| 6 | GATT | 客户端写入 CCCD 描述符,使能通知 |
| 7 | HCI | 控制器收到心率数据,通过 HCI ACL 数据包上传至主机 |
| 8 | L2CAP → GATT | L2CAP 重组数据包,GATT 解析 ATT Handle Value Notification,回调应用层 |
性能关键点:整个链路中,HCI 层的 ACL 数据包大小(默认 27 字节)是 BLE 吞吐量的主要瓶颈。通过 L2CAP MTU 协商(最大可到 251 字节)和 DLE(Data Length Extension)可以显著提升传输效率。