(The summary was written by GPT, for archiving the debug process)
I connected a Bluetooth speaker on Arch Linux and saw several profile choices in the desktop Bluetooth/audio UI:
Audio GatewayHeadset Head Unit (CVSD)Headset Head Unit (mSBC)The observed behavior was:
CVSD worked, but the sound quality was terrible.mSBC worked and sounded better.Audio Gateway did not work.This was confusing, because for a Bluetooth speaker, the high-quality playback path should normally be the music profile, not the headset/call profile.
There are two different kinds of Bluetooth audio paths involved here.
This is the telephony-style path.
Properties:
Two codecs showed up under this path:
So it made sense that:
This is the normal playback path for music.
Properties:
So in principle, the goal was not “pick the best headset codec”, but rather “make the actual music profile work”.
pactl list cardsThe important command output was:
Card #85
Name: bluez_card.F4_4E_FD_8E_A4_04
device.description = "BOGASING-M5"
Profiles:
off: Off (sinks: 0, sources: 0, ...)
audio-gateway: Audio Gateway (A2DP Source & HSP/HFP AG) (sinks: 0, sources: 0, ...)
headset-head-unit-cvsd: Headset Head Unit (HSP/HFP, codec CVSD) (sinks: 1, sources: 1, ...)
headset-head-unit: Headset Head Unit (HSP/HFP, codec MSBC) (sinks: 1, sources: 1, ...)
Active Profile: audio-gateway
The crucial part was:
audio-gateway ... (sinks: 0, sources: 0)
That meant the supposedly “better” profile was not actually creating any usable playback or capture node.
So although audio-gateway was selected, it was effectively useless in the current state.
By contrast, the HFP/HSP profiles did create working nodes:
sinks: 1, sources: 1sinks: 1, sources: 1This immediately explained why CVSD and mSBC worked while audio-gateway did not.
wpctl statusThe status output showed that the Bluetooth device existed, but there was no Bluetooth output sink:
Audio
├─ Devices:
│ 71. BOGASING-M5 [bluez5]
│
├─ Sinks:
│ * 61. Built-in Audio Analog Stereo [vol: 0.54]
│
├─ Sources:
│ 62. Built-in Audio Analog Stereo [vol: 0.24]
│
├─ Filters:
│ - loopback-2548-18
│ * 73. bluez_input.F4:4E:FD:8E:A4:04 [Audio/Source]
The important thing was:
bluez_input... sourcebluez_output... sinkSo PipeWire had not created the normal playback endpoint for the Bluetooth speaker.
That meant the music transport had failed to come up.
To determine whether the speaker itself was at fault, the following command was used:
bluetoothctl info F4:4E:FD:8E:A4:04The output included:
UUID: Audio Source
UUID: Audio Sink
UUID: Handsfree
This was important.
A speaker advertising Audio Sink means it is capable of receiving normal Bluetooth music playback.
So the device was not limited to headset mode. The music path should have been possible.
The journal contained the crucial error lines:
bluetoothd[1106]: src/service.c:btd_service_connect() a2dp-sink profile connect failed for F4:4E:FD:8E:A4:04: Device or resource busy
bluetoothd[1106]: profiles/audio/avctp.c:avctp_control_confirm() Control: Refusing unexpected connect
This suggested that the A2DP music connection was failing during negotiation.
So the picture became:
The profile name audio-gateway is easy to misunderstand.
For a speaker that advertises Audio Sink, the laptop must act as the A2DP Source when sending music to the speaker.
So in WirePlumber’s terminology:
This is why the relevant role on the Linux machine is a2dp_source, even though the goal is “play music to the speaker”.
The likely issue was not that the speaker lacked A2DP.
The more plausible explanation was that the Bluetooth stack was trying to juggle:
and that this role negotiation was getting stuck or conflicting, leading to the Device or resource busy failure.
So the strategy was:
Force the stack to use only the music role, and disable the headset/call roles for this device.
A WirePlumber configuration override was created.
File:
~/.config/wireplumber/wireplumber.conf.d/51-bluez-a2dp.conf
Contents:
monitor.bluez.properties = {
bluez5.roles = [ a2dp_source ]
}This tells WirePlumber to use only the A2DP source role for Bluetooth devices.
In effect, this disables the HFP/HSP headset roles and keeps only the music path.
After creating the config file, restart the user audio services:
systemctl --user restart wireplumber pipewire pipewire-pulseThen reconnect the Bluetooth device.
A full reconnect sequence can be done with:
bluetoothctl
disconnect F4:4E:FD:8E:A4:04
trust F4:4E:FD:8E:A4:04
connect F4:4E:FD:8E:A4:04The trust step is not necessarily the core fix, but it is sensible for stable reconnect behavior.
After forcing only a2dp_source, the speaker worked correctly.
So the effective fix was:
In short:
The fix was not “make CVSD better” or “switch to mSBC”. The real fix was to bypass the headset path entirely and force the correct A2DP music role.
Then:
pactl list cards shows:audio-gateway ... (sinks: 0, sources: 0)
and wpctl status shows no bluez_output... sink, then the music path is not really up.
UUID: Audio Sink
then it should be capable of normal Bluetooth music playback, and the issue is likely in negotiation/configuration rather than the hardware simply lacking support.
pactl list cardsLook for:
wpctl statusLook for:
bluez_output... sinkbluez_input... sourcebluetoothctl info <MAC>Look for UUIDs such as:
Audio SinkAudio SourceHandsfreejournalctl -b | grep -Ei 'bluetooth|bluez|a2dp'Look for negotiation or connection failures.
monitor.bluez.properties = {
bluez5.roles = [ a2dp_source ]
}and restart WirePlumber/PipeWire.
The issue was a Bluetooth speaker on Arch Linux where:
The debugging process showed that:
Audio Sink)Device or resource busyThe fix was to force WirePlumber to use only the music role:
monitor.bluez.properties = {
bluez5.roles = [ a2dp_source ]
}After restarting the audio services and reconnecting the device, the speaker worked normally.
pactl list cards
wpctl status
bluetoothctl info F4:4E:FD:8E:A4:04
journalctl -b | grep -Ei 'bluetooth|bluez|a2dp|mSBC|CVSD'Config file:
~/.config/wireplumber/wireplumber.conf.d/51-bluez-a2dp.conf
monitor.bluez.properties = {
bluez5.roles = [ a2dp_source ]
}Restart:
systemctl --user restart wireplumber pipewire pipewire-pulse