第四章 蓝牙驱动开发环境搭建

说实话,环境搭建这事儿,看着简单,但坑特别多。我见过太多人卡在这一步,源码下好了编译不过,或者编译完了跑不起来。今天我就把这几年的经验捋一捋,带你把这套环境搭得明明白白。

4.1 交叉编译工具链配置

做蓝牙驱动开发,你肯定不是在PC上直接编译。目标板通常是ARM架构,比如树莓派、RK系列或者高通平台。这时候就需要交叉编译工具链。

我个人习惯用Linaro提供的工具链,稳定且更新及时。以ARM64为例:

# 下载工具链
wget https://releases.linaro.org/components/toolchain/binaries/latest/aarch64-linux-gnu/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz

# 解压
tar -xvf gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu.tar.xz

# 设置环境变量
export CROSS_COMPILE=aarch64-linux-gnu-
export ARCH=arm64
export PATH=$PATH:/path/to/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin
注意:工具链版本要和内核版本匹配。我曾经用过一个太新的工具链编译老内核,结果一堆语法错误,排查了两天才发现是工具链的问题。

验证工具链是否可用:

aarch64-linux-gnu-gcc --version
# 输出类似:
# aarch64-linux-gnu-gcc (Linaro GCC 7.5-2019.12) 7.5.0

4.2 内核源码获取与编译

蓝牙驱动是内核的一部分,所以你得先拿到内核源码。我建议直接从kernel.org拉取主线版本,或者用你目标板厂商提供的分支。

# 拉取主线内核
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux

# 或者拉取稳定版本
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git

配置内核时,蓝牙相关的选项一定要打开。你想想看,如果内核里蓝牙模块都没编译,后面驱动写得再好也没用。

# 配置内核
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig

# 手动配置蓝牙相关选项
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig

在menuconfig中,需要确保以下选项开启:

配置项 路径 说明
CONFIG_BT Networking support → Bluetooth subsystem support 蓝牙核心支持
CONFIG_BT_HCIBTUSB Networking support → Bluetooth subsystem support → Bluetooth device drivers → HCI USB driver USB蓝牙适配器驱动
CONFIG_BT_HCIUART 同上 → HCI UART driver 串口蓝牙驱动
CONFIG_BT_HCIVHCI 同上 → Virtual HCI driver 虚拟HCI,QEMU模拟用

核心要点:CONFIG_BT_HCIVHCI这个选项很多人会漏掉。没有它,QEMU里模拟的蓝牙控制器就没法跟内核通信。

配置完开始编译:

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc)

# 编译内核模块
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules

# 安装模块到目标文件系统
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- modules_install INSTALL_MOD_PATH=/path/to/rootfs

编译时间取决于你的机器性能。我笔记本上大概要15-20分钟。嗯,这时候可以去泡杯咖啡。

4.3 QEMU模拟蓝牙控制器

没有硬件的时候怎么办?QEMU可以模拟蓝牙控制器。这招我在项目早期验证驱动框架时经常用。

首先,QEMU需要支持蓝牙模拟。从QEMU 5.0开始,内置了虚拟蓝牙控制器支持。

# 启动QEMU并添加虚拟蓝牙控制器
qemu-system-aarch64 \
  -machine virt \
  -cpu cortex-a57 \
  -kernel arch/arm64/boot/Image \
  -append "console=ttyAMA0 root=/dev/vda" \
  -drive file=rootfs.img,format=raw,if=virtio \
  -netdev user,id=net0 \
  -device virtio-net-device,netdev=net0 \
  -device virtio-blk-device,drive=rootfs \
  -chardev socket,id=bt,path=/tmp/bt-vhci \
  -device virtio-serial-device \
  -device virtconsole,chardev=bt

然后在QEMU内部,加载虚拟HCI驱动:

# 加载虚拟HCI驱动
modprobe hci_vhci

# 查看蓝牙设备
hciconfig -a
# 应该能看到 hci0 设备

小技巧:我习惯在宿主机上跑一个蓝牙协议栈(比如BlueZ的btvirt),然后通过socket跟QEMU里的虚拟控制器通信。这样可以在宿主机上模拟各种蓝牙设备的行为。

用QEMU模拟的好处是:

  • 不需要真实硬件,随时可以调试
  • 可以模拟异常情况(比如蓝牙断开、信号弱)
  • 配合GDB,可以单步调试驱动代码

4.4 硬件调试工具

模拟器终究是模拟器。真刀真枪干的时候,还是得上硬件。

4.4.1 BT Dongle的选择

市面上蓝牙适配器很多,但不是所有都适合驱动开发。我个人推荐这几款:

型号 芯片 特点
CSR 8510 CSR8510 A10 经典蓝牙,HCI命令支持完整
Broadcom BCM20702 BCM20702 蓝牙4.0,支持BLE
Intel 8265 Intel 8265 蓝牙5.0,M.2接口
避坑指南:我曾经买过一个杂牌蓝牙适配器,芯片是山寨的CSR。结果发HCI命令时,返回的数据包格式跟标准不一样,排查了整整三天。后来换了CSR原厂芯片的适配器,问题立刻解决。所以,别在工具上省钱。

4.4.2 逻辑分析仪的使用

调试蓝牙驱动时,逻辑分析仪是你的第三只眼。特别是调试UART接口的蓝牙模块时,没有它你根本不知道数据在线上是怎么跑的。

我常用的配置:

# Saleae Logic Analyzer 配置示例
采样率: 24MHz(对于蓝牙UART,115200波特率足够了)
通道: 
  CH0 - UART TX (蓝牙模块发送)
  CH1 - UART RX (蓝牙模块接收)
  CH2 - 蓝牙中断引脚
触发条件: 
  UART RX 下降沿触发

抓取到数据后,你可以:

  • 验证HCI命令是否按预期发送
  • 检查ACL数据包的时序
  • 分析蓝牙协议栈的响应延迟

个人经验:有一次我写的驱动在发送HCI命令后,蓝牙模块一直没响应。用逻辑分析仪一看,发现是UART的流控引脚没接对。硬件工程师把RTS/CTS接反了。这种问题,光看代码是永远找不到的。

4.4.3 其他调试工具

除了逻辑分析仪,还有几个工具我几乎天天用:

  • btmon:BlueZ自带的蓝牙监控工具,可以抓取HCI数据包
  • hcitool:发送HCI命令的瑞士军刀
  • btattach:将UART蓝牙设备挂载到BlueZ协议栈
  • usbmon:监控USB蓝牙适配器的数据流

举个例子,用btmon抓取蓝牙扫描过程:

# 启动btmon
btmon &

# 执行扫描
hcitool scan

# btmon输出示例
> HCI Event: Command Complete (0x0e) plen 4
    Inquiry (0x01|0x0001) ncmd 1
    Status: Success (0x00)
> HCI Event: Inquiry Result (0x02) plen 15
    Num responses: 1
    BD_ADDR: 11:22:33:44:55:66 (Unknown)
    Page scan repetition mode: R1 (0x01)
    ...

看到这些HCI事件,你就能清楚地知道蓝牙控制器内部发生了什么。说白了,这就是蓝牙驱动的"调试日志"。

小结

环境搭建这一步,说白了就是打好地基。工具链配好了,内核能编译了,QEMU能跑了,硬件工具到位了,后面写驱动的时候才能心不慌。

我建议你按照这个顺序一步步来:先配工具链,再编译内核,然后用QEMU验证,最后上硬件。别一上来就插USB蓝牙适配器,那样出了问题你都不知道是驱动的问题还是环境的问题。

下一章,我们就要开始真正写蓝牙驱动了。到时候这些工具都会派上用场。