All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 1/2] Bluetooth: btrtl: Firmware format v3 support
@ 2025-07-08 12:45 Hilda Wu
  2025-07-08 12:45 ` [PATCH v3 2/2] Bluetooth: btrtl: Add enhanced download support Hilda Wu
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Hilda Wu @ 2025-07-08 12:45 UTC (permalink / raw)
  To: marcel; +Cc: luiz.dentz, linux-bluetooth, linux-kernel, alex_lu, max.chou

Realtek changed the format of the firmware file as v3. The driver
should implement the patch to extract the firmware data from the
firmware file. The future chips must apply this patch for firmware loading.
This patch is compatible with the both previous format, v2 and v3 as well.

Signed-off-by: Alex Lu <alex_lu@realsil.com.cn>
Signed-off-by: Hilda Wu <hildawu@realtek.com>
---
Change in V3:
- Fixed cocci warning

Change in V2:
- Fill in the missing symbols
- Fix build warnings
---
---
 drivers/bluetooth/btrtl.c | 669 +++++++++++++++++++++++++++++++++++++-
 drivers/bluetooth/btrtl.h | 102 ++++++
 drivers/bluetooth/btusb.c |   3 +
 3 files changed, 766 insertions(+), 8 deletions(-)

diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 7838c89e529e..af28f5355aa1 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -22,6 +22,12 @@
 #define RTL_CHIP_8723CS_XX	5
 #define RTL_EPATCH_SIGNATURE	"Realtech"
 #define RTL_EPATCH_SIGNATURE_V2	"RTBTCore"
+#define RTL_EPATCH_SIGNATURE_V3	"BTNIC003"
+#define RTL_PATCH_V3_1			0x01
+#define RTL_PATCH_V3_PATCH_IMAGE	0x02
+#define IMAGE_ID_F000		0xf000
+#define IMAGE_ID_F001		0xf001
+#define IMAGE_ID_F002		0xf002
 #define RTL_ROM_LMP_8703B	0x8703
 #define RTL_ROM_LMP_8723A	0x1200
 #define RTL_ROM_LMP_8723B	0x8723
@@ -72,6 +78,7 @@ enum btrtl_chip_id {
 	CHIP_ID_8851B = 36,
 	CHIP_ID_8922A = 44,
 	CHIP_ID_8852BT = 47,
+	CHIP_ID_8922D = 55,
 };
 
 struct id_table {
@@ -98,8 +105,11 @@ struct btrtl_device_info {
 	int cfg_len;
 	bool drop_fw;
 	int project_id;
+	u32 opcode;
+	u8 fw_type;
 	u8 key_id;
 	struct list_head patch_subsecs;
+	struct list_head patch_images;
 };
 
 static const struct id_table ic_id_table[] = {
@@ -328,6 +338,15 @@ static const struct id_table ic_id_table[] = {
 	  .fw_name  = "rtl_bt/rtl8852btu_fw",
 	  .cfg_name = "rtl_bt/rtl8852btu_config",
 	  .hw_info  = "rtl8852btu" },
+
+	/* 8922DU */
+	{ IC_INFO(RTL_ROM_LMP_8922A, 0xd, 0xe, HCI_USB),
+	  .config_needed = false,
+	  .has_rom_version = true,
+	  .has_msft_ext = true,
+	  .fw_name  = "rtl_bt/rtl8922du_fw",
+	  .cfg_name = "rtl_bt/rtl8922du_config",
+	  .hw_info  = "rtl8922du" },
 	};
 
 static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
@@ -361,6 +380,33 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
 	return &ic_id_table[i];
 }
 
+static int btrtl_read_chip_id(struct hci_dev *hdev, u8 *chip_id)
+{
+	struct rtl_rp_read_chip_id *rp;
+	struct sk_buff *skb;
+
+	/* Read RTL chip id command */
+	skb = __hci_cmd_sync(hdev, 0xfc6f, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	if (skb->len != sizeof(*rp)) {
+		rtl_dev_err(hdev, "read chip id event length mismatch");
+		kfree_skb(skb);
+		return -EIO;
+	}
+
+	rp = (struct rtl_rp_read_chip_id *)skb->data;
+	rtl_dev_info(hdev, "chip_id status=0x%02x id=0x%02x",
+		     rp->status, rp->chip_id);
+
+	if (chip_id)
+		*chip_id = rp->chip_id;
+
+	kfree_skb(skb);
+	return 0;
+}
+
 static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
 {
 	struct sk_buff *skb;
@@ -439,6 +485,26 @@ static int btrtl_vendor_read_reg16(struct hci_dev *hdev,
 	return 0;
 }
 
+static int btrtl_vendor_write_mem(struct hci_dev *hdev, u32 addr, u32 val)
+{
+	struct rtl_vendor_write_cmd cp;
+	struct sk_buff *skb;
+	int err = 0;
+
+	cp.type = 0x21;
+	cp.addr = cpu_to_le32(addr);
+	cp.val = cpu_to_le32(val);
+	skb = __hci_cmd_sync(hdev, 0xfc62, sizeof(cp), &cp, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		bt_dev_err(hdev, "RTL: Write mem32 failed (%d)", err);
+		return err;
+	}
+
+	kfree_skb(skb);
+	return 0;
+}
+
 static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
 {
 	void *data = iov->data;
@@ -452,6 +518,30 @@ static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
 	return data;
 }
 
+static void btrtl_insert_ordered_patch_image(struct rtl_section_patch_image *image,
+					     struct btrtl_device_info *btrtl_dev)
+{
+	struct list_head *pos;
+	struct list_head *next;
+	struct rtl_section_patch_image *node;
+
+	list_for_each_safe(pos, next, &btrtl_dev->patch_images) {
+		node = list_entry(pos, struct rtl_section_patch_image, list);
+
+		if (node->image_id > image->image_id) {
+			__list_add(&image->list, pos->prev, pos);
+			return;
+		}
+
+		if (node->image_id == image->image_id &&
+		    node->index > image->index) {
+			__list_add(&image->list, pos->prev, pos);
+			return;
+		}
+	}
+	__list_add(&image->list, pos->prev, pos);
+}
+
 static void btrtl_insert_ordered_subsec(struct rtl_subsection *node,
 					struct btrtl_device_info *btrtl_dev)
 {
@@ -629,6 +719,295 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
 		return -EPERM;
 
 	*_buf = ptr;
+	btrtl_dev->fw_type = FW_TYPE_V2;
+	return len;
+}
+
+static int rtlbt_parse_config(struct hci_dev *hdev,
+			      struct rtl_section_patch_image *patch_image,
+			      struct btrtl_device_info *btrtl_dev)
+{
+	const struct id_table *ic_info = NULL;
+	const struct firmware *fw;
+	char tmp_name[32];
+	char filename[64];
+	u8 *cfg_buf;
+	char *str;
+	char *p;
+	int len;
+	int ret;
+
+	if (btrtl_dev && btrtl_dev->ic_info)
+		ic_info = btrtl_dev->ic_info;
+
+	if (!ic_info)
+		return -EINVAL;
+
+	str = ic_info->cfg_name;
+	if (btrtl_dev->fw_type == FW_TYPE_V3_1) {
+		if (!patch_image->image_id && !patch_image->index) {
+			snprintf(filename, sizeof(filename), "%s.bin", str);
+			goto load_fw;
+		}
+		goto done;
+	}
+
+	len = strlen(str);
+	if (len > sizeof(tmp_name) - 1)
+		len = sizeof(tmp_name) - 1;
+	memcpy(tmp_name, str, len);
+	tmp_name[len] = '\0';
+
+	str = tmp_name;
+	p = strsep(&str, ".");
+
+	ret = snprintf(filename, sizeof(filename), "%s", p);
+	if (patch_image->config_rule && patch_image->need_config) {
+		switch (patch_image->image_id) {
+		case IMAGE_ID_F000:
+		case IMAGE_ID_F001:
+		case IMAGE_ID_F002:
+			ret += snprintf(filename + ret, sizeof(filename) - ret,
+					"_%04x", patch_image->image_id);
+			break;
+		default:
+			goto done;
+		}
+	} else {
+		goto done;
+	}
+
+	if (str)
+		snprintf(filename + ret, sizeof(filename) - ret, ".%s", str);
+	else
+		snprintf(filename + ret, sizeof(filename) - ret, ".bin");
+load_fw:
+	rtl_dev_info(hdev, "config file: %s", filename);
+	ret = request_firmware(&fw, filename, &hdev->dev);
+	if (ret < 0) {
+		rtl_dev_err(hdev, "request_firmware [%s] error", filename);
+		if (btrtl_dev->fw_type == FW_TYPE_V3_2) {
+			len = 4;
+			cfg_buf = kvmalloc(len, GFP_KERNEL);
+			if (!cfg_buf)
+				return -ENOMEM;
+
+			memset(cfg_buf, 0xff, len);
+			patch_image->cfg_buf = cfg_buf;
+			patch_image->cfg_len = len;
+			return 0;
+		}
+		goto err_req_fw;
+	}
+	cfg_buf = kvmalloc(fw->size, GFP_KERNEL);
+	if (!cfg_buf) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	memcpy(cfg_buf, fw->data, fw->size);
+	len = fw->size;
+	release_firmware(fw);
+
+	patch_image->cfg_buf = cfg_buf;
+	patch_image->cfg_len = len;
+done:
+	return 0;
+err:
+	release_firmware(fw);
+err_req_fw:
+	return ret;
+}
+
+static int rtlbt_parse_section_v3(struct hci_dev *hdev,
+				  struct btrtl_device_info *btrtl_dev,
+				  u32 opcode, u8 *data, u32 len)
+{
+	struct rtl_section_patch_image *patch_image;
+	struct rtl_patch_image_hdr *hdr;
+	u16 image_id;
+	u16 chip_id;
+	u32 patch_image_len;
+	u8 *ptr;
+	int ret = 0;
+	u8 i;
+	struct rtl_iovec iov = {
+		.data = data,
+		.len  = len,
+	};
+
+	hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
+	if (!hdr)
+		return -EINVAL;
+
+	if (btrtl_dev->opcode && btrtl_dev->opcode != opcode) {
+		rtl_dev_err(hdev, "invalid opcode 0x%02x", opcode);
+		return -EINVAL;
+	}
+
+	if (!btrtl_dev->opcode) {
+		btrtl_dev->opcode = opcode;
+		switch (btrtl_dev->opcode) {
+		case RTL_PATCH_V3_1:
+			btrtl_dev->fw_type = FW_TYPE_V3_1;
+			break;
+		case RTL_PATCH_V3_PATCH_IMAGE:
+			btrtl_dev->fw_type = FW_TYPE_V3_2;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	patch_image_len = (u32)le64_to_cpu(hdr->patch_image_len);
+	chip_id = le16_to_cpu(hdr->chip_id);
+	image_id = le16_to_cpu(hdr->image_id);
+	rtl_dev_info(hdev, "image (%04x:%02x), chip id %u, cut 0x%02x, len %08x"
+		     , image_id, hdr->index, chip_id, hdr->ic_cut,
+		     patch_image_len);
+
+	if (btrtl_dev->key_id && btrtl_dev->key_id != hdr->key_id) {
+		rtl_dev_err(hdev, "invalid key_id (%u, %u)", hdr->key_id,
+			    btrtl_dev->key_id);
+		return -EINVAL;
+	}
+
+	if (hdr->ic_cut != btrtl_dev->rom_version + 1) {
+		rtl_dev_info(hdev, "unused ic_cut (%u, %u)", hdr->ic_cut,
+			    btrtl_dev->rom_version + 1);
+		return -EINVAL;
+	}
+
+	if (btrtl_dev->fw_type == FW_TYPE_V3_1 && !btrtl_dev->project_id)
+		btrtl_dev->project_id = chip_id;
+
+	if (btrtl_dev->fw_type == FW_TYPE_V3_2 &&
+	    chip_id != btrtl_dev->project_id) {
+		rtl_dev_err(hdev, "invalid chip_id (%u, %d)", chip_id,
+			    btrtl_dev->project_id);
+		return -EINVAL;
+	}
+
+	ptr = rtl_iov_pull_data(&iov, patch_image_len);
+	if (!ptr)
+		return -ENODATA;
+
+	patch_image = kzalloc(sizeof(*patch_image), GFP_KERNEL);
+	if (!patch_image)
+		return -ENOMEM;
+	patch_image->index = hdr->index;
+	patch_image->image_id = image_id;
+	patch_image->config_rule = hdr->config_rule;
+	patch_image->need_config = hdr->need_config;
+
+	for (i = 0; i < DL_FIX_ADDR_MAX; i++) {
+		patch_image->fix[i].addr =
+			(u32)le64_to_cpu(hdr->addr_fix[i * 2]);
+		patch_image->fix[i].value =
+			(u32)le64_to_cpu(hdr->addr_fix[i * 2 + 1]);
+	}
+
+	patch_image->image_len = patch_image_len;
+	patch_image->image_data = kvmalloc(patch_image_len, GFP_KERNEL);
+	if (!patch_image->image_data) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	memcpy(patch_image->image_data, ptr, patch_image_len);
+	patch_image->image_ver =
+		get_unaligned_le32(ptr + patch_image->image_len - 4);
+	rtl_dev_info(hdev, "image version: %08x", patch_image->image_ver);
+
+	rtlbt_parse_config(hdev, patch_image, btrtl_dev);
+
+	ret = patch_image->image_len;
+
+	btrtl_insert_ordered_patch_image(patch_image, btrtl_dev);
+
+	return ret;
+err:
+	kfree(patch_image);
+	return ret;
+}
+
+static int rtlbt_parse_firmware_v3(struct hci_dev *hdev,
+				   struct btrtl_device_info *btrtl_dev)
+{
+	struct rtl_epatch_header_v3 *hdr;
+	int rc;
+	u32 num_sections;
+	struct rtl_section_v3 *section;
+	u32 section_len;
+	u32 opcode;
+	int len = 0;
+	int i;
+	u8 *ptr;
+	struct rtl_iovec iov = {
+		.data = btrtl_dev->fw_data,
+		.len  = btrtl_dev->fw_len,
+	};
+	struct rtl_vendor_cmd cmd_data = { {0x10, 0xa4, 0xad, 0x00, 0xb0} };
+	u8 reg_val[2];
+
+	if (btrtl_dev->project_id >= CHIP_ID_8922D) {
+		/* A0010DA4 */
+		cmd_data.param[2] = 0x0d;
+		cmd_data.param[3] = 0x01;
+		cmd_data.param[4] = 0xa0;
+	}
+
+	rc = btrtl_vendor_read_reg16(hdev, &cmd_data, reg_val);
+	if (rc < 0)
+		return -EIO;
+
+	rtl_dev_info(hdev, "key id %u", reg_val[0]);
+
+	btrtl_dev->key_id = reg_val[0];
+
+	hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
+	if (!hdr)
+		return -EINVAL;
+	num_sections = le32_to_cpu(hdr->num_sections);
+
+	rtl_dev_dbg(hdev, "timpstamp %08x-%08x", *((u32 *)hdr->timestamp),
+		    *((u32 *)(hdr->timestamp + 4)));
+
+	for (i = 0; i < num_sections; i++) {
+		section = rtl_iov_pull_data(&iov, sizeof(*section));
+		if (!section)
+			break;
+
+		section_len = (u32)le64_to_cpu(section->len);
+		opcode = le32_to_cpu(section->opcode);
+
+		rtl_dev_dbg(hdev, "opcode 0x%04x", section->opcode);
+
+		ptr = rtl_iov_pull_data(&iov, section_len);
+		if (!ptr)
+			break;
+
+		rc = 0;
+		switch (opcode) {
+		case RTL_PATCH_V3_1:
+		case RTL_PATCH_V3_PATCH_IMAGE:
+			rc = rtlbt_parse_section_v3(hdev, btrtl_dev, opcode,
+						    ptr, section_len);
+			break;
+		default:
+			rtl_dev_warn(hdev, "Unknown opcode %08x", opcode);
+			break;
+		}
+		if (rc < 0) {
+			rtl_dev_err(hdev, "Parse section (%u) err (%d)",
+				    opcode, rc);
+			continue;
+		}
+		len += rc;
+	}
+
+	rtl_dev_info(hdev, "image payload total len: 0x%08x", len);
+	if (!len)
+		return -ENODATA;
+
 	return len;
 }
 
@@ -673,6 +1052,9 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
 	if (btrtl_dev->fw_len <= 8)
 		return -EINVAL;
 
+	if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V3, 8))
+		return rtlbt_parse_firmware_v3(hdev, btrtl_dev);
+
 	if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8))
 		min_size = sizeof(struct rtl_epatch_header) +
 				sizeof(extension_sig) + 3;
@@ -808,10 +1190,11 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
 	memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
 
 	*_buf = buf;
+	btrtl_dev->fw_type = FW_TYPE_V1;
 	return len;
 }
 
-static int rtl_download_firmware(struct hci_dev *hdev,
+static int rtl_download_firmware(struct hci_dev *hdev, u8 fw_type,
 				 const unsigned char *data, int fw_len)
 {
 	struct rtl_download_cmd *dl_cmd;
@@ -822,6 +1205,13 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 	int j = 0;
 	struct sk_buff *skb;
 	struct hci_rp_read_local_version *rp;
+	u8 dl_rp_len = sizeof(struct rtl_download_response);
+
+	if (is_v3_fw(fw_type)) {
+		j = 1;
+		if (fw_type == FW_TYPE_V3_2)
+			dl_rp_len++;
+	}
 
 	dl_cmd = kmalloc(sizeof(*dl_cmd), GFP_KERNEL);
 	if (!dl_cmd)
@@ -834,7 +1224,7 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 		if (dl_cmd->index == 0x7f)
 			j = 1;
 
-		if (i == (frag_num - 1)) {
+		if (i == (frag_num - 1) && !is_v3_fw(fw_type)) {
 			dl_cmd->index |= 0x80; /* data end */
 			frag_len = fw_len % RTL_FRAG_LEN;
 		}
@@ -852,7 +1242,7 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 			goto out;
 		}
 
-		if (skb->len != sizeof(struct rtl_download_response)) {
+		if (skb->len != dl_rp_len) {
 			rtl_dev_err(hdev, "download fw event length mismatch");
 			kfree_skb(skb);
 			ret = -EIO;
@@ -863,6 +1253,9 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 		data += RTL_FRAG_LEN;
 	}
 
+	if (is_v3_fw(fw_type))
+		goto out;
+
 	skb = btrtl_read_local_version(hdev);
 	if (IS_ERR(skb)) {
 		ret = PTR_ERR(skb);
@@ -880,6 +1273,226 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 	return ret;
 }
 
+static int rtl_check_download_state(struct hci_dev *hdev,
+				    struct btrtl_device_info *btrtl_dev)
+{
+	struct sk_buff *skb;
+	int ret = 0;
+	u8 state;
+
+	skb = __hci_cmd_sync(hdev, 0xfdcf, 0, NULL, HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb)) {
+		rtl_dev_err(hdev, "write tb error %lu", PTR_ERR(skb));
+		return -EIO;
+	}
+
+	/* Other driver might be downloading the combined firmware. */
+	state = skb->data[0];
+	kfree_skb(skb);
+	if (state == 0x03) {
+		btrealtek_set_flag(hdev, REALTEK_DOWNLOADING);
+		ret = btrealtek_wait_on_flag_timeout(hdev, REALTEK_DOWNLOADING,
+						     TASK_INTERRUPTIBLE,
+						     msecs_to_jiffies(5000));
+		if (ret == -EINTR) {
+			bt_dev_err(hdev, "Firmware loading interrupted");
+			return ret;
+		}
+
+		if (ret) {
+			bt_dev_err(hdev, "Firmware loading timeout");
+			return -ETIMEDOUT;
+		}
+
+		ret = -EALREADY;
+	}
+
+	return 0;
+}
+
+static int rtl_finalize_download(struct hci_dev *hdev,
+				 struct btrtl_device_info *btrtl_dev)
+{
+	struct hci_rp_read_local_version *rp_ver;
+	u8 params[2] = { 0x03, 0xb2 };
+	struct sk_buff *skb;
+	u16 opcode;
+	u32 len;
+	int ret;
+
+	opcode = 0xfc8e;
+	len = 2;
+	if (btrtl_dev->opcode == RTL_PATCH_V3_1) {
+		opcode = 0xfc20;
+		params[0] = 0x80;
+		len = 1;
+	}
+	skb = __hci_cmd_sync(hdev, opcode, len, params, HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb)) {
+		rtl_dev_err(hdev, "Watchdog reset err (%ld)", PTR_ERR(skb));
+		return -EIO;
+	}
+	rtl_dev_info(hdev, "Watchdog reset status %02x", skb->data[0]);
+	kfree_skb(skb);
+
+	skb = btrtl_read_local_version(hdev);
+	if (IS_ERR(skb)) {
+		ret = PTR_ERR(skb);
+		rtl_dev_err(hdev, "read local version failed (%d)", ret);
+		return ret;
+	}
+
+	rp_ver = (struct hci_rp_read_local_version *)skb->data;
+	rtl_dev_info(hdev, "fw version 0x%04x%04x",
+		     __le16_to_cpu(rp_ver->hci_rev),
+		     __le16_to_cpu(rp_ver->lmp_subver));
+	kfree_skb(skb);
+
+	return 0;
+}
+
+static int rtl_security_check(struct hci_dev *hdev,
+			      struct btrtl_device_info *btrtl_dev)
+{
+	struct rtl_section_patch_image *tmp = NULL;
+	struct rtl_section_patch_image *image = NULL;
+	u32 val;
+	int ret;
+
+	list_for_each_entry_reverse(tmp, &btrtl_dev->patch_images, list) {
+		/* Check security hdr */
+		if (!tmp->fix[DL_FIX_SEC_HDR_ADDR].value ||
+		    !tmp->fix[DL_FIX_SEC_HDR_ADDR].addr ||
+		    tmp->fix[DL_FIX_SEC_HDR_ADDR].addr == 0xffffffff)
+			continue;
+		rtl_dev_info(hdev, "addr 0x%08x, value 0x%08x",
+			     tmp->fix[DL_FIX_SEC_HDR_ADDR].addr,
+			     tmp->fix[DL_FIX_SEC_HDR_ADDR].value);
+		image = tmp;
+		break;
+	}
+
+	if (!image)
+		return 0;
+
+	rtl_dev_info(hdev, "sec image (%04x:%02x)", image->image_id,
+		     image->index);
+	val = image->fix[DL_FIX_PATCH_ADDR].value + image->image_len -
+					image->fix[DL_FIX_SEC_HDR_ADDR].value;
+	ret = btrtl_vendor_write_mem(hdev, image->fix[DL_FIX_PATCH_ADDR].addr,
+				     val);
+	if (ret) {
+		rtl_dev_err(hdev, "write sec reg failed (%d)", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int rtl_download_firmware_v3(struct hci_dev *hdev,
+				    struct btrtl_device_info *btrtl_dev)
+{
+	struct rtl_section_patch_image *image, *tmp;
+	struct rtl_rp_dl_v3 *rp;
+	struct sk_buff *skb;
+	u8 *fw_data;
+	int fw_len;
+	int ret = 0;
+	u8 i;
+
+	if (btrtl_dev->fw_type == FW_TYPE_V3_2) {
+		ret = rtl_check_download_state(hdev, btrtl_dev);
+		if (ret) {
+			if (ret == -EALREADY)
+				return 0;
+			return ret;
+		}
+	}
+
+	list_for_each_entry_safe(image, tmp, &btrtl_dev->patch_images, list) {
+		rtl_dev_dbg(hdev, "image (%04x:%02x)", image->image_id,
+			    image->index);
+
+		for (i = DL_FIX_CI_ID; i < DL_FIX_ADDR_MAX; i++) {
+			if (!image->fix[i].addr ||
+			    image->fix[i].addr == 0xffffffff) {
+				rtl_dev_dbg(hdev, "no need to write addr %08x",
+					    image->fix[i].addr);
+				continue;
+			}
+			rtl_dev_dbg(hdev, "write addr and val, 0x%08x, 0x%08x",
+				    image->fix[i].addr, image->fix[i].value);
+			if (btrtl_vendor_write_mem(hdev, image->fix[i].addr,
+						   image->fix[i].value)) {
+				rtl_dev_err(hdev, "write reg failed");
+				ret = -EIO;
+				goto done;
+			}
+		}
+
+		fw_len = image->image_len + image->cfg_len;
+		fw_data = kvmalloc(fw_len, GFP_KERNEL);
+		if (!fw_data) {
+			rtl_dev_err(hdev, "Couldn't alloc buf for image data");
+			ret = -ENOMEM;
+			goto done;
+		}
+		memcpy(fw_data, image->image_data, image->image_len);
+		if (image->cfg_len > 0)
+			memcpy(fw_data + image->image_len, image->cfg_buf,
+			       image->cfg_len);
+
+		rtl_dev_dbg(hdev, "patch image (%04x:%02x). len: %d",
+			    image->image_id, image->index, fw_len);
+		rtl_dev_dbg(hdev, "fw_data %p, image buf %p, len %u", fw_data,
+			    image->image_data, image->image_len);
+
+		ret = rtl_download_firmware(hdev, btrtl_dev->fw_type, fw_data,
+					    fw_len);
+		kvfree(fw_data);
+		if (ret < 0) {
+			rtl_dev_err(hdev, "download firmware failed (%d)", ret);
+			goto done;
+		}
+
+		if (image->list.next != &btrtl_dev->patch_images &&
+		    image->image_id == tmp->image_id)
+			continue;
+
+		if (btrtl_dev->fw_type == FW_TYPE_V3_1)
+			continue;
+
+		i = 0x80;
+		skb = __hci_cmd_sync(hdev, 0xfc20, 1, &i, HCI_CMD_TIMEOUT);
+		if (IS_ERR(skb)) {
+			ret = -EIO;
+			rtl_dev_err(hdev, "Failed to issue last cmd fc20, %ld",
+				    PTR_ERR(skb));
+			goto done;
+		}
+		rp = (void *)skb->data;
+		ret = rp->err;
+		kfree_skb(skb);
+		if (ret == 2) {
+			/* Verification failure */
+			ret = -EFAULT;
+			goto done;
+		}
+	}
+
+	if (btrtl_dev->fw_type == FW_TYPE_V3_1) {
+		ret = rtl_security_check(hdev, btrtl_dev);
+		if (ret) {
+			rtl_dev_err(hdev, "Security check failed (%d)", ret);
+			goto done;
+		}
+	}
+
+	ret = rtl_finalize_download(hdev, btrtl_dev);
+
+done:
+	return ret;
+}
+
 static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
 {
 	const struct firmware *fw;
@@ -913,7 +1526,7 @@ static int btrtl_setup_rtl8723a(struct hci_dev *hdev,
 		return -EINVAL;
 	}
 
-	return rtl_download_firmware(hdev, btrtl_dev->fw_data,
+	return rtl_download_firmware(hdev, FW_TYPE_V0, btrtl_dev->fw_data,
 				     btrtl_dev->fw_len);
 }
 
@@ -928,7 +1541,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
 	if (ret < 0)
 		goto out;
 
-	if (btrtl_dev->cfg_len > 0) {
+	if (!is_v3_fw(btrtl_dev->fw_type) && btrtl_dev->cfg_len > 0) {
 		tbuff = kvzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
 		if (!tbuff) {
 			ret = -ENOMEM;
@@ -944,9 +1557,14 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
 		fw_data = tbuff;
 	}
 
+	if (is_v3_fw(btrtl_dev->fw_type)) {
+		ret = rtl_download_firmware_v3(hdev, btrtl_dev);
+		goto out;
+	}
+
 	rtl_dev_info(hdev, "cfg_sz %d, total sz %d", btrtl_dev->cfg_len, ret);
 
-	ret = rtl_download_firmware(hdev, fw_data, ret);
+	ret = rtl_download_firmware(hdev, btrtl_dev->fw_type, fw_data, ret);
 
 out:
 	kvfree(fw_data);
@@ -1042,6 +1660,7 @@ static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type)
 void btrtl_free(struct btrtl_device_info *btrtl_dev)
 {
 	struct rtl_subsection *entry, *tmp;
+	struct rtl_section_patch_image *image, *next;
 
 	kvfree(btrtl_dev->fw_data);
 	kvfree(btrtl_dev->cfg_data);
@@ -1051,6 +1670,13 @@ void btrtl_free(struct btrtl_device_info *btrtl_dev)
 		kfree(entry);
 	}
 
+	list_for_each_entry_safe(image, next, &btrtl_dev->patch_images, list) {
+		list_del(&image->list);
+		kvfree(image->image_data);
+		kvfree(image->cfg_buf);
+		kfree(image);
+	}
+
 	kfree(btrtl_dev);
 }
 EXPORT_SYMBOL_GPL(btrtl_free);
@@ -1058,7 +1684,7 @@ EXPORT_SYMBOL_GPL(btrtl_free);
 struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 					   const char *postfix)
 {
-	struct btrealtek_data *coredump_info = hci_get_priv(hdev);
+	struct btrealtek_data *btrtl_data = hci_get_priv(hdev);
 	struct btrtl_device_info *btrtl_dev;
 	struct sk_buff *skb;
 	struct hci_rp_read_local_version *resp;
@@ -1069,6 +1695,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 	u8 hci_ver, lmp_ver, chip_type = 0;
 	int ret;
 	u8 reg_val[2];
+	u8 chip_id = 0;
 
 	btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
 	if (!btrtl_dev) {
@@ -1077,8 +1704,15 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 	}
 
 	INIT_LIST_HEAD(&btrtl_dev->patch_subsecs);
+	INIT_LIST_HEAD(&btrtl_dev->patch_images);
 
 check_version:
+	ret = btrtl_read_chip_id(hdev, &chip_id);
+	if (!ret && chip_id == CHIP_ID_8922D) {
+		btrtl_dev->project_id = chip_id;
+		goto read_local_ver;
+	}
+
 	ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_SUBVER, reg_val);
 	if (ret < 0)
 		goto err_free;
@@ -1101,6 +1735,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 		}
 	}
 
+read_local_ver:
 	skb = btrtl_read_local_version(hdev);
 	if (IS_ERR(skb)) {
 		ret = PTR_ERR(skb);
@@ -1228,7 +1863,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 		hci_set_msft_opcode(hdev, 0xFCF0);
 
 	if (btrtl_dev->ic_info)
-		coredump_info->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
+		btrtl_data->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
 
 	return btrtl_dev;
 
@@ -1301,6 +1936,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
 	case CHIP_ID_8851B:
 	case CHIP_ID_8922A:
 	case CHIP_ID_8852BT:
+	case CHIP_ID_8922D:
 		set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
 
 		/* RTL8852C needs to transmit mSBC data continuously without
@@ -1387,6 +2023,23 @@ int btrtl_shutdown_realtek(struct hci_dev *hdev)
 }
 EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek);
 
+int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_event_hdr *hdr = (void *)skb->data;
+
+	if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
+	    hdr->plen > 0) {
+		if (skb->data[2] == 0x77 &&
+		    btrealtek_test_and_clear_flag(hdev, REALTEK_DOWNLOADING)) {
+			btrealtek_wake_up_flag(hdev, REALTEK_DOWNLOADING);
+			return 0;
+		}
+	}
+
+	return hci_recv_frame(hdev, skb);
+}
+EXPORT_SYMBOL_GPL(btrtl_recv_event);
+
 static unsigned int btrtl_convert_baudrate(u32 device_baudrate)
 {
 	switch (device_baudrate) {
diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
index a2d9d34f9fb0..f6f03a5fefba 100644
--- a/drivers/bluetooth/btrtl.h
+++ b/drivers/bluetooth/btrtl.h
@@ -12,6 +12,19 @@
 #define rtl_dev_info(dev, fmt, ...) bt_dev_info(dev, "RTL: " fmt, ##__VA_ARGS__)
 #define rtl_dev_dbg(dev, fmt, ...) bt_dev_dbg(dev, "RTL: " fmt, ##__VA_ARGS__)
 
+#define FW_TYPE_V0		0
+#define FW_TYPE_V1		1
+#define FW_TYPE_V2		2
+#define FW_TYPE_V3_1		3
+#define FW_TYPE_V3_2		4
+#define is_v3_fw(type)	(type == FW_TYPE_V3_1 || type == FW_TYPE_V3_2)
+
+#define DL_FIX_CI_ID		0
+#define DL_FIX_CI_ADDR		1
+#define DL_FIX_PATCH_ADDR	2
+#define DL_FIX_SEC_HDR_ADDR	3
+#define DL_FIX_ADDR_MAX		4
+
 struct btrtl_device_info;
 
 struct rtl_chip_type_evt {
@@ -103,8 +116,79 @@ struct rtl_vendor_cmd {
 	__u8 param[5];
 } __packed;
 
+struct rtl_vendor_write_cmd {
+	u8 type;
+	__le32 addr;
+	__le32 val;
+} __packed;
+
+struct rtl_rp_read_chip_id {
+	__u8 status;
+	__u8 chip_id;
+} __packed;
+
+struct rtl_rp_dl_v3 {
+	__u8 status;
+	__u8 index;
+	__u8 err;
+} __packed;
+
+struct rtl_epatch_header_v3 {
+	__u8 signature[8];
+	__u8 timestamp[8];
+	__le32 ver_rsvd;
+	__le32 num_sections;
+} __packed;
+
+struct rtl_section_v3 {
+	__le32 opcode;
+	__le64 len;
+	u8 data[];
+} __packed;
+
+struct rtl_addr_fix {
+	u32 addr;
+	u32 value;
+};
+
+struct rtl_section_patch_image {
+	u16 image_id;
+	u8 index;
+	u8 config_rule;
+	u8 need_config;
+
+	struct rtl_addr_fix fix[DL_FIX_ADDR_MAX];
+
+	u32 image_len;
+	u8 *image_data;
+	u32 image_ver;
+
+	u8  *cfg_buf;
+	u16 cfg_len;
+
+	struct list_head list;
+};
+
+struct rtl_patch_image_hdr {
+	__le16 chip_id;
+	u8 ic_cut;
+	u8 key_id;
+	u8 enable_ota;
+	__le16 image_id;
+	u8 config_rule;
+	u8 need_config;
+	u8 rsv[950];
+
+	__le64 addr_fix[DL_FIX_ADDR_MAX * 2];
+	u8 index;
+
+	__le64 patch_image_len;
+	__u8 data[];
+} __packed;
+
 enum {
 	REALTEK_ALT6_CONTINUOUS_TX_CHIP,
+	REALTEK_DOWNLOADING,
 
 	__REALTEK_NUM_FLAGS,
 };
@@ -130,8 +214,20 @@ struct btrealtek_data {
 #define btrealtek_get_flag(hdev)					\
 	(((struct btrealtek_data *)hci_get_priv(hdev))->flags)
 
+#define btrealtek_wake_up_flag(hdev, nr)				\
+	do {								\
+		struct btrealtek_data *rtl = hci_get_priv((hdev));	\
+		wake_up_bit(rtl->flags, (nr));				\
+	} while (0)
+
 #define btrealtek_test_flag(hdev, nr)	test_bit((nr), btrealtek_get_flag(hdev))
 
+#define btrealtek_test_and_clear_flag(hdev, nr)				\
+	test_and_clear_bit((nr), btrealtek_get_flag(hdev))
+
+#define btrealtek_wait_on_flag_timeout(hdev, nr, m, to)			\
+	wait_on_bit_timeout(btrealtek_get_flag(hdev), (nr), m, to)
+
 #if IS_ENABLED(CONFIG_BT_RTL)
 
 struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
@@ -148,6 +244,7 @@ int btrtl_get_uart_settings(struct hci_dev *hdev,
 			    unsigned int *controller_baudrate,
 			    u32 *device_baudrate, bool *flow_control);
 void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name);
+int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
 
 #else
 
@@ -195,4 +292,9 @@ static inline void btrtl_set_driver_name(struct hci_dev *hdev, const char *drive
 {
 }
 
+static inline int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	return -EOPNOTSUPP;
+}
+
 #endif
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index f8f256ff79a3..a87ea836d730 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -2677,6 +2677,9 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
 		return 0;
 	}
 
+	if (skb->data[0] == HCI_VENDOR_PKT)
+		return btrtl_recv_event(hdev, skb);
+
 	return hci_recv_frame(hdev, skb);
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v3 2/2] Bluetooth: btrtl: Add enhanced download support
  2025-07-08 12:45 [PATCH v3 1/2] Bluetooth: btrtl: Firmware format v3 support Hilda Wu
@ 2025-07-08 12:45 ` Hilda Wu
  2025-07-10 16:10   ` Luiz Augusto von Dentz
  2025-07-08 13:30 ` [v3,1/2] Bluetooth: btrtl: Firmware format v3 support bluez.test.bot
  2025-07-10 15:58 ` [PATCH v3 1/2] " Luiz Augusto von Dentz
  2 siblings, 1 reply; 10+ messages in thread
From: Hilda Wu @ 2025-07-08 12:45 UTC (permalink / raw)
  To: marcel; +Cc: luiz.dentz, linux-bluetooth, linux-kernel, alex_lu, max.chou

Add an enhanced download mode for firmware format v3.
Use ACL to speed up firmware downloads.

Signed-off-by: Alex Lu <alex_lu@realsil.com.cn>
Signed-off-by: Hilda Wu <hildawu@realtek.com>
---
Change in V3:
- Avoiding memory leak

Change in V2:
- Move structure to btrtl.h
- Fix build warnings
---
---
 drivers/bluetooth/btrtl.c | 193 +++++++++++++++++++++++++++++++++++++-
 drivers/bluetooth/btrtl.h |  20 ++++
 2 files changed, 211 insertions(+), 2 deletions(-)

diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index af28f5355aa1..27df5e439e89 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -108,6 +108,8 @@ struct btrtl_device_info {
 	u32 opcode;
 	u8 fw_type;
 	u8 key_id;
+	u16 handle;
+	u16 acldata_pkt_len;
 	struct list_head patch_subsecs;
 	struct list_head patch_images;
 };
@@ -1310,6 +1312,163 @@ static int rtl_check_download_state(struct hci_dev *hdev,
 	return 0;
 }
 
+static int btrtl_enhanced_download_mode_enable(struct hci_dev *hdev,
+					struct btrtl_device_info *btrtl_dev)
+{
+	struct hci_rp_enhanced_download_mode *ev;
+	struct sk_buff *skb;
+	u16 opcode = 0xfc1f;
+	u8 val = 1;
+	int ret = -EINVAL;
+
+	skb = __hci_cmd_sync(hdev, opcode, 1, &val, HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb)) {
+		bt_dev_err(hdev, "send %04x error (%lu)", opcode, PTR_ERR(skb));
+		return -EIO;
+	}
+	if (skb->len != sizeof(*ev)) {
+		bt_dev_err(hdev, "got invalid cmd complete, %u %zu", skb->len,
+			   sizeof(*ev));
+		goto err;
+	}
+	ev = (struct hci_rp_enhanced_download_mode *)skb->data;
+	if (ev->status) {
+		bt_dev_err(hdev, "got invalid status 0x%02x", ev->status);
+		goto err;
+	}
+	btrtl_dev->handle = le16_to_cpu(ev->handle);
+	btrtl_dev->acldata_pkt_len = le16_to_cpu(ev->acldata_pkt_len);
+	kfree_skb(skb);
+
+	bt_dev_info(hdev, "enhanced download mode enabled, handle %04x, acl %u",
+		    btrtl_dev->handle, btrtl_dev->acldata_pkt_len);
+
+	return 0;
+err:
+	kfree_skb(skb);
+	return ret;
+}
+
+static int rtl_acl_download_firmware(struct hci_dev *hdev,
+				     struct btrtl_device_info *btrtl_dev,
+				     const unsigned char *data, int fw_len)
+{
+	struct btrealtek_data *btrtl_data = hci_get_priv(hdev);
+	int frag_num = fw_len / RTL_FRAG_LEN + 1;
+	int frag_len = RTL_FRAG_LEN;
+	int ret = 0;
+	int i;
+	int j = 0;
+	struct sk_buff *skb;
+	struct rtl_acl_download_rp *rp;
+	u16 max_payload_len;
+	struct hci_acl_hdr *hdr;
+	u8 index;
+
+	if (is_v3_fw(btrtl_dev->fw_type))
+		j = 1;
+
+	btrtl_data->dlreq_status = 0;
+	btrtl_data->dlreq_result = 0;
+	btrtl_data->dlreq_rsp = NULL;
+	max_payload_len = (btrtl_dev->acldata_pkt_len - 1) & ~0x3;
+
+	for (i = 0; i < frag_num; i++) {
+		index = j++;
+		if (index == 0x7f)
+			j = 1;
+
+		if (i == (frag_num - 1) && !is_v3_fw(btrtl_dev->fw_type)) {
+			index |= 0x80; /* data end */
+			frag_len = fw_len % max_payload_len;
+		}
+		rtl_dev_dbg(hdev, "acl download fw (%d/%d). index = %d", i,
+			    frag_num, index);
+
+		skb = bt_skb_alloc(sizeof(*hdr) + 1 + frag_len, GFP_KERNEL);
+		if (!skb)
+			return -ENOMEM;
+		hdr = (struct hci_acl_hdr *)skb_put(skb, sizeof(*hdr));
+		hdr->handle = cpu_to_le16(btrtl_dev->handle | 0x8000);
+		hdr->dlen = cpu_to_le16(1 + frag_len);
+		*(u8 *)skb_put(skb, 1) = index;
+		memcpy(skb_put(skb, frag_len), data, frag_len);
+
+		hci_skb_pkt_type(skb) = HCI_ACLDATA_PKT;
+
+		btrtl_data->dlreq_status = HCI_REQ_PEND;
+
+		ret = hdev->send(hdev, skb);
+		if (ret < 0) {
+			bt_dev_err(hdev, "sending frame failed (%d)", ret);
+			goto err;
+		}
+
+		ret = wait_event_interruptible_timeout(btrtl_data->dlreq_wait_q,
+				btrtl_data->dlreq_status != HCI_REQ_PEND,
+				HCI_INIT_TIMEOUT);
+		if (ret == -ERESTARTSYS)
+			goto out;
+
+		switch (btrtl_data->dlreq_status) {
+		case HCI_REQ_DONE:
+			ret = -bt_to_errno(btrtl_data->dlreq_result);
+			break;
+
+		case HCI_REQ_CANCELED:
+			ret = -btrtl_data->dlreq_result;
+			break;
+
+		default:
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		btrtl_data->dlreq_status = 0;
+		btrtl_data->dlreq_result = 0;
+		skb = btrtl_data->dlreq_rsp;
+		btrtl_data->dlreq_rsp = NULL;
+
+		bt_dev_dbg(hdev, "end: err %d", ret);
+
+		if (ret < 0) {
+			bt_dev_err(hdev, "wait on complete err (%d)", ret);
+			goto err;
+		}
+
+		if (!skb)
+			return -ENODATA;
+
+		if (skb->len != sizeof(*rp)) {
+			rtl_dev_err(hdev, "acl download fw event len mismatch");
+			ret = -EIO;
+			goto err;
+		}
+		rp = (struct rtl_acl_download_rp *)skb->data;
+		if ((btrtl_dev->handle & 0xfff) != le16_to_cpu(rp->handle)) {
+			rtl_dev_err(hdev, "handle mismatch (%04x %04x)",
+				    btrtl_dev->handle & 0xfff,
+				    le16_to_cpu(rp->handle));
+			ret = -EINVAL;
+			goto err;
+		}
+		if (index != rp->index) {
+			rtl_dev_err(hdev, "index mismatch (%u, %u)", index,
+				    rp->index);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		kfree_skb(skb);
+		data += frag_len;
+	}
+out:
+	return ret;
+err:
+	kfree_skb(skb);
+	return ret;
+}
+
 static int rtl_finalize_download(struct hci_dev *hdev,
 				 struct btrtl_device_info *btrtl_dev)
 {
@@ -1394,6 +1553,7 @@ static int rtl_download_firmware_v3(struct hci_dev *hdev,
 	struct rtl_section_patch_image *image, *tmp;
 	struct rtl_rp_dl_v3 *rp;
 	struct sk_buff *skb;
+	u8 enh_dl = 0;
 	u8 *fw_data;
 	int fw_len;
 	int ret = 0;
@@ -1408,6 +1568,16 @@ static int rtl_download_firmware_v3(struct hci_dev *hdev,
 		}
 	}
 
+	switch (btrtl_dev->project_id) {
+	case CHIP_ID_8852C:
+	case CHIP_ID_8922D:
+		if (!btrtl_enhanced_download_mode_enable(hdev, btrtl_dev))
+			enh_dl = 1;
+		break;
+	default:
+		break;
+	}
+
 	list_for_each_entry_safe(image, tmp, &btrtl_dev->patch_images, list) {
 		rtl_dev_dbg(hdev, "image (%04x:%02x)", image->image_id,
 			    image->index);
@@ -1446,8 +1616,13 @@ static int rtl_download_firmware_v3(struct hci_dev *hdev,
 		rtl_dev_dbg(hdev, "fw_data %p, image buf %p, len %u", fw_data,
 			    image->image_data, image->image_len);
 
-		ret = rtl_download_firmware(hdev, btrtl_dev->fw_type, fw_data,
-					    fw_len);
+		if (enh_dl)
+			ret = rtl_acl_download_firmware(hdev, btrtl_dev,
+							fw_data, fw_len);
+		else
+			ret = rtl_download_firmware(hdev, btrtl_dev->fw_type,
+						    fw_data, fw_len);
+
 		kvfree(fw_data);
 		if (ret < 0) {
 			rtl_dev_err(hdev, "download firmware failed (%d)", ret);
@@ -1705,6 +1880,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 
 	INIT_LIST_HEAD(&btrtl_dev->patch_subsecs);
 	INIT_LIST_HEAD(&btrtl_dev->patch_images);
+	init_waitqueue_head(&btrtl_data->dlreq_wait_q);
 
 check_version:
 	ret = btrtl_read_chip_id(hdev, &chip_id);
@@ -2025,6 +2201,7 @@ EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek);
 
 int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
 {
+	struct btrealtek_data *btrtl_data = hci_get_priv(hdev);
 	struct hci_event_hdr *hdr = (void *)skb->data;
 
 	if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
@@ -2032,6 +2209,18 @@ int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
 		if (skb->data[2] == 0x77 &&
 		    btrealtek_test_and_clear_flag(hdev, REALTEK_DOWNLOADING)) {
 			btrealtek_wake_up_flag(hdev, REALTEK_DOWNLOADING);
+			/* skb should be free here. */
+			kfree_skb(skb);
+			return 0;
+		} else if (skb->data[2] == 0x2a) {
+			if (btrtl_data->dlreq_status == HCI_REQ_PEND) {
+				btrtl_data->dlreq_result = 0;
+				btrtl_data->dlreq_status = HCI_REQ_DONE;
+				skb_pull(skb, sizeof(*hdr));
+				btrtl_data->dlreq_rsp = skb_get(skb);
+				wake_up_interruptible(&btrtl_data->dlreq_wait_q);
+			}
+			kfree_skb(skb);
 			return 0;
 		}
 	}
diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
index f6f03a5fefba..7f15b30680d7 100644
--- a/drivers/bluetooth/btrtl.h
+++ b/drivers/bluetooth/btrtl.h
@@ -203,8 +203,28 @@ struct btrealtek_data {
 	DECLARE_BITMAP(flags, __REALTEK_NUM_FLAGS);
 
 	struct rtl_dump_info rtl_dump;
+
+	wait_queue_head_t	dlreq_wait_q;
+	__u32                   dlreq_status;
+	__u32                   dlreq_result;
+	struct sk_buff          *dlreq_rsp;
 };
 
+struct rtl_acl_download_rp {
+	__u8 subevent;
+	__u8 index;
+	__le16 handle;
+	__le32 loaded_len;
+} __packed;
+
+struct hci_rp_enhanced_download_mode {
+	__u8 status;
+	__u8 reserved1;
+	__le16 handle;
+	__le16 acldata_pkt_len;
+	__u8 reserved2;
+} __packed;
+
 #define btrealtek_set_flag(hdev, nr)					\
 	do {								\
 		struct btrealtek_data *realtek = hci_get_priv((hdev));	\
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* RE: [v3,1/2] Bluetooth: btrtl: Firmware format v3 support
  2025-07-08 12:45 [PATCH v3 1/2] Bluetooth: btrtl: Firmware format v3 support Hilda Wu
  2025-07-08 12:45 ` [PATCH v3 2/2] Bluetooth: btrtl: Add enhanced download support Hilda Wu
