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 via MODULE_DEVICE_TABLE, matches any standard Bluetooth USB device by USB class. Used by udev to load the module.
  • quirks_table - internal secondary lookup in btusb_probe(). Maps specific VID:PID (Vendor ID:Product ID) pairs to driver flags such as BTUSB_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_id interface - the new_id sysfs interface allows adding custom USB IDs to a driver at runtime and could be automated via a udev rule. However, copying flags from an existing entry requires a reference in btusb_table - which contains no entries with BTUSB_REALTEK set. All Realtek entries live in quirks_table, which is not exposed via this interface. Not applicable here.

  • patch btusb.c - add the missing entry directly to quirks_table and build a patched out-of-tree module against the running kernel. Requires rebuilding on every kernel update. This is the approach used below.

Fix:#

  1. Download source and headers:

    Download btusb.c and its internal driver headers matching the running kernel — kernel-devel does not include these. A version mismatch causes symbol errors on insmod.

    $ 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"
      done
    
  2. Apply the patch:

    Save the following as ub600.patch in ~/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.c
    
  3. Build:

    ~/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
    
    $ make
    
  4. Load:

    $ sudo modprobe -r btusb
    $ sudo modprobe -a btrtl btintel btbcm btmtk
    $ sudo insmod ~/btusb-patched/btusb.ko
    
  5. Confirm:

    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 0xdfc6d922
    

    The RTL: loading... line confirms firmware is now being loaded. hci_ver=0a is the chip ROM version read before upload - expected, btrtl uses 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 firmwareWindows firmware
Sourcerealtek-firmware RPMUB600_V1_2.11.3032.3001 driver package
FormatRealtech v1RTBTCore v2
Size44,484 bytes29,215 bytes
BT version exposed5.1 (hci_ver=0x0a)6.0 (hci_ver=0x0e)
Chip fw version0xdfc6d9220x2afb4be5

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

  1. Windows firmware blob from TP-Link driver package UB600_V1_2.11.3032.3001 (Realtek driver 2.11.3032.3001, dated 2025-08-15), available from the TP-Link UB600 download page↩︎