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驱动,配合btserverbtctl工具。我记得第一次调蓝牙电话功能时,发现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包开始,实现了IWifiIWifiStaIfaceIWifiApIface等接口。蓝牙HAL则是android.hardware.bluetooth,核心是IBluetoothHciIBluetoothSocket

我建议你重点关注两个文件:wifi_hal.cppbluetooth_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-pkttx_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

性能优化方面,我建议从三个维度入手:

  1. 吞吐量:Wi-Fi的TCP吞吐量要能达到200Mbps以上。检查iw dev wlan0 link的MCS索引,如果低于7,说明信号或驱动有问题。
  2. 延迟:蓝牙音频的往返延迟要小于100ms。用hcitool cmd发送Read RSSI命令,如果响应时间超过50ms,说明HCI传输层有瓶颈。
  3. 功耗:车载虽然不愁电,但Wi-Fi/蓝牙模块发热会影响稳定性。我习惯在驱动层加power_save模式,空闲时让芯片进入休眠。

个人经验:有一次客户抱怨热点速度慢,我查了半天发现是Wi-Fi的beacon_interval设成了100ms,导致客户端频繁唤醒。改成300ms后,吞吐量提升了30%。这种参数,文档里不会写,全靠实战积累。

车载Wi-Fi/蓝牙驱动开发知识体系 QNX协议栈 io-pkt + devnp-wlan devb-bluetooth + btserver wpa_supplicant / wlanctl Android HAL层 IWifi / IWifiStaIface IBluetoothHci / IBluetoothSocket Hostapd / BluetoothHeadset 车载功能 车载热点(AP模式) 蓝牙电话(HFP) 音频路由(SCO) 共存与性能优化 时分复用(PTA) 信道隔离(5GHz) 优先级管理 功耗/发热控制

最后说一句,Wi-Fi和蓝牙的共存问题,没有银弹。每个芯片方案(Broadcom、Qualcomm、Realtek)的PTA实现都不一样。我建议你在项目初期就拿到芯片的共存调试指南,然后花一周时间专门调参。别等到系统联调时才发现问题,那时候改驱动就晚了。

公众号:蓝海资料掘金营,微信deep3321