3. 蓝牙硬件接口与驱动模型:UART、USB、SDIO

蓝牙控制器(Controller)与主机(Host)之间的物理连接方式,直接决定了驱动模型的设计、数据吞吐量、延迟特性以及功耗表现。在 Linux 蓝牙子系统中,UART、USB 和 SDIO 是三种最主流的硬件接口。本章将深入分析这三种接口的电气与协议特点,并详细拆解对应 Linux 蓝牙驱动的注册与探测流程。

3.1 三种主流蓝牙硬件接口特点对比

下表从物理层、传输速率、适用场景、Linux 驱动模块四个维度对 UART、USB、SDIO 进行横向对比:

特性 UART (串口) USB SDIO
物理层 TX/RX 两线(+流控 RTS/CTS) D+/D- 差分信号 CLK/CMD/DAT0~DAT3
传输速率 最高 4 Mbps (H5 协议) USB 2.0: 12 Mbps (全速) / 480 Mbps (高速) SDIO 3.0: 可达 200+ Mbps
协议封装 H4 / H5 / BCSP / H:4 USB 批量传输 (Bulk) / 中断传输 SDIO 块传输 (Block Mode)
典型功耗 极低 (无协议开销时) 中等 (需 USB 总线轮询) 较低 (支持 SDIO 休眠)
适用场景 嵌入式 IoT、穿戴、车载 PC、笔记本、开发板 Wi-Fi/BT 组合模块、高性能嵌入式
Linux 驱动模块 hci_uart + 协议驱动 btusb btintel / btrtl (通过 MMC 子系统)
HCI 传输层 UART 字节流 USB 批量端点 SDIO 函数接口
选型建议: 对于低功耗 BLE 应用,UART 接口因其极低的空闲功耗和简单的硬件连线,仍然是嵌入式设备的首选。USB 接口适合需要高吞吐量(如 LE Audio 广播)且对功耗不敏感的平台。SDIO 接口则常见于需要同时支持 Wi-Fi 和蓝牙的组合芯片方案。
三种蓝牙硬件接口连接架构对比 UART 接口 主机 SoC (UART) H4 / H5 协议层 蓝牙控制器 (UART) USB 接口 主机 USB 控制器 USB 批量/中断传输 蓝牙 Dongle / 模块 SDIO 接口 主机 MMC/SDIO 控制器 SDIO 块传输协议 组合芯片 (Wi-Fi/BT)

3.2 Linux 蓝牙驱动注册与探测流程

Linux 蓝牙驱动遵循内核的设备驱动模型(Device Driver Model)。每个蓝牙硬件接口对应一个具体的驱动模块,其核心任务是:注册驱动 → 匹配设备 → 探测(probe) → 初始化 HCI 设备 → 注册到蓝牙子系统。

3.2.1 hci_uart 驱动(UART 接口)

hci_uart 是一个核心框架驱动,它本身不直接操作硬件,而是通过注册不同的线路规程(Line Discipline)来支持多种 UART 协议(H4、H5、BCSP 等)。

注册与探测流程:

  1. 模块初始化: hci_uart_init() 调用 tty_register_ldisc() 注册线路规程 N_HCI。
  2. 设备匹配: 当 UART 设备节点(如 /dev/ttyS0)被用户空间工具(如 hciattach 或 btattach)打开时,内核通过线路规程编号匹配到 hci_uart。
  3. probe 过程: hci_uart_tty_open() 被调用,分配 hci_uart 私有数据结构,初始化读写缓冲区。
  4. 协议绑定: 用户空间工具通过 ioctl 设置协议类型(如 HCIUARTSETPROTO),驱动加载对应的协议驱动(如 h4_open())。
  5. HCI 设备注册: 协议驱动调用 hci_alloc_dev() 和 hci_register_dev(),将 UART 蓝牙控制器注册到 BlueZ 协议栈。
// hci_uart 核心注册流程伪代码
static int __init hci_uart_init(void)
{
    int err = tty_register_ldisc(N_HCI, &hci_uart_ldisc);
    if (err)
        BT_ERR("Failed to register line discipline");
    return err;
}

static int hci_uart_tty_open(struct tty_struct *tty)
{
    struct hci_uart *hu = kzalloc(sizeof(*hu), GFP_KERNEL);
    tty->disc_data = hu;
    hu->tty = tty;
    // 初始化锁、缓冲区、工作队列
    return 0;
}
常见问题: UART 蓝牙驱动经常遇到流控(RTS/CTS)配置不匹配导致数据丢包的问题。在 probe 阶段,务必确认硬件流控已通过 termios 正确开启,否则 BLE 连接可能频繁断开。

3.2.2 btusb 驱动(USB 接口)

