@@ -187,6 +187,10 @@ struct btnxpuart_dev {
#define NXP_NAK_V3 0x7b
#define NXP_CRC_ERROR_V3 0x7c
+#define NXP_ACK_RX_TIMEOUT 0x0002
+#define NXP_HDR_RX_TIMEOUT 0x0003
+#define NXP_DATA_RX_TIMEOUT 0x0004
+
#define HDR_LEN 16
#define NXP_RECV_CHIP_VER_V1 \
@@ -277,6 +281,12 @@ struct nxp_bootloader_cmd {
__be32 crc;
} __packed;
+struct nxp_v3_rx_timeout_nak {
+ u8 nak;
+ __le32 offset;
+ u8 crc;
+} __packed;
+
static u8 crc8_table[CRC8_TABLE_SIZE];
/* Default configurations */
@@ -899,6 +909,32 @@ static int nxp_recv_chip_ver_v3(struct hci_dev *hdev, struct sk_buff *skb)
return 0;
}
+static void nxp_handle_fw_dnld_error(struct hci_dev *hdev, struct v3_data_req *req)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ __u32 offset = __le32_to_cpu(req->offset);
+ __u16 err = __le16_to_cpu(req->error);
+ struct nxp_v3_rx_timeout_nak nak_tx_buf;
+
+ switch (err) {
+ case NXP_ACK_RX_TIMEOUT:
+ case NXP_HDR_RX_TIMEOUT:
+ case NXP_DATA_RX_TIMEOUT:
+ nak_tx_buf.nak = NXP_NAK_V3;
+ nak_tx_buf.offset = __cpu_to_le32(offset);
+ nak_tx_buf.crc = crc8(crc8_table, (u8 *)&nak_tx_buf,
+ sizeof(nak_tx_buf) - 1, 0xff);
+ serdev_device_write_buf(nxpdev->serdev, (u8 *)&nak_tx_buf,
+ sizeof(nak_tx_buf));
+ break;
+ default:
+ bt_dev_dbg(hdev, "Unknown bootloader error: %d", err);
+ break;
+
+ }
+
+}
+
static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
@@ -913,7 +949,12 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
if (!req || !nxpdev->fw)
goto free_skb;
- nxp_send_ack(NXP_ACK_V3, hdev);
+ if (!req->error) {
+ nxp_send_ack(NXP_ACK_V3, hdev);
+ } else {
+ nxp_handle_fw_dnld_error(hdev, req);
+ goto free_skb;
+ }
len = __le16_to_cpu(req->len);
@@ -940,9 +981,6 @@ static int nxp_recv_fw_req_v3(struct hci_dev *hdev, struct sk_buff *skb)
wake_up_interruptible(&nxpdev->fw_dnld_done_wait_q);
goto free_skb;
}
- if (req->error)
- bt_dev_dbg(hdev, "FW Download received err 0x%02x from chip",
- req->error);
offset = __le32_to_cpu(req->offset);
if (offset < nxpdev->fw_v3_offset_correction) {