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 和蓝牙的组合芯片方案。
3.2 Linux 蓝牙驱动注册与探测流程
Linux 蓝牙驱动遵循内核的设备驱动模型(Device Driver Model)。每个蓝牙硬件接口对应一个具体的驱动模块,其核心任务是:注册驱动 → 匹配设备 → 探测(probe) → 初始化 HCI 设备 → 注册到蓝牙子系统。
3.2.1 hci_uart 驱动(UART 接口)
hci_uart 是一个核心框架驱动,它本身不直接操作硬件,而是通过注册不同的线路规程(Line Discipline)来支持多种 UART 协议(H4、H5、BCSP 等)。
注册与探测流程:
- 模块初始化:
hci_uart_init()调用tty_register_ldisc()注册线路规程N_HCI。 - 设备匹配: 当 UART 设备节点(如
/dev/ttyS0)被用户空间工具(如hciattach或btattach)打开时,内核通过线路规程编号匹配到hci_uart。 - probe 过程:
hci_uart_tty_open()被调用,分配hci_uart私有数据结构,初始化读写缓冲区。 - 协议绑定: 用户空间工具通过
ioctl设置协议类型(如HCIUARTSETPROTO),驱动加载对应的协议驱动(如h4_open())。 - 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 框架进行设备管理。
注册与探测流程:
- 模块初始化:
btusb_init()调用usb_register()注册btusb_driver。 - 设备匹配: USB 核心根据
id_table(VID/PID 列表)匹配蓝牙设备。 - probe 过程:
btusb_probe()被调用,执行以下关键步骤:- 获取 USB 接口描述符,找到批量输入、批量输出和中断端点。
- 分配
btusb_data结构体,初始化 URBs(USB Request Blocks)。 - 调用
usb_submit_urb()提交中断 URB 用于接收 HCI 事件。
- HCI 设备注册: 调用
hci_register_dev(),将 USB 蓝牙设备注册到 HCI 核心。 - 后处理: 对于 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()完成固件写入。
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 等高层协议对底层接口透明。