yon*_*ran 1 linux l2cap bluetooth-lowenergy
我编写了一个应用程序,它在 Linux 上以非阻塞模式使用蓝牙 LE L2CAP 连接来读/写 ATT 数据包(使用socket(PF_BLUETOOTH, SOCK_SEQPACKET|SOCK_CLOEXEC, BTPROTO_L2CAP))。通常,当设备关闭或超出范围时,read()给出 errno=ETIMEDOUT。
但是,read()当蓝牙 LE 设备似乎仍在工作时,给出 errno=ETIMEDOUT 的频率比它应该的要高。超时的原因是什么?超时是否可配置?
我的 Linux 配置是 3.13.0-24-generic;蓝牙核心版本 2.17。
已建立的 LE L2CAP 套接字上的 ETIMEDOUT 错误实际上来自蓝牙适配器,在连续丢失几个数据包后。多少取决于连接参数。一旦主机发起与从机的连接,主机会在每个连接间隔(7.5ms–4s)对从机进行 ping 操作,如果从机听到 ping,则会响应。如果任一设备在监督超时(100 毫秒-32 秒)后未能收到另一台设备的回复,它将关闭连接。第三个参数,Slave Latency (0-499),允许从设备通过不响应某些 ping 来节省电池电量。另请参阅什么是连接参数?
启动连接时,操作系统会设置默认连接参数。从设备可能会推荐一组更合适的连接参数来平衡电池寿命、延迟和对障碍物/干扰的恢复能力,并且操作系统有机会批准这些参数(请参阅Apple 的蓝牙设计指南,了解 Apple 操作系统的连接参数范围会接受)。但是,如果从站不建议一组新的参数,那么它就受操作系统默认值的支配,操作系统的默认值因操作系统而异!
在查看 Wireshark 中的 hcidump btsnoop 文件时,似乎我的特定设备(蓝牙笔)从未建议不同的间隔和超时。因此,其可靠性将取决于操作系统的默认设置。
以下是通过实验确定的默认间隔和超时。
带有内部蓝牙适配器的 Linux 3.13(当前在
hci_core.c 中定义):操作系统允许的间隔:50-70ms
蓝牙适配器选择的连接间隔:67.5ms
监督超时:420ms(断开连接前6 个丢失的数据包)
带 iOS 7 的 iPhone 4S:
操作系统允许的间隔:未知
蓝牙适配器选择的连接间隔:30 毫秒
从延迟:0 包
监督超时:720 毫秒(断开连接前23 个丢失包)
Android 5.0.1 Lollipop on Nexus 4
操作系统允许的间隔:30-50ms
蓝牙适配器选择的连接间隔:48.75ms
从设备延迟:0 个数据包
监督超时:2000ms(断开连接前41 个丢失数据包)
OSX 10.10.1(Apple 蓝牙软件版本:4.3.1f2 15015)带外部适配器:
OS 允许的间隔:未知
蓝牙适配器选择的连接间隔:15ms
从设备延迟:0 数据包
监督超时:2000ms(断开连接前133 个丢失数据包)
这解释了为什么我与 Livescribe 笔的连接在 Linux 上显得不太可靠。内核默认是只丢6个包就断开连接,笔从来不推荐更好的参数。
在 Linux 3.17 及更高版本上,可以通过写入 /sys/kernel/debug/bluetooth/hci0/supervision_timeout 来调整监督超时。
测试方法:Linux 上的跟踪是使用 hcidump(在 Android 的开发人员选项中)获得的,并在 Wireshark 中分析了 HCI LE 创建连接命令。OSX 和 iPhone 的跟踪是使用 TI CC2540 和 TI 的数据包嗅探器软件获得的。