Commit 0b701513 authored by Jing Cai's avatar Jing Cai Committed by Luiz Augusto von Dentz

Bluetooth: btusb: mediatek: add MediaTek devcoredump support

This patch implement function .coredump() and dmp_hdr() in btusb
driver for MediaTek controller.  FW core dump was triggered by FW
specific event to show something unexpected happened in the controller.

The driver would be responsible for collecting and uploading the device
core dump pieces in hci driver using core dump API. Once we finished
the whole process, the driver would reset the controller to recover the
kind of fatal error.
Co-developed-by: default avatarChris Lu <chris.lu@mediatek.com>
Signed-off-by: default avatarChris Lu <chris.lu@mediatek.com>
Co-developed-by: default avatarSean Wang <sean.wang@mediatek.com>
Signed-off-by: default avatarSean Wang <sean.wang@mediatek.com>
Signed-off-by: default avatarJing Cai <jing.cai@mediatek.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent 25b6d759
...@@ -53,6 +53,56 @@ struct btmtk_section_map { ...@@ -53,6 +53,56 @@ struct btmtk_section_map {
}; };
} __packed; } __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, int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
wmt_cmd_sync_func_t wmt_cmd_sync) wmt_cmd_sync_func_t wmt_cmd_sync)
{ {
...@@ -300,6 +350,68 @@ void btmtk_reset_sync(struct hci_dev *hdev) ...@@ -300,6 +350,68 @@ void btmtk_reset_sync(struct hci_dev *hdev)
} }
EXPORT_SYMBOL_GPL(btmtk_reset_sync); 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("Sean Wang <sean.wang@mediatek.com>");
MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>"); MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>");
MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION); MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION);
......
...@@ -22,6 +22,10 @@ ...@@ -22,6 +22,10 @@
#define MT7921_DLSTATUS 0x7c053c10 #define MT7921_DLSTATUS 0x7c053c10
#define BT_DL_STATE BIT(1) #define BT_DL_STATE BIT(1)
#define MTK_COREDUMP_SIZE (1024 * 1000)
#define MTK_COREDUMP_END "coredump end"
#define MTK_COREDUMP_NUM 255
enum { enum {
BTMTK_WMT_PATCH_DWNLD = 0x1, BTMTK_WMT_PATCH_DWNLD = 0x1,
BTMTK_WMT_TEST = 0x2, BTMTK_WMT_TEST = 0x2,
...@@ -122,9 +126,17 @@ struct btmtk_hci_wmt_params { ...@@ -122,9 +126,17 @@ struct btmtk_hci_wmt_params {
typedef int (*btmtk_reset_sync_func_t)(struct hci_dev *, void *); typedef int (*btmtk_reset_sync_func_t)(struct hci_dev *, void *);
struct btmtk_coredump_info {
const char *driver_name;
u32 fw_version;
u16 cnt;
int state;
};
struct btmediatek_data { struct btmediatek_data {
u32 dev_id; u32 dev_id;
btmtk_reset_sync_func_t reset_sync; btmtk_reset_sync_func_t reset_sync;
struct btmtk_coredump_info cd_info;
}; };
typedef int (*wmt_cmd_sync_func_t)(struct hci_dev *, typedef int (*wmt_cmd_sync_func_t)(struct hci_dev *,
...@@ -141,6 +153,11 @@ int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname, ...@@ -141,6 +153,11 @@ int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
wmt_cmd_sync_func_t wmt_cmd_sync); wmt_cmd_sync_func_t wmt_cmd_sync);
void btmtk_reset_sync(struct hci_dev *hdev); void btmtk_reset_sync(struct hci_dev *hdev);
int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
u32 fw_version);
int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb);
#else #else
static inline int btmtk_set_bdaddr(struct hci_dev *hdev, static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
...@@ -164,4 +181,15 @@ static int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname, ...@@ -164,4 +181,15 @@ static int btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
static void btmtk_reset_sync(struct hci_dev *hdev) static void btmtk_reset_sync(struct hci_dev *hdev)
{ {
} }
static int btmtk_register_coredump(struct hci_dev *hdev, const char *name,
u32 fw_version)
{
return -EOPNOTSUPP;
}
static int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
{
return -EOPNOTSUPP;
}
#endif #endif
...@@ -3153,6 +3153,10 @@ static int btusb_mtk_setup(struct hci_dev *hdev) ...@@ -3153,6 +3153,10 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
mediatek->dev_id = dev_id; mediatek->dev_id = dev_id;
mediatek->reset_sync = btusb_mtk_reset; mediatek->reset_sync = btusb_mtk_reset;
err = btmtk_register_coredump(hdev, btusb_driver.name, fw_version);
if (err < 0)
bt_dev_err(hdev, "Failed to register coredump (%d)", err);
switch (dev_id) { switch (dev_id) {
case 0x7663: case 0x7663:
fwname = FIRMWARE_MT7663; fwname = FIRMWARE_MT7663;
...@@ -3313,6 +3317,7 @@ static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -3313,6 +3317,7 @@ static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
{ {
struct btusb_data *data = hci_get_drvdata(hdev); struct btusb_data *data = hci_get_drvdata(hdev);
u16 handle = le16_to_cpu(hci_acl_hdr(skb)->handle); u16 handle = le16_to_cpu(hci_acl_hdr(skb)->handle);
struct sk_buff *skb_cd;
switch (handle) { switch (handle) {
case 0xfc6f: /* Firmware dump from device */ case 0xfc6f: /* Firmware dump from device */
...@@ -3320,6 +3325,15 @@ static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -3320,6 +3325,15 @@ static int btusb_recv_acl_mtk(struct hci_dev *hdev, struct sk_buff *skb)
* suspend and thus disable auto-suspend. * suspend and thus disable auto-suspend.
*/ */
usb_disable_autosuspend(data->udev); usb_disable_autosuspend(data->udev);
/* We need to forward the diagnostic packet to userspace daemon
* for backward compatibility, so we have to clone the packet
* extraly for the in-kernel coredump support.
*/
skb_cd = skb_clone(skb, GFP_ATOMIC);
if (skb_cd)
btmtk_process_coredump(hdev, skb_cd);
fallthrough; fallthrough;
case 0x05ff: /* Firmware debug logging 1 */ case 0x05ff: /* Firmware debug logging 1 */
case 0x05fe: /* Firmware debug logging 2 */ case 0x05fe: /* Firmware debug logging 2 */
......
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