[WCNCR00152295] Add turnkey sdio driver to new repo

[Description]
1. Add turnkey sdio driver to new repo

CR-Id: WCNCR00152295
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c0e0cdb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,56 @@
+export KERNEL_SRC := /lib/modules/$(shell uname -r)/build
+
+###############################################################################
+# USB
+###############################################################################
+USB_MOD_NAME = btmtk_usb
+USB_CFILES := \
+	btmtk_usb_main.c \
+	btmtk_usb_dbgfs.c \
+	btmtk_usb_fifo.c
+$(USB_MOD_NAME)-objs := $(USB_CFILES:.c=.o)
+
+###############################################################################
+# SDIO
+###############################################################################
+VPATH = /opt/toolchains/gcc-linaro-aarch64-linux-gnu-4.9-2014.09_linux
+SDIO_MOD_NAME = btmtksdio
+SDIO_CFILES := \
+	btmtk_sdio.c btmtk_main.c
+$(SDIO_MOD_NAME)-objs := $(SDIO_CFILES:.c=.o)
+
+###############################################################################
+# Common
+###############################################################################
+obj-m := $(USB_MOD_NAME).o $(SDIO_MOD_NAME).o
+
+all:
+	make -C $(KERNEL_SRC) M=$(PWD) modules
+
+usb:
+	make -C $(KERNEL_SRC) M=$(PWD) $(USB_MOD_NAME).ko
+
+sdio:
+	#make -C $(KERNEL_SRC) M=$(PWD) $(SDIO_MOD_NAME).ko
+	make -C /Projects/ZTE/linux M=$(PWD) modules ARCH=arm64 CROSS_COMPILE=/opt/toolchains/gcc-linaro-aarch64-linux-gnu-4.9-2014.09_linux/bin/aarch64-linux-gnu-
+
+clean:
+	make -C $(KERNEL_SRC) M=$(PWD) clean
+
+# Check coding style
+export IGNORE_CODING_STYLE_RULES := NEW_TYPEDEFS,LEADING_SPACE,CODE_INDENT,SUSPECT_CODE_INDENT
+ccs:
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_usb_main.c
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_usb_main.h
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_config.h
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_define.h
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_usb_dbgfs.c
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_usb_dbgfs.h
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_usb_fifo.c
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_usb_fifo.h
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_drv.h
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_main.c
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_sdio.c
+	./util/checkpatch.pl --no-tree --show-types --max-line-length=120 --ignore $(IGNORE_CODING_STYLE_RULES) -f btmtk_sdio.h
+
+
diff --git a/btmtk_config.h b/btmtk_config.h
new file mode 100644
index 0000000..8e4445f
--- /dev/null
+++ b/btmtk_config.h
@@ -0,0 +1,81 @@
+/*
+*  Copyright (c) 2016 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 the
+*  GNU General Public License for more details.
+*/
+#ifndef __BTMTK_CONFIG_H__
+#define __BTMTK_CONFIG_H__
+
+#include <linux/usb.h>
+#include <linux/version.h>
+
+/**
+ * Kernel configuration check
+ */
+#ifndef CONFIG_PM
+	#error "ERROR : CONFIG_PM should be turn on."
+#endif
+
+/**
+ * Support IC configuration
+ */
+#define SUPPORT_MT7662 1
+#define SUPPORT_MT7668 1
+
+/**
+ * Debug Level Configureation
+ */
+#define ENABLE_BT_FIFO_THREAD	1
+
+/**
+ * BTMTK LOG location, last char must be '/'
+ */
+/* #define BTMTK_LOG_PATH	"/data/misc/bluedroid/" */
+
+/**
+ * USB device ID configureation
+ */
+static struct usb_device_id btmtk_usb_table[] = {
+#if SUPPORT_MT7662
+	{USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7662, 0xe0, 0x01, 0x01), .bInterfaceNumber = 0},	/* MT7662U */
+	{USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7632, 0xe0, 0x01, 0x01), .bInterfaceNumber = 0},	/* MT7632U */
+	{USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x76a0, 0xe0, 0x01, 0x01), .bInterfaceNumber = 0},	/* MT7662T */
+	{USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x76a1, 0xe0, 0x01, 0x01), .bInterfaceNumber = 0},	/* MT7632T */
+#endif
+
+#if SUPPORT_MT7668
+	{USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7668, 0xe0, 0x01, 0x01), .bInterfaceNumber = 0},
+#endif
+	{}
+};
+
+/**
+ * Fixed STPBT Major Device Id
+ */
+#define FIXED_STPBT_MAJOR_DEV_ID 111
+
+/**
+ * GPIO PIN configureation
+ */
+#ifndef BT_DONGLE_RESET_GPIO_PIN
+	#define BT_DONGLE_RESET_GPIO_PIN	220
+#endif /* BT_DONGLE_RESET_GPIO_PIN */
+
+
+/**
+ * WoBLE by BLE RC
+ */
+#ifndef SUPPORT_LEGACY_WOBLE
+	#define SUPPORT_LEGACY_WOBLE 0
+	#define BT_RC_VENDOR_DEFAULT 1
+	#define BT_RC_VENDOR_S0 0
+#endif
+
+#endif /* __BTMTK_CONFIG_H__ */
diff --git a/btmtk_define.h b/btmtk_define.h
new file mode 100644
index 0000000..91b2a96
--- /dev/null
+++ b/btmtk_define.h
@@ -0,0 +1,178 @@
+/*
+*  Copyright (c) 2016 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 the
+*  GNU General Public License for more details.
+*/
+#ifndef __BTMTK_DEFINE_H__
+#define __BTMTK_DEFINE_H__
+
+#include "btmtk_config.h"
+
+/**
+ * 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 level definition
+ */
+#define BTMTK_LOG_LEVEL_ERROR		1
+#define BTMTK_LOG_LEVEL_WARNING		2
+#define BTMTK_LOG_LEVEL_INFO		3
+#define BTMTK_LOG_LEVEL_DEBUG		4
+#define BTMTK_LOG_LEVEL_DEFAULT		BTMTK_LOG_LEVEL_INFO	/* default setting */
+
+#if defined(CONFIG_DEBUG_FS) && (CONFIG_DEBUG_FS == 1)
+
+extern u8 btmtk_log_lvl;
+
+#define BTUSB_ERR(fmt, ...)	 \
+	do { if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_ERROR) pr_warn("[btmtk_err] "fmt"\n", ##__VA_ARGS__); } while (0)
+#define BTUSB_WARN(fmt, ...)	\
+	do { if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_WARNING) pr_warn("[btmtk_warn] "fmt"\n", ##__VA_ARGS__); } while (0)
+#define BTUSB_INFO(fmt, ...)	\
+	do { if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_INFO) pr_warn("[btmtk_info] "fmt"\n", ##__VA_ARGS__); } while (0)
+#define BTUSB_DBG(fmt, ...)	 \
+	do { if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_DEBUG) pr_warn("[btmtk_debug] "fmt"\n", ##__VA_ARGS__); } while (0)
+
+#define BTUSB_DBG_RAW(p, l, fmt, ...)						\
+	do {									\
+		if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_DEBUG) {			\
+			int raw_count = 0;					\
+			const unsigned char *ptr = p;				\
+			pr_cont("[btmtk_debug] "fmt, ##__VA_ARGS__);		\
+			for (raw_count = 0; raw_count <= l; ++raw_count) {	\
+				pr_cont(" %02X", ptr[raw_count]);		\
+			}							\
+			pr_cont("\n");						\
+		}								\
+	} while (0)
+
+#else /* CONFIG_DEBUG_FS */
+
+#define BTUSB_ERR(fmt, ...)	 pr_warn("[btmtk_err] "fmt"\n", ##__VA_ARGS__)
+#define BTUSB_WARN(fmt, ...)	pr_warn("[btmtk_warn] "fmt"\n", ##__VA_ARGS__)
+#define BTUSB_INFO(fmt, ...)	pr_warn("[btmtk_info] "fmt"\n", ##__VA_ARGS__)
+#define BTUSB_DBG(fmt, ...)
+#define BTUSB_DBG_RAW(p, l, fmt, ...)
+
+#endif /* CONFIG_DEBUG_FS */
+
+/**
+ *
+ * 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
+
+/**
+ * Log file path & name, the default path is /sdcard
+ */
+#define SYSLOG_FNAME			"bt_sys_log"
+#define FWDUMP_FNAME			"bt_fw_dump"
+#ifdef BTMTK_LOG_PATH
+	#define SYS_LOG_FILE_NAME	(BTMTK_LOG_PATH SYSLOG_FNAME)
+	#define FW_DUMP_FILE_NAME	(BTMTK_LOG_PATH FWDUMP_FNAME)
+#else
+	#define SYS_LOG_FILE_NAME	"/sdcard/"SYSLOG_FNAME
+	#define FW_DUMP_FILE_NAME	"/sdcard/"FWDUMP_FNAME
+#endif /* FW_DUMP_FILE_NAME */
+
+/**
+ * SYS control
+ */
+#define SYSCTL	0x400000
+
+/**
+ * WLAN
+ */
+#define WLAN	0x410000
+
+/**
+ * MCUCTL
+ */
+#define CLOCK_CTL	0x0708
+#define INT_LEVEL	0x0718
+#define COM_REG0	0x0730
+#define SEMAPHORE_00	0x07B0
+#define SEMAPHORE_01	0x07B4
+#define SEMAPHORE_02	0x07B8
+#define SEMAPHORE_03	0x07BC
+
+/**
+ * Timeout setting, mescs
+ */
+#define USB_CTRL_IO_TIMO	100
+#define USB_INTR_MSG_TIMO	2000
+
+/**
+ * USB request type definition
+ */
+#define DEVICE_VENDOR_REQUEST_OUT	0x40
+#define DEVICE_VENDOR_REQUEST_IN	0xc0
+#define DEVICE_CLASS_REQUEST_OUT	0x20
+#define DEVICE_CLASS_REQUEST_IN	 0xa0
+
+#define BTUSB_MAX_ISOC_FRAMES	10
+#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
+
+/**
+ * ROM patch related
+ */
+#define PATCH_HCI_HEADER_SIZE	4
+#define PATCH_WMT_HEADER_SIZE	5
+#define PATCH_HEADER_SIZE	(PATCH_HCI_HEADER_SIZE + PATCH_WMT_HEADER_SIZE)
+#define UPLOAD_PATCH_UNIT	2048
+#define PATCH_INFO_SIZE		30
+#define PATCH_PHASE1		1
+#define PATCH_PHASE2		2
+#define PATCH_PHASE3		3
+
+#define META_BUFFER_SIZE	(1024 * 500)
+#define USB_IO_BUF_SIZE		(HCI_MAX_EVENT_SIZE > 256 ? HCI_MAX_EVENT_SIZE : 256)
+#define HCI_SNOOP_ENTRY_NUM	30
+#define HCI_SNOOP_BUF_SIZE	32
+
+/**
+ * stpbt device node
+ */
+#define BUFFER_SIZE	(1024 * 4)	/* Size of RX Queue */
+#define IOC_MAGIC	0xb0
+#define IOCTL_FW_ASSERT _IOWR(IOC_MAGIC, 0, void *)
+
+/**
+ * fw log queue count
+ */
+#define FWLOG_QUEUE_COUNT 200
+
+/**
+ * Maximum rom patch file name length
+ */
+#define MAX_BIN_FILE_NAME_LEN 32
+
+#endif /* __BTMTK_DEFINE_H__ */
diff --git a/btmtk_drv.h b/btmtk_drv.h
new file mode 100644
index 0000000..d130c1a
--- /dev/null
+++ b/btmtk_drv.h
@@ -0,0 +1,247 @@
+ /*
+  * Mediatek Bluetooth driver: global definitions & declarations
+  *
+  * Copyright (C) 2015, Mediatek International Ltd.
+  *
+  * This software file (the "File") is distributed by Mediatek International
+  * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+  * (the "License").  You may use, redistribute and/or modify this File in
+  * accordance with the terms and conditions of the License, a copy of which
+  * is available by writing to the Free Software Foundation, Inc.,
+  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+  * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+  *
+  *
+  * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+  * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+  * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+  * this warranty disclaimer.
+  *
+  */
+
+ #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;
+ };
+
+ struct btmtk_thread {
+         struct task_struct *task;
+         wait_queue_head_t wait_q;
+         void *priv;
+ };
+
+ struct btmtk_device {
+         void *card;
+         struct hci_dev *hcidev;
+
+         u8 dev_type;
+
+         u8 tx_dnld_rdy;
+
+         u8 psmode;
+         u8 pscmd;
+         u8 hsmode;
+         u8 hscmd;
+
+         /* Low byte is gap, high byte is GPIO */
+         u16 gpio_gap;
+
+         u8 hscfgcmd;
+         u8 sendcmdflag;
+ };
+
+ struct btmtk_adapter {
+         void *hw_regs_buf;
+         u8 *hw_regs;
+         u32 int_count;
+         struct sk_buff_head tx_queue;
+         struct sk_buff_head fops_queue;
+         u8 fops_mode;
+         u8 psmode;
+         u8 ps_state;
+         u8 hs_state;
+         u8 wakeup_tries;
+         wait_queue_head_t cmd_wait_q;
+         wait_queue_head_t event_hs_wait_q;
+         u8 cmd_complete;
+         bool is_suspended;
+ };
+
+ struct btmtk_private {
+         struct btmtk_device btmtk_dev;
+         struct btmtk_adapter *adapter;
+         struct btmtk_thread main_thread;
+         int (*hw_host_to_card)(struct btmtk_private *priv,
+                                 u8 *payload, u16 nb);
+
+         int (*hw_set_own_back)(int owntype);
+         int (*hw_process_int_status)(struct btmtk_private *priv);
+         void (*firmware_dump)(struct btmtk_private *priv);
+         spinlock_t driver_lock;         /* spinlock used by driver */
+ #ifdef CONFIG_DEBUG_FS
+         void *debugfs_data;
+ #endif
+         bool surprise_removed;
+#if SUPPORT_FW_DUMP
+		 struct semaphore fw_dump_semaphore;
+		 struct task_struct *fw_dump_tsk;
+		 struct task_struct *fw_dump_end_check_tsk;
+#endif
+ };
+
+ #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 */
+
+ int btmtk_register_hdev(struct btmtk_private *priv);
+ 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)
+
+typedef struct _OSAL_UNSLEEPABLE_LOCK_ {
+	spinlock_t lock;
+	unsigned long flag;
+} OSAL_UNSLEEPABLE_LOCK, *P_OSAL_UNSLEEPABLE_LOCK;
+
+typedef 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 */
+} ring_buffer_struct;
+
+ #ifdef CONFIG_DEBUG_FS
+ void btmtk_debugfs_init(struct hci_dev *hdev);
+ void btmtk_debugfs_remove(struct hci_dev *hdev);
+
+#define FIXED_STPBT_MAJOR_DEV_ID 111
+
+
+#define FW_DUMP_FILE_NAME "/tmp/log"
+#define FW_DUMP_END_EVENT "coredump end"
+
+#define BTMTK_LOG_LEVEL_ERROR       1
+#define BTMTK_LOG_LEVEL_WARNING     2
+#define BTMTK_LOG_LEVEL_INFO        3
+#define BTMTK_LOG_LEVEL_DEBUG       4
+
+#define BTMTK_LOG_LEVEL_DEFAULT      BTMTK_LOG_LEVEL_INFO  /* default setting */
+extern u8 btmtk_log_lvl;
+
+#define BTMTK_ERR(fmt, ...)     \
+    do {if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_ERROR) pr_warn("btmtk_err: "fmt"\n", ##__VA_ARGS__); } while (0)
+#define BTMTK_WARN(fmt, ...)    \
+    do {if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_WARNING) pr_warn("btmtk_warn: "fmt"\n", ##__VA_ARGS__); } while (0)
+#define BTMTK_INFO(fmt, ...)    \
+    do {if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_INFO) pr_warn("btmtk_info: "fmt"\n", ##__VA_ARGS__); } while (0)
+#define BTMTK_DBG(fmt, ...)     \
+    do {if (btmtk_log_lvl >= BTMTK_LOG_LEVEL_DEBUG) pr_warn("btmtk_debug: "fmt"\n", ##__VA_ARGS__); } while (0)
+
+
+
+#endif
+
diff --git a/btmtk_main.c b/btmtk_main.c
new file mode 100644
index 0000000..771c4d8
--- /dev/null
+++ b/btmtk_main.c
@@ -0,0 +1,404 @@
+/**
+ * Mediatek Bluetooth driver
+ *
+ * Copyright (C) 2009, Mediatek International Ltd.
+ *
+ * This software file (the "File") is distributed by Mediatek International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmtk_drv.h"
+#include "btmtk_sdio.h"
+
+
+
+
+/*
+* extern unsigned char probe_counter;
+* extern unsigned char *txbuf;
+*/
+
+extern u8 probe_ready;
+
+
+/*
+* int fw_dump_buffer_full = 0;
+* int fw_dump_total_read_size = 0;
+* int fw_dump_total_write_size = 0;
+* int fw_dump_buffer_used_size = 0;
+* int fw_dump_task_should_stop = 0;
+* u8 *fw_dump_ptr = NULL;
+* u8 *fw_dump_read_ptr = NULL;
+* u8 *fw_dump_write_ptr = NULL;
+* struct timeval fw_dump_last_write_time;
+* int fw_dump_end_checking_task_should_stop = 0;
+
+* static volatile int is_assert = 0;
+*/
+
+
+/*
+ * This function is called by interface specific interrupt handler.
+ * It updates Power Save & Host Sleep states, and wakes up the main
+ * thread.
+ */
+void btmtk_interrupt(struct btmtk_private *priv)
+{
+    priv->adapter->ps_state = PS_AWAKE;
+
+        priv->adapter->wakeup_tries = 0;
+
+        priv->adapter->int_count++;
+
+        wake_up_interruptible(&priv->main_thread.wait_q);
+}
+EXPORT_SYMBOL_GPL(btmtk_interrupt);
+
+int btmtk_enable_hs(struct btmtk_private *priv)
+{
+        struct btmtk_adapter *adapter = priv->adapter;
+        int ret = 0;
+
+        BTMTK_INFO("%s begin\n", __func__);
+
+        ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q,
+                                               adapter->hs_state,
+                        msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED));
+        if (ret < 0) {
+                BTMTK_ERR("event_hs_wait_q terminated (%d): %d,%d,%d",
+                       ret, adapter->hs_state, adapter->ps_state,
+                       adapter->wakeup_tries);
+
+        } else {
+                BTMTK_DBG("host sleep enabled: %d,%d,%d", adapter->hs_state,
+                       adapter->ps_state, adapter->wakeup_tries);
+                ret = 0;
+        }
+
+        return ret;
+}
+EXPORT_SYMBOL_GPL(btmtk_enable_hs);
+
+static int btmtk_tx_pkt(struct btmtk_private *priv, struct sk_buff *skb)
+{
+        int ret = 0;
+        u32 sdio_header_len = 0;
+
+        BTMTK_DBG("%s skb->len %d", __func__, skb->len);
+
+        if (!skb) {
+                BTMTK_WARN("%s skb is NULL return -EINVAL", __func__);
+                return -EINVAL;
+        }
+
+        if (!skb->data) {
+                BTMTK_WARN("%s skb->data is NULL return -EINVAL", __func__);
+                return -EINVAL;
+        }
+
+        if (!skb->len || ((skb->len + BTM_HEADER_LEN) > BTM_UPLD_SIZE)) {
+                BTMTK_WARN("Tx Error: Bad skb length %d : %d",
+                                                skb->len, BTM_UPLD_SIZE);
+                return -EINVAL;
+        }
+
+
+        sdio_header_len = skb->len + BTM_HEADER_LEN;
+        memset(txbuf, 0, MTK_TXDATA_SIZE);
+        txbuf[0] = (sdio_header_len & 0x0000ff);
+        txbuf[1] = (sdio_header_len & 0x00ff00) >> 8;
+        txbuf[2] = 0;
+        txbuf[3] = 0;
+        txbuf[4] = bt_cb(skb)->pkt_type;
+        memcpy(&txbuf[5], &skb->data[0], skb->len);
+        if (priv->hw_host_to_card)
+                ret = priv->hw_host_to_card(priv, txbuf, sdio_header_len);
+
+        BTMTK_DBG("%s end", __func__);
+        return ret;
+}
+
+static void btmtk_init_adapter(struct btmtk_private *priv)
+{
+        int buf_size;
+
+        skb_queue_head_init(&priv->adapter->tx_queue);
+        skb_queue_head_init(&priv->adapter->fops_queue);
+        priv->adapter->ps_state = PS_AWAKE;
+
+        buf_size = ALIGN_SZ(SDIO_BLOCK_SIZE, BTSDIO_DMA_ALIGN);
+        priv->adapter->hw_regs_buf = kzalloc(buf_size, GFP_KERNEL);
+        if (!priv->adapter->hw_regs_buf) {
+                priv->adapter->hw_regs = NULL;
+                BTMTK_ERR("Unable to allocate buffer for hw_regs.");
+        } else {
+                priv->adapter->hw_regs =
+                        (u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf,
+                                         BTSDIO_DMA_ALIGN);
+                BTMTK_DBG("hw_regs_buf=%p hw_regs=%p",
+                       priv->adapter->hw_regs_buf, priv->adapter->hw_regs);
+        }
+
+        init_waitqueue_head(&priv->adapter->cmd_wait_q);
+        init_waitqueue_head(&priv->adapter->event_hs_wait_q);
+}
+
+static void btmtk_free_adapter(struct btmtk_private *priv)
+{
+        skb_queue_purge(&priv->adapter->tx_queue);
+
+        kfree(priv->adapter->hw_regs_buf);
+        kfree(priv->adapter);
+
+        priv->adapter = NULL;
+}
+
+/*
+ * This function handles the event generated by firmware, rx data
+ * received from firmware, and tx data sent from kernel.
+ */
+
+static int btmtk_service_main_thread(void *data)
+{
+        struct btmtk_thread *thread = data;
+        struct btmtk_private *priv = thread->priv;
+        struct btmtk_adapter *adapter = NULL;
+        wait_queue_t wait;
+        struct sk_buff *skb;
+        int ret = 0;
+        int i = 0;
+        ulong flags;
+
+        BTMTK_WARN("main_thread begin 50");
+        //mdelay(50);
+        
+        for(i=0;i<=1000;i++){
+            if (kthread_should_stop()) {
+                        BTMTK_WARN("main_thread: break from main thread for probe_ready");
+                        break;
+            }
+
+            if (probe_ready) {
+                break;
+            } else {
+                BTMTK_WARN("%s probe_ready %d delay 10ms",__func__,probe_ready);
+                mdelay(10);
+            }
+
+            if(i==1000){
+                BTMTK_WARN("%s probe_ready %d i = %d try too many times return",__func__,probe_ready,i);
+                return 0;
+            }
+        }
+
+        if(priv->adapter)
+            adapter = priv->adapter;
+        else {
+            BTMTK_ERR("%s priv->adapter is NULL return",__func__);
+            return 0;
+        }
+
+        init_waitqueue_entry(&wait, current);
+        for (;;) {
+                add_wait_queue(&thread->wait_q, &wait);
+                set_current_state(TASK_INTERRUPTIBLE);
+                if (kthread_should_stop()) {
+                        BTMTK_WARN("main_thread: break from main thread");
+                        break;
+                }
+
+                if (adapter->wakeup_tries ||
+                                ((!adapter->int_count) &&
+                                (!priv->btmtk_dev.tx_dnld_rdy ||
+                                skb_queue_empty(&adapter->tx_queue)))) {
+                        BTMTK_DBG("main_thread is sleeping...");
+                        schedule();
+                }
+
+                set_current_state(TASK_RUNNING);
+
+                remove_wait_queue(&thread->wait_q, &wait);
+
+                if (kthread_should_stop()) {
+                        BTMTK_WARN("main_thread: break after wake up");
+                        break;
+                }
+
+                ret = priv->hw_set_own_back(DRIVER_OWN);
+                if (ret) {
+                    BTMTK_ERR("%s set driver own return fail", __func__);
+                    break;
+                }
+
+                spin_lock_irqsave(&priv->driver_lock, flags);
+                if (adapter->int_count) {
+                        BTMTK_DBG("%s go int\n", __func__);
+                        adapter->int_count = 0;
+                        spin_unlock_irqrestore(&priv->driver_lock, flags);
+                        priv->hw_process_int_status(priv);
+                } else if (adapter->ps_state == PS_SLEEP &&
+                                        !skb_queue_empty(&adapter->tx_queue)) {
+                        BTMTK_DBG("%s go vender, todo\n", __func__);
+                        spin_unlock_irqrestore(&priv->driver_lock, flags);
+                        adapter->wakeup_tries++;
+                        continue;
+                } else {
+                        BTMTK_DBG("%s go tx\n", __func__);
+                        spin_unlock_irqrestore(&priv->driver_lock, flags);
+                }
+
+                if (adapter->ps_state == PS_SLEEP) {
+                    BTMTK_DBG("%s ps_state == PS_SLEEP, continue\n", __func__);
+                    continue;
+                }
+
+                if (!priv->btmtk_dev.tx_dnld_rdy) {
+                    BTMTK_DBG("%s tx_dnld_rdy == 0, continue\n", __func__);
+                    continue;
+                }
+
+                spin_lock_irqsave(&priv->driver_lock, flags);
+                skb = skb_dequeue(&adapter->tx_queue);
+                spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+                if (skb) {
+                        if (skb->len < 16)
+                            btmtk_print_buffer_conent(skb->data, skb->len);
+                        else
+                            btmtk_print_buffer_conent(skb->data, 16);
+
+                        btmtk_tx_pkt(priv, skb);
+
+                        if (skb) {
+                            BTMTK_DBG("%s after btmtk_tx_pkt kfree_skb", __func__);
+                            kfree_skb(skb);
+                        }
+                }
+
+
+                if (skb_queue_empty(&adapter->tx_queue)) {
+                    ret = priv->hw_set_own_back(FW_OWN);
+                    if (ret) {
+                        BTMTK_ERR("%s set fw own return fail", __func__);
+                        break;
+                    }
+                }
+
+
+        }
+        BTMTK_INFO("%s  end\n", __func__);
+        return 0;
+}
+
+int btmtk_register_hdev(struct btmtk_private *priv)
+{
+        struct hci_dev *hdev = NULL;
+
+        BTMTK_INFO("%s\n", __func__);
+        hdev = hci_alloc_dev();
+        if (!hdev) {
+                BTMTK_ERR("Can not allocate HCI device");
+                goto err_hdev;
+        }
+
+        hci_set_drvdata(hdev, priv);
+        return 0;
+
+err_hdev:
+        /* Stop the thread servicing the interrupts */
+        kthread_stop(priv->main_thread.task);
+
+        btmtk_free_adapter(priv);
+        kfree(priv);
+
+        return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(btmtk_register_hdev);
+
+struct btmtk_private *btmtk_add_card(void *card)
+{
+        struct btmtk_private *priv;
+
+        BTMTK_INFO("%s begin", __func__);
+        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+        if (!priv) {
+                BTMTK_ERR("Can not allocate priv");
+                goto err_priv;
+        }
+
+        priv->adapter = kzalloc(sizeof(*priv->adapter), GFP_KERNEL);
+        if (!priv->adapter) {
+                BTMTK_ERR("Allocate buffer for btmtk_adapter failed!");
+                goto err_adapter;
+        }
+
+        btmtk_init_adapter(priv);
+
+        BTMTK_INFO("Starting kthread...");
+        priv->main_thread.priv = priv;
+        spin_lock_init(&priv->driver_lock);
+
+        init_waitqueue_head(&priv->main_thread.wait_q);
+        priv->main_thread.task = kthread_run(btmtk_service_main_thread,
+                                &priv->main_thread, "btmtk_main_service");
+        if (IS_ERR(priv->main_thread.task))
+                goto err_thread;
+
+        priv->btmtk_dev.card = card;
+        priv->btmtk_dev.tx_dnld_rdy = true;
+
+        return priv;
+
+err_thread:
+        btmtk_free_adapter(priv);
+
+err_adapter:
+        kfree(priv);
+
+err_priv:
+        return NULL;
+}
+EXPORT_SYMBOL_GPL(btmtk_add_card);
+
+int btmtk_remove_card(struct btmtk_private *priv)
+{
+
+
+        BTMTK_INFO("%s begin\n", __func__);
+
+
+        BTMTK_INFO("%s stop main_thread\n", __func__);
+        kthread_stop(priv->main_thread.task);
+        BTMTK_INFO("%s stop main_thread done\n", __func__);
+#ifdef CONFIG_DEBUG_FS
+        /*btmtk_debugfs_remove(hdev);*/
+#endif
+
+        btmtk_free_adapter(priv);
+
+        kfree(priv);
+
+        return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_remove_card);
+
+MODULE_AUTHOR("Mediatek Ltd.");
+MODULE_DESCRIPTION("Mediatek Bluetooth driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/btmtk_sdio.c b/btmtk_sdio.c
new file mode 100644
index 0000000..b5aa886
--- /dev/null
+++ b/btmtk_sdio.c
@@ -0,0 +1,2587 @@
+/**
+ * Mediatek BT-over-SDIO driver: SDIO interface related functions.
+ * Copyright (C) 2009, Mediatek International Ltd.
+ *
+ * This software file (the "File") is distributed by Mediatek International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+#include <linux/version.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/cdev.h>
+#include <linux/spinlock.h>
+#include <linux/kallsyms.h>
+#include <linux/device.h>
+
+
+#include "btmtk_drv.h"
+#include "btmtk_sdio.h"
+
+u8 btmtk_log_lvl = BTMTK_LOG_LEVEL_DEFAULT;
+
+static struct class *pBTClass;
+static struct device *pBTDev;
+static wait_queue_head_t inq;
+
+/* The btmtk_sdio_remove() callback function is called
+ * when user removes this module from kernel space or ejects
+ * the card from the slot. The driver handles these 2 cases
+ * differently.
+ * If the user is removing the module, a MODULE_SHUTDOWN_REQ
+ * command is sent to firmware and interrupt will be disabled.
+ * If the card is removed, there is no need to send command
+ * or disable interrupt.
+ *
+ * The variable 'user_rmmod' is used to distinguish these two
+ * scenarios. This flag is initialized as FALSE in case the card
+ * is removed, and will be set to TRUE for module removal when
+ * module_exit function is called.
+ */
+typedef void (*register_sdio_early_suspend) (void (*f) (void));
+typedef void (*register_sdio_late_resume) (void (*f) (void));
+register_sdio_early_suspend register_sdio_early_suspend_func;
+register_sdio_late_resume register_sdio_late_resume_func;
+
+/*============================================================================*/
+/* Callback Functions */
+/*============================================================================*/
+
+static u8 user_rmmod;
+
+struct completion g_done;
+unsigned char probe_counter;
+unsigned char current_fwdump_file_number;
+struct btmtk_private *g_priv;
+#define STR_COREDUMP_END "coredump end\n\n"
+
+
+static ring_buffer_struct metabuffer;
+u8 probe_ready;
+
+static char fw_dump_file_name[FW_DUMP_FILE_NAME_SIZE] = {0};
+int fw_dump_buffer_full;
+int fw_dump_total_read_size;
+int fw_dump_total_write_size;
+int fw_dump_buffer_used_size;
+int fw_dump_task_should_stop;
+u8 *fw_dump_ptr;
+u8 *fw_dump_read_ptr;
+u8 *fw_dump_write_ptr;
+struct timeval fw_dump_last_write_time;
+int fw_dump_end_checking_task_should_stop;
+int fw_is_doing_coredump;
+int fw_is_coredump_end_packet;
+
+#if SAVE_FW_DUMP_IN_KERNEL
+    static struct file *fw_dump_file;
+#else
+    static int    fw_dump_file;
+#endif
+
+static const struct btmtk_sdio_card_reg btmtk_reg_6630 = {
+        .cfg = 0x03,
+        .host_int_mask = 0x04,
+        .host_intstatus = 0x05,
+        .card_status = 0x20,
+        .sq_read_base_addr_a0 = 0x10,
+        .sq_read_base_addr_a1 = 0x11,
+        .card_fw_status0 = 0x40,
+        .card_fw_status1 = 0x41,
+        .card_rx_len = 0x42,
+        .card_rx_unit = 0x43,
+        .io_port_0 = 0x00,
+        .io_port_1 = 0x01,
+        .io_port_2 = 0x02,
+        .int_read_to_clear = false,
+    .func_num = 2,
+};
+
+static const struct btmtk_sdio_card_reg btmtk_reg_6632 = {
+        .cfg = 0x03,
+        .host_int_mask = 0x04,
+        .host_intstatus = 0x05,
+        .card_status = 0x20,
+        .sq_read_base_addr_a0 = 0x10,
+        .sq_read_base_addr_a1 = 0x11,
+        .card_fw_status0 = 0x40,
+        .card_fw_status1 = 0x41,
+        .card_rx_len = 0x42,
+        .card_rx_unit = 0x43,
+        .io_port_0 = 0x00,
+        .io_port_1 = 0x01,
+        .io_port_2 = 0x02,
+        .int_read_to_clear = false,
+    .func_num = 2,
+};
+
+static const struct btmtk_sdio_card_reg btmtk_reg_7668 = {
+        .cfg = 0x03,
+        .host_int_mask = 0x04,
+        .host_intstatus = 0x05,
+        .card_status = 0x20,
+        .sq_read_base_addr_a0 = 0x10,
+        .sq_read_base_addr_a1 = 0x11,
+        .card_fw_status0 = 0x40,
+        .card_fw_status1 = 0x41,
+        .card_rx_len = 0x42,
+        .card_rx_unit = 0x43,
+        .io_port_0 = 0x00,
+        .io_port_1 = 0x01,
+        .io_port_2 = 0x02,
+        .int_read_to_clear = false,
+    .func_num = 2,
+};
+
+static const struct btmtk_sdio_card_reg btmtk_reg_7666 = {
+        .cfg = 0x03,
+        .host_int_mask = 0x04,
+        .host_intstatus = 0x05,
+        .card_status = 0x20,
+        .sq_read_base_addr_a0 = 0x10,
+        .sq_read_base_addr_a1 = 0x11,
+        .card_fw_status0 = 0x40,
+        .card_fw_status1 = 0x41,
+        .card_rx_len = 0x42,
+        .card_rx_unit = 0x43,
+        .io_port_0 = 0x00,
+        .io_port_1 = 0x01,
+        .io_port_2 = 0x02,
+        .int_read_to_clear = false,
+    .func_num = 2,
+};
+
+static const struct btmtk_sdio_device btmtk_sdio_6630 = {
+        .helper         = "mtmk/sd8688_helper.bin",
+        .firmware       = "mt6632_patch_e1_hdr.bin",
+        .reg            = &btmtk_reg_6630,
+        .support_pscan_win_report = false,
+        .sd_blksz_fw_dl = 64,
+        .supports_fw_dump = false,
+};
+
+static const struct btmtk_sdio_device btmtk_sdio_6632 = {
+        .helper         = "mtmk/sd8688_helper.bin",
+        .firmware       = "mt6632_patch_e1_hdr.bin",
+        .reg            = &btmtk_reg_6632,
+        .support_pscan_win_report = false,
+        .sd_blksz_fw_dl = 64,
+        .supports_fw_dump = false,
+};
+
+static const struct btmtk_sdio_device btmtk_sdio_7668 = {
+        .helper         = "mtmk/sd8688_helper.bin",
+#if CFG_THREE_IN_ONE_FIRMWARE
+        .firmware       = "MT7668_FW",
+#else
+        .firmware        = "mt7668_patch_e1_hdr.bin",
+        .firmware1       = "mt7668_patch_e2_hdr.bin",
+#endif
+        .reg            = &btmtk_reg_7668,
+        .support_pscan_win_report = false,
+        .sd_blksz_fw_dl = 64,
+        .supports_fw_dump = false,
+};
+
+static const struct btmtk_sdio_device btmtk_sdio_7666 = {
+        .helper         = "mtmk/sd8688_helper.bin",
+        .firmware       = "mt7668_patch_e1_hdr.bin",
+        .reg            = &btmtk_reg_7666,
+        .support_pscan_win_report = false,
+        .sd_blksz_fw_dl = 64,
+        .supports_fw_dump = false,
+};
+
+
+unsigned char *txbuf;
+static unsigned char *rxbuf;
+static u32 rx_length;
+static struct btmtk_sdio_card *g_card;
+
+/*
+*add in /include/linux/mmc/sdio_ids.h
+*/
+#define SDIO_VENDOR_ID_MEDIATEK 0x037A
+
+static const struct sdio_device_id btmtk_sdio_ids[] = {
+        /* Mediatek SD8688 Bluetooth device */
+        { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x6630),
+                        .driver_data = (unsigned long) &btmtk_sdio_6630 },
+
+    { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x6632),
+                        .driver_data = (unsigned long) &btmtk_sdio_6632 },
+
+    { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7668),
+                        .driver_data = (unsigned long) &btmtk_sdio_7668 },
+
+    { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7666),
+                        .driver_data = (unsigned long) &btmtk_sdio_7666 },
+
+        { }     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(sdio, btmtk_sdio_ids);
+
+
+u32 lock_unsleepable_lock(P_OSAL_UNSLEEPABLE_LOCK pUSL)
+{
+    spin_lock_irqsave(&(pUSL->lock), pUSL->flag);
+    return 0;
+}
+
+u32 unlock_unsleepable_lock(P_OSAL_UNSLEEPABLE_LOCK pUSL)
+{
+    spin_unlock_irqrestore(&(pUSL->lock), pUSL->flag);
+    return 0;
+}
+
+static int btmtk_sdio_readb(u32 offset, u32 *val)
+{
+    u32 ret = 0;
+
+    if (g_card->func == NULL) {
+        BTMTK_ERR("%s g_card->func is NULL", __func__);
+        return -EIO;
+    }
+    sdio_claim_host(g_card->func);
+    *val = sdio_readb(g_card->func, offset, &ret);
+    sdio_release_host(g_card->func);
+    return ret;
+}
+
+static int btmtk_sdio_writel(u32 offset, u32 val)
+{
+    u32 ret = 0;
+
+    if (g_card->func == NULL) {
+        BTMTK_ERR("%s g_card->func is NULL", __func__);
+        return -EIO;
+    }
+    sdio_claim_host(g_card->func);
+    sdio_writel(g_card->func, val, offset, &ret);
+    sdio_release_host(g_card->func);
+    return ret;
+}
+
+static int btmtk_sdio_readl(u32 offset,  u32 *val)
+{
+    u32 ret = 0;
+
+    if (g_card->func == NULL) {
+        BTMTK_ERR("g_card->func is NULL");
+        return -EIO;
+    }
+    sdio_claim_host(g_card->func);
+    *val = sdio_readl(g_card->func, offset, &ret);
+    sdio_release_host(g_card->func);
+    return ret;
+}
+struct sk_buff *btmtk_create_send_data(struct sk_buff *skb)
+{
+    struct sk_buff *queue_skb = NULL;
+    u32 sdio_header_len = skb->len + BTM_HEADER_LEN;
+
+    if (skb_headroom(skb) < (BTM_HEADER_LEN)) {
+        queue_skb = bt_skb_alloc(sdio_header_len, GFP_ATOMIC);
+        if (queue_skb == NULL) {
+                BTMTK_ERR("bt_skb_alloc fail return");
+                return 0;
+        }
+
+        queue_skb->data[0] = (sdio_header_len & 0x0000ff);
+        queue_skb->data[1] = (sdio_header_len & 0x00ff00) >> 8;
+        queue_skb->data[2] = 0;
+        queue_skb->data[3] = 0;
+        queue_skb->data[4] = bt_cb(skb)->pkt_type;
+        queue_skb->len = sdio_header_len;
+        memcpy(&queue_skb->data[5], &skb->data[0], skb->len);
+        kfree_skb(skb);
+    } else {
+        queue_skb = skb;
+        skb_push(queue_skb, BTM_HEADER_LEN);
+        queue_skb->data[0] = (sdio_header_len & 0x0000ff);
+        queue_skb->data[1] = (sdio_header_len & 0x00ff00) >> 8;
+        queue_skb->data[2] = 0;
+        queue_skb->data[3] = 0;
+        queue_skb->data[4] = bt_cb(skb)->pkt_type;
+    }
+
+    BTMTK_INFO("%s end", __func__);
+    return queue_skb;
+}
+static int btmtk_sdio_set_own_back(int owntype)
+{
+    /*Set driver own*/
+    int ret = 0;
+    u32 u32LoopCount = 0;
+    u32 u32ReadCRValue = 0;
+    u32 ownValue = 0;
+    u32 set_checkretry = 30;
+
+    BTMTK_DBG("%s owntype %d", __func__, owntype);
+
+    if(user_rmmod)
+        set_checkretry = 1;
+
+    ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+
+    BTMTK_DBG("%s btmtk_sdio_readl  CHLPCR done", __func__);
+    if (owntype == DRIVER_OWN) {
+        if ((u32ReadCRValue&0x100) == 0x100) {
+            BTMTK_DBG("%s already driver own 0x%0x, return\n", __func__, u32ReadCRValue);
+            return ret;
+        }
+
+    } else if (owntype == FW_OWN) {
+        if ((u32ReadCRValue&0x100) == 0) {
+            BTMTK_DBG("%s already FW own 0x%0x, return\n", __func__, u32ReadCRValue);
+            return ret;
+        }
+
+    }
+
+
+setretry:
+
+    if (owntype == DRIVER_OWN)
+        ownValue = 0x00000200;
+    else
+        ownValue = 0x00000100;
+
+    BTMTK_DBG("%s write CHLPCR 0x%x", __func__, ownValue);
+    ret = btmtk_sdio_writel(CHLPCR, ownValue);
+    if (ret) {
+        ret = -EINVAL;
+        goto done;
+    }
+    BTMTK_DBG("%s write CHLPCR 0x%x done", __func__, ownValue);
+
+    u32LoopCount = 200;
+
+    if (owntype == DRIVER_OWN) {
+        do {
+            udelay(1);
+            ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+            u32LoopCount--;
+            BTMTK_DBG("%s DRIVER_OWN btmtk_sdio_readl CHLPCR 0x%x ", __func__, u32ReadCRValue);
+        } while ((u32LoopCount > 0) && ((u32ReadCRValue&0x100) != 0x100));
+
+        if ((u32LoopCount == 0) && (0x100 != (u32ReadCRValue&0x100)) && (set_checkretry > 0)) {
+            BTMTK_WARN("%s retry set_check driver own, CHLPCR 0x%x", __func__,u32ReadCRValue);
+            set_checkretry--;
+            mdelay(20);
+            goto setretry;
+        }
+    } else {
+        do {
+        udelay(1);
+        ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+        u32LoopCount--;
+        BTMTK_DBG("%s FW_OWN btmtk_sdio_readl CHLPCR 0x%x ", __func__, u32ReadCRValue);
+        } while ((u32LoopCount > 0) &&  ((u32ReadCRValue&0x100) != 0));
+
+        if ((u32LoopCount == 0) && ((u32ReadCRValue&0x100) != 0) && (set_checkretry > 0)) {
+            BTMTK_WARN("%s retry set_check FW own, CHLPCR 0x%x", __func__,u32ReadCRValue);
+            set_checkretry--;
+            goto setretry;
+        }
+    }
+
+    BTMTK_DBG("%s CHLPCR(0x%x), is 0x%x\n", __func__, CHLPCR, u32ReadCRValue);
+
+    if (owntype == DRIVER_OWN) {
+        if ((u32ReadCRValue&0x100) == 0x100)
+            BTMTK_DBG("%s check %04x, is 0x100 driver own success\n", __func__, CHLPCR);
+        else {
+                BTMTK_DBG("%s check %04x, is %x shuld be 0x100\n", __func__, CHLPCR, u32ReadCRValue);
+                ret = EINVAL;
+                goto done;
+        }
+    } else {
+        if (0x0 == (u32ReadCRValue&0x100))
+            BTMTK_DBG("%s check %04x, bit 8 is 0 FW own success\n", __func__, CHLPCR);
+        else{
+            BTMTK_DBG("%s bit 8 should be 0, %04x bit 8 is %04x\n", __func__, u32ReadCRValue, (u32ReadCRValue&0x100));
+            ret = EINVAL;
+            goto done;
+        }
+    }
+
+
+
+done:
+
+    if (owntype == DRIVER_OWN) {
+        if (ret)
+            BTMTK_ERR("%s set driver own fail\n", __func__);
+        else
+            BTMTK_DBG("%s set driver own success\n", __func__);
+    } else if (owntype == FW_OWN) {
+        if (ret)
+            BTMTK_ERR("%s set FW own fail\n", __func__);
+        else
+            BTMTK_DBG("%s set FW own success\n", __func__);
+
+    } else
+        BTMTK_ERR("%s unknow type %d\n", __func__, owntype);
+
+
+    return ret;
+}
+static int btmtk_sdio_enable_interrupt(int enable)
+{
+    u32 ret = 0;
+    u32 cr_value = 0;
+
+    if (enable)
+        cr_value |= C_FW_INT_EN_SET;
+    else
+        cr_value |= C_FW_INT_EN_CLEAR;
+
+    ret = btmtk_sdio_writel(CHLPCR, cr_value);
+    BTMTK_DBG("%s enable %d write CHLPCR 0x%08x\n", __func__, enable, cr_value);
+
+    return ret;
+}
+
+static int btmtk_sdio_get_rx_unit(struct btmtk_sdio_card *card)
+{
+        u8 reg;
+        int ret;
+
+        reg = sdio_readb(card->func, card->reg->card_rx_unit, &ret);
+        if (!ret)
+                card->rx_unit = reg;
+
+        return ret;
+}
+
+static int btmtk_sdio_enable_host_int_mask(struct btmtk_sdio_card *card,
+                                                                u8 mask)
+{
+        int ret;
+
+        sdio_writeb(card->func, mask, card->reg->host_int_mask, &ret);
+        if (ret) {
+                BTMTK_ERR("Unable to enable the host interrupt!");
+                ret = -EIO;
+        }
+
+        return ret;
+}
+
+static int btmtk_sdio_disable_host_int_mask(struct btmtk_sdio_card *card,
+                                                                u8 mask)
+{
+        u8 host_int_mask;
+        int ret;
+
+        host_int_mask = sdio_readb(card->func, card->reg->host_int_mask, &ret);
+        if (ret)
+                return -EIO;
+
+        host_int_mask &= ~mask;
+
+        sdio_writeb(card->func, host_int_mask, card->reg->host_int_mask, &ret);
+        if (ret < 0) {
+                BTMTK_ERR("Unable to disable the host interrupt!");
+                return -EIO;
+        }
+
+        return 0;
+}
+
+/*for debug*/
+int btmtk_print_buffer_conent(u8 *buf, u32 Datalen)
+{
+    int i = 0;
+    int print_finish = 0;
+    /*Print out txbuf data for debug*/
+    for (i = 0; i <= (Datalen-1); i += 16) {
+        if ((i+16) <= Datalen) {
+            BTMTK_DBG("%s: %02X%02X%02X%02X%02X %02X%02X%02X%02X%02X %02X%02X%02X%02X%02X %02X\n", __func__,
+            buf[i], buf[i+1], buf[i+2], buf[i+3], buf[i+4],
+            buf[i+5], buf[i+6], buf[i+7], buf[i+8], buf[i+9],
+            buf[i+10], buf[i+11], buf[i+12], buf[i+13], buf[i+14],
+            buf[i+15]);
+        } else {
+            for (; i < (Datalen); i++)
+                BTMTK_DBG("%s: %02X\n", __func__, buf[i]);
+
+            print_finish = 1;
+        }
+
+        if (print_finish)
+            break;
+
+    }
+    return 0;
+}
+
+static int btmtk_sdio_send_tx_data(u8 *buffer, int tx_data_len)
+{
+    int ret = 0;
+    u8 MultiBluckCount = 0;
+    u8 redundant = 0;
+
+    MultiBluckCount = tx_data_len/SDIO_BLOCK_SIZE;
+    redundant = tx_data_len % SDIO_BLOCK_SIZE;
+
+    if (redundant)
+        tx_data_len = (MultiBluckCount+1)*SDIO_BLOCK_SIZE;
+
+    sdio_claim_host(g_card->func);
+    ret = sdio_writesb(g_card->func, CTDR, buffer, tx_data_len);
+    sdio_release_host(g_card->func);
+
+    return ret;
+}
+
+static int btmtk_sdio_recv_rx_data(void)
+{
+    int ret = 0;
+    u32 u32ReadCRValue = 0;
+    int retry_count = 5;
+
+    memset(rxbuf, 0, MTK_RXDATA_SIZE);
+
+    do {
+        ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
+        BTMTK_DBG("%s: loop Get CHISR 0x%08X\n", __func__, u32ReadCRValue);
+        rx_length = (u32ReadCRValue & RX_PKT_LEN) >> 16;
+        if (rx_length == 0xFFFF) {
+            BTMTK_WARN("%s: 0xFFFF==rx_length, error return -EIO\n", __func__);
+            ret = -EIO;
+            break;
+        }
+
+        if ((RX_DONE&u32ReadCRValue) && rx_length) {
+            BTMTK_DBG("%s: u32ReadCRValue = %08X\n", __func__, u32ReadCRValue);
+            u32ReadCRValue &= 0xFFFB;
+            ret = btmtk_sdio_writel(CHISR, u32ReadCRValue);
+            BTMTK_DBG("%s: write = %08X\n", __func__, u32ReadCRValue);
+            
+
+            sdio_claim_host(g_card->func);
+            ret = sdio_readsb(g_card->func, rxbuf, CRDR, rx_length);
+            sdio_release_host(g_card->func);
+            if (rxbuf[0] == 0) {
+                BTMTK_WARN("%s: get rx length = %d, but no rx content\n", __func__, rx_length);
+                continue;
+            }
+
+
+            break;
+        }
+
+
+        retry_count--;
+        if (retry_count <= 0) {
+            BTMTK_WARN("%s: retry_count = %d,timeout\n", __func__, retry_count);
+            ret = -EIO;
+            break;
+        }
+
+/*
+* msleep(1);
+*/
+        mdelay(3);
+        BTMTK_DBG("%s: retry_count = %d,wait\n", __func__, retry_count);
+
+        if (ret)
+            break;
+
+    } while (1);
+
+
+
+    if (ret)
+        return -EIO;
+
+
+
+
+
+    return ret;
+}
+
+static int btmtk_sdio_send_wmt_reset(void)
+{
+    int ret = 0;
+    u8 wmt_event[8] = {4, 0xE4, 5, 2, 7, 1, 0, 0};
+    u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+    u8 mtksdio_wmt_reset[9] = {1, 0x6F, 0xFC, 5, 1, 7, 1, 0, 4};
+
+    BTMTK_INFO("%s:\n", __func__);
+    mtksdio_packet_header[0] = sizeof(mtksdio_packet_header) + sizeof(mtksdio_wmt_reset);
+
+    memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
+    memcpy(txbuf+MTK_SDIO_PACKET_HEADER_SIZE, mtksdio_wmt_reset, sizeof(mtksdio_wmt_reset));
+
+    btmtk_sdio_send_tx_data(txbuf, MTK_SDIO_PACKET_HEADER_SIZE+sizeof(mtksdio_wmt_reset));
+    btmtk_sdio_recv_rx_data();
+
+    /*compare rx data is wmt reset correct response or not*/
+    if (memcmp(wmt_event, rxbuf+MTK_SDIO_PACKET_HEADER_SIZE, sizeof(wmt_event)) != 0) {
+        ret = -EIO;
+        BTMTK_WARN("%s: fail\n", __func__);
+    }
+
+    return ret;
+}
+
+static u32 btmtk_sdio_bt_memRegister_read(u32 cr)
+{
+	int retrytime = 300;
+	u32 result = 0;
+	u8 wmt_event[15] = {0x04,0xE4,0x10,0x02,0x08,0x0C/*0x1C*/,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x80 };
+	//msleep(1000);
+	u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+	u8 mtksdio_wmt_cmd[16] = {0x1,0x6F,0xFC,0x0C,0x01,0x08,0x08,0x00,0x02,0x01,0x00,0x01,0x00,0x00,0x00,0x00};
+	mtksdio_packet_header[0] = sizeof(mtksdio_packet_header) + sizeof(mtksdio_wmt_cmd);
+        BTMTK_INFO("%s: read cr %x\n",__func__,cr);
+
+        memcpy(&mtksdio_wmt_cmd[12],&cr,sizeof(cr));
+
+	memcpy(txbuf,mtksdio_packet_header,MTK_SDIO_PACKET_HEADER_SIZE);
+	memcpy(txbuf+MTK_SDIO_PACKET_HEADER_SIZE,mtksdio_wmt_cmd,sizeof(mtksdio_wmt_cmd));
+
+	btmtk_sdio_send_tx_data(txbuf,MTK_SDIO_PACKET_HEADER_SIZE+sizeof(mtksdio_wmt_cmd));
+	btmtk_print_buffer_conent(txbuf,MTK_SDIO_PACKET_HEADER_SIZE+sizeof(mtksdio_wmt_cmd));
+
+        do{
+            msleep(10);
+	    btmtk_sdio_recv_rx_data();
+            retrytime--;
+		if(retrytime<=0)
+			break;
+
+		BTMTK_INFO("%s: retrytime %d\n",__func__,retrytime);
+	}while(!rxbuf[0]);
+
+	btmtk_print_buffer_conent(rxbuf,rx_length);
+	/*compare rx data is wmt reset correct response or not*/
+	/*if(0!=memcmp(wmt_event,rxbuf+MTK_SDIO_PACKET_HEADER_SIZE,sizeof(wmt_event))){
+		ret = -EIO;
+		BTMTK_INFO("%s: fail\n",__func__);
+	}*/
+
+	memcpy(&result,rxbuf+MTK_SDIO_PACKET_HEADER_SIZE+sizeof(wmt_event),sizeof(result));
+        BTMTK_INFO("%s: ger cr 0x%x value 0x%x\n",__func__,cr,result);
+	return result;
+}
+
+/*
+*1:on ,  0:off
+*/
+static int btmtk_sdio_bt_set_power(u8 onoff)
+{
+    int ret = 0;
+    int retrytime = 60;
+    u8 wmt_event[8] = {4, 0xE4, 5, 2, 6, 1, 0, 0};
+    u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+    u8 mtksdio_wmt_cmd[10] = {1, 0x6F, 0xFC, 6, 1, 6, 2, 0, 0, 1};
+    if (onoff==0)
+        retrytime = 3;
+
+    mtksdio_packet_header[0] = sizeof(mtksdio_packet_header) + sizeof(mtksdio_wmt_cmd);
+    BTMTK_INFO("%s: onoff %d\n", __func__, onoff);
+
+    mtksdio_wmt_cmd[9] = onoff;
+
+    memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
+    memcpy(txbuf+MTK_SDIO_PACKET_HEADER_SIZE, mtksdio_wmt_cmd, sizeof(mtksdio_wmt_cmd));
+
+    btmtk_sdio_send_tx_data(txbuf, MTK_SDIO_PACKET_HEADER_SIZE+sizeof(mtksdio_wmt_cmd));
+
+
+    do {
+        msleep(100);
+        btmtk_sdio_recv_rx_data();
+        retrytime--;
+        if (retrytime <= 0)
+            break;
+
+        if (retrytime < 40)
+            BTMTK_WARN("%s: retry over 2s, retrytime %d\n", __func__, retrytime);
+
+        BTMTK_INFO("%s: retrytime %d\n", __func__, retrytime);
+    } while (!rxbuf[0]);
+
+
+    /*compare rx data is wmt reset correct response or not*/
+    if (memcmp(wmt_event, rxbuf+MTK_SDIO_PACKET_HEADER_SIZE, sizeof(wmt_event)) != 0) {
+        ret = -EIO;
+        BTMTK_INFO("%s: fail\n", __func__);
+    }
+
+    return ret;
+}
+/*
+* 1:on ,  0:off
+*/
+static int btmtk_sdio_set_sleep(void)
+{
+    int ret = 0;
+    u8 wmt_event[8] = {4, 0xE, 4, 1, 0x7A, 0xFC, 0};
+    u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+    u8 mtksdio_wmt_cmd[11] = {1, 0x7A, 0xFC, 7, /*3:sdio, 5:usb*/03,
+    /*host non sleep duration*/0x80, 0x02, /*host non sleep duration*/0x80, 0x02, 0x0, 0x00};
+
+    mtksdio_packet_header[0] = sizeof(mtksdio_packet_header) + sizeof(mtksdio_wmt_cmd);
+    BTMTK_INFO("%s begin\n", __func__);
+
+    memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
+    memcpy(txbuf+MTK_SDIO_PACKET_HEADER_SIZE, mtksdio_wmt_cmd, sizeof(mtksdio_wmt_cmd));
+
+    btmtk_sdio_send_tx_data(txbuf, MTK_SDIO_PACKET_HEADER_SIZE+sizeof(mtksdio_wmt_cmd));
+    btmtk_sdio_recv_rx_data();
+    btmtk_print_buffer_conent(rxbuf, rx_length);
+    /*compare rx data is wmt reset correct response or not*/
+    if (memcmp(wmt_event, rxbuf+MTK_SDIO_PACKET_HEADER_SIZE, sizeof(wmt_event)) != 0) {
+        ret = -EIO;
+        BTMTK_INFO("%s: fail\n", __func__);
+    }
+
+    return ret;
+}
+
+static int btmtk_send_rom_patch(u8 *fwbuf, u32 fwlen, int mode)
+{
+    int ret = 0;
+    u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+    int stp_len = 0;
+    u8 mtkdata_header[MTKDATA_HEADER_SIZE] = {0};
+
+    int copy_len = 0;
+    int Datalen = fwlen;
+    u32 u32ReadCRValue = 0;
+
+
+    BTMTK_DBG("%s fwlen %d, mode = %d", __func__, fwlen, mode);
+    if (fwlen < Datalen) {
+        BTMTK_ERR("%s file size = %d,is not corect", __func__, fwlen);
+        return -ENOENT;
+    }
+
+    stp_len = Datalen + MTKDATA_HEADER_SIZE;
+
+
+    mtkdata_header[0] = 0x2;/*ACL data*/
+    mtkdata_header[1] = 0x6F;
+    mtkdata_header[2] = 0xFC;
+
+    mtkdata_header[3] = ((Datalen+4+1)&0x00FF);
+    mtkdata_header[4] = ((Datalen+4+1)&0xFF00)>>8;
+
+    mtkdata_header[5] = 0x1;
+    mtkdata_header[6] = 0x1;
+
+    mtkdata_header[7] = ((Datalen+1)&0x00FF);
+    mtkdata_header[8] = ((Datalen+1)&0xFF00)>>8;
+
+    mtkdata_header[9] = mode;
+
+/*
+* 0 and 1 is packet length, include MTKSTP_HEADER_SIZE
+*/
+    mtksdio_packet_header[0] = (Datalen+4+MTKSTP_HEADER_SIZE+6)&0xFF;
+    mtksdio_packet_header[1] = ((Datalen+4+MTKSTP_HEADER_SIZE+6)&0xFF00)>>8;
+    mtksdio_packet_header[2] = 0;
+    mtksdio_packet_header[3] = 0;
+
+/*
+*mtksdio_packet_header[2] and mtksdio_packet_header[3] are reserved
+*/
+    BTMTK_DBG("%s result %02x  %02x\n", __func__
+    , ((Datalen+4+MTKSTP_HEADER_SIZE+6)&0xFF00)>>8
+    , (Datalen+4+MTKSTP_HEADER_SIZE+6));
+
+    memcpy(txbuf+copy_len, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
+    copy_len += MTK_SDIO_PACKET_HEADER_SIZE;
+
+    memcpy(txbuf+copy_len, mtkdata_header, MTKDATA_HEADER_SIZE);
+    copy_len += MTKDATA_HEADER_SIZE;
+
+
+
+    memcpy(txbuf+copy_len, fwbuf, Datalen);
+    copy_len += Datalen;
+
+    BTMTK_DBG("%s txbuf %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", __func__,
+    txbuf[0], txbuf[1], txbuf[2], txbuf[3], txbuf[4],
+    txbuf[5], txbuf[6], txbuf[7], txbuf[8], txbuf[9]);
+
+
+    ret = btmtk_sdio_readl(CHIER, &u32ReadCRValue);
+    BTMTK_DBG("%s: CHIER u32ReadCRValue %x, ret %d\n", __func__, u32ReadCRValue, ret);
+
+    ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+    BTMTK_DBG("%s: CHLPCR u32ReadCRValue %x, ret %d\n", __func__, u32ReadCRValue, ret);
+
+    ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
+    BTMTK_DBG("%s: 0CHISR u32ReadCRValue %x, ret %d\n", __func__, u32ReadCRValue, ret);
+    ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
+    BTMTK_DBG("%s: 00CHISR u32ReadCRValue %x, ret %d\n", __func__, u32ReadCRValue, ret);
+
+    btmtk_sdio_send_tx_data(txbuf, copy_len);
+
+
+    ret = btmtk_sdio_recv_rx_data();
+
+    return ret;
+}
+
+
+
+/*
+* type: cmd:1, ACL:2
+* -------------------------------------------------
+* mtksdio hedaer 4 byte| wmt header  |
+*
+*
+* data len should less than 512-4-4
+*/
+static int btmtk_sdio_send_wohci(u8 type, u32 len, u8 *data)
+{
+    u32 ret = 0;
+    u32 push_in_data_len = 0;
+    u8 mtk_wmt_header[MTKWMT_HEADER_SIZE] = {0};
+    u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
+    u8 mtk_tx_data[512] = {0};
+
+    mtk_wmt_header[0] = type;
+    mtk_wmt_header[1] = 0x6F;
+    mtk_wmt_header[2] = 0xFC;
+    mtk_wmt_header[3] = len;
+
+    mtksdio_packet_header[0] = (len+MTKWMT_HEADER_SIZE+MTK_SDIO_PACKET_HEADER_SIZE)&0xFF;
+    mtksdio_packet_header[1] = ((len+MTKWMT_HEADER_SIZE+MTK_SDIO_PACKET_HEADER_SIZE)&0xFF00)>>8;
+    mtksdio_packet_header[2] = 0;
+    mtksdio_packet_header[3] = 0;
+/*
+* mtksdio_packet_header[2] and mtksdio_packet_header[3] are reserved
+*/
+
+    memcpy(mtk_tx_data, mtksdio_packet_header, sizeof(mtksdio_packet_header));
+    push_in_data_len += sizeof(mtksdio_packet_header);
+
+    memcpy(mtk_tx_data+push_in_data_len, mtk_wmt_header, sizeof(mtk_wmt_header));
+    push_in_data_len += sizeof(mtk_wmt_header);
+
+    memcpy(mtk_tx_data+push_in_data_len, data, len);
+    push_in_data_len += len;
+
+    sdio_claim_host(g_card->func);
+    ret = sdio_writesb(g_card->func, CTDR, mtk_tx_data, push_in_data_len);
+    sdio_release_host(g_card->func);
+
+
+    BTMTK_INFO("%s retrun  0x%0x\n", __func__, ret);
+    return ret;
+}
+/*
+* data event:
+* return
+* 0:
+* patch download is not complete/get patch semaphore fail
+* 1:
+* patch download is complete by others
+* 2
+* patch download is not coplete
+* 3:(for debug)
+* release patch semaphore success
+*/
+static int btmtk_sdio_need_load_rom_patch(void)
+{
+    u32 ret = 0;
+    u8 cmd[] = {0x1, 0x17, 0x1, 0x0, 0x1};
+    u8 event[] = {0x2, 0x17, 0x1, 0x0};
+
+    do {
+        ret = btmtk_sdio_send_wohci(HCI_COMMAND_PKT, sizeof(cmd), cmd);
+
+        if (ret) {
+            BTMTK_ERR("%s btmtk_sdio_send_wohci return fail ret %d", __func__, ret);
+            break;
+        }
+
+        ret = btmtk_sdio_recv_rx_data();
+        if (ret)
+            break;
+
+        if (rx_length == 12) {
+            if (memcmp(rxbuf+7, event, sizeof(event)) == 0)
+                return rxbuf[11];
+            else{
+                BTMTK_ERR("%s receive event content is not correct, print receive data\n", __func__);
+                btmtk_print_buffer_conent(rxbuf, rx_length);
+            }
+        }
+
+    } while (0);
+
+    return ret;
+}
+static int btmtk_sdio_set_write_clear(void)
+{
+    u32 u32ReadCRValue = 0;
+    u32 ret = 0;
+
+    ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue);
+    if (ret) {
+       BTMTK_ERR("%s read CHCR error\n", __func__);
+       ret = EINVAL;
+       return ret;
+    }
+
+    u32ReadCRValue |= 0x00000002;
+    btmtk_sdio_writel(CHCR, u32ReadCRValue);
+    BTMTK_INFO("%s write CHCR 0x%08X\n", __func__, u32ReadCRValue);
+    ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue);
+    BTMTK_INFO("%s read CHCR 0x%08X\n", __func__, u32ReadCRValue);
+    if (u32ReadCRValue&0x00000002)
+       BTMTK_INFO("%s write clear\n", __func__);
+    else
+       BTMTK_INFO("%s read clear\n", __func__);
+
+
+    return ret;
+}
+static int btmtk_sdio_download_rom_patch(struct btmtk_sdio_card *card)
+{
+        const struct firmware *fw_firmware = NULL;
+        const u8 *firmware = NULL;
+        int firmwarelen, ret = 0;
+        void *tmpfwbuf = NULL;
+        u8 *fwbuf;
+        P_PATCH_HEADER patchHdr;
+        u8 *cDateTime = NULL;
+        u16 u2HwVer = 0;
+        u16 u2SwVer = 0;
+        u32 u4PatchVer = 0;
+        u32 uhwversion = 0;
+
+        u32 u32ReadCRValue = 0;
+
+        int RedundantSize = 0;
+        u32 bufferOffset = 0;
+        u8  patch_status = 0;
+
+        ret = btmtk_sdio_set_own_back(DRIVER_OWN);
+
+        if (ret)
+            return ret;
+
+        patch_status = btmtk_sdio_need_load_rom_patch();
+
+        BTMTK_DBG("%s patch_status %d\n", __func__, patch_status);
+
+        if (PATCH_IS_DOWNLOAD_BT_OTHER == patch_status || PATCH_READY == patch_status) {
+            BTMTK_INFO("%s patch is ready no need load patch again\n", __func__);
+
+            ret = btmtk_sdio_readl(0, &u32ReadCRValue);
+            BTMTK_INFO("%s read chipid =  %x\n", __func__, u32ReadCRValue);
+
+            /*Set interrupt output*/
+            ret = btmtk_sdio_writel(CHIER, FIRMWARE_INT|TX_FIFO_OVERFLOW |
+                FW_INT_IND_INDICATOR | TX_COMPLETE_COUNT |
+                TX_UNDER_THOLD | TX_EMPTY | RX_DONE);
+
+            if (ret) {
+                BTMTK_ERR("Set interrupt output fail(%d)\n", ret);
+                ret = -EIO;
+            }
+
+
+            /*enable interrupt output*/
+            ret = btmtk_sdio_writel(CHLPCR, C_FW_INT_EN_SET);
+
+
+
+            if (ret) {
+                BTMTK_ERR("enable interrupt output fail(%d)\n", ret);
+                ret = -EIO;
+                        goto done;
+            }
+
+            ret = btmtk_sdio_bt_set_power(1);
+            if (ret)
+                return ret;
+
+            ret = btmtk_sdio_set_sleep();
+        btmtk_sdio_set_write_clear();
+            return ret;
+        }
+
+        uhwversion = btmtk_sdio_bt_memRegister_read(HW_VERSION);
+        BTMTK_INFO("%s uhwversion 0x%x\n",__func__,uhwversion);
+
+        if (uhwversion == 0x8A00){
+            BTMTK_INFO("%s request_firmware(firmware name %s)\n",__func__,card->firmware);
+	    ret = request_firmware(&fw_firmware, card->firmware,
+                                                        &card->func->dev);
+
+            if ((ret < 0) || !fw_firmware) {
+                BTMTK_ERR("request_firmware(firmware name %s) failed, error code = %d",
+                                                                        card->firmware,
+									ret);
+                ret = -ENOENT;
+                goto done;
+            }
+        }
+        else {
+            BTMTK_INFO("%s request_firmware(firmware name %s)\n",__func__,card->firmware1);
+	    ret = request_firmware(&fw_firmware, card->firmware1,
+                                                        &card->func->dev);
+
+            if ((ret < 0) || !fw_firmware) {
+                BTMTK_ERR("request_firmware(firmware name %s) failed, error code = %d",
+                                                                        card->firmware1,
+									ret);
+                ret = -ENOENT;
+                goto done;
+            }
+     	}
+
+
+        firmware = fw_firmware->data;
+        firmwarelen = fw_firmware->size;
+
+        BTMTK_DBG("Downloading FW image (%d bytes)", firmwarelen);
+
+        tmpfwbuf = kzalloc(firmwarelen, GFP_KERNEL);
+
+        if (!tmpfwbuf) {
+                    BTMTK_ERR("%s Unable to allocate buffer for firmware. Terminating download"
+                    , __func__);
+                    ret = -ENOMEM;
+                    goto done;
+        }
+
+        /* Ensure aligned firmware buffer */
+        memcpy(tmpfwbuf, firmware, firmwarelen);
+        fwbuf = tmpfwbuf;
+
+        /*Display rom patch info*/
+        patchHdr =  (P_PATCH_HEADER)fwbuf;
+        cDateTime = patchHdr->ucDateTime;
+        u2HwVer = patchHdr->u2HwVer;
+        u2SwVer = patchHdr->u2SwVer;
+        u4PatchVer = patchHdr->u4PatchVer;
+
+        BTMTK_DBG("=====================================\n");
+        BTMTK_INFO("===============Patch Info============\n");
+        BTMTK_INFO("Built Time = %s\n", cDateTime);
+        BTMTK_INFO("Hw Ver = 0x%x\n",
+                  ((u2HwVer & 0x00ff) << 8) | ((u2HwVer & 0xff00) >> 8));
+        BTMTK_INFO("Sw Ver = 0x%x\n",
+                  ((u2SwVer & 0x00ff) << 8) | ((u2SwVer & 0xff00) >> 8));
+        BTMTK_INFO("Patch Ver = 0x%04x\n",
+                  ((u4PatchVer & 0xff000000) >> 24) | ((u4PatchVer & 0x00ff0000) >>
+                                   16));
+        BTMTK_INFO("Platform = %c%c%c%c\n", patchHdr->ucPlatform[0],
+                  patchHdr->ucPlatform[1], patchHdr->ucPlatform[2], patchHdr->ucPlatform[3]);
+        BTMTK_INFO("Patch start addr = %02x\n", patchHdr->u2PatchStartAddr);
+        BTMTK_INFO("=====================================\n");
+
+
+        fwbuf += sizeof(PATCH_HEADER);
+        BTMTK_DBG("%s PATCH_HEADER size %zd\n", __func__, sizeof(PATCH_HEADER));
+        firmwarelen -= sizeof(PATCH_HEADER);
+
+
+
+        ret = btmtk_sdio_readl(0, &u32ReadCRValue);
+        BTMTK_INFO("%s read chipid =  %x\n", __func__, u32ReadCRValue);
+
+        /*Set interrupt output*/
+        ret = btmtk_sdio_writel(CHIER, FIRMWARE_INT|TX_FIFO_OVERFLOW |
+            FW_INT_IND_INDICATOR | TX_COMPLETE_COUNT |
+            TX_UNDER_THOLD | TX_EMPTY | RX_DONE);
+
+        if (ret) {
+            BTMTK_ERR("Set interrupt output fail(%d)\n", ret);
+            ret = -EIO;
+                    goto done;
+        }
+
+        /*enable interrupt output*/
+        ret = btmtk_sdio_writel(CHLPCR, C_FW_INT_EN_SET);
+
+
+
+        if (ret) {
+            BTMTK_ERR("enable interrupt output fail(%d)\n", ret);
+            ret = -EIO;
+                    goto done;
+        }
+
+        RedundantSize = firmwarelen;
+        BTMTK_DBG("%s firmwarelen %d\n", __func__, firmwarelen);
+
+
+        do {
+            bufferOffset = firmwarelen - RedundantSize;
+
+            if (RedundantSize == firmwarelen && PATCH_DOWNLOAD_SIZE <= RedundantSize)
+                ret = btmtk_send_rom_patch(fwbuf+bufferOffset, PATCH_DOWNLOAD_SIZE, SDIO_PATCH_DOWNLOAD_FIRST);
+            else if (RedundantSize == firmwarelen)
+                ret = btmtk_send_rom_patch(fwbuf+bufferOffset, RedundantSize, SDIO_PATCH_DOWNLOAD_FIRST);
+            else if (RedundantSize < PATCH_DOWNLOAD_SIZE) {
+                ret = btmtk_send_rom_patch(fwbuf+bufferOffset, RedundantSize, SDIO_PATCH_DOWNLOAD_END);
+                BTMTK_DBG("%s patch downoad last patch part\n", __func__);
+            } else
+                ret = btmtk_send_rom_patch(fwbuf+bufferOffset, PATCH_DOWNLOAD_SIZE, SDIO_PATCH_DOWNLOAD_CON);
+
+            RedundantSize -= PATCH_DOWNLOAD_SIZE;
+
+            if (ret) {
+                BTMTK_ERR("%s btmtk_send_rom_patch fail\n", __func__);
+                goto done;
+            }
+            BTMTK_DBG("%s RedundantSize %d", __func__, RedundantSize);
+            if (RedundantSize <= 0) {
+                BTMTK_DBG("%s patch downoad finish\n", __func__);
+                break;
+            }
+
+        } while (1);
+
+        btmtk_sdio_set_write_clear();
+
+
+        if (btmtk_sdio_need_load_rom_patch() == PATCH_READY)
+            BTMTK_INFO("%s patch is ready\n", __func__);
+
+
+        ret = btmtk_sdio_send_wmt_reset();
+
+        if (ret)
+            goto done;
+
+
+        ret = btmtk_sdio_bt_set_power(1);
+
+        if (ret){
+            ret = EINVAL;
+            goto done;
+        }
+
+        ret = btmtk_sdio_set_sleep();
+
+
+done:
+
+        kfree(tmpfwbuf);
+            release_firmware(fw_firmware);
+
+
+
+
+        if (!ret)
+            BTMTK_INFO("%s success\n", __func__);
+        else
+            BTMTK_INFO("%s fail\n", __func__);
+
+
+        return ret;
+}
+
+
+static int btmtk_sdio_card_to_host(struct btmtk_private *priv)
+{
+        u16 buf_len = 0;
+        int ret = 0, num_blocks = 0, blksz = 0;
+        struct sk_buff *skb = NULL;
+        struct sk_buff *fops_skb = NULL;
+        u32 type;
+        u8 *payload = NULL;
+        u32 fourbalignment_len = 0;
+        struct btmtk_sdio_card *card = priv->btmtk_dev.card;
+        u32 dump_len = 0;
+        char *core_dump_end = NULL;
+
+        if (!card || !card->func) {
+                BTMTK_ERR("card or function or is NULL!");
+                ret = -EINVAL;
+                goto exit;
+        }
+
+#if SUPPORT_FW_DUMP
+    fw_is_coredump_end_packet = false;
+    if (rx_length > (SDIO_HEADER_LEN+8)) {
+        if (rxbuf[SDIO_HEADER_LEN] == 0x80) {
+            dump_len = (rxbuf[SDIO_HEADER_LEN+1]&0x0F)*256 + rxbuf[SDIO_HEADER_LEN+2];
+            BTMTK_WARN("%s get dump length %d", __func__, dump_len);
+            if (rxbuf[SDIO_HEADER_LEN+5] == 0x6F && rxbuf[SDIO_HEADER_LEN+6] == 0xFC) {
+
+                fw_is_doing_coredump = true;
+
+                #if SAVE_FW_DUMP_IN_KERNEL
+                if ((fw_dump_total_read_size == 0) && (fw_dump_file == NULL)) {
+                    if(current_fwdump_file_number == probe_counter)
+                        goto FW_DONE;
+                //#if SAVE_FW_DUMP_IN_KERNEL
+                    memset(fw_dump_file_name, 0, sizeof(fw_dump_file_name));
+                    snprintf(fw_dump_file_name, sizeof(fw_dump_file_name),
+                            FW_DUMP_FILE_NAME"_%d", probe_counter);
+                    BTMTK_WARN("%s : open file %s\n", __func__, fw_dump_file_name);
+                    fw_dump_file = filp_open(fw_dump_file_name, O_RDWR | O_CREAT, 0644);
+                    if (fw_dump_file){
+                        current_fwdump_file_number =  probe_counter;
+                        BTMTK_WARN("%s : open file %s success\n", __func__, fw_dump_file_name);
+                    }
+                    else
+                        BTMTK_WARN("%s : open file %s fail\n", __func__, fw_dump_file_name);
+                //#endif
+
+                }
+                #endif
+                fw_dump_total_read_size += dump_len;
+
+                #if SAVE_FW_DUMP_IN_KERNEL
+                if (fw_dump_file->f_op == NULL)
+                    BTMTK_WARN("%s : fw_dump_file->f_op is NULL\n", __func__);
+
+                if (fw_dump_file->f_op->write == NULL)
+                    BTMTK_WARN("%s : fw_dump_file->f_op->write is NULL\n", __func__);
+
+
+                if ((dump_len > 0) && fw_dump_file)
+                    fw_dump_file->f_op->write(fw_dump_file, &rxbuf[SDIO_HEADER_LEN+10], dump_len, &fw_dump_file->f_pos);
+
+                #endif
+
+                if (dump_len >= sizeof(FW_DUMP_END_EVENT)) {
+                    core_dump_end = strstr(&rxbuf[SDIO_HEADER_LEN+10], FW_DUMP_END_EVENT);
+                    BTMTK_WARN("%s : core_dump_end %d\n", __func__, SDIO_HEADER_LEN);
+                    if (core_dump_end) {
+                        BTMTK_INFO("%s  vfs_fsync ", __func__);
+
+                        #if SAVE_FW_DUMP_IN_KERNEL
+                        if(fw_dump_file)
+                            vfs_fsync(fw_dump_file, 0);
+                        #endif
+
+                        if (fw_dump_file) {
+                            BTMTK_INFO("%s : close file  %s\n", __func__, fw_dump_file_name);
+                            #if SAVE_FW_DUMP_IN_KERNEL
+                            filp_close(fw_dump_file, NULL);
+                            //#endif
+                            fw_dump_file = NULL;
+                            #endif
+                            fw_is_doing_coredump = false;
+                            fw_is_coredump_end_packet = true;
+                        } else{
+                            fw_is_doing_coredump = false;
+                            BTMTK_INFO("%s : fw_dump_file is NULL can't close file %s", __func__, fw_dump_file_name);
+                        }
+                    }
+
+                }
+
+            }
+
+        }
+    }
+#endif
+
+#if SAVE_FW_DUMP_IN_KERNEL
+FW_DONE:
+#endif
+
+        type = rxbuf[MTK_SDIO_PACKET_HEADER_SIZE];
+
+
+        btmtk_print_buffer_conent(rxbuf, rx_length);
+
+        /* Read the length of data to be transferred , not include pkt type*/
+        buf_len = rx_length-(MTK_SDIO_PACKET_HEADER_SIZE+1);
+
+
+        BTMTK_DBG("buf_len : %d", buf_len);
+        if (rx_length <= SDIO_HEADER_LEN) {
+                BTMTK_WARN("invalid packet length: %d", buf_len);
+                ret = -EINVAL;
+                goto exit;
+        }
+
+        /* Allocate buffer */
+        skb = bt_skb_alloc(rx_length/*num_blocks * blksz + BTSDIO_DMA_ALIGN*/, GFP_ATOMIC);
+        if (skb == NULL) {
+                BTMTK_WARN("No free skb");
+                ret = -ENOMEM;
+                goto exit;
+        }
+
+        payload = rxbuf;
+        BTMTK_DBG("%s rx_length %d,buf_len %d", __func__, rx_length, buf_len);
+
+        memcpy(skb->data, &rxbuf[MTK_SDIO_PACKET_HEADER_SIZE+1], buf_len);
+
+        switch (type) {
+        case HCI_ACLDATA_PKT:
+                BTMTK_DBG("%s data[2] 0x%02x, data[3] 0x%02x\n", __func__, skb->data[2], skb->data[3]);
+                buf_len = skb->data[2] + skb->data[3]*256 + 4;
+                BTMTK_DBG("%s acl buf_len %d\n", __func__, buf_len);
+                break;
+        case HCI_SCODATA_PKT:
+                buf_len = skb->data[3] + 3;
+                break;
+        case HCI_EVENT_PKT:
+                buf_len = skb->data[1] + 2;
+                break;
+        }
+
+        lock_unsleepable_lock(&(metabuffer.spin_lock));
+        if (priv->adapter->fops_mode & (!fw_is_doing_coredump) & (!fw_is_coredump_end_packet)) {
+            fops_skb = bt_skb_alloc(buf_len, GFP_ATOMIC);
+            bt_cb(fops_skb)->pkt_type = type;
+            memcpy(fops_skb->data, skb->data, buf_len);
+            fops_skb->len = buf_len;
+            skb_queue_tail(&g_priv->adapter->fops_queue, fops_skb);
+            BTMTK_DBG("%s push fops_queue\n", __func__);
+            if (skb_queue_empty(&g_priv->adapter->fops_queue))
+                BTMTK_INFO("%s fops_queue is empty\n", __func__);
+
+            kfree_skb(skb);
+            unlock_unsleepable_lock(&(metabuffer.spin_lock));
+            BTMTK_DBG("%s call inq wake up\n", __func__);
+            wake_up_interruptible(&inq);
+            goto exit;
+        }
+        unlock_unsleepable_lock(&(metabuffer.spin_lock));
+
+        switch (type) {
+        case HCI_ACLDATA_PKT:
+        case HCI_SCODATA_PKT:
+        case HCI_EVENT_PKT:
+                bt_cb(skb)->pkt_type = type;
+
+                skb_put(skb, buf_len);
+
+                break;
+
+        case MTK_VENDOR_PKT:
+                BTMTK_WARN("%s, MTK_VENDOR_PKT no handle now, break\n", __func__);
+                kfree_skb(skb);
+                break;
+
+                bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+                skb_put(skb, buf_len);
+                skb_pull(skb, SDIO_HEADER_LEN);
+
+
+/*
+* if kernel < 3, 11, 0, should use hci_recv_frame(skb);
+*/
+
+                break;
+
+        default:
+                BTMTK_WARN("Unknown packet type:%d", type);
+                BTMTK_WARN("hex: %*ph", blksz * num_blocks, payload);
+
+                kfree_skb(skb);
+                skb = NULL;
+                break;
+        }
+
+exit:
+        if (ret) {
+                BTMTK_DBG("%s fail free skb\n", __func__);
+                kfree_skb(skb);
+        }
+
+
+        buf_len += 1;
+        if (buf_len%4)
+            fourbalignment_len = buf_len + 4 - buf_len%4;
+        else
+            fourbalignment_len = buf_len;
+
+        rx_length -= fourbalignment_len;
+
+        if (rx_length > (MTK_SDIO_PACKET_HEADER_SIZE)) {
+            memcpy(&rxbuf[MTK_SDIO_PACKET_HEADER_SIZE],
+            &rxbuf[MTK_SDIO_PACKET_HEADER_SIZE+fourbalignment_len],
+            rx_length-MTK_SDIO_PACKET_HEADER_SIZE);
+        }
+
+        BTMTK_DBG("%s ret %d, rx_length, %d,fourbalignment_len %d <--\n", __func__, ret, rx_length, fourbalignment_len);
+
+        return ret;
+}
+
+static int btmtk_sdio_process_int_status(struct btmtk_private *priv)
+{
+        int ret = 0;
+        u32 u32rxdatacount = 0;
+        u32 u32ReadCRValue = 0;
+
+        ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
+        BTMTK_DBG("%s check TX_EMPTY CHISR 0x%08x\n", __func__, u32ReadCRValue);
+        if (TX_EMPTY&u32ReadCRValue) {
+            ret = btmtk_sdio_writel(CHISR, TX_EMPTY);
+            priv->btmtk_dev.tx_dnld_rdy = true;
+            BTMTK_DBG("%s set tx_dnld_rdy 1\n", __func__);
+        }
+
+        if (RX_DONE&u32ReadCRValue)
+            ret = btmtk_sdio_recv_rx_data();
+
+
+        if (ret == 0)
+            while (rx_length > (MTK_SDIO_PACKET_HEADER_SIZE)) {
+                btmtk_sdio_card_to_host(priv);
+                u32rxdatacount++;
+                BTMTK_DBG("%s u32rxdatacount %d\n", __func__, u32rxdatacount);
+            }
+
+        btmtk_sdio_enable_interrupt(1);
+
+        return 0;
+}
+
+int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count)
+{
+        int rem = 0;
+
+
+
+        return rem;
+}
+
+static void btmtk_sdio_interrupt(struct sdio_func *func)
+{
+    struct btmtk_private *priv;
+    struct btmtk_sdio_card *card;
+
+
+    card = sdio_get_drvdata(func);
+
+    if (!card)
+            return;
+
+
+    if (!card->priv)
+            return;
+
+    priv = card->priv;
+    btmtk_sdio_enable_interrupt(0);
+
+
+    btmtk_interrupt(priv);
+
+    return;
+}
+
+static int btmtk_sdio_register_dev(struct btmtk_sdio_card *card)
+{
+        struct sdio_func *func;
+        u32    u32ReadCRValue = 0;
+        u8 reg;
+        int ret = 0;
+
+        if (!card || !card->func) {
+                BTMTK_ERR("Error: card or function is NULL!");
+                ret = -EINVAL;
+                goto failed;
+        }
+
+        func = card->func;
+
+        sdio_claim_host(func);
+
+        ret = sdio_enable_func(func);
+        if (ret) {
+                BTMTK_ERR("sdio_enable_func() failed: ret=%d", ret);
+                ret = -EIO;
+                goto release_host;
+        }
+
+
+        btmtk_sdio_readb(SDIO_CCCR_IENx, &u32ReadCRValue);
+        BTMTK_INFO("before claim irq read SDIO_CCCR_IENx %x, func num %d\n", u32ReadCRValue, func->num);
+
+
+        ret = sdio_claim_irq(func, btmtk_sdio_interrupt);
+        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, &u32ReadCRValue);
+        BTMTK_INFO("after claim irq read SDIO_CCCR_IENx %x\n", u32ReadCRValue);
+
+        ret = sdio_set_block_size(card->func, SDIO_BLOCK_SIZE);
+        if (ret) {
+                BTMTK_ERR("cannot set SDIO block size");
+                ret = -EIO;
+                goto release_irq;
+        }
+
+        reg = sdio_readb(func, card->reg->io_port_0, &ret);
+        if (ret < 0) {
+                ret = -EIO;
+                goto release_irq;
+        }
+
+        card->ioport = reg;
+
+        reg = sdio_readb(func, card->reg->io_port_1, &ret);
+        if (ret < 0) {
+                ret = -EIO;
+                goto release_irq;
+        }
+
+        card->ioport |= (reg << 8);
+
+        reg = sdio_readb(func, card->reg->io_port_2, &ret);
+        if (ret < 0) {
+                ret = -EIO;
+                goto release_irq;
+        }
+
+        card->ioport |= (reg << 16);
+
+        BTMTK_INFO("SDIO FUNC%d IO port: 0x%x", func->num, card->ioport);
+
+        if (card->reg->int_read_to_clear) {
+                reg = sdio_readb(func, card->reg->host_int_rsr, &ret);
+                if (ret < 0) {
+                        ret = -EIO;
+                        goto release_irq;
+                }
+                sdio_writeb(func, reg | 0x3f, card->reg->host_int_rsr, &ret);
+                if (ret < 0) {
+                        ret = -EIO;
+                        goto release_irq;
+                }
+
+                reg = sdio_readb(func, card->reg->card_misc_cfg, &ret);
+                if (ret < 0) {
+                        ret = -EIO;
+                        goto release_irq;
+                }
+                sdio_writeb(func, reg | 0x10, card->reg->card_misc_cfg, &ret);
+                if (ret < 0) {
+                        ret = -EIO;
+                        goto release_irq;
+                }
+        }
+
+        sdio_set_drvdata(func, card);
+
+        sdio_release_host(func);
+
+        return 0;
+
+release_irq:
+        sdio_release_irq(func);
+
+disable_func:
+        sdio_disable_func(func);
+
+release_host:
+        sdio_release_host(func);
+
+failed:
+        BTMTK_INFO("%s fail\n", __func__);
+        return ret;
+}
+
+static int btmtk_sdio_unregister_dev(struct btmtk_sdio_card *card)
+{
+        if (card && card->func) {
+                sdio_claim_host(card->func);
+                sdio_release_irq(card->func);
+                sdio_disable_func(card->func);
+                sdio_release_host(card->func);
+                sdio_set_drvdata(card->func, NULL);
+        }
+
+        return 0;
+}
+
+static int btmtk_sdio_enable_host_int(struct btmtk_sdio_card *card)
+{
+        int ret;
+        u32 read_data = 0;
+
+        if (!card || !card->func)
+                return -EINVAL;
+
+        sdio_claim_host(card->func);
+
+        ret = btmtk_sdio_enable_host_int_mask(card, HIM_ENABLE);
+
+        btmtk_sdio_get_rx_unit(card);
+
+if (0) {
+                typedef int (*fp_sdio_hook)(struct mmc_host *host, unsigned int width);
+                fp_sdio_hook func_sdio_hook = (fp_sdio_hook) kallsyms_lookup_name("mmc_set_bus_width");
+                unsigned char data = 0;
+
+                data = sdio_f0_readb(card->func, SDIO_CCCR_IF, &ret);
+                if (ret)
+                    BTMTK_INFO("%s sdio_f0_readb ret %d\n", __func__, ret);
+
+                BTMTK_INFO("%s sdio_f0_readb data 0x%X!\n", __func__, data);
+
+                data  &= ~SDIO_BUS_WIDTH_MASK;
+                data  |= SDIO_BUS_ASYNC_INT;
+                card->func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+
+                sdio_f0_writeb(card->func, data, SDIO_CCCR_IF, &ret);
+                if (ret)
+                    BTMTK_INFO("%s sdio_f0_writeb ret %d\n", __func__, ret);
+
+                BTMTK_INFO("%s func_sdio_hook at 0x%p!\n", __func__, func_sdio_hook);
+                if (func_sdio_hook)
+                    func_sdio_hook(card->func->card->host, MMC_BUS_WIDTH_1);
+
+
+                data = sdio_f0_readb(card->func, SDIO_CCCR_IF, &ret);
+                if (ret)
+                BTMTK_INFO("%s sdio_f0_readb 2 ret %d\n", __func__, ret);
+
+
+                BTMTK_INFO("%s sdio_f0_readb2 data 0x%X\n", __func__, data);
+}
+
+        sdio_release_host(card->func);
+
+
+/*
+* workaround for some platfrom no host clock sometimes
+*/
+
+        btmtk_sdio_readl(CSDIOCSR, &read_data);
+        BTMTK_INFO("%s read CSDIOCSR is 0x%X\n", __func__, read_data);
+        read_data |= 0x4;
+        btmtk_sdio_writel(CSDIOCSR, read_data);
+        BTMTK_INFO("%s write CSDIOCSR is 0x%X\n", __func__, read_data);
+
+
+
+        return ret;
+}
+
+static int btmtk_sdio_disable_host_int(struct btmtk_sdio_card *card)
+{
+        int ret;
+
+        if (!card || !card->func)
+                return -EINVAL;
+
+        sdio_claim_host(card->func);
+
+        ret = btmtk_sdio_disable_host_int_mask(card, HIM_DISABLE);
+
+        sdio_release_host(card->func);
+
+        return ret;
+}
+
+static int btmtk_sdio_host_to_card(struct btmtk_private *priv,
+                                u8 *payload, u16 nb)
+{
+        struct btmtk_sdio_card *card = priv->btmtk_dev.card;
+        int ret = 0;
+        int i = 0;
+        u8 MultiBluckCount = 0;
+        u8 redundant = 0;
+
+
+        if (!card || !card->func) {
+                BTMTK_ERR("card or function is NULL!");
+                return -EINVAL;
+        }
+
+        MultiBluckCount = nb/SDIO_BLOCK_SIZE;
+        redundant = nb % SDIO_BLOCK_SIZE;
+
+        if (redundant)
+           nb = (MultiBluckCount+1)*SDIO_BLOCK_SIZE;
+
+
+       
+
+       if (nb < 16)
+            btmtk_print_buffer_conent(txbuf, nb);
+       else
+            btmtk_print_buffer_conent(txbuf, 16);
+
+       do {
+                 /* Transfer data to card */
+                 sdio_claim_host(card->func);
+                 ret = sdio_writesb(card->func, CTDR, txbuf,
+                                    nb);
+                 sdio_release_host(card->func);
+                 if (ret < 0) {
+                         i++;
+                         BTMTK_ERR("i=%d writesb failed: %d", i, ret);
+                         BTMTK_ERR("hex: %*ph", nb, txbuf);
+                         ret = -EIO;
+                         if (i > MAX_WRITE_IOMEM_RETRY)
+                                 goto exit;
+                 }
+         } while (ret);
+
+         if (priv)
+            priv->btmtk_dev.tx_dnld_rdy = false;
+
+ exit:
+         
+
+
+         return ret;
+ }
+
+ static int btmtk_sdio_download_fw(struct btmtk_sdio_card *card)
+ {
+         int ret;
+
+         BTMTK_INFO("%s begin", __func__);
+         if (!card || !card->func) {
+                 BTMTK_ERR("card or function is NULL!");
+                 return -EINVAL;
+         }
+
+
+
+         sdio_claim_host(card->func);
+
+         if (btmtk_sdio_download_rom_patch(card)) {
+                         BTMTK_ERR("Failed to download firmware!");
+                         ret = -EIO;
+                         goto done;
+         }
+
+         /*
+          * winner or not, with this test the FW synchronizes when the
+          * module can continue its initialization
+          */
+         sdio_release_host(card->func);
+
+         return 0;
+
+ done:
+         sdio_release_host(card->func);
+         return ret;
+ }
+
+ static int btmtk_sdio_probe(struct sdio_func *func,
+                                         const struct sdio_device_id *id)
+ {
+    int ret = 0;
+    struct btmtk_private *priv = NULL;
+    struct btmtk_sdio_card *card = NULL;
+    struct btmtk_sdio_device *data = (void *) id->driver_data;
+    u32 u32ReadCRValue = 0;
+
+    probe_counter++;
+    BTMTK_INFO("%s Mediatek Bluetooth driver Version=%s", __func__, VERSION);
+    BTMTK_INFO("vendor=0x%x, device=0x%x, class=%d, fn=%d, support func_num %d",
+                        id->vendor, id->device, id->class, func->num, data->reg->func_num);
+
+    if (func->num != data->reg->func_num) {
+        BTMTK_INFO("func num is not match");
+        return -ENODEV;
+    }
+
+
+        card = devm_kzalloc(&func->dev, sizeof(*card), GFP_KERNEL);
+        if (!card)
+                return -ENOMEM;
+
+        card->func = func;
+        g_card = card;
+
+        if (id->driver_data) {
+
+                card->helper = data->helper;
+                card->firmware = data->firmware;
+                card->firmware1 = data->firmware1;
+                card->reg = data->reg;
+                card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
+                card->support_pscan_win_report = data->support_pscan_win_report;
+                card->supports_fw_dump = data->supports_fw_dump;
+        }
+
+        BTMTK_INFO("%s func device %X\n", __func__, card->func->device);
+        BTMTK_INFO("%s Call btmtk_sdio_register_dev\n", __func__);
+        if (btmtk_sdio_register_dev(card) < 0) {
+                BTMTK_ERR("Failed to register BT device!");
+                return -ENODEV;
+        } else
+            BTMTK_INFO("%s btmtk_sdio_register_dev success\n", __func__);
+
+
+        /* Disable the interrupts on the card */
+        btmtk_sdio_enable_host_int(card);
+        BTMTK_INFO("call btmtk_sdio_enable_host_int done");
+        if (btmtk_sdio_download_fw(card)) {
+            BTMTK_ERR("Downloading firmware failed!");
+            ret = -ENODEV;
+            goto unreg_dev;
+        }
+
+
+
+
+
+         priv = btmtk_add_card(card);
+         if (!priv) {
+                 BTMTK_ERR("Initializing card failed!");
+                 ret = -ENODEV;
+                 goto unreg_dev;
+         }
+         BTMTK_INFO("btmtk_add_card success");
+         card->priv = priv;
+         BTMTK_INFO("assign priv done");
+         /* Initialize the interface specific function pointers */
+         priv->hw_host_to_card = btmtk_sdio_host_to_card;
+         priv->hw_process_int_status = btmtk_sdio_process_int_status;
+         priv->hw_set_own_back =  btmtk_sdio_set_own_back;
+         if (btmtk_register_hdev(priv)) {
+                 BTMTK_ERR("Register hdev failed!");
+                 ret = -ENODEV;
+                 goto unreg_dev;
+         }
+        g_priv = priv;
+        if (fw_dump_ptr == NULL)
+            fw_dump_ptr = kmalloc(FW_DUMP_BUF_SIZE, GFP_ATOMIC);
+
+
+        if (fw_dump_ptr == NULL) {
+            BTMTK_ERR("%s : alloc fw_dump_ptr fail\n", __func__);
+            ret = -ENODEV;
+            return ret;
+        }
+
+        memset(&metabuffer.buffer, 0, META_BUFFER_SIZE);
+        memset(fw_dump_ptr, 0, FW_DUMP_BUF_SIZE);
+        fw_dump_task_should_stop = 0;
+#if SAVE_FW_DUMP_IN_KERNEL
+        fw_dump_file = NULL;
+#endif
+        fw_dump_read_ptr = fw_dump_ptr;
+        fw_dump_write_ptr = fw_dump_ptr;
+        fw_dump_total_read_size = 0;
+        fw_dump_total_write_size = 0;
+        fw_dump_buffer_used_size = 0;
+        fw_dump_buffer_full = 0;
+
+        ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
+        BTMTK_INFO("%s read CHLPCR (0x%08X)\n", __func__, u32ReadCRValue);
+        BTMTK_INFO("%s normal end\n", __func__);
+        probe_ready = true;
+        return 0;
+
+ unreg_dev:
+         btmtk_sdio_unregister_dev(card);
+
+         BTMTK_ERR("%s fail end\n", __func__);
+         return ret;
+ }
+
+ static void btmtk_sdio_remove(struct sdio_func *func)
+ {
+         struct btmtk_sdio_card *card;
+
+         BTMTK_INFO("%s begin user_rmmod %d\n", __func__,user_rmmod);
+         probe_ready = false;
+
+         if (func) {
+                 card = sdio_get_drvdata(func);
+                 if (card) {
+                         /* Send SHUTDOWN command & disable interrupt
+                          * if user removes the module.
+                          */
+                         if (user_rmmod) {
+                                 BTMTK_INFO("%s begin user_rmmod %d in user mode\n", __func__,user_rmmod);
+                                 btmtk_sdio_set_own_back(DRIVER_OWN);
+                                 btmtk_sdio_enable_interrupt(0);
+                                 btmtk_sdio_bt_set_power(0);
+                                 btmtk_sdio_set_own_back(FW_OWN);
+
+                                 btmtk_sdio_disable_host_int(card);
+                         }
+                         BTMTK_DBG("unregester dev");
+                         card->priv->surprise_removed = true;
+                         btmtk_sdio_unregister_dev(card);
+                         btmtk_remove_card(card->priv);
+
+
+
+                 }
+         }
+         BTMTK_INFO("%s end\n", __func__);
+ }
+
+ static int btmtk_sdio_suspend(struct device *dev)
+ {
+         struct sdio_func *func = dev_to_sdio_func(dev);
+         struct btmtk_sdio_card *card;
+         struct btmtk_private *priv;
+         mmc_pm_flag_t pm_flags;
+         struct hci_dev *hcidev;
+         u8 ret = 0;
+
+         BTMTK_INFO("%s begin return 0, do nothing", __func__);
+         return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+
+         btmtk_sdio_bt_set_power(0);
+         ret = btmtk_sdio_set_own_back(FW_OWN);
+         if (ret) {
+            BTMTK_ERR("%s set DRIVER_FW fail", __func__);
+            return -EBUSY;
+         }
+
+         if (func) {
+                 pm_flags = sdio_get_host_pm_caps(func);
+                 BTMTK_DBG("%s: suspend: PM flags = 0x%x", sdio_func_id(func),
+                        pm_flags);
+                 if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+                         BTMTK_ERR("%s: cannot remain alive while suspended",
+                                sdio_func_id(func));
+                         return -EINVAL;
+                 }
+                 card = sdio_get_drvdata(func);
+                 if (!card || !card->priv) {
+                         BTMTK_ERR("card or priv structure is not valid");
+                         return 0;
+                 }
+         } else {
+                 BTMTK_ERR("sdio_func is not specified");
+                 return 0;
+         }
+
+         priv = card->priv;
+         hcidev = priv->btmtk_dev.hcidev;
+         BTMTK_DBG("%s: SDIO suspend", hcidev->name);
+         skb_queue_purge(&priv->adapter->tx_queue);
+
+
+
+
+         if (priv->adapter->hs_state != HS_ACTIVATED) {
+                 if (btmtk_enable_hs(priv)) {
+                         BTMTK_ERR("HS not actived, suspend failed!");
+                         return -EBUSY;
+                 }
+         }
+
+
+         priv->adapter->is_suspended = true;
+
+
+         if (priv->adapter->hs_state == HS_ACTIVATED) {
+                 BTMTK_DBG("suspend with MMC_PM_KEEP_POWER");
+                 return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+         } else {
+                 BTMTK_DBG("suspend without MMC_PM_KEEP_POWER");
+                 return 0;
+         }
+ }
+
+ static int btmtk_sdio_resume(struct device *dev)
+ {
+
+         struct sdio_func *func = dev_to_sdio_func(dev);
+         struct btmtk_sdio_card *card;
+         struct btmtk_private *priv;
+         mmc_pm_flag_t pm_flags;
+         struct hci_dev *hcidev;
+
+         BTMTK_INFO("%s begin return 0, do nothing", __func__);
+         return 0;
+
+         if (func) {
+                 pm_flags = sdio_get_host_pm_caps(func);
+                 BTMTK_DBG("%s: resume: PM flags = 0x%x", sdio_func_id(func),
+                        pm_flags);
+                 card = sdio_get_drvdata(func);
+                 if (!card || !card->priv) {
+                         BTMTK_ERR("card or priv structure is not valid");
+                         return 0;
+                 }
+         } else {
+                 BTMTK_ERR("sdio_func is not specified");
+                 return 0;
+         }
+         priv = card->priv;
+
+         if (!priv->adapter->is_suspended)
+                 BTMTK_DBG("device already resumed");
+
+         priv->adapter->hs_state = HS_DEACTIVATED;
+         hcidev = priv->btmtk_dev.hcidev;
+         BTMTK_DBG("%s: HS DEACTIVATED in resume!", hcidev->name);
+         priv->adapter->is_suspended = false;
+         BTMTK_DBG("%s: SDIO resume", hcidev->name);
+
+         return 0;
+ }
+
+ static const struct dev_pm_ops btmtk_sdio_pm_ops = {
+         .suspend        = btmtk_sdio_suspend,
+         .resume         = btmtk_sdio_resume,
+ };
+
+ static struct sdio_driver bt_mtk_sdio = {
+         .name           = "btmtk_sdio",
+         .id_table       = btmtk_sdio_ids,
+         .probe          = btmtk_sdio_probe,
+         .remove         = btmtk_sdio_remove,
+         .drv = {
+                 .owner = THIS_MODULE,
+                 .pm = &btmtk_sdio_pm_ops,
+         }
+ };
+
+
+static int btmtk_fops_open(struct inode *inode, struct file *file)
+{
+    BTMTK_INFO("%s begin", __func__);
+
+    if (!probe_ready) {
+        BTMTK_ERR("%s probe_ready is %d return", __func__,probe_ready);
+        return -EFAULT;
+    }
+
+    spin_lock_init(&(metabuffer.spin_lock.lock));
+    BTMTK_INFO("%s spin_lock_init end", __func__);
+    if (g_priv == NULL) {
+        BTMTK_ERR("%s g_priv is NULL", __func__);
+        return -ENOENT;
+    }
+
+    if (g_priv->adapter == NULL) {
+        BTMTK_ERR("%s g_priv->adapter is NULL", __func__);
+        return -ENOENT;
+    }
+
+    if (g_priv)
+        g_priv->adapter->fops_mode = true;
+
+    BTMTK_INFO("%s fops_mode=%d end", __func__, g_priv->adapter->fops_mode);
+    return 0;
+}
+
+static int btmtk_fops_close(struct inode *inode, struct file *file)
+{
+    struct sk_buff *skb = NULL;
+
+    BTMTK_INFO("%s begin", __func__);
+
+    if (!probe_ready) {
+        BTMTK_ERR("%s probe_ready is %d return", __func__,probe_ready);
+        return -EFAULT;
+    }
+
+    spin_lock_init(&(metabuffer.spin_lock.lock));
+    if (g_priv)
+        g_priv->adapter->fops_mode = false;
+
+    lock_unsleepable_lock(&(metabuffer.spin_lock));
+
+
+    if (!skb_queue_empty(&g_priv->adapter->fops_queue)) {
+       BTMTK_INFO("%s clean data in fops_queue", __func__);
+       do {
+            skb = skb_dequeue(&g_priv->adapter->fops_queue);
+            if (skb == NULL) {
+                BTMTK_INFO("%s skb=NULL error break", __func__);
+                break;
+            }
+
+           kfree_skb(skb);
+        } while (!skb_queue_empty(&g_priv->adapter->fops_queue));
+
+    }
+
+
+    unlock_unsleepable_lock(&(metabuffer.spin_lock));
+
+    BTMTK_INFO("%s fops_mode=%d end", __func__, g_priv->adapter->fops_mode);
+    return 0;
+}
+
+ssize_t btmtk_fops_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
+{
+    int retval = 0;
+    struct sk_buff *skb = NULL;
+    u32 crAddr = 0, crValue = 0, crMask = 0;
+    struct sk_buff *queue_skb = NULL;
+    /*int i = 0;*/
+
+    if (!probe_ready) {
+        BTMTK_ERR("%s probe_ready is %d return", __func__,probe_ready);
+        return -EFAULT;
+    }
+
+    if (g_priv == NULL) {
+        BTMTK_INFO("%s g_priv is NULL", __func__);
+        return -EFAULT;
+    }
+
+    if (g_priv->adapter->fops_mode == 0) {
+        BTMTK_INFO("%s fops_mode is 0", __func__);
+        return -EFAULT;
+    }
+
+
+	/*BTMTK_INFO("%s : (%d) %02X %02X %02X %02X %02X %02X %02X %02X\n", __func__, (int)count,
+			buf[0],
+			buf[1],
+			buf[2],
+			buf[3],
+			buf[4],
+			buf[5],
+			buf[6],
+			buf[7]);*/
+
+    /*
+    BTMTK_INFO("%s print write data", __func__);
+    if (count > 10)
+        BTMTK_INFO("  %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],buf[8],buf[9]);
+    else {
+        for(i=0;i<count;i++)
+            BTMTK_INFO("%d %02X",i,buf[i]);
+    }*/
+
+    if (buf[0] == 0x7) {
+/*
+* write CR
+*/
+        if (count < 15) {
+            BTMTK_INFO("%s count=%zd less than 15, error", __func__, count);
+            return -EFAULT;
+        }
+
+        crAddr = (buf[3]&0xff) + ((buf[4]&0xff)<<8) + ((buf[5]&0xff)<<16) + ((buf[6]&0xff)<<24);
+        crValue = (buf[7]&0xff) + ((buf[8]&0xff)<<8) + ((buf[9]&0xff)<<16) + ((buf[10]&0xff)<<24);
+        crMask = (buf[11]&0xff) + ((buf[12]&0xff)<<8) + ((buf[13]&0xff)<<16) + ((buf[14]&0xff)<<24);
+
+        BTMTK_INFO("%s crAddr=0x%08x crValue=0x%08x crMask=0x%08x", __func__, crAddr, crValue, crMask);
+        crValue &= crMask;
+
+
+        BTMTK_INFO("%s write crAddr=0x%08x crValue=0x%08x", __func__, crAddr, crValue);
+        btmtk_sdio_writel(crAddr, crValue);
+        retval = count;
+    } else if (buf[0] == 0x8) {
+/*
+* read CR
+*/
+        if (count < 16) {
+            BTMTK_INFO("%s count=%zd less than 15, error", __func__, count);
+            return -EFAULT;
+        }
+
+        crAddr = (buf[3]&0xff) + ((buf[4]&0xff)<<8) + ((buf[5]&0xff)<<16) + ((buf[6]&0xff)<<24);
+        crMask = (buf[11]&0xff) + ((buf[12]&0xff)<<8) + ((buf[13]&0xff)<<16) + ((buf[14]&0xff)<<24);
+
+        btmtk_sdio_readl(crAddr, &crValue);
+        BTMTK_INFO("%s read crAddr=0x%08x crValue=0x%08x crMask=0x%08x", __func__, crAddr, crValue, crMask);
+        retval = count;
+    } else {
+
+        skb = bt_skb_alloc(count-1, GFP_ATOMIC);
+        bt_cb(skb)->pkt_type = buf[0];
+        memcpy(&skb->data[0], &buf[1], count-1);
+        skb->len = count-1;
+        queue_skb = skb;
+        skb_queue_tail(&g_priv->adapter->tx_queue, queue_skb);
+        wake_up_interruptible(&g_priv->main_thread.wait_q);
+
+        retval = count;
+    }
+    BTMTK_DBG("%s end", __func__);
+    return retval;
+}
+
+ssize_t btmtk_fops_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+    int copyLen = 0;
+    struct sk_buff *skb = NULL;
+    unsigned long ret = 0;
+
+    if (!probe_ready) {
+        BTMTK_ERR("%s probe_ready is %d return", __func__,probe_ready);
+        return -EFAULT;
+    }
+
+    if (g_priv == NULL) {
+        BTMTK_INFO("%s g_priv is NULL", __func__);
+        return -EFAULT;
+    }
+
+    if (g_priv->adapter->fops_mode == 0) {
+        BTMTK_INFO("%s fops_mode is 0", __func__);
+        return -EFAULT;
+    }
+
+    lock_unsleepable_lock(&(metabuffer.spin_lock));
+
+
+    if (skb_queue_empty(&g_priv->adapter->fops_queue)) {
+        if (filp->f_flags & O_NONBLOCK) {
+            unlock_unsleepable_lock(&(metabuffer.spin_lock));
+            return 0;
+        }
+    }
+
+    do {
+        skb = skb_dequeue(&g_priv->adapter->fops_queue);
+        if (skb == NULL) {
+            BTMTK_INFO("%s skb=NULL error break", __func__);
+            break;
+        }
+        //BTMTK_DBG("%s pkt_type %d metabuffer.buffer %d", __func__, bt_cb(skb)->pkt_type, metabuffer.buffer[copyLen]);
+        btmtk_print_buffer_conent(skb->data, skb->len);
+
+        if (((copyLen + 1 + skb->len) > META_BUFFER_SIZE) || ((copyLen + 1 + skb->len) > count)) {
+            BTMTK_ERR("%s copy copyLen %d > META_BUFFER_SIZE(%d), push back to queue", __func__,
+            (copyLen + 1 + skb->len), META_BUFFER_SIZE);
+            skb_queue_head(&g_priv->adapter->fops_queue, skb);
+            break;
+        }
+
+        metabuffer.buffer[copyLen] = bt_cb(skb)->pkt_type;
+        copyLen++;
+
+        memcpy(&metabuffer.buffer[copyLen], skb->data, skb->len);
+        copyLen += skb->len;
+
+        kfree_skb(skb);
+
+    } while (!skb_queue_empty(&g_priv->adapter->fops_queue));
+    unlock_unsleepable_lock(&(metabuffer.spin_lock));
+
+
+    ret = copy_to_user(buf, metabuffer.buffer, copyLen);
+    if (ret) {
+        BTMTK_ERR("%s copy to user fail, ret %d", __func__, (int)ret);
+        copyLen = (copyLen - ret);
+    }/* else {
+    	BTMTK_INFO("%s : (%d) %02X %02X %02X %02X %02X %02X %02X %02X\n", __func__, copyLen, 
+    			metabuffer.buffer[0],
+    			metabuffer.buffer[1],
+    			metabuffer.buffer[2],
+    			metabuffer.buffer[3],
+    			metabuffer.buffer[4],
+    			metabuffer.buffer[5],
+    			metabuffer.buffer[6],
+    			metabuffer.buffer[7]);
+    }*/
+
+    //BTMTK_DBG("%s copyLen %d", __func__, copyLen);
+    return copyLen;
+}
+
+unsigned int btmtk_fops_poll(struct file *filp, poll_table *wait)
+{
+    unsigned int mask = 0;
+
+    if (!probe_ready) {
+        BTMTK_ERR("%s probe_ready is %d return", __func__,probe_ready);
+        return mask;
+    }
+
+    if (g_priv == NULL) {
+        BTMTK_ERR("%s g_priv is NULL", __func__);
+        return -ENODEV;
+    }
+
+    if (skb_queue_empty(&g_priv->adapter->fops_queue)) {
+        poll_wait(filp, &inq, wait);
+
+        if (!skb_queue_empty(&g_priv->adapter->fops_queue)) {
+            mask |= (POLLIN | POLLRDNORM);
+            //BTMTK_INFO("%s poll done\n", __func__);
+	}
+    } else
+        mask |= (POLLIN | POLLRDNORM);
+
+    mask |= (POLLOUT | POLLWRNORM);
+
+    //BTMTK_INFO("%s poll mask 0x%x\n", __func__,mask);
+    return mask;
+}
+
+long btmtk_fops_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+    u32 retval = 0;
+
+    return retval;
+}
+
+ static int btmtk_fops_openfwlog(struct inode *inode, struct file *file)
+ {
+     if (g_priv == NULL) {
+         BTMTK_ERR("%s: ERROR, g_data is NULL!", __func__);
+         return -ENODEV;
+     }
+
+     BTMTK_ERR("%s: OK", __func__);
+     return 0;
+ }
+
+ static int btmtk_fops_closefwlog(struct inode *inode, struct file *file)
+ {
+     if (g_priv == NULL) {
+         BTMTK_ERR("%s: ERROR, g_data is NULL!", __func__);
+         return -ENODEV;
+     }
+
+
+     BTMTK_ERR("%s: OK", __func__);
+     return 0;
+ }
+
+ static ssize_t btmtk_fops_readfwlog(struct file *filp, char __user *buf,
+                    size_t count, loff_t *f_pos)
+ {
+     int copyLen = 0;
+
+     if (g_priv == NULL)
+         return -ENODEV;
+
+
+     return copyLen;
+ }
+
+ static ssize_t btmtk_fops_writefwlog(struct file *filp, const char __user *buf,
+                     size_t count, loff_t *f_pos)
+ {
+     int retval = 0;
+     return retval;
+ }
+
+ static unsigned int btmtk_fops_pollfwlog(struct file *file, poll_table *wait)
+ {
+     unsigned int mask = 0;
+     return mask;
+ }
+
+ static long btmtk_fops_unlocked_ioctlfwlog(struct file *filp, unsigned int cmd,
+                       unsigned long arg)
+ {
+     int retval = 0;
+
+     BTMTK_INFO("%s: ->", __func__);
+     if (g_priv == NULL) {
+         BTMTK_ERR("%s: ERROR, g_data is NULL!", __func__);
+         return -ENODEV;
+     }
+
+     return retval;
+ }
+
+
+ static int BTMTK_major;
+ static struct cdev BTMTK_cdev;
+ static int BTMTK_devs = 1;
+
+static wait_queue_head_t inq;
+const struct file_operations BTMTK_fops = {
+    .open = btmtk_fops_open,
+    .release = btmtk_fops_close,
+    .read = btmtk_fops_read,
+    .write = btmtk_fops_write,
+    .poll = btmtk_fops_poll,
+    .unlocked_ioctl = btmtk_fops_unlocked_ioctl
+};
+
+
+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_init(void)
+{
+    dev_t devID = MKDEV(BTMTK_major, 0);
+    int ret = 0;
+    int cdevErr = 0;
+    int major = 0;
+
+    BTMTK_INFO("BTMTK_init\n");
+
+    g_card = NULL;
+    txbuf = NULL;
+    rxbuf = NULL;
+    rx_length = 0;
+
+#if SAVE_FW_DUMP_IN_KERNEL
+    fw_dump_file = NULL;
+#else
+    fw_dump_file = 0;
+#endif
+    g_priv = NULL;
+
+
+    fw_dump_buffer_full = 0;
+    fw_dump_total_read_size = 0;
+    fw_dump_total_write_size = 0;
+    fw_dump_buffer_used_size = 0;
+    fw_dump_task_should_stop = 0;
+    fw_dump_ptr = NULL;
+    fw_dump_read_ptr = NULL;
+    fw_dump_write_ptr = NULL;
+    probe_counter = 0;
+    fw_dump_end_checking_task_should_stop = 0;
+    fw_is_doing_coredump = 0;
+
+
+    ret = alloc_chrdev_region(&devID, 0, 1, BT_DRIVER_NAME);
+    if (ret) {
+        BTMTK_ERR("fail to allocate chrdev\n");
+        return ret;
+    }
+
+    BTMTK_major = major = MAJOR(devID);
+    BTMTK_INFO("major number:%d", BTMTK_major);
+
+    cdev_init(&BTMTK_cdev, &BTMTK_fops);
+    BTMTK_cdev.owner = THIS_MODULE;
+
+    cdevErr = cdev_add(&BTMTK_cdev, devID, BTMTK_devs);
+    if (cdevErr)
+        goto error;
+
+    BTMTK_INFO("%s driver(major %d) installed.\n", BT_DRIVER_NAME, BTMTK_major);
+
+    pBTClass = class_create(THIS_MODULE, BT_DRIVER_NAME);
+    if (IS_ERR(pBTClass)) {
+        BTMTK_ERR("class create fail, error code(%ld)\n", PTR_ERR(pBTClass));
+        goto err1;
+    }
+
+    pBTDev = device_create(pBTClass, NULL, devID, NULL, BT_NODE);
+    if (IS_ERR(pBTDev)) {
+        BTMTK_ERR("device create fail, error code(%ld)\n", PTR_ERR(pBTDev));
+        goto err2;
+    }
+
+    init_waitqueue_head(&(inq));
+
+    return 0;
+
+ err2:
+    if (pBTClass) {
+        class_destroy(pBTClass);
+        pBTClass = NULL;
+    }
+
+ err1:
+
+ error:
+    if (cdevErr == 0)
+        cdev_del(&BTMTK_cdev);
+
+    if (ret == 0)
+        unregister_chrdev_region(devID, BTMTK_devs);
+
+    return -1;
+}
+
+static void BTMTK_exit(void)
+{
+    dev_t dev = MKDEV(BTMTK_major, 0);
+
+    BTMTK_INFO("BTMTK_exit\n");
+
+    if (pBTDev) {
+        device_destroy(pBTClass, dev);
+        pBTDev = NULL;
+    }
+
+    if (pBTClass) {
+        class_destroy(pBTClass);
+        pBTClass = NULL;
+    }
+
+    cdev_del(&BTMTK_cdev);
+    unregister_chrdev_region(dev, 1);
+    BTMTK_INFO("%s driver removed.\n", BT_DRIVER_NAME);
+}
+
+ static int __init btmtk_sdio_init_module(void)
+{
+     BTMTK_init();
+
+     if (txbuf == NULL) {
+        txbuf = kmalloc(MTK_TXDATA_SIZE, GFP_ATOMIC);
+        memset(txbuf, 0, MTK_TXDATA_SIZE);
+     }
+
+     if (rxbuf == NULL) {
+        rxbuf = kmalloc(MTK_RXDATA_SIZE, GFP_ATOMIC);
+        memset(rxbuf, 0, MTK_RXDATA_SIZE);
+     }
+
+
+     if (sdio_register_driver(&bt_mtk_sdio) != 0) {
+                 BTMTK_ERR("SDIO Driver Registration Failed");
+                 return -ENODEV;
+     } else
+         BTMTK_INFO("SDIO Driver Registration Success");
+
+         /* Clear the flag in case user removes the card. */
+         user_rmmod = 0;
+
+         return 0;
+ }
+
+ static void __exit btmtk_sdio_exit_module(void)
+ {
+     /* Set the flag as user is removing this module. */
+     user_rmmod = 1;
+
+     BTMTK_exit();
+
+     sdio_unregister_driver(&bt_mtk_sdio);
+
+     kfree(txbuf);
+
+     kfree(rxbuf);
+
+ }
+
+ module_init(btmtk_sdio_init_module);
+ module_exit(btmtk_sdio_exit_module);
diff --git a/btmtk_sdio.h b/btmtk_sdio.h
new file mode 100644
index 0000000..8863f10
--- /dev/null
+++ b/btmtk_sdio.h
@@ -0,0 +1,230 @@
+/**
+ * Mediatek BT-over-SDIO driver: SDIO interface related definitions
+ *
+ * Copyright (C) 2015, Mediatek International Ltd.
+ *
+ * This software file (the "File") is distributed by Mediatek International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ *
+ **/
+
+#define VERSION "v0.0.0.19"
+
+#define SDIO_HEADER_LEN                 4
+
+#define DUMP_HCI_LOG_FILE_NAME          "/sys/hcilog"
+/* SD block size can not bigger than 64 due to buf size limit in firmware */
+/* define SD block size for data Tx/Rx */
+#define SDIO_BLOCK_SIZE                 256
+
+#define SDIO_PATCH_DOWNLOAD_FIRST    1/*first packet*/
+#define SDIO_PATCH_DOWNLOAD_CON        2/*continue*/
+#define SDIO_PATCH_DOWNLOAD_END        3/*end*/
+
+/* Number of blocks for firmware transfer */
+#define FIRMWARE_TRANSFER_NBLOCK        2
+
+/* This is for firmware specific length */
+#define FW_EXTRA_LEN                    36
+
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+
+#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
+        (HCI_MAX_FRAME_SIZE + FW_EXTRA_LEN)
+
+#define ALLOC_BUF_SIZE  (((max_t (int, MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
+                        MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+                        + SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \
+                        * SDIO_BLOCK_SIZE)
+
+/* The number of times to try when polling for status */
+#define MAX_POLL_TRIES                  100
+
+/* Max retry number of CMD53 write */
+#define MAX_WRITE_IOMEM_RETRY           2
+
+/* register bitmasks */
+#define HOST_POWER_UP                           BIT(1)
+#define HOST_CMD53_FIN                          BIT(2)
+
+#define HIM_DISABLE                             0xff
+#define HIM_ENABLE                              (BIT(0) | BIT(1))
+
+#define UP_LD_HOST_INT_STATUS                   BIT(0)
+#define DN_LD_HOST_INT_STATUS                   BIT(1)
+
+#define DN_LD_CARD_RDY                          BIT(0)
+#define CARD_IO_READY                           BIT(3)
+
+#define FIRMWARE_READY                          0xfedc
+#define CFG_THREE_IN_ONE_FIRMWARE               0
+
+
+#if CFG_THREE_IN_ONE_FIRMWARE
+#define BUILD_SIGN(ch0, ch1, ch2, ch3) \
+                    ((unsigned int)(unsigned char)(ch0) | ((unsigned int)(unsigned char)(ch1) << 8) |    \
+                    ((unsigned int)(unsigned char)(ch2) << 16) | ((unsigned int)(unsigned char)(ch3) << 24))
+
+#define MTK_WIFI_SIGNATURE BUILD_SIGN('M', 'T', 'K', 'W')
+#define FW_TYPE_PATCH   2
+typedef struct _FW_SECTION_T {
+        unsigned int u4FwType;
+        unsigned int u4Offset;
+        unsigned int u4Length;
+} FW_SECTION_T, *P_FW_SECTION_T;
+
+typedef struct _FIRMWARE_HEADER_T {
+        unsigned int u4Signature;
+        unsigned int u4CRC;
+        unsigned int u4NumOfEntries;
+        unsigned int u4Reserved;
+        FW_SECTION_T arSection[];
+} FIRMWARE_HEADER_T, *P_FIRMWARE_HEADER_T;
+#endif
+
+struct btmtk_sdio_card_reg {
+        u8 cfg;
+        u8 host_int_mask;
+        u8 host_intstatus;
+        u8 card_status;
+        u8 sq_read_base_addr_a0;
+        u8 sq_read_base_addr_a1;
+        u8 card_revision;
+        u8 card_fw_status0;
+        u8 card_fw_status1;
+        u8 card_rx_len;
+        u8 card_rx_unit;
+        u8 io_port_0;
+        u8 io_port_1;
+        u8 io_port_2;
+        bool int_read_to_clear;
+        u8 host_int_rsr;
+        u8 card_misc_cfg;
+        u8 fw_dump_ctrl;
+        u8 fw_dump_start;
+        u8 fw_dump_end;
+    u8 func_num;
+};
+
+struct btmtk_sdio_card {
+        struct sdio_func *func;
+        u32 ioport;
+        const char *helper;
+        const char *firmware;
+        const char *firmware1;
+        const struct btmtk_sdio_card_reg *reg;
+        bool support_pscan_win_report;
+        bool supports_fw_dump;
+        u16 sd_blksz_fw_dl;
+        u8 rx_unit;
+        struct btmtk_private *priv;
+};
+
+struct btmtk_sdio_device {
+        const char *helper;
+        const char *firmware;
+        const char *firmware1;
+        const struct btmtk_sdio_card_reg *reg;
+        const bool support_pscan_win_report;
+        u16 sd_blksz_fw_dl;
+        bool supports_fw_dump;
+};
+#pragma pack(1)
+typedef struct _PATCH_HEADER {
+    u8 ucDateTime[16];
+    u8 ucPlatform[4];
+    u16 u2HwVer;
+    u16 u2SwVer;
+    u32 u4PatchVer;
+    u16 u2PatchStartAddr;/*Patch ram start address*/
+} PATCH_HEADER, *P_PATCH_HEADER;
+#pragma pack()
+
+#define HW_VERSION 0x80000000
+#define FW_VERSION 0x80000004
+
+/*common register address*/
+#define CHLPCR 0x0004
+#define CSDIOCSR 0x0008
+#define CHCR   0x000C
+#define CHISR  0x0010
+#define CHIER  0x0014
+#define CTDR   0x0018
+#define CRDR   0x001C
+
+/*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
+#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
+
+
+#define MTKSTP_HEADER_SIZE 0x0004
+
+#define MTK_SDIO_PACKET_HEADER_SIZE 4
+#define MTKDATA_HEADER_SIZE 10
+#define MTKWMT_HEADER_SIZE 4
+
+#define PATCH_DOWNLOAD_SIZE 1970
+
+#define DRIVER_OWN 0
+#define FW_OWN 1
+
+#define MTK_WMT_HEADER_LEN 4
+
+#define DEFAULE_PATCH_FRAG_SIZE    1000
+
+#define PATCH_IS_DOWNLOAD_BT_OTHER 0
+#define PATCH_READY 1
+#define PATCH_NEED_DOWNLOAD 2
+
+/*
+* data event:
+* return
+* 0:
+* patch download is not complete/get patch semaphore fail
+* 1:
+* patch download is complete by others
+* 2
+* patch download is not coplete
+* 3:(for debug)
+* release patch semaphore success
+*/
+
+/* Platform specific DMA alignment */
+#define BTSDIO_DMA_ALIGN                8
+
+/* Macros for Data Alignment : size */
+#define ALIGN_SZ(p, a)  \
+        (((p) + ((a) - 1)) & ~((a) - 1))
+
+/* Macros for Data Alignment : address */
+#define ALIGN_ADDR(p, a)        \
+        ((((unsigned long)(p)) + (((unsigned long)(a)) - 1)) & \
+                                        ~(((unsigned long)(a)) - 1))
+struct sk_buff *btmtk_create_send_data(struct sk_buff *skb);
+int btmtk_print_buffer_conent(u8 *buf, u32 Datalen);
+u32 lock_unsleepable_lock(P_OSAL_UNSLEEPABLE_LOCK pUSL);
+u32 unlock_unsleepable_lock(P_OSAL_UNSLEEPABLE_LOCK pUSL);
+
+extern unsigned char probe_counter;
+extern unsigned char *txbuf;
diff --git a/btmtk_usb_dbgfs.c b/btmtk_usb_dbgfs.c
new file mode 100644
index 0000000..56c0bc9
--- /dev/null
+++ b/btmtk_usb_dbgfs.c
@@ -0,0 +1,644 @@
+/*
+*  Copyright (c) 2016 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 the
+*  GNU General Public License for more details.
+*/
+#if defined(CONFIG_DEBUG_FS) && (CONFIG_DEBUG_FS == 1)
+
+#include <linux/debugfs.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/kref.h>
+#include <linux/semaphore.h>
+#include <linux/compiler.h>
+
+#include "btmtk_usb_dbgfs.h"
+#include "btmtk_usb_main.h"
+
+/**
+ * Directory define.,
+ * If this functionality enabled you should find specific file in
+ *  /sys/kernel/debug/BTDBG_ROOT/
+ */
+#define BTDBG_ROOT	"mtkbt"
+#define BTDBG_DEV	"bt_dev"
+#define BTDBG_TRACE	"log_lvl"
+
+#define FS_BUFFER_SIZE 2048
+#define MTKBT_MAX_HCI_FRAME_SIZE 1028	/* (BT_MAX_ACL_SIZE + 4) */
+
+struct BTDbg_Func_t {
+	const char *name;
+	int (*fp)(const char *value, void *dbg_filp);
+	const char *doc;
+	const char *help;
+};
+
+struct BTDbg_FW_Func_t {
+	const char *major;
+	const char *minor[DBG_FW_MAX_CMD_COUNT];
+	const char *cmd[DBG_FW_MAX_CMD_COUNT];
+	const char *doc[DBG_FW_MAX_CMD_COUNT];
+};
+
+/**
+ * Global variable
+ */
+static struct mtkdbg_data_t *g_mtkbt_dbg;
+u8 btmtk_log_lvl = BTMTK_LOG_LEVEL_DEFAULT;
+
+/**
+ * Function prototype
+ */
+static int btmtk_dbgfs_data_alloc(void *dbg_filp);
+static void btmtk_dbgfs_data_free(void *dbg_filp);
+static int btmtk_dbgfs_fw_func_main(const char *value, void *dbg_filp);
+static int btmtk_dbgfs_tx_frame(void *drv_data, const unsigned char *buffer, const unsigned int length);
+
+static int strtol_access_ok(char ch)
+{
+	if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F')
+		|| (ch >= 'a' && ch <= 'f'))
+		return 0;
+
+	BTUSB_ERR("wrong char_%c_", ch);
+
+	return -1;
+}
+
+static int str_to_u8(const char *ptr)
+{
+	u8 ret = 0;
+	u8 temp_str[3] = { 0 };
+	long res = 0;
+
+	temp_str[0] = *ptr;
+	temp_str[1] = *(ptr + 1);
+
+	ret = (u8) (kstrtol((char *)temp_str, 16, &res) & 0xff);
+
+	return res;
+}
+
+static int str_to_bytes(u8 *cmd_buf, const char *ptr)
+{
+	int i = 0, j = 0, len = 0;
+
+	len = strlen(ptr);
+
+	while (i < len) {
+		if (ptr[i] == ' ' || ptr[i] == '\t' || ptr[i] == '\r'
+			|| ptr[i] == '\n') {
+			i++;
+			continue;
+		}
+
+		if ((ptr[i] == '0' && ptr[i + 1] == 'x')
+			|| (ptr[0] == '0' && ptr[i + 1] == 'X')) {
+			i += 2;
+			continue;
+		}
+
+		if (strtol_access_ok(ptr[i]) != 0)
+			break;
+
+		cmd_buf[j++] = str_to_u8(&ptr[i]);
+		i += 2;
+	}
+
+	return j;
+}
+
+static int btmtk_dbgfs_tx_frame(void *drv_data, const unsigned char *buffer, const unsigned int length)
+{
+	int retval = -1;
+
+	BTUSB_DBG("%s: hci cmd", __func__);
+
+	if (unlikely(buffer == NULL || length <= 0)) {
+		BTUSB_ERR("%s: [hci_buf = %p][length = 0x%x]", __func__, buffer, length);
+		return -EFAULT;
+	}
+
+	if (drv_data != NULL)
+		BTUSB_DBG("%s: [drv_data=%p]", __func__, drv_data);
+
+
+	switch (buffer[0]) {
+	case MTK_HCI_COMMAND_PKT:
+		retval = btmtk_usb_meta_send_data(buffer, length);
+		break;
+
+	case MTK_HCI_ACLDATA_PKT:
+	case MTK_HCI_SCODATA_PKT:
+		retval = btmtk_usb_send_data(buffer, length);
+		break;
+
+	default:
+		BTUSB_ERR("%s: [INVALID PACKAGE TYPE = %d]", __func__, buffer[0]);
+		return -EILSEQ;
+	}
+
+	BTUSB_DBG("%s: retval = %d", __func__, retval);
+
+	return retval;
+}
+
+static int btmtk_dbgfs_tx_hci_data(const char *value, void *dbg_filp)
+{
+	int length = 0, ret = 0;
+	u8 *hci_cmd = NULL;
+	struct mtkdbg_data_t *mtkdbg_filp = (struct mtkdbg_data_t *)dbg_filp;
+
+	if (!mtkdbg_filp->hci_buf) {
+
+		BTUSB_ERR("%s: hci buff is null", __func__);
+		btmtk_dbgfs_data_alloc(mtkdbg_filp);
+
+		if (!mtkdbg_filp->hci_buf)
+			return -1;
+	}
+
+	hci_cmd = mtkdbg_filp->hci_buf;
+
+	length = str_to_bytes(hci_cmd, value);
+
+	BTUSB_DBG_RAW(hci_cmd, length, "%s: dump cmd:", __func__);
+
+	if (mtkdbg_filp->drv_data)
+		BTUSB_DBG("%s: [mtkdbg_filp->drv_data = %p]", __func__,
+			mtkdbg_filp->drv_data);
+
+	ret = btmtk_dbgfs_tx_frame(mtkdbg_filp->drv_data, hci_cmd, length);
+
+	BTUSB_DBG("%s: ret = %d", __func__, ret);
+
+	return length;
+}
+
+static struct BTDbg_Func_t dbgfs_func_list[] = {
+	{"HCI", btmtk_dbgfs_tx_hci_data, "tx hci data with type only",
+		"01 6F FC 05 01 02 01 00 08"},
+	{"FW", btmtk_dbgfs_fw_func_main, "set fw to do something", "DUMP"},
+	{"", NULL, NULL, NULL}
+};
+
+static struct BTDbg_FW_Func_t dbgfs_fw_func_list[] = {
+	{ "DUMP", {"1", "2"},
+		{"01 6F FC 05 01 02 01 00 08", "02 6F FC 05 01 02 01 00 08"},
+		{"tx fw assert cmd from endpoint 0", "tx fw assert cmd from endpoint 2"} },
+	{ NULL, {NULL, NULL}, {NULL, NULL}, {NULL, NULL} }
+};
+
+static void btmtk_dbgfs_func_usage(struct BTDbg_Func_t *funclp)
+{
+	int i;
+
+	BTUSB_DBG("Usage:\n\t <command>= value01 value02 ... ");
+	for (i = 0; funclp[i].fp; i++)
+		BTUSB_DBG("[%s]example:\n \t \t <%s>=<%s>", funclp[i].doc, funclp[i].name, funclp[i].help);
+
+	BTUSB_DBG("<command> should be less than DBG_TAG_MAX_LEN(0x%x) bytes",
+		DBG_TAG_MAX_LEN);
+	BTUSB_DBG("<value> should be less than FS_BUFFER_SIZE(0x%x) bytes",
+		FS_BUFFER_SIZE);
+}
+
+static void btmtk_dbgfs_fw_func_usage(struct BTDbg_FW_Func_t *funclp)
+{
+	int i, j;
+
+	BTUSB_DBG("Usage:\n\t <FW>= value01 value02 ...");
+	for (i = 0; funclp[i].major; i++) {
+		for (j = 0; j < DBG_FW_MAX_CMD_COUNT; j++) {
+			BTUSB_DBG("%s: example:\n \t \t \t \t <FW>=<%s><%s>",
+				__func__, funclp[i].major,
+				funclp[i].minor[j]);
+			BTUSB_DBG("%s: means:[%s]", __func__,
+				funclp[i].doc[j]);
+			BTUSB_DBG("%s: tx fw cmd [%s]", __func__,
+				funclp[i].cmd[j]);
+		}
+	}
+}
+
+static int btmtk_dbgfs_fw_func_main(const char *value, void *dbg_filp)
+{
+	int i;
+	u32 index = 0;
+	const char *s2 = value;
+
+	BTUSB_DBG("%s: [FW=%s]", __func__, value);
+
+	while (1) {
+		if (dbgfs_fw_func_list[index].major == NULL || value == NULL)
+			goto fw_exit;
+
+		s2 = strstr(value, dbgfs_fw_func_list[index].major);
+
+		if (s2 != NULL) {
+			for (i = 0; i < DBG_FW_MAX_CMD_COUNT; i++) {
+				if (dbgfs_fw_func_list[index].minor[i] ==
+					NULL)
+					continue;
+
+				if (strstr(s2, dbgfs_fw_func_list[index].minor[i]) != NULL)
+					break;
+			}
+
+			if (i < DBG_FW_MAX_CMD_COUNT)
+				break;
+		}
+		index++;
+	}
+
+	BTUSB_DBG("%s: match [cmd =%s]", __func__,
+		dbgfs_fw_func_list[index].cmd[i]);
+	btmtk_dbgfs_tx_hci_data(dbgfs_fw_func_list[index].cmd[i], dbg_filp);
+
+	return 0;
+
+fw_exit:
+	BTUSB_DBG("%s: [FW=Wrong cmd!] Fail", __func__);
+	btmtk_dbgfs_fw_func_usage(dbgfs_fw_func_list);
+	return -1;
+}
+
+static int btmtk_dbgfs_get_option(char *option)
+{
+	u32 index = 0;
+
+	while (1) {
+		if (strcmp(option, dbgfs_func_list[index].name) == 0)
+			return index;
+
+		if (dbgfs_func_list[index].fp == NULL)
+			return -1;
+
+		index++;
+	}
+}
+
+int BTDbg_Func_Entry(void *dbg_filp, u8 *tag, const char *value)
+{
+	int index;
+	int ret = 0;
+
+	if (tag == NULL)
+		goto exit;
+
+	index = btmtk_dbgfs_get_option(tag);
+
+	if (index == -1)
+		goto exit;
+
+	ret = dbgfs_func_list[index].fp(value, dbg_filp);
+	if (ret < 0) {
+		BTUSB_ERR("[tag = %s][value = %s], fail", tag, value);
+		return -1;
+	}
+
+	return 0;
+exit:
+	btmtk_dbgfs_func_usage(dbgfs_func_list);
+	return -1;
+}
+
+int btmtk_dbgfs_func_main(const char *value, void *dbg_filp)
+{
+	static int count_f;
+	int val_len = 0, i;
+	u8 Tag_param[DBG_TAG_MAX_LEN] = { 0 };
+	const char *val_param = NULL;
+
+	if (unlikely(dbg_filp == NULL || value == NULL)) {
+		BTUSB_ERR("%s: [flp =0x%p][value = 0x%p] fail", __func__,
+			dbg_filp, value);
+		return -1;
+	}
+
+	count_f++;
+
+	BTUSB_DBG("%s: in count = %d", __func__, count_f);
+
+	val_len = strlen(value);
+
+	val_len = val_len < DBG_TAG_MAX_LEN ? val_len : DBG_TAG_MAX_LEN;
+
+	for (i = 0; i < val_len; i++) {
+		if (value[i] != '=')
+			Tag_param[i] = value[i];
+		else
+			break;
+	}
+
+	if (i < val_len) {
+		val_param = &value[i + 1];
+		BTDbg_Func_Entry(dbg_filp, Tag_param, val_param);
+
+	} else {
+		btmtk_dbgfs_func_usage(dbgfs_func_list);
+	}
+
+	BTUSB_DBG("%s: out = %d", __func__, count_f);
+	return 0;
+}
+
+static int BTDbg_dev_open(struct inode *inode, struct file *file)
+{
+	struct mtkdbg_data_t *mtkdbg_filp = NULL;
+
+	BTUSB_DBG("%s: %s/%s open", __func__, BTDBG_ROOT, BTDBG_DEV);
+
+	file->private_data = inode->i_private;
+
+	mtkdbg_filp = (struct mtkdbg_data_t *)file->private_data;
+
+	if (mtkdbg_filp)
+		sema_init(&mtkdbg_filp->wr_lock, 1);
+
+	return 0;
+}
+
+static int BTDbg_dev_close(struct inode *inode, struct file *filp)
+{
+	BTUSB_DBG("[%s][%s]close", BTDBG_ROOT, BTDBG_DEV);
+
+	filp->private_data = NULL;
+
+	return 0;
+}
+
+ssize_t BTDbg_dev_write(struct file *filp, const char __user *buf,
+			size_t count, loff_t *f_pos)
+{
+	struct mtkdbg_data_t *mtkdbg_filp = (struct mtkdbg_data_t *)filp->private_data;
+
+	if (mtkdbg_filp == NULL) {
+
+		BTUSB_ERR("%s: no mtkbt dgb data", __func__);
+		return -ENODEV;
+	}
+
+	down(&mtkdbg_filp->wr_lock);
+
+	if (mtkdbg_filp->w_buf == NULL) {
+		BTUSB_ERR("%s: no w_buf", __func__);
+
+		btmtk_dbgfs_data_alloc((void *)mtkdbg_filp);
+
+		if (mtkdbg_filp->w_buf == NULL)
+			goto wt_exit;
+	}
+
+	memset(mtkdbg_filp->w_buf, 0, FS_BUFFER_SIZE);
+
+	if (unlikely(count > FS_BUFFER_SIZE)) {
+		BTUSB_WARN("%s: w count(%zu) > FS_BUFFER_SIZE(%d)", __func__, count, FS_BUFFER_SIZE);
+		count = FS_BUFFER_SIZE - 1;
+	}
+
+	if (count < FS_BUFFER_SIZE)
+		mtkdbg_filp->w_buf[count] = 0;
+
+	if (copy_from_user(mtkdbg_filp->w_buf, buf, count) != 0) {
+		BTUSB_ERR("[%s][%s]copy_from_user fail", BTDBG_ROOT,
+			BTDBG_DEV);
+		goto wt_exit;
+	}
+
+	BTUSB_DBG("%s: w_buf: %s", __func__, mtkdbg_filp->w_buf);
+
+#if 1
+	mtkdbg_filp->w_buf[count] = 0;
+	btmtk_dbgfs_func_main(mtkdbg_filp->w_buf, mtkdbg_filp);
+#else
+
+	io_len = strlen(mtkdbg_filp->w_buf);
+
+	if (io_len != count)
+		BTUSB_WARN("WARN:io_len != count");
+
+	target_len = io_len < 512 ? io_len : 512;
+
+	for (i = 0; i < 512; i++) {
+		if (mtkdbg_filp->w_buf[i] != '=')
+			parm_tag[i] = mtkdbg_filp->w_buf[i];
+		else
+			break;
+	}
+	if (i < 512) {
+		pValue = &mtkdbg_filp->w_buf[i + 1];
+		BTDbg_Func_Entry(parm_tag, pValue);
+	} else {
+		BTUSB_WARN("tag len should be less than 512");
+		goto wt_exit;
+	}
+#endif
+wt_exit:
+	up(&mtkdbg_filp->wr_lock);
+
+	return count;
+}
+
+const struct file_operations BTDbg_dev_fops = {
+	.open = BTDbg_dev_open,
+	.release = BTDbg_dev_close,
+	.write = BTDbg_dev_write
+};
+
+static int btmtk_dbgfs_data_alloc(void *dbg_filp)
+{
+	struct mtkdbg_data_t *mtkdbg_filp = (struct mtkdbg_data_t *)dbg_filp;
+
+	if (!mtkdbg_filp) {
+		BTUSB_ERR("%s: fail to alloc data for dbg file is null",
+			__func__);
+		return -1;
+	}
+
+	if (!mtkdbg_filp->btmtk_root_entery) {
+
+		BTUSB_ERR("%s: btmtk_root_entery is null", __func__);
+		return -1;
+	}
+
+	if (!mtkdbg_filp->btmtk_bt_dev_entry)
+		mtkdbg_filp->btmtk_bt_dev_entry =
+			debugfs_create_file(BTDBG_DEV, 0644,
+					mtkdbg_filp->btmtk_root_entery,
+					(void *)mtkdbg_filp, &BTDbg_dev_fops);
+
+	if (!mtkdbg_filp->btmtk_trace_entry)
+		mtkdbg_filp->btmtk_trace_entry =
+			debugfs_create_u8(BTDBG_TRACE, 0644,
+					mtkdbg_filp->btmtk_root_entery,
+					&btmtk_log_lvl);
+
+	if (mtkdbg_filp->btmtk_bt_dev_entry == NULL
+		|| mtkdbg_filp->btmtk_trace_entry == NULL) {
+
+		BTUSB_WARN("[%s = %p], [%s = %p]", BTDBG_DEV,
+			mtkdbg_filp->btmtk_bt_dev_entry, BTDBG_TRACE,
+			mtkdbg_filp->btmtk_trace_entry);
+
+	}
+	if (!mtkdbg_filp->w_buf)
+		mtkdbg_filp->w_buf = kmalloc(FS_BUFFER_SIZE, GFP_KERNEL);
+
+	if (!mtkdbg_filp->hci_buf)
+		mtkdbg_filp->hci_buf =
+			kmalloc(MTKBT_MAX_HCI_FRAME_SIZE, GFP_KERNEL);
+
+	if (mtkdbg_filp->w_buf == NULL || mtkdbg_filp->hci_buf == NULL) {
+		BTUSB_WARN("%s: [w_buf = %p][hci_buf = %p]", __func__,
+				mtkdbg_filp->w_buf, mtkdbg_filp->hci_buf);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void btmtk_dbgfs_data_free(void *dbg_filp)
+{
+	struct mtkdbg_data_t *mtkdbg_filp = (struct mtkdbg_data_t *)dbg_filp;
+
+	if (!mtkdbg_filp) {
+		BTUSB_ERR("%s: fail to free data for dbg file is null",
+			__func__);
+		return;
+	}
+
+	if (!mtkdbg_filp->btmtk_root_entery) {
+		BTUSB_ERR("%s: fail to free for root entry is null", __func__);
+		return;
+	}
+
+	debugfs_remove(mtkdbg_filp->btmtk_bt_dev_entry);
+	mtkdbg_filp->btmtk_bt_dev_entry = NULL;
+
+	debugfs_remove(mtkdbg_filp->btmtk_trace_entry);
+	mtkdbg_filp->btmtk_trace_entry = NULL;
+
+	kfree(mtkdbg_filp->w_buf);
+	mtkdbg_filp->w_buf = NULL;
+
+	kfree(mtkdbg_filp->hci_buf);
+	mtkdbg_filp->hci_buf = NULL;
+}
+
+void btmtk_dbgfs_set_drvdata(void *drv_data)
+{
+	struct mtkdbg_data_t *mtkdbg_filp = g_mtkbt_dbg;
+
+	BTUSB_DBG("%s: enter", __func__);
+
+	if (unlikely(drv_data == NULL)) {
+
+		BTUSB_ERR("%s: fail for drv_data is null", __func__);
+		return;
+	}
+
+	if (unlikely(mtkdbg_filp == NULL)) {
+		BTUSB_ERR("%s: no mtkbt dgb data", __func__);
+		return;
+	}
+
+	mtkdbg_filp->drv_data = drv_data;
+}
+
+void btmtk_dbgfs_remove_drvdata(void)
+{
+	struct mtkdbg_data_t *mtkdbg_filp = g_mtkbt_dbg;
+
+	BTUSB_DBG("%s: enter", __func__);
+
+	if (unlikely(mtkdbg_filp == NULL)) {
+		BTUSB_ERR("%s: no mtkbt dgb data", __func__);
+		return;
+	}
+
+	mtkdbg_filp->drv_data = NULL;
+}
+
+int btmtk_dbgfs_init(void)
+{
+	struct mtkdbg_data_t *mtkdbg_filp;
+
+	UNUSED(btmtk_usb_table);	/* clear warning */
+	BTUSB_DBG("%s", __func__);
+
+	if (g_mtkbt_dbg != NULL)
+		BTUSB_WARN("%s: mtkbt dbg is not null, memory leak !", __func__);
+
+	mtkdbg_filp = kzalloc(sizeof(struct mtkdbg_data_t), GFP_KERNEL);
+
+	if (!mtkdbg_filp) {
+		BTUSB_ERR("%s: alloc mtk dbg file fail!", __func__);
+		return -1;
+	}
+
+	mtkdbg_filp->btmtk_root_entery = debugfs_create_dir(BTDBG_ROOT, NULL);
+
+	if (mtkdbg_filp->btmtk_root_entery == NULL) {
+		BTUSB_ERR("%s: fail to create dbg root dir !", __func__);
+		kfree(mtkdbg_filp);
+		return -1;
+	}
+
+	sema_init(&mtkdbg_filp->wr_lock, 1);
+
+	g_mtkbt_dbg = mtkdbg_filp;
+
+	btmtk_dbgfs_data_alloc((void *)g_mtkbt_dbg);
+
+	BTUSB_DBG("%s: success", __func__);
+	return 0;
+}
+
+void btmtk_dbgfs_exit(void)
+{
+
+	if (!g_mtkbt_dbg) {
+		BTUSB_ERR("%s: fail to exit dbg file is null", __func__);
+		return;
+	}
+
+	btmtk_dbgfs_data_free((void *)g_mtkbt_dbg);
+
+	debugfs_remove(g_mtkbt_dbg->btmtk_root_entery);	/*(bt_dev_entry); */
+	g_mtkbt_dbg->btmtk_root_entery = NULL;
+
+	kfree(g_mtkbt_dbg);
+	g_mtkbt_dbg = NULL;
+
+}
+
+#else /* CONFIG_DEBUG_FS */
+void btmtk_dbgfs_set_drvdata(void *drv_data)
+{
+}
+
+void btmtk_dbgfs_remove_drvdata(void)
+{
+}
+
+int btmtk_dbgfs_init(void)
+{
+	BTUSB_INFO("%s: debugfs is not enable now", __func__);
+	return 0;
+}
+
+void btmtk_dbgfs_exit(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_FS */
diff --git a/btmtk_usb_dbgfs.h b/btmtk_usb_dbgfs.h
new file mode 100644
index 0000000..e7e81c9
--- /dev/null
+++ b/btmtk_usb_dbgfs.h
@@ -0,0 +1,46 @@
+/*
+*  Copyright (c) 2016 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 the
+*  GNU General Public License for more details.
+*/
+#ifndef __BTMTK_USB_DBGFS_H__
+#define __BTMTK_USB_DBGFS_H__
+
+#include "btmtk_define.h"
+
+#define DBG_TAG_MAX_LEN		 32
+#define DBG_FW_MAX_CMD_COUNT	2
+
+struct mtkdbg_data_t {
+	/**
+	 * kref_get(&kref);
+	 * kref_put(&kref, btusb_delete);
+	 * kref_init(&kref);
+	 */
+	struct kref		 kref;
+	struct dentry	*btmtk_root_entery;
+	struct dentry	*btmtk_bt_dev_entry;
+	struct dentry	*btmtk_trace_entry;
+	struct dentry	*btmtk_fifo_entry;
+	struct semaphore	wr_lock;
+	void		*drv_data;
+	u8		*w_buf;
+	u8		*hci_buf;
+};
+
+/**
+ * Extern functions
+ */
+void btmtk_dbgfs_set_drvdata(void *drv_data);
+void btmtk_dbgfs_remove_drvdata(void);
+int btmtk_dbgfs_init(void);
+void btmtk_dbgfs_exit(void);
+
+#endif /* __BTMTK_USB_DBGFS_H__ */
diff --git a/btmtk_usb_fifo.c b/btmtk_usb_fifo.c
new file mode 100644
index 0000000..6170a79
--- /dev/null
+++ b/btmtk_usb_fifo.c
@@ -0,0 +1,451 @@
+/*
+*  Copyright (c) 2016 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 the
+*  GNU General Public License for more details.
+*/
+
+#include "btmtk_define.h"
+
+#if ENABLE_BT_FIFO_THREAD
+#include <linux/kthread.h>
+#include <asm/uaccess.h>
+
+#include "btmtk_usb_fifo.h"
+#include "btmtk_usb_main.h"
+
+/**
+ * Definition
+ */
+#define FW_DUMP_SIZE (1024 * 16)
+#define SYS_LOG_SIZE (1024 * 16)
+
+#define KFIO_D(pData, t)	(((struct btmtk_fifo_data_t *)(pData))->fifo_l[t].kfifo.data)
+#define pKFIO(pData, t)		(&((struct btmtk_fifo_data_t *)(pData))->fifo_l[t].kfifo)
+#define pFIO(pData, t)		(&((struct btmtk_fifo_data_t *)(pData))->fifo_l[t])
+
+static struct btmtk_fifo_data_t g_fifo_data;
+
+static struct btmtk_fifo_t btmtk_fifo_list[] = {
+	{ FIFO_SYSLOG, SYS_LOG_SIZE, SYS_LOG_FILE_NAME, {0}, NULL, {0} },
+	{ FIFO_COREDUMP, FW_DUMP_SIZE, FW_DUMP_FILE_NAME, {0}, NULL, {0} },
+	{ FIFO_END, 0, NULL, {0}, NULL, {0} },
+	{ FIFO_BTBUF, 0, NULL, {0}, NULL, {0} }
+};
+
+static int fifo_alloc(struct btmtk_fifo_t *bt_fifo)
+{
+	int ret = 0;
+	struct __kfifo *kfio = &bt_fifo->kfifo;
+
+	ret = __kfifo_alloc(kfio, bt_fifo->size, 1, GFP_KERNEL);
+	if (ret == 0)
+		bt_fifo->size = kfio->mask + 1;
+
+	return ret;
+}
+
+static int fifo_init(struct btmtk_fifo_t *bt_fifo)
+{
+	int ret = 0;
+	struct __kfifo *kfio = &bt_fifo->kfifo;
+
+	BTUSB_INFO("%s: %d", __func__, bt_fifo->type);
+
+	ret = __kfifo_init(kfio, kfio->data, bt_fifo->size, kfio->esize);
+
+	if (bt_fifo->filp) {
+		BTUSB_INFO("%s: call filp_close", __func__);
+		vfs_fsync(bt_fifo->filp, 0);
+		filp_close(bt_fifo->filp, NULL);
+		bt_fifo->filp = NULL;
+		BTUSB_WARN("%s: FW dump file closed, rx=0x%X, tx =0x%x",
+				__func__, bt_fifo->stat.rx, bt_fifo->stat.tx);
+	}
+
+	bt_fifo->stat.rx = 0;
+	bt_fifo->stat.tx = 0;
+	return ret;
+}
+
+static void fifo_out_info(struct btmtk_fifo_data_t *data_p)
+{
+	struct __kfifo *fifo = NULL;
+
+	if (data_p == NULL) {
+		BTUSB_ERR("%s: data_p == NULL return", __func__);
+		return;
+	}
+
+	fifo = pKFIO(data_p, FIFO_SYSLOG);
+
+	if (fifo->data != NULL) {
+		if (fifo->in > fifo->out + fifo->mask)
+			BTUSB_WARN("sys_log buffer full");
+
+		if (fifo->in == fifo->out + fifo->mask)
+			BTUSB_WARN("sys_log buffer empty");
+
+		BTUSB_DBG("[syslog][fifo in - fifo out = 0x%x]",
+			fifo->in - fifo->out);
+	}
+
+	fifo = pKFIO(data_p, FIFO_COREDUMP);
+
+	if (fifo->data != NULL) {
+		if (fifo->in > fifo->out + fifo->mask)
+			BTUSB_WARN("coredump buffer full");
+
+		if (fifo->in == fifo->out + fifo->mask)
+			BTUSB_WARN("coredump buffer empty");
+	}
+}
+
+static u32 fifo_out_accessible(struct btmtk_fifo_data_t *data_p)
+{
+	int i;
+	struct btmtk_fifo_t *fio_p = NULL;
+
+	if (data_p == NULL)
+		return 0;
+
+	if (test_bit(TSK_SHOULD_STOP, &data_p->tsk_flag)) {
+		BTUSB_WARN("%s: task should stop", __func__);
+		return 1;
+	}
+
+	for (i = 0; i < FIFO_END; i++) {
+
+		fio_p = &data_p->fifo_l[i];
+
+		if (fio_p->type != i)
+			continue;
+
+		if (fio_p->kfifo.data != NULL) {
+			if (fio_p->kfifo.in != fio_p->kfifo.out)
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+static struct file *fifo_filp_open(const char *file, int flags, umode_t mode, unsigned int type)
+{
+	struct file *f = NULL;
+	/* Retry to open following path */
+	static const char * const sys[] = {
+		"/sdcard/"SYSLOG_FNAME, "/data/misc/bluedroid/"SYSLOG_FNAME, "/tmp/"SYSLOG_FNAME};
+	static const char * const fw[] = {
+		"/sdcard/"FWDUMP_FNAME, "/data/misc/bluedroid/"FWDUMP_FNAME, "/tmp/"SYSLOG_FNAME};
+	u8 i = 0;
+
+	f = filp_open(file, flags, mode);
+	if (IS_ERR(f)) {
+		const char *p = NULL;
+		u8 count = ARRAY_SIZE(sys);
+
+		f = NULL;
+		BTUSB_ERR("%s: open file error: %s, try to open others default path",
+				__func__, file);
+
+		if (type != FIFO_SYSLOG && type != FIFO_COREDUMP) {
+			BTUSB_ERR("%s: Incorrect type: %d", __func__, type);
+			return NULL;
+		}
+
+		for (i = 0; i < count; ++i) {
+			if (type == FIFO_SYSLOG)
+				p = sys[i];
+			else
+				p = fw[i];
+
+			if (memcmp(file, p, MIN(strlen(file), strlen(p))) != 0) {
+				BTUSB_INFO("%s: Try to open %s", __func__, p);
+				f = filp_open(p, flags, mode);
+				if (IS_ERR(f)) {
+					f = NULL;
+					continue;
+				}
+			} else {
+				continue;
+			}
+			BTUSB_INFO("%s: %s opened", __func__, p);
+			break;
+		}
+	}
+	return f;
+}
+
+static u32 fifo_out_to_file(struct btmtk_fifo_t *fio, u32 len, u32 off, u8 *end)
+{
+	struct __kfifo *fifo = &fio->kfifo;
+	unsigned int size = fifo->mask + 1;
+	unsigned int esize = fifo->esize;
+	unsigned int l;
+	unsigned char *dump_end = NULL;
+	mm_segment_t old_fs;
+
+	off &= fifo->mask;
+
+	if (esize != 1) {
+		off *= esize;
+		size *= esize;
+		len *= esize;
+	}
+	l = min(len, size - off);
+
+	if (l > 0 && fifo->data != NULL) {
+		old_fs = get_fs();
+		set_fs(KERNEL_DS);
+
+		if (fio->filp == NULL) {
+			BTUSB_WARN("%s: FW Dump started, open file %s", __func__,
+				fio->folder_name);
+
+			fio->filp = fifo_filp_open(fio->folder_name, O_RDWR | O_CREAT, 0644, fio->type);
+			if (IS_ERR(fio->filp)) {
+				set_fs(old_fs);
+				fio->filp = NULL;
+				return 0;
+			}
+		}
+
+		if (fio->filp != NULL) {
+			dump_end = strstr(fifo->data, FW_DUMP_END_EVENT);
+			fio->filp->f_op->write(fio->filp, fifo->data + off, l,
+					&fio->filp->f_pos);
+			fio->filp->f_op->write(fio->filp, fifo->data, len - l,
+					&fio->filp->f_pos);
+			fio->stat.tx += len;
+
+			if (dump_end && end) {
+				*end = 1;
+				BTUSB_INFO("%s: FW Dump finished(success)", __func__);
+			}
+		}
+		set_fs(old_fs);
+	}
+
+	/*
+	 * make sure that the data is copied before
+	 * incrementing the fifo->out index counter
+	 */
+	smp_wmb();
+	return len;
+}
+
+static u32 fifo_out_peek_fs(struct btmtk_fifo_t *fio, u32 len, u8 *end)
+{
+	unsigned int l;
+	struct __kfifo *fifo = &fio->kfifo;
+
+	l = fifo->in - fifo->out;
+	if (len > l)
+		len = l;
+
+	fifo_out_to_file(fio, len, fifo->out, end);
+
+	return len;
+}
+
+static u32 fifo_out_fs(void *fifo_d)
+{
+	u8 end = 0;
+	u32 len = 0;
+	struct btmtk_fifo_t *syslog_p = NULL;
+	struct btmtk_fifo_t *coredump_p = NULL;
+	struct btmtk_fifo_data_t *data_p = (void *)fifo_d;
+
+	if (fifo_d == NULL) {
+		BTUSB_ERR("[%s: fifo data is null]", __func__);
+		return len;
+	}
+
+	if (KFIO_D(fifo_d, FIFO_SYSLOG) != NULL) {
+		syslog_p = pFIO(fifo_d, FIFO_SYSLOG);
+		len = fifo_out_peek_fs(syslog_p, SYS_LOG_SIZE, &end);
+		syslog_p->kfifo.out += len;
+	}
+
+	if (KFIO_D(fifo_d, FIFO_COREDUMP) != NULL) {
+		coredump_p = pFIO(fifo_d, FIFO_COREDUMP);
+		len = fifo_out_peek_fs(coredump_p, FW_DUMP_SIZE, &end);
+		coredump_p->kfifo.out += len;
+	}
+
+	if (end) {
+		set_bit(TSK_SHOULD_STOP, &data_p->tsk_flag);
+		BTUSB_INFO("%s: [call btmtk_usb_toggle_rst_pinl]", __func__);
+		btmtk_usb_toggle_rst_pin();
+	}
+
+	return len;
+}
+
+static int btmtk_fifo_thread(void *ptr)
+{
+	struct btmtk_fifo_data_t *data_p = (struct btmtk_fifo_data_t *)ptr;
+
+	if (data_p == NULL) {
+		BTUSB_ERR("%s: [FIFO Data is null]", __func__);
+		return -1;
+	}
+
+	while (!kthread_should_stop() || test_bit(TSK_START, &data_p->tsk_flag)) {
+		wait_event_interruptible(data_p->rx_wait_q,
+					 fifo_out_accessible(data_p));
+		if (test_bit(TSK_SHOULD_STOP, &data_p->tsk_flag))
+			break;
+
+		fifo_out_info(data_p);
+		fifo_out_fs(data_p);
+		fifo_out_info(data_p);
+	}
+
+	set_bit(TSK_EXIT, &data_p->tsk_flag);
+	BTUSB_INFO("%s: end: down != 0", __func__);
+
+	return 0;
+}
+
+void *btmtk_fifo_init(void)
+{
+	int i;
+	struct btmtk_fifo_data_t *data_p = &g_fifo_data;
+	struct btmtk_fifo_t *fio_p = NULL;
+
+	UNUSED(btmtk_usb_table);	/* clear warning */
+	data_p->fifo_l = btmtk_fifo_list;
+
+	while (test_bit(TSK_RUNNING, &data_p->tsk_flag)) {
+		if (test_bit(TSK_EXIT, &data_p->tsk_flag))
+			break;
+
+		set_bit(TSK_SHOULD_STOP, &data_p->tsk_flag);
+		msleep(100);
+	}
+
+	data_p->tsk_flag = 0;
+
+	for (i = 0; i < FIFO_END; i++) {
+		fio_p = &(data_p->fifo_l[i]);
+		if (fio_p->type != i)
+			continue;
+
+		if (fio_p->kfifo.data == NULL)
+			fifo_alloc(fio_p);
+
+		fifo_init(fio_p);
+	}
+
+	init_waitqueue_head(&data_p->rx_wait_q);
+	set_bit(TSK_INIT, &data_p->tsk_flag);
+
+	return (void *)(data_p);
+}
+
+u32 btmtk_fifo_in(unsigned int type, void *fifo_d, const void *buf,
+			 unsigned int length)
+{
+	u32 len = 0;
+	u8 hci_pkt = MTK_HCI_EVENT_PKT;
+	struct btmtk_fifo_data_t *data_p = (struct btmtk_fifo_data_t *)fifo_d;
+	struct btmtk_fifo_t *fio_p = NULL;
+	struct __kfifo *fifo = NULL;
+
+	if (fifo_d == NULL) {
+		BTUSB_ERR("%s: [fifo data is null]", __func__);
+		return len;
+	}
+
+	if (!test_bit(TSK_START, &data_p->tsk_flag)) {
+		BTUSB_ERR("%s: [fail task not start ]", __func__);
+		return 0;
+	}
+
+	fio_p = pFIO(fifo_d, type);
+	fifo = pKFIO(fifo_d, type);
+
+	if (fifo->data != NULL) {
+		if (type == FIFO_SYSLOG) {
+			BTUSB_DBG("%s: [type == FIFO_SYSLOG ]", __func__);
+			len = __kfifo_in(pKFIO(fifo_d, type), (const void *)&hci_pkt,
+					sizeof(hci_pkt));
+
+			if (len != sizeof(hci_pkt))
+				return len;
+		}
+
+		len = __kfifo_in(pKFIO(fifo_d, type), buf, length);
+		fio_p->stat.rx += len;
+	}
+
+	if (len != 0)
+		wake_up_interruptible(&data_p->rx_wait_q);
+
+	return len;
+}
+
+int btmtk_fifo_start(void *fio_d)
+{
+	struct btmtk_fifo_data_t *data_p = (struct btmtk_fifo_data_t *)fio_d;
+	int err;
+
+	if (data_p == NULL) {
+		BTUSB_ERR("%s: [fail][fifo data is null]", __func__);
+		return -1;
+	}
+
+	if (!test_bit(TSK_INIT, &data_p->tsk_flag)) {
+		BTUSB_ERR("%s: [fail task not init ]", __func__);
+		return -1;
+	}
+
+	if (!KFIO_D(data_p, FIFO_SYSLOG) && !KFIO_D(data_p, FIFO_COREDUMP))
+		return -1;
+
+	data_p->fifo_tsk = kthread_create(btmtk_fifo_thread, fio_d,
+			"btmtk_fifo_thread");
+	if (IS_ERR(data_p->fifo_tsk)) {
+		BTUSB_ERR("%s: create FIFO thread failed!", __func__);
+		err = PTR_ERR(data_p->fifo_tsk);
+		data_p->fifo_tsk = NULL;
+		return -1;
+	}
+
+	set_bit(TSK_START, &data_p->tsk_flag);
+	BTUSB_INFO("%s: set TSK_START", __func__);
+	wake_up_process(data_p->fifo_tsk);
+	BTUSB_INFO("%s: [ok]", __func__);
+
+	return 0;
+}
+
+#else /* ENABLE_BT_FIFO_THREAD */
+void *btmtk_fifo_init(void)
+{
+	int *p = 0;
+
+	UNUSED(btmtk_usb_table);	/* clear warning */
+	return NULL;
+}
+
+int btmtk_fifo_start(void *fio_d)
+{
+	return 0;
+}
+
+u32 btmtk_fifo_in(unsigned int type, void *fifo_d, const void *buf,
+			 unsigned int length)
+{
+	return 0;
+}
+
+#endif /* ENABLE_BT_FIFO_THREAD */
diff --git a/btmtk_usb_fifo.h b/btmtk_usb_fifo.h
new file mode 100644
index 0000000..cfc8c49
--- /dev/null
+++ b/btmtk_usb_fifo.h
@@ -0,0 +1,64 @@
+/*
+*  Copyright (c) 2016 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 the
+*  GNU General Public License for more details.
+*/
+#ifndef __BTMTK_USB_FIFO_H__
+#define __BTMTK_USB_FIFO_H__
+
+#include <linux/kfifo.h>	/* Used for kfifo */
+
+#define FW_DUMP_END_EVENT "coredump end"
+
+enum {
+	FIFO_SYSLOG = 0,
+	FIFO_COREDUMP,
+	FIFO_BTBUF,
+	FIFO_END
+};
+
+enum {
+	TSK_INIT = 0,
+	TSK_START,
+	TSK_RUNNING,
+	TSK_SHOULD_STOP,
+	TSK_EXIT,
+};
+
+struct btfio_stats {
+	unsigned int rx;
+	unsigned int tx;
+};
+
+struct btmtk_fifo_t {
+	unsigned int		type;
+	unsigned int		size;
+	const char		*folder_name;
+	struct __kfifo		kfifo;
+	struct file		*filp;
+	struct btfio_stats	stat;
+};
+
+struct btmtk_fifo_data_t {
+	struct task_struct	*fifo_tsk;
+	struct btmtk_fifo_t	*fifo_l;
+	wait_queue_head_t	rx_wait_q;
+	unsigned long		tsk_flag;
+};
+
+/**
+ * Extern functions
+ */
+void *btmtk_fifo_init(void);
+int btmtk_fifo_start(void *fio_d);
+u32 btmtk_fifo_in(unsigned int type, void *fifo_d, const void *buf,
+		unsigned int length);
+
+#endif /* __BTMTK_USB_FIFO_H__ */
diff --git a/btmtk_usb_main.c b/btmtk_usb_main.c
new file mode 100644
index 0000000..1ea93fc
--- /dev/null
+++ b/btmtk_usb_main.c
@@ -0,0 +1,3809 @@
+/*
+*  Copyright (c) 2016 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 the
+*  GNU General Public License for more details.
+*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/completion.h>
+#include <linux/usb.h>
+#include <linux/version.h>
+#include <linux/firmware.h>
+#include <linux/skbuff.h>
+#include <linux/cdev.h>
+#include <linux/spinlock.h>
+#include <linux/kallsyms.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <asm/uaccess.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmtk_usb_main.h"
+#include "btmtk_usb_dbgfs.h"
+#include "btmtk_usb_fifo.h"
+
+/*============================================================================*/
+/* Local Configuration */
+/*============================================================================*/
+#define VERSION "6.0.16110301"
+
+/*============================================================================*/
+/* Function Prototype */
+/*============================================================================*/
+#define ___________________________________________________Function_Prototype
+static int btmtk_usb_standby(void);
+static void btmtk_usb_cap_init(void);
+static int btmtk_usb_BT_init(void);
+static void btmtk_usb_BT_exit(void);
+static int btmtk_usb_send_assert_cmd(void);
+static int btmtk_usb_send_assert_cmd_ctrl(void);
+static int btmtk_usb_send_assert_cmd_bulk(void);
+static int btmtk_usb_submit_intr_urb(void);
+static int btmtk_usb_submit_bulk_in_urb(void);
+static int btmtk_usb_send_hci_reset_cmd(void);
+static int btmtk_usb_send_woble_suspend_cmd(void);
+static void btmtk_usb_load_rom_patch_complete(struct urb *urb);
+static void btmtk_usb_bulk_in_complete(struct urb *urb);
+
+static void btmtk_usb_hci_snoop_print_to_log(void);
+static int btmtk_usb_get_rom_patch_result(void);
+static void btmtk_usb_tx_complete_meta(struct urb *urb);
+static int btmtk_usb_load_rom_patch(void);
+static int btmtk_usb_send_wmt_reset_cmd(void);
+static void btmtk_usb_early_suspend(void);
+static void btmtk_usb_late_resume(void);
+static void btmtk_usb_intr_complete(struct urb *urb);
+static int btmtk_usb_send_wmt_cmd(const u8 *cmd, const int cmd_len, const u8 *event,
+									const int event_len, u32 delay);
+static int btmtk_usb_send_hci_cmd(const u8 *cmd, int cmd_len, const u8 *event, int event_len);
+/** file_operations: stpbt */
+static int btmtk_usb_fops_open(struct inode *inode, struct file *file);
+static int btmtk_usb_fops_close(struct inode *inode, struct file *file);
+static ssize_t btmtk_usb_fops_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos);
+static ssize_t btmtk_usb_fops_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos);
+static unsigned int btmtk_usb_fops_poll(struct file *file, poll_table *wait);
+static long btmtk_usb_fops_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+static int btmtk_usb_fops_fasync(int fd, struct file *file, int on);
+static int btmtk_usb_fops_openfwlog(struct inode *inode, struct file *file);
+static int btmtk_usb_fops_closefwlog(struct inode *inode, struct file *file);
+static ssize_t btmtk_usb_fops_readfwlog(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
+static ssize_t btmtk_usb_fops_writefwlog(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
+static unsigned int btmtk_usb_fops_pollfwlog(struct file *filp, poll_table *wait);
+static long btmtk_usb_fops_unlocked_ioctlfwlog(struct file *filp, unsigned int cmd, unsigned long arg);
+
+#if SUPPORT_MT7662
+static int btmtk_usb_load_rom_patch_7662(void);
+static int btmtk_usb_io_read32_7662(u32 reg, u32 *val);
+static int btmtk_usb_io_write32_7662(u32 reg, u32 val);
+static int btmtk_usb_switch_iobase_7662(int base);
+static int btmtk_usb_chk_crc_7662(u32 checksum_len);
+static u16 btmtk_usb_get_crc_7662(void);
+static u16 btmtk_usb_checksum16_7662(u8 *pData, int len);
+static int btmtk_usb_send_hci_set_tx_power_cmd_7662(void);
+static int btmtk_usb_send_hci_radio_on_cmd_7662(void);
+static int btmtk_usb_send_hci_radio_off_cmd_7662(void);
+static int btmtk_usb_send_hci_set_ce_cmd_7662(void);
+static int btmtk_usb_send_hci_check_rom_patch_result_cmd_7662(void);
+static int btmtk_usb_send_hci_low_power_cmd_7662(bool enable);
+static void btmtk_usb_send_dummy_bulk_out_packet_7662(void);
+#endif /* SUPPORT_MT7662 */
+
+#if SUPPORT_MT7668
+static int btmtk_usb_load_rom_patch_7668(void);
+static int btmtk_usb_io_read32_7668(u32 reg, u32 *val);
+static int btmtk_usb_io_read32_7668(u32 reg, u32 *val);
+static int btmtk_usb_send_hci_tci_set_sleep_cmd_7668(void);
+static int btmtk_usb_send_wmt_power_on_cmd_7668(void);
+static int btmtk_usb_send_wmt_power_off_cmd_7668(void);
+#endif /* SUPPORT_MT7668 */
+
+/*============================================================================*/
+/* Global Variable */
+/*============================================================================*/
+#define _____________________________________________________Global_Variables
+/* stpbt character device for meta */
+#ifdef FIXED_STPBT_MAJOR_DEV_ID
+static int BT_major = FIXED_STPBT_MAJOR_DEV_ID;
+static int BT_majorfwlog = FIXED_STPBT_MAJOR_DEV_ID+1;
+#else
+static int BT_major;
+static int BT_majorfwlog;
+#endif
+static struct class *pBTClass;
+
+static struct device *pBTDev;
+static struct device *pBTDevfwlog;
+
+static struct cdev BT_cdev;
+static struct cdev BT_cdevfwlog;
+
+static struct usb_driver btmtk_usb_driver;
+static wait_queue_head_t inq;
+static struct btmtk_usb_data *g_data;
+static int probe_counter;
+static int need_reset_stack;
+static int leftHciEventSize;
+static int leftACLSize;
+static int btmtk_usb_state = BTMTK_USB_STATE_UNKNOWN;
+static int btmtk_fops_state = BTMTK_FOPS_STATE_UNKNOWN;
+static DECLARE_WAIT_QUEUE_HEAD(BT_wq);
+static DEFINE_MUTEX(btmtk_usb_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)
+static struct fasync_struct *fasync;
+
+typedef void (*register_early_suspend) (void (*f) (void));
+typedef void (*register_late_resume) (void (*f) (void));
+register_early_suspend register_early_suspend_func;
+register_late_resume register_late_resume_func;
+
+/* Hci Snoop */
+static u8 hci_cmd_snoop_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_BUF_SIZE];
+static u8 hci_cmd_snoop_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+
+static unsigned int hci_cmd_snoop_timestamp[HCI_SNOOP_ENTRY_NUM];
+static u8 hci_event_snoop_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_BUF_SIZE];
+static u8 hci_event_snoop_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+
+static unsigned int hci_event_snoop_timestamp[HCI_SNOOP_ENTRY_NUM];
+static u8 hci_acl_snoop_buf[HCI_SNOOP_ENTRY_NUM][HCI_SNOOP_BUF_SIZE];
+static u8 hci_acl_snoop_len[HCI_SNOOP_ENTRY_NUM] = { 0 };
+
+static unsigned int hci_acl_snoop_timestamp[HCI_SNOOP_ENTRY_NUM];
+static int hci_cmd_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+static int hci_event_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+static int hci_acl_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+
+static dev_t g_devIDfwlog;
+
+const struct file_operations BT_fops = {
+	.open = btmtk_usb_fops_open,
+	.release = btmtk_usb_fops_close,
+	.read = btmtk_usb_fops_read,
+	.write = btmtk_usb_fops_write,
+	.poll = btmtk_usb_fops_poll,
+	.unlocked_ioctl = btmtk_usb_fops_unlocked_ioctl,
+	.fasync = btmtk_usb_fops_fasync,
+};
+
+const struct file_operations BT_fopsfwlog = {
+	.open = btmtk_usb_fops_openfwlog,
+	.release = btmtk_usb_fops_closefwlog,
+	.read = btmtk_usb_fops_readfwlog,
+	.write = btmtk_usb_fops_writefwlog,
+	.poll = btmtk_usb_fops_pollfwlog,
+	.unlocked_ioctl = btmtk_usb_fops_unlocked_ioctlfwlog
+
+};
+
+/*============================================================================*/
+/* Internal Functions */
+/*============================================================================*/
+#define ___________________________________________________Internal_Functions
+static int btmtk_usb_init_memory(void)
+{
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: g_data is NULL !", __func__);
+		return -1;
+	}
+
+	g_data->chip_id = 0;
+	g_data->suspend_count = 0;
+
+	if (g_data->metabuffer)
+		memset(g_data->metabuffer->buffer, 0, META_BUFFER_SIZE);
+
+	if (g_data->io_buf)
+		memset(g_data->io_buf, 0, USB_IO_BUF_SIZE);
+
+	if (g_data->rom_patch_bin_file_name)
+		memset(g_data->rom_patch_bin_file_name, 0, MAX_BIN_FILE_NAME_LEN);
+
+	if (g_data->i_buf)
+		memset(g_data->i_buf, 0, BUFFER_SIZE);
+
+	if (g_data->o_buf)
+		memset(g_data->o_buf, 0, BUFFER_SIZE);
+	BTUSB_INFO("%s: Success", __func__);
+	return 1;
+}
+
+static int btmtk_usb_allocate_memory(void)
+{
+	if (g_data == NULL) {
+		g_data = kzalloc(sizeof(*g_data), GFP_KERNEL);
+		if (!g_data) {
+			BTUSB_ERR("%s: alloc memory fail (g_data)", __func__);
+			return -1;
+		}
+	}
+
+	if (g_data->metabuffer == NULL) {
+		g_data->metabuffer = kzalloc(sizeof(struct ring_buffer_struct), GFP_KERNEL);
+		if (!g_data->metabuffer) {
+			BTUSB_ERR("%s: alloc memory fail (g_data->metabuffer)",
+				__func__);
+			return -1;
+		}
+	}
+
+	if (g_data->io_buf == NULL) {
+		g_data->io_buf = kzalloc(USB_IO_BUF_SIZE, GFP_KERNEL);
+		if (!g_data->io_buf) {
+			BTUSB_ERR("%s: alloc memory fail (g_data->io_buf)",
+				__func__);
+			return -1;
+		}
+	}
+
+	if (g_data->rom_patch_bin_file_name == NULL) {
+		g_data->rom_patch_bin_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
+		if (!g_data->rom_patch_bin_file_name) {
+			BTUSB_ERR
+				("%s: alloc memory fail (g_data->rom_patch_bin_file_name)",
+				 __func__);
+			return -1;
+		}
+	}
+
+	if (g_data->i_buf == NULL) {
+		g_data->i_buf = kzalloc(BUFFER_SIZE, GFP_KERNEL);
+		if (!g_data->i_buf) {
+			BTUSB_ERR("%s: alloc memory fail (g_data->)", __func__);
+			return -1;
+		}
+	}
+
+	if (g_data->o_buf == NULL) {
+		g_data->o_buf = kzalloc(BUFFER_SIZE, GFP_KERNEL);
+		if (!g_data->o_buf) {
+			BTUSB_ERR("%s: alloc memory fail (g_data->o_buf)",
+				__func__);
+			return -1;
+		}
+	}
+	BTUSB_INFO("%s: Success", __func__);
+	return 1;
+}
+
+static void btmtk_usb_free_memory(void)
+{
+	if (!g_data) {
+		BTUSB_ERR("%s: g_data is NULL!\n", __func__);
+		return;
+	}
+
+	kfree(g_data->metabuffer);
+	g_data->metabuffer = NULL;
+
+	kfree(g_data->i_buf);
+	g_data->i_buf = NULL;
+
+	kfree(g_data->o_buf);
+	g_data->o_buf = NULL;
+
+	kfree(g_data->rom_patch_bin_file_name);
+	g_data->rom_patch_bin_file_name = NULL;
+
+	kfree(g_data->io_buf);
+	g_data->io_buf = NULL;
+
+	kfree(g_data->rom_patch_image);
+	g_data->rom_patch_image = NULL;
+
+	kfree(g_data);
+	g_data = NULL;
+
+	BTUSB_INFO("%s: Success", __func__);
+}
+
+static int btmtk_usb_get_state(void)
+{
+	return btmtk_usb_state;
+}
+
+static void btmtk_usb_set_state(int new_state)
+{
+	static const char * const state_msg[] = {
+		"UNKNOWN", "INIT", "DISCONNECT", "PROBE", "WORKING", "EARLY_SUSPEND",
+		"SUSPEND", "RESUME", "LATE_RESUME", "FW_DUMP", "SUSPEND_DISCONNECT",
+		"SUSPEND_PROBE", "RESUME_DISCONNECT", "RESUME_PROBE", "RESUME_FW_DUMP"
+	};
+
+	BTUSB_INFO("%s: %s(%d) -> %s(%d)", __func__, state_msg[btmtk_usb_state],
+			btmtk_usb_state, state_msg[new_state], new_state);
+	btmtk_usb_state = new_state;
+}
+
+static int btmtk_fops_get_state(void)
+{
+	return btmtk_fops_state;
+}
+
+static void btmtk_fops_set_state(int new_state)
+{
+	static const char * const fstate_msg[] = {"UNKNOWN", "INIT", "OPENED", "CLOSING", "CLOSED"};
+
+	BTUSB_INFO("%s: FOPS_%s(%d) -> FOPS_%s(%d)", __func__, fstate_msg[btmtk_fops_state],
+			btmtk_fops_state, fstate_msg[new_state], new_state);
+	btmtk_fops_state = new_state;
+}
+
+static unsigned int btmtk_usb_hci_snoop_get_microseconds(void)
+{
+	struct timeval now;
+
+	do_gettimeofday(&now);
+	return (now.tv_sec * 1000000 + now.tv_usec);
+}
+
+static void btmtk_usb_hci_snoop_init(void)
+{
+	int i;
+
+	hci_cmd_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+	hci_event_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+	hci_acl_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+	for (i = 0; i < HCI_SNOOP_ENTRY_NUM; i++) {
+		hci_cmd_snoop_len[i] = 0;
+		hci_event_snoop_len[i] = 0;
+		hci_acl_snoop_len[i] = 0;
+	}
+}
+
+static void btmtk_usb_hci_snoop_save_cmd(u32 len, u8 *buf)
+{
+	u32 copy_len = HCI_SNOOP_BUF_SIZE;
+
+	if (buf) {
+		if (len < HCI_SNOOP_BUF_SIZE)
+			copy_len = len;
+		hci_cmd_snoop_len[hci_cmd_snoop_index] = copy_len & 0xff;
+		memset(hci_cmd_snoop_buf[hci_cmd_snoop_index], 0, HCI_SNOOP_BUF_SIZE);
+		memcpy(hci_cmd_snoop_buf[hci_cmd_snoop_index], buf, copy_len & 0xff);
+		hci_cmd_snoop_timestamp[hci_cmd_snoop_index] = btmtk_usb_hci_snoop_get_microseconds();
+
+		hci_cmd_snoop_index--;
+		if (hci_cmd_snoop_index < 0)
+			hci_cmd_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+	}
+}
+
+static void btmtk_usb_hci_snoop_save_event(u32 len, u8 *buf)
+{
+	u32 copy_len = HCI_SNOOP_BUF_SIZE;
+
+	if (buf) {
+		if (len < HCI_SNOOP_BUF_SIZE)
+			copy_len = len;
+		hci_event_snoop_len[hci_event_snoop_index] = copy_len;
+		memset(hci_event_snoop_buf[hci_event_snoop_index], 0,
+			HCI_SNOOP_BUF_SIZE);
+		memcpy(hci_event_snoop_buf[hci_event_snoop_index], buf, copy_len);
+		hci_event_snoop_timestamp[hci_event_snoop_index] =
+			btmtk_usb_hci_snoop_get_microseconds();
+
+		hci_event_snoop_index--;
+		if (hci_event_snoop_index < 0)
+			hci_event_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+	}
+}
+
+static void btmtk_usb_hci_snoop_save_acl(u32 len, u8 *buf)
+{
+	u32 copy_len = HCI_SNOOP_BUF_SIZE;
+
+	if (buf) {
+		if (len < HCI_SNOOP_BUF_SIZE)
+			copy_len = len;
+		hci_acl_snoop_len[hci_acl_snoop_index] = copy_len & 0xff;
+		memset(hci_acl_snoop_buf[hci_acl_snoop_index], 0,
+			HCI_SNOOP_BUF_SIZE);
+		memcpy(hci_acl_snoop_buf[hci_acl_snoop_index], buf, copy_len & 0xff);
+		hci_acl_snoop_timestamp[hci_acl_snoop_index] =
+			btmtk_usb_hci_snoop_get_microseconds();
+
+		hci_acl_snoop_index--;
+		if (hci_acl_snoop_index < 0)
+			hci_acl_snoop_index = HCI_SNOOP_ENTRY_NUM - 1;
+	}
+}
+
+static void btmtk_usb_hci_snoop_print_to_log(void)
+{
+	int counter, index, j;
+
+	BTUSB_INFO("HCI Command Dump");
+	BTUSB_INFO("  index(len)(timestamp:us) :HCI Command");
+	index = hci_cmd_snoop_index + 1;
+	if (index >= HCI_SNOOP_ENTRY_NUM)
+		index = 0;
+	for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+		if (hci_cmd_snoop_len[index] > 0) {
+			pr_cont("	%d(%02d)(%u) :", counter,
+				hci_cmd_snoop_len[index],
+				hci_cmd_snoop_timestamp[index]);
+			for (j = 0; j < hci_cmd_snoop_len[index]; j++)
+				pr_cont("%02X ", hci_cmd_snoop_buf[index][j]);
+			pr_cont("\n");
+		}
+		index++;
+		if (index >= HCI_SNOOP_ENTRY_NUM)
+			index = 0;
+	}
+
+	BTUSB_INFO("HCI Event Dump");
+	BTUSB_INFO("  index(len)(timestamp:us) :HCI Event");
+	index = hci_event_snoop_index + 1;
+	if (index >= HCI_SNOOP_ENTRY_NUM)
+		index = 0;
+	for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+		if (hci_event_snoop_len[index] > 0) {
+			pr_cont("	%d(%02d)(%u) :", counter,
+				hci_event_snoop_len[index],
+				hci_event_snoop_timestamp[index]);
+			for (j = 0; j < hci_event_snoop_len[index]; j++)
+				pr_cont("%02X ", hci_event_snoop_buf[index][j]);
+			pr_cont("\n");
+		}
+		index++;
+		if (index >= HCI_SNOOP_ENTRY_NUM)
+			index = 0;
+	}
+
+	BTUSB_INFO("HCI ACL Dump");
+	BTUSB_INFO("  index(len)(timestamp:us) :ACL");
+	index = hci_acl_snoop_index + 1;
+	if (index >= HCI_SNOOP_ENTRY_NUM)
+		index = 0;
+	for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
+		if (hci_acl_snoop_len[index] > 0) {
+			pr_cont("	%d(%02d)(%u) :", counter,
+				hci_acl_snoop_len[index],
+				hci_acl_snoop_timestamp[index]);
+			for (j = 0; j < hci_acl_snoop_len[index]; j++)
+				pr_cont("%02X ", hci_acl_snoop_buf[index][j]);
+			pr_cont("\n");
+		}
+		index++;
+		if (index >= HCI_SNOOP_ENTRY_NUM)
+			index = 0;
+	}
+}
+
+static int btmtk_usb_send_assert_cmd(void)
+{
+	int ret = 0;
+
+	if (g_data->interrupt_urb_submitted == 0) {
+		ret = btmtk_usb_submit_bulk_in_urb();
+		if (ret < 0) {
+			BTUSB_ERR("%s: failed", __func__);
+			return -1;
+		}
+	}
+
+	ret = btmtk_usb_send_assert_cmd_ctrl();
+	if (ret < 0)
+		ret = btmtk_usb_send_assert_cmd_bulk();
+	if (ret < 0) {
+		BTUSB_ERR("%s: send assert cmd fail,tigger hw reset only",
+			__func__);
+		btmtk_usb_toggle_rst_pin();
+	}
+	return ret;
+}
+
+void btmtk_usb_toggle_rst_pin(void)
+{
+	BTUSB_INFO("%s: begig", __func__);
+	need_reset_stack = 1;
+
+	do {
+		typedef void (*pdwnc_func) (u8 fgReset);
+		char *pdwnc_func_name = "PDWNC_SetBTInResetState";
+		pdwnc_func pdwndFunc =
+			(pdwnc_func) kallsyms_lookup_name(pdwnc_func_name);
+
+		if (pdwndFunc) {
+			BTUSB_INFO("%s: Invoke %s(%d)", __func__,
+				pdwnc_func_name, 1);
+			pdwndFunc(1);
+		} else
+			BTUSB_INFO("%s: No Exported Func Found [%s]", __func__,
+				pdwnc_func_name);
+	} while (0);
+
+	btmtk_usb_hci_snoop_print_to_log();
+
+	BTUSB_INFO("%s: toggle GPIO %d", __func__, BT_DONGLE_RESET_GPIO_PIN);
+	do {
+		typedef void (*func_ptr) (unsigned gpio, int init_value);
+		char *func_name = "mtk_gpio_direction_output";
+		func_ptr pFunc = (func_ptr) kallsyms_lookup_name(func_name);
+
+		if (pFunc) {
+			BTUSB_INFO("%s: Invoke %s(%d)", __func__, func_name, 1);
+			pFunc(BT_DONGLE_RESET_GPIO_PIN, 0);
+			mdelay(500);
+			pFunc(BT_DONGLE_RESET_GPIO_PIN, 1);
+		} else
+			BTUSB_INFO("%s: No Exported Func Found [%s]", __func__,
+				func_name);
+
+	} while (0);
+	BTUSB_INFO("%s: end", __func__);
+}
+
+static void btmtk_usb_lock_unsleepable_lock(struct OSAL_UNSLEEPABLE_LOCK *pUSL)
+{
+	spin_lock_irqsave(&(pUSL->lock), pUSL->flag);
+}
+
+static void btmtk_usb_unlock_unsleepable_lock(struct OSAL_UNSLEEPABLE_LOCK *pUSL)
+{
+	spin_unlock_irqrestore(&(pUSL->lock), pUSL->flag);
+}
+
+static void btmtk_usb_load_code_from_bin(u8 **image, char *bin_name,
+					 struct device *dev, u32 *code_len)
+{
+	const struct firmware *fw_entry;
+	int err = 0;
+	static u32 chip_id;
+
+	if (g_data->rom_patch_image && g_data->rom_patch_image_len
+			&& g_data->chip_id == chip_id) {
+		/* no need to request firmware again. */
+		*image = g_data->rom_patch_image;
+		*code_len = g_data->rom_patch_image_len;
+		return;
+
+	} else {
+		chip_id = g_data->chip_id;
+		kfree(g_data->rom_patch_image);
+		g_data->rom_patch_image = NULL;
+		g_data->rom_patch_image_len = 0;
+	}
+
+	err = request_firmware(&fw_entry, bin_name, dev);
+	if (err != 0) {
+		*image = NULL;
+		BTUSB_ERR("%s: request_firmware fail!! error code = %d",
+			__func__, err);
+		return;
+	}
+
+	*image = kmalloc(fw_entry->size, GFP_KERNEL);
+	if (*image == NULL) {
+		BTUSB_ERR("%s: kmalloc failed!! error code = %d",
+			__func__, err);
+		return;
+	}
+
+	memcpy(*image, fw_entry->data, fw_entry->size);
+	*code_len = fw_entry->size;
+
+	g_data->rom_patch_image = *image;
+	g_data->rom_patch_image_len = *code_len;
+
+	release_firmware(fw_entry);
+}
+
+static void btmtk_usb_stop_traffic(void)
+{
+	if ((g_data->bulk_urb_submitted == 0) &&
+		(g_data->interrupt_urb_submitted == 0)) {
+		return;
+	}
+
+	BTUSB_INFO("%s: start", __func__);
+	usb_kill_anchored_urbs(&g_data->intr_anchor);
+	usb_kill_anchored_urbs(&g_data->bulk_anchor);
+	g_data->bulk_urb_submitted = 0;
+	g_data->interrupt_urb_submitted = 0;
+
+	BTUSB_INFO("%s: end", __func__);
+}
+
+static void btmtk_usb_waker(struct work_struct *work)
+{
+	int err;
+
+	err = usb_autopm_get_interface(g_data->intf);
+
+	if (err < 0)
+		return;
+
+	usb_autopm_put_interface(g_data->intf);
+}
+
+static int btmtk_usb_submit_intr_urb(void)
+{
+	struct urb *urb;
+	u8 *buf;
+	unsigned int pipe;
+	int err, size;
+
+	BTUSB_INFO("%s: begin", __func__);
+
+	if (g_data->interrupt_urb_submitted) {
+		BTUSB_WARN("%s: already submitted", __func__);
+		return 0;
+	}
+	g_data->interrupt_urb_submitted = 0;
+
+	if (!g_data->intr_ep) {
+		BTUSB_ERR("%s: error 1", __func__);
+		return -ENODEV;
+	}
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		BTUSB_ERR("%s: error 2", __func__);
+		return -ENOMEM;
+	}
+	/* size = le16_to_cpu(g_data->intr_ep->wMaxPacketSize); */
+	size = le16_to_cpu(HCI_MAX_EVENT_SIZE);
+	BTUSB_INFO("%s: maximum packet size:%d", __func__, size);
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf) {
+		usb_free_urb(urb);
+		BTUSB_ERR("%s: error 3", __func__);
+		return -ENOMEM;
+	}
+
+	pipe = usb_rcvintpipe(g_data->udev, g_data->intr_ep->bEndpointAddress);
+
+	usb_fill_int_urb(urb, g_data->udev, pipe, buf, size,
+			 btmtk_usb_intr_complete, g_data,
+			 g_data->intr_ep->bInterval);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_anchor_urb(urb, &g_data->intr_anchor);
+
+	err = usb_submit_urb(urb, GFP_KERNEL);
+	if (err < 0) {
+		if (err != -EPERM && err != -ENODEV)
+			BTUSB_ERR("%s: urb %p submission failed (%d)", __func__,
+				urb, -err);
+		usb_unanchor_urb(urb);
+	} else {
+		g_data->interrupt_urb_submitted = 1;
+	}
+
+	usb_free_urb(urb);
+	BTUSB_INFO("%s: end", __func__);
+	return err;
+}
+
+static int btmtk_usb_submit_bulk_in_urb(void)
+{
+	struct urb *urb = NULL;
+	u8 *buf = NULL;
+	unsigned int pipe = 0;
+	int err = 0;
+	int size = HCI_MAX_FRAME_SIZE;
+
+	BTUSB_INFO("%s: begin", __func__);
+
+	if (g_data->bulk_urb_submitted) {
+		BTUSB_WARN("%s: already submitted", __func__);
+		return 0;
+	}
+	g_data->bulk_urb_submitted = 0;
+
+	if (!g_data->bulk_rx_ep) {
+		BTUSB_ERR("%s: end error 1", __func__);
+		return -ENODEV;
+	}
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb) {
+		BTUSB_ERR("%s: end error 2", __func__);
+		return -ENOMEM;
+	}
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (!buf) {
+		usb_free_urb(urb);
+		BTUSB_ERR("%s: end error 3", __func__);
+		return -ENOMEM;
+	}
+	pipe =
+		usb_rcvbulkpipe(g_data->udev, g_data->bulk_rx_ep->bEndpointAddress);
+	usb_fill_bulk_urb(urb, g_data->udev, pipe, buf, size,
+			btmtk_usb_bulk_in_complete, g_data);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_mark_last_busy(g_data->udev);
+	usb_anchor_urb(urb, &g_data->bulk_anchor);
+
+	err = usb_submit_urb(urb, GFP_KERNEL);
+	if (err < 0) {
+		if (err != -EPERM && err != -ENODEV)
+			BTUSB_ERR("%s: urb %p submission failed (%d)", __func__,
+				urb, -err);
+		usb_unanchor_urb(urb);
+	} else {
+		g_data->bulk_urb_submitted = 1;
+	}
+
+	usb_free_urb(urb);
+	BTUSB_INFO("%s: end", __func__);
+	return err;
+}
+
+static int btmtk_usb_send_wmt_cmd(const u8 *cmd, const int cmd_len,
+		const u8 *event, const int event_len, u32 delay)
+{
+	int ret = -1;	/* if successful, read length */
+	int i = 0;
+	bool check = FALSE;
+
+	if (g_data == NULL || g_data->udev == NULL || g_data->io_buf == NULL
+			|| cmd == NULL) {
+		BTUSB_ERR("%s: incorrect cmd pointer", __func__);
+		return ret;
+	}
+	if (event != NULL && event_len > 0)
+		check = TRUE;
+
+	/* send WMT command */
+	ret = usb_control_msg(g_data->udev, usb_sndctrlpipe(g_data->udev, 0),
+			0x01, DEVICE_CLASS_REQUEST_OUT, 0x30, 0x00, (void *)cmd, cmd_len,
+			USB_CTRL_IO_TIMO);
+	if (ret < 0) {
+		BTUSB_ERR("%s: command send failed(%d)", __func__, ret);
+		return ret;
+	}
+
+	/* ms delay */
+	mdelay(delay);
+
+	if (event_len == -1) {
+		/**
+		 * If event_len == -1, DO NOT read event, since FW wouldn't feedback
+		 */
+		return 0;
+	}
+
+	/* check WMT event */
+	memset(g_data->io_buf, 0, USB_IO_BUF_SIZE);
+	ret = usb_control_msg(g_data->udev, usb_rcvctrlpipe(g_data->udev, 0),
+				0x01, DEVICE_VENDOR_REQUEST_IN, 0x30, 0x00, g_data->io_buf,
+				USB_IO_BUF_SIZE, USB_CTRL_IO_TIMO);
+	if (ret < 0) {
+		BTUSB_ERR("%s: event get failed(%d)", __func__, ret);
+		if (check == TRUE)
+			return ret;
+		else
+			return 0;	/* Do not ask read so return 0 */
+	}
+
+	if (check == TRUE) {
+		/* maybe don't care some returned parameters */
+		if (ret >= event_len) {
+			for (i = 0; i < event_len; i++) {
+				if (event[i] != g_data->io_buf[i])
+					break;
+			}
+		} else {
+			BTUSB_ERR("%s: event length is not match(%d/%d)", __func__, ret, event_len);
+		}
+		if (i != event_len) {
+			BTUSB_ERR("%s: got unknown event:(%d/%d)", __func__, i, event_len);
+			pr_cont("\t");
+			for (i = 0; i < ret && i < 64; i++)
+				pr_cont("%02X ", g_data->io_buf[i]);
+			pr_cont("\n");
+		} else {
+			return ret; /* return read length */
+		}
+	}
+	return -1;
+}
+
+static int btmtk_usb_send_hci_cmd(const u8 *cmd, const int cmd_len,
+		const u8 *event, const int event_len)
+{
+	int ret = -1;	/* if successful, read length */
+	int len = 0;
+	int i = 0;
+	unsigned long timo = 0;
+	bool check = FALSE;
+
+	/* parameter check */
+	if (g_data == NULL || g_data->udev == NULL || g_data->io_buf == NULL
+			|| cmd == NULL) {
+		BTUSB_ERR("%s: incorrect cmd pointer", __func__);
+		return ret;
+	}
+	if (event != NULL && event_len > 0)
+		check = TRUE;
+
+	/* need get event by interrupt, stop traffic before cmd send */
+	btmtk_usb_stop_traffic();
+
+	/* send HCI command */
+	ret = usb_control_msg(g_data->udev, usb_sndctrlpipe(g_data->udev, 0),
+			0, DEVICE_CLASS_REQUEST_OUT, 0, 0,
+			(void *)cmd, cmd_len, USB_CTRL_IO_TIMO);
+	if (ret < 0) {
+		BTUSB_ERR("%s: command send failed(%d)", __func__, ret);
+		return ret;
+	}
+
+	if (event_len == -1) {
+		/**
+		 * If event_len == -1, DO NOT read event, since FW wouldn't feedback
+		 */
+		return 0;
+	}
+
+	/* check HCI event */
+	timo = jiffies + msecs_to_jiffies(USB_INTR_MSG_TIMO);
+	do {
+		memset(g_data->io_buf, 0, USB_IO_BUF_SIZE);
+		ret = usb_interrupt_msg(g_data->udev, usb_rcvintpipe(g_data->udev, 1),
+				g_data->io_buf, USB_IO_BUF_SIZE, &len, USB_INTR_MSG_TIMO);
+		if (ret < 0) {
+			BTUSB_ERR("%s: event get failed(%d)", __func__, ret);
+			if (check == TRUE)
+				return ret;
+			else
+				return 0;	/* Do not ask read so return 0 */
+		}
+
+		if (check == TRUE) {
+			/* maybe don't care some returned parameters */
+			if (len >= event_len) {
+				for (i = 0; i < event_len; i++) {
+					if (event[i] != g_data->io_buf[i])
+						break;
+				}
+			} else {
+				BTUSB_ERR("%s: event length is not match(%d/%d)", __func__, len, event_len);
+			}
+			if (i != event_len) {
+				BTUSB_ERR("%s: got unknown event:(%d/%d)", __func__, i, event_len);
+				pr_cont("\t");
+				for (i = 0; i < len && i < 64; i++)
+					pr_cont("%02X ", g_data->io_buf[i]);
+				pr_cont("\n");
+			} else {
+				return len; /* actually read length */
+			}
+		}
+	} while (time_before(jiffies, timo));
+
+	BTUSB_ERR("%s: error, got event timeout, jiffies = %lu", __func__, jiffies);
+	return -1;
+}
+
+int btmtk_usb_meta_send_data(const u8 *buffer, const unsigned int length)
+{
+	int ret = 0;
+
+	if (buffer[0] != 0x01) {
+		BTUSB_WARN("the data from meta isn't HCI command, value: 0x%X",
+			buffer[0]);
+	} else {
+		u8 *buf = (u8 *)buffer;
+
+		ret =
+			usb_control_msg(g_data->udev,
+					usb_sndctrlpipe(g_data->udev, 0), 0x0,
+					DEVICE_CLASS_REQUEST_OUT, 0x00, 0x00,
+					buf + 1, length - 1,
+					USB_CTRL_IO_TIMO);
+	}
+
+	if (ret < 0) {
+		BTUSB_ERR("%s: error1(%d)", __func__, ret);
+		return ret;
+	}
+
+	return length;
+}
+
+int btmtk_usb_send_data(const u8 *buffer, const unsigned int length)
+{
+	struct urb *urb;
+	unsigned int pipe;
+	int err;
+	int send_data_len = length - 1;
+	char *buf = NULL;
+
+	if (!g_data->bulk_tx_ep) {
+		BTUSB_ERR("%s: No bulk_tx_ep", __func__);
+		return -ENODEV;
+	}
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+
+	if (!urb) {
+		BTUSB_ERR("%s: No memory", __func__);
+		return -ENOMEM;
+	}
+
+	if (buffer[0] == 0x02) {
+
+		while (g_data->meta_tx != 0)
+			udelay(500);
+
+		g_data->meta_tx = 1;
+
+		buf = usb_alloc_coherent(g_data->udev, send_data_len, GFP_KERNEL, &urb->transfer_dma);
+
+		urb->transfer_buffer = buf;
+		urb->transfer_buffer_length = send_data_len;
+
+		if (!buf) {
+			BTUSB_ERR("%s: usb_alloc_coherent error\n", __func__);
+			err = -ENOMEM;
+			goto error_buffer;
+		}
+		/* remove the ACL header:0x02 */
+		memcpy(buf, buffer + 1, send_data_len);
+
+		pipe =
+			usb_sndbulkpipe(g_data->udev,
+					g_data->bulk_tx_ep->bEndpointAddress);
+
+		usb_fill_bulk_urb(urb, g_data->udev, pipe, buf,
+				length - 1, btmtk_usb_tx_complete_meta,
+				g_data);
+
+		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+		usb_anchor_urb(urb, &g_data->tx_anchor);
+
+		err = usb_submit_urb(urb, GFP_KERNEL);
+		if (err == 0)
+			err = length;
+		else
+			BTUSB_ERR("send ACL data fail, error:%d", err);
+		mdelay(1);
+	} else if (buffer[0] == 0x03) {
+		BTUSB_WARN("%s: Iso not supported", __func__);
+		err = -1;
+	} else {
+		BTUSB_WARN("%s: unknown data", __func__);
+		err = -1;
+	}
+
+error_buffer:
+	if (err < 0) {
+		BTUSB_ERR("%s: urb %p submission failed (%d)", __func__, urb,
+			-err);
+
+		usb_free_coherent(g_data->udev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma);
+		kfree(urb->setup_packet);
+		usb_unanchor_urb(urb);
+	} else {
+		usb_mark_last_busy(g_data->udev);
+	}
+
+	usb_free_urb(urb);
+	return err;
+
+}
+
+static int btmtk_usb_BT_init(void)
+{
+	dev_t devID = MKDEV(BT_major, 0);
+	dev_t devIDfwlog = MKDEV(BT_majorfwlog, 1);
+	int ret = 0;
+	int cdevErr = 0;
+	int major = 0;
+	int majorfwlog = 0;
+
+	BTUSB_INFO("%s: begin", __func__);
+	BTUSB_INFO("%s: devID %d, devIDfwlog %d", __func__, devID, devIDfwlog);
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	btmtk_usb_set_state(BTMTK_USB_STATE_INIT);
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+#ifdef FIXED_STPBT_MAJOR_DEV_ID
+	ret = register_chrdev_region(devID, 1, "BT_chrdev");
+	if (ret) {
+		BTUSB_ERR("fail to register_chrdev %d, allocate chrdev again", MAJOR(devID));
+		ret = alloc_chrdev_region(&devID, 0, 1, "BT_chrdev");
+		if (ret) {
+			BTUSB_ERR("fail to allocate chrdev");
+			return ret;
+		}
+	}
+
+	ret = register_chrdev_region(devIDfwlog, 1, "BT_chrdevfwlog");
+	if (ret) {
+		BTUSB_ERR("fail to register_chrdev %d, allocate chrdev again", MAJOR(devIDfwlog));
+		ret = alloc_chrdev_region(&devIDfwlog, 0, 1, "BT_chrdevfwlog");
+		if (ret) {
+			BTUSB_ERR("fail to allocate chrdev fwlog");
+			return ret;
+		}
+	}
+#else
+	ret = alloc_chrdev_region(&devID, 0, 1, "BT_chrdev");
+	if (ret) {
+		BTUSB_ERR("fail to allocate chrdev");
+		return ret;
+	}
+
+	ret = alloc_chrdev_region(&devIDfwlog, 0, 1, "BT_chrdevfwlog");
+	if (ret) {
+		BTUSB_ERR("fail to allocate chrdev");
+		return ret;
+	}
+#endif
+
+	BT_major = major = MAJOR(devID);
+	BTUSB_INFO("%s: major number: %d", __func__, BT_major);
+	BT_majorfwlog = majorfwlog = MAJOR(devIDfwlog);
+	BTUSB_INFO("%s: BT_majorfwlog number: %d", __func__, BT_majorfwlog);
+
+	cdev_init(&BT_cdev, &BT_fops);
+	BT_cdev.owner = THIS_MODULE;
+
+	cdev_init(&BT_cdevfwlog, &BT_fopsfwlog);
+	BT_cdevfwlog.owner = THIS_MODULE;
+	cdevErr = cdev_add(&BT_cdev, devID, 1);
+	if (cdevErr)
+		goto error;
+
+	cdevErr = cdev_add(&BT_cdevfwlog, devIDfwlog, 1);
+	if (cdevErr)
+		goto error;
+	BTUSB_INFO("%s: %s driver(major %d) installed.", __func__, "BT_chrdev",
+		BT_major);
+
+	BTUSB_INFO("%s: %s driver(major %d) installed.", __func__, "BT_chrdevfwlog",
+		BT_majorfwlog);
+	pBTClass = class_create(THIS_MODULE, "BT_chrdev");
+	if (IS_ERR(pBTClass)) {
+		BTUSB_ERR("class create fail, error code(%ld)",
+			PTR_ERR(pBTClass));
+		goto err1;
+	}
+
+	pBTDev = device_create(pBTClass, NULL, devID, NULL, "stpbt");
+	if (IS_ERR(pBTDev)) {
+		BTUSB_ERR("device create fail, error code(%ld)",
+			PTR_ERR(pBTDev));
+		goto err2;
+	}
+
+	pBTDevfwlog = device_create(pBTClass, NULL, devIDfwlog, NULL, "stpbtfwlog");
+	if (IS_ERR(pBTDevfwlog)) {
+		BTUSB_ERR("device(stpbtfwlog) create fail, error code(%ld)",
+			PTR_ERR(pBTDevfwlog));
+		goto err2;
+	}
+
+	BTUSB_INFO("%s: BT_major %d, BT_majorfwlog %d", __func__, BT_major, BT_majorfwlog);
+	BTUSB_INFO("%s: devID %d, devIDfwlog %d", __func__, devID, devIDfwlog);
+	g_devIDfwlog = devIDfwlog;
+	FOPS_MUTEX_LOCK();
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_INIT);
+	FOPS_MUTEX_UNLOCK();
+
+	/* init wait queue */
+	init_waitqueue_head(&(inq));
+
+	/* register system power off callback function. */
+	do {
+		typedef void (*func_ptr) (int (*f) (void));
+		char *func_name = "RegisterPdwncCallback";
+		func_ptr pFunc = (func_ptr) kallsyms_lookup_name(func_name);
+
+		if (pFunc) {
+			BTUSB_INFO("%s: Register Pdwnc callback success.",
+				__func__);
+			pFunc(&btmtk_usb_standby);
+		} else
+			BTUSB_WARN
+				("%s: No Exported Func Found [%s], just skip!",
+				 __func__, func_name);
+	} while (0);
+
+	/* register early_suspend / late_reasume callback function. */
+	do {
+		char *register_early_suspend_func_name =
+			"RegisterEarlySuspendNotification";
+		char *register_late_resume_func_name =
+			"RegisterLateResumeNotification";
+
+		register_early_suspend_func = (register_early_suspend)
+			kallsyms_lookup_name(register_early_suspend_func_name);
+		register_late_resume_func = (register_late_resume)
+			kallsyms_lookup_name(register_late_resume_func_name);
+
+		if (register_early_suspend_func && register_late_resume_func) {
+			BTUSB_INFO
+				("%s: Register early suspend/late resume nitofication success.",
+				 __func__);
+			register_early_suspend_func(&btmtk_usb_early_suspend);
+			register_late_resume_func(&btmtk_usb_late_resume);
+		} else {
+			BTUSB_WARN
+				("%s: No Exported Func Found [%s], just skip!",
+				 __func__, register_late_resume_func_name);
+		}
+	} while (0);
+
+	/* allocate buffers. */
+	if (btmtk_usb_allocate_memory() < 0) {
+		BTUSB_ERR("%s: allocate memory failed!", __func__);
+		goto err2;
+	}
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	btmtk_usb_set_state(BTMTK_USB_STATE_DISCONNECT);
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	BTUSB_INFO("%s: end", __func__);
+	return 0;
+
+err2:
+	if (pBTClass) {
+		class_destroy(pBTClass);
+		pBTClass = NULL;
+	}
+
+	btmtk_usb_free_memory();
+
+err1:
+error:
+	if (cdevErr == 0)
+		cdev_del(&BT_cdev);
+
+	if (ret == 0)
+		unregister_chrdev_region(devID, 1);
+	BTUSB_ERR("%s: error", __func__);
+	return -1;
+}
+
+static void btmtk_usb_BT_exit(void)
+{
+	dev_t dev = MKDEV(BT_major, 0);
+	dev_t devIDfwlog = g_devIDfwlog;/*MKDEV(BT_majorfwlog, 0);*/
+
+	BTUSB_INFO("%s: BT_major %d, BT_majorfwlog %d", __func__, BT_major, BT_majorfwlog);
+	BTUSB_INFO("%s: dev %d, devIDfwlog %d", __func__, dev, devIDfwlog);
+
+	FOPS_MUTEX_LOCK();
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_UNKNOWN);
+	FOPS_MUTEX_UNLOCK();
+
+	if (pBTDevfwlog) {
+		device_destroy(pBTClass, devIDfwlog);
+		pBTDevfwlog = NULL;
+	}
+
+	if (pBTDev) {
+		device_destroy(pBTClass, dev);
+		pBTDev = NULL;
+	}
+
+	if (pBTClass) {
+		class_destroy(pBTClass);
+		pBTClass = NULL;
+	}
+
+	cdev_del(&BT_cdevfwlog);
+	unregister_chrdev_region(devIDfwlog, 1);
+
+	cdev_del(&BT_cdev);
+	unregister_chrdev_region(dev, 1);
+
+	btmtk_usb_free_memory();
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	btmtk_usb_set_state(BTMTK_USB_STATE_UNKNOWN);
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	BTUSB_INFO("%s: BT_chrdev driver removed.", __func__);
+}
+
+static int btmtk_usb_handle_resume(void)
+{
+	if (btmtk_usb_submit_intr_urb() < 0)
+		return -1;
+
+	if (btmtk_usb_submit_bulk_in_urb() < 0)
+		return -1;
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	btmtk_usb_set_state(BTMTK_USB_STATE_WORKING);
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	wake_up_interruptible(&inq);
+	return 0;
+}
+
+static void btmtk_usb_handle_entering_WoBLE_state(void)
+{
+	BTUSB_INFO("%s: begin", __func__);
+#if SUPPORT_MT7662
+	if (is_mt7662(g_data)) {
+		btmtk_usb_send_woble_suspend_cmd();
+		BTUSB_INFO("%s: end", __func__);
+		return;
+	}
+#endif
+#if SUPPORT_MT7668
+	if (is_mt7668(g_data)) {
+		while (g_data->is_mt7668_dongle_state == BTMTK_USB_7668_DONGLE_STATE_POWERING_ON ||
+			g_data->is_mt7668_dongle_state == BTMTK_USB_7668_DONGLE_STATE_POWERING_OFF) {
+			BTUSB_INFO("%s: dongle state is POWERING ON or OFF.", __func__);
+			mdelay(100);
+		}
+
+		if (g_data->is_mt7668_dongle_state == BTMTK_USB_7668_DONGLE_STATE_POWER_OFF) {
+			if (btmtk_usb_send_wmt_power_on_cmd_7668() < 0) {
+				g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_ERROR;
+				BTUSB_INFO("%s: end", __func__);
+				return;
+			}
+			btmtk_usb_send_hci_tci_set_sleep_cmd_7668();
+			btmtk_usb_send_hci_reset_cmd();
+			g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_POWER_ON;
+		}
+
+		if (g_data->is_mt7668_dongle_state == BTMTK_USB_7668_DONGLE_STATE_POWER_ON) {
+			if (btmtk_usb_send_woble_suspend_cmd() < 0) {
+				g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_ERROR;
+				BTUSB_INFO("%s: end", __func__);
+				return;
+			}
+			g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_WOBLE;
+		}
+
+		if (g_data->is_mt7668_dongle_state == BTMTK_USB_7668_DONGLE_STATE_WOBLE)
+			BTUSB_INFO("%s: Dongle is in WoBLE state.", __func__);
+		else
+			BTUSB_INFO("%s: Dongle state error (%d)", __func__, g_data->is_mt7668_dongle_state);
+
+		BTUSB_INFO("%s: end", __func__);
+		return;
+	}
+#endif
+	BTUSB_WARN("%s: No matched dongle!", __func__);
+}
+
+/*============================================================================*/
+/* Internal Functions : Chip Related */
+/*============================================================================*/
+#define ______________________________________Internal_Functions_Chip_Related
+/**
+ * Only for load rom patch function, tmp_str[15] is '\n'
+ */
+#define SHOW_FW_DETAILS(s)							\
+	BTUSB_INFO("%s: %s = %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", __func__, s,	\
+			tmp_str[0], tmp_str[1], tmp_str[2], tmp_str[3],		\
+			tmp_str[4], tmp_str[5], tmp_str[6], tmp_str[7],		\
+			tmp_str[8], tmp_str[9], tmp_str[10], tmp_str[11],	\
+			tmp_str[12], tmp_str[13], tmp_str[14]/*, tmp_str[15]*/)
+
+#if SUPPORT_MT7662
+static int btmtk_usb_load_rom_patch_7662(void)
+{
+	u32 loop = 0;
+	u32 value;
+	s32 sent_len;
+	int ret = 0;
+	u16 total_checksum = 0;
+	struct urb *urb;
+	u32 patch_len = 0;
+	u32 cur_len = 0;
+	dma_addr_t data_dma;
+	struct completion sent_to_mcu_done;
+	int first_block = 1;
+	u8 phase;
+	void *buf;
+	u8 *pos;
+	char *tmp_str;
+	unsigned int pipe = usb_sndbulkpipe(g_data->udev,
+						g_data->
+						bulk_tx_ep->bEndpointAddress);
+
+load_patch_protect:
+	btmtk_usb_switch_iobase_7662(WLAN);
+	btmtk_usb_io_read32_7662(SEMAPHORE_03, &value);
+	loop++;
+
+	if ((value & 0x01) == 0x00) {
+		if (loop < 1000) {
+			mdelay(1);
+			goto load_patch_protect;
+		} else {
+			BTUSB_WARN("%s: WARNING! Can't get semaphore! Continue",
+				__func__);
+		}
+	}
+
+	btmtk_usb_switch_iobase_7662(SYSCTL);
+
+	btmtk_usb_io_write32_7662(0x1c, 0x30);
+
+	btmtk_usb_switch_iobase_7662(WLAN);
+
+	/* check ROM patch if upgrade */
+	btmtk_usb_io_read32_7662(CLOCK_CTL, &value);
+	if ((value & 0x01) == 0x01) {
+		BTUSB_INFO("%s: no need to load rom patch", __func__);
+		if (!is_mt7662T(g_data))
+			btmtk_usb_send_dummy_bulk_out_packet_7662();
+		goto error0;
+	}
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+
+	if (!urb) {
+		ret = -ENOMEM;
+		goto error0;
+	}
+
+	buf =
+		usb_alloc_coherent(g_data->udev, UPLOAD_PATCH_UNIT, GFP_KERNEL,
+				&data_dma);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto error1;
+	}
+
+	pos = buf;
+	btmtk_usb_load_code_from_bin(&g_data->rom_patch,
+					 g_data->rom_patch_bin_file_name,
+					 &g_data->udev->dev,
+					 &g_data->rom_patch_len);
+
+	if (!g_data->rom_patch) {
+		BTUSB_ERR
+			("%s: please assign a rom patch(/etc/firmware/%s)or(/lib/firmware/%s)",
+			 __func__, g_data->rom_patch_bin_file_name,
+			 g_data->rom_patch_bin_file_name);
+
+		ret = -1;
+		goto error2;
+	}
+
+	tmp_str = g_data->rom_patch;
+	SHOW_FW_DETAILS("FW Version");
+	SHOW_FW_DETAILS("build Time");
+
+	tmp_str = g_data->rom_patch + 16;
+	BTUSB_INFO("%s: platform = %c%c%c%c", __func__, tmp_str[0], tmp_str[1],
+		tmp_str[2], tmp_str[3]);
+
+	tmp_str = g_data->rom_patch + 20;
+	BTUSB_INFO("%s: HW/SW version = %c%c%c%c", __func__, tmp_str[0],
+		tmp_str[1], tmp_str[2], tmp_str[3]);
+
+	tmp_str = g_data->rom_patch + 24;
+	BTUSB_INFO("%s: Patch version = %c%c%c%c", __func__, tmp_str[0],
+		tmp_str[1], tmp_str[2], tmp_str[3]);
+
+	do {
+		typedef void (*pdwnc_func) (u8 fgReset);
+		char *pdwnc_func_name = "PDWNC_SetBTInResetState";
+		pdwnc_func pdwndFunc = NULL;
+
+		pdwndFunc = (pdwnc_func) kallsyms_lookup_name(pdwnc_func_name);
+
+		if (pdwndFunc) {
+			BTUSB_INFO("%s: Invoke %s(%d)", __func__,
+				pdwnc_func_name, 1);
+			pdwndFunc(1);
+		} else {
+			BTUSB_WARN("%s: No Exported Func Found [%s]", __func__,
+				pdwnc_func_name);
+		}
+	} while (0);
+
+	BTUSB_INFO("%s: rom patch %s loading...", __func__, g_data->rom_patch_bin_file_name);
+
+	init_completion(&sent_to_mcu_done);
+
+	cur_len = 0x00;
+	patch_len = g_data->rom_patch_len - PATCH_INFO_SIZE;
+	BTUSB_INFO("%s: patch_len = %d", __func__, patch_len);
+
+	/* 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) {
+				phase = PATCH_PHASE2;
+			} else {
+				phase = PATCH_PHASE3;
+			}
+
+			/* prepare HCI header */
+			pos[0] = 0x6F;
+			pos[1] = 0xFC;
+			pos[2] = (sent_len + 5) & 0xFF;
+			pos[3] = ((sent_len + 5) >> 8) & 0xFF;
+
+			/* prepare WMT header */
+			pos[4] = 0x01;
+			pos[5] = 0x01;
+			pos[6] = (sent_len + 1) & 0xFF;
+			pos[7] = ((sent_len + 1) >> 8) & 0xFF;
+
+			pos[8] = phase;
+
+			memcpy(&pos[9], g_data->rom_patch + PATCH_INFO_SIZE + cur_len,
+					sent_len);
+
+			BTUSB_INFO
+				("%s: sent_len = %d, cur_len = %d, phase = %d",
+				 __func__, sent_len, cur_len, phase);
+
+			usb_fill_bulk_urb(urb,
+					g_data->udev,
+					pipe,
+					buf,
+					sent_len + PATCH_HEADER_SIZE,
+					btmtk_usb_load_rom_patch_complete,
+					&sent_to_mcu_done);
+
+			urb->transfer_dma = data_dma;
+			urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+			ret = usb_submit_urb(urb, GFP_KERNEL);
+
+			if (ret)
+				goto error2;
+
+			if (!wait_for_completion_timeout
+				(&sent_to_mcu_done, msecs_to_jiffies(1000))) {
+				usb_kill_urb(urb);
+				BTUSB_ERR("%s: upload rom_patch timeout",
+					__func__);
+				ret = -ETIME;
+				goto error2;
+			}
+
+			cur_len += sent_len;
+			mdelay(1);
+
+		} else {
+			BTUSB_INFO("%s: loading rom patch... Done", __func__);
+			break;
+		}
+	}
+
+	mdelay(20);
+	ret = btmtk_usb_get_rom_patch_result();
+	mdelay(1);
+
+	/* Send Checksum request */
+	total_checksum =
+		btmtk_usb_checksum16_7662(g_data->rom_patch + PATCH_INFO_SIZE,
+					patch_len);
+	btmtk_usb_chk_crc_7662(patch_len);
+
+	mdelay(1);
+
+	if (total_checksum != btmtk_usb_get_crc_7662()) {
+		BTUSB_ERR("checksum fail!, local(0x%x) <> fw(0x%x)",
+			total_checksum, btmtk_usb_get_crc_7662());
+		ret = -1;
+		goto error2;
+	}
+
+	/* send check rom patch result request */
+	mdelay(1);
+	btmtk_usb_send_hci_check_rom_patch_result_cmd_7662();
+
+	/* CHIP_RESET */
+	mdelay(1);
+	ret = btmtk_usb_send_wmt_reset_cmd();
+
+	/* BT_RESET */
+	mdelay(1);
+	btmtk_usb_send_hci_reset_cmd();
+
+	/* Enable BT Low Power */
+	mdelay(1);
+	btmtk_usb_send_hci_low_power_cmd_7662(TRUE);
+
+	/* for WoBLE/WoW low power */
+	mdelay(1);
+	btmtk_usb_send_hci_set_ce_cmd_7662();
+
+	mdelay(1);
+	btmtk_usb_send_hci_set_tx_power_cmd_7662();
+
+error2:
+	usb_free_coherent(g_data->udev, UPLOAD_PATCH_UNIT, buf, data_dma);
+error1:
+	usb_free_urb(urb);
+error0:
+	btmtk_usb_io_write32_7662(SEMAPHORE_03, 0x1);
+
+	do {
+		typedef void (*pdwnc_func) (u8 fgReset);
+		char *pdwnc_func_name = "PDWNC_SetBTInResetState";
+		pdwnc_func pdwndFunc = NULL;
+
+		pdwndFunc = (pdwnc_func) kallsyms_lookup_name(pdwnc_func_name);
+
+		if (pdwndFunc) {
+			BTUSB_INFO("%s: Invoke %s(%d)", __func__,
+				pdwnc_func_name, 0);
+			pdwndFunc(0);
+		} else
+			BTUSB_WARN("%s: No Exported Func Found [%s]", __func__,
+				pdwnc_func_name);
+	} while (0);
+
+	return ret;
+}
+
+static int btmtk_usb_io_read32_7662(u32 reg, u32 *val)
+{
+	int ret = -1;
+	u8 request = g_data->r_request;
+
+	memset(g_data->io_buf, 0, USB_IO_BUF_SIZE);
+	ret = usb_control_msg(g_data->udev, usb_rcvctrlpipe(g_data->udev, 0),
+			request, DEVICE_VENDOR_REQUEST_IN, 0x0, reg,
+			g_data->io_buf, sizeof(u32), USB_CTRL_IO_TIMO);
+
+	if (ret < 0) {
+		*val = 0xffffffff;
+		BTUSB_ERR("%s: error(%d), reg=%x, value=%x", __func__, ret, reg,
+			*val);
+		return ret;
+	}
+
+	memmove(val, g_data->io_buf, sizeof(u32));
+	*val = le32_to_cpu(*val);
+
+	return 0;
+}
+
+static int btmtk_usb_io_write32_7662(u32 reg, u32 val)
+{
+	int ret;
+	u16 value, index;
+	u8 request = g_data->w_request;
+
+	index = (u16) reg;
+	value = val & 0x0000ffff;
+
+	ret =
+		usb_control_msg(g_data->udev, usb_sndctrlpipe(g_data->udev, 0),
+				request, DEVICE_VENDOR_REQUEST_OUT, value, index,
+				NULL, 0, USB_CTRL_IO_TIMO);
+
+	if (ret < 0) {
+		BTUSB_ERR("%s: error(%d), reg=%x, value=%x", __func__, ret, reg,
+			val);
+		return ret;
+	}
+
+	index = (u16) (reg + 2);
+	value = (val & 0xffff0000) >> 16;
+
+	ret =
+		usb_control_msg(g_data->udev, usb_sndctrlpipe(g_data->udev, 0),
+				request, DEVICE_VENDOR_REQUEST_OUT, value, index,
+				NULL, 0, USB_CTRL_IO_TIMO);
+
+	if (ret < 0) {
+		BTUSB_ERR("%s: error(%d), reg=%x, value=%x", __func__, ret, reg,
+			val);
+		return ret;
+	}
+
+	if (ret > 0)
+		ret = 0;
+
+	return ret;
+}
+
+static int btmtk_usb_switch_iobase_7662(int base)
+{
+	int ret = 0;
+
+	switch (base) {
+	case SYSCTL:
+		g_data->w_request = 0x42;
+		g_data->r_request = 0x47;
+		break;
+	case WLAN:
+		g_data->w_request = 0x02;
+		g_data->r_request = 0x07;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static u16 btmtk_usb_checksum16_7662(u8 *pData, int len)
+{
+	u32 sum = 0;
+
+	while (len > 1) {
+		sum += *((u16 *) pData);
+
+		pData = pData + 2;
+
+		if (sum & 0x80000000)
+			sum = (sum & 0xFFFF) + (sum >> 16);
+
+		len -= 2;
+	}
+
+	if (len)
+		sum += *((u8 *) pData);
+
+	while (sum >> 16)
+		sum = (sum & 0xFFFF) + (sum >> 16);
+
+	return (u16)(~sum);
+}
+
+static int btmtk_usb_chk_crc_7662(u32 checksum_len)
+{
+	int ret = 0;
+
+	BT_DBG("%s", __func__);
+
+	memmove(g_data->io_buf, &g_data->rom_patch_offset, 4);
+	memmove(&g_data->io_buf[4], &checksum_len, 4);
+
+	ret =
+		usb_control_msg(g_data->udev, usb_sndctrlpipe(g_data->udev, 0), 0x1,
+				DEVICE_VENDOR_REQUEST_OUT, 0x20, 0x00,
+				g_data->io_buf, 8, USB_CTRL_IO_TIMO);
+
+	if (ret < 0)
+		BTUSB_ERR("%s: error(%d)", __func__, ret);
+
+	return ret;
+}
+
+static u16 btmtk_usb_get_crc_7662(void)
+{
+	int ret = 0;
+	u16 crc, count = 0;
+
+	while (1) {
+		ret =
+			usb_control_msg(g_data->udev,
+					usb_rcvctrlpipe(g_data->udev, 0), 0x01,
+					DEVICE_VENDOR_REQUEST_IN, 0x21, 0x00,
+					g_data->io_buf, USB_IO_BUF_SIZE,
+					USB_CTRL_IO_TIMO);
+
+		if (ret < 0) {
+			crc = 0xFFFF;
+			BTUSB_ERR("%s: error(%d)", __func__, ret);
+		}
+
+		memmove(&crc, g_data->io_buf, 2);
+
+		crc = le16_to_cpu(crc);
+
+		if (crc != 0xFFFF)
+			break;
+
+		mdelay(1);
+
+		if (count++ > 100) {
+			BTUSB_WARN("Query CRC over %d times", count);
+			break;
+		}
+	}
+
+	return crc;
+}
+
+static int btmtk_usb_send_hci_set_tx_power_cmd_7662(void)
+{
+	u8 cmd[] = { 0x79, 0xFC, 0x06, 0x07, 0x80, 0x00, 0x06, 0x07, 0x07 };
+	u8 event[] = { 0x0E, 0x04, 0x01, 0x79, 0xFC, 0x00 };
+	int ret = -1;	/* if successful, 0 */
+
+	ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), event, sizeof(event));
+	if (ret < 0) {
+		BTUSB_ERR("%s: failed(%d)", __func__, ret);
+	} else {
+		BTUSB_INFO("%s: OK", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int btmtk_usb_send_hci_check_rom_patch_result_cmd_7662(void)
+{
+	u8 cmd[] = { 0xD1, 0xFC, 0x04, 0x00, 0xE2, 0x40, 0x00 };
+	u8 event[] = { 0x0E, 0x08, 0x01, 0xD1, 0xFC, 0x00 };
+	int ret = -1;	/* if successful, 0 */
+
+	ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), event, sizeof(event));
+	if (ret < 0) {
+		BTUSB_ERR("%s: failed(%d)", __func__, ret);
+
+	} else if (ret == sizeof(event) + 4) {
+		if (g_data->io_buf[6] == 0 && g_data->io_buf[7] == 0
+				&& g_data->io_buf[8] == 0 && g_data->io_buf[9] == 0) {
+			BTUSB_WARN("Check rom patch result: NG");
+			return -1;
+
+		} else {
+			BTUSB_INFO("Check rom patch result: OK");
+			ret = 0;
+		}
+
+	} else {
+		BTUSB_ERR("%s: failed, incorrect response length(%d)", __func__, ret);
+		return -1;
+	}
+
+	return ret;
+}
+
+static int btmtk_usb_send_hci_radio_on_cmd_7662(void)
+{
+	u8 cmd[] = { 0xC9, 0xFC, 0x02, 0x01, 0x01 };
+	u8 event[] = { 0xE6, 0x02, 0x08, 0x01 };
+	int ret = -1;	/* if successful, 0 */
+
+	ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), event, sizeof(event));
+	if (ret < 0) {
+		BTUSB_ERR("%s: failed(%d)", __func__, ret);
+	} else {
+		BTUSB_INFO("%s: OK", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int btmtk_usb_send_hci_set_ce_cmd_7662(void)
+{
+	u8 cmd[] = { 0xD1, 0xFC, 0x04, 0x0C, 0x07, 0x41, 0x00 };
+	u8 event[] = { 0x0E, 0x08, 0x01, 0xD1, 0xFC, 0x00 };
+	int ret = -1;	/* if successful, 0 */
+
+	ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), event, sizeof(event));
+	if (ret < 0) {
+		BTUSB_ERR("%s: failed(%d)", __func__, ret);
+
+	} else if (ret == sizeof(event) + 4) {
+		if (g_data->io_buf[6] & 0x01) {
+			BTUSB_WARN("warning, 0x41070c[0] is 1!");
+			ret = 0;
+		} else {
+			/* write 0x41070C[0] to 1 */
+			u8 cmd2[11] = { 0xD0, 0xFC, 0x08, 0x0C, 0x07, 0x41, 0x00 };
+
+			cmd2[7] = g_data->io_buf[6] | 0x01;
+			cmd2[8] = g_data->io_buf[7];
+			cmd2[9] = g_data->io_buf[8];
+			cmd2[10] = g_data->io_buf[9];
+
+			ret = btmtk_usb_send_hci_cmd(cmd2, sizeof(cmd2), NULL, 0);
+			if (ret < 0) {
+				BTUSB_ERR("%s: write 0x41070C failed(%d)", __func__, ret);
+			} else {
+				BTUSB_INFO("%s: OK", __func__);
+				ret = 0;
+			}
+		}
+	} else {
+		BTUSB_ERR("%s: failed, incorrect response length(%d)", __func__, ret);
+		return -1;
+	}
+
+	return ret;
+}
+
+static int btmtk_usb_send_hci_radio_off_cmd_7662(void)
+{
+	u8 cmd[] = { 0xC9, 0xFC, 0x02, 0x01, 0x00 };
+	u8 event[] = { 0xE6, 0x02, 0x08, 0x00 };	/* unexpected opcode */
+	int ret = -1;	/* if successful, 0 */
+
+	ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), event, sizeof(event));
+	if (ret < 0) {
+		BTUSB_ERR("%s: failed(%d)", __func__, ret);
+	} else {
+		BTUSB_INFO("%s: OK", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int btmtk_usb_send_hci_low_power_cmd_7662(bool enable)
+{
+	/* default for disable */
+	u8 cmd[] = { 0x40, 0xFC, 0x00 };
+	u8 event[] = { 0x0E, 0x04, 0x01, 0x40, 0xFC, 0x00 };
+	int ret = -1;	/* if successful, 0 */
+
+	/* for enable */
+	if (enable == TRUE) {
+		cmd[0] = 0x41;
+		event[3] = 0x41;
+	}
+	ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), event, sizeof(event));
+	if (ret < 0) {
+		BTUSB_ERR("%s: failed(%d)", __func__, ret);
+	} else {
+		BTUSB_INFO("%s: OK", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static void btmtk_usb_send_dummy_bulk_out_packet_7662(void)
+{
+	int ret = 0;
+	int actual_len;
+	unsigned int pipe;
+	u8 dummy_bulk_out_fuffer[8] = { 0 };
+
+	pipe =
+		usb_sndbulkpipe(g_data->udev, g_data->bulk_tx_ep->bEndpointAddress);
+	ret =
+		usb_bulk_msg(g_data->udev, pipe, dummy_bulk_out_fuffer, 8,
+			 &actual_len, 100);
+	if (ret)
+		BTUSB_ERR("%s: submit dummy bulk out failed!", __func__);
+	else
+		BTUSB_INFO("%s: 1. OK", __func__);
+
+	ret =
+		usb_bulk_msg(g_data->udev, pipe, dummy_bulk_out_fuffer, 8,
+			 &actual_len, 100);
+	if (ret)
+		BTUSB_ERR("%s: submit dummy bulk out failed!", __func__);
+	else
+		BTUSB_INFO("%s: 2. OK", __func__);
+}
+#endif /* SUPPORT_MT7662 */
+
+#if SUPPORT_MT7668
+static int btmtk_usb_io_read32_7668(u32 reg, u32 *val)
+{
+	int ret = -1;
+	__le16 reg_high;
+	__le16 reg_low;
+
+	reg_high = ((reg >> 16) & 0xffff);
+	reg_low = (reg & 0xffff);
+
+	memset(g_data->io_buf, 0, USB_IO_BUF_SIZE);
+	ret = usb_control_msg(g_data->udev, usb_rcvctrlpipe(g_data->udev, 0),
+			0x63,				/* bRequest */
+			DEVICE_VENDOR_REQUEST_IN,	/* bRequestType */
+			reg_high,			/* wValue */
+			reg_low,			/* wIndex */
+			g_data->io_buf,
+			sizeof(u32), USB_CTRL_IO_TIMO);
+
+	if (ret < 0) {
+		*val = 0xffffffff;
+		BTUSB_ERR("%s: error(%d), reg=%x, value=%x", __func__, ret, reg, *val);
+		return ret;
+	}
+
+	memmove(val, g_data->io_buf, sizeof(u32));
+	*val = le32_to_cpu(*val);
+
+	return 0;
+}
+
+static bool btmtk_usb_check_need_load_rom_patch_7668(void)
+{
+	/* TRUE: need load patch., FALSE: do not need */
+	u8 cmd[] = { 0x6F, 0xFC, 0x05, 0x01, 0x17, 0x01, 0x00, 0x01 };
+	u8 event[] = { 0xE4, 0x05, 0x02, 0x17, 0x01, 0x00, /* 0x02 */ };	/* event[6] is key */
+	int ret = -1;
+
+	BTUSB_DBG_RAW(cmd, sizeof(cmd), "%s: Send CMD:", __func__);
+	ret = btmtk_usb_send_wmt_cmd(cmd, sizeof(cmd), event, sizeof(event), 20);
+	/* can't get correct event, need load patch */
+	if (ret < 0)
+		return TRUE;
+
+	if (ret == sizeof(event) + 1 && (g_data->io_buf[6] == 0x00 || g_data->io_buf[6] == 0x01))
+		return FALSE;	/* Do not need */
+
+	return TRUE;
+}
+
+static int btmtk_usb_load_rom_patch_7668(void)
+{
+	s32 sent_len;
+	int ret = 0;
+	struct urb *urb;
+	u32 patch_len = 0;
+	u32 cur_len = 0;
+	dma_addr_t data_dma;
+	struct completion sent_to_mcu_done;
+	int first_block = 1;
+	u8 phase;
+	void *buf;
+	u8 *pos;
+	char *tmp_str;
+	unsigned int pipe = usb_sndbulkpipe(g_data->udev,
+			g_data->bulk_tx_ep->bEndpointAddress);
+
+	BTUSB_INFO("%s: begin", __func__);
+
+	if (btmtk_usb_check_need_load_rom_patch_7668() == FALSE) {
+		BTUSB_INFO("%s: no need to load rom patch", __func__);
+		ret = btmtk_usb_send_wmt_reset_cmd();
+		goto error0;
+	}
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+
+	if (!urb) {
+		ret = -ENOMEM;
+		goto error0;
+	}
+
+	buf = usb_alloc_coherent(g_data->udev, UPLOAD_PATCH_UNIT, GFP_KERNEL,
+			&data_dma);
+
+	if (!buf) {
+		ret = -ENOMEM;
+		goto error1;
+	}
+
+	pos = buf;
+
+	btmtk_usb_load_code_from_bin(&g_data->rom_patch,
+			g_data->rom_patch_bin_file_name, &g_data->udev->dev,
+			&g_data->rom_patch_len);
+
+	if (!g_data->rom_patch) {
+		BTUSB_ERR("%s: please assign a rom patch(/etc/firmware/%s)or(/lib/firmware/%s)",
+			 __func__, g_data->rom_patch_bin_file_name,
+			 g_data->rom_patch_bin_file_name);
+
+		ret = -1;
+		goto error2;
+	}
+
+	tmp_str = g_data->rom_patch;
+	SHOW_FW_DETAILS("FW Version");
+	SHOW_FW_DETAILS("build Time");
+
+	tmp_str = g_data->rom_patch + 16;
+	BTUSB_INFO("%s: platform = %c%c%c%c", __func__, tmp_str[0], tmp_str[1],
+		tmp_str[2], tmp_str[3]);
+
+	tmp_str = g_data->rom_patch + 20;
+	BTUSB_INFO("%s: HW/SW version = %c%c%c%c", __func__, tmp_str[0],
+		tmp_str[1], tmp_str[2], tmp_str[3]);
+
+	tmp_str = g_data->rom_patch + 24;
+
+	BTUSB_INFO("loading rom patch...");
+
+	init_completion(&sent_to_mcu_done);
+
+	cur_len = 0x00;
+	patch_len = g_data->rom_patch_len - PATCH_INFO_SIZE;
+	BTUSB_INFO("%s: patch_len = %d", __func__, patch_len);
+
+	BTUSB_INFO("%s: loading rom patch...", __func__);
+	/* loading rom patch */
+	while (1) {
+		s32 sent_len_max = UPLOAD_PATCH_UNIT - PATCH_HEADER_SIZE;
+		int status = -1;
+
+		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) {
+				phase = PATCH_PHASE2;
+			} else {
+				phase = PATCH_PHASE3;
+			}
+
+			/* prepare HCI header */
+			pos[0] = 0x6F;
+			pos[1] = 0xFC;
+			pos[2] = (sent_len + 5) & 0xFF;
+			pos[3] = ((sent_len + 5) >> 8) & 0xFF;
+
+			/* prepare WMT header */
+			pos[4] = 0x01;
+			pos[5] = 0x01;
+			pos[6] = (sent_len + 1) & 0xFF;
+			pos[7] = ((sent_len + 1) >> 8) & 0xFF;
+
+			pos[8] = phase;
+
+			memcpy(&pos[9], g_data->rom_patch + PATCH_INFO_SIZE + cur_len,
+					sent_len);
+
+			BTUSB_INFO
+				("%s: sent_len = %d, cur_len = %d, phase = %d",
+				 __func__, sent_len, cur_len, phase);
+
+			usb_fill_bulk_urb(urb,
+					g_data->udev,
+					pipe,
+					buf,
+					sent_len + PATCH_HEADER_SIZE,
+					btmtk_usb_load_rom_patch_complete,
+					&sent_to_mcu_done);
+
+			urb->transfer_dma = data_dma;
+			urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+			status = usb_submit_urb(urb, GFP_KERNEL);
+			if (status) {
+				BTUSB_ERR("%s: submit urb failed (%d)", __func__, status);
+				ret = status;
+				goto error2;
+			}
+
+			if (!wait_for_completion_timeout
+				(&sent_to_mcu_done, msecs_to_jiffies(1000))) {
+				usb_kill_urb(urb);
+				BTUSB_ERR("%s: upload rom_patch timeout",
+					__func__);
+				ret = -ETIME;
+				goto error2;
+			}
+
+			cur_len += sent_len;
+
+			mdelay(1);
+			btmtk_usb_get_rom_patch_result();
+			mdelay(1);
+
+		} else {
+			BTUSB_INFO("%s: loading rom patch... Done", __func__);
+			break;
+		}
+	}
+
+	/* CHIP_RESET */
+	ret = btmtk_usb_send_wmt_reset_cmd();
+	g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_POWER_OFF;
+error2:
+	usb_free_coherent(g_data->udev, UPLOAD_PATCH_UNIT, buf, data_dma);
+error1:
+	usb_free_urb(urb);
+error0:
+	BTUSB_INFO("btmtk_usb_load_rom_patch end");
+	return ret;
+}
+
+static int btmtk_usb_send_hci_tci_set_sleep_cmd_7668(void)
+{
+	u8 cmd[] = { 0x7A, 0xFC, 0x07, 0x05, 0x40, 0x06, 0x40, 0x06, 0x00, 0x00 };
+	u8 event[] = { 0x0E, 0x04, 0x01, 0x7A, 0xFC, 0x00 };
+	int ret = -1;	/* if successful, 0 */
+
+	ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), event, sizeof(event));
+	if (ret < 0) {
+		BTUSB_ERR("%s: failed(%d)", __func__, ret);
+	} else {
+		BTUSB_INFO("%s: OK", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int btmtk_usb_send_wmt_power_on_cmd_7668(void)
+{
+	u8 count = 0;	/* retry 3 times */
+	u8 cmd[] = { 0x6F, 0xFC, 0x06, 0x01, 0x06, 0x02, 0x00, 0x00, 0x01 };
+	u8 event[] = { 0xE4, 0x05, 0x02, 0x06, 0x01, 0x00 };	/* event[6] is key */
+	int ret = -1;	/* if successful, 0 */
+
+	g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_POWERING_ON;
+	do {
+		ret = btmtk_usb_send_wmt_cmd(cmd, sizeof(cmd), event, sizeof(event), 20);
+		if (ret < 0) {
+			BTUSB_ERR("%s: failed(%d)", __func__, ret);
+			g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_ERROR;
+
+		} else if (ret == sizeof(event) + 1) {
+			switch (g_data->io_buf[6]) {
+			case 0:			 /* successful */
+				BTUSB_INFO("%s: OK", __func__);
+				g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_POWER_ON;
+				ret = 0;
+				break;
+			case 2:			 /* retry */
+				BTUSB_INFO("%s: Try again", __func__);
+				continue;
+			default:
+				BTUSB_INFO("%s: Unknown result: %02X", __func__, g_data->io_buf[6]);
+				g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_ERROR;
+				return -1;
+			}
+
+		} else {
+			BTUSB_ERR("%s: failed, incorrect response length(%d)", __func__, ret);
+			g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_ERROR;
+			return -1;
+		}
+	} while (++count < 3 && ret > 0);
+
+	return ret;
+}
+
+static int btmtk_usb_send_wmt_power_off_cmd_7668(void)
+{
+	u8 cmd[] = { 0x6F, 0xFC, 0x06, 0x01, 0x06, 0x02, 0x00, 0x00, 0x00 };
+	u8 event[] = { 0xE4, 0x05, 0x02, 0x06, 0x01, 0x00, 0x00 };
+	int ret = -1;	/* if successful, 0 */
+
+	g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_POWERING_OFF;
+	ret = btmtk_usb_send_wmt_cmd(cmd, sizeof(cmd), event, sizeof(event), 20);
+	if (ret < 0) {
+		BTUSB_ERR("%s: failed(%d)", __func__, ret);
+		g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_ERROR;
+		return ret;
+	}
+
+	BTUSB_INFO("%s: OK", __func__);
+	g_data->is_mt7668_dongle_state = BTMTK_USB_7668_DONGLE_STATE_POWER_OFF;
+	return 0;
+}
+#endif /* SUPPORT_MT7668 */
+
+static void btmtk_usb_cap_init(void)
+{
+#if SUPPORT_MT7662
+	btmtk_usb_switch_iobase_7662(WLAN);
+	btmtk_usb_io_read32_7662(0x00, &g_data->chip_id);
+
+	if (g_data->chip_id) {
+		BTUSB_INFO("%s: chip id = %x", __func__, g_data->chip_id);
+		if (is_mt7662T(g_data)) {
+			BTUSB_INFO("%s: This is MT7662T chip", __func__);
+			if (g_data->rom_patch_bin_file_name) {
+				memset(g_data->rom_patch_bin_file_name, 0, MAX_BIN_FILE_NAME_LEN);
+				memcpy(g_data->rom_patch_bin_file_name,
+						"mt7662t_patch_e1_hdr.bin", 24);
+			}
+			g_data->rom_patch_offset = 0xBC000;
+			g_data->rom_patch_len = 0;
+			return;
+		} else if (is_mt7662(g_data)) {
+			BTUSB_INFO("%s: This is MT7662U chip", __func__);
+			if (g_data->rom_patch_bin_file_name) {
+				memset(g_data->rom_patch_bin_file_name, 0, MAX_BIN_FILE_NAME_LEN);
+				memcpy(g_data->rom_patch_bin_file_name,
+						"mt7662_patch_e3_hdr.bin", 23);
+			}
+			g_data->rom_patch_offset = 0x90000;
+			g_data->rom_patch_len = 0;
+			return;
+		}
+	}
+#endif /* SUPPORT_MT7662 */
+
+#if SUPPORT_MT7668
+	btmtk_usb_io_read32_7668(0x80000008, &g_data->chip_id);
+	if (g_data->chip_id) {
+		BTUSB_INFO("%s: chip id = %x", __func__, g_data->chip_id);
+		if (is_mt7668(g_data)) {
+			unsigned int chip_version = 0;
+
+			btmtk_usb_io_read32_7668(0x80000000, &chip_version);
+			memset(g_data->rom_patch_bin_file_name, 0, MAX_BIN_FILE_NAME_LEN);
+			if (chip_version == 0x8a00) {
+				BTUSB_INFO("%s: This is MT7668 E1 chip", __func__);
+				g_data->chip_id |= chip_version << 16;
+				memcpy(g_data->rom_patch_bin_file_name, "mt7668_patch_e1_hdr.bin", 23);
+			} else if (chip_version == 0x8b10) {
+				BTUSB_INFO("%s: This is MT7668 E2 chip", __func__);
+				g_data->chip_id |= chip_version << 16;
+				memcpy(g_data->rom_patch_bin_file_name, "mt7668_patch_e2_hdr.bin", 23);
+			} else
+				BTUSB_ERR("%s: Unknown MT7668 chip version 0x%x", __func__, chip_version);
+
+			g_data->rom_patch_offset = 0x2000000;
+			g_data->rom_patch_len = 0;
+			return;
+		}
+	}
+#endif /* SUPPORT_MT7668 */
+
+	BTUSB_WARN("Unknown chip(%x)", g_data->chip_id);
+
+}
+
+static int btmtk_usb_load_rom_patch(void)
+{
+	int err = -1;
+
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: g_data is NULL !", __func__);
+		return err;
+	}
+
+#if SUPPORT_MT7662
+	if (is_mt7662(g_data))
+		return btmtk_usb_load_rom_patch_7662();
+#endif
+
+#if SUPPORT_MT7668
+	if (is_mt7668(g_data))
+		return btmtk_usb_load_rom_patch_7668();
+#endif
+
+	BTUSB_WARN("%s: unknown chip id (%d)", __func__, g_data->chip_id);
+	return err;
+}
+
+/*============================================================================*/
+/* Internal Functions : Send HCI/WMT */
+/*============================================================================*/
+#define ______________________________________Internal_Functions_Send_HCI_WMT
+static int btmtk_usb_send_woble_suspend_cmd(void)
+{
+	int ret = 0;	/* if successful, 0 */
+#if SUPPORT_LEGACY_WOBLE
+#if BT_RC_VENDOR_DEFAULT
+	do {
+		u8 cmd[] = { 0xC9, 0xFC, 0x0D, 0x01, 0x0E, 0x00, 0x05, 0x43,
+			0x52, 0x4B, 0x54, 0x4D, 0x20, 0x04, 0x32, 0x00 };
+
+		BTUSB_INFO("%s: BT_RC_VENDOR_T0 or Default", __func__);
+		ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), NULL, -1);
+	} while (0);
+#elif BT_RC_VENDOR_S0
+	do {
+		u8 cmd[] = { 0xC9, 0xFC, 0x02, 0x01, 0x0B };
+
+		BTUSB_INFO("%s: BT_RC_VENDOR_S0", __func__);
+		ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), NULL, -1);
+	} while (0);
+#endif
+	if (ret < 0) {
+		BTUSB_ERR("%s: failed(%d)", __func__, ret);
+	} else {
+		BTUSB_INFO("%s: OK", __func__);
+		ret = 0;
+	}
+#endif /* SUPPORT_LEGACY_WOBLE */
+	return ret;
+}
+
+static int btmtk_usb_send_wmt_reset_cmd(void)
+{
+	u8 cmd[] = { 0x6F, 0xFC, 0x05, 0x01, 0x07, 0x01, 0x00, 0x04 };
+	u8 event[] = { 0xE4, 0x05, 0x02, 0x07, 0x01, 0x00, 0x00 };
+	int ret = -1;	/* if successful, 0 */
+
+	BTUSB_DBG_RAW(cmd, sizeof(cmd), "%s: Send CMD:", __func__);
+	ret = btmtk_usb_send_wmt_cmd(cmd, sizeof(cmd), event, sizeof(event), 20);
+	if (ret < 0) {
+		BTUSB_ERR("Check reset wmt result: NG");
+	} else {
+		BTUSB_INFO("Check reset wmt result: OK");
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int btmtk_usb_get_rom_patch_result(void)
+{
+	u8 event[] = { 0xE4, 0x05, 0x02, 0x01, 0x01, 0x00, 0x00 };
+	int ret = -1;	/* if successful, 0 */
+
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: g_data == NULL!", __func__);
+		return -1;
+	}
+	if (g_data->udev == NULL) {
+		BTUSB_ERR("%s: g_data->udev == NULL!", __func__);
+		return -1;
+	}
+	if (g_data->io_buf == NULL) {
+		BTUSB_ERR("%s: g_data->io_buf == NULL!", __func__);
+		return -1;
+	}
+
+	memset(g_data->io_buf, 0, USB_IO_BUF_SIZE);
+	ret =
+		usb_control_msg(g_data->udev, usb_rcvctrlpipe(g_data->udev, 0),
+				0x01, DEVICE_VENDOR_REQUEST_IN, 0x30, 0x00,
+				g_data->io_buf, USB_IO_BUF_SIZE,
+				USB_CTRL_IO_TIMO);
+
+	if (ret < 0)
+		BTUSB_ERR("%s: error(%d)", __func__, ret);
+
+	/* ret should be 16 bytes */
+	if (ret >= sizeof(event) && !memcmp(g_data->io_buf, event, sizeof(event))) {
+		BTUSB_INFO("Get rom patch result: OK");
+		return 0;
+
+	} else {
+		BTUSB_WARN("Get rom patch result: NG");
+		BTUSB_DBG_RAW(g_data->io_buf, ret, "%s: Get unknown event is:", __func__);
+		return -1;
+	}
+
+	return ret;
+}
+
+static int btmtk_usb_send_hci_reset_cmd(void)
+{
+	u8 cmd[] = { 0x03, 0x0C };
+	u8 event[] = { 0x0E, 0x04, 0x01, 0x03, 0x0C, 0x00 };
+	int ret = -1;	/* if successful, 0 */
+
+	ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), event, sizeof(event));
+	if (ret < 0) {
+		BTUSB_ERR("%s: failed(%d)", __func__, ret);
+	} else {
+		BTUSB_INFO("%s: OK", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int btmtk_usb_send_assert_cmd_ctrl(void)
+{
+	u8 cmd[] = { 0x6F, 0xFC, 0x05, 0x01, 0x02, 0x01, 0x00, 0x08 };
+	int ret = -1;	/* if successful, 0 */
+
+	BTUSB_DBG_RAW(cmd, sizeof(cmd), "%s: Send CMD:", __func__);
+	/* Ask DO NOT read event */
+	ret = btmtk_usb_send_hci_cmd(cmd, sizeof(cmd), NULL, -1);
+	if (ret < 0) {
+		BTUSB_ERR("%s: error(%d)", __func__, ret);
+	} else {
+		BTUSB_INFO("%s: OK", __func__);
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int btmtk_usb_send_assert_cmd_bulk(void)
+{
+	int ret = 0;
+	int actual_length = 9;
+	u8 buf[] = { 0x6F, 0xFC, 0x05, 0x00, 0x01, 0x02, 0x01, 0x00, 0x08 };
+
+	BTUSB_DBG_RAW(buf, sizeof(buf), "%s: Send CMD:", __func__);
+	ret = usb_bulk_msg(g_data->udev, usb_sndbulkpipe(g_data->udev, 2), buf,
+			 sizeof(buf), &actual_length, 100);
+
+	if (ret < 0) {
+		BTUSB_ERR("%s: error(%d)", __func__, ret);
+		return ret;
+	}
+	BTUSB_INFO("%s: OK", __func__);
+	return 0;
+}
+
+/*============================================================================*/
+/* Callback Functions */
+/*============================================================================*/
+#define ___________________________________________________Callback_Functions
+static void btmtk_usb_early_suspend(void)
+{
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return;
+	}
+
+	BTUSB_INFO("%s: begin", __func__);
+	BTUSB_WARN("%s: skip this state", __func__);
+	/*
+	* mutex_lock(&btmtk_usb_state_mutex);
+	* btmtk_usb_set_state(BTMTK_USB_STATE_EARLY_SUSPEND);
+	* mutex_unlock(&btmtk_usb_state_mutex);
+	*/
+	BTUSB_INFO("%s: end", __func__);
+}
+
+static void btmtk_usb_late_resume(void)
+{
+	int retry_counter = 10;
+	int state = BTMTK_USB_STATE_UNKNOWN;
+
+late_resume_again:
+	BTUSB_INFO("%s: begin", __func__);
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return;
+	}
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state == BTMTK_USB_STATE_EARLY_SUSPEND || state == BTMTK_USB_STATE_WORKING) {
+		BTUSB_WARN("%s: invoked immediately after early suspend, ignore.", __func__);
+		btmtk_usb_set_state(BTMTK_USB_STATE_WORKING);
+		BTUSB_INFO("%s: end", __func__);
+		mutex_unlock(&btmtk_usb_state_mutex);
+		return;
+	} else if (state == BTMTK_USB_STATE_DISCONNECT || state == BTMTK_USB_STATE_SUSPEND_DISCONNECT
+			|| state == BTMTK_USB_STATE_RESUME_DISCONNECT) {
+		BTUSB_WARN("%s: previous state is disconnect(%d), ignore and set to disconnect state.",
+			__func__, state);
+		btmtk_usb_set_state(BTMTK_USB_STATE_DISCONNECT);
+		BTUSB_INFO("%s: end", __func__);
+		mutex_unlock(&btmtk_usb_state_mutex);
+		return;
+	} else if (state == BTMTK_USB_STATE_FW_DUMP || state == BTMTK_USB_STATE_RESUME_FW_DUMP) {
+		BTUSB_WARN("%s: previous state is fw dump(%d), ignore and set to fw dump state.", __func__, state);
+		btmtk_usb_set_state(BTMTK_USB_STATE_FW_DUMP);
+		BTUSB_INFO("%s: end", __func__);
+		mutex_unlock(&btmtk_usb_state_mutex);
+		return;
+	} else if (state == BTMTK_USB_STATE_PROBE ||
+			state == BTMTK_USB_STATE_SUSPEND_PROBE || state == BTMTK_USB_STATE_RESUME_PROBE) {
+		BTUSB_WARN("%s: previous state is probe(%d), ignore and set to probe state.", __func__, state);
+		btmtk_usb_set_state(BTMTK_USB_STATE_PROBE);
+		BTUSB_INFO("%s: end", __func__);
+		mutex_unlock(&btmtk_usb_state_mutex);
+		return;
+	} else if (state != BTMTK_USB_STATE_RESUME && state != BTMTK_USB_STATE_SUSPEND) {
+		BTUSB_WARN("%s: previous state is not suspend/resume(%d), ignore.", __func__, state);
+		BTUSB_INFO("%s: end", __func__);
+		mutex_unlock(&btmtk_usb_state_mutex);
+		return;
+	}
+	btmtk_usb_set_state(BTMTK_USB_STATE_LATE_RESUME);
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	/* Since late_resume is running in another kernel thread, handle error case here to avoid race condition. */
+	if (btmtk_usb_handle_resume() < 0) {
+		if (retry_counter > 0) {
+			retry_counter--;
+			BTUSB_WARN("%s: failed to handle resume, wait 500ms and retry again.", __func__);
+			BTUSB_INFO("%s: end", __func__);
+			msleep(500);
+			goto late_resume_again;
+		} else {
+			btmtk_usb_send_assert_cmd();
+		}
+	}
+	BTUSB_INFO("%s: end", __func__);
+}
+
+static void btmtk_usb_load_rom_patch_complete(struct urb *urb)
+{
+	struct completion *sent_to_mcu_done = (struct completion *)urb->context;
+
+	complete(sent_to_mcu_done);
+}
+
+static void btmtk_usb_intr_complete(struct urb *urb)
+{
+	u8 *event_buf;
+	u8 ebf0 = 0, ebf1 = 0, ebf2 = 0;
+	u32 roomLeft, last_len, length;
+	int err;
+	struct sk_buff *skb_fwlog = NULL;
+	ulong flags = 0;
+
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return;
+	}
+
+	if (urb->status != 0)
+		BTUSB_WARN("%s: urb %p urb->status %d count %d", __func__,
+			urb, urb->status, urb->actual_length);
+
+	if (urb->status == 0 && urb->actual_length != 0) {
+		event_buf = urb->transfer_buffer;
+		ebf0 = event_buf[0];
+		ebf1 = event_buf[1];
+		ebf2 = event_buf[2];
+		btmtk_usb_hci_snoop_save_event(urb->actual_length,
+						urb->transfer_buffer);
+
+	if (urb->actual_length >= 3 && g_data->fwlog_queue_count < FWLOG_QUEUE_COUNT) {
+		BTUSB_DBG("%s: test", __func__);
+		if ((urb->actual_length == 2 + ebf1) && ebf0 == 0xFF && (ebf2 == 0x50 || ebf2 == 0x51)) {
+
+		skb_fwlog = alloc_skb(urb->actual_length, GFP_ATOMIC);
+		if (skb_fwlog == NULL) {
+			BTUSB_ERR("%s: alloc_skb return 0, error", __func__);
+			goto intr_resub;
+		}
+
+		memcpy(&skb_fwlog->data[0], &event_buf[0], urb->actual_length);
+		skb_fwlog->len = urb->actual_length;
+
+		spin_lock_irqsave(&g_data->fwlog_lock, flags);
+		skb_queue_tail(&g_data->fwlog_queue, skb_fwlog);
+		g_data->fwlog_queue_count++;
+		spin_unlock_irqrestore(&g_data->fwlog_lock, flags);
+		}
+	}
+
+		/* In this condition, need to add header */
+		if (leftHciEventSize == 0)
+			length = urb->actual_length + 1;
+		else
+			length = urb->actual_length;
+
+		btmtk_usb_lock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+
+		/* roomleft means the usable space */
+		if (g_data->metabuffer->read_p <= g_data->metabuffer->write_p) {
+			roomLeft = META_BUFFER_SIZE - g_data->metabuffer->write_p
+				+ g_data->metabuffer->read_p - 1;
+		} else {
+			roomLeft = g_data->metabuffer->read_p - g_data->metabuffer->write_p - 1;
+		}
+
+		/* no enough space to store the received data */
+		if (roomLeft < length)
+			BTUSB_WARN("%s: Queue is full !!", __func__);
+
+		if (length + g_data->metabuffer->write_p < META_BUFFER_SIZE) {
+			/* only one interrupt event, not be splited */
+			if (leftHciEventSize == 0) {
+				/* copy event header: 0x04 */
+				g_data->metabuffer->buffer[g_data->metabuffer->write_p] = 0x04;
+				g_data->metabuffer->write_p += 1;
+			}
+			/* copy event data */
+			memcpy(g_data->metabuffer->buffer + g_data->metabuffer->write_p,
+					event_buf, urb->actual_length);
+			g_data->metabuffer->write_p += urb->actual_length;
+		} else {
+			BTUSB_DBG("%s: back to meta buffer head", __func__);
+			last_len = META_BUFFER_SIZE - g_data->metabuffer->write_p;
+
+			/* only one interrupt event, not be splited */
+			if (leftHciEventSize == 0) {
+				if (last_len != 0) {
+					/* copy evnet header: 0x04 */
+					g_data->metabuffer->buffer[g_data->metabuffer->write_p] = 0x04;
+					g_data->metabuffer->write_p += 1;
+					last_len--;
+					/* copy event data */
+					memcpy(g_data->metabuffer->buffer + g_data->metabuffer->write_p,
+							event_buf, last_len);
+					memcpy(g_data->metabuffer->buffer, event_buf + last_len,
+							urb->actual_length - last_len);
+					g_data->metabuffer->write_p = urb->actual_length - last_len;
+				} else {
+					g_data->metabuffer->buffer[0] = 0x04;
+					g_data->metabuffer->write_p = 1;
+					/* copy event data */
+					memcpy(g_data->metabuffer->buffer + g_data->metabuffer->write_p,
+							event_buf, urb->actual_length);
+					g_data->metabuffer->write_p += urb->actual_length;
+				}
+			/* leftHciEventSize != 0 */
+			} else {
+				/* copy event data */
+				memcpy(g_data->metabuffer->buffer + g_data->metabuffer->write_p,
+						event_buf, last_len);
+				memcpy(g_data->metabuffer->buffer, event_buf + last_len,
+						urb->actual_length - last_len);
+				g_data->metabuffer->write_p = urb->actual_length - last_len;
+			}
+		}
+
+		btmtk_usb_unlock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+
+		if (leftHciEventSize != 0) {
+			leftHciEventSize -= urb->actual_length;
+
+			/* error handling. Length wrong, drop some bytes to recovery counter!! */
+			if (leftHciEventSize < 0) {
+				BTUSB_WARN
+					("%s: *size wrong(%d), this event may be wrong!!",
+					 __func__, leftHciEventSize);
+				leftHciEventSize = 0;	/* reset count */
+			}
+
+			if (leftHciEventSize == 0) {
+				if (btmtk_usb_get_state() ==
+					BTMTK_USB_STATE_WORKING) {
+					wake_up(&BT_wq);
+					wake_up_interruptible(&inq);
+				} else {
+					BTUSB_WARN
+						("%s: current is in suspend/resume (%d), Don't wake-up wait queue",
+						 __func__, btmtk_usb_get_state());
+				}
+			}
+		} else if (leftHciEventSize == 0) {
+			if (btmtk_usb_get_state() == BTMTK_USB_STATE_WORKING) {
+				wake_up(&BT_wq);
+				wake_up_interruptible(&inq);
+			} else {
+				BTUSB_WARN
+					("%s: current is in suspend/resume (%d), Don't wake-up wait queue",
+					 __func__, btmtk_usb_get_state());
+			}
+		}
+	} else {
+		BTUSB_INFO("%s: urb->status:%d, urb->length:%d", __func__,
+			urb->status, urb->actual_length);
+	}
+intr_resub:
+	usb_mark_last_busy(g_data->udev);
+	usb_anchor_urb(urb, &g_data->intr_anchor);
+
+	err = usb_submit_urb(urb, GFP_KERNEL);
+	if (err < 0) {
+		/* -EPERM: urb is being killed; */
+		/* -ENODEV: device got disconnected */
+		if (err != -EPERM && err != -ENODEV)
+			BTUSB_ERR("%s: urb %p failed to resubmit intr_in_urb(%d)",
+				 __func__, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static void btmtk_usb_bulk_in_complete(struct urb *urb)
+{
+	/* actual_length: the ACL data size (doesn't contain header) */
+	u32 roomLeft, last_len, length, index, actual_length;
+	u8 *event_buf;
+	int state = btmtk_usb_get_state();
+	int err;
+	u8 *buf;
+	u16 len;
+
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return;
+	}
+
+	roomLeft = 0;
+	last_len = 0;
+	length = 0;
+	index = 0;
+	actual_length = 0;
+
+	if (urb->status != 0)
+		BTUSB_INFO("%s: urb %p urb->status %d count %d", __func__,
+			urb, urb->status, urb->actual_length);
+
+	/* Handle FW Dump Data */
+	buf = urb->transfer_buffer;
+	len = 0;
+	if (urb->actual_length > 4) {
+		len = buf[2] + ((buf[3] << 8) & 0xff00);
+		if (buf[0] == 0x6f && buf[1] == 0xfc && len + 4 == urb->actual_length) {
+			if (state != BTMTK_USB_STATE_FW_DUMP && state != BTMTK_USB_STATE_RESUME_FW_DUMP) {
+				/* This is the first BULK_IN packet of FW dump. */
+				if (state == BTMTK_USB_STATE_RESUME)
+					btmtk_usb_set_state(BTMTK_USB_STATE_RESUME_FW_DUMP);
+				else
+					btmtk_usb_set_state(BTMTK_USB_STATE_FW_DUMP);
+				btmtk_usb_hci_snoop_print_to_log();
+			}
+
+			btmtk_fifo_in(FIFO_COREDUMP, g_data->bt_fifo, (const void *)&buf[4], len);
+			/* set to 0xff to avoide BlueDroid dropping this ACL packet. */
+			buf[0] = 0xff;
+			buf[1] = 0xff;
+		}
+	}
+
+	if (urb->status == 0) {
+		event_buf = urb->transfer_buffer;
+		len = buf[2] + ((buf[3] << 8) & 0xff00);
+
+		btmtk_usb_hci_snoop_save_acl(urb->actual_length,
+						 urb->transfer_buffer);
+
+		if (urb->actual_length > 4 && event_buf[0] == 0x6f
+			&& event_buf[1] == 0xfc && len + 4 == urb->actual_length) {
+			BTUSB_DBG("Coredump message");
+		} else {
+			length = urb->actual_length + 1;
+
+			actual_length =
+				1 * (event_buf[2] & 0x0f) +
+				16 * ((event_buf[2] & 0xf0) >> 4)
+				+ 256 * ((event_buf[3] & 0x0f)) +
+				4096 * ((event_buf[3] & 0xf0) >> 4);
+
+			btmtk_usb_lock_unsleepable_lock(&
+							(g_data->
+							 metabuffer->spin_lock));
+
+			/* roomleft means the usable space */
+			if (g_data->metabuffer->read_p <=
+				g_data->metabuffer->write_p)
+				roomLeft =
+					META_BUFFER_SIZE -
+					g_data->metabuffer->write_p +
+					g_data->metabuffer->read_p - 1;
+			else
+				roomLeft =
+					g_data->metabuffer->read_p -
+					g_data->metabuffer->write_p - 1;
+
+			/* no enough space to store the received data */
+			if (roomLeft < length)
+				BTUSB_WARN("%s: Queue is full !!", __func__);
+
+			if (length + g_data->metabuffer->write_p <
+				META_BUFFER_SIZE) {
+
+				if (leftACLSize == 0) {
+					/* copy ACL data header: 0x02 */
+					g_data->metabuffer->
+						buffer[g_data->metabuffer->write_p]
+						= 0x02;
+					g_data->metabuffer->write_p += 1;
+				}
+
+				/* copy event data */
+				memcpy(g_data->metabuffer->buffer + g_data->metabuffer->write_p,
+						event_buf, urb->actual_length);
+				g_data->metabuffer->write_p += urb->actual_length;
+			} else {
+				last_len =
+					META_BUFFER_SIZE -
+					g_data->metabuffer->write_p;
+				if (leftACLSize == 0) {
+					if (last_len != 0) {
+						/* copy ACL data header: 0x02 */
+						g_data->metabuffer->buffer[g_data->metabuffer->write_p] = 0x02;
+						g_data->metabuffer->write_p += 1;
+						last_len--;
+						/* copy event data */
+						memcpy(g_data->metabuffer->buffer + g_data->metabuffer->write_p,
+								event_buf, last_len);
+						memcpy(g_data->metabuffer->buffer, event_buf + last_len,
+								urb->actual_length - last_len);
+						g_data->metabuffer->write_p =
+							urb->actual_length -
+							last_len;
+					} else {
+						g_data->metabuffer->buffer[0] =
+							0x02;
+						g_data->metabuffer->write_p = 1;
+						/* copy event data */
+						memcpy(g_data->metabuffer->buffer + g_data->metabuffer->write_p,
+								event_buf, urb->actual_length);
+						g_data->metabuffer->write_p += urb->actual_length;
+					}
+				} else {	/* leftACLSize !=0 */
+
+					/* copy event data */
+					memcpy(g_data->metabuffer->buffer + g_data->metabuffer->write_p,
+							event_buf, last_len);
+					memcpy(g_data->metabuffer->buffer, event_buf + last_len,
+							urb->actual_length - last_len);
+					g_data->metabuffer->write_p =
+						urb->actual_length - last_len;
+				}
+			}
+
+			btmtk_usb_unlock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+
+			/* the maximize bulk in ACL data packet size is 512 (4byte header + 508 byte data) */
+			/* maximum receieved data size of one packet is 1025 (4byte header + 1021 byte data) */
+			if (leftACLSize == 0 && actual_length > 1017) {
+				/* the data in next interrupt event */
+				leftACLSize =
+					actual_length + 4 - urb->actual_length;
+				BTUSB_DBG("there is left ACL event, size: %d",
+					leftACLSize);
+			} else if (leftACLSize > 0) {
+				leftACLSize -= urb->actual_length;
+
+				/* error handling. Length wrong, drop some bytes to recovery counter!! */
+				if (leftACLSize < 0) {
+					BTUSB_WARN
+						("* size wrong(%d), this acl data may be wrong!!",
+						 leftACLSize);
+					leftACLSize = 0;	/* reset count */
+				}
+
+				if (leftACLSize == 0) {
+					if (state == BTMTK_USB_STATE_WORKING || state == BTMTK_USB_STATE_FW_DUMP ||
+						state == BTMTK_USB_STATE_RESUME_FW_DUMP) {
+						wake_up(&BT_wq);
+						wake_up_interruptible(&inq);
+					} else {
+						BTUSB_DBG
+							("%s: now is in suspend/resume(%d), Don't wake-up wait queue",
+							__func__, state);
+					}
+				}
+			} else if (leftACLSize == 0 && actual_length <= 1017) {
+				if (state == BTMTK_USB_STATE_WORKING || state == BTMTK_USB_STATE_FW_DUMP ||
+					state == BTMTK_USB_STATE_RESUME_FW_DUMP) {
+					wake_up(&BT_wq);
+					wake_up_interruptible(&inq);
+				} else {
+					BTUSB_DBG
+						("%s: current is in suspend/resume (%d), Don't wake-up wait queue",
+						 __func__, state);
+				}
+			} else {
+				BTUSB_WARN
+					("ACL data count fail, leftACLSize:%d",
+					 leftACLSize);
+			}
+		}
+
+	} else {
+		BTUSB_INFO("%s: urb->status:%d", __func__, urb->status);
+	}
+
+	usb_anchor_urb(urb, &g_data->bulk_anchor);
+	usb_mark_last_busy(g_data->udev);
+
+	err = usb_submit_urb(urb, GFP_KERNEL);
+
+	if (err != 0) {
+		/* -EPERM: urb is being killed; */
+		/* -ENODEV: device got disconnected */
+		if (err != -EPERM && err != -ENODEV)
+			BTUSB_ERR
+				("%s: urb %p failed to resubmit bulk_in_urb(%d)",
+				 __func__, urb, -err);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static void btmtk_usb_tx_complete_meta(struct urb *urb)
+{
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return;
+	}
+
+	usb_free_coherent(g_data->udev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma);
+
+	g_data->meta_tx = 0;
+	kfree(urb->setup_packet);
+}
+
+static int btmtk_usb_standby(void)
+{
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return -ENODEV;
+	}
+
+	BTUSB_INFO("%s: begin", __func__);
+
+	btmtk_usb_handle_entering_WoBLE_state();
+
+	BTUSB_INFO("%s: end", __func__);
+	BTUSB_WARN("%s: Add 500ms delay", __func__);
+	msleep(500); /* Add 500ms delay to avoid log lost. */
+	return 0;
+}
+
+void btmtk_usb_trigger_core_dump(void)
+{
+	int state = BTMTK_USB_STATE_UNKNOWN;
+
+	BTUSB_WARN("%s: Invoked by other module (WiFi).", __func__);
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state == BTMTK_USB_STATE_FW_DUMP || state == BTMTK_USB_STATE_RESUME_FW_DUMP) {
+		BTUSB_WARN("%s: current in dump state, skip.", __func__);
+		mutex_unlock(&btmtk_usb_state_mutex);
+		return;
+	}
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	btmtk_usb_toggle_rst_pin();
+}
+EXPORT_SYMBOL(btmtk_usb_trigger_core_dump);
+
+/*============================================================================*/
+/* Interface Functions : BT Stack */
+/*============================================================================*/
+#define ______________________________________Interface_Function_for_BT_Stack
+static ssize_t btmtk_usb_fops_write(struct file *file, const char __user *buf,
+					size_t count, loff_t *f_pos)
+{
+	int retval = 0;
+	int state = BTMTK_USB_STATE_UNKNOWN;
+	int fstate = BTMTK_FOPS_STATE_UNKNOWN;
+
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return -ENODEV;
+	}
+
+	FOPS_MUTEX_LOCK();
+	fstate = btmtk_fops_get_state();
+	if (fstate != BTMTK_FOPS_STATE_OPENED) {
+		BTUSB_WARN("%s: fops is not open yet(%d)!", __func__, fstate);
+		FOPS_MUTEX_UNLOCK();
+		return -ENODEV;
+	}
+	FOPS_MUTEX_UNLOCK();
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state != BTMTK_USB_STATE_WORKING) {
+		BTUSB_DBG("%s: current is in suspend/resume (%d).", __func__, state);
+		mutex_unlock(&btmtk_usb_state_mutex);
+		msleep(3000);
+		return -EAGAIN;
+	}
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	/* semaphore mechanism, the waited process will sleep */
+	down(&g_data->wr_mtx);
+
+	if (count > 0) {
+		u32 copy_size = (count < BUFFER_SIZE) ? count : BUFFER_SIZE;
+
+		if (copy_from_user(&g_data->o_buf[0], &buf[0], copy_size)) {
+			retval = -EFAULT;
+			BTUSB_ERR("%s: copy data from user fail", __func__);
+			goto OUT;
+		}
+		BTUSB_DBG_RAW(buf, 16, "%s: (len=%2d)", __func__, copy_size);
+
+		/* command */
+		if (g_data->o_buf[0] == 0x01) {
+			/* parsing commands */
+			u8 fw_assert_cmd[] = { 0x01, 0x6F, 0xFC, 0x05, 0x01, 0x02, 0x01, 0x00, 0x08 };
+			u8 reset_cmd[] = { 0x01, 0x03, 0x0C, 0x00 };
+			u8 read_ver_cmd[] = { 0x01, 0x01, 0x10, 0x00 };
+
+			if (copy_size == sizeof(fw_assert_cmd) &&
+					!memcmp(g_data->o_buf, fw_assert_cmd, sizeof(fw_assert_cmd))) {
+				BTUSB_INFO("%s: Donge FW Assert Triggered by BT Stack!", __func__);
+				btmtk_usb_hci_snoop_print_to_log();
+
+			} else if (copy_size == sizeof(reset_cmd) &&
+					!memcmp(g_data->o_buf, reset_cmd, sizeof(reset_cmd))) {
+				BTUSB_INFO("%s: got command: 0x03 0C 00 (HCI_RESET)", __func__);
+
+			} else if (copy_size == sizeof(read_ver_cmd) &&
+					!memcmp(g_data->o_buf, read_ver_cmd, sizeof(read_ver_cmd))) {
+				BTUSB_INFO("%s: got command: 0x01 10 00 (READ_LOCAL_VERSION)",
+						__func__);
+			}
+
+			btmtk_usb_hci_snoop_save_cmd(copy_size, &g_data->o_buf[0]);
+			retval = btmtk_usb_meta_send_data(&g_data->o_buf[0], copy_size);
+
+		/* ACL data */
+		} else if (g_data->o_buf[0] == 0x02) {
+			retval = btmtk_usb_send_data(&g_data->o_buf[0], copy_size);
+
+		/* SCO data */
+		} else if (g_data->o_buf[0] == 0x03) {
+			retval = btmtk_usb_send_data(&g_data->o_buf[0], copy_size);
+
+		/* Unknown */
+		} else {
+			BTUSB_WARN("%s: this is unknown bt data:0x%02x", __func__,
+					g_data->o_buf[0]);
+		}
+	} else {
+		retval = -EFAULT;
+		BTUSB_ERR
+			("%s: target packet length:%zu is not allowed, retval = %d",
+			 __func__, count, retval);
+	}
+
+OUT:
+	up(&g_data->wr_mtx);
+	return retval;
+}
+
+static ssize_t btmtk_usb_fops_writefwlog(struct file *filp, const char __user *buf,
+					size_t count, loff_t *f_pos)
+{
+	int retval = 0;
+	return retval;
+}
+
+static ssize_t btmtk_usb_fops_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
+{
+	int retval = 0;
+	int copyLen = 0;
+	int state = BTMTK_USB_STATE_UNKNOWN;
+	int fstate = BTMTK_FOPS_STATE_UNKNOWN;
+	unsigned short tailLen = 0;
+	u8 *buffer = NULL;
+
+	FOPS_MUTEX_LOCK();
+	fstate = btmtk_fops_get_state();
+	if (fstate != BTMTK_FOPS_STATE_OPENED) {
+		BTUSB_WARN("%s: fops is not open yet(%d)!", __func__, fstate);
+		FOPS_MUTEX_UNLOCK();
+		return -ENODEV;
+	}
+	FOPS_MUTEX_UNLOCK();
+
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return -ENODEV;
+	}
+	buffer = g_data->i_buf;
+
+	down(&g_data->rd_mtx);
+
+	if (count > BUFFER_SIZE) {
+		count = BUFFER_SIZE;
+		BTUSB_WARN("read size is bigger than 1024");
+	}
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state == BTMTK_USB_STATE_FW_DUMP || state == BTMTK_USB_STATE_RESUME_FW_DUMP) {
+		/* do nothing */
+
+	} else if (state != BTMTK_USB_STATE_WORKING) {
+		BTUSB_ERR("%s: current is in suspend/resume (%d).", __func__, state);
+		mutex_unlock(&btmtk_usb_state_mutex);
+		return -EAGAIN;
+	} else if (need_reset_stack == 1 && register_late_resume_func != NULL) {
+		BTUSB_WARN("%s: Reset BT stack", __func__);
+		mutex_unlock(&btmtk_usb_state_mutex);
+
+		/* notify vendor library by signal */
+		kill_fasync(&fasync, SIGIO, POLL_IN);
+
+		need_reset_stack = 0;
+		up(&g_data->rd_mtx);
+		return -ENODEV;
+	}
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	btmtk_usb_lock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+
+	/* means the buffer is empty */
+	while (g_data->metabuffer->read_p == g_data->metabuffer->write_p) {
+
+		/* unlock the buffer to let other process write data to buffer */
+		btmtk_usb_unlock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+
+		/* If nonblocking mode, return directly O_NONBLOCK is specified during open() */
+		if (file->f_flags & O_NONBLOCK) {
+			/* BTUSB_DBG("Non-blocking btmtk_usb_fops_read()"); */
+			retval = -EAGAIN;
+			goto OUT;
+		}
+		wait_event(BT_wq, g_data->metabuffer->read_p != g_data->metabuffer->write_p);
+		btmtk_usb_lock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+	}
+
+	while (g_data->metabuffer->read_p != g_data->metabuffer->write_p) {
+		if (g_data->metabuffer->write_p > g_data->metabuffer->read_p) {
+			copyLen =
+				g_data->metabuffer->write_p -
+				g_data->metabuffer->read_p;
+			if (copyLen > count)
+				copyLen = count;
+			memcpy(g_data->i_buf, g_data->metabuffer->buffer + g_data->metabuffer->read_p,
+					copyLen);
+			g_data->metabuffer->read_p += copyLen;
+			break;
+
+		} else {
+			tailLen = META_BUFFER_SIZE - g_data->metabuffer->read_p;
+			if (tailLen > count) {	/* exclude equal case to skip wrap check */
+				copyLen = count;
+				memcpy(g_data->i_buf, g_data->metabuffer->buffer + g_data->metabuffer->read_p,
+						copyLen);
+				g_data->metabuffer->read_p += copyLen;
+			} else {
+				/* part 1: copy tailLen */
+				memcpy(g_data->i_buf, g_data->metabuffer->buffer + g_data->metabuffer->read_p,
+						tailLen);
+
+				buffer += tailLen;	/* update buffer offset */
+
+				/* part 2: check if head length is enough */
+				copyLen = count - tailLen;
+
+				/* if write_p < copyLen: means we can copy all data until write_p; */
+				/* else: we can only copy data for copyLen */
+				copyLen = (g_data->metabuffer->write_p < copyLen) ? g_data->
+					metabuffer->write_p : copyLen;
+
+				/* if copylen not 0, copy data to buffer */
+				if (copyLen)
+					memcpy(buffer, g_data->metabuffer->buffer + 0, copyLen);
+				/* Update read_p final position */
+				g_data->metabuffer->read_p = copyLen;
+
+				/* update return length: head + tail */
+				copyLen += tailLen;
+			}
+			break;
+		}
+	}
+
+	btmtk_usb_unlock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+
+	BTUSB_DBG_RAW(g_data->i_buf, 16, "%s:  (len=%2d)", __func__, copyLen);
+	if (copy_to_user(buf, g_data->i_buf, copyLen)) {
+		BTUSB_ERR("copy to user fail");
+		copyLen = -EFAULT;
+		goto OUT;
+	}
+OUT:
+	up(&g_data->rd_mtx);
+	return copyLen;
+}
+
+static int btmtk_usb_fops_open(struct inode *inode, struct file *file)
+{
+	int state = BTMTK_USB_STATE_UNKNOWN;
+	int fstate = BTMTK_FOPS_STATE_UNKNOWN;
+
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return -ENODEV;
+	}
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state == BTMTK_USB_STATE_INIT || state == BTMTK_USB_STATE_DISCONNECT) {
+		mutex_unlock(&btmtk_usb_state_mutex);
+		return -EAGAIN;
+	}
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	FOPS_MUTEX_LOCK();
+	fstate = btmtk_fops_get_state();
+	if (fstate == BTMTK_FOPS_STATE_OPENED) {
+		BTUSB_WARN("%s: fops opened!", __func__);
+		FOPS_MUTEX_UNLOCK();
+		return 0;
+	}
+
+	if (fstate == BTMTK_FOPS_STATE_CLOSING) {
+		BTUSB_WARN("%s: fops close is on-going !", __func__);
+		FOPS_MUTEX_UNLOCK();
+		return -ENODEV;
+	}
+	FOPS_MUTEX_UNLOCK();
+
+	BTUSB_INFO("%s: Mediatek Bluetooth USB driver ver %s", __func__, VERSION);
+
+	BTUSB_INFO("%s: major %d minor %d (pid %d), probe counter: %d",
+		__func__, imajor(inode), iminor(inode), current->pid,
+		probe_counter);
+
+	if (current->pid == 1) {
+		BTUSB_WARN("%s: return 0", __func__);
+		return 0;
+	}
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state != BTMTK_USB_STATE_WORKING) {
+		BTUSB_WARN("%s: not in working state(%d).", __func__,
+			state);
+		mutex_unlock(&btmtk_usb_state_mutex);
+		return -ENODEV;
+	}
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	/* init meta buffer */
+	spin_lock_init(&(g_data->metabuffer->spin_lock.lock));
+
+	sema_init(&g_data->wr_mtx, 1);
+	sema_init(&g_data->rd_mtx, 1);
+
+	/* init wait queue */
+	init_waitqueue_head(&(inq));
+
+	/* Init Hci Snoop */
+	btmtk_usb_hci_snoop_init();
+
+#if SUPPORT_MT7662
+	if (is_mt7662(g_data)) {
+		btmtk_usb_send_hci_reset_cmd();
+		btmtk_usb_send_hci_low_power_cmd_7662(TRUE);
+		btmtk_usb_send_hci_set_ce_cmd_7662();
+		btmtk_usb_send_hci_set_tx_power_cmd_7662();
+		btmtk_usb_send_hci_radio_on_cmd_7662();
+	}
+#endif
+#if SUPPORT_MT7668
+	if (is_mt7668(g_data)) {
+		btmtk_usb_send_wmt_power_on_cmd_7668();
+		btmtk_usb_send_hci_tci_set_sleep_cmd_7668();
+		btmtk_usb_send_hci_reset_cmd();
+	}
+#endif
+
+	btmtk_usb_lock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+	g_data->metabuffer->read_p = 0;
+	g_data->metabuffer->write_p = 0;
+	btmtk_usb_unlock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+
+	BTUSB_INFO("enable interrupt and bulk in urb");
+	btmtk_usb_submit_intr_urb();
+	btmtk_usb_submit_bulk_in_urb();
+
+	FOPS_MUTEX_LOCK();
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_OPENED);
+	FOPS_MUTEX_UNLOCK();
+	BTUSB_INFO("%s: OK", __func__);
+
+	return 0;
+}
+
+static int btmtk_usb_fops_close(struct inode *inode, struct file *file)
+{
+	int state = BTMTK_USB_STATE_UNKNOWN;
+	int fstate = BTMTK_FOPS_STATE_UNKNOWN;
+
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return -ENODEV;
+	}
+
+	FOPS_MUTEX_LOCK();
+	fstate = btmtk_fops_get_state();
+	if (fstate != BTMTK_FOPS_STATE_OPENED) {
+		BTUSB_WARN("%s: fops is not allow close(%d)", __func__, fstate);
+		FOPS_MUTEX_UNLOCK();
+		return 0;
+	}
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_CLOSING);
+	FOPS_MUTEX_UNLOCK();
+	BTUSB_INFO("%s: major %d minor %d (pid %d), probe:%d", __func__,
+		imajor(inode), iminor(inode), current->pid, probe_counter);
+
+	btmtk_usb_fops_fasync(-1, file, 0);
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state != BTMTK_USB_STATE_WORKING) {
+		BTUSB_WARN("%s: not in working state(%d).", __func__, state);
+		mutex_unlock(&btmtk_usb_state_mutex);
+		FOPS_MUTEX_LOCK();
+		btmtk_fops_set_state(BTMTK_FOPS_STATE_CLOSED);
+		FOPS_MUTEX_UNLOCK();
+		return 0;
+	}
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	btmtk_usb_stop_traffic();
+	btmtk_usb_send_hci_reset_cmd();
+
+#if SUPPORT_MT7662
+	if (is_mt7662(g_data))
+		btmtk_usb_send_hci_radio_off_cmd_7662();
+#endif
+
+#if SUPPORT_MT7668
+	if (is_mt7668(g_data))
+		btmtk_usb_send_wmt_power_off_cmd_7668();
+#endif
+
+	btmtk_usb_lock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+	g_data->metabuffer->read_p = 0;
+	g_data->metabuffer->write_p = 0;
+	btmtk_usb_unlock_unsleepable_lock(&(g_data->metabuffer->spin_lock));
+
+	FOPS_MUTEX_LOCK();
+	btmtk_fops_set_state(BTMTK_FOPS_STATE_CLOSED);
+	FOPS_MUTEX_UNLOCK();
+
+	BTUSB_INFO("%s: OK", __func__);
+	return 0;
+}
+
+static long btmtk_usb_fops_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	long retval = 0;
+
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return -ENODEV;
+	}
+
+	switch (cmd) {
+	case IOCTL_FW_ASSERT:
+		/* BT trigger fw assert for debug */
+		BTUSB_INFO("BT Set fw assert..., reason:%lu", arg);
+		break;
+	default:
+		retval = -EFAULT;
+		BTUSB_WARN("BT_ioctl(): unknown cmd (%d)", cmd);
+		break;
+	}
+
+	return retval;
+}
+
+static int btmtk_usb_fops_fasync(int fd, struct file *file, int on)
+{
+	BTUSB_INFO("%s: fd = 0x%X, flag = 0x%X", __func__, fd, on);
+	return fasync_helper(fd, file, on, &fasync);
+}
+
+static unsigned int btmtk_usb_fops_poll(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+	int state = BTMTK_USB_STATE_UNKNOWN;
+
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return -ENODEV;
+	}
+
+	if (g_data->metabuffer->read_p == g_data->metabuffer->write_p) {
+		poll_wait(file, &inq, wait);
+
+		/* empty let select sleep */
+		if ((g_data->metabuffer->read_p != g_data->metabuffer->write_p)
+			|| need_reset_stack == 1) {
+			mask |= POLLIN | POLLRDNORM;		/* readable */
+		}
+	} else {
+		mask |= POLLIN | POLLRDNORM;			/* readable */
+	}
+
+	/* do we need condition? */
+	mask |= POLLOUT | POLLWRNORM;				/* writable */
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state == BTMTK_USB_STATE_FW_DUMP || state == BTMTK_USB_STATE_RESUME_FW_DUMP)
+		mask |= POLLIN | POLLRDNORM;			/* readable */
+	else if (state != BTMTK_USB_STATE_WORKING)	/* BTMTK_USB_STATE_WORKING: do nothing */
+		mask = 0;
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	return mask;
+}
+/*fops fwlog*/
+static ssize_t btmtk_usb_fops_readfwlog(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+	int copyLen = 0;
+	ulong flags = 0;
+	struct sk_buff *skb = NULL;
+
+	if (g_data == NULL)
+		return -ENODEV;
+
+	if (g_data->fwlog_queue_count <= 0)
+		return 0;
+
+
+	spin_lock_irqsave(&g_data->fwlog_lock, flags);
+	skb = skb_dequeue(&g_data->fwlog_queue);
+	g_data->fwlog_queue_count--;
+	spin_unlock_irqrestore(&g_data->fwlog_lock, flags);
+	memcpy(buf, skb->data, skb->len);
+	copyLen = skb->len;
+
+	return copyLen;
+}
+
+static int btmtk_usb_fops_openfwlog(struct inode *inode, struct file *file)
+{
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return -ENODEV;
+	}
+
+	BTUSB_INFO("%s: OK", __func__);
+	return 0;
+}
+
+static int btmtk_usb_fops_closefwlog(struct inode *inode, struct file *file)
+{
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return -ENODEV;
+	}
+
+
+	BTUSB_INFO("%s: OK", __func__);
+	return 0;
+}
+
+static long btmtk_usb_fops_unlocked_ioctlfwlog(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	int retval = 0;
+
+	BTUSB_ERR("%s: ->", __func__);
+	if (g_data == NULL) {
+		BTUSB_ERR("%s: ERROR, g_data is NULL!", __func__);
+		return -ENODEV;
+	}
+
+	return retval;
+}
+
+static unsigned int btmtk_usb_fops_pollfwlog(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+	return mask;
+}
+
+/*============================================================================*/
+/* Interface Functions : Kernel */
+/*============================================================================*/
+#define ________________________________________Interface_Function_for_Kernel
+static int btmtk_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_endpoint_descriptor *ep_desc;
+	int i;
+	int state = BTMTK_USB_STATE_UNKNOWN;
+	int err = -1;
+
+	probe_counter++;
+
+	BTUSB_INFO("%s: begin", __func__);
+	BTUSB_INFO("========================================================");
+	BTUSB_INFO("btmtk_usb Mediatek Bluetooth USB driver ver %s", VERSION);
+	BTUSB_INFO("========================================================");
+	BTUSB_INFO("probe_counter = %d", probe_counter);
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state == BTMTK_USB_STATE_SUSPEND_DISCONNECT)
+		btmtk_usb_set_state(BTMTK_USB_STATE_SUSPEND_PROBE);
+	else if (state == BTMTK_USB_STATE_RESUME_DISCONNECT)
+		btmtk_usb_set_state(BTMTK_USB_STATE_RESUME_PROBE);
+	else
+		btmtk_usb_set_state(BTMTK_USB_STATE_PROBE);
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	/* interface numbers are hardcoded in the spec */
+	if (intf->cur_altsetting->desc.bInterfaceNumber != 0) {
+		BTUSB_ERR("[ERR] interface number != 0 (%d)",
+			intf->cur_altsetting->desc.bInterfaceNumber);
+
+		mutex_lock(&btmtk_usb_state_mutex);
+		btmtk_usb_set_state(BTMTK_USB_STATE_DISCONNECT);
+		mutex_unlock(&btmtk_usb_state_mutex);
+
+		BTUSB_ERR("btmtk_usb_probe end Error 1");
+		return -ENODEV;
+	}
+
+	if (!g_data) {
+		mutex_lock(&btmtk_usb_state_mutex);
+		btmtk_usb_set_state(BTMTK_USB_STATE_DISCONNECT);
+		mutex_unlock(&btmtk_usb_state_mutex);
+
+		BTUSB_ERR("btmtk_usb_probe end Error 2");
+		return -ENOMEM;
+	}
+
+	btmtk_usb_init_memory();
+
+	/* set the endpoint type of the interface to btmtk_usb_data */
+	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+		ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+		if (usb_endpoint_is_int_in(ep_desc)) {
+			g_data->intr_ep = ep_desc;
+			continue;
+		}
+
+		if (usb_endpoint_is_bulk_out(ep_desc)) {
+			g_data->bulk_tx_ep = ep_desc;
+			continue;
+		}
+
+		if (usb_endpoint_is_bulk_in(ep_desc)) {
+			g_data->bulk_rx_ep = ep_desc;
+			continue;
+		}
+	}
+
+	if (!g_data->intr_ep || !g_data->bulk_tx_ep || !g_data->bulk_rx_ep) {
+		mutex_lock(&btmtk_usb_state_mutex);
+		btmtk_usb_set_state(BTMTK_USB_STATE_DISCONNECT);
+		mutex_unlock(&btmtk_usb_state_mutex);
+
+		BTUSB_ERR("btmtk_usb_probe end Error 3");
+		return -ENODEV;
+	}
+
+	g_data->udev = interface_to_usbdev(intf);
+	g_data->intf = intf;
+
+	spin_lock_init(&g_data->txlock);
+	INIT_WORK(&g_data->waker, btmtk_usb_waker);
+
+	g_data->meta_tx = 0;
+
+	/* init all usb anchor */
+	init_usb_anchor(&g_data->tx_anchor);
+	init_usb_anchor(&g_data->intr_anchor);
+	init_usb_anchor(&g_data->bulk_anchor);
+
+	g_data->metabuffer->read_p = 0;
+	g_data->metabuffer->write_p = 0;
+	memset(g_data->metabuffer->buffer, 0, META_BUFFER_SIZE);
+
+	btmtk_usb_cap_init();
+
+	err = btmtk_usb_load_rom_patch();
+	if (err < 0) {
+		mutex_lock(&btmtk_usb_state_mutex);
+		btmtk_usb_set_state(BTMTK_USB_STATE_DISCONNECT);
+		mutex_unlock(&btmtk_usb_state_mutex);
+
+		BTUSB_ERR("btmtk_usb_probe end Error 4");
+		return err;
+	}
+
+	/* Interface numbers are hardcoded in the specification */
+	g_data->isoc = usb_ifnum_to_if(g_data->udev, 1);
+
+	/* bind isoc interface to usb driver */
+	if (g_data->isoc) {
+		err =
+			usb_driver_claim_interface(&btmtk_usb_driver, g_data->isoc, g_data);
+		if (err < 0) {
+			mutex_lock(&btmtk_usb_state_mutex);
+			btmtk_usb_set_state(BTMTK_USB_STATE_DISCONNECT);
+			mutex_unlock(&btmtk_usb_state_mutex);
+
+			BTUSB_ERR("btmtk_usb_probe end Error 7");
+			return err;
+		}
+	}
+
+	usb_set_intfdata(intf, g_data);
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state == BTMTK_USB_STATE_SUSPEND_PROBE)
+		btmtk_usb_set_state(BTMTK_USB_STATE_SUSPEND);
+	else if (state == BTMTK_USB_STATE_RESUME_PROBE)
+		btmtk_usb_set_state(BTMTK_USB_STATE_RESUME);
+	else
+		btmtk_usb_set_state(BTMTK_USB_STATE_WORKING);
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	if (need_reset_stack) {
+		if (register_late_resume_func == NULL) {
+			BTUSB_INFO("%s: Notify BT vendor-lib to restart stack", __func__);
+			kill_fasync(&fasync, SIGIO, POLL_IN);
+			need_reset_stack = 0;
+		} else {
+			wake_up_interruptible(&inq);
+		}
+	}
+
+	g_data->bt_fifo = btmtk_fifo_init();
+	btmtk_fifo_start(g_data->bt_fifo);
+
+	spin_lock_init(&g_data->fwlog_lock);
+	skb_queue_head_init(&g_data->fwlog_queue);
+
+	BTUSB_INFO("%s: end", __func__);
+	return 0;
+}
+
+static void btmtk_usb_disconnect(struct usb_interface *intf)
+{
+	int state = BTMTK_USB_STATE_UNKNOWN;
+
+	if (!usb_get_intfdata(intf))
+		return;
+
+	BTUSB_INFO("%s: begin", __func__);
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	state = btmtk_usb_get_state();
+	if (state == BTMTK_USB_STATE_SUSPEND || state == BTMTK_USB_STATE_SUSPEND_DISCONNECT) {
+		if (register_late_resume_func) {
+			BTUSB_WARN
+				("%s: disconnect happens in suspend state --> should stay in suspend state later !",
+				 __func__);
+			btmtk_usb_set_state(BTMTK_USB_STATE_SUSPEND_DISCONNECT);
+		} else {
+			BTUSB_WARN
+				("%s: disconnect hapens in suspend state --> go to disconnect state !",
+				 __func__);
+			btmtk_usb_set_state(BTMTK_USB_STATE_DISCONNECT);
+		}
+	} else if (state == BTMTK_USB_STATE_RESUME || state == BTMTK_USB_STATE_RESUME_FW_DUMP ||
+			state == BTMTK_USB_STATE_RESUME_DISCONNECT) {
+		BTUSB_WARN
+			("%s: disconnect hapens when driver is in resume state, should stay in resume state later !",
+			 __func__);
+		btmtk_usb_set_state(BTMTK_USB_STATE_RESUME_DISCONNECT);
+	} else
+		btmtk_usb_set_state(BTMTK_USB_STATE_DISCONNECT);
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	if (!g_data)
+		return;
+
+	need_reset_stack = 1;
+
+	usb_set_intfdata(g_data->intf, NULL);
+
+	if (g_data->isoc)
+		usb_set_intfdata(g_data->isoc, NULL);
+
+	if (intf == g_data->isoc)
+		usb_driver_release_interface(&btmtk_usb_driver, g_data->intf);
+	else if (g_data->isoc)
+		usb_driver_release_interface(&btmtk_usb_driver, g_data->isoc);
+
+	g_data->metabuffer->read_p = 0;
+	g_data->metabuffer->write_p = 0;
+
+	cancel_work_sync(&g_data->waker);
+
+	btmtk_dbgfs_remove_drvdata();
+
+	btmtk_fifo_init();
+	g_data->bt_fifo = NULL;
+
+	BTUSB_INFO("%s: end", __func__);
+}
+
+static int btmtk_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	BTUSB_INFO("%s: begin", __func__);
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	btmtk_usb_set_state(BTMTK_USB_STATE_SUSPEND);
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+	if ((g_data->suspend_count++)) {
+		BTUSB_WARN("%s: Has suspended. suspend_count: %d", __func__, g_data->suspend_count);
+		BTUSB_INFO("%s: end", __func__);
+		return 0;
+	}
+
+	btmtk_usb_handle_entering_WoBLE_state();
+
+	btmtk_usb_stop_traffic();
+	usb_kill_anchored_urbs(&g_data->tx_anchor);
+#if SUPPORT_LEGACY_WOBLE
+	need_reset_stack = 1;
+#endif
+	BTUSB_INFO("%s: end", __func__);
+	return 0;
+}
+
+static int btmtk_usb_resume(struct usb_interface *intf)
+{
+	g_data->suspend_count--;
+	if (g_data->suspend_count) {
+		BTUSB_WARN("%s: data->suspend_count %d, return 0", __func__, g_data->suspend_count);
+		return 0;
+	}
+
+	BTUSB_INFO("%s: begin", __func__);
+
+	mutex_lock(&btmtk_usb_state_mutex);
+	btmtk_usb_set_state(BTMTK_USB_STATE_RESUME);
+	mutex_unlock(&btmtk_usb_state_mutex);
+
+#if SUPPORT_LEGACY_WOBLE
+	if (register_late_resume_func == NULL && need_reset_stack) {
+		BTUSB_INFO("%s: Notify BT vendor-lib to restart stack", __func__);
+		kill_fasync(&fasync, SIGIO, POLL_IN);
+		need_reset_stack = 0;
+	}
+#endif /* SUPPORT_LEGACY_WOBLE */
+
+	if (register_late_resume_func == NULL)
+		btmtk_usb_handle_resume();
+	BTUSB_INFO("%s: end", __func__);
+	return 0;
+}
+
+static int btmtk_usb_reset_resume(struct usb_interface *intf)
+{
+	BTUSB_INFO("%s: Call resume directly", __func__);
+	return btmtk_usb_resume(intf);
+}
+
+static struct usb_driver btmtk_usb_driver = {
+	.name = "btmtk_usb",
+	.probe = btmtk_usb_probe,
+	.disconnect = btmtk_usb_disconnect,
+	.suspend = btmtk_usb_suspend,
+	.resume = btmtk_usb_resume,
+	.reset_resume = btmtk_usb_reset_resume,
+	.id_table = btmtk_usb_table,
+	.supports_autosuspend = 1,
+	.disable_hub_initiated_lpm = 1,
+};
+
+static int __init btmtk_usb_init(void)
+{
+	BTUSB_INFO("%s: btmtk usb driver ver %s", __func__, VERSION);
+
+	btmtk_dbgfs_init();
+	BTUSB_DBG("debugfs is enable");
+
+	btmtk_usb_BT_init();
+
+	return usb_register(&btmtk_usb_driver);
+}
+
+static void __exit btmtk_usb_exit(void)
+{
+	BTUSB_INFO("%s: btmtk usb driver ver %s", __func__, VERSION);
+
+	btmtk_dbgfs_exit();
+
+	usb_deregister(&btmtk_usb_driver);
+	btmtk_usb_BT_exit();
+}
+
+module_init(btmtk_usb_init);
+module_exit(btmtk_usb_exit);
+
+/**
+ * Module information
+ */
+MODULE_DESCRIPTION("Mediatek Bluetooth USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/btmtk_usb_main.h b/btmtk_usb_main.h
new file mode 100644
index 0000000..6d2682b
--- /dev/null
+++ b/btmtk_usb_main.h
@@ -0,0 +1,155 @@
+/*
+*  Copyright (c) 2016 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 the
+*  GNU General Public License for more details.
+*/
+#ifndef __BTMTK_USB_MAIN_H__
+#define __BTMTK_USB_MAIN_H__
+
+#include <net/bluetooth/bluetooth.h>
+#include "btmtk_define.h"
+
+/* Please keep sync with btmtk_usb_set_state function */
+enum {
+	BTMTK_USB_STATE_UNKNOWN,
+	BTMTK_USB_STATE_INIT,
+	BTMTK_USB_STATE_DISCONNECT,
+	BTMTK_USB_STATE_PROBE,
+	BTMTK_USB_STATE_WORKING,
+	BTMTK_USB_STATE_EARLY_SUSPEND,
+	BTMTK_USB_STATE_SUSPEND,
+	BTMTK_USB_STATE_RESUME,
+	BTMTK_USB_STATE_LATE_RESUME,
+	BTMTK_USB_STATE_FW_DUMP,
+	BTMTK_USB_STATE_SUSPEND_DISCONNECT,
+	BTMTK_USB_STATE_SUSPEND_PROBE,
+	BTMTK_USB_STATE_RESUME_DISCONNECT,
+	BTMTK_USB_STATE_RESUME_PROBE,
+	BTMTK_USB_STATE_RESUME_FW_DUMP,
+};
+
+/* Please keep sync with btmtk_fops_set_state function */
+enum {
+	BTMTK_FOPS_STATE_UNKNOWN,	/* deinit in stpbt destroy */
+	BTMTK_FOPS_STATE_INIT,		/* init in stpbt created */
+	BTMTK_FOPS_STATE_OPENED,	/* open in fops_open */
+	BTMTK_FOPS_STATE_CLOSING,	/* during closing */
+	BTMTK_FOPS_STATE_CLOSED,	/* closed */
+};
+
+struct OSAL_UNSLEEPABLE_LOCK {
+	spinlock_t	lock;
+	unsigned long	flag;
+};
+
+struct ring_buffer_struct {
+	struct OSAL_UNSLEEPABLE_LOCK	spin_lock;
+	unsigned char			buffer[META_BUFFER_SIZE];	/* MTKSTP_BUFFER_SIZE:1024 */
+	unsigned int			read_p;				/* indicate the current read index */
+	unsigned int			write_p;			/* indicate the current write index */
+};
+
+struct btmtk_usb_data {
+	struct usb_device	*udev;	/* store the usb device informaiton */
+	struct usb_interface	*intf;	/* current interface */
+	struct usb_interface	*isoc;	/* isochronous interface */
+	struct work_struct	waker;
+	struct semaphore	wr_mtx;
+	struct semaphore	rd_mtx;
+	struct usb_anchor	tx_anchor;
+	struct usb_anchor	intr_anchor;
+	struct usb_anchor	bulk_anchor;
+	int			meta_tx;
+	spinlock_t		txlock;
+
+	/* USB endpoint */
+	struct usb_endpoint_descriptor	*intr_ep;
+	struct usb_endpoint_descriptor	*bulk_tx_ep;
+	struct usb_endpoint_descriptor	*bulk_rx_ep;
+
+	int				 suspend_count;
+
+	/* request for different io operation */
+	unsigned char	w_request;
+	unsigned char	r_request;
+
+	/* io buffer for usb control transfer */
+	unsigned char	*io_buf;
+
+	unsigned char		*rom_patch;
+	unsigned char		*rom_patch_bin_file_name;
+	unsigned char		*rom_patch_image;
+	unsigned int		rom_patch_image_len;
+	unsigned int		chip_id;
+	unsigned int		rom_patch_offset;
+	unsigned int		rom_patch_len;
+
+	unsigned char		*i_buf;
+	unsigned char		*o_buf;
+	struct ring_buffer_struct	*metabuffer;
+
+	unsigned char		interrupt_urb_submitted;
+	unsigned char		bulk_urb_submitted;
+
+	void			*bt_fifo;
+#if SUPPORT_MT7668
+	unsigned char		is_mt7668_dongle_state;
+#endif
+	struct sk_buff_head	fwlog_queue;
+	spinlock_t		fwlog_lock;
+	u32			fwlog_queue_count;
+
+};
+
+/**
+ * Inline functions
+ */
+#if SUPPORT_MT7662
+static inline int is_mt7662(struct btmtk_usb_data *data)
+{
+	/** Return 1, if MT76x2 or MT76x2T */
+	return ((data->chip_id & 0xffff0000) == 0x76620000)
+		|| ((data->chip_id & 0xffff0000) == 0x76320000);
+}
+
+static inline int is_mt7662T(struct btmtk_usb_data *data)
+{
+	/** Return 1, if MT76x2T */
+	return ((data->chip_id & 0xffffffff) == 0x76620100)
+		|| ((data->chip_id & 0xffffffff) == 0x76320100);
+}
+#endif
+
+#if SUPPORT_MT7668
+static inline int is_mt7668(struct btmtk_usb_data *data)
+{
+	return ((data->chip_id & 0xffff) == 0x7668);
+}
+
+enum {
+	BTMTK_USB_7668_DONGLE_STATE_UNKNOWN,
+	BTMTK_USB_7668_DONGLE_STATE_POWERING_ON,
+	BTMTK_USB_7668_DONGLE_STATE_POWER_ON,
+	BTMTK_USB_7668_DONGLE_STATE_WOBLE,
+	BTMTK_USB_7668_DONGLE_STATE_POWERING_OFF,
+	BTMTK_USB_7668_DONGLE_STATE_POWER_OFF,
+	BTMTK_USB_7668_DONGLE_STATE_ERROR
+};
+#endif
+
+/**
+ * Extern functions
+ */
+int btmtk_usb_send_data(const unsigned char *buffer, const unsigned int length);
+int btmtk_usb_meta_send_data(const unsigned char *buffer,
+		const unsigned int length);
+void btmtk_usb_toggle_rst_pin(void);
+
+#endif /* __BTMTK_USB_MAIN_H__ */