Bluetooth: btnxpuart: Handle FW Download Abort scenario
authorNeeraj Sanjay Kale <neeraj.sanjaykale@nxp.com>
Wed, 15 May 2024 07:06:57 +0000 (12:36 +0530)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 15 Jul 2024 01:33:25 +0000 (21:33 -0400)
This adds a new flag BTNXPUART_FW_DOWNLOAD_ABORT which handles the
situation where driver is removed while firmware download is in
progress.

logs:
modprobe btnxpuart
[65239.230431] Bluetooth: hci0: ChipID: 7601, Version: 0
[65239.236670] Bluetooth: hci0: Request Firmware: nxp/uartspi_n61x_v1.bin.se
rmmod btnxpuart
[65241.425300] Bluetooth: hci0: FW Download Aborted

Signed-off-by: Neeraj Sanjay Kale <neeraj.sanjaykale@nxp.com>
Tested-by: Guillaume Legoupil <guillaume.legoupil@nxp.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
drivers/bluetooth/btnxpuart.c

index 81ad7ea..ecefae4 100644 (file)
@@ -29,6 +29,7 @@
 #define BTNXPUART_CHECK_BOOT_SIGNATURE 3
 #define BTNXPUART_SERDEV_OPEN          4
 #define BTNXPUART_IR_IN_PROGRESS       5
+#define BTNXPUART_FW_DOWNLOAD_ABORT    6
 
 /* NXP HW err codes */
 #define BTNXPUART_IR_HW_ERR            0xb0
@@ -159,6 +160,7 @@ struct btnxpuart_dev {
        u8 fw_name[MAX_FW_FILE_NAME_LEN];
        u32 fw_dnld_v1_offset;
        u32 fw_v1_sent_bytes;
+       u32 fw_dnld_v3_offset;
        u32 fw_v3_offset_correction;
        u32 fw_v1_expected_len;
        u32 boot_reg_offset;
@@ -550,6 +552,7 @@ static int nxp_download_firmware(struct hci_dev *hdev)
        nxpdev->fw_v1_sent_bytes = 0;
        nxpdev->fw_v1_expected_len = HDR_LEN;
        nxpdev->boot_reg_offset = 0;
+       nxpdev->fw_dnld_v3_offset = 0;
        nxpdev->fw_v3_offset_correction = 0;
        nxpdev->baudrate_changed = false;
        nxpdev->timeout_changed = false;
@@ -564,14 +567,23 @@ static int nxp_download_firmware(struct hci_dev *hdev)
                                               !test_bit(BTNXPUART_FW_DOWNLOADING,
                                                         &nxpdev->tx_state),
                                               msecs_to_jiffies(60000));
+
+       release_firmware(nxpdev->fw);
+       memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name));
+
        if (err == 0) {
-               bt_dev_err(hdev, "FW Download Timeout.");
+               bt_dev_err(hdev, "FW Download Timeout. offset: %d",
+                               nxpdev->fw_dnld_v1_offset ?
+                               nxpdev->fw_dnld_v1_offset :
+                               nxpdev->fw_dnld_v3_offset);
                return -ETIMEDOUT;
        }
+       if (test_bit(BTNXPUART_FW_DOWNLOAD_ABORT, &nxpdev->tx_state)) {
+               bt_dev_err(hdev, "FW Download Aborted");
+               return -EINTR;
+       }
 
        serdev_device_set_flow_control(nxpdev->serdev, true);
-       release_firmware(nxpdev->fw);
-       memset(nxpdev->fw_name, 0, sizeof(nxpdev->fw_name));
 
        /* Allow the downloaded FW to initialize */
        msleep(1200);
@@ -955,8 +967,9 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
                goto free_skb;
        }
 
-       serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data + offset -
-                               nxpdev->fw_v3_offset_correction, len);
+       nxpdev->fw_dnld_v3_offset = offset - nxpdev->fw_v3_offset_correction;
+       serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data +
+                               nxpdev->fw_dnld_v3_offset, len);
 
 free_skb:
        kfree_skb(skb);
@@ -1390,16 +1403,22 @@ static void nxp_serdev_remove(struct serdev_device *serdev)
        struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev);
        struct hci_dev *hdev = nxpdev->hdev;
 
-       /* Restore FW baudrate to fw_init_baudrate if changed.
-        * This will ensure FW baudrate is in sync with
-        * driver baudrate in case this driver is re-inserted.
-        */
-       if (nxpdev->current_baudrate != nxpdev->fw_init_baudrate) {
-               nxpdev->new_baudrate = nxpdev->fw_init_baudrate;
-               nxp_set_baudrate_cmd(hdev, NULL);
+       if (is_fw_downloading(nxpdev)) {
+               set_bit(BTNXPUART_FW_DOWNLOAD_ABORT, &nxpdev->tx_state);
+               clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+               wake_up_interruptible(&nxpdev->check_boot_sign_wait_q);
+               wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q);
+       } else {
+               /* Restore FW baudrate to fw_init_baudrate if changed.
+                * This will ensure FW baudrate is in sync with
+                * driver baudrate in case this driver is re-inserted.
+                */
+               if (nxpdev->current_baudrate != nxpdev->fw_init_baudrate) {
+                       nxpdev->new_baudrate = nxpdev->fw_init_baudrate;
+                       nxp_set_baudrate_cmd(hdev, NULL);
+               }
+               ps_cancel_timer(nxpdev);
        }
-
-       ps_cancel_timer(nxpdev);
        hci_unregister_dev(hdev);
        hci_free_dev(hdev);
 }