diff --git a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_rtl.c b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_rtl.c index c3fb98b46e6a..0181a67ac604 100644 --- a/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_rtl.c +++ b/sys/netgraph/bluetooth/drivers/ubt/ng_ubt_rtl.c @@ -1,266 +1,286 @@ /* * ng_ubt_rtl.c */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 Vladimir Kondratyev * 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. */ /* * Attempt to initialize FreeBSD bluetooth stack while Realtek 87XX/88XX USB * device is in bootloader mode locks the adapter hardly so it requires power * on/off cycle to restore. This driver blocks ng_ubt attachment until * operational firmware is loaded by rtlbtfw utility thus avoiding the lock up. */ #include #include #include #include #include #include #include #include "usbdevs.h" #include #include #define USB_DEBUG_VAR usb_debug #include #include #include #include #include #include #include #include static device_probe_t ubt_rtl_probe; /* * List of supported bluetooth devices. If you add a new device PID here ensure * that it is blacklisted in ng_ubt.c and is supported by rtlbtfw utility. */ const STRUCT_USB_HOST_ID ubt_rtl_devs[] = { /* Generic Realtek Bluetooth class devices */ { USB_VENDOR(USB_VENDOR_REALTEK), USB_IFACE_CLASS(UDCLASS_WIRELESS), USB_IFACE_SUBCLASS(UDSUBCLASS_RF), USB_IFACE_PROTOCOL(UDPROTO_BLUETOOTH) }, /* Realtek 8821CE Bluetooth devices */ { USB_VPI(0x13d3, 0x3529, 0) }, /* Realtek 8822CE Bluetooth devices */ { USB_VPI(0x0bda, 0xb00c, 0) }, { USB_VPI(0x0bda, 0xc822, 0) }, /* Realtek 8822CU Bluetooth devices */ { USB_VPI(0x13d3, 0x3549, 0) }, + /* Realtek 8851BE Bluetooth devices */ + { USB_VPI(0x13d3, 0x3600, 0) }, + /* Realtek 8852AE Bluetooth devices */ { USB_VPI(0x0bda, 0x2852, 0) }, { USB_VPI(0x0bda, 0xc852, 0) }, { USB_VPI(0x0bda, 0x385a, 0) }, { USB_VPI(0x0bda, 0x4852, 0) }, { USB_VPI(0x04c5, 0x165c, 0) }, { USB_VPI(0x04ca, 0x4006, 0) }, { USB_VPI(0x0cb8, 0xc549, 0) }, /* Realtek 8852CE Bluetooth devices */ { USB_VPI(0x04ca, 0x4007, 0) }, { USB_VPI(0x04c5, 0x1675, 0) }, { USB_VPI(0x0cb8, 0xc558, 0) }, { USB_VPI(0x13d3, 0x3587, 0) }, { USB_VPI(0x13d3, 0x3586, 0) }, { USB_VPI(0x13d3, 0x3592, 0) }, + { USB_VPI(0x0489, 0xe122, 0) }, /* Realtek 8852BE Bluetooth devices */ { USB_VPI(0x0cb8, 0xc559, 0) }, + { USB_VPI(0x0bda, 0x4853, 0) }, { USB_VPI(0x0bda, 0x887b, 0) }, + { USB_VPI(0x0bda, 0xb85b, 0) }, + { USB_VPI(0x13d3, 0x3570, 0) }, { USB_VPI(0x13d3, 0x3571, 0) }, + { USB_VPI(0x13d3, 0x3572, 0) }, + { USB_VPI(0x13d3, 0x3591, 0) }, + { USB_VPI(0x0489, 0xe123, 0) }, + { USB_VPI(0x0489, 0xe125, 0) }, + + /* Realtek 8852BT/8852BE-VT Bluetooth devices */ + { USB_VPI(0x0bda, 0x8520, 0) }, + + /* Realtek 8922AE Bluetooth devices */ + { USB_VPI(0x0bda, 0x8922, 0) }, + { USB_VPI(0x13d3, 0x3617, 0) }, + { USB_VPI(0x13d3, 0x3616, 0) }, + { USB_VPI(0x0489, 0xe130, 0) }, /* Realtek 8723AE Bluetooth devices */ { USB_VPI(0x0930, 0x021d, 0) }, { USB_VPI(0x13d3, 0x3394, 0) }, /* Realtek 8723BE Bluetooth devices */ { USB_VPI(0x0489, 0xe085, 0) }, { USB_VPI(0x0489, 0xe08b, 0) }, { USB_VPI(0x04f2, 0xb49f, 0) }, { USB_VPI(0x13d3, 0x3410, 0) }, { USB_VPI(0x13d3, 0x3416, 0) }, { USB_VPI(0x13d3, 0x3459, 0) }, { USB_VPI(0x13d3, 0x3494, 0) }, /* Realtek 8723BU Bluetooth devices */ { USB_VPI(0x7392, 0xa611, 0) }, /* Realtek 8723DE Bluetooth devices */ { USB_VPI(0x0bda, 0xb009, 0) }, { USB_VPI(0x2ff8, 0xb011, 0) }, /* Realtek 8761BUV Bluetooth devices */ { USB_VPI(0x2c4e, 0x0115, 0) }, { USB_VPI(0x2357, 0x0604, 0) }, { USB_VPI(0x0b05, 0x190e, 0) }, { USB_VPI(0x2550, 0x8761, 0) }, { USB_VPI(0x0bda, 0x8771, 0) }, { USB_VPI(0x6655, 0x8771, 0) }, { USB_VPI(0x7392, 0xc611, 0) }, { USB_VPI(0x2b89, 0x8761, 0) }, /* Realtek 8821AE Bluetooth devices */ { USB_VPI(0x0b05, 0x17dc, 0) }, { USB_VPI(0x13d3, 0x3414, 0) }, { USB_VPI(0x13d3, 0x3458, 0) }, { USB_VPI(0x13d3, 0x3461, 0) }, { USB_VPI(0x13d3, 0x3462, 0) }, /* Realtek 8822BE Bluetooth devices */ { USB_VPI(0x13d3, 0x3526, 0) }, { USB_VPI(0x0b05, 0x185c, 0) }, /* Realtek 8822CE Bluetooth devices */ { USB_VPI(0x04ca, 0x4005, 0) }, { USB_VPI(0x04c5, 0x161f, 0) }, { USB_VPI(0x0b05, 0x18ef, 0) }, { USB_VPI(0x13d3, 0x3548, 0) }, { USB_VPI(0x13d3, 0x3549, 0) }, { USB_VPI(0x13d3, 0x3553, 0) }, { USB_VPI(0x13d3, 0x3555, 0) }, { USB_VPI(0x2ff8, 0x3051, 0) }, { USB_VPI(0x1358, 0xc123, 0) }, { USB_VPI(0x0bda, 0xc123, 0) }, { USB_VPI(0x0cb5, 0xc547, 0) }, }; const size_t ubt_rtl_devs_sizeof = sizeof(ubt_rtl_devs); /* * List of lmp_subversion values that correspond to Realtek firmwares * incompatible with ng_ubt driver. Alternative firmware for these devices * has to be loaded with rtlbtfw utility. That will trigger lmp_subversion * change to different value. */ static const uint16_t ubt_rtl_lmp_subvers[] = { 0x8703, 0x1200, 0x8723, 0x8821, 0x8761, 0x8822, 0x8852, 0x8851, }; /* * Execute generic HCI command and return response in provided buffer. */ static usb_error_t ubt_rtl_do_hci_request(struct usb_device *udev, uint16_t opcode, void *resp, uint8_t resp_len) { #define UBT_RTL_HCICMD_TIMEOUT 2000 /* ms */ struct ubt_hci_event_command_compl *evt; struct ubt_hci_cmd cmd; usb_error_t error; memset(&cmd, 0, sizeof(cmd)); cmd.opcode = htole16(opcode); evt = malloc(offsetof(struct ubt_hci_event_command_compl, data) + resp_len, M_TEMP, M_ZERO | M_WAITOK); evt->header.event = NG_HCI_EVENT_COMMAND_COMPL; evt->header.length = resp_len + UBT_HCI_EVENT_COMPL_HEAD_SIZE; error = ubt_do_hci_request(udev, &cmd, evt, UBT_RTL_HCICMD_TIMEOUT); if (error != USB_ERR_NORMAL_COMPLETION) goto exit; if (evt->header.event == NG_HCI_EVENT_COMMAND_COMPL && evt->header.length == resp_len + UBT_HCI_EVENT_COMPL_HEAD_SIZE) memcpy(resp, evt->data, resp_len); else error = USB_ERR_INVAL; exit: free(evt, M_TEMP); return (error); } /* * Probe for a Realtek 87XX/88XX USB Bluetooth device. */ static int ubt_rtl_probe(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); ng_hci_read_local_ver_rp ver; unsigned int i; int error; if (uaa->usb_mode != USB_MODE_HOST) return (ENXIO); if (uaa->info.bIfaceIndex != 0) return (ENXIO); error = usbd_lookup_id_by_uaa(ubt_rtl_devs, sizeof(ubt_rtl_devs), uaa); if (error != 0) return (error); if (ubt_rtl_do_hci_request(uaa->device, NG_HCI_OPCODE(NG_HCI_OGF_INFO, NG_HCI_OCF_READ_LOCAL_VER), &ver, sizeof(ver)) != USB_ERR_NORMAL_COMPLETION) return (ENXIO); DPRINTFN(2, "hci_version 0x%02x\n", ver.hci_version); DPRINTFN(2, "hci_revision 0x%04x\n", le16toh(ver.hci_revision)); DPRINTFN(2, "lmp_version 0x%02x\n", ver.lmp_version); DPRINTFN(2, "lmp_subversion 0x%04x\n", le16toh(ver.lmp_subversion)); for (i = 0; i < nitems(ubt_rtl_lmp_subvers); i++) if (le16toh(ver.lmp_subversion) == ubt_rtl_lmp_subvers[i]) return (ENXIO); return (BUS_PROBE_DEFAULT); } /* * Module interface. Attach and detach methods, netgraph node type * registration and PNP string are inherited from ng_ubt.c driver. */ static device_method_t ubt_rtl_methods[] = { DEVMETHOD(device_probe, ubt_rtl_probe), DEVMETHOD_END }; DEFINE_CLASS_1(ubt, ubt_rtl_driver, ubt_rtl_methods, sizeof(struct ubt_softc), ubt_driver); DRIVER_MODULE(ng_ubt_rtl, uhub, ubt_rtl_driver, 0, 0); MODULE_VERSION(ng_ubt_rtl, NG_BLUETOOTH_VERSION); MODULE_DEPEND(ng_ubt_rtl, netgraph, NG_ABI_VERSION, NG_ABI_VERSION, NG_ABI_VERSION); MODULE_DEPEND(ng_ubt_rtl, ng_hci, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION); MODULE_DEPEND(ng_ubt_rtl, usb, 1, 1, 1); diff --git a/usr.sbin/bluetooth/rtlbtfw/main.c b/usr.sbin/bluetooth/rtlbtfw/main.c index 700b9b43bafa..e87a98036265 100644 --- a/usr.sbin/bluetooth/rtlbtfw/main.c +++ b/usr.sbin/bluetooth/rtlbtfw/main.c @@ -1,537 +1,557 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 Adrian Chadd * Copyright (c) 2019 Vladimir Kondratyev * 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 #include #include #include #include #include #include #include #include #include #include #include #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 8851BE Bluetooth devices */ + { .vendor_id = 0x13d3, .product_id = 0x3600 }, + /* 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 }, /* 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 }, + { .vendor_id = 0x0489, .product_id = 0xe122 }, /* Realtek 8852BE Bluetooth devices */ { .vendor_id = 0x0cb8, .product_id = 0xc559 }, + { .vendor_id = 0x0bda, .product_id = 0x4853 }, { .vendor_id = 0x0bda, .product_id = 0x887b }, + { .vendor_id = 0x0bda, .product_id = 0xb85b }, + { .vendor_id = 0x13d3, .product_id = 0x3570 }, { .vendor_id = 0x13d3, .product_id = 0x3571 }, + { .vendor_id = 0x13d3, .product_id = 0x3572 }, + { .vendor_id = 0x13d3, .product_id = 0x3591 }, + { .vendor_id = 0x0489, .product_id = 0xe123 }, + { .vendor_id = 0x0489, .product_id = 0xe125 }, + + /* Realtek 8852BT/8852BE-VT Bluetooth devices */ + { .vendor_id = 0x0bda, .product_id = 0x8520 }, + + /* Realtek 8922AE Bluetooth devices */ + { .vendor_id = 0x0bda, .product_id = 0x8922 }, + { .vendor_id = 0x13d3, .product_id = 0x3617 }, + { .vendor_id = 0x13d3, .product_id = 0x3616 }, + { .vendor_id = 0x0489, .product_id = 0xe130 }, /* 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 = 0x2c4e, .product_id = 0x0115 }, { .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; const char *fw_suffix; 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); fw_suffix = ic->fw_suffix == NULL ? "_fw.bin" : ic->fw_suffix; firmware_path = rtlbt_get_fwname(ic->fw_name, firmware_dir, fw_suffix); 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 */ if (fw_type == RTLBT_FW_TYPE_V2) { uint8_t key_id, reg_val[2]; r = rtlbt_read_reg16(hdl, RTLBT_SEC_PROJ, reg_val); if (r < 0) { rtlbt_err("rtlbt_read_reg16() failed code %d", r); goto shutdown; } key_id = reg_val[0]; rtlbt_debug("key_id = %d", key_id); r = rtlbt_parse_fwfile_v2(&fw, rom_version, key_id); } else 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_fw.c b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c index c58a8feb41a4..d7e9f2f939c6 100644 --- a/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c @@ -1,568 +1,582 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 Adrian Chadd * Copyright (c) 2019 Vladimir Kondratyev * 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 #include #include #include #include #include #include #include #include #include #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", }, { /* 8852C */ .lmp_subversion = RTLBT_ROM_LMP_8852A, .hci_revision = 0xc, .hci_version = 0xc, .flags = RTLBT_IC_FLAG_MSFT, .fw_name = "rtl8852cu", .fw_suffix = "_fw_v2.bin", }, { /* 8851B */ .lmp_subversion = RTLBT_ROM_LMP_8851B, .hci_revision = 0xb, .hci_version = 0xc, .flags = RTLBT_IC_FLAG_MSFT, .fw_name = "rtl8851bu", + }, { /* 8922A */ + .lmp_subversion = RTLBT_ROM_LMP_8922A, + .hci_revision = 0xa, + .hci_version = 0xc, + .flags = RTLBT_IC_FLAG_MSFT, + .fw_name = "rtl8922au", + }, { /* 8852BT/8852BE-VT */ + .lmp_subversion = RTLBT_ROM_LMP_8852A, + .hci_revision = 0x87, + .hci_version = 0xc, + .flags = RTLBT_IC_FLAG_MSFT, + .fw_name = "rtl8852btu", }, }; 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 */ + [ 44 ] = RTLBT_ROM_LMP_8922A, /* 8922A */ + [ 47 ] = RTLBT_ROM_LMP_8852A, /* 8852BT */ }; /* Signatures */ static const uint8_t fw_header_sig_v1[8] = {0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68}; /* Realtech */ static const uint8_t fw_header_sig_v2[8] = {0x52, 0x54, 0x42, 0x54, 0x43, 0x6F, 0x72, 0x65}; /* RTBTCore */ 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 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 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); } static void * rtlbt_iov_fetch(struct rtlbt_iov *iov, uint32_t len) { void *data = NULL; if (iov->len >= len) { data = iov->data; iov->data += len; iov->len -= len; } return (data); } static int rtlbt_patch_entry_cmp(struct rtlbt_patch_entry *a, struct rtlbt_patch_entry *b, void *thunk __unused) { return ((a->prio > b->prio) - (a->prio < b->prio)); } static int rtlbt_parse_section(struct rtlbt_patch_list *patch_list, uint32_t opcode, uint8_t *data, uint32_t len, uint8_t rom_version, uint8_t key_id) { struct rtlbt_sec_hdr *hdr; struct rtlbt_patch_entry *entry; struct rtlbt_subsec_hdr *subsec_hdr; struct rtlbt_subsec_security_hdr *subsec_security_hdr; uint16_t num_subsecs; uint8_t *subsec_data; uint32_t subsec_len; int i, sec_len = 0; struct rtlbt_iov iov = { .data = data, .len = len, }; hdr = rtlbt_iov_fetch(&iov, sizeof(*hdr)); if (hdr == NULL) { errno = EINVAL; return (-1); } num_subsecs = le16toh(hdr->num); for (i = 0; i < num_subsecs; i++) { subsec_hdr = rtlbt_iov_fetch(&iov, sizeof(*subsec_hdr)); if (subsec_hdr == NULL) break; subsec_len = le32toh(subsec_hdr->len); rtlbt_debug("subsection, eco 0x%02x, prio 0x%02x, len 0x%x", subsec_hdr->eco, subsec_hdr->prio, subsec_len); subsec_data = rtlbt_iov_fetch(&iov, subsec_len); if (subsec_data == NULL) break; if (subsec_hdr->eco == rom_version + 1) { if (opcode == RTLBT_PATCH_SECURITY_HEADER) { subsec_security_hdr = (void *)subsec_hdr; if (subsec_security_hdr->key_id == key_id) break; continue; } entry = calloc(1, sizeof(*entry)); if (entry == NULL) { errno = ENOMEM; return (-1); } *entry = (struct rtlbt_patch_entry) { .opcode = opcode, .len = subsec_len, .prio = subsec_hdr->prio, .data = subsec_data, }; SLIST_INSERT_HEAD(patch_list, entry, next); sec_len += subsec_len; } } return (sec_len); } int rtlbt_parse_fwfile_v2(struct rtlbt_firmware *fw, uint8_t rom_version, uint8_t key_id) { struct rtlbt_fw_header_v2 *hdr; struct rtlbt_section *section; struct rtlbt_patch_entry *entry; uint32_t num_sections; uint32_t section_len; uint32_t opcode; int seclen, len = 0, patch_len = 0; uint32_t i; uint8_t *section_data, *patch_buf; struct rtlbt_patch_list patch_list = SLIST_HEAD_INITIALIZER(patch_list); struct rtlbt_iov iov = { .data = fw->buf, .len = fw->len - 7, }; hdr = rtlbt_iov_fetch(&iov, sizeof(*hdr)); if (hdr == NULL) { errno = EINVAL; return (-1); } num_sections = le32toh(hdr->num_sections); rtlbt_debug("FW version %02x%02x%02x%02x-%02x%02x%02x%02x", hdr->fw_version[0], hdr->fw_version[1], hdr->fw_version[2], hdr->fw_version[3], hdr->fw_version[4], hdr->fw_version[5], hdr->fw_version[6], hdr->fw_version[7]); for (i = 0; i < num_sections; i++) { section = rtlbt_iov_fetch(&iov, sizeof(*section)); if (section == NULL) break; section_len = le32toh(section->len); opcode = le32toh(section->opcode); rtlbt_debug("section, opcode 0x%08x", section->opcode); section_data = rtlbt_iov_fetch(&iov, section_len); if (section_data == NULL) break; seclen = 0; switch (opcode) { case RTLBT_PATCH_SECURITY_HEADER: if (key_id == 0) break; /* FALLTHROUGH */ case RTLBT_PATCH_SNIPPETS: case RTLBT_PATCH_DUMMY_HEADER: seclen = rtlbt_parse_section(&patch_list, opcode, section_data, section_len, rom_version, key_id); break; default: break; } if (seclen < 0) { rtlbt_err("Section parse (0x%08x) failed. err %d", opcode, errno); return (-1); } len += seclen; } if (len == 0) { errno = ENOMSG; return (-1); } patch_buf = calloc(1, len); if (patch_buf == NULL) { errno = ENOMEM; return (-1); } SLIST_MERGESORT(&patch_list, NULL, rtlbt_patch_entry_cmp, rtlbt_patch_entry, next); while (!SLIST_EMPTY(&patch_list)) { entry = SLIST_FIRST(&patch_list); rtlbt_debug("opcode 0x%08x, addr 0x%p, len 0x%x", entry->opcode, entry->data, entry->len); memcpy(patch_buf + patch_len, entry->data, entry->len); patch_len += entry->len; SLIST_REMOVE_HEAD(&patch_list, next); free(entry); } if (patch_len == 0) { errno = EPERM; return (-1); } free(fw->buf); fw->buf = patch_buf; fw->len = patch_len; 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 index 48b30cb2289b..e9af6c93950e 100644 --- a/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h @@ -1,139 +1,140 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2013 Adrian Chadd * Copyright (c) 2019 Vladimir Kondratyev * 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 #include #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 +#define RTLBT_ROM_LMP_8922A 0x8922 #define RTLBT_PATCH_SNIPPETS 0x01 #define RTLBT_PATCH_DUMMY_HEADER 0x02 #define RTLBT_PATCH_SECURITY_HEADER 0x03 enum rtlbt_fw_type { RTLBT_FW_TYPE_UNKNOWN, RTLBT_FW_TYPE_V1, RTLBT_FW_TYPE_V2, }; 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; const char *fw_suffix; }; struct rtlbt_firmware { char *fwname; size_t len; unsigned char *buf; }; SLIST_HEAD(rtlbt_patch_list, rtlbt_patch_entry); struct rtlbt_patch_entry { SLIST_ENTRY(rtlbt_patch_entry) next; uint32_t opcode; uint32_t len; uint8_t prio; uint8_t *data; }; struct rtlbt_iov { uint8_t *data; uint32_t len; }; 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)); struct rtlbt_section { uint32_t opcode; uint32_t len; uint8_t data[]; } __attribute__ ((packed)); struct rtlbt_sec_hdr { uint16_t num; uint16_t reserved; } __attribute__ ((packed)); struct rtlbt_subsec_hdr { uint8_t eco; uint8_t prio; uint8_t cb[2]; uint32_t len; } __attribute__ ((packed)); struct rtlbt_subsec_security_hdr { uint8_t eco; uint8_t prio; uint8_t key_id; uint8_t reserved; uint32_t len; } __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_parse_fwfile_v2(struct rtlbt_firmware *fw, uint8_t rom_version, uint8_t reg_id); int rtlbt_append_fwfile(struct rtlbt_firmware *fw, struct rtlbt_firmware *opt); #endif diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf b/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf index d45ba0bd92c4..61ae53db8f39 100644 --- a/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf @@ -1,381 +1,443 @@ # # 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 8851BE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x13d3"; + match "product" "0x3600"; + 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"; }; +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0489"; + match "product" "0xe122"; + 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"; + match "product" "(0x4853|0x887b|0xb85b)"; + 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" "(0x3570|0x3571|0x3572|0x3591)"; + 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" "0x0489"; + match "product" "(0xe123|0xe125)"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8852BT/8852BE-VT Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0bda"; + match "product" "0x8520"; + action "/usr/sbin/rtlbtfw -d $cdev -f /usr/local/share/rtlbt-firmware"; +}; + +# Realtek 8922AE Bluetooth devices +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0bda"; + match "product" "0x8922"; 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"; + match "product" "(0x3617|0x3616)"; + 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" "0x0489"; + match "product" "0xe130"; 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" "0x2c4e"; match "product" "0x0115"; 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" "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"; };