@ 2025-07-08 13:30 ` bluez.test.bot
  2025-07-10 15:58 ` [PATCH v3 1/2] " Luiz Augusto von Dentz
  2 siblings, 0 replies; 10+ messages in thread
From: bluez.test.bot @ 2025-07-08 13:30 UTC (permalink / raw)
  To: linux-bluetooth, hildawu

[-- Attachment #1: Type: text/plain, Size: 2296 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=980045

---Test result---

Test Summary:
CheckPatch                    PENDING   0.34 seconds
GitLint                       PENDING   0.24 seconds
SubjectPrefix                 PASS      0.25 seconds
BuildKernel                   PASS      24.07 seconds
CheckAllWarning               PASS      26.45 seconds
CheckSparse                   PASS      31.10 seconds
BuildKernel32                 PASS      24.17 seconds
TestRunnerSetup               PASS      479.60 seconds
TestRunner_l2cap-tester       PASS      25.91 seconds
TestRunner_iso-tester         PASS      37.12 seconds
TestRunner_bnep-tester        PASS      6.23 seconds
TestRunner_mgmt-tester        FAIL      136.41 seconds
TestRunner_rfcomm-tester      PASS      9.46 seconds
TestRunner_sco-tester         PASS      15.13 seconds
TestRunner_ioctl-tester       PASS      10.40 seconds
TestRunner_mesh-tester        FAIL      11.54 seconds
TestRunner_smp-tester         PASS      8.79 seconds
TestRunner_userchan-tester    PASS      6.36 seconds
IncrementalBuild              PENDING   0.69 seconds

Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:

##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:

##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 490, Passed: 485 (99.0%), Failed: 1, Not Run: 4

Failed Test Cases
LL Privacy - Start Discovery 2 (Disable RL)          Failed       0.233 seconds
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner
Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0

Failed Test Cases
Mesh - Send cancel - 1                               Timed out    1.939 seconds
Mesh - Send cancel - 2                               Timed out    1.995 seconds
##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series
Output:



---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 1/2] Bluetooth: btrtl: Firmware format v3 support
  2025-07-08 12:45 [PATCH v3 1/2] Bluetooth: btrtl: Firmware format v3 support Hilda Wu
  2025-07-08 12:45 ` [PATCH v3 2/2] Bluetooth: btrtl: Add enhanced download support Hilda Wu
  2025-07-08 13:30 ` [v3,1/2] Bluetooth: btrtl: Firmware format v3 support bluez.test.bot
