diff options
Diffstat (limited to 'usr.sbin/bluetooth/rtlbtfw')
| -rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/Makefile | 9 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/main.c | 554 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbt_dbg.h | 46 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c | 582 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h | 140 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c | 270 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h | 117 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbtfw.8 | 99 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf | 433 | 
9 files changed, 2250 insertions, 0 deletions
| 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..58503b8087b5 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/main.c @@ -0,0 +1,554 @@ +/*- + * 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 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("Parsing 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..d7e9f2f939c6 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c @@ -0,0 +1,582 @@ +/*- + * 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", +	}, { /* 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 new file mode 100644 index 000000000000..e9af6c93950e --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h @@ -0,0 +1,140 @@ +/*- + * 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 <sys/queue.h> +#include <sys/queue_mergesort.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 +#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/rtlbt_hw.c b/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c new file mode 100644 index 000000000000..82e22d406ea9 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c @@ -0,0 +1,270 @@ +/*- + * 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_read_reg16(struct libusb_device_handle *hdl, +    struct rtlbt_vendor_cmd *vcmd, uint8_t *resp) +{ +	int ret, transferred; +	struct rtlbt_hci_event_cmd_compl *event; +	uint8_t cmd_buf[offsetof(struct rtlbt_hci_cmd, data) + sizeof(*vcmd)]; +	struct rtlbt_hci_cmd *cmd = (struct rtlbt_hci_cmd *)cmd_buf; +	cmd->opcode = htole16(0xfc61); +	cmd->length = sizeof(struct rtlbt_vendor_cmd); +	memcpy(cmd->data, vcmd, sizeof(struct rtlbt_vendor_cmd)); +	uint8_t buf[RTLBT_HCI_EVT_COMPL_SIZE(struct rtlbt_vendor_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 reg16: code=%d, size=%d", +		     ret, +		     transferred); +		 return (-1); +	} + +	event = (struct rtlbt_hci_event_cmd_compl *)buf; +	memcpy(resp, &(((struct rtlbt_vendor_rp *)event->data)->data), 2); + +	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, j; +	int ret, transferred; + +	for (i = 0, j = 0; i < frag_num; i++, j++) { + +		rtlbt_debug("download fw (%d/%d)", i + 1, frag_num); + +		memset(cmd_buf, 0, sizeof(cmd_buf)); +		cmd->opcode = htole16(0xfc20); +		if (j > 0x7f) +			j = 1; +		dl_cmd->index = j; + +		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..a7200a440272 --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h @@ -0,0 +1,117 @@ +/*- + * 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)); + +/* Vendor USB request payload */ +struct rtlbt_vendor_cmd { +	uint8_t data[5]; +}  __attribute__ ((packed)); +#define	RTLBT_SEC_PROJ	(&(struct rtlbt_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x00, 0xb0}}) + +struct rtlbt_vendor_rp { +	uint8_t status; +	uint8_t data[2]; +}; + +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_read_reg16(struct libusb_device_handle *hdl, +	    struct rtlbt_vendor_cmd *cmd, uint8_t *resp); +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..5cae9c9d288d --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.8 @@ -0,0 +1,99 @@ +.\" 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 15, 2025 +.Dt RTLBTFW 8 +.Os +.Sh NAME +.Nm rtlbtfw +.Nd load firmware for Realtek 87XX/88XX 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..2ef56d2af93a --- /dev/null +++ b/usr.sbin/bluetooth/rtlbtfw/rtlbtfw.conf @@ -0,0 +1,433 @@ +# +# 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 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"		"(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"		"(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"; +}; | 