btusb 是 Linux 中最成熟的蓝牙驱动之一,支持绝大多数 USB 蓝牙适配器。它利用 USB 核心的 usb_driver 框架进行设备管理。

注册与探测流程:

  1. 模块初始化: btusb_init() 调用 usb_register() 注册 btusb_driver。
  2. 设备匹配: USB 核心根据 id_table(VID/PID 列表)匹配蓝牙设备。
  3. probe 过程: btusb_probe() 被调用,执行以下关键步骤:
    • 获取 USB 接口描述符,找到批量输入、批量输出和中断端点。
    • 分配 btusb_data 结构体,初始化 URBs(USB Request Blocks)。
    • 调用 usb_submit_urb() 提交中断 URB 用于接收 HCI 事件。
  4. HCI 设备注册: 调用 hci_register_dev(),将 USB 蓝牙设备注册到 HCI 核心。
  5. 后处理: 对于 Intel、Realtek 等特定芯片,btusb 会调用 btintel 或 btrtl 的辅助函数进行固件加载。
// btusb probe 核心片段
static int btusb_probe(struct usb_interface *intf,
                       const struct usb_device_id *id)
{
    struct usb_device *udev = interface_to_usbdev(intf);
    struct btusb_data *data;

    data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
    data->udev = udev;
    data->intf = intf;

    // 查找端点
    data->isoc_in = usb_altnum_to_altsetting(intf, 1);
    // 初始化 URBs
    btusb_init_urbs(data);
    // 注册 HCI 设备
    hci_register_dev(data->hdev);
    return 0;
}

3.2.3 btintel 与 btrtl 驱动(SDIO 及专用接口)

btintel 和 btrtl 通常作为辅助驱动模块存在,它们不直接注册为 USB 或 SDIO 驱动,而是被 btusb 或 hci_uart 调用,用于处理芯片特定的初始化序列、固件下载和配置命令。

btintel 驱动特点:

  • 通过 btintel_register() 向 HCI 设备注册回调函数。
  • 在 probe 阶段执行 btintel_setup(),读取芯片版本号,判断是否需要加载新固件。
  • 支持 Secure Send 和 Bootloader 模式切换。

btrtl 驱动特点:

  • 通过 btrtl_initialize() 初始化 Realtek 芯片。
  • 在 probe 阶段从文件系统加载 rtl_bt/ 目录下的固件补丁文件。
  • 处理 USB 和 UART 两种接口的差异,通过 btrtl_download_firmware() 完成固件写入。
Linux 蓝牙驱动注册与探测流程 1. 驱动模块注册 usb_register / tty_register_ldisc 2. 设备匹配 id_table / 线路规程匹配 3. probe 调用 分配数据结构 / 初始化端点 芯片特定初始化 (btintel / btrtl) 4. HCI 设备注册 hci_alloc_dev + hci_register_dev 5. BlueZ 协议栈就绪 HCI 核心 / L2CAP / GATT 层 说明:btintel 和 btrtl 通常作为辅助模块被 btusb 或 hci_uart 调用,不独立注册为总线驱动。 probe 失败时,驱动应返回 -ENODEV,内核不会创建对应的 hciX 设备。

3.3 驱动探测流程中的关键数据结构

在 probe 过程中,驱动需要正确初始化以下核心数据结构,才能保证蓝牙控制器正常工作:

数据结构 所属模块 关键字段 说明
hci_dev 蓝牙核心 bus, dev_type, send_frame 代表一个蓝牙控制器实例,bus 字段标识接口类型(UART/USB/SDIO)
btusb_data btusb isoc_iface, bulk_tx_urb USB 蓝牙驱动的私有数据,管理所有 URB 和端点信息
hci_uart hci_uart tty, proto, hdev UART 蓝牙驱动的私有数据,绑定 TTY 设备和协议操作函数
btintel_data btintel boot_param, firmware Intel 芯片的固件管理状态,存储下载进度和版本信息
调试技巧: 在 probe 函数中添加 dev_info() 或 BT_INFO() 打印,可以快速定位驱动是否成功匹配设备。使用 lsusb -v 查看 USB 蓝牙设备的端点描述符,确保驱动解析的端点地址与硬件一致。

3.4 本章小结

  • UART 接口 依赖线路规程和 hci_uart 框架,适合低功耗嵌入式场景,但需要用户空间工具辅助触发 probe。
  • USB 接口 通过 btusb 驱动实现全自动探测,支持热插拔,是 Linux 桌面和服务器平台的标准方案。
  • SDIO 接口 通常由组合芯片驱动(如 btintel)通过 MMC 子系统完成探测,probe 流程中固件加载是关键步骤。
  • 所有蓝牙驱动最终都通过 hci_register_dev() 将控制器注册到 BlueZ 协议栈,后续的 L2CAP、GATT 等高层协议对底层接口透明。