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

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
......@@ -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);
......
......@@ -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;
}
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment