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>
---
drivers/bluetooth/btrtl.c | 205 +++++++++++++++++++++++++++++++++++++-
drivers/bluetooth/btrtl.h | 5 +
2 files changed, 208 insertions(+), 2 deletions(-)
diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c
index 0e6332a45ced..2c6239091a68 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,178 @@ static int rtl_check_download_state(struct hci_dev *hdev,
return 0;
}
+struct hci_rp_enhanced_download_mode {
+ __u8 status;
+ __u8 reserved1;
+ __le16 handle;
+ __le16 acldata_pkt_len;
+ __u8 reserved2;
+} __packed;
+
+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 %lu", 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;
+}
+
+struct rtl_acl_download_rp {
+ __u8 subevent;
+ __u8 index;
+ __le16 handle;
+ __le32 loaded_len;
+} __packed;
+
+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 +1568,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 +1583,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 +1631,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);
@@ -1707,6 +1897,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);
@@ -2026,6 +2217,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 &&
@@ -2034,6 +2226,15 @@ int btrtl_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
btrealtek_test_and_clear_flag(hdev, REALTEK_DOWNLOADING)) {
btrealtek_wake_up_flag(hdev, REALTEK_DOWNLOADING);
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;
+ wake_up_interruptible(&btrtl_data->dlreq_wait_q);
+ }
+ return 0;
}
}
diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h
index ea3537b9d4fd..781ac845944e 100644
--- a/drivers/bluetooth/btrtl.h
+++ b/drivers/bluetooth/btrtl.h
@@ -203,6 +203,11 @@ 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;
};
#define btrealtek_set_flag(hdev, nr) \
--
2.34.1
Hi Hilda,
kernel test robot noticed the following build warnings:
[auto build test WARNING on bluetooth/master]
[also build test WARNING on bluetooth-next/master linus/master v6.15 next-20250530]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Hilda-Wu/Bluetooth-btrtl-Firmware-format-v3-support/20250529-205020
base: https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git master
patch link: https://lore.kernel.org/r/20250529124816.4186320-3-hildawu%40realtek.com
patch subject: [PATCH 2/2] Bluetooth: btrtl: Add enhanced download support
config: i386-buildonly-randconfig-004-20250530 (https://download.01.org/0day-ci/archive/20250530/202505301910.8q5W0vf5-lkp@intel.com/config)
compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250530/202505301910.8q5W0vf5-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202505301910.8q5W0vf5-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/bluetooth/btrtl.c:1339:7: warning: format specifies type 'unsigned long' but the argument has type 'unsigned int' [-Wformat]
1338 | bt_dev_err(hdev, "got invalid cmd complete, %u %lu", skb->len,
| ~~~
| %u
1339 | sizeof(*ev));
| ^~~~~~~~~~~
include/net/bluetooth/bluetooth.h:280:42: note: expanded from macro 'bt_dev_err'
280 | BT_ERR("%s: " fmt, bt_dev_name(hdev), ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
include/net/bluetooth/bluetooth.h:265:45: note: expanded from macro 'BT_ERR'
265 | #define BT_ERR(fmt, ...) bt_err(fmt "\n", ##__VA_ARGS__)
| ~~~ ^~~~~~~~~~~
drivers/bluetooth/btrtl.c:2071:1: error: function definition is not allowed here
2071 | {
| ^
drivers/bluetooth/btrtl.c:2113:1: error: function definition is not allowed here
2113 | {
| ^
drivers/bluetooth/btrtl.c:2172:1: error: function definition is not allowed here
2172 | {
| ^
drivers/bluetooth/btrtl.c:2199:1: error: function definition is not allowed here
2199 | {
| ^
drivers/bluetooth/btrtl.c:2219:1: error: function definition is not allowed here
2219 | {
| ^
drivers/bluetooth/btrtl.c:2246:1: error: function definition is not allowed here
2246 | {
| ^
drivers/bluetooth/btrtl.c:2286:1: error: function definition is not allowed here
2286 | {
| ^
drivers/bluetooth/btrtl.c:2404:48: error: expected '}'
2404 | MODULE_FIRMWARE("rtl_bt/rtl8922au_config.bin");
| ^
drivers/bluetooth/btrtl.c:1878:1: note: to match this '{'
1878 | {
| ^
1 warning and 8 errors generated.
vim +1339 drivers/bluetooth/btrtl.c
1322
1323 static int btrtl_enhanced_download_mode_enable(struct hci_dev *hdev,
1324 struct btrtl_device_info *btrtl_dev)
1325 {
1326 struct hci_rp_enhanced_download_mode *ev;
1327 struct sk_buff *skb;
1328 u16 opcode = 0xfc1f;
1329 u8 val = 1;
1330 int ret = -EINVAL;
1331
1332 skb = __hci_cmd_sync(hdev, opcode, 1, &val, HCI_CMD_TIMEOUT);
1333 if (IS_ERR(skb)) {
1334 bt_dev_err(hdev, "send %04x error (%lu)", opcode, PTR_ERR(skb));
1335 return -EIO;
1336 }
1337 if (skb->len != sizeof(*ev)) {
1338 bt_dev_err(hdev, "got invalid cmd complete, %u %lu", skb->len,
> 1339 sizeof(*ev));
1340 goto err;
1341 }
1342 ev = (struct hci_rp_enhanced_download_mode *)skb->data;
1343 if (ev->status) {
1344 bt_dev_err(hdev, "got invalid status 0x%02x", ev->status);
1345 goto err;
1346 }
1347 btrtl_dev->handle = le16_to_cpu(ev->handle);
1348 btrtl_dev->acldata_pkt_len = le16_to_cpu(ev->acldata_pkt_len);
1349 kfree_skb(skb);
1350
1351 bt_dev_info(hdev, "enhanced download mode enabled, handle %04x, acl %u",
1352 btrtl_dev->handle, btrtl_dev->acldata_pkt_len);
1353
1354 return 0;
1355 err:
1356 kfree_skb(skb);
1357 return ret;
1358 }
1359
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2025 Red Hat, Inc.