Path: blob/main/usr.sbin/bluetooth/rtlbtfw/main.c
104817 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2013 Adrian Chadd <[email protected]>4* Copyright (c) 2019 Vladimir Kondratyev <[email protected]>5* Copyright (c) 2023 Future Crew LLC.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10* 1. Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829#include <sys/param.h>30#include <sys/stat.h>31#include <sys/endian.h>3233#include <err.h>34#include <errno.h>35#include <fcntl.h>36#include <libgen.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>40#include <unistd.h>4142#include <libusb.h>4344#include "rtlbt_fw.h"45#include "rtlbt_hw.h"46#include "rtlbt_dbg.h"4748#define _DEFAULT_RTLBT_FIRMWARE_PATH "/usr/share/firmware/rtlbt"4950int rtlbt_do_debug = 0;51int rtlbt_do_info = 0;5253struct rtlbt_devid {54uint16_t product_id;55uint16_t vendor_id;56};5758static struct rtlbt_devid rtlbt_list[] = {59/* Realtek 8821CE Bluetooth devices */60{ .vendor_id = 0x13d3, .product_id = 0x3529 },6162/* Realtek 8822CE Bluetooth devices */63{ .vendor_id = 0x0bda, .product_id = 0xb00c },64{ .vendor_id = 0x0bda, .product_id = 0xc822 },6566/* Realtek 8851BE Bluetooth devices */67{ .vendor_id = 0x13d3, .product_id = 0x3600 },6869/* Realtek 8852AE Bluetooth devices */70{ .vendor_id = 0x0bda, .product_id = 0x2852 },71{ .vendor_id = 0x0bda, .product_id = 0xc852 },72{ .vendor_id = 0x0bda, .product_id = 0x385a },73{ .vendor_id = 0x0bda, .product_id = 0x4852 },74{ .vendor_id = 0x04c5, .product_id = 0x165c },75{ .vendor_id = 0x04ca, .product_id = 0x4006 },76{ .vendor_id = 0x0cb8, .product_id = 0xc549 },7778/* Realtek 8852CE Bluetooth devices */79{ .vendor_id = 0x04ca, .product_id = 0x4007 },80{ .vendor_id = 0x04c5, .product_id = 0x1675 },81{ .vendor_id = 0x0cb8, .product_id = 0xc558 },82{ .vendor_id = 0x13d3, .product_id = 0x3587 },83{ .vendor_id = 0x13d3, .product_id = 0x3586 },84{ .vendor_id = 0x13d3, .product_id = 0x3592 },85{ .vendor_id = 0x0489, .product_id = 0xe122 },8687/* Realtek 8852BE Bluetooth devices */88{ .vendor_id = 0x0cb8, .product_id = 0xc559 },89{ .vendor_id = 0x0bda, .product_id = 0x4853 },90{ .vendor_id = 0x0bda, .product_id = 0x887b },91{ .vendor_id = 0x0bda, .product_id = 0xb85b },92{ .vendor_id = 0x13d3, .product_id = 0x3570 },93{ .vendor_id = 0x13d3, .product_id = 0x3571 },94{ .vendor_id = 0x13d3, .product_id = 0x3572 },95{ .vendor_id = 0x13d3, .product_id = 0x3591 },96{ .vendor_id = 0x0489, .product_id = 0xe123 },97{ .vendor_id = 0x0489, .product_id = 0xe125 },9899/* Realtek 8852BT/8852BE-VT Bluetooth devices */100{ .vendor_id = 0x0bda, .product_id = 0x8520 },101102/* Realtek 8922AE Bluetooth devices */103{ .vendor_id = 0x0bda, .product_id = 0x8922 },104{ .vendor_id = 0x13d3, .product_id = 0x3617 },105{ .vendor_id = 0x13d3, .product_id = 0x3616 },106{ .vendor_id = 0x0489, .product_id = 0xe130 },107108/* Realtek 8723AE Bluetooth devices */109{ .vendor_id = 0x0930, .product_id = 0x021d },110{ .vendor_id = 0x13d3, .product_id = 0x3394 },111112/* Realtek 8723BE Bluetooth devices */113{ .vendor_id = 0x0489, .product_id = 0xe085 },114{ .vendor_id = 0x0489, .product_id = 0xe08b },115{ .vendor_id = 0x04f2, .product_id = 0xb49f },116{ .vendor_id = 0x13d3, .product_id = 0x3410 },117{ .vendor_id = 0x13d3, .product_id = 0x3416 },118{ .vendor_id = 0x13d3, .product_id = 0x3459 },119{ .vendor_id = 0x13d3, .product_id = 0x3494 },120121/* Realtek 8723BU Bluetooth devices */122{ .vendor_id = 0x7392, .product_id = 0xa611 },123124/* Realtek 8723DE Bluetooth devices */125{ .vendor_id = 0x0bda, .product_id = 0xb009 },126{ .vendor_id = 0x2ff8, .product_id = 0xb011 },127128/* Realtek 8761BUV Bluetooth devices */129{ .vendor_id = 0x2c4e, .product_id = 0x0115 },130{ .vendor_id = 0x2357, .product_id = 0x0604 },131{ .vendor_id = 0x0b05, .product_id = 0x190e },132{ .vendor_id = 0x2550, .product_id = 0x8761 },133{ .vendor_id = 0x0bda, .product_id = 0x8771 },134{ .vendor_id = 0x6655, .product_id = 0x8771 },135{ .vendor_id = 0x7392, .product_id = 0xc611 },136{ .vendor_id = 0x2b89, .product_id = 0x8761 },137138/* Realtek 8821AE Bluetooth devices */139{ .vendor_id = 0x0b05, .product_id = 0x17dc },140{ .vendor_id = 0x13d3, .product_id = 0x3414 },141{ .vendor_id = 0x13d3, .product_id = 0x3458 },142{ .vendor_id = 0x13d3, .product_id = 0x3461 },143{ .vendor_id = 0x13d3, .product_id = 0x3462 },144145/* Realtek 8822BE Bluetooth devices */146{ .vendor_id = 0x13d3, .product_id = 0x3526 },147{ .vendor_id = 0x0b05, .product_id = 0x185c },148149/* Realtek 8822CE Bluetooth devices */150{ .vendor_id = 0x04ca, .product_id = 0x4005 },151{ .vendor_id = 0x04c5, .product_id = 0x161f },152{ .vendor_id = 0x0b05, .product_id = 0x18ef },153{ .vendor_id = 0x13d3, .product_id = 0x3548 },154{ .vendor_id = 0x13d3, .product_id = 0x3549 },155{ .vendor_id = 0x13d3, .product_id = 0x3553 },156{ .vendor_id = 0x13d3, .product_id = 0x3555 },157{ .vendor_id = 0x2ff8, .product_id = 0x3051 },158{ .vendor_id = 0x1358, .product_id = 0xc123 },159{ .vendor_id = 0x0bda, .product_id = 0xc123 },160{ .vendor_id = 0x0cb5, .product_id = 0xc547 },161};162163static int164rtlbt_is_realtek(struct libusb_device_descriptor *d)165{166int i;167168/* Search looking for whether it's a Realtek-based device */169for (i = 0; i < (int) nitems(rtlbt_list); i++) {170if ((rtlbt_list[i].product_id == d->idProduct) &&171(rtlbt_list[i].vendor_id == d->idVendor)) {172rtlbt_info("found USB Realtek");173return (1);174}175}176177/* Not found */178return (0);179}180181static int182rtlbt_is_bluetooth(struct libusb_device *dev)183{184struct libusb_config_descriptor *cfg;185const struct libusb_interface *ifc;186const struct libusb_interface_descriptor *d;187int r;188189r = libusb_get_active_config_descriptor(dev, &cfg);190if (r < 0) {191rtlbt_err("Cannot retrieve config descriptor: %s",192libusb_error_name(r));193return (0);194}195196if (cfg->bNumInterfaces != 0) {197/* Only 0-th HCI/ACL interface is supported by downloader */198ifc = &cfg->interface[0];199if (ifc->num_altsetting != 0) {200/* BT HCI/ACL interface has no altsettings */201d = &ifc->altsetting[0];202/* Check if interface is a bluetooth */203if (d->bInterfaceClass == LIBUSB_CLASS_WIRELESS &&204d->bInterfaceSubClass == 0x01 &&205d->bInterfaceProtocol == 0x01) {206rtlbt_info("found USB Realtek");207libusb_free_config_descriptor(cfg);208return (1);209}210}211}212libusb_free_config_descriptor(cfg);213214/* Not found */215return (0);216}217218static libusb_device *219rtlbt_find_device(libusb_context *ctx, int bus_id, int dev_id)220{221libusb_device **list, *dev = NULL, *found = NULL;222struct libusb_device_descriptor d;223ssize_t cnt, i;224int r;225226cnt = libusb_get_device_list(ctx, &list);227if (cnt < 0) {228rtlbt_err("libusb_get_device_list() failed: code %lld",229(long long int) cnt);230return (NULL);231}232233/*234* Scan through USB device list.235*/236for (i = 0; i < cnt; i++) {237dev = list[i];238if (bus_id == libusb_get_bus_number(dev) &&239dev_id == libusb_get_device_address(dev)) {240/* Get the device descriptor for this device entry */241r = libusb_get_device_descriptor(dev, &d);242if (r != 0) {243rtlbt_err("libusb_get_device_descriptor: %s",244libusb_strerror(r));245break;246}247248/* For non-Realtek match on the vendor/product id */249if (rtlbt_is_realtek(&d)) {250/*251* Take a reference so it's not freed later on.252*/253found = libusb_ref_device(dev);254break;255}256/* For Realtek vendor match on the interface class */257if (d.idVendor == 0x0bda && rtlbt_is_bluetooth(dev)) {258/*259* Take a reference so it's not freed later on.260*/261found = libusb_ref_device(dev);262break;263}264}265}266267libusb_free_device_list(list, 1);268return (found);269}270271static void272rtlbt_dump_version(ng_hci_read_local_ver_rp *ver)273{274rtlbt_info("hci_version 0x%02x", ver->hci_version);275rtlbt_info("hci_revision 0x%04x", le16toh(ver->hci_revision));276rtlbt_info("lmp_version 0x%02x", ver->lmp_version);277rtlbt_info("lmp_subversion 0x%04x", le16toh(ver->lmp_subversion));278}279280/*281* Parse ugen name and extract device's bus and address282*/283284static int285parse_ugen_name(char const *ugen, uint8_t *bus, uint8_t *addr)286{287char *ep;288289if (strncmp(ugen, "ugen", 4) != 0)290return (-1);291292*bus = (uint8_t) strtoul(ugen + 4, &ep, 10);293if (*ep != '.')294return (-1);295296*addr = (uint8_t) strtoul(ep + 1, &ep, 10);297if (*ep != '\0')298return (-1);299300return (0);301}302303static void304usage(void)305{306fprintf(stderr,307"Usage: rtlbtfw (-D) -d ugenX.Y (-f firmware path) (-I)\n");308fprintf(stderr, " -D: enable debugging\n");309fprintf(stderr, " -d: device to operate upon\n");310fprintf(stderr, " -f: firmware path, if not default\n");311fprintf(stderr, " -I: enable informational output\n");312exit(127);313}314315int316main(int argc, char *argv[])317{318libusb_context *ctx = NULL;319libusb_device *dev = NULL;320libusb_device_handle *hdl = NULL;321ng_hci_read_local_ver_rp ver;322int r;323uint8_t bus_id = 0, dev_id = 0;324int devid_set = 0;325int n;326char *firmware_dir = NULL;327char *firmware_path = NULL;328char *config_path = NULL;329const char *fw_suffix;330int retcode = 1;331const struct rtlbt_id_table *ic;332uint8_t rom_version;333struct rtlbt_firmware fw, cfg;334enum rtlbt_fw_type fw_type;335uint16_t fw_lmp_subversion;336337/* Parse command line arguments */338while ((n = getopt(argc, argv, "Dd:f:hIm:p:v:")) != -1) {339switch (n) {340case 'd': /* ugen device name */341devid_set = 1;342if (parse_ugen_name(optarg, &bus_id, &dev_id) < 0)343usage();344break;345case 'D':346rtlbt_do_debug = 1;347break;348case 'f': /* firmware dir */349if (firmware_dir)350free(firmware_dir);351firmware_dir = strdup(optarg);352break;353case 'I':354rtlbt_do_info = 1;355break;356case 'h':357default:358usage();359break;360/* NOT REACHED */361}362}363364/* Ensure the devid was given! */365if (devid_set == 0) {366usage();367/* NOTREACHED */368}369370/* libusb setup */371r = libusb_init(&ctx);372if (r != 0) {373rtlbt_err("libusb_init failed: code %d", r);374exit(127);375}376377rtlbt_debug("opening dev %d.%d", (int) bus_id, (int) dev_id);378379/* Find a device based on the bus/dev id */380dev = rtlbt_find_device(ctx, bus_id, dev_id);381if (dev == NULL) {382rtlbt_err("device not found");383goto shutdown;384}385386/* XXX enforce that bInterfaceNumber is 0 */387388/* XXX enforce the device/product id if they're non-zero */389390/* Grab device handle */391r = libusb_open(dev, &hdl);392if (r != 0) {393rtlbt_err("libusb_open() failed: code %d", r);394goto shutdown;395}396397/* Check if ng_ubt is attached */398r = libusb_kernel_driver_active(hdl, 0);399if (r < 0) {400rtlbt_err("libusb_kernel_driver_active() failed: code %d", r);401goto shutdown;402}403if (r > 0) {404rtlbt_info("Firmware has already been downloaded");405retcode = 0;406goto shutdown;407}408409/* Get local version */410r = rtlbt_read_local_ver(hdl, &ver);411if (r < 0) {412rtlbt_err("rtlbt_read_local_ver() failed code %d", r);413goto shutdown;414}415rtlbt_dump_version(&ver);416417ic = rtlbt_get_ic(ver.lmp_subversion, ver.hci_revision,418ver.hci_version);419if (ic == NULL) {420rtlbt_err("rtlbt_get_ic() failed: Unknown IC");421goto shutdown;422}423424/* Default the firmware path */425if (firmware_dir == NULL)426firmware_dir = strdup(_DEFAULT_RTLBT_FIRMWARE_PATH);427428fw_suffix = ic->fw_suffix == NULL ? "_fw.bin" : ic->fw_suffix;429firmware_path = rtlbt_get_fwname(ic->fw_name, firmware_dir, fw_suffix);430if (firmware_path == NULL)431goto shutdown;432433rtlbt_debug("firmware_path = %s", firmware_path);434435rtlbt_info("loading firmware %s", firmware_path);436437/* Read in the firmware */438if (rtlbt_fw_read(&fw, firmware_path) <= 0) {439rtlbt_debug("rtlbt_fw_read() failed");440return (-1);441}442443fw_type = rtlbt_get_fw_type(&fw, &fw_lmp_subversion);444if (fw_type == RTLBT_FW_TYPE_UNKNOWN &&445(ic->flags & RTLBT_IC_FLAG_SIMPLE) == 0) {446rtlbt_debug("Unknown firmware type");447goto shutdown;448}449450if (fw_type != RTLBT_FW_TYPE_UNKNOWN) {451452/* Match hardware and firmware lmp_subversion */453if (fw_lmp_subversion != ver.lmp_subversion) {454rtlbt_err("firmware is for %x but this is a %x",455fw_lmp_subversion, ver.lmp_subversion);456goto shutdown;457}458459/* Query a ROM version */460r = rtlbt_read_rom_ver(hdl, &rom_version);461if (r < 0) {462rtlbt_err("rtlbt_read_rom_ver() failed code %d", r);463goto shutdown;464}465rtlbt_debug("rom_version = %d", rom_version);466467/* Load in the firmware */468if (fw_type == RTLBT_FW_TYPE_V2) {469uint8_t key_id, reg_val[2];470r = rtlbt_read_reg16(hdl, RTLBT_SEC_PROJ, reg_val);471if (r < 0) {472rtlbt_err("rtlbt_read_reg16() failed code %d", r);473goto shutdown;474}475key_id = reg_val[0];476rtlbt_debug("key_id = %d", key_id);477r = rtlbt_parse_fwfile_v2(&fw, rom_version, key_id);478} else479r = rtlbt_parse_fwfile_v1(&fw, rom_version);480if (r < 0) {481rtlbt_err("Parsing firmware file failed");482goto shutdown;483}484485config_path = rtlbt_get_fwname(ic->fw_name, firmware_dir,486"_config.bin");487if (config_path == NULL)488goto shutdown;489490rtlbt_info("loading config %s", config_path);491492/* Read in the config file */493if (rtlbt_fw_read(&cfg, config_path) <= 0) {494rtlbt_err("rtlbt_fw_read() failed");495if ((ic->flags & RTLBT_IC_FLAG_CONFIG) != 0)496goto shutdown;497} else {498r = rtlbt_append_fwfile(&fw, &cfg);499rtlbt_fw_free(&cfg);500if (r < 0) {501rtlbt_err("Appending config file failed");502goto shutdown;503}504}505}506507r = rtlbt_load_fwfile(hdl, &fw);508if (r < 0) {509rtlbt_debug("Loading firmware file failed");510goto shutdown;511}512513/* free it */514rtlbt_fw_free(&fw);515516rtlbt_info("Firmware download complete");517518/* Execute Read Local Version one more time */519r = rtlbt_read_local_ver(hdl, &ver);520if (r < 0) {521rtlbt_err("rtlbt_read_local_ver() failed code %d", r);522goto shutdown;523}524rtlbt_dump_version(&ver);525526retcode = 0;527528/* Ask kernel driver to probe and attach device again */529r = libusb_reset_device(hdl);530if (r != 0)531rtlbt_err("libusb_reset_device() failed: %s",532libusb_strerror(r));533534shutdown:535536/* Shutdown */537538if (hdl != NULL)539libusb_close(hdl);540541if (dev != NULL)542libusb_unref_device(dev);543544if (ctx != NULL)545libusb_exit(ctx);546547if (retcode == 0)548rtlbt_info("Firmware download is successful!");549else550rtlbt_err("Firmware download failed!");551552return (retcode);553}554555556