[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__ */