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>