diff options
Diffstat (limited to 'drivers/bluetooth/btmtk.c')
-rw-r--r-- | drivers/bluetooth/btmtk.c | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index c83ea3fd524f..786f775196ae 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -53,6 +53,56 @@ struct btmtk_section_map { }; } __packed; +static void btmtk_coredump(struct hci_dev *hdev) +{ + int err; + + err = __hci_cmd_send(hdev, 0xfd5b, 0, NULL); + if (err < 0) + bt_dev_err(hdev, "Coredump failed (%d)", err); +} + +static void btmtk_coredump_hdr(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btmediatek_data *data = hci_get_priv(hdev); + char buf[80]; + + snprintf(buf, sizeof(buf), "Controller Name: 0x%X\n", + data->dev_id); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Firmware Version: 0x%X\n", + data->cd_info.fw_version); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Driver: %s\n", + data->cd_info.driver_name); + skb_put_data(skb, buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "Vendor: MediaTek\n"); + skb_put_data(skb, buf, strlen(buf)); +} + +static void btmtk_coredump_notify(struct hci_dev *hdev, int state) +{ + struct btmediatek_data *data = hci_get_priv(hdev); + + switch (state) { + case HCI_DEVCOREDUMP_IDLE: + data->cd_info.state = HCI_DEVCOREDUMP_IDLE; + break; + case HCI_DEVCOREDUMP_ACTIVE: + data->cd_info.state = HCI_DEVCOREDUMP_ACTIVE; + break; + case HCI_DEVCOREDUMP_TIMEOUT: + case HCI_DEVCOREDUMP_ABORT: + case HCI_DEVCOREDUMP_DONE: + data->cd_info.state = HCI_DEVCOREDUMP_IDLE; + btmtk_reset_sync(hdev); + break; + } +} + int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname, wmt_cmd_sync_func_t wmt_cmd_sync) { @@ -300,6 +350,68 @@ void btmtk_reset_sync(struct hci_dev *hdev) } EXPORT_SYMBOL_GPL(btmtk_reset_sync); +int btmtk_register_coredump(struct hci_dev *hdev, const char *name, + u32 fw_version) +{ + struct btmediatek_data *data = hci_get_priv(hdev); + + if (!IS_ENABLED(CONFIG_DEV_COREDUMP)) + return -EOPNOTSUPP; + + data->cd_info.fw_version = fw_version; + data->cd_info.state = HCI_DEVCOREDUMP_IDLE; + data->cd_info.driver_name = name; + + return hci_devcd_register(hdev, btmtk_coredump, btmtk_coredump_hdr, + btmtk_coredump_notify); +} +EXPORT_SYMBOL_GPL(btmtk_register_coredump); + +int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btmediatek_data *data = hci_get_priv(hdev); + int err; + + if (!IS_ENABLED(CONFIG_DEV_COREDUMP)) + return 0; + + switch (data->cd_info.state) { + case HCI_DEVCOREDUMP_IDLE: + err = hci_devcd_init(hdev, MTK_COREDUMP_SIZE); + if (err < 0) + break; + data->cd_info.cnt = 0; + + /* It is supposed coredump can be done within 5 seconds */ + schedule_delayed_work(&hdev->dump.dump_timeout, + msecs_to_jiffies(5000)); + fallthrough; + case HCI_DEVCOREDUMP_ACTIVE: + default: + err = hci_devcd_append(hdev, skb); + if (err < 0) + break; + data->cd_info.cnt++; + + /* Mediatek coredump data would be more than MTK_COREDUMP_NUM */ + if (data->cd_info.cnt > MTK_COREDUMP_NUM && + skb->len > sizeof(MTK_COREDUMP_END) && + !memcmp((char *)&skb->data[skb->len - sizeof(MTK_COREDUMP_END)], + MTK_COREDUMP_END, sizeof(MTK_COREDUMP_END) - 1)) { + bt_dev_info(hdev, "Mediatek coredump end"); + hci_devcd_complete(hdev); + } + + break; + } + + if (err < 0) + kfree_skb(skb); + + return err; +} +EXPORT_SYMBOL_GPL(btmtk_process_coredump); + MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>"); MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION); |