Skip to content

Commit 998e447

Browse files
NeerajSanjayKaleVudentz
authored andcommitted
Bluetooth: btnxpuart: Add support for HCI coredump feature
This adds support for Bluetooth Coredump feature to BTNXPUART driver to collect FW dumps on demand, or in case FW goes in a bad state. To trigger manual FW dump, following command can be used: echo 1 > /sys/class/bluetooth/hci0/device/coredump Once FW dump is complete, it can be written to a file: cat /sys/class/bluetooth/hci0/devcoredump/data > fw_dump While FW dump is in progress, any HCI command will return -EBUSY. After FW dump is complete, driver will give HCI_NXP_IND_RESET command which soft-resets the chip, allowing FW re-download. Signed-off-by: Neeraj Sanjay Kale <[email protected]> Signed-off-by: Luiz Augusto von Dentz <[email protected]>
1 parent 6fca678 commit 998e447

File tree

1 file changed

+132
-15
lines changed

1 file changed

+132
-15
lines changed

drivers/bluetooth/btnxpuart.c

Lines changed: 132 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#define BTNXPUART_SERDEV_OPEN 4
3232
#define BTNXPUART_IR_IN_PROGRESS 5
3333
#define BTNXPUART_FW_DOWNLOAD_ABORT 6
34+
#define BTNXPUART_FW_DUMP_IN_PROGRESS 7
3435

3536
/* NXP HW err codes */
3637
#define BTNXPUART_IR_HW_ERR 0xb0
@@ -106,6 +107,8 @@
106107
#define HCI_NXP_SET_OPER_SPEED 0xfc09
107108
/* Bluetooth vendor command: Independent Reset */
108109
#define HCI_NXP_IND_RESET 0xfcfc
110+
/* Bluetooth vendor command: Trigger FW dump */
111+
#define HCI_NXP_TRIGGER_DUMP 0xfe91
109112

110113
/* Bluetooth Power State : Vendor cmd params */
111114
#define BT_PS_ENABLE 0x02
@@ -310,6 +313,16 @@ union nxp_v3_rx_timeout_nak_u {
310313
u8 buf[6];
311314
};
312315

316+
/* FW dump */
317+
#define NXP_FW_DUMP_SIZE (1024 * 1000)
318+
319+
struct nxp_fw_dump_hdr {
320+
__le16 seq_num;
321+
__le16 reserved;
322+
__le16 buf_type;
323+
__le16 buf_len;
324+
};
325+
313326
static u8 crc8_table[CRC8_TABLE_SIZE];
314327

315328
/* Default configurations */
@@ -774,6 +787,16 @@ static bool is_fw_downloading(struct btnxpuart_dev *nxpdev)
774787
return test_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
775788
}
776789

790+
static bool ind_reset_in_progress(struct btnxpuart_dev *nxpdev)
791+
{
792+
return test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state);
793+
}
794+
795+
static bool fw_dump_in_progress(struct btnxpuart_dev *nxpdev)
796+
{
797+
return test_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state);
798+
}
799+
777800
static bool process_boot_signature(struct btnxpuart_dev *nxpdev)
778801
{
779802
if (test_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state)) {
@@ -1175,7 +1198,7 @@ static int nxp_set_baudrate_cmd(struct hci_dev *hdev, void *data)
11751198
static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev)
11761199
{
11771200
serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE);
1178-
if (test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state))
1201+
if (ind_reset_in_progress(nxpdev))
11791202
serdev_device_set_flow_control(nxpdev->serdev, false);
11801203
else
11811204
serdev_device_set_flow_control(nxpdev->serdev, true);
@@ -1204,6 +1227,73 @@ static int nxp_set_ind_reset(struct hci_dev *hdev, void *data)
12041227
return hci_recv_frame(hdev, skb);
12051228
}
12061229

