The branch stable/14 has been updated by wulf:

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

commit 08ad5c42343b3ac15fa4169ac801e52e2de87135
Author:     Vladimir Kondratyev <w...@freebsd.org>
AuthorDate: 2025-05-14 01:09:40 +0000
Commit:     Vladimir Kondratyev <w...@freebsd.org>
CommitDate: 2025-07-29 22:11:43 +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
    
    (cherry picked from commit b87a926098b291e2baf45ffc13c076ba0b0f0d74)
---
 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);
 

Reply via email to