mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
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: Abhishek Pandit-Subedi <abhishekpandit@chromium.org> Signed-off-by: Manish Mandlik <mmandlik@google.com> Reviewed-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org> Reviewed-by: Chethan Tumkur Narayan <chethan.tumkur.narayan@intel.com> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
4f9c1a0896
commit
af395330ab
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user