@ 2025-07-10 15:58 ` Luiz Augusto von Dentz
  2026-06-24  5:33   ` [PATCH v5] Bluetooth: btrtl: Add firmware " Hilda Wu
  2 siblings, 1 reply; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2025-07-10 15:58 UTC (permalink / raw)
  To: Hilda Wu; +Cc: marcel, linux-bluetooth, linux-kernel, alex_lu, max.chou

Hi Hilda,

On Tue, Jul 8, 2025 at 8:45 AM Hilda Wu <hildawu@realtek.com> wrote:
>
> Realtek changed the format of the firmware file as v3. The driver
> should implement the patch to extract the firmware data from the
> firmware file. The future chips must apply this patch for firmware loading.
> This patch is compatible with the both previous format, v2 and v3 as well.

Can you please add the expected output, there seems to be a lot of
info being added. Is this really necessary for regular users to see
these messages? Also please review all the access to skb->data without
first checking its boundaries with skb->len, I catch of few of them
but there might be more.

> Signed-off-by: Alex Lu <alex_lu@realsil.com.cn>
> Signed-off-by: Hilda Wu <hildawu@realtek.com>
> ---
> Change in V3:
> - Fixed cocci warning
>
> Change in V2:
> - Fill in the missing symbols
> - Fix build warnings
> ---
> ---
>  drivers/bluetooth/btrtl.c | 669 +++++++++++++++++++++++++++++++++++++-
>  drivers/bluetooth/btrtl.h | 102 ++++++
>  drivers/bluetooth/btusb.c |   3 +
>  3 files changed, 766 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
> index 7838c89e529e..af28f5355aa1 100644
> --- a/drivers/bluetooth/btrtl.c
> +++ b/drivers/bluetooth/btrtl.c
> @@ -22,6 +22,12 @@
>  #define RTL_CHIP_8723CS_XX     5
>  #define RTL_EPATCH_SIGNATURE   "Realtech"
>  #define RTL_EPATCH_SIGNATURE_V2        "RTBTCore"
> +#define RTL_EPATCH_SIGNATURE_V3        "BTNIC003"
> +#define RTL_PATCH_V3_1                 0x01
> +#define RTL_PATCH_V3_PATCH_IMAGE       0x02
> +#define IMAGE_ID_F000          0xf000
> +#define IMAGE_ID_F001          0xf001
> +#define IMAGE_ID_F002          0xf002
>  #define RTL_ROM_LMP_8703B      0x8703
>  #define RTL_ROM_LMP_8723A      0x1200
>  #define RTL_ROM_LMP_8723B      0x8723
> @@ -72,6 +78,7 @@ enum btrtl_chip_id {
>         CHIP_ID_8851B = 36,
>         CHIP_ID_8922A = 44,
>         CHIP_ID_8852BT = 47,
> +       CHIP_ID_8922D = 55,
>  };
>
>  struct id_table {
> @@ -98,8 +105,11 @@ struct btrtl_device_info {
>         int cfg_len;
>         bool drop_fw;
>         int project_id;
> +       u32 opcode;
> +       u8 fw_type;
>         u8 key_id;
>         struct list_head patch_subsecs;
> +       struct list_head patch_images;
>  };
>
>  static const struct id_table ic_id_table[] = {
> @@ -328,6 +338,15 @@ static const struct id_table ic_id_table[] = {
>           .fw_name  = "rtl_bt/rtl8852btu_fw",
>           .cfg_name = "rtl_bt/rtl8852btu_config",
>           .hw_info  = "rtl8852btu" },
> +
> +       /* 8922DU */
> +       { IC_INFO(RTL_ROM_LMP_8922A, 0xd, 0xe, HCI_USB),
> +         .config_needed = false,
> +         .has_rom_version = true,
> +         .has_msft_ext = true,
> +         .fw_name  = "rtl_bt/rtl8922du_fw",
> +         .cfg_name = "rtl_bt/rtl8922du_config",
> +         .hw_info  = "rtl8922du" },
>         };
>
>  static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
> @@ -361,6 +380,33 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
>         return &ic_id_table[i];
>  }
>
> +static int btrtl_read_chip_id(struct hci_dev *hdev, u8 *chip_id)
> +{
> +       struct rtl_rp_read_chip_id *rp;
> +       struct sk_buff *skb;
> +
> +       /* Read RTL chip id command */
> +       skb = __hci_cmd_sync(hdev, 0xfc6f, 0, NULL, HCI_INIT_TIMEOUT);
> +       if (IS_ERR(skb))
> +               return PTR_ERR(skb);
> +
> +       if (skb->len != sizeof(*rp)) {
> +               rtl_dev_err(hdev, "read chip id event length mismatch");
> +               kfree_skb(skb);
> +               return -EIO;
> +       }
> +
> +       rp = (struct rtl_rp_read_chip_id *)skb->data;

You don't need to do checks for the size and casts if you use skb_pull_data.

> +       rtl_dev_info(hdev, "chip_id status=0x%02x id=0x%02x",
> +                    rp->status, rp->chip_id);
> +
> +       if (chip_id)
> +               *chip_id = rp->chip_id;
> +
> +       kfree_skb(skb);
> +       return 0;
> +}
> +
>  static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
>  {
>         struct sk_buff *skb;
> @@ -439,6 +485,26 @@ static int btrtl_vendor_read_reg16(struct hci_dev *hdev,
>         return 0;
>  }
>
> +static int btrtl_vendor_write_mem(struct hci_dev *hdev, u32 addr, u32 val)
> +{
> +       struct rtl_vendor_write_cmd cp;
> +       struct sk_buff *skb;
> +       int err = 0;
> +
> +       cp.type = 0x21;
> +       cp.addr = cpu_to_le32(addr);
> +       cp.val = cpu_to_le32(val);
> +       skb = __hci_cmd_sync(hdev, 0xfc62, sizeof(cp), &cp, HCI_INIT_TIMEOUT);
> +       if (IS_ERR(skb)) {
> +               err = PTR_ERR(skb);
> +               bt_dev_err(hdev, "RTL: Write mem32 failed (%d)", err);
> +               return err;
> +       }
> +
> +       kfree_skb(skb);
> +       return 0;
> +}
> +
>  static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
>  {
>         void *data = iov->data;
> @@ -452,6 +518,30 @@ static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
>         return data;
>  }
>
> +static void btrtl_insert_ordered_patch_image(struct rtl_section_patch_image *image,
> +                                            struct btrtl_device_info *btrtl_dev)
> +{
> +       struct list_head *pos;
> +       struct list_head *next;
> +       struct rtl_section_patch_image *node;
> +
> +       list_for_each_safe(pos, next, &btrtl_dev->patch_images) {
> +               node = list_entry(pos, struct rtl_section_patch_image, list);
> +
> +               if (node->image_id > image->image_id) {
> +                       __list_add(&image->list, pos->prev, pos);
> +                       return;
> +               }
> +
> +               if (node->image_id == image->image_id &&
> +                   node->index > image->index) {
> +                       __list_add(&image->list, pos->prev, pos);
> +                       return;
> +               }
> +       }
> +       __list_add(&image->list, pos->prev, pos);
> +}
> +
>  static void btrtl_insert_ordered_subsec(struct rtl_subsection *node,
>                                         struct btrtl_device_info *btrtl_dev)
>  {
> @@ -629,6 +719,295 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
>                 return -EPERM;
>
>         *_buf = ptr;
> +       btrtl_dev->fw_type = FW_TYPE_V2;
> +       return len;
> +}
> +
> +static int rtlbt_parse_config(struct hci_dev *hdev,
> +                             struct rtl_section_patch_image *patch_image,
> +                             struct btrtl_device_info *btrtl_dev)
> +{
> +       const struct id_table *ic_info = NULL;
> +       const struct firmware *fw;
> +       char tmp_name[32];
> +       char filename[64];
> +       u8 *cfg_buf;
> +       char *str;
> +       char *p;
> +       int len;
> +       int ret;
> +
> +       if (btrtl_dev && btrtl_dev->ic_info)
> +               ic_info = btrtl_dev->ic_info;
> +
> +       if (!ic_info)
> +               return -EINVAL;
> +
> +       str = ic_info->cfg_name;
> +       if (btrtl_dev->fw_type == FW_TYPE_V3_1) {
> +               if (!patch_image->image_id && !patch_image->index) {
> +                       snprintf(filename, sizeof(filename), "%s.bin", str);
> +                       goto load_fw;
> +               }
> +               goto done;
> +       }
> +
> +       len = strlen(str);
> +       if (len > sizeof(tmp_name) - 1)
> +               len = sizeof(tmp_name) - 1;
> +       memcpy(tmp_name, str, len);
> +       tmp_name[len] = '\0';
> +
> +       str = tmp_name;
> +       p = strsep(&str, ".");
> +
> +       ret = snprintf(filename, sizeof(filename), "%s", p);
> +       if (patch_image->config_rule && patch_image->need_config) {
> +               switch (patch_image->image_id) {
> +               case IMAGE_ID_F000:
> +               case IMAGE_ID_F001:
> +               case IMAGE_ID_F002:
> +                       ret += snprintf(filename + ret, sizeof(filename) - ret,
> +                                       "_%04x", patch_image->image_id);
> +                       break;
> +               default:
> +                       goto done;
> +               }
> +       } else {
> +               goto done;
> +       }
> +
> +       if (str)
> +               snprintf(filename + ret, sizeof(filename) - ret, ".%s", str);
> +       else
> +               snprintf(filename + ret, sizeof(filename) - ret, ".bin");
> +load_fw:
> +       rtl_dev_info(hdev, "config file: %s", filename);
> +       ret = request_firmware(&fw, filename, &hdev->dev);
> +       if (ret < 0) {
> +               rtl_dev_err(hdev, "request_firmware [%s] error", filename);
> +               if (btrtl_dev->fw_type == FW_TYPE_V3_2) {
> +                       len = 4;
> +                       cfg_buf = kvmalloc(len, GFP_KERNEL);
> +                       if (!cfg_buf)
> +                               return -ENOMEM;
> +
> +                       memset(cfg_buf, 0xff, len);
> +                       patch_image->cfg_buf = cfg_buf;
> +                       patch_image->cfg_len = len;
> +                       return 0;
> +               }
> +               goto err_req_fw;
> +       }
> +       cfg_buf = kvmalloc(fw->size, GFP_KERNEL);
> +       if (!cfg_buf) {
> +               ret = -ENOMEM;
> +               goto err;
> +       }
> +       memcpy(cfg_buf, fw->data, fw->size);
> +       len = fw->size;
> +       release_firmware(fw);
> +
> +       patch_image->cfg_buf = cfg_buf;
> +       patch_image->cfg_len = len;
> +done:
> +       return 0;
> +err:
> +       release_firmware(fw);
> +err_req_fw:
> +       return ret;
> +}
> +
> +static int rtlbt_parse_section_v3(struct hci_dev *hdev,
> +                                 struct btrtl_device_info *btrtl_dev,
> +                                 u32 opcode, u8 *data, u32 len)
> +{
> +       struct rtl_section_patch_image *patch_image;
> +       struct rtl_patch_image_hdr *hdr;
> +       u16 image_id;
> +       u16 chip_id;
> +       u32 patch_image_len;
> +       u8 *ptr;
> +       int ret = 0;
> +       u8 i;
> +       struct rtl_iovec iov = {
> +               .data = data,
> +               .len  = len,
> +       };
> +
> +       hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
> +       if (!hdr)
> +               return -EINVAL;
> +
> +       if (btrtl_dev->opcode && btrtl_dev->opcode != opcode) {
> +               rtl_dev_err(hdev, "invalid opcode 0x%02x", opcode);
> +               return -EINVAL;
> +       }
> +
> +       if (!btrtl_dev->opcode) {
> +               btrtl_dev->opcode = opcode;
> +               switch (btrtl_dev->opcode) {
> +               case RTL_PATCH_V3_1:
> +                       btrtl_dev->fw_type = FW_TYPE_V3_1;
> +                       break;
> +               case RTL_PATCH_V3_PATCH_IMAGE:
> +                       btrtl_dev->fw_type = FW_TYPE_V3_2;
> +                       break;
> +               default:
> +                       return -EINVAL;
> +               }
> +       }
> +
> +       patch_image_len = (u32)le64_to_cpu(hdr->patch_image_len);
> +       chip_id = le16_to_cpu(hdr->chip_id);
> +       image_id = le16_to_cpu(hdr->image_id);
> +       rtl_dev_info(hdev, "image (%04x:%02x), chip id %u, cut 0x%02x, len %08x"
> +                    , image_id, hdr->index, chip_id, hdr->ic_cut,
> +                    patch_image_len);
> +
> +       if (btrtl_dev->key_id && btrtl_dev->key_id != hdr->key_id) {
> +               rtl_dev_err(hdev, "invalid key_id (%u, %u)", hdr->key_id,
> +                           btrtl_dev->key_id);
> +               return -EINVAL;
> +       }
> +
> +       if (hdr->ic_cut != btrtl_dev->rom_version + 1) {
> +               rtl_dev_info(hdev, "unused ic_cut (%u, %u)", hdr->ic_cut,
> +                           btrtl_dev->rom_version + 1);
> +               return -EINVAL;
> +       }
> +
> +       if (btrtl_dev->fw_type == FW_TYPE_V3_1 && !btrtl_dev->project_id)
> +               btrtl_dev->project_id = chip_id;
> +
> +       if (btrtl_dev->fw_type == FW_TYPE_V3_2 &&
> +           chip_id != btrtl_dev->project_id) {
> +               rtl_dev_err(hdev, "invalid chip_id (%u, %d)", chip_id,
> +                           btrtl_dev->project_id);
> +               return -EINVAL;
> +       }
> +
> +       ptr = rtl_iov_pull_data(&iov, patch_image_len);
> +       if (!ptr)
> +               return -ENODATA;
> +
> +       patch_image = kzalloc(sizeof(*patch_image), GFP_KERNEL);
> +       if (!patch_image)
> +               return -ENOMEM;
> +       patch_image->index = hdr->index;
> +       patch_image->image_id = image_id;
> +       patch_image->config_rule = hdr->config_rule;
> +       patch_image->need_config = hdr->need_config;
> +
> +       for (i = 0; i < DL_FIX_ADDR_MAX; i++) {
> +               patch_image->fix[i].addr =
> +                       (u32)le64_to_cpu(hdr->addr_fix[i * 2]);
> +               patch_image->fix[i].value =
> +                       (u32)le64_to_cpu(hdr->addr_fix[i * 2 + 1]);
> +       }
> +
> +       patch_image->image_len = patch_image_len;
> +       patch_image->image_data = kvmalloc(patch_image_len, GFP_KERNEL);
> +       if (!patch_image->image_data) {
> +               ret = -ENOMEM;
> +               goto err;
> +       }
> +       memcpy(patch_image->image_data, ptr, patch_image_len);
> +       patch_image->image_ver =
> +               get_unaligned_le32(ptr + patch_image->image_len - 4);
> +       rtl_dev_info(hdev, "image version: %08x", patch_image->image_ver);
> +
> +       rtlbt_parse_config(hdev, patch_image, btrtl_dev);
> +
> +       ret = patch_image->image_len;
> +
> +       btrtl_insert_ordered_patch_image(patch_image, btrtl_dev);
> +
> +       return ret;
> +err:
> +       kfree(patch_image);
> +       return ret;
> +}
> +
> +static int rtlbt_parse_firmware_v3(struct hci_dev *hdev,
> +                                  struct btrtl_device_info *btrtl_dev)
> +{
> +       struct rtl_epatch_header_v3 *hdr;
> +       int rc;
> +       u32 num_sections;
> +       struct rtl_section_v3 *section;
> +       u32 section_len;
> +       u32 opcode;
> +       int len = 0;
> +       int i;
> +       u8 *ptr;
> +       struct rtl_iovec iov = {
> +               .data = btrtl_dev->fw_data,
> +               .len  = btrtl_dev->fw_len,
> +       };
> +       struct rtl_vendor_cmd cmd_data = { {0x10, 0xa4, 0xad, 0x00, 0xb0} };
> +       u8 reg_val[2];
> +
> +       if (btrtl_dev->project_id >= CHIP_ID_8922D) {
> +               /* A0010DA4 */
> +               cmd_data.param[2] = 0x0d;
> +               cmd_data.param[3] = 0x01;
> +               cmd_data.param[4] = 0xa0;
> +       }
> +
> +       rc = btrtl_vendor_read_reg16(hdev, &cmd_data, reg_val);
> +       if (rc < 0)
> +               return -EIO;
> +
> +       rtl_dev_info(hdev, "key id %u", reg_val[0]);
> +
> +       btrtl_dev->key_id = reg_val[0];
> +
> +       hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
> +       if (!hdr)
> +               return -EINVAL;
> +       num_sections = le32_to_cpu(hdr->num_sections);
> +
> +       rtl_dev_dbg(hdev, "timpstamp %08x-%08x", *((u32 *)hdr->timestamp),
> +                   *((u32 *)(hdr->timestamp + 4)));
> +
> +       for (i = 0; i < num_sections; i++) {
> +               section = rtl_iov_pull_data(&iov, sizeof(*section));
> +               if (!section)
> +                       break;
> +
> +               section_len = (u32)le64_to_cpu(section->len);
> +               opcode = le32_to_cpu(section->opcode);
> +
> +               rtl_dev_dbg(hdev, "opcode 0x%04x", section->opcode);
> +
> +               ptr = rtl_iov_pull_data(&iov, section_len);
> +               if (!ptr)
> +                       break;
> +
> +               rc = 0;
> +               switch (opcode) {
> +               case RTL_PATCH_V3_1:
> +               case RTL_PATCH_V3_PATCH_IMAGE:
> +                       rc = rtlbt_parse_section_v3(hdev, btrtl_dev, opcode,
> +                                                   ptr, section_len);
> +                       break;
> +               default:
> +                       rtl_dev_warn(hdev, "Unknown opcode %08x", opcode);
> +                       break;
> +               }
> +               if (rc < 0) {
> +                       rtl_dev_err(hdev, "Parse section (%u) err (%d)",
> +                                   opcode, rc);
> +                       continue;
> +               }
> +               len += rc;
> +       }
> +
> +       rtl_dev_info(hdev, "image payload total len: 0x%08x", len);
> +       if (!len)
> +               return -ENODATA;
> +
>         return len;
>  }
>
> @@ -673,6 +1052,9 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
>         if (btrtl_dev->fw_len <= 8)
>                 return -EINVAL;
>
> +       if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V3, 8))
> +               return rtlbt_parse_firmware_v3(hdev, btrtl_dev);
> +
>         if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8))
>                 min_size = sizeof(struct rtl_epatch_header) +
>                                 sizeof(extension_sig) + 3;
> @@ -808,10 +1190,11 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
>         memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
>
>         *_buf = buf;
> +       btrtl_dev->fw_type = FW_TYPE_V1;
>         return len;
>  }
>
> -static int rtl_download_firmware(struct hci_dev *hdev,
> +static int rtl_download_firmware(struct hci_dev *hdev, u8 fw_type,
>                                  const unsigned char *data, int fw_len)
>  {
>         struct rtl_download_cmd *dl_cmd;
> @@ -822,6 +1205,13 @@ static int rtl_download_firmware(struct hci_dev *hdev,
>         int j = 0;
>         struct sk_buff *skb;
>         struct hci_rp_read_local_version *rp;
> +       u8 dl_rp_len = sizeof(struct rtl_download_response);
> +
> +       if (is_v3_fw(fw_type)) {
> +               j = 1;
> +               if (fw_type == FW_TYPE_V3_2)
> +                       dl_rp_len++;
> +       }
>
>         dl_cmd = kmalloc(sizeof(*dl_cmd), GFP_KERNEL);
>         if (!dl_cmd)
> @@ -834,7 +1224,7 @@ static int rtl_download_firmware(struct hci_dev *hdev,
>                 if (dl_cmd->index == 0x7f)
>                         j = 1;
>
> -               if (i == (frag_num - 1)) {
> +               if (i == (frag_num - 1) && !is_v3_fw(fw_type)) {
>                         dl_cmd->index |= 0x80; /* data end */
>                         frag_len = fw_len % RTL_FRAG_LEN;
>                 }
> @@ -852,7 +1242,7 @@ static int rtl_download_firmware(struct hci_dev *hdev,
>                         goto out;
>                 }
>
> -               if (skb->len != sizeof(struct rtl_download_response)) {
> +               if (skb->len != dl_rp_len) {
>                         rtl_dev_err(hdev, "download fw event length mismatch");
>                         kfree_skb(skb);
>                         ret = -EIO;
> @@ -863,6 +1253,9 @@ static int rtl_download_firmware(struct hci_dev *hdev,
>                 data += RTL_FRAG_LEN;
>         }
>
> +       if (is_v3_fw(fw_type))
> +               goto out;
> +
>         skb = btrtl_read_local_version(hdev);
>         if (IS_ERR(skb)) {
>                 ret = PTR_ERR(skb);
> @@ -880,6 +1273,226 @@ static int rtl_download_firmware(struct hci_dev *hdev,
>         return ret;
>  }
>
> +static int rtl_check_download_state(struct hci_dev *hdev,
> +                                   struct btrtl_device_info *btrtl_dev)
> +{
> +       struct sk_buff *skb;
> +       int ret = 0;
> +       u8 state;
> +
> +       skb = __hci_cmd_sync(hdev, 0xfdcf, 0, NULL, HCI_CMD_TIMEOUT);
> +       if (IS_ERR(skb)) {
> +               rtl_dev_err(hdev, "write tb error %lu", PTR_ERR(skb));
> +               return -EIO;
> +       }
> +
> +       /* Other driver might be downloading the combined firmware. */
> +       state = skb->data[0];

skb->len can be zero so accessing data[0] is not safe above, then
again there is the likes of skb_pull_data to ensure there is enough
data under the skb.

> +       kfree_skb(skb);
> +       if (state == 0x03) {
> +               btrealtek_set_flag(hdev, REALTEK_DOWNLOADING);
> +               ret = btrealtek_wait_on_flag_timeout(hdev, REALTEK_DOWNLOADING,
> +                                                    TASK_INTERRUPTIBLE,
> +                                                    msecs_to_jiffies(5000));
> +               if (ret == -EINTR) {
> +                       bt_dev_err(hdev, "Firmware loading interrupted");
> +                       return ret;
> +               }
> +
> +               if (ret) {
> +                       bt_dev_err(hdev, "Firmware loading timeout");
> +                       return -ETIMEDOUT;
> +               }
> +
> +               ret = -EALREADY;
> +       }
> +
> +       return 0;
> +}
> +
> +static int rtl_finalize_download(struct hci_dev *hdev,
> +                                struct btrtl_device_info *btrtl_dev)
> +{
> +       struct hci_rp_read_local_version *rp_ver;
> +       u8 params[2] = { 0x03, 0xb2 };
> +       struct sk_buff *skb;
> +       u16 opcode;
> +       u32 len;
> +       int ret;
> +
> +       opcode = 0xfc8e;
> +       len = 2;
> +       if (btrtl_dev->opcode == RTL_PATCH_V3_1) {
> +               opcode = 0xfc20;
> +               params[0] = 0x80;
> +               len = 1;
> +       }
> +       skb = __hci_cmd_sync(hdev, opcode, len, params, HCI_CMD_TIMEOUT);
> +       if (IS_ERR(skb)) {
> +               rtl_dev_err(hdev, "Watchdog reset err (%ld)", PTR_ERR(skb));
> +               return -EIO;
> +       }
> +       rtl_dev_info(hdev, "Watchdog reset status %02x", skb->data[0]);

Ditto.

> +       kfree_skb(skb);
> +
> +       skb = btrtl_read_local_version(hdev);
> +       if (IS_ERR(skb)) {
> +               ret = PTR_ERR(skb);
> +               rtl_dev_err(hdev, "read local version failed (%d)", ret);
> +               return ret;
> +       }
> +
> +       rp_ver = (struct hci_rp_read_local_version *)skb->data;
> +       rtl_dev_info(hdev, "fw version 0x%04x%04x",
> +                    __le16_to_cpu(rp_ver->hci_rev),
> +                    __le16_to_cpu(rp_ver->lmp_subver));
> +       kfree_skb(skb);
> +
> +       return 0;
> +}
> +
> +static int rtl_security_check(struct hci_dev *hdev,
> +                             struct btrtl_device_info *btrtl_dev)
> +{
> +       struct rtl_section_patch_image *tmp = NULL;
> +       struct rtl_section_patch_image *image = NULL;
> +       u32 val;
> +       int ret;
> +
> +       list_for_each_entry_reverse(tmp, &btrtl_dev->patch_images, list) {
> +               /* Check security hdr */
> +               if (!tmp->fix[DL_FIX_SEC_HDR_ADDR].value ||
> +                   !tmp->fix[DL_FIX_SEC_HDR_ADDR].addr ||
> +                   tmp->fix[DL_FIX_SEC_HDR_ADDR].addr == 0xffffffff)
> +                       continue;
> +               rtl_dev_info(hdev, "addr 0x%08x, value 0x%08x",
> +                            tmp->fix[DL_FIX_SEC_HDR_ADDR].addr,
> +                            tmp->fix[DL_FIX_SEC_HDR_ADDR].value);
> +               image = tmp;
> +               break;
> +       }
> +
> +       if (!image)
> +               return 0;
> +
> +       rtl_dev_info(hdev, "sec image (%04x:%02x)", image->image_id,
> +                    image->index);
> +       val = image->fix[DL_FIX_PATCH_ADDR].value + image->image_len -
> +                                       image->fix[DL_FIX_SEC_HDR_ADDR].value;
> +       ret = btrtl_vendor_write_mem(hdev, image->fix[DL_FIX_PATCH_ADDR].addr,
> +                                    val);
> +       if (ret) {
> +               rtl_dev_err(hdev, "write sec reg failed (%d)", ret);
> +               return ret;
> +       }
> +       return 0;
> +}
> +
> +static int rtl_download_firmware_v3(struct hci_dev *hdev,
> +                                   struct btrtl_device_info *btrtl_dev)
> +{
> +       struct rtl_section_patch_image *image, *tmp;
> +       struct rtl_rp_dl_v3 *rp;
> +       struct sk_buff *skb;
> +       u8 *fw_data;
> +       int fw_len;
> +       int ret = 0;
> +       u8 i;
> +
> +       if (btrtl_dev->fw_type == FW_TYPE_V3_2) {
> +               ret = rtl_check_download_state(hdev, btrtl_dev);
> +               if (ret) {
> +                       if (ret == -EALREADY)
> +                               return 0;
> +                       return ret;
> +               }
> +       }
> +
> +       list_for_each_entry_safe(image, tmp, &btrtl_dev->patch_images, list) {
> +               rtl_dev_dbg(hdev, "image (%04x:%02x)", image->image_id,
> +                           image->index);
> +
> +               for (i = DL_FIX_CI_ID; i < DL_FIX_ADDR_MAX; i++) {
> +                       if (!image->fix[i].addr ||
> +                           image->fix[i].addr == 0xffffffff) {
> +                               rtl_dev_dbg(hdev, "no need to write addr %08x",
> +                                           image->fix[i].addr);
> +                               continue;
> +                       }
> +                       rtl_dev_dbg(hdev, "write addr and val, 0x%08x, 0x%08x",
> +                                   image->fix[i].addr, image->fix[i].value);
> +                       if (btrtl_vendor_write_mem(hdev, image->fix[i].addr,
> +                                                  image->fix[i].value)) {
> +                               rtl_dev_err(hdev, "write reg failed");
> +                               ret = -EIO;
> +                               goto done;
> +                       }
> +               }
> +
> +               fw_len = image->image_len + image->cfg_len;
> +               fw_data = kvmalloc(fw_len, GFP_KERNEL);
> +               if (!fw_data) {
> +                       rtl_dev_err(hdev, "Couldn't alloc buf for image data");
> +                       ret = -ENOMEM;
> +                       goto done;
> +               }
> +               memcpy(fw_data, image->image_data, image->image_len);
> +               if (image->cfg_len > 0)
> +                       memcpy(fw_data + image->image_len, image->cfg_buf,
> +                              image->cfg_len);
> +
> +               rtl_dev_dbg(hdev, "patch image (%04x:%02x). len: %d",
> +                           image->image_id, image->index, fw_len);
> +               rtl_dev_dbg(hdev, "fw_data %p, image buf %p, len %u", fw_data,
> +                           image->image_data, image->image_len);
> +
> +               ret = rtl_download_firmware(hdev, btrtl_dev->fw_type, fw_data,
> +                                           fw_len);
> +               kvfree(fw_data);
> +               if (ret < 0) {
> +                       rtl_dev_err(hdev, "download firmware failed (%d)", ret);
> +                       goto done;
> +               }
> +
> +               if (image->list.next != &btrtl_dev->patch_images &&
> +                   image->image_id == tmp->image_id)
> +                       continue;
> +
> +               if (btrtl_dev->fw_type == FW_TYPE_V3_1)
> +                       continue;
> +
> +               i = 0x80;
> +               skb = __hci_cmd_sync(hdev, 0xfc20, 1, &i, HCI_CMD_TIMEOUT);
> +               if (IS_ERR(skb)) {
> +                       ret = -EIO;
> +                       rtl_dev_err(hdev, "Failed to issue last cmd fc20, %ld",
> +                                   PTR_ERR(skb));
> +                       goto done;
> +               }
> +               rp = (void *)skb->data;

Again unsafe access, use skb_pull_data.

> +               ret = rp->err;
> +               kfree_skb(skb);
> +               if (ret == 2) {
> +                       /* Verification failure */
> +                       ret = -EFAULT;
> +                       goto done;
> +               }
> +       }
> +
> +       if (btrtl_dev->fw_type == FW_TYPE_V3_1) {
> +               ret = rtl_security_check(hdev, btrtl_dev);
> +               if (ret) {
> +                       rtl_dev_err(hdev, "Security check failed (%d)", ret);
> +                       goto done;
> +               }
> +       }
> +
> +       ret = rtl_finalize_download(hdev, btrtl_dev);
> +
> +done:
> +       return ret;
> +}
> +
>  static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
>  {
>         const struct firmware *fw;
> @@ -913,7 +1526,7 @@ static int btrtl_setup_rtl8723a(struct hci_dev *hdev,
>                 return -EINVAL;
>         }
>
> -       return rtl_download_firmware(hdev, btrtl_dev->fw_data,
> +       return rtl_download_firmware(hdev, FW_TYPE_V0, btrtl_dev->fw_data,
>                                      btrtl_dev->fw_len);
>  }
>
> @@ -928,7 +1541,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
>         if (ret < 0)
>                 goto out;
>
> -       if (btrtl_dev->cfg_len > 0) {
> +       if (!is_v3_fw(btrtl_dev->fw_type) && btrtl_dev->cfg_len > 0) {
>                 tbuff = kvzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
>                 if (!tbuff) {
>                         ret = -ENOMEM;
> @@ -944,9 +1557,14 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
>                 fw_data = tbuff;
>         }
>
> +       if (is_v3_fw(btrtl_dev->fw_type)) {
> +               ret = rtl_download_firmware_v3(hdev, btrtl_dev);
> +               goto out;
> +       }
> +
>         rtl_dev_info(hdev, "cfg_sz %d, total sz %d", btrtl_dev->cfg_len, ret);
>
> -       ret = rtl_download_firmware(hdev, fw_data, ret);
> +       ret = rtl_download_firmware(hdev, btrtl_dev->fw_type, fw_data, ret);
>
>  out:
>         kvfree(fw_data);
> @@ -1042,6 +1660,7 @@ static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type)
>  void btrtl_free(struct btrtl_device_info *btrtl_dev)
>  {
>         struct rtl_subsection *entry, *tmp;
> +       struct rtl_section_patch_image *image, *next;
>
>         kvfree(btrtl_dev->fw_data);
>         kvfree(btrtl_dev->cfg_data);
> @@ -1051,6 +1670,13 @@ void btrtl_free(struct btrtl_device_info *btrtl_dev)
>                 kfree(entry);
>         }
>
> +       list_for_each_entry_safe(image, next, &btrtl_dev->patch_images, list) {
> +               list_del(&image->list);
> +               kvfree(image->image_data);
> +               kvfree(image->cfg_buf);
> +               kfree(image);
> +       }
> +
>         kfree(btrtl_dev);
>  }
>  EXPORT_SYMBOL_GPL(btrtl_free);
> @@ -1058,7 +1684,7 @@ EXPORT_SYMBOL_GPL(btrtl_free);
>  struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
>                                            const char *postfix)
>  {
> -       struct btrealtek_data *coredump_info = hci_get_priv(hdev);
> +       struct btrealtek_data *btrtl_data = hci_get_priv(hdev);
>         struct btrtl_device_info *btrtl_dev;
>         struct sk_buff *skb;
>         struct hci_rp_read_local_version *resp;
> @@ -1069,6 +1695,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
>         u8 hci_ver, lmp_ver, chip_type = 0;
>         int ret;
>         u8 reg_val[2];
> +       u8 chip_id = 0;
>
>         btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
>         if (!btrtl_dev) {
> @@ -1077,8 +1704,15 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
>         }
>
>         INIT_LIST_HEAD(&btrtl_dev->patch_subsecs);
> +       INIT_LIST_HEAD(&btrtl_dev->patch_images);
>
>  check_version:
> +       ret = btrtl_read_chip_id(hdev, &chip_id);
> +       if (!ret && chip_id == CHIP_ID_8922D) {
> +               btrtl_dev->project_id = chip_id;
> +               goto read_local_ver;
> +       }
> +
>         ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_SUBVER, reg_val);
>         if (ret < 0)
>                 goto err_free;
> @@ -1101,6 +1735,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
>                 }
>         }
>
> +read_local_ver:
>         skb = btrtl_read_local_version(hdev);
>         if (IS_ERR(skb)) {
>                 ret = PTR_ERR(skb);
> @@ -1228,7 +1863,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
>                 hci_set_msft_opcode(hdev, 0xFCF0);
>
>         if (btrtl_dev->ic_info)
> -               coredump_info->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
> +               btrtl_data->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
>
>         return btrtl_dev;
>
> @@ -1301,6 +1936,7 @@ void btrtl_set_quirks(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev)
>         case CHIP_ID_8851B:
>         case CHIP_ID_8922A:
>         case CHIP_ID_8852BT:
> +       case CHIP_ID_8922D:
>                 set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
>
>                 /* RTL8852C needs to transmit mSBC data continuously without
> @@ -1387,6 +2023,23 @@ int btrtl_shutdown_realtek(struct hci_dev *hdev)
>  }
>  EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek);
>
> +int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
> +{
> +       struct hci_event_hdr *hdr = (void *)skb->data;

Unsafe access, use skb_pull_data and check that it doesn't return NULL.

> +
> +       if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
> +           hdr->plen > 0) {
> +               if (skb->data[2] == 0x77 &&
> +                   btrealtek_test_and_clear_flag(hdev, REALTEK_DOWNLOADING)) {
> +                       btrealtek_wake_up_flag(hdev, REALTEK_DOWNLOADING);
> +                       return 0;
> +               }
> +       }
> +
> +       return hci_recv_frame(hdev, skb);
> +}
> +EXPORT_SYMBOL_GPL(btrtl_recv_event);
> +
>  static unsigned int btrtl_convert_baudrate(u32 device_baudrate)
>  {
>         switch (device_baudrate) {
> diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
> index a2d9d34f9fb0..f6f03a5fefba 100644
> --- a/drivers/bluetooth/btrtl.h
> +++ b/drivers/bluetooth/btrtl.h
> @@ -12,6 +12,19 @@
>  #define rtl_dev_info(dev, fmt, ...) bt_dev_info(dev, "RTL: " fmt, ##__VA_ARGS__)
>  #define rtl_dev_dbg(dev, fmt, ...) bt_dev_dbg(dev, "RTL: " fmt, ##__VA_ARGS__)
>
> +#define FW_TYPE_V0             0
> +#define FW_TYPE_V1             1
> +#define FW_TYPE_V2             2
> +#define FW_TYPE_V3_1           3
> +#define FW_TYPE_V3_2           4
> +#define is_v3_fw(type) (type == FW_TYPE_V3_1 || type == FW_TYPE_V3_2)
> +
> +#define DL_FIX_CI_ID           0
> +#define DL_FIX_CI_ADDR         1
> +#define DL_FIX_PATCH_ADDR      2
> +#define DL_FIX_SEC_HDR_ADDR    3
> +#define DL_FIX_ADDR_MAX                4
> +
>  struct btrtl_device_info;
>
>  struct rtl_chip_type_evt {
> @@ -103,8 +116,79 @@ struct rtl_vendor_cmd {
>         __u8 param[5];
>  } __packed;
>
> +struct rtl_vendor_write_cmd {
> +       u8 type;
> +       __le32 addr;
> +       __le32 val;
> +} __packed;
> +
> +struct rtl_rp_read_chip_id {
> +       __u8 status;
> +       __u8 chip_id;
> +} __packed;
> +
> +struct rtl_rp_dl_v3 {
> +       __u8 status;
> +       __u8 index;
> +       __u8 err;
> +} __packed;
> +
> +struct rtl_epatch_header_v3 {
> +       __u8 signature[8];
> +       __u8 timestamp[8];
> +       __le32 ver_rsvd;
> +       __le32 num_sections;
> +} __packed;
> +
> +struct rtl_section_v3 {
> +       __le32 opcode;
> +       __le64 len;
> +       u8 data[];
> +} __packed;
> +
> +struct rtl_addr_fix {
> +       u32 addr;
> +       u32 value;
> +};
> +
> +struct rtl_section_patch_image {
> +       u16 image_id;
> +       u8 index;
> +       u8 config_rule;
> +       u8 need_config;
> +
> +       struct rtl_addr_fix fix[DL_FIX_ADDR_MAX];
> +
> +       u32 image_len;
> +       u8 *image_data;
> +       u32 image_ver;
> +
> +       u8  *cfg_buf;
> +       u16 cfg_len;
> +
> +       struct list_head list;
> +};
> +
> +struct rtl_patch_image_hdr {
> +       __le16 chip_id;
> +       u8 ic_cut;
> +       u8 key_id;
> +       u8 enable_ota;
> +       __le16 image_id;
> +       u8 config_rule;
> +       u8 need_config;
> +       u8 rsv[950];
> +
> +       __le64 addr_fix[DL_FIX_ADDR_MAX * 2];
> +       u8 index;
> +
> +       __le64 patch_image_len;
> +       __u8 data[];
> +} __packed;
> +
>  enum {
>         REALTEK_ALT6_CONTINUOUS_TX_CHIP,
> +       REALTEK_DOWNLOADING,
>
>         __REALTEK_NUM_FLAGS,
>  };
> @@ -130,8 +214,20 @@ struct btrealtek_data {
>  #define btrealtek_get_flag(hdev)                                       \
>         (((struct btrealtek_data *)hci_get_priv(hdev))->flags)
>
> +#define btrealtek_wake_up_flag(hdev, nr)                               \
> +       do {                                                            \
> +               struct btrealtek_data *rtl = hci_get_priv((hdev));      \
> +               wake_up_bit(rtl->flags, (nr));                          \
> +       } while (0)
> +
>  #define btrealtek_test_flag(hdev, nr)  test_bit((nr), btrealtek_get_flag(hdev))
>
> +#define btrealtek_test_and_clear_flag(hdev, nr)                                \
> +       test_and_clear_bit((nr), btrealtek_get_flag(hdev))
> +
> +#define btrealtek_wait_on_flag_timeout(hdev, nr, m, to)                        \
> +       wait_on_bit_timeout(btrealtek_get_flag(hdev), (nr), m, to)
> +
>  #if IS_ENABLED(CONFIG_BT_RTL)
>
>  struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
> @@ -148,6 +244,7 @@ int btrtl_get_uart_settings(struct hci_dev *hdev,
>                             unsigned int *controller_baudrate,
>                             u32 *device_baudrate, bool *flow_control);
>  void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name);
> +int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
>
>  #else
>
> @@ -195,4 +292,9 @@ static inline void btrtl_set_driver_name(struct hci_dev *hdev, const char *drive
>  {
>  }
>
> +static inline int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
> +{
> +       return -EOPNOTSUPP;
> +}
> +
>  #endif
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index f8f256ff79a3..a87ea836d730 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -2677,6 +2677,9 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
>                 return 0;
>         }
>
> +       if (skb->data[0] == HCI_VENDOR_PKT)
> +               return btrtl_recv_event(hdev, skb);
> +
>         return hci_recv_frame(hdev, skb);
>  }
>
> --
> 2.34.1
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 2/2] Bluetooth: btrtl: Add enhanced download support
  2025-07-08 12:45 ` [PATCH v3 2/2] Bluetooth: btrtl: Add enhanced download support Hilda Wu
@ 2025-07-10 16:10   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 10+ messages in thread
From: Luiz Augusto von Dentz @ 2025-07-10 16:10 UTC (permalink / raw)
  To: Hilda Wu; +Cc: marcel, linux-bluetooth, linux-kernel, alex_lu, max.chou

Hi Hilda,

On Tue, Jul 8, 2025 at 8:45 AM Hilda Wu <hildawu@realtek.com> wrote:
>
> Add an enhanced download mode for firmware format v3.
> Use ACL to speed up firmware downloads.
>
> Signed-off-by: Alex Lu <alex_lu@realsil.com.cn>
> Signed-off-by: Hilda Wu <hildawu@realtek.com>
> ---
> Change in V3:
> - Avoiding memory leak
>
> Change in V2:
> - Move structure to btrtl.h
> - Fix build warnings
> ---
> ---
>  drivers/bluetooth/btrtl.c | 193 +++++++++++++++++++++++++++++++++++++-
>  drivers/bluetooth/btrtl.h |  20 ++++
>  2 files changed, 211 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
> index af28f5355aa1..27df5e439e89 100644
> --- a/drivers/bluetooth/btrtl.c
> +++ b/drivers/bluetooth/btrtl.c
> @@ -108,6 +108,8 @@ struct btrtl_device_info {
>         u32 opcode;
>         u8 fw_type;
>         u8 key_id;
> +       u16 handle;
> +       u16 acldata_pkt_len;
>         struct list_head patch_subsecs;
>         struct list_head patch_images;
>  };
> @@ -1310,6 +1312,163 @@ static int rtl_check_download_state(struct hci_dev *hdev,
>         return 0;
>  }
>
> +static int btrtl_enhanced_download_mode_enable(struct hci_dev *hdev,
> +                                       struct btrtl_device_info *btrtl_dev)
> +{
> +       struct hci_rp_enhanced_download_mode *ev;
> +       struct sk_buff *skb;
> +       u16 opcode = 0xfc1f;
> +       u8 val = 1;
> +       int ret = -EINVAL;
> +
> +       skb = __hci_cmd_sync(hdev, opcode, 1, &val, HCI_CMD_TIMEOUT);
> +       if (IS_ERR(skb)) {
> +               bt_dev_err(hdev, "send %04x error (%lu)", opcode, PTR_ERR(skb));
> +               return -EIO;
> +       }
> +       if (skb->len != sizeof(*ev)) {
> +               bt_dev_err(hdev, "got invalid cmd complete, %u %zu", skb->len,
> +                          sizeof(*ev));
> +               goto err;
> +       }
> +       ev = (struct hci_rp_enhanced_download_mode *)skb->data;

Use skb_pull_data above.

> +       if (ev->status) {
> +               bt_dev_err(hdev, "got invalid status 0x%02x", ev->status);
> +               goto err;
> +       }
> +       btrtl_dev->handle = le16_to_cpu(ev->handle);
> +       btrtl_dev->acldata_pkt_len = le16_to_cpu(ev->acldata_pkt_len);
> +       kfree_skb(skb);
> +
> +       bt_dev_info(hdev, "enhanced download mode enabled, handle %04x, acl %u",
> +                   btrtl_dev->handle, btrtl_dev->acldata_pkt_len);

Don't think the users need to see this message, please use bt_dev_dbg
for debug messages.

> +       return 0;
> +err:
> +       kfree_skb(skb);
> +       return ret;
> +}
> +
> +static int rtl_acl_download_firmware(struct hci_dev *hdev,
> +                                    struct btrtl_device_info *btrtl_dev,
> +                                    const unsigned char *data, int fw_len)
> +{
> +       struct btrealtek_data *btrtl_data = hci_get_priv(hdev);
> +       int frag_num = fw_len / RTL_FRAG_LEN + 1;
> +       int frag_len = RTL_FRAG_LEN;
> +       int ret = 0;
> +       int i;
> +       int j = 0;
> +       struct sk_buff *skb;
> +       struct rtl_acl_download_rp *rp;
> +       u16 max_payload_len;
> +       struct hci_acl_hdr *hdr;
> +       u8 index;
> +
> +       if (is_v3_fw(btrtl_dev->fw_type))
> +               j = 1;
> +
> +       btrtl_data->dlreq_status = 0;
> +       btrtl_data->dlreq_result = 0;
> +       btrtl_data->dlreq_rsp = NULL;
> +       max_payload_len = (btrtl_dev->acldata_pkt_len - 1) & ~0x3;
> +
> +       for (i = 0; i < frag_num; i++) {
> +               index = j++;
> +               if (index == 0x7f)
> +                       j = 1;
> +
> +               if (i == (frag_num - 1) && !is_v3_fw(btrtl_dev->fw_type)) {
> +                       index |= 0x80; /* data end */
> +                       frag_len = fw_len % max_payload_len;
> +               }
> +               rtl_dev_dbg(hdev, "acl download fw (%d/%d). index = %d", i,
> +                           frag_num, index);
> +
> +               skb = bt_skb_alloc(sizeof(*hdr) + 1 + frag_len, GFP_KERNEL);
> +               if (!skb)
> +                       return -ENOMEM;
> +               hdr = (struct hci_acl_hdr *)skb_put(skb, sizeof(*hdr));
> +               hdr->handle = cpu_to_le16(btrtl_dev->handle | 0x8000);
> +               hdr->dlen = cpu_to_le16(1 + frag_len);
> +               *(u8 *)skb_put(skb, 1) = index;
> +               memcpy(skb_put(skb, frag_len), data, frag_len);
> +
> +               hci_skb_pkt_type(skb) = HCI_ACLDATA_PKT;
> +
> +               btrtl_data->dlreq_status = HCI_REQ_PEND;
> +
> +               ret = hdev->send(hdev, skb);
> +               if (ret < 0) {
> +                       bt_dev_err(hdev, "sending frame failed (%d)", ret);
> +                       goto err;
> +               }
> +
> +               ret = wait_event_interruptible_timeout(btrtl_data->dlreq_wait_q,
> +                               btrtl_data->dlreq_status != HCI_REQ_PEND,
> +                               HCI_INIT_TIMEOUT);
> +               if (ret == -ERESTARTSYS)
> +                       goto out;
> +
> +               switch (btrtl_data->dlreq_status) {
> +               case HCI_REQ_DONE:
> +                       ret = -bt_to_errno(btrtl_data->dlreq_result);
> +                       break;
> +
> +               case HCI_REQ_CANCELED:
> +                       ret = -btrtl_data->dlreq_result;
> +                       break;
> +
> +               default:
> +                       ret = -ETIMEDOUT;
> +                       break;
> +               }
> +
> +               btrtl_data->dlreq_status = 0;
> +               btrtl_data->dlreq_result = 0;
> +               skb = btrtl_data->dlreq_rsp;
> +               btrtl_data->dlreq_rsp = NULL;
> +
> +               bt_dev_dbg(hdev, "end: err %d", ret);
> +
> +               if (ret < 0) {
> +                       bt_dev_err(hdev, "wait on complete err (%d)", ret);
> +                       goto err;
> +               }
> +
> +               if (!skb)
> +                       return -ENODATA;
> +
> +               if (skb->len != sizeof(*rp)) {
> +                       rtl_dev_err(hdev, "acl download fw event len mismatch");
> +                       ret = -EIO;
> +                       goto err;
> +               }
> +               rp = (struct rtl_acl_download_rp *)skb->data;

Ditto, use skb_pull_data.

> +               if ((btrtl_dev->handle & 0xfff) != le16_to_cpu(rp->handle)) {
> +                       rtl_dev_err(hdev, "handle mismatch (%04x %04x)",
> +                                   btrtl_dev->handle & 0xfff,
> +                                   le16_to_cpu(rp->handle));
> +                       ret = -EINVAL;
> +                       goto err;
> +               }
> +               if (index != rp->index) {
> +                       rtl_dev_err(hdev, "index mismatch (%u, %u)", index,
> +                                   rp->index);
> +                       ret = -EINVAL;
> +                       goto err;
> +               }
> +
> +               kfree_skb(skb);
> +               data += frag_len;
> +       }
> +out:
> +       return ret;
> +err:
> +       kfree_skb(skb);
> +       return ret;
> +}
> +
>  static int rtl_finalize_download(struct hci_dev *hdev,
>                                  struct btrtl_device_info *btrtl_dev)
>  {
> @@ -1394,6 +1553,7 @@ static int rtl_download_firmware_v3(struct hci_dev *hdev,
>         struct rtl_section_patch_image *image, *tmp;
>         struct rtl_rp_dl_v3 *rp;
>         struct sk_buff *skb;
> +       u8 enh_dl = 0;
>         u8 *fw_data;
>         int fw_len;
>         int ret = 0;
> @@ -1408,6 +1568,16 @@ static int rtl_download_firmware_v3(struct hci_dev *hdev,
>                 }
>         }
>
> +       switch (btrtl_dev->project_id) {
> +       case CHIP_ID_8852C:
> +       case CHIP_ID_8922D:
> +               if (!btrtl_enhanced_download_mode_enable(hdev, btrtl_dev))
> +                       enh_dl = 1;
> +               break;
> +       default:
> +               break;
> +       }
> +
>         list_for_each_entry_safe(image, tmp, &btrtl_dev->patch_images, list) {
>                 rtl_dev_dbg(hdev, "image (%04x:%02x)", image->image_id,
>                             image->index);
> @@ -1446,8 +1616,13 @@ static int rtl_download_firmware_v3(struct hci_dev *hdev,
>                 rtl_dev_dbg(hdev, "fw_data %p, image buf %p, len %u", fw_data,
>                             image->image_data, image->image_len);
>
> -               ret = rtl_download_firmware(hdev, btrtl_dev->fw_type, fw_data,
> -                                           fw_len);
> +               if (enh_dl)
> +                       ret = rtl_acl_download_firmware(hdev, btrtl_dev,
> +                                                       fw_data, fw_len);
> +               else
> +                       ret = rtl_download_firmware(hdev, btrtl_dev->fw_type,
> +                                                   fw_data, fw_len);
> +
>                 kvfree(fw_data);
>                 if (ret < 0) {
>                         rtl_dev_err(hdev, "download firmware failed (%d)", ret);
> @@ -1705,6 +1880,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
>
>         INIT_LIST_HEAD(&btrtl_dev->patch_subsecs);
>         INIT_LIST_HEAD(&btrtl_dev->patch_images);
> +       init_waitqueue_head(&btrtl_data->dlreq_wait_q);
>
>  check_version:
>         ret = btrtl_read_chip_id(hdev, &chip_id);
> @@ -2025,6 +2201,7 @@ EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek);
>
>  int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
>  {
> +       struct btrealtek_data *btrtl_data = hci_get_priv(hdev);
>         struct hci_event_hdr *hdr = (void *)skb->data;
>
>         if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
> @@ -2032,6 +2209,18 @@ int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
>                 if (skb->data[2] == 0x77 &&
>                     btrealtek_test_and_clear_flag(hdev, REALTEK_DOWNLOADING)) {
>                         btrealtek_wake_up_flag(hdev, REALTEK_DOWNLOADING);
> +                       /* skb should be free here. */
> +                       kfree_skb(skb);
> +                       return 0;
> +               } else if (skb->data[2] == 0x2a) {

These accesses of skb->data probably need to be removed, even if you
are doing some checks for skb->len it is probably more readable to use
skb_pull_data, we may as well route the vendor events (0xff) from
hci_event.c with usage of an hci_ev table the driver can register to
avoid the drivers having to intercept the events like in the above.

> +                       if (btrtl_data->dlreq_status == HCI_REQ_PEND) {
> +                               btrtl_data->dlreq_result = 0;
> +                               btrtl_data->dlreq_status = HCI_REQ_DONE;
> +                               skb_pull(skb, sizeof(*hdr));
> +                               btrtl_data->dlreq_rsp = skb_get(skb);
> +                               wake_up_interruptible(&btrtl_data->dlreq_wait_q);
> +                       }
> +                       kfree_skb(skb);
>                         return 0;
>                 }
>         }
> diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
> index f6f03a5fefba..7f15b30680d7 100644
> --- a/drivers/bluetooth/btrtl.h
> +++ b/drivers/bluetooth/btrtl.h
> @@ -203,8 +203,28 @@ struct btrealtek_data {
>         DECLARE_BITMAP(flags, __REALTEK_NUM_FLAGS);
>
>         struct rtl_dump_info rtl_dump;
> +
> +       wait_queue_head_t       dlreq_wait_q;
> +       __u32                   dlreq_status;
> +       __u32                   dlreq_result;
> +       struct sk_buff          *dlreq_rsp;
>  };
>
> +struct rtl_acl_download_rp {
> +       __u8 subevent;
> +       __u8 index;
> +       __le16 handle;
> +       __le32 loaded_len;
> +} __packed;
> +
> +struct hci_rp_enhanced_download_mode {
> +       __u8 status;
> +       __u8 reserved1;
> +       __le16 handle;
> +       __le16 acldata_pkt_len;
> +       __u8 reserved2;
> +} __packed;
> +
>  #define btrealtek_set_flag(hdev, nr)                                   \
>         do {                                                            \
>                 struct btrealtek_data *realtek = hci_get_priv((hdev));  \
> --
> 2.34.1
>


-- 
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [v5] Bluetooth: btrtl: Add firmware format v3 support
  2026-03-16 11:30 [PATCH v5] " Hilda Wu
@ 2026-03-16 12:05 ` bluez.test.bot
  0 siblings, 0 replies; 10+ messages in thread
From: bluez.test.bot @ 2026-03-16 12:05 UTC (permalink / raw)
  To: linux-bluetooth, hildawu

[-- Attachment #1: Type: text/plain, Size: 7053 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1067266

---Test result---

Test Summary:
CheckPatch                    PENDING   0.59 seconds
GitLint                       PENDING   0.27 seconds
SubjectPrefix                 PASS      0.07 seconds
BuildKernel                   PASS      25.68 seconds
CheckAllWarning               PASS      28.58 seconds
CheckSparse                   WARNING   32.10 seconds
BuildKernel32                 PASS      25.39 seconds
TestRunnerSetup               PASS      563.76 seconds
TestRunner_l2cap-tester       PASS      29.42 seconds
TestRunner_iso-tester         PASS      80.60 seconds
TestRunner_bnep-tester        PASS      6.38 seconds
TestRunner_mgmt-tester        FAIL      114.80 seconds
TestRunner_rfcomm-tester      PASS      9.64 seconds
TestRunner_sco-tester         FAIL      14.76 seconds
TestRunner_ioctl-tester       PASS      10.24 seconds
TestRunner_mesh-tester        FAIL      12.45 seconds
TestRunner_smp-tester         PASS      8.78 seconds
TestRunner_userchan-tester    PASS      6.67 seconds
IncrementalBuild              PENDING   0.63 seconds

Details
##############################
Test: CheckPatch - PENDING
Desc: Run checkpatch.pl script
Output:

##############################
Test: GitLint - PENDING
Desc: Run gitlint
Output:

##############################
Test: CheckSparse - WARNING
Desc: Run sparse tool with linux kernel
Output:
drivers/bluetooth/btrtl.c:2178:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2179:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2180:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2181:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2181:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2182:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2183:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2184:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2185:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2186:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2187:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2188:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2189:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2190:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2191:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2192:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2193:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2194:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2195:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2196:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2197:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2198:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2199:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2200:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2201:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2202:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2203:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2204:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2205:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2206:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2207:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2208:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2209:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2210:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2211:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2212:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2213:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2214:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2215:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2216:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2217:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2218:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2219:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2220:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2221:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2222:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2223:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2224:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2225:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2226:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2227:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2228:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2229:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2230:1: error: bad constant expressiondrivers/bluetooth/btrtl.c:2231:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4674:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4675:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4677:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4678:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4680:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4681:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4683:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4684:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4686:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4687:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4688:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4689:1: error: bad constant expressiondrivers/bluetooth/btusb.c:4689:1: error: bad constant expression
##############################
Test: TestRunner_mgmt-tester - FAIL
Desc: Run mgmt-tester with test-runner
Output:
Total: 494, Passed: 489 (99.0%), Failed: 1, Not Run: 4

Failed Test Cases
Read Exp Feature - Success                           Failed       0.108 seconds
##############################
Test: TestRunner_sco-tester - FAIL
Desc: Run sco-tester with test-runner
Output:
WARNING: possible circular locking dependency detected
BUG: sleeping function called from invalid context at net/core/sock.c:3782
Total: 30, Passed: 30 (100.0%), Failed: 0, Not Run: 0
##############################
Test: TestRunner_mesh-tester - FAIL
Desc: Run mesh-tester with test-runner
Output:
Total: 10, Passed: 8 (80.0%), Failed: 2, Not Run: 0

Failed Test Cases
Mesh - Send cancel - 1                               Timed out    2.737 seconds
Mesh - Send cancel - 2                               Timed out    1.996 seconds
##############################
Test: IncrementalBuild - PENDING
Desc: Incremental build with the patches in the series
Output:



---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [v5] Bluetooth: btrtl: Add firmware format v3 support
  2026-05-13  9:24 [PATCH v5] " Hilda Wu
@ 2026-05-13 10:31 ` bluez.test.bot
  0 siblings, 0 replies; 10+ messages in thread
From: bluez.test.bot @ 2026-05-13 10:31 UTC (permalink / raw)
  To: linux-bluetooth, hildawu

[-- Attachment #1: Type: text/plain, Size: 882 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1094070

---Test result---

Test Summary:
CheckPatch                    PASS      1.75 seconds
GitLint                       PASS      0.34 seconds
SubjectPrefix                 PASS      0.13 seconds
BuildKernel                   PASS      24.61 seconds
CheckAllWarning               PASS      27.06 seconds
CheckSparse                   PASS      25.95 seconds
BuildKernel32                 PASS      24.65 seconds
TestRunnerSetup               PASS      519.47 seconds
IncrementalBuild              PASS      23.66 seconds



https://github.com/bluez/bluetooth-next/pull/183

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v5] Bluetooth: btrtl: Add firmware format v3 support
  2025-07-10 15:58 ` [PATCH v3 1/2] " Luiz Augusto von Dentz
@ 2026-06-24  5:33   ` Hilda Wu
  2026-06-24  8:06     ` Paul Menzel
  2026-06-24  9:14     ` [v5] " bluez.test.bot
  0 siblings, 2 replies; 10+ messages in thread
From: Hilda Wu @ 2026-06-24  5:33 UTC (permalink / raw)
  To: marcel
  Cc: luiz.dentz, linux-bluetooth, linux-kernel, alex_lu, jason_mao,
	zoey_zhou, max.chou, kidman

Realtek updated its Bluetooth firmware format to v3.
This patch extends the btrtl driver to recognise and parse the new v3 file
format, including:
- New signature string and image ID definitions
- Extension of btrtl_device_info to store v3-specific metadata
- Logic to extract and load firmware data out of v3 images
- Maintains compatibility with existing v2 firmware format

This is required for future Realtek Bluetooth chips that ship with
v3 firmware.
The RTL8922D is the first IC to use firmware format V3, so the following
example uses the RTL8922D's log as expected fw format v3 output:

Bluetooth: btrtl_read_chip_id() hci0: RTL: chip_id status=0x00 id=0x37
Bluetooth: btrtl_initialize() hci0: RTL: examining hci_ver=0d
hci_rev=000d lmp_ver=0d lmp_subver=8922
Bluetooth: rtl_read_rom_version() hci0: RTL: rom_version status=0 version=1
Bluetooth: btrtl_initialize() hci0: RTL: btrtl_initialize: key id 0
Bluetooth: rtl_load_file() hci0: RTL: loading rtl_bt/rtl8922du_fw.bin
Bluetooth: rtl_load_file() hci0: RTL: loading rtl_bt/rtl8922du_config.bin
Bluetooth: rtlbt_parse_firmware_v3() hci0: RTL: key id 0
Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image (f000:00), chip id
55, cut 0x02, len 00007185
Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image version: 35fd7908
Bluetooth: rtlbt_parse_config() hci0: RTL: config file:
rtl_bt/rtl8922du_config_f000.bin
Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image (f002:00), chip id
55, cut 0x02, len 000078f5
Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image version: 47b6874d
Bluetooth: rtlbt_parse_config() hci0: RTL: config file:
rtl_bt/rtl8922du_config_f002.bin
Bluetooth: rtlbt_parse_firmware_v3() hci0: RTL: image payload total len:
0x0000ea7a
Bluetooth: rtl_finalize_download() hci0: RTL: Watchdog reset status 00
Bluetooth: rtl_finalize_download() hci0: RTL: fw version 0x47b6874d

Signed-off-by: Alex Lu <alex_lu@realsil.com.cn>
Signed-off-by: Zoey Zhou <zoey_zhou@realsil.com.cn>
Signed-off-by: Hilda Wu <hildawu@realtek.com>

---
V4 -> V5::
- Add independent support for RTL8922D section
- Introduce macros to improve code readability
- Document firmware format v3 and its differences
- Align implementation with reviewer feedback

V3 -> V4:
- Rework skb->data access and add clarifying comments
- Fix latent issues

V2 -> V3:
- Address coccinelle warning

V1 -> V2:
- Add missing symbols
- Resolve build warnings
---
 drivers/bluetooth/btrtl.c | 698 +++++++++++++++++++++++++++++++++++++-
 drivers/bluetooth/btrtl.h | 102 ++++++
 drivers/bluetooth/btusb.c |   3 +
 3 files changed, 786 insertions(+), 17 deletions(-)

diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 49ecb18fea45..450b32bcfa63 100644
--- a/drivers/bluetooth/btrtl.c
+++ b/drivers/bluetooth/btrtl.c
@@ -22,6 +22,12 @@
 #define RTL_CHIP_8723CS_XX	5
 #define RTL_EPATCH_SIGNATURE	"Realtech"
 #define RTL_EPATCH_SIGNATURE_V2	"RTBTCore"
+#define RTL_EPATCH_SIGNATURE_V3	"BTNIC003"
+#define RTL_PATCH_V3_1		0x01
+#define RTL_PATCH_V3_2		0x02
+#define IMAGE_ID_F000		0xf000
+#define IMAGE_ID_F001		0xf001
+#define IMAGE_ID_F002		0xf002
 #define RTL_ROM_LMP_8703B	0x8703
 #define RTL_ROM_LMP_8723A	0x1200
 #define RTL_ROM_LMP_8723B	0x8723
@@ -33,7 +39,14 @@
 #define RTL_ROM_LMP_8922A	0x8922
 #define RTL_CONFIG_MAGIC	0x8723ab55
 
-#define RTL_VSC_OP_COREDUMP	0xfcff
+#define RTL_VSC_OP_DOWNLOAD_CMD		0xfc20
+#define RTL_VSC_OP_READ_VENDER		0xfc61
+#define RTL_VSC_OP_WRITE_VENDOR		0xfc62
+#define RTL_VSC_OP_READ_ROM_VER		0xfc6d
+#define RTL_VSC_OP_READ_CHIP_ID		0xfc6f
+#define RTL_VSC_OP_COREDUMP		0xfcff
+#define RTL_VSC_OP_CHECK_DOWNLOAD_STATE	0xfdcf
+#define RTL_VSC_OP_WDG_RESET_CMD	0xfc8e
 
 #define IC_MATCH_FL_LMPSUBV	(1 << 0)
 #define IC_MATCH_FL_HCIREV	(1 << 1)
@@ -50,12 +63,16 @@
 
 #define	RTL_CHIP_SUBVER (&(struct rtl_vendor_cmd) {{0x10, 0x38, 0x04, 0x28, 0x80}})
 #define	RTL_CHIP_REV    (&(struct rtl_vendor_cmd) {{0x10, 0x3A, 0x04, 0x28, 0x80}})
-#define	RTL_SEC_PROJ    (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0xAD, 0x00, 0xb0}})
+#define	RTL_SEC_PROJ_V2 (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0xAD, 0x00, 0xb0}})
+#define	RTL_SEC_PROJ_V3 (&(struct rtl_vendor_cmd) {{0x10, 0xA4, 0x0D, 0x01, 0xa0}})
 
 #define RTL_PATCH_SNIPPETS		0x01
 #define RTL_PATCH_DUMMY_HEADER		0x02
 #define RTL_PATCH_SECURITY_HEADER	0x03
 
+#define CHIP_ID_V3_BASE		55
+#define RTL_VENDOR_WRITE_TYPE		0x21
+
 enum btrtl_chip_id {
 	CHIP_ID_8723A,
 	CHIP_ID_8723B,
@@ -99,8 +116,11 @@ struct btrtl_device_info {
 	int cfg_len;
 	bool drop_fw;
 	int project_id;
+	u32 opcode;
+	u8 fw_type;
 	u8 key_id;
 	struct list_head patch_subsecs;
+	struct list_head patch_images;
 };
 
 static const struct id_table ic_id_table[] = {
@@ -371,6 +391,33 @@ static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
 	return &ic_id_table[i];
 }
 
+static int btrtl_read_chip_id(struct hci_dev *hdev, u8 *chip_id)
+{
+	struct rtl_rp_read_chip_id *rp;
+	struct sk_buff *skb;
+	int ret = 0;
+
+	skb = __hci_cmd_sync(hdev, RTL_VSC_OP_READ_CHIP_ID, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	rp = skb_pull_data(skb, sizeof(*rp));
+	if (!rp) {
+		ret = -EIO;
+		goto out;
+	}
+
+	rtl_dev_info(hdev, "chip_id status=0x%02x id=0x%02x",
+		     rp->status, rp->chip_id);
+
+	if (chip_id)
+		*chip_id = rp->chip_id;
+
+out:
+	kfree_skb(skb);
+	return ret;
+}
+
 static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
 {
 	struct sk_buff *skb;
@@ -397,8 +444,7 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
 	struct rtl_rom_version_evt *rom_version;
 	struct sk_buff *skb;
 
-	/* Read RTL ROM version command */
-	skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
+	skb = __hci_cmd_sync(hdev, RTL_VSC_OP_READ_ROM_VER, 0, NULL, HCI_INIT_TIMEOUT);
 	if (IS_ERR(skb)) {
 		rtl_dev_err(hdev, "Read ROM version failed (%ld)",
 			    PTR_ERR(skb));
@@ -427,7 +473,7 @@ static int btrtl_vendor_read_reg16(struct hci_dev *hdev,
 	struct sk_buff *skb;
 	int err = 0;
 
-	skb = __hci_cmd_sync(hdev, 0xfc61, sizeof(*cmd), cmd,
+	skb = __hci_cmd_sync(hdev, RTL_VSC_OP_READ_VENDER, sizeof(*cmd), cmd,
 			     HCI_INIT_TIMEOUT);
 	if (IS_ERR(skb)) {
 		err = PTR_ERR(skb);
@@ -449,6 +495,26 @@ static int btrtl_vendor_read_reg16(struct hci_dev *hdev,
 	return 0;
 }
 
+static int btrtl_vendor_write_mem(struct hci_dev *hdev, u32 addr, u32 val)
+{
+	struct rtl_vendor_write_cmd cp;
+	struct sk_buff *skb;
+	int err = 0;
+
+	cp.type = RTL_VENDOR_WRITE_TYPE;
+	cp.addr = cpu_to_le32(addr);
+	cp.val = cpu_to_le32(val);
+	skb = __hci_cmd_sync(hdev, RTL_VSC_OP_WRITE_VENDOR, sizeof(cp), &cp, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		bt_dev_err(hdev, "RTL: Write mem32 failed (%d)", err);
+		return err;
+	}
+
+	kfree_skb(skb);
+	return 0;
+}
+
 static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
 {
 	void *data = iov->data;
@@ -462,6 +528,31 @@ static void *rtl_iov_pull_data(struct rtl_iovec *iov, u32 len)
 	return data;
 }
 
+
+static void btrtl_insert_ordered_patch_image(struct rtl_section_patch_image *image,
+					     struct btrtl_device_info *btrtl_dev)
+{
+	struct list_head *pos;
+	struct list_head *next;
+	struct rtl_section_patch_image *node;
+
+	list_for_each_safe(pos, next, &btrtl_dev->patch_images) {
+		node = list_entry(pos, struct rtl_section_patch_image, list);
+
+		if (node->image_id > image->image_id) {
+			__list_add(&image->list, pos->prev, pos);
+			return;
+		}
+
+		if (node->image_id == image->image_id &&
+		    node->index > image->index) {
+			__list_add(&image->list, pos->prev, pos);
+			return;
+		}
+	}
+	__list_add(&image->list, pos->prev, pos);
+}
+
 static void btrtl_insert_ordered_subsec(struct rtl_subsection *node,
 					struct btrtl_device_info *btrtl_dev)
 {
@@ -633,6 +724,279 @@ static int rtlbt_parse_firmware_v2(struct hci_dev *hdev,
 	}
 
 	*_buf = ptr;
+	btrtl_dev->fw_type = FW_TYPE_V2;
+	return len;
+}
+
+static int rtlbt_parse_config(struct hci_dev *hdev,
+			      struct rtl_section_patch_image *patch_image,
+			      struct btrtl_device_info *btrtl_dev)
+{
+	const struct id_table *ic_info = NULL;
+	const struct firmware *fw;
+	char tmp_name[32];
+	char filename[64];
+	u8 *cfg_buf;
+	char *str;
+	char *p;
+	size_t len;
+	int ret;
+
+	if (btrtl_dev && btrtl_dev->ic_info)
+		ic_info = btrtl_dev->ic_info;
+
+	if (!ic_info)
+		return -EINVAL;
+
+	str = ic_info->cfg_name;
+	if (btrtl_dev->fw_type == FW_TYPE_V3_1) {
+		if (!patch_image->image_id && !patch_image->index) {
+			snprintf(filename, sizeof(filename), "%s.bin", str);
+			goto load_fw;
+		}
+		goto done;
+	}
+
+	len = strlen(str);
+	if (len > sizeof(tmp_name) - 1)
+		len = sizeof(tmp_name) - 1;
+	memcpy(tmp_name, str, len);
+	tmp_name[len] = '\0';
+
+	str = tmp_name;
+	p = strsep(&str, ".");
+
+	ret = snprintf(filename, sizeof(filename), "%s", p);
+	if (patch_image->config_rule && patch_image->need_config) {
+		switch (patch_image->image_id) {
+		case IMAGE_ID_F000:
+		case IMAGE_ID_F001:
+		case IMAGE_ID_F002:
+			ret += snprintf(filename + ret, sizeof(filename) - ret,
+					"_%04x", patch_image->image_id);
+			break;
+		default:
+			goto done;
+		}
+	} else {
+		goto done;
+	}
+
+	snprintf(filename + ret, sizeof(filename) - ret, ".%s", str ? str : "bin");
+
+load_fw:
+	rtl_dev_info(hdev, "config file: %s", filename);
+	ret = request_firmware(&fw, filename, &hdev->dev);
+	if (ret < 0) {
+		if (btrtl_dev->fw_type == FW_TYPE_V3_2) {
+			len = 4;
+			cfg_buf = kvmalloc(len, GFP_KERNEL);
+			if (!cfg_buf)
+				return -ENOMEM;
+
+			memset(cfg_buf, 0xff, len);
+			patch_image->cfg_buf = cfg_buf;
+			patch_image->cfg_len = len;
+			return 0;
+		}
+		goto err_req_fw;
+	}
+	rtl_dev_info(hdev, "config file: %s found", filename);
+	cfg_buf = kvmalloc(fw->size, GFP_KERNEL);
+	if (!cfg_buf) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	memcpy(cfg_buf, fw->data, fw->size);
+	len = fw->size;
+	release_firmware(fw);
+
+	patch_image->cfg_buf = cfg_buf;
+	patch_image->cfg_len = len;
+done:
+	return 0;
+err:
+	release_firmware(fw);
+err_req_fw:
+	rtl_dev_info(hdev, "config file: [%s] not found", filename);
+	return ret;
+}
+
+static int rtlbt_parse_section_v3(struct hci_dev *hdev,
+				  struct btrtl_device_info *btrtl_dev,
+				  u32 opcode, u8 *data, u32 len)
+{
+	struct rtl_section_patch_image *patch_image;
+	struct rtl_patch_image_hdr *hdr;
+	u16 image_id;
+	u16 chip_id;
+	size_t patch_image_len;
+	u8 *ptr;
+	int ret = 0;
+	size_t i;
+	struct rtl_iovec iov = {
+		.data = data,
+		.len  = len,
+	};
+
+	hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
+	if (!hdr)
+		return -EINVAL;
+
+	if (btrtl_dev->opcode && btrtl_dev->opcode != opcode) {
+		rtl_dev_err(hdev, "invalid opcode 0x%02x", opcode);
+		return -EINVAL;
+	}
+
+	if (!btrtl_dev->opcode) {
+		btrtl_dev->opcode = opcode;
+		switch (btrtl_dev->opcode) {
+		case RTL_PATCH_V3_1:
+			btrtl_dev->fw_type = FW_TYPE_V3_1;
+			break;
+		case RTL_PATCH_V3_2:
+			btrtl_dev->fw_type = FW_TYPE_V3_2;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	patch_image_len = (u32)le64_to_cpu(hdr->patch_image_len);
+	chip_id = le16_to_cpu(hdr->chip_id);
+	image_id = le16_to_cpu(hdr->image_id);
+	rtl_dev_info(hdev, "image (%04x:%02x), chip id %u, cut 0x%02x, len %08zx"
+		     , image_id, hdr->index, chip_id, hdr->ic_cut,
+		     patch_image_len);
+
+	if (btrtl_dev->key_id != hdr->key_id) {
+		rtl_dev_err(hdev, "invalid key_id (%u, %u)", hdr->key_id,
+			    btrtl_dev->key_id);
+		return -EINVAL;
+	}
+
+	if (hdr->ic_cut != btrtl_dev->rom_version + 1) {
+		rtl_dev_info(hdev, "unused ic_cut (%u, %u)", hdr->ic_cut,
+			    btrtl_dev->rom_version + 1);
+		return -EINVAL;
+	}
+
+	if (btrtl_dev->fw_type == FW_TYPE_V3_1 && !btrtl_dev->project_id)
+		btrtl_dev->project_id = chip_id;
+
+	if (btrtl_dev->fw_type == FW_TYPE_V3_2 &&
+	    chip_id != btrtl_dev->project_id) {
+		rtl_dev_err(hdev, "invalid chip_id (%u, %d)", chip_id,
+			    btrtl_dev->project_id);
+		return -EINVAL;
+	}
+
+	ptr = rtl_iov_pull_data(&iov, patch_image_len);
+	if (!ptr)
+		return -ENODATA;
+
+	patch_image = kzalloc_obj(*patch_image);
+	if (!patch_image)
+		return -ENOMEM;
+	patch_image->index = hdr->index;
+	patch_image->image_id = image_id;
+	patch_image->config_rule = hdr->config_rule;
+	patch_image->need_config = hdr->need_config;
+
+	for (i = 0; i < DL_FIX_ADDR_MAX; i++) {
+		patch_image->fix[i].addr =
+			(u32)le64_to_cpu(hdr->addr_fix[i * 2]);
+		patch_image->fix[i].value =
+			(u32)le64_to_cpu(hdr->addr_fix[i * 2 + 1]);
+	}
+
+	patch_image->image_len = patch_image_len;
+	patch_image->image_data = kvmalloc(patch_image_len, GFP_KERNEL);
+	if (!patch_image->image_data) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	memcpy(patch_image->image_data, ptr, patch_image_len);
+	patch_image->image_ver =
+		get_unaligned_le32(ptr + patch_image->image_len - 4);
+	rtl_dev_info(hdev, "image version: %08x", patch_image->image_ver);
+
+	rtlbt_parse_config(hdev, patch_image, btrtl_dev);
+
+	ret = patch_image->image_len;
+
+	btrtl_insert_ordered_patch_image(patch_image, btrtl_dev);
+
+	return ret;
+err:
+	kfree(patch_image);
+	return ret;
+}
+
+static int rtlbt_parse_firmware_v3(struct hci_dev *hdev,
+				   struct btrtl_device_info *btrtl_dev)
+{
+	struct rtl_epatch_header_v3 *hdr;
+	int rc;
+	u32 num_sections;
+	struct rtl_section_v3 *section;
+	u32 section_len;
+	u32 opcode;
+	int len = 0;
+	int i;
+	u8 *ptr;
+	struct rtl_iovec iov = {
+		.data = btrtl_dev->fw_data,
+		.len  = btrtl_dev->fw_len,
+	};
+
+	rtl_dev_info(hdev, "key id %u", btrtl_dev->key_id);
+
+	hdr = rtl_iov_pull_data(&iov, sizeof(*hdr));
+	if (!hdr)
+		return -EINVAL;
+	num_sections = le32_to_cpu(hdr->num_sections);
+
+	rtl_dev_dbg(hdev, "timpstamp %08x-%08x", *((u32 *)hdr->timestamp),
+		    *((u32 *)(hdr->timestamp + 4)));
+
+	for (i = 0; i < num_sections; i++) {
+		section = rtl_iov_pull_data(&iov, sizeof(*section));
+		if (!section)
+			break;
+
+		section_len = (u32)le64_to_cpu(section->len);
+		opcode = le32_to_cpu(section->opcode);
+
+		rtl_dev_dbg(hdev, "opcode 0x%04x", section->opcode);
+
+		ptr = rtl_iov_pull_data(&iov, section_len);
+		if (!ptr)
+			break;
+
+		rc = 0;
+		switch (opcode) {
+		case RTL_PATCH_V3_1:
+		case RTL_PATCH_V3_2:
+			rc = rtlbt_parse_section_v3(hdev, btrtl_dev, opcode,
+						    ptr, section_len);
+			break;
+		default:
+			rtl_dev_warn(hdev, "Unknown opcode %08x", opcode);
+			break;
+		}
+		if (rc < 0) {
+			rtl_dev_err(hdev, "Parse section (%u) err (%d)",
+				    opcode, rc);
+			continue;
+		}
+		len += rc;
+	}
+
+	rtl_dev_info(hdev, "image payload total len: 0x%08x", len);
+	if (!len)
+		return -ENODATA;
+
 	return len;
 }
 
@@ -678,6 +1042,9 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
 	if (btrtl_dev->fw_len <= 8)
 		return -EINVAL;
 
+	if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE_V3, 8))
+		return rtlbt_parse_firmware_v3(hdev, btrtl_dev);
+
 	if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8))
 		min_size = sizeof(struct rtl_epatch_header) +
 				sizeof(extension_sig) + 3;
@@ -813,10 +1180,11 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
 	memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
 
 	*_buf = buf;
+	btrtl_dev->fw_type = FW_TYPE_V1;
 	return len;
 }
 
-static int rtl_download_firmware(struct hci_dev *hdev,
+static int rtl_download_firmware(struct hci_dev *hdev, u8 fw_type,
 				 const unsigned char *data, int fw_len)
 {
 	struct rtl_download_cmd *dl_cmd;
@@ -827,6 +1195,13 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 	int j = 0;
 	struct sk_buff *skb;
 	struct hci_rp_read_local_version *rp;
+	u8 dl_rp_len = sizeof(struct rtl_download_response);
+
+	if (is_v3_fw(fw_type)) {
+		j = 1;
+		if (fw_type == FW_TYPE_V3_2)
+			dl_rp_len++;
+	}
 
 	dl_cmd = kmalloc_obj(*dl_cmd);
 	if (!dl_cmd)
@@ -840,15 +1215,15 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 			j = 1;
 
 		if (i == (frag_num - 1)) {
-			dl_cmd->index |= 0x80; /* data end */
+			if (!is_v3_fw(fw_type))
+				dl_cmd->index |= 0x80; /* data end */
 			frag_len = fw_len % RTL_FRAG_LEN;
 		}
 		rtl_dev_dbg(hdev, "download fw (%d/%d). index = %d", i,
 				frag_num, dl_cmd->index);
 		memcpy(dl_cmd->data, data, frag_len);
 
-		/* Send download command */
-		skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd,
+		skb = __hci_cmd_sync(hdev, RTL_VSC_OP_DOWNLOAD_CMD, frag_len + 1, dl_cmd,
 				     HCI_INIT_TIMEOUT);
 		if (IS_ERR(skb)) {
 			rtl_dev_err(hdev, "download fw command failed (%ld)",
@@ -857,7 +1232,7 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 			goto out;
 		}
 
-		if (skb->len != sizeof(struct rtl_download_response)) {
+		if (skb->len != dl_rp_len) {
 			rtl_dev_err(hdev, "download fw event length mismatch");
 			kfree_skb(skb);
 			ret = -EIO;
@@ -868,6 +1243,9 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 		data += RTL_FRAG_LEN;
 	}
 
+	if (is_v3_fw(fw_type))
+		goto out;
+
 	skb = btrtl_read_local_version(hdev);
 	if (IS_ERR(skb)) {
 		ret = PTR_ERR(skb);
@@ -885,6 +1263,237 @@ static int rtl_download_firmware(struct hci_dev *hdev,
 	return ret;
 }
 
+static int rtl_check_download_state(struct hci_dev *hdev,
+				    struct btrtl_device_info *btrtl_dev)
+{
+	struct sk_buff *skb;
+	int ret = 0;
+	u8 *state;
+
+	skb = __hci_cmd_sync(hdev, RTL_VSC_OP_CHECK_DOWNLOAD_STATE, 0, NULL, HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb)) {
+		rtl_dev_err(hdev, "write tb error %lu", PTR_ERR(skb));
+		return -EIO;
+	}
+
+	/* Other driver might be downloading the combined firmware. */
+	state = skb_pull_data(skb, sizeof(*state));
+	if (state && *state == 0x03) {
+		btrealtek_set_flag(hdev, REALTEK_DOWNLOADING);
+		ret = btrealtek_wait_on_flag_timeout(hdev, REALTEK_DOWNLOADING,
+						     TASK_INTERRUPTIBLE,
+						     msecs_to_jiffies(5000));
+		if (ret == -EINTR) {
+			bt_dev_err(hdev, "Firmware loading interrupted");
+			goto out;
+		}
+
+		if (ret) {
+			bt_dev_err(hdev, "Firmware loading timeout");
+			ret = -ETIMEDOUT;
+		} else {
+			ret = -EALREADY;
+		}
+
+	}
+
+out:
+	kfree_skb(skb);
+	return ret;
+}
+
+static int rtl_finalize_download(struct hci_dev *hdev,
+				 struct btrtl_device_info *btrtl_dev)
+{
+	struct hci_rp_read_local_version *rp_ver;
+	u8 params[2] = { 0x03, 0xb2 };
+	struct sk_buff *skb;
+	int ret = 0;
+	u16 opcode;
+	u32 len;
+	u8 *p;
+
+	opcode = RTL_VSC_OP_WDG_RESET_CMD;
+	len = 2;
+	if (btrtl_dev->opcode == RTL_PATCH_V3_1) {
+		opcode = RTL_VSC_OP_DOWNLOAD_CMD;
+		params[0] = 0x80;
+		len = 1;
+	}
+	skb = __hci_cmd_sync(hdev, opcode, len, params, HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb)) {
+		rtl_dev_err(hdev, "Watchdog reset err (%ld)", PTR_ERR(skb));
+		return -EIO;
+	}
+	p = skb_pull_data(skb, 1);
+	if (!p) {
+		ret = -ENODATA;
+		goto out;
+	}
+	rtl_dev_info(hdev, "Watchdog reset status %02x", *p);
+	kfree_skb(skb);
+
+	skb = btrtl_read_local_version(hdev);
+	if (IS_ERR(skb)) {
+		ret = PTR_ERR(skb);
+		rtl_dev_err(hdev, "read local version failed (%d)", ret);
+		return ret;
+	}
+
+	rp_ver = skb_pull_data(skb, sizeof(*rp_ver));
+	if (rp_ver)
+		rtl_dev_info(hdev, "fw version 0x%04x%04x",
+			     __le16_to_cpu(rp_ver->hci_rev),
+			     __le16_to_cpu(rp_ver->lmp_subver));
+out:
+	kfree_skb(skb);
+	return ret;
+}
+
+static int rtl_security_check(struct hci_dev *hdev,
+			      struct btrtl_device_info *btrtl_dev)
+{
+	struct rtl_section_patch_image *tmp = NULL;
+	struct rtl_section_patch_image *image = NULL;
+	u32 val;
+	int ret;
+
+	list_for_each_entry_reverse(tmp, &btrtl_dev->patch_images, list) {
+		/* Check security hdr */
+		if (!tmp->fix[DL_FIX_SEC_HDR_ADDR].value ||
+		    !tmp->fix[DL_FIX_SEC_HDR_ADDR].addr ||
+		    tmp->fix[DL_FIX_SEC_HDR_ADDR].addr == 0xffffffff)
+			continue;
+		rtl_dev_info(hdev, "addr 0x%08x, value 0x%08x",
+			     tmp->fix[DL_FIX_SEC_HDR_ADDR].addr,
+			     tmp->fix[DL_FIX_SEC_HDR_ADDR].value);
+		image = tmp;
+		break;
+	}
+
+	if (!image)
+		return 0;
+
+	rtl_dev_info(hdev, "sec image (%04x:%02x)", image->image_id,
+		     image->index);
+	val = image->fix[DL_FIX_PATCH_ADDR].value + image->image_len -
+					image->fix[DL_FIX_SEC_HDR_ADDR].value;
+	ret = btrtl_vendor_write_mem(hdev, image->fix[DL_FIX_PATCH_ADDR].addr,
+				     val);
+	if (ret) {
+		rtl_dev_err(hdev, "write sec reg failed (%d)", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int rtl_download_firmware_v3(struct hci_dev *hdev,
+				    struct btrtl_device_info *btrtl_dev)
+{
+	struct rtl_section_patch_image *image, *tmp;
+	struct rtl_rp_dl_v3 *rp;
+	struct sk_buff *skb;
+	u8 *fw_data;
+	int fw_len;
+	int ret = 0;
+	u8 i;
+
+	if (btrtl_dev->fw_type == FW_TYPE_V3_2) {
+		ret = rtl_check_download_state(hdev, btrtl_dev);
+		if (ret) {
+			if (ret == -EALREADY)
+				return 0;
+			return ret;
+		}
+	}
+
+	list_for_each_entry_safe(image, tmp, &btrtl_dev->patch_images, list) {
+		rtl_dev_dbg(hdev, "image (%04x:%02x)", image->image_id,
+			    image->index);
+
+		for (i = DL_FIX_CI_ID; i < DL_FIX_ADDR_MAX; i++) {
+			if (!image->fix[i].addr ||
+			    image->fix[i].addr == 0xffffffff) {
+				rtl_dev_dbg(hdev, "no need to write addr %08x",
+					    image->fix[i].addr);
+				continue;
+			}
+			rtl_dev_dbg(hdev, "write addr and val, 0x%08x, 0x%08x",
+				    image->fix[i].addr, image->fix[i].value);
+			if (btrtl_vendor_write_mem(hdev, image->fix[i].addr,
+						   image->fix[i].value)) {
+				rtl_dev_err(hdev, "write reg failed");
+				ret = -EIO;
+				goto done;
+			}
+		}
+
+		fw_len = image->image_len + image->cfg_len;
+		fw_data = kvmalloc(fw_len, GFP_KERNEL);
+		if (!fw_data) {
+			rtl_dev_err(hdev, "Couldn't alloc buf for image data");
+			ret = -ENOMEM;
+			goto done;
+		}
+		memcpy(fw_data, image->image_data, image->image_len);
+		if (image->cfg_len > 0)
+			memcpy(fw_data + image->image_len, image->cfg_buf,
+			       image->cfg_len);
+
+		rtl_dev_dbg(hdev, "patch image (%04x:%02x). len: %d",
+			    image->image_id, image->index, fw_len);
+		rtl_dev_dbg(hdev, "fw_data %p, image buf %p, len %u", fw_data,
+			    image->image_data, image->image_len);
+
+		ret = rtl_download_firmware(hdev, btrtl_dev->fw_type, fw_data,
+					    fw_len);
+		kvfree(fw_data);
+		if (ret < 0) {
+			rtl_dev_err(hdev, "download firmware failed (%d)", ret);
+			goto done;
+		}
+
+		if (image->list.next != &btrtl_dev->patch_images &&
+		    image->image_id == tmp->image_id)
+			continue;
+
+		if (btrtl_dev->fw_type == FW_TYPE_V3_1)
+			continue;
+
+		i = 0x80;
+		skb = __hci_cmd_sync(hdev, RTL_VSC_OP_DOWNLOAD_CMD, 1, &i, HCI_CMD_TIMEOUT);
+		if (IS_ERR(skb)) {
+			ret = -EIO;
+			rtl_dev_err(hdev, "Failed to issue last cmd fc20, %ld",
+				    PTR_ERR(skb));
+			goto done;
+		}
+		ret = 2;
+		rp = skb_pull_data(skb, sizeof(*rp));
+		if (rp)
+			ret = rp->err;
+		kfree_skb(skb);
+		if (ret == 2) {
+			/* Verification failure */
+			ret = -EFAULT;
+			goto done;
+		}
+	}
+
+	if (btrtl_dev->fw_type == FW_TYPE_V3_1) {
+		ret = rtl_security_check(hdev, btrtl_dev);
+		if (ret) {
+			rtl_dev_err(hdev, "Security check failed (%d)", ret);
+			goto done;
+		}
+	}
+
+	ret = rtl_finalize_download(hdev, btrtl_dev);
+
+done:
+	return ret;
+}
+
 static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
 {
 	const struct firmware *fw;
@@ -918,7 +1527,7 @@ static int btrtl_setup_rtl8723a(struct hci_dev *hdev,
 		return -EINVAL;
 	}
 
-	return rtl_download_firmware(hdev, btrtl_dev->fw_data,
+	return rtl_download_firmware(hdev, FW_TYPE_V0, btrtl_dev->fw_data,
 				     btrtl_dev->fw_len);
 }
 
@@ -933,7 +1542,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
 	if (ret < 0)
 		goto out;
 
-	if (btrtl_dev->cfg_len > 0) {
+	if (!is_v3_fw(btrtl_dev->fw_type) && btrtl_dev->cfg_len > 0) {
 		tbuff = kvzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
 		if (!tbuff) {
 			ret = -ENOMEM;
@@ -949,9 +1558,14 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
 		fw_data = tbuff;
 	}
 
+	if (is_v3_fw(btrtl_dev->fw_type)) {
+		ret = rtl_download_firmware_v3(hdev, btrtl_dev);
+		goto out;
+	}
+
 	rtl_dev_info(hdev, "cfg_sz %d, total sz %d", btrtl_dev->cfg_len, ret);
 
-	ret = rtl_download_firmware(hdev, fw_data, ret);
+	ret = rtl_download_firmware(hdev, btrtl_dev->fw_type, fw_data, ret);
 
 out:
 	kvfree(fw_data);
@@ -1021,7 +1635,7 @@ static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type)
 	const unsigned char cmd_buf[] = {0x00, 0x94, 0xa0, 0x00, 0xb0};
 
 	/* Read RTL chip type command */
-	skb = __hci_cmd_sync(hdev, 0xfc61, 5, cmd_buf, HCI_INIT_TIMEOUT);
+	skb = __hci_cmd_sync(hdev, RTL_VSC_OP_READ_VENDER, 5, cmd_buf, HCI_INIT_TIMEOUT);
 	if (IS_ERR(skb)) {
 		rtl_dev_err(hdev, "Read chip type failed (%ld)",
 			    PTR_ERR(skb));
@@ -1047,6 +1661,7 @@ static int rtl_read_chip_type(struct hci_dev *hdev, u8 *type)
 void btrtl_free(struct btrtl_device_info *btrtl_dev)
 {
 	struct rtl_subsection *entry, *tmp;
+	struct rtl_section_patch_image *image, *next;
 
 	kvfree(btrtl_dev->fw_data);
 	kvfree(btrtl_dev->cfg_data);
@@ -1056,6 +1671,13 @@ void btrtl_free(struct btrtl_device_info *btrtl_dev)
 		kfree(entry);
 	}
 
+	list_for_each_entry_safe(image, next, &btrtl_dev->patch_images, list) {
+		list_del(&image->list);
+		kvfree(image->image_data);
+		kvfree(image->cfg_buf);
+		kfree(image);
+	}
+
 	kfree(btrtl_dev);
 }
 EXPORT_SYMBOL_GPL(btrtl_free);
@@ -1063,7 +1685,7 @@ EXPORT_SYMBOL_GPL(btrtl_free);
 struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 					   const char *postfix)
 {
-	struct btrealtek_data *coredump_info = hci_get_priv(hdev);
+	struct btrealtek_data *btrtl_data  = hci_get_priv(hdev);
 	struct btrtl_device_info *btrtl_dev;
 	struct sk_buff *skb;
 	struct hci_rp_read_local_version *resp;
@@ -1072,6 +1694,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 	char cfg_name[40];
 	u16 hci_rev, lmp_subver;
 	u8 hci_ver, lmp_ver, chip_type = 0;
+	u8 chip_id = 0;
 	int ret;
 	int rc;
 	u8 key_id;
@@ -1084,8 +1707,15 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 	}
 
 	INIT_LIST_HEAD(&btrtl_dev->patch_subsecs);
+	INIT_LIST_HEAD(&btrtl_dev->patch_images);
 
 check_version:
+	ret = btrtl_read_chip_id(hdev, &chip_id);
+	if (!ret && chip_id >= CHIP_ID_V3_BASE) {
+		btrtl_dev->project_id = chip_id;
+		goto read_local_ver;
+	}
+
 	ret = btrtl_vendor_read_reg16(hdev, RTL_CHIP_SUBVER, reg_val);
 	if (ret < 0)
 		goto err_free;
@@ -1108,6 +1738,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 		}
 	}
 
+read_local_ver:
 	skb = btrtl_read_local_version(hdev);
 	if (IS_ERR(skb)) {
 		ret = PTR_ERR(skb);
@@ -1185,7 +1816,11 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 		goto err_free;
 	}
 
-	rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ, reg_val);
+	if (btrtl_dev->project_id >= CHIP_ID_V3_BASE)
+		rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ_V3, reg_val);
+	else
+		rc = btrtl_vendor_read_reg16(hdev, RTL_SEC_PROJ_V2, reg_val);
+
 	if (rc < 0)
 		goto err_free;
 
@@ -1243,7 +1878,7 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 		hci_set_msft_opcode(hdev, 0xFCF0);
 
 	if (btrtl_dev->ic_info)
-		coredump_info->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
+		btrtl_data->rtl_dump.controller = btrtl_dev->ic_info->hw_info;
 
 	return btrtl_dev;
 
@@ -1416,6 +2051,35 @@ int btrtl_shutdown_realtek(struct hci_dev *hdev)
 }
 EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek);
 
+
+int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+	struct hci_event_hdr *hdr;
+	u8 *p;
+
+	if (!clone)
+		goto out;
+
+	hdr = skb_pull_data(clone, sizeof(*hdr));
+	if (!hdr || hdr->evt != HCI_VENDOR_PKT)
+		goto out;
+
+	p = skb_pull_data(clone, 1);
+	if (!p)
+		goto out;
+	switch (*p) {
+	case 0x77:
+		if (btrealtek_test_and_clear_flag(hdev, REALTEK_DOWNLOADING))
+			btrealtek_wake_up_flag(hdev, REALTEK_DOWNLOADING);
+		break;
+	}
+out:
+	consume_skb(clone);
+	return hci_recv_frame(hdev, skb);
+}
+EXPORT_SYMBOL_GPL(btrtl_recv_event);
+
 static unsigned int btrtl_convert_baudrate(u32 device_baudrate)
 {
 	switch (device_baudrate) {
diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
index a2d9d34f9fb0..dd4acdf29b2f 100644
--- a/drivers/bluetooth/btrtl.h
+++ b/drivers/bluetooth/btrtl.h
@@ -12,6 +12,19 @@
 #define rtl_dev_info(dev, fmt, ...) bt_dev_info(dev, "RTL: " fmt, ##__VA_ARGS__)
 #define rtl_dev_dbg(dev, fmt, ...) bt_dev_dbg(dev, "RTL: " fmt, ##__VA_ARGS__)
 
+#define FW_TYPE_V0		0
+#define FW_TYPE_V1		1
+#define FW_TYPE_V2		2
+#define FW_TYPE_V3_1		3
+#define FW_TYPE_V3_2		4
+#define is_v3_fw(type)	(type == FW_TYPE_V3_1 || type == FW_TYPE_V3_2)
+
+#define DL_FIX_CI_ID		0
+#define DL_FIX_CI_ADDR		1
+#define DL_FIX_PATCH_ADDR	2
+#define DL_FIX_SEC_HDR_ADDR	3
+#define DL_FIX_ADDR_MAX		4
+
 struct btrtl_device_info;
 
 struct rtl_chip_type_evt {
@@ -103,8 +116,79 @@ struct rtl_vendor_cmd {
 	__u8 param[5];
 } __packed;
 
+struct rtl_vendor_write_cmd {
+	u8 type;
+	__le32 addr;
+	__le32 val;
+} __packed;
+
+struct rtl_rp_read_chip_id {
+	__u8 status;
+	__u8 chip_id;
+} __packed;
+
+struct rtl_rp_dl_v3 {
+	__u8 status;
+	__u8 index;
+	__u8 err;
+} __packed;
+
+struct rtl_epatch_header_v3 {
+	__u8 signature[8];
+	__u8 timestamp[8];
+	__le32 ver_rsvd;
+	__le32 num_sections;
+} __packed;
+
+struct rtl_section_v3 {
+	__le32 opcode;
+	__le64 len;
+	u8 data[];
+} __packed;
+
+struct rtl_addr_fix {
+	u32 addr;
+	u32 value;
+};
+
+struct rtl_section_patch_image {
+	u16 image_id;
+	u8 index;
+	u8 config_rule;
+	u8 need_config;
+
+	struct rtl_addr_fix fix[DL_FIX_ADDR_MAX];
+
+	u32 image_len;
+	u8 *image_data;
+	u32 image_ver;
+
+	u8  *cfg_buf;
+	u16 cfg_len;
+
+	struct list_head list;
+};
+
+struct rtl_patch_image_hdr {
+	__le16 chip_id;
+	u8 ic_cut;
+	u8 key_id;
+	u8 enable_ota;
+	__le16 image_id;
+	u8 config_rule;
+	u8 need_config;
+	u8 rsv[950];
+
+	__le64 addr_fix[DL_FIX_ADDR_MAX * 2];
+	u8 index;
+
+	__le64 patch_image_len;
+	__u8 data[];
+} __packed;
+
 enum {
 	REALTEK_ALT6_CONTINUOUS_TX_CHIP,
+	REALTEK_DOWNLOADING,
 
 	__REALTEK_NUM_FLAGS,
 };
@@ -130,8 +214,20 @@ struct btrealtek_data {
 #define btrealtek_get_flag(hdev)					\
 	(((struct btrealtek_data *)hci_get_priv(hdev))->flags)
 
+#define btrealtek_wake_up_flag(hdev, nr)				\
+	do {								\
+		struct btrealtek_data *rtl = hci_get_priv((hdev));	\
+		wake_up_bit(rtl->flags, (nr));				\
+	} while (0)
+
 #define btrealtek_test_flag(hdev, nr)	test_bit((nr), btrealtek_get_flag(hdev))
 
+#define btrealtek_test_and_clear_flag(hdev, nr)				\
+		test_and_clear_bit((nr), btrealtek_get_flag(hdev))
+
+#define btrealtek_wait_on_flag_timeout(hdev, nr, m, to)			\
+		wait_on_bit_timeout(btrealtek_get_flag(hdev), (nr), m, to)
+
 #if IS_ENABLED(CONFIG_BT_RTL)
 
 struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
@@ -148,6 +244,7 @@ int btrtl_get_uart_settings(struct hci_dev *hdev,
 			    unsigned int *controller_baudrate,
 			    u32 *device_baudrate, bool *flow_control);
 void btrtl_set_driver_name(struct hci_dev *hdev, const char *driver_name);
+int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
 
 #else
 
@@ -157,6 +254,11 @@ static inline struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
 	return ERR_PTR(-EOPNOTSUPP);
 }
 
+static inline int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	return -EOPNOTSUPP;
+}
+
 static inline void btrtl_free(struct btrtl_device_info *btrtl_dev)
 {
 }
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 7f14ce96319b..55a845efaeb5 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -2798,6 +2798,9 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
 		return 0;
 	}
 
+	if (skb->data[0] == HCI_VENDOR_PKT)
+		return btrtl_recv_event(hdev, skb);
+
 	return hci_recv_frame(hdev, skb);
 }
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v5] Bluetooth: btrtl: Add firmware format v3 support
  2026-06-24  5:33   ` [PATCH v5] Bluetooth: btrtl: Add firmware " Hilda Wu
@ 2026-06-24  8:06     ` Paul Menzel
  2026-06-24  9:14     ` [v5] " bluez.test.bot
  1 sibling, 0 replies; 10+ messages in thread
From: Paul Menzel @ 2026-06-24  8:06 UTC (permalink / raw)
  To: Hilda Wu
  Cc: marcel, luiz.dentz, linux-bluetooth, linux-kernel, alex_lu,
	jason_mao, zoey_zhou, max.chou, kidman

Dear Hilda,


Thank you for your patch.

Am 24.06.26 um 07:33 schrieb Hilda Wu:
> Realtek updated its Bluetooth firmware format to v3.

Please name the specification document, and state whether it’s 
publically accessible.

> This patch extends the btrtl driver to recognise and parse the new v3 file
> format, including:
> - New signature string and image ID definitions
> - Extension of btrtl_device_info to store v3-specific metadata
> - Logic to extract and load firmware data out of v3 images
> - Maintains compatibility with existing v2 firmware format
> 
> This is required for future Realtek Bluetooth chips that ship with
> v3 firmware.

Please add a blank line between paragraphs or do not wrap the line after 
the sentence.

> The RTL8922D is the first IC to use firmware format V3, so the following
> example uses the RTL8922D's log as expected fw format v3 output:
> 
> Bluetooth: btrtl_read_chip_id() hci0: RTL: chip_id status=0x00 id=0x37
> Bluetooth: btrtl_initialize() hci0: RTL: examining hci_ver=0d
> hci_rev=000d lmp_ver=0d lmp_subver=8922

Please do not wrap the lines of the pasted lines. If you keep the 
timestamp in the front, `checkpatch.pl` should not complain.

> Bluetooth: rtl_read_rom_version() hci0: RTL: rom_version status=0 version=1
> Bluetooth: btrtl_initialize() hci0: RTL: btrtl_initialize: key id 0
> Bluetooth: rtl_load_file() hci0: RTL: loading rtl_bt/rtl8922du_fw.bin
> Bluetooth: rtl_load_file() hci0: RTL: loading rtl_bt/rtl8922du_config.bin
> Bluetooth: rtlbt_parse_firmware_v3() hci0: RTL: key id 0
> Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image (f000:00), chip id
> 55, cut 0x02, len 00007185
> Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image version: 35fd7908
> Bluetooth: rtlbt_parse_config() hci0: RTL: config file:
> rtl_bt/rtl8922du_config_f000.bin
> Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image (f002:00), chip id
> 55, cut 0x02, len 000078f5
> Bluetooth: rtlbt_parse_section_v3() hci0: RTL: image version: 47b6874d
> Bluetooth: rtlbt_parse_config() hci0: RTL: config file:
> rtl_bt/rtl8922du_config_f002.bin
> Bluetooth: rtlbt_parse_firmware_v3() hci0: RTL: image payload total len:
> 0x0000ea7a
> Bluetooth: rtl_finalize_download() hci0: RTL: Watchdog reset status 00
> Bluetooth: rtl_finalize_download() hci0: RTL: fw version 0x47b6874d

How big in the firmware file, and how long did it take to load it?

> Signed-off-by: Alex Lu <alex_lu@realsil.com.cn>
> Signed-off-by: Zoey Zhou <zoey_zhou@realsil.com.cn>
> Signed-off-by: Hilda Wu <hildawu@realtek.com>
> 
> ---
> V4 -> V5::
> - Add independent support for RTL8922D section
> - Introduce macros to improve code readability
> - Document firmware format v3 and its differences
> - Align implementation with reviewer feedback
> 
> V3 -> V4:
> - Rework skb->data access and add clarifying comments
> - Fix latent issues
> 
> V2 -> V3:
> - Address coccinelle warning
> 
> V1 -> V2:
> - Add missing symbols
> - Resolve build warnings
> ---
>   drivers/bluetooth/btrtl.c | 698 +++++++++++++++++++++++++++++++++++++-
>   drivers/bluetooth/btrtl.h | 102 ++++++

The files get bigger. Would it be good to split the firmware handling 
out into a separate file?

>   drivers/bluetooth/btusb.c |   3 +
>   3 files changed, 786 insertions(+), 17 deletions(-)

For easier review, would it be possible to split out the refactoring 
like defining the macros FW_TYPE_V0, … and using them?

[…]


Kind regards,

Paul

^ permalink raw reply	[flat|nested] 10+ messages in thread

* RE: [v5] Bluetooth: btrtl: Add firmware format v3 support
  2026-06-24  5:33   ` [PATCH v5] Bluetooth: btrtl: Add firmware " Hilda Wu
  2026-06-24  8:06     ` Paul Menzel
@ 2026-06-24  9:14     ` bluez.test.bot
  1 sibling, 0 replies; 10+ messages in thread
From: bluez.test.bot @ 2026-06-24  9:14 UTC (permalink / raw)
  To: linux-bluetooth, hildawu

[-- Attachment #1: Type: text/plain, Size: 1181 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1115683

---Test result---

Test Summary:
CheckPatch                    PASS      2.58 seconds
VerifyFixes                   PASS      0.24 seconds
VerifySignedoff               PASS      0.25 seconds
GitLint                       PASS      0.56 seconds
SubjectPrefix                 PASS      0.36 seconds
BuildKernel                   PASS      26.77 seconds
CheckAllWarning               PASS      29.75 seconds
CheckSparse                   PASS      28.35 seconds
BuildKernel32                 PASS      26.19 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      574.02 seconds
IncrementalBuild              PASS      25.77 seconds

Details
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found


https://github.com/bluez/bluetooth-next/pull/343

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-06-24  9:14 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-08 12:45 [PATCH v3 1/2] Bluetooth: btrtl: Firmware format v3 support Hilda Wu
2025-07-08 12:45 ` [PATCH v3 2/2] Bluetooth: btrtl: Add enhanced download support Hilda Wu
2025-07-10 16:10   ` Luiz Augusto von Dentz
2025-07-08 13:30 ` [v3,1/2] Bluetooth: btrtl: Firmware format v3 support bluez.test.bot
2025-07-10 15:58 ` [PATCH v3 1/2] " Luiz Augusto von Dentz
2026-06-24  5:33   ` [PATCH v5] Bluetooth: btrtl: Add firmware " Hilda Wu
2026-06-24  8:06     ` Paul Menzel
2026-06-24  9:14     ` [v5] " bluez.test.bot
  -- strict thread matches above, loose matches on Subject: below --
2026-03-16 11:30 [PATCH v5] " Hilda Wu
2026-03-16 12:05 ` [v5] " bluez.test.bot
2026-05-13  9:24 [PATCH v5] " Hilda Wu
2026-05-13 10:31 ` [v5] " bluez.test.bot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.