1230+
/* Firmware dump */
1231+
static void nxp_coredump(struct hci_dev *hdev)
1232+
{
1233+
struct sk_buff *skb;
1234+
u8 pcmd = 2;
1235+
1236+
skb = nxp_drv_send_cmd(hdev, HCI_NXP_TRIGGER_DUMP, 1, &pcmd);
1237+
if (!IS_ERR(skb))
1238+
kfree_skb(skb);
1239+
}
1240+
1241+
static void nxp_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb)
1242+
{
1243+
/* Nothing to be added in FW dump header */
1244+
}
1245+
1246+
static int nxp_process_fw_dump(struct hci_dev *hdev, struct sk_buff *skb)
1247+
{
1248+
struct hci_acl_hdr *acl_hdr = (struct hci_acl_hdr *)skb_pull_data(skb,
1249+
sizeof(*acl_hdr));
1250+
struct nxp_fw_dump_hdr *fw_dump_hdr = (struct nxp_fw_dump_hdr *)skb->data;
1251+
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
1252+
__u16 seq_num = __le16_to_cpu(fw_dump_hdr->seq_num);
1253+
__u16 buf_len = __le16_to_cpu(fw_dump_hdr->buf_len);
1254+
int err;
1255+
1256+
if (seq_num == 0x0001) {
1257+
if (test_and_set_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state)) {
1258+
bt_dev_err(hdev, "FW dump already in progress");
1259+
goto free_skb;
1260+
}
1261+
bt_dev_warn(hdev, "==== Start FW dump ===");
1262+
err = hci_devcd_init(hdev, NXP_FW_DUMP_SIZE);
1263+
if (err < 0)
1264+
goto free_skb;
1265+
1266+
schedule_delayed_work(&hdev->dump.dump_timeout,
1267+
msecs_to_jiffies(20000));
1268+
}
1269+
1270+
err = hci_devcd_append(hdev, skb_clone(skb, GFP_ATOMIC));
1271+
if (err < 0)
1272+
goto free_skb;
1273+
1274+
if (buf_len == 0) {
1275+
bt_dev_warn(hdev, "==== FW dump complete ===");
1276+
clear_bit(BTNXPUART_FW_DUMP_IN_PROGRESS, &nxpdev->tx_state);
1277+
hci_devcd_complete(hdev);
1278+
nxp_set_ind_reset(hdev, NULL);
1279+
}
1280+
1281+
free_skb:
1282+
kfree_skb(skb);
1283+
return 0;
1284+
}
1285+
1286+
static int nxp_recv_acl_pkt(struct hci_dev *hdev, struct sk_buff *skb)
1287+
{
1288+
__u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
1289+
1290+
/* FW dump chunks are ACL packets with conn handle 0xfff */
1291+
if ((handle & 0x0FFF) == 0xFFF)
1292+
return nxp_process_fw_dump(hdev, skb);
1293+
else
1294+
return hci_recv_frame(hdev, skb);
1295+
}
1296+
12071297
/* NXP protocol */
12081298
static int nxp_setup(struct hci_dev *hdev)
12091299
{
@@ -1265,20 +1355,15 @@ static int nxp_shutdown(struct hci_dev *hdev)
12651355
{
12661356
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
12671357
struct sk_buff *skb;
1268-
u8 *status;
12691358
u8 pcmd = 0;
12701359

1271-
if (test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state)) {
1360+
if (ind_reset_in_progress(nxpdev)) {
12721361
skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd);
1273-
if (IS_ERR(skb))
1274-
return PTR_ERR(skb);
1275-
1276-
status = skb_pull_data(skb, 1);
1277-
if (status) {
1278-
serdev_device_set_flow_control(nxpdev->serdev, false);
1279-
set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
1280-
}
1281-
kfree_skb(skb);
1362+
serdev_device_set_flow_control(nxpdev->serdev, false);
1363+
set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
1364+
/* HCI_NXP_IND_RESET command may not returns any response */
1365+
if (!IS_ERR(skb))
1366+
kfree_skb(skb);
12821367
} else if (nxpdev->current_baudrate != nxpdev->fw_init_baudrate) {
12831368
nxpdev->new_baudrate = nxpdev->fw_init_baudrate;
12841369
nxp_set_baudrate_cmd(hdev, NULL);
@@ -1298,6 +1383,16 @@ static bool nxp_wakeup(struct hci_dev *hdev)
12981383
return false;
12991384
}
13001385

1386+
static void nxp_reset(struct hci_dev *hdev)
1387+
{
1388+
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
1389+
1390+
if (!ind_reset_in_progress(nxpdev) && !fw_dump_in_progress(nxpdev)) {
1391+
bt_dev_dbg(hdev, "CMD Timeout detected. Resetting.");
1392+
nxp_set_ind_reset(hdev, NULL);
1393+
}
1394+
}
1395+
13011396
static int btnxpuart_queue_skb(struct hci_dev *hdev, struct sk_buff *skb)
13021397
{
13031398
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
@@ -1318,6 +1413,9 @@ static int nxp_enqueue(struct hci_dev *hdev, struct sk_buff *skb)
13181413
struct wakeup_cmd_payload wakeup_parm;
13191414
__le32 baudrate_parm;
13201415

1416+
if (fw_dump_in_progress(nxpdev))
1417+
return -EBUSY;
1418+
13211419
/* if vendor commands are received from user space (e.g. hcitool), update
13221420
* driver flags accordingly and ask driver to re-send the command to FW.
13231421
* In case the payload for any command does not match expected payload
@@ -1486,7 +1584,7 @@ static int btnxpuart_flush(struct hci_dev *hdev)
14861584
}
14871585

14881586
static const struct h4_recv_pkt nxp_recv_pkts[] = {
1489-
{ H4_RECV_ACL, .recv = hci_recv_frame },
1587+
{ H4_RECV_ACL, .recv = nxp_recv_acl_pkt },
14901588
{ H4_RECV_SCO, .recv = hci_recv_frame },
14911589
{ H4_RECV_EVENT, .recv = hci_recv_frame },
14921590
{ H4_RECV_ISO, .recv = hci_recv_frame },
@@ -1508,11 +1606,13 @@ static size_t btnxpuart_receive_buf(struct serdev_device *serdev,
15081606
if (IS_ERR(nxpdev->rx_skb)) {
15091607
int err = PTR_ERR(nxpdev->rx_skb);
15101608
/* Safe to ignore out-of-sync bootloader signatures */
1511-
if (!is_fw_downloading(nxpdev))
1609+
if (!is_fw_downloading(nxpdev) &&
1610+
!ind_reset_in_progress(nxpdev))
15121611
bt_dev_err(nxpdev->hdev, "Frame reassembly failed (%d)", err);
15131612
return count;
15141613
}
1515-
if (!is_fw_downloading(nxpdev))
1614+
if (!is_fw_downloading(nxpdev) &&
1615+
!ind_reset_in_progress(nxpdev))
15161616
nxpdev->hdev->stat.byte_rx += count;
15171617
return count;
15181618
}
@@ -1580,6 +1680,7 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
15801680
hdev->hw_error = nxp_hw_err;
15811681
hdev->shutdown = nxp_shutdown;
15821682
hdev->wakeup = nxp_wakeup;
1683+
hdev->reset = nxp_reset;
15831684
SET_HCIDEV_DEV(hdev, &serdev->dev);
15841685

