Notes on Bluetooth on Linux internals
Introduction
These are notes I made while trying to make my Sony WH-CH510 bluetooth headphones work properly with my Linux Mint 19 machine. It’s evident that an upgrade of the whole OS would have fixed the problem, but I have my opinion on upgrades.
The problem: It takes a long time for the headphones to connect (around 20-30 seconds), and once that happens, the headphones are not automatically chosen by Pulseaudio as the output device. There are hacky solutions to the second part of the problems, but I have this thing about wanting to solve a problem properly.
The twist is that there’s no problem at all with Sony’s WH-CH500 headphones, neither with a pair of junky earbuds, which are labeled Y30.
So after trying quite a few quick fixes, I decided to get to the bottom of the problem. The good news is that I found out the reason for the problem. The bad news is that I didn’t find any direct solution for it.
So my solution was to upgrade the bluetooth daemon, and I did it in a rather exotic way: I basically installed a Linux Mint 21.1 (“Victoria”) pretty much in the same way as explained in this post. Now bluetoothd version 5.64 runs instead of Linux Mint’s 19 version 5.48 of this daemon, and everything works like a charm. But it’s a rather tangled solution if you don’t have this parallel Linux distribution installed for other reasons.
The rest of this post is a collection of things I tried before upgrading the daemon.
Bluetooth is handled by the kernel, which presents an hci device (typically hci0). The heavy lifting of implementing the protocol is done by bluetoothd ( /usr/lib/bluetooth/bluetoothd on my machine, started by the bluetooth systemd service). Try, for example,
$ hciconfig
Sniffing
To capture bluetooth communication, there are two primary option: The bluetooth0 interface in Wireshark or the btmon command line utility. Wireshark is more comprehensive as always, however btmon is actually easier to work with, because all crucial information is concentrated in a text file. The eternal tradeoff between GUI and text. Possibly, use both.
I’m going to show excerpts from btmon dumps below, obtained with e.g.
$ sudo stdbuf -oL btmon | tee btmon.txt
This could have been just “sudo btmon”, but using stdbuf and tee, there’s also data printed out to console in real time (stdbuf removes unnecessary buffering).
I skip a lot of entries, because there are, well, a lot of them. Hopefully I didn’t throw away anything relevant.
With btmon, “>” means incoming to host, and “<” means outgoing from host (the device is at the left side, which is a bit odd).
I’ve never carried out a bluetooth-related project, so all my comments on btmon’s output below are no more than hopefully intelligent guesses. I don’t pretend to be acquainted with any of the related protocols.
The connection
This is WH-CH510′s connection request (the earbud’s request was exactly the same):
> HCI Event: Connect Request (0x04) plen 10 #11 [hci0] 10.725859 Address: 30:53:C1:11:40:2D (OUI 30-53-C1) Class: 0x240404 Major class: Audio/Video (headset, speaker, stereo, video, vcr) Minor class: Wearable Headset Device Rendering (Printing, Speaker) Audio (Speaker, Microphone, Headset) Link type: ACL (0x01) < HCI Command: Accept Connection R.. (0x01|0x0009) plen 7 #12 [hci0] 10.725914 Address: 30:53:C1:11:40:2D (OUI 30-53-C1) Role: Master (0x00)
And after a few packet exchanges, this appears in the dump output (three times, not clear why):
@ MGMT Event: Device Connected (0x000b) plen 28 {0x0001} [hci0] 11.256871 BR/EDR Address: 30:53:C1:11:40:2D (OUI 30-53-C1) Flags: 0x00000000 Data length: 15 Name (complete): WH-CH510 Class: 0x240404 Major class: Audio/Video (headset, speaker, stereo, video, vcr) Minor class: Wearable Headset Device Rendering (Printing, Speaker) Audio (Speaker, Microphone, Headset)
Cheap earbud’s service negotiation
Unlike USB, the device is in control: The requests information, and the host responds. The device chooses how to set up the connection, and the host follows suit. And it’s also the device that possibly messes up.
For the Y30 earbuds, the attribute request / response session went as follows.
First, the device checks if the host supports Handsfree Audio Gateway (0x111f), by asking for attributes, but the host doesn’t have any of it:
> ACL Data RX: Handle 17 flags 0x02 dlen 24 #42 [hci0] 14.317596
Channel: 64 len 20 [PSM 1 mode 0] {chan 0}
SDP: Service Search Attribute Request (0x06) tid 1 len 15
Search pattern: [len 6]
Sequence (6) with 3 bytes [16 extra bits] len 6
UUID (3) with 2 bytes [0 extra bits] len 3
Handsfree Audio Gateway (0x111f)
Max record count: 512
Attribute list: [len 6]
Sequence (6) with 3 bytes [16 extra bits] len 6
Unsigned Integer (1) with 2 bytes [0 extra bits] len 3
0x0004
Continuation state: 0
< ACL Data TX: Handle 17 flags 0x00 dlen 14 #43 [hci0] 14.317758
Channel: 64 len 10 [PSM 1 mode 0] {chan 0}
SDP: Service Search Attribute Response (0x07) tid 1 len 5
Attribute bytes: 2
Continuation state: 0
So the device tries AVDTP (0x0019) instead:
> ACL Data RX: Handle 17 flags 0x02 dlen 24 #45 [hci0] 14.322578 Channel: 64 len 20 [PSM 1 mode 0] {chan 0} SDP: Service Search Attribute Request (0x06) tid 2 len 15 Search pattern: [len 6] Sequence (6) with 3 bytes [16 extra bits] len 6 UUID (3) with 2 bytes [0 extra bits] len 3 AVDTP (0x0019) Max record count: 512 Attribute list: [len 6] Sequence (6) with 3 bytes [16 extra bits] len 6 Unsigned Integer (1) with 2 bytes [0 extra bits] len 3 0x0004 Continuation state: 0 < ACL Data TX: Handle 17 flags 0x00 dlen 60 #46 [hci0] 14.322737 Channel: 64 len 56 [PSM 1 mode 0] {chan 0} SDP: Service Search Attribute Response (0x07) tid 2 len 51 Attribute bytes: 48 Attribute list: [len 21] {position 0} Attribute: Protocol Descriptor List (0x0004) [len 2] Sequence (6) with 6 bytes [8 extra bits] len 8 UUID (3) with 2 bytes [0 extra bits] len 3 L2CAP (0x0100) Unsigned Integer (1) with 2 bytes [0 extra bits] len 3 0x0019 Sequence (6) with 6 bytes [8 extra bits] len 8 UUID (3) with 2 bytes [0 extra bits] len 3 AVDTP (0x0019) Unsigned Integer (1) with 2 bytes [0 extra bits] len 3 0x0103 Attribute list: [len 21] {position 1} Attribute: Protocol Descriptor List (0x0004) [len 2] Sequence (6) with 6 bytes [8 extra bits] len 8 UUID (3) with 2 bytes [0 extra bits] len 3 L2CAP (0x0100) Unsigned Integer (1) with 2 bytes [0 extra bits] len 3 0x0019 Sequence (6) with 6 bytes [8 extra bits] len 8 UUID (3) with 2 bytes [0 extra bits] len 3 AVDTP (0x0019) Unsigned Integer (1) with 2 bytes [0 extra bits] len 3 0x0103 Continuation state: 0
And yes, the host supports it.
AVDTP is (according to Wikipedia) used by the advanced audio distribution (A2DP) profile to stream music to stereo headsets over an L2CAP channel intended for video distribution profile in the Bluetooth transmission.
So it goes on with connecting channel 65 to service number 0x0019 (PSM stands for Protocol Service Multiplexor).
> ACL Data RX: Handle 17 flags 0x02 dlen 12 #51 [hci0] 14.358888 L2CAP: Connection Request (0x02) ident 3 len 4 PSM: 25 (0x0019) Source CID: 65 < ACL Data TX: Handle 17 flags 0x00 dlen 16 #52 [hci0] 14.358945 L2CAP: Connection Response (0x03) ident 3 len 8 Destination CID: 65 Source CID: 65 Result: Connection pending (0x0001) Status: Authorization pending (0x0002) < ACL Data TX: Handle 17 flags 0x00 dlen 16 #53 [hci0] 14.359202 L2CAP: Connection Response (0x03) ident 3 len 8 Destination CID: 65 Source CID: 65 Result: Connection successful (0x0000) Status: No further information available (0x0000) < ACL Data TX: Handle 17 flags 0x00 dlen 12 #54 [hci0] 14.359220 L2CAP: Configure Request (0x04) ident 3 len 4 Destination CID: 65 Flags: 0x0000 AVDTP (0x0019)> HCI Event: Number of Completed Packets (0x13) plen 5 #55 [hci0] 14.361709 Num handles: 1 Handle: 17 Count: 1
This is followed by a discovery phase:
@ MGMT Command: Start Discovery (0x0023) plen 1 {0x0001} [hci0] 14.362330 Address type: 0x07 BR/EDR LE Public LE Random
In the packet exchange that follows, the device obtains the capabilities of the host, and the audio connection is set up.
Sony WH-CH510′s service negotiation
The WH-CH510 takes another approach: It issues a Service Search Request for the Headset AG (0x1112). AG means Audio Gateway.
> ACL Data RX: Handle 20 flags 0x02 dlen 17 #48 [hci0] 11.736388 Channel: 64 len 13 [PSM 1 mode 0] {chan 0} SDP: Service Search Request (0x02) tid 1 len 8 Search pattern: [len 5] Sequence (6) with 3 bytes [8 extra bits] len 5 UUID (3) with 2 bytes [0 extra bits] len 3 Headset AG (0x1112) Max record count: 68 Continuation state: 0 < ACL Data TX: Handle 20 flags 0x00 dlen 18 #49 [hci0] 11.736611 Channel: 64 len 14 [PSM 1 mode 0] {chan 0} SDP: Service Search Response (0x03) tid 1 len 9 Total record count: 1 Current record count: 1 Record handle: 0x1000c Continuation state: 0
And yes, it’s supported and given the handle 0x1000c. Using this handle, the device asks for this service’s attributes:
> ACL Data RX: Handle 20 flags 0x02 dlen 23 #51 [hci0] 11.741392 Channel: 64 len 19 [PSM 1 mode 0] {chan 0} SDP: Service Attribute Request (0x04) tid 2 len 14 Record handle: 0x1000c Max attribute bytes: 277 Attribute list: [len 7] Sequence (6) with 5 bytes [8 extra bits] len 7 Unsigned Integer (1) with 4 bytes [0 extra bits] len 5 0x0000ffff Continuation state: 0 < ACL Data TX: Handle 20 flags 0x00 dlen 97 #52 [hci0] 11.741610 Channel: 64 len 93 [PSM 1 mode 0] {chan 0} SDP: Service Attribute Response (0x05) tid 2 len 88 Attribute bytes: 85 Attribute list: [len 83] {position 0} Attribute: Service Record Handle (0x0000) [len 2] 0x0001000c Attribute: Service Class ID List (0x0001) [len 2] UUID (3) with 2 bytes [0 extra bits] len 3 Headset AG (0x1112) UUID (3) with 2 bytes [0 extra bits] len 3 Generic Audio (0x1203) Attribute: Protocol Descriptor List (0x0004) [len 2] Sequence (6) with 3 bytes [8 extra bits] len 5 UUID (3) with 2 bytes [0 extra bits] len 3 L2CAP (0x0100) Sequence (6) with 5 bytes [8 extra bits] len 7 UUID (3) with 2 bytes [0 extra bits] len 3 RFCOMM (0x0003) Unsigned Integer (1) with 1 byte [0 extra bits] len 2 0x0c Attribute: Browse Group List (0x0005) [len 2] UUID (3) with 2 bytes [0 extra bits] len 3 Public Browse Root (0x1002) Attribute: Bluetooth Profile Descriptor List (0x0009) [len 2] Sequence (6) with 6 bytes [8 extra bits] len 8 UUID (3) with 2 bytes [0 extra bits] len 3 Headset (0x1108) Unsigned Integer (1) with 2 bytes [0 extra bits] len 3 0x0102 Attribute: Unknown (0x0100) [len 2] Headset Voice gateway [len 21] Continuation state: 0
The device chooses to connect channel 65 to the RFCOMM service:
> ACL Data RX: Handle 20 flags 0x02 dlen 12 #62 [hci0] 12.337691
L2CAP: Connection Request (0x02) ident 6 len 4
PSM: 3 (0x0003)
Source CID: 65
< ACL Data TX: Handle 20 flags 0x00 dlen 16 #63 [hci0] 12.337747
L2CAP: Connection Response (0x03) ident 6 len 8
Destination CID: 64
Source CID: 65
Result: Connection successful (0x0000)
Status: No further information available (0x0000)
After some configuration packets, the discovery begins:
@ MGMT Event: Discovering (0x0013) plen 2 {0x0001} [hci0] 14.686897
Address type: 0x07
BR/EDR
LE Public
LE Random
Discovery: Disabled (0x00)
> ACL Data RX: Handle 20 flags 0x02 dlen 18 #91 [hci0] 15.848944
Channel: 64 len 14 [PSM 3 mode 0] {chan 0}
RFCOMM: Unnumbered Info with Header Check (UIH) (0xef)
Address: 0x63 cr 1 dlci 0x18
Control: 0xef poll/final 0
Length: 10
FCS: 0x0e
41 54 2b 43 49 4e 44 3d 3f 0d 0e AT+CIND=?..
> ACL Data RX: Handle 20 flags 0x02 dlen 17 #92 [hci0] 18.849015
Channel: 64 len 13 [PSM 3 mode 0] {chan 0}
RFCOMM: Unnumbered Info with Header Check (UIH) (0xef)
Address: 0x63 cr 1 dlci 0x18
Control: 0xef poll/final 0
Length: 9
FCS: 0x0e
41 54 2b 43 49 4e 44 3f 0d 0e AT+CIND?..
This discovery isn’t all that successful: The device sends AT commands, but the host doesn’t respond to them. 4 seconds of futile attempts.
After some useless back and forth, the device tries again with the same Service Search Request, to which it receives the same answer, and so it goes on.
> ACL Data RX: Handle 20 flags 0x02 dlen 17 #108 [hci0] 20.717682
Channel: 65 len 13 [PSM 1 mode 0] {chan 1}
SDP: Service Search Request (0x02) tid 3 len 8
Search pattern: [len 5]
Sequence (6) with 3 bytes [8 extra bits] len 5
UUID (3) with 2 bytes [0 extra bits] len 3
Headset AG (0x1112)
Max record count: 68
Continuation state: 0
[ ... ]
The device disconnects the futile channel 65:
> ACL Data RX: Handle 20 flags 0x02 dlen 12 #114 [hci0] 20.818859 L2CAP: Disconnection Request (0x06) ident 10 len 4 Destination CID: 65 Source CID: 64 < ACL Data TX: Handle 20 flags 0x00 dlen 12 #115 [hci0] 20.818906 L2CAP: Disconnection Response (0x07) ident 10 len 4 Destination CID: 65 Source CID: 64
And then it tries another few AT commands on this same channel, despite having disconnected it. It didn’t reconnect it, so probably disconnection doesn’t mean what I think it does…?
> ACL Data RX: Handle 20 flags 0x02 dlen 24 #118 [hci0] 22.225305 Channel: 64 len 20 [PSM 3 mode 0] {chan 0} RFCOMM: Unnumbered Info with Header Check (UIH) (0xef) Address: 0x63 cr 1 dlci 0x18 Control: 0xef poll/final 0 Length: 16 FCS: 0x0e 41 54 2b 43 4d 45 52 3d 33 2c 30 2c 30 2c 31 0d AT+CMER=3,0,0,1. 0e . > ACL Data RX: Handle 20 flags 0x02 dlen 18 #119 [hci0] 25.285306 Channel: 64 len 14 [PSM 3 mode 0] {chan 0} RFCOMM: Unnumbered Info with Header Check (UIH) (0xef) Address: 0x63 cr 1 dlci 0x18 Control: 0xef poll/final 0 Length: 10 FCS: 0x0e 41 54 2b 43 43 57 41 3d 31 0d 0e AT+CCWA=1..
Needless to say, this was futile as well.
And then, out of the blue, the device requests to connect to AVDTP by choosing the service at address 0x0019.
> ACL Data RX: Handle 20 flags 0x02 dlen 12 #125 [hci0] 29.875346
L2CAP: Connection Request (0x02) ident 11 len 4
PSM: 25 (0x0019)
Source CID: 64
< ACL Data TX: Handle 20 flags 0x00 dlen 16 #126 [hci0] 29.875431
L2CAP: Connection Response (0x03) ident 11 len 8
Destination CID: 65
Source CID: 64
Result: Connection pending (0x0001)
Status: Authorization pending (0x0002)
< ACL Data TX: Handle 20 flags 0x00 dlen 16 #128 [hci0] 29.875677
L2CAP: Connection Response (0x03) ident 11 len 8
Destination CID: 65
Source CID: 64
Result: Connection successful (0x0000)
Status: No further information available (0x0000)
It’s not clear where the device got the idea to do this: As far as I can tell, the device wasn’t informed by the host about the existence of this service, at least not directly.
From this point, the negotiation goes well, and an audio device is set up. This allows selecting the headphones in the Sound Settings. Something that is done automatically with the old headphones and the junky earbuds.
So what is the problem? Maybe that the host doesn’t answer the AT commands. This is maybe solved in later versions of bluetoothd or Linux distributions in general. It’s quite possible that my distribution is too old for these newer headphones. Upgrade or perish.
Or go the other way: Downgrade. I suppose the solution would be to disable the Headset AG profile (0x1112), so that bluetoothd refuses to play ball when this is requested. This would speed up the fallback to A2DP, I hope, and possibly solve the problem.
I’ve tried hard to find a way to make bluetoothd refuse to the Headset AG profile, but in vain (so far?). sdptool has the option to disable a service, however it’s deprecated, and bluez 5 won’t talk with it. The updated method to tickle bluetoothd is through Dbus. Not sure if it has an API for turning off a service.
Unfortunately, I have no idea how to do this except for compiling bluetoothd from its sources and remove that option altogether. Actually, changing the UUID is enough to make it unusable.
I tried that, but it didn’t work all that well. More on that below.
Now to some extra stuff I randomly found out while working on this.
bluetootctl
This utility talks with bluetoothd through Dbus.
Doesn’t require root (when run from a terminal window on the computer’s desktop):
$ bluetoothctl [NEW] Controller 9C:BB:CC:DD:EE:FF compname [default] [NEW] Device 78:2E:D4:D9:62:C1 Y30 [NEW] Device 00:18:09:76:27:29 WH-CH500 [NEW] Device 30:53:C1:11:40:2D WH-CH510 Agent registered [bluetooth]# show Controller 9C:BB:CC:DD:EE:FF (public) Name: compname Alias: compname Class: 0x001c0104 Powered: yes Discoverable: yes Pairable: yes UUID: Headset AG (00001112-0000-1000-8000-00805f9b34fb) UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb) UUID: A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb) UUID: OBEX File Transfer (00001106-0000-1000-8000-00805f9b34fb) UUID: Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb) UUID: OBEX Object Push (00001105-0000-1000-8000-00805f9b34fb) UUID: PnP Information (00001200-0000-1000-8000-00805f9b34fb) UUID: IrMC Sync (00001104-0000-1000-8000-00805f9b34fb) UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb) UUID: Audio Source (0000110a-0000-1000-8000-00805f9b34fb) UUID: Audio Sink (0000110b-0000-1000-8000-00805f9b34fb) UUID: Vendor specific (00005005-0000-1000-8000-0002ee000001) UUID: Message Notification Se.. (00001133-0000-1000-8000-00805f9b34fb) UUID: Phonebook Access Server (0000112f-0000-1000-8000-00805f9b34fb) UUID: Message Access Server (00001132-0000-1000-8000-00805f9b34fb) UUID: Headset (00001108-0000-1000-8000-00805f9b34fb) Modalias: usb:v1D6Bp0246d0530 Discovering: no
This utility spits out a lot of information by itself when the daemon is restarted with e.g. “systemctl restart bluetooth”. There’s also output when a device is connected and disconnected.
An interesting feature of bluetootctl is the submenus, in particular “gatt” and “advertise”. Maybe the former allows deregistering UUIDs.
Try
[bluetooth]# menu gatt
and when done, go back to original menu:
[bluetooth]# back
btmgmt
btmgmt talks with the kernel directly through an AF_BLUETOOTH raw network socket. I considered this tool because it has the rm-uuid command, which is supposed to remove a UUID.
$ sudo btmgmt [mgmt]# rm-uuid 00001112-0000-1000-8000-00805f9b34fb Remove UUID succeeded. Class 0x1c0104
This is reverted when the bluetooth service is restarted. But it doesn’t seem to have any effect on the interface anyhow. The UUID keeps appearing in bluetoothctl’s “show” and the service is advertised and used. “clr-uuids” apparently removes all UUIDs, but this has no real effect.
It seems like the effective UUIDs are kept in bluetoothd. btmgmt changes the UUIDs in the kernel. See “Remove UUID Command” in mgmt-api.txt.
btmgmt also gets very active when bluetoothd is restarted and other events occur.
Talking with bluetoothd directly through DBus
See my notes on DBus on this post. Getting a property:
$ dbus-send --system --dest=org.bluez --print-reply /org/bluez/hci0 org.freedesktop.DBus.Properties.Get string:org.bluez.Adapter1 string:Address method return time=1685778877.065819 sender=:1.4 -> destination=:1.4934 serial=82 reply_serial=2 variant string "9C:BB:CC:DD:EE:FF"
Let’s break this down. Bluetoothd’ Dbus API is published in its source’s doc/ subdirectory. The Address property of the adapter is documented in adapter-api.txt. So:
- –print-reply: We want the reply to this query, of course.
- –system: Bluetoothd is connected to the system DBus.
- –dest=org.bluez: Taken from “Service” in adapter-api.txt.
- /org/bluez/hci0: Taken from “Object path” in adapter-api.txt.
- org.freedesktop.DBus.Properties.Get: Every dbus-send command needs a method. In this case fetching a property’s value. So use the generic method for fetching a property.
- string:org.bluez.Adapter1: Taken from “Interface” in adapter-api.txt.
- string:Address: This is the name of the property, as listed in adapter-api.txt.
Likewise, I can fetch the UUIDs:
$ dbus-send --print-reply --system --dest=org.bluez /org/bluez/hci0 org.freedesktop.DBus.Properties.Get string:org.bluez.Adapter1 string:UUIDs method return time=1685779625.693856 sender=:1.4 -> destination=:1.5017 serial=89 reply_serial=2 variant array [ string "00001112-0000-1000-8000-00805f9b34fb" string "00001801-0000-1000-8000-00805f9b34fb" string "0000110e-0000-1000-8000-00805f9b34fb" string "00001106-0000-1000-8000-00805f9b34fb" string "00001800-0000-1000-8000-00805f9b34fb" string "00001105-0000-1000-8000-00805f9b34fb" string "00001200-0000-1000-8000-00805f9b34fb" string "0000110c-0000-1000-8000-00805f9b34fb" string "00001104-0000-1000-8000-00805f9b34fb" string "0000110a-0000-1000-8000-00805f9b34fb" string "0000110b-0000-1000-8000-00805f9b34fb" string "00005005-0000-1000-8000-0002ee000001" string "00001133-0000-1000-8000-00805f9b34fb" string "0000112f-0000-1000-8000-00805f9b34fb" string "00001132-0000-1000-8000-00805f9b34fb" string "00001108-0000-1000-8000-00805f9b34fb" ]
So this is how bluetoothctl got these values. Unfortunately, this property is read-only according to adapter-api.txt, so it can’t be manipulated with a Set method.
It’s of course possible to run methods that are published in bluetoothd’s DBus API, but I didn’t find anything related to disabling services.
Hacking the source
Download the sources for bluez 5.48-0ubuntu3.1 as bluez_5.48.orig.tar.xz (which is the version running on Mint 19).
In lib/sdp.h, change 0x1112 to 0xeb12. Same in lib/uuid.h and in src/profile.c.
Then in the source’s root directory, go:
$ ./configure && echo Success
and then just
$ make && echo Success
On my machine, there was a need to install libical, to make configure work, i.e.
$ sudo apt-get install libical-dev
And then replace /usr/lib/bluetooth/bluetoothd with the compiled version in src/. Keep a copy of the old executable, of course.
That didn’t work at all. The old UUID kept appearing in bluetoothctl’s output for “show”. There was a change, however: The WH-CH510 headphones refused to connect to the host, and reverted to pairing. At least I did something, I thought. But as it turned out, these headphones refused to connect to the host even after going back to the original bluetooth daemon (but had no problem with my cellphone). Y30 had no problems, as usual.
Resetting the headphone by pressing the power button and “-” button for 7 seconds didn’t help either. What eventually did the trick was to remove /usr/lib/bluetooth/bluetoothd, go “systemctl restart bluetooth”, which failed of course. Then return bluetoothd to its place, which worked, as expected. And then everything was back to normal again.
Extra reading
- A Debian wiki page with several interesting examples (in particular how to use dbus-send to talk with bluetoothd)
- This page contains a lot of information about using command line
- The list of tools that come with the bluez package
- Some information about configuration files.
Random jots
- The bluetooth service is defined in /lib/systemd/system/bluetooth.service, which runs /usr/lib/bluetooth/bluetoothd.
- It’s also possible to run the daemon directly, by typing “/usr/lib/bluetooth/bluetoothd” at shell prompt. The daemon runs in the foreground in this case. Add the “-n” flag to get the logging information on the console as well.
- bluetoothd stores persistent information (the list and information about already discovered devices in particular) in /var/lib/bluetooth. This directory can be removed
It’s possible to stop the Bluetooth daemon with
# systemctl stop bluetooth
however clicking on desktop’s bluetooth applet restarts it automatically, unless it is running by other means (e.g. started manually with a command).
The “bluetoothctl” command won’t do this, but rather wait for a connection. It’s actually OK to turn off the bluetooth deamon while this utility is running.
After upgrading bluetoothd as mentioned above, I noticed a lot of log messages of this sort every 16 seconds or so:
kernel: Bluetooth: hci0: last event is not cmd complete (0x0f)
But looking at old logs, it’s evident that these existed in the past as well. So they’re not related to my manipulations with the Bluetooth daemon. In fact, these messages appear while the Cinnamon Bluetooth applet’s utility window is displayed. Close this window, and the messages stop. This is a kernel bug that was fixed in kernel v5.0.