diff options
author | Vladimir Kondratyev <wulf@FreeBSD.org> | 2024-11-06 23:28:49 +0000 |
---|---|---|
committer | Vladimir Kondratyev <wulf@FreeBSD.org> | 2024-11-06 23:30:29 +0000 |
commit | 5036d9652a5701d00e9e40ea942c278e9f77d33d (patch) | |
tree | c94e3cf7a140561373701ab98d1cb7e49a04b63d | |
parent | 24ae172a50352ad4fd22989477f29ecca5aed6e3 (diff) | |
download | src-5036d9652a57.tar.gz src-5036d9652a57.zip |
rtlbtfw: Firmware loader for Realtek 87XX/88XX bluetooth USB adaptors
Firmware files are available in the comms/rtlbt-firmware port.
Sponsored by: Future Crew LLC
MFC after: 1 month
Differential Revision: https://reviews.freebsd.org/D46739
-rw-r--r-- | targets/pseudo/userland/Makefile.depend | 1 | ||||
-rw-r--r-- | tools/build/mk/OptionalObsoleteFiles.inc | 3 | ||||
-rw-r--r-- | usr.sbin/bluetooth/Makefile | 1 | ||||
-rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/Makefile | 9 | ||||
-rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/main.c | 525 | ||||
-rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbt_dbg.h | 46 | ||||
-rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c | 385 | ||||
-rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h | 92 | ||||
-rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c | 236 | ||||
-rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h | 104 | ||||
-rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbtfw.8 | 100 | ||||
-rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf | 373 |
12 files changed, 1875 insertions, 0 deletions
diff --git a/targets/pseudo/userland/Makefile.depend b/targets/pseudo/userland/Makefile.depend index 6a844630c999..698db40042ef 100644 --- a/targets/pseudo/userland/Makefile.depend +++ b/targets/pseudo/userland/Makefile.depend @@ -430,6 +430,7 @@ DIRDEPS+= \ usr.sbin/bluetooth/l2control \ usr.sbin/bluetooth/l2ping \ usr.sbin/bluetooth/rfcomm_pppd \ + usr.sbin/bluetooth/rtlbtfw \ usr.sbin/bluetooth/sdpcontrol \ usr.sbin/bluetooth/sdpd \ usr.sbin/bootparamd/bootparamd \ diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc index da2f0c15d11e..c03611c976a6 100644 --- a/tools/build/mk/OptionalObsoleteFiles.inc +++ b/tools/build/mk/OptionalObsoleteFiles.inc @@ -193,6 +193,7 @@ OLD_FILES+=etc/bluetooth/hosts OLD_FILES+=etc/bluetooth/protocols OLD_FILES+=etc/defaults/bluetooth.device.conf OLD_FILES+=etc/devd/iwmbtfw.conf +OLD_FILES+=etc/devd/rtlbtfw.conf OLD_DIRS+=etc/bluetooth OLD_FILES+=etc/rc.d/bluetooth OLD_FILES+=etc/rc.d/bthidd @@ -240,6 +241,7 @@ OLD_FILES+=usr/sbin/iwmbtfw OLD_FILES+=usr/sbin/l2control OLD_FILES+=usr/sbin/l2ping OLD_FILES+=usr/sbin/rfcomm_pppd +OLD_FILES+=usr/sbin/rtlbtfw OLD_FILES+=usr/sbin/sdpcontrol OLD_FILES+=usr/sbin/sdpd OLD_FILES+=usr/share/examples/etc/defaults/bluetooth.device.conf @@ -318,6 +320,7 @@ OLD_FILES+=usr/share/man/man8/iwmbtfw.8.gz OLD_FILES+=usr/share/man/man8/l2control.8.gz OLD_FILES+=usr/share/man/man8/l2ping.8.gz OLD_FILES+=usr/share/man/man8/rfcomm_pppd.8.gz +OLD_FILES+=usr/share/man/man8/rtlbtfw.8.gz OLD_FILES+=usr/share/man/man8/sdpcontrol.8.gz OLD_FILES+=usr/share/man/man8/sdpd.8.gz .endif diff --git a/usr.sbin/bluetooth/Makefile b/usr.sbin/bluetooth/Makefile index fb660029b3d1..5afc12450194 100644 --- a/usr.sbin/bluetooth/Makefile +++ b/usr.sbin/bluetooth/Makefile @@ -19,6 +19,7 @@ SUBDIR+= bcmfw SUBDIR+= bthidcontrol SUBDIR+= bthidd SUBDIR+= iwmbtfw +SUBDIR+= rtlbtfw .endif .include <bsd.subdir.mk> diff --git a/usr.sbin/bluetooth/rtlbtfw/Makefile b/usr.sbin/bluetooth/rtlbtfw/Makefile new file mode 100644 index 000000000000..f9c5dfd12b1f --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/Makefile @@ -0,0 +1,9 @@ +PACKAGE= bluetooth +CONFS= rtlbtfw.conf +CONFSDIR= /etc/devd +PROG= rtlbtfw +MAN= rtlbtfw.8 +LIBADD+= usb +SRCS= main.c rtlbt_fw.c rtlbt_hw.c + +.include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/rtlbtfw/main.c b/usr.sbin/bluetooth/rtlbtfw/main.c new file mode 100644 index 000000000000..029c04f98b26 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/main.c @@ -0,0 +1,525 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org> + * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> + * Copyright (c) 2023 Future Crew LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/endian.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <libusb.h> + +#include "rtlbt_fw.h" +#include "rtlbt_hw.h" +#include "rtlbt_dbg.h" + +#define _DEFAULT_RTLBT_FIRMWARE_PATH "/usr/share/firmware/rtlbt" + +int rtlbt_do_debug = 0; +int rtlbt_do_info = 0; + +struct rtlbt_devid { + uint16_t product_id; + uint16_t vendor_id; +}; + +static struct rtlbt_devid rtlbt_list[] = { + /* Realtek 8821CE Bluetooth devices */ + { .vendor_id = 0x13d3, .product_id = 0x3529 }, + + /* Realtek 8822CE Bluetooth devices */ + { .vendor_id = 0x0bda, .product_id = 0xb00c }, + { .vendor_id = 0x0bda, .product_id = 0xc822 }, + + /* Realtek 8822CU Bluetooth devices */ + { .vendor_id = 0x13d3, .product_id = 0x3549 }, + + /* Realtek 8852AE Bluetooth devices */ + { .vendor_id = 0x0bda, .product_id = 0x2852 }, + { .vendor_id = 0x0bda, .product_id = 0xc852 }, + { .vendor_id = 0x0bda, .product_id = 0x385a }, + { .vendor_id = 0x0bda, .product_id = 0x4852 }, + { .vendor_id = 0x04c5, .product_id = 0x165c }, + { .vendor_id = 0x04ca, .product_id = 0x4006 }, + { .vendor_id = 0x0cb8, .product_id = 0xc549 }, + +#ifdef RTLBTFW_SUPPORTS_FW_V2 + /* Realtek 8852CE Bluetooth devices */ + { .vendor_id = 0x04ca, .product_id = 0x4007 }, + { .vendor_id = 0x04c5, .product_id = 0x1675 }, + { .vendor_id = 0x0cb8, .product_id = 0xc558 }, + { .vendor_id = 0x13d3, .product_id = 0x3587 }, + { .vendor_id = 0x13d3, .product_id = 0x3586 }, + { .vendor_id = 0x13d3, .product_id = 0x3592 }, +#endif + + /* Realtek 8852BE Bluetooth devices */ + { .vendor_id = 0x0cb8, .product_id = 0xc559 }, + { .vendor_id = 0x0bda, .product_id = 0x887b }, + { .vendor_id = 0x13d3, .product_id = 0x3571 }, + + /* Realtek 8723AE Bluetooth devices */ + { .vendor_id = 0x0930, .product_id = 0x021d }, + { .vendor_id = 0x13d3, .product_id = 0x3394 }, + + /* Realtek 8723BE Bluetooth devices */ + { .vendor_id = 0x0489, .product_id = 0xe085 }, + { .vendor_id = 0x0489, .product_id = 0xe08b }, + { .vendor_id = 0x04f2, .product_id = 0xb49f }, + { .vendor_id = 0x13d3, .product_id = 0x3410 }, + { .vendor_id = 0x13d3, .product_id = 0x3416 }, + { .vendor_id = 0x13d3, .product_id = 0x3459 }, + { .vendor_id = 0x13d3, .product_id = 0x3494 }, + + /* Realtek 8723BU Bluetooth devices */ + { .vendor_id = 0x7392, .product_id = 0xa611 }, + + /* Realtek 8723DE Bluetooth devices */ + { .vendor_id = 0x0bda, .product_id = 0xb009 }, + { .vendor_id = 0x2ff8, .product_id = 0xb011 }, + + /* Realtek 8761BUV Bluetooth devices */ + { .vendor_id = 0x2357, .product_id = 0x0604 }, + { .vendor_id = 0x0b05, .product_id = 0x190e }, + { .vendor_id = 0x2550, .product_id = 0x8761 }, + { .vendor_id = 0x0bda, .product_id = 0x8771 }, + { .vendor_id = 0x6655, .product_id = 0x8771 }, + { .vendor_id = 0x7392, .product_id = 0xc611 }, + { .vendor_id = 0x2b89, .product_id = 0x8761 }, + + /* Realtek 8821AE Bluetooth devices */ + { .vendor_id = 0x0b05, .product_id = 0x17dc }, + { .vendor_id = 0x13d3, .product_id = 0x3414 }, + { .vendor_id = 0x13d3, .product_id = 0x3458 }, + { .vendor_id = 0x13d3, .product_id = 0x3461 }, + { .vendor_id = 0x13d3, .product_id = 0x3462 }, + + /* Realtek 8822BE Bluetooth devices */ + { .vendor_id = 0x13d3, .product_id = 0x3526 }, + { .vendor_id = 0x0b05, .product_id = 0x185c }, + + /* Realtek 8822CE Bluetooth devices */ + { .vendor_id = 0x04ca, .product_id = 0x4005 }, + { .vendor_id = 0x04c5, .product_id = 0x161f }, + { .vendor_id = 0x0b05, .product_id = 0x18ef }, + { .vendor_id = 0x13d3, .product_id = 0x3548 }, + { .vendor_id = 0x13d3, .product_id = 0x3549 }, + { .vendor_id = 0x13d3, .product_id = 0x3553 }, + { .vendor_id = 0x13d3, .product_id = 0x3555 }, + { .vendor_id = 0x2ff8, .product_id = 0x3051 }, + { .vendor_id = 0x1358, .product_id = 0xc123 }, + { .vendor_id = 0x0bda, .product_id = 0xc123 }, + { .vendor_id = 0x0cb5, .product_id = 0xc547 }, +}; + +static int +rtlbt_is_realtek(struct libusb_device_descriptor *d) +{ + int i; + + /* Search looking for whether it's a Realtek-based device */ + for (i = 0; i < (int) nitems(rtlbt_list); i++) { + if ((rtlbt_list[i].product_id == d->idProduct) && + (rtlbt_list[i].vendor_id == d->idVendor)) { + rtlbt_info("found USB Realtek"); + return (1); + } + } + + /* Not found */ + return (0); +} + +static int +rtlbt_is_bluetooth(struct libusb_device *dev) +{ + struct libusb_config_descriptor *cfg; + const struct libusb_interface *ifc; + const struct libusb_interface_descriptor *d; + int r; + + r = libusb_get_active_config_descriptor(dev, &cfg); + if (r < 0) { + rtlbt_err("Cannot retrieve config descriptor: %s", + libusb_error_name(r)); + return (0); + } + + if (cfg->bNumInterfaces != 0) { + /* Only 0-th HCI/ACL interface is supported by downloader */ + ifc = &cfg->interface[0]; + if (ifc->num_altsetting != 0) { + /* BT HCI/ACL interface has no altsettings */ + d = &ifc->altsetting[0]; + /* Check if interface is a bluetooth */ + if (d->bInterfaceClass == LIBUSB_CLASS_WIRELESS && + d->bInterfaceSubClass == 0x01 && + d->bInterfaceProtocol == 0x01) { + rtlbt_info("found USB Realtek"); + libusb_free_config_descriptor(cfg); + return (1); + } + } + } + libusb_free_config_descriptor(cfg); + + /* Not found */ + return (0); +} + +static libusb_device * +rtlbt_find_device(libusb_context *ctx, int bus_id, int dev_id) +{ + libusb_device **list, *dev = NULL, *found = NULL; + struct libusb_device_descriptor d; + ssize_t cnt, i; + int r; + + cnt = libusb_get_device_list(ctx, &list); + if (cnt < 0) { + rtlbt_err("libusb_get_device_list() failed: code %lld", + (long long int) cnt); + return (NULL); + } + + /* + * Scan through USB device list. + */ + for (i = 0; i < cnt; i++) { + dev = list[i]; + if (bus_id == libusb_get_bus_number(dev) && + dev_id == libusb_get_device_address(dev)) { + /* Get the device descriptor for this device entry */ + r = libusb_get_device_descriptor(dev, &d); + if (r != 0) { + rtlbt_err("libusb_get_device_descriptor: %s", + libusb_strerror(r)); + break; + } + + /* For non-Realtek match on the vendor/product id */ + if (rtlbt_is_realtek(&d)) { + /* + * Take a reference so it's not freed later on. + */ + found = libusb_ref_device(dev); + break; + } + /* For Realtek vendor match on the interface class */ + if (d.idVendor == 0x0bda && rtlbt_is_bluetooth(dev)) { + /* + * Take a reference so it's not freed later on. + */ + found = libusb_ref_device(dev); + break; + } + } + } + + libusb_free_device_list(list, 1); + return (found); +} + +static void +rtlbt_dump_version(ng_hci_read_local_ver_rp *ver) +{ + rtlbt_info("hci_version 0x%02x", ver->hci_version); + rtlbt_info("hci_revision 0x%04x", le16toh(ver->hci_revision)); + rtlbt_info("lmp_version 0x%02x", ver->lmp_version); + rtlbt_info("lmp_subversion 0x%04x", le16toh(ver->lmp_subversion)); +} + +/* + * Parse ugen name and extract device's bus and address + */ + +static int +parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr) +{ + char *ep; + + if (strncmp(ugen, "ugen", 4) != 0) + return (-1); + + *bus = (uint8_t) strtoul(ugen + 4, &ep, 10); + if (*ep != '.') + return (-1); + + *addr = (uint8_t) strtoul(ep + 1, &ep, 10); + if (*ep != '\0') + return (-1); + + return (0); +} + +static void +usage(void) +{ + fprintf(stderr, + "Usage: rtlbtfw (-D) -d ugenX.Y (-f firmware path) (-I)\n"); + fprintf(stderr, " -D: enable debugging\n"); + fprintf(stderr, " -d: device to operate upon\n"); + fprintf(stderr, " -f: firmware path, if not default\n"); + fprintf(stderr, " -I: enable informational output\n"); + exit(127); +} + +int +main(int argc, char *argv[]) +{ + libusb_context *ctx = NULL; + libusb_device *dev = NULL; + libusb_device_handle *hdl = NULL; + ng_hci_read_local_ver_rp ver; + int r; + uint8_t bus_id = 0, dev_id = 0; + int devid_set = 0; + int n; + char *firmware_dir = NULL; + char *firmware_path = NULL; + char *config_path = NULL; + int retcode = 1; + const struct rtlbt_id_table *ic; + uint8_t rom_version; + struct rtlbt_firmware fw, cfg; + enum rtlbt_fw_type fw_type; + uint16_t fw_lmp_subversion; + + /* Parse command line arguments */ + while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) { + switch (n) { + case 'd': /* ugen device name */ + devid_set = 1; + if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0) + usage(); + break; + case 'D': + rtlbt_do_debug = 1; + break; + case 'f': /* firmware dir */ + if (firmware_dir) + free(firmware_dir); + firmware_dir = strdup(optarg); + break; + case 'I': + rtlbt_do_info = 1; + break; + case 'h': + default: + usage(); + break; + /* NOT REACHED */ + } + } + + /* Ensure the devid was given! */ + if (devid_set == 0) { + usage(); + /* NOTREACHED */ + } + + /* libusb setup */ + r = libusb_init(&ctx); + if (r != 0) { + rtlbt_err("libusb_init failed: code %d", r); + exit(127); + } + + rtlbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id); + + /* Find a device based on the bus/dev id */ + dev = rtlbt_find_device(ctx, bus_id, dev_id); + if (dev == NULL) { + rtlbt_err("device not found"); + goto shutdown; + } + + /* XXX enforce that bInterfaceNumber is 0 */ + + /* XXX enforce the device/product id if they're non-zero */ + + /* Grab device handle */ + r = libusb_open(dev, &hdl); + if (r != 0) { + rtlbt_err("libusb_open() failed: code %d", r); + goto shutdown; + } + + /* Check if ng_ubt is attached */ + r = libusb_kernel_driver_active(hdl, 0); + if (r < 0) { + rtlbt_err("libusb_kernel_driver_active() failed: code %d", r); + goto shutdown; + } + if (r > 0) { + rtlbt_info("Firmware has already been downloaded"); + retcode = 0; + goto shutdown; + } + + /* Get local version */ + r = rtlbt_read_local_ver(hdl, &ver); + if (r < 0) { + rtlbt_err("rtlbt_read_local_ver() failed code %d", r); + goto shutdown; + } + rtlbt_dump_version(&ver); + + ic = rtlbt_get_ic(ver.lmp_subversion, ver.hci_revision, + ver.hci_version); + if (ic == NULL) { + rtlbt_err("rtlbt_get_ic() failed: Unknown IC"); + goto shutdown; + } + + /* Default the firmware path */ + if (firmware_dir == NULL) + firmware_dir = strdup(_DEFAULT_RTLBT_FIRMWARE_PATH); + + firmware_path = rtlbt_get_fwname(ic->fw_name, firmware_dir, "_fw.bin"); + if (firmware_path == NULL) + goto shutdown; + + rtlbt_debug("firmware_path = %s", firmware_path); + + rtlbt_info("loading firmware %s", firmware_path); + + /* Read in the firmware */ + if (rtlbt_fw_read(&fw, firmware_path) <= 0) { + rtlbt_debug("rtlbt_fw_read() failed"); + return (-1); + } + + fw_type = rtlbt_get_fw_type(&fw, &fw_lmp_subversion); + if (fw_type == RTLBT_FW_TYPE_UNKNOWN && + (ic->flags & RTLBT_IC_FLAG_SIMPLE) == 0) { + rtlbt_debug("Unknown firmware type"); + goto shutdown; + } + + if (fw_type != RTLBT_FW_TYPE_UNKNOWN) { + + /* Match hardware and firmware lmp_subversion */ + if (fw_lmp_subversion != ver.lmp_subversion) { + rtlbt_err("firmware is for %x but this is a %x", + fw_lmp_subversion, ver.lmp_subversion); + goto shutdown; + } + + /* Query a ROM version */ + r = rtlbt_read_rom_ver(hdl, &rom_version); + if (r < 0) { + rtlbt_err("rtlbt_read_rom_ver() failed code %d", r); + goto shutdown; + } + rtlbt_debug("rom_version = %d", rom_version); + + /* Load in the firmware */ + r = rtlbt_parse_fwfile_v1(&fw, rom_version); + if (r < 0) { + rtlbt_err("Parseing firmware file failed"); + goto shutdown; + } + + config_path = rtlbt_get_fwname(ic->fw_name, firmware_dir, + "_config.bin"); + if (config_path == NULL) + goto shutdown; + + rtlbt_info("loading config %s", config_path); + + /* Read in the config file */ + if (rtlbt_fw_read(&cfg, config_path) <= 0) { + rtlbt_err("rtlbt_fw_read() failed"); + if ((ic->flags & RTLBT_IC_FLAG_CONFIG) != 0) + goto shutdown; + } else { + r = rtlbt_append_fwfile(&fw, &cfg); + rtlbt_fw_free(&cfg); + if (r < 0) { + rtlbt_err("Appending config file failed"); + goto shutdown; + } + } + } + + r = rtlbt_load_fwfile(hdl, &fw); + if (r < 0) { + rtlbt_debug("Loading firmware file failed"); + goto shutdown; + } + + /* free it */ + rtlbt_fw_free(&fw); + + rtlbt_info("Firmware download complete"); + + /* Execute Read Local Version one more time */ + r = rtlbt_read_local_ver(hdl, &ver); + if (r < 0) { + rtlbt_err("rtlbt_read_local_ver() failed code %d", r); + goto shutdown; + } + rtlbt_dump_version(&ver); + + retcode = 0; + + /* Ask kernel driver to probe and attach device again */ + r = libusb_reset_device(hdl); + if (r != 0) + rtlbt_err("libusb_reset_device() failed: %s", + libusb_strerror(r)); + +shutdown: + + /* Shutdown */ + + if (hdl != NULL) + libusb_close(hdl); + + if (dev != NULL) + libusb_unref_device(dev); + + if (ctx != NULL) + libusb_exit(ctx); + + if (retcode == 0) + rtlbt_info("Firmware download is successful!"); + else + rtlbt_err("Firmware download failed!"); + + return (retcode); +} diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbt_dbg.h b/usr.sbin/bluetooth/rtlbtfw/rtlbt_dbg.h new file mode 100644 index 000000000000..54c982119d40 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_dbg.h @@ -0,0 +1,46 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org> + * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __RTLBT_DEBUG_H__ +#define __RTLBT_DEBUG_H__ + +extern int rtlbt_do_debug; +extern int rtlbt_do_info; + +#define rtlbt_err(fmt, ...) \ + fprintf(stderr, "rtlbtfw: %s: "fmt"\n", __func__, ##__VA_ARGS__) +#define rtlbt_info(fmt, ...) do { \ + if (rtlbt_do_info) \ + fprintf(stderr, "%s: "fmt"\n", __func__, ##__VA_ARGS__);\ +} while (0) +#define rtlbt_debug(fmt, ...) do { \ + if (rtlbt_do_debug) \ + fprintf(stderr, "%s: "fmt"\n", __func__, ##__VA_ARGS__);\ +} while (0) + +#endif diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c new file mode 100644 index 000000000000..bb3d20d79527 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c @@ -0,0 +1,385 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org> + * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> + * Copyright (c) 2023 Future Crew LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "rtlbt_fw.h" +#include "rtlbt_dbg.h" + +static const struct rtlbt_id_table rtlbt_ic_id_table[] = { + { /* 8723A */ + .lmp_subversion = RTLBT_ROM_LMP_8723A, + .hci_revision = 0xb, + .hci_version = 0x6, + .flags = RTLBT_IC_FLAG_SIMPLE, + .fw_name = "rtl8723a", + }, { /* 8723B */ + .lmp_subversion = RTLBT_ROM_LMP_8723B, + .hci_revision = 0xb, + .hci_version = 0x6, + .fw_name = "rtl8723b", + }, { /* 8723D */ + .lmp_subversion = RTLBT_ROM_LMP_8723B, + .hci_revision = 0xd, + .hci_version = 0x8, + .flags = RTLBT_IC_FLAG_CONFIG, + .fw_name = "rtl8723d", + }, { /* 8821A */ + .lmp_subversion = RTLBT_ROM_LMP_8821A, + .hci_revision = 0xa, + .hci_version = 0x6, + .fw_name = "rtl8821a", + }, { /* 8821C */ + .lmp_subversion = RTLBT_ROM_LMP_8821A, + .hci_revision = 0xc, + .hci_version = 0x8, + .flags = RTLBT_IC_FLAG_MSFT, + .fw_name = "rtl8821c", + }, { /* 8761A */ + .lmp_subversion = RTLBT_ROM_LMP_8761A, + .hci_revision = 0xa, + .hci_version = 0x6, + .fw_name = "rtl8761a", + }, { /* 8761BU */ + .lmp_subversion = RTLBT_ROM_LMP_8761A, + .hci_revision = 0xb, + .hci_version = 0xa, + .fw_name = "rtl8761bu", + }, { /* 8822C with USB interface */ + .lmp_subversion = RTLBT_ROM_LMP_8822B, + .hci_revision = 0xc, + .hci_version = 0xa, + .flags = RTLBT_IC_FLAG_MSFT, + .fw_name = "rtl8822cu", + }, { /* 8822B */ + .lmp_subversion = RTLBT_ROM_LMP_8822B, + .hci_revision = 0xb, + .hci_version = 0x7, + .flags = RTLBT_IC_FLAG_CONFIG | RTLBT_IC_FLAG_MSFT, + .fw_name = "rtl8822b", + }, { /* 8852A */ + .lmp_subversion = RTLBT_ROM_LMP_8852A, + .hci_revision = 0xa, + .hci_version = 0xb, + .flags = RTLBT_IC_FLAG_MSFT, + .fw_name = "rtl8852au", + }, { /* 8852B */ + .lmp_subversion = RTLBT_ROM_LMP_8852A, + .hci_revision = 0xb, + .hci_version = 0xb, + .flags = RTLBT_IC_FLAG_MSFT, + .fw_name = "rtl8852bu", +#ifdef RTLBTFW_SUPPORTS_FW_V2 + }, { /* 8852C */ + .lmp_subversion = RTLBT_ROM_LMP_8852A, + .hci_revision = 0xc, + .hci_version = 0xc, + .flags = RTLBT_IC_FLAG_MSFT, + .fw_name = "rtl8852cu", + }, { /* 8851B */ + .lmp_subversion = RTLBT_ROM_LMP_8851B, + .hci_revision = 0xb, + .hci_version = 0xc, + .flags = RTLBT_IC_FLAG_MSFT, + .fw_name = "rtl8851bu", +#endif + }, +}; + +static const uint16_t project_ids[] = { + [ 0 ] = RTLBT_ROM_LMP_8723A, + [ 1 ] = RTLBT_ROM_LMP_8723B, + [ 2 ] = RTLBT_ROM_LMP_8821A, + [ 3 ] = RTLBT_ROM_LMP_8761A, + [ 7 ] = RTLBT_ROM_LMP_8703B, + [ 8 ] = RTLBT_ROM_LMP_8822B, + [ 9 ] = RTLBT_ROM_LMP_8723B, /* 8723DU */ + [ 10 ] = RTLBT_ROM_LMP_8821A, /* 8821CU */ + [ 13 ] = RTLBT_ROM_LMP_8822B, /* 8822CU */ + [ 14 ] = RTLBT_ROM_LMP_8761A, /* 8761BU */ + [ 18 ] = RTLBT_ROM_LMP_8852A, /* 8852AU */ + [ 19 ] = RTLBT_ROM_LMP_8723B, /* 8723FU */ + [ 20 ] = RTLBT_ROM_LMP_8852A, /* 8852BU */ + [ 25 ] = RTLBT_ROM_LMP_8852A, /* 8852CU */ + [ 33 ] = RTLBT_ROM_LMP_8822B, /* 8822EU */ + [ 36 ] = RTLBT_ROM_LMP_8851B, /* 8851BU */ +}; + +/* Signatures */ +static const uint8_t fw_header_sig_v1[8] = + {0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68}; /* Realtech */ +#ifdef RTLBTFW_SUPPORTS_FW_V2 +static const uint8_t fw_header_sig_v2[8] = + {0x52, 0x54, 0x42, 0x54, 0x43, 0x6F, 0x72, 0x65}; /* RTBTCore */ +#endif +static const uint8_t fw_ext_sig[4] = {0x51, 0x04, 0xFD, 0x77}; + +int +rtlbt_fw_read(struct rtlbt_firmware *fw, const char *fwname) +{ + int fd; + struct stat sb; + unsigned char *buf; + ssize_t r; + + fd = open(fwname, O_RDONLY); + if (fd < 0) { + warn("%s: open: %s", __func__, fwname); + return (0); + } + + if (fstat(fd, &sb) != 0) { + warn("%s: stat: %s", __func__, fwname); + close(fd); + return (0); + } + + buf = calloc(1, sb.st_size); + if (buf == NULL) { + warn("%s: calloc", __func__); + close(fd); + return (0); + } + + /* XXX handle partial reads */ + r = read(fd, buf, sb.st_size); + if (r < 0) { + warn("%s: read", __func__); + free(buf); + close(fd); + return (0); + } + + if (r != sb.st_size) { + rtlbt_err("read len %d != file size %d", + (int) r, + (int) sb.st_size); + free(buf); + close(fd); + return (0); + } + + /* We have everything, so! */ + + memset(fw, 0, sizeof(*fw)); + + fw->fwname = strdup(fwname); + fw->len = sb.st_size; + fw->buf = buf; + + close(fd); + return (1); +} + +void +rtlbt_fw_free(struct rtlbt_firmware *fw) +{ + if (fw->fwname) + free(fw->fwname); + if (fw->buf) + free(fw->buf); + memset(fw, 0, sizeof(*fw)); +} + +char * +rtlbt_get_fwname(const char *fw_name, const char *prefix, const char *suffix) +{ + char *fwname; + + asprintf(&fwname, "%s/%s%s", prefix, fw_name, suffix); + + return (fwname); +} + +const struct rtlbt_id_table * +rtlbt_get_ic(uint16_t lmp_subversion, uint16_t hci_revision, + uint8_t hci_version) +{ + unsigned int i; + + for (i = 0; i < nitems(rtlbt_ic_id_table); i++) { + if (rtlbt_ic_id_table[i].lmp_subversion == lmp_subversion && + rtlbt_ic_id_table[i].hci_revision == hci_revision && + rtlbt_ic_id_table[i].hci_version == hci_version) + return (rtlbt_ic_id_table + i); + } + + return (NULL); +} + +enum rtlbt_fw_type +rtlbt_get_fw_type(struct rtlbt_firmware *fw, uint16_t *fw_lmp_subversion) +{ + enum rtlbt_fw_type fw_type; + size_t fw_header_len; + uint8_t *ptr; + uint8_t opcode, oplen, project_id; + + if (fw->len < 8) { + rtlbt_err("firmware file too small"); + return (RTLBT_FW_TYPE_UNKNOWN); + } + + if (memcmp(fw->buf, fw_header_sig_v1, sizeof(fw_header_sig_v1)) == 0) { + fw_type = RTLBT_FW_TYPE_V1; + fw_header_len = sizeof(struct rtlbt_fw_header_v1); + } else +#ifdef RTLBTFW_SUPPORTS_FW_V2 + if (memcmp(fw->buf, fw_header_sig_v2, sizeof(fw_header_sig_v2)) == 0) { + fw_type = RTLBT_FW_TYPE_V2; + fw_header_len = sizeof(struct rtlbt_fw_header_v2); + } else +#endif + return (RTLBT_FW_TYPE_UNKNOWN); + + if (fw->len < fw_header_len + sizeof(fw_ext_sig) + 4) { + rtlbt_err("firmware file too small"); + return (RTLBT_FW_TYPE_UNKNOWN); + } + + ptr = fw->buf + fw->len - sizeof(fw_ext_sig); + if (memcmp(ptr, fw_ext_sig, sizeof(fw_ext_sig)) != 0) { + rtlbt_err("invalid extension section signature"); + return (RTLBT_FW_TYPE_UNKNOWN); + } + + do { + opcode = *--ptr; + oplen = *--ptr; + ptr -= oplen; + + rtlbt_debug("code=%x len=%x", opcode, oplen); + + if (opcode == 0x00) { + if (oplen != 1) { + rtlbt_err("invalid instruction length"); + return (RTLBT_FW_TYPE_UNKNOWN); + } + project_id = *ptr; + rtlbt_debug("project_id=%x", project_id); + if (project_id >= nitems(project_ids) || + project_ids[project_id] == 0) { + rtlbt_err("unknown project id %x", project_id); + return (RTLBT_FW_TYPE_UNKNOWN); + } + *fw_lmp_subversion = project_ids[project_id]; + return (fw_type); + } + } while (opcode != 0xff && ptr > fw->buf + fw_header_len); + + rtlbt_err("can not find project id instruction"); + return (RTLBT_FW_TYPE_UNKNOWN); +}; + +int +rtlbt_parse_fwfile_v1(struct rtlbt_firmware *fw, uint8_t rom_version) +{ + struct rtlbt_fw_header_v1 *fw_header; + uint8_t *patch_buf; + unsigned int i; + const uint8_t *chip_id_base; + uint32_t patch_offset; + uint16_t patch_length, num_patches; + + fw_header = (struct rtlbt_fw_header_v1 *)fw->buf; + num_patches = le16toh(fw_header->num_patches); + rtlbt_debug("fw_version=%x, num_patches=%d", + le32toh(fw_header->fw_version), num_patches); + + /* Find a right patch for the chip. */ + if (fw->len < sizeof(struct rtlbt_fw_header_v1) + + sizeof(fw_ext_sig) + 4 + 8 * num_patches) { + errno = EINVAL; + return (-1); + } + + chip_id_base = fw->buf + sizeof(struct rtlbt_fw_header_v1); + for (i = 0; i < num_patches; i++) { + if (le16dec(chip_id_base + i * 2) != rom_version + 1) + continue; + patch_length = le16dec(chip_id_base + 2 * (num_patches + i)); + patch_offset = le32dec(chip_id_base + 4 * (num_patches + i)); + break; + } + + if (i >= num_patches) { + rtlbt_err("can not find patch for chip id %d", rom_version); + errno = EINVAL; + return (-1); + } + + rtlbt_debug( + "index=%d length=%x offset=%x", i, patch_length, patch_offset); + if (fw->len < patch_offset + patch_length) { + errno = EINVAL; + return (-1); + } + + patch_buf = malloc(patch_length); + if (patch_buf == NULL) { + errno = ENOMEM; + return (-1); + } + + memcpy(patch_buf, fw->buf + patch_offset, patch_length - 4); + memcpy(patch_buf + patch_length - 4, &fw_header->fw_version, 4); + + free(fw->buf); + fw->buf = patch_buf; + fw->len = patch_length; + + return (0); +} + +int +rtlbt_append_fwfile(struct rtlbt_firmware *fw, struct rtlbt_firmware *opt) +{ + uint8_t *buf; + int len; + + len = fw->len + opt->len; + buf = realloc(fw->buf, len); + if (buf == NULL) + return (-1); + memcpy(buf + fw->len, opt->buf, opt->len); + fw->buf = buf; + fw->len = len; + + return (0); +} diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h new file mode 100644 index 000000000000..340abacba759 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h @@ -0,0 +1,92 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org> + * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> + * Copyright (c) 2023 Future Crew LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __RTLBT_FW_H__ +#define __RTLBT_FW_H__ + +#include <stdbool.h> + +#define RTLBT_ROM_LMP_8703B 0x8703 +#define RTLBT_ROM_LMP_8723A 0x1200 +#define RTLBT_ROM_LMP_8723B 0x8723 +#define RTLBT_ROM_LMP_8821A 0x8821 +#define RTLBT_ROM_LMP_8761A 0x8761 +#define RTLBT_ROM_LMP_8822B 0x8822 +#define RTLBT_ROM_LMP_8852A 0x8852 +#define RTLBT_ROM_LMP_8851B 0x8851 + +enum rtlbt_fw_type { + RTLBT_FW_TYPE_UNKNOWN, + RTLBT_FW_TYPE_V1, +#ifdef RTLBTFW_SUPPORTS_FW_V2 + RTLBT_FW_TYPE_V2, +#endif +}; + +struct rtlbt_id_table { + uint16_t lmp_subversion; + uint16_t hci_revision; + uint8_t hci_version; + uint8_t flags; +#define RTLBT_IC_FLAG_SIMPLE (0 << 1) +#define RTLBT_IC_FLAG_CONFIG (1 << 1) +#define RTLBT_IC_FLAG_MSFT (2 << 1) + const char *fw_name; +}; + +struct rtlbt_firmware { + char *fwname; + size_t len; + unsigned char *buf; +}; + +struct rtlbt_fw_header_v1 { + uint8_t signature[8]; + uint32_t fw_version; + uint16_t num_patches; +} __attribute__ ((packed)); + +struct rtlbt_fw_header_v2 { + uint8_t signature[8]; + uint8_t fw_version[8]; + uint32_t num_sections; +} __attribute__ ((packed)); + +int rtlbt_fw_read(struct rtlbt_firmware *fw, const char *fwname); +void rtlbt_fw_free(struct rtlbt_firmware *fw); +char *rtlbt_get_fwname(const char *fw_name, const char *prefix, + const char *suffix); +const struct rtlbt_id_table *rtlbt_get_ic(uint16_t lmp_subversion, + uint16_t hci_revision, uint8_t hci_version); +enum rtlbt_fw_type rtlbt_get_fw_type(struct rtlbt_firmware *fw, + uint16_t *fw_lmp_subversion); +int rtlbt_parse_fwfile_v1(struct rtlbt_firmware *fw, uint8_t rom_version); +int rtlbt_append_fwfile(struct rtlbt_firmware *fw, struct rtlbt_firmware *opt); + +#endif diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c b/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c new file mode 100644 index 000000000000..493358294c07 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c @@ -0,0 +1,236 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> + * Copyright (c) 2023 Future Crew LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +#include <sys/endian.h> +#include <sys/stat.h> + +#include <netgraph/bluetooth/include/ng_hci.h> + +#include <err.h> +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <libusb.h> + +#include "rtlbt_fw.h" +#include "rtlbt_hw.h" +#include "rtlbt_dbg.h" + +static int +rtlbt_hci_command(struct libusb_device_handle *hdl, struct rtlbt_hci_cmd *cmd, + void *event, int size, int *transferred, int timeout) +{ + struct timespec to, now, remains; + int ret; + + ret = libusb_control_transfer(hdl, + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_DEVICE, + 0, + 0, + 0, + (uint8_t *)cmd, + RTLBT_HCI_CMD_SIZE(cmd), + timeout); + + if (ret < 0) { + rtlbt_err("libusb_control_transfer() failed: err=%s", + libusb_strerror(ret)); + return (ret); + } + + clock_gettime(CLOCK_MONOTONIC, &now); + to = RTLBT_MSEC2TS(timeout); + timespecadd(&to, &now, &to); + + do { + timespecsub(&to, &now, &remains); + ret = libusb_interrupt_transfer(hdl, + RTLBT_INTERRUPT_ENDPOINT_ADDR, + event, + size, + transferred, + RTLBT_TS2MSEC(remains) + 1); + + if (ret < 0) { + rtlbt_err("libusb_interrupt_transfer() failed: err=%s", + libusb_strerror(ret)); + return (ret); + } + + switch (((struct rtlbt_hci_event *)event)->header.event) { + case NG_HCI_EVENT_COMMAND_COMPL: + if (*transferred < + (int)offsetof(struct rtlbt_hci_event_cmd_compl, data)) + break; + if (cmd->opcode != + ((struct rtlbt_hci_event_cmd_compl *)event)->opcode) + break; + return (0); + default: + break; + } + rtlbt_debug("Stray HCI event: %x", + ((struct rtlbt_hci_event *)event)->header.event); + } while (timespeccmp(&to, &now, >)); + + rtlbt_err("libusb_interrupt_transfer() failed: err=%s", + libusb_strerror(LIBUSB_ERROR_TIMEOUT)); + + return (LIBUSB_ERROR_TIMEOUT); +} + +int +rtlbt_read_local_ver(struct libusb_device_handle *hdl, + ng_hci_read_local_ver_rp *ver) +{ + int ret, transferred; + struct rtlbt_hci_event_cmd_compl *event; + struct rtlbt_hci_cmd cmd = { + .opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_INFO, + NG_HCI_OCF_READ_LOCAL_VER)), + .length = 0, + }; + uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(ng_hci_read_local_ver_rp)]; + + memset(buf, 0, sizeof(buf)); + + ret = rtlbt_hci_command(hdl, + &cmd, + buf, + sizeof(buf), + &transferred, + RTLBT_HCI_CMD_TIMEOUT); + + if (ret < 0 || transferred != sizeof(buf)) { + rtlbt_debug("Can't read local version: code=%d, size=%d", + ret, + transferred); + return (-1); + } + + event = (struct rtlbt_hci_event_cmd_compl *)buf; + memcpy(ver, event->data, sizeof(ng_hci_read_local_ver_rp)); + + return (0); +} + +int +rtlbt_read_rom_ver(struct libusb_device_handle *hdl, uint8_t *ver) +{ + int ret, transferred; + struct rtlbt_hci_event_cmd_compl *event; + struct rtlbt_hci_cmd cmd = { + .opcode = htole16(0xfc6d), + .length = 0, + }; + uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_rom_ver_rp)]; + + memset(buf, 0, sizeof(buf)); + + ret = rtlbt_hci_command(hdl, + &cmd, + buf, + sizeof(buf), + &transferred, + RTLBT_HCI_CMD_TIMEOUT); + + if (ret < 0 || transferred != sizeof(buf)) { + rtlbt_debug("Can't read ROM version: code=%d, size=%d", + ret, + transferred); + return (-1); + } + + event = (struct rtlbt_hci_event_cmd_compl *)buf; + *ver = ((struct rtlbt_rom_ver_rp *)event->data)->version; + + return (0); +} + +int +rtlbt_load_fwfile(struct libusb_device_handle *hdl, + const struct rtlbt_firmware *fw) +{ + uint8_t cmd_buf[RTLBT_HCI_MAX_CMD_SIZE]; + struct rtlbt_hci_cmd *cmd = (struct rtlbt_hci_cmd *)cmd_buf; + struct rtlbt_hci_dl_cmd *dl_cmd = (struct rtlbt_hci_dl_cmd *)cmd->data; + uint8_t evt_buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_hci_dl_rp)]; + uint8_t *data = fw->buf; + int frag_num = fw->len / RTLBT_MAX_CMD_DATA_LEN + 1; + int frag_len = RTLBT_MAX_CMD_DATA_LEN; + int i; + int ret, transferred; + + for (i = 0; i < frag_num; i++) { + + rtlbt_debug("download fw (%d/%d)", i + 1, frag_num); + + memset(cmd_buf, 0, sizeof(cmd_buf)); + cmd->opcode = htole16(0xfc20); + if (i > 0x7f) + dl_cmd->index = (i & 0x7f) + 1; + else + dl_cmd->index = i; + + if (i == (frag_num - 1)) { + dl_cmd->index |= 0x80; /* data end */ + frag_len = fw->len % RTLBT_MAX_CMD_DATA_LEN; + } + cmd->length = frag_len + 1; + memcpy(dl_cmd->data, data, frag_len); + + /* Send download command */ + ret = rtlbt_hci_command(hdl, + cmd, + evt_buf, + sizeof(evt_buf), + &transferred, + RTLBT_HCI_CMD_TIMEOUT); + if (ret < 0) { + rtlbt_err("download fw command failed (%d)", errno); + goto out; + } + if (transferred != sizeof(evt_buf)) { + rtlbt_err("download fw event length mismatch"); + errno = EIO; + ret = -1; + goto out; + } + + data += RTLBT_MAX_CMD_DATA_LEN; + } + +out: + return (ret); +} diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h b/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h new file mode 100644 index 000000000000..bc7c9fee3f57 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h @@ -0,0 +1,104 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> + * Copyright (c) 2023 Future Crew LLC. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#ifndef __RTLBT_HW_H__ +#define __RTLBT_HW_H__ + +#include <netgraph/bluetooth/include/ng_hci.h> + +/* USB control request (HCI command) structure */ +struct rtlbt_hci_cmd { + uint16_t opcode; + uint8_t length; + uint8_t data[]; +} __attribute__ ((packed)); + +#define RTLBT_HCI_CMD_SIZE(cmd) \ + ((cmd)->length + offsetof(struct rtlbt_hci_cmd, data)) + +/* USB interrupt transfer HCI event header structure */ +struct rtlbt_hci_evhdr { + uint8_t event; + uint8_t length; +} __attribute__ ((packed)); + +/* USB interrupt transfer (generic HCI event) structure */ +struct rtlbt_hci_event { + struct rtlbt_hci_evhdr header; + uint8_t data[]; +} __attribute__ ((packed)); + +/* USB interrupt transfer (HCI command completion event) structure */ +struct rtlbt_hci_event_cmd_compl { + struct rtlbt_hci_evhdr header; + uint8_t numpkt; + uint16_t opcode; + uint8_t data[]; +} __attribute__ ((packed)); + +#define RTLBT_HCI_EVT_COMPL_SIZE(payload) \ + (offsetof(struct rtlbt_hci_event_cmd_compl, data) + sizeof(payload)) + +#define RTLBT_CONTROL_ENDPOINT_ADDR 0x00 +#define RTLBT_INTERRUPT_ENDPOINT_ADDR 0x81 + +#define RTLBT_HCI_MAX_CMD_SIZE 256 +#define RTLBT_HCI_MAX_EVENT_SIZE 16 + +#define RTLBT_MSEC2TS(msec) \ + (struct timespec) { \ + .tv_sec = (msec) / 1000, \ + .tv_nsec = ((msec) % 1000) * 1000000 \ + }; +#define RTLBT_TS2MSEC(ts) ((ts).tv_sec * 1000 + (ts).tv_nsec / 1000000) +#define RTLBT_HCI_CMD_TIMEOUT 2000 /* ms */ +#define RTLBT_LOADCMPL_TIMEOUT 5000 /* ms */ + +#define RTLBT_MAX_CMD_DATA_LEN 252 + +struct rtlbt_rom_ver_rp { + uint8_t status; + uint8_t version; +} __attribute__ ((packed)); + +struct rtlbt_hci_dl_cmd { + uint8_t index; + uint8_t data[RTLBT_MAX_CMD_DATA_LEN]; +} __attribute__ ((packed)); + +struct rtlbt_hci_dl_rp { + uint8_t status; + uint8_t index; +} __attribute__ ((packed)); + +int rtlbt_read_local_ver(struct libusb_device_handle *hdl, + ng_hci_read_local_ver_rp *ver); +int rtlbt_read_rom_ver(struct libusb_device_handle *hdl, uint8_t *ver); +int rtlbt_load_fwfile(struct libusb_device_handle *hdl, + const struct rtlbt_firmware *fw); + +#endif diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.8 b/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.8 new file mode 100644 index 000000000000..c3c0b83d97e5 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.8 @@ -0,0 +1,100 @@ +.\" Copyright (c) 2013, 2016 Adrian Chadd <adrian@freebsd.org> +.\" Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> +.\" Copyright (c) 2023 Future Crew LLC. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd July 19, 2023 +.Dt RTLBTFW 8 +.Os +.Sh NAME +.Nm rtlbtfw +.Nd firmware download utility for Realtek 87XX/88XX chip based Bluetooth +USB devices +.Sh SYNOPSIS +.Nm +.Fl d Ar device_name +.Fl f Ar firmware_path +.Nm +.Fl h +.Sh DESCRIPTION +The +.Nm +utility downloads the specified firmware file to the specified +.Xr ugen 4 +device. +.Pp +This utility will +.Em only +work with Realtek 87XX/88XX chip based Bluetooth USB devices and some of +their successors. +The identification is currently based on USB vendor ID/product ID pair and +interface class. +For Realtek devices the vendor ID should be 0x0bda +.Pq Dv USB_VENDOR_REALTEK +and the 0-th interface class/subclass/protocol should be a Bluetooth RF +Wireless Controller. +Non-Realtek devices are identified based on USB vendor ID/product ID pair. +.Pp +Firmware files are available in the +.Pa comms/rtlbt-firmware +port. +.Pp +The +.Nm +utility will query the device to determine which firmware image and board +configuration to load in at runtime. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl D +Enable verbose debugging. +.It Fl d Ar device_name +Specify +.Xr ugen 4 +device name. +.It Fl I +Enable informational debugging. +.It Fl f Ar firmware_path +Specify the directory containing the firmware files to search and upload. +.It Fl h +Display usage message and exit. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr libusb 3 , +.Xr ng_ubt 4 , +.Xr ugen 4 , +.Xr devd 8 +.Sh AUTHORS +.Nm +is based on +.Xr ath3kfw 8 +utility used as firmware downloader template and on Linux btrtl driver +source code. +It is written by +.An Vladimir Kondratyev Aq Mt wulf@FreeBSD.org +under sponsorship from Future Crew LLC. +.Sh BUGS +Most likely. +Please report if found. diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf b/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf new file mode 100644 index 000000000000..d544913aaa12 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf @@ -0,0 +1,373 @@ +# +# Download Realtek 87XX/88XX bluetooth adaptor firmware +# + +# Generic Realtek vendor Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "INTERFACE"; + match "type" "ATTACH"; + match "vendor" "0x0bda"; + # only interface 0 is supported by rtlbtfw + match "interface" "0"; + match "intclass" "0xe0"; + match "intsubclass" "0x01"; + match "intprotocol" "0x01"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8821CE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x13d3"; + match "product" "0x3529"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8822CE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0bda"; + match "product" "(0xb00c|0xc822)"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8822CU Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x13d3"; + match "product" "0x3549"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8852AE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0bda"; + match "product" "(0x2852|0xc852|0x385a|0x4852)"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04c5"; + match "product" "0x165c"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04ca"; + match "product" "0x4006"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0cb8"; + match "product" "0xc549"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8852CE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04ca"; + match "product" "0x4007"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04c5"; + match "product" "0x1675"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0cb8"; + match "product" "0xc558"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x13d3"; + match "product" "(0x3587|0x3586|0x3592)"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8852BE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0cb8"; + match "product" "0xc559"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0bda"; + match "product" "0x887b"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x13d3"; + match "product" "0x3571"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8723AE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0930"; + match "product" "0x021d"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x13d3"; + match "product" "0x3394"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8723BE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0489"; + match "product" "(0xe085|0xe08b)"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04f2"; + match "product" "0xb49f"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x13d3"; + match "product" "(0x3410|0x3416|0x3459|0x3494)"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8723BU Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x7392"; + match "product" "0xa611"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8723DE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0bda"; + match "product" "0xb009"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2ff8"; + match "product" "0xb011"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8761BUV Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2357"; + match "product" "0x0604"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0b05"; + match "product" "0x190e"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2550"; + match "product" "0x8761"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0bda"; + match "product" "0x8771"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x6655"; + match "product" "0x8771"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x7392"; + match "product" "0xc611"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2b89"; + match "product" "0x8761"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8821AE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0b05"; + match "product" "0x17dc"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x13d3"; + match "product" "(0x3414|0x3458|0x3461|0x3462)"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8822BE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x13d3"; + match "product" "0x3526"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0b05"; + match "product" "0x185c"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8822CE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04ca"; + match "product" "0x4005"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04c5"; + match "product" "0x161f"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0b05"; + match "product" "0x18ef"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x13d3"; + match "product" "(0x3548|0x3549|0x3553|0x3555)"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2ff8"; + match "product" "0x3051"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x1358"; + match "product" "0xc123"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0bda"; + match "product" "0xc123"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0cb5"; + match "product" "0xc547"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; |