16、Wi-Fi与蓝牙驱动开发:QNX Wi-Fi/蓝牙协议栈、Android Wi-Fi HAL与Bluetooth HAL、车载热点与蓝牙电话功能、共存与性能优化
说实话,车载无线通信这块,是我这些年踩坑最多的领域之一。Wi-Fi和蓝牙,看似是两个独立的功能,但在座舱里它们经常“打架”。你想想看,一边要开热点给后排乘客刷视频,一边又要连蓝牙电话,稍不注意就互相干扰。今天我就把这两套协议栈的底裤扒开,讲讲QNX和Android Automotive下到底怎么玩。
QNX下的Wi-Fi与蓝牙协议栈
QNX的无线协议栈,和Linux那套完全不同。它没有netdev、没有cfg80211,而是走自己的io-pkt + devnp-xxx驱动体系。我刚开始从Linux转过来时,差点被这个架构搞疯。
核心要点:QNX的Wi-Fi驱动本质是一个devnp-*共享库,加载到io-pkt进程空间里运行。蓝牙则走devb-*体系,通过HCI传输层与控制器通信。
具体来说,Wi-Fi协议栈分三层:
- MAC层:直接操作SDIO/USB控制器,处理帧收发。我习惯用
devnp-wlan.so配合wlan.conf配置。 - 协议栈层:io-pkt内部实现TCP/IP,Wi-Fi只提供链路层接口。
- 应用层:通过
wpa_supplicant或QNX自带的wlanctl管理连接。
蓝牙那边,QNX用devb-bluetooth驱动,配合btserver和btctl工具。我记得第一次调蓝牙电话功能时,发现HCI命令的响应超时设置太保守,导致免提协议栈频繁重试。后来把hci_timeout从5秒改成2秒,问题就解决了。
# QNX Wi-Fi驱动加载示例
io-pkt -d wlan -p tcpip
mount -T io-pkt /dev/wlan0
# 蓝牙驱动加载
devb-bluetooth -d sdio -p hci_transport=sdio
btserver -d /dev/bt0
避坑指南:我曾经在QNX 7.0上遇到Wi-Fi扫描时蓝牙断连的问题。后来发现是io-pkt的线程优先级设置不当。把wlan驱动的线程优先级从10降到5,蓝牙就稳了。记住:QNX的优先级调度非常敏感,别乱设。
Android Automotive的Wi-Fi HAL与Bluetooth HAL
Android这边,HAL层是核心。Wi-Fi HAL从android.hardware.wifi包开始,实现了IWifi、IWifiStaIface、IWifiApIface等接口。蓝牙HAL则是android.hardware.bluetooth,核心是IBluetoothHci和IBluetoothSocket。
我建议你重点关注两个文件:wifi_hal.cpp和bluetooth_hci.cc。前者负责把HAL调用转成内核的cfg80211命令,后者通过HCI socket与蓝牙芯片通信。
| HAL接口 | 对应功能 | 关键实现文件 |
|---|---|---|
| IWifiStaIface::startScan | Wi-Fi扫描 | wifi_hal.cpp |
| IWifiApIface::startHostapd | 开启热点 | hostapd.cpp |
| IBluetoothHci::sendHciCommand | 发送HCI命令 | bluetooth_hci.cc |
| IBluetoothSocket::connect | 蓝牙连接 | bt_socket.cc |
这里有个坑:Android 12之后,Wi-Fi HAL强制要求使用AIDL接口,不再支持HIDL。我手头一个项目从Android 11升级到12时,整个HAL层重写了一遍。你如果做新平台,直接上AIDL版本,别走弯路。
车载热点与蓝牙电话功能实现
车载热点,说白了就是让车机当路由器。Android这边用Hostapd,QNX那边用wlanctl启动AP模式。但车载环境有个特殊要求:热点必须在车辆启动后自动开启,且不能因为Wi-Fi扫描而中断。
我实现过一个方案:在init.rc里加一个service,监听sys.boot_completed属性,然后调用WifiManager.startLocalOnlyHotspot()。但要注意,Android 10之后,这个API需要SYSTEM_ALERT_WINDOW权限,否则会静默失败。
// Android热点启动核心代码
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
wifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {
@Override
public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
// 热点已开启,获取SSID和密码
WifiConfiguration config = reservation.getWifiConfiguration();
}
}, new Handler());
蓝牙电话功能,核心是HFP(Hands-Free Profile)协议栈。Android的BluetoothHeadset类封装了大部分逻辑,但车载场景需要处理多路音频路由。我遇到过一个问题:蓝牙电话接通后,声音从手机扬声器出来,而不是车机。后来发现是AudioManager.setBluetoothScoOn(true)调用时机不对,必须在onAudioStateChanged回调里设置。
注意:蓝牙电话的音频延迟必须控制在50ms以内,否则驾驶员会感觉“回声”。我建议在HCI层增加Voice Setting配置,强制使用CVSD编码,不要用mSBC,虽然mSBC音质好,但延迟高。
共存与性能优化
Wi-Fi和蓝牙共用2.4GHz频段,共存问题是个硬骨头。硬件上,很多芯片支持PTA(Packet Traffic Arbitration)机制,但软件配置不对,照样打架。
我总结了一套优化策略:
- 时分复用:给Wi-Fi和蓝牙分配固定的时间片。比如Wi-Fi占70%,蓝牙占30%。在QNX下,通过
io-pkt的tx_quantum参数控制。 - 信道隔离:如果Wi-Fi用5GHz,蓝牙用2.4GHz,基本不冲突。但车载热点通常只能用2.4GHz(因为5GHz穿透性差),这时就得靠PTA。
- 优先级管理:蓝牙电话的优先级必须高于Wi-Fi数据。我在Android的
wifi_hal.cpp里加了一个hook,当蓝牙HCI层检测到SCO连接时,主动降低Wi-Fi的TX功率。
// QNX下调整Wi-Fi/蓝牙共存参数
# 设置Wi-Fi TX时间片为70%
io-pkt -d wlan -o tx_quantum=70
# 蓝牙HCI层设置PTA模式
devb-bluetooth -d sdio -p pta_mode=1 -p sco_priority=high
性能优化方面,我建议从三个维度入手:
- 吞吐量:Wi-Fi的TCP吞吐量要能达到200Mbps以上。检查
iw dev wlan0 link的MCS索引,如果低于7,说明信号或驱动有问题。 - 延迟:蓝牙音频的往返延迟要小于100ms。用
hcitool cmd发送Read RSSI命令,如果响应时间超过50ms,说明HCI传输层有瓶颈。 - 功耗:车载虽然不愁电,但Wi-Fi/蓝牙模块发热会影响稳定性。我习惯在驱动层加
power_save模式,空闲时让芯片进入休眠。
个人经验:有一次客户抱怨热点速度慢,我查了半天发现是Wi-Fi的beacon_interval设成了100ms,导致客户端频繁唤醒。改成300ms后,吞吐量提升了30%。这种参数,文档里不会写,全靠实战积累。
最后说一句,Wi-Fi和蓝牙的共存问题,没有银弹。每个芯片方案(Broadcom、Qualcomm、Realtek)的PTA实现都不一样。我建议你在项目初期就拿到芯片的共存调试指南,然后花一周时间专门调参。别等到系统联调时才发现问题,那时候改驱动就晚了。