Commit af395330 authored by Abhishek Pandit-Subedi's avatar Abhishek Pandit-Subedi Committed by Luiz Augusto von Dentz
Browse files

Bluetooth: btintel: Add Intel devcoredump support



Intercept debug exception events from the controller and put them into
a devcoredump using hci devcoredump APIs. The debug exception contains
data in a TLV format and it will be parsed in userspace.

Signed-off-by: default avatarAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Signed-off-by: default avatarManish Mandlik <mmandlik@google.com>
Reviewed-by: default avatarAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Reviewed-by: default avatarChethan Tumkur Narayan <chethan.tumkur.narayan@intel.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 4f9c1a08
Loading
Loading
Loading
Loading
+71 −1
Original line number Diff line number Diff line
@@ -43,6 +43,12 @@ struct cmd_write_boot_params {
	u8  fw_build_yy;
} __packed;

static struct {
	const char *driver_name;
	u8         hw_variant;
	u32        fw_build_num;
} coredump_info;

int btintel_check_bdaddr(struct hci_dev *hdev)
{
	struct hci_rp_read_bd_addr *bda;
@@ -315,6 +321,9 @@ int btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
		return -EINVAL;
	}

	coredump_info.hw_variant = ver->hw_variant;
	coredump_info.fw_build_num = ver->fw_build_num;

	bt_dev_info(hdev, "%s revision %u.%u build %u week %u %u",
		    variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f,
		    ver->fw_build_num, ver->fw_build_ww,
@@ -509,6 +518,9 @@ static int btintel_version_info_tlv(struct hci_dev *hdev,
		return -EINVAL;
	}

	coredump_info.hw_variant = INTEL_HW_VARIANT(version->cnvi_bt);
	coredump_info.fw_build_num = version->build_num;

	bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant,
		    2000 + (version->timestamp >> 8), version->timestamp & 0xff,
		    version->build_type, version->build_num);
@@ -1462,6 +1474,59 @@ int btintel_set_quality_report(struct hci_dev *hdev, bool enable)
}
EXPORT_SYMBOL_GPL(btintel_set_quality_report);

static void btintel_coredump(struct hci_dev *hdev)
{
	struct sk_buff *skb;

	skb = __hci_cmd_sync(hdev, 0xfc4e, 0, NULL, HCI_CMD_TIMEOUT);
	if (IS_ERR(skb)) {
		bt_dev_err(hdev, "Coredump failed (%ld)", PTR_ERR(skb));
		return;
	}

	kfree_skb(skb);
}

static void btintel_dmp_hdr(struct hci_dev *hdev, struct sk_buff *skb)
{
	char buf[80];

	snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n",
		 coredump_info.hw_variant);
	skb_put_data(skb, buf, strlen(buf));

	snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n",
		 coredump_info.fw_build_num);
	skb_put_data(skb, buf, strlen(buf));

	snprintf(buf, sizeof(buf), "Driver: %s\n", coredump_info.driver_name);
	skb_put_data(skb, buf, strlen(buf));

	snprintf(buf, sizeof(buf), "Vendor: Intel\n");
	skb_put_data(skb, buf, strlen(buf));
}

static int btintel_register_devcoredump_support(struct hci_dev *hdev)
{
	struct intel_debug_features features;
	int err;

	err = btintel_read_debug_features(hdev, &features);
	if (err) {
		bt_dev_info(hdev, "Error reading debug features");
		return err;
	}

	if (!(features.page1[0] & 0x3f)) {
		bt_dev_dbg(hdev, "Telemetry exception format not supported");
		return -EOPNOTSUPP;
	}

	hci_devcd_register(hdev, btintel_coredump, btintel_dmp_hdr, NULL);

	return err;
}

static const struct firmware *btintel_legacy_rom_get_fw(struct hci_dev *hdev,
					       struct intel_version *ver)
{
@@ -2597,6 +2662,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
			btintel_set_msft_opcode(hdev, ver.hw_variant);

			err = btintel_bootloader_setup(hdev, &ver);
			btintel_register_devcoredump_support(hdev);
			break;
		default:
			bt_dev_err(hdev, "Unsupported Intel hw variant (%u)",
@@ -2670,6 +2736,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
		btintel_set_msft_opcode(hdev, ver.hw_variant);

		err = btintel_bootloader_setup(hdev, &ver);
		btintel_register_devcoredump_support(hdev);
		break;
	case 0x17:
	case 0x18:
@@ -2692,6 +2759,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
					INTEL_HW_VARIANT(ver_tlv.cnvi_bt));

