lkml.org 
[lkml]   [2020]   [Nov]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH] MTK mt7921 driver upstream
Date
From: Peter Tsao <peter.tsao@mediatek.com>

Signed-off-by: Peter Tsao <peter.tsao@mediatek.com>
---
drivers/bluetooth/btmtk_buffer_mode.c | 263 +
drivers/bluetooth/btmtk_main.c | 5517 +++++++++++++++++
drivers/bluetooth/include/btmtk_buffer_mode.h | 78 +
drivers/bluetooth/include/btmtk_chip_if.h | 30 +
drivers/bluetooth/include/btmtk_define.h | 304 +
drivers/bluetooth/include/btmtk_drv.h | 157 +
drivers/bluetooth/include/btmtk_main.h | 587 ++
drivers/bluetooth/include/sdio/btmtk_sdio.h | 147 +
drivers/bluetooth/include/uart/btmtk_uart.h | 86 +
drivers/bluetooth/include/usb/btmtk_usb.h | 100 +
drivers/bluetooth/sdio/btmtksdio.c | 2004 ++++++
drivers/bluetooth/usb/btmtkusb.c | 3218 ++++++++++
12 files changed, 12491 insertions(+)
create mode 100644 drivers/bluetooth/btmtk_buffer_mode.c
create mode 100644 drivers/bluetooth/btmtk_main.c
create mode 100644 drivers/bluetooth/include/btmtk_buffer_mode.h
create mode 100644 drivers/bluetooth/include/btmtk_chip_if.h
create mode 100644 drivers/bluetooth/include/btmtk_define.h
create mode 100644 drivers/bluetooth/include/btmtk_drv.h
create mode 100644 drivers/bluetooth/include/btmtk_main.h
create mode 100644 drivers/bluetooth/include/sdio/btmtk_sdio.h
create mode 100644 drivers/bluetooth/include/uart/btmtk_uart.h
create mode 100644 drivers/bluetooth/include/usb/btmtk_usb.h
create mode 100644 drivers/bluetooth/sdio/btmtksdio.c
create mode 100644 drivers/bluetooth/usb/btmtkusb.c

diff --git a/drivers/bluetooth/btmtk_buffer_mode.c b/drivers/bluetooth/btmtk_buffer_mode.c
new file mode 100644
index 000000000000..a4216ce4a440
--- /dev/null
+++ b/drivers/bluetooth/btmtk_buffer_mode.c
@@ -0,0 +1,263 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#include "btmtk_define.h"
+#include "btmtk_main.h"
+
+static struct btmtk_buffer_mode_struct btmtk_buffer_mode;
+
+static int btmtk_buffer_mode_check_auto_mode(struct btmtk_buffer_mode_struct *buffer_mode)
+{
+ u16 addr = 1;
+ u8 value = 0;
+
+ if (buffer_mode->efuse_mode != AUTO_MODE)
+ return 0;
+
+ if (btmtk_efuse_read(buffer_mode->bdev, addr, &value)) {
+ BTMTK_WARN("read fail");
+ BTMTK_WARN("Use EEPROM Bin file mode");
+ buffer_mode->efuse_mode = BIN_FILE_MODE;
+ return -EIO;
+ }
+
+ if (value == ((buffer_mode->bdev->chip_id & 0xFF00) >> 8)) {
+ BTMTK_WARN("get efuse[1]: 0x%02x", value);
+ BTMTK_WARN("use efuse mode");
+ buffer_mode->efuse_mode = EFUSE_MODE;
+ } else {
+ BTMTK_WARN("get efuse[1]: 0x%02x", value);
+ BTMTK_WARN("Use EEPROM Bin file mode");
+ buffer_mode->efuse_mode = BIN_FILE_MODE;
+ }
+
+ return 0;
+}
+
+static int btmtk_buffer_mode_parse_mode(uint8_t *buf, size_t buf_size)
+{
+ int efuse_mode = EFUSE_MODE;
+ char *p_buf = NULL;
+ char *ptr = NULL, *p = NULL;
+
+ if (!buf) {
+ BTMTK_WARN("buf is null");
+ return efuse_mode;
+ } else if (buf_size < (strlen(BUFFER_MODE_SWITCH_FIELD) + 2)) {
+ BTMTK_WARN("incorrect buf size(%d)", (int)buf_size);
+ return efuse_mode;
+ }
+
+ p_buf = kmalloc(buf_size + 1, GFP_KERNEL);
+ if (!p_buf)
+ return efuse_mode;
+ memcpy(p_buf, buf, buf_size);
+ p_buf[buf_size] = '\0';
+
+ /* find string */
+ p = ptr = strstr(p_buf, BUFFER_MODE_SWITCH_FIELD);
+ if (!ptr) {
+ BTMTK_ERR("Can't find %s", BUFFER_MODE_SWITCH_FIELD);
+ goto out;
+ }
+
+ if (p > p_buf) {
+ p--;
+ while ((*p == ' ') && (p != p_buf))
+ p--;
+ if (*p == '#') {
+ BTMTK_ERR("It's not EEPROM - Bin file mode");
+ goto out;
+ }
+ }
+
+ /* check access mode */
+ ptr += (strlen(BUFFER_MODE_SWITCH_FIELD) + 1);
+ BTMTK_WARN("It's EEPROM bin mode: %c", *ptr);
+ efuse_mode = *ptr - '0';
+ if (efuse_mode > AUTO_MODE)
+ efuse_mode = EFUSE_MODE;
+out:
+ kfree(p_buf);
+ return efuse_mode;
+}
+
+static int btmtk_buffer_mode_set_addr(struct btmtk_buffer_mode_struct *buffer_mode)
+{
+ u8 cmd[] = {0x01, 0x1A, 0xFC, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ /* To-Do, for event check */
+ u8 event[] = {0x04, 0x0E, 0x04, 0x01, 0x1A, 0xFC, 0x00};
+ int ret = 0;
+
+ cmd[9] = buffer_mode->bt0_mac[0];
+ cmd[8] = buffer_mode->bt0_mac[1];
+ cmd[7] = buffer_mode->bt0_mac[2];
+ cmd[6] = buffer_mode->bt0_mac[3];
+ cmd[5] = buffer_mode->bt0_mac[4];
+ cmd[4] = buffer_mode->bt0_mac[5];
+
+ BTMTK_INFO_RAW(cmd, sizeof(cmd), "%s: Send", __func__);
+ ret = btmtk_main_send_cmd(buffer_mode->bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_CMD_FROM_DRV);
+
+ BTMTK_INFO("%s done", __func__);
+ return ret;
+}
+
+static int btmtk_buffer_mode_set_radio(struct btmtk_buffer_mode_struct *buffer_mode)
+{
+ u8 cmd[] = {0x01, 0x2C, 0xFC, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ /* To-Do, for event check */
+ u8 event[] = {0x04, 0x0E, 0x04, 0x01, 0x2C, 0xFC, 0x00};
+ int ret = 0;
+
+ cmd[4] = buffer_mode->bt0_radio.radio_0 & 0x3F; /* edr_init_pwr */
+ cmd[8] = buffer_mode->bt0_radio.radio_2 & 0x3F; /* ble_default_pwr */
+ cmd[9] = buffer_mode->bt0_radio.radio_1 & 0x3F; /* edr_max_pwr */
+ cmd[11] = (buffer_mode->bt0_radio.radio_0 & 0xC0) >> 6; /* edr_pwr_mode */
+
+ BTMTK_INFO_RAW(cmd, sizeof(cmd), "%s: Send", __func__);
+ ret = btmtk_main_send_cmd(buffer_mode->bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_CMD_FROM_DRV);
+
+ BTMTK_INFO("%s done", __func__);
+ return ret;
+}
+
+static int btmtk_buffer_mode_set_group_boundary(struct btmtk_buffer_mode_struct *buffer_mode)
+{
+ u8 cmd[] = {0x01, 0xEA, 0xFC, 0x09, 0x02, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ /* To-Do, for event check */
+ u8 event[] = {0x04, 0x0E, 0x04, 0x01, 0xEA, 0xFC, 0x00};
+ int ret = 0;
+
+ memcpy(&cmd[8], buffer_mode->bt0_ant0_grp_boundary,
+ sizeof(buffer_mode->bt0_ant0_grp_boundary));
+
+ BTMTK_INFO_RAW(cmd, sizeof(cmd), "%s: Send", __func__);
+ ret = btmtk_main_send_cmd(buffer_mode->bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_CMD_FROM_DRV);
+
+ BTMTK_INFO("%s done", __func__);
+ return ret;
+}
+
+static int btmtk_buffer_mode_set_power_offset(struct btmtk_buffer_mode_struct *buffer_mode)
+{
+ u8 cmd[] = {0x01, 0xEA, 0xFC, 0x0A, 0x02, 0x0A,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ /* To-Do, for event check */
+ u8 event[] = {0x04, 0x0E, 0x04, 0x01, 0xEA, 0xFC, 0x00};
+ int ret = 0;
+
+ memcpy(&cmd[8], buffer_mode->bt0_ant0_pwr_offset, sizeof(buffer_mode->bt0_ant0_pwr_offset));
+
+ BTMTK_INFO_RAW(cmd, sizeof(cmd), "%s: Send", __func__);
+ ret = btmtk_main_send_cmd(buffer_mode->bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_CMD_FROM_DRV);
+
+ BTMTK_INFO("%s done", __func__);
+ return ret;
+}
+
+int btmtk_buffer_mode_send(struct btmtk_buffer_mode_struct *buffer_mode)
+{
+ int ret = 0;
+
+ if (buffer_mode == NULL) {
+ BTMTK_INFO("buffer_mode is NULL, not support");
+ return -EIO;
+ }
+
+ if (btmtk_buffer_mode_check_auto_mode(buffer_mode)) {
+ BTMTK_ERR("check auto mode failed");
+ return -EIO;
+ }
+
+ if (buffer_mode->efuse_mode == BIN_FILE_MODE) {
+ ret = btmtk_buffer_mode_set_addr(buffer_mode);
+ if (ret < 0)
+ BTMTK_ERR("set addr failed");
+
+ ret = btmtk_buffer_mode_set_radio(buffer_mode);
+ if (ret < 0)
+ BTMTK_ERR("set radio failed");
+
+ ret = btmtk_buffer_mode_set_group_boundary(buffer_mode);
+ if (ret < 0)
+ BTMTK_ERR("set group_boundary failed");
+
+ ret = btmtk_buffer_mode_set_power_offset(buffer_mode);
+ if (ret < 0)
+ BTMTK_ERR("set power_offset failed");
+ }
+ return 0;
+}
+
+void btmtk_buffer_mode_initialize(struct btmtk_dev *bdev,
+ struct btmtk_buffer_mode_struct **buffer_mode)
+{
+ int ret = 0;
+ u32 code_len = 0;
+
+ btmtk_buffer_mode.bdev = bdev;
+ ret = btmtk_load_code_from_setting_files(BUFFER_MODE_SWITCH_FILE, bdev->intf_dev,
+ &code_len, bdev);
+
+ btmtk_buffer_mode.efuse_mode = btmtk_buffer_mode_parse_mode(bdev->setting_file, code_len);
+ if (btmtk_buffer_mode.efuse_mode == EFUSE_MODE)
+ return;
+
+ if (bdev->flavor)
+ snprintf(btmtk_buffer_mode.file_name, MAX_BIN_FILE_NAME_LEN, "EEPROM_MT%04x_1a.bin",
+ bdev->chip_id & 0xffff);
+ else
+ snprintf(btmtk_buffer_mode.file_name, MAX_BIN_FILE_NAME_LEN, "EEPROM_MT%04x_1.bin",
+ bdev->chip_id & 0xffff);
+
+ ret = btmtk_load_code_from_setting_files(btmtk_buffer_mode.file_name,
+ bdev->intf_dev, &code_len, bdev);
+ if (ret < 0) {
+ BTMTK_ERR("set load %s failed", btmtk_buffer_mode.file_name);
+ return;
+ }
+
+ memcpy(btmtk_buffer_mode.bt0_mac, &bdev->setting_file[BT0_MAC_OFFSET],
+ BUFFER_MODE_MAC_LENGTH);
+ memcpy(btmtk_buffer_mode.bt1_mac, &bdev->setting_file[BT1_MAC_OFFSET],
+ BUFFER_MODE_MAC_LENGTH);
+ memcpy(&btmtk_buffer_mode.bt0_radio, &bdev->setting_file[BT0_RADIO_OFFSET],
+ BUFFER_MODE_RADIO_LENGTH);
+ memcpy(&btmtk_buffer_mode.bt1_radio, &bdev->setting_file[BT1_RADIO_OFFSET],
+ BUFFER_MODE_RADIO_LENGTH);
+ memcpy(btmtk_buffer_mode.bt0_ant0_grp_boundary, &bdev->setting_file[BT0_GROUP_ANT0_OFFSET],
+ BUFFER_MODE_GROUP_LENGTH);
+ memcpy(btmtk_buffer_mode.bt0_ant1_grp_boundary, &bdev->setting_file[BT0_GROUP_ANT1_OFFSET],
+ BUFFER_MODE_GROUP_LENGTH);
+ memcpy(btmtk_buffer_mode.bt1_ant0_grp_boundary, &bdev->setting_file[BT1_GROUP_ANT0_OFFSET],
+ BUFFER_MODE_GROUP_LENGTH);
+ memcpy(btmtk_buffer_mode.bt1_ant1_grp_boundary, &bdev->setting_file[BT1_GROUP_ANT1_OFFSET],
+ BUFFER_MODE_GROUP_LENGTH);
+ memcpy(btmtk_buffer_mode.bt0_ant0_pwr_offset, &bdev->setting_file[BT0_CAL_ANT0_OFFSET],
+ BUFFER_MODE_CAL_LENGTH);
+ memcpy(btmtk_buffer_mode.bt0_ant1_pwr_offset, &bdev->setting_file[BT0_CAL_ANT1_OFFSET],
+ BUFFER_MODE_CAL_LENGTH);
+ memcpy(btmtk_buffer_mode.bt1_ant0_pwr_offset, &bdev->setting_file[BT1_CAL_ANT0_OFFSET],
+ BUFFER_MODE_CAL_LENGTH);
+ memcpy(btmtk_buffer_mode.bt1_ant1_pwr_offset, &bdev->setting_file[BT1_CAL_ANT1_OFFSET],
+ BUFFER_MODE_CAL_LENGTH);
+
+ *buffer_mode = &btmtk_buffer_mode;
+}
+
diff --git a/drivers/bluetooth/btmtk_main.c b/drivers/bluetooth/btmtk_main.c
new file mode 100644
index 000000000000..748684a36b91
--- /dev/null
+++ b/drivers/bluetooth/btmtk_main.c
@@ -0,0 +1,5517 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/input.h>
+#include <linux/pm_wakeup.h>
+#include <linux/reboot.h>
+#include <linux/string.h>
+
+#include "btmtk_define.h"
+#include "btmtk_main.h"
+
+#define MTKBT_UNSLEEPABLE_LOCK(x, y) spin_lock_irqsave(x, y)
+#define MTKBT_UNSLEEPABLE_UNLOCK(x, y) spin_unlock_irqsave(x, y)
+
+/* TODO, need to modify the state mutex for each hci dev*/
+static DEFINE_MUTEX(btmtk_chip_state_mutex);
+#define CHIP_STATE_MUTEX_LOCK() mutex_lock(&btmtk_chip_state_mutex)
+#define CHIP_STATE_MUTEX_UNLOCK() mutex_unlock(&btmtk_chip_state_mutex)
+static DEFINE_MUTEX(btmtk_fops_state_mutex);
+#define FOPS_MUTEX_LOCK() mutex_lock(&btmtk_fops_state_mutex)
+#define FOPS_MUTEX_UNLOCK() mutex_unlock(&btmtk_fops_state_mutex)
+
+const struct file_operations BT_fopsfwlog = {
+ .open = btmtk_fops_openfwlog,
+ .release = btmtk_fops_closefwlog,
+ .read = btmtk_fops_readfwlog,
+ .write = btmtk_fops_writefwlog,
+ .poll = btmtk_fops_pollfwlog,
+ .unlocked_ioctl = btmtk_fops_unlocked_ioctlfwlog
+};
+
+static int btmtk_fops_get_state(struct btmtk_dev *bdev);
+
+/**
+ * Global parameters(mtkbt_)
+ */
+u8 btmtk_log_lvl = BTMTK_LOG_LVL_DEF;
+
+/* To support dynamic mount of interface can be probed */
+static int btmtk_intf_num = BT_MCU_MINIMUM_INTERFACE_NUM;
+/* To allow g_bdev being sized from btmtk_intf_num setting */
+static struct btmtk_dev **g_bdev;
+/* For fwlog dev node setting */
+static struct btmtk_fops_fwlog *g_fwlog;
+
+const u8 RESET_EVENT[] = { 0x0E, 0x04, 0x01, 0x03, 0x0c, 0x00 };
+const u8 READ_ISO_PACKET_SIZE_CMD[] = { 0x01, 0x98, 0xFD, 0x02 };
+
+u8 wmt_over_hci_header[] = { 0x01, 0x6F, 0xFC};
+
+/*btmtk main information*/
+static struct btmtk_main_info main_info;
+
+/* bluetooth kpi */
+static u8 btmtk_bluetooth_kpi;
+
+/* save Hci Snoop for debug*/
+static u8 hci_cmd_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_MAX_BUF_SIZE];
+static u8 hci_cmd_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+static u16 hci_cmd_actual_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+static unsigned int hci_cmd_timestamp[HCI_SNOOP_ENTRY_NUM];
+static int hci_cmd_index = HCI_SNOOP_ENTRY_NUM - 1;
+
+static u8 hci_event_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_MAX_BUF_SIZE];
+static u8 hci_event_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+static u16 hci_event_actual_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+static unsigned int hci_event_timestamp[HCI_SNOOP_ENTRY_NUM];
+static int hci_event_index = HCI_SNOOP_ENTRY_NUM - 1;
+
+static u8 hci_adv_event_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_MAX_BUF_SIZE];
+static u8 hci_adv_event_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+static u16 hci_adv_event_actual_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+static unsigned int hci_adv_event_timestamp[HCI_SNOOP_ENTRY_NUM];
+static int hci_adv_event_index = HCI_SNOOP_ENTRY_NUM - 1;
+
+static u8 hci_acl_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_MAX_BUF_SIZE];
+static u8 hci_acl_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+static u16 hci_acl_actual_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+static unsigned int hci_acl_timestamp[HCI_SNOOP_ENTRY_NUM];
+static int hci_acl_index = HCI_SNOOP_ENTRY_NUM - 1;
+
+/* State machine table that clarify through each HIF events,
+ * To specify HIF event on
+ * Entering / End / Error
+ */
+static const struct btmtk_cif_state g_cif_state[] = {
+ /* HIF_EVENT_PROBE */
+ {BTMTK_STATE_PROBE, BTMTK_STATE_WORKING, BTMTK_STATE_DISCONNECT},
+ /* HIF_EVENT_DISCONNECT */
+ {BTMTK_STATE_DISCONNECT, BTMTK_STATE_DISCONNECT, BTMTK_STATE_DISCONNECT},
+ /* HIF_EVENT_SUSPEND */
+ {BTMTK_STATE_SUSPEND, BTMTK_STATE_SUSPEND, BTMTK_STATE_FW_DUMP},
+ /* HIF_EVENT_RESUME */
+ {BTMTK_STATE_RESUME, BTMTK_STATE_WORKING, BTMTK_STATE_FW_DUMP},
+ /* HIF_EVENT_STANDBY */
+ {BTMTK_STATE_STANDBY, BTMTK_STATE_STANDBY, BTMTK_STATE_FW_DUMP},
+ /* BTMTK_STATE_FW_DUMP */
+ {BTMTK_STATE_SUBSYS_RESET, BTMTK_STATE_WORKING, BTMTK_STATE_FW_DUMP},
+ /* BTMTK_STATE_FW_DUMP */
+ {BTMTK_STATE_FW_DUMP, BTMTK_STATE_DISCONNECT, BTMTK_STATE_FW_DUMP},
+ /* BTMTK_STATE_FW_DUMP */
+ {BTMTK_STATE_FW_DUMP, BTMTK_STATE_FW_DUMP, BTMTK_STATE_FW_DUMP},
+};
+
+__weak int btmtk_cif_register(void)
+{
+ BTMTK_ERR("No cif register function");
+ return -1;
+}
+
+__weak int btmtk_cif_deregister(void)
+{
+ BTMTK_ERR("No cif deregister function");
+ return -1;
+}
+
+__weak int btmtk_cif_send_calibration(struct btmtk_dev *bdev)
+{
+ BTMTK_ERR("No cif %s", __func__);
+ return -1;
+}
+
+__weak void do_gettimeofday(struct timeval *tv)
+{
+ struct timespec64 ts;
+
+ ktime_get_real_ts64(&ts);
+ tv->tv_sec = ts.tv_sec;
+ tv->tv_usec = ts.tv_nsec / 1000;
+}
+
+static int btmtk_enter_standby(void);
+static int btmtk_send_hci_tci_set_sleep_cmd_766x(struct btmtk_dev *bdev);
+
+void btmtk_woble_wake_lock(struct btmtk_dev *bdev)
+{
+ if (bdev->bt_cfg.support_woble_wakelock) {
+ BTMTK_INFO("%s: enter", __func__);
+ __pm_stay_awake(main_info.woble_ws);
+ BTMTK_INFO("%s: exit", __func__);
+ }
+}
+
+void btmtk_woble_wake_unlock(struct btmtk_dev *bdev)
+{
+ if (bdev->bt_cfg.support_woble_wakelock) {
+ BTMTK_INFO("%s: enter", __func__);
+ __pm_relax(main_info.woble_ws);
+ BTMTK_INFO("%s: exit", __func__);
+ }
+}
+
+void btmtk_fwdump_wake_lock(struct btmtk_dev *bdev)
+{
+ BTMTK_INFO("%s: enter", __func__);
+ __pm_stay_awake(main_info.fwdump_ws);
+ BTMTK_INFO("%s: exit", __func__);
+}
+
+void btmtk_fwdump_wake_unlock(struct btmtk_dev *bdev)
+{
+ BTMTK_INFO("%s: enter", __func__);
+ __pm_relax(main_info.fwdump_ws);
+ BTMTK_INFO("%s: exit", __func__);
+}
+
+/*get 1 byte only*/
+int btmtk_efuse_read(struct btmtk_dev *bdev, u16 addr, u8 *value)
+{
+ u8 efuse_r[] = {0x01, 0x6F, 0xFC, 0x0E,
+ 0x01, 0x0D, 0x0A, 0x00, 0x02, 0x04,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};/*4 sub block number(sub block 0~3)*/
+
+ u8 efuse_r_event[] = {0x04, 0xE4, 0x1E, 0x02, 0x0D, 0x1A, 0x00, 02, 04};
+ /*check event
+ *04 E4 LEN(1B) 02 0D LEN(2Byte) 02 04 ADDR(2Byte) VALUE(4B) ADDR(2Byte) VALUE(4Byte)
+ *ADDR(2Byte) VALUE(4B) ADDR(2Byte) VALUE(4Byte)
+ */
+ int ret = 0;
+ u8 sub_block_addr_in_event = 0;
+ u16 sub_block = (addr / 16) * 4;
+ u8 temp = 0;
+
+ efuse_r[10] = sub_block & 0xFF;
+ efuse_r[11] = (sub_block & 0xFF00) >> 8;
+ efuse_r[12] = (sub_block + 1) & 0xFF;
+ efuse_r[13] = ((sub_block + 1) & 0xFF00) >> 8;
+ efuse_r[14] = (sub_block + 2) & 0xFF;
+ efuse_r[15] = ((sub_block + 2) & 0xFF00) >> 8;
+ efuse_r[16] = (sub_block + 3) & 0xFF;
+ efuse_r[17] = ((sub_block + 3) & 0xFF00) >> 8;
+
+ ret = btmtk_main_send_cmd(bdev,
+ efuse_r, sizeof(efuse_r),
+ efuse_r_event, sizeof(efuse_r_event),
+ 0, 0, BTMTK_TX_CMD_FROM_DRV);
+ if (ret) {
+ BTMTK_WARN("btmtk_main_send_cmd error");
+ return ret;
+ }
+
+ if (memcmp(bdev->io_buf, efuse_r_event, sizeof(efuse_r_event)) == 0) {
+ /*compare rxbuf format ok, compare addr*/
+ BTMTK_DBG("compare rxbuf format ok");
+ if (efuse_r[10] == bdev->io_buf[9] &&
+ efuse_r[11] == bdev->io_buf[10] &&
+ efuse_r[12] == bdev->io_buf[15] &&
+ efuse_r[13] == bdev->io_buf[16] &&
+ efuse_r[14] == bdev->io_buf[21] &&
+ efuse_r[15] == bdev->io_buf[22] &&
+ efuse_r[16] == bdev->io_buf[27] &&
+ efuse_r[17] == bdev->io_buf[28]) {
+ BTMTK_DBG("address compare ok");
+ /*Get value*/
+ sub_block_addr_in_event = ((addr / 16) / 4);/*cal block num*/
+ temp = addr % 16;
+ BTMTK_DBG("address in block %d", temp);
+ switch (temp) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ *value = bdev->io_buf[11 + temp];
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ *value = bdev->io_buf[17 + temp];
+ break;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ *value = bdev->io_buf[22 + temp];
+ break;
+
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ *value = bdev->io_buf[34 + temp];
+ break;
+ }
+ } else {
+ BTMTK_WARN("address compare fail");
+ ret = -1;
+ }
+ } else {
+ BTMTK_WARN("compare rxbuf format fail");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int btmtk_skb_enq_fwlog(struct hci_dev *hdev,
+ void *src, u32 len, u8 type, struct sk_buff_head *queue)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct sk_buff *skb_tmp = NULL;
+ ulong flags = 0;
+ int retry = 10, index = FWLOG_TL_SIZE;
+
+ do {
+ skb_tmp = alloc_skb(len + FWLOG_PRSV_LEN, GFP_ATOMIC);
+ if (skb_tmp) {
+ break;
+ } else if (retry <= 0) {
+ pr_err("%s: alloc_skb return 0, error", __func__);
+ return -ENOMEM;
+ }
+ pr_err("%s: alloc_skb return 0, error, retry = %d", __func__, retry);
+ } while (retry-- > 0);
+
+ if (type) {
+ skb_tmp->data[0] = FWLOG_TYPE;
+ /* 01 for dongle index */
+ skb_tmp->data[index] = FWLOG_DONGLE_IDX;
+ skb_tmp->data[index + 1] = sizeof(bdev->dongle_index);
+ skb_tmp->data[index + 2] = bdev->dongle_index;
+ index += (FWLOG_ATTR_RX_LEN_LEN + FWLOG_ATTR_TYPE_LEN);
+ /* 11 for rx data*/
+ skb_tmp->data[index] = FWLOG_RX;
+ if (type == HCI_ACLDATA_PKT || type == HCI_EVENT_PKT || type == HCI_COMMAND_PKT) {
+ skb_tmp->data[index + 1] = len & 0x00FF;
+ skb_tmp->data[index + 2] = (len & 0xFF00) >> 8;
+ skb_tmp->data[index + 3] = type;
+ index += (HCI_TYPE_SIZE + FWLOG_ATTR_RX_LEN_LEN + FWLOG_ATTR_TYPE_LEN);
+ } else {
+ skb_tmp->data[index + 1] = len & 0x00FF;
+ skb_tmp->data[index + 2] = (len & 0xFF00) >> 8;
+ index += (FWLOG_ATTR_RX_LEN_LEN + FWLOG_ATTR_TYPE_LEN);
+ }
+ memcpy(&skb_tmp->data[index], src, len);
+ skb_tmp->data[1] = (len + index - FWLOG_TL_SIZE) & 0x00FF;
+ skb_tmp->data[2] = ((len + index - FWLOG_TL_SIZE) & 0xFF00) >> 8;
+ skb_tmp->len = len + index;
+ } else {
+ memcpy(skb_tmp->data, src, len);
+ skb_tmp->len = len;
+ }
+
+ spin_lock_irqsave(&g_fwlog->fwlog_lock, flags);
+ skb_queue_tail(queue, skb_tmp);
+ spin_unlock_irqrestore(&g_fwlog->fwlog_lock, flags);
+ return 0;
+}
+
+static int btmtk_dispatch_data_bluetooth_kpi(struct hci_dev *hdev, u8 *buf, int len, u8 type)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ static u8 fwlog_blocking_warn;
+ int ret = 0;
+
+ if (btmtk_bluetooth_kpi &&
+ skb_queue_len(&g_fwlog->fwlog_queue) < FWLOG_BLUETOOTH_KPI_QUEUE_COUNT) {
+ /* sent event to queue, picus tool will log it for bluetooth KPI feature */
+ if (btmtk_skb_enq_fwlog(bdev->hdev, buf, len, type, &g_fwlog->fwlog_queue) == 0) {
+ wake_up_interruptible(&g_fwlog->fw_log_inq);
+ fwlog_blocking_warn = 0;
+ }
+ } else {
+ if (fwlog_blocking_warn == 0) {
+ fwlog_blocking_warn = 1;
+ pr_warn("btmtk_usb fwlog queue size is full(bluetooth_kpi)");
+ }
+ }
+ return ret;
+}
+
+ssize_t btmtk_fops_readfwlog(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+ int copy_len = 0;
+ ulong flags = 0;
+ struct sk_buff *skb = NULL;
+
+ /* picus read a queue, it may occur performace issue */
+ spin_lock_irqsave(&g_fwlog->fwlog_lock, flags);
+ if (skb_queue_len(&g_fwlog->fwlog_queue))
+ skb = skb_dequeue(&g_fwlog->fwlog_queue);
+
+ spin_unlock_irqrestore(&g_fwlog->fwlog_lock, flags);
+ if (!skb)
+ return 0;
+
+ if (skb->len <= count) {
+ if (copy_to_user(buf, skb->data, skb->len))
+ BT_ERR("%s: copy_to_user failed!", __func__);
+
+ copy_len = skb->len;
+ } else {
+ BTMTK_DBG("%s: socket buffer length error(count: %d, skb.len: %d)",
+ __func__, (int)count, skb->len);
+ }
+ kfree_skb(skb);
+ return copy_len;
+}
+
+static int whole_reset_flag;
+ssize_t btmtk_fops_writefwlog(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ int i = 0, len = 0, ret = -1;
+ int hci_idx = 0;
+ int vlen = 0, index = 3;
+ struct sk_buff *skb = NULL;
+ int state = BTMTK_STATE_INIT;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+ struct btmtk_dev *bdev = NULL;
+
+ u8 *i_fwlog_buf = kmalloc(HCI_MAX_COMMAND_BUF_SIZE, GFP_KERNEL);
+ u8 *o_fwlog_buf = kmalloc(HCI_MAX_COMMAND_SIZE, GFP_KERNEL);
+
+ for (i = 0; i < btmtk_intf_num; i++) {
+ /* Find valid dev for already probe interface. */
+ if (g_bdev[i]->hdev) {
+ bdev = g_bdev[i];
+
+ state = btmtk_get_chip_state(bdev);
+ if (state != BTMTK_STATE_WORKING) {
+ ret = -EBADFD;
+ goto exit;
+ }
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not open yet(%d)!", __func__, fstate);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ if (bdev->power_state == BTMTK_DONGLE_STATE_POWER_OFF) {
+ BTMTK_WARN("%s: dongle state already power off, do not write",
+ __func__);
+ ret = -EFAULT;
+ goto exit;
+ }
+ }
+ }
+
+ if (count > HCI_MAX_COMMAND_BUF_SIZE) {
+ BTMTK_ERR("%s: your command is larger than maximum length, count = %zd\n",
+ __func__, count);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ memset(i_fwlog_buf, 0, HCI_MAX_COMMAND_BUF_SIZE);
+ memset(o_fwlog_buf, 0, HCI_MAX_COMMAND_SIZE);
+ if (copy_from_user(i_fwlog_buf, buf, count) != 0) {
+ BT_ERR("%s: Failed to copy data", __func__);
+ ret = -ENODATA;
+ goto exit;
+ }
+
+ /* For log level, EX: echo log_lvl=1 > /dev/stpbtfwlog */
+ if (strncmp(i_fwlog_buf, "log_lvl=", strlen("log_lvl=")) == 0) {
+ u8 val = *(i_fwlog_buf + strlen("log_lvl=")) - 48;
+
+ if (val > BTMTK_LOG_LVL_MAX || val <= 0) {
+ BTMTK_ERR("Got incorrect value for log level(%d)", val);
+ count = -EINVAL;
+ goto exit;
+ }
+ btmtk_log_lvl = val;
+ BT_INFO("btmtk_log_lvl = %d", btmtk_log_lvl);
+ ret = count;
+ goto exit;
+ }
+
+ if (strncmp(i_fwlog_buf, "whole chip reset", strlen("whole chip reset")) == 0) {
+ BT_INFO("whole chip reset start");
+ whole_reset_flag = 1;
+ schedule_work(&bdev->reset_waker);
+ ret = count;
+ goto exit;
+ }
+ if (strncmp(i_fwlog_buf, "subsys chip reset", strlen("subsys chip reset")) == 0) {
+ BT_INFO("subsys chip reset");
+ schedule_work(&bdev->reset_waker);
+ ret = count;
+ goto exit;
+ }
+
+ /* For bperf, EX: echo bperf=1 > /dev/stpbtfwlog */
+ if (strncmp(i_fwlog_buf, "bperf=", strlen("bperf=")) == 0) {
+ u8 val = *(i_fwlog_buf + strlen("bperf=")) - 48;
+
+ btmtk_bluetooth_kpi = val;
+ BT_INFO("%s: set bluetooth KPI feature(bperf) to %d",
+ __func__, btmtk_bluetooth_kpi);
+ ret = count;
+ goto exit;
+ }
+
+ /* hci input command format : echo 01 be fc 01 05 > /dev/stpbtfwlog */
+ /* We take the data from index three to end. */
+ for (i = 0; i < count; i++) {
+ char *pos = i_fwlog_buf + i;
+ char temp_str[3] = {'\0'};
+ long res = 0;
+
+ if (*pos == ' ' || *pos == '\t' || *pos == '\r' || *pos == '\n') {
+ continue;
+ } else if (*pos == '0' && (*(pos + 1) == 'x' || *(pos + 1) == 'X')) {
+ i++;
+ continue;
+ } else if (!(*pos >= '0' && *pos <= '9') && !(*pos >= 'A' && *pos <= 'F') &&
+ !(*pos >= 'a' && *pos <= 'f')) {
+ BTMTK_ERR("%s: There is an invalid input(%c)", __func__, *pos);
+ ret = -EINVAL;
+ goto exit;
+ }
+ temp_str[0] = *pos;
+ temp_str[1] = *(pos + 1);
+ i++;
+ ret = kstrtol(temp_str, 16, &res);
+ if (ret == 0)
+ o_fwlog_buf[len++] = (u8)res;
+ else
+ BT_ERR("%s: Convert %s failed(%d)", __func__, temp_str, ret);
+ }
+
+ if (o_fwlog_buf[0] != HCI_COMMAND_PKT && o_fwlog_buf[0] != FWLOG_TYPE) {
+ BT_ERR("%s: Not support 0x%02X yet", __func__, o_fwlog_buf[0]);
+ ret = -EPROTONOSUPPORT;
+ goto exit;
+ }
+ /* check HCI command length */
+ if (len > HCI_MAX_COMMAND_SIZE) {
+ BT_ERR("%s: command is larger than max buf size, length = %d", __func__, len);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ skb = alloc_skb(sizeof(buf) + BT_SKB_RESERVE, GFP_ATOMIC);
+ if (!skb) {
+ BTMTK_ERR("%s allocate skb failed!!", __func__);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ /* send HCI command */
+ bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+
+ /* format */
+ /* 0xF0 XX XX 00 01 AA 10 BB CC CC CC CC ... */
+ /* XX XX total length */
+ /* 00 : hci index setting type */
+ /* AA hci index to indicate which hci send following command*/
+ /* 10 : raw data type*/
+ /* BB command length */
+ /* CC command */
+ if (o_fwlog_buf[0] == FWLOG_TYPE) {
+ while (index < ((o_fwlog_buf[2] << 8) + o_fwlog_buf[1])) {
+ switch (o_fwlog_buf[index]) {
+ case FWLOG_HCI_IDX: /* hci index */
+ vlen = o_fwlog_buf[index + 1];
+ hci_idx = o_fwlog_buf[index + 2];
+ BTMTK_DBG("%s: send to hci%d", __func__, hci_idx);
+ index += (FWLOG_ATTR_TL_SIZE + vlen);
+ break;
+ case FWLOG_TX: /* tx raw data */
+ vlen = o_fwlog_buf[index + 1];
+ memcpy(skb->data, o_fwlog_buf + index + FWLOG_ATTR_TL_SIZE, vlen);
+ skb->len = vlen;
+ index = index + FWLOG_ATTR_TL_SIZE + vlen;
+ break;
+ default:
+ BTMTK_WARN("Invalid opcode");
+ ret = -1;
+ goto free_skb;
+ }
+ }
+ } else {
+ memcpy(skb->data, o_fwlog_buf, len);
+ skb->len = len;
+ g_bdev[hci_idx]->opcode_usr[0] = o_fwlog_buf[1];
+ g_bdev[hci_idx]->opcode_usr[1] = o_fwlog_buf[2];
+ }
+
+ /* won't send command if g_bdev not define */
+ if (!g_bdev[hci_idx]->hdev) {
+ BTMTK_DBG("g_bdev[%d] not define", hci_idx);
+ ret = count;
+ goto free_skb;
+ }
+
+ /* clean fwlog queue before enable picus log */
+ if (skb_queue_len(&g_fwlog->fwlog_queue) && skb->data[0] == 0x01 &&
+ skb->data[1] == 0x5d && skb->data[2] == 0xfc && skb->data[4] == 0x00) {
+ skb_queue_purge(&g_fwlog->fwlog_queue);
+ BTMTK_INFO("clean fwlog_queue, skb_queue_len = %d",
+ skb_queue_len(&g_fwlog->fwlog_queue));
+ }
+
+ btmtk_dispatch_data_bluetooth_kpi(bdev->hdev, skb->data, skb->len, KPI_WITHOUT_TYPE);
+
+ ret = main_info.hif_hook.send_cmd(g_bdev[hci_idx], skb, 0, 0, (int)BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0) {
+ BTMTK_ERR("%s failed!!", __func__);
+ goto free_skb;
+ } else {
+ BTMTK_INFO("%s: OK", __func__);
+ }
+
+ BTMTK_INFO("%s: Write end(len: %d)", __func__, len);
+ ret = count;
+ goto exit;
+
+free_skb:
+ kfree_skb(skb);
+ skb = NULL;
+exit:
+ kfree(i_fwlog_buf);
+ kfree(o_fwlog_buf);
+
+ return ret; /* If input is correct should return the same length */
+}
+
+int btmtk_fops_openfwlog(struct inode *inode, struct file *file)
+{
+ BTMTK_INFO("%s: Start.", __func__);
+
+ return 0;
+}
+
+int btmtk_fops_closefwlog(struct inode *inode, struct file *file)
+{
+ BTMTK_INFO("%s: Start.", __func__);
+
+ return 0;
+}
+
+long btmtk_fops_unlocked_ioctlfwlog(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ BTMTK_INFO("%s: Start.", __func__);
+
+ return 0;
+}
+
+unsigned int btmtk_fops_pollfwlog(struct file *file, poll_table *wait)
+{
+ unsigned int mask = 0;
+
+ poll_wait(file, &g_fwlog->fw_log_inq, wait);
+ if (skb_queue_len(&g_fwlog->fwlog_queue) > 0)
+ mask |= POLLIN | POLLRDNORM; /* readable */
+
+ return mask;
+}
+
+static void btmtk_free_fw_cfg_struct(struct fw_cfg_struct *fw_cfg,
+ int count)
+{
+ int i = 0;
+
+ for (i = 0; i < count; i++) {
+ if (fw_cfg[i].content) {
+ BTMTK_INFO("%s:kfree %d", __func__, i);
+ kfree(fw_cfg[i].content);
+ fw_cfg[i].content = NULL;
+ fw_cfg[i].length = 0;
+ } else {
+ fw_cfg[i].length = 0;
+ }
+ }
+}
+
+void btmtk_free_setting_file(struct btmtk_dev *bdev)
+{
+ BTMTK_INFO("%s begin", __func__);
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev == NULL", __func__);
+ return;
+ }
+
+ btmtk_free_fw_cfg_struct(bdev->woble_setting_apcf, WOBLE_SETTING_COUNT);
+ btmtk_free_fw_cfg_struct(bdev->woble_setting_apcf_fill_mac, WOBLE_SETTING_COUNT);
+ btmtk_free_fw_cfg_struct(bdev->woble_setting_apcf_fill_mac_location, WOBLE_SETTING_COUNT);
+ btmtk_free_fw_cfg_struct(bdev->woble_setting_apcf_resume, WOBLE_SETTING_COUNT);
+ btmtk_free_fw_cfg_struct(&bdev->woble_setting_radio_off, 1);
+ btmtk_free_fw_cfg_struct(&bdev->woble_setting_radio_off_status_event, 1);
+ btmtk_free_fw_cfg_struct(&bdev->woble_setting_radio_off_comp_event, 1);
+ btmtk_free_fw_cfg_struct(&bdev->woble_setting_radio_on, 1);
+ btmtk_free_fw_cfg_struct(&bdev->woble_setting_radio_on_status_event, 1);
+ btmtk_free_fw_cfg_struct(&bdev->woble_setting_radio_on_comp_event, 1);
+ btmtk_free_fw_cfg_struct(&bdev->woble_setting_wakeup_type, 1);
+
+ bdev->woble_setting_len = 0;
+
+ btmtk_free_fw_cfg_struct(&bdev->bt_cfg.picus_filter, 1);
+ btmtk_free_fw_cfg_struct(&bdev->bt_cfg.picus_enable, 1);
+ btmtk_free_fw_cfg_struct(bdev->bt_cfg.phase1_wmt_cmd, PHASE1_WMT_CMD_COUNT);
+ btmtk_free_fw_cfg_struct(bdev->bt_cfg.vendor_cmd, VENDOR_CMD_COUNT);
+
+ if (bdev->bt_cfg.support_woble_by_eint) {
+ if (bdev->wobt_irq != 0 && atomic_read(&(bdev->irq_enable_count)) == 1) {
+ BTMTK_INFO("disable BT IRQ:%d", bdev->wobt_irq);
+ atomic_dec(&(bdev->irq_enable_count));
+ disable_irq_nosync(bdev->wobt_irq);
+ } else
+ BTMTK_INFO("irq_enable count:%d", atomic_read(&(bdev->irq_enable_count)));
+ free_irq(bdev->wobt_irq, bdev);
+ }
+
+ memset(&bdev->bt_cfg, 0, sizeof(bdev->bt_cfg));
+ /* reset pin initial value need to be -1, used to judge after
+ * disconnected before probe, can't do chip reset
+ */
+ bdev->bt_cfg.dongle_reset_gpio_pin = -1;
+}
+
+void btmtk_initialize_cfg_items(struct btmtk_dev *bdev)
+{
+ BTMTK_INFO("%s begin", __func__);
+ if (bdev == NULL) {
+ BTMTK_ERR("%s: bdev is NULL", __func__);
+ return;
+ }
+
+ bdev->bt_cfg.dongle_reset_gpio_pin = 220;
+ bdev->bt_cfg.support_dongle_reset = 0;
+ bdev->bt_cfg.support_full_fw_dump = 0;
+ bdev->bt_cfg.support_unify_woble = 1;
+ bdev->bt_cfg.unify_woble_type = 0;
+ bdev->bt_cfg.support_woble_by_eint = 0;
+ bdev->bt_cfg.support_woble_for_bt_disable = 0;
+ bdev->bt_cfg.support_woble_wakelock = 0;
+ bdev->bt_cfg.reset_stack_after_woble = 0;
+ bdev->bt_cfg.support_auto_picus = 0;
+ bdev->bt_cfg.support_picus_to_host = 0;
+ bdev->bt_cfg.support_bt_single_sku = 0;
+ btmtk_free_fw_cfg_struct(&bdev->bt_cfg.picus_filter, 1);
+ btmtk_free_fw_cfg_struct(&bdev->bt_cfg.picus_enable, 1);
+ btmtk_free_fw_cfg_struct(bdev->bt_cfg.phase1_wmt_cmd, PHASE1_WMT_CMD_COUNT);
+ btmtk_free_fw_cfg_struct(bdev->bt_cfg.vendor_cmd, VENDOR_CMD_COUNT);
+
+ BTMTK_INFO("%s end", __func__);
+}
+
+int btmtk_get_chip_state(struct btmtk_dev *bdev)
+{
+ int state = BTMTK_STATE_INIT;
+
+ CHIP_STATE_MUTEX_LOCK();
+ state = bdev->interface_state;
+ CHIP_STATE_MUTEX_UNLOCK();
+
+ return state;
+}
+
+void btmtk_set_chip_state(struct btmtk_dev *bdev, int new_state)
+{
+ static const char * const state_msg[] = {
+ "UNKNOWN", "INIT", "DISCONNECT", "PROBE", "WORKING", "SUSPEND", "RESUME",
+ "FW_DUMP", "STANDBY", "SUBSYS_RESET",
+ };
+
+ BTMTK_INFO("%s: %s(%d) -> %s(%d)", __func__, state_msg[bdev->interface_state],
+ bdev->interface_state, state_msg[new_state], new_state);
+
+ CHIP_STATE_MUTEX_LOCK();
+ bdev->interface_state = new_state;
+ CHIP_STATE_MUTEX_UNLOCK();
+}
+
+static int btmtk_fops_get_state(struct btmtk_dev *bdev)
+{
+ int state = BTMTK_FOPS_STATE_INIT;
+
+ FOPS_MUTEX_LOCK();
+ state = bdev->fops_state;
+ FOPS_MUTEX_UNLOCK();
+
+ return state;
+}
+
+static void btmtk_fops_set_state(struct btmtk_dev *bdev, int new_state)
+{
+ static const char * const fstate_msg[] = {
+ "UNKNOWN", "INIT", "OPENING", "OPENED", "CLOSING", "CLOSED",
+ };
+
+ BTMTK_INFO("%s: FOPS_%s(%d) -> FOPS_%s(%d)", __func__, fstate_msg[bdev->fops_state],
+ bdev->fops_state, fstate_msg[new_state], new_state);
+ FOPS_MUTEX_LOCK();
+ bdev->fops_state = new_state;
+ FOPS_MUTEX_UNLOCK();
+}
+
+unsigned long btmtk_kallsyms_lookup_name(const char *name)
+{
+ unsigned long ret = 0;
+
+ ret = kallsyms_lookup_name(name);
+ if (ret) {
+#ifdef CONFIG_ARM
+#ifdef CONFIG_THUMB2_KERNEL
+ /*set bit 0 in address for thumb mode*/
+ ret |= 1;
+#endif
+#endif
+ }
+ return ret;
+}
+
+struct btmtk_main_info *btmtk_get_main_info(void)
+{
+ return &main_info;
+}
+
+static void btmtk_hci_snoop_print_to_log(void)
+{
+ int counter, index;
+
+ BTMTK_INFO("HCI Command Dump");
+ BTMTK_INFO("Using A5 A5 to separator the head 32 bytes and the tail 32 bytes data");
+ BTMTK_INFO("index(len)(timestamp:us) :HCI Command");
+ index = hci_cmd_index + 1;
+ if (index >= HCI_SNOOP_ENTRY_NUM)
+ index = 0;
+ for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+ if (hci_cmd_len[index] > 0)
+ BTMTK_INFO_RAW(hci_cmd_buf[index], hci_cmd_len[index],
+ "time(%u)-act_len(%d)-len(%d):", hci_cmd_timestamp[index],
+ hci_cmd_actual_len[index], hci_cmd_len[index]);
+ index++;
+ if (index >= HCI_SNOOP_ENTRY_NUM)
+ index = 0;
+ }
+
+ BTMTK_INFO("HCI Event Dump");
+ BTMTK_INFO("Using A5 A5 to separator the head 32 bytes and the tail 32 bytes data");
+ BTMTK_INFO("index(len)(timestamp:us) :HCI Event");
+ index = hci_event_index + 1;
+ if (index >= HCI_SNOOP_ENTRY_NUM)
+ index = 0;
+ for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+ if (hci_event_len[index] > 0)
+ BTMTK_INFO_RAW(hci_event_buf[index], hci_event_len[index],
+ "time(%u)-act_len(%d)-len(%d):", hci_event_timestamp[index],
+ hci_event_actual_len[index], hci_event_len[index]);
+ index++;
+ if (index >= HCI_SNOOP_ENTRY_NUM)
+ index = 0;
+ }
+
+ BTMTK_INFO("HCI ADV Event Dump");
+ BTMTK_INFO("Using A5 A5 to separator the head 32 bytes and the tail 32 bytes data");
+ BTMTK_INFO("index(len)(timestamp:us) :HCI ADV Event");
+ index = hci_adv_event_index + 1;
+ if (index >= HCI_SNOOP_ENTRY_NUM)
+ index = 0;
+ for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+ if (hci_adv_event_len[index] > 0)
+ BTMTK_INFO_RAW(hci_adv_event_buf[index], hci_adv_event_len[index],
+ "time(%u)-act_len(%d)-len(%d):", hci_adv_event_timestamp[index],
+ hci_adv_event_actual_len[index], hci_adv_event_len[index]);
+ index++;
+ if (index >= HCI_SNOOP_ENTRY_NUM)
+ index = 0;
+ }
+
+ BTMTK_INFO("HCI ACL Dump");
+ BTMTK_INFO("Using A5 A5 to separator the head 32 bytes and the tail 32 bytes data");
+ BTMTK_INFO("index(len)(timestamp:us) :ACL");
+ index = hci_acl_index + 1;
+ if (index >= HCI_SNOOP_ENTRY_NUM)
+ index = 0;
+ for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+ if (hci_acl_len[index] > 0) {
+ BTMTK_INFO_RAW(hci_acl_buf[index], hci_acl_len[index],
+ "time(%u)-act_len(%d)-len(%d):", hci_acl_timestamp[index],
+ hci_acl_actual_len[index], hci_acl_len[index]);
+ }
+ index++;
+ if (index >= HCI_SNOOP_ENTRY_NUM)
+ index = 0;
+ }
+}
+
+static unsigned int btmtk_hci_snoop_get_microseconds(void)
+{
+ struct timeval now;
+
+ do_gettimeofday(&now);
+ return now.tv_sec * 1000000 + now.tv_usec;
+}
+
+void btmtk_hci_snoop_save_cmd(u32 len, u8 *buf)
+{
+ u32 copy_len = HCI_SNOOP_BUF_SIZE;
+ u32 copy_tail_len = HCI_SNOOP_BUF_SIZE;
+ u8 separator_char[] = {0xA5, 0xA5};
+ u8 *copy_tail_buf;
+
+ if (buf && len > 0) {
+ if (len < HCI_SNOOP_BUF_SIZE) {
+ copy_len = len;
+ copy_tail_len = 0;
+ } else if (len > HCI_SNOOP_BUF_SIZE && len <= HCI_SNOOP_BUF_SIZE * 2)
+ copy_tail_len = len - copy_len;
+
+ hci_cmd_len[hci_cmd_index] = copy_len & 0xff;
+ hci_cmd_actual_len[hci_cmd_index] = len & 0xffff;
+ hci_cmd_timestamp[hci_cmd_index] = btmtk_hci_snoop_get_microseconds();
+ memset(hci_cmd_buf[hci_cmd_index], 0, HCI_SNOOP_MAX_BUF_SIZE);
+ memcpy(hci_cmd_buf[hci_cmd_index], buf, copy_len & 0xff);
+ /* save less then 32 bytes data in the buffer tail, using A5 A5 to
+ * separator the head 32 bytes data and the tail 32 bytes data
+ */
+ if (copy_tail_len > 0) {
+ copy_tail_buf = buf + len - copy_tail_len;
+ hci_cmd_len[hci_cmd_index] +=
+ (copy_tail_len + sizeof(separator_char)) & 0xff;
+ memcpy(hci_cmd_buf[hci_cmd_index] + copy_len, separator_char,
+ sizeof(separator_char));
+ memcpy(hci_cmd_buf[hci_cmd_index] + copy_len + sizeof(separator_char),
+ copy_tail_buf, copy_tail_len);
+ }
+
+ hci_cmd_index--;
+ if (hci_cmd_index < 0)
+ hci_cmd_index = HCI_SNOOP_ENTRY_NUM - 1;
+ }
+}
+
+void btmtk_hci_snoop_save_adv_event(u32 len, u8 *buf)
+{
+ u32 copy_len = HCI_SNOOP_BUF_SIZE;
+ u32 copy_tail_len = HCI_SNOOP_BUF_SIZE;
+ u8 separator_char[] = {0xA5, 0xA5};
+ u8 *copy_tail_buf;
+
+ if (buf && len > 0) {
+ if (len < HCI_SNOOP_BUF_SIZE) {
+ copy_len = len;
+ copy_tail_len = 0;
+ } else if (len > HCI_SNOOP_BUF_SIZE && len <= HCI_SNOOP_BUF_SIZE * 2)
+ copy_tail_len = len - copy_len;
+
+ hci_adv_event_len[hci_adv_event_index] = copy_len & 0xff;
+ hci_adv_event_actual_len[hci_adv_event_index] = len & 0xffff;
+ hci_adv_event_timestamp[hci_adv_event_index] = btmtk_hci_snoop_get_microseconds();
+ memset(hci_adv_event_buf[hci_adv_event_index], 0, HCI_SNOOP_MAX_BUF_SIZE);
+ memcpy(hci_adv_event_buf[hci_adv_event_index], buf, copy_len);
+ /* save less then 32 bytes data in the buffer tail, using A5 A5 to
+ * separator the head 32 bytes data and the tail 32 bytes data
+ */
+ if (copy_tail_len > 0) {
+ copy_tail_buf = buf + len - copy_tail_len;
+ hci_adv_event_len[hci_adv_event_index] +=
+ (copy_tail_len + sizeof(separator_char)) & 0xff;
+ memcpy(hci_adv_event_buf[hci_adv_event_index] + copy_len, separator_char,
+ sizeof(separator_char));
+ memcpy(hci_adv_event_buf[hci_adv_event_index] + copy_len +
+ sizeof(separator_char), copy_tail_buf, copy_tail_len);
+ }
+
+ hci_adv_event_index--;
+ if (hci_adv_event_index < 0)
+ hci_adv_event_index = HCI_SNOOP_ENTRY_NUM - 1;
+ }
+}
+
+void btmtk_hci_snoop_save_event(u32 len, u8 *buf)
+{
+ u32 copy_len = HCI_SNOOP_BUF_SIZE;
+ u32 copy_tail_len = HCI_SNOOP_BUF_SIZE;
+ u8 separator_char[] = {0xA5, 0xA5};
+ u8 *copy_tail_buf;
+
+ if (buf && len > 0) {
+ if (len < HCI_SNOOP_BUF_SIZE) {
+ copy_len = len;
+ copy_tail_len = 0;
+ } else if (len > HCI_SNOOP_BUF_SIZE && len <= HCI_SNOOP_BUF_SIZE * 2)
+ copy_tail_len = len - copy_len;
+
+ hci_event_len[hci_event_index] = copy_len & 0xff;
+ hci_event_actual_len[hci_event_index] = len & 0xffff;
+ hci_event_timestamp[hci_event_index] = btmtk_hci_snoop_get_microseconds();
+ memset(hci_event_buf[hci_event_index], 0, HCI_SNOOP_MAX_BUF_SIZE);
+ memcpy(hci_event_buf[hci_event_index], buf, copy_len);
+ /* save less then 32 bytes data in the buffer tail, using A5 A5 to
+ * separator the head 32 bytes data and the tail 32 bytes data
+ */
+ if (copy_tail_len > 0) {
+ copy_tail_buf = buf + len - copy_tail_len;
+ hci_event_len[hci_event_index] +=
+ (copy_tail_len + sizeof(separator_char)) & 0xff;
+ memcpy(hci_event_buf[hci_event_index] + copy_len, separator_char,
+ sizeof(separator_char));
+ memcpy(hci_event_buf[hci_event_index] + copy_len + sizeof(separator_char),
+ copy_tail_buf, copy_tail_len);
+ }
+
+ hci_event_index--;
+ if (hci_event_index < 0)
+ hci_event_index = HCI_SNOOP_ENTRY_NUM - 1;
+ }
+}
+
+void btmtk_hci_snoop_save_acl(u32 len, u8 *buf)
+{
+ u32 copy_len = HCI_SNOOP_BUF_SIZE;
+ u32 copy_tail_len = HCI_SNOOP_BUF_SIZE;
+ u8 separator_char[] = {0xA5, 0xA5};
+ u8 *copy_tail_buf;
+
+ if (buf && len > 0) {
+ if (len < HCI_SNOOP_BUF_SIZE) {
+ copy_len = len;
+ copy_tail_len = 0;
+ } else if (len > HCI_SNOOP_BUF_SIZE && len <= HCI_SNOOP_BUF_SIZE * 2)
+ copy_tail_len = len - copy_len;
+
+ hci_acl_len[hci_acl_index] = copy_len & 0xff;
+ hci_acl_actual_len[hci_acl_index] = len & 0xffff;
+ hci_acl_timestamp[hci_acl_index] = btmtk_hci_snoop_get_microseconds();
+ memset(hci_acl_buf[hci_acl_index], 0, HCI_SNOOP_MAX_BUF_SIZE);
+ memcpy(hci_acl_buf[hci_acl_index], buf, copy_len & 0xff);
+ /* save less then 32 bytes data in the buffer tail, using A5 A5 to
+ * separator the head 32 bytes data and the tail 32 bytes data
+ */
+ if (copy_tail_len > 0) {
+ copy_tail_buf = buf + len - copy_tail_len;
+ hci_acl_len[hci_acl_index] += (copy_tail_len +
+ sizeof(separator_char)) & 0xff;
+ memcpy(hci_acl_buf[hci_acl_index] + copy_len, separator_char,
+ sizeof(separator_char));
+ memcpy(hci_acl_buf[hci_acl_index] + copy_len + sizeof(separator_char),
+ copy_tail_buf, copy_tail_len);
+ }
+
+ hci_acl_index--;
+ if (hci_acl_index < 0)
+ hci_acl_index = HCI_SNOOP_ENTRY_NUM - 1;
+ }
+}
+
+void btmtk_hci_snoop_print(u32 len, const u8 *buf)
+{
+ u32 copy_len = HCI_SNOOP_BUF_SIZE;
+ u32 copy_tail_len = HCI_SNOOP_BUF_SIZE;
+ u8 separator_char[] = {0xA5, 0xA5};
+ const u8 *copy_tail_buf;
+ u8 hci_snoop_buf[HCI_SNOOP_MAX_BUF_SIZE] = {0};
+ u16 hci_snoop_len = 0;
+
+ if (buf && len > 0) {
+ if (len < HCI_SNOOP_BUF_SIZE) {
+ copy_len = len;
+ copy_tail_len = 0;
+ } else if (len > HCI_SNOOP_BUF_SIZE && len <= HCI_SNOOP_BUF_SIZE * 2)
+ copy_tail_len = len - copy_len;
+
+ memcpy(hci_snoop_buf, buf, copy_len & 0xff);
+ hci_snoop_len = copy_len & 0xff;
+
+ /* save less then 32 bytes data in the buffer tail, using A5 A5 to
+ * separator the head 32 bytes data and the tail 32 bytes data
+ */
+ if (copy_tail_len > 0) {
+ copy_tail_buf = buf + len - copy_tail_len;
+ hci_snoop_len += (copy_tail_len + sizeof(separator_char)) & 0xff;
+ memcpy(hci_snoop_buf + copy_len, separator_char, sizeof(separator_char));
+ memcpy(hci_snoop_buf + copy_len + sizeof(separator_char),
+ copy_tail_buf, copy_tail_len);
+ }
+
+ if (hci_snoop_len > 0)
+ BTMTK_INFO_RAW(hci_snoop_buf, hci_snoop_len, "act_len(%d)-len(%d)-buf(%p):",
+ len, hci_snoop_len, buf);
+ }
+}
+
+static int btmtk_reboot_notify(struct notifier_block *nb,
+ unsigned long event, void *unused)
+{
+ int ret = 0;
+ int i = 0;
+ int cif_event = 0;
+ int fstate = 0;
+ int state = 0;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+
+ BTMTK_INFO("%s: (%d)", __func__, (int)event);
+
+ if (event == SYS_RESTART) {
+ BTMTK_INFO("%s: enter", __func__);
+ for (i = 0; i < btmtk_intf_num; i++) {
+ /* Find valid dev for already probe interface. */
+ if (!g_bdev[i]->hdev) {
+ bdev = g_bdev[i];
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not opened(%d)", __func__, fstate);
+ continue;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state != BTMTK_STATE_WORKING) {
+ BTMTK_WARN("%s: not in working(%d).", __func__, state);
+ continue;
+ }
+
+ cif_event = HIF_EVENT_DISCONNECT;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s parameter is NULL", __func__);
+ continue;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSING);
+
+ if (main_info.hif_hook.cif_mutex_lock)
+ main_info.hif_hook.cif_mutex_lock(bdev);
+
+ ret = btmtk_send_deinit_cmds(bdev);
+ if (ret < 0)
+ BTMTK_ERR("%s, btmtk_send_deinit_cmds failed", __func__);
+
+ main_info.hif_hook.close(bdev->hdev);
+
+ if (main_info.hif_hook.cif_mutex_unlock)
+ main_info.hif_hook.cif_mutex_unlock(bdev);
+
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSED);
+
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct notifier_block btmtk_reboot_notifier = {
+ .notifier_call = btmtk_reboot_notify,
+ .next = NULL,
+ .priority = 0,
+};
+
+static int main_init(void)
+{
+ int i = 0;
+
+ BTMTK_INFO("%s", __func__);
+
+ /* Check if user changes default minimum supported intf count */
+ if (btmtk_intf_num < BT_MCU_MINIMUM_INTERFACE_NUM) {
+ btmtk_intf_num = BT_MCU_MINIMUM_INTERFACE_NUM;
+ BTMTK_WARN("%s minimum interface is %d", __func__, btmtk_intf_num);
+ }
+
+ BTMTK_INFO("%s supported intf count <%d>", __func__, btmtk_intf_num);
+
+ /* register system power off callback function. */
+ do {
+ typedef void (*func_ptr) (int (*f) (void));
+ char *func_name = "RegisterPdwncCallback";
+ func_ptr pFunc = (func_ptr) btmtk_kallsyms_lookup_name(func_name);
+
+ if (pFunc) {
+ BTMTK_INFO("%s: Register Pdwnc callback success.", __func__);
+ pFunc(&btmtk_enter_standby);
+ } else
+ BTMTK_WARN("%s: No Exported Func Found [%s], just skip!",
+ __func__, func_name);
+ } while (0);
+
+ BTMTK_INFO("%s: Register reboot_notifier callback success.", __func__);
+ /* Is it necessary? bt_close will be called by reboot. */
+ register_reboot_notifier(&btmtk_reboot_notifier);
+
+ g_bdev = kzalloc((sizeof(*g_bdev) * btmtk_intf_num), GFP_KERNEL);
+ if (!g_bdev) {
+ BTMTK_WARN("%s insufficient memory", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < btmtk_intf_num; i++) {
+ g_bdev[i] = btmtk_allocate_dev_memory(NULL);
+ if (g_bdev[i]) {
+ /* BTMTK_STATE_UNKNOWN instead? */
+ /* btmtk_set_chip_state(g_bdev[i], BTMTK_STATE_INIT); */
+
+ /* BTMTK_FOPS_STATE_UNKNOWN instead? */
+ btmtk_fops_set_state(g_bdev[i], BTMTK_FOPS_STATE_INIT);
+ } else {
+ return -ENOMEM;
+ }
+ }
+
+#ifdef CONFIG_MP_WAKEUP_SOURCE_SYSFS_STAT
+ main_info.fwdump_ws = wakeup_source_register(NULL, "btmtk_fwdump_wakelock");
+ main_info.woble_ws = wakeup_source_register(NULL, "btmtk_woble_wakelock");
+ main_info.eint_ws = wakeup_source_register(NULL, "btevent_eint");
+#else
+ main_info.fwdump_ws = wakeup_source_register("btmtk_fwdump_wakelock");
+ main_info.woble_ws = wakeup_source_register("btmtk_woble_wakelock");
+ main_info.eint_ws = wakeup_source_register("btevent_eint");
+#endif
+ return 0;
+}
+
+static int main_exit(void)
+{
+ int i = 0;
+
+ BTMTK_INFO("%s releasing intf count <%d>", __func__, btmtk_intf_num);
+
+ if (g_bdev == NULL) {
+ BTMTK_WARN("%s g_data is NULL", __func__);
+ return 0;
+ }
+
+ BTMTK_INFO("%s: Register reboot_notifier callback success.", __func__);
+ /* Is it necessary? bt_close will be called by reboot. */
+ unregister_reboot_notifier(&btmtk_reboot_notifier);
+
+ wakeup_source_unregister(main_info.fwdump_ws);
+ wakeup_source_unregister(main_info.woble_ws);
+ wakeup_source_unregister(main_info.eint_ws);
+
+ for (i = 0; i < btmtk_intf_num; i++) {
+ if (g_bdev[i] != NULL)
+ btmtk_free_dev_memory(NULL, g_bdev[i]);
+ }
+
+ kfree(g_bdev);
+ return 0;
+}
+
+/* HCI receive mechnism */
+
+
+static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
+ struct sk_buff *skb,
+ const unsigned char *buffer,
+ int count,
+ const struct h4_recv_pkt *pkts,
+ int pkts_count)
+{
+ struct btmtk_dev *bdev = NULL;
+ /* used for print debug log*/
+ const unsigned char *buffer_dbg = buffer;
+ int count_dbg = count;
+
+ if (hdev == NULL || buffer == NULL) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (bdev == NULL) {
+ BTMTK_ERR("%s, bdev is invalid", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+ /* Check for error from previous call */
+ if (IS_ERR(skb))
+ skb = NULL;
+ /* BTMTK_DBG("%s begin, count = %d", __func__, count); */
+
+ while (count) {
+ int i, len;
+
+ if (!skb) {
+ for (i = 0; i < pkts_count; i++) {
+ if (buffer[0] != (&pkts[i])->type)
+ continue;
+
+ skb = bt_skb_alloc((&pkts[i])->maxlen,
+ GFP_ATOMIC);
+ if (!skb) {
+ BTMTK_ERR("%s, alloc skb failed!", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ hci_skb_pkt_type(skb) = (&pkts[i])->type;
+ hci_skb_expect(skb) = (&pkts[i])->hlen;
+ break;
+ }
+
+ /* Check for invalid packet type */
+ if (!skb) {
+ BTMTK_ERR("%s,skb is invalid, buffer[0] = %d!", __func__,
+ buffer[0]);
+ btmtk_hci_snoop_print(count_dbg, buffer_dbg);
+ btmtk_hci_snoop_print(count, buffer);
+ btmtk_hci_snoop_print_to_log();
+ return ERR_PTR(-EILSEQ);
+ }
+
+ count -= 1;
+ buffer += 1;
+ }
+
+ len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
+ memcpy(skb_put(skb, len), buffer, len);
+ /* If kernel version > 4.x */
+ /* skb_put_data(skb, buffer, len); */
+
+ count -= len;
+ buffer += len;
+
+ /* BTMTK_DBG("%s skb->len = %d, %d", __func__, skb->len, hci_skb_expect(skb)); */
+
+ /* Check for partial packet */
+ if (skb->len < hci_skb_expect(skb))
+ continue;
+
+ for (i = 0; i < pkts_count; i++) {
+ if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
+ break;
+ }
+
+ if (i >= pkts_count) {
+ BTMTK_ERR("%s, pkt type is invalid!", __func__);
+ btmtk_hci_snoop_print(count_dbg, buffer_dbg);
+ btmtk_hci_snoop_print(count, buffer);
+ btmtk_hci_snoop_print_to_log();
+ kfree_skb(skb);
+ return ERR_PTR(-EILSEQ);
+ }
+
+ if (skb->len == (&pkts[i])->hlen) {
+ u16 dlen;
+
+ /* BTMTK_DBG("%s begin, skb->len = %d, %d, %d", __func__, skb->len, */
+ /* (&pkts[i])->hlen, (&pkts[i])->lsize); */
+ switch ((&pkts[i])->lsize) {
+ case 0:
+ /* No variable data length */
+ dlen = 0;
+ break;
+ case 1:
+ /* Single octet variable length */
+ dlen = skb->data[(&pkts[i])->loff];
+ hci_skb_expect(skb) += dlen;
+
+ if (skb_tailroom(skb) < dlen) {
+ BTMTK_ERR("%s, skb_tailroom is not enough, dlen:%d!",
+ __func__, dlen);
+ btmtk_hci_snoop_print(skb->len, skb->data);
+ btmtk_hci_snoop_print(count_dbg, buffer_dbg);
+ btmtk_hci_snoop_print(count, buffer);
+ btmtk_hci_snoop_print_to_log();
+ kfree_skb(skb);
+ return ERR_PTR(-EMSGSIZE);
+ }
+ break;
+ case 2:
+ /* Double octet variable length */
+ dlen = get_unaligned_le16(skb->data +
+ (&pkts[i])->loff);
+ hci_skb_expect(skb) += dlen;
+
+ if (skb_tailroom(skb) < dlen) {
+ BTMTK_ERR("%s, skb_tailroom in case 2, dlen:%d!"
+ , __func__, dlen);
+ btmtk_hci_snoop_print(skb->len, skb->data);
+ btmtk_hci_snoop_print(count_dbg, buffer_dbg);
+ btmtk_hci_snoop_print(count, buffer);
+ btmtk_hci_snoop_print_to_log();
+ kfree_skb(skb);
+ return ERR_PTR(-EMSGSIZE);
+ }
+ break;
+ default:
+ /* Unsupported variable length */
+ BTMTK_ERR("%s, Unsupported variable length!", __func__);
+ btmtk_hci_snoop_print(count_dbg, buffer_dbg);
+ btmtk_hci_snoop_print(count, buffer);
+ btmtk_hci_snoop_print_to_log();
+ kfree_skb(skb);
+ return ERR_PTR(-EILSEQ);
+ }
+
+ if (!dlen) {
+ /* No more data, complete frame */
+ (&pkts[i])->recv(hdev, skb);
+ skb = NULL;
+ }
+ } else {
+ /* Complete frame */
+ (&pkts[i])->recv(hdev, skb);
+ skb = NULL;
+ }
+ }
+
+ return skb;
+}
+
+static const struct h4_recv_pkt mtk_recv_pkts[] = {
+ { H4_RECV_ACL, .recv = btmtk_recv_acl },
+ { H4_RECV_SCO, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = btmtk_recv_event },
+};
+#if ENABLESTP
+static inline struct sk_buff *mtk_add_stp(struct btmtk_dev *bdev, struct sk_buff *skb)
+{
+ struct mtk_stp_hdr *shdr;
+ int dlen, err = 0, type = 0;
+ u8 stp_crc[] = {0x00, 0x00};
+
+ if (unlikely(skb_headroom(skb) < sizeof(*shdr)) ||
+ (skb_tailroom(skb) < MTK_STP_TLR_SIZE)) {
+ BTMTK_DBG("%s, add pskb_expand_head, headroom = %d, tailroom = %d",
+ __func__, skb_headroom(skb), skb_tailroom(skb));
+
+ err = pskb_expand_head(skb, sizeof(*shdr), MTK_STP_TLR_SIZE,
+ GFP_ATOMIC);
+ }
+ dlen = skb->len;
+ shdr = (void *) skb_push(skb, sizeof(*shdr));
+ shdr->prefix = 0x80;
+ shdr->dlen = cpu_to_be16((dlen & 0x0fff) | (type << 12));
+ shdr->cs = 0;
+ // Add the STP trailer
+ // kernel version > 4.20
+ // skb_put_zero(skb, MTK_STP_TLR_SIZE);
+ // kernel version < 4.20
+ skb_put(skb, sizeof(stp_crc));
+
+ return skb;
+}
+
+static const unsigned char *
+mtk_stp_split(struct btmtk_dev *bdev, const unsigned char *data, int count,
+ int *sz_h4)
+{
+ struct mtk_stp_hdr *shdr;
+
+ /* The cursor is reset when all the data of STP is consumed out */
+ if (!bdev->stp_dlen && bdev->stp_cursor >= 6) {
+ bdev->stp_cursor = 0;
+ BTMTK_ERR("reset cursor = %d\n", bdev->stp_cursor);
+ }
+
+ /* Filling pad until all STP info is obtained */
+ while (bdev->stp_cursor < 6 && count > 0) {
+ bdev->stp_pad[bdev->stp_cursor] = *data;
+ pr_err("fill stp format (%02x, %d, %d)\n",
+ bdev->stp_pad[bdev->stp_cursor], bdev->stp_cursor, count);
+ bdev->stp_cursor++;
+ data++;
+ count--;
+ }
+
+ /* Retrieve STP info and have a sanity check */
+ if (!bdev->stp_dlen && bdev->stp_cursor >= 6) {
+ shdr = (struct mtk_stp_hdr *)&bdev->stp_pad[2];
+ bdev->stp_dlen = be16_to_cpu(shdr->dlen) & 0x0fff;
+ pr_err("stp format (%02x, %02x)",
+ shdr->prefix, bdev->stp_dlen);
+
+ /* Resync STP when unexpected data is being read */
+ if (shdr->prefix != 0x80 || bdev->stp_dlen > 2048) {
+ BTMTK_ERR("stp format unexpect (%02x, %02x)",
+ shdr->prefix, bdev->stp_dlen);
+ BTMTK_ERR("reset cursor = %d\n", bdev->stp_cursor);
+ bdev->stp_cursor = 2;
+ bdev->stp_dlen = 0;
+ }
+ }
+
+ /* Directly quit when there's no data found for H4 can process */
+ if (count <= 0)
+ return NULL;
+
+ /* Tranlate to how much the size of data H4 can handle so far */
+ *sz_h4 = min_t(int, count, bdev->stp_dlen);
+
+ /* Update the remaining size of STP packet */
+ bdev->stp_dlen -= *sz_h4;
+
+ /* Data points to STP payload which can be handled by H4 */
+ return data;
+}
+#endif
+
+int btmtk_recv(struct hci_dev *hdev, const u8 *data, size_t count)
+{
+ struct btmtk_dev *bdev = NULL;
+ const unsigned char *p_left = data;
+ int sz_left = count;
+ int err;
+#if ENABLESTP
+ const unsigned char **p_h4 = NULL;
+ int sz_h4 = 0, adv = 0;
+#endif
+
+ if (hdev == NULL || data == NULL) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -EINVAL;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (bdev == NULL) {
+ BTMTK_ERR("%s, bdev is NULL!", __func__);
+ return -EINVAL;
+ }
+
+ while (sz_left > 0) {
+ /* The serial data received from MT7622 BT controller is
+ * at all time padded around with the STP header and tailer.
+ *
+ * A full STP packet is looking like
+ * -----------------------------------
+ * | STP header | H:4 | STP tailer |
+ * -----------------------------------
+ * but it doesn't guarantee to contain a full H:4 packet which
+ * means that it's possible for multiple STP packets forms a
+ * full H:4 packet that means extra STP header + length doesn't
+ * indicate a full H:4 frame, things can fragment. Whose length
+ * recorded in STP header just shows up the most length the
+ * H:4 engine can handle currently.
+ */
+#if ENABLESTP
+ p_h4 = mtk_stp_split(bdev, p_left, sz_left, &sz_h4);
+ if (!p_h4)
+ break;
+
+ adv = p_h4 - p_left;
+ sz_left -= adv;
+ p_left += adv;
+#endif
+
+#if ENABLESTP
+ bdev->rx_skb = h4_recv_buf(hdev, bdev->rx_skb, p_h4,
+ sz_h4, mtk_recv_pkts,
+ ARRAY_SIZE(mtk_recv_pkts));
+#else
+ bdev->rx_skb = h4_recv_buf(hdev, bdev->rx_skb, data,
+ count, mtk_recv_pkts,
+ ARRAY_SIZE(mtk_recv_pkts));
+#endif
+
+ if (IS_ERR(bdev->rx_skb)) {
+ err = PTR_ERR(bdev->rx_skb);
+ pr_err("Frame reassembly failed (%d)", err);
+ bdev->rx_skb = NULL;
+ return err;
+ }
+
+#if ENABLESTP
+ sz_left -= sz_h4;
+ p_left += sz_h4;
+#else
+ sz_left -= count;
+ p_left += count;
+#endif
+ }
+
+ return 0;
+}
+
+static int btmtk_dispatch_pkt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ static u8 fwlog_picus_blocking_warn;
+ static u8 fwlog_fwdump_blocking_warn;
+ int state = BTMTK_STATE_INIT;
+
+ if ((bt_cb(skb)->pkt_type == HCI_ACLDATA_PKT) &&
+ skb->data[0] == 0x6f &&
+ skb->data[1] == 0xfc) {
+ static int dump_data_counter;
+ static int dump_data_length;
+
+ state = btmtk_get_chip_state(bdev);
+ if (state != BTMTK_STATE_FW_DUMP) {
+ BTMTK_INFO("%s: FW dump begin", __func__);
+ btmtk_hci_snoop_print_to_log();
+ /* Print too much log, it may cause kernel panic. */
+ dump_data_counter = 0;
+ dump_data_length = 0;
+ btmtk_set_chip_state(bdev, BTMTK_STATE_FW_DUMP);
+ btmtk_fwdump_wake_lock(bdev);
+ }
+
+ dump_data_counter++;
+ dump_data_length += skb->len;
+
+ /* coredump */
+ /* print dump data to console */
+ if (dump_data_counter % 1000 == 0) {
+ BTMTK_INFO("%s: FW dump on-going, total_packet = %d, total_length = %d",
+ __func__, dump_data_counter, dump_data_length);
+ }
+
+ /* print dump data to console */
+ if (dump_data_counter < 20)
+ BTMTK_INFO("%s: FW dump data (%d): %s",
+ __func__, dump_data_counter, &skb->data[4]);
+
+ /* In the new generation, we will check the keyword of coredump (; coredump end)
+ * Such as : 79xx
+ */
+ if (skb->data[skb->len - 4] == 'e' &&
+ skb->data[skb->len - 3] == 'n' &&
+ skb->data[skb->len - 2] == 'd') {
+ /* This is the latest coredump packet. */
+ BTMTK_INFO("%s: FW dump end, dump_data_counter = %d",
+ __func__, dump_data_counter);
+ /* TODO: Chip reset*/
+ main_info.reset_stack_flag = HW_ERR_CODE_CORE_DUMP;
+ btmtk_fwdump_wake_unlock(bdev);
+ }
+
+ if (skb_queue_len(&g_fwlog->fwlog_queue) < FWLOG_ASSERT_QUEUE_COUNT) {
+ /* sent picus data to queue, picus tool will log it */
+ if (btmtk_skb_enq_fwlog(bdev->hdev, skb->data,
+ skb->len, 0, &g_fwlog->fwlog_queue) == 0) {
+ wake_up_interruptible(&g_fwlog->fw_log_inq);
+ fwlog_fwdump_blocking_warn = 0;
+ }
+ } else {
+ if (fwlog_fwdump_blocking_warn == 0) {
+ fwlog_fwdump_blocking_warn = 1;
+ pr_warn("btmtk fwlog queue size is full(coredump)");
+ }
+ }
+
+ if (!bdev->bt_cfg.support_picus_to_host)
+ return 1;
+ } else if ((bt_cb(skb)->pkt_type == HCI_ACLDATA_PKT) &&
+ (skb->data[0] == 0xff || skb->data[0] == 0xfe) &&
+ skb->data[1] == 0x05 &&
+ !bdev->bt_cfg.support_picus_to_host) {
+ /* picus or syslog */
+ if (skb_queue_len(&g_fwlog->fwlog_queue) < FWLOG_QUEUE_COUNT) {
+ if (btmtk_skb_enq_fwlog(bdev->hdev, skb->data, skb->len,
+ FWLOG_TYPE, &g_fwlog->fwlog_queue) == 0) {
+ wake_up_interruptible(&g_fwlog->fw_log_inq);
+ fwlog_picus_blocking_warn = 0;
+ }
+ } else {
+ if (fwlog_picus_blocking_warn == 0) {
+ fwlog_picus_blocking_warn = 1;
+ pr_warn("btmtk fwlog queue size is full(picus)");
+ }
+ }
+ return 1;
+ } else if ((bt_cb(skb)->pkt_type == HCI_EVENT_PKT) &&
+ skb->data[0] == 0x0E &&
+ bdev->opcode_usr[0] == skb->data[3] &&
+ bdev->opcode_usr[1] == skb->data[4]) {
+ BTMTK_INFO_RAW(skb->data, skb->len,
+ "%s: Discard event from user hci command - ", __func__);
+ bdev->opcode_usr[0] = 0;
+ bdev->opcode_usr[1] = 0;
+ return 1;
+ } else if (memcmp(skb->data, RESET_EVENT, sizeof(RESET_EVENT)) == 0) {
+ BTMTK_INFO("%s: Get RESET_EVENT", __func__);
+ /* Need confirm with Shawn */
+ /* if (bdev->bt_cfg.support_auto_picus == true) {
+ * if (btmtk_picus_enable(bdev) < 0) {
+ * BTMTK_ERR("send picus filter param failed");
+ * }
+ }
+ */
+ }
+ return 0;
+}
+
+int btmtk_recv_acl(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = NULL;
+
+ if (hdev == NULL || skb == NULL) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -EINVAL;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (bdev == NULL || bdev->workqueue == NULL) {
+ BTMTK_ERR("%s, bdev or workqueue is invalid!", __func__);
+ return -EINVAL;
+ }
+
+ skb_queue_tail(&bdev->rx_q, skb);
+ queue_work(bdev->workqueue, &bdev->rx_work);
+
+ return 0;
+}
+
+
+int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = NULL;
+ //struct hci_event_hdr *hdr = (void *)skb->data;
+
+ if (hdev == NULL || skb == NULL) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -EINVAL;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (bdev == NULL || bdev->workqueue == NULL) {
+ BTMTK_ERR("%s, bdev or workqueue is invalid!", __func__);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ /* Fix up the vendor event id with 0xff for vendor specific instead
+ * of 0xe4 so that event send via monitoring socket can be parsed
+ * properly.
+ */
+ /* if (hdr->evt == 0xe4) {
+ * BTMTK_DBG("%s hdr->evt is %02x", __func__, hdr->evt);
+ * hdr->evt = HCI_EV_VENDOR;
+ * }
+ */
+
+ BTMTK_DBG_RAW(skb->data, skb->len, "%s, recv evt(hci_recv_frame)", __func__);
+
+ skb_queue_tail(&bdev->rx_q, skb);
+ queue_work(bdev->workqueue, &bdev->rx_work);
+
+ return 0;
+}
+
+int btmtk_main_send_cmd(struct btmtk_dev *bdev, const u8 *cmd,
+ const int cmd_len, const u8 *event, const int event_len, int delay,
+ int retry, int pkt_type)
+{
+ struct sk_buff *skb = NULL;
+ int ret = 0;
+ int state = BTMTK_STATE_INIT;
+
+ if (bdev == NULL || bdev->hdev == NULL ||
+ cmd == NULL || cmd_len <= 0) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (memcmp(cmd, wmt_over_hci_header, sizeof(wmt_over_hci_header)) &&
+ pkt_type != BTMTK_TX_ACL_FROM_DRV &&
+ bdev->power_state != BTMTK_DONGLE_STATE_POWER_ON) {
+ BTMTK_WARN("%s: chip power isn't on, ignore this command, state is %d",
+ __func__, bdev->power_state);
+ goto exit;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state == BTMTK_STATE_FW_DUMP) {
+ BTMTK_WARN("%s: FW dumping ongoing, don't send any cmd to FW!!!", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ skb = alloc_skb(cmd_len + BT_SKB_RESERVE, GFP_ATOMIC);
+ if (skb == NULL) {
+ BTMTK_ERR("%s allocate skb failed!!", __func__);
+ goto exit;
+ }
+ /* Reserv for core and drivers use */
+ skb_reserve(skb, 7);
+ bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ memcpy(skb->data, cmd, cmd_len);
+ skb->len = cmd_len;
+
+#if ENABLESTP
+ skb = mtk_add_stp(bdev, skb);
+#endif
+ /* wmt cmd and download fw patch using wmt cmd with USB interface, need use
+ * usb_control_msg to recv wmt event;
+ * other HIF don't use this method to recv wmt event
+ */
+
+ ret = main_info.hif_hook.send_and_recv(bdev,
+ skb,
+ event, event_len,
+ delay, retry, pkt_type);
+
+ if (ret < 0)
+ BTMTK_ERR("%s send_and_recv failed!!", __func__);
+
+exit:
+ BTMTK_DBG("%s end!!", __func__);
+ return ret;
+}
+
+static int btmtk_check_need_load_rom_patch(struct btmtk_dev *bdev)
+{
+ u8 cmd[] = { 0x01, 0x6F, 0xFC, 0x05, 0x01, 0x17, 0x01, 0x00, 0x01 };
+ /* event[6] is key */
+ u8 event[] = { 0x04, 0xE4, 0x05, 0x02, 0x17, 0x01, 0x00, /* 0x02 */ };
+ int ret = -1;
+
+ if (!bdev) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ ret = -EINVAL;
+ return ret;
+ }
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), DELAY_TIMES,
+ RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
+ /* can't get correct event */
+ if (ret < 0)
+ return PATCH_ERR;
+
+ if (bdev->recv_evt_len == sizeof(event))
+ return bdev->io_buf[6];
+
+ return PATCH_ERR;
+}
+
+static void btmtk_load_code_from_bin(const struct firmware **fw_firmware,
+ char *bin_name, struct device *dev, u8 **image, u32 *code_len)
+{
+ int err = 0;
+ int retry = 10;
+
+ if (!bin_name) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return;
+ }
+
+ do {
+ err = request_firmware(fw_firmware, bin_name, dev);
+ if (err == 0) {
+ break;
+ } else if (retry <= 0) {
+ *fw_firmware = NULL;
+ BTMTK_INFO("%s: request_firmware %d times fail", __func__, 10);
+ BTMTK_INFO("maybe file not exist, err = %d", err);
+ return;
+ }
+ BTMTK_INFO("%s: request_firmware fail, maybe file not exist, err = %d, retry = %d",
+ __func__, err, retry);
+ msleep(100);
+ } while (retry-- > 0);
+
+ *image = (u8 *)(*fw_firmware)->data;
+ *code_len = (*fw_firmware)->size;
+}
+
+static void btmtk_print_bt_patch_info(struct btmtk_dev *bdev, u8 *fwbuf)
+{
+ struct _PATCH_HEADER *patchHdr = NULL;
+ struct _Global_Descr *globalDesrc = NULL;
+
+ if (fwbuf == NULL) {
+ BTMTK_WARN("%s, fwbuf is NULL!", __func__);
+ return;
+ }
+
+ patchHdr = (struct _PATCH_HEADER *)fwbuf;
+
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id))
+ globalDesrc = (struct _Global_Descr *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
+
+ BTMTK_INFO("[btmtk] =============== Patch Info ==============");
+ if (patchHdr) {
+ BTMTK_INFO("[btmtk] Built Time = %s", patchHdr->ucDateTime);
+ BTMTK_INFO("[btmtk] Hw Ver = 0x%04x", patchHdr->u2HwVer);
+ BTMTK_INFO("[btmtk] Sw Ver = 0x%04x", patchHdr->u2SwVer);
+ BTMTK_INFO("[btmtk] Magic Number = 0x%08x", patchHdr->u4MagicNum);
+
+ BTMTK_INFO("[btmtk] Platform = %c%c%c%c",
+ patchHdr->ucPlatform[0],
+ patchHdr->ucPlatform[1],
+ patchHdr->ucPlatform[2],
+ patchHdr->ucPlatform[3]);
+ } else
+ BTMTK_WARN("%s, patchHdr is NULL!", __func__);
+
+ if (globalDesrc) {
+ BTMTK_INFO("[btmtk] Patch Ver = 0x%08x", globalDesrc->u4PatchVer);
+ BTMTK_INFO("[btmtk] Section num = 0x%08x", globalDesrc->u4SectionNum);
+ } else
+ BTMTK_WARN("%s, globalDesrc is NULL!", __func__);
+ BTMTK_INFO("[btmtk] =========================================");
+}
+
+static void btmtk_print_wifi_patch_info(struct btmtk_dev *bdev, u8 *fwbuf)
+{
+ struct _PATCH_HEADER *patchHdr = NULL;
+ struct _Global_Descr *globalDesrc = NULL;
+
+ if (fwbuf == NULL) {
+ BTMTK_WARN("%s, fwbuf is NULL!", __func__);
+ return;
+ }
+
+ patchHdr = (struct _PATCH_HEADER *)fwbuf;
+
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id))
+ globalDesrc = (struct _Global_Descr *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
+
+ BTMTK_INFO("[btmtk] =============== Wifi Patch Info ==============");
+ if (patchHdr) {
+ BTMTK_INFO("[btmtk] Built Time = %s", patchHdr->ucDateTime);
+ BTMTK_INFO("[btmtk] Hw Ver = 0x%04x",
+ ((patchHdr->u2HwVer & 0x00ff) << 8) | ((patchHdr->u2HwVer & 0xff00) >> 8));
+ BTMTK_INFO("[btmtk] Sw Ver = 0x%04x",
+ ((patchHdr->u2SwVer & 0x00ff) << 8) | ((patchHdr->u2SwVer & 0xff00) >> 8));
+ BTMTK_INFO("[btmtk] Magic Number = 0x%08x", be2cpu32(patchHdr->u4MagicNum));
+
+ BTMTK_INFO("[btmtk] Platform = %c%c%c%c",
+ patchHdr->ucPlatform[0],
+ patchHdr->ucPlatform[1],
+ patchHdr->ucPlatform[2],
+ patchHdr->ucPlatform[3]);
+ } else
+ BTMTK_WARN("%s, patchHdr is NULL!", __func__);
+
+ if (globalDesrc) {
+ BTMTK_INFO("[btmtk] Patch Ver = 0x%08x",
+ be2cpu32(globalDesrc->u4PatchVer));
+ BTMTK_INFO("[btmtk] Section num = 0x%08x",
+ be2cpu32(globalDesrc->u4SectionNum));
+ } else
+ BTMTK_WARN("%s, globalDesrc is NULL!", __func__);
+ BTMTK_INFO("[btmtk] =========================================");
+}
+
+static int btmtk_send_wmt_download_cmd(struct btmtk_dev *bdev, u8 *cmd,
+ int cmd_len, u8 *event, int event_len, struct _Section_Map *sectionMap,
+ u8 fw_state, u8 dma_flag, bool patch_flag)
+{
+ int payload_len = 0;
+ int ret = -1;
+ int i = 0;
+ u32 revert_SecSpec = 0;
+
+ if (bdev == NULL || cmd == NULL || event == NULL || sectionMap == NULL) {
+ BTMTK_ERR("%s: invalid parameter!", __func__);
+ return ret;
+ }
+
+ /* need refine this cmd to mtk_wmt_hdr struct*/
+ /* prepare HCI header */
+ cmd[0] = 0x01;
+ cmd[1] = 0x6F;
+ cmd[2] = 0xFC;
+
+ /* prepare WMT header */
+ cmd[4] = 0x01;
+ cmd[5] = 0x01; /* opcode */
+
+ if (fw_state == 0) {
+ /* prepare WMT DL cmd */
+ payload_len = SEC_MAP_NEED_SEND_SIZE + 2;
+
+ cmd[3] = (payload_len + 4) & 0xFF; /* length*/
+ cmd[6] = payload_len & 0xFF;
+ cmd[7] = (payload_len >> 8) & 0xFF;
+ cmd[8] = 0x00; /* which is the FW download state 0 */
+ cmd[9] = dma_flag; /* 1:using DMA to download, 0:using legacy wmt cmd*/
+ cmd_len = SEC_MAP_NEED_SEND_SIZE + PATCH_HEADER_SIZE;
+
+ if (patch_flag) {
+ for (i = 0; i < SECTION_SPEC_NUM; i++) {
+ revert_SecSpec = be2cpu32(sectionMap->u4SecSpec[i]);
+ memcpy(&cmd[PATCH_HEADER_SIZE] + i * sizeof(u32),
+ (u8 *)&revert_SecSpec, sizeof(u32));
+ }
+ } else
+ memcpy(&cmd[PATCH_HEADER_SIZE], (u8 *)(sectionMap->u4SecSpec),
+ SEC_MAP_NEED_SEND_SIZE);
+
+ BTMTK_INFO_RAW(cmd, cmd_len, "%s: CMD:", __func__);
+
+ ret = btmtk_main_send_cmd(bdev, cmd, cmd_len,
+ event, event_len, DELAY_TIMES, RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_ERR("%s: send wmd dl cmd failed, terminate!", __func__);
+ return PATCH_ERR;
+ }
+
+ if (bdev->recv_evt_len >= event_len)
+ return bdev->io_buf[PATCH_STATUS];
+
+ return PATCH_ERR;
+ }
+
+ BTMTK_ERR("%s: fw state is error!", __func__);
+
+ return ret;
+}
+
+static int btmtk_load_fw_patch_using_wmt_cmd(struct btmtk_dev *bdev,
+ u8 *image, u8 *fwbuf, u8 *event, int event_len, u32 patch_len, int offset)
+{
+ int ret = 0;
+ u32 cur_len = 0;
+ s32 sent_len;
+ int first_block = 1;
+ u8 phase;
+ int delay = PATCH_DOWNLOAD_PHASE1_2_DELAY_TIME;
+ int retry = PATCH_DOWNLOAD_PHASE1_2_RETRY;
+
+ if (bdev == NULL || image == NULL || fwbuf == NULL) {
+ BTMTK_WARN("%s, invalid parameters!", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ /* loading rom patch */
+ while (1) {
+ s32 sent_len_max = UPLOAD_PATCH_UNIT - PATCH_HEADER_SIZE;
+
+ sent_len = (patch_len - cur_len) >= sent_len_max ?
+ sent_len_max : (patch_len - cur_len);
+
+ if (sent_len > 0) {
+ if (first_block == 1) {
+ if (sent_len < sent_len_max)
+ phase = PATCH_PHASE3;
+ else
+ phase = PATCH_PHASE1;
+ first_block = 0;
+ } else if (sent_len == sent_len_max) {
+ if (patch_len - cur_len == sent_len_max)
+ phase = PATCH_PHASE3;
+ else
+ phase = PATCH_PHASE2;
+ } else {
+ phase = PATCH_PHASE3;
+ }
+
+
+ /* prepare HCI header */
+ image[0] = 0x02;
+ image[1] = 0x6F;
+ image[2] = 0xFC;
+ image[3] = (sent_len + 5) & 0xFF;
+ image[4] = ((sent_len + 5) >> 8) & 0xFF;
+
+ /* prepare WMT header */
+ image[5] = 0x01;
+ image[6] = 0x01;
+ image[7] = (sent_len + 1) & 0xFF;
+ image[8] = ((sent_len + 1) >> 8) & 0xFF;
+
+ image[9] = phase;
+ memcpy(&image[10], fwbuf + offset + cur_len, sent_len);
+ if (phase == PATCH_PHASE3) {
+ delay = PATCH_DOWNLOAD_PHASE3_DELAY_TIME;
+ retry = PATCH_DOWNLOAD_PHASE3_RETRY;
+ }
+
+ cur_len += sent_len;
+ BTMTK_DBG("%s: sent_len = %d, cur_len = %d, phase = %d", __func__,
+ sent_len, cur_len, phase);
+
+ ret = btmtk_main_send_cmd(bdev, image, sent_len + PATCH_HEADER_SIZE,
+ event, event_len, delay, retry, BTMTK_TX_ACL_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_INFO("%s: send patch failed, terminate", __func__);
+ goto exit;
+ }
+ } else
+ break;
+ }
+
+exit:
+ return ret;
+}
+
+void btmtk_send_hw_err_to_host(struct btmtk_dev *bdev)
+{
+ struct sk_buff *skb = NULL;
+ u8 hwerr_event[] = { 0x04, 0x10, 0x01, 0xff };
+
+ BTMTK_ERR("%s reset_stack_flag = %d!!", __func__, main_info.reset_stack_flag);
+ if (main_info.reset_stack_flag) {
+ skb = alloc_skb(sizeof(hwerr_event) + BT_SKB_RESERVE, GFP_ATOMIC);
+ if (skb == NULL) {
+ BTMTK_ERR("%s allocate skb failed!!", __func__);
+ } else {
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+ skb->data[0] = hwerr_event[1];
+ skb->data[1] = hwerr_event[2];
+ skb->data[2] = main_info.reset_stack_flag;
+ skb->len = sizeof(hwerr_event) - 1;
+ BTMTK_DBG_RAW(skb->data, skb->len, "%s: hw err event:", __func__);
+ hci_recv_frame(bdev->hdev, skb);
+ }
+ }
+}
+
+static int btmtk_send_fw_rom_patch_79xx(struct btmtk_dev *bdev,
+ u8 *fwbuf, bool patch_flag)
+{
+ u8 *pos;
+ int loop_count = 0;
+ int ret = 0;
+ u32 section_num = 0;
+ u32 section_offset = 0;
+ u32 dl_size = 0;
+ int patch_status = 0;
+ int retry = 20;
+ u8 dma_flag = PATCH_DOWNLOAD_USING_WMT;
+ struct _Section_Map *sectionMap;
+ struct _Global_Descr *globalDescr;
+ u8 event[] = {0x04, 0xE4, 0x05, 0x02, 0x01, 0x01, 0x00, 0x00}; /* event[7] is status*/
+
+ if (fwbuf == NULL) {
+ BTMTK_WARN("%s, fwbuf is NULL!", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ globalDescr = (struct _Global_Descr *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
+
+ BTMTK_INFO("%s: loading rom patch...\n", __func__);
+
+ if (patch_flag)
+ section_num = be2cpu32(globalDescr->u4SectionNum);
+ else
+ section_num = globalDescr->u4SectionNum;
+ BTMTK_INFO("%s: section_num = 0x%08x\n", __func__, section_num);
+
+ pos = kmalloc(UPLOAD_PATCH_UNIT, GFP_ATOMIC);
+ if (!pos) {
+ BTMTK_ERR("%s: alloc memory failed", __func__);
+ goto exit;
+ }
+
+ do {
+ sectionMap = (struct _Section_Map *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE +
+ FW_ROM_PATCH_GD_SIZE + FW_ROM_PATCH_SEC_MAP_SIZE * loop_count);
+ dma_flag = PATCH_DOWNLOAD_USING_WMT;
+ if (patch_flag) {
+ /* wifi is big-endian */
+ section_offset = be2cpu32(sectionMap->u4SecOffset);
+ dl_size = be2cpu32(sectionMap->bin_info_spec.u4DLSize);
+ dma_flag = be2cpu32(sectionMap->bin_info_spec.u4DLModeCrcType) & 0xFF;
+ } else {
+ /* BT is little-endian */
+ section_offset = sectionMap->u4SecOffset;
+ dl_size = sectionMap->bin_info_spec.u4DLSize;
+ /*
+ * loop_count = 0: BGF patch
+ * 1: BT ILM
+ * only BT ILM support DL DMA for Buzzard
+ */
+ dma_flag = le2cpu32(sectionMap->bin_info_spec.u4DLModeCrcType) & 0xFF;
+ }
+ BTMTK_INFO("%s: loop_count = %d, section_offset = 0x%08x",
+ __func__, loop_count, section_offset);
+ BTMTK_INFO("download patch_len = 0x%08x, dl mode = %d\n", dl_size, dma_flag);
+ if (dl_size > 0) {
+ retry = 20;
+ do {
+ patch_status = btmtk_send_wmt_download_cmd(bdev, pos, 0, event,
+ sizeof(event) - 1, sectionMap,
+ 0, dma_flag, patch_flag);
+ BTMTK_INFO("%s: patch_status %d", __func__, patch_status);
+
+ if (patch_status > PATCH_READY || patch_status == PATCH_ERR) {
+ BTMTK_ERR("%s: patch_status error", __func__);
+ ret = -1;
+ goto err;
+ } else if (patch_status == PATCH_READY) {
+ BTMTK_INFO("%s: no need to load rom patch section%d",
+ __func__, loop_count);
+ goto next_section;
+ } else if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
+ msleep(100);
+ retry--;
+ } else if (patch_status == PATCH_NEED_DOWNLOAD) {
+ break; /* Download ROM patch directly */
+ }
+ } while (retry > 0);
+
+ if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
+ BTMTK_WARN("%s: Hold by another fun more than 2 seconds", __func__);
+ ret = -1;
+ goto err;
+ }
+
+ if (dma_flag == PATCH_DOWNLOAD_USING_DMA && main_info.hif_hook.dl_dma) {
+ /* using DMA to download fw patch*/
+ ret = main_info.hif_hook.dl_dma(bdev,
+ pos, fwbuf,
+ dl_size, section_offset);
+ if (ret < 0) {
+ BTMTK_ERR("%s: btmtk_load_fw_patch_using_dma failed!",
+ __func__);
+ goto err;
+ }
+ } else {
+ /* using legacy wmt cmd to download fw patch */
+ ret = btmtk_load_fw_patch_using_wmt_cmd(bdev, pos, fwbuf, event,
+ sizeof(event) - 1, dl_size, section_offset);
+ if (ret < 0) {
+ BTMTK_ERR("%s: btmtk_load_fw_patch_using_wmt_cmd failed!",
+ __func__);
+ goto err;
+ }
+ }
+ }
+ /* FW Download finished */
+ if (loop_count == section_num - 1) {
+ /* need to remove delay according to
+ * Jyun-ji's comment later
+ */
+ if (patch_flag)
+ mdelay(500);
+ }
+next_section:
+ continue;
+ } while (++loop_count < section_num);
+
+err:
+ kfree(pos);
+ pos = NULL;
+
+exit:
+ return ret;
+}
+
+int btmtk_load_rom_patch_79xx(struct btmtk_dev *bdev, bool patch_flag)
+{
+ int ret = 0;
+ const struct firmware *fw_firmware = NULL;
+ u8 *rom_patch = NULL;
+ unsigned int rom_patch_len = 0;
+
+ BTMTK_ERR("%s, patch_flag = %d!", __func__, patch_flag);
+
+ if (!bdev) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -EINVAL;
+ }
+
+ if (patch_flag) {
+ if (bdev->flavor) {
+ /* if flavor equals 1, it represent 7920, else it represent 7921*/
+ snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
+ "WIFI_MT%04x_patch_mcu_1a_%x_hdr.bin",
+ bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
+ } else
+ snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
+ "WIFI_MT%04x_patch_mcu_1_%x_hdr.bin",
+ bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
+ }
+
+ btmtk_load_code_from_bin(&fw_firmware, bdev->rom_patch_bin_file_name, NULL,
+ &rom_patch, &rom_patch_len);
+
+ if (!rom_patch) {
+ BTMTK_ERR("%s: please assign a rom patch(/etc/firmware/%s)or(/lib/firmware/%s)",
+ __func__, bdev->rom_patch_bin_file_name, bdev->rom_patch_bin_file_name);
+ ret = -1;
+ goto err;
+ }
+
+ if (patch_flag)
+ /*Display rom patch info*/
+ btmtk_print_wifi_patch_info(bdev, rom_patch);
+ else
+ btmtk_print_bt_patch_info(bdev, rom_patch);
+
+ ret = btmtk_send_fw_rom_patch_79xx(bdev, rom_patch, patch_flag);
+ if (ret < 0) {
+ BTMTK_ERR("%s, failed!", __func__);
+ goto err;
+ }
+
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
+ BTMTK_INFO("%s end", __func__);
+
+err:
+ if (fw_firmware)
+ release_firmware(fw_firmware);
+ return ret;
+}
+
+int btmtk_load_rom_patch_766x(struct btmtk_dev *bdev)
+{
+ u32 patch_len = 0;
+ int ret = 0;
+ int patch_status = 0;
+ int retry = 20;
+ u8 *pos = NULL;
+ u8 event[] = {0x04, 0xE4, 0x05, 0x02, 0x01, 0x01, 0x00, 0x00};
+ const struct firmware *fw_firmware = NULL;
+ u8 *rom_patch = NULL;
+ unsigned int rom_patch_len = 0;
+ struct _PATCH_HEADER *patchHdr;
+
+ if (!bdev) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -EINVAL;
+ }
+
+ btmtk_load_code_from_bin(&fw_firmware, bdev->rom_patch_bin_file_name, NULL,
+ &rom_patch, &rom_patch_len);
+
+ do {
+ patch_status = btmtk_check_need_load_rom_patch(bdev);
+ BTMTK_INFO("%s: patch_status %d", __func__, patch_status);
+
+ if (patch_status > MT766X_PATCH_NEED_DOWNLOAD || patch_status == PATCH_ERR) {
+ BTMTK_ERR("%s: patch_status error", __func__);
+ ret = -1;
+ goto err1;
+ } else if (patch_status == MT766X_PATCH_READY) {
+ BTMTK_INFO("%s: no need to load rom patch", __func__);
+ goto patch_end;
+ } else if (patch_status == MT766X_PATCH_IS_DOWNLOAD_BY_OTHER) {
+ msleep(100);
+ retry--;
+ } else if (patch_status == MT766X_PATCH_NEED_DOWNLOAD) {
+ break; /* Download ROM patch directly */
+ }
+ } while (retry > 0);
+
+ if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
+ BTMTK_WARN("%s: Hold by another fun more than 2 seconds", __func__);
+ ret = -1;
+ goto err1;
+ }
+
+ patchHdr = (struct _PATCH_HEADER *)rom_patch;
+ /*Display rom patch info*/
+ btmtk_print_bt_patch_info(bdev, rom_patch);
+
+ pos = kmalloc(UPLOAD_PATCH_UNIT, GFP_ATOMIC);
+ if (!pos) {
+ BTMTK_ERR("%s: alloc memory failed", __func__);
+ ret = -1;
+ goto err1;
+ }
+
+ patch_len = rom_patch_len - PATCH_INFO_SIZE;
+
+ BTMTK_INFO("%s: loading rom patch...\n", __func__);
+ BTMTK_INFO("%s: patch_len = %d\n", __func__, patch_len);
+ ret = btmtk_load_fw_patch_using_wmt_cmd(bdev, pos, rom_patch, event,
+ sizeof(event) - 1, patch_len, PATCH_INFO_SIZE);
+ if (ret < 0) {
+ BTMTK_ERR("%s, btmtk_send_fw_rom_patch_766x failed!", __func__);
+ goto err0;
+ }
+
+ ret = btmtk_send_wmt_reset(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s: btmtk_send_wmt_reset failed!", __func__);
+ goto err0;
+ }
+ BTMTK_INFO("%s: loading rom patch... Done", __func__);
+
+ btmtk_send_hw_err_to_host(bdev);
+
+patch_end:
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
+ BTMTK_INFO("btmtk_load_rom_patch end");
+
+err0:
+ kfree(pos);
+ pos = NULL;
+
+err1:
+ if (fw_firmware)
+ release_firmware(fw_firmware);
+ return ret;
+}
+
+int btmtk_load_rom_patch(struct btmtk_dev *bdev)
+{
+ int err = -1;
+
+ if (!bdev || !bdev->hdev) {
+ BTMTK_ERR("%s: invalid parameters!", __func__);
+ return err;
+ }
+
+ if (is_mt7663(bdev->chip_id))
+ err = btmtk_load_rom_patch_766x(bdev);
+ else if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) {
+ err = btmtk_load_rom_patch_79xx(bdev, BT_DOWNLOAD);
+ if (err < 0) {
+ BTMTK_ERR("%s: btmtk_load_rom_patch_79xx bt patch failed!", __func__);
+ return err;
+ }
+
+#if CFG_SUPPORT_BT_DL_WIFI_PATCH
+ err = btmtk_load_rom_patch_79xx(bdev, WIFI_DOWNLOAD);
+ if (err < 0) {
+ BTMTK_WARN("%s: btmtk_load_rom_patch_79xx wifi patch failed!", __func__);
+ err = 0;
+ }
+#endif
+ } else
+ BTMTK_WARN("%s: unknown chip id (%d)", __func__, bdev->chip_id);
+
+ return err;
+}
+
+struct btmtk_dev *btmtk_get_dev(void)
+{
+ int i = 0;
+ struct btmtk_dev *tmp_bdev = NULL;
+
+ BTMTK_INFO("%s", __func__);
+
+ for (i = 0; i < btmtk_intf_num; i++) {
+ /* Find empty slot for newly probe interface.
+ * Judged from load_rom_patch is done and
+ * Identified chip_id from cap_init.
+ */
+ if (g_bdev[i]->hdev == NULL) {
+ if (i == 0)
+ g_bdev[i]->dongle_index = i;
+ else
+ g_bdev[i]->dongle_index = g_bdev[i - 1]->dongle_index + 1;
+
+ /* reset pin initial value need to be -1, used to judge after
+ * disconnected before probe, can't do chip reset
+ */
+ g_bdev[i]->bt_cfg.dongle_reset_gpio_pin = -1;
+ tmp_bdev = g_bdev[i];
+
+ /* Hook pre-defined table on state machine */
+ g_bdev[i]->cif_state = (struct btmtk_cif_state *)g_cif_state;
+ break;
+ }
+ }
+
+ return tmp_bdev;
+}
+
+void btmtk_release_dev(struct btmtk_dev *bdev)
+{
+ int i = 0;
+ struct btmtk_dev *tmp_bdev = NULL;
+
+ BTMTK_INFO("%s", __func__);
+
+ tmp_bdev = bdev;
+ if (tmp_bdev != NULL) {
+ for (i = 0; i < btmtk_intf_num; i++) {
+ /* Find slot on probed interface.
+ * Judged from load_rom_patch is done and
+ * Identified chip_id from cap_init.
+ */
+ if (memcmp(tmp_bdev, g_bdev[i], sizeof(*tmp_bdev)) == 0) {
+ memset(tmp_bdev, 0, sizeof(*tmp_bdev));
+ /* reset pin initial value need to be -1, used to judge after
+ * disconnected before probe, can't do chip reset
+ */
+ bdev->bt_cfg.dongle_reset_gpio_pin = -1;
+
+ tmp_bdev = NULL;
+ break;
+ }
+ }
+ }
+
+}
+
+struct btmtk_dev *btmtk_allocate_dev_memory(struct device *dev)
+{
+ struct btmtk_dev *bdev;
+ size_t len = sizeof(*bdev);
+
+ BTMTK_INFO("%s", __func__);
+
+ if (dev != NULL)
+ bdev = devm_kzalloc(dev, len, GFP_KERNEL);
+ else
+ bdev = kzalloc(len, GFP_KERNEL);
+
+ if (!bdev)
+ return NULL;
+
+ btmtk_set_chip_state(bdev, BTMTK_STATE_INIT);
+
+ return bdev;
+}
+
+void btmtk_free_dev_memory(struct device *dev, struct btmtk_dev *bdev)
+{
+ BTMTK_INFO("%s", __func__);
+
+ if (bdev != NULL) {
+ if (dev != NULL)
+ devm_kfree(dev, bdev);
+ else
+ kfree(bdev);
+ }
+}
+
+static int btmtk_calibration_flow(struct btmtk_dev *bdev)
+{
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ return -1;
+ }
+
+ btmtk_cif_send_calibration(bdev);
+ BTMTK_INFO("%s done", __func__);
+ return 0;
+}
+
+static int btmtk_send_hci_reset_cmd(struct btmtk_dev *bdev)
+{
+ u8 cmd[] = { 0x01, 0x03, 0x0C, 0x00 };
+ u8 event[] = { 0x04, 0x0E, 0x04, 0x01, 0x03, 0x0C, 0x00 };
+ int ret = -1; /* if successful, 0 */
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ return ret;
+ }
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_PKT_FROM_HOST);
+
+ BTMTK_INFO("%s done", __func__);
+
+ return ret;
+}
+
+/* Check power status, if power is off, try to set power on */
+int btmtk_reset_power_on(struct btmtk_dev *bdev)
+{
+ if (bdev->power_state == BTMTK_DONGLE_STATE_POWER_OFF) {
+ bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
+ if (btmtk_send_wmt_power_on_cmd(bdev) < 0)
+ return -1;
+ if (is_mt7663(bdev->chip_id)) {
+ if (btmtk_send_hci_tci_set_sleep_cmd_766x(bdev) < 0)
+ return -1;
+
+ if (btmtk_send_hci_reset_cmd(bdev) < 0)
+ return -1;
+ }
+
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_ON;
+ }
+
+ if (bdev->power_state != BTMTK_DONGLE_STATE_POWER_ON) {
+ BTMTK_WARN("%s: end of Incorrect state:%d", __func__, bdev->power_state);
+ return -1;
+ }
+ BTMTK_INFO("%s: end success", __func__);
+
+ return 0;
+}
+
+int btmtk_send_wmt_reset(struct btmtk_dev *bdev)
+{
+ /* Support 7668 and 7663 */
+ u8 cmd[] = { 0x01, 0x6F, 0xFC, 0x05, 0x01, 0x07, 0x01, 0x00, 0x04 };
+ /* To-Do, for event check */
+ u8 event[] = { 0x04, 0xE4, 0x05, 0x02, 0x07, 0x01, 0x00, 0x00 };
+ int ret = -1;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ return ret;
+ }
+ if (bdev->power_state == BTMTK_DONGLE_STATE_POWER_OFF) {
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), 20,
+ 0, BTMTK_TX_CMD_FROM_DRV);
+
+ if (ret >= 0)
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_ON;
+ else
+ bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
+ }
+
+ if (bdev->power_state != BTMTK_DONGLE_STATE_POWER_ON) {
+ BTMTK_WARN("%s: end of Incorrect state:%d", __func__, bdev->power_state);
+ return -EBADFD;
+ }
+
+ BTMTK_INFO("%s done", __func__);
+ return ret;
+}
+
+int btmtk_send_wmt_power_on_cmd(struct btmtk_dev *bdev)
+{
+ /* Support 7668 and 7663 and 7961 */
+ u8 cmd[] = { 0x01, 0x6F, 0xFC, 0x06, 0x01, 0x06, 0x02, 0x00, 0x00, 0x01 };
+ /* To-Do, for event check */
+ u8 event[] = { 0x04, 0xE4, 0x05, 0x02, 0x06, 0x01, 0x00 }; /* event[6] is key */
+ int ret = -1, retry = RETRY_TIMES;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ return ret;
+ }
+
+retry_again:
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), WMT_DELAY_TIMES,
+ RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_ERR("%s: failed(%d)", __func__, ret);
+ bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
+ ret = -1;
+ } else if (ret == 0 && bdev->recv_evt_len > 0) {
+ switch (bdev->io_buf[6]) {
+ case 0: /* successful */
+ BTMTK_INFO("%s: OK", __func__);
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_ON;
+ break;
+ case 2: /* TODO:retry */
+ if (retry > 0) {
+ /* comment from fw,
+ * we need to retry a sec until power on successfully.
+ */
+ retry--;
+ BTMTK_INFO("%s: need to try again", __func__);
+ mdelay(50);
+ goto retry_again;
+ }
+ break;
+ default:
+ BTMTK_WARN("%s: Unknown result: %02X", __func__, bdev->io_buf[6]);
+ bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
+ ret = -1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int btmtk_send_wmt_power_off_cmd(struct btmtk_dev *bdev)
+{
+ /* Support 7668 and 7663 and 7961 */
+ u8 cmd[] = { 0x01, 0x6F, 0xFC, 0x06, 0x01, 0x06, 0x02, 0x00, 0x00, 0x00 };
+ /* To-Do, for event check */
+ u8 event[] = { 0x04, 0xE4, 0x05, 0x02, 0x06, 0x01, 0x00 };
+ int ret = -1;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ return ret;
+ }
+
+ if (bdev->power_state == BTMTK_DONGLE_STATE_POWER_OFF) {
+ BTMTK_WARN("%s: power_state already power off", __func__);
+ return 0;
+ }
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), DELAY_TIMES,
+ RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_ERR("%s: failed(%d)", __func__, ret);
+ bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
+ return ret;
+ }
+
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
+ BTMTK_INFO("%s done", __func__);
+ return ret;
+}
+
+int btmtk_picus_enable(struct btmtk_dev *bdev)
+{
+ u8 dft_enable_cmd[] = { 0x01, 0x5D, 0xFC, 0x04, 0x00, 0x00, 0x02, 0x02 };
+ u8 *enable_cmd = NULL;
+ u8 enable_event[] = { 0x04, 0x0E, 0x08, 0x01, 0x5D, 0xFC, 0x00, 0x00, 0x00 };
+ int enable_len = 0;
+ int ret = -1; /* if successful, 0 */
+
+ struct fw_cfg_struct *picus_setting = &bdev->bt_cfg.picus_enable;
+
+ BTMTK_INFO("%s", __func__);
+
+ if (picus_setting->content && picus_setting->length) {
+ BTMTK_INFO("%s load picus from bt.cfg", __func__);
+ enable_cmd = picus_setting->content;
+ enable_len = picus_setting->length;
+ } else {
+ enable_cmd = dft_enable_cmd;
+ enable_len = sizeof(dft_enable_cmd);
+ }
+ BTMTK_INFO_RAW(enable_cmd, enable_len, "%s: Send CMD:", __func__);
+
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id))
+ ret = btmtk_main_send_cmd(bdev,
+ enable_cmd, enable_len,
+ enable_event, sizeof(enable_event),
+ DELAY_TIMES, RETRY_TIMES,
+ BTMTK_TX_PKT_FROM_HOST);
+ else
+ BTMTK_WARN("%s: not support for 0x%x", __func__, bdev->chip_id);
+
+ BTMTK_INFO("%s: ret %d", __func__, ret);
+ return ret;
+}
+
+int btmtk_picus_disable(struct btmtk_dev *bdev)
+{
+ u8 dft_disable_cmd[] = { 0x01, 0x5D, 0xFC, 0x04, 0x00, 0x00, 0x02, 0x00 };
+ u8 dft_disable_event[] = { 0x04, 0x0E, 0x08, 0x01, 0x5D, 0xFC, 0x00, 0x00, 0x00 };
+ int ret = -1; /* if successful, 0 */
+
+ BTMTK_INFO("%s\n", __func__);
+
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id))
+ ret = btmtk_main_send_cmd(bdev,
+ dft_disable_cmd, sizeof(dft_disable_cmd),
+ dft_disable_event, sizeof(dft_disable_event),
+ DELAY_TIMES, RETRY_TIMES,
+ BTMTK_TX_PKT_FROM_HOST);
+ else
+ BTMTK_WARN("%s: not support for 0x%x", __func__, bdev->chip_id);
+
+ BTMTK_INFO("%s: ret %d", __func__, ret);
+ return ret;
+}
+
+static int btmtk_send_apcf_reserved(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+
+ u8 reserve_apcf_cmd[] = { 0x01, 0xC9, 0xFC, 0x05, 0x01, 0x30, 0x02, 0x61, 0x02 };
+ u8 reserve_apcf_event[] = { 0x04, 0xE6, 0x02, 0x08, 0x11 };
+
+ if (bdev == NULL) {
+ BTMTK_ERR("%s: Incorrect bdev", __func__);
+ return ret;
+ }
+
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id))
+ ret = btmtk_main_send_cmd(bdev, reserve_apcf_cmd, sizeof(reserve_apcf_cmd),
+ reserve_apcf_event, sizeof(reserve_apcf_event), 0, 0,
+ BTMTK_TX_PKT_FROM_HOST);
+ else
+ BTMTK_WARN("%s: not support for 0x%x", __func__, bdev->chip_id);
+
+ BTMTK_INFO("%s: ret %d", __func__, ret);
+ return ret;
+}
+
+int btmtk_send_read_BDADDR_cmd(struct btmtk_dev *bdev)
+{
+ u8 cmd[] = { 0x01, 0x09, 0x10, 0x00 };
+ u8 event[] = { 0x04, 0x0E, 0x0A, 0x01, 0x09, 0x10, 0x00, /* AA, BB, CC, DD, EE, FF */ };
+ int i;
+ int ret = -1;
+
+ BTMTK_INFO("%s: begin", __func__);
+ if (bdev == NULL || bdev->io_buf == NULL) {
+ BTMTK_ERR("%s: Incorrect bdev", __func__);
+ return ret;
+ }
+
+ for (i = 0; i < BD_ADDRESS_SIZE; i++) {
+ if (bdev->bdaddr[i] != 0) {
+ ret = 0;
+ goto done;
+ }
+ }
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_PKT_FROM_HOST);
+ /*BD address will get in btmtk_rx_work*/
+ if (ret < 0)
+ BTMTK_ERR("%s: failed(%d)", __func__, ret);
+
+done:
+ BTMTK_INFO("%s, end, ret = %d", __func__, ret);
+ return ret;
+}
+
+static int btmtk_send_unify_woble_suspend_default_cmd(struct btmtk_dev *bdev)
+{
+ int ret = 0; /* if successful, 0 */
+ u8 cmd[] = { 0x01, 0xC9, 0xFC, 0x24, 0x01, 0x20, 0x02, 0x00, 0x01,
+ 0x02, 0x01, 0x00, 0x05, 0x10, 0x00, 0x00, 0x40, 0x06,
+ 0x02, 0x40, 0x0A, 0x02, 0x41, 0x0F, 0x05, 0x24, 0x20,
+ 0x04, 0x32, 0x00, 0x09, 0x26, 0xC0, 0x12, 0x00, 0x00,
+ 0x12, 0x00, 0x00, 0x00};
+ /*u8 status[] = { 0x0F, 0x04, 0x00, 0x01, 0xC9, 0xFC }; */
+ u8 comp_event[] = { 0x04, 0xE6, 0x02, 0x08, 0x00 };
+
+ BTMTK_INFO("%s: begin", __func__);
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), comp_event, sizeof(comp_event),
+ 0, 0, BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0)
+ BTMTK_ERR("%s: failed(%d)", __func__, ret);
+
+ BTMTK_INFO("%s: end. ret = %d", __func__, ret);
+ return ret;
+}
+
+static int btmtk_send_unify_woble_resume_default_cmd(struct btmtk_dev *bdev)
+{
+ int ret = 0; /* if successful, 0 */
+ u8 cmd[] = { 0x01, 0xC9, 0xFC, 0x05, 0x01, 0x21, 0x02, 0x00, 0x00 };
+ /*u8 status[] = { 0x0F, 0x04, 0x00, 0x01, 0xC9, 0xFC };*/
+ u8 comp_event[] = { 0x04, 0xE6, 0x02, 0x08, 0x01 };
+
+ BTMTK_INFO("%s: begin", __func__);
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), comp_event, sizeof(comp_event),
+ 0, 0, BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0)
+ BTMTK_ERR("%s: failed(%d)", __func__, ret);
+
+ BTMTK_INFO("%s: end. ret = %d", __func__, ret);
+ return ret;
+}
+
+static int btmtk_send_woble_suspend_cmd(struct btmtk_dev *bdev)
+{
+ int ret = 0; /* if successful, 0 */
+ /* radio off cmd with wobx_mode_disable, used when unify woble off */
+ u8 radio_off_cmd[] = { 0x01, 0xC9, 0xFC, 0x05, 0x01, 0x20, 0x02, 0x00, 0x00 };
+ /*u8 status_event[] = { 0x0F, 0x04, 0x00, 0x01, 0xC9, 0xFC };*/
+ u8 comp_event[] = { 0x04, 0xE6, 0x02, 0x08, 0x00 };
+
+ BTMTK_INFO("%s: not support woble, send radio off cmd", __func__);
+ ret = btmtk_main_send_cmd(bdev, radio_off_cmd, sizeof(radio_off_cmd),
+ comp_event, sizeof(comp_event), 0, 0, BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0)
+ BTMTK_ERR("%s: failed(%d)", __func__, ret);
+
+ return ret;
+}
+
+static int btmtk_send_woble_resume_cmd(struct btmtk_dev *bdev)
+{
+ int ret = 0; /* if successful, 0 */
+ /* radio on cmd with wobx_mode_disable, used when unify woble off */
+ u8 radio_on_cmd[] = { 0x01, 0xC9, 0xFC, 0x05, 0x01, 0x21, 0x02, 0x00, 0x00 };
+ /*u8 status[] = { 0x0F, 0x04, 0x00, 0x01, 0xC9, 0xFC };*/
+ u8 comp_event[] = { 0x04, 0xE6, 0x02, 0x08, 0x01 };
+
+ BTMTK_INFO("%s: begin", __func__);
+ ret = btmtk_main_send_cmd(bdev, radio_on_cmd, sizeof(radio_on_cmd),
+ comp_event, sizeof(comp_event), 0, 0, BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0)
+ BTMTK_ERR("%s: failed(%d)", __func__, ret);
+
+ return ret;
+}
+
+
+int btmtk_set_Woble_APCF_filter_parameter(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+ u8 cmd[] = { 0x01, 0x57, 0xFD, 0x0A, 0x01, 0x00, 0x0A,
+ 0x20, 0x00, 0x20, 0x00, 0x01, 0x80, 0x00 };
+ u8 event_complete[] = { 0x04, 0x0E, 0x07, 0x01, 0x57, 0xFD, 0x00, 0x01/*, 00, 63*/ };
+
+ BTMTK_INFO("%s: begin", __func__);
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd),
+ event_complete, sizeof(event_complete), 0, 0, BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0)
+ BTMTK_ERR("%s: end ret %d", __func__, ret);
+ else
+ ret = 0;
+
+ BTMTK_INFO("%s: end ret=%d", __func__, ret);
+ return ret;
+}
+
+
+/**
+ * Set APCF manufacturer data and filter parameter
+ */
+int btmtk_set_Woble_APCF(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+ int i = 0;
+ u8 manufactur_data[] = { 0x01, 0x57, 0xFD, 0x27, 0x06, 0x00, 0x0A,
+ 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x4B, 0x54, 0x4D,
+ 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ u8 event[] = { 0x04, 0x0E, 0x07, 0x01, 0x57, 0xFD, 0x00, /* 0x06 00 63 */ };
+
+ BTMTK_INFO("%s: woble_setting_apcf[0].length %d",
+ __func__, bdev->woble_setting_apcf[0].length);
+
+ /* start to send apcf cmd from woble setting file */
+ if (bdev->woble_setting_apcf[0].length) {
+ for (i = 0; i < WOBLE_SETTING_COUNT; i++) {
+ if (!bdev->woble_setting_apcf[i].length)
+ continue;
+
+ BTMTK_INFO("%s: apcf_fill_mac[%d].content[0] = 0x%02x", __func__, i,
+ bdev->woble_setting_apcf_fill_mac[i].content[0]);
+ BTMTK_INFO("%s: apcf_fill_mac_location[%d].length = %d", __func__, i,
+ bdev->woble_setting_apcf_fill_mac_location[i].length);
+
+ if ((bdev->woble_setting_apcf_fill_mac[i].content[0] == 1) &&
+ bdev->woble_setting_apcf_fill_mac_location[i].length) {
+ /* need add BD addr to apcf cmd */
+ memcpy(bdev->woble_setting_apcf[i].content +
+ (*bdev->woble_setting_apcf_fill_mac_location[i].content + 1),
+ bdev->bdaddr, BD_ADDRESS_SIZE);
+ BTMTK_INFO("%s: apcf[%d], add local BDADDR to location %d",
+ __func__, i,
+ (*bdev->woble_setting_apcf_fill_mac_location[i].content));
+ }
+
+ BTMTK_INFO_RAW(bdev->woble_setting_apcf[i].content,
+ bdev->woble_setting_apcf[i].length,
+ "Send woble_setting_apcf[%d] ", i);
+ ret = btmtk_main_send_cmd(bdev, bdev->woble_setting_apcf[i].content,
+ bdev->woble_setting_apcf[i].length, event, sizeof(event), 0, 0,
+ BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0) {
+ BTMTK_ERR("%s: manufactur_data error ret %d", __func__, ret);
+ return ret;
+ }
+ }
+ } else { /* use default */
+ BTMTK_INFO("%s: use default manufactur data", __func__);
+ memcpy(manufactur_data + 10, bdev->bdaddr, BD_ADDRESS_SIZE);
+ ret = btmtk_main_send_cmd(bdev, manufactur_data, sizeof(manufactur_data),
+ event, sizeof(event), 0, 0, BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0) {
+ BTMTK_ERR("%s: manufactur_data error ret %d", __func__, ret);
+ return ret;
+ }
+
+ ret = btmtk_set_Woble_APCF_filter_parameter(bdev);
+ }
+
+ BTMTK_INFO("%s: end ret=%d", __func__, ret);
+ return 0;
+}
+
+static int btmtk_set_Woble_Radio_Off(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+ int length = 0;
+ char *radio_off = NULL;
+
+ BTMTK_INFO("%s: woble_setting_radio_off.length %d", __func__,
+ bdev->woble_setting_radio_off.length);
+ if (bdev->woble_setting_radio_off.length) {
+ /* start to send radio off cmd from woble setting file */
+ length = bdev->woble_setting_radio_off.length +
+ bdev->woble_setting_wakeup_type.length;
+ radio_off = kzalloc(length, GFP_KERNEL);
+ if (!radio_off) {
+ BTMTK_ERR("%s: alloc memory fail (radio_off)",
+ __func__);
+ ret = -ENOMEM;
+ goto Finish;
+ }
+
+ memcpy(radio_off,
+ bdev->woble_setting_radio_off.content,
+ bdev->woble_setting_radio_off.length);
+ if (bdev->woble_setting_wakeup_type.length) {
+ memcpy(radio_off + bdev->woble_setting_radio_off.length,
+ bdev->woble_setting_wakeup_type.content,
+ bdev->woble_setting_wakeup_type.length);
+ radio_off[3] += bdev->woble_setting_wakeup_type.length;
+ }
+
+ BTMTK_INFO_RAW(radio_off, length, "Send radio off");
+ ret = btmtk_main_send_cmd(bdev, radio_off, length,
+ bdev->woble_setting_radio_off_comp_event.content,
+ bdev->woble_setting_radio_off_comp_event.length, 0, 0,
+ BTMTK_TX_PKT_FROM_HOST);
+
+ kfree(radio_off);
+ radio_off = NULL;
+ } else { /* use default */
+ BTMTK_INFO("%s: use default radio off cmd", __func__);
+ ret = btmtk_send_unify_woble_suspend_default_cmd(bdev);
+ }
+
+Finish:
+ BTMTK_INFO("%s, end ret=%d", __func__, ret);
+ return ret;
+}
+
+static int btmtk_set_Woble_Radio_On(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+
+ BTMTK_INFO("%s: woble_setting_radio_on.length %d", __func__,
+ bdev->woble_setting_radio_on.length);
+ if (bdev->woble_setting_radio_on.length) {
+ /* start to send radio on cmd from woble setting file */
+ BTMTK_INFO_RAW(bdev->woble_setting_radio_on.content,
+ bdev->woble_setting_radio_on.length, "send radio on");
+
+ ret = btmtk_main_send_cmd(bdev, bdev->woble_setting_radio_on.content,
+ bdev->woble_setting_radio_on.length,
+ bdev->woble_setting_radio_on_comp_event.content,
+ bdev->woble_setting_radio_on_comp_event.length, 0, 0,
+ BTMTK_TX_PKT_FROM_HOST);
+ } else { /* use default */
+ BTMTK_WARN("%s: use default radio on cmd", __func__);
+ ret = btmtk_send_unify_woble_resume_default_cmd(bdev);
+ }
+
+ BTMTK_INFO("%s, end ret=%d", __func__, ret);
+ return ret;
+}
+
+static int btmtk_del_Woble_APCF_index(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+ u8 cmd[] = { 0x01, 0x57, 0xFD, 0x03, 0x01, 0x01, 0x0A };
+ u8 event[] = { 0x04, 0x0e, 0x07, 0x01, 0x57, 0xfd, 0x00, 0x01, /* 00, 63 */ };
+
+ BTMTK_INFO("%s, enter", __func__);
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event),
+ 0, 0, BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0)
+ BTMTK_ERR("%s: got error %d", __func__, ret);
+
+ BTMTK_INFO("%s, end", __func__);
+ return ret;
+}
+
+static int btmtk_set_Woble_APCF_Resume(struct btmtk_dev *bdev)
+{
+ int i = 0;
+ int ret = -1;
+ u8 event_complete[] = { 0x04, 0x0e, 0x07, 0x01, 0x57, 0xfd, 0x00 };
+
+ BTMTK_INFO("%s, enter, bdev->woble_setting_apcf_resume[0].length= %d",
+ __func__, bdev->woble_setting_apcf_resume[0].length);
+ if (bdev->woble_setting_apcf_resume[0].length) {
+ BTMTK_INFO("%s: handle leave woble apcf from file", __func__);
+ for (i = 0; i < WOBLE_SETTING_COUNT; i++) {
+ if (!bdev->woble_setting_apcf_resume[i].length)
+ continue;
+
+ BTMTK_INFO_RAW(bdev->woble_setting_apcf_resume[i].content,
+ bdev->woble_setting_apcf_resume[i].length,
+ "%s: send apcf resume %d:", __func__, i);
+
+ ret = btmtk_main_send_cmd(bdev,
+ bdev->woble_setting_apcf_resume[i].content,
+ bdev->woble_setting_apcf_resume[i].length,
+ event_complete, sizeof(event_complete),
+ 0, 0, BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0) {
+ BTMTK_ERR("%s: Send apcf resume fail %d", __func__, ret);
+ return ret;
+ }
+ }
+ } else { /* use default */
+ BTMTK_WARN("%s: use default apcf resume cmd", __func__);
+ ret = btmtk_del_Woble_APCF_index(bdev);
+ if (ret < 0)
+ BTMTK_ERR("%s: btmtk_del_Woble_APCF_index return fail %d", __func__, ret);
+ }
+ BTMTK_INFO("%s, end", __func__);
+ return ret;
+}
+
+int btmtk_load_fw_cfg_setting(char *block_name, struct fw_cfg_struct *save_content,
+ int counter, u8 *searchcontent, enum fw_cfg_index_len index_length)
+{
+ int ret = 0, i = 0;
+ int temp_len = 0;
+ u8 temp[260]; /* save for total hex number */
+ unsigned long parsing_result = 0;
+ char *search_result = NULL;
+ char *search_end = NULL;
+ char search[32];
+ char *next_block = NULL;
+ char number[CHAR2HEX_SIZE + 1]; /* 1 is for '\0' */
+
+ memset(search, 0, sizeof(search));
+ memset(temp, 0, sizeof(temp));
+ memset(number, 0, sizeof(number));
+
+ /* search block name */
+ for (i = 0; i < counter; i++) {
+ temp_len = 0;
+ if (index_length == FW_CFG_INX_LEN_2) /* EX: APCF01 */
+ snprintf(search, sizeof(search), "%s%02d:", block_name, i);
+ else if (index_length == FW_CFG_INX_LEN_3) /* EX: APCF001 */
+ snprintf(search, sizeof(search), "%s%03d:", block_name, i);
+ else
+ snprintf(search, sizeof(search), "%s:", block_name);
+ search_result = strstr((char *)searchcontent, search);
+
+ if (search_result) {
+ memset(temp, 0, sizeof(temp));
+ search_result = strstr(search_result, "0x");
+ /* find next line as end of this command line, if NULL means last line */
+ next_block = strstr(search_result, ":");
+
+ /* Add HCI packet type to front of each command/event */
+ if (!memcmp(block_name, "APCF", sizeof("APCF")) ||
+ !memcmp(block_name, "RADIOOFF", sizeof("RADIOOFF")) ||
+ !memcmp(block_name, "RADIOON", sizeof("RADIOON")) ||
+ !memcmp(block_name, "APCF_RESUME", sizeof("APCF_RESUME")) ||
+ !memcmp(block_name, "VENDOR_CMD", sizeof("VENDOR_CMD")) ||
+ !memcmp(block_name, "PHASE1_WMT_CMD", sizeof("PHASE1_WMT_CMD"))) {
+ temp[0] = 0x01;
+ temp_len++;
+ } else if (!memcmp(block_name, "RADIOOFF_STATUS_EVENT",
+ sizeof("RADIOOFF_STATUS_EVENT")) ||
+ !memcmp(block_name, "RADIOOFF_COMPLETE_EVENT",
+ sizeof("RADIOOFF_COMPLETE_EVENT")) ||
+ !memcmp(block_name, "RADIOON_STATUS_EVENT",
+ sizeof("RADIOON_STATUS_EVENT")) ||
+ !memcmp(block_name, "RADIOON_COMPLETE_EVENT",
+ sizeof("RADIOON_COMPLETE_EVENT"))) {
+ temp[0] = 0x04;
+ temp_len++;
+ }
+
+ do {
+ search_end = strstr(search_result, ",");
+ if (search_end - search_result != CHAR2HEX_SIZE) {
+ BTMTK_ERR("%s: Incorrect Format in %s", __func__, search);
+ break;
+ }
+
+ memset(number, 0, sizeof(number));
+ memcpy(number, search_result, CHAR2HEX_SIZE);
+ ret = kstrtoul(number, 0, &parsing_result);
+ if (ret == 0) {
+ if (temp_len >= sizeof(temp)) {
+ BTMTK_ERR("%s: %s data over %zu",
+ __func__, search, sizeof(temp));
+ break;
+ }
+ temp[temp_len++] = parsing_result;
+ } else {
+ BTMTK_WARN("%s: %s kstrtoul fail: %d",
+ __func__, search, ret);
+ break;
+ }
+ search_result = strstr(search_end, "0x");
+ } while (search_result < next_block ||
+ (search_result && next_block == NULL));
+ } else
+ BTMTK_DBG("%s: %s is not found in %d", __func__, search, i);
+
+ if (temp_len && temp_len < sizeof(temp)) {
+ BTMTK_INFO("%s: %s found & stored in %d", __func__, search, i);
+ save_content[i].content = kzalloc(temp_len, GFP_KERNEL);
+ if (save_content[i].content == NULL) {
+ BTMTK_ERR("%s: Allocate memory fail(%d)", __func__, i);
+ return -ENOMEM;
+ }
+ memcpy(save_content[i].content, temp, temp_len);
+ save_content[i].length = temp_len;
+ BTMTK_DBG_RAW(save_content[i].content,
+ save_content[i].length, "%s", search);
+ }
+ }
+
+ return ret;
+}
+
+int btmtk_load_code_from_setting_files(char *setting_file_name,
+ struct device *dev, u32 *code_len, struct btmtk_dev *bdev)
+{
+ int err;
+ const struct firmware *fw_entry = NULL;
+
+ *code_len = 0;
+
+ if (bdev == NULL) {
+ BTMTK_ERR("%s: g_data is NULL!!", __func__);
+ err = -1;
+ return err;
+ }
+
+ BTMTK_INFO("%s: begin setting_file_name = %s", __func__, setting_file_name);
+ err = request_firmware(&fw_entry, setting_file_name, dev);
+ if (err != 0 || fw_entry == NULL) {
+ BTMTK_INFO("%s: request_firmware fail, file not exist, err = %d, fw_entry = %p",
+ __func__, err, fw_entry);
+ if (fw_entry)
+ release_firmware(fw_entry);
+ return err;
+ }
+
+ BTMTK_INFO("%s: setting file request_firmware size %zu success", __func__, fw_entry->size);
+ if (bdev->setting_file != NULL) {
+ kfree(bdev->setting_file);
+ bdev->setting_file = NULL;
+ }
+ /* alloc setting file memory */
+ bdev->setting_file = kzalloc(fw_entry->size + 1, GFP_KERNEL);
+ if (bdev->setting_file == NULL) {
+ BTMTK_ERR("%s: kzalloc size %zu failed!!", __func__, fw_entry->size);
+ release_firmware(fw_entry);
+ return err;
+ }
+
+ memcpy(bdev->setting_file, fw_entry->data, fw_entry->size);
+ bdev->setting_file[fw_entry->size] = '\0';
+
+ *code_len = fw_entry->size;
+ release_firmware(fw_entry);
+
+ BTMTK_INFO("%s: setting_file len (%d) assign done", __func__, *code_len);
+ return err;
+}
+
+int btmtk_load_woble_setting(char *bin_name,
+ struct device *dev, u32 *code_len, struct btmtk_dev *bdev)
+{
+ int err;
+ *code_len = 0;
+
+ err = btmtk_load_code_from_setting_files(bin_name, dev, code_len, bdev);
+ if (err) {
+ BTMTK_ERR("woble_setting btmtk_load_code_from_setting_files failed!!");
+ goto LOAD_END;
+ }
+
+ err = btmtk_load_fw_cfg_setting("APCF",
+ bdev->woble_setting_apcf, WOBLE_SETTING_COUNT,
+ bdev->setting_file, FW_CFG_INX_LEN_2);
+ if (err)
+ goto LOAD_END;
+
+ err = btmtk_load_fw_cfg_setting("APCF_ADD_MAC",
+ bdev->woble_setting_apcf_fill_mac, WOBLE_SETTING_COUNT,
+ bdev->setting_file, FW_CFG_INX_LEN_2);
+ if (err)
+ goto LOAD_END;
+
+ err = btmtk_load_fw_cfg_setting("APCF_ADD_MAC_LOCATION",
+ bdev->woble_setting_apcf_fill_mac_location, WOBLE_SETTING_COUNT,
+ bdev->setting_file, FW_CFG_INX_LEN_2);
+ if (err)
+ goto LOAD_END;
+
+ err = btmtk_load_fw_cfg_setting("RADIOOFF", &bdev->woble_setting_radio_off, 1,
+ bdev->setting_file, FW_CFG_INX_LEN_2);
+ if (err)
+ goto LOAD_END;
+
+ switch (bdev->bt_cfg.unify_woble_type) {
+ case 0:
+ err = btmtk_load_fw_cfg_setting("WAKEUP_TYPE_LEGACY",
+ &bdev->woble_setting_wakeup_type, 1,
+ bdev->setting_file, FW_CFG_INX_LEN_2);
+ break;
+ case 1:
+ err = btmtk_load_fw_cfg_setting("WAKEUP_TYPE_WAVEFORM",
+ &bdev->woble_setting_wakeup_type, 1,
+ bdev->setting_file, FW_CFG_INX_LEN_2);
+ break;
+ case 2:
+ err = btmtk_load_fw_cfg_setting("WAKEUP_TYPE_IR",
+ &bdev->woble_setting_wakeup_type, 1,
+ bdev->setting_file, FW_CFG_INX_LEN_2);
+ break;
+ default:
+ BTMTK_WARN("%s: unify_woble_type unknown(%d)",
+ __func__, bdev->bt_cfg.unify_woble_type);
+ }
+ if (err)
+ BTMTK_WARN("%s: Parse unify_woble_type(%d) failed",
+ __func__, bdev->bt_cfg.unify_woble_type);
+
+ err = btmtk_load_fw_cfg_setting("RADIOOFF_STATUS_EVENT",
+ &bdev->woble_setting_radio_off_status_event,
+ 1, bdev->setting_file, FW_CFG_INX_LEN_2);
+ if (err)
+ goto LOAD_END;
+
+ err = btmtk_load_fw_cfg_setting("RADIOOFF_COMPLETE_EVENT",
+ &bdev->woble_setting_radio_off_comp_event,
+ 1, bdev->setting_file, FW_CFG_INX_LEN_2);
+ if (err)
+ goto LOAD_END;
+
+ err = btmtk_load_fw_cfg_setting("RADIOON",
+ &bdev->woble_setting_radio_on, 1, bdev->setting_file, FW_CFG_INX_LEN_2);
+ if (err)
+ goto LOAD_END;
+
+ err = btmtk_load_fw_cfg_setting("RADIOON_STATUS_EVENT",
+ &bdev->woble_setting_radio_on_status_event,
+ 1, bdev->setting_file, FW_CFG_INX_LEN_2);
+ if (err)
+ goto LOAD_END;
+
+ err = btmtk_load_fw_cfg_setting("RADIOON_COMPLETE_EVENT",
+ &bdev->woble_setting_radio_on_comp_event,
+ 1, bdev->setting_file, FW_CFG_INX_LEN_2);
+ if (err)
+ goto LOAD_END;
+
+ err = btmtk_load_fw_cfg_setting("APCF_RESUME",
+ bdev->woble_setting_apcf_resume, WOBLE_SETTING_COUNT,
+ bdev->setting_file, FW_CFG_INX_LEN_2);
+
+LOAD_END:
+ /* release setting file memory */
+ if (bdev) {
+ kfree(bdev->setting_file);
+ bdev->setting_file = NULL;
+ }
+
+ if (err)
+ BTMTK_ERR("%s: error return %d", __func__, err);
+
+ return err;
+}
+
+static void btmtk_check_wobx_debug_log(struct btmtk_dev *bdev)
+{
+ /* 0xFF, 0xFF, 0xFF, 0xFF is log level */
+ u8 cmd[] = { 0X01, 0xCE, 0xFC, 0x04, 0xFF, 0xFF, 0xFF, 0xFF };
+ u8 event[] = { 0x04, 0xE8 };
+ int ret = -1;
+
+ BTMTK_INFO("%s: begin", __func__);
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0)
+ BTMTK_ERR("%s: failed(%d)", __func__, ret);
+
+ /* Driver just print event to kernel log in rx_work,
+ * Please reference wiki to know what it is.
+ */
+}
+
+int btmtk_handle_leaving_WoBLE_state(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+
+ BTMTK_INFO("%s: begin", __func__);
+ fstate = btmtk_fops_get_state(bdev);
+ if (!bdev->bt_cfg.support_woble_for_bt_disable) {
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not opened, return", __func__);
+ return 0;
+ }
+ }
+
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not open yet(%d), need to start traffic before leaving",
+ __func__, fstate);
+ /* start traffic to recv event*/
+ ret = main_info.hif_hook.open(bdev->hdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, cif_open failed", __func__);
+ goto Finish;
+ }
+ }
+
+ if (is_support_unify_woble(bdev)) {
+ ret = btmtk_set_Woble_Radio_On(bdev);
+ if (ret < 0)
+ goto Finish;
+
+ ret = btmtk_set_Woble_APCF_Resume(bdev);
+ if (ret < 0)
+ goto Finish;
+ } else {
+ /* radio on cmd with wobx_mode_disable, used when unify woble off */
+ ret = btmtk_send_woble_resume_cmd(bdev);
+ }
+
+Finish:
+ if (ret < 0) {
+ BTMTK_INFO("%s: woble_resume_fail!!!", __func__);
+ } else {
+ /* It's wobx debug log method. */
+ btmtk_check_wobx_debug_log(bdev);
+
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ ret = btmtk_send_deinit_cmds(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, btmtk_send_deinit_cmds failed", __func__);
+ goto exit;
+ }
+
+ BTMTK_WARN("%s: fops is not open(%d) \,
+ need to stop traffic after leaving woble",
+ __func__, fstate);
+ /* stop traffic to stop recv data from fw*/
+ ret = main_info.hif_hook.close(bdev->hdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, cif_close failed", __func__);
+ goto exit;
+ }
+ } else
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_ON;
+ BTMTK_INFO("%s: success", __func__);
+ }
+
+exit:
+ BTMTK_INFO("%s: end", __func__);
+ return ret;
+}
+
+int btmtk_handle_entering_WoBLE_state(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+ int state = BTMTK_STATE_INIT;
+
+ BTMTK_INFO("%s: begin", __func__);
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (!bdev->bt_cfg.support_woble_for_bt_disable) {
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not open yet(%d)!, return", __func__, fstate);
+ return 0;
+ }
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state == BTMTK_STATE_FW_DUMP) {
+ BTMTK_WARN("%s: FW dumping ongoing, don't send any cmd to FW!!!", __func__);
+ goto Finish;
+ }
+
+ if (bdev->chip_reset || bdev->subsys_reset) {
+ BTMTK_ERR("%s chip_reset is %d, subsys_reset is %d", __func__,
+ bdev->chip_reset, bdev->subsys_reset);
+ goto Finish;
+ }
+
+ /* Power on first if state is power off */
+ ret = btmtk_reset_power_on(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s: reset power_on fail return", __func__);
+ goto Finish;
+ }
+
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not open yet(%d), need to start traffic before enter woble",
+ __func__, fstate);
+ /* start traffic to recv event*/
+ ret = main_info.hif_hook.open(bdev->hdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, cif_open failed", __func__);
+ goto Finish;
+ }
+ }
+ if (is_support_unify_woble(bdev)) {
+ do {
+ typedef ssize_t (*func) (u16 u16Key, const char *buf, size_t size);
+ char *func_name = "MDrv_PM_Write_Key";
+ func pFunc = NULL;
+ ssize_t sret = 0;
+ u8 buf = 0;
+
+ pFunc = (func) btmtk_kallsyms_lookup_name(func_name);
+ if (pFunc && bdev->bt_cfg.unify_woble_type == 1) {
+ buf = 1;
+ sret = pFunc(PM_KEY_BTW, &buf, sizeof(u8));
+ BTMTK_INFO("%s: Invoke %s, buf = %d, sret = %zd", __func__,
+ func_name, buf, sret);
+
+ } else {
+ BTMTK_WARN("%s: No Exported Func Found [%s]", __func__, func_name);
+ }
+ } while (0);
+
+ ret = btmtk_send_apcf_reserved(bdev);
+ if (ret < 0)
+ goto STOP_TRAFFIC;
+
+ ret = btmtk_send_read_BDADDR_cmd(bdev);
+ if (ret < 0)
+ goto STOP_TRAFFIC;
+
+ ret = btmtk_set_Woble_APCF(bdev);
+ if (ret < 0)
+ goto STOP_TRAFFIC;
+
+ ret = btmtk_set_Woble_Radio_Off(bdev);
+ if (ret < 0)
+ goto STOP_TRAFFIC;
+ } else {
+ /* radio off cmd with wobx_mode_disable, used when unify woble off */
+ ret = btmtk_send_woble_suspend_cmd(bdev);
+ }
+
+STOP_TRAFFIC:
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not open(%d), need to stop traffic after enter woble",
+ __func__, fstate);
+ /* stop traffic to stop recv data from fw*/
+ ret = main_info.hif_hook.close(bdev->hdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, cif_close failed", __func__);
+ goto Finish;
+ }
+ }
+
+Finish:
+ if (ret) {
+ bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
+ if (bdev->bt_cfg.support_woble_wakelock)
+ btmtk_woble_wake_lock(bdev);
+ }
+
+ BTMTK_INFO("%s: end ret = %d, power_state =%d", __func__, ret, bdev->power_state);
+ return ret;
+}
+
+int btmtk_woble_suspend(struct btmtk_dev *bdev)
+{
+ int ret = 0;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+
+ BTMTK_INFO("%s: enter", __func__);
+
+ fstate = btmtk_fops_get_state(bdev);
+
+ if (!is_support_unify_woble(bdev) && (fstate != BTMTK_FOPS_STATE_OPENED)) {
+ BTMTK_WARN("%s: when not support woble, in bt off state, do nothing!", __func__);
+ goto exit;
+ }
+
+ ret = btmtk_handle_entering_WoBLE_state(bdev);
+ if (ret)
+ BTMTK_ERR("%s: btmtk_handle_entering_WoBLE_state return fail %d", __func__, ret);
+
+ if (bdev->bt_cfg.support_woble_by_eint) {
+ if (bdev->wobt_irq != 0 && atomic_read(&(bdev->irq_enable_count)) == 0) {
+ BTMTK_INFO("enable BT IRQ:%d", bdev->wobt_irq);
+ irq_set_irq_wake(bdev->wobt_irq, 1);
+ enable_irq(bdev->wobt_irq);
+ atomic_inc(&(bdev->irq_enable_count));
+ } else
+ BTMTK_INFO("irq_enable count:%d", atomic_read(&(bdev->irq_enable_count)));
+ }
+
+exit:
+ BTMTK_INFO("%s: end", __func__);
+ return ret;
+}
+
+int btmtk_woble_resume(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+
+ BTMTK_INFO("%s: enter", __func__);
+ fstate = btmtk_fops_get_state(bdev);
+
+ if (!is_support_unify_woble(bdev) && (fstate != BTMTK_FOPS_STATE_OPENED)) {
+ BTMTK_WARN("%s: when not support woble, in bt off state, do nothing!", __func__);
+ goto exit;
+ }
+
+ if (bdev->power_state == BTMTK_DONGLE_STATE_ERROR) {
+ BTMTK_INFO("%s: In BTMTK_DONGLE_STATE_ERROR(Could suspend caused), do assert",
+ __func__);
+ btmtk_send_assert_cmd(bdev);
+ ret = -EBADFD;
+ goto exit;
+ }
+
+
+ if (bdev->bt_cfg.support_woble_by_eint) {
+ if (bdev->wobt_irq != 0 && atomic_read(&(bdev->irq_enable_count)) == 1) {
+ BTMTK_INFO("disable BT IRQ:%d", bdev->wobt_irq);
+ atomic_dec(&(bdev->irq_enable_count));
+ disable_irq_nosync(bdev->wobt_irq);
+ } else
+ BTMTK_INFO("irq_enable count:%d", atomic_read(&(bdev->irq_enable_count)));
+ }
+
+ ret = btmtk_handle_leaving_WoBLE_state(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s: btmtk_handle_leaving_WoBLE_state return fail %d", __func__, ret);
+ /* avoid rtc to do suspend again, do FW dump first */
+ btmtk_woble_wake_lock(bdev);
+ btmtk_send_assert_cmd(bdev);
+ goto exit;
+ }
+
+ if (bdev->bt_cfg.reset_stack_after_woble
+ && main_info.reset_stack_flag == HW_ERR_NONE
+ && fstate == BTMTK_FOPS_STATE_OPENED)
+ main_info.reset_stack_flag = HW_ERR_CODE_RESET_STACK_AFTER_WOBLE;
+
+ btmtk_send_hw_err_to_host(bdev);
+ BTMTK_INFO("%s: end(%d), reset_stack_flag = %d, fstate = %d", __func__, ret,
+ main_info.reset_stack_flag, fstate);
+
+exit:
+ BTMTK_INFO("%s: end", __func__);
+ return ret;
+}
+
+static int btmtk_enter_standby(void)
+{
+ int ret = 0;
+ int i = 0;
+ int cif_event = 0;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+
+ BTMTK_INFO("%s: enter", __func__);
+ for (i = 0; i < btmtk_intf_num; i++) {
+ /* Find valid dev for already probe interface. */
+ if (g_bdev[i]->hdev != NULL) {
+ bdev = g_bdev[i];
+
+ /* Retrieve current HIF event state */
+ cif_event = HIF_EVENT_STANDBY;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s parameter is NULL", __func__);
+ return -ENODEV;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ if (main_info.hif_hook.cif_mutex_lock)
+ main_info.hif_hook.cif_mutex_lock(bdev);
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ ret = btmtk_woble_suspend(bdev);
+
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+
+ if (main_info.hif_hook.cif_mutex_unlock)
+ main_info.hif_hook.cif_mutex_unlock(bdev);
+
+ if (ret)
+ break;
+ }
+ }
+
+ BTMTK_INFO("%s: end", __func__);
+ return ret;
+}
+
+static bool btmtk_parse_bt_cfg_file(char *item_name,
+ char *text, u8 *searchcontent)
+{
+ bool ret = true;
+ int temp_len = 0;
+ char search[32];
+ char *ptr = NULL, *p = NULL;
+ char *temp = text;
+
+ if (text == NULL) {
+ BTMTK_ERR("%s: text param is invalid!", __func__);
+ ret = false;
+ goto out;
+ }
+
+ memset(search, 0, sizeof(search));
+ snprintf(search, sizeof(search), "%s", item_name); /* EX: SUPPORT_UNIFY_WOBLE */
+ p = ptr = strstr((char *)searchcontent, search);
+
+ if (!ptr) {
+ BTMTK_ERR("%s: Can't find %s\n", __func__, item_name);
+ ret = false;
+ goto out;
+ }
+
+ if (p > (char *)searchcontent) {
+ p--;
+ while ((*p == ' ') && (p != (char *)searchcontent))
+ p--;
+ if (*p == '#') {
+ BTMTK_ERR("%s: It's invalid bt cfg item\n", __func__);
+ ret = false;
+ goto out;
+ }
+ }
+
+ p = ptr + strlen(item_name) + 1;
+ ptr = p;
+
+ for (;;) {
+ switch (*p) {
+ case '\n':
+ goto textdone;
+ default:
+ *temp++ = *p++;
+ break;
+ }
+ }
+
+textdone:
+ temp_len = p - ptr;
+ *temp = '\0';
+
+out:
+ return ret;
+}
+
+static void btmtk_bt_cfg_item_value_to_bool(char *item_value, bool *value)
+{
+ unsigned long text_value = 0;
+
+ if (item_value == NULL) {
+ BTMTK_ERR("%s: item_value is NULL!", __func__);
+ return;
+ }
+
+ if (kstrtoul(item_value, 10, &text_value) == 0) {
+ if (text_value == 1)
+ *value = true;
+ else
+ *value = false;
+ } else {
+ BTMTK_WARN("%s: kstrtoul failed!", __func__);
+ }
+}
+
+static bool btmtk_load_bt_cfg_item(struct bt_cfg_struct *bt_cfg_content,
+ u8 *searchcontent, struct btmtk_dev *bdev)
+{
+ bool ret = true;
+ char text[128]; /* save for search text */
+ unsigned long text_value = 0;
+
+ memset(text, 0, sizeof(text));
+ ret = btmtk_parse_bt_cfg_file(BT_UNIFY_WOBLE, text, searchcontent);
+ if (ret) {
+ btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_unify_woble);
+ BTMTK_INFO("%s: bt_cfg_content->support_unify_woble = %d", __func__,
+ bt_cfg_content->support_unify_woble);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_UNIFY_WOBLE);
+ }
+
+ ret = btmtk_parse_bt_cfg_file(BT_UNIFY_WOBLE_TYPE, text, searchcontent);
+ if (ret) {
+ if (kstrtoul(text, 10, &text_value) == 0)
+ bt_cfg_content->unify_woble_type = text_value;
+ else
+ BTMTK_WARN("%s: kstrtoul failed %s!", __func__, BT_UNIFY_WOBLE_TYPE);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_UNIFY_WOBLE_TYPE);
+ }
+
+ BTMTK_INFO("%s: bt_cfg_content->unify_woble_type = %d", __func__,
+ bt_cfg_content->unify_woble_type);
+
+ ret = btmtk_parse_bt_cfg_file(BT_WOBLE_BY_EINT, text, searchcontent);
+ if (ret) {
+ btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_woble_by_eint);
+ BTMTK_INFO("%s: bt_cfg_content->support_woble_by_eint = %d", __func__,
+ bt_cfg_content->support_woble_by_eint);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_WOBLE_BY_EINT);
+ }
+
+ ret = btmtk_parse_bt_cfg_file(BT_DONGLE_RESET_PIN, text, searchcontent);
+ if (ret) {
+ if (kstrtoul(text, 10, &text_value) == 0)
+ bt_cfg_content->dongle_reset_gpio_pin = text_value;
+ else
+ BTMTK_WARN("%s: kstrtoul failed %s!", __func__, BT_DONGLE_RESET_PIN);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_DONGLE_RESET_PIN);
+ }
+
+ BTMTK_INFO("%s: bt_cfg_content->dongle_reset_gpio_pin = %d", __func__,
+ bt_cfg_content->dongle_reset_gpio_pin);
+
+ ret = btmtk_parse_bt_cfg_file(BT_RESET_DONGLE, text, searchcontent);
+ if (ret) {
+ btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_dongle_reset);
+ BTMTK_INFO("%s: bt_cfg_content->support_dongle_reset = %d", __func__,
+ bt_cfg_content->support_dongle_reset);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_RESET_DONGLE);
+ }
+
+ ret = btmtk_parse_bt_cfg_file(BT_FULL_FW_DUMP, text, searchcontent);
+ if (ret) {
+ btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_full_fw_dump);
+ BTMTK_INFO("%s: bt_cfg_content->support_full_fw_dump = %d", __func__,
+ bt_cfg_content->support_full_fw_dump);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_FULL_FW_DUMP);
+ }
+
+ ret = btmtk_parse_bt_cfg_file(BT_WOBLE_WAKELOCK, text, searchcontent);
+ if (ret) {
+ btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_woble_wakelock);
+ BTMTK_INFO("%s: bt_cfg_content->support_woble_wakelock = %d", __func__,
+ bt_cfg_content->support_woble_wakelock);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_WOBLE_WAKELOCK);
+ }
+
+ ret = btmtk_parse_bt_cfg_file(BT_WOBLE_FOR_BT_DISABLE, text, searchcontent);
+ if (ret) {
+ btmtk_bt_cfg_item_value_to_bool(text,
+ &bt_cfg_content->support_woble_for_bt_disable);
+ BTMTK_INFO("%s: bt_cfg_content->support_woble_for_bt_disable = %d", __func__,
+ bt_cfg_content->support_woble_for_bt_disable);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_WOBLE_FOR_BT_DISABLE);
+ }
+
+ ret = btmtk_parse_bt_cfg_file(BT_RESET_STACK_AFTER_WOBLE, text, searchcontent);
+ if (ret) {
+ btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->reset_stack_after_woble);
+ BTMTK_INFO("%s: bt_cfg_content->reset_stack_after_woble = %d", __func__,
+ bt_cfg_content->reset_stack_after_woble);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_RESET_STACK_AFTER_WOBLE);
+ }
+
+ ret = btmtk_parse_bt_cfg_file(BT_AUTO_PICUS, text, searchcontent);
+ if (ret) {
+ btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_auto_picus);
+ BTMTK_INFO("%s: bt_cfg_content->support_auto_picus = %d", __func__,
+ bt_cfg_content->support_auto_picus);
+ if (bt_cfg_content->support_auto_picus == true) {
+ ret = btmtk_load_fw_cfg_setting(BT_AUTO_PICUS_FILTER,
+ &bt_cfg_content->picus_filter,
+ 1, searchcontent, FW_CFG_INX_LEN_NONE);
+ if (ret)
+ BTMTK_WARN("%s: search item %s is invalid!",
+ __func__, BT_AUTO_PICUS_FILTER);
+
+ ret = btmtk_load_fw_cfg_setting(BT_AUTO_PICUS_ENABLE,
+ &bt_cfg_content->picus_enable,
+ 1, searchcontent, FW_CFG_INX_LEN_NONE);
+ if (ret)
+ BTMTK_WARN("%s: search item %s is invalid!",
+ __func__, BT_AUTO_PICUS_ENABLE);
+ }
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_AUTO_PICUS);
+ }
+
+ ret = btmtk_parse_bt_cfg_file(BT_PICUS_TO_HOST, text, searchcontent);
+ if (ret) {
+ btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_picus_to_host);
+ BTMTK_INFO("%s: bt_cfg_content->support_picus_to_host = %d", __func__,
+ bt_cfg_content->support_picus_to_host);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_PICUS_TO_HOST);
+ }
+
+ ret = btmtk_parse_bt_cfg_file(BT_SINGLE_SKU, text, searchcontent);
+ if (ret) {
+ btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_bt_single_sku);
+ BTMTK_INFO("%s: bt_cfg_content->support_bt_single_sku = %d", __func__,
+ bt_cfg_content->support_bt_single_sku);
+ } else {
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_SINGLE_SKU);
+ }
+
+ ret = btmtk_load_fw_cfg_setting(BT_PHASE1_WMT_CMD, bt_cfg_content->phase1_wmt_cmd,
+ PHASE1_WMT_CMD_COUNT, searchcontent, FW_CFG_INX_LEN_3);
+ if (ret)
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_PHASE1_WMT_CMD);
+
+ ret = btmtk_load_fw_cfg_setting(BT_VENDOR_CMD, bt_cfg_content->vendor_cmd,
+ VENDOR_CMD_COUNT, searchcontent, FW_CFG_INX_LEN_3);
+ if (ret)
+ BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_VENDOR_CMD);
+
+ /* release setting file memory */
+ if (bdev) {
+ kfree(bdev->setting_file);
+ bdev->setting_file = NULL;
+ }
+ return ret;
+}
+
+bool btmtk_load_bt_cfg(char *cfg_name, struct device *dev, struct btmtk_dev *bdev)
+{
+ bool err = false;
+ u32 code_len = 0;
+
+ if (btmtk_load_code_from_setting_files(cfg_name, dev, &code_len, bdev)) {
+ BTMTK_ERR("btmtk_usb_load_code_from_setting_files failed!!");
+ goto exit;
+ }
+
+ err = btmtk_load_bt_cfg_item(&bdev->bt_cfg, bdev->setting_file, bdev);
+ if (err)
+ BTMTK_ERR("btmtk_usb_load_bt_cfg_item error return %d", err);
+
+exit:
+ return err;
+}
+
+#if ENABLESTP
+static int btmtk_send_set_stp_cmd(struct btmtk_dev *bdev)
+{
+ u8 cmd[] = { 0x01, 0x6F, 0xFC, 0x09, 0x01, 0x04, 0x05, 0x00, 0x03, 0x11, 0x0E, 0x00, 0x00};
+ /* To-Do, for event check */
+ u8 event[] = { 0x04, 0xE4, 0x06, 0x02, 0x04, 0x02, 0x00, 0x00, 0x03};
+ int ret = 0;
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_CMD_FROM_DRV);
+
+ BTMTK_INFO("%s done", __func__);
+ return ret;
+}
+
+static int btmtk_send_set_stp1_cmd(struct btmtk_dev *bdev)
+{
+ u8 cmd[] = {0x01, 0x6F, 0xFC, 0x0C, 0x01, 0x08, 0x08, 0x00,
+ 0x02, 0x01, 0x00, 0x01, 0x08, 0x00, 0x00, 0x80};
+ /* To-Do, for event check */
+ u8 event[] = {0x04, 0xE4, 0x10, 0x02, 0x08,
+ 0x0C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08,
+ 0x00, 0x00, 0x80, 0x63, 0x76, 0x00, 0x00};
+ int ret = 0;
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_CMD_FROM_DRV);
+
+ BTMTK_INFO("%s done", __func__);
+ return ret;
+}
+#endif
+
+static int btmtk_send_hci_tci_set_sleep_cmd_766x(struct btmtk_dev *bdev)
+{
+ u8 cmd[] = { 0x01, 0x7A, 0xFC, 0x07, 0x05, 0x40, 0x06, 0x40, 0x06, 0x00, 0x00 };
+ /* To-Do, for event check */
+ u8 event[] = { 0x04, 0x0E, 0x04, 0x01, 0x7A, 0xFC, 0x00 };
+ int ret = -1;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ return ret;
+ }
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_PKT_FROM_HOST);
+
+ BTMTK_INFO("%s done", __func__);
+
+ return ret;
+}
+
+int btmtk_cap_init(struct btmtk_dev *bdev)
+{
+ if (!bdev) {
+ BTMTK_ERR("%s, bdev is NULL!", __func__);
+ return -1;
+ }
+ /* Todo read wifi fw version
+ * int wifi_fw_ver;
+
+ * btmtk_cif_write_register(bdev, 0x7C4001C4, 0x00008800);
+ * btmtk_cif_read_register(bdev, 0x7c4f0004, &wifi_fw_ver);
+ * BTMTK_ERR("wifi fw_ver = %04X", wifi_fw_ver);
+ */
+
+ main_info.hif_hook.reg_read(bdev, CHIP_ID, &bdev->chip_id);
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) {
+ main_info.hif_hook.reg_read(bdev, FLAVOR, &bdev->flavor);
+ main_info.hif_hook.reg_read(bdev, FW_VERSION, &bdev->fw_version);
+ } else {
+ BTMTK_ERR("Unknown Mediatek device(%04X)\n", bdev->chip_id);
+ return -1;
+ }
+
+ BTMTK_INFO("%s: Chip ID = 0x%x", __func__, bdev->chip_id);
+ BTMTK_INFO("%s: flavor = 0x%x", __func__, bdev->flavor);
+ BTMTK_INFO("%s: FW Ver = 0x%x", __func__, bdev->fw_version);
+
+ memset(bdev->rom_patch_bin_file_name, 0, MAX_BIN_FILE_NAME_LEN);
+ if ((bdev->fw_version & 0xff) == 0xff) {
+ BTMTK_ERR("%s: failed, wrong FW version : 0x%x !", __func__, bdev->fw_version);
+ return -1;
+ }
+
+ /* Bin filename format : "BT_RAM_CODE_MT%04x_%x_%x_hdr.bin"
+ * $$$$ : chip id
+ * % : fw version & 0xFF + 1 (in HEX)
+ */
+ bdev->flavor = (bdev->flavor & 0x00000080) >> 7;
+ BTMTK_INFO("%s: flavor1 = 0x%x", __func__, bdev->flavor);
+
+ /* if flavor equals 1, it represent 7920, else it represent 7921 */
+ if (bdev->flavor)
+ snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
+ "BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
+ bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
+ else
+ snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
+ "BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
+ bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
+
+ BTMTK_INFO("%s: rom patch file name is %s", __func__, bdev->rom_patch_bin_file_name);
+
+ if (is_mt7663(bdev->chip_id)) {
+ memcpy(bdev->woble_setting_file_name, WOBLE_SETTING_FILE_NAME_7663,
+ sizeof(WOBLE_SETTING_FILE_NAME_7663));
+ BTMTK_INFO("%s: woble setting file name is %s",
+ __func__, WOBLE_SETTING_FILE_NAME_7663);
+ }
+
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) {
+ memcpy(bdev->woble_setting_file_name, WOBLE_SETTING_FILE_NAME_7961,
+ sizeof(WOBLE_SETTING_FILE_NAME_7961));
+ BTMTK_INFO("%s: woble setting file name is %s",
+ __func__, WOBLE_SETTING_FILE_NAME_7961);
+ }
+
+ memcpy(bdev->bt_cfg_file_name, BT_CFG_NAME, sizeof(BT_CFG_NAME));
+ memset(bdev->bdaddr, 0, BD_ADDRESS_SIZE);
+ return 0;
+}
+
+static int btmtk_send_vendor_cfg(struct btmtk_dev *bdev)
+{
+ int ret = 0;
+ int index = 0;
+ u8 event[2] = { 0x04, 0x0E };
+
+ BTMTK_INFO("%s enter", __func__);
+
+ for (index = 0; index < VENDOR_CMD_COUNT; index++) {
+ if (bdev->bt_cfg.vendor_cmd[index].content &&
+ bdev->bt_cfg.vendor_cmd[index].length) {
+ ret = btmtk_main_send_cmd(bdev, bdev->bt_cfg.vendor_cmd[index].content,
+ bdev->bt_cfg.vendor_cmd[index].length,
+ event, sizeof(event),
+ 0, 0, BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0) {
+ BTMTK_ERR("%s: Send vendor cmd failed(%d)! Index: %d",
+ __func__, ret, index);
+ goto exit;
+ }
+
+ BTMTK_INFO_RAW(bdev->bt_cfg.vendor_cmd[index].content,
+ bdev->bt_cfg.vendor_cmd[index].length, "send vendor cmd");
+ }
+ }
+
+exit:
+ BTMTK_INFO("%s exit", __func__);
+ return ret;
+}
+
+static int btmtk_send_phase1_wmt_cfg(struct btmtk_dev *bdev)
+{
+ int ret = 0;
+ int index = 0;
+ u8 event[2] = { 0x04, 0xE4 };
+
+ BTMTK_INFO("%s", __func__);
+
+ for (index = 0; index < PHASE1_WMT_CMD_COUNT; index++) {
+ if (bdev->bt_cfg.phase1_wmt_cmd[index].content &&
+ bdev->bt_cfg.phase1_wmt_cmd[index].length) {
+ ret = btmtk_main_send_cmd(bdev, bdev->bt_cfg.phase1_wmt_cmd[index].content,
+ bdev->bt_cfg.phase1_wmt_cmd[index].length,
+ event, sizeof(event),
+ 20, 20, BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_ERR("%s: Send phase1 wmt cmd failed(%d)! Index: %d",
+ __func__, ret, index);
+ goto exit;
+ }
+
+ BTMTK_INFO_RAW(bdev->bt_cfg.phase1_wmt_cmd[index].content,
+ bdev->bt_cfg.phase1_wmt_cmd[index].length, "send wmt cmd");
+ }
+ }
+
+exit:
+ BTMTK_INFO("%s exit", __func__);
+ return ret;
+}
+
+int btmtk_send_init_cmds(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ goto exit;
+ }
+
+ BTMTK_INFO("%s", __func__);
+
+#if ENABLESTP
+ btmtk_send_set_stp_cmd(bdev);
+ btmtk_send_set_stp1_cmd(bdev);
+#endif
+ ret = btmtk_calibration_flow(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, btmtk_calibration_flow failed!", __func__);
+ goto exit;
+ }
+ ret = btmtk_send_wmt_power_on_cmd(bdev);
+ if (ret < 0) {
+ if (bdev->power_state != BTMTK_DONGLE_STATE_POWER_ON) {
+ BTMTK_ERR("%s, btmtk_send_wmt_power_on_cmd failed!", __func__);
+ if (main_info.reset_stack_flag == HW_ERR_NONE)
+ main_info.reset_stack_flag = HW_ERR_CODE_POWER_ON;
+ /* TODO */
+ /* btmtk_usb_toggle_rst_pin(); */
+ }
+ goto exit;
+ }
+
+ ret = btmtk_send_phase1_wmt_cfg(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("btmtk_send_wmt_cfg failed");
+ btmtk_send_assert_cmd(bdev);
+ goto exit;
+ }
+
+ if (bdev->bt_cfg.support_auto_picus == true) {
+ if (btmtk_picus_enable(bdev) < 0) {
+ BTMTK_ERR("send picus filter param failed");
+ btmtk_send_assert_cmd(bdev);
+ ret = -1;
+ goto exit;
+ }
+ }
+
+ ret = btmtk_send_vendor_cfg(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("btmtk_send_vendor_cfg failed");
+ btmtk_send_assert_cmd(bdev);
+ goto exit;
+ }
+
+ if (is_mt7663(bdev->chip_id))
+ ret = btmtk_send_hci_tci_set_sleep_cmd_766x(bdev);
+
+exit:
+ return ret;
+}
+
+
+int btmtk_send_deinit_cmds(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ return ret;
+ }
+
+ BTMTK_INFO("%s", __func__);
+
+ if (bdev->bt_cfg.support_auto_picus == true) {
+ if (btmtk_picus_disable(bdev) < 0) {
+ BTMTK_ERR("send picus filter param failed");
+ btmtk_send_assert_cmd(bdev);
+ return -1;
+ }
+ }
+
+ ret = btmtk_send_wmt_power_off_cmd(bdev);
+ if (bdev->power_state != BTMTK_DONGLE_STATE_POWER_OFF) {
+ BTMTK_WARN("Power off failed, reset it");
+ if (main_info.reset_stack_flag == HW_ERR_NONE)
+ main_info.reset_stack_flag = HW_ERR_CODE_POWER_OFF;
+ btmtk_send_assert_cmd(bdev);
+ }
+
+ return ret;
+}
+
+int btmtk_send_assert_cmd(struct btmtk_dev *bdev)
+{
+ int ret = 0;
+ int state;
+ u8 buf[] = { 0x01, 0x6F, 0xFC, 0x05, 0x01, 0x02, 0x01, 0x00, 0x08 };
+ struct sk_buff *skb = NULL;
+
+ if (!bdev) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state == BTMTK_STATE_FW_DUMP) {
+ BTMTK_WARN("%s: FW dumping already!!!", __func__);
+ return ret;
+ }
+
+ BTMTK_INFO("%s: send assert cmd", __func__);
+
+ skb = alloc_skb(sizeof(buf) + BT_SKB_RESERVE, GFP_ATOMIC);
+ if (!skb) {
+ BTMTK_ERR("%s allocate skb failed!!", __func__);
+ goto exit;
+ }
+ bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ memcpy(skb->data, buf, sizeof(buf));
+ skb->len = sizeof(buf);
+
+ ret = main_info.hif_hook.send_cmd(bdev, skb, 100, 20, (int)BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0)
+ BTMTK_ERR("%s failed!!", __func__);
+ else
+ BTMTK_INFO("%s: OK", __func__);
+
+exit:
+ return ret;
+}
+
+static int btmtk_send_txpower_cmd(struct btmtk_dev *bdev)
+{
+ /**
+ * TCI Set TX Power Command
+ * 01 2C FC 0C QQ 00 00 00 XX YY ZZ GG AA BB CC DD
+ * QQ: EDR init TX power dbm // the value is equal to EDR MAX
+ * XX: BLE TX power dbm
+ * YY: EDR MAX TX power dbm
+ * ZZ: Enable LV9
+ * GG: 3db diff mode
+ * AA: [5:4] Indicator // [5] 1: command send to BT1, [4] 1: command send to BT0
+ * [3:0] Resolution // 0: 1dBm, 1: 0.5dBm, 2: 0.25dBm
+ * BB: BLE 2M
+ * CC: BLE S2
+ * DD: BLE S8
+ */
+
+ u8 cmd[] = { 0x01, 0x2C, 0xFC, 0x0C, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ u8 event[] = { 0x04, 0x0E, 0x04, 0x01, 0x2C, 0xFC, 0x00 };
+ int ret = 0;
+
+ cmd[4] = (u8)main_info.PWS.EDR_Max;
+ cmd[8] = (u8)main_info.PWS.BLE_1M;
+ cmd[9] = (u8)main_info.PWS.EDR_Max;
+ cmd[10] = (u8)main_info.PWS.LV9;
+ cmd[11] = (u8)main_info.PWS.DM;
+ cmd[12] = (u8)main_info.PWS.IR;
+ cmd[13] = (u8)main_info.PWS.BLE_2M;
+ cmd[14] = (u8)main_info.PWS.BLE_LR_S2;
+ cmd[15] = (u8)main_info.PWS.BLE_LR_S8;
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), 0, 0,
+ BTMTK_TX_CMD_FROM_DRV);
+
+ if (ret < 0)
+ BTMTK_ERR("%s failed!!", __func__);
+ else
+ BTMTK_INFO("%s: OK", __func__);
+
+ return ret;
+}
+
+static int btmtk_set_power_value(char *str, int resolution, int is_edr)
+{
+ int power = ERR_PWR, integer = 0, decimal = 0, ret;
+
+ if (resolution == RES_DOT_25) {
+ /* XX.YY => XX.YY/0.25 = XX*4 + YY/25 */
+ if (strstr(str, ".")) {
+ ret = sscanf(str, "%d.%d", &integer, &decimal);
+ if (ret != 2)
+ return ERR_PWR;
+ if (decimal != 25 && decimal != 75 && decimal != 5 && decimal != 50)
+ return ERR_PWR;
+ if (decimal == 5)
+ decimal = 50;
+ if (integer >= 0)
+ power = integer * 4 + decimal / 25;
+ else
+ power = integer * 4 - decimal / 25;
+ } else {
+ kstrtoint(str, 0, &integer);
+ power = integer * 4;
+ }
+
+ BTMTK_DBG("%s: power = %d", __func__, power);
+
+ if (is_edr) {
+ if (power > EDR_MAX_R2 || power < EDR_MIN_R2)
+ return ERR_PWR;
+ if (power >= EDR_MIN_LV9_R2)
+ main_info.PWS.LV9 = 1;
+ } else if (!is_edr && (power > BLE_MAX_R2 || power < BLE_MIN_R2)) {
+ return ERR_PWR;
+ }
+ } else if (resolution == RES_DOT_5) {
+ /* XX.YY => XX.YY/0.5 = XX*2 + YY/5 */
+ if (strstr(str, ".")) {
+ ret = sscanf(str, "%d.%d", &integer, &decimal);
+ if (ret != 2)
+ return ERR_PWR;
+ if (decimal != 5)
+ return ERR_PWR;
+ if (integer >= 0)
+ power = integer * 2 + decimal / 5;
+ if (integer < 0)
+ power = integer * 2 - decimal / 5;
+ } else {
+ kstrtoint(str, 0, &integer);
+ power = integer * 2;
+ }
+
+ BTMTK_DBG("%s: power = %d", __func__, power);
+
+ if (is_edr) {
+ if (power > EDR_MAX_R1 || power < EDR_MIN_R1)
+ return ERR_PWR;
+ if (power >= EDR_MIN_LV9_R1)
+ main_info.PWS.LV9 = 1;
+ } else if (!is_edr && (power > BLE_MAX_R1 || power < BLE_MIN_R1)) {
+ return ERR_PWR;
+ }
+ } else if (resolution == RES_1) {
+ kstrtoint(str, 0, &power);
+ BTMTK_DBG("%s: power = %d", __func__, power);
+
+ if (is_edr) {
+ if (power > EDR_MAX || power < EDR_MIN)
+ return ERR_PWR;
+ if (power >= EDR_MIN_LV9)
+ main_info.PWS.LV9 = 1;
+ } else if (!is_edr && (power > BLE_MAX || power < BLE_MIN)) {
+ return ERR_PWR;
+ }
+ }
+
+ return power;
+}
+
+static int btmtk_check_power_resolution(char *str)
+{
+ if (!str)
+ return -1;
+ if (strstr(str, ".25") || strstr(str, ".75"))
+ return RES_DOT_25;
+ if (strstr(str, ".5"))
+ return RES_DOT_5;
+ if (!strstr(str, ".") || strstr(str, ".0"))
+ return RES_1;
+ return -1;
+}
+
+static void btmtk_init_power_setting_struct(void)
+{
+ main_info.PWS.BLE_1M = 0;
+ main_info.PWS.EDR_Max = 0;
+ main_info.PWS.LV9 = 0;
+ main_info.PWS.DM = 0;
+ main_info.PWS.IR = 0;
+ main_info.PWS.BLE_2M = 0;
+ main_info.PWS.BLE_LR_S2 = 0;
+ main_info.PWS.BLE_LR_S8 = 0;
+}
+
+static int btmtk_parse_power_table(char *context)
+{
+ char *ptr = NULL;
+ int step = 0, temp;
+ int resolution;
+ int power;
+
+ if (!context) {
+ BTMTK_ERR("%s context is NULL", __func__);
+ return -1;
+ }
+
+ BTMTK_INFO("%s", __func__);
+ btmtk_init_power_setting_struct();
+
+ /* Send to BT0? BT1? */
+ if (strstr(context, "BT0")) {
+ BTMTK_INFO("Parse power for BT0");
+ main_info.PWS.IR |= 0x10;
+ context += strlen("[BT0]");
+ } else if (strstr(context, "BT1")) {
+ BTMTK_INFO("Parse power for BT1");
+ main_info.PWS.IR |= 0x20;
+ context += strlen("[BT1]");
+ } else {
+ BTMTK_ERR("%s BT indicator error", __func__);
+ return -1;
+ }
+
+ resolution = btmtk_check_power_resolution(context);
+ if (resolution == -1) {
+ BTMTK_ERR("Check resolution fail");
+ return -1;
+ }
+
+ main_info.PWS.IR |= resolution;
+ BTMTK_INFO("%s: resolution = %d", __func__, resolution);
+
+ while ((ptr = strsep(&context, ",")) != NULL) {
+ while (*ptr == '\t' || *ptr == ' ')
+ ptr++;
+
+ switch (step) {
+ /* BR_EDR_PWR_MODE */
+ case CHECK_SINGLE_SKU_PWR_MODE:
+ if (kstrtoint(ptr, 0, &temp) == 0) {
+ if (temp == 0 || temp == 1) {
+ main_info.PWS.DM = temp;
+ step++;
+ continue;
+ } else {
+ BTMTK_ERR("PWR MODE value wrong");
+ return -1;
+ }
+ } else {
+ BTMTK_ERR("Read PWR MODE Fail");
+ return -1;
+ }
+ break;
+ /* Parse EDR MAX */
+ case CHECK_SINGLE_SKU_EDR_MAX:
+ power = btmtk_set_power_value(ptr, resolution, 1);
+ if (power == ERR_PWR) {
+ BTMTK_ERR("EDR MAX value wrong");
+ return -1;
+ }
+ main_info.PWS.EDR_Max = power;
+ step++;
+ break;
+ /* Parse BLE Default */
+ case CHECK_SINGLE_SKU_BLE:
+ power = btmtk_set_power_value(ptr, resolution, 0);
+ if (power == ERR_PWR) {
+ BTMTK_ERR("BLE value wrong");
+ return -1;
+ }
+ main_info.PWS.BLE_1M = power;
+ step++;
+ break;
+ /* Parse BLE 2M */
+ case CHECK_SINGLE_SKU_BLE_2M:
+ power = btmtk_set_power_value(ptr, resolution, 0);
+ if (power == ERR_PWR) {
+ BTMTK_ERR("BLE 2M value wrong");
+ return -1;
+ }
+ main_info.PWS.BLE_2M = power;
+ step++;
+ break;
+ /* Parse BLE long range S2 */
+ case CHECK_SINGLE_SKU_BLE_LR_S2:
+ power = btmtk_set_power_value(ptr, resolution, 0);
+ if (power == ERR_PWR) {
+ BTMTK_ERR("BLE LR S2 value wrong");
+ return -1;
+ }
+ main_info.PWS.BLE_LR_S2 = power;
+ step++;
+ break;
+ /* Parse BLE long range S8 */
+ case CHECK_SINGLE_SKU_BLE_LR_S8:
+ power = btmtk_set_power_value(ptr, resolution, 0);
+ if (power == ERR_PWR) {
+ BTMTK_ERR("BLE LR S8 value wrong");
+ return -1;
+ }
+ main_info.PWS.BLE_LR_S8 = power;
+ step++;
+ break;
+ default:
+ BTMTK_ERR("%s step is wrong: %d", __func__, step);
+ break;
+ }
+ continue;
+ }
+
+ return step;
+}
+
+static void btmtk_send_txpower_cmd_to_all_interface(void)
+{
+ int i, ret;
+ struct btmtk_dev *bdev = NULL;
+
+ for (i = 0; i < btmtk_intf_num; i++) {
+ if (g_bdev[i]->hdev) {
+ bdev = g_bdev[i];
+ BTMTK_INFO("send to %d", i);
+ ret = btmtk_send_txpower_cmd(bdev);
+ if (ret < 0)
+ BTMTK_ERR("Device %d send txpower cmd fail", i);
+ }
+ }
+}
+
+static void btmtk_requset_country_cb(const struct firmware *fw, void *context)
+{
+ char *ptr, *data, *p_data = NULL;
+ char *country = NULL;
+ int ret = 0;
+ bool find_country = false;
+ bool read_next = false;
+
+ if (!fw) {
+ BTMTK_ERR("fw is NULL");
+ return;
+ }
+
+ BTMTK_INFO("%s request %s success", __func__, DEFAULT_COUNTRY_TABLE_NAME);
+ data = kzalloc(fw->size, GFP_KERNEL);
+ p_data = data;
+ if (!data) {
+ BTMTK_WARN("%s allocate memory fail (data)", __func__);
+ goto exit;
+ }
+
+ memcpy(data, fw->data, fw->size);
+ while ((ptr = strsep(&p_data, "\n")) != NULL) {
+ /* If the '#' in front of the line, ignore this line */
+ if (*ptr == '#')
+ continue;
+
+ /* Set power for BT1 */
+ if (read_next) {
+ if (strncmp(ptr, "[BT1]", 5) == 0) {
+ ret = btmtk_parse_power_table(ptr);
+ if (ret != CHECK_SINGLE_SKU_ALL) {
+ BTMTK_ERR("Parse power fail, ret = %d", ret);
+ break;
+ }
+
+ btmtk_send_txpower_cmd_to_all_interface();
+ } else {
+ BTMTK_INFO("No power data for BT1");
+ }
+ break;
+ }
+
+ if (find_country) {
+ ret = btmtk_parse_power_table(ptr);
+ /* Check if the next line has power value for BT1 */
+ read_next = true;
+ if (ret != CHECK_SINGLE_SKU_ALL) {
+ BTMTK_ERR("Parse power fail, ret = %d", ret);
+ continue;
+ }
+
+ btmtk_send_txpower_cmd_to_all_interface();
+ continue;
+ }
+
+ while ((country = strsep(&ptr, ",[]")) != NULL) {
+ if (strlen(country) != COUNTRY_CODE_LEN)
+ continue;
+ if (strcmp(country, main_info.PWS.country_code) == 0) {
+ find_country = true;
+ break;
+ }
+ }
+ }
+ kfree(data);
+
+ if (!find_country)
+ BTMTK_ERR("Can't find country in the table");
+
+exit:
+ release_firmware(fw);
+}
+
+static int btmtk_load_country_table(void)
+{
+ int err = 0;
+
+ err = request_firmware_nowait(THIS_MODULE, true, DEFAULT_COUNTRY_TABLE_NAME,
+ NULL, GFP_KERNEL, NULL, btmtk_requset_country_cb);
+
+ return err;
+}
+
+void btmtk_set_country_code_from_wifi(char *code)
+{
+ int i;
+ struct btmtk_dev *bdev = NULL;
+
+ if (!code)
+ return;
+
+ if (strlen(code) == COUNTRY_CODE_LEN) {
+ BTMTK_INFO("%s country code is %s", __func__, code);
+ memcpy(main_info.PWS.country_code, code, sizeof(main_info.PWS.country_code));
+ for (i = 0; i < btmtk_intf_num; i++) {
+ if (!g_bdev[i]->hdev) {
+ bdev = g_bdev[i];
+ if (bdev->bt_cfg.support_bt_single_sku) {
+ btmtk_load_country_table();
+ break;
+ }
+ }
+ }
+ } else {
+ BTMTK_INFO("%s country code is not valid", __func__);
+ }
+}
+EXPORT_SYMBOL_GPL(btmtk_set_country_code_from_wifi);
+
+int btmtk_fops_init(void)
+{
+ static int BT_majorfwlog;
+ dev_t dev_ID_fwlog = MKDEV(BT_majorfwlog, 0);
+ int ret = 0;
+ int cdev_err = 0;
+ int majorfwlog = 0;
+
+ BTMTK_INFO("%s: Start", __func__);
+
+ if (!g_fwlog) {
+ g_fwlog = kzalloc(sizeof(*g_fwlog), GFP_KERNEL);
+ if (!g_fwlog) {
+ BTMTK_ERR("%s: alloc memory fail (g_data)", __func__);
+ return -1;
+ }
+ }
+
+ BTMTK_INFO("%s: g_fwlog init", __func__);
+ spin_lock_init(&g_fwlog->fwlog_lock);
+ skb_queue_head_init(&g_fwlog->fwlog_queue);
+ init_waitqueue_head(&(g_fwlog->fw_log_inq));
+
+ ret = alloc_chrdev_region(&dev_ID_fwlog, 0, 1, BT_CHR_DEV);
+ if (ret) {
+ BT_ERR("%s: fail to allocate chrdev", __func__);
+ return ret;
+ }
+
+ BT_majorfwlog = majorfwlog = MAJOR(dev_ID_fwlog);
+
+ cdev_init(&g_fwlog->BT_cdevfwlog, &BT_fopsfwlog);
+ g_fwlog->BT_cdevfwlog.owner = THIS_MODULE;
+
+ cdev_err = cdev_add(&g_fwlog->BT_cdevfwlog, dev_ID_fwlog, 1);
+ if (cdev_err)
+ goto error;
+
+ g_fwlog->pBTClass = class_create(THIS_MODULE, BT_CHR_DEV);
+ if (IS_ERR(g_fwlog->pBTClass)) {
+ BT_ERR("%s: class create fail, error code(%ld)\n",
+ __func__, PTR_ERR(g_fwlog->pBTClass));
+ goto err1;
+ }
+
+ g_fwlog->pBTDevfwlog = device_create(g_fwlog->pBTClass,
+ NULL, dev_ID_fwlog, NULL, BT_DEV_NODE);
+ if (IS_ERR(g_fwlog->pBTDevfwlog)) {
+ BT_ERR("%s: device(stpbtfwlog) create fail, error code(%ld)",
+ __func__, PTR_ERR(g_fwlog->pBTDevfwlog));
+ goto error;
+ }
+ BT_INFO("%s: BT_majorfwlog %d, dev_ID_fwlog %d", __func__, BT_majorfwlog, dev_ID_fwlog);
+
+ g_fwlog->g_devIDfwlog = dev_ID_fwlog;
+
+ return 0;
+
+err1:
+ if (g_fwlog->pBTClass) {
+ class_destroy(g_fwlog->pBTClass);
+ g_fwlog->pBTClass = NULL;
+ }
+
+error:
+ if (cdev_err == 0)
+ cdev_del(&g_fwlog->BT_cdevfwlog);
+
+ if (ret == 0)
+ unregister_chrdev_region(dev_ID_fwlog, 1);
+
+ return -1;
+}
+
+int btmtk_fops_exit(void)
+{
+ dev_t devIDfwlog = g_fwlog->g_devIDfwlog;
+
+ BT_INFO("%s: Start\n", __func__);
+ if (g_fwlog->pBTDevfwlog) {
+ device_destroy(g_fwlog->pBTClass, devIDfwlog);
+ g_fwlog->pBTDevfwlog = NULL;
+ }
+
+ if (g_fwlog->pBTClass) {
+ class_destroy(g_fwlog->pBTClass);
+ g_fwlog->pBTClass = NULL;
+ }
+ BT_INFO("%s: pBTDevfwlog, pBTClass done\n", __func__);
+ cdev_del(&g_fwlog->BT_cdevfwlog);
+ unregister_chrdev_region(devIDfwlog, 1);
+ BT_INFO("%s: BT_chrdevfwlog driver removed.\n", __func__);
+ kfree(g_fwlog);
+
+ return 0;
+}
+
+/**
+ * Kernel HCI Interface Registration
+ */
+static int bt_flush(struct hci_dev *hdev)
+{
+ return 0;
+}
+
+static int bt_close(struct hci_dev *hdev)
+{
+ int ret = -1;
+ int state = BTMTK_STATE_INIT;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+ struct btmtk_dev *bdev = NULL;
+
+ if (!hdev) {
+ BTMTK_ERR("%s: invalid parameters!", __func__);
+ return ret;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is invalid!", __func__);
+ return ret;
+ }
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not allow close(%d)", __func__, fstate);
+ goto err;
+ }
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSING);
+
+ state = btmtk_get_chip_state(bdev);
+ if (state != BTMTK_STATE_WORKING && state != BTMTK_STATE_STANDBY) {
+ BTMTK_WARN("%s: not in working state and standby state(%d).", __func__, state);
+ goto exit;
+ }
+
+ BTMTK_INFO("%s, enter", __func__);
+
+ if (main_info.hif_hook.cif_mutex_lock)
+ main_info.hif_hook.cif_mutex_lock(bdev);
+
+#if CFG_SUPPORT_DVT
+ /* Don't send init cmd for DVT
+ * Such as Lowpower DVT
+ */
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
+#else
+ if (state != BTMTK_STATE_STANDBY) {
+ ret = btmtk_send_deinit_cmds(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, btmtk_send_deinit_cmds failed", __func__);
+ goto unlock;
+ }
+ }
+#endif /* CFG_SUPPORT_DVT */
+
+ /* Flush RX works */
+ flush_work(&bdev->rx_work);
+
+ /* Drop queues */
+ skb_queue_purge(&bdev->rx_q);
+
+ main_info.hif_hook.close(hdev);
+
+unlock:
+ if (main_info.hif_hook.cif_mutex_unlock)
+ main_info.hif_hook.cif_mutex_unlock(bdev);
+exit:
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSED);
+
+err:
+ main_info.reset_stack_flag = HW_ERR_NONE;
+
+ BTMTK_INFO("%s: end, reset_stack_flag = %d", __func__, main_info.reset_stack_flag);
+ return 0;
+}
+
+static int bt_open(struct hci_dev *hdev)
+{
+ int ret = -1;
+ int state = BTMTK_STATE_INIT;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+ struct btmtk_dev *bdev = NULL;
+ void (*rlm_get_alpha2)(char *code);
+ const char *wifi_func_name = "rlm_get_alpha2";
+ char alpha2[5];
+
+ BTMTK_INFO("%s: MTK BT Driver Version : %s", __func__, VERSION);
+
+ if (!hdev) {
+ BTMTK_ERR("%s: invalid parameters!", __func__);
+ return -EFAULT;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is invalid", __func__);
+ return -EFAULT;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state == BTMTK_STATE_INIT || state == BTMTK_STATE_DISCONNECT) {
+ ret = -EAGAIN;
+ goto failed;
+ }
+
+ if (state != BTMTK_STATE_WORKING && state != BTMTK_STATE_STANDBY) {
+ BTMTK_WARN("%s: not in working state and standby state(%d).", __func__, state);
+ ret = -ENODEV;
+ goto failed;
+ }
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate == BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops opened!", __func__);
+ ret = 0;
+ goto failed;
+ }
+
+ if (fstate == BTMTK_FOPS_STATE_CLOSING || fstate == BTMTK_FOPS_STATE_OPENING) {
+ BTMTK_WARN("%s: fops open/close is on-going !", __func__);
+ ret = -EAGAIN;
+ goto failed;
+ }
+
+ BTMTK_INFO("%s", __func__);
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_OPENING);
+ ret = main_info.hif_hook.open(hdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, cif_open failed", __func__);
+ goto failed;
+ }
+
+#if CFG_SUPPORT_DVT
+ /* Don't send init cmd for DVT
+ * Such as Lowpower DVT
+ */
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_ON;
+#else
+ ret = btmtk_send_init_cmds(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, btmtk_send_init_cmds failed", __func__);
+ goto failed;
+ }
+#endif /* CFG_SUPPORT_DVT */
+
+ if (main_info.hif_hook.open_done)
+ main_info.hif_hook.open_done(bdev);
+
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_OPENED);
+ main_info.reset_stack_flag = HW_ERR_NONE;
+
+ if (bdev->bt_cfg.support_bt_single_sku) {
+ rlm_get_alpha2 = (void *)kallsyms_lookup_name(wifi_func_name);
+
+ if (rlm_get_alpha2) {
+ rlm_get_alpha2(alpha2);
+ if (strlen(alpha2) == COUNTRY_CODE_LEN) {
+ BTMTK_INFO("Wifi set country code %s", alpha2);
+ memcpy(main_info.PWS.country_code, alpha2,
+ sizeof(main_info.PWS.country_code));
+ } else {
+ BTMTK_ERR("Country code length is wrong");
+ }
+ } else {
+ BTMTK_INFO("Wifi didn't set country code");
+ }
+
+ if (strcmp(main_info.PWS.country_code, "") != 0)
+ btmtk_load_country_table();
+ }
+
+ return 0;
+
+failed:
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSED);
+
+ return ret;
+}
+
+static int bt_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ int ret = -1;
+ int state = BTMTK_STATE_INIT;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+ /* parsing commands */
+ u8 fw_assert_cmd[] = { 0x01, 0x5B, 0xFD, 0x00 };
+ u8 reset_cmd[] = { 0x01, 0x03, 0x0C, 0x00 };
+ struct btmtk_dev *bdev = NULL;
+
+ if (!hdev || !skb) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -ENODEV;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s, bdev is invalid!", __func__);
+ return -ENODEV;
+ }
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not open yet(%d)!", __func__, fstate);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state != BTMTK_STATE_WORKING) {
+ BTMTK_WARN("%s: chip state is %d.", __func__, state);
+ if (state == BTMTK_STATE_DISCONNECT) {
+ ret = -ENODEV;
+ } else {
+ msleep(3000);
+ ret = -EAGAIN;
+ }
+ goto exit;
+ }
+
+ if (bdev->power_state == BTMTK_DONGLE_STATE_POWER_OFF) {
+ BTMTK_WARN("%s: dongle state already power off, do not write", __func__);
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ if (main_info.reset_stack_flag) {
+ BTMTK_WARN("%s: reset_stack_flag (%d)!", __func__, main_info.reset_stack_flag);
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ btmtk_dispatch_data_bluetooth_kpi(hdev, skb->data, skb->len, hci_skb_pkt_type(skb));
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
+#if ENABLESTP
+ skb = mtk_add_stp(bdev, skb);
+#endif
+
+ /* For Ble ISO packet size */
+ if (memcmp(skb->data, READ_ISO_PACKET_SIZE_CMD, sizeof(READ_ISO_PACKET_SIZE_CMD)) == 0) {
+ bdev->iso_threshold = skb->data[sizeof(READ_ISO_PACKET_SIZE_CMD)] +
+ (skb->data[sizeof(READ_ISO_PACKET_SIZE_CMD) + 1] << 8);
+ BTMTK_INFO("%s: Ble iso pkt size is %d", __func__, bdev->iso_threshold);
+ }
+
+ if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) {
+ /* save hci cmd pkt for debug */
+ btmtk_hci_snoop_save_cmd(skb->len, skb->data);
+ if (skb->len == sizeof(fw_assert_cmd) &&
+ !memcmp(skb->data, fw_assert_cmd, sizeof(fw_assert_cmd))) {
+ BTMTK_INFO("%s: Dongle FW Assert Triggered by BT Stack!", __func__);
+ btmtk_hci_snoop_print_to_log();
+ } else if (skb->len == sizeof(reset_cmd) &&
+ !memcmp(skb->data, reset_cmd, sizeof(reset_cmd)))
+ BTMTK_INFO("%s: got command: 0x03 0C 00 (HCI_RESET)", __func__);
+ }
+
+ ret = main_info.hif_hook.send_cmd(bdev, skb, 0, 0, (int)BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0)
+ BTMTK_ERR("%s failed!!", __func__);
+exit:
+ return ret;
+}
+
+static int bt_setup(struct hci_dev *hdev)
+{
+ BTMTK_INFO("%s", __func__);
+ return 0;
+}
+
+void btmtk_reg_hif_hook(struct hif_hook_ptr *hook)
+{
+ memcpy(&main_info.hif_hook, hook, sizeof(struct hif_hook_ptr));
+}
+
+void btmtk_reset_waker(struct work_struct *work)
+{
+ struct btmtk_dev *bdev = container_of(work, struct btmtk_dev, reset_waker);
+ struct btmtk_cif_state *cif_state = NULL;
+ int cif_event = 0, err = 0;
+
+ cif_event = HIF_EVENT_SUBSYS_RESET;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s priv setting is NULL", __func__);
+ goto Finish;
+ }
+
+ while (!bdev->bt_cfg.support_dongle_reset) {
+ BTMTK_ERR("%s chip_reset is not support", __func__);
+ msleep(2000);
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ BTMTK_INFO("%s: Receive a byte (0xFF)", __func__);
+ /* read interrupt EP15 CR */
+
+ bdev->subsys_reset = 1;
+ bdev->sco_num = 0;
+
+ if (whole_reset_flag == 0) {
+ if (main_info.hif_hook.subsys_reset)
+ err = main_info.hif_hook.subsys_reset(bdev);
+ else
+ BTMTK_INFO("%s: Not support subsys chip reset", __func__);
+ } else {
+ err = -1;
+ BTMTK_INFO("%s: whole_reset_flag is %d", __func__, whole_reset_flag);
+ }
+
+ if (err) {
+ /* L0.5 reset failed, do whole chip reset */
+ /* We will add support dongle reset flag, reading from bt.cfg */
+ bdev->subsys_reset = 0;
+ /* TODO: need to confirm with usb host when suspend fail, to do chip reset,
+ * because usb3.0 need to toggle reset pin after hub_event unfreeze,
+ * otherwise, it will not occur disconnect on Capy Platform. When Mstar
+ * chip has usb3.0 port, we will use Mstar platform to do comparison
+ * test, then found the final solution.
+ */
+ //msleep(2000);
+ if (main_info.hif_hook.whole_reset)
+ main_info.hif_hook.whole_reset(bdev);
+ else
+ BTMTK_INFO("%s: Not support whole chip reset", __func__);
+ whole_reset_flag = 0;
+ goto Finish;
+ }
+
+ main_info.reset_stack_flag = HW_ERR_CODE_CHIP_RESET;
+ bdev->subsys_reset = 0;
+
+ err = btmtk_cap_init(bdev);
+ if (err < 0) {
+ BTMTK_ERR("btmtk init failed!");
+ goto Finish;
+ }
+
+ err = btmtk_load_rom_patch(bdev);
+ if (err < 0) {
+ BTMTK_ERR("btmtk load rom patch failed!");
+ goto Finish;
+ }
+ btmtk_send_hw_err_to_host(bdev);
+ btmtk_woble_wake_unlock(bdev);
+
+Finish:
+ main_info.hif_hook.chip_reset_notify(bdev);
+
+ /* Set End/Error state */
+ if (err < 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+}
+
+static void btmtk_rx_work(struct work_struct *work)
+{
+ int err = 0, skip_pkt = 0;
+ struct btmtk_dev *bdev = container_of(work, struct btmtk_dev, rx_work);
+ struct sk_buff *skb;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+ int state = 0;
+
+ BTMTK_DBG("%s enter", __func__);
+
+ while ((skb = skb_dequeue(&bdev->rx_q))) {
+ /* BTMTK_DBG_RAW(skb->data, skb->len, "%s, recv evt", __func__); */
+ skip_pkt = btmtk_dispatch_pkt(bdev->hdev, skb);
+ if (skip_pkt != 0) {
+ /* kfree_skb should be moved to btmtk_dispach_pkt */
+ kfree_skb(skb);
+ continue;
+ }
+
+ if (hci_skb_pkt_type(skb) == HCI_EVENT_PKT) {
+ /* save hci evt pkt for debug */
+ if (skb->data[0] == 0x3E)
+ btmtk_hci_snoop_save_adv_event(skb->len, skb->data);
+ else
+ btmtk_hci_snoop_save_event(skb->len, skb->data);
+
+ if (main_info.hif_hook.event_filter(bdev, skb)) {
+ /* Drop by driver, don't send to stack */
+ kfree_skb(skb);
+ continue;
+ }
+ } else if (hci_skb_pkt_type(skb) == HCI_ACLDATA_PKT) {
+ /* save hci acl pkt for debug, not include picus log and coredump*/
+ btmtk_hci_snoop_save_acl(skb->len, skb->data);
+ }
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ /* BT close case, drop by driver, don't send to stack */
+ kfree_skb(skb);
+ continue;
+ }
+
+ /* for bluetooth kpi */
+ btmtk_dispatch_data_bluetooth_kpi(bdev->hdev, skb->data,
+ skb->len, hci_skb_pkt_type(skb));
+
+ /* Can't send to stack when is not WORKING */
+ state = btmtk_get_chip_state(bdev);
+ if (state != BTMTK_STATE_WORKING) {
+ kfree_skb(skb);
+ continue;
+ }
+
+ err = hci_recv_frame(bdev->hdev, skb);
+ if (err < 0) {
+ if (err != -ENXIO)
+ BTMTK_ERR("%s, err = %d", __func__, err);
+ return;
+ }
+ }
+}
+
+static irqreturn_t btmtk_woble_isr(int irq, struct btmtk_dev *bdev)
+{
+#define WAIT_POWERKEY_TIMEOUT 5000
+ BTMTK_DBG("%s begin", __func__);
+ disable_irq_nosync(bdev->wobt_irq);
+ atomic_dec(&(bdev->irq_enable_count));
+ BTMTK_INFO("disable BT IRQ, call wake lock");
+ __pm_wakeup_event(main_info.eint_ws, WAIT_POWERKEY_TIMEOUT);
+
+ input_report_key(bdev->WoBLEInputDev, KEY_WAKEUP, 1);
+ input_sync(bdev->WoBLEInputDev);
+ input_report_key(bdev->WoBLEInputDev, KEY_WAKEUP, 0);
+ input_sync(bdev->WoBLEInputDev);
+ BTMTK_DBG("%s end", __func__);
+ return IRQ_HANDLED;
+}
+
+static int btmtk_RegisterBTIrq(struct btmtk_dev *bdev)
+{
+ struct device_node *eint_node = NULL;
+ int interrupts[2];
+
+ BTMTK_DBG("%s begin", __func__);
+ eint_node = of_find_compatible_node(NULL, NULL, "mediatek,woble_eint");
+ if (eint_node) {
+ BTMTK_INFO("Get woble_eint compatible node");
+ bdev->wobt_irq = irq_of_parse_and_map(eint_node, 0);
+ BTMTK_INFO("woble_irq number:%d", bdev->wobt_irq);
+ if (bdev->wobt_irq) {
+ of_property_read_u32_array(eint_node, "interrupts",
+ interrupts, ARRAY_SIZE(interrupts));
+ bdev->wobt_irqlevel = interrupts[1];
+ if (request_irq(bdev->wobt_irq, (void *)btmtk_woble_isr,
+ bdev->wobt_irqlevel, "woble-eint", bdev))
+ BTMTK_INFO("WOBTIRQ LINE NOT AVAILABLE!!");
+ else {
+ BTMTK_INFO("disable BT IRQ");
+ disable_irq_nosync(bdev->wobt_irq);
+ }
+
+ } else
+ BTMTK_INFO("can't find woble_eint irq");
+
+ } else {
+ bdev->wobt_irq = 0;
+ BTMTK_INFO("can't find woble_eint compatible node");
+ }
+
+ BTMTK_DBG("%s end", __func__);
+ return 0;
+}
+
+static int btmtk_woble_input_init(struct btmtk_dev *bdev)
+{
+ int ret = 0;
+
+ bdev->WoBLEInputDev = input_allocate_device();
+ if (IS_ERR(bdev->WoBLEInputDev)) {
+ BTMTK_ERR("input_allocate_device error");
+ return -ENOMEM;
+ }
+
+ bdev->WoBLEInputDev->name = "WOBLE_INPUT_DEVICE";
+ bdev->WoBLEInputDev->id.bustype = BUS_HOST;
+ bdev->WoBLEInputDev->id.vendor = 0x0002;
+ bdev->WoBLEInputDev->id.product = 0x0002;
+ bdev->WoBLEInputDev->id.version = 0x0002;
+
+ __set_bit(EV_KEY, bdev->WoBLEInputDev->evbit);
+ __set_bit(KEY_WAKEUP, bdev->WoBLEInputDev->keybit);
+
+ ret = input_register_device(bdev->WoBLEInputDev);
+ if (ret < 0) {
+ input_free_device(bdev->WoBLEInputDev);
+ BTMTK_ERR("input_register_device %d", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+void btmtk_free_hci_device(struct btmtk_dev *bdev, int hci_bus_type)
+{
+ int fstate = BTMTK_FOPS_STATE_INIT;
+
+ if (!bdev)
+ return;
+
+ /* Flush RX works */
+ flush_work(&bdev->rx_work);
+
+ /* Drop queues */
+ skb_queue_purge(&bdev->rx_q);
+ destroy_workqueue(bdev->workqueue);
+
+ BTMTK_INFO("%s", __func__);
+
+ if (bdev->hdev)
+ hci_free_dev(bdev->hdev);
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate == BTMTK_FOPS_STATE_OPENED || fstate == BTMTK_FOPS_STATE_CLOSING) {
+ BTMTK_WARN("%s: fstate = %d , set reset_stack_flag", __func__, fstate);
+ if (main_info.reset_stack_flag == HW_ERR_NONE)
+ main_info.reset_stack_flag = HW_ERR_CODE_USB_DISC;
+ }
+
+ bdev->chip_reset = 0;
+ BTMTK_INFO("%s done", __func__);
+}
+
+int btmtk_allocate_hci_device(struct btmtk_dev *bdev, int hci_bus_type)
+{
+ struct hci_dev *hdev;
+ int err = 0;
+
+ if (!bdev) {
+ BTMTK_ERR("%s, bdev is NULL!", __func__);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ BTMTK_INFO("%s", __func__);
+ /* Add hci device */
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BTMTK_ERR("%s, hdev is NULL!", __func__);
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ hdev->bus = hci_bus_type;
+ hci_set_drvdata(hdev, bdev);
+
+ /* HCI_PRIMARY = 0x00 */
+ hdev->dev_type = 0x00;
+
+ bdev->hdev = hdev;
+
+ /* register hci callback */
+ hdev->open = bt_open;
+ hdev->close = bt_close;
+ hdev->flush = bt_flush;
+ hdev->send = bt_send_frame;
+ hdev->setup = bt_setup;
+
+ INIT_WORK(&bdev->rx_work, btmtk_rx_work);
+
+ init_waitqueue_head(&bdev->p_wait_event_q);
+
+ skb_queue_head_init(&bdev->rx_q);
+
+ bdev->workqueue = alloc_workqueue("BTMTK_RX_WQ", WQ_HIGHPRI | WQ_UNBOUND |
+ WQ_MEM_RECLAIM, 1);
+ if (!bdev->workqueue) {
+ BTMTK_ERR("%s, bdev->workqueue is NULL!", __func__);
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ BTMTK_INFO("%s done", __func__);
+
+exit:
+ return err;
+}
+
+int btmtk_register_hci_device(struct btmtk_dev *bdev)
+{
+ struct hci_dev *hdev;
+ int err = 0;
+
+ hdev = bdev->hdev;
+
+ err = hci_register_dev(hdev);
+ /* After hci_register_dev completed
+ * It will set dev_flags to HCI_SETUP
+ * That cause vendor_lib create socket failed
+ */
+ if (err < 0) {
+ BTMTK_INFO("%s can't register", __func__);
+ hci_free_dev(hdev);
+ goto exit;
+ }
+
+exit:
+ return err;
+}
+
+int btmtk_deregister_hci_device(struct btmtk_dev *bdev)
+{
+ int err = 0;
+
+ if (bdev && bdev->hdev)
+ hci_unregister_dev(bdev->hdev);
+
+ return err;
+}
+
+static int btmtk_main_allocate_memory(struct btmtk_dev *bdev)
+{
+ BTMTK_INFO("%s", __func__);
+
+ if (bdev->rom_patch_bin_file_name == NULL) {
+ bdev->rom_patch_bin_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
+ if (!bdev->rom_patch_bin_file_name) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->rom_patch_bin_file_name)",
+ __func__);
+ return -1;
+ }
+ }
+
+ if (bdev->io_buf == NULL) {
+ bdev->io_buf = kzalloc(IO_BUF_SIZE, GFP_KERNEL);
+ if (!bdev->io_buf) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->io_buf)", __func__);
+ return -1;
+ }
+ }
+
+ if (bdev->woble_setting_file_name == NULL) {
+ bdev->woble_setting_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
+ if (!bdev->woble_setting_file_name) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->woble_setting_file_name)",
+ __func__);
+ return -1;
+ }
+ }
+
+ if (bdev->bt_cfg_file_name == NULL) {
+ bdev->bt_cfg_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
+ if (!bdev->bt_cfg_file_name) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->bt_cfg_file_name)", __func__);
+ return -1;
+ }
+ }
+
+ BTMTK_INFO("%s: Done", __func__);
+ return 0;
+}
+
+static void btmtk_main_free_memory(struct btmtk_dev *bdev)
+{
+ kfree(bdev->rom_patch_bin_file_name);
+ bdev->rom_patch_bin_file_name = NULL;
+
+ kfree(bdev->woble_setting_file_name);
+ bdev->woble_setting_file_name = NULL;
+
+ kfree(bdev->bt_cfg_file_name);
+ bdev->bt_cfg_file_name = NULL;
+
+ kfree(bdev->io_buf);
+ bdev->io_buf = NULL;
+
+ BTMTK_INFO("%s: Success", __func__);
+}
+
+int btmtk_main_cif_initialize(struct btmtk_dev *bdev, int hci_bus)
+{
+ int err = 0;
+
+ err = btmtk_main_allocate_memory(bdev);
+ if (err < 0) {
+ BTMTK_ERR("btmtk_main_allocate_memory failed!");
+ goto end;
+ }
+
+ btmtk_initialize_cfg_items(bdev);
+
+ err = btmtk_allocate_hci_device(bdev, hci_bus);
+ if (err < 0) {
+ BTMTK_ERR("btmtk_allocate_hci_device failed!");
+ goto free_mem;
+ }
+
+ err = btmtk_cap_init(bdev);
+ if (err < 0) {
+ BTMTK_ERR("btmtk_cap_init failed!");
+ goto free_hci_dev;
+ }
+
+ if (btmtk_load_bt_cfg(bdev->bt_cfg_file_name, bdev->intf_dev, bdev)) {
+ BTMTK_ERR("btmtk_load_bt_cfg failed!");
+ err = -1;
+ goto free_hci_dev;
+ }
+
+ return 0;
+
+free_hci_dev:
+ btmtk_free_hci_device(bdev, hci_bus);
+free_mem:
+ btmtk_main_free_memory(bdev);
+end:
+ return err;
+}
+
+void btmtk_main_cif_uninitialize(struct btmtk_dev *bdev, int hci_bus)
+{
+ btmtk_free_hci_device(bdev, hci_bus);
+ btmtk_initialize_cfg_items(bdev);
+ btmtk_main_free_memory(bdev);
+}
+
+
+int btmtk_main_woble_initialize(struct btmtk_dev *bdev)
+{
+ int err = 0;
+
+ /* Need to add Woble flow */
+ if (is_support_unify_woble(bdev)) {
+ btmtk_load_woble_setting(bdev->woble_setting_file_name,
+ bdev->intf_dev,
+ &bdev->woble_setting_len,
+ bdev);
+ /* if reset_stack is true, when chip reset is done, we need to power on chip to do
+ * reset stack
+ */
+ if (main_info.reset_stack_flag) {
+ err = btmtk_reset_power_on(bdev);
+ if (err < 0) {
+ BTMTK_ERR("reset power on failed!");
+ goto end;
+ }
+ }
+ }
+
+ if (bdev->bt_cfg.support_woble_by_eint) {
+ btmtk_woble_input_init(bdev);
+ btmtk_RegisterBTIrq(bdev);
+ }
+
+end:
+ return err;
+}
+
+int btmtk_main_cif_disconnect_notify(struct btmtk_dev *bdev, int hci_bus)
+{
+ btmtk_free_setting_file(bdev);
+ btmtk_deregister_hci_device(bdev);
+ btmtk_free_hci_device(bdev, hci_bus);
+
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
+ btmtk_release_dev(bdev);
+
+ return 0;
+}
+
+/**
+ * Kernel Module init/exit Functions
+ */
+static int __init main_driver_init(void)
+{
+ int ret = 0;
+ int i;
+
+ /* Mediatek Driver Version */
+ BTMTK_INFO("%s: MTK BT Driver Version : %s", __func__, VERSION);
+
+ ret = main_init();
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < btmtk_intf_num; i++)
+ btmtk_set_chip_state(g_bdev[i], BTMTK_STATE_DISCONNECT);
+
+ ret = btmtk_cif_register();
+ if (ret < 0) {
+ BTMTK_ERR("*** USB registration failed(%d)! ***", ret);
+ main_exit();
+ return ret;
+ }
+
+ ret = btmtk_fops_init();
+ if (ret < 0) {
+ BTMTK_ERR("*** STPBTFWLOG registration failed(%d)! ***", ret);
+ main_exit();
+ return ret;
+ }
+
+ BTMTK_INFO("%s: Done", __func__);
+ return ret;
+}
+
+static void __exit main_driver_exit(void)
+{
+ BTMTK_INFO("%s", __func__);
+ btmtk_cif_deregister();
+ btmtk_fops_exit();
+ main_exit();
+}
+module_init(main_driver_init);
+module_exit(main_driver_exit);
+
+/**
+ * Module Common Information
+ */
+MODULE_DESCRIPTION("Mediatek Bluetooth Driver");
+MODULE_VERSION(VERSION SUBVER);
+MODULE_LICENSE("GPL");
+module_param(btmtk_intf_num, int, 0444);
diff --git a/drivers/bluetooth/include/btmtk_buffer_mode.h b/drivers/bluetooth/include/btmtk_buffer_mode.h
new file mode 100644
index 000000000000..290b63357d24
--- /dev/null
+++ b/drivers/bluetooth/include/btmtk_buffer_mode.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2016,2017 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef __BTMTK_BUFFER_MODE_H__
+#define __BTMTK_BUFFER_MODE_H__
+
+#include "btmtk_main.h"
+
+#define BUFFER_MODE_SWITCH_FILE "wifi.cfg"
+#define BUFFER_MODE_SWITCH_FIELD "EfuseBufferModeCal"
+#define BUFFER_MODE_CFG_FILE "EEPROM_MT%X_1.bin"
+#define EFUSE_MODE 0
+#define BIN_FILE_MODE 1
+#define AUTO_MODE 2
+
+#define BUFFER_MODE_MAC_LENGTH 6
+#define BT0_MAC_OFFSET 0x139
+#define BT1_MAC_OFFSET 0x13F
+
+#define BUFFER_MODE_RADIO_LENGTH 4
+#define BT0_RADIO_OFFSET 0x145
+#define BT1_RADIO_OFFSET 0x149
+
+#define BUFFER_MODE_GROUP_LENGTH 5
+#define BT0_GROUP_ANT0_OFFSET 0x984
+#define BT0_GROUP_ANT1_OFFSET 0x9BE
+#define BT1_GROUP_ANT0_OFFSET 0x9A1
+#define BT1_GROUP_ANT1_OFFSET 0x9DB
+
+#define BUFFER_MODE_CAL_LENGTH 6
+#define BT0_CAL_ANT0_OFFSET 0x96C
+#define BT0_CAL_ANT1_OFFSET 0x9A6
+#define BT1_CAL_ANT0_OFFSET 0x989
+#define BT1_CAL_ANT1_OFFSET 0x9C3
+
+struct btmtk_buffer_mode_radio_struct {
+ u8 radio_0; /* bit 0-5:edr_init_pwr, 6-7:edr_pwr_mode */
+ u8 radio_1; /* bit 0-5:edr_max_pwr, 6-7:reserved */
+ u8 radio_2; /* bit 0-5:ble_default_pwr, 6-7:reserved */
+ u8 radio_3; /* reserved */
+};
+
+struct btmtk_buffer_mode_struct {
+ struct btmtk_dev *bdev;
+
+ unsigned char file_name[MAX_BIN_FILE_NAME_LEN];
+ int efuse_mode;
+
+ u8 bt0_mac[BUFFER_MODE_MAC_LENGTH];
+ u8 bt1_mac[BUFFER_MODE_MAC_LENGTH];
+ struct btmtk_buffer_mode_radio_struct bt0_radio;
+ struct btmtk_buffer_mode_radio_struct bt1_radio;
+ u8 bt0_ant0_grp_boundary[BUFFER_MODE_GROUP_LENGTH];
+ u8 bt0_ant1_grp_boundary[BUFFER_MODE_GROUP_LENGTH];
+ u8 bt1_ant0_grp_boundary[BUFFER_MODE_GROUP_LENGTH];
+ u8 bt1_ant1_grp_boundary[BUFFER_MODE_GROUP_LENGTH];
+ u8 bt0_ant0_pwr_offset[BUFFER_MODE_CAL_LENGTH];
+ u8 bt0_ant1_pwr_offset[BUFFER_MODE_CAL_LENGTH];
+ u8 bt1_ant0_pwr_offset[BUFFER_MODE_CAL_LENGTH];
+ u8 bt1_ant1_pwr_offset[BUFFER_MODE_CAL_LENGTH];
+};
+
+int btmtk_buffer_mode_send(struct btmtk_buffer_mode_struct *buffer_mode);
+void btmtk_buffer_mode_initialize(struct btmtk_dev *bdev,
+ struct btmtk_buffer_mode_struct **buffer_mode);
+#endif /* __BTMTK_BUFFER_MODE_H__ */
+
diff --git a/drivers/bluetooth/include/btmtk_chip_if.h b/drivers/bluetooth/include/btmtk_chip_if.h
new file mode 100644
index 000000000000..9e377341250b
--- /dev/null
+++ b/drivers/bluetooth/include/btmtk_chip_if.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/**
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+#ifndef __BTMTK_CHIP_IF_H__
+#define __BTMTK_CHIP_IF_H__
+
+#ifdef CHIP_IF_USB
+#include "btmtk_usb.h"
+#elif defined(CHIP_IF_SDIO)
+#include "btmtk_sdio.h"
+#elif defined(CHIP_IF_UART)
+#include "btmtk_uart.h"
+#elif defined(CHIP_IF_BTIF)
+#include "btmtk_btif.h"
+#endif
+
+int btmtk_cif_register(void);
+int btmtk_cif_deregister(void);
+
+#endif /* __BTMTK_CHIP_IF_H__ */
diff --git a/drivers/bluetooth/include/btmtk_define.h b/drivers/bluetooth/include/btmtk_define.h
new file mode 100644
index 000000000000..9033fa286e10
--- /dev/null
+++ b/drivers/bluetooth/include/btmtk_define.h
@@ -0,0 +1,304 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2016,2017 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef __BTMTK_DEFINE_H__
+#define __BTMTK_DEFINE_H__
+
+#include <linux/version.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include <linux/cdev.h>
+#include <linux/spinlock.h>
+#include <linux/kallsyms.h>
+#include <linux/device.h>
+#include <asm/unaligned.h>
+
+/* Define for proce node */
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+/* Define for whole chip reset */
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+/** Driver version */
+#define VERSION "7.0.2020110301"
+#define SUBVER ":turnkey"
+
+#define ENABLESTP FALSE
+#define BTMTKUART_TX_STATE_ACTIVE 1
+#define BTMTKUART_TX_STATE_WAKEUP 2
+#define BTMTK_TX_WAIT_VND_EVT 3
+#define BTMTKUART_REQUIRED_WAKEUP 4
+#define BTMTKUART_REQUIRED_DOWNLOAD 5
+#define BTMTK_TX_SKIP_VENDOR_EVT 6
+
+#define BTMTKUART_RX_STATE_ACTIVE 1
+#define BTMTKUART_RX_STATE_WAKEUP 2
+#define BTMTKUART_RX_STATE_RESET 3
+
+/**
+ * Maximum rom patch file name length
+ */
+#define MAX_BIN_FILE_NAME_LEN 64
+
+/**
+ * Type definition
+ */
+#ifndef TRUE
+ #define TRUE 1
+#endif
+#ifndef FALSE
+ #define FALSE 0
+#endif
+
+#ifndef UNUSED
+ #define UNUSED(x) ((void)(x))
+#endif
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+
+/**
+ * Log and level definition
+ */
+#define BTMTK_LOG_LVL_ERR 1
+#define BTMTK_LOG_LVL_WARN 2
+#define BTMTK_LOG_LVL_INFO 3
+#define BTMTK_LOG_LVL_DBG 4
+#define BTMTK_LOG_LVL_MAX BTMTK_LOG_LVL_DBG
+#define BTMTK_LOG_LVL_DEF BTMTK_LOG_LVL_INFO /* default setting */
+
+#define HCI_SNOOP_ENTRY_NUM 30
+#define HCI_SNOOP_BUF_SIZE 32
+#define HCI_SNOOP_MAX_BUF_SIZE 66
+#define WMT_OVER_HCI_HEADER_SIZE 3
+
+
+extern uint8_t btmtk_log_lvl;
+
+#define BTMTK_ERR(fmt, ...) \
+ do { \
+ if (btmtk_log_lvl >= BTMTK_LOG_LVL_ERR) \
+ pr_warn("[btmtk_err] ***"fmt"***\n", ##__VA_ARGS__); \
+ } while (0)
+#define BTMTK_WARN(fmt, ...) \
+ do { \
+ if (btmtk_log_lvl >= BTMTK_LOG_LVL_WARN) \
+ pr_warn("[btmtk_warn] "fmt"\n", ##__VA_ARGS__); \
+ } while (0)
+#define BTMTK_INFO(fmt, ...) \
+ do { \
+ if (btmtk_log_lvl >= BTMTK_LOG_LVL_INFO) \
+ pr_warn("[btmtk_info] "fmt"\n", ##__VA_ARGS__); \
+ } while (0)
+#define BTMTK_DBG(fmt, ...) \
+ do { \
+ if (btmtk_log_lvl >= BTMTK_LOG_LVL_DBG) \
+ pr_warn("[btmtk_dbg] "fmt"\n", ##__VA_ARGS__); \
+ } while (0)
+
+#define BTMTK_INFO_RAW(p, l, fmt, ...) \
+ do { \
+ if (btmtk_log_lvl >= BTMTK_LOG_LVL_INFO) { \
+ int raw_count = 0; \
+ char str[HCI_SNOOP_MAX_BUF_SIZE * 3 + 1]; \
+ char *p_str = str; \
+ const unsigned char *ptr = p; \
+ pr_warn("[btmtk_info] "fmt, ##__VA_ARGS__); \
+ for (raw_count = 0; \
+ raw_count < MIN(l, HCI_SNOOP_MAX_BUF_SIZE); ++raw_count) \
+ p_str += sprintf(p_str, " %02X", ptr[raw_count]); \
+ *p_str = '\0'; \
+ pr_warn("%s\n", str); \
+ } \
+ } while (0)
+
+#define BTMTK_DBG_RAW(p, l, fmt, ...) \
+ do { \
+ if (btmtk_log_lvl >= BTMTK_LOG_LVL_DBG) { \
+ int raw_count = 0; \
+ char str[HCI_SNOOP_MAX_BUF_SIZE * 3 + 1]; \
+ char *p_str = str; \
+ const unsigned char *ptr = p; \
+ pr_warn("[btmtk_debug] "fmt, ##__VA_ARGS__); \
+ for (raw_count = 0; \
+ raw_count < MIN(l, HCI_SNOOP_MAX_BUF_SIZE); ++raw_count) \
+ p_str += sprintf(p_str, " %02X", ptr[raw_count]); \
+ *p_str = '\0'; \
+ pr_warn("%s", str); \
+ } \
+ } while (0)
+
+#define BTMTK_CIF_IS_NULL(bdev, cif_event) \
+ (!bdev || !(&bdev->cif_state[cif_event]))
+
+/**
+ *
+ * HCI packet type
+ */
+#define MTK_HCI_COMMAND_PKT 0x01
+#define MTK_HCI_ACLDATA_PKT 0x02
+#define MTK_HCI_SCODATA_PKT 0x03
+#define MTK_HCI_EVENT_PKT 0x04
+#define HCI_ISO_PKT 0x05
+#define HCI_ISO_PKT_HEADER_SIZE 4
+#define HCI_ISO_PKT_WITH_ACL_HEADER_SIZE 5
+
+/**
+ * ROM patch related
+ */
+#define PATCH_HCI_HEADER_SIZE 4
+#define PATCH_WMT_HEADER_SIZE 5
+/*
+ * Enable STP
+ * HCI+WMT+STP = 4 + 5 + 1(phase) +(4=STP_HEADER + 2=CRC)
+#define PATCH_HEADER_SIZE 16
+ */
+/*#ifdef ENABLESTP
+ * #define PATCH_HEADER_SIZE (PATCH_HCI_HEADER_SIZE + PATCH_WMT_HEADER_SIZE + 1 + 6)
+ * #define UPLOAD_PATCH_UNIT 916
+ * #define PATCH_INFO_SIZE 30
+ *#else
+ */
+#define PATCH_HEADER_SIZE (PATCH_HCI_HEADER_SIZE + PATCH_WMT_HEADER_SIZE + 1)
+/* TODO, If usb use 901 patch unit size, download patch will timeout
+ * because the timeout has been set to 1s
+ */
+#define UPLOAD_PATCH_UNIT 2048
+#define PATCH_INFO_SIZE 30
+//#endif
+#define PATCH_PHASE1 1
+#define PATCH_PHASE2 2
+#define PATCH_PHASE3 3
+
+/* It is for mt7961 download rom patch*/
+#define FW_ROM_PATCH_HEADER_SIZE 32
+#define FW_ROM_PATCH_GD_SIZE 64
+#define FW_ROM_PATCH_SEC_MAP_SIZE 64
+#define SEC_MAP_NEED_SEND_SIZE 52
+#define PATCH_STATUS 7
+
+
+#define IO_BUF_SIZE (HCI_MAX_EVENT_SIZE > 256 ? HCI_MAX_EVENT_SIZE : 256)
+#define EVENT_COMPARE_SIZE 64
+
+#define SECTION_SPEC_NUM 13
+
+/* Define for WoBLE */
+#define BD_ADDRESS_SIZE 6
+#define WOBLE_SETTING_COUNT 10
+#define PHASE1_WMT_CMD_COUNT 255
+#define VENDOR_CMD_COUNT 255
+#define WOBLE_SETTING_FILE_NAME_7663 "woble_setting_7663.bin"
+#define WOBLE_SETTING_FILE_NAME_7961 "woble_setting_7961.bin"
+#define WOBLE_EVENT_INTERVAL_TIMO 500
+#define WOBLE_COMP_EVENT_TIMO 5000
+
+#define BT_CFG_NAME "bt.cfg"
+#define BT_UNIFY_WOBLE "SUPPORT_UNIFY_WOBLE"
+#define BT_UNIFY_WOBLE_TYPE "UNIFY_WOBLE_TYPE"
+#define BT_WOBLE_BY_EINT "SUPPORT_WOBLE_BY_EINT"
+#define BT_DONGLE_RESET_PIN "BT_DONGLE_RESET_GPIO_PIN"
+#define BT_RESET_DONGLE "SUPPORT_DONGLE_RESET"
+#define BT_FULL_FW_DUMP "SUPPORT_FULL_FW_DUMP"
+#define BT_WOBLE_WAKELOCK "SUPPORT_WOBLE_WAKELOCK"
+#define BT_WOBLE_FOR_BT_DISABLE "SUPPORT_WOBLE_FOR_BT_DISABLE"
+#define BT_RESET_STACK_AFTER_WOBLE "RESET_STACK_AFTER_WOBLE"
+#define BT_AUTO_PICUS "SUPPORT_AUTO_PICUS"
+#define BT_AUTO_PICUS_FILTER "PICUS_FILTER_COMMAND"
+#define BT_AUTO_PICUS_ENABLE "PICUS_ENABLE_COMMAND"
+#define BT_PICUS_TO_HOST "SUPPORT_PICUS_TO_HOST"
+#define BT_PHASE1_WMT_CMD "PHASE1_WMT_CMD"
+#define BT_VENDOR_CMD "VENDOR_CMD"
+#define BT_SINGLE_SKU "SUPPORT_BT_SINGLE_SKU"
+
+
+#define PM_KEY_BTW (0x0015) /* Notify PM the unify woble type */
+
+/**
+ * Disable RESUME_RESUME
+ */
+#ifndef BT_DISABLE_RESET_RESUME
+#define BT_DISABLE_RESET_RESUME 0
+#endif
+
+enum fw_cfg_index_len {
+ FW_CFG_INX_LEN_NONE = 0,
+ FW_CFG_INX_LEN_2 = 2,
+ FW_CFG_INX_LEN_3 = 3,
+};
+
+struct fw_cfg_struct {
+ char *content; /* APCF content or radio off content */
+ int length; /* APCF content or radio off content of length */
+};
+
+struct bt_cfg_struct {
+ bool support_unify_woble; /* support unify woble or not */
+ bool support_woble_by_eint; /* support woble by eint or not */
+ bool support_dongle_reset; /* support chip reset or not */
+ bool support_full_fw_dump; /* dump full fw coredump or not */
+ bool support_woble_wakelock; /* support when woble error, do wakelock or not */
+ bool support_woble_for_bt_disable; /* when bt disable, support enter susend or not */
+ bool reset_stack_after_woble; /* support reset stack to re-connect IOT after resume */
+ bool support_auto_picus; /* support enable PICUS automatically */
+ struct fw_cfg_struct picus_filter; /* support on PICUS filter command customization */
+ struct fw_cfg_struct picus_enable; /* support on PICUS enable command customization */
+ bool support_picus_to_host; /* support picus log to host (boots/bluedroid) */
+ int dongle_reset_gpio_pin; /* BT_DONGLE_RESET_GPIO_PIN number */
+ unsigned int unify_woble_type; /* 0: legacy. 1: waveform. 2: IR */
+ struct fw_cfg_struct phase1_wmt_cmd[PHASE1_WMT_CMD_COUNT];
+ struct fw_cfg_struct vendor_cmd[VENDOR_CMD_COUNT];
+ bool support_bt_single_sku;
+};
+
+#define WIFI_DOWNLOAD TRUE
+#define BT_DOWNLOAD FALSE
+
+#define SWAP32(x) \
+ ((u32) (\
+ (((u32) (x) & (u32) 0x000000ffUL) << 24) | \
+ (((u32) (x) & (u32) 0x0000ff00UL) << 8) | \
+ (((u32) (x) & (u32) 0x00ff0000UL) >> 8) | \
+ (((u32) (x) & (u32) 0xff000000UL) >> 24)))
+
+/* Endian byte swapping codes */
+#ifdef __LITTLE_ENDIAN
+#define cpu2le32(x) ((uint32_t)(x))
+#define le2cpu32(x) ((uint32_t)(x))
+#define cpu2be32(x) SWAP32((x))
+#define be2cpu32(x) SWAP32((x))
+#else
+#define cpu2le32(x) SWAP32((x))
+#define le2cpu32(x) SWAP32((x))
+#define cpu2be32(x) ((uint32_t)(x))
+#define be2cpu32(x) ((uint32_t)(x))
+#endif
+
+#define FW_VERSION 0x80021004
+#define CHIP_ID 0x70010200
+#define FLAVOR 0x70010020
+
+
+#endif /* __BTMTK_DEFINE_H__ */
diff --git a/drivers/bluetooth/include/btmtk_drv.h b/drivers/bluetooth/include/btmtk_drv.h
new file mode 100644
index 000000000000..005cca677730
--- /dev/null
+++ b/drivers/bluetooth/include/btmtk_drv.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2016,2017 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef _BTMTK_DRV_H_
+#define _BTMTK_DRV_H_
+
+#include <linux/kthread.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <net/bluetooth/bluetooth.h>
+
+#define SAVE_FW_DUMP_IN_KERNEL 1
+
+#define SUPPORT_FW_DUMP 1
+#define BTM_HEADER_LEN 5
+#define BTM_UPLD_SIZE 2312
+
+#define MTK_TXDATA_SIZE 2000
+#define MTK_RXDATA_SIZE 2000
+
+/* Time to wait until Host Sleep state change in millisecond */
+#define WAIT_UNTIL_HS_STATE_CHANGED msecs_to_jiffies(5000)
+/* Time to wait for command response in millisecond */
+#define WAIT_UNTIL_CMD_RESP msecs_to_jiffies(5000)
+
+enum rdwr_status {
+ RDWR_STATUS_SUCCESS = 0,
+ RDWR_STATUS_FAILURE = 1,
+ RDWR_STATUS_DONE = 2
+};
+
+#define FW_DUMP_MAX_NAME_LEN 8
+#define FW_DUMP_HOST_READY 0xEE
+#define FW_DUMP_DONE 0xFF
+#define FW_DUMP_READ_DONE 0xFE
+
+struct memory_type_mapping {
+ u8 mem_name[FW_DUMP_MAX_NAME_LEN];
+ u8 *mem_ptr;
+ u32 mem_size;
+ u8 done_flag;
+};
+
+#define MTK_VENDOR_PKT 0xFE
+
+/* Vendor specific Bluetooth commands */
+#define BT_CMD_PSCAN_WIN_REPORT_ENABLE 0xFC03
+#define BT_CMD_ROUTE_SCO_TO_HOST 0xFC1D
+#define BT_CMD_SET_BDADDR 0xFC22
+#define BT_CMD_AUTO_SLEEP_MODE 0xFC23
+#define BT_CMD_HOST_SLEEP_CONFIG 0xFC59
+#define BT_CMD_HOST_SLEEP_ENABLE 0xFC5A
+#define BT_CMD_MODULE_CFG_REQ 0xFC5B
+#define BT_CMD_LOAD_CONFIG_DATA 0xFC61
+
+/* Sub-commands: Module Bringup/Shutdown Request/Response */
+#define MODULE_BRINGUP_REQ 0xF1
+#define MODULE_BROUGHT_UP 0x00
+#define MODULE_ALREADY_UP 0x0C
+
+#define MODULE_SHUTDOWN_REQ 0xF2
+
+/* Vendor specific Bluetooth events */
+#define BT_EVENT_AUTO_SLEEP_MODE 0x23
+#define BT_EVENT_HOST_SLEEP_CONFIG 0x59
+#define BT_EVENT_HOST_SLEEP_ENABLE 0x5A
+#define BT_EVENT_MODULE_CFG_REQ 0x5B
+#define BT_EVENT_POWER_STATE 0x20
+
+/* Bluetooth Power States */
+#define BT_PS_ENABLE 0x02
+#define BT_PS_DISABLE 0x03
+#define BT_PS_SLEEP 0x01
+
+/* Host Sleep states */
+#define HS_ACTIVATED 0x01
+#define HS_DEACTIVATED 0x00
+
+/* Power Save modes */
+#define PS_SLEEP 0x01
+#define PS_AWAKE 0x00
+
+#define BT_CAL_HDR_LEN 4
+#define BT_CAL_DATA_SIZE 28
+
+#define FW_DUMP_BUF_SIZE (1024*512)
+
+#define FW_DUMP_FILE_NAME_SIZE 64
+
+
+/* #define SAVE_FW_DUMP_IN_KERNEL 1 */
+
+/* stpbt device node */
+#define BT_NODE "stpbt"
+#define BT_DRIVER_NAME "BT_chrdev"
+
+struct btmtk_event {
+ u8 ec; /* event counter */
+ u8 length;
+ u8 data[4];
+} __packed;
+
+/* Prototype of global function */
+
+struct btmtk_private *btmtk_add_card(void *card);
+int btmtk_remove_card(struct btmtk_private *priv);
+
+void btmtk_interrupt(struct btmtk_private *priv);
+
+bool btmtk_check_evtpkt(struct btmtk_private *priv, struct sk_buff *skb);
+int btmtk_process_event(struct btmtk_private *priv, struct sk_buff *skb);
+
+int btmtk_send_module_cfg_cmd(struct btmtk_private *priv, u8 subcmd);
+int btmtk_pscan_window_reporting(struct btmtk_private *priv, u8 subcmd);
+int btmtk_send_hscfg_cmd(struct btmtk_private *priv);
+int btmtk_enable_ps(struct btmtk_private *priv);
+int btmtk_prepare_command(struct btmtk_private *priv);
+int btmtk_enable_hs(struct btmtk_private *priv);
+void btmtk_firmware_dump(struct btmtk_private *priv);
+
+#define META_BUFFER_SIZE (1024*50)
+
+struct _OSAL_UNSLEEPABLE_LOCK_ {
+ spinlock_t lock;
+ unsigned long flag;
+};
+
+struct ring_buffer {
+ struct _OSAL_UNSLEEPABLE_LOCK_ spin_lock;
+ u8 buffer[META_BUFFER_SIZE]; /* MTKSTP_BUFFER_SIZE:1024 */
+ u32 read_p; /* indicate the current read index */
+ u32 write_p; /* indicate the current write index */
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+#define FIXED_STPBT_MAJOR_DEV_ID 111
+
+
+
+#define FW_DUMP_END_EVENT "coredump end"
+
+#endif
+
+#endif
+
diff --git a/drivers/bluetooth/include/btmtk_main.h b/drivers/bluetooth/include/btmtk_main.h
new file mode 100644
index 000000000000..21e4d71a75e2
--- /dev/null
+++ b/drivers/bluetooth/include/btmtk_main.h
@@ -0,0 +1,587 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/**
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+#ifndef __BTMTK_MAIN_H__
+#define __BTMTK_MAIN_H__
+#include "btmtk_define.h"
+#include "btmtk_chip_if.h"
+
+#define DEFAULT_COUNTRY_TABLE_NAME "btPowerTable.dat"
+
+
+//static inline struct sk_buff *mtk_add_stp(struct btmtk_dev *bdev, struct sk_buff *skb);
+
+#define hci_dev_test_and_clear_flag(hdev, nr) test_and_clear_bit((nr), (hdev)->dev_flags)
+
+/* h4_recv */
+#define hci_skb_pkt_type(skb) bt_cb((skb))->pkt_type
+#define hci_skb_expect(skb) bt_cb((skb))->expect
+#define hci_skb_opcode(skb) bt_cb((skb))->hci.opcode
+
+/* HCI bus types */
+#define HCI_VIRTUAL 0
+#define HCI_USB 1
+#define HCI_PCCARD 2
+#define HCI_UART 3
+#define HCI_RS232 4
+#define HCI_PCI 5
+#define HCI_SDIO 6
+#define HCI_SPI 7
+#define HCI_I2C 8
+#define HCI_SMD 9
+
+#define HCI_TYPE_SIZE 1
+
+/* this for 7663 need download patch staus
+ * 0:
+ * patch download is not complete/BT get patch semaphore fail (WiFi get semaphore success)
+ * 1:
+ * patch download is complete
+ * 2:
+ * patch download is not complete/BT get patch semaphore success
+ */
+#define MT766X_PATCH_IS_DOWNLOAD_BY_OTHER 0
+#define MT766X_PATCH_READY 1
+#define MT766X_PATCH_NEED_DOWNLOAD 2
+
+/* this for 79XX need download patch staus
+ * 0:
+ * patch download is not complete, BT driver need to download patch
+ * 1:
+ * patch is downloading by Wifi,BT driver need to retry until status = PATCH_READY
+ * 2:
+ * patch download is complete, BT driver no need to download patch
+ */
+#define PATCH_ERR -1
+#define PATCH_NEED_DOWNLOAD 0
+#define PATCH_IS_DOWNLOAD_BY_OTHER 1
+#define PATCH_READY 2
+
+/* 0:
+ * using legacy wmt cmd/evt to download fw patch, usb/sdio just support 0 now
+ * 1:
+ * using DMA to download fw patch
+ */
+#define PATCH_DOWNLOAD_USING_WMT 0
+#define PATCH_DOWNLOAD_USING_DMA 1
+
+#define PATCH_DOWNLOAD_PHASE1_2_DELAY_TIME 1
+#define PATCH_DOWNLOAD_PHASE1_2_RETRY 5
+#define PATCH_DOWNLOAD_PHASE3_DELAY_TIME 20
+#define PATCH_DOWNLOAD_PHASE3_RETRY 20
+
+/* * delay and retrey for main_send_cmd */
+#define WMT_DELAY_TIMES 100
+#define DELAY_TIMES 20
+#define RETRY_TIMES 20
+
+/* Expected minimum supported interface */
+#define BT_MCU_MINIMUM_INTERFACE_NUM 4
+
+/* Bus event */
+#define HIF_EVENT_PROBE 0
+#define HIF_EVENT_DISCONNECT 1
+#define HIF_EVENT_SUSPEND 2
+#define HIF_EVENT_RESUME 3
+#define HIF_EVENT_STANDBY 4
+#define HIF_EVENT_SUBSYS_RESET 5
+#define HIF_EVENT_WHOLE_CHIP_RESET 6
+#define HIF_EVENT_FW_DUMP 7
+
+
+#define CHAR2HEX_SIZE 4
+
+/**
+ * For chip reset pin
+ */
+#define RESET_PIN_SET_LOW_TIME 100
+
+/* stpbtfwlog setting */
+#define FWLOG_QUEUE_COUNT (400 * BT_MCU_MINIMUM_INTERFACE_NUM)
+#define FWLOG_ASSERT_QUEUE_COUNT 45000
+#define FWLOG_BLUETOOTH_KPI_QUEUE_COUNT 400
+#define HCI_MAX_COMMAND_SIZE 255
+#define HCI_MAX_COMMAND_BUF_SIZE (HCI_MAX_COMMAND_SIZE * 3)
+#define HCI_MAX_ISO_SIZE 340
+
+/* fwlog information define */
+#define FWLOG_TYPE 0xF0
+#define FWLOG_LEN_SIZE 2
+#define FWLOG_TL_SIZE (HCI_TYPE_SIZE + FWLOG_LEN_SIZE)
+#define FWLOG_ATTR_TYPE_LEN 1
+#define FWLOG_ATTR_LEN_LEN 1
+#define FWLOG_ATTR_RX_LEN_LEN 2
+#define FWLOG_ATTR_TL_SIZE (FWLOG_ATTR_TYPE_LEN + FWLOG_ATTR_LEN_LEN)
+
+#define FWLOG_HCI_IDX 0x00
+#define FWLOG_DONGLE_IDX 0x01
+#define FWLOG_TX 0x10
+#define FWLOG_RX 0x11
+
+/* total fwlog info len */
+#define FWLOG_PRSV_LEN 32
+
+/* bluetooth kpi */
+#define KPI_WITHOUT_TYPE 0
+#define COUNTRY_CODE_LEN 2
+
+
+#define EDR_MIN -32
+#define EDR_MAX 20
+#define EDR_MIN_LV9 13
+#define BLE_MIN -29
+#define BLE_MAX 20
+#define EDR_MIN_R1 -64
+#define EDR_MAX_R1 40
+#define EDR_MIN_LV9_R1 26
+#define BLE_MIN_R1 -58
+#define BLE_MAX_R1 40
+#define EDR_MIN_R2 -128
+#define EDR_MAX_R2 80
+#define EDR_MIN_LV9_R2 52
+#define BLE_MIN_R2 -116
+#define BLE_MAX_R2 80
+
+#define ERR_PWR -9999
+
+enum {
+ RES_1 = 0,
+ RES_DOT_5,
+ RES_DOT_25
+};
+
+enum {
+ CHECK_SINGLE_SKU_PWR_MODE = 0,
+ CHECK_SINGLE_SKU_EDR_MAX,
+ CHECK_SINGLE_SKU_BLE,
+ CHECK_SINGLE_SKU_BLE_2M,
+ CHECK_SINGLE_SKU_BLE_LR_S2,
+ CHECK_SINGLE_SKU_BLE_LR_S8,
+ CHECK_SINGLE_SKU_ALL
+};
+
+enum {
+ DISABLE_LV9 = 0,
+ ENABLE_LV9
+};
+
+enum {
+ DIFF_MODE_3DB = 0,
+ DIFF_MODE_0DB
+};
+
+struct btmtk_cif_state {
+ unsigned char ops_enter;
+ unsigned char ops_end;
+ unsigned char ops_error;
+};
+
+enum TX_TYPE {
+ BTMTK_TX_CMD_FROM_DRV = 0, /* send hci cmd and wmt cmd by driver */
+ BTMTK_TX_ACL_FROM_DRV, /* send acl pkt with load rompatch by driver */
+ BTMTK_TX_PKT_FROM_HOST, /* send pkt from host, include acl and hci */
+};
+
+/* Device node */
+#if CFG_SUPPORT_MULTI_DEV_NODE
+ #define BT_CHR_DEV "BT_multi_chrdevfwlog"
+ #define BT_DEV_NODE "stpbt_multi_fwlog"
+#else
+ #define BT_CHR_DEV "BT_chrdevfwlog"
+ #define BT_DEV_NODE "stpbtfwlog"
+#endif
+
+struct bt_power_setting {
+ int8_t EDR_Max;
+ int8_t LV9;
+ int8_t DM;
+ int8_t IR;
+ int8_t BLE_1M;
+ int8_t BLE_2M;
+ int8_t BLE_LR_S2;
+ int8_t BLE_LR_S8;
+ char country_code[3];
+};
+
+struct btmtk_fops_fwlog {
+ dev_t g_devIDfwlog;
+ struct cdev BT_cdevfwlog;
+ wait_queue_head_t fw_log_inq;
+ struct sk_buff_head fwlog_queue;
+ struct class *pBTClass;
+ struct device *pBTDevfwlog;
+ spinlock_t fwlog_lock;
+ u8 btmtk_bluetooth_kpi;
+};
+
+enum {
+ BTMTK_DONGLE_STATE_UNKNOWN,
+ BTMTK_DONGLE_STATE_POWER_ON,
+ BTMTK_DONGLE_STATE_POWER_OFF,
+ BTMTK_DONGLE_STATE_ERROR,
+};
+
+enum {
+ HW_ERR_NONE = 0x00,
+ HW_ERR_CODE_CHIP_RESET = 0xF0,
+ HW_ERR_CODE_USB_DISC = 0xF1,
+ HW_ERR_CODE_CORE_DUMP = 0xF2,
+ HW_ERR_CODE_POWER_ON = 0xF3,
+ HW_ERR_CODE_POWER_OFF = 0xF4,
+ HW_ERR_CODE_SET_SLEEP_CMD = 0xF5,
+ HW_ERR_CODE_RESET_STACK_AFTER_WOBLE = 0xF6,
+};
+
+/* Please keep sync with btmtk_set_state function */
+enum {
+ /* BTMTK_STATE_UNKNOWN = 0, */
+ BTMTK_STATE_INIT = 1,
+ BTMTK_STATE_DISCONNECT,
+ BTMTK_STATE_PROBE,
+ BTMTK_STATE_WORKING,
+ BTMTK_STATE_SUSPEND,
+ BTMTK_STATE_RESUME,
+ BTMTK_STATE_FW_DUMP,
+ BTMTK_STATE_STANDBY,
+ BTMTK_STATE_SUBSYS_RESET,
+};
+
+/* Please keep sync with btmtk_fops_set_state function */
+enum {
+ /* BTMTK_FOPS_STATE_UNKNOWN = 0, */
+ BTMTK_FOPS_STATE_INIT = 1,
+ BTMTK_FOPS_STATE_OPENING, /* during opening */
+ BTMTK_FOPS_STATE_OPENED, /* open in fops_open */
+ BTMTK_FOPS_STATE_CLOSING, /* during closing */
+ BTMTK_FOPS_STATE_CLOSED, /* closed */
+};
+
+enum {
+ BTMTK_EVENT_COMPARE_STATE_UNKNOWN,
+ BTMTK_EVENT_COMPARE_STATE_NOTHING_NEED_COMPARE,
+ BTMTK_EVENT_COMPARE_STATE_NEED_COMPARE,
+ BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS,
+};
+
+struct h4_recv_pkt {
+ u8 type; /* Packet type */
+ u8 hlen; /* Header length */
+ u8 loff; /* Data length offset in header */
+ u8 lsize; /* Data length field size */
+ u16 maxlen; /* Max overall packet length */
+ int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
+};
+
+#pragma pack(1)
+struct _PATCH_HEADER {
+ u8 ucDateTime[16];
+ u8 ucPlatform[4];
+ u16 u2HwVer;
+ u16 u2SwVer;
+ u32 u4MagicNum;
+};
+
+struct _Global_Descr {
+ u32 u4PatchVer;
+ u32 u4SubSys;
+ u32 u4FeatureOpt;
+ u32 u4SectionNum;
+};
+
+struct _Section_Map {
+ u32 u4SecType;
+ u32 u4SecOffset;
+ u32 u4SecSize;
+ union {
+ u32 u4SecSpec[SECTION_SPEC_NUM];
+ struct {
+ u32 u4DLAddr;
+ u32 u4DLSize;
+ u32 u4SecKeyIdx;
+ u32 u4AlignLen;
+ u32 u4SecType;
+ u32 u4DLModeCrcType;
+ u32 u4Crc;
+ u32 reserved[6];
+ } bin_info_spec;
+ };
+};
+#pragma pack()
+
+#define H4_RECV_ACL \
+ .type = HCI_ACLDATA_PKT, \
+ .hlen = HCI_ACL_HDR_SIZE, \
+ .loff = 2, \
+ .lsize = 2, \
+ .maxlen = HCI_MAX_FRAME_SIZE \
+
+#define H4_RECV_SCO \
+ .type = HCI_SCODATA_PKT, \
+ .hlen = HCI_SCO_HDR_SIZE, \
+ .loff = 2, \
+ .lsize = 1, \
+ .maxlen = HCI_MAX_SCO_SIZE
+
+#define H4_RECV_EVENT \
+ .type = HCI_EVENT_PKT, \
+ .hlen = HCI_EVENT_HDR_SIZE, \
+ .loff = 1, \
+ .lsize = 1, \
+ .maxlen = HCI_MAX_EVENT_SIZE
+
+
+struct btmtk_dev {
+ struct hci_dev *hdev;
+ unsigned long hdev_flags;
+ unsigned long flags;
+ void *intf_dev;
+ void *cif_dev;
+
+ struct work_struct work;
+ struct work_struct waker;
+ struct work_struct reset_waker;
+
+ int recv_evt_len;
+ int tx_in_flight;
+ spinlock_t txlock;
+ spinlock_t rxlock;
+
+ struct sk_buff *evt_skb;
+ struct sk_buff *sco_skb;
+
+ /* For ble iso packet size */
+ int iso_threshold;
+
+ unsigned int sco_num;
+ int isoc_altsetting;
+
+ int suspend_count;
+
+ /* For tx queue */
+ unsigned long tx_state;
+
+ /* For rx queue */
+ struct workqueue_struct *workqueue;
+ struct sk_buff_head rx_q;
+ struct work_struct rx_work;
+ struct sk_buff *rx_skb;
+
+ wait_queue_head_t p_wait_event_q;
+
+ unsigned int subsys_reset;
+ unsigned int chip_reset;
+ unsigned char *rom_patch_bin_file_name;
+ unsigned int chip_id;
+ unsigned int flavor;
+ unsigned int fw_version;
+ unsigned char dongle_index;
+ unsigned char power_state;
+ unsigned char fops_state;
+ unsigned char interface_state;
+ struct btmtk_cif_state *cif_state;
+
+ /* io buffer for usb control transfer */
+ unsigned char *io_buf;
+
+ unsigned char *setting_file;
+ unsigned char *woble_setting_file_name;
+ unsigned int woble_setting_len;
+
+ struct fw_cfg_struct woble_setting_apcf[WOBLE_SETTING_COUNT];
+ struct fw_cfg_struct woble_setting_apcf_fill_mac[WOBLE_SETTING_COUNT];
+ struct fw_cfg_struct woble_setting_apcf_fill_mac_location[WOBLE_SETTING_COUNT];
+
+ struct fw_cfg_struct woble_setting_radio_off;
+ struct fw_cfg_struct woble_setting_wakeup_type;
+ struct fw_cfg_struct woble_setting_radio_off_status_event;
+ /* complete event */
+ struct fw_cfg_struct woble_setting_radio_off_comp_event;
+
+ struct fw_cfg_struct woble_setting_radio_on;
+ struct fw_cfg_struct woble_setting_radio_on_status_event;
+ struct fw_cfg_struct woble_setting_radio_on_comp_event;
+
+ /* set apcf after resume(radio on) */
+ struct fw_cfg_struct woble_setting_apcf_resume[WOBLE_SETTING_COUNT];
+ unsigned char bdaddr[BD_ADDRESS_SIZE];
+ unsigned int woble_need_trigger_coredump;
+ unsigned int woble_need_set_radio_off_in_probe;
+
+ unsigned char *bt_cfg_file_name;
+ struct bt_cfg_struct bt_cfg;
+
+ /* Foe Woble eint */
+ unsigned int wobt_irq;
+ int wobt_irqlevel;
+ atomic_t irq_enable_count;
+ struct input_dev *WoBLEInputDev;
+
+ u8 opcode_usr[2];
+};
+
+typedef int (*cif_open_ptr)(struct hci_dev *hdev);
+typedef int (*cif_close_ptr)(struct hci_dev *hdev);
+typedef int (*cif_reg_read_ptr)(struct btmtk_dev *bdev, u32 reg, u32 *val);
+typedef int (*cif_reg_write_ptr)(struct btmtk_dev *bdev, u32 reg, u32 val);
+typedef int (*cif_send_cmd_ptr)(struct btmtk_dev *bdev, struct sk_buff *skb,
+ int delay, int retry, int pkt_type);
+typedef int (*cif_send_and_recv_ptr)(struct btmtk_dev *bdev,
+ struct sk_buff *skb,
+ const uint8_t *event, const int event_len,
+ int delay, int retry, int pkt_type);
+typedef int (*cif_event_filter_ptr)(struct btmtk_dev *bdev, struct sk_buff *skb);
+typedef int (*cif_subsys_reset_ptr)(struct btmtk_dev *bdev);
+typedef int (*cif_whole_reset_ptr)(struct btmtk_dev *bdev);
+typedef void (*cif_chip_reset_notify_ptr)(struct btmtk_dev *bdev);
+typedef void (*cif_mutex_lock_ptr)(struct btmtk_dev *bdev);
+typedef void (*cif_mutex_unlock_ptr)(struct btmtk_dev *bdev);
+typedef void (*cif_open_done_ptr)(struct btmtk_dev *bdev);
+typedef int (*cif_dl_dma_ptr)(struct btmtk_dev *bdev, u8 *image,
+ u8 *fwbuf, int section_dl_size, int section_offset);
+
+struct hif_hook_ptr {
+ cif_open_ptr open;
+ cif_close_ptr close;
+ cif_reg_read_ptr reg_read;
+ cif_reg_write_ptr reg_write;
+ cif_send_cmd_ptr send_cmd;
+ cif_send_and_recv_ptr send_and_recv;
+ cif_event_filter_ptr event_filter;
+ cif_subsys_reset_ptr subsys_reset;
+ cif_whole_reset_ptr whole_reset;
+ cif_chip_reset_notify_ptr chip_reset_notify;
+ cif_mutex_lock_ptr cif_mutex_lock;
+ cif_mutex_unlock_ptr cif_mutex_unlock;
+ cif_open_done_ptr open_done;
+ cif_dl_dma_ptr dl_dma;
+};
+
+struct btmtk_main_info {
+ u8 reset_stack_flag;
+ struct wakeup_source *fwdump_ws;
+ struct wakeup_source *woble_ws;
+ struct wakeup_source *eint_ws;
+ struct hif_hook_ptr hif_hook;
+ struct bt_power_setting PWS;
+};
+
+static inline int is_mt7922(u32 chip_id)
+{
+ chip_id &= 0xFFFF;
+ if (chip_id == 0x7922)
+ return 1;
+ return 0;
+}
+
+static inline int is_mt7961(u32 chip_id)
+{
+ chip_id &= 0xFFFF;
+ if (chip_id == 0x7961)
+ return 1;
+ return 0;
+}
+
+static inline int is_mt7663(u32 chip_id)
+{
+ chip_id &= 0xFFFF;
+ if (chip_id == 0x7663)
+ return 1;
+ return 0;
+}
+
+static inline int is_support_unify_woble(struct btmtk_dev *bdev)
+{
+ if (bdev->bt_cfg.support_unify_woble) {
+ if (is_mt7922(bdev->chip_id) ||
+ is_mt7961(bdev->chip_id) || is_mt7663(bdev->chip_id))
+ return 1;
+ else
+ return 0;
+ } else {
+ return 0;
+ }
+}
+
+int btmtk_get_chip_state(struct btmtk_dev *bdev);
+void btmtk_set_chip_state(struct btmtk_dev *bdev, int new_state);
+int btmtk_allocate_hci_device(struct btmtk_dev *bdev, int hci_bus_type);
+void btmtk_free_hci_device(struct btmtk_dev *bdev, int hci_bus_type);
+int btmtk_register_hci_device(struct btmtk_dev *bdev);
+int btmtk_deregister_hci_device(struct btmtk_dev *bdev);
+int btmtk_recv(struct hci_dev *hdev, const u8 *data, size_t count);
+int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
+int btmtk_recv_acl(struct hci_dev *hdev, struct sk_buff *skb);
+int btmtk_send_init_cmds(struct btmtk_dev *hdev);
+int btmtk_send_deinit_cmds(struct btmtk_dev *hdev);
+int btmtk_main_send_cmd(struct btmtk_dev *bdev, const uint8_t *cmd,
+ const int cmd_len, const uint8_t *event, const int event_len, int delay,
+ int retry, int pkt_type);
+int btmtk_send_wmt_reset(struct btmtk_dev *hdev);
+int btmtk_send_wmt_power_on_cmd(struct btmtk_dev *hdev);
+int btmtk_send_wmt_power_off_cmd(struct btmtk_dev *hdev);
+int btmtk_woble_suspend(struct btmtk_dev *bdev);
+int btmtk_woble_resume(struct btmtk_dev *bdev);
+int btmtk_handle_leaving_WoBLE_state(struct btmtk_dev *bdev);
+int btmtk_handle_entering_WoBLE_state(struct btmtk_dev *bdev);
+int btmtk_load_code_from_setting_files(char *setting_file_name,
+ struct device *dev, u32 *code_len, struct btmtk_dev *bdev);
+int btmtk_load_woble_setting(char *bin_name,
+ struct device *dev, u32 *code_len, struct btmtk_dev *bdev);
+int btmtk_load_rom_patch_766x(struct btmtk_dev *hdev);
+int btmtk_uart_send_wakeup_cmd(struct hci_dev *hdev);
+int btmtk_uart_send_set_uart_cmd(struct hci_dev *hdev);
+int btmtk_load_rom_patch(struct btmtk_dev *bdev);
+struct btmtk_dev *btmtk_get_dev(void);
+void btmtk_release_dev(struct btmtk_dev *bdev);
+struct btmtk_dev *btmtk_allocate_dev_memory(struct device *dev);
+void btmtk_free_dev_memory(struct device *dev, struct btmtk_dev *bdev);
+void btmtk_reset_waker(struct work_struct *work);
+void btmtk_initialize_cfg_items(struct btmtk_dev *bdev);
+bool btmtk_load_bt_cfg(char *cfg_name, struct device *dev, struct btmtk_dev *bdev);
+struct btmtk_main_info *btmtk_get_main_info(void);
+int btmtk_reset_power_on(struct btmtk_dev *bdev);
+void btmtk_send_hw_err_to_host(struct btmtk_dev *bdev);
+void btmtk_free_setting_file(struct btmtk_dev *bdev);
+/** file_operations: stpbtfwlog */
+int btmtk_fops_openfwlog(struct inode *inode, struct file *file);
+int btmtk_fops_closefwlog(struct inode *inode, struct file *file);
+ssize_t btmtk_fops_readfwlog(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
+ssize_t btmtk_fops_writefwlog(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos);
+unsigned int btmtk_fops_pollfwlog(struct file *filp, poll_table *wait);
+long btmtk_fops_unlocked_ioctlfwlog(struct file *filp, unsigned int cmd, unsigned long arg);
+
+/* Auto enable picus */
+int btmtk_picus_enable(struct btmtk_dev *bdev);
+int btmtk_picus_disable(struct btmtk_dev *bdev);
+
+void btmtk_hci_snoop_save_cmd(u32 len, u8 *buf);
+void btmtk_hci_snoop_save_event(u32 len, u8 *buf);
+void btmtk_hci_snoop_save_adv_event(u32 len, u8 *buf);
+void btmtk_hci_snoop_save_acl(u32 len, u8 *buf);
+void btmtk_hci_snoop_print(u32 len, const u8 *buf);
+unsigned long btmtk_kallsyms_lookup_name(const char *name);
+void btmtk_woble_wake_lock(struct btmtk_dev *bdev);
+void btmtk_woble_wake_unlock(struct btmtk_dev *bdev);
+void btmtk_reg_hif_hook(struct hif_hook_ptr *hook);
+int btmtk_main_cif_initialize(struct btmtk_dev *bdev, int hci_bus);
+void btmtk_main_cif_uninitialize(struct btmtk_dev *bdev, int hci_bus);
+int btmtk_main_woble_initialize(struct btmtk_dev *bdev);
+int btmtk_main_cif_disconnect_notify(struct btmtk_dev *bdev, int hci_bus);
+int btmtk_cif_send_calibration(struct btmtk_dev *bdev);
+int btmtk_send_assert_cmd(struct btmtk_dev *bdev);
+int btmtk_efuse_read(struct btmtk_dev *bdev, u16 addr, u8 *value);
+
+void btmtk_set_country_code_from_wifi(char *code);
+
+#endif /* __BTMTK_MAIN_H__ */
diff --git a/drivers/bluetooth/include/sdio/btmtk_sdio.h b/drivers/bluetooth/include/sdio/btmtk_sdio.h
new file mode 100644
index 000000000000..ae1067cea0e0
--- /dev/null
+++ b/drivers/bluetooth/include/sdio/btmtk_sdio.h
@@ -0,0 +1,147 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2016,2017 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef _BTMTK_SDIO_H_
+#define _BTMTK_SDIO_H_
+/* It's for reset procedure */
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/module.h>
+
+#include <linux/of_gpio.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "btmtk_define.h"
+#include "btmtk_main.h"
+#include "btmtk_buffer_mode.h"
+
+#ifndef SDIO_DEBUG
+#define SDIO_DEBUG 0
+#endif
+
+/**
+ * Card-relate definition.
+ */
+#define SDIO_VENDOR_ID_MEDIATEK 0x037A
+
+#define HCI_HEADER_LEN 4
+
+#define MTK_STP_TLR_SIZE 2
+#define STP_HEADER_LEN 4
+#define STP_HEADER_CRC_LEN 2
+#define HCI_MAX_COMMAND_SIZE 255
+#define URB_MAX_BUFFER_SIZE (4*1024)
+#define BTMTK_SDIO_FUNC 2
+
+
+/* common register address */
+#define CCIR 0x0000
+#define CHLPCR 0x0004
+#define CSDIOCSR 0x0008
+#define CHCR 0x000C
+#define CHISR 0x0010
+#define CHIER 0x0014
+#define CTDR 0x0018
+#define CRDR 0x001C
+#define CTFSR 0x0020
+#define CRPLR 0x0024
+#define PD2HRM0R 0x00DC
+#define SWPCDBGR 0x0154
+/* CHLPCR */
+#define C_FW_INT_EN_SET 0x00000001
+#define C_FW_INT_EN_CLEAR 0x00000002
+/* CHISR */
+#define RX_PKT_LEN 0xFFFF0000
+#define FIRMWARE_INT 0x0000FE00
+/* MCU notify host dirver for L0.5 reset */
+#define FIRMWARE_INT_BIT31 0x80000000
+/* MCU notify host driver for coredump */
+#define FIRMWARE_INT_BIT15 0x00008000
+#define TX_FIFO_OVERFLOW 0x00000100
+#define FW_INT_IND_INDICATOR 0x00000080
+#define TX_COMPLETE_COUNT 0x00000070
+#define TX_UNDER_THOLD 0x00000008
+#define TX_EMPTY 0x00000004
+#define RX_DONE 0x00000002
+#define FW_OWN_BACK_INT 0x00000001
+
+/* MCU address offset */
+#define MCU_ADDRESS_OFFSET_CMD 12
+#define MCU_ADDRESS_OFFSET_EVT 16
+
+/* wifi CR */
+#define CONDBGCR 0x0034
+#define CONDBGCR_SEL 0x0040
+#define SDIO_CTRL_EN (1 << 31)
+#define WM_MONITER_SEL (~(0x40000000))
+#define PC_MONITER_SEL (~(0x20000000))
+#define PC_IDX_SWH(val, idx) ((val & (~(0x3F << 16))) | ((0x3F & idx) << 16))
+
+typedef int (*pdwnc_func) (u8 fgReset);
+typedef int (*reset_func_ptr2) (unsigned int gpio, int init_value);
+typedef int (*set_gpio_low)(u8 gpio);
+typedef int (*set_gpio_high)(u8 gpio);
+
+
+/**
+ * Send cmd dispatch evt
+ */
+#define HCI_EV_VENDOR 0xff
+#define SDIO_BLOCK_SIZE 512
+#define SDIO_RW_RETRY_COUNT 500
+#define MTK_SDIO_PACKET_HEADER_SIZE 4
+
+/* Driver & FW own related */
+#define DRIVER_OWN 0
+#define FW_OWN 1
+#define SET_OWN_LOOP_COUNT 20
+
+struct btmtk_sdio_hdr {
+ /* For SDIO Header */
+ __le16 len;
+ __le16 reserved;
+ /* For hci type */
+ u8 bt_type;
+} __packed;
+
+struct btmtk_sdio_thread {
+ struct task_struct *task;
+ wait_queue_head_t wait_q;
+ void *priv;
+ u8 thread_status;
+};
+
+struct btmtk_sdio_dev {
+ struct sdio_func *func;
+
+ bool no_fw_own;
+ atomic_t int_count;
+ atomic_t tx_rdy;
+
+ /* TODO, need to confirm the max size of urb data, also need to confirm
+ * whether intr_complete and bulk_complete and soc_complete can all share
+ * this urb_transfer_buf
+ */
+ unsigned char *transfer_buf;
+ unsigned char *sdio_packet;
+
+ struct sk_buff_head tx_queue;
+ struct btmtk_sdio_thread sdio_thread;
+
+ struct btmtk_buffer_mode_struct *buffer_mode;
+};
+#endif
diff --git a/drivers/bluetooth/include/uart/btmtk_uart.h b/drivers/bluetooth/include/uart/btmtk_uart.h
new file mode 100644
index 000000000000..e6f85778cee2
--- /dev/null
+++ b/drivers/bluetooth/include/uart/btmtk_uart.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2016,2017 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef _BTMTK_UART_H_
+#define _BTMTK_UART_H_
+#include "btmtk_define.h"
+
+
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/serial.h>
+
+#define HCI_HEADER_LEN 4
+
+struct mtk_stp_hdr {
+ u8 prefix;
+ __be16 dlen;
+ u8 cs;
+} __packed;
+#define MTK_STP_TLR_SIZE 2
+#define STP_HEADER_LEN 4
+#define STP_HEADER_CRC_LEN 2
+
+
+struct btmtk_uart_dev {
+ struct hci_dev *hdev;
+ struct tty_struct *tty;
+ unsigned long hdev_flags;
+
+ /* For tx queue */
+ struct sk_buff *tx_skb;
+ unsigned long tx_state;
+
+ /* For rx queue */
+ struct sk_buff *rx_skb;
+ unsigned long rx_state;
+
+ struct sk_buff *evt_skb;
+ wait_queue_head_t p_wait_event_q;
+
+ unsigned int subsys_reset;
+
+ u8 stp_pad[6];
+ u8 stp_cursor;
+ u16 stp_dlen;
+};
+
+
+/**
+ * Maximum rom patch file name length
+ */
+#define MAX_BIN_FILE_NAME_LEN 32
+
+#define N_MTK (15+1)
+/**
+ * Upper layeard IOCTL
+ */
+#define HCIUARTSETPROTO _IOW('U', 200, int)
+#define HCIUARTSETBAUD _IOW('U', 201, int)
+#define HCIUARTGETBAUD _IOW('U', 202, int)
+#define HCIUARTSETSTP _IOW('U', 203, int)
+#define HCIUARTLOADPATCH _IOW('U', 204, int)
+#define HCIUARTSETWAKEUP _IOW('U', 205, int)
+
+/**
+ * Send cmd dispatch evt
+ */
+#define RETRY_TIMES 10
+#define HCI_EV_VENDOR 0xff
+
+#define N_MTK (15+1)
+
+int btmtk_cif_send_calibration(struct hci_dev *hdev);
+#endif
+
diff --git a/drivers/bluetooth/include/usb/btmtk_usb.h b/drivers/bluetooth/include/usb/btmtk_usb.h
new file mode 100644
index 000000000000..5c4b31783c25
--- /dev/null
+++ b/drivers/bluetooth/include/usb/btmtk_usb.h
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2016,2017 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#ifndef _BTMTK_USB_H_
+#define _BTMTK_USB_H_
+#include <linux/usb.h>
+#include "btmtk_define.h"
+#include "btmtk_main.h"
+
+#define HCI_MAX_COMMAND_SIZE 255
+#define URB_MAX_BUFFER_SIZE (4*1024)
+
+#define BT0_MCU_INTERFACE_NUM 0
+#define BT1_MCU_INTERFACE_NUM 3
+#define BT_MCU_INTERFACE_NUM_MAX 4
+#define BT_MCU_NUM_MAX 2
+
+typedef int (*pdwnc_func) (u8 fgReset);
+typedef int (*reset_func_ptr2) (unsigned int gpio, int init_value);
+typedef int (*set_gpio_low)(u8 gpio);
+typedef int (*set_gpio_high)(u8 gpio);
+
+/**
+ * Send cmd dispatch evt
+ */
+#define HCI_EV_VENDOR 0xff
+#define HCI_USB_IO_BUF_SIZE 256
+
+
+/* UHW CR mapping */
+#define BT_MISC 0x70002510
+#define BT_SUBSYS_RST 0x70002610
+#define UDMA_INT_STA_BT 0x74000024
+#define UDMA_INT_STA_BT1 0x74000308
+#define BT_WDT_STATUS 0x740003A0
+#define EP_RST_OPT 0x74011890
+#define EP_RST_IN_OUT_OPT 0x00010001
+
+extern u8 wmt_over_hci_header[];
+
+struct btmtk_cif_chip_reset {
+ /* For Whole chip reset */
+ pdwnc_func pf_pdwndFunc;
+ reset_func_ptr2 pf_resetFunc2;
+ set_gpio_low pf_lowFunc;
+ set_gpio_high pf_highFunc;
+};
+
+struct btmtk_usb_dev {
+ struct usb_endpoint_descriptor *intr_ep;
+ /* EP10 OUT */
+ struct usb_endpoint_descriptor *intr_iso_tx_ep;
+ /* EP10 IN */
+ struct usb_endpoint_descriptor *intr_iso_rx_ep;
+ /* BULK CMD EP1 OUT or EP 11 OUT */
+ struct usb_endpoint_descriptor *bulk_cmd_tx_ep;
+ /* EP15 in for reset */
+ struct usb_endpoint_descriptor *reset_intr_ep;
+ struct usb_endpoint_descriptor *bulk_tx_ep;
+ struct usb_endpoint_descriptor *bulk_rx_ep;
+ struct usb_endpoint_descriptor *isoc_tx_ep;
+ struct usb_endpoint_descriptor *isoc_rx_ep;
+
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct usb_interface *isoc;
+ struct usb_interface *iso_channel;
+
+
+ struct usb_anchor tx_anchor;
+ struct usb_anchor intr_anchor;
+ struct usb_anchor bulk_anchor;
+ struct usb_anchor isoc_anchor;
+ struct usb_anchor ctrl_anchor;
+ struct usb_anchor ble_isoc_anchor;
+
+ __u8 cmdreq_type;
+ __u8 cmdreq;
+
+ int new_isoc_altsetting;
+ int new_isoc_altsetting_interface;
+
+ unsigned char *o_usb_buf;
+
+ unsigned char *urb_intr_buf;
+ unsigned char *urb_bulk_buf;
+ unsigned char *urb_ble_isoc_buf;
+};
+#endif
diff --git a/drivers/bluetooth/sdio/btmtksdio.c b/drivers/bluetooth/sdio/btmtksdio.c
new file mode 100644
index 000000000000..d686de310f4c
--- /dev/null
+++ b/drivers/bluetooth/sdio/btmtksdio.c
@@ -0,0 +1,2004 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+#include "btmtk_define.h"
+#include "btmtk_sdio.h"
+#include "btmtk_main.h"
+
+static char event_need_compare[EVENT_COMPARE_SIZE] = {0};
+static char event_need_compare_len;
+static char event_compare_status;
+const u8 READ_ADDRESS_EVENT[] = { 0x0E, 0x0A, 0x01, 0x09, 0x10, 0x00 };
+
+static DEFINE_MUTEX(btmtk_sdio_ops_mutex);
+#define SDIO_OPS_MUTEX_LOCK() mutex_lock(&btmtk_sdio_ops_mutex)
+#define SDIO_OPS_MUTEX_UNLOCK() mutex_unlock(&btmtk_sdio_ops_mutex)
+
+static DEFINE_MUTEX(btmtk_sdio_debug_mutex);
+#define SDIO_DEBUG_MUTEX_LOCK() mutex_lock(&btmtk_sdio_debug_mutex)
+#define SDIO_DEBUG_MUTEX_UNLOCK() mutex_unlock(&btmtk_sdio_debug_mutex)
+
+static int btmtk_sdio_readl(u32 offset, u32 *val, struct sdio_func *func);
+static int btmtk_sdio_writel(u32 offset, u32 val, struct sdio_func *func);
+
+static int btmtk_sdio_read_bt_mcu_pc(u32 *val);
+static int btmtk_sdio_read_conn_infra_pc(u32 *val);
+
+#define DUMP_FW_PC(cif_dev) \
+do { \
+ u32 __value = 0; \
+ btmtk_sdio_read_bt_mcu_pc(&__value); \
+ BTMTK_INFO("%s, BT mcu pc: 0x%08X", __func__, __value); \
+ btmtk_sdio_read_conn_infra_pc(&__value); \
+ BTMTK_INFO("%s, conn infra pc: 0x%08X", __func__, __value); \
+} while (0)
+
+static struct btmtk_sdio_dev g_sdio_dev;
+
+static const struct sdio_device_id btmtk_sdio_tabls[] = {
+ /* Mediatek SD8688 Bluetooth device */
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7663) },
+
+ /* Bring-up only */
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7668) },
+
+ { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7961) },
+
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(sdio, btmtk_sdio_tabls);
+
+#if SDIO_DEBUG
+#define RX_DEBUG_ENTRY_NUM 50
+enum {
+ CHISR_r_1 = 0,
+ CHISR_r_2,
+ CRPLR_r,
+ PD2HRM0R_r,
+ SDIO_DEBUG_CR_MAX,
+ RX_TIMESTAMP,
+ RX_BUF
+};
+
+struct rx_debug_struct {
+ unsigned int rx_intr_timestamp;
+ u32 cr[SDIO_DEBUG_CR_MAX];
+ u8 buf[16];
+};
+
+static struct rx_debug_struct rx_debug[RX_DEBUG_ENTRY_NUM];
+static int rx_debug_index;
+
+static int rx_done_cnt;
+static int tx_empty_cnt;
+static int intr_cnt;
+static int driver_own_cnt;
+static int fw_own_cnt;
+
+static unsigned int btmtk_sdio_hci_snoop_get_microseconds(void)
+{
+ struct timeval now;
+
+ do_gettimeofday(&now);
+ return now.tv_sec * 1000000 + now.tv_usec;
+}
+
+void rx_debug_print(void)
+{
+ int i;
+ int j = rx_debug_index;
+
+ BTMTK_ERR("%s: rx_done_cnt = %d, tx_empty_cnt = %d", __func__, rx_done_cnt, tx_empty_cnt);
+ BTMTK_ERR("intr_cnt = %d, driver_own_cnt = %d, fw_own_cnt = %d",
+ intr_cnt, driver_own_cnt, fw_own_cnt);
+ for (i = 0; i < RX_DEBUG_ENTRY_NUM; i++) {
+ BTMTK_ERR("%02d: timestamp = %u, CHISR_r_1 = 0x%08x, CHISR_r_2 = 0x%08x",
+ i, rx_debug[j].rx_intr_timestamp,
+ rx_debug[j].cr[CHISR_r_1], rx_debug[j].cr[CHISR_r_2]);
+ BTMTK_ERR("CRPLR = 0x%08x, PD2HRM0R = 0x%08x",
+ rx_debug[j].cr[CRPLR_r], rx_debug[j].cr[PD2HRM0R_r]);
+ BTMTK_ERR("buf = %02x %02x %02x %02x %02x %02x %02x",
+ rx_debug[j].buf[0], rx_debug[j].buf[1],
+ rx_debug[j].buf[2], rx_debug[j].buf[3],
+ rx_debug[j].buf[4], rx_debug[j].buf[5],
+ rx_debug[j].buf[6], rx_debug[j].buf[7]);
+ BTMTK_ERR("02x %02x %02x %02x %02x %02x %02x %02x %02x",
+ rx_debug[j].buf[8], rx_debug[j].buf[9],
+ rx_debug[j].buf[10], rx_debug[j].buf[11],
+ rx_debug[j].buf[12], rx_debug[j].buf[13],
+ rx_debug[j].buf[14], rx_debug[j].buf[15]);
+ if (j == 0)
+ j = RX_DEBUG_ENTRY_NUM;
+ j--;
+ }
+}
+
+void rx_debug_save(int type, u32 value, u8 *buf)
+{
+ switch (type) {
+ case CHISR_r_1:
+ case CHISR_r_2:
+ case CRPLR_r:
+ case PD2HRM0R_r:
+ rx_debug[rx_debug_index].cr[type] = value;
+ break;
+ case RX_TIMESTAMP:
+ rx_debug_index++;
+ if (rx_debug_index == RX_DEBUG_ENTRY_NUM)
+ rx_debug_index = 0;
+ rx_debug[rx_debug_index].rx_intr_timestamp =
+ btmtk_sdio_hci_snoop_get_microseconds();
+ break;
+ case RX_BUF:
+ memset(rx_debug[rx_debug_index].buf, 0, 16);
+ memcpy(rx_debug[rx_debug_index].buf, buf, 16);
+ break;
+ }
+}
+#endif
+
+static void btmtk_sdio_cif_mutex_lock(struct btmtk_dev *bdev)
+{
+ SDIO_OPS_MUTEX_LOCK();
+}
+
+static void btmtk_sdio_cif_mutex_unlock(struct btmtk_dev *bdev)
+{
+ SDIO_OPS_MUTEX_UNLOCK();
+}
+
+void btmtk_sdio_set_no_fwn_own(struct btmtk_sdio_dev *cif_dev, int flag)
+{
+ if (cif_dev->no_fw_own != flag)
+ BTMTK_INFO("%s set no_fw_own %d", __func__, flag);
+ cif_dev->no_fw_own = flag;
+}
+
+int btmtk_sdio_set_own_back(struct btmtk_sdio_dev *cif_dev, int owntype, int retry)
+{
+ /*Set driver own*/
+ int ret = 0;
+ u32 u32LoopCount = 0;
+ u32 u32PollNum = 0;
+ u32 u32ReadCRValue = 0;
+ u32 ownValue = 0;
+ int i = 0;
+
+ BTMTK_DBG("%s owntype %d", __func__, owntype);
+
+ if (owntype == FW_OWN) {
+ if (cif_dev->no_fw_own)
+ return ret;
+ }
+
+ ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue, cif_dev->func);
+
+ BTMTK_DBG("%s CHLPCR = 0x%0x", __func__, u32ReadCRValue);
+
+ /* For CHLPCR, bit 8 could help us to check driver own or fw own
+ * 0: COM driver doesn't have ownership
+ * 1: COM driver has ownership
+ */
+ if (owntype == DRIVER_OWN &&
+ (u32ReadCRValue & 0x100) == 0x100) {
+ goto set_own_end;
+ } else if (owntype == FW_OWN &&
+ (u32ReadCRValue & 0x100) == 0) {
+ goto set_own_end;
+ }
+
+ if (owntype == DRIVER_OWN)
+ ownValue = 0x00000200;
+ else
+ ownValue = 0x00000100;
+
+retry_own:
+ /* Write CR for Driver or FW own */
+ ret = btmtk_sdio_writel(CHLPCR, ownValue, cif_dev->func);
+ if (ret) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ u32LoopCount = SET_OWN_LOOP_COUNT;
+
+ if (owntype == DRIVER_OWN) {
+ do {
+ usleep_range(100, 200);
+ ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue, cif_dev->func);
+ u32LoopCount--;
+ u32PollNum++;
+ BTMTK_DBG("%s DRIVER_OWN btmtk_sdio_readl(%d) CHLPCR 0x%x",
+ __func__, u32PollNum, u32ReadCRValue);
+ } while ((u32LoopCount > 0) &&
+ ((u32ReadCRValue & 0x100) != 0x100));
+
+ if ((u32LoopCount == 0) && (0x100 != (u32ReadCRValue & 0x100))
+ && (retry > 0)) {
+ BTMTK_WARN("%s retry set_check driver own(%d), CHLPCR 0x%x",
+ __func__, u32PollNum, u32ReadCRValue);
+ for (i = 0; i < 3; i++)
+ DUMP_FW_PC(cif_dev);
+
+ retry--;
+ mdelay(5);
+ goto retry_own;
+ }
+ } else {
+ /*
+ * Can't check result for fw_own
+ * Because it will wakeup sdio hw
+ */
+ goto done;
+ }
+
+ BTMTK_DBG("%s CHLPCR(0x%x), is 0x%x",
+ __func__, CHLPCR, u32ReadCRValue);
+
+ if (owntype == DRIVER_OWN) {
+ if ((u32ReadCRValue & 0x100) == 0x100) {
+ BTMTK_DBG("%s check %04x, is 0x100 driver own success",
+ __func__, CHLPCR);
+ } else {
+ BTMTK_DBG("%s check %04x, is %x shuld be 0x100",
+ __func__, CHLPCR, u32ReadCRValue);
+ ret = -EINVAL;
+ goto done;
+ }
+ }
+
+done:
+ if (owntype == DRIVER_OWN) {
+#if SDIO_DEBUG
+ driver_own_cnt++;
+#endif
+ if (ret) {
+ BTMTK_ERR("%s set driver own fail", __func__);
+ for (i = 0; i < 8; i++) {
+ DUMP_FW_PC(cif_dev);
+ msleep(200);
+ }
+ } else
+ BTMTK_DBG("%s set driver own success", __func__);
+ } else if (owntype == FW_OWN) {
+#if SDIO_DEBUG
+ fw_own_cnt++;
+#endif
+ if (ret)
+ BTMTK_ERR("%s set FW own fail", __func__);
+ else
+ BTMTK_DBG("%s set FW own success", __func__);
+ } else
+ BTMTK_ERR("%s unknown type %d", __func__, owntype);
+
+set_own_end:
+ return ret;
+}
+
+static int btmtk_sdio_read_register(struct btmtk_dev *bdev, u32 reg, u32 *val)
+{
+ int ret;
+ u8 cmd[] = {0x01, 0x6F, 0xFC, 0x0C,
+ 0x01, 0x08, 0x08, 0x00,
+ 0x02, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00};
+
+ u8 event[] = {0x04, 0xE4, 0x10, 0x02,
+ 0x08, 0x0C, 0x00, 0x00,
+ 0x00, 0x00, 0x01};
+
+ BTMTK_INFO("%s: read cr %x", __func__, reg);
+
+ memcpy(&cmd[MCU_ADDRESS_OFFSET_CMD], &reg, sizeof(reg));
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event), 20,
+ 20, BTMTK_TX_CMD_FROM_DRV);
+
+ memcpy(val, bdev->io_buf + MCU_ADDRESS_OFFSET_EVT - HCI_TYPE_SIZE, sizeof(u32));
+ *val = le32_to_cpu(*val);
+
+ BTMTK_INFO("%s: reg=%x, value=0x%08x", __func__, reg, *val);
+
+ return 0;
+}
+
+static int btmtk_sdio_write_register(struct btmtk_dev *bdev, u32 reg, u32 val)
+{
+ BTMTK_INFO("%s: reg=%x, value=0x%08x, not support", __func__, reg, val);
+ return 0;
+}
+
+int btmtk_cif_send_calibration(struct btmtk_dev *bdev)
+{
+ return 0;
+}
+
+static int btmtk_cif_allocate_memory(struct btmtk_sdio_dev *cif_dev)
+{
+ if (cif_dev->transfer_buf == NULL) {
+ cif_dev->transfer_buf = kzalloc(URB_MAX_BUFFER_SIZE, GFP_KERNEL);
+ if (!cif_dev->transfer_buf) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->transfer_buf)", __func__);
+ return -1;
+ }
+ }
+
+ if (cif_dev->sdio_packet == NULL) {
+ cif_dev->sdio_packet = kzalloc(URB_MAX_BUFFER_SIZE, GFP_KERNEL);
+ if (!cif_dev->sdio_packet) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->transfer_buf)", __func__);
+ return -1;
+ }
+ }
+
+ BTMTK_INFO("%s: Done", __func__);
+ return 0;
+}
+
+static void btmtk_cif_free_memory(struct btmtk_sdio_dev *cif_dev)
+{
+ kfree(cif_dev->transfer_buf);
+ cif_dev->transfer_buf = NULL;
+
+ kfree(cif_dev->sdio_packet);
+ cif_dev->sdio_packet = NULL;
+
+ BTMTK_INFO("%s: Success", __func__);
+}
+
+int btmtk_sdio_read_wifi_mcu_pc(u8 PcLogSel, u32 *val)
+{
+ int ret = 0;
+ unsigned int value = 0;
+
+ if (!g_sdio_dev.func)
+ return -EINVAL;
+
+ SDIO_DEBUG_MUTEX_LOCK();
+
+ ret = btmtk_sdio_readl(CONDBGCR_SEL, &value, g_sdio_dev.func);
+ value |= SDIO_CTRL_EN;
+ value &= WM_MONITER_SEL;
+ value &= PC_MONITER_SEL;
+ value = PC_IDX_SWH(value, PcLogSel);
+
+ ret = btmtk_sdio_writel(CONDBGCR_SEL, value, g_sdio_dev.func);
+ ret = btmtk_sdio_readl(CONDBGCR, val, g_sdio_dev.func);
+
+ SDIO_DEBUG_MUTEX_UNLOCK();
+
+ return 0;
+}
+EXPORT_SYMBOL(btmtk_sdio_read_wifi_mcu_pc);
+
+int btmtk_sdio_read_bt_mcu_pc(u32 *val)
+{
+ if (!g_sdio_dev.func)
+ return -EINVAL;
+
+ SDIO_DEBUG_MUTEX_LOCK();
+
+ btmtk_sdio_writel(0x30, 0xFD, g_sdio_dev.func);
+ btmtk_sdio_readl(0x2c, val, g_sdio_dev.func);
+
+ SDIO_DEBUG_MUTEX_UNLOCK();
+
+ return 0;
+}
+EXPORT_SYMBOL(btmtk_sdio_read_bt_mcu_pc);
+
+int btmtk_sdio_read_conn_infra_pc(u32 *val)
+{
+ if (!g_sdio_dev.func)
+ return -EINVAL;
+
+ SDIO_DEBUG_MUTEX_LOCK();
+
+ btmtk_sdio_writel(0x3C, 0x9F1E0000, g_sdio_dev.func);
+ btmtk_sdio_readl(0x38, val, g_sdio_dev.func);
+
+ SDIO_DEBUG_MUTEX_UNLOCK();
+
+ return 0;
+}
+EXPORT_SYMBOL(btmtk_sdio_read_conn_infra_pc);
+
+static int btmtk_sdio_open(struct hci_dev *hdev)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_sdio_dev *cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+
+ BTMTK_INFO("%s enter!", __func__);
+ skb_queue_purge(&cif_dev->tx_queue);
+
+#if SDIO_DEBUG
+ rx_done_cnt = 0;
+ tx_empty_cnt = 0;
+ intr_cnt = 0;
+ driver_own_cnt = 0;
+ fw_own_cnt = 0;
+#endif
+
+ return 0;
+}
+
+static int btmtk_sdio_close(struct hci_dev *hdev)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+
+ BTMTK_INFO("%s enter!", __func__);
+ cancel_work_sync(&bdev->reset_waker);
+ return 0;
+}
+
+static void btmtk_sdio_open_done(struct btmtk_dev *bdev)
+{
+ struct btmtk_sdio_dev *cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+
+ BTMTK_INFO("%s enter!", __func__);
+ (void)btmtk_buffer_mode_send(cif_dev->buffer_mode);
+}
+
+static int btmtk_sdio_writesb(u32 offset, u8 *val, int len, struct sdio_func *func)
+{
+ u32 ret = 0;
+ u32 retry_count = 0;
+
+ if (!func) {
+ BTMTK_ERR("%s func is NULL", __func__);
+ return -EIO;
+ }
+
+ do {
+ sdio_claim_host(func);
+ ret = sdio_writesb(func, offset, val, len);
+ sdio_release_host(func);
+ retry_count++;
+ if (retry_count > SDIO_RW_RETRY_COUNT) {
+ BTMTK_ERR(" %s, ret:%d", __func__, ret);
+ break;
+ }
+ } while (ret);
+
+ return ret;
+}
+
+static int btmtk_sdio_readsb(u32 offset, u8 *val, int len, struct sdio_func *func)
+{
+ u32 ret = 0;
+ u32 retry_count = 0;
+
+ if (!func) {
+ BTMTK_ERR("%s func is NULL", __func__);
+ return -EIO;
+ }
+
+ do {
+ sdio_claim_host(func);
+ ret = sdio_readsb(func, val, offset, len);
+ sdio_release_host(func);
+ retry_count++;
+ if (retry_count > SDIO_RW_RETRY_COUNT) {
+ BTMTK_ERR(" %s, ret:%d", __func__, ret);
+ break;
+ }
+ } while (ret);
+
+ return ret;
+}
+
+int btmtk_sdio_writeb(u32 offset, u8 val, struct sdio_func *func)
+{
+ u32 ret = 0;
+ u32 retry_count = 0;
+
+ if (!func) {
+ BTMTK_ERR("%s func is NULL", __func__);
+ return -EIO;
+ }
+
+ do {
+ sdio_claim_host(func);
+ sdio_writeb(func, val, offset, &ret);
+ sdio_release_host(func);
+ retry_count++;
+ if (retry_count > SDIO_RW_RETRY_COUNT) {
+ BTMTK_ERR(" %s, ret:%d", __func__, ret);
+ break;
+ }
+ } while (ret);
+
+ return ret;
+}
+
+int btmtk_sdio_writel(u32 offset, u32 val, struct sdio_func *func)
+{
+ u32 ret = 0;
+ u32 retry_count = 0;
+
+ if (!func) {
+ BTMTK_ERR("%s func is NULL", __func__);
+ return -EIO;
+ }
+
+ do {
+ sdio_claim_host(func);
+ sdio_writel(func, val, offset, &ret);
+ sdio_release_host(func);
+ retry_count++;
+ if (retry_count > SDIO_RW_RETRY_COUNT) {
+ BTMTK_ERR(" %s, ret:%d", __func__, ret);
+ break;
+ }
+ } while (ret);
+
+ return ret;
+}
+
+int btmtk_sdio_readl(u32 offset, u32 *val, struct sdio_func *func)
+{
+ u32 ret = 0;
+ u32 retry_count = 0;
+
+ if (!func) {
+ BTMTK_ERR("func is NULL");
+ return -EIO;
+ }
+
+ do {
+ sdio_claim_host(func);
+ *val = sdio_readl(func, offset, &ret);
+ sdio_release_host(func);
+ retry_count++;
+ if (retry_count > SDIO_RW_RETRY_COUNT) {
+ BTMTK_ERR(" %s, ret:%d", __func__, ret);
+ break;
+ }
+ } while (ret);
+
+ return ret;
+}
+
+static int btmtk_sdio_readb(u32 offset, u8 *val, struct sdio_func *func)
+{
+ u32 ret = 0;
+ u32 retry_count = 0;
+
+ if (!func) {
+ BTMTK_ERR("%s func is NULL", __func__);
+ return -EIO;
+ }
+
+ do {
+ sdio_claim_host(func);
+ *val = sdio_readb(func, offset, &ret);
+ sdio_release_host(func);
+ retry_count++;
+ if (retry_count > SDIO_RW_RETRY_COUNT) {
+ BTMTK_ERR(" %s, ret:%d", __func__, ret);
+ break;
+ }
+ } while (ret);
+
+ return ret;
+}
+
+static void btmtk_sdio_print_debug_sr(struct btmtk_sdio_dev *cif_dev)
+{
+ u32 ret = 0;
+ u32 CCIR_Value = 0;
+ u32 CHLPCR_Value = 0;
+ u32 CSDIOCSR_Value = 0;
+ u32 CHISR_Value = 0;
+ u32 CHIER_Value = 0;
+ u32 CTFSR_Value = 0;
+ u32 CRPLR_Value = 0;
+ u32 SWPCDBGR_Value = 0;
+ unsigned char X0_Value = 0;
+ unsigned char X4_Value = 0;
+ unsigned char X5_Value = 0;
+ unsigned char F8_Value = 0;
+ unsigned char F9_Value = 0;
+ unsigned char FA_Value = 0;
+ unsigned char FB_Value = 0;
+ unsigned char FC_Value = 0;
+ unsigned char FD_Value = 0;
+ unsigned char FE_Value = 0;
+ unsigned char FF_Value = 0;
+
+ ret = btmtk_sdio_readl(CCIR, &CCIR_Value, cif_dev->func);
+ ret = btmtk_sdio_readl(CHLPCR, &CHLPCR_Value, cif_dev->func);
+ ret = btmtk_sdio_readl(CSDIOCSR, &CSDIOCSR_Value, cif_dev->func);
+ ret = btmtk_sdio_readl(CHISR, &CHISR_Value, cif_dev->func);
+ ret = btmtk_sdio_readl(CHIER, &CHIER_Value, cif_dev->func);
+ ret = btmtk_sdio_readl(CTFSR, &CTFSR_Value, cif_dev->func);
+ ret = btmtk_sdio_readl(CRPLR, &CRPLR_Value, cif_dev->func);
+ ret = btmtk_sdio_readl(SWPCDBGR, &SWPCDBGR_Value, cif_dev->func);
+ sdio_claim_host(cif_dev->func);
+ X0_Value = sdio_f0_readb(cif_dev->func, 0x00, &ret);
+ X4_Value = sdio_f0_readb(cif_dev->func, 0x04, &ret);
+ X5_Value = sdio_f0_readb(cif_dev->func, 0x05, &ret);
+ F8_Value = sdio_f0_readb(cif_dev->func, 0xF8, &ret);
+ F9_Value = sdio_f0_readb(cif_dev->func, 0xF9, &ret);
+ FA_Value = sdio_f0_readb(cif_dev->func, 0xFA, &ret);
+ FB_Value = sdio_f0_readb(cif_dev->func, 0xFB, &ret);
+ FC_Value = sdio_f0_readb(cif_dev->func, 0xFC, &ret);
+ FD_Value = sdio_f0_readb(cif_dev->func, 0xFD, &ret);
+ FE_Value = sdio_f0_readb(cif_dev->func, 0xFE, &ret);
+ FF_Value = sdio_f0_readb(cif_dev->func, 0xFF, &ret);
+ sdio_release_host(cif_dev->func);
+ BTMTK_INFO("CCIR: 0x%x, CHLPCR: 0x%x, CSDIOCSR: 0x%x, CHISR: 0x%x",
+ CCIR_Value, CHLPCR_Value, CSDIOCSR_Value, CHISR_Value);
+ BTMTK_INFO("CHIER: 0x%x, CTFSR: 0x%x, CRPLR: 0x%x, SWPCDBGR: 0x%x",
+ CHIER_Value, CTFSR_Value, CRPLR_Value, SWPCDBGR_Value);
+ BTMTK_INFO("CCCR 00: 0x%x, 04: 0x%x, 05: 0x%x",
+ X0_Value, X4_Value, X5_Value);
+ BTMTK_INFO("F8: 0x%x, F9: 0x%x, FA: 0x%x, FB: 0x%x",
+ F8_Value, F9_Value, FA_Value, FB_Value);
+ BTMTK_INFO("FC: 0x%x, FD: 0x%x, FE: 0x%x, FF: 0x%x",
+ FC_Value, FD_Value, FE_Value, FF_Value);
+}
+
+static int btmtk_sdio_enable_interrupt(int enable, struct sdio_func *func)
+{
+ u32 ret = 0;
+ u32 cr_value = 0;
+
+ BTMTK_DBG("%s enable=%d", __func__, enable);
+ if (enable)
+ cr_value |= C_FW_INT_EN_SET;
+ else
+ cr_value |= C_FW_INT_EN_CLEAR;
+
+ ret = btmtk_sdio_writel(CHLPCR, cr_value, func);
+
+ return ret;
+}
+
+int btmtk_sdio_send_cmd(struct btmtk_dev *bdev, struct sk_buff *skb,
+ int delay, int retry, int pkt_type)
+{
+ int ret = 0;
+ u32 crAddr = 0, crValue = 0;
+ ulong flags;
+ struct btmtk_sdio_dev *cif_dev = NULL;
+
+ /* for fw assert */
+ u8 fw_assert_cmd[] = { 0x01, 0x5B, 0xFD, 0x00 };
+ u8 fw_assert_cmd1[] = { 0x01, 0x6F, 0xFC, 0x05, 0x01, 0x02, 0x01, 0x00, 0x08 };
+ /* for read/write CR */
+ u8 notify_alt_evt[] = {0x0E, 0x04, 0x01, 0x03, 0x0c, 0x00};
+ struct sk_buff *evt_skb;
+
+ if (bdev == NULL) {
+ BTMTK_ERR("bdev is NULL");
+ ret = -1;
+ kfree_skb(skb);
+ skb = NULL;
+ goto exit;
+ }
+ cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+ if (cif_dev == NULL) {
+ BTMTK_ERR("cif_dev is NULL, bdev=%p", bdev);
+ ret = -1;
+ kfree_skb(skb);
+ skb = NULL;
+ goto exit;
+ }
+
+ /* For read write CR */
+ if (skb->len > 9) {
+ if (skb->data[0] == 0x01 && skb->data[1] == 0x6f && skb->data[2] == 0xfc &&
+ skb->data[3] == 0x0D && skb->data[4] == 0x01 &&
+ skb->data[5] == 0xff && skb->data[6] == 0x09 &&
+ skb->data[7] == 0x00 && skb->data[8] == 0x02) {
+ crAddr = ((skb->data[9] & 0xff) << 24) + ((skb->data[10] & 0xff) << 16)
+ + ((skb->data[11] & 0xff) << 8) + (skb->data[12] & 0xff);
+ crValue = ((skb->data[13] & 0xff) << 24) + ((skb->data[14] & 0xff) << 16)
+ + ((skb->data[15] & 0xff) << 8) + (skb->data[16] & 0xff);
+
+ BTMTK_INFO("%s crAddr=0x%08x crValue=0x%08x",
+ __func__, crAddr, crValue);
+
+ btmtk_sdio_writel(crAddr, crValue, cif_dev->func);
+ evt_skb = skb_copy(skb, GFP_KERNEL);
+ bt_cb(evt_skb)->pkt_type = HCI_EVENT_PKT;
+ notify_alt_evt[2] = (crValue & 0xFF000000) >> 24;
+ notify_alt_evt[3] = (crValue & 0x00FF0000) >> 16;
+ notify_alt_evt[4] = (crValue & 0x0000FF00) >> 8;
+ notify_alt_evt[5] = (crValue & 0x000000FF);
+ memcpy(evt_skb->data, &notify_alt_evt, sizeof(notify_alt_evt));
+ evt_skb->len = sizeof(notify_alt_evt);
+ hci_recv_frame(bdev->hdev, evt_skb);
+ kfree_skb(skb);
+ skb = NULL;
+ goto exit;
+ } else if (skb->data[0] == 0x01 && skb->data[1] == 0x6f && skb->data[2] == 0xfc &&
+ skb->data[3] == 0x09 && skb->data[4] == 0x01 &&
+ skb->data[5] == 0xff && skb->data[6] == 0x05 &&
+ skb->data[7] == 0x00 && skb->data[8] == 0x01) {
+
+ crAddr = ((skb->data[9] & 0xff) << 24) + ((skb->data[10] & 0xff) << 16) +
+ ((skb->data[11]&0xff) << 8) + (skb->data[12]&0xff);
+
+ btmtk_sdio_readl(crAddr, &crValue, cif_dev->func);
+ BTMTK_INFO("%s read crAddr=0x%08x crValue=0x%08x",
+ __func__, crAddr, crValue);
+ evt_skb = skb_copy(skb, GFP_KERNEL);
+ bt_cb(evt_skb)->pkt_type = HCI_EVENT_PKT;
+ //memcpy(&notify_alt_evt[2], &crValue, sizeof(crValue));
+ notify_alt_evt[2] = (crValue & 0xFF000000) >> 24;
+ notify_alt_evt[3] = (crValue & 0x00FF0000) >> 16;
+ notify_alt_evt[4] = (crValue & 0x0000FF00) >> 8;
+ notify_alt_evt[5] = (crValue & 0x000000FF);
+ memcpy(evt_skb->data, &notify_alt_evt, sizeof(notify_alt_evt));
+ evt_skb->len = sizeof(notify_alt_evt);
+ hci_recv_frame(bdev->hdev, evt_skb);
+ kfree_skb(skb);
+ skb = NULL;
+ goto exit;
+ }
+ }
+
+ if (skb->data[0] == 0x02 && skb->data[1] == 0x00 && skb->data[2] == 0x44) {
+ /* it's for ble iso, remove speicific header
+ * 02 00 44 len_a len_b + payload to 05 + payload
+ */
+ skb_pull(skb, 4);
+ skb->data[0] = HCI_ISO_PKT;
+ }
+
+ if ((skb->len == sizeof(fw_assert_cmd) &&
+ !memcmp(skb->data, fw_assert_cmd, sizeof(fw_assert_cmd)))
+ || (skb->len == sizeof(fw_assert_cmd1) &&
+ !memcmp(skb->data, fw_assert_cmd1, sizeof(fw_assert_cmd1)))) {
+ BTMTK_INFO_RAW(skb->data, skb->len, "%s: Trigger FW assert, dump CR", __func__);
+#if SDIO_DEBUG
+ rx_debug_print();
+#endif
+ btmtk_sdio_set_own_back(cif_dev, DRIVER_OWN, 20);
+ btmtk_sdio_set_no_fwn_own(cif_dev, 1);
+ btmtk_sdio_print_debug_sr(cif_dev);
+ }
+
+ spin_lock_irqsave(&bdev->txlock, flags);
+ skb_queue_tail(&cif_dev->tx_queue, skb);
+ spin_unlock_irqrestore(&bdev->txlock, flags);
+ wake_up_interruptible(&cif_dev->sdio_thread.wait_q);
+
+exit:
+ return ret;
+}
+
+static int btmtk_cif_recv_evt(struct btmtk_dev *bdev)
+{
+ int ret = 0;
+ u32 u32ReadCRValue = 0;
+ u32 u32ReadCRLEN = 0;
+ u32 sdio_header_length = 0;
+ int rx_length = 0;
+ int payload = 0;
+ u16 hci_pkt_len = 0;
+ u8 hci_type = 0;
+ struct btmtk_sdio_dev *cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+
+ memset(bdev->io_buf, 0, IO_BUF_SIZE);
+
+ /* keep polling method */
+ /* If interrupt method is working, we can remove it */
+ ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue, cif_dev->func);
+ BTMTK_DBG("%s: loop Get CHISR 0x%08X",
+ __func__, u32ReadCRValue);
+#if SDIO_DEBUG
+ rx_debug_save(CHISR_r_2, u32ReadCRValue, NULL);
+#endif
+
+ ret = btmtk_sdio_readl(CRPLR, &u32ReadCRLEN, cif_dev->func);
+#if SDIO_DEBUG
+ rx_debug_save(CRPLR_r, u32ReadCRLEN, NULL);
+#endif
+
+ rx_length = (u32ReadCRLEN & RX_PKT_LEN) >> 16;
+ if (rx_length == 0xFFFF || rx_length == 0) {
+ BTMTK_WARN("%s: rx_length = %d, error return -EIO", __func__, rx_length);
+ return -EIO;
+ }
+
+ BTMTK_DBG("%s: u32ReadCRValue = %08X", __func__, u32ReadCRValue);
+ u32ReadCRValue &= 0xFFFB;
+ ret = btmtk_sdio_writel(CHISR, u32ReadCRValue, cif_dev->func);
+ BTMTK_DBG("%s: write = %08X", __func__, u32ReadCRValue);
+ ret = btmtk_sdio_readl(PD2HRM0R, &u32ReadCRValue, cif_dev->func);
+#if SDIO_DEBUG
+ rx_debug_save(PD2HRM0R_r, u32ReadCRValue, NULL);
+#endif
+
+ ret = btmtk_sdio_readsb(CRDR, cif_dev->transfer_buf, rx_length, cif_dev->func);
+#if SDIO_DEBUG
+ rx_debug_save(RX_BUF, 0, cif_dev->transfer_buf);
+#endif
+ sdio_header_length = (cif_dev->transfer_buf[1] << 8);
+ sdio_header_length |= cif_dev->transfer_buf[0];
+ if (sdio_header_length != rx_length) {
+ BTMTK_ERR("%s sdio header length %d, rx_length %d mismatch, trigger assert",
+ __func__, sdio_header_length, rx_length);
+ BTMTK_INFO_RAW(cif_dev->transfer_buf, rx_length, "%s: raw data is :", __func__);
+ btmtk_send_assert_cmd(bdev);
+ return -EIO;
+ }
+
+ BTMTK_DBG_RAW(cif_dev->transfer_buf, rx_length, "%s: raw data is :", __func__);
+
+ hci_type = cif_dev->transfer_buf[MTK_SDIO_PACKET_HEADER_SIZE];
+ switch (hci_type) {
+ /* Please reference hci header format
+ * A = len
+ * acl : 02 xx_a xx_b AA AA + payload
+ * sco : 03 xx_c xx_d AA + payload
+ * evt : 04 xx AA + payload
+ * ISO : 05 xx_e xx_f AA_a AA_b + payload
+ */
+ case HCI_ACLDATA_PKT:
+ hci_pkt_len = cif_dev->transfer_buf[MTK_SDIO_PACKET_HEADER_SIZE + 3] +
+ (cif_dev->transfer_buf[MTK_SDIO_PACKET_HEADER_SIZE + 4] << 8) + 5;
+ break;
+ case HCI_SCODATA_PKT:
+ hci_pkt_len = cif_dev->transfer_buf[MTK_SDIO_PACKET_HEADER_SIZE + 4] + 4;
+ break;
+ case HCI_EVENT_PKT:
+ hci_pkt_len = cif_dev->transfer_buf[MTK_SDIO_PACKET_HEADER_SIZE + 2] + 3;
+ break;
+ case HCI_ISO_PKT:
+ hci_pkt_len = cif_dev->transfer_buf[MTK_SDIO_PACKET_HEADER_SIZE + 3] +
+ (cif_dev->transfer_buf[MTK_SDIO_PACKET_HEADER_SIZE + 4] << 8) + 4;
+ bdev->io_buf[0] = HCI_ACLDATA_PKT;
+ bdev->io_buf[1] = 0x00;
+ bdev->io_buf[2] = 0x44;
+ bdev->io_buf[3] = (hci_pkt_len & 0x00ff);
+ bdev->io_buf[4] = ((hci_pkt_len & 0xff00) >> 8);
+ memcpy(bdev->io_buf + 5, cif_dev->transfer_buf +
+ MTK_SDIO_PACKET_HEADER_SIZE + 1, hci_pkt_len);
+ memset(cif_dev->transfer_buf, 0, URB_MAX_BUFFER_SIZE);
+ hci_pkt_len += 5;
+ memcpy(cif_dev->transfer_buf + MTK_SDIO_PACKET_HEADER_SIZE,
+ bdev->io_buf, hci_pkt_len);
+ BTMTK_DBG_RAW(cif_dev->transfer_buf, hci_pkt_len, "%s: raw data is :", __func__);
+ break;
+ }
+ ret = hci_pkt_len;
+ bdev->recv_evt_len = hci_pkt_len;
+
+ BTMTK_DBG("%s sdio header length %d, rx_length %d, hci_pkt_len = %d",
+ __func__, sdio_header_length, rx_length, hci_pkt_len);
+ ret = btmtk_recv(bdev->hdev, cif_dev->transfer_buf +
+ MTK_SDIO_PACKET_HEADER_SIZE, hci_pkt_len);
+ if (cif_dev->transfer_buf[4] == HCI_EVENT_PKT) {
+ payload = rx_length - cif_dev->transfer_buf[6] - 3;
+ ret = rx_length - MTK_SDIO_PACKET_HEADER_SIZE - payload;
+ }
+
+ BTMTK_DBG("%s: done", __func__);
+ return ret;
+}
+
+int btmtk_sdio_event_filter(struct btmtk_dev *bdev, struct sk_buff *skb)
+{
+ if (event_compare_status == BTMTK_EVENT_COMPARE_STATE_NEED_COMPARE &&
+ skb->len >= event_need_compare_len) {
+ if (memcmp(skb->data, READ_ADDRESS_EVENT,
+ sizeof(READ_ADDRESS_EVENT)) == 0 && (skb->len == 12)) {
+ memcpy(bdev->bdaddr, &skb->data[6], BD_ADDRESS_SIZE);
+ BTMTK_INFO("GET BDADDR = %02X:%02X:%02X:%02X:%02X:%02X",
+ bdev->bdaddr[0], bdev->bdaddr[1], bdev->bdaddr[2],
+ bdev->bdaddr[3], bdev->bdaddr[4], bdev->bdaddr[5]);
+
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
+ } else if (memcmp(skb->data, event_need_compare,
+ event_need_compare_len) == 0) {
+ /* if it is wobx debug event, just print in kernel log, drop it
+ * by driver, don't send to stack
+ */
+ if (skb->data[0] == 0xE8)
+ BTMTK_INFO_RAW(skb->data, skb->len,
+ "%s: wobx debug log:", __func__);
+
+ /* If driver need to check result from skb, it can get from io_buf */
+ /* Such as chip_id, fw_version, etc. */
+ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+ memcpy(bdev->io_buf, skb->data, skb->len);
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
+ BTMTK_DBG("%s, compare success", __func__);
+ } else {
+ BTMTK_INFO("%s compare fail", __func__);
+ BTMTK_INFO_RAW(event_need_compare, event_need_compare_len,
+ "%s: event_need_compare:", __func__);
+ BTMTK_INFO_RAW(skb->data, skb->len, "%s: skb->data:", __func__);
+ return 0;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int btmtk_sdio_send_and_recv(struct btmtk_dev *bdev,
+ struct sk_buff *skb,
+ const uint8_t *event, const int event_len,
+ int delay, int retry, int pkt_type)
+{
+ unsigned long comp_event_timo = 0, start_time = 0;
+ int ret = -1;
+
+ if (event) {
+ if (event_len > EVENT_COMPARE_SIZE) {
+ BTMTK_ERR("%s, event_len (%d) > EVENT_COMPARE_SIZE(%d), error",
+ __func__, event_len, EVENT_COMPARE_SIZE);
+ ret = -1;
+ goto exit;
+ }
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_NEED_COMPARE;
+ memcpy(event_need_compare, event + 1, event_len - 1);
+ event_need_compare_len = event_len - 1;
+
+ start_time = jiffies;
+ /* check hci event /wmt event for SDIO/UART interface, check hci
+ * event for USB interface
+ */
+ comp_event_timo = jiffies + msecs_to_jiffies(WOBLE_COMP_EVENT_TIMO);
+ BTMTK_DBG("event_need_compare_len %d, event_compare_status %d",
+ event_need_compare_len, event_compare_status);
+ } else {
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
+ }
+
+ BTMTK_DBG_RAW(skb->data, skb->len, "%s, send, len = %d", __func__, skb->len);
+
+ ret = btmtk_sdio_send_cmd(bdev, skb, delay, retry, pkt_type);
+ if (ret < 0) {
+ BTMTK_ERR("%s btmtk_sdio_send_cmd failed!!", __func__);
+ goto exit;
+ }
+
+ do {
+ /* check if event_compare_success */
+ if (event_compare_status == BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS) {
+ ret = 0;
+ break;
+ }
+ usleep_range(10, 100);
+ } while (time_before(jiffies, comp_event_timo));
+
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_NOTHING_NEED_COMPARE;
+exit:
+ return ret;
+}
+
+static void btmtk_sdio_interrupt(struct sdio_func *func)
+{
+ struct btmtk_dev *bdev;
+ struct btmtk_sdio_dev *cif_dev;
+
+#if SDIO_DEBUG
+ rx_debug_save(RX_TIMESTAMP, 0, NULL);
+ intr_cnt++;
+#endif
+
+ bdev = sdio_get_drvdata(func);
+ if (!bdev)
+ return;
+ cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+
+ btmtk_sdio_enable_interrupt(0, cif_dev->func);
+ atomic_set(&cif_dev->int_count, 1);
+ wake_up_interruptible(&cif_dev->sdio_thread.wait_q);
+}
+
+static int btmtk_sdio_load_fw_patch_using_dma(struct btmtk_dev *bdev, u8 *image,
+ u8 *fwbuf, int section_dl_size, int section_offset)
+{
+ int cur_len = 0;
+ int ret = -1;
+ s32 sent_len = 0;
+ s32 sdio_len = 0;
+ u32 u32ReadCRValue = 0;
+ u32 block_count = 0;
+ u32 redundant = 0;
+ u32 delay_count = 0;
+ struct btmtk_sdio_dev *cif_dev = NULL;
+ u8 cmd[] = {0x02, 0x6F, 0xFC, 0x05, 0x00, 0x01, 0x01, 0x01, 0x00, PATCH_PHASE3};
+ u8 event[] = {0x04, 0xE4, 0x05, 0x02, 0x01, 0x01, 0x00, 0x00}; /* event[7] is status*/
+
+ cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+
+ if (bdev == NULL || image == NULL || fwbuf == NULL) {
+ BTMTK_ERR("%s: invalid parameters!", __func__);
+ return -1;
+ }
+
+ BTMTK_INFO("%s: loading rom patch... start", __func__);
+ btmtk_sdio_enable_interrupt(0, cif_dev->func);
+ while (section_dl_size != cur_len) {
+ if (!atomic_read(&cif_dev->tx_rdy)) {
+ ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue, cif_dev->func);
+ if ((TX_EMPTY & u32ReadCRValue) != 0) {
+ ret = btmtk_sdio_writel(CHISR,
+ (TX_EMPTY | TX_COMPLETE_COUNT), cif_dev->func);
+ if (ret != 0) {
+ BTMTK_ERR("%s: btmtk_sdio_writel fail", __func__);
+ goto enable_intr;
+ }
+ atomic_set(&cif_dev->tx_rdy, 1);
+ } else if (delay_count > 1000) {
+ BTMTK_ERR("%s: delay_count > 1000", __func__);
+ goto enable_intr;
+ } else {
+ udelay(200);
+ ++delay_count;
+ continue;
+ }
+ }
+
+ sent_len = (section_dl_size - cur_len) >= UPLOAD_PATCH_UNIT ?
+ UPLOAD_PATCH_UNIT : (section_dl_size - cur_len);
+
+ BTMTK_DBG("%s: sent_len = %d, cur_len = %d, delay_count = %d",
+ __func__, sent_len, cur_len, delay_count);
+
+ sdio_len = sent_len + MTK_SDIO_PACKET_HEADER_SIZE;
+ memset(image, 0, sdio_len);
+ image[0] = (sdio_len & 0x00FF);
+ image[1] = (sdio_len & 0xFF00) >> 8;
+ image[2] = 0;
+ image[3] = 0;
+
+ memcpy(image + MTK_SDIO_PACKET_HEADER_SIZE,
+ fwbuf + section_offset + cur_len,
+ sent_len);
+
+ block_count = sdio_len / SDIO_BLOCK_SIZE;
+ redundant = sdio_len % SDIO_BLOCK_SIZE;
+ if (redundant)
+ sdio_len = (block_count + 1) * SDIO_BLOCK_SIZE;
+
+ ret = btmtk_sdio_writesb(CTDR, image, sdio_len, cif_dev->func);
+ atomic_set(&cif_dev->tx_rdy, 0);
+ cur_len += sent_len;
+
+ if (ret < 0) {
+ BTMTK_ERR("%s: send patch failed, terminate", __func__);
+ goto enable_intr;
+ }
+ }
+ btmtk_sdio_enable_interrupt(1, cif_dev->func);
+
+ BTMTK_INFO("%s: send dl cmd", __func__);
+ ret = btmtk_main_send_cmd(bdev,
+ cmd, sizeof(cmd), event, sizeof(event),
+ PATCH_DOWNLOAD_PHASE3_DELAY_TIME,
+ PATCH_DOWNLOAD_PHASE3_RETRY,
+ BTMTK_TX_ACL_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_ERR("%s: send wmd dl cmd failed, terminate!", __func__);
+ return ret;
+ }
+
+ BTMTK_INFO("%s: loading rom patch... Done", __func__);
+ return ret;
+
+enable_intr:
+ btmtk_sdio_enable_interrupt(1, cif_dev->func);
+ return ret;
+}
+
+static int btmtk_sdio_register_dev(struct btmtk_sdio_dev *bdev)
+{
+ struct sdio_func *func;
+ u8 u8ReadCRValue = 0;
+ int ret = 0;
+
+ if (!bdev || !bdev->func) {
+ BTMTK_ERR("Error: card or function is NULL!");
+ ret = -EINVAL;
+ goto failed;
+ }
+
+ func = bdev->func;
+
+ sdio_claim_host(func);
+ ret = sdio_enable_func(func);
+ sdio_release_host(func);
+ if (ret) {
+ BTMTK_ERR("sdio_enable_func() failed: ret=%d", ret);
+ ret = -EIO;
+ goto failed;
+ }
+
+ btmtk_sdio_readb(SDIO_CCCR_IENx, &u8ReadCRValue, func);
+ BTMTK_INFO("before claim irq read SDIO_CCCR_IENx %x, func num %d",
+ u8ReadCRValue, func->num);
+
+ sdio_claim_host(func);
+ ret = sdio_claim_irq(func, btmtk_sdio_interrupt);
+ sdio_release_host(func);
+ if (ret) {
+ BTMTK_ERR("sdio_claim_irq failed: ret=%d", ret);
+ ret = -EIO;
+ goto disable_func;
+ }
+
+ BTMTK_INFO("sdio_claim_irq success: ret=%d", ret);
+
+ btmtk_sdio_readb(SDIO_CCCR_IENx, &u8ReadCRValue, func);
+ BTMTK_INFO("after claim irq read SDIO_CCCR_IENx %x", u8ReadCRValue);
+
+ sdio_claim_host(func);
+ ret = sdio_set_block_size(func, SDIO_BLOCK_SIZE);
+ sdio_release_host(func);
+ if (ret) {
+ pr_err("cannot set SDIO block size");
+ ret = -EIO;
+ goto release_irq;
+ }
+
+
+ return 0;
+
+release_irq:
+ sdio_release_irq(func);
+
+disable_func:
+ sdio_disable_func(func);
+
+failed:
+ pr_info("%s fail", __func__);
+ return ret;
+}
+
+static int btmtk_sdio_enable_host_int(struct btmtk_sdio_dev *cif_dev)
+{
+ int ret;
+ u32 read_data = 0;
+
+ if (!cif_dev || !cif_dev->func)
+ return -EINVAL;
+
+ /* workaround for some platform no host clock sometimes */
+
+ btmtk_sdio_readl(CSDIOCSR, &read_data, cif_dev->func);
+ BTMTK_INFO("%s read CSDIOCSR is 0x%X", __func__, read_data);
+ read_data |= 0x4;
+ btmtk_sdio_writel(CSDIOCSR, read_data, cif_dev->func);
+ BTMTK_INFO("%s write CSDIOCSR is 0x%X", __func__, read_data);
+
+ return ret;
+}
+
+static int btmtk_sdio_unregister_dev(struct btmtk_sdio_dev *cif_dev)
+{
+ if (cif_dev && cif_dev->func) {
+ sdio_claim_host(cif_dev->func);
+ sdio_release_irq(cif_dev->func);
+ sdio_disable_func(cif_dev->func);
+ sdio_release_host(cif_dev->func);
+ sdio_set_drvdata(cif_dev->func, NULL);
+ }
+ return 0;
+}
+
+static int btmtk_sdio_set_write_clear(struct btmtk_sdio_dev *cif_dev)
+{
+ u32 u32ReadCRValue = 0;
+ u32 ret = 0;
+
+ ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue, cif_dev->func);
+ if (ret) {
+ BTMTK_ERR("%s read CHCR error", __func__);
+ ret = EINVAL;
+ return ret;
+ }
+
+ u32ReadCRValue |= 0x00000002;
+ btmtk_sdio_writel(CHCR, u32ReadCRValue, cif_dev->func);
+ BTMTK_INFO("%s write CHCR 0x%08X", __func__, u32ReadCRValue);
+ ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue, cif_dev->func);
+ BTMTK_INFO("%s read CHCR 0x%08X", __func__, u32ReadCRValue);
+ if (u32ReadCRValue&0x00000002)
+ BTMTK_INFO("%s write clear", __func__);
+ else
+ BTMTK_INFO("%s read clear", __func__);
+
+ return ret;
+}
+
+static int btmtk_sdio_poll_subsys_done(struct btmtk_sdio_dev *cif_dev)
+{
+ u32 u32ReadCRValue = 0;
+ int retry = 100;
+
+// btmtk_sdio_writel(0x30, 0xFD, cif_dev->func);
+// BTMTK_INFO("%s write 0x30 = 0xFD, retry = %d", __func__, retry);
+ while (retry-- > 0) {
+// btmtk_sdio_readl(0x2c, &u32ReadCRValue, cif_dev->func);
+// BTMTK_INFO("%s read 0x2c = 0x%08X, retry = %d", __func__, u32ReadCRValue, retry);
+// btmtk_sdio_readl(CHLPCR, &u32ReadCRValue, cif_dev->func);
+// BTMTK_INFO("%s read CHLPCR 0x%08X, retry = %d", __func__, u32ReadCRValue, retry);
+// btmtk_sdio_readl(SWPCDBGR, &u32ReadCRValue, cif_dev->func);
+// BTMTK_INFO("%s read SWPCDBGR 0x%08X, retry = %d", __func__, u32ReadCRValue, retry);
+ btmtk_sdio_readl(CHCR, &u32ReadCRValue, cif_dev->func);
+ BTMTK_INFO("%s read CHCR 0x%08X, retry = %d", __func__, u32ReadCRValue, retry);
+ if (u32ReadCRValue & (0x1 << 8))
+ return 0;
+ msleep(20);
+ }
+
+ return -1;
+}
+
+static int btmtk_sdio_subsys_reset(struct btmtk_dev *bdev)
+{
+ struct btmtk_sdio_dev *cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+ u32 u32ReadCRValue = 0;
+ u32 ret = 0;
+
+ btmtk_sdio_set_no_fwn_own(cif_dev, 1);
+ btmtk_sdio_set_own_back(cif_dev, DRIVER_OWN, 20);
+
+ /* write CHCR[3] 0 */
+ ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue, cif_dev->func);
+ BTMTK_INFO("%s read CHCR 0x%08X", __func__, u32ReadCRValue);
+ u32ReadCRValue &= 0xFFFFFFF7;
+ BTMTK_INFO("%s write CHCR 0x%08X", __func__, u32ReadCRValue);
+ btmtk_sdio_writel(CHCR, u32ReadCRValue, cif_dev->func);
+
+ /* write CHCR[3] to 1 */
+ ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue, cif_dev->func);
+ BTMTK_INFO("%s read CHCR 0x%08X", __func__, u32ReadCRValue);
+ u32ReadCRValue |= 0x00000008;
+ BTMTK_INFO("%s write CHCR 0x%08X", __func__, u32ReadCRValue);
+ btmtk_sdio_writel(CHCR, u32ReadCRValue, cif_dev->func);
+
+ /* write CHCR[5] to 0 */
+ ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue, cif_dev->func);
+ BTMTK_INFO("%s read CHCR 0x%08X", __func__, u32ReadCRValue);
+ u32ReadCRValue &= 0xFFFFFFDF;
+ BTMTK_INFO("%s write CHCR 0x%08X", __func__, u32ReadCRValue);
+ btmtk_sdio_writel(CHCR, u32ReadCRValue, cif_dev->func);
+
+ /* write CHCR[5] to 1 */
+ ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue, cif_dev->func);
+ BTMTK_INFO("%s read CHCR 0x%08X", __func__, u32ReadCRValue);
+ u32ReadCRValue |= 0x00000020;
+ BTMTK_INFO("%s write CHCR 0x%08X", __func__, u32ReadCRValue);
+ btmtk_sdio_writel(CHCR, u32ReadCRValue, cif_dev->func);
+
+ /* Poll subsys reset done */
+ if (btmtk_sdio_poll_subsys_done(cif_dev))
+ return -EIO;
+
+ /* Do-init cr */
+ /* Disable the interrupts on the card */
+ btmtk_sdio_enable_host_int(cif_dev);
+ BTMTK_DBG("call btmtk_sdio_enable_host_int done");
+
+ /* Set interrupt output */
+ ret = btmtk_sdio_writel(CHIER, FIRMWARE_INT_BIT31 | FIRMWARE_INT|TX_FIFO_OVERFLOW |
+ FW_INT_IND_INDICATOR | TX_COMPLETE_COUNT |
+ TX_UNDER_THOLD | TX_EMPTY | RX_DONE, cif_dev->func);
+ if (ret) {
+ BTMTK_ERR("Set interrupt output fail(%d)", ret);
+ ret = -EIO;
+ return ret;
+ }
+
+ /* Enable interrupt output */
+ ret = btmtk_sdio_writel(CHLPCR, C_FW_INT_EN_SET, cif_dev->func);
+ if (ret) {
+ BTMTK_ERR("enable interrupt output fail(%d)", ret);
+ ret = -EIO;
+ return ret;
+ }
+
+ /* Adopt write clear method */
+ btmtk_sdio_set_write_clear(cif_dev);
+
+ ret = btmtk_sdio_readl(0, &u32ReadCRValue, cif_dev->func);
+ BTMTK_INFO("%s read chipid = %x", __func__, u32ReadCRValue);
+
+ return ret;
+}
+
+static int btmtk_sdio_whole_reset(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+ struct btmtk_sdio_dev *cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+ struct mmc_card *card = cif_dev->func->card;
+ struct mmc_host *host = NULL;
+
+ if ((card == NULL) || (card->host == NULL)) {
+ BTMTK_ERR("mmc structs are NULL");
+ return ret;
+ }
+
+ host = card->host;
+ if (host->rescan_entered != 0) {
+ host->rescan_entered = 0;
+ BTMTK_INFO("set mmc_host rescan to 0");
+ }
+
+ BTMTK_INFO("mmc_remove_host");
+ mmc_remove_host(host);
+
+ /* Replace hooked SDIO driver probe to new API;
+ * 1. It will be new kthread(state) after mmc_add_host;
+ * 2. Extend flexibility to notify us that HW reset was triggered,
+ * more flexiable on reviving in exchanging old/new kthread(state).
+ */
+ BTMTK_INFO("mmc_add_host");
+ ret = mmc_add_host(host);
+
+ BTMTK_INFO("mmc_add_host return %d", ret);
+ return ret;
+}
+
+/* bt_tx_wait_for_msg
+ *
+ * Check needing action of current bt status to wake up bt thread
+ *
+ * Arguments:
+ * [IN] bdev - bt driver control strcuture
+ *
+ * Return Value:
+ * return check - 1 for waking up bt thread, 0 otherwise
+ *
+ */
+static bool btmtk_thread_wait_for_msg(struct btmtk_sdio_dev *cif_dev)
+{
+ if (!skb_queue_empty(&cif_dev->tx_queue)) {
+ BTMTK_DBG("tx queue is not empty");
+ return true;
+ }
+
+ if (atomic_read(&cif_dev->int_count)) {
+ BTMTK_DBG("cif_dev->int_count is %d", atomic_read(&cif_dev->int_count));
+ return true;
+ }
+
+ if (kthread_should_stop()) {
+ BTMTK_DBG("kthread_should_stop");
+ return true;
+ }
+
+ return false;
+}
+
+static int btmtk_tx_pkt(struct btmtk_sdio_dev *cif_dev, struct sk_buff *skb)
+{
+ u8 MultiBluckCount = 0;
+ u8 redundant = 0;
+ int len = 0;
+ int ret = 0;
+
+ BTMTK_DBG("btmtk_tx_pkt");
+
+ cif_dev->sdio_packet[0] = (4 + skb->len) & 0xFF;
+ cif_dev->sdio_packet[1] = ((4 + skb->len) & 0xFF00) >> 8;
+
+ memcpy(cif_dev->sdio_packet + MTK_SDIO_PACKET_HEADER_SIZE, skb->data,
+ skb->len);
+ len = skb->len + MTK_SDIO_PACKET_HEADER_SIZE;
+ BTMTK_DBG_RAW(cif_dev->sdio_packet, len,
+ "%s: sent, len =%d:", __func__, len);
+
+ MultiBluckCount = len / SDIO_BLOCK_SIZE;
+ redundant = len % SDIO_BLOCK_SIZE;
+ if (redundant)
+ len = (MultiBluckCount+1)*SDIO_BLOCK_SIZE;
+
+ atomic_set(&cif_dev->tx_rdy, 0);
+ ret = btmtk_sdio_writesb(CTDR, cif_dev->sdio_packet, len, cif_dev->func);
+ if (ret < 0)
+ BTMTK_ERR("ret = %d", ret);
+ kfree_skb(skb);
+ return ret;
+}
+
+static int btmtk_sdio_interrupt_process(struct btmtk_dev *bdev)
+{
+ struct btmtk_sdio_dev *cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+ int ret = 0;
+ u32 u32ReadCRValue = 0;
+
+ ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue, cif_dev->func);
+#if SDIO_DEBUG
+ rx_debug_save(CHISR_r_1, u32ReadCRValue, NULL);
+#endif
+ BTMTK_DBG("%s CHISR 0x%08x", __func__, u32ReadCRValue);
+
+ if (u32ReadCRValue & FIRMWARE_INT_BIT15)
+ btmtk_sdio_set_no_fwn_own(cif_dev, 1);
+
+ if (u32ReadCRValue & FIRMWARE_INT_BIT31) {
+ /* It's read-only bit (WDT interrupt)
+ * Host can't modify it.
+ */
+ ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue, cif_dev->func);
+ BTMTK_INFO("%s CHISR 0x%08x", __func__, u32ReadCRValue);
+ /* FW can't send TX_EMPTY for 0xFD5B */
+ atomic_set(&cif_dev->tx_rdy, 1);
+ schedule_work(&bdev->reset_waker);
+ return ret;
+ }
+
+ if (TX_EMPTY & u32ReadCRValue) {
+ ret = btmtk_sdio_writel(CHISR, (TX_EMPTY | TX_COMPLETE_COUNT), cif_dev->func);
+ atomic_set(&cif_dev->tx_rdy, 1);
+ BTMTK_DBG("%s set tx_rdy true", __func__);
+#if SDIO_DEBUG
+ tx_empty_cnt++;
+#endif
+ }
+
+ if (RX_DONE & u32ReadCRValue)
+ ret = btmtk_cif_recv_evt(bdev);
+
+ ret = btmtk_sdio_enable_interrupt(1, cif_dev->func);
+ BTMTK_DBG("%s done, ret = %d", __func__, ret);
+ return ret;
+}
+
+
+/*
+ * This function handles the event generated by firmware, rx data
+ * received from firmware, and tx data sent from kernel.
+ */
+static int btmtk_sdio_main_thread(void *data)
+{
+ struct btmtk_dev *bdev = data;
+ struct btmtk_sdio_dev *cif_dev = NULL;
+ struct sk_buff *skb;
+ int ret = 0;
+ ulong flags;
+// struct sched_param param = { .sched_priority = 90 };/*RR 90 is the same as audio*/
+
+ cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+
+// sched_setscheduler(current, SCHED_RR, &param);
+
+ BTMTK_INFO("%s start running...", __func__);
+ for (;;) {
+ wait_event_interruptible(cif_dev->sdio_thread.wait_q,
+ btmtk_thread_wait_for_msg(cif_dev));
+ if (kthread_should_stop()) {
+ BTMTK_WARN("sdio_thread: break from main thread");
+ break;
+ }
+ BTMTK_DBG("%s doing...", __func__);
+
+ ret = btmtk_sdio_set_own_back(cif_dev, DRIVER_OWN, 20);
+ if (ret) {
+ BTMTK_ERR("set driver own return fail");
+ schedule_work(&bdev->reset_waker);
+ continue;
+ }
+
+ /* Do interrupt */
+ if (atomic_read(&cif_dev->int_count)) {
+ BTMTK_DBG("go int");
+// spin_lock_irqsave(&bdev->rxlock, flags);
+ atomic_set(&cif_dev->int_count, 0);
+// spin_unlock_irqrestore(&bdev->rxlock, flags);
+ if (btmtk_sdio_interrupt_process(bdev)) {
+ schedule_work(&bdev->reset_waker);
+ continue;
+ }
+ } else {
+ BTMTK_DBG("go tx");
+ }
+
+ /* Do TX */
+ if (!atomic_read(&cif_dev->tx_rdy)) {
+ BTMTK_DBG("tx_rdy == 0, continue");
+ continue;
+ }
+
+ spin_lock_irqsave(&bdev->txlock, flags);
+ skb = skb_dequeue(&cif_dev->tx_queue);
+ spin_unlock_irqrestore(&bdev->txlock, flags);
+ if (skb) {
+ ret = btmtk_tx_pkt(cif_dev, skb);
+ if (ret) {
+ BTMTK_ERR("tx pkt return fail %d", ret);
+ schedule_work(&bdev->reset_waker);
+ continue;
+ }
+ }
+
+ /* Confirm with Travis later */
+ /* Travis Hsieh: */
+ /* It shall be fine to set FW_OWN if no more task to do in this thread */
+ if (skb_queue_empty(&cif_dev->tx_queue)) {
+ ret = btmtk_sdio_set_own_back(cif_dev, FW_OWN, 20);
+ if (ret) {
+ BTMTK_ERR("set fw own return fail");
+ schedule_work(&bdev->reset_waker);
+ }
+ }
+ }
+
+ BTMTK_WARN("end");
+ return 0;
+}
+
+static int btmtk_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int err = -1;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_sdio_dev *cif_dev = NULL;
+
+ bdev = sdio_get_drvdata(func);
+ if (!bdev) {
+ BTMTK_ERR("[ERR] bdev is NULL");
+ return -ENOMEM;
+ }
+
+ cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+
+ cif_dev->func = func;
+ BTMTK_INFO("%s func device %p", __func__, func);
+
+ /* it's for L0/L0.5 reset */
+ INIT_WORK(&bdev->reset_waker, btmtk_reset_waker);
+ spin_lock_init(&bdev->txlock);
+ spin_lock_init(&bdev->rxlock);
+
+ if (btmtk_sdio_register_dev(cif_dev) < 0) {
+ BTMTK_ERR("Failed to register BT device!");
+ return -ENODEV;
+ }
+
+ /* Disable the interrupts on the card */
+ btmtk_sdio_enable_host_int(cif_dev);
+ BTMTK_DBG("call btmtk_sdio_enable_host_int done");
+
+ sdio_set_drvdata(func, bdev);
+
+ btmtk_sdio_set_own_back(cif_dev, DRIVER_OWN, 20);
+ btmtk_sdio_set_no_fwn_own(cif_dev, 1);
+
+ /* create tx/rx thread */
+ init_waitqueue_head(&cif_dev->sdio_thread.wait_q);
+ skb_queue_head_init(&cif_dev->tx_queue);
+ atomic_set(&cif_dev->int_count, 0);
+ atomic_set(&cif_dev->tx_rdy, 1);
+ cif_dev->sdio_thread.task = kthread_run(btmtk_sdio_main_thread,
+ bdev, "btmtk_sdio_main_thread");
+ if (IS_ERR(cif_dev->sdio_thread.task)) {
+ BTMTK_DBG("btmtk_sdio_ps failed to start!");
+ err = PTR_ERR(cif_dev->sdio_thread.task);
+ goto unreg_sdio;
+
+ }
+
+ /* Set interrupt output */
+ err = btmtk_sdio_writel(CHIER, FIRMWARE_INT_BIT31 | FIRMWARE_INT_BIT15 |
+ FIRMWARE_INT|TX_FIFO_OVERFLOW |
+ FW_INT_IND_INDICATOR | TX_COMPLETE_COUNT |
+ TX_UNDER_THOLD | TX_EMPTY | RX_DONE, cif_dev->func);
+ if (err) {
+ BTMTK_ERR("Set interrupt output fail(%d)", err);
+ err = -EIO;
+ goto free_thread;
+ }
+
+ /* Enable interrupt output */
+ err = btmtk_sdio_writel(CHLPCR, C_FW_INT_EN_SET, cif_dev->func);
+ if (err) {
+ BTMTK_ERR("enable interrupt output fail(%d)", err);
+ err = -EIO;
+ goto free_thread;
+ }
+
+ /* write clear method */
+ btmtk_sdio_set_write_clear(cif_dev);
+
+ /* old method for chip id
+ * btmtk_sdio_readl(0, &u32ReadCRValue, bdev->func);
+ * BTMTK_INFO("%s read chipid = %x", __func__, u32ReadCRValue);
+ */
+
+ err = btmtk_cif_allocate_memory(cif_dev);
+ if (err < 0) {
+ BTMTK_ERR("[ERR] btmtk_cif_allocate_memory failed!");
+ goto free_thread;
+ }
+
+ err = btmtk_main_cif_initialize(bdev, HCI_SDIO);
+ if (err < 0) {
+ BTMTK_ERR("[ERR] btmtk_main_cif_initialize failed!");
+ goto free_mem;
+ }
+
+ err = btmtk_load_rom_patch(bdev);
+ if (err < 0) {
+ BT_ERR("btmtk load rom patch failed!");
+ goto deinit;
+ }
+
+ err = btmtk_main_woble_initialize(bdev);
+ if (err < 0) {
+ BT_ERR("btmtk_main_woble_initialize failed!");
+ goto free_setting;
+ }
+
+ btmtk_buffer_mode_initialize(bdev, &cif_dev->buffer_mode);
+
+ err = btmtk_register_hci_device(bdev);
+ if (err < 0) {
+ BT_ERR("btmtk_register_hci_device failed!");
+ goto free_setting;
+ }
+
+ btmtk_sdio_writel(0x40, 0x9F1E0000, cif_dev->func);
+
+ goto end;
+
+free_setting:
+ btmtk_free_setting_file(bdev);
+deinit:
+ btmtk_main_cif_uninitialize(bdev, HCI_USB);
+free_mem:
+ btmtk_cif_free_memory(cif_dev);
+free_thread:
+ kthread_stop(cif_dev->sdio_thread.task);
+ wake_up_interruptible(&cif_dev->sdio_thread.wait_q);
+ BTMTK_INFO("wake_up_interruptible main_thread done");
+unreg_sdio:
+ btmtk_sdio_unregister_dev(cif_dev);
+end:
+ BTMTK_INFO("%s normal end, ret = %d", __func__, err);
+ btmtk_sdio_set_no_fwn_own(cif_dev, 0);
+ btmtk_sdio_set_own_back(cif_dev, FW_OWN, 20);
+
+ return 0;
+}
+
+static void btmtk_sdio_disconnect(struct sdio_func *func)
+{
+ struct btmtk_dev *bdev = sdio_get_drvdata(func);
+
+ if (!bdev)
+ return;
+
+ btmtk_cif_free_memory(bdev->cif_dev);
+ btmtk_sdio_unregister_dev(bdev->cif_dev);
+
+ btmtk_main_cif_disconnect_notify(bdev, HCI_SDIO);
+}
+
+static int btmtk_cif_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ int ret = -1;
+ int cif_event = 0;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+
+ /* Mediatek Driver Version */
+ BTMTK_INFO("%s: MTK BT Driver Version : %s", __func__, VERSION);
+
+ BTMTK_DBG("vendor=0x%x, device=0x%x, class=%d, fn=%d",
+ id->vendor, id->device, id->class,
+ func->num);
+
+ /* sdio interface numbers */
+ if (func->num != BTMTK_SDIO_FUNC) {
+ BTMTK_INFO("func num is not match, func_num = %d", func->num);
+ return -ENODEV;
+ }
+
+ /* Retrieve priv data and set to interface structure */
+ bdev = btmtk_get_dev();
+ bdev->intf_dev = &func->dev;
+ bdev->cif_dev = &g_sdio_dev;
+ sdio_set_drvdata(func, bdev);
+
+ /* Retrieve current HIF event state */
+ cif_event = HIF_EVENT_PROBE;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s priv setting is NULL", __func__);
+ return -ENODEV;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ ret = btmtk_sdio_probe(func, id);
+
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+
+ return ret;
+}
+
+static void btmtk_cif_disconnect(struct sdio_func *func)
+{
+ int cif_event = 0;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+
+ bdev = sdio_get_drvdata(func);
+
+ /* Retrieve current HIF event state */
+ cif_event = HIF_EVENT_DISCONNECT;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s priv setting is NULL", __func__);
+ return;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ btmtk_sdio_cif_mutex_lock(bdev);
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ btmtk_sdio_disconnect(func);
+
+ /* Set End/Error state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ btmtk_sdio_cif_mutex_unlock(bdev);
+}
+
+#ifdef CONFIG_PM
+static int btmtk_cif_suspend(struct device *dev)
+{
+ int ret = 0;
+ int cif_event = 0;
+ struct btmtk_cif_state *cif_state = NULL;
+ int state = BTMTK_STATE_INIT;
+ struct sdio_func *func = NULL;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_sdio_dev *cif_dev = NULL;
+ mmc_pm_flag_t pm_flags;
+
+ BTMTK_INFO("%s, enter", __func__);
+
+ if (!dev)
+ return 0;
+ func = dev_to_sdio_func(dev);
+ if (!func)
+ return 0;
+ bdev = sdio_get_drvdata(func);
+ if (!bdev)
+ return 0;
+
+ cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+
+ if (bdev->suspend_count++) {
+ BTMTK_WARN("Has suspended. suspend_count: %d, end", bdev->suspend_count);
+ return 0;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ /* Retrieve current HIF event state */
+ if (state == BTMTK_STATE_FW_DUMP) {
+ BTMTK_WARN("%s: FW dumping ongoing, don't dos suspend flow!!!", __func__);
+ cif_event = HIF_EVENT_FW_DUMP;
+ } else
+ cif_event = HIF_EVENT_SUSPEND;
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+#if CFG_SUPPORT_DVT
+ BTMTK_INFO("%s: SKIP Driver woble_suspend flow", __func__);
+#else
+ ret = btmtk_woble_suspend(bdev);
+ if (ret < 0)
+ BTMTK_ERR("%s: btmtk_woble_suspend return fail %d", __func__, ret);
+#endif
+
+ if (bdev->bt_cfg.support_woble_by_eint) {
+ if (bdev->wobt_irq != 0 && atomic_read(&(bdev->irq_enable_count)) == 0) {
+ BTMTK_INFO("enable BT IRQ:%d", bdev->wobt_irq);
+ irq_set_irq_wake(bdev->wobt_irq, 1);
+ enable_irq(bdev->wobt_irq);
+ atomic_inc(&(bdev->irq_enable_count));
+ } else
+ BTMTK_INFO("irq_enable count:%d", atomic_read(&(bdev->irq_enable_count)));
+ }
+
+ pm_flags = sdio_get_host_pm_caps(func);
+ if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+ BTMTK_ERR("%s cannot remain alive while suspended(0x%x)",
+ sdio_func_id(func), pm_flags);
+ }
+
+ pm_flags = MMC_PM_KEEP_POWER;
+ ret = sdio_set_host_pm_flags(func, pm_flags);
+ if (ret) {
+ BTMTK_ERR("set flag 0x%x err %d", pm_flags, (int)ret);
+ ret = -ENOTSUP;
+ }
+
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+
+ BTMTK_INFO("%s, end. ret = %d", __func__, ret);
+ return ret;
+}
+
+static int btmtk_cif_resume(struct device *dev)
+{
+ u8 ret = 0;
+ struct sdio_func *func = NULL;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_sdio_dev *cif_dev = NULL;
+ struct btmtk_cif_state *cif_state = NULL;
+
+ BTMTK_INFO("%s, enter", __func__);
+
+ if (!dev)
+ return 0;
+ func = dev_to_sdio_func(dev);
+ if (!func)
+ return 0;
+ bdev = sdio_get_drvdata(func);
+ if (!bdev)
+ return 0;
+
+ cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+
+ bdev->suspend_count--;
+ if (bdev->suspend_count) {
+ BTMTK_INFO("data->suspend_count %d, return 0", bdev->suspend_count);
+ return 0;
+ }
+
+ if (bdev->bt_cfg.support_woble_by_eint) {
+ if (bdev->wobt_irq != 0 && atomic_read(&(bdev->irq_enable_count)) == 1) {
+ BTMTK_INFO("disable BT IRQ:%d", bdev->wobt_irq);
+ atomic_dec(&(bdev->irq_enable_count));
+ disable_irq_nosync(bdev->wobt_irq);
+ } else
+ BTMTK_INFO("irq_enable count:%d", atomic_read(&(bdev->irq_enable_count)));
+ }
+
+ cif_state = &bdev->cif_state[HIF_EVENT_RESUME];
+
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+#if CFG_SUPPORT_DVT
+ BTMTK_INFO("%s: SKIP Driver woble_resume flow", __func__);
+#else
+ ret = btmtk_woble_resume(bdev);
+ if (ret < 0)
+ BTMTK_ERR("%s: btmtk_woble_resume return fail %d", __func__, ret);
+#endif
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+
+ BTMTK_INFO("end");
+ return 0;
+}
+#endif // CONFIG_PM //
+
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops btmtk_sdio_pm_ops = {
+ .suspend = btmtk_cif_suspend,
+ .resume = btmtk_cif_resume,
+};
+#endif
+
+static struct sdio_driver btmtk_sdio_driver = {
+ .name = "btsdio",
+ .id_table = btmtk_sdio_tabls,
+ .probe = btmtk_cif_probe,
+ .remove = btmtk_cif_disconnect,
+ .drv = {
+ .owner = THIS_MODULE,
+ .pm = &btmtk_sdio_pm_ops,
+ }
+};
+
+static int sdio_register(void)
+{
+ BTMTK_INFO("%s", __func__);
+
+ if (sdio_register_driver(&btmtk_sdio_driver) != 0)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int sdio_deregister(void)
+{
+ BTMTK_INFO("%s", __func__);
+ sdio_unregister_driver(&btmtk_sdio_driver);
+ return 0;
+}
+
+static void btmtk_sdio_chip_reset_notify(struct btmtk_dev *bdev)
+{
+ struct btmtk_sdio_dev *cif_dev = (struct btmtk_sdio_dev *)bdev->cif_dev;
+
+ if (!cif_dev) {
+ BTMTK_INFO("%s, cif_dev is NULL", __func__);
+ return;
+ }
+ btmtk_sdio_set_no_fwn_own(cif_dev, 0);
+ btmtk_sdio_set_own_back(cif_dev, FW_OWN, 20);
+ atomic_set(&cif_dev->tx_rdy, 1);
+}
+
+int btmtk_cif_register(void)
+{
+ int retval = 0;
+ struct hif_hook_ptr hook;
+
+ BTMTK_INFO("%s", __func__);
+
+ memset(&hook, 0, sizeof(hook));
+ hook.open = btmtk_sdio_open;
+ hook.close = btmtk_sdio_close;
+ hook.reg_read = btmtk_sdio_read_register;
+ hook.reg_write = btmtk_sdio_write_register;
+ hook.send_cmd = btmtk_sdio_send_cmd;
+ hook.send_and_recv = btmtk_sdio_send_and_recv;
+ hook.event_filter = btmtk_sdio_event_filter;
+ hook.subsys_reset = btmtk_sdio_subsys_reset;
+ hook.whole_reset = btmtk_sdio_whole_reset;
+ hook.chip_reset_notify = btmtk_sdio_chip_reset_notify;
+ hook.cif_mutex_lock = btmtk_sdio_cif_mutex_lock;
+ hook.cif_mutex_unlock = btmtk_sdio_cif_mutex_unlock;
+ hook.open_done = btmtk_sdio_open_done;
+ hook.dl_dma = btmtk_sdio_load_fw_patch_using_dma;
+ btmtk_reg_hif_hook(&hook);
+
+ retval = sdio_register();
+ if (retval)
+ BTMTK_ERR("*** SDIO registration fail(%d)! ***", retval);
+ else
+ BTMTK_INFO("%s, SDIO registration success!", __func__);
+ return retval;
+}
+
+int btmtk_cif_deregister(void)
+{
+ BT_INFO("%s", __func__);
+ sdio_deregister();
+ BT_INFO("%s: Done", __func__);
+ return 0;
+}
+
+
diff --git a/drivers/bluetooth/usb/btmtkusb.c b/drivers/bluetooth/usb/btmtkusb.c
new file mode 100644
index 000000000000..a927b419a7e0
--- /dev/null
+++ b/drivers/bluetooth/usb/btmtkusb.c
@@ -0,0 +1,3218 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/quirks.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include "btmtk_define.h"
+#include "btmtk_usb.h"
+#include "btmtk_main.h"
+
+static struct usb_driver btusb_driver;
+static struct btmtk_cif_chip_reset reset_func;
+static int intf_to_idx[BT_MCU_INTERFACE_NUM_MAX] = {0, -1, -1, 1};
+static struct btmtk_usb_dev g_usb_dev[BT_MCU_MINIMUM_INTERFACE_NUM][BT_MCU_NUM_MAX];
+
+static const struct usb_device_id btusb_table[] = {
+ /* Mediatek MT7961 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xe0, 0x01, 0x01) },
+ /* Mediatek MT7915 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7915, 0xe0, 0x01, 0x01) },
+ /* Mediatek MT7663 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7663, 0xe0, 0x01, 0x01) },
+ /* Mediatek MT7922 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7922, 0xe0, 0x01, 0x01) },
+
+ { } /* Terminating entry */
+};
+
+static char event_need_compare[EVENT_COMPARE_SIZE] = {0};
+static char event_need_compare_len;
+static char event_compare_status;
+const u8 READ_ADDRESS_EVENT[] = { 0x0E, 0x0A, 0x01, 0x09, 0x10, 0x00 };
+
+static DEFINE_MUTEX(btmtk_usb_ops_mutex);
+#define USB_OPS_MUTEX_LOCK() mutex_lock(&btmtk_usb_ops_mutex)
+#define USB_OPS_MUTEX_UNLOCK() mutex_unlock(&btmtk_usb_ops_mutex)
+
+MODULE_DEVICE_TABLE(usb, btusb_table);
+
+/* remove #define BTUSB_MAX_ISOC_FRAMES 10
+ * ISCO_FRAMES max is 24
+ */
+#define BTUSB_MAX_ISOC_FRAMES 24
+
+#define BTUSB_INTR_RUNNING 0
+#define BTUSB_BULK_RUNNING 1
+#define BTUSB_ISOC_RUNNING 2
+#define BTUSB_SUSPENDING 3
+#define BTUSB_DID_ISO_RESUME 4
+#define BTUSB_BLE_ISOC_RUNNING 5
+
+#define DEVICE_VENDOR_REQUEST_IN 0xc0
+#define DEVICE_CLASS_REQUEST_OUT 0x20
+#define USB_CTRL_IO_TIMO 100
+
+#define BTMTK_IS_BT_0_INTF(ifnum_base) \
+ (ifnum_base == BT0_MCU_INTERFACE_NUM)
+
+#define BTMTK_IS_BT_1_INTF(ifnum_base) \
+ (ifnum_base == BT1_MCU_INTERFACE_NUM)
+
+#define BTMTK_CIF_GET_DEV_PRIV(bdev, intf, ifnum_base) \
+ do { \
+ bdev = usb_get_intfdata(intf); \
+ ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber; \
+ } while (0)
+
+static int btmtk_cif_allocate_memory(struct btmtk_usb_dev *cif_dev);
+static void btmtk_cif_free_memory(struct btmtk_usb_dev *cif_dev);
+static int btmtk_cif_write_uhw_register(struct btmtk_dev *bdev, u32 reg, u32 val);
+static int btmtk_cif_read_uhw_register(struct btmtk_dev *bdev, u32 reg, u32 *val);
+
+static int btmtk_usb_send_and_recv(struct btmtk_dev *bdev,
+ struct sk_buff *skb,
+ const uint8_t *event, const int event_len,
+ int delay, int retry, int pkt_type);
+static void btmtk_usb_chip_reset_notify(struct btmtk_dev *bdev);
+static int btmtk_usb_event_filter(struct btmtk_dev *bdev, struct sk_buff *skb);
+static int btmtk_usb_send_cmd(struct btmtk_dev *bdev, struct sk_buff *skb,
+ int delay, int retry, int pkt_type);
+static int btmtk_usb_read_register(struct btmtk_dev *bdev, u32 reg, u32 *val);
+static int btmtk_usb_write_register(struct btmtk_dev *bdev, u32 reg, u32 val);
+
+static void btmtk_usb_cif_mutex_lock(struct btmtk_dev *bdev)
+{
+ USB_OPS_MUTEX_LOCK();
+}
+
+static void btmtk_usb_cif_mutex_unlock(struct btmtk_dev *bdev)
+{
+ USB_OPS_MUTEX_UNLOCK();
+}
+
+static inline void btusb_free_frags(struct btmtk_dev *bdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&bdev->rxlock, flags);
+
+ kfree_skb(bdev->evt_skb);
+ bdev->evt_skb = NULL;
+
+ kfree_skb(bdev->sco_skb);
+ bdev->sco_skb = NULL;
+
+ spin_unlock_irqrestore(&bdev->rxlock, flags);
+}
+
+static int btusb_recv_isoc(struct btmtk_dev *bdev, void *buffer, int count)
+{
+ struct sk_buff *skb;
+ int err = 0;
+
+ spin_lock(&bdev->rxlock);
+ skb = bdev->sco_skb;
+
+ while (count) {
+ int len;
+
+ if (!skb) {
+ skb = bt_skb_alloc(HCI_MAX_SCO_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ err = -ENOMEM;
+ break;
+ }
+
+ hci_skb_pkt_type(skb) = HCI_SCODATA_PKT;
+ hci_skb_expect(skb) = HCI_SCO_HDR_SIZE;
+ }
+
+ len = min_t(uint, hci_skb_expect(skb), count);
+ memcpy(skb_put(skb, len), buffer, len);
+
+ count -= len;
+ buffer += len;
+ hci_skb_expect(skb) -= len;
+
+ if (skb->len == HCI_SCO_HDR_SIZE) {
+ /* Complete SCO header */
+ hci_skb_expect(skb) = hci_sco_hdr(skb)->dlen;
+
+ if (skb_tailroom(skb) < hci_skb_expect(skb)) {
+ kfree_skb(skb);
+ skb = NULL;
+
+ err = -EILSEQ;
+ break;
+ }
+ }
+
+ if (!hci_skb_expect(skb)) {
+ /* Complete frame */
+ hci_recv_frame(bdev->hdev, skb);
+ skb = NULL;
+ }
+ }
+
+ bdev->sco_skb = skb;
+ spin_unlock(&bdev->rxlock);
+
+ return err;
+}
+
+static void btusb_intr_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = NULL;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int err;
+ u8 *buf;
+ static u8 intr_blocking_usb_warn;
+
+ if (urb == NULL) {
+ BTMTK_ERR("%s: ERROR, urb is NULL!", __func__);
+ return;
+ }
+
+ hdev = urb->context;
+ if (hdev == NULL) {
+ BTMTK_ERR("%s: ERROR, hdev is NULL!", __func__);
+ return;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (bdev == NULL) {
+ BTMTK_ERR("%s: ERROR, bdev is NULL!", __func__);
+ return;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (cif_dev == NULL) {
+ BTMTK_ERR("%s: ERROR, cif_dev is NULL!", __func__);
+ return;
+ }
+
+ BTMTK_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
+ urb->actual_length);
+
+ if (urb->status != 0 && intr_blocking_usb_warn < 10) {
+ intr_blocking_usb_warn++;
+ BTMTK_WARN("%s: urb %p urb->status %d count %d", __func__,
+ urb, urb->status, urb->actual_length);
+ } else if (urb->status == 0 && urb->actual_length != 0)
+ intr_blocking_usb_warn = 0;
+
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+
+ if (!cif_dev->urb_intr_buf) {
+ BT_ERR("%s: bdev->urb_intr_buf is NULL!", __func__);
+ return;
+ }
+
+ buf = urb->transfer_buffer;
+ if (urb->actual_length >= URB_MAX_BUFFER_SIZE ||
+ (urb->actual_length != (buf[1] + 2) && urb->actual_length > 1)) {
+ BTMTK_ERR("%s: urb->actual_length is invalid, buf[1] = %d!",
+ __func__, buf[1]);
+ btmtk_hci_snoop_print(urb->actual_length, urb->transfer_buffer);
+ goto intr_resub;
+ }
+ memset(cif_dev->urb_intr_buf, 0, URB_MAX_BUFFER_SIZE);
+ cif_dev->urb_intr_buf[0] = HCI_EVENT_PKT;
+ memcpy(cif_dev->urb_intr_buf + 1, urb->transfer_buffer, urb->actual_length);
+
+ BTMTK_DBG("%s ,urb->actual_length = %d", __func__, urb->actual_length);
+ BTMTK_DBG_RAW(cif_dev->urb_intr_buf, urb->actual_length + 1,
+ "%s, recv evt", __func__);
+ BTMTK_DBG_RAW(urb->transfer_buffer, urb->actual_length, "%s, recv evt", __func__);
+ if (cif_dev->urb_intr_buf[1] == 0xFF && urb->actual_length == 1) {
+ /* We can't use usb_control_msg in interrupt.
+ * If you use usb_control_msg , it will cause crash.
+ * Receive a bytes 0xFF from controller, it's WDT interrupt to driver.
+ * WDT interrupt is a mechanism to do L0.5 reset.
+ */
+ schedule_work(&bdev->reset_waker);
+ goto intr_resub;
+ }
+
+ err = btmtk_recv(hdev, cif_dev->urb_intr_buf, urb->actual_length + 1);
+ if (err) {
+ BT_ERR("%s corrupted event packet, urb_intr_buf = %p, transfer_buffer = %p",
+ hdev->name, cif_dev->urb_intr_buf, urb->transfer_buffer);
+ btmtk_hci_snoop_print(urb->actual_length, urb->transfer_buffer);
+ btmtk_hci_snoop_print(urb->actual_length + 1, cif_dev->urb_intr_buf);
+ hdev->stat.err_rx++;
+ }
+ } else if (urb->status == -ENOENT) {
+ BTMTK_INFO("%s: urb->status is ENOENT!", __func__);
+ return;
+ }
+
+ if (!test_bit(BTUSB_INTR_RUNNING, &bdev->flags)) {
+ BTMTK_INFO("%s: test_bit is not running!", __func__);
+ return;
+ }
+
+intr_resub:
+ usb_mark_last_busy(cif_dev->udev);
+ usb_anchor_urb(urb, &cif_dev->intr_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected
+ */
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btusb_submit_intr_reset_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+ struct btmtk_usb_dev *cif_dev = NULL;
+
+ /* If WDT reset happened, fw will send a bytes (FF) to host */
+ BTMTK_DBG("%s", hdev->name);
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (!cif_dev->reset_intr_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+ /* Default size is 16 */
+ /* size = le16_to_cpu(data->intr_ep->wMaxPacketSize); */
+ /* 7663 & 7668 & Buzzard Endpoint description.
+ * bEndpointAddress 0x8f EP 15 IN
+ * wMaxPacketSize 0x0001 1x 1 bytes
+ */
+ size = le16_to_cpu(HCI_MAX_EVENT_SIZE);
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvintpipe(cif_dev->udev, cif_dev->reset_intr_ep->bEndpointAddress);
+
+ /* fw issue, we need to submit urb with a byte
+ * If driver set size = le16_to_cpu(HCI_MAX_EVENT_SIZE) to usb_fill_int_urb
+ * We can't get interrupt callback from bus.
+ */
+ usb_fill_int_urb(urb, cif_dev->udev, pipe, buf, 1,
+ btusb_intr_complete, hdev, cif_dev->reset_intr_ep->bInterval);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &cif_dev->intr_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_mtk_wmt_recv(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct sk_buff *skb;
+ int err;
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ BTMTK_DBG("%s : %s urb %p status %d count %d", __func__, hdev->name, urb, urb->status,
+ urb->actual_length);
+
+ if (urb->status == 0 && urb->actual_length > 0) {
+ BTMTK_DBG_RAW(urb->transfer_buffer, urb->actual_length, "%s, recv evt", __func__);
+ hdev->stat.byte_rx += urb->actual_length;
+ skb = bt_skb_alloc(HCI_MAX_EVENT_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ BTMTK_ERR("%s skb is null!", __func__);
+ hdev->stat.err_rx++;
+ goto exit;
+ }
+
+ if (urb->actual_length >= HCI_MAX_EVENT_SIZE) {
+ BTMTK_ERR("%s urb->actual_length is invalid!", __func__);
+ BTMTK_INFO_RAW(urb->transfer_buffer, urb->actual_length,
+ "urb->actual_length:%d, urb->transfer_buffer:%p",
+ urb->actual_length, urb->transfer_buffer);
+ kfree_skb(skb);
+ hdev->stat.err_rx++;
+ goto exit;
+ }
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+ memcpy(skb_put(skb, urb->actual_length), urb->transfer_buffer, urb->actual_length);
+ BTMTK_DBG_RAW(skb->data, skb->len, "%s, skb recv evt", __func__);
+
+ hci_recv_frame(hdev, skb);
+ return;
+ } else if (urb->status == -ENOENT) {
+ /* Avoid suspend failed when usb_kill_urb */
+ goto exit;
+ }
+
+ usb_mark_last_busy(cif_dev->udev);
+
+ /* The URB complete handler is still called with urb->actual_length = 0
+ * when the event is not available, so we should keep re-submitting
+ * URB until WMT event returns, Also, It's necessary to wait some time
+ * between the two consecutive control URBs to relax the target device
+ * to generate the event. Otherwise, the WMT event cannot return from
+ * the device successfully.
+ */
+ udelay(100);
+
+ usb_anchor_urb(urb, &cif_dev->ctrl_anchor);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ kfree(urb->setup_packet);
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected
+ */
+ if (err != -EPERM && err != -ENODEV)
+ usb_unanchor_urb(urb);
+ }
+
+ return;
+
+exit:
+ kfree(urb->setup_packet);
+}
+
+static int btusb_submit_wmt_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+ unsigned int ifnum_base;
+
+ BTMTK_DBG("%s : %s", __func__, hdev->name);
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(HCI_MAX_EVENT_SIZE);
+
+ dr = kmalloc(sizeof(*dr), GFP_KERNEL);
+ if (!dr) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ if (BTMTK_IS_BT_0_INTF(ifnum_base)) {
+ dr->bRequestType = 0xC0;
+ dr->bRequest = 0x01;
+ dr->wIndex = 0;
+ dr->wValue = 0x30;
+ dr->wLength = __cpu_to_le16(size);
+ } else if (BTMTK_IS_BT_1_INTF(ifnum_base)) {
+ dr->bRequestType = 0xA1;
+ dr->bRequest = 0x01;
+ dr->wIndex = 0x03;
+ dr->wValue = 0x30;
+ dr->wLength = __cpu_to_le16(size);
+ }
+
+ pipe = usb_rcvctrlpipe(cif_dev->udev, 0);
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf) {
+ kfree(dr);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ usb_fill_control_urb(urb, cif_dev->udev, pipe, (void *)dr,
+ buf, size, btusb_mtk_wmt_recv, hdev);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &cif_dev->ctrl_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ kfree(dr);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ BTMTK_DBG("%s", hdev->name);
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ if (!cif_dev->intr_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ /* size = le16_to_cpu(data->intr_ep->wMaxPacketSize); */
+ /* 7663 & 7668 & Buzzard Endpoint description.
+ * bEndpointAddress 0x81 EP 1 IN
+ * wMaxPacketSize 0x0010 1x 16 bytes
+ */
+ size = le16_to_cpu(HCI_MAX_EVENT_SIZE);
+ BTMTK_INFO("%s: maximum packet size:%d", __func__, size);
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvintpipe(cif_dev->udev, cif_dev->intr_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, cif_dev->udev, pipe, buf, size,
+ btusb_intr_complete, hdev, cif_dev->intr_ep->bInterval);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &cif_dev->intr_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_bulk_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = NULL;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int err;
+ u8 *buf;
+ u16 len = 0;
+ static u8 block_bulkin_usb_warn;
+
+ if (urb == NULL) {
+ BTMTK_ERR("%s: ERROR, urb is NULL!", __func__);
+ return;
+ }
+
+ hdev = urb->context;
+ if (hdev == NULL) {
+ BTMTK_ERR("%s: ERROR, hdev is NULL!", __func__);
+ return;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (bdev == NULL) {
+ BTMTK_ERR("%s: ERROR, bdev is NULL!", __func__);
+ return;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (cif_dev == NULL) {
+ BTMTK_ERR("%s: ERROR, cif_dev is NULL!", __func__);
+ return;
+ }
+
+ if (urb->status != 0 && block_bulkin_usb_warn < 10) {
+ block_bulkin_usb_warn++;
+ BTMTK_INFO("%s: urb %p urb->status %d count %d", __func__, urb,
+ urb->status, urb->actual_length);
+ } else if (urb->status == 0 && urb->actual_length != 0)
+ block_bulkin_usb_warn = 0;
+
+ /*
+ * This flag didn't support in kernel 4.x
+ * Driver will remove it
+ * if (!test_bit(HCI_RUNNING, &hdev->flags))
+ * return;
+ */
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+ if (!cif_dev->urb_bulk_buf) {
+ BT_ERR("%s: bdev->urb_bulk_buf is NULL!", __func__);
+ return;
+ }
+
+ buf = urb->transfer_buffer;
+ len = buf[2] + ((buf[3] << 8) & 0xff00);
+ if (urb->actual_length >= URB_MAX_BUFFER_SIZE ||
+ urb->actual_length != len + 4) {
+ BTMTK_ERR("%s urb->actual_length is invalid, len = %d!", __func__, len);
+ btmtk_hci_snoop_print(urb->actual_length, urb->transfer_buffer);
+ goto bulk_resub;
+ }
+ memset(cif_dev->urb_bulk_buf, 0, URB_MAX_BUFFER_SIZE);
+ cif_dev->urb_bulk_buf[0] = HCI_ACLDATA_PKT;
+ memcpy(cif_dev->urb_bulk_buf + 1, urb->transfer_buffer, urb->actual_length);
+
+ err = btmtk_recv(hdev, cif_dev->urb_bulk_buf, urb->actual_length + 1);
+ if (err) {
+ BT_ERR("%s corrupted ACL packet, urb_bulk_buf = %p, transfer_buffer = %p",
+ hdev->name, cif_dev->urb_bulk_buf, urb->transfer_buffer);
+ btmtk_hci_snoop_print(urb->actual_length, urb->transfer_buffer);
+ btmtk_hci_snoop_print(urb->actual_length + 1, cif_dev->urb_bulk_buf);
+ hdev->stat.err_rx++;
+ }
+ } else if (urb->status == -ENOENT) {
+ /* Avoid suspend failed when usb_kill_urb */
+ BTMTK_INFO("%s urb %p status %d count %d", hdev->name, urb, urb->status,
+ urb->actual_length);
+ return;
+ }
+
+ if (!test_bit(BTUSB_BULK_RUNNING, &bdev->flags)) {
+ BTMTK_INFO("%s test flag failed", __func__);
+ return;
+ }
+
+bulk_resub:
+ usb_anchor_urb(urb, &cif_dev->bulk_anchor);
+ usb_mark_last_busy(cif_dev->udev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected
+ */
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size = HCI_MAX_FRAME_SIZE;
+
+ BTMTK_DBG("%s", hdev->name);
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ if (!cif_dev->bulk_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvbulkpipe(cif_dev->udev, cif_dev->bulk_rx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, cif_dev->udev, pipe, buf, size,
+ btusb_bulk_complete, hdev);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_mark_last_busy(cif_dev->udev);
+ usb_anchor_urb(urb, &cif_dev->bulk_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_ble_isoc_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = NULL;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int err;
+ u8 *isoc_buf;
+ int isoc_pkt_len;
+
+ /*
+ * This flag didn't support in kernel 4.x
+ * Driver will remove it
+ * if (!test_bit(HCI_RUNNING, &hdev->flags))
+ * return;
+ */
+ if (urb == NULL) {
+ BTMTK_ERR("%s: ERROR, urb is NULL!", __func__);
+ return;
+ }
+
+ hdev = urb->context;
+ if (hdev == NULL) {
+ BTMTK_ERR("%s: ERROR, hdev is NULL!", __func__);
+ return;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (bdev == NULL) {
+ BTMTK_ERR("%s: ERROR, bdev is NULL!", __func__);
+ return;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (cif_dev == NULL) {
+ BTMTK_ERR("%s: ERROR, cif_dev is NULL!", __func__);
+ return;
+ }
+
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+ isoc_buf = urb->transfer_buffer;
+
+ if (!cif_dev->urb_ble_isoc_buf) {
+ BT_ERR("%s: bdev->urb_ble_isoc_buf is NULL!", __func__);
+ return;
+ }
+ isoc_pkt_len = isoc_buf[2] + (isoc_buf[3] << 8) + HCI_ISO_PKT_HEADER_SIZE;
+
+ /* Skip padding */
+ BTMTK_DBG("%s: isoc_pkt_len = %d, urb->actual_length = %d",
+ __func__, isoc_pkt_len, urb->actual_length);
+ if (isoc_pkt_len == HCI_ISO_PKT_HEADER_SIZE) {
+ BTMTK_DBG("%s: goto ble_iso_resub", __func__);
+ goto ble_iso_resub;
+ }
+
+ if (urb->actual_length + HCI_ISO_PKT_WITH_ACL_HEADER_SIZE > URB_MAX_BUFFER_SIZE) {
+ BTMTK_ERR("%s urb->actual_length is invalid!", __func__);
+ btmtk_hci_snoop_print(urb->actual_length, urb->transfer_buffer);
+ goto ble_iso_resub;
+ }
+ /* It's mtk specific heade for stack
+ * hci layered didn't support 0x05 for ble iso,
+ * it will drop the packet type with 0x05
+ * Driver will replace 0x05 to 0x02
+ * header format : 0x02 0x00 0x44 xx_a xx_2 + isoc packet header & payload
+ */
+ memset(cif_dev->urb_ble_isoc_buf, 0, URB_MAX_BUFFER_SIZE);
+ cif_dev->urb_ble_isoc_buf[0] = HCI_ACLDATA_PKT;
+ cif_dev->urb_ble_isoc_buf[1] = 0x00;
+ cif_dev->urb_ble_isoc_buf[2] = 0x44;
+ cif_dev->urb_ble_isoc_buf[3] = (isoc_pkt_len & 0x00ff);
+ cif_dev->urb_ble_isoc_buf[4] = (isoc_pkt_len >> 8);
+ memcpy(cif_dev->urb_ble_isoc_buf + HCI_ISO_PKT_WITH_ACL_HEADER_SIZE,
+ urb->transfer_buffer, isoc_pkt_len + HCI_ISO_PKT_HEADER_SIZE);
+
+ BTMTK_DBG_RAW(cif_dev->urb_ble_isoc_buf,
+ isoc_pkt_len + HCI_ISO_PKT_WITH_ACL_HEADER_SIZE,
+ "%s: raw data is :", __func__);
+
+ err = btmtk_recv(hdev, cif_dev->urb_ble_isoc_buf,
+ isoc_pkt_len + HCI_ISO_PKT_WITH_ACL_HEADER_SIZE);
+ if (err) {
+ BTMTK_ERR("%s corrupted ACL packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ } else if (urb->status == -ENOENT) {
+ BTMTK_INFO("%s: urb->status is ENOENT!", __func__);
+ return;
+ }
+
+ if (!test_bit(BTUSB_BLE_ISOC_RUNNING, &bdev->flags)) {
+ BTMTK_INFO("%s: bdev->flags is RUNNING!", __func__);
+ return;
+ }
+
+ble_iso_resub:
+ usb_anchor_urb(urb, &cif_dev->ble_isoc_anchor);
+ usb_mark_last_busy(cif_dev->udev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected
+ */
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btusb_submit_intr_ble_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ if (!cif_dev->intr_iso_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+ /* Default size is 16 */
+ /* size = le16_to_cpu(data->intr_ep->wMaxPacketSize); */
+ /* we need to consider the wMaxPacketSize in BLE ISO */
+ size = le16_to_cpu(2000);
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvintpipe(cif_dev->udev, cif_dev->intr_iso_rx_ep->bEndpointAddress);
+ BTMTK_INFO("btusb_submit_intr_iso_urb : polling 0x%02X",
+ cif_dev->intr_iso_rx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, cif_dev->udev, pipe, buf, size,
+ btusb_ble_isoc_complete, hdev, cif_dev->intr_iso_rx_ep->bInterval);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &cif_dev->ble_isoc_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_isoc_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = NULL;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int i, err;
+
+ if (urb == NULL) {
+ BTMTK_ERR("%s: ERROR, urb is NULL!", __func__);
+ return;
+ }
+
+ hdev = urb->context;
+ if (hdev == NULL) {
+ BTMTK_ERR("%s: ERROR, hdev is NULL!", __func__);
+ return;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (bdev == NULL) {
+ BTMTK_ERR("%s: ERROR, bdev is NULL!", __func__);
+ return;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (cif_dev == NULL) {
+ BTMTK_ERR("%s: ERROR, cif_dev is NULL!", __func__);
+ return;
+ }
+
+
+ BTMTK_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
+ urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int offset = urb->iso_frame_desc[i].offset;
+ unsigned int length = urb->iso_frame_desc[i].actual_length;
+
+ if (urb->iso_frame_desc[i].status)
+ continue;
+
+ hdev->stat.byte_rx += length;
+
+ if (btusb_recv_isoc(bdev, urb->transfer_buffer + offset,
+ length) < 0) {
+ BTMTK_ERR("%s corrupted SCO packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ }
+ } else if (urb->status == -ENOENT) {
+ BTMTK_INFO("%s: urb->status is ENOENT!", __func__);
+ return;
+ }
+
+ if (!test_bit(BTUSB_ISOC_RUNNING, &bdev->flags)) {
+ BTMTK_INFO("%s: bdev->flags is RUNNING!", __func__);
+ return;
+ }
+
+ usb_anchor_urb(urb, &cif_dev->isoc_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ /* -EPERM: urb is being killed;
+ * -ENODEV: device got disconnected
+ */
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
+{
+ int i, offset = 0;
+
+ BTMTK_DBG("len %d mtu %d", len, mtu);
+
+ for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
+ i++, offset += mtu, len -= mtu) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = mtu;
+ }
+
+ if (len && i < BTUSB_MAX_ISOC_FRAMES) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = len;
+ i++;
+ }
+
+ urb->number_of_packets = i;
+}
+
+static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ BTMTK_DBG("%s", hdev->name);
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ if (!cif_dev->isoc_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(cif_dev->isoc_rx_ep->wMaxPacketSize) *
+ BTUSB_MAX_ISOC_FRAMES;
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvisocpipe(cif_dev->udev, cif_dev->isoc_rx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, cif_dev->udev, pipe, buf, size, btusb_isoc_complete,
+ hdev, cif_dev->isoc_rx_ep->bInterval);
+
+ urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
+
+ __fill_isoc_descriptor(urb, size,
+ le16_to_cpu(cif_dev->isoc_rx_ep->wMaxPacketSize));
+
+ usb_anchor_urb(urb, &cif_dev->isoc_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ unsigned long flags;
+
+ BTMTK_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
+ urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+done:
+ spin_lock_irqsave(&bdev->txlock, flags);
+ bdev->tx_in_flight--;
+ spin_unlock_irqrestore(&bdev->txlock, flags);
+
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+static void btusb_isoc_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+
+ BTMTK_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
+ urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+done:
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+static int btmtk_usb_open(struct hci_dev *hdev)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int err;
+ unsigned int ifnum_base;
+
+ BTMTK_INFO("%s enter!", __func__);
+
+ BTMTK_DBG("%s", hdev->name);
+
+ err = usb_autopm_get_interface(cif_dev->intf);
+ if (err < 0)
+ return err;
+
+ cif_dev->intf->needs_remote_wakeup = 1;
+
+ if (test_and_set_bit(BTUSB_INTR_RUNNING, &bdev->flags))
+ goto done;
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) {
+ BTMTK_INFO("%s 7961 submit urb\n", __func__);
+ if (BTMTK_IS_BT_0_INTF(ifnum_base)) {
+ if (cif_dev->reset_intr_ep) {
+ err = btusb_submit_intr_reset_urb(hdev, GFP_KERNEL);
+ if (err < 0)
+ goto failed;
+ } else
+ BTMTK_INFO("%s, reset_intr_ep missing,", __func__);
+ BTMTK_INFO("don't submit_intr_reset_urb!");
+
+ if (cif_dev->intr_iso_rx_ep) {
+ err = btusb_submit_intr_ble_isoc_urb(hdev, GFP_KERNEL);
+ if (err < 0) {
+ usb_kill_anchored_urbs(&cif_dev->ble_isoc_anchor);
+ goto failed;
+ }
+ } else
+ BTMTK_INFO("%s, intr_iso_rx_ep missing," __func__);
+ BTMTK_INFO("don't submit_intr_ble_isoc_urb!");
+ } else if (BTMTK_IS_BT_1_INTF(ifnum_base)) {
+ /*need to do in bt_open in btmtk_main.c */
+ /* btmtk_usb_send_power_on_cmd_7668(hdev); */
+ }
+ }
+ err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
+ if (err < 0)
+ goto failed;
+
+ err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
+ if (err < 0) {
+ usb_kill_anchored_urbs(&cif_dev->intr_anchor);
+ goto failed;
+ }
+
+
+ set_bit(BTUSB_BULK_RUNNING, &bdev->flags);
+
+done:
+ usb_autopm_put_interface(cif_dev->intf);
+ return 0;
+
+failed:
+ clear_bit(BTUSB_INTR_RUNNING, &bdev->flags);
+ usb_autopm_put_interface(cif_dev->intf);
+ return err;
+}
+
+static void btusb_stop_traffic(struct btmtk_usb_dev *cif_dev)
+{
+ usb_kill_anchored_urbs(&cif_dev->intr_anchor);
+ usb_kill_anchored_urbs(&cif_dev->bulk_anchor);
+ usb_kill_anchored_urbs(&cif_dev->isoc_anchor);
+ usb_kill_anchored_urbs(&cif_dev->ctrl_anchor);
+ usb_kill_anchored_urbs(&cif_dev->ble_isoc_anchor);
+}
+
+static int btmtk_usb_close(struct hci_dev *hdev)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int err;
+
+ BTMTK_INFO("%s enter!", __func__);
+
+ BTMTK_DBG("%s", hdev->name);
+
+ cancel_work_sync(&bdev->work);
+ cancel_work_sync(&bdev->waker);
+ cancel_work_sync(&bdev->reset_waker);
+
+ clear_bit(BTUSB_BLE_ISOC_RUNNING, &bdev->flags);
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ clear_bit(BTUSB_BULK_RUNNING, &bdev->flags);
+ clear_bit(BTUSB_INTR_RUNNING, &bdev->flags);
+
+ btusb_stop_traffic(cif_dev);
+ btusb_free_frags(bdev);
+
+ err = usb_autopm_get_interface(cif_dev->intf);
+ if (err < 0)
+ goto failed;
+
+ cif_dev->intf->needs_remote_wakeup = 0;
+ usb_autopm_put_interface(cif_dev->intf);
+
+failed:
+ return 0;
+}
+
+static struct urb *alloc_intr_iso_urb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct urb *urb;
+ unsigned int pipe;
+
+ if (!cif_dev->intr_iso_tx_ep)
+ return ERR_PTR(-ENODEV);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return ERR_PTR(-ENOMEM);
+
+ pipe = usb_sndintpipe(cif_dev->udev, cif_dev->intr_iso_tx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, cif_dev->udev, pipe,
+ skb->data, skb->len, btusb_tx_complete, skb, 1);
+
+ skb->dev = (void *)hdev;
+
+ return urb;
+}
+
+static struct urb *alloc_ctrl_bgf1_urb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned int pipe;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return ERR_PTR(-ENOMEM);
+
+ BTMTK_DBG("%s\n", __func__);
+ dr = kmalloc(sizeof(*dr), GFP_KERNEL);
+ if (!dr) {
+ usb_free_urb(urb);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ dr->bRequestType = 0x21;
+ dr->bRequest = 0x00;
+ dr->wIndex = 3;
+ dr->wValue = 0;
+ dr->wLength = __cpu_to_le16(skb->len);
+
+ pipe = usb_sndctrlpipe(cif_dev->udev, 0x00);
+
+ usb_fill_control_urb(urb, cif_dev->udev, pipe, (void *)dr,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ skb->dev = (void *)hdev;
+
+ return urb;
+}
+
+static struct urb *alloc_bulk_cmd_urb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct urb *urb;
+ unsigned int pipe;
+
+ BTMTK_DBG("%s start\n", __func__);
+ if (!cif_dev->bulk_cmd_tx_ep)
+ return ERR_PTR(-ENODEV);
+
+ BTMTK_DBG("%s\n", __func__);
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return ERR_PTR(-ENOMEM);
+
+ pipe = usb_sndbulkpipe(cif_dev->udev, cif_dev->bulk_cmd_tx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, cif_dev->udev, pipe,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ skb->dev = (void *)hdev;
+
+ return urb;
+}
+
+static struct urb *alloc_ctrl_urb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned int pipe;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return ERR_PTR(-ENOMEM);
+
+ dr = kmalloc(sizeof(*dr), GFP_KERNEL);
+ if (!dr) {
+ usb_free_urb(urb);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ dr->bRequestType = cif_dev->cmdreq_type;
+ dr->bRequest = cif_dev->cmdreq;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+ dr->wLength = __cpu_to_le16(skb->len);
+
+ pipe = usb_sndctrlpipe(cif_dev->udev, 0x00);
+
+ usb_fill_control_urb(urb, cif_dev->udev, pipe, (void *)dr,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ skb->dev = (void *)hdev;
+
+ return urb;
+}
+
+static struct urb *alloc_bulk_urb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct urb *urb;
+ unsigned int pipe;
+
+ if (!cif_dev->bulk_tx_ep)
+ return ERR_PTR(-ENODEV);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return ERR_PTR(-ENOMEM);
+
+ pipe = usb_sndbulkpipe(cif_dev->udev, cif_dev->bulk_tx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, cif_dev->udev, pipe,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ skb->dev = (void *)hdev;
+
+ return urb;
+}
+
+static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct urb *urb;
+ unsigned int pipe;
+
+ if (!cif_dev->isoc_tx_ep)
+ return ERR_PTR(-ENODEV);
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_KERNEL);
+ if (!urb)
+ return ERR_PTR(-ENOMEM);
+
+ pipe = usb_sndisocpipe(cif_dev->udev, cif_dev->isoc_tx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, cif_dev->udev, pipe,
+ skb->data, skb->len, btusb_isoc_tx_complete,
+ skb, cif_dev->isoc_tx_ep->bInterval);
+
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ __fill_isoc_descriptor(urb, skb->len,
+ le16_to_cpu(cif_dev->isoc_tx_ep->wMaxPacketSize));
+
+ skb->dev = (void *)hdev;
+
+ return urb;
+}
+
+static int submit_tx_urb(struct hci_dev *hdev, struct urb *urb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int err;
+
+ usb_anchor_urb(urb, &cif_dev->tx_anchor);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ kfree(urb->setup_packet);
+ usb_unanchor_urb(urb);
+ } else {
+ usb_mark_last_busy(cif_dev->udev);
+ }
+
+ usb_free_urb(urb);
+ return err;
+}
+
+static int submit_or_queue_tx_urb(struct hci_dev *hdev, struct urb *urb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ unsigned long flags;
+ bool suspending;
+
+ spin_lock_irqsave(&bdev->txlock, flags);
+ suspending = test_bit(BTUSB_SUSPENDING, &bdev->flags);
+ if (!suspending)
+ bdev->tx_in_flight++;
+ spin_unlock_irqrestore(&bdev->txlock, flags);
+
+ if (!suspending)
+ return submit_tx_urb(hdev, urb);
+
+ schedule_work(&bdev->waker);
+
+ usb_free_urb(urb);
+ return 0;
+}
+
+static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct urb *urb = NULL;
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ unsigned int ifnum_base;
+ int ret = 0;
+ struct sk_buff *iso_skb = NULL;
+#ifdef CFG_SUPPORT_HW_DVT
+ struct sk_buff *evt_skb;
+ uint8_t notify_alt_evt[] = {0x0E, 0x04, 0x01, 0x03, 0x0c, 0x00};
+ u16 crBaseAddr = 0, crRegOffset = 0;
+#endif
+
+ if (skb->len <= 0) {
+ ret = -EFAULT;
+ BTMTK_ERR("%s: target packet length:%zu is not allowed",
+ __func__, (size_t)skb->len);
+ }
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ skb_pull(skb, 1);
+ BTMTK_DBG_RAW(skb->data, skb->len, "%s, send_frame, type = %d", __func__,
+ hci_skb_pkt_type(skb));
+ switch (hci_skb_pkt_type(skb)) {
+ case HCI_COMMAND_PKT:
+#ifdef CFG_SUPPORT_HW_DVT
+ if (skb->len > 7) {
+ if (skb->data[0] == 0x6f && skb->data[1] == 0xfc &&
+ skb->data[2] == 0x06 && skb->data[3] == 0x01 &&
+ skb->data[4] == 0xff && skb->data[5] == 0x03 &&
+ skb->data[6] == 0x00 && skb->data[7] == 0x00) {
+ /* return evt to upper layered */
+ evt_skb = skb_copy(skb, GFP_KERNEL);
+ bt_cb(evt_skb)->pkt_type = HCI_EVENT_PKT;
+ memcpy(evt_skb->data, &notify_alt_evt, sizeof(notify_alt_evt));
+ evt_skb->len = sizeof(notify_alt_evt);
+ /* After set alternate setting, we will return evt to boots */
+ hci_recv_frame(hdev, evt_skb);
+ hdev->conn_hash.sco_num++;
+ bdev->sco_num = hdev->conn_hash.sco_num;
+ cif_dev->new_isoc_altsetting = skb->data[8];
+ BTMTK_INFO("alt_setting = %d, new_isoc_altsetting_interface = %d\n",
+ cif_dev->new_isoc_altsetting,
+ cif_dev->new_isoc_altsetting_interface);
+ schedule_work(&bdev->work);
+ msleep(20);
+ kfree_skb(skb);
+ skb = NULL;
+ return 0;
+ } else if (skb->data[0] == 0x6f && skb->data[1] == 0xfc &&
+ skb->data[2] == 0x07 && skb->data[3] == 0x01 &&
+ skb->data[4] == 0xff && skb->data[5] == 0x03 &&
+ skb->data[6] == 0x00 && skb->data[7] == 0x00 &&
+ skb->data[9] == 0x00) {
+ evt_skb = skb_copy(skb, GFP_KERNEL);
+ bt_cb(evt_skb)->pkt_type = HCI_EVENT_PKT;
+ memcpy(evt_skb->data, &notify_alt_evt, sizeof(notify_alt_evt));
+ evt_skb->len = sizeof(notify_alt_evt);
+ /* After set alternate setting, we will return evt to boots */
+ hci_recv_frame(hdev, evt_skb);
+ /* if sco_num == 0, btusb_work will set alternate setting to zero */
+ hdev->conn_hash.sco_num--;
+ bdev->sco_num = hdev->conn_hash.sco_num;
+ cif_dev->new_isoc_altsetting_interface = skb->data[8];
+ BTMTK_INFO("alt_setting to = %d\n",
+ cif_dev->new_isoc_altsetting);
+ BTMTK_INFO("new_isoc_altsetting_interface = %d\n",
+ cif_dev->new_isoc_altsetting_interface);
+ schedule_work(&bdev->work);
+ /*
+ * If we don't sleep 50ms,
+ * it will failed to set alternate setting to zero
+ */
+ msleep(50);
+ kfree_skb(skb);
+ skb = NULL;
+ return 0;
+ } else if (skb->data[0] == 0x6f && skb->data[1] == 0xfc &&
+ skb->data[2] == 0x09 && skb->data[3] == 0x01 &&
+ skb->data[4] == 0xff && skb->data[5] == 0x05 &&
+ skb->data[6] == 0x00 && skb->data[7] == 0x01) {
+ BTMTK_INFO("read CR skb->data = %02x %02x %02x %02x\n",
+ skb->data[8], skb->data[9], skb->data[10], skb->data[11]);
+ crBaseAddr = (skb->data[8]<<8) + skb->data[9];
+ crRegOffset = (skb->data[10]<<8) + skb->data[11];
+ BTMTK_INFO("base + offset = %04x %04x\n", crBaseAddr, crRegOffset);
+ memset(bdev->io_buf, 0, IO_BUF_SIZE);
+ ret = usb_control_msg(cif_dev->udev,
+ usb_rcvctrlpipe(cif_dev->udev, 0),
+ 1, 0xDE, crBaseAddr, crRegOffset,
+ bdev->io_buf, 4, USB_CTRL_IO_TIMO);
+ if (ret < 0) {
+ BTMTK_ERR("read CR(%04X[%04X]) FAILED\n",
+ crBaseAddr, crRegOffset);
+ } else {
+ BTMTK_INFO("read CR(%04X[%04X])",
+ crBaseAddr, crRegOffset);
+ BTMTK_INFO("value = 0x%02x%02x%02x%02x\n",
+ bdev->io_buf[3], bdev->io_buf[2],
+ bdev->io_buf[1], bdev->io_buf[0]);
+ }
+ kfree_skb(skb);
+ skb = NULL;
+ return 0;
+ } else if (skb->data[0] == 0x6f && skb->data[1] == 0xfc &&
+ skb->data[2] == 0x0D && skb->data[3] == 0x01 &&
+ skb->data[4] == 0xff && skb->data[5] == 0x09 &&
+ skb->data[6] == 0x00 && skb->data[7] == 0x02) {
+ crBaseAddr = (skb->data[8] << 8) + skb->data[9];
+ crRegOffset = (skb->data[10] << 8) + skb->data[11];
+ BTMTK_INFO("base + offset = %04x %04x\n", crBaseAddr, crRegOffset);
+ memset(cif_dev->o_usb_buf, 0, HCI_MAX_COMMAND_SIZE);
+ cif_dev->o_usb_buf[0] = skb->data[12];
+ cif_dev->o_usb_buf[1] = skb->data[13];
+ cif_dev->o_usb_buf[2] = skb->data[14];
+ cif_dev->o_usb_buf[3] = skb->data[15];
+ ret = usb_control_msg(cif_dev->udev,
+ usb_sndctrlpipe(cif_dev->udev, 0),
+ 2, 0x5E, crBaseAddr, crRegOffset,
+ cif_dev->o_usb_buf, 4, USB_CTRL_IO_TIMO);
+ if (ret < 0)
+ BTMTK_ERR("write CR(%04X[%04X]) FAILED\n",
+ crBaseAddr, crRegOffset);
+ else
+ BTMTK_INFO("write CR(%04X[%04X])",
+ crBaseAddr, crRegOffset);
+ BTMTK_INFO("value = 0x%02x%02x%02x%02x\n",
+ cif_dev->o_usb_buf[3], cif_dev->o_usb_buf[2],
+ cif_dev->o_usb_buf[1], cif_dev->o_usb_buf[0]);
+ kfree_skb(skb);
+ skb = NULL;
+ return 0;
+ }
+ }
+#endif
+
+ /* For wmt cmd/evt */
+ if (!memcmp(skb->data, &wmt_over_hci_header[1], WMT_OVER_HCI_HEADER_SIZE - 1)) {
+ skb_push(skb, 1);
+ skb->data[0] = 0x01;
+ BTMTK_DBG_RAW(skb->data, skb->len, "%s, 6ffc send_frame", __func__);
+ btmtk_usb_send_cmd(bdev, skb, 100, 20, BTMTK_TX_CMD_FROM_DRV);
+ btusb_submit_wmt_urb(hdev, GFP_KERNEL);
+ return 0;
+ }
+
+ if (BTMTK_IS_BT_0_INTF(ifnum_base)) {
+ if ((is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) &&
+ cif_dev->bulk_cmd_tx_ep)
+ urb = alloc_bulk_cmd_urb(hdev, skb);
+ else
+ urb = alloc_ctrl_urb(hdev, skb);
+ } else if (BTMTK_IS_BT_1_INTF(ifnum_base)) {
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) {
+ if (cif_dev->bulk_cmd_tx_ep) {
+ UNUSED(alloc_ctrl_bgf1_urb);
+ urb = alloc_bulk_cmd_urb(hdev, skb);
+ } else
+ urb = alloc_ctrl_bgf1_urb(hdev, skb);
+ } else if (is_mt7663(bdev->chip_id)) {
+ urb = alloc_ctrl_urb(hdev, skb);
+ } else {
+ BTMTK_ERR("%s: chip_id(%d) is invalid", __func__, bdev->chip_id);
+ return -ENODEV;
+ }
+ } else {
+ BTMTK_ERR("%s: ifnum_base(%d) is invalid", __func__, ifnum_base);
+ return -ENODEV;
+ }
+
+ if (IS_ERR(urb)) {
+ kfree_skb(skb);
+ skb = NULL;
+ return PTR_ERR(urb);
+ }
+
+ hdev->stat.cmd_tx++;
+ return submit_or_queue_tx_urb(hdev, urb);
+
+ case HCI_ACLDATA_PKT:
+ if (skb->data[0] == 0x00 && skb->data[1] == 0x44) {
+ if (cif_dev->iso_channel && bdev->iso_threshold) {
+ int isoc_pkt_len = 0;
+ int isoc_pkt_padding = 0;
+
+ skb_pull(skb, 4);
+ isoc_pkt_len = skb->data[2] + (skb->data[3] << 8) +
+ HCI_ISO_PKT_HEADER_SIZE;
+ isoc_pkt_padding = bdev->iso_threshold - isoc_pkt_len;
+
+ if (skb_tailroom(skb) < isoc_pkt_padding) {
+ /*
+ * hci driver allocate the size of skb that is to small,
+ * need re-allocate
+ */
+ iso_skb = alloc_skb(HCI_MAX_ISO_SIZE +
+ BT_SKB_RESERVE, GFP_ATOMIC);
+ if (!iso_skb) {
+ BTMTK_ERR("%s allocate skb failed!!", __func__);
+ kfree_skb(skb);
+ skb = NULL;
+ return -ENOMEM;
+ }
+ /* copy skb data into iso_skb */
+ skb_copy_bits(skb, 0,
+ skb_put(iso_skb, skb->len), skb->len);
+ memset(skb_put(iso_skb, isoc_pkt_padding),
+ 0, isoc_pkt_padding);
+
+ /* After call back, bt drive will free iso_skb */
+ urb = alloc_intr_iso_urb(hdev, iso_skb);
+ BTMTK_DBG_RAW(iso_skb->data, iso_skb->len,
+ "%s, it's ble iso packet",
+ __func__);
+ /* It's alloc by hci drver, bt driver must be free it. */
+ kfree_skb(skb);
+ skb = NULL;
+ if (IS_ERR(urb)) {
+ kfree_skb(iso_skb);
+ iso_skb = NULL;
+ return PTR_ERR(urb);
+ }
+ } else {
+ memset(skb_put(skb, isoc_pkt_padding), 0, isoc_pkt_padding);
+ urb = alloc_intr_iso_urb(hdev, skb);
+ BTMTK_DBG_RAW(skb->data, skb->len,
+ "%s, it's ble iso packet",
+ __func__);
+ if (IS_ERR(urb)) {
+ kfree_skb(skb);
+ skb = NULL;
+ return PTR_ERR(urb);
+ }
+ }
+ } else {
+ BTMTK_WARN("%s send iso data, but iso channel not exit", __func__);
+ /*
+ * if iso channel not exist,
+ * we need to drop iso data then free the skb
+ */
+ kfree_skb(skb);
+ skb = NULL;
+ return 0;
+ }
+ } else {
+ urb = alloc_bulk_urb(hdev, skb);
+ if (IS_ERR(urb)) {
+ kfree_skb(skb);
+ skb = NULL;
+ return PTR_ERR(urb);
+ }
+ }
+ hdev->stat.acl_tx++;
+ return submit_or_queue_tx_urb(hdev, urb);
+
+ case HCI_SCODATA_PKT:
+ if (hci_conn_num(hdev, SCO_LINK) < 1) {
+ BTMTK_INFO("%s hci_conn sco link = %d",
+ __func__, hci_conn_num(hdev, SCO_LINK));
+ /* We need to study how to solve this in hw_dvt case.*/
+#ifndef CFG_SUPPORT_HW_DVT
+ kfree_skb(skb);
+ skb = NULL;
+ return -ENODEV;
+#endif
+ }
+
+ urb = alloc_isoc_urb(hdev, skb);
+ if (IS_ERR(urb)) {
+ kfree_skb(skb);
+ skb = NULL;
+ return PTR_ERR(urb);
+ }
+
+ hdev->stat.sco_tx++;
+ return submit_tx_urb(hdev, urb);
+ }
+
+ kfree_skb(skb);
+ skb = NULL;
+ return -EILSEQ;
+}
+
+static int btmtk_usb_load_fw_patch_using_dma(struct btmtk_dev *bdev, u8 *image,
+ u8 *fwbuf, int section_dl_size, int section_offset)
+{
+ int cur_len = 0;
+ int ret = 0;
+ s32 sent_len;
+ u8 dl_done_cmd[] = {0x01, 0x6F, 0xFC, 0x05, 0x01, 0x01, 0x01, 0x00, PATCH_PHASE3};
+ u8 event[] = {0x04, 0xE4, 0x05, 0x02, 0x01, 0x01, 0x00, 0x00}; /* event[7] is status*/
+
+ if (bdev == NULL || image == NULL || fwbuf == NULL) {
+ BTMTK_ERR("%s: invalid parameters!", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ BTMTK_INFO("%s: loading rom patch... start", __func__);
+ while (1) {
+ sent_len = (section_dl_size - cur_len) >= (UPLOAD_PATCH_UNIT - HCI_TYPE_SIZE) ?
+ (UPLOAD_PATCH_UNIT - HCI_TYPE_SIZE) : (section_dl_size - cur_len);
+
+ if (sent_len > 0) {
+ /* btmtk_cif_send_bulk_out will send from image[1],
+ * image[0] will be ingored
+ */
+ image[0] = HCI_ACLDATA_PKT;
+ memcpy(&image[HCI_TYPE_SIZE], fwbuf + section_offset + cur_len, sent_len);
+ BTMTK_DBG("%s: sent_len = %d, cur_len = %d", __func__,
+ sent_len, cur_len);
+ ret = btmtk_main_send_cmd(bdev,
+ image, sent_len + HCI_TYPE_SIZE,
+ NULL, -1,
+ 0, 0, BTMTK_TX_ACL_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_ERR("%s: send patch failed, terminate", __func__);
+ goto exit;
+ }
+ cur_len += sent_len;
+ } else
+ break;
+ }
+
+ BTMTK_INFO_RAW(dl_done_cmd, sizeof(dl_done_cmd), "%s: send dl cmd - ", __func__);
+ ret = btmtk_main_send_cmd(bdev, dl_done_cmd, sizeof(dl_done_cmd),
+ event, sizeof(event),
+ DELAY_TIMES, RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0)
+ BTMTK_ERR("%s: send wmd dl cmd failed, terminate!", __func__);
+ BTMTK_INFO("%s: loading rom patch... Done", __func__);
+
+exit:
+ return ret;
+}
+
+
+static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+
+ BTMTK_DBG("%s evt %d", hdev->name, evt);
+
+ if (hci_conn_num(hdev, SCO_LINK) != bdev->sco_num) {
+ bdev->sco_num = hci_conn_num(hdev, SCO_LINK);
+ schedule_work(&bdev->work);
+ }
+}
+
+static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct usb_interface *intf = cif_dev->isoc;
+ struct usb_endpoint_descriptor *ep_desc;
+ int i, err;
+ unsigned int ifnum_base;
+
+ if (!cif_dev->isoc)
+ return -ENODEV;
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+ if (BTMTK_IS_BT_0_INTF(ifnum_base))
+ cif_dev->new_isoc_altsetting_interface = 1;
+ else if (BTMTK_IS_BT_1_INTF(ifnum_base))
+ cif_dev->new_isoc_altsetting_interface = 4;
+ err = usb_set_interface(cif_dev->udev, cif_dev->new_isoc_altsetting_interface, altsetting);
+ BTMTK_DBG("setting interface alt = %d, interface = %d",
+ altsetting, cif_dev->new_isoc_altsetting_interface);
+
+ if (err < 0) {
+ BTMTK_ERR("%s setting interface failed (%d)", hdev->name, -err);
+ return err;
+ }
+
+ bdev->isoc_altsetting = altsetting;
+
+ cif_dev->isoc_tx_ep = NULL;
+ cif_dev->isoc_rx_ep = NULL;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ if (!cif_dev->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
+ cif_dev->isoc_tx_ep = ep_desc;
+ continue;
+ }
+
+ if (!cif_dev->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
+ cif_dev->isoc_rx_ep = ep_desc;
+ continue;
+ }
+ }
+
+ if (!cif_dev->isoc_tx_ep || !cif_dev->isoc_rx_ep) {
+ BTMTK_ERR("%s invalid SCO descriptors", hdev->name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void btusb_work(struct work_struct *work)
+{
+ struct btmtk_dev *bdev = container_of(work, struct btmtk_dev, work);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct hci_dev *hdev = bdev->hdev;
+ int new_alts;
+ int err;
+ unsigned long flags;
+
+ if (bdev->sco_num > 0) {
+ if (!test_bit(BTUSB_DID_ISO_RESUME, &bdev->flags)) {
+ err = usb_autopm_get_interface(cif_dev->isoc ?
+ cif_dev->isoc : cif_dev->intf);
+ if (err < 0) {
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ usb_kill_anchored_urbs(&cif_dev->isoc_anchor);
+ return;
+ }
+
+ set_bit(BTUSB_DID_ISO_RESUME, &bdev->flags);
+ }
+
+#ifdef CFG_SUPPORT_HW_DVT
+ new_alts = cif_dev->new_isoc_altsetting;
+#else
+ if (hdev->voice_setting & 0x0020) {
+ static const int alts[3] = { 2, 4, 5 };
+
+ new_alts = alts[bdev->sco_num - 1];
+ } else {
+ new_alts = bdev->sco_num;
+ }
+#endif
+
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ usb_kill_anchored_urbs(&cif_dev->isoc_anchor);
+
+ /* When isochronous alternate setting needs to be
+ * changed, because SCO connection has been added
+ * or removed, a packet fragment may be left in the
+ * reassembling state. This could lead to wrongly
+ * assembled fragments.
+ *
+ * Clear outstanding fragment when selecting a new
+ * alternate setting.
+ */
+ spin_lock_irqsave(&bdev->rxlock, flags);
+ kfree_skb(bdev->sco_skb);
+ bdev->sco_skb = NULL;
+ spin_unlock_irqrestore(&bdev->rxlock, flags);
+
+ if (__set_isoc_interface(hdev, new_alts) < 0)
+ return;
+
+ if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &bdev->flags)) {
+ if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ else
+ btusb_submit_isoc_urb(hdev, GFP_KERNEL);
+ }
+ } else {
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ usb_kill_anchored_urbs(&cif_dev->isoc_anchor);
+ BTMTK_INFO("%s set alt to zero", __func__);
+ __set_isoc_interface(hdev, 0);
+ if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &bdev->flags))
+ usb_autopm_put_interface(cif_dev->isoc ?
+ cif_dev->isoc : cif_dev->intf);
+ }
+}
+
+static void btusb_waker(struct work_struct *work)
+{
+ struct btmtk_dev *bdev = container_of(work, struct btmtk_dev, waker);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int err;
+
+ err = usb_autopm_get_interface(cif_dev->intf);
+ if (err < 0)
+ return;
+
+ usb_autopm_put_interface(cif_dev->intf);
+}
+
+static int btmtk_usb_toggle_rst_pin(struct btmtk_dev *bdev)
+{
+ struct device_node *node;
+ int rst_pin_num = 0;
+
+ if (!bdev) {
+ BTMTK_WARN("%s: bdev is NULL!", __func__);
+ return -1;
+ }
+ if (bdev->bt_cfg.dongle_reset_gpio_pin == -1) {
+ BTMTK_WARN("%s: bt driver is not ready, please don't call chip reset!", __func__);
+ return -1;
+ }
+
+ BTMTK_INFO("%s: begin", __func__);
+
+ bdev->chip_reset = 1;
+ /* Initialize the interface specific function pointers */
+ reset_func.pf_pdwndFunc =
+ (pdwnc_func) btmtk_kallsyms_lookup_name("PDWNC_SetBTInResetState");
+ if (reset_func.pf_pdwndFunc)
+ BTMTK_INFO("%s: Found PDWNC_SetBTInResetState", __func__);
+ else
+ BTMTK_WARN("%s: No Exported Func Found PDWNC_SetBTInResetState", __func__);
+
+ reset_func.pf_resetFunc2 =
+ (reset_func_ptr2) btmtk_kallsyms_lookup_name("mtk_gpio_set_value");
+ if (!reset_func.pf_resetFunc2)
+ BTMTK_ERR("%s: No Exported Func Found mtk_gpio_set_value", __func__);
+ else
+ BTMTK_INFO("%s: Found mtk_gpio_set_value", __func__);
+
+ reset_func.pf_lowFunc = (set_gpio_low) btmtk_kallsyms_lookup_name("MDrv_GPIO_Set_Low");
+ reset_func.pf_highFunc = (set_gpio_high) btmtk_kallsyms_lookup_name("MDrv_GPIO_Set_High");
+ if (!reset_func.pf_lowFunc || !reset_func.pf_highFunc)
+ BTMTK_WARN("%s: No Exported Func Found MDrv_GPIO_Set_Low or High", __func__);
+ else
+ BTMTK_INFO("%s: Found MDrv_GPIO_Set_Low & MDrv_GPIO_Set_High", __func__);
+
+ if (reset_func.pf_pdwndFunc) {
+ BTMTK_INFO("%s: Invoke PDWNC_SetBTInResetState(%d)", __func__, 1);
+ reset_func.pf_pdwndFunc(1);
+ } else
+ BTMTK_INFO("%s: No Exported Func Found PDWNC_SetBTInResetState", __func__);
+
+ if (reset_func.pf_resetFunc2) {
+ rst_pin_num = bdev->bt_cfg.dongle_reset_gpio_pin;
+ BTMTK_INFO("%s: Invoke bdev->pf_resetFunc2(%d,%d)", __func__, rst_pin_num, 0);
+ reset_func.pf_resetFunc2(rst_pin_num, 0);
+ msleep(RESET_PIN_SET_LOW_TIME);
+ BTMTK_INFO("%s: Invoke bdev->pf_resetFunc2(%d,%d)", __func__, rst_pin_num, 1);
+ reset_func.pf_resetFunc2(rst_pin_num, 1);
+ goto exit;
+ }
+
+ node = of_find_compatible_node(NULL, NULL, "mstar,gpio-wifi-ctl");
+ if (node) {
+ if (of_property_read_u32(node, "wifi-ctl-gpio", &rst_pin_num) == 0) {
+ if (reset_func.pf_lowFunc && reset_func.pf_highFunc) {
+ BTMTK_INFO("%s: Invoke bdev->pf_lowFunc(%d)",
+ __func__, rst_pin_num);
+ reset_func.pf_lowFunc(rst_pin_num);
+ msleep(RESET_PIN_SET_LOW_TIME);
+ BTMTK_INFO("%s: Invoke bdev->pf_highFunc(%d)",
+ __func__, rst_pin_num);
+ reset_func.pf_highFunc(rst_pin_num);
+ goto exit;
+ }
+ } else
+ BTMTK_WARN("%s, failed to obtain wifi control gpio\n", __func__);
+ } else {
+ if (reset_func.pf_lowFunc && reset_func.pf_highFunc) {
+ rst_pin_num = bdev->bt_cfg.dongle_reset_gpio_pin;
+ BTMTK_INFO("%s: Invoke bdev->pf_lowFunc(%d)", __func__, rst_pin_num);
+ reset_func.pf_lowFunc(rst_pin_num);
+ msleep(RESET_PIN_SET_LOW_TIME);
+ BTMTK_INFO("%s: Invoke bdev->pf_highFunc(%d)", __func__, rst_pin_num);
+ reset_func.pf_highFunc(rst_pin_num);
+ goto exit;
+ }
+ }
+
+ /* use linux kernel common api */
+ do {
+ struct device_node *node;
+ int mt76xx_reset_gpio = bdev->bt_cfg.dongle_reset_gpio_pin;
+
+ node = of_find_compatible_node(NULL, NULL, "mediatek,connectivity-combo");
+ if (node) {
+ mt76xx_reset_gpio = of_get_named_gpio(node, "mt76xx-reset-gpio", 0);
+ if (gpio_is_valid(mt76xx_reset_gpio))
+ BTMTK_INFO("%s: Get chip reset gpio(%d)",
+ __func__, mt76xx_reset_gpio);
+ else
+ mt76xx_reset_gpio = bdev->bt_cfg.dongle_reset_gpio_pin;
+ }
+
+ BTMTK_INFO("%s: Invoke Low(%d)", __func__, mt76xx_reset_gpio);
+ gpio_direction_output(mt76xx_reset_gpio, 0);
+ msleep(RESET_PIN_SET_LOW_TIME);
+ BTMTK_INFO("%s: Invoke High(%d)", __func__, mt76xx_reset_gpio);
+ gpio_direction_output(mt76xx_reset_gpio, 1);
+ goto exit;
+ } while (0);
+
+exit:
+ BTMTK_INFO("%s: end", __func__);
+ return 0;
+}
+
+static int btmtk_usb_subsys_reset(struct btmtk_dev *bdev)
+{
+ int val, retry = 10;
+
+ cancel_work_sync(&bdev->work);
+ cancel_work_sync(&bdev->waker);
+
+ clear_bit(BTUSB_BLE_ISOC_RUNNING, &bdev->flags);
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ clear_bit(BTUSB_BULK_RUNNING, &bdev->flags);
+ clear_bit(BTUSB_INTR_RUNNING, &bdev->flags);
+ bdev->sco_num = 0;
+
+ btusb_stop_traffic((struct btmtk_usb_dev *)bdev->cif_dev);
+
+ /* For reset */
+ btmtk_cif_write_uhw_register(bdev, EP_RST_OPT, EP_RST_IN_OUT_OPT);
+
+ /* read interrupt EP15 CR */
+ btmtk_cif_read_uhw_register(bdev, BT_WDT_STATUS, &val);
+
+ /* Write Reset CR to 1 */
+ btmtk_cif_write_uhw_register(bdev, BT_SUBSYS_RST, 1);
+
+ btmtk_cif_write_uhw_register(bdev, UDMA_INT_STA_BT, 0x000000FF);
+ btmtk_cif_read_uhw_register(bdev, UDMA_INT_STA_BT, &val);
+ btmtk_cif_write_uhw_register(bdev, UDMA_INT_STA_BT1, 0x000000FF);
+ btmtk_cif_read_uhw_register(bdev, UDMA_INT_STA_BT1, &val);
+
+ /* Write Reset CR to 0 */
+ btmtk_cif_write_uhw_register(bdev, BT_SUBSYS_RST, 0);
+
+ /* Read reset CR */
+ btmtk_cif_read_uhw_register(bdev, BT_SUBSYS_RST, &val);
+
+ do {
+ /* polling re-init CR */
+ btmtk_cif_read_uhw_register(bdev, BT_MISC, &val);
+ BTMTK_INFO("%s: reg=%x, value=0x%08x", __func__, BT_MISC, val);
+ if ((val & 0x00000300) == 0x00000300) {
+ /* L0.5 reset done */
+ BTMTK_INFO("%s: Do L0.5 reset successfully.", __func__);
+ goto Finish;
+ } else {
+ BTMTK_INFO("%s: polling MCU-init done CR", __func__);
+ }
+ msleep(100);
+ } while (retry-- > 0);
+
+ /* L0.5 reset failed, do whole chip reset */
+ return -1;
+
+Finish:
+ return 0;
+}
+
+static int btusb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ unsigned int ifnum_base;
+ int i, err = 0;
+
+ ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber;
+ BTMTK_DBG("intf %p id %p, interfacenum = %d", intf, id, ifnum_base);
+
+ bdev = usb_get_intfdata(intf);
+ if (!bdev) {
+ BTMTK_ERR("[ERR] bdev is NULL");
+ err = -ENOMEM;
+ goto end;
+ }
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ /* reset_intr_ep must be initialized before intr_ep,
+ * otherwise its address may be the intr_ep address
+ */
+ if (!cif_dev->reset_intr_ep && ep_desc->bEndpointAddress == 0x8f &&
+ usb_endpoint_is_int_in(ep_desc)) {
+ BTMTK_INFO("intr_reset_rx__ep i = %d Endpoints 0x%02X",
+ i, ep_desc->bEndpointAddress);
+ BTMTK_INFO("number_of_endpoints=%d",
+ intf->cur_altsetting->desc.bNumEndpoints);
+ cif_dev->reset_intr_ep = ep_desc;
+ continue;
+ }
+
+ /* bulk_cmd_tx_ep must be initialized before bulk_tx_ep,
+ * otherwise its address will be the bulk_tx_ep address
+ */
+ if (!cif_dev->bulk_cmd_tx_ep && usb_endpoint_is_bulk_out(ep_desc) &&
+ (ep_desc->bEndpointAddress == 0x01 || ep_desc->bEndpointAddress == 0x0b)) {
+ cif_dev->bulk_cmd_tx_ep = ep_desc;
+ BTMTK_INFO("bulk_cmd_tx_ep i = %d, Endpoints 0x%02X"
+ i, ep_desc->bEndpointAddress);
+ BTMTK_INFO("number_of_endpoints=%d",
+ intf->cur_altsetting->desc.bNumEndpoints);
+ continue;
+ }
+
+ if (!cif_dev->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
+ cif_dev->intr_ep = ep_desc;
+ BTMTK_INFO("intr_rx_ep i = %d Endpoints 0x%02X, number_of_endpoints=%d",
+ i, ep_desc->bEndpointAddress,
+ intf->cur_altsetting->desc.bNumEndpoints);
+ continue;
+ }
+
+ if (!cif_dev->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
+ cif_dev->bulk_tx_ep = ep_desc;
+ BTMTK_INFO("bulk_tx_ep i = %d Endpoints 0x%02X, number_of_endpoints=%d",
+ i, ep_desc->bEndpointAddress,
+ intf->cur_altsetting->desc.bNumEndpoints);
+ continue;
+ }
+
+ if (!cif_dev->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
+ cif_dev->bulk_rx_ep = ep_desc;
+ BTMTK_INFO("bulk_rx_ep i = %d Endpoints 0x%02X, number_of_endpoints=%d",
+ i, ep_desc->bEndpointAddress,
+ intf->cur_altsetting->desc.bNumEndpoints);
+ continue;
+ }
+ }
+
+ if (!cif_dev->intr_ep || !cif_dev->bulk_tx_ep || !cif_dev->bulk_rx_ep) {
+ BTMTK_ERR("[ERR] intr_ep or bulk_tx_ep or bulk_rx_ep is NULL");
+ err = -ENODEV;
+ goto end;
+ }
+
+ cif_dev->cmdreq_type = USB_TYPE_CLASS;
+ cif_dev->cmdreq = 0x00;
+
+
+ cif_dev->udev = interface_to_usbdev(intf);
+ cif_dev->intf = intf;
+ bdev->intf_dev = &cif_dev->udev->dev;
+
+ INIT_WORK(&bdev->work, btusb_work);
+ INIT_WORK(&bdev->waker, btusb_waker);
+ /* it's for L0/L0.5 reset */
+ INIT_WORK(&bdev->reset_waker, btmtk_reset_waker);
+ init_usb_anchor(&cif_dev->tx_anchor);
+ spin_lock_init(&bdev->txlock);
+
+ init_usb_anchor(&cif_dev->intr_anchor);
+ init_usb_anchor(&cif_dev->bulk_anchor);
+ init_usb_anchor(&cif_dev->isoc_anchor);
+ init_usb_anchor(&cif_dev->ctrl_anchor);
+ init_usb_anchor(&cif_dev->ble_isoc_anchor);
+ spin_lock_init(&bdev->rxlock);
+
+ err = btmtk_cif_allocate_memory(cif_dev);
+ if (err < 0) {
+ BTMTK_ERR("[ERR] btmtk_cif_allocate_memory failed!");
+ goto end;
+ }
+
+ err = btmtk_main_cif_initialize(bdev, HCI_USB);
+ if (err < 0) {
+ BTMTK_ERR("[ERR] btmtk_main_cif_initialize failed!");
+ goto free_mem;
+ }
+
+ /* only usb interface need this callback to allocate isoc trx endpoint
+ * There is no need for other interface such as sdio to use this function
+ */
+ bdev->hdev->notify = btusb_notify;
+
+ SET_HCIDEV_DEV(bdev->hdev, &cif_dev->intf->dev);
+
+ if (BTMTK_IS_BT_0_INTF(ifnum_base) && !(is_mt7922(bdev->chip_id)))
+ err = btmtk_load_rom_patch(bdev);
+ else
+ BTMTK_INFO("interface = %d, don't download patch", ifnum_base);
+
+ if (err < 0) {
+ BTMTK_ERR("btmtk load rom patch failed!");
+ goto deinit;
+ }
+
+ /* For reset */
+ btmtk_cif_write_uhw_register(bdev, EP_RST_OPT, 0x00010001);
+
+ /* Interface numbers are hardcoded in the specification */
+ if (BTMTK_IS_BT_0_INTF(ifnum_base)) {
+ cif_dev->isoc = usb_ifnum_to_if(cif_dev->udev, 1);
+
+ BTMTK_INFO("set interface number 2 for iso ");
+ cif_dev->iso_channel = usb_ifnum_to_if(cif_dev->udev, 2);
+ usb_set_interface(cif_dev->udev, 2, 1);
+ if (cif_dev->iso_channel) {
+ for (i = 0;
+ i < cif_dev->iso_channel->cur_altsetting->desc.bNumEndpoints;
+ i++) {
+ ep_desc = &cif_dev->iso_channel->cur_altsetting->endpoint[i].desc;
+
+ if (!cif_dev->intr_iso_tx_ep && usb_endpoint_is_int_out(ep_desc)) {
+ cif_dev->intr_iso_tx_ep = ep_desc;
+ BTMTK_INFO("intr_iso_tx_ep i = %d Endpoints 0x%02X",
+ i, ep_desc->bEndpointAddress);
+ BTMTK_INFO("number_of_endpoints=%d",
+ intf->cur_altsetting->desc.bNumEndpoints);
+ continue;
+ }
+
+ if (!cif_dev->intr_iso_rx_ep && usb_endpoint_is_int_in(ep_desc)) {
+ cif_dev->intr_iso_rx_ep = ep_desc;
+ BTMTK_INFO("intr_iso_rx_ep i = %d Endpoints 0x%02X",
+ i, ep_desc->bEndpointAddress);
+ BTMTK_INFO("number_of_endpoints=%d",
+ intf->cur_altsetting->desc.bNumEndpoints);
+ continue;
+ }
+ }
+
+ err = usb_driver_claim_interface(&btusb_driver,
+ cif_dev->iso_channel, bdev);
+ if (err < 0)
+ goto deinit;
+ }
+ } else if (BTMTK_IS_BT_1_INTF(ifnum_base)) {
+ BTMTK_INFO("interface number = 3, set interface number 4");
+ cif_dev->isoc = usb_ifnum_to_if(cif_dev->udev, 4);
+ }
+
+ if (cif_dev->isoc) {
+ err = usb_driver_claim_interface(&btusb_driver,
+ cif_dev->isoc, bdev);
+ if (err < 0)
+ goto deinit;
+ }
+
+ /* dongle_index - 1 since BT1 is in same interface */
+ if (BTMTK_IS_BT_1_INTF(ifnum_base))
+ bdev->dongle_index--;
+ BTMTK_DBG("%s: bdev->dongle_index = %d ", __func__, bdev->dongle_index);
+
+ usb_set_intfdata(intf, bdev);
+
+ err = btmtk_main_woble_initialize(bdev);
+ if (err < 0) {
+ BTMTK_ERR("btmtk_main_woble_initialize failed!");
+ goto free_setting;
+ }
+
+ btmtk_woble_wake_unlock(bdev);
+
+#if CFG_SUPPORT_BLUEZ
+ err = btmtk_send_init_cmds(bdev);
+ if (err < 0)
+ BTMTK_ERR("%s, btmtk_send_init_cmds failed, err = %d", __func__, err);
+#endif /* CFG_SUPPORT_BLUEZ */
+
+ err = btmtk_register_hci_device(bdev);
+ if (err < 0) {
+ BTMTK_ERR("btmtk_register_hci_device failed!");
+ goto free_setting;
+ }
+
+ return 0;
+
+free_setting:
+ btmtk_free_setting_file(bdev);
+deinit:
+ btmtk_main_cif_uninitialize(bdev, HCI_USB);
+free_mem:
+ btmtk_cif_free_memory(cif_dev);
+end:
+ return err;
+}
+
+static void btusb_disconnect(struct usb_interface *intf)
+{
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct hci_dev *hdev;
+
+ BTMTK_DBG("intf %p", intf);
+ bdev = usb_get_intfdata(intf);
+ if (!bdev) {
+ BTMTK_WARN("%s: bdev is NULL!", __func__);
+ return;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (!cif_dev) {
+ BTMTK_WARN("%s: cif_dev is NULL!", __func__);
+ return;
+ }
+
+ hdev = bdev->hdev;
+ usb_set_intfdata(cif_dev->intf, NULL);
+
+ if (cif_dev->isoc)
+ usb_set_intfdata(cif_dev->isoc, NULL);
+
+ if (cif_dev->iso_channel)
+ usb_set_intfdata(cif_dev->iso_channel, NULL);
+
+ if (intf == cif_dev->intf) {
+ if (cif_dev->isoc)
+ usb_driver_release_interface(&btusb_driver, cif_dev->isoc);
+ if (cif_dev->iso_channel)
+ usb_driver_release_interface(&btusb_driver, cif_dev->iso_channel);
+ } else if (intf == cif_dev->isoc) {
+ usb_driver_release_interface(&btusb_driver, cif_dev->intf);
+ } else if (intf == cif_dev->iso_channel) {
+ usb_driver_release_interface(&btusb_driver, cif_dev->intf);
+ }
+
+ btmtk_cif_free_memory(cif_dev);
+
+ btmtk_main_cif_disconnect_notify(bdev, HCI_USB);
+}
+
+#ifdef CONFIG_PM
+static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct btmtk_dev *bdev = usb_get_intfdata(intf);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int ret = 0;
+
+ BTMTK_DBG("intf %p", intf);
+
+ if (bdev->suspend_count++) {
+ BTMTK_WARN("%s: Has suspended. suspend_count: %d end",
+ __func__, bdev->suspend_count);
+ return 0;
+ }
+
+#if CFG_SUPPORT_DVT
+ BTMTK_INFO("%s: SKIP Driver woble_suspend flow", __func__);
+#else
+ ret = btmtk_woble_suspend(bdev);
+ if (ret < 0)
+ BTMTK_ERR("%s: btmtk_woble_suspend return fail %d", __func__, ret);
+#endif
+
+ spin_lock_irq(&bdev->txlock);
+ if (!(PMSG_IS_AUTO(message) && bdev->tx_in_flight)) {
+ set_bit(BTUSB_SUSPENDING, &bdev->flags);
+ spin_unlock_irq(&bdev->txlock);
+ } else {
+ spin_unlock_irq(&bdev->txlock);
+ bdev->suspend_count--;
+ return -EBUSY;
+ }
+
+ cancel_work_sync(&bdev->work);
+
+ btusb_stop_traffic(cif_dev);
+ usb_kill_anchored_urbs(&cif_dev->tx_anchor);
+
+ BTMTK_INFO("%s end, suspend_count = %d", __func__, bdev->suspend_count);
+
+ return ret;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+ struct btmtk_dev *bdev = usb_get_intfdata(intf);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct hci_dev *hdev = bdev->hdev;
+ int err = 0;
+ unsigned int ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ BTMTK_INFO("%s begin", __func__);
+
+ if (--bdev->suspend_count) {
+ BTMTK_WARN("%s: bdev->suspend_count %d, return 0", __func__,
+ bdev->suspend_count);
+ return 0;
+ }
+
+ /* need to remove it when BT off, need support woble case*/
+ /* if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ * BTMTK_WARN("%s: hdev flags is not hci running. return", __func__);
+ * goto done;
+ * }
+ */
+
+ /* when BT off, BTUSB_INTR_RUNNING will be clear,
+ * so we need to start traffic in btmtk_woble_resume when BT off
+ */
+ if (test_bit(BTUSB_INTR_RUNNING, &bdev->flags)) {
+ err = btusb_submit_intr_urb(hdev, GFP_NOIO);
+ if (err < 0) {
+ clear_bit(BTUSB_INTR_RUNNING, &bdev->flags);
+ goto done;
+ }
+
+ if (is_mt7961(bdev->chip_id) && BTMTK_IS_BT_0_INTF(ifnum_base)) {
+ BTMTK_INFO("%s 7961 submit urb\n", __func__);
+ if (cif_dev->reset_intr_ep) {
+ err = btusb_submit_intr_reset_urb(hdev, GFP_KERNEL);
+ if (err < 0) {
+ clear_bit(BTUSB_INTR_RUNNING, &bdev->flags);
+ goto done;
+ }
+ } else
+ BTMTK_INFO("%s, reset_intr_ep missing, don't summit",
+ __func__);
+
+ if (cif_dev->intr_iso_rx_ep) {
+ err = btusb_submit_intr_ble_isoc_urb(hdev, GFP_KERNEL);
+ if (err < 0) {
+ usb_kill_anchored_urbs(&cif_dev->ble_isoc_anchor);
+ clear_bit(BTUSB_INTR_RUNNING, &bdev->flags);
+ goto done;
+ }
+ } else
+ BTMTK_INFO("%s, intr_iso_rx_ep missing, don't summit",
+ __func__);
+ }
+ }
+
+ if (test_bit(BTUSB_BULK_RUNNING, &bdev->flags)) {
+ err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ if (err < 0) {
+ clear_bit(BTUSB_BULK_RUNNING, &bdev->flags);
+ goto done;
+ }
+
+ btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ }
+
+ if (test_bit(BTUSB_ISOC_RUNNING, &bdev->flags)) {
+ if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ else
+ btusb_submit_isoc_urb(hdev, GFP_NOIO);
+ }
+
+ if (test_bit(BTUSB_BLE_ISOC_RUNNING, &bdev->flags)) {
+ if (btusb_submit_intr_ble_isoc_urb(hdev, GFP_NOIO) < 0)
+ clear_bit(BTUSB_BLE_ISOC_RUNNING, &bdev->flags);
+ else
+ btusb_submit_intr_ble_isoc_urb(hdev, GFP_NOIO);
+ }
+
+ spin_lock_irq(&bdev->txlock);
+ clear_bit(BTUSB_SUSPENDING, &bdev->flags);
+ spin_unlock_irq(&bdev->txlock);
+ schedule_work(&bdev->work);
+
+#if CFG_SUPPORT_DVT
+ BTMTK_INFO("%s: SKIP Driver woble_resume flow", __func__);
+#else
+ err = btmtk_woble_resume(bdev);
+ if (err < 0) {
+ BTMTK_ERR("%s: btmtk_woble_resume return fail %d", __func__, err);
+ goto done;
+ }
+#endif
+
+ BTMTK_INFO("%s end", __func__);
+
+ return 0;
+
+done:
+ spin_lock_irq(&bdev->txlock);
+ clear_bit(BTUSB_SUSPENDING, &bdev->flags);
+ spin_unlock_irq(&bdev->txlock);
+
+ return err;
+}
+#endif
+
+static int btmtk_cif_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret = -1;
+ int cif_event = 0;
+ unsigned int ifnum_base;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+
+ /* Mediatek Driver Version */
+ BTMTK_INFO("%s: MTK BT Driver Version : %s", __func__, VERSION);
+
+ /* USB interface only.
+ * USB will need to identify thru descriptor's interface numbering.
+ */
+ ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber;
+ BTMTK_DBG("intf %p id %p, interfacenum = %d", intf, id, ifnum_base);
+
+ /* interface numbers are hardcoded in the spec */
+ if (ifnum_base != BT0_MCU_INTERFACE_NUM &&
+ ifnum_base != BT1_MCU_INTERFACE_NUM)
+ return -ENODEV;
+
+ /* Retrieve priv data and set to interface structure */
+ bdev = btmtk_get_dev();
+ usb_set_intfdata(intf, bdev);
+ bdev->cif_dev = &g_usb_dev[bdev->dongle_index][intf_to_idx[ifnum_base]];
+
+ /* Retrieve current HIF event state */
+ cif_event = HIF_EVENT_PROBE;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s intf[%d] priv setting is NULL", __func__, ifnum_base);
+ return -ENODEV;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ ret = btusb_probe(intf, id);
+
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+
+ return ret;
+}
+
+static void btmtk_cif_disconnect(struct usb_interface *intf)
+{
+ int cif_event = 0;
+ unsigned int ifnum_base;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+
+ BTMTK_CIF_GET_DEV_PRIV(bdev, intf, ifnum_base);
+
+ /* Retrieve current HIF event state */
+ cif_event = HIF_EVENT_DISCONNECT;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s intf[%d] priv setting is NULL", __func__, ifnum_base);
+ return;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ btmtk_usb_cif_mutex_lock(bdev);
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ btusb_disconnect(intf);
+
+ /* Set End/Error state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ btmtk_usb_cif_mutex_unlock(bdev);
+}
+
+#ifdef CONFIG_PM
+static int btmtk_cif_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ int ret = 0;
+ unsigned int ifnum_base;
+ int cif_event = 0;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+ int state = BTMTK_STATE_INIT;
+
+ BTMTK_INFO("%s, enter", __func__);
+ BTMTK_CIF_GET_DEV_PRIV(bdev, intf, ifnum_base);
+
+ state = btmtk_get_chip_state(bdev);
+ /* Retrieve current HIF event state */
+ if (state == BTMTK_STATE_FW_DUMP) {
+ BTMTK_WARN("%s: FW dumping ongoing, don't dos suspend flow!!!", __func__);
+ cif_event = HIF_EVENT_FW_DUMP;
+ } else
+ cif_event = HIF_EVENT_SUSPEND;
+
+ if (BTMTK_IS_BT_0_INTF(ifnum_base) || BTMTK_IS_BT_1_INTF(ifnum_base)) {
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s intf[%d] priv setting is NULL", __func__, ifnum_base);
+ return -ENODEV;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ ret = btusb_suspend(intf, message);
+
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+ } else
+ BTMTK_INFO("%s, interface num is for isoc interface, do't do suspend!", __func__);
+
+ BTMTK_INFO("%s, end. ret = %d", __func__, ret);
+ return ret;
+}
+
+static int btmtk_cif_resume(struct usb_interface *intf)
+{
+ int ret = 0;
+ unsigned int ifnum_base;
+ int cif_event = 0;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+
+ BTMTK_INFO("%s, enter", __func__);
+ BTMTK_CIF_GET_DEV_PRIV(bdev, intf, ifnum_base);
+
+ if (BTMTK_IS_BT_0_INTF(ifnum_base) || BTMTK_IS_BT_1_INTF(ifnum_base)) {
+ /* Retrieve current HIF event state */
+ cif_event = HIF_EVENT_RESUME;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s intf[%d] priv setting is NULL", __func__, ifnum_base);
+ return -ENODEV;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ ret = btusb_resume(intf);
+
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+ } else
+ BTMTK_INFO("%s, interface num is for isoc interface, do't do resume!", __func__);
+
+ BTMTK_INFO("%s, end. ret = %d", __func__, ret);
+ return ret;
+}
+#endif // CONFIG_PM //
+
+#if !BT_DISABLE_RESET_RESUME
+static int btmtk_cif_reset_resume(struct usb_interface *intf)
+{
+ BTMTK_INFO("%s: Call resume directly", __func__);
+ return btmtk_cif_resume(intf);
+}
+#endif
+
+static struct usb_driver btusb_driver = {
+ .name = "btusb",
+ .probe = btmtk_cif_probe,
+ .disconnect = btmtk_cif_disconnect,
+#ifdef CONFIG_PM
+ .suspend = btmtk_cif_suspend,
+ .resume = btmtk_cif_resume,
+#endif
+#if !BT_DISABLE_RESET_RESUME
+ .reset_resume = btmtk_cif_reset_resume,
+#endif
+ .id_table = btusb_table,
+ .supports_autosuspend = 1,
+ .disable_hub_initiated_lpm = 1,
+};
+
+int btmtk_cif_register(void)
+{
+ int retval = 0;
+ struct hif_hook_ptr hook;
+
+ BTMTK_INFO("%s", __func__);
+
+ memset(&hook, 0, sizeof(hook));
+ hook.open = btmtk_usb_open;
+ hook.close = btmtk_usb_close;
+ hook.reg_read = btmtk_usb_read_register;
+ hook.reg_write = btmtk_usb_write_register;
+ hook.send_cmd = btmtk_usb_send_cmd;
+ hook.send_and_recv = btmtk_usb_send_and_recv;
+ hook.event_filter = btmtk_usb_event_filter;
+ hook.subsys_reset = btmtk_usb_subsys_reset;
+ hook.whole_reset = btmtk_usb_toggle_rst_pin;
+ hook.chip_reset_notify = btmtk_usb_chip_reset_notify;
+ hook.cif_mutex_lock = btmtk_usb_cif_mutex_lock;
+ hook.cif_mutex_unlock = btmtk_usb_cif_mutex_unlock;
+ hook.dl_dma = btmtk_usb_load_fw_patch_using_dma;
+ btmtk_reg_hif_hook(&hook);
+
+ retval = usb_register(&btusb_driver);
+ if (retval)
+ BTMTK_ERR("*** USB registration fail(%d)! ***", retval);
+ else
+ BTMTK_INFO("%s, usb registration success!", __func__);
+ return retval;
+}
+
+int btmtk_cif_deregister(void)
+{
+ BTMTK_INFO("%s", __func__);
+ usb_deregister(&btusb_driver);
+ BTMTK_INFO("%s: Done", __func__);
+ return 0;
+}
+
+static int btmtk_cif_allocate_memory(struct btmtk_usb_dev *cif_dev)
+{
+ BTMTK_INFO("%s", __func__);
+
+ if (cif_dev->o_usb_buf == NULL) {
+ cif_dev->o_usb_buf = kzalloc(HCI_MAX_COMMAND_SIZE, GFP_KERNEL);
+ if (!cif_dev->o_usb_buf) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->o_usb_buf)", __func__);
+ return -1;
+ }
+ }
+
+ if (cif_dev->urb_intr_buf == NULL) {
+ cif_dev->urb_intr_buf = kzalloc(URB_MAX_BUFFER_SIZE, GFP_KERNEL);
+ if (!cif_dev->urb_intr_buf) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->urb_intr_buf)", __func__);
+ return -1;
+ }
+ }
+ if (cif_dev->urb_bulk_buf == NULL) {
+ cif_dev->urb_bulk_buf = kzalloc(URB_MAX_BUFFER_SIZE, GFP_KERNEL);
+ if (!cif_dev->urb_bulk_buf) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->urb_bulk_buf)", __func__);
+ return -1;
+ }
+ }
+ if (cif_dev->urb_ble_isoc_buf == NULL) {
+ cif_dev->urb_ble_isoc_buf = kzalloc(URB_MAX_BUFFER_SIZE, GFP_KERNEL);
+ if (!cif_dev->urb_ble_isoc_buf) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->urb_ble_isoc_buf)", __func__);
+ return -1;
+ }
+ }
+
+ BTMTK_INFO("%s: Done", __func__);
+ return 0;
+}
+
+static void btmtk_cif_free_memory(struct btmtk_usb_dev *cif_dev)
+{
+ if (!cif_dev) {
+ BTMTK_ERR("%s: bdev is NULL!", __func__);
+ return;
+ }
+
+ kfree(cif_dev->o_usb_buf);
+ cif_dev->o_usb_buf = NULL;
+
+ kfree(cif_dev->urb_intr_buf);
+ cif_dev->urb_intr_buf = NULL;
+
+ kfree(cif_dev->urb_bulk_buf);
+ cif_dev->urb_bulk_buf = NULL;
+
+ kfree(cif_dev->urb_ble_isoc_buf);
+ cif_dev->urb_ble_isoc_buf = NULL;
+
+ memset(cif_dev, 0, sizeof(struct btmtk_usb_dev));
+
+ BTMTK_INFO("%s: Success", __func__);
+}
+
+static int btmtk_cif_write_uhw_register(struct btmtk_dev *bdev, u32 reg, u32 val)
+{
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int ret = -1;
+ __le16 reg_high;
+ __le16 reg_low;
+ u8 reset_buf[4];
+
+ reg_high = ((reg >> 16) & 0xffff);
+ reg_low = (reg & 0xffff);
+
+ reset_buf[0] = (val & 0x00ff);
+ reset_buf[1] = ((val >> 8) & 0x00ff);
+ reset_buf[2] = ((val >> 16) & 0x00ff);
+ reset_buf[3] = ((val >> 24) & 0x00ff);
+
+ memcpy(cif_dev->o_usb_buf, reset_buf, sizeof(reset_buf));
+ ret = usb_control_msg(cif_dev->udev, usb_sndctrlpipe(cif_dev->udev, 0),
+ 0x02, /* bRequest */
+ 0x5E, /* bRequestType */
+ reg_high, /* wValue */
+ reg_low, /* wIndex */
+ cif_dev->o_usb_buf,
+ sizeof(reset_buf), USB_CTRL_IO_TIMO);
+
+ BTMTK_DBG("%s: high=%x, reg_low=%x, val=%x", __func__, reg_high, reg_low, val);
+ BTMTK_DBG("%s: reset_buf = %x %x %x %x",
+ __func__, reset_buf[3], reset_buf[2], reset_buf[1], reset_buf[0]);
+
+ if (ret < 0) {
+ val = 0xffffffff;
+ BTMTK_ERR("%s: error(%d), reg=%x, value=%x", __func__, ret, reg, val);
+ return ret;
+ }
+ return 0;
+}
+
+static int btmtk_cif_read_uhw_register(struct btmtk_dev *bdev, u32 reg, u32 *val)
+{
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int ret = -1;
+ __le16 reg_high;
+ __le16 reg_low;
+
+ reg_high = ((reg >> 16) & 0xffff);
+ reg_low = (reg & 0xffff);
+
+ memset(bdev->io_buf, 0, IO_BUF_SIZE);
+ ret = usb_control_msg(cif_dev->udev, usb_rcvctrlpipe(cif_dev->udev, 0),
+ 0x01, /* bRequest */
+ 0xDE, /* bRequestType */
+ reg_high, /* wValue */
+ reg_low, /* wIndex */
+ bdev->io_buf,
+ 4, USB_CTRL_IO_TIMO);
+
+ if (ret < 0) {
+ *val = 0xffffffff;
+ BTMTK_ERR("%s: error(%d), reg=%x, value=0x%08x", __func__, ret, reg, *val);
+ return ret;
+ }
+
+ memmove(val, bdev->io_buf, sizeof(u32));
+ *val = le32_to_cpu(*val);
+
+ BTMTK_DBG("%s: reg=%x, value=0x%08x", __func__, reg, *val);
+
+ return 0;
+}
+
+static int btmtk_usb_read_register(struct btmtk_dev *bdev, u32 reg, u32 *val)
+{
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int ret = -1;
+ __le16 reg_high;
+ __le16 reg_low;
+
+ reg_high = ((reg >> 16) & 0xffff);
+ reg_low = (reg & 0xffff);
+
+ memset(bdev->io_buf, 0, IO_BUF_SIZE);
+ ret = usb_control_msg(cif_dev->udev, usb_rcvctrlpipe(cif_dev->udev, 0),
+ 0x63, /* bRequest */
+ DEVICE_VENDOR_REQUEST_IN, /* bRequestType */
+ reg_high, /* wValue */
+ reg_low, /* wIndex */
+ bdev->io_buf,
+ sizeof(u32), USB_CTRL_IO_TIMO);
+
+ if (ret < 0) {
+ *val = 0xffffffff;
+ BTMTK_ERR("%s: error(%d), reg=%x, value=%x", __func__, ret, reg, *val);
+ return ret;
+ }
+
+ memmove(val, bdev->io_buf, sizeof(u32));
+ *val = le32_to_cpu(*val);
+
+ return 0;
+}
+
+static int btmtk_usb_write_register(struct btmtk_dev *bdev, u32 reg, u32 val)
+{
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int ret = -1;
+ __le16 reg_high;
+ __le16 reg_low;
+ u8 buf[4];
+
+ reg_high = ((reg >> 16) & 0xffff);
+ reg_low = (reg & 0xffff);
+
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = (val & 0x00ff);
+ buf[3] = ((val >> 8) & 0x00ff);
+
+ memcpy(cif_dev->o_usb_buf, buf, sizeof(buf));
+ ret = usb_control_msg(cif_dev->udev, usb_sndctrlpipe(cif_dev->udev, 0),
+ 0x66, /* bRequest */
+ 0x40, /* bRequestType */
+ reg_high, /* wValue */
+ reg_low, /* wIndex */
+ cif_dev->o_usb_buf,
+ sizeof(buf), USB_CTRL_IO_TIMO);
+
+ BTMTK_DBG("%s: buf = %x %x %x %x", __func__, buf[3], buf[2], buf[1], buf[0]);
+
+ if (ret < 0) {
+ val = 0xffffffff;
+ BTMTK_ERR("%s: error(%d), reg=%x, value=%x", __func__, ret, reg, val);
+ return ret;
+ }
+
+ return 0;
+}
+
+
+int btmtk_cif_send_calibration(struct btmtk_dev *bdev)
+{
+ return 0;
+}
+
+static void btmtk_cif_load_rom_patch_complete(struct urb *urb)
+{
+ struct completion *sent_to_mcu_done = (struct completion *)urb->context;
+
+ complete(sent_to_mcu_done);
+}
+
+int btmtk_cif_send_control_out(struct btmtk_dev *bdev, struct sk_buff *skb, int delay, int retry)
+{
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int ret = 0;
+ unsigned int ifnum_base;
+
+ if (bdev == NULL || bdev->hdev == NULL || bdev->io_buf == NULL || skb == NULL ||
+ skb->len > HCI_MAX_COMMAND_SIZE || skb->len <= 0) {
+ BTMTK_ERR("%s: incorrect parameter", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (cif_dev->udev == NULL || cif_dev->o_usb_buf == NULL) {
+ BTMTK_ERR("%s: cif_dev is invalid", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ /* send wmt command */
+ memcpy(cif_dev->o_usb_buf, skb->data + 1, skb->len - 1);
+ BTMTK_INFO_RAW(skb->data + 1, skb->len - 1, "%s: cmd:", __func__);
+ if (BTMTK_IS_BT_0_INTF(ifnum_base))
+ ret = usb_control_msg(cif_dev->udev, usb_sndctrlpipe(cif_dev->udev, 0),
+ 0x01, DEVICE_CLASS_REQUEST_OUT,
+ 0x30, 0x00, (void *)cif_dev->o_usb_buf,
+ skb->len - 1, USB_CTRL_IO_TIMO);
+ else if (BTMTK_IS_BT_1_INTF(ifnum_base))
+ ret = usb_control_msg(cif_dev->udev, usb_sndctrlpipe(cif_dev->udev, 0),
+ 0x00, 0x21, 0x00, 0x03,
+ (void *)cif_dev->o_usb_buf, skb->len - 1, USB_CTRL_IO_TIMO);
+
+ if (ret < 0) {
+ BTMTK_ERR("%s: command send failed(%d)", __func__, ret);
+ goto exit;
+ }
+exit:
+ kfree_skb(skb);
+ skb = NULL;
+ return ret;
+}
+
+static int btmtk_cif_send_bulk_out(struct btmtk_dev *bdev, struct sk_buff *skb)
+{
+ int ret = 0;
+ struct urb *urb;
+ unsigned int pipe;
+ struct completion sent_to_mcu_done;
+ void *buf;
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ /* why need to alloc dma buffer??*/
+ buf = usb_alloc_coherent(cif_dev->udev, UPLOAD_PATCH_UNIT, GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ init_completion(&sent_to_mcu_done);
+
+ pipe = usb_sndbulkpipe(cif_dev->udev, cif_dev->bulk_tx_ep->bEndpointAddress);
+
+ memcpy(buf, skb->data + 1, skb->len - 1);
+ usb_fill_bulk_urb(urb,
+ cif_dev->udev,
+ pipe,
+ buf,
+ skb->len - 1,
+ (usb_complete_t)btmtk_cif_load_rom_patch_complete,
+ &sent_to_mcu_done);
+
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0) {
+ BTMTK_ERR("%s: submit urb failed (%d)", __func__, ret);
+ goto error;
+ }
+
+ if (!wait_for_completion_timeout
+ (&sent_to_mcu_done, msecs_to_jiffies(1000))) {
+ usb_kill_urb(urb);
+ BTMTK_ERR("%s: upload rom_patch timeout", __func__);
+ ret = -ETIME;
+ goto error;
+ }
+
+error:
+ usb_free_coherent(cif_dev->udev, UPLOAD_PATCH_UNIT, buf, urb->transfer_dma);
+exit:
+ usb_free_urb(urb);
+ kfree_skb(skb);
+ skb = NULL;
+ return ret;
+}
+
+int btmtk_usb_send_cmd(struct btmtk_dev *bdev, struct sk_buff *skb,
+ int delay, int retry, int pkt_type)
+{
+ int ret = -1;
+
+ if (pkt_type == BTMTK_TX_CMD_FROM_DRV) {
+ /* handle wmt cmd from driver */
+ ret = btmtk_cif_send_control_out(bdev, skb, delay, retry);
+ } else if (pkt_type == BTMTK_TX_ACL_FROM_DRV) {
+ /* bulk out for load rom patch*/
+ ret = btmtk_cif_send_bulk_out(bdev, skb);
+ } else if (pkt_type == BTMTK_TX_PKT_FROM_HOST) {
+ /* handle hci cmd and acl pkt from host, handle hci cmd from driver */
+ ret = btusb_send_frame(bdev->hdev, skb);
+ }
+
+ return ret;
+}
+
+static int btmtk_cif_recv_evt(struct btmtk_dev *bdev, int delay, int retry)
+{
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int ret = -1; /* if successful, 0 */
+ unsigned int ifnum_base;
+
+ if (bdev == NULL) {
+ BTMTK_ERR("%s: bdev == NULL!\n", __func__);
+ return ret;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (!cif_dev->udev || !bdev->hdev) {
+ BTMTK_ERR("%s: invalid parameters!\n", __func__);
+ return ret;
+ }
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+get_response_again:
+ /* ms delay */
+ mdelay(delay);
+
+ /* check WMT event */
+ memset(bdev->io_buf, 0, IO_BUF_SIZE);
+ bdev->io_buf[0] = HCI_EVENT_PKT;
+ if (BTMTK_IS_BT_0_INTF(ifnum_base))
+ ret = usb_control_msg(cif_dev->udev, usb_rcvctrlpipe(cif_dev->udev, 0),
+ 0x01, DEVICE_VENDOR_REQUEST_IN, 0x30, 0x00, bdev->io_buf + 1,
+ HCI_USB_IO_BUF_SIZE, USB_CTRL_IO_TIMO);
+ else if (BTMTK_IS_BT_1_INTF(ifnum_base))
+ ret = usb_control_msg(cif_dev->udev, usb_rcvctrlpipe(cif_dev->udev, 0),
+ 0x01, 0xA1, 0x30, 0x03, bdev->io_buf + 1, HCI_USB_IO_BUF_SIZE,
+ USB_CTRL_IO_TIMO);
+
+ if (ret < 0) {
+ BTMTK_ERR("%s: event get failed(%d)", __func__, ret);
+ return ret;
+ }
+
+ if (ret > 0) {
+ BTMTK_DBG_RAW(bdev->io_buf, ret + 1, "%s OK: EVT:", __func__);
+ return ret + 1; /* return read length */
+ } else if (retry > 0) {
+ BTMTK_WARN("%s: Trying to get response... (%d)", __func__, ret);
+ retry--;
+ goto get_response_again;
+ } else
+ BTMTK_ERR("%s NG: do not got response:(%d)", __func__, ret);
+
+ return -1;
+}
+
+int btmtk_usb_send_and_recv(struct btmtk_dev *bdev,
+ struct sk_buff *skb,
+ const uint8_t *event, const int event_len,
+ int delay, int retry, int pkt_type)
+{
+ unsigned long comp_event_timo = 0, start_time = 0;
+ int ret = 0;
+
+ if (bdev == NULL) {
+ BTMTK_ERR("%s: bdev == NULL!\n", __func__);
+ return ret;
+ }
+
+ if ((pkt_type == BTMTK_TX_CMD_FROM_DRV || pkt_type == BTMTK_TX_ACL_FROM_DRV)) {
+ BTMTK_DBG_RAW(skb->data, skb->len, "%s, send, len = %d", __func__, skb->len);
+
+ ret = btmtk_usb_send_cmd(bdev, skb, delay, retry, pkt_type);
+ if (ret < 0) {
+ BTMTK_ERR("%s btmtk_usb_send_cmd failed!!", __func__);
+ goto exit;
+ }
+
+ if (event && event_len > 0) {
+ bdev->recv_evt_len = btmtk_cif_recv_evt(bdev, delay, retry);
+ if (bdev->recv_evt_len < 0) {
+ BTMTK_ERR("%s btmtk_cif_recv_evt failed!!", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ if (bdev->io_buf && bdev->recv_evt_len >= event_len) {
+ if (memcmp(bdev->io_buf, event, event_len) == 0) {
+ ret = 0;
+ goto exit;
+ }
+ }
+ BTMTK_INFO("%s compare fail\n", __func__);
+ BTMTK_INFO_RAW(event, event_len, "%s: event_need_compare:", __func__);
+ BTMTK_INFO_RAW(bdev->io_buf, bdev->recv_evt_len, "%s: RCV:", __func__);
+ ret = -1;
+ } else {
+ ret = 0;
+ }
+ } else {
+ if (event) {
+ if (event_len > EVENT_COMPARE_SIZE) {
+ BTMTK_ERR("%s, event_len (%d) > EVENT_COMPARE_SIZE(%d), error",
+ __func__, event_len, EVENT_COMPARE_SIZE);
+ ret = -1;
+ goto exit;
+ }
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_NEED_COMPARE;
+ memcpy(event_need_compare, event + 1, event_len - 1);
+ event_need_compare_len = event_len - 1;
+
+ start_time = jiffies;
+ /* check hci event /wmt event for SDIO/UART interface, check hci
+ * event for USB interface
+ */
+ comp_event_timo = jiffies + msecs_to_jiffies(WOBLE_COMP_EVENT_TIMO);
+ BTMTK_INFO("event_need_compare_len %d, event_compare_status %d",
+ event_need_compare_len, event_compare_status);
+ } else {
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
+ }
+
+ BTMTK_DBG_RAW(skb->data, skb->len, "%s, send, len = %d", __func__, skb->len);
+
+ ret = btmtk_usb_send_cmd(bdev, skb, delay, retry, pkt_type);
+ if (ret < 0) {
+ BTMTK_ERR("%s btmtk_sdio_send_cmd failed!!", __func__);
+ goto exit;
+ }
+
+ do {
+ /* check if event_compare_success */
+ if (event_compare_status == BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS) {
+ ret = 0;
+ break;
+ }
+ usleep_range(10, 100);
+ } while (time_before(jiffies, comp_event_timo));
+
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_NOTHING_NEED_COMPARE;
+ }
+
+exit:
+ return ret;
+}
+
+void btmtk_usb_chip_reset_notify(struct btmtk_dev *bdev)
+{
+ cancel_work_sync(&bdev->work);
+ cancel_work_sync(&bdev->waker);
+}
+
+int btmtk_usb_event_filter(struct btmtk_dev *bdev, struct sk_buff *skb)
+{
+ if (event_compare_status == BTMTK_EVENT_COMPARE_STATE_NEED_COMPARE &&
+ skb->len >= event_need_compare_len) {
+ if (memcmp(skb->data, READ_ADDRESS_EVENT,
+ sizeof(READ_ADDRESS_EVENT)) == 0 && (skb->len == 12)) {
+ memcpy(bdev->bdaddr, &skb->data[6], BD_ADDRESS_SIZE);
+ BTMTK_INFO("GET BDADDR = %02X:%02X:%02X:%02X:%02X:%02X",
+ bdev->bdaddr[0], bdev->bdaddr[1], bdev->bdaddr[2],
+ bdev->bdaddr[3], bdev->bdaddr[4], bdev->bdaddr[5]);
+
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
+ } else if (memcmp(skb->data, event_need_compare,
+ event_need_compare_len) == 0) {
+ /* if it is wobx debug event, just print in kernel log, drop it
+ * by driver, don't send to stack
+ */
+ if (skb->data[0] == 0xE8)
+ BTMTK_INFO_RAW(skb->data, skb->len,
+ "%s: wobx debug log:", __func__);
+
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
+ BTMTK_INFO("%s, compare success", __func__);
+ } else {
+ BTMTK_INFO("%s compare fail", __func__);
+ BTMTK_INFO_RAW(event_need_compare, event_need_compare_len,
+ "%s: event_need_compare:", __func__);
+ BTMTK_INFO_RAW(skb->data, skb->len, "%s: skb->data:", __func__);
+ return 0;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
--
2.18.0
\
 
 \ /
  Last update: 2020-11-30 08:20    [W:0.374 / U:0.112 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site