Linux蓝牙HCI驱动开发与中断数据通路调试 - 第1节

1.1 蓝牙核心协议栈分层模型

蓝牙协议栈并非单一协议,而是一套分层的通信协议体系。从底层射频到上层应用,每一层都承担明确的职责。理解分层模型是编写蓝牙驱动的第一步。

核心要点: Linux 蓝牙驱动开发主要关注 HCI(主机-控制器接口)层及以上的协议栈,而控制器固件(如 CSR、Broadcom、Intel)通常由芯片厂商提供。

1.1.1 蓝牙核心协议(HCI / L2CAP / RFCOMM)

协议层 全称 核心职责 驱动开发关注点
HCI Host Controller Interface 主机与蓝牙控制器之间的命令、事件、数据通道 HCI 传输层驱动(UART、USB、SDIO)
L2CAP Logical Link Control and Adaptation Protocol 分段/重组、协议复用、服务质量(QoS) Socket 接口、通道注册
RFCOMM Radio Frequency Communication 串口仿真,承载传统蓝牙 SPP 服务 TTY 设备层、用户态接口

下图展示了蓝牙核心协议栈在主机与控制器之间的分层关系:

蓝牙核心协议栈分层架构 应用层(SPP / A2DP / HID / GATT) RFCOMM(串口仿真协议) L2CAP(逻辑链路控制与适配协议) HCI(主机-控制器接口 - 主机侧) HCI 传输层(UART / USB / SDIO) 蓝牙控制器(固件 / 链路层 / 射频) 驱动开发核心区域:HCI 及以上协议栈,控制器固件由芯片厂商提供
开发提示: 在 Linux 内核中,HCI 层是驱动开发者最常打交道的协议层。无论是通过 UART 还是 USB 连接蓝牙芯片,最终都需要注册一个 struct hci_dev 设备,并通过 HCI 命令与控制器交互。

1.2 Linux 内核蓝牙子系统架构

Linux 内核的蓝牙子系统(BlueZ)采用模块化设计,从上到下依次为:套接字层 → 协议层 → HCI 核心层 → HCI 传输层驱动。每个模块都可以作为内核模块独立加载或卸载。

1.2.1 核心模块与依赖关系

模块名称 内核路径 功能描述 依赖
bluetooth.ko net/bluetooth/ 蓝牙子系统核心,注册 AF_BLUETOOTH 协议族 无
btusb.ko drivers/bluetooth/ USB 接口蓝牙控制器 HCI 传输层驱动 bluetooth.ko, usbcore
hci_uart.ko drivers/bluetooth/ UART 接口蓝牙控制器 HCI 传输层驱动 bluetooth.ko, serdev/tty
btintel.ko drivers/bluetooth/ Intel 蓝牙控制器专用配置与固件加载 bluetooth.ko
btbcm.ko drivers/bluetooth/ Broadcom 蓝牙控制器专用配置与固件加载 bluetooth.ko
常见陷阱: 加载顺序至关重要!必须先加载 bluetooth.ko 核心模块,再加载传输层驱动(如 btusb.ko)。如果顺序颠倒,传输层驱动将无法找到 HCI 核心注册接口,导致 modprobe 失败。

1.2.2 内核蓝牙子系统软件架构图

下图展示了 Linux 内核蓝牙子系统的完整软件栈,从用户态 socket 到底层硬件:

Linux 内核蓝牙子系统软件架构 用户态(BlueZ 工具:hcitool / bluetoothctl / hcidump) AF_BLUETOOTH 协议族 / 蓝牙 Socket 层 协议层(L2CAP / RFCOMM / SCO / BNEP / HIDP) 每个协议注册独立的 proto_ops 和 socket 类型 HCI 核心层(hci_core.ko) hci_dev 注册/注销 / 命令队列 / 事件分发 / ACL/SCO 数据调度 HCI 传输层驱动(btusb / hci_uart / btintel / btbcm) 实现 hci_transport_ops:open / close / send / recv / setup 蓝牙硬件控制器(UART / USB / SDIO 接口) 驱动开发重点:HCI 传输层驱动 + HCI 核心层回调实现

1.2.3 关键数据结构关系

