[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/kernel/modules/connectivity/bt_driver/mt66xx/Android.mk b/src/kernel/modules/connectivity/bt_driver/mt66xx/Android.mk
new file mode 100644
index 0000000..8661892
--- /dev/null
+++ b/src/kernel/modules/connectivity/bt_driver/mt66xx/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(MTK_BT_SUPPORT),true)
+ifneq ($(filter MTK_MT66%, $(MTK_BT_CHIP)),)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := bt_drv.ko
+LCOAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_OWNER := mtk
+LOCAL_INIT_RC := init.bt_drv.rc
+LOCAL_REQUIRED_MODULES := wmt_drv.ko
+
+include $(MTK_KERNEL_MODULE)
+
+endif
+endif
\ No newline at end of file
diff --git a/src/kernel/modules/connectivity/bt_driver/mt66xx/Makefile b/src/kernel/modules/connectivity/bt_driver/mt66xx/Makefile
new file mode 100644
index 0000000..60759b2
--- /dev/null
+++ b/src/kernel/modules/connectivity/bt_driver/mt66xx/Makefile
@@ -0,0 +1,63 @@
+export KERNEL_SRC := /lib/modules/$(shell uname -r)/build
+
+$(warning $(CC))
+
+###############################################################################
+# stp_cdev_bt
+###############################################################################
+
+ifneq ($(findstring $(PLATFORM), MT8133_YOCTO MT8512_YOCTO mt6771),)
+    MODULE_NAME = wmt_cdev_bt
+else
+    MODULE_NAME = bt_drv
+endif
+
+$(MODULE_NAME)-objs := stp_chrdev_bt.o
+ifneq ($(CONFIG_MTK_CONNSYS_DEDICATED_LOG_PATH),)
+$(MODULE_NAME)-objs += fw_log_bt.o
+endif
+###############################################################################
+# Common
+###############################################################################
+EXTRA_SYMBOLS = ${TOPDIR}/tmp/work/${PACKAGE_ARCH}${TARGET_VENDOR}-${TARGET_OS}/mt66xx-wmt-drv/1.0-r0/mt66xx-wmt-drv-1.0/Module.symvers
+
+ccflags-y += -fno-pic
+ccflags-y += -D MTK_WCN_WMT_STP_EXP_SYMBOL_ABSTRACT
+ccflags-y += -D CREATE_NODE_DYNAMIC=1
+ccflags-y += -D CFG_MTK_ANDROID_WMT=1
+ccflags-y += \
+	-I$(CONNECTIVITY_SRC)/wmt_mt66xx/common_main/include \
+	-I$(CONNECTIVITY_SRC)/wmt_mt66xx/common_main/linux/include \
+	-I$(CONNECTIVITY_SRC)/../../linux/${KERNEL_VER}/drivers/misc/mediatek/include/mt-plat
+ifneq ($(CONFIG_MTK_CONNSYS_DEDICATED_LOG_PATH),)
+ccflags-y += -I$(CONNECTIVITY_SRC)/wmt_mt66xx/debug_utility
+endif
+
+WMT_SRC_FOLDER := $(TOP)/vendor/mediatek/kernel_modules/connectivity/common
+
+ccflags-y += \
+        -I$(WMT_SRC_FOLDER)/common_main/include \
+	-I$(WMT_SRC_FOLDER)/common_main/linux/include \
+	-I$(srctree)/drivers/misc/mediatek/include/mt-plat
+
+obj-m := $(MODULE_NAME).o
+
+ifeq ($(CONFIG_MTK_CONN_LTE_IDC_SUPPORT),y)
+    ccflags-y += -D WMT_IDC_SUPPORT=1
+else
+    ccflags-y += -D WMT_IDC_SUPPORT=0
+endif
+
+all:
+	make -C $(LINUX_SRC) M=$(PWD) modules
+
+bt_drv:
+ifneq ($(findstring $(PLATFORM), MT8133_YOCTO MT8512_YOCTO MT2712_YOCTO 2731_YOCTO  mt6771),)
+	make -C $(LINUX_SRC) M=$(PWD) modules
+else
+	make -C $(LINUX_SRC) M=$(PWD) $(MODULE_NAME).ko
+endif
+
+clean:
+	make -C $(LINUX_SRC) M=$(PWD) clean
+
diff --git a/src/kernel/modules/connectivity/bt_driver/mt66xx/bt.h b/src/kernel/modules/connectivity/bt_driver/mt66xx/bt.h
new file mode 100644
index 0000000..f4a4d33
--- /dev/null
+++ b/src/kernel/modules/connectivity/bt_driver/mt66xx/bt.h
@@ -0,0 +1,53 @@
+#ifndef _BT_EXP_H_
+#define _BT_EXP_H_
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/sched.h>
+#include <asm/current.h>
+#include <linux/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/poll.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/uio.h>
+#include <linux/mutex.h>
+
+#include "wmt_exp.h"
+#include "stp_exp.h"
+
+
+#ifdef CONFIG_MTK_CONNSYS_DEDICATED_LOG_PATH
+#include "connsys_debug_utility.h"
+
+/* Flags to control BT FW log flow */
+#define OFF 0x00
+#define ON  0xff
+
+/* *****************************************************************************************
+ * BT Logger Tool will send 3 levels(Low, SQC and Debug)
+ * Driver will not check its range so we can provide capability of extention.
+ ******************************************************************************************/
+#define DEFAULT_LEVEL 0x02 /* 0x00:OFF, 0x01: LOW POWER, 0x02: SQC, 0x03: DEBUG */
+
+extern int  fw_log_bt_init(void);
+extern void fw_log_bt_exit(void);
+extern void bt_state_notify(UINT32 on_off);
+extern ssize_t send_hci_frame(const PUINT8 buf, size_t count);
+
+#endif
+
+enum {
+	BTDRV_FOPS_STATE_INIT = 0,
+	BTDRV_FOPS_STATE_OPENING,
+	BTDRV_FOPS_STATE_OPENED,
+	BTDRV_FOPS_STATE_CLOSING,
+};
+
+#endif
diff --git a/src/kernel/modules/connectivity/bt_driver/mt66xx/fw_log_bt.c b/src/kernel/modules/connectivity/bt_driver/mt66xx/fw_log_bt.c
new file mode 100644
index 0000000..6943fbb
--- /dev/null
+++ b/src/kernel/modules/connectivity/bt_driver/mt66xx/fw_log_bt.c
@@ -0,0 +1,377 @@
+/*
+*  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.
+*
+* You should have received a copy of the GNU General Public License along with this program.
+* If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef CONFIG_MTK_CONNSYS_DEDICATED_LOG_PATH
+#include "bt.h"
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*******************************************************************************
+*                                 M A C R O S
+********************************************************************************
+*/
+
+#define PFX                         "[BTFWLOG]"
+#define BT_LOG_DBG                  3
+#define BT_LOG_INFO                 2
+#define BT_LOG_WARN                 1
+#define BT_LOG_ERR                  0
+
+static UINT32 gDbgLevel = BT_LOG_INFO;
+
+#define BT_LOG_PR_DBG(fmt, arg...)	\
+	do { if (gDbgLevel >= BT_LOG_DBG) pr_info(PFX "%s: " fmt, __func__, ##arg); } while (0)
+#define BT_LOG_PR_INFO(fmt, arg...)	\
+	do { if (gDbgLevel >= BT_LOG_INFO) pr_info(PFX "%s: " fmt, __func__, ##arg); } while (0)
+#define BT_LOG_PR_WARN(fmt, arg...)	\
+	do { if (gDbgLevel >= BT_LOG_WARN) pr_warn(PFX "%s: " fmt, __func__, ##arg); } while (0)
+#define BT_LOG_PR_ERR(fmt, arg...)	\
+	do { if (gDbgLevel >= BT_LOG_ERR) pr_err(PFX "%s: " fmt, __func__, ##arg); } while (0)
+
+#define BT_LOG_NODE_NAME "fw_log_bt"
+
+#define BT_FW_LOG_IOC_MAGIC          (0xfc)
+#define BT_FW_LOG_IOCTL_ON_OFF      _IOW(BT_FW_LOG_IOC_MAGIC, 0, int)
+#define BT_FW_LOG_IOCTL_SET_LEVEL   _IOW(BT_FW_LOG_IOC_MAGIC, 1, int)
+#define BT_FW_LOG_IOCTL_GET_LEVEL   _IOW(BT_FW_LOG_IOC_MAGIC, 2, int)
+
+static unsigned char g_bt_on = OFF;
+static unsigned char g_log_on = OFF;
+static unsigned char g_log_level = DEFAULT_LEVEL;
+static unsigned char g_log_current = OFF;
+
+#define BT_LOG_BUFFER_SIZE  512
+
+static struct cdev log_cdev;
+#if CREATE_NODE_DYNAMIC
+static struct class *log_class;
+static struct device *log_dev;
+#endif
+static dev_t devno;
+
+wait_queue_head_t BT_log_wq;
+
+static struct semaphore ioctl_mtx;
+
+static int ascii_to_hex(unsigned char ascii, unsigned char *hex)
+{
+	int ret = 0;
+
+	if('0' <= ascii && ascii <= '9')
+		*hex = ascii - '0';
+	else if ('a' <= ascii && ascii <= 'f')
+		*hex = ascii - 'a' + 10;
+	else if ('A' <= ascii && ascii <= 'F')
+		*hex = ascii - 'A' + 10;
+	else
+		ret = -1;
+
+	return ret;
+}
+
+static int set_fw_log(unsigned char flag)
+{
+	ssize_t retval = 0;
+
+	/* Opcode 0xfc5d TCI_MTK_DEBUG_VERSION_INFO */
+	unsigned char HCI_CMD_FW_LOG_DEBUG[] = {0x01, 0x5d, 0xfc, 0x04, 0x02, 0x00, 0x01, 0xff}; // Via EMI
+
+	HCI_CMD_FW_LOG_DEBUG[7] = flag;
+	BT_LOG_PR_INFO("hci_cmd: %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x\n",
+			HCI_CMD_FW_LOG_DEBUG[0], HCI_CMD_FW_LOG_DEBUG[1],
+			HCI_CMD_FW_LOG_DEBUG[2], HCI_CMD_FW_LOG_DEBUG[3],
+			HCI_CMD_FW_LOG_DEBUG[4], HCI_CMD_FW_LOG_DEBUG[5],
+			HCI_CMD_FW_LOG_DEBUG[6], HCI_CMD_FW_LOG_DEBUG[7]);
+
+	retval = send_hci_frame(HCI_CMD_FW_LOG_DEBUG, sizeof(HCI_CMD_FW_LOG_DEBUG));
+
+	if (likely(retval == sizeof(HCI_CMD_FW_LOG_DEBUG)))
+		return 0;
+	else if (retval < 0)
+		return retval;
+	else {
+		BT_LOG_PR_ERR("Only partial sent %zu bytes, but hci cmd has %zu bytes", retval, sizeof(HCI_CMD_FW_LOG_DEBUG));
+		return -EFAULT;
+	}
+}
+
+void bt_state_notify(UINT32 on_off)
+{
+	BT_LOG_PR_INFO("g_bt_on %d, on_off %d\n", g_bt_on, on_off);
+
+	if (g_bt_on == on_off) {
+		// no change.
+	} else {
+		// changed.
+		if (on_off == OFF) { // should turn off.
+			g_bt_on = OFF;
+			BT_LOG_PR_INFO("BT func off, no need to send hci cmd\n");
+		} else {
+			g_bt_on = ON;
+			if(g_log_current)
+				set_fw_log(g_log_current);
+		}
+	}
+}
+
+long fw_log_bt_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	long retval = 0;
+	unsigned char log_tmp = OFF;
+
+	down(&ioctl_mtx);
+	switch (cmd) {
+		case BT_FW_LOG_IOCTL_ON_OFF:
+			/* connsyslogger daemon dynamically enable/disable Picus log */
+			BT_LOG_PR_INFO("BT_FW_LOG_IOCTL_ON_OFF: arg(%lu), g_bt_on(0x%02x), g_log_on(0x%02x), g_log_level(0x%02x), g_log_current(0x%02x)\n",
+					arg, g_bt_on, g_log_on, g_log_level, g_log_current);
+			log_tmp = (arg == 0 ? OFF: ON);
+			if (log_tmp == g_log_on) // no change
+				break;
+			else { // changed
+				g_log_on = log_tmp;
+				g_log_current = g_log_on & g_log_level;
+				if (g_bt_on)
+					retval = set_fw_log(g_log_current);
+			}
+			break;
+		case BT_FW_LOG_IOCTL_SET_LEVEL:
+			/* connsyslogger daemon dynamically set Picus log level */
+			BT_LOG_PR_INFO("BT_FW_LOG_IOCTL_SET_LEVEL: arg(%lu), g_bt_on(0x%02x),  g_log_on(0x%02x), g_log_level(0x%02x), g_log_current(0x%02x)\n",
+					arg,  g_bt_on, g_log_on, g_log_level, g_log_current);
+			log_tmp = (unsigned char)arg;
+			if(log_tmp == g_log_level) // no change
+				break;
+			else {
+				g_log_level = log_tmp;
+				g_log_current = g_log_on & g_log_level;
+				if (g_bt_on & g_log_on) // driver on and log on
+					retval = set_fw_log(g_log_current);
+			}
+			break;
+		case BT_FW_LOG_IOCTL_GET_LEVEL:
+			retval = g_log_level;
+			BT_LOG_PR_INFO("BT_FW_LOG_IOCTL_GET_LEVEL: %ld\n", retval);
+			break;
+		default:
+			BT_LOG_PR_ERR("Unknown cmd: 0x%08x\n", cmd);
+			retval = -EOPNOTSUPP;
+			break;
+	}
+
+	up(&ioctl_mtx);
+	return retval;
+}
+
+long fw_log_bt_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	return fw_log_bt_unlocked_ioctl(filp, cmd, arg);
+}
+
+static void fw_log_bt_event_cb(void)
+{
+	BT_LOG_PR_DBG("fw_log_bt_event_cb");
+	wake_up_interruptible(&BT_log_wq);
+}
+
+static unsigned int fw_log_bt_poll(struct file *file, poll_table *wait)
+{
+	unsigned int mask = 0;
+
+	poll_wait(file, &BT_log_wq, wait);
+	if (connsys_log_get_buf_size(CONNLOG_TYPE_BT) > 0) {
+		mask = (POLLIN | POLLRDNORM);
+	}
+	return mask;
+}
+
+static ssize_t fw_log_bt_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
+{
+	ssize_t retval = 0;
+	UINT8 tmp_buf[BT_LOG_BUFFER_SIZE] = {0};
+	UINT8 hci_cmd[BT_LOG_BUFFER_SIZE] = {0};
+	UINT8 tmp = 0, tmp_h = 0;
+	size_t i = 0, j = 0, k = 0;
+
+	if(count >= BT_LOG_BUFFER_SIZE) {
+		BT_LOG_PR_ERR("write count %zd exceeds max buffer size %d", count, BT_LOG_BUFFER_SIZE);
+		retval = -EINVAL;
+		goto OUT;
+	}
+
+	if (copy_from_user(tmp_buf, buf, count)) {
+		BT_LOG_PR_ERR("copy_from_user failed!\n");
+		retval = -EFAULT;
+	} else {
+		BT_LOG_PR_INFO("adb input: %s, len %zd\n", tmp_buf, strlen(tmp_buf));
+		if (0 == memcmp(tmp_buf, "raw-hex,", strlen("raw-hex,"))) {
+			// Skip prefix
+			for(i = strlen("raw-hex,"); i < strlen(tmp_buf); i++) {
+				if(tmp_buf[i] == ' ')  // get space
+					continue;
+				else if(tmp_buf[i] == '\r' || tmp_buf[i] =='\n') // get 0x0a('\n') or 0x0d('\r')
+					break;
+				// Two input char should turn to one byte
+				if (ascii_to_hex(tmp_buf[i], &tmp) == 0) {
+					if (j%2 == 0)
+						tmp_h = tmp;
+					else {
+						hci_cmd[k] = tmp_h * 16 + tmp;
+						BT_LOG_PR_DBG("hci_cmd[%zd] = 0x%02x\n", k, hci_cmd[k]);
+						k++;
+					 }
+				} else {
+					BT_LOG_PR_ERR("get unexpected char %c\n", tmp_buf[i]);
+					retval = -EINVAL;
+					goto OUT;
+				}
+				j++;
+			}
+		}
+		// ONLY send hci cmd when BT func on
+		if (!g_bt_on) {
+			retval = -EIO;
+			BT_LOG_PR_ERR("BT func off, skip to send hci cmd\n");
+		} else {
+			retval = send_hci_frame(hci_cmd, k);
+			if (retval == k)    //success
+				retval = count;
+			else
+				retval = -EFAULT;
+		}
+		{ //check log setting cmd and save the log level in case of restart fwlog
+			UINT8 log_level_cmd[8] = { 0x01, 0x5d, 0xfc, 0x04, 0x02, 0x00, 0x01, 0x00 };
+			if (k == 8) {
+				if (memcmp(hci_cmd, log_level_cmd, 7) == 0) { //cmpare the first 7 bytes
+					g_log_current = ON & hci_cmd[7];
+					BT_LOG_PR_INFO("BT fwlog is set to level: 0x%x\n", g_log_current);
+				}
+			}
+		}
+	}
+OUT:
+
+	return retval;
+}
+
+static ssize_t fw_log_bt_read(struct file *filp, char __user *buf, size_t len, loff_t *f_pos)
+{
+	size_t ret = 0;
+
+	ret = connsys_log_read_to_user(CONNLOG_TYPE_BT, buf, len);
+	BT_LOG_PR_DBG("BT F/W log from connsys len %zd\n", ret);
+	return ret;
+}
+
+static int fw_log_bt_open(struct inode *inode, struct file *file)
+{
+	BT_LOG_PR_INFO("major %d minor %d (pid %d)\n", imajor(inode), iminor(inode), current->pid);
+	return 0;
+}
+
+static int fw_log_bt_close(struct inode *inode, struct file *file)
+{
+	BT_LOG_PR_INFO("major %d minor %d (pid %d)\n", imajor(inode), iminor(inode), current->pid);
+	return 0;
+}
+
+struct file_operations log_fops = {
+	.open = fw_log_bt_open,
+	.release = fw_log_bt_close,
+	.read = fw_log_bt_read,
+	.write = fw_log_bt_write,
+	.unlocked_ioctl = fw_log_bt_unlocked_ioctl,
+	.compat_ioctl = fw_log_bt_compat_ioctl,
+	.poll = fw_log_bt_poll
+};
+
+int fw_log_bt_init(void)
+{
+	INT32 alloc_ret = 0;
+	INT32 cdev_err = 0;
+
+	connsys_log_init(CONNLOG_TYPE_BT);
+
+	init_waitqueue_head(&BT_log_wq);
+	connsys_log_register_event_cb(CONNLOG_TYPE_BT, fw_log_bt_event_cb);
+	sema_init(&ioctl_mtx, 1);
+
+	/* Allocate char device */
+	alloc_ret = alloc_chrdev_region(&devno, 0, 1, BT_LOG_NODE_NAME);
+	if (alloc_ret) {
+		BT_LOG_PR_ERR("Failed to register device numbers\n");
+		return alloc_ret;
+	}
+
+	cdev_init(&log_cdev, &log_fops);
+	log_cdev.owner = THIS_MODULE;
+
+	cdev_err = cdev_add(&log_cdev, devno, 1);
+	if (cdev_err)
+		goto error;
+
+#if CREATE_NODE_DYNAMIC /* mknod replace */
+	log_class = class_create(THIS_MODULE, BT_LOG_NODE_NAME);
+	if (IS_ERR(log_class))
+		goto error;
+
+	log_dev = device_create(log_class, NULL, devno, NULL, BT_LOG_NODE_NAME);
+	if (IS_ERR(log_dev))
+		goto error;
+#endif
+
+	BT_LOG_PR_INFO("%s driver(major %d, minor %d) installed\n", BT_LOG_NODE_NAME, MAJOR(devno), MINOR(devno));
+	return 0;
+
+error:
+
+#if CREATE_NODE_DYNAMIC
+	if (log_dev && !IS_ERR(log_dev)) {
+		device_destroy(log_class, devno);
+		log_dev = NULL;
+	}
+	if (log_class && !IS_ERR(log_class)) {
+		class_destroy(log_class);
+		log_class = NULL;
+	}
+#endif
+	if (cdev_err == 0)
+		cdev_del(&log_cdev);
+
+	if (alloc_ret == 0)
+		unregister_chrdev_region(devno, 1);
+
+	return -1;
+}
+
+void fw_log_bt_exit(void)
+{
+	connsys_log_deinit(CONNLOG_TYPE_BT);
+
+	cdev_del(&log_cdev);
+	unregister_chrdev_region(devno, 1);
+
+#if CREATE_NODE_DYNAMIC
+	if (log_dev && !IS_ERR(log_dev)) {
+		device_destroy(log_class, devno);
+		log_dev = NULL;
+	}
+	if (log_class && !IS_ERR(log_class)) {
+		class_destroy(log_class);
+		log_class = NULL;
+	}
+#endif
+	BT_LOG_PR_INFO("%s driver removed\n", BT_LOG_NODE_NAME);
+}
+#endif
diff --git a/src/kernel/modules/connectivity/bt_driver/mt66xx/init.bt_drv.rc b/src/kernel/modules/connectivity/bt_driver/mt66xx/init.bt_drv.rc
new file mode 100644
index 0000000..4c8f8e4
--- /dev/null
+++ b/src/kernel/modules/connectivity/bt_driver/mt66xx/init.bt_drv.rc
@@ -0,0 +1,5 @@
+
+# load bt_drv
+on property:vendor.connsys.driver.ready=yes
+  insmod /vendor/lib/modules/bt_drv.ko
+
diff --git a/src/kernel/modules/connectivity/bt_driver/mt66xx/stp_chrdev_bt.c b/src/kernel/modules/connectivity/bt_driver/mt66xx/stp_chrdev_bt.c
new file mode 100644
index 0000000..d895a15
--- /dev/null
+++ b/src/kernel/modules/connectivity/bt_driver/mt66xx/stp_chrdev_bt.c
@@ -0,0 +1,908 @@
+/*
+* 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.
+*
+* You should have received a copy of the GNU General Public License along with this program.
+* If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "bt.h"
+#include <linux/pm_wakeup.h>
+#include <linux/version.h>
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+/*******************************************************************************
+*                                 M A C R O S
+********************************************************************************
+*/
+#define BT_DRIVER_NAME "mtk_stp_bt_chrdev"
+#define BT_DEV_MAJOR 192
+
+#define PFX                         "[MTK-BT] "
+#define BT_LOG_DBG                  3
+#define BT_LOG_INFO                 2
+#define BT_LOG_WARN                 1
+#define BT_LOG_ERR                  0
+
+static UINT32 gDbgLevel = BT_LOG_INFO;
+
+#define BT_LOG_PR_DBG(fmt, arg...)	\
+	do { if (gDbgLevel >= BT_LOG_DBG) pr_info(PFX "%s: " fmt, __func__, ##arg); } while (0)
+#define BT_LOG_PR_INFO(fmt, arg...)	\
+	do { if (gDbgLevel >= BT_LOG_INFO) pr_info(PFX "%s: " fmt, __func__, ##arg); } while (0)
+#define BT_LOG_PR_WARN(fmt, arg...)	\
+	do { if (gDbgLevel >= BT_LOG_WARN) pr_warn(PFX "%s: " fmt, __func__, ##arg); } while (0)
+#define BT_LOG_PR_ERR(fmt, arg...)	\
+	do { if (gDbgLevel >= BT_LOG_ERR) pr_err(PFX "%s: " fmt, __func__, ##arg); } while (0)
+#define BT_LOG_PR_INFO_RATELIMITED(fmt, arg...)	\
+	do { if (gDbgLevel >= BT_LOG_ERR) pr_info_ratelimited(PFX "%s: " fmt, __func__, ##arg); } while (0)
+
+#define VERSION "2.0"
+
+#define COMBO_IOC_MAGIC             0xb0
+#define COMBO_IOCTL_FW_ASSERT       _IOW(COMBO_IOC_MAGIC, 0, int)
+#define COMBO_IOCTL_BT_SET_PSM      _IOW(COMBO_IOC_MAGIC, 1, bool)
+#define COMBO_IOCTL_BT_IC_HW_VER    _IOR(COMBO_IOC_MAGIC, 2, void*)
+#define COMBO_IOCTL_BT_IC_FW_VER    _IOR(COMBO_IOC_MAGIC, 3, void*)
+
+#define BT_BUFFER_SIZE              2048
+#define FTRACE_STR_LOG_SIZE         256
+
+#define BT_NVRAM_IN_DTBO 1
+#if BT_NVRAM_IN_DTBO
+#include <linux/of.h>
+#define COMBO_IOCTL_BT_FW_CFG_SET_OFFSET    _IOW(COMBO_IOC_MAGIC, 5U, int)
+#define COMBO_IOCTL_BT_FW_CFG_SET_LENGTH    _IOW(COMBO_IOC_MAGIC, 6U, int)
+#define COMBO_IOCTL_BT_FW_CFG_GET_VALUE     _IOR(COMBO_IOC_MAGIC, 7U, void*)
+// the record structure define of bt nvram file
+struct ap_nvram_btradio {
+	unsigned char addr[6];      // BT address
+	unsigned char Voice[2];     // Voice setting for SCO connection
+	unsigned char Codec[4];     // PCM codec setting
+	unsigned char Radio[6];     // RF configuration
+	unsigned char Sleep[7];     // Sleep mode configuration
+	unsigned char BtFTR[2];     // Other feature setting
+	unsigned char TxPWOffset[3];// TX power channel offset compensation
+	unsigned char CoexAdjust[6];// BT/WIFI coex performance adjust
+	unsigned char Reserved1[2]; // Reserved
+	unsigned char Reserved2[2]; // Reserved
+	unsigned char Reserved3[4]; // Reserved
+	unsigned char Reserved4[4]; // Reserved
+	unsigned char Reserved5[8]; // Reserved
+	unsigned char Reserved6[8]; // Reserved
+};
+union BT_NVRAM_DATA_T {
+	struct ap_nvram_btradio fields;
+	unsigned char raw[sizeof(struct ap_nvram_btradio)];
+};
+static union BT_NVRAM_DATA_T bt_fw_cfg;
+static int r_offset;
+static int r_length;
+static int r_flag;
+#endif
+/*******************************************************************************
+*                            P U B L I C   D A T A
+********************************************************************************
+*/
+
+/*******************************************************************************
+*                           P R I V A T E   D A T A
+********************************************************************************
+*/
+static INT32 BT_devs = 1;
+static INT32 BT_major = BT_DEV_MAJOR;
+module_param(BT_major, uint, 0);
+static struct cdev BT_cdev;
+#if CREATE_NODE_DYNAMIC
+static struct class *stpbt_class;
+static struct device *stpbt_dev;
+#endif
+
+static UINT8 i_buf[BT_BUFFER_SIZE]; /* Input buffer for read */
+static UINT8 o_buf[BT_BUFFER_SIZE]; /* Output buffer for write */
+
+static DEFINE_MUTEX(bt_fops_state_mutex);
+static int bt_fops_state = BTDRV_FOPS_STATE_INIT;
+
+static struct semaphore wr_mtx, rd_mtx;
+static struct wakeup_source *bt_wakelock;
+/* Wait queue for poll and read */
+static wait_queue_head_t inq;
+static DECLARE_WAIT_QUEUE_HEAD(BT_wq);
+static INT32 flag;
+static INT32 bt_ftrace_flag;
+/*
+ * Reset flag for whole chip reset scenario, to indicate reset status:
+ *   0 - normal, no whole chip reset occurs
+ *   1 - reset start
+ *   2 - reset end, have not sent Hardware Error event yet
+ *   3 - reset end, already sent Hardware Error event
+ */
+static UINT32 rstflag;
+static UINT8 HCI_EVT_HW_ERROR[] = {0x04, 0x10, 0x01, 0x00};
+static loff_t rd_offset;
+
+/*******************************************************************************
+*                              F U N C T I O N S
+********************************************************************************
+*/
+
+static INT32 ftrace_print(const PINT8 str, ...)
+{
+#ifdef CONFIG_TRACING
+	va_list args;
+	INT8 temp_string[FTRACE_STR_LOG_SIZE];
+
+	if (bt_ftrace_flag) {
+		va_start(args, str);
+		vsnprintf(temp_string, FTRACE_STR_LOG_SIZE, str, args);
+		va_end(args);
+		trace_printk("%s\n", temp_string);
+	}
+#endif
+	return 0;
+}
+
+static void bt_set_fops_state(int new_state)
+{
+	mutex_lock(&bt_fops_state_mutex);
+	bt_fops_state = new_state;
+	mutex_unlock(&bt_fops_state_mutex);
+}
+
+static size_t bt_report_hw_error(char *buf, size_t count, loff_t *f_pos)
+{
+	size_t bytes_rest, bytes_read;
+
+	if (*f_pos == 0)
+		BT_LOG_PR_INFO("Send Hardware Error event to stack to restart Bluetooth\n");
+
+	bytes_rest = sizeof(HCI_EVT_HW_ERROR) - *f_pos;
+	bytes_read = count < bytes_rest ? count : bytes_rest;
+	memcpy(buf, HCI_EVT_HW_ERROR + *f_pos, bytes_read);
+	*f_pos += bytes_read;
+
+	return bytes_read;
+}
+
+/*******************************************************************
+*  WHOLE CHIP RESET message handler
+********************************************************************
+*/
+static VOID bt_cdev_rst_cb(ENUM_WMTDRV_TYPE_T src,
+			   ENUM_WMTDRV_TYPE_T dst, ENUM_WMTMSG_TYPE_T type, PVOID buf, UINT32 sz)
+{
+	ENUM_WMTRSTMSG_TYPE_T rst_msg;
+
+	if (sz > sizeof(ENUM_WMTRSTMSG_TYPE_T)) {
+		BT_LOG_PR_WARN("Invalid message format!\n");
+		return;
+	}
+
+	memcpy((PINT8)&rst_msg, (PINT8)buf, sz);
+	BT_LOG_PR_DBG("src = %d, dst = %d, type = %d, buf = 0x%x sz = %d, max = %d\n",
+		       src, dst, type, rst_msg, sz, WMTRSTMSG_RESET_MAX);
+	if ((src == WMTDRV_TYPE_WMT) && (dst == WMTDRV_TYPE_BT) && (type == WMTMSG_TYPE_RESET)) {
+		switch (rst_msg) {
+		case WMTRSTMSG_RESET_START:
+#ifdef CONFIG_MTK_CONNSYS_DEDICATED_LOG_PATH
+			bt_state_notify(OFF);
+#endif
+			BT_LOG_PR_INFO("Whole chip reset start!\n");
+			rstflag = 1;
+			break;
+
+		case WMTRSTMSG_RESET_END:
+		case WMTRSTMSG_RESET_END_FAIL:
+			if (rst_msg == WMTRSTMSG_RESET_END)
+				BT_LOG_PR_INFO("Whole chip reset end!\n");
+			else
+				BT_LOG_PR_INFO("Whole chip reset fail!\n");
+			rd_offset = 0;
+			rstflag = 2;
+			flag = 1;
+			wake_up_interruptible(&inq);
+			wake_up(&BT_wq);
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+static VOID BT_event_cb(VOID)
+{
+	BT_LOG_PR_DBG("BT_event_cb\n");
+	ftrace_print("%s get called", __func__);
+
+	/*
+	 * Hold wakelock for 100ms to avoid system enter suspend in such case:
+	 *   FW has sent data to host, STP driver received the data and put it
+	 *   into BT rx queue, then send sleep command and release wakelock as
+	 *   quick sleep mechanism for low power, BT driver will wake up stack
+	 *   hci thread stuck in poll or read.
+	 *   But before hci thread comes to read data, system enter suspend,
+	 *   hci command timeout timer keeps counting during suspend period till
+	 *   expires, then the RTC interrupt wakes up system, command timeout
+	 *   handler is executed and meanwhile the event is received.
+	 *   This will false trigger FW assert and should never happen.
+	 */
+	__pm_wakeup_event(bt_wakelock, 100);
+
+	/*
+	 * Finally, wake up any reader blocked in poll or read
+	 */
+	flag = 1;
+	wake_up_interruptible(&inq);
+	wake_up(&BT_wq);
+	ftrace_print("%s wake_up triggered", __func__);
+}
+
+unsigned int BT_poll(struct file *filp, poll_table *wait)
+{
+	UINT32 mask = 0;
+
+	if ((mtk_wcn_stp_is_rxqueue_empty(BT_TASK_INDX) && rstflag == 0) ||
+	    (rstflag == 1) || (rstflag == 3)) {
+		/*
+		 * BT rx queue is empty, or whole chip reset start, or already sent Hardware Error event
+		 * for whole chip reset end, add to wait queue.
+		 */
+		poll_wait(filp, &inq, wait);
+		/*
+		 * Check if condition changes before poll_wait return, in case of
+		 * wake_up_interruptible is called before add_wait_queue, otherwise,
+		 * do_poll will get into sleep and never be waken up until timeout.
+		 */
+		if (!((mtk_wcn_stp_is_rxqueue_empty(BT_TASK_INDX) && rstflag == 0) ||
+		      (rstflag == 1) || (rstflag == 3)))
+			mask |= POLLIN | POLLRDNORM;	/* Readable */
+	} else {
+		/* BT rx queue has valid data, or whole chip reset end, have not sent Hardware Error event yet */
+		mask |= POLLIN | POLLRDNORM;	/* Readable */
+	}
+
+	/* Do we need condition here? */
+	mask |= POLLOUT | POLLWRNORM;	/* Writable */
+	ftrace_print("%s: return mask = 0x%04x", __func__, mask);
+
+	return mask;
+}
+
+static ssize_t __bt_write(const PUINT8 buffer, size_t count)
+{
+	INT32 retval = 0;
+
+	retval = mtk_wcn_stp_send_data(buffer, count, BT_TASK_INDX);
+
+	if (retval < 0)
+		BT_LOG_PR_ERR("mtk_wcn_stp_send_data fail, retval %d\n", retval);
+	else if (retval == 0) {
+		/* Device cannot process data in time, STP queue is full and no space is available for write,
+		 * native program should not call writev with no delay.
+		 */
+		BT_LOG_PR_INFO_RATELIMITED("write count %zd, sent bytes %d, no space is available!\n", count, retval);
+		retval = -EAGAIN;
+	} else
+		BT_LOG_PR_DBG("write count %zd, sent bytes %d\n", count, retval);
+
+	return retval;
+}
+
+ssize_t send_hci_frame(const PUINT8 buff, size_t count)
+{
+	ssize_t retval = 0;
+	int retry = 0;
+
+	down(&wr_mtx);
+
+	do {
+		if (retry > 0) {
+			msleep(30);
+			BT_LOG_PR_ERR("Send hci cmd failed, retry %d time(s)\n", retry);
+		}
+		retval = __bt_write(buff, count);
+		retry++;
+	} while (retval == -EAGAIN && retry < 3);
+
+	up(&wr_mtx);
+
+	return retval;
+}
+
+ssize_t BT_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+	INT32 retval = 0;
+	size_t count = iov_iter_count(from);
+
+	ftrace_print("%s get called, count %zd", __func__, count);
+	down(&wr_mtx);
+
+	BT_LOG_PR_DBG("count %zd", count);
+
+	if (rstflag) {
+		BT_LOG_PR_ERR("whole chip reset occurs! rstflag=%d\n", rstflag);
+		retval = -EIO;
+		goto OUT;
+	}
+
+	if (count > 0) {
+		if (count > BT_BUFFER_SIZE) {
+			BT_LOG_PR_ERR("write count %zd exceeds max buffer size %d", count, BT_BUFFER_SIZE);
+			retval = -EINVAL;
+			goto OUT;
+		}
+
+		if (copy_from_iter(o_buf, count, from) != count) {
+			retval = -EFAULT;
+			goto OUT;
+		}
+
+		retval = __bt_write(o_buf, count);
+	}
+
+OUT:
+	up(&wr_mtx);
+	return retval;
+}
+
+ssize_t BT_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
+{
+	INT32 retval = 0;
+
+	ftrace_print("%s get called, count %zd", __func__, count);
+	down(&wr_mtx);
+
+	BT_LOG_PR_DBG("count %zd pos %lld\n", count, *f_pos);
+
+	if (rstflag) {
+		BT_LOG_PR_ERR("whole chip reset occurs! rstflag=%d\n", rstflag);
+		retval = -EIO;
+		goto OUT;
+	}
+
+	if (count > 0) {
+		if (count > BT_BUFFER_SIZE) {
+			BT_LOG_PR_ERR("write count %zd exceeds max buffer size %d", count, BT_BUFFER_SIZE);
+			retval = -EINVAL;
+			goto OUT;
+		}
+
+		if (copy_from_user(o_buf, buf, count)) {
+			retval = -EFAULT;
+			goto OUT;
+		}
+
+		retval = __bt_write(o_buf, count);
+	}
+
+OUT:
+	up(&wr_mtx);
+	return retval;
+}
+
+ssize_t BT_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+	INT32 retval = 0;
+
+	ftrace_print("%s get called, count %zd", __func__, count);
+	down(&rd_mtx);
+
+	BT_LOG_PR_DBG("count %zd pos %lld\n", count, *f_pos);
+
+	if (rstflag) {
+		while (rstflag != 2) {
+			/*
+			 * If nonblocking mode, return directly.
+			 * O_NONBLOCK is specified during open()
+			 */
+			if (filp->f_flags & O_NONBLOCK) {
+				BT_LOG_PR_ERR("Non-blocking read, whole chip reset occurs! rstflag=%d\n", rstflag);
+				retval = -EIO;
+				goto OUT;
+			}
+
+			wait_event(BT_wq, flag != 0);
+			flag = 0;
+		}
+		/*
+		 * Reset end, send Hardware Error event to stack only once.
+		 * To avoid high frequency read from stack before process is killed, set rstflag to 3
+		 * to block poll and read after Hardware Error event is sent.
+		 */
+		retval = bt_report_hw_error(i_buf, count, &rd_offset);
+		if (rd_offset == sizeof(HCI_EVT_HW_ERROR)) {
+			rd_offset = 0;
+			rstflag = 3;
+		}
+
+		if (copy_to_user(buf, i_buf, retval)) {
+			retval = -EFAULT;
+			if (rstflag == 3)
+				rstflag = 2;
+		}
+
+		goto OUT;
+	}
+
+	if (count > BT_BUFFER_SIZE) {
+		count = BT_BUFFER_SIZE;
+		BT_LOG_PR_WARN("Shorten read count from %zd to %d\n", count, BT_BUFFER_SIZE);
+	}
+
+	do {
+		retval = mtk_wcn_stp_receive_data(i_buf, count, BT_TASK_INDX);
+		if (retval < 0) {
+			BT_LOG_PR_ERR("mtk_wcn_stp_receive_data fail, retval %d\n", retval);
+			goto OUT;
+		} else if (retval == 0) {	/* Got nothing, wait for STP's signal */
+			/*
+			 * If nonblocking mode, return directly.
+			 * O_NONBLOCK is specified during open()
+			 */
+			if (filp->f_flags & O_NONBLOCK) {
+				BT_LOG_PR_ERR("Non-blocking read, no data is available!\n");
+				retval = -EAGAIN;
+				goto OUT;
+			}
+
+			wait_event(BT_wq, flag != 0);
+			flag = 0;
+		} else {	/* Got something from STP driver */
+			BT_LOG_PR_DBG("Read bytes %d\n", retval);
+			break;
+		}
+	} while (!mtk_wcn_stp_is_rxqueue_empty(BT_TASK_INDX) && rstflag == 0);
+
+	if (retval == 0) {
+		if (rstflag != 2) {	/* Should never happen */
+			WARN(1, "Blocking read is waken up with no data but rstflag=%d\n", rstflag);
+			retval = -EIO;
+			goto OUT;
+		} else {	/* Reset end, send Hardware Error event only once */
+			retval = bt_report_hw_error(i_buf, count, &rd_offset);
+			if (rd_offset == sizeof(HCI_EVT_HW_ERROR)) {
+				rd_offset = 0;
+				rstflag = 3;
+			}
+		}
+	}
+
+	if (copy_to_user(buf, i_buf, retval)) {
+		retval = -EFAULT;
+		if (rstflag == 3)
+			rstflag = 2;
+	}
+
+OUT:
+	up(&rd_mtx);
+	return retval;
+}
+
+#if BT_NVRAM_IN_DTBO
+static void BT_get_fw_cfg(void)
+{
+	struct device_node *node;
+	memset(&bt_fw_cfg, 0, sizeof(union BT_NVRAM_DATA_T));
+	r_offset = 0;
+	r_length = 0;
+	r_flag = 0;
+	node = of_find_compatible_node(NULL, NULL,
+				"mediatek,connectivity-combo");
+	if (node) {
+		const UINT8 *prop;
+		prop = of_get_property(node, "btAddr", NULL);
+		if (prop != NULL) {
+			memcpy(bt_fw_cfg.fields.addr, prop,
+					6 * sizeof(unsigned char));
+			BT_LOG_PR_DBG("[BDAddr %02x-%02x-%02x-%02x-%02x-%02x]\n",
+				bt_fw_cfg.fields.addr[0],
+				bt_fw_cfg.fields.addr[1],
+				bt_fw_cfg.fields.addr[2],
+				bt_fw_cfg.fields.addr[3],
+				bt_fw_cfg.fields.addr[4],
+				bt_fw_cfg.fields.addr[5]);
+		}
+		prop = of_get_property(node, "btVoice", NULL);
+		if (prop != NULL) {
+			memcpy(bt_fw_cfg.fields.Voice, prop,
+					2 * sizeof(unsigned char));
+			BT_LOG_PR_DBG("[Voice %02x %02x]\n",
+				bt_fw_cfg.fields.Voice[0],
+				bt_fw_cfg.fields.Voice[1]);
+		}
+		prop = of_get_property(node, "btCodec", NULL);
+		if (prop != NULL) {
+			memcpy(bt_fw_cfg.fields.Codec, prop,
+					4 * sizeof(unsigned char));
+			BT_LOG_PR_DBG("[Codec %02x %02x %02x %02x]\n",
+				bt_fw_cfg.fields.Codec[0],
+				bt_fw_cfg.fields.Codec[1],
+				bt_fw_cfg.fields.Codec[2],
+				bt_fw_cfg.fields.Codec[3]);
+		}
+		prop = of_get_property(node, "btRadio", NULL);
+		if (prop != NULL) {
+			memcpy(bt_fw_cfg.fields.Radio, prop,
+					6 * sizeof(unsigned char));
+			BT_LOG_PR_DBG("[Radio %02x %02x %02x %02x %02x %02x]\n",
+				bt_fw_cfg.fields.Radio[0],
+				bt_fw_cfg.fields.Radio[1],
+				bt_fw_cfg.fields.Radio[2],
+				bt_fw_cfg.fields.Radio[3],
+				bt_fw_cfg.fields.Radio[4],
+				bt_fw_cfg.fields.Radio[5]);
+		}
+		prop = of_get_property(node, "btSleep", NULL);
+		if (prop != NULL) {
+			memcpy(bt_fw_cfg.fields.Sleep, prop,
+					7 * sizeof(unsigned char));
+		BT_LOG_PR_DBG("[Sleep %02x %02x %02x %02x %02x %02x %02x]\n",
+				bt_fw_cfg.fields.Sleep[0],
+				bt_fw_cfg.fields.Sleep[1],
+				bt_fw_cfg.fields.Sleep[2],
+				bt_fw_cfg.fields.Sleep[3],
+				bt_fw_cfg.fields.Sleep[4],
+				bt_fw_cfg.fields.Sleep[5],
+				bt_fw_cfg.fields.Sleep[6]);
+		}
+		prop = of_get_property(node, "btFtr", NULL);
+		if (prop != NULL) {
+			memcpy(bt_fw_cfg.fields.BtFTR, prop,
+					2 * sizeof(unsigned char));
+			BT_LOG_PR_DBG("[BtFTR %02x %02x]\n",
+				bt_fw_cfg.fields.BtFTR[0],
+				bt_fw_cfg.fields.BtFTR[1]);
+		}
+		prop = of_get_property(node, "btTxPwOffset", NULL);
+		if (prop != NULL) {
+			memcpy(bt_fw_cfg.fields.TxPWOffset, prop,
+					3 * sizeof(unsigned char));
+			BT_LOG_PR_DBG("[TxPWOffset %02x %02x %02x]\n",
+				bt_fw_cfg.fields.TxPWOffset[0],
+				bt_fw_cfg.fields.TxPWOffset[1],
+				bt_fw_cfg.fields.TxPWOffset[2]);
+		}
+		prop = of_get_property(node, "btCoexAdjust", NULL);
+		if (prop != NULL) {
+			memcpy(bt_fw_cfg.fields.CoexAdjust, prop,
+					6 * sizeof(unsigned char));
+		BT_LOG_PR_DBG("[CoexAdj %02x %02x %02x %02x %02x %02x]\n",
+				bt_fw_cfg.fields.CoexAdjust[0],
+				bt_fw_cfg.fields.CoexAdjust[1],
+				bt_fw_cfg.fields.CoexAdjust[2],
+				bt_fw_cfg.fields.CoexAdjust[3],
+				bt_fw_cfg.fields.CoexAdjust[4],
+				bt_fw_cfg.fields.CoexAdjust[5]);
+		}
+	} else
+		BT_LOG_PR_ERR("find combo node failed!\n");
+}
+#endif
+/* int BT_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) */
+long BT_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	INT32 retval = 0;
+	UINT32 reason;
+	UINT32 ver = 0;
+
+	BT_LOG_PR_DBG("cmd: 0x%08x\n", cmd);
+
+	switch (cmd) {
+	case COMBO_IOCTL_FW_ASSERT:
+		/* Trigger FW assert for debug */
+		reason = (UINT32)arg & 0xFFFF;
+		BT_LOG_PR_INFO("Host trigger FW assert......, reason:%d\n", reason);
+		if (reason == 31) /* HCI command timeout */
+			BT_LOG_PR_INFO("HCI command timeout OpCode 0x%04x\n", ((UINT32)arg >> 16) & 0xFFFF);
+
+		if (mtk_wcn_wmt_assert(WMTDRV_TYPE_BT, reason) == MTK_WCN_BOOL_TRUE) {
+			BT_LOG_PR_INFO("Host trigger FW assert succeed\n");
+			retval = 0;
+		} else {
+			BT_LOG_PR_ERR("Host trigger FW assert failed\n");
+			retval = -EBUSY;
+		}
+		break;
+	case COMBO_IOCTL_BT_SET_PSM:
+		/* BT stack may need to dynamically enable/disable Power Saving Mode
+		 * in some scenarios for performance, e.g. A2DP chopping.
+		 */
+		BT_LOG_PR_INFO("BT stack change PSM setting: %lu\n", arg);
+		retval = mtk_wcn_wmt_psm_ctrl((MTK_WCN_BOOL)arg);
+		break;
+	case COMBO_IOCTL_BT_IC_HW_VER:
+		ver = mtk_wcn_wmt_ic_info_get(WMTCHIN_HWVER);
+		BT_LOG_PR_INFO("HW ver: 0x%x\n", ver);
+		if (copy_to_user((UINT32 __user *)arg, &ver, sizeof(ver)))
+			retval = -EFAULT;
+		break;
+	case COMBO_IOCTL_BT_IC_FW_VER:
+		ver = mtk_wcn_wmt_ic_info_get(WMTCHIN_FWVER);
+		BT_LOG_PR_INFO("FW ver: 0x%x\n", ver);
+		if (copy_to_user((UINT32 __user *)arg, &ver, sizeof(ver)))
+			retval = -EFAULT;
+		break;
+#if BT_NVRAM_IN_DTBO
+	case COMBO_IOCTL_BT_FW_CFG_SET_OFFSET:
+		r_offset = (int)arg;
+		if (r_offset > sizeof(struct ap_nvram_btradio)) {
+			BT_LOG_PR_ERR("Error OFFSET: %d\n", r_offset);
+			retval = -EOPNOTSUPP;
+		} else {
+			r_flag |= 0x1;
+		}
+		break;
+	case COMBO_IOCTL_BT_FW_CFG_SET_LENGTH:
+		r_length = (int)arg;
+		if (r_offset + r_length > sizeof(struct ap_nvram_btradio)) {
+			BT_LOG_PR_ERR("Error length: %d %d\n",
+						r_offset, r_length);
+			retval = -EOPNOTSUPP;
+		} else {
+			r_flag |= 0x2;
+		}
+		break;
+	case COMBO_IOCTL_BT_FW_CFG_GET_VALUE:
+		if (r_flag != 3) {
+			BT_LOG_PR_ERR("Error flag: %d\n", r_flag);
+			retval = -EOPNOTSUPP;
+		} else {
+			if (copy_to_user((char __user *)arg,
+					&bt_fw_cfg.raw[r_offset],
+					r_length) != 0UL)
+				retval = -EFAULT;
+		}
+		r_flag = 0;
+		break;
+#endif
+	default:
+		BT_LOG_PR_ERR("Unknown cmd: 0x%08x\n", cmd);
+		retval = -EOPNOTSUPP;
+		break;
+	}
+
+	return retval;
+}
+
+long BT_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	return BT_unlocked_ioctl(filp, cmd, arg);
+}
+
+static int BT_open(struct inode *inode, struct file *file)
+{
+	BT_LOG_PR_INFO("major %d minor %d (pid %d)\n", imajor(inode), iminor(inode), current->pid);
+
+	mutex_lock(&bt_fops_state_mutex);
+	if ((bt_fops_state == BTDRV_FOPS_STATE_OPENING)
+		|| (bt_fops_state == BTDRV_FOPS_STATE_OPENED)){
+		BT_LOG_PR_ERR("BT is in using, fops_state = %d\n", bt_fops_state);
+		mutex_unlock(&bt_fops_state_mutex);
+		return -EBUSY;
+	} else if (bt_fops_state == BTDRV_FOPS_STATE_CLOSING) {
+		BT_LOG_PR_ERR("BT is closing, fops_state = %d\n", bt_fops_state);
+		mutex_unlock(&bt_fops_state_mutex);
+		return -EAGAIN;
+	}
+	bt_fops_state = BTDRV_FOPS_STATE_OPENING;
+	mutex_unlock(&bt_fops_state_mutex);
+
+	/* Turn on BT */
+	if (mtk_wcn_wmt_func_on(WMTDRV_TYPE_BT) == MTK_WCN_BOOL_FALSE) {
+		BT_LOG_PR_ERR("WMT turn on BT fail!\n");
+		bt_set_fops_state(BTDRV_FOPS_STATE_INIT);
+		return -EIO;
+	}
+
+	BT_LOG_PR_INFO("WMT turn on BT OK!\n");
+
+	if (mtk_wcn_stp_is_ready() == MTK_WCN_BOOL_FALSE) {
+
+		BT_LOG_PR_ERR("STP is not ready!\n");
+		mtk_wcn_wmt_func_off(WMTDRV_TYPE_BT);
+		bt_set_fops_state(BTDRV_FOPS_STATE_INIT);
+		return -EIO;
+	}
+
+	mtk_wcn_stp_set_bluez(0);
+
+	BT_LOG_PR_INFO("Now it's in MTK Bluetooth Mode\n");
+	BT_LOG_PR_INFO("STP is ready!\n");
+
+	BT_LOG_PR_DBG("Register BT event callback!\n");
+	mtk_wcn_stp_register_event_cb(BT_TASK_INDX, BT_event_cb);
+
+	BT_LOG_PR_DBG("Register BT reset callback!\n");
+	mtk_wcn_wmt_msgcb_reg(WMTDRV_TYPE_BT, bt_cdev_rst_cb);
+
+	rstflag = 0;
+	bt_ftrace_flag = 1;
+
+	sema_init(&wr_mtx, 1);
+	sema_init(&rd_mtx, 1);
+
+#ifdef CONFIG_MTK_CONNSYS_DEDICATED_LOG_PATH
+	bt_state_notify(ON);
+#endif
+	bt_set_fops_state(BTDRV_FOPS_STATE_OPENED);
+
+	return 0;
+}
+
+static int BT_close(struct inode *inode, struct file *file)
+{
+	BT_LOG_PR_INFO("major %d minor %d (pid %d)\n", imajor(inode), iminor(inode), current->pid);
+
+	mutex_lock(&bt_fops_state_mutex);
+	if (bt_fops_state != BTDRV_FOPS_STATE_OPENED) {
+		BT_LOG_PR_ERR("BT is not opened, fops_state = %d\n", bt_fops_state);
+		mutex_unlock(&bt_fops_state_mutex);
+		return 0;
+	}
+	bt_fops_state = BTDRV_FOPS_STATE_CLOSING;
+	mutex_unlock(&bt_fops_state_mutex);
+
+#ifdef CONFIG_MTK_CONNSYS_DEDICATED_LOG_PATH
+	bt_state_notify(OFF);
+#endif
+
+	rstflag = 0;
+	bt_ftrace_flag = 0;
+	mtk_wcn_wmt_msgcb_unreg(WMTDRV_TYPE_BT);
+	mtk_wcn_stp_register_event_cb(BT_TASK_INDX, NULL);
+
+	if (mtk_wcn_wmt_func_off(WMTDRV_TYPE_BT) == MTK_WCN_BOOL_FALSE) {
+		BT_LOG_PR_ERR("WMT turn off BT fail!\n");
+		bt_set_fops_state(BTDRV_FOPS_STATE_INIT);
+		return -EIO;	/* Mostly, native program will not check this return value. */
+	}
+
+	BT_LOG_PR_INFO("WMT turn off BT OK!\n");
+	bt_set_fops_state(BTDRV_FOPS_STATE_INIT);
+	return 0;
+}
+
+const struct file_operations BT_fops = {
+	.open = BT_open,
+	.release = BT_close,
+	.read = BT_read,
+	.write = BT_write,
+	.write_iter = BT_write_iter,
+	/* .ioctl = BT_ioctl, */
+	.unlocked_ioctl = BT_unlocked_ioctl,
+	.compat_ioctl = BT_compat_ioctl,
+	.poll = BT_poll
+};
+
+static int BT_init(void)
+{
+	dev_t dev;
+	INT32 alloc_ret = 0;
+	INT32 cdev_err = 0;
+	dev = MKDEV(BT_major, 0);
+
+	/* Initialize wait queue */
+	init_waitqueue_head(&(inq));
+	/* Initialize wake lock */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 80)
+	bt_wakelock = wakeup_source_register(NULL, "bt_drv");
+#else
+	bt_wakelock = wakeup_source_register("bt_drv");
+#endif
+	if(!bt_wakelock) {
+		BT_LOG_PR_ERR("%s: init bt_wakelock failed!\n", __func__);
+	}
+
+	/* Allocate char device */
+	alloc_ret = register_chrdev_region(dev, BT_devs, BT_DRIVER_NAME);
+	if (alloc_ret) {
+		BT_LOG_PR_ERR("Failed to register device numbers\n");
+		return alloc_ret;
+	}
+
+	cdev_init(&BT_cdev, &BT_fops);
+	BT_cdev.owner = THIS_MODULE;
+
+	cdev_err = cdev_add(&BT_cdev, dev, BT_devs);
+	if (cdev_err)
+		goto error;
+
+#if CREATE_NODE_DYNAMIC /* mknod replace */
+	stpbt_class = class_create(THIS_MODULE, "stpbt");
+	if (IS_ERR(stpbt_class))
+		goto error;
+	stpbt_dev = device_create(stpbt_class, NULL, dev, NULL, "stpbt");
+	if (IS_ERR(stpbt_dev))
+		goto error;
+#endif
+
+	BT_LOG_PR_INFO("%s driver(major %d) installed\n", BT_DRIVER_NAME, BT_major);
+
+#ifdef CONFIG_MTK_CONNSYS_DEDICATED_LOG_PATH
+	fw_log_bt_init();
+#endif
+#if BT_NVRAM_IN_DTBO
+	BT_get_fw_cfg();
+#endif
+	bt_set_fops_state(BTDRV_FOPS_STATE_INIT);
+	return 0;
+
+error:
+#if CREATE_NODE_DYNAMIC
+	if (stpbt_dev && !IS_ERR(stpbt_dev)) {
+		device_destroy(stpbt_class, dev);
+		stpbt_dev = NULL;
+	}
+	if (stpbt_class && !IS_ERR(stpbt_class)) {
+		class_destroy(stpbt_class);
+		stpbt_class = NULL;
+	}
+#endif
+	if (cdev_err == 0)
+		cdev_del(&BT_cdev);
+
+	if (alloc_ret == 0)
+		unregister_chrdev_region(dev, BT_devs);
+
+	return -1;
+}
+
+static void BT_exit(void)
+{
+	dev_t dev;
+
+#ifdef CONFIG_MTK_CONNSYS_DEDICATED_LOG_PATH
+	fw_log_bt_exit();
+#endif
+
+	dev = MKDEV(BT_major, 0);
+	/* Destroy wake lock*/
+	wakeup_source_unregister(bt_wakelock);
+
+#if CREATE_NODE_DYNAMIC
+	if (stpbt_dev && !IS_ERR(stpbt_dev)) {
+		device_destroy(stpbt_class, dev);
+		stpbt_dev = NULL;
+	}
+	if (stpbt_class && !IS_ERR(stpbt_class)) {
+		class_destroy(stpbt_class);
+		stpbt_class = NULL;
+	}
+#endif
+
+	cdev_del(&BT_cdev);
+	unregister_chrdev_region(dev, BT_devs);
+
+	bt_set_fops_state(BTDRV_FOPS_STATE_INIT);
+	BT_LOG_PR_INFO("%s driver removed\n", BT_DRIVER_NAME);
+}
+
+#ifdef MTK_WCN_REMOVE_KERNEL_MODULE
+
+int mtk_wcn_stpbt_drv_init(void)
+{
+	return BT_init();
+}
+EXPORT_SYMBOL(mtk_wcn_stpbt_drv_init);
+
+void mtk_wcn_stpbt_drv_exit(void)
+{
+	return BT_exit();
+}
+EXPORT_SYMBOL(mtk_wcn_stpbt_drv_exit);
+
+#else
+
+module_init(BT_init);
+module_exit(BT_exit);
+
+#endif
diff --git a/src/kernel/modules/connectivity/bt_driver/mt66xx/stp_chrdev_bt_bluez.c b/src/kernel/modules/connectivity/bt_driver/mt66xx/stp_chrdev_bt_bluez.c
new file mode 100644
index 0000000..134604a
--- /dev/null
+++ b/src/kernel/modules/connectivity/bt_driver/mt66xx/stp_chrdev_bt_bluez.c
@@ -0,0 +1,1014 @@
+/*
+* 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.
+*
+* You should have received a copy of the GNU General Public License along with this program.
+* If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/sched.h>
+#include <asm/current.h>
+#include <linux/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/poll.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "wmt_exp.h"
+#include "stp_exp.h"
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define BT_DRIVER_NAME "MTK BT"
+
+#define BTMTK_LOG_LEVEL_ERROR       1
+#define BTMTK_LOG_LEVEL_WARNING     2
+#define BTMTK_LOG_LEVEL_INFO        3
+#define BTMTK_LOG_LEVEL_DEBUG       4
+
+unsigned char btmtk_log_lvl = BTMTK_LOG_LEVEL_INFO;
+
+#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)
+
+struct btmtk_thread {
+	struct task_struct *task;
+	wait_queue_head_t wait_q;
+	void *priv;
+};
+
+struct btmtk_device {
+	void *card;
+	struct hci_dev *hcidev;
+
+	unsigned char dev_type;
+
+	unsigned char tx_dnld_rdy;
+
+	unsigned char psmode;
+	unsigned char pscmd;
+	unsigned char hsmode;
+	unsigned char hscmd;
+
+	/* Low byte is gap, high byte is GPIO */
+	unsigned short gpio_gap;
+
+	unsigned char hscfgcmd;
+	unsigned char sendcmdflag;
+};
+
+struct btmtk_adapter {
+	void *hw_regs_buf;
+	unsigned char *hw_regs;
+	unsigned int int_count;
+	struct sk_buff_head tx_queue;
+	struct sk_buff_head fops_queue;
+	struct sk_buff_head fwlog_fops_queue;
+	struct sk_buff_head fwlog_tx_queue;
+	unsigned char fops_mode;
+	unsigned char psmode;
+	unsigned char ps_state;
+	unsigned char hs_state;
+	unsigned char wakeup_tries;
+	wait_queue_head_t cmd_wait_q;
+	wait_queue_head_t event_hs_wait_q;
+	unsigned char 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 sk_buff *skb);
+	int (*hci_close)(void);
+	int (*sdio_download_fw)(void);
+
+	int (*hw_set_own_back)(int owntype);
+	//int (*hw_wakeup_firmware)(struct btmtk_private *priv,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;
+};
+
+#define VERSION "1.0"
+
+#define BT_BUFFER_SIZE              2048
+
+static struct btmtk_private *btmtk_priv;
+static unsigned char *txbuf = NULL;
+static unsigned char *rxbuf = NULL;
+static unsigned int rx_length = 0;
+static int init_flag = 0;
+
+#define PRINT_BUF(buf, len) \
+do { \
+	int i = 0; \
+	char str[128]; \
+	char *p_str; \
+	for (i = 0; i < len; i += 16) { \
+		p_str = str; \
+		if ((i + 16) <= len) { \
+			p_str += sprintf(p_str, \
+				"%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 { \
+			p_str += sprintf(p_str, "%s:", __func__); \
+			for(; i < len; i++) \
+				p_str += sprintf(p_str, " %02X", buf[i]); \
+			p_str += sprintf(p_str, "\n"); \
+		} \
+		BTMTK_DBG("%s", str); \
+	} \
+}while(0)
+
+/*
+ * Reset flag for whole chip reset scenario, to indicate reset status:
+ *   0 - normal, no whole chip reset occurs
+ *   1 - reset start
+ *   2 - reset end, have not sent Hardware Error event yet
+ *   3 - reset end, already sent Hardware Error event
+ */
+//static DECLARE_WAIT_QUEUE_HEAD(BT_wq);
+static volatile UINT32 rstflag;
+static INT32 flag;
+//static UINT8 HCI_EVT_HW_ERROR[] = {0x04, 0x10, 0x01, 0x00};
+
+/*******************************************************************
+ * WHOLE CHIP RESET message handler
+ *******************************************************************
+*/
+static VOID bt_cdev_rst_cb(ENUM_WMTDRV_TYPE_T src,
+				ENUM_WMTDRV_TYPE_T dst,
+				ENUM_WMTMSG_TYPE_T type,
+				PVOID buf, UINT32 sz)
+{
+	ENUM_WMTRSTMSG_TYPE_T rst_msg;
+
+	if (sz > sizeof(ENUM_WMTRSTMSG_TYPE_T)) {
+		BTMTK_WARN("Invalid message format!\n");
+		return;
+	}
+
+	memcpy((PINT8)&rst_msg, (PINT8)buf, sz);
+	BTMTK_DBG("src = %d, dst = %d, type = %d, buf = 0x%x sz = %d, max = %d\n",
+			src, dst, type, rst_msg, sz, WMTRSTMSG_RESET_MAX);
+	if ((src == WMTDRV_TYPE_WMT) && (dst == WMTDRV_TYPE_BT) && (type == WMTMSG_TYPE_RESET)) {
+		switch (rst_msg) {
+		case WMTRSTMSG_RESET_START:
+			BTMTK_INFO("Whole chip reset start!\n");
+			rstflag = 1;
+			break;
+
+		case WMTRSTMSG_RESET_END:
+		case WMTRSTMSG_RESET_END_FAIL:
+			if (rst_msg == WMTRSTMSG_RESET_END)
+				BTMTK_INFO("Whole chip reset end!\n");
+			else
+				BTMTK_INFO("Whole chip reset fail!\n");
+			rstflag = 2;
+			flag = 1;
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+#define CFG_SUPPORT_NVRAM 1
+#if CFG_SUPPORT_NVRAM
+#define BT_NVRAM_FILE_NAME   "/data/nvram/APCFG/APRDEB/BT_Addr"
+// the record structure define of bt nvram file
+typedef struct
+{
+	unsigned char addr[6];            // BT address
+	unsigned char Voice[2];           // Voice setting for SCO connection
+	unsigned char Codec[4];           // PCM codec setting
+	unsigned char Radio[6];           // RF configuration
+	unsigned char Sleep[7];           // Sleep mode configuration
+	unsigned char BtFTR[2];           // Other feature setting
+	unsigned char TxPWOffset[3];      // TX power channel offset compensation
+	unsigned char CoexAdjust[6];      // BT/WIFI coexistence performance adjustment
+}ap_nvram_btradio_struct;
+
+
+static ap_nvram_btradio_struct stBtDefault =
+{
+	{0x00, 0x00, 0x46, 0x81, 0x67, 0x01},
+	{0x60, 0x00}, //not used
+#if defined(MTK_MERGE_INTERFACE_SUPPORT)
+	{0x63, 0x10, 0x00, 0x00},
+#elif defined(__MTK_BT_I2S_SUPPORT__)
+	{0x03, 0x10, 0x00, 0x02},
+#else
+	{0x23, 0x10, 0x00, 0x00},
+#endif
+	{0x07, 0x80, 0x00, 0x06, 0x05, 0x07},
+	{0x03, 0x40, 0x1F, 0x40, 0x1F, 0x00, 0x04},
+	{0x80, 0x00}, //not used
+	{0xFF, 0xFF, 0xFF},
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+};
+
+static bool cfg_flag;
+static bool rcv_flag;
+static char opcode[2];
+static unsigned char rx_evt[16];
+
+typedef int (*HCI_CMD_FUNC_T)(ap_nvram_btradio_struct *bt_nvm);
+typedef struct {
+	HCI_CMD_FUNC_T command_func;
+}HCI_SEQ_T;
+
+static int btmtk_fw_cfg_set_check(char *cmd, int cmd_len,
+						char *event, int event_len)
+{
+	int retrytime = 60;
+	char str[128];
+	char *p_str;
+	int i;
+
+	PRINT_BUF(cmd, (cmd_len < 16 ? cmd_len : 16));
+
+	if (mtk_wcn_stp_send_data(cmd, cmd_len, BT_TASK_INDX) < 0) {
+		memset(str, 0, sizeof(str));
+		p_str = str;
+		p_str += sprintf(p_str, "%02X", cmd[0]);
+		for (i = 1; i < cmd_len; i++)
+			p_str += sprintf(p_str, " %02X", cmd[i]);
+		BTMTK_ERR("%s: send fail(%s)\n", __func__, str);
+		return -EIO;
+	}
+
+	if (event && (event_len != 0)) {
+		rcv_flag = false;
+		opcode[0] = cmd[1];
+		opcode[1] = cmd[2];
+
+		do {
+			if (rcv_flag == true)
+				break;
+
+			if (retrytime == 0) {
+				BTMTK_ERR("%s: recv %02X%02X fail\n", __func__,
+						cmd[2], cmd[1]);
+				return -EIO;
+			}
+
+			if (retrytime < 40)
+				BTMTK_WARN("%s: retry over 2s, retrytime %d\n",
+					__func__, retrytime);
+			retrytime--;
+			msleep(100);
+		} while(1);
+
+		if (memcmp(rx_evt, event, event_len) != 0) {
+	 		memset(str, 0, sizeof(str));
+			p_str = str;
+			p_str += sprintf(p_str, "%02X", rx_evt[0]);
+			for (i = 1; i < event_len; i++)
+				p_str += sprintf(p_str, " %02X", rx_evt[i]);
+			BTMTK_ERR("%s: check %02X%02X fail(%s)\n", __func__,
+					cmd[2], cmd[1],
+					str);
+			return -EIO;
+		}
+	}
+	return 0;
+}
+
+#ifdef BD_ADDR_AUTOGEN
+static void GetRandomValue(u8 string[6])
+{
+	int iRandom = 0;
+
+	BTMTK_INFO("Enable random generation\n");
+
+	/* first random */
+	get_random_bytes(&iRandom, sizeof(int));
+	BTMTK_INFO("iRandom = [%d]", iRandom);
+	string[0] = (((iRandom>>24|iRandom>>16) & (0xFE)) | (0x02)); /* Must use private bit(1) and no BCMC bit(0) */
+
+	/* second random */
+	get_random_bytes(&iRandom, sizeof(int));
+	BTMTK_INFO("iRandom = [%d]", iRandom);
+	string[1] = ((iRandom>>8) & 0xFF);
+
+	/* third random */
+	get_random_bytes(&iRandom, sizeof(int));
+	BTMTK_INFO("iRandom = [%d]", iRandom);
+	string[5] = (iRandom & 0xFF);
+
+	return;
+}
+#endif
+
+static int nvram_write(char *filename, char *buf, ssize_t len, int offset)
+{
+	struct file *fd;
+	int retLen = -1;
+
+	mm_segment_t old_fs = get_fs();
+
+	set_fs(KERNEL_DS);
+
+	fd = filp_open(filename, O_WRONLY, 0644);
+
+	if (IS_ERR(fd)) {
+		BTMTK_INFO("[nvram_write] : failed to open!!");
+		return -1;
+	}
+
+	fd->f_pos = offset;
+	retLen = vfs_write(fd, buf, len, &fd->f_pos);
+	filp_close(fd, NULL);
+
+	set_fs(old_fs);
+
+	return retLen;
+}
+
+int btmtk_set_cfg_to_nvram(UINT8 *buf, ssize_t len)
+{
+	return nvram_write(BT_NVRAM_FILE_NAME, buf, len, 0);
+}
+
+static int btmtk_set_local_bd_addr(ap_nvram_btradio_struct *bt_nvm)
+{
+	char cmd[] = {0x01, 0x1A, 0xFC, 0x06,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	char event[] = {0x04, 0x0E, 0x04,
+			0x01, 0x1A, 0xFC, 0x00};
+
+	cmd[4] = bt_nvm->addr[5];
+	cmd[5] = bt_nvm->addr[4];
+	cmd[6] = bt_nvm->addr[3];
+	cmd[7] = bt_nvm->addr[2];
+	cmd[8] = bt_nvm->addr[1];
+	cmd[9] = bt_nvm->addr[0];
+
+	return btmtk_fw_cfg_set_check(cmd, sizeof(cmd), event, sizeof(event));
+}
+
+static int btmtk_set_radio(ap_nvram_btradio_struct *bt_nvm)
+{
+	char cmd[] = {0x01, 0x79, 0xFC, 0x06,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	char event[] = {0x04, 0x0E, 0x04,
+			0x01, 0x79, 0xFC, 0x00};
+
+	cmd[4] = bt_nvm->Radio[0];
+	cmd[5] = bt_nvm->Radio[1];
+	cmd[6] = bt_nvm->Radio[2];
+	cmd[7] = bt_nvm->Radio[3];
+	cmd[8] = bt_nvm->Radio[4];
+	cmd[9] = bt_nvm->Radio[5];
+
+	return btmtk_fw_cfg_set_check(cmd, sizeof(cmd), event, sizeof(event));
+}
+
+static int btmtk_set_tx_power_offset(ap_nvram_btradio_struct *bt_nvm)
+{
+	char cmd[] = {0x01, 0x93, 0xFC, 0x03,
+			0x00, 0x00, 0x00};
+	char event[] = {0x04, 0x0E, 0x04,
+			0x01, 0x93, 0xFC, 0x00};
+
+	cmd[4] = bt_nvm->TxPWOffset[0];
+	cmd[5] = bt_nvm->TxPWOffset[1];
+	cmd[6] = bt_nvm->TxPWOffset[2];
+
+	return btmtk_fw_cfg_set_check(cmd, sizeof(cmd), event, sizeof(event));
+}
+
+static int btmtk_set_sleep_timeout(ap_nvram_btradio_struct *bt_nvm)
+{
+	char cmd[] = {0x01, 0x7A, 0xFC, 0x07,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	char event[] = {0x04, 0x0E, 0x04,
+			0x01, 0x7A, 0xFC, 0x00};
+
+	cmd[4] = bt_nvm->Sleep[0];
+	cmd[5] = bt_nvm->Sleep[1];
+	cmd[6] = bt_nvm->Sleep[2];
+	cmd[7] = bt_nvm->Sleep[3];
+	cmd[8] = bt_nvm->Sleep[4];
+	cmd[9] = bt_nvm->Sleep[5];
+	cmd[10] = bt_nvm->Sleep[6];
+
+	return btmtk_fw_cfg_set_check(cmd, sizeof(cmd), event, sizeof(event));
+}
+
+static int btmtk_reset(ap_nvram_btradio_struct *bt_nvm)
+{
+	char cmd[] = {0x01, 0x03, 0x0C, 0x00};
+	char event[] = {0x04, 0x0E, 0x04,
+			0x01, 0x03, 0x0C, 0x00};
+
+	return btmtk_fw_cfg_set_check(cmd, sizeof(cmd), event, sizeof(event));
+}
+
+HCI_SEQ_T bt_init_preload_script[] =
+{
+    {  btmtk_set_local_bd_addr       }, /*0xFC1A*/
+    {  btmtk_set_radio               }, /*0xFC79*/
+    {  btmtk_set_tx_power_offset     }, /*0xFC93*/
+    {  btmtk_set_sleep_timeout       }, /*0xFC7A*/
+    {  btmtk_reset                   }, /*0x0C03*/
+    {  0  },
+};
+
+/*----------------------------------------------------------------------------*/
+/*!
+* \brief Utility function for reading data from files on NVRAM-FS
+*
+* \param[in]
+*           filename
+*           len
+*           offset
+* \param[out]
+*           buf
+* \return
+*           actual length of data being read
+*/
+/*----------------------------------------------------------------------------*/
+static int nvram_read(char *filename, char *buf, ssize_t len, int offset)
+{
+	struct file *fd;
+	int retLen = -1;
+
+	mm_segment_t old_fs = get_fs();
+
+	set_fs(KERNEL_DS);
+
+	fd = filp_open(filename, O_RDONLY, 0644);
+
+	if (IS_ERR(fd)) {
+		BTMTK_INFO("[nvram_read] : failed to open!!");
+		return -1;
+	}
+#if 0
+	do {
+		if ((fd->f_op == NULL) || (fd->f_op->read == NULL)) {
+			BTMTK_INFO("[nvram_read] : file can not be read!!\n");
+			break;
+		}
+
+		if (fd->f_pos != offset) {
+			if (fd->f_op->llseek) {
+				if (fd->f_op->llseek(fd, offset, 0) != offset) {
+					BTMTK_INFO("[nvram_read] : failed to seek!!\n");
+					break;
+				}
+			} else {
+				fd->f_pos = offset;
+			}
+		}
+
+		retLen = fd->f_op->read(fd, buf, len, &fd->f_pos);
+
+	} while (false);
+#else
+	fd->f_pos = offset;
+	retLen = vfs_read(fd, buf, len, &fd->f_pos);
+#endif
+	filp_close(fd, NULL);
+
+	set_fs(old_fs);
+
+	return retLen;
+}
+
+int btmtk_get_cfg_from_nvram(UINT8 *buf, ssize_t len)
+{
+	return nvram_read(BT_NVRAM_FILE_NAME, buf, len, 0);
+}
+
+void btmtk_fw_cfg(void)
+{
+	ap_nvram_btradio_struct bt_nvm;
+	int i = 0;
+#ifdef BD_ADDR_AUTOGEN
+	UINT8 ucZeroAddr[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+#endif
+
+	if (btmtk_get_cfg_from_nvram((UINT8 *)&bt_nvm, sizeof(bt_nvm)) <= 0) {
+		BTMTK_ERR("%s: get configuration failed!\n", __func__);
+		return;
+	}
+	BTMTK_INFO("[BDAddr %02x-%02x-%02x-%02x-%02x-%02x]",
+		bt_nvm.addr[0], bt_nvm.addr[1], bt_nvm.addr[2], bt_nvm.addr[3], bt_nvm.addr[4], bt_nvm.addr[5]);
+	BTMTK_INFO("[Voice %02x %02x]",
+		bt_nvm.Voice[0], bt_nvm.Voice[1]);
+	BTMTK_INFO("[Codec %02x %02x %02x %02x]",
+		bt_nvm.Codec[0], bt_nvm.Codec[1], bt_nvm.Codec[2], bt_nvm.Codec[3]);
+	BTMTK_INFO("[Radio %02x %02x %02x %02x %02x %02x]",
+		bt_nvm.Radio[0], bt_nvm.Radio[1], bt_nvm.Radio[2], bt_nvm.Radio[3], bt_nvm.Radio[4], bt_nvm.Radio[5]);
+	BTMTK_INFO("[Sleep %02x %02x %02x %02x %02x %02x %02x]",
+		bt_nvm.Sleep[0], bt_nvm.Sleep[1], bt_nvm.Sleep[2], bt_nvm.Sleep[3], bt_nvm.Sleep[4], bt_nvm.Sleep[5], bt_nvm.Sleep[6]);
+	BTMTK_INFO("[BtFTR %02x %02x]",
+		bt_nvm.BtFTR[0], bt_nvm.BtFTR[1]);
+	BTMTK_INFO("[TxPWOffset %02x %02x %02x]",
+		bt_nvm.TxPWOffset[0], bt_nvm.TxPWOffset[1], bt_nvm.TxPWOffset[2]);
+	BTMTK_INFO("[CoexAdjust %02x %02x %02x %02x %02x %02x]",
+		bt_nvm.CoexAdjust[0], bt_nvm.CoexAdjust[1], bt_nvm.CoexAdjust[2], bt_nvm.CoexAdjust[3], bt_nvm.CoexAdjust[4], bt_nvm.CoexAdjust[5]);
+
+	cfg_flag = true;
+
+#ifdef BD_ADDR_AUTOGEN
+	if ((0 == memcmp(bt_nvm.addr, stBtDefault.addr, 6)) ||
+		(0 == memcmp(bt_nvm.addr, ucZeroAddr, 6))) {
+		GetRandomValue(bt_nvm.addr);
+		if (btmtk_set_cfg_to_nvram((UINT8 *)&bt_nvm, sizeof(bt_nvm)) <= 0) {
+	                BTMTK_ERR("%s: set configuration failed!\n", __func__);
+	                return;
+		}
+#if 0
+		if (btmtk_get_cfg_from_nvram((UINT8 *)&bt_nvm, sizeof(bt_nvm)) <= 0) {
+			BTMTK_ERR("%s: get configuration failed!\n", __func__);
+			return;
+		}
+		BTMTK_DBG("after read");
+		BTMTK_DBG("[BDAddr %02x-%02x-%02x-%02x-%02x-%02x]",
+				bt_nvm.addr[0], bt_nvm.addr[1], bt_nvm.addr[2], bt_nvm.addr[3], bt_nvm.addr[4], bt_nvm.addr[5]);
+		BTMTK_DBG("[Voice %02x %02x]",
+				bt_nvm.Voice[0], bt_nvm.Voice[1]);
+		BTMTK_DBG("[Codec %02x %02x %02x %02x]",
+				bt_nvm.Codec[0], bt_nvm.Codec[1], bt_nvm.Codec[2], bt_nvm.Codec[3]);
+		BTMTK_DBG("[Radio %02x %02x %02x %02x %02x %02x]",
+				bt_nvm.Radio[0], bt_nvm.Radio[1], bt_nvm.Radio[2], bt_nvm.Radio[3], bt_nvm.Radio[4], bt_nvm.Radio[5]);
+		BTMTK_DBG("[Sleep %02x %02x %02x %02x %02x %02x %02x]",
+				bt_nvm.Sleep[0], bt_nvm.Sleep[1], bt_nvm.Sleep[2], bt_nvm.Sleep[3], bt_nvm.Sleep[4], bt_nvm.Sleep[5], bt_nvm.Sleep[6]);
+		BTMTK_DBG("[BtFTR %02x %02x]",
+				bt_nvm.BtFTR[0], bt_nvm.BtFTR[1]);
+		BTMTK_DBG("[TxPWOffset %02x %02x %02x]",
+				bt_nvm.TxPWOffset[0], bt_nvm.TxPWOffset[1], bt_nvm.TxPWOffset[2]);
+		BTMTK_DBG("[CoexAdjust %02x %02x %02x %02x %02x %02x]",
+				bt_nvm.CoexAdjust[0], bt_nvm.CoexAdjust[1], bt_nvm.CoexAdjust[2], bt_nvm.CoexAdjust[3], bt_nvm.CoexAdjust[4], bt_nvm.CoexAdjust[5]);
+#endif
+	}
+#endif /* BD_ADDR_AUTOGEN */
+
+	while(bt_init_preload_script[i].command_func) {
+		if (bt_init_preload_script[i].command_func(&bt_nvm) < 0)
+			BTMTK_ERR("%s: set fw failed(%d)!", __func__, i);
+		i++;
+	}
+
+	cfg_flag = false;
+}
+#endif
+
+static int btmtk_sdio_host_to_card(struct sk_buff *skb)
+{
+	int ret = 0;
+	int len = 0;
+
+	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 > BT_BUFFER_SIZE)) {
+		BTMTK_WARN("%s Tx Error: Bad skb length %d : %d", __func__,
+			skb->len, BT_BUFFER_SIZE);
+		return -EINVAL;
+	}
+
+	txbuf[0] = bt_cb(skb)->pkt_type;
+	memcpy(&txbuf[1], &skb->data[0], skb->len);
+	len = skb->len + 1;
+	kfree_skb(skb);
+	PRINT_BUF(txbuf, (len < 16 ? len : 16));
+
+	ret = mtk_wcn_stp_send_data(txbuf, len, BT_TASK_INDX);
+
+	return ret;
+}
+
+static int btmtk_sdio_card_to_host(struct btmtk_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	struct hci_dev *hdev;
+	unsigned char *buf;
+	int type;
+	int buf_len, copy_len, hdr_len, payload_len;
+	int err = 0;
+
+	PRINT_BUF(rxbuf, (rx_length < 16 ? rx_length : 16));
+
+	hdev = priv->btmtk_dev.hcidev;
+
+	type = rxbuf[0];
+	buf = rxbuf + 1;
+	buf_len = rx_length - 1;
+
+	switch (type) {
+	case HCI_ACLDATA_PKT:
+		hdr_len = HCI_ACL_HDR_SIZE;
+		payload_len = rxbuf[3] | (rxbuf[4] << 8);
+		break;
+	case HCI_SCODATA_PKT:
+		hdr_len = HCI_SCO_HDR_SIZE;
+		payload_len = rxbuf[3];
+		break;
+	case HCI_EVENT_PKT:
+		hdr_len = HCI_EVENT_HDR_SIZE;
+		payload_len = rxbuf[2];
+		if (rxbuf[4] == 0x04 && rxbuf[5] == 0x10 && rxbuf[6] != 0) {
+			BTMTK_INFO("%s opcode 0x1004, status 0x%x -> 0", __func__, rxbuf[6]);
+			rxbuf[6] = 0;
+		}
+		break;
+	default:
+		BTMTK_WARN("%s Unknown packet type:%d", __func__, type);
+		err = -1;
+		goto exit;
+	}
+
+	while (buf_len) {
+		if (!skb) {
+			skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+			if (!skb) {
+				err = -ENOMEM;
+				break;
+			}
+			bt_cb(skb)->pkt_type = type;
+			bt_cb(skb)->expect = hdr_len;
+		}
+
+		copy_len = min_t(int, bt_cb(skb)->expect, buf_len);
+		memcpy(skb_put(skb, copy_len), buf, copy_len);
+
+		buf += copy_len;
+		buf_len -= copy_len;
+		bt_cb(skb)->expect -= copy_len;
+
+		if (skb->len == hdr_len) {
+			/* Complete header */
+			bt_cb(skb)->expect = payload_len;
+
+			if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+				kfree_skb(skb);
+				skb = NULL;
+				err = -EILSEQ;
+				break;
+			}
+		}
+
+		if (bt_cb(skb)->expect == 0) {
+			/* Complete frame */
+			hci_recv_frame(hdev, skb);
+			skb = NULL;
+		}
+	}
+
+exit:
+	if (err) {
+		BTMTK_DBG("%s fail free skb\n", __func__);
+		hdev->stat.err_rx++;
+		if (skb)
+			kfree_skb(skb);
+	}
+
+	return err;
+}
+
+static VOID btmtk_recv_cb(const PUINT8 data, INT32 size)
+{
+        if (size) {
+		memcpy(rxbuf, data, size);
+		rx_length = size;
+
+#if CFG_SUPPORT_NVRAM
+		if (rcv_flag == false && opcode[0] == rxbuf[4] && opcode[1] == rxbuf[5]) {
+			PRINT_BUF(rxbuf, (rx_length < 16 ? rx_length : 16));
+			memcpy(rx_evt, rxbuf, (rx_length < 16 ? rx_length : 16));
+			rcv_flag = true;
+		} else {
+#endif
+	        btmtk_sdio_card_to_host(btmtk_priv);
+#if CFG_SUPPORT_NVRAM
+		}
+#endif
+	}
+
+	return;
+}
+
+static int btmtk_open(struct hci_dev *hdev)
+{
+	/* Turn on BT */
+#if 0
+	if (mtk_wcn_wmt_func_on(WMTDRV_TYPE_BT) == MTK_WCN_BOOL_FALSE) {
+		BTMTK_WARN("WMT turn on BT fail!\n");
+		return -EIO;
+	}
+#else
+	while (mtk_wcn_wmt_func_on(WMTDRV_TYPE_BT) == MTK_WCN_BOOL_FALSE) {
+		int cnt = 0;
+		BTMTK_WARN("WMT turn on BT fail!, retry %d\n", cnt);
+		cnt++;
+		msleep(1000);
+	}
+#endif
+
+	BTMTK_INFO("WMT turn on BT OK!\n");
+	rstflag = 0;
+
+	if (mtk_wcn_stp_is_ready()) {
+
+		mtk_wcn_stp_set_bluez(1);
+		mtk_wcn_stp_register_if_rx(btmtk_recv_cb);
+		BTMTK_INFO("Now it's in MTK Bluetooth Mode\n");
+		BTMTK_INFO("STP is ready!\n");
+
+		BTMTK_DBG("Register BT event callback!\n");
+		mtk_wcn_stp_register_event_cb(BT_TASK_INDX, NULL);
+	} else {
+		BTMTK_ERR("STP is not ready!\n");
+		mtk_wcn_wmt_func_off(WMTDRV_TYPE_BT);
+		return -EIO;
+	}
+
+	BTMTK_DBG("Register BT reset callback!\n");
+	mtk_wcn_wmt_msgcb_reg(WMTDRV_TYPE_BT, bt_cdev_rst_cb);
+
+        BTMTK_INFO("%s set HCI_RUNNIN %08x\n",__func__, HCI_RUNNING);
+        set_bit(HCI_RUNNING, &hdev->flags);
+
+#if CFG_SUPPORT_NVRAM
+	btmtk_fw_cfg();
+#endif
+	return 0;
+}
+
+static int btmtk_close(struct hci_dev *hdev)
+{
+	BTMTK_INFO("%s \n",__func__);
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) {
+		BTMTK_WARN("%s is not hci running\n",__func__);
+		//return 0;
+	}
+
+	rstflag = 0;
+	mtk_wcn_wmt_msgcb_unreg(WMTDRV_TYPE_BT);
+	mtk_wcn_stp_register_event_cb(BT_TASK_INDX, NULL);
+
+	if (mtk_wcn_wmt_func_off(WMTDRV_TYPE_BT) == MTK_WCN_BOOL_FALSE) {
+		BTMTK_ERR("WMT turn off BT fail!\n");
+		/* Mostly, native program will not check this return value. */
+		return -EIO;
+	}
+
+	BTMTK_INFO("WMT turn off BT OK!\n");
+	return 0;
+}
+static int btmtk_flush(struct hci_dev *hdev)
+{
+        //struct btmtk_private *priv = hci_get_drvdata(hdev);
+
+        //skb_queue_purge(&priv->adapter->tx_queue);
+
+        return 0;
+}
+
+static int btmtk_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	if (!hdev) {
+		BTMTK_INFO("%s hdev=NULL return\n",__func__);
+		return -ENODEV;
+	}
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+		BTMTK_ERR("%s Failed testing HCI_RUNING, flags=%lx\n",
+			__func__, hdev->flags);
+		BTMTK_INFO("%s return -EBUSY\n",__func__);
+		return -EBUSY;
+	}
+
+	BTMTK_DBG("%s type=%d, len=%d\n",__func__, bt_cb(skb)->pkt_type, skb->len);
+
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	}
+
+	btmtk_sdio_host_to_card(skb);
+	return 0;
+}
+
+static int btmtk_setup(struct hci_dev *hdev)
+{
+	BTMTK_INFO("%s \n",__func__);
+
+	BTMTK_INFO("%s set HCI_RUNNIN %08x\n", __func__, HCI_RUNNING);
+	set_bit(HCI_RUNNING, &hdev->flags);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		BTMTK_ERR("%s Failed testing HCI_RUNING, flags=%lx",
+				__func__, hdev->flags);
+	return 0;
+}
+
+static int btmtk_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+	unsigned char set_bdaddr[] = {0x01, 0x1A, 0xFC, 0x06,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	int ret = 0;
+
+	memcpy(&set_bdaddr[4], &bdaddr->b[0], 6);
+
+	PRINT_BUF(set_bdaddr, (sizeof(set_bdaddr) < 16 ? sizeof(set_bdaddr) : 16));
+
+	ret = mtk_wcn_stp_send_data(set_bdaddr, sizeof(set_bdaddr), BT_TASK_INDX);
+	if (ret < 0)
+		BTMTK_ERR("Set BD addr failed!");
+	return ret;
+}
+
+struct btmtk_private *btmtk_add_card(void)
+{
+	struct btmtk_private *priv;
+	BTMTK_INFO("%s -->", __func__);
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		BTMTK_ERR("Can not allocate priv");
+
+	return priv;
+}
+
+int btmtk_remove_card(struct btmtk_private *priv)
+{
+	struct hci_dev *hdev;
+
+	BTMTK_INFO("%s \n",__func__);
+	hdev = priv->btmtk_dev.hcidev;
+
+	hci_unregister_dev(hdev);
+
+	hci_free_dev(hdev);
+
+	priv->btmtk_dev.hcidev = NULL;
+
+	kfree(priv);
+
+	return 0;
+}
+
+int btmtk_register_hdev(struct btmtk_private *priv)
+{
+	struct hci_dev *hdev = NULL;
+	int ret;
+
+	BTMTK_INFO("%s \n",__func__);
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		BTMTK_ERR("Can not allocate HCI device");
+		goto err_hdev;
+	}
+
+	priv->btmtk_dev.hcidev = hdev;
+	hci_set_drvdata(hdev, priv);
+
+	hdev->bus   = HCI_SDIO;
+	hdev->open  = btmtk_open;
+	hdev->close = btmtk_close;
+	hdev->flush = btmtk_flush;
+	hdev->send  = btmtk_send_frame;
+	hdev->setup = btmtk_setup;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,18,0))
+	hdev->set_bdaddr = btmtk_set_bdaddr;
+#endif
+	hdev->dev_type = priv->btmtk_dev.dev_type;
+
+	ret = hci_register_dev(hdev);
+	if (ret < 0) {
+		BTMTK_ERR("Can not register HCI device");
+		goto err_hci_register_dev;
+	}
+
+	return 0;
+
+err_hci_register_dev:
+	hci_free_dev(hdev);
+
+err_hdev:
+	kfree(priv);
+
+	return -ENOMEM;
+}
+
+static int BT_init(void)
+{
+	if (init_flag == 1) {
+		BTMTK_INFO("Already initialized");
+		return 0;
+	}
+
+	BTMTK_INFO();
+#if CFG_SUPPORT_NVRAM
+	cfg_flag = false;
+#endif
+	if (txbuf == NULL) {
+		txbuf = kmalloc(BT_BUFFER_SIZE, GFP_ATOMIC);
+		memset(txbuf, 0, BT_BUFFER_SIZE);
+	}
+
+	if (rxbuf == NULL) {
+		rxbuf = kmalloc(BT_BUFFER_SIZE, GFP_ATOMIC);
+		memset(rxbuf, 0, BT_BUFFER_SIZE);
+	}
+
+	btmtk_priv = btmtk_add_card();
+	if (!btmtk_priv) {
+		BTMTK_ERR("Initializing card failed!");
+		kfree(txbuf);
+		kfree(rxbuf);
+		return -ENODEV;
+	}
+
+	btmtk_register_hdev(btmtk_priv);
+
+	init_flag = 1;
+
+	return 0;
+}
+
+static void BT_exit(void)
+{
+	if (init_flag != 1) {
+		BTMTK_INFO("No initialize");
+		return;
+	}
+
+	btmtk_remove_card(btmtk_priv);
+	kfree(txbuf);
+	kfree(rxbuf);
+	init_flag = 0;
+	BTMTK_INFO("%s driver removed\n", BT_DRIVER_NAME);
+}
+
+#ifdef MTK_WCN_REMOVE_KERNEL_MODULE
+int mtk_wcn_stpbt_drv_init(void)
+{
+	return BT_init();
+}
+EXPORT_SYMBOL(mtk_wcn_stpbt_drv_init);
+
+void mtk_wcn_stpbt_drv_exit(void)
+{
+	return BT_exit();
+}
+EXPORT_SYMBOL(mtk_wcn_stpbt_drv_exit);
+
+#else
+
+module_init(BT_init);
+module_exit(BT_exit);
+
+#endif