15851686
if (hci_register_dev(hdev) < 0) {
@@ -1590,6 +1691,8 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
15901691
if (ps_setup(hdev))
15911692
goto probe_fail;
15921693

1694+
hci_devcd_register(hdev, nxp_coredump, nxp_coredump_hdr, NULL);
1695+
15931696
return 0;
15941697

15951698
probe_fail:
@@ -1641,6 +1744,17 @@ static int nxp_serdev_resume(struct device *dev)
16411744
}
16421745
#endif
16431746

1747+
#ifdef CONFIG_DEV_COREDUMP
1748+
static void nxp_serdev_coredump(struct device *dev)
1749+
{
1750+
struct btnxpuart_dev *nxpdev = dev_get_drvdata(dev);
1751+
struct hci_dev *hdev = nxpdev->hdev;
1752+
1753+
if (hdev->dump.coredump)
1754+
hdev->dump.coredump(hdev);
1755+
}
1756+
#endif
1757+
16441758
static struct btnxpuart_data w8987_data __maybe_unused = {
16451759
.helper_fw_name = NULL,
16461760
.fw_name = FIRMWARE_W8987,
@@ -1671,6 +1785,9 @@ static struct serdev_device_driver nxp_serdev_driver = {
16711785
.name = "btnxpuart",
16721786
.of_match_table = of_match_ptr(nxpuart_of_match_table),
16731787
.pm = &nxp_pm_ops,
1788+
#ifdef CONFIG_DEV_COREDUMP
1789+
.coredump = nxp_serdev_coredump,
1790+
#endif
16741791
},
16751792
};
16761793

0 commit comments

Comments
 (0)