The problem: I am unable to receive (RX) bluetooth SCO data packets although a SCO link is present.
- Board: intel NUC (NUC6i7KYK) with integrated bluetooth adapter
- Network Chipset: Intel Wireless 8260 (8086:24F3)
- OS: Linux Kernel 4.8.8
- Bluez-5.45
- Homebrewed distro (every binary compiled from source at the farm... I like to know where the bits come from...)
- Remote bluetooth devices: 3 different models of headsets/speakers have been tested: Jabra ROX Wireless v2.9.0, JBL Charge 2, Sennheiser URBANITE: behavior is the same.
This is about BR/EDR audio over SCO. It is not bluetooth Low Energy (LE) related.
Computer is used as Audio Gateway (i.e. HFAG bluetooth profile), headsets have Headset and HandsFree profile registered
Bluetooth connection of the headset works. I know that because A2DP works like a charm: i.e. "Audio Sink" profile on the headset device and "Audio Source" profile on the computer connect together:
bluetoothd[1884]: src/service.c:change_state() 0x216b9d0: device AA:BB:CC:DD:EE:FF profile a2dp-sink state changed: disconnected -> connected (0)
bluetoothd[1884]: src/service.c:btd_service_ref() 0x216b9d0: ref=3
bluetoothd[1884]: plugins/policy.c:service_cb() Added a2dp-sink reconnect 0
bluetoothd[1884]: profiles/audio/sink.c:sink_set_state() State changed /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF: SINK_STATE_CONNECTING -> SINK_STATE_CONNECTED
bluetoothd[1884]: profiles/audio/transport.c:transport_update_playing() /org/bluez/hci0/dev_AA_BB_CC_DD_EE_FF/fd0 State=TRANSPORT_STATE_IDLE Playing=0
The problem arises when I want to get a SCO link: i.e. having my computer playing the Handsfree Audio Gateway profile (HFAG) role, and linking to the headset's Headset/Handsfree profile (HSP/HFP).
I first used Pulseaudio 10-0 along with sox 14.4.2 audioplayer (compiled with pulseaudio support):
- A2DP: works fine: audio goes through both ways (sox play and sox rec work).
- SCO: setting pulseaudio's bluez card profile to headset_head_unit and adjusting default source/sink to bluez HFP/HSP: No audio goes through: sox and pulseaudio block as soon as I start playing/recording
SCO connection is there: I deduced that from the outputs of:
hcitool con:
Connections:
< eSCO AA:BB:CC:DD:EE:FF handle 257 state 1 lm MASTER
> ACL AA:BB:CC:DD:EE:FF handle 256 state 1 lm MASTER AUTH ENCRYPT
cat /sys/kernel/debug/bluetooth/sco:
gg:hh:ii:jj:kk:ll aa:bb:cc:dd:ee:ff 1
btmon (or hcidump):
...
< HCI Command: Setup Synchronous.. (0x01|0x0028) plen 17 #578 [hci0] 12.108972
...
> HCI Event: Synchronous Connect Complete (0x2c) plen 17 #593 [hci0] 12.261784
Status: Success (0x00)
Handle: 257
Address: AA:BB:CC:DD:EE:FF (OUI 1C-48-F9)
Link type: eSCO (0x02)
Transmission interval: 0x0c
Retransmission window: 0x02
RX packet length: 60
TX packet length: 60
Air mode: CVSD (0x02)
Going through btmon output, I see that even though SCO packets are transmitted (TX), none are received (RX):
...
< SCO Data TX: Handle 257 flags 0x00 dlen 60 #811 [hci0] 82.683653
< SCO Data TX: Handle 257 flags 0x00 dlen 60 #812 [hci0] 83.683862
< SCO Data TX: Handle 257 flags 0x00 dlen 60 #813 [hci0] 84.684030
...
hciconfig hci0 -a shows the TX sco packet count incrementing but RX sco count desperately stays at 0:
# hciconfig -a
hci0: Type: Primary Bus: USB
BD Address: AA:BB:CC:DD:EE:FF ACL MTU: 1021:4 SCO MTU: 60:8
UP RUNNING PSCAN
RX bytes:21578 acl:271 sco:0 events:2728 errors:0
TX bytes:597283 acl:260 sco:358 commands:2446 errors:0
So I then tried without Pulseaudio, to try to narrow down to the problem. I used bluez's package scotest tool (in the package's tools/ directory):
Reading the Hands-Free profile specs: first a RFCOMM link has to be created and a 'Service Level Connexion' ('SLC': negociation between AG and HF/HS device for audio codec etc...) must be run. So I manually registered a HFAG profile on the computer using sdptool (sdptool HFAG add) and created a RFCOMM link manually using socat and a chat script so I could have total control on the SLC sequence (and control AT commands: +BRSF, +CIEV: indicators...)
- At this point: The device connects, a RFCOMM link is created and is stable, and I can tweak the SLC sequence around
I now can come back to the initial problem: having a working SCO connection with SCO packets going both ways, using scotest:
bluez-5.45/tools/scotest -d -V 0x0060 <BD_ADDRESS_REMOTE>
I tried every command line options ('mode') of scotest: -d: dump, -s: send, -r: receive, -n: connect
Getting into scotest's code I could figure out the connect() call worked.
sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
bind(sk, (struct sockaddr *) &addr, sizeof(addr);
connect(sk, (struct sockaddr *) &addr, sizeof(addr);
These lines of code create an SCO connection (hcitool con, kernel debugfs, btmon, report the same as above).
But at this point: same symptoms: I can send packets (now I can choose how much and when I send, etc...), but none received.
EDIT: I tried on another computer: Intel XPS13 with a Qualcomm Ahteros Killer QCA6174 network card and the behavior is as expected on this chipset: as soon as an SCO connection is made scotest -n will connect and a whole bunch of these lines will flood btmon's output:
> SCO Data RX: Handle 9 flags 0x00 dlen 48 #819 [hci0] 16.686260
> SCO Data RX: Handle 9 flags 0x00 dlen 48 #820 [hci0] 16.696251
> SCO Data RX: Handle 9 flags 0x00 dlen 48 #821 [hci0] 16.696258
> SCO Data RX: Handle 9 flags 0x00 dlen 48 #822 [hci0] 16.696259
> SCO Data RX: Handle 9 flags 0x00 dlen 48 #823 [hci0] 16.706253
> SCO Data RX: Handle 9 flags 0x00 dlen 48 #824 [hci0] 16.706259
> SCO Data RX: Handle 9 flags 0x00 dlen 48 #825 [hci0] 16.706260
> SCO Data RX: Handle 9 flags 0x00 dlen 48 #826 [hci0] 16.706261
> SCO Data RX: Handle 9 flags 0x00 dlen 48 #827 [hci0] 16.716256
> SCO Data RX: Handle 9 flags 0x00 dlen 48 #828 [hci0] 16.716262
My hypothesis at this point is that it's an issue that has to do with SCO audio routing for the chipset, as mentioned here: https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Bluetooth/ EDIT
So I tried several things:
- On the socket's file descriptor returned by connect() in scotest's code: replacing scotest's read() with recv() or recvmsg(), setting socket flag MSG_DONTWAIT: the call either waits infinitely (using read(), recv(), recvmsg()) or exits immediately (MSG_DONTWAIT flag on).
- Fork()ing to read() and write() on the socket's file descriptor at the same time (duplexing).
- Doing the connect() call at several time points during the SLC (beginning, after / indicators are modified, making pauses (using sleep() calls): Once the device is connected (RFCOMM launched), the leds on the headsets react to the existence of the SCO connection the same way they do with a phone (tipically: red when no connection, blue when SCO connection is on).
- Figured out why the setsockopt(sk, SOL_BLUETOOTH, BT_VOICE, &opts, sizeof(opts)) call in scotest's dump_mode() returns an error "Invalid argument": because in the kernel the sk->sk_state is BT_CONNECTED since do_connect(). But that error does not affect the setting which is done earlier in do_connect if using the "-V 0x0060" command line parameter.
- Modify the MTU size (hciconfig hci0 scomtu 60:8): It doesn't affect the related HCI command parameters (Setup Synchronous Connection ogf:0x01 | ocf:0x0028) at SCO connexion, but I use it to match with the packet size obtained from the Setup synchronous connexion response I see in btmon/hcidump (i.e. > HCI Event: Synchronous Connect Complete 0x2c: RX/TX packet length: 60)
- Modify packet types, which I could only do by modifying the kernel: struct esco_param_cvsd[] in net/bluetooth/hci_conn.c. After kernel debugging I found out the commands "hciconfig 0 " or " hcitool cpt " can not affect the SCO connection setup hci command parameters. (I use a vanilla Kernel)
- Use btsco (from this post), but I didn't give it a 'hard try': the docs do not mention kernel versions above 3 and the most recent tarball available are from 2006
From what I understand I tend to believe SCO Data packets should be incoming quite automatically. So I'm really wondering where I went wrong.