🌑

Using Blued on FreeBSD to Pair Bluetooth Devices

Posted at — Sep 06, 2022

Introduction

Following up on my post about how I pair my bluetooth headphones on FreeBSD, I decided to test drive the new blued project by Andreas Kempe on my Lenovo T480 laptop running FreeBSD 13.1.

I first saw mention of blued configuration daemon on the FreeBSD Quarterly Report and decided to take a look.

The blued build is straightforward. The ng_hci kernel driver needs to be patched, recompiled, and reloaded to handle several HCI commands that aren’t supported by the current driver. This is probably the hardest part of installing the blued software if you’re unfamiliar with compiling custom FreeBSD drivers / kernels.

I highly recommend using Andreas' build documentation to build blued since the software still quite new and the build process will likely change during development. Andreas explains how to properly build blued here.

The FreeBSD handbook also has useful information to help you understand how custom kernels are built here.

Config

After building and installing, you can either run the blued daemon as a service or by hand. If you run it as a service, you’ll have to remember to re-patch the ng_hci driver each time you update your kernel.

First copy the example config into /usr/local/etc/. The blued daemon will fail to start if the config file is missing. The defaults worked fine for me.

1
cp /usr/local/share/examples/blued/blued.conf.example /usr/local/etc/blued.conf

I chose to start the blued daemon with the service script.

1
2
sysrc blued_enable="YES"
service blued start

You can also run the daemon by hand. General messages are sent to /var/log/messages and debug output is sent to /var/log/debug.log.

Connecting and Using Bluetooth Devices

Once the blued daemon is running, it was easy to pair my headphones.

Put your device in pairing mode and scan for it:

1
bluecontrol scan
1
2c:41:a1:07:c9:c9

You can ask the name of the returned BDADDR to check if it’s your device:

1
bluecontrol request_name 2c:41:a1:07:c9:c9
1
2
Querying name from device...
Grafton's Headphones (2c:41:a1:07:c9:c9)

Now ask blued if it knows your device:

1
bluecontrol list_known
1
2c:41:a1:07:c9:c9 Grafton's Headphones

Finally attempt to pair the device:

1
bluecontrol pair 2c:41:a1:07:c9:c9
1
2
Pairing started
Device paired successfully

Success!

Virtual OSS

If you’re pairing an audio device like headphones, Virtual OSS is still required to create a dsp device which applications on the system use to output audio.

You can run it by hand but in my experience it also works well as a service.

By hand:

1
virtual_oss -T /dev/sndstat -S -a o,-4 -C 2 -c 2 -r 44100 -b 16 -s 1024 -R /dev/dsp0 -P /dev/bluetooth/headphones -d dsp -t vdsp.ctl

As a service, you’ll need to create a custom virtual_oss config and specify virtual_oss to use it in rc.conf. The following works well on my system:

1
2
3
virtual_oss_enable="YES"
virtual_oss_configs="blued"
virtual_oss_blued="-T /dev/sndstat -S -a o,-4 -C 2 -c 2 -r 44100 -b 16 -s 1024 -R /dev/dsp0 -P /dev/bluetooth/headphones -d dsp -t vdsp.ctl"

Once I started virtual_oss, my headphones told me they’re paired with the laptop and audio seems to function well.

I’d recommend using the blued service in conjunction with the virtual_oss service if you’re pairing headphones. The services appear to work well enough for me that I don’t have to think much about bluetooth anymore. I just start up my laptop, turn on my headphones, wait for them to connect, and start working. No more embarrassing script! :)

blued is definitely a step in the right direction for managing bluetooth devices on FreeBSD!

Notes

rc.conf configuration

1
2
3
4
virtual_oss_enable="YES"
virtual_oss_configs="blued"
virtual_oss_blued="-T /dev/sndstat -S -a o,-4 -C 2 -c 2 -r 44100 -b 16 -s 1024 -R /dev/dsp0 -P /dev/bluetooth/headphones -d dsp -t vdsp.ctl"
blued_enable="YES"

unix socket for rpc

Make sure your user has access /var/run/blued/rpc or you won’t be able to communicate with the blued daemon.

1
2
# ls /var/run/blued
srwxrwx---   1 root  wheel     0B Sep  1 13:54 rpc

Installed Files

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
-- Install configuration: "Debug"
-- Installing: /usr/local/lib/libblue.so.1.0
-- Set runtime path of "/usr/local/lib/libblue.so.1.0" to ""
-- Installing: /usr/local/lib/libblue.so
-- Installing: /usr/local/include/blued
-- Installing: /usr/local/include/blued/util
-- Installing: /usr/local/include/blued/util/unique_fd.h
-- Installing: /usr/local/include/blued/util/shared_cap_chan.h
-- Installing: /usr/local/include/blued/sdp_parser.h
-- Installing: /usr/local/include/blued/l2cap_socket.h
-- Installing: /usr/local/include/blued/hci_socket.h
-- Installing: /usr/local/include/blued/cap_kbd.h
-- Installing: /usr/local/include/blued/btdevice.h
-- Installing: /usr/local/include/blued/bdaddr.h
-- Installing: /usr/local/include/blued/hid
-- Installing: /usr/local/include/blued/hid/bthid.h
-- Installing: /usr/local/include/blued/hid/hid_device.h
-- Installing: /usr/local/include/blued/hid/kbd.h
-- Installing: /usr/local/include/blued/cap_l2cap.h
-- Installing: /usr/local/include/blued/cap_sdp.h
-- Installing: /usr/local/share/cmake/Modules/FindBlued.cmake
-- Installing: /usr/local/share/cmake/Modules/FindBlued-debug.cmake
-- Installing: /usr/local/sbin/blued
-- Set runtime path of "/usr/local/sbin/blued" to ""
-- Installing: /usr/local/share/man/man8/blued.8
-- Installing: /usr/local/share/examples/blued/blued.conf.example
-- Installing: /usr/local/etc/rc.d/blued
-- Installing: /usr/local/bin/bluecontrol
-- Set runtime path of "/usr/local/bin/bluecontrol" to ""
-- Installing: /usr/local/share/man/man8/bluecontrol.8