在 Linux 内核蓝牙驱动中,以下三个数据结构构成了驱动开发的核心骨架:

  • struct hci_dev:代表一个蓝牙控制器设备,包含设备状态、命令队列、连接列表等。驱动通过 hci_alloc_dev() 分配,通过 hci_register_dev() 注册到内核。
  • struct hci_ops:HCI 核心层回调函数集合,包括 open、close、send、flush、setup 等。传输层驱动必须实现这些回调。
  • struct sk_buff:内核网络子系统标准数据包结构,蓝牙驱动使用它承载 HCI 命令、ACL 数据、SCO 数据和事件包。
驱动开发流程速览:
1. 分配 struct hci_dev → 2. 设置 hci_ops 回调 → 3. 配置设备地址、厂商信息 → 4. 调用 hci_register_dev() → 5. 实现中断处理函数接收 HCI 事件 → 6. 通过 hci_recv_frame() 将数据送入 HCI 核心层。

1.3 中断配置在蓝牙驱动中的角色

蓝牙控制器通过硬件中断通知主机有数据到达(HCI 事件、ACL 数据、SCO 数据)。中断处理函数的质量直接影响蓝牙吞吐量和系统实时性。

1.3.1 中断类型与处理策略

中断类型 触发条件 推荐处理策略 注意事项
HCI 事件中断 控制器发送事件包(命令完成、连接指示等) 上半部(硬中断)→ 调度 workqueue 或 tasklet 事件包通常较小,但频率高,不可在硬中断中长时间处理
ACL 数据中断 ACL 数据包到达(承载 L2CAP 及以上数据) 上半部 → NAPI 轮询或专用 RX 线程 ACL 数据量大,建议使用 NAPI 减少中断次数
SCO 数据中断 SCO 同步数据包到达(语音) 上半部 → 直接拷贝到音频缓冲区 SCO 对延迟敏感,避免使用 workqueue 引入调度延迟
错误/异常中断 控制器检测到硬件错误、超时等 上半部记录状态 → 调度错误恢复 work 需要配合定时器实现超时恢复机制
性能建议: 对于 USB 接口的蓝牙控制器,推荐使用 URB(USB Request Block) 配合中断端点接收数据,而不是在 USB 中断处理函数中直接处理。URB 的回调函数运行在软中断上下文,更适合执行数据解析和协议栈提交。

1.3.2 中断处理流程示例(UART 蓝牙)

以 UART 接口蓝牙控制器为例,中断处理流程如下:

  1. UART 硬件检测到 RX 数据,触发 IRQ。
  2. 中断处理函数读取 UART 接收寄存器,将数据存入临时缓冲区。
  3. 调用 hci_uart_proto->recv() 进行 HCI 协议解析(识别 HCI 事件/ACL/SCO 包类型)。
  4. 解析完成后,调用 hci_recv_frame() 将数据提交到 HCI 核心层。
  5. HCI 核心层根据包类型分发到对应的协议处理模块。
/* UART 蓝牙中断处理函数简化示例 */
static irqreturn_t hci_uart_irq_handler(int irq, void *dev_id)
{
    struct hci_uart *hu = dev_id;
    struct sk_buff *skb;
    unsigned char byte;

    while (uart_fifo_get(hu->port, &byte)) {
        /* 将字节送入 HCI UART 协议解析器 */
        hci_uart_proto_recv(hu, byte);
    }

    /* 如果解析完成一个完整的 HCI 包,则提交到核心层 */
    skb = hci_uart_proto_dequeue(hu);
    if (skb)
        hci_recv_frame(hu->hdev, skb);

    return IRQ_HANDLED;
}
中断上下文限制: 在硬中断处理函数中,禁止调用可能导致睡眠的函数(如 kmalloc(GFP_KERNEL)、mutex_lock())。如果需要分配内存,必须使用 GFP_ATOMIC 标志。推荐将大部分处理逻辑推迟到 tasklet、workqueue 或线程化中断中执行。

1.4 本章小结

  • 蓝牙协议栈采用分层设计,HCI 层是主机与控制器之间的关键接口,也是驱动开发的核心。
  • Linux 内核蓝牙子系统(BlueZ)模块化程度高,bluetooth.ko 为核心模块,传输层驱动(如 btusb.ko)必须依赖它。
  • 中断配置是蓝牙驱动性能的关键,需要根据数据类型(HCI 事件、ACL、SCO)选择不同的处理策略。
  • 驱动开发者需要熟练掌握 struct hci_dev、struct hci_ops 和 struct sk_buff 三个核心数据结构。