xref: /linux/net/bluetooth/coredump.c (revision 3cd43dd1)
19695ef87SAbhishek Pandit-Subedi // SPDX-License-Identifier: GPL-2.0-only
29695ef87SAbhishek Pandit-Subedi /*
39695ef87SAbhishek Pandit-Subedi  * Copyright (C) 2023 Google Corporation
49695ef87SAbhishek Pandit-Subedi  */
59695ef87SAbhishek Pandit-Subedi 
69695ef87SAbhishek Pandit-Subedi #include <linux/devcoredump.h>
79695ef87SAbhishek Pandit-Subedi 
8*0ab905c3SZijun Hu #include <asm/unaligned.h>
99695ef87SAbhishek Pandit-Subedi #include <net/bluetooth/bluetooth.h>
109695ef87SAbhishek Pandit-Subedi #include <net/bluetooth/hci_core.h>
119695ef87SAbhishek Pandit-Subedi 
129695ef87SAbhishek Pandit-Subedi enum hci_devcoredump_pkt_type {
139695ef87SAbhishek Pandit-Subedi 	HCI_DEVCOREDUMP_PKT_INIT,
149695ef87SAbhishek Pandit-Subedi 	HCI_DEVCOREDUMP_PKT_SKB,
159695ef87SAbhishek Pandit-Subedi 	HCI_DEVCOREDUMP_PKT_PATTERN,
169695ef87SAbhishek Pandit-Subedi 	HCI_DEVCOREDUMP_PKT_COMPLETE,
179695ef87SAbhishek Pandit-Subedi 	HCI_DEVCOREDUMP_PKT_ABORT,
189695ef87SAbhishek Pandit-Subedi };
199695ef87SAbhishek Pandit-Subedi 
209695ef87SAbhishek Pandit-Subedi struct hci_devcoredump_skb_cb {
219695ef87SAbhishek Pandit-Subedi 	u16 pkt_type;
229695ef87SAbhishek Pandit-Subedi };
239695ef87SAbhishek Pandit-Subedi 
249695ef87SAbhishek Pandit-Subedi struct hci_devcoredump_skb_pattern {
259695ef87SAbhishek Pandit-Subedi 	u8 pattern;
269695ef87SAbhishek Pandit-Subedi 	u32 len;
279695ef87SAbhishek Pandit-Subedi } __packed;
289695ef87SAbhishek Pandit-Subedi 
299695ef87SAbhishek Pandit-Subedi #define hci_dmp_cb(skb)	((struct hci_devcoredump_skb_cb *)((skb)->cb))
309695ef87SAbhishek Pandit-Subedi 
319695ef87SAbhishek Pandit-Subedi #define DBG_UNEXPECTED_STATE() \
329695ef87SAbhishek Pandit-Subedi 	bt_dev_dbg(hdev, \
339695ef87SAbhishek Pandit-Subedi 		   "Unexpected packet (%d) for state (%d). ", \
349695ef87SAbhishek Pandit-Subedi 		   hci_dmp_cb(skb)->pkt_type, hdev->dump.state)
359695ef87SAbhishek Pandit-Subedi 
369695ef87SAbhishek Pandit-Subedi #define MAX_DEVCOREDUMP_HDR_SIZE	512	/* bytes */
379695ef87SAbhishek Pandit-Subedi 
hci_devcd_update_hdr_state(char * buf,size_t size,int state)389695ef87SAbhishek Pandit-Subedi static int hci_devcd_update_hdr_state(char *buf, size_t size, int state)
399695ef87SAbhishek Pandit-Subedi {
409695ef87SAbhishek Pandit-Subedi 	int len = 0;
419695ef87SAbhishek Pandit-Subedi 
429695ef87SAbhishek Pandit-Subedi 	if (!buf)
439695ef87SAbhishek Pandit-Subedi 		return 0;
449695ef87SAbhishek Pandit-Subedi 
459695ef87SAbhishek Pandit-Subedi 	len = scnprintf(buf, size, "Bluetooth devcoredump\nState: %d\n", state);
469695ef87SAbhishek Pandit-Subedi 
479695ef87SAbhishek Pandit-Subedi 	return len + 1; /* scnprintf adds \0 at the end upon state rewrite */
489695ef87SAbhishek Pandit-Subedi }
499695ef87SAbhishek Pandit-Subedi 
509695ef87SAbhishek Pandit-Subedi /* Call with hci_dev_lock only. */
hci_devcd_update_state(struct hci_dev * hdev,int state)519695ef87SAbhishek Pandit-Subedi static int hci_devcd_update_state(struct hci_dev *hdev, int state)
529695ef87SAbhishek Pandit-Subedi {
539695ef87SAbhishek Pandit-Subedi 	bt_dev_dbg(hdev, "Updating devcoredump state from %d to %d.",
549695ef87SAbhishek Pandit-Subedi 		   hdev->dump.state, state);
559695ef87SAbhishek Pandit-Subedi 
569695ef87SAbhishek Pandit-Subedi 	hdev->dump.state = state;
579695ef87SAbhishek Pandit-Subedi 
589695ef87SAbhishek Pandit-Subedi 	return hci_devcd_update_hdr_state(hdev->dump.head,
599695ef87SAbhishek Pandit-Subedi 					  hdev->dump.alloc_size, state);
609695ef87SAbhishek Pandit-Subedi }
619695ef87SAbhishek Pandit-Subedi 
hci_devcd_mkheader(struct hci_dev * hdev,struct sk_buff * skb)629695ef87SAbhishek Pandit-Subedi static int hci_devcd_mkheader(struct hci_dev *hdev, struct sk_buff *skb)
639695ef87SAbhishek Pandit-Subedi {
649695ef87SAbhishek Pandit-Subedi 	char dump_start[] = "--- Start dump ---\n";
659695ef87SAbhishek Pandit-Subedi 	char hdr[80];
669695ef87SAbhishek Pandit-Subedi 	int hdr_len;
679695ef87SAbhishek Pandit-Subedi 
689695ef87SAbhishek Pandit-Subedi 	hdr_len = hci_devcd_update_hdr_state(hdr, sizeof(hdr),
699695ef87SAbhishek Pandit-Subedi 					     HCI_DEVCOREDUMP_IDLE);
709695ef87SAbhishek Pandit-Subedi 	skb_put_data(skb, hdr, hdr_len);
719695ef87SAbhishek Pandit-Subedi 
729695ef87SAbhishek Pandit-Subedi 	if (hdev->dump.dmp_hdr)
739695ef87SAbhishek Pandit-Subedi 		hdev->dump.dmp_hdr(hdev, skb);
749695ef87SAbhishek Pandit-Subedi 
759695ef87SAbhishek Pandit-Subedi 	skb_put_data(skb, dump_start, strlen(dump_start));
769695ef87SAbhishek Pandit-Subedi 
779695ef87SAbhishek Pandit-Subedi 	return skb->len;
789695ef87SAbhishek Pandit-Subedi }
799695ef87SAbhishek Pandit-Subedi 
809695ef87SAbhishek Pandit-Subedi /* Do not call with hci_dev_lock since this calls driver code. */
hci_devcd_notify(struct hci_dev * hdev,int state)819695ef87SAbhishek Pandit-Subedi static void hci_devcd_notify(struct hci_dev *hdev, int state)
829695ef87SAbhishek Pandit-Subedi {
839695ef87SAbhishek Pandit-Subedi 	if (hdev->dump.notify_change)
849695ef87SAbhishek Pandit-Subedi 		hdev->dump.notify_change(hdev, state);
859695ef87SAbhishek Pandit-Subedi }
869695ef87SAbhishek Pandit-Subedi 
879695ef87SAbhishek Pandit-Subedi /* Call with hci_dev_lock only. */
hci_devcd_reset(struct hci_dev * hdev)889695ef87SAbhishek Pandit-Subedi void hci_devcd_reset(struct hci_dev *hdev)
899695ef87SAbhishek Pandit-Subedi {
909695ef87SAbhishek Pandit-Subedi 	hdev->dump.head = NULL;
919695ef87SAbhishek Pandit-Subedi 	hdev->dump.tail = NULL;
929695ef87SAbhishek Pandit-Subedi 	hdev->dump.alloc_size = 0;
939695ef87SAbhishek Pandit-Subedi 
949695ef87SAbhishek Pandit-Subedi 	hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_IDLE);
959695ef87SAbhishek Pandit-Subedi 
969695ef87SAbhishek Pandit-Subedi 	cancel_delayed_work(&hdev->dump.dump_timeout);
979695ef87SAbhishek Pandit-Subedi 	skb_queue_purge(&hdev->dump.dump_q);
989695ef87SAbhishek Pandit-Subedi }
999695ef87SAbhishek Pandit-Subedi 
1009695ef87SAbhishek Pandit-Subedi /* Call with hci_dev_lock only. */
hci_devcd_free(struct hci_dev * hdev)1019695ef87SAbhishek Pandit-Subedi static void hci_devcd_free(struct hci_dev *hdev)
1029695ef87SAbhishek Pandit-Subedi {
1039695ef87SAbhishek Pandit-Subedi 	vfree(hdev->dump.head);
1049695ef87SAbhishek Pandit-Subedi 
1059695ef87SAbhishek Pandit-Subedi 	hci_devcd_reset(hdev);
1069695ef87SAbhishek Pandit-Subedi }
1079695ef87SAbhishek Pandit-Subedi 
1089695ef87SAbhishek Pandit-Subedi /* Call with hci_dev_lock only. */
hci_devcd_alloc(struct hci_dev * hdev,u32 size)1099695ef87SAbhishek Pandit-Subedi static int hci_devcd_alloc(struct hci_dev *hdev, u32 size)
1109695ef87SAbhishek Pandit-Subedi {
1119695ef87SAbhishek Pandit-Subedi 	hdev->dump.head = vmalloc(size);
1129695ef87SAbhishek Pandit-Subedi 	if (!hdev->dump.head)
1139695ef87SAbhishek Pandit-Subedi 		return -ENOMEM;
1149695ef87SAbhishek Pandit-Subedi 
1159695ef87SAbhishek Pandit-Subedi 	hdev->dump.alloc_size = size;
1169695ef87SAbhishek Pandit-Subedi 	hdev->dump.tail = hdev->dump.head;
1179695ef87SAbhishek Pandit-Subedi 	hdev->dump.end = hdev->dump.head + size;
1189695ef87SAbhishek Pandit-Subedi 
1199695ef87SAbhishek Pandit-Subedi 	hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_IDLE);
1209695ef87SAbhishek Pandit-Subedi 
1219695ef87SAbhishek Pandit-Subedi 	return 0;
1229695ef87SAbhishek Pandit-Subedi }
1239695ef87SAbhishek Pandit-Subedi 
1249695ef87SAbhishek Pandit-Subedi /* Call with hci_dev_lock only. */
hci_devcd_copy(struct hci_dev * hdev,char * buf,u32 size)1259695ef87SAbhishek Pandit-Subedi static bool hci_devcd_copy(struct hci_dev *hdev, char *buf, u32 size)
1269695ef87SAbhishek Pandit-Subedi {
1279695ef87SAbhishek Pandit-Subedi 	if (hdev->dump.tail + size > hdev->dump.end)
1289695ef87SAbhishek Pandit-Subedi 		return false;
1299695ef87SAbhishek Pandit-Subedi 
1309695ef87SAbhishek Pandit-Subedi 	memcpy(hdev->dump.tail, buf, size);
1319695ef87SAbhishek Pandit-Subedi 	hdev->dump.tail += size;
1329695ef87SAbhishek Pandit-Subedi 
1339695ef87SAbhishek Pandit-Subedi 	return true;
1349695ef87SAbhishek Pandit-Subedi }
1359695ef87SAbhishek Pandit-Subedi 
1369695ef87SAbhishek Pandit-Subedi /* Call with hci_dev_lock only. */
hci_devcd_memset(struct hci_dev * hdev,u8 pattern,u32 len)1379695ef87SAbhishek Pandit-Subedi static bool hci_devcd_memset(struct hci_dev *hdev, u8 pattern, u32 len)
1389695ef87SAbhishek Pandit-Subedi {
1399695ef87SAbhishek Pandit-Subedi 	if (hdev->dump.tail + len > hdev->dump.end)
1409695ef87SAbhishek Pandit-Subedi 		return false;
1419695ef87SAbhishek Pandit-Subedi 
1429695ef87SAbhishek Pandit-Subedi 	memset(hdev->dump.tail, pattern, len);
1439695ef87SAbhishek Pandit-Subedi 	hdev->dump.tail += len;
1449695ef87SAbhishek Pandit-Subedi 
1459695ef87SAbhishek Pandit-Subedi 	return true;
1469695ef87SAbhishek Pandit-Subedi }
1479695ef87SAbhishek Pandit-Subedi 
1489695ef87SAbhishek Pandit-Subedi /* Call with hci_dev_lock only. */
hci_devcd_prepare(struct hci_dev * hdev,u32 dump_size)1499695ef87SAbhishek Pandit-Subedi static int hci_devcd_prepare(struct hci_dev *hdev, u32 dump_size)
1509695ef87SAbhishek Pandit-Subedi {
1519695ef87SAbhishek Pandit-Subedi 	struct sk_buff *skb;
1529695ef87SAbhishek Pandit-Subedi 	int dump_hdr_size;
1539695ef87SAbhishek Pandit-Subedi 	int err = 0;
1549695ef87SAbhishek Pandit-Subedi 
1559695ef87SAbhishek Pandit-Subedi 	skb = alloc_skb(MAX_DEVCOREDUMP_HDR_SIZE, GFP_ATOMIC);
1569695ef87SAbhishek Pandit-Subedi 	if (!skb)
1579695ef87SAbhishek Pandit-Subedi 		return -ENOMEM;
1589695ef87SAbhishek Pandit-Subedi 
1599695ef87SAbhishek Pandit-Subedi 	dump_hdr_size = hci_devcd_mkheader(hdev, skb);
1609695ef87SAbhishek Pandit-Subedi 
1619695ef87SAbhishek Pandit-Subedi 	if (hci_devcd_alloc(hdev, dump_hdr_size + dump_size)) {
1629695ef87SAbhishek Pandit-Subedi 		err = -ENOMEM;
1639695ef87SAbhishek Pandit-Subedi 		goto hdr_free;
1649695ef87SAbhishek Pandit-Subedi 	}
1659695ef87SAbhishek Pandit-Subedi 
1669695ef87SAbhishek Pandit-Subedi 	/* Insert the device header */
1679695ef87SAbhishek Pandit-Subedi 	if (!hci_devcd_copy(hdev, skb->data, skb->len)) {
1689695ef87SAbhishek Pandit-Subedi 		bt_dev_err(hdev, "Failed to insert header");
1699695ef87SAbhishek Pandit-Subedi 		hci_devcd_free(hdev);
1709695ef87SAbhishek Pandit-Subedi 
1719695ef87SAbhishek Pandit-Subedi 		err = -ENOMEM;
1729695ef87SAbhishek Pandit-Subedi 		goto hdr_free;
1739695ef87SAbhishek Pandit-Subedi 	}
1749695ef87SAbhishek Pandit-Subedi 
1759695ef87SAbhishek Pandit-Subedi hdr_free:
1769695ef87SAbhishek Pandit-Subedi 	kfree_skb(skb);
1779695ef87SAbhishek Pandit-Subedi 
1789695ef87SAbhishek Pandit-Subedi 	return err;
1799695ef87SAbhishek Pandit-Subedi }
1809695ef87SAbhishek Pandit-Subedi 
hci_devcd_handle_pkt_init(struct hci_dev * hdev,struct sk_buff * skb)1819695ef87SAbhishek Pandit-Subedi static void hci_devcd_handle_pkt_init(struct hci_dev *hdev, struct sk_buff *skb)
1829695ef87SAbhishek Pandit-Subedi {
183*0ab905c3SZijun Hu 	u32 dump_size;
1849695ef87SAbhishek Pandit-Subedi 
1859695ef87SAbhishek Pandit-Subedi 	if (hdev->dump.state != HCI_DEVCOREDUMP_IDLE) {
1869695ef87SAbhishek Pandit-Subedi 		DBG_UNEXPECTED_STATE();
1879695ef87SAbhishek Pandit-Subedi 		return;
1889695ef87SAbhishek Pandit-Subedi 	}
1899695ef87SAbhishek Pandit-Subedi 
190*0ab905c3SZijun Hu 	if (skb->len != sizeof(dump_size)) {
1919695ef87SAbhishek Pandit-Subedi 		bt_dev_dbg(hdev, "Invalid dump init pkt");
1929695ef87SAbhishek Pandit-Subedi 		return;
1939695ef87SAbhishek Pandit-Subedi 	}
1949695ef87SAbhishek Pandit-Subedi 
195*0ab905c3SZijun Hu 	dump_size = get_unaligned_le32(skb_pull_data(skb, 4));
196*0ab905c3SZijun Hu 	if (!dump_size) {
1979695ef87SAbhishek Pandit-Subedi 		bt_dev_err(hdev, "Zero size dump init pkt");
1989695ef87SAbhishek Pandit-Subedi 		return;
1999695ef87SAbhishek Pandit-Subedi 	}
2009695ef87SAbhishek Pandit-Subedi 
201*0ab905c3SZijun Hu 	if (hci_devcd_prepare(hdev, dump_size)) {
2029695ef87SAbhishek Pandit-Subedi 		bt_dev_err(hdev, "Failed to prepare for dump");
2039695ef87SAbhishek Pandit-Subedi 		return;
2049695ef87SAbhishek Pandit-Subedi 	}
2059695ef87SAbhishek Pandit-Subedi 
2069695ef87SAbhishek Pandit-Subedi 	hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_ACTIVE);
2079695ef87SAbhishek Pandit-Subedi 	queue_delayed_work(hdev->workqueue, &hdev->dump.dump_timeout,
2089695ef87SAbhishek Pandit-Subedi 			   hdev->dump.timeout);
2099695ef87SAbhishek Pandit-Subedi }
2109695ef87SAbhishek Pandit-Subedi 
hci_devcd_handle_pkt_skb(struct hci_dev * hdev,struct sk_buff * skb)2119695ef87SAbhishek Pandit-Subedi static void hci_devcd_handle_pkt_skb(struct hci_dev *hdev, struct sk_buff *skb)
2129695ef87SAbhishek Pandit-Subedi {
2139695ef87SAbhishek Pandit-Subedi 	if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) {
2149695ef87SAbhishek Pandit-Subedi 		DBG_UNEXPECTED_STATE();
2159695ef87SAbhishek Pandit-Subedi 		return;
2169695ef87SAbhishek Pandit-Subedi 	}
2179695ef87SAbhishek Pandit-Subedi 
2189695ef87SAbhishek Pandit-Subedi 	if (!hci_devcd_copy(hdev, skb->data, skb->len))
2199695ef87SAbhishek Pandit-Subedi 		bt_dev_dbg(hdev, "Failed to insert skb");
2209695ef87SAbhishek Pandit-Subedi }
2219695ef87SAbhishek Pandit-Subedi 
hci_devcd_handle_pkt_pattern(struct hci_dev * hdev,struct sk_buff * skb)2229695ef87SAbhishek Pandit-Subedi static void hci_devcd_handle_pkt_pattern(struct hci_dev *hdev,
2239695ef87SAbhishek Pandit-Subedi 					 struct sk_buff *skb)
2249695ef87SAbhishek Pandit-Subedi {
2259695ef87SAbhishek Pandit-Subedi 	struct hci_devcoredump_skb_pattern *pattern;
2269695ef87SAbhishek Pandit-Subedi 
2279695ef87SAbhishek Pandit-Subedi 	if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) {
2289695ef87SAbhishek Pandit-Subedi 		DBG_UNEXPECTED_STATE();
2299695ef87SAbhishek Pandit-Subedi 		return;
2309695ef87SAbhishek Pandit-Subedi 	}
2319695ef87SAbhishek Pandit-Subedi 
2329695ef87SAbhishek Pandit-Subedi 	if (skb->len != sizeof(*pattern)) {
2339695ef87SAbhishek Pandit-Subedi 		bt_dev_dbg(hdev, "Invalid pattern skb");
2349695ef87SAbhishek Pandit-Subedi 		return;
2359695ef87SAbhishek Pandit-Subedi 	}
2369695ef87SAbhishek Pandit-Subedi 
2379695ef87SAbhishek Pandit-Subedi 	pattern = skb_pull_data(skb, sizeof(*pattern));
2389695ef87SAbhishek Pandit-Subedi 
2399695ef87SAbhishek Pandit-Subedi 	if (!hci_devcd_memset(hdev, pattern->pattern, pattern->len))
2409695ef87SAbhishek Pandit-Subedi 		bt_dev_dbg(hdev, "Failed to set pattern");
2419695ef87SAbhishek Pandit-Subedi }
2429695ef87SAbhishek Pandit-Subedi 
hci_devcd_handle_pkt_complete(struct hci_dev * hdev,struct sk_buff * skb)2439695ef87SAbhishek Pandit-Subedi static void hci_devcd_handle_pkt_complete(struct hci_dev *hdev,
2449695ef87SAbhishek Pandit-Subedi 					  struct sk_buff *skb)
2459695ef87SAbhishek Pandit-Subedi {
2469695ef87SAbhishek Pandit-Subedi 	u32 dump_size;
2479695ef87SAbhishek Pandit-Subedi 
2489695ef87SAbhishek Pandit-Subedi 	if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) {
2499695ef87SAbhishek Pandit-Subedi 		DBG_UNEXPECTED_STATE();
2509695ef87SAbhishek Pandit-Subedi 		return;
2519695ef87SAbhishek Pandit-Subedi 	}
2529695ef87SAbhishek Pandit-Subedi 
2539695ef87SAbhishek Pandit-Subedi 	hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_DONE);
2549695ef87SAbhishek Pandit-Subedi 	dump_size = hdev->dump.tail - hdev->dump.head;
2559695ef87SAbhishek Pandit-Subedi 
2569695ef87SAbhishek Pandit-Subedi 	bt_dev_dbg(hdev, "complete with size %u (expect %zu)", dump_size,
2579695ef87SAbhishek Pandit-Subedi 		   hdev->dump.alloc_size);
2589695ef87SAbhishek Pandit-Subedi 
2599695ef87SAbhishek Pandit-Subedi 	dev_coredumpv(&hdev->dev, hdev->dump.head, dump_size, GFP_KERNEL);
2609695ef87SAbhishek Pandit-Subedi }
2619695ef87SAbhishek Pandit-Subedi 
hci_devcd_handle_pkt_abort(struct hci_dev * hdev,struct sk_buff * skb)2629695ef87SAbhishek Pandit-Subedi static void hci_devcd_handle_pkt_abort(struct hci_dev *hdev,
2639695ef87SAbhishek Pandit-Subedi 				       struct sk_buff *skb)
2649695ef87SAbhishek Pandit-Subedi {
2659695ef87SAbhishek Pandit-Subedi 	u32 dump_size;
2669695ef87SAbhishek Pandit-Subedi 
2679695ef87SAbhishek Pandit-Subedi 	if (hdev->dump.state != HCI_DEVCOREDUMP_ACTIVE) {
2689695ef87SAbhishek Pandit-Subedi 		DBG_UNEXPECTED_STATE();
2699695ef87SAbhishek Pandit-Subedi 		return;
2709695ef87SAbhishek Pandit-Subedi 	}
2719695ef87SAbhishek Pandit-Subedi 
2729695ef87SAbhishek Pandit-Subedi 	hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_ABORT);
2739695ef87SAbhishek Pandit-Subedi 	dump_size = hdev->dump.tail - hdev->dump.head;
2749695ef87SAbhishek Pandit-Subedi 
2759695ef87SAbhishek Pandit-Subedi 	bt_dev_dbg(hdev, "aborted with size %u (expect %zu)", dump_size,
2769695ef87SAbhishek Pandit-Subedi 		   hdev->dump.alloc_size);
2779695ef87SAbhishek Pandit-Subedi 
2789695ef87SAbhishek Pandit-Subedi 	/* Emit a devcoredump with the available data */
2799695ef87SAbhishek Pandit-Subedi 	dev_coredumpv(&hdev->dev, hdev->dump.head, dump_size, GFP_KERNEL);
2809695ef87SAbhishek Pandit-Subedi }
2819695ef87SAbhishek Pandit-Subedi 
2829695ef87SAbhishek Pandit-Subedi /* Bluetooth devcoredump state machine.
2839695ef87SAbhishek Pandit-Subedi  *
2849695ef87SAbhishek Pandit-Subedi  * Devcoredump states:
2859695ef87SAbhishek Pandit-Subedi  *
2869695ef87SAbhishek Pandit-Subedi  *      HCI_DEVCOREDUMP_IDLE: The default state.
2879695ef87SAbhishek Pandit-Subedi  *
2889695ef87SAbhishek Pandit-Subedi  *      HCI_DEVCOREDUMP_ACTIVE: A devcoredump will be in this state once it has
2899695ef87SAbhishek Pandit-Subedi  *              been initialized using hci_devcd_init(). Once active, the driver
2909695ef87SAbhishek Pandit-Subedi  *              can append data using hci_devcd_append() or insert a pattern
2919695ef87SAbhishek Pandit-Subedi  *              using hci_devcd_append_pattern().
2929695ef87SAbhishek Pandit-Subedi  *
2939695ef87SAbhishek Pandit-Subedi  *      HCI_DEVCOREDUMP_DONE: Once the dump collection is complete, the drive
2949695ef87SAbhishek Pandit-Subedi  *              can signal the completion using hci_devcd_complete(). A
2959695ef87SAbhishek Pandit-Subedi  *              devcoredump is generated indicating the completion event and
2969695ef87SAbhishek Pandit-Subedi  *              then the state machine is reset to the default state.
2979695ef87SAbhishek Pandit-Subedi  *
2989695ef87SAbhishek Pandit-Subedi  *      HCI_DEVCOREDUMP_ABORT: The driver can cancel ongoing dump collection in
2999695ef87SAbhishek Pandit-Subedi  *              case of any error using hci_devcd_abort(). A devcoredump is
3009695ef87SAbhishek Pandit-Subedi  *              still generated with the available data indicating the abort
3019695ef87SAbhishek Pandit-Subedi  *              event and then the state machine is reset to the default state.
3029695ef87SAbhishek Pandit-Subedi  *
3039695ef87SAbhishek Pandit-Subedi  *      HCI_DEVCOREDUMP_TIMEOUT: A timeout timer for HCI_DEVCOREDUMP_TIMEOUT sec
3049695ef87SAbhishek Pandit-Subedi  *              is started during devcoredump initialization. Once the timeout
3059695ef87SAbhishek Pandit-Subedi  *              occurs, the driver is notified, a devcoredump is generated with
3069695ef87SAbhishek Pandit-Subedi  *              the available data indicating the timeout event and then the
3079695ef87SAbhishek Pandit-Subedi  *              state machine is reset to the default state.
3089695ef87SAbhishek Pandit-Subedi  *
3099695ef87SAbhishek Pandit-Subedi  * The driver must register using hci_devcd_register() before using the hci
3109695ef87SAbhishek Pandit-Subedi  * devcoredump APIs.
3119695ef87SAbhishek Pandit-Subedi  */
hci_devcd_rx(struct work_struct * work)3129695ef87SAbhishek Pandit-Subedi void hci_devcd_rx(struct work_struct *work)
3139695ef87SAbhishek Pandit-Subedi {
3149695ef87SAbhishek Pandit-Subedi 	struct hci_dev *hdev = container_of(work, struct hci_dev, dump.dump_rx);
3159695ef87SAbhishek Pandit-Subedi 	struct sk_buff *skb;
3169695ef87SAbhishek Pandit-Subedi 	int start_state;
3179695ef87SAbhishek Pandit-Subedi 
3189695ef87SAbhishek Pandit-Subedi 	while ((skb = skb_dequeue(&hdev->dump.dump_q))) {
3199695ef87SAbhishek Pandit-Subedi 		/* Return if timeout occurs. The timeout handler function
3209695ef87SAbhishek Pandit-Subedi 		 * hci_devcd_timeout() will report the available dump data.
3219695ef87SAbhishek Pandit-Subedi 		 */
3229695ef87SAbhishek Pandit-Subedi 		if (hdev->dump.state == HCI_DEVCOREDUMP_TIMEOUT) {
3239695ef87SAbhishek Pandit-Subedi 			kfree_skb(skb);
3249695ef87SAbhishek Pandit-Subedi 			return;
3259695ef87SAbhishek Pandit-Subedi 		}
3269695ef87SAbhishek Pandit-Subedi 
3279695ef87SAbhishek Pandit-Subedi 		hci_dev_lock(hdev);
3289695ef87SAbhishek Pandit-Subedi 		start_state = hdev->dump.state;
3299695ef87SAbhishek Pandit-Subedi 
3309695ef87SAbhishek Pandit-Subedi 		switch (hci_dmp_cb(skb)->pkt_type) {
3319695ef87SAbhishek Pandit-Subedi 		case HCI_DEVCOREDUMP_PKT_INIT:
3329695ef87SAbhishek Pandit-Subedi 			hci_devcd_handle_pkt_init(hdev, skb);
3339695ef87SAbhishek Pandit-Subedi 			break;
3349695ef87SAbhishek Pandit-Subedi 
3359695ef87SAbhishek Pandit-Subedi 		case HCI_DEVCOREDUMP_PKT_SKB:
3369695ef87SAbhishek Pandit-Subedi 			hci_devcd_handle_pkt_skb(hdev, skb);
3379695ef87SAbhishek Pandit-Subedi 			break;
3389695ef87SAbhishek Pandit-Subedi 
3399695ef87SAbhishek Pandit-Subedi 		case HCI_DEVCOREDUMP_PKT_PATTERN:
3409695ef87SAbhishek Pandit-Subedi 			hci_devcd_handle_pkt_pattern(hdev, skb);
3419695ef87SAbhishek Pandit-Subedi 			break;
3429695ef87SAbhishek Pandit-Subedi 
3439695ef87SAbhishek Pandit-Subedi 		case HCI_DEVCOREDUMP_PKT_COMPLETE:
3449695ef87SAbhishek Pandit-Subedi 			hci_devcd_handle_pkt_complete(hdev, skb);
3459695ef87SAbhishek Pandit-Subedi 			break;
3469695ef87SAbhishek Pandit-Subedi 
3479695ef87SAbhishek Pandit-Subedi 		case HCI_DEVCOREDUMP_PKT_ABORT:
3489695ef87SAbhishek Pandit-Subedi 			hci_devcd_handle_pkt_abort(hdev, skb);
3499695ef87SAbhishek Pandit-Subedi 			break;
3509695ef87SAbhishek Pandit-Subedi 
3519695ef87SAbhishek Pandit-Subedi 		default:
3529695ef87SAbhishek Pandit-Subedi 			bt_dev_dbg(hdev, "Unknown packet (%d) for state (%d). ",
3539695ef87SAbhishek Pandit-Subedi 				   hci_dmp_cb(skb)->pkt_type, hdev->dump.state);
3549695ef87SAbhishek Pandit-Subedi 			break;
3559695ef87SAbhishek Pandit-Subedi 		}
3569695ef87SAbhishek Pandit-Subedi 
3579695ef87SAbhishek Pandit-Subedi 		hci_dev_unlock(hdev);
3589695ef87SAbhishek Pandit-Subedi 		kfree_skb(skb);
3599695ef87SAbhishek Pandit-Subedi 
3609695ef87SAbhishek Pandit-Subedi 		/* Notify the driver about any state changes before resetting
3619695ef87SAbhishek Pandit-Subedi 		 * the state machine
3629695ef87SAbhishek Pandit-Subedi 		 */
3639695ef87SAbhishek Pandit-Subedi 		if (start_state != hdev->dump.state)
3649695ef87SAbhishek Pandit-Subedi 			hci_devcd_notify(hdev, hdev->dump.state);
3659695ef87SAbhishek Pandit-Subedi 
3669695ef87SAbhishek Pandit-Subedi 		/* Reset the state machine if the devcoredump is complete */
3679695ef87SAbhishek Pandit-Subedi 		hci_dev_lock(hdev);
3689695ef87SAbhishek Pandit-Subedi 		if (hdev->dump.state == HCI_DEVCOREDUMP_DONE ||
3699695ef87SAbhishek Pandit-Subedi 		    hdev->dump.state == HCI_DEVCOREDUMP_ABORT)
3709695ef87SAbhishek Pandit-Subedi 			hci_devcd_reset(hdev);
3719695ef87SAbhishek Pandit-Subedi 		hci_dev_unlock(hdev);
3729695ef87SAbhishek Pandit-Subedi 	}
3739695ef87SAbhishek Pandit-Subedi }
3749695ef87SAbhishek Pandit-Subedi EXPORT_SYMBOL(hci_devcd_rx);
3759695ef87SAbhishek Pandit-Subedi 
hci_devcd_timeout(struct work_struct * work)3769695ef87SAbhishek Pandit-Subedi void hci_devcd_timeout(struct work_struct *work)
3779695ef87SAbhishek Pandit-Subedi {
3789695ef87SAbhishek Pandit-Subedi 	struct hci_dev *hdev = container_of(work, struct hci_dev,
3799695ef87SAbhishek Pandit-Subedi 					    dump.dump_timeout.work);
3809695ef87SAbhishek Pandit-Subedi 	u32 dump_size;
3819695ef87SAbhishek Pandit-Subedi 
3829695ef87SAbhishek Pandit-Subedi 	hci_devcd_notify(hdev, HCI_DEVCOREDUMP_TIMEOUT);
3839695ef87SAbhishek Pandit-Subedi 
3849695ef87SAbhishek Pandit-Subedi 	hci_dev_lock(hdev);
3859695ef87SAbhishek Pandit-Subedi 
3869695ef87SAbhishek Pandit-Subedi 	cancel_work(&hdev->dump.dump_rx);
3879695ef87SAbhishek Pandit-Subedi 
3889695ef87SAbhishek Pandit-Subedi 	hci_devcd_update_state(hdev, HCI_DEVCOREDUMP_TIMEOUT);
3899695ef87SAbhishek Pandit-Subedi 
3909695ef87SAbhishek Pandit-Subedi 	dump_size = hdev->dump.tail - hdev->dump.head;
3919695ef87SAbhishek Pandit-Subedi 	bt_dev_dbg(hdev, "timeout with size %u (expect %zu)", dump_size,
3929695ef87SAbhishek Pandit-Subedi 		   hdev->dump.alloc_size);
3939695ef87SAbhishek Pandit-Subedi 
3949695ef87SAbhishek Pandit-Subedi 	/* Emit a devcoredump with the available data */
3959695ef87SAbhishek Pandit-Subedi 	dev_coredumpv(&hdev->dev, hdev->dump.head, dump_size, GFP_KERNEL);
3969695ef87SAbhishek Pandit-Subedi 
3979695ef87SAbhishek Pandit-Subedi 	hci_devcd_reset(hdev);
3989695ef87SAbhishek Pandit-Subedi 
3999695ef87SAbhishek Pandit-Subedi 	hci_dev_unlock(hdev);
4009695ef87SAbhishek Pandit-Subedi }
4019695ef87SAbhishek Pandit-Subedi EXPORT_SYMBOL(hci_devcd_timeout);
4029695ef87SAbhishek Pandit-Subedi 
hci_devcd_register(struct hci_dev * hdev,coredump_t coredump,dmp_hdr_t dmp_hdr,notify_change_t notify_change)4039695ef87SAbhishek Pandit-Subedi int hci_devcd_register(struct hci_dev *hdev, coredump_t coredump,
4049695ef87SAbhishek Pandit-Subedi 		       dmp_hdr_t dmp_hdr, notify_change_t notify_change)
4059695ef87SAbhishek Pandit-Subedi {
4069695ef87SAbhishek Pandit-Subedi 	/* Driver must implement coredump() and dmp_hdr() functions for
4079695ef87SAbhishek Pandit-Subedi 	 * bluetooth devcoredump. The coredump() should trigger a coredump
4089695ef87SAbhishek Pandit-Subedi 	 * event on the controller when the device's coredump sysfs entry is
4099695ef87SAbhishek Pandit-Subedi 	 * written to. The dmp_hdr() should create a dump header to identify
4109695ef87SAbhishek Pandit-Subedi 	 * the controller/fw/driver info.
4119695ef87SAbhishek Pandit-Subedi 	 */
4129695ef87SAbhishek Pandit-Subedi 	if (!coredump || !dmp_hdr)
4139695ef87SAbhishek Pandit-Subedi 		return -EINVAL;
4149695ef87SAbhishek Pandit-Subedi 
4159695ef87SAbhishek Pandit-Subedi 	hci_dev_lock(hdev);
4169695ef87SAbhishek Pandit-Subedi 	hdev->dump.coredump = coredump;
4179695ef87SAbhishek Pandit-Subedi 	hdev->dump.dmp_hdr = dmp_hdr;
4189695ef87SAbhishek Pandit-Subedi 	hdev->dump.notify_change = notify_change;
4199695ef87SAbhishek Pandit-Subedi 	hdev->dump.supported = true;
4209695ef87SAbhishek Pandit-Subedi 	hdev->dump.timeout = DEVCOREDUMP_TIMEOUT;
4219695ef87SAbhishek Pandit-Subedi 	hci_dev_unlock(hdev);
4229695ef87SAbhishek Pandit-Subedi 
4239695ef87SAbhishek Pandit-Subedi 	return 0;
4249695ef87SAbhishek Pandit-Subedi }
4259695ef87SAbhishek Pandit-Subedi EXPORT_SYMBOL(hci_devcd_register);
4269695ef87SAbhishek Pandit-Subedi 
hci_devcd_enabled(struct hci_dev * hdev)4279695ef87SAbhishek Pandit-Subedi static inline bool hci_devcd_enabled(struct hci_dev *hdev)
4289695ef87SAbhishek Pandit-Subedi {
4299695ef87SAbhishek Pandit-Subedi 	return hdev->dump.supported;
4309695ef87SAbhishek Pandit-Subedi }
4319695ef87SAbhishek Pandit-Subedi 
hci_devcd_init(struct hci_dev * hdev,u32 dump_size)4329695ef87SAbhishek Pandit-Subedi int hci_devcd_init(struct hci_dev *hdev, u32 dump_size)
4339695ef87SAbhishek Pandit-Subedi {
4349695ef87SAbhishek Pandit-Subedi 	struct sk_buff *skb;
4359695ef87SAbhishek Pandit-Subedi 
4369695ef87SAbhishek Pandit-Subedi 	if (!hci_devcd_enabled(hdev))
4379695ef87SAbhishek Pandit-Subedi 		return -EOPNOTSUPP;
4389695ef87SAbhishek Pandit-Subedi 
4399695ef87SAbhishek Pandit-Subedi 	skb = alloc_skb(sizeof(dump_size), GFP_ATOMIC);
4409695ef87SAbhishek Pandit-Subedi 	if (!skb)
4419695ef87SAbhishek Pandit-Subedi 		return -ENOMEM;
4429695ef87SAbhishek Pandit-Subedi 
4439695ef87SAbhishek Pandit-Subedi 	hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_INIT;
444*0ab905c3SZijun Hu 	put_unaligned_le32(dump_size, skb_put(skb, 4));
4459695ef87SAbhishek Pandit-Subedi 
4469695ef87SAbhishek Pandit-Subedi 	skb_queue_tail(&hdev->dump.dump_q, skb);
4479695ef87SAbhishek Pandit-Subedi 	queue_work(hdev->workqueue, &hdev->dump.dump_rx);
4489695ef87SAbhishek Pandit-Subedi 
4499695ef87SAbhishek Pandit-Subedi 	return 0;
4509695ef87SAbhishek Pandit-Subedi }
4519695ef87SAbhishek Pandit-Subedi EXPORT_SYMBOL(hci_devcd_init);
4529695ef87SAbhishek Pandit-Subedi 
hci_devcd_append(struct hci_dev * hdev,struct sk_buff * skb)4539695ef87SAbhishek Pandit-Subedi int hci_devcd_append(struct hci_dev *hdev, struct sk_buff *skb)
4549695ef87SAbhishek Pandit-Subedi {
4559695ef87SAbhishek Pandit-Subedi 	if (!skb)
4569695ef87SAbhishek Pandit-Subedi 		return -ENOMEM;
4579695ef87SAbhishek Pandit-Subedi 
4589695ef87SAbhishek Pandit-Subedi 	if (!hci_devcd_enabled(hdev)) {
4599695ef87SAbhishek Pandit-Subedi 		kfree_skb(skb);
4609695ef87SAbhishek Pandit-Subedi 		return -EOPNOTSUPP;
4619695ef87SAbhishek Pandit-Subedi 	}
4629695ef87SAbhishek Pandit-Subedi 
4639695ef87SAbhishek Pandit-Subedi 	hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_SKB;
4649695ef87SAbhishek Pandit-Subedi 
4659695ef87SAbhishek Pandit-Subedi 	skb_queue_tail(&hdev->dump.dump_q, skb);
4669695ef87SAbhishek Pandit-Subedi 	queue_work(hdev->workqueue, &hdev->dump.dump_rx);
4679695ef87SAbhishek Pandit-Subedi 
4689695ef87SAbhishek Pandit-Subedi 	return 0;
4699695ef87SAbhishek Pandit-Subedi }
4709695ef87SAbhishek Pandit-Subedi EXPORT_SYMBOL(hci_devcd_append);
4719695ef87SAbhishek Pandit-Subedi 
hci_devcd_append_pattern(struct hci_dev * hdev,u8 pattern,u32 len)4729695ef87SAbhishek Pandit-Subedi int hci_devcd_append_pattern(struct hci_dev *hdev, u8 pattern, u32 len)
4739695ef87SAbhishek Pandit-Subedi {
4749695ef87SAbhishek Pandit-Subedi 	struct hci_devcoredump_skb_pattern p;
4759695ef87SAbhishek Pandit-Subedi 	struct sk_buff *skb;
4769695ef87SAbhishek Pandit-Subedi 
4779695ef87SAbhishek Pandit-Subedi 	if (!hci_devcd_enabled(hdev))
4789695ef87SAbhishek Pandit-Subedi 		return -EOPNOTSUPP;
4799695ef87SAbhishek Pandit-Subedi 
4809695ef87SAbhishek Pandit-Subedi 	skb = alloc_skb(sizeof(p), GFP_ATOMIC);
4819695ef87SAbhishek Pandit-Subedi 	if (!skb)
4829695ef87SAbhishek Pandit-Subedi 		return -ENOMEM;
4839695ef87SAbhishek Pandit-Subedi 
4849695ef87SAbhishek Pandit-Subedi 	p.pattern = pattern;
4859695ef87SAbhishek Pandit-Subedi 	p.len = len;
4869695ef87SAbhishek Pandit-Subedi 
4879695ef87SAbhishek Pandit-Subedi 	hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_PATTERN;
4889695ef87SAbhishek Pandit-Subedi 	skb_put_data(skb, &p, sizeof(p));
4899695ef87SAbhishek Pandit-Subedi 
4909695ef87SAbhishek Pandit-Subedi 	skb_queue_tail(&hdev->dump.dump_q, skb);
4919695ef87SAbhishek Pandit-Subedi 	queue_work(hdev->workqueue, &hdev->dump.dump_rx);
4929695ef87SAbhishek Pandit-Subedi 
4939695ef87SAbhishek Pandit-Subedi 	return 0;
4949695ef87SAbhishek Pandit-Subedi }
4959695ef87SAbhishek Pandit-Subedi EXPORT_SYMBOL(hci_devcd_append_pattern);
4969695ef87SAbhishek Pandit-Subedi 
hci_devcd_complete(struct hci_dev * hdev)4979695ef87SAbhishek Pandit-Subedi int hci_devcd_complete(struct hci_dev *hdev)
4989695ef87SAbhishek Pandit-Subedi {
4999695ef87SAbhishek Pandit-Subedi 	struct sk_buff *skb;
5009695ef87SAbhishek Pandit-Subedi 
5019695ef87SAbhishek Pandit-Subedi 	if (!hci_devcd_enabled(hdev))
5029695ef87SAbhishek Pandit-Subedi 		return -EOPNOTSUPP;
5039695ef87SAbhishek Pandit-Subedi 
5049695ef87SAbhishek Pandit-Subedi 	skb = alloc_skb(0, GFP_ATOMIC);
5059695ef87SAbhishek Pandit-Subedi 	if (!skb)
5069695ef87SAbhishek Pandit-Subedi 		return -ENOMEM;
5079695ef87SAbhishek Pandit-Subedi 
5089695ef87SAbhishek Pandit-Subedi 	hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_COMPLETE;
5099695ef87SAbhishek Pandit-Subedi 
5109695ef87SAbhishek Pandit-Subedi 	skb_queue_tail(&hdev->dump.dump_q, skb);
5119695ef87SAbhishek Pandit-Subedi 	queue_work(hdev->workqueue, &hdev->dump.dump_rx);
5129695ef87SAbhishek Pandit-Subedi 
5139695ef87SAbhishek Pandit-Subedi 	return 0;
5149695ef87SAbhishek Pandit-Subedi }
5159695ef87SAbhishek Pandit-Subedi EXPORT_SYMBOL(hci_devcd_complete);
5169695ef87SAbhishek Pandit-Subedi 
hci_devcd_abort(struct hci_dev * hdev)5179695ef87SAbhishek Pandit-Subedi int hci_devcd_abort(struct hci_dev *hdev)
5189695ef87SAbhishek Pandit-Subedi {
5199695ef87SAbhishek Pandit-Subedi 	struct sk_buff *skb;
5209695ef87SAbhishek Pandit-Subedi 
5219695ef87SAbhishek Pandit-Subedi 	if (!hci_devcd_enabled(hdev))
5229695ef87SAbhishek Pandit-Subedi 		return -EOPNOTSUPP;
5239695ef87SAbhishek Pandit-Subedi 
5249695ef87SAbhishek Pandit-Subedi 	skb = alloc_skb(0, GFP_ATOMIC);
5259695ef87SAbhishek Pandit-Subedi 	if (!skb)
5269695ef87SAbhishek Pandit-Subedi 		return -ENOMEM;
5279695ef87SAbhishek Pandit-Subedi 
5289695ef87SAbhishek Pandit-Subedi 	hci_dmp_cb(skb)->pkt_type = HCI_DEVCOREDUMP_PKT_ABORT;
5299695ef87SAbhishek Pandit-Subedi 
5309695ef87SAbhishek Pandit-Subedi 	skb_queue_tail(&hdev->dump.dump_q, skb);
5319695ef87SAbhishek Pandit-Subedi 	queue_work(hdev->workqueue, &hdev->dump.dump_rx);
5329695ef87SAbhishek Pandit-Subedi 
5339695ef87SAbhishek Pandit-Subedi 	return 0;
5349695ef87SAbhishek Pandit-Subedi }
5359695ef87SAbhishek Pandit-Subedi EXPORT_SYMBOL(hci_devcd_abort);
536