Commit e3c48910 authored by Neeraj Sanjay Kale's avatar Neeraj Sanjay Kale Committed by Luiz Augusto von Dentz
Browse files

Bluetooth: btnxpuart: Handle FW Download Abort scenario



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: default avatarNeeraj Sanjay Kale <neeraj.sanjaykale@nxp.com>
Tested-by: default avatarGuillaume Legoupil <guillaume.legoupil@nxp.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 2684dd61
Loading
Loading
Loading
Loading
+33 −14
Original line number Diff line number Diff line
@@ -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,6 +1403,12 @@ static void nxp_serdev_remove(struct serdev_device *serdev)
	struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev);
	struct hci_dev *hdev = nxpdev->hdev;

	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.
@@ -1398,8 +1417,8 @@ static void nxp_serdev_remove(struct serdev_device *serdev)
			nxpdev->new_baudrate = nxpdev->fw_init_baudrate;
			nxp_set_baudrate_cmd(hdev, NULL);
		}

		ps_cancel_timer(nxpdev);
	}
	hci_unregister_dev(hdev);
	hci_free_dev(hdev);
}