		err = btintel_bootloader_setup_tlv(hdev, &ver_tlv);
		btintel_register_devcoredump_support(hdev);
		break;
	default:
		bt_dev_err(hdev, "Unsupported Intel hw variant (%u)",
@@ -2741,7 +2809,7 @@ static int btintel_shutdown_combined(struct hci_dev *hdev)
	return 0;
}

int btintel_configure_setup(struct hci_dev *hdev)
int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name)
{
	hdev->manufacturer = 2;
	hdev->setup = btintel_setup_combined;
@@ -2750,6 +2818,8 @@ int btintel_configure_setup(struct hci_dev *hdev)
	hdev->set_diag = btintel_set_diag_combined;
	hdev->set_bdaddr = btintel_set_bdaddr;

	coredump_info.driver_name = driver_name;

	return 0;
}
EXPORT_SYMBOL_GPL(btintel_configure_setup);
+10 −2
Original line number Diff line number Diff line
@@ -143,6 +143,13 @@ struct btintel_loc_aware_reg {
	__le32 delta;
} __packed;

#define INTEL_TLV_TYPE_ID		0x01

#define INTEL_TLV_SYSTEM_EXCEPTION	0x00
#define INTEL_TLV_FATAL_EXCEPTION	0x01
#define INTEL_TLV_DEBUG_EXCEPTION	0x02
#define INTEL_TLV_TEST_EXCEPTION	0xDE

#define INTEL_HW_PLATFORM(cnvx_bt)	((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
#define INTEL_HW_VARIANT(cnvx_bt)	((u8)(((cnvx_bt) & 0x003f0000) >> 16))
#define INTEL_CNVX_TOP_TYPE(cnvx_top)	((cnvx_top) & 0x00000fff)
@@ -212,7 +219,7 @@ int btintel_read_boot_params(struct hci_dev *hdev,
			     struct intel_boot_params *params);
int btintel_download_firmware(struct hci_dev *dev, struct intel_version *ver,
			      const struct firmware *fw, u32 *boot_param);
int btintel_configure_setup(struct hci_dev *hdev);
int btintel_configure_setup(struct hci_dev *hdev, const char *driver_name);
void btintel_bootup(struct hci_dev *hdev, const void *ptr, unsigned int len);
void btintel_secure_send_result(struct hci_dev *hdev,
				const void *ptr, unsigned int len);
@@ -293,7 +300,8 @@ static inline int btintel_download_firmware(struct hci_dev *dev,
	return -EOPNOTSUPP;
}

static inline int btintel_configure_setup(struct hci_dev *hdev)
static inline int btintel_configure_setup(struct hci_dev *hdev,
					  const char *driver_name)
{
	return -ENODEV;
}
+47 −7
Original line number Diff line number Diff line
@@ -2386,16 +2386,47 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer,
	return btusb_recv_bulk(data, buffer, count);
}

static int btusb_intel_diagnostics(struct hci_dev *hdev, struct sk_buff *skb)
{
	struct intel_tlv *tlv = (void *)&skb->data[5];

	/* The first event is always an event type TLV */
	if (tlv->type != INTEL_TLV_TYPE_ID)
		goto recv_frame;

	switch (tlv->val[0]) {
	case INTEL_TLV_SYSTEM_EXCEPTION:
	case INTEL_TLV_FATAL_EXCEPTION:
	case INTEL_TLV_DEBUG_EXCEPTION:
	case INTEL_TLV_TEST_EXCEPTION:
		/* Generate devcoredump from exception */
		if (!hci_devcd_init(hdev, skb->len)) {
			hci_devcd_append(hdev, skb);
			hci_devcd_complete(hdev);
		} else {
			bt_dev_err(hdev, "Failed to generate devcoredump");
			kfree_skb(skb);
		}
		return 0;
	default:
		bt_dev_err(hdev, "Invalid exception type %02X", tlv->val[0]);
	}

recv_frame:
	return hci_recv_frame(hdev, skb);
}

static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
{
	if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
	struct hci_event_hdr *hdr = (void *)skb->data;
	const char diagnostics_hdr[] = { 0x87, 0x80, 0x03 };

	if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
	    hdr->plen > 0) {
		const void *ptr = skb->data + HCI_EVENT_HDR_SIZE + 1;
		unsigned int len = skb->len - HCI_EVENT_HDR_SIZE - 1;

		if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
			switch (skb->data[2]) {
			case 0x02:
				/* When switching to the operational firmware
@@ -2414,6 +2445,15 @@ static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
				break;
			}
		}

		/* Handle all diagnostics events separately. May still call
		 * hci_recv_frame.
		 */
		if (len >= sizeof(diagnostics_hdr) &&
		    memcmp(&skb->data[2], diagnostics_hdr,
			   sizeof(diagnostics_hdr)) == 0) {
			return btusb_intel_diagnostics(hdev, skb);
		}
	}

	return hci_recv_frame(hdev, skb);
@@ -4018,7 +4058,7 @@ static int btusb_probe(struct usb_interface *intf,

	/* Combined Intel Device setup to support multiple setup routine */
	if (id->driver_info & BTUSB_INTEL_COMBINED) {
		err = btintel_configure_setup(hdev);
		err = btintel_configure_setup(hdev, btusb_driver.name);
		if (err)
			goto out_free_dev;