4、HCI层调试:hcitool、hciconfig、hcidump工具实战
HCI层,说白了就是主机和蓝牙控制器之间的“翻译官”。
你想想看,上层协议栈要发数据,底层芯片要收数据,中间全靠HCI命令、事件和数据包来沟通。我做了这么多年蓝牙驱动,最头疼的问题十有八九都出在这一层——不是命令发错了,就是事件没响应。
所以,掌握HCI层的调试工具,是每个蓝牙开发者的基本功。今天我就带你把 hcitool、hciconfig、hcidump 这三个家伙彻底搞明白。
4.1 hciconfig:先看看你的蓝牙长啥样
我个人习惯,拿到一块新的蓝牙模组,第一件事就是跑 hciconfig。
它就像Linux下的 ifconfig,专门用来查看和配置蓝牙设备的状态。
# 查看所有蓝牙设备
hciconfig -a
# 输出示例:
hci0: Type: Primary Bus: USB
BD Address: 00:1A:7D:DA:71:13 ACL MTU: 1021:8 SCO MTU: 64:1
UP RUNNING
RX bytes:12345 acl:100 sco:0 events:500 errors:0
TX bytes:67890 acl:200 sco:0 commands:300 errors:0
这里有几个关键信息你要盯紧了:
- UP RUNNING:设备正常启动并运行。如果显示DOWN,说明蓝牙没打开。
- BD Address:蓝牙地址,每个设备唯一。我遇到过两次地址全是0的情况,那基本是固件没烧好。
- ACL MTU:最大传输单元。这个值太小会影响吞吐量,我调音频传输时经常要改它。
- errors:错误计数。如果这个数字一直涨,赶紧查硬件连接。
hciconfig hci0 up 启动设备,hciconfig hci0 down 关闭设备。调试时经常需要重启蓝牙,我习惯写个脚本一键搞定。
4.2 hcitool:发命令、扫设备、查信息
hcitool 是我用得最多的工具。它可以直接向蓝牙控制器发送HCI命令,也能扫描周围的设备。
4.2.1 扫描周围设备
# 扫描蓝牙设备
hcitool scan
# 输出示例:
Scanning ...
00:11:22:33:44:55 MyPhone
AA:BB:CC:DD:EE:FF Speaker
嗯,这里要注意:hcitool scan 只扫描经典蓝牙(BR/EDR)。如果你想扫低功耗蓝牙(BLE),得用 hcitool lescan。
我曾经在一个项目里,怎么都扫不到某个BLE设备。折腾了半天,才发现自己一直在用 scan 而不是 lescan。这种低级错误,犯一次就记住了。
4.2.2 查看连接信息
# 查看当前连接
hcitool con
# 输出示例:
Connections:
< ACL 00:11:22:33:44:55 handle 12 state 1 lm MASTER
handle 是连接句柄,后面调试数据流的时候会频繁用到。state 1 表示已连接,如果是0就是正在连接中。
4.2.3 发送原始HCI命令
这是最硬核的部分。有时候工具不提供某个功能,你就得自己拼命令。
# 发送HCI命令:读取本地版本信息
# 命令格式:hcitool cmd <ogf> <ocf> [参数]
# OGF=0x04(控制与基带命令组),OCF=0x0001(读取本地版本信息)
hcitool cmd 0x04 0x0001
# 输出示例:
< HCI Command: ogf 0x04, ocf 0x0001, plen 0
> HCI Event: 0x0e plen 12
01 01 04 00 01 08 00 00 00 00 00 00
返回的数据怎么解析?我一般对照蓝牙核心规范(Core Spec)来查。上面这个例子,第4个字节 01 表示HCI版本号,第5个字节 08 表示LMP版本号。
4.3 hcidump:抓包分析,真相只有一个
hcidump 是HCI层的抓包工具。它能捕获主机和控制器之间所有的HCI数据包,包括命令、事件、ACL数据和SCO数据。
说白了,它就是蓝牙驱动的“Wireshark”。
4.3.1 基本用法
# 抓取所有HCI数据包
hcidump -i hci0
# 输出示例:
> HCI Event: Connect Complete (0x03) plen 11
status 0x00 handle 12 bdaddr 00:11:22:33:44:55 link_type ACL encrypt_mode 0x00
看到 status 0x00 就放心了,表示连接成功。如果返回非零值,比如 0x04(硬件故障),那就要检查硬件了。
4.3.2 保存到文件并分析
# 保存为文本格式
hcidump -i hci0 -w dump.txt
# 保存为二进制格式(可以用Wireshark打开)
hcidump -i hci0 -w dump.pklg
我个人习惯先用 -w 保存成二进制文件,然后用Wireshark打开分析。Wireshark的图形界面比命令行友好太多了,还能自动解析各种协议。
4.3.3 过滤特定类型的数据包
# 只抓取ACL数据
hcidump -i hci0 -t acl
# 只抓取SCO数据(音频)
hcidump -i hci0 -t sco
# 只抓取命令和事件
hcidump -i hci0 -t cmd
hcidump -i hci0 -t event
调试音频传输时,我经常用 -t sco 来过滤。有一次客户反馈通话有杂音,我抓了SCO数据一看,发现每隔几个包就丢一个。最后定位到是USB传输的延迟太大,换了个USB口就解决了。
4.4 实战案例:排查蓝牙连接失败
光说不练假把式。我拿一个真实案例来演示这三个工具的配合使用。
问题现象: 手机扫描不到某个蓝牙设备。
排查步骤:
-
先用 hciconfig 检查设备状态
hciconfig -a # 发现 hci0 是 DOWN 状态嗯,问题找到了。设备都没启动,怎么可能扫得到?
-
启动设备,再次扫描
hciconfig hci0 up hcitool scan # 还是扫不到!这就奇怪了。设备启动了,但就是扫不到目标。
-
用 hcidump 抓包看看
hcidump -i hci0 -t cmd # 发现扫描命令已经发出去了 # 但没有任何 Inquiry Result 事件返回这说明蓝牙控制器确实在执行扫描,但周围没有设备响应。要么是目标设备没开广播,要么是距离太远。
-
检查目标设备
我拿着手机靠近开发板,再次扫描——成功了!原来是距离问题。客户把设备放在10米外的金属柜子里,信号被屏蔽了。
核心思路: 从设备状态 → 命令发送 → 事件响应,一步步缩小范围。HCI层的调试,本质上就是在验证“命令是否发出”和“事件是否返回”这两个关键点。
4.5 避坑指南
最后,分享几个我踩过的坑:
- 权限问题: 运行这些工具通常需要root权限。如果提示权限不足,记得加
sudo。 - 多设备冲突: 如果系统里有多个蓝牙适配器(比如内置蓝牙+USB蓝牙),用
-i hci0指定设备,别搞混了。 - hcidump 卡死: 有时候抓包会卡住不动,按
Ctrl+C退出就行。如果还不行,killall hcidump强制结束。 - 日志太多: 生产环境不要一直开着hcidump,日志文件会暴涨。我见过有人把嵌入式设备的Flash写爆了。
好了,HCI层的三个调试工具就讲到这里。下一章我们会深入ACL数据流,看看蓝牙数据到底是怎么在主机和控制器之间穿梭的。