Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 14 May 2025 01:11:33 GMT
From:      Vladimir Kondratyev <wulf@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: b87a926098b2 - main - rtlbtfw(8): Add support for firmware file format V2
Message-ID:  <202505140111.54E1BXp6076602@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=b87a926098b291e2baf45ffc13c076ba0b0f0d74

commit b87a926098b291e2baf45ffc13c076ba0b0f0d74
Author:     Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2025-05-14 01:09:40 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2025-05-14 01:09:40 +0000

    rtlbtfw(8): Add support for firmware file format V2
    
    As Realtek changed format of the firmware files for recent adaptors.
    
    Sponsored by:   Future Crew, LLC
    MFC after:      1 month
    Differential Revision:  https://reviews.freebsd.org/D50082
---
 usr.sbin/bluetooth/rtlbtfw/main.c     |  19 +++-
 usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c | 195 ++++++++++++++++++++++++++++++++--
 usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h |  53 ++++++++-
 usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c |  35 ++++++
 usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h |  13 +++
 5 files changed, 302 insertions(+), 13 deletions(-)

diff --git a/usr.sbin/bluetooth/rtlbtfw/main.c b/usr.sbin/bluetooth/rtlbtfw/main.c
index e0445726c3a2..700b9b43bafa 100644
--- a/usr.sbin/bluetooth/rtlbtfw/main.c
+++ b/usr.sbin/bluetooth/rtlbtfw/main.c
@@ -76,7 +76,6 @@ static struct rtlbt_devid rtlbt_list[] = {
 	{ .vendor_id = 0x04ca, .product_id = 0x4006 },
 	{ .vendor_id = 0x0cb8, .product_id = 0xc549 },
 
-#ifdef RTLBTFW_SUPPORTS_FW_V2
 	/* Realtek 8852CE Bluetooth devices */
 	{ .vendor_id = 0x04ca, .product_id = 0x4007 },
 	{ .vendor_id = 0x04c5, .product_id = 0x1675 },
@@ -84,7 +83,6 @@ static struct rtlbt_devid rtlbt_list[] = {
 	{ .vendor_id = 0x13d3, .product_id = 0x3587 },
 	{ .vendor_id = 0x13d3, .product_id = 0x3586 },
 	{ .vendor_id = 0x13d3, .product_id = 0x3592 },
-#endif
 
 	/* Realtek 8852BE Bluetooth devices */
 	{ .vendor_id = 0x0cb8, .product_id = 0xc559 },
@@ -312,6 +310,7 @@ main(int argc, char *argv[])
 	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;
@@ -410,7 +409,8 @@ main(int argc, char *argv[])
 	if (firmware_dir == NULL)
 		firmware_dir = strdup(_DEFAULT_RTLBT_FIRMWARE_PATH);
 
-	firmware_path = rtlbt_get_fwname(ic->fw_name, firmware_dir, "_fw.bin");
+	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;
 
@@ -449,7 +449,18 @@ main(int argc, char *argv[])
 		rtlbt_debug("rom_version = %d", rom_version);
 
 		/* Load in the firmware */
-		r = rtlbt_parse_fwfile_v1(&fw, rom_version);
+		if (fw_type == RTLBT_FW_TYPE_V2) {
+			uint8_t key_id, reg_val[2];
+			r = rtlbt_read_reg16(hdl, RTLBT_SEC_PROJ, reg_val);
+			if (r < 0) {
+				rtlbt_err("rtlbt_read_reg16() failed code %d", r);
+				goto shutdown;
+			}
+			key_id = reg_val[0];
+			rtlbt_debug("key_id = %d", key_id);
+			r = rtlbt_parse_fwfile_v2(&fw, rom_version, key_id);
+		} else
+			r = rtlbt_parse_fwfile_v1(&fw, rom_version);
 		if (r < 0) {
 			rtlbt_err("Parseing firmware file failed");
 			goto shutdown;
diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c
index bb3d20d79527..c58a8feb41a4 100644
--- a/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c
+++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.c
@@ -105,20 +105,19 @@ static const struct rtlbt_id_table rtlbt_ic_id_table[] = {
 	    .hci_version = 0xb,
 	    .flags = RTLBT_IC_FLAG_MSFT,
 	    .fw_name = "rtl8852bu",
-#ifdef RTLBTFW_SUPPORTS_FW_V2
 	}, { /* 8852C */
 	    .lmp_subversion = RTLBT_ROM_LMP_8852A,
 	    .hci_revision = 0xc,
 	    .hci_version = 0xc,
 	    .flags = RTLBT_IC_FLAG_MSFT,
 	    .fw_name  = "rtl8852cu",
+	    .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",
-#endif
 	},
 };
 
@@ -144,10 +143,8 @@ static const uint16_t project_ids[] = {
 /* Signatures */
 static const uint8_t fw_header_sig_v1[8] =
     {0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68};	/* Realtech */
-#ifdef RTLBTFW_SUPPORTS_FW_V2
 static const uint8_t fw_header_sig_v2[8] =
     {0x52, 0x54, 0x42, 0x54, 0x43, 0x6F, 0x72, 0x65};	/* RTBTCore */
-#endif
 static const uint8_t fw_ext_sig[4] = {0x51, 0x04, 0xFD, 0x77};
 
 int
@@ -260,12 +257,10 @@ rtlbt_get_fw_type(struct rtlbt_firmware *fw, uint16_t *fw_lmp_subversion)
 		fw_type = RTLBT_FW_TYPE_V1;
 		fw_header_len = sizeof(struct rtlbt_fw_header_v1);
 	} else
-#ifdef RTLBTFW_SUPPORTS_FW_V2
 	if (memcmp(fw->buf, fw_header_sig_v2, sizeof(fw_header_sig_v2)) == 0) {
 		fw_type = RTLBT_FW_TYPE_V2;
 		fw_header_len = sizeof(struct rtlbt_fw_header_v2);
 	} else
-#endif
 		return (RTLBT_FW_TYPE_UNKNOWN);
 
 	if (fw->len < fw_header_len + sizeof(fw_ext_sig) + 4) {
@@ -367,6 +362,194 @@ rtlbt_parse_fwfile_v1(struct rtlbt_firmware *fw, uint8_t rom_version)
 	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)
 {
diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h
index 340abacba759..48b30cb2289b 100644
--- a/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h
+++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_fw.h
@@ -30,7 +30,8 @@
 #ifndef	__RTLBT_FW_H__
 #define	__RTLBT_FW_H__
 
-#include <stdbool.h>
+#include <sys/queue.h>
+#include <sys/queue_mergesort.h>
 
 #define	RTLBT_ROM_LMP_8703B	0x8703
 #define	RTLBT_ROM_LMP_8723A	0x1200
@@ -41,12 +42,14 @@
 #define	RTLBT_ROM_LMP_8852A	0x8852
 #define	RTLBT_ROM_LMP_8851B	0x8851
 
+#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,
-#ifdef RTLBTFW_SUPPORTS_FW_V2
 	RTLBT_FW_TYPE_V2,
-#endif
 };
 
 struct rtlbt_id_table {
@@ -58,6 +61,7 @@ struct rtlbt_id_table {
 #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 {
@@ -66,6 +70,21 @@ struct rtlbt_firmware {
 	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;
@@ -78,6 +97,32 @@ struct rtlbt_fw_header_v2 {
 	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,
@@ -87,6 +132,8 @@ const struct rtlbt_id_table *rtlbt_get_ic(uint16_t lmp_subversion,
 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
index 21f2c3e2804f..82e22d406ea9 100644
--- a/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c
+++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.c
@@ -178,6 +178,41 @@ rtlbt_read_rom_ver(struct libusb_device_handle *hdl, uint8_t *ver)
 	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)
diff --git a/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h b/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h
index bc7c9fee3f57..a7200a440272 100644
--- a/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h
+++ b/usr.sbin/bluetooth/rtlbtfw/rtlbt_hw.h
@@ -95,9 +95,22 @@ struct rtlbt_hci_dl_rp {
         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);
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202505140111.54E1BXp6076602>