TP-Link UB600 Bluetooth - fixing RTL8761BU firmware loading on Linux
Problem:#
The TP-Link UB600 Bluetooth 6.0 USB adapter is detected by the kernel but doesn’t function properly.
Note: Tested on Fedora Silverblue
44, kernel 7.0.11-200.fc44.x86_64. TP-Link does not officially support Linux for this adapter.
lsusb confirms the adapter is present:
$ lsusb | grep -i tp-link
Bus 002 Device 027: ID 37ad:0600 TP-Link Bluetooth USB Adapter
Watching kernel messages on plug-in shows that no firmware is loaded:
$ sudo journalctl -b -k -o cat -f
...
usb 2-2: new full-speed USB device number 49 using xhci_hcd
usb 2-2: New USB device found, idVendor=37ad, idProduct=0600, bcdDevice= 2.00
usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 2-2: Product: TP-Link Bluetooth USB Adapter
usb 2-2: Manufacturer:
usb 2-2: SerialNumber: ...
Bluetooth: MGMT ver 1.23
Scanning finds no devices:
$ bluetoothctl scan on
Discovery started
# no devices appear
Root cause:#
The chip used by this adapter is a Realtek RTL8761BU (BU = USB variant), identified via lmp_subver=0x8761 in the kernel log after applying the fix below. The chip operates in a limited ROM-only mode and requires a firmware blob to be uploaded from the host on every plug-in. Firmware upload is handled by the btrtl kernel module, but only when the btusb driver sets the BTUSB_REALTEK flag for the device.
btusb maintains two lookup tables in drivers/bluetooth/btusb.c
:
btusb_table- exported viaMODULE_DEVICE_TABLE, matches any standard Bluetooth USB device by USB class. Used byudevto load the module.quirks_table- internal secondary lookup inbtusb_probe(). Maps specificVID:PID(Vendor ID:Product ID) pairs to driver flags such asBTUSB_REALTEK.
The UB600 uses TP-Link’s vendor ID (0x37ad), which has no entry in quirks_table:
btusb_probe()
-> device matched via generic USB class entry (hci0 appears)
-> usb_match_id(intf, quirks_table) finds nothing for 0x37ad:0x0600
-> BTUSB_REALTEK (BIT(16)) never set
-> btrtl_setup_realtek() never called
-> chip runs on ROM firmware only
-> scan finds no devices
lsusb and bluetoothctl report from different layers: lsusb shows the USB descriptor VID set by TP-Link (0x37ad), while bluetoothctl show reports the HCI manufacturer code set by the chip ROM (Realtek, 0x005d). The chip exposes a Realtek manufacturer code at the HCI level, but btusb only checks the USB vendor ID:
$ bluetoothctl show
Controller XX:XX:XX:XX:XX:XX (public)
Manufacturer: 0x005d (93)
Version: 0x0a (10)
...
Note: Version: 0x0a (10) corresponds to Bluetooth 5.1 per the Bluetooth Assigned Numbers
specification - not the advertised 6.0. This is due to the outdated Linux firmware blob shipped in the realtek-firmware RPM, not the hardware:
$ rpm -q realtek-firmware
realtek-firmware-20260519-1.fc44.noarch
$ rpm -ql realtek-firmware | grep rtl8761bu
/usr/lib/firmware/rtl_bt/rtl8761bu_config.bin.xz
/usr/lib/firmware/rtl_bt/rtl8761bu_fw.bin.xz
See the Firmware upgrade section below.
The only clean permanent fix would be an upstream kernel patch. Two runtime options exist - one clean but not applicable here, one that works but requires maintaining a patched module.
sysfs
new_idinterface - thenew_idsysfs interface allows adding custom USB IDs to a driver at runtime and could be automated via audevrule. However, copying flags from an existing entry requires a reference inbtusb_table- which contains no entries withBTUSB_REALTEKset. All Realtek entries live inquirks_table, which is not exposed via this interface. Not applicable here.patch
btusb.c- add the missing entry directly toquirks_tableand build a patched out-of-tree module against the running kernel. Requires rebuilding on every kernel update. This is the approach used below.
Fix:#
Download source and headers:
Download
btusb.cand its internal driver headers matching the running kernel —kernel-develdoes not include these. A version mismatch causes symbol errors oninsmod.$ mkdir ~/btusb-patched && cd ~/btusb-patched $ VER="v$(uname -r | cut -d'-' -f1)" $ BASE='https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/plain/drivers/bluetooth' $ for f in btusb.c btintel.h btbcm.h btrtl.h btmtk.h; do curl -fsSL -o $f "$BASE/$f?h=$VER" doneApply the patch:
Save the following as
ub600.patchin~/btusb-patched/:--- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -601,6 +601,9 @@ /* Realtek 8922AE Bluetooth devices */ { USB_DEVICE(0x0bda, 0x8922), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, + /* TP-Link UB600 (RTL8761BU under TP-Link VID) */ + { USB_DEVICE(0x37ad, 0x0600), .driver_info = BTUSB_REALTEK | + BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3617), .driver_info = BTUSB_REALTEK | BTUSB_WIDEBAND_SPEECH }, { USB_DEVICE(0x13d3, 0x3616), .driver_info = BTUSB_REALTEK |$ patch btusb.c ub600.patch patching file btusb.cBuild:
~/btusb-patched/Makefile:obj-m := btusb.o KDIR := /lib/modules/$(shell uname -r)/build all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean$ makeLoad:
$ sudo modprobe -r btusb $ sudo modprobe -a btrtl btintel btbcm btmtk $ sudo insmod ~/btusb-patched/btusb.koConfirm:
Replug the adapter. The kernel log should now show the firmware loading line that was absent before:
$ sudo journalctl -b -k -o cat -f ... Bluetooth: hci0: RTL: examining hci_ver=0a hci_rev=000b lmp_ver=0a lmp_subver=8761 Bluetooth: hci0: RTL: rom_version status=0 version=1 Bluetooth: hci0: RTL: btrtl_initialize: key id 0 Bluetooth: hci0: RTL: loading rtl_bt/rtl8761bu_fw.bin Bluetooth: hci0: RTL: loading rtl_bt/rtl8761bu_config.bin Bluetooth: hci0: RTL: cfg_sz 6, total sz 30210 Bluetooth: hci0: RTL: fw version 0xdfc6d922The
RTL: loading...line confirms firmware is now being loaded.hci_ver=0ais the chip ROM version read before upload - expected,btrtluses it for identification only. The adapter scans and connects.This workaround requires rebuilding and reloading the module after every kernel update.
Firmware upgrade (Bluetooth 6.0):#
The realtek-firmware RPM ships an outdated rtl8761bu_fw.bin that only enables Bluetooth 5.1. The Windows driver package1 includes a newer blob that unlocks the chip’s full Bluetooth 6.0 capability.
| Linux firmware | Windows firmware | |
|---|---|---|
| Source | realtek-firmware RPM | UB600_V1_2.11.3032.3001 driver package |
| Format | Realtech v1 | RTBTCore v2 |
| Size | 44,484 bytes | 29,215 bytes |
| BT version exposed | 5.1 (hci_ver=0x0a) | 6.0 (hci_ver=0x0e) |
| Chip fw version | 0xdfc6d922 | 0x2afb4be5 |
btrtl supports both formats. Since /usr/lib/firmware/ is read-only on Fedora Silverblue, use the kernel’s firmware search path override
instead:
$ sudo mkdir -p /var/lib/firmware/rtl_bt
# xz-compress with CRC32 - the kernel's XZ decoder requires CRC32, not the default CRC64
$ xz --check=crc32 -k -c rtl8761b_mp_chip_bt40_fw_asic_rom_patch_new \
> /var/lib/firmware/rtl_bt/rtl8761bu_fw.bin.xz
$ echo -n /var/lib/firmware | sudo tee /sys/module/firmware_class/parameters/path
Unplug and replug the adapter. The kernel log should now show the new firmware version:
$ sudo journalctl -b -k -o cat -f
...
Bluetooth: hci0: RTL: examining hci_ver=0a hci_rev=000b lmp_ver=0a lmp_subver=8761
Bluetooth: hci0: RTL: rom_version status=0 version=1
Bluetooth: hci0: RTL: btrtl_initialize: key id 0
Bluetooth: hci0: RTL: loading rtl_bt/rtl8761bu_fw.bin
Bluetooth: hci0: RTL: loading rtl_bt/rtl8761bu_config.bin
Bluetooth: hci0: RTL: cfg_sz 6, total sz 29162
Bluetooth: hci0: RTL: fw version 0x2afb4be5
The version 14 field in btmgmt output confirms Bluetooth 6.0 is now active:
$ sudo btmgmt -i hci0 info | grep version
addr XX:XX:XX:XX:XX:XX version 14 manufacturer 93 class 0x7c0104
Windows firmware blob from TP-Link driver package
UB600_V1_2.11.3032.3001(Realtek driver2.11.3032.3001, dated 2025-08-15), available from the TP-Link UB600 download page . ↩︎