I have written an application that uses Bluetooth LE L2CAP connections in nonblocking mode on Linux to read/write ATT packets (using socket(PF_BLUETOOTH, SOCK_SEQPACKET|SOCK_CLOEXEC, BTPROTO_L2CAP)
). Normally, when the device turns off or goes out of range, read()
gives errno=ETIMEDOUT.
However, read()
is giving errno=ETIMEDOUT more often than it should, when the Bluetooth LE device still appears to be working. What is the cause of the timeout? Is the timeout configurable?
My Linux configuration is 3.13.0-24-generic; Bluetooth Core ver 2.17.
The ETIMEDOUT errors on an established LE L2CAP socket actually come from the Bluetooth adapter after several consecutive missed packets. How many depends on the Connection Parameters. Once a master initiates a connection with a slave, the master pings the slave every Connection Interval (7.5ms–4s), and the slave will respond if it heard the ping. If either device fails to hear back from the other device after Supervision Timeout (100ms–32s), it will close the connection. A third parameter, the Slave Latency (0-499), allows the slave to save battery power by not responding to some pings. See also What are connection parameters?
The OS sets the default connection parameters when initiating a connection. The slave may recommend a more appropriate set of connection parameters to balance battery life, latency, and resilience to obstructions/interference, and the OS gets a chance to approve these parameters (see Apple’s Bluetooth Design Guidelines for the range of connection parameters that Apple’s OSs will accept). But if the slave does not suggest a new set of parameters, then it is at the mercy of the operating system’s defaults, which vary wildly from one OS to another!
In looking at the hcidump btsnoop files in Wireshark, it appears that my particular device (a bluetooth pen) never suggests a different interval and timeout. Therefore, its reliability will depend on the OS defaults.
Here are experimentally determined default intervals and timeouts.
Linux 3.13 with internal bluetooth adapter (currently defined in hci_core.c):
Intervals allowed by OS: 50–70ms
Connection Interval chosen by bluetooth adapter: 67.5ms
Supervision timeout: 420ms (6 missing packets before disconnect)
iPhone 4S with iOS 7:
Intervals allowed by OS: unknown
Connection Interval chosen by bluetooth adapter: 30ms
Slave latency: 0 packets
Supervision timeout: 720ms (23 missing packets before disconnect)
Android 5.0.1 Lollipop on Nexus 4
Intervals allowed by OS: 30–50ms
Connection Interval chosen by bluetooth adapter: 48.75ms
Slave latency: 0 packets
Supervision timeout: 2000ms (41 missing packets before disconnect)
OSX 10.10.1 (Apple Bluetooth Software Version: 4.3.1f2 15015) with external adapter:
Intervals allowed by OS: unknown
Connection Interval chosen by bluetooth adapter: 15ms
Slave latency: 0 packets
Supervision timeout: 2000ms (133 missing packets before disconnect)
This explains why my connections to my Livescribe pen appeared less reliable on Linux. The kernel default is to drop the connection after only 6 missing packets, and the pen never recommended better parameters.
On Linux 3.17 and above, the supervision timeout can be tweaked by writing to /sys/kernel/debug/bluetooth/hci0/supervision_timeout.
Testing methodology: the traces on Linux were obtained using hcidump (in Developer Options in Android) and were analyzed in Wireshark for HCI LE Create Connection commands. The traces for OSX and iPhone were obtained using the TI CC2540 with TI’s packet sniffer software.