1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2013 Adrian Chadd <adrian@freebsd.org> 5 * Copyright (c) 2019 Vladimir Kondratyev <wulf@FreeBSD.org> 6 * Copyright (c) 2023 Future Crew LLC. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/stat.h> 32 #include <sys/endian.h> 33 34 #include <err.h> 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <libgen.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <unistd.h> 42 43 #include <libusb.h> 44 45 #include "rtlbt_fw.h" 46 #include "rtlbt_hw.h" 47 #include "rtlbt_dbg.h" 48 49 #define _DEFAULT_RTLBT_FIRMWARE_PATH "/usr/share/firmware/rtlbt" 50 51 int rtlbt_do_debug = 0; 52 int rtlbt_do_info = 0; 53 54 struct rtlbt_devid { 55 uint16_t product_id; 56 uint16_t vendor_id; 57 }; 58 59 static struct rtlbt_devid rtlbt_list[] = { 60 /* Realtek 8821CE Bluetooth devices */ 61 { .vendor_id = 0x13d3, .product_id = 0x3529 }, 62 63 /* Realtek 8822CE Bluetooth devices */ 64 { .vendor_id = 0x0bda, .product_id = 0xb00c }, 65 { .vendor_id = 0x0bda, .product_id = 0xc822 }, 66 67 /* Realtek 8822CU Bluetooth devices */ 68 { .vendor_id = 0x13d3, .product_id = 0x3549 }, 69 70 /* Realtek 8851BE Bluetooth devices */ 71 { .vendor_id = 0x13d3, .product_id = 0x3600 }, 72 73 /* Realtek 8852AE Bluetooth devices */ 74 { .vendor_id = 0x0bda, .product_id = 0x2852 }, 75 { .vendor_id = 0x0bda, .product_id = 0xc852 }, 76 { .vendor_id = 0x0bda, .product_id = 0x385a }, 77 { .vendor_id = 0x0bda, .product_id = 0x4852 }, 78 { .vendor_id = 0x04c5, .product_id = 0x165c }, 79 { .vendor_id = 0x04ca, .product_id = 0x4006 }, 80 { .vendor_id = 0x0cb8, .product_id = 0xc549 }, 81 82 /* Realtek 8852CE Bluetooth devices */ 83 { .vendor_id = 0x04ca, .product_id = 0x4007 }, 84 { .vendor_id = 0x04c5, .product_id = 0x1675 }, 85 { .vendor_id = 0x0cb8, .product_id = 0xc558 }, 86 { .vendor_id = 0x13d3, .product_id = 0x3587 }, 87 { .vendor_id = 0x13d3, .product_id = 0x3586 }, 88 { .vendor_id = 0x13d3, .product_id = 0x3592 }, 89 { .vendor_id = 0x0489, .product_id = 0xe122 }, 90 91 /* Realtek 8852BE Bluetooth devices */ 92 { .vendor_id = 0x0cb8, .product_id = 0xc559 }, 93 { .vendor_id = 0x0bda, .product_id = 0x4853 }, 94 { .vendor_id = 0x0bda, .product_id = 0x887b }, 95 { .vendor_id = 0x0bda, .product_id = 0xb85b }, 96 { .vendor_id = 0x13d3, .product_id = 0x3570 }, 97 { .vendor_id = 0x13d3, .product_id = 0x3571 }, 98 { .vendor_id = 0x13d3, .product_id = 0x3572 }, 99 { .vendor_id = 0x13d3, .product_id = 0x3591 }, 100 { .vendor_id = 0x0489, .product_id = 0xe123 }, 101 { .vendor_id = 0x0489, .product_id = 0xe125 }, 102 103 /* Realtek 8852BT/8852BE-VT Bluetooth devices */ 104 { .vendor_id = 0x0bda, .product_id = 0x8520 }, 105 106 /* Realtek 8922AE Bluetooth devices */ 107 { .vendor_id = 0x0bda, .product_id = 0x8922 }, 108 { .vendor_id = 0x13d3, .product_id = 0x3617 }, 109 { .vendor_id = 0x13d3, .product_id = 0x3616 }, 110 { .vendor_id = 0x0489, .product_id = 0xe130 }, 111 112 /* Realtek 8723AE Bluetooth devices */ 113 { .vendor_id = 0x0930, .product_id = 0x021d }, 114 { .vendor_id = 0x13d3, .product_id = 0x3394 }, 115 116 /* Realtek 8723BE Bluetooth devices */ 117 { .vendor_id = 0x0489, .product_id = 0xe085 }, 118 { .vendor_id = 0x0489, .product_id = 0xe08b }, 119 { .vendor_id = 0x04f2, .product_id = 0xb49f }, 120 { .vendor_id = 0x13d3, .product_id = 0x3410 }, 121 { .vendor_id = 0x13d3, .product_id = 0x3416 }, 122 { .vendor_id = 0x13d3, .product_id = 0x3459 }, 123 { .vendor_id = 0x13d3, .product_id = 0x3494 }, 124 125 /* Realtek 8723BU Bluetooth devices */ 126 { .vendor_id = 0x7392, .product_id = 0xa611 }, 127 128 /* Realtek 8723DE Bluetooth devices */ 129 { .vendor_id = 0x0bda, .product_id = 0xb009 }, 130 { .vendor_id = 0x2ff8, .product_id = 0xb011 }, 131 132 /* Realtek 8761BUV Bluetooth devices */ 133 { .vendor_id = 0x2c4e, .product_id = 0x0115 }, 134 { .vendor_id = 0x2357, .product_id = 0x0604 }, 135 { .vendor_id = 0x0b05, .product_id = 0x190e }, 136 { .vendor_id = 0x2550, .product_id = 0x8761 }, 137 { .vendor_id = 0x0bda, .product_id = 0x8771 }, 138 { .vendor_id = 0x6655, .product_id = 0x8771 }, 139 { .vendor_id = 0x7392, .product_id = 0xc611 }, 140 { .vendor_id = 0x2b89, .product_id = 0x8761 }, 141 142 /* Realtek 8821AE Bluetooth devices */ 143 { .vendor_id = 0x0b05, .product_id = 0x17dc }, 144 { .vendor_id = 0x13d3, .product_id = 0x3414 }, 145 { .vendor_id = 0x13d3, .product_id = 0x3458 }, 146 { .vendor_id = 0x13d3, .product_id = 0x3461 }, 147 { .vendor_id = 0x13d3, .product_id = 0x3462 }, 148 149 /* Realtek 8822BE Bluetooth devices */ 150 { .vendor_id = 0x13d3, .product_id = 0x3526 }, 151 { .vendor_id = 0x0b05, .product_id = 0x185c }, 152 153 /* Realtek 8822CE Bluetooth devices */ 154 { .vendor_id = 0x04ca, .product_id = 0x4005 }, 155 { .vendor_id = 0x04c5, .product_id = 0x161f }, 156 { .vendor_id = 0x0b05, .product_id = 0x18ef }, 157 { .vendor_id = 0x13d3, .product_id = 0x3548 }, 158 { .vendor_id = 0x13d3, .product_id = 0x3549 }, 159 { .vendor_id = 0x13d3, .product_id = 0x3553 }, 160 { .vendor_id = 0x13d3, .product_id = 0x3555 }, 161 { .vendor_id = 0x2ff8, .product_id = 0x3051 }, 162 { .vendor_id = 0x1358, .product_id = 0xc123 }, 163 { .vendor_id = 0x0bda, .product_id = 0xc123 }, 164 { .vendor_id = 0x0cb5, .product_id = 0xc547 }, 165 }; 166 167 static int 168 rtlbt_is_realtek(struct libusb_device_descriptor *d) 169 { 170 int i; 171 172 /* Search looking for whether it's a Realtek-based device */ 173 for (i = 0; i < (int) nitems(rtlbt_list); i++) { 174 if ((rtlbt_list[i].product_id == d->idProduct) && 175 (rtlbt_list[i].vendor_id == d->idVendor)) { 176 rtlbt_info("found USB Realtek"); 177 return (1); 178 } 179 } 180 181 /* Not found */ 182 return (0); 183 } 184 185 static int 186 rtlbt_is_bluetooth(struct libusb_device *dev) 187 { 188 struct libusb_config_descriptor *cfg; 189 const struct libusb_interface *ifc; 190 const struct libusb_interface_descriptor *d; 191 int r; 192 193 r = libusb_get_active_config_descriptor(dev, &cfg); 194 if (r < 0) { 195 rtlbt_err("Cannot retrieve config descriptor: %s", 196 libusb_error_name(r)); 197 return (0); 198 } 199 200 if (cfg->bNumInterfaces != 0) { 201 /* Only 0-th HCI/ACL interface is supported by downloader */ 202 ifc = &cfg->interface[0]; 203 if (ifc->num_altsetting != 0) { 204 /* BT HCI/ACL interface has no altsettings */ 205 d = &ifc->altsetting[0]; 206 /* Check if interface is a bluetooth */ 207 if (d->bInterfaceClass == LIBUSB_CLASS_WIRELESS && 208 d->bInterfaceSubClass == 0x01 && 209 d->bInterfaceProtocol == 0x01) { 210 rtlbt_info("found USB Realtek"); 211 libusb_free_config_descriptor(cfg); 212 return (1); 213 } 214 } 215 } 216 libusb_free_config_descriptor(cfg); 217 218 /* Not found */ 219 return (0); 220 } 221 222 static libusb_device * 223 rtlbt_find_device(libusb_context *ctx, int bus_id, int dev_id) 224 { 225 libusb_device **list, *dev = NULL, *found = NULL; 226 struct libusb_device_descriptor d; 227 ssize_t cnt, i; 228 int r; 229 230 cnt = libusb_get_device_list(ctx, &list); 231 if (cnt < 0) { 232 rtlbt_err("libusb_get_device_list() failed: code %lld", 233 (long long int) cnt); 234 return (NULL); 235 } 236 237 /* 238 * Scan through USB device list. 239 */ 240 for (i = 0; i < cnt; i++) { 241 dev = list[i]; 242 if (bus_id == libusb_get_bus_number(dev) && 243 dev_id == libusb_get_device_address(dev)) { 244 /* Get the device descriptor for this device entry */ 245 r = libusb_get_device_descriptor(dev, &d); 246 if (r != 0) { 247 rtlbt_err("libusb_get_device_descriptor: %s", 248 libusb_strerror(r)); 249 break; 250 } 251 252 /* For non-Realtek match on the vendor/product id */ 253 if (rtlbt_is_realtek(&d)) { 254 /* 255 * Take a reference so it's not freed later on. 256 */ 257 found = libusb_ref_device(dev); 258 break; 259 } 260 /* For Realtek vendor match on the interface class */ 261 if (d.idVendor == 0x0bda && rtlbt_is_bluetooth(dev)) { 262 /* 263 * Take a reference so it's not freed later on. 264 */ 265 found = libusb_ref_device(dev); 266 break; 267 } 268 } 269 } 270 271 libusb_free_device_list(list, 1); 272 return (found); 273 } 274 275 static void 276 rtlbt_dump_version(ng_hci_read_local_ver_rp *ver) 277 { 278 rtlbt_info("hci_version 0x%02x", ver->hci_version); 279 rtlbt_info("hci_revision 0x%04x", le16toh(ver->hci_revision)); 280 rtlbt_info("lmp_version 0x%02x", ver->lmp_version); 281 rtlbt_info("lmp_subversion 0x%04x", le16toh(ver->lmp_subversion)); 282 } 283 284 /* 285 * Parse ugen name and extract device's bus and address 286 */ 287 288 static int 289 parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr) 290 { 291 char *ep; 292 293 if (strncmp(ugen, "ugen", 4) != 0) 294 return (-1); 295 296 *bus = (uint8_t) strtoul(ugen + 4, &ep, 10); 297 if (*ep != '.') 298 return (-1); 299 300 *addr = (uint8_t) strtoul(ep + 1, &ep, 10); 301 if (*ep != '\0') 302 return (-1); 303 304 return (0); 305 } 306 307 static void 308 usage(void) 309 { 310 fprintf(stderr, 311 "Usage: rtlbtfw (-D) -d ugenX.Y (-f firmware path) (-I)\n"); 312 fprintf(stderr, " -D: enable debugging\n"); 313 fprintf(stderr, " -d: device to operate upon\n"); 314 fprintf(stderr, " -f: firmware path, if not default\n"); 315 fprintf(stderr, " -I: enable informational output\n"); 316 exit(127); 317 } 318 319 int 320 main(int argc, char *argv[]) 321 { 322 libusb_context *ctx = NULL; 323 libusb_device *dev = NULL; 324 libusb_device_handle *hdl = NULL; 325 ng_hci_read_local_ver_rp ver; 326 int r; 327 uint8_t bus_id = 0, dev_id = 0; 328 int devid_set = 0; 329 int n; 330 char *firmware_dir = NULL; 331 char *firmware_path = NULL; 332 char *config_path = NULL; 333 const char *fw_suffix; 334 int retcode = 1; 335 const struct rtlbt_id_table *ic; 336 uint8_t rom_version; 337 struct rtlbt_firmware fw, cfg; 338 enum rtlbt_fw_type fw_type; 339 uint16_t fw_lmp_subversion; 340 341 /* Parse command line arguments */ 342 while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) { 343 switch (n) { 344 case 'd': /* ugen device name */ 345 devid_set = 1; 346 if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0) 347 usage(); 348 break; 349 case 'D': 350 rtlbt_do_debug = 1; 351 break; 352 case 'f': /* firmware dir */ 353 if (firmware_dir) 354 free(firmware_dir); 355 firmware_dir = strdup(optarg); 356 break; 357 case 'I': 358 rtlbt_do_info = 1; 359 break; 360 case 'h': 361 default: 362 usage(); 363 break; 364 /* NOT REACHED */ 365 } 366 } 367 368 /* Ensure the devid was given! */ 369 if (devid_set == 0) { 370 usage(); 371 /* NOTREACHED */ 372 } 373 374 /* libusb setup */ 375 r = libusb_init(&ctx); 376 if (r != 0) { 377 rtlbt_err("libusb_init failed: code %d", r); 378 exit(127); 379 } 380 381 rtlbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id); 382 383 /* Find a device based on the bus/dev id */ 384 dev = rtlbt_find_device(ctx, bus_id, dev_id); 385 if (dev == NULL) { 386 rtlbt_err("device not found"); 387 goto shutdown; 388 } 389 390 /* XXX enforce that bInterfaceNumber is 0 */ 391 392 /* XXX enforce the device/product id if they're non-zero */ 393 394 /* Grab device handle */ 395 r = libusb_open(dev, &hdl); 396 if (r != 0) { 397 rtlbt_err("libusb_open() failed: code %d", r); 398 goto shutdown; 399 } 400 401 /* Check if ng_ubt is attached */ 402 r = libusb_kernel_driver_active(hdl, 0); 403 if (r < 0) { 404 rtlbt_err("libusb_kernel_driver_active() failed: code %d", r); 405 goto shutdown; 406 } 407 if (r > 0) { 408 rtlbt_info("Firmware has already been downloaded"); 409 retcode = 0; 410 goto shutdown; 411 } 412 413 /* Get local version */ 414 r = rtlbt_read_local_ver(hdl, &ver); 415 if (r < 0) { 416 rtlbt_err("rtlbt_read_local_ver() failed code %d", r); 417 goto shutdown; 418 } 419 rtlbt_dump_version(&ver); 420 421 ic = rtlbt_get_ic(ver.lmp_subversion, ver.hci_revision, 422 ver.hci_version); 423 if (ic == NULL) { 424 rtlbt_err("rtlbt_get_ic() failed: Unknown IC"); 425 goto shutdown; 426 } 427 428 /* Default the firmware path */ 429 if (firmware_dir == NULL) 430 firmware_dir = strdup(_DEFAULT_RTLBT_FIRMWARE_PATH); 431 432 fw_suffix = ic->fw_suffix == NULL ? "_fw.bin" : ic->fw_suffix; 433 firmware_path = rtlbt_get_fwname(ic->fw_name, firmware_dir, fw_suffix); 434 if (firmware_path == NULL) 435 goto shutdown; 436 437 rtlbt_debug("firmware_path = %s", firmware_path); 438 439 rtlbt_info("loading firmware %s", firmware_path); 440 441 /* Read in the firmware */ 442 if (rtlbt_fw_read(&fw, firmware_path) <= 0) { 443 rtlbt_debug("rtlbt_fw_read() failed"); 444 return (-1); 445 } 446 447 fw_type = rtlbt_get_fw_type(&fw, &fw_lmp_subversion); 448 if (fw_type == RTLBT_FW_TYPE_UNKNOWN && 449 (ic->flags & RTLBT_IC_FLAG_SIMPLE) == 0) { 450 rtlbt_debug("Unknown firmware type"); 451 goto shutdown; 452 } 453 454 if (fw_type != RTLBT_FW_TYPE_UNKNOWN) { 455 456 /* Match hardware and firmware lmp_subversion */ 457 if (fw_lmp_subversion != ver.lmp_subversion) { 458 rtlbt_err("firmware is for %x but this is a %x", 459 fw_lmp_subversion, ver.lmp_subversion); 460 goto shutdown; 461 } 462 463 /* Query a ROM version */ 464 r = rtlbt_read_rom_ver(hdl, &rom_version); 465 if (r < 0) { 466 rtlbt_err("rtlbt_read_rom_ver() failed code %d", r); 467 goto shutdown; 468 } 469 rtlbt_debug("rom_version = %d", rom_version); 470 471 /* Load in the firmware */ 472 if (fw_type == RTLBT_FW_TYPE_V2) { 473 uint8_t key_id, reg_val[2]; 474 r = rtlbt_read_reg16(hdl, RTLBT_SEC_PROJ, reg_val); 475 if (r < 0) { 476 rtlbt_err("rtlbt_read_reg16() failed code %d", r); 477 goto shutdown; 478 } 479 key_id = reg_val[0]; 480 rtlbt_debug("key_id = %d", key_id); 481 r = rtlbt_parse_fwfile_v2(&fw, rom_version, key_id); 482 } else 483 r = rtlbt_parse_fwfile_v1(&fw, rom_version); 484 if (r < 0) { 485 rtlbt_err("Parseing firmware file failed"); 486 goto shutdown; 487 } 488 489 config_path = rtlbt_get_fwname(ic->fw_name, firmware_dir, 490 "_config.bin"); 491 if (config_path == NULL) 492 goto shutdown; 493 494 rtlbt_info("loading config %s", config_path); 495 496 /* Read in the config file */ 497 if (rtlbt_fw_read(&cfg, config_path) <= 0) { 498 rtlbt_err("rtlbt_fw_read() failed"); 499 if ((ic->flags & RTLBT_IC_FLAG_CONFIG) != 0) 500 goto shutdown; 501 } else { 502 r = rtlbt_append_fwfile(&fw, &cfg); 503 rtlbt_fw_free(&cfg); 504 if (r < 0) { 505 rtlbt_err("Appending config file failed"); 506 goto shutdown; 507 } 508 } 509 } 510 511 r = rtlbt_load_fwfile(hdl, &fw); 512 if (r < 0) { 513 rtlbt_debug("Loading firmware file failed"); 514 goto shutdown; 515 } 516 517 /* free it */ 518 rtlbt_fw_free(&fw); 519 520 rtlbt_info("Firmware download complete"); 521 522 /* Execute Read Local Version one more time */ 523 r = rtlbt_read_local_ver(hdl, &ver); 524 if (r < 0) { 525 rtlbt_err("rtlbt_read_local_ver() failed code %d", r); 526 goto shutdown; 527 } 528 rtlbt_dump_version(&ver); 529 530 retcode = 0; 531 532 /* Ask kernel driver to probe and attach device again */ 533 r = libusb_reset_device(hdl); 534 if (r != 0) 535 rtlbt_err("libusb_reset_device() failed: %s", 536 libusb_strerror(r)); 537 538 shutdown: 539 540 /* Shutdown */ 541 542 if (hdl != NULL) 543 libusb_close(hdl); 544 545 if (dev != NULL) 546 libusb_unref_device(dev); 547 548 if (ctx != NULL) 549 libusb_exit(ctx); 550 551 if (retcode == 0) 552 rtlbt_info("Firmware download is successful!"); 553 else 554 rtlbt_err("Firmware download failed!"); 555 556 return (retcode); 557 } 558