Project import generated by Copybara.

GitOrigin-RevId: c415cd6adaf576dac916f3adff080fcd4ac23400
diff --git a/bt_sd8897/Makefile b/bt_sd8897/Makefile
new file mode 100644
index 0000000..444c953
--- /dev/null
+++ b/bt_sd8897/Makefile
@@ -0,0 +1,169 @@
+# File: Makefile
+# Copyright (C) 2007-2018, Marvell International Ltd.
+#
+
+CC=		$(CROSS_COMPILE)gcc
+LD=		$(CROSS_COMPILE)ld
+
+BACKUP=		/root/backup
+YMD=		`date +%Y%m%d%H%M`
+
+#############################################################################
+# Configuration Options
+#############################################################################
+
+# Debug Option
+# DEBUG LEVEL n/1/2:
+# n: NO DEBUG
+# 1: PRINTM(MSG,...), PRINTM(FATAL,...), PRINTM(WARN,...) and PRINTM(INFO,...)
+# 2: All PRINTM()
+CONFIG_DEBUG=1
+
+
+# SDIO suspend/resume
+CONFIG_SDIO_SUSPEND_RESUME=y
+
+
+CONFIG_BLE_WAKEUP=y
+
+#############################################################################
+# Select Platform Tools
+#############################################################################
+
+MODEXT = ko
+
+ifeq ($(CONFIG_64BIT), y)
+	EXTRA_CFLAGS += -DMBT_64BIT
+endif
+
+ifeq ($(CONFIG_T50), y)
+        EXTRA_CFLAGS += -DT50
+        EXTRA_CFLAGS += -DT40
+        EXTRA_CFLAGS += -DT3T
+endif
+
+ifeq ($(CONFIG_BLE_WAKEUP), y)
+        EXTRA_CFLAGS += -DBLE_WAKEUP
+endif
+
+
+
+
+
+
+ARCH ?= arm
+KERNELDIR ?= /usr/src/arm/linux-4.9.70-bg4ct
+CROSS_COMPILE ?= /usr/local/gcc-linaro-5.3-2016.02-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+
+EXTRA_CFLAGS += -I$(KERNELDIR)/include
+
+EXTRA_CFLAGS += -I$(M)/../mbtchar_src
+EXTRA_CFLAGS += -I$(M)/bt
+LD += -S
+
+#ifdef SD8xxx
+BINDIR = ../bin_sd8xxx_btchar
+#endif
+
+
+#############################################################################
+# Compiler Flags
+#############################################################################
+	EXTRA_CFLAGS += -DFPNUM='"28"'
+
+ifeq ($(CONFIG_DEBUG),1)
+	EXTRA_CFLAGS += -DDEBUG_LEVEL1
+endif
+
+ifeq ($(CONFIG_DEBUG),2)
+	EXTRA_CFLAGS += -DDEBUG_LEVEL1
+	EXTRA_CFLAGS += -DDEBUG_LEVEL2
+	DBG=	-dbg
+endif
+
+ifeq ($(CONFIG_SDIO_SUSPEND_RESUME),y)
+	EXTRA_CFLAGS += -DSDIO_SUSPEND_RESUME
+endif
+
+#############################################################################
+# Make Targets
+#############################################################################
+
+ifneq ($(KERNELRELEASE),)
+
+BTOBJS = bt/bt_main.o bt/bt_sdiommc.o bt/bt_proc.o bt/mbt_char.o
+
+BTOBJS += bt/bt_init.o
+
+obj-m := mbt8897.o
+mbt8897-objs := $(BTOBJS)
+
+
+
+# Otherwise we were called directly from the command line; invoke the kernel build system.
+else
+default:
+	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules
+endif
+
+###############################################################
+
+export		CC LD EXTRA_CFLAGS KERNELDIR
+
+.PHONY: app/fm_app clean distclean
+
+app/fm_app:
+	$(MAKE) -C  $@
+
+echo:
+
+build:		echo default
+
+	@if [ ! -d $(BINDIR) ]; then \
+		mkdir $(BINDIR); \
+	fi
+
+	cp -f mbt8xxx.$(MODEXT) $(BINDIR)/mbt8xxx$(DBG).$(MODEXT)
+	cp -r config $(BINDIR)
+
+
+
+
+
+	cp -f README $(BINDIR)
+
+	$(MAKE) -C app/fm_app $@ INSTALLDIR=$(BINDIR);
+	cp -f app/fm_app/fmapp $(BINDIR);
+
+clean:
+	-find . -name "*.o" -exec rm {} \;
+	-find . -name "*.ko" -exec rm {} \;
+	-find . -name ".*.cmd" -exec rm {} \;
+	-find . -name "*.mod.c" -exec rm {} \;
+	-find . -name "*.symvers" -exec rm {} \;
+	-find . -name "modules.order" -exec rm {} \;
+	-find . -name ".*.dwo" -exec rm {} \;
+	-find . -name "*dwo" -exec rm {} \;
+	-rm -rf .tmp_versions
+	$(MAKE) -C app/fm_app $@
+
+install: default
+
+distclean:
+	-find . -name "*.o" -exec rm {} \;
+	-find . -name "*.orig" -exec rm {} \;
+	-find . -name "*.swp" -exec rm {} \;
+	-find . -name "*.*~" -exec rm {} \;
+	-find . -name "*~" -exec rm {} \;
+	-find . -name "*.d" -exec rm {} \;
+	-find . -name "*.a" -exec rm {} \;
+	-find . -name "tags" -exec rm {} \;
+	-find . -name ".*" -exec rm -rf 2> /dev/null \;
+	-find . -name "*.ko" -exec rm {} \;
+	-find . -name ".*.cmd" -exec rm {} \;
+	-find . -name "*.mod.c" -exec rm {} \;
+	-find . -name ".*.dwo" -exec rm {} \;
+	-find . -name "*dwo" -exec rm {} \;
+	-rm -rf .tmp_versions
+	$(MAKE) -C app/fm_app $@
+# End of file;
diff --git a/bt_sd8897/README b/bt_sd8897/README
new file mode 100644
index 0000000..ed48a72
--- /dev/null
+++ b/bt_sd8897/README
@@ -0,0 +1,224 @@
+===============================================================================
+			U S E R  M A N U A L
+
+ Copyright (C) 2003-2016, Marvell International Ltd.
+
+ This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+
+===============================================================================
+
+1) FOR DRIVER BUILD
+
+	Goto source code directory mbtc_src/.
+		make [clean] build
+	The driver binary can be found in ../bin_xxxx_btchar directory.
+
+2) FOR DRIVER INSTALL
+
+	a) Copy sd8787.bin | ... to /lib/firmware/mrvl/ directory,
+	   create the directory if it doesn't exist.
+	b) Install bluetooth driver,
+		insmod bt8787.ko | mbt8787.ko | ... [fw_name=mrvl/sd8xxx.bin]
+	c) Uninstall bluetooth driver,
+		rmmod bt8xxx | mbt8xxx
+
+	There are some other parameters for debugging purpose etc. Use modinfo to check details.
+	  The bit settings of drv_mode are,
+		Bit 0: BT/AMP/BLE
+		Bit 1: FM
+		Bit 2: NFC
+	  mbt_drvdbg=<bit mask of driver debug message control>
+	  psmode=1|0 <enable PS mode (default) | disable PS mode>
+	  dts_enable=1|0 <Disable DTS | Enable DTS (default)>
+	  bt_name=<BT interface name>
+	  fm_name=<FM interface name>
+	  nfc_name=<NFC interface name>
+	  debug_intf=1|0 <Enable debug interface (default) | Disable debug interface>
+	  debug_name=<Debug interface name>
+	  mbt_pm_keep_power=1|0 <PM keep power in suspend (default) | PM no power in suspend>
+	  fw=0|other <Skip firmware download | Download firmware (default)>
+	  bt_req_fw_nowait=0|1 <use request_firmware API (default) | use request_firmware_nowait API>
+	  init_cfg=<init config (MAC addresses, registers etc.) file name>
+		e.g. copy bt_init_cfg.conf to firmware directory, init_cfg=mrvl/bt_init_cfg.conf
+      bt_mac=xx:xx:xx:xx:xx:xx <override the MAC address (in hex)>
+      cal_cfg=<BT CAL data config file name>
+		e.g. copy bt_cal_data.conf to firmware directory, cal_cfg=mrvl/bt_cal_data.conf
+      cal_cfg_ext=<CAL data config file name>
+		e.g. copy cal_data.conf to firmware directory, cal_cfg_ext=mrvl/cal_data.conf
+	  mbt_gpio_pin=<GPIO pin to interrupt host. 0xFFFF: disable GPIO interrupt mode; Others: GPIO pin assigned to generate pulse to host.>
+
+	btindrst = Independent reset configuration; high byte:GPIOpin;low byte:MODE
+
+	<MODE> :
+		0x00 : disable independent reset
+		0x01 : enable out-band gpio independent reset.
+		0x02 : enable in-band independent reset
+	<GPIOpin> :
+		0xFF : default GPIO pins will be used. Currently for BT it is GPIO[15].
+		0xXX : specified GPIO pin number will be used for out-band reset.
+
+	Example:
+		btindrst=0x0e01        : outband-reset, gpio pin 14
+		btindrst=0xff01        : outband-reset, use firmware default GPIO pin
+
+	Note: On some platforms (e.g. PXA910/920) double quotation marks ("") need to used
+	for module parameters.
+		insmod mbt8xxx.ko "<para1> <para2> ..."
+
+3) cat /proc/mbt/mbtcharx/status
+	This command is used to get driver status.
+
+4) cat /proc/mbt/mbtcharx/config
+	This command is used to get the current driver settings.
+
+5) proc commands to config bluetooth parameters
+
+mbt_drvdbg=[n]
+	This command is used to set the bit mask of driver debug message control.
+
+	bit 0:  MSG  		PRINTM(MSG,...)
+	bit 1:  FATAL		PRINTM(FATAL,...)
+	bit 2:  ERROR		PRINTM(ERROR,...)
+	bit 3:  DATA 		PRINTM(DATA,...)
+	bit 4:  CMD  		PRINTM(CMD,...)
+	bit 5:  EVENT		PRINTM(EVENT,...)
+	bit 6:  INTR		PRINTM(INTR,...)
+	...
+	bit 16: DAT_D		PRINTM(DAT_D,...), DBG_HEXDUMP(DAT_D,...)
+	bit 17: CMD_D		PRINTM(CMD_D,...), DBG_HEXDUMP(CMD_D,...)
+	...
+	bit 28: ENTRY		PRINTM(ENTRY,...), ENTER(), LEAVE()
+	bit 29: WARN 		PRINTM(WARN,...)
+	bit 30: INFO 		PRINTM(INFO,...)
+
+	Usage:
+		echo "drvdbg=0x7" > /proc/mbt/mbtcharx/config		#enable MSG,FATAL,ERROR messages
+
+gpio_gap=[n]
+	This command is used to configure the host sleep parameters.
+
+	bit 8:0  -- Gap
+	bit 16:8 -- GPIO
+	where GPIO is the pin number of GPIO used to wakeup the host. It could be any valid
+		GPIO pin# (e.g. 0-7) or 0xff (Interface, e.g. SDIO will be used instead).
+	where Gap is the gap in milliseconds between wakeup signal and wakeup event
+		or 0xff for special setting when GPIO is used to wakeup host.
+
+	Usage:
+		echo "gpio_gap=0xff80" > /proc/mbt/mbtcharx/config   	# use Interface (e.g. SDIO)
+		echo "hscfgcmd=1" > /proc/mbt/mbtcharx/config		# gap = 0x80
+
+		echo "gpio_gap=0x03ff" > /proc/mbt/mbtcharx/config   	# use gpio 3
+		echo "hscfgcmd=1" > /proc/mbt/mbtcharx/config		# and special host sleep mode
+
+psmode=[n]
+	This command is used to enable/disable auto sleep mode
+
+	where the option is:
+			1 	-- Enable auto sleep mode
+			0 	-- Disable auto sleep mode
+
+	Usage:
+		echo "psmode=1" > /proc/mbt/mbtcharx/config			#enable power save mode
+		echo "idle_timeout=0x0100" > /proc/mbt/mbtcharx/config		#configure idle, timeout value in ms
+		echo "pscmd=1" > /proc/mbt/mbtcharx/config
+
+		echo "psmode=0" > /proc/mbt/mbtcharx/config			#disable power save mode
+		echo "pscmd=1" > /proc/mbt/mbtcharx/config
+
+sdio_pull_cfg=[n]
+	This command is used to configure the delay values for pull up and pull down the SDIO lines.
+
+   	where value is:
+	        bit 15:0  -- Pull up delay in microsecond
+	        bit 31:16 -- Pull down delay in microsecond
+	        0xffff disables PullUp and PullDown in BT controller
+
+	Usage:
+		echo "sdio_pull_cfg=0x00020002" > /proc/mbt/mbtcharx/config   	# Enable sdio pull control
+		echo "sdio_pull_ctrl=1" > /proc/mbt/mbtcharx/config             # configure sdio pull up delay to 2 microseconds
+                                                                        # configure sdio pull down delay to 2 microseconds
+
+		echo "sdio_pull_cfg=0xffffffff" > /proc/mbt/mbtcharx/config   	# Disable sdio pull control
+		echo "sdio_pull_ctrl=1" > /proc/mbt/mbtcharx/config
+
+6) cat /proc/mbt/mbtcharx/debug
+	This command is used to get driver debug parameters.
+
+7) proc command to config debug parameters
+
+sdcmd52rw=<func> <reg> [data]
+	This command is used to read/write a controller register in
+	Secure Digital I/O Interfaces.
+
+	func: The function number to use (0-7)
+	reg:  The address of the register
+	data: The value to write, read if the value is absent
+
+	For SDIO MMC driver, only function 0 and BT function (2/3) access is allowed.
+	And there is a limitation for function 0 write, only vendor specific CCCR
+	registers (0xf0 -0xff) are permiited.
+
+	Usage:
+		echo "sdcmd52rw= 2 3 0xf" > /proc/mbt/mbtcharx/debug	# write 0xf to func 2 address 3
+		echo "sdcmd52rw= 0 4" > /proc/mbt/mbtcharx/debug    	# read func 0 address 4
+
+Issue debug_dump command through proc.
+	Usage:
+		echo "debug_dump" > /proc/mbt/mbtcharx/debug		# dump driver internal debug status.
+	Use dmesg or cat /var/log/debug to check driver debug messages.
+
+proc command to enable BT test mode
+    Usage:
+        echo "test_mode=1" > /proc/mbt/mbtcharx/config   #enable BT test mode
+
+FOR FW RELOAD
+    a) Enable parallel firmware download in driver parameter
+       insmod bt8xxx.ko fw_serail=0
+
+    b) default fw name for parallel firmware download
+       sd8887_bt_a2.bin
+
+    c) Trigger FW reload
+	echo "fw_reload=1" > /proc/mbt/hcix/debug    		    # start inband reset and fw reload.
+	echo "fw_reload=2" > proc/mbt/hcix/debug       		    # start fw reload
+
+    (Note: This feature should works on SD8977/SD8997 board,
+           For CAC-A2 board, only works on the board which supports parallel fw download)
+
+
+==============================================================================
+			U S E R  M A N U A L  F O R  F M A P P
+
+1) FOR TOOL BUILD
+
+	a) Enter directory
+	b) make
+	c) After building, the executable binary "fmapp" is in the directory
+
+2) FOR TOOL RUN
+
+	a) chmod 777 fmapp (optional)
+	b) Run fmapp utility based on usage below
+		Usage: fmapp <Options> devicename ogf ocf [command content]
+		devicename example: mfmchar0 /mnfccahr0
+		FM ogf/ocf example: 0x3f 0x280
+		NFC ogf/ocf example: 0x3f 0x281
+	Options:
+		-h: Display help
+		-v: Verbose
+
+3) TEST EXAMPLES
+    ./fmapp mfmchar0 0x3f 0x280 0x01 0x01
+    ./fmapp mnfcchar0 0x3f 0x281 0x20 0x00 0x01 0x01
diff --git a/bt_sd8897/bt/bt_drv.h b/bt_sd8897/bt/bt_drv.h
new file mode 100644
index 0000000..69b801f
--- /dev/null
+++ b/bt_sd8897/bt/bt_drv.h
@@ -0,0 +1,901 @@
+/** @file bt_drv.h
+ *  @brief This header file contains global constant/enum definitions,
+ *  global variable declaration.
+ *
+ *  Copyright (C) 2007-2018, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _BT_DRV_H_
+#define _BT_DRV_H_
+
+#include <linux/version.h>
+#include <linux/kthread.h>
+#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
+
+#include "hci_wrapper.h"
+
+/** MAX adapter BT driver supported */
+#define MAX_BT_ADAPTER    3
+
+#ifndef BIT
+/** BIT definition */
+#define BIT(x) (1UL << (x))
+#endif
+
+#ifdef MBT_64BIT
+typedef u64 t_ptr;
+#else
+typedef u32 t_ptr;
+#endif
+
+/** max number of adapter supported */
+#define MAX_BT_ADAPTER      3
+/** Define drv_mode bit */
+#define DRV_MODE_BT         BIT(0)
+#define DRV_MODE_NFC       BIT(2)
+
+/** Define devFeature bit */
+#define DEV_FEATURE_BT     BIT(0)
+#define DEV_FEATURE_BTAMP     BIT(1)
+#define DEV_FEATURE_BLE     BIT(2)
+#define DEV_FEATURE_NFC     BIT(4)
+
+/** Define maximum number of radio func supported */
+#define MAX_RADIO_FUNC     4
+
+/** MAC address print format */
+#ifndef MACSTR
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+#endif
+
+/** MAC address print arguments */
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#endif
+
+/** Debug level : Message */
+#define	DBG_MSG			BIT(0)
+/** Debug level : Fatal */
+#define DBG_FATAL		BIT(1)
+/** Debug level : Error */
+#define DBG_ERROR		BIT(2)
+/** Debug level : Data */
+#define DBG_DATA		BIT(3)
+/** Debug level : Command */
+#define DBG_CMD			BIT(4)
+/** Debug level : Event */
+#define DBG_EVENT		BIT(5)
+/** Debug level : Interrupt */
+#define DBG_INTR		BIT(6)
+
+/** Debug entry : Data dump */
+#define DBG_DAT_D		BIT(16)
+/** Debug entry : Data dump */
+#define DBG_CMD_D		BIT(17)
+
+/** Debug level : Entry */
+#define DBG_ENTRY		BIT(28)
+/** Debug level : Warning */
+#define DBG_WARN		BIT(29)
+/** Debug level : Informative */
+#define DBG_INFO		BIT(30)
+
+#ifdef	DEBUG_LEVEL1
+extern u32 mbt_drvdbg;
+
+#ifdef	DEBUG_LEVEL2
+/** Print informative message */
+#define	PRINTM_INFO(msg...)  \
+	do {if (mbt_drvdbg & DBG_INFO)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print warning message */
+#define	PRINTM_WARN(msg...) \
+	do {if (mbt_drvdbg & DBG_WARN)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print entry message */
+#define	PRINTM_ENTRY(msg...) \
+	do {if (mbt_drvdbg & DBG_ENTRY) \
+		printk(KERN_DEBUG msg); } while (0)
+#else
+/** Print informative message */
+#define	PRINTM_INFO(msg...)  do {} while (0)
+/** Print warning message */
+#define	PRINTM_WARN(msg...)  do {} while (0)
+/** Print entry message */
+#define	PRINTM_ENTRY(msg...) do {} while (0)
+#endif /* DEBUG_LEVEL2 */
+
+/** Print interrupt message */
+#define	PRINTM_INTR(msg...)  \
+	do {if (mbt_drvdbg & DBG_INTR)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print event message */
+#define	PRINTM_EVENT(msg...) \
+	do {if (mbt_drvdbg & DBG_EVENT) \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print command message */
+#define	PRINTM_CMD(msg...)   \
+	do {if (mbt_drvdbg & DBG_CMD)   \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print data message */
+#define	PRINTM_DATA(msg...)  \
+	do {if (mbt_drvdbg & DBG_DATA)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print error message */
+#define	PRINTM_ERROR(msg...) \
+	do {if (mbt_drvdbg & DBG_ERROR) \
+		printk(KERN_ERR msg); } while (0)
+/** Print fatal message */
+#define	PRINTM_FATAL(msg...) \
+	do {if (mbt_drvdbg & DBG_FATAL) \
+		printk(KERN_ERR msg); } while (0)
+/** Print message */
+#define	PRINTM_MSG(msg...)   \
+	do {if (mbt_drvdbg & DBG_MSG)   \
+		printk(KERN_ALERT msg); } while (0)
+
+/** Print data dump message */
+#define	PRINTM_DAT_D(msg...)  \
+	do {if (mbt_drvdbg & DBG_DAT_D)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print data dump message */
+#define	PRINTM_CMD_D(msg...)  \
+	do {if (mbt_drvdbg & DBG_CMD_D)  \
+		printk(KERN_DEBUG msg); } while (0)
+
+/** Print message with required level */
+#define	PRINTM(level, msg...) PRINTM_##level(msg)
+
+/** Debug dump buffer length */
+#define DBG_DUMP_BUF_LEN	64
+/** Maximum number of dump per line */
+#define MAX_DUMP_PER_LINE	16
+/** Maximum data dump length */
+#define MAX_DATA_DUMP_LEN	48
+
+/**
+ * @brief Prints buffer data upto provided length
+ *
+ * @param prompt          Char pointer
+ * @param buf			  Buffer
+ * @param len    		  Length
+ *
+ * @return                N/A
+ */
+static inline void
+hexdump(char *prompt, u8 *buf, int len)
+{
+	int i;
+	char dbgdumpbuf[DBG_DUMP_BUF_LEN];
+	char *ptr = dbgdumpbuf;
+
+	printk(KERN_DEBUG "%s: len=%d\n", prompt, len);
+	for (i = 1; i <= len; i++) {
+		ptr += snprintf(ptr, 4, "%02x ", *buf);
+		buf++;
+		if (i % MAX_DUMP_PER_LINE == 0) {
+			*ptr = 0;
+			printk(KERN_DEBUG "%s\n", dbgdumpbuf);
+			ptr = dbgdumpbuf;
+		}
+	}
+	if (len % MAX_DUMP_PER_LINE) {
+		*ptr = 0;
+		printk(KERN_DEBUG "%s\n", dbgdumpbuf);
+	}
+}
+
+/** Debug hexdump of debug data */
+#define DBG_HEXDUMP_DAT_D(x, y, z) \
+	do {if (mbt_drvdbg & DBG_DAT_D) \
+		hexdump(x, y, z); } while (0)
+/** Debug hexdump of debug command */
+#define DBG_HEXDUMP_CMD_D(x, y, z) \
+	do {if (mbt_drvdbg & DBG_CMD_D) \
+		hexdump(x, y, z); } while (0)
+
+/** Debug hexdump */
+#define	DBG_HEXDUMP(level, x, y, z)    DBG_HEXDUMP_##level(x, y, z)
+
+/** Mark entry point */
+#define	ENTER()			PRINTM(ENTRY, "Enter: %s, %s:%i\n", __func__, \
+							__FILE__, __LINE__)
+/** Mark exit point */
+#define	LEAVE()			PRINTM(ENTRY, "Leave: %s, %s:%i\n", __func__, \
+							__FILE__, __LINE__)
+#else
+/** Do nothing */
+#define	PRINTM(level, msg...) do {} while (0)
+/** Do nothing */
+#define DBG_HEXDUMP(level, x, y, z)    do {} while (0)
+/** Do nothing */
+#define	ENTER()  do {} while (0)
+/** Do nothing */
+#define	LEAVE()  do {} while (0)
+#endif /* DEBUG_LEVEL1 */
+
+/** Bluetooth upload size */
+#define	BT_UPLD_SIZE				2312
+/** Bluetooth status success */
+#define BT_STATUS_SUCCESS			(0)
+/** Bluetooth status pending */
+#define BT_STATUS_PENDING           (1)
+/** Bluetooth status failure */
+#define BT_STATUS_FAILURE			(-1)
+
+#ifndef	TRUE
+/** True value */
+#define TRUE			1
+#endif
+#ifndef	FALSE
+/** False value */
+#define	FALSE			0
+#endif
+
+/** Set thread state */
+#define OS_SET_THREAD_STATE(x)		set_current_state(x)
+/** Time to wait until Host Sleep state change in millisecond */
+#define WAIT_UNTIL_HS_STATE_CHANGED 2000
+/** Time to wait cmd resp in millisecond */
+#define WAIT_UNTIL_CMD_RESP	    5000
+
+/** Sleep until a condition gets true or a timeout elapses */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+#define os_wait_interruptible_timeout(waitq, cond, timeout) \
+	interruptible_sleep_on_timeout(&waitq, ((timeout) * HZ / 1000))
+#else
+#define os_wait_interruptible_timeout(waitq, cond, timeout) \
+	wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000))
+#endif
+
+/** bt thread structure */
+typedef struct {
+	/** Task */
+	struct task_struct *task;
+	/** Queue */
+	wait_queue_head_t waitQ;
+	/** PID */
+	pid_t pid;
+	/** Private structure */
+	void *priv;
+} bt_thread;
+
+/**
+ * @brief Activates bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline void
+bt_activate_thread(bt_thread *thr)
+{
+	/** Initialize the wait queue */
+	init_waitqueue_head(&thr->waitQ);
+
+	/** Record the thread pid */
+	thr->pid = current->pid;
+}
+
+/**
+ * @brief De-activates bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline void
+bt_deactivate_thread(bt_thread *thr)
+{
+	thr->pid = 0;
+	return;
+}
+
+/**
+ * @brief Creates bt thread
+ *
+ * @param btfunc          Function pointer
+ * @param thr			  A pointer to bt_thread structure
+ * @param name    		  Char pointer
+ *
+ * @return                N/A
+ */
+static inline void
+bt_create_thread(int (*btfunc) (void *), bt_thread *thr, char *name)
+{
+	thr->task = kthread_run(btfunc, thr, "%s", name);
+}
+
+/**
+ * @brief Delete bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline int
+bt_terminate_thread(bt_thread *thr)
+{
+	/* Check if the thread is active or not */
+	if (!thr->pid)
+		return -1;
+
+	kthread_stop(thr->task);
+	return 0;
+}
+
+/**
+ * @brief  Set scheduled timeout
+ *
+ * @param millisec		 Time unit in ms
+ *
+ * @return                N/A
+ */
+static inline void
+os_sched_timeout(u32 millisec)
+{
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	schedule_timeout((millisec * HZ) / 1000);
+}
+
+#ifndef __ATTRIB_ALIGN__
+#define __ATTRIB_ALIGN__ __attribute__((aligned(4)))
+#endif
+
+#ifndef __ATTRIB_PACK__
+#define __ATTRIB_PACK__ __attribute__((packed))
+#endif
+
+/** Data structure for the Marvell Bluetooth device */
+typedef struct _bt_dev {
+	/** device name */
+	char name[DEV_NAME_LEN];
+	/** card pointer */
+	void *card;
+	/** IO port */
+	u32 ioport;
+	/** m_dev structure */
+	struct m_dev m_dev[MAX_RADIO_FUNC];
+
+	/** Tx download ready flag */
+	u8 tx_dnld_rdy;
+	/** Function */
+	u8 fn;
+	/** Rx unit */
+	u8 rx_unit;
+	/** Power Save mode : Timeout configuration */
+	u16 idle_timeout;
+	/** Power Save mode */
+	u8 psmode;
+	/** Power Save command */
+	u8 pscmd;
+	/** Host Sleep mode */
+	u8 hsmode;
+	/** Host Sleep command */
+	u8 hscmd;
+	/** Low byte is gap, high byte is GPIO */
+	u16 gpio_gap;
+	/** Host Sleep configuration command */
+	u8 hscfgcmd;
+	/** Host Send Cmd Flag		 */
+	u8 sendcmdflag;
+	/** opcode for Send Cmd */
+	u16 send_cmd_opcode;
+	/** Device Type			*/
+	u8 devType;
+	/** Device Features    */
+	u8 devFeature;
+	/** cmd52 function */
+	u8 cmd52_func;
+	/** cmd52 register */
+	u8 cmd52_reg;
+	/** cmd52 value */
+	u8 cmd52_val;
+	/** SDIO pull control command */
+	u8 sdio_pull_ctrl;
+	/** Low 2 bytes is pullUp, high 2 bytes for pull-down */
+	u32 sdio_pull_cfg;
+	/** Test mode command */
+	u8 test_mode;
+} bt_dev_t, *pbt_dev_t;
+
+/** Marvell bt adapter structure */
+typedef struct _bt_adapter {
+	/** Chip revision ID */
+	u8 chip_rev;
+    /** magic val */
+	u8 magic_val;
+	/** Surprise removed flag */
+	u8 SurpriseRemoved;
+	/** IRQ number */
+	int irq;
+	/** Interrupt counter */
+	u32 IntCounter;
+	/** Tx packet queue */
+	struct sk_buff_head tx_queue;
+
+	/** Pointer of fw dump file name */
+	char *fwdump_fname;
+	/** Fw dump queue */
+	struct sk_buff_head fwdump_queue;
+	/** Pending Tx packet queue */
+	struct sk_buff_head pending_queue;
+	/** tx lock flag */
+	u8 tx_lock;
+	/** Power Save mode */
+	u8 psmode;
+	/** Power Save state */
+	u8 ps_state;
+	/** Host Sleep state */
+	u8 hs_state;
+	/** hs skip count */
+	u32 hs_skip;
+	/** suspend_fail flag */
+	u8 suspend_fail;
+	/** suspended flag */
+	u8 is_suspended;
+	/** Number of wakeup tries */
+	u8 WakeupTries;
+	/** Host Sleep wait queue */
+	wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__;
+	/** Host Cmd complet state */
+	u8 cmd_complete;
+	/** last irq recv */
+	u8 irq_recv;
+	/** last irq processed */
+	u8 irq_done;
+	/** sdio int status */
+	u8 sd_ireg;
+     /** buf allocated for transmit */
+	u8 *tx_buffer;
+    /** buf for transmit */
+	u8 *tx_buf;
+    /** buf allocated for read interrupt status */
+	u8 *hw_regs_buf;
+    /** buf for read interrupt status */
+	u8 *hw_regs;
+	/** tx pending */
+	u32 skb_pending;
+/** Version string buffer length */
+#define MAX_VER_STR_LEN         128
+	/** Driver version */
+	u8 drv_ver[MAX_VER_STR_LEN];
+	/** Number of command timeout */
+	u32 num_cmd_timeout;
+} bt_adapter, *pbt_adapter;
+
+/** Length of prov name */
+#define PROC_NAME_LEN				32
+
+/** Item data structure */
+struct item_data {
+	/** Name */
+	char name[PROC_NAME_LEN];
+	/** Size */
+	u32 size;
+	/** Address */
+	t_ptr addr;
+	/** Offset */
+	u32 offset;
+	/** Flag */
+	u32 flag;
+};
+
+/** Proc private data structure */
+struct proc_private_data {
+	/** Name */
+	char name[PROC_NAME_LEN];
+	/** File flag */
+	u32 fileflag;
+	/** Buffer size */
+	u32 bufsize;
+	/** Number of items */
+	u32 num_items;
+	/** Item data */
+	struct item_data *pdata;
+	/** Private structure */
+	struct _bt_private *pbt;
+	/** File operations */
+	const struct file_operations *fops;
+};
+
+/** Device proc structure */
+struct device_proc {
+	/** Proc directory entry */
+	struct proc_dir_entry *proc_entry;
+	/** num of proc files */
+	u8 num_proc_files;
+	/** pointer to proc_private_data */
+	struct proc_private_data *pfiles;
+};
+
+/** Private structure for the MV device */
+typedef struct _bt_private {
+	/** Bluetooth device */
+	bt_dev_t bt_dev;
+	/** Adapter */
+	bt_adapter *adapter;
+	/** Firmware helper */
+	const struct firmware *fw_helper;
+	/** Firmware */
+	const struct firmware *firmware;
+	/** Init user configure file */
+	const struct firmware *init_user_cfg;
+	/** Init user configure wait queue token */
+	u16 init_user_conf_wait_flag;
+	/** Init user configure file wait queue */
+	wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__;
+	/** Firmware request start time */
+	struct timeval req_fw_time;
+	/** Hotplug device */
+	struct device *hotplug_device;
+	/** thread to service interrupts */
+	bt_thread MainThread;
+	 /** proc data */
+	struct device_proc dev_proc[MAX_RADIO_FUNC];
+	/** Driver lock */
+	spinlock_t driver_lock;
+	/** Driver lock flags */
+	ulong driver_flags;
+	/** Driver reference flags */
+	struct kobject kobj;
+	/** CRC check flag */
+	u16 fw_crc_check;
+	/** Card type */
+	u16 card_type;
+	/** sdio device */
+	const struct sdio_device *psdio_device;
+	int debug_device_pending;
+	int debug_ocf_ogf[2];
+	u8 fw_reload;
+#ifdef BLE_WAKEUP
+	u8 ble_wakeup_buf_size;
+	u8 *ble_wakeup_buf;
+#endif
+} bt_private, *pbt_private;
+
+/** Disable interrupt */
+#define OS_INT_DISABLE	spin_lock_irqsave(&priv->driver_lock, \
+						priv->driver_flags)
+/** Enable interrupt */
+#define	OS_INT_RESTORE	spin_unlock_irqrestore(&priv->driver_lock, \
+						priv->driver_flags)
+
+#ifndef HCI_BT_AMP
+/** BT_AMP flag for device type */
+#define  HCI_BT_AMP		0x80
+#endif
+
+/** Device type of BT */
+#define DEV_TYPE_BT		0x00
+/** Device type of AMP */
+#define DEV_TYPE_AMP		0x01
+/** Device type of NFC */
+#define DEV_TYPE_NFC		0x04
+
+/** Marvell vendor packet */
+#define MRVL_VENDOR_PKT			0xFE
+
+/** Bluetooth command : Get FW Version */
+#define BT_CMD_GET_FW_VERSION       0x0F
+/** Bluetooth command : Sleep mode */
+#define BT_CMD_AUTO_SLEEP_MODE		0x23
+/** Bluetooth command : Host Sleep configuration */
+#define BT_CMD_HOST_SLEEP_CONFIG	0x59
+/** Bluetooth command : Host Sleep enable */
+#define BT_CMD_HOST_SLEEP_ENABLE	0x5A
+/** Bluetooth command : Module Configuration request */
+#define BT_CMD_MODULE_CFG_REQ		0x5B
+
+/** Bluetooth command : PMIC Configure */
+#define BT_CMD_PMIC_CONFIGURE           0x7D
+
+/** Bluetooth command : SDIO pull up down configuration request */
+#define BT_CMD_SDIO_PULL_CFG_REQ	0x69
+/** Bluetooth command : Set Evt Filter Command */
+#define BT_CMD_SET_EVT_FILTER		0x05
+/** Bluetooth command : Enable Write Scan Command */
+#define BT_CMD_ENABLE_WRITE_SCAN	0x1A
+/** Bluetooth command : Enable Device under test mode */
+#define BT_CMD_ENABLE_DEVICE_TESTMODE	0x03
+/** Sub Command: Module Bring Up Request */
+#define MODULE_BRINGUP_REQ		0xF1
+/** Sub Command: Module Shut Down Request */
+#define MODULE_SHUTDOWN_REQ		0xF2
+/** Module already up */
+#define MODULE_CFG_RESP_ALREADY_UP      0x0c
+/** Sub Command: Host Interface Control Request */
+#define MODULE_INTERFACE_CTRL_REQ	0xF5
+
+/** Bluetooth event : Power State */
+#define BT_EVENT_POWER_STATE		0x20
+
+/** Bluetooth Power State : Enable */
+#define BT_PS_ENABLE			0x02
+/** Bluetooth Power State : Disable */
+#define BT_PS_DISABLE			0x03
+/** Bluetooth Power State : Sleep */
+#define BT_PS_SLEEP			0x01
+/** Bluetooth Power State : Awake */
+#define BT_PS_AWAKE			0x02
+
+/** Vendor OGF */
+#define VENDOR_OGF				0x3F
+/** OGF for reset */
+#define RESET_OGF		0x03
+/** Bluetooth command : Reset */
+#define BT_CMD_RESET	0x03
+
+/** Host Sleep activated */
+#define HS_ACTIVATED			0x01
+/** Host Sleep deactivated */
+#define HS_DEACTIVATED			0x00
+
+/** Power Save sleep */
+#define PS_SLEEP			0x01
+/** Power Save awake */
+#define PS_AWAKE			0x00
+
+/** bt header length */
+#define BT_HEADER_LEN			4
+
+#ifndef MAX
+/** Return maximum of two */
+#define MAX(a, b)		((a) > (b) ? (a) : (b))
+#endif
+
+/** This is for firmware specific length */
+#define EXTRA_LEN	36
+
+/** Command buffer size for Marvell driver */
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+
+/** Bluetooth Rx packet buffer size for Marvell driver */
+#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
+	(HCI_MAX_FRAME_SIZE + EXTRA_LEN)
+
+/** Buffer size to allocate */
+#define ALLOC_BUF_SIZE	(((MAX(MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
+			MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+			+ SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE)
+
+/** Request FW timeout in second */
+#define REQUEST_FW_TIMEOUT		30
+
+/** The number of times to try when polling for status bits */
+#define MAX_POLL_TRIES			100
+
+/** The number of times to try when waiting for downloaded firmware to
+    become active when multiple interface is present */
+#define MAX_MULTI_INTERFACE_POLL_TRIES  150
+
+/** The number of times to try when waiting for downloaded firmware to
+     become active. (polling the scratch register). */
+#define MAX_FIRMWARE_POLL_TRIES		100
+
+/** default idle time */
+#define DEFAULT_IDLE_TIME           1000
+
+#define BT_CMD_HEADER_SIZE    3
+
+/** BT command structure */
+typedef struct _BT_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** Data */
+	u8 data[128];
+} __ATTRIB_PACK__ BT_CMD;
+
+/** BT event structure */
+typedef struct _BT_EVENT {
+	/** Event Counter */
+	u8 EC;
+	/** Length */
+	u8 length;
+	/** Data */
+	u8 data[8];
+} BT_EVENT;
+
+#if defined(SDIO_SUSPEND_RESUME)
+#define DEF_GPIO_GAP        0xffff
+#endif
+
+#ifdef BLE_WAKEUP
+#define BD_ADDR_SIZE 6
+/** Vendor specific event */
+#define VENDOR_SPECIFIC_EVENT     0xff
+/** system suspend event */
+#define HCI_SYSTEM_SUSPEND_EVT    0x80
+/** system suspend */
+#define HCI_SYSTEM_SUSPEND        0x00
+/** system resume */
+#define HCI_SYSTEM_RESUME         0x01
+/** This function enables ble wake up pattern */
+int bt_config_ble_wakeup(bt_private *priv);
+int bt_send_system_event(bt_private *priv, u8 flag);
+#endif
+
+/** This function verify the received event pkt */
+int check_evtpkt(bt_private *priv, struct sk_buff *skb);
+
+/* Prototype of global function */
+/** This function gets the priv reference */
+struct kobject *bt_priv_get(bt_private *priv);
+/** This function release the priv reference */
+void bt_priv_put(bt_private *priv);
+/** This function adds the card */
+bt_private *bt_add_card(void *card);
+/** This function removes the card */
+int bt_remove_card(void *card);
+/** This function handles the interrupt */
+void bt_interrupt(struct m_dev *m_dev);
+
+/** This function creates proc interface directory structure */
+int bt_root_proc_init(void);
+/** This function removes proc interface directory structure */
+int bt_root_proc_remove(void);
+/** This function initializes proc entry */
+int bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq);
+/** This function removes proc interface */
+void bt_proc_remove(bt_private *priv);
+
+/** This function process the received event */
+int bt_process_event(bt_private *priv, struct sk_buff *skb);
+/** This function enables host sleep */
+int bt_enable_hs(bt_private *priv);
+/** This function used to send command to firmware */
+int bt_prepare_command(bt_private *priv);
+/** This function frees the structure of adapter */
+void bt_free_adapter(bt_private *priv);
+/** This function handle the receive packet */
+void bt_recv_frame(bt_private *priv, struct sk_buff *skb);
+
+/** clean up m_devs */
+void clean_up_m_devs(bt_private *priv);
+/** bt driver call this function to register to bus driver */
+int *sbi_register(void);
+/** bt driver call this function to unregister to bus driver */
+void sbi_unregister(void);
+/** bt driver calls this function to register the device  */
+int sbi_register_dev(bt_private *priv);
+/** bt driver calls this function to unregister the device */
+int sbi_unregister_dev(bt_private *priv);
+/** This function initializes firmware */
+int sbi_download_fw(bt_private *priv);
+/** Configures hardware to quit deep sleep state */
+int sbi_wakeup_firmware(bt_private *priv);
+/** Module configuration and register device */
+int sbi_register_conf_dpc(bt_private *priv);
+
+/** This function is used to send the data/cmd to hardware */
+int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb);
+/** This function reads the current interrupt status register */
+int sbi_get_int_status(bt_private *priv);
+/** This function enables the host interrupts */
+int sbi_enable_host_int(bt_private *priv);
+/** This function disables the host interrupts */
+int sbi_disable_host_int(bt_private *priv);
+
+/** bt fw reload flag */
+extern int bt_fw_reload;
+/** driver initial the fw reset */
+#define FW_RELOAD_SDIO_INBAND_RESET   1
+/** out band reset trigger reset, no interface re-emulation */
+#define FW_RELOAD_NO_EMULATION  2
+/** out band reset with interface re-emulation */
+#define FW_RELOAD_WITH_EMULATION 3
+/** This function reload firmware */
+void bt_request_fw_reload(bt_private *priv, int mode);
+#define MAX_TX_BUF_SIZE     2312
+/** This function downloads firmware image to the card */
+int sd_download_firmware_w_helper(bt_private *priv);
+void bt_dump_sdio_regs(bt_private *priv);
+/* dumps the firmware to /var/ or /data/ */
+void bt_dump_firmware_info_v2(bt_private *priv);
+
+/** Max line length allowed in init config file */
+#define MAX_LINE_LEN        256
+/** Max MAC address string length allowed */
+#define MAX_MAC_ADDR_LEN    18
+/** Max register type/offset/value etc. parameter length allowed */
+#define MAX_PARAM_LEN       12
+
+/** Bluetooth command : Mac address configuration */
+#define BT_CMD_CONFIG_MAC_ADDR		0x22
+/** Bluetooth command : Write CSU register */
+#define BT_CMD_CSU_WRITE_REG		0x66
+/** Bluetooth command : Load calibrate data */
+#define BT_CMD_LOAD_CONFIG_DATA     0x61
+/** Bluetooth command : Load calibrate ext data */
+#define BT_CMD_LOAD_CONFIG_DATA_EXT     0x60
+
+/** Bluetooth command : BLE deepsleep */
+#define BT_CMD_BLE_DEEP_SLEEP       0x8b
+
+/** BT_BLE command structure */
+typedef struct _BT_BLE_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** deepsleep flag */
+	u8 deepsleep;
+} __ATTRIB_PACK__ BT_BLE_CMD;
+
+/** BT_CSU command structure */
+typedef struct _BT_CSU_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** reg type */
+	u8 type;
+	/** address */
+	u8 offset[4];
+	/** Data */
+	u8 value[2];
+} __ATTRIB_PACK__ BT_CSU_CMD;
+
+/** This function sets mac address */
+int bt_set_mac_address(bt_private *priv, u8 *mac);
+/** This function writes value to CSU registers */
+int bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value);
+/** BT set user defined init data and param */
+int bt_init_config(bt_private *priv, char *cfg_file);
+/** BT PMIC Configure command */
+int bt_pmic_configure(bt_private *priv);
+/** This function load the calibrate data */
+int bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac);
+/** This function load the calibrate ext data */
+int bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len);
+/** BT set user defined calibration data */
+int bt_cal_config(bt_private *priv, char *cfg_file, char *mac);
+/** BT set user defined calibration ext data */
+int bt_cal_config_ext(bt_private *priv, char *cfg_file);
+int bt_init_mac_address(bt_private *priv, char *mac);
+int bt_set_gpio_pin(bt_private *priv);
+/** Bluetooth command : Set gpio pin */
+#define BT_CMD_SET_GPIO_PIN     0xEC
+/** Interrupt Raising Edge**/
+#define INT_RASING_EDGE  0
+/** Interrupt Falling Edge**/
+#define INT_FALLING_EDGE 1
+#define DELAY_50_US      50
+
+int bt_set_independent_reset(bt_private *priv);
+/** Bluetooth command : Independent reset */
+#define BT_CMD_INDEPENDENT_RESET     0x0D
+
+/** BT HCI command structure */
+typedef struct _BT_HCI_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** cmd type */
+	u8 cmd_type;
+	/** cmd len */
+	u8 cmd_len;
+	/** Data */
+	u8 data[6];
+} __ATTRIB_PACK__ BT_HCI_CMD;
+
+#endif /* _BT_DRV_H_ */
diff --git a/bt_sd8897/bt/bt_init.c b/bt_sd8897/bt/bt_init.c
new file mode 100644
index 0000000..9d18912
--- /dev/null
+++ b/bt_sd8897/bt/bt_init.c
@@ -0,0 +1,751 @@
+/** @file bt_init.c
+  *
+  * @brief This file contains the init functions for BlueTooth
+  * driver.
+  *
+  * Copyright (C) 2011-2018, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/string.h>
+#include <linux/firmware.h>
+
+#include "bt_drv.h"
+
+extern int bt_req_fw_nowait;
+
+#define isxdigit(c)	(('0' <= (c) && (c) <= '9') \
+			 || ('a' <= (c) && (c) <= 'f') \
+			 || ('A' <= (c) && (c) <= 'F'))
+
+#define isdigit(c)	(('0' <= (c) && (c) <= '9'))
+#define isspace(c)  (c <= ' ' && (c == ' ' || (c <= 13 && c >= 9)))
+/**
+ *  @brief Returns hex value of a give character
+ *
+ *  @param chr	Character to be converted
+ *
+ *  @return	The converted character if chr is a valid hex, else 0
+ */
+static int
+bt_hexval(char chr)
+{
+	ENTER();
+
+	if (chr >= '0' && chr <= '9')
+		return chr - '0';
+	if (chr >= 'A' && chr <= 'F')
+		return chr - 'A' + 10;
+	if (chr >= 'a' && chr <= 'f')
+		return chr - 'a' + 10;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Extension of strsep lib command. This function will also take care
+ *	   escape character
+ *
+ *  @param s         A pointer to array of chars to process
+ *  @param delim     The delimiter character to end the string
+ *  @param esc       The escape character to ignore for delimiter
+ *
+ *  @return          Pointer to the separated string if delim found, else NULL
+ */
+static char *
+bt_strsep(char **s, char delim, char esc)
+{
+	char *se = *s, *sb;
+
+	ENTER();
+
+	if (!(*s) || (*se == '\0')) {
+		LEAVE();
+		return NULL;
+	}
+
+	for (sb = *s; *sb != '\0'; ++sb) {
+		if (*sb == esc && *(sb + 1) == esc) {
+			/*
+			 * We get a esc + esc seq then keep the one esc
+			 * and chop off the other esc character
+			 */
+			memmove(sb, sb + 1, strlen(sb));
+			continue;
+		}
+		if (*sb == esc && *(sb + 1) == delim) {
+			/*
+			 * We get a delim + esc seq then keep the delim
+			 * and chop off the esc character
+			 */
+			memmove(sb, sb + 1, strlen(sb));
+			continue;
+		}
+		if (*sb == delim)
+			break;
+	}
+
+	if (*sb == '\0')
+		sb = NULL;
+	else
+		*sb++ = '\0';
+
+	*s = sb;
+
+	LEAVE();
+	return se;
+}
+
+/**
+ *  @brief Returns hex value of a given ascii string
+ *
+ *  @param a	String to be converted
+ *
+ *  @return	hex value
+ */
+static int
+bt_atox(const char *a)
+{
+	int i = 0;
+	ENTER();
+	while (isxdigit(*a))
+		i = i * 16 + bt_hexval(*a++);
+
+	LEAVE();
+	return i;
+}
+
+/**
+ *  @brief Converts mac address from string to t_u8 buffer.
+ *
+ *  @param mac_addr The buffer to store the mac address in.
+ *  @param buf      The source of mac address which is a string.
+ *
+ *  @return	N/A
+ */
+static void
+bt_mac2u8(u8 *mac_addr, char *buf)
+{
+	char *begin, *end, *mac_buff;
+	int i;
+
+	ENTER();
+
+	if (!buf) {
+		LEAVE();
+		return;
+	}
+
+	mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL);
+	if (!mac_buff) {
+		LEAVE();
+		return;
+	}
+	memcpy(mac_buff, buf, strlen(buf));
+
+	begin = mac_buff;
+	for (i = 0; i < ETH_ALEN; ++i) {
+		end = bt_strsep(&begin, ':', '/');
+		if (end)
+			mac_addr[i] = bt_atox(end);
+	}
+
+	kfree(mac_buff);
+	LEAVE();
+}
+
+/**
+ *  @brief Returns integer value of a given ascii string
+ *
+ *  @param data    Converted data to be returned
+ *  @param a       String to be converted
+ *
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_atoi(int *data, char *a)
+{
+	int i, val = 0, len;
+
+	ENTER();
+
+	len = strlen(a);
+	if (!strncmp(a, "0x", 2)) {
+		a = a + 2;
+		len -= 2;
+		*data = bt_atox(a);
+		return BT_STATUS_SUCCESS;
+	}
+	for (i = 0; i < len; i++) {
+		if (isdigit(a[i])) {
+			val = val * 10 + (a[i] - '0');
+		} else {
+			PRINTM(ERROR, "Invalid char %c in string %s\n", a[i],
+			       a);
+			return BT_STATUS_FAILURE;
+		}
+	}
+	*data = val;
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief parse cal-data
+ *
+ *  @param src      a pointer to cal-data string
+ *  @param len      len of cal-data
+ *  @param dst      a pointer to return cal-data
+ *  @param dst_size size of dest buffer
+ *
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 *dst_size)
+{
+	const u8 *ptr;
+	u8 *dptr;
+	u32 count = 0;
+	int ret = BT_STATUS_FAILURE;
+
+	ENTER();
+	ptr = src;
+	dptr = dst;
+
+	while ((ptr - src) < len) {
+		if (*ptr && isspace(*ptr)) {
+			ptr++;
+			continue;
+		}
+
+		if (isxdigit(*ptr)) {
+			if ((dptr - dst) >= *dst_size) {
+				PRINTM(ERROR, "cal_file size too big!!!\n");
+				goto done;
+			}
+			*dptr++ = bt_atox((const char *)ptr);
+			ptr += 2;
+			count++;
+		} else {
+			ptr++;
+		}
+	}
+	if (dptr == dst) {
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	*dst_size = count;
+	ret = BT_STATUS_SUCCESS;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT get one line data from ASCII format data
+ *
+ *    @param data         Source data
+ *    @param size         Source data length
+ *    @param line_pos     Destination data
+ *    @return             -1 or length of the line
+ */
+int
+parse_cfg_get_line(u8 *data, u32 size, u8 *line_pos)
+{
+	static s32 pos;
+	u8 *src, *dest;
+
+	if (pos >= size) {	/* reach the end */
+		pos = 0;	/* Reset position for rfkill */
+		return -1;
+	}
+	memset(line_pos, 0, MAX_LINE_LEN);
+	src = data + pos;
+	dest = line_pos;
+
+	while (pos < size && *src != '\x0A' && *src != '\0') {
+		if (*src != ' ' && *src != '\t')	/* parse space */
+			*dest++ = *src++;
+		else
+			src++;
+		pos++;
+	}
+	*dest = '\0';
+	/* parse new line */
+	pos++;
+	return strlen((const char *)line_pos);
+}
+
+/**
+ *    @brief BT parse ASCII format data to MAC address
+ *
+ *    @param priv          BT private handle
+ *    @param data          Source data
+ *    @param size          data length
+ *    @return              BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_init_cfg(bt_private *priv, u8 *data, u32 size)
+{
+	u8 *pos;
+	u8 *intf_s, *intf_e;
+	u8 s[MAX_LINE_LEN];	/* 1 line data */
+	u32 line_len;
+	char dev_name[MAX_PARAM_LEN];
+	u8 buf[MAX_PARAM_LEN];
+	u8 bt_addr[MAX_MAC_ADDR_LEN];
+	u8 bt_mac[ETH_ALEN];
+	int setting = 0;
+	u8 type = 0;
+	u16 value = 0;
+	u32 offset = 0;
+	int ret = BT_STATUS_FAILURE;
+
+	memset(dev_name, 0, sizeof(dev_name));
+	memset(bt_addr, 0, sizeof(bt_addr));
+	memset(bt_mac, 0, sizeof(bt_mac));
+
+	while ((line_len = parse_cfg_get_line(data, size, s)) != -1) {
+		pos = s;
+		while (*pos == ' ' || *pos == '\t')
+			pos++;
+
+		if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') ||
+		    *pos == '\n' || *pos == '\0')
+			continue;	/* Need n't process this line */
+
+		/* Process MAC addr */
+		if (strncmp((char *)pos, "mac_addr", 8) == 0) {
+			intf_s = (u8 *)strchr((const char *)pos, '=');
+			if (intf_s != NULL)
+				intf_e = (u8 *)strchr((const char *)intf_s,
+						      ':');
+			else
+				intf_e = NULL;
+			if (intf_s != NULL && intf_e != NULL) {
+				if ((intf_e - intf_s) > MAX_PARAM_LEN) {
+					PRINTM(ERROR,
+					       "BT: Too long interface name %d\n",
+					       __LINE__);
+					goto done;
+				}
+				strncpy(dev_name, (const char *)intf_s + 1,
+					intf_e - intf_s - 1);
+				dev_name[intf_e - intf_s - 1] = '\0';
+				strncpy((char *)bt_addr,
+					(const char *)intf_e + 1,
+					MAX_MAC_ADDR_LEN - 1);
+				bt_addr[MAX_MAC_ADDR_LEN - 1] = '\0';
+				/* Convert MAC format */
+				bt_mac2u8(bt_mac, (char *)bt_addr);
+				PRINTM(CMD,
+				       "HCI: %s new BT Address " MACSTR "\n",
+				       dev_name, MAC2STR(bt_mac));
+				if (BT_STATUS_SUCCESS !=
+				    bt_set_mac_address(priv, bt_mac)) {
+					PRINTM(FATAL,
+					       "BT: Fail to set mac address\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+		}
+		/* Process REG value */
+		else if (strncmp((char *)pos, "bt_reg", 6) == 0) {
+			intf_s = (u8 *)strchr((const char *)pos, '=');
+			if (intf_s != NULL)
+				intf_e = (u8 *)strchr((const char *)intf_s,
+						      ',');
+			else
+				intf_e = NULL;
+			if (intf_s != NULL && intf_e != NULL) {
+				/* Copy type */
+				memset(buf, 0, sizeof(buf));
+				strncpy((char *)buf, (const char *)intf_s + 1,
+					1);
+				buf[1] = '\0';
+				if (0 == bt_atoi(&setting, (char *)buf))
+					type = (u8)setting;
+				else {
+					PRINTM(ERROR,
+					       "BT: Fail to parse reg type\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+			intf_s = intf_e + 1;
+			intf_e = (u8 *)strchr((const char *)intf_s, ',');
+			if (intf_e != NULL) {
+				if ((intf_e - intf_s) >= MAX_PARAM_LEN) {
+					PRINTM(ERROR,
+					       "BT: Regsier offset is too long %d\n",
+					       __LINE__);
+					goto done;
+				}
+				/* Copy offset */
+				memset(buf, 0, sizeof(buf));
+				strncpy((char *)buf, (const char *)intf_s,
+					intf_e - intf_s);
+				buf[intf_e - intf_s] = '\0';
+				if (0 == bt_atoi(&setting, (char *)buf))
+					offset = (u32)setting;
+				else {
+					PRINTM(ERROR,
+					       "BT: Fail to parse reg offset\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+			intf_s = intf_e + 1;
+			if ((strlen((const char *)intf_s) >= MAX_PARAM_LEN)) {
+				PRINTM(ERROR,
+				       "BT: Regsier value is too long %d\n",
+				       __LINE__);
+				goto done;
+			}
+			/* Copy value */
+			memset(buf, 0, sizeof(buf));
+			strncpy((char *)buf, (const char *)intf_s, sizeof(buf));
+			if (0 == bt_atoi(&setting, (char *)buf))
+				value = (u16) setting;
+			else {
+				PRINTM(ERROR, "BT: Fail to parse reg value\n");
+				goto done;
+			}
+
+			PRINTM(CMD,
+			       "BT: Write reg type: %d offset: 0x%x value: 0x%x\n",
+			       type, offset, value);
+			if (BT_STATUS_SUCCESS !=
+			    bt_write_reg(priv, type, offset, value)) {
+				PRINTM(FATAL,
+				       "BT: Write reg failed. type: %d offset: 0x%x value: 0x%x\n",
+				       type, offset, value);
+				goto done;
+			}
+		}
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief BT request init conf firmware callback
+ *        This function is invoked by request_firmware_nowait system call
+ *
+ * @param firmware  A pointer to firmware image
+ * @param context   A pointer to bt_private structure
+ *
+ * @return          N/A
+ */
+static void
+bt_request_init_user_conf_callback(const struct firmware *firmware,
+				   void *context)
+{
+	bt_private *priv = (bt_private *)context;
+
+	ENTER();
+
+	if (!firmware)
+		PRINTM(ERROR, "BT user init config request firmware failed\n");
+
+	priv->init_user_cfg = firmware;
+	priv->init_user_conf_wait_flag = TRUE;
+	wake_up_interruptible(&priv->init_user_conf_wait_q);
+
+	LEAVE();
+	return;
+}
+
+/**
+ *    @brief BT set user defined init data and param
+ *
+ *    @param priv     BT private handle
+ *    @param cfg_file user cofig file
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_init_config(bt_private *priv, char *cfg_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if ((request_firmware(&cfg, cfg_file, priv->hotplug_device)) < 0) {
+		PRINTM(FATAL, "BT: request_firmware() %s failed\n", cfg_file);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (cfg)
+		ret = bt_process_init_cfg(priv, (u8 *)cfg->data, cfg->size);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration data
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param data    a pointer to cal data
+ *    @param size    cal data size
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_cal_cfg(bt_private *priv, u8 *data, u32 size, char *mac)
+{
+	u8 bt_mac[ETH_ALEN];
+	u8 cal_data[32];
+	u8 *mac_data = NULL;
+	u32 cal_data_len;
+	int ret = BT_STATUS_FAILURE;
+	u8 *pcal_data = cal_data;
+
+	memset(bt_mac, 0, sizeof(bt_mac));
+	cal_data_len = sizeof(cal_data);
+	if (BT_STATUS_SUCCESS !=
+	    bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) {
+		goto done;
+	}
+	if (mac != NULL) {
+		/* Convert MAC format */
+		bt_mac2u8(bt_mac, mac);
+		PRINTM(CMD, "HCI: new BT Address " MACSTR "\n",
+		       MAC2STR(bt_mac));
+		mac_data = bt_mac;
+	}
+	if (BT_STATUS_SUCCESS != bt_load_cal_data(priv, pcal_data, mac_data)) {
+		PRINTM(FATAL, "BT: Fail to load calibrate data\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration EXT data
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param data    a pointer to cal data
+ *    @param size    cal data size
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_cal_cfg_ext(bt_private *priv, u8 *data, u32 size)
+{
+	u8 cal_data[128];
+	u32 cal_data_len;
+	int ret = BT_STATUS_FAILURE;
+
+	cal_data_len = sizeof(cal_data);
+	if (BT_STATUS_SUCCESS !=
+	    bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) {
+		goto done;
+	}
+	if (BT_STATUS_SUCCESS !=
+	    bt_load_cal_data_ext(priv, cal_data, cal_data_len)) {
+		PRINTM(FATAL, "BT: Fail to load calibrate data\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration file
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param cal_file calibration file name
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_cal_config(bt_private *priv, char *cal_file, char *mac)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      bt_request_init_user_conf_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#endif
+#endif
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: bt_cal_config() failed, error code = %#x cal_file=%s\n",
+			       ret, cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->init_user_conf_wait_flag = FALSE;
+		wait_event_interruptible(priv->init_user_conf_wait_q,
+					 priv->init_user_conf_wait_flag);
+		cfg = priv->init_user_cfg;
+	} else {
+		if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) <
+		    0) {
+			PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+			       cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cfg)
+		ret = bt_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size, mac);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration EXT file
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param cal_file calibration file name
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_cal_config_ext(bt_private *priv, char *cal_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      bt_request_init_user_conf_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#endif
+#endif
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: bt_cal_config_ext() failed, error code = %#x cal_file=%s\n",
+			       ret, cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->init_user_conf_wait_flag = FALSE;
+		wait_event_interruptible(priv->init_user_conf_wait_q,
+					 priv->init_user_conf_wait_flag);
+		cfg = priv->init_user_cfg;
+	} else {
+		if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) <
+		    0) {
+			PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+			       cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cfg)
+		ret = bt_process_cal_cfg_ext(priv, (u8 *)cfg->data, cfg->size);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT init mac address from bt_mac parametre when insmod
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param bt_mac  mac address buf
+ *    @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_init_mac_address(bt_private *priv, char *mac)
+{
+	u8 bt_mac[ETH_ALEN];
+	int ret = BT_STATUS_FAILURE;
+
+	ENTER();
+	memset(bt_mac, 0, sizeof(bt_mac));
+	bt_mac2u8(bt_mac, mac);
+	PRINTM(CMD, "HCI: New BT Address " MACSTR "\n", MAC2STR(bt_mac));
+	ret = bt_set_mac_address(priv, bt_mac);
+	if (ret != BT_STATUS_SUCCESS)
+		PRINTM(FATAL,
+		       "BT: Fail to set mac address from insmod parametre.\n");
+
+	LEAVE();
+	return ret;
+}
diff --git a/bt_sd8897/bt/bt_main.c b/bt_sd8897/bt/bt_main.c
new file mode 100644
index 0000000..744bba3
--- /dev/null
+++ b/bt_sd8897/bt/bt_main.c
@@ -0,0 +1,3958 @@
+/** @file bt_main.c
+  *
+  * @brief This file contains the major functions in BlueTooth
+  * driver. It includes init, exit, open, close and main
+  * thread etc..
+  *
+  * Copyright (C) 2007-2018, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+  *
+  */
+/**
+  * @mainpage M-BT Linux Driver
+  *
+  * @section overview_sec Overview
+  *
+  * The M-BT is a Linux reference driver for Marvell Bluetooth chipset.
+  *
+  * @section copyright_sec Copyright
+  *
+  * Copyright (C) 2007-2018, Marvell International Ltd.
+  *
+  */
+#include <linux/module.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
+
+#include <linux/mmc/sdio_func.h>
+
+#include "bt_drv.h"
+#include "mbt_char.h"
+#include "bt_sdio.h"
+
+/** Version */
+#define VERSION "C3X14113"
+
+/** Driver version */
+static char mbt_driver_version[] = "SD8XXX-%s-" VERSION "-(" "FP" FPNUM ")"
+#ifdef DEBUG_LEVEL2
+	"-dbg"
+#endif
+	" ";
+
+/** SD8787 Card */
+#define CARD_SD8787     "SD8787"
+/** SD8777 Card */
+#define CARD_SD8777     "SD8777"
+/** SD8887 Card */
+#define CARD_SD8887     "SD8887"
+/** SD8897 Card */
+#define CARD_SD8897     "SD8897"
+/** SD8797 Card */
+#define CARD_SD8797     "SD8797"
+/** SD8977 Card */
+#define CARD_SD8977     "SD8977"
+/** SD8997 Card */
+#define CARD_SD8997     "SD8997"
+/** SD8987 Card */
+#define CARD_SD8987     "SD8987"
+
+/** Declare and initialize fw_version */
+static char fw_version[32] = "0.0.0.p0";
+
+#define AID_SYSTEM        1000	/* system server */
+
+#define AID_BLUETOOTH     1002	/* bluetooth subsystem */
+
+#define AID_NET_BT_STACK  3008	/* bluetooth stack */
+
+/** Define module name */
+
+#define MODULE_NAME  "bt_fm_nfc"
+
+/** Declaration of chardev class */
+static struct class *chardev_class;
+
+/** Interface specific variables */
+static int mbtchar_minor;
+static int nfcchar_minor;
+static int debugchar_minor;
+
+/**
+ * The global variable of a pointer to bt_private
+ * structure variable
+ **/
+bt_private *m_priv[MAX_BT_ADAPTER];
+
+/** Default Driver mode */
+static int drv_mode = (DRV_MODE_BT | DRV_MODE_NFC);
+
+/** BT interface name */
+static char *bt_name;
+/** NFC interface name */
+static char *nfc_name;
+/** BT debug interface name */
+static char *debug_name;
+
+/** fw reload flag */
+int bt_fw_reload;
+/** fw serial download flag */
+int bt_fw_serial = 1;
+
+/** Firmware flag */
+static int fw = 1;
+/** default powermode */
+static int psmode = 1;
+/** default BLE deep sleep */
+static int deep_sleep = 1;
+/** Default CRC check control */
+static int fw_crc_check = 1;
+/** Init config file (MAC address, register etc.) */
+static char *init_cfg;
+/** Calibration config file (MAC address, init powe etc.) */
+static char *cal_cfg;
+/** Calibration config file EXT */
+static char *cal_cfg_ext;
+/** Init MAC address */
+static char *bt_mac;
+static int mbt_gpio_pin;
+static int btindrst = -1;
+
+/** Setting mbt_drvdbg value based on DEBUG level */
+#ifdef DEBUG_LEVEL1
+#ifdef DEBUG_LEVEL2
+#define DEFAULT_DEBUG_MASK  (0xffffffff & ~DBG_EVENT)
+#else
+#define DEFAULT_DEBUG_MASK  (DBG_MSG | DBG_FATAL | DBG_ERROR)
+#endif /* DEBUG_LEVEL2 */
+u32 mbt_drvdbg = DEFAULT_DEBUG_MASK;
+#endif
+
+#ifdef CONFIG_OF
+static int dts_enable = 1;
+#endif
+
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+int mbt_pm_keep_power = 1;
+#endif
+
+static int debug_intf = 1;
+
+static int btpmic = 0;
+
+/** Offset of sequence number in event */
+#define OFFSET_SEQNUM 4
+
+/**
+ *  @brief handle received packet
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *
+ *  @return        N/A
+ */
+void
+bt_recv_frame(bt_private *priv, struct sk_buff *skb)
+{
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+	if (mbt_dev) {
+		skb->dev = (void *)mdev_bt;
+		mdev_recv_frame(skb);
+		mdev_bt->stat.byte_rx += skb->len;
+	}
+	return;
+}
+
+/**
+ *  @brief Alloc bt device
+ *
+ *  @return    pointer to structure mbt_dev or NULL
+ */
+struct mbt_dev *
+alloc_mbt_dev(void)
+{
+	struct mbt_dev *mbt_dev;
+	ENTER();
+
+	mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL);
+	if (!mbt_dev) {
+		LEAVE();
+		return NULL;
+	}
+
+	LEAVE();
+	return mbt_dev;
+}
+
+/**
+ *  @brief Alloc nfc device
+ *
+ *  @return    pointer to structure nfc_dev or NULL
+ */
+struct nfc_dev *
+alloc_nfc_dev(void)
+{
+	struct nfc_dev *nfc_dev;
+	ENTER();
+
+	nfc_dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL);
+	if (!nfc_dev) {
+		LEAVE();
+		return NULL;
+	}
+
+	LEAVE();
+	return nfc_dev;
+}
+
+/**
+ *  @brief Alloc debug device
+ *
+ *  @return    pointer to structure debug_level or NULL
+ */
+struct debug_dev *
+alloc_debug_dev(void)
+{
+	struct debug_dev *debug_dev;
+	ENTER();
+
+	debug_dev = kzalloc(sizeof(struct debug_dev), GFP_KERNEL);
+	if (!debug_dev) {
+		LEAVE();
+		return NULL;
+	}
+
+	LEAVE();
+	return debug_dev;
+}
+
+/**
+ *  @brief Frees m_dev
+ *
+ *  @return    N/A
+ */
+void
+free_m_dev(struct m_dev *m_dev)
+{
+	ENTER();
+	kfree(m_dev->dev_pointer);
+	m_dev->dev_pointer = NULL;
+	LEAVE();
+}
+
+/**
+ *  @brief clean up m_devs
+ *
+ *  @return    N/A
+ */
+void
+clean_up_m_devs(bt_private *priv)
+{
+	struct m_dev *m_dev = NULL;
+	int i;
+
+	ENTER();
+	if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+		PRINTM(MSG, "BT: Delete %s\n", m_dev->name);
+		if (m_dev->spec_type == IANYWHERE_SPEC) {
+			if ((drv_mode & DRV_MODE_BT) && (mbtchar_minor > 0))
+				mbtchar_minor--;
+			m_dev->close(m_dev);
+			for (i = 0; i < 3; i++)
+				kfree_skb(((struct mbt_dev *)
+					   (m_dev->dev_pointer))->
+					  reassembly[i]);
+			/**  unregister m_dev to char_dev */
+			if (chardev_class)
+				chardev_cleanup_one(m_dev, chardev_class);
+			free_m_dev(m_dev);
+		}
+		priv->bt_dev.m_dev[BT_SEQ].dev_pointer = NULL;
+	}
+	if (priv->bt_dev.m_dev[NFC_SEQ].dev_pointer) {
+		m_dev = &(priv->bt_dev.m_dev[NFC_SEQ]);
+		PRINTM(MSG, "BT: Delete %s\n", m_dev->name);
+		if ((drv_mode & DRV_MODE_NFC) && (nfcchar_minor > 0))
+			nfcchar_minor--;
+		m_dev->close(m_dev);
+		/** unregister m_dev to char_dev */
+		if (chardev_class)
+			chardev_cleanup_one(m_dev, chardev_class);
+		free_m_dev(m_dev);
+		priv->bt_dev.m_dev[NFC_SEQ].dev_pointer = NULL;
+	}
+	if (priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer) {
+		m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+		PRINTM(MSG, "BT: Delete %s\n", m_dev->name);
+		if ((debug_intf) && (debugchar_minor > 0))
+			debugchar_minor--;
+		/** unregister m_dev to char_dev */
+		if (chardev_class)
+			chardev_cleanup_one(m_dev, chardev_class);
+		free_m_dev(m_dev);
+		priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = NULL;
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function verify the received event pkt
+ *
+ *  Event format:
+ *  +--------+--------+--------+--------+--------+
+ *  | Event  | Length |  ncmd  |      Opcode     |
+ *  +--------+--------+--------+--------+--------+
+ *  | 1-byte | 1-byte | 1-byte |      2-byte     |
+ *  +--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+check_evtpkt(bt_private *priv, struct sk_buff *skb)
+{
+	struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data;
+	struct hci_ev_cmd_complete *ec;
+	u16 opcode, ocf;
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (!priv->bt_dev.sendcmdflag) {
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	if (hdr->evt == HCI_EV_CMD_COMPLETE) {
+		ec = (struct hci_ev_cmd_complete *)
+			(skb->data + HCI_EVENT_HDR_SIZE);
+		opcode = __le16_to_cpu(ec->opcode);
+		ocf = hci_opcode_ocf(opcode);
+		PRINTM(CMD,
+		       "BT: CMD_COMPLTE opcode=0x%x, ocf=0x%x, send_cmd_opcode=0x%x\n",
+		       opcode, ocf, priv->bt_dev.send_cmd_opcode);
+		if (opcode != priv->bt_dev.send_cmd_opcode) {
+			ret = BT_STATUS_FAILURE;
+			goto exit;
+		}
+		switch (ocf) {
+		case BT_CMD_MODULE_CFG_REQ:
+		case BT_CMD_BLE_DEEP_SLEEP:
+		case BT_CMD_CONFIG_MAC_ADDR:
+		case BT_CMD_CSU_WRITE_REG:
+		case BT_CMD_LOAD_CONFIG_DATA:
+		case BT_CMD_LOAD_CONFIG_DATA_EXT:
+		case BT_CMD_AUTO_SLEEP_MODE:
+		case BT_CMD_HOST_SLEEP_CONFIG:
+		case BT_CMD_SDIO_PULL_CFG_REQ:
+		case BT_CMD_SET_EVT_FILTER:
+		case BT_CMD_ENABLE_WRITE_SCAN:
+			// case BT_CMD_ENABLE_DEVICE_TESTMODE:
+		case BT_CMD_RESET:
+		case BT_CMD_PMIC_CONFIGURE:
+		case BT_CMD_SET_GPIO_PIN:
+		case BT_CMD_INDEPENDENT_RESET:
+			priv->bt_dev.sendcmdflag = FALSE;
+			priv->adapter->cmd_complete = TRUE;
+			wake_up_interruptible(&priv->adapter->cmd_wait_q);
+			break;
+		case BT_CMD_GET_FW_VERSION:
+			{
+				u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE +
+					   sizeof(struct hci_ev_cmd_complete) +
+					   1);
+				snprintf(fw_version, sizeof(fw_version),
+					 "%u.%u.%u.p%u", pos[2], pos[1], pos[0],
+					 pos[3]);
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+				break;
+			}
+		case BT_CMD_HOST_SLEEP_ENABLE:
+			priv->bt_dev.sendcmdflag = FALSE;
+			break;
+		default:
+			/** Ignore command not defined but send by driver */
+			if (opcode == priv->bt_dev.send_cmd_opcode) {
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			} else {
+				ret = BT_STATUS_FAILURE;
+			}
+			break;
+		}
+	}
+exit:
+	if (ret == BT_STATUS_SUCCESS)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+/**
+*  @brief This function stores the FW dumps received from events in a file
+*
+*  @param priv    A pointer to bt_private structure
+*  @param skb     A pointer to rx skb
+*
+*  @return        N/A
+*/
+void
+bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len)
+{
+	struct file *pfile_fwdump = NULL;
+	loff_t pos = 0;
+	u16 seqnum = 0;
+	struct timeval t;
+	u32 sec;
+
+	ENTER();
+
+	seqnum = __le16_to_cpu(*(u16 *) (buf + OFFSET_SEQNUM));
+
+	if (priv->adapter->fwdump_fname && seqnum != 1) {
+		pfile_fwdump =
+			filp_open((const char *)priv->adapter->fwdump_fname,
+				  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		if (IS_ERR(pfile_fwdump)) {
+			PRINTM(MSG, "Cannot create firmware dump file.\n");
+			LEAVE();
+			return;
+		}
+	} else {
+		if (!priv->adapter->fwdump_fname) {
+			gfp_t flag;
+			flag = (in_atomic() ||
+				irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
+			priv->adapter->fwdump_fname = kzalloc(64, flag);
+		} else
+			memset(priv->adapter->fwdump_fname, 0, 64);
+
+		do_gettimeofday(&t);
+		sec = (u32)t.tv_sec;
+		sprintf(priv->adapter->fwdump_fname, "%s%u",
+			"/var/log/bt_fwdump_", sec);
+		pfile_fwdump =
+			filp_open(priv->adapter->fwdump_fname,
+				  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		if (IS_ERR(pfile_fwdump)) {
+			sprintf(priv->adapter->fwdump_fname, "%s%u",
+				"/data/bt_fwdump_", sec);
+			pfile_fwdump =
+				filp_open((const char *)priv->adapter->
+					  fwdump_fname,
+					  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		}
+	}
+
+	if (IS_ERR(pfile_fwdump)) {
+		PRINTM(MSG, "Cannot create firmware dump file\n");
+		LEAVE();
+		return;
+	}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	vfs_write(pfile_fwdump, buf, len, &pos);
+#else
+	kernel_write(pfile_fwdump, buf, len, &pos);
+#endif
+	filp_close(pfile_fwdump, NULL);
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function process the received event
+ *
+ *  Event format:
+ *  +--------+--------+--------+--------+-----+
+ *  |   EC   | Length |           Data        |
+ *  +--------+--------+--------+--------+-----+
+ *  | 1-byte | 1-byte |          n-byte       |
+ *  +--------+--------+--------+--------+-----+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_event(bt_private *priv, struct sk_buff *skb)
+{
+	int ret = BT_STATUS_SUCCESS;
+#ifdef DEBUG_LEVEL1
+	struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+#endif
+	BT_EVENT *pevent;
+
+	ENTER();
+	if (!m_dev) {
+		PRINTM(CMD, "BT: bt_process_event without m_dev\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pevent = (BT_EVENT *)skb->data;
+	if (pevent->EC != 0xff) {
+		PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pevent->EC);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	switch (pevent->data[0]) {
+	case BT_CMD_AUTO_SLEEP_MODE:
+		if (pevent->data[2] == BT_STATUS_SUCCESS) {
+			if (pevent->data[1] == BT_PS_ENABLE)
+				priv->adapter->psmode = 1;
+			else
+				priv->adapter->psmode = 0;
+			PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name,
+			       (priv->adapter->psmode) ? "Enable" : "Disable");
+
+		} else {
+			PRINTM(CMD, "BT: PS Mode Command Fail %s\n",
+			       m_dev->name);
+		}
+		break;
+	case BT_CMD_HOST_SLEEP_CONFIG:
+		if (pevent->data[3] == BT_STATUS_SUCCESS) {
+			PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n",
+			       m_dev->name, pevent->data[1], pevent->data[2]);
+		} else {
+			PRINTM(CMD, "BT: %s: HSCFG Command Fail\n",
+			       m_dev->name);
+		}
+		break;
+	case BT_CMD_HOST_SLEEP_ENABLE:
+		if (pevent->data[1] == BT_STATUS_SUCCESS) {
+			priv->adapter->hs_state = HS_ACTIVATED;
+			if (priv->adapter->suspend_fail == FALSE) {
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+				bt_is_suspended(priv);
+#endif
+#endif
+#endif
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			}
+			if (priv->adapter->psmode)
+				priv->adapter->ps_state = PS_SLEEP;
+			PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n",
+			       m_dev->name);
+
+		} else {
+			PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name);
+		}
+		break;
+	case BT_CMD_MODULE_CFG_REQ:
+		if ((priv->bt_dev.sendcmdflag == TRUE) &&
+		    ((pevent->data[1] == MODULE_BRINGUP_REQ)
+		     || (pevent->data[1] == MODULE_SHUTDOWN_REQ))) {
+			if (pevent->data[1] == MODULE_BRINGUP_REQ) {
+				PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+				       (pevent->data[2] && (pevent->data[2] !=
+							    MODULE_CFG_RESP_ALREADY_UP))
+				       ? "Bring up Fail" : "Bring up success");
+				priv->bt_dev.devType = pevent->data[3];
+				PRINTM(CMD, "devType:%s\n",
+				       (pevent->data[3] ==
+					DEV_TYPE_AMP) ? "AMP controller" :
+				       "BR/EDR controller");
+				priv->bt_dev.devFeature = pevent->data[4];
+				PRINTM(CMD, "devFeature:  %s,    %s,    %s"
+				       ",    %s"
+				       "\n",
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BT) ?
+					"BT Feature" : "No BT Feature"),
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BTAMP) ?
+					"BTAMP Feature" : "No BTAMP Feature"),
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BLE) ?
+					"BLE Feature" : "No BLE Feature")
+				       ,
+				       ((pevent->
+					 data[4] & DEV_FEATURE_NFC) ?
+					"NFC Feature" : "No NFC Feature")
+					);
+			}
+			if (pevent->data[1] == MODULE_SHUTDOWN_REQ) {
+				PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+				       (pevent->data[2]) ? "Shut down Fail"
+				       : "Shut down success");
+
+			}
+			if (pevent->data[2]) {
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			}
+		} else {
+			PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n");
+			ret = BT_STATUS_FAILURE;
+		}
+		break;
+	case BT_EVENT_POWER_STATE:
+		if (pevent->data[1] == BT_PS_SLEEP)
+			priv->adapter->ps_state = PS_SLEEP;
+		if (priv->adapter->ps_state == PS_SLEEP
+		    && (priv->card_type == CARD_TYPE_SD8887)
+			)
+			os_sched_timeout(5);
+		PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+		       (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
+
+		break;
+	case BT_CMD_SDIO_PULL_CFG_REQ:
+		if (pevent->data[pevent->length - 1] == BT_STATUS_SUCCESS)
+			PRINTM(CMD, "BT: %s: SDIO pull configuration success\n",
+			       m_dev->name);
+
+		else {
+			PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n",
+			       m_dev->name);
+
+		}
+		break;
+	default:
+		PRINTM(CMD, "BT: Unknown Event=%d %s\n", pevent->data[0],
+		       m_dev->name);
+		ret = BT_STATUS_FAILURE;
+		break;
+	}
+exit:
+	if (ret == BT_STATUS_SUCCESS)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function save the dump info to file
+ *
+ *  @param dir_name     directory name
+ *  @param file_name    file_name
+ *  @param buf			buffer
+ *  @param buf_len		buffer length
+ *
+ *  @return   		    0 --success otherwise fail
+ */
+int
+bt_save_dump_info_to_file(char *dir_name, char *file_name, u8 *buf, u32 buf_len)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct file *pfile = NULL;
+	u8 name[64];
+	loff_t pos;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	mm_segment_t fs;
+#endif
+
+	ENTER();
+
+	if (!dir_name || !file_name || !buf) {
+		PRINTM(ERROR, "Can't save dump info to file\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	memset(name, 0, sizeof(name));
+	sprintf((char *)name, "%s/%s", dir_name, file_name);
+	pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644);
+	if (IS_ERR(pfile)) {
+		PRINTM(MSG,
+		       "Create file %s error, try to save dump file in /var\n",
+		       name);
+		memset(name, 0, sizeof(name));
+		sprintf((char *)name, "%s/%s", "/var", file_name);
+		pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644);
+	}
+	if (IS_ERR(pfile)) {
+		PRINTM(ERROR, "Create Dump file for %s error\n", name);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	PRINTM(MSG, "Dump data %s saved in %s\n", file_name, name);
+
+	pos = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	vfs_write(pfile, (const char __user *)buf, buf_len, &pos);
+	set_fs(fs);
+#else
+	kernel_write(pfile, (const char __user *)buf, buf_len, &pos);
+#endif
+	filp_close(pfile, NULL);
+
+	PRINTM(MSG, "Dump data %s saved in %s successfully\n", file_name, name);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+#define DEBUG_HOST_READY		0xEE
+#define DEBUG_FW_DONE			0xFF
+#define DUMP_MAX_POLL_TRIES			200
+
+#define DEBUG_DUMP_CTRL_REG_8897               0xE2
+#define DEBUG_DUMP_START_REG_8897              0xE3
+#define DEBUG_DUMP_END_REG_8897                0xEA
+#define DEBUG_DUMP_CTRL_REG_8887               0xA2
+#define DEBUG_DUMP_START_REG_8887              0xA3
+#define DEBUG_DUMP_END_REG_8887                0xAA
+#define DEBUG_DUMP_CTRL_REG_8977               0xF0
+#define DEBUG_DUMP_START_REG_8977              0xF1
+#define DEBUG_DUMP_END_REG_8977                0xF8
+#define DEBUG_DUMP_CTRL_REG_8997               0xF0
+#define DEBUG_DUMP_START_REG_8997              0xF1
+#define DEBUG_DUMP_END_REG_8997                0xF8
+#define DEBUG_DUMP_CTRL_REG_8987               0xF0
+#define DEBUG_DUMP_START_REG_8987              0xF1
+#define DEBUG_DUMP_END_REG_8987                0xF8
+
+typedef enum {
+	DUMP_TYPE_ITCM = 0,
+	DUMP_TYPE_DTCM = 1,
+	DUMP_TYPE_SQRAM = 2,
+	DUMP_TYPE_APU_REGS = 3,
+	DUMP_TYPE_CIU_REGS = 4,
+	DUMP_TYPE_ICU_REGS = 5,
+	DUMP_TYPE_MAC_REGS = 6,
+	DUMP_TYPE_EXTEND_7 = 7,
+	DUMP_TYPE_EXTEND_8 = 8,
+	DUMP_TYPE_EXTEND_9 = 9,
+	DUMP_TYPE_EXTEND_10 = 10,
+	DUMP_TYPE_EXTEND_11 = 11,
+	DUMP_TYPE_EXTEND_12 = 12,
+	DUMP_TYPE_EXTEND_13 = 13,
+	DUMP_TYPE_EXTEND_LAST = 14
+} dumped_mem_type;
+
+#define MAX_NAME_LEN               8
+#define MAX_FULL_NAME_LEN               32
+
+/** Memory type mapping structure */
+typedef struct {
+	/** memory name */
+	u8 mem_name[MAX_NAME_LEN];
+	/** memory pointer */
+	u8 *mem_Ptr;
+	/** file structure */
+	struct file *pfile_mem;
+	/** donbe flag */
+	u8 done_flag;
+} memory_type_mapping;
+
+memory_type_mapping bt_mem_type_mapping_tbl[] = {
+	{"ITCM", NULL, NULL, 0xF0},
+	{"DTCM", NULL, NULL, 0xF1},
+	{"SQRAM", NULL, NULL, 0xF2},
+	{"APU", NULL, NULL, 0xF3},
+	{"CIU", NULL, NULL, 0xF4},
+	{"ICU", NULL, NULL, 0xF5},
+	{"MAC", NULL, NULL, 0xF6},
+	{"EXT7", NULL, NULL, 0xF7},
+	{"EXT8", NULL, NULL, 0xF8},
+	{"EXT9", NULL, NULL, 0xF9},
+	{"EXT10", NULL, NULL, 0xFA},
+	{"EXT11", NULL, NULL, 0xFB},
+	{"EXT12", NULL, NULL, 0xFC},
+	{"EXT13", NULL, NULL, 0xFD},
+	{"EXTLAST", NULL, NULL, 0xFE},
+};
+
+typedef enum {
+	RDWR_STATUS_SUCCESS = 0,
+	RDWR_STATUS_FAILURE = 1,
+	RDWR_STATUS_DONE = 2
+} rdwr_status;
+
+/**
+ *  @brief This function read/write firmware via cmd52
+ *
+ *  @param phandle   A pointer to moal_handle
+ *
+ *  @return         MLAN_STATUS_SUCCESS
+ */
+rdwr_status
+bt_cmd52_rdwr_firmware(bt_private *priv, u8 doneflag)
+{
+	int ret = 0;
+	int tries = 0;
+	u8 ctrl_data = 0;
+	u8 dbg_dump_ctrl_reg = 0;
+
+	if (priv->card_type == CARD_TYPE_SD8887)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8887;
+	else if (priv->card_type == CARD_TYPE_SD8897)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8897;
+	else if (priv->card_type == CARD_TYPE_SD8977)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8977;
+	else if (priv->card_type == CARD_TYPE_SD8997)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8997;
+	else if (priv->card_type == CARD_TYPE_SD8987)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8987;
+
+	sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.card)->func,
+		    DEBUG_HOST_READY, dbg_dump_ctrl_reg, &ret);
+	if (ret) {
+		PRINTM(ERROR, "SDIO Write ERR\n");
+		return RDWR_STATUS_FAILURE;
+	}
+	for (tries = 0; tries < DUMP_MAX_POLL_TRIES; tries++) {
+		ctrl_data =
+			sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->
+				   func, dbg_dump_ctrl_reg, &ret);
+		if (ret) {
+			PRINTM(ERROR, "SDIO READ ERR\n");
+			return RDWR_STATUS_FAILURE;
+		}
+		if (ctrl_data == DEBUG_FW_DONE)
+			break;
+		if (doneflag && ctrl_data == doneflag)
+			return RDWR_STATUS_DONE;
+		if (ctrl_data != DEBUG_HOST_READY) {
+			PRINTM(INFO,
+			       "The ctrl reg was changed, re-try again!\n");
+			sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.
+				     card)->func, DEBUG_HOST_READY,
+				    dbg_dump_ctrl_reg, &ret);
+			if (ret) {
+				PRINTM(ERROR, "SDIO Write ERR\n");
+				return RDWR_STATUS_FAILURE;
+			}
+		}
+		udelay(100);
+	}
+	if (ctrl_data == DEBUG_HOST_READY) {
+		PRINTM(ERROR, "Fail to pull ctrl_data\n");
+		return RDWR_STATUS_FAILURE;
+	}
+
+	return RDWR_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function dump firmware memory to file
+ *
+ *  @param phandle   A pointer to moal_handle
+ *
+ *  @return         N/A
+ */
+void
+bt_dump_firmware_info_v2(bt_private *priv)
+{
+	int ret = 0;
+	unsigned int reg, reg_start, reg_end;
+	u8 *dbg_ptr = NULL;
+	u8 dump_num = 0;
+	u8 idx = 0;
+	u8 doneflag = 0;
+	rdwr_status stat;
+	u8 i = 0;
+	u8 read_reg = 0;
+	u32 memory_size = 0;
+	u8 path_name[64], file_name[32];
+	u8 *end_ptr = NULL;
+	u8 dbg_dump_start_reg = 0;
+	u8 dbg_dump_end_reg = 0;
+
+	if (!priv) {
+		PRINTM(ERROR, "Could not dump firmwware info\n");
+		return;
+	}
+
+	if ((priv->card_type != CARD_TYPE_SD8887) &&
+	    (priv->card_type != CARD_TYPE_SD8897)
+	    && (priv->card_type != CARD_TYPE_SD8977) &&
+	    (priv->card_type != CARD_TYPE_SD8997)
+	    && (priv->card_type != CARD_TYPE_SD8987)) {
+		PRINTM(MSG, "card_type %d don't support FW dump\n",
+		       priv->card_type);
+		return;
+	}
+
+	memset(path_name, 0, sizeof(path_name));
+	strcpy((char *)path_name, "/data");
+	PRINTM(MSG, "Create DUMP directory success:dir_name=%s\n", path_name);
+
+	if (priv->card_type == CARD_TYPE_SD8887) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8887;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8887;
+	} else if (priv->card_type == CARD_TYPE_SD8897) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8897;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8897;
+	} else if (priv->card_type == CARD_TYPE_SD8977) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8977;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8977;
+	} else if (priv->card_type == CARD_TYPE_SD8997) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8997;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8997;
+	} else if (priv->card_type == CARD_TYPE_SD8987) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8987;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8987;
+	}
+
+	sbi_wakeup_firmware(priv);
+	sdio_claim_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func);
+	/* start dump fw memory */
+	PRINTM(MSG, "==== DEBUG MODE OUTPUT START ====\n");
+	/* read the number of the memories which will dump */
+	if (RDWR_STATUS_FAILURE == bt_cmd52_rdwr_firmware(priv, doneflag))
+		goto done;
+	reg = dbg_dump_start_reg;
+	dump_num =
+		sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->func,
+			   reg, &ret);
+	if (ret) {
+		PRINTM(MSG, "SDIO READ MEM NUM ERR\n");
+		goto done;
+	}
+
+	/* read the length of every memory which will dump */
+	for (idx = 0; idx < dump_num; idx++) {
+		if (RDWR_STATUS_FAILURE ==
+		    bt_cmd52_rdwr_firmware(priv, doneflag))
+			goto done;
+		memory_size = 0;
+		reg = dbg_dump_start_reg;
+		for (i = 0; i < 4; i++) {
+			read_reg =
+				sdio_readb(((struct sdio_mmc_card *)priv->
+					    bt_dev.card)->func, reg, &ret);
+			if (ret) {
+				PRINTM(MSG, "SDIO READ ERR\n");
+				goto done;
+			}
+			memory_size |= (read_reg << i * 8);
+			reg++;
+		}
+		if (memory_size == 0) {
+			PRINTM(MSG, "Firmware Dump Finished!\n");
+			break;
+		} else {
+			PRINTM(MSG, "%s_SIZE=0x%x\n",
+			       bt_mem_type_mapping_tbl[idx].mem_name,
+			       memory_size);
+			bt_mem_type_mapping_tbl[idx].mem_Ptr =
+				vmalloc(memory_size + 1);
+			if ((ret != BT_STATUS_SUCCESS) ||
+			    !bt_mem_type_mapping_tbl[idx].mem_Ptr) {
+				PRINTM(ERROR,
+				       "Error: vmalloc %s buffer failed!!!\n",
+				       bt_mem_type_mapping_tbl[idx].mem_name);
+				goto done;
+			}
+			dbg_ptr = bt_mem_type_mapping_tbl[idx].mem_Ptr;
+			end_ptr = dbg_ptr + memory_size;
+		}
+		doneflag = bt_mem_type_mapping_tbl[idx].done_flag;
+		PRINTM(MSG, "Start %s output, please wait...\n",
+		       bt_mem_type_mapping_tbl[idx].mem_name);
+		do {
+			stat = bt_cmd52_rdwr_firmware(priv, doneflag);
+			if (RDWR_STATUS_FAILURE == stat)
+				goto done;
+
+			reg_start = dbg_dump_start_reg;
+			reg_end = dbg_dump_end_reg;
+			for (reg = reg_start; reg <= reg_end; reg++) {
+				*dbg_ptr =
+					sdio_readb(((struct sdio_mmc_card *)
+						    priv->bt_dev.card)->func,
+						   reg, &ret);
+				if (ret) {
+					PRINTM(MSG, "SDIO READ ERR\n");
+					goto done;
+				}
+				if (dbg_ptr < end_ptr)
+					dbg_ptr++;
+				else
+					PRINTM(MSG,
+					       "pre-allocced buf is not enough\n");
+			}
+			if (RDWR_STATUS_DONE == stat) {
+				PRINTM(MSG, "%s done:"
+				       "size = 0x%x\n",
+				       bt_mem_type_mapping_tbl[idx].mem_name,
+				       (unsigned int)(dbg_ptr -
+						      bt_mem_type_mapping_tbl
+						      [idx].mem_Ptr));
+				memset(file_name, 0, sizeof(file_name));
+				sprintf((char *)file_name, "%s%s", "file_bt_",
+					bt_mem_type_mapping_tbl[idx].mem_name);
+				if (BT_STATUS_SUCCESS !=
+				    bt_save_dump_info_to_file((char *)path_name,
+							      (char *)file_name,
+							      bt_mem_type_mapping_tbl
+							      [idx].mem_Ptr,
+							      memory_size))
+					PRINTM(MSG,
+					       "Can't save dump file %s in %s\n",
+					       file_name, path_name);
+				vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr);
+				bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL;
+				break;
+			}
+		} while (1);
+	}
+	PRINTM(MSG, "==== DEBUG MODE OUTPUT END ====\n");
+	/* end dump fw memory */
+done:
+	sdio_release_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func);
+	for (idx = 0; idx < dump_num; idx++) {
+		if (bt_mem_type_mapping_tbl[idx].mem_Ptr) {
+			vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr);
+			bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL;
+		}
+	}
+	PRINTM(MSG, "==== DEBUG MODE END ====\n");
+	return;
+}
+
+/**
+ *  @brief This function shows debug info for timeout of command sending.
+ *
+ *  @param adapter  A pointer to bt_private
+ *  @param cmd      Timeout command id
+ *
+ *  @return         N/A
+ */
+static void
+bt_cmd_timeout_func(bt_private *priv, u16 cmd)
+{
+	bt_adapter *adapter = priv->adapter;
+	ENTER();
+
+	adapter->num_cmd_timeout++;
+
+	PRINTM(ERROR, "Version = %s\n", adapter->drv_ver);
+	PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd);
+	PRINTM(ERROR, "Number of command timeout = %d\n",
+	       adapter->num_cmd_timeout);
+	PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter);
+	PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode);
+	PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state);
+	PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state);
+	PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip);
+	PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail);
+	PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended);
+	PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries);
+	PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete);
+	PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv);
+	PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done);
+	PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending);
+	PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg);
+	bt_dump_sdio_regs(priv);
+	LEAVE();
+}
+
+/**
+ *  @brief This function queue frame
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    N/A
+ */
+static void
+bt_queue_frame(bt_private *priv, struct sk_buff *skb)
+{
+	skb_queue_tail(&priv->adapter->tx_queue, skb);
+}
+
+/**
+ *  @brief This function send reset cmd to firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return	       BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_reset_command(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((RESET_OGF << 10) | BT_CMD_RESET);
+	pcmd->length = 0x00;
+	pcmd->cmd_type = 0x00;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, 3);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "Queue Reset Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Reset timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_RESET);
+	} else {
+		PRINTM(CMD, "BT: Reset Command done\n");
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends module cfg cmd to firmware
+ *
+ *  Command format:
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     OCF OGF     | Length |                Data               |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     2-byte      | 1-byte |               4-byte              |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param subcmd  sub command
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_module_cfg_cmd(bt_private *priv, int subcmd)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "BT: No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ);
+	pcmd->length = 1;
+	pcmd->data[0] = subcmd;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "Queue module cfg Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	/*
+	   On some Android platforms certain delay is needed for HCI daemon to
+	   remove this module and close itself gracefully. Otherwise it hangs.
+	   This 10ms delay is a workaround for such platforms as the root cause
+	   has not been found yet. */
+	mdelay(10);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: module_cfg_cmd(%#x): timeout sendcmdflag=%d\n",
+		       subcmd, priv->bt_dev.sendcmdflag);
+		bt_cmd_timeout_func(priv, BT_CMD_MODULE_CFG_REQ);
+	} else {
+		PRINTM(CMD, "BT: module cfg Command done\n");
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables power save mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_ps(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE);
+	if (priv->bt_dev.psmode)
+		pcmd->data[0] = BT_PS_ENABLE;
+	else
+		pcmd->data[0] = BT_PS_DISABLE;
+	if (priv->bt_dev.idle_timeout) {
+		pcmd->length = 3;
+		pcmd->data[1] = (u8)(priv->bt_dev.idle_timeout & 0x00ff);
+		pcmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8;
+	} else {
+		pcmd->length = 1;
+	}
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: psmode timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_AUTO_SLEEP_MODE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends hscfg command
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_hscfg_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG);
+	pcmd->length = 2;
+	pcmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8;
+	pcmd->data[1] = (u8)(priv->bt_dev.gpio_gap & 0x00ff);
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0], pcmd->data[1]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: HSCFG timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_CONFIG);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends sdio pull ctrl command
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_sdio_pull_ctrl_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ);
+	pcmd->length = 4;
+	pcmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff);
+	pcmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8;
+	pcmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16;
+	pcmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD,
+	       "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[1], pcmd->data[0],
+	       pcmd->data[3], pcmd->data[2]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SDIO_PULL_CFG_REQ);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends command to configure PMIC
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_pmic_configure(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_PMIC_CONFIGURE);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue PMIC Configure Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: PMIC Configure timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_PMIC_CONFIGURE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables host sleep
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_hs(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	priv->adapter->suspend_fail = FALSE;
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	PRINTM(CMD, "Queue hs enable Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->hs_state,
+	     WAIT_UNTIL_HS_STATE_CHANGED)) {
+		PRINTM(MSG, "BT: Enable host sleep timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE);
+	}
+	OS_INT_DISABLE;
+	if ((priv->adapter->hs_state == HS_ACTIVATED) ||
+	    (priv->adapter->is_suspended == TRUE)) {
+		OS_INT_RESTORE;
+		PRINTM(MSG, "BT: suspend success! skip=%d\n",
+		       priv->adapter->hs_skip);
+	} else {
+		priv->adapter->suspend_fail = TRUE;
+		OS_INT_RESTORE;
+		priv->adapter->hs_skip++;
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG,
+		       "BT: suspend skipped! "
+		       "state=%d skip=%d ps_state= %d WakeupTries=%d\n",
+		       priv->adapter->hs_state, priv->adapter->hs_skip,
+		       priv->adapter->ps_state, priv->adapter->WakeupTries);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Set Evt Filter
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_evt_filter(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_SET_EVT_FILTER);
+	pcmd->length = 0x03;
+	pcmd->data[0] = 0x02;
+	pcmd->data[1] = 0x00;
+	pcmd->data[2] = 0x03;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Set Evt Filter Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set Evt Filter timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SET_EVT_FILTER);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Enable Write Scan - Page and Inquiry
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_write_scan(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_ENABLE_WRITE_SCAN);
+	pcmd->length = 0x01;
+	pcmd->data[0] = 0x03;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Enable Write Scan Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Enable Write Scan timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_ENABLE_WRITE_SCAN);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Enable Device under test mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_device_under_testmode(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((0x06 << 10) | BT_CMD_ENABLE_DEVICE_TESTMODE);
+	pcmd->length = 0x00;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue enable device under testmode Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Enable Device under TEST mode timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_ENABLE_DEVICE_TESTMODE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables test mode and send cmd
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_test_mode(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	/** Set Evt Filter Command */
+	ret = bt_set_evt_filter(priv);
+	if (ret != BT_STATUS_SUCCESS) {
+		PRINTM(ERROR, "BT test_mode: Set Evt filter fail\n");
+		goto exit;
+	}
+
+	/** Enable Write Scan Command */
+	ret = bt_enable_write_scan(priv);
+	if (ret != BT_STATUS_SUCCESS) {
+		PRINTM(ERROR, "BT test_mode: Enable Write Scan fail\n");
+		goto exit;
+	}
+
+	/** Enable Device under test mode */
+	ret = bt_enable_device_under_testmode(priv);
+	if (ret != BT_STATUS_SUCCESS)
+		PRINTM(ERROR,
+		       "BT test_mode: Enable device under testmode fail\n");
+
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function set GPIO pin
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_gpio_pin(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	/**Interrupt falling edge **/
+	u8 gpio_int_edge = INT_FALLING_EDGE;
+	/**Delay 50 usec **/
+	u8 gpio_pulse_width = DELAY_50_US;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SET_GPIO_PIN);
+	pcmd->data[0] = mbt_gpio_pin;
+	pcmd->data[1] = gpio_int_edge;
+	pcmd->data[2] = gpio_pulse_width;
+	pcmd->length = 3;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set GPIO pin: timeout!\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SET_GPIO_PIN);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+#define DISABLE_RESET  0x0
+#define ENABLE_OUTBAND_RESET 0x1
+#define ENABLE_INBAND_RESET  0x02
+#define DEFAULT_GPIO 0xff
+/**
+ *  @brief This function set GPIO pin
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_independent_reset(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	u8 mode, gpio;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_INDEPENDENT_RESET);
+	mode = btindrst & 0xff;
+	gpio = (btindrst & 0xff00) >> 8;
+	if (mode == ENABLE_OUTBAND_RESET) {
+		pcmd->data[0] = ENABLE_OUTBAND_RESET;
+		if (!gpio)
+			pcmd->data[1] = DEFAULT_GPIO;
+		else
+			pcmd->data[1] = gpio;
+	} else if (mode == ENABLE_INBAND_RESET) {
+		pcmd->data[0] = ENABLE_INBAND_RESET;
+		pcmd->data[1] = DEFAULT_GPIO;
+	} else if (mode == DISABLE_RESET) {
+		pcmd->data[0] = DISABLE_RESET;
+		pcmd->data[1] = DEFAULT_GPIO;
+	} else {
+		PRINTM(WARN, "Unsupport mode\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	PRINTM(CMD, "BT: independant reset mode=%d gpio=%d\n", mode, gpio);
+	pcmd->length = 2;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Independent reset : timeout!\n");
+		bt_cmd_timeout_func(priv, BT_CMD_INDEPENDENT_RESET);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sets ble deepsleep mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mode    TRUE/FALSE
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_ble_deepsleep(bt_private *priv, int mode)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_BLE_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_BLE_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP);
+	pcmd->length = 1;
+	pcmd->deepsleep = mode;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_BLE_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode,
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_BLE_DEEP_SLEEP);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function gets FW version
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_get_fw_version(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION);
+	pcmd->length = 0x01;
+	pcmd->cmd_type = 0x00;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, 4);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Get FW version: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_GET_FW_VERSION);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sets mac address
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_mac_address(bt_private *priv, u8 *mac)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	int i = 0;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR);
+	pcmd->length = 8;
+	pcmd->cmd_type = MRVL_VENDOR_PKT;
+	pcmd->cmd_len = 6;
+	for (i = 0; i < 6; i++)
+		pcmd->data[i] = mac[5 - i];
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_HCI_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set mac addr " MACSTR " (0x%x)\n", MAC2STR(mac),
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set mac addr: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_CONFIG_MAC_ADDR);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	int i = 0;
+	/* u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF 0x01
+	   0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE 0xC0 0xC6 0x2D 0x00 0x00 0x00
+	   0x00 0x00 0x00 0x00 0xF0}; */
+
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA);
+	pcmd->length = 0x20;
+	pcmd->data[0] = 0x00;
+	pcmd->data[1] = 0x00;
+	pcmd->data[2] = 0x00;
+	pcmd->data[3] = 0x1C;
+	/* swip cal-data byte */
+	for (i = 4; i < 32; i++)
+		pcmd->data[i] = *(config_data + ((i / 4) * 8 - 1 - i));
+	if (mac != NULL) {
+		pcmd->data[2] = 0x01;	/* skip checksum */
+		for (i = 24; i < 30; i++)
+			pcmd->data[i] = mac[29 - i];
+	}
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "calirate data: ", pcmd->data, 32);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load calibrate data: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate EXT data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA_EXT);
+	pcmd->length = cfg_data_len;
+
+	memcpy(pcmd->data, config_data, cfg_data_len);
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "calirate ext data", pcmd->data, pcmd->length);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load calibrate ext data: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function writes value to CSU registers
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param type    reg type
+ *  @param offset  register address
+ *  @param value   register value to write
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CSU_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CSU_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG);
+	pcmd->length = 7;
+	pcmd->type = type;
+	pcmd->offset[0] = (offset & 0x000000ff);
+	pcmd->offset[1] = (offset & 0x0000ff00) >> 8;
+	pcmd->offset[2] = (offset & 0x00ff0000) >> 16;
+	pcmd->offset[3] = (offset & 0xff000000) >> 24;
+	pcmd->value[0] = (value & 0x00ff);
+	pcmd->value[1] = (value & 0xff00) >> 8;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_CSU_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n",
+	       type, offset, value);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Set CSU reg timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_CSU_WRITE_REG);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function used to restore tx_queue
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        N/A
+ */
+void
+bt_restore_tx_queue(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	while (!skb_queue_empty(&priv->adapter->pending_queue)) {
+		skb = skb_dequeue(&priv->adapter->pending_queue);
+		if (skb)
+			bt_queue_frame(priv, skb);
+	}
+	wake_up_interruptible(&priv->MainThread.waitQ);
+}
+
+/**
+ *  @brief This function used to send command to firmware
+ *
+ *  Command format:
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     OCF OGF     | Length |                Data               |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     2-byte      | 1-byte |               4-byte              |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_prepare_command(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (priv->bt_dev.hscfgcmd) {
+		priv->bt_dev.hscfgcmd = 0;
+		ret = bt_send_hscfg_cmd(priv);
+	}
+	if (priv->bt_dev.pscmd) {
+		priv->bt_dev.pscmd = 0;
+		ret = bt_enable_ps(priv);
+	}
+	if (priv->bt_dev.sdio_pull_ctrl) {
+		priv->bt_dev.sdio_pull_ctrl = 0;
+		ret = bt_send_sdio_pull_ctrl_cmd(priv);
+	}
+	if (priv->bt_dev.hscmd) {
+		priv->bt_dev.hscmd = 0;
+		if (priv->bt_dev.hsmode)
+			ret = bt_enable_hs(priv);
+		else {
+			ret = sbi_wakeup_firmware(priv);
+			priv->adapter->hs_state = HS_DEACTIVATED;
+		}
+	}
+	if (priv->bt_dev.test_mode) {
+		priv->bt_dev.test_mode = 0;
+		ret = bt_enable_test_mode(priv);
+	}
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function processes a single packet
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to skb which includes TX packet
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+send_single_packet(bt_private *priv, struct sk_buff *skb)
+{
+	int ret;
+	struct sk_buff *new_skb = NULL;
+	ENTER();
+	if (!skb || !skb->data) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) {
+		PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len,
+		       BT_UPLD_SIZE);
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	if (skb_headroom(skb) < BT_HEADER_LEN) {
+		new_skb = skb_realloc_headroom(skb, BT_HEADER_LEN);
+		if (!new_skb) {
+			PRINTM(ERROR, "TX error: realloc_headroom failed %d\n",
+			       BT_HEADER_LEN);
+			kfree_skb(skb);
+			LEAVE();
+			return BT_STATUS_FAILURE;
+		} else {
+			if (new_skb != skb)
+				dev_kfree_skb_any(skb);
+			skb = new_skb;
+		}
+	}
+	/* This is SDIO/PCIE specific header length: byte[3][2][1], * type:
+	   byte[0] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor)
+	 */
+	skb_push(skb, BT_HEADER_LEN);
+	skb->data[0] = (skb->len & 0x0000ff);
+	skb->data[1] = (skb->len & 0x00ff00) >> 8;
+	skb->data[2] = (skb->len & 0xff0000) >> 16;
+	skb->data[3] = bt_cb(skb)->pkt_type;
+	if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT)
+		PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n",
+		       __le16_to_cpu(*((u16 *) & skb->data[4])), skb->len);
+	ret = sbi_host_to_card(priv, skb->data, skb->len);
+	if (ret == BT_STATUS_FAILURE)
+		((struct m_dev *)skb->dev)->stat.err_tx++;
+	else
+		((struct m_dev *)skb->dev)->stat.byte_tx += skb->len;
+	if (ret != BT_STATUS_PENDING)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+#ifdef CONFIG_OF
+/**
+ *  @brief This function read the initial parameter from device tress
+ *
+ *
+ *  @return         N/A
+ */
+static void
+bt_init_from_dev_tree(void)
+{
+	struct device_node *dt_node = NULL;
+	struct property *prop;
+	u32 data;
+	const char *string_data;
+
+	ENTER();
+
+	if (!dts_enable) {
+		PRINTM(CMD, "DTS is disabled!");
+		return;
+	}
+
+	dt_node = of_find_node_by_name(NULL, "sd8xxx-bt");
+	if (!dt_node) {
+		LEAVE();
+		return;
+	}
+	for_each_property_of_node(dt_node, prop) {
+#ifdef DEBUG_LEVEL1
+		if (!strncmp(prop->name, "mbt_drvdbg", strlen("mbt_drvdbg"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				PRINTM(CMD, "mbt_drvdbg=0x%x\n", data);
+				mbt_drvdbg = data;
+			}
+		}
+#endif
+		else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				init_cfg = (char *)string_data;
+				PRINTM(CMD, "init_cfg=%s\n", init_cfg);
+			}
+		} else if (!strncmp
+			   (prop->name, "cal_cfg_ext", strlen("cal_cfg_ext"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				cal_cfg_ext = (char *)string_data;
+				PRINTM(CMD, "cal_cfg_ext=%s\n", cal_cfg_ext);
+			}
+		} else if (!strncmp(prop->name, "cal_cfg", strlen("cal_cfg"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				cal_cfg = (char *)string_data;
+				PRINTM(CMD, "cal_cfg=%s\n", cal_cfg);
+			}
+		} else if (!strncmp(prop->name, "bt_mac", strlen("bt_mac"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				bt_mac = (char *)string_data;
+				PRINTM(CMD, "bt_mac=%s\n", bt_mac);
+			}
+		} else if (!strncmp
+			   (prop->name, "mbt_gpio_pin",
+			    strlen("mbt_gpio_pin"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				mbt_gpio_pin = data;
+				PRINTM(CMD, "mbt_gpio_pin=%d\n", mbt_gpio_pin);
+			}
+		} else if (!strncmp(prop->name, "btindrst", strlen("btindrst"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				btindrst = data;
+				PRINTM(CMD, "btindrst=%d\n", btindrst);
+			}
+		} else if (!strncmp(prop->name, "btpmic", strlen("btpmic"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				btpmic = data;
+				PRINTM(CMD, "btpmic=%d\n", btpmic);
+			}
+		}
+	}
+	LEAVE();
+	return;
+}
+#endif
+
+/**
+ *  @brief This function initializes the adapter structure
+ *  and set default value to the member of adapter.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    N/A
+ */
+static void
+bt_init_adapter(bt_private *priv)
+{
+	ENTER();
+#ifdef CONFIG_OF
+	bt_init_from_dev_tree();
+#endif
+	skb_queue_head_init(&priv->adapter->tx_queue);
+	skb_queue_head_init(&priv->adapter->fwdump_queue);
+
+	skb_queue_head_init(&priv->adapter->pending_queue);
+	priv->adapter->tx_lock = FALSE;
+	priv->adapter->ps_state = PS_AWAKE;
+	priv->adapter->suspend_fail = FALSE;
+	priv->adapter->is_suspended = FALSE;
+	priv->adapter->hs_skip = 0;
+	priv->adapter->num_cmd_timeout = 0;
+	priv->adapter->fwdump_fname = NULL;
+	init_waitqueue_head(&priv->adapter->cmd_wait_q);
+	LEAVE();
+}
+
+/**
+ *  @brief This function initializes firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_fw(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (fw == 0) {
+		sbi_enable_host_int(priv);
+		goto done;
+	}
+	sbi_disable_host_int(priv);
+	if ((priv->card_type == CARD_TYPE_SD8787) ||
+	    (priv->card_type == CARD_TYPE_SD8777))
+		priv->fw_crc_check = fw_crc_check;
+	if (sbi_download_fw(priv)) {
+		PRINTM(ERROR, " FW failed to be download!\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+done:
+	LEAVE();
+	return ret;
+}
+
+#define FW_POLL_TRIES 100
+#define SD8897_FW_RESET_REG  0x0E8
+#define SD8887_FW_RESET_REG  0x0B6
+#define SD8977_SD8997_FW_RESET_REG  0x0EE
+#define SD8887_SD8897_FW_RESET_VAL  1
+#define SD8977_SD8997_FW_RESET_VAL  0x99
+
+/**
+ *  @brief This function reload firmware
+ *
+ *  @param priv   A pointer to bt_private
+ *  @param mode   FW reload mode
+ *
+ *  @return       0--success, otherwise failure
+ */
+static int
+bt_reload_fw(bt_private *priv, int mode)
+{
+	int ret = 0, tries = 0;
+	u8 value = 1;
+	u32 reset_reg = 0;
+	u8 reset_val = 0;
+
+	ENTER();
+	if ((mode != FW_RELOAD_SDIO_INBAND_RESET) &&
+	    (mode != FW_RELOAD_NO_EMULATION)) {
+		PRINTM(ERROR, "Invalid fw reload mode=%d\n", mode);
+		return -EFAULT;
+	}
+
+    /** flush pending tx_queue */
+	skb_queue_purge(&priv->adapter->tx_queue);
+	if (mode == FW_RELOAD_SDIO_INBAND_RESET) {
+		if (priv->card_type == CARD_TYPE_SD8887) {
+			reset_reg = SD8887_FW_RESET_REG;
+			reset_val = SD8887_SD8897_FW_RESET_VAL;
+		} else if (priv->card_type == CARD_TYPE_SD8897) {
+			reset_reg = SD8897_FW_RESET_REG;
+			reset_val = SD8887_SD8897_FW_RESET_VAL;
+		} else if ((priv->card_type == CARD_TYPE_SD8977) ||
+			   (priv->card_type == CARD_TYPE_SD8997) ||
+			   (priv->card_type == CARD_TYPE_SD8987)) {
+			reset_reg = SD8977_SD8997_FW_RESET_REG;
+			reset_val = SD8977_SD8997_FW_RESET_VAL;
+		}
+		sbi_disable_host_int(priv);
+	    /** Wake up firmware firstly */
+		sbi_wakeup_firmware(priv);
+
+	/** wait SOC fully wake up */
+		for (tries = 0; tries < FW_POLL_TRIES; ++tries) {
+			ret = sd_write_reg(priv, reset_reg, 0xba);
+			if (!ret) {
+				ret = sd_read_reg(priv, reset_reg, &value);
+				if (!ret && (value == 0xba)) {
+					PRINTM(MSG, "Fw wake up\n");
+					break;
+				}
+			}
+			udelay(1000);
+		}
+
+		ret = sd_write_reg(priv, reset_reg, reset_val);
+		if (ret) {
+			PRINTM(ERROR, "Failed to write register.\n");
+			goto done;
+		}
+
+	    /** Poll register around 1 ms */
+		for (; tries < FW_POLL_TRIES; ++tries) {
+			ret = sd_read_reg(priv, reset_reg, &value);
+			if (ret) {
+				PRINTM(ERROR, "Failed to read register.\n");
+				goto done;
+			}
+			if (value == 0)
+			    /** FW is ready */
+				break;
+			udelay(1000);
+		}
+		if (value) {
+			PRINTM(ERROR,
+			       "Failed to poll FW reset register %X=0x%x\n",
+			       reset_reg, value);
+			ret = -EFAULT;
+			goto done;
+		}
+	}
+
+	sbi_enable_host_int(priv);
+	/** reload FW */
+	ret = bt_init_fw(priv);
+	if (ret) {
+		PRINTM(ERROR, "Re download firmware failed.\n");
+		ret = -EFAULT;
+	}
+	LEAVE();
+	return ret;
+done:
+	sbi_enable_host_int(priv);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function request to reload firmware
+ *
+ *  @param priv   A pointer to bt_private
+ *  @param mode   fw reload mode.
+ *
+ *  @return         N/A
+ */
+void
+bt_request_fw_reload(bt_private *priv, int mode)
+{
+	ENTER();
+	if (mode == FW_RELOAD_WITH_EMULATION) {
+		bt_fw_reload = FW_RELOAD_WITH_EMULATION;
+		PRINTM(MSG, "BT: FW reload with re-emulation...\n");
+		LEAVE();
+		return;
+	}
+	/** Reload FW */
+	priv->fw_reload = TRUE;
+	if (bt_reload_fw(priv, mode)) {
+		PRINTM(ERROR, "FW reload fail\n");
+		goto done;
+	}
+	priv->fw_reload = FALSE;
+	/** Other operation here? */
+done:
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function frees the structure of adapter
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    N/A
+ */
+void
+bt_free_adapter(bt_private *priv)
+{
+	bt_adapter *adapter = priv->adapter;
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	skb_queue_purge(&priv->adapter->fwdump_queue);
+	kfree(adapter->tx_buffer);
+	kfree(adapter->hw_regs_buf);
+	/* Free allocated memory for fwdump filename */
+	if (adapter->fwdump_fname) {
+		kfree(adapter->fwdump_fname);
+		adapter->fwdump_fname = NULL;
+	}
+	/* Free the adapter object itself */
+	kfree(adapter);
+	priv->adapter = NULL;
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function handles the wrapper_dev ioctl
+ *
+ *  @param hev     A pointer to wrapper_dev structure
+ *  @cmd            ioctl cmd
+ *  @arg            argument
+ *  @return    -ENOIOCTLCMD
+ */
+static int
+mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, void *arg)
+{
+	bt_private *priv = NULL;
+	int ret = 0;
+#ifdef BLE_WAKEUP
+	u16 len;
+#endif
+
+	ENTER();
+
+	if (!m_dev || !m_dev->driver_data) {
+		PRINTM(ERROR, "Ioctl for unknown device (m_dev=NULL)\n");
+		ret = -ENODEV;
+		goto done;
+	}
+	priv = (bt_private *)m_dev->driver_data;
+	if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+		PRINTM(ERROR, "HCI_RUNNING not set, flag=0x%lx\n",
+		       m_dev->flags);
+		ret = -EBUSY;
+		goto done;
+	}
+	PRINTM(INFO, "IOCTL: cmd=%d\n", cmd);
+	switch (cmd) {
+#ifdef BLE_WAKEUP
+	case MBTCHAR_IOCTL_BLE_WAKEUP_PARAM:
+		PRINTM(MSG, "BT: Set ble wakeup parameters\n");
+		if (copy_from_user(&len, arg, sizeof(u16))) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Fail to copy ble wakeup params length\n");
+			ret = -EFAULT;
+			goto done;
+		}
+		/** Convert little endian length */
+		len = __le16_to_cpu(len);
+		if (len < 2) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Invalid ble wakeup params len %d\n",
+			       len);
+			ret = -EFAULT;
+			goto done;
+		}
+		if ((len + sizeof(u16)) > priv->ble_wakeup_buf_size) {
+			if (priv->ble_wakeup_buf) {
+				kfree(priv->ble_wakeup_buf);
+				priv->ble_wakeup_buf = NULL;
+				priv->ble_wakeup_buf_size = 0;
+			}
+			priv->ble_wakeup_buf =
+				kzalloc(len + sizeof(u16), GFP_KERNEL);
+			if (!priv->ble_wakeup_buf) {
+				PRINTM(ERROR, "BT_IOCTL: Fail to alloc buffer\t"
+				       "for ble wakeup parameters\n");
+				ret = -ENOMEM;
+				goto done;
+			}
+			priv->ble_wakeup_buf_size = len + sizeof(u16);
+		}
+		if (copy_from_user
+		    (priv->ble_wakeup_buf, arg, len + sizeof(u16))) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Fail to copy ble wakeup params\n");
+			ret = -EFAULT;
+			goto done;
+		}
+		DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_PARAM:", priv->ble_wakeup_buf,
+			    len + sizeof(u16));
+		break;
+#endif
+	default:
+		break;
+	}
+
+done:
+#ifdef BLE_WAKEUP
+	if (ret && priv->ble_wakeup_buf) {
+		kfree(priv->ble_wakeup_buf);
+		priv->ble_wakeup_buf = NULL;
+		priv->ble_wakeup_buf_size = 0;
+	}
+#endif
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function handles wrapper device destruct
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    N/A
+ */
+static void
+mdev_destruct(struct m_dev *m_dev)
+{
+	ENTER();
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function handles the wrapper device transmit
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    BT_STATUS_SUCCESS or other error no.
+ */
+static int
+mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb)
+{
+	bt_private *priv = NULL;
+
+	ENTER();
+	if (!m_dev || !m_dev->driver_data) {
+		PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n");
+		LEAVE();
+		return -ENODEV;
+	}
+	priv = (bt_private *)m_dev->driver_data;
+	if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+		PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n",
+		       m_dev->flags);
+		LEAVE();
+		return -EBUSY;
+	}
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		m_dev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		m_dev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		m_dev->stat.sco_tx++;
+		break;
+	}
+
+	if (m_dev->dev_type == DEBUG_TYPE) {
+		/* remember the ogf_ocf */
+		priv->debug_device_pending = 1;
+		priv->debug_ocf_ogf[0] = skb->data[0];
+		priv->debug_ocf_ogf[1] = skb->data[1];
+		PRINTM(CMD, "debug_ocf_ogf[0]=0x%x debug_ocf_ogf[1]=0x%x\n",
+		       priv->debug_ocf_ogf[0], priv->debug_ocf_ogf[1]);
+	}
+
+	if (priv->adapter->tx_lock == TRUE)
+		skb_queue_tail(&priv->adapter->pending_queue, skb);
+	else
+		bt_queue_frame(priv, skb);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function flushes the transmit queue
+ *
+ *  @param m_dev     A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+mdev_flush(struct m_dev *m_dev)
+{
+	bt_private *priv = (bt_private *)m_dev->driver_data;
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	skb_queue_purge(&priv->adapter->pending_queue);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function closes the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+mdev_close(struct m_dev *m_dev)
+{
+
+	ENTER();
+	mdev_req_lock(m_dev);
+	if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) {
+		mdev_req_unlock(m_dev);
+		LEAVE();
+		return 0;
+	}
+
+	if (m_dev->flush)
+		m_dev->flush(m_dev);
+	/* wait up pending read and unregister char dev */
+	wake_up_interruptible(&m_dev->req_wait_q);
+	/* Drop queues */
+	skb_queue_purge(&m_dev->rx_q);
+	if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) {
+		mdev_req_unlock(m_dev);
+		LEAVE();
+		return 0;
+	}
+	module_put(THIS_MODULE);
+	m_dev->flags = 0;
+	mdev_req_unlock(m_dev);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function opens the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+static int
+mdev_open(struct m_dev *m_dev)
+{
+	ENTER();
+
+	if (try_module_get(THIS_MODULE) == 0)
+		return BT_STATUS_FAILURE;
+
+	set_bit(HCI_RUNNING, &m_dev->flags);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function queries the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @param arg     arguement
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+void
+mdev_query(struct m_dev *m_dev, void *arg)
+{
+	struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer;
+
+	ENTER();
+	if (copy_to_user(arg, &mbt_dev->type, sizeof(mbt_dev->type)))
+		PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n");
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function initializes the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+void
+init_m_dev(struct m_dev *m_dev)
+{
+	m_dev->dev_pointer = NULL;
+	m_dev->driver_data = NULL;
+	m_dev->dev_type = 0;
+	m_dev->spec_type = 0;
+	skb_queue_head_init(&m_dev->rx_q);
+	init_waitqueue_head(&m_dev->req_wait_q);
+	init_waitqueue_head(&m_dev->rx_wait_q);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+	init_MUTEX(&m_dev->req_lock);
+#else
+	sema_init(&m_dev->req_lock, 1);
+#endif
+	spin_lock_init(&m_dev->rxlock);
+	memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats));
+	m_dev->open = mdev_open;
+	m_dev->close = mdev_close;
+	m_dev->flush = mdev_flush;
+	m_dev->send = mdev_send_frame;
+	m_dev->destruct = mdev_destruct;
+	m_dev->ioctl = mdev_ioctl;
+	m_dev->query = mdev_query;
+	m_dev->owner = THIS_MODULE;
+
+}
+
+/**
+ *  @brief This function handles the major job in bluetooth driver.
+ *  it handles the event generated by firmware, rx data received
+ *  from firmware and tx data sent from kernel.
+ *
+ *  @param data    A pointer to bt_thread structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+static int
+bt_service_main_thread(void *data)
+{
+	bt_thread *thread = data;
+	bt_private *priv = thread->priv;
+	bt_adapter *adapter = priv->adapter;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
+	wait_queue_t wait;
+#else
+	wait_queue_entry_t wait;
+#endif
+	struct sk_buff *skb;
+	ENTER();
+	bt_activate_thread(thread);
+	init_waitqueue_entry(&wait, current);
+	current->flags |= PF_NOFREEZE;
+
+	for (;;) {
+		add_wait_queue(&thread->waitQ, &wait);
+		OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE);
+		if (priv->adapter->WakeupTries ||
+		    ((!priv->adapter->IntCounter) &&
+		     (!priv->bt_dev.tx_dnld_rdy ||
+		      skb_queue_empty(&priv->adapter->tx_queue))
+		     && skb_queue_empty(&priv->adapter->fwdump_queue)
+		    )) {
+			PRINTM(INFO, "Main: Thread sleeping...\n");
+			schedule();
+		}
+		OS_SET_THREAD_STATE(TASK_RUNNING);
+		remove_wait_queue(&thread->waitQ, &wait);
+		if (kthread_should_stop() || adapter->SurpriseRemoved) {
+			PRINTM(INFO, "main-thread: break from main thread: "
+			       "SurpriseRemoved=0x%x\n",
+			       adapter->SurpriseRemoved);
+			break;
+		}
+
+		PRINTM(INFO, "Main: Thread waking up...\n");
+
+		if (priv->adapter->IntCounter) {
+			OS_INT_DISABLE;
+			adapter->IntCounter = 0;
+			OS_INT_RESTORE;
+			sbi_get_int_status(priv);
+		} else if ((priv->adapter->ps_state == PS_SLEEP) &&
+			   !skb_queue_empty(&priv->adapter->tx_queue)) {
+			priv->adapter->WakeupTries++;
+			sbi_wakeup_firmware(priv);
+			continue;
+		}
+		if (priv->adapter->ps_state == PS_SLEEP)
+			continue;
+		if (priv->bt_dev.tx_dnld_rdy == TRUE) {
+			if (!skb_queue_empty(&priv->adapter->tx_queue)) {
+				skb = skb_dequeue(&priv->adapter->tx_queue);
+				if (skb)
+					send_single_packet(priv, skb);
+			}
+		}
+		if (!skb_queue_empty(&priv->adapter->fwdump_queue)) {
+			skb = skb_dequeue(&priv->adapter->fwdump_queue);
+			if (skb) {
+				bt_store_firmware_dump(priv, skb->data,
+						       skb->len);
+				dev_kfree_skb_any(skb);
+			}
+		}
+	}
+	bt_deactivate_thread(thread);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handles the interrupt. it will change PS
+ *  state if applicable. it will wake up main_thread to handle
+ *  the interrupt event as well.
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @return        N/A
+ */
+void
+bt_interrupt(struct m_dev *m_dev)
+{
+	bt_private *priv = (bt_private *)m_dev->driver_data;
+	ENTER();
+	if (!priv || !priv->adapter) {
+		LEAVE();
+		return;
+	}
+	PRINTM(INTR, "*\n");
+	priv->adapter->ps_state = PS_AWAKE;
+	if (priv->adapter->hs_state == HS_ACTIVATED) {
+		PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name);
+		priv->adapter->hs_state = HS_DEACTIVATED;
+	}
+	priv->adapter->WakeupTries = 0;
+	priv->adapter->IntCounter++;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	LEAVE();
+}
+
+/**
+ * @brief  Dynamic release of char dev
+ *
+ * @param kobj          A pointer to kobject structure
+ *
+ * @return                N/A
+ */
+static void
+char_dev_release_dynamic(struct kobject *kobj)
+{
+	struct char_dev *cdev = container_of(kobj, struct char_dev, kobj);
+	ENTER();
+	PRINTM(INFO, "free char_dev\n");
+	kfree(cdev);
+	LEAVE();
+}
+
+static struct kobj_type ktype_char_dev_dynamic = {
+	.release = char_dev_release_dynamic,
+};
+
+/**
+ * @brief  Allocation of char dev
+ *
+ * @param           	N/A
+ *
+ * @return              char_dev
+ */
+static struct char_dev *
+alloc_char_dev(void)
+{
+	struct char_dev *cdev;
+	ENTER();
+	cdev = kzalloc(sizeof(struct char_dev), GFP_KERNEL);
+	if (cdev) {
+		kobject_init(&cdev->kobj, &ktype_char_dev_dynamic);
+		PRINTM(INFO, "alloc char_dev\n");
+	}
+	return cdev;
+}
+
+/**
+ * @brief  Dynamic release of bt private
+ *
+ * @param kobj          A pointer to kobject structure
+ *
+ * @return                N/A
+ */
+static void
+bt_private_dynamic_release(struct kobject *kobj)
+{
+	bt_private *priv = container_of(kobj, bt_private, kobj);
+	ENTER();
+	PRINTM(INFO, "free bt priv\n");
+	kfree(priv);
+	LEAVE();
+}
+
+static struct kobj_type ktype_bt_private_dynamic = {
+	.release = bt_private_dynamic_release,
+};
+
+/**
+ * @brief  Allocation of bt private
+ *
+ * @param           	N/A
+ *
+ * @return              bt_private
+ */
+static bt_private *
+bt_alloc_priv(void)
+{
+	bt_private *priv;
+	ENTER();
+	priv = kzalloc(sizeof(bt_private), GFP_KERNEL);
+	if (priv) {
+		kobject_init(&priv->kobj, &ktype_bt_private_dynamic);
+		PRINTM(INFO, "alloc bt priv\n");
+	}
+	LEAVE();
+	return priv;
+}
+
+/**
+ * @brief  Get bt private structure
+ *
+ * @param priv          A pointer to bt_private structure
+ *
+ * @return              kobject structure
+ */
+struct kobject *
+bt_priv_get(bt_private *priv)
+{
+	PRINTM(INFO, "bt priv get object");
+	return kobject_get(&priv->kobj);
+}
+
+/**
+ * @brief  Get bt private structure
+ *
+ * @param priv          A pointer to bt_private structure
+ *
+ * @return              N/A
+ */
+void
+bt_priv_put(bt_private *priv)
+{
+	PRINTM(INFO, "bt priv put object");
+	kobject_put(&priv->kobj);
+}
+
+/**
+ *  @brief This function send init commands to firmware
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @return       BT_STATUS_SUCESS/BT_STATUS_FAILURE
+ */
+int
+bt_init_cmd(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	if (mbt_gpio_pin) {
+		ret = bt_set_gpio_pin(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "GPIO pin set failed!\n");
+			goto done;
+		}
+	}
+	ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+	if (ret < 0) {
+		PRINTM(FATAL, "Module cfg command send failed!\n");
+		goto done;
+	}
+	if (btindrst != -1) {
+		ret = bt_set_independent_reset(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "Independent reset failed!\n");
+			goto done;
+		}
+	}
+	if (btpmic
+	    && ((priv->card_type == CARD_TYPE_SD8997) ||
+		(priv->card_type == CARD_TYPE_SD8977))
+		) {
+		if (BT_STATUS_SUCCESS != bt_pmic_configure(priv)) {
+			PRINTM(FATAL, "BT: PMIC Configure failed \n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	ret = bt_set_ble_deepsleep(priv, deep_sleep ? TRUE : FALSE);
+	if (ret < 0) {
+		PRINTM(FATAL, "%s BLE deepsleep failed!\n",
+		       deep_sleep ? "Enable" : "Disable");
+		goto done;
+	}
+	if (psmode) {
+		priv->bt_dev.psmode = TRUE;
+		priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME;
+		ret = bt_enable_ps(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "Enable PS mode failed!\n");
+			goto done;
+		}
+	}
+#if defined(SDIO_SUSPEND_RESUME)
+	priv->bt_dev.gpio_gap = DEF_GPIO_GAP;
+	ret = bt_send_hscfg_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "Send HSCFG failed!\n");
+		goto done;
+	}
+#endif
+	priv->bt_dev.sdio_pull_cfg = 0xffffffff;
+	priv->bt_dev.sdio_pull_ctrl = 0;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reinit firmware after redownload firmware
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @return       BT_STATUS_SUCESS/BT_STATUS_FAILURE
+ */
+int
+bt_reinit_fw(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	priv->adapter->tx_lock = FALSE;
+	priv->adapter->ps_state = PS_AWAKE;
+	priv->adapter->suspend_fail = FALSE;
+	priv->adapter->is_suspended = FALSE;
+	priv->adapter->hs_skip = 0;
+	priv->adapter->num_cmd_timeout = 0;
+
+	ret = bt_init_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "BT init command failed!\n");
+		goto done;
+	}
+	/* block all the packet from bluez */
+	if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext)
+		priv->adapter->tx_lock = TRUE;
+
+	if (init_cfg)
+		if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+			PRINTM(FATAL,
+			       "BT: Set user init data and param failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+
+	if (cal_cfg) {
+		if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+			PRINTM(FATAL, "BT: Set cal data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	if (bt_mac) {
+		PRINTM(INFO,
+		       "Set BT mac_addr from insmod parametre bt_mac = %s\n",
+		       bt_mac);
+		if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) {
+			PRINTM(FATAL,
+			       "BT: Fail to set mac address from insmod parametre\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	if (cal_cfg_ext) {
+		if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) {
+			PRINTM(FATAL, "BT: Set cal ext data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) {
+		priv->adapter->tx_lock = FALSE;
+		bt_restore_tx_queue(priv);
+	}
+	bt_get_fw_version(priv);
+	snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN,
+		 mbt_driver_version, fw_version);
+done:
+	return ret;
+}
+
+/**
+ *  @brief Module configuration and register device
+ *
+ *  @param priv      A Pointer to bt_private structure
+ *  @return      BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_conf_dpc(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct mbt_dev *mbt_dev = NULL;
+	struct nfc_dev *nfc_dev = NULL;
+	struct debug_dev *debug_dev = NULL;
+	int i = 0;
+	struct char_dev *char_dev = NULL;
+	char dev_file[DEV_NAME_LEN + 5];
+	unsigned char dev_type = 0;
+
+	ENTER();
+
+	priv->bt_dev.tx_dnld_rdy = TRUE;
+	if (priv->fw_reload) {
+		bt_reinit_fw(priv);
+		LEAVE();
+		return ret;
+	}
+
+	if (drv_mode & DRV_MODE_BT) {
+		mbt_dev = alloc_mbt_dev();
+		if (!mbt_dev) {
+			PRINTM(FATAL, "Can not allocate mbt dev\n");
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		init_m_dev(&(priv->bt_dev.m_dev[BT_SEQ]));
+		priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE;
+		priv->bt_dev.m_dev[BT_SEQ].spec_type = IANYWHERE_SPEC;
+		priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)mbt_dev;
+		priv->bt_dev.m_dev[BT_SEQ].driver_data = priv;
+		priv->bt_dev.m_dev[BT_SEQ].read_continue_flag = 0;
+	}
+
+	dev_type = HCI_SDIO;
+
+	if (mbt_dev)
+		mbt_dev->type = dev_type;
+
+	ret = bt_init_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "BT init command failed!\n");
+		goto done;
+	}
+
+	if (mbt_dev && priv->bt_dev.devType == DEV_TYPE_AMP) {
+		mbt_dev->type |= HCI_BT_AMP;
+		priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_AMP_TYPE;
+	}
+	/** Process device tree init parameters before register hci device.
+	 *  Since uplayer device has not yet registered, no need to block tx queue.
+	 * */
+	if (init_cfg)
+		if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+			PRINTM(FATAL,
+			       "BT: Set user init data and param failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	if (cal_cfg) {
+		if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+			PRINTM(FATAL, "BT: Set cal data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	} else if (bt_mac) {
+		PRINTM(INFO,
+		       "Set BT mac_addr from insmod parametre bt_mac = %s\n",
+		       bt_mac);
+		if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) {
+			PRINTM(FATAL,
+			       "BT: Fail to set mac address from insmod parametre\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cal_cfg_ext) {
+		if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) {
+			PRINTM(FATAL, "BT: Set cal ext data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	/* Get FW version */
+	bt_get_fw_version(priv);
+	snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN,
+		 mbt_driver_version, fw_version);
+
+	if (mbt_dev) {
+		/** init mbt_dev */
+		mbt_dev->flags = 0;
+		mbt_dev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
+		mbt_dev->esco_type = (ESCO_HV1);
+		mbt_dev->link_mode = (HCI_LM_ACCEPT);
+
+		mbt_dev->idle_timeout = 0;
+		mbt_dev->sniff_max_interval = 800;
+		mbt_dev->sniff_min_interval = 80;
+		for (i = 0; i < 3; i++)
+			mbt_dev->reassembly[i] = NULL;
+		atomic_set(&mbt_dev->promisc, 0);
+
+		/** alloc char dev node */
+		char_dev = alloc_char_dev();
+		if (!char_dev) {
+			class_destroy(chardev_class);
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		char_dev->minor = MBTCHAR_MINOR_BASE + mbtchar_minor;
+		if (mbt_dev->type & HCI_BT_AMP)
+			char_dev->dev_type = BT_AMP_TYPE;
+		else
+			char_dev->dev_type = BT_TYPE;
+
+		if (bt_name)
+			snprintf(mbt_dev->name, sizeof(mbt_dev->name), "%s%d",
+				 bt_name, mbtchar_minor);
+		else
+			snprintf(mbt_dev->name, sizeof(mbt_dev->name),
+				 "mbtchar%d", mbtchar_minor);
+		snprintf(dev_file, sizeof(dev_file), "/dev/%s", mbt_dev->name);
+		mbtchar_minor++;
+		PRINTM(MSG, "BT: Create %s\n", dev_file);
+
+		/** register m_dev to BT char device */
+		priv->bt_dev.m_dev[BT_SEQ].index = char_dev->minor;
+		char_dev->m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+
+		/** create BT char device node */
+		register_char_dev(char_dev, chardev_class, MODULE_NAME,
+				  mbt_dev->name);
+
+		/** chmod & chown for BT char device */
+		mbtchar_chown(dev_file, AID_SYSTEM, AID_NET_BT_STACK);
+		mbtchar_chmod(dev_file, 0666);
+
+		/** create proc device */
+		snprintf(priv->bt_dev.m_dev[BT_SEQ].name,
+			 sizeof(priv->bt_dev.m_dev[BT_SEQ].name),
+			 mbt_dev->name);
+		bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ);
+	}
+
+	if ((drv_mode & DRV_MODE_NFC) &&
+	    (!(priv->bt_dev.devType == DEV_TYPE_AMP)) &&
+	    (priv->bt_dev.devFeature & DEV_FEATURE_NFC)) {
+
+		/** alloc nfc_dev */
+		nfc_dev = alloc_nfc_dev();
+		if (!nfc_dev) {
+			PRINTM(FATAL, "Can not allocate nfc dev\n");
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+
+		/** init m_dev */
+		init_m_dev(&(priv->bt_dev.m_dev[NFC_SEQ]));
+		priv->bt_dev.m_dev[NFC_SEQ].dev_type = NFC_TYPE;
+		priv->bt_dev.m_dev[NFC_SEQ].spec_type = GENERIC_SPEC;
+		priv->bt_dev.m_dev[NFC_SEQ].dev_pointer = (void *)nfc_dev;
+		priv->bt_dev.m_dev[NFC_SEQ].driver_data = priv;
+		priv->bt_dev.m_dev[NFC_SEQ].read_continue_flag = 0;
+
+		/** create char device for NFC */
+		char_dev = alloc_char_dev();
+		if (!char_dev) {
+			class_destroy(chardev_class);
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		char_dev->minor = NFCCHAR_MINOR_BASE + nfcchar_minor;
+		char_dev->dev_type = NFC_TYPE;
+		if (nfc_name)
+			snprintf(nfc_dev->name, sizeof(nfc_dev->name), "%s%d",
+				 nfc_name, nfcchar_minor);
+		else
+			snprintf(nfc_dev->name, sizeof(nfc_dev->name),
+				 "mnfcchar%d", nfcchar_minor);
+		snprintf(dev_file, sizeof(dev_file), "/dev/%s", nfc_dev->name);
+		PRINTM(MSG, "BT: Create %s\n", dev_file);
+		nfcchar_minor++;
+
+		/** register m_dev to NFC char device */
+		priv->bt_dev.m_dev[NFC_SEQ].index = char_dev->minor;
+		char_dev->m_dev = &(priv->bt_dev.m_dev[NFC_SEQ]);
+
+		/** register char dev */
+		register_char_dev(char_dev, chardev_class, MODULE_NAME,
+				  nfc_dev->name);
+
+		/** chmod for NFC char device */
+		mbtchar_chmod(dev_file, 0666);
+
+		/** create proc device */
+		snprintf(priv->bt_dev.m_dev[NFC_SEQ].name,
+			 sizeof(priv->bt_dev.m_dev[NFC_SEQ].name),
+			 nfc_dev->name);
+		bt_proc_init(priv, &(priv->bt_dev.m_dev[NFC_SEQ]), NFC_SEQ);
+	}
+
+	if ((debug_intf) && ((drv_mode & DRV_MODE_BT)
+			     || (drv_mode & DRV_MODE_NFC)
+	    )) {
+		/** alloc debug_dev */
+		debug_dev = alloc_debug_dev();
+		if (!debug_dev) {
+			PRINTM(FATAL, "Can not allocate debug dev\n");
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+
+		/** init m_dev */
+		init_m_dev(&(priv->bt_dev.m_dev[DEBUG_SEQ]));
+		priv->bt_dev.m_dev[DEBUG_SEQ].dev_type = DEBUG_TYPE;
+		priv->bt_dev.m_dev[DEBUG_SEQ].spec_type = GENERIC_SPEC;
+		priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = (void *)debug_dev;
+		priv->bt_dev.m_dev[DEBUG_SEQ].driver_data = priv;
+
+		/** create char device for Debug */
+		char_dev = alloc_char_dev();
+		if (!char_dev) {
+			class_destroy(chardev_class);
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		char_dev->minor = DEBUGCHAR_MINOR_BASE + debugchar_minor;
+		char_dev->dev_type = DEBUG_TYPE;
+		if (debug_name)
+			snprintf(debug_dev->name, sizeof(debug_dev->name),
+				 "%s%d", debug_name, debugchar_minor);
+		else
+			snprintf(debug_dev->name, sizeof(debug_dev->name),
+				 "mdebugchar%d", debugchar_minor);
+		snprintf(dev_file, sizeof(dev_file), "/dev/%s",
+			 debug_dev->name);
+		PRINTM(MSG, "BT: Create %s\n", dev_file);
+		debugchar_minor++;
+
+		/** register char dev */
+		priv->bt_dev.m_dev[DEBUG_SEQ].index = char_dev->minor;
+		char_dev->m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+		register_char_dev(char_dev, chardev_class, MODULE_NAME,
+				  debug_dev->name);
+
+		/** chmod for debug char device */
+		mbtchar_chmod(dev_file, 0666);
+
+		/** create proc device */
+		snprintf(priv->bt_dev.m_dev[DEBUG_SEQ].name,
+			 sizeof(priv->bt_dev.m_dev[DEBUG_SEQ].name),
+			 debug_dev->name);
+		bt_proc_init(priv, &(priv->bt_dev.m_dev[DEBUG_SEQ]), DEBUG_SEQ);
+	}
+
+done:
+	LEAVE();
+	return ret;
+err_kmalloc:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function adds the card. it will probe the
+ *  card, allocate the bt_priv and initialize the device.
+ *
+ *  @param card    A pointer to card
+ *  @return        A pointer to bt_private structure
+ */
+
+bt_private *
+bt_add_card(void *card)
+{
+	bt_private *priv = NULL;
+	int index = 0;
+
+	ENTER();
+
+	priv = bt_alloc_priv();
+	if (!priv) {
+		PRINTM(FATAL, "Can not allocate priv\n");
+		LEAVE();
+		return NULL;
+	}
+	/* Save the handle */
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == NULL)
+			break;
+	}
+	if (index < MAX_BT_ADAPTER) {
+		m_priv[index] = priv;
+	} else {
+		PRINTM(ERROR, "Exceeded maximum cards supported!\n");
+		goto err_kmalloc;
+	}
+	/* allocate buffer for bt_adapter */
+	priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL);
+	if (!priv->adapter) {
+		PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->tx_buffer =
+		kzalloc(MAX_TX_BUF_SIZE + DMA_ALIGNMENT, GFP_KERNEL);
+	if (!priv->adapter->tx_buffer) {
+		PRINTM(FATAL, "Allocate buffer for transmit\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->tx_buf =
+		(u8 *)ALIGN_ADDR(priv->adapter->tx_buffer, DMA_ALIGNMENT);
+	priv->adapter->hw_regs_buf =
+		kzalloc(SD_BLOCK_SIZE + DMA_ALIGNMENT, GFP_KERNEL);
+	if (!priv->adapter->hw_regs_buf) {
+		PRINTM(FATAL, "Allocate buffer for INT read buf failed!\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->hw_regs =
+		(u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, DMA_ALIGNMENT);
+	bt_init_adapter(priv);
+
+	PRINTM(INFO, "Starting kthread...\n");
+	priv->MainThread.priv = priv;
+	spin_lock_init(&priv->driver_lock);
+
+	bt_create_thread(bt_service_main_thread, &priv->MainThread,
+			 "bt_main_service");
+
+	/* wait for mainthread to up */
+	while (!priv->MainThread.pid)
+		os_sched_timeout(1);
+
+	sdio_update_card_type(priv, card);
+	/* Update driver version */
+	if (priv->card_type == CARD_TYPE_SD8787)
+		memcpy(mbt_driver_version, CARD_SD8787, strlen(CARD_SD8787));
+	else if (priv->card_type == CARD_TYPE_SD8777)
+		memcpy(mbt_driver_version, CARD_SD8777, strlen(CARD_SD8777));
+	else if (priv->card_type == CARD_TYPE_SD8887)
+		memcpy(mbt_driver_version, CARD_SD8887, strlen(CARD_SD8887));
+	else if (priv->card_type == CARD_TYPE_SD8897)
+		memcpy(mbt_driver_version, CARD_SD8897, strlen(CARD_SD8897));
+	else if (priv->card_type == CARD_TYPE_SD8797)
+		memcpy(mbt_driver_version, CARD_SD8797, strlen(CARD_SD8797));
+	else if (priv->card_type == CARD_TYPE_SD8977)
+		memcpy(mbt_driver_version, CARD_SD8977, strlen(CARD_SD8977));
+	else if (priv->card_type == CARD_TYPE_SD8997)
+		memcpy(mbt_driver_version, CARD_SD8997, strlen(CARD_SD8997));
+	else if (priv->card_type == CARD_TYPE_SD8987)
+		memcpy(mbt_driver_version, CARD_SD8987, strlen(CARD_SD8987));
+
+	if (BT_STATUS_SUCCESS != sdio_get_sdio_device(priv))
+		goto err_kmalloc;
+
+	/** user config file */
+	init_waitqueue_head(&priv->init_user_conf_wait_q);
+
+	priv->bt_dev.card = card;
+
+	((struct sdio_mmc_card *)card)->priv = priv;
+	priv->adapter->sd_ireg = 0;
+	/*
+	 * Register the device. Fillup the private data structure with
+	 * relevant information from the card and request for the required
+	 * IRQ.
+	 */
+	if (sbi_register_dev(priv) < 0) {
+		PRINTM(FATAL, "Failed to register bt device!\n");
+		goto err_registerdev;
+	}
+	if (bt_init_fw(priv)) {
+		PRINTM(FATAL, "BT Firmware Init Failed\n");
+		goto err_init_fw;
+	}
+	LEAVE();
+	return priv;
+
+err_init_fw:
+	clean_up_m_devs(priv);
+	bt_proc_remove(priv);
+	PRINTM(INFO, "Unregister device\n");
+	sbi_unregister_dev(priv);
+err_registerdev:
+	((struct sdio_mmc_card *)card)->priv = NULL;
+	/* Stop the thread servicing the interrupts */
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid)
+		os_sched_timeout(1);
+err_kmalloc:
+	if (priv->adapter)
+		bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return NULL;
+}
+
+/**
+ *  @brief This function send hardware remove event
+ *
+ *  @param priv    A pointer to bt_private
+ *  @return        N/A
+ */
+void
+bt_send_hw_remove_event(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+	ENTER();
+	if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		LEAVE();
+		return;
+	}
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+#define HCI_HARDWARE_ERROR_EVT  0x10
+#define HCI_HARDWARE_REMOVE     0x24
+	skb = bt_skb_alloc(3, GFP_ATOMIC);
+	skb->data[0] = HCI_HARDWARE_ERROR_EVT;
+	skb->data[1] = 1;
+	skb->data[2] = HCI_HARDWARE_REMOVE;
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+	skb_put(skb, 3);
+	if (mbt_dev) {
+		skb->dev = (void *)mdev_bt;
+		PRINTM(MSG, "Send HW ERROR event\n");
+		if (!mdev_recv_frame(skb)) {
+#define RX_WAIT_TIMEOUT				300
+			mdev_bt->wait_rx_complete = TRUE;
+			mdev_bt->rx_complete_flag = FALSE;
+			if (os_wait_interruptible_timeout
+			    (mdev_bt->rx_wait_q, mdev_bt->rx_complete_flag,
+			     RX_WAIT_TIMEOUT))
+				PRINTM(MSG, "BT stack received the event\n");
+			mdev_bt->stat.byte_rx += 3;
+		}
+	}
+	LEAVE();
+	return;
+}
+
+#ifdef BLE_WAKEUP
+/**
+ *  @brief This function used to config BLE wakeup pattern
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        N/A
+ */
+int
+bt_config_ble_wakeup(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sk_buff *skb = NULL;
+	u16 ocf, left_len;
+	u8 len, more_cmd;
+	u8 *pos;
+
+	ENTER();
+
+	if (!priv->ble_wakeup_buf) {
+		PRINTM(ERROR, "BT: no ble wakeup parameters found\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	PRINTM(MSG, "Config ble wakeup pattern\n");
+
+	pos = priv->ble_wakeup_buf;
+	left_len = *(u16 *) pos;
+	left_len = __le16_to_cpu(left_len);
+	pos += sizeof(u16);
+
+	while (left_len >= 2) {
+		more_cmd = *pos;
+		len = *(pos + 1);
+		if (((len + 2) > left_len) ||
+		    (!more_cmd && ((len + 2) < left_len))) {
+			PRINTM(ERROR, "Invalid ble parameters\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		skb = bt_skb_alloc(len, GFP_ATOMIC);
+		if (!skb) {
+			PRINTM(ERROR, "BT BLE WAKEUP: fail to alloc skb\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		memcpy(skb_put(skb, len), pos + 2, len);
+		bt_cb(skb)->pkt_type = *(u8 *)skb->data;
+		skb_pull(skb, 1);
+		DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_CMD:", skb->data, skb->len);
+		skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+		skb_queue_head(&priv->adapter->tx_queue, skb);
+		priv->bt_dev.sendcmdflag = TRUE;
+		priv->bt_dev.send_cmd_opcode = *(u16 *) skb->data;
+		ocf = hci_opcode_ocf(priv->bt_dev.send_cmd_opcode);
+		priv->adapter->cmd_complete = FALSE;
+
+		wake_up_interruptible(&priv->MainThread.waitQ);
+		if (!os_wait_interruptible_timeout
+		    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+		     WAIT_UNTIL_CMD_RESP)) {
+			ret = BT_STATUS_FAILURE;
+			PRINTM(ERROR,
+			       "BT: Set  Set ble wakeup cmd 0x%x timeout:\n",
+			       priv->bt_dev.send_cmd_opcode);
+			bt_cmd_timeout_func(priv, ocf);
+			goto done;
+		}
+
+		pos += len + 2;
+		left_len -= len + 2;
+	}
+
+done:
+	if (ret != BT_STATUS_SUCCESS) {
+		if (priv->ble_wakeup_buf) {
+			kfree(priv->ble_wakeup_buf);
+			priv->ble_wakeup_buf = NULL;
+			priv->ble_wakeup_buf_size = 0;
+		}
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function send system suspend event
+ *
+ *  @param priv    A pointer to bt_private
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_send_system_event(bt_private *priv, u8 flag)
+{
+	struct sk_buff *skb = NULL;
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+
+	ENTER();
+
+	if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+
+	skb = bt_skb_alloc(4, GFP_ATOMIC);
+	if (!skb) {
+		PRINTM(ERROR, "Fail to allocate sys suspend event skb\n");
+		return BT_STATUS_FAILURE;
+	}
+	skb->data[0] = VENDOR_SPECIFIC_EVENT;
+	skb->data[1] = 2;
+	skb->data[2] = HCI_SYSTEM_SUSPEND_EVT;
+	if (flag)
+		skb->data[3] = HCI_SYSTEM_SUSPEND;
+	else
+		skb->data[3] = HCI_SYSTEM_RESUME;
+
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+	skb_put(skb, 4);
+	if (mbt_dev) {
+		skb->dev = (void *)mdev_bt;
+		PRINTM(MSG, "Send system %s event\n",
+		       flag ? "suspend" : "resume");
+		if (!mdev_recv_frame(skb)) {
+#define RX_WAIT_TIMEOUT                         300
+			mdev_bt->wait_rx_complete = TRUE;
+			mdev_bt->rx_complete_flag = FALSE;
+			if (os_wait_interruptible_timeout(mdev_bt->rx_wait_q,
+							  mdev_bt->
+							  rx_complete_flag,
+							  RX_WAIT_TIMEOUT))
+				PRINTM(MSG, "BT stack received the event\n");
+			mdev_bt->stat.byte_rx += 4;
+		}
+	}
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+#endif
+
+/**
+ *  @brief This function removes the card.
+ *
+ *  @param card    A pointer to card
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_remove_card(void *card)
+{
+	bt_private *priv = (bt_private *)card;
+	int index;
+	ENTER();
+	if (!priv) {
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv->adapter->SurpriseRemoved = TRUE;
+
+	bt_send_hw_remove_event(priv);
+#ifdef BLE_WAKEUP
+	if (priv->ble_wakeup_buf) {
+		kfree(priv->ble_wakeup_buf);
+		priv->ble_wakeup_buf = NULL;
+		priv->ble_wakeup_buf_size = 0;
+	}
+#endif
+	wake_up_interruptible(&priv->adapter->cmd_wait_q);
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid) {
+		os_sched_timeout(1);
+		wake_up_interruptible(&priv->MainThread.waitQ);
+	}
+
+	bt_proc_remove(priv);
+	PRINTM(INFO, "Unregister device\n");
+	sbi_unregister_dev(priv);
+	clean_up_m_devs(priv);
+	PRINTM(INFO, "Free Adapter\n");
+	bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function initializes module.
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_module(void)
+{
+	int ret = BT_STATUS_SUCCESS;
+	int index;
+	ENTER();
+	PRINTM(MSG, "BT: Loading driver\n");
+	/* Init the bt_private pointer array first */
+	for (index = 0; index < MAX_BT_ADAPTER; index++)
+		m_priv[index] = NULL;
+	bt_root_proc_init();
+
+	/** create char device class */
+	chardev_class = class_create(THIS_MODULE, MODULE_NAME);
+	if (IS_ERR(chardev_class)) {
+		PRINTM(ERROR, "Unable to allocate class\n");
+		bt_root_proc_remove();
+		ret = PTR_ERR(chardev_class);
+		goto done;
+	}
+
+	if (sbi_register() == NULL) {
+		bt_root_proc_remove();
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+done:
+	if (ret)
+		PRINTM(MSG, "BT: Driver loading failed\n");
+	else
+		PRINTM(MSG, "BT: Driver loaded successfully\n");
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function cleans module
+ *
+ *  @return        N/A
+ */
+static void
+bt_exit_module(void)
+{
+	bt_private *priv;
+	int index;
+	ENTER();
+	PRINTM(MSG, "BT: Unloading driver\n");
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		priv = m_priv[index];
+		if (!priv)
+			continue;
+		if (priv && !priv->adapter->SurpriseRemoved) {
+			if (BT_STATUS_SUCCESS == bt_send_reset_command(priv))
+				bt_send_module_cfg_cmd(priv,
+						       MODULE_SHUTDOWN_REQ);
+		}
+		sbi_disable_host_int(priv);
+
+	}
+
+	sbi_unregister();
+
+	bt_root_proc_remove();
+	class_destroy(chardev_class);
+	PRINTM(MSG, "BT: Driver unloaded\n");
+	LEAVE();
+}
+
+module_init(bt_init_module);
+module_exit(bt_exit_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+module_param(fw, int, 0);
+MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware");
+module_param(fw_crc_check, int, 0);
+MODULE_PARM_DESC(fw_crc_check,
+		 "1: Enable FW download CRC check (default); 0: Disable FW download CRC check");
+module_param(psmode, int, 0);
+MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode");
+module_param(deep_sleep, int, 0);
+MODULE_PARM_DESC(deep_sleep, "1: Enable deep sleep; 0: Disable deep sleep");
+#ifdef CONFIG_OF
+module_param(dts_enable, int, 0);
+MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS");
+#endif
+#ifdef	DEBUG_LEVEL1
+module_param(mbt_drvdbg, uint, 0);
+MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL");
+#endif
+#ifdef SDIO_SUSPEND_RESUME
+module_param(mbt_pm_keep_power, int, 0);
+MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power");
+#endif
+module_param(init_cfg, charp, 0);
+MODULE_PARM_DESC(init_cfg, "BT init config file name");
+module_param(cal_cfg, charp, 0);
+MODULE_PARM_DESC(cal_cfg, "BT calibrate file name");
+module_param(cal_cfg_ext, charp, 0);
+MODULE_PARM_DESC(cal_cfg_ext, "BT calibrate ext file name");
+module_param(bt_mac, charp, 0660);
+MODULE_PARM_DESC(bt_mac, "BT init mac address");
+module_param(drv_mode, int, 0);
+MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE;" "Bit 2: NFC");
+module_param(bt_name, charp, 0);
+MODULE_PARM_DESC(bt_name, "BT interface name");
+module_param(nfc_name, charp, 0);
+MODULE_PARM_DESC(nfc_name, "NFC interface name");
+module_param(debug_intf, int, 0);
+MODULE_PARM_DESC(debug_intf,
+		 "1: Enable debug interface; 0: Disable debug interface ");
+module_param(debug_name, charp, 0);
+MODULE_PARM_DESC(debug_name, "Debug interface name");
+module_param(bt_fw_reload, int, 0);
+MODULE_PARM_DESC(bt_fw_reload,
+		 "0: disable fw_reload; 1: enable fw reload feature");
+module_param(mbt_gpio_pin, int, 0);
+MODULE_PARM_DESC(mbt_gpio_pin,
+		 "GPIO pin to interrupt host. 0xFF: disable GPIO interrupt mode; Others: GPIO pin assigned to generate pulse to host.");
+module_param(btindrst, int, 0);
+MODULE_PARM_DESC(btindrst,
+		 "Independent reset configuration; high byte:GPIO pin number;low byte:0x0:disable, 0x1:out-band reset, 0x2:in-band reset.");
+module_param(btpmic, int, 0);
+MODULE_PARM_DESC(btpmic,
+		 "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware (default)");
+module_param(bt_fw_serial, int, 0);
+MODULE_PARM_DESC(bt_fw_serial,
+		 "0: Support parallel download FW; 1: Support serial download FW");
diff --git a/bt_sd8897/bt/bt_proc.c b/bt_sd8897/bt/bt_proc.c
new file mode 100644
index 0000000..04f87b4
--- /dev/null
+++ b/bt_sd8897/bt/bt_proc.c
@@ -0,0 +1,720 @@
+/** @file bt_proc.c
+  *
+  * @brief This file handle the functions for proc files
+  *
+  * Copyright (C) 2007-2018, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/proc_fs.h>
+
+#include "bt_drv.h"
+#include "bt_sdio.h"
+
+/** proc diretory root */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+#define PROC_DIR NULL
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+#define PROC_DIR (&proc_root)
+#else
+#define PROC_DIR proc_net
+#endif
+
+/** Proc mbt directory entry */
+static struct proc_dir_entry *proc_mbt;
+
+#define     CMD52_STR_LEN   50
+static char cmd52_string[CMD52_STR_LEN];
+
+/** proc data structure */
+struct proc_data {
+	/** Read length */
+	int rdlen;
+	/** Read buffer */
+	char *rdbuf;
+	/** Write length */
+	int wrlen;
+	/** Maximum write length */
+	int maxwrlen;
+	/** Write buffer */
+	char *wrbuf;
+	/** Private structure */
+	struct _bt_private *pbt;
+	void (*on_close) (struct inode *, struct file *);
+};
+
+/** Default file permission */
+#define DEFAULT_FILE_PERM  0644
+
+/** Bluetooth device offset */
+#define OFFSET_BT_DEV		0x01
+/** Bluetooth adapter offset */
+#define OFFSET_BT_ADAPTER	0x02
+/** Show integer */
+#define SHOW_INT		0x10
+/** Show hex */
+#define SHOW_HEX		0x20
+/** Show string */
+#define SHOW_STRING		0x40
+
+/** Device size */
+#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n)
+/** Device address */
+#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n)
+
+/** Adapter size */
+#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n)
+/** Adapter address */
+#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n)
+
+static struct item_data config_items[] = {
+#ifdef	DEBUG_LEVEL1
+	{"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX}
+	,
+#endif
+	{"idle_timeout", item_dev_size(idle_timeout), 0,
+	 item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap),
+	 OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0,
+	 item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0,
+	 item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+
+};
+
+static struct item_data status_items[] = {
+	{"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver),
+	 OFFSET_BT_ADAPTER | SHOW_STRING},
+	{"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0,
+	 item_dev_addr(tx_dnld_rdy),
+	 OFFSET_BT_DEV | SHOW_INT},
+	{"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"hs_state", item_adapter_size(hs_state), 0,
+	 item_adapter_addr(hs_state),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"ps_state", item_adapter_size(ps_state), 0,
+	 item_adapter_addr(ps_state),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"WakeupTries", item_adapter_size(WakeupTries), 0,
+	 item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT},
+	{"irq_recv", item_adapter_size(irq_recv), 0,
+	 item_adapter_addr(irq_recv),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"irq_done", item_adapter_size(irq_done), 0,
+	 item_adapter_addr(irq_done),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"skb_pending", item_adapter_size(skb_pending), 0,
+	 item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT},
+};
+
+static struct item_data debug_items[] = {
+	{"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING},
+};
+
+/**
+ *  @brief convert string to number
+ *
+ *  @param s	pointer to numbered string
+ *  @return	converted number from string s
+ */
+int
+string_to_number(char *s)
+{
+	int r = 0;
+	int base = 0;
+	int pn = 1;
+
+	if (strncmp(s, "-", 1) == 0) {
+		pn = -1;
+		s++;
+	}
+	if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) {
+		base = 16;
+		s += 2;
+	} else
+		base = 10;
+
+	for (s = s; *s != 0; s++) {
+		if ((*s >= '0') && (*s <= '9'))
+			r = (r * base) + (*s - '0');
+		else if ((*s >= 'A') && (*s <= 'F'))
+			r = (r * base) + (*s - 'A' + 10);
+		else if ((*s >= 'a') && (*s <= 'f'))
+			r = (r * base) + (*s - 'a' + 10);
+		else
+			break;
+	}
+
+	return r * pn;
+}
+
+/**
+ *  @brief Create cmd52 string
+ *
+ *  @param priv	A pointer to bt_private structure
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+form_cmd52_string(bt_private *priv)
+{
+	ENTER();
+
+	memset(cmd52_string, 0, CMD52_STR_LEN);
+	snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X",
+		 priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg,
+		 priv->bt_dev.cmd52_val);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/*
+ *  @brief Parse cmd52 string
+ *
+ *  @param buffer  A pointer user buffer
+ *  @param len     Length of user buffer
+ *  @param func    Parsed func number
+ *  @param reg     Parsed reg value
+ *  @param val     Parsed value to set
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+parse_cmd52_string(const char __user * buffer, size_t len,
+		   int *func, int *reg, int *val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	char *string = NULL;
+	char *pos = NULL;
+
+	ENTER();
+
+	string = kzalloc(CMD52_STR_LEN, GFP_KERNEL);
+	if (!string) {
+		PRINTM(ERROR, "BT: Can not alloc mem for cmd52 string\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+	memcpy(string, buffer + strlen("sdcmd52rw="),
+	       len - strlen("sdcmd52rw="));
+	string = strstrip(string);
+
+	*func = -1;
+	*reg = -1;
+	*val = -1;
+
+	/* Get func */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*func = string_to_number(pos);
+
+	/* Get reg */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*reg = string_to_number(pos);
+
+	/* Get val (optional) */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*val = string_to_number(pos);
+	kfree(string);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function handle generic proc file close
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+proc_close(struct inode *inode, struct file *file)
+{
+	struct proc_data *pdata = file->private_data;
+	ENTER();
+	if (pdata) {
+		if (pdata->on_close != NULL)
+			pdata->on_close(inode, file);
+		kfree(pdata->rdbuf);
+		kfree(pdata->wrbuf);
+		kfree(pdata);
+	}
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handle generic proc file read
+ *
+ *  @param file    A pointer to file structure
+ *  @param buffer  A pointer to output buffer
+ *  @param len     number of byte to read
+ *  @param offset  A pointer to offset of file
+ *  @return		number of output data
+ */
+static ssize_t
+proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset)
+{
+	loff_t pos = *offset;
+	struct proc_data *pdata = (struct proc_data *)file->private_data;
+	if ((!pdata->rdbuf) || (pos < 0))
+		return -EINVAL;
+	if (pos >= pdata->rdlen)
+		return 0;
+	if (len > pdata->rdlen - pos)
+		len = pdata->rdlen - pos;
+	if (copy_to_user(buffer, pdata->rdbuf + pos, len))
+		return -EFAULT;
+	*offset = pos + len;
+	return len;
+}
+
+/**
+ *  @brief This function handle generic proc file write
+ *
+ *  @param file    A pointer to file structure
+ *  @param buffer  A pointer to input buffer
+ *  @param len     number of byte to write
+ *  @param offset  A pointer to offset of file
+ *  @return		number of input data
+ */
+static ssize_t
+proc_write(struct file *file,
+	   const char __user * buffer, size_t len, loff_t * offset)
+{
+	loff_t pos = *offset;
+	struct proc_data *pdata = (struct proc_data *)file->private_data;
+	int func = 0, reg = 0, val = 0;
+	int config_data = 0;
+	char *line = NULL;
+
+	if (!pdata->wrbuf || (pos < 0))
+		return -EINVAL;
+	if (pos >= pdata->maxwrlen)
+		return 0;
+	if (len > pdata->maxwrlen - pos)
+		len = pdata->maxwrlen - pos;
+	if (copy_from_user(pdata->wrbuf + pos, buffer, len))
+		return -EFAULT;
+	if (!strncmp(pdata->wrbuf + pos, "fw_reload", strlen("fw_reload"))) {
+		if (!strncmp
+		    (pdata->wrbuf + pos, "fw_reload=", strlen("fw_reload="))) {
+			line = pdata->wrbuf + pos;
+			line += strlen("fw_reload") + 1;
+			config_data = string_to_number(line);
+		} else
+			config_data = FW_RELOAD_SDIO_INBAND_RESET;
+		PRINTM(MSG, "Request fw_reload=%d\n", config_data);
+		bt_request_fw_reload(pdata->pbt, config_data);
+	}
+	if (!strncmp(pdata->wrbuf + pos, "sdcmd52rw=", strlen("sdcmd52rw="))) {
+		parse_cmd52_string(pdata->wrbuf + pos, len, &func, &reg, &val);
+		sd_write_cmd52_val(pdata->pbt, func, reg, val);
+	}
+	if (!strncmp(pdata->wrbuf + pos, "debug_dump", strlen("debug_dump"))) {
+		bt_dump_sdio_regs(pdata->pbt);
+		bt_dump_firmware_info_v2(pdata->pbt);
+	}
+
+	if (pos + len > pdata->wrlen)
+		pdata->wrlen = len + file->f_pos;
+	*offset = pos + len;
+	return len;
+}
+
+/**
+ *  @brief This function handle the generic file close
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return		N/A
+ */
+static void
+proc_on_close(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	struct proc_private_data *priv = PDE_DATA(inode);
+#else
+	struct proc_private_data *priv = PDE(inode)->data;
+#endif
+	struct proc_data *pdata = file->private_data;
+	char *line;
+	int i;
+	ENTER();
+	if (!pdata->wrlen)
+		return;
+	line = pdata->wrbuf;
+	while (line[0]) {
+		for (i = 0; i < priv->num_items; i++) {
+			if (!strncmp
+			    (line, priv->pdata[i].name,
+			     strlen(priv->pdata[i].name))) {
+				line += strlen(priv->pdata[i].name) + 1;
+				if (priv->pdata[i].size == 1)
+					*((u8 *)priv->pdata[i].addr) =
+						(u8)string_to_number(line);
+				else if (priv->pdata[i].size == 2)
+					*((u16 *) priv->pdata[i].addr) =
+						(u16) string_to_number(line);
+				else if (priv->pdata[i].size == 4)
+					*((u32 *)priv->pdata[i].addr) =
+						(u32)string_to_number(line);
+			}
+		}
+		while (line[0] && line[0] != '\n')
+			line++;
+		if (line[0])
+			line++;
+	}
+	if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd
+	    || priv->pbt->bt_dev.sdio_pull_ctrl
+	    || priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) {
+		bt_prepare_command(priv->pbt);
+		wake_up_interruptible(&priv->pbt->MainThread.waitQ);
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function handle the generic file open
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return	BT_STATUS_SUCCESS or other error no.
+ */
+static int
+proc_open(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	struct proc_private_data *priv = PDE_DATA(inode);
+#else
+	struct proc_private_data *priv = PDE(inode)->data;
+#endif
+	struct proc_data *pdata;
+	int i;
+	char *p;
+	u32 val = 0;
+	ENTER();
+	priv->pbt->adapter->skb_pending =
+		skb_queue_len(&priv->pbt->adapter->tx_queue);
+	file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL);
+	if (file->private_data == NULL) {
+		PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+	pdata = (struct proc_data *)file->private_data;
+	pdata->pbt = priv->pbt;
+	pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL);
+	if (pdata->rdbuf == NULL) {
+		PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n");
+		kfree(file->private_data);
+		LEAVE();
+		return -ENOMEM;
+	}
+	if (priv->fileflag == DEFAULT_FILE_PERM) {
+		pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL);
+		if (pdata->wrbuf == NULL) {
+			PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n");
+			kfree(pdata->rdbuf);
+			kfree(file->private_data);
+			return -ENOMEM;
+		}
+		pdata->maxwrlen = priv->bufsize;
+		pdata->on_close = proc_on_close;
+	}
+	p = pdata->rdbuf;
+	for (i = 0; i < priv->num_items; i++) {
+		if (priv->pdata[i].size == 1)
+			val = *((u8 *)priv->pdata[i].addr);
+		else if (priv->pdata[i].size == 2)
+			val = *((u16 *) priv->pdata[i].addr);
+		else if (priv->pdata[i].size == 4)
+			val = *((u32 *)priv->pdata[i].addr);
+		if (priv->pdata[i].flag & SHOW_INT)
+			p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val);
+		else if (priv->pdata[i].flag & SHOW_HEX)
+			p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val);
+		else if (priv->pdata[i].flag & SHOW_STRING) {
+			if (!strncmp
+			    (priv->pdata[i].name, "sdcmd52rw",
+			     strlen("sdcmd52rw"))) {
+				sd_read_cmd52_val(priv->pbt);
+				form_cmd52_string(priv->pbt);
+			}
+			p += sprintf(p, "%s=%s\n", priv->pdata[i].name,
+				     (char *)priv->pdata[i].addr);
+		}
+	}
+	pdata->rdlen = strlen(pdata->rdbuf);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/** Proc read ops */
+static const struct file_operations proc_read_ops = {
+	.read = proc_read,
+	.open = proc_open,
+	.release = proc_close
+};
+
+/** Proc Read-Write ops */
+static const struct file_operations proc_rw_ops = {
+	.read = proc_read,
+	.write = proc_write,
+	.open = proc_open,
+	.release = proc_close
+};
+
+static struct proc_private_data proc_files[] = {
+	{"status", S_IRUGO, 1024,
+	 sizeof(status_items) / sizeof(status_items[0]),
+	 &status_items[0], NULL, &proc_read_ops}
+	,
+	{"config", DEFAULT_FILE_PERM, 512,
+	 sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL,
+	 &proc_rw_ops}
+	,
+	{"debug", DEFAULT_FILE_PERM, 512,
+	 sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL,
+	 &proc_rw_ops}
+	,
+};
+
+/**
+ *  @brief This function initializes proc entry
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param m_dev    A pointer to struct m_dev
+ *  @param seq      Sequence number
+ *
+ *  @return	BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct proc_dir_entry *entry;
+	int i, j;
+	ENTER();
+
+	memset(cmd52_string, 0, CMD52_STR_LEN);
+	if (proc_mbt) {
+		priv->dev_proc[seq].proc_entry =
+			proc_mkdir(m_dev->name, proc_mbt);
+		if (!priv->dev_proc[seq].proc_entry) {
+			PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->dev_proc[seq].pfiles =
+			kmalloc(sizeof(proc_files), GFP_ATOMIC);
+		if (!priv->dev_proc[seq].pfiles) {
+			PRINTM(ERROR,
+			       "BT: Could not alloc memory for pfile!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files,
+		       sizeof(proc_files));
+		priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files);
+		for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++)
+			priv->dev_proc[seq].pfiles[j].pdata = NULL;
+		for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
+			priv->dev_proc[seq].pfiles[j].pdata =
+				kmalloc(priv->dev_proc[seq].pfiles[j].
+					num_items * sizeof(struct item_data),
+					GFP_ATOMIC);
+			if (!priv->dev_proc[seq].pfiles[j].pdata) {
+				PRINTM(ERROR,
+				       "BT: Could not alloc memory for pdata!\n");
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata,
+			       (u8 *)proc_files[j].pdata,
+			       priv->dev_proc[seq].pfiles[j].num_items *
+			       sizeof(struct item_data));
+			for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items;
+			     i++) {
+				if (priv->dev_proc[seq].pfiles[j].
+				    pdata[i].flag & OFFSET_BT_DEV)
+					priv->dev_proc[seq].pfiles[j].pdata[i].
+						addr =
+						priv->dev_proc[seq].pfiles[j].
+						pdata[i].offset +
+						(t_ptr)&priv->bt_dev;
+				if (priv->dev_proc[seq].pfiles[j].
+				    pdata[i].flag & OFFSET_BT_ADAPTER)
+					priv->dev_proc[seq].pfiles[j].pdata[i].
+						addr =
+						priv->dev_proc[seq].pfiles[j].
+						pdata[i].offset +
+						(t_ptr)priv->adapter;
+			}
+			priv->dev_proc[seq].pfiles[j].pbt = priv;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+			entry = proc_create_data(proc_files[j].name,
+						 S_IFREG | proc_files[j].
+						 fileflag,
+						 priv->dev_proc[seq].proc_entry,
+						 proc_files[j].fops,
+						 &priv->dev_proc[seq].
+						 pfiles[j]);
+			if (entry == NULL)
+#else
+			entry = create_proc_entry(proc_files[j].name,
+						  S_IFREG | proc_files[j].
+						  fileflag,
+						  priv->dev_proc[seq].
+						  proc_entry);
+			if (entry) {
+				entry->data = &priv->dev_proc[seq].pfiles[j];
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+				entry->owner = THIS_MODULE;
+#endif
+				entry->proc_fops = proc_files[j].fops;
+			} else
+#endif
+				PRINTM(MSG, "BT: Fail to create proc %s\n",
+				       proc_files[j].name);
+		}
+	}
+done:
+	if (ret == BT_STATUS_FAILURE) {
+		if (priv->dev_proc[seq].proc_entry) {
+			remove_proc_entry(m_dev->name, proc_mbt);
+			priv->dev_proc[seq].proc_entry = NULL;
+		}
+		if (priv->dev_proc[seq].pfiles) {
+			for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
+				if (priv->dev_proc[seq].pfiles[j].pdata) {
+					kfree(priv->dev_proc[seq].pfiles[j].
+					      pdata);
+					priv->dev_proc[seq].pfiles[j].pdata =
+						NULL;
+				}
+			}
+			kfree(priv->dev_proc[seq].pfiles);
+			priv->dev_proc[seq].pfiles = NULL;
+		}
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function removes proc interface
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return	N/A
+ */
+void
+bt_proc_remove(bt_private *priv)
+{
+	int j, i;
+	ENTER();
+	PRINTM(INFO, "BT: Remove Proc Interface\n");
+	if (proc_mbt) {
+		for (i = 0; i < MAX_RADIO_FUNC; i++) {
+			if (!priv->dev_proc[i].proc_entry)
+				continue;
+			for (j = 0; j < ARRAY_SIZE(proc_files); j++) {
+				remove_proc_entry(proc_files[j].name,
+						  priv->dev_proc[i].proc_entry);
+			}
+			remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt);
+			priv->dev_proc[i].proc_entry = NULL;
+
+			if (priv->dev_proc[i].pfiles) {
+				for (j = 0;
+				     j < priv->dev_proc[i].num_proc_files;
+				     j++) {
+					if (priv->dev_proc[i].pfiles[j].pdata) {
+						kfree(priv->dev_proc[i].
+						      pfiles[j].pdata);
+						priv->dev_proc[i].pfiles[j].
+							pdata = NULL;
+					}
+				}
+				kfree(priv->dev_proc[i].pfiles);
+				priv->dev_proc[i].pfiles = NULL;
+			}
+		}
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function creates proc interface
+ *  directory structure
+ *
+ *  @return		BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_root_proc_init(void)
+{
+	PRINTM(INFO, "BT: Create Proc Interface\n");
+	proc_mbt = proc_mkdir("mbt", PROC_DIR);
+	if (!proc_mbt) {
+		PRINTM(ERROR, "BT: Cannot create proc interface\n");
+		return BT_STATUS_FAILURE;
+	}
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function removes proc interface
+ *  directory structure
+ *
+ *  @return		BT_STATUS_SUCCESS
+ */
+int
+bt_root_proc_remove(void)
+{
+	remove_proc_entry("mbt", PROC_DIR);
+	proc_mbt = NULL;
+	return BT_STATUS_SUCCESS;
+}
diff --git a/bt_sd8897/bt/bt_sdio.h b/bt_sd8897/bt/bt_sdio.h
new file mode 100644
index 0000000..8e18b7c
--- /dev/null
+++ b/bt_sd8897/bt/bt_sdio.h
@@ -0,0 +1,429 @@
+/** @file bt_sdio.h
+ *  @brief This file contains SDIO (interface) module
+ *  related macros, enum, and structure.
+ *
+ *  Copyright (C) 2007-2018, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _BT_SDIO_H_
+#define _BT_SDIO_H_
+
+#include <linux/irqreturn.h>
+
+/** SD8787 card type */
+#define CARD_TYPE_SD8787   0x01
+/** SD8777 card type */
+#define CARD_TYPE_SD8777   0x02
+/** SD8887 card type */
+#define CARD_TYPE_SD8887   0x03
+/** SD8897 card type */
+#define CARD_TYPE_SD8897   0x04
+/** SD8797 card type */
+#define CARD_TYPE_SD8797   0x05
+/** SD8977 card type */
+#define CARD_TYPE_SD8977   0x06
+/** SD8997 card type */
+#define CARD_TYPE_SD8997   0x07
+/** SD8987 card type */
+#define CARD_TYPE_SD8987   0x08
+
+/** IRQ return type */
+typedef irqreturn_t IRQ_RET_TYPE;
+/** IRQ return */
+#define IRQ_RET		(return IRQ_HANDLED)
+/** ISR notifier function */
+typedef IRQ_RET_TYPE (*isr_notifier_fn_t) (s32 irq, void *dev_id,
+					   struct pt_regs * reg);
+
+/** SDIO header length */
+#define SDIO_HEADER_LEN			4
+
+/** Interrupt Mode SDIO */
+#define INT_MODE_SDIO       0
+/** Interrupt Mode GPIO */
+#define INT_MODE_GPIO       1
+/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */
+#define GPIO_INT_NEW_MODE   255
+/* SD block size can not bigger than 64 due to buf size limit in firmware */
+/** define SD block size for data Tx/Rx */
+#define SD_BLOCK_SIZE			64
+/** define SD block size for firmware download */
+#define SD_BLOCK_SIZE_FW_DL		256
+
+/** Number of blocks for firmware transfer */
+#define FIRMWARE_TRANSFER_NBLOCK	2
+
+/** Firmware ready */
+#define FIRMWARE_READY			0xfedc
+
+/* Bus Interface Control Reg 0x07 */
+/** SD BUS width 1 */
+#define SD_BUS_WIDTH_1			0x00
+/** SD BUS width 4 */
+#define SD_BUS_WIDTH_4			0x02
+/** SD BUS width mask */
+#define SD_BUS_WIDTH_MASK		0x03
+/** Asynchronous interrupt mode */
+#define ASYNC_INT_MODE			0x20
+
+/** magic register */
+#define CARD_MAGIC_REG          0xF0
+/** magic value */
+#define MAGIC_VAL               0x24
+
+/* Host Control Registers */
+/** Host Control Registers : Configuration */
+#define CONFIGURATION_REG		0x00
+/** Host Control Registers : Host without Command 53 finish host*/
+#define HOST_TO_CARD_EVENT		(0x1U << 3)
+/** Host Control Registers : Host terminates Command 53 */
+#define HOST_TERM_CMD53			(0x1U << 2)
+/** Host Control Registers : Host power up */
+#define HOST_POWER_UP			(0x1U << 1)
+/** Host Control Registers : Host power down */
+#define HOST_POWER_DOWN			(0x1U << 0)
+
+/** Host Control Registers : Host interrupt RSR */
+#define HOST_INT_RSR_REG		0x01
+
+/** Host Control Registers : Upload host interrupt RSR */
+#define UP_LD_HOST_INT_RSR		(0x1U)
+
+/** Host Control Registers : Host interrupt mask */
+#define HOST_INT_MASK_REG		0x02
+
+/** Host Control Registers : Upload host interrupt mask */
+#define UP_LD_HOST_INT_MASK		(0x1U)
+/** Host Control Registers : Download host interrupt mask */
+#define DN_LD_HOST_INT_MASK		(0x2U)
+/** Enable Host interrupt mask */
+#define HIM_ENABLE			(UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK)
+/** Disable Host interrupt mask */
+#define	HIM_DISABLE			0xff
+
+#define HOST_INTSTATUS_REG		0x03
+/** Host Control Registers : Upload host interrupt status */
+#define UP_LD_HOST_INT_STATUS		(0x1U)
+/** Host Control Registers : Download host interrupt status */
+#define DN_LD_HOST_INT_STATUS		(0x2U)
+
+/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/
+#define ENABLE_GPIO_1_INT_MODE  0x88
+/** Scratch reg 3 2  :     Configure GPIO-1 INT*/
+#define SCRATCH_REG_32          0xEE
+
+/** Host Control Registers : Host interrupt status */
+#define HOST_INT_STATUS_REG		0x28
+/** Host Control Registers : Upload CRC error */
+#define UP_LD_CRC_ERR			(0x1U << 2)
+/** Host Control Registers : Upload restart */
+#define UP_LD_RESTART			(0x1U << 1)
+/** Host Control Registers : Download restart */
+#define DN_LD_RESTART			(0x1U << 0)
+
+/* Card Control Registers */
+/** Card Control Registers : Read SQ base address A0 register */
+#define SQ_READ_BASE_ADDRESS_A0_REG		0x40
+/** Card Control Registers : Read SQ base address A1 register */
+#define SQ_READ_BASE_ADDRESS_A1_REG		0x41
+/** Card Control Registers : Read SQ base address A2 register */
+#define SQ_READ_BASE_ADDRESS_A2_REG		0x42
+/** Card Control Registers : Read SQ base address A3 register */
+#define SQ_READ_BASE_ADDRESS_A3_REG		0x43
+/** Card Control Registers : Read SQ base address B0 register */
+#define SQ_READ_BASE_ADDRESS_B0_REG		0x44
+/** Card Control Registers : Read SQ base address B1 register */
+#define SQ_READ_BASE_ADDRESS_B1_REG		0x45
+/** Card Control Registers : Read SQ base address B2 register */
+#define SQ_READ_BASE_ADDRESS_B2_REG		0x46
+/** Card Control Registers : Read SQ base address B3 register */
+#define SQ_READ_BASE_ADDRESS_B3_REG		0x47
+
+/** Card Control Registers : Card status register */
+#define CARD_STATUS_REG					0x30
+/** Card Control Registers : Card I/O ready */
+#define CARD_IO_READY					(0x1U << 3)
+/** Card Control Registers : CIS card ready */
+#define CIS_CARD_RDY					(0x1U << 2)
+/** Card Control Registers : Upload card ready */
+#define UP_LD_CARD_RDY					(0x1U << 1)
+/** Card Control Registers : Download card ready */
+#define DN_LD_CARD_RDY					(0x1U << 0)
+
+/** Card Control Registers : Host interrupt mask register */
+#define HOST_INTERRUPT_MASK_REG			0x34
+/** Card Control Registers : Host power interrupt mask */
+#define HOST_POWER_INT_MASK				(0x1U << 3)
+/** Card Control Registers : Abort card interrupt mask */
+#define ABORT_CARD_INT_MASK				(0x1U << 2)
+/** Card Control Registers : Upload card interrupt mask */
+#define UP_LD_CARD_INT_MASK				(0x1U << 1)
+/** Card Control Registers : Download card interrupt mask */
+#define DN_LD_CARD_INT_MASK				(0x1U << 0)
+
+/** Card Control Registers : Card interrupt status register */
+#define CARD_INTERRUPT_STATUS_REG		0x38
+/** Card Control Registers : Power up interrupt */
+#define POWER_UP_INT					(0x1U << 4)
+/** Card Control Registers : Power down interrupt */
+#define POWER_DOWN_INT					(0x1U << 3)
+
+/** Card Control Registers : Card interrupt RSR register */
+#define CARD_INTERRUPT_RSR_REG			0x3c
+/** Card Control Registers : Power up RSR */
+#define POWER_UP_RSR					(0x1U << 4)
+/** Card Control Registers : Power down RSR */
+#define POWER_DOWN_RSR					(0x1U << 3)
+
+/** Card Control Registers : Debug 0 register */
+#define DEBUG_0_REG						0x70
+/** Card Control Registers : SD test BUS 0 */
+#define SD_TESTBUS0						(0x1U)
+/** Card Control Registers : Debug 1 register */
+#define DEBUG_1_REG						0x71
+/** Card Control Registers : SD test BUS 1 */
+#define SD_TESTBUS1						(0x1U)
+/** Card Control Registers : Debug 2 register */
+#define DEBUG_2_REG						0x72
+/** Card Control Registers : SD test BUS 2 */
+#define SD_TESTBUS2						(0x1U)
+/** Card Control Registers : Debug 3 register */
+#define DEBUG_3_REG						0x73
+/** Card Control Registers : SD test BUS 3 */
+#define SD_TESTBUS3						(0x1U)
+
+/** Host Control Registers : I/O port 0 */
+#define IO_PORT_0_REG			0x78
+/** Host Control Registers : I/O port 1 */
+#define IO_PORT_1_REG			0x79
+/** Host Control Registers : I/O port 2 */
+#define IO_PORT_2_REG			0x7A
+
+/** Firmware status 0 register */
+#define CARD_FW_STATUS0_REG		0x60
+/** Firmware status 1 register */
+#define CARD_FW_STATUS1_REG		0x61
+/** Rx length register */
+#define CARD_RX_LEN_REG			0x62
+/** Rx unit register */
+#define CARD_RX_UNIT_REG		0x63
+/** Card Control Registers : Miscellaneous Configuration Register */
+#define CARD_MISC_CFG_REG		0x6C
+/** Misc. Config Register : Auto Re-enable interrupts */
+#define AUTO_RE_ENABLE_INT		(0x1U << 4)
+
+/** Card Control Registers : Card OCR 0 register */
+#define CARD_OCR_0_REG			0x68
+/** Card Control Registers : Card OCR 1 register */
+#define CARD_OCR_1_REG			0x69
+/** Card Control Registers : Card OCR 3 register */
+#define CARD_OCR_3_REG			0x6A
+/** Card Control Registers : Card config register */
+#define CARD_CONFIG_REG			0x6B
+/** Card Control Registers : Card revision register */
+#define CARD_REVISION_REG		0x5c
+/** Card Control Registers : Command 53 finish G BUS */
+#define CMD53_FINISH_GBUS		(0x1U << 1)
+/** Card Control Registers : SD negative edge */
+#define SD_NEG_EDGE				(0x1U << 0)
+
+/* Special registers in function 0 of the SDxx card */
+/** Special register in function 0 of the SDxxx card : Scratch 0 */
+#define	SCRATCH_0_REG			0x80fe
+/** Special register in function 0 of the SDxxx card : Scratch 1 */
+#define	SCRATCH_1_REG			0x80ff
+/** Host F1 read base 0 */
+#define HOST_F1_RD_BASE_0		0x0040
+/** Host F1 read base 1 */
+#define HOST_F1_RD_BASE_1		0x0041
+/** Host F1 card ready */
+#define HOST_F1_CARD_RDY		0x0020
+
+/** Chip Id Register 0 */
+#define CARD_CHIP_ID_0_REG		0x801c
+/** Chip Id Register 1 */
+#define CARD_CHIP_ID_1_REG		0x801d
+
+struct sdio_mmc_card {
+	/** sdio_func structure pointer */
+	struct sdio_func *func;
+	/** bt_private structure pointer */
+	bt_private *priv;
+};
+
+struct sdio_card_reg {
+	u8 cfg;
+	u8 host_int_mask;	// HOST_INT_MASK_REG
+	u8 host_intstatus;	// HOST_INTSTATUS_REG
+	u8 host_int_rsr_reg;	// HOST_INT_RSR_REG
+	u8 card_misc_cfg_reg;	// CARD_MISC_CFG_REG
+	u8 card_status;		// CARD_STATUS_REG
+	u8 sq_read_base_addr_a0;	// SQ_READ_BASE_ADDRESS_A0_REG
+	u8 sq_read_base_addr_a1;	// SQ_READ_BASE_ADDRESS_A1_REG
+	u8 card_revision;	// CARD_REVISION_REG
+	u8 card_fw_status0;	// CARD_FW_STATUS0_REG
+	u8 card_fw_status1;	// CARD_FW_STATUS1_REG
+	u8 card_rx_len;		// CARD_RX_LEN_REG
+	u8 card_rx_unit;	// CARD_RX_UNIT_REG
+	u8 io_port_0;		// IO_PORT_0_REG
+	u8 io_port_1;		// IO_PORT_1_REG
+	u8 io_port_2;		// IO_PORT_2_REG
+};
+
+struct sdio_device {
+	const struct sdio_card_reg *reg;
+};
+
+static const struct sdio_card_reg bt_reg_87xx = {
+	.cfg = 0x00,
+	.host_int_mask = 0x02,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x03,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x01,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0x6c,	// CARD_MISC_CFG_REG
+	.card_status = 0x30,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0x40,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0x41,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0x5C,	// CARD_REVISION_REG
+	.card_fw_status0 = 0x60,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0x61,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0x62,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0x63,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0x78,	// IO_PORT_0_REG
+	.io_port_1 = 0x79,	// IO_PORT_1_REG
+	.io_port_2 = 0x7a,	// IO_PORT_2_REG
+};
+
+static const struct sdio_card_reg bt_reg_8887 = {
+	.cfg = 0x00,
+	.host_int_mask = 0x08,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x0C,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x04,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0xD8,	// CARD_MISC_CFG_REG
+	.card_status = 0x5C,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0x6C,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0x6D,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0xC8,	// CARD_REVISION_REG
+	.card_fw_status0 = 0x88,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0x89,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0x8A,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0x8B,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0xE4,	// IO_PORT_0_REG
+	.io_port_1 = 0xE5,	// IO_PORT_1_REG
+	.io_port_2 = 0xE6,	// IO_PORT_2_REG
+};
+
+static const struct sdio_card_reg bt_reg_8897 = {
+	.cfg = 0x00,
+	.host_int_mask = 0x02,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x03,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x01,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0xCC,	// CARD_MISC_CFG_REG
+	.card_status = 0x50,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0x60,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0x61,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0xBC,	// CARD_REVISION_REG
+	.card_fw_status0 = 0xC0,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0xC1,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0xC2,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0xC3,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0xD8,	// IO_PORT_0_REG
+	.io_port_1 = 0xD9,	// IO_PORT_1_REG
+	.io_port_2 = 0xDA,	// IO_PORT_2_REG
+};
+
+static const struct sdio_card_reg bt_reg_8977_8997 = {
+	.cfg = 0x00,
+	.host_int_mask = 0x08,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x0C,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x04,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0xD8,	// CARD_MISC_CFG_REG
+	.card_status = 0x5C,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0xF8,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0xF9,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0xC8,	// CARD_REVISION_REG
+	.card_fw_status0 = 0xE8,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0xE9,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0xEA,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0xEB,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0xE4,	// IO_PORT_0_REG
+	.io_port_1 = 0xE5,	// IO_PORT_1_REG
+	.io_port_2 = 0xE6,	// IO_PORT_2_REG
+};
+
+static const struct sdio_device bt_sdio_sd8787 = {
+	.reg = &bt_reg_87xx,
+};
+
+static const struct sdio_device bt_sdio_sd8777 = {
+	.reg = &bt_reg_87xx,
+};
+
+static const struct sdio_device bt_sdio_sd8887 = {
+	.reg = &bt_reg_8887,
+};
+
+static const struct sdio_device bt_sdio_sd8897 = {
+	.reg = &bt_reg_8897,
+};
+
+static const struct sdio_device bt_sdio_sd8797 = {
+	.reg = &bt_reg_87xx,
+};
+
+static const struct sdio_device bt_sdio_sd8977 = {
+	.reg = &bt_reg_8977_8997,
+};
+
+static const struct sdio_device bt_sdio_sd8997 = {
+	.reg = &bt_reg_8977_8997,
+};
+
+static const struct sdio_device bt_sdio_sd8987 = {
+	.reg = &bt_reg_8977_8997,
+};
+
+/** DMA alignment value */
+#define DMA_ALIGNMENT	64
+/** 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)	\
+	((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1))
+
+/** This function read cmd52 register */
+int sd_write_reg(bt_private *priv, int reg, u8 val);
+/** This function write cmd52 value to register */
+int sd_read_reg(bt_private *priv, int reg, u8 *data);
+/** This function reads the Cmd52 value in dev structure */
+int sd_read_cmd52_val(bt_private *priv);
+/** This function updates card reg based on the Cmd52 value in dev structure */
+int sd_write_cmd52_val(bt_private *priv, int func, int reg, int val);
+
+void sdio_update_card_type(bt_private *priv, void *card);
+int sdio_get_sdio_device(bt_private *priv);
+
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+/** This function tells lower driver that BT is suspended */
+void bt_is_suspended(bt_private *priv);
+#endif
+#endif
+#endif
+#endif /* _BT_SDIO_H_ */
diff --git a/bt_sd8897/bt/bt_sdiommc.c b/bt_sd8897/bt/bt_sdiommc.c
new file mode 100644
index 0000000..83a76fc
--- /dev/null
+++ b/bt_sd8897/bt/bt_sdiommc.c
@@ -0,0 +1,2355 @@
+/** @file bt_sdiommc.c
+ *  @brief This file contains SDIO IF (interface) module
+ *  related functions.
+ *
+ * Copyright (C) 2007-2018, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/firmware.h>
+#include <linux/mmc/sdio_func.h>
+#include        <linux/mmc/sdio.h>
+#include        <linux/mmc/card.h>
+
+#include "bt_drv.h"
+#include "bt_sdio.h"
+
+/** define marvell vendor id */
+#define MARVELL_VENDOR_ID 0x02df
+
+/** Max retry number of CMD53 read/write */
+#define MAX_CMD53_RETRY 	3
+/** Max retry number of CMD53 read/write */
+#define MAX_CMD52_RETRY     3
+/** Firmware name */
+static char *fw_name;
+/** fw serial download flag */
+extern int bt_fw_serial;
+int bt_intmode = INT_MODE_SDIO;
+/** request firmware nowait */
+int bt_req_fw_nowait;
+static int multi_fn = BIT(2);
+
+#define DEFAULT_FW_NAME ""
+
+/** FW header length for CRC check disable */
+#define FW_CRC_HEADER_RB2   28
+/** FW header for CRC check disable */
+static u8 fw_crc_header_rb_2[FW_CRC_HEADER_RB2] = {
+	0x05, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+	0x9d, 0x32, 0xbb, 0x11, 0x01, 0x00, 0x00, 0x7f,
+	0x00, 0x00, 0x00, 0x00, 0x67, 0xd6, 0xfc, 0x25
+};
+
+/** FW header length for CRC check disable */
+#define FW_CRC_HEADER_RB   24
+/** FW header for CRC check disable */
+static u8 fw_crc_header_rb_1[FW_CRC_HEADER_RB] = {
+	0x01, 0x00, 0x00, 0x00, 0x04, 0xfd, 0x00, 0x04,
+	0x08, 0x00, 0x00, 0x00, 0x26, 0x52, 0x2a, 0x7b,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/** Default firmware name */
+#define DEFAULT_FW_NAME_8777 "mrvl/sd8777_uapsta.bin"
+#define DEFAULT_FW_NAME_8787 "mrvl/sd8787_uapsta.bin"
+#define DEFAULT_FW_NAME_8797 "mrvl/sd8797_uapsta.bin"
+#define DEFAULT_FW_NAME_8887 "mrvl/sd8887_uapsta.bin"
+#define DEFAULT_FW_NAME_8897 "mrvl/sd8897_uapsta.bin"
+#define DEFAULT_FW_NAME_8977 "mrvl/sdsd8977_combo.bin"
+#define DEFAULT_FW_NAME_8997 "mrvl/sdsd8997_combo.bin"
+
+/** SD8787 chip revision ID */
+#define SD8787_W0      0x30
+#define SD8787_W1      0x31
+#define SD8787_A0_A1   0x40
+/** SD8797 chip revision ID */
+#define SD8797_A0       0x00
+#define SD8797_B0       0x10
+/** SD8897 chip revision ID */
+#define SD8897_A0       0x10
+#define SD8897_B0       0x20
+
+/** SD8887 chip revision ID */
+#define SD8887_A0       0x0
+#define SD8887_A2       0x2
+#define SD8887_A0_FW_NAME "mrvl/sd8887_uapsta.bin"
+#define SD8887_A2_FW_NAME "mrvl/sd8887_uapsta_a2.bin"
+#define SD8887_A2_BT_FW_NAME "mrvl/sd8887_bt_a2.bin"
+
+#define SD8897_A0_FW_NAME "mrvl/sd8897_uapsta_a0.bin"
+#define SD8897_B0_FW_NAME "mrvl/sd8897_uapsta.bin"
+
+#define SD8787_W1_FW_NAME "mrvl/sd8787_uapsta_w1.bin"
+#define SD8787_AX_FW_NAME "mrvl/sd8787_uapsta.bin"
+#define SD8797_A0_FW_NAME "mrvl/sd8797_uapsta_a0.bin"
+#define SD8797_B0_FW_NAME "mrvl/sd8797_uapsta.bin"
+
+/** SD8977 chip revision ID */
+#define SD8977_V0       0x0
+#define SD8977_V1       0x8
+#define SD8977_V2       0x9
+#define SD8977_V0_FW_NAME "mrvl/sdsd8977_combo.bin"
+#define SD8977_V0_BT_FW_NAME "mrvl/sd8977_bt.bin"
+#define SD8977_V1_FW_NAME "mrvl/sdsd8977_combo_v1.bin"
+#define SD8977_V1_BT_FW_NAME "mrvl/sd8977_bt_v1.bin"
+#define SD8977_V2_FW_NAME "mrvl/sdsd8977_combo_v2.bin"
+#define SD8977_V2_BT_FW_NAME "mrvl/sd8977_bt_v2.bin"
+
+/** SD8997 chip revision ID */
+#define SD8997_Z        0x02
+#define SD8997_V2       0x10
+#define SD8997_Z_FW_NAME "mrvl/sdsd8997_combo.bin"
+#define SD8997_Z_BT_FW_NAME "mrvl/sd8997_bt.bin"
+#define SD8997_V2_FW_NAME "mrvl/sdsd8997_combo_v2.bin"
+#define SD8997_V2_BT_FW_NAME "mrvl/sd8997_bt_v2.bin"
+#define SD8997_V3_FW_NAME "mrvl/sdsd8997_combo_v3.bin"
+#define SD8997_V3_BT_FW_NAME "mrvl/sd8997_bt_v3.bin"
+
+/** SD8987 */
+#define SD8987_FW_NAME  "mrvl/sdsd8987_combo.bin"
+#define SD8987_BT_FW_NAME "mrvl/sd8987_bt.bin"
+
+/** Function number 2 */
+#define FN2			2
+/** Device ID for SD8787 FN2 */
+#define SD_DEVICE_ID_8787_BT_FN2    0x911A
+/** Device ID for SD8787 FN3 */
+#define SD_DEVICE_ID_8787_BT_FN3    0x911B
+/** Device ID for SD8777 FN2 */
+#define SD_DEVICE_ID_8777_BT_FN2    0x9132
+/** Device ID for SD8777 FN3 */
+#define SD_DEVICE_ID_8777_BT_FN3    0x9133
+/** Device ID for SD8887 FN2 */
+#define SD_DEVICE_ID_8887_BT_FN2    0x9136
+/** Device ID for SD8887 FN3 */
+#define SD_DEVICE_ID_8887_BT_FN3    0x9137
+/** Device ID for SD8897 FN2 */
+#define SD_DEVICE_ID_8897_BT_FN2    0x912E
+/** Device ID for SD8897 FN3 */
+#define SD_DEVICE_ID_8897_BT_FN3    0x912F
+/** Device ID for SD8797 FN2 */
+#define SD_DEVICE_ID_8797_BT_FN2    0x912A
+/** Device ID for SD8797 FN3 */
+#define SD_DEVICE_ID_8797_BT_FN3    0x912B
+/** Device ID for SD8977 FN2 */
+#define SD_DEVICE_ID_8977_BT_FN2    0x9146
+/** Device ID for SD8997 FN2 */
+#define SD_DEVICE_ID_8997_BT_FN2    0x9142
+/** Device ID for SD8987 FN2 */
+#define SD_DEVICE_ID_8987_BT_FN2    0x914a
+/** Device ID for SD8987 FN3 */
+#define SD_DEVICE_ID_8987_BT_FN3    0x914b
+
+/** Array of SDIO device ids when multi_fn=0x12 */
+static const struct sdio_device_id bt_ids[] = {
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8787_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8777_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8887_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8897_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8797_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8977_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8987_BT_FN2)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(sdio, bt_ids);
+
+/********************************************************
+		Global Variables
+********************************************************/
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+extern int mbt_pm_keep_power;
+#endif
+
+extern bt_private *m_priv[];
+/********************************************************
+		Local Functions
+********************************************************/
+
+/**
+ *  @brief This function gets rx_unit value
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_get_rx_unit(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_rx_unit_reg = priv->psdio_device->reg->card_rx_unit;
+
+	ENTER();
+
+	reg = sdio_readb(card->func, card_rx_unit_reg, &ret);
+	if (ret == BT_STATUS_SUCCESS)
+		priv->bt_dev.rx_unit = reg;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads fwstatus registers
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param dat	   A pointer to keep returned data
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_read_firmware_status(bt_private *priv, u16 * dat)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 fws0;
+	u8 fws1;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0;
+	u8 card_fw_status1_reg = priv->psdio_device->reg->card_fw_status1;
+
+	ENTER();
+
+	fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret);
+	if (ret < 0) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret);
+	if (ret < 0) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	*dat = (((u16) fws1) << 8) | fws0;
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function reads rx length
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param dat	   A pointer to keep returned data
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+static int
+sd_read_rx_len(bt_private *priv, u16 * dat)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_rx_len_reg = priv->psdio_device->reg->card_rx_len;
+
+	ENTER();
+
+	reg = sdio_readb(card->func, card_rx_len_reg, &ret);
+	if (ret == BT_STATUS_SUCCESS)
+		*dat = (u16) reg << priv->bt_dev.rx_unit;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables the host interrupts mask
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mask	   the interrupt mask
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_enable_host_int_mask(bt_private *priv, u8 mask)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask;
+
+	ENTER();
+
+	sdio_writeb(card->func, mask, host_int_mask_reg, &ret);
+	if (ret) {
+		PRINTM(WARN, "BT: Unable to enable the host interrupt!\n");
+		ret = BT_STATUS_FAILURE;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function disables the host interrupts mask.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mask	   the interrupt mask
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+static int
+sbi_disable_host_int_mask(bt_private *priv, u8 mask)
+{
+	int ret = BT_STATUS_FAILURE;
+	u8 host_int_mask;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask;
+
+	ENTER();
+
+	/* Read back the host_int_mask register */
+	host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret);
+	if (ret)
+		goto done;
+
+	/* Update with the mask and write back to the register */
+	host_int_mask &= ~mask;
+	sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret);
+	if (ret < 0) {
+		PRINTM(WARN, "BT: Unable to diable the host interrupt!\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function polls the card status register
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param bits     the bit mask
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_poll_card_status(bt_private *priv, u8 bits)
+{
+	int tries;
+	int rval;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 cs;
+	u8 card_status_reg = priv->psdio_device->reg->card_status;
+
+	ENTER();
+
+	for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) {
+		cs = sdio_readb(card->func, card_status_reg, &rval);
+		if (rval != 0)
+			break;
+		if (rval == 0 && (cs & bits) == bits) {
+			LEAVE();
+			return BT_STATUS_SUCCESS;
+		}
+		udelay(1);
+	}
+	PRINTM(ERROR,
+	       "BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n",
+	       rval, tries, cs);
+
+	LEAVE();
+	return BT_STATUS_FAILURE;
+}
+
+/**
+ *  @brief This function reads updates the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_read_cmd52_val(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 func, reg, val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	ENTER();
+
+	func = priv->bt_dev.cmd52_func;
+	reg = priv->bt_dev.cmd52_reg;
+	sdio_claim_host(card->func);
+	if (func)
+		val = sdio_readb(card->func, reg, &ret);
+	else
+		val = sdio_f0_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	if (ret) {
+		PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n",
+		       func, reg);
+	} else {
+		priv->bt_dev.cmd52_val = val;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function updates card reg based on the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param func     Stores func variable
+ *  @param reg      Stores reg variable
+ *  @param val      Stores val variable
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_write_cmd52_val(bt_private *priv, int func, int reg, int val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	ENTER();
+
+	if (val >= 0) {
+		/* Perform actual write only if val is provided */
+		sdio_claim_host(card->func);
+		if (func)
+			sdio_writeb(card->func, val, reg, &ret);
+		else
+			sdio_f0_writeb(card->func, val, reg, &ret);
+		sdio_release_host(card->func);
+		if (ret) {
+			PRINTM(ERROR,
+			       "BT: Cannot write value (0x%x) to func %d reg %d\n",
+			       val, func, reg);
+			goto done;
+		}
+		priv->bt_dev.cmd52_val = val;
+	}
+
+	/* Save current func and reg for future read */
+	priv->bt_dev.cmd52_func = func;
+	priv->bt_dev.cmd52_reg = reg;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function updates card reg based on the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to write
+ *  @param val      value
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_write_reg(bt_private *priv, int reg, u8 val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	sdio_writeb(card->func, val, reg, &ret);
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads updates the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to read
+ *  @param data		Data
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_read_reg(bt_private *priv, int reg, u8 *data)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	val = sdio_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	*data = val;
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function probes the card
+ *
+ *  @param func    A pointer to sdio_func structure.
+ *  @param id      A pointer to structure sdio_device_id
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+static int
+sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id)
+{
+	int ret = BT_STATUS_SUCCESS;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *card = NULL;
+
+	ENTER();
+
+	PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor,
+	       id->device, id->class, func->num);
+	card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL);
+	if (!card) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	card->func = func;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+	/* wait for chip fully wake up */
+	if (!func->enable_timeout)
+		func->enable_timeout = 200;
+#endif
+	sdio_claim_host(func);
+	ret = sdio_enable_func(func);
+	if (ret) {
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret);
+		kfree(card);
+		LEAVE();
+		return -EIO;
+	}
+	sdio_release_host(func);
+	priv = bt_add_card(card);
+	if (!priv) {
+		sdio_claim_host(func);
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		ret = BT_STATUS_FAILURE;
+		kfree(card);
+	}
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function checks if the firmware is ready to accept
+ *  command or not.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param pollnum  Number of times to poll fw status
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_verify_fw_download(bt_private *priv, int pollnum)
+{
+	int ret = BT_STATUS_FAILURE;
+	u16 firmwarestat = 0;
+	int tries;
+
+	ENTER();
+
+	/* Wait for firmware initialization event */
+	for (tries = 0; tries < pollnum; tries++) {
+		if (sd_read_firmware_status(priv, &firmwarestat) < 0)
+			continue;
+		if (firmwarestat == FIRMWARE_READY) {
+			PRINTM(MSG, "BT FW is active(%d)\n", tries);
+			ret = BT_STATUS_SUCCESS;
+			break;
+		}
+		mdelay(100);
+	}
+	if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS)) {
+		PRINTM(ERROR,
+		       "Fail to poll firmware status: firmwarestat=0x%x\n",
+		       firmwarestat);
+		bt_dump_sdio_regs(priv);
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Transfers firmware to card
+ *
+ *  @param priv      A Pointer to bt_private structure
+ *  @param fw        A Pointer to fw image
+ *  @param fw_len    fw image len
+ *  @return          BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+static int
+sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len)
+{
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 *firmware = fw;
+	int firmwarelen = fw_len;
+	u8 base0;
+	u8 base1;
+	int ret = BT_STATUS_SUCCESS;
+	int offset;
+	void *tmpfwbuf = NULL;
+	int tmpfwbufsz;
+	u8 *fwbuf;
+	u16 len;
+	int txlen = 0;
+	int tx_blocks = 0;
+	int i = 0;
+	int tries = 0;
+#ifdef FW_DOWNLOAD_SPEED
+	u32 tv1, tv2;
+#endif
+	u8 sq_read_base_address_a0_reg =
+		priv->psdio_device->reg->sq_read_base_addr_a0;
+	u8 sq_read_base_address_a1_reg =
+		priv->psdio_device->reg->sq_read_base_addr_a1;
+	u8 crc_buffer = 0;
+	u8 *header_crc_fw = NULL;
+	u8 header_crc_fw_len = 0;
+
+	if (priv->card_type == CARD_TYPE_SD8787) {
+		header_crc_fw = fw_crc_header_rb_1;
+		header_crc_fw_len = FW_CRC_HEADER_RB;
+	} else if (priv->card_type == CARD_TYPE_SD8777) {
+		header_crc_fw = fw_crc_header_rb_2;
+		header_crc_fw_len = FW_CRC_HEADER_RB2;
+	}
+
+	ENTER();
+
+	PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen);
+
+#ifdef FW_DOWNLOAD_SPEED
+	tv1 = get_utimeofday();
+#endif
+
+	tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT;
+	tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL);
+	if (!tmpfwbuf) {
+		PRINTM(ERROR,
+		       "BT: Unable to allocate buffer for firmware. Terminating download\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	/* Ensure aligned firmware buffer */
+	fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT);
+
+	if (!(priv->fw_crc_check)
+	    && ((priv->card_type == CARD_TYPE_SD8787) ||
+		(priv->card_type == CARD_TYPE_SD8777))
+		) {
+		/* CRC check not required, use custom header first */
+		firmware = header_crc_fw;
+		firmwarelen = header_crc_fw_len;
+		crc_buffer = 1;
+	}
+
+	/* Perform firmware data transfer */
+	offset = 0;
+	do {
+		/* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits
+		 */
+		ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY);
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: FW download with helper poll status timeout @ %d\n",
+			       offset);
+			goto done;
+		}
+		if (!crc_buffer)
+			/* More data? */
+			if (offset >= firmwarelen)
+				break;
+
+		for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+			base0 = sdio_readb(card->func,
+					   sq_read_base_address_a0_reg, &ret);
+			if (ret) {
+				PRINTM(WARN, "Dev BASE0 register read failed:"
+				       " base0=0x%04X(%d). Terminating download\n",
+				       base0, base0);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			base1 = sdio_readb(card->func,
+					   sq_read_base_address_a1_reg, &ret);
+			if (ret) {
+				PRINTM(WARN, "Dev BASE1 register read failed:"
+				       " base1=0x%04X(%d). Terminating download\n",
+				       base1, base1);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			len = (((u16) base1) << 8) | base0;
+
+			if (len != 0)
+				break;
+			udelay(10);
+		}
+
+		if (len == 0)
+			break;
+		else if (len > BT_UPLD_SIZE) {
+			PRINTM(FATAL,
+			       "BT: FW download failure @ %d, invalid length %d\n",
+			       offset, len);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	/** ignore CRC check before download the first packet */
+		if (offset == 0 && (len & BIT(0)))
+			len &= ~BIT(0);
+		txlen = len;
+
+		if (len & BIT(0)) {
+			i++;
+			if (i >= MAX_CMD53_RETRY) {
+				PRINTM(FATAL,
+				       "BT: FW download failure @ %d, over max retry count\n",
+				       offset);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			PRINTM(ERROR,
+			       "BT: FW CRC error indicated by the helper:"
+			       " len = 0x%04X, txlen = %d\n", len, txlen);
+			len &= ~BIT(0);
+
+			PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset);
+			/* Setting this to 0 to resend from same offset */
+			txlen = 0;
+		} else {
+			i = 0;
+
+			/* Set blocksize to transfer - checking for last block */
+			if (firmwarelen - offset < txlen)
+				txlen = firmwarelen - offset;
+
+			PRINTM(INFO, ".");
+
+			tx_blocks =
+				(txlen + SD_BLOCK_SIZE_FW_DL -
+				 1) / SD_BLOCK_SIZE_FW_DL;
+
+			/* Copy payload to buffer */
+			memcpy(fwbuf, &firmware[offset], txlen);
+		}
+
+		/* Send data */
+		ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf,
+				   tx_blocks * SD_BLOCK_SIZE_FW_DL);
+
+		if (ret < 0) {
+			PRINTM(ERROR,
+			       "BT: FW download, write iomem (%d) failed @ %d\n",
+			       i, offset);
+			sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret);
+			if (ret)
+				PRINTM(ERROR, "write ioreg failed (CFG)\n");
+		}
+
+		offset += txlen;
+		if (crc_buffer
+		    && ((priv->card_type == CARD_TYPE_SD8787) ||
+			(priv->card_type == CARD_TYPE_SD8777))
+			) {
+			if (offset >= header_crc_fw_len) {
+				/* Custom header download complete, restore
+				   original FW */
+				offset = 0;
+				firmware = fw;
+				firmwarelen = fw_len;
+				crc_buffer = 0;
+			}
+		}
+	} while (TRUE);
+
+	PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset);
+
+	ret = BT_STATUS_SUCCESS;
+done:
+#ifdef FW_DOWNLOAD_SPEED
+	tv2 = get_utimeofday();
+	PRINTM(INFO, "FW: %d.%03d.%03d ", tv1 / 1000000,
+	       (tv1 % 1000000) / 1000, tv1 % 1000);
+	PRINTM(INFO, " -> %d.%03d.%03d ", tv2 / 1000000,
+	       (tv2 % 1000000) / 1000, tv2 % 1000);
+	tv2 -= tv1;
+	PRINTM(INFO, " == %d.%03d.%03d\n", tv2 / 1000000,
+	       (tv2 % 1000000) / 1000, tv2 % 1000);
+#endif
+	kfree(tmpfwbuf);
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief request_firmware callback
+ *
+ * @param fw_firmware  A pointer to firmware structure
+ * @param context      A Pointer to bt_private structure
+ * @return             BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_request_fw_dpc(const struct firmware *fw_firmware, void *context)
+{
+	int ret = BT_STATUS_SUCCESS;
+	bt_private *priv = (bt_private *)context;
+	struct sdio_mmc_card *card = NULL;
+	struct m_dev *m_dev_bt = NULL;
+	struct m_dev *m_dev_nfc = NULL;
+	struct timeval tstamp;
+	int index;
+
+	ENTER();
+
+	m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ];
+	m_dev_nfc = &priv->bt_dev.m_dev[NFC_SEQ];
+
+	if ((priv == NULL) || (priv->adapter == NULL) ||
+	    (priv->bt_dev.card == NULL) || (m_dev_bt == NULL)
+	    || (m_dev_nfc == NULL)
+		) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	if (!fw_firmware) {
+		do_gettimeofday(&tstamp);
+		if (tstamp.tv_sec >
+		    (priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) {
+			PRINTM(ERROR,
+			       "BT: No firmware image found. Skipping download\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		PRINTM(ERROR,
+		       "BT: No firmware image found! Retrying download\n");
+		/* Wait a second here before calling the callback again */
+		os_sched_timeout(1000);
+		sd_download_firmware_w_helper(priv);
+		LEAVE();
+		return ret;
+	}
+
+	priv->firmware = fw_firmware;
+
+	if (BT_STATUS_FAILURE ==
+	    sd_init_fw_dpc(priv, (u8 *)priv->firmware->data,
+			   priv->firmware->size)) {
+		PRINTM(ERROR,
+		       "BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n",
+		       bt_req_fw_nowait);
+		sdio_release_host(card->func);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* check if the fimware is downloaded successfully or not */
+	if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) {
+		PRINTM(ERROR, "BT: FW failed to be active in time!\n");
+		ret = BT_STATUS_FAILURE;
+		sdio_release_host(card->func);
+		goto done;
+	}
+	sdio_release_host(card->func);
+	sbi_enable_host_int(priv);
+	if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+		PRINTM(ERROR,
+		       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (fw_firmware) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+		if (!bt_req_fw_nowait)
+#endif
+			release_firmware(fw_firmware);
+	}
+	LEAVE();
+	return ret;
+
+done:
+	if (fw_firmware) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+		if (!bt_req_fw_nowait)
+#endif
+			release_firmware(fw_firmware);
+	}
+	/* For synchronous download cleanup will be done in add_card */
+	if (!bt_req_fw_nowait)
+		return ret;
+	PRINTM(INFO, "unregister device\n");
+	sbi_unregister_dev(priv);
+	((struct sdio_mmc_card *)card)->priv = NULL;
+	/* Stop the thread servicing the interrupts */
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid)
+		os_sched_timeout(1);
+	bt_proc_remove(priv);
+	clean_up_m_devs(priv);
+	bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief request_firmware callback
+ *        This function is invoked by request_firmware_nowait system call
+ *
+ * @param firmware     A pointer to firmware structure
+ * @param context      A Pointer to bt_private structure
+ * @return             None
+ **/
+static void
+sd_request_fw_callback(const struct firmware *firmware, void *context)
+{
+	ENTER();
+	sd_request_fw_dpc(firmware, context);
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function downloads firmware image to the card.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sd_download_firmware_w_helper(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	int err;
+	char *cur_fw_name = NULL;
+
+	ENTER();
+
+	cur_fw_name = fw_name;
+
+	if (fw_name == NULL) {
+		if (priv->card_type == CARD_TYPE_SD8787)
+			cur_fw_name = DEFAULT_FW_NAME_8787;
+		else if (priv->card_type == CARD_TYPE_SD8777)
+			cur_fw_name = DEFAULT_FW_NAME_8777;
+		else if (priv->card_type == CARD_TYPE_SD8887) {
+			/* Check revision ID */
+			switch (priv->adapter->chip_rev) {
+			case SD8887_A0:
+				cur_fw_name = SD8887_A0_FW_NAME;
+				break;
+			case SD8887_A2:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8887_A2_FW_NAME;
+				else
+					cur_fw_name = SD8887_A2_BT_FW_NAME;
+				break;
+			default:
+				cur_fw_name = DEFAULT_FW_NAME_8887;
+				break;
+			}
+		} else if (priv->card_type == CARD_TYPE_SD8897)
+			cur_fw_name = DEFAULT_FW_NAME_8897;
+		else if (priv->card_type == CARD_TYPE_SD8797)
+			cur_fw_name = DEFAULT_FW_NAME_8797;
+		else if (priv->card_type == CARD_TYPE_SD8977) {
+			switch (priv->adapter->chip_rev) {
+			case SD8977_V0:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8977_V0_FW_NAME;
+				else
+					cur_fw_name = SD8977_V0_BT_FW_NAME;
+				break;
+			case SD8977_V1:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8977_V1_FW_NAME;
+				else
+					cur_fw_name = SD8977_V1_BT_FW_NAME;
+				break;
+			case SD8977_V2:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8977_V2_FW_NAME;
+				else
+					cur_fw_name = SD8977_V2_BT_FW_NAME;
+				break;
+			default:
+				cur_fw_name = DEFAULT_FW_NAME_8977;
+				break;
+			}
+		} else if (priv->card_type == CARD_TYPE_SD8997)
+			switch (priv->adapter->chip_rev) {
+			case SD8997_Z:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8997_Z_FW_NAME;
+				else
+					cur_fw_name = SD8997_Z_BT_FW_NAME;
+				break;
+			case SD8997_V2:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload) {
+					if (priv->adapter->magic_val ==
+					    MAGIC_VAL)
+						cur_fw_name = SD8997_V3_FW_NAME;
+					else
+						cur_fw_name = SD8997_V2_FW_NAME;
+				} else {
+					if (priv->adapter->magic_val ==
+					    MAGIC_VAL)
+						cur_fw_name =
+							SD8997_V3_BT_FW_NAME;
+					else
+						cur_fw_name =
+							SD8997_V2_BT_FW_NAME;
+				}
+				break;
+			default:
+				cur_fw_name = DEFAULT_FW_NAME_8997;
+				break;
+		} else if (priv->card_type == CARD_TYPE_SD8987) {
+			if (bt_fw_serial == 1
+			    && !priv->fw_reload && !bt_fw_reload)
+				cur_fw_name = SD8987_FW_NAME;
+			else
+				cur_fw_name = SD8987_BT_FW_NAME;
+		}
+	}
+
+	PRINTM(MSG, "BT Request firmware: %s\n", cur_fw_name);
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cur_fw_name, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      sd_request_fw_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cur_fw_name, priv->hotplug_device,
+					      priv, sd_request_fw_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cur_fw_name, priv->hotplug_device,
+					      priv, sd_request_fw_callback);
+#endif
+#endif
+		if (ret < 0)
+			PRINTM(FATAL,
+			       "BT: request_firmware_nowait() failed, error code = %#x\n",
+			       ret);
+	} else {
+		err = request_firmware(&priv->firmware, cur_fw_name,
+				       priv->hotplug_device);
+		if (err < 0) {
+			PRINTM(FATAL,
+			       "BT: request_firmware() failed, error code = %#x\n",
+			       err);
+			ret = BT_STATUS_FAILURE;
+		} else
+			ret = sd_request_fw_dpc(priv->firmware, priv);
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads data from the card.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_card_to_host(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u16 buf_len = 0;
+	int buf_block_len;
+	int blksz;
+	struct sk_buff *skb = NULL;
+	u32 type;
+	u8 *payload = NULL;
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+	struct m_dev *mdev_nfc = &(priv->bt_dev.m_dev[NFC_SEQ]);
+	struct nfc_dev *nfc_dev =
+		(struct nfc_dev *)priv->bt_dev.m_dev[NFC_SEQ].dev_pointer;
+	struct m_dev *mdev_debug = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+	struct debug_dev *debug_dev =
+		(struct debug_dev *)priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int i = 0;
+
+	ENTER();
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	do {
+		/* Read the length of data to be transferred */
+		ret = sd_read_rx_len(priv, &buf_len);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR, "BT: Read scratch reg failed (%d)\n", i);
+			if (i >= MAX_CMD52_RETRY) {
+				ret = BT_STATUS_FAILURE;
+				goto exit;
+			}
+			udelay(20);
+		}
+	}
+	while (ret == BT_STATUS_FAILURE);
+
+	/* Allocate buffer */
+	blksz = SD_BLOCK_SIZE;
+	buf_block_len = (buf_len + blksz - 1) / blksz;
+	if (buf_len <= BT_HEADER_LEN ||
+	    (buf_block_len * blksz) > ALLOC_BUF_SIZE) {
+		PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n",
+		       buf_len);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "BT: No free skb\n");
+		goto exit;
+	}
+	if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) {
+		skb_put(skb,
+			DMA_ALIGNMENT -
+			((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
+		skb_pull(skb,
+			 DMA_ALIGNMENT -
+			 ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
+	}
+
+	payload = skb->data;
+	i = 0;
+	do {
+		ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport,
+				  buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR,
+			       "BT: card_to_host, read iomem (%d) failed: %d\n",
+			       i, ret);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+			/* issue abort cmd52 command through F0 */
+			sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret);
+#endif
+			sdio_writeb(card->func, HOST_TERM_CMD53,
+				    CONFIGURATION_REG, &ret);
+			udelay(20);
+			ret = BT_STATUS_FAILURE;
+			if (i >= MAX_CMD53_RETRY) {
+				kfree_skb(skb);
+				skb = NULL;
+				goto exit;
+			}
+		}
+	} while (ret == BT_STATUS_FAILURE);
+	/* This is SDIO specific header length: byte[2][1][0], * type: byte[3]
+	   (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */
+	buf_len = payload[0];
+	buf_len |= (u16) payload[1] << 8;
+	type = payload[3];
+	PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", mbt_dev->name,
+	       buf_len, type);
+	if (buf_len > buf_block_len * blksz) {
+		PRINTM(ERROR,
+		       "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n",
+		       buf_len, buf_block_len * blksz);
+		ret = BT_STATUS_FAILURE;
+		kfree_skb(skb);
+		skb = NULL;
+		goto exit;
+	}
+	DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len);
+	switch (type) {
+	case HCI_ACLDATA_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (*(u16 *) skb->data == 0xffff) {
+			skb_queue_tail(&priv->adapter->fwdump_queue, skb);
+			break;
+		}
+		bt_recv_frame(priv, skb);
+		break;
+	case HCI_SCODATA_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		bt_recv_frame(priv, skb);
+		break;
+	case HCI_EVENT_PKT:
+		/** add EVT Demux */
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb))
+			break;
+		switch (skb->data[0]) {
+		case 0x0E:
+			/** cmd complete */
+			if (priv->debug_device_pending) {
+				if (priv->debug_ocf_ogf[0] == skb->data[3] &&
+				    priv->debug_ocf_ogf[1] == skb->data[4]) {
+					priv->debug_device_pending = 0;
+					priv->debug_ocf_ogf[0] = 0;
+					priv->debug_ocf_ogf[1] = 0;
+					/** debug cmd complete */
+					if (debug_dev) {
+						skb->dev = (void *)mdev_debug;
+						mdev_recv_frame(skb);
+						mdev_debug->stat.byte_rx +=
+							buf_len;
+					}
+					break;
+				}
+			}
+			if (skb->data[3] == 0x81 && skb->data[4] == 0xFE) {
+				/** NFC cmd complete */
+				if (nfc_dev) {
+					skb->dev = (void *)mdev_nfc;
+					mdev_recv_frame(skb);
+					mdev_nfc->stat.byte_rx += buf_len;
+				}
+				break;
+			}
+			bt_recv_frame(priv, skb);
+			break;
+		case 0x0F:
+			/** cmd status */
+			if (skb->data[4] == 0x81 && skb->data[5] == 0xFE) {
+				/** NFC cmd ststus */
+				if (nfc_dev) {
+					skb->dev = (void *)mdev_nfc;
+					mdev_recv_frame(skb);
+					mdev_nfc->stat.byte_rx += buf_len;
+				}
+				break;
+			}
+			bt_recv_frame(priv, skb);
+			break;
+		case 0xFF:
+			/** Vendor specific pkt */
+			if (skb->data[2] == 0xC0) {
+				/** NFC EVT */
+				if (nfc_dev) {
+					skb->dev = (void *)mdev_nfc;
+					mdev_recv_frame(skb);
+					mdev_nfc->stat.byte_rx += buf_len;
+				}
+				break;
+			}
+			bt_recv_frame(priv, skb);
+			break;
+		default:
+			bt_recv_frame(priv, skb);
+			break;
+		}
+		break;
+	case MRVL_VENDOR_PKT:
+		/* Just think here need to back compatible FM */
+		bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (BT_STATUS_SUCCESS != bt_process_event(priv, skb))
+			bt_recv_frame(priv, skb);
+		break;
+	default:
+		/* Driver specified event and command resp should be handle
+		   here */
+		PRINTM(INFO, "BT: Unknown PKT type:%d\n", type);
+		kfree_skb(skb);
+		skb = NULL;
+		break;
+	}
+exit:
+	if (ret) {
+		if (mbt_dev)
+			mdev_bt->stat.err_rx++;
+		PRINTM(ERROR, "error when recv pkt!\n");
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function removes the card
+ *
+ *  @param func    A pointer to sdio_func structure
+ *  @return        N/A
+ */
+static void
+sd_remove_card(struct sdio_func *func)
+{
+	struct sdio_mmc_card *card;
+
+	ENTER();
+
+	if (func) {
+		card = sdio_get_drvdata(func);
+		if (card) {
+			bt_remove_card(card->priv);
+			kfree(card);
+		}
+	}
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function handles the interrupt.
+ *
+ *  @param func  A pointer to sdio_func structure
+ *  @return      N/A
+ */
+static void
+sd_interrupt(struct sdio_func *func)
+{
+	bt_private *priv;
+	struct m_dev *m_dev = NULL;
+	struct sdio_mmc_card *card;
+	int ret = BT_STATUS_SUCCESS;
+	u8 ireg = 0;
+	u8 host_intstatus_reg = 0;
+
+	ENTER();
+
+	card = sdio_get_drvdata(func);
+	if (!card || !card->priv) {
+		PRINTM(INFO,
+		       "BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n",
+		       __func__, func, card);
+		LEAVE();
+		return;
+	}
+	priv = card->priv;
+	host_intstatus_reg = priv->psdio_device->reg->host_intstatus;
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	if (priv->card_type == CARD_TYPE_SD8887 ||
+	    priv->card_type == CARD_TYPE_SD8897 ||
+	    priv->card_type == CARD_TYPE_SD8977 ||
+	    priv->card_type == CARD_TYPE_SD8997 ||
+	    priv->card_type == CARD_TYPE_SD8987) {
+		ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0,
+				  SD_BLOCK_SIZE);
+		if (ret) {
+			PRINTM(ERROR,
+			       "BT: sdio_read_ioreg: cmd53 read int status register failed %d\n",
+			       ret);
+			goto done;
+		}
+		ireg = priv->adapter->hw_regs[host_intstatus_reg];
+	} else {
+		ireg = sdio_readb(card->func, host_intstatus_reg, &ret);
+	}
+	if (ret) {
+		PRINTM(ERROR,
+		       "BT: sdio_read_ioreg: CMD52 read int status register failed %d\n",
+		       ret);
+		goto done;
+	}
+	if (ireg != 0) {
+		/*
+		 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+		 * Clear the interrupt status register and re-enable
+		 * the interrupt
+		 */
+		PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name,
+		       ireg);
+		priv->adapter->irq_recv = ireg;
+		if (priv->card_type == CARD_TYPE_SD8777 ||
+		    priv->card_type == CARD_TYPE_SD8787) {
+			sdio_writeb(card->func,
+				    ~(ireg) & (DN_LD_HOST_INT_STATUS |
+					       UP_LD_HOST_INT_STATUS),
+				    host_intstatus_reg, &ret);
+			if (ret) {
+				PRINTM(ERROR,
+				       "BT: sdio_write_ioreg: clear int status register failed\n");
+				goto done;
+			}
+		}
+	} else {
+		PRINTM(ERROR, "BT: ERR: ireg=0\n");
+	}
+	OS_INT_DISABLE;
+	priv->adapter->sd_ireg |= ireg;
+	OS_INT_RESTORE;
+	bt_interrupt(m_dev);
+done:
+	LEAVE();
+}
+
+/**
+ *  @brief This function checks if the interface is ready to download
+ *  or not while other download interfaces are present
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @param val    Winner status (0: winner)
+ *  @return       BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_check_winner_status(bt_private *priv, u8 *val)
+{
+
+	int ret = BT_STATUS_SUCCESS;
+	u8 winner = 0;
+	struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0;
+
+	ENTER();
+	winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret);
+	if (ret != BT_STATUS_SUCCESS) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	*val = winner;
+
+	LEAVE();
+	return ret;
+}
+
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+/** @brief This function tells lower driver that BT is suspended
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        None
+ */
+void
+bt_is_suspended(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	priv->adapter->is_suspended = TRUE;
+	sdio_func_suspended(card->func);
+}
+#endif
+
+/** @brief This function handles client driver suspend
+ *
+ *  @param dev	   A pointer to device structure
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+int
+bt_sdio_suspend(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+		PRINTM(ERROR,
+		       "BT: %s: cannot remain alive while host is suspended\n",
+		       sdio_func_id(func));
+		return -ENOSYS;
+	}
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv = cardp->priv;
+
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name);
+	mbt_hci_suspend_dev(m_dev);
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
+#ifdef BLE_WAKEUP
+		/** Set BLE Wake up pattern */
+		if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv))
+			PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n");
+#endif
+
+		if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+			PRINTM(CMD, "BT: HS not actived, suspend fail!\n");
+			if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+				PRINTM(CMD,
+				       "BT: HS not actived the second time, force to suspend!\n");
+			}
+		}
+	}
+
+	priv->adapter->is_suspended = TRUE;
+
+	LEAVE();
+	/* We will keep the power when hs enabled successfully */
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) {
+#ifdef MMC_PM_SKIP_RESUME_PROBE
+		PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and "
+		       "MMC_PM_SKIP_RESUME_PROBE\n");
+		return sdio_set_host_pm_flags(func,
+					      MMC_PM_KEEP_POWER |
+					      MMC_PM_SKIP_RESUME_PROBE);
+#else
+		PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n");
+		return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+#endif
+	} else {
+		PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n");
+		return BT_STATUS_SUCCESS;
+	}
+}
+
+void
+bt_sdio_shutdown(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+
+	ENTER();
+
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: shutdown: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+		PRINTM(ERROR,
+		       "BT: %s: cannot remain alive while host is shutdown\n",
+		       sdio_func_id(func));
+		return;
+	}
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return;
+	}
+
+	priv = cardp->priv;
+
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
+#ifdef BLE_WAKEUP
+		/** Set BLE Wake up pattern */
+		if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv))
+			PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n");
+#endif
+
+		if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+			PRINTM(CMD, "BT: HS not actived, shutdown fail!\n");
+			if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+				PRINTM(CMD,
+				       "BT: HS not actived the second time, force to shutdown!\n");
+			}
+		}
+	}
+	LEAVE();
+}
+
+/** @brief This function handles client driver resume
+ *
+ *  @param dev	   A pointer to device structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_sdio_resume(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv = cardp->priv;
+	priv->adapter->is_suspended = FALSE;
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name);
+#ifdef BLE_WAKEUP
+	if (priv->ble_wakeup_buf) {
+		PRINTM(CMD, "BT: Send system resume event\n");
+		bt_send_system_event(priv, FALSE);
+	}
+#endif
+	mbt_hci_resume_dev(m_dev);
+	sbi_wakeup_firmware(priv);
+	priv->adapter->hs_state = HS_DEACTIVATED;
+	PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+#endif
+#endif
+
+/********************************************************
+		Global Functions
+********************************************************/
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+static const struct dev_pm_ops bt_sdio_pm_ops = {
+	.suspend = bt_sdio_suspend,
+	.resume = bt_sdio_resume,
+};
+#endif
+#endif
+static struct sdio_driver sdio_bt = {
+	.name = "sdio_bt",
+	.id_table = bt_ids,
+	.probe = sd_probe_card,
+	.remove = sd_remove_card,
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+	.drv = {
+		.pm = &bt_sdio_pm_ops,
+		.shutdown = bt_sdio_shutdown,
+		}
+#endif
+#endif
+};
+
+/**
+ *  @brief This function registers the bt module in bus driver.
+ *
+ *  @return	   An int pointer that keeps returned value
+ */
+int *
+sbi_register(void)
+{
+	int *ret;
+
+	ENTER();
+
+	if (sdio_register_driver(&sdio_bt) != 0) {
+		PRINTM(FATAL, "BT: SD Driver Registration Failed\n");
+		LEAVE();
+		return NULL;
+	} else
+		ret = (int *)1;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function de-registers the bt module in bus driver.
+ *
+ *  @return        N/A
+ */
+void
+sbi_unregister(void)
+{
+	ENTER();
+	sdio_unregister_driver(&sdio_bt);
+	LEAVE();
+}
+
+/**
+ *  @brief This function registers the device.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_dev(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	u8 chiprev;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	struct sdio_func *func;
+	u8 host_intstatus_reg = priv->psdio_device->reg->host_intstatus;
+	u8 host_int_rsr_reg = priv->psdio_device->reg->host_int_rsr_reg;
+	u8 card_misc_cfg_reg = priv->psdio_device->reg->card_misc_cfg_reg;
+	u8 card_revision_reg = priv->psdio_device->reg->card_revision;
+	u8 io_port_0_reg = priv->psdio_device->reg->io_port_0;
+	u8 io_port_1_reg = priv->psdio_device->reg->io_port_1;
+	u8 io_port_2_reg = priv->psdio_device->reg->io_port_2;
+	u8 card_magic_reg = CARD_MAGIC_REG;
+	u8 magic_val = 0;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: Error: card or function is NULL!\n");
+		goto failed;
+	}
+	func = card->func;
+	priv->hotplug_device = &func->dev;
+
+	/* Initialize the private structure */
+	strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name));
+	priv->bt_dev.ioport = 0;
+	priv->bt_dev.fn = func->num;
+
+	sdio_claim_host(func);
+	ret = sdio_claim_irq(func, sd_interrupt);
+	if (ret) {
+		PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret);
+		goto release_host;
+	}
+	ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE);
+	if (ret) {
+		PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__);
+		goto release_irq;
+	}
+
+	/* read Revision Register to get the chip revision number */
+	chiprev = sdio_readb(func, card_revision_reg, &ret);
+	if (ret) {
+		PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n");
+		goto release_irq;
+	}
+	priv->adapter->chip_rev = chiprev;
+	PRINTM(INFO, "revision=%#x\n", chiprev);
+
+	magic_val = sdio_readb(func, card_magic_reg, &ret);
+	if (ret) {
+		PRINTM(FATAL, ": cannot read CARD_MAGIC_REG\n");
+		goto release_irq;
+	}
+	priv->adapter->magic_val = magic_val;
+	PRINTM(INFO, "magic_val=%#x\n", magic_val);
+
+	/*
+	 * Read the HOST_INTSTATUS_REG for ACK the first interrupt got
+	 * from the bootloader. If we don't do this we get a interrupt
+	 * as soon as we register the irq.
+	 */
+	reg = sdio_readb(func, host_intstatus_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+
+	/* Read the IO port */
+	reg = sdio_readb(func, io_port_0_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= reg;
+
+	reg = sdio_readb(func, io_port_1_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= (reg << 8);
+
+	reg = sdio_readb(func, io_port_2_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= (reg << 16);
+
+	PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn,
+	       priv->bt_dev.ioport);
+
+	if (priv->card_type == CARD_TYPE_SD8977) {
+		if (bt_intmode == INT_MODE_GPIO) {
+			PRINTM(MSG, "Enable GPIO-1 INT\n");
+			sdio_writeb(func, ENABLE_GPIO_1_INT_MODE,
+				    SCRATCH_REG_32, &ret);
+			if (ret < 0)
+				goto release_irq;
+		}
+	}
+
+#define SDIO_INT_MASK       0x3F
+	if (priv->card_type == CARD_TYPE_SD8887 ||
+	    priv->card_type == CARD_TYPE_SD8897 ||
+	    priv->card_type == CARD_TYPE_SD8797 ||
+	    priv->card_type == CARD_TYPE_SD8977 ||
+	    priv->card_type == CARD_TYPE_SD8997 ||
+	    priv->card_type == CARD_TYPE_SD8987) {
+		/* Set Host interrupt reset to read to clear */
+		reg = sdio_readb(func, host_int_rsr_reg, &ret);
+		if (ret < 0)
+			goto release_irq;
+		sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret);
+		if (ret < 0)
+			goto release_irq;
+		/* Set auto re-enable */
+		reg = sdio_readb(func, card_misc_cfg_reg, &ret);
+		if (ret < 0)
+			goto release_irq;
+		sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg,
+			    &ret);
+		if (ret < 0)
+			goto release_irq;
+	}
+
+	sdio_set_drvdata(func, card);
+	sdio_release_host(func);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+release_irq:
+	sdio_release_irq(func);
+release_host:
+	sdio_release_host(func);
+failed:
+
+	LEAVE();
+	return BT_STATUS_FAILURE;
+}
+
+/**
+ *  @brief This function de-registers the device.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+sbi_unregister_dev(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+
+	ENTER();
+
+	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);
+	}
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function enables the host interrupts.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_enable_host_int(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	ret = sd_enable_host_int_mask(priv, HIM_ENABLE);
+	sd_get_rx_unit(priv);
+	sdio_release_host(card->func);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function disables the host interrupts.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sbi_disable_host_int(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	ret = sbi_disable_host_int_mask(priv, HIM_DISABLE);
+	sdio_release_host(card->func);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends data to the card.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param payload A pointer to the data/cmd buffer
+ *  @param nb      Length of data/cmd
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+#ifdef DEBUG_LEVEL1
+	struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+#endif
+	int ret = BT_STATUS_SUCCESS;
+	int buf_block_len;
+	int blksz;
+	int i = 0;
+	u8 *buf = NULL;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	buf = payload;
+
+	blksz = SD_BLOCK_SIZE;
+	buf_block_len = (nb + blksz - 1) / blksz;
+	/* Allocate buffer and copy payload */
+	if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) {
+		if (nb > MAX_TX_BUF_SIZE) {
+			PRINTM(ERROR, "BT: Invalid tx packet, size=%d\n", nb);
+			LEAVE();
+			return BT_STATUS_FAILURE;
+		}
+		/* Ensure 8-byte aligned CMD buffer */
+		buf = priv->adapter->tx_buf;
+		memcpy(buf, payload, nb);
+	}
+	sdio_claim_host(card->func);
+	do {
+		/* Transfer data to card */
+		ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf,
+				   buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR,
+			       "BT: host_to_card, write iomem (%d) failed: %d\n",
+			       i, ret);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+			/* issue abort cmd52 command through F0 */
+			sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret);
+#endif
+			sdio_writeb(card->func, HOST_TERM_CMD53,
+				    CONFIGURATION_REG, &ret);
+			udelay(20);
+			ret = BT_STATUS_FAILURE;
+			if (i >= MAX_CMD53_RETRY)
+				goto exit;
+		} else {
+			PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n",
+			       m_dev->name, nb);
+			DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb);
+		}
+	} while (ret == BT_STATUS_FAILURE);
+	priv->bt_dev.tx_dnld_rdy = FALSE;
+exit:
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function downloads firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_download_fw(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+	u8 winner = 0;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	sdio_claim_host(card->func);
+	if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) {
+		PRINTM(MSG, "BT: FW already downloaded!\n");
+		sdio_release_host(card->func);
+		sbi_enable_host_int(priv);
+		if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+			PRINTM(ERROR,
+			       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+			ret = BT_STATUS_FAILURE;
+			goto err_register;
+		}
+		goto exit;
+	}
+	/* Check if other interface is downloading */
+	ret = sd_check_winner_status(priv, &winner);
+	if (ret == BT_STATUS_FAILURE) {
+		PRINTM(FATAL, "BT read winner status failed!\n");
+		goto done;
+	}
+	if (winner) {
+		PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n",
+		       winner);
+		/* check if the fimware is downloaded successfully or not */
+		if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) {
+			PRINTM(FATAL, "BT: FW failed to be active in time!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		sdio_release_host(card->func);
+		sbi_enable_host_int(priv);
+		if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+			PRINTM(ERROR,
+			       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+			ret = BT_STATUS_FAILURE;
+			goto err_register;
+		}
+		goto exit;
+	}
+
+	do_gettimeofday(&priv->req_fw_time);
+	/* Download the main firmware via the helper firmware */
+	if (sd_download_firmware_w_helper(priv)) {
+		PRINTM(INFO, "BT: FW download failed!\n");
+		ret = BT_STATUS_FAILURE;
+	}
+	goto exit;
+done:
+	sdio_release_host(card->func);
+exit:
+	LEAVE();
+	return ret;
+err_register:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function checks the interrupt status and handle it accordingly.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+sbi_get_int_status(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 sdio_ireg = 0;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+
+	ENTER();
+
+	OS_INT_DISABLE;
+	sdio_ireg = priv->adapter->sd_ireg;
+	priv->adapter->sd_ireg = 0;
+	OS_INT_RESTORE;
+	sdio_claim_host(card->func);
+	priv->adapter->irq_done = sdio_ireg;
+	if (sdio_ireg & DN_LD_HOST_INT_STATUS) {	/* tx_done INT */
+		if (priv->bt_dev.tx_dnld_rdy) {	/* tx_done already received */
+			PRINTM(INFO,
+			       "BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n",
+			       priv->bt_dev.tx_dnld_rdy, sdio_ireg);
+		} else {
+			priv->bt_dev.tx_dnld_rdy = TRUE;
+		}
+	}
+	if (sdio_ireg & UP_LD_HOST_INT_STATUS)
+		sd_card_to_host(priv);
+
+	ret = BT_STATUS_SUCCESS;
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function wakeup firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sbi_wakeup_firmware(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
+	sdio_release_host(card->func);
+	PRINTM(CMD, "BT wake up firmware\n");
+
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function updates the SDIO card types
+ *
+ *  @param priv     A Pointer to the bt_private structure
+ *  @param card     A Pointer to card
+ *
+ *  @return         N/A
+ */
+void
+sdio_update_card_type(bt_private *priv, void *card)
+{
+	struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)card;
+
+	/* Update card type */
+	if (cardp->func->device == SD_DEVICE_ID_8777_BT_FN2 ||
+	    cardp->func->device == SD_DEVICE_ID_8777_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8777;
+	else if (cardp->func->device == SD_DEVICE_ID_8787_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8787_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8787;
+	else if (cardp->func->device == SD_DEVICE_ID_8887_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8887_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8887;
+	else if (cardp->func->device == SD_DEVICE_ID_8897_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8897_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8897;
+	else if (cardp->func->device == SD_DEVICE_ID_8797_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8797_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8797;
+	else if (cardp->func->device == SD_DEVICE_ID_8977_BT_FN2)
+		priv->card_type = CARD_TYPE_SD8977;
+	else if (cardp->func->device == SD_DEVICE_ID_8997_BT_FN2)
+		priv->card_type = CARD_TYPE_SD8997;
+	else if (cardp->func->device == SD_DEVICE_ID_8987_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8987_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8987;
+}
+
+/**
+ *  @brief This function get sdio device from card type
+ *
+ *  @param pmadapter  A pointer to mlan_adapter structure
+ *  @return           MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+int
+sdio_get_sdio_device(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u16 card_type = priv->card_type;
+
+	ENTER();
+
+	switch (card_type) {
+	case CARD_TYPE_SD8777:
+		priv->psdio_device = &bt_sdio_sd8777;
+		break;
+	case CARD_TYPE_SD8787:
+		priv->psdio_device = &bt_sdio_sd8787;
+		break;
+	case CARD_TYPE_SD8887:
+		priv->psdio_device = &bt_sdio_sd8887;
+		break;
+	case CARD_TYPE_SD8897:
+		priv->psdio_device = &bt_sdio_sd8897;
+		break;
+	case CARD_TYPE_SD8797:
+		priv->psdio_device = &bt_sdio_sd8797;
+		break;
+	case CARD_TYPE_SD8977:
+		priv->psdio_device = &bt_sdio_sd8977;
+		break;
+	case CARD_TYPE_SD8997:
+		priv->psdio_device = &bt_sdio_sd8997;
+		break;
+	case CARD_TYPE_SD8987:
+		priv->psdio_device = &bt_sdio_sd8987;
+		break;
+	default:
+		PRINTM(ERROR, "BT can't get right card type \n");
+		ret = BT_STATUS_FAILURE;
+		break;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+#define SD8897_INIT_START_REG  0xDC
+#define SD8897_INIT_END_REG  0xE1
+#define SD8887_INIT_START_REG  0xA0
+#define SD8887_INIT_END_REG  0xA5
+#define SD8977_SD8997_INIT_START_REG 0xF1
+#define SD8977_SD8997_INIT_END_REG 0xF6
+
+/** @brief This function dump the SDIO register
+ *
+ *  @param priv     A Pointer to the bt_private structure
+ *
+ *  @return         N/A
+ */
+void
+bt_dump_sdio_regs(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+	char buf[256], *ptr;
+	u8 loop, func, data;
+	unsigned int reg, reg_start, reg_end;
+	u8 index = 0;
+	unsigned int reg_table_8887[] = { 0x58, 0x59, 0x5c, 0x60, 0x64, 0x70,
+		0x71, 0x72, 0x73, 0xd8, 0xd9, 0xda
+	};
+	u8 loop_num = 0;
+	unsigned int *reg_table = NULL;
+	u8 reg_table_size = 0;
+	unsigned int init_reg_start = 0;
+	unsigned int init_reg_end = 0;
+	if (priv->card_type == CARD_TYPE_SD8887) {
+		init_reg_start = SD8887_INIT_START_REG;
+		init_reg_end = SD8887_INIT_END_REG;
+	} else if (priv->card_type == CARD_TYPE_SD8897) {
+		init_reg_start = SD8897_INIT_START_REG;
+		init_reg_end = SD8897_INIT_END_REG;
+	} else if (priv->card_type == CARD_TYPE_SD8977 ||
+		   priv->card_type == CARD_TYPE_SD8997 ||
+		   priv->card_type == CARD_TYPE_SD8987) {
+		init_reg_start = SD8977_SD8997_INIT_START_REG;
+		init_reg_end = SD8977_SD8997_INIT_END_REG;
+	}
+
+	if (priv->card_type == CARD_TYPE_SD8887) {
+		loop_num = 3;
+		reg_table = reg_table_8887;
+		reg_table_size = sizeof(reg_table_8887) / sizeof(int);
+	} else
+		loop_num = 2;
+	if (priv->adapter->ps_state)
+		sbi_wakeup_firmware(priv);
+
+	sdio_claim_host(card->func);
+	for (loop = 0; loop < loop_num; loop++) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+		if (loop == 0) {
+			/* Read the registers of SDIO function0 */
+			func = loop;
+			reg_start = 0;
+			reg_end = 9;
+
+		} else if (loop == 2) {
+			/* Read specific registers of SDIO function1 */
+			index = 0;
+			func = 2;
+			reg_start = reg_table[index++];
+			reg_end = reg_table[reg_table_size - 1];
+		} else {
+			func = 2;
+			reg_start = 0;
+			reg_end = 0x09;
+		}
+		if (loop == 2)
+			ptr += sprintf(ptr, "SDIO Func%d: ", func);
+		else
+			ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func,
+				       reg_start, reg_end);
+		for (reg = reg_start; reg <= reg_end;) {
+			if (func == 0)
+				data = sdio_f0_readb(card->func, reg, &ret);
+			else
+				data = sdio_readb(card->func, reg, &ret);
+			if (loop == 2)
+				ptr += sprintf(ptr, "(%#x)", reg);
+			if (!ret)
+				ptr += sprintf(ptr, "%02x ", data);
+			else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+			if (loop == 2 && reg < reg_end)
+				reg = reg_table[index++];
+			else
+				reg++;
+		}
+		PRINTM(MSG, "%s\n", buf);
+	}
+
+	if (init_reg_start) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+		ptr += sprintf(ptr, "Init Status Reg (%#x-%#x): ",
+			       init_reg_start, init_reg_end);
+		for (reg = init_reg_start; reg <= init_reg_end;) {
+			data = sdio_readb(card->func, reg, &ret);
+			if (!ret)
+				ptr += sprintf(ptr, "%02x ", data);
+			else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+			reg++;
+		}
+		PRINTM(MSG, "%s\n", buf);
+	}
+	sdio_release_host(card->func);
+}
+
+module_param(fw_name, charp, 0);
+MODULE_PARM_DESC(fw_name, "Firmware name");
+module_param(bt_req_fw_nowait, int, 0);
+MODULE_PARM_DESC(bt_req_fw_nowait,
+		 "0: Use request_firmware API; 1: Use request_firmware_nowait API");
+module_param(multi_fn, int, 0);
+MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;");
+
+module_param(bt_intmode, int, 0);
+MODULE_PARM_DESC(bt_intmode, "0: INT_MODE_SDIO, 1: INT_MODE_GPIO");
diff --git a/bt_sd8897/bt/hci_wrapper.h b/bt_sd8897/bt/hci_wrapper.h
new file mode 100644
index 0000000..3537a17
--- /dev/null
+++ b/bt_sd8897/bt/hci_wrapper.h
@@ -0,0 +1,180 @@
+/** @file hci_wrapper.h
+ *  @brief This file contains HCI related definitions
+ *
+ *  Copyright (C) 2011-2018, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _HCI_WRAPPER_H_
+#define _HCI_WRAPPER_H_
+
+#include <linux/module.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+/**  Define Seq num */
+#define BT_SEQ      0
+#define NFC_SEQ     2
+#define DEBUG_SEQ   3
+
+/** Define dev type */
+#define BT_TYPE     1
+#define BT_AMP_TYPE 2
+#define NFC_TYPE    4
+#define DEBUG_TYPE  5
+
+/** Define spec type */
+#define BLUEZ_SPEC     1
+#define IANYWHERE_SPEC 2
+#define GENERIC_SPEC   3
+
+/** Define lock/unlock wrapper */
+#define mdev_req_lock(d)		down(&d->req_lock)
+#define mdev_req_unlock(d)		up(&d->req_lock)
+
+/** Length of device name */
+#define DEV_NAME_LEN				32
+
+/** Define struct m_dev */
+struct m_dev {
+	char name[DEV_NAME_LEN];
+	int index;
+	unsigned long flags;
+	spinlock_t lock;
+	struct semaphore req_lock;
+	struct sk_buff_head rx_q;
+	wait_queue_head_t req_wait_q;
+	struct hci_dev_stats stat;
+	struct module *owner;
+	void *dev_pointer;
+	int dev_type;
+	int spec_type;
+	void *driver_data;
+	int read_continue_flag;
+	int wait_rx_complete;
+	int rx_complete_flag;
+	wait_queue_head_t rx_wait_q;
+	spinlock_t rxlock;
+	atomic_t extra_cnt;
+
+	struct sk_buff *evt_skb;
+	struct sk_buff *acl_skb;
+	struct sk_buff *sco_skb;
+
+	int (*open) (struct m_dev * m_dev);
+	int (*close) (struct m_dev * m_dev);
+	int (*flush) (struct m_dev * m_dev);
+	int (*send) (struct m_dev * m_dev, struct sk_buff * skb);
+	void (*destruct) (struct m_dev * m_dev);
+	void (*notify) (struct m_dev * m_dev, unsigned int evt);
+	int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, void *arg);
+	void (*query) (struct m_dev * m_dev, void *arg);
+
+};
+
+/** Define struct mbt_dev */
+struct mbt_dev {
+	/** maybe could add some private member later */
+	char name[DEV_NAME_LEN];
+	unsigned long flags;
+	__u8 type;
+
+	__u16 pkt_type;
+	__u16 esco_type;
+	__u16 link_policy;
+	__u16 link_mode;
+
+	__u32 idle_timeout;
+	__u16 sniff_min_interval;
+	__u16 sniff_max_interval;
+
+	struct sk_buff *reassembly[3];
+
+	atomic_t promisc;
+};
+
+/** Define 'nfc' interface specific struct fm_dev */
+struct nfc_dev {
+	/** maybe could add some private member later */
+	char name[DEV_NAME_LEN];
+	unsigned long flags;
+};
+
+struct debug_dev {
+	/** maybe could add some private member later */
+	char name[DEV_NAME_LEN];
+	unsigned long flags;
+};
+
+/** This function frees m_dev allocation */
+void free_m_dev(struct m_dev *m_dev);
+
+/**
+ *  @brief This function receives frames
+ *
+ *  @param skb	A pointer to struct sk_buff
+ *  @return	0--success otherwise error code
+ */
+static inline int
+mdev_recv_frame(struct sk_buff *skb)
+{
+	struct m_dev *m_dev = (struct m_dev *)skb->dev;
+	if (!m_dev || (!test_bit(HCI_UP, &m_dev->flags)
+		       && !test_bit(HCI_INIT, &m_dev->flags))) {
+		kfree_skb(skb);
+		return -ENXIO;
+	}
+
+	/* Incomming skb */
+	bt_cb(skb)->incoming = 1;
+
+	/* Time stamp */
+	__net_timestamp(skb);
+
+	/* Queue frame for rx task */
+	skb_queue_tail(&m_dev->rx_q, skb);
+
+	/* Wakeup rx thread */
+	wake_up_interruptible(&m_dev->req_wait_q);
+
+	return 0;
+}
+
+/**
+ *  @brief mbt dev suspend handler
+ *
+ *  @param m_dev   A pointer to struct m_dev
+ *  @return        0
+ */
+static inline int
+mbt_hci_suspend_dev(struct m_dev *m_dev)
+{
+	return 0;
+}
+
+/**
+ *  @brief mbt dev resume handler
+ *
+ *  @param m_dev   A pointer to struct m_dev
+ *  @return        0
+ */
+static inline int
+mbt_hci_resume_dev(struct m_dev *m_dev)
+{
+	return 0;
+}
+
+#endif /* _HCI_WRAPPER_H_ */
diff --git a/bt_sd8897/bt/mbt_char.c b/bt_sd8897/bt/mbt_char.c
new file mode 100644
index 0000000..22f650a
--- /dev/null
+++ b/bt_sd8897/bt/mbt_char.c
@@ -0,0 +1,815 @@
+/** @file mbt_char.c
+  *
+  * @brief This file contains the char device function calls
+  *
+  * Copyright (C) 2010-2018, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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/path.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+
+#include "bt_drv.h"
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#include <linux/sched/signal.h>
+#endif
+#include "mbt_char.h"
+
+static LIST_HEAD(char_dev_list);
+
+static DEFINE_SPINLOCK(char_dev_list_lock);
+
+static int mbtchar_major = MBTCHAR_MAJOR_NUM;
+
+/**
+ *	@brief  Gets char device structure
+ *
+ *	@param dev		A pointer to char_dev
+ *
+ *	@return			kobject structure
+ */
+struct kobject *
+chardev_get(struct char_dev *dev)
+{
+	struct kobject *kobj;
+
+	kobj = bt_priv_get(dev->m_dev->driver_data);
+	if (!kobj)
+		return NULL;
+	PRINTM(INFO, "dev get kobj\n");
+	kobj = kobject_get(&dev->kobj);
+	if (!kobj)
+		bt_priv_put(dev->m_dev->driver_data);
+	return kobj;
+}
+
+/**
+ *	@brief  Prints char device structure
+ *
+ *	@param dev		A pointer to char_dev
+ *
+ *	@return			N/A
+ */
+void
+chardev_put(struct char_dev *dev)
+{
+	if (dev) {
+		struct m_dev *m_dev = dev->m_dev;
+		PRINTM(INFO, "dev put kobj\n");
+		kobject_put(&dev->kobj);
+		if (m_dev)
+			bt_priv_put(m_dev->driver_data);
+	}
+}
+
+/**
+ *	@brief Changes permissions of the dev
+ *
+ *	@param name	pointer to character
+ *	@param mode		mode_t type data
+ *	@return			0--success otherwise failure
+ */
+int
+mbtchar_chmod(char *name, mode_t mode)
+{
+	struct path path;
+	struct inode *inode;
+	struct iattr newattrs;
+	int ret;
+	int retrycount = 0;
+
+	ENTER();
+	do {
+		os_sched_timeout(30);
+		ret = kern_path(name, LOOKUP_FOLLOW, &path);
+		if (++retrycount >= 10) {
+			PRINTM(ERROR,
+			       "mbtchar_chmod(): fail to get kern_path\n");
+			LEAVE();
+			return -EFAULT;
+		}
+	} while (ret);
+	inode = path.dentry->d_inode;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_lock(&inode->i_mutex);
+#else
+	inode_lock(inode);
+#endif
+	ret = mnt_want_write(path.mnt);
+	if (ret)
+		goto out_unlock;
+	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	if (inode->i_op->setattr)
+		ret = inode->i_op->setattr(path.dentry, &newattrs);
+	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+		ret = simple_setattr(path.dentry, &newattrs);
+#else
+		ret = inode_setattr(inode, &newattrs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+
+	path_put(&path);
+	LEAVE();
+	return ret;
+out_unlock:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+	path_put(&path);
+	return ret;
+}
+
+/**
+ *	@brief Changes ownership of the dev
+ *
+ *	@param name	pointer to character
+ *	@param user		uid_t type data
+ *	@param group	gid_t type data
+ *	@return			0--success otherwise failure
+ */
+int
+mbtchar_chown(char *name, uid_t user, gid_t group)
+{
+	struct path path;
+	struct inode *inode = NULL;
+	struct iattr newattrs;
+	int ret = 0;
+	int retrycount = 0;
+
+	ENTER();
+	do {
+		os_sched_timeout(30);
+		ret = kern_path(name, LOOKUP_FOLLOW, &path);
+		if (++retrycount >= 10) {
+			PRINTM(ERROR,
+			       "mbtchar_chown(): fail to get kern_path\n");
+			LEAVE();
+			return -EFAULT;
+		}
+	} while (ret);
+	inode = path.dentry->d_inode;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_lock(&inode->i_mutex);
+#else
+	inode_lock(inode);
+#endif
+	ret = mnt_want_write(path.mnt);
+	if (ret)
+		goto out_unlock;
+	newattrs.ia_valid = ATTR_CTIME;
+	if (user != (uid_t) (-1)) {
+		newattrs.ia_valid |= ATTR_UID;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+		newattrs.ia_uid = user;
+#else
+		newattrs.ia_uid = KUIDT_INIT(user);
+#endif
+	}
+	if (group != (gid_t) (-1)) {
+		newattrs.ia_valid |= ATTR_GID;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+		newattrs.ia_gid = group;
+#else
+		newattrs.ia_gid = KGIDT_INIT(group);
+#endif
+	}
+	if (!S_ISDIR(inode->i_mode))
+		newattrs.ia_valid |=
+			ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+	if (inode->i_op->setattr)
+		ret = inode->i_op->setattr(path.dentry, &newattrs);
+	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+		ret = simple_setattr(path.dentry, &newattrs);
+#else
+		ret = inode_setattr(inode, &newattrs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+
+	path_put(&path);
+	LEAVE();
+	return ret;
+out_unlock:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+	path_put(&path);
+	return ret;
+}
+
+/**
+ *	@brief write handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param buf		pointer to char buffer
+ *	@param count	size of receive buffer
+ *	@param f_pos	pointer to loff_t type data
+ *	@return			number of bytes written
+ */
+ssize_t
+chardev_write(struct file * filp, const char *buf, size_t count, loff_t * f_pos)
+{
+	int nwrite = 0;
+	struct sk_buff *skb;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	if (!test_bit(HCI_UP, &m_dev->flags)) {
+		LEAVE();
+		return -EBUSY;
+	}
+	nwrite = count;
+	skb = bt_skb_alloc(count, GFP_ATOMIC);
+	if (!skb) {
+		PRINTM(ERROR, "mbtchar_write(): fail to alloc skb\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+
+	if (copy_from_user((void *)skb_put(skb, count), buf, count)) {
+		PRINTM(ERROR, "mbtchar_write(): cp_from_user failed\n");
+		kfree_skb(skb);
+		nwrite = -EFAULT;
+		goto exit;
+	}
+
+	skb->dev = (void *)m_dev;
+	bt_cb(skb)->pkt_type = *((unsigned char *)skb->data);
+	skb_pull(skb, 1);
+
+	PRINTM(DATA, "Write: pkt_type: 0x%x, len=%d @%lu\n",
+	       bt_cb(skb)->pkt_type, skb->len, jiffies);
+	DBG_HEXDUMP(DAT_D, "chardev_write", skb->data, skb->len);
+
+	/* Send skb to the hci wrapper layer */
+	if (m_dev->send(m_dev, skb)) {
+		PRINTM(ERROR, "Write: Fail\n");
+		nwrite = 0;
+		/* Send failed */
+		kfree_skb(skb);
+	}
+exit:
+	LEAVE();
+	return nwrite;
+}
+
+/**
+ *	@brief read handler for BT char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param buf		pointer to char buffer
+ *	@param count	size of receive buffer
+ *	@param f_pos	pointer to loff_t type data
+ *	@return			number of bytes read
+ */
+ssize_t
+chardev_read(struct file * filp, char *buf, size_t count, loff_t * f_pos)
+{
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	struct sk_buff *skb = NULL;
+
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	/* Wait for rx data */
+	add_wait_queue(&m_dev->req_wait_q, &wait);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		skb = skb_dequeue(&m_dev->rx_q);
+		if (skb)
+			break;
+		if (!test_bit(HCI_UP, &m_dev->flags)) {
+			ret = -EBUSY;
+			break;
+		}
+
+		if (filp->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			break;
+		}
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&m_dev->req_wait_q, &wait);
+
+	if (!skb)
+		goto out;
+
+	if (m_dev->read_continue_flag == 0) {
+		/* Put type byte before the data */
+		memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+		PRINTM(DATA, "Read: pkt_type: 0x%x, len=%d @%lu\n",
+		       bt_cb(skb)->pkt_type, skb->len, jiffies);
+	}
+	DBG_HEXDUMP(DAT_D, "chardev_read", skb->data, skb->len);
+	if (skb->len > count) {
+		/* user data length is smaller than the skb length */
+		if (copy_to_user(buf, skb->data, count)) {
+			ret = -EFAULT;
+			goto outf;
+		}
+		skb_pull(skb, count);
+		skb_queue_head(&m_dev->rx_q, skb);
+		m_dev->read_continue_flag = 1;
+		wake_up_interruptible(&m_dev->req_wait_q);
+		ret = count;
+		goto out;
+	} else {
+		if (copy_to_user(buf, skb->data, skb->len)) {
+			ret = -EFAULT;
+			goto outf;
+		}
+		m_dev->read_continue_flag = 0;
+		ret = skb->len;
+	}
+outf:
+	kfree_skb(skb);
+out:
+	if (m_dev->wait_rx_complete && skb_queue_empty(&m_dev->rx_q)) {
+		m_dev->rx_complete_flag = TRUE;
+		wake_up_interruptible(&m_dev->rx_wait_q);
+	}
+	LEAVE();
+	return ret;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief ioctl common handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+char_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg)
+#else
+/**
+ *	@brief ioctl common handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+char_ioctl(struct file *filp, unsigned int cmd, void *arg)
+#endif
+{
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	PRINTM(INFO, "IOCTL: cmd=%d\n", cmd);
+	switch (cmd) {
+	case MBTCHAR_IOCTL_RELEASE:
+		m_dev->close(m_dev);
+		break;
+	case MBTCHAR_IOCTL_QUERY_TYPE:
+		m_dev->query(m_dev, arg);
+		break;
+	default:
+		m_dev->ioctl(m_dev, cmd, arg);
+		break;
+	}
+	LEAVE();
+	return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief ioctl handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_ioctl(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+#else
+/**
+ *	@brief ioctl handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	return char_ioctl(inode, filp, cmd, (void *)arg);
+#else
+	return char_ioctl(filp, cmd, (void *)arg);
+#endif
+}
+
+#ifdef CONFIG_COMPAT
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief compat ioctl handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_ioctl_compat(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg)
+#else
+/**
+ *	@brief compat ioctl handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+chardev_ioctl_compat(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	return char_ioctl(inode, filp, cmd, compat_ptr(arg));
+#else
+	return char_ioctl(filp, cmd, compat_ptr(arg));
+#endif
+}
+#endif /* CONFIG_COMPAT */
+
+/**
+ *	@brief open handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct char_dev *dev = NULL;
+	struct m_dev *m_dev = NULL;
+	struct char_dev *cdev = NULL;
+	struct list_head *p = NULL;
+	ENTER();
+
+	list_for_each(p, &char_dev_list) {
+		cdev = list_entry(p, struct char_dev, list);
+		if (mbtchar_major == MAJOR(inode->i_cdev->dev) &&
+		    cdev->minor == MINOR(inode->i_cdev->dev)) {
+			dev = cdev;
+			break;
+		}
+	}
+	if (!dev) {
+		PRINTM(ERROR, "cannot find dev from inode\n");
+		LEAVE();
+		return -ENXIO;
+	}
+	if (!chardev_get(dev)) {
+		LEAVE();
+		return -ENXIO;
+	}
+	filp->private_data = dev;	/* for other methods */
+	m_dev = dev->m_dev;
+	mdev_req_lock(m_dev);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
+	if (test_bit(HCI_UP, &m_dev->flags)) {
+		atomic_inc(&m_dev->extra_cnt);
+		goto done;
+	}
+#endif
+	if (m_dev->open(m_dev)) {
+		ret = -EIO;
+		goto done;
+	}
+	set_bit(HCI_UP, &m_dev->flags);
+
+done:
+	mdev_req_unlock(m_dev);
+	if (ret)
+		chardev_put(dev);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief release handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_release(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	ENTER();
+	if (!dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
+	if (m_dev && (atomic_dec_if_positive(&m_dev->extra_cnt) >= 0)) {
+		LEAVE();
+		return ret;
+	}
+#endif
+	if (m_dev)
+		ret = dev->m_dev->close(dev->m_dev);
+	filp->private_data = NULL;
+	chardev_put(dev);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief poll handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param wait		pointer to poll_table structure
+ *	@return			mask
+ */
+static unsigned int
+chardev_poll(struct file *filp, poll_table * wait)
+{
+	unsigned int mask;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+
+	m_dev = dev->m_dev;
+	poll_wait(filp, &m_dev->req_wait_q, wait);
+	mask = POLLOUT | POLLWRNORM;
+	if (skb_peek(&m_dev->rx_q))
+		mask |= POLLIN | POLLRDNORM;
+	if (!test_bit(HCI_UP, &(m_dev->flags)))
+		mask |= POLLHUP;
+	PRINTM(INFO, "poll mask=0x%x\n", mask);
+	LEAVE();
+	return mask;
+}
+
+/* File ops for the Char driver */
+const struct file_operations chardev_fops = {
+	.owner = THIS_MODULE,
+	.read = chardev_read,
+	.write = chardev_write,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	.ioctl = chardev_ioctl,
+#else
+	.unlocked_ioctl = chardev_ioctl,
+#endif
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = chardev_ioctl_compat,
+#endif
+	.open = chardev_open,
+	.release = chardev_release,
+	.poll = chardev_poll,
+};
+
+/**
+ *	@brief This function creates the char dev
+ *
+ *	@param dev			A pointer to structure char_dev
+ *  @param char_class	A pointer to class struct
+ *  @param mod_name		A pointer to char
+ *  @param dev_name		A pointer to char
+ *	@return				0--success otherwise failure
+ */
+int
+register_char_dev(struct char_dev *dev, struct class *char_class,
+		  char *mod_name, char *dev_name)
+{
+	int ret = 0, dev_num;
+	unsigned long flags;
+	ENTER();
+	/* create the chrdev region */
+	if (mbtchar_major) {
+		dev_num = MKDEV(mbtchar_major, dev->minor);
+		ret = register_chrdev_region(dev_num, 1, mod_name);
+	} else {
+		PRINTM(INFO, "chardev: no major # yet\n");
+		ret = alloc_chrdev_region((dev_t *) & dev_num, dev->minor, 1,
+					  mod_name);
+	}
+
+	if (ret) {
+		PRINTM(ERROR, "chardev: create chrdev_region failed\n");
+		LEAVE();
+		return ret;
+	}
+	if (!mbtchar_major) {
+		/* Store the allocated dev major # */
+		mbtchar_major = MAJOR(dev_num);
+	}
+	dev->cdev = cdev_alloc();
+	dev->cdev->ops = &chardev_fops;
+	dev->cdev->owner = chardev_fops.owner;
+	dev_num = MKDEV(mbtchar_major, dev->minor);
+
+	if (cdev_add(dev->cdev, dev_num, 1)) {
+		PRINTM(ERROR, "chardev: cdev_add failed\n");
+		ret = -EFAULT;
+		goto free_cdev_region;
+	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+	if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), NULL, dev_name);
+	}
+	if (dev->dev_type == NFC_TYPE) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), NULL, dev_name);
+	}
+	if (dev->dev_type == DEBUG_TYPE) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), NULL, dev_name);
+	}
+#else
+	if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), dev_name);
+	}
+	if (dev->dev_type == NFC_TYPE) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), dev_name);
+	}
+	if (dev->dev_type == DEBUG_TYPE) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), dev_name);
+	}
+#endif
+	PRINTM(INFO, "register char dev=%s\n", dev_name);
+
+	/** modify later */
+
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	list_add_tail(&dev->list, &char_dev_list);
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+
+	LEAVE();
+	return ret;
+free_cdev_region:
+	unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief This function deletes the char dev
+ *
+ *  @param dev			A pointer to structure char_dev
+ *  @param char_class	A pointer to class struct
+ *  @param dev_name		A pointer to char
+ *  @return				0--success otherwise failure
+ */
+int
+unregister_char_dev(struct char_dev *dev, struct class *char_class,
+		    char *dev_name)
+{
+	ENTER();
+	device_destroy(char_class, MKDEV(mbtchar_major, dev->minor));
+	cdev_del(dev->cdev);
+	unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1);
+	PRINTM(INFO, "unregister char dev=%s\n", dev_name);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *	@brief This function cleans module
+ *
+ *  @param char_class	A pointer to class struct
+ *  @return				N/A
+ */
+void
+chardev_cleanup(struct class *char_class)
+{
+	unsigned long flags;
+	struct list_head *p = NULL;
+	struct char_dev *dev = NULL;
+	ENTER();
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	do {
+		dev = NULL;
+		list_for_each(p, &char_dev_list) {
+			dev = list_entry(p, struct char_dev, list);
+			list_del(p);
+			spin_unlock_irqrestore(&char_dev_list_lock, flags);
+			unregister_char_dev(dev, char_class, dev->m_dev->name);
+			kobject_put(&dev->kobj);
+			spin_lock_irqsave(&char_dev_list_lock, flags);
+			break;
+		}
+	} while (dev);
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+	class_destroy(char_class);
+	LEAVE();
+}
+
+/**
+ *	@brief This function cleans module
+ *
+ *  @param m_dev	A pointer to m_dev struct
+ *  @param char_class	A pointer to class struct
+ *  @return			N/A
+ */
+void
+chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class)
+{
+	unsigned long flags;
+	struct list_head *p = NULL;
+	struct char_dev *dev = NULL;
+	ENTER();
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	list_for_each(p, &char_dev_list) {
+		dev = list_entry(p, struct char_dev, list);
+		if (dev->minor == m_dev->index) {
+			list_del(p);
+			spin_unlock_irqrestore(&char_dev_list_lock, flags);
+			dev->m_dev = NULL;
+			unregister_char_dev(dev, char_class, m_dev->name);
+			kobject_put(&dev->kobj);
+			spin_lock_irqsave(&char_dev_list_lock, flags);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+	LEAVE();
+}
diff --git a/bt_sd8897/bt/mbt_char.h b/bt_sd8897/bt/mbt_char.h
new file mode 100644
index 0000000..9997a38
--- /dev/null
+++ b/bt_sd8897/bt/mbt_char.h
@@ -0,0 +1,72 @@
+/** @file mbt_char.h
+  *
+  * @brief This file contains mbtchar driver specific defines etc
+  *
+  * Copyright (C) 2010-2018, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+#ifndef __MBT_CHAR_H__
+#define __MBT_CHAR_H__
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+/** Define ioctl */
+#define MBTCHAR_IOCTL_RELEASE       _IO('M', 1)
+#define MBTCHAR_IOCTL_QUERY_TYPE    _IO('M', 2)
+#ifdef BLE_WAKEUP
+#define MBTCHAR_IOCTL_BLE_WAKEUP_PARAM _IO('M', 4)
+#endif
+
+#define MBTCHAR_MAJOR_NUM            (0)
+
+/** Interface specific macros */
+#define MBTCHAR_MINOR_BASE           (0)
+#define FMCHAR_MINOR_BASE            (10)
+#define NFCCHAR_MINOR_BASE           (20)
+#define DEBUGCHAR_MINOR_BASE         (30)
+
+/** Declaration of char_dev struct */
+struct char_dev {
+	struct list_head list;
+	int minor;
+	int dev_type;
+	struct cdev *cdev;
+	struct m_dev *m_dev;
+	struct kobject kobj;
+};
+
+/** Changes permissions of the dev */
+int mbtchar_chmod(char *name, mode_t mode);
+
+/** Changes ownership of the dev */
+int mbtchar_chown(char *name, uid_t user, gid_t group);
+
+/**	This function creates the char dev */
+int register_char_dev(struct char_dev *dev, struct class *char_class,
+		      char *mod_name, char *dev_name);
+
+/**	This function deletes the char dev */
+int unregister_char_dev(struct char_dev *dev, struct class *char_class,
+			char *dev_name);
+
+/**	This function cleans module */
+void chardev_cleanup(struct class *char_class);
+
+/**	This function cleans module */
+void chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class);
+
+#endif /*__MBT_CHAR_H__*/
diff --git a/bt_sd8897/config/bt_cal_data.conf b/bt_sd8897/config/bt_cal_data.conf
new file mode 100644
index 0000000..13aac4e
--- /dev/null
+++ b/bt_sd8897/config/bt_cal_data.conf
@@ -0,0 +1,5 @@
+00 1C 01 37 FF FF FF FF 02 04 7F 01
+CE BA 00 00 00 2D C6 C0 00 00 00 00
+00 F0 00 00
+
+
diff --git a/bt_sd8897/config/bt_init_cfg.conf b/bt_sd8897/config/bt_init_cfg.conf
new file mode 100644
index 0000000..deb5a63
--- /dev/null
+++ b/bt_sd8897/config/bt_init_cfg.conf
@@ -0,0 +1,11 @@
+# File: init_cfg_.conf
+
+# MAC address (interface: address)
+#ifdef BT_HCI_BLUEZ
+mac_addr= hci0: 00:50:43:20:43:21
+#else
+mac_addr= mbtchar0: 00:50:43:20:43:21
+#endif
+# Register (type, offset, value)
+bt_reg=2,0x00000026,0x0040
+
diff --git a/bt_sd8987/Makefile b/bt_sd8987/Makefile
new file mode 100644
index 0000000..40037dd
--- /dev/null
+++ b/bt_sd8987/Makefile
@@ -0,0 +1,188 @@
+# File: Makefile
+# Copyright (C) 2007-2018, Marvell International Ltd.
+#
+
+CC=		$(CROSS_COMPILE)clang
+LD=		$(CROSS_COMPILE)ld.lld
+
+BACKUP=		/root/backup
+YMD=		`date +%Y%m%d%H%M`
+
+#############################################################################
+# Configuration Options
+#############################################################################
+
+# Debug Option
+# DEBUG LEVEL n/1/2:
+# n: NO DEBUG
+# 1: PRINTM(MSG,...), PRINTM(FATAL,...), PRINTM(WARN,...) and PRINTM(INFO,...)
+# 2: All PRINTM()
+CONFIG_DEBUG=1
+
+
+# SDIO suspend/resume
+CONFIG_SDIO_SUSPEND_RESUME=y
+
+
+CONFIG_BLE_WAKEUP=y
+
+#############################################################################
+# Select Platform Tools
+#############################################################################
+
+MODEXT = ko
+
+ifeq ($(CONFIG_64BIT), y)
+	EXTRA_CFLAGS += -DMBT_64BIT
+endif
+
+ifeq ($(CONFIG_T50), y)
+        EXTRA_CFLAGS += -DT50
+        EXTRA_CFLAGS += -DT40
+        EXTRA_CFLAGS += -DT3T
+endif
+
+ifeq ($(CONFIG_BLE_WAKEUP), y)
+        EXTRA_CFLAGS += -DBLE_WAKEUP
+endif
+
+
+
+
+
+KERNELDIR?=KBUILD_SRC
+
+EXTRA_CFLAGS += -I$(KERNELDIR)/include
+
+EXTRA_CFLAGS += -I$(M)/../mbtchar_src
+EXTRA_CFLAGS += -I$(M)/bt
+LD += -S
+
+#ifdef SD8xxx
+BINDIR = ../bin_sd8xxx_btchar
+#endif
+BINDIR = ../bin_sd8987_btchar
+
+
+#############################################################################
+# Compiler Flags
+#############################################################################
+	EXTRA_CFLAGS += -DFPNUM='"36"'
+
+ifeq ($(CONFIG_DEBUG),1)
+	EXTRA_CFLAGS += -DDEBUG_LEVEL1
+endif
+
+ifeq ($(CONFIG_DEBUG),2)
+	EXTRA_CFLAGS += -DDEBUG_LEVEL1
+	EXTRA_CFLAGS += -DDEBUG_LEVEL2
+	DBG=	-dbg
+endif
+
+ifeq ($(CONFIG_SDIO_SUSPEND_RESUME),y)
+	EXTRA_CFLAGS += -DSDIO_SUSPEND_RESUME
+endif
+
+#############################################################################
+# Make Targets
+#############################################################################
+
+BT_CHAR_OBJS = bt_char/bt_main.o bt_char/bt_sdiommc.o bt_char/bt_proc.o bt_char/mbt_char.o
+BT_CHAR_OBJS += bt_char/bt_init.o
+
+BT_BLOCK_OBJS = bt/bt_main.o bt/bt_sdiommc.o bt/bt_proc.o bt/mbt_char.o
+BT_BLOCK_OBJS += bt/bt_init.o
+
+ifneq ($(KERNELRELEASE),)
+
+ifneq ($(CONFIG_BERLIN_SDIO_BT_8987_CHAR_DRV),)
+
+BTOBJS = bt_char/bt_main.o bt_char/bt_sdiommc.o bt_char/bt_proc.o bt_char/mbt_char.o
+BTOBJS += bt_char/bt_init.o
+obj-$(CONFIG_BERLIN_SDIO_BT_8987_CHAR_DRV) := bt8xxx.o
+
+else
+
+BTOBJS = bt/bt_main.o bt/bt_sdiommc.o bt/bt_proc.o bt/mbt_char.o
+BTOBJS += bt/bt_init.o
+obj-m := bt8xxx.o
+
+endif
+
+obj-$(MODULE_LINK) := bt8xxx.o
+bt8xxx-objs := $(BTOBJS)
+
+
+
+# Otherwise we were called directly from the command line; invoke the kernel build system.
+else
+default:
+	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules
+endif
+
+###############################################################
+
+export		CC LD EXTRA_CFLAGS KERNELDIR
+
+.PHONY: app/fm_app clean distclean
+
+app/fm_app:
+	$(MAKE) -C  $@
+
+echo:
+
+build:		echo default
+
+	@if [ ! -d $(BINDIR) ]; then \
+		mkdir $(BINDIR); \
+	fi
+
+ifeq ($(CONFIG_MULTI_INTERFACE), y)
+	cp -f mbt8xxx_sdio.$(MODEXT) $(BINDIR)/mbt8987_sdio$(DBG).$(MODEXT)
+else
+	cp -f mbt8xxx.$(MODEXT) $(BINDIR)/mbt8987$(DBG).$(MODEXT)
+endif
+	cp -r config $(BINDIR)
+
+
+
+
+
+	cp -f README $(BINDIR)
+
+	$(MAKE) -C app/fm_app $@ INSTALLDIR=$(BINDIR);
+	cp -f app/fm_app/fmapp $(BINDIR);
+
+clean:
+	-find . -name "*.o" -exec rm {} \;
+	-find . -name "*.ko" -exec rm {} \;
+	-find . -name ".*.cmd" -exec rm {} \;
+	-find . -name "*.mod.c" -exec rm {} \;
+	-find . -name "*.symvers" -exec rm {} \;
+	-find . -name "modules.order" -exec rm {} \;
+	-find . -name ".*.dwo" -exec rm {} \;
+	-find . -name "*dwo" -exec rm {} \;
+	-rm -rf .tmp_versions
+	$(MAKE) -C app/fm_app $@
+
+install: default
+
+distclean:
+	-find . -name "*.o" -exec rm {} \;
+	-find . -name "*.orig" -exec rm {} \;
+	-find . -name "*.swp" -exec rm {} \;
+	-find . -name "*.*~" -exec rm {} \;
+	-find . -name "*~" -exec rm {} \;
+	-find . -name "*.d" -exec rm {} \;
+	-find . -name "*.a" -exec rm {} \;
+	-find . -name "tags" -exec rm {} \;
+	-find . -name ".*" -exec rm -rf 2> /dev/null \;
+	-find . -name "*.ko" -exec rm {} \;
+	-find . -name ".*.cmd" -exec rm {} \;
+	-find . -name "*.mod.c" -exec rm {} \;
+	-find . -name ".*.dwo" -exec rm {} \;
+	-find . -name "*dwo" -exec rm {} \;
+	-rm -rf .tmp_versions
+	$(MAKE) -C app/fm_app $@
+# End of file;
+
diff --git a/bt_sd8987/bt/bt_drv.h b/bt_sd8987/bt/bt_drv.h
new file mode 100644
index 0000000..aa6fa19
--- /dev/null
+++ b/bt_sd8987/bt/bt_drv.h
@@ -0,0 +1,885 @@
+/** @file bt_drv.h
+ *  @brief This header file contains global constant/enum definitions,
+ *  global variable declaration.
+ *
+ *  Copyright (C) 2007-2019, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _BT_DRV_H_
+#define _BT_DRV_H_
+
+#include <linux/version.h>
+#include <linux/kthread.h>
+#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
+
+#include "hci_wrapper.h"
+
+/** MAX adapter BT driver supported */
+#define MAX_BT_ADAPTER    3
+
+#ifndef BIT
+/** BIT definition */
+#define BIT(x) (1UL << (x))
+#endif
+
+#ifdef MBT_64BIT
+typedef u64 t_ptr;
+#else
+typedef u32 t_ptr;
+#endif
+
+/** max number of adapter supported */
+#define MAX_BT_ADAPTER      3
+/** Define drv_mode bit */
+#define DRV_MODE_BT         BIT(0)
+
+/** Define devFeature bit */
+#define DEV_FEATURE_BT     BIT(0)
+#define DEV_FEATURE_BTAMP     BIT(1)
+#define DEV_FEATURE_BLE     BIT(2)
+
+/** Define maximum number of radio func supported */
+#define MAX_RADIO_FUNC     3
+
+/** MAC address print format */
+#ifndef MACSTR
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+#endif
+
+/** MAC address print arguments */
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#endif
+
+/** Debug level : Message */
+#define	DBG_MSG			BIT(0)
+/** Debug level : Fatal */
+#define DBG_FATAL		BIT(1)
+/** Debug level : Error */
+#define DBG_ERROR		BIT(2)
+/** Debug level : Data */
+#define DBG_DATA		BIT(3)
+/** Debug level : Command */
+#define DBG_CMD			BIT(4)
+/** Debug level : Event */
+#define DBG_EVENT		BIT(5)
+/** Debug level : Interrupt */
+#define DBG_INTR		BIT(6)
+
+/** Debug entry : Data dump */
+#define DBG_DAT_D		BIT(16)
+/** Debug entry : Data dump */
+#define DBG_CMD_D		BIT(17)
+
+/** Debug level : Entry */
+#define DBG_ENTRY		BIT(28)
+/** Debug level : Warning */
+#define DBG_WARN		BIT(29)
+/** Debug level : Informative */
+#define DBG_INFO		BIT(30)
+
+#ifdef	DEBUG_LEVEL1
+extern u32 mbt_drvdbg;
+
+#ifdef	DEBUG_LEVEL2
+/** Print informative message */
+#define	PRINTM_INFO(msg...)  \
+	do {if (mbt_drvdbg & DBG_INFO)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print warning message */
+#define	PRINTM_WARN(msg...) \
+	do {if (mbt_drvdbg & DBG_WARN)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print entry message */
+#define	PRINTM_ENTRY(msg...) \
+	do {if (mbt_drvdbg & DBG_ENTRY) \
+		printk(KERN_DEBUG msg); } while (0)
+#else
+/** Print informative message */
+#define	PRINTM_INFO(msg...)  do {} while (0)
+/** Print warning message */
+#define	PRINTM_WARN(msg...)  do {} while (0)
+/** Print entry message */
+#define	PRINTM_ENTRY(msg...) do {} while (0)
+#endif /* DEBUG_LEVEL2 */
+
+/** Print interrupt message */
+#define	PRINTM_INTR(msg...)  \
+	do {if (mbt_drvdbg & DBG_INTR)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print event message */
+#define	PRINTM_EVENT(msg...) \
+	do {if (mbt_drvdbg & DBG_EVENT) \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print command message */
+#define	PRINTM_CMD(msg...)   \
+	do {if (mbt_drvdbg & DBG_CMD)   \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print data message */
+#define	PRINTM_DATA(msg...)  \
+	do {if (mbt_drvdbg & DBG_DATA)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print error message */
+#define	PRINTM_ERROR(msg...) \
+	do {if (mbt_drvdbg & DBG_ERROR) \
+		printk(KERN_ERR msg); } while (0)
+/** Print fatal message */
+#define	PRINTM_FATAL(msg...) \
+	do {if (mbt_drvdbg & DBG_FATAL) \
+		printk(KERN_ERR msg); } while (0)
+/** Print message */
+#define	PRINTM_MSG(msg...)   \
+	do {if (mbt_drvdbg & DBG_MSG)   \
+		printk(KERN_ALERT msg); } while (0)
+
+/** Print data dump message */
+#define	PRINTM_DAT_D(msg...)  \
+	do {if (mbt_drvdbg & DBG_DAT_D)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print data dump message */
+#define	PRINTM_CMD_D(msg...)  \
+	do {if (mbt_drvdbg & DBG_CMD_D)  \
+		printk(KERN_DEBUG msg); } while (0)
+
+/** Print message with required level */
+#define	PRINTM(level, msg...) PRINTM_##level(msg)
+
+/** Debug dump buffer length */
+#define DBG_DUMP_BUF_LEN	64
+/** Maximum number of dump per line */
+#define MAX_DUMP_PER_LINE	16
+/** Maximum data dump length */
+#define MAX_DATA_DUMP_LEN	48
+
+/**
+ * @brief Prints buffer data upto provided length
+ *
+ * @param prompt          Char pointer
+ * @param buf			  Buffer
+ * @param len    		  Length
+ *
+ * @return                N/A
+ */
+static inline void
+hexdump(char *prompt, u8 *buf, int len)
+{
+	int i;
+	char dbgdumpbuf[DBG_DUMP_BUF_LEN];
+	char *ptr = dbgdumpbuf;
+
+	printk(KERN_DEBUG "%s: len=%d\n", prompt, len);
+	for (i = 1; i <= len; i++) {
+		ptr += snprintf(ptr, 4, "%02x ", *buf);
+		buf++;
+		if (i % MAX_DUMP_PER_LINE == 0) {
+			*ptr = 0;
+			printk(KERN_DEBUG "%s\n", dbgdumpbuf);
+			ptr = dbgdumpbuf;
+		}
+	}
+	if (len % MAX_DUMP_PER_LINE) {
+		*ptr = 0;
+		printk(KERN_DEBUG "%s\n", dbgdumpbuf);
+	}
+}
+
+/** Debug hexdump of debug data */
+#define DBG_HEXDUMP_DAT_D(x, y, z) \
+	do {if (mbt_drvdbg & DBG_DAT_D) \
+		hexdump(x, y, z); } while (0)
+/** Debug hexdump of debug command */
+#define DBG_HEXDUMP_CMD_D(x, y, z) \
+	do {if (mbt_drvdbg & DBG_CMD_D) \
+		hexdump(x, y, z); } while (0)
+
+/** Debug hexdump */
+#define	DBG_HEXDUMP(level, x, y, z)    DBG_HEXDUMP_##level(x, y, z)
+
+/** Mark entry point */
+#define	ENTER()			PRINTM(ENTRY, "Enter: %s, %s:%i\n", __func__, \
+							__FILE__, __LINE__)
+/** Mark exit point */
+#define	LEAVE()			PRINTM(ENTRY, "Leave: %s, %s:%i\n", __func__, \
+							__FILE__, __LINE__)
+#else
+/** Do nothing */
+#define	PRINTM(level, msg...) do {} while (0)
+/** Do nothing */
+#define DBG_HEXDUMP(level, x, y, z)    do {} while (0)
+/** Do nothing */
+#define	ENTER()  do {} while (0)
+/** Do nothing */
+#define	LEAVE()  do {} while (0)
+#endif /* DEBUG_LEVEL1 */
+
+/** Bluetooth upload size */
+#define	BT_UPLD_SIZE				2312
+/** Bluetooth status success */
+#define BT_STATUS_SUCCESS			(0)
+/** Bluetooth status pending */
+#define BT_STATUS_PENDING           (1)
+/** Bluetooth status failure */
+#define BT_STATUS_FAILURE			(-1)
+
+#ifndef	TRUE
+/** True value */
+#define TRUE			1
+#endif
+#ifndef	FALSE
+/** False value */
+#define	FALSE			0
+#endif
+
+/** Set thread state */
+#define OS_SET_THREAD_STATE(x)		set_current_state(x)
+/** Time to wait until Host Sleep state change in millisecond */
+#define WAIT_UNTIL_HS_STATE_CHANGED 2000
+/** Time to wait cmd resp in millisecond */
+#define WAIT_UNTIL_CMD_RESP	    5000
+
+/** Sleep until a condition gets true or a timeout elapses */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+#define os_wait_interruptible_timeout(waitq, cond, timeout) \
+	interruptible_sleep_on_timeout(&waitq, ((timeout) * HZ / 1000))
+#else
+#define os_wait_interruptible_timeout(waitq, cond, timeout) \
+	wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000))
+#endif
+
+#define os_wait_timeout(waitq, cond, timeout) \
+         wait_event_timeout(waitq, cond, ((timeout) * HZ / 1000))
+
+/** bt thread structure */
+typedef struct {
+	/** Task */
+	struct task_struct *task;
+	/** Queue */
+	wait_queue_head_t waitQ;
+	/** PID */
+	pid_t pid;
+	/** Private structure */
+	void *priv;
+} bt_thread;
+
+/**
+ * @brief Activates bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline void
+bt_activate_thread(bt_thread *thr)
+{
+	/** Initialize the wait queue */
+	init_waitqueue_head(&thr->waitQ);
+
+	/** Record the thread pid */
+	thr->pid = current->pid;
+}
+
+/**
+ * @brief De-activates bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline void
+bt_deactivate_thread(bt_thread *thr)
+{
+	thr->pid = 0;
+	return;
+}
+
+/**
+ * @brief Creates bt thread
+ *
+ * @param btfunc          Function pointer
+ * @param thr			  A pointer to bt_thread structure
+ * @param name    		  Char pointer
+ *
+ * @return                N/A
+ */
+static inline void
+bt_create_thread(int (*btfunc) (void *), bt_thread *thr, char *name)
+{
+	thr->task = kthread_run(btfunc, thr, "%s", name);
+}
+
+/**
+ * @brief Delete bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline int
+bt_terminate_thread(bt_thread *thr)
+{
+	/* Check if the thread is active or not */
+	if (!thr->pid)
+		return -1;
+
+	kthread_stop(thr->task);
+	return 0;
+}
+
+/**
+ * @brief  Set scheduled timeout
+ *
+ * @param millisec		 Time unit in ms
+ *
+ * @return                N/A
+ */
+static inline void
+os_sched_timeout(u32 millisec)
+{
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	schedule_timeout((millisec * HZ) / 1000);
+}
+
+#ifndef __ATTRIB_ALIGN__
+#define __ATTRIB_ALIGN__ __attribute__((aligned(4)))
+#endif
+
+#ifndef __ATTRIB_PACK__
+#define __ATTRIB_PACK__ __attribute__((packed))
+#endif
+
+/** Data structure for the Marvell Bluetooth device */
+typedef struct _bt_dev {
+	/** device name */
+	char name[DEV_NAME_LEN];
+	/** card pointer */
+	void *card;
+	/** IO port */
+	u32 ioport;
+	/** m_dev structure */
+	struct m_dev m_dev[MAX_RADIO_FUNC];
+
+	/** Tx download ready flag */
+	u8 tx_dnld_rdy;
+	/** Function */
+	u8 fn;
+	/** Rx unit */
+	u8 rx_unit;
+	/** Power Save mode : Timeout configuration */
+	u16 idle_timeout;
+	/** Power Save mode */
+	u8 psmode;
+	/** Power Save command */
+	u8 pscmd;
+	/** Host Sleep mode */
+	u8 hsmode;
+	/** Host Sleep command */
+	u8 hscmd;
+	/** Low byte is gap, high byte is GPIO */
+	u16 gpio_gap;
+	/** Host Sleep configuration command */
+	u8 hscfgcmd;
+	/** Host Send Cmd Flag		 */
+	u8 sendcmdflag;
+	/** opcode for Send Cmd */
+	u16 send_cmd_opcode;
+	/** Device Type			*/
+	u8 devType;
+	/** Device Features    */
+	u8 devFeature;
+	/** cmd52 function */
+	u8 cmd52_func;
+	/** cmd52 register */
+	u8 cmd52_reg;
+	/** cmd52 value */
+	u8 cmd52_val;
+	/** SDIO pull control command */
+	u8 sdio_pull_ctrl;
+	/** Low 2 bytes is pullUp, high 2 bytes for pull-down */
+	u32 sdio_pull_cfg;
+	/** Test mode command */
+	u8 test_mode;
+} bt_dev_t, *pbt_dev_t;
+
+/** Marvell bt adapter structure */
+typedef struct _bt_adapter {
+	/** Chip revision ID */
+	u8 chip_rev;
+    /** magic val */
+	u8 magic_val;
+	/** Surprise removed flag */
+	u8 SurpriseRemoved;
+	/** IRQ number */
+	int irq;
+	/** Interrupt counter */
+	u32 IntCounter;
+	/** Tx packet queue */
+	struct sk_buff_head tx_queue;
+
+	/** Pointer of fw dump file name */
+	char *fwdump_fname;
+	/** Pending Tx packet queue */
+	struct sk_buff_head pending_queue;
+	/** tx lock flag */
+	u8 tx_lock;
+	/** Power Save mode */
+	u8 psmode;
+	/** Power Save state */
+	u8 ps_state;
+	/** Host Sleep state */
+	u8 hs_state;
+	/** hs skip count */
+	u32 hs_skip;
+	/** suspend_fail flag */
+	u8 suspend_fail;
+	/** suspended flag */
+	u8 is_suspended;
+	/** Number of wakeup tries */
+	u8 WakeupTries;
+	/** Host Sleep wait queue */
+	wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__;
+	/** Host Cmd complet state */
+	u8 cmd_complete;
+	/** indicate using wait event timeout */
+	u8 wait_event_timeout;
+	/** last irq recv */
+	u8 irq_recv;
+	/** last irq processed */
+	u8 irq_done;
+	/** sdio int status */
+	u8 sd_ireg;
+     /** buf allocated for transmit */
+	u8 *tx_buffer;
+    /** buf for transmit */
+	u8 *tx_buf;
+    /** buf allocated for read interrupt status */
+	u8 *hw_regs_buf;
+    /** buf for read interrupt status */
+	u8 *hw_regs;
+	/** tx pending */
+	u32 skb_pending;
+/** Version string buffer length */
+#define MAX_VER_STR_LEN         128
+	/** Driver version */
+	u8 drv_ver[MAX_VER_STR_LEN];
+	/** Number of command timeout */
+	u32 num_cmd_timeout;
+} bt_adapter, *pbt_adapter;
+
+/** Length of prov name */
+#define PROC_NAME_LEN				32
+
+/** Item data structure */
+struct item_data {
+	/** Name */
+	char name[PROC_NAME_LEN];
+	/** Size */
+	u32 size;
+	/** Address */
+	t_ptr addr;
+	/** Offset */
+	u32 offset;
+	/** Flag */
+	u32 flag;
+};
+
+/** Proc private data structure */
+struct proc_private_data {
+	/** Name */
+	char name[PROC_NAME_LEN];
+	/** File flag */
+	u32 fileflag;
+	/** Buffer size */
+	u32 bufsize;
+	/** Number of items */
+	u32 num_items;
+	/** Item data */
+	struct item_data *pdata;
+	/** Private structure */
+	struct _bt_private *pbt;
+	/** File operations */
+	const struct file_operations *fops;
+};
+
+/** Device proc structure */
+struct device_proc {
+	/** Proc directory entry */
+	struct proc_dir_entry *proc_entry;
+	/** num of proc files */
+	u8 num_proc_files;
+	/** pointer to proc_private_data */
+	struct proc_private_data *pfiles;
+};
+
+/** Private structure for the MV device */
+typedef struct _bt_private {
+	/** Bluetooth device */
+	bt_dev_t bt_dev;
+	/** Adapter */
+	bt_adapter *adapter;
+	/** Firmware helper */
+	const struct firmware *fw_helper;
+	/** Firmware */
+	const struct firmware *firmware;
+	/** Init user configure file */
+	const struct firmware *init_user_cfg;
+	/** Init user configure wait queue token */
+	u16 init_user_conf_wait_flag;
+	/** Init user configure file wait queue */
+	wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__;
+	/** Firmware request start time */
+	struct timeval req_fw_time;
+	/** Hotplug device */
+	struct device *hotplug_device;
+	/** thread to service interrupts */
+	bt_thread MainThread;
+	 /** proc data */
+	struct device_proc dev_proc[MAX_RADIO_FUNC];
+	/** Driver lock */
+	spinlock_t driver_lock;
+	/** Driver lock flags */
+	ulong driver_flags;
+	/** Driver reference flags */
+	struct kobject kobj;
+	/** CRC check flag */
+	u16 fw_crc_check;
+	/** Card type */
+	u16 card_type;
+	/** sdio device */
+	const struct sdio_device *psdio_device;
+	u8 fw_reload;
+    /** fw dump state */
+	u8 fw_dump;
+} bt_private, *pbt_private;
+
+/** Disable interrupt */
+#define OS_INT_DISABLE	spin_lock_irqsave(&priv->driver_lock, \
+						priv->driver_flags)
+/** Enable interrupt */
+#define	OS_INT_RESTORE	spin_unlock_irqrestore(&priv->driver_lock, \
+						priv->driver_flags)
+
+#ifndef HCI_BT_AMP
+/** BT_AMP flag for device type */
+#define  HCI_BT_AMP		0x80
+#endif
+
+/** Device type of BT */
+#define DEV_TYPE_BT		0x00
+/** Device type of AMP */
+#define DEV_TYPE_AMP		0x01
+
+/** Marvell vendor packet */
+#define MRVL_VENDOR_PKT			0xFE
+
+/** Bluetooth command : Get FW Version */
+#define BT_CMD_GET_FW_VERSION       0x0F
+/** Bluetooth command : Sleep mode */
+#define BT_CMD_AUTO_SLEEP_MODE		0x23
+/** Bluetooth command : Host Sleep configuration */
+#define BT_CMD_HOST_SLEEP_CONFIG	0x59
+/** Bluetooth command : Host Sleep enable */
+#define BT_CMD_HOST_SLEEP_ENABLE	0x5A
+/** Bluetooth command : Module Configuration request */
+#define BT_CMD_MODULE_CFG_REQ		0x5B
+/** Bluetooth command : PMIC Configure */
+#define BT_CMD_PMIC_CONFIGURE           0x7D
+
+/** Bluetooth command : SDIO pull up down configuration request */
+#define BT_CMD_SDIO_PULL_CFG_REQ	0x69
+/** Bluetooth command : Set Evt Filter Command */
+#define BT_CMD_SET_EVT_FILTER		0x05
+/** Bluetooth command : Enable Write Scan Command */
+#define BT_CMD_ENABLE_WRITE_SCAN	0x1A
+/** Bluetooth command : Enable Device under test mode */
+#define BT_CMD_ENABLE_DEVICE_TESTMODE	0x03
+/** Sub Command: Module Bring Up Request */
+#define MODULE_BRINGUP_REQ		0xF1
+/** Sub Command: Module Shut Down Request */
+#define MODULE_SHUTDOWN_REQ		0xF2
+/** Module already up */
+#define MODULE_CFG_RESP_ALREADY_UP      0x0c
+/** Sub Command: Host Interface Control Request */
+#define MODULE_INTERFACE_CTRL_REQ	0xF5
+
+/** Bluetooth event : Power State */
+#define BT_EVENT_POWER_STATE		0x20
+
+/** Bluetooth Power State : Enable */
+#define BT_PS_ENABLE			0x02
+/** Bluetooth Power State : Disable */
+#define BT_PS_DISABLE			0x03
+/** Bluetooth Power State : Sleep */
+#define BT_PS_SLEEP			0x01
+/** Bluetooth Power State : Awake */
+#define BT_PS_AWAKE			0x02
+
+/** Vendor OGF */
+#define VENDOR_OGF				0x3F
+/** OGF for reset */
+#define RESET_OGF		0x03
+/** Bluetooth command : Reset */
+#define BT_CMD_RESET	0x03
+
+/** Host Sleep activated */
+#define HS_ACTIVATED			0x01
+/** Host Sleep deactivated */
+#define HS_DEACTIVATED			0x00
+
+/** Power Save sleep */
+#define PS_SLEEP			0x01
+/** Power Save awake */
+#define PS_AWAKE			0x00
+
+/** bt header length */
+#define BT_HEADER_LEN			4
+
+#ifndef MAX
+/** Return maximum of two */
+#define MAX(a, b)		((a) > (b) ? (a) : (b))
+#endif
+
+/** This is for firmware specific length */
+#define EXTRA_LEN	36
+
+/** Command buffer size for Marvell driver */
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+
+/** Bluetooth Rx packet buffer size for Marvell driver */
+#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
+	(HCI_MAX_FRAME_SIZE + EXTRA_LEN)
+
+/** Buffer size to allocate */
+#define ALLOC_BUF_SIZE	(((MAX(MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
+			MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+			+ SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE)
+
+/** Request FW timeout in second */
+#define REQUEST_FW_TIMEOUT		30
+
+/** The number of times to try when polling for status bits */
+#define MAX_POLL_TRIES			100
+
+/** The number of times to try when waiting for downloaded firmware to
+    become active when multiple interface is present */
+#define MAX_MULTI_INTERFACE_POLL_TRIES  150
+
+/** The number of times to try when waiting for downloaded firmware to
+     become active. (polling the scratch register). */
+#define MAX_FIRMWARE_POLL_TRIES		100
+
+/** default idle time */
+#define DEFAULT_IDLE_TIME           1000
+
+#define BT_CMD_HEADER_SIZE    3
+
+#define BT_CMD_DATA_LEN    128
+#define BT_EVT_DATA_LEN    8
+
+/** BT command structure */
+typedef struct _BT_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** Data */
+	u8 data[BT_CMD_DATA_LEN];
+} __ATTRIB_PACK__ BT_CMD;
+
+/** BT event structure */
+typedef struct _BT_EVENT {
+	/** Event Counter */
+	u8 EC;
+	/** Length */
+	u8 length;
+	/** Data */
+	u8 data[BT_EVT_DATA_LEN];
+} BT_EVENT;
+
+#if defined(SDIO_SUSPEND_RESUME)
+#define DEF_GPIO_GAP        0xffff
+#endif
+
+/** This function verify the received event pkt */
+int check_evtpkt(bt_private *priv, struct sk_buff *skb);
+
+/* Prototype of global function */
+/** This function gets the priv reference */
+struct kobject *bt_priv_get(bt_private *priv);
+/** This function release the priv reference */
+void bt_priv_put(bt_private *priv);
+/** This function adds the card */
+bt_private *bt_add_card(void *card);
+/** This function removes the card */
+int bt_remove_card(void *card);
+/** This function handles the interrupt */
+void bt_interrupt(struct m_dev *m_dev);
+
+/** This function creates proc interface directory structure */
+int bt_root_proc_init(void);
+/** This function removes proc interface directory structure */
+int bt_root_proc_remove(void);
+/** This function initializes proc entry */
+int bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq);
+/** This function removes proc interface */
+void bt_proc_remove(bt_private *priv);
+
+/** This function process the received event */
+int bt_process_event(bt_private *priv, struct sk_buff *skb);
+/** This function enables host sleep */
+int bt_enable_hs(bt_private *priv, bool is_shutdown);
+/** This function used to send command to firmware */
+int bt_prepare_command(bt_private *priv);
+/** This function frees the structure of adapter */
+void bt_free_adapter(bt_private *priv);
+/** This function handle the receive packet */
+void bt_recv_frame(bt_private *priv, struct sk_buff *skb);
+void bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len);
+
+/** clean up m_devs */
+void clean_up_m_devs(bt_private *priv);
+/** bt driver call this function to register to bus driver */
+int *sbi_register(void);
+/** bt driver call this function to unregister to bus driver */
+void sbi_unregister(void);
+/** bt driver calls this function to register the device  */
+int sbi_register_dev(bt_private *priv);
+/** bt driver calls this function to unregister the device */
+int sbi_unregister_dev(bt_private *priv);
+/** This function initializes firmware */
+int sbi_download_fw(bt_private *priv);
+/** Configures hardware to quit deep sleep state */
+int sbi_wakeup_firmware(bt_private *priv);
+/** Module configuration and register device */
+int sbi_register_conf_dpc(bt_private *priv);
+
+/** This function is used to send the data/cmd to hardware */
+int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb);
+/** This function reads the current interrupt status register */
+int sbi_get_int_status(bt_private *priv);
+/** This function enables the host interrupts */
+int sbi_enable_host_int(bt_private *priv);
+/** This function disables the host interrupts */
+int sbi_disable_host_int(bt_private *priv);
+
+/** bt fw reload flag */
+extern int bt_fw_reload;
+/** driver initial the fw reset */
+#define FW_RELOAD_SDIO_INBAND_RESET   1
+/** out band reset trigger reset, no interface re-emulation */
+#define FW_RELOAD_NO_EMULATION  2
+/** out band reset with interface re-emulation */
+#define FW_RELOAD_WITH_EMULATION 3
+/** This function reload firmware */
+void bt_request_fw_reload(bt_private *priv, int mode);
+#define MAX_TX_BUF_SIZE     2312
+/** This function downloads firmware image to the card */
+int sd_download_firmware_w_helper(bt_private *priv);
+void bt_dump_sdio_regs(bt_private *priv);
+#define FW_DUMP_TYPE_ENDED                    0x002
+#define FW_DUMP_TYPE_MEM_ITCM                 0x004
+#define FW_DUMP_TYPE_MEM_DTCM                 0x005
+#define FW_DUMP_TYPE_MEM_SQRAM                0x006
+#define FW_DUMP_TYPE_MEM_IRAM                 0x007
+#define FW_DUMP_TYPE_REG_MAC                  0x009
+#define FW_DUMP_TYPE_REG_CIU                  0x00E
+#define FW_DUMP_TYPE_REG_APU                  0x00F
+#define FW_DUMP_TYPE_REG_ICU                  0x014
+/* dumps the firmware to /var/ or /data/ */
+void bt_dump_firmware_info_v2(bt_private *priv);
+
+/** Max line length allowed in init config file */
+#define MAX_LINE_LEN        256
+/** Max MAC address string length allowed */
+#define MAX_MAC_ADDR_LEN    18
+/** Max register type/offset/value etc. parameter length allowed */
+#define MAX_PARAM_LEN       12
+
+/** Bluetooth command : Mac address configuration */
+#define BT_CMD_CONFIG_MAC_ADDR		0x22
+/** Bluetooth command : Write CSU register */
+#define BT_CMD_CSU_WRITE_REG		0x66
+/** Bluetooth command : Load calibrate data */
+#define BT_CMD_LOAD_CONFIG_DATA     0x61
+/** Bluetooth command : Load calibrate ext data */
+#define BT_CMD_LOAD_CONFIG_DATA_EXT     0x60
+
+/** Bluetooth command : BLE deepsleep */
+#define BT_CMD_BLE_DEEP_SLEEP       0x8b
+
+/** BT_BLE command structure */
+typedef struct _BT_BLE_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** deepsleep flag */
+	u8 deepsleep;
+} __ATTRIB_PACK__ BT_BLE_CMD;
+
+/** BT_CSU command structure */
+typedef struct _BT_CSU_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** reg type */
+	u8 type;
+	/** address */
+	u8 offset[4];
+	/** Data */
+	u8 value[2];
+} __ATTRIB_PACK__ BT_CSU_CMD;
+
+/** This function sets mac address */
+int bt_set_mac_address(bt_private *priv, u8 *mac);
+/** This function writes value to CSU registers */
+int bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value);
+/** BT set user defined init data and param */
+int bt_init_config(bt_private *priv, char *cfg_file);
+/** BT PMIC Configure command */
+int bt_pmic_configure(bt_private *priv);
+/** This function load the calibrate data */
+int bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac);
+/** This function load the calibrate ext data */
+int bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len);
+/** BT set user defined calibration data */
+int bt_cal_config(bt_private *priv, char *cfg_file, char *mac);
+/** BT set user defined calibration ext data */
+int bt_cal_config_ext(bt_private *priv, char *cfg_file);
+int bt_init_mac_address(bt_private *priv, char *mac);
+
+int bt_set_independent_reset(bt_private *priv);
+/** Bluetooth command : Independent reset */
+#define BT_CMD_INDEPENDENT_RESET     0x0D
+
+/** BT HCI command structure */
+typedef struct _BT_HCI_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** cmd type */
+	u8 cmd_type;
+	/** cmd len */
+	u8 cmd_len;
+	/** Data */
+	u8 data[6];
+} __ATTRIB_PACK__ BT_HCI_CMD;
+
+#endif /* _BT_DRV_H_ */
diff --git a/bt_sd8987/bt/bt_init.c b/bt_sd8987/bt/bt_init.c
new file mode 100644
index 0000000..904b66f
--- /dev/null
+++ b/bt_sd8987/bt/bt_init.c
@@ -0,0 +1,752 @@
+/** @file bt_init.c
+  *
+  * @brief This file contains the init functions for BlueTooth
+  * driver.
+  *
+  * Copyright (C) 2011-2019, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/string.h>
+#include <linux/firmware.h>
+
+#include "bt_drv.h"
+
+extern int bt_req_fw_nowait;
+
+#define isxdigit(c)	(('0' <= (c) && (c) <= '9') \
+			 || ('a' <= (c) && (c) <= 'f') \
+			 || ('A' <= (c) && (c) <= 'F'))
+
+#define isdigit(c)	(('0' <= (c) && (c) <= '9'))
+#define isspace(c)  (c <= ' ' && (c == ' ' || (c <= 13 && c >= 9)))
+/**
+ *  @brief Returns hex value of a give character
+ *
+ *  @param chr	Character to be converted
+ *
+ *  @return	The converted character if chr is a valid hex, else 0
+ */
+static int
+bt_hexval(char chr)
+{
+	ENTER();
+
+	if (chr >= '0' && chr <= '9')
+		return chr - '0';
+	if (chr >= 'A' && chr <= 'F')
+		return chr - 'A' + 10;
+	if (chr >= 'a' && chr <= 'f')
+		return chr - 'a' + 10;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Extension of strsep lib command. This function will also take care
+ *	   escape character
+ *
+ *  @param s         A pointer to array of chars to process
+ *  @param delim     The delimiter character to end the string
+ *  @param esc       The escape character to ignore for delimiter
+ *
+ *  @return          Pointer to the separated string if delim found, else NULL
+ */
+static char *
+bt_strsep(char **s, char delim, char esc)
+{
+	char *se = *s, *sb;
+
+	ENTER();
+
+	if (!(*s) || (*se == '\0')) {
+		LEAVE();
+		return NULL;
+	}
+
+	for (sb = *s; *sb != '\0'; ++sb) {
+		if (*sb == esc && *(sb + 1) == esc) {
+			/*
+			 * We get a esc + esc seq then keep the one esc
+			 * and chop off the other esc character
+			 */
+			memmove(sb, sb + 1, strlen(sb));
+			continue;
+		}
+		if (*sb == esc && *(sb + 1) == delim) {
+			/*
+			 * We get a delim + esc seq then keep the delim
+			 * and chop off the esc character
+			 */
+			memmove(sb, sb + 1, strlen(sb));
+			continue;
+		}
+		if (*sb == delim)
+			break;
+	}
+
+	if (*sb == '\0')
+		sb = NULL;
+	else
+		*sb++ = '\0';
+
+	*s = sb;
+
+	LEAVE();
+	return se;
+}
+
+/**
+ *  @brief Returns hex value of a given ascii string
+ *
+ *  @param a	String to be converted
+ *
+ *  @return	hex value
+ */
+static int
+bt_atox(const char *a)
+{
+	int i = 0;
+	ENTER();
+	while (isxdigit(*a))
+		i = i * 16 + bt_hexval(*a++);
+
+	LEAVE();
+	return i;
+}
+
+/**
+ *  @brief Converts mac address from string to t_u8 buffer.
+ *
+ *  @param mac_addr The buffer to store the mac address in.
+ *  @param buf      The source of mac address which is a string.
+ *
+ *  @return	N/A
+ */
+static void
+bt_mac2u8(u8 *mac_addr, char *buf)
+{
+	char *begin, *end, *mac_buff;
+	int i;
+
+	ENTER();
+
+	if (!buf) {
+		LEAVE();
+		return;
+	}
+
+	mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL);
+	if (!mac_buff) {
+		LEAVE();
+		return;
+	}
+	memcpy(mac_buff, buf, strlen(buf));
+
+	begin = mac_buff;
+	for (i = 0; i < ETH_ALEN; ++i) {
+		end = bt_strsep(&begin, ':', '/');
+		if (end)
+			mac_addr[i] = bt_atox(end);
+	}
+
+	kfree(mac_buff);
+	LEAVE();
+}
+
+/**
+ *  @brief Returns integer value of a given ascii string
+ *
+ *  @param data    Converted data to be returned
+ *  @param a       String to be converted
+ *
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_atoi(int *data, char *a)
+{
+	int i, val = 0, len;
+
+	ENTER();
+
+	len = strlen(a);
+	if (!strncmp(a, "0x", 2)) {
+		a = a + 2;
+		len -= 2;
+		*data = bt_atox(a);
+		return BT_STATUS_SUCCESS;
+	}
+	for (i = 0; i < len; i++) {
+		if (isdigit(a[i])) {
+			val = val * 10 + (a[i] - '0');
+		} else {
+			PRINTM(ERROR, "Invalid char %c in string %s\n", a[i],
+			       a);
+			return BT_STATUS_FAILURE;
+		}
+	}
+	*data = val;
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief parse cal-data
+ *
+ *  @param src      a pointer to cal-data string
+ *  @param len      len of cal-data
+ *  @param dst      a pointer to return cal-data
+ *  @param dst_size size of dest buffer
+ *
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 *dst_size)
+{
+	const u8 *ptr;
+	u8 *dptr;
+	u32 count = 0;
+	int ret = BT_STATUS_FAILURE;
+
+	ENTER();
+	ptr = src;
+	dptr = dst;
+
+	while ((ptr - src) < len) {
+		if (*ptr && isspace(*ptr)) {
+			ptr++;
+			continue;
+		}
+
+		if (isxdigit(*ptr)) {
+			if ((dptr - dst) >= *dst_size) {
+				PRINTM(ERROR, "cal_file size too big!!!\n");
+				goto done;
+			}
+			*dptr++ = bt_atox((const char *)ptr);
+			ptr += 2;
+			count++;
+		} else {
+			ptr++;
+		}
+	}
+	if (dptr == dst) {
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	*dst_size = count;
+	ret = BT_STATUS_SUCCESS;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT get one line data from ASCII format data
+ *
+ *    @param data         Source data
+ *    @param size         Source data length
+ *    @param line_pos     Destination data
+ *    @return             -1 or length of the line
+ */
+int
+parse_cfg_get_line(u8 *data, u32 size, u8 *line_pos)
+{
+	static s32 pos;
+	u8 *src, *dest;
+
+	if (pos >= size) {	/* reach the end */
+		pos = 0;	/* Reset position for rfkill */
+		return -1;
+	}
+	memset(line_pos, 0, MAX_LINE_LEN);
+	src = data + pos;
+	dest = line_pos;
+
+	while ((dest - line_pos < MAX_LINE_LEN - 1) && pos < size &&
+	       *src != '\x0A' && *src != '\0') {
+		if (*src != ' ' && *src != '\t')	/* parse space */
+			*dest++ = *src++;
+		else
+			src++;
+		pos++;
+	}
+	*dest = '\0';
+	/* parse new line */
+	pos++;
+	return strlen((const char *)line_pos);
+}
+
+/**
+ *    @brief BT parse ASCII format data to MAC address
+ *
+ *    @param priv          BT private handle
+ *    @param data          Source data
+ *    @param size          data length
+ *    @return              BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_init_cfg(bt_private *priv, u8 *data, u32 size)
+{
+	u8 *pos;
+	u8 *intf_s, *intf_e;
+	u8 s[MAX_LINE_LEN];	/* 1 line data */
+	u32 line_len;
+	char dev_name[MAX_PARAM_LEN];
+	u8 buf[MAX_PARAM_LEN];
+	u8 bt_addr[MAX_MAC_ADDR_LEN];
+	u8 bt_mac[ETH_ALEN];
+	int setting = 0;
+	u8 type = 0;
+	u16 value = 0;
+	u32 offset = 0;
+	int ret = BT_STATUS_FAILURE;
+
+	memset(dev_name, 0, sizeof(dev_name));
+	memset(bt_addr, 0, sizeof(bt_addr));
+	memset(bt_mac, 0, sizeof(bt_mac));
+
+	while ((line_len = parse_cfg_get_line(data, size, s)) != -1) {
+		pos = s;
+		while (*pos == ' ' || *pos == '\t')
+			pos++;
+
+		if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') ||
+		    *pos == '\n' || *pos == '\0')
+			continue;	/* Need n't process this line */
+
+		/* Process MAC addr */
+		if (strncmp((char *)pos, "mac_addr", 8) == 0) {
+			intf_s = (u8 *)strchr((const char *)pos, '=');
+			if (intf_s != NULL)
+				intf_e = (u8 *)strchr((const char *)intf_s,
+						      ':');
+			else
+				intf_e = NULL;
+			if (intf_s != NULL && intf_e != NULL) {
+				if ((intf_e - intf_s) > MAX_PARAM_LEN) {
+					PRINTM(ERROR,
+					       "BT: Too long interface name %d\n",
+					       __LINE__);
+					goto done;
+				}
+				strncpy(dev_name, (const char *)intf_s + 1,
+					intf_e - intf_s - 1);
+				dev_name[intf_e - intf_s - 1] = '\0';
+				strncpy((char *)bt_addr,
+					(const char *)intf_e + 1,
+					MAX_MAC_ADDR_LEN - 1);
+				bt_addr[MAX_MAC_ADDR_LEN - 1] = '\0';
+				/* Convert MAC format */
+				bt_mac2u8(bt_mac, (char *)bt_addr);
+				PRINTM(CMD,
+				       "HCI: %s new BT Address " MACSTR "\n",
+				       dev_name, MAC2STR(bt_mac));
+				if (BT_STATUS_SUCCESS !=
+				    bt_set_mac_address(priv, bt_mac)) {
+					PRINTM(FATAL,
+					       "BT: Fail to set mac address\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+		}
+		/* Process REG value */
+		else if (strncmp((char *)pos, "bt_reg", 6) == 0) {
+			intf_s = (u8 *)strchr((const char *)pos, '=');
+			if (intf_s != NULL)
+				intf_e = (u8 *)strchr((const char *)intf_s,
+						      ',');
+			else
+				intf_e = NULL;
+			if (intf_s != NULL && intf_e != NULL) {
+				/* Copy type */
+				memset(buf, 0, sizeof(buf));
+				strncpy((char *)buf, (const char *)intf_s + 1,
+					1);
+				buf[1] = '\0';
+				if (0 == bt_atoi(&setting, (char *)buf))
+					type = (u8)setting;
+				else {
+					PRINTM(ERROR,
+					       "BT: Fail to parse reg type\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+			intf_s = intf_e + 1;
+			intf_e = (u8 *)strchr((const char *)intf_s, ',');
+			if (intf_e != NULL) {
+				if ((intf_e - intf_s) >= MAX_PARAM_LEN) {
+					PRINTM(ERROR,
+					       "BT: Regsier offset is too long %d\n",
+					       __LINE__);
+					goto done;
+				}
+				/* Copy offset */
+				memset(buf, 0, sizeof(buf));
+				strncpy((char *)buf, (const char *)intf_s,
+					intf_e - intf_s);
+				buf[intf_e - intf_s] = '\0';
+				if (0 == bt_atoi(&setting, (char *)buf))
+					offset = (u32)setting;
+				else {
+					PRINTM(ERROR,
+					       "BT: Fail to parse reg offset\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+			intf_s = intf_e + 1;
+			if ((strlen((const char *)intf_s) >= MAX_PARAM_LEN)) {
+				PRINTM(ERROR,
+				       "BT: Regsier value is too long %d\n",
+				       __LINE__);
+				goto done;
+			}
+			/* Copy value */
+			memset(buf, 0, sizeof(buf));
+			strncpy((char *)buf, (const char *)intf_s, sizeof(buf));
+			if (0 == bt_atoi(&setting, (char *)buf))
+				value = (u16) setting;
+			else {
+				PRINTM(ERROR, "BT: Fail to parse reg value\n");
+				goto done;
+			}
+
+			PRINTM(CMD,
+			       "BT: Write reg type: %d offset: 0x%x value: 0x%x\n",
+			       type, offset, value);
+			if (BT_STATUS_SUCCESS !=
+			    bt_write_reg(priv, type, offset, value)) {
+				PRINTM(FATAL,
+				       "BT: Write reg failed. type: %d offset: 0x%x value: 0x%x\n",
+				       type, offset, value);
+				goto done;
+			}
+		}
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief BT request init conf firmware callback
+ *        This function is invoked by request_firmware_nowait system call
+ *
+ * @param firmware  A pointer to firmware image
+ * @param context   A pointer to bt_private structure
+ *
+ * @return          N/A
+ */
+static void
+bt_request_init_user_conf_callback(const struct firmware *firmware,
+				   void *context)
+{
+	bt_private *priv = (bt_private *)context;
+
+	ENTER();
+
+	if (!firmware)
+		PRINTM(ERROR, "BT user init config request firmware failed\n");
+
+	priv->init_user_cfg = firmware;
+	priv->init_user_conf_wait_flag = TRUE;
+	wake_up_interruptible(&priv->init_user_conf_wait_q);
+
+	LEAVE();
+	return;
+}
+
+/**
+ *    @brief BT set user defined init data and param
+ *
+ *    @param priv     BT private handle
+ *    @param cfg_file user cofig file
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_init_config(bt_private *priv, char *cfg_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if ((request_firmware(&cfg, cfg_file, priv->hotplug_device)) < 0) {
+		PRINTM(FATAL, "BT: request_firmware() %s failed\n", cfg_file);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (cfg)
+		ret = bt_process_init_cfg(priv, (u8 *)cfg->data, cfg->size);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration data
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param data    a pointer to cal data
+ *    @param size    cal data size
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_cal_cfg(bt_private *priv, u8 *data, u32 size, char *mac)
+{
+	u8 bt_mac[ETH_ALEN];
+	u8 cal_data[32];
+	u8 *mac_data = NULL;
+	u32 cal_data_len;
+	int ret = BT_STATUS_FAILURE;
+	u8 *pcal_data = cal_data;
+
+	memset(bt_mac, 0, sizeof(bt_mac));
+	cal_data_len = sizeof(cal_data);
+	if (BT_STATUS_SUCCESS !=
+	    bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) {
+		goto done;
+	}
+	if (mac != NULL) {
+		/* Convert MAC format */
+		bt_mac2u8(bt_mac, mac);
+		PRINTM(CMD, "HCI: new BT Address " MACSTR "\n",
+		       MAC2STR(bt_mac));
+		mac_data = bt_mac;
+	}
+	if (BT_STATUS_SUCCESS != bt_load_cal_data(priv, pcal_data, mac_data)) {
+		PRINTM(FATAL, "BT: Fail to load calibrate data\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration EXT data
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param data    a pointer to cal data
+ *    @param size    cal data size
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_cal_cfg_ext(bt_private *priv, u8 *data, u32 size)
+{
+	u8 cal_data[128];
+	u32 cal_data_len;
+	int ret = BT_STATUS_FAILURE;
+
+	cal_data_len = sizeof(cal_data);
+	if (BT_STATUS_SUCCESS !=
+	    bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) {
+		goto done;
+	}
+	if (BT_STATUS_SUCCESS !=
+	    bt_load_cal_data_ext(priv, cal_data, cal_data_len)) {
+		PRINTM(FATAL, "BT: Fail to load calibrate data\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration file
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param cal_file calibration file name
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_cal_config(bt_private *priv, char *cal_file, char *mac)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      bt_request_init_user_conf_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#endif
+#endif
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: bt_cal_config() failed, error code = %#x cal_file=%s\n",
+			       ret, cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->init_user_conf_wait_flag = FALSE;
+		wait_event_interruptible(priv->init_user_conf_wait_q,
+					 priv->init_user_conf_wait_flag);
+		cfg = priv->init_user_cfg;
+	} else {
+		if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) <
+		    0) {
+			PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+			       cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cfg)
+		ret = bt_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size, mac);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration EXT file
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param cal_file calibration file name
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_cal_config_ext(bt_private *priv, char *cal_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      bt_request_init_user_conf_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#endif
+#endif
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: bt_cal_config_ext() failed, error code = %#x cal_file=%s\n",
+			       ret, cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->init_user_conf_wait_flag = FALSE;
+		wait_event_interruptible(priv->init_user_conf_wait_q,
+					 priv->init_user_conf_wait_flag);
+		cfg = priv->init_user_cfg;
+	} else {
+		if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) <
+		    0) {
+			PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+			       cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cfg)
+		ret = bt_process_cal_cfg_ext(priv, (u8 *)cfg->data, cfg->size);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT init mac address from bt_mac parametre when insmod
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param bt_mac  mac address buf
+ *    @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_init_mac_address(bt_private *priv, char *mac)
+{
+	u8 bt_mac[ETH_ALEN];
+	int ret = BT_STATUS_FAILURE;
+
+	ENTER();
+	memset(bt_mac, 0, sizeof(bt_mac));
+	bt_mac2u8(bt_mac, mac);
+	PRINTM(CMD, "HCI: New BT Address " MACSTR "\n", MAC2STR(bt_mac));
+	ret = bt_set_mac_address(priv, bt_mac);
+	if (ret != BT_STATUS_SUCCESS)
+		PRINTM(FATAL,
+		       "BT: Fail to set mac address from insmod parametre.\n");
+
+	LEAVE();
+	return ret;
+}
diff --git a/bt_sd8987/bt/bt_main.c b/bt_sd8987/bt/bt_main.c
new file mode 100644
index 0000000..8086c0a
--- /dev/null
+++ b/bt_sd8987/bt/bt_main.c
@@ -0,0 +1,3652 @@
+/** @file bt_main.c
+  *
+  * @brief This file contains the major functions in BlueTooth
+  * driver. It includes init, exit, open, close and main
+  * thread etc..
+  *
+  * Copyright (C) 2007-2019, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+  *
+  */
+/**
+  * @mainpage M-BT Linux Driver
+  *
+  * @section overview_sec Overview
+  *
+  * The M-BT is a Linux reference driver for Marvell Bluetooth chipset.
+  *
+  * @section copyright_sec Copyright
+  *
+  * Copyright (C) 2007-2019, Marvell International Ltd.
+  *
+  */
+#include <linux/module.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
+
+#include <linux/mmc/sdio_func.h>
+
+#include "bt_drv.h"
+#include "mbt_char.h"
+#include "bt_sdio.h"
+
+/** Version */
+#define VERSION "C4X14113"
+
+/** Driver version */
+static char mbt_driver_version[] = "SD8XXX-%s-" VERSION "-(" "FP" FPNUM ")"
+#ifdef DEBUG_LEVEL2
+	"-dbg"
+#endif
+	" ";
+
+/** SD8787 Card */
+#define CARD_SD8787     "SD8787"
+/** SD8777 Card */
+#define CARD_SD8777     "SD8777"
+/** SD8887 Card */
+#define CARD_SD8887     "SD8887"
+/** SD8897 Card */
+#define CARD_SD8897     "SD8897"
+/** SD8797 Card */
+#define CARD_SD8797     "SD8797"
+/** SD8977 Card */
+#define CARD_SD8977     "SD8977"
+/** SD8978 Card */
+#define CARD_SD8978     "SD8978"
+/** SD8997 Card */
+#define CARD_SD8997     "SD8997"
+/** SD8987 Card */
+#define CARD_SD8987     "SD8987"
+
+/** Declare and initialize fw_version */
+static char fw_version[32] = "0.0.0.p0";
+
+#define AID_SYSTEM        1000	/* system server */
+
+#define AID_BLUETOOTH     1002	/* bluetooth subsystem */
+
+#define AID_NET_BT_STACK  3008	/* bluetooth stack */
+
+/** Define module name */
+
+#define MODULE_NAME  "bt_fm_nfc"
+
+/** Declaration of chardev class */
+static struct class *chardev_class;
+
+/** Interface specific variables */
+
+/**
+ * The global variable of a pointer to bt_private
+ * structure variable
+ **/
+bt_private *m_priv[MAX_BT_ADAPTER];
+
+/** Default Driver mode */
+static int drv_mode = (DRV_MODE_BT);
+
+/** fw reload flag */
+int bt_fw_reload;
+/** fw serial download flag */
+int bt_fw_serial = 1;
+
+/** Firmware flag */
+static int fw = 1;
+/** default powermode */
+static int psmode = 1;
+/** default BLE deep sleep */
+static int deep_sleep = 1;
+/** Default CRC check control */
+static int fw_crc_check = 1;
+/** Init config file (MAC address, register etc.) */
+static char *init_cfg;
+/** Calibration config file (MAC address, init powe etc.) */
+static char *cal_cfg;
+/** Calibration config file EXT */
+static char *cal_cfg_ext;
+/** Init MAC address */
+static char *bt_mac;
+static int btindrst = -1;
+
+/** Setting mbt_drvdbg value based on DEBUG level */
+#ifdef DEBUG_LEVEL1
+#ifdef DEBUG_LEVEL2
+#define DEFAULT_DEBUG_MASK  (0xffffffff & ~DBG_EVENT)
+#else
+#define DEFAULT_DEBUG_MASK  (DBG_MSG | DBG_FATAL | DBG_ERROR)
+#endif /* DEBUG_LEVEL2 */
+u32 mbt_drvdbg = DEFAULT_DEBUG_MASK;
+#endif
+
+#ifdef CONFIG_OF
+static int dts_enable = 1;
+#endif
+
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+int mbt_pm_keep_power = 1;
+#endif
+
+static int btpmic = 0;
+
+/** Offset of sequence number in event */
+#define OFFSET_SEQNUM 4
+
+/**
+ *  @brief handle received packet
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *
+ *  @return        N/A
+ */
+void
+bt_recv_frame(bt_private *priv, struct sk_buff *skb)
+{
+	struct hci_dev *hdev = NULL;
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC)
+		hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer;
+	if (hdev) {
+		skb->dev = (void *)hdev;
+		hdev->stat.byte_rx += skb->len;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+		hci_recv_frame(skb);
+#else
+		hci_recv_frame(hdev, skb);
+#endif
+	}
+	return;
+}
+
+/**
+ *  @brief Alloc bt device
+ *
+ *  @return    pointer to structure mbt_dev or NULL
+ */
+struct mbt_dev *
+alloc_mbt_dev(void)
+{
+	struct mbt_dev *mbt_dev;
+	ENTER();
+
+	mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL);
+	if (!mbt_dev) {
+		LEAVE();
+		return NULL;
+	}
+
+	LEAVE();
+	return mbt_dev;
+}
+
+/**
+ *  @brief Frees m_dev
+ *
+ *  @return    N/A
+ */
+void
+free_m_dev(struct m_dev *m_dev)
+{
+	ENTER();
+	kfree(m_dev->dev_pointer);
+	m_dev->dev_pointer = NULL;
+	LEAVE();
+}
+
+/**
+ *  @brief clean up m_devs
+ *
+ *  @return    N/A
+ */
+void
+clean_up_m_devs(bt_private *priv)
+{
+	struct m_dev *m_dev = NULL;
+	struct hci_dev *hdev = NULL;
+
+	ENTER();
+	if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+		PRINTM(MSG, "BT: Delete %s\n", m_dev->name);
+		if (m_dev->spec_type == BLUEZ_SPEC) {
+			hdev = (struct hci_dev *)m_dev->dev_pointer;
+			/** check if dev->name has been assigned */
+			if (strstr(hdev->name, "hci"))
+				hci_unregister_dev(hdev);
+			hci_free_dev(hdev);
+		}
+		priv->bt_dev.m_dev[BT_SEQ].dev_pointer = NULL;
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function verify the received event pkt
+ *
+ *  Event format:
+ *  +--------+--------+--------+--------+--------+
+ *  | Event  | Length |  ncmd  |      Opcode     |
+ *  +--------+--------+--------+--------+--------+
+ *  | 1-byte | 1-byte | 1-byte |      2-byte     |
+ *  +--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+check_evtpkt(bt_private *priv, struct sk_buff *skb)
+{
+	struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data;
+	struct hci_ev_cmd_complete *ec;
+	u16 opcode, ocf;
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (!priv->bt_dev.sendcmdflag) {
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	if (hdr->evt == HCI_EV_CMD_COMPLETE) {
+		ec = (struct hci_ev_cmd_complete *)
+			(skb->data + HCI_EVENT_HDR_SIZE);
+		opcode = __le16_to_cpu(ec->opcode);
+		ocf = hci_opcode_ocf(opcode);
+		PRINTM(CMD,
+		       "BT: CMD_COMPLTE opcode=0x%x, ocf=0x%x, send_cmd_opcode=0x%x\n",
+		       opcode, ocf, priv->bt_dev.send_cmd_opcode);
+		if (opcode != priv->bt_dev.send_cmd_opcode) {
+			ret = BT_STATUS_FAILURE;
+			goto exit;
+		}
+		switch (ocf) {
+		case BT_CMD_MODULE_CFG_REQ:
+		case BT_CMD_BLE_DEEP_SLEEP:
+		case BT_CMD_CONFIG_MAC_ADDR:
+		case BT_CMD_CSU_WRITE_REG:
+		case BT_CMD_LOAD_CONFIG_DATA:
+		case BT_CMD_LOAD_CONFIG_DATA_EXT:
+		case BT_CMD_AUTO_SLEEP_MODE:
+		case BT_CMD_HOST_SLEEP_CONFIG:
+		case BT_CMD_SDIO_PULL_CFG_REQ:
+		case BT_CMD_SET_EVT_FILTER:
+			// case BT_CMD_ENABLE_DEVICE_TESTMODE:
+		case BT_CMD_PMIC_CONFIGURE:
+		case BT_CMD_INDEPENDENT_RESET:
+			priv->bt_dev.sendcmdflag = FALSE;
+			priv->adapter->cmd_complete = TRUE;
+			wake_up_interruptible(&priv->adapter->cmd_wait_q);
+			break;
+		case BT_CMD_GET_FW_VERSION:
+			{
+				u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE +
+					   sizeof(struct hci_ev_cmd_complete) +
+					   1);
+				snprintf(fw_version, sizeof(fw_version),
+					 "%u.%u.%u.p%u", pos[2], pos[1], pos[0],
+					 pos[3]);
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+				break;
+			}
+		case BT_CMD_RESET:
+		case BT_CMD_ENABLE_WRITE_SCAN:
+			{
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				if (priv->adapter->wait_event_timeout == TRUE) {
+					wake_up(&priv->adapter->cmd_wait_q);
+					priv->adapter->wait_event_timeout =
+						FALSE;
+				} else
+					wake_up_interruptible(&priv->adapter->
+							      cmd_wait_q);
+			}
+			break;
+		case BT_CMD_HOST_SLEEP_ENABLE:
+			priv->bt_dev.sendcmdflag = FALSE;
+			break;
+		default:
+			/** Ignore command not defined but send by driver */
+			if (opcode == priv->bt_dev.send_cmd_opcode) {
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			} else {
+				ret = BT_STATUS_FAILURE;
+			}
+			break;
+		}
+	} else
+		ret = BT_STATUS_FAILURE;
+exit:
+	if (ret == BT_STATUS_SUCCESS)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+/**
+*  @brief This function stores the FW dumps received from events
+*
+*  @param priv    A pointer to bt_private structure
+*  @param skb     A pointer to rx skb
+*
+*  @return        N/A
+*/
+void
+bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len)
+{
+	struct file *pfile_fwdump = NULL;
+	loff_t pos = 0;
+	u16 seqnum = 0;
+	struct timeval t;
+	u32 sec;
+
+	ENTER();
+
+	seqnum = __le16_to_cpu(*(u16 *) (buf + OFFSET_SEQNUM));
+
+	if (priv->adapter->fwdump_fname && seqnum != 1) {
+		pfile_fwdump =
+			filp_open((const char *)priv->adapter->fwdump_fname,
+				  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		if (IS_ERR(pfile_fwdump)) {
+			PRINTM(MSG, "Cannot create firmware dump file.\n");
+			LEAVE();
+			return;
+		}
+	} else {
+		if (!priv->adapter->fwdump_fname) {
+			gfp_t flag;
+			flag = (in_atomic() ||
+				irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
+			priv->adapter->fwdump_fname = kzalloc(64, flag);
+		} else
+			memset(priv->adapter->fwdump_fname, 0, 64);
+
+		do_gettimeofday(&t);
+		sec = (u32)t.tv_sec;
+		sprintf(priv->adapter->fwdump_fname, "%s%u",
+			"/var/log/bt_fwdump_", sec);
+		pfile_fwdump =
+			filp_open(priv->adapter->fwdump_fname,
+				  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		if (IS_ERR(pfile_fwdump)) {
+			sprintf(priv->adapter->fwdump_fname, "%s%u",
+				"/data/bt_fwdump_", sec);
+			pfile_fwdump =
+				filp_open((const char *)priv->adapter->
+					  fwdump_fname,
+					  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		}
+	}
+
+	if (IS_ERR(pfile_fwdump)) {
+		PRINTM(MSG, "Cannot create firmware dump file\n");
+		LEAVE();
+		return;
+	}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	vfs_write(pfile_fwdump, buf, len, &pos);
+#else
+	kernel_write(pfile_fwdump, buf, len, &pos);
+#endif
+	filp_close(pfile_fwdump, NULL);
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function process the received event
+ *
+ *  Event format:
+ *  +--------+--------+--------+--------+-----+
+ *  |   EC   | Length |           Data        |
+ *  +--------+--------+--------+--------+-----+
+ *  | 1-byte | 1-byte |          n-byte       |
+ *  +--------+--------+--------+--------+-----+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_event(bt_private *priv, struct sk_buff *skb)
+{
+	int ret = BT_STATUS_SUCCESS;
+#ifdef DEBUG_LEVEL1
+	struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+#endif
+	BT_EVENT *pevent;
+
+	ENTER();
+	if (!m_dev) {
+		PRINTM(CMD, "BT: bt_process_event without m_dev\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pevent = (BT_EVENT *)skb->data;
+	if (pevent->EC != 0xff) {
+		PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pevent->EC);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	switch (pevent->data[0]) {
+	case BT_CMD_AUTO_SLEEP_MODE:
+		if (pevent->data[2] == BT_STATUS_SUCCESS) {
+			if (pevent->data[1] == BT_PS_ENABLE)
+				priv->adapter->psmode = 1;
+			else
+				priv->adapter->psmode = 0;
+			PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name,
+			       (priv->adapter->psmode) ? "Enable" : "Disable");
+
+		} else {
+			PRINTM(CMD, "BT: PS Mode Command Fail %s\n",
+			       m_dev->name);
+		}
+		break;
+	case BT_CMD_HOST_SLEEP_CONFIG:
+		if (pevent->data[3] == BT_STATUS_SUCCESS) {
+			PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n",
+			       m_dev->name, pevent->data[1], pevent->data[2]);
+		} else {
+			PRINTM(CMD, "BT: %s: HSCFG Command Fail\n",
+			       m_dev->name);
+		}
+		break;
+	case BT_CMD_HOST_SLEEP_ENABLE:
+		if (pevent->data[1] == BT_STATUS_SUCCESS) {
+			priv->adapter->hs_state = HS_ACTIVATED;
+			if (priv->adapter->suspend_fail == FALSE) {
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+				bt_is_suspended(priv);
+#endif
+#endif
+#endif
+				if (priv->adapter->wait_event_timeout) {
+					wake_up(&priv->adapter->cmd_wait_q);
+					priv->adapter->wait_event_timeout =
+						FALSE;
+				} else
+					wake_up_interruptible(&priv->adapter->
+							      cmd_wait_q);
+
+			}
+			if (priv->adapter->psmode)
+				priv->adapter->ps_state = PS_SLEEP;
+			PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n",
+			       m_dev->name);
+
+		} else {
+			PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name);
+		}
+		break;
+	case BT_CMD_MODULE_CFG_REQ:
+		if ((priv->bt_dev.sendcmdflag == TRUE) &&
+		    ((pevent->data[1] == MODULE_BRINGUP_REQ)
+		     || (pevent->data[1] == MODULE_SHUTDOWN_REQ))) {
+			if (pevent->data[1] == MODULE_BRINGUP_REQ) {
+				PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+				       (pevent->data[2] && (pevent->data[2] !=
+							    MODULE_CFG_RESP_ALREADY_UP))
+				       ? "Bring up Fail" : "Bring up success");
+				priv->bt_dev.devType = pevent->data[3];
+				PRINTM(CMD, "devType:%s\n",
+				       (pevent->data[3] ==
+					DEV_TYPE_AMP) ? "AMP controller" :
+				       "BR/EDR controller");
+				priv->bt_dev.devFeature = pevent->data[4];
+				PRINTM(CMD, "devFeature:  %s,    %s,    %s"
+				       "\n",
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BT) ?
+					"BT Feature" : "No BT Feature"),
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BTAMP) ?
+					"BTAMP Feature" : "No BTAMP Feature"),
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BLE) ?
+					"BLE Feature" : "No BLE Feature")
+					);
+			}
+			if (pevent->data[1] == MODULE_SHUTDOWN_REQ) {
+				PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+				       (pevent->data[2]) ? "Shut down Fail"
+				       : "Shut down success");
+
+			}
+			if (pevent->data[2]) {
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			}
+		} else {
+			PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n");
+			ret = BT_STATUS_FAILURE;
+		}
+		break;
+	case BT_EVENT_POWER_STATE:
+		if (pevent->data[1] == BT_PS_SLEEP)
+			priv->adapter->ps_state = PS_SLEEP;
+		if (priv->adapter->ps_state == PS_SLEEP
+		    && (priv->card_type == CARD_TYPE_SD8887)
+			)
+			os_sched_timeout(5);
+		PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+		       (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
+
+		break;
+	case BT_CMD_SDIO_PULL_CFG_REQ:
+		if (pevent->data[pevent->length - 1] == BT_STATUS_SUCCESS)
+			PRINTM(CMD, "BT: %s: SDIO pull configuration success\n",
+			       m_dev->name);
+
+		else {
+			PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n",
+			       m_dev->name);
+
+		}
+		break;
+	default:
+		PRINTM(CMD, "BT: Unknown Event=%d %s\n", pevent->data[0],
+		       m_dev->name);
+		ret = BT_STATUS_FAILURE;
+		break;
+	}
+exit:
+	if (ret == BT_STATUS_SUCCESS)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function save the dump info to file
+ *
+ *  @param dir_name     directory name
+ *  @param file_name    file_name
+ *  @param buf			buffer
+ *  @param buf_len		buffer length
+ *
+ *  @return   		    0 --success otherwise fail
+ */
+int
+bt_save_dump_info_to_file(char *dir_name, char *file_name, u8 *buf, u32 buf_len)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct file *pfile = NULL;
+	u8 name[64];
+	loff_t pos;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	mm_segment_t fs;
+#endif
+
+	ENTER();
+
+	if (!dir_name || !file_name || !buf) {
+		PRINTM(ERROR, "Can't save dump info to file\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	memset(name, 0, sizeof(name));
+	snprintf((char *)name, sizeof(name), "%s/%s", dir_name, file_name);
+	pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644);
+	if (IS_ERR(pfile)) {
+		PRINTM(MSG,
+		       "Create file %s error, try to save dump file in /var\n",
+		       name);
+		memset(name, 0, sizeof(name));
+		snprintf((char *)name, sizeof(name), "%s/%s", "/var",
+			 file_name);
+		pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644);
+	}
+	if (IS_ERR(pfile)) {
+		PRINTM(ERROR, "Create Dump file for %s error\n", name);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	PRINTM(MSG, "Dump data %s saved in %s\n", file_name, name);
+
+	pos = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	vfs_write(pfile, (const char __user *)buf, buf_len, &pos);
+	set_fs(fs);
+#else
+	kernel_write(pfile, (const char __user *)buf, buf_len, &pos);
+#endif
+	filp_close(pfile, NULL);
+
+	PRINTM(MSG, "Dump data %s saved in %s successfully\n", file_name, name);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+#define DEBUG_HOST_READY		0xEE
+#define DEBUG_FW_DONE			0xFF
+#define DUMP_MAX_POLL_TRIES			200
+
+#define DEBUG_DUMP_CTRL_REG_8897               0xE2
+#define DEBUG_DUMP_START_REG_8897              0xE3
+#define DEBUG_DUMP_END_REG_8897                0xEA
+#define DEBUG_DUMP_CTRL_REG_8887               0xA2
+#define DEBUG_DUMP_START_REG_8887              0xA3
+#define DEBUG_DUMP_END_REG_8887                0xAA
+#define DEBUG_DUMP_CTRL_REG_8977               0xF0
+#define DEBUG_DUMP_START_REG_8977              0xF1
+#define DEBUG_DUMP_END_REG_8977                0xF8
+#define DEBUG_DUMP_CTRL_REG_8978               0xF0
+#define DEBUG_DUMP_START_REG_8978              0xF1
+#define DEBUG_DUMP_END_REG_8978                0xF8
+#define DEBUG_DUMP_CTRL_REG_8997               0xF0
+#define DEBUG_DUMP_START_REG_8997              0xF1
+#define DEBUG_DUMP_END_REG_8997                0xF8
+#define DEBUG_DUMP_CTRL_REG_8987               0xF0
+#define DEBUG_DUMP_START_REG_8987              0xF1
+#define DEBUG_DUMP_END_REG_8987                0xF8
+
+typedef enum {
+	DUMP_TYPE_ITCM = 0,
+	DUMP_TYPE_DTCM = 1,
+	DUMP_TYPE_SQRAM = 2,
+	DUMP_TYPE_APU_REGS = 3,
+	DUMP_TYPE_CIU_REGS = 4,
+	DUMP_TYPE_ICU_REGS = 5,
+	DUMP_TYPE_MAC_REGS = 6,
+	DUMP_TYPE_EXTEND_7 = 7,
+	DUMP_TYPE_EXTEND_8 = 8,
+	DUMP_TYPE_EXTEND_9 = 9,
+	DUMP_TYPE_EXTEND_10 = 10,
+	DUMP_TYPE_EXTEND_11 = 11,
+	DUMP_TYPE_EXTEND_12 = 12,
+	DUMP_TYPE_EXTEND_13 = 13,
+	DUMP_TYPE_EXTEND_LAST = 14
+} dumped_mem_type;
+
+#define MAX_NAME_LEN               8
+#define MAX_FULL_NAME_LEN               32
+
+/** Memory type mapping structure */
+typedef struct {
+	/** memory name */
+	u8 mem_name[MAX_NAME_LEN];
+	/** memory pointer */
+	u8 *mem_Ptr;
+	/** file structure */
+	struct file *pfile_mem;
+	/** donbe flag */
+	u8 done_flag;
+	/** dump type */
+	u8 type;
+} memory_type_mapping;
+
+memory_type_mapping bt_mem_type_mapping_tbl[] = {
+	{"ITCM", NULL, NULL, 0xF0, FW_DUMP_TYPE_MEM_ITCM},
+	{"DTCM", NULL, NULL, 0xF1, FW_DUMP_TYPE_MEM_DTCM},
+	{"SQRAM", NULL, NULL, 0xF2, FW_DUMP_TYPE_MEM_SQRAM},
+	{"APU", NULL, NULL, 0xF3, FW_DUMP_TYPE_REG_APU},
+	{"CIU", NULL, NULL, 0xF4, FW_DUMP_TYPE_REG_CIU},
+	{"ICU", NULL, NULL, 0xF5, FW_DUMP_TYPE_REG_ICU},
+	{"MAC", NULL, NULL, 0xF6, FW_DUMP_TYPE_REG_MAC},
+	{"EXT7", NULL, NULL, 0xF7},
+	{"EXT8", NULL, NULL, 0xF8},
+	{"EXT9", NULL, NULL, 0xF9},
+	{"EXT10", NULL, NULL, 0xFA},
+	{"EXT11", NULL, NULL, 0xFB},
+	{"EXT12", NULL, NULL, 0xFC},
+	{"EXT13", NULL, NULL, 0xFD},
+	{"EXTLAST", NULL, NULL, 0xFE},
+};
+
+typedef enum {
+	RDWR_STATUS_SUCCESS = 0,
+	RDWR_STATUS_FAILURE = 1,
+	RDWR_STATUS_DONE = 2
+} rdwr_status;
+
+/**
+ *  @brief This function read/write firmware via cmd52
+ *
+ *  @param phandle   A pointer to moal_handle
+ *
+ *  @return         MLAN_STATUS_SUCCESS
+ */
+rdwr_status
+bt_cmd52_rdwr_firmware(bt_private *priv, u8 doneflag)
+{
+	int ret = 0;
+	int tries = 0;
+	u8 ctrl_data = 0;
+	u8 dbg_dump_ctrl_reg = 0;
+
+	if (priv->card_type == CARD_TYPE_SD8887)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8887;
+	else if (priv->card_type == CARD_TYPE_SD8897)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8897;
+	else if (priv->card_type == CARD_TYPE_SD8977)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8977;
+	else if (priv->card_type == CARD_TYPE_SD8978)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8978;
+	else if (priv->card_type == CARD_TYPE_SD8997)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8997;
+	else if (priv->card_type == CARD_TYPE_SD8987)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8987;
+
+	sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.card)->func,
+		    DEBUG_HOST_READY, dbg_dump_ctrl_reg, &ret);
+	if (ret) {
+		PRINTM(ERROR, "SDIO Write ERR\n");
+		return RDWR_STATUS_FAILURE;
+	}
+	for (tries = 0; tries < DUMP_MAX_POLL_TRIES; tries++) {
+		ctrl_data =
+			sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->
+				   func, dbg_dump_ctrl_reg, &ret);
+		if (ret) {
+			PRINTM(ERROR, "SDIO READ ERR\n");
+			return RDWR_STATUS_FAILURE;
+		}
+		if (ctrl_data == DEBUG_FW_DONE)
+			break;
+		if (doneflag && ctrl_data == doneflag)
+			return RDWR_STATUS_DONE;
+		if (ctrl_data != DEBUG_HOST_READY) {
+			PRINTM(INFO,
+			       "The ctrl reg was changed, re-try again!\n");
+			sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.
+				     card)->func, DEBUG_HOST_READY,
+				    dbg_dump_ctrl_reg, &ret);
+			if (ret) {
+				PRINTM(ERROR, "SDIO Write ERR\n");
+				return RDWR_STATUS_FAILURE;
+			}
+		}
+		udelay(100);
+	}
+	if (ctrl_data == DEBUG_HOST_READY) {
+		PRINTM(ERROR, "Fail to pull ctrl_data\n");
+		return RDWR_STATUS_FAILURE;
+	}
+
+	return RDWR_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function dump firmware memory to file
+ *
+ *  @param phandle   A pointer to moal_handle
+ *
+ *  @return         N/A
+ */
+void
+bt_dump_firmware_info_v2(bt_private *priv)
+{
+	int ret = 0;
+	unsigned int reg, reg_start, reg_end;
+	u8 *dbg_ptr = NULL;
+	u8 dump_num = 0;
+	u8 idx = 0;
+	u8 doneflag = 0;
+	rdwr_status stat;
+	u8 i = 0;
+	u8 read_reg = 0;
+	u32 memory_size = 0;
+	u8 path_name[64], file_name[32];
+	u8 *end_ptr = NULL;
+	u8 dbg_dump_start_reg = 0;
+	u8 dbg_dump_end_reg = 0;
+
+	if (!priv) {
+		PRINTM(ERROR, "Could not dump firmwware info\n");
+		return;
+	}
+
+	if ((priv->card_type != CARD_TYPE_SD8887) &&
+	    (priv->card_type != CARD_TYPE_SD8897)
+	    && (priv->card_type != CARD_TYPE_SD8977) &&
+	    (priv->card_type != CARD_TYPE_SD8997)
+	    && (priv->card_type != CARD_TYPE_SD8987) &&
+	    (priv->card_type != CARD_TYPE_SD8978)) {
+		PRINTM(MSG, "card_type %d don't support FW dump\n",
+		       priv->card_type);
+		return;
+	}
+
+	memset(path_name, 0, sizeof(path_name));
+	strcpy((char *)path_name, "/data");
+	PRINTM(MSG, "Create DUMP directory success:dir_name=%s\n", path_name);
+
+	if (priv->card_type == CARD_TYPE_SD8887) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8887;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8887;
+	} else if (priv->card_type == CARD_TYPE_SD8897) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8897;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8897;
+	} else if (priv->card_type == CARD_TYPE_SD8977) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8977;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8977;
+	} else if (priv->card_type == CARD_TYPE_SD8978) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8978;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8978;
+	} else if (priv->card_type == CARD_TYPE_SD8997) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8997;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8997;
+	} else if (priv->card_type == CARD_TYPE_SD8987) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8987;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8987;
+	}
+
+	sbi_wakeup_firmware(priv);
+	sdio_claim_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func);
+	priv->fw_dump = TRUE;
+	/* start dump fw memory */
+	PRINTM(MSG, "==== DEBUG MODE OUTPUT START ====\n");
+	/* read the number of the memories which will dump */
+	if (RDWR_STATUS_FAILURE == bt_cmd52_rdwr_firmware(priv, doneflag))
+		goto done;
+	reg = dbg_dump_start_reg;
+	dump_num =
+		sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->func,
+			   reg, &ret);
+	if (ret) {
+		PRINTM(MSG, "SDIO READ MEM NUM ERR\n");
+		goto done;
+	}
+
+	/* read the length of every memory which will dump */
+	for (idx = 0; idx < dump_num; idx++) {
+		if (RDWR_STATUS_FAILURE ==
+		    bt_cmd52_rdwr_firmware(priv, doneflag))
+			goto done;
+		memory_size = 0;
+		reg = dbg_dump_start_reg;
+		for (i = 0; i < 4; i++) {
+			read_reg =
+				sdio_readb(((struct sdio_mmc_card *)priv->
+					    bt_dev.card)->func, reg, &ret);
+			if (ret) {
+				PRINTM(MSG, "SDIO READ ERR\n");
+				goto done;
+			}
+			memory_size |= (read_reg << i * 8);
+			reg++;
+		}
+		if (memory_size == 0) {
+			PRINTM(MSG, "Firmware Dump Finished!\n");
+			break;
+		} else {
+			PRINTM(MSG, "%s_SIZE=0x%x\n",
+			       bt_mem_type_mapping_tbl[idx].mem_name,
+			       memory_size);
+			bt_mem_type_mapping_tbl[idx].mem_Ptr =
+				vmalloc(memory_size + 1);
+			if ((ret != BT_STATUS_SUCCESS) ||
+			    !bt_mem_type_mapping_tbl[idx].mem_Ptr) {
+				PRINTM(ERROR,
+				       "Error: vmalloc %s buffer failed!!!\n",
+				       bt_mem_type_mapping_tbl[idx].mem_name);
+				goto done;
+			}
+			dbg_ptr = bt_mem_type_mapping_tbl[idx].mem_Ptr;
+			end_ptr = dbg_ptr + memory_size;
+		}
+		doneflag = bt_mem_type_mapping_tbl[idx].done_flag;
+		PRINTM(MSG, "Start %s output, please wait...\n",
+		       bt_mem_type_mapping_tbl[idx].mem_name);
+		do {
+			stat = bt_cmd52_rdwr_firmware(priv, doneflag);
+			if (RDWR_STATUS_FAILURE == stat)
+				goto done;
+
+			reg_start = dbg_dump_start_reg;
+			reg_end = dbg_dump_end_reg;
+			for (reg = reg_start; reg <= reg_end; reg++) {
+				*dbg_ptr =
+					sdio_readb(((struct sdio_mmc_card *)
+						    priv->bt_dev.card)->func,
+						   reg, &ret);
+				if (ret) {
+					PRINTM(MSG, "SDIO READ ERR\n");
+					goto done;
+				}
+				if (dbg_ptr < end_ptr)
+					dbg_ptr++;
+				else
+					PRINTM(MSG,
+					       "pre-allocced buf is not enough\n");
+			}
+			if (RDWR_STATUS_DONE == stat) {
+				PRINTM(MSG, "%s done:"
+				       "size = 0x%x\n",
+				       bt_mem_type_mapping_tbl[idx].mem_name,
+				       (unsigned int)(dbg_ptr -
+						      bt_mem_type_mapping_tbl
+						      [idx].mem_Ptr));
+				memset(file_name, 0, sizeof(file_name));
+				snprintf((char *)file_name, sizeof(file_name),
+					 "%s%s", "file_bt_",
+					 bt_mem_type_mapping_tbl[idx].mem_name);
+				if (BT_STATUS_SUCCESS !=
+				    bt_save_dump_info_to_file((char *)path_name,
+							      (char *)file_name,
+							      bt_mem_type_mapping_tbl
+							      [idx].mem_Ptr,
+							      memory_size))
+					PRINTM(MSG,
+					       "Can't save dump file %s in %s\n",
+					       file_name, path_name);
+				vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr);
+				bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL;
+				break;
+			}
+		} while (1);
+	}
+	PRINTM(MSG, "==== DEBUG MODE OUTPUT END ====\n");
+	/* end dump fw memory */
+done:
+	priv->fw_dump = FALSE;
+	sdio_release_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func);
+	for (idx = 0; idx < dump_num; idx++) {
+		if (bt_mem_type_mapping_tbl[idx].mem_Ptr) {
+			vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr);
+			bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL;
+		}
+	}
+	PRINTM(MSG, "==== DEBUG MODE END ====\n");
+	return;
+}
+
+/**
+ *  @brief This function shows debug info for timeout of command sending.
+ *
+ *  @param adapter  A pointer to bt_private
+ *  @param cmd      Timeout command id
+ *
+ *  @return         N/A
+ */
+static void
+bt_cmd_timeout_func(bt_private *priv, u16 cmd)
+{
+	bt_adapter *adapter = priv->adapter;
+	ENTER();
+
+	adapter->num_cmd_timeout++;
+
+	PRINTM(ERROR, "Version = %s\n", adapter->drv_ver);
+	PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd);
+	PRINTM(ERROR, "Number of command timeout = %d\n",
+	       adapter->num_cmd_timeout);
+	PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter);
+	PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode);
+	PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state);
+	PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state);
+	PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip);
+	PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail);
+	PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended);
+	PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries);
+	PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete);
+	PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv);
+	PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done);
+	PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending);
+	PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg);
+	bt_dump_sdio_regs(priv);
+	LEAVE();
+}
+
+/**
+ *  @brief This function queue frame
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    N/A
+ */
+static void
+bt_queue_frame(bt_private *priv, struct sk_buff *skb)
+{
+	skb_queue_tail(&priv->adapter->tx_queue, skb);
+}
+
+/**
+ *  @brief This function send reset cmd to firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return	       BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_reset_command(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((RESET_OGF << 10) | BT_CMD_RESET);
+	pcmd->length = 0x00;
+	pcmd->cmd_type = 0x00;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, 3);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "Queue Reset Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Reset timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_RESET);
+	} else {
+		PRINTM(CMD, "BT: Reset Command done\n");
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends module cfg cmd to firmware
+ *
+ *  Command format:
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     OCF OGF     | Length |                Data               |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     2-byte      | 1-byte |               4-byte              |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param subcmd  sub command
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_module_cfg_cmd(bt_private *priv, int subcmd)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "BT: No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ);
+	pcmd->length = 1;
+	pcmd->data[0] = subcmd;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "Queue module cfg Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	/*
+	   On some Android platforms certain delay is needed for HCI daemon to
+	   remove this module and close itself gracefully. Otherwise it hangs.
+	   This 10ms delay is a workaround for such platforms as the root cause
+	   has not been found yet. */
+	mdelay(10);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: module_cfg_cmd(%#x): timeout sendcmdflag=%d\n",
+		       subcmd, priv->bt_dev.sendcmdflag);
+		bt_cmd_timeout_func(priv, BT_CMD_MODULE_CFG_REQ);
+	} else {
+		PRINTM(CMD, "BT: module cfg Command done\n");
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables power save mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_ps(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE);
+	if (priv->bt_dev.psmode)
+		pcmd->data[0] = BT_PS_ENABLE;
+	else
+		pcmd->data[0] = BT_PS_DISABLE;
+	if (priv->bt_dev.idle_timeout) {
+		pcmd->length = 3;
+		pcmd->data[1] = (u8)(priv->bt_dev.idle_timeout & 0x00ff);
+		pcmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8;
+	} else {
+		pcmd->length = 1;
+	}
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: psmode timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_AUTO_SLEEP_MODE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends hscfg command
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_hscfg_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG);
+	pcmd->length = 2;
+	pcmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8;
+	pcmd->data[1] = (u8)(priv->bt_dev.gpio_gap & 0x00ff);
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0], pcmd->data[1]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: HSCFG timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_CONFIG);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends sdio pull ctrl command
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_sdio_pull_ctrl_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ);
+	pcmd->length = 4;
+	pcmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff);
+	pcmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8;
+	pcmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16;
+	pcmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD,
+	       "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[1], pcmd->data[0],
+	       pcmd->data[3], pcmd->data[2]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SDIO_PULL_CFG_REQ);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends command to configure PMIC
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_pmic_configure(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_PMIC_CONFIGURE);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue PMIC Configure Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: PMIC Configure timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_PMIC_CONFIGURE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables host sleep
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param is_shutdown  indicate shutdown mode
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_hs(bt_private *priv, bool is_shutdown)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	priv->adapter->suspend_fail = FALSE;
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->adapter->wait_event_timeout = is_shutdown;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	PRINTM(CMD, "Queue hs enable Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (is_shutdown) {
+		if (!os_wait_timeout
+		    (priv->adapter->cmd_wait_q, priv->adapter->hs_state,
+		     WAIT_UNTIL_HS_STATE_CHANGED)) {
+			PRINTM(MSG, "BT: Enable host sleep timeout:\n");
+			priv->adapter->wait_event_timeout = FALSE;
+			bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE);
+		}
+	} else {
+		if (!os_wait_interruptible_timeout
+		    (priv->adapter->cmd_wait_q, priv->adapter->hs_state,
+		     WAIT_UNTIL_HS_STATE_CHANGED)) {
+			PRINTM(MSG, "BT: Enable host sleep timeout:\n");
+			bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE);
+		}
+	}
+	OS_INT_DISABLE;
+	if ((priv->adapter->hs_state == HS_ACTIVATED) ||
+	    (priv->adapter->is_suspended == TRUE)) {
+		OS_INT_RESTORE;
+		PRINTM(MSG, "BT: suspend success! skip=%d\n",
+		       priv->adapter->hs_skip);
+	} else {
+		priv->adapter->suspend_fail = TRUE;
+		OS_INT_RESTORE;
+		priv->adapter->hs_skip++;
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG,
+		       "BT: suspend skipped! "
+		       "state=%d skip=%d ps_state= %d WakeupTries=%d\n",
+		       priv->adapter->hs_state, priv->adapter->hs_skip,
+		       priv->adapter->ps_state, priv->adapter->WakeupTries);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Set Evt Filter
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_evt_filter(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_SET_EVT_FILTER);
+	pcmd->length = 0x03;
+	pcmd->data[0] = 0x02;
+	pcmd->data[1] = 0x00;
+	pcmd->data[2] = 0x03;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Set Evt Filter Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set Evt Filter timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SET_EVT_FILTER);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Enable Write Scan - Page and Inquiry
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_write_scan(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_ENABLE_WRITE_SCAN);
+	pcmd->length = 0x01;
+	pcmd->data[0] = 0x03;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Enable Write Scan Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Enable Write Scan timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_ENABLE_WRITE_SCAN);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Enable Device under test mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_device_under_testmode(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((0x06 << 10) | BT_CMD_ENABLE_DEVICE_TESTMODE);
+	pcmd->length = 0x00;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue enable device under testmode Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Enable Device under TEST mode timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_ENABLE_DEVICE_TESTMODE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables test mode and send cmd
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_test_mode(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	/** Set Evt Filter Command */
+	ret = bt_set_evt_filter(priv);
+	if (ret != BT_STATUS_SUCCESS) {
+		PRINTM(ERROR, "BT test_mode: Set Evt filter fail\n");
+		goto exit;
+	}
+
+	/** Enable Write Scan Command */
+	ret = bt_enable_write_scan(priv);
+	if (ret != BT_STATUS_SUCCESS) {
+		PRINTM(ERROR, "BT test_mode: Enable Write Scan fail\n");
+		goto exit;
+	}
+
+	/** Enable Device under test mode */
+	ret = bt_enable_device_under_testmode(priv);
+	if (ret != BT_STATUS_SUCCESS)
+		PRINTM(ERROR,
+		       "BT test_mode: Enable device under testmode fail\n");
+
+exit:
+	LEAVE();
+	return ret;
+}
+
+#define DISABLE_RESET  0x0
+#define ENABLE_OUTBAND_RESET 0x1
+#define ENABLE_INBAND_RESET  0x02
+#define DEFAULT_GPIO 0xff
+/**
+ *  @brief This function set GPIO pin
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_independent_reset(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	u8 mode, gpio;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_INDEPENDENT_RESET);
+	mode = btindrst & 0xff;
+	gpio = (btindrst & 0xff00) >> 8;
+	if (mode == ENABLE_OUTBAND_RESET) {
+		pcmd->data[0] = ENABLE_OUTBAND_RESET;
+		if (!gpio)
+			pcmd->data[1] = DEFAULT_GPIO;
+		else
+			pcmd->data[1] = gpio;
+	} else if (mode == ENABLE_INBAND_RESET) {
+		pcmd->data[0] = ENABLE_INBAND_RESET;
+		pcmd->data[1] = DEFAULT_GPIO;
+	} else if (mode == DISABLE_RESET) {
+		pcmd->data[0] = DISABLE_RESET;
+		pcmd->data[1] = DEFAULT_GPIO;
+	} else {
+		PRINTM(WARN, "Unsupport mode\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	PRINTM(CMD, "BT: independant reset mode=%d gpio=%d\n", mode, gpio);
+	pcmd->length = 2;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Independent reset : timeout!\n");
+		bt_cmd_timeout_func(priv, BT_CMD_INDEPENDENT_RESET);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sets ble deepsleep mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mode    TRUE/FALSE
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_ble_deepsleep(bt_private *priv, int mode)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_BLE_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_BLE_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP);
+	pcmd->length = 1;
+	pcmd->deepsleep = mode;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_BLE_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode,
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_BLE_DEEP_SLEEP);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function gets FW version
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_get_fw_version(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION);
+	pcmd->length = 0x01;
+	pcmd->cmd_type = 0x00;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, 4);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Get FW version: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_GET_FW_VERSION);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sets mac address
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_mac_address(bt_private *priv, u8 *mac)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	int i = 0;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR);
+	pcmd->length = 8;
+	pcmd->cmd_type = MRVL_VENDOR_PKT;
+	pcmd->cmd_len = 6;
+	for (i = 0; i < 6; i++)
+		pcmd->data[i] = mac[5 - i];
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_HCI_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set mac addr " MACSTR " (0x%x)\n", MAC2STR(mac),
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set mac addr: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_CONFIG_MAC_ADDR);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	int i = 0;
+	/* u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF 0x01
+	   0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE 0xC0 0xC6 0x2D 0x00 0x00 0x00
+	   0x00 0x00 0x00 0x00 0xF0}; */
+
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA);
+	pcmd->length = 0x20;
+	pcmd->data[0] = 0x00;
+	pcmd->data[1] = 0x00;
+	pcmd->data[2] = 0x00;
+	pcmd->data[3] = 0x1C;
+	/* swip cal-data byte */
+	for (i = 4; i < 32; i++)
+		pcmd->data[i] = *(config_data + ((i / 4) * 8 - 1 - i));
+	if (mac != NULL) {
+		pcmd->data[2] = 0x01;	/* skip checksum */
+		for (i = 24; i < 30; i++)
+			pcmd->data[i] = mac[29 - i];
+	}
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "calirate data: ", pcmd->data, 32);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load calibrate data: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate EXT data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+
+	ENTER();
+
+	if (cfg_data_len > BT_CMD_DATA_LEN) {
+		PRINTM(WARN, "cfg_data_len is too long exceed %d.\n",
+		       BT_CMD_DATA_LEN);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA_EXT);
+	pcmd->length = cfg_data_len;
+
+	memcpy(pcmd->data, config_data, cfg_data_len);
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "calirate ext data", pcmd->data, pcmd->length);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load calibrate ext data: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function writes value to CSU registers
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param type    reg type
+ *  @param offset  register address
+ *  @param value   register value to write
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CSU_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CSU_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG);
+	pcmd->length = 7;
+	pcmd->type = type;
+	pcmd->offset[0] = (offset & 0x000000ff);
+	pcmd->offset[1] = (offset & 0x0000ff00) >> 8;
+	pcmd->offset[2] = (offset & 0x00ff0000) >> 16;
+	pcmd->offset[3] = (offset & 0xff000000) >> 24;
+	pcmd->value[0] = (value & 0x00ff);
+	pcmd->value[1] = (value & 0xff00) >> 8;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_CSU_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n",
+	       type, offset, value);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Set CSU reg timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_CSU_WRITE_REG);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function used to restore tx_queue
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        N/A
+ */
+void
+bt_restore_tx_queue(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	while (!skb_queue_empty(&priv->adapter->pending_queue)) {
+		skb = skb_dequeue(&priv->adapter->pending_queue);
+		if (skb)
+			bt_queue_frame(priv, skb);
+	}
+	wake_up_interruptible(&priv->MainThread.waitQ);
+}
+
+/**
+ *  @brief This function used to send command to firmware
+ *
+ *  Command format:
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     OCF OGF     | Length |                Data               |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     2-byte      | 1-byte |               4-byte              |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_prepare_command(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (priv->bt_dev.hscfgcmd) {
+		priv->bt_dev.hscfgcmd = 0;
+		ret = bt_send_hscfg_cmd(priv);
+	}
+	if (priv->bt_dev.pscmd) {
+		priv->bt_dev.pscmd = 0;
+		ret = bt_enable_ps(priv);
+	}
+	if (priv->bt_dev.sdio_pull_ctrl) {
+		priv->bt_dev.sdio_pull_ctrl = 0;
+		ret = bt_send_sdio_pull_ctrl_cmd(priv);
+	}
+	if (priv->bt_dev.hscmd) {
+		priv->bt_dev.hscmd = 0;
+		if (priv->bt_dev.hsmode)
+			ret = bt_enable_hs(priv, FALSE);
+		else {
+			ret = sbi_wakeup_firmware(priv);
+			priv->adapter->hs_state = HS_DEACTIVATED;
+		}
+	}
+	if (priv->bt_dev.test_mode) {
+		priv->bt_dev.test_mode = 0;
+		ret = bt_enable_test_mode(priv);
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief Update tx state
+ *
+ * @param priv          A pointer to bt_private structure
+ * @param skb			A pointer to sk_buff structure
+ *
+ * @return                N/A
+ */
+static void
+update_stat_byte_tx(bt_private *priv, struct sk_buff *skb)
+{
+	((struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer)->stat.
+		byte_tx += skb->len;
+}
+
+/**
+ * @brief Update tx error state
+ *
+ * @param priv          A pointer to bt_private structure
+ * @param skb			A pointer to sk_buff structure
+ *
+ * @return                N/A
+ */
+static void
+update_stat_err_tx(bt_private *priv, struct sk_buff *skb)
+{
+	((struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer)->stat.
+		err_tx++;
+}
+
+/** @brief This function processes a single packet
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to skb which includes TX packet
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+send_single_packet(bt_private *priv, struct sk_buff *skb)
+{
+	int ret;
+	struct sk_buff *new_skb = NULL;
+	ENTER();
+	if (!skb || !skb->data) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) {
+		PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len,
+		       BT_UPLD_SIZE);
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	if (skb_headroom(skb) < BT_HEADER_LEN) {
+		new_skb = skb_realloc_headroom(skb, BT_HEADER_LEN);
+		if (!new_skb) {
+			PRINTM(ERROR, "TX error: realloc_headroom failed %d\n",
+			       BT_HEADER_LEN);
+			kfree_skb(skb);
+			LEAVE();
+			return BT_STATUS_FAILURE;
+		} else {
+			if (new_skb != skb)
+				dev_kfree_skb_any(skb);
+			skb = new_skb;
+		}
+	}
+	/* This is SDIO/PCIE specific header length: byte[3][2][1], * type:
+	   byte[0] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor)
+	 */
+	skb_push(skb, BT_HEADER_LEN);
+	skb->data[0] = (skb->len & 0x0000ff);
+	skb->data[1] = (skb->len & 0x00ff00) >> 8;
+	skb->data[2] = (skb->len & 0xff0000) >> 16;
+	skb->data[3] = bt_cb(skb)->pkt_type;
+	if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT)
+		PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n",
+		       __le16_to_cpu(*((u16 *) & skb->data[4])), skb->len);
+	ret = sbi_host_to_card(priv, skb->data, skb->len);
+	if (ret == BT_STATUS_FAILURE)
+		update_stat_err_tx(priv, skb);
+	else
+		update_stat_byte_tx(priv, skb);
+	if (ret != BT_STATUS_PENDING)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+#ifdef CONFIG_OF
+/**
+ *  @brief This function read the initial parameter from device tress
+ *
+ *
+ *  @return         N/A
+ */
+static void
+bt_init_from_dev_tree(void)
+{
+	struct device_node *dt_node = NULL;
+	struct property *prop;
+	u32 data;
+	const char *string_data;
+
+	ENTER();
+
+	if (!dts_enable) {
+		PRINTM(CMD, "DTS is disabled!");
+		return;
+	}
+
+	dt_node = of_find_node_by_name(NULL, "sd8xxx-bt");
+	if (!dt_node) {
+		LEAVE();
+		return;
+	}
+	for_each_property_of_node(dt_node, prop) {
+#ifdef DEBUG_LEVEL1
+		if (!strncmp(prop->name, "mbt_drvdbg", strlen("mbt_drvdbg"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				PRINTM(CMD, "mbt_drvdbg=0x%x\n", data);
+				mbt_drvdbg = data;
+			}
+		}
+#endif
+		else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				init_cfg = (char *)string_data;
+				PRINTM(CMD, "init_cfg=%s\n", init_cfg);
+			}
+		} else if (!strncmp
+			   (prop->name, "cal_cfg_ext", strlen("cal_cfg_ext"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				cal_cfg_ext = (char *)string_data;
+				PRINTM(CMD, "cal_cfg_ext=%s\n", cal_cfg_ext);
+			}
+		} else if (!strncmp(prop->name, "cal_cfg", strlen("cal_cfg"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				cal_cfg = (char *)string_data;
+				PRINTM(CMD, "cal_cfg=%s\n", cal_cfg);
+			}
+		} else if (!strncmp(prop->name, "bt_mac", strlen("bt_mac"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				bt_mac = (char *)string_data;
+				PRINTM(CMD, "bt_mac=%s\n", bt_mac);
+			}
+		} else if (!strncmp(prop->name, "btindrst", strlen("btindrst"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				btindrst = data;
+				PRINTM(CMD, "btindrst=%d\n", btindrst);
+			}
+		} else if (!strncmp(prop->name, "btpmic", strlen("btpmic"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				btpmic = data;
+				PRINTM(CMD, "btpmic=%d\n", btpmic);
+			}
+		}
+	}
+	LEAVE();
+	return;
+}
+#endif
+
+/**
+ *  @brief This function initializes the adapter structure
+ *  and set default value to the member of adapter.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    N/A
+ */
+static void
+bt_init_adapter(bt_private *priv)
+{
+	ENTER();
+#ifdef CONFIG_OF
+	bt_init_from_dev_tree();
+#endif
+	skb_queue_head_init(&priv->adapter->tx_queue);
+	skb_queue_head_init(&priv->adapter->pending_queue);
+	priv->adapter->tx_lock = FALSE;
+	priv->adapter->ps_state = PS_AWAKE;
+	priv->adapter->suspend_fail = FALSE;
+	priv->adapter->is_suspended = FALSE;
+	priv->adapter->hs_skip = 0;
+	priv->adapter->num_cmd_timeout = 0;
+	priv->adapter->fwdump_fname = NULL;
+	init_waitqueue_head(&priv->adapter->cmd_wait_q);
+	LEAVE();
+}
+
+/**
+ *  @brief This function initializes firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_fw(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (fw == 0) {
+		sbi_enable_host_int(priv);
+		goto done;
+	}
+	sbi_disable_host_int(priv);
+	if ((priv->card_type == CARD_TYPE_SD8787) ||
+	    (priv->card_type == CARD_TYPE_SD8777))
+		priv->fw_crc_check = fw_crc_check;
+	if (sbi_download_fw(priv)) {
+		PRINTM(ERROR, " FW failed to be download!\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+done:
+	LEAVE();
+	return ret;
+}
+
+#define FW_POLL_TRIES 100
+#define SD8897_FW_RESET_REG  0x0E8
+#define SD8887_FW_RESET_REG  0x0B6
+#define SD8977_SD8978_SD8997_FW_RESET_REG  0x0EE
+#define SD8887_SD8897_FW_RESET_VAL  1
+#define SD8977_SD8978_SD8997_FW_RESET_VAL  0x99
+
+/**
+ *  @brief This function reload firmware
+ *
+ *  @param priv   A pointer to bt_private
+ *  @param mode   FW reload mode
+ *
+ *  @return       0--success, otherwise failure
+ */
+static int
+bt_reload_fw(bt_private *priv, int mode)
+{
+	int ret = 0, tries = 0;
+	u8 value = 1;
+	u32 reset_reg = 0;
+	u8 reset_val = 0;
+
+	ENTER();
+	if ((mode != FW_RELOAD_SDIO_INBAND_RESET) &&
+	    (mode != FW_RELOAD_NO_EMULATION)) {
+		PRINTM(ERROR, "Invalid fw reload mode=%d\n", mode);
+		return -EFAULT;
+	}
+
+    /** flush pending tx_queue */
+	skb_queue_purge(&priv->adapter->tx_queue);
+	if (mode == FW_RELOAD_SDIO_INBAND_RESET) {
+		if (priv->card_type == CARD_TYPE_SD8887) {
+			reset_reg = SD8887_FW_RESET_REG;
+			reset_val = SD8887_SD8897_FW_RESET_VAL;
+		} else if (priv->card_type == CARD_TYPE_SD8897) {
+			reset_reg = SD8897_FW_RESET_REG;
+			reset_val = SD8887_SD8897_FW_RESET_VAL;
+		} else if ((priv->card_type == CARD_TYPE_SD8977) ||
+			   (priv->card_type == CARD_TYPE_SD8997) ||
+			   (priv->card_type == CARD_TYPE_SD8978) ||
+			   (priv->card_type == CARD_TYPE_SD8987)) {
+			reset_reg = SD8977_SD8978_SD8997_FW_RESET_REG;
+			reset_val = SD8977_SD8978_SD8997_FW_RESET_VAL;
+		}
+		sbi_disable_host_int(priv);
+	    /** Wake up firmware firstly */
+		sbi_wakeup_firmware(priv);
+
+	/** wait SOC fully wake up */
+		for (tries = 0; tries < FW_POLL_TRIES; ++tries) {
+			ret = sd_write_reg(priv, reset_reg, 0xba);
+			if (!ret) {
+				ret = sd_read_reg(priv, reset_reg, &value);
+				if (!ret && (value == 0xba)) {
+					PRINTM(MSG, "Fw wake up\n");
+					break;
+				}
+			}
+			udelay(1000);
+		}
+
+		ret = sd_write_reg(priv, reset_reg, reset_val);
+		if (ret) {
+			PRINTM(ERROR, "Failed to write register.\n");
+			goto done;
+		}
+
+	    /** Poll register around 1 ms */
+		for (; tries < FW_POLL_TRIES; ++tries) {
+			ret = sd_read_reg(priv, reset_reg, &value);
+			if (ret) {
+				PRINTM(ERROR, "Failed to read register.\n");
+				goto done;
+			}
+			if (value == 0)
+			    /** FW is ready */
+				break;
+			udelay(1000);
+		}
+		if (value) {
+			PRINTM(ERROR,
+			       "Failed to poll FW reset register %X=0x%x\n",
+			       reset_reg, value);
+			ret = -EFAULT;
+			goto done;
+		}
+	}
+
+	sbi_enable_host_int(priv);
+	/** reload FW */
+	ret = bt_init_fw(priv);
+	if (ret) {
+		PRINTM(ERROR, "Re download firmware failed.\n");
+		ret = -EFAULT;
+	}
+	LEAVE();
+	return ret;
+done:
+	sbi_enable_host_int(priv);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function request to reload firmware
+ *
+ *  @param priv   A pointer to bt_private
+ *  @param mode   fw reload mode.
+ *
+ *  @return         N/A
+ */
+void
+bt_request_fw_reload(bt_private *priv, int mode)
+{
+	ENTER();
+	if (mode == FW_RELOAD_WITH_EMULATION) {
+		bt_fw_reload = FW_RELOAD_WITH_EMULATION;
+		PRINTM(MSG, "BT: FW reload with re-emulation...\n");
+		LEAVE();
+		return;
+	}
+	/** Reload FW */
+	priv->fw_reload = TRUE;
+	if (bt_reload_fw(priv, mode)) {
+		PRINTM(ERROR, "FW reload fail\n");
+		goto done;
+	}
+	priv->fw_reload = FALSE;
+	/** Other operation here? */
+done:
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function frees the structure of adapter
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    N/A
+ */
+void
+bt_free_adapter(bt_private *priv)
+{
+	bt_adapter *adapter = priv->adapter;
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	kfree(adapter->tx_buffer);
+	kfree(adapter->hw_regs_buf);
+	/* Free allocated memory for fwdump filename */
+	if (adapter->fwdump_fname) {
+		kfree(adapter->fwdump_fname);
+		adapter->fwdump_fname = NULL;
+	}
+	/* Free the adapter object itself */
+	kfree(adapter);
+	priv->adapter = NULL;
+
+	LEAVE();
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+/**
+ *  @brief This function handles the BT ioctl
+ *
+ *  @param hdev     A pointer to hci_dev structure
+ *  @cmd            ioctl cmd
+ *  @arg            argument
+ *  @return    -ENOIOCTLCMD
+ */
+static int
+bt_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	ENTER();
+	LEAVE();
+	return -ENOIOCTLCMD;
+}
+#endif
+
+/**
+ *  @brief This function handles the wrapper_dev ioctl
+ *
+ *  @param hev     A pointer to wrapper_dev structure
+ *  @cmd            ioctl cmd
+ *  @arg            argument
+ *  @return    -ENOIOCTLCMD
+ */
+static int
+mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, void *arg)
+{
+	bt_private *priv = NULL;
+	int ret = 0;
+
+	ENTER();
+
+	if (!m_dev || !m_dev->driver_data) {
+		PRINTM(ERROR, "Ioctl for unknown device (m_dev=NULL)\n");
+		ret = -ENODEV;
+		goto done;
+	}
+	priv = (bt_private *)m_dev->driver_data;
+	if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+		PRINTM(ERROR, "HCI_RUNNING not set, flag=0x%lx\n",
+		       m_dev->flags);
+		ret = -EBUSY;
+		goto done;
+	}
+	PRINTM(INFO, "IOCTL: cmd=%d\n", cmd);
+	switch (cmd) {
+	default:
+		break;
+	}
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function handles BT destruct
+ *
+ *  @param hdev    A pointer to hci_dev structure
+ *
+ *  @return    N/A
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+static void
+bt_destruct(struct hci_dev *hdev)
+{
+	ENTER();
+	LEAVE();
+	return;
+}
+#endif
+
+/**
+ *  @brief This function handles wrapper device destruct
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    N/A
+ */
+static void
+mdev_destruct(struct m_dev *m_dev)
+{
+	ENTER();
+	LEAVE();
+	return;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+/**
+ *  @brief This function handles the BT transmit
+ *
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    BT_STATUS_SUCCESS or other error no.
+ */
+static int
+bt_send_frame(struct sk_buff *skb)
+#else
+/**
+ *  @brief This function handles the BT transmit
+ *
+ *  @param hdev    A pointer to hci_dev structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    BT_STATUS_SUCCESS or other error no.
+ */
+static int
+bt_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+#endif
+{
+	bt_private *priv = NULL;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+	struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+#else
+	skb->dev = (void *)hdev;
+#endif
+
+	ENTER();
+	PRINTM(DATA, "bt_send_frame %s: Type=%d, len=%d\n", hdev->name,
+	       bt_cb(skb)->pkt_type, skb->len);
+	DBG_HEXDUMP(CMD_D, "bt_send_frame", skb->data, skb->len);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+	if (!hdev || !hci_get_drvdata(hdev)) {
+#else
+	if (!hdev || !hdev->driver_data) {
+#endif
+		PRINTM(ERROR, "Frame for unknown HCI device (hdev=NULL)\n");
+		LEAVE();
+		return -ENODEV;
+	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+	priv = (bt_private *)hci_get_drvdata(hdev);
+#else
+	priv = (bt_private *)hdev->driver_data;
+#endif
+	if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+		PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n",
+		       hdev->flags);
+		LEAVE();
+		return -EBUSY;
+	}
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	}
+	if (priv->adapter->tx_lock == TRUE)
+		skb_queue_tail(&priv->adapter->pending_queue, skb);
+	else
+		bt_queue_frame(priv, skb);
+
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handles the wrapper device transmit
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    BT_STATUS_SUCCESS or other error no.
+ */
+static int
+mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb)
+{
+	bt_private *priv = NULL;
+
+	ENTER();
+	if (!m_dev || !m_dev->driver_data) {
+		PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n");
+		LEAVE();
+		return -ENODEV;
+	}
+	priv = (bt_private *)m_dev->driver_data;
+	if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+		PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n",
+		       m_dev->flags);
+		LEAVE();
+		return -EBUSY;
+	}
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		m_dev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		m_dev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		m_dev->stat.sco_tx++;
+		break;
+	}
+
+	if (priv->adapter->tx_lock == TRUE)
+		skb_queue_tail(&priv->adapter->pending_queue, skb);
+	else
+		bt_queue_frame(priv, skb);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function flushes the transmit queue
+ *
+ *  @param hdev     A pointer to hci_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+bt_flush(struct hci_dev *hdev)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+	bt_private *priv = (bt_private *)hci_get_drvdata(hdev);
+#else
+	bt_private *priv = (bt_private *)hdev->driver_data;
+#endif
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	skb_queue_purge(&priv->adapter->pending_queue);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function flushes the transmit queue
+ *
+ *  @param m_dev     A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+mdev_flush(struct m_dev *m_dev)
+{
+	bt_private *priv = (bt_private *)m_dev->driver_data;
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	skb_queue_purge(&priv->adapter->pending_queue);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function closes the bluetooth device
+ *
+ *  @param hdev    A pointer to hci_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+bt_close(struct hci_dev *hdev)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+	bt_private *priv = (bt_private *)hci_get_drvdata(hdev);
+#else
+	bt_private *priv = (bt_private *)hdev->driver_data;
+#endif
+
+	ENTER();
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 0)
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) {
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+#endif
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	module_put(THIS_MODULE);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function closes the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+mdev_close(struct m_dev *m_dev)
+{
+
+	ENTER();
+	mdev_req_lock(m_dev);
+	if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) {
+		mdev_req_unlock(m_dev);
+		LEAVE();
+		return 0;
+	}
+
+	if (m_dev->flush)
+		m_dev->flush(m_dev);
+	/* wait up pending read and unregister char dev */
+	wake_up_interruptible(&m_dev->req_wait_q);
+	/* Drop queues */
+	skb_queue_purge(&m_dev->rx_q);
+	if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) {
+		mdev_req_unlock(m_dev);
+		LEAVE();
+		return 0;
+	}
+	module_put(THIS_MODULE);
+	m_dev->flags = 0;
+	mdev_req_unlock(m_dev);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function opens the bluetooth device
+ *
+ *  @param hdev    A pointer to hci_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+static int
+bt_open(struct hci_dev *hdev)
+{
+	ENTER();
+	if (try_module_get(THIS_MODULE) == 0)
+		return BT_STATUS_FAILURE;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 0)
+	set_bit(HCI_RUNNING, &hdev->flags);
+#endif
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function opens the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+static int
+mdev_open(struct m_dev *m_dev)
+{
+	ENTER();
+
+	if (try_module_get(THIS_MODULE) == 0)
+		return BT_STATUS_FAILURE;
+
+	set_bit(HCI_RUNNING, &m_dev->flags);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function queries the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @param arg     arguement
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+void
+mdev_query(struct m_dev *m_dev, void *arg)
+{
+	struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer;
+
+	ENTER();
+	if (copy_to_user(arg, &mbt_dev->type, sizeof(mbt_dev->type)))
+		PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n");
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function initializes the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+void
+init_m_dev(struct m_dev *m_dev)
+{
+	m_dev->dev_pointer = NULL;
+	m_dev->driver_data = NULL;
+	m_dev->dev_type = 0;
+	m_dev->spec_type = 0;
+	skb_queue_head_init(&m_dev->rx_q);
+	init_waitqueue_head(&m_dev->req_wait_q);
+	init_waitqueue_head(&m_dev->rx_wait_q);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+	init_MUTEX(&m_dev->req_lock);
+#else
+	sema_init(&m_dev->req_lock, 1);
+#endif
+	spin_lock_init(&m_dev->rxlock);
+	memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats));
+	m_dev->open = mdev_open;
+	m_dev->close = mdev_close;
+	m_dev->flush = mdev_flush;
+	m_dev->send = mdev_send_frame;
+	m_dev->destruct = mdev_destruct;
+	m_dev->ioctl = mdev_ioctl;
+	m_dev->query = mdev_query;
+	m_dev->owner = THIS_MODULE;
+
+}
+
+/**
+ *  @brief This function handles the major job in bluetooth driver.
+ *  it handles the event generated by firmware, rx data received
+ *  from firmware and tx data sent from kernel.
+ *
+ *  @param data    A pointer to bt_thread structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+static int
+bt_service_main_thread(void *data)
+{
+	bt_thread *thread = data;
+	bt_private *priv = thread->priv;
+	bt_adapter *adapter = priv->adapter;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
+	wait_queue_t wait;
+#else
+	wait_queue_entry_t wait;
+#endif
+	struct sk_buff *skb;
+	ENTER();
+	bt_activate_thread(thread);
+	init_waitqueue_entry(&wait, current);
+	current->flags |= PF_NOFREEZE;
+
+	for (;;) {
+		add_wait_queue(&thread->waitQ, &wait);
+		OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE);
+		if (priv->adapter->WakeupTries ||
+		    ((!priv->adapter->IntCounter) &&
+		     (!priv->bt_dev.tx_dnld_rdy ||
+		      skb_queue_empty(&priv->adapter->tx_queue))
+		    )) {
+			PRINTM(INFO, "Main: Thread sleeping...\n");
+			schedule();
+		}
+		OS_SET_THREAD_STATE(TASK_RUNNING);
+		remove_wait_queue(&thread->waitQ, &wait);
+		if (kthread_should_stop() || adapter->SurpriseRemoved) {
+			PRINTM(INFO, "main-thread: break from main thread: "
+			       "SurpriseRemoved=0x%x\n",
+			       adapter->SurpriseRemoved);
+			break;
+		}
+
+		PRINTM(INFO, "Main: Thread waking up...\n");
+
+		if (priv->adapter->IntCounter) {
+			OS_INT_DISABLE;
+			adapter->IntCounter = 0;
+			OS_INT_RESTORE;
+			sbi_get_int_status(priv);
+		} else if ((priv->adapter->ps_state == PS_SLEEP) &&
+			   (!skb_queue_empty(&priv->adapter->tx_queue)
+			   )) {
+			priv->adapter->WakeupTries++;
+			sbi_wakeup_firmware(priv);
+			continue;
+		}
+		if (priv->adapter->ps_state == PS_SLEEP)
+			continue;
+		if (priv->bt_dev.tx_dnld_rdy == TRUE) {
+			if (!skb_queue_empty(&priv->adapter->tx_queue)) {
+				skb = skb_dequeue(&priv->adapter->tx_queue);
+				if (skb)
+					send_single_packet(priv, skb);
+			}
+		}
+	}
+	bt_deactivate_thread(thread);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handles the interrupt. it will change PS
+ *  state if applicable. it will wake up main_thread to handle
+ *  the interrupt event as well.
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @return        N/A
+ */
+void
+bt_interrupt(struct m_dev *m_dev)
+{
+	bt_private *priv = (bt_private *)m_dev->driver_data;
+	ENTER();
+	if (!priv || !priv->adapter) {
+		LEAVE();
+		return;
+	}
+	PRINTM(INTR, "*\n");
+	priv->adapter->ps_state = PS_AWAKE;
+	if (priv->adapter->hs_state == HS_ACTIVATED) {
+		PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name);
+		priv->adapter->hs_state = HS_DEACTIVATED;
+	}
+	priv->adapter->WakeupTries = 0;
+	priv->adapter->IntCounter++;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	LEAVE();
+}
+
+/**
+ * @brief  Dynamic release of bt private
+ *
+ * @param kobj          A pointer to kobject structure
+ *
+ * @return                N/A
+ */
+static void
+bt_private_dynamic_release(struct kobject *kobj)
+{
+	bt_private *priv = container_of(kobj, bt_private, kobj);
+	ENTER();
+	PRINTM(INFO, "free bt priv\n");
+	kfree(priv);
+	LEAVE();
+}
+
+static struct kobj_type ktype_bt_private_dynamic = {
+	.release = bt_private_dynamic_release,
+};
+
+/**
+ * @brief  Allocation of bt private
+ *
+ * @param           	N/A
+ *
+ * @return              bt_private
+ */
+static bt_private *
+bt_alloc_priv(void)
+{
+	bt_private *priv;
+	ENTER();
+	priv = kzalloc(sizeof(bt_private), GFP_KERNEL);
+	if (priv) {
+		kobject_init(&priv->kobj, &ktype_bt_private_dynamic);
+		PRINTM(INFO, "alloc bt priv\n");
+	}
+	LEAVE();
+	return priv;
+}
+
+/**
+ * @brief  Get bt private structure
+ *
+ * @param priv          A pointer to bt_private structure
+ *
+ * @return              kobject structure
+ */
+struct kobject *
+bt_priv_get(bt_private *priv)
+{
+	PRINTM(INFO, "bt priv get object");
+	return kobject_get(&priv->kobj);
+}
+
+/**
+ * @brief  Get bt private structure
+ *
+ * @param priv          A pointer to bt_private structure
+ *
+ * @return              N/A
+ */
+void
+bt_priv_put(bt_private *priv)
+{
+	PRINTM(INFO, "bt priv put object");
+	kobject_put(&priv->kobj);
+}
+
+/**
+ *  @brief This function send init commands to firmware
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @return       BT_STATUS_SUCESS/BT_STATUS_FAILURE
+ */
+int
+bt_init_cmd(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+	if (ret < 0) {
+		PRINTM(FATAL, "Module cfg command send failed!\n");
+		goto done;
+	}
+	if (btindrst != -1) {
+		ret = bt_set_independent_reset(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "Independent reset failed!\n");
+			goto done;
+		}
+	}
+	if (btpmic
+	    && ((priv->card_type == CARD_TYPE_SD8997) ||
+		(priv->card_type == CARD_TYPE_SD8977) ||
+		(priv->card_type == CARD_TYPE_SD8978))
+		) {
+		if (BT_STATUS_SUCCESS != bt_pmic_configure(priv)) {
+			PRINTM(FATAL, "BT: PMIC Configure failed \n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	ret = bt_set_ble_deepsleep(priv, deep_sleep ? TRUE : FALSE);
+	if (ret < 0) {
+		PRINTM(FATAL, "%s BLE deepsleep failed!\n",
+		       deep_sleep ? "Enable" : "Disable");
+		goto done;
+	}
+	if (psmode) {
+		priv->bt_dev.psmode = TRUE;
+		priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME;
+		ret = bt_enable_ps(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "Enable PS mode failed!\n");
+			goto done;
+		}
+	}
+#if defined(SDIO_SUSPEND_RESUME)
+	priv->bt_dev.gpio_gap = DEF_GPIO_GAP;
+	ret = bt_send_hscfg_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "Send HSCFG failed!\n");
+		goto done;
+	}
+#endif
+	priv->bt_dev.sdio_pull_cfg = 0xffffffff;
+	priv->bt_dev.sdio_pull_ctrl = 0;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reinit firmware after redownload firmware
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @return       BT_STATUS_SUCESS/BT_STATUS_FAILURE
+ */
+int
+bt_reinit_fw(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	priv->adapter->tx_lock = FALSE;
+	priv->adapter->ps_state = PS_AWAKE;
+	priv->adapter->suspend_fail = FALSE;
+	priv->adapter->is_suspended = FALSE;
+	priv->adapter->hs_skip = 0;
+	priv->adapter->num_cmd_timeout = 0;
+
+	ret = bt_init_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "BT init command failed!\n");
+		goto done;
+	}
+	/* block all the packet from bluez */
+	if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext)
+		priv->adapter->tx_lock = TRUE;
+
+	if (init_cfg)
+		if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+			PRINTM(FATAL,
+			       "BT: Set user init data and param failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+
+	if (cal_cfg) {
+		if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+			PRINTM(FATAL, "BT: Set cal data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	if (bt_mac) {
+		PRINTM(INFO,
+		       "Set BT mac_addr from insmod parametre bt_mac = %s\n",
+		       bt_mac);
+		if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) {
+			PRINTM(FATAL,
+			       "BT: Fail to set mac address from insmod parametre\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	if (cal_cfg_ext) {
+		if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) {
+			PRINTM(FATAL, "BT: Set cal ext data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) {
+		priv->adapter->tx_lock = FALSE;
+		bt_restore_tx_queue(priv);
+	}
+	bt_get_fw_version(priv);
+	snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN,
+		 mbt_driver_version, fw_version);
+done:
+	return ret;
+}
+
+/**
+ *  @brief Module configuration and register device
+ *
+ *  @param priv      A Pointer to bt_private structure
+ *  @return      BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_conf_dpc(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct hci_dev *hdev = NULL;
+	unsigned char dev_type = 0;
+
+	ENTER();
+
+	priv->bt_dev.tx_dnld_rdy = TRUE;
+	if (priv->fw_reload) {
+		bt_reinit_fw(priv);
+		LEAVE();
+		return ret;
+	}
+
+	if (drv_mode & DRV_MODE_BT) {
+		hdev = hci_alloc_dev();
+		if (!hdev) {
+			PRINTM(FATAL, "Can not allocate HCI device\n");
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		hdev->open = bt_open;
+		hdev->close = bt_close;
+		hdev->flush = bt_flush;
+		hdev->send = bt_send_frame;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+		hdev->destruct = bt_destruct;
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+		hdev->ioctl = bt_ioctl;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+		hci_set_drvdata(hdev, priv);
+#else
+		hdev->driver_data = priv;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+		hdev->owner = THIS_MODULE;
+#endif
+		priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE;
+		priv->bt_dev.m_dev[BT_SEQ].spec_type = BLUEZ_SPEC;
+		priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)hdev;
+		priv->bt_dev.m_dev[BT_SEQ].driver_data = priv;
+		priv->bt_dev.m_dev[BT_SEQ].read_continue_flag = 0;
+	}
+
+	dev_type = HCI_SDIO;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
+	if (hdev)
+		hdev->bus = dev_type;
+#else
+	if (hdev)
+		hdev->type = dev_type;
+#endif /* >= 2.6.34 */
+
+	ret = bt_init_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "BT init command failed!\n");
+		goto done;
+	}
+
+	/** Process device tree init parameters before register hci device.
+	 *  Since uplayer device has not yet registered, no need to block tx queue.
+	 * */
+	if (init_cfg)
+		if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+			PRINTM(FATAL,
+			       "BT: Set user init data and param failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	if (cal_cfg) {
+		if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+			PRINTM(FATAL, "BT: Set cal data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	} else if (bt_mac) {
+		PRINTM(INFO,
+		       "Set BT mac_addr from insmod parametre bt_mac = %s\n",
+		       bt_mac);
+		if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) {
+			PRINTM(FATAL,
+			       "BT: Fail to set mac address from insmod parametre\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cal_cfg_ext) {
+		if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) {
+			PRINTM(FATAL, "BT: Set cal ext data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	/* Get FW version */
+	bt_get_fw_version(priv);
+	snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN,
+		 mbt_driver_version, fw_version);
+
+	if (hdev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
+		hdev->dev_type = priv->bt_dev.devType;
+#endif
+		ret = hci_register_dev(hdev);
+		if (ret < 0) {
+			PRINTM(FATAL, "Can not register HCI device\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		snprintf((char *)priv->bt_dev.m_dev[BT_SEQ].name,
+			 sizeof(priv->bt_dev.m_dev[BT_SEQ].name), hdev->name);
+		bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ);
+	}
+
+done:
+	LEAVE();
+	return ret;
+err_kmalloc:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function adds the card. it will probe the
+ *  card, allocate the bt_priv and initialize the device.
+ *
+ *  @param card    A pointer to card
+ *  @return        A pointer to bt_private structure
+ */
+
+bt_private *
+bt_add_card(void *card)
+{
+	bt_private *priv = NULL;
+	int index = 0;
+
+	ENTER();
+
+	priv = bt_alloc_priv();
+	if (!priv) {
+		PRINTM(FATAL, "Can not allocate priv\n");
+		LEAVE();
+		return NULL;
+	}
+	/* Save the handle */
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == NULL)
+			break;
+	}
+	if (index < MAX_BT_ADAPTER) {
+		m_priv[index] = priv;
+	} else {
+		PRINTM(ERROR, "Exceeded maximum cards supported!\n");
+		goto err_kmalloc;
+	}
+	/* allocate buffer for bt_adapter */
+	priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL);
+	if (!priv->adapter) {
+		PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->tx_buffer =
+		kzalloc(MAX_TX_BUF_SIZE + DMA_ALIGNMENT, GFP_KERNEL);
+	if (!priv->adapter->tx_buffer) {
+		PRINTM(FATAL, "Allocate buffer for transmit\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->tx_buf =
+		(u8 *)ALIGN_ADDR(priv->adapter->tx_buffer, DMA_ALIGNMENT);
+	priv->adapter->hw_regs_buf =
+		kzalloc(SD_BLOCK_SIZE + DMA_ALIGNMENT, GFP_KERNEL);
+	if (!priv->adapter->hw_regs_buf) {
+		PRINTM(FATAL, "Allocate buffer for INT read buf failed!\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->hw_regs =
+		(u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, DMA_ALIGNMENT);
+	bt_init_adapter(priv);
+
+	PRINTM(INFO, "Starting kthread...\n");
+	priv->MainThread.priv = priv;
+	spin_lock_init(&priv->driver_lock);
+
+	bt_create_thread(bt_service_main_thread, &priv->MainThread,
+			 "bt_main_service");
+
+	/* wait for mainthread to up */
+	while (!priv->MainThread.pid)
+		os_sched_timeout(1);
+
+	sdio_update_card_type(priv, card);
+	/* Update driver version */
+	if (priv->card_type == CARD_TYPE_SD8787)
+		memcpy(mbt_driver_version, CARD_SD8787, strlen(CARD_SD8787));
+	else if (priv->card_type == CARD_TYPE_SD8777)
+		memcpy(mbt_driver_version, CARD_SD8777, strlen(CARD_SD8777));
+	else if (priv->card_type == CARD_TYPE_SD8887)
+		memcpy(mbt_driver_version, CARD_SD8887, strlen(CARD_SD8887));
+	else if (priv->card_type == CARD_TYPE_SD8897)
+		memcpy(mbt_driver_version, CARD_SD8897, strlen(CARD_SD8897));
+	else if (priv->card_type == CARD_TYPE_SD8797)
+		memcpy(mbt_driver_version, CARD_SD8797, strlen(CARD_SD8797));
+	else if (priv->card_type == CARD_TYPE_SD8977)
+		memcpy(mbt_driver_version, CARD_SD8977, strlen(CARD_SD8977));
+	else if (priv->card_type == CARD_TYPE_SD8978)
+		memcpy(mbt_driver_version, CARD_SD8978, strlen(CARD_SD8978));
+	else if (priv->card_type == CARD_TYPE_SD8997)
+		memcpy(mbt_driver_version, CARD_SD8997, strlen(CARD_SD8997));
+	else if (priv->card_type == CARD_TYPE_SD8987)
+		memcpy(mbt_driver_version, CARD_SD8987, strlen(CARD_SD8987));
+
+	if (BT_STATUS_SUCCESS != sdio_get_sdio_device(priv))
+		goto err_kmalloc;
+
+	/** user config file */
+	init_waitqueue_head(&priv->init_user_conf_wait_q);
+
+	priv->bt_dev.card = card;
+
+	((struct sdio_mmc_card *)card)->priv = priv;
+	priv->adapter->sd_ireg = 0;
+	/*
+	 * Register the device. Fillup the private data structure with
+	 * relevant information from the card and request for the required
+	 * IRQ.
+	 */
+	if (sbi_register_dev(priv) < 0) {
+		PRINTM(FATAL, "Failed to register bt device!\n");
+		goto err_registerdev;
+	}
+	if (bt_init_fw(priv)) {
+		PRINTM(FATAL, "BT Firmware Init Failed\n");
+		goto err_init_fw;
+	}
+	LEAVE();
+	return priv;
+
+err_init_fw:
+	clean_up_m_devs(priv);
+	bt_proc_remove(priv);
+	PRINTM(INFO, "Unregister device\n");
+	sbi_unregister_dev(priv);
+err_registerdev:
+	((struct sdio_mmc_card *)card)->priv = NULL;
+	/* Stop the thread servicing the interrupts */
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid)
+		os_sched_timeout(1);
+err_kmalloc:
+	if (priv->adapter)
+		bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return NULL;
+}
+
+/**
+ *  @brief This function send hardware remove event
+ *
+ *  @param priv    A pointer to bt_private
+ *  @return        N/A
+ */
+void
+bt_send_hw_remove_event(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	struct hci_dev *hdev = NULL;
+	ENTER();
+	if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		LEAVE();
+		return;
+	}
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC)
+		hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer;
+#define HCI_HARDWARE_ERROR_EVT  0x10
+#define HCI_HARDWARE_REMOVE     0x24
+	skb = bt_skb_alloc(3, GFP_ATOMIC);
+	skb->data[0] = HCI_HARDWARE_ERROR_EVT;
+	skb->data[1] = 1;
+	skb->data[2] = HCI_HARDWARE_REMOVE;
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+	skb_put(skb, 3);
+	if (hdev) {
+		skb->dev = (void *)hdev;
+		PRINTM(MSG, "Send HW ERROR event\n");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+		hci_recv_frame(skb);
+#else
+		hci_recv_frame(hdev, skb);
+#endif
+		os_sched_timeout(5);
+		hdev->stat.byte_rx += 3;
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function removes the card.
+ *
+ *  @param card    A pointer to card
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_remove_card(void *card)
+{
+	bt_private *priv = (bt_private *)card;
+	int index;
+	ENTER();
+	if (!priv) {
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv->adapter->SurpriseRemoved = TRUE;
+
+	bt_send_hw_remove_event(priv);
+	wake_up_interruptible(&priv->adapter->cmd_wait_q);
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid) {
+		os_sched_timeout(1);
+		wake_up_interruptible(&priv->MainThread.waitQ);
+	}
+
+	bt_proc_remove(priv);
+	PRINTM(INFO, "Unregister device\n");
+	sbi_unregister_dev(priv);
+	clean_up_m_devs(priv);
+	PRINTM(INFO, "Free Adapter\n");
+	bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function initializes module.
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_module(void)
+{
+	int ret = BT_STATUS_SUCCESS;
+	int index;
+	ENTER();
+	PRINTM(MSG, "BT: Loading driver\n");
+	/* Init the bt_private pointer array first */
+	for (index = 0; index < MAX_BT_ADAPTER; index++)
+		m_priv[index] = NULL;
+	bt_root_proc_init();
+
+	/** create char device class */
+	chardev_class = class_create(THIS_MODULE, MODULE_NAME);
+	if (IS_ERR(chardev_class)) {
+		PRINTM(ERROR, "Unable to allocate class\n");
+		bt_root_proc_remove();
+		ret = PTR_ERR(chardev_class);
+		goto done;
+	}
+
+	if (sbi_register() == NULL) {
+		bt_root_proc_remove();
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+done:
+	if (ret)
+		PRINTM(MSG, "BT: Driver loading failed\n");
+	else
+		PRINTM(MSG, "BT: Driver loaded successfully\n");
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function cleans module
+ *
+ *  @return        N/A
+ */
+static void
+bt_exit_module(void)
+{
+	bt_private *priv;
+	int index;
+	ENTER();
+	PRINTM(MSG, "BT: Unloading driver\n");
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		priv = m_priv[index];
+		if (!priv)
+			continue;
+		if (priv && !priv->adapter->SurpriseRemoved) {
+			if (BT_STATUS_SUCCESS == bt_send_reset_command(priv))
+				bt_send_module_cfg_cmd(priv,
+						       MODULE_SHUTDOWN_REQ);
+		}
+		sbi_disable_host_int(priv);
+
+	}
+
+	sbi_unregister();
+
+	bt_root_proc_remove();
+	class_destroy(chardev_class);
+	PRINTM(MSG, "BT: Driver unloaded\n");
+	LEAVE();
+}
+
+module_init(bt_init_module);
+module_exit(bt_exit_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+module_param(fw, int, 0);
+MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware");
+module_param(fw_crc_check, int, 0);
+MODULE_PARM_DESC(fw_crc_check,
+		 "1: Enable FW download CRC check (default); 0: Disable FW download CRC check");
+module_param(psmode, int, 0);
+MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode");
+module_param(deep_sleep, int, 0);
+MODULE_PARM_DESC(deep_sleep, "1: Enable deep sleep; 0: Disable deep sleep");
+#ifdef CONFIG_OF
+module_param(dts_enable, int, 0);
+MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS");
+#endif
+#ifdef	DEBUG_LEVEL1
+module_param(mbt_drvdbg, uint, 0);
+MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL");
+#endif
+#ifdef SDIO_SUSPEND_RESUME
+module_param(mbt_pm_keep_power, int, 0);
+MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power");
+#endif
+module_param(init_cfg, charp, 0);
+MODULE_PARM_DESC(init_cfg, "BT init config file name");
+module_param(cal_cfg, charp, 0);
+MODULE_PARM_DESC(cal_cfg, "BT calibrate file name");
+module_param(cal_cfg_ext, charp, 0);
+MODULE_PARM_DESC(cal_cfg_ext, "BT calibrate ext file name");
+module_param(bt_mac, charp, 0660);
+MODULE_PARM_DESC(bt_mac, "BT init mac address");
+module_param(drv_mode, int, 0);
+MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE;");
+module_param(bt_fw_reload, int, 0);
+MODULE_PARM_DESC(bt_fw_reload,
+		 "0: disable fw_reload; 1: enable fw reload feature");
+module_param(btindrst, int, 0);
+MODULE_PARM_DESC(btindrst,
+		 "Independent reset configuration; high byte:GPIO pin number;low byte:0x0:disable, 0x1:out-band reset, 0x2:in-band reset.");
+module_param(btpmic, int, 0);
+MODULE_PARM_DESC(btpmic,
+		 "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware (default)");
+module_param(bt_fw_serial, int, 0);
+MODULE_PARM_DESC(bt_fw_serial,
+		 "0: Support parallel download FW; 1: Support serial download FW");
diff --git a/bt_sd8987/bt/bt_proc.c b/bt_sd8987/bt/bt_proc.c
new file mode 100644
index 0000000..0264c94
--- /dev/null
+++ b/bt_sd8987/bt/bt_proc.c
@@ -0,0 +1,723 @@
+/** @file bt_proc.c
+  *
+  * @brief This file handle the functions for proc files
+  *
+  * Copyright (C) 2007-2019, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/proc_fs.h>
+
+#include "bt_drv.h"
+#include "bt_sdio.h"
+
+/** proc diretory root */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+#define PROC_DIR NULL
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+#define PROC_DIR (&proc_root)
+#else
+#define PROC_DIR proc_net
+#endif
+
+/** Proc mbt directory entry */
+static struct proc_dir_entry *proc_mbt;
+
+#define     CMD52_STR_LEN   50
+static char cmd52_string[CMD52_STR_LEN];
+
+/** proc data structure */
+struct proc_data {
+	/** Read length */
+	int rdlen;
+	/** Read buffer */
+	char *rdbuf;
+	/** Write length */
+	int wrlen;
+	/** Maximum write length */
+	int maxwrlen;
+	/** Write buffer */
+	char *wrbuf;
+	/** Private structure */
+	struct _bt_private *pbt;
+	void (*on_close) (struct inode *, struct file *);
+};
+
+/** Default file permission */
+#define DEFAULT_FILE_PERM  0644
+
+/** Bluetooth device offset */
+#define OFFSET_BT_DEV		0x01
+/** Bluetooth adapter offset */
+#define OFFSET_BT_ADAPTER	0x02
+/** Show integer */
+#define SHOW_INT		0x10
+/** Show hex */
+#define SHOW_HEX		0x20
+/** Show string */
+#define SHOW_STRING		0x40
+
+/** Device size */
+#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n)
+/** Device address */
+#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n)
+
+/** Adapter size */
+#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n)
+/** Adapter address */
+#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n)
+
+static struct item_data config_items[] = {
+#ifdef	DEBUG_LEVEL1
+	{"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX}
+	,
+#endif
+	{"idle_timeout", item_dev_size(idle_timeout), 0,
+	 item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap),
+	 OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0,
+	 item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0,
+	 item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+
+};
+
+static struct item_data status_items[] = {
+	{"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver),
+	 OFFSET_BT_ADAPTER | SHOW_STRING},
+	{"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0,
+	 item_dev_addr(tx_dnld_rdy),
+	 OFFSET_BT_DEV | SHOW_INT},
+	{"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"hs_state", item_adapter_size(hs_state), 0,
+	 item_adapter_addr(hs_state),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"ps_state", item_adapter_size(ps_state), 0,
+	 item_adapter_addr(ps_state),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"WakeupTries", item_adapter_size(WakeupTries), 0,
+	 item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT},
+	{"irq_recv", item_adapter_size(irq_recv), 0,
+	 item_adapter_addr(irq_recv),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"irq_done", item_adapter_size(irq_done), 0,
+	 item_adapter_addr(irq_done),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"skb_pending", item_adapter_size(skb_pending), 0,
+	 item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT},
+};
+
+static struct item_data debug_items[] = {
+	{"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING},
+};
+
+/**
+ *  @brief convert string to number
+ *
+ *  @param s	pointer to numbered string
+ *  @return	converted number from string s
+ */
+int
+string_to_number(char *s)
+{
+	int r = 0;
+	int base = 0;
+	int pn = 1;
+
+	if (strncmp(s, "-", 1) == 0) {
+		pn = -1;
+		s++;
+	}
+	if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) {
+		base = 16;
+		s += 2;
+	} else
+		base = 10;
+
+	for (s = s; *s != 0; s++) {
+		if ((*s >= '0') && (*s <= '9'))
+			r = (r * base) + (*s - '0');
+		else if ((*s >= 'A') && (*s <= 'F'))
+			r = (r * base) + (*s - 'A' + 10);
+		else if ((*s >= 'a') && (*s <= 'f'))
+			r = (r * base) + (*s - 'a' + 10);
+		else
+			break;
+	}
+
+	return r * pn;
+}
+
+/**
+ *  @brief Create cmd52 string
+ *
+ *  @param priv	A pointer to bt_private structure
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+form_cmd52_string(bt_private *priv)
+{
+	ENTER();
+
+	memset(cmd52_string, 0, CMD52_STR_LEN);
+	snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X",
+		 priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg,
+		 priv->bt_dev.cmd52_val);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/*
+ *  @brief Parse cmd52 string
+ *
+ *  @param buffer  A pointer user buffer
+ *  @param len     Length of user buffer
+ *  @param func    Parsed func number
+ *  @param reg     Parsed reg value
+ *  @param val     Parsed value to set
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+parse_cmd52_string(const char __user * buffer, size_t len,
+		   int *func, int *reg, int *val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	char *string = NULL;
+	char *pos = NULL;
+
+	ENTER();
+
+	string = kzalloc(CMD52_STR_LEN, GFP_KERNEL);
+	if (!string) {
+		PRINTM(ERROR, "BT: Can not alloc mem for cmd52 string\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+	memcpy(string, buffer + strlen("sdcmd52rw="),
+	       len - strlen("sdcmd52rw="));
+	string = strstrip(string);
+
+	*func = -1;
+	*reg = -1;
+	*val = -1;
+
+	/* Get func */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*func = string_to_number(pos);
+
+	/* Get reg */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*reg = string_to_number(pos);
+
+	/* Get val (optional) */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*val = string_to_number(pos);
+	kfree(string);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function handle generic proc file close
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+proc_close(struct inode *inode, struct file *file)
+{
+	struct proc_data *pdata = file->private_data;
+	ENTER();
+	if (pdata) {
+		if (pdata->on_close != NULL)
+			pdata->on_close(inode, file);
+		kfree(pdata->rdbuf);
+		kfree(pdata->wrbuf);
+		kfree(pdata);
+	}
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handle generic proc file read
+ *
+ *  @param file    A pointer to file structure
+ *  @param buffer  A pointer to output buffer
+ *  @param len     number of byte to read
+ *  @param offset  A pointer to offset of file
+ *  @return		number of output data
+ */
+static ssize_t
+proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset)
+{
+	loff_t pos = *offset;
+	struct proc_data *pdata = (struct proc_data *)file->private_data;
+	if ((!pdata->rdbuf) || (pos < 0))
+		return -EINVAL;
+	if (pos >= pdata->rdlen)
+		return 0;
+	if (len > pdata->rdlen - pos)
+		len = pdata->rdlen - pos;
+	if (copy_to_user(buffer, pdata->rdbuf + pos, len))
+		return -EFAULT;
+	*offset = pos + len;
+	return len;
+}
+
+/**
+ *  @brief This function handle generic proc file write
+ *
+ *  @param file    A pointer to file structure
+ *  @param buffer  A pointer to input buffer
+ *  @param len     number of byte to write
+ *  @param offset  A pointer to offset of file
+ *  @return		number of input data
+ */
+static ssize_t
+proc_write(struct file *file,
+	   const char __user * buffer, size_t len, loff_t * offset)
+{
+	loff_t pos = *offset;
+	struct proc_data *pdata = (struct proc_data *)file->private_data;
+	int func = 0, reg = 0, val = 0;
+	int config_data = 0;
+	char *line = NULL;
+
+	if (!pdata->wrbuf || (pos < 0))
+		return -EINVAL;
+	if (pos >= pdata->maxwrlen)
+		return 0;
+	if (len > pdata->maxwrlen - pos)
+		len = pdata->maxwrlen - pos;
+	if (copy_from_user(pdata->wrbuf + pos, buffer, len))
+		return -EFAULT;
+	if (!strncmp(pdata->wrbuf + pos, "fw_reload", strlen("fw_reload"))) {
+		if (!strncmp
+		    (pdata->wrbuf + pos, "fw_reload=", strlen("fw_reload="))) {
+			line = pdata->wrbuf + pos;
+			line += strlen("fw_reload") + 1;
+			config_data = string_to_number(line);
+		} else
+			config_data = FW_RELOAD_SDIO_INBAND_RESET;
+		PRINTM(MSG, "Request fw_reload=%d\n", config_data);
+		bt_request_fw_reload(pdata->pbt, config_data);
+	}
+	if (!strncmp(pdata->wrbuf + pos, "sdcmd52rw=", strlen("sdcmd52rw="))) {
+		parse_cmd52_string(pdata->wrbuf + pos, len, &func, &reg, &val);
+		sd_write_cmd52_val(pdata->pbt, func, reg, val);
+	}
+	if (!strncmp(pdata->wrbuf + pos, "debug_dump", strlen("debug_dump"))) {
+		bt_dump_sdio_regs(pdata->pbt);
+		bt_dump_firmware_info_v2(pdata->pbt);
+	}
+
+	if (pos + len > pdata->wrlen)
+		pdata->wrlen = len + file->f_pos;
+	*offset = pos + len;
+	return len;
+}
+
+/**
+ *  @brief This function handle the generic file close
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return		N/A
+ */
+static void
+proc_on_close(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	struct proc_private_data *priv = PDE_DATA(inode);
+#else
+	struct proc_private_data *priv = PDE(inode)->data;
+#endif
+	struct proc_data *pdata = file->private_data;
+	char *line;
+	int i;
+	ENTER();
+	if (!pdata->wrlen)
+		return;
+	line = pdata->wrbuf;
+	while (line[0]) {
+		for (i = 0; i < priv->num_items; i++) {
+			if (!strncmp
+			    (line, priv->pdata[i].name,
+			     strlen(priv->pdata[i].name))) {
+				line += strlen(priv->pdata[i].name) + 1;
+				if (priv->pdata[i].size == 1)
+					*((u8 *)priv->pdata[i].addr) =
+						(u8)string_to_number(line);
+				else if (priv->pdata[i].size == 2)
+					*((u16 *) priv->pdata[i].addr) =
+						(u16) string_to_number(line);
+				else if (priv->pdata[i].size == 4)
+					*((u32 *)priv->pdata[i].addr) =
+						(u32)string_to_number(line);
+			}
+		}
+		while (line[0] && line[0] != '\n')
+			line++;
+		if (line[0])
+			line++;
+	}
+	if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd
+	    || priv->pbt->bt_dev.sdio_pull_ctrl
+	    || priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) {
+		bt_prepare_command(priv->pbt);
+		wake_up_interruptible(&priv->pbt->MainThread.waitQ);
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function handle the generic file open
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return	BT_STATUS_SUCCESS or other error no.
+ */
+static int
+proc_open(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	struct proc_private_data *priv = PDE_DATA(inode);
+#else
+	struct proc_private_data *priv = PDE(inode)->data;
+#endif
+	struct proc_data *pdata;
+	int i;
+	char *p;
+	u32 val = 0;
+	ENTER();
+	priv->pbt->adapter->skb_pending =
+		skb_queue_len(&priv->pbt->adapter->tx_queue);
+	file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL);
+	if (file->private_data == NULL) {
+		PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+	pdata = (struct proc_data *)file->private_data;
+	pdata->pbt = priv->pbt;
+	pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL);
+	if (pdata->rdbuf == NULL) {
+		PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n");
+		kfree(file->private_data);
+		LEAVE();
+		return -ENOMEM;
+	}
+	if (priv->fileflag == DEFAULT_FILE_PERM) {
+		pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL);
+		if (pdata->wrbuf == NULL) {
+			PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n");
+			kfree(pdata->rdbuf);
+			kfree(file->private_data);
+			return -ENOMEM;
+		}
+		pdata->maxwrlen = priv->bufsize;
+		pdata->on_close = proc_on_close;
+	}
+	p = pdata->rdbuf;
+	for (i = 0; i < priv->num_items; i++) {
+		if (priv->pdata[i].size == 1)
+			val = *((u8 *)priv->pdata[i].addr);
+		else if (priv->pdata[i].size == 2)
+			val = *((u16 *) priv->pdata[i].addr);
+		else if (priv->pdata[i].size == 4)
+			val = *((u32 *)priv->pdata[i].addr);
+		if (priv->pdata[i].flag & SHOW_INT)
+			p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val);
+		else if (priv->pdata[i].flag & SHOW_HEX)
+			p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val);
+		else if (priv->pdata[i].flag & SHOW_STRING) {
+			if (!strncmp
+			    (priv->pdata[i].name, "sdcmd52rw",
+			     strlen("sdcmd52rw"))) {
+				sd_read_cmd52_val(priv->pbt);
+				form_cmd52_string(priv->pbt);
+			}
+			p += sprintf(p, "%s=%s\n", priv->pdata[i].name,
+				     (char *)priv->pdata[i].addr);
+		}
+	}
+	pdata->rdlen = strlen(pdata->rdbuf);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/** Proc read ops */
+static const struct file_operations proc_read_ops = {
+	.read = proc_read,
+	.open = proc_open,
+	.release = proc_close
+};
+
+/** Proc Read-Write ops */
+static const struct file_operations proc_rw_ops = {
+	.read = proc_read,
+	.write = proc_write,
+	.open = proc_open,
+	.release = proc_close
+};
+
+static struct proc_private_data proc_files[] = {
+	{"status", S_IRUGO, 1024,
+	 sizeof(status_items) / sizeof(status_items[0]),
+	 &status_items[0], NULL, &proc_read_ops}
+	,
+	{"config", DEFAULT_FILE_PERM, 512,
+	 sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL,
+	 &proc_rw_ops}
+	,
+	{"debug", DEFAULT_FILE_PERM, 512,
+	 sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL,
+	 &proc_rw_ops}
+	,
+};
+
+/**
+ *  @brief This function initializes proc entry
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param m_dev    A pointer to struct m_dev
+ *  @param seq      Sequence number
+ *
+ *  @return	BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct proc_dir_entry *entry;
+	int i, j;
+
+	ENTER();
+
+	memset(cmd52_string, 0, CMD52_STR_LEN);
+	if (proc_mbt) {
+		priv->dev_proc[seq].proc_entry =
+			proc_mkdir(m_dev->name, proc_mbt);
+		if (!priv->dev_proc[seq].proc_entry) {
+			PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+
+		priv->dev_proc[seq].pfiles =
+			kmalloc(sizeof(proc_files), GFP_ATOMIC);
+		if (!priv->dev_proc[seq].pfiles) {
+			PRINTM(ERROR,
+			       "BT: Could not alloc memory for pfile!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files,
+		       sizeof(proc_files));
+		priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files);
+		for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++)
+			priv->dev_proc[seq].pfiles[j].pdata = NULL;
+		for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
+			priv->dev_proc[seq].pfiles[j].pdata =
+				kmalloc(priv->dev_proc[seq].pfiles[j].
+					num_items * sizeof(struct item_data),
+					GFP_ATOMIC);
+			if (!priv->dev_proc[seq].pfiles[j].pdata) {
+				PRINTM(ERROR,
+				       "BT: Could not alloc memory for pdata!\n");
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata,
+			       (u8 *)proc_files[j].pdata,
+			       priv->dev_proc[seq].pfiles[j].num_items *
+			       sizeof(struct item_data));
+			for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items;
+			     i++) {
+				if (priv->dev_proc[seq].pfiles[j].
+				    pdata[i].flag & OFFSET_BT_DEV)
+					priv->dev_proc[seq].pfiles[j].pdata[i].
+						addr =
+						priv->dev_proc[seq].pfiles[j].
+						pdata[i].offset +
+						(t_ptr)&priv->bt_dev;
+				if (priv->dev_proc[seq].pfiles[j].
+				    pdata[i].flag & OFFSET_BT_ADAPTER)
+					priv->dev_proc[seq].pfiles[j].pdata[i].
+						addr =
+						priv->dev_proc[seq].pfiles[j].
+						pdata[i].offset +
+						(t_ptr)priv->adapter;
+			}
+			priv->dev_proc[seq].pfiles[j].pbt = priv;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+			entry = proc_create_data(proc_files[j].name,
+						 S_IFREG | proc_files[j].
+						 fileflag,
+						 priv->dev_proc[seq].proc_entry,
+						 proc_files[j].fops,
+						 &priv->dev_proc[seq].
+						 pfiles[j]);
+			if (entry == NULL)
+#else
+			entry = create_proc_entry(proc_files[j].name,
+						  S_IFREG | proc_files[j].
+						  fileflag,
+						  priv->dev_proc[seq].
+						  proc_entry);
+			if (entry) {
+				entry->data = &priv->dev_proc[seq].pfiles[j];
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+				entry->owner = THIS_MODULE;
+#endif
+				entry->proc_fops = proc_files[j].fops;
+			} else
+#endif
+				PRINTM(MSG, "BT: Fail to create proc %s\n",
+				       proc_files[j].name);
+		}
+	}
+done:
+	if (ret == BT_STATUS_FAILURE) {
+		if (priv->dev_proc[seq].proc_entry) {
+			remove_proc_entry(m_dev->name, proc_mbt);
+			priv->dev_proc[seq].proc_entry = NULL;
+		}
+		if (priv->dev_proc[seq].pfiles) {
+			for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
+				if (priv->dev_proc[seq].pfiles[j].pdata) {
+					kfree(priv->dev_proc[seq].pfiles[j].
+					      pdata);
+					priv->dev_proc[seq].pfiles[j].pdata =
+						NULL;
+				}
+			}
+			kfree(priv->dev_proc[seq].pfiles);
+			priv->dev_proc[seq].pfiles = NULL;
+		}
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function removes proc interface
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return	N/A
+ */
+void
+bt_proc_remove(bt_private *priv)
+{
+	int j, i;
+
+	ENTER();
+	PRINTM(INFO, "BT: Remove Proc Interface\n");
+	if (proc_mbt) {
+		for (i = 0; i < MAX_RADIO_FUNC; i++) {
+			if (!priv->dev_proc[i].proc_entry)
+				continue;
+			for (j = 0; j < ARRAY_SIZE(proc_files); j++) {
+				remove_proc_entry(proc_files[j].name,
+						  priv->dev_proc[i].proc_entry);
+			}
+			remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt);
+			priv->dev_proc[i].proc_entry = NULL;
+
+			if (priv->dev_proc[i].pfiles) {
+				for (j = 0;
+				     j < priv->dev_proc[i].num_proc_files;
+				     j++) {
+					if (priv->dev_proc[i].pfiles[j].pdata) {
+						kfree(priv->dev_proc[i].
+						      pfiles[j].pdata);
+						priv->dev_proc[i].pfiles[j].
+							pdata = NULL;
+					}
+				}
+				kfree(priv->dev_proc[i].pfiles);
+				priv->dev_proc[i].pfiles = NULL;
+			}
+		}
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function creates proc interface
+ *  directory structure
+ *
+ *  @return		BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_root_proc_init(void)
+{
+	PRINTM(INFO, "BT: Create Proc Interface\n");
+	proc_mbt = proc_mkdir("mbt", PROC_DIR);
+	if (!proc_mbt) {
+		PRINTM(ERROR, "BT: Cannot create proc interface\n");
+		return BT_STATUS_FAILURE;
+	}
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function removes proc interface
+ *  directory structure
+ *
+ *  @return		BT_STATUS_SUCCESS
+ */
+int
+bt_root_proc_remove(void)
+{
+	remove_proc_entry("mbt", PROC_DIR);
+	proc_mbt = NULL;
+	return BT_STATUS_SUCCESS;
+}
diff --git a/bt_sd8987/bt/bt_sdio.h b/bt_sd8987/bt/bt_sdio.h
new file mode 100644
index 0000000..3fced8f
--- /dev/null
+++ b/bt_sd8987/bt/bt_sdio.h
@@ -0,0 +1,435 @@
+/** @file bt_sdio.h
+ *  @brief This file contains SDIO (interface) module
+ *  related macros, enum, and structure.
+ *
+ *  Copyright (C) 2007-2019, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _BT_SDIO_H_
+#define _BT_SDIO_H_
+
+#include <linux/irqreturn.h>
+
+/** SD8787 card type */
+#define CARD_TYPE_SD8787   0x01
+/** SD8777 card type */
+#define CARD_TYPE_SD8777   0x02
+/** SD8887 card type */
+#define CARD_TYPE_SD8887   0x03
+/** SD8897 card type */
+#define CARD_TYPE_SD8897   0x04
+/** SD8797 card type */
+#define CARD_TYPE_SD8797   0x05
+/** SD8977 card type */
+#define CARD_TYPE_SD8977   0x06
+/** SD8997 card type */
+#define CARD_TYPE_SD8997   0x07
+/** SD8987 card type */
+#define CARD_TYPE_SD8987   0x08
+/** SD8978 card type */
+#define CARD_TYPE_SD8978   0x09
+
+/** IRQ return type */
+typedef irqreturn_t IRQ_RET_TYPE;
+/** IRQ return */
+#define IRQ_RET		(return IRQ_HANDLED)
+/** ISR notifier function */
+typedef IRQ_RET_TYPE (*isr_notifier_fn_t) (s32 irq, void *dev_id,
+					   struct pt_regs * reg);
+
+/** SDIO header length */
+#define SDIO_HEADER_LEN			4
+
+/** Interrupt Mode SDIO */
+#define INT_MODE_SDIO       0
+/** Interrupt Mode GPIO */
+#define INT_MODE_GPIO       1
+/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */
+#define GPIO_INT_NEW_MODE   255
+/* SD block size can not bigger than 64 due to buf size limit in firmware */
+/** define SD block size for data Tx/Rx */
+#define SD_BLOCK_SIZE			64
+/** define SD block size for firmware download */
+#define SD_BLOCK_SIZE_FW_DL		256
+
+/** Number of blocks for firmware transfer */
+#define FIRMWARE_TRANSFER_NBLOCK	2
+
+/** Firmware ready */
+#define FIRMWARE_READY			0xfedc
+
+/* Bus Interface Control Reg 0x07 */
+/** SD BUS width 1 */
+#define SD_BUS_WIDTH_1			0x00
+/** SD BUS width 4 */
+#define SD_BUS_WIDTH_4			0x02
+/** SD BUS width mask */
+#define SD_BUS_WIDTH_MASK		0x03
+/** Asynchronous interrupt mode */
+#define ASYNC_INT_MODE			0x20
+
+/** magic register */
+#define CARD_MAGIC_REG          0xF0
+/** magic value */
+#define MAGIC_VAL               0x24
+
+/* Host Control Registers */
+/** Host Control Registers : Configuration */
+#define CONFIGURATION_REG		0x00
+/** Host Control Registers : Host without Command 53 finish host*/
+#define HOST_TO_CARD_EVENT		(0x1U << 3)
+/** Host Control Registers : Host terminates Command 53 */
+#define HOST_TERM_CMD53			(0x1U << 2)
+/** Host Control Registers : Host power up */
+#define HOST_POWER_UP			(0x1U << 1)
+/** Host Control Registers : Host power down */
+#define HOST_POWER_DOWN			(0x1U << 0)
+
+/** Host Control Registers : Host interrupt RSR */
+#define HOST_INT_RSR_REG		0x01
+
+/** Host Control Registers : Upload host interrupt RSR */
+#define UP_LD_HOST_INT_RSR		(0x1U)
+
+/** Host Control Registers : Host interrupt mask */
+#define HOST_INT_MASK_REG		0x02
+
+/** Host Control Registers : Upload host interrupt mask */
+#define UP_LD_HOST_INT_MASK		(0x1U)
+/** Host Control Registers : Download host interrupt mask */
+#define DN_LD_HOST_INT_MASK		(0x2U)
+/** Enable Host interrupt mask */
+#define HIM_ENABLE			(UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK)
+/** Disable Host interrupt mask */
+#define	HIM_DISABLE			0xff
+
+#define HOST_INTSTATUS_REG		0x03
+/** Host Control Registers : Upload host interrupt status */
+#define UP_LD_HOST_INT_STATUS		(0x1U)
+/** Host Control Registers : Download host interrupt status */
+#define DN_LD_HOST_INT_STATUS		(0x2U)
+
+/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/
+#define ENABLE_GPIO_1_INT_MODE  0x88
+/** Scratch reg 3 2  :     Configure GPIO-1 INT*/
+#define SCRATCH_REG_32          0xEE
+
+/** Host Control Registers : Host interrupt status */
+#define HOST_INT_STATUS_REG		0x28
+/** Host Control Registers : Upload CRC error */
+#define UP_LD_CRC_ERR			(0x1U << 2)
+/** Host Control Registers : Upload restart */
+#define UP_LD_RESTART			(0x1U << 1)
+/** Host Control Registers : Download restart */
+#define DN_LD_RESTART			(0x1U << 0)
+
+/* Card Control Registers */
+/** Card Control Registers : Read SQ base address A0 register */
+#define SQ_READ_BASE_ADDRESS_A0_REG		0x40
+/** Card Control Registers : Read SQ base address A1 register */
+#define SQ_READ_BASE_ADDRESS_A1_REG		0x41
+/** Card Control Registers : Read SQ base address A2 register */
+#define SQ_READ_BASE_ADDRESS_A2_REG		0x42
+/** Card Control Registers : Read SQ base address A3 register */
+#define SQ_READ_BASE_ADDRESS_A3_REG		0x43
+/** Card Control Registers : Read SQ base address B0 register */
+#define SQ_READ_BASE_ADDRESS_B0_REG		0x44
+/** Card Control Registers : Read SQ base address B1 register */
+#define SQ_READ_BASE_ADDRESS_B1_REG		0x45
+/** Card Control Registers : Read SQ base address B2 register */
+#define SQ_READ_BASE_ADDRESS_B2_REG		0x46
+/** Card Control Registers : Read SQ base address B3 register */
+#define SQ_READ_BASE_ADDRESS_B3_REG		0x47
+
+/** Card Control Registers : Card status register */
+#define CARD_STATUS_REG					0x30
+/** Card Control Registers : Card I/O ready */
+#define CARD_IO_READY					(0x1U << 3)
+/** Card Control Registers : CIS card ready */
+#define CIS_CARD_RDY					(0x1U << 2)
+/** Card Control Registers : Upload card ready */
+#define UP_LD_CARD_RDY					(0x1U << 1)
+/** Card Control Registers : Download card ready */
+#define DN_LD_CARD_RDY					(0x1U << 0)
+
+/** Card Control Registers : Host interrupt mask register */
+#define HOST_INTERRUPT_MASK_REG			0x34
+/** Card Control Registers : Host power interrupt mask */
+#define HOST_POWER_INT_MASK				(0x1U << 3)
+/** Card Control Registers : Abort card interrupt mask */
+#define ABORT_CARD_INT_MASK				(0x1U << 2)
+/** Card Control Registers : Upload card interrupt mask */
+#define UP_LD_CARD_INT_MASK				(0x1U << 1)
+/** Card Control Registers : Download card interrupt mask */
+#define DN_LD_CARD_INT_MASK				(0x1U << 0)
+
+/** Card Control Registers : Card interrupt status register */
+#define CARD_INTERRUPT_STATUS_REG		0x38
+/** Card Control Registers : Power up interrupt */
+#define POWER_UP_INT					(0x1U << 4)
+/** Card Control Registers : Power down interrupt */
+#define POWER_DOWN_INT					(0x1U << 3)
+
+/** Card Control Registers : Card interrupt RSR register */
+#define CARD_INTERRUPT_RSR_REG			0x3c
+/** Card Control Registers : Power up RSR */
+#define POWER_UP_RSR					(0x1U << 4)
+/** Card Control Registers : Power down RSR */
+#define POWER_DOWN_RSR					(0x1U << 3)
+
+/** Card Control Registers : Debug 0 register */
+#define DEBUG_0_REG						0x70
+/** Card Control Registers : SD test BUS 0 */
+#define SD_TESTBUS0						(0x1U)
+/** Card Control Registers : Debug 1 register */
+#define DEBUG_1_REG						0x71
+/** Card Control Registers : SD test BUS 1 */
+#define SD_TESTBUS1						(0x1U)
+/** Card Control Registers : Debug 2 register */
+#define DEBUG_2_REG						0x72
+/** Card Control Registers : SD test BUS 2 */
+#define SD_TESTBUS2						(0x1U)
+/** Card Control Registers : Debug 3 register */
+#define DEBUG_3_REG						0x73
+/** Card Control Registers : SD test BUS 3 */
+#define SD_TESTBUS3						(0x1U)
+
+/** Host Control Registers : I/O port 0 */
+#define IO_PORT_0_REG			0x78
+/** Host Control Registers : I/O port 1 */
+#define IO_PORT_1_REG			0x79
+/** Host Control Registers : I/O port 2 */
+#define IO_PORT_2_REG			0x7A
+
+/** Firmware status 0 register */
+#define CARD_FW_STATUS0_REG		0x60
+/** Firmware status 1 register */
+#define CARD_FW_STATUS1_REG		0x61
+/** Rx length register */
+#define CARD_RX_LEN_REG			0x62
+/** Rx unit register */
+#define CARD_RX_UNIT_REG		0x63
+/** Card Control Registers : Miscellaneous Configuration Register */
+#define CARD_MISC_CFG_REG		0x6C
+/** Misc. Config Register : Auto Re-enable interrupts */
+#define AUTO_RE_ENABLE_INT		(0x1U << 4)
+
+/** Card Control Registers : Card OCR 0 register */
+#define CARD_OCR_0_REG			0x68
+/** Card Control Registers : Card OCR 1 register */
+#define CARD_OCR_1_REG			0x69
+/** Card Control Registers : Card OCR 3 register */
+#define CARD_OCR_3_REG			0x6A
+/** Card Control Registers : Card config register */
+#define CARD_CONFIG_REG			0x6B
+/** Card Control Registers : Card revision register */
+#define CARD_REVISION_REG		0x5c
+/** Card Control Registers : Command 53 finish G BUS */
+#define CMD53_FINISH_GBUS		(0x1U << 1)
+/** Card Control Registers : SD negative edge */
+#define SD_NEG_EDGE				(0x1U << 0)
+
+/* Special registers in function 0 of the SDxx card */
+/** Special register in function 0 of the SDxxx card : Scratch 0 */
+#define	SCRATCH_0_REG			0x80fe
+/** Special register in function 0 of the SDxxx card : Scratch 1 */
+#define	SCRATCH_1_REG			0x80ff
+/** Host F1 read base 0 */
+#define HOST_F1_RD_BASE_0		0x0040
+/** Host F1 read base 1 */
+#define HOST_F1_RD_BASE_1		0x0041
+/** Host F1 card ready */
+#define HOST_F1_CARD_RDY		0x0020
+
+/** Chip Id Register 0 */
+#define CARD_CHIP_ID_0_REG		0x801c
+/** Chip Id Register 1 */
+#define CARD_CHIP_ID_1_REG		0x801d
+
+struct sdio_mmc_card {
+	/** sdio_func structure pointer */
+	struct sdio_func *func;
+	/** bt_private structure pointer */
+	bt_private *priv;
+};
+
+struct sdio_card_reg {
+	u8 cfg;
+	u8 host_int_mask;	// HOST_INT_MASK_REG
+	u8 host_intstatus;	// HOST_INTSTATUS_REG
+	u8 host_int_rsr_reg;	// HOST_INT_RSR_REG
+	u8 card_misc_cfg_reg;	// CARD_MISC_CFG_REG
+	u8 card_status;		// CARD_STATUS_REG
+	u8 sq_read_base_addr_a0;	// SQ_READ_BASE_ADDRESS_A0_REG
+	u8 sq_read_base_addr_a1;	// SQ_READ_BASE_ADDRESS_A1_REG
+	u8 card_revision;	// CARD_REVISION_REG
+	u8 card_fw_status0;	// CARD_FW_STATUS0_REG
+	u8 card_fw_status1;	// CARD_FW_STATUS1_REG
+	u8 card_rx_len;		// CARD_RX_LEN_REG
+	u8 card_rx_unit;	// CARD_RX_UNIT_REG
+	u8 io_port_0;		// IO_PORT_0_REG
+	u8 io_port_1;		// IO_PORT_1_REG
+	u8 io_port_2;		// IO_PORT_2_REG
+};
+
+struct sdio_device {
+	const struct sdio_card_reg *reg;
+};
+
+static const struct sdio_card_reg bt_reg_87xx = {
+	.cfg = 0x00,
+	.host_int_mask = 0x02,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x03,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x01,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0x6c,	// CARD_MISC_CFG_REG
+	.card_status = 0x30,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0x40,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0x41,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0x5C,	// CARD_REVISION_REG
+	.card_fw_status0 = 0x60,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0x61,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0x62,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0x63,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0x78,	// IO_PORT_0_REG
+	.io_port_1 = 0x79,	// IO_PORT_1_REG
+	.io_port_2 = 0x7a,	// IO_PORT_2_REG
+};
+
+static const struct sdio_card_reg bt_reg_8887 = {
+	.cfg = 0x00,
+	.host_int_mask = 0x08,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x0C,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x04,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0xD8,	// CARD_MISC_CFG_REG
+	.card_status = 0x5C,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0x6C,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0x6D,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0xC8,	// CARD_REVISION_REG
+	.card_fw_status0 = 0x88,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0x89,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0x8A,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0x8B,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0xE4,	// IO_PORT_0_REG
+	.io_port_1 = 0xE5,	// IO_PORT_1_REG
+	.io_port_2 = 0xE6,	// IO_PORT_2_REG
+};
+
+static const struct sdio_card_reg bt_reg_8897 = {
+	.cfg = 0x00,
+	.host_int_mask = 0x02,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x03,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x01,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0xCC,	// CARD_MISC_CFG_REG
+	.card_status = 0x50,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0x60,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0x61,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0xBC,	// CARD_REVISION_REG
+	.card_fw_status0 = 0xC0,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0xC1,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0xC2,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0xC3,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0xD8,	// IO_PORT_0_REG
+	.io_port_1 = 0xD9,	// IO_PORT_1_REG
+	.io_port_2 = 0xDA,	// IO_PORT_2_REG
+};
+
+static const struct sdio_card_reg bt_reg_8977_8978_8997 = {
+	.cfg = 0x00,
+	.host_int_mask = 0x08,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x0C,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x04,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0xD8,	// CARD_MISC_CFG_REG
+	.card_status = 0x5C,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0xF8,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0xF9,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0xC8,	// CARD_REVISION_REG
+	.card_fw_status0 = 0xE8,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0xE9,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0xEA,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0xEB,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0xE4,	// IO_PORT_0_REG
+	.io_port_1 = 0xE5,	// IO_PORT_1_REG
+	.io_port_2 = 0xE6,	// IO_PORT_2_REG
+};
+
+static const struct sdio_device bt_sdio_sd8787 = {
+	.reg = &bt_reg_87xx,
+};
+
+static const struct sdio_device bt_sdio_sd8777 = {
+	.reg = &bt_reg_87xx,
+};
+
+static const struct sdio_device bt_sdio_sd8887 = {
+	.reg = &bt_reg_8887,
+};
+
+static const struct sdio_device bt_sdio_sd8897 = {
+	.reg = &bt_reg_8897,
+};
+
+static const struct sdio_device bt_sdio_sd8797 = {
+	.reg = &bt_reg_87xx,
+};
+
+static const struct sdio_device bt_sdio_sd8977 = {
+	.reg = &bt_reg_8977_8978_8997,
+};
+
+static const struct sdio_device bt_sdio_sd8978 = {
+	.reg = &bt_reg_8977_8978_8997,
+};
+
+static const struct sdio_device bt_sdio_sd8997 = {
+	.reg = &bt_reg_8977_8978_8997,
+};
+
+static const struct sdio_device bt_sdio_sd8987 = {
+	.reg = &bt_reg_8977_8978_8997,
+};
+
+/** DMA alignment value */
+#define DMA_ALIGNMENT	64
+/** 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)	\
+	((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1))
+
+/** This function read cmd52 register */
+int sd_write_reg(bt_private *priv, int reg, u8 val);
+/** This function write cmd52 value to register */
+int sd_read_reg(bt_private *priv, int reg, u8 *data);
+/** This function reads the Cmd52 value in dev structure */
+int sd_read_cmd52_val(bt_private *priv);
+/** This function updates card reg based on the Cmd52 value in dev structure */
+int sd_write_cmd52_val(bt_private *priv, int func, int reg, int val);
+
+void sdio_update_card_type(bt_private *priv, void *card);
+int sdio_get_sdio_device(bt_private *priv);
+
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+/** This function tells lower driver that BT is suspended */
+void bt_is_suspended(bt_private *priv);
+#endif
+#endif
+#endif
+#endif /* _BT_SDIO_H_ */
diff --git a/bt_sd8987/bt/bt_sdiommc.c b/bt_sd8987/bt/bt_sdiommc.c
new file mode 100644
index 0000000..0a6f951
--- /dev/null
+++ b/bt_sd8987/bt/bt_sdiommc.c
@@ -0,0 +1,2337 @@
+/** @file bt_sdiommc.c
+ *  @brief This file contains SDIO IF (interface) module
+ *  related functions.
+ *
+ * Copyright (C) 2007-2019, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/firmware.h>
+#include <linux/mmc/sdio_func.h>
+#include        <linux/mmc/sdio.h>
+#include        <linux/mmc/card.h>
+
+#include "bt_drv.h"
+#include "bt_sdio.h"
+
+/** define marvell vendor id */
+#define MARVELL_VENDOR_ID 0x02df
+
+/** Max retry number of CMD53 read/write */
+#define MAX_CMD53_RETRY 	3
+/** Max retry number of CMD53 read/write */
+#define MAX_CMD52_RETRY     3
+/** Firmware name */
+static char *fw_name;
+/** fw serial download flag */
+extern int bt_fw_serial;
+int bt_intmode = INT_MODE_SDIO;
+/** request firmware nowait */
+int bt_req_fw_nowait;
+static int multi_fn = BIT(2);
+
+#define DEFAULT_FW_NAME ""
+
+/** FW header length for CRC check disable */
+#define FW_CRC_HEADER_RB2   28
+/** FW header for CRC check disable */
+static u8 fw_crc_header_rb_2[FW_CRC_HEADER_RB2] = {
+	0x05, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+	0x9d, 0x32, 0xbb, 0x11, 0x01, 0x00, 0x00, 0x7f,
+	0x00, 0x00, 0x00, 0x00, 0x67, 0xd6, 0xfc, 0x25
+};
+
+/** FW header length for CRC check disable */
+#define FW_CRC_HEADER_RB   24
+/** FW header for CRC check disable */
+static u8 fw_crc_header_rb_1[FW_CRC_HEADER_RB] = {
+	0x01, 0x00, 0x00, 0x00, 0x04, 0xfd, 0x00, 0x04,
+	0x08, 0x00, 0x00, 0x00, 0x26, 0x52, 0x2a, 0x7b,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/** Default firmware name */
+#define DEFAULT_FW_NAME_8777 "mrvl/sd8777_uapsta.bin"
+#define DEFAULT_FW_NAME_8787 "mrvl/sd8787_uapsta.bin"
+#define DEFAULT_FW_NAME_8797 "mrvl/sd8797_uapsta.bin"
+#define DEFAULT_FW_NAME_8887 "mrvl/sd8887_uapsta.bin"
+#define DEFAULT_FW_NAME_8897 "mrvl/sd8897_uapsta.bin"
+#define DEFAULT_FW_NAME_8977 "mrvl/sdsd8977_combo.bin"
+#define DEFAULT_FW_NAME_8978 "mrvl/sdsd8978_combo.bin"
+#define DEFAULT_FW_NAME_8997 "mrvl/sdsd8997_combo.bin"
+
+/** SD8787 chip revision ID */
+#define SD8787_W0      0x30
+#define SD8787_W1      0x31
+#define SD8787_A0_A1   0x40
+/** SD8797 chip revision ID */
+#define SD8797_A0       0x00
+#define SD8797_B0       0x10
+/** SD8897 chip revision ID */
+#define SD8897_A0       0x10
+#define SD8897_B0       0x20
+
+/** SD8887 chip revision ID */
+#define SD8887_A0       0x0
+#define SD8887_A2       0x2
+#define SD8887_A0_FW_NAME "mrvl/sd8887_uapsta.bin"
+#define SD8887_A2_FW_NAME "mrvl/sd8887_uapsta_a2.bin"
+#define SD8887_A2_BT_FW_NAME "mrvl/sd8887_bt_a2.bin"
+
+#define SD8897_A0_FW_NAME "mrvl/sd8897_uapsta_a0.bin"
+#define SD8897_B0_FW_NAME "mrvl/sd8897_uapsta.bin"
+
+#define SD8787_W1_FW_NAME "mrvl/sd8787_uapsta_w1.bin"
+#define SD8787_AX_FW_NAME "mrvl/sd8787_uapsta.bin"
+#define SD8797_A0_FW_NAME "mrvl/sd8797_uapsta_a0.bin"
+#define SD8797_B0_FW_NAME "mrvl/sd8797_uapsta.bin"
+
+/** SD8977 chip revision ID */
+#define SD8977_V0       0x0
+#define SD8977_V1       0x8
+#define SD8977_V2       0x9
+#define SD8977_V0_FW_NAME "mrvl/sdsd8977_combo.bin"
+#define SD8977_V0_BT_FW_NAME "mrvl/sd8977_bt.bin"
+#define SD8977_V1_FW_NAME "mrvl/sdsd8977_combo_v1.bin"
+#define SD8977_V1_BT_FW_NAME "mrvl/sd8977_bt_v1.bin"
+#define SD8977_V2_FW_NAME "mrvl/sdsd8977_combo_v2.bin"
+#define SD8977_V2_BT_FW_NAME "mrvl/sd8977_bt_v2.bin"
+/** SD8978 FW NAME */
+#define SD8978_FW_NAME      "mrvl/sdsd8978_combo.bin"
+#define SD8978_BT_FW_NAME   "mrvl/sd8978_bt.bin"
+
+/** SD8997 chip revision ID */
+#define SD8997_Z        0x02
+#define SD8997_V2       0x10
+#define SD8997_Z_FW_NAME "mrvl/sdsd8997_combo.bin"
+#define SD8997_Z_BT_FW_NAME "mrvl/sd8997_bt.bin"
+#define SD8997_V2_FW_NAME "mrvl/sdsd8997_combo_v2.bin"
+#define SD8997_V2_BT_FW_NAME "mrvl/sd8997_bt_v2.bin"
+#define SD8997_V3_FW_NAME "mrvl/sdsd8997_combo_v3.bin"
+#define SD8997_V3_BT_FW_NAME "mrvl/sd8997_bt_v3.bin"
+
+/** SD8987 */
+#define SD8987_FW_NAME  "mrvl/sdsd8987_combo.bin"
+#define SD8987_BT_FW_NAME "mrvl/sd8987_bt.bin"
+
+/** Function number 2 */
+#define FN2			2
+/** Device ID for SD8787 FN2 */
+#define SD_DEVICE_ID_8787_BT_FN2    0x911A
+/** Device ID for SD8787 FN3 */
+#define SD_DEVICE_ID_8787_BT_FN3    0x911B
+/** Device ID for SD8777 FN2 */
+#define SD_DEVICE_ID_8777_BT_FN2    0x9132
+/** Device ID for SD8777 FN3 */
+#define SD_DEVICE_ID_8777_BT_FN3    0x9133
+/** Device ID for SD8887 FN2 */
+#define SD_DEVICE_ID_8887_BT_FN2    0x9136
+/** Device ID for SD8887 FN3 */
+#define SD_DEVICE_ID_8887_BT_FN3    0x9137
+/** Device ID for SD8897 FN2 */
+#define SD_DEVICE_ID_8897_BT_FN2    0x912E
+/** Device ID for SD8897 FN3 */
+#define SD_DEVICE_ID_8897_BT_FN3    0x912F
+/** Device ID for SD8797 FN2 */
+#define SD_DEVICE_ID_8797_BT_FN2    0x912A
+/** Device ID for SD8797 FN3 */
+#define SD_DEVICE_ID_8797_BT_FN3    0x912B
+/** Device ID for SD8977 FN2 */
+#define SD_DEVICE_ID_8977_BT_FN2    0x9146
+/** Device ID for SD8978 FN2 */
+#define SD_DEVICE_ID_8978_BT_FN2    0x915a
+/** Device ID for SD8997 FN2 */
+#define SD_DEVICE_ID_8997_BT_FN2    0x9142
+/** Device ID for SD8987 FN2 */
+#define SD_DEVICE_ID_8987_BT_FN2    0x914a
+/** Device ID for SD8987 FN3 */
+#define SD_DEVICE_ID_8987_BT_FN3    0x914b
+
+/** Array of SDIO device ids when multi_fn=0x12 */
+static const struct sdio_device_id bt_ids[] = {
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8787_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8777_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8887_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8897_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8797_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8977_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8978_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8987_BT_FN2)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(sdio, bt_ids);
+
+#ifdef SDIO_OOB_IRQ
+extern int mrvl_sdio_claim_irq(struct sdio_func *func,
+			       sdio_irq_handler_t * handler);
+extern int mrvl_sdio_release_irq(struct sdio_func *func);
+extern int mrvl_sdio_suspend(struct sdio_func *func);
+extern int mrvl_sdio_resume(struct sdio_func *func);
+#endif
+
+/********************************************************
+		Global Variables
+********************************************************/
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+extern int mbt_pm_keep_power;
+#endif
+
+extern bt_private *m_priv[];
+/********************************************************
+		Local Functions
+********************************************************/
+
+/**
+ *  @brief This function gets rx_unit value
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_get_rx_unit(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_rx_unit_reg = priv->psdio_device->reg->card_rx_unit;
+
+	ENTER();
+
+	reg = sdio_readb(card->func, card_rx_unit_reg, &ret);
+	if (ret == BT_STATUS_SUCCESS)
+		priv->bt_dev.rx_unit = reg;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads fwstatus registers
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param dat	   A pointer to keep returned data
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_read_firmware_status(bt_private *priv, u16 * dat)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 fws0;
+	u8 fws1;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0;
+	u8 card_fw_status1_reg = priv->psdio_device->reg->card_fw_status1;
+
+	ENTER();
+
+	fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret);
+	if (ret < 0) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret);
+	if (ret < 0) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	*dat = (((u16) fws1) << 8) | fws0;
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function reads rx length
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param dat	   A pointer to keep returned data
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+static int
+sd_read_rx_len(bt_private *priv, u16 * dat)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_rx_len_reg = priv->psdio_device->reg->card_rx_len;
+
+	ENTER();
+
+	reg = sdio_readb(card->func, card_rx_len_reg, &ret);
+	if (ret == BT_STATUS_SUCCESS)
+		*dat = (u16) reg << priv->bt_dev.rx_unit;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables the host interrupts mask
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mask	   the interrupt mask
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_enable_host_int_mask(bt_private *priv, u8 mask)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask;
+
+	ENTER();
+
+	sdio_writeb(card->func, mask, host_int_mask_reg, &ret);
+	if (ret) {
+		PRINTM(WARN, "BT: Unable to enable the host interrupt!\n");
+		ret = BT_STATUS_FAILURE;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function disables the host interrupts mask.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mask	   the interrupt mask
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+static int
+sbi_disable_host_int_mask(bt_private *priv, u8 mask)
+{
+	int ret = BT_STATUS_FAILURE;
+	u8 host_int_mask;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask;
+
+	ENTER();
+
+	/* Read back the host_int_mask register */
+	host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret);
+	if (ret)
+		goto done;
+
+	/* Update with the mask and write back to the register */
+	host_int_mask &= ~mask;
+	sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret);
+	if (ret < 0) {
+		PRINTM(WARN, "BT: Unable to diable the host interrupt!\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function polls the card status register
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param bits     the bit mask
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_poll_card_status(bt_private *priv, u8 bits)
+{
+	int tries;
+	int rval;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 cs;
+	u8 card_status_reg = priv->psdio_device->reg->card_status;
+
+	ENTER();
+
+	for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) {
+		cs = sdio_readb(card->func, card_status_reg, &rval);
+		if (rval != 0)
+			break;
+		if (rval == 0 && (cs & bits) == bits) {
+			LEAVE();
+			return BT_STATUS_SUCCESS;
+		}
+		udelay(1);
+	}
+	PRINTM(ERROR,
+	       "BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n",
+	       rval, tries, cs);
+
+	LEAVE();
+	return BT_STATUS_FAILURE;
+}
+
+/**
+ *  @brief This function reads updates the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_read_cmd52_val(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 func, reg, val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	ENTER();
+
+	func = priv->bt_dev.cmd52_func;
+	reg = priv->bt_dev.cmd52_reg;
+	sdio_claim_host(card->func);
+	if (func)
+		val = sdio_readb(card->func, reg, &ret);
+	else
+		val = sdio_f0_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	if (ret) {
+		PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n",
+		       func, reg);
+	} else {
+		priv->bt_dev.cmd52_val = val;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function updates card reg based on the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param func     Stores func variable
+ *  @param reg      Stores reg variable
+ *  @param val      Stores val variable
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_write_cmd52_val(bt_private *priv, int func, int reg, int val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	ENTER();
+
+	if (val >= 0) {
+		/* Perform actual write only if val is provided */
+		sdio_claim_host(card->func);
+		if (func)
+			sdio_writeb(card->func, val, reg, &ret);
+		else
+			sdio_f0_writeb(card->func, val, reg, &ret);
+		sdio_release_host(card->func);
+		if (ret) {
+			PRINTM(ERROR,
+			       "BT: Cannot write value (0x%x) to func %d reg %d\n",
+			       val, func, reg);
+			goto done;
+		}
+		priv->bt_dev.cmd52_val = val;
+	}
+
+	/* Save current func and reg for future read */
+	priv->bt_dev.cmd52_func = func;
+	priv->bt_dev.cmd52_reg = reg;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function updates card reg based on the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to write
+ *  @param val      value
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_write_reg(bt_private *priv, int reg, u8 val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	sdio_writeb(card->func, val, reg, &ret);
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads updates the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to read
+ *  @param data		Data
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_read_reg(bt_private *priv, int reg, u8 *data)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	val = sdio_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	*data = val;
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function probes the card
+ *
+ *  @param func    A pointer to sdio_func structure.
+ *  @param id      A pointer to structure sdio_device_id
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+static int
+sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id)
+{
+	int ret = BT_STATUS_SUCCESS;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *card = NULL;
+
+	ENTER();
+
+	PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor,
+	       id->device, id->class, func->num);
+	card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL);
+	if (!card) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	card->func = func;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+	/* wait for chip fully wake up */
+	if (!func->enable_timeout)
+		func->enable_timeout = 200;
+#endif
+	sdio_claim_host(func);
+	ret = sdio_enable_func(func);
+	if (ret) {
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret);
+		kfree(card);
+		LEAVE();
+		return -EIO;
+	}
+	sdio_release_host(func);
+	priv = bt_add_card(card);
+	if (!priv) {
+		sdio_claim_host(func);
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		ret = BT_STATUS_FAILURE;
+		kfree(card);
+	}
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function checks if the firmware is ready to accept
+ *  command or not.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param pollnum  Number of times to poll fw status
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_verify_fw_download(bt_private *priv, int pollnum)
+{
+	int ret = BT_STATUS_FAILURE;
+	u16 firmwarestat = 0;
+	int tries;
+
+	ENTER();
+
+	/* Wait for firmware initialization event */
+	for (tries = 0; tries < pollnum; tries++) {
+		if (sd_read_firmware_status(priv, &firmwarestat) < 0)
+			continue;
+		if (firmwarestat == FIRMWARE_READY) {
+			PRINTM(MSG, "BT FW is active(%d)\n", tries);
+			ret = BT_STATUS_SUCCESS;
+			break;
+		}
+		mdelay(100);
+	}
+	if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS)) {
+		PRINTM(ERROR,
+		       "Fail to poll firmware status: firmwarestat=0x%x\n",
+		       firmwarestat);
+		bt_dump_sdio_regs(priv);
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Transfers firmware to card
+ *
+ *  @param priv      A Pointer to bt_private structure
+ *  @param fw        A Pointer to fw image
+ *  @param fw_len    fw image len
+ *  @return          BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+static int
+sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len)
+{
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 *firmware = fw;
+	int firmwarelen = fw_len;
+	u8 base0;
+	u8 base1;
+	int ret = BT_STATUS_SUCCESS;
+	int offset;
+	void *tmpfwbuf = NULL;
+	int tmpfwbufsz;
+	u8 *fwbuf;
+	u16 len;
+	int txlen = 0;
+	int tx_blocks = 0;
+	int i = 0;
+	int tries = 0;
+#ifdef FW_DOWNLOAD_SPEED
+	u32 tv1, tv2;
+#endif
+	u8 sq_read_base_address_a0_reg =
+		priv->psdio_device->reg->sq_read_base_addr_a0;
+	u8 sq_read_base_address_a1_reg =
+		priv->psdio_device->reg->sq_read_base_addr_a1;
+	u8 crc_buffer = 0;
+	u8 *header_crc_fw = NULL;
+	u8 header_crc_fw_len = 0;
+
+	if (priv->card_type == CARD_TYPE_SD8787) {
+		header_crc_fw = fw_crc_header_rb_1;
+		header_crc_fw_len = FW_CRC_HEADER_RB;
+	} else if (priv->card_type == CARD_TYPE_SD8777) {
+		header_crc_fw = fw_crc_header_rb_2;
+		header_crc_fw_len = FW_CRC_HEADER_RB2;
+	}
+
+	ENTER();
+
+	PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen);
+
+#ifdef FW_DOWNLOAD_SPEED
+	tv1 = get_utimeofday();
+#endif
+
+	tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT;
+	tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL);
+	if (!tmpfwbuf) {
+		PRINTM(ERROR,
+		       "BT: Unable to allocate buffer for firmware. Terminating download\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	/* Ensure aligned firmware buffer */
+	fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT);
+
+	if (!(priv->fw_crc_check)
+	    && ((priv->card_type == CARD_TYPE_SD8787) ||
+		(priv->card_type == CARD_TYPE_SD8777))
+		) {
+		/* CRC check not required, use custom header first */
+		firmware = header_crc_fw;
+		firmwarelen = header_crc_fw_len;
+		crc_buffer = 1;
+	}
+
+	/* Perform firmware data transfer */
+	offset = 0;
+	do {
+		/* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits
+		 */
+		ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY);
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: FW download with helper poll status timeout @ %d\n",
+			       offset);
+			goto done;
+		}
+		if (!crc_buffer)
+			/* More data? */
+			if (offset >= firmwarelen)
+				break;
+
+		for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+			base0 = sdio_readb(card->func,
+					   sq_read_base_address_a0_reg, &ret);
+			if (ret) {
+				PRINTM(WARN, "Dev BASE0 register read failed:"
+				       " base0=0x%04X(%d). Terminating download\n",
+				       base0, base0);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			base1 = sdio_readb(card->func,
+					   sq_read_base_address_a1_reg, &ret);
+			if (ret) {
+				PRINTM(WARN, "Dev BASE1 register read failed:"
+				       " base1=0x%04X(%d). Terminating download\n",
+				       base1, base1);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			len = (((u16) base1) << 8) | base0;
+
+			if (len != 0)
+				break;
+			udelay(10);
+		}
+
+		if (len == 0)
+			break;
+		else if (len > BT_UPLD_SIZE) {
+			PRINTM(FATAL,
+			       "BT: FW download failure @ %d, invalid length %d\n",
+			       offset, len);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	/** ignore CRC check before download the first packet */
+		if (offset == 0 && (len & BIT(0)))
+			len &= ~BIT(0);
+		txlen = len;
+
+		if (len & BIT(0)) {
+			i++;
+			if (i >= MAX_CMD53_RETRY) {
+				PRINTM(FATAL,
+				       "BT: FW download failure @ %d, over max retry count\n",
+				       offset);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			PRINTM(ERROR,
+			       "BT: FW CRC error indicated by the helper:"
+			       " len = 0x%04X, txlen = %d\n", len, txlen);
+			len &= ~BIT(0);
+
+			PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset);
+			/* Setting this to 0 to resend from same offset */
+			txlen = 0;
+		} else {
+			i = 0;
+
+			/* Set blocksize to transfer - checking for last block */
+			if (firmwarelen - offset < txlen)
+				txlen = firmwarelen - offset;
+
+			PRINTM(INFO, ".");
+
+			tx_blocks =
+				(txlen + SD_BLOCK_SIZE_FW_DL -
+				 1) / SD_BLOCK_SIZE_FW_DL;
+
+			/* Copy payload to buffer */
+			memcpy(fwbuf, &firmware[offset], txlen);
+		}
+
+		/* Send data */
+		ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf,
+				   tx_blocks * SD_BLOCK_SIZE_FW_DL);
+
+		if (ret < 0) {
+			PRINTM(ERROR,
+			       "BT: FW download, write iomem (%d) failed @ %d\n",
+			       i, offset);
+			sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret);
+			if (ret)
+				PRINTM(ERROR, "write ioreg failed (CFG)\n");
+		}
+
+		offset += txlen;
+		if (crc_buffer
+		    && ((priv->card_type == CARD_TYPE_SD8787) ||
+			(priv->card_type == CARD_TYPE_SD8777))
+			) {
+			if (offset >= header_crc_fw_len) {
+				/* Custom header download complete, restore
+				   original FW */
+				offset = 0;
+				firmware = fw;
+				firmwarelen = fw_len;
+				crc_buffer = 0;
+			}
+		}
+	} while (TRUE);
+
+	PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset);
+
+	ret = BT_STATUS_SUCCESS;
+done:
+#ifdef FW_DOWNLOAD_SPEED
+	tv2 = get_utimeofday();
+	PRINTM(INFO, "FW: %d.%03d.%03d ", tv1 / 1000000,
+	       (tv1 % 1000000) / 1000, tv1 % 1000);
+	PRINTM(INFO, " -> %d.%03d.%03d ", tv2 / 1000000,
+	       (tv2 % 1000000) / 1000, tv2 % 1000);
+	tv2 -= tv1;
+	PRINTM(INFO, " == %d.%03d.%03d\n", tv2 / 1000000,
+	       (tv2 % 1000000) / 1000, tv2 % 1000);
+#endif
+	kfree(tmpfwbuf);
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief request_firmware callback
+ *
+ * @param fw_firmware  A pointer to firmware structure
+ * @param context      A Pointer to bt_private structure
+ * @return             BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_request_fw_dpc(const struct firmware *fw_firmware, void *context)
+{
+	int ret = BT_STATUS_SUCCESS;
+	bt_private *priv = (bt_private *)context;
+	struct sdio_mmc_card *card = NULL;
+	struct m_dev *m_dev_bt = NULL;
+	struct timeval tstamp;
+	int index;
+
+	ENTER();
+
+	m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ];
+
+	if ((priv == NULL) || (priv->adapter == NULL) ||
+	    (priv->bt_dev.card == NULL) || (m_dev_bt == NULL)
+		) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	if (!fw_firmware) {
+		do_gettimeofday(&tstamp);
+		if (tstamp.tv_sec >
+		    (priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) {
+			PRINTM(ERROR,
+			       "BT: No firmware image found. Skipping download\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		PRINTM(ERROR,
+		       "BT: No firmware image found! Retrying download\n");
+		/* Wait a second here before calling the callback again */
+		os_sched_timeout(1000);
+		sd_download_firmware_w_helper(priv);
+		LEAVE();
+		return ret;
+	}
+
+	priv->firmware = fw_firmware;
+
+	if (BT_STATUS_FAILURE ==
+	    sd_init_fw_dpc(priv, (u8 *)priv->firmware->data,
+			   priv->firmware->size)) {
+		PRINTM(ERROR,
+		       "BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n",
+		       bt_req_fw_nowait);
+		sdio_release_host(card->func);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* check if the fimware is downloaded successfully or not */
+	if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) {
+		PRINTM(ERROR, "BT: FW failed to be active in time!\n");
+		ret = BT_STATUS_FAILURE;
+		sdio_release_host(card->func);
+		goto done;
+	}
+	sdio_release_host(card->func);
+	sbi_enable_host_int(priv);
+	if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+		PRINTM(ERROR,
+		       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (fw_firmware) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+		if (!bt_req_fw_nowait)
+#endif
+			release_firmware(fw_firmware);
+	}
+	LEAVE();
+	return ret;
+
+done:
+	if (fw_firmware) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+		if (!bt_req_fw_nowait)
+#endif
+			release_firmware(fw_firmware);
+	}
+	/* For synchronous download cleanup will be done in add_card */
+	if (!bt_req_fw_nowait)
+		return ret;
+	PRINTM(INFO, "unregister device\n");
+	sbi_unregister_dev(priv);
+	((struct sdio_mmc_card *)card)->priv = NULL;
+	/* Stop the thread servicing the interrupts */
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid)
+		os_sched_timeout(1);
+	bt_proc_remove(priv);
+	clean_up_m_devs(priv);
+	bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief request_firmware callback
+ *        This function is invoked by request_firmware_nowait system call
+ *
+ * @param firmware     A pointer to firmware structure
+ * @param context      A Pointer to bt_private structure
+ * @return             None
+ **/
+static void
+sd_request_fw_callback(const struct firmware *firmware, void *context)
+{
+	ENTER();
+	sd_request_fw_dpc(firmware, context);
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function downloads firmware image to the card.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sd_download_firmware_w_helper(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	int err;
+	char *cur_fw_name = NULL;
+
+	ENTER();
+
+	cur_fw_name = fw_name;
+
+	if (fw_name == NULL) {
+		if (priv->card_type == CARD_TYPE_SD8787)
+			cur_fw_name = DEFAULT_FW_NAME_8787;
+		else if (priv->card_type == CARD_TYPE_SD8777)
+			cur_fw_name = DEFAULT_FW_NAME_8777;
+		else if (priv->card_type == CARD_TYPE_SD8887) {
+			/* Check revision ID */
+			switch (priv->adapter->chip_rev) {
+			case SD8887_A0:
+				cur_fw_name = SD8887_A0_FW_NAME;
+				break;
+			case SD8887_A2:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8887_A2_FW_NAME;
+				else
+					cur_fw_name = SD8887_A2_BT_FW_NAME;
+				break;
+			default:
+				cur_fw_name = DEFAULT_FW_NAME_8887;
+				break;
+			}
+		} else if (priv->card_type == CARD_TYPE_SD8897)
+			cur_fw_name = DEFAULT_FW_NAME_8897;
+		else if (priv->card_type == CARD_TYPE_SD8797)
+			cur_fw_name = DEFAULT_FW_NAME_8797;
+		else if (priv->card_type == CARD_TYPE_SD8977) {
+			switch (priv->adapter->chip_rev) {
+			case SD8977_V0:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8977_V0_FW_NAME;
+				else
+					cur_fw_name = SD8977_V0_BT_FW_NAME;
+				break;
+			case SD8977_V1:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8977_V1_FW_NAME;
+				else
+					cur_fw_name = SD8977_V1_BT_FW_NAME;
+				break;
+			case SD8977_V2:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8977_V2_FW_NAME;
+				else
+					cur_fw_name = SD8977_V2_BT_FW_NAME;
+				break;
+			default:
+				cur_fw_name = DEFAULT_FW_NAME_8977;
+				break;
+			}
+		} else if (priv->card_type == CARD_TYPE_SD8978) {
+			if (bt_fw_serial == 1
+			    && !priv->fw_reload && !bt_fw_reload)
+				cur_fw_name = SD8978_FW_NAME;
+			else
+				cur_fw_name = SD8978_BT_FW_NAME;
+		} else if (priv->card_type == CARD_TYPE_SD8997)
+			switch (priv->adapter->chip_rev) {
+			case SD8997_Z:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8997_Z_FW_NAME;
+				else
+					cur_fw_name = SD8997_Z_BT_FW_NAME;
+				break;
+			case SD8997_V2:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload) {
+					if (priv->adapter->magic_val ==
+					    MAGIC_VAL)
+						cur_fw_name = SD8997_V3_FW_NAME;
+					else
+						cur_fw_name = SD8997_V2_FW_NAME;
+				} else {
+					if (priv->adapter->magic_val ==
+					    MAGIC_VAL)
+						cur_fw_name =
+							SD8997_V3_BT_FW_NAME;
+					else
+						cur_fw_name =
+							SD8997_V2_BT_FW_NAME;
+				}
+				break;
+			default:
+				cur_fw_name = DEFAULT_FW_NAME_8997;
+				break;
+		} else if (priv->card_type == CARD_TYPE_SD8987) {
+			if (bt_fw_serial == 1
+			    && !priv->fw_reload && !bt_fw_reload)
+				cur_fw_name = SD8987_FW_NAME;
+			else
+				cur_fw_name = SD8987_BT_FW_NAME;
+		}
+	}
+
+	PRINTM(MSG, "BT Request firmware: %s\n", cur_fw_name);
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cur_fw_name, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      sd_request_fw_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cur_fw_name, priv->hotplug_device,
+					      priv, sd_request_fw_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cur_fw_name, priv->hotplug_device,
+					      priv, sd_request_fw_callback);
+#endif
+#endif
+		if (ret < 0)
+			PRINTM(FATAL,
+			       "BT: request_firmware_nowait() failed, error code = %#x\n",
+			       ret);
+	} else {
+		err = request_firmware(&priv->firmware, cur_fw_name,
+				       priv->hotplug_device);
+		if (err < 0) {
+			PRINTM(FATAL,
+			       "BT: request_firmware() failed, error code = %#x\n",
+			       err);
+			ret = BT_STATUS_FAILURE;
+		} else
+			ret = sd_request_fw_dpc(priv->firmware, priv);
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads data from the card.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_card_to_host(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u16 buf_len = 0;
+	int buf_block_len;
+	int blksz;
+	struct sk_buff *skb = NULL;
+	u32 type;
+	u8 *payload = NULL;
+	struct hci_dev *hdev = NULL;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int i = 0;
+
+	ENTER();
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC)
+		hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer;
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	do {
+		/* Read the length of data to be transferred */
+		ret = sd_read_rx_len(priv, &buf_len);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR, "BT: Read scratch reg failed (%d)\n", i);
+			if (i >= MAX_CMD52_RETRY) {
+				ret = BT_STATUS_FAILURE;
+				goto exit;
+			}
+			udelay(20);
+		}
+	}
+	while (ret == BT_STATUS_FAILURE);
+
+	/* Allocate buffer */
+	blksz = SD_BLOCK_SIZE;
+	buf_block_len = (buf_len + blksz - 1) / blksz;
+	if (buf_len <= BT_HEADER_LEN ||
+	    (buf_block_len * blksz) > ALLOC_BUF_SIZE) {
+		PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n",
+		       buf_len);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "BT: No free skb\n");
+		goto exit;
+	}
+	if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) {
+		skb_put(skb,
+			DMA_ALIGNMENT -
+			((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
+		skb_pull(skb,
+			 DMA_ALIGNMENT -
+			 ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
+	}
+
+	payload = skb->data;
+	i = 0;
+	do {
+		ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport,
+				  buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR,
+			       "BT: card_to_host, read iomem (%d) failed: %d\n",
+			       i, ret);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+			/* issue abort cmd52 command through F0 */
+			sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret);
+#endif
+			sdio_writeb(card->func, HOST_TERM_CMD53,
+				    CONFIGURATION_REG, &ret);
+			udelay(20);
+			ret = BT_STATUS_FAILURE;
+			if (i >= MAX_CMD53_RETRY) {
+				kfree_skb(skb);
+				skb = NULL;
+				goto exit;
+			}
+		}
+	} while (ret == BT_STATUS_FAILURE);
+	/* This is SDIO specific header length: byte[2][1][0], * type: byte[3]
+	   (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */
+	buf_len = payload[0];
+	buf_len |= (u16) payload[1] << 8;
+	type = payload[3];
+	PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", hdev->name,
+	       buf_len, type);
+	if (buf_len > buf_block_len * blksz) {
+		PRINTM(ERROR,
+		       "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n",
+		       buf_len, buf_block_len * blksz);
+		ret = BT_STATUS_FAILURE;
+		kfree_skb(skb);
+		skb = NULL;
+		goto exit;
+	}
+	DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len);
+	switch (type) {
+	case HCI_ACLDATA_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (*(u16 *) skb->data == 0xffff) {
+			bt_store_firmware_dump(priv, skb->data, skb->len);
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		bt_recv_frame(priv, skb);
+		break;
+	case HCI_SCODATA_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		bt_recv_frame(priv, skb);
+		break;
+	case HCI_EVENT_PKT:
+		/** add EVT Demux */
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb))
+			break;
+		switch (skb->data[0]) {
+		case 0x0E:
+			/** cmd complete */
+			bt_recv_frame(priv, skb);
+			break;
+		case 0x0F:
+			/** cmd status */
+			bt_recv_frame(priv, skb);
+			break;
+		case 0xFF:
+			/** Vendor specific pkt */
+			bt_recv_frame(priv, skb);
+			break;
+		default:
+			bt_recv_frame(priv, skb);
+			break;
+		}
+		break;
+	case MRVL_VENDOR_PKT:
+		/* Just think here need to back compatible FM */
+		bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (BT_STATUS_SUCCESS != bt_process_event(priv, skb))
+			bt_recv_frame(priv, skb);
+		break;
+	default:
+		/* Driver specified event and command resp should be handle
+		   here */
+		PRINTM(INFO, "BT: Unknown PKT type:%d\n", type);
+		kfree_skb(skb);
+		skb = NULL;
+		break;
+	}
+exit:
+	if (ret) {
+		if (hdev)
+			hdev->stat.err_rx++;
+		PRINTM(ERROR, "error when recv pkt!\n");
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function removes the card
+ *
+ *  @param func    A pointer to sdio_func structure
+ *  @return        N/A
+ */
+static void
+sd_remove_card(struct sdio_func *func)
+{
+	struct sdio_mmc_card *card;
+
+	ENTER();
+
+	if (func) {
+		card = sdio_get_drvdata(func);
+		if (card) {
+			bt_remove_card(card->priv);
+			kfree(card);
+		}
+	}
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function handles the interrupt.
+ *
+ *  @param func  A pointer to sdio_func structure
+ *  @return      N/A
+ */
+static void
+sd_interrupt(struct sdio_func *func)
+{
+	bt_private *priv;
+	struct m_dev *m_dev = NULL;
+	struct sdio_mmc_card *card;
+	int ret = BT_STATUS_SUCCESS;
+	u8 ireg = 0;
+	u8 host_intstatus_reg = 0;
+
+	ENTER();
+
+	card = sdio_get_drvdata(func);
+	if (!card || !card->priv) {
+		PRINTM(INFO,
+		       "BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n",
+		       __func__, func, card);
+		LEAVE();
+		return;
+	}
+	priv = card->priv;
+	host_intstatus_reg = priv->psdio_device->reg->host_intstatus;
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	if (priv->card_type == CARD_TYPE_SD8887 ||
+	    priv->card_type == CARD_TYPE_SD8897 ||
+	    priv->card_type == CARD_TYPE_SD8977 ||
+	    priv->card_type == CARD_TYPE_SD8997 ||
+	    priv->card_type == CARD_TYPE_SD8987 ||
+	    priv->card_type == CARD_TYPE_SD8978) {
+		ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0,
+				  SD_BLOCK_SIZE);
+		if (ret) {
+			PRINTM(ERROR,
+			       "BT: sdio_read_ioreg: cmd53 read int status register failed %d\n",
+			       ret);
+			goto done;
+		}
+		ireg = priv->adapter->hw_regs[host_intstatus_reg];
+	} else {
+		ireg = sdio_readb(card->func, host_intstatus_reg, &ret);
+		if (ret) {
+			PRINTM(ERROR,
+			       "BT: sdio_read_ioreg: CMD52 read int status register failed %d\n",
+			       ret);
+			goto done;
+		}
+	}
+	if (ireg != 0) {
+		/*
+		 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+		 * Clear the interrupt status register and re-enable
+		 * the interrupt
+		 */
+		PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name,
+		       ireg);
+		priv->adapter->irq_recv = ireg;
+		if (priv->card_type == CARD_TYPE_SD8777 ||
+		    priv->card_type == CARD_TYPE_SD8787) {
+			sdio_writeb(card->func,
+				    ~(ireg) & (DN_LD_HOST_INT_STATUS |
+					       UP_LD_HOST_INT_STATUS),
+				    host_intstatus_reg, &ret);
+			if (ret) {
+				PRINTM(ERROR,
+				       "BT: sdio_write_ioreg: clear int status register failed\n");
+				goto done;
+			}
+		}
+	} else {
+		PRINTM(ERROR, "BT: ERR: ireg=0\n");
+	}
+	OS_INT_DISABLE;
+	priv->adapter->sd_ireg |= ireg;
+	OS_INT_RESTORE;
+	bt_interrupt(m_dev);
+done:
+	LEAVE();
+}
+
+/**
+ *  @brief This function checks if the interface is ready to download
+ *  or not while other download interfaces are present
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @param val    Winner status (0: winner)
+ *  @return       BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_check_winner_status(bt_private *priv, u8 *val)
+{
+
+	int ret = BT_STATUS_SUCCESS;
+	u8 winner = 0;
+	struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0;
+
+	ENTER();
+	winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret);
+	if (ret != BT_STATUS_SUCCESS) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	*val = winner;
+
+	LEAVE();
+	return ret;
+}
+
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+/** @brief This function tells lower driver that BT is suspended
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        None
+ */
+void
+bt_is_suspended(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	priv->adapter->is_suspended = TRUE;
+	sdio_func_suspended(card->func);
+}
+#endif
+
+/** @brief This function handles client driver suspend
+ *
+ *  @param dev	   A pointer to device structure
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+int
+bt_sdio_suspend(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+	struct m_dev *m_dev = NULL;
+	struct hci_dev *hcidev;
+
+	ENTER();
+
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+		PRINTM(ERROR,
+		       "BT: %s: cannot remain alive while host is suspended\n",
+		       sdio_func_id(func));
+		return -ENOSYS;
+	}
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv = cardp->priv;
+
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name);
+	hcidev = (struct hci_dev *)m_dev->dev_pointer;
+	hci_suspend_dev(hcidev);
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
+
+		if (BT_STATUS_SUCCESS != bt_enable_hs(priv, FALSE)) {
+			PRINTM(CMD, "BT: HS not actived, suspend fail!\n");
+			if (BT_STATUS_SUCCESS != bt_enable_hs(priv, FALSE)) {
+				PRINTM(CMD,
+				       "BT: HS not actived the second time, force to suspend!\n");
+			}
+		}
+	}
+
+	priv->adapter->is_suspended = TRUE;
+
+#ifdef SDIO_OOB_IRQ
+	mrvl_sdio_suspend(func);
+#endif
+	LEAVE();
+	/* We will keep the power when hs enabled successfully */
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) {
+#ifdef MMC_PM_SKIP_RESUME_PROBE
+		PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and "
+		       "MMC_PM_SKIP_RESUME_PROBE\n");
+		return sdio_set_host_pm_flags(func,
+					      MMC_PM_KEEP_POWER |
+					      MMC_PM_SKIP_RESUME_PROBE);
+#else
+		PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n");
+		return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+#endif
+	} else {
+		PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n");
+		return BT_STATUS_SUCCESS;
+	}
+}
+
+void
+bt_sdio_shutdown(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+
+	ENTER();
+
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: shutdown: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+		PRINTM(ERROR,
+		       "BT: %s: cannot remain alive while host is shutdown\n",
+		       sdio_func_id(func));
+		return;
+	}
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return;
+	}
+
+	priv = cardp->priv;
+
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
+
+		if (BT_STATUS_SUCCESS != bt_enable_hs(priv, TRUE)) {
+			PRINTM(CMD, "BT: HS not actived, shutdown fail!\n");
+			if (BT_STATUS_SUCCESS != bt_enable_hs(priv, TRUE)) {
+				PRINTM(CMD,
+				       "BT: HS not actived the second time, force to shutdown!\n");
+			}
+		}
+	}
+	LEAVE();
+}
+
+/** @brief This function handles client driver resume
+ *
+ *  @param dev	   A pointer to device structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_sdio_resume(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+	struct m_dev *m_dev = NULL;
+	struct hci_dev *hcidev;
+
+	ENTER();
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv = cardp->priv;
+	priv->adapter->is_suspended = FALSE;
+#ifdef SDIO_OOB_IRQ
+	mrvl_sdio_resume(func);
+#endif
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name);
+	hcidev = (struct hci_dev *)m_dev->dev_pointer;
+	hci_resume_dev(hcidev);
+	sbi_wakeup_firmware(priv);
+	priv->adapter->hs_state = HS_DEACTIVATED;
+	PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+#endif
+#endif
+
+/********************************************************
+		Global Functions
+********************************************************/
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+static const struct dev_pm_ops bt_sdio_pm_ops = {
+	.suspend = bt_sdio_suspend,
+	.resume = bt_sdio_resume,
+};
+#endif
+#endif
+static struct sdio_driver sdio_bt = {
+	.name = "sdio_bt",
+	.id_table = bt_ids,
+	.probe = sd_probe_card,
+	.remove = sd_remove_card,
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+	.drv = {
+		.pm = &bt_sdio_pm_ops,
+		.shutdown = bt_sdio_shutdown,
+		}
+#endif
+#endif
+};
+
+/**
+ *  @brief This function registers the bt module in bus driver.
+ *
+ *  @return	   An int pointer that keeps returned value
+ */
+int *
+sbi_register(void)
+{
+	int *ret;
+
+	ENTER();
+
+	if (sdio_register_driver(&sdio_bt) != 0) {
+		PRINTM(FATAL, "BT: SD Driver Registration Failed\n");
+		LEAVE();
+		return NULL;
+	} else
+		ret = (int *)1;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function de-registers the bt module in bus driver.
+ *
+ *  @return        N/A
+ */
+void
+sbi_unregister(void)
+{
+	ENTER();
+	sdio_unregister_driver(&sdio_bt);
+	LEAVE();
+}
+
+/**
+ *  @brief This function registers the device.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_dev(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	u8 chiprev;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	struct sdio_func *func;
+	u8 host_intstatus_reg = priv->psdio_device->reg->host_intstatus;
+	u8 host_int_rsr_reg = priv->psdio_device->reg->host_int_rsr_reg;
+	u8 card_misc_cfg_reg = priv->psdio_device->reg->card_misc_cfg_reg;
+	u8 card_revision_reg = priv->psdio_device->reg->card_revision;
+	u8 io_port_0_reg = priv->psdio_device->reg->io_port_0;
+	u8 io_port_1_reg = priv->psdio_device->reg->io_port_1;
+	u8 io_port_2_reg = priv->psdio_device->reg->io_port_2;
+	u8 card_magic_reg = CARD_MAGIC_REG;
+	u8 magic_val = 0;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: Error: card or function is NULL!\n");
+		goto failed;
+	}
+	func = card->func;
+	priv->hotplug_device = &func->dev;
+
+	/* Initialize the private structure */
+	strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name));
+	priv->bt_dev.ioport = 0;
+	priv->bt_dev.fn = func->num;
+
+	sdio_claim_host(func);
+#ifdef SDIO_OOB_IRQ
+	ret = mrvl_sdio_claim_irq(func, sd_interrupt);
+#else
+	ret = sdio_claim_irq(func, sd_interrupt);
+#endif
+	if (ret) {
+		PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret);
+		goto release_host;
+	}
+	ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE);
+	if (ret) {
+		PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__);
+		goto release_irq;
+	}
+
+	/* read Revision Register to get the chip revision number */
+	chiprev = sdio_readb(func, card_revision_reg, &ret);
+	if (ret) {
+		PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n");
+		goto release_irq;
+	}
+	priv->adapter->chip_rev = chiprev;
+	PRINTM(INFO, "revision=%#x\n", chiprev);
+
+	magic_val = sdio_readb(func, card_magic_reg, &ret);
+	if (ret) {
+		PRINTM(FATAL, ": cannot read CARD_MAGIC_REG\n");
+		goto release_irq;
+	}
+	priv->adapter->magic_val = magic_val;
+	PRINTM(INFO, "magic_val=%#x\n", magic_val);
+
+	/*
+	 * Read the HOST_INTSTATUS_REG for ACK the first interrupt got
+	 * from the bootloader. If we don't do this we get a interrupt
+	 * as soon as we register the irq.
+	 */
+	reg = sdio_readb(func, host_intstatus_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+
+	/* Read the IO port */
+	reg = sdio_readb(func, io_port_0_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= reg;
+
+	reg = sdio_readb(func, io_port_1_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= (reg << 8);
+
+	reg = sdio_readb(func, io_port_2_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= (reg << 16);
+
+	PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn,
+	       priv->bt_dev.ioport);
+
+	if (priv->card_type == CARD_TYPE_SD8977 ||
+	    priv->card_type == CARD_TYPE_SD8978) {
+		if (bt_intmode == INT_MODE_GPIO) {
+			PRINTM(MSG, "Enable GPIO-1 INT\n");
+			sdio_writeb(func, ENABLE_GPIO_1_INT_MODE,
+				    SCRATCH_REG_32, &ret);
+			if (ret < 0)
+				goto release_irq;
+		}
+	}
+
+#define SDIO_INT_MASK       0x3F
+	if (priv->card_type == CARD_TYPE_SD8887 ||
+	    priv->card_type == CARD_TYPE_SD8897 ||
+	    priv->card_type == CARD_TYPE_SD8797 ||
+	    priv->card_type == CARD_TYPE_SD8977 ||
+	    priv->card_type == CARD_TYPE_SD8997 ||
+	    priv->card_type == CARD_TYPE_SD8987 ||
+	    priv->card_type == CARD_TYPE_SD8978) {
+		/* Set Host interrupt reset to read to clear */
+		reg = sdio_readb(func, host_int_rsr_reg, &ret);
+		if (ret < 0)
+			goto release_irq;
+		sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret);
+		if (ret < 0)
+			goto release_irq;
+		/* Set auto re-enable */
+		reg = sdio_readb(func, card_misc_cfg_reg, &ret);
+		if (ret < 0)
+			goto release_irq;
+		sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg,
+			    &ret);
+		if (ret < 0)
+			goto release_irq;
+	}
+
+	sdio_set_drvdata(func, card);
+	sdio_release_host(func);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+release_irq:
+#ifdef SDIO_OOB_IRQ
+	mrvl_sdio_release_irq(func);
+#else
+	sdio_release_irq(func);
+#endif
+release_host:
+	sdio_release_host(func);
+failed:
+
+	LEAVE();
+	return BT_STATUS_FAILURE;
+}
+
+/**
+ *  @brief This function de-registers the device.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+sbi_unregister_dev(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+
+	ENTER();
+
+	if (card && card->func) {
+		sdio_claim_host(card->func);
+#ifdef SDIO_OOB_IRQ
+		mrvl_sdio_release_irq(card->func);
+#else
+		sdio_release_irq(card->func);
+#endif
+		sdio_disable_func(card->func);
+		sdio_release_host(card->func);
+		sdio_set_drvdata(card->func, NULL);
+	}
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function enables the host interrupts.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_enable_host_int(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	ret = sd_enable_host_int_mask(priv, HIM_ENABLE);
+	sd_get_rx_unit(priv);
+	sdio_release_host(card->func);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function disables the host interrupts.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sbi_disable_host_int(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	ret = sbi_disable_host_int_mask(priv, HIM_DISABLE);
+	sdio_release_host(card->func);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends data to the card.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param payload A pointer to the data/cmd buffer
+ *  @param nb      Length of data/cmd
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+#ifdef DEBUG_LEVEL1
+	struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+#endif
+	int ret = BT_STATUS_SUCCESS;
+	int buf_block_len;
+	int blksz;
+	int i = 0;
+	u8 *buf = NULL;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	buf = payload;
+
+	blksz = SD_BLOCK_SIZE;
+	buf_block_len = (nb + blksz - 1) / blksz;
+	/* Allocate buffer and copy payload */
+	if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) {
+		if (nb > MAX_TX_BUF_SIZE) {
+			PRINTM(ERROR, "BT: Invalid tx packet, size=%d\n", nb);
+			LEAVE();
+			return BT_STATUS_FAILURE;
+		}
+		/* Ensure 8-byte aligned CMD buffer */
+		buf = priv->adapter->tx_buf;
+		memcpy(buf, payload, nb);
+	}
+	sdio_claim_host(card->func);
+	do {
+		/* Transfer data to card */
+		ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf,
+				   buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR,
+			       "BT: host_to_card, write iomem (%d) failed: %d\n",
+			       i, ret);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+			/* issue abort cmd52 command through F0 */
+			sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret);
+#endif
+			sdio_writeb(card->func, HOST_TERM_CMD53,
+				    CONFIGURATION_REG, &ret);
+			udelay(20);
+			ret = BT_STATUS_FAILURE;
+			if (i >= MAX_CMD53_RETRY)
+				goto exit;
+		} else {
+			PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n",
+			       m_dev->name, nb);
+			DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb);
+		}
+	} while (ret == BT_STATUS_FAILURE);
+	priv->bt_dev.tx_dnld_rdy = FALSE;
+exit:
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function downloads firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_download_fw(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+	u8 winner = 0;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	sdio_claim_host(card->func);
+	if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) {
+		PRINTM(MSG, "BT: FW already downloaded!\n");
+		sdio_release_host(card->func);
+		sbi_enable_host_int(priv);
+		if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+			PRINTM(ERROR,
+			       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+			ret = BT_STATUS_FAILURE;
+			goto err_register;
+		}
+		goto exit;
+	}
+	/* Check if other interface is downloading */
+	ret = sd_check_winner_status(priv, &winner);
+	if (ret == BT_STATUS_FAILURE) {
+		PRINTM(FATAL, "BT read winner status failed!\n");
+		goto done;
+	}
+	if (winner) {
+		PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n",
+		       winner);
+		/* check if the fimware is downloaded successfully or not */
+		if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) {
+			PRINTM(FATAL, "BT: FW failed to be active in time!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		sdio_release_host(card->func);
+		sbi_enable_host_int(priv);
+		if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+			PRINTM(ERROR,
+			       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+			ret = BT_STATUS_FAILURE;
+			goto err_register;
+		}
+		goto exit;
+	}
+
+	do_gettimeofday(&priv->req_fw_time);
+	/* Download the main firmware via the helper firmware */
+	if (sd_download_firmware_w_helper(priv)) {
+		PRINTM(INFO, "BT: FW download failed!\n");
+		ret = BT_STATUS_FAILURE;
+	}
+	goto exit;
+done:
+	sdio_release_host(card->func);
+exit:
+	LEAVE();
+	return ret;
+err_register:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function checks the interrupt status and handle it accordingly.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+sbi_get_int_status(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 sdio_ireg = 0;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+
+	ENTER();
+
+	OS_INT_DISABLE;
+	sdio_ireg = priv->adapter->sd_ireg;
+	priv->adapter->sd_ireg = 0;
+	OS_INT_RESTORE;
+	sdio_claim_host(card->func);
+	priv->adapter->irq_done = sdio_ireg;
+	if (sdio_ireg & DN_LD_HOST_INT_STATUS) {	/* tx_done INT */
+		if (priv->bt_dev.tx_dnld_rdy) {	/* tx_done already received */
+			PRINTM(INFO,
+			       "BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n",
+			       priv->bt_dev.tx_dnld_rdy, sdio_ireg);
+		} else {
+			priv->bt_dev.tx_dnld_rdy = TRUE;
+		}
+	}
+	if (sdio_ireg & UP_LD_HOST_INT_STATUS)
+		sd_card_to_host(priv);
+
+	ret = BT_STATUS_SUCCESS;
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function wakeup firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sbi_wakeup_firmware(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
+	sdio_release_host(card->func);
+	PRINTM(CMD, "BT wake up firmware\n");
+
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function updates the SDIO card types
+ *
+ *  @param priv     A Pointer to the bt_private structure
+ *  @param card     A Pointer to card
+ *
+ *  @return         N/A
+ */
+void
+sdio_update_card_type(bt_private *priv, void *card)
+{
+	struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)card;
+
+	/* Update card type */
+	if (cardp->func->device == SD_DEVICE_ID_8777_BT_FN2 ||
+	    cardp->func->device == SD_DEVICE_ID_8777_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8777;
+	else if (cardp->func->device == SD_DEVICE_ID_8787_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8787_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8787;
+	else if (cardp->func->device == SD_DEVICE_ID_8887_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8887_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8887;
+	else if (cardp->func->device == SD_DEVICE_ID_8897_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8897_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8897;
+	else if (cardp->func->device == SD_DEVICE_ID_8797_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8797_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8797;
+	else if (cardp->func->device == SD_DEVICE_ID_8977_BT_FN2)
+		priv->card_type = CARD_TYPE_SD8977;
+	else if (cardp->func->device == SD_DEVICE_ID_8978_BT_FN2)
+		priv->card_type = CARD_TYPE_SD8978;
+	else if (cardp->func->device == SD_DEVICE_ID_8997_BT_FN2)
+		priv->card_type = CARD_TYPE_SD8997;
+	else if (cardp->func->device == SD_DEVICE_ID_8987_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8987_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8987;
+}
+
+/**
+ *  @brief This function get sdio device from card type
+ *
+ *  @param pmadapter  A pointer to mlan_adapter structure
+ *  @return           MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+int
+sdio_get_sdio_device(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u16 card_type = priv->card_type;
+
+	ENTER();
+
+	switch (card_type) {
+	case CARD_TYPE_SD8777:
+		priv->psdio_device = &bt_sdio_sd8777;
+		break;
+	case CARD_TYPE_SD8787:
+		priv->psdio_device = &bt_sdio_sd8787;
+		break;
+	case CARD_TYPE_SD8887:
+		priv->psdio_device = &bt_sdio_sd8887;
+		break;
+	case CARD_TYPE_SD8897:
+		priv->psdio_device = &bt_sdio_sd8897;
+		break;
+	case CARD_TYPE_SD8797:
+		priv->psdio_device = &bt_sdio_sd8797;
+		break;
+	case CARD_TYPE_SD8977:
+		priv->psdio_device = &bt_sdio_sd8977;
+		break;
+	case CARD_TYPE_SD8978:
+		priv->psdio_device = &bt_sdio_sd8978;
+		break;
+	case CARD_TYPE_SD8997:
+		priv->psdio_device = &bt_sdio_sd8997;
+		break;
+	case CARD_TYPE_SD8987:
+		priv->psdio_device = &bt_sdio_sd8987;
+		break;
+	default:
+		PRINTM(ERROR, "BT can't get right card type \n");
+		ret = BT_STATUS_FAILURE;
+		break;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+#define SD8897_INIT_START_REG  0xDC
+#define SD8897_INIT_END_REG  0xE1
+#define SD8887_INIT_START_REG  0xA0
+#define SD8887_INIT_END_REG  0xA5
+#define SD8977_SD8978_SD8997_INIT_START_REG 0xF1
+#define SD8977_SD8978_SD8997_INIT_END_REG 0xF6
+
+/** @brief This function dump the SDIO register
+ *
+ *  @param priv     A Pointer to the bt_private structure
+ *
+ *  @return         N/A
+ */
+void
+bt_dump_sdio_regs(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+	char buf[256], *ptr;
+	u8 loop, func, data;
+	unsigned int reg, reg_start, reg_end;
+	u8 index = 0;
+	unsigned int reg_table_8887[] = { 0x58, 0x59, 0x5c, 0x60, 0x64, 0x70,
+		0x71, 0x72, 0x73, 0xd8, 0xd9, 0xda
+	};
+	u8 loop_num = 0;
+	unsigned int *reg_table = NULL;
+	u8 reg_table_size = 0;
+	unsigned int init_reg_start = 0;
+	unsigned int init_reg_end = 0;
+	if (priv->card_type == CARD_TYPE_SD8887) {
+		init_reg_start = SD8887_INIT_START_REG;
+		init_reg_end = SD8887_INIT_END_REG;
+	} else if (priv->card_type == CARD_TYPE_SD8897) {
+		init_reg_start = SD8897_INIT_START_REG;
+		init_reg_end = SD8897_INIT_END_REG;
+	} else if (priv->card_type == CARD_TYPE_SD8977 ||
+		   priv->card_type == CARD_TYPE_SD8997 ||
+		   priv->card_type == CARD_TYPE_SD8987 ||
+		   priv->card_type == CARD_TYPE_SD8978) {
+		init_reg_start = SD8977_SD8978_SD8997_INIT_START_REG;
+		init_reg_end = SD8977_SD8978_SD8997_INIT_END_REG;
+	}
+
+	if (priv->card_type == CARD_TYPE_SD8887) {
+		loop_num = 3;
+		reg_table = reg_table_8887;
+		reg_table_size = sizeof(reg_table_8887) / sizeof(int);
+	} else
+		loop_num = 2;
+	if (priv->adapter->ps_state)
+		sbi_wakeup_firmware(priv);
+
+	sdio_claim_host(card->func);
+	for (loop = 0; loop < loop_num; loop++) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+		if (loop == 0) {
+			/* Read the registers of SDIO function0 */
+			func = loop;
+			reg_start = 0;
+			reg_end = 9;
+
+		} else if (loop == 2) {
+			/* Read specific registers of SDIO function1 */
+			index = 0;
+			func = 2;
+			reg_start = reg_table[index++];
+			reg_end = reg_table[reg_table_size - 1];
+		} else {
+			func = 2;
+			reg_start = 0;
+			reg_end = 0x09;
+		}
+		if (loop == 2)
+			ptr += sprintf(ptr, "SDIO Func%d: ", func);
+		else
+			ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func,
+				       reg_start, reg_end);
+		for (reg = reg_start; reg <= reg_end;) {
+			if (func == 0)
+				data = sdio_f0_readb(card->func, reg, &ret);
+			else
+				data = sdio_readb(card->func, reg, &ret);
+			if (loop == 2)
+				ptr += sprintf(ptr, "(%#x)", reg);
+			if (!ret)
+				ptr += sprintf(ptr, "%02x ", data);
+			else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+			if (loop == 2 && reg < reg_end)
+				reg = reg_table[index++];
+			else
+				reg++;
+		}
+		PRINTM(MSG, "%s\n", buf);
+	}
+
+	if (init_reg_start) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+		ptr += sprintf(ptr, "Init Status Reg (%#x-%#x): ",
+			       init_reg_start, init_reg_end);
+		for (reg = init_reg_start; reg <= init_reg_end;) {
+			data = sdio_readb(card->func, reg, &ret);
+			if (!ret)
+				ptr += sprintf(ptr, "%02x ", data);
+			else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+			reg++;
+		}
+		PRINTM(MSG, "%s\n", buf);
+	}
+	sdio_release_host(card->func);
+}
+
+module_param(fw_name, charp, 0);
+MODULE_PARM_DESC(fw_name, "Firmware name");
+module_param(bt_req_fw_nowait, int, 0);
+MODULE_PARM_DESC(bt_req_fw_nowait,
+		 "0: Use request_firmware API; 1: Use request_firmware_nowait API");
+module_param(multi_fn, int, 0);
+MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;");
+
+module_param(bt_intmode, int, 0);
+MODULE_PARM_DESC(bt_intmode, "0: INT_MODE_SDIO, 1: INT_MODE_GPIO");
diff --git a/bt_sd8987/bt/hci_wrapper.h b/bt_sd8987/bt/hci_wrapper.h
new file mode 100644
index 0000000..c3c5035
--- /dev/null
+++ b/bt_sd8987/bt/hci_wrapper.h
@@ -0,0 +1,163 @@
+/** @file hci_wrapper.h
+ *  @brief This file contains HCI related definitions
+ *
+ *  Copyright (C) 2011-2019, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _HCI_WRAPPER_H_
+#define _HCI_WRAPPER_H_
+
+#include <linux/module.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+/**  Define Seq num */
+#define BT_SEQ      0
+
+/** Define dev type */
+#define BT_TYPE     1
+#define BT_AMP_TYPE 2
+
+/** Define spec type */
+#define BLUEZ_SPEC     1
+#define IANYWHERE_SPEC 2
+#define GENERIC_SPEC   3
+
+/** Define lock/unlock wrapper */
+#define mdev_req_lock(d)		down(&d->req_lock)
+#define mdev_req_unlock(d)		up(&d->req_lock)
+
+/** Length of device name */
+#define DEV_NAME_LEN				32
+
+/** Define struct m_dev */
+struct m_dev {
+	char name[DEV_NAME_LEN];
+	int index;
+	unsigned long flags;
+	spinlock_t lock;
+	struct semaphore req_lock;
+	struct sk_buff_head rx_q;
+	wait_queue_head_t req_wait_q;
+	struct hci_dev_stats stat;
+	struct module *owner;
+	void *dev_pointer;
+	int dev_type;
+	int spec_type;
+	void *driver_data;
+	int read_continue_flag;
+	int wait_rx_complete;
+	int rx_complete_flag;
+	wait_queue_head_t rx_wait_q;
+	spinlock_t rxlock;
+	atomic_t extra_cnt;
+
+	struct sk_buff *evt_skb;
+	struct sk_buff *acl_skb;
+	struct sk_buff *sco_skb;
+
+	int (*open) (struct m_dev * m_dev);
+	int (*close) (struct m_dev * m_dev);
+	int (*flush) (struct m_dev * m_dev);
+	int (*send) (struct m_dev * m_dev, struct sk_buff * skb);
+	void (*destruct) (struct m_dev * m_dev);
+	void (*notify) (struct m_dev * m_dev, unsigned int evt);
+	int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, void *arg);
+	void (*query) (struct m_dev * m_dev, void *arg);
+
+};
+
+/** Define struct mbt_dev */
+struct mbt_dev {
+	/** maybe could add some private member later */
+	char name[DEV_NAME_LEN];
+	unsigned long flags;
+	__u8 type;
+
+	__u16 pkt_type;
+	__u16 esco_type;
+	__u16 link_policy;
+	__u16 link_mode;
+
+	__u32 idle_timeout;
+	__u16 sniff_min_interval;
+	__u16 sniff_max_interval;
+
+	struct sk_buff *reassembly[3];
+
+	atomic_t promisc;
+};
+
+/** This function frees m_dev allocation */
+void free_m_dev(struct m_dev *m_dev);
+
+/**
+ *  @brief This function receives frames
+ *
+ *  @param skb	A pointer to struct sk_buff
+ *  @return	0--success otherwise error code
+ */
+static inline int
+mdev_recv_frame(struct sk_buff *skb)
+{
+	struct m_dev *m_dev = (struct m_dev *)skb->dev;
+	if (!m_dev || (!test_bit(HCI_UP, &m_dev->flags)
+		       && !test_bit(HCI_INIT, &m_dev->flags))) {
+		kfree_skb(skb);
+		return -ENXIO;
+	}
+
+	/* Incomming skb */
+	bt_cb(skb)->incoming = 1;
+
+	/* Time stamp */
+	__net_timestamp(skb);
+
+	/* Queue frame for rx task */
+	skb_queue_tail(&m_dev->rx_q, skb);
+
+	/* Wakeup rx thread */
+	wake_up_interruptible(&m_dev->req_wait_q);
+
+	return 0;
+}
+
+/**
+ *  @brief mbt dev suspend handler
+ *
+ *  @param m_dev   A pointer to struct m_dev
+ *  @return        0
+ */
+static inline int
+mbt_hci_suspend_dev(struct m_dev *m_dev)
+{
+	return 0;
+}
+
+/**
+ *  @brief mbt dev resume handler
+ *
+ *  @param m_dev   A pointer to struct m_dev
+ *  @return        0
+ */
+static inline int
+mbt_hci_resume_dev(struct m_dev *m_dev)
+{
+	return 0;
+}
+
+#endif /* _HCI_WRAPPER_H_ */
diff --git a/bt_sd8987/bt/mbt_char.c b/bt_sd8987/bt/mbt_char.c
new file mode 100644
index 0000000..f863bc6
--- /dev/null
+++ b/bt_sd8987/bt/mbt_char.c
@@ -0,0 +1,799 @@
+/** @file mbt_char.c
+  *
+  * @brief This file contains the char device function calls
+  *
+  * Copyright (C) 2010-2019, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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/path.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+
+#include "bt_drv.h"
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#include <linux/sched/signal.h>
+#endif
+#include "mbt_char.h"
+
+static LIST_HEAD(char_dev_list);
+
+static DEFINE_SPINLOCK(char_dev_list_lock);
+
+static int mbtchar_major = MBTCHAR_MAJOR_NUM;
+
+/**
+ *	@brief  Gets char device structure
+ *
+ *	@param dev		A pointer to char_dev
+ *
+ *	@return			kobject structure
+ */
+struct kobject *
+chardev_get(struct char_dev *dev)
+{
+	struct kobject *kobj;
+
+	kobj = bt_priv_get(dev->m_dev->driver_data);
+	if (!kobj)
+		return NULL;
+	PRINTM(INFO, "dev get kobj\n");
+	kobj = kobject_get(&dev->kobj);
+	if (!kobj)
+		bt_priv_put(dev->m_dev->driver_data);
+	return kobj;
+}
+
+/**
+ *	@brief  Prints char device structure
+ *
+ *	@param dev		A pointer to char_dev
+ *
+ *	@return			N/A
+ */
+void
+chardev_put(struct char_dev *dev)
+{
+	if (dev) {
+		struct m_dev *m_dev = dev->m_dev;
+		PRINTM(INFO, "dev put kobj\n");
+		kobject_put(&dev->kobj);
+		if (m_dev)
+			bt_priv_put(m_dev->driver_data);
+	}
+}
+
+/**
+ *	@brief Changes permissions of the dev
+ *
+ *	@param name	pointer to character
+ *	@param mode		mode_t type data
+ *	@return			0--success otherwise failure
+ */
+int
+mbtchar_chmod(char *name, mode_t mode)
+{
+	struct path path;
+	struct inode *inode;
+	struct iattr newattrs;
+	int ret;
+	int retrycount = 0;
+
+	ENTER();
+	do {
+		os_sched_timeout(30);
+		ret = kern_path(name, LOOKUP_FOLLOW, &path);
+		if (++retrycount >= 10) {
+			PRINTM(ERROR,
+			       "mbtchar_chmod(): fail to get kern_path\n");
+			LEAVE();
+			return -EFAULT;
+		}
+	} while (ret);
+	inode = path.dentry->d_inode;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_lock(&inode->i_mutex);
+#else
+	inode_lock(inode);
+#endif
+	ret = mnt_want_write(path.mnt);
+	if (ret)
+		goto out_unlock;
+	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	if (inode->i_op->setattr)
+		ret = inode->i_op->setattr(path.dentry, &newattrs);
+	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+		ret = simple_setattr(path.dentry, &newattrs);
+#else
+		ret = inode_setattr(inode, &newattrs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+
+	path_put(&path);
+	LEAVE();
+	return ret;
+out_unlock:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+	path_put(&path);
+	return ret;
+}
+
+/**
+ *	@brief Changes ownership of the dev
+ *
+ *	@param name	pointer to character
+ *	@param user		uid_t type data
+ *	@param group	gid_t type data
+ *	@return			0--success otherwise failure
+ */
+int
+mbtchar_chown(char *name, uid_t user, gid_t group)
+{
+	struct path path;
+	struct inode *inode = NULL;
+	struct iattr newattrs;
+	int ret = 0;
+	int retrycount = 0;
+
+	ENTER();
+	do {
+		os_sched_timeout(30);
+		ret = kern_path(name, LOOKUP_FOLLOW, &path);
+		if (++retrycount >= 10) {
+			PRINTM(ERROR,
+			       "mbtchar_chown(): fail to get kern_path\n");
+			LEAVE();
+			return -EFAULT;
+		}
+	} while (ret);
+	inode = path.dentry->d_inode;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_lock(&inode->i_mutex);
+#else
+	inode_lock(inode);
+#endif
+	ret = mnt_want_write(path.mnt);
+	if (ret)
+		goto out_unlock;
+	newattrs.ia_valid = ATTR_CTIME;
+	if (user != (uid_t) (-1)) {
+		newattrs.ia_valid |= ATTR_UID;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+		newattrs.ia_uid = user;
+#else
+		newattrs.ia_uid = KUIDT_INIT(user);
+#endif
+	}
+	if (group != (gid_t) (-1)) {
+		newattrs.ia_valid |= ATTR_GID;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+		newattrs.ia_gid = group;
+#else
+		newattrs.ia_gid = KGIDT_INIT(group);
+#endif
+	}
+	if (!S_ISDIR(inode->i_mode))
+		newattrs.ia_valid |=
+			ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+	if (inode->i_op->setattr)
+		ret = inode->i_op->setattr(path.dentry, &newattrs);
+	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+		ret = simple_setattr(path.dentry, &newattrs);
+#else
+		ret = inode_setattr(inode, &newattrs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+
+	path_put(&path);
+	LEAVE();
+	return ret;
+out_unlock:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+	path_put(&path);
+	return ret;
+}
+
+/**
+ *	@brief write handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param buf		pointer to char buffer
+ *	@param count	size of receive buffer
+ *	@param f_pos	pointer to loff_t type data
+ *	@return			number of bytes written
+ */
+ssize_t
+chardev_write(struct file * filp, const char *buf, size_t count, loff_t * f_pos)
+{
+	int nwrite = 0;
+	struct sk_buff *skb;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	if (!test_bit(HCI_UP, &m_dev->flags)) {
+		LEAVE();
+		return -EBUSY;
+	}
+	nwrite = count;
+	skb = bt_skb_alloc(count, GFP_ATOMIC);
+	if (!skb) {
+		PRINTM(ERROR, "mbtchar_write(): fail to alloc skb\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+
+	if (copy_from_user((void *)skb_put(skb, count), buf, count)) {
+		PRINTM(ERROR, "mbtchar_write(): cp_from_user failed\n");
+		kfree_skb(skb);
+		nwrite = -EFAULT;
+		goto exit;
+	}
+
+	skb->dev = (void *)m_dev;
+	bt_cb(skb)->pkt_type = *((unsigned char *)skb->data);
+	skb_pull(skb, 1);
+
+	PRINTM(DATA, "Write: pkt_type: 0x%x, len=%d @%lu\n",
+	       bt_cb(skb)->pkt_type, skb->len, jiffies);
+	DBG_HEXDUMP(DAT_D, "chardev_write", skb->data, skb->len);
+
+	/* Send skb to the hci wrapper layer */
+	if (m_dev->send(m_dev, skb)) {
+		PRINTM(ERROR, "Write: Fail\n");
+		nwrite = 0;
+		/* Send failed */
+		kfree_skb(skb);
+	}
+exit:
+	LEAVE();
+	return nwrite;
+}
+
+/**
+ *	@brief read handler for BT char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param buf		pointer to char buffer
+ *	@param count	size of receive buffer
+ *	@param f_pos	pointer to loff_t type data
+ *	@return			number of bytes read
+ */
+ssize_t
+chardev_read(struct file * filp, char *buf, size_t count, loff_t * f_pos)
+{
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	struct sk_buff *skb = NULL;
+
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	/* Wait for rx data */
+	add_wait_queue(&m_dev->req_wait_q, &wait);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		skb = skb_dequeue(&m_dev->rx_q);
+		if (skb)
+			break;
+		if (!test_bit(HCI_UP, &m_dev->flags)) {
+			ret = -EBUSY;
+			break;
+		}
+
+		if (filp->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			break;
+		}
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&m_dev->req_wait_q, &wait);
+
+	if (!skb)
+		goto out;
+
+	if (m_dev->read_continue_flag == 0) {
+		/* Put type byte before the data */
+		memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+		PRINTM(DATA, "Read: pkt_type: 0x%x, len=%d @%lu\n",
+		       bt_cb(skb)->pkt_type, skb->len, jiffies);
+	}
+	DBG_HEXDUMP(DAT_D, "chardev_read", skb->data, skb->len);
+	if (skb->len > count) {
+		/* user data length is smaller than the skb length */
+		if (copy_to_user(buf, skb->data, count)) {
+			ret = -EFAULT;
+			goto outf;
+		}
+		skb_pull(skb, count);
+		skb_queue_head(&m_dev->rx_q, skb);
+		m_dev->read_continue_flag = 1;
+		wake_up_interruptible(&m_dev->req_wait_q);
+		ret = count;
+		goto out;
+	} else {
+		if (copy_to_user(buf, skb->data, skb->len)) {
+			ret = -EFAULT;
+			goto outf;
+		}
+		m_dev->read_continue_flag = 0;
+		ret = skb->len;
+	}
+outf:
+	kfree_skb(skb);
+out:
+	if (m_dev->wait_rx_complete && skb_queue_empty(&m_dev->rx_q)) {
+		m_dev->rx_complete_flag = TRUE;
+		wake_up_interruptible(&m_dev->rx_wait_q);
+	}
+	LEAVE();
+	return ret;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief ioctl common handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+char_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg)
+#else
+/**
+ *	@brief ioctl common handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+char_ioctl(struct file *filp, unsigned int cmd, void *arg)
+#endif
+{
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	PRINTM(INFO, "IOCTL: cmd=%d\n", cmd);
+	switch (cmd) {
+	case MBTCHAR_IOCTL_RELEASE:
+		m_dev->close(m_dev);
+		break;
+	case MBTCHAR_IOCTL_QUERY_TYPE:
+		m_dev->query(m_dev, arg);
+		break;
+	default:
+		m_dev->ioctl(m_dev, cmd, arg);
+		break;
+	}
+	LEAVE();
+	return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief ioctl handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_ioctl(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+#else
+/**
+ *	@brief ioctl handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	return char_ioctl(inode, filp, cmd, (void *)arg);
+#else
+	return char_ioctl(filp, cmd, (void *)arg);
+#endif
+}
+
+#ifdef CONFIG_COMPAT
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief compat ioctl handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_ioctl_compat(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg)
+#else
+/**
+ *	@brief compat ioctl handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+chardev_ioctl_compat(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	return char_ioctl(inode, filp, cmd, compat_ptr(arg));
+#else
+	return char_ioctl(filp, cmd, compat_ptr(arg));
+#endif
+}
+#endif /* CONFIG_COMPAT */
+
+/**
+ *	@brief open handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct char_dev *dev = NULL;
+	struct m_dev *m_dev = NULL;
+	struct char_dev *cdev = NULL;
+	struct list_head *p = NULL;
+	ENTER();
+
+	list_for_each(p, &char_dev_list) {
+		cdev = list_entry(p, struct char_dev, list);
+		if (mbtchar_major == MAJOR(inode->i_cdev->dev) &&
+		    cdev->minor == MINOR(inode->i_cdev->dev)) {
+			dev = cdev;
+			break;
+		}
+	}
+	if (!dev) {
+		PRINTM(ERROR, "cannot find dev from inode\n");
+		LEAVE();
+		return -ENXIO;
+	}
+	if (!chardev_get(dev)) {
+		LEAVE();
+		return -ENXIO;
+	}
+	filp->private_data = dev;	/* for other methods */
+	m_dev = dev->m_dev;
+	mdev_req_lock(m_dev);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
+	if (test_bit(HCI_UP, &m_dev->flags)) {
+		atomic_inc(&m_dev->extra_cnt);
+		goto done;
+	}
+#endif
+	if (m_dev->open(m_dev)) {
+		ret = -EIO;
+		goto done;
+	}
+	set_bit(HCI_UP, &m_dev->flags);
+
+done:
+	mdev_req_unlock(m_dev);
+	if (ret)
+		chardev_put(dev);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief release handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_release(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	ENTER();
+	if (!dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
+	if (m_dev && (atomic_dec_if_positive(&m_dev->extra_cnt) >= 0)) {
+		LEAVE();
+		return ret;
+	}
+#endif
+	if (m_dev)
+		ret = dev->m_dev->close(dev->m_dev);
+	filp->private_data = NULL;
+	chardev_put(dev);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief poll handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param wait		pointer to poll_table structure
+ *	@return			mask
+ */
+static unsigned int
+chardev_poll(struct file *filp, poll_table * wait)
+{
+	unsigned int mask;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+
+	m_dev = dev->m_dev;
+	poll_wait(filp, &m_dev->req_wait_q, wait);
+	mask = POLLOUT | POLLWRNORM;
+	if (skb_peek(&m_dev->rx_q))
+		mask |= POLLIN | POLLRDNORM;
+	if (!test_bit(HCI_UP, &(m_dev->flags)))
+		mask |= POLLHUP;
+	PRINTM(INFO, "poll mask=0x%x\n", mask);
+	LEAVE();
+	return mask;
+}
+
+/* File ops for the Char driver */
+const struct file_operations chardev_fops = {
+	.owner = THIS_MODULE,
+	.read = chardev_read,
+	.write = chardev_write,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	.ioctl = chardev_ioctl,
+#else
+	.unlocked_ioctl = chardev_ioctl,
+#endif
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = chardev_ioctl_compat,
+#endif
+	.open = chardev_open,
+	.release = chardev_release,
+	.poll = chardev_poll,
+};
+
+/**
+ *	@brief This function creates the char dev
+ *
+ *	@param dev			A pointer to structure char_dev
+ *  @param char_class	A pointer to class struct
+ *  @param mod_name		A pointer to char
+ *  @param dev_name		A pointer to char
+ *	@return				0--success otherwise failure
+ */
+int
+register_char_dev(struct char_dev *dev, struct class *char_class,
+		  char *mod_name, char *dev_name)
+{
+	int ret = 0, dev_num;
+	unsigned long flags;
+	ENTER();
+	/* create the chrdev region */
+	if (mbtchar_major) {
+		dev_num = MKDEV(mbtchar_major, dev->minor);
+		ret = register_chrdev_region(dev_num, 1, mod_name);
+	} else {
+		PRINTM(INFO, "chardev: no major # yet\n");
+		ret = alloc_chrdev_region((dev_t *) & dev_num, dev->minor, 1,
+					  mod_name);
+	}
+
+	if (ret) {
+		PRINTM(ERROR, "chardev: create chrdev_region failed\n");
+		LEAVE();
+		return ret;
+	}
+	if (!mbtchar_major) {
+		/* Store the allocated dev major # */
+		mbtchar_major = MAJOR(dev_num);
+	}
+	dev->cdev = cdev_alloc();
+	dev->cdev->ops = &chardev_fops;
+	dev->cdev->owner = chardev_fops.owner;
+	dev_num = MKDEV(mbtchar_major, dev->minor);
+
+	if (cdev_add(dev->cdev, dev_num, 1)) {
+		PRINTM(ERROR, "chardev: cdev_add failed\n");
+		ret = -EFAULT;
+		goto free_cdev_region;
+	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+	if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), NULL, dev_name);
+	}
+#else
+	if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), dev_name);
+	}
+#endif
+	PRINTM(INFO, "register char dev=%s\n", dev_name);
+
+	/** modify later */
+
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	list_add_tail(&dev->list, &char_dev_list);
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+
+	LEAVE();
+	return ret;
+free_cdev_region:
+	unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief This function deletes the char dev
+ *
+ *  @param dev			A pointer to structure char_dev
+ *  @param char_class	A pointer to class struct
+ *  @param dev_name		A pointer to char
+ *  @return				0--success otherwise failure
+ */
+int
+unregister_char_dev(struct char_dev *dev, struct class *char_class,
+		    char *dev_name)
+{
+	ENTER();
+	device_destroy(char_class, MKDEV(mbtchar_major, dev->minor));
+	cdev_del(dev->cdev);
+	unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1);
+	PRINTM(INFO, "unregister char dev=%s\n", dev_name);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *	@brief This function cleans module
+ *
+ *  @param char_class	A pointer to class struct
+ *  @return				N/A
+ */
+void
+chardev_cleanup(struct class *char_class)
+{
+	unsigned long flags;
+	struct list_head *p = NULL;
+	struct char_dev *dev = NULL;
+	ENTER();
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	do {
+		dev = NULL;
+		list_for_each(p, &char_dev_list) {
+			dev = list_entry(p, struct char_dev, list);
+			list_del(p);
+			spin_unlock_irqrestore(&char_dev_list_lock, flags);
+			unregister_char_dev(dev, char_class, dev->m_dev->name);
+			kobject_put(&dev->kobj);
+			spin_lock_irqsave(&char_dev_list_lock, flags);
+			break;
+		}
+	} while (dev);
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+	class_destroy(char_class);
+	LEAVE();
+}
+
+/**
+ *	@brief This function cleans module
+ *
+ *  @param m_dev	A pointer to m_dev struct
+ *  @param char_class	A pointer to class struct
+ *  @return			N/A
+ */
+void
+chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class)
+{
+	unsigned long flags;
+	struct list_head *p = NULL;
+	struct char_dev *dev = NULL;
+	ENTER();
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	list_for_each(p, &char_dev_list) {
+		dev = list_entry(p, struct char_dev, list);
+		if (dev->minor == m_dev->index) {
+			list_del(p);
+			spin_unlock_irqrestore(&char_dev_list_lock, flags);
+			dev->m_dev = NULL;
+			unregister_char_dev(dev, char_class, m_dev->name);
+			kobject_put(&dev->kobj);
+			spin_lock_irqsave(&char_dev_list_lock, flags);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+	LEAVE();
+}
diff --git a/bt_sd8987/bt/mbt_char.h b/bt_sd8987/bt/mbt_char.h
new file mode 100644
index 0000000..d753601
--- /dev/null
+++ b/bt_sd8987/bt/mbt_char.h
@@ -0,0 +1,67 @@
+/** @file mbt_char.h
+  *
+  * @brief This file contains mbtchar driver specific defines etc
+  *
+  * Copyright (C) 2010-2019, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+#ifndef __MBT_CHAR_H__
+#define __MBT_CHAR_H__
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+/** Define ioctl */
+#define MBTCHAR_IOCTL_RELEASE       _IO('M', 1)
+#define MBTCHAR_IOCTL_QUERY_TYPE    _IO('M', 2)
+
+#define MBTCHAR_MAJOR_NUM            (0)
+
+/** Interface specific macros */
+#define FMCHAR_MINOR_BASE            (10)
+#define NFCCHAR_MINOR_BASE           (20)
+
+/** Declaration of char_dev struct */
+struct char_dev {
+	struct list_head list;
+	int minor;
+	int dev_type;
+	struct cdev *cdev;
+	struct m_dev *m_dev;
+	struct kobject kobj;
+};
+
+/** Changes permissions of the dev */
+int mbtchar_chmod(char *name, mode_t mode);
+
+/** Changes ownership of the dev */
+int mbtchar_chown(char *name, uid_t user, gid_t group);
+
+/**	This function creates the char dev */
+int register_char_dev(struct char_dev *dev, struct class *char_class,
+		      char *mod_name, char *dev_name);
+
+/**	This function deletes the char dev */
+int unregister_char_dev(struct char_dev *dev, struct class *char_class,
+			char *dev_name);
+
+/**	This function cleans module */
+void chardev_cleanup(struct class *char_class);
+
+/**	This function cleans module */
+void chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class);
+
+#endif /*__MBT_CHAR_H__*/
diff --git a/bt_sd8987/bt_char/bt_drv.h b/bt_sd8987/bt_char/bt_drv.h
new file mode 100644
index 0000000..2c4c9c6
--- /dev/null
+++ b/bt_sd8987/bt_char/bt_drv.h
@@ -0,0 +1,924 @@
+/** @file bt_drv.h
+ *  @brief This header file contains global constant/enum definitions,
+ *  global variable declaration.
+ *
+ *  Copyright (C) 2007-2019, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _BT_DRV_H_
+#define _BT_DRV_H_
+
+#include <linux/version.h>
+#include <linux/kthread.h>
+#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
+
+#include "hci_wrapper.h"
+
+/** MAX adapter BT driver supported */
+#define MAX_BT_ADAPTER    3
+
+#ifndef BIT
+/** BIT definition */
+#define BIT(x) (1UL << (x))
+#endif
+
+#ifdef MBT_64BIT
+typedef u64 t_ptr;
+#else
+typedef u32 t_ptr;
+#endif
+
+/** max number of adapter supported */
+#define MAX_BT_ADAPTER      3
+/** Define drv_mode bit */
+#define DRV_MODE_BT         BIT(0)
+
+/** Define devFeature bit */
+#define DEV_FEATURE_BT     BIT(0)
+#define DEV_FEATURE_BTAMP     BIT(1)
+#define DEV_FEATURE_BLE     BIT(2)
+
+/** Define maximum number of radio func supported */
+#define MAX_RADIO_FUNC     4
+
+/** MAC address print format */
+#ifndef MACSTR
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+#endif
+
+/** MAC address print arguments */
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#endif
+
+/** Debug level : Message */
+#define	DBG_MSG			BIT(0)
+/** Debug level : Fatal */
+#define DBG_FATAL		BIT(1)
+/** Debug level : Error */
+#define DBG_ERROR		BIT(2)
+/** Debug level : Data */
+#define DBG_DATA		BIT(3)
+/** Debug level : Command */
+#define DBG_CMD			BIT(4)
+/** Debug level : Event */
+#define DBG_EVENT		BIT(5)
+/** Debug level : Interrupt */
+#define DBG_INTR		BIT(6)
+
+/** Debug entry : Data dump */
+#define DBG_DAT_D		BIT(16)
+/** Debug entry : Data dump */
+#define DBG_CMD_D		BIT(17)
+
+/** Debug level : Entry */
+#define DBG_ENTRY		BIT(28)
+/** Debug level : Warning */
+#define DBG_WARN		BIT(29)
+/** Debug level : Informative */
+#define DBG_INFO		BIT(30)
+
+#ifdef	DEBUG_LEVEL1
+extern u32 mbt_drvdbg;
+
+#ifdef	DEBUG_LEVEL2
+/** Print informative message */
+#define	PRINTM_INFO(msg...)  \
+	do {if (mbt_drvdbg & DBG_INFO)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print warning message */
+#define	PRINTM_WARN(msg...) \
+	do {if (mbt_drvdbg & DBG_WARN)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print entry message */
+#define	PRINTM_ENTRY(msg...) \
+	do {if (mbt_drvdbg & DBG_ENTRY) \
+		printk(KERN_DEBUG msg); } while (0)
+#else
+/** Print informative message */
+#define	PRINTM_INFO(msg...)  do {} while (0)
+/** Print warning message */
+#define	PRINTM_WARN(msg...)  do {} while (0)
+/** Print entry message */
+#define	PRINTM_ENTRY(msg...) do {} while (0)
+#endif /* DEBUG_LEVEL2 */
+
+/** Print interrupt message */
+#define	PRINTM_INTR(msg...)  \
+	do {if (mbt_drvdbg & DBG_INTR)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print event message */
+#define	PRINTM_EVENT(msg...) \
+	do {if (mbt_drvdbg & DBG_EVENT) \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print command message */
+#define	PRINTM_CMD(msg...)   \
+	do {if (mbt_drvdbg & DBG_CMD)   \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print data message */
+#define	PRINTM_DATA(msg...)  \
+	do {if (mbt_drvdbg & DBG_DATA)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print error message */
+#define	PRINTM_ERROR(msg...) \
+	do {if (mbt_drvdbg & DBG_ERROR) \
+		printk(KERN_ERR msg); } while (0)
+/** Print fatal message */
+#define	PRINTM_FATAL(msg...) \
+	do {if (mbt_drvdbg & DBG_FATAL) \
+		printk(KERN_ERR msg); } while (0)
+/** Print message */
+#define	PRINTM_MSG(msg...)   \
+	do {if (mbt_drvdbg & DBG_MSG)   \
+		printk(KERN_ALERT msg); } while (0)
+
+/** Print data dump message */
+#define	PRINTM_DAT_D(msg...)  \
+	do {if (mbt_drvdbg & DBG_DAT_D)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print data dump message */
+#define	PRINTM_CMD_D(msg...)  \
+	do {if (mbt_drvdbg & DBG_CMD_D)  \
+		printk(KERN_DEBUG msg); } while (0)
+
+/** Print message with required level */
+#define	PRINTM(level, msg...) PRINTM_##level(msg)
+
+/** Debug dump buffer length */
+#define DBG_DUMP_BUF_LEN	64
+/** Maximum number of dump per line */
+#define MAX_DUMP_PER_LINE	16
+/** Maximum data dump length */
+#define MAX_DATA_DUMP_LEN	48
+
+/**
+ * @brief Prints buffer data upto provided length
+ *
+ * @param prompt          Char pointer
+ * @param buf			  Buffer
+ * @param len    		  Length
+ *
+ * @return                N/A
+ */
+static inline void
+hexdump(char *prompt, u8 *buf, int len)
+{
+	int i;
+	char dbgdumpbuf[DBG_DUMP_BUF_LEN];
+	char *ptr = dbgdumpbuf;
+
+	printk(KERN_DEBUG "%s: len=%d\n", prompt, len);
+	for (i = 1; i <= len; i++) {
+		ptr += snprintf(ptr, 4, "%02x ", *buf);
+		buf++;
+		if (i % MAX_DUMP_PER_LINE == 0) {
+			*ptr = 0;
+			printk(KERN_DEBUG "%s\n", dbgdumpbuf);
+			ptr = dbgdumpbuf;
+		}
+	}
+	if (len % MAX_DUMP_PER_LINE) {
+		*ptr = 0;
+		printk(KERN_DEBUG "%s\n", dbgdumpbuf);
+	}
+}
+
+/** Debug hexdump of debug data */
+#define DBG_HEXDUMP_DAT_D(x, y, z) \
+	do {if (mbt_drvdbg & DBG_DAT_D) \
+		hexdump(x, y, z); } while (0)
+/** Debug hexdump of debug command */
+#define DBG_HEXDUMP_CMD_D(x, y, z) \
+	do {if (mbt_drvdbg & DBG_CMD_D) \
+		hexdump(x, y, z); } while (0)
+
+/** Debug hexdump */
+#define	DBG_HEXDUMP(level, x, y, z)    DBG_HEXDUMP_##level(x, y, z)
+
+/** Mark entry point */
+#define	ENTER()			PRINTM(ENTRY, "Enter: %s, %s:%i\n", __func__, \
+							__FILE__, __LINE__)
+/** Mark exit point */
+#define	LEAVE()			PRINTM(ENTRY, "Leave: %s, %s:%i\n", __func__, \
+							__FILE__, __LINE__)
+#else
+/** Do nothing */
+#define	PRINTM(level, msg...) do {} while (0)
+/** Do nothing */
+#define DBG_HEXDUMP(level, x, y, z)    do {} while (0)
+/** Do nothing */
+#define	ENTER()  do {} while (0)
+/** Do nothing */
+#define	LEAVE()  do {} while (0)
+#endif /* DEBUG_LEVEL1 */
+
+/** Bluetooth upload size */
+#define	BT_UPLD_SIZE				2312
+/** Bluetooth status success */
+#define BT_STATUS_SUCCESS			(0)
+/** Bluetooth status pending */
+#define BT_STATUS_PENDING           (1)
+/** Bluetooth status failure */
+#define BT_STATUS_FAILURE			(-1)
+
+#ifndef	TRUE
+/** True value */
+#define TRUE			1
+#endif
+#ifndef	FALSE
+/** False value */
+#define	FALSE			0
+#endif
+
+/** Set thread state */
+#define OS_SET_THREAD_STATE(x)		set_current_state(x)
+/** Time to wait until Host Sleep state change in millisecond */
+#define WAIT_UNTIL_HS_STATE_CHANGED 2000
+/** Time to wait cmd resp in millisecond */
+#define WAIT_UNTIL_CMD_RESP	    5000
+
+/** Sleep until a condition gets true or a timeout elapses */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+#define os_wait_interruptible_timeout(waitq, cond, timeout) \
+	interruptible_sleep_on_timeout(&waitq, ((timeout) * HZ / 1000))
+#else
+#define os_wait_interruptible_timeout(waitq, cond, timeout) \
+	wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000))
+#endif
+
+#define os_wait_timeout(waitq, cond, timeout) \
+         wait_event_timeout(waitq, cond, ((timeout) * HZ / 1000))
+
+/** bt thread structure */
+typedef struct {
+	/** Task */
+	struct task_struct *task;
+	/** Queue */
+	wait_queue_head_t waitQ;
+	/** PID */
+	pid_t pid;
+	/** Private structure */
+	void *priv;
+} bt_thread;
+
+/**
+ * @brief Activates bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline void
+bt_activate_thread(bt_thread *thr)
+{
+	/** Initialize the wait queue */
+	init_waitqueue_head(&thr->waitQ);
+
+	/** Record the thread pid */
+	thr->pid = current->pid;
+}
+
+/**
+ * @brief De-activates bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline void
+bt_deactivate_thread(bt_thread *thr)
+{
+	thr->pid = 0;
+	return;
+}
+
+/**
+ * @brief Creates bt thread
+ *
+ * @param btfunc          Function pointer
+ * @param thr			  A pointer to bt_thread structure
+ * @param name    		  Char pointer
+ *
+ * @return                N/A
+ */
+static inline void
+bt_create_thread(int (*btfunc) (void *), bt_thread *thr, char *name)
+{
+	thr->task = kthread_run(btfunc, thr, "%s", name);
+}
+
+/**
+ * @brief Delete bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline int
+bt_terminate_thread(bt_thread *thr)
+{
+	/* Check if the thread is active or not */
+	if (!thr->pid)
+		return -1;
+
+	kthread_stop(thr->task);
+	return 0;
+}
+
+/**
+ * @brief  Set scheduled timeout
+ *
+ * @param millisec		 Time unit in ms
+ *
+ * @return                N/A
+ */
+static inline void
+os_sched_timeout(u32 millisec)
+{
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	schedule_timeout((millisec * HZ) / 1000);
+}
+
+#ifndef __ATTRIB_ALIGN__
+#define __ATTRIB_ALIGN__ __attribute__((aligned(4)))
+#endif
+
+#ifndef __ATTRIB_PACK__
+#define __ATTRIB_PACK__ __attribute__((packed))
+#endif
+
+/** Data structure for the Marvell Bluetooth device */
+typedef struct _bt_dev {
+	/** device name */
+	char name[DEV_NAME_LEN];
+	/** card pointer */
+	void *card;
+	/** IO port */
+	u32 ioport;
+	/** m_dev structure */
+	struct m_dev m_dev[MAX_RADIO_FUNC];
+
+	/** Tx download ready flag */
+	u8 tx_dnld_rdy;
+	/** Function */
+	u8 fn;
+	/** Rx unit */
+	u8 rx_unit;
+	/** Power Save mode : Timeout configuration */
+	u16 idle_timeout;
+	/** Power Save mode */
+	u8 psmode;
+	/** Power Save command */
+	u8 pscmd;
+	/** Host Sleep mode */
+	u8 hsmode;
+	/** Host Sleep command */
+	u8 hscmd;
+	/** Low byte is gap, high byte is GPIO */
+	u16 gpio_gap;
+	/** Host Sleep configuration command */
+	u8 hscfgcmd;
+	/** Host Send Cmd Flag		 */
+	u8 sendcmdflag;
+	/** opcode for Send Cmd */
+	u16 send_cmd_opcode;
+	/** Device Type			*/
+	u8 devType;
+	/** Device Features    */
+	u8 devFeature;
+	/** cmd52 function */
+	u8 cmd52_func;
+	/** cmd52 register */
+	u8 cmd52_reg;
+	/** cmd52 value */
+	u8 cmd52_val;
+	/** SDIO pull control command */
+	u8 sdio_pull_ctrl;
+	/** Low 2 bytes is pullUp, high 2 bytes for pull-down */
+	u32 sdio_pull_cfg;
+	/** Test mode command */
+	u8 test_mode;
+} bt_dev_t, *pbt_dev_t;
+
+/** Marvell bt adapter structure */
+typedef struct _bt_adapter {
+	/** Chip revision ID */
+	u8 chip_rev;
+    /** magic val */
+	u8 magic_val;
+	/** Surprise removed flag */
+	u8 SurpriseRemoved;
+	/** IRQ number */
+	int irq;
+	/** Interrupt counter */
+	u32 IntCounter;
+	/** Tx packet queue */
+	struct sk_buff_head tx_queue;
+
+	/** Pointer of fw dump file name */
+	char *fwdump_fname;
+	/** Pending Tx packet queue */
+	struct sk_buff_head pending_queue;
+	/** tx lock flag */
+	u8 tx_lock;
+	/** Power Save mode */
+	u8 psmode;
+	/** Power Save state */
+	u8 ps_state;
+	/** Host Sleep state */
+	u8 hs_state;
+	/** hs skip count */
+	u32 hs_skip;
+	/** suspend_fail flag */
+	u8 suspend_fail;
+	/** suspended flag */
+	u8 is_suspended;
+	/** Number of wakeup tries */
+	u8 WakeupTries;
+	/** Host Sleep wait queue */
+	wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__;
+	/** Host Cmd complet state */
+	u8 cmd_complete;
+	/** indicate using wait event timeout */
+	u8 wait_event_timeout;
+	/** last irq recv */
+	u8 irq_recv;
+	/** last irq processed */
+	u8 irq_done;
+	/** sdio int status */
+	u8 sd_ireg;
+     /** buf allocated for transmit */
+	u8 *tx_buffer;
+    /** buf for transmit */
+	u8 *tx_buf;
+    /** buf allocated for read interrupt status */
+	u8 *hw_regs_buf;
+    /** buf for read interrupt status */
+	u8 *hw_regs;
+	/** tx pending */
+	u32 skb_pending;
+/** Version string buffer length */
+#define MAX_VER_STR_LEN         128
+	/** Driver version */
+	u8 drv_ver[MAX_VER_STR_LEN];
+	/** Number of command timeout */
+	u32 num_cmd_timeout;
+} bt_adapter, *pbt_adapter;
+
+/** Length of prov name */
+#define PROC_NAME_LEN				32
+
+/** Item data structure */
+struct item_data {
+	/** Name */
+	char name[PROC_NAME_LEN];
+	/** Size */
+	u32 size;
+	/** Address */
+	t_ptr addr;
+	/** Offset */
+	u32 offset;
+	/** Flag */
+	u32 flag;
+};
+
+/** Proc private data structure */
+struct proc_private_data {
+	/** Name */
+	char name[PROC_NAME_LEN];
+	/** File flag */
+	u32 fileflag;
+	/** Buffer size */
+	u32 bufsize;
+	/** Number of items */
+	u32 num_items;
+	/** Item data */
+	struct item_data *pdata;
+	/** Private structure */
+	struct _bt_private *pbt;
+	/** File operations */
+	const struct file_operations *fops;
+};
+
+/** Device proc structure */
+struct device_proc {
+	/** Proc directory entry */
+	struct proc_dir_entry *proc_entry;
+	/** num of proc files */
+	u8 num_proc_files;
+	/** pointer to proc_private_data */
+	struct proc_private_data *pfiles;
+};
+
+/** Private structure for the MV device */
+typedef struct _bt_private {
+	/** Bluetooth device */
+	bt_dev_t bt_dev;
+	/** Adapter */
+	bt_adapter *adapter;
+	/** Firmware helper */
+	const struct firmware *fw_helper;
+	/** Firmware */
+	const struct firmware *firmware;
+	/** Init user configure file */
+	const struct firmware *init_user_cfg;
+	/** Init user configure wait queue token */
+	u16 init_user_conf_wait_flag;
+	/** Init user configure file wait queue */
+	wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__;
+	/** Firmware request start time */
+	struct timeval req_fw_time;
+	/** Hotplug device */
+	struct device *hotplug_device;
+	/** thread to service interrupts */
+	bt_thread MainThread;
+	 /** proc data */
+	struct device_proc dev_proc[MAX_RADIO_FUNC];
+	/** Driver lock */
+	spinlock_t driver_lock;
+	/** Driver lock flags */
+	ulong driver_flags;
+	/** Driver reference flags */
+	struct kobject kobj;
+	/** CRC check flag */
+	u16 fw_crc_check;
+	/** Card type */
+	u16 card_type;
+	/** sdio device */
+	const struct sdio_device *psdio_device;
+	int debug_device_pending;
+	int debug_ocf_ogf[2];
+	u8 fw_reload;
+#ifdef BLE_WAKEUP
+	u8 ble_wakeup_buf_size;
+	u8 *ble_wakeup_buf;
+    /** white list address: address count + count* address*/
+	u8 white_list[61];
+#endif
+    /** fw dump state */
+	u8 fw_dump;
+} bt_private, *pbt_private;
+
+/** Disable interrupt */
+#define OS_INT_DISABLE	spin_lock_irqsave(&priv->driver_lock, \
+						priv->driver_flags)
+/** Enable interrupt */
+#define	OS_INT_RESTORE	spin_unlock_irqrestore(&priv->driver_lock, \
+						priv->driver_flags)
+
+#ifndef HCI_BT_AMP
+/** BT_AMP flag for device type */
+#define  HCI_BT_AMP		0x80
+#endif
+
+/** Device type of BT */
+#define DEV_TYPE_BT		0x00
+/** Device type of AMP */
+#define DEV_TYPE_AMP		0x01
+
+/** Marvell vendor packet */
+#define MRVL_VENDOR_PKT			0xFE
+
+/** Bluetooth command : Get FW Version */
+#define BT_CMD_GET_FW_VERSION       0x0F
+/** Bluetooth command : Sleep mode */
+#define BT_CMD_AUTO_SLEEP_MODE		0x23
+/** Bluetooth command : Host Sleep configuration */
+#define BT_CMD_HOST_SLEEP_CONFIG	0x59
+/** Bluetooth command : Host Sleep enable */
+#define BT_CMD_HOST_SLEEP_ENABLE	0x5A
+/** Bluetooth command : Module Configuration request */
+#define BT_CMD_MODULE_CFG_REQ		0x5B
+#ifdef BLE_WAKEUP
+/** Bluetooth command : Get whitelist */
+#define BT_CMD_GET_WHITELIST        0x9C
+
+#define HCI_BLE_GRP_BLE_CMDS                 0x08
+#define HCI_BT_SET_EVENTMASK_OCF           0x0001
+#define HCI_BLE_ADD_DEV_TO_WHITELIST_OCF     0x0011
+#define HCI_BLE_SET_SCAN_PARAMETERS_OCF      0x000B
+#define HCI_BLE_SET_SCAN_ENABLE_OCF          0x000C
+
+#endif
+/** Bluetooth command : PMIC Configure */
+#define BT_CMD_PMIC_CONFIGURE           0x7D
+
+/** Bluetooth command : SDIO pull up down configuration request */
+#define BT_CMD_SDIO_PULL_CFG_REQ	0x69
+/** Bluetooth command : Set Evt Filter Command */
+#define BT_CMD_SET_EVT_FILTER		0x05
+/** Bluetooth command : Enable Write Scan Command */
+#define BT_CMD_ENABLE_WRITE_SCAN	0x1A
+/** Bluetooth command : Enable Device under test mode */
+#define BT_CMD_ENABLE_DEVICE_TESTMODE	0x03
+/** Sub Command: Module Bring Up Request */
+#define MODULE_BRINGUP_REQ		0xF1
+/** Sub Command: Module Shut Down Request */
+#define MODULE_SHUTDOWN_REQ		0xF2
+/** Module already up */
+#define MODULE_CFG_RESP_ALREADY_UP      0x0c
+/** Sub Command: Host Interface Control Request */
+#define MODULE_INTERFACE_CTRL_REQ	0xF5
+
+/** Bluetooth event : Power State */
+#define BT_EVENT_POWER_STATE		0x20
+
+/** Bluetooth Power State : Enable */
+#define BT_PS_ENABLE			0x02
+/** Bluetooth Power State : Disable */
+#define BT_PS_DISABLE			0x03
+/** Bluetooth Power State : Sleep */
+#define BT_PS_SLEEP			0x01
+/** Bluetooth Power State : Awake */
+#define BT_PS_AWAKE			0x02
+
+/** Vendor OGF */
+#define VENDOR_OGF				0x3F
+/** OGF for reset */
+#define RESET_OGF		0x03
+/** Bluetooth command : Reset */
+#define BT_CMD_RESET	0x03
+
+/** Host Sleep activated */
+#define HS_ACTIVATED			0x01
+/** Host Sleep deactivated */
+#define HS_DEACTIVATED			0x00
+
+/** Power Save sleep */
+#define PS_SLEEP			0x01
+/** Power Save awake */
+#define PS_AWAKE			0x00
+
+/** bt header length */
+#define BT_HEADER_LEN			4
+
+#ifndef MAX
+/** Return maximum of two */
+#define MAX(a, b)		((a) > (b) ? (a) : (b))
+#endif
+
+/** This is for firmware specific length */
+#define EXTRA_LEN	36
+
+/** Command buffer size for Marvell driver */
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+
+/** Bluetooth Rx packet buffer size for Marvell driver */
+#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
+	(HCI_MAX_FRAME_SIZE + EXTRA_LEN)
+
+/** Buffer size to allocate */
+#define ALLOC_BUF_SIZE	(((MAX(MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
+			MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+			+ SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE)
+
+/** Request FW timeout in second */
+#define REQUEST_FW_TIMEOUT		30
+
+/** The number of times to try when polling for status bits */
+#define MAX_POLL_TRIES			100
+
+/** The number of times to try when waiting for downloaded firmware to
+    become active when multiple interface is present */
+#define MAX_MULTI_INTERFACE_POLL_TRIES  150
+
+/** The number of times to try when waiting for downloaded firmware to
+     become active. (polling the scratch register). */
+#define MAX_FIRMWARE_POLL_TRIES		100
+
+/** default idle time */
+#define DEFAULT_IDLE_TIME           1000
+
+#define BT_CMD_HEADER_SIZE    3
+
+#define BT_CMD_DATA_LEN    128
+#define BT_EVT_DATA_LEN    8
+
+/** BT command structure */
+typedef struct _BT_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** Data */
+	u8 data[BT_CMD_DATA_LEN];
+} __ATTRIB_PACK__ BT_CMD;
+
+/** BT event structure */
+typedef struct _BT_EVENT {
+	/** Event Counter */
+	u8 EC;
+	/** Length */
+	u8 length;
+	/** Data */
+	u8 data[BT_EVT_DATA_LEN];
+} BT_EVENT;
+
+#if defined(SDIO_SUSPEND_RESUME)
+#define DEF_GPIO_GAP        0xffff
+#endif
+
+#ifdef BLE_WAKEUP
+#define BD_ADDR_SIZE 6
+/** Vendor specific event */
+#define VENDOR_SPECIFIC_EVENT     0xff
+/** system suspend event */
+#define HCI_SYSTEM_SUSPEND_EVT    0x80
+/** system suspend */
+#define HCI_SYSTEM_SUSPEND        0x00
+/** system resume */
+#define HCI_SYSTEM_RESUME         0x01
+/** This function enables ble wake up pattern */
+int bt_config_ble_wakeup(bt_private *priv, bool is_shutdown);
+int bt_send_system_event(bt_private *priv, u8 flag);
+void bt_send_hw_remove_event(bt_private *priv);
+#endif
+
+/** This function verify the received event pkt */
+int check_evtpkt(bt_private *priv, struct sk_buff *skb);
+
+/* Prototype of global function */
+/** This function gets the priv reference */
+struct kobject *bt_priv_get(bt_private *priv);
+/** This function release the priv reference */
+void bt_priv_put(bt_private *priv);
+/** This function adds the card */
+bt_private *bt_add_card(void *card);
+/** This function removes the card */
+int bt_remove_card(void *card);
+/** This function handles the interrupt */
+void bt_interrupt(struct m_dev *m_dev);
+
+/** This function creates proc interface directory structure */
+int bt_root_proc_init(void);
+/** This function removes proc interface directory structure */
+int bt_root_proc_remove(void);
+/** This function initializes proc entry */
+int bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq);
+/** This function removes proc interface */
+void bt_proc_remove(bt_private *priv);
+
+/** This function process the received event */
+int bt_process_event(bt_private *priv, struct sk_buff *skb);
+/** This function enables host sleep */
+int bt_enable_hs(bt_private *priv, bool is_shutdown);
+/** This function used to send command to firmware */
+int bt_prepare_command(bt_private *priv);
+/** This function frees the structure of adapter */
+void bt_free_adapter(bt_private *priv);
+/** This function handle the receive packet */
+void bt_recv_frame(bt_private *priv, struct sk_buff *skb);
+void bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len);
+
+/** clean up m_devs */
+void clean_up_m_devs(bt_private *priv);
+/** bt driver call this function to register to bus driver */
+int *sbi_register(void);
+/** bt driver call this function to unregister to bus driver */
+void sbi_unregister(void);
+/** bt driver calls this function to register the device  */
+int sbi_register_dev(bt_private *priv);
+/** bt driver calls this function to unregister the device */
+int sbi_unregister_dev(bt_private *priv);
+/** This function initializes firmware */
+int sbi_download_fw(bt_private *priv);
+/** Configures hardware to quit deep sleep state */
+int sbi_wakeup_firmware(bt_private *priv);
+/** Module configuration and register device */
+int sbi_register_conf_dpc(bt_private *priv);
+
+/** This function is used to send the data/cmd to hardware */
+int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb);
+/** This function reads the current interrupt status register */
+int sbi_get_int_status(bt_private *priv);
+/** This function enables the host interrupts */
+int sbi_enable_host_int(bt_private *priv);
+/** This function disables the host interrupts */
+int sbi_disable_host_int(bt_private *priv);
+
+/** bt fw reload flag */
+extern int bt_fw_reload;
+/** driver initial the fw reset */
+#define FW_RELOAD_SDIO_INBAND_RESET   1
+/** out band reset trigger reset, no interface re-emulation */
+#define FW_RELOAD_NO_EMULATION  2
+/** out band reset with interface re-emulation */
+#define FW_RELOAD_WITH_EMULATION 3
+/** This function reload firmware */
+void bt_request_fw_reload(bt_private *priv, int mode);
+#define MAX_TX_BUF_SIZE     2312
+/** This function downloads firmware image to the card */
+int sd_download_firmware_w_helper(bt_private *priv);
+void bt_dump_sdio_regs(bt_private *priv);
+#define FW_DUMP_TYPE_ENDED                    0x002
+#define FW_DUMP_TYPE_MEM_ITCM                 0x004
+#define FW_DUMP_TYPE_MEM_DTCM                 0x005
+#define FW_DUMP_TYPE_MEM_SQRAM                0x006
+#define FW_DUMP_TYPE_MEM_IRAM                 0x007
+#define FW_DUMP_TYPE_REG_MAC                  0x009
+#define FW_DUMP_TYPE_REG_CIU                  0x00E
+#define FW_DUMP_TYPE_REG_APU                  0x00F
+#define FW_DUMP_TYPE_REG_ICU                  0x014
+/* dumps the firmware to /var/ or /data/ */
+void bt_dump_firmware_info_v2(bt_private *priv);
+
+/** Max line length allowed in init config file */
+#define MAX_LINE_LEN        256
+/** Max MAC address string length allowed */
+#define MAX_MAC_ADDR_LEN    18
+/** Max register type/offset/value etc. parameter length allowed */
+#define MAX_PARAM_LEN       12
+
+/** Bluetooth command : Mac address configuration */
+#define BT_CMD_CONFIG_MAC_ADDR		0x22
+/** Bluetooth command : Write CSU register */
+#define BT_CMD_CSU_WRITE_REG		0x66
+/** Bluetooth command : Load calibrate data */
+#define BT_CMD_LOAD_CONFIG_DATA     0x61
+/** Bluetooth command : Load calibrate ext data */
+#define BT_CMD_LOAD_CONFIG_DATA_EXT     0x60
+
+/** Bluetooth command : BLE deepsleep */
+#define BT_CMD_BLE_DEEP_SLEEP       0x8b
+
+/** BT_BLE command structure */
+typedef struct _BT_BLE_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** deepsleep flag */
+	u8 deepsleep;
+} __ATTRIB_PACK__ BT_BLE_CMD;
+
+/** BT_CSU command structure */
+typedef struct _BT_CSU_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** reg type */
+	u8 type;
+	/** address */
+	u8 offset[4];
+	/** Data */
+	u8 value[2];
+} __ATTRIB_PACK__ BT_CSU_CMD;
+
+/** This function sets mac address */
+int bt_set_mac_address(bt_private *priv, u8 *mac);
+/** This function writes value to CSU registers */
+int bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value);
+/** BT set user defined init data and param */
+int bt_init_config(bt_private *priv, char *cfg_file);
+/** BT set uer defined init commands */
+int bt_init_cmds(bt_private *priv, char *init_cmds_file);
+/** BT process command */
+int bt_process_commands(bt_private *priv, u8 *cmd_data, u32 cmd_len);
+/** BT PMIC Configure command */
+int bt_pmic_configure(bt_private *priv);
+/** This function load the calibrate data */
+int bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac);
+/** This function load the calibrate ext data */
+int bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len);
+/** BT set user defined calibration data */
+int bt_cal_config(bt_private *priv, char *cfg_file, char *mac);
+/** BT set user defined calibration ext data */
+int bt_cal_config_ext(bt_private *priv, char *cfg_file);
+int bt_init_mac_address(bt_private *priv, char *mac);
+
+int bt_set_independent_reset(bt_private *priv);
+/** Bluetooth command : Independent reset */
+#define BT_CMD_INDEPENDENT_RESET     0x0D
+
+/** BT HCI command structure */
+typedef struct _BT_HCI_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** cmd type */
+	u8 cmd_type;
+	/** cmd len */
+	u8 cmd_len;
+	/** Data */
+	u8 data[6];
+} __ATTRIB_PACK__ BT_HCI_CMD;
+
+#endif /* _BT_DRV_H_ */
diff --git a/bt_sd8987/bt_char/bt_init.c b/bt_sd8987/bt_char/bt_init.c
new file mode 100644
index 0000000..270a4aa
--- /dev/null
+++ b/bt_sd8987/bt_char/bt_init.c
@@ -0,0 +1,871 @@
+/** @file bt_init.c
+  *
+  * @brief This file contains the init functions for BlueTooth
+  * driver.
+  *
+  * Copyright (C) 2011-2019, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/string.h>
+#include <linux/firmware.h>
+
+#include "bt_drv.h"
+
+extern int bt_req_fw_nowait;
+
+#define isxdigit(c)	(('0' <= (c) && (c) <= '9') \
+			 || ('a' <= (c) && (c) <= 'f') \
+			 || ('A' <= (c) && (c) <= 'F'))
+
+#define isdigit(c)	(('0' <= (c) && (c) <= '9'))
+#define isspace(c)  (c <= ' ' && (c == ' ' || (c <= 13 && c >= 9)))
+/**
+ *  @brief Returns hex value of a give character
+ *
+ *  @param chr	Character to be converted
+ *
+ *  @return	The converted character if chr is a valid hex, else 0
+ */
+static int
+bt_hexval(char chr)
+{
+	ENTER();
+
+	if (chr >= '0' && chr <= '9')
+		return chr - '0';
+	if (chr >= 'A' && chr <= 'F')
+		return chr - 'A' + 10;
+	if (chr >= 'a' && chr <= 'f')
+		return chr - 'a' + 10;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Extension of strsep lib command. This function will also take care
+ *	   escape character
+ *
+ *  @param s         A pointer to array of chars to process
+ *  @param delim     The delimiter character to end the string
+ *  @param esc       The escape character to ignore for delimiter
+ *
+ *  @return          Pointer to the separated string if delim found, else NULL
+ */
+static char *
+bt_strsep(char **s, char delim, char esc)
+{
+	char *se = *s, *sb;
+
+	ENTER();
+
+	if (!(*s) || (*se == '\0')) {
+		LEAVE();
+		return NULL;
+	}
+
+	for (sb = *s; *sb != '\0'; ++sb) {
+		if (*sb == esc && *(sb + 1) == esc) {
+			/*
+			 * We get a esc + esc seq then keep the one esc
+			 * and chop off the other esc character
+			 */
+			memmove(sb, sb + 1, strlen(sb));
+			continue;
+		}
+		if (*sb == esc && *(sb + 1) == delim) {
+			/*
+			 * We get a delim + esc seq then keep the delim
+			 * and chop off the esc character
+			 */
+			memmove(sb, sb + 1, strlen(sb));
+			continue;
+		}
+		if (*sb == delim)
+			break;
+	}
+
+	if (*sb == '\0')
+		sb = NULL;
+	else
+		*sb++ = '\0';
+
+	*s = sb;
+
+	LEAVE();
+	return se;
+}
+
+/**
+ *  @brief Returns hex value of a given ascii string
+ *
+ *  @param a	String to be converted
+ *
+ *  @return	hex value
+ */
+static int
+bt_atox(const char *a)
+{
+	int i = 0;
+	ENTER();
+	while (isxdigit(*a))
+		i = i * 16 + bt_hexval(*a++);
+
+	LEAVE();
+	return i;
+}
+
+/**
+ *  @brief Converts mac address from string to t_u8 buffer.
+ *
+ *  @param mac_addr The buffer to store the mac address in.
+ *  @param buf      The source of mac address which is a string.
+ *
+ *  @return	N/A
+ */
+static void
+bt_mac2u8(u8 *mac_addr, char *buf)
+{
+	char *begin, *end, *mac_buff;
+	int i;
+
+	ENTER();
+
+	if (!buf) {
+		LEAVE();
+		return;
+	}
+
+	mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL);
+	if (!mac_buff) {
+		LEAVE();
+		return;
+	}
+	memcpy(mac_buff, buf, strlen(buf));
+
+	begin = mac_buff;
+	for (i = 0; i < ETH_ALEN; ++i) {
+		end = bt_strsep(&begin, ':', '/');
+		if (end)
+			mac_addr[i] = bt_atox(end);
+	}
+
+	kfree(mac_buff);
+	LEAVE();
+}
+
+/**
+ *  @brief Returns integer value of a given ascii string
+ *
+ *  @param data    Converted data to be returned
+ *  @param a       String to be converted
+ *
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_atoi(int *data, char *a)
+{
+	int i, val = 0, len;
+
+	ENTER();
+
+	len = strlen(a);
+	if (!strncmp(a, "0x", 2)) {
+		a = a + 2;
+		len -= 2;
+		*data = bt_atox(a);
+		return BT_STATUS_SUCCESS;
+	}
+	for (i = 0; i < len; i++) {
+		if (isdigit(a[i])) {
+			val = val * 10 + (a[i] - '0');
+		} else {
+			PRINTM(ERROR, "Invalid char %c in string %s\n", a[i],
+			       a);
+			return BT_STATUS_FAILURE;
+		}
+	}
+	*data = val;
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief parse cal-data
+ *
+ *  @param src      a pointer to cal-data string
+ *  @param len      len of cal-data
+ *  @param dst      a pointer to return cal-data
+ *  @param dst_size size of dest buffer
+ *
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 *dst_size)
+{
+	const u8 *ptr;
+	u8 *dptr;
+	u32 count = 0;
+	int ret = BT_STATUS_FAILURE;
+
+	ENTER();
+	ptr = src;
+	dptr = dst;
+
+	while ((ptr - src) < len) {
+		if (*ptr && isspace(*ptr)) {
+			ptr++;
+			continue;
+		}
+
+		if (isxdigit(*ptr)) {
+			if ((dptr - dst) >= *dst_size) {
+				PRINTM(ERROR, "cal_file size too big!!!\n");
+				goto done;
+			}
+			*dptr++ = bt_atox((const char *)ptr);
+			ptr += 2;
+			count++;
+		} else {
+			ptr++;
+		}
+	}
+	if (dptr == dst) {
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	*dst_size = count;
+	ret = BT_STATUS_SUCCESS;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief parse ASCII format raw data to hex format
+ *
+ *    @param priv         bt_private
+ *    @param data         Source data
+ *    @param size         Source data length
+ *    @return             MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+bt_process_init_raw_cmds(bt_private *priv, u8 *data, u32 size)
+{
+	int ret = 0;
+	u8 *pos = data;
+	u8 *intf_s, *intf_e;
+	u8 *buf = NULL;
+	u8 *ptr = NULL;
+	u8 cmd_len = 0;
+	bool start_raw = false;
+	gfp_t flag;
+
+	flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
+	buf = kzalloc(MRVDRV_SIZE_OF_CMD_BUFFER, flag);
+
+	if (!buf) {
+		PRINTM(ERROR, "Could not allocate buffer space!\n");
+		return -EFAULT;
+	}
+	ptr = buf;
+	while ((pos - data) < size) {
+		while ((*pos == ' ' || *pos == '\t') && ((pos - data) < size))
+			pos++;
+		if (*pos == '#') {	/* Line comment */
+			while ((*pos != '\n') && ((pos - data) < size))
+				pos++;
+			pos++;
+		}
+		if ((*pos == '\r' && *(pos + 1) == '\n') || *pos == '\n' ||
+		    *pos == '\0') {
+			pos++;
+			continue;	/* Needn't process this line */
+		}
+
+		if (*pos == '}') {
+			/* For hostcmd data conf */
+			cmd_len = *(buf + sizeof(u16));
+			ret = bt_process_commands(priv, buf,
+						  cmd_len + BT_CMD_HEADER_SIZE);
+			memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+			ptr = buf;
+			start_raw = false;
+			pos++;
+			continue;
+		}
+
+		if (start_raw == false) {
+			intf_s = strchr(pos, '=');
+			if (intf_s)
+				intf_e = strchr(intf_s, '{');
+			else
+				intf_e = NULL;
+
+			if (intf_s && intf_e) {
+				start_raw = true;
+				pos = intf_e + 1;
+				continue;
+			}
+		}
+
+		if (start_raw) {
+			/* Raw data block exists */
+			while ((*pos != '\n') && ((pos - data) < size)) {
+				if (isxdigit(*pos)) {
+					if((ptr-buf) < MRVDRV_SIZE_OF_CMD_BUFFER)
+						*ptr++ = bt_atox(pos);
+					pos += 2;
+				} else
+					pos++;
+			}
+		}
+	}
+	kfree(buf);
+	return ret;
+}
+
+/**
+ *    @brief BT set user init commands
+ *
+ *    @param priv     BT private handle
+ *    @param init_cmds_file user init commands file
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+
+int
+bt_init_cmds(bt_private *priv, char *init_cmds_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if ((request_firmware(&cfg, init_cmds_file, priv->hotplug_device)) < 0) {
+		PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+		       init_cmds_file);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (cfg) {
+		ret = bt_process_init_raw_cmds(priv, (u8 *)cfg->data,
+					       cfg->size);
+	}
+	else {
+		ret = BT_STATUS_FAILURE;
+	}
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT get one line data from ASCII format data
+ *
+ *    @param data         Source data
+ *    @param size         Source data length
+ *    @param line_pos     Destination data
+ *    @return             -1 or length of the line
+ */
+int
+parse_cfg_get_line(u8 *data, u32 size, u8 *line_pos)
+{
+	static s32 pos;
+	u8 *src, *dest;
+
+	if (pos >= size) {	/* reach the end */
+		pos = 0;	/* Reset position for rfkill */
+		return -1;
+	}
+	memset(line_pos, 0, MAX_LINE_LEN);
+	src = data + pos;
+	dest = line_pos;
+
+	while ((dest - line_pos < MAX_LINE_LEN - 1) && pos < size &&
+	       *src != '\x0A' && *src != '\0') {
+		if (*src != ' ' && *src != '\t')	/* parse space */
+			*dest++ = *src++;
+		else
+			src++;
+		pos++;
+	}
+	*dest = '\0';
+	/* parse new line */
+	pos++;
+	return strlen((const char *)line_pos);
+}
+
+/**
+ *    @brief BT parse ASCII format data to MAC address
+ *
+ *    @param priv          BT private handle
+ *    @param data          Source data
+ *    @param size          data length
+ *    @return              BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_init_cfg(bt_private *priv, u8 *data, u32 size)
+{
+	u8 *pos;
+	u8 *intf_s, *intf_e;
+	u8 s[MAX_LINE_LEN];	/* 1 line data */
+	u32 line_len;
+	char dev_name[MAX_PARAM_LEN];
+	u8 buf[MAX_PARAM_LEN];
+	u8 bt_addr[MAX_MAC_ADDR_LEN];
+	u8 bt_mac[ETH_ALEN];
+	int setting = 0;
+	u8 type = 0;
+	u16 value = 0;
+	u32 offset = 0;
+	int ret = BT_STATUS_FAILURE;
+
+	memset(dev_name, 0, sizeof(dev_name));
+	memset(bt_addr, 0, sizeof(bt_addr));
+	memset(bt_mac, 0, sizeof(bt_mac));
+
+	while ((line_len = parse_cfg_get_line(data, size, s)) != -1) {
+		pos = s;
+		while (*pos == ' ' || *pos == '\t')
+			pos++;
+
+		if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') ||
+		    *pos == '\n' || *pos == '\0')
+			continue;	/* Need n't process this line */
+
+		/* Process MAC addr */
+		if (strncmp((char *)pos, "mac_addr", 8) == 0) {
+			intf_s = (u8 *)strchr((const char *)pos, '=');
+			if (intf_s != NULL)
+				intf_e = (u8 *)strchr((const char *)intf_s,
+						      ':');
+			else
+				intf_e = NULL;
+			if (intf_s != NULL && intf_e != NULL) {
+				if ((intf_e - intf_s) > MAX_PARAM_LEN) {
+					PRINTM(ERROR,
+					       "BT: Too long interface name %d\n",
+					       __LINE__);
+					goto done;
+				}
+				strncpy(dev_name, (const char *)intf_s + 1,
+					intf_e - intf_s - 1);
+				dev_name[intf_e - intf_s - 1] = '\0';
+				strncpy((char *)bt_addr,
+					(const char *)intf_e + 1,
+					MAX_MAC_ADDR_LEN - 1);
+				bt_addr[MAX_MAC_ADDR_LEN - 1] = '\0';
+				/* Convert MAC format */
+				bt_mac2u8(bt_mac, (char *)bt_addr);
+				PRINTM(CMD,
+				       "HCI: %s new BT Address " MACSTR "\n",
+				       dev_name, MAC2STR(bt_mac));
+				if (BT_STATUS_SUCCESS !=
+				    bt_set_mac_address(priv, bt_mac)) {
+					PRINTM(FATAL,
+					       "BT: Fail to set mac address\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+		}
+		/* Process REG value */
+		else if (strncmp((char *)pos, "bt_reg", 6) == 0) {
+			intf_s = (u8 *)strchr((const char *)pos, '=');
+			if (intf_s != NULL)
+				intf_e = (u8 *)strchr((const char *)intf_s,
+						      ',');
+			else
+				intf_e = NULL;
+			if (intf_s != NULL && intf_e != NULL) {
+				/* Copy type */
+				memset(buf, 0, sizeof(buf));
+				strncpy((char *)buf, (const char *)intf_s + 1,
+					1);
+				buf[1] = '\0';
+				if (0 == bt_atoi(&setting, (char *)buf))
+					type = (u8)setting;
+				else {
+					PRINTM(ERROR,
+					       "BT: Fail to parse reg type\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+			intf_s = intf_e + 1;
+			intf_e = (u8 *)strchr((const char *)intf_s, ',');
+			if (intf_e != NULL) {
+				if ((intf_e - intf_s) >= MAX_PARAM_LEN) {
+					PRINTM(ERROR,
+					       "BT: Regsier offset is too long %d\n",
+					       __LINE__);
+					goto done;
+				}
+				/* Copy offset */
+				memset(buf, 0, sizeof(buf));
+				strncpy((char *)buf, (const char *)intf_s,
+					intf_e - intf_s);
+				buf[intf_e - intf_s] = '\0';
+				if (0 == bt_atoi(&setting, (char *)buf))
+					offset = (u32)setting;
+				else {
+					PRINTM(ERROR,
+					       "BT: Fail to parse reg offset\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+			intf_s = intf_e + 1;
+			if ((strlen((const char *)intf_s) >= MAX_PARAM_LEN)) {
+				PRINTM(ERROR,
+				       "BT: Regsier value is too long %d\n",
+				       __LINE__);
+				goto done;
+			}
+			/* Copy value */
+			memset(buf, 0, sizeof(buf));
+			strncpy((char *)buf, (const char *)intf_s, sizeof(buf));
+			if (0 == bt_atoi(&setting, (char *)buf))
+				value = (u16) setting;
+			else {
+				PRINTM(ERROR, "BT: Fail to parse reg value\n");
+				goto done;
+			}
+
+			PRINTM(CMD,
+			       "BT: Write reg type: %d offset: 0x%x value: 0x%x\n",
+			       type, offset, value);
+			if (BT_STATUS_SUCCESS !=
+			    bt_write_reg(priv, type, offset, value)) {
+				PRINTM(FATAL,
+				       "BT: Write reg failed. type: %d offset: 0x%x value: 0x%x\n",
+				       type, offset, value);
+				goto done;
+			}
+		}
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief BT request init conf firmware callback
+ *        This function is invoked by request_firmware_nowait system call
+ *
+ * @param firmware  A pointer to firmware image
+ * @param context   A pointer to bt_private structure
+ *
+ * @return          N/A
+ */
+static void
+bt_request_init_user_conf_callback(const struct firmware *firmware,
+				   void *context)
+{
+	bt_private *priv = (bt_private *)context;
+
+	ENTER();
+
+	if (!firmware)
+		PRINTM(ERROR, "BT user init config request firmware failed\n");
+
+	priv->init_user_cfg = firmware;
+	priv->init_user_conf_wait_flag = TRUE;
+	wake_up_interruptible(&priv->init_user_conf_wait_q);
+
+	LEAVE();
+	return;
+}
+
+/**
+ *    @brief BT set user defined init data and param
+ *
+ *    @param priv     BT private handle
+ *    @param cfg_file user cofig file
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_init_config(bt_private *priv, char *cfg_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if ((request_firmware(&cfg, cfg_file, priv->hotplug_device)) < 0) {
+		PRINTM(FATAL, "BT: request_firmware() %s failed\n", cfg_file);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (cfg)
+		ret = bt_process_init_cfg(priv, (u8 *)cfg->data, cfg->size);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration data
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param data    a pointer to cal data
+ *    @param size    cal data size
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_cal_cfg(bt_private *priv, u8 *data, u32 size, char *mac)
+{
+	u8 bt_mac[ETH_ALEN];
+	u8 cal_data[32];
+	u8 *mac_data = NULL;
+	u32 cal_data_len;
+	int ret = BT_STATUS_FAILURE;
+	u8 *pcal_data = cal_data;
+
+	memset(bt_mac, 0, sizeof(bt_mac));
+	cal_data_len = sizeof(cal_data);
+	if (BT_STATUS_SUCCESS !=
+	    bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) {
+		goto done;
+	}
+	if (mac != NULL) {
+		/* Convert MAC format */
+		bt_mac2u8(bt_mac, mac);
+		PRINTM(CMD, "HCI: new BT Address " MACSTR "\n",
+		       MAC2STR(bt_mac));
+		mac_data = bt_mac;
+	}
+	if (BT_STATUS_SUCCESS != bt_load_cal_data(priv, pcal_data, mac_data)) {
+		PRINTM(FATAL, "BT: Fail to load calibrate data\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration EXT data
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param data    a pointer to cal data
+ *    @param size    cal data size
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_cal_cfg_ext(bt_private *priv, u8 *data, u32 size)
+{
+	u8 cal_data[128];
+	u32 cal_data_len;
+	int ret = BT_STATUS_FAILURE;
+
+	cal_data_len = sizeof(cal_data);
+	if (BT_STATUS_SUCCESS !=
+	    bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) {
+		goto done;
+	}
+	if (BT_STATUS_SUCCESS !=
+	    bt_load_cal_data_ext(priv, cal_data, cal_data_len)) {
+		PRINTM(FATAL, "BT: Fail to load calibrate data\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration file
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param cal_file calibration file name
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_cal_config(bt_private *priv, char *cal_file, char *mac)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      bt_request_init_user_conf_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#endif
+#endif
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: bt_cal_config() failed, error code = %#x cal_file=%s\n",
+			       ret, cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->init_user_conf_wait_flag = FALSE;
+		wait_event_interruptible(priv->init_user_conf_wait_q,
+					 priv->init_user_conf_wait_flag);
+		cfg = priv->init_user_cfg;
+	} else {
+		if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) <
+		    0) {
+			PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+			       cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cfg)
+		ret = bt_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size, mac);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration EXT file
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param cal_file calibration file name
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_cal_config_ext(bt_private *priv, char *cal_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      bt_request_init_user_conf_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#endif
+#endif
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: bt_cal_config_ext() failed, error code = %#x cal_file=%s\n",
+			       ret, cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->init_user_conf_wait_flag = FALSE;
+		wait_event_interruptible(priv->init_user_conf_wait_q,
+					 priv->init_user_conf_wait_flag);
+		cfg = priv->init_user_cfg;
+	} else {
+		if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) <
+		    0) {
+			PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+			       cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cfg)
+		ret = bt_process_cal_cfg_ext(priv, (u8 *)cfg->data, cfg->size);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT init mac address from bt_mac parametre when insmod
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param bt_mac  mac address buf
+ *    @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_init_mac_address(bt_private *priv, char *mac)
+{
+	u8 bt_mac[ETH_ALEN];
+	int ret = BT_STATUS_FAILURE;
+
+	ENTER();
+	memset(bt_mac, 0, sizeof(bt_mac));
+	bt_mac2u8(bt_mac, mac);
+	PRINTM(CMD, "HCI: New BT Address " MACSTR "\n", MAC2STR(bt_mac));
+	ret = bt_set_mac_address(priv, bt_mac);
+	if (ret != BT_STATUS_SUCCESS)
+		PRINTM(FATAL,
+		       "BT: Fail to set mac address from insmod parametre.\n");
+
+	LEAVE();
+	return ret;
+}
diff --git a/bt_sd8987/bt_char/bt_main.c b/bt_sd8987/bt_char/bt_main.c
new file mode 100644
index 0000000..ffd0936
--- /dev/null
+++ b/bt_sd8987/bt_char/bt_main.c
@@ -0,0 +1,4083 @@
+/** @file bt_main.c
+  *
+  * @brief This file contains the major functions in BlueTooth
+  * driver. It includes init, exit, open, close and main
+  * thread etc..
+  *
+  * Copyright (C) 2007-2019, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+  *
+  */
+/**
+  * @mainpage M-BT Linux Driver
+  *
+  * @section overview_sec Overview
+  *
+  * The M-BT is a Linux reference driver for Marvell Bluetooth chipset.
+  *
+  * @section copyright_sec Copyright
+  *
+  * Copyright (C) 2007-2019, Marvell International Ltd.
+  *
+  */
+#include <linux/module.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
+
+#include <linux/mmc/sdio_func.h>
+
+#include "bt_drv.h"
+#include "mbt_char.h"
+#include "bt_sdio.h"
+
+/** Version */
+#define VERSION "C4X14114"
+
+/** Driver version */
+static char mbt_driver_version[] = "SD8XXX-%s-" VERSION "-(" "FP" FPNUM ")"
+#ifdef DEBUG_LEVEL2
+	"-dbg"
+#endif
+	" ";
+
+/** SD8787 Card */
+#define CARD_SD8787     "SD8787"
+/** SD8777 Card */
+#define CARD_SD8777     "SD8777"
+/** SD8887 Card */
+#define CARD_SD8887     "SD8887"
+/** SD8897 Card */
+#define CARD_SD8897     "SD8897"
+/** SD8797 Card */
+#define CARD_SD8797     "SD8797"
+/** SD8977 Card */
+#define CARD_SD8977     "SD8977"
+/** SD8978 Card */
+#define CARD_SD8978     "SD8978"
+/** SD8997 Card */
+#define CARD_SD8997     "SD8997"
+/** SD8987 Card */
+#define CARD_SD8987     "SD8987"
+
+/** Declare and initialize fw_version */
+static char fw_version[32] = "0.0.0.p0";
+
+#define AID_SYSTEM        1000	/* system server */
+
+#define AID_BLUETOOTH     1002	/* bluetooth subsystem */
+
+#define AID_NET_BT_STACK  3008	/* bluetooth stack */
+
+/** Define module name */
+
+#define MODULE_NAME  "bt_fm_nfc"
+
+/** Declaration of chardev class */
+static struct class *chardev_class;
+
+/** Interface specific variables */
+static int mbtchar_minor;
+static int debugchar_minor;
+
+/**
+ * The global variable of a pointer to bt_private
+ * structure variable
+ **/
+bt_private *m_priv[MAX_BT_ADAPTER];
+
+/** Default Driver mode */
+static int drv_mode = (DRV_MODE_BT);
+
+/** BT interface name */
+static char *bt_name;
+/** BT debug interface name */
+static char *debug_name;
+
+/** fw reload flag */
+int bt_fw_reload;
+/** fw serial download flag */
+int bt_fw_serial = 1;
+
+/** Firmware flag */
+static int fw = 1;
+/** default powermode */
+static int psmode = 1;
+/** default BLE deep sleep */
+static int deep_sleep = 1;
+/** Default CRC check control */
+static int fw_crc_check = 1;
+/** init cmds file */
+static char *init_cmds;
+/** Init config file (MAC address, register etc.) */
+static char *init_cfg;
+/** Calibration config file (MAC address, init powe etc.) */
+static char *cal_cfg;
+/** Calibration config file EXT */
+static char *cal_cfg_ext;
+/** Init MAC address */
+static char *bt_mac;
+static int btindrst = -1;
+
+/** Setting mbt_drvdbg value based on DEBUG level */
+#ifdef DEBUG_LEVEL1
+#ifdef DEBUG_LEVEL2
+#define DEFAULT_DEBUG_MASK  (0xffffffff & ~DBG_EVENT)
+#else
+#define DEFAULT_DEBUG_MASK  (DBG_MSG | DBG_FATAL | DBG_ERROR)
+#endif /* DEBUG_LEVEL2 */
+u32 mbt_drvdbg = DEFAULT_DEBUG_MASK;
+#endif
+
+#ifdef CONFIG_OF
+static int dts_enable = 1;
+#endif
+
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+int mbt_pm_keep_power = 1;
+#endif
+
+static int debug_intf = 1;
+
+static int btpmic = 0;
+
+/** Offset of sequence number in event */
+#define OFFSET_SEQNUM 4
+
+/**
+ *  @brief handle received packet
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *
+ *  @return        N/A
+ */
+void
+bt_recv_frame(bt_private *priv, struct sk_buff *skb)
+{
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+	if (mbt_dev) {
+		skb->dev = (void *)mdev_bt;
+		mdev_bt->stat.byte_rx += skb->len;
+		mdev_recv_frame(skb);
+	}
+	return;
+}
+
+/**
+ *  @brief Alloc bt device
+ *
+ *  @return    pointer to structure mbt_dev or NULL
+ */
+struct mbt_dev *
+alloc_mbt_dev(void)
+{
+	struct mbt_dev *mbt_dev;
+	ENTER();
+
+	mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL);
+	if (!mbt_dev) {
+		LEAVE();
+		return NULL;
+	}
+
+	LEAVE();
+	return mbt_dev;
+}
+
+/**
+ *  @brief Alloc debug device
+ *
+ *  @return    pointer to structure debug_level or NULL
+ */
+struct debug_dev *
+alloc_debug_dev(void)
+{
+	struct debug_dev *debug_dev;
+	ENTER();
+
+	debug_dev = kzalloc(sizeof(struct debug_dev), GFP_KERNEL);
+	if (!debug_dev) {
+		LEAVE();
+		return NULL;
+	}
+
+	LEAVE();
+	return debug_dev;
+}
+
+/**
+ *  @brief Frees m_dev
+ *
+ *  @return    N/A
+ */
+void
+free_m_dev(struct m_dev *m_dev)
+{
+	ENTER();
+	kfree(m_dev->dev_pointer);
+	m_dev->dev_pointer = NULL;
+	LEAVE();
+}
+
+/**
+ *  @brief clean up m_devs
+ *
+ *  @return    N/A
+ */
+void
+clean_up_m_devs(bt_private *priv)
+{
+	struct m_dev *m_dev = NULL;
+	int i;
+
+	ENTER();
+	if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+		PRINTM(MSG, "BT: Delete %s\n", m_dev->name);
+		if (m_dev->spec_type == IANYWHERE_SPEC) {
+			if ((drv_mode & DRV_MODE_BT) && (mbtchar_minor > 0))
+				mbtchar_minor--;
+			m_dev->close(m_dev);
+			for (i = 0; i < 3; i++)
+				kfree_skb(((struct mbt_dev *)
+					   (m_dev->dev_pointer))->
+					  reassembly[i]);
+			/**  unregister m_dev to char_dev */
+			if (chardev_class)
+				chardev_cleanup_one(m_dev, chardev_class);
+			free_m_dev(m_dev);
+		}
+		priv->bt_dev.m_dev[BT_SEQ].dev_pointer = NULL;
+	}
+	if (priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer) {
+		m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+		PRINTM(MSG, "BT: Delete %s\n", m_dev->name);
+		if ((debug_intf) && (debugchar_minor > 0))
+			debugchar_minor--;
+		/** unregister m_dev to char_dev */
+		if (chardev_class)
+			chardev_cleanup_one(m_dev, chardev_class);
+		free_m_dev(m_dev);
+		priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = NULL;
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function verify the received event pkt
+ *
+ *  Event format:
+ *  +--------+--------+--------+--------+--------+
+ *  | Event  | Length |  ncmd  |      Opcode     |
+ *  +--------+--------+--------+--------+--------+
+ *  | 1-byte | 1-byte | 1-byte |      2-byte     |
+ *  +--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+check_evtpkt(bt_private *priv, struct sk_buff *skb)
+{
+	struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data;
+	struct hci_ev_cmd_complete *ec;
+	u16 opcode, ocf;
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (!priv->bt_dev.sendcmdflag) {
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	if (hdr->evt == HCI_EV_CMD_COMPLETE) {
+		ec = (struct hci_ev_cmd_complete *)
+			(skb->data + HCI_EVENT_HDR_SIZE);
+		opcode = __le16_to_cpu(ec->opcode);
+		ocf = hci_opcode_ocf(opcode);
+		PRINTM(CMD,
+		       "BT: CMD_COMPLTE opcode=0x%x, ocf=0x%x, send_cmd_opcode=0x%x\n",
+		       opcode, ocf, priv->bt_dev.send_cmd_opcode);
+		if (opcode != priv->bt_dev.send_cmd_opcode) {
+			ret = BT_STATUS_FAILURE;
+			goto exit;
+		}
+		switch (ocf) {
+		case BT_CMD_MODULE_CFG_REQ:
+		case BT_CMD_BLE_DEEP_SLEEP:
+		case BT_CMD_CONFIG_MAC_ADDR:
+		case BT_CMD_CSU_WRITE_REG:
+		case BT_CMD_LOAD_CONFIG_DATA:
+		case BT_CMD_LOAD_CONFIG_DATA_EXT:
+		case BT_CMD_AUTO_SLEEP_MODE:
+		case BT_CMD_HOST_SLEEP_CONFIG:
+		case BT_CMD_SDIO_PULL_CFG_REQ:
+		case BT_CMD_SET_EVT_FILTER:
+			// case BT_CMD_ENABLE_DEVICE_TESTMODE:
+		case BT_CMD_PMIC_CONFIGURE:
+		case BT_CMD_INDEPENDENT_RESET:
+			priv->bt_dev.sendcmdflag = FALSE;
+			priv->adapter->cmd_complete = TRUE;
+			wake_up_interruptible(&priv->adapter->cmd_wait_q);
+			break;
+		case BT_CMD_GET_FW_VERSION:
+			{
+				u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE +
+					   sizeof(struct hci_ev_cmd_complete) +
+					   1);
+				snprintf(fw_version, sizeof(fw_version),
+					 "%u.%u.%u.p%u", pos[2], pos[1], pos[0],
+					 pos[3]);
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+				break;
+			}
+#ifdef BLE_WAKEUP
+		case BT_CMD_GET_WHITELIST:
+			{
+				u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE +
+					   sizeof(struct hci_ev_cmd_complete) +
+					   1);
+
+				if ((hdr->plen -
+				     sizeof(struct hci_ev_cmd_complete) - 1) <=
+				    sizeof(priv->white_list))
+					memcpy(priv->white_list, pos,
+					       hdr->plen -
+					       sizeof(struct
+						      hci_ev_cmd_complete) - 1);
+
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+				break;
+			}
+#endif
+		case BT_CMD_RESET:
+		case BT_CMD_ENABLE_WRITE_SCAN:
+#ifdef BLE_WAKEUP
+		case HCI_BT_SET_EVENTMASK_OCF:
+		case HCI_BLE_ADD_DEV_TO_WHITELIST_OCF:
+		case HCI_BLE_SET_SCAN_PARAMETERS_OCF:
+		case HCI_BLE_SET_SCAN_ENABLE_OCF:
+#endif
+			{
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				if (priv->adapter->wait_event_timeout == TRUE) {
+					wake_up(&priv->adapter->cmd_wait_q);
+					priv->adapter->wait_event_timeout =
+						FALSE;
+				} else
+					wake_up_interruptible(&priv->adapter->
+							      cmd_wait_q);
+			}
+			break;
+		case BT_CMD_HOST_SLEEP_ENABLE:
+			priv->bt_dev.sendcmdflag = FALSE;
+			break;
+		default:
+			/** Ignore command not defined but send by driver */
+			if (opcode == priv->bt_dev.send_cmd_opcode) {
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			} else {
+				ret = BT_STATUS_FAILURE;
+			}
+			break;
+		}
+	} else
+		ret = BT_STATUS_FAILURE;
+exit:
+	if (ret == BT_STATUS_SUCCESS)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+/**
+*  @brief This function stores the FW dumps received from events
+*
+*  @param priv    A pointer to bt_private structure
+*  @param skb     A pointer to rx skb
+*
+*  @return        N/A
+*/
+void
+bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len)
+{
+	struct file *pfile_fwdump = NULL;
+	loff_t pos = 0;
+	u16 seqnum = 0;
+	struct timeval t;
+	u32 sec;
+
+	ENTER();
+
+	seqnum = __le16_to_cpu(*(u16 *) (buf + OFFSET_SEQNUM));
+
+	if (priv->adapter->fwdump_fname && seqnum != 1) {
+		pfile_fwdump =
+			filp_open((const char *)priv->adapter->fwdump_fname,
+				  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		if (IS_ERR(pfile_fwdump)) {
+			PRINTM(MSG, "Cannot create firmware dump file.\n");
+			LEAVE();
+			return;
+		}
+	} else {
+		if (!priv->adapter->fwdump_fname) {
+			gfp_t flag;
+			flag = (in_atomic() ||
+				irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
+			priv->adapter->fwdump_fname = kzalloc(64, flag);
+		} else
+			memset(priv->adapter->fwdump_fname, 0, 64);
+
+		do_gettimeofday(&t);
+		sec = (u32)t.tv_sec;
+		sprintf(priv->adapter->fwdump_fname, "%s%u",
+			"/var/log/bt_fwdump_", sec);
+		pfile_fwdump =
+			filp_open(priv->adapter->fwdump_fname,
+				  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		if (IS_ERR(pfile_fwdump)) {
+			sprintf(priv->adapter->fwdump_fname, "%s%u",
+				"/data/bt_fwdump_", sec);
+			pfile_fwdump =
+				filp_open((const char *)priv->adapter->
+					  fwdump_fname,
+					  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		}
+	}
+
+	if (IS_ERR(pfile_fwdump)) {
+		PRINTM(MSG, "Cannot create firmware dump file\n");
+		LEAVE();
+		return;
+	}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	vfs_write(pfile_fwdump, buf, len, &pos);
+#else
+	kernel_write(pfile_fwdump, buf, len, &pos);
+#endif
+	filp_close(pfile_fwdump, NULL);
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function process the received event
+ *
+ *  Event format:
+ *  +--------+--------+--------+--------+-----+
+ *  |   EC   | Length |           Data        |
+ *  +--------+--------+--------+--------+-----+
+ *  | 1-byte | 1-byte |          n-byte       |
+ *  +--------+--------+--------+--------+-----+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_event(bt_private *priv, struct sk_buff *skb)
+{
+	int ret = BT_STATUS_SUCCESS;
+#ifdef DEBUG_LEVEL1
+	struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+#endif
+	BT_EVENT *pevent;
+
+	ENTER();
+	if (!m_dev) {
+		PRINTM(CMD, "BT: bt_process_event without m_dev\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pevent = (BT_EVENT *)skb->data;
+	if (pevent->EC != 0xff) {
+		PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pevent->EC);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	switch (pevent->data[0]) {
+	case BT_CMD_AUTO_SLEEP_MODE:
+		if (pevent->data[2] == BT_STATUS_SUCCESS) {
+			if (pevent->data[1] == BT_PS_ENABLE)
+				priv->adapter->psmode = 1;
+			else
+				priv->adapter->psmode = 0;
+			PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name,
+			       (priv->adapter->psmode) ? "Enable" : "Disable");
+
+		} else {
+			PRINTM(CMD, "BT: PS Mode Command Fail %s\n",
+			       m_dev->name);
+		}
+		break;
+	case BT_CMD_HOST_SLEEP_CONFIG:
+		if (pevent->data[3] == BT_STATUS_SUCCESS) {
+			PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n",
+			       m_dev->name, pevent->data[1], pevent->data[2]);
+		} else {
+			PRINTM(CMD, "BT: %s: HSCFG Command Fail\n",
+			       m_dev->name);
+		}
+		break;
+	case BT_CMD_HOST_SLEEP_ENABLE:
+		if (pevent->data[1] == BT_STATUS_SUCCESS) {
+			priv->adapter->hs_state = HS_ACTIVATED;
+			if (priv->adapter->suspend_fail == FALSE) {
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+				bt_is_suspended(priv);
+#endif
+#endif
+#endif
+				if (priv->adapter->wait_event_timeout) {
+					wake_up(&priv->adapter->cmd_wait_q);
+					priv->adapter->wait_event_timeout =
+						FALSE;
+				} else
+					wake_up_interruptible(&priv->adapter->
+							      cmd_wait_q);
+
+			}
+			if (priv->adapter->psmode)
+				priv->adapter->ps_state = PS_SLEEP;
+			PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n",
+			       m_dev->name);
+
+		} else {
+			PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name);
+		}
+		break;
+	case BT_CMD_MODULE_CFG_REQ:
+		if ((priv->bt_dev.sendcmdflag == TRUE) &&
+		    ((pevent->data[1] == MODULE_BRINGUP_REQ)
+		     || (pevent->data[1] == MODULE_SHUTDOWN_REQ))) {
+			if (pevent->data[1] == MODULE_BRINGUP_REQ) {
+				PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+				       (pevent->data[2] && (pevent->data[2] !=
+							    MODULE_CFG_RESP_ALREADY_UP))
+				       ? "Bring up Fail" : "Bring up success");
+				priv->bt_dev.devType = pevent->data[3];
+				PRINTM(CMD, "devType:%s\n",
+				       (pevent->data[3] ==
+					DEV_TYPE_AMP) ? "AMP controller" :
+				       "BR/EDR controller");
+				priv->bt_dev.devFeature = pevent->data[4];
+				PRINTM(CMD, "devFeature:  %s,    %s,    %s"
+				       "\n",
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BT) ?
+					"BT Feature" : "No BT Feature"),
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BTAMP) ?
+					"BTAMP Feature" : "No BTAMP Feature"),
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BLE) ?
+					"BLE Feature" : "No BLE Feature")
+					);
+			}
+			if (pevent->data[1] == MODULE_SHUTDOWN_REQ) {
+				PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+				       (pevent->data[2]) ? "Shut down Fail"
+				       : "Shut down success");
+
+			}
+			if (pevent->data[2]) {
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			}
+		} else {
+			PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n");
+			ret = BT_STATUS_FAILURE;
+		}
+		break;
+	case BT_EVENT_POWER_STATE:
+		if (pevent->data[1] == BT_PS_SLEEP)
+			priv->adapter->ps_state = PS_SLEEP;
+		if (priv->adapter->ps_state == PS_SLEEP
+		    && (priv->card_type == CARD_TYPE_SD8887)
+			)
+			os_sched_timeout(5);
+		PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+		       (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
+
+		break;
+	case BT_CMD_SDIO_PULL_CFG_REQ:
+		if (pevent->data[pevent->length - 1] == BT_STATUS_SUCCESS)
+			PRINTM(CMD, "BT: %s: SDIO pull configuration success\n",
+			       m_dev->name);
+
+		else {
+			PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n",
+			       m_dev->name);
+
+		}
+		break;
+	default:
+		PRINTM(CMD, "BT: Unknown Event=%d %s\n", pevent->data[0],
+		       m_dev->name);
+		ret = BT_STATUS_FAILURE;
+		break;
+	}
+exit:
+	if (ret == BT_STATUS_SUCCESS)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function save the dump info to file
+ *
+ *  @param dir_name     directory name
+ *  @param file_name    file_name
+ *  @param buf			buffer
+ *  @param buf_len		buffer length
+ *
+ *  @return   		    0 --success otherwise fail
+ */
+int
+bt_save_dump_info_to_file(char *dir_name, char *file_name, u8 *buf, u32 buf_len)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct file *pfile = NULL;
+	u8 name[64];
+	loff_t pos;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	mm_segment_t fs;
+#endif
+
+	ENTER();
+
+	if (!dir_name || !file_name || !buf) {
+		PRINTM(ERROR, "Can't save dump info to file\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	memset(name, 0, sizeof(name));
+	snprintf((char *)name, sizeof(name), "%s/%s", dir_name, file_name);
+	pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644);
+	if (IS_ERR(pfile)) {
+		PRINTM(MSG,
+		       "Create file %s error, try to save dump file in /var\n",
+		       name);
+		memset(name, 0, sizeof(name));
+		snprintf((char *)name, sizeof(name), "%s/%s", "/var",
+			 file_name);
+		pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644);
+	}
+	if (IS_ERR(pfile)) {
+		PRINTM(ERROR, "Create Dump file for %s error\n", name);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	PRINTM(MSG, "Dump data %s saved in %s\n", file_name, name);
+
+	pos = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	vfs_write(pfile, (const char __user *)buf, buf_len, &pos);
+	set_fs(fs);
+#else
+	kernel_write(pfile, (const char __user *)buf, buf_len, &pos);
+#endif
+	filp_close(pfile, NULL);
+
+	PRINTM(MSG, "Dump data %s saved in %s successfully\n", file_name, name);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+#define DEBUG_HOST_READY		0xEE
+#define DEBUG_FW_DONE			0xFF
+#define DUMP_MAX_POLL_TRIES			200
+
+#define DEBUG_DUMP_CTRL_REG_8897               0xE2
+#define DEBUG_DUMP_START_REG_8897              0xE3
+#define DEBUG_DUMP_END_REG_8897                0xEA
+#define DEBUG_DUMP_CTRL_REG_8887               0xA2
+#define DEBUG_DUMP_START_REG_8887              0xA3
+#define DEBUG_DUMP_END_REG_8887                0xAA
+#define DEBUG_DUMP_CTRL_REG_8977               0xF0
+#define DEBUG_DUMP_START_REG_8977              0xF1
+#define DEBUG_DUMP_END_REG_8977                0xF8
+#define DEBUG_DUMP_CTRL_REG_8978               0xF0
+#define DEBUG_DUMP_START_REG_8978              0xF1
+#define DEBUG_DUMP_END_REG_8978                0xF8
+#define DEBUG_DUMP_CTRL_REG_8997               0xF0
+#define DEBUG_DUMP_START_REG_8997              0xF1
+#define DEBUG_DUMP_END_REG_8997                0xF8
+#define DEBUG_DUMP_CTRL_REG_8987               0xF0
+#define DEBUG_DUMP_START_REG_8987              0xF1
+#define DEBUG_DUMP_END_REG_8987                0xF8
+
+typedef enum {
+	DUMP_TYPE_ITCM = 0,
+	DUMP_TYPE_DTCM = 1,
+	DUMP_TYPE_SQRAM = 2,
+	DUMP_TYPE_APU_REGS = 3,
+	DUMP_TYPE_CIU_REGS = 4,
+	DUMP_TYPE_ICU_REGS = 5,
+	DUMP_TYPE_MAC_REGS = 6,
+	DUMP_TYPE_EXTEND_7 = 7,
+	DUMP_TYPE_EXTEND_8 = 8,
+	DUMP_TYPE_EXTEND_9 = 9,
+	DUMP_TYPE_EXTEND_10 = 10,
+	DUMP_TYPE_EXTEND_11 = 11,
+	DUMP_TYPE_EXTEND_12 = 12,
+	DUMP_TYPE_EXTEND_13 = 13,
+	DUMP_TYPE_EXTEND_LAST = 14
+} dumped_mem_type;
+
+#define MAX_NAME_LEN               8
+#define MAX_FULL_NAME_LEN               32
+
+/** Memory type mapping structure */
+typedef struct {
+	/** memory name */
+	u8 mem_name[MAX_NAME_LEN];
+	/** memory pointer */
+	u8 *mem_Ptr;
+	/** file structure */
+	struct file *pfile_mem;
+	/** donbe flag */
+	u8 done_flag;
+	/** dump type */
+	u8 type;
+} memory_type_mapping;
+
+memory_type_mapping bt_mem_type_mapping_tbl[] = {
+	{"ITCM", NULL, NULL, 0xF0, FW_DUMP_TYPE_MEM_ITCM},
+	{"DTCM", NULL, NULL, 0xF1, FW_DUMP_TYPE_MEM_DTCM},
+	{"SQRAM", NULL, NULL, 0xF2, FW_DUMP_TYPE_MEM_SQRAM},
+	{"APU", NULL, NULL, 0xF3, FW_DUMP_TYPE_REG_APU},
+	{"CIU", NULL, NULL, 0xF4, FW_DUMP_TYPE_REG_CIU},
+	{"ICU", NULL, NULL, 0xF5, FW_DUMP_TYPE_REG_ICU},
+	{"MAC", NULL, NULL, 0xF6, FW_DUMP_TYPE_REG_MAC},
+	{"EXT7", NULL, NULL, 0xF7},
+	{"EXT8", NULL, NULL, 0xF8},
+	{"EXT9", NULL, NULL, 0xF9},
+	{"EXT10", NULL, NULL, 0xFA},
+	{"EXT11", NULL, NULL, 0xFB},
+	{"EXT12", NULL, NULL, 0xFC},
+	{"EXT13", NULL, NULL, 0xFD},
+	{"EXTLAST", NULL, NULL, 0xFE},
+};
+
+typedef enum {
+	RDWR_STATUS_SUCCESS = 0,
+	RDWR_STATUS_FAILURE = 1,
+	RDWR_STATUS_DONE = 2
+} rdwr_status;
+
+/**
+ *  @brief This function read/write firmware via cmd52
+ *
+ *  @param phandle   A pointer to moal_handle
+ *
+ *  @return         MLAN_STATUS_SUCCESS
+ */
+rdwr_status
+bt_cmd52_rdwr_firmware(bt_private *priv, u8 doneflag)
+{
+	int ret = 0;
+	int tries = 0;
+	u8 ctrl_data = 0;
+	u8 dbg_dump_ctrl_reg = 0;
+
+	if (priv->card_type == CARD_TYPE_SD8887)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8887;
+	else if (priv->card_type == CARD_TYPE_SD8897)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8897;
+	else if (priv->card_type == CARD_TYPE_SD8977)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8977;
+	else if (priv->card_type == CARD_TYPE_SD8978)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8978;
+	else if (priv->card_type == CARD_TYPE_SD8997)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8997;
+	else if (priv->card_type == CARD_TYPE_SD8987)
+		dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8987;
+
+	ret = sd_write_reg(priv, dbg_dump_ctrl_reg, DEBUG_HOST_READY);
+	if (ret) {
+		PRINTM(ERROR, "SDIO Write ERR\n");
+		return RDWR_STATUS_FAILURE;
+	}
+	for (tries = 0; tries < DUMP_MAX_POLL_TRIES; tries++) {
+		ret = sd_read_reg(priv, dbg_dump_ctrl_reg, &ctrl_data);
+		if (ret) {
+			PRINTM(ERROR, "SDIO READ ERR\n");
+			return RDWR_STATUS_FAILURE;
+		}
+		if (ctrl_data == DEBUG_FW_DONE)
+			break;
+		if (doneflag && ctrl_data == doneflag)
+			return RDWR_STATUS_DONE;
+		if (ctrl_data != DEBUG_HOST_READY) {
+			PRINTM(INFO,
+			       "The ctrl reg was changed, re-try again!\n");
+			ret = sd_write_reg(priv, dbg_dump_ctrl_reg,
+					   DEBUG_HOST_READY);
+			if (ret) {
+				PRINTM(ERROR, "SDIO Write ERR\n");
+				return RDWR_STATUS_FAILURE;
+			}
+		}
+		udelay(100);
+	}
+	if (ctrl_data == DEBUG_HOST_READY) {
+		PRINTM(ERROR, "Fail to pull ctrl_data\n");
+		return RDWR_STATUS_FAILURE;
+	}
+
+	return RDWR_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function dump firmware memory to file
+ *
+ *  @param phandle   A pointer to moal_handle
+ *
+ *  @return         N/A
+ */
+void
+bt_dump_firmware_info_v2(bt_private *priv)
+{
+	int ret = 0;
+	unsigned int reg, reg_start, reg_end;
+	u8 *dbg_ptr = NULL;
+	u8 dump_num = 0;
+	u8 idx = 0;
+	u8 doneflag = 0;
+	rdwr_status stat;
+	u8 i = 0;
+	u8 read_reg = 0;
+	u32 memory_size = 0;
+	u8 path_name[64], file_name[32];
+	u8 *end_ptr = NULL;
+	u8 dbg_dump_start_reg = 0;
+	u8 dbg_dump_end_reg = 0;
+
+	if (!priv) {
+		PRINTM(ERROR, "Could not dump firmwware info\n");
+		return;
+	}
+
+	if ((priv->card_type != CARD_TYPE_SD8887) &&
+	    (priv->card_type != CARD_TYPE_SD8897)
+	    && (priv->card_type != CARD_TYPE_SD8977) &&
+	    (priv->card_type != CARD_TYPE_SD8997)
+	    && (priv->card_type != CARD_TYPE_SD8987) &&
+	    (priv->card_type != CARD_TYPE_SD8978)) {
+		PRINTM(MSG, "card_type %d don't support FW dump\n",
+		       priv->card_type);
+		return;
+	}
+
+	memset(path_name, 0, sizeof(path_name));
+	strcpy((char *)path_name, "/data");
+	PRINTM(MSG, "Create DUMP directory success:dir_name=%s\n", path_name);
+
+	if (priv->card_type == CARD_TYPE_SD8887) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8887;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8887;
+	} else if (priv->card_type == CARD_TYPE_SD8897) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8897;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8897;
+	} else if (priv->card_type == CARD_TYPE_SD8977) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8977;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8977;
+	} else if (priv->card_type == CARD_TYPE_SD8978) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8978;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8978;
+	} else if (priv->card_type == CARD_TYPE_SD8997) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8997;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8997;
+	} else if (priv->card_type == CARD_TYPE_SD8987) {
+		dbg_dump_start_reg = DEBUG_DUMP_START_REG_8987;
+		dbg_dump_end_reg = DEBUG_DUMP_END_REG_8987;
+	}
+
+	sbi_wakeup_firmware(priv);
+	priv->fw_dump = TRUE;
+	/* start dump fw memory */
+	PRINTM(MSG, "==== DEBUG MODE OUTPUT START ====\n");
+	/* read the number of the memories which will dump */
+	if (RDWR_STATUS_FAILURE == bt_cmd52_rdwr_firmware(priv, doneflag))
+		goto done;
+	reg = dbg_dump_start_reg;
+	ret = sd_read_reg(priv, reg, &dump_num);
+	if (ret) {
+		PRINTM(MSG, "SDIO READ MEM NUM ERR\n");
+		goto done;
+	}
+
+	/* read the length of every memory which will dump */
+	for (idx = 0; idx < dump_num; idx++) {
+		if (RDWR_STATUS_FAILURE ==
+		    bt_cmd52_rdwr_firmware(priv, doneflag))
+			goto done;
+		memory_size = 0;
+		reg = dbg_dump_start_reg;
+		for (i = 0; i < 4; i++) {
+			ret = sd_read_reg(priv, reg, &read_reg);
+			if (ret) {
+				PRINTM(MSG, "SDIO READ ERR\n");
+				goto done;
+			}
+			memory_size |= (read_reg << i * 8);
+			reg++;
+		}
+		if (memory_size == 0) {
+			PRINTM(MSG, "Firmware Dump Finished!\n");
+			break;
+		} else {
+			PRINTM(MSG, "%s_SIZE=0x%x\n",
+			       bt_mem_type_mapping_tbl[idx].mem_name,
+			       memory_size);
+			bt_mem_type_mapping_tbl[idx].mem_Ptr =
+				vmalloc(memory_size + 1);
+			if ((ret != BT_STATUS_SUCCESS) ||
+			    !bt_mem_type_mapping_tbl[idx].mem_Ptr) {
+				PRINTM(ERROR,
+				       "Error: vmalloc %s buffer failed!!!\n",
+				       bt_mem_type_mapping_tbl[idx].mem_name);
+				goto done;
+			}
+			dbg_ptr = bt_mem_type_mapping_tbl[idx].mem_Ptr;
+			end_ptr = dbg_ptr + memory_size;
+		}
+		doneflag = bt_mem_type_mapping_tbl[idx].done_flag;
+		PRINTM(MSG, "Start %s output, please wait...\n",
+		       bt_mem_type_mapping_tbl[idx].mem_name);
+		do {
+			stat = bt_cmd52_rdwr_firmware(priv, doneflag);
+			if (RDWR_STATUS_FAILURE == stat)
+				goto done;
+
+			reg_start = dbg_dump_start_reg;
+			reg_end = dbg_dump_end_reg;
+			for (reg = reg_start; reg <= reg_end; reg++) {
+				ret = sd_read_reg(priv, reg, dbg_ptr);
+				if (ret) {
+					PRINTM(MSG, "SDIO READ ERR\n");
+					goto done;
+				}
+				if (dbg_ptr < end_ptr)
+					dbg_ptr++;
+				else
+					PRINTM(MSG,
+					       "pre-allocced buf is not enough\n");
+			}
+			if (RDWR_STATUS_DONE == stat) {
+				PRINTM(MSG, "%s done:"
+				       "size = 0x%x\n",
+				       bt_mem_type_mapping_tbl[idx].mem_name,
+				       (unsigned int)(dbg_ptr -
+						      bt_mem_type_mapping_tbl
+						      [idx].mem_Ptr));
+				memset(file_name, 0, sizeof(file_name));
+				snprintf((char *)file_name, sizeof(file_name),
+					 "%s%s", "file_bt_",
+					 bt_mem_type_mapping_tbl[idx].mem_name);
+				if (BT_STATUS_SUCCESS !=
+				    bt_save_dump_info_to_file((char *)path_name,
+							      (char *)file_name,
+							      bt_mem_type_mapping_tbl
+							      [idx].mem_Ptr,
+							      memory_size))
+					PRINTM(MSG,
+					       "Can't save dump file %s in %s\n",
+					       file_name, path_name);
+				vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr);
+				bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL;
+				break;
+			}
+		} while (1);
+	}
+	PRINTM(MSG, "==== DEBUG MODE OUTPUT END ====\n");
+	/* end dump fw memory */
+done:
+	priv->fw_dump = FALSE;
+	for (idx = 0; idx < dump_num; idx++) {
+		if (bt_mem_type_mapping_tbl[idx].mem_Ptr) {
+			vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr);
+			bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL;
+		}
+	}
+	PRINTM(MSG, "==== DEBUG MODE END ====\n");
+	return;
+}
+
+/**
+ *  @brief This function shows debug info for timeout of command sending.
+ *
+ *  @param adapter  A pointer to bt_private
+ *  @param cmd      Timeout command id
+ *
+ *  @return         N/A
+ */
+static void
+bt_cmd_timeout_func(bt_private *priv, u16 cmd)
+{
+	bt_adapter *adapter = priv->adapter;
+	ENTER();
+
+	adapter->num_cmd_timeout++;
+
+	PRINTM(ERROR, "Version = %s\n", adapter->drv_ver);
+	PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd);
+	PRINTM(ERROR, "Number of command timeout = %d\n",
+	       adapter->num_cmd_timeout);
+	PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter);
+	PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode);
+	PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state);
+	PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state);
+	PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip);
+	PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail);
+	PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended);
+	PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries);
+	PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete);
+	PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv);
+	PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done);
+	PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending);
+	PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg);
+	bt_dump_sdio_regs(priv);
+	LEAVE();
+}
+
+/**
+ *  @brief This function queue frame
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    N/A
+ */
+static void
+bt_queue_frame(bt_private *priv, struct sk_buff *skb)
+{
+	skb_queue_tail(&priv->adapter->tx_queue, skb);
+}
+
+/**
+ *  @brief This function send reset cmd to firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return	       BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_reset_command(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((RESET_OGF << 10) | BT_CMD_RESET);
+	pcmd->length = 0x00;
+	pcmd->cmd_type = 0x00;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, 3);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "Queue Reset Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Reset timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_RESET);
+	} else {
+		PRINTM(CMD, "BT: Reset Command done\n");
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends module cfg cmd to firmware
+ *
+ *  Command format:
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     OCF OGF     | Length |                Data               |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     2-byte      | 1-byte |               4-byte              |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param subcmd  sub command
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_module_cfg_cmd(bt_private *priv, int subcmd)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "BT: No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ);
+	pcmd->length = 1;
+	pcmd->data[0] = subcmd;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "Queue module cfg Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	/*
+	   On some Android platforms certain delay is needed for HCI daemon to
+	   remove this module and close itself gracefully. Otherwise it hangs.
+	   This 10ms delay is a workaround for such platforms as the root cause
+	   has not been found yet. */
+	mdelay(10);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: module_cfg_cmd(%#x): timeout sendcmdflag=%d\n",
+		       subcmd, priv->bt_dev.sendcmdflag);
+		bt_cmd_timeout_func(priv, BT_CMD_MODULE_CFG_REQ);
+	} else {
+		PRINTM(CMD, "BT: module cfg Command done\n");
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables power save mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_ps(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE);
+	if (priv->bt_dev.psmode)
+		pcmd->data[0] = BT_PS_ENABLE;
+	else
+		pcmd->data[0] = BT_PS_DISABLE;
+	if (priv->bt_dev.idle_timeout) {
+		pcmd->length = 3;
+		pcmd->data[1] = (u8)(priv->bt_dev.idle_timeout & 0x00ff);
+		pcmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8;
+	} else {
+		pcmd->length = 1;
+	}
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: psmode timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_AUTO_SLEEP_MODE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends hscfg command
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_hscfg_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG);
+	pcmd->length = 2;
+	pcmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8;
+	pcmd->data[1] = (u8)(priv->bt_dev.gpio_gap & 0x00ff);
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0], pcmd->data[1]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: HSCFG timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_CONFIG);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends sdio pull ctrl command
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_sdio_pull_ctrl_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ);
+	pcmd->length = 4;
+	pcmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff);
+	pcmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8;
+	pcmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16;
+	pcmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD,
+	       "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[1], pcmd->data[0],
+	       pcmd->data[3], pcmd->data[2]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SDIO_PULL_CFG_REQ);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends command to configure PMIC
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_pmic_configure(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_PMIC_CONFIGURE);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue PMIC Configure Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: PMIC Configure timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_PMIC_CONFIGURE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables host sleep
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param is_shutdown  indicate shutdown mode
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_hs(bt_private *priv, bool is_shutdown)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	priv->adapter->suspend_fail = FALSE;
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->adapter->wait_event_timeout = is_shutdown;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	PRINTM(CMD, "Queue hs enable Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (is_shutdown) {
+		if (!os_wait_timeout
+		    (priv->adapter->cmd_wait_q, priv->adapter->hs_state,
+		     WAIT_UNTIL_HS_STATE_CHANGED)) {
+			PRINTM(MSG, "BT: Enable host sleep timeout:\n");
+			priv->adapter->wait_event_timeout = FALSE;
+			bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE);
+		}
+	} else {
+		if (!os_wait_interruptible_timeout
+		    (priv->adapter->cmd_wait_q, priv->adapter->hs_state,
+		     WAIT_UNTIL_HS_STATE_CHANGED)) {
+			PRINTM(MSG, "BT: Enable host sleep timeout:\n");
+			bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE);
+		}
+	}
+	OS_INT_DISABLE;
+	if ((priv->adapter->hs_state == HS_ACTIVATED) ||
+	    (priv->adapter->is_suspended == TRUE)) {
+		OS_INT_RESTORE;
+		PRINTM(MSG, "BT: suspend success! skip=%d\n",
+		       priv->adapter->hs_skip);
+	} else {
+		priv->adapter->suspend_fail = TRUE;
+		OS_INT_RESTORE;
+		priv->adapter->hs_skip++;
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG,
+		       "BT: suspend skipped! "
+		       "state=%d skip=%d ps_state= %d WakeupTries=%d\n",
+		       priv->adapter->hs_state, priv->adapter->hs_skip,
+		       priv->adapter->ps_state, priv->adapter->WakeupTries);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Set Evt Filter
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_evt_filter(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_SET_EVT_FILTER);
+	pcmd->length = 0x03;
+	pcmd->data[0] = 0x02;
+	pcmd->data[1] = 0x00;
+	pcmd->data[2] = 0x03;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Set Evt Filter Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set Evt Filter timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SET_EVT_FILTER);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Enable Write Scan - Page and Inquiry
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_write_scan(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_ENABLE_WRITE_SCAN);
+	pcmd->length = 0x01;
+	pcmd->data[0] = 0x03;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Enable Write Scan Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Enable Write Scan timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_ENABLE_WRITE_SCAN);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Enable Device under test mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_device_under_testmode(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((0x06 << 10) | BT_CMD_ENABLE_DEVICE_TESTMODE);
+	pcmd->length = 0x00;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue enable device under testmode Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Enable Device under TEST mode timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_ENABLE_DEVICE_TESTMODE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables test mode and send cmd
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_test_mode(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	/** Set Evt Filter Command */
+	ret = bt_set_evt_filter(priv);
+	if (ret != BT_STATUS_SUCCESS) {
+		PRINTM(ERROR, "BT test_mode: Set Evt filter fail\n");
+		goto exit;
+	}
+
+	/** Enable Write Scan Command */
+	ret = bt_enable_write_scan(priv);
+	if (ret != BT_STATUS_SUCCESS) {
+		PRINTM(ERROR, "BT test_mode: Enable Write Scan fail\n");
+		goto exit;
+	}
+
+	/** Enable Device under test mode */
+	ret = bt_enable_device_under_testmode(priv);
+	if (ret != BT_STATUS_SUCCESS)
+		PRINTM(ERROR,
+		       "BT test_mode: Enable device under testmode fail\n");
+
+exit:
+	LEAVE();
+	return ret;
+}
+
+#define DISABLE_RESET  0x0
+#define ENABLE_OUTBAND_RESET 0x1
+#define ENABLE_INBAND_RESET  0x02
+#define DEFAULT_GPIO 0xff
+/**
+ *  @brief This function set GPIO pin
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_independent_reset(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	u8 mode, gpio;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_INDEPENDENT_RESET);
+	mode = btindrst & 0xff;
+	gpio = (btindrst & 0xff00) >> 8;
+	if (mode == ENABLE_OUTBAND_RESET) {
+		pcmd->data[0] = ENABLE_OUTBAND_RESET;
+		if (!gpio)
+			pcmd->data[1] = DEFAULT_GPIO;
+		else
+			pcmd->data[1] = gpio;
+	} else if (mode == ENABLE_INBAND_RESET) {
+		pcmd->data[0] = ENABLE_INBAND_RESET;
+		pcmd->data[1] = DEFAULT_GPIO;
+	} else if (mode == DISABLE_RESET) {
+		pcmd->data[0] = DISABLE_RESET;
+		pcmd->data[1] = DEFAULT_GPIO;
+	} else {
+		PRINTM(WARN, "Unsupport mode\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	PRINTM(CMD, "BT: independant reset mode=%d gpio=%d\n", mode, gpio);
+	pcmd->length = 2;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Independent reset : timeout!\n");
+		bt_cmd_timeout_func(priv, BT_CMD_INDEPENDENT_RESET);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sets ble deepsleep mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mode    TRUE/FALSE
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_ble_deepsleep(bt_private *priv, int mode)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_BLE_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_BLE_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP);
+	pcmd->length = 1;
+	pcmd->deepsleep = mode;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_BLE_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode,
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_BLE_DEEP_SLEEP);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function gets FW version
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_get_fw_version(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION);
+	pcmd->length = 0x01;
+	pcmd->cmd_type = 0x00;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, 4);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Get FW version: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_GET_FW_VERSION);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sets mac address
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_mac_address(bt_private *priv, u8 *mac)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	int i = 0;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR);
+	pcmd->length = 8;
+	pcmd->cmd_type = MRVL_VENDOR_PKT;
+	pcmd->cmd_len = 6;
+	for (i = 0; i < 6; i++)
+		pcmd->data[i] = mac[5 - i];
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_HCI_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set mac addr " MACSTR " (0x%x)\n", MAC2STR(mac),
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set mac addr: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_CONFIG_MAC_ADDR);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate EXT data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_commands(bt_private *priv, u8 *cmd_data, u32 cmd_len)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	u16 ogf = 0;
+
+	ENTER();
+	PRINTM(CMD, "BT: init cmds: len=%d\n", cmd_len);
+	if (cmd_len > BT_CMD_DATA_LEN) {
+		PRINTM(WARN, "cfg_data_len is too long exceed %d.\n",
+		       BT_CMD_DATA_LEN);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	memcpy(skb->data, cmd_data, cmd_len);
+	pcmd = (BT_CMD *)skb->data;
+
+	ogf = hci_opcode_ogf(pcmd->ocf_ogf);
+	if (ogf == VENDOR_OGF)
+		bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	else
+		bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "init_cmds", skb->data, skb->len);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load init cmds: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	int i = 0;
+	/* u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF 0x01
+	   0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE 0xC0 0xC6 0x2D 0x00 0x00 0x00
+	   0x00 0x00 0x00 0x00 0xF0}; */
+
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA);
+	pcmd->length = 0x20;
+	pcmd->data[0] = 0x00;
+	pcmd->data[1] = 0x00;
+	pcmd->data[2] = 0x00;
+	pcmd->data[3] = 0x1C;
+	/* swip cal-data byte */
+	for (i = 4; i < 32; i++)
+		pcmd->data[i] = *(config_data + ((i / 4) * 8 - 1 - i));
+	if (mac != NULL) {
+		pcmd->data[2] = 0x01;	/* skip checksum */
+		for (i = 24; i < 30; i++)
+			pcmd->data[i] = mac[29 - i];
+	}
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "calirate data: ", pcmd->data, 32);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load calibrate data: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate EXT data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+
+	ENTER();
+
+	if (cfg_data_len > BT_CMD_DATA_LEN) {
+		PRINTM(WARN, "cfg_data_len is too long exceed %d.\n",
+		       BT_CMD_DATA_LEN);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA_EXT);
+	pcmd->length = cfg_data_len;
+
+	memcpy(pcmd->data, config_data, cfg_data_len);
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "calirate ext data", pcmd->data, pcmd->length);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load calibrate ext data: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function writes value to CSU registers
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param type    reg type
+ *  @param offset  register address
+ *  @param value   register value to write
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CSU_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CSU_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG);
+	pcmd->length = 7;
+	pcmd->type = type;
+	pcmd->offset[0] = (offset & 0x000000ff);
+	pcmd->offset[1] = (offset & 0x0000ff00) >> 8;
+	pcmd->offset[2] = (offset & 0x00ff0000) >> 16;
+	pcmd->offset[3] = (offset & 0xff000000) >> 24;
+	pcmd->value[0] = (value & 0x00ff);
+	pcmd->value[1] = (value & 0xff00) >> 8;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_CSU_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n",
+	       type, offset, value);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Set CSU reg timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_CSU_WRITE_REG);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function used to restore tx_queue
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        N/A
+ */
+void
+bt_restore_tx_queue(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	while (!skb_queue_empty(&priv->adapter->pending_queue)) {
+		skb = skb_dequeue(&priv->adapter->pending_queue);
+		if (skb)
+			bt_queue_frame(priv, skb);
+	}
+	wake_up_interruptible(&priv->MainThread.waitQ);
+}
+
+/**
+ *  @brief This function used to send command to firmware
+ *
+ *  Command format:
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     OCF OGF     | Length |                Data               |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     2-byte      | 1-byte |               4-byte              |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_prepare_command(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (priv->bt_dev.hscfgcmd) {
+		priv->bt_dev.hscfgcmd = 0;
+		ret = bt_send_hscfg_cmd(priv);
+	}
+	if (priv->bt_dev.pscmd) {
+		priv->bt_dev.pscmd = 0;
+		ret = bt_enable_ps(priv);
+	}
+	if (priv->bt_dev.sdio_pull_ctrl) {
+		priv->bt_dev.sdio_pull_ctrl = 0;
+		ret = bt_send_sdio_pull_ctrl_cmd(priv);
+	}
+	if (priv->bt_dev.hscmd) {
+		priv->bt_dev.hscmd = 0;
+		if (priv->bt_dev.hsmode)
+			ret = bt_enable_hs(priv, FALSE);
+		else {
+			ret = sbi_wakeup_firmware(priv);
+			priv->adapter->hs_state = HS_DEACTIVATED;
+		}
+	}
+	if (priv->bt_dev.test_mode) {
+		priv->bt_dev.test_mode = 0;
+		ret = bt_enable_test_mode(priv);
+	}
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function processes a single packet
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to skb which includes TX packet
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+send_single_packet(bt_private *priv, struct sk_buff *skb)
+{
+	int ret;
+	struct sk_buff *new_skb = NULL;
+	ENTER();
+	if (!skb || !skb->data) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) {
+		PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len,
+		       BT_UPLD_SIZE);
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	if (skb_headroom(skb) < BT_HEADER_LEN) {
+		new_skb = skb_realloc_headroom(skb, BT_HEADER_LEN);
+		if (!new_skb) {
+			PRINTM(ERROR, "TX error: realloc_headroom failed %d\n",
+			       BT_HEADER_LEN);
+			kfree_skb(skb);
+			LEAVE();
+			return BT_STATUS_FAILURE;
+		} else {
+			if (new_skb != skb)
+				dev_kfree_skb_any(skb);
+			skb = new_skb;
+		}
+	}
+	/* This is SDIO/PCIE specific header length: byte[3][2][1], * type:
+	   byte[0] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor)
+	 */
+	skb_push(skb, BT_HEADER_LEN);
+	skb->data[0] = (skb->len & 0x0000ff);
+	skb->data[1] = (skb->len & 0x00ff00) >> 8;
+	skb->data[2] = (skb->len & 0xff0000) >> 16;
+	skb->data[3] = bt_cb(skb)->pkt_type;
+	if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT)
+		PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n",
+		       __le16_to_cpu(*((u16 *) & skb->data[4])), skb->len);
+	ret = sbi_host_to_card(priv, skb->data, skb->len);
+	if (ret == BT_STATUS_FAILURE)
+		((struct m_dev *)skb->dev)->stat.err_tx++;
+	else
+		((struct m_dev *)skb->dev)->stat.byte_tx += skb->len;
+	if (ret != BT_STATUS_PENDING)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+#ifdef CONFIG_OF
+/**
+ *  @brief This function read the initial parameter from device tress
+ *
+ *
+ *  @return         N/A
+ */
+static void
+bt_init_from_dev_tree(void)
+{
+	struct device_node *dt_node = NULL;
+	struct property *prop;
+	u32 data;
+	const char *string_data;
+
+	ENTER();
+
+	if (!dts_enable) {
+		PRINTM(CMD, "DTS is disabled!");
+		return;
+	}
+
+	dt_node = of_find_node_by_name(NULL, "sd8xxx-bt");
+	if (!dt_node) {
+		LEAVE();
+		return;
+	}
+	for_each_property_of_node(dt_node, prop) {
+#ifdef DEBUG_LEVEL1
+		if (!strncmp(prop->name, "mbt_drvdbg", strlen("mbt_drvdbg"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				PRINTM(CMD, "mbt_drvdbg=0x%x\n", data);
+				mbt_drvdbg = data;
+			}
+		}
+#endif
+		else if (!strncmp(prop->name, "init_cmds", strlen("init_cmds"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				init_cmds = (char *)string_data;
+				PRINTM(CMD, "init_cmds=%s\n", init_cmds);
+			}
+		} else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				init_cfg = (char *)string_data;
+				PRINTM(CMD, "init_cfg=%s\n", init_cfg);
+			}
+		} else if (!strncmp
+			   (prop->name, "cal_cfg_ext", strlen("cal_cfg_ext"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				cal_cfg_ext = (char *)string_data;
+				PRINTM(CMD, "cal_cfg_ext=%s\n", cal_cfg_ext);
+			}
+		} else if (!strncmp(prop->name, "cal_cfg", strlen("cal_cfg"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				cal_cfg = (char *)string_data;
+				PRINTM(CMD, "cal_cfg=%s\n", cal_cfg);
+			}
+		} else if (!strncmp(prop->name, "bt_mac", strlen("bt_mac"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				bt_mac = (char *)string_data;
+				PRINTM(CMD, "bt_mac=%s\n", bt_mac);
+			}
+		} else if (!strncmp(prop->name, "btindrst", strlen("btindrst"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				btindrst = data;
+				PRINTM(CMD, "btindrst=%d\n", btindrst);
+			}
+		} else if (!strncmp(prop->name, "btpmic", strlen("btpmic"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				btpmic = data;
+				PRINTM(CMD, "btpmic=%d\n", btpmic);
+			}
+		}
+	}
+	LEAVE();
+	return;
+}
+#endif
+
+#ifdef BLE_WAKEUP
+/**
+ *  @brief This function send getting whitelist cmd to FW
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_get_whitelist_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_WHITELIST);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(MSG, "Queue get whitelist Command(0x%x):%d\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]);
+
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Get BLE_GET_WHITELIST: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_GET_WHITELIST);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function send  whitelist cmd to FW
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param is_shutdown  indicate shutdown mode
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_send_whitelist_cmd(bt_private *priv, bool is_shutdown)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	u8 count = 0, i = 0;
+	BT_CMD *pcmd;
+	ENTER();
+
+	count = priv->white_list[0];
+	for (i = 0; (i < count) && (i < 10); i++) {
+		skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+		if (skb == NULL) {
+			PRINTM(WARN, "No free skb\n");
+			ret = BT_STATUS_FAILURE;
+			goto exit;
+		}
+		pcmd = (BT_CMD *)skb->data;
+		pcmd->ocf_ogf =
+			__cpu_to_le16((HCI_BLE_GRP_BLE_CMDS << 10) |
+				      HCI_BLE_ADD_DEV_TO_WHITELIST_OCF);
+		pcmd->length = 7;
+		bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+		pcmd->data[0] = 0;
+		memcpy(&pcmd->data[1], &priv->white_list[1 + i * 6], 6);
+		skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+		skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+		skb_queue_head(&priv->adapter->tx_queue, skb);
+		PRINTM(MSG, "Queue send whitelist Command(0x%x):%d\n",
+		       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]);
+
+		priv->bt_dev.sendcmdflag = TRUE;
+		priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+		priv->adapter->cmd_complete = FALSE;
+		priv->adapter->wait_event_timeout = is_shutdown;
+		wake_up_interruptible(&priv->MainThread.waitQ);
+		if (is_shutdown) {
+			if (!os_wait_timeout
+			    (priv->adapter->cmd_wait_q,
+			     priv->adapter->cmd_complete,
+			     WAIT_UNTIL_CMD_RESP)) {
+				ret = BT_STATUS_FAILURE;
+				priv->adapter->wait_event_timeout = FALSE;
+				PRINTM(ERROR,
+				       "BT: Get BLE_GET_WHITELIST: timeout:\n");
+				bt_cmd_timeout_func(priv,
+						    HCI_BLE_ADD_DEV_TO_WHITELIST_OCF);
+			}
+		} else {
+			if (!os_wait_interruptible_timeout
+			    (priv->adapter->cmd_wait_q,
+			     priv->adapter->cmd_complete,
+			     WAIT_UNTIL_CMD_RESP)) {
+				ret = BT_STATUS_FAILURE;
+				PRINTM(ERROR,
+				       "BT: Get BLE_GET_WHITELIST: timeout:\n");
+				bt_cmd_timeout_func(priv,
+						    HCI_BLE_ADD_DEV_TO_WHITELIST_OCF);
+			}
+		}
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+#endif
+
+/**
+ *  @brief This function initializes the adapter structure
+ *  and set default value to the member of adapter.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    N/A
+ */
+static void
+bt_init_adapter(bt_private *priv)
+{
+	ENTER();
+#ifdef CONFIG_OF
+	bt_init_from_dev_tree();
+#endif
+	skb_queue_head_init(&priv->adapter->tx_queue);
+	skb_queue_head_init(&priv->adapter->pending_queue);
+	priv->adapter->tx_lock = FALSE;
+	priv->adapter->ps_state = PS_AWAKE;
+	priv->adapter->suspend_fail = FALSE;
+	priv->adapter->is_suspended = FALSE;
+	priv->adapter->hs_skip = 0;
+	priv->adapter->num_cmd_timeout = 0;
+	priv->adapter->fwdump_fname = NULL;
+	init_waitqueue_head(&priv->adapter->cmd_wait_q);
+	LEAVE();
+}
+
+/**
+ *  @brief This function initializes firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_fw(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (fw == 0) {
+		sbi_enable_host_int(priv);
+		goto done;
+	}
+	sbi_disable_host_int(priv);
+	if ((priv->card_type == CARD_TYPE_SD8787) ||
+	    (priv->card_type == CARD_TYPE_SD8777))
+		priv->fw_crc_check = fw_crc_check;
+	if (sbi_download_fw(priv)) {
+		PRINTM(ERROR, " FW failed to be download!\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+done:
+	LEAVE();
+	return ret;
+}
+
+#define FW_POLL_TRIES 100
+#define SD8897_FW_RESET_REG  0x0E8
+#define SD8887_FW_RESET_REG  0x0B6
+#define SD8977_SD8978_SD8997_FW_RESET_REG  0x0EE
+#define SD8887_SD8897_FW_RESET_VAL  1
+#define SD8977_SD8978_SD8997_FW_RESET_VAL  0x99
+
+/**
+ *  @brief This function reload firmware
+ *
+ *  @param priv   A pointer to bt_private
+ *  @param mode   FW reload mode
+ *
+ *  @return       0--success, otherwise failure
+ */
+static int
+bt_reload_fw(bt_private *priv, int mode)
+{
+	int ret = 0, tries = 0;
+	u8 value = 1;
+	u32 reset_reg = 0;
+	u8 reset_val = 0;
+
+	ENTER();
+	if ((mode != FW_RELOAD_SDIO_INBAND_RESET) &&
+	    (mode != FW_RELOAD_NO_EMULATION)) {
+		PRINTM(ERROR, "Invalid fw reload mode=%d\n", mode);
+		return -EFAULT;
+	}
+
+    /** flush pending tx_queue */
+	skb_queue_purge(&priv->adapter->tx_queue);
+	if (mode == FW_RELOAD_SDIO_INBAND_RESET) {
+		if (priv->card_type == CARD_TYPE_SD8887) {
+			reset_reg = SD8887_FW_RESET_REG;
+			reset_val = SD8887_SD8897_FW_RESET_VAL;
+		} else if (priv->card_type == CARD_TYPE_SD8897) {
+			reset_reg = SD8897_FW_RESET_REG;
+			reset_val = SD8887_SD8897_FW_RESET_VAL;
+		} else if ((priv->card_type == CARD_TYPE_SD8977) ||
+			   (priv->card_type == CARD_TYPE_SD8997) ||
+			   (priv->card_type == CARD_TYPE_SD8978) ||
+			   (priv->card_type == CARD_TYPE_SD8987)) {
+			reset_reg = SD8977_SD8978_SD8997_FW_RESET_REG;
+			reset_val = SD8977_SD8978_SD8997_FW_RESET_VAL;
+		}
+		sbi_disable_host_int(priv);
+	    /** Wake up firmware firstly */
+		sbi_wakeup_firmware(priv);
+
+	/** wait SOC fully wake up */
+		for (tries = 0; tries < FW_POLL_TRIES; ++tries) {
+			ret = sd_write_reg(priv, reset_reg, 0xba);
+			if (!ret) {
+				ret = sd_read_reg(priv, reset_reg, &value);
+				if (!ret && (value == 0xba)) {
+					PRINTM(MSG, "Fw wake up\n");
+					break;
+				}
+			}
+			udelay(1000);
+		}
+
+		ret = sd_write_reg(priv, reset_reg, reset_val);
+		if (ret) {
+			PRINTM(ERROR, "Failed to write register.\n");
+			goto done;
+		}
+
+	    /** Poll register around 1 ms */
+		for (; tries < FW_POLL_TRIES; ++tries) {
+			ret = sd_read_reg(priv, reset_reg, &value);
+			if (ret) {
+				PRINTM(ERROR, "Failed to read register.\n");
+				goto done;
+			}
+			if (value == 0)
+			    /** FW is ready */
+				break;
+			udelay(1000);
+		}
+		if (value) {
+			PRINTM(ERROR,
+			       "Failed to poll FW reset register %X=0x%x\n",
+			       reset_reg, value);
+			ret = -EFAULT;
+			goto done;
+		}
+	}
+
+	sbi_enable_host_int(priv);
+	/** reload FW */
+	ret = bt_init_fw(priv);
+	if (ret) {
+		PRINTM(ERROR, "Re download firmware failed.\n");
+		ret = -EFAULT;
+	}
+	LEAVE();
+	return ret;
+done:
+	sbi_enable_host_int(priv);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function request to reload firmware
+ *
+ *  @param priv   A pointer to bt_private
+ *  @param mode   fw reload mode.
+ *
+ *  @return         N/A
+ */
+void
+bt_request_fw_reload(bt_private *priv, int mode)
+{
+	ENTER();
+	if (mode == FW_RELOAD_WITH_EMULATION) {
+		bt_fw_reload = FW_RELOAD_WITH_EMULATION;
+		PRINTM(MSG, "BT: FW reload with re-emulation...\n");
+		LEAVE();
+		return;
+	}
+	/** Reload FW */
+	priv->fw_reload = TRUE;
+	if (bt_reload_fw(priv, mode)) {
+		PRINTM(ERROR, "FW reload fail\n");
+		goto done;
+	}
+	priv->fw_reload = FALSE;
+	/** Other operation here? */
+done:
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function frees the structure of adapter
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    N/A
+ */
+void
+bt_free_adapter(bt_private *priv)
+{
+	bt_adapter *adapter = priv->adapter;
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	kfree(adapter->tx_buffer);
+	kfree(adapter->hw_regs_buf);
+	/* Free allocated memory for fwdump filename */
+	if (adapter->fwdump_fname) {
+		kfree(adapter->fwdump_fname);
+		adapter->fwdump_fname = NULL;
+	}
+	/* Free the adapter object itself */
+	kfree(adapter);
+	priv->adapter = NULL;
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function handles the wrapper_dev ioctl
+ *
+ *  @param hev     A pointer to wrapper_dev structure
+ *  @cmd            ioctl cmd
+ *  @arg            argument
+ *  @return    -ENOIOCTLCMD
+ */
+static int
+mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, void *arg)
+{
+	bt_private *priv = NULL;
+	int ret = 0;
+#ifdef BLE_WAKEUP
+	u16 len;
+#endif
+
+	ENTER();
+
+	if (!m_dev || !m_dev->driver_data) {
+		PRINTM(ERROR, "Ioctl for unknown device (m_dev=NULL)\n");
+		ret = -ENODEV;
+		goto done;
+	}
+	priv = (bt_private *)m_dev->driver_data;
+	if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+		PRINTM(ERROR, "HCI_RUNNING not set, flag=0x%lx\n",
+		       m_dev->flags);
+		ret = -EBUSY;
+		goto done;
+	}
+	PRINTM(INFO, "IOCTL: cmd=%d\n", cmd);
+	switch (cmd) {
+#ifdef BLE_WAKEUP
+	case MBTCHAR_IOCTL_BLE_WAKEUP_PARAM:
+		PRINTM(MSG, "BT: Set ble wakeup parameters\n");
+		if (copy_from_user(&len, arg, sizeof(u16))) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Fail to copy ble wakeup params length\n");
+			ret = -EFAULT;
+			goto done;
+		}
+		/** Convert little endian length */
+		len = __le16_to_cpu(len);
+		if (len < 2) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Invalid ble wakeup params len %d\n",
+			       len);
+			ret = -EFAULT;
+			goto done;
+		}
+		if ((len + sizeof(u16)) > priv->ble_wakeup_buf_size) {
+			if (priv->ble_wakeup_buf) {
+				kfree(priv->ble_wakeup_buf);
+				priv->ble_wakeup_buf = NULL;
+				priv->ble_wakeup_buf_size = 0;
+			}
+			priv->ble_wakeup_buf =
+				kzalloc(len + sizeof(u16), GFP_KERNEL);
+			if (!priv->ble_wakeup_buf) {
+				PRINTM(ERROR, "BT_IOCTL: Fail to alloc buffer\t"
+				       "for ble wakeup parameters\n");
+				ret = -ENOMEM;
+				goto done;
+			}
+			priv->ble_wakeup_buf_size = len + sizeof(u16);
+		}
+		if (copy_from_user
+		    (priv->ble_wakeup_buf, arg, len + sizeof(u16))) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Fail to copy ble wakeup params\n");
+			ret = -EFAULT;
+			goto done;
+		}
+		DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_PARAM:", priv->ble_wakeup_buf,
+			    len + sizeof(u16));
+		break;
+	case MBTCHAR_IOCTL_BLE_GET_WHITELIST:
+		bt_get_whitelist_cmd(priv);
+		DBG_HEXDUMP(DAT_D, "white_list:", priv->white_list,
+			    sizeof(priv->white_list));
+		break;
+#endif
+	default:
+		break;
+	}
+
+done:
+#ifdef BLE_WAKEUP
+	if (ret && priv->ble_wakeup_buf) {
+		kfree(priv->ble_wakeup_buf);
+		priv->ble_wakeup_buf = NULL;
+		priv->ble_wakeup_buf_size = 0;
+	}
+#endif
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function handles wrapper device destruct
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    N/A
+ */
+static void
+mdev_destruct(struct m_dev *m_dev)
+{
+	ENTER();
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function handles the wrapper device transmit
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    BT_STATUS_SUCCESS or other error no.
+ */
+static int
+mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb)
+{
+	bt_private *priv = NULL;
+
+	ENTER();
+	if (!m_dev || !m_dev->driver_data) {
+		PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n");
+		LEAVE();
+		return -ENODEV;
+	}
+	priv = (bt_private *)m_dev->driver_data;
+	if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+		PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n",
+		       m_dev->flags);
+		LEAVE();
+		return -EBUSY;
+	}
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		m_dev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		m_dev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		m_dev->stat.sco_tx++;
+		break;
+	}
+
+	if (m_dev->dev_type == DEBUG_TYPE) {
+		/* remember the ogf_ocf */
+		priv->debug_device_pending = 1;
+		priv->debug_ocf_ogf[0] = skb->data[0];
+		priv->debug_ocf_ogf[1] = skb->data[1];
+		PRINTM(CMD, "debug_ocf_ogf[0]=0x%x debug_ocf_ogf[1]=0x%x\n",
+		       priv->debug_ocf_ogf[0], priv->debug_ocf_ogf[1]);
+	}
+
+	if (priv->adapter->tx_lock == TRUE)
+		skb_queue_tail(&priv->adapter->pending_queue, skb);
+	else
+		bt_queue_frame(priv, skb);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function flushes the transmit queue
+ *
+ *  @param m_dev     A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+mdev_flush(struct m_dev *m_dev)
+{
+	bt_private *priv = (bt_private *)m_dev->driver_data;
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	skb_queue_purge(&priv->adapter->pending_queue);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function closes the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+mdev_close(struct m_dev *m_dev)
+{
+
+	ENTER();
+	mdev_req_lock(m_dev);
+	if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) {
+		mdev_req_unlock(m_dev);
+		LEAVE();
+		return 0;
+	}
+
+	if (m_dev->flush)
+		m_dev->flush(m_dev);
+	/* wait up pending read and unregister char dev */
+	wake_up_interruptible(&m_dev->req_wait_q);
+	/* Drop queues */
+	skb_queue_purge(&m_dev->rx_q);
+	if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) {
+		mdev_req_unlock(m_dev);
+		LEAVE();
+		return 0;
+	}
+	module_put(THIS_MODULE);
+	m_dev->flags = 0;
+	mdev_req_unlock(m_dev);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function opens the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+static int
+mdev_open(struct m_dev *m_dev)
+{
+	ENTER();
+
+	if (try_module_get(THIS_MODULE) == 0)
+		return BT_STATUS_FAILURE;
+
+	set_bit(HCI_RUNNING, &m_dev->flags);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function queries the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @param arg     arguement
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+void
+mdev_query(struct m_dev *m_dev, void *arg)
+{
+	struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer;
+
+	ENTER();
+	if (copy_to_user(arg, &mbt_dev->type, sizeof(mbt_dev->type)))
+		PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n");
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function initializes the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+void
+init_m_dev(struct m_dev *m_dev)
+{
+	m_dev->dev_pointer = NULL;
+	m_dev->driver_data = NULL;
+	m_dev->dev_type = 0;
+	m_dev->spec_type = 0;
+	skb_queue_head_init(&m_dev->rx_q);
+	init_waitqueue_head(&m_dev->req_wait_q);
+	init_waitqueue_head(&m_dev->rx_wait_q);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+	init_MUTEX(&m_dev->req_lock);
+#else
+	sema_init(&m_dev->req_lock, 1);
+#endif
+	spin_lock_init(&m_dev->rxlock);
+	memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats));
+	m_dev->open = mdev_open;
+	m_dev->close = mdev_close;
+	m_dev->flush = mdev_flush;
+	m_dev->send = mdev_send_frame;
+	m_dev->destruct = mdev_destruct;
+	m_dev->ioctl = mdev_ioctl;
+	m_dev->query = mdev_query;
+	m_dev->owner = THIS_MODULE;
+
+}
+
+/**
+ *  @brief This function handles the major job in bluetooth driver.
+ *  it handles the event generated by firmware, rx data received
+ *  from firmware and tx data sent from kernel.
+ *
+ *  @param data    A pointer to bt_thread structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+static int
+bt_service_main_thread(void *data)
+{
+	bt_thread *thread = data;
+	bt_private *priv = thread->priv;
+	bt_adapter *adapter = priv->adapter;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
+	wait_queue_t wait;
+#else
+	wait_queue_entry_t wait;
+#endif
+	struct sk_buff *skb;
+	ENTER();
+	bt_activate_thread(thread);
+	init_waitqueue_entry(&wait, current);
+	current->flags |= PF_NOFREEZE;
+
+	for (;;) {
+		add_wait_queue(&thread->waitQ, &wait);
+		OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE);
+		if (priv->adapter->WakeupTries ||
+		    ((!priv->adapter->IntCounter) &&
+		     (!priv->bt_dev.tx_dnld_rdy ||
+		      skb_queue_empty(&priv->adapter->tx_queue))
+		    )) {
+			PRINTM(INFO, "Main: Thread sleeping...\n");
+			schedule();
+		}
+		OS_SET_THREAD_STATE(TASK_RUNNING);
+		remove_wait_queue(&thread->waitQ, &wait);
+		if (kthread_should_stop() || adapter->SurpriseRemoved) {
+			PRINTM(INFO, "main-thread: break from main thread: "
+			       "SurpriseRemoved=0x%x\n",
+			       adapter->SurpriseRemoved);
+			break;
+		}
+
+		PRINTM(INFO, "Main: Thread waking up...\n");
+
+		if (priv->adapter->IntCounter) {
+			OS_INT_DISABLE;
+			adapter->IntCounter = 0;
+			OS_INT_RESTORE;
+			sbi_get_int_status(priv);
+		} else if ((priv->adapter->ps_state == PS_SLEEP) &&
+			   (!skb_queue_empty(&priv->adapter->tx_queue)
+			   )) {
+			priv->adapter->WakeupTries++;
+			sbi_wakeup_firmware(priv);
+			continue;
+		}
+		if (priv->adapter->ps_state == PS_SLEEP)
+			continue;
+		if (priv->bt_dev.tx_dnld_rdy == TRUE) {
+			if (!skb_queue_empty(&priv->adapter->tx_queue)) {
+				skb = skb_dequeue(&priv->adapter->tx_queue);
+				if (skb)
+					send_single_packet(priv, skb);
+			}
+		}
+	}
+	bt_deactivate_thread(thread);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handles the interrupt. it will change PS
+ *  state if applicable. it will wake up main_thread to handle
+ *  the interrupt event as well.
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @return        N/A
+ */
+void
+bt_interrupt(struct m_dev *m_dev)
+{
+	bt_private *priv = (bt_private *)m_dev->driver_data;
+	ENTER();
+	if (!priv || !priv->adapter) {
+		LEAVE();
+		return;
+	}
+	PRINTM(INTR, "*\n");
+	priv->adapter->ps_state = PS_AWAKE;
+	if (priv->adapter->hs_state == HS_ACTIVATED) {
+		PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name);
+		priv->adapter->hs_state = HS_DEACTIVATED;
+	}
+	priv->adapter->WakeupTries = 0;
+	priv->adapter->IntCounter++;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	LEAVE();
+}
+
+/**
+ * @brief  Dynamic release of char dev
+ *
+ * @param kobj          A pointer to kobject structure
+ *
+ * @return                N/A
+ */
+static void
+char_dev_release_dynamic(struct kobject *kobj)
+{
+	struct char_dev *cdev = container_of(kobj, struct char_dev, kobj);
+	ENTER();
+	PRINTM(INFO, "free char_dev\n");
+	kfree(cdev);
+	LEAVE();
+}
+
+static struct kobj_type ktype_char_dev_dynamic = {
+	.release = char_dev_release_dynamic,
+};
+
+/**
+ * @brief  Allocation of char dev
+ *
+ * @param           	N/A
+ *
+ * @return              char_dev
+ */
+static struct char_dev *
+alloc_char_dev(void)
+{
+	struct char_dev *cdev;
+	ENTER();
+	cdev = kzalloc(sizeof(struct char_dev), GFP_KERNEL);
+	if (cdev) {
+		kobject_init(&cdev->kobj, &ktype_char_dev_dynamic);
+		PRINTM(INFO, "alloc char_dev\n");
+	}
+	return cdev;
+}
+
+/**
+ * @brief  Dynamic release of bt private
+ *
+ * @param kobj          A pointer to kobject structure
+ *
+ * @return                N/A
+ */
+static void
+bt_private_dynamic_release(struct kobject *kobj)
+{
+	bt_private *priv = container_of(kobj, bt_private, kobj);
+	ENTER();
+	PRINTM(INFO, "free bt priv\n");
+	kfree(priv);
+	LEAVE();
+}
+
+static struct kobj_type ktype_bt_private_dynamic = {
+	.release = bt_private_dynamic_release,
+};
+
+/**
+ * @brief  Allocation of bt private
+ *
+ * @param           	N/A
+ *
+ * @return              bt_private
+ */
+static bt_private *
+bt_alloc_priv(void)
+{
+	bt_private *priv;
+	ENTER();
+	priv = kzalloc(sizeof(bt_private), GFP_KERNEL);
+	if (priv) {
+		kobject_init(&priv->kobj, &ktype_bt_private_dynamic);
+		PRINTM(INFO, "alloc bt priv\n");
+	}
+	LEAVE();
+	return priv;
+}
+
+/**
+ * @brief  Get bt private structure
+ *
+ * @param priv          A pointer to bt_private structure
+ *
+ * @return              kobject structure
+ */
+struct kobject *
+bt_priv_get(bt_private *priv)
+{
+	PRINTM(INFO, "bt priv get object");
+	return kobject_get(&priv->kobj);
+}
+
+/**
+ * @brief  Get bt private structure
+ *
+ * @param priv          A pointer to bt_private structure
+ *
+ * @return              N/A
+ */
+void
+bt_priv_put(bt_private *priv)
+{
+	PRINTM(INFO, "bt priv put object");
+	kobject_put(&priv->kobj);
+}
+
+/**
+ *  @brief This function send init commands to firmware
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @return       BT_STATUS_SUCESS/BT_STATUS_FAILURE
+ */
+int
+bt_init_cmd(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+	if (ret < 0) {
+		PRINTM(FATAL, "Module cfg command send failed!\n");
+		goto done;
+	}
+	PRINTM(MSG, "BT: btindrst=0x%x\n", btindrst);
+	if (btindrst != -1) {
+		ret = bt_set_independent_reset(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "Independent reset failed!\n");
+			goto done;
+		}
+	}
+	if (btpmic
+	    && ((priv->card_type == CARD_TYPE_SD8997) ||
+		(priv->card_type == CARD_TYPE_SD8977) ||
+		(priv->card_type == CARD_TYPE_SD8978))
+		) {
+		if (BT_STATUS_SUCCESS != bt_pmic_configure(priv)) {
+			PRINTM(FATAL, "BT: PMIC Configure failed \n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	ret = bt_set_ble_deepsleep(priv, deep_sleep ? TRUE : FALSE);
+	if (ret < 0) {
+		PRINTM(FATAL, "%s BLE deepsleep failed!\n",
+		       deep_sleep ? "Enable" : "Disable");
+		goto done;
+	}
+	if (psmode) {
+		priv->bt_dev.psmode = TRUE;
+		priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME;
+		ret = bt_enable_ps(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "Enable PS mode failed!\n");
+			goto done;
+		}
+	}
+#if defined(SDIO_SUSPEND_RESUME)
+	priv->bt_dev.gpio_gap = DEF_GPIO_GAP;
+	ret = bt_send_hscfg_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "Send HSCFG failed!\n");
+		goto done;
+	}
+#endif
+	priv->bt_dev.sdio_pull_cfg = 0xffffffff;
+	priv->bt_dev.sdio_pull_ctrl = 0;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reinit firmware after redownload firmware
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @return       BT_STATUS_SUCESS/BT_STATUS_FAILURE
+ */
+int
+bt_reinit_fw(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	priv->adapter->tx_lock = FALSE;
+	priv->adapter->ps_state = PS_AWAKE;
+	priv->adapter->suspend_fail = FALSE;
+	priv->adapter->is_suspended = FALSE;
+	priv->adapter->hs_skip = 0;
+	priv->adapter->num_cmd_timeout = 0;
+
+	ret = bt_init_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "BT init command failed!\n");
+		goto done;
+	}
+	/* block all the packet from bluez */
+	if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext)
+		priv->adapter->tx_lock = TRUE;
+
+	if (init_cfg)
+		if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+			PRINTM(FATAL,
+			       "BT: Set user init data and param failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+
+	if (cal_cfg) {
+		if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+			PRINTM(FATAL, "BT: Set cal data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	if (bt_mac) {
+		PRINTM(INFO,
+		       "Set BT mac_addr from insmod parametre bt_mac = %s\n",
+		       bt_mac);
+		if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) {
+			PRINTM(FATAL,
+			       "BT: Fail to set mac address from insmod parametre\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	if (cal_cfg_ext) {
+		if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) {
+			PRINTM(FATAL, "BT: Set cal ext data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) {
+		priv->adapter->tx_lock = FALSE;
+		bt_restore_tx_queue(priv);
+	}
+
+	if (init_cmds) {
+		if (BT_STATUS_SUCCESS != bt_init_cmds(priv, init_cmds)) {
+			PRINTM(FATAL, "BT: Set user init commands failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	bt_get_fw_version(priv);
+	snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN,
+		 mbt_driver_version, fw_version);
+done:
+	return ret;
+}
+
+/**
+ *  @brief Module configuration and register device
+ *
+ *  @param priv      A Pointer to bt_private structure
+ *  @return      BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_conf_dpc(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct mbt_dev *mbt_dev = NULL;
+	struct debug_dev *debug_dev = NULL;
+	int i = 0;
+	struct char_dev *char_dev = NULL;
+	char dev_file[DEV_NAME_LEN + 5];
+	unsigned char dev_type = 0;
+
+	ENTER();
+
+	priv->bt_dev.tx_dnld_rdy = TRUE;
+	if (priv->fw_reload) {
+		bt_reinit_fw(priv);
+		LEAVE();
+		return ret;
+	}
+
+	if (drv_mode & DRV_MODE_BT) {
+		mbt_dev = alloc_mbt_dev();
+		if (!mbt_dev) {
+			PRINTM(FATAL, "Can not allocate mbt dev\n");
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		init_m_dev(&(priv->bt_dev.m_dev[BT_SEQ]));
+		priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE;
+		priv->bt_dev.m_dev[BT_SEQ].spec_type = IANYWHERE_SPEC;
+		priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)mbt_dev;
+		priv->bt_dev.m_dev[BT_SEQ].driver_data = priv;
+	}
+
+	dev_type = HCI_SDIO;
+
+	if (mbt_dev)
+		mbt_dev->type = dev_type;
+
+	ret = bt_init_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "BT init command failed!\n");
+		goto done;
+	}
+
+	if (mbt_dev && priv->bt_dev.devType == DEV_TYPE_AMP) {
+		mbt_dev->type |= HCI_BT_AMP;
+		priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_AMP_TYPE;
+	}
+	/** Process device tree init parameters before register hci device.
+	 *  Since uplayer device has not yet registered, no need to block tx queue.
+	 * */
+	if (init_cfg)
+		if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+			PRINTM(FATAL,
+			       "BT: Set user init data and param failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	if (cal_cfg) {
+		if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+			PRINTM(FATAL, "BT: Set cal data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	} else if (bt_mac) {
+		PRINTM(INFO,
+		       "Set BT mac_addr from insmod parametre bt_mac = %s\n",
+		       bt_mac);
+		if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) {
+			PRINTM(FATAL,
+			       "BT: Fail to set mac address from insmod parametre\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cal_cfg_ext) {
+		if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) {
+			PRINTM(FATAL, "BT: Set cal ext data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (init_cmds) {
+		if (BT_STATUS_SUCCESS != bt_init_cmds(priv, init_cmds)) {
+			PRINTM(FATAL, "BT: Set user init commands failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	/* Get FW version */
+	bt_get_fw_version(priv);
+	snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN,
+		 mbt_driver_version, fw_version);
+
+	if (mbt_dev) {
+		/** init mbt_dev */
+		mbt_dev->flags = 0;
+		mbt_dev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
+		mbt_dev->esco_type = (ESCO_HV1);
+		mbt_dev->link_mode = (HCI_LM_ACCEPT);
+
+		mbt_dev->idle_timeout = 0;
+		mbt_dev->sniff_max_interval = 800;
+		mbt_dev->sniff_min_interval = 80;
+		for (i = 0; i < 3; i++)
+			mbt_dev->reassembly[i] = NULL;
+		atomic_set(&mbt_dev->promisc, 0);
+
+		/** alloc char dev node */
+		char_dev = alloc_char_dev();
+		if (!char_dev) {
+			class_destroy(chardev_class);
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		char_dev->minor = MBTCHAR_MINOR_BASE + mbtchar_minor;
+		if (mbt_dev->type & HCI_BT_AMP)
+			char_dev->dev_type = BT_AMP_TYPE;
+		else
+			char_dev->dev_type = BT_TYPE;
+
+		if (bt_name)
+			snprintf(mbt_dev->name, sizeof(mbt_dev->name), "%s%d",
+				 bt_name, mbtchar_minor);
+		else
+			snprintf(mbt_dev->name, sizeof(mbt_dev->name),
+				 "mbtchar%d", mbtchar_minor);
+		snprintf(dev_file, sizeof(dev_file), "/dev/%s", mbt_dev->name);
+		mbtchar_minor++;
+		PRINTM(MSG, "BT: Create %s\n", dev_file);
+
+		/** register m_dev to BT char device */
+		priv->bt_dev.m_dev[BT_SEQ].index = char_dev->minor;
+		char_dev->m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+
+		/** create BT char device node */
+		register_char_dev(char_dev, chardev_class, MODULE_NAME,
+				  mbt_dev->name);
+
+		/** chmod & chown for BT char device */
+		mbtchar_chown(dev_file, AID_SYSTEM, AID_NET_BT_STACK);
+		mbtchar_chmod(dev_file, 0666);
+
+		/** create proc device */
+		snprintf(priv->bt_dev.m_dev[BT_SEQ].name,
+			 sizeof(priv->bt_dev.m_dev[BT_SEQ].name),
+			 mbt_dev->name);
+		bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ);
+	}
+
+	if ((debug_intf) && ((drv_mode & DRV_MODE_BT)
+	    )) {
+		/** alloc debug_dev */
+		debug_dev = alloc_debug_dev();
+		if (!debug_dev) {
+			PRINTM(FATAL, "Can not allocate debug dev\n");
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+
+		/** init m_dev */
+		init_m_dev(&(priv->bt_dev.m_dev[DEBUG_SEQ]));
+		priv->bt_dev.m_dev[DEBUG_SEQ].dev_type = DEBUG_TYPE;
+		priv->bt_dev.m_dev[DEBUG_SEQ].spec_type = GENERIC_SPEC;
+		priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = (void *)debug_dev;
+		priv->bt_dev.m_dev[DEBUG_SEQ].driver_data = priv;
+
+		/** create char device for Debug */
+		char_dev = alloc_char_dev();
+		if (!char_dev) {
+			class_destroy(chardev_class);
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		char_dev->minor = DEBUGCHAR_MINOR_BASE + debugchar_minor;
+		char_dev->dev_type = DEBUG_TYPE;
+		if (debug_name)
+			snprintf(debug_dev->name, sizeof(debug_dev->name),
+				 "%s%d", debug_name, debugchar_minor);
+		else
+			snprintf(debug_dev->name, sizeof(debug_dev->name),
+				 "mdebugchar%d", debugchar_minor);
+		snprintf(dev_file, sizeof(dev_file), "/dev/%s",
+			 debug_dev->name);
+		PRINTM(MSG, "BT: Create %s\n", dev_file);
+		debugchar_minor++;
+
+		/** register char dev */
+		priv->bt_dev.m_dev[DEBUG_SEQ].index = char_dev->minor;
+		char_dev->m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+		register_char_dev(char_dev, chardev_class, MODULE_NAME,
+				  debug_dev->name);
+
+		/** chmod for debug char device */
+		mbtchar_chmod(dev_file, 0666);
+
+		/** create proc device */
+		snprintf(priv->bt_dev.m_dev[DEBUG_SEQ].name,
+			 sizeof(priv->bt_dev.m_dev[DEBUG_SEQ].name),
+			 debug_dev->name);
+		bt_proc_init(priv, &(priv->bt_dev.m_dev[DEBUG_SEQ]), DEBUG_SEQ);
+	}
+
+done:
+	LEAVE();
+	return ret;
+err_kmalloc:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function adds the card. it will probe the
+ *  card, allocate the bt_priv and initialize the device.
+ *
+ *  @param card    A pointer to card
+ *  @return        A pointer to bt_private structure
+ */
+
+bt_private *
+bt_add_card(void *card)
+{
+	bt_private *priv = NULL;
+	int index = 0;
+
+	ENTER();
+
+	priv = bt_alloc_priv();
+	if (!priv) {
+		PRINTM(FATAL, "Can not allocate priv\n");
+		LEAVE();
+		return NULL;
+	}
+	/* Save the handle */
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == NULL)
+			break;
+	}
+	if (index < MAX_BT_ADAPTER) {
+		m_priv[index] = priv;
+	} else {
+		PRINTM(ERROR, "Exceeded maximum cards supported!\n");
+		goto err_kmalloc;
+	}
+	/* allocate buffer for bt_adapter */
+	priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL);
+	if (!priv->adapter) {
+		PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->tx_buffer =
+		kzalloc(MAX_TX_BUF_SIZE + DMA_ALIGNMENT, GFP_KERNEL);
+	if (!priv->adapter->tx_buffer) {
+		PRINTM(FATAL, "Allocate buffer for transmit\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->tx_buf =
+		(u8 *)ALIGN_ADDR(priv->adapter->tx_buffer, DMA_ALIGNMENT);
+	priv->adapter->hw_regs_buf =
+		kzalloc(SD_BLOCK_SIZE + DMA_ALIGNMENT, GFP_KERNEL);
+	if (!priv->adapter->hw_regs_buf) {
+		PRINTM(FATAL, "Allocate buffer for INT read buf failed!\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->hw_regs =
+		(u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, DMA_ALIGNMENT);
+	bt_init_adapter(priv);
+
+	PRINTM(INFO, "Starting kthread...\n");
+	priv->MainThread.priv = priv;
+	spin_lock_init(&priv->driver_lock);
+
+	bt_create_thread(bt_service_main_thread, &priv->MainThread,
+			 "bt_main_service");
+
+	/* wait for mainthread to up */
+	while (!priv->MainThread.pid)
+		os_sched_timeout(1);
+
+	sdio_update_card_type(priv, card);
+	/* Update driver version */
+	if (priv->card_type == CARD_TYPE_SD8787)
+		memcpy(mbt_driver_version, CARD_SD8787, strlen(CARD_SD8787));
+	else if (priv->card_type == CARD_TYPE_SD8777)
+		memcpy(mbt_driver_version, CARD_SD8777, strlen(CARD_SD8777));
+	else if (priv->card_type == CARD_TYPE_SD8887)
+		memcpy(mbt_driver_version, CARD_SD8887, strlen(CARD_SD8887));
+	else if (priv->card_type == CARD_TYPE_SD8897)
+		memcpy(mbt_driver_version, CARD_SD8897, strlen(CARD_SD8897));
+	else if (priv->card_type == CARD_TYPE_SD8797)
+		memcpy(mbt_driver_version, CARD_SD8797, strlen(CARD_SD8797));
+	else if (priv->card_type == CARD_TYPE_SD8977)
+		memcpy(mbt_driver_version, CARD_SD8977, strlen(CARD_SD8977));
+	else if (priv->card_type == CARD_TYPE_SD8978)
+		memcpy(mbt_driver_version, CARD_SD8978, strlen(CARD_SD8978));
+	else if (priv->card_type == CARD_TYPE_SD8997)
+		memcpy(mbt_driver_version, CARD_SD8997, strlen(CARD_SD8997));
+	else if (priv->card_type == CARD_TYPE_SD8987)
+		memcpy(mbt_driver_version, CARD_SD8987, strlen(CARD_SD8987));
+
+	if (BT_STATUS_SUCCESS != sdio_get_sdio_device(priv))
+		goto err_kmalloc;
+
+	/** user config file */
+	init_waitqueue_head(&priv->init_user_conf_wait_q);
+
+	priv->bt_dev.card = card;
+
+	((struct sdio_mmc_card *)card)->priv = priv;
+	priv->adapter->sd_ireg = 0;
+	/*
+	 * Register the device. Fillup the private data structure with
+	 * relevant information from the card and request for the required
+	 * IRQ.
+	 */
+	if (sbi_register_dev(priv) < 0) {
+		PRINTM(FATAL, "Failed to register bt device!\n");
+		goto err_registerdev;
+	}
+	if (bt_init_fw(priv)) {
+		PRINTM(FATAL, "BT Firmware Init Failed\n");
+		goto err_init_fw;
+	}
+	LEAVE();
+	return priv;
+
+err_init_fw:
+	clean_up_m_devs(priv);
+	bt_proc_remove(priv);
+	PRINTM(INFO, "Unregister device\n");
+	sbi_unregister_dev(priv);
+err_registerdev:
+	((struct sdio_mmc_card *)card)->priv = NULL;
+	/* Stop the thread servicing the interrupts */
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid)
+		os_sched_timeout(1);
+err_kmalloc:
+	if (priv->adapter)
+		bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return NULL;
+}
+
+/**
+ *  @brief This function send hardware remove event
+ *
+ *  @param priv    A pointer to bt_private
+ *  @return        N/A
+ */
+void
+bt_send_hw_remove_event(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+	ENTER();
+	if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		LEAVE();
+		return;
+	}
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+#define HCI_HARDWARE_ERROR_EVT  0x10
+#define HCI_HARDWARE_REMOVE     0x24
+	skb = bt_skb_alloc(3, GFP_ATOMIC);
+	skb->data[0] = HCI_HARDWARE_ERROR_EVT;
+	skb->data[1] = 1;
+	skb->data[2] = HCI_HARDWARE_REMOVE;
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+	skb_put(skb, 3);
+	if (mbt_dev) {
+		skb->dev = (void *)mdev_bt;
+		PRINTM(MSG, "Send HW ERROR event\n");
+		if (!mdev_recv_frame(skb)) {
+#define RX_WAIT_TIMEOUT				300
+			mdev_bt->wait_rx_complete = TRUE;
+			mdev_bt->rx_complete_flag = FALSE;
+			if (os_wait_interruptible_timeout
+			    (mdev_bt->rx_wait_q, mdev_bt->rx_complete_flag,
+			     RX_WAIT_TIMEOUT))
+				PRINTM(MSG, "BT stack received the event\n");
+			mdev_bt->stat.byte_rx += 3;
+		}
+	}
+	LEAVE();
+	return;
+}
+
+#ifdef BLE_WAKEUP
+/**
+ *  @brief This function used to config BLE wakeup pattern
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param is_shutdown  indicate shutdown mode
+ *  @return        N/A
+ */
+int
+bt_config_ble_wakeup(bt_private *priv, bool is_shutdown)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sk_buff *skb = NULL;
+	u16 ocf = 0, left_len;
+	u8 len, more_cmd;
+	u8 *pos;
+
+	ENTER();
+
+	if (!priv->ble_wakeup_buf) {
+		PRINTM(ERROR, "BT: no ble wakeup parameters found\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	PRINTM(MSG, "Config ble wakeup pattern\n");
+
+	pos = priv->ble_wakeup_buf;
+	left_len = *(u16 *) pos;
+	left_len = __le16_to_cpu(left_len);
+	pos += sizeof(u16);
+
+	while (left_len >= 2) {
+		more_cmd = *pos;
+		len = *(pos + 1);
+		if (((len + 2) > left_len) ||
+		    (!more_cmd && ((len + 2) < left_len))) {
+			PRINTM(ERROR, "Invalid ble parameters\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		if (ocf == BT_CMD_ENABLE_WRITE_SCAN)
+			bt_send_whitelist_cmd(priv, is_shutdown);
+		skb = bt_skb_alloc(len, GFP_ATOMIC);
+		if (!skb) {
+			PRINTM(ERROR, "BT BLE WAKEUP: fail to alloc skb\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		memcpy(skb_put(skb, len), pos + 2, len);
+		bt_cb(skb)->pkt_type = *(u8 *)skb->data;
+		skb_pull(skb, 1);
+		DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_CMD:", skb->data, skb->len);
+		skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+		skb_queue_head(&priv->adapter->tx_queue, skb);
+		priv->bt_dev.sendcmdflag = TRUE;
+		priv->bt_dev.send_cmd_opcode = *(u16 *) skb->data;
+		ocf = hci_opcode_ocf(priv->bt_dev.send_cmd_opcode);
+		priv->adapter->cmd_complete = FALSE;
+		priv->adapter->wait_event_timeout = is_shutdown;
+
+		wake_up_interruptible(&priv->MainThread.waitQ);
+		if (is_shutdown) {
+			if (!os_wait_timeout
+			    (priv->adapter->cmd_wait_q,
+			     priv->adapter->cmd_complete,
+			     WAIT_UNTIL_CMD_RESP)) {
+				ret = BT_STATUS_FAILURE;
+				priv->adapter->wait_event_timeout = FALSE;
+				PRINTM(ERROR,
+				       "BT: Set  Set ble wakeup cmd 0x%x timeout:\n",
+				       priv->bt_dev.send_cmd_opcode);
+				bt_cmd_timeout_func(priv, ocf);
+				goto done;
+			}
+		} else {
+			if (!os_wait_interruptible_timeout
+			    (priv->adapter->cmd_wait_q,
+			     priv->adapter->cmd_complete,
+			     WAIT_UNTIL_CMD_RESP)) {
+				ret = BT_STATUS_FAILURE;
+				PRINTM(ERROR,
+				       "BT: Set  Set ble wakeup cmd 0x%x timeout:\n",
+				       priv->bt_dev.send_cmd_opcode);
+				bt_cmd_timeout_func(priv, ocf);
+				goto done;
+			}
+		}
+
+		pos += len + 2;
+		left_len -= len + 2;
+	}
+
+done:
+	if (ret != BT_STATUS_SUCCESS) {
+		if (priv->ble_wakeup_buf) {
+			kfree(priv->ble_wakeup_buf);
+			priv->ble_wakeup_buf = NULL;
+			priv->ble_wakeup_buf_size = 0;
+		}
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function send system suspend event
+ *
+ *  @param priv    A pointer to bt_private
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_send_system_event(bt_private *priv, u8 flag)
+{
+	struct sk_buff *skb = NULL;
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+
+	ENTER();
+
+	if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+
+	skb = bt_skb_alloc(4, GFP_ATOMIC);
+	if (!skb) {
+		PRINTM(ERROR, "Fail to allocate sys suspend event skb\n");
+		return BT_STATUS_FAILURE;
+	}
+	skb->data[0] = VENDOR_SPECIFIC_EVENT;
+	skb->data[1] = 2;
+	skb->data[2] = HCI_SYSTEM_SUSPEND_EVT;
+	if (flag)
+		skb->data[3] = HCI_SYSTEM_SUSPEND;
+	else
+		skb->data[3] = HCI_SYSTEM_RESUME;
+
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+	skb_put(skb, 4);
+	if (mbt_dev) {
+		skb->dev = (void *)mdev_bt;
+		PRINTM(MSG, "Send system %s event\n",
+		       flag ? "suspend" : "resume");
+		if (!mdev_recv_frame(skb)) {
+#define RX_WAIT_TIMEOUT                         300
+			mdev_bt->wait_rx_complete = TRUE;
+			mdev_bt->rx_complete_flag = FALSE;
+			if (os_wait_interruptible_timeout(mdev_bt->rx_wait_q,
+							  mdev_bt->
+							  rx_complete_flag,
+							  RX_WAIT_TIMEOUT))
+				PRINTM(MSG, "BT stack received the event\n");
+			mdev_bt->stat.byte_rx += 4;
+		}
+	}
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+#endif
+
+/**
+ *  @brief This function removes the card.
+ *
+ *  @param card    A pointer to card
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_remove_card(void *card)
+{
+	bt_private *priv = (bt_private *)card;
+	int index;
+	ENTER();
+	if (!priv) {
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv->adapter->SurpriseRemoved = TRUE;
+
+	bt_send_hw_remove_event(priv);
+#ifdef BLE_WAKEUP
+	if (priv->ble_wakeup_buf) {
+		kfree(priv->ble_wakeup_buf);
+		priv->ble_wakeup_buf = NULL;
+		priv->ble_wakeup_buf_size = 0;
+	}
+#endif
+	wake_up_interruptible(&priv->adapter->cmd_wait_q);
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid) {
+		os_sched_timeout(1);
+		wake_up_interruptible(&priv->MainThread.waitQ);
+	}
+
+	bt_proc_remove(priv);
+	PRINTM(INFO, "Unregister device\n");
+	sbi_unregister_dev(priv);
+	clean_up_m_devs(priv);
+	PRINTM(INFO, "Free Adapter\n");
+	bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function initializes module.
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_module(void)
+{
+	int ret = BT_STATUS_SUCCESS;
+	int index;
+	ENTER();
+	PRINTM(MSG, "BT: Loading driver\n");
+	/* Init the bt_private pointer array first */
+	for (index = 0; index < MAX_BT_ADAPTER; index++)
+		m_priv[index] = NULL;
+	bt_root_proc_init();
+
+	/** create char device class */
+	chardev_class = class_create(THIS_MODULE, MODULE_NAME);
+	if (IS_ERR(chardev_class)) {
+		PRINTM(ERROR, "Unable to allocate class\n");
+		bt_root_proc_remove();
+		ret = PTR_ERR(chardev_class);
+		goto done;
+	}
+
+	if (sbi_register() == NULL) {
+		bt_root_proc_remove();
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+done:
+	if (ret)
+		PRINTM(MSG, "BT: Driver loading failed\n");
+	else
+		PRINTM(MSG, "BT: Driver loaded successfully\n");
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function cleans module
+ *
+ *  @return        N/A
+ */
+static void
+bt_exit_module(void)
+{
+	bt_private *priv;
+	int index;
+	ENTER();
+	PRINTM(MSG, "BT: Unloading driver\n");
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		priv = m_priv[index];
+		if (!priv)
+			continue;
+		if (priv && !priv->adapter->SurpriseRemoved) {
+			if (BT_STATUS_SUCCESS == bt_send_reset_command(priv))
+				bt_send_module_cfg_cmd(priv,
+						       MODULE_SHUTDOWN_REQ);
+		}
+		sbi_disable_host_int(priv);
+
+	}
+
+	sbi_unregister();
+
+	bt_root_proc_remove();
+	class_destroy(chardev_class);
+	PRINTM(MSG, "BT: Driver unloaded\n");
+	LEAVE();
+}
+
+module_init(bt_init_module);
+module_exit(bt_exit_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+module_param(fw, int, 0);
+MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware");
+module_param(fw_crc_check, int, 0);
+MODULE_PARM_DESC(fw_crc_check,
+		 "1: Enable FW download CRC check (default); 0: Disable FW download CRC check");
+module_param(psmode, int, 0);
+MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode");
+module_param(deep_sleep, int, 0);
+MODULE_PARM_DESC(deep_sleep, "1: Enable deep sleep; 0: Disable deep sleep");
+#ifdef CONFIG_OF
+module_param(dts_enable, int, 0);
+MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS");
+#endif
+#ifdef	DEBUG_LEVEL1
+module_param(mbt_drvdbg, uint, 0);
+MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL");
+#endif
+#ifdef SDIO_SUSPEND_RESUME
+module_param(mbt_pm_keep_power, int, 0);
+MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power");
+#endif
+module_param(init_cfg, charp, 0);
+MODULE_PARM_DESC(init_cfg, "BT init config file name");
+module_param(cal_cfg, charp, 0);
+MODULE_PARM_DESC(cal_cfg, "BT calibrate file name");
+module_param(cal_cfg_ext, charp, 0);
+MODULE_PARM_DESC(cal_cfg_ext, "BT calibrate ext file name");
+module_param(bt_mac, charp, 0660);
+MODULE_PARM_DESC(bt_mac, "BT init mac address");
+module_param(init_cmds, charp, 0);
+MODULE_PARM_DESC(init_cmds, "BT init commands file name");
+module_param(drv_mode, int, 0);
+MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE;");
+module_param(bt_name, charp, 0);
+MODULE_PARM_DESC(bt_name, "BT interface name");
+module_param(debug_intf, int, 0);
+MODULE_PARM_DESC(debug_intf,
+		 "1: Enable debug interface; 0: Disable debug interface ");
+module_param(debug_name, charp, 0);
+MODULE_PARM_DESC(debug_name, "Debug interface name");
+module_param(bt_fw_reload, int, 0);
+MODULE_PARM_DESC(bt_fw_reload,
+		 "0: disable fw_reload; 1: enable fw reload feature");
+module_param(btindrst, int, 0);
+MODULE_PARM_DESC(btindrst,
+		 "Independent reset configuration; high byte:GPIO pin number;low byte:0x0:disable, 0x1:out-band reset, 0x2:in-band reset.");
+module_param(btpmic, int, 0);
+MODULE_PARM_DESC(btpmic,
+		 "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware (default)");
+module_param(bt_fw_serial, int, 0);
+MODULE_PARM_DESC(bt_fw_serial,
+		 "0: Support parallel download FW; 1: Support serial download FW");
diff --git a/bt_sd8987/bt_char/bt_proc.c b/bt_sd8987/bt_char/bt_proc.c
new file mode 100644
index 0000000..0264c94
--- /dev/null
+++ b/bt_sd8987/bt_char/bt_proc.c
@@ -0,0 +1,723 @@
+/** @file bt_proc.c
+  *
+  * @brief This file handle the functions for proc files
+  *
+  * Copyright (C) 2007-2019, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/proc_fs.h>
+
+#include "bt_drv.h"
+#include "bt_sdio.h"
+
+/** proc diretory root */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+#define PROC_DIR NULL
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+#define PROC_DIR (&proc_root)
+#else
+#define PROC_DIR proc_net
+#endif
+
+/** Proc mbt directory entry */
+static struct proc_dir_entry *proc_mbt;
+
+#define     CMD52_STR_LEN   50
+static char cmd52_string[CMD52_STR_LEN];
+
+/** proc data structure */
+struct proc_data {
+	/** Read length */
+	int rdlen;
+	/** Read buffer */
+	char *rdbuf;
+	/** Write length */
+	int wrlen;
+	/** Maximum write length */
+	int maxwrlen;
+	/** Write buffer */
+	char *wrbuf;
+	/** Private structure */
+	struct _bt_private *pbt;
+	void (*on_close) (struct inode *, struct file *);
+};
+
+/** Default file permission */
+#define DEFAULT_FILE_PERM  0644
+
+/** Bluetooth device offset */
+#define OFFSET_BT_DEV		0x01
+/** Bluetooth adapter offset */
+#define OFFSET_BT_ADAPTER	0x02
+/** Show integer */
+#define SHOW_INT		0x10
+/** Show hex */
+#define SHOW_HEX		0x20
+/** Show string */
+#define SHOW_STRING		0x40
+
+/** Device size */
+#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n)
+/** Device address */
+#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n)
+
+/** Adapter size */
+#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n)
+/** Adapter address */
+#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n)
+
+static struct item_data config_items[] = {
+#ifdef	DEBUG_LEVEL1
+	{"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX}
+	,
+#endif
+	{"idle_timeout", item_dev_size(idle_timeout), 0,
+	 item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap),
+	 OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0,
+	 item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0,
+	 item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+
+};
+
+static struct item_data status_items[] = {
+	{"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver),
+	 OFFSET_BT_ADAPTER | SHOW_STRING},
+	{"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0,
+	 item_dev_addr(tx_dnld_rdy),
+	 OFFSET_BT_DEV | SHOW_INT},
+	{"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"hs_state", item_adapter_size(hs_state), 0,
+	 item_adapter_addr(hs_state),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"ps_state", item_adapter_size(ps_state), 0,
+	 item_adapter_addr(ps_state),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"WakeupTries", item_adapter_size(WakeupTries), 0,
+	 item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT},
+	{"irq_recv", item_adapter_size(irq_recv), 0,
+	 item_adapter_addr(irq_recv),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"irq_done", item_adapter_size(irq_done), 0,
+	 item_adapter_addr(irq_done),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"skb_pending", item_adapter_size(skb_pending), 0,
+	 item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT},
+};
+
+static struct item_data debug_items[] = {
+	{"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING},
+};
+
+/**
+ *  @brief convert string to number
+ *
+ *  @param s	pointer to numbered string
+ *  @return	converted number from string s
+ */
+int
+string_to_number(char *s)
+{
+	int r = 0;
+	int base = 0;
+	int pn = 1;
+
+	if (strncmp(s, "-", 1) == 0) {
+		pn = -1;
+		s++;
+	}
+	if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) {
+		base = 16;
+		s += 2;
+	} else
+		base = 10;
+
+	for (s = s; *s != 0; s++) {
+		if ((*s >= '0') && (*s <= '9'))
+			r = (r * base) + (*s - '0');
+		else if ((*s >= 'A') && (*s <= 'F'))
+			r = (r * base) + (*s - 'A' + 10);
+		else if ((*s >= 'a') && (*s <= 'f'))
+			r = (r * base) + (*s - 'a' + 10);
+		else
+			break;
+	}
+
+	return r * pn;
+}
+
+/**
+ *  @brief Create cmd52 string
+ *
+ *  @param priv	A pointer to bt_private structure
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+form_cmd52_string(bt_private *priv)
+{
+	ENTER();
+
+	memset(cmd52_string, 0, CMD52_STR_LEN);
+	snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X",
+		 priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg,
+		 priv->bt_dev.cmd52_val);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/*
+ *  @brief Parse cmd52 string
+ *
+ *  @param buffer  A pointer user buffer
+ *  @param len     Length of user buffer
+ *  @param func    Parsed func number
+ *  @param reg     Parsed reg value
+ *  @param val     Parsed value to set
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+parse_cmd52_string(const char __user * buffer, size_t len,
+		   int *func, int *reg, int *val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	char *string = NULL;
+	char *pos = NULL;
+
+	ENTER();
+
+	string = kzalloc(CMD52_STR_LEN, GFP_KERNEL);
+	if (!string) {
+		PRINTM(ERROR, "BT: Can not alloc mem for cmd52 string\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+	memcpy(string, buffer + strlen("sdcmd52rw="),
+	       len - strlen("sdcmd52rw="));
+	string = strstrip(string);
+
+	*func = -1;
+	*reg = -1;
+	*val = -1;
+
+	/* Get func */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*func = string_to_number(pos);
+
+	/* Get reg */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*reg = string_to_number(pos);
+
+	/* Get val (optional) */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*val = string_to_number(pos);
+	kfree(string);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function handle generic proc file close
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+proc_close(struct inode *inode, struct file *file)
+{
+	struct proc_data *pdata = file->private_data;
+	ENTER();
+	if (pdata) {
+		if (pdata->on_close != NULL)
+			pdata->on_close(inode, file);
+		kfree(pdata->rdbuf);
+		kfree(pdata->wrbuf);
+		kfree(pdata);
+	}
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handle generic proc file read
+ *
+ *  @param file    A pointer to file structure
+ *  @param buffer  A pointer to output buffer
+ *  @param len     number of byte to read
+ *  @param offset  A pointer to offset of file
+ *  @return		number of output data
+ */
+static ssize_t
+proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset)
+{
+	loff_t pos = *offset;
+	struct proc_data *pdata = (struct proc_data *)file->private_data;
+	if ((!pdata->rdbuf) || (pos < 0))
+		return -EINVAL;
+	if (pos >= pdata->rdlen)
+		return 0;
+	if (len > pdata->rdlen - pos)
+		len = pdata->rdlen - pos;
+	if (copy_to_user(buffer, pdata->rdbuf + pos, len))
+		return -EFAULT;
+	*offset = pos + len;
+	return len;
+}
+
+/**
+ *  @brief This function handle generic proc file write
+ *
+ *  @param file    A pointer to file structure
+ *  @param buffer  A pointer to input buffer
+ *  @param len     number of byte to write
+ *  @param offset  A pointer to offset of file
+ *  @return		number of input data
+ */
+static ssize_t
+proc_write(struct file *file,
+	   const char __user * buffer, size_t len, loff_t * offset)
+{
+	loff_t pos = *offset;
+	struct proc_data *pdata = (struct proc_data *)file->private_data;
+	int func = 0, reg = 0, val = 0;
+	int config_data = 0;
+	char *line = NULL;
+
+	if (!pdata->wrbuf || (pos < 0))
+		return -EINVAL;
+	if (pos >= pdata->maxwrlen)
+		return 0;
+	if (len > pdata->maxwrlen - pos)
+		len = pdata->maxwrlen - pos;
+	if (copy_from_user(pdata->wrbuf + pos, buffer, len))
+		return -EFAULT;
+	if (!strncmp(pdata->wrbuf + pos, "fw_reload", strlen("fw_reload"))) {
+		if (!strncmp
+		    (pdata->wrbuf + pos, "fw_reload=", strlen("fw_reload="))) {
+			line = pdata->wrbuf + pos;
+			line += strlen("fw_reload") + 1;
+			config_data = string_to_number(line);
+		} else
+			config_data = FW_RELOAD_SDIO_INBAND_RESET;
+		PRINTM(MSG, "Request fw_reload=%d\n", config_data);
+		bt_request_fw_reload(pdata->pbt, config_data);
+	}
+	if (!strncmp(pdata->wrbuf + pos, "sdcmd52rw=", strlen("sdcmd52rw="))) {
+		parse_cmd52_string(pdata->wrbuf + pos, len, &func, &reg, &val);
+		sd_write_cmd52_val(pdata->pbt, func, reg, val);
+	}
+	if (!strncmp(pdata->wrbuf + pos, "debug_dump", strlen("debug_dump"))) {
+		bt_dump_sdio_regs(pdata->pbt);
+		bt_dump_firmware_info_v2(pdata->pbt);
+	}
+
+	if (pos + len > pdata->wrlen)
+		pdata->wrlen = len + file->f_pos;
+	*offset = pos + len;
+	return len;
+}
+
+/**
+ *  @brief This function handle the generic file close
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return		N/A
+ */
+static void
+proc_on_close(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	struct proc_private_data *priv = PDE_DATA(inode);
+#else
+	struct proc_private_data *priv = PDE(inode)->data;
+#endif
+	struct proc_data *pdata = file->private_data;
+	char *line;
+	int i;
+	ENTER();
+	if (!pdata->wrlen)
+		return;
+	line = pdata->wrbuf;
+	while (line[0]) {
+		for (i = 0; i < priv->num_items; i++) {
+			if (!strncmp
+			    (line, priv->pdata[i].name,
+			     strlen(priv->pdata[i].name))) {
+				line += strlen(priv->pdata[i].name) + 1;
+				if (priv->pdata[i].size == 1)
+					*((u8 *)priv->pdata[i].addr) =
+						(u8)string_to_number(line);
+				else if (priv->pdata[i].size == 2)
+					*((u16 *) priv->pdata[i].addr) =
+						(u16) string_to_number(line);
+				else if (priv->pdata[i].size == 4)
+					*((u32 *)priv->pdata[i].addr) =
+						(u32)string_to_number(line);
+			}
+		}
+		while (line[0] && line[0] != '\n')
+			line++;
+		if (line[0])
+			line++;
+	}
+	if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd
+	    || priv->pbt->bt_dev.sdio_pull_ctrl
+	    || priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) {
+		bt_prepare_command(priv->pbt);
+		wake_up_interruptible(&priv->pbt->MainThread.waitQ);
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function handle the generic file open
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return	BT_STATUS_SUCCESS or other error no.
+ */
+static int
+proc_open(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	struct proc_private_data *priv = PDE_DATA(inode);
+#else
+	struct proc_private_data *priv = PDE(inode)->data;
+#endif
+	struct proc_data *pdata;
+	int i;
+	char *p;
+	u32 val = 0;
+	ENTER();
+	priv->pbt->adapter->skb_pending =
+		skb_queue_len(&priv->pbt->adapter->tx_queue);
+	file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL);
+	if (file->private_data == NULL) {
+		PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+	pdata = (struct proc_data *)file->private_data;
+	pdata->pbt = priv->pbt;
+	pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL);
+	if (pdata->rdbuf == NULL) {
+		PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n");
+		kfree(file->private_data);
+		LEAVE();
+		return -ENOMEM;
+	}
+	if (priv->fileflag == DEFAULT_FILE_PERM) {
+		pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL);
+		if (pdata->wrbuf == NULL) {
+			PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n");
+			kfree(pdata->rdbuf);
+			kfree(file->private_data);
+			return -ENOMEM;
+		}
+		pdata->maxwrlen = priv->bufsize;
+		pdata->on_close = proc_on_close;
+	}
+	p = pdata->rdbuf;
+	for (i = 0; i < priv->num_items; i++) {
+		if (priv->pdata[i].size == 1)
+			val = *((u8 *)priv->pdata[i].addr);
+		else if (priv->pdata[i].size == 2)
+			val = *((u16 *) priv->pdata[i].addr);
+		else if (priv->pdata[i].size == 4)
+			val = *((u32 *)priv->pdata[i].addr);
+		if (priv->pdata[i].flag & SHOW_INT)
+			p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val);
+		else if (priv->pdata[i].flag & SHOW_HEX)
+			p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val);
+		else if (priv->pdata[i].flag & SHOW_STRING) {
+			if (!strncmp
+			    (priv->pdata[i].name, "sdcmd52rw",
+			     strlen("sdcmd52rw"))) {
+				sd_read_cmd52_val(priv->pbt);
+				form_cmd52_string(priv->pbt);
+			}
+			p += sprintf(p, "%s=%s\n", priv->pdata[i].name,
+				     (char *)priv->pdata[i].addr);
+		}
+	}
+	pdata->rdlen = strlen(pdata->rdbuf);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/** Proc read ops */
+static const struct file_operations proc_read_ops = {
+	.read = proc_read,
+	.open = proc_open,
+	.release = proc_close
+};
+
+/** Proc Read-Write ops */
+static const struct file_operations proc_rw_ops = {
+	.read = proc_read,
+	.write = proc_write,
+	.open = proc_open,
+	.release = proc_close
+};
+
+static struct proc_private_data proc_files[] = {
+	{"status", S_IRUGO, 1024,
+	 sizeof(status_items) / sizeof(status_items[0]),
+	 &status_items[0], NULL, &proc_read_ops}
+	,
+	{"config", DEFAULT_FILE_PERM, 512,
+	 sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL,
+	 &proc_rw_ops}
+	,
+	{"debug", DEFAULT_FILE_PERM, 512,
+	 sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL,
+	 &proc_rw_ops}
+	,
+};
+
+/**
+ *  @brief This function initializes proc entry
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param m_dev    A pointer to struct m_dev
+ *  @param seq      Sequence number
+ *
+ *  @return	BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct proc_dir_entry *entry;
+	int i, j;
+
+	ENTER();
+
+	memset(cmd52_string, 0, CMD52_STR_LEN);
+	if (proc_mbt) {
+		priv->dev_proc[seq].proc_entry =
+			proc_mkdir(m_dev->name, proc_mbt);
+		if (!priv->dev_proc[seq].proc_entry) {
+			PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+
+		priv->dev_proc[seq].pfiles =
+			kmalloc(sizeof(proc_files), GFP_ATOMIC);
+		if (!priv->dev_proc[seq].pfiles) {
+			PRINTM(ERROR,
+			       "BT: Could not alloc memory for pfile!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files,
+		       sizeof(proc_files));
+		priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files);
+		for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++)
+			priv->dev_proc[seq].pfiles[j].pdata = NULL;
+		for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
+			priv->dev_proc[seq].pfiles[j].pdata =
+				kmalloc(priv->dev_proc[seq].pfiles[j].
+					num_items * sizeof(struct item_data),
+					GFP_ATOMIC);
+			if (!priv->dev_proc[seq].pfiles[j].pdata) {
+				PRINTM(ERROR,
+				       "BT: Could not alloc memory for pdata!\n");
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata,
+			       (u8 *)proc_files[j].pdata,
+			       priv->dev_proc[seq].pfiles[j].num_items *
+			       sizeof(struct item_data));
+			for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items;
+			     i++) {
+				if (priv->dev_proc[seq].pfiles[j].
+				    pdata[i].flag & OFFSET_BT_DEV)
+					priv->dev_proc[seq].pfiles[j].pdata[i].
+						addr =
+						priv->dev_proc[seq].pfiles[j].
+						pdata[i].offset +
+						(t_ptr)&priv->bt_dev;
+				if (priv->dev_proc[seq].pfiles[j].
+				    pdata[i].flag & OFFSET_BT_ADAPTER)
+					priv->dev_proc[seq].pfiles[j].pdata[i].
+						addr =
+						priv->dev_proc[seq].pfiles[j].
+						pdata[i].offset +
+						(t_ptr)priv->adapter;
+			}
+			priv->dev_proc[seq].pfiles[j].pbt = priv;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+			entry = proc_create_data(proc_files[j].name,
+						 S_IFREG | proc_files[j].
+						 fileflag,
+						 priv->dev_proc[seq].proc_entry,
+						 proc_files[j].fops,
+						 &priv->dev_proc[seq].
+						 pfiles[j]);
+			if (entry == NULL)
+#else
+			entry = create_proc_entry(proc_files[j].name,
+						  S_IFREG | proc_files[j].
+						  fileflag,
+						  priv->dev_proc[seq].
+						  proc_entry);
+			if (entry) {
+				entry->data = &priv->dev_proc[seq].pfiles[j];
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+				entry->owner = THIS_MODULE;
+#endif
+				entry->proc_fops = proc_files[j].fops;
+			} else
+#endif
+				PRINTM(MSG, "BT: Fail to create proc %s\n",
+				       proc_files[j].name);
+		}
+	}
+done:
+	if (ret == BT_STATUS_FAILURE) {
+		if (priv->dev_proc[seq].proc_entry) {
+			remove_proc_entry(m_dev->name, proc_mbt);
+			priv->dev_proc[seq].proc_entry = NULL;
+		}
+		if (priv->dev_proc[seq].pfiles) {
+			for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
+				if (priv->dev_proc[seq].pfiles[j].pdata) {
+					kfree(priv->dev_proc[seq].pfiles[j].
+					      pdata);
+					priv->dev_proc[seq].pfiles[j].pdata =
+						NULL;
+				}
+			}
+			kfree(priv->dev_proc[seq].pfiles);
+			priv->dev_proc[seq].pfiles = NULL;
+		}
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function removes proc interface
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return	N/A
+ */
+void
+bt_proc_remove(bt_private *priv)
+{
+	int j, i;
+
+	ENTER();
+	PRINTM(INFO, "BT: Remove Proc Interface\n");
+	if (proc_mbt) {
+		for (i = 0; i < MAX_RADIO_FUNC; i++) {
+			if (!priv->dev_proc[i].proc_entry)
+				continue;
+			for (j = 0; j < ARRAY_SIZE(proc_files); j++) {
+				remove_proc_entry(proc_files[j].name,
+						  priv->dev_proc[i].proc_entry);
+			}
+			remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt);
+			priv->dev_proc[i].proc_entry = NULL;
+
+			if (priv->dev_proc[i].pfiles) {
+				for (j = 0;
+				     j < priv->dev_proc[i].num_proc_files;
+				     j++) {
+					if (priv->dev_proc[i].pfiles[j].pdata) {
+						kfree(priv->dev_proc[i].
+						      pfiles[j].pdata);
+						priv->dev_proc[i].pfiles[j].
+							pdata = NULL;
+					}
+				}
+				kfree(priv->dev_proc[i].pfiles);
+				priv->dev_proc[i].pfiles = NULL;
+			}
+		}
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function creates proc interface
+ *  directory structure
+ *
+ *  @return		BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_root_proc_init(void)
+{
+	PRINTM(INFO, "BT: Create Proc Interface\n");
+	proc_mbt = proc_mkdir("mbt", PROC_DIR);
+	if (!proc_mbt) {
+		PRINTM(ERROR, "BT: Cannot create proc interface\n");
+		return BT_STATUS_FAILURE;
+	}
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function removes proc interface
+ *  directory structure
+ *
+ *  @return		BT_STATUS_SUCCESS
+ */
+int
+bt_root_proc_remove(void)
+{
+	remove_proc_entry("mbt", PROC_DIR);
+	proc_mbt = NULL;
+	return BT_STATUS_SUCCESS;
+}
diff --git a/bt_sd8987/bt_char/bt_sdio.h b/bt_sd8987/bt_char/bt_sdio.h
new file mode 100644
index 0000000..3fced8f
--- /dev/null
+++ b/bt_sd8987/bt_char/bt_sdio.h
@@ -0,0 +1,435 @@
+/** @file bt_sdio.h
+ *  @brief This file contains SDIO (interface) module
+ *  related macros, enum, and structure.
+ *
+ *  Copyright (C) 2007-2019, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _BT_SDIO_H_
+#define _BT_SDIO_H_
+
+#include <linux/irqreturn.h>
+
+/** SD8787 card type */
+#define CARD_TYPE_SD8787   0x01
+/** SD8777 card type */
+#define CARD_TYPE_SD8777   0x02
+/** SD8887 card type */
+#define CARD_TYPE_SD8887   0x03
+/** SD8897 card type */
+#define CARD_TYPE_SD8897   0x04
+/** SD8797 card type */
+#define CARD_TYPE_SD8797   0x05
+/** SD8977 card type */
+#define CARD_TYPE_SD8977   0x06
+/** SD8997 card type */
+#define CARD_TYPE_SD8997   0x07
+/** SD8987 card type */
+#define CARD_TYPE_SD8987   0x08
+/** SD8978 card type */
+#define CARD_TYPE_SD8978   0x09
+
+/** IRQ return type */
+typedef irqreturn_t IRQ_RET_TYPE;
+/** IRQ return */
+#define IRQ_RET		(return IRQ_HANDLED)
+/** ISR notifier function */
+typedef IRQ_RET_TYPE (*isr_notifier_fn_t) (s32 irq, void *dev_id,
+					   struct pt_regs * reg);
+
+/** SDIO header length */
+#define SDIO_HEADER_LEN			4
+
+/** Interrupt Mode SDIO */
+#define INT_MODE_SDIO       0
+/** Interrupt Mode GPIO */
+#define INT_MODE_GPIO       1
+/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */
+#define GPIO_INT_NEW_MODE   255
+/* SD block size can not bigger than 64 due to buf size limit in firmware */
+/** define SD block size for data Tx/Rx */
+#define SD_BLOCK_SIZE			64
+/** define SD block size for firmware download */
+#define SD_BLOCK_SIZE_FW_DL		256
+
+/** Number of blocks for firmware transfer */
+#define FIRMWARE_TRANSFER_NBLOCK	2
+
+/** Firmware ready */
+#define FIRMWARE_READY			0xfedc
+
+/* Bus Interface Control Reg 0x07 */
+/** SD BUS width 1 */
+#define SD_BUS_WIDTH_1			0x00
+/** SD BUS width 4 */
+#define SD_BUS_WIDTH_4			0x02
+/** SD BUS width mask */
+#define SD_BUS_WIDTH_MASK		0x03
+/** Asynchronous interrupt mode */
+#define ASYNC_INT_MODE			0x20
+
+/** magic register */
+#define CARD_MAGIC_REG          0xF0
+/** magic value */
+#define MAGIC_VAL               0x24
+
+/* Host Control Registers */
+/** Host Control Registers : Configuration */
+#define CONFIGURATION_REG		0x00
+/** Host Control Registers : Host without Command 53 finish host*/
+#define HOST_TO_CARD_EVENT		(0x1U << 3)
+/** Host Control Registers : Host terminates Command 53 */
+#define HOST_TERM_CMD53			(0x1U << 2)
+/** Host Control Registers : Host power up */
+#define HOST_POWER_UP			(0x1U << 1)
+/** Host Control Registers : Host power down */
+#define HOST_POWER_DOWN			(0x1U << 0)
+
+/** Host Control Registers : Host interrupt RSR */
+#define HOST_INT_RSR_REG		0x01
+
+/** Host Control Registers : Upload host interrupt RSR */
+#define UP_LD_HOST_INT_RSR		(0x1U)
+
+/** Host Control Registers : Host interrupt mask */
+#define HOST_INT_MASK_REG		0x02
+
+/** Host Control Registers : Upload host interrupt mask */
+#define UP_LD_HOST_INT_MASK		(0x1U)
+/** Host Control Registers : Download host interrupt mask */
+#define DN_LD_HOST_INT_MASK		(0x2U)
+/** Enable Host interrupt mask */
+#define HIM_ENABLE			(UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK)
+/** Disable Host interrupt mask */
+#define	HIM_DISABLE			0xff
+
+#define HOST_INTSTATUS_REG		0x03
+/** Host Control Registers : Upload host interrupt status */
+#define UP_LD_HOST_INT_STATUS		(0x1U)
+/** Host Control Registers : Download host interrupt status */
+#define DN_LD_HOST_INT_STATUS		(0x2U)
+
+/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/
+#define ENABLE_GPIO_1_INT_MODE  0x88
+/** Scratch reg 3 2  :     Configure GPIO-1 INT*/
+#define SCRATCH_REG_32          0xEE
+
+/** Host Control Registers : Host interrupt status */
+#define HOST_INT_STATUS_REG		0x28
+/** Host Control Registers : Upload CRC error */
+#define UP_LD_CRC_ERR			(0x1U << 2)
+/** Host Control Registers : Upload restart */
+#define UP_LD_RESTART			(0x1U << 1)
+/** Host Control Registers : Download restart */
+#define DN_LD_RESTART			(0x1U << 0)
+
+/* Card Control Registers */
+/** Card Control Registers : Read SQ base address A0 register */
+#define SQ_READ_BASE_ADDRESS_A0_REG		0x40
+/** Card Control Registers : Read SQ base address A1 register */
+#define SQ_READ_BASE_ADDRESS_A1_REG		0x41
+/** Card Control Registers : Read SQ base address A2 register */
+#define SQ_READ_BASE_ADDRESS_A2_REG		0x42
+/** Card Control Registers : Read SQ base address A3 register */
+#define SQ_READ_BASE_ADDRESS_A3_REG		0x43
+/** Card Control Registers : Read SQ base address B0 register */
+#define SQ_READ_BASE_ADDRESS_B0_REG		0x44
+/** Card Control Registers : Read SQ base address B1 register */
+#define SQ_READ_BASE_ADDRESS_B1_REG		0x45
+/** Card Control Registers : Read SQ base address B2 register */
+#define SQ_READ_BASE_ADDRESS_B2_REG		0x46
+/** Card Control Registers : Read SQ base address B3 register */
+#define SQ_READ_BASE_ADDRESS_B3_REG		0x47
+
+/** Card Control Registers : Card status register */
+#define CARD_STATUS_REG					0x30
+/** Card Control Registers : Card I/O ready */
+#define CARD_IO_READY					(0x1U << 3)
+/** Card Control Registers : CIS card ready */
+#define CIS_CARD_RDY					(0x1U << 2)
+/** Card Control Registers : Upload card ready */
+#define UP_LD_CARD_RDY					(0x1U << 1)
+/** Card Control Registers : Download card ready */
+#define DN_LD_CARD_RDY					(0x1U << 0)
+
+/** Card Control Registers : Host interrupt mask register */
+#define HOST_INTERRUPT_MASK_REG			0x34
+/** Card Control Registers : Host power interrupt mask */
+#define HOST_POWER_INT_MASK				(0x1U << 3)
+/** Card Control Registers : Abort card interrupt mask */
+#define ABORT_CARD_INT_MASK				(0x1U << 2)
+/** Card Control Registers : Upload card interrupt mask */
+#define UP_LD_CARD_INT_MASK				(0x1U << 1)
+/** Card Control Registers : Download card interrupt mask */
+#define DN_LD_CARD_INT_MASK				(0x1U << 0)
+
+/** Card Control Registers : Card interrupt status register */
+#define CARD_INTERRUPT_STATUS_REG		0x38
+/** Card Control Registers : Power up interrupt */
+#define POWER_UP_INT					(0x1U << 4)
+/** Card Control Registers : Power down interrupt */
+#define POWER_DOWN_INT					(0x1U << 3)
+
+/** Card Control Registers : Card interrupt RSR register */
+#define CARD_INTERRUPT_RSR_REG			0x3c
+/** Card Control Registers : Power up RSR */
+#define POWER_UP_RSR					(0x1U << 4)
+/** Card Control Registers : Power down RSR */
+#define POWER_DOWN_RSR					(0x1U << 3)
+
+/** Card Control Registers : Debug 0 register */
+#define DEBUG_0_REG						0x70
+/** Card Control Registers : SD test BUS 0 */
+#define SD_TESTBUS0						(0x1U)
+/** Card Control Registers : Debug 1 register */
+#define DEBUG_1_REG						0x71
+/** Card Control Registers : SD test BUS 1 */
+#define SD_TESTBUS1						(0x1U)
+/** Card Control Registers : Debug 2 register */
+#define DEBUG_2_REG						0x72
+/** Card Control Registers : SD test BUS 2 */
+#define SD_TESTBUS2						(0x1U)
+/** Card Control Registers : Debug 3 register */
+#define DEBUG_3_REG						0x73
+/** Card Control Registers : SD test BUS 3 */
+#define SD_TESTBUS3						(0x1U)
+
+/** Host Control Registers : I/O port 0 */
+#define IO_PORT_0_REG			0x78
+/** Host Control Registers : I/O port 1 */
+#define IO_PORT_1_REG			0x79
+/** Host Control Registers : I/O port 2 */
+#define IO_PORT_2_REG			0x7A
+
+/** Firmware status 0 register */
+#define CARD_FW_STATUS0_REG		0x60
+/** Firmware status 1 register */
+#define CARD_FW_STATUS1_REG		0x61
+/** Rx length register */
+#define CARD_RX_LEN_REG			0x62
+/** Rx unit register */
+#define CARD_RX_UNIT_REG		0x63
+/** Card Control Registers : Miscellaneous Configuration Register */
+#define CARD_MISC_CFG_REG		0x6C
+/** Misc. Config Register : Auto Re-enable interrupts */
+#define AUTO_RE_ENABLE_INT		(0x1U << 4)
+
+/** Card Control Registers : Card OCR 0 register */
+#define CARD_OCR_0_REG			0x68
+/** Card Control Registers : Card OCR 1 register */
+#define CARD_OCR_1_REG			0x69
+/** Card Control Registers : Card OCR 3 register */
+#define CARD_OCR_3_REG			0x6A
+/** Card Control Registers : Card config register */
+#define CARD_CONFIG_REG			0x6B
+/** Card Control Registers : Card revision register */
+#define CARD_REVISION_REG		0x5c
+/** Card Control Registers : Command 53 finish G BUS */
+#define CMD53_FINISH_GBUS		(0x1U << 1)
+/** Card Control Registers : SD negative edge */
+#define SD_NEG_EDGE				(0x1U << 0)
+
+/* Special registers in function 0 of the SDxx card */
+/** Special register in function 0 of the SDxxx card : Scratch 0 */
+#define	SCRATCH_0_REG			0x80fe
+/** Special register in function 0 of the SDxxx card : Scratch 1 */
+#define	SCRATCH_1_REG			0x80ff
+/** Host F1 read base 0 */
+#define HOST_F1_RD_BASE_0		0x0040
+/** Host F1 read base 1 */
+#define HOST_F1_RD_BASE_1		0x0041
+/** Host F1 card ready */
+#define HOST_F1_CARD_RDY		0x0020
+
+/** Chip Id Register 0 */
+#define CARD_CHIP_ID_0_REG		0x801c
+/** Chip Id Register 1 */
+#define CARD_CHIP_ID_1_REG		0x801d
+
+struct sdio_mmc_card {
+	/** sdio_func structure pointer */
+	struct sdio_func *func;
+	/** bt_private structure pointer */
+	bt_private *priv;
+};
+
+struct sdio_card_reg {
+	u8 cfg;
+	u8 host_int_mask;	// HOST_INT_MASK_REG
+	u8 host_intstatus;	// HOST_INTSTATUS_REG
+	u8 host_int_rsr_reg;	// HOST_INT_RSR_REG
+	u8 card_misc_cfg_reg;	// CARD_MISC_CFG_REG
+	u8 card_status;		// CARD_STATUS_REG
+	u8 sq_read_base_addr_a0;	// SQ_READ_BASE_ADDRESS_A0_REG
+	u8 sq_read_base_addr_a1;	// SQ_READ_BASE_ADDRESS_A1_REG
+	u8 card_revision;	// CARD_REVISION_REG
+	u8 card_fw_status0;	// CARD_FW_STATUS0_REG
+	u8 card_fw_status1;	// CARD_FW_STATUS1_REG
+	u8 card_rx_len;		// CARD_RX_LEN_REG
+	u8 card_rx_unit;	// CARD_RX_UNIT_REG
+	u8 io_port_0;		// IO_PORT_0_REG
+	u8 io_port_1;		// IO_PORT_1_REG
+	u8 io_port_2;		// IO_PORT_2_REG
+};
+
+struct sdio_device {
+	const struct sdio_card_reg *reg;
+};
+
+static const struct sdio_card_reg bt_reg_87xx = {
+	.cfg = 0x00,
+	.host_int_mask = 0x02,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x03,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x01,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0x6c,	// CARD_MISC_CFG_REG
+	.card_status = 0x30,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0x40,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0x41,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0x5C,	// CARD_REVISION_REG
+	.card_fw_status0 = 0x60,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0x61,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0x62,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0x63,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0x78,	// IO_PORT_0_REG
+	.io_port_1 = 0x79,	// IO_PORT_1_REG
+	.io_port_2 = 0x7a,	// IO_PORT_2_REG
+};
+
+static const struct sdio_card_reg bt_reg_8887 = {
+	.cfg = 0x00,
+	.host_int_mask = 0x08,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x0C,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x04,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0xD8,	// CARD_MISC_CFG_REG
+	.card_status = 0x5C,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0x6C,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0x6D,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0xC8,	// CARD_REVISION_REG
+	.card_fw_status0 = 0x88,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0x89,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0x8A,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0x8B,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0xE4,	// IO_PORT_0_REG
+	.io_port_1 = 0xE5,	// IO_PORT_1_REG
+	.io_port_2 = 0xE6,	// IO_PORT_2_REG
+};
+
+static const struct sdio_card_reg bt_reg_8897 = {
+	.cfg = 0x00,
+	.host_int_mask = 0x02,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x03,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x01,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0xCC,	// CARD_MISC_CFG_REG
+	.card_status = 0x50,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0x60,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0x61,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0xBC,	// CARD_REVISION_REG
+	.card_fw_status0 = 0xC0,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0xC1,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0xC2,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0xC3,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0xD8,	// IO_PORT_0_REG
+	.io_port_1 = 0xD9,	// IO_PORT_1_REG
+	.io_port_2 = 0xDA,	// IO_PORT_2_REG
+};
+
+static const struct sdio_card_reg bt_reg_8977_8978_8997 = {
+	.cfg = 0x00,
+	.host_int_mask = 0x08,	// HOST_INT_MASK_REG
+	.host_intstatus = 0x0C,	// HOST_INTSTATUS_REG
+	.host_int_rsr_reg = 0x04,	// HOST_INT_RSR_REG
+	.card_misc_cfg_reg = 0xD8,	// CARD_MISC_CFG_REG
+	.card_status = 0x5C,	// CARD_STATUS_REG
+	.sq_read_base_addr_a0 = 0xF8,	// SQ_READ_BASE_ADDRESS_A0_REG
+	.sq_read_base_addr_a1 = 0xF9,	// SQ_READ_BASE_ADDRESS_A1_REG
+	.card_revision = 0xC8,	// CARD_REVISION_REG
+	.card_fw_status0 = 0xE8,	// CARD_FW_STATUS0_REG
+	.card_fw_status1 = 0xE9,	// CARD_FW_STATUS1_REG
+	.card_rx_len = 0xEA,	// CARD_RX_LEN_REG
+	.card_rx_unit = 0xEB,	// CARD_RX_UNIT_REG
+	.io_port_0 = 0xE4,	// IO_PORT_0_REG
+	.io_port_1 = 0xE5,	// IO_PORT_1_REG
+	.io_port_2 = 0xE6,	// IO_PORT_2_REG
+};
+
+static const struct sdio_device bt_sdio_sd8787 = {
+	.reg = &bt_reg_87xx,
+};
+
+static const struct sdio_device bt_sdio_sd8777 = {
+	.reg = &bt_reg_87xx,
+};
+
+static const struct sdio_device bt_sdio_sd8887 = {
+	.reg = &bt_reg_8887,
+};
+
+static const struct sdio_device bt_sdio_sd8897 = {
+	.reg = &bt_reg_8897,
+};
+
+static const struct sdio_device bt_sdio_sd8797 = {
+	.reg = &bt_reg_87xx,
+};
+
+static const struct sdio_device bt_sdio_sd8977 = {
+	.reg = &bt_reg_8977_8978_8997,
+};
+
+static const struct sdio_device bt_sdio_sd8978 = {
+	.reg = &bt_reg_8977_8978_8997,
+};
+
+static const struct sdio_device bt_sdio_sd8997 = {
+	.reg = &bt_reg_8977_8978_8997,
+};
+
+static const struct sdio_device bt_sdio_sd8987 = {
+	.reg = &bt_reg_8977_8978_8997,
+};
+
+/** DMA alignment value */
+#define DMA_ALIGNMENT	64
+/** 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)	\
+	((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1))
+
+/** This function read cmd52 register */
+int sd_write_reg(bt_private *priv, int reg, u8 val);
+/** This function write cmd52 value to register */
+int sd_read_reg(bt_private *priv, int reg, u8 *data);
+/** This function reads the Cmd52 value in dev structure */
+int sd_read_cmd52_val(bt_private *priv);
+/** This function updates card reg based on the Cmd52 value in dev structure */
+int sd_write_cmd52_val(bt_private *priv, int func, int reg, int val);
+
+void sdio_update_card_type(bt_private *priv, void *card);
+int sdio_get_sdio_device(bt_private *priv);
+
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+/** This function tells lower driver that BT is suspended */
+void bt_is_suspended(bt_private *priv);
+#endif
+#endif
+#endif
+#endif /* _BT_SDIO_H_ */
diff --git a/bt_sd8987/bt_char/bt_sdiommc.c b/bt_sd8987/bt_char/bt_sdiommc.c
new file mode 100644
index 0000000..d7f51bb
--- /dev/null
+++ b/bt_sd8987/bt_char/bt_sdiommc.c
@@ -0,0 +1,2397 @@
+/** @file bt_sdiommc.c
+ *  @brief This file contains SDIO IF (interface) module
+ *  related functions.
+ *
+ * Copyright (C) 2007-2019, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/firmware.h>
+#include <linux/mmc/sdio_func.h>
+#include        <linux/mmc/sdio.h>
+#include        <linux/mmc/card.h>
+
+#include "bt_drv.h"
+#include "bt_sdio.h"
+
+/** define marvell vendor id */
+#define MARVELL_VENDOR_ID 0x02df
+
+/** Max retry number of CMD53 read/write */
+#define MAX_CMD53_RETRY 	3
+/** Max retry number of CMD53 read/write */
+#define MAX_CMD52_RETRY     3
+/** Firmware name */
+static char *fw_name;
+/** fw serial download flag */
+extern int bt_fw_serial;
+int bt_intmode = INT_MODE_SDIO;
+/** request firmware nowait */
+int bt_req_fw_nowait;
+static int multi_fn = BIT(2);
+
+#define DEFAULT_FW_NAME ""
+
+/** FW header length for CRC check disable */
+#define FW_CRC_HEADER_RB2   28
+/** FW header for CRC check disable */
+static u8 fw_crc_header_rb_2[FW_CRC_HEADER_RB2] = {
+	0x05, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+	0x9d, 0x32, 0xbb, 0x11, 0x01, 0x00, 0x00, 0x7f,
+	0x00, 0x00, 0x00, 0x00, 0x67, 0xd6, 0xfc, 0x25
+};
+
+/** FW header length for CRC check disable */
+#define FW_CRC_HEADER_RB   24
+/** FW header for CRC check disable */
+static u8 fw_crc_header_rb_1[FW_CRC_HEADER_RB] = {
+	0x01, 0x00, 0x00, 0x00, 0x04, 0xfd, 0x00, 0x04,
+	0x08, 0x00, 0x00, 0x00, 0x26, 0x52, 0x2a, 0x7b,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/** Default firmware name */
+#define DEFAULT_FW_NAME_8777 "mrvl/sd8777_uapsta.bin"
+#define DEFAULT_FW_NAME_8787 "mrvl/sd8787_uapsta.bin"
+#define DEFAULT_FW_NAME_8797 "mrvl/sd8797_uapsta.bin"
+#define DEFAULT_FW_NAME_8887 "mrvl/sd8887_uapsta.bin"
+#define DEFAULT_FW_NAME_8897 "mrvl/sd8897_uapsta.bin"
+#define DEFAULT_FW_NAME_8977 "mrvl/sdsd8977_combo.bin"
+#define DEFAULT_FW_NAME_8978 "mrvl/sdsd8978_combo.bin"
+#define DEFAULT_FW_NAME_8997 "mrvl/sdsd8997_combo.bin"
+
+/** SD8787 chip revision ID */
+#define SD8787_W0      0x30
+#define SD8787_W1      0x31
+#define SD8787_A0_A1   0x40
+/** SD8797 chip revision ID */
+#define SD8797_A0       0x00
+#define SD8797_B0       0x10
+/** SD8897 chip revision ID */
+#define SD8897_A0       0x10
+#define SD8897_B0       0x20
+
+/** SD8887 chip revision ID */
+#define SD8887_A0       0x0
+#define SD8887_A2       0x2
+#define SD8887_A0_FW_NAME "mrvl/sd8887_uapsta.bin"
+#define SD8887_A2_FW_NAME "mrvl/sd8887_uapsta_a2.bin"
+#define SD8887_A2_BT_FW_NAME "mrvl/sd8887_bt_a2.bin"
+
+#define SD8897_A0_FW_NAME "mrvl/sd8897_uapsta_a0.bin"
+#define SD8897_B0_FW_NAME "mrvl/sd8897_uapsta.bin"
+
+#define SD8787_W1_FW_NAME "mrvl/sd8787_uapsta_w1.bin"
+#define SD8787_AX_FW_NAME "mrvl/sd8787_uapsta.bin"
+#define SD8797_A0_FW_NAME "mrvl/sd8797_uapsta_a0.bin"
+#define SD8797_B0_FW_NAME "mrvl/sd8797_uapsta.bin"
+
+/** SD8977 chip revision ID */
+#define SD8977_V0       0x0
+#define SD8977_V1       0x8
+#define SD8977_V2       0x9
+#define SD8977_V0_FW_NAME "mrvl/sdsd8977_combo.bin"
+#define SD8977_V0_BT_FW_NAME "mrvl/sd8977_bt.bin"
+#define SD8977_V1_FW_NAME "mrvl/sdsd8977_combo_v1.bin"
+#define SD8977_V1_BT_FW_NAME "mrvl/sd8977_bt_v1.bin"
+#define SD8977_V2_FW_NAME "mrvl/sdsd8977_combo_v2.bin"
+#define SD8977_V2_BT_FW_NAME "mrvl/sd8977_bt_v2.bin"
+/** SD8978 FW NAME */
+#define SD8978_FW_NAME      "mrvl/sdsd8978_combo.bin"
+#define SD8978_BT_FW_NAME   "mrvl/sd8978_bt.bin"
+
+/** SD8997 chip revision ID */
+#define SD8997_Z        0x02
+#define SD8997_V2       0x10
+#define SD8997_Z_FW_NAME "mrvl/sdsd8997_combo.bin"
+#define SD8997_Z_BT_FW_NAME "mrvl/sd8997_bt.bin"
+#define SD8997_V2_FW_NAME "mrvl/sdsd8997_combo_v2.bin"
+#define SD8997_V2_BT_FW_NAME "mrvl/sd8997_bt_v2.bin"
+#define SD8997_V3_FW_NAME "mrvl/sdsd8997_combo_v3.bin"
+#define SD8997_V3_BT_FW_NAME "mrvl/sd8997_bt_v3.bin"
+
+/** SD8987 */
+#define SD8987_FW_NAME  "mrvl/sdsd8987_combo.bin"
+#define SD8987_BT_FW_NAME "mrvl/sd8987_bt.bin"
+
+/** Function number 2 */
+#define FN2			2
+/** Device ID for SD8787 FN2 */
+#define SD_DEVICE_ID_8787_BT_FN2    0x911A
+/** Device ID for SD8787 FN3 */
+#define SD_DEVICE_ID_8787_BT_FN3    0x911B
+/** Device ID for SD8777 FN2 */
+#define SD_DEVICE_ID_8777_BT_FN2    0x9132
+/** Device ID for SD8777 FN3 */
+#define SD_DEVICE_ID_8777_BT_FN3    0x9133
+/** Device ID for SD8887 FN2 */
+#define SD_DEVICE_ID_8887_BT_FN2    0x9136
+/** Device ID for SD8887 FN3 */
+#define SD_DEVICE_ID_8887_BT_FN3    0x9137
+/** Device ID for SD8897 FN2 */
+#define SD_DEVICE_ID_8897_BT_FN2    0x912E
+/** Device ID for SD8897 FN3 */
+#define SD_DEVICE_ID_8897_BT_FN3    0x912F
+/** Device ID for SD8797 FN2 */
+#define SD_DEVICE_ID_8797_BT_FN2    0x912A
+/** Device ID for SD8797 FN3 */
+#define SD_DEVICE_ID_8797_BT_FN3    0x912B
+/** Device ID for SD8977 FN2 */
+#define SD_DEVICE_ID_8977_BT_FN2    0x9146
+/** Device ID for SD8978 FN2 */
+#define SD_DEVICE_ID_8978_BT_FN2    0x915a
+/** Device ID for SD8997 FN2 */
+#define SD_DEVICE_ID_8997_BT_FN2    0x9142
+/** Device ID for SD8987 FN2 */
+#define SD_DEVICE_ID_8987_BT_FN2    0x914a
+/** Device ID for SD8987 FN3 */
+#define SD_DEVICE_ID_8987_BT_FN3    0x914b
+
+/** Array of SDIO device ids when multi_fn=0x12 */
+static const struct sdio_device_id bt_ids[] = {
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8787_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8777_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8887_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8897_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8797_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8977_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8978_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997_BT_FN2)},
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8987_BT_FN2)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(sdio, bt_ids);
+
+#ifdef SDIO_OOB_IRQ
+extern int mrvl_sdio_claim_irq(struct sdio_func *func,
+			       sdio_irq_handler_t * handler);
+extern int mrvl_sdio_release_irq(struct sdio_func *func);
+extern int mrvl_sdio_suspend(struct sdio_func *func);
+extern int mrvl_sdio_resume(struct sdio_func *func);
+#endif
+
+/********************************************************
+		Global Variables
+********************************************************/
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+extern int mbt_pm_keep_power;
+#endif
+
+extern bt_private *m_priv[];
+/********************************************************
+		Local Functions
+********************************************************/
+
+/**
+ *  @brief This function gets rx_unit value
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_get_rx_unit(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_rx_unit_reg = priv->psdio_device->reg->card_rx_unit;
+
+	ENTER();
+
+	reg = sdio_readb(card->func, card_rx_unit_reg, &ret);
+	if (ret == BT_STATUS_SUCCESS)
+		priv->bt_dev.rx_unit = reg;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads fwstatus registers
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param dat	   A pointer to keep returned data
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_read_firmware_status(bt_private *priv, u16 * dat)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 fws0;
+	u8 fws1;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0;
+	u8 card_fw_status1_reg = priv->psdio_device->reg->card_fw_status1;
+
+	ENTER();
+
+	fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret);
+	if (ret < 0) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret);
+	if (ret < 0) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	*dat = (((u16) fws1) << 8) | fws0;
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function reads rx length
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param dat	   A pointer to keep returned data
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+static int
+sd_read_rx_len(bt_private *priv, u16 * dat)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_rx_len_reg = priv->psdio_device->reg->card_rx_len;
+
+	ENTER();
+
+	reg = sdio_readb(card->func, card_rx_len_reg, &ret);
+	if (ret == BT_STATUS_SUCCESS)
+		*dat = (u16) reg << priv->bt_dev.rx_unit;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables the host interrupts mask
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mask	   the interrupt mask
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_enable_host_int_mask(bt_private *priv, u8 mask)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask;
+
+	ENTER();
+
+	sdio_writeb(card->func, mask, host_int_mask_reg, &ret);
+	if (ret) {
+		PRINTM(WARN, "BT: Unable to enable the host interrupt!\n");
+		ret = BT_STATUS_FAILURE;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function disables the host interrupts mask.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mask	   the interrupt mask
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+static int
+sbi_disable_host_int_mask(bt_private *priv, u8 mask)
+{
+	int ret = BT_STATUS_FAILURE;
+	u8 host_int_mask;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask;
+
+	ENTER();
+
+	/* Read back the host_int_mask register */
+	host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret);
+	if (ret)
+		goto done;
+
+	/* Update with the mask and write back to the register */
+	host_int_mask &= ~mask;
+	sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret);
+	if (ret < 0) {
+		PRINTM(WARN, "BT: Unable to diable the host interrupt!\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function polls the card status register
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param bits     the bit mask
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_poll_card_status(bt_private *priv, u8 bits)
+{
+	int tries;
+	int rval;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 cs;
+	u8 card_status_reg = priv->psdio_device->reg->card_status;
+
+	ENTER();
+
+	for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) {
+		cs = sdio_readb(card->func, card_status_reg, &rval);
+		if (rval != 0)
+			break;
+		if (rval == 0 && (cs & bits) == bits) {
+			LEAVE();
+			return BT_STATUS_SUCCESS;
+		}
+		udelay(1);
+	}
+	PRINTM(ERROR,
+	       "BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n",
+	       rval, tries, cs);
+
+	LEAVE();
+	return BT_STATUS_FAILURE;
+}
+
+/**
+ *  @brief This function reads updates the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_read_cmd52_val(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 func, reg, val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	ENTER();
+
+	func = priv->bt_dev.cmd52_func;
+	reg = priv->bt_dev.cmd52_reg;
+	sdio_claim_host(card->func);
+	if (func)
+		val = sdio_readb(card->func, reg, &ret);
+	else
+		val = sdio_f0_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	if (ret) {
+		PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n",
+		       func, reg);
+	} else {
+		priv->bt_dev.cmd52_val = val;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function updates card reg based on the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param func     Stores func variable
+ *  @param reg      Stores reg variable
+ *  @param val      Stores val variable
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_write_cmd52_val(bt_private *priv, int func, int reg, int val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	ENTER();
+
+	if (val >= 0) {
+		/* Perform actual write only if val is provided */
+		sdio_claim_host(card->func);
+		if (func)
+			sdio_writeb(card->func, val, reg, &ret);
+		else
+			sdio_f0_writeb(card->func, val, reg, &ret);
+		sdio_release_host(card->func);
+		if (ret) {
+			PRINTM(ERROR,
+			       "BT: Cannot write value (0x%x) to func %d reg %d\n",
+			       val, func, reg);
+			goto done;
+		}
+		priv->bt_dev.cmd52_val = val;
+	}
+
+	/* Save current func and reg for future read */
+	priv->bt_dev.cmd52_func = func;
+	priv->bt_dev.cmd52_reg = reg;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function updates card reg based on the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to write
+ *  @param val      value
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_write_reg(bt_private *priv, int reg, u8 val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	sdio_writeb(card->func, val, reg, &ret);
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads updates the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to read
+ *  @param data		Data
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_read_reg(bt_private *priv, int reg, u8 *data)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	val = sdio_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	*data = val;
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads FN0 reg value
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to read
+ *  @param data		Data
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_f0_read_reg(bt_private *priv, int reg, u8 *data)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	val = sdio_f0_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	*data = val;
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function probes the card
+ *
+ *  @param func    A pointer to sdio_func structure.
+ *  @param id      A pointer to structure sdio_device_id
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+static int
+sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id)
+{
+	int ret = BT_STATUS_SUCCESS;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *card = NULL;
+
+	ENTER();
+
+	PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor,
+	       id->device, id->class, func->num);
+	card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL);
+	if (!card) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	card->func = func;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+	/* wait for chip fully wake up */
+	if (!func->enable_timeout)
+		func->enable_timeout = 200;
+#endif
+	sdio_claim_host(func);
+	ret = sdio_enable_func(func);
+	if (ret) {
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret);
+		kfree(card);
+		LEAVE();
+		return -EIO;
+	}
+	sdio_release_host(func);
+	priv = bt_add_card(card);
+	if (!priv) {
+		sdio_claim_host(func);
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		ret = BT_STATUS_FAILURE;
+		kfree(card);
+	}
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function checks if the firmware is ready to accept
+ *  command or not.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param pollnum  Number of times to poll fw status
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_verify_fw_download(bt_private *priv, int pollnum)
+{
+	int ret = BT_STATUS_FAILURE;
+	u16 firmwarestat = 0;
+	int tries;
+
+	ENTER();
+
+	/* Wait for firmware initialization event */
+	for (tries = 0; tries < pollnum; tries++) {
+		if (sd_read_firmware_status(priv, &firmwarestat) < 0)
+			continue;
+		if (firmwarestat == FIRMWARE_READY) {
+			PRINTM(MSG, "BT FW is active(%d)\n", tries);
+			ret = BT_STATUS_SUCCESS;
+			break;
+		}
+		mdelay(100);
+	}
+	if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS)) {
+		PRINTM(ERROR,
+		       "Fail to poll firmware status: firmwarestat=0x%x\n",
+		       firmwarestat);
+		bt_dump_sdio_regs(priv);
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Transfers firmware to card
+ *
+ *  @param priv      A Pointer to bt_private structure
+ *  @param fw        A Pointer to fw image
+ *  @param fw_len    fw image len
+ *  @return          BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+static int
+sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len)
+{
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 *firmware = fw;
+	int firmwarelen = fw_len;
+	u8 base0;
+	u8 base1;
+	int ret = BT_STATUS_SUCCESS;
+	int offset;
+	void *tmpfwbuf = NULL;
+	int tmpfwbufsz;
+	u8 *fwbuf;
+	u16 len;
+	int txlen = 0;
+	int tx_blocks = 0;
+	int i = 0;
+	int tries = 0;
+#ifdef FW_DOWNLOAD_SPEED
+	u32 tv1, tv2;
+#endif
+	u8 sq_read_base_address_a0_reg =
+		priv->psdio_device->reg->sq_read_base_addr_a0;
+	u8 sq_read_base_address_a1_reg =
+		priv->psdio_device->reg->sq_read_base_addr_a1;
+	u8 crc_buffer = 0;
+	u8 *header_crc_fw = NULL;
+	u8 header_crc_fw_len = 0;
+
+	if (priv->card_type == CARD_TYPE_SD8787) {
+		header_crc_fw = fw_crc_header_rb_1;
+		header_crc_fw_len = FW_CRC_HEADER_RB;
+	} else if (priv->card_type == CARD_TYPE_SD8777) {
+		header_crc_fw = fw_crc_header_rb_2;
+		header_crc_fw_len = FW_CRC_HEADER_RB2;
+	}
+
+	ENTER();
+
+	PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen);
+
+#ifdef FW_DOWNLOAD_SPEED
+	tv1 = get_utimeofday();
+#endif
+
+	tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT;
+	tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL);
+	if (!tmpfwbuf) {
+		PRINTM(ERROR,
+		       "BT: Unable to allocate buffer for firmware. Terminating download\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	/* Ensure aligned firmware buffer */
+	fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT);
+
+	if (!(priv->fw_crc_check)
+	    && ((priv->card_type == CARD_TYPE_SD8787) ||
+		(priv->card_type == CARD_TYPE_SD8777))
+		) {
+		/* CRC check not required, use custom header first */
+		firmware = header_crc_fw;
+		firmwarelen = header_crc_fw_len;
+		crc_buffer = 1;
+	}
+
+	/* Perform firmware data transfer */
+	offset = 0;
+	do {
+		/* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits
+		 */
+		ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY);
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: FW download with helper poll status timeout @ %d\n",
+			       offset);
+			goto done;
+		}
+		if (!crc_buffer)
+			/* More data? */
+			if (offset >= firmwarelen)
+				break;
+
+		for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+			base0 = sdio_readb(card->func,
+					   sq_read_base_address_a0_reg, &ret);
+			if (ret) {
+				PRINTM(WARN, "Dev BASE0 register read failed:"
+				       " base0=0x%04X(%d). Terminating download\n",
+				       base0, base0);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			base1 = sdio_readb(card->func,
+					   sq_read_base_address_a1_reg, &ret);
+			if (ret) {
+				PRINTM(WARN, "Dev BASE1 register read failed:"
+				       " base1=0x%04X(%d). Terminating download\n",
+				       base1, base1);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			len = (((u16) base1) << 8) | base0;
+
+			if (len != 0)
+				break;
+			udelay(10);
+		}
+
+		if (len == 0)
+			break;
+		else if (len > BT_UPLD_SIZE) {
+			PRINTM(FATAL,
+			       "BT: FW download failure @ %d, invalid length %d\n",
+			       offset, len);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	/** ignore CRC check before download the first packet */
+		if (offset == 0 && (len & BIT(0)))
+			len &= ~BIT(0);
+		txlen = len;
+
+		if (len & BIT(0)) {
+			i++;
+			if (i >= MAX_CMD53_RETRY) {
+				PRINTM(FATAL,
+				       "BT: FW download failure @ %d, over max retry count\n",
+				       offset);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			PRINTM(ERROR,
+			       "BT: FW CRC error indicated by the helper:"
+			       " len = 0x%04X, txlen = %d\n", len, txlen);
+			len &= ~BIT(0);
+
+			PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset);
+			/* Setting this to 0 to resend from same offset */
+			txlen = 0;
+		} else {
+			i = 0;
+
+			/* Set blocksize to transfer - checking for last block */
+			if (firmwarelen - offset < txlen)
+				txlen = firmwarelen - offset;
+
+			PRINTM(INFO, ".");
+
+			tx_blocks =
+				(txlen + SD_BLOCK_SIZE_FW_DL -
+				 1) / SD_BLOCK_SIZE_FW_DL;
+
+			/* Copy payload to buffer */
+			memcpy(fwbuf, &firmware[offset], txlen);
+		}
+
+		/* Send data */
+		ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf,
+				   tx_blocks * SD_BLOCK_SIZE_FW_DL);
+
+		if (ret < 0) {
+			PRINTM(ERROR,
+			       "BT: FW download, write iomem (%d) failed @ %d\n",
+			       i, offset);
+			sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret);
+			if (ret)
+				PRINTM(ERROR, "write ioreg failed (CFG)\n");
+		}
+
+		offset += txlen;
+		if (crc_buffer
+		    && ((priv->card_type == CARD_TYPE_SD8787) ||
+			(priv->card_type == CARD_TYPE_SD8777))
+			) {
+			if (offset >= header_crc_fw_len) {
+				/* Custom header download complete, restore
+				   original FW */
+				offset = 0;
+				firmware = fw;
+				firmwarelen = fw_len;
+				crc_buffer = 0;
+			}
+		}
+	} while (TRUE);
+
+	PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset);
+
+	ret = BT_STATUS_SUCCESS;
+done:
+#ifdef FW_DOWNLOAD_SPEED
+	tv2 = get_utimeofday();
+	PRINTM(INFO, "FW: %d.%03d.%03d ", tv1 / 1000000,
+	       (tv1 % 1000000) / 1000, tv1 % 1000);
+	PRINTM(INFO, " -> %d.%03d.%03d ", tv2 / 1000000,
+	       (tv2 % 1000000) / 1000, tv2 % 1000);
+	tv2 -= tv1;
+	PRINTM(INFO, " == %d.%03d.%03d\n", tv2 / 1000000,
+	       (tv2 % 1000000) / 1000, tv2 % 1000);
+#endif
+	kfree(tmpfwbuf);
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief request_firmware callback
+ *
+ * @param fw_firmware  A pointer to firmware structure
+ * @param context      A Pointer to bt_private structure
+ * @return             BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_request_fw_dpc(const struct firmware *fw_firmware, void *context)
+{
+	int ret = BT_STATUS_SUCCESS;
+	bt_private *priv = (bt_private *)context;
+	struct sdio_mmc_card *card = NULL;
+	struct m_dev *m_dev_bt = NULL;
+	struct timeval tstamp;
+	int index;
+
+	ENTER();
+
+	m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ];
+
+	if ((priv == NULL) || (priv->adapter == NULL) ||
+	    (priv->bt_dev.card == NULL) || (m_dev_bt == NULL)
+		) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	if (!fw_firmware) {
+		do_gettimeofday(&tstamp);
+		if (tstamp.tv_sec >
+		    (priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) {
+			PRINTM(ERROR,
+			       "BT: No firmware image found. Skipping download\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		PRINTM(ERROR,
+		       "BT: No firmware image found! Retrying download\n");
+		/* Wait a second here before calling the callback again */
+		os_sched_timeout(1000);
+		sd_download_firmware_w_helper(priv);
+		LEAVE();
+		return ret;
+	}
+
+	priv->firmware = fw_firmware;
+
+	if (BT_STATUS_FAILURE ==
+	    sd_init_fw_dpc(priv, (u8 *)priv->firmware->data,
+			   priv->firmware->size)) {
+		PRINTM(ERROR,
+		       "BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n",
+		       bt_req_fw_nowait);
+		sdio_release_host(card->func);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* check if the fimware is downloaded successfully or not */
+	if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) {
+		PRINTM(ERROR, "BT: FW failed to be active in time!\n");
+		ret = BT_STATUS_FAILURE;
+		sdio_release_host(card->func);
+		goto done;
+	}
+	sdio_release_host(card->func);
+	sbi_enable_host_int(priv);
+	if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+		PRINTM(ERROR,
+		       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (fw_firmware) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+		if (!bt_req_fw_nowait)
+#endif
+			release_firmware(fw_firmware);
+	}
+	LEAVE();
+	return ret;
+
+done:
+	if (fw_firmware) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+		if (!bt_req_fw_nowait)
+#endif
+			release_firmware(fw_firmware);
+	}
+	/* For synchronous download cleanup will be done in add_card */
+	if (!bt_req_fw_nowait)
+		return ret;
+	PRINTM(INFO, "unregister device\n");
+	sbi_unregister_dev(priv);
+	((struct sdio_mmc_card *)card)->priv = NULL;
+	/* Stop the thread servicing the interrupts */
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid)
+		os_sched_timeout(1);
+	bt_proc_remove(priv);
+	clean_up_m_devs(priv);
+	bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief request_firmware callback
+ *        This function is invoked by request_firmware_nowait system call
+ *
+ * @param firmware     A pointer to firmware structure
+ * @param context      A Pointer to bt_private structure
+ * @return             None
+ **/
+static void
+sd_request_fw_callback(const struct firmware *firmware, void *context)
+{
+	ENTER();
+	sd_request_fw_dpc(firmware, context);
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function downloads firmware image to the card.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sd_download_firmware_w_helper(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	int err;
+	char *cur_fw_name = NULL;
+
+	ENTER();
+
+	cur_fw_name = fw_name;
+
+	if (fw_name == NULL) {
+		if (priv->card_type == CARD_TYPE_SD8787)
+			cur_fw_name = DEFAULT_FW_NAME_8787;
+		else if (priv->card_type == CARD_TYPE_SD8777)
+			cur_fw_name = DEFAULT_FW_NAME_8777;
+		else if (priv->card_type == CARD_TYPE_SD8887) {
+			/* Check revision ID */
+			switch (priv->adapter->chip_rev) {
+			case SD8887_A0:
+				cur_fw_name = SD8887_A0_FW_NAME;
+				break;
+			case SD8887_A2:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8887_A2_FW_NAME;
+				else
+					cur_fw_name = SD8887_A2_BT_FW_NAME;
+				break;
+			default:
+				cur_fw_name = DEFAULT_FW_NAME_8887;
+				break;
+			}
+		} else if (priv->card_type == CARD_TYPE_SD8897)
+			cur_fw_name = DEFAULT_FW_NAME_8897;
+		else if (priv->card_type == CARD_TYPE_SD8797)
+			cur_fw_name = DEFAULT_FW_NAME_8797;
+		else if (priv->card_type == CARD_TYPE_SD8977) {
+			switch (priv->adapter->chip_rev) {
+			case SD8977_V0:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8977_V0_FW_NAME;
+				else
+					cur_fw_name = SD8977_V0_BT_FW_NAME;
+				break;
+			case SD8977_V1:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8977_V1_FW_NAME;
+				else
+					cur_fw_name = SD8977_V1_BT_FW_NAME;
+				break;
+			case SD8977_V2:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8977_V2_FW_NAME;
+				else
+					cur_fw_name = SD8977_V2_BT_FW_NAME;
+				break;
+			default:
+				cur_fw_name = DEFAULT_FW_NAME_8977;
+				break;
+			}
+		} else if (priv->card_type == CARD_TYPE_SD8978) {
+			if (bt_fw_serial == 1
+			    && !priv->fw_reload && !bt_fw_reload)
+				cur_fw_name = SD8978_FW_NAME;
+			else
+				cur_fw_name = SD8978_BT_FW_NAME;
+		} else if (priv->card_type == CARD_TYPE_SD8997)
+			switch (priv->adapter->chip_rev) {
+			case SD8997_Z:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload)
+					cur_fw_name = SD8997_Z_FW_NAME;
+				else
+					cur_fw_name = SD8997_Z_BT_FW_NAME;
+				break;
+			case SD8997_V2:
+				if (bt_fw_serial == 1
+				    && !priv->fw_reload && !bt_fw_reload) {
+					if (priv->adapter->magic_val ==
+					    MAGIC_VAL)
+						cur_fw_name = SD8997_V3_FW_NAME;
+					else
+						cur_fw_name = SD8997_V2_FW_NAME;
+				} else {
+					if (priv->adapter->magic_val ==
+					    MAGIC_VAL)
+						cur_fw_name =
+							SD8997_V3_BT_FW_NAME;
+					else
+						cur_fw_name =
+							SD8997_V2_BT_FW_NAME;
+				}
+				break;
+			default:
+				cur_fw_name = DEFAULT_FW_NAME_8997;
+				break;
+		} else if (priv->card_type == CARD_TYPE_SD8987) {
+			if (bt_fw_serial == 1
+			    && !priv->fw_reload && !bt_fw_reload)
+				cur_fw_name = SD8987_FW_NAME;
+			else
+				cur_fw_name = SD8987_BT_FW_NAME;
+		}
+	}
+
+	PRINTM(MSG, "BT Request firmware: %s\n", cur_fw_name);
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cur_fw_name, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      sd_request_fw_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cur_fw_name, priv->hotplug_device,
+					      priv, sd_request_fw_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cur_fw_name, priv->hotplug_device,
+					      priv, sd_request_fw_callback);
+#endif
+#endif
+		if (ret < 0)
+			PRINTM(FATAL,
+			       "BT: request_firmware_nowait() failed, error code = %#x\n",
+			       ret);
+	} else {
+		err = request_firmware(&priv->firmware, cur_fw_name,
+				       priv->hotplug_device);
+		if (err < 0) {
+			PRINTM(FATAL,
+			       "BT: request_firmware() failed, error code = %#x\n",
+			       err);
+			ret = BT_STATUS_FAILURE;
+		} else
+			ret = sd_request_fw_dpc(priv->firmware, priv);
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads data from the card.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_card_to_host(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u16 buf_len = 0;
+	int buf_block_len;
+	int blksz;
+	struct sk_buff *skb = NULL;
+	u32 type;
+	u8 *payload = NULL;
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+	struct m_dev *mdev_debug = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+	struct debug_dev *debug_dev =
+		(struct debug_dev *)priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int i = 0;
+
+	ENTER();
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	do {
+		/* Read the length of data to be transferred */
+		ret = sd_read_rx_len(priv, &buf_len);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR, "BT: Read scratch reg failed (%d)\n", i);
+			if (i >= MAX_CMD52_RETRY) {
+				ret = BT_STATUS_FAILURE;
+				goto exit;
+			}
+			udelay(20);
+		}
+	}
+	while (ret == BT_STATUS_FAILURE);
+
+	/* Allocate buffer */
+	blksz = SD_BLOCK_SIZE;
+	buf_block_len = (buf_len + blksz - 1) / blksz;
+	if (buf_len <= BT_HEADER_LEN ||
+	    (buf_block_len * blksz) > ALLOC_BUF_SIZE) {
+		PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n",
+		       buf_len);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "BT: No free skb\n");
+		goto exit;
+	}
+	if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) {
+		skb_put(skb,
+			DMA_ALIGNMENT -
+			((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
+		skb_pull(skb,
+			 DMA_ALIGNMENT -
+			 ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
+	}
+
+	payload = skb->data;
+	i = 0;
+	do {
+		ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport,
+				  buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR,
+			       "BT: card_to_host, read iomem (%d) failed: %d\n",
+			       i, ret);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+			/* issue abort cmd52 command through F0 */
+			sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret);
+#endif
+			sdio_writeb(card->func, HOST_TERM_CMD53,
+				    CONFIGURATION_REG, &ret);
+			udelay(20);
+			ret = BT_STATUS_FAILURE;
+			if (i >= MAX_CMD53_RETRY) {
+				kfree_skb(skb);
+				skb = NULL;
+				goto exit;
+			}
+		}
+	} while (ret == BT_STATUS_FAILURE);
+	/* This is SDIO specific header length: byte[2][1][0], * type: byte[3]
+	   (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */
+	buf_len = payload[0];
+	buf_len |= (u16) payload[1] << 8;
+	type = payload[3];
+	PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", mbt_dev->name,
+	       buf_len, type);
+	if (buf_len > buf_block_len * blksz) {
+		PRINTM(ERROR,
+		       "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n",
+		       buf_len, buf_block_len * blksz);
+		ret = BT_STATUS_FAILURE;
+		kfree_skb(skb);
+		skb = NULL;
+		goto exit;
+	}
+	DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len);
+	switch (type) {
+	case HCI_ACLDATA_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (*(u16 *) skb->data == 0xffff) {
+			bt_store_firmware_dump(priv, skb->data, skb->len);
+			dev_kfree_skb_any(skb);
+			break;
+		}
+		bt_recv_frame(priv, skb);
+		break;
+	case HCI_SCODATA_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		bt_recv_frame(priv, skb);
+		break;
+	case HCI_EVENT_PKT:
+		/** add EVT Demux */
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb))
+			break;
+		switch (skb->data[0]) {
+		case 0x0E:
+			/** cmd complete */
+			if (priv->debug_device_pending) {
+				if (priv->debug_ocf_ogf[0] == skb->data[3] &&
+				    priv->debug_ocf_ogf[1] == skb->data[4]) {
+					priv->debug_device_pending = 0;
+					priv->debug_ocf_ogf[0] = 0;
+					priv->debug_ocf_ogf[1] = 0;
+					/** debug cmd complete */
+					if (debug_dev) {
+						skb->dev = (void *)mdev_debug;
+						mdev_recv_frame(skb);
+						mdev_debug->stat.byte_rx +=
+							buf_len;
+					}
+					break;
+				}
+			}
+			bt_recv_frame(priv, skb);
+			break;
+		case 0x0F:
+			/** cmd status */
+			bt_recv_frame(priv, skb);
+			break;
+		case 0xFF:
+			/** Vendor specific pkt */
+			bt_recv_frame(priv, skb);
+			break;
+		default:
+			bt_recv_frame(priv, skb);
+			break;
+		}
+		break;
+	case MRVL_VENDOR_PKT:
+		/* Just think here need to back compatible FM */
+		bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (BT_STATUS_SUCCESS != bt_process_event(priv, skb))
+			bt_recv_frame(priv, skb);
+		break;
+	default:
+		/* Driver specified event and command resp should be handle
+		   here */
+		PRINTM(INFO, "BT: Unknown PKT type:%d\n", type);
+		kfree_skb(skb);
+		skb = NULL;
+		break;
+	}
+exit:
+	if (ret) {
+		if (mbt_dev)
+			mdev_bt->stat.err_rx++;
+		PRINTM(ERROR, "error when recv pkt!\n");
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function removes the card
+ *
+ *  @param func    A pointer to sdio_func structure
+ *  @return        N/A
+ */
+static void
+sd_remove_card(struct sdio_func *func)
+{
+	struct sdio_mmc_card *card;
+
+	ENTER();
+
+	if (func) {
+		card = sdio_get_drvdata(func);
+		if (card) {
+			bt_remove_card(card->priv);
+			kfree(card);
+		}
+	}
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function handles the interrupt.
+ *
+ *  @param func  A pointer to sdio_func structure
+ *  @return      N/A
+ */
+static void
+sd_interrupt(struct sdio_func *func)
+{
+	bt_private *priv;
+	struct m_dev *m_dev = NULL;
+	struct sdio_mmc_card *card;
+	int ret = BT_STATUS_SUCCESS;
+	u8 ireg = 0;
+	u8 host_intstatus_reg = 0;
+
+	ENTER();
+
+	card = sdio_get_drvdata(func);
+	if (!card || !card->priv) {
+		PRINTM(INFO,
+		       "BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n",
+		       __func__, func, card);
+		LEAVE();
+		return;
+	}
+	priv = card->priv;
+	host_intstatus_reg = priv->psdio_device->reg->host_intstatus;
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	if (priv->card_type == CARD_TYPE_SD8887 ||
+	    priv->card_type == CARD_TYPE_SD8897 ||
+	    priv->card_type == CARD_TYPE_SD8977 ||
+	    priv->card_type == CARD_TYPE_SD8997 ||
+	    priv->card_type == CARD_TYPE_SD8987 ||
+	    priv->card_type == CARD_TYPE_SD8978) {
+		ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0,
+				  SD_BLOCK_SIZE);
+		if (ret) {
+			PRINTM(ERROR,
+			       "BT: sdio_read_ioreg: cmd53 read int status register failed %d\n",
+			       ret);
+			goto done;
+		}
+		ireg = priv->adapter->hw_regs[host_intstatus_reg];
+	} else {
+		ireg = sdio_readb(card->func, host_intstatus_reg, &ret);
+		if (ret) {
+			PRINTM(ERROR,
+			       "BT: sdio_read_ioreg: CMD52 read int status register failed %d\n",
+			       ret);
+			goto done;
+		}
+	}
+	if (ireg != 0) {
+		/*
+		 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+		 * Clear the interrupt status register and re-enable
+		 * the interrupt
+		 */
+		PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name,
+		       ireg);
+		priv->adapter->irq_recv = ireg;
+		if (priv->card_type == CARD_TYPE_SD8777 ||
+		    priv->card_type == CARD_TYPE_SD8787) {
+			sdio_writeb(card->func,
+				    ~(ireg) & (DN_LD_HOST_INT_STATUS |
+					       UP_LD_HOST_INT_STATUS),
+				    host_intstatus_reg, &ret);
+			if (ret) {
+				PRINTM(ERROR,
+				       "BT: sdio_write_ioreg: clear int status register failed\n");
+				goto done;
+			}
+		}
+	} else {
+		PRINTM(ERROR, "BT: ERR: ireg=0\n");
+	}
+	OS_INT_DISABLE;
+	priv->adapter->sd_ireg |= ireg;
+	OS_INT_RESTORE;
+	bt_interrupt(m_dev);
+done:
+	LEAVE();
+}
+
+/**
+ *  @brief This function checks if the interface is ready to download
+ *  or not while other download interfaces are present
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @param val    Winner status (0: winner)
+ *  @return       BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_check_winner_status(bt_private *priv, u8 *val)
+{
+
+	int ret = BT_STATUS_SUCCESS;
+	u8 winner = 0;
+	struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0;
+
+	ENTER();
+	winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret);
+	if (ret != BT_STATUS_SUCCESS) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	*val = winner;
+
+	LEAVE();
+	return ret;
+}
+
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+/** @brief This function tells lower driver that BT is suspended
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        None
+ */
+void
+bt_is_suspended(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	priv->adapter->is_suspended = TRUE;
+	sdio_func_suspended(card->func);
+}
+#endif
+
+/** @brief This function handles client driver suspend
+ *
+ *  @param dev	   A pointer to device structure
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+int
+bt_sdio_suspend(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+		PRINTM(ERROR,
+		       "BT: %s: cannot remain alive while host is suspended\n",
+		       sdio_func_id(func));
+		return -ENOSYS;
+	}
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv = cardp->priv;
+
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name);
+	mbt_hci_suspend_dev(m_dev);
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
+#ifdef BLE_WAKEUP
+		/** Set BLE Wake up pattern */
+		if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv, FALSE))
+			PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n");
+#endif
+
+		if (BT_STATUS_SUCCESS != bt_enable_hs(priv, FALSE)) {
+			PRINTM(CMD, "BT: HS not actived, suspend fail!\n");
+			if (BT_STATUS_SUCCESS != bt_enable_hs(priv, FALSE)) {
+				PRINTM(CMD,
+				       "BT: HS not actived the second time, force to suspend!\n");
+			}
+		}
+	}
+
+	priv->adapter->is_suspended = TRUE;
+
+#ifdef SDIO_OOB_IRQ
+	mrvl_sdio_suspend(func);
+#endif
+	LEAVE();
+	/* We will keep the power when hs enabled successfully */
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) {
+#ifdef MMC_PM_SKIP_RESUME_PROBE
+		PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and "
+		       "MMC_PM_SKIP_RESUME_PROBE\n");
+		return sdio_set_host_pm_flags(func,
+					      MMC_PM_KEEP_POWER |
+					      MMC_PM_SKIP_RESUME_PROBE);
+#else
+		PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n");
+		return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+#endif
+	} else {
+		PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n");
+		return BT_STATUS_SUCCESS;
+	}
+}
+
+void
+bt_sdio_shutdown(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+
+	ENTER();
+
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: shutdown: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+		PRINTM(ERROR,
+		       "BT: %s: cannot remain alive while host is shutdown\n",
+		       sdio_func_id(func));
+		return;
+	}
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return;
+	}
+
+	priv = cardp->priv;
+
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
+#ifdef BLE_WAKEUP
+		/** Set BLE Wake up pattern */
+		if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv, TRUE))
+			PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n");
+#endif
+
+		if (BT_STATUS_SUCCESS != bt_enable_hs(priv, TRUE)) {
+			PRINTM(CMD, "BT: HS not actived, shutdown fail!\n");
+			if (BT_STATUS_SUCCESS != bt_enable_hs(priv, TRUE)) {
+				PRINTM(CMD,
+				       "BT: HS not actived the second time, force to shutdown!\n");
+			}
+		}
+	}
+	LEAVE();
+}
+
+/** @brief This function handles client driver resume
+ *
+ *  @param dev	   A pointer to device structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_sdio_resume(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv = cardp->priv;
+#ifdef BLE_WAKEUP
+	if (priv->ble_wakeup_buf) {
+
+		if (priv->adapter->is_suspended == TRUE) {
+			PRINTM(CMD, "BT, send bt_send_hw_remove_event\n");
+			bt_send_hw_remove_event(priv);
+		} else {
+			PRINTM(CMD, "BT: Send system resume event\n");;
+			bt_send_system_event(priv, FALSE);
+		}
+	}
+#endif
+	priv->adapter->is_suspended = FALSE;
+#ifdef SDIO_OOB_IRQ
+	mrvl_sdio_resume(func);
+#endif
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name);
+	mbt_hci_resume_dev(m_dev);
+	sbi_wakeup_firmware(priv);
+	priv->adapter->hs_state = HS_DEACTIVATED;
+	PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+#endif
+#endif
+
+/********************************************************
+		Global Functions
+********************************************************/
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+static const struct dev_pm_ops bt_sdio_pm_ops = {
+	.suspend = bt_sdio_suspend,
+	.resume = bt_sdio_resume,
+};
+#endif
+#endif
+static struct sdio_driver sdio_bt = {
+	.name = "sdio_bt",
+	.id_table = bt_ids,
+	.probe = sd_probe_card,
+	.remove = sd_remove_card,
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+	.drv = {
+		.pm = &bt_sdio_pm_ops,
+		.shutdown = bt_sdio_shutdown,
+		}
+#endif
+#endif
+};
+
+/**
+ *  @brief This function registers the bt module in bus driver.
+ *
+ *  @return	   An int pointer that keeps returned value
+ */
+int *
+sbi_register(void)
+{
+	int *ret;
+
+	ENTER();
+
+	if (sdio_register_driver(&sdio_bt) != 0) {
+		PRINTM(FATAL, "BT: SD Driver Registration Failed\n");
+		LEAVE();
+		return NULL;
+	} else
+		ret = (int *)1;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function de-registers the bt module in bus driver.
+ *
+ *  @return        N/A
+ */
+void
+sbi_unregister(void)
+{
+	ENTER();
+	sdio_unregister_driver(&sdio_bt);
+	LEAVE();
+}
+
+/**
+ *  @brief This function registers the device.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_dev(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	u8 chiprev;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	struct sdio_func *func;
+	u8 host_intstatus_reg = priv->psdio_device->reg->host_intstatus;
+	u8 host_int_rsr_reg = priv->psdio_device->reg->host_int_rsr_reg;
+	u8 card_misc_cfg_reg = priv->psdio_device->reg->card_misc_cfg_reg;
+	u8 card_revision_reg = priv->psdio_device->reg->card_revision;
+	u8 io_port_0_reg = priv->psdio_device->reg->io_port_0;
+	u8 io_port_1_reg = priv->psdio_device->reg->io_port_1;
+	u8 io_port_2_reg = priv->psdio_device->reg->io_port_2;
+	u8 card_magic_reg = CARD_MAGIC_REG;
+	u8 magic_val = 0;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: Error: card or function is NULL!\n");
+		goto failed;
+	}
+	func = card->func;
+	priv->hotplug_device = &func->dev;
+
+	/* Initialize the private structure */
+	strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name));
+	priv->bt_dev.ioport = 0;
+	priv->bt_dev.fn = func->num;
+
+	sdio_claim_host(func);
+#ifdef SDIO_OOB_IRQ
+	ret = mrvl_sdio_claim_irq(func, sd_interrupt);
+#else
+	ret = sdio_claim_irq(func, sd_interrupt);
+#endif
+	if (ret) {
+		PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret);
+		goto release_host;
+	}
+	ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE);
+	if (ret) {
+		PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__);
+		goto release_irq;
+	}
+
+	/* read Revision Register to get the chip revision number */
+	chiprev = sdio_readb(func, card_revision_reg, &ret);
+	if (ret) {
+		PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n");
+		goto release_irq;
+	}
+	priv->adapter->chip_rev = chiprev;
+	PRINTM(INFO, "revision=%#x\n", chiprev);
+
+	magic_val = sdio_readb(func, card_magic_reg, &ret);
+	if (ret) {
+		PRINTM(FATAL, ": cannot read CARD_MAGIC_REG\n");
+		goto release_irq;
+	}
+	priv->adapter->magic_val = magic_val;
+	PRINTM(INFO, "magic_val=%#x\n", magic_val);
+
+	/*
+	 * Read the HOST_INTSTATUS_REG for ACK the first interrupt got
+	 * from the bootloader. If we don't do this we get a interrupt
+	 * as soon as we register the irq.
+	 */
+	reg = sdio_readb(func, host_intstatus_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+
+	/* Read the IO port */
+	reg = sdio_readb(func, io_port_0_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= reg;
+
+	reg = sdio_readb(func, io_port_1_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= (reg << 8);
+
+	reg = sdio_readb(func, io_port_2_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= (reg << 16);
+
+	PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn,
+	       priv->bt_dev.ioport);
+
+	if (priv->card_type == CARD_TYPE_SD8977 ||
+	    priv->card_type == CARD_TYPE_SD8978) {
+		if (bt_intmode == INT_MODE_GPIO) {
+			PRINTM(MSG, "Enable GPIO-1 INT\n");
+			sdio_writeb(func, ENABLE_GPIO_1_INT_MODE,
+				    SCRATCH_REG_32, &ret);
+			if (ret < 0)
+				goto release_irq;
+		}
+	}
+
+#define SDIO_INT_MASK       0x3F
+	if (priv->card_type == CARD_TYPE_SD8887 ||
+	    priv->card_type == CARD_TYPE_SD8897 ||
+	    priv->card_type == CARD_TYPE_SD8797 ||
+	    priv->card_type == CARD_TYPE_SD8977 ||
+	    priv->card_type == CARD_TYPE_SD8997 ||
+	    priv->card_type == CARD_TYPE_SD8987 ||
+	    priv->card_type == CARD_TYPE_SD8978) {
+		/* Set Host interrupt reset to read to clear */
+		reg = sdio_readb(func, host_int_rsr_reg, &ret);
+		if (ret < 0)
+			goto release_irq;
+		sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret);
+		if (ret < 0)
+			goto release_irq;
+		/* Set auto re-enable */
+		reg = sdio_readb(func, card_misc_cfg_reg, &ret);
+		if (ret < 0)
+			goto release_irq;
+		sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg,
+			    &ret);
+		if (ret < 0)
+			goto release_irq;
+	}
+
+	sdio_set_drvdata(func, card);
+	sdio_release_host(func);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+release_irq:
+#ifdef SDIO_OOB_IRQ
+	mrvl_sdio_release_irq(func);
+#else
+	sdio_release_irq(func);
+#endif
+release_host:
+	sdio_release_host(func);
+failed:
+
+	LEAVE();
+	return BT_STATUS_FAILURE;
+}
+
+/**
+ *  @brief This function de-registers the device.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+sbi_unregister_dev(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+
+	ENTER();
+
+	if (card && card->func) {
+		sdio_claim_host(card->func);
+#ifdef SDIO_OOB_IRQ
+		mrvl_sdio_release_irq(card->func);
+#else
+		sdio_release_irq(card->func);
+#endif
+		sdio_disable_func(card->func);
+		sdio_release_host(card->func);
+		sdio_set_drvdata(card->func, NULL);
+	}
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function enables the host interrupts.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_enable_host_int(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	ret = sd_enable_host_int_mask(priv, HIM_ENABLE);
+	sd_get_rx_unit(priv);
+	sdio_release_host(card->func);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function disables the host interrupts.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sbi_disable_host_int(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	ret = sbi_disable_host_int_mask(priv, HIM_DISABLE);
+	sdio_release_host(card->func);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends data to the card.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param payload A pointer to the data/cmd buffer
+ *  @param nb      Length of data/cmd
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+#ifdef DEBUG_LEVEL1
+	struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+#endif
+	int ret = BT_STATUS_SUCCESS;
+	int buf_block_len;
+	int blksz;
+	int i = 0;
+	u8 *buf = NULL;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	buf = payload;
+
+	blksz = SD_BLOCK_SIZE;
+	buf_block_len = (nb + blksz - 1) / blksz;
+	/* Allocate buffer and copy payload */
+	if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) {
+		if (nb > MAX_TX_BUF_SIZE) {
+			PRINTM(ERROR, "BT: Invalid tx packet, size=%d\n", nb);
+			LEAVE();
+			return BT_STATUS_FAILURE;
+		}
+		/* Ensure 8-byte aligned CMD buffer */
+		buf = priv->adapter->tx_buf;
+		memcpy(buf, payload, nb);
+	}
+	sdio_claim_host(card->func);
+	do {
+		/* Transfer data to card */
+		ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf,
+				   buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR,
+			       "BT: host_to_card, write iomem (%d) failed: %d\n",
+			       i, ret);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+			/* issue abort cmd52 command through F0 */
+			sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret);
+#endif
+			sdio_writeb(card->func, HOST_TERM_CMD53,
+				    CONFIGURATION_REG, &ret);
+			udelay(20);
+			ret = BT_STATUS_FAILURE;
+			if (i >= MAX_CMD53_RETRY)
+				goto exit;
+		} else {
+			PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n",
+			       m_dev->name, nb);
+			DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb);
+		}
+	} while (ret == BT_STATUS_FAILURE);
+	priv->bt_dev.tx_dnld_rdy = FALSE;
+exit:
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function downloads firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_download_fw(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+	u8 winner = 0;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	sdio_claim_host(card->func);
+	if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) {
+		PRINTM(MSG, "BT: FW already downloaded!\n");
+		sdio_release_host(card->func);
+		sbi_enable_host_int(priv);
+		if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+			PRINTM(ERROR,
+			       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+			ret = BT_STATUS_FAILURE;
+			goto err_register;
+		}
+		goto exit;
+	}
+	/* Check if other interface is downloading */
+	ret = sd_check_winner_status(priv, &winner);
+	if (ret == BT_STATUS_FAILURE) {
+		PRINTM(FATAL, "BT read winner status failed!\n");
+		goto done;
+	}
+	if (winner) {
+		PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n",
+		       winner);
+		/* check if the fimware is downloaded successfully or not */
+		if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) {
+			PRINTM(FATAL, "BT: FW failed to be active in time!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		sdio_release_host(card->func);
+		sbi_enable_host_int(priv);
+		if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+			PRINTM(ERROR,
+			       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+			ret = BT_STATUS_FAILURE;
+			goto err_register;
+		}
+		goto exit;
+	}
+
+	do_gettimeofday(&priv->req_fw_time);
+	/* Download the main firmware via the helper firmware */
+	if (sd_download_firmware_w_helper(priv)) {
+		PRINTM(INFO, "BT: FW download failed!\n");
+		ret = BT_STATUS_FAILURE;
+	}
+	goto exit;
+done:
+	sdio_release_host(card->func);
+exit:
+	LEAVE();
+	return ret;
+err_register:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function checks the interrupt status and handle it accordingly.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+sbi_get_int_status(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 sdio_ireg = 0;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+
+	ENTER();
+
+	OS_INT_DISABLE;
+	sdio_ireg = priv->adapter->sd_ireg;
+	priv->adapter->sd_ireg = 0;
+	OS_INT_RESTORE;
+	sdio_claim_host(card->func);
+	priv->adapter->irq_done = sdio_ireg;
+	if (sdio_ireg & DN_LD_HOST_INT_STATUS) {	/* tx_done INT */
+		if (priv->bt_dev.tx_dnld_rdy) {	/* tx_done already received */
+			PRINTM(INFO,
+			       "BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n",
+			       priv->bt_dev.tx_dnld_rdy, sdio_ireg);
+		} else {
+			priv->bt_dev.tx_dnld_rdy = TRUE;
+		}
+	}
+	if (sdio_ireg & UP_LD_HOST_INT_STATUS)
+		sd_card_to_host(priv);
+
+	ret = BT_STATUS_SUCCESS;
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function wakeup firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sbi_wakeup_firmware(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
+	sdio_release_host(card->func);
+	PRINTM(CMD, "BT wake up firmware\n");
+
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function updates the SDIO card types
+ *
+ *  @param priv     A Pointer to the bt_private structure
+ *  @param card     A Pointer to card
+ *
+ *  @return         N/A
+ */
+void
+sdio_update_card_type(bt_private *priv, void *card)
+{
+	struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)card;
+
+	/* Update card type */
+	if (cardp->func->device == SD_DEVICE_ID_8777_BT_FN2 ||
+	    cardp->func->device == SD_DEVICE_ID_8777_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8777;
+	else if (cardp->func->device == SD_DEVICE_ID_8787_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8787_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8787;
+	else if (cardp->func->device == SD_DEVICE_ID_8887_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8887_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8887;
+	else if (cardp->func->device == SD_DEVICE_ID_8897_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8897_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8897;
+	else if (cardp->func->device == SD_DEVICE_ID_8797_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8797_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8797;
+	else if (cardp->func->device == SD_DEVICE_ID_8977_BT_FN2)
+		priv->card_type = CARD_TYPE_SD8977;
+	else if (cardp->func->device == SD_DEVICE_ID_8978_BT_FN2)
+		priv->card_type = CARD_TYPE_SD8978;
+	else if (cardp->func->device == SD_DEVICE_ID_8997_BT_FN2)
+		priv->card_type = CARD_TYPE_SD8997;
+	else if (cardp->func->device == SD_DEVICE_ID_8987_BT_FN2 ||
+		 cardp->func->device == SD_DEVICE_ID_8987_BT_FN3)
+		priv->card_type = CARD_TYPE_SD8987;
+}
+
+/**
+ *  @brief This function get sdio device from card type
+ *
+ *  @param pmadapter  A pointer to mlan_adapter structure
+ *  @return           MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+int
+sdio_get_sdio_device(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u16 card_type = priv->card_type;
+
+	ENTER();
+
+	switch (card_type) {
+	case CARD_TYPE_SD8777:
+		priv->psdio_device = &bt_sdio_sd8777;
+		break;
+	case CARD_TYPE_SD8787:
+		priv->psdio_device = &bt_sdio_sd8787;
+		break;
+	case CARD_TYPE_SD8887:
+		priv->psdio_device = &bt_sdio_sd8887;
+		break;
+	case CARD_TYPE_SD8897:
+		priv->psdio_device = &bt_sdio_sd8897;
+		break;
+	case CARD_TYPE_SD8797:
+		priv->psdio_device = &bt_sdio_sd8797;
+		break;
+	case CARD_TYPE_SD8977:
+		priv->psdio_device = &bt_sdio_sd8977;
+		break;
+	case CARD_TYPE_SD8978:
+		priv->psdio_device = &bt_sdio_sd8978;
+		break;
+	case CARD_TYPE_SD8997:
+		priv->psdio_device = &bt_sdio_sd8997;
+		break;
+	case CARD_TYPE_SD8987:
+		priv->psdio_device = &bt_sdio_sd8987;
+		break;
+	default:
+		PRINTM(ERROR, "BT can't get right card type \n");
+		ret = BT_STATUS_FAILURE;
+		break;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+#define SD8897_INIT_START_REG  0xDC
+#define SD8897_INIT_END_REG  0xE1
+#define SD8887_INIT_START_REG  0xA0
+#define SD8887_INIT_END_REG  0xA5
+#define SD8977_SD8978_SD8997_INIT_START_REG 0xF1
+#define SD8977_SD8978_SD8997_INIT_END_REG 0xF6
+
+/** @brief This function dump the SDIO register
+ *
+ *  @param priv     A Pointer to the bt_private structure
+ *
+ *  @return         N/A
+ */
+void
+bt_dump_sdio_regs(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	char buf[256], *ptr;
+	u8 loop, func, data;
+	unsigned int reg, reg_start, reg_end;
+	u8 index = 0;
+	unsigned int reg_table_8887[] = { 0x58, 0x59, 0x5c, 0x60, 0x64, 0x70,
+		0x71, 0x72, 0x73, 0xd8, 0xd9, 0xda
+	};
+	u8 loop_num = 0;
+	unsigned int *reg_table = NULL;
+	u8 reg_table_size = 0;
+	unsigned int init_reg_start = 0;
+	unsigned int init_reg_end = 0;
+	if (priv->card_type == CARD_TYPE_SD8887) {
+		init_reg_start = SD8887_INIT_START_REG;
+		init_reg_end = SD8887_INIT_END_REG;
+	} else if (priv->card_type == CARD_TYPE_SD8897) {
+		init_reg_start = SD8897_INIT_START_REG;
+		init_reg_end = SD8897_INIT_END_REG;
+	} else if (priv->card_type == CARD_TYPE_SD8977 ||
+		   priv->card_type == CARD_TYPE_SD8997 ||
+		   priv->card_type == CARD_TYPE_SD8987 ||
+		   priv->card_type == CARD_TYPE_SD8978) {
+		init_reg_start = SD8977_SD8978_SD8997_INIT_START_REG;
+		init_reg_end = SD8977_SD8978_SD8997_INIT_END_REG;
+	}
+
+	if (priv->card_type == CARD_TYPE_SD8887) {
+		loop_num = 3;
+		reg_table = reg_table_8887;
+		reg_table_size = sizeof(reg_table_8887) / sizeof(int);
+	} else
+		loop_num = 2;
+	if (priv->adapter->ps_state)
+		sbi_wakeup_firmware(priv);
+
+	for (loop = 0; loop < loop_num; loop++) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+		if (loop == 0) {
+			/* Read the registers of SDIO function0 */
+			func = loop;
+			reg_start = 0;
+			reg_end = 9;
+
+		} else if (loop == 2) {
+			/* Read specific registers of SDIO function1 */
+			index = 0;
+			func = 2;
+			reg_start = reg_table[index++];
+			reg_end = reg_table[reg_table_size - 1];
+		} else {
+			func = 2;
+			reg_start = 0;
+			reg_end = 0x09;
+		}
+		if (loop == 2)
+			ptr += sprintf(ptr, "SDIO Func%d: ", func);
+		else
+			ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func,
+				       reg_start, reg_end);
+		for (reg = reg_start; reg <= reg_end;) {
+			if (func == 0)
+				ret = sd_f0_read_reg(priv, reg, &data);
+			else
+				ret = sd_read_reg(priv, reg, &data);
+			if (loop == 2)
+				ptr += sprintf(ptr, "(%#x)", reg);
+			if (!ret)
+				ptr += sprintf(ptr, "%02x ", data);
+			else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+			if (loop == 2 && reg < reg_end)
+				reg = reg_table[index++];
+			else
+				reg++;
+		}
+		PRINTM(MSG, "%s\n", buf);
+	}
+
+	if (init_reg_start) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+		ptr += sprintf(ptr, "Init Status Reg (%#x-%#x): ",
+			       init_reg_start, init_reg_end);
+		for (reg = init_reg_start; reg <= init_reg_end;) {
+			ret = sd_read_reg(priv, reg, &data);
+			if (!ret)
+				ptr += sprintf(ptr, "%02x ", data);
+			else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+			reg++;
+		}
+		PRINTM(MSG, "%s\n", buf);
+	}
+}
+
+module_param(fw_name, charp, 0);
+MODULE_PARM_DESC(fw_name, "Firmware name");
+module_param(bt_req_fw_nowait, int, 0);
+MODULE_PARM_DESC(bt_req_fw_nowait,
+		 "0: Use request_firmware API; 1: Use request_firmware_nowait API");
+module_param(multi_fn, int, 0);
+MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;");
+
+module_param(bt_intmode, int, 0);
+MODULE_PARM_DESC(bt_intmode, "0: INT_MODE_SDIO, 1: INT_MODE_GPIO");
diff --git a/bt_sd8987/bt_char/hci_wrapper.h b/bt_sd8987/bt_char/hci_wrapper.h
new file mode 100644
index 0000000..a780b69
--- /dev/null
+++ b/bt_sd8987/bt_char/hci_wrapper.h
@@ -0,0 +1,173 @@
+/** @file hci_wrapper.h
+ *  @brief This file contains HCI related definitions
+ *
+ *  Copyright (C) 2011-2019, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _HCI_WRAPPER_H_
+#define _HCI_WRAPPER_H_
+
+#include <linux/module.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+/**  Define Seq num */
+#define BT_SEQ      0
+#define DEBUG_SEQ   3
+
+/** Define dev type */
+#define BT_TYPE     1
+#define BT_AMP_TYPE 2
+#define DEBUG_TYPE  5
+
+/** Define spec type */
+#define BLUEZ_SPEC     1
+#define IANYWHERE_SPEC 2
+#define GENERIC_SPEC   3
+
+/** Define lock/unlock wrapper */
+#define mdev_req_lock(d)		down(&d->req_lock)
+#define mdev_req_unlock(d)		up(&d->req_lock)
+
+/** Length of device name */
+#define DEV_NAME_LEN				32
+
+/** Define struct m_dev */
+struct m_dev {
+	char name[DEV_NAME_LEN];
+	int index;
+	unsigned long flags;
+	spinlock_t lock;
+	struct semaphore req_lock;
+	struct sk_buff_head rx_q;
+	wait_queue_head_t req_wait_q;
+	struct hci_dev_stats stat;
+	struct module *owner;
+	void *dev_pointer;
+	int dev_type;
+	int spec_type;
+	void *driver_data;
+	int wait_rx_complete;
+	int rx_complete_flag;
+	wait_queue_head_t rx_wait_q;
+	spinlock_t rxlock;
+	atomic_t extra_cnt;
+
+	struct sk_buff *evt_skb;
+	struct sk_buff *acl_skb;
+	struct sk_buff *sco_skb;
+
+	int (*open) (struct m_dev * m_dev);
+	int (*close) (struct m_dev * m_dev);
+	int (*flush) (struct m_dev * m_dev);
+	int (*send) (struct m_dev * m_dev, struct sk_buff * skb);
+	void (*destruct) (struct m_dev * m_dev);
+	void (*notify) (struct m_dev * m_dev, unsigned int evt);
+	int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, void *arg);
+	void (*query) (struct m_dev * m_dev, void *arg);
+
+};
+
+/** Define struct mbt_dev */
+struct mbt_dev {
+	/** maybe could add some private member later */
+	char name[DEV_NAME_LEN];
+	unsigned long flags;
+	__u8 type;
+
+	__u16 pkt_type;
+	__u16 esco_type;
+	__u16 link_policy;
+	__u16 link_mode;
+
+	__u32 idle_timeout;
+	__u16 sniff_min_interval;
+	__u16 sniff_max_interval;
+
+	struct sk_buff *reassembly[3];
+
+	atomic_t promisc;
+};
+
+struct debug_dev {
+	/** maybe could add some private member later */
+	char name[DEV_NAME_LEN];
+	unsigned long flags;
+};
+
+/** This function frees m_dev allocation */
+void free_m_dev(struct m_dev *m_dev);
+
+/**
+ *  @brief This function receives frames
+ *
+ *  @param skb	A pointer to struct sk_buff
+ *  @return	0--success otherwise error code
+ */
+static inline int
+mdev_recv_frame(struct sk_buff *skb)
+{
+	struct m_dev *m_dev = (struct m_dev *)skb->dev;
+	if (!m_dev || (!test_bit(HCI_UP, &m_dev->flags)
+		       && !test_bit(HCI_INIT, &m_dev->flags))) {
+		kfree_skb(skb);
+		return -ENXIO;
+	}
+
+	/* Incomming skb */
+	bt_cb(skb)->incoming = 1;
+
+	/* Time stamp */
+	__net_timestamp(skb);
+
+	/* Put type byte before the data */
+	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+
+	/* Queue frame for rx task */
+	skb_queue_tail(&m_dev->rx_q, skb);
+
+	/* Wakeup rx thread */
+	wake_up_interruptible(&m_dev->req_wait_q);
+
+	return 0;
+}
+
+/**
+ *  @brief mbt dev suspend handler
+ *
+ *  @param m_dev   A pointer to struct m_dev
+ *  @return        0
+ */
+static inline int
+mbt_hci_suspend_dev(struct m_dev *m_dev)
+{
+	return 0;
+}
+
+/**
+ *  @brief mbt dev resume handler
+ *
+ *  @param m_dev   A pointer to struct m_dev
+ *  @return        0
+ */
+static inline int
+mbt_hci_resume_dev(struct m_dev *m_dev)
+{
+	return 0;
+}
+
+#endif /* _HCI_WRAPPER_H_ */
diff --git a/bt_sd8987/bt_char/mbt_char.c b/bt_sd8987/bt_char/mbt_char.c
new file mode 100644
index 0000000..e758427
--- /dev/null
+++ b/bt_sd8987/bt_char/mbt_char.c
@@ -0,0 +1,797 @@
+/** @file mbt_char.c
+  *
+  * @brief This file contains the char device function calls
+  *
+  * Copyright (C) 2010-2019, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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/path.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+
+#include "bt_drv.h"
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#include <linux/sched/signal.h>
+#endif
+#include "mbt_char.h"
+
+#ifndef MIN
+/** Find minimum value */
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
+
+static LIST_HEAD(char_dev_list);
+
+static DEFINE_SPINLOCK(char_dev_list_lock);
+
+static int mbtchar_major = MBTCHAR_MAJOR_NUM;
+
+/**
+ *	@brief  Gets char device structure
+ *
+ *	@param dev		A pointer to char_dev
+ *
+ *	@return			kobject structure
+ */
+struct kobject *
+chardev_get(struct char_dev *dev)
+{
+	struct kobject *kobj;
+
+	kobj = bt_priv_get(dev->m_dev->driver_data);
+	if (!kobj)
+		return NULL;
+	PRINTM(INFO, "dev get kobj\n");
+	kobj = kobject_get(&dev->kobj);
+	if (!kobj)
+		bt_priv_put(dev->m_dev->driver_data);
+	return kobj;
+}
+
+/**
+ *	@brief  Prints char device structure
+ *
+ *	@param dev		A pointer to char_dev
+ *
+ *	@return			N/A
+ */
+void
+chardev_put(struct char_dev *dev)
+{
+	if (dev) {
+		struct m_dev *m_dev = dev->m_dev;
+		PRINTM(INFO, "dev put kobj\n");
+		kobject_put(&dev->kobj);
+		if (m_dev)
+			bt_priv_put(m_dev->driver_data);
+	}
+}
+
+/**
+ *	@brief Changes permissions of the dev
+ *
+ *	@param name	pointer to character
+ *	@param mode		mode_t type data
+ *	@return			0--success otherwise failure
+ */
+int
+mbtchar_chmod(char *name, mode_t mode)
+{
+	struct path path;
+	struct inode *inode;
+	struct iattr newattrs;
+	int ret;
+	int retrycount = 0;
+
+	ENTER();
+	do {
+		os_sched_timeout(30);
+		ret = kern_path(name, LOOKUP_FOLLOW, &path);
+		if (++retrycount >= 10) {
+			PRINTM(ERROR,
+			       "mbtchar_chmod(): fail to get kern_path\n");
+			LEAVE();
+			return -EFAULT;
+		}
+	} while (ret);
+	inode = path.dentry->d_inode;
+	ret = mnt_want_write(path.mnt);
+	if (ret)
+		goto out_unlock;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_lock(&inode->i_mutex);
+#else
+	inode_lock(inode);
+#endif
+	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	if (inode->i_op->setattr)
+		ret = inode->i_op->setattr(path.dentry, &newattrs);
+	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+		ret = simple_setattr(path.dentry, &newattrs);
+#else
+		ret = inode_setattr(inode, &newattrs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+
+	path_put(&path);
+	LEAVE();
+	return ret;
+out_unlock:
+	mnt_drop_write(path.mnt);
+	path_put(&path);
+	return ret;
+}
+
+/**
+ *	@brief Changes ownership of the dev
+ *
+ *	@param name	pointer to character
+ *	@param user		uid_t type data
+ *	@param group	gid_t type data
+ *	@return			0--success otherwise failure
+ */
+int
+mbtchar_chown(char *name, uid_t user, gid_t group)
+{
+	struct path path;
+	struct inode *inode = NULL;
+	struct iattr newattrs;
+	int ret = 0;
+	int retrycount = 0;
+
+	ENTER();
+	do {
+		os_sched_timeout(30);
+		ret = kern_path(name, LOOKUP_FOLLOW, &path);
+		if (++retrycount >= 10) {
+			PRINTM(ERROR,
+			       "mbtchar_chown(): fail to get kern_path\n");
+			LEAVE();
+			return -EFAULT;
+		}
+	} while (ret);
+	inode = path.dentry->d_inode;
+	ret = mnt_want_write(path.mnt);
+	if (ret)
+		goto out_unlock;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_lock(&inode->i_mutex);
+#else
+	inode_lock(inode);
+#endif
+	newattrs.ia_valid = ATTR_CTIME;
+	if (user != (uid_t) (-1)) {
+		newattrs.ia_valid |= ATTR_UID;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+		newattrs.ia_uid = user;
+#else
+		newattrs.ia_uid = KUIDT_INIT(user);
+#endif
+	}
+	if (group != (gid_t) (-1)) {
+		newattrs.ia_valid |= ATTR_GID;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+		newattrs.ia_gid = group;
+#else
+		newattrs.ia_gid = KGIDT_INIT(group);
+#endif
+	}
+	if (!S_ISDIR(inode->i_mode))
+		newattrs.ia_valid |=
+			ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+	if (inode->i_op->setattr)
+		ret = inode->i_op->setattr(path.dentry, &newattrs);
+	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+		ret = simple_setattr(path.dentry, &newattrs);
+#else
+		ret = inode_setattr(inode, &newattrs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+
+	path_put(&path);
+	LEAVE();
+	return ret;
+out_unlock:
+	mnt_drop_write(path.mnt);
+	path_put(&path);
+	return ret;
+}
+
+/**
+ *	@brief write handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param buf		pointer to char buffer
+ *	@param count	size of receive buffer
+ *	@param f_pos	pointer to loff_t type data
+ *	@return			number of bytes written
+ */
+ssize_t
+chardev_write(struct file * filp, const char *buf, size_t count, loff_t * f_pos)
+{
+	int nwrite = 0;
+	struct sk_buff *skb;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	if (!test_bit(HCI_UP, &m_dev->flags)) {
+		LEAVE();
+		return -EBUSY;
+	}
+	nwrite = count;
+	skb = bt_skb_alloc(count, GFP_ATOMIC);
+	if (!skb) {
+		PRINTM(ERROR, "mbtchar_write(): fail to alloc skb\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+
+	if (copy_from_user((void *)skb_put(skb, count), buf, count)) {
+		PRINTM(ERROR, "mbtchar_write(): cp_from_user failed\n");
+		kfree_skb(skb);
+		nwrite = -EFAULT;
+		goto exit;
+	}
+
+	skb->dev = (void *)m_dev;
+	bt_cb(skb)->pkt_type = *((unsigned char *)skb->data);
+	skb_pull(skb, 1);
+
+	PRINTM(DATA, "Write: pkt_type: 0x%x, len=%d @%lu\n",
+	       bt_cb(skb)->pkt_type, skb->len, jiffies);
+	DBG_HEXDUMP(DAT_D, "chardev_write", skb->data, skb->len);
+
+	/* Send skb to the hci wrapper layer */
+	if (m_dev->send(m_dev, skb)) {
+		PRINTM(ERROR, "Write: Fail\n");
+		nwrite = 0;
+		/* Send failed */
+		kfree_skb(skb);
+	}
+exit:
+	LEAVE();
+	return nwrite;
+}
+
+/**
+ *	@brief read handler for BT char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param buf		pointer to char buffer
+ *	@param count	size of receive buffer
+ *	@param f_pos	pointer to loff_t type data
+ *	@return			number of bytes read
+ */
+ssize_t
+chardev_read(struct file * filp, char *buf, size_t count, loff_t * f_pos)
+{
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	struct sk_buff *skb = NULL;
+
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	/* Wait for rx data */
+	add_wait_queue(&m_dev->req_wait_q, &wait);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		skb = skb_dequeue(&m_dev->rx_q);
+		if (skb)
+			break;
+		if (!test_bit(HCI_UP, &m_dev->flags)) {
+			ret = -EBUSY;
+			break;
+		}
+
+		if (filp->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			break;
+		}
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&m_dev->req_wait_q, &wait);
+
+	if (!skb)
+		goto out;
+
+	PRINTM(DATA, "BT: chardev_read count=%d pkt_type: 0x%x, len=%d %p\n",
+		(int)count, bt_cb(skb)->pkt_type, skb->len, skb);
+	DBG_HEXDUMP(DAT_D, "chardev_read", skb->data,MIN((int)count,skb->len));
+	if (skb->len > count) {
+		/* user data length is smaller than the skb length */
+		if (copy_to_user(buf, skb->data, count)) {
+			ret = -EFAULT;
+			goto outf;
+		}
+		skb_pull(skb, count);
+		skb_queue_head(&m_dev->rx_q, skb);
+		wake_up_interruptible(&m_dev->req_wait_q);
+		ret = count;
+		goto out;
+	} else {
+		if (copy_to_user(buf, skb->data, skb->len)) {
+			ret = -EFAULT;
+			goto outf;
+		}
+		ret = skb->len;
+		PRINTM(DATA, "BT: chardev_read complete %p\n",skb);
+	}
+outf:
+	kfree_skb(skb);
+out:
+	if (m_dev->wait_rx_complete && skb_queue_empty(&m_dev->rx_q)) {
+		m_dev->rx_complete_flag = TRUE;
+		wake_up_interruptible(&m_dev->rx_wait_q);
+	}
+	LEAVE();
+	return ret;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief ioctl common handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+char_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg)
+#else
+/**
+ *	@brief ioctl common handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+char_ioctl(struct file *filp, unsigned int cmd, void *arg)
+#endif
+{
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	PRINTM(INFO, "IOCTL: cmd=%d\n", cmd);
+	switch (cmd) {
+	case MBTCHAR_IOCTL_RELEASE:
+		m_dev->close(m_dev);
+		break;
+	case MBTCHAR_IOCTL_QUERY_TYPE:
+		m_dev->query(m_dev, arg);
+		break;
+	default:
+		m_dev->ioctl(m_dev, cmd, arg);
+		break;
+	}
+	LEAVE();
+	return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief ioctl handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_ioctl(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+#else
+/**
+ *	@brief ioctl handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	return char_ioctl(inode, filp, cmd, (void *)arg);
+#else
+	return char_ioctl(filp, cmd, (void *)arg);
+#endif
+}
+
+#ifdef CONFIG_COMPAT
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief compat ioctl handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_ioctl_compat(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg)
+#else
+/**
+ *	@brief compat ioctl handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+chardev_ioctl_compat(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	return char_ioctl(inode, filp, cmd, compat_ptr(arg));
+#else
+	return char_ioctl(filp, cmd, compat_ptr(arg));
+#endif
+}
+#endif /* CONFIG_COMPAT */
+
+/**
+ *	@brief open handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct char_dev *dev = NULL;
+	struct m_dev *m_dev = NULL;
+	struct char_dev *cdev = NULL;
+	struct list_head *p = NULL;
+	ENTER();
+
+	list_for_each(p, &char_dev_list) {
+		cdev = list_entry(p, struct char_dev, list);
+		if (mbtchar_major == MAJOR(inode->i_cdev->dev) &&
+		    cdev->minor == MINOR(inode->i_cdev->dev)) {
+			dev = cdev;
+			break;
+		}
+	}
+	if (!dev) {
+		PRINTM(ERROR, "cannot find dev from inode\n");
+		LEAVE();
+		return -ENXIO;
+	}
+	if (!chardev_get(dev)) {
+		LEAVE();
+		return -ENXIO;
+	}
+	filp->private_data = dev;	/* for other methods */
+	m_dev = dev->m_dev;
+	mdev_req_lock(m_dev);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
+	if (test_bit(HCI_UP, &m_dev->flags)) {
+		atomic_inc(&m_dev->extra_cnt);
+		goto done;
+	}
+#endif
+	if (m_dev->open(m_dev)) {
+		ret = -EIO;
+		goto done;
+	}
+	set_bit(HCI_UP, &m_dev->flags);
+
+done:
+	mdev_req_unlock(m_dev);
+	if (ret)
+		chardev_put(dev);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief release handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_release(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	ENTER();
+	if (!dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
+	if (m_dev && (atomic_dec_if_positive(&m_dev->extra_cnt) >= 0)) {
+		LEAVE();
+		return ret;
+	}
+#endif
+	if (m_dev)
+		ret = dev->m_dev->close(dev->m_dev);
+	filp->private_data = NULL;
+	chardev_put(dev);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief poll handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param wait		pointer to poll_table structure
+ *	@return			mask
+ */
+static unsigned int
+chardev_poll(struct file *filp, poll_table * wait)
+{
+	unsigned int mask;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+
+	m_dev = dev->m_dev;
+	poll_wait(filp, &m_dev->req_wait_q, wait);
+	mask = POLLOUT | POLLWRNORM;
+	if (skb_peek(&m_dev->rx_q))
+		mask |= POLLIN | POLLRDNORM;
+	if (!test_bit(HCI_UP, &(m_dev->flags)))
+		mask |= POLLHUP;
+	PRINTM(INFO, "poll mask=0x%x\n", mask);
+	LEAVE();
+	return mask;
+}
+
+/* File ops for the Char driver */
+const struct file_operations chardev_fops = {
+	.owner = THIS_MODULE,
+	.read = chardev_read,
+	.write = chardev_write,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	.ioctl = chardev_ioctl,
+#else
+	.unlocked_ioctl = chardev_ioctl,
+#endif
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = chardev_ioctl_compat,
+#endif
+	.open = chardev_open,
+	.release = chardev_release,
+	.poll = chardev_poll,
+};
+
+/**
+ *	@brief This function creates the char dev
+ *
+ *	@param dev			A pointer to structure char_dev
+ *  @param char_class	A pointer to class struct
+ *  @param mod_name		A pointer to char
+ *  @param dev_name		A pointer to char
+ *	@return				0--success otherwise failure
+ */
+int
+register_char_dev(struct char_dev *dev, struct class *char_class,
+		  char *mod_name, char *dev_name)
+{
+	int ret = 0, dev_num;
+	unsigned long flags;
+	ENTER();
+	/* create the chrdev region */
+	if (mbtchar_major) {
+		dev_num = MKDEV(mbtchar_major, dev->minor);
+		ret = register_chrdev_region(dev_num, 1, mod_name);
+	} else {
+		PRINTM(INFO, "chardev: no major # yet\n");
+		ret = alloc_chrdev_region((dev_t *) & dev_num, dev->minor, 1,
+					  mod_name);
+	}
+
+	if (ret) {
+		PRINTM(ERROR, "chardev: create chrdev_region failed\n");
+		LEAVE();
+		return ret;
+	}
+	if (!mbtchar_major) {
+		/* Store the allocated dev major # */
+		mbtchar_major = MAJOR(dev_num);
+	}
+	dev->cdev = cdev_alloc();
+	dev->cdev->ops = &chardev_fops;
+	dev->cdev->owner = chardev_fops.owner;
+	dev_num = MKDEV(mbtchar_major, dev->minor);
+
+	if (cdev_add(dev->cdev, dev_num, 1)) {
+		PRINTM(ERROR, "chardev: cdev_add failed\n");
+		ret = -EFAULT;
+		goto free_cdev_region;
+	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+	if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), NULL, dev_name);
+	}
+	if (dev->dev_type == DEBUG_TYPE) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), NULL, dev_name);
+	}
+#else
+	if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), dev_name);
+	}
+	if (dev->dev_type == DEBUG_TYPE) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), dev_name);
+	}
+#endif
+	PRINTM(INFO, "register char dev=%s\n", dev_name);
+
+	/** modify later */
+
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	list_add_tail(&dev->list, &char_dev_list);
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+
+	LEAVE();
+	return ret;
+free_cdev_region:
+	unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief This function deletes the char dev
+ *
+ *  @param dev			A pointer to structure char_dev
+ *  @param char_class	A pointer to class struct
+ *  @param dev_name		A pointer to char
+ *  @return				0--success otherwise failure
+ */
+int
+unregister_char_dev(struct char_dev *dev, struct class *char_class,
+		    char *dev_name)
+{
+	ENTER();
+	device_destroy(char_class, MKDEV(mbtchar_major, dev->minor));
+	cdev_del(dev->cdev);
+	unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1);
+	PRINTM(INFO, "unregister char dev=%s\n", dev_name);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *	@brief This function cleans module
+ *
+ *  @param char_class	A pointer to class struct
+ *  @return				N/A
+ */
+void
+chardev_cleanup(struct class *char_class)
+{
+	unsigned long flags;
+	struct list_head *p = NULL;
+	struct char_dev *dev = NULL;
+	ENTER();
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	do {
+		dev = NULL;
+		list_for_each(p, &char_dev_list) {
+			dev = list_entry(p, struct char_dev, list);
+			list_del(p);
+			spin_unlock_irqrestore(&char_dev_list_lock, flags);
+			unregister_char_dev(dev, char_class, dev->m_dev->name);
+			kobject_put(&dev->kobj);
+			spin_lock_irqsave(&char_dev_list_lock, flags);
+			break;
+		}
+	} while (dev);
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+	class_destroy(char_class);
+	LEAVE();
+}
+
+/**
+ *	@brief This function cleans module
+ *
+ *  @param m_dev	A pointer to m_dev struct
+ *  @param char_class	A pointer to class struct
+ *  @return			N/A
+ */
+void
+chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class)
+{
+	unsigned long flags;
+	struct list_head *p = NULL;
+	struct char_dev *dev = NULL;
+	ENTER();
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	list_for_each(p, &char_dev_list) {
+		dev = list_entry(p, struct char_dev, list);
+		if (dev->minor == m_dev->index) {
+			list_del(p);
+			spin_unlock_irqrestore(&char_dev_list_lock, flags);
+			dev->m_dev = NULL;
+			unregister_char_dev(dev, char_class, m_dev->name);
+			kobject_put(&dev->kobj);
+			spin_lock_irqsave(&char_dev_list_lock, flags);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+	LEAVE();
+}
diff --git a/bt_sd8987/bt_char/mbt_char.h b/bt_sd8987/bt_char/mbt_char.h
new file mode 100644
index 0000000..6b34088
--- /dev/null
+++ b/bt_sd8987/bt_char/mbt_char.h
@@ -0,0 +1,73 @@
+/** @file mbt_char.h
+  *
+  * @brief This file contains mbtchar driver specific defines etc
+  *
+  * Copyright (C) 2010-2019, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+#ifndef __MBT_CHAR_H__
+#define __MBT_CHAR_H__
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+/** Define ioctl */
+#define MBTCHAR_IOCTL_RELEASE       _IO('M', 1)
+#define MBTCHAR_IOCTL_QUERY_TYPE    _IO('M', 2)
+#ifdef BLE_WAKEUP
+#define MBTCHAR_IOCTL_BLE_WAKEUP_PARAM _IO('M', 4)
+#define MBTCHAR_IOCTL_BLE_GET_WHITELIST _IO('M', 5)
+#endif
+
+#define MBTCHAR_MAJOR_NUM            (0)
+
+/** Interface specific macros */
+#define MBTCHAR_MINOR_BASE           (0)
+#define FMCHAR_MINOR_BASE            (10)
+#define NFCCHAR_MINOR_BASE           (20)
+#define DEBUGCHAR_MINOR_BASE         (30)
+
+/** Declaration of char_dev struct */
+struct char_dev {
+	struct list_head list;
+	int minor;
+	int dev_type;
+	struct cdev *cdev;
+	struct m_dev *m_dev;
+	struct kobject kobj;
+};
+
+/** Changes permissions of the dev */
+int mbtchar_chmod(char *name, mode_t mode);
+
+/** Changes ownership of the dev */
+int mbtchar_chown(char *name, uid_t user, gid_t group);
+
+/**	This function creates the char dev */
+int register_char_dev(struct char_dev *dev, struct class *char_class,
+		      char *mod_name, char *dev_name);
+
+/**	This function deletes the char dev */
+int unregister_char_dev(struct char_dev *dev, struct class *char_class,
+			char *dev_name);
+
+/**	This function cleans module */
+void chardev_cleanup(struct class *char_class);
+
+/**	This function cleans module */
+void chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class);
+
+#endif /*__MBT_CHAR_H__*/
diff --git a/bt_sd8997/Makefile b/bt_sd8997/Makefile
new file mode 100644
index 0000000..ada84eb
--- /dev/null
+++ b/bt_sd8997/Makefile
@@ -0,0 +1,194 @@
+# File: Makefile
+# Copyright (C) 2007-2018, Marvell International Ltd.
+#
+
+CC=		$(CROSS_COMPILE)gcc
+LD=		$(CROSS_COMPILE)ld
+
+BACKUP=		/root/backup
+YMD=		`date +%Y%m%d%H%M`
+
+#############################################################################
+# Configuration Options
+#############################################################################
+
+# Debug Option
+# DEBUG LEVEL n/1/2:
+# n: NO DEBUG
+# 1: PRINTM(MSG,...), PRINTM(FATAL,...), PRINTM(WARN,...) and PRINTM(INFO,...)
+# 2: All PRINTM()
+CONFIG_DEBUG=1
+
+
+# SDIO suspend/resume
+CONFIG_SDIO_SUSPEND_RESUME=y
+
+
+CONFIG_BLE_WAKEUP=y
+
+#############################################################################
+# Select Platform Tools
+#############################################################################
+
+MODEXT = ko
+
+ifeq ($(CONFIG_64BIT), y)
+	EXTRA_CFLAGS += -DMBT_64BIT
+endif
+
+ifeq ($(CONFIG_T50), y)
+        EXTRA_CFLAGS += -DT50
+        EXTRA_CFLAGS += -DT40
+        EXTRA_CFLAGS += -DT3T
+endif
+
+ifeq ($(CONFIG_BLE_WAKEUP), y)
+        EXTRA_CFLAGS += -DBLE_WAKEUP
+endif
+
+
+
+
+KERNELDIR ?= /usr/src/arm/linux-4.9.61-bg5ct
+CROSS_COMPILE ?= /usr/local/gcc-linaro-5.3-2016.02-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+
+
+KERNELVERSION_X86 := 	$(shell uname -r)
+KERNELDIR?=/lib/modules/$(KERNELVERSION_X86)/build
+
+EXTRA_CFLAGS += -I$(KERNELDIR)/include
+
+EXTRA_CFLAGS += -I$(M)/../mbtchar_src
+EXTRA_CFLAGS += -I$(M)/bt
+LD += -S
+
+#ifdef SD8xxx
+BINDIR = ../bin_sd8xxx_btchar
+#endif
+BINDIR = ../bin_sd8997_btchar
+
+
+#############################################################################
+# Compiler Flags
+#############################################################################
+	EXTRA_CFLAGS += -DFPNUM='"36"'
+
+ifeq ($(CONFIG_DEBUG),1)
+	EXTRA_CFLAGS += -DDEBUG_LEVEL1
+endif
+
+ifeq ($(CONFIG_DEBUG),2)
+	EXTRA_CFLAGS += -DDEBUG_LEVEL1
+	EXTRA_CFLAGS += -DDEBUG_LEVEL2
+	DBG=	-dbg
+endif
+
+ifeq ($(CONFIG_SDIO_SUSPEND_RESUME),y)
+	EXTRA_CFLAGS += -DSDIO_SUSPEND_RESUME
+endif
+
+#############################################################################
+# Make Targets
+#############################################################################
+
+BT_CHAR_OBJS = bt_char/bt_main.o bt_char/bt_sdiommc.o bt_char/bt_proc.o bt_char/mbt_char.o
+BT_CHAR_OBJS += bt_char/bt_init.o
+
+BT_BLOCK_OBJS = bt/bt_main.o bt/bt_sdiommc.o bt/bt_proc.o bt/mbt_char.o
+BT_BLOCK_OBJS += bt/bt_init.o
+
+ifneq ($(KERNELRELEASE),)
+
+ifeq ($(CONFIG_BERLIN_SDIO_BT_8997_CHAR_DRV),y)
+BTOBJS = $(BT_CHAR_OBJS)
+MODULE_LINK = y
+endif
+ifeq ($(CONFIG_BERLIN_SDIO_BT_8997_CHAR_DRV),m)
+BTOBJS = $(BT_CHAR_OBJS)
+MODULE_LINK = m
+endif
+ifeq ($(CONFIG_BERLIN_SDIO_BT_8997),y)
+BTOBJS = $(BT_BLOCK_OBJS)
+MODULE_LINK = y
+endif
+ifeq ($(CONFIG_BERLIN_SDIO_BT_8997),m)
+BTOBJS = $(BT_BLOCK_OBJS)
+MODULE_LINK = m
+endif
+
+obj-$(MODULE_LINK) := bt8xxx.o
+bt8xxx-objs := $(BTOBJS)
+
+
+
+# Otherwise we were called directly from the command line; invoke the kernel build system.
+else
+default:
+	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules
+endif
+
+###############################################################
+
+export		CC LD EXTRA_CFLAGS KERNELDIR
+
+.PHONY: app/fm_app clean distclean
+
+app/fm_app:
+	$(MAKE) -C  $@
+
+echo:
+
+build:		echo default
+
+	@if [ ! -d $(BINDIR) ]; then \
+		mkdir $(BINDIR); \
+	fi
+
+ifeq ($(CONFIG_MULTI_INTERFACE), y)
+	cp -f mbt8xxx_sdio.$(MODEXT) $(BINDIR)/mbt8997_sdio$(DBG).$(MODEXT)
+else
+	cp -f mbt8xxx.$(MODEXT) $(BINDIR)/mbt8997$(DBG).$(MODEXT)
+endif
+	cp -r config $(BINDIR)
+
+
+
+
+
+	cp -f README $(BINDIR)
+
+	$(MAKE) -C app/fm_app $@ INSTALLDIR=$(BINDIR);
+	cp -f app/fm_app/fmapp $(BINDIR);
+
+clean:
+	-find . -name "*.o" -exec rm {} \;
+	-find . -name "*.ko" -exec rm {} \;
+	-find . -name ".*.cmd" -exec rm {} \;
+	-find . -name "*.mod.c" -exec rm {} \;
+	-find . -name "*.symvers" -exec rm {} \;
+	-find . -name "modules.order" -exec rm {} \;
+	-find . -name ".*.dwo" -exec rm {} \;
+	-find . -name "*dwo" -exec rm {} \;
+	-rm -rf .tmp_versions
+	$(MAKE) -C app/fm_app $@
+
+install: default
+
+distclean:
+	-find . -name "*.o" -exec rm {} \;
+	-find . -name "*.orig" -exec rm {} \;
+	-find . -name "*.swp" -exec rm {} \;
+	-find . -name "*.*~" -exec rm {} \;
+	-find . -name "*~" -exec rm {} \;
+	-find . -name "*.d" -exec rm {} \;
+	-find . -name "*.a" -exec rm {} \;
+	-find . -name "tags" -exec rm {} \;
+	-find . -name ".*" -exec rm -rf 2> /dev/null \;
+	-find . -name "*.ko" -exec rm {} \;
+	-find . -name ".*.cmd" -exec rm {} \;
+	-find . -name "*.mod.c" -exec rm {} \;
+	-find . -name ".*.dwo" -exec rm {} \;
+	-find . -name "*dwo" -exec rm {} \;
+	-rm -rf .tmp_versions
+	$(MAKE) -C app/fm_app $@
+# End of file;
diff --git a/bt_sd8997/bt/bt_drv.h b/bt_sd8997/bt/bt_drv.h
new file mode 100644
index 0000000..3a75eb0
--- /dev/null
+++ b/bt_sd8997/bt/bt_drv.h
@@ -0,0 +1,858 @@
+/** @file bt_drv.h
+ *  @brief This header file contains global constant/enum definitions,
+ *  global variable declaration.
+ *
+ *  Copyright (C) 2007-2017, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _BT_DRV_H_
+#define _BT_DRV_H_
+
+#include <linux/version.h>
+#include <linux/kthread.h>
+#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
+
+#include "hci_wrapper.h"
+
+#ifndef BIT
+/** BIT definition */
+#define BIT(x) (1UL << (x))
+#endif
+
+#ifdef MBT_64BIT
+typedef u64 t_ptr;
+#else
+typedef u32 t_ptr;
+#endif
+
+/** Define drv_mode bit */
+#define DRV_MODE_BT         BIT(0)
+
+/** Define devFeature bit */
+#define DEV_FEATURE_BT     BIT(0)
+#define DEV_FEATURE_BTAMP     BIT(1)
+#define DEV_FEATURE_BLE     BIT(2)
+
+/** Define maximum number of radio func supported */
+#define MAX_RADIO_FUNC     3
+
+/** MAC address print format */
+#ifndef MACSTR
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+#endif
+
+/** MAC address print arguments */
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#endif
+
+/** Debug level : Message */
+#define	DBG_MSG			BIT(0)
+/** Debug level : Fatal */
+#define DBG_FATAL		BIT(1)
+/** Debug level : Error */
+#define DBG_ERROR		BIT(2)
+/** Debug level : Data */
+#define DBG_DATA		BIT(3)
+/** Debug level : Command */
+#define DBG_CMD			BIT(4)
+/** Debug level : Event */
+#define DBG_EVENT		BIT(5)
+/** Debug level : Interrupt */
+#define DBG_INTR		BIT(6)
+
+/** Debug entry : Data dump */
+#define DBG_DAT_D		BIT(16)
+/** Debug entry : Data dump */
+#define DBG_CMD_D		BIT(17)
+
+/** Debug level : Entry */
+#define DBG_ENTRY		BIT(28)
+/** Debug level : Warning */
+#define DBG_WARN		BIT(29)
+/** Debug level : Informative */
+#define DBG_INFO		BIT(30)
+
+#ifdef	DEBUG_LEVEL1
+extern u32 mbt_drvdbg;
+
+#ifdef	DEBUG_LEVEL2
+/** Print informative message */
+#define	PRINTM_INFO(msg...)  \
+	do {if (mbt_drvdbg & DBG_INFO)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print warning message */
+#define	PRINTM_WARN(msg...) \
+	do {if (mbt_drvdbg & DBG_WARN)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print entry message */
+#define	PRINTM_ENTRY(msg...) \
+	do {if (mbt_drvdbg & DBG_ENTRY) \
+		printk(KERN_DEBUG msg); } while (0)
+#else
+/** Print informative message */
+#define	PRINTM_INFO(msg...)  do {} while (0)
+/** Print warning message */
+#define	PRINTM_WARN(msg...)  do {} while (0)
+/** Print entry message */
+#define	PRINTM_ENTRY(msg...) do {} while (0)
+#endif /* DEBUG_LEVEL2 */
+
+/** Print interrupt message */
+#define	PRINTM_INTR(msg...)  \
+	do {if (mbt_drvdbg & DBG_INTR)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print event message */
+#define	PRINTM_EVENT(msg...) \
+	do {if (mbt_drvdbg & DBG_EVENT) \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print command message */
+#define	PRINTM_CMD(msg...)   \
+	do {if (mbt_drvdbg & DBG_CMD)   \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print data message */
+#define	PRINTM_DATA(msg...)  \
+	do {if (mbt_drvdbg & DBG_DATA)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print error message */
+#define	PRINTM_ERROR(msg...) \
+	do {if (mbt_drvdbg & DBG_ERROR) \
+		printk(KERN_ERR msg); } while (0)
+/** Print fatal message */
+#define	PRINTM_FATAL(msg...) \
+	do {if (mbt_drvdbg & DBG_FATAL) \
+		printk(KERN_ERR msg); } while (0)
+/** Print message */
+#define	PRINTM_MSG(msg...)   \
+	do {if (mbt_drvdbg & DBG_MSG)   \
+		printk(KERN_ALERT msg); } while (0)
+
+/** Print data dump message */
+#define	PRINTM_DAT_D(msg...)  \
+	do {if (mbt_drvdbg & DBG_DAT_D)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print data dump message */
+#define	PRINTM_CMD_D(msg...)  \
+	do {if (mbt_drvdbg & DBG_CMD_D)  \
+		printk(KERN_DEBUG msg); } while (0)
+
+/** Print message with required level */
+#define	PRINTM(level, msg...) PRINTM_##level(msg)
+
+/** Debug dump buffer length */
+#define DBG_DUMP_BUF_LEN	64
+/** Maximum number of dump per line */
+#define MAX_DUMP_PER_LINE	16
+/** Maximum data dump length */
+#define MAX_DATA_DUMP_LEN	48
+
+static inline void
+hexdump(char *prompt, u8 *buf, int len)
+{
+	int i;
+	char dbgdumpbuf[DBG_DUMP_BUF_LEN];
+	char *ptr = dbgdumpbuf;
+
+	printk(KERN_DEBUG "%s: len=%d\n", prompt, len);
+	for (i = 1; i <= len; i++) {
+		ptr += snprintf(ptr, 4, "%02x ", *buf);
+		buf++;
+		if (i % MAX_DUMP_PER_LINE == 0) {
+			*ptr = 0;
+			printk(KERN_DEBUG "%s\n", dbgdumpbuf);
+			ptr = dbgdumpbuf;
+		}
+	}
+	if (len % MAX_DUMP_PER_LINE) {
+		*ptr = 0;
+		printk(KERN_DEBUG "%s\n", dbgdumpbuf);
+	}
+}
+
+/** Debug hexdump of debug data */
+#define DBG_HEXDUMP_DAT_D(x, y, z) \
+	do {if (mbt_drvdbg & DBG_DAT_D) \
+		hexdump(x, y, z); } while (0)
+/** Debug hexdump of debug command */
+#define DBG_HEXDUMP_CMD_D(x, y, z) \
+	do {if (mbt_drvdbg & DBG_CMD_D) \
+		hexdump(x, y, z); } while (0)
+
+/** Debug hexdump */
+#define	DBG_HEXDUMP(level, x, y, z)    DBG_HEXDUMP_##level(x, y, z)
+
+/** Mark entry point */
+#define	ENTER()			PRINTM(ENTRY, "Enter: %s, %s:%i\n", __func__, \
+							__FILE__, __LINE__)
+/** Mark exit point */
+#define	LEAVE()			PRINTM(ENTRY, "Leave: %s, %s:%i\n", __func__, \
+							__FILE__, __LINE__)
+#else
+/** Do nothing */
+#define	PRINTM(level, msg...) do {} while (0)
+/** Do nothing */
+#define DBG_HEXDUMP(level, x, y, z)    do {} while (0)
+/** Do nothing */
+#define	ENTER()  do {} while (0)
+/** Do nothing */
+#define	LEAVE()  do {} while (0)
+#endif /* DEBUG_LEVEL1 */
+
+/** Bluetooth upload size */
+#define	BT_UPLD_SIZE				2312
+/** Bluetooth status success */
+#define BT_STATUS_SUCCESS			(0)
+/** Bluetooth status failure */
+#define BT_STATUS_FAILURE			(-1)
+
+#ifndef	TRUE
+/** True value */
+#define TRUE			1
+#endif
+#ifndef	FALSE
+/** False value */
+#define	FALSE			0
+#endif
+
+/** Set thread state */
+#define OS_SET_THREAD_STATE(x)		set_current_state(x)
+/** Time to wait until Host Sleep state change in millisecond */
+#define WAIT_UNTIL_HS_STATE_CHANGED 2000
+/** Time to wait cmd resp in millisecond */
+#define WAIT_UNTIL_CMD_RESP	    5000
+
+/** Sleep until a condition gets true or a timeout elapses */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+#define os_wait_interruptible_timeout(waitq, cond, timeout) \
+	interruptible_sleep_on_timeout(&waitq, ((timeout) * HZ / 1000))
+#else
+#define os_wait_interruptible_timeout(waitq, cond, timeout) \
+	wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000))
+#endif
+
+typedef struct {
+	/** Task */
+	struct task_struct *task;
+	/** Queue */
+	wait_queue_head_t waitQ;
+	/** PID */
+	pid_t pid;
+	/** Private structure */
+	void *priv;
+} bt_thread;
+
+static inline void
+bt_activate_thread(bt_thread *thr)
+{
+	/** Initialize the wait queue */
+	init_waitqueue_head(&thr->waitQ);
+
+	/** Record the thread pid */
+	thr->pid = current->pid;
+}
+
+static inline void
+bt_deactivate_thread(bt_thread *thr)
+{
+	thr->pid = 0;
+	return;
+}
+
+static inline void
+bt_create_thread(int (*btfunc) (void *), bt_thread *thr, char *name)
+{
+	thr->task = kthread_run(btfunc, thr, "%s", name);
+}
+
+static inline int
+bt_terminate_thread(bt_thread *thr)
+{
+	/* Check if the thread is active or not */
+	if (!thr->pid)
+		return -1;
+
+	kthread_stop(thr->task);
+	return 0;
+}
+
+static inline void
+os_sched_timeout(u32 millisec)
+{
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	schedule_timeout((millisec * HZ) / 1000);
+}
+
+#ifndef __ATTRIB_ALIGN__
+#define __ATTRIB_ALIGN__ __attribute__((aligned(4)))
+#endif
+
+#ifndef __ATTRIB_PACK__
+#define __ATTRIB_PACK__ __attribute__((packed))
+#endif
+
+/** BT histogram command */
+#define BT_CMD_HISTOGRAM            0xEA
+/** max antenna num */
+#define MAX_ANTENNA_NUM             2
+/** BDR 1M */
+#define BDR_RATE_1M					1
+/** EDR 2/3 M */
+#define EDR_RATE_2_3M			    2
+/** BLE 1M */
+#define BLE_RATE_1M                 5
+/** max bt link number */
+#define MAX_BT_LINK                 10
+/** max ble link number */
+#define MAX_BLE_LINK                16
+
+typedef struct _bt_link_stat {
+    /** txrx rate 1: BDR_1M, 2:EDR 2/3 M, 3:BLE 1M */
+	u8 txrxrate;
+    /** power: -30 = N = 20 dbm*/
+	s8 txpower;
+    /** rssi: -127 to +20 (For BT), -128 to +127 (For BLE) */
+	s8 rssi;
+} __ATTRIB_PACK__ bt_link_stat;
+
+typedef struct _bt_histogram_data {
+	u8 antenna;
+	u8 powerclass;
+	bt_link_stat link[MAX_BT_LINK + MAX_BLE_LINK];
+} __ATTRIB_PACK__ bt_histogram_data;
+
+typedef struct _bt_hist_proc_data {
+    /** antenna */
+	u8 antenna;
+	/** Private structure */
+	struct _bt_private *pbt;
+} bt_hist_proc_data;
+
+/** Data structure for the Marvell Bluetooth device */
+typedef struct _bt_dev {
+	/** device name */
+	char name[DEV_NAME_LEN];
+	/** card pointer */
+	void *card;
+	/** IO port */
+	u32 ioport;
+
+	struct m_dev m_dev[MAX_RADIO_FUNC];
+
+	/** Tx download ready flag */
+	u8 tx_dnld_rdy;
+	/** Function */
+	u8 fn;
+	/** Rx unit */
+	u8 rx_unit;
+	/** Power Save mode : Timeout configuration */
+	u16 idle_timeout;
+	/** Power Save mode */
+	u8 psmode;
+	/** Power Save command */
+	u8 pscmd;
+	/** Host Sleep mode */
+	u8 hsmode;
+	/** Host Sleep command */
+	u8 hscmd;
+	/** Low byte is gap, high byte is GPIO */
+	u16 gpio_gap;
+	/** Host Sleep configuration command */
+	u8 hscfgcmd;
+	/** Host Send Cmd Flag		 */
+	u8 sendcmdflag;
+	/** opcode for Send Cmd */
+	u16 send_cmd_opcode;
+	/** Device Type			*/
+	u8 devType;
+	/** Device Features    */
+	u8 devFeature;
+	/** cmd52 function */
+	u8 cmd52_func;
+	/** cmd52 register */
+	u8 cmd52_reg;
+	/** cmd52 value */
+	u8 cmd52_val;
+	/** SDIO pull control command */
+	u8 sdio_pull_ctrl;
+	/** Low 2 bytes is pullUp, high 2 bytes for pull-down */
+	u32 sdio_pull_cfg;
+	/** Test mode command */
+	u8 test_mode;
+} bt_dev_t, *pbt_dev_t;
+
+typedef struct _bt_adapter {
+	/** Chip revision ID */
+	u8 chip_rev;
+    /** magic val */
+	u8 magic_val;
+	/** Surprise removed flag */
+	u8 SurpriseRemoved;
+	/** IRQ number */
+	int irq;
+	/** Interrupt counter */
+	u32 IntCounter;
+	/** Tx packet queue */
+	struct sk_buff_head tx_queue;
+	/** Pending Tx packet queue */
+	struct sk_buff_head pending_queue;
+	/** tx lock flag */
+	u8 tx_lock;
+	/** Power Save mode */
+	u8 psmode;
+	/** Power Save state */
+	u8 ps_state;
+	/** Host Sleep state */
+	u8 hs_state;
+	/** hs skip count */
+	u32 hs_skip;
+	/** suspend_fail flag */
+	u8 suspend_fail;
+	/** suspended flag */
+	u8 is_suspended;
+	/** Number of wakeup tries */
+	u8 WakeupTries;
+	/** Host Sleep wait queue */
+	wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__;
+	/** Host Cmd complet state */
+	u8 cmd_complete;
+	/** last irq recv */
+	u8 irq_recv;
+	/** last irq processed */
+	u8 irq_done;
+	/** sdio int status */
+	u8 sd_ireg;
+     /** buf allocated for transmit */
+	u8 *tx_buffer;
+    /** buf for transmit */
+	u8 *tx_buf;
+    /** buf allocated for read interrupt status */
+	u8 *hw_regs_buf;
+    /** buf for read interrupt status */
+	u8 *hw_regs;
+	/** tx pending */
+	u32 skb_pending;
+/** Version string buffer length */
+#define MAX_VER_STR_LEN         128
+	/** Driver version */
+	u8 drv_ver[MAX_VER_STR_LEN];
+	/** Number of command timeout */
+	u32 num_cmd_timeout;
+} bt_adapter, *pbt_adapter;
+
+/** Length of prov name */
+#define PROC_NAME_LEN				32
+
+struct item_data {
+	/** Name */
+	char name[PROC_NAME_LEN];
+	/** Size */
+	u32 size;
+	/** Address */
+	t_ptr addr;
+	/** Offset */
+	u32 offset;
+	/** Flag */
+	u32 flag;
+};
+
+struct proc_private_data {
+	/** Name */
+	char name[PROC_NAME_LEN];
+	/** File flag */
+	u32 fileflag;
+	/** Buffer size */
+	u32 bufsize;
+	/** Number of items */
+	u32 num_items;
+	/** Item data */
+	struct item_data *pdata;
+	/** Private structure */
+	struct _bt_private *pbt;
+	/** File operations */
+	const struct file_operations *fops;
+};
+
+struct device_proc {
+	/** Proc directory entry */
+	struct proc_dir_entry *proc_entry;
+    /** proc entry for hist */
+	struct proc_dir_entry *hist_entry;
+	/** num of proc files */
+	u8 num_proc_files;
+	/** pointer to proc_private_data */
+	struct proc_private_data *pfiles;
+};
+
+/** Private structure for the MV device */
+typedef struct _bt_private {
+	/** Bluetooth device */
+	bt_dev_t bt_dev;
+	/** Adapter */
+	bt_adapter *adapter;
+	/** Firmware helper */
+	const struct firmware *fw_helper;
+	/** Firmware */
+	const struct firmware *firmware;
+	/** Init user configure file */
+	const struct firmware *init_user_cfg;
+	/** Init user configure wait queue token */
+	u16 init_user_conf_wait_flag;
+	/** Init user configure file wait queue */
+	wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__;
+	/** Firmware request start time */
+	struct timeval req_fw_time;
+	/** Hotplug device */
+	struct device *hotplug_device;
+	/** thread to service interrupts */
+	bt_thread MainThread;
+	 /** proc data */
+	struct device_proc dev_proc[MAX_RADIO_FUNC];
+	/** Driver lock */
+	spinlock_t driver_lock;
+	/** Driver lock flags */
+	ulong driver_flags;
+	/** Driver reference flags */
+	struct kobject kobj;
+	u8 fw_reload;
+	/* hist_data_len */
+	u8 hist_data_len;
+    /** hist data */
+	bt_histogram_data hist_data[MAX_ANTENNA_NUM];
+    /** hist proc data */
+	bt_hist_proc_data hist_proc[MAX_ANTENNA_NUM];
+#ifdef BLE_WAKEUP
+	u8 ble_wakeup_buf_size;
+	u8 *ble_wakeup_buf;
+#endif
+} bt_private, *pbt_private;
+
+int bt_get_histogram(bt_private *priv);
+
+/** Disable interrupt */
+#define OS_INT_DISABLE	spin_lock_irqsave(&priv->driver_lock, \
+						priv->driver_flags)
+/** Enable interrupt */
+#define	OS_INT_RESTORE	spin_unlock_irqrestore(&priv->driver_lock, \
+						priv->driver_flags)
+
+#ifndef HCI_BT_AMP
+/** BT_AMP flag for device type */
+#define  HCI_BT_AMP		0x80
+#endif
+
+/** Device type of BT */
+#define DEV_TYPE_BT		0x00
+/** Device type of AMP */
+#define DEV_TYPE_AMP		0x01
+
+/** Marvell vendor packet */
+#define MRVL_VENDOR_PKT			0xFE
+
+/** Bluetooth command : Get FW Version */
+#define BT_CMD_GET_FW_VERSION       0x0F
+/** Bluetooth command : Sleep mode */
+#define BT_CMD_AUTO_SLEEP_MODE		0x23
+/** Bluetooth command : Host Sleep configuration */
+#define BT_CMD_HOST_SLEEP_CONFIG	0x59
+/** Bluetooth command : Host Sleep enable */
+#define BT_CMD_HOST_SLEEP_ENABLE	0x5A
+/** Bluetooth command : Module Configuration request */
+#define BT_CMD_MODULE_CFG_REQ		0x5B
+
+/** Bluetooth command : PMIC Configure */
+#define BT_CMD_PMIC_CONFIGURE           0x7D
+
+/** Bluetooth command : SDIO pull up down configuration request */
+#define BT_CMD_SDIO_PULL_CFG_REQ	0x69
+/** Bluetooth command : Set Evt Filter Command */
+#define BT_CMD_SET_EVT_FILTER		0x05
+/** Bluetooth command : Enable Write Scan Command */
+#define BT_CMD_ENABLE_WRITE_SCAN	0x1A
+/** Bluetooth command : Enable Device under test mode */
+#define BT_CMD_ENABLE_DEVICE_TESTMODE	0x03
+/** Sub Command: Module Bring Up Request */
+#define MODULE_BRINGUP_REQ		0xF1
+/** Sub Command: Module Shut Down Request */
+#define MODULE_SHUTDOWN_REQ		0xF2
+/** Module already up */
+#define MODULE_CFG_RESP_ALREADY_UP      0x0c
+/** Sub Command: Host Interface Control Request */
+#define MODULE_INTERFACE_CTRL_REQ	0xF5
+
+/** Bluetooth event : Power State */
+#define BT_EVENT_POWER_STATE		0x20
+
+/** Bluetooth Power State : Enable */
+#define BT_PS_ENABLE			0x02
+/** Bluetooth Power State : Disable */
+#define BT_PS_DISABLE			0x03
+/** Bluetooth Power State : Sleep */
+#define BT_PS_SLEEP			0x01
+/** Bluetooth Power State : Awake */
+#define BT_PS_AWAKE			0x02
+
+/** Vendor OGF */
+#define VENDOR_OGF				0x3F
+/** OGF for reset */
+#define RESET_OGF		0x03
+/** Bluetooth command : Reset */
+#define BT_CMD_RESET	0x03
+
+/** Host Sleep activated */
+#define HS_ACTIVATED			0x01
+/** Host Sleep deactivated */
+#define HS_DEACTIVATED			0x00
+
+/** Power Save sleep */
+#define PS_SLEEP			0x01
+/** Power Save awake */
+#define PS_AWAKE			0x00
+
+/** bt header length */
+#define BT_HEADER_LEN			4
+
+#ifndef MAX
+/** Return maximum of two */
+#define MAX(a, b)		((a) > (b) ? (a) : (b))
+#endif
+
+/** This is for firmware specific length */
+#define EXTRA_LEN	36
+
+/** Command buffer size for Marvell driver */
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+
+/** Bluetooth Rx packet buffer size for Marvell driver */
+#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
+	(HCI_MAX_FRAME_SIZE + EXTRA_LEN)
+
+/** Buffer size to allocate */
+#define ALLOC_BUF_SIZE	(((MAX(MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
+			MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+			+ SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE)
+
+/** Request FW timeout in second */
+#define REQUEST_FW_TIMEOUT		30
+
+/** The number of times to try when polling for status bits */
+#define MAX_POLL_TRIES			100
+
+/** The number of times to try when waiting for downloaded firmware to
+    become active when multiple interface is present */
+#define MAX_MULTI_INTERFACE_POLL_TRIES  150
+
+/** The number of times to try when waiting for downloaded firmware to
+     become active. (polling the scratch register). */
+#define MAX_FIRMWARE_POLL_TRIES		100
+
+/** default idle time */
+#define DEFAULT_IDLE_TIME           1000
+
+#define BT_CMD_HEADER_SIZE    3
+
+typedef struct _BT_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** Data */
+	u8 data[128];
+} __ATTRIB_PACK__ BT_CMD;
+
+typedef struct _BT_EVENT {
+	/** Event Counter */
+	u8 EC;
+	/** Length */
+	u8 length;
+	/** Data */
+	u8 data[8];
+} BT_EVENT;
+
+#if defined(SDIO_SUSPEND_RESUME)
+#define DEF_GPIO_GAP        0xffff
+#endif
+
+#ifdef BLE_WAKEUP
+#define BD_ADDR_SIZE 6
+/** Vendor specific event */
+#define VENDOR_SPECIFIC_EVENT     0xff
+/** system suspend event */
+#define HCI_SYSTEM_SUSPEND_EVT    0x80
+/** system suspend */
+#define HCI_SYSTEM_SUSPEND        0x00
+/** system resume */
+#define HCI_SYSTEM_RESUME         0x01
+/** This function enables ble wake up pattern */
+int bt_config_ble_wakeup(bt_private *priv);
+int bt_send_system_event(bt_private *priv, u8 flag);
+#endif
+
+/** This function verify the received event pkt */
+int check_evtpkt(bt_private *priv, struct sk_buff *skb);
+
+/* Prototype of global function */
+/** This function gets the priv reference */
+struct kobject *bt_priv_get(bt_private *priv);
+/** This function release the priv reference */
+void bt_priv_put(bt_private *priv);
+/** This function adds the card */
+bt_private *bt_add_card(void *card);
+/** This function removes the card */
+int bt_remove_card(void *card);
+/** This function handles the interrupt */
+void bt_interrupt(struct m_dev *m_dev);
+
+/** This function creates proc interface directory structure */
+int bt_root_proc_init(void);
+/** This function removes proc interface directory structure */
+int bt_root_proc_remove(void);
+/** This function initializes proc entry */
+int bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq);
+/** This function removes proc interface */
+void bt_proc_remove(bt_private *priv);
+
+/** This function process the received event */
+int bt_process_event(bt_private *priv, struct sk_buff *skb);
+/** This function enables host sleep */
+int bt_enable_hs(bt_private *priv);
+/** This function used to send command to firmware */
+int bt_prepare_command(bt_private *priv);
+/** This function frees the structure of adapter */
+void bt_free_adapter(bt_private *priv);
+
+/** clean up m_devs */
+void clean_up_m_devs(bt_private *priv);
+/** bt driver call this function to register to bus driver */
+int *sbi_register(void);
+/** bt driver call this function to unregister to bus driver */
+void sbi_unregister(void);
+/** bt driver calls this function to register the device  */
+int sbi_register_dev(bt_private *priv);
+/** bt driver calls this function to unregister the device */
+int sbi_unregister_dev(bt_private *priv);
+/** This function initializes firmware */
+int sbi_download_fw(bt_private *priv);
+/** Configures hardware to quit deep sleep state */
+int sbi_wakeup_firmware(bt_private *priv);
+/** Module configuration and register device */
+int sbi_register_conf_dpc(bt_private *priv);
+
+/** This function is used to send the data/cmd to hardware */
+int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb);
+/** This function reads the current interrupt status register */
+int sbi_get_int_status(bt_private *priv);
+
+/** bt fw reload flag */
+extern int bt_fw_reload;
+/** driver initial the fw reset */
+#define FW_RELOAD_SDIO_INBAND_RESET   1
+/** out band reset trigger reset, no interface re-emulation */
+#define FW_RELOAD_NO_EMULATION  2
+/** out band reset with interface re-emulation */
+#define FW_RELOAD_WITH_EMULATION 3
+/** This function reload firmware */
+void bt_request_fw_reload(bt_private *priv, int mode);
+/** This function enables the host interrupts */
+int sd_enable_host_int(bt_private *priv);
+/** This function disables the host interrupts */
+int sd_disable_host_int(bt_private *priv);
+#define MAX_TX_BUF_SIZE     2312
+/** This function downloads firmware image to the card */
+int sd_download_firmware_w_helper(bt_private *priv);
+void bt_dump_sdio_regs(bt_private *priv);
+/* dumps the firmware to /var/ or /data/ */
+void bt_dump_firmware_info_v2(bt_private *priv);
+
+/** Max line length allowed in init config file */
+#define MAX_LINE_LEN        256
+/** Max MAC address string length allowed */
+#define MAX_MAC_ADDR_LEN    18
+/** Max register type/offset/value etc. parameter length allowed */
+#define MAX_PARAM_LEN       12
+
+/** Bluetooth command : Mac address configuration */
+#define BT_CMD_CONFIG_MAC_ADDR		0x22
+/** Bluetooth command : Write CSU register */
+#define BT_CMD_CSU_WRITE_REG		0x66
+/** Bluetooth command : Load calibrate data */
+#define BT_CMD_LOAD_CONFIG_DATA     0x61
+/** Bluetooth command : Load calibrate ext data */
+#define BT_CMD_LOAD_CONFIG_DATA_EXT     0x60
+
+/** Bluetooth command : BLE deepsleep */
+#define BT_CMD_BLE_DEEP_SLEEP       0x8b
+
+typedef struct _BT_BLE_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** deepsleep flag */
+	u8 deepsleep;
+} __ATTRIB_PACK__ BT_BLE_CMD;
+
+typedef struct _BT_CSU_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** reg type */
+	u8 type;
+	/** address */
+	u8 offset[4];
+	/** Data */
+	u8 value[2];
+} __ATTRIB_PACK__ BT_CSU_CMD;
+
+/** This function sets mac address */
+int bt_set_mac_address(bt_private *priv, u8 *mac);
+/** This function writes value to CSU registers */
+int bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value);
+/** BT set user defined init data and param */
+int bt_init_config(bt_private *priv, char *cfg_file);
+/** BT PMIC Configure command */
+int bt_pmic_configure(bt_private *priv);
+/** This function load the calibrate data */
+int bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac);
+/** This function load the calibrate ext data */
+int bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len);
+/** BT set user defined calibration data */
+int bt_cal_config(bt_private *priv, char *cfg_file, char *mac);
+/** BT set user defined calibration ext data */
+int bt_cal_config_ext(bt_private *priv, char *cfg_file);
+int bt_init_mac_address(bt_private *priv, char *mac);
+
+int bt_set_independent_reset(bt_private *priv);
+/** Bluetooth command : Independent reset */
+#define BT_CMD_INDEPENDENT_RESET     0x0D
+
+typedef struct _BT_HCI_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** cmd type */
+	u8 cmd_type;
+	/** cmd len */
+	u8 cmd_len;
+	/** Data */
+	u8 data[6];
+} __ATTRIB_PACK__ BT_HCI_CMD;
+
+#endif /* _BT_DRV_H_ */
diff --git a/bt_sd8997/bt/bt_init.c b/bt_sd8997/bt/bt_init.c
new file mode 100644
index 0000000..498c0de
--- /dev/null
+++ b/bt_sd8997/bt/bt_init.c
@@ -0,0 +1,751 @@
+/** @file bt_init.c
+  *
+  * @brief This file contains the init functions for BlueTooth
+  * driver.
+  *
+  * Copyright (C) 2011-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/string.h>
+#include <linux/firmware.h>
+
+#include "bt_drv.h"
+
+extern int bt_req_fw_nowait;
+
+#define isxdigit(c)	(('0' <= (c) && (c) <= '9') \
+			 || ('a' <= (c) && (c) <= 'f') \
+			 || ('A' <= (c) && (c) <= 'F'))
+
+#define isdigit(c)	(('0' <= (c) && (c) <= '9'))
+#define isspace(c)  (c <= ' ' && (c == ' ' || (c <= 13 && c >= 9)))
+/**
+ *  @brief Returns hex value of a give character
+ *
+ *  @param chr	Character to be converted
+ *
+ *  @return	The converted character if chr is a valid hex, else 0
+ */
+static int
+bt_hexval(char chr)
+{
+	ENTER();
+
+	if (chr >= '0' && chr <= '9')
+		return chr - '0';
+	if (chr >= 'A' && chr <= 'F')
+		return chr - 'A' + 10;
+	if (chr >= 'a' && chr <= 'f')
+		return chr - 'a' + 10;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Extension of strsep lib command. This function will also take care
+ *	   escape character
+ *
+ *  @param s         A pointer to array of chars to process
+ *  @param delim     The delimiter character to end the string
+ *  @param esc       The escape character to ignore for delimiter
+ *
+ *  @return          Pointer to the separated string if delim found, else NULL
+ */
+static char *
+bt_strsep(char **s, char delim, char esc)
+{
+	char *se = *s, *sb;
+
+	ENTER();
+
+	if (!(*s) || (*se == '\0')) {
+		LEAVE();
+		return NULL;
+	}
+
+	for (sb = *s; *sb != '\0'; ++sb) {
+		if (*sb == esc && *(sb + 1) == esc) {
+			/*
+			 * We get a esc + esc seq then keep the one esc
+			 * and chop off the other esc character
+			 */
+			memmove(sb, sb + 1, strlen(sb));
+			continue;
+		}
+		if (*sb == esc && *(sb + 1) == delim) {
+			/*
+			 * We get a delim + esc seq then keep the delim
+			 * and chop off the esc character
+			 */
+			memmove(sb, sb + 1, strlen(sb));
+			continue;
+		}
+		if (*sb == delim)
+			break;
+	}
+
+	if (*sb == '\0')
+		sb = NULL;
+	else
+		*sb++ = '\0';
+
+	*s = sb;
+
+	LEAVE();
+	return se;
+}
+
+/**
+ *  @brief Returns hex value of a given ascii string
+ *
+ *  @param a	String to be converted
+ *
+ *  @return	hex value
+ */
+static int
+bt_atox(const char *a)
+{
+	int i = 0;
+	ENTER();
+	while (isxdigit(*a))
+		i = i * 16 + bt_hexval(*a++);
+
+	LEAVE();
+	return i;
+}
+
+/**
+ *  @brief Converts mac address from string to t_u8 buffer.
+ *
+ *  @param mac_addr The buffer to store the mac address in.
+ *  @param buf      The source of mac address which is a string.
+ *
+ *  @return	N/A
+ */
+static void
+bt_mac2u8(u8 *mac_addr, char *buf)
+{
+	char *begin, *end, *mac_buff;
+	int i;
+
+	ENTER();
+
+	if (!buf) {
+		LEAVE();
+		return;
+	}
+
+	mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL);
+	if (!mac_buff) {
+		LEAVE();
+		return;
+	}
+	memcpy(mac_buff, buf, strlen(buf));
+
+	begin = mac_buff;
+	for (i = 0; i < ETH_ALEN; ++i) {
+		end = bt_strsep(&begin, ':', '/');
+		if (end)
+			mac_addr[i] = bt_atox(end);
+	}
+
+	kfree(mac_buff);
+	LEAVE();
+}
+
+/**
+ *  @brief Returns integer value of a given ascii string
+ *
+ *  @param data    Converted data to be returned
+ *  @param a       String to be converted
+ *
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_atoi(int *data, char *a)
+{
+	int i, val = 0, len;
+
+	ENTER();
+
+	len = strlen(a);
+	if (!strncmp(a, "0x", 2)) {
+		a = a + 2;
+		len -= 2;
+		*data = bt_atox(a);
+		return BT_STATUS_SUCCESS;
+	}
+	for (i = 0; i < len; i++) {
+		if (isdigit(a[i])) {
+			val = val * 10 + (a[i] - '0');
+		} else {
+			PRINTM(ERROR, "Invalid char %c in string %s\n", a[i],
+			       a);
+			return BT_STATUS_FAILURE;
+		}
+	}
+	*data = val;
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief parse cal-data
+ *
+ *  @param src      a pointer to cal-data string
+ *  @param len      len of cal-data
+ *  @param dst      a pointer to return cal-data
+ *  @param dst_size size of dest buffer
+ *
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 *dst_size)
+{
+	const u8 *ptr;
+	u8 *dptr;
+	u32 count = 0;
+	int ret = BT_STATUS_FAILURE;
+
+	ENTER();
+	ptr = src;
+	dptr = dst;
+
+	while ((ptr - src) < len) {
+		if (*ptr && isspace(*ptr)) {
+			ptr++;
+			continue;
+		}
+
+		if (isxdigit(*ptr)) {
+			if ((dptr - dst) >= *dst_size) {
+				PRINTM(ERROR, "cal_file size too big!!!\n");
+				goto done;
+			}
+			*dptr++ = bt_atox((const char *)ptr);
+			ptr += 2;
+			count++;
+		} else {
+			ptr++;
+		}
+	}
+	if (dptr == dst) {
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	*dst_size = count;
+	ret = BT_STATUS_SUCCESS;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT get one line data from ASCII format data
+ *
+ *    @param data         Source data
+ *    @param size         Source data length
+ *    @param line_pos     Destination data
+ *    @return             -1 or length of the line
+ */
+int
+parse_cfg_get_line(u8 *data, u32 size, u8 *line_pos)
+{
+	static s32 pos;
+	u8 *src, *dest;
+
+	if (pos >= size) {	/* reach the end */
+		pos = 0;	/* Reset position for rfkill */
+		return -1;
+	}
+	memset(line_pos, 0, MAX_LINE_LEN);
+	src = data + pos;
+	dest = line_pos;
+
+	while (pos < size && *src != '\x0A' && *src != '\0') {
+		if (*src != ' ' && *src != '\t')	/* parse space */
+			*dest++ = *src++;
+		else
+			src++;
+		pos++;
+	}
+	*dest = '\0';
+	/* parse new line */
+	pos++;
+	return strlen((const char *)line_pos);
+}
+
+/**
+ *    @brief BT parse ASCII format data to MAC address
+ *
+ *    @param priv          BT private handle
+ *    @param data          Source data
+ *    @param size          data length
+ *    @return              BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_init_cfg(bt_private *priv, u8 *data, u32 size)
+{
+	u8 *pos;
+	u8 *intf_s, *intf_e;
+	u8 s[MAX_LINE_LEN];	/* 1 line data */
+	u32 line_len;
+	char dev_name[MAX_PARAM_LEN];
+	u8 buf[MAX_PARAM_LEN];
+	u8 bt_addr[MAX_MAC_ADDR_LEN];
+	u8 bt_mac[ETH_ALEN];
+	int setting = 0;
+	u8 type = 0;
+	u16 value = 0;
+	u32 offset = 0;
+	int ret = BT_STATUS_FAILURE;
+
+	memset(dev_name, 0, sizeof(dev_name));
+	memset(bt_addr, 0, sizeof(bt_addr));
+	memset(bt_mac, 0, sizeof(bt_mac));
+
+	while ((line_len = parse_cfg_get_line(data, size, s)) != -1) {
+		pos = s;
+		while (*pos == ' ' || *pos == '\t')
+			pos++;
+
+		if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') ||
+		    *pos == '\n' || *pos == '\0')
+			continue;	/* Need n't process this line */
+
+		/* Process MAC addr */
+		if (strncmp((char *)pos, "mac_addr", 8) == 0) {
+			intf_s = (u8 *)strchr((const char *)pos, '=');
+			if (intf_s != NULL)
+				intf_e = (u8 *)strchr((const char *)intf_s,
+						      ':');
+			else
+				intf_e = NULL;
+			if (intf_s != NULL && intf_e != NULL) {
+				if ((intf_e - intf_s) > MAX_PARAM_LEN) {
+					PRINTM(ERROR,
+					       "BT: Too long interface name %d\n",
+					       __LINE__);
+					goto done;
+				}
+				strncpy(dev_name, (const char *)intf_s + 1,
+					intf_e - intf_s - 1);
+				dev_name[intf_e - intf_s - 1] = '\0';
+				strncpy((char *)bt_addr,
+					(const char *)intf_e + 1,
+					MAX_MAC_ADDR_LEN - 1);
+				bt_addr[MAX_MAC_ADDR_LEN - 1] = '\0';
+				/* Convert MAC format */
+				bt_mac2u8(bt_mac, (char *)bt_addr);
+				PRINTM(CMD,
+				       "HCI: %s new BT Address " MACSTR "\n",
+				       dev_name, MAC2STR(bt_mac));
+				if (BT_STATUS_SUCCESS !=
+				    bt_set_mac_address(priv, bt_mac)) {
+					PRINTM(FATAL,
+					       "BT: Fail to set mac address\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+		}
+		/* Process REG value */
+		else if (strncmp((char *)pos, "bt_reg", 6) == 0) {
+			intf_s = (u8 *)strchr((const char *)pos, '=');
+			if (intf_s != NULL)
+				intf_e = (u8 *)strchr((const char *)intf_s,
+						      ',');
+			else
+				intf_e = NULL;
+			if (intf_s != NULL && intf_e != NULL) {
+				/* Copy type */
+				memset(buf, 0, sizeof(buf));
+				strncpy((char *)buf, (const char *)intf_s + 1,
+					1);
+				buf[1] = '\0';
+				if (0 == bt_atoi(&setting, (char *)buf))
+					type = (u8)setting;
+				else {
+					PRINTM(ERROR,
+					       "BT: Fail to parse reg type\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+			intf_s = intf_e + 1;
+			intf_e = (u8 *)strchr((const char *)intf_s, ',');
+			if (intf_e != NULL) {
+				if ((intf_e - intf_s) >= MAX_PARAM_LEN) {
+					PRINTM(ERROR,
+					       "BT: Regsier offset is too long %d\n",
+					       __LINE__);
+					goto done;
+				}
+				/* Copy offset */
+				memset(buf, 0, sizeof(buf));
+				strncpy((char *)buf, (const char *)intf_s,
+					intf_e - intf_s);
+				buf[intf_e - intf_s] = '\0';
+				if (0 == bt_atoi(&setting, (char *)buf))
+					offset = (u32)setting;
+				else {
+					PRINTM(ERROR,
+					       "BT: Fail to parse reg offset\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+			intf_s = intf_e + 1;
+			if ((strlen((const char *)intf_s) >= MAX_PARAM_LEN)) {
+				PRINTM(ERROR,
+				       "BT: Regsier value is too long %d\n",
+				       __LINE__);
+				goto done;
+			}
+			/* Copy value */
+			memset(buf, 0, sizeof(buf));
+			strncpy((char *)buf, (const char *)intf_s, sizeof(buf));
+			if (0 == bt_atoi(&setting, (char *)buf))
+				value = (u16) setting;
+			else {
+				PRINTM(ERROR, "BT: Fail to parse reg value\n");
+				goto done;
+			}
+
+			PRINTM(CMD,
+			       "BT: Write reg type: %d offset: 0x%x value: 0x%x\n",
+			       type, offset, value);
+			if (BT_STATUS_SUCCESS !=
+			    bt_write_reg(priv, type, offset, value)) {
+				PRINTM(FATAL,
+				       "BT: Write reg failed. type: %d offset: 0x%x value: 0x%x\n",
+				       type, offset, value);
+				goto done;
+			}
+		}
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief BT request init conf firmware callback
+ *        This function is invoked by request_firmware_nowait system call
+ *
+ * @param firmware  A pointer to firmware image
+ * @param context   A pointer to bt_private structure
+ *
+ * @return          N/A
+ */
+static void
+bt_request_init_user_conf_callback(const struct firmware *firmware,
+				   void *context)
+{
+	bt_private *priv = (bt_private *)context;
+
+	ENTER();
+
+	if (!firmware)
+		PRINTM(ERROR, "BT user init config request firmware failed\n");
+
+	priv->init_user_cfg = firmware;
+	priv->init_user_conf_wait_flag = TRUE;
+	wake_up_interruptible(&priv->init_user_conf_wait_q);
+
+	LEAVE();
+	return;
+}
+
+/**
+ *    @brief BT set user defined init data and param
+ *
+ *    @param priv     BT private handle
+ *    @param cfg_file user cofig file
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_init_config(bt_private *priv, char *cfg_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if ((request_firmware(&cfg, cfg_file, priv->hotplug_device)) < 0) {
+		PRINTM(FATAL, "BT: request_firmware() %s failed\n", cfg_file);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (cfg)
+		ret = bt_process_init_cfg(priv, (u8 *)cfg->data, cfg->size);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration data
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param data    a pointer to cal data
+ *    @param size    cal data size
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_cal_cfg(bt_private *priv, u8 *data, u32 size, char *mac)
+{
+	u8 bt_mac[ETH_ALEN];
+	u8 cal_data[32];
+	u8 *mac_data = NULL;
+	u32 cal_data_len;
+	int ret = BT_STATUS_FAILURE;
+	u8 *pcal_data = cal_data;
+
+	memset(bt_mac, 0, sizeof(bt_mac));
+	cal_data_len = sizeof(cal_data);
+	if (BT_STATUS_SUCCESS !=
+	    bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) {
+		goto done;
+	}
+	if (mac != NULL) {
+		/* Convert MAC format */
+		bt_mac2u8(bt_mac, mac);
+		PRINTM(CMD, "HCI: new BT Address " MACSTR "\n",
+		       MAC2STR(bt_mac));
+		mac_data = bt_mac;
+	}
+	if (BT_STATUS_SUCCESS != bt_load_cal_data(priv, pcal_data, mac_data)) {
+		PRINTM(FATAL, "BT: Fail to load calibrate data\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration EXT data
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param data    a pointer to cal data
+ *    @param size    cal data size
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_cal_cfg_ext(bt_private *priv, u8 *data, u32 size)
+{
+	u8 cal_data[128];
+	u32 cal_data_len;
+	int ret = BT_STATUS_FAILURE;
+
+	cal_data_len = sizeof(cal_data);
+	if (BT_STATUS_SUCCESS !=
+	    bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) {
+		goto done;
+	}
+	if (BT_STATUS_SUCCESS !=
+	    bt_load_cal_data_ext(priv, cal_data, cal_data_len)) {
+		PRINTM(FATAL, "BT: Fail to load calibrate data\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration file
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param cal_file calibration file name
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_cal_config(bt_private *priv, char *cal_file, char *mac)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      bt_request_init_user_conf_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#endif
+#endif
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: bt_cal_config() failed, error code = %#x cal_file=%s\n",
+			       ret, cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->init_user_conf_wait_flag = FALSE;
+		wait_event_interruptible(priv->init_user_conf_wait_q,
+					 priv->init_user_conf_wait_flag);
+		cfg = priv->init_user_cfg;
+	} else {
+		if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) <
+		    0) {
+			PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+			       cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cfg)
+		ret = bt_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size, mac);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration EXT file
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param cal_file calibration file name
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_cal_config_ext(bt_private *priv, char *cal_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      bt_request_init_user_conf_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#endif
+#endif
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: bt_cal_config_ext() failed, error code = %#x cal_file=%s\n",
+			       ret, cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->init_user_conf_wait_flag = FALSE;
+		wait_event_interruptible(priv->init_user_conf_wait_q,
+					 priv->init_user_conf_wait_flag);
+		cfg = priv->init_user_cfg;
+	} else {
+		if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) <
+		    0) {
+			PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+			       cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cfg)
+		ret = bt_process_cal_cfg_ext(priv, (u8 *)cfg->data, cfg->size);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT init mac address from bt_mac parametre when insmod
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param bt_mac  mac address buf
+ *    @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_init_mac_address(bt_private *priv, char *mac)
+{
+	u8 bt_mac[ETH_ALEN];
+	int ret = BT_STATUS_FAILURE;
+
+	ENTER();
+	memset(bt_mac, 0, sizeof(bt_mac));
+	bt_mac2u8(bt_mac, mac);
+	PRINTM(CMD, "HCI: New BT Address " MACSTR "\n", MAC2STR(bt_mac));
+	ret = bt_set_mac_address(priv, bt_mac);
+	if (ret != BT_STATUS_SUCCESS)
+		PRINTM(FATAL,
+		       "BT: Fail to set mac address from insmod parametre.\n");
+
+	LEAVE();
+	return ret;
+}
diff --git a/bt_sd8997/bt/bt_main.c b/bt_sd8997/bt/bt_main.c
new file mode 100644
index 0000000..dc225fc
--- /dev/null
+++ b/bt_sd8997/bt/bt_main.c
@@ -0,0 +1,3525 @@
+/** @file bt_main.c
+  *
+  * @brief This file contains the major functions in BlueTooth
+  * driver. It includes init, exit, open, close and main
+  * thread etc..
+  *
+  * Copyright (C) 2007-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+  *
+  */
+/**
+  * @mainpage M-BT Linux Driver
+  *
+  * @section overview_sec Overview
+  *
+  * The M-BT is a Linux reference driver for Marvell Bluetooth chipset.
+  *
+  * @section copyright_sec Copyright
+  *
+  * Copyright (C) 2007-2016, Marvell International Ltd.
+  *
+  */
+
+#include <linux/module.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
+
+#include <linux/mmc/sdio_func.h>
+
+#include "bt_drv.h"
+#include "mbt_char.h"
+#include "bt_sdio.h"
+
+/** Version */
+#define VERSION "C4X14113"
+
+/** Driver version */
+static char mbt_driver_version[] = "SD8997-%s-" VERSION "-(" "FP" FPNUM ")"
+#ifdef DEBUG_LEVEL2
+	"-dbg"
+#endif
+	" ";
+
+/** Declare and initialize fw_version */
+static char fw_version[32] = "0.0.0.p0";
+
+#define AID_SYSTEM        1000	/* system server */
+
+#define AID_BLUETOOTH     1002	/* bluetooth subsystem */
+
+#define AID_NET_BT_STACK  3008	/* bluetooth stack */
+
+/** Define module name */
+
+#define MODULE_NAME  "bt_fm_nfc"
+
+/** Declaration of chardev class */
+static struct class *chardev_class;
+
+/** Interface specific variables */
+
+/** Default Driver mode */
+static int drv_mode = (DRV_MODE_BT);
+
+/** fw reload flag */
+int bt_fw_reload;
+/** fw serial download flag */
+int bt_fw_serial = 1;
+
+/** Firmware flag */
+static int fw = 1;
+/** default powermode */
+static int psmode = 1;
+/** Init config file (MAC address, register etc.) */
+static char *init_cfg;
+/** Calibration config file (MAC address, init powe etc.) */
+static char *cal_cfg;
+/** Calibration config file EXT */
+static char *cal_cfg_ext;
+/** Init MAC address */
+static char *bt_mac;
+static int btindrst = -1;
+
+/** Setting mbt_drvdbg value based on DEBUG level */
+#ifdef DEBUG_LEVEL1
+#ifdef DEBUG_LEVEL2
+#define DEFAULT_DEBUG_MASK  (0xffffffff & ~DBG_EVENT)
+#else
+#define DEFAULT_DEBUG_MASK  (DBG_MSG | DBG_FATAL | DBG_ERROR)
+#endif /* DEBUG_LEVEL2 */
+u32 mbt_drvdbg = DEFAULT_DEBUG_MASK;
+#endif
+
+#ifdef CONFIG_OF
+static int dts_enable = 1;
+#endif
+
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+int mbt_pm_keep_power = 1;
+#endif
+
+static int btpmic = 0;
+
+/**
+ *  @brief Alloc bt device
+ *
+ *  @return    pointer to structure mbt_dev or NULL
+ */
+struct mbt_dev *
+alloc_mbt_dev(void)
+{
+	struct mbt_dev *mbt_dev;
+	ENTER();
+
+	mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL);
+	if (!mbt_dev) {
+		LEAVE();
+		return NULL;
+	}
+
+	LEAVE();
+	return mbt_dev;
+}
+
+/**
+ *  @brief Frees m_dev
+ *
+ *  @return    N/A
+ */
+void
+free_m_dev(struct m_dev *m_dev)
+{
+	ENTER();
+	kfree(m_dev->dev_pointer);
+	m_dev->dev_pointer = NULL;
+	LEAVE();
+}
+
+/**
+ *  @brief clean up m_devs
+ *
+ *  @return    N/A
+ */
+void
+clean_up_m_devs(bt_private *priv)
+{
+	struct m_dev *m_dev = NULL;
+	struct hci_dev *hdev = NULL;
+
+	ENTER();
+	if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+		PRINTM(MSG, "BT: Delete %s\n", m_dev->name);
+		if (m_dev->spec_type == BLUEZ_SPEC) {
+			hdev = (struct hci_dev *)m_dev->dev_pointer;
+			/** check if dev->name has been assigned */
+			if (strstr(hdev->name, "hci"))
+				hci_unregister_dev(hdev);
+			hci_free_dev(hdev);
+		}
+		priv->bt_dev.m_dev[BT_SEQ].dev_pointer = NULL;
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function verify the received event pkt
+ *
+ *  Event format:
+ *  +--------+--------+--------+--------+--------+
+ *  | Event  | Length |  ncmd  |      Opcode     |
+ *  +--------+--------+--------+--------+--------+
+ *  | 1-byte | 1-byte | 1-byte |      2-byte     |
+ *  +--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+check_evtpkt(bt_private *priv, struct sk_buff *skb)
+{
+	struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data;
+	struct hci_ev_cmd_complete *ec;
+	u16 opcode, ocf;
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (!priv->bt_dev.sendcmdflag) {
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	if (hdr->evt == HCI_EV_CMD_COMPLETE) {
+		ec = (struct hci_ev_cmd_complete *)
+			(skb->data + HCI_EVENT_HDR_SIZE);
+		opcode = __le16_to_cpu(ec->opcode);
+		ocf = hci_opcode_ocf(opcode);
+		PRINTM(CMD,
+		       "BT: CMD_COMPLTE opcode=0x%x, ocf=0x%x, send_cmd_opcode=0x%x\n",
+		       opcode, ocf, priv->bt_dev.send_cmd_opcode);
+		if (opcode != priv->bt_dev.send_cmd_opcode) {
+			ret = BT_STATUS_FAILURE;
+			goto exit;
+		}
+		switch (ocf) {
+		case BT_CMD_MODULE_CFG_REQ:
+		case BT_CMD_BLE_DEEP_SLEEP:
+		case BT_CMD_CONFIG_MAC_ADDR:
+		case BT_CMD_CSU_WRITE_REG:
+		case BT_CMD_LOAD_CONFIG_DATA:
+		case BT_CMD_LOAD_CONFIG_DATA_EXT:
+		case BT_CMD_AUTO_SLEEP_MODE:
+		case BT_CMD_HOST_SLEEP_CONFIG:
+		case BT_CMD_SDIO_PULL_CFG_REQ:
+		case BT_CMD_SET_EVT_FILTER:
+		case BT_CMD_ENABLE_WRITE_SCAN:
+			// case BT_CMD_ENABLE_DEVICE_TESTMODE:
+		case BT_CMD_RESET:
+		case BT_CMD_PMIC_CONFIGURE:
+		case BT_CMD_INDEPENDENT_RESET:
+			priv->bt_dev.sendcmdflag = FALSE;
+			priv->adapter->cmd_complete = TRUE;
+			wake_up_interruptible(&priv->adapter->cmd_wait_q);
+			break;
+		case BT_CMD_GET_FW_VERSION:
+			{
+				u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE +
+					   sizeof(struct hci_ev_cmd_complete) +
+					   1);
+				snprintf(fw_version, sizeof(fw_version),
+					 "%u.%u.%u.p%u", pos[2], pos[1], pos[0],
+					 pos[3]);
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+				break;
+			}
+		case BT_CMD_HISTOGRAM:
+			{
+				u8 *status =
+					skb->data + HCI_EVENT_HDR_SIZE +
+					sizeof(struct hci_ev_cmd_complete);
+				u8 *pos =
+					(skb->data + HCI_EVENT_HDR_SIZE +
+					 sizeof(struct hci_ev_cmd_complete) +
+					 1);
+				if (*status == 0) {
+					priv->hist_data_len =
+						hdr->plen -
+						sizeof(struct
+						       hci_ev_cmd_complete) - 1;
+					if (priv->hist_data_len >
+					    sizeof(priv->hist_data))
+						priv->hist_data_len =
+							sizeof(priv->hist_data);
+					memcpy(priv->hist_data, pos,
+					       priv->hist_data_len);
+					PRINTM(CMD, "histogram len=%d\n",
+					       priv->hist_data_len);
+				}
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+				break;
+			}
+		case BT_CMD_HOST_SLEEP_ENABLE:
+			priv->bt_dev.sendcmdflag = FALSE;
+			break;
+		default:
+			/** Ignore command not defined but send by driver */
+			if (opcode == priv->bt_dev.send_cmd_opcode) {
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			} else {
+				ret = BT_STATUS_FAILURE;
+			}
+			break;
+		}
+	}
+exit:
+	if (ret == BT_STATUS_SUCCESS)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function process the received event
+ *
+ *  Event format:
+ *  +--------+--------+--------+--------+-----+
+ *  |   EC   | Length |           Data        |
+ *  +--------+--------+--------+--------+-----+
+ *  | 1-byte | 1-byte |          n-byte       |
+ *  +--------+--------+--------+--------+-----+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_event(bt_private *priv, struct sk_buff *skb)
+{
+	int ret = BT_STATUS_SUCCESS;
+#ifdef DEBUG_LEVEL1
+	struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+#endif
+	BT_EVENT *pevent;
+
+	ENTER();
+	pevent = (BT_EVENT *)skb->data;
+	if (pevent->EC != 0xff) {
+		PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pevent->EC);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	switch (pevent->data[0]) {
+	case BT_CMD_HISTOGRAM:
+		break;
+	case BT_CMD_AUTO_SLEEP_MODE:
+		if (pevent->data[2] == BT_STATUS_SUCCESS) {
+			if (pevent->data[1] == BT_PS_ENABLE)
+				priv->adapter->psmode = 1;
+			else
+				priv->adapter->psmode = 0;
+			PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name,
+			       (priv->adapter->psmode) ? "Enable" : "Disable");
+
+		} else {
+			PRINTM(CMD, "BT: PS Mode Command Fail %s\n",
+			       m_dev->name);
+		}
+		break;
+	case BT_CMD_HOST_SLEEP_CONFIG:
+		if (pevent->data[3] == BT_STATUS_SUCCESS) {
+			PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n",
+			       m_dev->name, pevent->data[1], pevent->data[2]);
+		} else {
+			PRINTM(CMD, "BT: %s: HSCFG Command Fail\n",
+			       m_dev->name);
+		}
+		break;
+	case BT_CMD_HOST_SLEEP_ENABLE:
+		if (pevent->data[1] == BT_STATUS_SUCCESS) {
+			priv->adapter->hs_state = HS_ACTIVATED;
+			if (priv->adapter->suspend_fail == FALSE) {
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+				bt_is_suspended(priv);
+#endif
+#endif
+#endif
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			}
+			if (priv->adapter->psmode)
+				priv->adapter->ps_state = PS_SLEEP;
+			PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n",
+			       m_dev->name);
+
+		} else {
+			PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name);
+		}
+		break;
+	case BT_CMD_MODULE_CFG_REQ:
+		if ((priv->bt_dev.sendcmdflag == TRUE) &&
+		    ((pevent->data[1] == MODULE_BRINGUP_REQ)
+		     || (pevent->data[1] == MODULE_SHUTDOWN_REQ))) {
+			if (pevent->data[1] == MODULE_BRINGUP_REQ) {
+				PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+				       (pevent->data[2] && (pevent->data[2] !=
+							    MODULE_CFG_RESP_ALREADY_UP))
+				       ? "Bring up Fail" : "Bring up success");
+				priv->bt_dev.devType = pevent->data[3];
+				PRINTM(CMD, "devType:%s\n",
+				       (pevent->data[3] ==
+					DEV_TYPE_AMP) ? "AMP controller" :
+				       "BR/EDR controller");
+				priv->bt_dev.devFeature = pevent->data[4];
+				PRINTM(CMD, "devFeature:  %s,    %s,    %s"
+				       "\n",
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BT) ?
+					"BT Feature" : "No BT Feature"),
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BTAMP) ?
+					"BTAMP Feature" : "No BTAMP Feature"),
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BLE) ?
+					"BLE Feature" : "No BLE Feature")
+					);
+			}
+			if (pevent->data[1] == MODULE_SHUTDOWN_REQ) {
+				PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+				       (pevent->data[2]) ? "Shut down Fail"
+				       : "Shut down success");
+
+			}
+			if (pevent->data[2]) {
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			}
+		} else {
+			PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n");
+			ret = BT_STATUS_FAILURE;
+		}
+		break;
+	case BT_EVENT_POWER_STATE:
+		if (pevent->data[1] == BT_PS_SLEEP)
+			priv->adapter->ps_state = PS_SLEEP;
+		PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+		       (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
+
+		break;
+	case BT_CMD_SDIO_PULL_CFG_REQ:
+		if (pevent->data[pevent->length - 1] == BT_STATUS_SUCCESS)
+			PRINTM(CMD, "BT: %s: SDIO pull configuration success\n",
+			       m_dev->name);
+
+		else {
+			PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n",
+			       m_dev->name);
+
+		}
+		break;
+	default:
+		PRINTM(CMD, "BT: Unknown Event=%d %s\n", pevent->data[0],
+		       m_dev->name);
+		ret = BT_STATUS_FAILURE;
+		break;
+	}
+exit:
+	if (ret == BT_STATUS_SUCCESS)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function save the dump info to file
+ *
+ *
+ *  @param dir_name     directory name
+ *  @param file_name    file_name
+ *  @return    0 --success otherwise fail
+ */
+int
+bt_save_dump_info_to_file(char *dir_name, char *file_name, u8 *buf, u32 buf_len)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct file *pfile = NULL;
+	u8 name[64];
+	mm_segment_t fs;
+	loff_t pos;
+
+	ENTER();
+
+	if (!dir_name || !file_name || !buf) {
+		PRINTM(ERROR, "Can't save dump info to file\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	memset(name, 0, sizeof(name));
+	sprintf((char *)name, "%s/%s", dir_name, file_name);
+	pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644);
+	if (IS_ERR(pfile)) {
+		PRINTM(MSG,
+		       "Create file %s error, try to save dump file in /var\n",
+		       name);
+		memset(name, 0, sizeof(name));
+		sprintf((char *)name, "%s/%s", "/var", file_name);
+		pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644);
+	}
+	if (IS_ERR(pfile)) {
+		PRINTM(ERROR, "Create Dump file for %s error\n", name);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	PRINTM(MSG, "Dump data %s saved in %s\n", file_name, name);
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	pos = 0;
+	vfs_write(pfile, (const char __user *)buf, buf_len, &pos);
+	filp_close(pfile, NULL);
+	set_fs(fs);
+
+	PRINTM(MSG, "Dump data %s saved in %s successfully\n", file_name, name);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+#define DEBUG_HOST_READY		0xEE
+#define DEBUG_FW_DONE			0xFF
+#define DUMP_MAX_POLL_TRIES			200
+
+#define DEBUG_DUMP_CTRL_REG               0xF0
+#define DEBUG_DUMP_START_REG              0xF1
+#define DEBUG_DUMP_END_REG                0xF8
+
+typedef enum {
+	DUMP_TYPE_ITCM = 0,
+	DUMP_TYPE_DTCM = 1,
+	DUMP_TYPE_SQRAM = 2,
+	DUMP_TYPE_APU_REGS = 3,
+	DUMP_TYPE_CIU_REGS = 4,
+	DUMP_TYPE_ICU_REGS = 5,
+	DUMP_TYPE_MAC_REGS = 6,
+	DUMP_TYPE_EXTEND_7 = 7,
+	DUMP_TYPE_EXTEND_8 = 8,
+	DUMP_TYPE_EXTEND_9 = 9,
+	DUMP_TYPE_EXTEND_10 = 10,
+	DUMP_TYPE_EXTEND_11 = 11,
+	DUMP_TYPE_EXTEND_12 = 12,
+	DUMP_TYPE_EXTEND_13 = 13,
+	DUMP_TYPE_EXTEND_LAST = 14
+} dumped_mem_type;
+
+#define MAX_NAME_LEN               8
+#define MAX_FULL_NAME_LEN               32
+
+typedef struct {
+	u8 mem_name[MAX_NAME_LEN];
+	u8 *mem_Ptr;
+	struct file *pfile_mem;
+	u8 done_flag;
+} memory_type_mapping;
+
+memory_type_mapping bt_mem_type_mapping_tbl[] = {
+	{"ITCM", NULL, NULL, 0xF0},
+	{"DTCM", NULL, NULL, 0xF1},
+	{"SQRAM", NULL, NULL, 0xF2},
+	{"APU", NULL, NULL, 0xF3},
+	{"CIU", NULL, NULL, 0xF4},
+	{"ICU", NULL, NULL, 0xF5},
+	{"MAC", NULL, NULL, 0xF6},
+	{"EXT7", NULL, NULL, 0xF7},
+	{"EXT8", NULL, NULL, 0xF8},
+	{"EXT9", NULL, NULL, 0xF9},
+	{"EXT10", NULL, NULL, 0xFA},
+	{"EXT11", NULL, NULL, 0xFB},
+	{"EXT12", NULL, NULL, 0xFC},
+	{"EXT13", NULL, NULL, 0xFD},
+	{"EXTLAST", NULL, NULL, 0xFE},
+};
+
+typedef enum {
+	RDWR_STATUS_SUCCESS = 0,
+	RDWR_STATUS_FAILURE = 1,
+	RDWR_STATUS_DONE = 2
+} rdwr_status;
+
+/**
+ *  @brief This function read/write firmware via cmd52
+ *
+ *  @param phandle   A pointer to moal_handle
+ *
+ *  @return         MLAN_STATUS_SUCCESS
+ */
+rdwr_status
+bt_cmd52_rdwr_firmware(bt_private *priv, u8 doneflag)
+{
+	int ret = 0;
+	int tries = 0;
+	u8 ctrl_data = 0;
+	u8 dbg_dump_ctrl_reg = 0;
+
+	dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG;
+
+	sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.card)->func,
+		    DEBUG_HOST_READY, dbg_dump_ctrl_reg, &ret);
+	if (ret) {
+		PRINTM(ERROR, "SDIO Write ERR\n");
+		return RDWR_STATUS_FAILURE;
+	}
+	for (tries = 0; tries < DUMP_MAX_POLL_TRIES; tries++) {
+		ctrl_data =
+			sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->
+				   func, dbg_dump_ctrl_reg, &ret);
+		if (ret) {
+			PRINTM(ERROR, "SDIO READ ERR\n");
+			return RDWR_STATUS_FAILURE;
+		}
+		if (ctrl_data == DEBUG_FW_DONE)
+			break;
+		if (doneflag && ctrl_data == doneflag)
+			return RDWR_STATUS_DONE;
+		if (ctrl_data != DEBUG_HOST_READY) {
+			PRINTM(INFO,
+			       "The ctrl reg was changed, re-try again!\n");
+			sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.
+				     card)->func, DEBUG_HOST_READY,
+				    dbg_dump_ctrl_reg, &ret);
+			if (ret) {
+				PRINTM(ERROR, "SDIO Write ERR\n");
+				return RDWR_STATUS_FAILURE;
+			}
+		}
+		udelay(100);
+	}
+	if (ctrl_data == DEBUG_HOST_READY) {
+		PRINTM(ERROR, "Fail to pull ctrl_data\n");
+		return RDWR_STATUS_FAILURE;
+	}
+
+	return RDWR_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function dump firmware memory to file
+ *
+ *  @param phandle   A pointer to moal_handle
+ *
+ *  @return         N/A
+ */
+void
+bt_dump_firmware_info_v2(bt_private *priv)
+{
+	int ret = 0;
+	unsigned int reg, reg_start, reg_end;
+	u8 *dbg_ptr = NULL;
+	u8 dump_num = 0;
+	u8 idx = 0;
+	u8 doneflag = 0;
+	rdwr_status stat;
+	u8 i = 0;
+	u8 read_reg = 0;
+	u32 memory_size = 0;
+	u8 path_name[64], file_name[32];
+	u8 *end_ptr = NULL;
+	u8 dbg_dump_start_reg = 0;
+	u8 dbg_dump_end_reg = 0;
+
+	if (!priv) {
+		PRINTM(ERROR, "Could not dump firmwware info\n");
+		return;
+	}
+
+	memset(path_name, 0, sizeof(path_name));
+	strcpy((char *)path_name, "/data");
+	PRINTM(MSG, "Create DUMP directory success:dir_name=%s\n", path_name);
+
+	dbg_dump_start_reg = DEBUG_DUMP_START_REG;
+	dbg_dump_end_reg = DEBUG_DUMP_END_REG;
+
+	sbi_wakeup_firmware(priv);
+	sdio_claim_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func);
+	/* start dump fw memory */
+	PRINTM(MSG, "==== DEBUG MODE OUTPUT START ====\n");
+	/* read the number of the memories which will dump */
+	if (RDWR_STATUS_FAILURE == bt_cmd52_rdwr_firmware(priv, doneflag))
+		goto done;
+	reg = dbg_dump_start_reg;
+	dump_num =
+		sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->func,
+			   reg, &ret);
+	if (ret) {
+		PRINTM(MSG, "SDIO READ MEM NUM ERR\n");
+		goto done;
+	}
+
+	/* read the length of every memory which will dump */
+	for (idx = 0; idx < dump_num; idx++) {
+		if (RDWR_STATUS_FAILURE ==
+		    bt_cmd52_rdwr_firmware(priv, doneflag))
+			goto done;
+		memory_size = 0;
+		reg = dbg_dump_start_reg;
+		for (i = 0; i < 4; i++) {
+			read_reg =
+				sdio_readb(((struct sdio_mmc_card *)priv->
+					    bt_dev.card)->func, reg, &ret);
+			if (ret) {
+				PRINTM(MSG, "SDIO READ ERR\n");
+				goto done;
+			}
+			memory_size |= (read_reg << i * 8);
+			reg++;
+		}
+		if (memory_size == 0) {
+			PRINTM(MSG, "Firmware Dump Finished!\n");
+			break;
+		} else {
+			PRINTM(MSG, "%s_SIZE=0x%x\n",
+			       bt_mem_type_mapping_tbl[idx].mem_name,
+			       memory_size);
+			bt_mem_type_mapping_tbl[idx].mem_Ptr =
+				vmalloc(memory_size + 1);
+			if ((ret != BT_STATUS_SUCCESS) ||
+			    !bt_mem_type_mapping_tbl[idx].mem_Ptr) {
+				PRINTM(ERROR,
+				       "Error: vmalloc %s buffer failed!!!\n",
+				       bt_mem_type_mapping_tbl[idx].mem_name);
+				goto done;
+			}
+			dbg_ptr = bt_mem_type_mapping_tbl[idx].mem_Ptr;
+			end_ptr = dbg_ptr + memory_size;
+		}
+		doneflag = bt_mem_type_mapping_tbl[idx].done_flag;
+		PRINTM(MSG, "Start %s output, please wait...\n",
+		       bt_mem_type_mapping_tbl[idx].mem_name);
+		do {
+			stat = bt_cmd52_rdwr_firmware(priv, doneflag);
+			if (RDWR_STATUS_FAILURE == stat)
+				goto done;
+
+			reg_start = dbg_dump_start_reg;
+			reg_end = dbg_dump_end_reg;
+			for (reg = reg_start; reg <= reg_end; reg++) {
+				*dbg_ptr =
+					sdio_readb(((struct sdio_mmc_card *)
+						    priv->bt_dev.card)->func,
+						   reg, &ret);
+				if (ret) {
+					PRINTM(MSG, "SDIO READ ERR\n");
+					goto done;
+				}
+				if (dbg_ptr < end_ptr)
+					dbg_ptr++;
+				else
+					PRINTM(MSG,
+					       "pre-allocced buf is not enough\n");
+			}
+			if (RDWR_STATUS_DONE == stat) {
+				PRINTM(MSG, "%s done:"
+				       "size = 0x%x\n",
+				       bt_mem_type_mapping_tbl[idx].mem_name,
+				       (unsigned int)(dbg_ptr -
+						      bt_mem_type_mapping_tbl
+						      [idx].mem_Ptr));
+				memset(file_name, 0, sizeof(file_name));
+				sprintf((char *)file_name, "%s%s", "file_bt_",
+					bt_mem_type_mapping_tbl[idx].mem_name);
+				if (BT_STATUS_SUCCESS !=
+				    bt_save_dump_info_to_file((char *)path_name,
+							      (char *)file_name,
+							      bt_mem_type_mapping_tbl
+							      [idx].mem_Ptr,
+							      memory_size))
+					PRINTM(MSG,
+					       "Can't save dump file %s in %s\n",
+					       file_name, path_name);
+				vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr);
+				bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL;
+				break;
+			}
+		} while (1);
+	}
+	PRINTM(MSG, "==== DEBUG MODE OUTPUT END ====\n");
+	/* end dump fw memory */
+done:
+	sdio_release_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func);
+	for (idx = 0; idx < dump_num; idx++) {
+		if (bt_mem_type_mapping_tbl[idx].mem_Ptr) {
+			vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr);
+			bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL;
+		}
+	}
+	PRINTM(MSG, "==== DEBUG MODE END ====\n");
+	return;
+}
+
+/**
+ *  @brief This function shows debug info for timeout of command sending.
+ *
+ *  @param adapter  A pointer to bt_private
+ *  @param cmd      Timeout command id
+ *
+ *  @return         N/A
+ */
+static void
+bt_cmd_timeout_func(bt_private *priv, u16 cmd)
+{
+	bt_adapter *adapter = priv->adapter;
+	ENTER();
+
+	adapter->num_cmd_timeout++;
+
+	PRINTM(ERROR, "Version = %s\n", adapter->drv_ver);
+	PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd);
+	PRINTM(ERROR, "Number of command timeout = %d\n",
+	       adapter->num_cmd_timeout);
+	PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter);
+	PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode);
+	PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state);
+	PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state);
+	PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip);
+	PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail);
+	PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended);
+	PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries);
+	PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete);
+	PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv);
+	PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done);
+	PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending);
+	PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg);
+	bt_dump_sdio_regs(priv);
+	LEAVE();
+}
+
+/**
+ *  @brief This function send reset cmd to firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return	       BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_reset_command(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((RESET_OGF << 10) | BT_CMD_RESET);
+	pcmd->length = 0x00;
+	pcmd->cmd_type = 0x00;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, 3);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "Queue Reset Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Reset timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_RESET);
+	} else {
+		PRINTM(CMD, "BT: Reset Command done\n");
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends module cfg cmd to firmware
+ *
+ *  Command format:
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     OCF OGF     | Length |                Data               |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     2-byte      | 1-byte |               4-byte              |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param subcmd  sub command
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_module_cfg_cmd(bt_private *priv, int subcmd)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "BT: No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ);
+	pcmd->length = 1;
+	pcmd->data[0] = subcmd;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "Queue module cfg Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	/*
+	   On some Android platforms certain delay is needed for HCI daemon to
+	   remove this module and close itself gracefully. Otherwise it hangs.
+	   This 10ms delay is a workaround for such platforms as the root cause
+	   has not been found yet. */
+	mdelay(10);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: module_cfg_cmd(%#x): timeout sendcmdflag=%d\n",
+		       subcmd, priv->bt_dev.sendcmdflag);
+		bt_cmd_timeout_func(priv, BT_CMD_MODULE_CFG_REQ);
+	} else {
+		PRINTM(CMD, "BT: module cfg Command done\n");
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function to get histogram
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_get_histogram(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HISTOGRAM);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Histogram cmd(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	priv->hist_data_len = 0;
+	memset(priv->hist_data, 0, sizeof(priv->hist_data));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: histogram timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_HISTOGRAM);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables power save mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_ps(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE);
+	if (priv->bt_dev.psmode)
+		pcmd->data[0] = BT_PS_ENABLE;
+	else
+		pcmd->data[0] = BT_PS_DISABLE;
+	if (priv->bt_dev.idle_timeout) {
+		pcmd->length = 3;
+		pcmd->data[1] = (u8)(priv->bt_dev.idle_timeout & 0x00ff);
+		pcmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8;
+	} else {
+		pcmd->length = 1;
+	}
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: psmode timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_AUTO_SLEEP_MODE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends hscfg command
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_hscfg_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG);
+	pcmd->length = 2;
+	pcmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8;
+	pcmd->data[1] = (u8)(priv->bt_dev.gpio_gap & 0x00ff);
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0], pcmd->data[1]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: HSCFG timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_CONFIG);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends sdio pull ctrl command
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_sdio_pull_ctrl_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ);
+	pcmd->length = 4;
+	pcmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff);
+	pcmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8;
+	pcmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16;
+	pcmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD,
+	       "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[1], pcmd->data[0],
+	       pcmd->data[3], pcmd->data[2]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SDIO_PULL_CFG_REQ);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends command to configure PMIC
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_pmic_configure(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_PMIC_CONFIGURE);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue PMIC Configure Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: PMIC Configure timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_PMIC_CONFIGURE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables host sleep
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_hs(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	priv->adapter->suspend_fail = FALSE;
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	PRINTM(CMD, "Queue hs enable Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->hs_state,
+	     WAIT_UNTIL_HS_STATE_CHANGED)) {
+		PRINTM(MSG, "BT: Enable host sleep timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE);
+	}
+	OS_INT_DISABLE;
+	if ((priv->adapter->hs_state == HS_ACTIVATED) ||
+	    (priv->adapter->is_suspended == TRUE)) {
+		OS_INT_RESTORE;
+		PRINTM(MSG, "BT: suspend success! skip=%d\n",
+		       priv->adapter->hs_skip);
+	} else {
+		priv->adapter->suspend_fail = TRUE;
+		OS_INT_RESTORE;
+		priv->adapter->hs_skip++;
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG,
+		       "BT: suspend skipped! "
+		       "state=%d skip=%d ps_state= %d WakeupTries=%d\n",
+		       priv->adapter->hs_state, priv->adapter->hs_skip,
+		       priv->adapter->ps_state, priv->adapter->WakeupTries);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Set Evt Filter
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_evt_filter(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_SET_EVT_FILTER);
+	pcmd->length = 0x03;
+	pcmd->data[0] = 0x02;
+	pcmd->data[1] = 0x00;
+	pcmd->data[2] = 0x03;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Set Evt Filter Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set Evt Filter timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SET_EVT_FILTER);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Enable Write Scan - Page and Inquiry
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_write_scan(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_ENABLE_WRITE_SCAN);
+	pcmd->length = 0x01;
+	pcmd->data[0] = 0x03;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Enable Write Scan Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Enable Write Scan timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_ENABLE_WRITE_SCAN);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Enable Device under test mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_device_under_testmode(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((0x06 << 10) | BT_CMD_ENABLE_DEVICE_TESTMODE);
+	pcmd->length = 0x00;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue enable device under testmode Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Enable Device under TEST mode timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_ENABLE_DEVICE_TESTMODE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables test mode and send cmd
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_test_mode(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	/** Set Evt Filter Command */
+	ret = bt_set_evt_filter(priv);
+	if (ret != BT_STATUS_SUCCESS) {
+		PRINTM(ERROR, "BT test_mode: Set Evt filter fail\n");
+		goto exit;
+	}
+
+	/** Enable Write Scan Command */
+	ret = bt_enable_write_scan(priv);
+	if (ret != BT_STATUS_SUCCESS) {
+		PRINTM(ERROR, "BT test_mode: Enable Write Scan fail\n");
+		goto exit;
+	}
+
+	/** Enable Device under test mode */
+	ret = bt_enable_device_under_testmode(priv);
+	if (ret != BT_STATUS_SUCCESS)
+		PRINTM(ERROR,
+		       "BT test_mode: Enable device under testmode fail\n");
+
+exit:
+	LEAVE();
+	return ret;
+}
+
+#define DISABLE_RESET  0x0
+#define ENABLE_OUTBAND_RESET 0x1
+#define ENABLE_INBAND_RESET  0x02
+#define DEFAULT_GPIO 0xff
+/**
+ *  @brief This function set GPIO pin
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_independent_reset(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	u8 mode, gpio;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_INDEPENDENT_RESET);
+	mode = btindrst & 0xff;
+	gpio = (btindrst & 0xff00) >> 8;
+	if (mode == ENABLE_OUTBAND_RESET) {
+		pcmd->data[0] = ENABLE_OUTBAND_RESET;
+		if (!gpio)
+			pcmd->data[1] = DEFAULT_GPIO;
+		else
+			pcmd->data[1] = gpio;
+	} else if (mode == ENABLE_INBAND_RESET) {
+		pcmd->data[0] = ENABLE_INBAND_RESET;
+		pcmd->data[1] = DEFAULT_GPIO;
+	} else if (mode == DISABLE_RESET) {
+		pcmd->data[0] = DISABLE_RESET;
+		pcmd->data[1] = DEFAULT_GPIO;
+	} else {
+		PRINTM(WARN, "Unsupport mode\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	PRINTM(CMD, "BT: independant reset mode=%d gpio=%d\n", mode, gpio);
+	pcmd->length = 2;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Independent reset : timeout!\n");
+		bt_cmd_timeout_func(priv, BT_CMD_INDEPENDENT_RESET);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sets ble deepsleep mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mode    TRUE/FALSE
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_ble_deepsleep(bt_private *priv, int mode)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_BLE_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_BLE_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP);
+	pcmd->length = 1;
+	pcmd->deepsleep = mode;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_BLE_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode,
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_BLE_DEEP_SLEEP);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function gets FW version
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_get_fw_version(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION);
+	pcmd->length = 0x01;
+	pcmd->cmd_type = 0x00;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, 4);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Get FW version: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_GET_FW_VERSION);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sets mac address
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_mac_address(bt_private *priv, u8 *mac)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	int i = 0;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR);
+	pcmd->length = 8;
+	pcmd->cmd_type = MRVL_VENDOR_PKT;
+	pcmd->cmd_len = 6;
+	for (i = 0; i < 6; i++)
+		pcmd->data[i] = mac[5 - i];
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_HCI_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set mac addr " MACSTR " (0x%x)\n", MAC2STR(mac),
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set mac addr: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_CONFIG_MAC_ADDR);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	int i = 0;
+	/* u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF 0x01
+	   0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE 0xC0 0xC6 0x2D 0x00 0x00 0x00
+	   0x00 0x00 0x00 0x00 0xF0}; */
+
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA);
+	pcmd->length = 0x20;
+	pcmd->data[0] = 0x00;
+	pcmd->data[1] = 0x00;
+	pcmd->data[2] = 0x00;
+	pcmd->data[3] = 0x1C;
+	/* swip cal-data byte */
+	for (i = 4; i < 32; i++)
+		pcmd->data[i] = *(config_data + ((i / 4) * 8 - 1 - i));
+	if (mac != NULL) {
+		pcmd->data[2] = 0x01;	/* skip checksum */
+		for (i = 24; i < 30; i++)
+			pcmd->data[i] = mac[29 - i];
+	}
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "calirate data: ", pcmd->data, 32);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load calibrate data: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate EXT data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA_EXT);
+	pcmd->length = cfg_data_len;
+
+	memcpy(pcmd->data, config_data, cfg_data_len);
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "calirate ext data", pcmd->data, pcmd->length);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load calibrate ext data: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function writes value to CSU registers
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param type    reg type
+ *  @param offset  register address
+ *  @param value   register value to write
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CSU_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CSU_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG);
+	pcmd->length = 7;
+	pcmd->type = type;
+	pcmd->offset[0] = (offset & 0x000000ff);
+	pcmd->offset[1] = (offset & 0x0000ff00) >> 8;
+	pcmd->offset[2] = (offset & 0x00ff0000) >> 16;
+	pcmd->offset[3] = (offset & 0xff000000) >> 24;
+	pcmd->value[0] = (value & 0x00ff);
+	pcmd->value[1] = (value & 0xff00) >> 8;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_CSU_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n",
+	       type, offset, value);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Set CSU reg timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_CSU_WRITE_REG);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function used to restore tx_queue
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        N/A
+ */
+void
+bt_restore_tx_queue(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	while (!skb_queue_empty(&priv->adapter->pending_queue)) {
+		skb = skb_dequeue(&priv->adapter->pending_queue);
+		if (skb)
+			skb_queue_tail(&priv->adapter->tx_queue, skb);
+	}
+	wake_up_interruptible(&priv->MainThread.waitQ);
+}
+
+/**
+ *  @brief This function used to send command to firmware
+ *
+ *  Command format:
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     OCF OGF     | Length |                Data               |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     2-byte      | 1-byte |               4-byte              |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_prepare_command(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (priv->bt_dev.hscfgcmd) {
+		priv->bt_dev.hscfgcmd = 0;
+		ret = bt_send_hscfg_cmd(priv);
+	}
+	if (priv->bt_dev.pscmd) {
+		priv->bt_dev.pscmd = 0;
+		ret = bt_enable_ps(priv);
+	}
+	if (priv->bt_dev.sdio_pull_ctrl) {
+		priv->bt_dev.sdio_pull_ctrl = 0;
+		ret = bt_send_sdio_pull_ctrl_cmd(priv);
+	}
+	if (priv->bt_dev.hscmd) {
+		priv->bt_dev.hscmd = 0;
+		if (priv->bt_dev.hsmode)
+			ret = bt_enable_hs(priv);
+		else {
+			ret = sbi_wakeup_firmware(priv);
+			priv->adapter->hs_state = HS_DEACTIVATED;
+		}
+	}
+	if (priv->bt_dev.test_mode) {
+		priv->bt_dev.test_mode = 0;
+		ret = bt_enable_test_mode(priv);
+	}
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function processes a single packet
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to skb which includes TX packet
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+send_single_packet(bt_private *priv, struct sk_buff *skb)
+{
+	int ret;
+	int has_realloc = 0;
+	ENTER();
+	if (!skb || !skb->data) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) {
+		PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len,
+		       BT_UPLD_SIZE);
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	if (skb_headroom(skb) < BT_HEADER_LEN) {
+		skb = skb_realloc_headroom(skb, BT_HEADER_LEN);
+		if (!skb) {
+			PRINTM(ERROR, "TX error: realloc_headroom failed %d\n",
+			       BT_HEADER_LEN);
+			LEAVE();
+			return BT_STATUS_FAILURE;
+		}
+		has_realloc = 1;
+	}
+	/* This is SDIO specific header length: byte[3][2][1], * type: byte[0]
+	   (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */
+	skb_push(skb, BT_HEADER_LEN);
+	skb->data[0] = (skb->len & 0x0000ff);
+	skb->data[1] = (skb->len & 0x00ff00) >> 8;
+	skb->data[2] = (skb->len & 0xff0000) >> 16;
+	skb->data[3] = bt_cb(skb)->pkt_type;
+	if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT)
+		PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n",
+		       __le16_to_cpu(*((u16 *) & skb->data[4])), skb->len);
+	ret = sbi_host_to_card(priv, skb->data, skb->len);
+	if (has_realloc)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+static void
+update_stat_byte_tx(bt_private *priv, struct sk_buff *skb)
+{
+	((struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer)->stat.
+		byte_tx += skb->len;
+}
+
+static void
+update_stat_err_tx(bt_private *priv, struct sk_buff *skb)
+{
+	((struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer)->stat.
+		err_tx++;
+}
+
+#ifdef CONFIG_OF
+/**
+ *  @brief This function read the initial parameter from device tress
+ *
+ *
+ *  @return         N/A
+ */
+static void
+bt_init_from_dev_tree(void)
+{
+	struct device_node *dt_node = NULL;
+	struct property *prop;
+	u32 data;
+	const char *string_data;
+
+	ENTER();
+
+	if (!dts_enable) {
+		PRINTM(CMD, "DTS is disabled!");
+		return;
+	}
+
+	dt_node = of_find_node_by_name(NULL, "sd8xxx-bt");
+	if (!dt_node) {
+		LEAVE();
+		return;
+	}
+	for_each_property_of_node(dt_node, prop) {
+#ifdef DEBUG_LEVEL1
+		if (!strncmp(prop->name, "mbt_drvdbg", strlen("mbt_drvdbg"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				PRINTM(CMD, "mbt_drvdbg=0x%x\n", data);
+				mbt_drvdbg = data;
+			}
+		}
+#endif
+		else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				init_cfg = (char *)string_data;
+				PRINTM(CMD, "init_cfg=%s\n", init_cfg);
+			}
+		} else if (!strncmp
+			   (prop->name, "cal_cfg_ext", strlen("cal_cfg_ext"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				cal_cfg_ext = (char *)string_data;
+				PRINTM(CMD, "cal_cfg_ext=%s\n", cal_cfg_ext);
+			}
+		} else if (!strncmp(prop->name, "cal_cfg", strlen("cal_cfg"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				cal_cfg = (char *)string_data;
+				PRINTM(CMD, "cal_cfg=%s\n", cal_cfg);
+			}
+		} else if (!strncmp(prop->name, "bt_mac", strlen("bt_mac"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				bt_mac = (char *)string_data;
+				PRINTM(CMD, "bt_mac=%s\n", bt_mac);
+			}
+		} else if (!strncmp(prop->name, "btindrst", strlen("btindrst"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				btindrst = data;
+				PRINTM(CMD, "btindrst=%d\n", btindrst);
+			}
+		} else if (!strncmp(prop->name, "btpmic", strlen("btpmic"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				btpmic = data;
+				PRINTM(CMD, "btpmic=%d\n", btpmic);
+			}
+		}
+	}
+	LEAVE();
+	return;
+}
+#endif
+
+/**
+ *  @brief This function initializes the adapter structure
+ *  and set default value to the member of adapter.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    N/A
+ */
+static void
+bt_init_adapter(bt_private *priv)
+{
+	ENTER();
+#ifdef CONFIG_OF
+	bt_init_from_dev_tree();
+#endif
+	skb_queue_head_init(&priv->adapter->tx_queue);
+	skb_queue_head_init(&priv->adapter->pending_queue);
+	priv->adapter->tx_lock = FALSE;
+	priv->adapter->ps_state = PS_AWAKE;
+	priv->adapter->suspend_fail = FALSE;
+	priv->adapter->is_suspended = FALSE;
+	priv->adapter->hs_skip = 0;
+	priv->adapter->num_cmd_timeout = 0;
+	init_waitqueue_head(&priv->adapter->cmd_wait_q);
+	LEAVE();
+}
+
+/**
+ *  @brief This function initializes firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_fw(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (fw == 0) {
+		sd_enable_host_int(priv);
+		goto done;
+	}
+	sd_disable_host_int(priv);
+	if (sbi_download_fw(priv)) {
+		PRINTM(ERROR, " FW failed to be download!\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+done:
+	LEAVE();
+	return ret;
+}
+
+#define FW_POLL_TRIES 100
+#define FW_RESET_REG  0x0EE
+#define FW_RESET_VAL  0x99
+
+/**
+ *  @brief This function reload firmware
+ *
+ *  @param priv   A pointer to bt_private
+ *  @param mode   FW reload mode
+ *
+ *  @return       0--success, otherwise failure
+ */
+static int
+bt_reload_fw(bt_private *priv, int mode)
+{
+	int ret = 0, tries = 0;
+	u8 value = 1;
+	u32 reset_reg = FW_RESET_REG;
+	u8 reset_val = FW_RESET_VAL;
+
+	ENTER();
+	if ((mode != FW_RELOAD_SDIO_INBAND_RESET) &&
+	    (mode != FW_RELOAD_NO_EMULATION)) {
+		PRINTM(ERROR, "Invalid fw reload mode=%d\n", mode);
+		return -EFAULT;
+	}
+
+    /** flush pending tx_queue */
+	skb_queue_purge(&priv->adapter->tx_queue);
+	if (mode == FW_RELOAD_SDIO_INBAND_RESET) {
+		sd_disable_host_int(priv);
+	    /** Wake up firmware firstly */
+		sbi_wakeup_firmware(priv);
+
+	/** wait SOC fully wake up */
+		for (tries = 0; tries < FW_POLL_TRIES; ++tries) {
+			ret = sd_write_reg(priv, reset_reg, 0xba);
+			if (!ret) {
+				ret = sd_read_reg(priv, reset_reg, &value);
+				if (!ret && (value == 0xba)) {
+					PRINTM(MSG, "Fw wake up\n");
+					break;
+				}
+			}
+			udelay(1000);
+		}
+
+		ret = sd_write_reg(priv, reset_reg, reset_val);
+		if (ret) {
+			PRINTM(ERROR, "Failed to write register.\n");
+			goto done;
+		}
+
+	    /** Poll register around 1 ms */
+		for (; tries < FW_POLL_TRIES; ++tries) {
+			ret = sd_read_reg(priv, reset_reg, &value);
+			if (ret) {
+				PRINTM(ERROR, "Failed to read register.\n");
+				goto done;
+			}
+			if (value == 0)
+			    /** FW is ready */
+				break;
+			udelay(1000);
+		}
+		if (value) {
+			PRINTM(ERROR,
+			       "Failed to poll FW reset register %X=0x%x\n",
+			       reset_reg, value);
+			ret = -EFAULT;
+			goto done;
+		}
+	}
+
+	sd_enable_host_int(priv);
+	/** reload FW */
+	ret = bt_init_fw(priv);
+	if (ret) {
+		PRINTM(ERROR, "Re download firmware failed.\n");
+		ret = -EFAULT;
+	}
+	LEAVE();
+	return ret;
+done:
+	sd_enable_host_int(priv);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function request to reload firmware
+ *
+ *  @param priv   A pointer to bt_private
+ *  @param mode   fw reload mode.
+ *
+ *  @return         N/A
+ */
+void
+bt_request_fw_reload(bt_private *priv, int mode)
+{
+	ENTER();
+	if (mode == FW_RELOAD_WITH_EMULATION) {
+		bt_fw_reload = FW_RELOAD_WITH_EMULATION;
+		PRINTM(MSG, "BT: FW reload with re-emulation...\n");
+		LEAVE();
+		return;
+	}
+	/** Reload FW */
+	priv->fw_reload = TRUE;
+	if (bt_reload_fw(priv, mode)) {
+		PRINTM(ERROR, "FW reload fail\n");
+		goto done;
+	}
+	priv->fw_reload = FALSE;
+	/** Other operation here? */
+done:
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function frees the structure of adapter
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    N/A
+ */
+void
+bt_free_adapter(bt_private *priv)
+{
+	bt_adapter *adapter = priv->adapter;
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	kfree(adapter->tx_buffer);
+	kfree(adapter->hw_regs_buf);
+	/* Free the adapter object itself */
+	kfree(adapter);
+	priv->adapter = NULL;
+
+	LEAVE();
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+/**
+ *  @brief This function handles the BT ioctl
+ *
+ *  @param hdev     A pointer to hci_dev structure
+ *  @cmd            ioctl cmd
+ *  @arg            argument
+ *  @return    -ENOIOCTLCMD
+ */
+static int
+bt_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+	ENTER();
+	LEAVE();
+	return -ENOIOCTLCMD;
+}
+#endif
+
+/**
+ *  @brief This function handles the wrapper_dev ioctl
+ *
+ *  @param hev     A pointer to wrapper_dev structure
+ *  @cmd            ioctl cmd
+ *  @arg            argument
+ *  @return    -ENOIOCTLCMD
+ */
+static int
+mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, void *arg)
+{
+	bt_private *priv = NULL;
+	int ret = 0;
+#ifdef BLE_WAKEUP
+	u16 len;
+#endif
+
+	ENTER();
+
+	if (!m_dev || !m_dev->driver_data) {
+		PRINTM(ERROR, "Ioctl for unknown device (m_dev=NULL)\n");
+		ret = -ENODEV;
+		goto done;
+	}
+	priv = (bt_private *)m_dev->driver_data;
+	if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+		PRINTM(ERROR, "HCI_RUNNING not set, flag=0x%lx\n",
+		       m_dev->flags);
+		ret = -EBUSY;
+		goto done;
+	}
+	PRINTM(INFO, "IOCTL: cmd=%d\n", cmd);
+	switch (cmd) {
+#ifdef BLE_WAKEUP
+	case MBTCHAR_IOCTL_BLE_WAKEUP_PARAM:
+		PRINTM(MSG, "BT: Set ble wakeup parameters\n");
+		if (copy_from_user(&len, arg, sizeof(u16))) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Fail to copy ble wakeup params length\n");
+			ret = -EFAULT;
+			goto done;
+		}
+		/** Convert little endian length */
+		len = __le16_to_cpu(len);
+		if (len < 2) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Invalid ble wakeup params len %d\n",
+			       len);
+			ret = -EFAULT;
+			goto done;
+		}
+		if ((len + sizeof(u16)) > priv->ble_wakeup_buf_size) {
+			if (priv->ble_wakeup_buf) {
+				kfree(priv->ble_wakeup_buf);
+				priv->ble_wakeup_buf = NULL;
+				priv->ble_wakeup_buf_size = 0;
+			}
+			priv->ble_wakeup_buf =
+				kzalloc(len + sizeof(u16), GFP_KERNEL);
+			if (!priv->ble_wakeup_buf) {
+				PRINTM(ERROR, "BT_IOCTL: Fail to alloc buffer\t"
+				       "for ble wakeup parameters\n");
+				ret = -ENOMEM;
+				goto done;
+			}
+			priv->ble_wakeup_buf_size = len + sizeof(u16);
+		}
+		if (copy_from_user
+		    (priv->ble_wakeup_buf, arg, len + sizeof(u16))) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Fail to copy ble wakeup params\n");
+			ret = -EFAULT;
+			goto done;
+		}
+		DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_PARAM:", priv->ble_wakeup_buf,
+			    len + sizeof(u16));
+		break;
+#endif
+	default:
+		break;
+	}
+
+done:
+#ifdef BLE_WAKEUP
+	if (ret && priv->ble_wakeup_buf) {
+		kfree(priv->ble_wakeup_buf);
+		priv->ble_wakeup_buf = NULL;
+		priv->ble_wakeup_buf_size = 0;
+	}
+#endif
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function handles BT destruct
+ *
+ *  @param hdev    A pointer to hci_dev structure
+ *
+ *  @return    N/A
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+static void
+bt_destruct(struct hci_dev *hdev)
+{
+	ENTER();
+	LEAVE();
+	return;
+}
+#endif
+
+/**
+ *  @brief This function handles wrapper device destruct
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    N/A
+ */
+static void
+mdev_destruct(struct m_dev *m_dev)
+{
+	ENTER();
+	LEAVE();
+	return;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+/**
+ *  @brief This function handles the BT transmit
+ *
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    BT_STATUS_SUCCESS or other error no.
+ */
+static int
+bt_send_frame(struct sk_buff *skb)
+#else
+/**
+ *  @brief This function handles the BT transmit
+ *
+ *  @param hdev    A pointer to hci_dev structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    BT_STATUS_SUCCESS or other error no.
+ */
+static int
+bt_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+#endif
+{
+	bt_private *priv = NULL;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+	struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+#else
+	skb->dev = (void *)hdev;
+#endif
+
+	ENTER();
+	PRINTM(DATA, "bt_send_frame %s: Type=%d, len=%d\n", hdev->name,
+	       bt_cb(skb)->pkt_type, skb->len);
+	DBG_HEXDUMP(CMD_D, "bt_send_frame", skb->data, skb->len);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+	if (!hdev || !hci_get_drvdata(hdev)) {
+#else
+	if (!hdev || !hdev->driver_data) {
+#endif
+		PRINTM(ERROR, "Frame for unknown HCI device (hdev=NULL)\n");
+		LEAVE();
+		return -ENODEV;
+	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+	priv = (bt_private *)hci_get_drvdata(hdev);
+#else
+	priv = (bt_private *)hdev->driver_data;
+#endif
+	if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+		PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n",
+		       hdev->flags);
+		LEAVE();
+		return -EBUSY;
+	}
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	}
+	if (priv->adapter->tx_lock == TRUE)
+		skb_queue_tail(&priv->adapter->pending_queue, skb);
+	else
+		skb_queue_tail(&priv->adapter->tx_queue, skb);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handles the wrapper device transmit
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    BT_STATUS_SUCCESS or other error no.
+ */
+static int
+mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb)
+{
+	bt_private *priv = NULL;
+
+	ENTER();
+	if (!m_dev || !m_dev->driver_data) {
+		PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n");
+		LEAVE();
+		return -ENODEV;
+	}
+	priv = (bt_private *)m_dev->driver_data;
+	if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+		PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n",
+		       m_dev->flags);
+		LEAVE();
+		return -EBUSY;
+	}
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		m_dev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		m_dev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		m_dev->stat.sco_tx++;
+		break;
+	}
+
+	if (priv->adapter->tx_lock == TRUE)
+		skb_queue_tail(&priv->adapter->pending_queue, skb);
+	else
+		skb_queue_tail(&priv->adapter->tx_queue, skb);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function flushes the transmit queue
+ *
+ *  @param hdev     A pointer to hci_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+bt_flush(struct hci_dev *hdev)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+	bt_private *priv = (bt_private *)hci_get_drvdata(hdev);
+#else
+	bt_private *priv = (bt_private *)hdev->driver_data;
+#endif
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	skb_queue_purge(&priv->adapter->pending_queue);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function flushes the transmit queue
+ *
+ *  @param m_dev     A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+mdev_flush(struct m_dev *m_dev)
+{
+	bt_private *priv = (bt_private *)m_dev->driver_data;
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	skb_queue_purge(&priv->adapter->pending_queue);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function closes the bluetooth device
+ *
+ *  @param hdev    A pointer to hci_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+bt_close(struct hci_dev *hdev)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+	bt_private *priv = (bt_private *)hci_get_drvdata(hdev);
+#else
+	bt_private *priv = (bt_private *)hdev->driver_data;
+#endif
+
+	ENTER();
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 0)
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) {
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+#endif
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	module_put(THIS_MODULE);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function closes the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+mdev_close(struct m_dev *m_dev)
+{
+
+	ENTER();
+	mdev_req_lock(m_dev);
+	if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) {
+		mdev_req_unlock(m_dev);
+		LEAVE();
+		return 0;
+	}
+
+	if (m_dev->flush)
+		m_dev->flush(m_dev);
+	/* wait up pending read and unregister char dev */
+	wake_up_interruptible(&m_dev->req_wait_q);
+	/* Drop queues */
+	skb_queue_purge(&m_dev->rx_q);
+	if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) {
+		mdev_req_unlock(m_dev);
+		LEAVE();
+		return 0;
+	}
+	module_put(THIS_MODULE);
+	m_dev->flags = 0;
+	mdev_req_unlock(m_dev);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function opens the bluetooth device
+ *
+ *  @param hdev    A pointer to hci_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+static int
+bt_open(struct hci_dev *hdev)
+{
+	ENTER();
+	if (try_module_get(THIS_MODULE) == 0)
+		return BT_STATUS_FAILURE;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 0)
+	set_bit(HCI_RUNNING, &hdev->flags);
+#endif
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function opens the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+static int
+mdev_open(struct m_dev *m_dev)
+{
+	ENTER();
+
+	if (try_module_get(THIS_MODULE) == 0)
+		return BT_STATUS_FAILURE;
+
+	set_bit(HCI_RUNNING, &m_dev->flags);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function queries the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @param arg     arguement
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+void
+mdev_query(struct m_dev *m_dev, void *arg)
+{
+	struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer;
+
+	ENTER();
+	if (copy_to_user(arg, &mbt_dev->type, sizeof(mbt_dev->type)))
+		PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n");
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function initializes the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+void
+init_m_dev(struct m_dev *m_dev)
+{
+	m_dev->dev_pointer = NULL;
+	m_dev->driver_data = NULL;
+	m_dev->dev_type = 0;
+	m_dev->spec_type = 0;
+	skb_queue_head_init(&m_dev->rx_q);
+	init_waitqueue_head(&m_dev->req_wait_q);
+	init_waitqueue_head(&m_dev->rx_wait_q);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+	init_MUTEX(&m_dev->req_lock);
+#else
+	sema_init(&m_dev->req_lock, 1);
+#endif
+	spin_lock_init(&m_dev->rxlock);
+	memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats));
+	m_dev->open = mdev_open;
+	m_dev->close = mdev_close;
+	m_dev->flush = mdev_flush;
+	m_dev->send = mdev_send_frame;
+	m_dev->destruct = mdev_destruct;
+	m_dev->ioctl = mdev_ioctl;
+	m_dev->query = mdev_query;
+	m_dev->owner = THIS_MODULE;
+
+}
+
+/**
+ *  @brief This function handles the major job in bluetooth driver.
+ *  it handles the event generated by firmware, rx data received
+ *  from firmware and tx data sent from kernel.
+ *
+ *  @param data    A pointer to bt_thread structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+static int
+bt_service_main_thread(void *data)
+{
+	bt_thread *thread = data;
+	bt_private *priv = thread->priv;
+	bt_adapter *adapter = priv->adapter;
+	wait_queue_t wait;
+	struct sk_buff *skb;
+	ENTER();
+	bt_activate_thread(thread);
+	init_waitqueue_entry(&wait, current);
+	current->flags |= PF_NOFREEZE;
+
+	for (;;) {
+		add_wait_queue(&thread->waitQ, &wait);
+		OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE);
+		if (priv->adapter->WakeupTries ||
+		    ((!priv->adapter->IntCounter) &&
+		     (!priv->bt_dev.tx_dnld_rdy ||
+		      skb_queue_empty(&priv->adapter->tx_queue)))) {
+			PRINTM(INFO, "Main: Thread sleeping...\n");
+			schedule();
+		}
+		OS_SET_THREAD_STATE(TASK_RUNNING);
+		remove_wait_queue(&thread->waitQ, &wait);
+		if (kthread_should_stop() || adapter->SurpriseRemoved) {
+			PRINTM(INFO, "main-thread: break from main thread: "
+			       "SurpriseRemoved=0x%x\n",
+			       adapter->SurpriseRemoved);
+			break;
+		}
+
+		PRINTM(INFO, "Main: Thread waking up...\n");
+
+		if (priv->adapter->IntCounter) {
+			OS_INT_DISABLE;
+			adapter->IntCounter = 0;
+			OS_INT_RESTORE;
+			sbi_get_int_status(priv);
+		} else if ((priv->adapter->ps_state == PS_SLEEP) &&
+			   !skb_queue_empty(&priv->adapter->tx_queue)) {
+			priv->adapter->WakeupTries++;
+			sbi_wakeup_firmware(priv);
+			continue;
+		}
+		if (priv->adapter->ps_state == PS_SLEEP)
+			continue;
+		if (priv->bt_dev.tx_dnld_rdy == TRUE) {
+			if (!skb_queue_empty(&priv->adapter->tx_queue)) {
+				skb = skb_dequeue(&priv->adapter->tx_queue);
+				if (skb) {
+					if (send_single_packet(priv, skb))
+						update_stat_err_tx(priv, skb);
+					else
+						update_stat_byte_tx(priv, skb);
+					kfree_skb(skb);
+				}
+			}
+		}
+	}
+	bt_deactivate_thread(thread);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handles the interrupt. it will change PS
+ *  state if applicable. it will wake up main_thread to handle
+ *  the interrupt event as well.
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @return        N/A
+ */
+void
+bt_interrupt(struct m_dev *m_dev)
+{
+	bt_private *priv = (bt_private *)m_dev->driver_data;
+	ENTER();
+	if (!priv || !priv->adapter) {
+		LEAVE();
+		return;
+	}
+	PRINTM(INTR, "*\n");
+	priv->adapter->ps_state = PS_AWAKE;
+	if (priv->adapter->hs_state == HS_ACTIVATED) {
+		PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name);
+		priv->adapter->hs_state = HS_DEACTIVATED;
+	}
+	priv->adapter->WakeupTries = 0;
+	priv->adapter->IntCounter++;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	LEAVE();
+}
+
+static void
+bt_private_dynamic_release(struct kobject *kobj)
+{
+	bt_private *priv = container_of(kobj, bt_private, kobj);
+	ENTER();
+	PRINTM(INFO, "free bt priv\n");
+	kfree(priv);
+	LEAVE();
+}
+
+static struct kobj_type ktype_bt_private_dynamic = {
+	.release = bt_private_dynamic_release,
+};
+
+static bt_private *
+bt_alloc_priv(void)
+{
+	bt_private *priv;
+	ENTER();
+	priv = kzalloc(sizeof(bt_private), GFP_KERNEL);
+	if (priv) {
+		kobject_init(&priv->kobj, &ktype_bt_private_dynamic);
+		PRINTM(INFO, "alloc bt priv\n");
+	}
+	LEAVE();
+	return priv;
+}
+
+struct kobject *
+bt_priv_get(bt_private *priv)
+{
+	PRINTM(INFO, "bt priv get object");
+	return kobject_get(&priv->kobj);
+}
+
+void
+bt_priv_put(bt_private *priv)
+{
+	PRINTM(INFO, "bt priv put object");
+	kobject_put(&priv->kobj);
+}
+
+/**
+ *  @brief This function send init commands to firmware
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @return       BT_STATUS_SUCESS/BT_STATUS_FAILURE
+ */
+int
+bt_init_cmd(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+	if (ret < 0) {
+		PRINTM(FATAL, "Module cfg command send failed!\n");
+		goto done;
+	}
+	if (btindrst != -1) {
+		ret = bt_set_independent_reset(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "Independent reset failed!\n");
+			goto done;
+		}
+	}
+	if (btpmic) {
+		if (BT_STATUS_SUCCESS != bt_pmic_configure(priv)) {
+			PRINTM(FATAL, "BT: PMIC Configure failed \n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	ret = bt_set_ble_deepsleep(priv, TRUE);
+	if (ret < 0) {
+		PRINTM(FATAL, "Enable BLE deepsleep failed!\n");
+		goto done;
+	}
+	if (psmode) {
+		priv->bt_dev.psmode = TRUE;
+		priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME;
+		ret = bt_enable_ps(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "Enable PS mode failed!\n");
+			goto done;
+		}
+	}
+#if defined(SDIO_SUSPEND_RESUME)
+	priv->bt_dev.gpio_gap = DEF_GPIO_GAP;
+	ret = bt_send_hscfg_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "Send HSCFG failed!\n");
+		goto done;
+	}
+#endif
+	priv->bt_dev.sdio_pull_cfg = 0xffffffff;
+	priv->bt_dev.sdio_pull_ctrl = 0;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reinit firmware after redownload firmware
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @return       BT_STATUS_SUCESS/BT_STATUS_FAILURE
+ */
+int
+bt_reinit_fw(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	priv->adapter->tx_lock = FALSE;
+	priv->adapter->ps_state = PS_AWAKE;
+	priv->adapter->suspend_fail = FALSE;
+	priv->adapter->is_suspended = FALSE;
+	priv->adapter->hs_skip = 0;
+	priv->adapter->num_cmd_timeout = 0;
+
+	ret = bt_init_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "BT init command failed!\n");
+		goto done;
+	}
+	/* block all the packet from bluez */
+	if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext)
+		priv->adapter->tx_lock = TRUE;
+
+	if (init_cfg)
+		if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+			PRINTM(FATAL,
+			       "BT: Set user init data and param failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+
+	if (cal_cfg) {
+		if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+			PRINTM(FATAL, "BT: Set cal data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	if (bt_mac) {
+		PRINTM(INFO,
+		       "Set BT mac_addr from insmod parametre bt_mac = %s\n",
+		       bt_mac);
+		if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) {
+			PRINTM(FATAL,
+			       "BT: Fail to set mac address from insmod parametre\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	if (cal_cfg_ext) {
+		if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) {
+			PRINTM(FATAL, "BT: Set cal ext data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) {
+		priv->adapter->tx_lock = FALSE;
+		bt_restore_tx_queue(priv);
+	}
+	bt_get_fw_version(priv);
+	snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN,
+		 mbt_driver_version, fw_version);
+done:
+	return ret;
+}
+
+/**
+ *  @brief Module configuration and register device
+ *
+ *  @param priv      A Pointer to bt_private structure
+ *  @return      BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_conf_dpc(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct hci_dev *hdev = NULL;
+	unsigned char dev_type = 0;
+
+	ENTER();
+
+	priv->bt_dev.tx_dnld_rdy = TRUE;
+
+	if (priv->fw_reload) {
+		bt_reinit_fw(priv);
+		LEAVE();
+		return ret;
+	}
+
+	if (drv_mode & DRV_MODE_BT) {
+		hdev = hci_alloc_dev();
+		if (!hdev) {
+			PRINTM(FATAL, "Can not allocate HCI device\n");
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		hdev->open = bt_open;
+		hdev->close = bt_close;
+		hdev->flush = bt_flush;
+		hdev->send = bt_send_frame;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+		hdev->destruct = bt_destruct;
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+		hdev->ioctl = bt_ioctl;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
+		hci_set_drvdata(hdev, priv);
+#else
+		hdev->driver_data = priv;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
+		hdev->owner = THIS_MODULE;
+#endif
+		priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE;
+		priv->bt_dev.m_dev[BT_SEQ].spec_type = BLUEZ_SPEC;
+		priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)hdev;
+		priv->bt_dev.m_dev[BT_SEQ].driver_data = priv;
+		priv->bt_dev.m_dev[BT_SEQ].read_continue_flag = 0;
+	}
+
+	dev_type = HCI_SDIO;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
+	if (hdev)
+		hdev->bus = dev_type;
+#else
+	if (hdev)
+		hdev->type = dev_type;
+#endif /* >= 2.6.34 */
+
+	ret = bt_init_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "BT init command failed!\n");
+		goto done;
+	}
+
+	/** Process device tree init parameters before register hci device.
+	 *  Since uplayer device has not yet registered, no need to block tx queue.
+	 * */
+	if (init_cfg)
+		if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+			PRINTM(FATAL,
+			       "BT: Set user init data and param failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	if (cal_cfg) {
+		if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+			PRINTM(FATAL, "BT: Set cal data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	} else if (bt_mac) {
+		PRINTM(INFO,
+		       "Set BT mac_addr from insmod parametre bt_mac = %s\n",
+		       bt_mac);
+		if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) {
+			PRINTM(FATAL,
+			       "BT: Fail to set mac address from insmod parametre\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cal_cfg_ext) {
+		if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) {
+			PRINTM(FATAL, "BT: Set cal ext data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	/* Get FW version */
+	bt_get_fw_version(priv);
+	snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN,
+		 mbt_driver_version, fw_version);
+
+	if (hdev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
+		hdev->dev_type = priv->bt_dev.devType;
+#endif
+		ret = hci_register_dev(hdev);
+		if (ret < 0) {
+			PRINTM(FATAL, "Can not register HCI device\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		snprintf((char *)priv->bt_dev.m_dev[BT_SEQ].name,
+			 sizeof(priv->bt_dev.m_dev[BT_SEQ].name), hdev->name);
+		bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ);
+	}
+
+done:
+	LEAVE();
+	return ret;
+err_kmalloc:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function adds the card. it will probe the
+ *  card, allocate the bt_priv and initialize the device.
+ *
+ *  @param card    A pointer to card
+ *  @return        A pointer to bt_private structure
+ */
+
+bt_private *
+bt_add_card(void *card)
+{
+	bt_private *priv = NULL;
+
+	ENTER();
+
+	priv = bt_alloc_priv();
+	if (!priv) {
+		PRINTM(FATAL, "Can not allocate priv\n");
+		LEAVE();
+		return NULL;
+	}
+
+	/* allocate buffer for bt_adapter */
+	priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL);
+	if (!priv->adapter) {
+		PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->tx_buffer =
+		kzalloc(MAX_TX_BUF_SIZE + DMA_ALIGNMENT, GFP_KERNEL);
+	if (!priv->adapter->tx_buffer) {
+		PRINTM(FATAL, "Allocate buffer for transmit\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->tx_buf =
+		(u8 *)ALIGN_ADDR(priv->adapter->tx_buffer, DMA_ALIGNMENT);
+	priv->adapter->hw_regs_buf =
+		kzalloc(SD_BLOCK_SIZE + DMA_ALIGNMENT, GFP_KERNEL);
+	if (!priv->adapter->hw_regs_buf) {
+		PRINTM(FATAL, "Allocate buffer for INT read buf failed!\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->hw_regs =
+		(u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, DMA_ALIGNMENT);
+	bt_init_adapter(priv);
+
+	PRINTM(INFO, "Starting kthread...\n");
+	priv->MainThread.priv = priv;
+	spin_lock_init(&priv->driver_lock);
+
+	bt_create_thread(bt_service_main_thread, &priv->MainThread,
+			 "bt_main_service");
+
+	/* wait for mainthread to up */
+	while (!priv->MainThread.pid)
+		os_sched_timeout(1);
+
+	/** user config file */
+	init_waitqueue_head(&priv->init_user_conf_wait_q);
+
+	priv->bt_dev.card = card;
+
+	((struct sdio_mmc_card *)card)->priv = priv;
+	priv->adapter->sd_ireg = 0;
+	/*
+	 * Register the device. Fillup the private data structure with
+	 * relevant information from the card and request for the required
+	 * IRQ.
+	 */
+	if (sbi_register_dev(priv) < 0) {
+		PRINTM(FATAL, "Failed to register bt device!\n");
+		goto err_registerdev;
+	}
+	if (bt_init_fw(priv)) {
+		PRINTM(FATAL, "BT Firmware Init Failed\n");
+		goto err_init_fw;
+	}
+	LEAVE();
+	return priv;
+
+err_init_fw:
+	clean_up_m_devs(priv);
+	bt_proc_remove(priv);
+	PRINTM(INFO, "Unregister device\n");
+	sbi_unregister_dev(priv);
+err_registerdev:
+	((struct sdio_mmc_card *)card)->priv = NULL;
+	/* Stop the thread servicing the interrupts */
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid)
+		os_sched_timeout(1);
+err_kmalloc:
+	if (priv->adapter)
+		bt_free_adapter(priv);
+	bt_priv_put(priv);
+	LEAVE();
+	return NULL;
+}
+
+/**
+ *  @brief This function send hardware remove event
+ *
+ *  @param priv    A pointer to bt_private
+ *  @return        N/A
+ */
+void
+bt_send_hw_remove_event(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	struct hci_dev *hdev = NULL;
+	ENTER();
+	if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		LEAVE();
+		return;
+	}
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC)
+		hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer;
+#define HCI_HARDWARE_ERROR_EVT  0x10
+#define HCI_HARDWARE_REMOVE     0x24
+	skb = bt_skb_alloc(3, GFP_ATOMIC);
+	skb->data[0] = HCI_HARDWARE_ERROR_EVT;
+	skb->data[1] = 1;
+	skb->data[2] = HCI_HARDWARE_REMOVE;
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+	skb_put(skb, 3);
+	if (hdev) {
+		skb->dev = (void *)hdev;
+		PRINTM(MSG, "Send HW ERROR event\n");
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+		hci_recv_frame(skb);
+#else
+		hci_recv_frame(hdev, skb);
+#endif
+		os_sched_timeout(5);
+		hdev->stat.byte_rx += 3;
+	}
+	LEAVE();
+	return;
+}
+
+#ifdef BLE_WAKEUP
+/**
+ *  @brief This function used to config BLE wakeup pattern
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        N/A
+ */
+int
+bt_config_ble_wakeup(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sk_buff *skb = NULL;
+	u16 ocf, left_len;
+	u8 len, more_cmd;
+	u8 *pos;
+
+	ENTER();
+
+	if (!priv->ble_wakeup_buf) {
+		PRINTM(ERROR, "BT: no ble wakeup parameters found\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	PRINTM(MSG, "Config ble wakeup pattern\n");
+
+	pos = priv->ble_wakeup_buf;
+	left_len = *(u16 *) pos;
+	left_len = __le16_to_cpu(left_len);
+	pos += sizeof(u16);
+
+	while (left_len >= 2) {
+		more_cmd = *pos;
+		len = *(pos + 1);
+		if (((len + 2) > left_len) ||
+		    (!more_cmd && ((len + 2) < left_len))) {
+			PRINTM(ERROR, "Invalid ble parameters\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		skb = bt_skb_alloc(len, GFP_ATOMIC);
+		if (!skb) {
+			PRINTM(ERROR, "BT BLE WAKEUP: fail to alloc skb\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		memcpy(skb_put(skb, len), pos + 2, len);
+		bt_cb(skb)->pkt_type = *(u8 *)skb->data;
+		skb_pull(skb, 1);
+		DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_CMD:", skb->data, skb->len);
+		skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+		skb_queue_head(&priv->adapter->tx_queue, skb);
+		priv->bt_dev.sendcmdflag = TRUE;
+		priv->bt_dev.send_cmd_opcode = *(u16 *) skb->data;
+		ocf = hci_opcode_ocf(priv->bt_dev.send_cmd_opcode);
+		priv->adapter->cmd_complete = FALSE;
+
+		wake_up_interruptible(&priv->MainThread.waitQ);
+		if (!os_wait_interruptible_timeout
+		    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+		     WAIT_UNTIL_CMD_RESP)) {
+			ret = BT_STATUS_FAILURE;
+			PRINTM(ERROR,
+			       "BT: Set  Set ble wakeup cmd 0x%x timeout:\n",
+			       priv->bt_dev.send_cmd_opcode);
+			bt_cmd_timeout_func(priv, ocf);
+			goto done;
+		}
+
+		pos += len + 2;
+		left_len -= len + 2;
+	}
+
+done:
+	if (ret != BT_STATUS_SUCCESS) {
+		if (priv->ble_wakeup_buf) {
+			kfree(priv->ble_wakeup_buf);
+			priv->ble_wakeup_buf = NULL;
+			priv->ble_wakeup_buf_size = 0;
+		}
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function send system suspend event
+ *
+ *  @param priv    A pointer to bt_private
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_send_system_event(bt_private *priv, u8 flag)
+{
+	struct sk_buff *skb = NULL;
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+
+	ENTER();
+
+	if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+
+	skb = bt_skb_alloc(4, GFP_ATOMIC);
+	if (!skb) {
+		PRINTM(ERROR, "Fail to allocate sys suspend event skb\n");
+		return BT_STATUS_FAILURE;
+	}
+	skb->data[0] = VENDOR_SPECIFIC_EVENT;
+	skb->data[1] = 2;
+	skb->data[2] = HCI_SYSTEM_SUSPEND_EVT;
+	if (flag)
+		skb->data[3] = HCI_SYSTEM_SUSPEND;
+	else
+		skb->data[3] = HCI_SYSTEM_RESUME;
+
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+	skb_put(skb, 4);
+	if (mbt_dev) {
+		skb->dev = (void *)mdev_bt;
+		PRINTM(MSG, "Send system %s event\n",
+		       flag ? "suspend" : "resume");
+		if (!mdev_recv_frame(skb)) {
+#define RX_WAIT_TIMEOUT                         300
+			mdev_bt->wait_rx_complete = TRUE;
+			mdev_bt->rx_complete_flag = FALSE;
+			if (os_wait_interruptible_timeout(mdev_bt->rx_wait_q,
+							  mdev_bt->
+							  rx_complete_flag,
+							  RX_WAIT_TIMEOUT))
+				PRINTM(MSG, "BT stack received the event\n");
+			mdev_bt->stat.byte_rx += 4;
+		}
+	}
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+#endif
+
+/**
+ *  @brief This function removes the card.
+ *
+ *  @param card    A pointer to card
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_remove_card(void *card)
+{
+	bt_private *priv = (bt_private *)card;
+	ENTER();
+	if (!priv) {
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+	if (!priv->adapter->SurpriseRemoved) {
+		if (BT_STATUS_SUCCESS == bt_send_reset_command(priv))
+			bt_send_module_cfg_cmd(priv, MODULE_SHUTDOWN_REQ);
+		/* Disable interrupts on the card */
+		sd_disable_host_int(priv);
+		priv->adapter->SurpriseRemoved = TRUE;
+	}
+	bt_send_hw_remove_event(priv);
+#ifdef BLE_WAKEUP
+	if (priv->ble_wakeup_buf) {
+		kfree(priv->ble_wakeup_buf);
+		priv->ble_wakeup_buf = NULL;
+		priv->ble_wakeup_buf_size = 0;
+	}
+#endif
+	wake_up_interruptible(&priv->adapter->cmd_wait_q);
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid) {
+		os_sched_timeout(1);
+		wake_up_interruptible(&priv->MainThread.waitQ);
+	}
+
+	bt_proc_remove(priv);
+	PRINTM(INFO, "Unregister device\n");
+	sbi_unregister_dev(priv);
+	clean_up_m_devs(priv);
+	PRINTM(INFO, "Free Adapter\n");
+	bt_free_adapter(priv);
+	bt_priv_put(priv);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function initializes module.
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_module(void)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	PRINTM(MSG, "BT: Loading driver\n");
+
+	bt_root_proc_init();
+
+	/** create char device class */
+	chardev_class = class_create(THIS_MODULE, MODULE_NAME);
+	if (IS_ERR(chardev_class)) {
+		PRINTM(ERROR, "Unable to allocate class\n");
+		bt_root_proc_remove();
+		ret = PTR_ERR(chardev_class);
+		goto done;
+	}
+
+	if (sbi_register() == NULL) {
+		bt_root_proc_remove();
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+done:
+	if (ret)
+		PRINTM(MSG, "BT: Driver loading failed\n");
+	else
+		PRINTM(MSG, "BT: Driver loaded successfully\n");
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function cleans module
+ *
+ *  @return        N/A
+ */
+static void
+bt_exit_module(void)
+{
+	ENTER();
+	PRINTM(MSG, "BT: Unloading driver\n");
+	sbi_unregister();
+
+	bt_root_proc_remove();
+	class_destroy(chardev_class);
+	PRINTM(MSG, "BT: Driver unloaded\n");
+	LEAVE();
+}
+
+module_init(bt_init_module);
+module_exit(bt_exit_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+module_param(fw, int, 0);
+MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware");
+module_param(psmode, int, 0);
+MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode");
+#ifdef CONFIG_OF
+module_param(dts_enable, int, 0);
+MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS");
+#endif
+#ifdef	DEBUG_LEVEL1
+module_param(mbt_drvdbg, uint, 0);
+MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL");
+#endif
+#ifdef SDIO_SUSPEND_RESUME
+module_param(mbt_pm_keep_power, int, 0);
+MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power");
+#endif
+module_param(init_cfg, charp, 0);
+MODULE_PARM_DESC(init_cfg, "BT init config file name");
+module_param(cal_cfg, charp, 0);
+MODULE_PARM_DESC(cal_cfg, "BT calibrate file name");
+module_param(cal_cfg_ext, charp, 0);
+MODULE_PARM_DESC(cal_cfg_ext, "BT calibrate ext file name");
+module_param(bt_mac, charp, 0660);
+MODULE_PARM_DESC(bt_mac, "BT init mac address");
+module_param(drv_mode, int, 0);
+MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE;");
+module_param(bt_fw_reload, int, 0);
+MODULE_PARM_DESC(bt_fw_reload,
+		 "0: disable fw_reload; 1: enable fw reload feature");
+module_param(btindrst, int, 0);
+MODULE_PARM_DESC(btindrst,
+		 "Independent reset configuration; high byte:GPIO pin number;low byte:0x0:disable, 0x1:out-band reset, 0x2:in-band reset.");
+module_param(btpmic, int, 0);
+MODULE_PARM_DESC(btpmic,
+		 "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware (default)");
+module_param(bt_fw_serial, int, 0);
+MODULE_PARM_DESC(bt_fw_serial,
+		 "0: Support parallel download FW; 1: Support serial download FW");
diff --git a/bt_sd8997/bt/bt_proc.c b/bt_sd8997/bt/bt_proc.c
new file mode 100644
index 0000000..c401791
--- /dev/null
+++ b/bt_sd8997/bt/bt_proc.c
@@ -0,0 +1,881 @@
+/** @file bt_proc.c
+  *
+  * @brief This file handle the functions for proc files
+  *
+  * Copyright (C) 2007-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/proc_fs.h>
+
+#include "bt_drv.h"
+#include "bt_sdio.h"
+
+/** proc diretory root */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+#define PROC_DIR NULL
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+#define PROC_DIR (&proc_root)
+#else
+#define PROC_DIR proc_net
+#endif
+
+/** Proc mbt directory entry */
+static struct proc_dir_entry *proc_mbt;
+
+#define     CMD52_STR_LEN   50
+static char cmd52_string[CMD52_STR_LEN];
+
+struct proc_data {
+	/** Read length */
+	int rdlen;
+	/** Read buffer */
+	char *rdbuf;
+	/** Write length */
+	int wrlen;
+	/** Maximum write length */
+	int maxwrlen;
+	/** Write buffer */
+	char *wrbuf;
+	/** Private structure */
+	struct _bt_private *pbt;
+	void (*on_close) (struct inode *, struct file *);
+};
+
+/** Default file permission */
+#define DEFAULT_FILE_PERM  0644
+
+/** Bluetooth device offset */
+#define OFFSET_BT_DEV		0x01
+/** Bluetooth adapter offset */
+#define OFFSET_BT_ADAPTER	0x02
+/** Show integer */
+#define SHOW_INT		0x10
+/** Show hex */
+#define SHOW_HEX		0x20
+/** Show string */
+#define SHOW_STRING		0x40
+
+/** Device size */
+#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n)
+/** Device address */
+#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n)
+
+/** Adapter size */
+#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n)
+/** Adapter address */
+#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n)
+
+static struct item_data config_items[] = {
+#ifdef	DEBUG_LEVEL1
+	{"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX}
+	,
+#endif
+	{"idle_timeout", item_dev_size(idle_timeout), 0,
+	 item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap),
+	 OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0,
+	 item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0,
+	 item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+
+};
+
+static struct item_data status_items[] = {
+	{"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver),
+	 OFFSET_BT_ADAPTER | SHOW_STRING},
+	{"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0,
+	 item_dev_addr(tx_dnld_rdy),
+	 OFFSET_BT_DEV | SHOW_INT},
+	{"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"hs_state", item_adapter_size(hs_state), 0,
+	 item_adapter_addr(hs_state),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"ps_state", item_adapter_size(ps_state), 0,
+	 item_adapter_addr(ps_state),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"WakeupTries", item_adapter_size(WakeupTries), 0,
+	 item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT},
+	{"irq_recv", item_adapter_size(irq_recv), 0,
+	 item_adapter_addr(irq_recv),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"irq_done", item_adapter_size(irq_done), 0,
+	 item_adapter_addr(irq_done),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"skb_pending", item_adapter_size(skb_pending), 0,
+	 item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT},
+};
+
+static struct item_data debug_items[] = {
+	{"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING},
+};
+
+/**
+ *  @brief convert string to number
+ *
+ *  @param s	pointer to numbered string
+ *  @return	converted number from string s
+ */
+int
+string_to_number(char *s)
+{
+	int r = 0;
+	int base = 0;
+	int pn = 1;
+
+	if (strncmp(s, "-", 1) == 0) {
+		pn = -1;
+		s++;
+	}
+	if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) {
+		base = 16;
+		s += 2;
+	} else
+		base = 10;
+
+	for (s = s; *s != 0; s++) {
+		if ((*s >= '0') && (*s <= '9'))
+			r = (r * base) + (*s - '0');
+		else if ((*s >= 'A') && (*s <= 'F'))
+			r = (r * base) + (*s - 'A' + 10);
+		else if ((*s >= 'a') && (*s <= 'f'))
+			r = (r * base) + (*s - 'a' + 10);
+		else
+			break;
+	}
+
+	return r * pn;
+}
+
+/**
+ *  @brief Create cmd52 string
+ *
+ *  @param priv	A pointer to bt_private structure
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+form_cmd52_string(bt_private *priv)
+{
+	ENTER();
+
+	memset(cmd52_string, 0, CMD52_STR_LEN);
+	snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X",
+		 priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg,
+		 priv->bt_dev.cmd52_val);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/*
+ *  @brief Parse cmd52 string
+ *
+ *  @param buffer  A pointer user buffer
+ *  @param len     Length of user buffer
+ *  @param func    Parsed func number
+ *  @param reg     Parsed reg value
+ *  @param val     Parsed value to set
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+parse_cmd52_string(const char __user * buffer, size_t len,
+		   int *func, int *reg, int *val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	char *string = NULL;
+	char *pos = NULL;
+
+	ENTER();
+
+	string = kzalloc(CMD52_STR_LEN, GFP_KERNEL);
+	if (!string) {
+		PRINTM(ERROR, "BT: Can not alloc mem for cmd52 string\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+	memcpy(string, buffer + strlen("sdcmd52rw="),
+	       len - strlen("sdcmd52rw="));
+	string = strstrip(string);
+
+	*func = -1;
+	*reg = -1;
+	*val = -1;
+
+	/* Get func */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*func = string_to_number(pos);
+
+	/* Get reg */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*reg = string_to_number(pos);
+
+	/* Get val (optional) */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*val = string_to_number(pos);
+	kfree(string);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function handle generic proc file close
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+proc_close(struct inode *inode, struct file *file)
+{
+	struct proc_data *pdata = file->private_data;
+	ENTER();
+	if (pdata) {
+		if (pdata->on_close != NULL)
+			pdata->on_close(inode, file);
+		kfree(pdata->rdbuf);
+		kfree(pdata->wrbuf);
+		kfree(pdata);
+	}
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handle generic proc file read
+ *
+ *  @param file    A pointer to file structure
+ *  @param buffer  A pointer to output buffer
+ *  @param len     number of byte to read
+ *  @param offset  A pointer to offset of file
+ *  @return		number of output data
+ */
+static ssize_t
+proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset)
+{
+	loff_t pos = *offset;
+	struct proc_data *pdata = (struct proc_data *)file->private_data;
+	if ((!pdata->rdbuf) || (pos < 0))
+		return -EINVAL;
+	if (pos >= pdata->rdlen)
+		return 0;
+	if (len > pdata->rdlen - pos)
+		len = pdata->rdlen - pos;
+	if (copy_to_user(buffer, pdata->rdbuf + pos, len))
+		return -EFAULT;
+	*offset = pos + len;
+	return len;
+}
+
+/**
+ *  @brief This function handle generic proc file write
+ *
+ *  @param file    A pointer to file structure
+ *  @param buffer  A pointer to input buffer
+ *  @param len     number of byte to write
+ *  @param offset  A pointer to offset of file
+ *  @return		number of input data
+ */
+static ssize_t
+proc_write(struct file *file,
+	   const char __user * buffer, size_t len, loff_t * offset)
+{
+	loff_t pos = *offset;
+	struct proc_data *pdata = (struct proc_data *)file->private_data;
+	int func = 0, reg = 0, val = 0;
+	int config_data = 0;
+	char *line = NULL;
+
+	if (!pdata->wrbuf || (pos < 0))
+		return -EINVAL;
+	if (pos >= pdata->maxwrlen)
+		return 0;
+	if (len > pdata->maxwrlen - pos)
+		len = pdata->maxwrlen - pos;
+	if (copy_from_user(pdata->wrbuf + pos, buffer, len))
+		return -EFAULT;
+	if (!strncmp(pdata->wrbuf + pos, "fw_reload", strlen("fw_reload"))) {
+		if (!strncmp
+		    (pdata->wrbuf + pos, "fw_reload=", strlen("fw_reload="))) {
+			line = pdata->wrbuf + pos;
+			line += strlen("fw_reload") + 1;
+			config_data = string_to_number(line);
+		} else
+			config_data = FW_RELOAD_SDIO_INBAND_RESET;
+		PRINTM(MSG, "Request fw_reload=%d\n", config_data);
+		bt_request_fw_reload(pdata->pbt, config_data);
+	}
+	if (!strncmp(pdata->wrbuf + pos, "sdcmd52rw=", strlen("sdcmd52rw="))) {
+		parse_cmd52_string(pdata->wrbuf + pos, len, &func, &reg, &val);
+		sd_write_cmd52_val(pdata->pbt, func, reg, val);
+	}
+	if (!strncmp(pdata->wrbuf + pos, "debug_dump", strlen("debug_dump"))) {
+		bt_dump_sdio_regs(pdata->pbt);
+		bt_dump_firmware_info_v2(pdata->pbt);
+	}
+
+	if (pos + len > pdata->wrlen)
+		pdata->wrlen = len + file->f_pos;
+	*offset = pos + len;
+	return len;
+}
+
+/**
+ *  @brief This function handle the generic file close
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return		N/A
+ */
+static void
+proc_on_close(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	struct proc_private_data *priv = PDE_DATA(inode);
+#else
+	struct proc_private_data *priv = PDE(inode)->data;
+#endif
+	struct proc_data *pdata = file->private_data;
+	char *line;
+	int i;
+	ENTER();
+	if (!pdata->wrlen)
+		return;
+	line = pdata->wrbuf;
+	while (line[0]) {
+		for (i = 0; i < priv->num_items; i++) {
+			if (!strncmp
+			    (line, priv->pdata[i].name,
+			     strlen(priv->pdata[i].name))) {
+				line += strlen(priv->pdata[i].name) + 1;
+				if (priv->pdata[i].size == 1)
+					*((u8 *)priv->pdata[i].addr) =
+						(u8)string_to_number(line);
+				else if (priv->pdata[i].size == 2)
+					*((u16 *) priv->pdata[i].addr) =
+						(u16) string_to_number(line);
+				else if (priv->pdata[i].size == 4)
+					*((u32 *)priv->pdata[i].addr) =
+						(u32)string_to_number(line);
+			}
+		}
+		while (line[0] && line[0] != '\n')
+			line++;
+		if (line[0])
+			line++;
+	}
+	if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd
+	    || priv->pbt->bt_dev.sdio_pull_ctrl
+	    || priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) {
+		bt_prepare_command(priv->pbt);
+		wake_up_interruptible(&priv->pbt->MainThread.waitQ);
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function handle the generic file open
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return	BT_STATUS_SUCCESS or other error no.
+ */
+static int
+proc_open(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	struct proc_private_data *priv = PDE_DATA(inode);
+#else
+	struct proc_private_data *priv = PDE(inode)->data;
+#endif
+	struct proc_data *pdata;
+	int i;
+	char *p;
+	u32 val = 0;
+	ENTER();
+	priv->pbt->adapter->skb_pending =
+		skb_queue_len(&priv->pbt->adapter->tx_queue);
+	file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL);
+	if (file->private_data == NULL) {
+		PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+	pdata = (struct proc_data *)file->private_data;
+	pdata->pbt = priv->pbt;
+	pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL);
+	if (pdata->rdbuf == NULL) {
+		PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n");
+		kfree(file->private_data);
+		LEAVE();
+		return -ENOMEM;
+	}
+	if (priv->fileflag == DEFAULT_FILE_PERM) {
+		pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL);
+		if (pdata->wrbuf == NULL) {
+			PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n");
+			kfree(pdata->rdbuf);
+			kfree(file->private_data);
+			return -ENOMEM;
+		}
+		pdata->maxwrlen = priv->bufsize;
+		pdata->on_close = proc_on_close;
+	}
+	p = pdata->rdbuf;
+	for (i = 0; i < priv->num_items; i++) {
+		if (priv->pdata[i].size == 1)
+			val = *((u8 *)priv->pdata[i].addr);
+		else if (priv->pdata[i].size == 2)
+			val = *((u16 *) priv->pdata[i].addr);
+		else if (priv->pdata[i].size == 4)
+			val = *((u32 *)priv->pdata[i].addr);
+		if (priv->pdata[i].flag & SHOW_INT)
+			p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val);
+		else if (priv->pdata[i].flag & SHOW_HEX)
+			p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val);
+		else if (priv->pdata[i].flag & SHOW_STRING) {
+			if (!strncmp
+			    (priv->pdata[i].name, "sdcmd52rw",
+			     strlen("sdcmd52rw"))) {
+				sd_read_cmd52_val(priv->pbt);
+				form_cmd52_string(priv->pbt);
+			}
+			p += sprintf(p, "%s=%s\n", priv->pdata[i].name,
+				     (char *)priv->pdata[i].addr);
+		}
+	}
+	pdata->rdlen = strlen(pdata->rdbuf);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+static const struct file_operations proc_read_ops = {
+	.read = proc_read,
+	.open = proc_open,
+	.release = proc_close
+};
+
+static const struct file_operations proc_rw_ops = {
+	.read = proc_read,
+	.write = proc_write,
+	.open = proc_open,
+	.release = proc_close
+};
+
+static struct proc_private_data proc_files[] = {
+	{"status", S_IRUGO, 1024,
+	 sizeof(status_items) / sizeof(status_items[0]),
+	 &status_items[0], NULL, &proc_read_ops}
+	,
+	{"config", DEFAULT_FILE_PERM, 512,
+	 sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL,
+	 &proc_rw_ops}
+	,
+	{"debug", DEFAULT_FILE_PERM, 512,
+	 sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL,
+	 &proc_rw_ops}
+	,
+};
+
+/**
+ *  @brief Proc read function for histogram
+ *
+ *  @param sfp     pointer to seq_file structure
+ *  @param data
+ *
+ *  @return        Number of output data or MLAN_STATUS_FAILURE
+ */
+static int
+bt_histogram_read(struct seq_file *sfp, void *data)
+{
+	bt_hist_proc_data *pdata = (bt_hist_proc_data *) sfp->private;
+	bt_private *priv = (bt_private *)pdata->pbt;
+	u8 ant_num;
+	int i, j;
+
+	ENTER();
+	if (!priv) {
+		LEAVE();
+		return -EFAULT;
+	}
+	bt_get_histogram(priv);
+	ant_num = priv->hist_data_len / sizeof(bt_histogram_data);
+	seq_printf(sfp, "BT histogram:\n");
+	seq_printf(sfp, "antenna: 0=2.4G antenna a,  1=2.4G antenna b\n\n");
+	if (ant_num < 1) {
+		seq_printf(sfp, "no histogram data from FW\n");
+		LEAVE();
+		return 0;
+	}
+	for (i = 0; i < ant_num; i++) {
+		if (pdata->antenna != priv->hist_data[i].antenna)
+			continue;
+		seq_printf(sfp, "antenna %d\n", priv->hist_data[i].antenna);
+		switch (priv->hist_data[i].powerclass) {
+		case 2:
+			seq_printf(sfp, "Power class=1.5\n");
+			break;
+		case 5:
+			seq_printf(sfp, "Power class=2\n");
+			break;
+		case 6:
+			seq_printf(sfp, "Power class=1\n");
+			break;
+		default:
+			seq_printf(sfp, "Power class=%d\n",
+				   priv->hist_data[i].powerclass);
+			break;
+		}
+		for (j = 0; j < (MAX_BT_LINK + MAX_BLE_LINK); j++) {
+			switch (priv->hist_data[i].link[j].txrxrate) {
+			case BDR_RATE_1M:
+				seq_printf(sfp,
+					   "BT link[%d]: TxPower=%d dBm, TxRx Rate=BDR(1 mbps), RSSI=%d dBm\n",
+					   j + 1,
+					   priv->hist_data[i].link[j].txpower,
+					   priv->hist_data[i].link[j].rssi);
+				break;
+			case EDR_RATE_2_3M:
+				seq_printf(sfp,
+					   "BT link[%d]: TxPower=%d dBm, TxRx Rate=EDR(2/3 mbps), RSSI=%d dBm\n",
+					   j + 1,
+					   priv->hist_data[i].link[j].txpower,
+					   priv->hist_data[i].link[j].rssi);
+				break;
+			case BLE_RATE_1M:
+				seq_printf(sfp,
+					   "BLE link[%d]: TxPower=%d dBm, TxRx Rate=BLE(1 mbps), RSSI=%d dBm\n",
+					   j - MAX_BT_LINK + 0x80,
+					   priv->hist_data[i].link[j].txpower,
+					   priv->hist_data[i].link[j].rssi);
+				break;
+			default:
+				if (j < MAX_BT_LINK)
+					seq_printf(sfp,
+						   "BT link[%d]: TxPower=%d dBm, TxRx Rate=(%d), RSSI=%d dBm\n",
+						   j + 1,
+						   priv->hist_data[i].link[j].
+						   txpower,
+						   priv->hist_data[i].link[j].
+						   txrxrate,
+						   priv->hist_data[i].link[j].
+						   rssi);
+				else
+					seq_printf(sfp,
+						   "BLE link[%d]: TxPower=%d dBm, TxRx Rate=(%d), RSSI=%d dBm\n",
+						   j - MAX_BT_LINK + 0x80,
+						   priv->hist_data[i].link[j].
+						   txpower,
+						   priv->hist_data[i].link[j].
+						   txrxrate,
+						   priv->hist_data[i].link[j].
+						   rssi);
+				break;
+			}
+		}
+		seq_printf(sfp, "\n");
+	}
+	LEAVE();
+	return 0;
+}
+
+static int
+bt_histogram_proc_open(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	return single_open(file, bt_histogram_read, PDE_DATA(inode));
+#else
+	return single_open(file, bt_histogram_read, PDE(inode)->data);
+#endif
+}
+
+static const struct file_operations histogram_proc_fops = {
+	.owner = THIS_MODULE,
+	.open = bt_histogram_proc_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+/**
+ *  @brief This function initializes proc entry
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param m_dev    A pointer to struct m_dev
+ *  @param seq      Sequence number
+ *
+ *  @return	BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct proc_dir_entry *entry;
+	int i, j;
+	char hist_entry[50];
+	ENTER();
+
+	memset(cmd52_string, 0, CMD52_STR_LEN);
+	if (proc_mbt) {
+		priv->dev_proc[seq].proc_entry =
+			proc_mkdir(m_dev->name, proc_mbt);
+		if (!priv->dev_proc[seq].proc_entry) {
+			PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->dev_proc[seq].hist_entry =
+			proc_mkdir("histogram", priv->dev_proc[seq].proc_entry);
+		if (!priv->dev_proc[seq].hist_entry) {
+			PRINTM(ERROR, "BT: Could not mkdir histogram!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		for (i = 0; i < MAX_ANTENNA_NUM; i++) {
+			priv->hist_proc[i].antenna = i;
+			priv->hist_proc[i].pbt = priv;
+			snprintf(hist_entry, sizeof(hist_entry), "bt-ant%d", i);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+			entry = proc_create_data(hist_entry, 0644,
+						 priv->dev_proc[seq].hist_entry,
+						 &histogram_proc_fops,
+						 &priv->hist_proc[i]);
+			if (entry == NULL)
+#else
+			entry = create_proc_entry(hist_entry, 0644,
+						  priv->dev_proc[seq].
+						  hist_entry);
+			if (entry) {
+				entry->data = &priv->hist_proc[i];
+				entry->proc_fops = &histogram_proc_fops;
+			} else
+#endif
+			{
+				PRINTM(MSG,
+				       "Fail to create histogram proc %s\n",
+				       hist_entry);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+		}
+		priv->dev_proc[seq].pfiles =
+			kmalloc(sizeof(proc_files), GFP_ATOMIC);
+		if (!priv->dev_proc[seq].pfiles) {
+			PRINTM(ERROR,
+			       "BT: Could not alloc memory for pfile!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files,
+		       sizeof(proc_files));
+		priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files);
+		for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++)
+			priv->dev_proc[seq].pfiles[j].pdata = NULL;
+		for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
+			priv->dev_proc[seq].pfiles[j].pdata =
+				kmalloc(priv->dev_proc[seq].pfiles[j].
+					num_items * sizeof(struct item_data),
+					GFP_ATOMIC);
+			if (!priv->dev_proc[seq].pfiles[j].pdata) {
+				PRINTM(ERROR,
+				       "BT: Could not alloc memory for pdata!\n");
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata,
+			       (u8 *)proc_files[j].pdata,
+			       priv->dev_proc[seq].pfiles[j].num_items *
+			       sizeof(struct item_data));
+			for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items;
+			     i++) {
+				if (priv->dev_proc[seq].pfiles[j].
+				    pdata[i].flag & OFFSET_BT_DEV)
+					priv->dev_proc[seq].pfiles[j].pdata[i].
+						addr =
+						priv->dev_proc[seq].pfiles[j].
+						pdata[i].offset +
+						(t_ptr)&priv->bt_dev;
+				if (priv->dev_proc[seq].pfiles[j].
+				    pdata[i].flag & OFFSET_BT_ADAPTER)
+					priv->dev_proc[seq].pfiles[j].pdata[i].
+						addr =
+						priv->dev_proc[seq].pfiles[j].
+						pdata[i].offset +
+						(t_ptr)priv->adapter;
+			}
+			priv->dev_proc[seq].pfiles[j].pbt = priv;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+			entry = proc_create_data(proc_files[j].name,
+						 S_IFREG | proc_files[j].
+						 fileflag,
+						 priv->dev_proc[seq].proc_entry,
+						 proc_files[j].fops,
+						 &priv->dev_proc[seq].
+						 pfiles[j]);
+			if (entry == NULL)
+#else
+			entry = create_proc_entry(proc_files[j].name,
+						  S_IFREG | proc_files[j].
+						  fileflag,
+						  priv->dev_proc[seq].
+						  proc_entry);
+			if (entry) {
+				entry->data = &priv->dev_proc[seq].pfiles[j];
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+				entry->owner = THIS_MODULE;
+#endif
+				entry->proc_fops = proc_files[j].fops;
+			} else
+#endif
+				PRINTM(MSG, "BT: Fail to create proc %s\n",
+				       proc_files[j].name);
+		}
+	}
+done:
+	if (ret == BT_STATUS_FAILURE) {
+		if (priv->dev_proc[seq].proc_entry) {
+			remove_proc_entry(m_dev->name, proc_mbt);
+			priv->dev_proc[seq].proc_entry = NULL;
+		}
+		if (priv->dev_proc[seq].pfiles) {
+			for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
+				if (priv->dev_proc[seq].pfiles[j].pdata) {
+					kfree(priv->dev_proc[seq].pfiles[j].
+					      pdata);
+					priv->dev_proc[seq].pfiles[j].pdata =
+						NULL;
+				}
+			}
+			kfree(priv->dev_proc[seq].pfiles);
+			priv->dev_proc[seq].pfiles = NULL;
+		}
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function removes proc interface
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return	N/A
+ */
+void
+bt_proc_remove(bt_private *priv)
+{
+	int j, i;
+	char hist_entry[50];
+	ENTER();
+	PRINTM(INFO, "BT: Remove Proc Interface\n");
+	if (proc_mbt) {
+		for (i = 0; i < MAX_RADIO_FUNC; i++) {
+			if (!priv->dev_proc[i].proc_entry)
+				continue;
+			for (j = 0; j < ARRAY_SIZE(proc_files); j++) {
+				remove_proc_entry(proc_files[j].name,
+						  priv->dev_proc[i].proc_entry);
+			}
+			for (j = 0; j < MAX_ANTENNA_NUM; j++) {
+				snprintf(hist_entry, sizeof(hist_entry),
+					 "bt-ant%d", j);
+				remove_proc_entry(hist_entry,
+						  priv->dev_proc[i].hist_entry);
+			}
+			remove_proc_entry("histogram",
+					  priv->dev_proc[i].proc_entry);
+			remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt);
+			priv->dev_proc[i].proc_entry = NULL;
+
+			if (priv->dev_proc[i].pfiles) {
+				for (j = 0;
+				     j < priv->dev_proc[i].num_proc_files;
+				     j++) {
+					if (priv->dev_proc[i].pfiles[j].pdata) {
+						kfree(priv->dev_proc[i].
+						      pfiles[j].pdata);
+						priv->dev_proc[i].pfiles[j].
+							pdata = NULL;
+					}
+				}
+				kfree(priv->dev_proc[i].pfiles);
+				priv->dev_proc[i].pfiles = NULL;
+			}
+		}
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function creates proc interface
+ *  directory structure
+ *
+ *  @return		BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_root_proc_init(void)
+{
+	PRINTM(INFO, "BT: Create Proc Interface\n");
+	proc_mbt = proc_mkdir("mbt", PROC_DIR);
+	if (!proc_mbt) {
+		PRINTM(ERROR, "BT: Cannot create proc interface\n");
+		return BT_STATUS_FAILURE;
+	}
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function removes proc interface
+ *  directory structure
+ *
+ *  @return		BT_STATUS_SUCCESS
+ */
+int
+bt_root_proc_remove(void)
+{
+	remove_proc_entry("mbt", PROC_DIR);
+	proc_mbt = NULL;
+	return BT_STATUS_SUCCESS;
+}
diff --git a/bt_sd8997/bt/bt_sdio.h b/bt_sd8997/bt/bt_sdio.h
new file mode 100644
index 0000000..b344e92
--- /dev/null
+++ b/bt_sd8997/bt/bt_sdio.h
@@ -0,0 +1,258 @@
+/** @file bt_sdio.h
+ *  @brief This file contains SDIO (interface) module
+ *  related macros, enum, and structure.
+ *
+ *  Copyright (C) 2007-2017, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _BT_SDIO_H_
+#define _BT_SDIO_H_
+
+#include <linux/irqreturn.h>
+
+/** IRQ return type */
+typedef irqreturn_t IRQ_RET_TYPE;
+/** IRQ return */
+#define IRQ_RET		(return IRQ_HANDLED)
+/** ISR notifier function */
+typedef IRQ_RET_TYPE (*isr_notifier_fn_t) (s32 irq, void *dev_id,
+					   struct pt_regs * reg);
+
+/** SDIO header length */
+#define SDIO_HEADER_LEN			4
+
+/** Interrupt Mode SDIO */
+#define INT_MODE_SDIO       0
+/** Interrupt Mode GPIO */
+#define INT_MODE_GPIO       1
+/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */
+#define GPIO_INT_NEW_MODE   255
+/* SD block size can not bigger than 64 due to buf size limit in firmware */
+/** define SD block size for data Tx/Rx */
+#define SD_BLOCK_SIZE			64
+/** define SD block size for firmware download */
+#define SD_BLOCK_SIZE_FW_DL		256
+
+/** Number of blocks for firmware transfer */
+#define FIRMWARE_TRANSFER_NBLOCK	2
+
+/** Firmware ready */
+#define FIRMWARE_READY			0xfedc
+
+/* Bus Interface Control Reg 0x07 */
+/** SD BUS width 1 */
+#define SD_BUS_WIDTH_1			0x00
+/** SD BUS width 4 */
+#define SD_BUS_WIDTH_4			0x02
+/** SD BUS width mask */
+#define SD_BUS_WIDTH_MASK		0x03
+/** Asynchronous interrupt mode */
+#define ASYNC_INT_MODE			0x20
+
+/** magic register */
+#define CARD_MAGIC_REG          0xF0
+/** magic value */
+#define MAGIC_VAL               0x24
+
+/* Host Control Registers */
+/** Host Control Registers : Configuration */
+#define CONFIGURATION_REG		0x00
+/** Host Control Registers : Host without Command 53 finish host*/
+#define HOST_TO_CARD_EVENT		(0x1U << 3)
+/** Host Control Registers : Host terminates Command 53 */
+#define HOST_TERM_CMD53			(0x1U << 2)
+/** Host Control Registers : Host power up */
+#define HOST_POWER_UP			(0x1U << 1)
+/** Host Control Registers : Host power down */
+#define HOST_POWER_DOWN			(0x1U << 0)
+
+/** Host Control Registers : Host interrupt RSR */
+#define HOST_INT_RSR_REG		0x04
+
+/** Host Control Registers : Upload host interrupt RSR */
+#define UP_LD_HOST_INT_RSR		(0x1U)
+
+/** Host Control Registers : Host interrupt mask */
+#define HOST_INT_MASK_REG		0x08
+
+/** Host Control Registers : Upload host interrupt mask */
+#define UP_LD_HOST_INT_MASK		(0x1U)
+/** Host Control Registers : Download host interrupt mask */
+#define DN_LD_HOST_INT_MASK		(0x2U)
+/** Enable Host interrupt mask */
+#define HIM_ENABLE			(UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK)
+/** Disable Host interrupt mask */
+#define	HIM_DISABLE			0xff
+
+/** Host Control Registers : Host interrupt status */
+#define HOST_INTSTATUS_REG		0x0C
+/** Host Control Registers : Upload host interrupt status */
+#define UP_LD_HOST_INT_STATUS		(0x1U)
+/** Host Control Registers : Download host interrupt status */
+#define DN_LD_HOST_INT_STATUS		(0x2U)
+
+/** Host Control Registers : Host Transfer status */
+#define HOST_INT_STATUS_REG		0x58
+/** Host Control Registers : Upload CRC error */
+#define UP_LD_CRC_ERR			(0x1U << 2)
+/** Host Control Registers : Upload restart */
+#define UP_LD_RESTART			(0x1U << 1)
+/** Host Control Registers : Download restart */
+#define DN_LD_RESTART			(0x1U << 0)
+
+/** Card Control Registers : Card to Host Event register */
+#define CARD_STATUS_REG			0x5C
+/** Card Control Registers : Card I/O ready */
+#define CARD_IO_READY			(0x1U << 3)
+/** Card Control Registers : CIS card ready */
+#define CIS_CARD_RDY			(0x1U << 2)
+/** Card Control Registers : Upload card ready */
+#define UP_LD_CARD_RDY			(0x1U << 1)
+/** Card Control Registers : Download card ready */
+#define DN_LD_CARD_RDY			(0x1U << 0)
+
+/** Card Control Registers : Host interrupt mask register */
+#define HOST_INTERRUPT_MASK_REG 	0x60
+/** Card Control Registers : Host power interrupt mask */
+#define HOST_POWER_INT_MASK		(0x1U << 3)
+/** Card Control Registers : Abort card interrupt mask */
+#define ABORT_CARD_INT_MASK		(0x1U << 2)
+/** Card Control Registers : Upload card interrupt mask */
+#define UP_LD_CARD_INT_MASK          	(0x1U << 1)
+/** Card Control Registers : Download card interrupt mask */
+#define DN_LD_CARD_INT_MASK          	(0x1U << 0)
+
+/** Card Control Registers : Card interrupt status register */
+#define CARD_INTERRUPT_STATUS_REG    	0x64
+/** Card Control Registers : Power up interrupt */
+#define POWER_UP_INT			(0x1U << 4)
+/** Card Control Registers : Power down interrupt */
+#define POWER_DOWN_INT               	(0x1U << 3)
+
+/** Card Control Registers : Card interrupt RSR register */
+#define CARD_INTERRUPT_RSR_REG       	0x68
+/** Card Control Registers : Power up RSR */
+#define POWER_UP_RSR                 	(0x1U << 4)
+/** Card Control Registers : Power down RSR */
+#define POWER_DOWN_RSR               	(0x1U << 3)
+
+/* Card Control Registers */
+/** Card Control Registers : Read SQ base address A0 register */
+#define SQ_READ_BASE_ADDRESS_A0_REG  	0xf8
+/** Card Control Registers : Read SQ base address A1 register */
+#define SQ_READ_BASE_ADDRESS_A1_REG  	0xf9
+/** Card Control Registers : Read SQ base address A2 register */
+#define SQ_READ_BASE_ADDRESS_A2_REG  	0x6E
+/** Card Control Registers : Read SQ base address A3 register */
+#define SQ_READ_BASE_ADDRESS_A3_REG  	0x6F
+/** Card Control Registers : Write SQ base address A0 register */
+#define SQ_WRITE_BASE_ADDRESS_A0_REG  	0x70
+/** Card Control Registers : Write SQ base address A1 register */
+#define SQ_WRITE_BASE_ADDRESS_A1_REG  	0x71
+/** Card Control Registers : Write SQ base address A2 register */
+#define SQ_WRITE_BASE_ADDRESS_A2_REG  	0x72
+/** Card Control Registers : Write SQ base address A3 register */
+#define SQ_WRITE_BASE_ADDRESS_A3_REG  	0x73
+
+/** Card Control Registers : Card revision register */
+#define CARD_REVISION_REG            	0xC8
+
+/** Firmware status 0 register (SCRATCH0_0) */
+#define CARD_FW_STATUS0_REG             0xe8
+/** Firmware status 1 register (SCRATCH0_1) */
+#define CARD_FW_STATUS1_REG             0xe9
+/** Rx length register (SCRATCH0_2) */
+#define CARD_RX_LEN_REG                 0xea
+/** Rx unit register (SCRATCH0_3) */
+#define CARD_RX_UNIT_REG                0xeb
+/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/
+#define ENABLE_GPIO_1_INT_MODE  0x88
+/** Scratch reg 3 2  :     Configure GPIO-1 INT*/
+#define SCRATCH_REG_32          0xEE
+
+/** Card Control Registers : Card OCR 0 register */
+#define CARD_OCR_0_REG			0xD4
+/** Card Control Registers : Card OCR 1 register */
+#define CARD_OCR_1_REG			0xD5
+/** Card Control Registers : Card OCR 3 register */
+#define CARD_OCR_3_REG			0xD6
+/** Card Control Registers : Card config register */
+#define CARD_CONFIG_REG			0xD7
+/** Card Control Registers : Miscellaneous Configuration Register */
+#define CARD_MISC_CFG_REG		0xD8
+/** Misc. Config Register : Auto Re-enable interrupts */
+#define AUTO_RE_ENABLE_INT		(0x1U << 4)
+
+/** Card Control Registers : Debug 0 register */
+#define DEBUG_0_REG			0xDC
+/** Card Control Registers : SD test BUS 0 */
+#define SD_TESTBUS0			(0x1U)
+/** Card Control Registers : Debug 1 register */
+#define DEBUG_1_REG			0xDD
+/** Card Control Registers : SD test BUS 1 */
+#define SD_TESTBUS1			(0x1U)
+/** Card Control Registers : Debug 2 register */
+#define DEBUG_2_REG			0xDE
+/** Card Control Registers : SD test BUS 2 */
+#define SD_TESTBUS2			(0x1U)
+/** Card Control Registers : Debug 3 register */
+#define DEBUG_3_REG			0xDF
+/** Card Control Registers : SD test BUS 3 */
+#define SD_TESTBUS3			(0x1U)
+
+/** Host Control Registers : I/O port 0 */
+#define IO_PORT_0_REG			0xE4
+/** Host Control Registers : I/O port 1 */
+#define IO_PORT_1_REG			0xE5
+/** Host Control Registers : I/O port 2 */
+#define IO_PORT_2_REG			0xE6
+
+struct sdio_mmc_card {
+	/** sdio_func structure pointer */
+	struct sdio_func *func;
+	/** bt_private structure pointer */
+	bt_private *priv;
+};
+
+/** DMA alignment value */
+#define DMA_ALIGNMENT	64
+/** 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)	\
+	((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1))
+
+/** This function read cmd52 register */
+int sd_write_reg(bt_private *priv, int reg, u8 val);
+/** This function write cmd52 value to register */
+int sd_read_reg(bt_private *priv, int reg, u8 *data);
+/** This function reads the Cmd52 value in dev structure */
+int sd_read_cmd52_val(bt_private *priv);
+/** This function updates card reg based on the Cmd52 value in dev structure */
+int sd_write_cmd52_val(bt_private *priv, int func, int reg, int val);
+
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+/** This function tells lower driver that BT is suspended */
+void bt_is_suspended(bt_private *priv);
+#endif
+#endif
+#endif
+#endif /* _BT_SDIO_H_ */
diff --git a/bt_sd8997/bt/bt_sdiommc.c b/bt_sd8997/bt/bt_sdiommc.c
new file mode 100644
index 0000000..2bc4cdc
--- /dev/null
+++ b/bt_sd8997/bt/bt_sdiommc.c
@@ -0,0 +1,1935 @@
+/** @file bt_sdiommc.c
+ *  @brief This file contains SDIO IF (interface) module
+ *  related functions.
+ *
+ * Copyright (C) 2007-2017, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/firmware.h>
+#include <linux/mmc/sdio_func.h>
+#include        <linux/mmc/sdio.h>
+#include        <linux/mmc/card.h>
+
+#include "bt_drv.h"
+#include "bt_sdio.h"
+
+/** define marvell vendor id */
+#define MARVELL_VENDOR_ID 0x02df
+
+/** Max retry number of CMD53 read/write */
+#define MAX_CMD53_RETRY 	3
+/** Max retry number of CMD53 read/write */
+#define MAX_CMD52_RETRY     3
+/** Firmware name */
+static char *fw_name;
+/** fw serial download flag */
+extern int bt_fw_serial;
+/** request firmware nowait */
+int bt_req_fw_nowait;
+static int multi_fn = BIT(2);
+
+#define SD8997_FW_NAME "mrvl/sdsd8997_combo_v4.bin"
+#define SD8997_BT_FW_NAME "mrvl/sd8997_bt_v4.bin"
+#define DEFAULT_FW_NAME "mrvl/sdsd8997_combo_v4.bin"
+#define DEFAULT_BT_FW_NAME "mrvl/sd8997_bt_v4.bin"
+
+/** Function number 2 */
+#define FN2			2
+/** Device ID for SD8997 FN2 */
+#define SD_DEVICE_ID_8997_BT_FN2    0x9142
+
+/** Array of SDIO device ids when multi_fn=0x12 */
+static const struct sdio_device_id bt_ids[] = {
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997_BT_FN2)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(sdio, bt_ids);
+
+/********************************************************
+		Global Variables
+********************************************************/
+/** unregiser bus driver flag */
+static u8 unregister;
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+extern int mbt_pm_keep_power;
+#endif
+
+/********************************************************
+		Local Functions
+********************************************************/
+
+/**
+ *  @brief This function gets rx_unit value
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_get_rx_unit(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_rx_unit_reg = CARD_RX_UNIT_REG;
+
+	ENTER();
+
+	reg = sdio_readb(card->func, card_rx_unit_reg, &ret);
+	if (ret == BT_STATUS_SUCCESS)
+		priv->bt_dev.rx_unit = reg;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads fwstatus registers
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param dat	   A pointer to keep returned data
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_read_firmware_status(bt_private *priv, u16 * dat)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 fws0;
+	u8 fws1;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_fw_status0_reg = CARD_FW_STATUS0_REG;
+	u8 card_fw_status1_reg = CARD_FW_STATUS1_REG;
+
+	ENTER();
+
+	fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret);
+	if (ret < 0) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret);
+	if (ret < 0) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	*dat = (((u16) fws1) << 8) | fws0;
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function reads rx length
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param dat	   A pointer to keep returned data
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+static int
+sd_read_rx_len(bt_private *priv, u16 * dat)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_rx_len_reg = CARD_RX_LEN_REG;
+
+	ENTER();
+
+	reg = sdio_readb(card->func, card_rx_len_reg, &ret);
+	if (ret == BT_STATUS_SUCCESS)
+		*dat = (u16) reg << priv->bt_dev.rx_unit;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables the host interrupts mask
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mask	   the interrupt mask
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_enable_host_int_mask(bt_private *priv, u8 mask)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 host_int_mask_reg = HOST_INT_MASK_REG;
+
+	ENTER();
+
+	sdio_writeb(card->func, mask, host_int_mask_reg, &ret);
+	if (ret) {
+		PRINTM(WARN, "BT: Unable to enable the host interrupt!\n");
+		ret = BT_STATUS_FAILURE;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function disables the host interrupts mask.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mask	   the interrupt mask
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+static int
+sd_disable_host_int_mask(bt_private *priv, u8 mask)
+{
+	int ret = BT_STATUS_FAILURE;
+	u8 host_int_mask;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 host_int_mask_reg = HOST_INT_MASK_REG;
+
+	ENTER();
+
+	/* Read back the host_int_mask register */
+	host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret);
+	if (ret)
+		goto done;
+
+	/* Update with the mask and write back to the register */
+	host_int_mask &= ~mask;
+	sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret);
+	if (ret < 0) {
+		PRINTM(WARN, "BT: Unable to diable the host interrupt!\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function polls the card status register
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param bits     the bit mask
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_poll_card_status(bt_private *priv, u8 bits)
+{
+	int tries;
+	int rval;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 cs;
+	u8 card_status_reg = CARD_STATUS_REG;
+
+	ENTER();
+
+	for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) {
+		cs = sdio_readb(card->func, card_status_reg, &rval);
+		if (rval != 0)
+			break;
+		if (rval == 0 && (cs & bits) == bits) {
+			LEAVE();
+			return BT_STATUS_SUCCESS;
+		}
+		udelay(1);
+	}
+	PRINTM(ERROR,
+	       "BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n",
+	       rval, tries, cs);
+
+	LEAVE();
+	return BT_STATUS_FAILURE;
+}
+
+/**
+ *  @brief This function reads updates the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_read_cmd52_val(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 func, reg, val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	ENTER();
+
+	func = priv->bt_dev.cmd52_func;
+	reg = priv->bt_dev.cmd52_reg;
+	sdio_claim_host(card->func);
+	if (func)
+		val = sdio_readb(card->func, reg, &ret);
+	else
+		val = sdio_f0_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	if (ret) {
+		PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n",
+		       func, reg);
+	} else {
+		priv->bt_dev.cmd52_val = val;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function updates card reg based on the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param func     Stores func variable
+ *  @param reg      Stores reg variable
+ *  @param val      Stores val variable
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_write_cmd52_val(bt_private *priv, int func, int reg, int val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	ENTER();
+
+	if (val >= 0) {
+		/* Perform actual write only if val is provided */
+		sdio_claim_host(card->func);
+		if (func)
+			sdio_writeb(card->func, val, reg, &ret);
+		else
+			sdio_f0_writeb(card->func, val, reg, &ret);
+		sdio_release_host(card->func);
+		if (ret) {
+			PRINTM(ERROR,
+			       "BT: Cannot write value (0x%x) to func %d reg %d\n",
+			       val, func, reg);
+			goto done;
+		}
+		priv->bt_dev.cmd52_val = val;
+	}
+
+	/* Save current func and reg for future read */
+	priv->bt_dev.cmd52_func = func;
+	priv->bt_dev.cmd52_reg = reg;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function updates card reg based on the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to write
+ *  @param val      value
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_write_reg(bt_private *priv, int reg, u8 val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	sdio_writeb(card->func, val, reg, &ret);
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads updates the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to read
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_read_reg(bt_private *priv, int reg, u8 *data)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	val = sdio_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	*data = val;
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function probes the card
+ *
+ *  @param func    A pointer to sdio_func structure.
+ *  @param id      A pointer to structure sdio_device_id
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+static int
+sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id)
+{
+	int ret = BT_STATUS_SUCCESS;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *card = NULL;
+
+	ENTER();
+
+	PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor,
+	       id->device, id->class, func->num);
+	card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL);
+	if (!card) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	card->func = func;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+	/* wait for chip fully wake up */
+	if (!func->enable_timeout)
+		func->enable_timeout = 200;
+#endif
+	sdio_claim_host(func);
+	ret = sdio_enable_func(func);
+	if (ret) {
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret);
+		kfree(card);
+		LEAVE();
+		return -EIO;
+	}
+	sdio_release_host(func);
+	priv = bt_add_card(card);
+	if (!priv) {
+		sdio_claim_host(func);
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		ret = BT_STATUS_FAILURE;
+		kfree(card);
+	}
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function checks if the firmware is ready to accept
+ *  command or not.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param pollnum  Number of times to poll fw status
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_verify_fw_download(bt_private *priv, int pollnum)
+{
+	int ret = BT_STATUS_FAILURE;
+	u16 firmwarestat = 0;
+	int tries;
+
+	ENTER();
+
+	/* Wait for firmware initialization event */
+	for (tries = 0; tries < pollnum; tries++) {
+		if (sd_read_firmware_status(priv, &firmwarestat) < 0)
+			continue;
+		if (firmwarestat == FIRMWARE_READY) {
+			PRINTM(MSG, "BT FW is active(%d)\n", tries);
+			ret = BT_STATUS_SUCCESS;
+			break;
+		}
+		mdelay(100);
+	}
+	if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS)) {
+		PRINTM(ERROR,
+		       "Fail to poll firmware status: firmwarestat=0x%x\n",
+		       firmwarestat);
+		bt_dump_sdio_regs(priv);
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Transfers firmware to card
+ *
+ *  @param priv      A Pointer to bt_private structure
+ *  @param fw        A Pointer to fw image
+ *  @param fw_len    fw image len
+ *  @return          BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+static int
+sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len)
+{
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 *firmware = fw;
+	int firmwarelen = fw_len;
+	u8 base0;
+	u8 base1;
+	int ret = BT_STATUS_SUCCESS;
+	int offset;
+	void *tmpfwbuf = NULL;
+	int tmpfwbufsz;
+	u8 *fwbuf;
+	u16 len;
+	int txlen = 0;
+	int tx_blocks = 0;
+	int i = 0;
+	int tries = 0;
+	u8 sq_read_base_address_a0_reg = SQ_READ_BASE_ADDRESS_A0_REG;
+	u8 sq_read_base_address_a1_reg = SQ_READ_BASE_ADDRESS_A1_REG;
+
+	ENTER();
+
+	PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen);
+
+	tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT;
+	tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL);
+	if (!tmpfwbuf) {
+		PRINTM(ERROR,
+		       "BT: Unable to allocate buffer for firmware. Terminating download\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	/* Ensure aligned firmware buffer */
+	fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT);
+
+	/* Perform firmware data transfer */
+	offset = 0;
+	do {
+		/* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits
+		 */
+		ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY);
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: FW download with helper poll status timeout @ %d\n",
+			       offset);
+			goto done;
+		}
+		/* More data? */
+		if (offset >= firmwarelen)
+			break;
+
+		for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+			base0 = sdio_readb(card->func,
+					   sq_read_base_address_a0_reg, &ret);
+			if (ret) {
+				PRINTM(WARN, "Dev BASE0 register read failed:"
+				       " base0=0x%04X(%d). Terminating download\n",
+				       base0, base0);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			base1 = sdio_readb(card->func,
+					   sq_read_base_address_a1_reg, &ret);
+			if (ret) {
+				PRINTM(WARN, "Dev BASE1 register read failed:"
+				       " base1=0x%04X(%d). Terminating download\n",
+				       base1, base1);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			len = (((u16) base1) << 8) | base0;
+
+			if (len != 0)
+				break;
+			udelay(10);
+		}
+
+		if (len == 0)
+			break;
+		else if (len > BT_UPLD_SIZE) {
+			PRINTM(FATAL,
+			       "BT: FW download failure @ %d, invalid length %d\n",
+			       offset, len);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	/** ignore CRC check before download the first packet */
+		if (offset == 0 && (len & BIT(0)))
+			len &= ~BIT(0);
+		txlen = len;
+
+		if (len & BIT(0)) {
+			i++;
+			if (i >= MAX_CMD53_RETRY) {
+				PRINTM(FATAL,
+				       "BT: FW download failure @ %d, over max retry count\n",
+				       offset);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			PRINTM(ERROR,
+			       "BT: FW CRC error indicated by the helper:"
+			       " len = 0x%04X, txlen = %d\n", len, txlen);
+			len &= ~BIT(0);
+
+			PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset);
+			/* Setting this to 0 to resend from same offset */
+			txlen = 0;
+		} else {
+			i = 0;
+
+			/* Set blocksize to transfer - checking for last block */
+			if (firmwarelen - offset < txlen)
+				txlen = firmwarelen - offset;
+
+			PRINTM(INFO, ".");
+
+			tx_blocks =
+				(txlen + SD_BLOCK_SIZE_FW_DL -
+				 1) / SD_BLOCK_SIZE_FW_DL;
+
+			/* Copy payload to buffer */
+			memcpy(fwbuf, &firmware[offset], txlen);
+		}
+
+		/* Send data */
+		ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf,
+				   tx_blocks * SD_BLOCK_SIZE_FW_DL);
+
+		if (ret < 0) {
+			PRINTM(ERROR,
+			       "BT: FW download, write iomem (%d) failed @ %d\n",
+			       i, offset);
+			sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret);
+			if (ret)
+				PRINTM(ERROR, "write ioreg failed (CFG)\n");
+		}
+
+		offset += txlen;
+	} while (TRUE);
+
+	PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset);
+
+	ret = BT_STATUS_SUCCESS;
+done:
+	kfree(tmpfwbuf);
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief request_firmware callback
+ *
+ * @param fw_firmware  A pointer to firmware structure
+ * @param context      A Pointer to bt_private structure
+ * @return             BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_request_fw_dpc(const struct firmware *fw_firmware, void *context)
+{
+	int ret = BT_STATUS_SUCCESS;
+	bt_private *priv = (bt_private *)context;
+	struct sdio_mmc_card *card = NULL;
+	struct m_dev *m_dev_bt = NULL;
+	struct timeval tstamp;
+
+	ENTER();
+
+	m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ];
+
+	if ((priv == NULL) || (priv->adapter == NULL) ||
+	    (priv->bt_dev.card == NULL) || (m_dev_bt == NULL)
+		) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	if (!fw_firmware) {
+		do_gettimeofday(&tstamp);
+		if (tstamp.tv_sec >
+		    (priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) {
+			PRINTM(ERROR,
+			       "BT: No firmware image found. Skipping download\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		PRINTM(ERROR,
+		       "BT: No firmware image found! Retrying download\n");
+		/* Wait a second here before calling the callback again */
+		os_sched_timeout(1000);
+		sd_download_firmware_w_helper(priv);
+		LEAVE();
+		return ret;
+	}
+
+	priv->firmware = fw_firmware;
+
+	if (BT_STATUS_FAILURE ==
+	    sd_init_fw_dpc(priv, (u8 *)priv->firmware->data,
+			   priv->firmware->size)) {
+		PRINTM(ERROR,
+		       "BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n",
+		       bt_req_fw_nowait);
+		sdio_release_host(card->func);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* check if the fimware is downloaded successfully or not */
+	if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) {
+		PRINTM(ERROR, "BT: FW failed to be active in time!\n");
+		ret = BT_STATUS_FAILURE;
+		sdio_release_host(card->func);
+		goto done;
+	}
+	sdio_release_host(card->func);
+	sd_enable_host_int(priv);
+	if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+		PRINTM(ERROR,
+		       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (fw_firmware) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+		if (!bt_req_fw_nowait)
+#endif
+			release_firmware(fw_firmware);
+	}
+	LEAVE();
+	return ret;
+
+done:
+	if (fw_firmware) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+		if (!bt_req_fw_nowait)
+#endif
+			release_firmware(fw_firmware);
+	}
+	/* For synchronous download cleanup will be done in add_card */
+	if (!bt_req_fw_nowait)
+		return ret;
+	PRINTM(INFO, "unregister device\n");
+	sbi_unregister_dev(priv);
+	((struct sdio_mmc_card *)card)->priv = NULL;
+	/* Stop the thread servicing the interrupts */
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid)
+		os_sched_timeout(1);
+	bt_proc_remove(priv);
+	clean_up_m_devs(priv);
+	bt_free_adapter(priv);
+	bt_priv_put(priv);
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief request_firmware callback
+ *        This function is invoked by request_firmware_nowait system call
+ *
+ * @param firmware     A pointer to firmware structure
+ * @param context      A Pointer to bt_private structure
+ * @return             None
+ **/
+static void
+sd_request_fw_callback(const struct firmware *firmware, void *context)
+{
+	ENTER();
+	sd_request_fw_dpc(firmware, context);
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function downloads firmware image to the card.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sd_download_firmware_w_helper(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	int err;
+	char *cur_fw_name = NULL;
+
+	ENTER();
+
+	cur_fw_name = fw_name;
+	if (fw_name == NULL) {
+		if (!bt_fw_serial || priv->fw_reload || bt_fw_reload)
+			cur_fw_name = DEFAULT_BT_FW_NAME;
+		else
+			cur_fw_name = DEFAULT_FW_NAME;
+	}
+
+	PRINTM(MSG, "BT Request firmware: %s\n", cur_fw_name);
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cur_fw_name, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      sd_request_fw_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cur_fw_name, priv->hotplug_device,
+					      priv, sd_request_fw_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cur_fw_name, priv->hotplug_device,
+					      priv, sd_request_fw_callback);
+#endif
+#endif
+		if (ret < 0)
+			PRINTM(FATAL,
+			       "BT: request_firmware_nowait() failed, error code = %#x\n",
+			       ret);
+	} else {
+		err = request_firmware(&priv->firmware, cur_fw_name,
+				       priv->hotplug_device);
+		if (err < 0) {
+			PRINTM(FATAL,
+			       "BT: request_firmware() failed, error code = %#x\n",
+			       err);
+			ret = BT_STATUS_FAILURE;
+		} else
+			ret = sd_request_fw_dpc(priv->firmware, priv);
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads data from the card.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_card_to_host(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u16 buf_len = 0;
+	int buf_block_len;
+	int blksz;
+	struct sk_buff *skb = NULL;
+	u32 type;
+	u8 *payload = NULL;
+	struct hci_dev *hdev = NULL;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int i = 0;
+
+	ENTER();
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC)
+		hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer;
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	do {
+		/* Read the length of data to be transferred */
+		ret = sd_read_rx_len(priv, &buf_len);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR, "BT: Read scratch reg failed (%d)\n", i);
+			if (i >= MAX_CMD52_RETRY) {
+				ret = BT_STATUS_FAILURE;
+				goto exit;
+			}
+			udelay(20);
+		}
+	}
+	while (ret == BT_STATUS_FAILURE);
+
+	/* Allocate buffer */
+	blksz = SD_BLOCK_SIZE;
+	buf_block_len = (buf_len + blksz - 1) / blksz;
+	if (buf_len <= BT_HEADER_LEN ||
+	    (buf_block_len * blksz) > ALLOC_BUF_SIZE) {
+		PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n",
+		       buf_len);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "BT: No free skb\n");
+		goto exit;
+	}
+	if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) {
+		skb_put(skb,
+			DMA_ALIGNMENT -
+			((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
+		skb_pull(skb,
+			 DMA_ALIGNMENT -
+			 ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
+	}
+
+	payload = skb->data;
+	i = 0;
+	do {
+		ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport,
+				  buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR,
+			       "BT: card_to_host, read iomem (%d) failed: %d\n",
+			       i, ret);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+			/* issue abort cmd52 command through F0 */
+			sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret);
+#endif
+			sdio_writeb(card->func, HOST_TERM_CMD53,
+				    CONFIGURATION_REG, &ret);
+			udelay(20);
+			ret = BT_STATUS_FAILURE;
+			if (i >= MAX_CMD53_RETRY) {
+				kfree_skb(skb);
+				skb = NULL;
+				goto exit;
+			}
+		}
+	} while (ret == BT_STATUS_FAILURE);
+	/* This is SDIO specific header length: byte[2][1][0], * type: byte[3]
+	   (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */
+	buf_len = payload[0];
+	buf_len |= (u16) payload[1] << 8;
+	type = payload[3];
+	PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", hdev->name,
+	       buf_len, type);
+	if (buf_len > buf_block_len * blksz) {
+		PRINTM(ERROR,
+		       "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n",
+		       buf_len, buf_block_len * blksz);
+		ret = BT_STATUS_FAILURE;
+		kfree_skb(skb);
+		skb = NULL;
+		goto exit;
+	}
+	DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len);
+	switch (type) {
+	case HCI_ACLDATA_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (hdev) {
+			skb->dev = (void *)hdev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+			hci_recv_frame(skb);
+#else
+			hci_recv_frame(hdev, skb);
+#endif
+			hdev->stat.byte_rx += buf_len;
+		}
+		break;
+	case HCI_SCODATA_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (hdev) {
+			skb->dev = (void *)hdev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+			hci_recv_frame(skb);
+#else
+			hci_recv_frame(hdev, skb);
+#endif
+			hdev->stat.byte_rx += buf_len;
+		}
+		break;
+	case HCI_EVENT_PKT:
+		/** add EVT Demux */
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb))
+			break;
+		switch (skb->data[0]) {
+		case 0x0E:
+			/** cmd complete */
+			if (hdev) {
+				skb->dev = (void *)hdev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+				hci_recv_frame(skb);
+#else
+				hci_recv_frame(hdev, skb);
+#endif
+				hdev->stat.byte_rx += buf_len;
+			}
+			break;
+		case 0x0F:
+			/** cmd status */
+			/** BT cmd status */
+			if (hdev) {
+				skb->dev = (void *)hdev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+				hci_recv_frame(skb);
+#else
+				hci_recv_frame(hdev, skb);
+#endif
+				hdev->stat.byte_rx += buf_len;
+			}
+			break;
+		case 0xFF:
+			/** Vendor specific pkt */
+			/** BT EVT */
+			if (hdev) {
+				skb->dev = (void *)hdev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+				hci_recv_frame(skb);
+#else
+				hci_recv_frame(hdev, skb);
+#endif
+				hdev->stat.byte_rx += buf_len;
+			}
+			break;
+		default:
+			/** BT EVT */
+			if (hdev) {
+				skb->dev = (void *)hdev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+				hci_recv_frame(skb);
+#else
+				hci_recv_frame(hdev, skb);
+#endif
+				hdev->stat.byte_rx += buf_len;
+			}
+			break;
+		}
+		break;
+	case MRVL_VENDOR_PKT:
+		/* Just think here need to back compatible FM */
+		bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (hdev) {
+			if (BT_STATUS_SUCCESS != bt_process_event(priv, skb)) {
+				skb->dev = (void *)hdev;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
+				hci_recv_frame(skb);
+#else
+				hci_recv_frame(hdev, skb);
+#endif
+				hdev->stat.byte_rx += buf_len;
+			}
+		}
+
+		break;
+	default:
+		/* Driver specified event and command resp should be handle
+		   here */
+		PRINTM(INFO, "BT: Unknown PKT type:%d\n", type);
+		kfree_skb(skb);
+		skb = NULL;
+		break;
+	}
+exit:
+	if (ret) {
+		if (hdev)
+			hdev->stat.err_rx++;
+		PRINTM(ERROR, "error when recv pkt!\n");
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function removes the card
+ *
+ *  @param func    A pointer to sdio_func structure
+ *  @return        N/A
+ */
+static void
+sd_remove_card(struct sdio_func *func)
+{
+	struct sdio_mmc_card *card;
+
+	ENTER();
+
+	if (func) {
+		card = sdio_get_drvdata(func);
+		if (card) {
+			if (!unregister && card->priv) {
+				PRINTM(INFO, "BT: card removed from sd slot\n");
+				((bt_private *)(card->priv))->adapter->
+					SurpriseRemoved = TRUE;
+			}
+			bt_remove_card(card->priv);
+			kfree(card);
+		}
+	}
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function handles the interrupt.
+ *
+ *  @param func  A pointer to sdio_func structure
+ *  @return      N/A
+ */
+static void
+sd_interrupt(struct sdio_func *func)
+{
+	bt_private *priv;
+	struct m_dev *m_dev = NULL;
+	struct sdio_mmc_card *card;
+	int ret = BT_STATUS_SUCCESS;
+	u8 ireg = 0;
+	u8 host_intstatus_reg = HOST_INTSTATUS_REG;
+
+	ENTER();
+
+	card = sdio_get_drvdata(func);
+	if (!card || !card->priv) {
+		PRINTM(INFO,
+		       "BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n",
+		       __func__, func, card);
+		LEAVE();
+		return;
+	}
+	priv = card->priv;
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0, SD_BLOCK_SIZE);
+	if (ret) {
+		PRINTM(ERROR,
+		       "BT: sdio_read_ioreg: cmd53 read int status register failed %d\n",
+		       ret);
+		goto done;
+	}
+	ireg = priv->adapter->hw_regs[host_intstatus_reg];
+	if (ret) {
+		PRINTM(ERROR,
+		       "BT: sdio_read_ioreg: CMD52 read int status register failed %d\n",
+		       ret);
+		goto done;
+	}
+	if (ireg != 0) {
+		/*
+		 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+		 * Clear the interrupt status register and re-enable
+		 * the interrupt
+		 */
+		PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name,
+		       ireg);
+		priv->adapter->irq_recv = ireg;
+	} else {
+		PRINTM(ERROR, "BT: ERR: ireg=0\n");
+	}
+	OS_INT_DISABLE;
+	priv->adapter->sd_ireg |= ireg;
+	OS_INT_RESTORE;
+	bt_interrupt(m_dev);
+done:
+	LEAVE();
+}
+
+/**
+ *  @brief This function checks if the interface is ready to download
+ *  or not while other download interfaces are present
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @param val    Winner status (0: winner)
+ *  @return       BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_check_winner_status(bt_private *priv, u8 *val)
+{
+
+	int ret = BT_STATUS_SUCCESS;
+	u8 winner = 0;
+	struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_fw_status0_reg = CARD_FW_STATUS0_REG;
+
+	ENTER();
+	winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret);
+	if (ret != BT_STATUS_SUCCESS) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	*val = winner;
+
+	LEAVE();
+	return ret;
+}
+
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+/** @brief This function tells lower driver that BT is suspended
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        None
+ */
+void
+bt_is_suspended(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	priv->adapter->is_suspended = TRUE;
+	sdio_func_suspended(card->func);
+}
+#endif
+
+/** @brief This function handles client driver suspend
+ *
+ *  @param dev	   A pointer to device structure
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+int
+bt_sdio_suspend(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+	struct m_dev *m_dev = NULL;
+	struct hci_dev *hcidev;
+
+	ENTER();
+
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+		PRINTM(ERROR,
+		       "BT: %s: cannot remain alive while host is suspended\n",
+		       sdio_func_id(func));
+		return -ENOSYS;
+	}
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv = cardp->priv;
+
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name);
+	hcidev = (struct hci_dev *)m_dev->dev_pointer;
+	hci_suspend_dev(hcidev);
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
+#ifdef BLE_WAKEUP
+		/** Set BLE Wake up pattern */
+		if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv))
+			PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n");
+#endif
+
+		if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+			PRINTM(CMD, "BT: HS not actived, suspend fail!\n");
+			if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+				PRINTM(CMD,
+				       "BT: HS not actived the second time, force to suspend!\n");
+			}
+		}
+	}
+
+	priv->adapter->is_suspended = TRUE;
+
+	LEAVE();
+	/* We will keep the power when hs enabled successfully */
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) {
+#ifdef MMC_PM_SKIP_RESUME_PROBE
+		PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and "
+		       "MMC_PM_SKIP_RESUME_PROBE\n");
+		return sdio_set_host_pm_flags(func,
+					      MMC_PM_KEEP_POWER |
+					      MMC_PM_SKIP_RESUME_PROBE);
+#else
+		PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n");
+		return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+#endif
+	} else {
+		PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n");
+		return BT_STATUS_SUCCESS;
+	}
+}
+
+void
+bt_sdio_shutdown(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+
+	ENTER();
+
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: shutdown: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+		PRINTM(ERROR,
+		       "BT: %s: cannot remain alive while host is shutdown\n",
+		       sdio_func_id(func));
+		return;
+	}
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return;
+	}
+
+	priv = cardp->priv;
+
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
+#ifdef BLE_WAKEUP
+		/** Set BLE Wake up pattern */
+		if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv))
+			PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n");
+#endif
+
+		if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+			PRINTM(CMD, "BT: HS not actived, shutdown fail!\n");
+			if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+				PRINTM(CMD,
+				       "BT: HS not actived the second time, force to shutdown!\n");
+			}
+		}
+	}
+	LEAVE();
+}
+
+/** @brief This function handles client driver resume
+ *
+ *  @param dev	   A pointer to device structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_sdio_resume(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+	struct m_dev *m_dev = NULL;
+	struct hci_dev *hcidev;
+
+	ENTER();
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv = cardp->priv;
+	priv->adapter->is_suspended = FALSE;
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name);
+	hcidev = (struct hci_dev *)m_dev->dev_pointer;
+	hci_resume_dev(hcidev);
+	sbi_wakeup_firmware(priv);
+	priv->adapter->hs_state = HS_DEACTIVATED;
+	PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+#endif
+#endif
+
+/********************************************************
+		Global Functions
+********************************************************/
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+static const struct dev_pm_ops bt_sdio_pm_ops = {
+	.suspend = bt_sdio_suspend,
+	.resume = bt_sdio_resume,
+};
+#endif
+#endif
+static struct sdio_driver sdio_bt = {
+	.name = "sdio_bt",
+	.id_table = bt_ids,
+	.probe = sd_probe_card,
+	.remove = sd_remove_card,
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+	.drv = {
+		.pm = &bt_sdio_pm_ops,
+		.shutdown = bt_sdio_shutdown,
+		}
+#endif
+#endif
+};
+
+/**
+ *  @brief This function registers the bt module in bus driver.
+ *
+ *  @return	   An int pointer that keeps returned value
+ */
+int *
+sbi_register(void)
+{
+	int *ret;
+
+	ENTER();
+
+	if (sdio_register_driver(&sdio_bt) != 0) {
+		PRINTM(FATAL, "BT: SD Driver Registration Failed\n");
+		LEAVE();
+		return NULL;
+	} else
+		ret = (int *)1;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function de-registers the bt module in bus driver.
+ *
+ *  @return        N/A
+ */
+void
+sbi_unregister(void)
+{
+	ENTER();
+	unregister = TRUE;
+	sdio_unregister_driver(&sdio_bt);
+	LEAVE();
+}
+
+/**
+ *  @brief This function registers the device.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_dev(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	u8 chiprev;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	struct sdio_func *func;
+	u8 host_intstatus_reg = HOST_INTSTATUS_REG;
+	u8 host_int_rsr_reg = HOST_INT_RSR_REG;
+	u8 card_misc_cfg_reg = CARD_MISC_CFG_REG;
+	u8 card_revision_reg = CARD_REVISION_REG;
+	u8 io_port_0_reg = IO_PORT_0_REG;
+	u8 io_port_1_reg = IO_PORT_1_REG;
+	u8 io_port_2_reg = IO_PORT_2_REG;
+	u8 card_magic_reg = CARD_MAGIC_REG;
+	u8 magic_val = 0;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: Error: card or function is NULL!\n");
+		goto failed;
+	}
+	func = card->func;
+	priv->hotplug_device = &func->dev;
+
+	/* Initialize the private structure */
+	strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name));
+	priv->bt_dev.ioport = 0;
+	priv->bt_dev.fn = func->num;
+
+	sdio_claim_host(func);
+	ret = sdio_claim_irq(func, sd_interrupt);
+	if (ret) {
+		PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret);
+		goto release_host;
+	}
+	ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE);
+	if (ret) {
+		PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__);
+		goto release_irq;
+	}
+
+	/* read Revision Register to get the chip revision number */
+	chiprev = sdio_readb(func, card_revision_reg, &ret);
+	if (ret) {
+		PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n");
+		goto release_irq;
+	}
+	priv->adapter->chip_rev = chiprev;
+	PRINTM(INFO, "revision=%#x\n", chiprev);
+
+	magic_val = sdio_readb(func, card_magic_reg, &ret);
+	if (ret) {
+		PRINTM(FATAL, ": cannot read CARD_MAGIC_REG\n");
+		goto release_irq;
+	}
+	priv->adapter->magic_val = magic_val;
+	PRINTM(INFO, "magic_val=%#x\n", magic_val);
+
+	/*
+	 * Read the HOST_INTSTATUS_REG for ACK the first interrupt got
+	 * from the bootloader. If we don't do this we get a interrupt
+	 * as soon as we register the irq.
+	 */
+	reg = sdio_readb(func, host_intstatus_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+
+	/* Read the IO port */
+	reg = sdio_readb(func, io_port_0_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= reg;
+
+	reg = sdio_readb(func, io_port_1_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= (reg << 8);
+
+	reg = sdio_readb(func, io_port_2_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= (reg << 16);
+
+	PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn,
+	       priv->bt_dev.ioport);
+
+#define SDIO_INT_MASK       0x3F
+	/* Set Host interrupt reset to read to clear */
+	reg = sdio_readb(func, host_int_rsr_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	/* Set auto re-enable */
+	reg = sdio_readb(func, card_misc_cfg_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+
+	sdio_set_drvdata(func, card);
+	sdio_release_host(func);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+release_irq:
+	sdio_release_irq(func);
+release_host:
+	sdio_release_host(func);
+failed:
+
+	LEAVE();
+	return BT_STATUS_FAILURE;
+}
+
+/**
+ *  @brief This function de-registers the device.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+sbi_unregister_dev(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+
+	ENTER();
+
+	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);
+	}
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function enables the host interrupts.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_enable_host_int(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	ret = sd_enable_host_int_mask(priv, HIM_ENABLE);
+	sd_get_rx_unit(priv);
+	sdio_release_host(card->func);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function disables the host interrupts.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sd_disable_host_int(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	ret = sd_disable_host_int_mask(priv, HIM_DISABLE);
+	sdio_release_host(card->func);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends data to the card.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param payload A pointer to the data/cmd buffer
+ *  @param nb      Length of data/cmd
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+#ifdef DEBUG_LEVEL1
+	struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+#endif
+	int ret = BT_STATUS_SUCCESS;
+	int buf_block_len;
+	int blksz;
+	int i = 0;
+	u8 *buf = NULL;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	buf = payload;
+
+	blksz = SD_BLOCK_SIZE;
+	buf_block_len = (nb + blksz - 1) / blksz;
+	/* Allocate buffer and copy payload */
+	if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) {
+		if (nb > MAX_TX_BUF_SIZE) {
+			PRINTM(ERROR, "BT: Invalid tx packet, size=%d\n", nb);
+			LEAVE();
+			return BT_STATUS_FAILURE;
+		}
+		/* Ensure 8-byte aligned CMD buffer */
+		buf = priv->adapter->tx_buf;
+		memcpy(buf, payload, nb);
+	}
+	sdio_claim_host(card->func);
+	do {
+		/* Transfer data to card */
+		ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf,
+				   buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR,
+			       "BT: host_to_card, write iomem (%d) failed: %d\n",
+			       i, ret);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+			/* issue abort cmd52 command through F0 */
+			sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret);
+#endif
+			sdio_writeb(card->func, HOST_TERM_CMD53,
+				    CONFIGURATION_REG, &ret);
+			udelay(20);
+			ret = BT_STATUS_FAILURE;
+			if (i >= MAX_CMD53_RETRY)
+				goto exit;
+		} else {
+			PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n",
+			       m_dev->name, nb);
+			DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb);
+		}
+	} while (ret == BT_STATUS_FAILURE);
+	priv->bt_dev.tx_dnld_rdy = FALSE;
+exit:
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function downloads firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_download_fw(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+	u8 winner = 0;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	sdio_claim_host(card->func);
+	if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) {
+		PRINTM(MSG, "BT: FW already downloaded!\n");
+		sdio_release_host(card->func);
+		sd_enable_host_int(priv);
+		if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+			PRINTM(ERROR,
+			       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+			ret = BT_STATUS_FAILURE;
+			goto err_register;
+		}
+		goto exit;
+	}
+	/* Check if other interface is downloading */
+	ret = sd_check_winner_status(priv, &winner);
+	if (ret == BT_STATUS_FAILURE) {
+		PRINTM(FATAL, "BT read winner status failed!\n");
+		goto done;
+	}
+	if (winner) {
+		PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n",
+		       winner);
+		/* check if the fimware is downloaded successfully or not */
+		if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) {
+			PRINTM(FATAL, "BT: FW failed to be active in time!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		sdio_release_host(card->func);
+		sd_enable_host_int(priv);
+		if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+			PRINTM(ERROR,
+			       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+			ret = BT_STATUS_FAILURE;
+			goto err_register;
+		}
+		goto exit;
+	}
+
+	do_gettimeofday(&priv->req_fw_time);
+	/* Download the main firmware via the helper firmware */
+	if (sd_download_firmware_w_helper(priv)) {
+		PRINTM(INFO, "BT: FW download failed!\n");
+		ret = BT_STATUS_FAILURE;
+	}
+	goto exit;
+done:
+	sdio_release_host(card->func);
+exit:
+	LEAVE();
+	return ret;
+err_register:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function checks the interrupt status and handle it accordingly.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+sbi_get_int_status(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 sdio_ireg = 0;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+
+	ENTER();
+
+	OS_INT_DISABLE;
+	sdio_ireg = priv->adapter->sd_ireg;
+	priv->adapter->sd_ireg = 0;
+	OS_INT_RESTORE;
+	sdio_claim_host(card->func);
+	priv->adapter->irq_done = sdio_ireg;
+	if (sdio_ireg & DN_LD_HOST_INT_STATUS) {	/* tx_done INT */
+		if (priv->bt_dev.tx_dnld_rdy) {	/* tx_done already received */
+			PRINTM(INFO,
+			       "BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n",
+			       priv->bt_dev.tx_dnld_rdy, sdio_ireg);
+		} else {
+			priv->bt_dev.tx_dnld_rdy = TRUE;
+		}
+	}
+	if (sdio_ireg & UP_LD_HOST_INT_STATUS)
+		sd_card_to_host(priv);
+
+	ret = BT_STATUS_SUCCESS;
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function wakeup firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sbi_wakeup_firmware(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
+	sdio_release_host(card->func);
+	PRINTM(CMD, "BT wake up firmware\n");
+
+	LEAVE();
+	return ret;
+}
+
+#define INIT_START_REG  0xF1
+#define INIT_END_REG 0xF6
+
+/** @brief This function dump the SDIO register
+ *
+ *  @param priv     A Pointer to the bt_private structure
+ *
+ *  @return         N/A
+ */
+void
+bt_dump_sdio_regs(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+	char buf[256], *ptr;
+	u8 loop, func, data;
+	unsigned int reg, reg_start, reg_end;
+	u8 loop_num = 2;
+	unsigned int init_reg_start = 0;
+	unsigned int init_reg_end = 0;
+	init_reg_start = INIT_START_REG;
+	init_reg_end = INIT_END_REG;
+
+	if (priv->adapter->ps_state)
+		sbi_wakeup_firmware(priv);
+
+	sdio_claim_host(card->func);
+	for (loop = 0; loop < loop_num; loop++) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+		if (loop == 0) {
+			/* Read the registers of SDIO function0 */
+			func = loop;
+			reg_start = 0;
+			reg_end = 9;
+
+		} else {
+			func = 2;
+			reg_start = 0;
+			reg_end = 0x09;
+		}
+		ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, reg_start,
+			       reg_end);
+		for (reg = reg_start; reg <= reg_end;) {
+			if (func == 0)
+				data = sdio_f0_readb(card->func, reg, &ret);
+			else
+				data = sdio_readb(card->func, reg, &ret);
+			if (!ret)
+				ptr += sprintf(ptr, "%02x ", data);
+			else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+			reg++;
+		}
+		PRINTM(MSG, "%s\n", buf);
+	}
+
+	if (init_reg_start) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+		ptr += sprintf(ptr, "Init Status Reg (%#x-%#x): ",
+			       init_reg_start, init_reg_end);
+		for (reg = init_reg_start; reg <= init_reg_end;) {
+			data = sdio_readb(card->func, reg, &ret);
+			if (!ret)
+				ptr += sprintf(ptr, "%02x ", data);
+			else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+			reg++;
+		}
+		PRINTM(MSG, "%s\n", buf);
+	}
+	sdio_release_host(card->func);
+}
+
+module_param(fw_name, charp, 0);
+MODULE_PARM_DESC(fw_name, "Firmware name");
+module_param(bt_req_fw_nowait, int, 0);
+MODULE_PARM_DESC(bt_req_fw_nowait,
+		 "0: Use request_firmware API; 1: Use request_firmware_nowait API");
+module_param(multi_fn, int, 0);
+MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;");
diff --git a/bt_sd8997/bt/hci_wrapper.h b/bt_sd8997/bt/hci_wrapper.h
new file mode 100644
index 0000000..e2942de
--- /dev/null
+++ b/bt_sd8997/bt/hci_wrapper.h
@@ -0,0 +1,162 @@
+/** @file hci_wrapper.h
+ *  @brief This file contains HCI related definitions
+ *
+ *  Copyright (C) 2011-2017, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _HCI_WRAPPER_H_
+#define _HCI_WRAPPER_H_
+
+#include <linux/module.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+/**  Define Seq num */
+#define BT_SEQ      0
+
+/** Define dev type */
+#define BT_TYPE     1
+#define BT_AMP_TYPE 2
+
+/** Define spec type */
+#define BLUEZ_SPEC     1
+#define IANYWHERE_SPEC 2
+#define GENERIC_SPEC   3
+
+/** Define lock/unlock wrapper */
+#define mdev_req_lock(d)		down(&d->req_lock)
+#define mdev_req_unlock(d)		up(&d->req_lock)
+
+/** Length of device name */
+#define DEV_NAME_LEN				32
+
+/** Define struct m_dev */
+struct m_dev {
+	char name[DEV_NAME_LEN];
+	int index;
+	unsigned long flags;
+	spinlock_t lock;
+	struct semaphore req_lock;
+	struct sk_buff_head rx_q;
+	wait_queue_head_t req_wait_q;
+	struct hci_dev_stats stat;
+	struct module *owner;
+	void *dev_pointer;
+	int dev_type;
+	int spec_type;
+	void *driver_data;
+	int read_continue_flag;
+	int wait_rx_complete;
+	int rx_complete_flag;
+	wait_queue_head_t rx_wait_q;
+	spinlock_t rxlock;
+
+	struct sk_buff *evt_skb;
+	struct sk_buff *acl_skb;
+	struct sk_buff *sco_skb;
+
+	int (*open) (struct m_dev * m_dev);
+	int (*close) (struct m_dev * m_dev);
+	int (*flush) (struct m_dev * m_dev);
+	int (*send) (struct m_dev * m_dev, struct sk_buff * skb);
+	void (*destruct) (struct m_dev * m_dev);
+	void (*notify) (struct m_dev * m_dev, unsigned int evt);
+	int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, void *arg);
+	void (*query) (struct m_dev * m_dev, void *arg);
+
+};
+
+/** Define struct mbt_dev */
+struct mbt_dev {
+	/** maybe could add some private member later */
+	char name[DEV_NAME_LEN];
+	unsigned long flags;
+	__u8 type;
+
+	__u16 pkt_type;
+	__u16 esco_type;
+	__u16 link_policy;
+	__u16 link_mode;
+
+	__u32 idle_timeout;
+	__u16 sniff_min_interval;
+	__u16 sniff_max_interval;
+
+	struct sk_buff *reassembly[3];
+
+	atomic_t promisc;
+};
+
+/** This function frees m_dev allocation */
+void free_m_dev(struct m_dev *m_dev);
+
+/**
+ *  @brief This function receives frames
+ *
+ *  @param skb	A pointer to struct sk_buff
+ *  @return	0--success otherwise error code
+ */
+static inline int
+mdev_recv_frame(struct sk_buff *skb)
+{
+	struct m_dev *m_dev = (struct m_dev *)skb->dev;
+	if (!m_dev || (!test_bit(HCI_UP, &m_dev->flags)
+		       && !test_bit(HCI_INIT, &m_dev->flags))) {
+		kfree_skb(skb);
+		return -ENXIO;
+	}
+
+	/* Incomming skb */
+	bt_cb(skb)->incoming = 1;
+
+	/* Time stamp */
+	__net_timestamp(skb);
+
+	/* Queue frame for rx task */
+	skb_queue_tail(&m_dev->rx_q, skb);
+
+	/* Wakeup rx thread */
+	wake_up_interruptible(&m_dev->req_wait_q);
+
+	return 0;
+}
+
+/**
+ *  @brief mbt dev suspend handler
+ *
+ *  @param m_dev   A pointer to struct m_dev
+ *  @return        0
+ */
+static inline int
+mbt_hci_suspend_dev(struct m_dev *m_dev)
+{
+	return 0;
+}
+
+/**
+ *  @brief mbt dev resume handler
+ *
+ *  @param m_dev   A pointer to struct m_dev
+ *  @return        0
+ */
+static inline int
+mbt_hci_resume_dev(struct m_dev *m_dev)
+{
+	return 0;
+}
+
+#endif /* _HCI_WRAPPER_H_ */
diff --git a/bt_sd8997/bt/mbt_char.c b/bt_sd8997/bt/mbt_char.c
new file mode 100644
index 0000000..c0c025f
--- /dev/null
+++ b/bt_sd8997/bt/mbt_char.c
@@ -0,0 +1,774 @@
+/** @file mbt_char.c
+  *
+  * @brief This file contains the char device function calls
+  *
+  * Copyright (C) 2010-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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/path.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+
+#include "bt_drv.h"
+#include "mbt_char.h"
+
+static LIST_HEAD(char_dev_list);
+
+static DEFINE_SPINLOCK(char_dev_list_lock);
+
+static int mbtchar_major = MBTCHAR_MAJOR_NUM;
+
+struct kobject *
+chardev_get(struct char_dev *dev)
+{
+	struct kobject *kobj;
+
+	kobj = bt_priv_get(dev->m_dev->driver_data);
+	if (!kobj)
+		return NULL;
+	PRINTM(INFO, "dev get kobj\n");
+	kobj = kobject_get(&dev->kobj);
+	if (!kobj)
+		bt_priv_put(dev->m_dev->driver_data);
+	return kobj;
+}
+
+void
+chardev_put(struct char_dev *dev)
+{
+	if (dev) {
+		struct m_dev *m_dev = dev->m_dev;
+		PRINTM(INFO, "dev put kobj\n");
+		kobject_put(&dev->kobj);
+		if (m_dev)
+			bt_priv_put(m_dev->driver_data);
+	}
+}
+
+/**
+ *	@brief Changes permissions of the dev
+ *
+ *	@param name	pointer to character
+ *	@param mode		mode_t type data
+ *	@return			0--success otherwise failure
+ */
+int
+mbtchar_chmod(char *name, mode_t mode)
+{
+	struct path path;
+	struct inode *inode;
+	struct iattr newattrs;
+	int ret;
+	int retrycount = 0;
+
+	ENTER();
+	do {
+		os_sched_timeout(30);
+		ret = kern_path(name, LOOKUP_FOLLOW, &path);
+		if (++retrycount >= 10) {
+			PRINTM(ERROR,
+			       "mbtchar_chmod(): fail to get kern_path\n");
+			LEAVE();
+			return -EFAULT;
+		}
+	} while (ret);
+	inode = path.dentry->d_inode;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_lock(&inode->i_mutex);
+#else
+	inode_lock(inode);
+#endif
+	ret = mnt_want_write(path.mnt);
+	if (ret)
+		goto out_unlock;
+	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	if (inode->i_op->setattr)
+		ret = inode->i_op->setattr(path.dentry, &newattrs);
+	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+		ret = simple_setattr(path.dentry, &newattrs);
+#else
+		ret = inode_setattr(inode, &newattrs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+
+	path_put(&path);
+	LEAVE();
+	return ret;
+out_unlock:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+	path_put(&path);
+	return ret;
+}
+
+/**
+ *	@brief Changes ownership of the dev
+ *
+ *	@param name	pointer to character
+ *	@param user		uid_t type data
+ *	@param group	gid_t type data
+ *	@return			0--success otherwise failure
+ */
+int
+mbtchar_chown(char *name, uid_t user, gid_t group)
+{
+	struct path path;
+	struct inode *inode = NULL;
+	struct iattr newattrs;
+	int ret = 0;
+	int retrycount = 0;
+
+	ENTER();
+	do {
+		os_sched_timeout(30);
+		ret = kern_path(name, LOOKUP_FOLLOW, &path);
+		if (++retrycount >= 10) {
+			PRINTM(ERROR,
+			       "mbtchar_chown(): fail to get kern_path\n");
+			LEAVE();
+			return -EFAULT;
+		}
+	} while (ret);
+	inode = path.dentry->d_inode;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_lock(&inode->i_mutex);
+#else
+	inode_lock(inode);
+#endif
+	ret = mnt_want_write(path.mnt);
+	if (ret)
+		goto out_unlock;
+	newattrs.ia_valid = ATTR_CTIME;
+	if (user != (uid_t) (-1)) {
+		newattrs.ia_valid |= ATTR_UID;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+		newattrs.ia_uid = user;
+#else
+		newattrs.ia_uid = KUIDT_INIT(user);
+#endif
+	}
+	if (group != (gid_t) (-1)) {
+		newattrs.ia_valid |= ATTR_GID;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+		newattrs.ia_gid = group;
+#else
+		newattrs.ia_gid = KGIDT_INIT(group);
+#endif
+	}
+	if (!S_ISDIR(inode->i_mode))
+		newattrs.ia_valid |=
+			ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+	if (inode->i_op->setattr)
+		ret = inode->i_op->setattr(path.dentry, &newattrs);
+	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+		ret = simple_setattr(path.dentry, &newattrs);
+#else
+		ret = inode_setattr(inode, &newattrs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+
+	path_put(&path);
+	LEAVE();
+	return ret;
+out_unlock:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+	path_put(&path);
+	return ret;
+}
+
+/**
+ *	@brief write handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param buf		pointer to char buffer
+ *	@param count	size of receive buffer
+ *	@param f_pos	pointer to loff_t type data
+ *	@return			number of bytes written
+ */
+ssize_t
+chardev_write(struct file * filp, const char *buf, size_t count, loff_t * f_pos)
+{
+	int nwrite = 0;
+	struct sk_buff *skb;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	if (!test_bit(HCI_UP, &m_dev->flags)) {
+		LEAVE();
+		return -EBUSY;
+	}
+	nwrite = count;
+	skb = bt_skb_alloc(count, GFP_ATOMIC);
+	if (!skb) {
+		PRINTM(ERROR, "mbtchar_write(): fail to alloc skb\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+
+	if (copy_from_user((void *)skb_put(skb, count), buf, count)) {
+		PRINTM(ERROR, "mbtchar_write(): cp_from_user failed\n");
+		kfree_skb(skb);
+		nwrite = -EFAULT;
+		goto exit;
+	}
+
+	skb->dev = (void *)m_dev;
+	bt_cb(skb)->pkt_type = *((unsigned char *)skb->data);
+	skb_pull(skb, 1);
+
+	PRINTM(DATA, "Write: pkt_type: 0x%x, len=%d @%lu\n",
+	       bt_cb(skb)->pkt_type, skb->len, jiffies);
+	DBG_HEXDUMP(DAT_D, "chardev_write", skb->data, skb->len);
+
+	/* Send skb to the hci wrapper layer */
+	if (m_dev->send(m_dev, skb)) {
+		PRINTM(ERROR, "Write: Fail\n");
+		nwrite = 0;
+		/* Send failed */
+		kfree_skb(skb);
+	}
+exit:
+	LEAVE();
+	return nwrite;
+}
+
+/**
+ *	@brief read handler for BT char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param buf		pointer to char buffer
+ *	@param count	size of receive buffer
+ *	@param f_pos	pointer to loff_t type data
+ *	@return			number of bytes read
+ */
+ssize_t
+chardev_read(struct file * filp, char *buf, size_t count, loff_t * f_pos)
+{
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	struct sk_buff *skb = NULL;
+
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	/* Wait for rx data */
+	add_wait_queue(&m_dev->req_wait_q, &wait);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		skb = skb_dequeue(&m_dev->rx_q);
+		if (skb)
+			break;
+		if (!test_bit(HCI_UP, &m_dev->flags)) {
+			ret = -EBUSY;
+			break;
+		}
+
+		if (filp->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			break;
+		}
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&m_dev->req_wait_q, &wait);
+
+	if (!skb)
+		goto out;
+
+	if (m_dev->read_continue_flag == 0) {
+		/* Put type byte before the data */
+		memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+		PRINTM(DATA, "Read: pkt_type: 0x%x, len=%d @%lu\n",
+		       bt_cb(skb)->pkt_type, skb->len, jiffies);
+	}
+	DBG_HEXDUMP(DAT_D, "chardev_read", skb->data, skb->len);
+	if (skb->len > count) {
+		/* user data length is smaller than the skb length */
+		if (copy_to_user(buf, skb->data, count)) {
+			ret = -EFAULT;
+			goto outf;
+		}
+		skb_pull(skb, count);
+		skb_queue_head(&m_dev->rx_q, skb);
+		m_dev->read_continue_flag = 1;
+		wake_up_interruptible(&m_dev->req_wait_q);
+		ret = count;
+		goto out;
+	} else {
+		if (copy_to_user(buf, skb->data, skb->len)) {
+			ret = -EFAULT;
+			goto outf;
+		}
+		m_dev->read_continue_flag = 0;
+		ret = skb->len;
+	}
+outf:
+	kfree_skb(skb);
+out:
+	if (m_dev->wait_rx_complete && skb_queue_empty(&m_dev->rx_q)) {
+		m_dev->rx_complete_flag = TRUE;
+		wake_up_interruptible(&m_dev->rx_wait_q);
+	}
+	LEAVE();
+	return ret;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief ioctl common handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+char_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg)
+#else
+/**
+ *	@brief ioctl common handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+char_ioctl(struct file *filp, unsigned int cmd, void *arg)
+#endif
+{
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	PRINTM(INFO, "IOCTL: cmd=%d\n", cmd);
+	switch (cmd) {
+	case MBTCHAR_IOCTL_RELEASE:
+		m_dev->close(m_dev);
+		break;
+	case MBTCHAR_IOCTL_QUERY_TYPE:
+		m_dev->query(m_dev, arg);
+		break;
+	default:
+		m_dev->ioctl(m_dev, cmd, arg);
+		break;
+	}
+	LEAVE();
+	return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief ioctl handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_ioctl(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+#else
+/**
+ *	@brief ioctl handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	return char_ioctl(inode, filp, cmd, (void *)arg);
+#else
+	return char_ioctl(filp, cmd, (void *)arg);
+#endif
+}
+
+#ifdef CONFIG_COMPAT
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief compat ioctl handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_ioctl_compat(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg)
+#else
+/**
+ *	@brief compat ioctl handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+chardev_ioctl_compat(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	return char_ioctl(inode, filp, cmd, compat_ptr(arg));
+#else
+	return char_ioctl(filp, cmd, compat_ptr(arg));
+#endif
+}
+#endif /* CONFIG_COMPAT */
+
+/**
+ *	@brief open handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct char_dev *dev = NULL;
+	struct m_dev *m_dev = NULL;
+	struct char_dev *cdev = NULL;
+	struct list_head *p = NULL;
+	ENTER();
+
+	list_for_each(p, &char_dev_list) {
+		cdev = list_entry(p, struct char_dev, list);
+		if (mbtchar_major == MAJOR(inode->i_cdev->dev) &&
+		    cdev->minor == MINOR(inode->i_cdev->dev)) {
+			dev = cdev;
+			break;
+		}
+	}
+	if (!dev) {
+		PRINTM(ERROR, "cannot find dev from inode\n");
+		LEAVE();
+		return -ENXIO;
+	}
+	if (!chardev_get(dev)) {
+		LEAVE();
+		return -ENXIO;
+	}
+	filp->private_data = dev;	/* for other methods */
+	m_dev = dev->m_dev;
+	mdev_req_lock(m_dev);
+	if (test_bit(HCI_UP, &m_dev->flags)) {
+		ret = -EALREADY;
+		goto done;
+	}
+	if (m_dev->open(m_dev)) {
+		ret = -EIO;
+		goto done;
+	}
+	set_bit(HCI_UP, &m_dev->flags);
+
+done:
+	mdev_req_unlock(m_dev);
+	if (ret)
+		chardev_put(dev);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief release handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_release(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	ENTER();
+	if (!dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	if (m_dev)
+		ret = dev->m_dev->close(dev->m_dev);
+	filp->private_data = NULL;
+	chardev_put(dev);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief poll handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param wait		pointer to poll_table structure
+ *	@return			mask
+ */
+static unsigned int
+chardev_poll(struct file *filp, poll_table * wait)
+{
+	unsigned int mask;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+
+	m_dev = dev->m_dev;
+	poll_wait(filp, &m_dev->req_wait_q, wait);
+	mask = POLLOUT | POLLWRNORM;
+	if (skb_peek(&m_dev->rx_q))
+		mask |= POLLIN | POLLRDNORM;
+	if (!test_bit(HCI_UP, &(m_dev->flags)))
+		mask |= POLLHUP;
+	PRINTM(INFO, "poll mask=0x%x\n", mask);
+	LEAVE();
+	return mask;
+}
+
+/* File ops for the Char driver */
+const struct file_operations chardev_fops = {
+	.owner = THIS_MODULE,
+	.read = chardev_read,
+	.write = chardev_write,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	.ioctl = chardev_ioctl,
+#else
+	.unlocked_ioctl = chardev_ioctl,
+#endif
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = chardev_ioctl_compat,
+#endif
+	.open = chardev_open,
+	.release = chardev_release,
+	.poll = chardev_poll,
+};
+
+/**
+ *	@brief This function creates the char dev
+ *
+ *	@param dev			A pointer to structure char_dev
+ *  @param char_class	A pointer to class struct
+ *  @param mod_name		A pointer to char
+ *  @param dev_name		A pointer to char
+ *	@return				0--success otherwise failure
+ */
+int
+register_char_dev(struct char_dev *dev, struct class *char_class,
+		  char *mod_name, char *dev_name)
+{
+	int ret = 0, dev_num;
+	unsigned long flags;
+	ENTER();
+	/* create the chrdev region */
+	if (mbtchar_major) {
+		dev_num = MKDEV(mbtchar_major, dev->minor);
+		ret = register_chrdev_region(dev_num, 1, mod_name);
+	} else {
+		PRINTM(INFO, "chardev: no major # yet\n");
+		ret = alloc_chrdev_region((dev_t *) & dev_num, dev->minor, 1,
+					  mod_name);
+	}
+
+	if (ret) {
+		PRINTM(ERROR, "chardev: create chrdev_region failed\n");
+		LEAVE();
+		return ret;
+	}
+	if (!mbtchar_major) {
+		/* Store the allocated dev major # */
+		mbtchar_major = MAJOR(dev_num);
+	}
+	dev->cdev = cdev_alloc();
+	dev->cdev->ops = &chardev_fops;
+	dev->cdev->owner = chardev_fops.owner;
+	dev_num = MKDEV(mbtchar_major, dev->minor);
+
+	if (cdev_add(dev->cdev, dev_num, 1)) {
+		PRINTM(ERROR, "chardev: cdev_add failed\n");
+		ret = -EFAULT;
+		goto free_cdev_region;
+	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+	if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), NULL, dev_name);
+	}
+#else
+	if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), dev_name);
+	}
+#endif
+	PRINTM(INFO, "register char dev=%s\n", dev_name);
+
+	/** modify later */
+
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	list_add_tail(&dev->list, &char_dev_list);
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+
+	LEAVE();
+	return ret;
+free_cdev_region:
+	unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief This function deletes the char dev
+ *
+ *  @param dev			A pointer to structure char_dev
+ *  @param char_class	A pointer to class struct
+ *  @param dev_name		A pointer to char
+ *  @return				0--success otherwise failure
+ */
+int
+unregister_char_dev(struct char_dev *dev, struct class *char_class,
+		    char *dev_name)
+{
+	ENTER();
+	device_destroy(char_class, MKDEV(mbtchar_major, dev->minor));
+	cdev_del(dev->cdev);
+	unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1);
+	PRINTM(INFO, "unregister char dev=%s\n", dev_name);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *	@brief This function cleans module
+ *
+ *  @param char_class	A pointer to class struct
+ *  @return				N/A
+ */
+void
+chardev_cleanup(struct class *char_class)
+{
+	unsigned long flags;
+	struct list_head *p = NULL;
+	struct char_dev *dev = NULL;
+	ENTER();
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	do {
+		dev = NULL;
+		list_for_each(p, &char_dev_list) {
+			dev = list_entry(p, struct char_dev, list);
+			list_del(p);
+			spin_unlock_irqrestore(&char_dev_list_lock, flags);
+			unregister_char_dev(dev, char_class, dev->m_dev->name);
+			kobject_put(&dev->kobj);
+			spin_lock_irqsave(&char_dev_list_lock, flags);
+			break;
+		}
+	} while (dev);
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+	class_destroy(char_class);
+	LEAVE();
+}
+
+/**
+ *	@brief This function cleans module
+ *
+ *  @param m_dev	A pointer to m_dev struct
+ *  @param char_class	A pointer to class struct
+ *  @return			N/A
+ */
+void
+chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class)
+{
+	unsigned long flags;
+	struct list_head *p = NULL;
+	struct char_dev *dev = NULL;
+	ENTER();
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	list_for_each(p, &char_dev_list) {
+		dev = list_entry(p, struct char_dev, list);
+		if (dev->minor == m_dev->index) {
+			list_del(p);
+			spin_unlock_irqrestore(&char_dev_list_lock, flags);
+			dev->m_dev = NULL;
+			unregister_char_dev(dev, char_class, m_dev->name);
+			kobject_put(&dev->kobj);
+			spin_lock_irqsave(&char_dev_list_lock, flags);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+	LEAVE();
+}
diff --git a/bt_sd8997/bt/mbt_char.h b/bt_sd8997/bt/mbt_char.h
new file mode 100644
index 0000000..884d259
--- /dev/null
+++ b/bt_sd8997/bt/mbt_char.h
@@ -0,0 +1,70 @@
+/** @file mbt_char.h
+  *
+  * @brief This file contains mbtchar driver specific defines etc
+  *
+  * Copyright (C) 2010-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+#ifndef __MBT_CHAR_H__
+#define __MBT_CHAR_H__
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+/** Define ioctl */
+#define MBTCHAR_IOCTL_RELEASE       _IO('M', 1)
+#define MBTCHAR_IOCTL_QUERY_TYPE    _IO('M', 2)
+#ifdef BLE_WAKEUP
+#define MBTCHAR_IOCTL_BLE_WAKEUP_PARAM _IO('M', 4)
+#endif
+
+#define MBTCHAR_MAJOR_NUM            (0)
+
+/** Interface specific macros */
+#define FMCHAR_MINOR_BASE            (10)
+#define NFCCHAR_MINOR_BASE           (20)
+
+/** Declaration of char_dev struct */
+struct char_dev {
+	struct list_head list;
+	int minor;
+	int dev_type;
+	struct cdev *cdev;
+	struct m_dev *m_dev;
+	struct kobject kobj;
+};
+
+/** Changes permissions of the dev */
+int mbtchar_chmod(char *name, mode_t mode);
+
+/** Changes ownership of the dev */
+int mbtchar_chown(char *name, uid_t user, gid_t group);
+
+/**	This function creates the char dev */
+int register_char_dev(struct char_dev *dev, struct class *char_class,
+		      char *mod_name, char *dev_name);
+
+/**	This function deletes the char dev */
+int unregister_char_dev(struct char_dev *dev, struct class *char_class,
+			char *dev_name);
+
+/**	This function cleans module */
+void chardev_cleanup(struct class *char_class);
+
+/**	This function cleans module */
+void chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class);
+
+#endif /*__MBT_CHAR_H__*/
diff --git a/bt_sd8997/bt_char/bt_drv.h b/bt_sd8997/bt_char/bt_drv.h
new file mode 100644
index 0000000..8d8b659
--- /dev/null
+++ b/bt_sd8997/bt_char/bt_drv.h
@@ -0,0 +1,936 @@
+/** @file bt_drv.h
+ *  @brief This header file contains global constant/enum definitions,
+ *  global variable declaration.
+ *
+ *  Copyright (C) 2007-2018, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _BT_DRV_H_
+#define _BT_DRV_H_
+
+#include <linux/version.h>
+#include <linux/kthread.h>
+#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
+
+#include "hci_wrapper.h"
+
+/** MAX adapter BT driver supported */
+#define MAX_BT_ADAPTER    3
+
+#ifndef BIT
+/** BIT definition */
+#define BIT(x) (1UL << (x))
+#endif
+
+#ifdef MBT_64BIT
+typedef u64 t_ptr;
+#else
+typedef u32 t_ptr;
+#endif
+
+/** max number of adapter supported */
+#define MAX_BT_ADAPTER      3
+/** Define drv_mode bit */
+#define DRV_MODE_BT         BIT(0)
+
+/** Define devFeature bit */
+#define DEV_FEATURE_BT     BIT(0)
+#define DEV_FEATURE_BTAMP     BIT(1)
+#define DEV_FEATURE_BLE     BIT(2)
+
+/** Define maximum number of radio func supported */
+#define MAX_RADIO_FUNC     4
+
+/** MAC address print format */
+#ifndef MACSTR
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+#endif
+
+/** MAC address print arguments */
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#endif
+
+/** Debug level : Message */
+#define	DBG_MSG			BIT(0)
+/** Debug level : Fatal */
+#define DBG_FATAL		BIT(1)
+/** Debug level : Error */
+#define DBG_ERROR		BIT(2)
+/** Debug level : Data */
+#define DBG_DATA		BIT(3)
+/** Debug level : Command */
+#define DBG_CMD			BIT(4)
+/** Debug level : Event */
+#define DBG_EVENT		BIT(5)
+/** Debug level : Interrupt */
+#define DBG_INTR		BIT(6)
+
+/** Debug entry : Data dump */
+#define DBG_DAT_D		BIT(16)
+/** Debug entry : Data dump */
+#define DBG_CMD_D		BIT(17)
+
+/** Debug level : Entry */
+#define DBG_ENTRY		BIT(28)
+/** Debug level : Warning */
+#define DBG_WARN		BIT(29)
+/** Debug level : Informative */
+#define DBG_INFO		BIT(30)
+
+#ifdef	DEBUG_LEVEL1
+extern u32 mbt_drvdbg;
+
+#ifdef	DEBUG_LEVEL2
+/** Print informative message */
+#define	PRINTM_INFO(msg...)  \
+	do {if (mbt_drvdbg & DBG_INFO)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print warning message */
+#define	PRINTM_WARN(msg...) \
+	do {if (mbt_drvdbg & DBG_WARN)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print entry message */
+#define	PRINTM_ENTRY(msg...) \
+	do {if (mbt_drvdbg & DBG_ENTRY) \
+		printk(KERN_DEBUG msg); } while (0)
+#else
+/** Print informative message */
+#define	PRINTM_INFO(msg...)  do {} while (0)
+/** Print warning message */
+#define	PRINTM_WARN(msg...)  do {} while (0)
+/** Print entry message */
+#define	PRINTM_ENTRY(msg...) do {} while (0)
+#endif /* DEBUG_LEVEL2 */
+
+/** Print interrupt message */
+#define	PRINTM_INTR(msg...)  \
+	do {if (mbt_drvdbg & DBG_INTR)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print event message */
+#define	PRINTM_EVENT(msg...) \
+	do {if (mbt_drvdbg & DBG_EVENT) \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print command message */
+#define	PRINTM_CMD(msg...)   \
+	do {if (mbt_drvdbg & DBG_CMD)   \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print data message */
+#define	PRINTM_DATA(msg...)  \
+	do {if (mbt_drvdbg & DBG_DATA)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print error message */
+#define	PRINTM_ERROR(msg...) \
+	do {if (mbt_drvdbg & DBG_ERROR) \
+		printk(KERN_ERR msg); } while (0)
+/** Print fatal message */
+#define	PRINTM_FATAL(msg...) \
+	do {if (mbt_drvdbg & DBG_FATAL) \
+		printk(KERN_ERR msg); } while (0)
+/** Print message */
+#define	PRINTM_MSG(msg...)   \
+	do {if (mbt_drvdbg & DBG_MSG)   \
+		printk(KERN_ALERT msg); } while (0)
+
+/** Print data dump message */
+#define	PRINTM_DAT_D(msg...)  \
+	do {if (mbt_drvdbg & DBG_DAT_D)  \
+		printk(KERN_DEBUG msg); } while (0)
+/** Print data dump message */
+#define	PRINTM_CMD_D(msg...)  \
+	do {if (mbt_drvdbg & DBG_CMD_D)  \
+		printk(KERN_DEBUG msg); } while (0)
+
+/** Print message with required level */
+#define	PRINTM(level, msg...) PRINTM_##level(msg)
+
+/** Debug dump buffer length */
+#define DBG_DUMP_BUF_LEN	64
+/** Maximum number of dump per line */
+#define MAX_DUMP_PER_LINE	16
+/** Maximum data dump length */
+#define MAX_DATA_DUMP_LEN	48
+
+/**
+ * @brief Prints buffer data upto provided length
+ *
+ * @param prompt          Char pointer
+ * @param buf			  Buffer
+ * @param len    		  Length
+ *
+ * @return                N/A
+ */
+static inline void
+hexdump(char *prompt, u8 *buf, int len)
+{
+	int i;
+	char dbgdumpbuf[DBG_DUMP_BUF_LEN];
+	char *ptr = dbgdumpbuf;
+
+	printk(KERN_DEBUG "%s: len=%d\n", prompt, len);
+	for (i = 1; i <= len; i++) {
+		ptr += snprintf(ptr, 4, "%02x ", *buf);
+		buf++;
+		if (i % MAX_DUMP_PER_LINE == 0) {
+			*ptr = 0;
+			printk(KERN_DEBUG "%s\n", dbgdumpbuf);
+			ptr = dbgdumpbuf;
+		}
+	}
+	if (len % MAX_DUMP_PER_LINE) {
+		*ptr = 0;
+		printk(KERN_DEBUG "%s\n", dbgdumpbuf);
+	}
+}
+
+/** Debug hexdump of debug data */
+#define DBG_HEXDUMP_DAT_D(x, y, z) \
+	do {if (mbt_drvdbg & DBG_DAT_D) \
+		hexdump(x, y, z); } while (0)
+/** Debug hexdump of debug command */
+#define DBG_HEXDUMP_CMD_D(x, y, z) \
+	do {if (mbt_drvdbg & DBG_CMD_D) \
+		hexdump(x, y, z); } while (0)
+
+/** Debug hexdump */
+#define	DBG_HEXDUMP(level, x, y, z)    DBG_HEXDUMP_##level(x, y, z)
+
+/** Mark entry point */
+#define	ENTER()			PRINTM(ENTRY, "Enter: %s, %s:%i\n", __func__, \
+							__FILE__, __LINE__)
+/** Mark exit point */
+#define	LEAVE()			PRINTM(ENTRY, "Leave: %s, %s:%i\n", __func__, \
+							__FILE__, __LINE__)
+#else
+/** Do nothing */
+#define	PRINTM(level, msg...) do {} while (0)
+/** Do nothing */
+#define DBG_HEXDUMP(level, x, y, z)    do {} while (0)
+/** Do nothing */
+#define	ENTER()  do {} while (0)
+/** Do nothing */
+#define	LEAVE()  do {} while (0)
+#endif /* DEBUG_LEVEL1 */
+
+/** Bluetooth upload size */
+#define	BT_UPLD_SIZE				2312
+/** Bluetooth status success */
+#define BT_STATUS_SUCCESS			(0)
+/** Bluetooth status pending */
+#define BT_STATUS_PENDING           (1)
+/** Bluetooth status failure */
+#define BT_STATUS_FAILURE			(-1)
+
+#ifndef	TRUE
+/** True value */
+#define TRUE			1
+#endif
+#ifndef	FALSE
+/** False value */
+#define	FALSE			0
+#endif
+
+/** Set thread state */
+#define OS_SET_THREAD_STATE(x)		set_current_state(x)
+/** Time to wait until Host Sleep state change in millisecond */
+#define WAIT_UNTIL_HS_STATE_CHANGED 2000
+/** Time to wait cmd resp in millisecond */
+#define WAIT_UNTIL_CMD_RESP	    5000
+
+/** Sleep until a condition gets true or a timeout elapses */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+#define os_wait_interruptible_timeout(waitq, cond, timeout) \
+	interruptible_sleep_on_timeout(&waitq, ((timeout) * HZ / 1000))
+#else
+#define os_wait_interruptible_timeout(waitq, cond, timeout) \
+	wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000))
+#endif
+
+/** bt thread structure */
+typedef struct {
+	/** Task */
+	struct task_struct *task;
+	/** Queue */
+	wait_queue_head_t waitQ;
+	/** PID */
+	pid_t pid;
+	/** Private structure */
+	void *priv;
+} bt_thread;
+
+/**
+ * @brief Activates bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline void
+bt_activate_thread(bt_thread *thr)
+{
+	/** Initialize the wait queue */
+	init_waitqueue_head(&thr->waitQ);
+
+	/** Record the thread pid */
+	thr->pid = current->pid;
+}
+
+/**
+ * @brief De-activates bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline void
+bt_deactivate_thread(bt_thread *thr)
+{
+	thr->pid = 0;
+	return;
+}
+
+/**
+ * @brief Creates bt thread
+ *
+ * @param btfunc          Function pointer
+ * @param thr			  A pointer to bt_thread structure
+ * @param name    		  Char pointer
+ *
+ * @return                N/A
+ */
+static inline void
+bt_create_thread(int (*btfunc) (void *), bt_thread *thr, char *name)
+{
+	thr->task = kthread_run(btfunc, thr, "%s", name);
+}
+
+/**
+ * @brief Delete bt thread
+ *
+ * @param thr			  A pointer to bt_thread structure
+ *
+ * @return                N/A
+ */
+static inline int
+bt_terminate_thread(bt_thread *thr)
+{
+	/* Check if the thread is active or not */
+	if (!thr->pid)
+		return -1;
+
+	kthread_stop(thr->task);
+	return 0;
+}
+
+/**
+ * @brief  Set scheduled timeout
+ *
+ * @param millisec		 Time unit in ms
+ *
+ * @return                N/A
+ */
+static inline void
+os_sched_timeout(u32 millisec)
+{
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	schedule_timeout((millisec * HZ) / 1000);
+}
+
+#ifndef __ATTRIB_ALIGN__
+#define __ATTRIB_ALIGN__ __attribute__((aligned(4)))
+#endif
+
+#ifndef __ATTRIB_PACK__
+#define __ATTRIB_PACK__ __attribute__((packed))
+#endif
+
+/** BT histogram command */
+#define BT_CMD_HISTOGRAM            0xEA
+/** max antenna num */
+#define MAX_ANTENNA_NUM             2
+/** BDR 1M */
+#define BDR_RATE_1M					1
+/** EDR 2/3 M */
+#define EDR_RATE_2_3M			    2
+/** BLE 1M */
+#define BLE_RATE_1M                 5
+/** max bt link number */
+#define MAX_BT_LINK                 10
+/** max ble link number */
+#define MAX_BLE_LINK                16
+
+/** BT link state structure */
+typedef struct _bt_link_stat {
+    /** txrx rate 1: BDR_1M, 2:EDR 2/3 M, 3:BLE 1M */
+	u8 txrxrate;
+    /** power: -30 = N = 20 dbm*/
+	s8 txpower;
+    /** rssi: -127 to +20 (For BT), -128 to +127 (For BLE) */
+	s8 rssi;
+} __ATTRIB_PACK__ bt_link_stat;
+
+/** BT histogram data structure */
+typedef struct _bt_histogram_data {
+	/** Antenna */
+	u8 antenna;
+	/** Powerclass */
+	u8 powerclass;
+	/** bt link state structure */
+	bt_link_stat link[MAX_BT_LINK + MAX_BLE_LINK];
+} __ATTRIB_PACK__ bt_histogram_data;
+
+/** BT histogram proc data structure */
+typedef struct _bt_hist_proc_data {
+    /** antenna */
+	u8 antenna;
+	/** Private structure */
+	struct _bt_private *pbt;
+} bt_hist_proc_data;
+
+/** Data structure for the Marvell Bluetooth device */
+typedef struct _bt_dev {
+	/** device name */
+	char name[DEV_NAME_LEN];
+	/** card pointer */
+	void *card;
+	/** IO port */
+	u32 ioport;
+	/** m_dev structure */
+	struct m_dev m_dev[MAX_RADIO_FUNC];
+
+	/** Tx download ready flag */
+	u8 tx_dnld_rdy;
+	/** Function */
+	u8 fn;
+	/** Rx unit */
+	u8 rx_unit;
+	/** Power Save mode : Timeout configuration */
+	u16 idle_timeout;
+	/** Power Save mode */
+	u8 psmode;
+	/** Power Save command */
+	u8 pscmd;
+	/** Host Sleep mode */
+	u8 hsmode;
+	/** Host Sleep command */
+	u8 hscmd;
+	/** Low byte is gap, high byte is GPIO */
+	u16 gpio_gap;
+	/** Host Sleep configuration command */
+	u8 hscfgcmd;
+	/** Host Send Cmd Flag		 */
+	u8 sendcmdflag;
+	/** opcode for Send Cmd */
+	u16 send_cmd_opcode;
+	/** Device Type			*/
+	u8 devType;
+	/** Device Features    */
+	u8 devFeature;
+	/** cmd52 function */
+	u8 cmd52_func;
+	/** cmd52 register */
+	u8 cmd52_reg;
+	/** cmd52 value */
+	u8 cmd52_val;
+	/** SDIO pull control command */
+	u8 sdio_pull_ctrl;
+	/** Low 2 bytes is pullUp, high 2 bytes for pull-down */
+	u32 sdio_pull_cfg;
+	/** Test mode command */
+	u8 test_mode;
+} bt_dev_t, *pbt_dev_t;
+
+/** Marvell bt adapter structure */
+typedef struct _bt_adapter {
+	/** Chip revision ID */
+	u8 chip_rev;
+    /** magic val */
+	u8 magic_val;
+	/** Surprise removed flag */
+	u8 SurpriseRemoved;
+	/** IRQ number */
+	int irq;
+	/** Interrupt counter */
+	u32 IntCounter;
+	/** Tx packet queue */
+	struct sk_buff_head tx_queue;
+
+	/** Pointer of fw dump file name */
+	char *fwdump_fname;
+	/** Fw dump queue */
+	struct sk_buff_head fwdump_queue;
+	/** Pending Tx packet queue */
+	struct sk_buff_head pending_queue;
+	/** tx lock flag */
+	u8 tx_lock;
+	/** Power Save mode */
+	u8 psmode;
+	/** Power Save state */
+	u8 ps_state;
+	/** Host Sleep state */
+	u8 hs_state;
+	/** hs skip count */
+	u32 hs_skip;
+	/** suspend_fail flag */
+	u8 suspend_fail;
+	/** suspended flag */
+	u8 is_suspended;
+	/** Number of wakeup tries */
+	u8 WakeupTries;
+	/** Host Sleep wait queue */
+	wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__;
+	/** Host Cmd complet state */
+	u8 cmd_complete;
+	/** last irq recv */
+	u8 irq_recv;
+	/** last irq processed */
+	u8 irq_done;
+	/** sdio int status */
+	u8 sd_ireg;
+     /** buf allocated for transmit */
+	u8 *tx_buffer;
+    /** buf for transmit */
+	u8 *tx_buf;
+    /** buf allocated for read interrupt status */
+	u8 *hw_regs_buf;
+    /** buf for read interrupt status */
+	u8 *hw_regs;
+	/** tx pending */
+	u32 skb_pending;
+/** Version string buffer length */
+#define MAX_VER_STR_LEN         128
+	/** Driver version */
+	u8 drv_ver[MAX_VER_STR_LEN];
+	/** Number of command timeout */
+	u32 num_cmd_timeout;
+} bt_adapter, *pbt_adapter;
+
+/** Length of prov name */
+#define PROC_NAME_LEN				32
+
+/** Item data structure */
+struct item_data {
+	/** Name */
+	char name[PROC_NAME_LEN];
+	/** Size */
+	u32 size;
+	/** Address */
+	t_ptr addr;
+	/** Offset */
+	u32 offset;
+	/** Flag */
+	u32 flag;
+};
+
+/** Proc private data structure */
+struct proc_private_data {
+	/** Name */
+	char name[PROC_NAME_LEN];
+	/** File flag */
+	u32 fileflag;
+	/** Buffer size */
+	u32 bufsize;
+	/** Number of items */
+	u32 num_items;
+	/** Item data */
+	struct item_data *pdata;
+	/** Private structure */
+	struct _bt_private *pbt;
+	/** File operations */
+	const struct file_operations *fops;
+};
+
+/** Device proc structure */
+struct device_proc {
+	/** Proc directory entry */
+	struct proc_dir_entry *proc_entry;
+    /** proc entry for hist */
+	struct proc_dir_entry *hist_entry;
+	/** num of proc files */
+	u8 num_proc_files;
+	/** pointer to proc_private_data */
+	struct proc_private_data *pfiles;
+};
+
+/** Private structure for the MV device */
+typedef struct _bt_private {
+	/** Bluetooth device */
+	bt_dev_t bt_dev;
+	/** Adapter */
+	bt_adapter *adapter;
+	/** Firmware helper */
+	const struct firmware *fw_helper;
+	/** Firmware */
+	const struct firmware *firmware;
+	/** Init user configure file */
+	const struct firmware *init_user_cfg;
+	/** Init user configure wait queue token */
+	u16 init_user_conf_wait_flag;
+	/** Init user configure file wait queue */
+	wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__;
+	/** Firmware request start time */
+	struct timeval req_fw_time;
+	/** Hotplug device */
+	struct device *hotplug_device;
+	/** thread to service interrupts */
+	bt_thread MainThread;
+	 /** proc data */
+	struct device_proc dev_proc[MAX_RADIO_FUNC];
+	/** Driver lock */
+	spinlock_t driver_lock;
+	/** Driver lock flags */
+	ulong driver_flags;
+	/** Driver reference flags */
+	struct kobject kobj;
+	int debug_device_pending;
+	int debug_ocf_ogf[2];
+	u8 fw_reload;
+	/* hist_data_len */
+	u8 hist_data_len;
+    /** hist data */
+	bt_histogram_data hist_data[MAX_ANTENNA_NUM];
+    /** hist proc data */
+	bt_hist_proc_data hist_proc[MAX_ANTENNA_NUM];
+#ifdef BLE_WAKEUP
+	u8 ble_wakeup_buf_size;
+	u8 *ble_wakeup_buf;
+#endif
+} bt_private, *pbt_private;
+
+int bt_get_histogram(bt_private *priv);
+
+/** Disable interrupt */
+#define OS_INT_DISABLE	spin_lock_irqsave(&priv->driver_lock, \
+						priv->driver_flags)
+/** Enable interrupt */
+#define	OS_INT_RESTORE	spin_unlock_irqrestore(&priv->driver_lock, \
+						priv->driver_flags)
+
+#ifndef HCI_BT_AMP
+/** BT_AMP flag for device type */
+#define  HCI_BT_AMP		0x80
+#endif
+
+/** Device type of BT */
+#define DEV_TYPE_BT		0x00
+/** Device type of AMP */
+#define DEV_TYPE_AMP		0x01
+
+/** Marvell vendor packet */
+#define MRVL_VENDOR_PKT			0xFE
+
+/** Bluetooth command : Get FW Version */
+#define BT_CMD_GET_FW_VERSION       0x0F
+/** Bluetooth command : Sleep mode */
+#define BT_CMD_AUTO_SLEEP_MODE		0x23
+/** Bluetooth command : Host Sleep configuration */
+#define BT_CMD_HOST_SLEEP_CONFIG	0x59
+/** Bluetooth command : Host Sleep enable */
+#define BT_CMD_HOST_SLEEP_ENABLE	0x5A
+/** Bluetooth command : Module Configuration request */
+#define BT_CMD_MODULE_CFG_REQ		0x5B
+
+/** Bluetooth command : PMIC Configure */
+#define BT_CMD_PMIC_CONFIGURE           0x7D
+
+/** Bluetooth command : SDIO pull up down configuration request */
+#define BT_CMD_SDIO_PULL_CFG_REQ	0x69
+/** Bluetooth command : Set Evt Filter Command */
+#define BT_CMD_SET_EVT_FILTER		0x05
+/** Bluetooth command : Enable Write Scan Command */
+#define BT_CMD_ENABLE_WRITE_SCAN	0x1A
+/** Bluetooth command : Enable Device under test mode */
+#define BT_CMD_ENABLE_DEVICE_TESTMODE	0x03
+/** Sub Command: Module Bring Up Request */
+#define MODULE_BRINGUP_REQ		0xF1
+/** Sub Command: Module Shut Down Request */
+#define MODULE_SHUTDOWN_REQ		0xF2
+/** Module already up */
+#define MODULE_CFG_RESP_ALREADY_UP      0x0c
+/** Sub Command: Host Interface Control Request */
+#define MODULE_INTERFACE_CTRL_REQ	0xF5
+
+/** Bluetooth event : Power State */
+#define BT_EVENT_POWER_STATE		0x20
+
+/** Bluetooth Power State : Enable */
+#define BT_PS_ENABLE			0x02
+/** Bluetooth Power State : Disable */
+#define BT_PS_DISABLE			0x03
+/** Bluetooth Power State : Sleep */
+#define BT_PS_SLEEP			0x01
+/** Bluetooth Power State : Awake */
+#define BT_PS_AWAKE			0x02
+
+/** Vendor OGF */
+#define VENDOR_OGF				0x3F
+/** OGF for reset */
+#define RESET_OGF		0x03
+/** Bluetooth command : Reset */
+#define BT_CMD_RESET	0x03
+
+/** Host Sleep activated */
+#define HS_ACTIVATED			0x01
+/** Host Sleep deactivated */
+#define HS_DEACTIVATED			0x00
+
+/** Power Save sleep */
+#define PS_SLEEP			0x01
+/** Power Save awake */
+#define PS_AWAKE			0x00
+
+/** bt header length */
+#define BT_HEADER_LEN			4
+
+#ifndef MAX
+/** Return maximum of two */
+#define MAX(a, b)		((a) > (b) ? (a) : (b))
+#endif
+
+/** This is for firmware specific length */
+#define EXTRA_LEN	36
+
+/** Command buffer size for Marvell driver */
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+
+/** Bluetooth Rx packet buffer size for Marvell driver */
+#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
+	(HCI_MAX_FRAME_SIZE + EXTRA_LEN)
+
+/** Buffer size to allocate */
+#define ALLOC_BUF_SIZE	(((MAX(MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
+			MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+			+ SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE)
+
+/** Request FW timeout in second */
+#define REQUEST_FW_TIMEOUT		30
+
+/** The number of times to try when polling for status bits */
+#define MAX_POLL_TRIES			100
+
+/** The number of times to try when waiting for downloaded firmware to
+    become active when multiple interface is present */
+#define MAX_MULTI_INTERFACE_POLL_TRIES  150
+
+/** The number of times to try when waiting for downloaded firmware to
+     become active. (polling the scratch register). */
+#define MAX_FIRMWARE_POLL_TRIES		100
+
+/** default idle time */
+#define DEFAULT_IDLE_TIME           1000
+
+#define BT_CMD_HEADER_SIZE    3
+
+/** BT command structure */
+typedef struct _BT_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** Data */
+	u8 data[128];
+} __ATTRIB_PACK__ BT_CMD;
+
+/** BT event structure */
+typedef struct _BT_EVENT {
+	/** Event Counter */
+	u8 EC;
+	/** Length */
+	u8 length;
+	/** Data */
+	u8 data[8];
+} BT_EVENT;
+
+#if defined(SDIO_SUSPEND_RESUME)
+#define DEF_GPIO_GAP        0xffff
+#endif
+
+#ifdef BLE_WAKEUP
+#define BD_ADDR_SIZE 6
+/** Vendor specific event */
+#define VENDOR_SPECIFIC_EVENT     0xff
+/** system suspend event */
+#define HCI_SYSTEM_SUSPEND_EVT    0x80
+/** system suspend */
+#define HCI_SYSTEM_SUSPEND        0x00
+/** system resume */
+#define HCI_SYSTEM_RESUME         0x01
+/** This function enables ble wake up pattern */
+int bt_config_ble_wakeup(bt_private *priv);
+int bt_send_system_event(bt_private *priv, u8 flag);
+#endif
+
+/** This function verify the received event pkt */
+int check_evtpkt(bt_private *priv, struct sk_buff *skb);
+
+/* Prototype of global function */
+/** This function gets the priv reference */
+struct kobject *bt_priv_get(bt_private *priv);
+/** This function release the priv reference */
+void bt_priv_put(bt_private *priv);
+/** This function adds the card */
+bt_private *bt_add_card(void *card);
+/** This function removes the card */
+int bt_remove_card(void *card);
+/** This function handles the interrupt */
+void bt_interrupt(struct m_dev *m_dev);
+
+/** This function creates proc interface directory structure */
+int bt_root_proc_init(void);
+/** This function removes proc interface directory structure */
+int bt_root_proc_remove(void);
+/** This function initializes proc entry */
+int bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq);
+/** This function removes proc interface */
+void bt_proc_remove(bt_private *priv);
+
+/** This function process the received event */
+int bt_process_event(bt_private *priv, struct sk_buff *skb);
+/** This function enables host sleep */
+int bt_enable_hs(bt_private *priv);
+/** This function used to send command to firmware */
+int bt_prepare_command(bt_private *priv);
+/** This function frees the structure of adapter */
+void bt_free_adapter(bt_private *priv);
+/** This function handle the receive packet */
+void bt_recv_frame(bt_private *priv, struct sk_buff *skb);
+
+/** clean up m_devs */
+void clean_up_m_devs(bt_private *priv);
+/** bt driver call this function to register to bus driver */
+int *sbi_register(void);
+/** bt driver call this function to unregister to bus driver */
+void sbi_unregister(void);
+/** bt driver calls this function to register the device  */
+int sbi_register_dev(bt_private *priv);
+/** bt driver calls this function to unregister the device */
+int sbi_unregister_dev(bt_private *priv);
+/** This function initializes firmware */
+int sbi_download_fw(bt_private *priv);
+/** Configures hardware to quit deep sleep state */
+int sbi_wakeup_firmware(bt_private *priv);
+/** Module configuration and register device */
+int sbi_register_conf_dpc(bt_private *priv);
+
+/** This function is used to send the data/cmd to hardware */
+int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb);
+/** This function reads the current interrupt status register */
+int sbi_get_int_status(bt_private *priv);
+/** This function enables the host interrupts */
+int sbi_enable_host_int(bt_private *priv);
+/** This function disables the host interrupts */
+int sbi_disable_host_int(bt_private *priv);
+
+/** bt fw reload flag */
+extern int bt_fw_reload;
+/** driver initial the fw reset */
+#define FW_RELOAD_SDIO_INBAND_RESET   1
+/** out band reset trigger reset, no interface re-emulation */
+#define FW_RELOAD_NO_EMULATION  2
+/** out band reset with interface re-emulation */
+#define FW_RELOAD_WITH_EMULATION 3
+/** This function reload firmware */
+void bt_request_fw_reload(bt_private *priv, int mode);
+#define MAX_TX_BUF_SIZE     2312
+/** This function downloads firmware image to the card */
+int sd_download_firmware_w_helper(bt_private *priv);
+void bt_dump_sdio_regs(bt_private *priv);
+/* dumps the firmware to /var/ or /data/ */
+void bt_dump_firmware_info_v2(bt_private *priv);
+
+/** Max line length allowed in init config file */
+#define MAX_LINE_LEN        256
+/** Max MAC address string length allowed */
+#define MAX_MAC_ADDR_LEN    18
+/** Max register type/offset/value etc. parameter length allowed */
+#define MAX_PARAM_LEN       12
+
+/** Bluetooth command : Mac address configuration */
+#define BT_CMD_CONFIG_MAC_ADDR		0x22
+/** Bluetooth command : Write CSU register */
+#define BT_CMD_CSU_WRITE_REG		0x66
+/** Bluetooth command : Load calibrate data */
+#define BT_CMD_LOAD_CONFIG_DATA     0x61
+/** Bluetooth command : Load calibrate ext data */
+#define BT_CMD_LOAD_CONFIG_DATA_EXT     0x60
+
+/** Bluetooth command : BLE deepsleep */
+#define BT_CMD_BLE_DEEP_SLEEP       0x8b
+
+/** BT_BLE command structure */
+typedef struct _BT_BLE_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** deepsleep flag */
+	u8 deepsleep;
+} __ATTRIB_PACK__ BT_BLE_CMD;
+
+/** BT_CSU command structure */
+typedef struct _BT_CSU_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** reg type */
+	u8 type;
+	/** address */
+	u8 offset[4];
+	/** Data */
+	u8 value[2];
+} __ATTRIB_PACK__ BT_CSU_CMD;
+
+/** This function sets mac address */
+int bt_set_mac_address(bt_private *priv, u8 *mac);
+/** This function writes value to CSU registers */
+int bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value);
+/** BT set user defined init data and param */
+int bt_init_config(bt_private *priv, char *cfg_file);
+/** BT PMIC Configure command */
+int bt_pmic_configure(bt_private *priv);
+/** This function load the calibrate data */
+int bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac);
+/** This function load the calibrate ext data */
+int bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len);
+/** BT set user defined calibration data */
+int bt_cal_config(bt_private *priv, char *cfg_file, char *mac);
+/** BT set user defined calibration ext data */
+int bt_cal_config_ext(bt_private *priv, char *cfg_file);
+int bt_init_mac_address(bt_private *priv, char *mac);
+
+int bt_set_independent_reset(bt_private *priv);
+/** Bluetooth command : Independent reset */
+#define BT_CMD_INDEPENDENT_RESET     0x0D
+
+/** BT HCI command structure */
+typedef struct _BT_HCI_CMD {
+	/** OCF OGF */
+	u16 ocf_ogf;
+	/** Length */
+	u8 length;
+	/** cmd type */
+	u8 cmd_type;
+	/** cmd len */
+	u8 cmd_len;
+	/** Data */
+	u8 data[6];
+} __ATTRIB_PACK__ BT_HCI_CMD;
+
+#endif /* _BT_DRV_H_ */
diff --git a/bt_sd8997/bt_char/bt_init.c b/bt_sd8997/bt_char/bt_init.c
new file mode 100644
index 0000000..9d18912
--- /dev/null
+++ b/bt_sd8997/bt_char/bt_init.c
@@ -0,0 +1,751 @@
+/** @file bt_init.c
+  *
+  * @brief This file contains the init functions for BlueTooth
+  * driver.
+  *
+  * Copyright (C) 2011-2018, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/string.h>
+#include <linux/firmware.h>
+
+#include "bt_drv.h"
+
+extern int bt_req_fw_nowait;
+
+#define isxdigit(c)	(('0' <= (c) && (c) <= '9') \
+			 || ('a' <= (c) && (c) <= 'f') \
+			 || ('A' <= (c) && (c) <= 'F'))
+
+#define isdigit(c)	(('0' <= (c) && (c) <= '9'))
+#define isspace(c)  (c <= ' ' && (c == ' ' || (c <= 13 && c >= 9)))
+/**
+ *  @brief Returns hex value of a give character
+ *
+ *  @param chr	Character to be converted
+ *
+ *  @return	The converted character if chr is a valid hex, else 0
+ */
+static int
+bt_hexval(char chr)
+{
+	ENTER();
+
+	if (chr >= '0' && chr <= '9')
+		return chr - '0';
+	if (chr >= 'A' && chr <= 'F')
+		return chr - 'A' + 10;
+	if (chr >= 'a' && chr <= 'f')
+		return chr - 'a' + 10;
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Extension of strsep lib command. This function will also take care
+ *	   escape character
+ *
+ *  @param s         A pointer to array of chars to process
+ *  @param delim     The delimiter character to end the string
+ *  @param esc       The escape character to ignore for delimiter
+ *
+ *  @return          Pointer to the separated string if delim found, else NULL
+ */
+static char *
+bt_strsep(char **s, char delim, char esc)
+{
+	char *se = *s, *sb;
+
+	ENTER();
+
+	if (!(*s) || (*se == '\0')) {
+		LEAVE();
+		return NULL;
+	}
+
+	for (sb = *s; *sb != '\0'; ++sb) {
+		if (*sb == esc && *(sb + 1) == esc) {
+			/*
+			 * We get a esc + esc seq then keep the one esc
+			 * and chop off the other esc character
+			 */
+			memmove(sb, sb + 1, strlen(sb));
+			continue;
+		}
+		if (*sb == esc && *(sb + 1) == delim) {
+			/*
+			 * We get a delim + esc seq then keep the delim
+			 * and chop off the esc character
+			 */
+			memmove(sb, sb + 1, strlen(sb));
+			continue;
+		}
+		if (*sb == delim)
+			break;
+	}
+
+	if (*sb == '\0')
+		sb = NULL;
+	else
+		*sb++ = '\0';
+
+	*s = sb;
+
+	LEAVE();
+	return se;
+}
+
+/**
+ *  @brief Returns hex value of a given ascii string
+ *
+ *  @param a	String to be converted
+ *
+ *  @return	hex value
+ */
+static int
+bt_atox(const char *a)
+{
+	int i = 0;
+	ENTER();
+	while (isxdigit(*a))
+		i = i * 16 + bt_hexval(*a++);
+
+	LEAVE();
+	return i;
+}
+
+/**
+ *  @brief Converts mac address from string to t_u8 buffer.
+ *
+ *  @param mac_addr The buffer to store the mac address in.
+ *  @param buf      The source of mac address which is a string.
+ *
+ *  @return	N/A
+ */
+static void
+bt_mac2u8(u8 *mac_addr, char *buf)
+{
+	char *begin, *end, *mac_buff;
+	int i;
+
+	ENTER();
+
+	if (!buf) {
+		LEAVE();
+		return;
+	}
+
+	mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL);
+	if (!mac_buff) {
+		LEAVE();
+		return;
+	}
+	memcpy(mac_buff, buf, strlen(buf));
+
+	begin = mac_buff;
+	for (i = 0; i < ETH_ALEN; ++i) {
+		end = bt_strsep(&begin, ':', '/');
+		if (end)
+			mac_addr[i] = bt_atox(end);
+	}
+
+	kfree(mac_buff);
+	LEAVE();
+}
+
+/**
+ *  @brief Returns integer value of a given ascii string
+ *
+ *  @param data    Converted data to be returned
+ *  @param a       String to be converted
+ *
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_atoi(int *data, char *a)
+{
+	int i, val = 0, len;
+
+	ENTER();
+
+	len = strlen(a);
+	if (!strncmp(a, "0x", 2)) {
+		a = a + 2;
+		len -= 2;
+		*data = bt_atox(a);
+		return BT_STATUS_SUCCESS;
+	}
+	for (i = 0; i < len; i++) {
+		if (isdigit(a[i])) {
+			val = val * 10 + (a[i] - '0');
+		} else {
+			PRINTM(ERROR, "Invalid char %c in string %s\n", a[i],
+			       a);
+			return BT_STATUS_FAILURE;
+		}
+	}
+	*data = val;
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief parse cal-data
+ *
+ *  @param src      a pointer to cal-data string
+ *  @param len      len of cal-data
+ *  @param dst      a pointer to return cal-data
+ *  @param dst_size size of dest buffer
+ *
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 *dst_size)
+{
+	const u8 *ptr;
+	u8 *dptr;
+	u32 count = 0;
+	int ret = BT_STATUS_FAILURE;
+
+	ENTER();
+	ptr = src;
+	dptr = dst;
+
+	while ((ptr - src) < len) {
+		if (*ptr && isspace(*ptr)) {
+			ptr++;
+			continue;
+		}
+
+		if (isxdigit(*ptr)) {
+			if ((dptr - dst) >= *dst_size) {
+				PRINTM(ERROR, "cal_file size too big!!!\n");
+				goto done;
+			}
+			*dptr++ = bt_atox((const char *)ptr);
+			ptr += 2;
+			count++;
+		} else {
+			ptr++;
+		}
+	}
+	if (dptr == dst) {
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	*dst_size = count;
+	ret = BT_STATUS_SUCCESS;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT get one line data from ASCII format data
+ *
+ *    @param data         Source data
+ *    @param size         Source data length
+ *    @param line_pos     Destination data
+ *    @return             -1 or length of the line
+ */
+int
+parse_cfg_get_line(u8 *data, u32 size, u8 *line_pos)
+{
+	static s32 pos;
+	u8 *src, *dest;
+
+	if (pos >= size) {	/* reach the end */
+		pos = 0;	/* Reset position for rfkill */
+		return -1;
+	}
+	memset(line_pos, 0, MAX_LINE_LEN);
+	src = data + pos;
+	dest = line_pos;
+
+	while (pos < size && *src != '\x0A' && *src != '\0') {
+		if (*src != ' ' && *src != '\t')	/* parse space */
+			*dest++ = *src++;
+		else
+			src++;
+		pos++;
+	}
+	*dest = '\0';
+	/* parse new line */
+	pos++;
+	return strlen((const char *)line_pos);
+}
+
+/**
+ *    @brief BT parse ASCII format data to MAC address
+ *
+ *    @param priv          BT private handle
+ *    @param data          Source data
+ *    @param size          data length
+ *    @return              BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_init_cfg(bt_private *priv, u8 *data, u32 size)
+{
+	u8 *pos;
+	u8 *intf_s, *intf_e;
+	u8 s[MAX_LINE_LEN];	/* 1 line data */
+	u32 line_len;
+	char dev_name[MAX_PARAM_LEN];
+	u8 buf[MAX_PARAM_LEN];
+	u8 bt_addr[MAX_MAC_ADDR_LEN];
+	u8 bt_mac[ETH_ALEN];
+	int setting = 0;
+	u8 type = 0;
+	u16 value = 0;
+	u32 offset = 0;
+	int ret = BT_STATUS_FAILURE;
+
+	memset(dev_name, 0, sizeof(dev_name));
+	memset(bt_addr, 0, sizeof(bt_addr));
+	memset(bt_mac, 0, sizeof(bt_mac));
+
+	while ((line_len = parse_cfg_get_line(data, size, s)) != -1) {
+		pos = s;
+		while (*pos == ' ' || *pos == '\t')
+			pos++;
+
+		if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') ||
+		    *pos == '\n' || *pos == '\0')
+			continue;	/* Need n't process this line */
+
+		/* Process MAC addr */
+		if (strncmp((char *)pos, "mac_addr", 8) == 0) {
+			intf_s = (u8 *)strchr((const char *)pos, '=');
+			if (intf_s != NULL)
+				intf_e = (u8 *)strchr((const char *)intf_s,
+						      ':');
+			else
+				intf_e = NULL;
+			if (intf_s != NULL && intf_e != NULL) {
+				if ((intf_e - intf_s) > MAX_PARAM_LEN) {
+					PRINTM(ERROR,
+					       "BT: Too long interface name %d\n",
+					       __LINE__);
+					goto done;
+				}
+				strncpy(dev_name, (const char *)intf_s + 1,
+					intf_e - intf_s - 1);
+				dev_name[intf_e - intf_s - 1] = '\0';
+				strncpy((char *)bt_addr,
+					(const char *)intf_e + 1,
+					MAX_MAC_ADDR_LEN - 1);
+				bt_addr[MAX_MAC_ADDR_LEN - 1] = '\0';
+				/* Convert MAC format */
+				bt_mac2u8(bt_mac, (char *)bt_addr);
+				PRINTM(CMD,
+				       "HCI: %s new BT Address " MACSTR "\n",
+				       dev_name, MAC2STR(bt_mac));
+				if (BT_STATUS_SUCCESS !=
+				    bt_set_mac_address(priv, bt_mac)) {
+					PRINTM(FATAL,
+					       "BT: Fail to set mac address\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+		}
+		/* Process REG value */
+		else if (strncmp((char *)pos, "bt_reg", 6) == 0) {
+			intf_s = (u8 *)strchr((const char *)pos, '=');
+			if (intf_s != NULL)
+				intf_e = (u8 *)strchr((const char *)intf_s,
+						      ',');
+			else
+				intf_e = NULL;
+			if (intf_s != NULL && intf_e != NULL) {
+				/* Copy type */
+				memset(buf, 0, sizeof(buf));
+				strncpy((char *)buf, (const char *)intf_s + 1,
+					1);
+				buf[1] = '\0';
+				if (0 == bt_atoi(&setting, (char *)buf))
+					type = (u8)setting;
+				else {
+					PRINTM(ERROR,
+					       "BT: Fail to parse reg type\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+			intf_s = intf_e + 1;
+			intf_e = (u8 *)strchr((const char *)intf_s, ',');
+			if (intf_e != NULL) {
+				if ((intf_e - intf_s) >= MAX_PARAM_LEN) {
+					PRINTM(ERROR,
+					       "BT: Regsier offset is too long %d\n",
+					       __LINE__);
+					goto done;
+				}
+				/* Copy offset */
+				memset(buf, 0, sizeof(buf));
+				strncpy((char *)buf, (const char *)intf_s,
+					intf_e - intf_s);
+				buf[intf_e - intf_s] = '\0';
+				if (0 == bt_atoi(&setting, (char *)buf))
+					offset = (u32)setting;
+				else {
+					PRINTM(ERROR,
+					       "BT: Fail to parse reg offset\n");
+					goto done;
+				}
+			} else {
+				PRINTM(ERROR,
+				       "BT: Wrong config file format %d\n",
+				       __LINE__);
+				goto done;
+			}
+			intf_s = intf_e + 1;
+			if ((strlen((const char *)intf_s) >= MAX_PARAM_LEN)) {
+				PRINTM(ERROR,
+				       "BT: Regsier value is too long %d\n",
+				       __LINE__);
+				goto done;
+			}
+			/* Copy value */
+			memset(buf, 0, sizeof(buf));
+			strncpy((char *)buf, (const char *)intf_s, sizeof(buf));
+			if (0 == bt_atoi(&setting, (char *)buf))
+				value = (u16) setting;
+			else {
+				PRINTM(ERROR, "BT: Fail to parse reg value\n");
+				goto done;
+			}
+
+			PRINTM(CMD,
+			       "BT: Write reg type: %d offset: 0x%x value: 0x%x\n",
+			       type, offset, value);
+			if (BT_STATUS_SUCCESS !=
+			    bt_write_reg(priv, type, offset, value)) {
+				PRINTM(FATAL,
+				       "BT: Write reg failed. type: %d offset: 0x%x value: 0x%x\n",
+				       type, offset, value);
+				goto done;
+			}
+		}
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief BT request init conf firmware callback
+ *        This function is invoked by request_firmware_nowait system call
+ *
+ * @param firmware  A pointer to firmware image
+ * @param context   A pointer to bt_private structure
+ *
+ * @return          N/A
+ */
+static void
+bt_request_init_user_conf_callback(const struct firmware *firmware,
+				   void *context)
+{
+	bt_private *priv = (bt_private *)context;
+
+	ENTER();
+
+	if (!firmware)
+		PRINTM(ERROR, "BT user init config request firmware failed\n");
+
+	priv->init_user_cfg = firmware;
+	priv->init_user_conf_wait_flag = TRUE;
+	wake_up_interruptible(&priv->init_user_conf_wait_q);
+
+	LEAVE();
+	return;
+}
+
+/**
+ *    @brief BT set user defined init data and param
+ *
+ *    @param priv     BT private handle
+ *    @param cfg_file user cofig file
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_init_config(bt_private *priv, char *cfg_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if ((request_firmware(&cfg, cfg_file, priv->hotplug_device)) < 0) {
+		PRINTM(FATAL, "BT: request_firmware() %s failed\n", cfg_file);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (cfg)
+		ret = bt_process_init_cfg(priv, (u8 *)cfg->data, cfg->size);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration data
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param data    a pointer to cal data
+ *    @param size    cal data size
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_cal_cfg(bt_private *priv, u8 *data, u32 size, char *mac)
+{
+	u8 bt_mac[ETH_ALEN];
+	u8 cal_data[32];
+	u8 *mac_data = NULL;
+	u32 cal_data_len;
+	int ret = BT_STATUS_FAILURE;
+	u8 *pcal_data = cal_data;
+
+	memset(bt_mac, 0, sizeof(bt_mac));
+	cal_data_len = sizeof(cal_data);
+	if (BT_STATUS_SUCCESS !=
+	    bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) {
+		goto done;
+	}
+	if (mac != NULL) {
+		/* Convert MAC format */
+		bt_mac2u8(bt_mac, mac);
+		PRINTM(CMD, "HCI: new BT Address " MACSTR "\n",
+		       MAC2STR(bt_mac));
+		mac_data = bt_mac;
+	}
+	if (BT_STATUS_SUCCESS != bt_load_cal_data(priv, pcal_data, mac_data)) {
+		PRINTM(FATAL, "BT: Fail to load calibrate data\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration EXT data
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param data    a pointer to cal data
+ *    @param size    cal data size
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_cal_cfg_ext(bt_private *priv, u8 *data, u32 size)
+{
+	u8 cal_data[128];
+	u32 cal_data_len;
+	int ret = BT_STATUS_FAILURE;
+
+	cal_data_len = sizeof(cal_data);
+	if (BT_STATUS_SUCCESS !=
+	    bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) {
+		goto done;
+	}
+	if (BT_STATUS_SUCCESS !=
+	    bt_load_cal_data_ext(priv, cal_data, cal_data_len)) {
+		PRINTM(FATAL, "BT: Fail to load calibrate data\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration file
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param cal_file calibration file name
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_cal_config(bt_private *priv, char *cal_file, char *mac)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      bt_request_init_user_conf_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#endif
+#endif
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: bt_cal_config() failed, error code = %#x cal_file=%s\n",
+			       ret, cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->init_user_conf_wait_flag = FALSE;
+		wait_event_interruptible(priv->init_user_conf_wait_q,
+					 priv->init_user_conf_wait_flag);
+		cfg = priv->init_user_cfg;
+	} else {
+		if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) <
+		    0) {
+			PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+			       cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cfg)
+		ret = bt_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size, mac);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT process calibration EXT file
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param cal_file calibration file name
+ *    @param mac     mac address buf
+ *    @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_cal_config_ext(bt_private *priv, char *cal_file)
+{
+	const struct firmware *cfg = NULL;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      bt_request_init_user_conf_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cal_file, priv->hotplug_device,
+					      priv,
+					      bt_request_init_user_conf_callback);
+#endif
+#endif
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: bt_cal_config_ext() failed, error code = %#x cal_file=%s\n",
+			       ret, cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->init_user_conf_wait_flag = FALSE;
+		wait_event_interruptible(priv->init_user_conf_wait_q,
+					 priv->init_user_conf_wait_flag);
+		cfg = priv->init_user_cfg;
+	} else {
+		if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) <
+		    0) {
+			PRINTM(FATAL, "BT: request_firmware() %s failed\n",
+			       cal_file);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cfg)
+		ret = bt_process_cal_cfg_ext(priv, (u8 *)cfg->data, cfg->size);
+	else
+		ret = BT_STATUS_FAILURE;
+done:
+	if (cfg)
+		release_firmware(cfg);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *    @brief BT init mac address from bt_mac parametre when insmod
+ *
+ *    @param priv    a pointer to bt_private structure
+ *    @param bt_mac  mac address buf
+ *    @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_init_mac_address(bt_private *priv, char *mac)
+{
+	u8 bt_mac[ETH_ALEN];
+	int ret = BT_STATUS_FAILURE;
+
+	ENTER();
+	memset(bt_mac, 0, sizeof(bt_mac));
+	bt_mac2u8(bt_mac, mac);
+	PRINTM(CMD, "HCI: New BT Address " MACSTR "\n", MAC2STR(bt_mac));
+	ret = bt_set_mac_address(priv, bt_mac);
+	if (ret != BT_STATUS_SUCCESS)
+		PRINTM(FATAL,
+		       "BT: Fail to set mac address from insmod parametre.\n");
+
+	LEAVE();
+	return ret;
+}
diff --git a/bt_sd8997/bt_char/bt_main.c b/bt_sd8997/bt_char/bt_main.c
new file mode 100644
index 0000000..1ac0fc2
--- /dev/null
+++ b/bt_sd8997/bt_char/bt_main.c
@@ -0,0 +1,3750 @@
+/** @file bt_main.c
+  *
+  * @brief This file contains the major functions in BlueTooth
+  * driver. It includes init, exit, open, close and main
+  * thread etc..
+  *
+  * Copyright (C) 2007-2018, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+  *
+  */
+/**
+  * @mainpage M-BT Linux Driver
+  *
+  * @section overview_sec Overview
+  *
+  * The M-BT is a Linux reference driver for Marvell Bluetooth chipset.
+  *
+  * @section copyright_sec Copyright
+  *
+  * Copyright (C) 2007-2018, Marvell International Ltd.
+  *
+  */
+#include <linux/module.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
+
+#include <linux/mmc/sdio_func.h>
+
+#include "bt_drv.h"
+#include "mbt_char.h"
+#include "bt_sdio.h"
+
+/** Version */
+#define VERSION "C3X14113"
+
+/** Driver version */
+static char mbt_driver_version[] = "SD8997-%s-" VERSION "-(" "FP" FPNUM ")"
+#ifdef DEBUG_LEVEL2
+	"-dbg"
+#endif
+	" ";
+
+/** Declare and initialize fw_version */
+static char fw_version[32] = "0.0.0.p0";
+
+#define AID_SYSTEM        1000	/* system server */
+
+#define AID_BLUETOOTH     1002	/* bluetooth subsystem */
+
+#define AID_NET_BT_STACK  3008	/* bluetooth stack */
+
+/** Define module name */
+
+#define MODULE_NAME  "bt_fm_nfc"
+
+/** Declaration of chardev class */
+static struct class *chardev_class;
+
+/** Interface specific variables */
+static int mbtchar_minor;
+static int debugchar_minor;
+
+/**
+ * The global variable of a pointer to bt_private
+ * structure variable
+ **/
+bt_private *m_priv[MAX_BT_ADAPTER];
+
+/** Default Driver mode */
+static int drv_mode = (DRV_MODE_BT);
+
+/** BT interface name */
+static char *bt_name;
+/** BT debug interface name */
+static char *debug_name;
+
+/** fw reload flag */
+int bt_fw_reload;
+/** fw serial download flag */
+int bt_fw_serial = 1;
+
+/** Firmware flag */
+static int fw = 1;
+/** default powermode */
+static int psmode = 1;
+/** default BLE deep sleep */
+static int deep_sleep = 1;
+/** Init config file (MAC address, register etc.) */
+static char *init_cfg;
+/** Calibration config file (MAC address, init powe etc.) */
+static char *cal_cfg;
+/** Calibration config file EXT */
+static char *cal_cfg_ext;
+/** Init MAC address */
+static char *bt_mac;
+static int btindrst = -1;
+
+/** Setting mbt_drvdbg value based on DEBUG level */
+#ifdef DEBUG_LEVEL1
+#ifdef DEBUG_LEVEL2
+#define DEFAULT_DEBUG_MASK  (0xffffffff & ~DBG_EVENT)
+#else
+#define DEFAULT_DEBUG_MASK  (DBG_MSG | DBG_FATAL | DBG_ERROR)
+#endif /* DEBUG_LEVEL2 */
+u32 mbt_drvdbg = DEFAULT_DEBUG_MASK;
+#endif
+
+#ifdef CONFIG_OF
+static int dts_enable = 1;
+#endif
+
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+int mbt_pm_keep_power = 1;
+#endif
+
+static int debug_intf = 1;
+
+static int btpmic = 0;
+
+/** Offset of sequence number in event */
+#define OFFSET_SEQNUM 4
+
+/**
+ *  @brief handle received packet
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *
+ *  @return        N/A
+ */
+void
+bt_recv_frame(bt_private *priv, struct sk_buff *skb)
+{
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+	if (mbt_dev) {
+		skb->dev = (void *)mdev_bt;
+		mdev_recv_frame(skb);
+		mdev_bt->stat.byte_rx += skb->len;
+	}
+	return;
+}
+
+/**
+ *  @brief Alloc bt device
+ *
+ *  @return    pointer to structure mbt_dev or NULL
+ */
+struct mbt_dev *
+alloc_mbt_dev(void)
+{
+	struct mbt_dev *mbt_dev;
+	ENTER();
+
+	mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL);
+	if (!mbt_dev) {
+		LEAVE();
+		return NULL;
+	}
+
+	LEAVE();
+	return mbt_dev;
+}
+
+/**
+ *  @brief Alloc debug device
+ *
+ *  @return    pointer to structure debug_level or NULL
+ */
+struct debug_dev *
+alloc_debug_dev(void)
+{
+	struct debug_dev *debug_dev;
+	ENTER();
+
+	debug_dev = kzalloc(sizeof(struct debug_dev), GFP_KERNEL);
+	if (!debug_dev) {
+		LEAVE();
+		return NULL;
+	}
+
+	LEAVE();
+	return debug_dev;
+}
+
+/**
+ *  @brief Frees m_dev
+ *
+ *  @return    N/A
+ */
+void
+free_m_dev(struct m_dev *m_dev)
+{
+	ENTER();
+	kfree(m_dev->dev_pointer);
+	m_dev->dev_pointer = NULL;
+	LEAVE();
+}
+
+/**
+ *  @brief clean up m_devs
+ *
+ *  @return    N/A
+ */
+void
+clean_up_m_devs(bt_private *priv)
+{
+	struct m_dev *m_dev = NULL;
+	int i;
+
+	ENTER();
+	if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+		PRINTM(MSG, "BT: Delete %s\n", m_dev->name);
+		if (m_dev->spec_type == IANYWHERE_SPEC) {
+			if ((drv_mode & DRV_MODE_BT) && (mbtchar_minor > 0))
+				mbtchar_minor--;
+			m_dev->close(m_dev);
+			for (i = 0; i < 3; i++)
+				kfree_skb(((struct mbt_dev *)
+					   (m_dev->dev_pointer))->
+					  reassembly[i]);
+			/**  unregister m_dev to char_dev */
+			if (chardev_class)
+				chardev_cleanup_one(m_dev, chardev_class);
+			free_m_dev(m_dev);
+		}
+		priv->bt_dev.m_dev[BT_SEQ].dev_pointer = NULL;
+	}
+	if (priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer) {
+		m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+		PRINTM(MSG, "BT: Delete %s\n", m_dev->name);
+		if ((debug_intf) && (debugchar_minor > 0))
+			debugchar_minor--;
+		/** unregister m_dev to char_dev */
+		if (chardev_class)
+			chardev_cleanup_one(m_dev, chardev_class);
+		free_m_dev(m_dev);
+		priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = NULL;
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function verify the received event pkt
+ *
+ *  Event format:
+ *  +--------+--------+--------+--------+--------+
+ *  | Event  | Length |  ncmd  |      Opcode     |
+ *  +--------+--------+--------+--------+--------+
+ *  | 1-byte | 1-byte | 1-byte |      2-byte     |
+ *  +--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+check_evtpkt(bt_private *priv, struct sk_buff *skb)
+{
+	struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data;
+	struct hci_ev_cmd_complete *ec;
+	u16 opcode, ocf;
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (!priv->bt_dev.sendcmdflag) {
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	if (hdr->evt == HCI_EV_CMD_COMPLETE) {
+		ec = (struct hci_ev_cmd_complete *)
+			(skb->data + HCI_EVENT_HDR_SIZE);
+		opcode = __le16_to_cpu(ec->opcode);
+		ocf = hci_opcode_ocf(opcode);
+		PRINTM(CMD,
+		       "BT: CMD_COMPLTE opcode=0x%x, ocf=0x%x, send_cmd_opcode=0x%x\n",
+		       opcode, ocf, priv->bt_dev.send_cmd_opcode);
+		if (opcode != priv->bt_dev.send_cmd_opcode) {
+			ret = BT_STATUS_FAILURE;
+			goto exit;
+		}
+		switch (ocf) {
+		case BT_CMD_MODULE_CFG_REQ:
+		case BT_CMD_BLE_DEEP_SLEEP:
+		case BT_CMD_CONFIG_MAC_ADDR:
+		case BT_CMD_CSU_WRITE_REG:
+		case BT_CMD_LOAD_CONFIG_DATA:
+		case BT_CMD_LOAD_CONFIG_DATA_EXT:
+		case BT_CMD_AUTO_SLEEP_MODE:
+		case BT_CMD_HOST_SLEEP_CONFIG:
+		case BT_CMD_SDIO_PULL_CFG_REQ:
+		case BT_CMD_SET_EVT_FILTER:
+		case BT_CMD_ENABLE_WRITE_SCAN:
+			// case BT_CMD_ENABLE_DEVICE_TESTMODE:
+		case BT_CMD_RESET:
+		case BT_CMD_PMIC_CONFIGURE:
+		case BT_CMD_INDEPENDENT_RESET:
+			priv->bt_dev.sendcmdflag = FALSE;
+			priv->adapter->cmd_complete = TRUE;
+			wake_up_interruptible(&priv->adapter->cmd_wait_q);
+			break;
+		case BT_CMD_GET_FW_VERSION:
+			{
+				u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE +
+					   sizeof(struct hci_ev_cmd_complete) +
+					   1);
+				snprintf(fw_version, sizeof(fw_version),
+					 "%u.%u.%u.p%u", pos[2], pos[1], pos[0],
+					 pos[3]);
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+				break;
+			}
+		case BT_CMD_HISTOGRAM:
+			{
+				u8 *status =
+					skb->data + HCI_EVENT_HDR_SIZE +
+					sizeof(struct hci_ev_cmd_complete);
+				u8 *pos =
+					(skb->data + HCI_EVENT_HDR_SIZE +
+					 sizeof(struct hci_ev_cmd_complete) +
+					 1);
+				if (*status == 0) {
+					priv->hist_data_len =
+						hdr->plen -
+						sizeof(struct
+						       hci_ev_cmd_complete) - 1;
+					if (priv->hist_data_len >
+					    sizeof(priv->hist_data))
+						priv->hist_data_len =
+							sizeof(priv->hist_data);
+					memcpy(priv->hist_data, pos,
+					       priv->hist_data_len);
+					PRINTM(CMD, "histogram len=%d\n",
+					       priv->hist_data_len);
+				}
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+				break;
+			}
+		case BT_CMD_HOST_SLEEP_ENABLE:
+			priv->bt_dev.sendcmdflag = FALSE;
+			break;
+		default:
+			/** Ignore command not defined but send by driver */
+			if (opcode == priv->bt_dev.send_cmd_opcode) {
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			} else {
+				ret = BT_STATUS_FAILURE;
+			}
+			break;
+		}
+	}
+exit:
+	if (ret == BT_STATUS_SUCCESS)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+/**
+*  @brief This function stores the FW dumps received from events in a file
+*
+*  @param priv    A pointer to bt_private structure
+*  @param skb     A pointer to rx skb
+*
+*  @return        N/A
+*/
+void
+bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len)
+{
+	struct file *pfile_fwdump = NULL;
+	loff_t pos = 0;
+	u16 seqnum = 0;
+	struct timeval t;
+	u32 sec;
+
+	ENTER();
+
+	seqnum = __le16_to_cpu(*(u16 *) (buf + OFFSET_SEQNUM));
+
+	if (priv->adapter->fwdump_fname && seqnum != 1) {
+		pfile_fwdump =
+			filp_open((const char *)priv->adapter->fwdump_fname,
+				  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		if (IS_ERR(pfile_fwdump)) {
+			PRINTM(MSG, "Cannot create firmware dump file.\n");
+			LEAVE();
+			return;
+		}
+	} else {
+		if (!priv->adapter->fwdump_fname) {
+			gfp_t flag;
+			flag = (in_atomic() ||
+				irqs_disabled())? GFP_ATOMIC : GFP_KERNEL;
+			priv->adapter->fwdump_fname = kzalloc(64, flag);
+		} else
+			memset(priv->adapter->fwdump_fname, 0, 64);
+
+		do_gettimeofday(&t);
+		sec = (u32)t.tv_sec;
+		sprintf(priv->adapter->fwdump_fname, "%s%u",
+			"/var/log/bt_fwdump_", sec);
+		pfile_fwdump =
+			filp_open(priv->adapter->fwdump_fname,
+				  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		if (IS_ERR(pfile_fwdump)) {
+			sprintf(priv->adapter->fwdump_fname, "%s%u",
+				"/data/bt_fwdump_", sec);
+			pfile_fwdump =
+				filp_open((const char *)priv->adapter->
+					  fwdump_fname,
+					  O_CREAT | O_WRONLY | O_APPEND, 0644);
+		}
+	}
+
+	if (IS_ERR(pfile_fwdump)) {
+		PRINTM(MSG, "Cannot create firmware dump file\n");
+		LEAVE();
+		return;
+	}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	vfs_write(pfile_fwdump, buf, len, &pos);
+#else
+	kernel_write(pfile_fwdump, buf, len, &pos);
+#endif
+	filp_close(pfile_fwdump, NULL);
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function process the received event
+ *
+ *  Event format:
+ *  +--------+--------+--------+--------+-----+
+ *  |   EC   | Length |           Data        |
+ *  +--------+--------+--------+--------+-----+
+ *  | 1-byte | 1-byte |          n-byte       |
+ *  +--------+--------+--------+--------+-----+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to rx skb
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_event(bt_private *priv, struct sk_buff *skb)
+{
+	int ret = BT_STATUS_SUCCESS;
+#ifdef DEBUG_LEVEL1
+	struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+#endif
+	BT_EVENT *pevent;
+
+	ENTER();
+	if (!m_dev) {
+		PRINTM(CMD, "BT: bt_process_event without m_dev\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pevent = (BT_EVENT *)skb->data;
+	if (pevent->EC != 0xff) {
+		PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pevent->EC);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	switch (pevent->data[0]) {
+	case BT_CMD_HISTOGRAM:
+		break;
+	case BT_CMD_AUTO_SLEEP_MODE:
+		if (pevent->data[2] == BT_STATUS_SUCCESS) {
+			if (pevent->data[1] == BT_PS_ENABLE)
+				priv->adapter->psmode = 1;
+			else
+				priv->adapter->psmode = 0;
+			PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name,
+			       (priv->adapter->psmode) ? "Enable" : "Disable");
+
+		} else {
+			PRINTM(CMD, "BT: PS Mode Command Fail %s\n",
+			       m_dev->name);
+		}
+		break;
+	case BT_CMD_HOST_SLEEP_CONFIG:
+		if (pevent->data[3] == BT_STATUS_SUCCESS) {
+			PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n",
+			       m_dev->name, pevent->data[1], pevent->data[2]);
+		} else {
+			PRINTM(CMD, "BT: %s: HSCFG Command Fail\n",
+			       m_dev->name);
+		}
+		break;
+	case BT_CMD_HOST_SLEEP_ENABLE:
+		if (pevent->data[1] == BT_STATUS_SUCCESS) {
+			priv->adapter->hs_state = HS_ACTIVATED;
+			if (priv->adapter->suspend_fail == FALSE) {
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+				bt_is_suspended(priv);
+#endif
+#endif
+#endif
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			}
+			if (priv->adapter->psmode)
+				priv->adapter->ps_state = PS_SLEEP;
+			PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n",
+			       m_dev->name);
+
+		} else {
+			PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name);
+		}
+		break;
+	case BT_CMD_MODULE_CFG_REQ:
+		if ((priv->bt_dev.sendcmdflag == TRUE) &&
+		    ((pevent->data[1] == MODULE_BRINGUP_REQ)
+		     || (pevent->data[1] == MODULE_SHUTDOWN_REQ))) {
+			if (pevent->data[1] == MODULE_BRINGUP_REQ) {
+				PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+				       (pevent->data[2] && (pevent->data[2] !=
+							    MODULE_CFG_RESP_ALREADY_UP))
+				       ? "Bring up Fail" : "Bring up success");
+				priv->bt_dev.devType = pevent->data[3];
+				PRINTM(CMD, "devType:%s\n",
+				       (pevent->data[3] ==
+					DEV_TYPE_AMP) ? "AMP controller" :
+				       "BR/EDR controller");
+				priv->bt_dev.devFeature = pevent->data[4];
+				PRINTM(CMD, "devFeature:  %s,    %s,    %s"
+				       "\n",
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BT) ?
+					"BT Feature" : "No BT Feature"),
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BTAMP) ?
+					"BTAMP Feature" : "No BTAMP Feature"),
+				       ((pevent->
+					 data[4] & DEV_FEATURE_BLE) ?
+					"BLE Feature" : "No BLE Feature")
+					);
+			}
+			if (pevent->data[1] == MODULE_SHUTDOWN_REQ) {
+				PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+				       (pevent->data[2]) ? "Shut down Fail"
+				       : "Shut down success");
+
+			}
+			if (pevent->data[2]) {
+				priv->bt_dev.sendcmdflag = FALSE;
+				priv->adapter->cmd_complete = TRUE;
+				wake_up_interruptible(&priv->adapter->
+						      cmd_wait_q);
+			}
+		} else {
+			PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n");
+			ret = BT_STATUS_FAILURE;
+		}
+		break;
+	case BT_EVENT_POWER_STATE:
+		if (pevent->data[1] == BT_PS_SLEEP)
+			priv->adapter->ps_state = PS_SLEEP;
+		PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+		       (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
+
+		break;
+	case BT_CMD_SDIO_PULL_CFG_REQ:
+		if (pevent->data[pevent->length - 1] == BT_STATUS_SUCCESS)
+			PRINTM(CMD, "BT: %s: SDIO pull configuration success\n",
+			       m_dev->name);
+
+		else {
+			PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n",
+			       m_dev->name);
+
+		}
+		break;
+	default:
+		PRINTM(CMD, "BT: Unknown Event=%d %s\n", pevent->data[0],
+		       m_dev->name);
+		ret = BT_STATUS_FAILURE;
+		break;
+	}
+exit:
+	if (ret == BT_STATUS_SUCCESS)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function save the dump info to file
+ *
+ *  @param dir_name     directory name
+ *  @param file_name    file_name
+ *  @param buf			buffer
+ *  @param buf_len		buffer length
+ *
+ *  @return   		    0 --success otherwise fail
+ */
+int
+bt_save_dump_info_to_file(char *dir_name, char *file_name, u8 *buf, u32 buf_len)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct file *pfile = NULL;
+	u8 name[64];
+	loff_t pos;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	mm_segment_t fs;
+#endif
+
+	ENTER();
+
+	if (!dir_name || !file_name || !buf) {
+		PRINTM(ERROR, "Can't save dump info to file\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	memset(name, 0, sizeof(name));
+	sprintf((char *)name, "%s/%s", dir_name, file_name);
+	pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644);
+	if (IS_ERR(pfile)) {
+		PRINTM(MSG,
+		       "Create file %s error, try to save dump file in /var\n",
+		       name);
+		memset(name, 0, sizeof(name));
+		sprintf((char *)name, "%s/%s", "/var", file_name);
+		pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644);
+	}
+	if (IS_ERR(pfile)) {
+		PRINTM(ERROR, "Create Dump file for %s error\n", name);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	PRINTM(MSG, "Dump data %s saved in %s\n", file_name, name);
+
+	pos = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	vfs_write(pfile, (const char __user *)buf, buf_len, &pos);
+	set_fs(fs);
+#else
+	kernel_write(pfile, (const char __user *)buf, buf_len, &pos);
+#endif
+	filp_close(pfile, NULL);
+
+	PRINTM(MSG, "Dump data %s saved in %s successfully\n", file_name, name);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+#define DEBUG_HOST_READY		0xEE
+#define DEBUG_FW_DONE			0xFF
+#define DUMP_MAX_POLL_TRIES			200
+
+#define DEBUG_DUMP_CTRL_REG               0xF0
+#define DEBUG_DUMP_START_REG              0xF1
+#define DEBUG_DUMP_END_REG                0xF8
+
+typedef enum {
+	DUMP_TYPE_ITCM = 0,
+	DUMP_TYPE_DTCM = 1,
+	DUMP_TYPE_SQRAM = 2,
+	DUMP_TYPE_APU_REGS = 3,
+	DUMP_TYPE_CIU_REGS = 4,
+	DUMP_TYPE_ICU_REGS = 5,
+	DUMP_TYPE_MAC_REGS = 6,
+	DUMP_TYPE_EXTEND_7 = 7,
+	DUMP_TYPE_EXTEND_8 = 8,
+	DUMP_TYPE_EXTEND_9 = 9,
+	DUMP_TYPE_EXTEND_10 = 10,
+	DUMP_TYPE_EXTEND_11 = 11,
+	DUMP_TYPE_EXTEND_12 = 12,
+	DUMP_TYPE_EXTEND_13 = 13,
+	DUMP_TYPE_EXTEND_LAST = 14
+} dumped_mem_type;
+
+#define MAX_NAME_LEN               8
+#define MAX_FULL_NAME_LEN               32
+
+/** Memory type mapping structure */
+typedef struct {
+	/** memory name */
+	u8 mem_name[MAX_NAME_LEN];
+	/** memory pointer */
+	u8 *mem_Ptr;
+	/** file structure */
+	struct file *pfile_mem;
+	/** donbe flag */
+	u8 done_flag;
+} memory_type_mapping;
+
+memory_type_mapping bt_mem_type_mapping_tbl[] = {
+	{"ITCM", NULL, NULL, 0xF0},
+	{"DTCM", NULL, NULL, 0xF1},
+	{"SQRAM", NULL, NULL, 0xF2},
+	{"APU", NULL, NULL, 0xF3},
+	{"CIU", NULL, NULL, 0xF4},
+	{"ICU", NULL, NULL, 0xF5},
+	{"MAC", NULL, NULL, 0xF6},
+	{"EXT7", NULL, NULL, 0xF7},
+	{"EXT8", NULL, NULL, 0xF8},
+	{"EXT9", NULL, NULL, 0xF9},
+	{"EXT10", NULL, NULL, 0xFA},
+	{"EXT11", NULL, NULL, 0xFB},
+	{"EXT12", NULL, NULL, 0xFC},
+	{"EXT13", NULL, NULL, 0xFD},
+	{"EXTLAST", NULL, NULL, 0xFE},
+};
+
+typedef enum {
+	RDWR_STATUS_SUCCESS = 0,
+	RDWR_STATUS_FAILURE = 1,
+	RDWR_STATUS_DONE = 2
+} rdwr_status;
+
+/**
+ *  @brief This function read/write firmware via cmd52
+ *
+ *  @param phandle   A pointer to moal_handle
+ *
+ *  @return         MLAN_STATUS_SUCCESS
+ */
+rdwr_status
+bt_cmd52_rdwr_firmware(bt_private *priv, u8 doneflag)
+{
+	int ret = 0;
+	int tries = 0;
+	u8 ctrl_data = 0;
+	u8 dbg_dump_ctrl_reg = 0;
+
+	dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG;
+
+	sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.card)->func,
+		    DEBUG_HOST_READY, dbg_dump_ctrl_reg, &ret);
+	if (ret) {
+		PRINTM(ERROR, "SDIO Write ERR\n");
+		return RDWR_STATUS_FAILURE;
+	}
+	for (tries = 0; tries < DUMP_MAX_POLL_TRIES; tries++) {
+		ctrl_data =
+			sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->
+				   func, dbg_dump_ctrl_reg, &ret);
+		if (ret) {
+			PRINTM(ERROR, "SDIO READ ERR\n");
+			return RDWR_STATUS_FAILURE;
+		}
+		if (ctrl_data == DEBUG_FW_DONE)
+			break;
+		if (doneflag && ctrl_data == doneflag)
+			return RDWR_STATUS_DONE;
+		if (ctrl_data != DEBUG_HOST_READY) {
+			PRINTM(INFO,
+			       "The ctrl reg was changed, re-try again!\n");
+			sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.
+				     card)->func, DEBUG_HOST_READY,
+				    dbg_dump_ctrl_reg, &ret);
+			if (ret) {
+				PRINTM(ERROR, "SDIO Write ERR\n");
+				return RDWR_STATUS_FAILURE;
+			}
+		}
+		udelay(100);
+	}
+	if (ctrl_data == DEBUG_HOST_READY) {
+		PRINTM(ERROR, "Fail to pull ctrl_data\n");
+		return RDWR_STATUS_FAILURE;
+	}
+
+	return RDWR_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function dump firmware memory to file
+ *
+ *  @param phandle   A pointer to moal_handle
+ *
+ *  @return         N/A
+ */
+void
+bt_dump_firmware_info_v2(bt_private *priv)
+{
+	int ret = 0;
+	unsigned int reg, reg_start, reg_end;
+	u8 *dbg_ptr = NULL;
+	u8 dump_num = 0;
+	u8 idx = 0;
+	u8 doneflag = 0;
+	rdwr_status stat;
+	u8 i = 0;
+	u8 read_reg = 0;
+	u32 memory_size = 0;
+	u8 path_name[64], file_name[32];
+	u8 *end_ptr = NULL;
+	u8 dbg_dump_start_reg = 0;
+	u8 dbg_dump_end_reg = 0;
+
+	if (!priv) {
+		PRINTM(ERROR, "Could not dump firmwware info\n");
+		return;
+	}
+
+	memset(path_name, 0, sizeof(path_name));
+	strcpy((char *)path_name, "/data");
+	PRINTM(MSG, "Create DUMP directory success:dir_name=%s\n", path_name);
+
+	dbg_dump_start_reg = DEBUG_DUMP_START_REG;
+	dbg_dump_end_reg = DEBUG_DUMP_END_REG;
+
+	sbi_wakeup_firmware(priv);
+	sdio_claim_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func);
+	/* start dump fw memory */
+	PRINTM(MSG, "==== DEBUG MODE OUTPUT START ====\n");
+	/* read the number of the memories which will dump */
+	if (RDWR_STATUS_FAILURE == bt_cmd52_rdwr_firmware(priv, doneflag))
+		goto done;
+	reg = dbg_dump_start_reg;
+	dump_num =
+		sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->func,
+			   reg, &ret);
+	if (ret) {
+		PRINTM(MSG, "SDIO READ MEM NUM ERR\n");
+		goto done;
+	}
+
+	/* read the length of every memory which will dump */
+	for (idx = 0; idx < dump_num; idx++) {
+		if (RDWR_STATUS_FAILURE ==
+		    bt_cmd52_rdwr_firmware(priv, doneflag))
+			goto done;
+		memory_size = 0;
+		reg = dbg_dump_start_reg;
+		for (i = 0; i < 4; i++) {
+			read_reg =
+				sdio_readb(((struct sdio_mmc_card *)priv->
+					    bt_dev.card)->func, reg, &ret);
+			if (ret) {
+				PRINTM(MSG, "SDIO READ ERR\n");
+				goto done;
+			}
+			memory_size |= (read_reg << i * 8);
+			reg++;
+		}
+		if (memory_size == 0) {
+			PRINTM(MSG, "Firmware Dump Finished!\n");
+			break;
+		} else {
+			PRINTM(MSG, "%s_SIZE=0x%x\n",
+			       bt_mem_type_mapping_tbl[idx].mem_name,
+			       memory_size);
+			bt_mem_type_mapping_tbl[idx].mem_Ptr =
+				vmalloc(memory_size + 1);
+			if ((ret != BT_STATUS_SUCCESS) ||
+			    !bt_mem_type_mapping_tbl[idx].mem_Ptr) {
+				PRINTM(ERROR,
+				       "Error: vmalloc %s buffer failed!!!\n",
+				       bt_mem_type_mapping_tbl[idx].mem_name);
+				goto done;
+			}
+			dbg_ptr = bt_mem_type_mapping_tbl[idx].mem_Ptr;
+			end_ptr = dbg_ptr + memory_size;
+		}
+		doneflag = bt_mem_type_mapping_tbl[idx].done_flag;
+		PRINTM(MSG, "Start %s output, please wait...\n",
+		       bt_mem_type_mapping_tbl[idx].mem_name);
+		do {
+			stat = bt_cmd52_rdwr_firmware(priv, doneflag);
+			if (RDWR_STATUS_FAILURE == stat)
+				goto done;
+
+			reg_start = dbg_dump_start_reg;
+			reg_end = dbg_dump_end_reg;
+			for (reg = reg_start; reg <= reg_end; reg++) {
+				*dbg_ptr =
+					sdio_readb(((struct sdio_mmc_card *)
+						    priv->bt_dev.card)->func,
+						   reg, &ret);
+				if (ret) {
+					PRINTM(MSG, "SDIO READ ERR\n");
+					goto done;
+				}
+				if (dbg_ptr < end_ptr)
+					dbg_ptr++;
+				else
+					PRINTM(MSG,
+					       "pre-allocced buf is not enough\n");
+			}
+			if (RDWR_STATUS_DONE == stat) {
+				PRINTM(MSG, "%s done:"
+				       "size = 0x%x\n",
+				       bt_mem_type_mapping_tbl[idx].mem_name,
+				       (unsigned int)(dbg_ptr -
+						      bt_mem_type_mapping_tbl
+						      [idx].mem_Ptr));
+				memset(file_name, 0, sizeof(file_name));
+				sprintf((char *)file_name, "%s%s", "file_bt_",
+					bt_mem_type_mapping_tbl[idx].mem_name);
+				if (BT_STATUS_SUCCESS !=
+				    bt_save_dump_info_to_file((char *)path_name,
+							      (char *)file_name,
+							      bt_mem_type_mapping_tbl
+							      [idx].mem_Ptr,
+							      memory_size))
+					PRINTM(MSG,
+					       "Can't save dump file %s in %s\n",
+					       file_name, path_name);
+				vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr);
+				bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL;
+				break;
+			}
+		} while (1);
+	}
+	PRINTM(MSG, "==== DEBUG MODE OUTPUT END ====\n");
+	/* end dump fw memory */
+done:
+	sdio_release_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func);
+	for (idx = 0; idx < dump_num; idx++) {
+		if (bt_mem_type_mapping_tbl[idx].mem_Ptr) {
+			vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr);
+			bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL;
+		}
+	}
+	PRINTM(MSG, "==== DEBUG MODE END ====\n");
+	return;
+}
+
+/**
+ *  @brief This function shows debug info for timeout of command sending.
+ *
+ *  @param adapter  A pointer to bt_private
+ *  @param cmd      Timeout command id
+ *
+ *  @return         N/A
+ */
+static void
+bt_cmd_timeout_func(bt_private *priv, u16 cmd)
+{
+	bt_adapter *adapter = priv->adapter;
+	ENTER();
+
+	adapter->num_cmd_timeout++;
+
+	PRINTM(ERROR, "Version = %s\n", adapter->drv_ver);
+	PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd);
+	PRINTM(ERROR, "Number of command timeout = %d\n",
+	       adapter->num_cmd_timeout);
+	PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter);
+	PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode);
+	PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state);
+	PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state);
+	PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip);
+	PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail);
+	PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended);
+	PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries);
+	PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete);
+	PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv);
+	PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done);
+	PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending);
+	PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg);
+	bt_dump_sdio_regs(priv);
+	LEAVE();
+}
+
+/**
+ *  @brief This function queue frame
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    N/A
+ */
+static void
+bt_queue_frame(bt_private *priv, struct sk_buff *skb)
+{
+	skb_queue_tail(&priv->adapter->tx_queue, skb);
+}
+
+/**
+ *  @brief This function send reset cmd to firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return	       BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_reset_command(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((RESET_OGF << 10) | BT_CMD_RESET);
+	pcmd->length = 0x00;
+	pcmd->cmd_type = 0x00;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, 3);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "Queue Reset Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Reset timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_RESET);
+	} else {
+		PRINTM(CMD, "BT: Reset Command done\n");
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends module cfg cmd to firmware
+ *
+ *  Command format:
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     OCF OGF     | Length |                Data               |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     2-byte      | 1-byte |               4-byte              |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param subcmd  sub command
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_module_cfg_cmd(bt_private *priv, int subcmd)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "BT: No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ);
+	pcmd->length = 1;
+	pcmd->data[0] = subcmd;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "Queue module cfg Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	/*
+	   On some Android platforms certain delay is needed for HCI daemon to
+	   remove this module and close itself gracefully. Otherwise it hangs.
+	   This 10ms delay is a workaround for such platforms as the root cause
+	   has not been found yet. */
+	mdelay(10);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: module_cfg_cmd(%#x): timeout sendcmdflag=%d\n",
+		       subcmd, priv->bt_dev.sendcmdflag);
+		bt_cmd_timeout_func(priv, BT_CMD_MODULE_CFG_REQ);
+	} else {
+		PRINTM(CMD, "BT: module cfg Command done\n");
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function to get histogram
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_get_histogram(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HISTOGRAM);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Histogram cmd(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	priv->hist_data_len = 0;
+	memset(priv->hist_data, 0, sizeof(priv->hist_data));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: histogram timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_HISTOGRAM);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables power save mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_ps(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE);
+	if (priv->bt_dev.psmode)
+		pcmd->data[0] = BT_PS_ENABLE;
+	else
+		pcmd->data[0] = BT_PS_DISABLE;
+	if (priv->bt_dev.idle_timeout) {
+		pcmd->length = 3;
+		pcmd->data[1] = (u8)(priv->bt_dev.idle_timeout & 0x00ff);
+		pcmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8;
+	} else {
+		pcmd->length = 1;
+	}
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: psmode timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_AUTO_SLEEP_MODE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends hscfg command
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_hscfg_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG);
+	pcmd->length = 2;
+	pcmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8;
+	pcmd->data[1] = (u8)(priv->bt_dev.gpio_gap & 0x00ff);
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0], pcmd->data[1]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: HSCFG timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_CONFIG);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends sdio pull ctrl command
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_sdio_pull_ctrl_cmd(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ);
+	pcmd->length = 4;
+	pcmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff);
+	pcmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8;
+	pcmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16;
+	pcmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD,
+	       "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n",
+	       __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[1], pcmd->data[0],
+	       pcmd->data[3], pcmd->data[2]);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SDIO_PULL_CFG_REQ);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends command to configure PMIC
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_pmic_configure(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_PMIC_CONFIGURE);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue PMIC Configure Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: PMIC Configure timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_PMIC_CONFIGURE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables host sleep
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_hs(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	priv->adapter->suspend_fail = FALSE;
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE);
+	pcmd->length = 0;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	PRINTM(CMD, "Queue hs enable Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->hs_state,
+	     WAIT_UNTIL_HS_STATE_CHANGED)) {
+		PRINTM(MSG, "BT: Enable host sleep timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE);
+	}
+	OS_INT_DISABLE;
+	if ((priv->adapter->hs_state == HS_ACTIVATED) ||
+	    (priv->adapter->is_suspended == TRUE)) {
+		OS_INT_RESTORE;
+		PRINTM(MSG, "BT: suspend success! skip=%d\n",
+		       priv->adapter->hs_skip);
+	} else {
+		priv->adapter->suspend_fail = TRUE;
+		OS_INT_RESTORE;
+		priv->adapter->hs_skip++;
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG,
+		       "BT: suspend skipped! "
+		       "state=%d skip=%d ps_state= %d WakeupTries=%d\n",
+		       priv->adapter->hs_state, priv->adapter->hs_skip,
+		       priv->adapter->ps_state, priv->adapter->WakeupTries);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Set Evt Filter
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_evt_filter(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_SET_EVT_FILTER);
+	pcmd->length = 0x03;
+	pcmd->data[0] = 0x02;
+	pcmd->data[1] = 0x00;
+	pcmd->data[2] = 0x03;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Set Evt Filter Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set Evt Filter timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_SET_EVT_FILTER);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Enable Write Scan - Page and Inquiry
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_write_scan(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_ENABLE_WRITE_SCAN);
+	pcmd->length = 0x01;
+	pcmd->data[0] = 0x03;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue Enable Write Scan Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Enable Write Scan timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_ENABLE_WRITE_SCAN);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function Enable Device under test mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_device_under_testmode(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((0x06 << 10) | BT_CMD_ENABLE_DEVICE_TESTMODE);
+	pcmd->length = 0x00;
+	bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	PRINTM(CMD, "Queue enable device under testmode Command(0x%x)\n",
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Enable Device under TEST mode timeout\n");
+		bt_cmd_timeout_func(priv, BT_CMD_ENABLE_DEVICE_TESTMODE);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables test mode and send cmd
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_test_mode(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	/** Set Evt Filter Command */
+	ret = bt_set_evt_filter(priv);
+	if (ret != BT_STATUS_SUCCESS) {
+		PRINTM(ERROR, "BT test_mode: Set Evt filter fail\n");
+		goto exit;
+	}
+
+	/** Enable Write Scan Command */
+	ret = bt_enable_write_scan(priv);
+	if (ret != BT_STATUS_SUCCESS) {
+		PRINTM(ERROR, "BT test_mode: Enable Write Scan fail\n");
+		goto exit;
+	}
+
+	/** Enable Device under test mode */
+	ret = bt_enable_device_under_testmode(priv);
+	if (ret != BT_STATUS_SUCCESS)
+		PRINTM(ERROR,
+		       "BT test_mode: Enable device under testmode fail\n");
+
+exit:
+	LEAVE();
+	return ret;
+}
+
+#define DISABLE_RESET  0x0
+#define ENABLE_OUTBAND_RESET 0x1
+#define ENABLE_INBAND_RESET  0x02
+#define DEFAULT_GPIO 0xff
+/**
+ *  @brief This function set GPIO pin
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_independent_reset(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	u8 mode, gpio;
+	BT_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_INDEPENDENT_RESET);
+	mode = btindrst & 0xff;
+	gpio = (btindrst & 0xff00) >> 8;
+	if (mode == ENABLE_OUTBAND_RESET) {
+		pcmd->data[0] = ENABLE_OUTBAND_RESET;
+		if (!gpio)
+			pcmd->data[1] = DEFAULT_GPIO;
+		else
+			pcmd->data[1] = gpio;
+	} else if (mode == ENABLE_INBAND_RESET) {
+		pcmd->data[0] = ENABLE_INBAND_RESET;
+		pcmd->data[1] = DEFAULT_GPIO;
+	} else if (mode == DISABLE_RESET) {
+		pcmd->data[0] = DISABLE_RESET;
+		pcmd->data[1] = DEFAULT_GPIO;
+	} else {
+		PRINTM(WARN, "Unsupport mode\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	PRINTM(CMD, "BT: independant reset mode=%d gpio=%d\n", mode, gpio);
+	pcmd->length = 2;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Independent reset : timeout!\n");
+		bt_cmd_timeout_func(priv, BT_CMD_INDEPENDENT_RESET);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sets ble deepsleep mode
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mode    TRUE/FALSE
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_ble_deepsleep(bt_private *priv, int mode)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_BLE_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_BLE_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP);
+	pcmd->length = 1;
+	pcmd->deepsleep = mode;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_BLE_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode,
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_BLE_DEEP_SLEEP);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function gets FW version
+ *
+ *  @param priv    A pointer to bt_private structure
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_get_fw_version(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION);
+	pcmd->length = 0x01;
+	pcmd->cmd_type = 0x00;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, 4);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+					   priv->adapter->cmd_complete,
+					   WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Get FW version: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_GET_FW_VERSION);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sets mac address
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_mac_address(bt_private *priv, u8 *mac)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_HCI_CMD *pcmd;
+	int i = 0;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_HCI_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR);
+	pcmd->length = 8;
+	pcmd->cmd_type = MRVL_VENDOR_PKT;
+	pcmd->cmd_len = 6;
+	for (i = 0; i < 6; i++)
+		pcmd->data[i] = mac[5 - i];
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_HCI_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set mac addr " MACSTR " (0x%x)\n", MAC2STR(mac),
+	       __le16_to_cpu(pcmd->ocf_ogf));
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(MSG, "BT: Set mac addr: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_CONFIG_MAC_ADDR);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+	int i = 0;
+	/* u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF 0x01
+	   0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE 0xC0 0xC6 0x2D 0x00 0x00 0x00
+	   0x00 0x00 0x00 0x00 0xF0}; */
+
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA);
+	pcmd->length = 0x20;
+	pcmd->data[0] = 0x00;
+	pcmd->data[1] = 0x00;
+	pcmd->data[2] = 0x00;
+	pcmd->data[3] = 0x1C;
+	/* swip cal-data byte */
+	for (i = 4; i < 32; i++)
+		pcmd->data[i] = *(config_data + ((i / 4) * 8 - 1 - i));
+	if (mac != NULL) {
+		pcmd->data[2] = 0x01;	/* skip checksum */
+		for (i = 24; i < 30; i++)
+			pcmd->data[i] = mac[29 - i];
+	}
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "calirate data: ", pcmd->data, 32);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load calibrate data: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function load the calibrate EXT data
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param config_data     A pointer to calibrate data
+ *  @param mac     A pointer to mac address
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CMD *pcmd;
+
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA_EXT);
+	pcmd->length = cfg_data_len;
+
+	memcpy(pcmd->data, config_data, cfg_data_len);
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length);
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+
+	DBG_HEXDUMP(DAT_D, "calirate ext data", pcmd->data, pcmd->length);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Load calibrate ext data: timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function writes value to CSU registers
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param type    reg type
+ *  @param offset  register address
+ *  @param value   register value to write
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value)
+{
+	struct sk_buff *skb = NULL;
+	int ret = BT_STATUS_SUCCESS;
+	BT_CSU_CMD *pcmd;
+	ENTER();
+	skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "No free skb\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	pcmd = (BT_CSU_CMD *)skb->data;
+	pcmd->ocf_ogf =
+		__cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG);
+	pcmd->length = 7;
+	pcmd->type = type;
+	pcmd->offset[0] = (offset & 0x000000ff);
+	pcmd->offset[1] = (offset & 0x0000ff00) >> 8;
+	pcmd->offset[2] = (offset & 0x00ff0000) >> 16;
+	pcmd->offset[3] = (offset & 0xff000000) >> 24;
+	pcmd->value[0] = (value & 0x00ff);
+	pcmd->value[1] = (value & 0xff00) >> 8;
+	bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+	skb_put(skb, sizeof(BT_CSU_CMD));
+	skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+	skb_queue_head(&priv->adapter->tx_queue, skb);
+	priv->bt_dev.sendcmdflag = TRUE;
+	priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf);
+	priv->adapter->cmd_complete = FALSE;
+	PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n",
+	       type, offset, value);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	if (!os_wait_interruptible_timeout
+	    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+	     WAIT_UNTIL_CMD_RESP)) {
+		ret = BT_STATUS_FAILURE;
+		PRINTM(ERROR, "BT: Set CSU reg timeout:\n");
+		bt_cmd_timeout_func(priv, BT_CMD_CSU_WRITE_REG);
+	}
+exit:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function used to restore tx_queue
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        N/A
+ */
+void
+bt_restore_tx_queue(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	while (!skb_queue_empty(&priv->adapter->pending_queue)) {
+		skb = skb_dequeue(&priv->adapter->pending_queue);
+		if (skb)
+			bt_queue_frame(priv, skb);
+	}
+	wake_up_interruptible(&priv->MainThread.waitQ);
+}
+
+/**
+ *  @brief This function used to send command to firmware
+ *
+ *  Command format:
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     OCF OGF     | Length |                Data               |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *  |     2-byte      | 1-byte |               4-byte              |
+ *  +--------+--------+--------+--------+--------+--------+--------+
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_prepare_command(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (priv->bt_dev.hscfgcmd) {
+		priv->bt_dev.hscfgcmd = 0;
+		ret = bt_send_hscfg_cmd(priv);
+	}
+	if (priv->bt_dev.pscmd) {
+		priv->bt_dev.pscmd = 0;
+		ret = bt_enable_ps(priv);
+	}
+	if (priv->bt_dev.sdio_pull_ctrl) {
+		priv->bt_dev.sdio_pull_ctrl = 0;
+		ret = bt_send_sdio_pull_ctrl_cmd(priv);
+	}
+	if (priv->bt_dev.hscmd) {
+		priv->bt_dev.hscmd = 0;
+		if (priv->bt_dev.hsmode)
+			ret = bt_enable_hs(priv);
+		else {
+			ret = sbi_wakeup_firmware(priv);
+			priv->adapter->hs_state = HS_DEACTIVATED;
+		}
+	}
+	if (priv->bt_dev.test_mode) {
+		priv->bt_dev.test_mode = 0;
+		ret = bt_enable_test_mode(priv);
+	}
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function processes a single packet
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param skb     A pointer to skb which includes TX packet
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+send_single_packet(bt_private *priv, struct sk_buff *skb)
+{
+	int ret;
+	struct sk_buff *new_skb = NULL;
+	ENTER();
+	if (!skb || !skb->data) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) {
+		PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len,
+		       BT_UPLD_SIZE);
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	if (skb_headroom(skb) < BT_HEADER_LEN) {
+		new_skb = skb_realloc_headroom(skb, BT_HEADER_LEN);
+		if (!new_skb) {
+			PRINTM(ERROR, "TX error: realloc_headroom failed %d\n",
+			       BT_HEADER_LEN);
+			kfree_skb(skb);
+			LEAVE();
+			return BT_STATUS_FAILURE;
+		} else {
+			if (new_skb != skb)
+				dev_kfree_skb_any(skb);
+			skb = new_skb;
+		}
+	}
+	/* This is SDIO/PCIE specific header length: byte[3][2][1], * type:
+	   byte[0] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor)
+	 */
+	skb_push(skb, BT_HEADER_LEN);
+	skb->data[0] = (skb->len & 0x0000ff);
+	skb->data[1] = (skb->len & 0x00ff00) >> 8;
+	skb->data[2] = (skb->len & 0xff0000) >> 16;
+	skb->data[3] = bt_cb(skb)->pkt_type;
+	if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT)
+		PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n",
+		       __le16_to_cpu(*((u16 *) & skb->data[4])), skb->len);
+	ret = sbi_host_to_card(priv, skb->data, skb->len);
+	if (ret == BT_STATUS_FAILURE)
+		((struct m_dev *)skb->dev)->stat.err_tx++;
+	else
+		((struct m_dev *)skb->dev)->stat.byte_tx += skb->len;
+	if (ret != BT_STATUS_PENDING)
+		kfree_skb(skb);
+	LEAVE();
+	return ret;
+}
+
+#ifdef CONFIG_OF
+/**
+ *  @brief This function read the initial parameter from device tress
+ *
+ *
+ *  @return         N/A
+ */
+static void
+bt_init_from_dev_tree(void)
+{
+	struct device_node *dt_node = NULL;
+	struct property *prop;
+	u32 data;
+	const char *string_data;
+
+	ENTER();
+
+	if (!dts_enable) {
+		PRINTM(CMD, "DTS is disabled!");
+		return;
+	}
+
+	dt_node = of_find_node_by_name(NULL, "sd8xxx-bt");
+	if (!dt_node) {
+		LEAVE();
+		return;
+	}
+	for_each_property_of_node(dt_node, prop) {
+#ifdef DEBUG_LEVEL1
+		if (!strncmp(prop->name, "mbt_drvdbg", strlen("mbt_drvdbg"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				PRINTM(CMD, "mbt_drvdbg=0x%x\n", data);
+				mbt_drvdbg = data;
+			}
+		}
+#endif
+		else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				init_cfg = (char *)string_data;
+				PRINTM(CMD, "init_cfg=%s\n", init_cfg);
+			}
+		} else if (!strncmp
+			   (prop->name, "cal_cfg_ext", strlen("cal_cfg_ext"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				cal_cfg_ext = (char *)string_data;
+				PRINTM(CMD, "cal_cfg_ext=%s\n", cal_cfg_ext);
+			}
+		} else if (!strncmp(prop->name, "cal_cfg", strlen("cal_cfg"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				cal_cfg = (char *)string_data;
+				PRINTM(CMD, "cal_cfg=%s\n", cal_cfg);
+			}
+		} else if (!strncmp(prop->name, "bt_mac", strlen("bt_mac"))) {
+			if (!of_property_read_string
+			    (dt_node, prop->name, &string_data)) {
+				bt_mac = (char *)string_data;
+				PRINTM(CMD, "bt_mac=%s\n", bt_mac);
+			}
+		} else if (!strncmp(prop->name, "btindrst", strlen("btindrst"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				btindrst = data;
+				PRINTM(CMD, "btindrst=%d\n", btindrst);
+			}
+		} else if (!strncmp(prop->name, "btpmic", strlen("btpmic"))) {
+			if (!of_property_read_u32(dt_node, prop->name, &data)) {
+				btpmic = data;
+				PRINTM(CMD, "btpmic=%d\n", btpmic);
+			}
+		}
+	}
+	LEAVE();
+	return;
+}
+#endif
+
+/**
+ *  @brief This function initializes the adapter structure
+ *  and set default value to the member of adapter.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    N/A
+ */
+static void
+bt_init_adapter(bt_private *priv)
+{
+	ENTER();
+#ifdef CONFIG_OF
+	bt_init_from_dev_tree();
+#endif
+	skb_queue_head_init(&priv->adapter->tx_queue);
+	skb_queue_head_init(&priv->adapter->fwdump_queue);
+
+	skb_queue_head_init(&priv->adapter->pending_queue);
+	priv->adapter->tx_lock = FALSE;
+	priv->adapter->ps_state = PS_AWAKE;
+	priv->adapter->suspend_fail = FALSE;
+	priv->adapter->is_suspended = FALSE;
+	priv->adapter->hs_skip = 0;
+	priv->adapter->num_cmd_timeout = 0;
+	priv->adapter->fwdump_fname = NULL;
+	init_waitqueue_head(&priv->adapter->cmd_wait_q);
+	LEAVE();
+}
+
+/**
+ *  @brief This function initializes firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_fw(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	ENTER();
+	if (fw == 0) {
+		sbi_enable_host_int(priv);
+		goto done;
+	}
+	sbi_disable_host_int(priv);
+	if (sbi_download_fw(priv)) {
+		PRINTM(ERROR, " FW failed to be download!\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+done:
+	LEAVE();
+	return ret;
+}
+
+#define FW_POLL_TRIES 100
+#define FW_RESET_REG  0x0EE
+#define FW_RESET_VAL  0x99
+
+/**
+ *  @brief This function reload firmware
+ *
+ *  @param priv   A pointer to bt_private
+ *  @param mode   FW reload mode
+ *
+ *  @return       0--success, otherwise failure
+ */
+static int
+bt_reload_fw(bt_private *priv, int mode)
+{
+	int ret = 0, tries = 0;
+	u8 value = 1;
+	u32 reset_reg = FW_RESET_REG;
+	u8 reset_val = FW_RESET_VAL;
+
+	ENTER();
+	if ((mode != FW_RELOAD_SDIO_INBAND_RESET) &&
+	    (mode != FW_RELOAD_NO_EMULATION)) {
+		PRINTM(ERROR, "Invalid fw reload mode=%d\n", mode);
+		return -EFAULT;
+	}
+
+    /** flush pending tx_queue */
+	skb_queue_purge(&priv->adapter->tx_queue);
+	if (mode == FW_RELOAD_SDIO_INBAND_RESET) {
+		sbi_disable_host_int(priv);
+	    /** Wake up firmware firstly */
+		sbi_wakeup_firmware(priv);
+
+	/** wait SOC fully wake up */
+		for (tries = 0; tries < FW_POLL_TRIES; ++tries) {
+			ret = sd_write_reg(priv, reset_reg, 0xba);
+			if (!ret) {
+				ret = sd_read_reg(priv, reset_reg, &value);
+				if (!ret && (value == 0xba)) {
+					PRINTM(MSG, "Fw wake up\n");
+					break;
+				}
+			}
+			udelay(1000);
+		}
+
+		ret = sd_write_reg(priv, reset_reg, reset_val);
+		if (ret) {
+			PRINTM(ERROR, "Failed to write register.\n");
+			goto done;
+		}
+
+	    /** Poll register around 1 ms */
+		for (; tries < FW_POLL_TRIES; ++tries) {
+			ret = sd_read_reg(priv, reset_reg, &value);
+			if (ret) {
+				PRINTM(ERROR, "Failed to read register.\n");
+				goto done;
+			}
+			if (value == 0)
+			    /** FW is ready */
+				break;
+			udelay(1000);
+		}
+		if (value) {
+			PRINTM(ERROR,
+			       "Failed to poll FW reset register %X=0x%x\n",
+			       reset_reg, value);
+			ret = -EFAULT;
+			goto done;
+		}
+	}
+
+	sbi_enable_host_int(priv);
+	/** reload FW */
+	ret = bt_init_fw(priv);
+	if (ret) {
+		PRINTM(ERROR, "Re download firmware failed.\n");
+		ret = -EFAULT;
+	}
+	LEAVE();
+	return ret;
+done:
+	sbi_enable_host_int(priv);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function request to reload firmware
+ *
+ *  @param priv   A pointer to bt_private
+ *  @param mode   fw reload mode.
+ *
+ *  @return         N/A
+ */
+void
+bt_request_fw_reload(bt_private *priv, int mode)
+{
+	ENTER();
+	if (mode == FW_RELOAD_WITH_EMULATION) {
+		bt_fw_reload = FW_RELOAD_WITH_EMULATION;
+		PRINTM(MSG, "BT: FW reload with re-emulation...\n");
+		LEAVE();
+		return;
+	}
+	/** Reload FW */
+	priv->fw_reload = TRUE;
+	if (bt_reload_fw(priv, mode)) {
+		PRINTM(ERROR, "FW reload fail\n");
+		goto done;
+	}
+	priv->fw_reload = FALSE;
+	/** Other operation here? */
+done:
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function frees the structure of adapter
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return    N/A
+ */
+void
+bt_free_adapter(bt_private *priv)
+{
+	bt_adapter *adapter = priv->adapter;
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	skb_queue_purge(&priv->adapter->fwdump_queue);
+	kfree(adapter->tx_buffer);
+	kfree(adapter->hw_regs_buf);
+	/* Free allocated memory for fwdump filename */
+	if (adapter->fwdump_fname) {
+		kfree(adapter->fwdump_fname);
+		adapter->fwdump_fname = NULL;
+	}
+	/* Free the adapter object itself */
+	kfree(adapter);
+	priv->adapter = NULL;
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function handles the wrapper_dev ioctl
+ *
+ *  @param hev     A pointer to wrapper_dev structure
+ *  @cmd            ioctl cmd
+ *  @arg            argument
+ *  @return    -ENOIOCTLCMD
+ */
+static int
+mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, void *arg)
+{
+	bt_private *priv = NULL;
+	int ret = 0;
+#ifdef BLE_WAKEUP
+	u16 len;
+#endif
+
+	ENTER();
+
+	if (!m_dev || !m_dev->driver_data) {
+		PRINTM(ERROR, "Ioctl for unknown device (m_dev=NULL)\n");
+		ret = -ENODEV;
+		goto done;
+	}
+	priv = (bt_private *)m_dev->driver_data;
+	if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+		PRINTM(ERROR, "HCI_RUNNING not set, flag=0x%lx\n",
+		       m_dev->flags);
+		ret = -EBUSY;
+		goto done;
+	}
+	PRINTM(INFO, "IOCTL: cmd=%d\n", cmd);
+	switch (cmd) {
+#ifdef BLE_WAKEUP
+	case MBTCHAR_IOCTL_BLE_WAKEUP_PARAM:
+		PRINTM(MSG, "BT: Set ble wakeup parameters\n");
+		if (copy_from_user(&len, arg, sizeof(u16))) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Fail to copy ble wakeup params length\n");
+			ret = -EFAULT;
+			goto done;
+		}
+		/** Convert little endian length */
+		len = __le16_to_cpu(len);
+		if (len < 2) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Invalid ble wakeup params len %d\n",
+			       len);
+			ret = -EFAULT;
+			goto done;
+		}
+		if ((len + sizeof(u16)) > priv->ble_wakeup_buf_size) {
+			if (priv->ble_wakeup_buf) {
+				kfree(priv->ble_wakeup_buf);
+				priv->ble_wakeup_buf = NULL;
+				priv->ble_wakeup_buf_size = 0;
+			}
+			priv->ble_wakeup_buf =
+				kzalloc(len + sizeof(u16), GFP_KERNEL);
+			if (!priv->ble_wakeup_buf) {
+				PRINTM(ERROR, "BT_IOCTL: Fail to alloc buffer\t"
+				       "for ble wakeup parameters\n");
+				ret = -ENOMEM;
+				goto done;
+			}
+			priv->ble_wakeup_buf_size = len + sizeof(u16);
+		}
+		if (copy_from_user
+		    (priv->ble_wakeup_buf, arg, len + sizeof(u16))) {
+			PRINTM(ERROR,
+			       "BT_IOCTL: Fail to copy ble wakeup params\n");
+			ret = -EFAULT;
+			goto done;
+		}
+		DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_PARAM:", priv->ble_wakeup_buf,
+			    len + sizeof(u16));
+		break;
+#endif
+	default:
+		break;
+	}
+
+done:
+#ifdef BLE_WAKEUP
+	if (ret && priv->ble_wakeup_buf) {
+		kfree(priv->ble_wakeup_buf);
+		priv->ble_wakeup_buf = NULL;
+		priv->ble_wakeup_buf_size = 0;
+	}
+#endif
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function handles wrapper device destruct
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    N/A
+ */
+static void
+mdev_destruct(struct m_dev *m_dev)
+{
+	ENTER();
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function handles the wrapper device transmit
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @param skb     A pointer to sk_buff structure
+ *
+ *  @return    BT_STATUS_SUCCESS or other error no.
+ */
+static int
+mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb)
+{
+	bt_private *priv = NULL;
+
+	ENTER();
+	if (!m_dev || !m_dev->driver_data) {
+		PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n");
+		LEAVE();
+		return -ENODEV;
+	}
+	priv = (bt_private *)m_dev->driver_data;
+	if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+		PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n",
+		       m_dev->flags);
+		LEAVE();
+		return -EBUSY;
+	}
+	switch (bt_cb(skb)->pkt_type) {
+	case HCI_COMMAND_PKT:
+		m_dev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		m_dev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		m_dev->stat.sco_tx++;
+		break;
+	}
+
+	if (m_dev->dev_type == DEBUG_TYPE) {
+		/* remember the ogf_ocf */
+		priv->debug_device_pending = 1;
+		priv->debug_ocf_ogf[0] = skb->data[0];
+		priv->debug_ocf_ogf[1] = skb->data[1];
+		PRINTM(CMD, "debug_ocf_ogf[0]=0x%x debug_ocf_ogf[1]=0x%x\n",
+		       priv->debug_ocf_ogf[0], priv->debug_ocf_ogf[1]);
+	}
+
+	if (priv->adapter->tx_lock == TRUE)
+		skb_queue_tail(&priv->adapter->pending_queue, skb);
+	else
+		bt_queue_frame(priv, skb);
+	wake_up_interruptible(&priv->MainThread.waitQ);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function flushes the transmit queue
+ *
+ *  @param m_dev     A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+mdev_flush(struct m_dev *m_dev)
+{
+	bt_private *priv = (bt_private *)m_dev->driver_data;
+	ENTER();
+	skb_queue_purge(&priv->adapter->tx_queue);
+	skb_queue_purge(&priv->adapter->pending_queue);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function closes the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS
+ */
+static int
+mdev_close(struct m_dev *m_dev)
+{
+
+	ENTER();
+	mdev_req_lock(m_dev);
+	if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) {
+		mdev_req_unlock(m_dev);
+		LEAVE();
+		return 0;
+	}
+
+	if (m_dev->flush)
+		m_dev->flush(m_dev);
+	/* wait up pending read and unregister char dev */
+	wake_up_interruptible(&m_dev->req_wait_q);
+	/* Drop queues */
+	skb_queue_purge(&m_dev->rx_q);
+	if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) {
+		mdev_req_unlock(m_dev);
+		LEAVE();
+		return 0;
+	}
+	module_put(THIS_MODULE);
+	m_dev->flags = 0;
+	mdev_req_unlock(m_dev);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function opens the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+static int
+mdev_open(struct m_dev *m_dev)
+{
+	ENTER();
+
+	if (try_module_get(THIS_MODULE) == 0)
+		return BT_STATUS_FAILURE;
+
+	set_bit(HCI_RUNNING, &m_dev->flags);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function queries the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @param arg     arguement
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+void
+mdev_query(struct m_dev *m_dev, void *arg)
+{
+	struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer;
+
+	ENTER();
+	if (copy_to_user(arg, &mbt_dev->type, sizeof(mbt_dev->type)))
+		PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n");
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function initializes the wrapper device
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *
+ *  @return    BT_STATUS_SUCCESS  or other
+ */
+void
+init_m_dev(struct m_dev *m_dev)
+{
+	m_dev->dev_pointer = NULL;
+	m_dev->driver_data = NULL;
+	m_dev->dev_type = 0;
+	m_dev->spec_type = 0;
+	skb_queue_head_init(&m_dev->rx_q);
+	init_waitqueue_head(&m_dev->req_wait_q);
+	init_waitqueue_head(&m_dev->rx_wait_q);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+	init_MUTEX(&m_dev->req_lock);
+#else
+	sema_init(&m_dev->req_lock, 1);
+#endif
+	spin_lock_init(&m_dev->rxlock);
+	memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats));
+	m_dev->open = mdev_open;
+	m_dev->close = mdev_close;
+	m_dev->flush = mdev_flush;
+	m_dev->send = mdev_send_frame;
+	m_dev->destruct = mdev_destruct;
+	m_dev->ioctl = mdev_ioctl;
+	m_dev->query = mdev_query;
+	m_dev->owner = THIS_MODULE;
+
+}
+
+/**
+ *  @brief This function handles the major job in bluetooth driver.
+ *  it handles the event generated by firmware, rx data received
+ *  from firmware and tx data sent from kernel.
+ *
+ *  @param data    A pointer to bt_thread structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+static int
+bt_service_main_thread(void *data)
+{
+	bt_thread *thread = data;
+	bt_private *priv = thread->priv;
+	bt_adapter *adapter = priv->adapter;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0)
+	wait_queue_t wait;
+#else
+	wait_queue_entry_t wait;
+#endif
+	struct sk_buff *skb;
+	ENTER();
+	bt_activate_thread(thread);
+	init_waitqueue_entry(&wait, current);
+	current->flags |= PF_NOFREEZE;
+
+	for (;;) {
+		add_wait_queue(&thread->waitQ, &wait);
+		OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE);
+		if (priv->adapter->WakeupTries ||
+		    ((!priv->adapter->IntCounter) &&
+		     (!priv->bt_dev.tx_dnld_rdy ||
+		      skb_queue_empty(&priv->adapter->tx_queue))
+		     && skb_queue_empty(&priv->adapter->fwdump_queue)
+		    )) {
+			PRINTM(INFO, "Main: Thread sleeping...\n");
+			schedule();
+		}
+		OS_SET_THREAD_STATE(TASK_RUNNING);
+		remove_wait_queue(&thread->waitQ, &wait);
+		if (kthread_should_stop() || adapter->SurpriseRemoved) {
+			PRINTM(INFO, "main-thread: break from main thread: "
+			       "SurpriseRemoved=0x%x\n",
+			       adapter->SurpriseRemoved);
+			break;
+		}
+
+		PRINTM(INFO, "Main: Thread waking up...\n");
+
+		if (priv->adapter->IntCounter) {
+			OS_INT_DISABLE;
+			adapter->IntCounter = 0;
+			OS_INT_RESTORE;
+			sbi_get_int_status(priv);
+		} else if ((priv->adapter->ps_state == PS_SLEEP) &&
+			   !skb_queue_empty(&priv->adapter->tx_queue)) {
+			priv->adapter->WakeupTries++;
+			sbi_wakeup_firmware(priv);
+			continue;
+		}
+		if (priv->adapter->ps_state == PS_SLEEP)
+			continue;
+		if (priv->bt_dev.tx_dnld_rdy == TRUE) {
+			if (!skb_queue_empty(&priv->adapter->tx_queue)) {
+				skb = skb_dequeue(&priv->adapter->tx_queue);
+				if (skb)
+					send_single_packet(priv, skb);
+			}
+		}
+		if (!skb_queue_empty(&priv->adapter->fwdump_queue)) {
+			skb = skb_dequeue(&priv->adapter->fwdump_queue);
+			if (skb) {
+				bt_store_firmware_dump(priv, skb->data,
+						       skb->len);
+				dev_kfree_skb_any(skb);
+			}
+		}
+	}
+	bt_deactivate_thread(thread);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handles the interrupt. it will change PS
+ *  state if applicable. it will wake up main_thread to handle
+ *  the interrupt event as well.
+ *
+ *  @param m_dev   A pointer to m_dev structure
+ *  @return        N/A
+ */
+void
+bt_interrupt(struct m_dev *m_dev)
+{
+	bt_private *priv = (bt_private *)m_dev->driver_data;
+	ENTER();
+	if (!priv || !priv->adapter) {
+		LEAVE();
+		return;
+	}
+	PRINTM(INTR, "*\n");
+	priv->adapter->ps_state = PS_AWAKE;
+	if (priv->adapter->hs_state == HS_ACTIVATED) {
+		PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name);
+		priv->adapter->hs_state = HS_DEACTIVATED;
+	}
+	priv->adapter->WakeupTries = 0;
+	priv->adapter->IntCounter++;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	LEAVE();
+}
+
+/**
+ * @brief  Dynamic release of char dev
+ *
+ * @param kobj          A pointer to kobject structure
+ *
+ * @return                N/A
+ */
+static void
+char_dev_release_dynamic(struct kobject *kobj)
+{
+	struct char_dev *cdev = container_of(kobj, struct char_dev, kobj);
+	ENTER();
+	PRINTM(INFO, "free char_dev\n");
+	kfree(cdev);
+	LEAVE();
+}
+
+static struct kobj_type ktype_char_dev_dynamic = {
+	.release = char_dev_release_dynamic,
+};
+
+/**
+ * @brief  Allocation of char dev
+ *
+ * @param           	N/A
+ *
+ * @return              char_dev
+ */
+static struct char_dev *
+alloc_char_dev(void)
+{
+	struct char_dev *cdev;
+	ENTER();
+	cdev = kzalloc(sizeof(struct char_dev), GFP_KERNEL);
+	if (cdev) {
+		kobject_init(&cdev->kobj, &ktype_char_dev_dynamic);
+		PRINTM(INFO, "alloc char_dev\n");
+	}
+	return cdev;
+}
+
+/**
+ * @brief  Dynamic release of bt private
+ *
+ * @param kobj          A pointer to kobject structure
+ *
+ * @return                N/A
+ */
+static void
+bt_private_dynamic_release(struct kobject *kobj)
+{
+	bt_private *priv = container_of(kobj, bt_private, kobj);
+	ENTER();
+	PRINTM(INFO, "free bt priv\n");
+	kfree(priv);
+	LEAVE();
+}
+
+static struct kobj_type ktype_bt_private_dynamic = {
+	.release = bt_private_dynamic_release,
+};
+
+/**
+ * @brief  Allocation of bt private
+ *
+ * @param           	N/A
+ *
+ * @return              bt_private
+ */
+static bt_private *
+bt_alloc_priv(void)
+{
+	bt_private *priv;
+	ENTER();
+	priv = kzalloc(sizeof(bt_private), GFP_KERNEL);
+	if (priv) {
+		kobject_init(&priv->kobj, &ktype_bt_private_dynamic);
+		PRINTM(INFO, "alloc bt priv\n");
+	}
+	LEAVE();
+	return priv;
+}
+
+/**
+ * @brief  Get bt private structure
+ *
+ * @param priv          A pointer to bt_private structure
+ *
+ * @return              kobject structure
+ */
+struct kobject *
+bt_priv_get(bt_private *priv)
+{
+	PRINTM(INFO, "bt priv get object");
+	return kobject_get(&priv->kobj);
+}
+
+/**
+ * @brief  Get bt private structure
+ *
+ * @param priv          A pointer to bt_private structure
+ *
+ * @return              N/A
+ */
+void
+bt_priv_put(bt_private *priv)
+{
+	PRINTM(INFO, "bt priv put object");
+	kobject_put(&priv->kobj);
+}
+
+/**
+ *  @brief This function send init commands to firmware
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @return       BT_STATUS_SUCESS/BT_STATUS_FAILURE
+ */
+int
+bt_init_cmd(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+	if (ret < 0) {
+		PRINTM(FATAL, "Module cfg command send failed!\n");
+		goto done;
+	}
+	if (btindrst != -1) {
+		ret = bt_set_independent_reset(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "Independent reset failed!\n");
+			goto done;
+		}
+	}
+	if (btpmic) {
+		if (BT_STATUS_SUCCESS != bt_pmic_configure(priv)) {
+			PRINTM(FATAL, "BT: PMIC Configure failed \n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	ret = bt_set_ble_deepsleep(priv, deep_sleep ? TRUE : FALSE);
+	if (ret < 0) {
+		PRINTM(FATAL, "%s BLE deepsleep failed!\n",
+		       deep_sleep ? "Enable" : "Disable");
+		goto done;
+	}
+	if (psmode) {
+		priv->bt_dev.psmode = TRUE;
+		priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME;
+		ret = bt_enable_ps(priv);
+		if (ret < 0) {
+			PRINTM(FATAL, "Enable PS mode failed!\n");
+			goto done;
+		}
+	}
+#if defined(SDIO_SUSPEND_RESUME)
+	priv->bt_dev.gpio_gap = DEF_GPIO_GAP;
+	ret = bt_send_hscfg_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "Send HSCFG failed!\n");
+		goto done;
+	}
+#endif
+	priv->bt_dev.sdio_pull_cfg = 0xffffffff;
+	priv->bt_dev.sdio_pull_ctrl = 0;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reinit firmware after redownload firmware
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @return       BT_STATUS_SUCESS/BT_STATUS_FAILURE
+ */
+int
+bt_reinit_fw(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	priv->adapter->tx_lock = FALSE;
+	priv->adapter->ps_state = PS_AWAKE;
+	priv->adapter->suspend_fail = FALSE;
+	priv->adapter->is_suspended = FALSE;
+	priv->adapter->hs_skip = 0;
+	priv->adapter->num_cmd_timeout = 0;
+
+	ret = bt_init_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "BT init command failed!\n");
+		goto done;
+	}
+	/* block all the packet from bluez */
+	if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext)
+		priv->adapter->tx_lock = TRUE;
+
+	if (init_cfg)
+		if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+			PRINTM(FATAL,
+			       "BT: Set user init data and param failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+
+	if (cal_cfg) {
+		if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+			PRINTM(FATAL, "BT: Set cal data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	if (bt_mac) {
+		PRINTM(INFO,
+		       "Set BT mac_addr from insmod parametre bt_mac = %s\n",
+		       bt_mac);
+		if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) {
+			PRINTM(FATAL,
+			       "BT: Fail to set mac address from insmod parametre\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	if (cal_cfg_ext) {
+		if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) {
+			PRINTM(FATAL, "BT: Set cal ext data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) {
+		priv->adapter->tx_lock = FALSE;
+		bt_restore_tx_queue(priv);
+	}
+	bt_get_fw_version(priv);
+	snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN,
+		 mbt_driver_version, fw_version);
+done:
+	return ret;
+}
+
+/**
+ *  @brief Module configuration and register device
+ *
+ *  @param priv      A Pointer to bt_private structure
+ *  @return      BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_conf_dpc(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct mbt_dev *mbt_dev = NULL;
+	struct debug_dev *debug_dev = NULL;
+	int i = 0;
+	struct char_dev *char_dev = NULL;
+	char dev_file[DEV_NAME_LEN + 5];
+	unsigned char dev_type = 0;
+
+	ENTER();
+
+	priv->bt_dev.tx_dnld_rdy = TRUE;
+	if (priv->fw_reload) {
+		bt_reinit_fw(priv);
+		LEAVE();
+		return ret;
+	}
+
+	if (drv_mode & DRV_MODE_BT) {
+		mbt_dev = alloc_mbt_dev();
+		if (!mbt_dev) {
+			PRINTM(FATAL, "Can not allocate mbt dev\n");
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		init_m_dev(&(priv->bt_dev.m_dev[BT_SEQ]));
+		priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE;
+		priv->bt_dev.m_dev[BT_SEQ].spec_type = IANYWHERE_SPEC;
+		priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)mbt_dev;
+		priv->bt_dev.m_dev[BT_SEQ].driver_data = priv;
+		priv->bt_dev.m_dev[BT_SEQ].read_continue_flag = 0;
+	}
+
+	dev_type = HCI_SDIO;
+
+	if (mbt_dev)
+		mbt_dev->type = dev_type;
+
+	ret = bt_init_cmd(priv);
+	if (ret < 0) {
+		PRINTM(FATAL, "BT init command failed!\n");
+		goto done;
+	}
+
+	if (mbt_dev && priv->bt_dev.devType == DEV_TYPE_AMP) {
+		mbt_dev->type |= HCI_BT_AMP;
+		priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_AMP_TYPE;
+	}
+	/** Process device tree init parameters before register hci device.
+	 *  Since uplayer device has not yet registered, no need to block tx queue.
+	 * */
+	if (init_cfg)
+		if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+			PRINTM(FATAL,
+			       "BT: Set user init data and param failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	if (cal_cfg) {
+		if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+			PRINTM(FATAL, "BT: Set cal data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	} else if (bt_mac) {
+		PRINTM(INFO,
+		       "Set BT mac_addr from insmod parametre bt_mac = %s\n",
+		       bt_mac);
+		if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) {
+			PRINTM(FATAL,
+			       "BT: Fail to set mac address from insmod parametre\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+	if (cal_cfg_ext) {
+		if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) {
+			PRINTM(FATAL, "BT: Set cal ext data failed\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	}
+
+	/* Get FW version */
+	bt_get_fw_version(priv);
+	snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN,
+		 mbt_driver_version, fw_version);
+
+	if (mbt_dev) {
+		/** init mbt_dev */
+		mbt_dev->flags = 0;
+		mbt_dev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
+		mbt_dev->esco_type = (ESCO_HV1);
+		mbt_dev->link_mode = (HCI_LM_ACCEPT);
+
+		mbt_dev->idle_timeout = 0;
+		mbt_dev->sniff_max_interval = 800;
+		mbt_dev->sniff_min_interval = 80;
+		for (i = 0; i < 3; i++)
+			mbt_dev->reassembly[i] = NULL;
+		atomic_set(&mbt_dev->promisc, 0);
+
+		/** alloc char dev node */
+		char_dev = alloc_char_dev();
+		if (!char_dev) {
+			class_destroy(chardev_class);
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		char_dev->minor = MBTCHAR_MINOR_BASE + mbtchar_minor;
+		if (mbt_dev->type & HCI_BT_AMP)
+			char_dev->dev_type = BT_AMP_TYPE;
+		else
+			char_dev->dev_type = BT_TYPE;
+
+		if (bt_name)
+			snprintf(mbt_dev->name, sizeof(mbt_dev->name), "%s%d",
+				 bt_name, mbtchar_minor);
+		else
+			snprintf(mbt_dev->name, sizeof(mbt_dev->name),
+				 "mbtchar%d", mbtchar_minor);
+		snprintf(dev_file, sizeof(dev_file), "/dev/%s", mbt_dev->name);
+		mbtchar_minor++;
+		PRINTM(MSG, "BT: Create %s\n", dev_file);
+
+		/** register m_dev to BT char device */
+		priv->bt_dev.m_dev[BT_SEQ].index = char_dev->minor;
+		char_dev->m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+
+		/** create BT char device node */
+		register_char_dev(char_dev, chardev_class, MODULE_NAME,
+				  mbt_dev->name);
+
+		/** chmod & chown for BT char device */
+		mbtchar_chown(dev_file, AID_SYSTEM, AID_NET_BT_STACK);
+		mbtchar_chmod(dev_file, 0666);
+
+		/** create proc device */
+		snprintf(priv->bt_dev.m_dev[BT_SEQ].name,
+			 sizeof(priv->bt_dev.m_dev[BT_SEQ].name),
+			 mbt_dev->name);
+		bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ);
+	}
+
+	if ((debug_intf) && ((drv_mode & DRV_MODE_BT)
+	    )) {
+		/** alloc debug_dev */
+		debug_dev = alloc_debug_dev();
+		if (!debug_dev) {
+			PRINTM(FATAL, "Can not allocate debug dev\n");
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+
+		/** init m_dev */
+		init_m_dev(&(priv->bt_dev.m_dev[DEBUG_SEQ]));
+		priv->bt_dev.m_dev[DEBUG_SEQ].dev_type = DEBUG_TYPE;
+		priv->bt_dev.m_dev[DEBUG_SEQ].spec_type = GENERIC_SPEC;
+		priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = (void *)debug_dev;
+		priv->bt_dev.m_dev[DEBUG_SEQ].driver_data = priv;
+
+		/** create char device for Debug */
+		char_dev = alloc_char_dev();
+		if (!char_dev) {
+			class_destroy(chardev_class);
+			ret = -ENOMEM;
+			goto err_kmalloc;
+		}
+		char_dev->minor = DEBUGCHAR_MINOR_BASE + debugchar_minor;
+		char_dev->dev_type = DEBUG_TYPE;
+		if (debug_name)
+			snprintf(debug_dev->name, sizeof(debug_dev->name),
+				 "%s%d", debug_name, debugchar_minor);
+		else
+			snprintf(debug_dev->name, sizeof(debug_dev->name),
+				 "mdebugchar%d", debugchar_minor);
+		snprintf(dev_file, sizeof(dev_file), "/dev/%s",
+			 debug_dev->name);
+		PRINTM(MSG, "BT: Create %s\n", dev_file);
+		debugchar_minor++;
+
+		/** register char dev */
+		priv->bt_dev.m_dev[DEBUG_SEQ].index = char_dev->minor;
+		char_dev->m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+		register_char_dev(char_dev, chardev_class, MODULE_NAME,
+				  debug_dev->name);
+
+		/** chmod for debug char device */
+		mbtchar_chmod(dev_file, 0666);
+
+		/** create proc device */
+		snprintf(priv->bt_dev.m_dev[DEBUG_SEQ].name,
+			 sizeof(priv->bt_dev.m_dev[DEBUG_SEQ].name),
+			 debug_dev->name);
+		bt_proc_init(priv, &(priv->bt_dev.m_dev[DEBUG_SEQ]), DEBUG_SEQ);
+	}
+
+done:
+	LEAVE();
+	return ret;
+err_kmalloc:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function adds the card. it will probe the
+ *  card, allocate the bt_priv and initialize the device.
+ *
+ *  @param card    A pointer to card
+ *  @return        A pointer to bt_private structure
+ */
+
+bt_private *
+bt_add_card(void *card)
+{
+	bt_private *priv = NULL;
+	int index = 0;
+
+	ENTER();
+
+	priv = bt_alloc_priv();
+	if (!priv) {
+		PRINTM(FATAL, "Can not allocate priv\n");
+		LEAVE();
+		return NULL;
+	}
+	/* Save the handle */
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == NULL)
+			break;
+	}
+	if (index < MAX_BT_ADAPTER) {
+		m_priv[index] = priv;
+	} else {
+		PRINTM(ERROR, "Exceeded maximum cards supported!\n");
+		goto err_kmalloc;
+	}
+	/* allocate buffer for bt_adapter */
+	priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL);
+	if (!priv->adapter) {
+		PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->tx_buffer =
+		kzalloc(MAX_TX_BUF_SIZE + DMA_ALIGNMENT, GFP_KERNEL);
+	if (!priv->adapter->tx_buffer) {
+		PRINTM(FATAL, "Allocate buffer for transmit\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->tx_buf =
+		(u8 *)ALIGN_ADDR(priv->adapter->tx_buffer, DMA_ALIGNMENT);
+	priv->adapter->hw_regs_buf =
+		kzalloc(SD_BLOCK_SIZE + DMA_ALIGNMENT, GFP_KERNEL);
+	if (!priv->adapter->hw_regs_buf) {
+		PRINTM(FATAL, "Allocate buffer for INT read buf failed!\n");
+		goto err_kmalloc;
+	}
+	priv->adapter->hw_regs =
+		(u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, DMA_ALIGNMENT);
+	bt_init_adapter(priv);
+
+	PRINTM(INFO, "Starting kthread...\n");
+	priv->MainThread.priv = priv;
+	spin_lock_init(&priv->driver_lock);
+
+	bt_create_thread(bt_service_main_thread, &priv->MainThread,
+			 "bt_main_service");
+
+	/* wait for mainthread to up */
+	while (!priv->MainThread.pid)
+		os_sched_timeout(1);
+
+	/** user config file */
+	init_waitqueue_head(&priv->init_user_conf_wait_q);
+
+	priv->bt_dev.card = card;
+
+	((struct sdio_mmc_card *)card)->priv = priv;
+	priv->adapter->sd_ireg = 0;
+	/*
+	 * Register the device. Fillup the private data structure with
+	 * relevant information from the card and request for the required
+	 * IRQ.
+	 */
+	if (sbi_register_dev(priv) < 0) {
+		PRINTM(FATAL, "Failed to register bt device!\n");
+		goto err_registerdev;
+	}
+	if (bt_init_fw(priv)) {
+		PRINTM(FATAL, "BT Firmware Init Failed\n");
+		goto err_init_fw;
+	}
+	LEAVE();
+	return priv;
+
+err_init_fw:
+	clean_up_m_devs(priv);
+	bt_proc_remove(priv);
+	PRINTM(INFO, "Unregister device\n");
+	sbi_unregister_dev(priv);
+err_registerdev:
+	((struct sdio_mmc_card *)card)->priv = NULL;
+	/* Stop the thread servicing the interrupts */
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid)
+		os_sched_timeout(1);
+err_kmalloc:
+	if (priv->adapter)
+		bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return NULL;
+}
+
+/**
+ *  @brief This function send hardware remove event
+ *
+ *  @param priv    A pointer to bt_private
+ *  @return        N/A
+ */
+void
+bt_send_hw_remove_event(bt_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+	ENTER();
+	if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		LEAVE();
+		return;
+	}
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+#define HCI_HARDWARE_ERROR_EVT  0x10
+#define HCI_HARDWARE_REMOVE     0x24
+	skb = bt_skb_alloc(3, GFP_ATOMIC);
+	skb->data[0] = HCI_HARDWARE_ERROR_EVT;
+	skb->data[1] = 1;
+	skb->data[2] = HCI_HARDWARE_REMOVE;
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+	skb_put(skb, 3);
+	if (mbt_dev) {
+		skb->dev = (void *)mdev_bt;
+		PRINTM(MSG, "Send HW ERROR event\n");
+		if (!mdev_recv_frame(skb)) {
+#define RX_WAIT_TIMEOUT				300
+			mdev_bt->wait_rx_complete = TRUE;
+			mdev_bt->rx_complete_flag = FALSE;
+			if (os_wait_interruptible_timeout
+			    (mdev_bt->rx_wait_q, mdev_bt->rx_complete_flag,
+			     RX_WAIT_TIMEOUT))
+				PRINTM(MSG, "BT stack received the event\n");
+			mdev_bt->stat.byte_rx += 3;
+		}
+	}
+	LEAVE();
+	return;
+}
+
+#ifdef BLE_WAKEUP
+/**
+ *  @brief This function used to config BLE wakeup pattern
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        N/A
+ */
+int
+bt_config_ble_wakeup(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sk_buff *skb = NULL;
+	u16 ocf, left_len;
+	u8 len, more_cmd;
+	u8 *pos;
+
+	ENTER();
+
+	if (!priv->ble_wakeup_buf) {
+		PRINTM(ERROR, "BT: no ble wakeup parameters found\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	PRINTM(MSG, "Config ble wakeup pattern\n");
+
+	pos = priv->ble_wakeup_buf;
+	left_len = *(u16 *) pos;
+	left_len = __le16_to_cpu(left_len);
+	pos += sizeof(u16);
+
+	while (left_len >= 2) {
+		more_cmd = *pos;
+		len = *(pos + 1);
+		if (((len + 2) > left_len) ||
+		    (!more_cmd && ((len + 2) < left_len))) {
+			PRINTM(ERROR, "Invalid ble parameters\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		skb = bt_skb_alloc(len, GFP_ATOMIC);
+		if (!skb) {
+			PRINTM(ERROR, "BT BLE WAKEUP: fail to alloc skb\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		memcpy(skb_put(skb, len), pos + 2, len);
+		bt_cb(skb)->pkt_type = *(u8 *)skb->data;
+		skb_pull(skb, 1);
+		DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_CMD:", skb->data, skb->len);
+		skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+		skb_queue_head(&priv->adapter->tx_queue, skb);
+		priv->bt_dev.sendcmdflag = TRUE;
+		priv->bt_dev.send_cmd_opcode = *(u16 *) skb->data;
+		ocf = hci_opcode_ocf(priv->bt_dev.send_cmd_opcode);
+		priv->adapter->cmd_complete = FALSE;
+
+		wake_up_interruptible(&priv->MainThread.waitQ);
+		if (!os_wait_interruptible_timeout
+		    (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+		     WAIT_UNTIL_CMD_RESP)) {
+			ret = BT_STATUS_FAILURE;
+			PRINTM(ERROR,
+			       "BT: Set  Set ble wakeup cmd 0x%x timeout:\n",
+			       priv->bt_dev.send_cmd_opcode);
+			bt_cmd_timeout_func(priv, ocf);
+			goto done;
+		}
+
+		pos += len + 2;
+		left_len -= len + 2;
+	}
+
+done:
+	if (ret != BT_STATUS_SUCCESS) {
+		if (priv->ble_wakeup_buf) {
+			kfree(priv->ble_wakeup_buf);
+			priv->ble_wakeup_buf = NULL;
+			priv->ble_wakeup_buf_size = 0;
+		}
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function send system suspend event
+ *
+ *  @param priv    A pointer to bt_private
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_send_system_event(bt_private *priv, u8 flag)
+{
+	struct sk_buff *skb = NULL;
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+
+	ENTER();
+
+	if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+
+	skb = bt_skb_alloc(4, GFP_ATOMIC);
+	if (!skb) {
+		PRINTM(ERROR, "Fail to allocate sys suspend event skb\n");
+		return BT_STATUS_FAILURE;
+	}
+	skb->data[0] = VENDOR_SPECIFIC_EVENT;
+	skb->data[1] = 2;
+	skb->data[2] = HCI_SYSTEM_SUSPEND_EVT;
+	if (flag)
+		skb->data[3] = HCI_SYSTEM_SUSPEND;
+	else
+		skb->data[3] = HCI_SYSTEM_RESUME;
+
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+	skb_put(skb, 4);
+	if (mbt_dev) {
+		skb->dev = (void *)mdev_bt;
+		PRINTM(MSG, "Send system %s event\n",
+		       flag ? "suspend" : "resume");
+		if (!mdev_recv_frame(skb)) {
+#define RX_WAIT_TIMEOUT                         300
+			mdev_bt->wait_rx_complete = TRUE;
+			mdev_bt->rx_complete_flag = FALSE;
+			if (os_wait_interruptible_timeout(mdev_bt->rx_wait_q,
+							  mdev_bt->
+							  rx_complete_flag,
+							  RX_WAIT_TIMEOUT))
+				PRINTM(MSG, "BT stack received the event\n");
+			mdev_bt->stat.byte_rx += 4;
+		}
+	}
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+#endif
+
+/**
+ *  @brief This function removes the card.
+ *
+ *  @param card    A pointer to card
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_remove_card(void *card)
+{
+	bt_private *priv = (bt_private *)card;
+	int index;
+	ENTER();
+	if (!priv) {
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv->adapter->SurpriseRemoved = TRUE;
+
+	bt_send_hw_remove_event(priv);
+#ifdef BLE_WAKEUP
+	if (priv->ble_wakeup_buf) {
+		kfree(priv->ble_wakeup_buf);
+		priv->ble_wakeup_buf = NULL;
+		priv->ble_wakeup_buf_size = 0;
+	}
+#endif
+	wake_up_interruptible(&priv->adapter->cmd_wait_q);
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid) {
+		os_sched_timeout(1);
+		wake_up_interruptible(&priv->MainThread.waitQ);
+	}
+
+	bt_proc_remove(priv);
+	PRINTM(INFO, "Unregister device\n");
+	sbi_unregister_dev(priv);
+	clean_up_m_devs(priv);
+	PRINTM(INFO, "Free Adapter\n");
+	bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function initializes module.
+ *
+ *  @return    BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_module(void)
+{
+	int ret = BT_STATUS_SUCCESS;
+	int index;
+	ENTER();
+	PRINTM(MSG, "BT: Loading driver\n");
+	/* Init the bt_private pointer array first */
+	for (index = 0; index < MAX_BT_ADAPTER; index++)
+		m_priv[index] = NULL;
+	bt_root_proc_init();
+
+	/** create char device class */
+	chardev_class = class_create(THIS_MODULE, MODULE_NAME);
+	if (IS_ERR(chardev_class)) {
+		PRINTM(ERROR, "Unable to allocate class\n");
+		bt_root_proc_remove();
+		ret = PTR_ERR(chardev_class);
+		goto done;
+	}
+
+	if (sbi_register() == NULL) {
+		bt_root_proc_remove();
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+done:
+	if (ret)
+		PRINTM(MSG, "BT: Driver loading failed\n");
+	else
+		PRINTM(MSG, "BT: Driver loaded successfully\n");
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function cleans module
+ *
+ *  @return        N/A
+ */
+static void
+bt_exit_module(void)
+{
+	bt_private *priv;
+	int index;
+	ENTER();
+	PRINTM(MSG, "BT: Unloading driver\n");
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		priv = m_priv[index];
+		if (!priv)
+			continue;
+		if (priv && !priv->adapter->SurpriseRemoved) {
+			if (BT_STATUS_SUCCESS == bt_send_reset_command(priv))
+				bt_send_module_cfg_cmd(priv,
+						       MODULE_SHUTDOWN_REQ);
+		}
+		sbi_disable_host_int(priv);
+
+	}
+
+	sbi_unregister();
+
+	bt_root_proc_remove();
+	class_destroy(chardev_class);
+	PRINTM(MSG, "BT: Driver unloaded\n");
+	LEAVE();
+}
+
+module_init(bt_init_module);
+module_exit(bt_exit_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+module_param(fw, int, 0);
+MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware");
+module_param(psmode, int, 0);
+MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode");
+module_param(deep_sleep, int, 0);
+MODULE_PARM_DESC(deep_sleep, "1: Enable deep sleep; 0: Disable deep sleep");
+#ifdef CONFIG_OF
+module_param(dts_enable, int, 0);
+MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS");
+#endif
+#ifdef	DEBUG_LEVEL1
+module_param(mbt_drvdbg, uint, 0);
+MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL");
+#endif
+#ifdef SDIO_SUSPEND_RESUME
+module_param(mbt_pm_keep_power, int, 0);
+MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power");
+#endif
+module_param(init_cfg, charp, 0);
+MODULE_PARM_DESC(init_cfg, "BT init config file name");
+module_param(cal_cfg, charp, 0);
+MODULE_PARM_DESC(cal_cfg, "BT calibrate file name");
+module_param(cal_cfg_ext, charp, 0);
+MODULE_PARM_DESC(cal_cfg_ext, "BT calibrate ext file name");
+module_param(bt_mac, charp, 0660);
+MODULE_PARM_DESC(bt_mac, "BT init mac address");
+module_param(drv_mode, int, 0);
+MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE;");
+module_param(bt_name, charp, 0);
+MODULE_PARM_DESC(bt_name, "BT interface name");
+module_param(debug_intf, int, 0);
+MODULE_PARM_DESC(debug_intf,
+		 "1: Enable debug interface; 0: Disable debug interface ");
+module_param(debug_name, charp, 0);
+MODULE_PARM_DESC(debug_name, "Debug interface name");
+module_param(bt_fw_reload, int, 0);
+MODULE_PARM_DESC(bt_fw_reload,
+		 "0: disable fw_reload; 1: enable fw reload feature");
+module_param(btindrst, int, 0);
+MODULE_PARM_DESC(btindrst,
+		 "Independent reset configuration; high byte:GPIO pin number;low byte:0x0:disable, 0x1:out-band reset, 0x2:in-band reset.");
+module_param(btpmic, int, 0);
+MODULE_PARM_DESC(btpmic,
+		 "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware (default)");
+module_param(bt_fw_serial, int, 0);
+MODULE_PARM_DESC(bt_fw_serial,
+		 "0: Support parallel download FW; 1: Support serial download FW");
diff --git a/bt_sd8997/bt_char/bt_proc.c b/bt_sd8997/bt_char/bt_proc.c
new file mode 100644
index 0000000..d2fa99a
--- /dev/null
+++ b/bt_sd8997/bt_char/bt_proc.c
@@ -0,0 +1,893 @@
+/** @file bt_proc.c
+  *
+  * @brief This file handle the functions for proc files
+  *
+  * Copyright (C) 2007-2018, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/proc_fs.h>
+
+#include "bt_drv.h"
+#include "bt_sdio.h"
+
+/** proc diretory root */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+#define PROC_DIR NULL
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+#define PROC_DIR (&proc_root)
+#else
+#define PROC_DIR proc_net
+#endif
+
+/** Proc mbt directory entry */
+static struct proc_dir_entry *proc_mbt;
+
+#define     CMD52_STR_LEN   50
+static char cmd52_string[CMD52_STR_LEN];
+
+/** proc data structure */
+struct proc_data {
+	/** Read length */
+	int rdlen;
+	/** Read buffer */
+	char *rdbuf;
+	/** Write length */
+	int wrlen;
+	/** Maximum write length */
+	int maxwrlen;
+	/** Write buffer */
+	char *wrbuf;
+	/** Private structure */
+	struct _bt_private *pbt;
+	void (*on_close) (struct inode *, struct file *);
+};
+
+/** Default file permission */
+#define DEFAULT_FILE_PERM  0644
+
+/** Bluetooth device offset */
+#define OFFSET_BT_DEV		0x01
+/** Bluetooth adapter offset */
+#define OFFSET_BT_ADAPTER	0x02
+/** Show integer */
+#define SHOW_INT		0x10
+/** Show hex */
+#define SHOW_HEX		0x20
+/** Show string */
+#define SHOW_STRING		0x40
+
+/** Device size */
+#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n)
+/** Device address */
+#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n)
+
+/** Adapter size */
+#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n)
+/** Adapter address */
+#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n)
+
+static struct item_data config_items[] = {
+#ifdef	DEBUG_LEVEL1
+	{"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX}
+	,
+#endif
+	{"idle_timeout", item_dev_size(idle_timeout), 0,
+	 item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap),
+	 OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0,
+	 item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX}
+	,
+	{"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0,
+	 item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT}
+	,
+	{"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode),
+	 OFFSET_BT_DEV | SHOW_INT}
+	,
+
+};
+
+static struct item_data status_items[] = {
+	{"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver),
+	 OFFSET_BT_ADAPTER | SHOW_STRING},
+	{"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0,
+	 item_dev_addr(tx_dnld_rdy),
+	 OFFSET_BT_DEV | SHOW_INT},
+	{"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"hs_state", item_adapter_size(hs_state), 0,
+	 item_adapter_addr(hs_state),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"ps_state", item_adapter_size(ps_state), 0,
+	 item_adapter_addr(ps_state),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"WakeupTries", item_adapter_size(WakeupTries), 0,
+	 item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT},
+	{"irq_recv", item_adapter_size(irq_recv), 0,
+	 item_adapter_addr(irq_recv),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"irq_done", item_adapter_size(irq_done), 0,
+	 item_adapter_addr(irq_done),
+	 OFFSET_BT_ADAPTER | SHOW_INT},
+	{"skb_pending", item_adapter_size(skb_pending), 0,
+	 item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT},
+};
+
+static struct item_data debug_items[] = {
+	{"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING},
+};
+
+/**
+ *  @brief convert string to number
+ *
+ *  @param s	pointer to numbered string
+ *  @return	converted number from string s
+ */
+int
+string_to_number(char *s)
+{
+	int r = 0;
+	int base = 0;
+	int pn = 1;
+
+	if (strncmp(s, "-", 1) == 0) {
+		pn = -1;
+		s++;
+	}
+	if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) {
+		base = 16;
+		s += 2;
+	} else
+		base = 10;
+
+	for (s = s; *s != 0; s++) {
+		if ((*s >= '0') && (*s <= '9'))
+			r = (r * base) + (*s - '0');
+		else if ((*s >= 'A') && (*s <= 'F'))
+			r = (r * base) + (*s - 'A' + 10);
+		else if ((*s >= 'a') && (*s <= 'f'))
+			r = (r * base) + (*s - 'a' + 10);
+		else
+			break;
+	}
+
+	return r * pn;
+}
+
+/**
+ *  @brief Create cmd52 string
+ *
+ *  @param priv	A pointer to bt_private structure
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+form_cmd52_string(bt_private *priv)
+{
+	ENTER();
+
+	memset(cmd52_string, 0, CMD52_STR_LEN);
+	snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X",
+		 priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg,
+		 priv->bt_dev.cmd52_val);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/*
+ *  @brief Parse cmd52 string
+ *
+ *  @param buffer  A pointer user buffer
+ *  @param len     Length of user buffer
+ *  @param func    Parsed func number
+ *  @param reg     Parsed reg value
+ *  @param val     Parsed value to set
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+parse_cmd52_string(const char __user * buffer, size_t len,
+		   int *func, int *reg, int *val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	char *string = NULL;
+	char *pos = NULL;
+
+	ENTER();
+
+	string = kzalloc(CMD52_STR_LEN, GFP_KERNEL);
+	if (!string) {
+		PRINTM(ERROR, "BT: Can not alloc mem for cmd52 string\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+	memcpy(string, buffer + strlen("sdcmd52rw="),
+	       len - strlen("sdcmd52rw="));
+	string = strstrip(string);
+
+	*func = -1;
+	*reg = -1;
+	*val = -1;
+
+	/* Get func */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*func = string_to_number(pos);
+
+	/* Get reg */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*reg = string_to_number(pos);
+
+	/* Get val (optional) */
+	pos = strsep(&string, " \t");
+	if (pos)
+		*val = string_to_number(pos);
+	kfree(string);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function handle generic proc file close
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return	BT_STATUS_SUCCESS
+ */
+static int
+proc_close(struct inode *inode, struct file *file)
+{
+	struct proc_data *pdata = file->private_data;
+	ENTER();
+	if (pdata) {
+		if (pdata->on_close != NULL)
+			pdata->on_close(inode, file);
+		kfree(pdata->rdbuf);
+		kfree(pdata->wrbuf);
+		kfree(pdata);
+	}
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function handle generic proc file read
+ *
+ *  @param file    A pointer to file structure
+ *  @param buffer  A pointer to output buffer
+ *  @param len     number of byte to read
+ *  @param offset  A pointer to offset of file
+ *  @return		number of output data
+ */
+static ssize_t
+proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset)
+{
+	loff_t pos = *offset;
+	struct proc_data *pdata = (struct proc_data *)file->private_data;
+	if ((!pdata->rdbuf) || (pos < 0))
+		return -EINVAL;
+	if (pos >= pdata->rdlen)
+		return 0;
+	if (len > pdata->rdlen - pos)
+		len = pdata->rdlen - pos;
+	if (copy_to_user(buffer, pdata->rdbuf + pos, len))
+		return -EFAULT;
+	*offset = pos + len;
+	return len;
+}
+
+/**
+ *  @brief This function handle generic proc file write
+ *
+ *  @param file    A pointer to file structure
+ *  @param buffer  A pointer to input buffer
+ *  @param len     number of byte to write
+ *  @param offset  A pointer to offset of file
+ *  @return		number of input data
+ */
+static ssize_t
+proc_write(struct file *file,
+	   const char __user * buffer, size_t len, loff_t * offset)
+{
+	loff_t pos = *offset;
+	struct proc_data *pdata = (struct proc_data *)file->private_data;
+	int func = 0, reg = 0, val = 0;
+	int config_data = 0;
+	char *line = NULL;
+
+	if (!pdata->wrbuf || (pos < 0))
+		return -EINVAL;
+	if (pos >= pdata->maxwrlen)
+		return 0;
+	if (len > pdata->maxwrlen - pos)
+		len = pdata->maxwrlen - pos;
+	if (copy_from_user(pdata->wrbuf + pos, buffer, len))
+		return -EFAULT;
+	if (!strncmp(pdata->wrbuf + pos, "fw_reload", strlen("fw_reload"))) {
+		if (!strncmp
+		    (pdata->wrbuf + pos, "fw_reload=", strlen("fw_reload="))) {
+			line = pdata->wrbuf + pos;
+			line += strlen("fw_reload") + 1;
+			config_data = string_to_number(line);
+		} else
+			config_data = FW_RELOAD_SDIO_INBAND_RESET;
+		PRINTM(MSG, "Request fw_reload=%d\n", config_data);
+		bt_request_fw_reload(pdata->pbt, config_data);
+	}
+	if (!strncmp(pdata->wrbuf + pos, "sdcmd52rw=", strlen("sdcmd52rw="))) {
+		parse_cmd52_string(pdata->wrbuf + pos, len, &func, &reg, &val);
+		sd_write_cmd52_val(pdata->pbt, func, reg, val);
+	}
+	if (!strncmp(pdata->wrbuf + pos, "debug_dump", strlen("debug_dump"))) {
+		bt_dump_sdio_regs(pdata->pbt);
+		bt_dump_firmware_info_v2(pdata->pbt);
+	}
+
+	if (pos + len > pdata->wrlen)
+		pdata->wrlen = len + file->f_pos;
+	*offset = pos + len;
+	return len;
+}
+
+/**
+ *  @brief This function handle the generic file close
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return		N/A
+ */
+static void
+proc_on_close(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	struct proc_private_data *priv = PDE_DATA(inode);
+#else
+	struct proc_private_data *priv = PDE(inode)->data;
+#endif
+	struct proc_data *pdata = file->private_data;
+	char *line;
+	int i;
+	ENTER();
+	if (!pdata->wrlen)
+		return;
+	line = pdata->wrbuf;
+	while (line[0]) {
+		for (i = 0; i < priv->num_items; i++) {
+			if (!strncmp
+			    (line, priv->pdata[i].name,
+			     strlen(priv->pdata[i].name))) {
+				line += strlen(priv->pdata[i].name) + 1;
+				if (priv->pdata[i].size == 1)
+					*((u8 *)priv->pdata[i].addr) =
+						(u8)string_to_number(line);
+				else if (priv->pdata[i].size == 2)
+					*((u16 *) priv->pdata[i].addr) =
+						(u16) string_to_number(line);
+				else if (priv->pdata[i].size == 4)
+					*((u32 *)priv->pdata[i].addr) =
+						(u32)string_to_number(line);
+			}
+		}
+		while (line[0] && line[0] != '\n')
+			line++;
+		if (line[0])
+			line++;
+	}
+	if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd
+	    || priv->pbt->bt_dev.sdio_pull_ctrl
+	    || priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) {
+		bt_prepare_command(priv->pbt);
+		wake_up_interruptible(&priv->pbt->MainThread.waitQ);
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function handle the generic file open
+ *
+ *  @param inode   A pointer to inode structure
+ *  @param file    A pointer to file structure
+ *  @return	BT_STATUS_SUCCESS or other error no.
+ */
+static int
+proc_open(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	struct proc_private_data *priv = PDE_DATA(inode);
+#else
+	struct proc_private_data *priv = PDE(inode)->data;
+#endif
+	struct proc_data *pdata;
+	int i;
+	char *p;
+	u32 val = 0;
+	ENTER();
+	priv->pbt->adapter->skb_pending =
+		skb_queue_len(&priv->pbt->adapter->tx_queue);
+	file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL);
+	if (file->private_data == NULL) {
+		PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+	pdata = (struct proc_data *)file->private_data;
+	pdata->pbt = priv->pbt;
+	pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL);
+	if (pdata->rdbuf == NULL) {
+		PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n");
+		kfree(file->private_data);
+		LEAVE();
+		return -ENOMEM;
+	}
+	if (priv->fileflag == DEFAULT_FILE_PERM) {
+		pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL);
+		if (pdata->wrbuf == NULL) {
+			PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n");
+			kfree(pdata->rdbuf);
+			kfree(file->private_data);
+			return -ENOMEM;
+		}
+		pdata->maxwrlen = priv->bufsize;
+		pdata->on_close = proc_on_close;
+	}
+	p = pdata->rdbuf;
+	for (i = 0; i < priv->num_items; i++) {
+		if (priv->pdata[i].size == 1)
+			val = *((u8 *)priv->pdata[i].addr);
+		else if (priv->pdata[i].size == 2)
+			val = *((u16 *) priv->pdata[i].addr);
+		else if (priv->pdata[i].size == 4)
+			val = *((u32 *)priv->pdata[i].addr);
+		if (priv->pdata[i].flag & SHOW_INT)
+			p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val);
+		else if (priv->pdata[i].flag & SHOW_HEX)
+			p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val);
+		else if (priv->pdata[i].flag & SHOW_STRING) {
+			if (!strncmp
+			    (priv->pdata[i].name, "sdcmd52rw",
+			     strlen("sdcmd52rw"))) {
+				sd_read_cmd52_val(priv->pbt);
+				form_cmd52_string(priv->pbt);
+			}
+			p += sprintf(p, "%s=%s\n", priv->pdata[i].name,
+				     (char *)priv->pdata[i].addr);
+		}
+	}
+	pdata->rdlen = strlen(pdata->rdbuf);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/** Proc read ops */
+static const struct file_operations proc_read_ops = {
+	.read = proc_read,
+	.open = proc_open,
+	.release = proc_close
+};
+
+/** Proc Read-Write ops */
+static const struct file_operations proc_rw_ops = {
+	.read = proc_read,
+	.write = proc_write,
+	.open = proc_open,
+	.release = proc_close
+};
+
+static struct proc_private_data proc_files[] = {
+	{"status", S_IRUGO, 1024,
+	 sizeof(status_items) / sizeof(status_items[0]),
+	 &status_items[0], NULL, &proc_read_ops}
+	,
+	{"config", DEFAULT_FILE_PERM, 512,
+	 sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL,
+	 &proc_rw_ops}
+	,
+	{"debug", DEFAULT_FILE_PERM, 512,
+	 sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL,
+	 &proc_rw_ops}
+	,
+};
+
+/**
+ *  @brief Proc read function for histogram
+ *
+ *  @param sfp     pointer to seq_file structure
+ *  @param data
+ *
+ *  @return        Number of output data or MLAN_STATUS_FAILURE
+ */
+static int
+bt_histogram_read(struct seq_file *sfp, void *data)
+{
+	bt_hist_proc_data *pdata = (bt_hist_proc_data *) sfp->private;
+	bt_private *priv = (bt_private *)pdata->pbt;
+	u8 ant_num;
+	int i, j;
+
+	ENTER();
+	if (!priv) {
+		LEAVE();
+		return -EFAULT;
+	}
+	bt_get_histogram(priv);
+	ant_num = priv->hist_data_len / sizeof(bt_histogram_data);
+	seq_printf(sfp, "BT histogram:\n");
+	seq_printf(sfp, "antenna: 0=2.4G antenna a,  1=2.4G antenna b\n\n");
+	if (ant_num < 1) {
+		seq_printf(sfp, "no histogram data from FW\n");
+		LEAVE();
+		return 0;
+	}
+	for (i = 0; i < ant_num; i++) {
+		if (pdata->antenna != priv->hist_data[i].antenna)
+			continue;
+		seq_printf(sfp, "antenna %d\n", priv->hist_data[i].antenna);
+		switch (priv->hist_data[i].powerclass) {
+		case 2:
+			seq_printf(sfp, "Power class=1.5\n");
+			break;
+		case 5:
+			seq_printf(sfp, "Power class=2\n");
+			break;
+		case 6:
+			seq_printf(sfp, "Power class=1\n");
+			break;
+		default:
+			seq_printf(sfp, "Power class=%d\n",
+				   priv->hist_data[i].powerclass);
+			break;
+		}
+		for (j = 0; j < (MAX_BT_LINK + MAX_BLE_LINK); j++) {
+			switch (priv->hist_data[i].link[j].txrxrate) {
+			case BDR_RATE_1M:
+				seq_printf(sfp,
+					   "BT link[%d]: TxPower=%d dBm, TxRx Rate=BDR(1 mbps), RSSI=%d dBm\n",
+					   j + 1,
+					   priv->hist_data[i].link[j].txpower,
+					   priv->hist_data[i].link[j].rssi);
+				break;
+			case EDR_RATE_2_3M:
+				seq_printf(sfp,
+					   "BT link[%d]: TxPower=%d dBm, TxRx Rate=EDR(2/3 mbps), RSSI=%d dBm\n",
+					   j + 1,
+					   priv->hist_data[i].link[j].txpower,
+					   priv->hist_data[i].link[j].rssi);
+				break;
+			case BLE_RATE_1M:
+				seq_printf(sfp,
+					   "BLE link[%d]: TxPower=%d dBm, TxRx Rate=BLE(1 mbps), RSSI=%d dBm\n",
+					   j - MAX_BT_LINK + 0x80,
+					   priv->hist_data[i].link[j].txpower,
+					   priv->hist_data[i].link[j].rssi);
+				break;
+			default:
+				if (j < MAX_BT_LINK)
+					seq_printf(sfp,
+						   "BT link[%d]: TxPower=%d dBm, TxRx Rate=(%d), RSSI=%d dBm\n",
+						   j + 1,
+						   priv->hist_data[i].link[j].
+						   txpower,
+						   priv->hist_data[i].link[j].
+						   txrxrate,
+						   priv->hist_data[i].link[j].
+						   rssi);
+				else
+					seq_printf(sfp,
+						   "BLE link[%d]: TxPower=%d dBm, TxRx Rate=(%d), RSSI=%d dBm\n",
+						   j - MAX_BT_LINK + 0x80,
+						   priv->hist_data[i].link[j].
+						   txpower,
+						   priv->hist_data[i].link[j].
+						   txrxrate,
+						   priv->hist_data[i].link[j].
+						   rssi);
+				break;
+			}
+		}
+		seq_printf(sfp, "\n");
+	}
+	LEAVE();
+	return 0;
+}
+
+/**
+ *  @brief Proc open function for histogram
+ *
+ *  @param inode     A pointer to inode structure
+ *  @param file		 A pointer to file structure
+ *
+ *  @return        Number of output data or MLAN_STATUS_FAILURE
+ */
+static int
+bt_histogram_proc_open(struct inode *inode, struct file *file)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+	return single_open(file, bt_histogram_read, PDE_DATA(inode));
+#else
+	return single_open(file, bt_histogram_read, PDE(inode)->data);
+#endif
+}
+
+/** Histogram proc fops */
+static const struct file_operations histogram_proc_fops = {
+	.owner = THIS_MODULE,
+	.open = bt_histogram_proc_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+/**
+ *  @brief This function initializes proc entry
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param m_dev    A pointer to struct m_dev
+ *  @param seq      Sequence number
+ *
+ *  @return	BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct proc_dir_entry *entry;
+	int i, j;
+	char hist_entry[50];
+	ENTER();
+
+	memset(cmd52_string, 0, CMD52_STR_LEN);
+	if (proc_mbt) {
+		priv->dev_proc[seq].proc_entry =
+			proc_mkdir(m_dev->name, proc_mbt);
+		if (!priv->dev_proc[seq].proc_entry) {
+			PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		priv->dev_proc[seq].hist_entry =
+			proc_mkdir("histogram", priv->dev_proc[seq].proc_entry);
+		if (!priv->dev_proc[seq].hist_entry) {
+			PRINTM(ERROR, "BT: Could not mkdir histogram!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		for (i = 0; i < MAX_ANTENNA_NUM; i++) {
+			priv->hist_proc[i].antenna = i;
+			priv->hist_proc[i].pbt = priv;
+			snprintf(hist_entry, sizeof(hist_entry), "bt-ant%d", i);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+			entry = proc_create_data(hist_entry, 0644,
+						 priv->dev_proc[seq].hist_entry,
+						 &histogram_proc_fops,
+						 &priv->hist_proc[i]);
+			if (entry == NULL)
+#else
+			entry = create_proc_entry(hist_entry, 0644,
+						  priv->dev_proc[seq].
+						  hist_entry);
+			if (entry) {
+				entry->data = &priv->hist_proc[i];
+				entry->proc_fops = &histogram_proc_fops;
+			} else
+#endif
+			{
+				PRINTM(MSG,
+				       "Fail to create histogram proc %s\n",
+				       hist_entry);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+		}
+		priv->dev_proc[seq].pfiles =
+			kmalloc(sizeof(proc_files), GFP_ATOMIC);
+		if (!priv->dev_proc[seq].pfiles) {
+			PRINTM(ERROR,
+			       "BT: Could not alloc memory for pfile!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files,
+		       sizeof(proc_files));
+		priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files);
+		for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++)
+			priv->dev_proc[seq].pfiles[j].pdata = NULL;
+		for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
+			priv->dev_proc[seq].pfiles[j].pdata =
+				kmalloc(priv->dev_proc[seq].pfiles[j].
+					num_items * sizeof(struct item_data),
+					GFP_ATOMIC);
+			if (!priv->dev_proc[seq].pfiles[j].pdata) {
+				PRINTM(ERROR,
+				       "BT: Could not alloc memory for pdata!\n");
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata,
+			       (u8 *)proc_files[j].pdata,
+			       priv->dev_proc[seq].pfiles[j].num_items *
+			       sizeof(struct item_data));
+			for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items;
+			     i++) {
+				if (priv->dev_proc[seq].pfiles[j].
+				    pdata[i].flag & OFFSET_BT_DEV)
+					priv->dev_proc[seq].pfiles[j].pdata[i].
+						addr =
+						priv->dev_proc[seq].pfiles[j].
+						pdata[i].offset +
+						(t_ptr)&priv->bt_dev;
+				if (priv->dev_proc[seq].pfiles[j].
+				    pdata[i].flag & OFFSET_BT_ADAPTER)
+					priv->dev_proc[seq].pfiles[j].pdata[i].
+						addr =
+						priv->dev_proc[seq].pfiles[j].
+						pdata[i].offset +
+						(t_ptr)priv->adapter;
+			}
+			priv->dev_proc[seq].pfiles[j].pbt = priv;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+			entry = proc_create_data(proc_files[j].name,
+						 S_IFREG | proc_files[j].
+						 fileflag,
+						 priv->dev_proc[seq].proc_entry,
+						 proc_files[j].fops,
+						 &priv->dev_proc[seq].
+						 pfiles[j]);
+			if (entry == NULL)
+#else
+			entry = create_proc_entry(proc_files[j].name,
+						  S_IFREG | proc_files[j].
+						  fileflag,
+						  priv->dev_proc[seq].
+						  proc_entry);
+			if (entry) {
+				entry->data = &priv->dev_proc[seq].pfiles[j];
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
+				entry->owner = THIS_MODULE;
+#endif
+				entry->proc_fops = proc_files[j].fops;
+			} else
+#endif
+				PRINTM(MSG, "BT: Fail to create proc %s\n",
+				       proc_files[j].name);
+		}
+	}
+done:
+	if (ret == BT_STATUS_FAILURE) {
+		if (priv->dev_proc[seq].proc_entry) {
+			remove_proc_entry(m_dev->name, proc_mbt);
+			priv->dev_proc[seq].proc_entry = NULL;
+		}
+		if (priv->dev_proc[seq].pfiles) {
+			for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) {
+				if (priv->dev_proc[seq].pfiles[j].pdata) {
+					kfree(priv->dev_proc[seq].pfiles[j].
+					      pdata);
+					priv->dev_proc[seq].pfiles[j].pdata =
+						NULL;
+				}
+			}
+			kfree(priv->dev_proc[seq].pfiles);
+			priv->dev_proc[seq].pfiles = NULL;
+		}
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function removes proc interface
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return	N/A
+ */
+void
+bt_proc_remove(bt_private *priv)
+{
+	int j, i;
+	char hist_entry[50];
+	ENTER();
+	PRINTM(INFO, "BT: Remove Proc Interface\n");
+	if (proc_mbt) {
+		for (i = 0; i < MAX_RADIO_FUNC; i++) {
+			if (!priv->dev_proc[i].proc_entry)
+				continue;
+			for (j = 0; j < ARRAY_SIZE(proc_files); j++) {
+				remove_proc_entry(proc_files[j].name,
+						  priv->dev_proc[i].proc_entry);
+			}
+			for (j = 0; j < MAX_ANTENNA_NUM; j++) {
+				snprintf(hist_entry, sizeof(hist_entry),
+					 "bt-ant%d", j);
+				remove_proc_entry(hist_entry,
+						  priv->dev_proc[i].hist_entry);
+			}
+			remove_proc_entry("histogram",
+					  priv->dev_proc[i].proc_entry);
+			remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt);
+			priv->dev_proc[i].proc_entry = NULL;
+
+			if (priv->dev_proc[i].pfiles) {
+				for (j = 0;
+				     j < priv->dev_proc[i].num_proc_files;
+				     j++) {
+					if (priv->dev_proc[i].pfiles[j].pdata) {
+						kfree(priv->dev_proc[i].
+						      pfiles[j].pdata);
+						priv->dev_proc[i].pfiles[j].
+							pdata = NULL;
+					}
+				}
+				kfree(priv->dev_proc[i].pfiles);
+				priv->dev_proc[i].pfiles = NULL;
+			}
+		}
+	}
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function creates proc interface
+ *  directory structure
+ *
+ *  @return		BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_root_proc_init(void)
+{
+	PRINTM(INFO, "BT: Create Proc Interface\n");
+	proc_mbt = proc_mkdir("mbt", PROC_DIR);
+	if (!proc_mbt) {
+		PRINTM(ERROR, "BT: Cannot create proc interface\n");
+		return BT_STATUS_FAILURE;
+	}
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function removes proc interface
+ *  directory structure
+ *
+ *  @return		BT_STATUS_SUCCESS
+ */
+int
+bt_root_proc_remove(void)
+{
+	remove_proc_entry("mbt", PROC_DIR);
+	proc_mbt = NULL;
+	return BT_STATUS_SUCCESS;
+}
diff --git a/bt_sd8997/bt_char/bt_sdio.h b/bt_sd8997/bt_char/bt_sdio.h
new file mode 100644
index 0000000..ac3ac01
--- /dev/null
+++ b/bt_sd8997/bt_char/bt_sdio.h
@@ -0,0 +1,258 @@
+/** @file bt_sdio.h
+ *  @brief This file contains SDIO (interface) module
+ *  related macros, enum, and structure.
+ *
+ *  Copyright (C) 2007-2018, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _BT_SDIO_H_
+#define _BT_SDIO_H_
+
+#include <linux/irqreturn.h>
+
+/** IRQ return type */
+typedef irqreturn_t IRQ_RET_TYPE;
+/** IRQ return */
+#define IRQ_RET		(return IRQ_HANDLED)
+/** ISR notifier function */
+typedef IRQ_RET_TYPE (*isr_notifier_fn_t) (s32 irq, void *dev_id,
+					   struct pt_regs * reg);
+
+/** SDIO header length */
+#define SDIO_HEADER_LEN			4
+
+/** Interrupt Mode SDIO */
+#define INT_MODE_SDIO       0
+/** Interrupt Mode GPIO */
+#define INT_MODE_GPIO       1
+/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */
+#define GPIO_INT_NEW_MODE   255
+/* SD block size can not bigger than 64 due to buf size limit in firmware */
+/** define SD block size for data Tx/Rx */
+#define SD_BLOCK_SIZE			64
+/** define SD block size for firmware download */
+#define SD_BLOCK_SIZE_FW_DL		256
+
+/** Number of blocks for firmware transfer */
+#define FIRMWARE_TRANSFER_NBLOCK	2
+
+/** Firmware ready */
+#define FIRMWARE_READY			0xfedc
+
+/* Bus Interface Control Reg 0x07 */
+/** SD BUS width 1 */
+#define SD_BUS_WIDTH_1			0x00
+/** SD BUS width 4 */
+#define SD_BUS_WIDTH_4			0x02
+/** SD BUS width mask */
+#define SD_BUS_WIDTH_MASK		0x03
+/** Asynchronous interrupt mode */
+#define ASYNC_INT_MODE			0x20
+
+/** magic register */
+#define CARD_MAGIC_REG          0xF0
+/** magic value */
+#define MAGIC_VAL               0x24
+
+/* Host Control Registers */
+/** Host Control Registers : Configuration */
+#define CONFIGURATION_REG		0x00
+/** Host Control Registers : Host without Command 53 finish host*/
+#define HOST_TO_CARD_EVENT		(0x1U << 3)
+/** Host Control Registers : Host terminates Command 53 */
+#define HOST_TERM_CMD53			(0x1U << 2)
+/** Host Control Registers : Host power up */
+#define HOST_POWER_UP			(0x1U << 1)
+/** Host Control Registers : Host power down */
+#define HOST_POWER_DOWN			(0x1U << 0)
+
+/** Host Control Registers : Host interrupt RSR */
+#define HOST_INT_RSR_REG		0x04
+
+/** Host Control Registers : Upload host interrupt RSR */
+#define UP_LD_HOST_INT_RSR		(0x1U)
+
+/** Host Control Registers : Host interrupt mask */
+#define HOST_INT_MASK_REG		0x08
+
+/** Host Control Registers : Upload host interrupt mask */
+#define UP_LD_HOST_INT_MASK		(0x1U)
+/** Host Control Registers : Download host interrupt mask */
+#define DN_LD_HOST_INT_MASK		(0x2U)
+/** Enable Host interrupt mask */
+#define HIM_ENABLE			(UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK)
+/** Disable Host interrupt mask */
+#define	HIM_DISABLE			0xff
+
+/** Host Control Registers : Host interrupt status */
+#define HOST_INTSTATUS_REG		0x0C
+/** Host Control Registers : Upload host interrupt status */
+#define UP_LD_HOST_INT_STATUS		(0x1U)
+/** Host Control Registers : Download host interrupt status */
+#define DN_LD_HOST_INT_STATUS		(0x2U)
+
+/** Host Control Registers : Host Transfer status */
+#define HOST_INT_STATUS_REG		0x58
+/** Host Control Registers : Upload CRC error */
+#define UP_LD_CRC_ERR			(0x1U << 2)
+/** Host Control Registers : Upload restart */
+#define UP_LD_RESTART			(0x1U << 1)
+/** Host Control Registers : Download restart */
+#define DN_LD_RESTART			(0x1U << 0)
+
+/** Card Control Registers : Card to Host Event register */
+#define CARD_STATUS_REG			0x5C
+/** Card Control Registers : Card I/O ready */
+#define CARD_IO_READY			(0x1U << 3)
+/** Card Control Registers : CIS card ready */
+#define CIS_CARD_RDY			(0x1U << 2)
+/** Card Control Registers : Upload card ready */
+#define UP_LD_CARD_RDY			(0x1U << 1)
+/** Card Control Registers : Download card ready */
+#define DN_LD_CARD_RDY			(0x1U << 0)
+
+/** Card Control Registers : Host interrupt mask register */
+#define HOST_INTERRUPT_MASK_REG 	0x60
+/** Card Control Registers : Host power interrupt mask */
+#define HOST_POWER_INT_MASK		(0x1U << 3)
+/** Card Control Registers : Abort card interrupt mask */
+#define ABORT_CARD_INT_MASK		(0x1U << 2)
+/** Card Control Registers : Upload card interrupt mask */
+#define UP_LD_CARD_INT_MASK          	(0x1U << 1)
+/** Card Control Registers : Download card interrupt mask */
+#define DN_LD_CARD_INT_MASK          	(0x1U << 0)
+
+/** Card Control Registers : Card interrupt status register */
+#define CARD_INTERRUPT_STATUS_REG    	0x64
+/** Card Control Registers : Power up interrupt */
+#define POWER_UP_INT			(0x1U << 4)
+/** Card Control Registers : Power down interrupt */
+#define POWER_DOWN_INT               	(0x1U << 3)
+
+/** Card Control Registers : Card interrupt RSR register */
+#define CARD_INTERRUPT_RSR_REG       	0x68
+/** Card Control Registers : Power up RSR */
+#define POWER_UP_RSR                 	(0x1U << 4)
+/** Card Control Registers : Power down RSR */
+#define POWER_DOWN_RSR               	(0x1U << 3)
+
+/* Card Control Registers */
+/** Card Control Registers : Read SQ base address A0 register */
+#define SQ_READ_BASE_ADDRESS_A0_REG  	0xf8
+/** Card Control Registers : Read SQ base address A1 register */
+#define SQ_READ_BASE_ADDRESS_A1_REG  	0xf9
+/** Card Control Registers : Read SQ base address A2 register */
+#define SQ_READ_BASE_ADDRESS_A2_REG  	0x6E
+/** Card Control Registers : Read SQ base address A3 register */
+#define SQ_READ_BASE_ADDRESS_A3_REG  	0x6F
+/** Card Control Registers : Write SQ base address A0 register */
+#define SQ_WRITE_BASE_ADDRESS_A0_REG  	0x70
+/** Card Control Registers : Write SQ base address A1 register */
+#define SQ_WRITE_BASE_ADDRESS_A1_REG  	0x71
+/** Card Control Registers : Write SQ base address A2 register */
+#define SQ_WRITE_BASE_ADDRESS_A2_REG  	0x72
+/** Card Control Registers : Write SQ base address A3 register */
+#define SQ_WRITE_BASE_ADDRESS_A3_REG  	0x73
+
+/** Card Control Registers : Card revision register */
+#define CARD_REVISION_REG            	0xC8
+
+/** Firmware status 0 register (SCRATCH0_0) */
+#define CARD_FW_STATUS0_REG             0xe8
+/** Firmware status 1 register (SCRATCH0_1) */
+#define CARD_FW_STATUS1_REG             0xe9
+/** Rx length register (SCRATCH0_2) */
+#define CARD_RX_LEN_REG                 0xea
+/** Rx unit register (SCRATCH0_3) */
+#define CARD_RX_UNIT_REG                0xeb
+/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/
+#define ENABLE_GPIO_1_INT_MODE  0x88
+/** Scratch reg 3 2  :     Configure GPIO-1 INT*/
+#define SCRATCH_REG_32          0xEE
+
+/** Card Control Registers : Card OCR 0 register */
+#define CARD_OCR_0_REG			0xD4
+/** Card Control Registers : Card OCR 1 register */
+#define CARD_OCR_1_REG			0xD5
+/** Card Control Registers : Card OCR 3 register */
+#define CARD_OCR_3_REG			0xD6
+/** Card Control Registers : Card config register */
+#define CARD_CONFIG_REG			0xD7
+/** Card Control Registers : Miscellaneous Configuration Register */
+#define CARD_MISC_CFG_REG		0xD8
+/** Misc. Config Register : Auto Re-enable interrupts */
+#define AUTO_RE_ENABLE_INT		(0x1U << 4)
+
+/** Card Control Registers : Debug 0 register */
+#define DEBUG_0_REG			0xDC
+/** Card Control Registers : SD test BUS 0 */
+#define SD_TESTBUS0			(0x1U)
+/** Card Control Registers : Debug 1 register */
+#define DEBUG_1_REG			0xDD
+/** Card Control Registers : SD test BUS 1 */
+#define SD_TESTBUS1			(0x1U)
+/** Card Control Registers : Debug 2 register */
+#define DEBUG_2_REG			0xDE
+/** Card Control Registers : SD test BUS 2 */
+#define SD_TESTBUS2			(0x1U)
+/** Card Control Registers : Debug 3 register */
+#define DEBUG_3_REG			0xDF
+/** Card Control Registers : SD test BUS 3 */
+#define SD_TESTBUS3			(0x1U)
+
+/** Host Control Registers : I/O port 0 */
+#define IO_PORT_0_REG			0xE4
+/** Host Control Registers : I/O port 1 */
+#define IO_PORT_1_REG			0xE5
+/** Host Control Registers : I/O port 2 */
+#define IO_PORT_2_REG			0xE6
+
+struct sdio_mmc_card {
+	/** sdio_func structure pointer */
+	struct sdio_func *func;
+	/** bt_private structure pointer */
+	bt_private *priv;
+};
+
+/** DMA alignment value */
+#define DMA_ALIGNMENT	64
+/** 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)	\
+	((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1))
+
+/** This function read cmd52 register */
+int sd_write_reg(bt_private *priv, int reg, u8 val);
+/** This function write cmd52 value to register */
+int sd_read_reg(bt_private *priv, int reg, u8 *data);
+/** This function reads the Cmd52 value in dev structure */
+int sd_read_cmd52_val(bt_private *priv);
+/** This function updates card reg based on the Cmd52 value in dev structure */
+int sd_write_cmd52_val(bt_private *priv, int func, int reg, int val);
+
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+/** This function tells lower driver that BT is suspended */
+void bt_is_suspended(bt_private *priv);
+#endif
+#endif
+#endif
+#endif /* _BT_SDIO_H_ */
diff --git a/bt_sd8997/bt_char/bt_sdiommc.c b/bt_sd8997/bt_char/bt_sdiommc.c
new file mode 100644
index 0000000..9867049
--- /dev/null
+++ b/bt_sd8997/bt_char/bt_sdiommc.c
@@ -0,0 +1,1920 @@
+/** @file bt_sdiommc.c
+ *  @brief This file contains SDIO IF (interface) module
+ *  related functions.
+ *
+ * Copyright (C) 2007-2018, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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/firmware.h>
+#include <linux/mmc/sdio_func.h>
+#include        <linux/mmc/sdio.h>
+#include        <linux/mmc/card.h>
+
+#include "bt_drv.h"
+#include "bt_sdio.h"
+
+/** define marvell vendor id */
+#define MARVELL_VENDOR_ID 0x02df
+
+/** Max retry number of CMD53 read/write */
+#define MAX_CMD53_RETRY 	3
+/** Max retry number of CMD53 read/write */
+#define MAX_CMD52_RETRY     3
+/** Firmware name */
+static char *fw_name;
+/** fw serial download flag */
+extern int bt_fw_serial;
+/** request firmware nowait */
+int bt_req_fw_nowait;
+static int multi_fn = BIT(2);
+
+#define SD8997_FW_NAME "mrvl/sdsd8997_combo_v4.bin"
+#define SD8997_BT_FW_NAME "mrvl/sd8997_bt_v4.bin"
+#define DEFAULT_FW_NAME "mrvl/sdsd8997_combo_v4.bin"
+#define DEFAULT_BT_FW_NAME "mrvl/sd8997_bt_v4.bin"
+
+/** Function number 2 */
+#define FN2			2
+/** Device ID for SD8997 FN2 */
+#define SD_DEVICE_ID_8997_BT_FN2    0x9142
+
+/** Array of SDIO device ids when multi_fn=0x12 */
+static const struct sdio_device_id bt_ids[] = {
+	{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997_BT_FN2)},
+	{}
+};
+
+MODULE_DEVICE_TABLE(sdio, bt_ids);
+
+/********************************************************
+		Global Variables
+********************************************************/
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+extern int mbt_pm_keep_power;
+#endif
+
+extern bt_private *m_priv[];
+/********************************************************
+		Local Functions
+********************************************************/
+
+/**
+ *  @brief This function gets rx_unit value
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_get_rx_unit(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_rx_unit_reg = CARD_RX_UNIT_REG;
+
+	ENTER();
+
+	reg = sdio_readb(card->func, card_rx_unit_reg, &ret);
+	if (ret == BT_STATUS_SUCCESS)
+		priv->bt_dev.rx_unit = reg;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads fwstatus registers
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param dat	   A pointer to keep returned data
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_read_firmware_status(bt_private *priv, u16 * dat)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 fws0;
+	u8 fws1;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_fw_status0_reg = CARD_FW_STATUS0_REG;
+	u8 card_fw_status1_reg = CARD_FW_STATUS1_REG;
+
+	ENTER();
+
+	fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret);
+	if (ret < 0) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret);
+	if (ret < 0) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	*dat = (((u16) fws1) << 8) | fws0;
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function reads rx length
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param dat	   A pointer to keep returned data
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+static int
+sd_read_rx_len(bt_private *priv, u16 * dat)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_rx_len_reg = CARD_RX_LEN_REG;
+
+	ENTER();
+
+	reg = sdio_readb(card->func, card_rx_len_reg, &ret);
+	if (ret == BT_STATUS_SUCCESS)
+		*dat = (u16) reg << priv->bt_dev.rx_unit;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function enables the host interrupts mask
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mask	   the interrupt mask
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_enable_host_int_mask(bt_private *priv, u8 mask)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 host_int_mask_reg = HOST_INT_MASK_REG;
+
+	ENTER();
+
+	sdio_writeb(card->func, mask, host_int_mask_reg, &ret);
+	if (ret) {
+		PRINTM(WARN, "BT: Unable to enable the host interrupt!\n");
+		ret = BT_STATUS_FAILURE;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/** @brief This function disables the host interrupts mask.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param mask	   the interrupt mask
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+static int
+sbi_disable_host_int_mask(bt_private *priv, u8 mask)
+{
+	int ret = BT_STATUS_FAILURE;
+	u8 host_int_mask;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 host_int_mask_reg = HOST_INT_MASK_REG;
+
+	ENTER();
+
+	/* Read back the host_int_mask register */
+	host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret);
+	if (ret)
+		goto done;
+
+	/* Update with the mask and write back to the register */
+	host_int_mask &= ~mask;
+	sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret);
+	if (ret < 0) {
+		PRINTM(WARN, "BT: Unable to diable the host interrupt!\n");
+		goto done;
+	}
+	ret = BT_STATUS_SUCCESS;
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function polls the card status register
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param bits     the bit mask
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_poll_card_status(bt_private *priv, u8 bits)
+{
+	int tries;
+	int rval;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 cs;
+	u8 card_status_reg = CARD_STATUS_REG;
+
+	ENTER();
+
+	for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) {
+		cs = sdio_readb(card->func, card_status_reg, &rval);
+		if (rval != 0)
+			break;
+		if (rval == 0 && (cs & bits) == bits) {
+			LEAVE();
+			return BT_STATUS_SUCCESS;
+		}
+		udelay(1);
+	}
+	PRINTM(ERROR,
+	       "BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n",
+	       rval, tries, cs);
+
+	LEAVE();
+	return BT_STATUS_FAILURE;
+}
+
+/**
+ *  @brief This function reads updates the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_read_cmd52_val(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 func, reg, val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	ENTER();
+
+	func = priv->bt_dev.cmd52_func;
+	reg = priv->bt_dev.cmd52_reg;
+	sdio_claim_host(card->func);
+	if (func)
+		val = sdio_readb(card->func, reg, &ret);
+	else
+		val = sdio_f0_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	if (ret) {
+		PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n",
+		       func, reg);
+	} else {
+		priv->bt_dev.cmd52_val = val;
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function updates card reg based on the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param func     Stores func variable
+ *  @param reg      Stores reg variable
+ *  @param val      Stores val variable
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_write_cmd52_val(bt_private *priv, int func, int reg, int val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	ENTER();
+
+	if (val >= 0) {
+		/* Perform actual write only if val is provided */
+		sdio_claim_host(card->func);
+		if (func)
+			sdio_writeb(card->func, val, reg, &ret);
+		else
+			sdio_f0_writeb(card->func, val, reg, &ret);
+		sdio_release_host(card->func);
+		if (ret) {
+			PRINTM(ERROR,
+			       "BT: Cannot write value (0x%x) to func %d reg %d\n",
+			       val, func, reg);
+			goto done;
+		}
+		priv->bt_dev.cmd52_val = val;
+	}
+
+	/* Save current func and reg for future read */
+	priv->bt_dev.cmd52_func = func;
+	priv->bt_dev.cmd52_reg = reg;
+
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function updates card reg based on the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to write
+ *  @param val      value
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_write_reg(bt_private *priv, int reg, u8 val)
+{
+	int ret = BT_STATUS_SUCCESS;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	sdio_writeb(card->func, val, reg, &ret);
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads updates the Cmd52 value in dev structure
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param reg      register to read
+ *  @param data		Data
+ *  @return         BT_STATUS_SUCCESS or other error no.
+ */
+int
+sd_read_reg(bt_private *priv, int reg, u8 *data)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 val;
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	ENTER();
+	sdio_claim_host(card->func);
+	val = sdio_readb(card->func, reg, &ret);
+	sdio_release_host(card->func);
+	*data = val;
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function probes the card
+ *
+ *  @param func    A pointer to sdio_func structure.
+ *  @param id      A pointer to structure sdio_device_id
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+static int
+sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id)
+{
+	int ret = BT_STATUS_SUCCESS;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *card = NULL;
+
+	ENTER();
+
+	PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor,
+	       id->device, id->class, func->num);
+	card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL);
+	if (!card) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	card->func = func;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+	/* wait for chip fully wake up */
+	if (!func->enable_timeout)
+		func->enable_timeout = 200;
+#endif
+	sdio_claim_host(func);
+	ret = sdio_enable_func(func);
+	if (ret) {
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret);
+		kfree(card);
+		LEAVE();
+		return -EIO;
+	}
+	sdio_release_host(func);
+	priv = bt_add_card(card);
+	if (!priv) {
+		sdio_claim_host(func);
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		ret = BT_STATUS_FAILURE;
+		kfree(card);
+	}
+done:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function checks if the firmware is ready to accept
+ *  command or not.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @param pollnum  Number of times to poll fw status
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_verify_fw_download(bt_private *priv, int pollnum)
+{
+	int ret = BT_STATUS_FAILURE;
+	u16 firmwarestat = 0;
+	int tries;
+
+	ENTER();
+
+	/* Wait for firmware initialization event */
+	for (tries = 0; tries < pollnum; tries++) {
+		if (sd_read_firmware_status(priv, &firmwarestat) < 0)
+			continue;
+		if (firmwarestat == FIRMWARE_READY) {
+			PRINTM(MSG, "BT FW is active(%d)\n", tries);
+			ret = BT_STATUS_SUCCESS;
+			break;
+		}
+		mdelay(100);
+	}
+	if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS)) {
+		PRINTM(ERROR,
+		       "Fail to poll firmware status: firmwarestat=0x%x\n",
+		       firmwarestat);
+		bt_dump_sdio_regs(priv);
+	}
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief Transfers firmware to card
+ *
+ *  @param priv      A Pointer to bt_private structure
+ *  @param fw        A Pointer to fw image
+ *  @param fw_len    fw image len
+ *  @return          BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+static int
+sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len)
+{
+	struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 *firmware = fw;
+	int firmwarelen = fw_len;
+	u8 base0;
+	u8 base1;
+	int ret = BT_STATUS_SUCCESS;
+	int offset;
+	void *tmpfwbuf = NULL;
+	int tmpfwbufsz;
+	u8 *fwbuf;
+	u16 len;
+	int txlen = 0;
+	int tx_blocks = 0;
+	int i = 0;
+	int tries = 0;
+#ifdef FW_DOWNLOAD_SPEED
+	u32 tv1, tv2;
+#endif
+	u8 sq_read_base_address_a0_reg = SQ_READ_BASE_ADDRESS_A0_REG;
+	u8 sq_read_base_address_a1_reg = SQ_READ_BASE_ADDRESS_A1_REG;
+
+	ENTER();
+
+	PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen);
+
+#ifdef FW_DOWNLOAD_SPEED
+	tv1 = get_utimeofday();
+#endif
+
+	tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT;
+	tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL);
+	if (!tmpfwbuf) {
+		PRINTM(ERROR,
+		       "BT: Unable to allocate buffer for firmware. Terminating download\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	/* Ensure aligned firmware buffer */
+	fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT);
+
+	/* Perform firmware data transfer */
+	offset = 0;
+	do {
+		/* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits
+		 */
+		ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY);
+		if (ret < 0) {
+			PRINTM(FATAL,
+			       "BT: FW download with helper poll status timeout @ %d\n",
+			       offset);
+			goto done;
+		}
+		/* More data? */
+		if (offset >= firmwarelen)
+			break;
+
+		for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+			base0 = sdio_readb(card->func,
+					   sq_read_base_address_a0_reg, &ret);
+			if (ret) {
+				PRINTM(WARN, "Dev BASE0 register read failed:"
+				       " base0=0x%04X(%d). Terminating download\n",
+				       base0, base0);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			base1 = sdio_readb(card->func,
+					   sq_read_base_address_a1_reg, &ret);
+			if (ret) {
+				PRINTM(WARN, "Dev BASE1 register read failed:"
+				       " base1=0x%04X(%d). Terminating download\n",
+				       base1, base1);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			len = (((u16) base1) << 8) | base0;
+
+			if (len != 0)
+				break;
+			udelay(10);
+		}
+
+		if (len == 0)
+			break;
+		else if (len > BT_UPLD_SIZE) {
+			PRINTM(FATAL,
+			       "BT: FW download failure @ %d, invalid length %d\n",
+			       offset, len);
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+	/** ignore CRC check before download the first packet */
+		if (offset == 0 && (len & BIT(0)))
+			len &= ~BIT(0);
+		txlen = len;
+
+		if (len & BIT(0)) {
+			i++;
+			if (i >= MAX_CMD53_RETRY) {
+				PRINTM(FATAL,
+				       "BT: FW download failure @ %d, over max retry count\n",
+				       offset);
+				ret = BT_STATUS_FAILURE;
+				goto done;
+			}
+			PRINTM(ERROR,
+			       "BT: FW CRC error indicated by the helper:"
+			       " len = 0x%04X, txlen = %d\n", len, txlen);
+			len &= ~BIT(0);
+
+			PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset);
+			/* Setting this to 0 to resend from same offset */
+			txlen = 0;
+		} else {
+			i = 0;
+
+			/* Set blocksize to transfer - checking for last block */
+			if (firmwarelen - offset < txlen)
+				txlen = firmwarelen - offset;
+
+			PRINTM(INFO, ".");
+
+			tx_blocks =
+				(txlen + SD_BLOCK_SIZE_FW_DL -
+				 1) / SD_BLOCK_SIZE_FW_DL;
+
+			/* Copy payload to buffer */
+			memcpy(fwbuf, &firmware[offset], txlen);
+		}
+
+		/* Send data */
+		ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf,
+				   tx_blocks * SD_BLOCK_SIZE_FW_DL);
+
+		if (ret < 0) {
+			PRINTM(ERROR,
+			       "BT: FW download, write iomem (%d) failed @ %d\n",
+			       i, offset);
+			sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret);
+			if (ret)
+				PRINTM(ERROR, "write ioreg failed (CFG)\n");
+		}
+
+		offset += txlen;
+	} while (TRUE);
+
+	PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset);
+
+	ret = BT_STATUS_SUCCESS;
+done:
+#ifdef FW_DOWNLOAD_SPEED
+	tv2 = get_utimeofday();
+	PRINTM(INFO, "FW: %d.%03d.%03d ", tv1 / 1000000,
+	       (tv1 % 1000000) / 1000, tv1 % 1000);
+	PRINTM(INFO, " -> %d.%03d.%03d ", tv2 / 1000000,
+	       (tv2 % 1000000) / 1000, tv2 % 1000);
+	tv2 -= tv1;
+	PRINTM(INFO, " == %d.%03d.%03d\n", tv2 / 1000000,
+	       (tv2 % 1000000) / 1000, tv2 % 1000);
+#endif
+	kfree(tmpfwbuf);
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief request_firmware callback
+ *
+ * @param fw_firmware  A pointer to firmware structure
+ * @param context      A Pointer to bt_private structure
+ * @return             BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_request_fw_dpc(const struct firmware *fw_firmware, void *context)
+{
+	int ret = BT_STATUS_SUCCESS;
+	bt_private *priv = (bt_private *)context;
+	struct sdio_mmc_card *card = NULL;
+	struct m_dev *m_dev_bt = NULL;
+	struct timeval tstamp;
+	int index;
+
+	ENTER();
+
+	m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ];
+
+	if ((priv == NULL) || (priv->adapter == NULL) ||
+	    (priv->bt_dev.card == NULL) || (m_dev_bt == NULL)
+		) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+
+	card = (struct sdio_mmc_card *)priv->bt_dev.card;
+
+	if (!fw_firmware) {
+		do_gettimeofday(&tstamp);
+		if (tstamp.tv_sec >
+		    (priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) {
+			PRINTM(ERROR,
+			       "BT: No firmware image found. Skipping download\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		PRINTM(ERROR,
+		       "BT: No firmware image found! Retrying download\n");
+		/* Wait a second here before calling the callback again */
+		os_sched_timeout(1000);
+		sd_download_firmware_w_helper(priv);
+		LEAVE();
+		return ret;
+	}
+
+	priv->firmware = fw_firmware;
+
+	if (BT_STATUS_FAILURE ==
+	    sd_init_fw_dpc(priv, (u8 *)priv->firmware->data,
+			   priv->firmware->size)) {
+		PRINTM(ERROR,
+		       "BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n",
+		       bt_req_fw_nowait);
+		sdio_release_host(card->func);
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* check if the fimware is downloaded successfully or not */
+	if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) {
+		PRINTM(ERROR, "BT: FW failed to be active in time!\n");
+		ret = BT_STATUS_FAILURE;
+		sdio_release_host(card->func);
+		goto done;
+	}
+	sdio_release_host(card->func);
+	sbi_enable_host_int(priv);
+	if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+		PRINTM(ERROR,
+		       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+		ret = BT_STATUS_FAILURE;
+		goto done;
+	}
+	if (fw_firmware) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+		if (!bt_req_fw_nowait)
+#endif
+			release_firmware(fw_firmware);
+	}
+	LEAVE();
+	return ret;
+
+done:
+	if (fw_firmware) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
+		if (!bt_req_fw_nowait)
+#endif
+			release_firmware(fw_firmware);
+	}
+	/* For synchronous download cleanup will be done in add_card */
+	if (!bt_req_fw_nowait)
+		return ret;
+	PRINTM(INFO, "unregister device\n");
+	sbi_unregister_dev(priv);
+	((struct sdio_mmc_card *)card)->priv = NULL;
+	/* Stop the thread servicing the interrupts */
+	priv->adapter->SurpriseRemoved = TRUE;
+	wake_up_interruptible(&priv->MainThread.waitQ);
+	while (priv->MainThread.pid)
+		os_sched_timeout(1);
+	bt_proc_remove(priv);
+	clean_up_m_devs(priv);
+	bt_free_adapter(priv);
+	for (index = 0; index < MAX_BT_ADAPTER; index++) {
+		if (m_priv[index] == priv) {
+			m_priv[index] = NULL;
+			break;
+		}
+	}
+	bt_priv_put(priv);
+	LEAVE();
+	return ret;
+}
+
+/**
+ * @brief request_firmware callback
+ *        This function is invoked by request_firmware_nowait system call
+ *
+ * @param firmware     A pointer to firmware structure
+ * @param context      A Pointer to bt_private structure
+ * @return             None
+ **/
+static void
+sd_request_fw_callback(const struct firmware *firmware, void *context)
+{
+	ENTER();
+	sd_request_fw_dpc(firmware, context);
+	LEAVE();
+	return;
+}
+
+/**
+ *  @brief This function downloads firmware image to the card.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sd_download_firmware_w_helper(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	int err;
+	char *cur_fw_name = NULL;
+
+	ENTER();
+
+	cur_fw_name = fw_name;
+	if (fw_name == NULL) {
+		if (!bt_fw_serial || priv->fw_reload || bt_fw_reload)
+			cur_fw_name = DEFAULT_BT_FW_NAME;
+		else
+			cur_fw_name = DEFAULT_FW_NAME;
+	}
+
+	PRINTM(MSG, "BT Request firmware: %s\n", cur_fw_name);
+	if (bt_req_fw_nowait) {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cur_fw_name, priv->hotplug_device,
+					      GFP_KERNEL, priv,
+					      sd_request_fw_callback);
+#else
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13)
+		ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+					      cur_fw_name, priv->hotplug_device,
+					      priv, sd_request_fw_callback);
+#else
+		ret = request_firmware_nowait(THIS_MODULE,
+					      cur_fw_name, priv->hotplug_device,
+					      priv, sd_request_fw_callback);
+#endif
+#endif
+		if (ret < 0)
+			PRINTM(FATAL,
+			       "BT: request_firmware_nowait() failed, error code = %#x\n",
+			       ret);
+	} else {
+		err = request_firmware(&priv->firmware, cur_fw_name,
+				       priv->hotplug_device);
+		if (err < 0) {
+			PRINTM(FATAL,
+			       "BT: request_firmware() failed, error code = %#x\n",
+			       err);
+			ret = BT_STATUS_FAILURE;
+		} else
+			ret = sd_request_fw_dpc(priv->firmware, priv);
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function reads data from the card.
+ *
+ *  @param priv     A pointer to bt_private structure
+ *  @return         BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+sd_card_to_host(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u16 buf_len = 0;
+	int buf_block_len;
+	int blksz;
+	struct sk_buff *skb = NULL;
+	u32 type;
+	u8 *payload = NULL;
+	struct mbt_dev *mbt_dev = NULL;
+	struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]);
+	struct m_dev *mdev_debug = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+	struct debug_dev *debug_dev =
+		(struct debug_dev *)priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int i = 0;
+
+	ENTER();
+	if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC)
+		mbt_dev =
+			(struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ].
+			dev_pointer;
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	do {
+		/* Read the length of data to be transferred */
+		ret = sd_read_rx_len(priv, &buf_len);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR, "BT: Read scratch reg failed (%d)\n", i);
+			if (i >= MAX_CMD52_RETRY) {
+				ret = BT_STATUS_FAILURE;
+				goto exit;
+			}
+			udelay(20);
+		}
+	}
+	while (ret == BT_STATUS_FAILURE);
+
+	/* Allocate buffer */
+	blksz = SD_BLOCK_SIZE;
+	buf_block_len = (buf_len + blksz - 1) / blksz;
+	if (buf_len <= BT_HEADER_LEN ||
+	    (buf_block_len * blksz) > ALLOC_BUF_SIZE) {
+		PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n",
+		       buf_len);
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+	skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC);
+	if (skb == NULL) {
+		PRINTM(WARN, "BT: No free skb\n");
+		goto exit;
+	}
+	if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) {
+		skb_put(skb,
+			DMA_ALIGNMENT -
+			((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
+		skb_pull(skb,
+			 DMA_ALIGNMENT -
+			 ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)));
+	}
+
+	payload = skb->data;
+	i = 0;
+	do {
+		ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport,
+				  buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR,
+			       "BT: card_to_host, read iomem (%d) failed: %d\n",
+			       i, ret);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+			/* issue abort cmd52 command through F0 */
+			sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret);
+#endif
+			sdio_writeb(card->func, HOST_TERM_CMD53,
+				    CONFIGURATION_REG, &ret);
+			udelay(20);
+			ret = BT_STATUS_FAILURE;
+			if (i >= MAX_CMD53_RETRY) {
+				kfree_skb(skb);
+				skb = NULL;
+				goto exit;
+			}
+		}
+	} while (ret == BT_STATUS_FAILURE);
+	/* This is SDIO specific header length: byte[2][1][0], * type: byte[3]
+	   (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */
+	buf_len = payload[0];
+	buf_len |= (u16) payload[1] << 8;
+	type = payload[3];
+	PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", mbt_dev->name,
+	       buf_len, type);
+	if (buf_len > buf_block_len * blksz) {
+		PRINTM(ERROR,
+		       "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n",
+		       buf_len, buf_block_len * blksz);
+		ret = BT_STATUS_FAILURE;
+		kfree_skb(skb);
+		skb = NULL;
+		goto exit;
+	}
+	DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len);
+	switch (type) {
+	case HCI_ACLDATA_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (*(u16 *) skb->data == 0xffff) {
+			skb_queue_tail(&priv->adapter->fwdump_queue, skb);
+			break;
+		}
+		bt_recv_frame(priv, skb);
+		break;
+	case HCI_SCODATA_PKT:
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		bt_recv_frame(priv, skb);
+		break;
+	case HCI_EVENT_PKT:
+		/** add EVT Demux */
+		bt_cb(skb)->pkt_type = type;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb))
+			break;
+		switch (skb->data[0]) {
+		case 0x0E:
+			/** cmd complete */
+			if (priv->debug_device_pending) {
+				if (priv->debug_ocf_ogf[0] == skb->data[3] &&
+				    priv->debug_ocf_ogf[1] == skb->data[4]) {
+					priv->debug_device_pending = 0;
+					priv->debug_ocf_ogf[0] = 0;
+					priv->debug_ocf_ogf[1] = 0;
+					/** debug cmd complete */
+					if (debug_dev) {
+						skb->dev = (void *)mdev_debug;
+						mdev_recv_frame(skb);
+						mdev_debug->stat.byte_rx +=
+							buf_len;
+					}
+					break;
+				}
+			}
+			bt_recv_frame(priv, skb);
+			break;
+		case 0x0F:
+			/** cmd status */
+			bt_recv_frame(priv, skb);
+			break;
+		case 0xFF:
+			/** Vendor specific pkt */
+			bt_recv_frame(priv, skb);
+			break;
+		default:
+			bt_recv_frame(priv, skb);
+			break;
+		}
+		break;
+	case MRVL_VENDOR_PKT:
+		/* Just think here need to back compatible FM */
+		bt_cb(skb)->pkt_type = HCI_VENDOR_PKT;
+		skb_put(skb, buf_len);
+		skb_pull(skb, BT_HEADER_LEN);
+		if (BT_STATUS_SUCCESS != bt_process_event(priv, skb))
+			bt_recv_frame(priv, skb);
+		break;
+	default:
+		/* Driver specified event and command resp should be handle
+		   here */
+		PRINTM(INFO, "BT: Unknown PKT type:%d\n", type);
+		kfree_skb(skb);
+		skb = NULL;
+		break;
+	}
+exit:
+	if (ret) {
+		if (mbt_dev)
+			mdev_bt->stat.err_rx++;
+		PRINTM(ERROR, "error when recv pkt!\n");
+	}
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function removes the card
+ *
+ *  @param func    A pointer to sdio_func structure
+ *  @return        N/A
+ */
+static void
+sd_remove_card(struct sdio_func *func)
+{
+	struct sdio_mmc_card *card;
+
+	ENTER();
+
+	if (func) {
+		card = sdio_get_drvdata(func);
+		if (card) {
+			bt_remove_card(card->priv);
+			kfree(card);
+		}
+	}
+
+	LEAVE();
+}
+
+/**
+ *  @brief This function handles the interrupt.
+ *
+ *  @param func  A pointer to sdio_func structure
+ *  @return      N/A
+ */
+static void
+sd_interrupt(struct sdio_func *func)
+{
+	bt_private *priv;
+	struct m_dev *m_dev = NULL;
+	struct sdio_mmc_card *card;
+	int ret = BT_STATUS_SUCCESS;
+	u8 ireg = 0;
+	u8 host_intstatus_reg = HOST_INTSTATUS_REG;
+
+	ENTER();
+
+	card = sdio_get_drvdata(func);
+	if (!card || !card->priv) {
+		PRINTM(INFO,
+		       "BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n",
+		       __func__, func, card);
+		LEAVE();
+		return;
+	}
+	priv = card->priv;
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0, SD_BLOCK_SIZE);
+	if (ret) {
+		PRINTM(ERROR,
+		       "BT: sdio_read_ioreg: cmd53 read int status register failed %d\n",
+		       ret);
+		goto done;
+	}
+	ireg = priv->adapter->hw_regs[host_intstatus_reg];
+	if (ret) {
+		PRINTM(ERROR,
+		       "BT: sdio_read_ioreg: CMD52 read int status register failed %d\n",
+		       ret);
+		goto done;
+	}
+	if (ireg != 0) {
+		/*
+		 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+		 * Clear the interrupt status register and re-enable
+		 * the interrupt
+		 */
+		PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name,
+		       ireg);
+		priv->adapter->irq_recv = ireg;
+	} else {
+		PRINTM(ERROR, "BT: ERR: ireg=0\n");
+	}
+	OS_INT_DISABLE;
+	priv->adapter->sd_ireg |= ireg;
+	OS_INT_RESTORE;
+	bt_interrupt(m_dev);
+done:
+	LEAVE();
+}
+
+/**
+ *  @brief This function checks if the interface is ready to download
+ *  or not while other download interfaces are present
+ *
+ *  @param priv   A pointer to bt_private structure
+ *  @param val    Winner status (0: winner)
+ *  @return       BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sd_check_winner_status(bt_private *priv, u8 *val)
+{
+
+	int ret = BT_STATUS_SUCCESS;
+	u8 winner = 0;
+	struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card;
+	u8 card_fw_status0_reg = CARD_FW_STATUS0_REG;
+
+	ENTER();
+	winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret);
+	if (ret != BT_STATUS_SUCCESS) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	*val = winner;
+
+	LEAVE();
+	return ret;
+}
+
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+/** @brief This function tells lower driver that BT is suspended
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        None
+ */
+void
+bt_is_suspended(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	priv->adapter->is_suspended = TRUE;
+	sdio_func_suspended(card->func);
+}
+#endif
+
+/** @brief This function handles client driver suspend
+ *
+ *  @param dev	   A pointer to device structure
+ *  @return        BT_STATUS_SUCCESS or other error no.
+ */
+int
+bt_sdio_suspend(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+		PRINTM(ERROR,
+		       "BT: %s: cannot remain alive while host is suspended\n",
+		       sdio_func_id(func));
+		return -ENOSYS;
+	}
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv = cardp->priv;
+
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name);
+	mbt_hci_suspend_dev(m_dev);
+	skb_queue_purge(&priv->adapter->tx_queue);
+
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
+#ifdef BLE_WAKEUP
+		/** Set BLE Wake up pattern */
+		if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv))
+			PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n");
+#endif
+
+		if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+			PRINTM(CMD, "BT: HS not actived, suspend fail!\n");
+			if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+				PRINTM(CMD,
+				       "BT: HS not actived the second time, force to suspend!\n");
+			}
+		}
+	}
+
+	priv->adapter->is_suspended = TRUE;
+
+	LEAVE();
+	/* We will keep the power when hs enabled successfully */
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) {
+#ifdef MMC_PM_SKIP_RESUME_PROBE
+		PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and "
+		       "MMC_PM_SKIP_RESUME_PROBE\n");
+		return sdio_set_host_pm_flags(func,
+					      MMC_PM_KEEP_POWER |
+					      MMC_PM_SKIP_RESUME_PROBE);
+#else
+		PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n");
+		return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+#endif
+	} else {
+		PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n");
+		return BT_STATUS_SUCCESS;
+	}
+}
+
+void
+bt_sdio_shutdown(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+
+	ENTER();
+
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: shutdown: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	if (!(pm_flags & MMC_PM_KEEP_POWER)) {
+		PRINTM(ERROR,
+		       "BT: %s: cannot remain alive while host is shutdown\n",
+		       sdio_func_id(func));
+		return;
+	}
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return;
+	}
+
+	priv = cardp->priv;
+
+	if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) {
+#ifdef BLE_WAKEUP
+		/** Set BLE Wake up pattern */
+		if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv))
+			PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n");
+#endif
+
+		if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+			PRINTM(CMD, "BT: HS not actived, shutdown fail!\n");
+			if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) {
+				PRINTM(CMD,
+				       "BT: HS not actived the second time, force to shutdown!\n");
+			}
+		}
+	}
+	LEAVE();
+}
+
+/** @brief This function handles client driver resume
+ *
+ *  @param dev	   A pointer to device structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+bt_sdio_resume(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	mmc_pm_flag_t pm_flags = 0;
+	bt_private *priv = NULL;
+	struct sdio_mmc_card *cardp;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+	pm_flags = sdio_get_host_pm_caps(func);
+	PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func),
+	       pm_flags);
+	cardp = sdio_get_drvdata(func);
+	if (!cardp || !cardp->priv) {
+		PRINTM(ERROR, "BT: Card or priv structure is not valid\n");
+		LEAVE();
+		return BT_STATUS_SUCCESS;
+	}
+
+	priv = cardp->priv;
+	priv->adapter->is_suspended = FALSE;
+	m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+	PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name);
+#ifdef BLE_WAKEUP
+	if (priv->ble_wakeup_buf) {
+		PRINTM(CMD, "BT: Send system resume event\n");
+		bt_send_system_event(priv, FALSE);
+	}
+#endif
+	mbt_hci_resume_dev(m_dev);
+	sbi_wakeup_firmware(priv);
+	priv->adapter->hs_state = HS_DEACTIVATED;
+	PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name);
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+#endif
+#endif
+
+/********************************************************
+		Global Functions
+********************************************************/
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+static const struct dev_pm_ops bt_sdio_pm_ops = {
+	.suspend = bt_sdio_suspend,
+	.resume = bt_sdio_resume,
+};
+#endif
+#endif
+static struct sdio_driver sdio_bt = {
+	.name = "sdio_bt",
+	.id_table = bt_ids,
+	.probe = sd_probe_card,
+	.remove = sd_remove_card,
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+	.drv = {
+		.pm = &bt_sdio_pm_ops,
+		.shutdown = bt_sdio_shutdown,
+		}
+#endif
+#endif
+};
+
+/**
+ *  @brief This function registers the bt module in bus driver.
+ *
+ *  @return	   An int pointer that keeps returned value
+ */
+int *
+sbi_register(void)
+{
+	int *ret;
+
+	ENTER();
+
+	if (sdio_register_driver(&sdio_bt) != 0) {
+		PRINTM(FATAL, "BT: SD Driver Registration Failed\n");
+		LEAVE();
+		return NULL;
+	} else
+		ret = (int *)1;
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function de-registers the bt module in bus driver.
+ *
+ *  @return        N/A
+ */
+void
+sbi_unregister(void)
+{
+	ENTER();
+	sdio_unregister_driver(&sdio_bt);
+	LEAVE();
+}
+
+/**
+ *  @brief This function registers the device.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_dev(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 reg;
+	u8 chiprev;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	struct sdio_func *func;
+	u8 host_intstatus_reg = HOST_INTSTATUS_REG;
+	u8 host_int_rsr_reg = HOST_INT_RSR_REG;
+	u8 card_misc_cfg_reg = CARD_MISC_CFG_REG;
+	u8 card_revision_reg = CARD_REVISION_REG;
+	u8 io_port_0_reg = IO_PORT_0_REG;
+	u8 io_port_1_reg = IO_PORT_1_REG;
+	u8 io_port_2_reg = IO_PORT_2_REG;
+	u8 card_magic_reg = CARD_MAGIC_REG;
+	u8 magic_val = 0;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: Error: card or function is NULL!\n");
+		goto failed;
+	}
+	func = card->func;
+	priv->hotplug_device = &func->dev;
+
+	/* Initialize the private structure */
+	strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name));
+	priv->bt_dev.ioport = 0;
+	priv->bt_dev.fn = func->num;
+
+	sdio_claim_host(func);
+	ret = sdio_claim_irq(func, sd_interrupt);
+	if (ret) {
+		PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret);
+		goto release_host;
+	}
+	ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE);
+	if (ret) {
+		PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__);
+		goto release_irq;
+	}
+
+	/* read Revision Register to get the chip revision number */
+	chiprev = sdio_readb(func, card_revision_reg, &ret);
+	if (ret) {
+		PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n");
+		goto release_irq;
+	}
+	priv->adapter->chip_rev = chiprev;
+	PRINTM(INFO, "revision=%#x\n", chiprev);
+
+	magic_val = sdio_readb(func, card_magic_reg, &ret);
+	if (ret) {
+		PRINTM(FATAL, ": cannot read CARD_MAGIC_REG\n");
+		goto release_irq;
+	}
+	priv->adapter->magic_val = magic_val;
+	PRINTM(INFO, "magic_val=%#x\n", magic_val);
+
+	/*
+	 * Read the HOST_INTSTATUS_REG for ACK the first interrupt got
+	 * from the bootloader. If we don't do this we get a interrupt
+	 * as soon as we register the irq.
+	 */
+	reg = sdio_readb(func, host_intstatus_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+
+	/* Read the IO port */
+	reg = sdio_readb(func, io_port_0_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= reg;
+
+	reg = sdio_readb(func, io_port_1_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= (reg << 8);
+
+	reg = sdio_readb(func, io_port_2_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	else
+		priv->bt_dev.ioport |= (reg << 16);
+
+	PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn,
+	       priv->bt_dev.ioport);
+
+#define SDIO_INT_MASK       0x3F
+	/* Set Host interrupt reset to read to clear */
+	reg = sdio_readb(func, host_int_rsr_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	/* Set auto re-enable */
+	reg = sdio_readb(func, card_misc_cfg_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+	sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg, &ret);
+	if (ret < 0)
+		goto release_irq;
+
+	sdio_set_drvdata(func, card);
+	sdio_release_host(func);
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+release_irq:
+	sdio_release_irq(func);
+release_host:
+	sdio_release_host(func);
+failed:
+
+	LEAVE();
+	return BT_STATUS_FAILURE;
+}
+
+/**
+ *  @brief This function de-registers the device.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+sbi_unregister_dev(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+
+	ENTER();
+
+	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);
+	}
+
+	LEAVE();
+	return BT_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief This function enables the host interrupts.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_enable_host_int(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	ret = sd_enable_host_int_mask(priv, HIM_ENABLE);
+	sd_get_rx_unit(priv);
+	sdio_release_host(card->func);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function disables the host interrupts.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sbi_disable_host_int(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	ret = sbi_disable_host_int_mask(priv, HIM_DISABLE);
+	sdio_release_host(card->func);
+
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function sends data to the card.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @param payload A pointer to the data/cmd buffer
+ *  @param nb      Length of data/cmd
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+#ifdef DEBUG_LEVEL1
+	struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+#endif
+	int ret = BT_STATUS_SUCCESS;
+	int buf_block_len;
+	int blksz;
+	int i = 0;
+	u8 *buf = NULL;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	buf = payload;
+
+	blksz = SD_BLOCK_SIZE;
+	buf_block_len = (nb + blksz - 1) / blksz;
+	/* Allocate buffer and copy payload */
+	if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) {
+		if (nb > MAX_TX_BUF_SIZE) {
+			PRINTM(ERROR, "BT: Invalid tx packet, size=%d\n", nb);
+			LEAVE();
+			return BT_STATUS_FAILURE;
+		}
+		/* Ensure 8-byte aligned CMD buffer */
+		buf = priv->adapter->tx_buf;
+		memcpy(buf, payload, nb);
+	}
+	sdio_claim_host(card->func);
+	do {
+		/* Transfer data to card */
+		ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf,
+				   buf_block_len * blksz);
+		if (ret < 0) {
+			i++;
+			PRINTM(ERROR,
+			       "BT: host_to_card, write iomem (%d) failed: %d\n",
+			       i, ret);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
+			/* issue abort cmd52 command through F0 */
+			sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret);
+#endif
+			sdio_writeb(card->func, HOST_TERM_CMD53,
+				    CONFIGURATION_REG, &ret);
+			udelay(20);
+			ret = BT_STATUS_FAILURE;
+			if (i >= MAX_CMD53_RETRY)
+				goto exit;
+		} else {
+			PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n",
+			       m_dev->name, nb);
+			DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb);
+		}
+	} while (ret == BT_STATUS_FAILURE);
+	priv->bt_dev.tx_dnld_rdy = FALSE;
+exit:
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function downloads firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_download_fw(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+	u8 winner = 0;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		ret = BT_STATUS_FAILURE;
+		goto exit;
+	}
+
+	sdio_claim_host(card->func);
+	if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) {
+		PRINTM(MSG, "BT: FW already downloaded!\n");
+		sdio_release_host(card->func);
+		sbi_enable_host_int(priv);
+		if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+			PRINTM(ERROR,
+			       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+			ret = BT_STATUS_FAILURE;
+			goto err_register;
+		}
+		goto exit;
+	}
+	/* Check if other interface is downloading */
+	ret = sd_check_winner_status(priv, &winner);
+	if (ret == BT_STATUS_FAILURE) {
+		PRINTM(FATAL, "BT read winner status failed!\n");
+		goto done;
+	}
+	if (winner) {
+		PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n",
+		       winner);
+		/* check if the fimware is downloaded successfully or not */
+		if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) {
+			PRINTM(FATAL, "BT: FW failed to be active in time!\n");
+			ret = BT_STATUS_FAILURE;
+			goto done;
+		}
+		sdio_release_host(card->func);
+		sbi_enable_host_int(priv);
+		if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) {
+			PRINTM(ERROR,
+			       "BT: sbi_register_conf_dpc failed. Terminating download\n");
+			ret = BT_STATUS_FAILURE;
+			goto err_register;
+		}
+		goto exit;
+	}
+
+	do_gettimeofday(&priv->req_fw_time);
+	/* Download the main firmware via the helper firmware */
+	if (sd_download_firmware_w_helper(priv)) {
+		PRINTM(INFO, "BT: FW download failed!\n");
+		ret = BT_STATUS_FAILURE;
+	}
+	goto exit;
+done:
+	sdio_release_host(card->func);
+exit:
+	LEAVE();
+	return ret;
+err_register:
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function checks the interrupt status and handle it accordingly.
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS
+ */
+int
+sbi_get_int_status(bt_private *priv)
+{
+	int ret = BT_STATUS_SUCCESS;
+	u8 sdio_ireg = 0;
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+
+	ENTER();
+
+	OS_INT_DISABLE;
+	sdio_ireg = priv->adapter->sd_ireg;
+	priv->adapter->sd_ireg = 0;
+	OS_INT_RESTORE;
+	sdio_claim_host(card->func);
+	priv->adapter->irq_done = sdio_ireg;
+	if (sdio_ireg & DN_LD_HOST_INT_STATUS) {	/* tx_done INT */
+		if (priv->bt_dev.tx_dnld_rdy) {	/* tx_done already received */
+			PRINTM(INFO,
+			       "BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n",
+			       priv->bt_dev.tx_dnld_rdy, sdio_ireg);
+		} else {
+			priv->bt_dev.tx_dnld_rdy = TRUE;
+		}
+	}
+	if (sdio_ireg & UP_LD_HOST_INT_STATUS)
+		sd_card_to_host(priv);
+
+	ret = BT_STATUS_SUCCESS;
+	sdio_release_host(card->func);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *  @brief This function wakeup firmware
+ *
+ *  @param priv    A pointer to bt_private structure
+ *  @return        BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no.
+ */
+int
+sbi_wakeup_firmware(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+
+	ENTER();
+
+	if (!card || !card->func) {
+		PRINTM(ERROR, "BT: card or function is NULL!\n");
+		LEAVE();
+		return BT_STATUS_FAILURE;
+	}
+	sdio_claim_host(card->func);
+	sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
+	sdio_release_host(card->func);
+	PRINTM(CMD, "BT wake up firmware\n");
+
+	LEAVE();
+	return ret;
+}
+
+#define INIT_START_REG  0xF1
+#define INIT_END_REG 0xF6
+
+/** @brief This function dump the SDIO register
+ *
+ *  @param priv     A Pointer to the bt_private structure
+ *
+ *  @return         N/A
+ */
+void
+bt_dump_sdio_regs(bt_private *priv)
+{
+	struct sdio_mmc_card *card = priv->bt_dev.card;
+	int ret = BT_STATUS_SUCCESS;
+	char buf[256], *ptr;
+	u8 loop, func, data;
+	unsigned int reg, reg_start, reg_end;
+	u8 loop_num = 2;
+	unsigned int init_reg_start = 0;
+	unsigned int init_reg_end = 0;
+	init_reg_start = INIT_START_REG;
+	init_reg_end = INIT_END_REG;
+
+	if (priv->adapter->ps_state)
+		sbi_wakeup_firmware(priv);
+
+	sdio_claim_host(card->func);
+	for (loop = 0; loop < loop_num; loop++) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+		if (loop == 0) {
+			/* Read the registers of SDIO function0 */
+			func = loop;
+			reg_start = 0;
+			reg_end = 9;
+
+		} else {
+			func = 2;
+			reg_start = 0;
+			reg_end = 0x09;
+		}
+		ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, reg_start,
+			       reg_end);
+		for (reg = reg_start; reg <= reg_end;) {
+			if (func == 0)
+				data = sdio_f0_readb(card->func, reg, &ret);
+			else
+				data = sdio_readb(card->func, reg, &ret);
+			if (!ret)
+				ptr += sprintf(ptr, "%02x ", data);
+			else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+			reg++;
+		}
+		PRINTM(MSG, "%s\n", buf);
+	}
+
+	if (init_reg_start) {
+		memset(buf, 0, sizeof(buf));
+		ptr = buf;
+		ptr += sprintf(ptr, "Init Status Reg (%#x-%#x): ",
+			       init_reg_start, init_reg_end);
+		for (reg = init_reg_start; reg <= init_reg_end;) {
+			data = sdio_readb(card->func, reg, &ret);
+			if (!ret)
+				ptr += sprintf(ptr, "%02x ", data);
+			else {
+				ptr += sprintf(ptr, "ERR");
+				break;
+			}
+			reg++;
+		}
+		PRINTM(MSG, "%s\n", buf);
+	}
+	sdio_release_host(card->func);
+}
+
+module_param(fw_name, charp, 0);
+MODULE_PARM_DESC(fw_name, "Firmware name");
+module_param(bt_req_fw_nowait, int, 0);
+MODULE_PARM_DESC(bt_req_fw_nowait,
+		 "0: Use request_firmware API; 1: Use request_firmware_nowait API");
+module_param(multi_fn, int, 0);
+MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;");
diff --git a/bt_sd8997/bt_char/hci_wrapper.h b/bt_sd8997/bt_char/hci_wrapper.h
new file mode 100644
index 0000000..3fe16b6
--- /dev/null
+++ b/bt_sd8997/bt_char/hci_wrapper.h
@@ -0,0 +1,171 @@
+/** @file hci_wrapper.h
+ *  @brief This file contains HCI related definitions
+ *
+ *  Copyright (C) 2011-2018, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+
+#ifndef _HCI_WRAPPER_H_
+#define _HCI_WRAPPER_H_
+
+#include <linux/module.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+/**  Define Seq num */
+#define BT_SEQ      0
+#define DEBUG_SEQ   3
+
+/** Define dev type */
+#define BT_TYPE     1
+#define BT_AMP_TYPE 2
+#define DEBUG_TYPE  5
+
+/** Define spec type */
+#define BLUEZ_SPEC     1
+#define IANYWHERE_SPEC 2
+#define GENERIC_SPEC   3
+
+/** Define lock/unlock wrapper */
+#define mdev_req_lock(d)		down(&d->req_lock)
+#define mdev_req_unlock(d)		up(&d->req_lock)
+
+/** Length of device name */
+#define DEV_NAME_LEN				32
+
+/** Define struct m_dev */
+struct m_dev {
+	char name[DEV_NAME_LEN];
+	int index;
+	unsigned long flags;
+	spinlock_t lock;
+	struct semaphore req_lock;
+	struct sk_buff_head rx_q;
+	wait_queue_head_t req_wait_q;
+	struct hci_dev_stats stat;
+	struct module *owner;
+	void *dev_pointer;
+	int dev_type;
+	int spec_type;
+	void *driver_data;
+	int read_continue_flag;
+	int wait_rx_complete;
+	int rx_complete_flag;
+	wait_queue_head_t rx_wait_q;
+	spinlock_t rxlock;
+	atomic_t extra_cnt;
+
+	struct sk_buff *evt_skb;
+	struct sk_buff *acl_skb;
+	struct sk_buff *sco_skb;
+
+	int (*open) (struct m_dev * m_dev);
+	int (*close) (struct m_dev * m_dev);
+	int (*flush) (struct m_dev * m_dev);
+	int (*send) (struct m_dev * m_dev, struct sk_buff * skb);
+	void (*destruct) (struct m_dev * m_dev);
+	void (*notify) (struct m_dev * m_dev, unsigned int evt);
+	int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, void *arg);
+	void (*query) (struct m_dev * m_dev, void *arg);
+
+};
+
+/** Define struct mbt_dev */
+struct mbt_dev {
+	/** maybe could add some private member later */
+	char name[DEV_NAME_LEN];
+	unsigned long flags;
+	__u8 type;
+
+	__u16 pkt_type;
+	__u16 esco_type;
+	__u16 link_policy;
+	__u16 link_mode;
+
+	__u32 idle_timeout;
+	__u16 sniff_min_interval;
+	__u16 sniff_max_interval;
+
+	struct sk_buff *reassembly[3];
+
+	atomic_t promisc;
+};
+
+struct debug_dev {
+	/** maybe could add some private member later */
+	char name[DEV_NAME_LEN];
+	unsigned long flags;
+};
+
+/** This function frees m_dev allocation */
+void free_m_dev(struct m_dev *m_dev);
+
+/**
+ *  @brief This function receives frames
+ *
+ *  @param skb	A pointer to struct sk_buff
+ *  @return	0--success otherwise error code
+ */
+static inline int
+mdev_recv_frame(struct sk_buff *skb)
+{
+	struct m_dev *m_dev = (struct m_dev *)skb->dev;
+	if (!m_dev || (!test_bit(HCI_UP, &m_dev->flags)
+		       && !test_bit(HCI_INIT, &m_dev->flags))) {
+		kfree_skb(skb);
+		return -ENXIO;
+	}
+
+	/* Incomming skb */
+	bt_cb(skb)->incoming = 1;
+
+	/* Time stamp */
+	__net_timestamp(skb);
+
+	/* Queue frame for rx task */
+	skb_queue_tail(&m_dev->rx_q, skb);
+
+	/* Wakeup rx thread */
+	wake_up_interruptible(&m_dev->req_wait_q);
+
+	return 0;
+}
+
+/**
+ *  @brief mbt dev suspend handler
+ *
+ *  @param m_dev   A pointer to struct m_dev
+ *  @return        0
+ */
+static inline int
+mbt_hci_suspend_dev(struct m_dev *m_dev)
+{
+	return 0;
+}
+
+/**
+ *  @brief mbt dev resume handler
+ *
+ *  @param m_dev   A pointer to struct m_dev
+ *  @return        0
+ */
+static inline int
+mbt_hci_resume_dev(struct m_dev *m_dev)
+{
+	return 0;
+}
+
+#endif /* _HCI_WRAPPER_H_ */
diff --git a/bt_sd8997/bt_char/mbt_char.c b/bt_sd8997/bt_char/mbt_char.c
new file mode 100644
index 0000000..b0c39ac
--- /dev/null
+++ b/bt_sd8997/bt_char/mbt_char.c
@@ -0,0 +1,807 @@
+/** @file mbt_char.c
+  *
+  * @brief This file contains the char device function calls
+  *
+  * Copyright (C) 2010-2018, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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/path.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+
+#include "bt_drv.h"
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#include <linux/sched/signal.h>
+#endif
+#include "mbt_char.h"
+
+static LIST_HEAD(char_dev_list);
+
+static DEFINE_SPINLOCK(char_dev_list_lock);
+
+static int mbtchar_major = MBTCHAR_MAJOR_NUM;
+
+/**
+ *	@brief  Gets char device structure
+ *
+ *	@param dev		A pointer to char_dev
+ *
+ *	@return			kobject structure
+ */
+struct kobject *
+chardev_get(struct char_dev *dev)
+{
+	struct kobject *kobj;
+
+	kobj = bt_priv_get(dev->m_dev->driver_data);
+	if (!kobj)
+		return NULL;
+	PRINTM(INFO, "dev get kobj\n");
+	kobj = kobject_get(&dev->kobj);
+	if (!kobj)
+		bt_priv_put(dev->m_dev->driver_data);
+	return kobj;
+}
+
+/**
+ *	@brief  Prints char device structure
+ *
+ *	@param dev		A pointer to char_dev
+ *
+ *	@return			N/A
+ */
+void
+chardev_put(struct char_dev *dev)
+{
+	if (dev) {
+		struct m_dev *m_dev = dev->m_dev;
+		PRINTM(INFO, "dev put kobj\n");
+		kobject_put(&dev->kobj);
+		if (m_dev)
+			bt_priv_put(m_dev->driver_data);
+	}
+}
+
+/**
+ *	@brief Changes permissions of the dev
+ *
+ *	@param name	pointer to character
+ *	@param mode		mode_t type data
+ *	@return			0--success otherwise failure
+ */
+int
+mbtchar_chmod(char *name, mode_t mode)
+{
+	struct path path;
+	struct inode *inode;
+	struct iattr newattrs;
+	int ret;
+	int retrycount = 0;
+
+	ENTER();
+	do {
+		os_sched_timeout(30);
+		ret = kern_path(name, LOOKUP_FOLLOW, &path);
+		if (++retrycount >= 10) {
+			PRINTM(ERROR,
+			       "mbtchar_chmod(): fail to get kern_path\n");
+			LEAVE();
+			return -EFAULT;
+		}
+	} while (ret);
+	inode = path.dentry->d_inode;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_lock(&inode->i_mutex);
+#else
+	inode_lock(inode);
+#endif
+	ret = mnt_want_write(path.mnt);
+	if (ret)
+		goto out_unlock;
+	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
+	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+	if (inode->i_op->setattr)
+		ret = inode->i_op->setattr(path.dentry, &newattrs);
+	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+		ret = simple_setattr(path.dentry, &newattrs);
+#else
+		ret = inode_setattr(inode, &newattrs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+
+	path_put(&path);
+	LEAVE();
+	return ret;
+out_unlock:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+	path_put(&path);
+	return ret;
+}
+
+/**
+ *	@brief Changes ownership of the dev
+ *
+ *	@param name	pointer to character
+ *	@param user		uid_t type data
+ *	@param group	gid_t type data
+ *	@return			0--success otherwise failure
+ */
+int
+mbtchar_chown(char *name, uid_t user, gid_t group)
+{
+	struct path path;
+	struct inode *inode = NULL;
+	struct iattr newattrs;
+	int ret = 0;
+	int retrycount = 0;
+
+	ENTER();
+	do {
+		os_sched_timeout(30);
+		ret = kern_path(name, LOOKUP_FOLLOW, &path);
+		if (++retrycount >= 10) {
+			PRINTM(ERROR,
+			       "mbtchar_chown(): fail to get kern_path\n");
+			LEAVE();
+			return -EFAULT;
+		}
+	} while (ret);
+	inode = path.dentry->d_inode;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_lock(&inode->i_mutex);
+#else
+	inode_lock(inode);
+#endif
+	ret = mnt_want_write(path.mnt);
+	if (ret)
+		goto out_unlock;
+	newattrs.ia_valid = ATTR_CTIME;
+	if (user != (uid_t) (-1)) {
+		newattrs.ia_valid |= ATTR_UID;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+		newattrs.ia_uid = user;
+#else
+		newattrs.ia_uid = KUIDT_INIT(user);
+#endif
+	}
+	if (group != (gid_t) (-1)) {
+		newattrs.ia_valid |= ATTR_GID;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
+		newattrs.ia_gid = group;
+#else
+		newattrs.ia_gid = KGIDT_INIT(group);
+#endif
+	}
+	if (!S_ISDIR(inode->i_mode))
+		newattrs.ia_valid |=
+			ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+	if (inode->i_op->setattr)
+		ret = inode->i_op->setattr(path.dentry, &newattrs);
+	else
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
+		ret = simple_setattr(path.dentry, &newattrs);
+#else
+		ret = inode_setattr(inode, &newattrs);
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+
+	path_put(&path);
+	LEAVE();
+	return ret;
+out_unlock:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0)
+	mutex_unlock(&inode->i_mutex);
+#else
+	inode_unlock(inode);
+#endif
+	mnt_drop_write(path.mnt);
+	path_put(&path);
+	return ret;
+}
+
+/**
+ *	@brief write handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param buf		pointer to char buffer
+ *	@param count	size of receive buffer
+ *	@param f_pos	pointer to loff_t type data
+ *	@return			number of bytes written
+ */
+ssize_t
+chardev_write(struct file * filp, const char *buf, size_t count, loff_t * f_pos)
+{
+	int nwrite = 0;
+	struct sk_buff *skb;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	if (!test_bit(HCI_UP, &m_dev->flags)) {
+		LEAVE();
+		return -EBUSY;
+	}
+	nwrite = count;
+	skb = bt_skb_alloc(count, GFP_ATOMIC);
+	if (!skb) {
+		PRINTM(ERROR, "mbtchar_write(): fail to alloc skb\n");
+		LEAVE();
+		return -ENOMEM;
+	}
+
+	if (copy_from_user((void *)skb_put(skb, count), buf, count)) {
+		PRINTM(ERROR, "mbtchar_write(): cp_from_user failed\n");
+		kfree_skb(skb);
+		nwrite = -EFAULT;
+		goto exit;
+	}
+
+	skb->dev = (void *)m_dev;
+	bt_cb(skb)->pkt_type = *((unsigned char *)skb->data);
+	skb_pull(skb, 1);
+
+	PRINTM(DATA, "Write: pkt_type: 0x%x, len=%d @%lu\n",
+	       bt_cb(skb)->pkt_type, skb->len, jiffies);
+	DBG_HEXDUMP(DAT_D, "chardev_write", skb->data, skb->len);
+
+	/* Send skb to the hci wrapper layer */
+	if (m_dev->send(m_dev, skb)) {
+		PRINTM(ERROR, "Write: Fail\n");
+		nwrite = 0;
+		/* Send failed */
+		kfree_skb(skb);
+	}
+exit:
+	LEAVE();
+	return nwrite;
+}
+
+/**
+ *	@brief read handler for BT char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param buf		pointer to char buffer
+ *	@param count	size of receive buffer
+ *	@param f_pos	pointer to loff_t type data
+ *	@return			number of bytes read
+ */
+ssize_t
+chardev_read(struct file * filp, char *buf, size_t count, loff_t * f_pos)
+{
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t ret = 0;
+	struct sk_buff *skb = NULL;
+
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	/* Wait for rx data */
+	add_wait_queue(&m_dev->req_wait_q, &wait);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		skb = skb_dequeue(&m_dev->rx_q);
+		if (skb)
+			break;
+		if (!test_bit(HCI_UP, &m_dev->flags)) {
+			ret = -EBUSY;
+			break;
+		}
+
+		if (filp->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			break;
+		}
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&m_dev->req_wait_q, &wait);
+
+	if (!skb)
+		goto out;
+
+	if (m_dev->read_continue_flag == 0) {
+		/* Put type byte before the data */
+		memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+		PRINTM(DATA, "Read: pkt_type: 0x%x, len=%d @%lu\n",
+		       bt_cb(skb)->pkt_type, skb->len, jiffies);
+	}
+	DBG_HEXDUMP(DAT_D, "chardev_read", skb->data, skb->len);
+	if (skb->len > count) {
+		/* user data length is smaller than the skb length */
+		if (copy_to_user(buf, skb->data, count)) {
+			ret = -EFAULT;
+			goto outf;
+		}
+		skb_pull(skb, count);
+		skb_queue_head(&m_dev->rx_q, skb);
+		m_dev->read_continue_flag = 1;
+		wake_up_interruptible(&m_dev->req_wait_q);
+		ret = count;
+		goto out;
+	} else {
+		if (copy_to_user(buf, skb->data, skb->len)) {
+			ret = -EFAULT;
+			goto outf;
+		}
+		m_dev->read_continue_flag = 0;
+		ret = skb->len;
+	}
+outf:
+	kfree_skb(skb);
+out:
+	if (m_dev->wait_rx_complete && skb_queue_empty(&m_dev->rx_q)) {
+		m_dev->rx_complete_flag = TRUE;
+		wake_up_interruptible(&m_dev->rx_wait_q);
+	}
+	LEAVE();
+	return ret;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief ioctl common handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+char_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg)
+#else
+/**
+ *	@brief ioctl common handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+char_ioctl(struct file *filp, unsigned int cmd, void *arg)
+#endif
+{
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+	PRINTM(INFO, "IOCTL: cmd=%d\n", cmd);
+	switch (cmd) {
+	case MBTCHAR_IOCTL_RELEASE:
+		m_dev->close(m_dev);
+		break;
+	case MBTCHAR_IOCTL_QUERY_TYPE:
+		m_dev->query(m_dev, arg);
+		break;
+	default:
+		m_dev->ioctl(m_dev, cmd, arg);
+		break;
+	}
+	LEAVE();
+	return 0;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief ioctl handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_ioctl(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+#else
+/**
+ *	@brief ioctl handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	return char_ioctl(inode, filp, cmd, (void *)arg);
+#else
+	return char_ioctl(filp, cmd, (void *)arg);
+#endif
+}
+
+#ifdef CONFIG_COMPAT
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+/**
+ *	@brief compat ioctl handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_ioctl_compat(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg)
+#else
+/**
+ *	@brief compat ioctl handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param cmd		contains the IOCTL
+ *	@param arg		contains the arguement
+ *	@return			0--success otherwise failure
+ */
+long
+chardev_ioctl_compat(struct file *filp, unsigned int cmd, unsigned long arg)
+#endif
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	return char_ioctl(inode, filp, cmd, compat_ptr(arg));
+#else
+	return char_ioctl(filp, cmd, compat_ptr(arg));
+#endif
+}
+#endif /* CONFIG_COMPAT */
+
+/**
+ *	@brief open handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct char_dev *dev = NULL;
+	struct m_dev *m_dev = NULL;
+	struct char_dev *cdev = NULL;
+	struct list_head *p = NULL;
+	ENTER();
+
+	list_for_each(p, &char_dev_list) {
+		cdev = list_entry(p, struct char_dev, list);
+		if (mbtchar_major == MAJOR(inode->i_cdev->dev) &&
+		    cdev->minor == MINOR(inode->i_cdev->dev)) {
+			dev = cdev;
+			break;
+		}
+	}
+	if (!dev) {
+		PRINTM(ERROR, "cannot find dev from inode\n");
+		LEAVE();
+		return -ENXIO;
+	}
+	if (!chardev_get(dev)) {
+		LEAVE();
+		return -ENXIO;
+	}
+	filp->private_data = dev;	/* for other methods */
+	m_dev = dev->m_dev;
+	mdev_req_lock(m_dev);
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
+	if (test_bit(HCI_UP, &m_dev->flags)) {
+		atomic_inc(&m_dev->extra_cnt);
+		goto done;
+	}
+#endif
+	if (m_dev->open(m_dev)) {
+		ret = -EIO;
+		goto done;
+	}
+	set_bit(HCI_UP, &m_dev->flags);
+
+done:
+	mdev_req_unlock(m_dev);
+	if (ret)
+		chardev_put(dev);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief release handler for char dev
+ *
+ *	@param inode	pointer to structure inode
+ *	@param filp	pointer to structure file
+ *	@return			0--success otherwise failure
+ */
+int
+chardev_release(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	ENTER();
+	if (!dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+	m_dev = dev->m_dev;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
+	if (m_dev && (atomic_dec_if_positive(&m_dev->extra_cnt) >= 0)) {
+		LEAVE();
+		return ret;
+	}
+#endif
+	if (m_dev)
+		ret = dev->m_dev->close(dev->m_dev);
+	filp->private_data = NULL;
+	chardev_put(dev);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief poll handler for char dev
+ *
+ *	@param filp	pointer to structure file
+ *	@param wait		pointer to poll_table structure
+ *	@return			mask
+ */
+static unsigned int
+chardev_poll(struct file *filp, poll_table * wait)
+{
+	unsigned int mask;
+	struct char_dev *dev = (struct char_dev *)filp->private_data;
+	struct m_dev *m_dev = NULL;
+	ENTER();
+	if (!dev || !dev->m_dev) {
+		LEAVE();
+		return -ENXIO;
+	}
+
+	m_dev = dev->m_dev;
+	poll_wait(filp, &m_dev->req_wait_q, wait);
+	mask = POLLOUT | POLLWRNORM;
+	if (skb_peek(&m_dev->rx_q))
+		mask |= POLLIN | POLLRDNORM;
+	if (!test_bit(HCI_UP, &(m_dev->flags)))
+		mask |= POLLHUP;
+	PRINTM(INFO, "poll mask=0x%x\n", mask);
+	LEAVE();
+	return mask;
+}
+
+/* File ops for the Char driver */
+const struct file_operations chardev_fops = {
+	.owner = THIS_MODULE,
+	.read = chardev_read,
+	.write = chardev_write,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
+	.ioctl = chardev_ioctl,
+#else
+	.unlocked_ioctl = chardev_ioctl,
+#endif
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = chardev_ioctl_compat,
+#endif
+	.open = chardev_open,
+	.release = chardev_release,
+	.poll = chardev_poll,
+};
+
+/**
+ *	@brief This function creates the char dev
+ *
+ *	@param dev			A pointer to structure char_dev
+ *  @param char_class	A pointer to class struct
+ *  @param mod_name		A pointer to char
+ *  @param dev_name		A pointer to char
+ *	@return				0--success otherwise failure
+ */
+int
+register_char_dev(struct char_dev *dev, struct class *char_class,
+		  char *mod_name, char *dev_name)
+{
+	int ret = 0, dev_num;
+	unsigned long flags;
+	ENTER();
+	/* create the chrdev region */
+	if (mbtchar_major) {
+		dev_num = MKDEV(mbtchar_major, dev->minor);
+		ret = register_chrdev_region(dev_num, 1, mod_name);
+	} else {
+		PRINTM(INFO, "chardev: no major # yet\n");
+		ret = alloc_chrdev_region((dev_t *) & dev_num, dev->minor, 1,
+					  mod_name);
+	}
+
+	if (ret) {
+		PRINTM(ERROR, "chardev: create chrdev_region failed\n");
+		LEAVE();
+		return ret;
+	}
+	if (!mbtchar_major) {
+		/* Store the allocated dev major # */
+		mbtchar_major = MAJOR(dev_num);
+	}
+	dev->cdev = cdev_alloc();
+	dev->cdev->ops = &chardev_fops;
+	dev->cdev->owner = chardev_fops.owner;
+	dev_num = MKDEV(mbtchar_major, dev->minor);
+
+	if (cdev_add(dev->cdev, dev_num, 1)) {
+		PRINTM(ERROR, "chardev: cdev_add failed\n");
+		ret = -EFAULT;
+		goto free_cdev_region;
+	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+	if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), NULL, dev_name);
+	}
+	if (dev->dev_type == DEBUG_TYPE) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), NULL, dev_name);
+	}
+#else
+	if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), dev_name);
+	}
+	if (dev->dev_type == DEBUG_TYPE) {
+		device_create(char_class, NULL,
+			      MKDEV(mbtchar_major, dev->minor), dev_name);
+	}
+#endif
+	PRINTM(INFO, "register char dev=%s\n", dev_name);
+
+	/** modify later */
+
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	list_add_tail(&dev->list, &char_dev_list);
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+
+	LEAVE();
+	return ret;
+free_cdev_region:
+	unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1);
+	LEAVE();
+	return ret;
+}
+
+/**
+ *	@brief This function deletes the char dev
+ *
+ *  @param dev			A pointer to structure char_dev
+ *  @param char_class	A pointer to class struct
+ *  @param dev_name		A pointer to char
+ *  @return				0--success otherwise failure
+ */
+int
+unregister_char_dev(struct char_dev *dev, struct class *char_class,
+		    char *dev_name)
+{
+	ENTER();
+	device_destroy(char_class, MKDEV(mbtchar_major, dev->minor));
+	cdev_del(dev->cdev);
+	unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1);
+	PRINTM(INFO, "unregister char dev=%s\n", dev_name);
+
+	LEAVE();
+	return 0;
+}
+
+/**
+ *	@brief This function cleans module
+ *
+ *  @param char_class	A pointer to class struct
+ *  @return				N/A
+ */
+void
+chardev_cleanup(struct class *char_class)
+{
+	unsigned long flags;
+	struct list_head *p = NULL;
+	struct char_dev *dev = NULL;
+	ENTER();
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	do {
+		dev = NULL;
+		list_for_each(p, &char_dev_list) {
+			dev = list_entry(p, struct char_dev, list);
+			list_del(p);
+			spin_unlock_irqrestore(&char_dev_list_lock, flags);
+			unregister_char_dev(dev, char_class, dev->m_dev->name);
+			kobject_put(&dev->kobj);
+			spin_lock_irqsave(&char_dev_list_lock, flags);
+			break;
+		}
+	} while (dev);
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+	class_destroy(char_class);
+	LEAVE();
+}
+
+/**
+ *	@brief This function cleans module
+ *
+ *  @param m_dev	A pointer to m_dev struct
+ *  @param char_class	A pointer to class struct
+ *  @return			N/A
+ */
+void
+chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class)
+{
+	unsigned long flags;
+	struct list_head *p = NULL;
+	struct char_dev *dev = NULL;
+	ENTER();
+	spin_lock_irqsave(&char_dev_list_lock, flags);
+	list_for_each(p, &char_dev_list) {
+		dev = list_entry(p, struct char_dev, list);
+		if (dev->minor == m_dev->index) {
+			list_del(p);
+			spin_unlock_irqrestore(&char_dev_list_lock, flags);
+			dev->m_dev = NULL;
+			unregister_char_dev(dev, char_class, m_dev->name);
+			kobject_put(&dev->kobj);
+			spin_lock_irqsave(&char_dev_list_lock, flags);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&char_dev_list_lock, flags);
+	LEAVE();
+}
diff --git a/bt_sd8997/bt_char/mbt_char.h b/bt_sd8997/bt_char/mbt_char.h
new file mode 100644
index 0000000..9997a38
--- /dev/null
+++ b/bt_sd8997/bt_char/mbt_char.h
@@ -0,0 +1,72 @@
+/** @file mbt_char.h
+  *
+  * @brief This file contains mbtchar driver specific defines etc
+  *
+  * Copyright (C) 2010-2018, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+#ifndef __MBT_CHAR_H__
+#define __MBT_CHAR_H__
+
+#include <linux/cdev.h>
+#include <linux/device.h>
+
+/** Define ioctl */
+#define MBTCHAR_IOCTL_RELEASE       _IO('M', 1)
+#define MBTCHAR_IOCTL_QUERY_TYPE    _IO('M', 2)
+#ifdef BLE_WAKEUP
+#define MBTCHAR_IOCTL_BLE_WAKEUP_PARAM _IO('M', 4)
+#endif
+
+#define MBTCHAR_MAJOR_NUM            (0)
+
+/** Interface specific macros */
+#define MBTCHAR_MINOR_BASE           (0)
+#define FMCHAR_MINOR_BASE            (10)
+#define NFCCHAR_MINOR_BASE           (20)
+#define DEBUGCHAR_MINOR_BASE         (30)
+
+/** Declaration of char_dev struct */
+struct char_dev {
+	struct list_head list;
+	int minor;
+	int dev_type;
+	struct cdev *cdev;
+	struct m_dev *m_dev;
+	struct kobject kobj;
+};
+
+/** Changes permissions of the dev */
+int mbtchar_chmod(char *name, mode_t mode);
+
+/** Changes ownership of the dev */
+int mbtchar_chown(char *name, uid_t user, gid_t group);
+
+/**	This function creates the char dev */
+int register_char_dev(struct char_dev *dev, struct class *char_class,
+		      char *mod_name, char *dev_name);
+
+/**	This function deletes the char dev */
+int unregister_char_dev(struct char_dev *dev, struct class *char_class,
+			char *dev_name);
+
+/**	This function cleans module */
+void chardev_cleanup(struct class *char_class);
+
+/**	This function cleans module */
+void chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class);
+
+#endif /*__MBT_CHAR_H__*/
diff --git a/wlan_sd8897/Makefile b/wlan_sd8897/Makefile
new file mode 100644
index 0000000..f64c670
--- /dev/null
+++ b/wlan_sd8897/Makefile
@@ -0,0 +1,596 @@
+# File: Makefile
+#
+# Copyright (C) 2008-2018, Marvell International Ltd.
+#
+# This software file (the "File") is distributed by Marvell 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.
+#
+# A copy of the GPL is available in file gpl-2.0.txt accompanying in this
+# deliverables.
+#
+# 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.
+
+COMPATDIR=/lib/modules/$(KERNELVERSION_X86)/build/compat-wireless-3.2-rc1-1/include
+CC=		$(CROSS_COMPILE)gcc -I$(COMPATDIR)
+LD=		$(CROSS_COMPILE)ld
+BACKUP=		/root/backup
+YMD=		`date +%Y%m%d%H%M`
+
+ifneq ($(COMPAT_VERSION_CODE),)
+DRV_DIR ?= $(shell pwd)
+export DRV_DIR
+COMPAT_VERSION=$(shell echo $(COMPAT_VERSION_CODE) | awk -F '.' '{print $$1}')
+COMPAT_PATCHLEVEL=$(shell echo $(COMPAT_VERSION_CODE) | awk -F '.' '{print $$2}')
+COMPAT_SUBLEVEL=$(shell echo $(COMPAT_VERSION_CODE) | awk -F '.' '{print $$3}')
+DECL_HEADER_FILE=$(DRV_DIR)/mlinux/moal_main.h
+$(shell sed -i 's/COMPAT_VERSION_CODE KERNEL_VERSION.*/COMPAT_VERSION_CODE KERNEL_VERSION(\
+		$(COMPAT_VERSION), $(COMPAT_PATCHLEVEL), $(COMPAT_SUBLEVEL))/g' $(DECL_HEADER_FILE))
+endif
+
+#############################################################################
+# Configuration Options
+#############################################################################
+
+# Debug Option
+# DEBUG LEVEL n/1/2:
+# n: NO DEBUG
+# 1: Only PRINTM(MMSG,...), PRINTM(MFATAL,...), ...
+# 2: All PRINTM()
+CONFIG_DEBUG=1
+
+# Proc debug file
+CONFIG_PROC_DEBUG=y
+
+# Enable STA mode support
+CONFIG_STA_SUPPORT=y
+
+# Enable uAP mode support
+CONFIG_UAP_SUPPORT=y
+
+# Enable WIFIDIRECT support
+CONFIG_WIFI_DIRECT_SUPPORT=y
+
+# Enable WIFIDISPLAY support
+CONFIG_WIFI_DISPLAY_SUPPORT=y
+
+# Re-association in driver
+CONFIG_REASSOCIATION=y
+
+
+# Manufacturing firmware support
+CONFIG_MFG_CMD_SUPPORT=y
+
+# OpenWrt support
+CONFIG_OPENWRT_SUPPORT=n
+
+# Big-endian platform
+CONFIG_BIG_ENDIAN=n
+
+
+
+
+ifeq ($(CONFIG_DRV_EMBEDDED_SUPPLICANT), y)
+CONFIG_EMBEDDED_SUPP_AUTH=y
+else
+ifeq ($(CONFIG_DRV_EMBEDDED_AUTHENTICATOR), y)
+CONFIG_EMBEDDED_SUPP_AUTH=y
+endif
+endif
+
+# Enable SDIO multi-port Tx aggregation
+CONFIG_SDIO_MULTI_PORT_TX_AGGR=y
+
+# Enable SDIO multi-port Rx aggregation
+CONFIG_SDIO_MULTI_PORT_RX_AGGR=y
+
+# SDIO suspend/resume
+CONFIG_SDIO_SUSPEND_RESUME=y
+
+# DFS testing support
+CONFIG_DFS_TESTING_SUPPORT=y
+
+# Multi-channel support
+CONFIG_MULTI_CHAN_SUPPORT=y
+
+
+# Use static link for app build
+export CONFIG_STATIC_LINK=y
+CONFIG_ANDROID_KERNEL=y
+
+#32bit app over 64bit kernel support
+CONFIG_USERSPACE_32BIT_OVER_KERNEL_64BIT=y
+
+
+#############################################################################
+# Select Platform Tools
+#############################################################################
+
+MODEXT = ko
+ccflags-y += -I$(srctree)/drivers/soc/berlin/modules/wlan_sd8897/mlan
+ccflags-y += -DLINUX
+
+
+
+ARCH ?= arm
+KERNELDIR ?= /usr/src/arm/linux-4.1.43-bg4ct
+CROSS_COMPILE ?= /usr/local/gcc-linaro-5.3-2016.02-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-
+
+LD += -S
+
+BINDIR = ../bin_sd8897
+APPDIR= $(shell if test -d "mapp"; then echo mapp; fi)
+
+#############################################################################
+# Compiler Flags
+#############################################################################
+
+	ccflags-y += -I$(KERNELDIR)/include
+
+	ccflags-y += -DFPNUM='"68"'
+
+ifeq ($(CONFIG_DEBUG),1)
+	ccflags-y += -DDEBUG_LEVEL1
+endif
+
+ifeq ($(CONFIG_DEBUG),2)
+	ccflags-y += -DDEBUG_LEVEL1
+	ccflags-y += -DDEBUG_LEVEL2
+	DBG=	-dbg
+endif
+
+ifeq ($(CONFIG_PROC_DEBUG),y)
+	ccflags-y += -DPROC_DEBUG
+	export CONFIG_PROC_DEBUG
+endif
+
+ifeq ($(CONFIG_64BIT), y)
+	ccflags-y += -DMLAN_64BIT
+endif
+
+ifeq ($(CONFIG_STA_SUPPORT),y)
+	ccflags-y += -DSTA_SUPPORT
+ifeq ($(CONFIG_REASSOCIATION),y)
+	ccflags-y += -DREASSOCIATION
+endif
+else
+CONFIG_WIFI_DIRECT_SUPPORT=n
+CONFIG_WIFI_DISPLAY_SUPPORT=n
+CONFIG_STA_WEXT=n
+CONFIG_STA_CFG80211=n
+endif
+
+ifeq ($(CONFIG_UAP_SUPPORT),y)
+	ccflags-y += -DUAP_SUPPORT
+else
+CONFIG_WIFI_DIRECT_SUPPORT=n
+CONFIG_WIFI_DISPLAY_SUPPORT=n
+CONFIG_UAP_WEXT=n
+CONFIG_UAP_CFG80211=n
+endif
+
+ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y)
+	ccflags-y += -DWIFI_DIRECT_SUPPORT
+endif
+ifeq ($(CONFIG_WIFI_DISPLAY_SUPPORT),y)
+	ccflags-y += -DWIFI_DISPLAY_SUPPORT
+endif
+
+ifeq ($(CONFIG_MFG_CMD_SUPPORT),y)
+	ccflags-y += -DMFG_CMD_SUPPORT
+endif
+
+ifeq ($(CONFIG_BIG_ENDIAN),y)
+	ccflags-y += -DBIG_ENDIAN_SUPPORT
+endif
+
+ifeq ($(CONFIG_USERSPACE_32BIT_OVER_KERNEL_64BIT),y)
+	ccflags-y += -DUSERSPACE_32BIT_OVER_KERNEL_64BIT
+endif
+
+ifeq ($(CONFIG_SDIO_MULTI_PORT_TX_AGGR),y)
+	ccflags-y += -DSDIO_MULTI_PORT_TX_AGGR
+endif
+
+ifeq ($(CONFIG_SDIO_MULTI_PORT_RX_AGGR),y)
+	ccflags-y += -DSDIO_MULTI_PORT_RX_AGGR
+endif
+
+ifeq ($(CONFIG_SDIO_SUSPEND_RESUME),y)
+	ccflags-y += -DSDIO_SUSPEND_RESUME
+endif
+
+ifeq ($(CONFIG_MULTI_CHAN_SUPPORT),y)
+	ccflags-y += -DMULTI_CHAN_SUPPORT
+endif
+
+ifeq ($(CONFIG_DFS_TESTING_SUPPORT),y)
+	ccflags-y += -DDFS_TESTING_SUPPORT
+endif
+
+
+ifeq ($(CONFIG_ANDROID_KERNEL), y)
+	ccflags-y += -DANDROID_KERNEL
+endif
+
+ifeq ($(CONFIG_OPENWRT_SUPPORT), y)
+	ccflags-y += -DOPENWRT
+endif
+
+
+ifeq ($(CONFIG_T50), y)
+	ccflags-y += -DT50
+	ccflags-y += -DT40
+	ccflags-y += -DT3T
+endif
+
+# add -Wno-packed-bitfield-compat when GCC version greater than 4.4
+GCC_VERSION := $(shell echo `gcc -dumpversion | cut -f1-2 -d.` \>= 4.4 | sed -e 's/\./*100+/g' | bc )
+ifeq ($(GCC_VERSION),1)
+	ccflags-y += -Wno-packed-bitfield-compat
+endif
+
+#############################################################################
+# Make Targets
+#############################################################################
+
+ifneq ($(KERNELRELEASE),)
+
+ifeq ($(CONFIG_WIRELESS_EXT),y)
+ifeq ($(CONFIG_WEXT_PRIV),y)
+	# Enable WEXT for STA
+	CONFIG_STA_WEXT=y
+	# Enable WEXT for uAP
+	CONFIG_UAP_WEXT=y
+else
+# Disable WEXT for STA
+	CONFIG_STA_WEXT=n
+# Disable WEXT for uAP
+	CONFIG_UAP_WEXT=n
+endif
+endif
+
+# Enable CFG80211 for STA
+ifeq ($(CONFIG_CFG80211),y)
+	CONFIG_STA_CFG80211=y
+else
+ifeq ($(CONFIG_CFG80211),m)
+	CONFIG_STA_CFG80211=y
+else
+	CONFIG_STA_CFG80211=n
+endif
+endif
+
+# OpenWrt
+ifeq ($(CONFIG_OPENWRT_SUPPORT), y)
+ifeq ($(CPTCFG_CFG80211),y)
+	CONFIG_STA_CFG80211=y
+else
+ifeq ($(CPTCFG_CFG80211),m)
+	CONFIG_STA_CFG80211=y
+else
+	CONFIG_STA_CFG80211=n
+endif
+endif
+endif
+
+# Enable CFG80211 for uAP
+ifeq ($(CONFIG_CFG80211),y)
+	CONFIG_UAP_CFG80211=y
+else
+ifeq ($(CONFIG_CFG80211),m)
+	CONFIG_UAP_CFG80211=y
+else
+	CONFIG_UAP_CFG80211=n
+endif
+endif
+
+# OpenWrt
+ifeq ($(CONFIG_OPENWRT_SUPPORT), y)
+ifeq ($(CPTCFG_CFG80211),y)
+	CONFIG_UAP_CFG80211=y
+else
+ifeq ($(CPTCFG_CFG80211),m)
+	CONFIG_UAP_CFG80211=y
+else
+	CONFIG_UAP_CFG80211=n
+endif
+endif
+endif
+
+ifneq ($(CONFIG_STA_SUPPORT),y)
+	CONFIG_WIFI_DIRECT_SUPPORT=n
+	CONFIG_WIFI_DISPLAY_SUPPORT=n
+	CONFIG_STA_WEXT=n
+	CONFIG_STA_CFG80211=n
+endif
+
+ifneq ($(CONFIG_UAP_SUPPORT),y)
+	CONFIG_WIFI_DIRECT_SUPPORT=n
+	CONFIG_WIFI_DISPLAY_SUPPORT=n
+	CONFIG_UAP_WEXT=n
+	CONFIG_UAP_CFG80211=n
+endif
+
+ifeq ($(CONFIG_STA_SUPPORT),y)
+ifeq ($(CONFIG_STA_WEXT),y)
+	ccflags-y += -DSTA_WEXT
+endif
+ifeq ($(CONFIG_STA_CFG80211),y)
+	ccflags-y += -DSTA_CFG80211
+endif
+endif
+ifeq ($(CONFIG_UAP_SUPPORT),y)
+ifeq ($(CONFIG_UAP_WEXT),y)
+	ccflags-y += -DUAP_WEXT
+endif
+ifeq ($(CONFIG_UAP_CFG80211),y)
+	ccflags-y += -DUAP_CFG80211
+endif
+endif
+
+print:
+ifeq ($(CONFIG_STA_SUPPORT),y)
+ifeq ($(CONFIG_STA_WEXT),n)
+ifeq ($(CONFIG_STA_CFG80211),n)
+	@echo "Can not build STA without WEXT or CFG80211"
+	exit 2
+endif
+endif
+endif
+ifeq ($(CONFIG_UAP_SUPPORT),y)
+ifeq ($(CONFIG_UAP_WEXT),n)
+ifeq ($(CONFIG_UAP_CFG80211),n)
+	@echo "Can not build UAP without WEXT or CFG80211"
+	exit 2
+endif
+endif
+endif
+
+
+
+
+
+MOALOBJS =	mlinux/moal_main.o \
+		mlinux/moal_ioctl.o \
+		mlinux/moal_shim.o \
+		mlinux/moal_eth_ioctl.o
+
+MLANOBJS =	mlan/mlan_shim.o mlan/mlan_init.o \
+		mlan/mlan_txrx.o \
+		mlan/mlan_cmdevt.o mlan/mlan_misc.o \
+		mlan/mlan_cfp.o \
+		mlan/mlan_module.o
+
+MLANOBJS += mlan/mlan_wmm.o
+MLANOBJS += mlan/mlan_sdio.o
+MLANOBJS += mlan/mlan_11n_aggr.o
+MLANOBJS += mlan/mlan_11n_rxreorder.o
+MLANOBJS += mlan/mlan_11n.o
+MLANOBJS += mlan/mlan_11ac.o
+MLANOBJS += mlan/mlan_11d.o
+MLANOBJS += mlan/mlan_11h.o
+ifeq ($(CONFIG_STA_SUPPORT),y)
+MLANOBJS += mlan/mlan_meas.o
+MLANOBJS += mlan/mlan_scan.o \
+			mlan/mlan_sta_ioctl.o \
+			mlan/mlan_sta_rx.o \
+			mlan/mlan_sta_tx.o \
+			mlan/mlan_sta_event.o \
+			mlan/mlan_sta_cmd.o \
+			mlan/mlan_sta_cmdresp.o \
+			mlan/mlan_join.o
+ifeq ($(CONFIG_STA_WEXT),y)
+MOALOBJS += mlinux/moal_priv.o \
+            mlinux/moal_wext.o
+endif
+endif
+ifeq ($(CONFIG_UAP_SUPPORT),y)
+MLANOBJS += mlan/mlan_uap_ioctl.o
+MLANOBJS += mlan/mlan_uap_cmdevent.o
+MLANOBJS += mlan/mlan_uap_txrx.o
+MOALOBJS += mlinux/moal_uap.o
+ifeq ($(CONFIG_UAP_WEXT),y)
+MOALOBJS += mlinux/moal_uap_priv.o
+MOALOBJS += mlinux/moal_uap_wext.o
+endif
+endif
+ifeq ($(CONFIG_STA_CFG80211),y)
+MOALOBJS += mlinux/moal_cfg80211.o
+MOALOBJS += mlinux/moal_cfgvendor.o
+MOALOBJS += mlinux/moal_sta_cfg80211.o
+endif
+ifeq ($(CONFIG_UAP_CFG80211),y)
+MOALOBJS += mlinux/moal_cfg80211.o
+MOALOBJS += mlinux/moal_cfgvendor.o
+MOALOBJS += mlinux/moal_uap_cfg80211.o
+endif
+
+ifdef CONFIG_PROC_FS
+MOALOBJS += mlinux/moal_proc.o
+ifeq ($(CONFIG_PROC_DEBUG),y)
+MOALOBJS += mlinux/moal_debug.o
+endif
+endif
+
+
+
+
+ifeq ($(CONFIG_MULTI_INTERFACE),y)
+obj-m := mlan_sdio.o
+mlan_sdio-objs := $(MLANOBJS)
+else
+obj-m := mlan.o
+mlan-objs := $(MLANOBJS)
+endif
+
+obj-m := 8897mlan.o
+8897mlan-objs := $(MLANOBJS)
+MOALOBJS += mlinux/moal_sdio_mmc.o
+obj-m += sd8897.o
+sd8897-objs := $(MOALOBJS)
+
+# Otherwise we were called directly from the command line; invoke the kernel build system.
+else
+
+default:
+	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules
+
+endif
+
+###############################################################
+
+export		CC LD ccflags-y KERNELDIR
+
+ifeq ($(CONFIG_STA_SUPPORT),y)
+ifeq ($(CONFIG_UAP_SUPPORT),y)
+.PHONY: mapp/mlanconfig mapp/mlan2040coex mapp/mlanevent mapp/uaputl mapp/mlanutl clean distclean
+else
+.PHONY: mapp/mlanconfig mapp/mlanevent mapp/mlan2040coex mapp/mlanutl clean distclean
+endif
+else
+ifeq ($(CONFIG_UAP_SUPPORT),y)
+.PHONY: mapp/mlanevent mapp/uaputl clean distclean
+endif
+endif
+	@echo "Finished Making Marvell Wlan Linux Driver"
+
+ifeq ($(CONFIG_STA_SUPPORT),y)
+mapp/mlanconfig:
+	$(MAKE) -C $@
+mapp/mlanutl:
+	$(MAKE) -C $@
+mapp/mlan2040coex:
+	$(MAKE) -C $@
+endif
+ifeq ($(CONFIG_UAP_SUPPORT),y)
+mapp/uaputl:
+	$(MAKE) -C $@
+endif
+ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y)
+mapp/wifidirectutl:
+	$(MAKE) -C $@
+endif
+mapp/mlanevent:
+	$(MAKE) -C $@
+
+echo:
+
+build:		echo default
+
+	@if [ ! -d $(BINDIR) ]; then \
+		mkdir $(BINDIR); \
+	fi
+
+ifeq ($(CONFIG_MULTI_INTERFACE),y)
+	cp -f mlan_sdio.$(MODEXT) $(BINDIR)/mlan_sdio$(DBG).$(MODEXT)
+else
+	cp -f mlan.$(MODEXT) $(BINDIR)/mlan$(DBG).$(MODEXT)
+endif
+	cp -f sd8xxx.$(MODEXT) $(BINDIR)/sd8897$(DBG).$(MODEXT)
+
+ifeq ($(CONFIG_STA_SUPPORT),y)
+	cp -f README $(BINDIR)
+	cp -f README_MLAN $(BINDIR)
+	cp -f README_RBC $(BINDIR)
+ifeq ($(CONFIG_OPENWRT_SUPPORT),y)
+	cp -f README_OPENWRT $(BINDIR)
+endif
+ifneq ($(APPDIR),)
+	$(MAKE) -C mapp/mlanconfig $@ INSTALLDIR=$(BINDIR)
+	$(MAKE) -C mapp/mlanutl $@ INSTALLDIR=$(BINDIR)
+	$(MAKE) -C mapp/mlan2040coex $@ INSTALLDIR=$(BINDIR)
+endif
+endif
+ifeq ($(CONFIG_UAP_SUPPORT),y)
+	cp -f README_UAP $(BINDIR)
+ifneq ($(APPDIR),)
+	$(MAKE) -C mapp/uaputl $@ INSTALLDIR=$(BINDIR)
+endif
+endif
+ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y)
+	cp -f README_WIFIDIRECT $(BINDIR)
+	cp -rpf script/wifidirect $(BINDIR)
+ifeq ($(CONFIG_WIFI_DISPLAY_SUPPORT),y)
+	cp -rpf script/wifidisplay $(BINDIR)
+endif
+ifneq ($(APPDIR),)
+	$(MAKE) -C mapp/wifidirectutl $@ INSTALLDIR=$(BINDIR)
+endif
+endif
+ifneq ($(APPDIR),)
+	$(MAKE) -C mapp/mlanevent $@ INSTALLDIR=$(BINDIR)
+endif
+
+clean:
+	-find . -name "*.o" -exec rm {} \;
+	-find . -name "*.ko" -exec rm {} \;
+	-find . -name ".*.cmd" -exec rm {} \;
+	-find . -name "*.mod.c" -exec rm {} \;
+	-find . -name "Module.symvers" -exec rm {} \;
+	-find . -name "Module.markers" -exec rm {} \;
+	-find . -name "modules.order" -exec rm {} \;
+	-find . -name ".*.dwo" -exec rm {} \;
+	-find . -name "*dwo" -exec rm {} \;
+	-rm -rf .tmp_versions
+ifneq ($(APPDIR),)
+ifeq ($(CONFIG_STA_SUPPORT),y)
+	$(MAKE) -C mapp/mlanconfig $@
+	$(MAKE) -C mapp/mlanutl $@
+	$(MAKE) -C mapp/mlan2040coex $@
+endif
+ifeq ($(CONFIG_UAP_SUPPORT),y)
+	$(MAKE) -C mapp/uaputl $@
+endif
+ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y)
+	$(MAKE) -C mapp/wifidirectutl $@
+endif
+	$(MAKE) -C mapp/mlanevent $@
+endif
+
+install: default
+
+	cp -f mlan.$(MODEXT) $(INSTALLDIR)/mlan$(DBG).$(MODEXT)
+	cp -f ../io/sdio/$(PLATFORM)/sdio.$(MODEXT) $(INSTALLDIR)
+	cp -f sd8xxx.$(MODEXT) $(INSTALLDIR)/sd8897$(DBG).$(MODEXT)
+	echo "sd8897 Driver Installed"
+
+distclean:
+	-find . -name "*.o" -exec rm {} \;
+	-find . -name "*.orig" -exec rm {} \;
+	-find . -name "*.swp" -exec rm {} \;
+	-find . -name "*.*~" -exec rm {} \;
+	-find . -name "*~" -exec rm {} \;
+	-find . -name "*.d" -exec rm {} \;
+	-find . -name "*.a" -exec rm {} \;
+	-find . -name "tags" -exec rm {} \;
+	-find . -name ".*" -exec rm -rf 2> /dev/null \;
+	-find . -name "*.ko" -exec rm {} \;
+	-find . -name ".*.cmd" -exec rm {} \;
+	-find . -name "*.mod.c" -exec rm {} \;
+	-find . -name ".*.dwo" -exec rm {} \;
+	-find . -name "*dwo" -exec rm {} \;
+	-rm -rf .tmp_versions
+ifneq ($(APPDIR),)
+ifeq ($(CONFIG_STA_SUPPORT),y)
+	$(MAKE) -C mapp/mlanconfig $@
+	$(MAKE) -C mapp/mlanutl $@
+	$(MAKE) -C mapp/mlan2040coex $@
+endif
+ifeq ($(CONFIG_UAP_SUPPORT),y)
+	$(MAKE) -C mapp/uaputl $@
+endif
+ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y)
+	$(MAKE) -C mapp/wifidirectutl $@
+endif
+	$(MAKE) -C mapp/mlanevent $@
+endif
+
+# End of file
diff --git a/wlan_sd8897/README b/wlan_sd8897/README
new file mode 100644
index 0000000..73e44e0
--- /dev/null
+++ b/wlan_sd8897/README
@@ -0,0 +1,2365 @@
+===============================================================================
+			U S E R  M A N U A L
+
+ Copyright (C) 2008-2018, Marvell International Ltd.
+ All Rights Reserved
+
+1) FOR DRIVER BUILD
+
+	Goto source code directory wlan_src/.
+	make [clean] build
+	The driver and utility binaries can be found in ../bin_xxxx directory.
+	The driver code supports Linux kernel up to 4.15.
+
+2) FOR DRIVER INSTALL
+
+	a) Copy firmware image sd8786_uapsta.bin | sd8787_uapsta.bin | ... to
+	   /lib/firmware/mrvl/ directory, create the directory if it doesn't exist.
+	b) Install WLAN driver,
+	   There are drv_mode, max_sta_bss, max_uap_bss etc. module parameters.
+		The bit settings of drv_mode are,
+			Bit 0 :  STA
+			Bit 1 :  uAP
+			Bit 2 :  WIFIDIRECT
+		The default drv_mode is 7.
+			Bit 4 :  NAN
+
+		max_sta_bss: Maximum number of STA BSS (default 1, max 1)
+		sta_name: Name of the STA interface (default: "mlan")
+		max_uap_bss: Maximum number of uAP BSS (default 1, max 2)
+		uap_name: Name of the uAP interface (default: "uap")
+		max_wfd_bss: Maximum number of WIFIDIRECT BSS (default 1, max 1)
+		wfd_name: Name of the WIFIDIRECT interface (default: "wfd")
+		max_vir_bss: Number of Virtual interfaces (default 0)
+		nan_name: Name of the NAN interface (default: "nan")
+		max_nan_bss: Number of NAN interfaces (default 1)
+	   For example, to install SD8787 driver,
+		insmod mlan.ko
+		insmod sd8787.ko [drv_mode=3] [fw_name=mrvl/sd8787_uapsta.bin]
+	   To load driver in STA only mode,
+		insmod mlan.ko
+		insmod sd8787.ko drv_mode=1 [fw_name=mrvl/sd8787_uapsta.bin]
+	   To load driver in uAP only mode,
+		insmod mlan.ko
+		insmod sd8787.ko drv_mode=2 [fw_name=mrvl/sd8787_uapsta.bin]
+
+	   To switch mode between STA only, uAP only and uAPSTA etc. in run time,
+		echo drv_mode=1 > /proc/mwlan/config		// STA mode
+		echo drv_mode=2 > /proc/mwlan/config		// uAP mode
+		echo drv_mode=3 > /proc/mwlan/config		// STA+uAP mode
+		echo drv_mode=7 > /proc/mwlan/config		// STA+uAP+WIFIDIRECT mode
+	c) Uninstall WLAN driver,
+		ifconfig mlanX down
+		ifconfig uapX down
+		rmmod sd8xxx
+		rmmod mlan
+
+	To load driver with MFG firmware file, use mfg_mode=1 when insmod WLAN driver and
+	specify MFG firmware name if needed.
+
+	There are some other parameters for debugging purpose etc. Use modinfo to check details.
+	  drvdbg=<bit mask of driver debug message control>
+	  dev_cap_mask=<Bit mask of the device capability>
+	  mac_addr=xx:xx:xx:xx:xx:xx <override the MAC address (in hex)>
+	  auto_ds=0|1|2 <use MLAN default | enable auto deepsleep | disable auto deepsleep>
+	  ps_mode=0|1|2 <use MLAN default | enable IEEE PS mode | disable IEEE PS mode>
+	  p2p_enh=0|1 <Disable enhanced P2P (default) | Enable enhanced P2P>
+	  max_tx_buf=2048|4096|8192 <maximum AMSDU Tx buffer size>
+	  pm_keep_power=1|0 <PM keep power in suspend (default) | PM no power in suspend>
+	  shutdown_hs=1|0 <Enable HS when shutdown | No HS when shutdown (default)>
+	  cfg_11d=0|1|2 <use MLAN default | enable 11d | disable 11d>
+	  dts_enable=0|1 <Disable DTS | Enable DTS (default)>
+	  hw_test=0|1 <Disable hardware test (default) | Enable hardware test>
+	  fw_serial=0|1 <support parallel download FW | support serial download FW (default)>
+	  req_fw_nowait=0|1 <use request_firmware API (default) | use request_firmware_nowait API>
+	  init_cfg=<init config (MAC addresses, registers etc.) file name>
+		e.g. copy init_cfg.conf to firmware directory, init_cfg=mrvl/init_cfg.conf
+	  cal_data_cfg=<CAL data config file name>
+		e.g. copy cal_data.conf to firmware directory, cal_data_cfg=mrvl/cal_data.conf
+	  txpwrlimit_cfg=<Tx power limit config file name>
+		e.g. copy txpwrlimit_cfg_set.conf to firmware directory, txpwrlimit_cfg=mrvl/txpwrlimit_cfg_set.conf
+	  init_hostcmd_cfg=<init hostcmd config file name>
+		e.g. copy init_hostcmd_cfg.conf to firmware directory, init_hostcmd_cfg=mrvl/init_hostcmd_cfg.conf
+	  cfg80211_wext=<bit mask of CFG80211 and WEXT control>
+		Bit 0: STA WEXT
+		Bit 1: uAP WEXT
+		Bit 2: STA CFG80211
+		Bit 3: uAP CFG80211
+	cfg80211_drcs=1|0 <Enable DRCS support (default) | Disable DRCS support>
+	  reg_alpha2=<Regulatory alpha2 (default NULL)>
+	  wq_sched_prio: Priority for work queue
+	  wq_sched_policy: Scheduling policy for work queue
+		(0: SCHED_NORMAL, 1: SCHED_FIFO, 2: SCHED_RR, 3: SCHED_BATCH, 5: SCHED_IDLE)
+		Please note that, both wq_sched_prio and wq_sched_policy should be provided
+		as module parameters. If wq_sched_policy is (0, 3 or 5), then wq_sched_prio
+		must be 0. wq_sched_prio should be 1 to 99 otherwise.
+	  rx_work=0|1|2 <default | Enable rx_work_queue | Disable rx_work_queue>
+	  low_power_mode_enable=0|1 <disable low power mode (default)| enable low power mode>
+	  When low power mode is enabled, the output power will be clipped at ~+10dBm and the
+	  expected PA current is expected to be in the 80-90 mA range for b/g/n modes
+      indrstcfg=<2-byte IR configuration>
+      gpio pin (high byte): GPIO pin no to be used as trigger for out band reset
+        (0xFF: default pin configuration)
+      ir_mode (low byte) : independent reset mode
+        (0: disable, 1: enable out band, 2: enable in band)
+      For example, to enable out band reset via gpio_pin 14
+       indrstcfg=0x0e01
+      To enable out band reset via default gpio_pin
+       indrstcfg=0xff01
+      To enable in band reset and disable out band reset
+       indrstcfg=0x02
+
+	Note: On some platforms (e.g. PXA910/920) double quotation marks ("") need to used
+	for module parameters.
+		insmod sd8xxx.ko "<para1> <para2> ..."
+
+3) FOR DRIVER PROC & DEBUG
+
+	The following info are provided in /proc/net/mwlan/mlanX/info,
+	on kernel 2.6.24 or later, the entry is /proc/mwlan/mlanX/info.
+
+	driver_name = "wlan"
+	driver_version = <chip id, firmware version and driver version>
+	interface_name = "mlanX"
+	bss_mode = "Ad-hoc" | "Managed" | "Auto" | "Unknown"
+	media_state = "Disconnected" | "Connected"
+	mac_address = <6-byte adapter MAC address>
+	multicase_count = <multicast address count>
+	essid = <current SSID>
+	bssid = <current BSSID>
+	channel = <current channel>
+	region_code = <current region code>
+	multicast_address[n] = <multicast address>
+	num_tx_bytes = <number of bytes sent to device>
+	num_rx_bytes = <number of bytes received from device and sent to kernel>
+	num_tx_pkts = <number of packets sent to device>
+	num_rx_pkts = <number of packets received from device and sent to kernel>
+	num_tx_pkts_dropped = <number of Tx packets dropped by driver>
+	num_rx_pkts_dropped = <number of Rx packets dropped by driver>
+	num_tx_pkts_err = <number of Tx packets failed to send to device>
+	num_rx_pkts_err = <number of Rx packets failed to receive from device>
+	carrier "on" | "off"
+	tx queue "stopped" | "started"
+
+	The following debug info are provided in /proc/net/mwlan/mlanX/debug,
+	on kernel 2.6.24 or later, the entry is /proc/mwlan/mlanX/debug.
+
+	drvdbg = <bit mask of driver debug message control>
+	wmm_ac_vo = <number of packets sent to device from WMM AcVo queue>
+	wmm_ac_vi = <number of packets sent to device from WMM AcVi queue>
+	wmm_ac_be = <number of packets sent to device from WMM AcBE queue>
+	wmm_ac_bk = <number of packets sent to device from WMM AcBK queue>
+	max_tx_buf_size = <maximum Tx buffer size>
+	tx_buf_size = <current Tx buffer size>
+	curr_tx_buf_size = <current Tx buffer size in FW>
+	ps_mode = <0/1, CAM mode/PS mode>
+	ps_state = <0/1/2/3, awake state/pre-sleep state/sleep-confirm state/sleep state>
+	is_deep_sleep = <0/1, not deep sleep state/deep sleep state>
+	wakeup_dev_req = <0/1, wakeup device not required/required>
+	wakeup_tries = <wakeup device count, cleared when device awake>
+	hs_configured = <0/1, host sleep not configured/configured>
+	hs_activated = <0/1, extended host sleep not activated/activated>
+	tx_pkts_queued = <number of Tx packets queued>
+	pps_uapsd_mode = <0/1, PPS/UAPSD mode disabled/enabled>
+	sleep_pd = <sleep period in milliseconds>
+	qos_cfg = <WMM QoS info>
+	tx_lock_flag = <0/1, Tx lock flag>
+	port_open = <0/1, port open flag>
+	scan_processing = <0/1, scan processing flag>
+	num_tx_timeout = <number of Tx timeout>
+	num_cmd_timeout = <number of timeout commands>
+	timeout_cmd_id = <command id of the last timeout command>
+	timeout_cmd_act = <command action of the last timeout command>
+	last_cmd_id = <command id of the last several commands sent to device>
+	last_cmd_act = <command action of the last several commands sent to device>
+	last_cmd_index = <0 based last command index>
+	last_cmd_resp_id = <command id of the last several command responses received from device>
+	last_cmd_resp_index = <0 based last command response index>
+	last_event = <event id of the last several events received from device>
+	last_event_index = <0 based last event index>
+	num_cmd_h2c_fail = <number of commands failed to send to device>
+	num_cmd_sleep_cfm_fail = <number of sleep confirm failed to send to device>
+	num_tx_h2c_fail = <number of data packets failed to send to device>
+	num_cmdevt_c2h_fail = <number of commands/events failed to receive from device>
+	num_rx_c2h_fail = <number of data packets failed to receive from device>
+	num_int_read_fail = <number of interrupt read failures>
+	last_int_status = <last interrupt status>
+	num_evt_deauth = <number of deauthenticated events received from device>
+	num_evt_disassoc = <number of disassociated events received from device>
+	num_evt_link_lost = <number of link lost events received from device>
+	num_cmd_deauth = <number of deauthenticate commands sent to device>
+	num_cmd_assoc_ok = <number of associate commands with success return>
+	num_cmd_assoc_fail = <number of associate commands with failure return>
+	cmd_sent = <0/1, send command resources available/sending command to device>
+	data_sent = <0/1, send data resources available/sending data to device>
+	mp_rd_bitmap = <SDIO multi-port read bitmap>
+	curr_rd_port = <SDIO multi-port current read port>
+	mp_wr_bitmap = <SDIO multi-port write bitmap>
+	curr_wr_port = <SDIO multi-port current write port>
+	cmd_resp_received = <0/1, no cmd response to process/response received and yet to process>
+	event_received = <0/1, no event to process/event received and yet to process>
+	ioctl_pending = <number of ioctl pending>
+	tx_pending = <number of Tx packet pending>
+	rx_pending = <number of Rx packet pending>
+	lock_count = <number of lock used>
+	malloc_count = <number of malloc done>
+	mbufalloc_count = <number of mlan_buffer allocated>
+	main_state = <current state of the main process>
+	sdiocmd53w = <SDIO Cmd53 write status>
+	sdiocmd53r = <SDIO Cmd52 read status>
+	hs_skip_count = <number of skipped suspends>
+	hs_force_count = <number of forced suspends>
+
+	Issue SDIO cmd52 read/write through proc.
+	Usage:
+		echo "sdcmd52rw=<func> <reg> [data]" > /proc/mwlan/config
+	where the parameters:
+		func: The function number to use (0-7)
+		reg:  The address of the register
+		data: The value to write, read if the value is absent
+		For SDIO MMC driver, only function 0 and WLAN function access is allowed.
+		And there is a limitation for function 0 write, only vendor specific CCCR
+		registers (0xf0 -0xff) are permiited.
+	Examples:
+		echo "sdcmd52rw= 0 4" > /proc/mwlan/config      # read func 0 address 4
+		cat /proc/mwlan/config                          # display the register value
+		echo "sdcmd52rw= 1 3 0xf" > /proc/mwlan/config  # write 0xf to func 1 address 3
+
+	Use dmesg or cat /var/log/debug to check driver debug messages.
+
+	Update /proc/sys/kernel/printk to change message log levels.
+	For example,
+	echo 6 > /proc/sys/kernel/printk    (messages with a higher priority than 6
+	                                     will be printed to the console)
+	echo 15 > /proc/sys/kernel/printk   (all messages will be printed to console)
+
+4) FOR IWPRIV COMMAND
+
+NAME
+	This manual describes the usage of private commands used in Marvell MLAN
+	Linux Driver.
+
+	To use parameters as hex format, a '0x' must precede it for the parameters to
+	be parsed properly.
+
+SYNOPSIS
+	iwpriv <mlanX> <command> [sub-command] ...
+
+	iwpriv mlanX version
+	iwpriv mlanX verext
+	iwpriv mlanX getsignal [m] [n]
+	iwpriv mlanX antcfg [m] [n]
+	iwpriv mlanX regioncode [n]
+	iwpriv mlanX cfpcode [m] [n]
+	iwpriv mlanX wwscfg [m]
+	iwpriv mlanX esuppmode
+	iwpriv mlanX passphrase <ssid/psk/passphrase>
+	iwpriv mlanX httxcfg [<m>] [<n>]
+	iwpriv mlanX htcapinfo [<m>] [<n>]
+	iwpriv mlanX addbapara <m> <n> <o> <p> <q>
+	iwpriv mlanX aggrpriotbl <n>
+	iwpriv mlanX addbareject <n>
+	iwpriv mlanX txbufcfg
+	iwpriv mlanX amsduaggrctrl <n>
+	iwpriv mlanX httxbfcap [cap]
+	iwpriv mlanX httxbfcfg "<action>[;GlobalData/tsData/interval/txPeerData/snrData]"
+	iwpriv mlanX mpactrl [tx_ena] [rx_ena] [tx_size] [rx_size] [tx_ports] [rx_ports]
+	iwpriv mlanX deepsleep [n] [m]
+	iwpriv mlanX hscfg [condition [[GPIO# [gap]]]]
+	iwpriv mlanX hssetpara condition [GPIO# [gap]]
+	iwpriv mlanX deauth [n]
+	iwpriv mlanX radioctrl
+	iwpriv mlanX reassoctrl [n]
+	iwpriv mlanX adhocaes
+	iwpriv mlanX bandcfg [l] [m] [n] [o]
+	iwpriv mlanX getlog
+	iwpriv mlanX 11dcfg
+	iwpriv mlanX 11dclrtbl
+	iwpriv mlanX wmmcfg [n]
+	iwpriv mlanX hotspotcfg [n]
+	iwpriv mlanX txpowercfg [<RateIndex> [<MinPwr> [<MaxPwr> <step>]]]
+	iwpriv mlanX qoscfg
+	iwpriv mlanX getdatarate
+	iwpriv mlanX txratecfg [n]
+	iwpriv mlanX bcninterval [n]
+	iwpriv mlanX sysclock [clk1] [clk2] [clk3] [clk4]
+	iwpriv mlanX drvdbg [n]
+	iwpriv mlanX mgmtframectrl
+	iwpriv mlanX warmreset
+	iwpriv mlanX regrdwr <type> <offset> [value]
+	iwpriv mlanX rdeeprom <offset> <length>
+	iwpriv mlanX memrdwr <address> [value]
+	iwpriv mlanX inactivityto <n> <m> <l> [k]
+	iwpriv mlanX sdioclock <n>
+	iwpriv mlanX sdcmd52rw <FN no.> <address> [data]
+	iwpriv mlanX scancfg [t] [m] [p] [s] [a] [b] [ext]
+	iwpriv mlanX sleeppd [n]
+	iwpriv mlanX pscfg [k] [d] [l] ...
+        iwpriv mlanX fwwakeupmethod [n] [g]
+	iwpriv mlanX getkey
+	iwpriv mlanX associate "<bssid> <ssid>"
+	iwpriv mlanX sleepparams [<p1> <p2> <p3> <p4> <p5> <p6>]
+	iwpriv mlanX netmon [<act> [<filter> <band> <chan> [offset]]]
+	iwpriv mlanX authtype [n]
+	iwpriv mlanX powercons [n]
+	iwpriv mlanX htstreamcfg [n]
+	iwpriv mlanX ipaddr ["<op>;<ipaddr>"]
+	iwpriv mlanX macctrl [n]
+	iwpriv mlanX dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>]
+	iwpriv mlanX thermal
+    iwpriv mlanX indrstcfg <ir_mode> [gpio_pin]
+
+DESCRIPTION
+	Those commands are used to send additional commands to the Marvell MLAN
+	card via the Linux device driver.
+
+	The mlanX parameter specifies the network device that is to be used to
+	perform this command on. It could be mlan0, mlan1 etc.
+
+version
+	This is used to get the current version of the driver and the firmware.
+
+verext
+	Retrieve and display an extended version string from the firmware
+
+	Usage:
+		iwpriv mlanX verext [#]
+
+	where [#] is an optional argument to retrieve a specific version string,
+	omission of the argument retrieves the 0 indexed string.
+
+getsignal
+	This command gets the last and average value of RSSI, SNR and NF of
+	Beacon and Data.
+	Note: This command is available only when STA is connected.
+
+	where value of m is:
+		1   -- RSSI (Receive Signal Strength Indication)
+		2   -- SNR (Signal to Noise Ratio)
+		3   -- NF (Noise Floor)
+	where value of n is:
+		1   -- Beacon last
+		2   -- Beacon average
+		3   -- Data last
+		4   -- Data average
+
+	Examples:
+		iwpriv mlan0 getsignal 1        : Get the RSSI info (beacon last, beacon
+		                                  average, data last and data average)
+		iwpriv mlan0 getsignal 3 4      : Get the NF of data average
+		iwpriv mlan0 getsignal 2 1      : Get the SNR of beacon last
+		iwpriv mlan0 getsignal          : Get all of the signal info
+		mlan0     getsignal:-32  -33  -35  -36  67  59  63  56  -99  -92  -98  -92
+		RSSI info: beacon last -32, beacon average -33, data last -35, data average -36
+		SNR info: beacon last 67, beacon average 59, data last 63, data average 56
+		NF info: beacon last -99, beacon average -92, data last -98, data average -92
+
+antcfg
+	This command is used to set/get the mode of Tx/Rx path.
+
+	where value of m is:
+		Bit 0   -- Tx Path A
+		Bit 1   -- Tx Path B
+		Bit 0-1 -- Tx Path A+B
+
+	where value of n is:
+		Bit 0   -- Rx Path A
+		Bit 1   -- Rx Path B
+		Bit 0-1 -- Rx Path A+B
+	The Tx path setting (m) is used if Rx path (n) is not provided.
+
+	Examples:
+		iwpriv mlan0 antcfg             : Get Tx and Rx path
+		iwpriv mlan0 antcfg 3           : Set Tx and Rx path to A+B
+		iwpriv mlan0 antcfg 1 3         : Set Tx path to A and Rx path to A+B
+
+regioncode
+	This command is used to set/get the region code in the station.
+	Note: This command should be issued at beginning before band/channel selection
+	and association.
+
+	where value is 'region code' for various regions like
+	USA FCC, Canada IC, Europe ETSI, Japan ...
+	The special code (0xff) is used for Japan to support channel 1-14 in B/G/N mode.
+
+	Examples:
+		iwpriv mlan0 regioncode         : Get region code
+		iwpriv mlan0 regioncode 0x10    : Set region code to USA (0x10)
+
+	Note : in some case regioncode will be 0 after updated countycode or 80211d
+		i.e. mlanutl mlanX countrycode  (CA, JP, CN, DE, ES AT, BR, RU)
+		or uaputl.exe sys_cfg_80211d state 1 country (CA, JP, CN, DE, ES AT, BR, RU)
+		Please use cfp instead of it.
+
+cfpcode
+	This command is used to set/get the Channel-Frequency-Power table codes.
+	The region table can be selected through region code.
+	The current configuration is returned if no parameter provided.
+
+	where the parameters are,
+		[m]: code of the CFP table for 2.4GHz (0: unchanged)
+		[n]: code of the CFP table for 5GHz (0 or not provided: unchanged)
+
+	Examples:
+		iwpriv mlan0 cfpcode            : Get current configuration
+		iwpriv mlan0 cfpcode 0x30       : Set 2.4GHz CFP table code 0x30 (EU),
+		                                  keep 5GHz table unchanged
+		iwpriv mlan0 cfpcode 0x10 5     : Set 2.4GHz CFP table code 0x10 (USA)
+		                                  and 5GHz table code 5
+
+wwscfg
+	This command is used to set/get the WWS (World Wide Safe) mode.
+
+	where value of m is:
+		0       -- Disable WWS mode (default)
+		1       -- Enable WWS mode
+
+	Examples:
+		iwpriv mlan0 wwscfg             : Get WWS mode
+		iwpriv mlan0 wwscfg 1           : Enable WWS mode
+		iwpriv mlan0 wwscfg 0           : Disable WWS mode
+
+esuppmode
+	This command is used to get the current RSN mode and active pairwise/group
+    cipher for WPA/WPA2 mode.
+	Note: This command is available only when STA is connected.
+
+	These are bits settings used to indicate each RSN mode.
+		Bit 0    : No RSN
+		Bit 1-2  : RFU
+		Bit 3    : WPA
+		Bit 4    : WPA-NONE
+		Bit 5    : WPA2
+		Bit 6    : AES
+		Bit 7-15 : RFU
+
+	These are bits settings used to indicate each pairwise and group cipher.
+		Bit 0    : RFU
+		Bit 1    : RFU
+		Bit 2    : TKIP
+		Bit 3    : AES
+		Bit 2-7  : RFU
+
+	Example:
+		iwpriv mlan0 esuppmode          : Get RSN mode and pairwise/group cipher
+		8 4 4
+		(The current RSN mode is WPA, active pairwise cipher is TKIP and
+		 active group cipher is TKIP.)
+
+passphrase
+	This command is used to set/get passphrase for WPA-PSK/WPA2-PSK mode.
+
+	Where <n>
+		ASCII string for ssid/passphrase/psk.
+
+	1) "0;<ssid=valid ssid>" - This will get the passphrase, AKMP
+	   for specified ssid, if none specified then it will get all.
+
+	Example:
+		iwpriv mlan0 passphrase "0;ssid=marvell"
+
+	2) "1;<psk=64 byte hexpsk>;<passphrase=1-63 byte passphare>
+	   <ssid=valid ssid>" - Passphrase and psk cannot be provided for the same SSID.
+	   This command takes only one SSID at a time, If ssid= is present it should contain
+	   a passphrase or psk. If no arguments are provided then AKMP=802.1x, and passphrase
+	   should be provided after association.
+	   End of each parameter should be followed by a ';'(except for the last parameter)
+	   as the delimiter. If ';' or '/' has to be used in an SSID then a '/' should be preceded
+	   to ';' or '/' as a escape.
+
+	Examples:
+		iwpriv mlan0 passphrase "1;ssid=mrvlAP;passphrase=abcdefgd"
+		iwpriv mlan0 passphrase "1;ssid=mrvl AP;psk=<64 bytes hexpsk>"
+
+		If user wants to input the ssid as "mrvl; AP" then command has to be
+		iwpriv mlan0 passphrase "1;ssid=mrvl/; AP;passphrase=abcdefgh"
+
+		If user wants to input the ssid as "//;" then command has to be
+		iwpriv mlan0 passphrase "1;ssid=/////;;passphrase=abcdefgh"
+
+	3) "2;<ssid=valid ssid>" - This will clear the passphrase
+	   for specified ssid, if none specified then it will clear all.
+
+	Examples:
+		iwpriv mlan0 passphrase "2;ssid=marvell"
+		iwpriv mlan0 passphrase "2"     : Clear all profiles and disable embedded supplicant
+
+httxcfg
+	This command is used to configure various 11n specific configuration
+	for transmit (such as Short GI, Channel BW and Green field support)
+
+	where <m> is <txcfg>
+	This is a bitmap and should be used as following
+		Bit 15-8: Reserved set to 0
+		Bit 7: STBC enable/disable
+		Bit 6: Short GI in 40 Mhz enable/disable
+		Bit 5: Short GI in 20 Mhz enable/disable
+		Bit 4: Green field enable/disable
+		Bit 3-2: Reserved set to 1
+		Bit 1: 20/40 Mhz enable disable.
+		Bit 0: LDPC enable/disable
+
+	When Bit 1 is set then firmware could transmit in 20Mhz or 40Mhz based
+	on rate adaptation. When this bit is reset then firmware will only
+	transmit in 20Mhz.
+
+	where <n> is <band>
+	<band> - This is the band info for <txcfg> settings.
+		0: Settings for both 2.4G and 5G bands
+		1: Settings for 2.4G band
+		2: Settings for 5G band
+
+	Examples:
+		iwpriv mlanX httxcfg
+		This will display HT Tx configuration.
+		If the configurations for 2.4G and 5G are different,
+		the first value is for 2.4G and the second value is for 5G.
+		Otherwise, it will display a single value for both bands.
+
+		iwpriv mlanX httxcfg 0x62
+		This will enable 20/40 and Short GI but will disable Green field for 2.4G and 5G band.
+
+		iwpriv mlanX httxcfg 0x30 1
+		This will enable Short GI 20 Mhz and Green field for 2.4G band.
+
+	The default value is 0x20 for 2.4G and 0x62 for 5G.
+
+	Note:- If 20/40 MHz support is disabled in htcapinfo, device will not transmit
+	in 40 MHz even 20/40 MHz is enabled in httxcfg.
+
+htcapinfo
+	This command is used to configure some of parameters in HTCapInfo IE
+	(such as Short GI, Channel BW, and Green field support)
+
+	where <m> is <capinfo>
+	<capinfo> - This is a bitmap and should be used as following
+		Bit 29: Green field enable/disable
+		Bit 26: Rx STBC Support enable/disable. (As we support
+			single spatial stream only 1 bit is used for Rx STBC)
+		Bit 24: Short GI in 40 Mhz enable/disable
+		Bit 23: Short GI in 20 Mhz enable/disable
+		Bit 17: 20/40 Mhz enable disable.
+		Bit  8: Enable/disable 40Mhz Intolarent bit in ht capinfo.
+		        0 will reset this bit and 1 will set this bit in
+		        htcapinfo attached in assoc request.
+		All others are reserved and should be set to 0.
+
+	Setting of any other bits will return error.
+
+	where <n> is <band>
+	<band> - This is the band info for <capinfo> settings.
+		0: Settings for both 2.4G and 5G bands
+		1: Settings for 2.4G band
+		2: Settings for 5G band
+
+	Examples:
+		iwpriv mlanX htcapinfo
+		This will display HT capabilties information.
+		If the information for 2.4G and 5G is different,
+		the first value is for 2.4G and the second value is for 5G.
+		Otherwise, it will display a single value for both bands.
+
+		iwpriv mlanX htcapinfo 0x1820000
+		This will enable Short GI, Channel BW to 20/40 and disable Green field support for 2.4G and 5G band.
+
+		iwpriv mlanX htcapinfo 0x800000 2
+		This will enable Short GI, Channel BW to 20 only, No Rx STBC support and disable Green field support for 5G band.
+
+	The default value is 0x4800000 for 2.4G and 0x5820000 for 5G.
+
+	Note:- This command can be issued any time but it will only come to effect from
+	next association. (as HTCapInfo is sent only during Association).
+
+addbapara
+	This command can be used to update the default ADDBA parameters.
+
+	where <m> is <timeout>
+	<timeout> - This is the block ack timeout for ADDBA request.
+		0 : Disable (recommended for throughput test)
+		1 - 65535 : Block Ack Timeout in TU
+
+	where <n> is <txwinsize>
+	<txwinsize> - Window size for ADDBA request. (16 is recommended and default value)
+
+	where <o> is <rxwinsize>
+	<rxwinsize> - Window size for ADDBA response. (48 is recommended and 32 is default value)
+	              (16 is recommended for IWNCOMM AP in WAPI throughput test)
+
+	Current window size limit for Tx as well as Rx is 1023.
+
+	where <p> is <txamsdu>
+	<txamsdu> - amsdu support for ADDBA request. (1 is default value)
+            0: disable amsdu in ADDBA request
+            1: enable amsdu in ADDBA request
+
+	where <q> is <rxamsdu>
+	<rxamsdu> - amsdu support for ADDBA response. (1 is default value)
+            0: disable amsdu in ADDBA response
+            1: enable amsdu in ADDBA response
+
+	eg:
+	iwpriv mlanX addbapara - This command will get the current addba params
+	iwpriv mlanX addbapara 1000 64 8 0 0 - This will change the ADDBA timeout to (1000 * 1024) us,
+			txwinsize to 64 and rxwinsize to 8 and disable amdsu in ADDBA request/response.
+
+	The default setting is 65535 16 32 1 1.
+
+	In case the ADDBA timeout value is updated then a ADDBA is sent for all streams
+	to update the timeout value.
+
+	In case txwinsize and/or rxwinsize is updated, the effect could only be seen on
+	next ADDBA request/response. The current streams will not be affected with this
+	change.
+
+	In case of txamsdu/rxamsdu is updated, the effect could only be seen on
+	next ADDBA request/response. The current streams will not be affected with this
+	change. AMSDU in AMPDU stream will be enabled when AP support this feature
+	and AMSDU is enabled in aggrpriotbl.
+
+aggrpriotbl
+	This command is used set/get the priority table for AMPDU/AMSDU traffic per tid.
+	This command can also be used to disable AMPDU/AMSDU for a given tid.
+	In case of AMPDU this priority table will be used to setup block ack (to make
+	sure the highest priority tid always uses AMPDU as we have limited AMPDU streams)
+
+	where <m0> <n0> <m1> <n1> ... <m7> <n7>
+
+	<mx> - This is priority for Tid0 for AMPDU packet. A priority could be any
+		   values between 0 - 7, 0xff to disable aggregation.
+	<nx> - This is priority for Tid0 for AMSDU packet. A priority could be any
+		   values between 0 - 7, 0xff to disable aggregation.
+
+	eg:
+	iwpriv mlanX aggrpriotbl - This command will get the current Priority table for AMPDU and AMSDU.
+						  <2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255>. This is read as
+						  <"Prio for AMPDU for Tid0" "Prio for AMSDU for Tid0"
+						   "Prio for AMPDU for Tid1" "Prio for AMSDU for Tid1" and so on
+	iwpriv mlanX aggrpriotbl 2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255 -
+						This will set the priority table for AMPDU and AMSDU
+						Priority for Tid0/AMPDU = 2, Tid0/AMSDU = 2, Tid1/AMPDU = 0, Tid1/AMSDU = 0
+						and so on. Aggregation for Tid6 and Tid7 are disabled.
+						Here higher the priority number, higher the priority (i.e. 7
+						has higher priority than 6). Similarly for AMSDU.
+	iwpriv mlanX aggrpriotbl 0xff 2 0xff 0 0xff 1 0xff 3 0xff 4 0xff 5 0xff 0xff 0xff 0xff - This will disable
+						AMPDU for all the TIDs but will still keep AMSDU enabled to Tid0 to Tid5
+
+	The default setting is 2 255 0 255 1 255 3 255 4 255 5 255 255 255 255 255.
+
+	A delBA should be seen in case a disable happens on a TID for which AMPDU stream
+	is currently setup.
+
+	Note:- This command should only be issue in disconnected state.
+
+addbareject
+	This command is used set/get the addbareject table for all the TIDs.
+	This command can also be used to enable rejection of ADDBA requests for a given tid.
+
+	where <m0> <m1> ... <m7>
+
+	<mX> - This can be 0/1 for TidX. 1 enables rejection of ADDBA request for TidX and
+		   0 would accept any ADDBAs for TidX.
+
+	eg:
+	iwpriv mlanX addbareject - This command will get the current table.
+	    [0 0 0 0 0 0 0 0]. ADDBA would be accepted for all TIDs. This is the default state.
+
+	iwpriv mlanX addbareject 0 0 1 1 0 0 0 0 - This command will accept ADDBA requests for
+		Tid [0,1,4,5,6,7] and reject ADDBA requests for Tid [2,3]
+
+	iwpriv mlanX addbareject 1 1 1 1 1 1 1 1 - This will enable rejection of ADDBA requests for
+		all Tids.
+
+	Note:- This command should only be issue in disconnected state.
+
+txbufcfg
+	This command can be used to get current buffer size.
+
+	eg:
+	iwpriv mlanX txbufcfg 	  - This will display the current buffer size.
+
+	Note:- The actual tx buf size will depends on AP's capability and max transmit buffer size.
+
+amsduaggrctrl
+	This command could be used to enable/disable a feature where firmware gives feedback to driver
+	regarding the optimal AMSDU buffer size to use with the current rate. Firmware will use the
+	current rate to decide the buffer size we could transmit. The max buffer size will still be
+	limited by buffer size provided in txbufcfg. (i.e. if the txbufcfg is 4K, then we could only transmit
+	4K/2K AMSDU packets, if the txbufcfg is 8K then we could transmit 8k/4k/2k based on current rate)
+
+	If enabled AMSDU buffer size at various rates will be as follows
+
+	1.	Legacy B/G rate.
+		No AMSDU aggregation.
+
+	2.	BW20 HT Rate:
+		When TX rate goes down,
+		MCS 7, 6, 5, 4:
+			a	8K aggregation size (if TX buffer size is 8K)
+			b	4K aggregation size (if TX buffer size is 4K)
+			c	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 3, 2:
+			a	4K aggregation size (if TX buffer size is 8K/4K)
+			b	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 1, 0:
+			a	No aggregation
+
+		When TX rate goes up,
+		MCS 7, 6, 5:
+			a	8K aggregation size (if TX buffer size is 8K)
+			b	4K aggregation size (if TX buffer size is 4K)
+			c	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 4, 3:
+			a	4K aggregation size (if TX buffer size is 8K/4K)
+			b	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 2, 1, 0:
+			a	No aggregation
+
+	3.	BW40 HT Rate:
+		When TX rate goes down,
+		MCS 7, 6, 5, 4, 3, 2, 1:
+			a	8K aggregation size (if TX buffer size is 8K)
+			b	4K aggregation size (if TX buffer size is 4K)
+			c	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 0:
+			a	No aggregation
+
+		When TX rate goes up,
+		MCS 7, 6, 5, 4, 3:
+			a	8K aggregation size (if TX buffer size is 8K)
+			b	4K aggregation size (if TX buffer size is 4K)
+			c	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 2, 1, 0:
+			a	No aggregation
+
+	where <n> is 0/1 (for disable/enable)
+
+	eg:
+	iwpriv mlanx amsduaggrctrl 1 - Enable this feature
+	iwpriv mlanx amsduaggrctrl 0 - Disable this feature
+	iwpriv mlanx amsduaggrctrl - This will get the enable/disable flag
+	and the current AMSDU buffer size). The AMSDU buffer size returned is only
+	valid after association as before association there is no rate info.
+
+	Note:- This command to enable/disable could be given anytime (before/after
+			association). This feature is enabled by default by the driver during
+			initialization.
+
+httxbfcap
+	This command is used to set/get the TX beamforming capabilities.
+
+	Usage:
+		iwpriv mlanX httxbfcap [cap]
+
+	where the parameters are,
+		cap: TX beamforming capabilities
+			 Bit 0    : Implicit TX BF receiving capable
+			 Bit 1    : RX staggered sounding capable
+			 Bit 2    : TX staggered sounding capable
+			 Bit 3    : RX NDP capable
+			 Bit 4    : TX NDP capable
+			 Bit 5    : Implicit TX BF capable
+			 Bit 6-7  : Calibration
+			         0: - not supported
+			         1: - STA can respond to a calibration request using
+			              the CSI Report, but cannot initiate calibration
+			         2: - reserved
+			         3: - STA can both initiate and respond to a calibration request
+			 Bit 8    : Explicit CSI TX BF capable
+			 Bit 9    : Explicit non-compressed steering capable
+			 Bit 10   : Explicit compressed steering capable
+			 Bit 11-12: Explicit TX BF CSI feedback
+			         0: - not supported
+			         1: - delayed feedback
+			         2: - immediate feedback
+			         3: - delayed and immediate feedback
+			 Bit 13-14: Explicit non-compressed BF feedback capable
+			         0: - not supported
+			         1: - delayed feedback
+			         2: - immediate feedback
+			         3: - delayed and immediate feedback
+			 Bit 15-16: Explicit compressed BF feedback capable
+			         0: - not supported
+			         1: - delayed feedback
+			         2: - immediate feedback
+			         3: - delayed and immediate feedback
+			 Bit 17-18: Minimal grouping
+			         0: - no grouping (STA supports groups of 1)
+			         1: - groups of 1, 2
+			         2: - groups of 1, 4
+			         3: - groups of 1, 2, 4
+			 Bit 19-20: CSI number of beamformer antennas supported
+			         0: - single TX antenna sounding
+			         1: - 2 TX antenna sounding
+			         2: - 3 TX antenna sounding
+			         3: - 4 TX antenna sounding
+			 Bit 21-22: Non-compressed steering number of beamformer antennas supported
+			         0: - single TX antenna sounding
+			         1: - 2 TX antenna sounding
+			         2: - 3 TX antenna sounding
+			         3: - 4 TX antenna sounding
+			 Bit 23-24: Compressed steering number of beamformer antennas supported
+			         0: - single TX antenna sounding
+			         1: - 2 TX antenna sounding
+			         2: - 3 TX antenna sounding
+			         3: - 4 TX antenna sounding
+			 Bit 25-26: CSI max number of rows beamformer supported
+			         0: - single row of CSI
+			         1: - 2 rows of CSI
+			         2: - 3 rows of CSI
+			         3: - 4 rows of CSI
+			 Bit 27-28: Channel estimation capability
+			         0: - 1 space time stream
+			         1: - 2 space time streams
+			         2: - 3 space time streams
+			         3: - 4 space time streams
+			 Bit 29-31: Reserved
+
+	Examples:
+		iwpriv mlan0 httxbfcap             : Get the current TX BF capabilities
+		iwpriv mlan0 httxbfcap 0x0000001F  : Set the TX BF capabilities of the
+		                                     Implicit TX BF receiving capable,
+		                                     RX staggered sounding capable,
+		                                     TX staggered sounding capable,
+		                                     RX NDP capable and TX NDP capable
+
+httxbfcfg
+	This command is used to configure the TX beamforming options.
+	Note: Any new subcommand should be inserted in the second
+		argument and each argument of the sub command should be
+		separated by semicolon. For global configuration, the
+		arguments should be separated by space.
+
+	Usage:
+		iwpriv mlanX httxbfcfg "<action>[;GlobalData/tsData/interval/txPeerData/snrData]"
+
+	where the parameters are,
+		action: TX beamforming action
+			0: Control global parameters for beamforming
+		        1: Performs NDP Sounding for PEER
+		        2: TX BF interval in milliseconds
+		        3: Enable/Disable beamforming/sounding for a particular peer
+		        4: TX BF SNR Threshold for peer
+		        .. <for new subcommand>
+		GlobalData: Global parameter arguments.
+		    It contains beamforming enable, sounding enable, FB type, snr_threshold
+		    sounding interval, Beamformig mode values seperated by space.
+		    Syntax:
+			iwpriv mlanX httxbfcfg <action>;<beamforming enable> <sounding enable> <FB type>
+			                       <snr_threshold>  <sounding interval> <Beamforming mode>
+		tsData: Trigger sounding for PEER specific arguments,
+		        it contains PEER MAC and status
+		interval: TX BF interval in milliseconds
+		txPeerData: Enable/Disable beamforming/sounding for the indicated peer,
+		          it contains PEER MAC, sounding, beamfoming options and FB type;
+		snrData: TX BF SNR Threshold for peer, it contains PEER MAC and SNR
+
+	Examples:
+		iwpriv mlan0 httxbfcfg "0"                          : Get current global configuration parameter
+		iwpriv mlan0 httxbfcfg "2;00:50:43:20:BF:64"        : Get the TX BF periodicity for a given peer
+		iwpriv mlan0 httxbfcfg "3"                          : Get the list of MAC addresses that have
+		                                                      beamforming and/or sounding enabled
+		iwpriv mlan0 httxbfcfg "4"                          : Get the list of PEER MAC, SNR tuples
+		                                                      programmed into the firmware.
+		iwpriv mlan0 httxbfcfg "0;0 0 3 10 500 5"           : Disable beamforming, sounding, set FB type
+		                                                      to 3, snr threshold to 10, sounding interval
+		                                                      to 500 ms and beamforming mode to 5
+		iwpriv mlan0 httxbfcfg "1;00:50:43:20:BF:64"        : Perform NDP Trigger sounding to peer
+		                                                      00:50:43:20:BF:64
+		iwpriv mlan0 httxbfcfg "2;00:50:43:20:BF:64;500"    : Set TX BF periodicity for peer 00:50:43:20:BF:64
+		                                                      to 500 milliseconds
+		iwpriv mlan0 httxbfcfg "3;00:50:43:20:BF:43;1;0;3"  : Enable beamforming, disable sounding and set
+		                                                      FB type to 3 for peer 00:50:43:20:BF:43
+		iwpriv mlan0 httxbfcfg "4;00:50:43:20:BF:24;43"     : Set TX BF SNR threshold to peer
+		                                                      00:50:43:20:BF:24 with SNR 43
+
+mgmtframectrl
+	This command is used to get/set mask for the management frames which needs to be forwarded to application layer.
+
+	Usage:
+		iwpriv mlanX mgmtframectrl [m]
+
+	where the parameter [m] is the bit mask of management frame reception.
+		Following are the bit definitions.
+		Bit 0 : Association Request
+		Bit 1 : Association Response
+		Bit 2 : Re-Association Request
+		Bit 3 : Re-Association Response
+		Bit 4 : Probe Request
+		Bit 5 : Probe Response
+		Bit 8 : Beacon Frames
+
+	Examples:
+		iwpriv mlan0 mgmtframectrl         : Get the current Mgmt Frame forwarding mask
+		iwpriv mlan0 mgmtframectrl 0x0020  : Bit 5 is set, Forward probe response
+											 frames to application layer
+
+mpactrl
+	This command is used to set/get the Tx, Rx SDIO aggregation parameters.
+	Note: The parameters can be set only in disconnected state.
+
+	Usage:
+		iwpriv mlanX mpactrl [tx_ena] [rx_ena] [tx_size] [rx_size] [tx_ports] [rx_ports]
+
+	where the parameter are:
+		[tx_ena]: Enable/disable (1/0) Tx MP-A
+		[rx_ena]: Enable/disable (1/0) Rx MP-A
+		[tx_size]: Size of Tx MP-A buffer
+		[rx_size]: Size of Rx MP-A buffer
+		[tx_ports]: Max ports (1-16) for Tx MP-A
+		[rx_ports]: Max ports (1-16) for Rx MP-A
+	default values are 1 1 16384 32768 16 16
+	The MP-A may be disabled by default at build time if the MMC driver byte mode patch
+	is not available in kernel.
+
+	Examples:
+		iwpriv mlan0 mpactrl       : Get MP aggregation parameters
+		iwpriv mlan0 mpactrl 0 0
+		                           : Disable MP aggregation for Tx, Rx respectively
+		iwpriv mlan0 mpactrl 1 1 8192 8192 8 8
+		                           : Enable MP aggregation for Tx, Rx
+		                           : Set Tx, Rx buffer size to 8192 bytes
+		                           : Set maximum Tx, Rx ports to 8
+
+deepsleep
+	This command is used to set/get auto deep sleep mode.
+
+	Usage:
+		iwpriv mlanX deepsleep [n] [m]
+
+	where the parameters are:
+		[n]: Enable/disable auto deep sleep mode (1/0)
+		[m]: Idle time in milliseconds after which firmware will put the device
+		     in deep sleep mode. Default value is 100 ms.
+
+	Examples:
+		iwpriv mlan0 deepsleep          : Display auto deep sleep mode
+		iwpriv mlan0 deepsleep 1        : Enable auto deep sleep mode, idle time unchanged
+		iwpriv mlan0 deepsleep 0        : Disable auto deep sleep mode
+		iwpriv mlan0 deepsleep 1 500    : Enable auto deep sleep mode with idle time 500 ms
+        Note:
+            Deepsleep must be disabled before changing idle time.
+
+hscfg
+	This command is used to configure the host sleep parameters.
+
+	Usage:
+		iwpriv mlanX hscfg [condition [[GPIO# [gap]]]]
+
+	This command takes one (condition), two (condition and GPIO#) or three
+	(condition, GPIO# and gap) parameters for set. If no parameter provided,
+	get is performed.
+
+	where Condition is:
+		bit 0 = 1   -- broadcast data
+		bit 1 = 1   -- unicast data
+		bit 2 = 1   -- mac event
+		bit 3 = 1   -- multicast data
+		bit 6 = 1  --  Wakeup when mgmt frame received.
+		bit 7 = 1  --  Reserved
+		bit 8 = 1  --  Disable non maskable data wakeup.
+
+	The host sleep mode will be canceled if condition is set to -1. The default is 0x7.
+
+	where GPIO is the pin number of GPIO used to wakeup the host. It could be any valid
+	GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO will be used instead).
+	The default is 0xff.
+
+	where Gap is the gap in milliseconds between wakeup signal and wakeup event or 0xff
+	for special setting (host acknowledge required) when GPIO is used to wakeup host.
+	The default is 200.
+
+	The host sleep set except for cancellation will be blocked if host sleep is
+	already activated.
+
+	Examples:
+		iwpriv mlan0 hscfg              : Get current host sleep mode
+		iwpriv mlan0 hscfg -1           : Cancel host sleep mode
+		iwpriv mlan0 hscfg 3            : Broadcast and unicast data
+		                                  Use GPIO and gap set previously
+		iwpriv mlan0 hscfg 2 3          : Unicast data
+		                                  Use GPIO 3 and gap set previously
+		iwpriv mlan0 hscfg 2 1 0xa0     : Unicast data
+		                                  Use GPIO 1 and gap 160 ms
+		iwpriv mlan0 hscfg 2 0xff       : Unicast data
+		                                  Use interface (e.g. SDIO)
+		                                  Use gap set previously
+		iwpriv mlan0 hscfg 4 3 0xff     : MAC event
+		                                  Use GPIO 3
+		                                  Special host sleep mode
+		iwpriv mlan0 hscfg 1 0xff 0xff  : Broadcast data
+		                                  Use interface (e.g. SDIO)
+		                                  Use gap 255ms
+
+hssetpara
+	This command is used to set host sleep parameters.
+
+	Usage:
+		iwpriv mlanX hssetpara Condition [GPIO# [gap]]
+
+	Note:
+	1) The usages of parameters are the same as "hscfg" command.
+	2) The parameters will be saved in the driver and be used when host suspends.
+
+deauth
+	This command is used to send a de-authentication to an arbitrary AP.
+	If [n] is omitted, the driver will deauth the associated AP.
+	If in ad-hoc mode this command is used to stop beacon transmission
+	from the station and go into idle state.
+
+	When <n> is supplied as a MAC address, the driver will deauth the
+	  specified AP.  If the AP address matches the driver's associated AP,
+	  the driver will disconnect. Otherwise, the driver remains connected.
+
+radioctrl
+	This command is used to turn on/off the radio.
+	Note: The radio can be disabled only in disconnected state.
+
+	where value of n is:
+		0   -- Disable
+		1   -- Enable
+
+	Examples:
+		iwpriv mlan0 radioctrl 1        : Turn the radio on
+		iwpriv mlan0 radioctrl          : Get radio status
+
+reassoctrl
+	This command is used to turn on/off re-association in driver.
+
+	Usage:
+		iwpriv mlanX reassoctrl [n]
+
+	Where value of n is:
+		0   -- Disable
+		1   -- Enable
+
+	Examples:
+		iwpriv mlan0 reassoctrl         : Get re-association status
+		iwpriv mlan0 reassoctrl 1       : Turn re-association on
+
+adhocaes
+	This command is used to set/get the AES key, when the station is in ad-hoc mode.
+	Note: This command is only available in disconnected state.
+
+	where value can be any 16 byte value.
+
+	Examples:
+		iwpriv mlan0 adhocaes           : Get ad-hoc aes key
+		iwpriv mlan0 adhocaes "1;12345678901234567890123456789012"
+		                                : Set ad-hoc aes key
+		iwpriv mlan0 adhocaes 2         : Clear ad-hoc aes key
+
+bandcfg
+	This command is used to set/get infra/ad-hoc band.
+	Note: This command is only available in disconnected state.
+
+	Usage:
+		iwpriv mlanX bandcfg [l] [m] [n] [o]
+
+	where the parameters:
+		[l]: Infrastructure band
+		     bit 0: B
+		     bit 1: G
+		     bit 2: A
+		     bit 3: GN
+		     bit 4: AN
+
+			 bit 5: AC 2.4G
+			 bit 6: AC 5G
+		[m]: Ad-hoc start band
+		     bit 0: B
+		     bit 1: G
+		     bit 2: A
+		     bit 3: GN
+		     bit 4: AN
+			 bit 5: AC 2.4G
+			 bit 6: AC 5G
+		[n]: Ad-hoc start channel
+		[o]: 0 - Bandwidth 20Mhz
+		     1 - HT Bandwidth 40Mhz above
+		     3 - HT Bandwidth 40Mhz below
+			 4 - VHT Bandwidth 80Mhz
+	Examples:
+		iwpriv mlan0 bandcfg            : Get infra/ad-hoc band and ad-hoc
+		                                  start channel configurations
+		iwpriv mlan0 bandcfg 1          : Set infra band to B only
+		iwpriv mlan0 bandcfg 3 2 6      : Set infra band to B/G, ad-hoc start band
+		                                  to G and ad-hoc start channel to 6
+		iwpriv mlan0 bandcfg 7 11 6 1   : Set infra band to B/G/A, ad-hoc start band
+		                                  to B/G/GN, ad-hoc start channel to 6 and
+		                                  secondary channel to above
+
+getlog
+	This command is used to get the statistics available in the station.
+
+11dcfg
+	This command is used to control 11D. No argument is used to get.
+
+	where value of n is:
+		0   -- Disable
+		1   -- Enable
+
+	Examples:
+		iwpriv mlan0 11dcfg 1           : Enable 11D
+		iwpriv mlan0 11dcfg             : Get 11D status
+
+11dclrtbl
+	This command is used to clear the 11D channel table.
+
+	Usage:
+		iwpriv mlanX 11dclrtbl
+
+wmmcfg
+	This command is used to control WMM. No argument is used to get.
+
+	where value of n is:
+		0   -- Disable
+		1   -- Enable
+
+	Examples:
+		iwpriv mlan0 wmmcfg 1           : Enable WMM
+		iwpriv mlan0 wmmcfg             : Get WMM status
+
+hotspotcfg
+	This command is used to get/set the HotSpot configuration.
+
+	WHere
+		<n> - Configuration bitset
+	This is a bitmap and should be used as following
+		Bit 31-10: Reserved set to 0
+		Bit 9: TDLS support indication enable/disable
+		Bit 8: Interworking indication enable/disable
+		Bit 7-1: Reserved set to 0
+		Bit 0: HotSpot feature enable/disable
+
+txpowercfg
+	This command is used to get/set the Tx power configuration.
+
+	Where
+		<RateIndex> - Data rate index
+			0	1 Mbps
+			1	2 Mbps
+			2	5.5 Mbps
+			3	11 Mbps
+			4	6 Mbps
+			5	9 Mbps
+			6	12 Mbps
+			7	18 Mbps
+			8	24 Mbps
+			9	36 Mbps
+			10	48 Mbps
+			11	54 Mbps
+			12	MCS0 (BW20)
+			13	MCS1 (BW20)
+			14	MCS2 (BW20)
+			15	MCS3 (BW20)
+			16	MCS4 (BW20)
+			17	MCS5 (BW20)
+			18	MCS6 (BW20)
+			19	MCS7 (BW20)
+			20	MCS8 (BW20)
+			21	MCS9 (BW20)
+			22	MCS10 (BW20)
+			23	MCS11 (BW20)
+			24	MCS12 (BW20)
+			25	MCS13 (BW20)
+			26	MCS14 (BW20)
+			27	MCS15 (BW20)
+			140	MCS0 (BW40)
+			141	MCS1 (BW40)
+			142	MCS2 (BW40)
+			143	MCS3 (BW40)
+			144	MCS4 (BW40)
+			145	MCS5 (BW40)
+			146	MCS6 (BW40)
+			147	MCS7 (BW40)
+  			148	MCS0 (BW40)
+			149	MCS1 (BW40)
+			150	MCS2 (BW40)
+			151	MCS3 (BW40)
+			152	MCS4 (BW40)
+			153	MCS5 (BW40)
+			154	MCS6 (BW40)
+			155	MCS7 (BW40)
+			0xff	Default
+		<MinPwr> - Minimum power level in dBm
+		<MaxPwr> - Maximum power level in dBm
+		<step>   - Power step
+
+	Note: Firmware may adjust the setting if over limit, final value can be
+	      verified using get command.
+
+	Examples:
+		iwpriv mlan0 txpowercfg 0xff        : Default power configuration
+		iwpriv mlan0 txpowercfg 11 12       : Set power level 12 dBm to data rate 54 Mbps
+		iwpriv mlan0 txpowercfg 7 11 16 1   : Set power level 11 dBm to 16 dBm with
+		                                      step 1 to data rate 18 Mbps
+		iwpriv mlan0 txpowercfg             : Get current configuration
+		mlan0     txpowercfg:2  3  13  18  2  1  1  13  18  2  0  0  13  18  2
+		10  11  13  15  2  8  9  13  16  2  6  7  13  17  2  4  5  13  17  2
+		17  19  13  15  2  15  16  13  16  2  13  14  13  17  2  12  12  13  17  2
+		145  147  13  14  1  143  144  13  14  1  141  142  13  14  1  140  140  13  14  1
+
+		 2 -> First rate index is 5.5 Mbps.
+		 3 -> Last rate index is 11 Mbps.
+		13 -> Min Tx power value is 13 dBm.
+		18 -> Max Tx power value is 18 dBm.
+		 2 -> Power adjustment step value is 2.
+
+		Similarly
+		17 -> First rate index is MCS5 (BW20).
+		19 -> Last rate index is MCS7 (BW20).
+		13 -> Min Tx power value is 13 dBm.
+		15 -> Max Tx power value is 15 dBm.
+		 2 -> Power adjustment step value is 2.
+
+		so on...
+
+qoscfg
+	This command sets WMM IE QOS info when an argument is given, and gets current WMM
+	IE QOS info when no argument is given.
+
+	Examples:
+		iwpriv mlan0 qoscfg 0x0f        : Set WMM IE QOS info to 0x0f
+		iwpriv mlan0 qoscfg             : Get WMM IE QOS info
+
+getdatarate
+	This command is used to get the data rate (index) being used in last Tx
+	packet and last Rx packet.
+
+bcninterval
+	This command is used to set/get the beacon interval in ad-hoc mode.
+	The valid beacon interval is between 20 - 1000, default beacon
+	interval is 100.
+
+	Where <n>
+		Beacon interval in TU (Time Unit: 1024 us).
+
+	Examples:
+		iwpriv mlan0 bcninterval 200    : Set ad-hoc beacon interval to 200
+		iwpriv mlan0 bcninterval        : Get ad-hoc beacon interval
+
+sysclock
+	This command is used to set/get system clocks in MHz.
+	The current system clock, configurable system clocks and all of the
+	supported system clocks will be returned if no parameter provided.
+
+	Examples:
+		iwpriv mlan0 sysclock           : Get system clocks
+		80 80 128 128 128 5 11 16 20 22 32 40 44 64 80 106 128 160 ...
+		(The current system clock is 80 MHz.
+		 The configurable system clocks of non-security, security, non-security
+		 A-MPDU and security A-MPDU are 80 MHz, 128 MHz, 128 MHz and 128 MHz.
+		 The supported system clocks are 5 MHz, 11 MHz, ..., 160 MHz, 182 MHz,
+		 213 MHz, 256 MHz, 320 Mhz, 366 MHz , ... . the Max system clocks is different
+		 for different chips, you could use this command to get the supported system clock)
+
+		iwpriv mlanX sysclock 80        : Set system clock in non-security mode
+		                                  to 80 MHz, no change for others
+		iwpriv mlanX sysclock 0 0 128   : Set system clock in non-security A-MPDU
+		                                  mode to 128 MHz, no changes for others
+
+drvdbg
+	This command is used to set/get the bit masks of driver debug message control.
+
+	Usage:
+		iwpriv mlanX drvdbg [n]
+
+	Where the parameter <n> is the generic debug message control bit mask.
+	The following types of driver debug messages can be dynamically enabled or
+	disabled by setting or clearing the corresponding bits,
+		bit 0:  MMSG  		PRINTM(MMSG,...)
+		bit 1:  MFATAL		PRINTM(MFATAL,...)
+		bit 2:  MERROR		PRINTM(MERROR,...)
+		bit 3:  MDATA 		PRINTM(MDATA,...)
+		bit 4:  MCMND 		PRINTM(MCMND,...)
+		bit 5:  MEVENT		PRINTM(MEVENT,...)
+		bit 6:  MINTR 		PRINTM(MINTR,...)
+		bit 7:  MIOCTL 		PRINTM(MIOCTL,...)
+		...
+		bit 16: MDAT_D		PRINTM(MDAT_D,...), DBG_HEXDUMP(MDAT_D,...)
+		bit 17: MCMD_D		PRINTM(MCMD_D,...), DBG_HEXDUMP(MCMD_D,...)
+		bit 18: MEVT_D		PRINTM(MEVT_D,...), DBG_HEXDUMP(MEVT_D,...)
+		bit 19: MFW_D		PRINTM(MFW_D,...),  DBG_HEXDUMP(MFW_D,...)
+		bit 20: MIF_D 		PRINTM(MIF_D,...),  DBG_HEXDUMP(MIF_D,...)
+		...
+		bit 28: MENTRY		PRINTM(MENTRY,...), ENTER(), LEAVE()
+		bit 29: MWARN 		PRINTM(MWARN,...)
+		bit 30: MINFO 		PRINTM(MINFO,...)
+
+	If CONFIG_DEBUG=2, all kinds of debug messages can be configured.
+
+	If CONFIG_DEBUG=1, all kinds of debug messages can be configured except
+	for MENTRY, MWARN and MINFO. By default MMSG, MFATAL and MERROR are enabled.
+
+	Some special debug messages,
+		'*'		// MLAN driver ISR is called (bit 6 MINTR enabled)
+		'|'		// PS awake event is received (bit 5 MEVENT enabled)
+		'_'		// PS sleep event is received (bit 5 MEVENT enabled)
+		'+'		// PS sleep confirm is sent (bit 5 MEVENT enabled)
+
+	Examples:
+		iwpriv mlan0 drvdbg             : Get the current driver debug masks
+		iwpriv mlan0 drvdbg 0           : Disable all the debug messages
+		iwpriv mlan0 drvdbg 7           : Enable MMSG, MFATAL and MERROR messages
+		iwpriv mlan0 drvdbg 0x20037     : Enable MMSG, MFATAL, MEEROR,
+		                                  MCMND, MEVENT and MCMD_D messages
+		iwpriv mlan0 drvdbg -1          : Enable all the debug messages
+
+warmreset
+	This command is used for warm reset of the interface.
+
+	Usage:
+		iwpriv mlanX warmreset
+
+regrdwr
+	This command is used to read/write the adapter register.
+
+	Usage:
+		iwpriv mlanX regrdwr <type> <offset> [value]
+
+	where the parameters are,
+		<type>:     1:MAC/SOC, 2:BBP, 3:RF, 5:CAU
+		<offset>:   offset of register
+		[value]:    value to be written
+	Note: If highest bit of a 32-bit value needs to be set, use negative input.
+	The calculation is -(0xffffffff - value + 1). Range from -0x80000000 to -1.
+
+	Examples:
+		iwpriv mlan0 regrdwr 1 0xa060       : Read the MAC register
+		iwpriv mlan0 regrdwr 1 0xa060 0x12  : Write the MAC register
+		iwpriv mlan0 regrdwr 1 0xa794 -0x80000000
+		                                    : Write 0x80000000 to MAC register
+		iwpriv mlan0 regrdwr 1 0xa794 -0x00000001
+		                                    : Write 0xffffffff to MAC register
+
+rdeeprom
+	This command is used to read the EEPROM contents of the card.
+
+	Usage:
+		iwpriv mlanX rdeeprom <offset> <length>
+
+	where the parameters are,
+		<offset>:   multiples of 4
+		<length>:   4-20, multiples of 4
+
+	Example:
+		iwpriv mlan0 rdeeprom 0 20      : Read 20 bytes of EEPROM data from offset 0
+
+memrdwr
+	This command is used to read/write the adapter memory.
+
+	Usage:
+		iwpriv mlanX memrdwr <address> [value]
+
+	where the parameters are,
+		<address>:  memory address
+		[value]:    value to be written
+	Note: If highest bit of a 32-bit address/value needs to be set, use negitive input.
+	The calculation is -(0xffffffff - address/value + 1). Range from -0x80000000 to -1.
+
+	Examples:
+		iwpriv mlan0 memrdwr 0x4cf70    : Read memory address 0x4cf70
+		iwpriv mlan0 memrdwr -0x80000000
+		                                : Read memory address 0x80000000
+		iwpriv mlan0 memrdwr -0x7fff6000 -0x40000000
+		                                : Write 0xc0000000 to memory address 0x8000a000
+
+inactivityto
+	This command is used to set/get the inactivity timeout value, which specifies
+	when WLAN device is put to sleep.
+
+	Usage:
+		iwpriv mlanX inactivityto <n> <m> <l> [k]
+
+	where the parameter are:
+		<n>: timeout unit in microseconds.
+		<m>: Inactivity timeout for unicast data.
+		<l>: Inactivity timeout for multicast data.
+		[k]: Inactivity timeout for new Rx traffic after PS notification to AP.
+
+	Examples:
+		iwpriv mlan0 inactivityto           : Get the timeout value
+		iwpriv mlan0 inactivityto 1000 2 3  : Set timeout unit to 1000 us (1 ms),
+		                                      inactivity timeout for unicast data is 2 ms,
+		                                      inactivity timeout for multicast data is 3 ms
+
+sdioclock
+	Turn On(1) or Off(0) the SDIO clock.
+
+	Usage:
+		iwpriv mlanX sdioclock 1 (on)
+		iwpriv mlanX sdioclock 0 (off)
+		iwpriv mlanX sdioclock (get the current clock state)
+
+sdcmd52rw
+	This command is used to read/write a controller register in
+	Secure Digital I/O Interfaces.
+
+	Usage:
+		iwpriv mlanX sdcmd52rw <function number> <register address> [value]
+
+	For SDIO MMC driver, only function 0 and 1 access is allowed. And there
+	is a limitation for function 0 write, only vendor specific CCCR registers
+	(0xf0 -0xff) are permiited.
+
+	Examples:
+		iwpriv mlan0 sdcmd52rw 1 3
+		iwpriv mlan0 sdcmd52rw 1 1 0x3f
+
+scancfg
+	This command is used to set/get scan configuration parameters.
+
+	Usage:
+		iwpriv mlanX scancfg [t] [m] [p] [s] [a] [b] [ext]
+
+	where the parameters:
+		[t]: Scan Type (0: Unchanged, 1: Active, 2: Passive, default Active)
+		[m]: Scan Mode (0: Unchanged, 1: BSS, 2: IBSS, 3: Any, default Any)
+		[p]: Scan Probes (0: Unchanged, 1-4: Number of probes per channel, default 4)
+		[s]: Specific Scan Time (0: Unchanged, n: Value in ms, default 110 ms, max 500 ms)
+		[a]: Active Scan Time (0: Unchanged, n: Value in ms, default 200 ms, max 500 ms)
+		[b]: Passive Scan Time (0: Unchanged, n: Value in ms, default 200 ms, max 2000 ms)
+		[ext]: Extended scan (0: Legacy scan, 1: Extended scan)
+
+	No change if the parameter is 0 or the parameter is not provided.
+
+	Examples:
+		iwpriv mlan0 scancfg            : Get all the current scan configuration settings
+		iwpriv mlan0 scancfg 1 3        : Set scan type to active and scan mode to any,
+		                                  all the other scan configurations are unchanged
+		iwpriv mlan0 scancfg 0 1 2 200  : Set scan mode to BSS, number of probes to 2 and
+		                                  specific scan time to 200 ms, all the other scan
+		                                  configurations are unchanged
+
+sleeppd
+	This command is used to configure the sleep period of the WLAN device.
+
+	Usage:
+		iwpriv mlanX sleeppd [<period>]
+
+	Where the parameter is:
+		period: sleep period in milliseconds. Range 10~60. 0 for disable.
+
+	Examples:
+		iwpriv mlan0 sleeppd            : Get sleep period configuration
+		iwpriv mlan0 sleeppd 10         : Set sleep period to 10 ms
+
+pscfg
+	This command is used to set/get PS configuration parameters.
+
+	Usage:
+		iwpriv mlanX pscfg [k] [d] [l] ...
+
+	Where the parameters:
+		[k]: Keep alive null packet interval (0: Unchanged, -1: Disable, n: Interval in seconds)
+		[d]: DTIM interval (    0: Unchanged,
+		                      1-5: Value,
+		                    65534: DTIM will be ignored, listen interval will be used,
+		                    65533: Closest DTIM to the listen interval period will be used )
+		[l]: Local listen interval (     0: Unchanged,
+		                                -1: Disable,
+		                              1-49: Value in beacon intervals,
+		                             >= 50: Value in TUs )
+		[a]: Ad-hoc awake period (0: Unchanged, 1-31: Beacon interval, 255: Firmware
+		                          will go to sleep after beacon send out)
+		[b]: Beacon miss timeout (0: Unchanged, 1-50: Value in milliseconds, 65535: Disable)
+		[p]: Delay to PS (0-65535: Value in milliseconds, default 1000ms)
+		[m]: PS mode (0: Unchanged, 1: Auto mode, 2: PS-Poll mode, 3: PS Null mode)
+	No change if parameters are not provided.
+
+	Examples:
+		iwpriv mlan0 pscfg              : Get all the current PS configuration settings
+		iwpriv mlan0 pscfg 3 4          : Set PS keep alive null packet interval to 3 seconds
+		                                  and DTIM interval to 4, all the other configurations
+		                                  are unchanged
+		iwpriv mlan0 pscfg 0 0xfffe 10 0 20
+		                                : Disable DTIM interval, set local listen interval to
+		                                  10 beacon intervals and beacon miss interval to 20,
+		                                  all the other configurations are unchanged
+		iwpriv mlan0 pscfg 0 0 0 0 0 50 : Set delay to PS to 50 ms, keep the others unchanged
+
+fwwakeupmethod
+	This command is used to set/get the firmware wakeup method.
+
+	where value is:
+	        [n]:
+		1	-- Firmware wakeup through the interface command interrupt
+			    -- (default setting for SDIO/PCIe/USB)
+		2	-- Firmware wakeup through the GPIO pin
+		[g]:If firware wakeup throug GPIO pin, [g] is GPIO pin number
+
+	Examples:
+		iwpriv mlan0 fwwakeupmethod     : Get current firmware wakeup method
+		iwpriv mlan0 fwwakeupmethod 1    : Set firmware wakeup method to Interface mode
+		iwpriv mlan0 fwwakeupmethod 2 5  : Set firmware wakeup method to GPIO mode, GPIO_pin=5
+
+getkey
+	This command is used to get PTK/GTK
+
+	iwpriv mlanX getkey
+associate
+	Request an association to a given SSID/BSSID pair. This the only accurate
+	way to pick a specific AP and ESS for an association. The entry must
+	already exist in the scan table for the association to be attempted.
+
+	iwpriv mlanX associate "xx:xx:xx:xx:xx:xx SSID"
+
+sleepparams
+	This command is used to set the sleepclock configurations
+
+	Usage:
+		iwpriv mlanX sleepparams [<p1> <p2> <p3> <p4> <p5> <p6>]
+
+	where:
+		p1 is Sleep clock error in ppm (0-65535)
+		p2 is Wakeup offset in usec (0-65535)
+		p3 is Clock stabilization time in usec (0-65535)
+		p4 is Control periodic calibration (0-2)
+		p5 is Control the use of external sleep clock (0-2)
+		p6 is reserved for debug (0-65535)
+
+	Examples:
+		iwpriv mlan0 sleepparams                      : Get current sleepclock configuration
+		iwpriv mlan0 sleepparams 10 1000 2000 1 0 128 : Set sleepclock configuration
+
+netmon
+	This command is used to set/get sniffer mode configuration.
+	Note: The channel and band config is optional. If not specified, or if
+	any STA/uAP/STA+uAP connection is active, sniffer activity will be started
+	on the current config set in the FW.
+	'rtap' monitor interface will be created on enabling sniffer activity and
+	should be made 'up' for capturing in a sniffer app.
+
+	Usage:
+		iwpriv <interface> netmon [<act> [<filter>]]
+		iwpriv <interface> netmon [<act> [<filter>] [<band> <chan> [offset]]]
+
+	Where the parameters are:
+		<interface> : mlanX, uapX
+		<act>    : (1/0) enable/disable sniffer activity
+		<filter> : network monitor filer flag
+		    bit 0: (1/0) enable/disable management frame
+		    bit 1: (1/0) enable/disable control frame
+		    bit 2: (1/0) enable/disable data frame
+			bit 3: (1/0) enable/disable frames destined to active connection only
+			bit 4: (1/0) enable/disable decrypted unicast data/mgmt frames
+		<band>   : 802.11 band
+		    bit 0: B
+		    bit 1: G
+		    bit 2: A
+		    bit 3: GN
+		    bit 4: AN
+			 bit 5: AC 2.4G
+			 bit 6: AC 5G
+		<chan>   : channel to monitor
+		[offset] : secondary channel bandwidth
+			0 - Bandwidth 20Mhz
+			1 - HT Bandwidth 40Mhz sec channel above
+			3 - HT Bandwidth 40Mhz sec channel below
+			4 - VHT Bandwidth 80Mhz
+
+	Examples:
+		iwpriv mlan0 netmon             : Get the current sniffer mode configuration
+		iwpriv mlan0 netmon 0           : Disable network monitor activity
+		iwpriv uap0 netmon 1 7          : Enable sniffer activity on current channel set in FW,
+											set filter data, control, management frame.
+		iwpriv mlan0 netmon 1 4 11 6    : Enable sniffer activity in absence of active connection,
+											set filter data frame, band B/G/GN and channel 6
+		iwpriv mlan0 netmon 1 7 20 64 1 : Enable sniffer activity in absence of active connection,
+											set filter management, control and data frame, band A/AN,
+											channel 64 and secondary channel above
+		iwpriv uap0 netmon 1 0x0c        : Enable sniffer activity, set filter data frames
+											destined to the active uAP connection only
+
+		iwpriv mlan0 netmon 1 0x1d       : Enable sniffer activity, set filter decrypted data and
+											management frames destined to the active STA connection
+											only
+
+authtype
+	This command is used to set/get authentication type.
+
+	Usage:
+		iwpriv mlanX authtype [n]
+
+	where <n>
+		0: 802.11 open system authentication
+		1: 802.11 shared key authentication
+		255: allow open system or shared key authentication (default)
+
+	Examples:
+		iwpriv mlan0 authtype 0         : use open system authentication
+		iwpriv mlan0 authtype 1         : use shared key authentication
+		iwpriv mlan0 authtype 255       : allow open system or shared key authentication
+		iwpriv mlan0 authtype           : get current setting
+
+powercons
+	This command is used to set the local transmit power constraint.
+	Value is in dbm unit. This command is only used for ad-hoc start.
+
+	Usage:
+		iwpriv mlanX powercons [n]
+
+	Examples:
+		iwpriv mlanX powercons          : get the current setting
+		iwpriv mlanX powercons 12       : set local power constraint to 12 dbm
+
+htstreamcfg
+	This command is used to set/get HT stream configuration.
+	The setting only takes effect in next association.
+
+	Usage:
+		iwpriv mlanX htstreamcfg [n]
+
+	where <n>
+		0x11: HT stream 1x1 mode
+		0x22: HT stream 2x2 mode
+
+	Examples:
+		iwpriv mlan0 htstreamcfg        : Get current setting
+		iwpriv mlan0 htstreamcfg 0x11   : Set HT stream 1x1 mode
+		iwpriv mlan0 htstreamcfg 0x22   : Set HT stream 2x2 mode
+
+ipaddr
+	This command is used to set/get IP address.
+
+	Usage:
+		iwpriv mlanX ipaddr ["<op>;<ipaddr>"]
+
+	where <op>
+		0: Remove the IP address
+		bit 0: Set IP address for broadcast ARP filter, which will be auto enabled
+		       in next host sleep configuration
+		bit 1: Set IP address for auto broadcast ARP response
+
+	Examples:
+		iwpriv mlan0 ipaddr                 : Get current settings
+		iwpriv mlan0 ipaddr "0"             : Remove IP address
+		iwpriv mlan0 ipaddr "1;192.168.0.5" : Set IP address for ARP filter
+		iwpriv mlan0 ipaddr "3;192.168.0.6" : Set IP address for ARP filter
+		                                    : and auto ARP response
+
+macctrl
+	This command is used to set/get MAC control.
+	It's recommended to read the current setting first to avoid override issue.
+
+	Usage:
+		iwpriv mlanX macctrl [n]
+
+	where <n>
+		bit 0:  Rx enabled
+		bit 1:  Tx enabled
+		bit 3:  WEP enabled
+		bit 4:  EthernetII enabled
+		bit 7:  Promiscuous enabled
+		bit 8:  All multicast enabled
+		bit 9:  RTS/CTS enabled (0: CTS to self)
+		bit 11: Force 11n protection disabled
+		bit 12: Ad-hoc g protection disabled
+		...
+
+	Examples:
+		iwpriv mlan0 macctrl            : Get current MAC control
+		iwpriv mlan0 macctrl 0x13       : Set Tx/Rx on and EthernetII on
+		iwpriv mlan0 macctrl 0x813      : Set Tx/Rx on and EthernetII on
+		                                : Disable force 11n protection
+
+dfstesting
+	This command is used to set/get settings for DFS testing.
+
+	Usage:
+	    iwpriv mlanX dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>]
+
+	where <user_cac_pd> is user-configured Channel Availability Check in msec
+                        0 = disable, use default period (60000)
+                        1-65535 = enable with that period
+	where <user_nop_pd> is user-configured Non-Occupancy Period in sec
+                        0 = disable, use default period (1800)
+                        1-65535 = enable with that period
+	where <no_chan_change> is enable/disable no channel change on radar
+                           0 = disable, 1 = enable (overrides below)
+	where <fixed_chan_num> is user-configured channel to change to on radar
+                           0 = disable, 1-255 = enable with that channel
+                           (channel validity for region, etc. is not checked)
+                           (only takes effect if no_chan_change = 0)
+
+	Examples:
+		iwpriv mlan0 dfstesting             : Get current dfstesting settings
+		iwpriv mlan0 dfstesting 2000 0 0 0  : user_cac=2sec, others disabled/default
+		iwpriv mlan0 dfstesting 0 0 1 0     : only no_chan_change enabled
+		iwpriv mlan0 dfstesting 0 120 0 64  : user_nop=2min, force chan 64 on radar
+
+thermal
+	This command is used to get the current thermal reading.
+
+	Examples:
+		iwpriv mlan0 thermal            : Get thermal reading
+
+indrstcfg
+    This command is used to set/get settings for independent reset mode
+
+    Usage:
+        iwpriv mlanX indrstcfg <ir_mode> [gpio_pin]
+
+    where <ir_mode>  is independent reset mode
+                     0 = disable independent reset
+                     1 = enable out band and disable in band
+                     2 = enable in band and disable out band
+    where <gpio_pin> is user-configured gpio pin number to be used for oob mode
+                     0xFF = use FW default gpio assignment
+                     0-15 = gpio pin number
+
+    Examples:
+        iwpriv mlan0 indrstcfg              : Get current settings
+        iwpriv mlan0 indrstcfg 1 14         : Enable oob_mode with gpio pin 14
+        iwpriv mlan0 indrstcfg 1 0xff       : Enable oob_mode with default gpio pin
+        iwpriv mlan0 indrstcfg 0            : Disable ir_mode
+        iwpriv mlan0 indrstcfg 2            : Enable in band reset mode
+
+
+===============================================================================
+		U S E R  M A N U A L  F O R  M L A N C O N F I G
+
+NAME
+mlanconfig - configure the additional parameters available for the Marvell mdriver.
+
+SYNOPSIS
+mlanconfig -v
+mlanconfig <mlanX> <command> [parameters] ...
+
+mlanconfig mlanX hostcmd <bg_scan.conf> bgscfg
+mlanconfig mlanX hostcmd <requesttpc.conf> requesttpc
+mlanconfig mlanX hostcmd <crypto_test.conf> crypto_test
+mlanconfig mlanX hostcmd <subevent.conf> subevent_get
+mlanconfig mlanX hostcmd <subevent.conf> subevent_set
+mlanconfig mlanX hostcmd <auto_tx.conf> auto_tx_get
+mlanconfig mlanX hostcmd <auto_tx.conf> nat_keep_alive
+mlanconfig mlanX hostcmd <auto_tx.conf> auto_tx_unreg
+mlanconfig mlanX hostcmd <txrate_cfg.conf> txrate_cfg_get
+mlanconfig mlanX hostcmd <txrate_cfg.conf> txrate_cfg_set_bg
+mlanconfig mlanX hostcmd <txrate_cfg.conf> txrate_cfg_set_bgn
+mlanconfig mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_cfg_get
+mlanconfig mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_2g_cfg_set
+mlanconfig mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_5g_cfg_set
+mlanconfig mlanX hostcmd <pad_cfg.conf> pad_cfg_get
+mlanconfig mlanX hostcmd <pad_cfg.conf> pad_cfg_set
+mlanconfig mlanX hostcmd <11n_2040coex.conf> 2040coex
+mlanconfig mlanX hostcmd <robust_btc.conf> mode_get
+mlanconfig mlanX hostcmd <robust_btc.conf> mode_timeshare
+mlanconfig mlanX hostcmd <robust_btc.conf> mode_spatial
+mlanconfig mlanX hostcmd <robust_btc.conf> generictime
+mlanconfig mlanX hostcmd <robust_btc.conf> a2dptime
+mlanconfig mlanX hostcmd <robust_btc.conf> inquirytim
+mlanconfig mlanX hostcmd <robust_btc.conf> ap_generictime
+mlanconfig mlanX hostcmd <robust_btc.conf> ap_a2dptime
+mlanconfig mlanX hostcmd <robust_btc.conf> ap_inquirytime
+mlanconfig mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_get
+mlanconfig mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_set
+mlanconfig mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_disable
+mlanconfig mlanX arpfilter <arpfilter.conf>
+mlanconfig mlanX mefcfg <mef.conf>
+mlanconfig mlanX cfgdata <register type> <conf file>
+mlanconfig mlanX sdcmd52rw <FN no.> <address> [data]
+mlanconfig mlanX sdcmd53rw <FN no.> <address> <mode> <blksize> <blknum> [data1] ... [dataN]
+mlanconfig mlanX setuserscan [ARGS]
+mlanconfig mlanX getscantable [ARGS]
+mlanconfig mlanX addts <filename.conf> <section# of tspec> <timeout in ms>
+mlanconfig mlanX delts <filename.conf> <section# of tspec>
+mlanconfig mlanX qconfig set msdu <lifetime in TUs> [Queue Id: 0-3]
+mlanconfig mlanX qconfig get [Queue Id: 0-3]
+mlanconfig mlanX qconfig def [Queue Id: 0-3]
+mlanconfig mlanX qstats on  [Queue Id: 0-3]
+mlanconfig mlanX qstats off [Queue Id: 0-3]
+mlanconfig mlanX qstats get [Queue Id: 0-3]
+mlanconfig mlanX qstatus
+mlanconfig mlanX ts_status
+mlanconfig mlanX regrdwr <type> <offset> [value]
+mlanconfig mlanX memrdwr <address> [value]
+mlanconfig mlanX customie <index> <mask> <IE buffer>
+mlanconfig mlanX tdls_config <0/1>
+mlanconfig mlanX tdls_setinfo <tdls.conf>
+mlanconfig mlanX tdls_setup <tdls.conf>
+mlanconfig mlanX tdls_discovery <tdls.conf>
+mlanconfig mlanX tdls_teardown <tdls.conf>
+mlanconfig mlanX tdls_powermode <tdls.conf>
+mlanconfig mlanX tdls_channel_switch <tdls.conf>
+mlanconfig mlanX tdls_stop_channel_switch <tdls.conf>
+mlanconfig mlanX tdls_cs_params <tdls.conf>
+mlanconfig mlanX tdls_disable_cs <0/1>
+mlanconfig mlanX tdls_link_status
+mlanconfig mlanX tdls_debug "wrong_bss" <0/1>
+mlanconfig mlanX tdls_debug "setup_existing_link" <0/1>
+mlanconfig mlanX tdls_debug "fail_setup_confirm" <0/1>
+mlanconfig mlanX tdls_debug "setup_with_prohibited" <0/1>
+mlanconfig mlanX tdls_debug "higher_lower_mac" <0/1>
+mlanconfig mlanX tdls_debug "ignore_key_expiry" <0/1>
+mlanconfig mlanX tdls_debug "allow_weak_security" <0/1>
+mlanconfig mlanX tdls_debug "stop_rx" <0/1>
+mlanconfig mlanX tdls_debug "cs_im_return" <0/1>
+mlanconfig mlanX mgmtframetx <mgmt_frame.conf>
+
+
+DESCRIPTION
+
+Those commands are used in Marvell specific application called mlanconfig.
+
+===========
+-v
+	This command is used to display the version of mlanconfig utility.
+	Usage:
+		mlanconfig -v
+
+hostcmd bgscfg
+	This command is used to configure the various parameters for PPS/UAPSD
+	or normal background scan.
+
+	Usage:
+		mlanconfig mlanX hostcmd config/bg_scan.conf bgscfg
+
+hostcmd requesttpc
+	This command is used to request 802.11H TPC info.
+
+	Usage:
+		mlanconfig mlanX hostcmd config/requesttpc.conf requesttpc
+
+hostcmd crypto_test
+	This command is used to test the encryption/decryption API of the firmware.
+
+	Usage:
+		mlanconfig mlanX hostcmd config/crypto_test.conf crypto_test
+
+hostcmd subevent_get
+hostcmd subevent_set
+	This command is used to get/set the configurations for event descriptor
+	interface command.
+	subsvent_get: get subscribed event parameters
+	subsvent_set: set subscribed event parameters
+
+	Usage:
+		mlanconfig mlanX hostcmd config/subevent.conf subevent_get
+		mlanconfig mlanX hostcmd config/subevent.conf subevent_set
+
+hostcmd auto_tx_get
+hostcmd nat_keep_alive
+hostcmd auto_tx_unreg
+	This command is used to configures the Frame Auto Transmission parameters.
+	auto_tx_get: get auto_tx parameters
+	nat_keep_alive: register to firmware for sending NAT Keep Alive packet
+	auto_tx_unreg: unregister to firmware auto_tx
+
+	Usage:
+		mlanconfig mlanX hostcmd config/auto_tx.conf auto_tx_get
+		mlanconfig mlanX hostcmd config/auto_tx.conf nat_keep_alive
+		mlanconfig mlanX hostcmd config/auto_tx.conf auto_tx_unreg
+
+hostcmd txrate_cfg_get
+hostcmd txrate_cfg_set_bg
+hostcmd txrate_cfg_set_bgn
+	This command is used to set/get the transmit data rate.
+
+	Usage:
+		mlanconfig mlanX hostcmd config/txrate_cfg.conf txrate_cfg_get
+		mlanconfig mlanX hostcmd config/txrate_cfg.conf txrate_cfg_set_bg
+		mlanconfig mlanX hostcmd config/txrate_cfg.conf txrate_cfg_set_bgn
+
+hostcmd txpwrlimit_cfg_get
+hostcmd txpwrlimit_2g_cfg_set
+hostcmd txpwrlimit_5g_cfg_set
+	This command is used to set/get the configuration data of Tx power limitation.
+	Note: The configuration set should be issued when STA is disconnected.
+
+	Usage:
+		mlanconfig mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_cfg_get
+		mlanconfig mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_2g_cfg_set
+		mlanconfig mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_5g_cfg_set
+
+hostcmd pad_cfg_get
+hostcmd pad_cfg_set
+	This command is used to set/get the configuration data for PAD OR.
+
+	Usage:
+		mlanconfig mlanX hostcmd config/pad_cfg.conf pad_cfg_get
+		mlanconfig mlanX hostcmd config/pad_cfg.conf pad_cfg_set
+
+hostcmd 2040coex
+	This command is used to send the 11n 20/40 Coex command to firmware.
+	Firmware will send 11n 20/40 Coex management action frame to AP.
+
+	Usage:
+		mlanconfig mlanX hostcmd config/11n_2040coex.conf 2040coex
+
+hostcmd mode_get
+hostcmd mode_timeshare
+hostcmd mode_spatial
+	This command is used to get/set Robust BT Coex.
+	mode_get:       get the current mode
+	mode_timeshare: set Robust BT Coex to timeshare mode  (default on 1x1 chips)
+	mode_spatial:   set Robust BT Coex to spatial mode    (only for, and default on 2x2 chips)
+
+	Usage:
+		mlanconfig mlanX hostcmd config/robust_btc.conf mode_get
+		mlanconfig mlanX hostcmd config/robust_btc.conf mode_timeshare
+		mlanconfig mlanX hostcmd config/robust_btc.conf mode_spatial
+
+hostcmd generictime
+hostcmd a2dptime
+hostcmd inquirytime
+hostcmd ap_generictime
+hostcmd ap_a2dptime
+hostcmd ap_inquirytime
+        This command is used to configure the time slice of COEX (only works in timeshare mode)
+        generictime:       configure the Bttime and Wlantime in Station Generic case
+        a2dptime:          configure the Bttime and Wlantime in Station A2DP case
+        inquirytime:       configure the Bttime and Wlantime in Station Inquiry case
+        ap_generictime:    configure the Bttime and Wlantime in Ap Generic case
+        ap_a2dptime:       configure the Bttime and Wlantime in Ap A2DP case
+        ap_inquirytime:    configure the Bttime and Wlantime in Ap Inquiry case
+
+    Usage:
+                mlanutl mlanX hostcmd config/robust_btc.conf generictime
+                mlanutl mlanX hostcmd config/robust_btc.conf a2dptime
+                mlanutl mlanX hostcmd config/robust_btc.conf inquirytim
+                mlanutl mlanX hostcmd config/robust_btc.conf ap_generictime
+                mlanutl mlanX hostcmd config/robust_btc.conf ap_a2dptime
+                mlanutl mlanX hostcmd config/robust_btc.conf ap_inquirytime
+
+hostcmd sdio_pulldown_get
+hostcmd sdio_pulldown_set
+hostcmd sdio_pulldown_disable
+	This command is used to set/get the settings of pulling up and
+	pulling down of SDIO lines.
+
+	Usage:
+		mlanconfig mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_get
+		mlanconfig mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_set
+		mlanconfig mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_disable
+
+arpfilter
+	This command is used to configure the ARP filtering parameters.
+
+	Usage:
+		mlanconfig mlanX arpfilter config/arpfilter.conf
+
+mefcfg
+	This command is used to set MEF settings.
+
+	Usage:
+		mlanconfig mlanX mefcfg config/mef.conf
+
+cfgdata
+	This command is used to set/get the configuration data to/from firmware.
+
+	Usage:
+		mlanconfig mlanX cfgdata <type> <.conf file name>
+		This command is used to set the cfg data in the .conf file to firmware.
+
+		mlanconfig mlanX cfgdata <type>
+		This command is used to get the cfg data from firmware and display
+		on to the console.
+
+	Where the value of <type> field is:
+		2   -- Cal data download and <.conf file name> is cal_data.conf
+
+sdcmd52rw
+	This command is used to read/write a controller register in
+	Secure Digital I/O Interfaces.
+
+	Usage:
+		mlanconfig mlanX sdcmd52rw <function number> <register address> [value]
+
+	For SDIO MMC driver, only function 0 and 1 access is allowed. And there
+	is a limitation for function 0 write, only vendor specific CCCR registers
+	(0xf0 -0xff) are permiited.
+
+    Examples:
+		mlanconfig mlan0 sdcmd52rw 1 3
+		mlanconfig mlan0 sdcmd52rw 1 1 0x3f
+
+sdcmd53rw
+	This command is used to issue a CMD53 read/write data in
+	Secure Digital I/O Interfaces.
+
+	Usage:
+		mlanconfig mlanX sdcmd53rw <func> <address> <mode> <blksize> <blknum> [data1] ... [dataN]
+
+	where the parameters are,
+		<func>:     function number (0/1/2/..)
+		<address>:  data address
+		<mode>:     byte mode/block mode (0/1)
+		<blksize>:  block size (32/64/../512, NA for byte mode)
+		<blknum>:   block number or byte number
+		<data1> ... <dataN>:  data for write
+
+	Note: The total data length is block size * block number for block mode
+	or byte number for byte mode. The max data length is 2000-byte.
+	For write the data pattern will be duplicated to data buffer.
+
+	Examples:
+		mlanconfig mlan0 sdcmd53rw 0 0x8000 1 0x40 2
+		mlanconfig mlan0 sdcmd53rw 1 0x10000 0 1 5 0x0a 0x0b 0x0c 0x0d 0x0e
+
+setuserscan
+	Initiate a customized scan and retrieve the results
+
+	Usage:
+		mlanconfig mlanX setuserscan [ARGS]
+
+	Where [ARGS]:
+	  ssid="[SSID]"            specify a SSID filter for the scan
+	  chan=[chan#][band][mode] where band is [a,b,g,n] and mode is
+	                           blank for active or 'p' for passive
+	  bssid=xx:xx:xx:xx:xx:xx  specify a BSSID filter for the scan
+	  wc="[WILDCARD SSID]"     specify a UNIX pattern matching filter (using *
+	                           and ?) for SSIDs found in a broadcast probe
+	  keep=[0 or 1]            keep the previous scan results (1), discard (0)
+	  dur=[scan time]          time to scan for each channel in milliseconds
+	  gap=[time gap]           Time gap between two scans in milliseconds
+	  probes=[#]               number of probe requests to send on each chan
+	                           for each broadcast probe required and each SSID
+	                           specific probe required (1-4)
+	  type=[1,2,3]             BSS type: 1 (Infra), 2(Adhoc), 3(Any)
+
+	Any combination of the above arguments can be supplied on the command line.
+	If the chan token is absent, a full channel scan will be completed by driver.
+	If the dur or probes tokens are absent, the driver default setting will be
+	used. The bssid and ssid fields, if blank, will produce an unfiltered scan.
+	It's allowed to input multiple ssid/wc entries, the max entry number is 10.
+	The type field will default to 3 (Any) and the keep field will default to 0
+	(Discard).
+
+	Examples:
+	1) Perform an active scan on channels 1, 6, and 11 in the 'g' band:
+		setuserscan chan=1g,6g,11g
+
+	2) Perform a passive scan on channel 11 for 20 ms:
+		setuserscan chan=11gp dur=20
+
+	3) Perform an active scan on channels 1, 6, and 11; and a passive scan on
+	   channel 36 in the 'a' band:
+		setuserscan chan=1g,6g,11g,36ap
+
+	4) Perform an active scan on channel 6 and 36 for specific SSID:
+		setuserscan chan=6g,36a ssid=TestAP1 ssid=TestAP2
+
+	5) Scan all available channels (B/G/N, A bands) for a specific BSSID, keep
+	   the current scan table intact, update existing or append new scan data:
+		setuserscan bssid=00:50:43:20:12:82 keep=1
+
+	6) Scan channel 6, for all infrastructure networks, sending two probe
+	   requests.  Keep the previous scan table intact. Update any duplicate
+	   BSSID/SSID matches with the new scan data:
+		setuserscan chan=6g type=1 probes=2 keep=1
+
+	7) Scan channel 1 and 6, for all networks matching the Mrvl*AP
+	   or AP*Mrvl? patterns and for MrvlTst SSID.  Generate 3 broadcast
+	   probes for the patterns and 3 SSID specific probes for MrvlTst on
+	   both channel 1 and channel 6.
+		setuserscan chan=1g,6g probes=3 wc="Mrvl*AP" wc="AP*Mrvl?" ssid="MrvlTst"
+
+	8) Scan all the channels for specified band.
+		setuserscan chan=0g
+
+	9) Scan channel 1 and 6, send 3 probe requests, scan each channel for 40 ms
+	   with time gap of 50ms between 2 scans
+		setuserscan chan=1g,6g probes=3 dur=40 gap=50
+
+	All entries in the scan table (not just the new scan data when keep=1)
+	will be displayed upon completion by use of the getscantable ioctl.
+
+getscantable
+	Display the current contents of the driver scan table
+
+	Usage:
+		mlanconfig mlanX getscantable
+		mlanconfig mlanX getscantable [#]
+		mlanconfig mlanX getscantable tsf
+		mlanconfig mlanX getscantable help
+
+	1) Without argument, the entire scantable is displayed.
+	2) Specifying a # will display detailed information about a specific scan
+	   table entry.  '0' displays driver cached information regarding the
+	   current association (if any).
+	3) The tsf argument will display the entire scan table with the recorded
+	   TSF timestamp for the entry.
+	4) The help argument will display the legend for the capability field.
+
+addts
+	Send an ADDTS command to the associated AP.
+
+	Process a given conf file for a specific TSPEC data block.  Send the
+	  TSPEC along with any other IEs to the driver/firmware for transmission
+	  in an ADDTS request to the associated AP.
+
+	Return the execution status of the command as well as the ADDTS response
+	  from the AP if any.
+
+	Usage:
+		mlanconfig mlanX addts <filename.conf> <section# of tspec> <timeout(ms)>
+
+delts
+	Send a DELTS command to the associated AP.
+
+	Process a given conf file for a specific TSPEC data block.  Send the
+	  TSPEC along with any other IEs to the driver/firmware for transmission
+	  in a DELTS request to the associated AP.
+
+	Return the execution status of the command.  There is no response to a
+	  DELTS from the AP.
+
+	Usage:
+		mlanconfig mlanX delts <filename.conf> <section# of tspec>
+
+qconfig
+	Send a WMM AC Queue configuration command to get/set/default params
+
+	Configure or get the parameters of a WMM AC queue. The command takes
+	  an optional Queue Id as a last parameter.  Without the queue id, all
+	  queues will be acted upon.
+
+	Usage:
+		mlanconfig mlanX qconfig set msdu <lifetime in TUs> [Queue Id: 0-3]
+		mlanconfig mlanX qconfig get [Queue Id: 0-3]
+		mlanconfig mlanX qconfig def [Queue Id: 0-3]
+
+qstats
+	Turn on/off or retrieve and clear the queue statistics for an AC
+
+	Turn the queue statistics collection on/off for a given AC or retrieve the
+	  current accumulated stats and clear them from the firmware. The command
+	  takes an optional Queue Id as a last parameter. Without the queue id,
+	  all queues will be acted upon.
+
+	Usage:
+		mlanconfig mlanX qstats on  [Queue Id: 0-3]
+		mlanconfig mlanX qstats off [Queue Id: 0-3]
+		mlanconfig mlanX qstats get [Queue Id: 0-3]
+
+qstatus
+	This command retrieves the current status of the WMM queues. If WMM
+	  is enabled then it displays the information for each AC in a table.
+
+	Usage:
+		mlanconfig mlanX qstatus
+
+ts_status
+	This command queries the FW for the status of TSIDs 0 through 7
+	  configured via call admission control and displays the results in a
+	  table.
+
+	Usage:
+		mlanconfig mlanX ts_status
+
+regrdwr
+	This command is used to read/write the adapter register.
+
+	Usage:
+		mlanconfig mlanX regrdwr <type> <offset> [value]
+
+	where the parameters are,
+		<type>:     1:MAC/SOC, 2:BBP, 3:RF, 5:CAU
+		<offset>:   offset of register
+		[value]:    value to be written
+
+	Examples:
+		mlanconfig mlan0 regrdwr 1 0xa060   : Read the MAC register
+		mlanconfig mlan0 regrdwr 1 0xa794 0x80000000
+		                                    : Write 0x80000000 to MAC register
+
+memrdwr
+	This command is used to read/write the adapter memory.
+
+	Usage:
+		mlanconfig mlanX memrdwr <address> [value]
+
+	where the parameters are,
+		<address>:  memory address
+		[value]:    value to be written
+
+	Examples:
+		mlanconfig mlan0 memrdwr 0x80000000  : Read memory address 0x80000000
+		mlanconfig mlan0 memrdwr 0x80000000 0xffffffff
+		                                     : Write 0xffffffff to memory address 0x80000000
+
+customie
+	This command is used to set or get custom IEs for management frames.
+
+	Usage : customie [INDEX] [MASK] [IEBuffer]
+        empty - Get all IE settings
+        INDEX:  0 - Get/Set IE index 0 setting
+                1 - Get/Set IE index 1 setting
+                2 - Get/Set IE index 2 setting
+               MAX IE Index depends on device memory.
+
+               -1 - Append/Delete IE automatically
+                    Delete will delete the IE from the matching IE buffer
+                    Append will append the IE to the buffer with the same mask
+        MASK :  Management subtype mask value as per bit definitions
+             :  Bit 0 - Association request
+             :  Bit 1 - Association response
+             :  Bit 2 - Reassociation request
+             :  Bit 3 - Reassociation response
+             :  Bit 4 - Probe request
+             :  Bit 5 - Probe response
+             :  Bit 8 - Beacon
+        MASK :  MASK = 0 to clear the mask and the IE buffer
+        IEBuffer :  IE Buffer in hex (max 256 bytes)
+                    The Buffer should not be space separated.
+
+	Example:
+	 ./mlanconfig mlan0 customie
+		Get IE buffer, subtype mask settings for all indices.
+
+	./mlanconfig mlan0 customie 1
+		Get IE buffer and subtype mask for the Index = 1.
+
+	./mlanconfig mlan0 customie 2 0
+		Clear IE buffer and mask value for Index = 2.
+
+	./mlanconfig mlan0 customie 3 0x101 0xdd051234567890
+		Set IE buffer and mask value for Index = 3.
+
+	./mlanconfig mlan0 customie -1 0x101 0xdd051234567890
+		Append the specified IEBuffer at index with mask value of 0x101.
+
+	./mlanconfig mlan0 customie -1 0 0xdd051234567890
+		Delete the specified IEBuffer from all the IEs.
+
+	./mlanconfig mlan0 customie 2 0 0xdd051234567890
+		Delete the specified IEBuffer from the IEs at index 2.
+
+tdls_config
+	This command is used to enable/disable TDLS on device.
+
+	Usage : tdls_config <0/1>
+		1: Enable TDLS
+		0: Disable TDLS
+tdls_setinfo
+	This command is used for setting the capabilities of the TDLS station.
+
+        Usage : tdls_setinfo <tdls.conf>
+		Set capabilities of TDLS station.
+tdls_setup
+	This command is used to send TDLS setup request.
+
+	Usage: tdls_setup <tdls.conf>
+                Send TDLS setup request.
+tdls_discovery
+	This command is used to request TDLS discovery.
+
+	Usage : tdls_discovery <tdls.conf>
+        	Request TDLS discovery.
+tdls_teardown
+	This command is used to send TDLS teardown request.
+
+	Usage : tdls_teardown <tdls.conf>
+		Request teardown of TDLS link.
+tdls_powermode
+	This command is used to send TDLS powermode request.
+
+	Usage : tdls_powermode <tdls.conf>
+		Send TDLS powermode (either 0:Active, 1:PowerSave) command.
+tdls_channel_switch
+	This command is used to send TDLS channel switch request.
+
+	Usage : tdls_channel_switch <tdls.conf>
+		Send TDLS channel switch command.
+tdls_stop_channel_switch
+	This command is used to send stop TDLS channel switch request.
+
+	Usage : tdls_stop_channel_switch <tdls.conf>
+		Send stop TDLS channel switch command.
+tdls_cs_params
+	This command is used to set TDLS channel switch params
+
+	Usage : tdls_cs_params <tdls.conf>
+		Set TDLS channel switch params.
+tdls_disable_cs
+	This command is used to disable TDLS channel switch
+
+	Usage : tdls_disable_cs <0/1>
+		Disable TDLS channel switch.
+tdls_link_status [peer_mac_address]
+	This command is used to get link information about TDLS links or
+    a TDLS link correponding to peer mac address.
+
+	Usage : tdls_link_status
+		Send TDLS command to get current link status.
+tdls_debug
+	This command is used for FW debug functionality and tests.
+
+mgmtframetx
+	This command is used to send mgmt frame
+
+	Usage:
+		mlanconfig mlanX mgmtframetx <mgmt_frame.conf>
+
+
+===============================================================================
+		U S E R  M A N U A L  F O R  M L A N 2 0 4 0 C O E X
+
+NAME
+mlan2040coex - This application handles the 11n 20/40 coexistence operation for
+               the Marvell mdriver
+
+SYNOPSIS
+mlan2040coex [-d <n>] [-i <intfname>] [hvB]
+	-d = Device number (n)
+	-i = Interface name (intfname)
+	-h = Help
+	-v = Version
+	-B = Run the process in background
+Note: If device number (n) is not present, then 0 assumed.
+      If intfname is not present, then mlan0 assumed.
+
+===============================================================================
+        U S E R  M A N U A L  F O R  MLANEVENT
+
+NAME
+mlanevent.exe
+
+This tool can be used to listen for and obtain events from the driver
+through the netlink layer.
+
+----------------
+Supported events
+----------------
+STA_DEAUTH
+STA_ASSOC
+BSS_START
+BSS_IDLE
+BSS_ACTIVE
+
+-----------------
+Details of events
+-----------------
+
+STA_DEAUTH
+----------
+    For this event, the following information is shown:
+        + Deauthenticated STA MAC address.
+        + Reason for deauthentication.
+
+STA_ASSOC
+----------
+    For this event, the following information is shown:
+        + STA MAC address.
+
+BSS_START
+----------
+    For this event, the following information is shown:
+        + AP MAC address.
+
+BSS_IDLE
+----------
+    For this event, there is no associated information.
+
+BSS_ACTIVE
+----------
+    For this event, there is no associated information.
+
+===============================================================================
diff --git a/wlan_sd8897/README_MLAN b/wlan_sd8897/README_MLAN
new file mode 100644
index 0000000..47c1d62
--- /dev/null
+++ b/wlan_sd8897/README_MLAN
@@ -0,0 +1,3637 @@
+===============================================================================
+			U S E R  M A N U A L
+
+ Copyright (C) 2008-2018, Marvell International Ltd.
+ All Rights Reserved
+
+1) FOR DRIVER BUILD
+
+	Goto source code directory wlan_src/.
+	make [clean] build
+	The driver and utility binaries can be found in ../bin_xxxx directory.
+	The driver code supports Linux kernel up to 4.15.
+
+2) FOR DRIVER INSTALL
+
+	a) Copy firmware image sd8787_uapsta.bin | sd8797_uapsta.bin | ... to
+	   /lib/firmware/mrvl/ directory, create the directory if it doesn't exist.
+	b) Install WLAN driver,
+	   There are drv_mode, max_sta_bss, max_uap_bss etc. module parameters.
+		The bit settings of drv_mode are,
+			Bit 0 :  STA
+			Bit 1 :  uAP
+			Bit 2 :  WIFIDIRECT
+		The default drv_mode is 7.
+			Bit 4 :  NAN
+
+		max_sta_bss: Maximum number of STA BSS (default 1, max 1)
+		sta_name: Name of the STA interface (default: "mlan")
+		max_uap_bss: Maximum number of uAP BSS (default 1, max 2)
+		uap_name: Name of the uAP interface (default: "uap")
+		max_wfd_bss: Maximum number of WIFIDIRECT BSS (default 1, max 1)
+		wfd_name: Name of the WIFIDIRECT interface (default: "wfd")
+		max_vir_bss: Number of Virtual interfaces (default 0)
+		nan_name: Name of the NAN interface (default: "nan")
+		max_nan_bss: Number of NAN interfaces (default 1)
+	   For example, to install SD8787 driver,
+		insmod mlan.ko
+		insmod sd8787.ko [drv_mode=3] [fw_name=mrvl/sd8787_uapsta.bin]
+	   To load driver in STA only mode,
+		insmod mlan.ko
+		insmod sd8787.ko drv_mode=1 [fw_name=mrvl/sd8787_uapsta.bin]
+	   To load driver in uAP only mode,
+		insmod mlan.ko
+		insmod sd8787.ko drv_mode=2 [fw_name=mrvl/sd8787_uapsta.bin]
+
+	   To switch mode between STA only, uAP only and uAPSTA etc. in run time,
+		echo drv_mode=1 > /proc/mwlan/config		// STA mode
+		echo drv_mode=2 > /proc/mwlan/config		// uAP mode
+		echo drv_mode=3 > /proc/mwlan/config		// STA+uAP mode
+		echo drv_mode=7 > /proc/mwlan/config		// STA+uAP+WIFIDIRECT mode
+	c) Uninstall WLAN driver,
+		ifconfig mlanX down
+		ifconfig uapX down
+		rmmod sd8xxx
+		rmmod mlan
+
+	To load driver with MFG firmware file, use mfg_mode=1 when insmod WLAN driver and
+	specify MFG firmware name if needed.
+
+	There are some other parameters for debugging purpose etc. Use modinfo to check details.
+	  drvdbg=<bit mask of driver debug message control>
+	  dev_cap_mask=<Bit mask of the device capability>
+	  mac_addr=xx:xx:xx:xx:xx:xx <override the MAC address (in hex)>
+	  auto_ds=0|1|2 <use MLAN default | enable auto deepsleep | disable auto deepsleep>
+	  ps_mode=0|1|2 <use MLAN default | enable IEEE PS mode | disable IEEE PS mode>
+	  p2p_enh=0|1 <Disable enhanced P2P (default) | Enable enhanced P2P>
+	  max_tx_buf=2048|4096|8192 <maximum AMSDU Tx buffer size>
+	  pm_keep_power=1|0 <PM keep power in suspend (default) | PM no power in suspend>
+	  shutdown_hs=1|0 <Enable HS when shutdown | No HS when shutdown (default)>
+	  cfg_11d=0|1|2 <use MLAN default | enable 11d | disable 11d>
+	  dts_enable=0|1 <Disable DTS | Enable DTS (default)>
+	  hw_test=0|1 <Disable hardware test (default) | Enable hardware test>
+	  fw_serial=0|1 <support parallel download FW | support serial download FW (default)>
+	  req_fw_nowait=0|1 <use request_firmware API (default) | use request_firmware_nowait API>
+	  SD8887: antcfg=0|1|2|0xffff <default | Tx/Rx antenna 1 | Tx/Rx antenna 2 | enable antenna diversity>
+	  SD8897/SD8997: antcfg=0x11|0x13|0x33 <Bit0:Rx Path A, Bit1:Rx Path B, Bit 4:Tx Path A, Bit 5:Tx Path B>
+	  init_cfg=<init config (MAC addresses, registers etc.) file name>
+		e.g. copy init_cfg.conf to firmware directory, init_cfg=mrvl/init_cfg.conf
+	  cal_data_cfg=<CAL data config file name>
+		e.g. copy cal_data.conf to firmware directory, cal_data_cfg=mrvl/cal_data.conf
+          Note: Loading driver with 8887 must include correct cal_data_cfg parameter.
+	  txpwrlimit_cfg=<Tx power limit config file name>
+		e.g. copy txpwrlimit_cfg_set.conf to firmware directory, txpwrlimit_cfg=mrvl/txpwrlimit_cfg_set.conf
+	  init_hostcmd_cfg=<init hostcmd config file name>
+		e.g. copy init_hostcmd_cfg.conf to firmware directory, init_hostcmd_cfg=mrvl/init_hostcmd_cfg.conf
+	  cfg80211_wext=<bit mask of CFG80211 and WEXT control>
+		Bit 0: STA WEXT
+		Bit 1: uAP WEXT
+		Bit 2: STA CFG80211
+		Bit 3: uAP CFG80211
+	  cfg80211_drcs=1|0 <Enable DRCS support (default) | Disable DRCS support>
+	  reg_alpha2=<Regulatory alpha2 (default NULL)>
+	  wq_sched_prio: Priority for work queue
+	  wq_sched_policy: Scheduling policy for work queue
+		(0: SCHED_NORMAL, 1: SCHED_FIFO, 2: SCHED_RR, 3: SCHED_BATCH, 5: SCHED_IDLE)
+		Please note that, both wq_sched_prio and wq_sched_policy should be provided
+		as module parameters. If wq_sched_policy is (0, 3 or 5), then wq_sched_prio
+		must be 0. wq_sched_prio should be 1 to 99 otherwise.
+	  rx_work=0|1|2 <default | Enable rx_work_queue | Disable rx_work_queue>
+	  low_power_mode_enable=0|1 <disable low power mode (default)| enable low power mode>
+	  When low power mode is enabled, the output power will be clipped at ~+10dBm and the
+	  expected PA current is expected to be in the 80-90 mA range for b/g/n modes
+	  indication_gpio=0xXY <GPIO to indicate wakeup source and its level; high four bits X:
+                  level(0/1) for normal wakeup; low four bits Y: GPIO pin number. This parameter
+                  only works with specific board and firmware.>
+      napi=0|1 <disable napi | enable napi>
+	Note: On some platforms (e.g. PXA910/920) double quotation marks ("") need to used
+	for module parameters.
+		insmod sd8xxx.ko "<para1> <para2> ..."
+
+3) FOR DRIVER PROC & DEBUG
+
+	The following info are provided in /proc/net/mwlan/mlanX|uapX|wfdX/info,
+	on kernel 2.6.24 or later, the entry is /proc/mwlan/mlanX|uapX|wfdX/info.
+
+	driver_name = "wlan" or "uap"
+	driver_version = <chip id, firmware version and driver version>
+	interface_name = "mlanX", "uapX" or "wfdX"
+	bss_mode = "Ad-hoc" | "Managed" | "Auto" | "Unknown"
+	media_state = "Disconnected" | "Connected"
+	mac_address = <6-byte adapter MAC address>
+	multicase_count = <multicast address count>     // Only for STA
+	essid = <current SSID>                          // Only for STA
+	bssid = <current BSSID>                         // Only for STA
+	channel = <current channel>                     // Only for STA
+	region_code = <current region code>             // Only for STA
+	multicast_address[n] = <multicast address>      // Only for STA
+	num_tx_bytes = <number of bytes sent to device>
+	num_rx_bytes = <number of bytes received from device and sent to kernel>
+	num_tx_pkts = <number of packets sent to device>
+	num_rx_pkts = <number of packets received from device and sent to kernel>
+	num_tx_pkts_dropped = <number of Tx packets dropped by driver>
+	num_rx_pkts_dropped = <number of Rx packets dropped by driver>
+	num_tx_pkts_err = <number of Tx packets failed to send to device>
+	num_rx_pkts_err = <number of Rx packets failed to receive from device>
+	carrier "on" | "off"
+	tx queue "stopped" | "started"
+	tkip_mic_failures = 0                           // Only for uAP
+	ccmp_decrypt_errors = 0                         // Only for uAP
+	wep_undecryptable_count = 0                     // Only for uAP
+	wep_icv_error_count = 0                         // Only for uAP
+	decrypt_failure_count = 0                       // Only for uAP
+	mcast_tx_count = 0                              // Only for uAP
+	failed_count = 0                                // Only for uAP
+	retry_count = 0                                 // Only for uAP
+	multiple_retry_count = 0                        // Only for uAP
+	frame_duplicate_count = 0                       // Only for uAP
+	rts_success_count = 0                           // Only for uAP
+	rts_failure_count = 0                           // Only for uAP
+	ack_failure_count = 0                           // Only for uAP
+	rx_fragment_count = 0                           // Only for uAP
+	mcast_rx_frame_count = 0                        // Only for uAP
+	fcs_error_count = 0                             // Only for uAP
+	tx_frame_count = 0                              // Only for uAP
+	rsna_tkip_cm_invoked = 0                        // Only for uAP
+	rsna_4way_hshk_failures = 0                     // Only for uAP
+
+	The following debug info are provided in /proc/net/mwlan/mlanX|uapX|wfdX/debug,
+	on kernel 2.6.24 or later, the entry is /proc/mwlan/mlanX|uapX|wfdX/debug.
+
+	drvdbg = <bit mask of driver debug message control>
+	wmm_ac_vo = <number of packets sent to device from WMM AcVo queue>
+	wmm_ac_vi = <number of packets sent to device from WMM AcVi queue>
+	wmm_ac_be = <number of packets sent to device from WMM AcBE queue>
+	wmm_ac_bk = <number of packets sent to device from WMM AcBK queue>
+	max_tx_buf_size = <maximum Tx buffer size>
+	tx_buf_size = <current Tx buffer size>
+	curr_tx_buf_size = <current Tx buffer size in FW>
+	ps_mode = <0/1, CAM mode/PS mode>
+	ps_state = <0/1/2/3, awake state/pre-sleep state/sleep-confirm state/sleep state>
+	is_deep_sleep = <0/1, not deep sleep state/deep sleep state>    // Only for STA
+	wakeup_dev_req = <0/1, wakeup device not required/required>
+	wakeup_tries = <wakeup device count, cleared when device awake>
+	hs_configured = <0/1, host sleep not configured/configured>
+	hs_activated = <0/1, extended host sleep not activated/activated>
+	tx_pkts_queued = <number of Tx packets queued>
+	pps_uapsd_mode = <0/1, PPS/UAPSD mode disabled/enabled>     // Only for STA
+	sleep_pd = <sleep period in milliseconds>                   // Only for STA
+	qos_cfg = <WMM QoS info>                                    // Only for STA
+	tx_lock_flag = <0/1, Tx lock flag>                          // Only for STA
+	port_open = <0/1, port open flag>                           // Only for STA
+	scan_processing = <0/1, scan processing flag>               // Only for STA
+	num_bridge_pkts = <number of bridged packets>               // Only for uAP
+	num_drop_pkts = <number of dropped packets>                 // Only for uAP
+	num_tx_timeout = <number of Tx timeout>
+	num_cmd_timeout = <number of timeout commands>
+	timeout_cmd_id = <command id of the last timeout command>
+	timeout_cmd_act = <command action of the last timeout command>
+	last_cmd_id = <command id of the last several commands sent to device>
+	last_cmd_act = <command action of the last several commands sent to device>
+	last_cmd_index = <0 based last command index>
+	last_cmd_resp_id = <command id of the last several command responses received from device>
+	last_cmd_resp_index = <0 based last command response index>
+	last_event = <event id of the last several events received from device>
+	last_event_index = <0 based last event index>
+	num_cmd_h2c_fail = <number of commands failed to send to device>
+	num_cmd_sleep_cfm_fail = <number of sleep confirm failed to send to device>
+	num_tx_h2c_fail = <number of data packets failed to send to device>
+	num_cmdevt_c2h_fail = <number of commands/events failed to receive from device>
+	num_rx_c2h_fail = <number of data packets failed to receive from device>
+	num_int_read_fail = <number of interrupt read failures>
+	last_int_status = <last interrupt status>
+	num_evt_deauth = <number of deauthenticated events received from device>        // Only for STA
+	num_evt_disassoc = <number of disassociated events received from device>        // Only for STA
+	num_evt_link_lost = <number of link lost events received from device>           // Only for STA
+	num_cmd_deauth = <number of deauthenticate commands sent to device>             // Only for STA
+	num_cmd_assoc_ok = <number of associate commands with success return>           // Only for STA
+	num_cmd_assoc_fail = <number of associate commands with failure return>         // Only for STA
+	cmd_sent = <0/1, send command resources available/sending command to device>
+	data_sent = <0/1, send data resources available/sending data to device>
+	mp_rd_bitmap = <SDIO multi-port read bitmap>
+	curr_rd_port = <SDIO multi-port current read port>
+	mp_wr_bitmap = <SDIO multi-port write bitmap>
+	curr_wr_port = <SDIO multi-port current write port>
+	cmd_resp_received = <0/1, no cmd response to process/response received and yet to process>
+	event_received = <0/1, no event to process/event received and yet to process>
+	ioctl_pending = <number of ioctl pending>
+	tx_pending = <number of Tx packet pending>
+	rx_pending = <number of Rx packet pending>
+	lock_count = <number of lock used>
+	malloc_count = <number of malloc done>
+	mbufalloc_count = <number of mlan_buffer allocated>
+	main_state = <current state of the main process>
+	sdiocmd53w = <SDIO Cmd53 write status>
+	sdiocmd53r = <SDIO Cmd52 read status>
+	hs_skip_count = <number of skipped suspends>
+	hs_force_count = <number of forced suspends>
+
+	Issue SDIO cmd52 read/write through proc.
+	Usage:
+		echo "sdcmd52rw=<func> <reg> [data]" > /proc/mwlan/config
+	where the parameters:
+		func: The function number to use (0-7)
+		reg:  The address of the register
+		data: The value to write, read if the value is absent
+		For SDIO MMC driver, only function 0 and WLAN function access is allowed.
+		And there is a limitation for function 0 write, only vendor specific CCCR
+		registers (0xf0 -0xff) are permiited.
+	Examples:
+		echo "sdcmd52rw= 0 4" > /proc/mwlan/config      # read func 0 address 4
+		cat /proc/mwlan/config                          # display the register value
+		echo "sdcmd52rw= 1 3 0xf" > /proc/mwlan/config  # write 0xf to func 1 address 3
+
+	Issue debug_dump command through proc.
+	Usage:
+		echo "debug_dump" > /proc/mwlan/config
+
+	Examples:
+		echo "debug_dump" > /proc/mwlan/config      # dump driver internal debug status.
+
+	Use dmesg or cat /var/log/debug to check driver debug messages.
+
+	Update /proc/sys/kernel/printk to change message log levels.
+	For example,
+	echo 6 > /proc/sys/kernel/printk    (messages with a higher priority than 6
+	                                     will be printed to the console)
+	echo 15 > /proc/sys/kernel/printk   (all messages will be printed to console)
+
+4) FOR FW RELOAD
+    a) Enable parallel firmware download in driver parameter
+       insmod sd8xxx.ko fw_serial=0
+
+    b) default fw name for parallel firmware download
+       sd8887_wlan_a2.bin
+
+    c) Trigger FW reload
+       echo "fw_reload=1" > /proc/mwlan/config   trigger inband firmware reset and reload firmware
+       echo "fw_reload=2" > /proc/mwlan/config   trigger firmware reload
+       echo "fw_reload=3" > /proc/mwlan/config   set firmware reload flag in driver.
+
+    (Note: This feature will be supported on Robin3 and KF2.
+           For CAC-A2, it only work with the board which supports parallel fw download)
+
+
+
+===============================================================================
+        U S E R  M A N U A L  F O R  MLANUTL
+
+NAME
+    mlanutl - configure the additional parameters available for Marvell mdriver.
+
+SYNOPSIS
+    mlanutl -v
+    mlanutl <mlanX|uapX|wfdx> <command> [parameters] ...
+
+	mlanutl mlanX 11dcfg
+	mlanutl mlanX 11dclrtbl
+	mlanutl mlanX addbapara [<m> <n> <o> <p> <q>]
+	mlanutl uapX addbapara [<m> <n> <o> <p> <q>]
+	mlanutl mlanX addbareject [<m0> <m1> ... <m7>]
+	mlanutl uapX addbareject [<m0> <m1> ... <m7>]
+	mlanutl mlanX addts <filename.conf> <section# of tspec> <timeout in ms>
+	mlanutl mlanX adhocaes
+	mlanutl mlanX aggrpriotbl [<m0> <n0> <m1> <n1> ... <m7> <n7>]
+	mlanutl uapX aggrpriotbl [<m0> <n0> <m1> <n1> ... <m7> <n7>]
+	mlanutl mlanX amsduaggrctrl <n>
+	mlanutl mlanX antcfg [m] [n]
+	mlanutl mlanX arpfilter <arpfilter.conf>
+	mlanutl mlanX assocessid <"[essid]">
+	mlanutl mlanX assocessid_bssid <"[bssid] [essid]">
+	mlanutl mlanX associate "<bssid> <ssid>"
+	mlanutl mlanX authtype [n]
+	mlanutl mlanX autotdls [n]
+        mlanutl mlanX tdls_idle_time [n]
+        mlanutl uapX dfs_offload [n]
+	mlanutl mlanX bandcfg [l] [m] [n] [o]
+	mlanutl mlanX bcninterval [n]
+	mlanutl wfdX bssrole [l]
+	mlanutl mlanX cfgdata <register type> [<conf file>]
+	mlanutl mlanX cfpcode [m] [n]
+	mlanutl mlanX changraph [<load | anpi | anpiload> <loops>]
+	mlanutl mlanX coex_rx_winsize [m]
+	mlanutl mlanX countrycode [l]
+
+	mlanutl	mlanX cfpinfo
+	mlanutl uapX cfpinfo
+
+	mlanutl	mlanX acs [<chX> <chY> ... <chZ>]
+	mlanutl	uapX acs [<chX> <chY> ... <chZ>]
+
+	mlanutl mlanX customie [[[<index>] <mask>] <IEBuffer>]
+	mlanutl mlanX deauth [l]
+	mlanutl uapX deauth [l]
+	mlanutl mlanX deepsleep [l] [m]
+	mlanutl mlanX delba <l> [<m> <n>]
+	mlanutl uapX delba <l> [<m> <n>]
+	mlanutl mlanX delts <filename.conf> <section# of tspec>
+	mlanutl mlanX dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>]
+	mlanutl mlanX drvdbg [n]
+	mlanutl mlanX esuppmode [l] [m] [n]
+	mlanutl mlanX extcapcfg [<ext_cap>]
+	mlanutl mlanX fwmacaddr [mac_addr]
+	mlanutl mlanX getdatarate
+	mlanutl uapX getdatarate
+	mlanutl mlanX getkey
+	mlanutl mlanX getlog
+	mlanutl mlanX getscantable [ARGS]
+	mlanutl mlanX getsignal [m] [n]
+	mlanutl mlanX signalextcfg [m]
+	mlanutl mlanX getsignalextv2 [m]
+	mlanutl mlanX getsignalext [m]
+        mlanutl mlanX dyn_bw [n]
+	mlanutl uapX getstalist
+	mlanutl uapX channel_switch <switch mode> <oper class> <new channel> <switch count> <bandwidth>
+	mlanutl mlanX hostcmd <11n_2040coex.conf> 2040coex
+	mlanutl mlanX hostcmd <auto_tx.conf> auto_tx_get
+	mlanutl mlanX hostcmd <auto_tx.conf> auto_tx_unreg
+	mlanutl mlanX hostcmd <bg_scan.conf> bgscfg
+
+	mlanutl mlanX hostcmd <pkt_coalescing.conf> coalesce_cfg
+	mlanutl mlanX hostcmd <ed_mac_ctrl.conf> ed_mac_ctrl
+	mlanutl mlanX hostcmd <crypto_test.conf> crypto_test
+	mlanutl mlanX hostcmd <auto_tx.conf> nat_keep_alive
+	mlanutl mlanX hostcmd <pad_cfg.conf> pad_cfg_get
+	mlanutl mlanX hostcmd <pad_cfg.conf> pad_cfg_set
+	mlanutl mlanX hostcmd <requesttpc.conf> requesttpc
+	mlanutl mlanX hostcmd <robust_btc.conf> mode_get
+	mlanutl mlanX hostcmd <robust_btc.conf> mode_timeshare
+	mlanutl mlanX hostcmd <robust_btc.conf> mode_spatial
+	mlanutl mlanX hostcmd <robust_btc.conf> mode_none
+	mlanutl mlanX hostcmd <robust_btc.conf> gpio_cfg
+	mlanutl mlanX hostcmd <robust_btc.conf> generictime
+	mlanutl mlanX hostcmd <robust_btc.conf> a2dptime
+	mlanutl mlanX hostcmd <robust_btc.conf> inquirytim
+	mlanutl mlanX hostcmd <robust_btc.conf> ap_generictime
+	mlanutl mlanX hostcmd <robust_btc.conf> ap_a2dptime
+	mlanutl mlanX hostcmd <robust_btc.conf> ap_inquirytime
+	mlanutl mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_disable
+	mlanutl mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_get
+	mlanutl mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_set
+	mlanutl mlanX hostcmd <subevent.conf> subevent_get
+	mlanutl mlanX hostcmd <subevent.conf> subevent_set
+	mlanutl mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_2g_cfg_set
+	mlanutl mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_5g_cfg_set
+	mlanutl mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_cfg_get
+	mlanutl mlanX hostcmd <txrate_cfg.conf> txrate_cfg_get
+	mlanutl mlanX hostcmd <txrate_cfg.conf> txrate_cfg_set_bg
+	mlanutl mlanX hostcmd <txrate_cfg.conf> txrate_cfg_set_bgn
+	mlanutl mlanX hostcmd <conf> generate_raw <raw_data_file>
+	mlanutl mlanX hostcmd <fwdump.conf> fwdump
+
+	mlanutl mlanX hotspotcfg [<bitmap>]
+	mlanutl mlanX hscfg [condition [[GPIO# [gap]]]] [ind_GPIO# [level]]
+	mlanutl mlanX hssetpara condition [GPIO# [gap]] [ind_GPIO# [level]]
+	mlanutl mlanX mgmtfilter <mgmtfilter.conf>
+	mlanutl mlanX auto_arp [n]
+	mlanutl mlanX htcapinfo [<m>] [<n>]
+	mlanutl mlanX htstreamcfg [n]
+	mlanutl mlanX httxbfcap [cap]
+	mlanutl mlanX httxbfcfg "<action>[;GlobalData/tsData/interval/txPeerData/snrData]"
+	mlanutl mlanX httxcfg [<m>] [<n>]
+	mlanutl mlanX inactivityto <n> <m> <l> [k]
+	mlanutl mlanX ipaddr ["<op>;<ipaddr>"]
+	mlanutl mlanX linkstats
+	mlanutl mlanX listeninterval [l]
+	mlanutl mlanX macctrl [n]
+	mlanutl uapX macctrl [n]
+	mlanutl mlanX mefcfg <mef.conf>
+	mlanutl mlanX memrdwr <address> [value]
+	mlanutl mlanX miracastcfg [l] [m] [n]
+	mlanutl mlanX mgmtframectrl [<mask>]
+	mlanutl uapX mgmtframectrl [<mask>]
+	mlanutl mlanX mgmtframetx <mgmt_frame.conf>
+	mlanutl mlanX mpactrl [tx_ena] [rx_ena] [tx_size] [rx_size] [tx_ports] [rx_ports]
+	mlanutl mlanX netmon [<act> [<filter> <band> <chan> [offset]]]
+	mlanutl mlanX offchannel [<l> <m> <n>]
+	mlanutl mlanX otpuserdata <l>
+	mlanutl mlanX passphrase [l]
+	mlanutl mlanX pb_bypass [data_1, data_2, ... data_n]
+	mlanutl mlanX pmfcfg <mfpc> <mfpr>
+	mlanutl mlanX port_ctrl [n]
+	mlanutl mlanX powercons [n]
+	mlanutl mlanX pscfg [k] [d] [l] ...
+	mlanutl mlanX bcntimeoutcfg [l] [m] [o] [p]
+	mlanutl mlanX psmode [l]
+        mlanutl <interface> robustcoex <gpiocfg> [Enable/Disable] [gpionum] [gpiopolarity]
+        mlanutl <interface> keep_connect [Enable/Disable]
+	mlanutl mlanX qconfig def [Queue Id: 0-3]
+	mlanutl mlanX qconfig get [Queue Id: 0-3]
+	mlanutl mlanX qconfig set msdu <lifetime in TUs> [Queue Id: 0-3]
+	mlanutl mlanX qoscfg
+	mlanutl mlanX qstatus
+	mlanutl mlanX radioctrl [n]
+	mlanutl mlanX rdeeprom <offset> <length>
+	mlanutl mlanX reassoctrl [n]
+	mlanutl mlanX regioncode [n]
+	mlanutl mlanX regrdwr <type> <offset> [value]
+	mlanutl mlanX rejectaddbareq [conditions]
+	mlanutl uapX rejectaddbareq [conditions]
+	mlanutl mlanX scancfg [t] [m] [p] [s] [a] [b] [ext]
+	mlanutl mlanX sdcmd52rw <FN no.> <address> [data]
+	mlanutl mlanX sdcmd53rw <FN no.> <address> <mode> <blksize> <blknum> [data1] ... [dataN]
+	mlanutl mlanX sdioclock <n>
+	mlanutl mlanX setuserscan [ARGS]
+	mlanutl mlanX cancelscan
+	mlanutl mlanX sleepparams [<p1> <p2> <p3> <p4> <p5> <p6>]
+	mlanutl mlanX sleeppd [n]
+	mlanutl mlanX sysclock [clk1] [clk2] [clk3] [clk4]
+	mlanutl mlanX tcpackenh [l]
+	mlanutl mlanX tdls_channel_switch <tdls.conf>
+	mlanutl mlanX tdls_config <0/1>
+	mlanutl mlanX tdls_cs_params <tdls.conf>
+	mlanutl mlanX tdls_debug "allow_weak_security" <0/1>
+	mlanutl mlanX tdls_debug "cs_im_return" <0/1>
+	mlanutl mlanX tdls_debug "fail_setup_confirm" <0/1>
+	mlanutl mlanX tdls_debug "higher_lower_mac" <0/1>
+	mlanutl mlanX tdls_debug "ignore_key_expiry" <0/1>
+	mlanutl mlanX tdls_debug "setup_existing_link" <0/1>
+	mlanutl mlanX tdls_debug "setup_with_prohibited" <0/1>
+	mlanutl mlanX tdls_debug "stop_rx" <0/1>
+	mlanutl mlanX tdls_debug "wrong_bss" <0/1>
+	mlanutl mlanX tdls_disable_cs <0/1>
+	mlanutl mlanX tdls_discovery <tdls.conf>
+	mlanutl mlanX tdls_link_status
+	mlanutl mlanX tdls_powermode <tdls.conf>
+	mlanutl mlanX tdls_setinfo <tdls.conf>
+	mlanutl mlanX tdls_setup <tdls.conf>
+	mlanutl mlanX tdls_stop_channel_switch <tdls.conf>
+	mlanutl mlanX tdls_teardown <tdls.conf>
+	mlanutl mlanX thermal
+	mlanutl mlanX ts_status
+	mlanutl mlanX tsf
+	mlanutl mlanX txaggrctrl [m]
+	mlanutl mlanX txbufcfg
+	mlanutl mlanX txratecfg [l] [m] [n]
+	mlanutl uapX txratecfg [l] [m] [n]
+    mlanutl mlanX verext
+    mlanutl mlanX version
+	mlanutl mlanX vhtcfg <j> <k> [l] [m] [n] [o]
+	mlanutl uapX vhtcfg <j> <k> [l] [m] [n] [o]
+	mlanutl mlanX wakeupreason
+	mlanutl uapX wakeupreason
+	mlanutl mlanX warmreset
+	mlanutl mlanX wpssession [n]
+	mlanutl mlanX wmmcfg [n]
+	mlanutl mlanX wmmparamcfg [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP]
+				[AC_BK AIFSN ECW_MAX ECW_MIN TX_OP]
+				[AC_VI AIFSN ECW_MAX ECW_MIN TX_OP]
+				[AC_VO AIFSN ECW_MAX ECW_MIN TX_OP]
+	mlanutl mlanX wwscfg [m]
+	mlanutl mlanX mc_cfg [n]
+	mlanutl mlanX mc_policy [n]
+	mlanutl mlanX mc_cfg_ext [c] [s] [u] [m] <a> <b> <d> <e>
+	mlanutl p2pX cfg_noa [h] [i] [j] [k] [l]
+	mlanutl p2pX cfg_opp_ps [m] [n]
+        mlanutl mlanX get_sensor_temp
+        mlanutl <inteface> indrstcfg <ir_mode> [gpio_pin]
+
+        mlanutl <interface> fwwakeupmethod <method> <GPIO_pin>
+
+        mlanutl uapX ctrldeauth <n>
+
+        mlanutl mlanX/uapX bootsleep <1/0>
+
+DESCRIPTION
+	Those commands are used to send additional commands to the Marvell MLAN
+	card via the Linux device driver.
+
+	The mlanX parameter specifies the network device that is to be used to
+	perform this command on. It could be mlan0, mlan1 etc.
+
+11dcfg
+	This command is used to control 11D. No argument is used to get.
+
+	where value of n is:
+		0   -- Disable
+		1   -- Enable
+
+	Examples:
+		mlanutl mlan0 11dcfg 1           : Enable 11D
+		mlanutl mlan0 11dcfg             : Get 11D status
+
+11dclrtbl
+	This command is used to clear the 11D channel table.
+
+	Usage:
+		mlanutl mlanX 11dclrtbl
+
+addbapara
+	This command can be used to update the default ADDBA parameters.
+
+	where <m> is <timeout>
+	<timeout> - This is the block ack timeout for ADDBA request.
+		0 : Disable (recommended for throughput test)
+		1 - 65535 : Block Ack Timeout in TU
+
+	where <n> is <txwinsize>
+	<txwinsize> - Window size for ADDBA request. (16 is recommended and default value)
+
+	where <o> is <rxwinsize>
+	<rxwinsize> - Window size for ADDBA response. (48 is recommended and 32 is default value)
+	              (16 is recommended for IWNCOMM AP in WAPI throughput test)
+
+	Current window size limit for Tx as well as Rx is 1023.
+
+	where <p> is <txamsdu>
+	<txamsdu> - amsdu support for ADDBA request. (1 is default value)
+            0: disable amsdu in ADDBA request
+            1: enable amsdu in ADDBA request
+
+	where <q> is <rxamsdu>
+	<rxamsdu> - amsdu support for ADDBA response. (1 is default value)
+            0: disable amsdu in ADDBA response
+            1: enable amsdu in ADDBA response
+
+	eg:
+	mlanutl mlanX addbapara - This command will get the current addba params
+	mlanutl mlanX addbapara 1000 64 8 0 0 - This will change the ADDBA timeout to (1000 * 1024) us,
+			txwinsize to 64 and rxwinsize to 8 and disable amdsu in ADDBA request/response.
+
+	The default setting is 65535 16 32 1 1.
+
+	In case the ADDBA timeout value is updated then a ADDBA is sent for all streams
+	to update the timeout value.
+
+	In case txwinsize and/or rxwinsize is updated, the effect could only be seen on
+	next ADDBA request/response. The current streams will not be affected with this
+	change.
+
+	In case of txamsdu/rxamsdu is updated, the effect could only be seen on
+	next ADDBA request/response. The current streams will not be affected with this
+	change. AMSDU in AMPDU stream will be enabled when AP support this feature
+	and AMSDU is enabled in aggrpriotbl.
+
+addbareject
+	This command is used set/get the addbareject table for all the TIDs.
+	This command can also be used to enable rejection of ADDBA requests for a given tid.
+
+	where <m0> <m1> ... <m7>
+
+	<mX> - This can be 0/1 for TidX. 1 enables rejection of ADDBA request for TidX and
+		   0 would accept any ADDBAs for TidX.
+
+	eg:
+	mlanutl mlanX addbareject - This command will get the current table.
+	    [0 0 0 0 0 0 0 0]. ADDBA would be accepted for all TIDs. This is the default state.
+
+	mlanutl mlanX addbareject 0 0 1 1 0 0 0 0 - This command will accept ADDBA requests for
+		Tid [0,1,4,5,6,7] and reject ADDBA requests for Tid [2,3]
+
+	mlanutl mlanX addbareject 1 1 1 1 1 1 1 1 - This will enable rejection of ADDBA requests for
+		all Tids.
+
+	Note:- This command should only be issue in disconnected state.
+
+addts
+	Send an ADDTS command to the associated AP.
+
+	Process a given conf file for a specific TSPEC data block.  Send the
+	  TSPEC along with any other IEs to the driver/firmware for transmission
+	  in an ADDTS request to the associated AP.
+
+	Return the execution status of the command as well as the ADDTS response
+	  from the AP if any.
+
+	Usage:
+		mlanutl mlanX addts <filename.conf> <section# of tspec> <timeout(ms)>
+
+adhocaes
+	This command is used to set/get the AES key, when the station is in ad-hoc mode.
+	Note: This command is only available in disconnected state.
+
+	where value can be any 16 byte value.
+
+	Examples:
+		mlanutl mlan0 adhocaes           : Get ad-hoc aes key
+		mlanutl mlan0 adhocaes "1;12345678901234567890123456789012"
+		                                : Set ad-hoc aes key
+		mlanutl mlan0 adhocaes 2         : Clear ad-hoc aes key
+
+aggrpriotbl
+	This command is used set/get the priority table for AMPDU/AMSDU traffic per tid.
+	This command can also be used to disable AMPDU/AMSDU for a given tid.
+	In case of AMPDU this priority table will be used to setup block ack (to make
+	sure the highest priority tid always uses AMPDU as we have limited AMPDU streams)
+
+	where <m0> <n0> <m1> <n1> ... <m7> <n7>
+
+	<mx> - This is priority for Tid0 for AMPDU packet. A priority could be any
+		   values between 0 - 7, 0xff to disable aggregation.
+	<nx> - This is priority for Tid0 for AMSDU packet. A priority could be any
+		   values between 0 - 7, 0xff to disable aggregation.
+
+	eg:
+	mlanutl mlanX aggrpriotbl - This command will get the current Priority table for AMPDU and AMSDU.
+						  <2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255>. This is read as
+						  <"Prio for AMPDU for Tid0" "Prio for AMSDU for Tid0"
+						   "Prio for AMPDU for Tid1" "Prio for AMSDU for Tid1" and so on
+	mlanutl mlanX aggrpriotbl 2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255 -
+						This will set the priority table for AMPDU and AMSDU
+						Priority for Tid0/AMPDU = 2, Tid0/AMSDU = 2, Tid1/AMPDU = 0, Tid1/AMSDU = 0
+						and so on. Aggregation for Tid6 and Tid7 are disabled.
+						Here higher the priority number, higher the priority (i.e. 7
+						has higher priority than 6). Similarly for AMSDU.
+	mlanutl mlanX aggrpriotbl 0xff 2 0xff 0 0xff 1 0xff 3 0xff 4 0xff 5 0xff 0xff 0xff 0xff - This will disable
+						AMPDU for all the TIDs but will still keep AMSDU enabled to Tid0 to Tid5
+
+	The default setting is 2 255 0 255 1 255 3 255 4 255 5 255 255 255 255 255.
+
+	A delBA should be seen in case a disable happens on a TID for which AMPDU stream
+	is currently setup.
+
+	Note:- This command should only be issue in disconnected state.
+
+amsduaggrctrl
+	This command could be used to enable/disable a feature where firmware gives feedback to driver
+	regarding the optimal AMSDU buffer size to use with the current rate. Firmware will use the
+	current rate to decide the buffer size we could transmit. The max buffer size will still be
+	limited by buffer size provided in txbufcfg. (i.e. if the txbufcfg is 4K, then we could only transmit
+	4K/2K AMSDU packets, if the txbufcfg is 8K then we could transmit 8k/4k/2k based on current rate)
+
+	If enabled AMSDU buffer size at various rates will be as follows
+
+	1.	Legacy B/G rate.
+		No AMSDU aggregation.
+
+	2.	BW20 HT Rate:
+		When TX rate goes down,
+		MCS 7, 6, 5, 4:
+			a	8K aggregation size (if TX buffer size is 8K)
+			b	4K aggregation size (if TX buffer size is 4K)
+			c	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 3, 2:
+			a	4K aggregation size (if TX buffer size is 8K/4K)
+			b	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 1, 0:
+			a	No aggregation
+
+		When TX rate goes up,
+		MCS 7, 6, 5:
+			a	8K aggregation size (if TX buffer size is 8K)
+			b	4K aggregation size (if TX buffer size is 4K)
+			c	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 4, 3:
+			a	4K aggregation size (if TX buffer size is 8K/4K)
+			b	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 2, 1, 0:
+			a	No aggregation
+
+	3.	BW40 HT Rate:
+		When TX rate goes down,
+		MCS 7, 6, 5, 4, 3, 2, 1:
+			a	8K aggregation size (if TX buffer size is 8K)
+			b	4K aggregation size (if TX buffer size is 4K)
+			c	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 0:
+			a	No aggregation
+
+		When TX rate goes up,
+		MCS 7, 6, 5, 4, 3:
+			a	8K aggregation size (if TX buffer size is 8K)
+			b	4K aggregation size (if TX buffer size is 4K)
+			c	2K aggregation size (if TX buffer size is 2K)
+
+		MCS 2, 1, 0:
+			a	No aggregation
+
+	where <n> is 0/1 (for disable/enable)
+
+	eg:
+	mlanutl mlan0 amsduaggrctrl 1 - Enable this feature
+	mlanutl mlan0 amsduaggrctrl 0 - Disable this feature
+	mlanutl mlan0 amsduaggrctrl - This will get the enable/disable flag
+	and the current AMSDU buffer size). The AMSDU buffer size returned is only
+	valid after association as before association there is no rate info.
+
+	Note:- This command to enable/disable could be given anytime (before/after
+			association). This feature is enabled by default by the driver during
+			initialization.
+
+antcfg
+	This command is used to set/get the mode of Tx/Rx path.
+
+    For chip which support STREAM_2X2
+	where value of m is:
+		Bit 0   -- Tx Path A or Tx/Rx Path A if [n] is not provided
+		Bit 1   -- Tx Path B or Tx/Rx Path B if [n] is not provided
+		Bit 0-1 -- Tx Path A+B or Tx/Rx Path A+B if [n] is not provided
+	where value of n is:
+		Bit 0   -- Rx Path A
+		Bit 1   -- Rx Path B
+		Bit 0-1 -- Rx Path A+B
+	The Tx path setting (m) is used for both Tx and Rx if Rx path (n) is not provided.
+
+	Examples:
+		mlanutl mlan0 antcfg             : Get Tx and Rx path
+		mlanutl mlan0 antcfg 3           : Set Tx and Rx path to A+B
+		mlanutl mlan0 antcfg 1 3         : Set Tx path to A and Rx path to A+B
+
+    For chip which support SAD
+    	where value of m is:
+		Bit 0   -- Tx/Rx antenna 1
+		Bit 1   -- Tx/Rx antenna 2
+		...
+		0xFFFF  -- Tx/Rx antenna diversity
+
+        where value of n is:
+                SAD evaluate time interval, only be provided when m = 0xFFFF, default value is 6s(0x1770)
+
+	Examples:
+		mlanutl mlan0 antcfg               : Get Tx/Rx antenna mode
+		mlanutl mlan0 antcfg 1             : Set Tx/Rx antenna 1
+		mlanutl mlan0 antcfg 0xFFFF        : Set Tx/Rx antenna diversity
+		mlanutl mlan0 antcfg 0xFFFF 0x1770 : Set antenna evaluate time interval to 6s
+
+arpfilter
+	This command is used to configure the ARP filtering parameters.
+
+	Usage:
+		mlanutl mlanX arpfilter <arpfilter.conf>
+
+	Where the parameter is:
+		arpfilter.conf : The configuration file specifying ARP filtering parameters.
+
+	Example:
+		mlanutl mlan0 arpfilter config/arpfilter.conf
+
+assocessid
+	This command is used to assoc essid with asynced mode,
+	and driver will auto retry if driver auto assoc enabled.
+
+	Usage:
+		mlanutl mlanX assocessid <"[essid]">
+
+	Where
+	<"[essid]"> is the essid which need to be associated with asynced mode.
+
+	Examples:
+		mlanutl mlan0 assocessid "Marvell Micro AP"    : Associate to the ESSID "Marvell Micro AP"
+
+assocessid_bssid
+	This command is used to assoc AP by ssid/bssid pair with asynced mode,
+	and driver will auto retry if driver auto assoc enabled.
+
+	Usage:
+		mlanutl mlanX assocessid_bssid <"[bssid] [essid]">
+
+	Where
+	<"[bssid]"> is the bssid which need to be associated with asynced mode.
+	<"[essid]"> is the essid which need to be associated with asynced mode.
+
+	Examples:
+		mlanutl mlan0 assocessid_bssid "xx:xx:xx:xx:xx:xx Marvell Micro AP"   : Associate to the AP which ssid = "Marvell Micro AP", bssid = "xx:xx:xx:xx:xx:xx"
+
+associate
+	Request an association to a given SSID/BSSID pair. This the only accurate
+	way to pick a specific AP and ESS for an association. The entry must
+	already exist in the scan table for the association to be attempted.
+
+	mlanutl mlanX associate "xx:xx:xx:xx:xx:xx SSID"
+
+authtype
+	This command is used to set/get authentication type.
+
+	Usage:
+		mlanutl mlanX authtype [n]
+
+	where <n>
+		0: 802.11 open system authentication
+		1: 802.11 shared key authentication
+		255: allow open system or shared key authentication (default)
+
+	Examples:
+		mlanutl mlan0 authtype 0         : use open system authentication
+		mlanutl mlan0 authtype 1         : use shared key authentication
+		mlanutl mlan0 authtype 255       : allow open system or shared key authentication
+		mlanutl mlan0 authtype           : get current setting
+
+autotdls
+	This command is used to enable/disable auto TDLS.
+
+	Usage:
+		mlanutl mlanX autotdls [n]
+
+	where <n>
+		0: Disable auto tdls
+		1: Enable auto tdls
+
+	Examples:
+		mlanutl mlan0 autotdls 1         : enable auto TDLS
+		mlanutl mlan0 autotdls 0         : disable auto TDLS
+		mlanutl mlan0 autotdls           : get current setting
+
+dyn_bw
+	This command is used to set/get dynamic bandwidth.
+
+	Usage:
+		mlanutl mlanX dyn_bw [n]
+
+	where <n>
+           [BIT0]
+                 0 = TxInfo Indicated BW Disable
+                 1 = TxInfo Indicated BW Enable
+           [BIT1]
+                 0 = TxInfo Dynamatic BW Disable
+                 1 = TxInfo Dynamatic BW Enable
+           [BIT2]
+                 0 = TxInfo Force send RTS Disable
+                 1 = TxInfo Force send RTS Enable
+           [BIT3]
+                 0 = Mac Dynamic BW Operation Mode Disable (Static BW Operation Mode)
+                 1 = Mac Dynamic BW Operation Mode Enable
+           other bits reserved.
+
+        If no parameter provided, get is performed.
+
+	Examples:
+		mlanutl mlan0 dyn_bw 0x1       : Enable TxInfo Indicated BW
+		mlanutl mlan0 dyn_bw           : get current setting
+
+tdls_idle_time
+	This command is used to set/get TDLS idle timeout. The valid value is between 0-0xffff. When set to 0, the tdls_idle_time will use default value(60).
+
+	Usage:
+		mlanutl mlanX tdls_idle_time [n]
+
+	where <n>
+		TDLS idle timeout value
+
+	Examples:
+		mlanutl mlan0 tdls_idle_time 30        : set tdls_idle_time value to 30
+		mlanutl mlan0 tdls_idle_time 0         : use default tdls_idle_time value(60)
+		mlanutl mlan0 tdls_idle_time           : get current setting
+
+dfs_offload
+	This command is used to enable/disable DFS offload. The valid value is 0/1.
+	Note: The parameters can be set only in disconnected state.
+
+	Usage:
+		mlanutl uapX dfs_offload [n]
+
+	where <n>
+		Enable/disable
+
+	Examples:
+		mlanutl uap0 dfs_offload 1         : enable DFS offload
+		mlanutl uap0 dfs_offload 0         : disable DFS offload
+
+bandcfg
+	This command is used to set/get infra/ad-hoc band.
+	Note: This command is only available in disconnected state.
+
+	Usage:
+		mlanutl mlanX bandcfg [l] [m] [n] [o]
+
+	where the parameters:
+		[l]: Infrastructure band
+		     bit 0: B
+		     bit 1: G
+		     bit 2: A
+		     bit 3: GN
+		     bit 4: AN
+
+		     bit 5: AC 2.4G
+		     bit 6: AC 5G
+		[m]: Ad-hoc start band
+		     bit 0: B
+		     bit 1: G
+		     bit 2: A
+		     bit 3: GN
+		     bit 4: AN
+		     bit 5: AC 2.4G
+		     bit 6: AC 5G
+		[n]: Ad-hoc start channel
+		[o]: 0 - Bandwidth 20Mhz
+		     1 - HT Bandwidth 40Mhz above
+		     3 - HT Bandwidth 40Mhz below
+			 4 - VHT Bandwidth 80Mhz
+	Examples:
+		mlanutl mlan0 bandcfg            : Get infra/ad-hoc band and ad-hoc
+		                                  start channel configurations
+		mlanutl mlan0 bandcfg 1          : Set infra band to B only
+		mlanutl mlan0 bandcfg 3 2 6      : Set infra band to B/G, ad-hoc start band
+		                                  to G and ad-hoc start channel to 6
+		mlanutl mlan0 bandcfg 7 11 6 1   : Set infra band to B/G/A, ad-hoc start band
+		                                  to B/G/GN, ad-hoc start channel to 6 and
+		                                  secondary channel to above
+
+bcninterval
+	This command is used to set/get the beacon interval in ad-hoc mode.
+	The valid beacon interval is between 20 - 1000, default beacon
+	interval is 100.
+
+	Where <n>
+		Beacon interval in TU (Time Unit: 1024 us).
+
+	Examples:
+		mlanutl mlan0 bcninterval 200    : Set ad-hoc beacon interval to 200
+		mlanutl mlan0 bcninterval        : Get ad-hoc beacon interval
+
+bssrole
+	This command is used to set/get the BSS role.
+
+	Where
+	[l] is <bss_role>
+	<bss_role> - This parameter specifies the BSS role to set.
+		0       : STA
+		1       : uAP
+
+	Examples:
+		mlanutl wfd0 bssrole                 : Get the current BSS role
+		mlanutl wfd0 bssrole 1               : Set the current BSS role to uAP
+
+cfgdata
+	This command is used to set/get the configuration data to/from firmware.
+
+	Usage:
+		mlanutl mlanX cfgdata <type> [<.conf file name>]
+
+	Where the parameters are:
+		type :
+		        2 -- CAL data download and <.conf file name> is cal_data.conf
+		.conf file name : The configuration file used to set/get the configuration data.
+
+	Examples:
+		mlanutl mlan0 cfgdata 2
+			: This command is used to get and display the CAL data from firmware.
+
+cfpcode
+	This command is used to set/get the Channel-Frequency-Power table codes.
+	The region table can be selected through region code.
+	The current configuration is returned if no parameter provided.
+
+	where the parameters are,
+		[m]: code of the CFP table for 2.4GHz (0: unchanged)
+		[n]: code of the CFP table for 5GHz (0 or not provided: unchanged)
+
+	Examples:
+		mlanutl mlan0 cfpcode            : Get current configuration
+		mlanutl mlan0 cfpcode 0x30       : Set 2.4GHz CFP table code 0x30 (EU),
+		                                  keep 5GHz table unchanged
+		mlanutl mlan0 cfpcode 0x10 5     : Set 2.4GHz CFP table code 0x10 (USA)
+		                                  and 5GHz table code 5
+
+changraph
+	Displays 2-dimensional graph, plotting channel number along x-axis and
+	anpi or channel-load along y-axis, depending on whether it is an anpi graph
+	or a channel load graph.
+
+	Usage:
+		mlanutl mlanX changraph [<load | anpi | anpiload> <loops>]
+		where:
+			load:       Only channel vs channel-load graph is displayed
+			anpi:       Only channel vs Average Noise Power Indicator(ANPI)
+						graph is displayed
+			anpiload:   Both the graphs for anpi and for the load are displayed
+			loops:      This is used to calculate the number of times
+						the graph [load or anpi or both] will be printed
+
+coex_rx_winsize
+	This command is used to set/get control to coex RX window size
+
+	where value of m is:
+		0       -- Disable COEX RX winsize (default)
+		1       -- Enable COEX RX winsize
+
+	Examples:
+		mlanutl mlan0 coex_rx_winsize             : Get COEX RX winsize
+		mlanutl mlan0 coex_rx_winsize 1           : Enable COEX RX winsize
+
+countrycode
+	This command is used to set and get the country code.
+
+	Where
+	[l] is Country code
+
+	Examples:
+		mlanutl mlan0 countrycode            : Get current countrycode
+		mlanutl mlan0 countrycode CN         : Set countrycode as China (CN)
+
+cfpinfo
+	This command is used to get region, country, environment codes,
+	channel and power table information from the FW.
+
+	Examples:
+		mlanutl mlan0 cfpinfo		     : Display cfp tables
+		mlanutl uap0 cfpinfo
+
+acs
+	This command is used to issue acs scan, then FW will
+	report the best channel and channel statistics.
+	User could specify channels or not. If channel is not
+	specified, acs scan will be on all supported channels.
+
+	Examples:
+		mlanutl mlan0 acs		     : ACS scan on all supported channels
+		mlanutl uap0 acs		     : ACS scan on all supported channels
+		mlanutl mlan0 acs 5 6 7 8	     : ACS scan on 5 6 7 8 channels
+		mlanutl uap0 acs 1 2 3		     : ACS scan on 1 2 3 channels
+
+customie
+	This command is used to set or get custom IEs for management frames.
+
+	Usage:
+		mlanutl mlanX customie [[[<index>] <mask>] <IEBuffer>]
+
+	Where the parameter is:
+		empty - Get all IE settings
+		<index> :   0 - Get/Set IE index 0 setting
+		            1 - Get/Set IE index 1 setting
+		            2 - Get/Set IE index 2 setting
+		           MAX IE Index depends on device memory.
+		           -1 - Append/Delete IE automatically
+		                Delete will delete the IE from the matching IE buffer
+		                Append will append the IE to the buffer with the same mask
+		<mask>      :  Management subtype mask value as per bit definitions
+		            :  Bit 0 - Association request
+		            :  Bit 1 - Association response
+		            :  Bit 2 - Reassociation request
+		            :  Bit 3 - Reassociation response
+		            :  Bit 4 - Probe request
+		            :  Bit 5 - Probe response
+		            :  Bit 8 - Beacon
+		<mask>      :  mask = 0 to clear the mask and the IE buffer
+		<IEBuffer>  :  IE Buffer in hex (max 256 bytes)
+		               The Buffer should not be space separated.
+
+	Examples:
+		mlanutl mlan0 customie
+			: Get IE buffer, subtype mask settings for all indices.
+
+		mlanutl mlan0 customie 1
+			: Get IE buffer and subtype mask for the Index = 1.
+
+		mlanutl mlan0 customie 2 0
+			: Clear IE buffer and mask value for Index = 2.
+
+		mlanutl mlan0 customie 3 0x101 0xdd051234567890
+			: Set IE buffer and mask value for Index = 3.
+
+		mlanutl mlan0 customie -1 0x101 0xdd051234567890
+			: Append the specified IEBuffer at index with mask value of 0x101.
+
+		mlanutl mlan0 customie -1 0 0xdd051234567890
+			: Delete the specified IEBuffer from all the IEs.
+
+		mlanutl mlan0 customie 2 0 0xdd051234567890
+			: Delete the specified IEBuffer from the IEs at index 2.
+
+deauth
+	This command is used to send a de-authentication to an arbitrary AP.
+	If [l] is omitted, the driver will deauth the associated AP.
+	If in ad-hoc mode this command is used to stop beacon transmission
+	from the station and go into idle state.
+
+	When <l> is supplied as a MAC address, the driver will deauth the
+	  specified AP.  If the AP address matches the driver's associated AP,
+	  the driver will disconnect. Otherwise, the driver remains connected.
+
+	When this command is executed on AP interface, it is used to send
+	a de-authentication to associated station.
+
+deepsleep
+	This command is used to set/get auto deep sleep mode.
+
+	Usage:
+		mlanutl mlanX deepsleep [l] [m]
+
+	where the parameters are:
+		[l]: Enable/disable auto deep sleep mode (1/0)
+		[m]: Idle time in milliseconds after which firmware will put the device
+		     in deep sleep mode. Default value is 100 ms.
+
+	Examples:
+		mlanutl mlan0 deepsleep          : Display auto deep sleep mode
+		mlanutl mlan0 deepsleep 1        : Enable auto deep sleep mode, idle time unchanged
+		mlanutl mlan0 deepsleep 0        : Disable auto deep sleep mode
+		mlanutl mlan0 deepsleep 1 500    : Enable auto deep sleep mode with idle time 500 ms
+        Note:
+            Deepsleep must be disabled before changing idle time.
+
+delba
+	This command is used to delete either all Tx BA or all Rx BA or a specific BA stream
+	based on direction, TID and peer address.
+
+	where <l> [<m> <n>]
+		<l> - This is the direction of BA stream, Tx (bit 0), Rx (bit 1).
+		<m> - This is the TID (0-7, 0xff for all) of BA stream.
+		<n> - This is the peer MAC addres of BA stream.
+
+	eg:
+	mlanutl mlanX delba 2 - This command will delete all the Rx BA streams.
+	mlanutl mlanX delba 3 - This command will delete all the Tx and Rx BA streams.
+	mlanutl mlanX delba 1 0 - This command will delete all the Tx streams with TID 0.
+	mlanutl mlanX delba 2 0xff "00:11:22:33:44:55" - This command will delete all the Rx BA streams
+		with specified peer MAC address
+	mlanutl mlanX delba 1 3 "00:11:22:33:44:55" - This command will delete the Tx BA stream with
+		TID 3 and specified peer MAC address.
+
+delts
+	Send a DELTS command to the associated AP.
+
+	Process a given conf file for a specific TSPEC data block.  Send the
+	  TSPEC along with any other IEs to the driver/firmware for transmission
+	  in a DELTS request to the associated AP.
+
+	Return the execution status of the command.  There is no response to a
+	  DELTS from the AP.
+
+	Usage:
+		mlanutl mlanX delts <filename.conf> <section# of tspec>
+
+dfstesting
+	This command is used to set/get settings for DFS testing.
+
+	Usage:
+	    mlanutl mlanX dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>]
+
+	where <user_cac_pd> is user-configured Channel Availability Check in msec
+                        0 = disable, use default period (60000)
+                        1-65535 = enable with that period
+	where <user_nop_pd> is user-configured Non-Occupancy Period in sec
+                        0 = disable, use default period (1800)
+                        1-65535 = enable with that period
+	where <no_chan_change> is enable/disable no channel change on radar
+                           0 = disable, 1 = enable (overrides below)
+	where <fixed_chan_num> is user-configured channel to change to on radar
+                           0 = disable, 1-255 = enable with that channel
+                           (channel validity for region, etc. is not checked)
+                           (only takes effect if no_chan_change = 0)
+
+	Examples:
+		mlanutl mlan0 dfstesting             : Get current dfstesting settings
+		mlanutl mlan0 dfstesting 2000 0 0 0  : user_cac=2sec, others disabled/default
+		mlanutl mlan0 dfstesting 0 0 1 0     : only no_chan_change enabled
+		mlanutl mlan0 dfstesting 0 120 0 64  : user_nop=2min, force chan 64 on radar
+
+drvdbg
+	This command is used to set/get the bit masks of driver debug message control.
+
+	Usage:
+		mlanutl mlanX drvdbg [n]
+
+	Where the parameter <n> is the generic debug message control bit mask.
+	The following types of driver debug messages can be dynamically enabled or
+	disabled by setting or clearing the corresponding bits,
+		bit 0:  MMSG            PRINTM(MMSG,...)
+		bit 1:  MFATAL          PRINTM(MFATAL,...)
+		bit 2:  MERROR          PRINTM(MERROR,...)
+		bit 3:  MDATA           PRINTM(MDATA,...)
+		bit 4:  MCMND           PRINTM(MCMND,...)
+		bit 5:  MEVENT          PRINTM(MEVENT,...)
+		bit 6:  MINTR           PRINTM(MINTR,...)
+		bit 7:  MIOCTL          PRINTM(MIOCTL,...)
+		...
+		bit 16: MDAT_D          PRINTM(MDAT_D,...), DBG_HEXDUMP(MDAT_D,...)
+		bit 17: MCMD_D          PRINTM(MCMD_D,...), DBG_HEXDUMP(MCMD_D,...)
+		bit 18: MEVT_D          PRINTM(MEVT_D,...), DBG_HEXDUMP(MEVT_D,...)
+		bit 19: MFW_D           PRINTM(MFW_D,...),  DBG_HEXDUMP(MFW_D,...)
+		bit 20: MIF_D           PRINTM(MIF_D,...),  DBG_HEXDUMP(MIF_D,...)
+		...
+		bit 28: MENTRY          PRINTM(MENTRY,...), ENTER(), LEAVE()
+		bit 29: MWARN           PRINTM(MWARN,...)
+		bit 30: MINFO           PRINTM(MINFO,...)
+
+	If CONFIG_DEBUG=2, all kinds of debug messages can be configured.
+
+	If CONFIG_DEBUG=1, all kinds of debug messages can be configured except
+	for MENTRY, MWARN and MINFO. By default MMSG, MFATAL and MERROR are enabled.
+
+	Some special debug messages,
+		'*'             // MLAN driver ISR is called (bit 6 MINTR enabled)
+		'|'             // PS awake event is received (bit 5 MEVENT enabled)
+		'_'             // PS sleep event is received (bit 5 MEVENT enabled)
+		'+'             // PS sleep confirm is sent (bit 5 MEVENT enabled)
+
+	Examples:
+		mlanutl mlan0 drvdbg             : Get the current driver debug masks
+		mlanutl mlan0 drvdbg 0           : Disable all the debug messages
+		mlanutl mlan0 drvdbg 7           : Enable MMSG, MFATAL and MERROR messages
+		mlanutl mlan0 drvdbg 0x20037     : Enable MMSG, MFATAL, MEEROR,
+		                                   MCMND, MEVENT and MCMD_D messages
+		mlanutl mlan0 drvdbg -1          : Enable all the debug messages
+
+esuppmode
+	This command is used to set/get the e-supplicant mode configurations/status.
+
+	Note: The configurations can be set only before association.
+	      For get, the configurations will be returned before association
+	      and the current status will be returned after association.
+
+	Where
+	[l] is <rsn_mode>
+	<rsn_mode> - This parameter specifies the RSN mode configuration
+		Bit 0    : No RSN
+		Bit 1-2  : RFU
+		Bit 3    : WPA
+		Bit 4    : WPA-NONE
+		Bit 5    : WPA2
+		Bit 6-15 : RFU
+	[m] is <pairwise_cipher>
+	<pairwise_cipher> - This parameter specifies the pairwise cipher
+		Bit 0    : RFU
+		Bit 1    : RFU
+		Bit 2    : TKIP
+		Bit 3    : AES
+		Bit 4-7  : RFU
+	[n] is <group_cipher>
+	<group_cipher> - This parameter specifies the group cipher
+		Bit 0    : RFU
+		Bit 1    : RFU
+		Bit 2    : TKIP
+		Bit 3    : AES
+		Bit 4-7  : RFU
+	Note that: the RFU bits cannot be SET.
+
+	Examples:
+		mlanutl mlan0 esuppmode          : Get RSN mode and pairwise/group cipher
+		mlanutl mlan0 esuppmode 8 4 4    : Set RSN mode yo WPA, active pairwise and
+		                                   group ciphers to TKIP
+
+extcapcfg
+	This command is used to set/get extended capabilities.
+
+	Usage:
+		mlanutl mlanX extcapcfg [<ext_cap>]
+
+	where <ext_cap> :  Extended capabilities in hex (max 9 bytes)
+		               The Buffer should not be space separated.
+
+	Examples:
+		mlanutl mlan0 extcapcfg 0x0000008020	: Set TDLS support and Interworking bits.
+
+fwmacaddr
+	This command is used to set/get FW side MAC address but host side address will remain as earlier.
+
+	Usage:
+		mlanutl mlanX fwmacaddr [mac_addr]
+
+	where <mac_addr> is desired MAC address
+
+	Examples:
+		mlanutl mlan0 fwmacaddr                        : Get current FW MAC address
+		mlanutl mlan0 fwmacaddr 00:50:43:20:bc:44      : Set FW side MAC address
+
+getdatarate
+	This command is used to get the data rate being used in last Tx
+	packet and last Rx packet.
+
+getkey
+	This command is used to get PTK/GTK
+
+	mlanutl mlanX getkey
+
+getlog
+	This command is used to get the statistics available in the station.
+	Following stats are displayed:-
+	dot11MulticastTransmittedFrameCount	Increments when the multicast bit is set in the destination
+						MAC address of a successfully transmitted MSDU
+
+	dot11FailedCount			Increments when an MSDU is not transmitted successfully
+
+	dot11RetryCount				Increments when an MSDU is successfully transmitted after one
+						or more retransmissions
+
+	dot11MultipleRetryCount			Increments when an MSDU is successfully transmitted after more
+						than one retransmission
+
+	dot11FrameDuplicateCount		Increments when a frame is received that the Sequence Control
+						field is indicating a duplicate count
+
+	dot11RTSSuccessCount			Increments when a CTS is received in response to an RTS
+
+	dot11RTSFailureCount			Increments when a CTS is not received in response to an RTS
+
+	dot11ACKFaliureCount			Increments when an ACK is not received when expected
+
+	dot11ReceivedFragmentCount		Increments for each successfully received MPDU of type Data or Management
+
+	dot11MulticastReceivedFrameCount	Increments when a MSDU is received with the multicast bit set in the destination MAC address
+
+	dot11FCSErrorCount			Increments when an FCS error is detected in a received MPDU
+
+	dot11TransmittedFrameCount		Increments for each successfully transmitted MSDU
+
+	dot11WeplcvErrCnt			Increment when WEP decryption error for key index 0.3
+
+	beaconReceivedCnt			Increments when received beacon
+
+	beaconMissedCnt				Increments when beacon missed
+
+	dot11TransmittedFrameCount		Increments for each successfully transmitted MSDU
+
+	dot11QosTransmittedFragmentCount	Increments when a corresponding UP's MPDU transmitted successfully
+
+	dot11QosFailedCount			Increments when a corresponding UP's MSDU not transmitted successfully
+
+	dot11QosRetryCount			Increment when a corresponding UP's MSDU transmitted successfully after one or more retransmission
+
+	dot11QosMultipleRetryCount		Increments when a corresponding UP's MSDU is successfully transmitted after more than one retransmission
+
+	dot11QosFrameDuplicateCount		Increments when a corresponding UP's frame is received that the Sequence
+						Control field is indicating a duplicate frame
+
+	dot11QosRTSSuccessCount			Increments when a CTS is received in response to an RTS, which is sent for a corresponding UP's Qos frame
+
+	dot11QosRTSFailureCount			Increments when a CTS is not received in response to an RTS, which is sent for a corresponding UP's
+						Qos frame
+
+	dot11QosACKFailureCount			Increments when an ACK is not received when expected for a corresponding UP's Qos frame
+
+	dot11QosReceivedFragmentCount		Increments when a corresponding UP's MPDU received
+
+	dot11QosTransmittedFrameCount		Increments when a corresponding UP's MSDU transmitted
+
+	dot11QosDiscardedFrameCount		Increments when a corresponding UP's MSDU not transmitted successfully
+
+	dot11QosMPDUsReceivedCount		Increments when a corresponding UP's MDPU received
+
+
+	dot11QosRetriesReceivedCount		Increments when a corresponding UP's MDPU received which retry bit is set
+
+	dot11RSNAStatsCMACICVErrors		Increment when a MPDU discard by CMAC integrity check
+
+	dot11RSNAStatsCMACReplays		Increments when a MPDU discarded by the CMAC replay error
+
+	dot11RSNAStatsRobustMgmtCCMPReplays	Increments when a robust management frame discarded by CCMP replay error
+
+	dot11RSNAStatsTKIPICVErrors		Increments when a MPDU discarded by TKIP ICV error
+
+	dot11RSNAStatsTKIPReplays		Increments when a MPDU discarded by TKIP replay error
+
+	dot11RSNAStatsCCMPDecryptErrors		Increments when a MPDU discarded by CCMP decryption error
+
+	dot11RSNAStatsCCMPReplays		Increments when a MPDU discarded by CCMP replay error
+
+	dot11TransmittedAMSDUCount		Increments when a A-MSDU transmitted successfully
+
+	dot11FailedAMSDUCount			Increments when a A-MSDU not transmitted successfully
+
+	dot11RetryAMSDUCount			Increments when a A-MSDU is successfully transmitted after one or more retransmissions
+
+	dot11MultipleRetryAMSDUCount		Increments when a A-MSDU is successfully transmitted after more than one retransmissions
+
+	dot11TransmittedOctetsInAMSDUCount	Increments by the number of octets in the frame body
+						of an A-MSDU frame when an A-MSDU frame is successfully transmitted
+
+	dot11AMSDUAckFailureCount		Increments when an acknowledgment to an A-MSDU is not received when expected.
+
+	dot11ReceivedAMSDUCount			Increments when a A-MSDU frame received
+
+	dot11ReceivedOctetsInAMSDUCount		Increments by the number of octets in the frame body
+						of an A-MSDU frame when an A-MSDU frame is received
+
+	dot11TransmittedAMPDUCount		Increments when an A-MPDU is transmitted
+
+	dot11TransmittedMPDUsInAMPDUCount	Increments by the number of MPDUs in the A-MPDU when an A-MPDU is transmitted
+
+	dot11TransmittedOctetsInAMPDUCount	Increments by the number of octets in the A-MPDU frame when an A-MPDU frame is transmitted
+
+	dot11AMPDUReceivedCount			Increments when the MAC receives an A-MPDU from the PHY
+
+	dot11MPDUInReceivedAMPDUCount		Increments by the number of MPDUs received in the
+						A-MPDU when an A-MPDU is received
+
+	dot11ReceivedOctetsInAMPDUCount		Increments by the number of octets in the A-MPDU
+						frame when an A-MPDU frame is received
+
+	dot11AMPDUDelimiterCRCErrorCount	Increments when an MPDU delimiter has a CRC error when this is the first
+						CRC error in the received A-MPDU or when the previous delimiter has been decoded correctly
+
+getscantable
+	Display the current contents of the driver scan table
+
+	Usage:
+		mlanutl mlanX getscantable
+		mlanutl mlanX getscantable [#]
+		mlanutl mlanX getscantable tsf
+		mlanutl mlanX getscantable ch
+		mlanutl mlanX getscantable help
+
+	1) Without argument, the entire scantable is displayed in terms of channel (ch), signal strength (ss), BSS id (bssid), capability (cap), and SSID,
+		where each column in the capability is described as follows:
+		column 1 indicates the IBSS capability: A (Adhoc), I (Infra)
+		column 2 indicates the encryption capability: P (WEP), W (WPA), 2 (WPA2)
+		column 3 indicates the 11D capability: D (11D)
+		column 4 indicates the WMM capability: W (WMM), C (CAC)
+		column 5 indicates the 11K capability: K (11K)
+		column 6 indicates the 11R capability: R (11R)
+		column 7 indicates the WPS capability: S (WPS)
+		column 8 indicates the 11N/11AC capability: N (11N), A (11AC)
+
+	2) Specifying a # will display detailed information about a specific scan
+	   table entry.  '0' displays driver cached information regarding the
+	   current association (if any).
+	3) The tsf argument will display the entire scan table with the recorded
+	   TSF timestamp for the entry.
+	4) The ch argument will display the entire scan table sorted by channel
+	   number in the ascending order. If this argument is not specified,
+           scan table is sorted by signal strength in the descending order.
+	6) The help argument will display the legend for the capability field.
+
+getsignal
+	This command gets the last and average value of RSSI, SNR and NF of
+	Beacon and Data.
+	Note: This command is available only when STA is connected.
+
+	where value of m is:
+		1   -- RSSI (Receive Signal Strength Indication)
+		2   -- SNR (Signal to Noise Ratio)
+		3   -- NF (Noise Floor)
+	where value of n is:
+		1   -- Beacon last
+		2   -- Beacon average
+		3   -- Data last
+		4   -- Data average
+
+	Examples:
+		mlanutl mlan0 getsignal 1        : Get the RSSI info (beacon last, beacon
+		                                  average, data last and data average)
+		mlanutl mlan0 getsignal 3 4      : Get the NF of data average
+		mlanutl mlan0 getsignal 2 1      : Get the SNR of beacon last
+		mlanutl mlan0 getsignal          : Get all of the signal info
+		mlan0     getsignal:-32  -33  -35  -36  67  59  63  56  -99  -92  -98  -92
+		RSSI info: beacon last -32, beacon average -33, data last -35, data average -36
+		SNR info: beacon last 67, beacon average 59, data last 63, data average 56
+		NF info: beacon last -99, beacon average -92, data last -98, data average -92
+
+getsignalext
+	This command gets the last and average value of RSSI, SNR and NF of
+	Beacon and Data of spectific antenna path.
+	Note: This command is available only when STA is connected.
+
+	where value of m is:
+		1   -- PATH A
+		2   -- PATH B
+		3   -- PATH A+B
+
+	Examples:
+        	mlanutl mlan0 getsignalext          :Get All path's signal.
+        	mlanutl maln0 getsignalext 3        :Get path A+B signal
+        	mlanutl maln0 getsignalext 1        :Get path A signal
+		mlanutl mlan0 getsignalext 2        :Get path B signal
+
+        PATH A: -46 -46 -49 -49 65 45 39 42 -111 -91 -88 -91
+
+	PATH A: RSSI info: beacon last -46, beacon average -46, data last -49, data average -49
+		SNR info: beacon last 65, beacon average 45, data last 39, data average 42
+		NF info: beacon last -111, beacon average -91, data last -88, data average -91
+
+signalextcfg
+	This command is used to enable/disable signalext
+	Usage:
+    		mlanutl mlanX signalextcfg [m]
+	where the value of [m] is:
+		1 -- enable signalext in firmware
+		0 -- disable signalext in firmware
+	Examples:
+		mlanutl mlan0 signalextcfg 1 : Enable signalext in firmware
+		mlanutl mlan0 signalextcfg 0 : Disable signalext in firmware
+
+getsignalextv2
+	This command gets the last and average value of RSSI, SNR and NF of
+	Beacon and Data of spectific antenna path.
+	Note: This command is available only when STA is connected.
+
+	where value of m is:
+		1   -- PATH A
+		2   -- PATH B
+		3   -- PATH A+B
+
+	Examples:
+        	mlanutl mlan0 getsignalext          :Get All path's signal.
+        	mlanutl maln0 getsignalext 3        :Get path A+B signal
+        	mlanutl maln0 getsignalext 1        :Get path A signal
+		mlanutl mlan0 getsignalext 2        :Get path B signal
+
+        PATH A: -46 -46 -49 -49 65 45 39 42 -111 -91 -88 -91
+
+	PATH A: RSSI info: beacon last -46, beacon average -46, data last -49, data average -49
+		SNR info: beacon last 65, beacon average 45, data last 39, data average 42
+		NF info: beacon last -111, beacon average -91, data last -88, data average -91
+
+getstalist
+    This command is used to get list of associated stations to the AP.
+
+    Example:
+        mlanutl uap0 getstalist
+
+channel_switch <switch mode> <oper class> <new channel> <switch count> <bandwidth>
+    This command is used to do channel switch according to spec.
+
+    Where the paramters are:
+        switch mode : 0 -- no need to block traffic, 1 -- need block traffic
+        oper class  : operating class according to IEEE std802.11 spec, when 0 is used, only CSA IE will be used
+        new channel : the channel will switch to
+        switch count: channel switch time to send ECSA ie
+        bandwidth   : channel width switch to(optional),only for 5G channels.
+                      Support value 1 -- 40M above, 3 -- 40M below, 4 -- 80M, 5 -- 160M
+
+    Example:
+        mlanutl uap0 channel_switch 1 115 36 10      :switch to channel 36, oper class 115
+        mlanutl uap0 channel_switch 1 81 6 10        :switch to channel 6, oper class 81
+        mlanutl uap0 channel_switch 1 0 6 10         :switch to channel 6
+        mlanutl uap0 channel_switch 1 0 36 10 1      :switch to channel 36, bandwidth 40MHz above
+
+hostcmd 2040coex
+	This command is used to send the 11n 20/40 Coex command to firmware.
+	Firmware will send 11n 20/40 Coex management action frame to AP.
+
+	Usage:
+		mlanutl mlanX hostcmd config/11n_2040coex.conf 2040coex
+
+hostcmd auto_tx_get
+hostcmd auto_tx_unreg
+	This command is used to configures the Frame Auto Transmission parameters.
+	auto_tx_get: get auto_tx parameters
+	auto_tx_unreg: unregister to firmware auto_tx
+
+	Usage:
+		mlanutl mlanX hostcmd config/auto_tx.conf auto_tx_get
+		mlanutl mlanX hostcmd config/auto_tx.conf auto_tx_unreg
+
+hostcmd bgscfg
+	This command is used to configure the various parameters for PPS/UAPSD
+	or normal background scan.
+
+	Usage:
+		mlanutl mlanX hostcmd config/bg_scan.conf bgscfg
+
+hostcmd <pkt_coalescing.conf> coalesce_cfg
+	This command is used to set/clear rules to filter and buffer
+	broadcast/multicast packet which reduce unwanted patcket or interrupt to
+	host.
+
+	Usage:
+		mlanutl mlanX hostcmd <pkt_coalescing.conf> coalesce_cfg
+
+hostcmd <ed_mac_ctrl.conf> ed_mac_ctrl
+	This command is used to control ED MAC.
+
+	Usage:
+		mlanutl mlanX hostcmd <ed_mac_ctrl.conf> ed_mac_ctrl
+
+hostcmd crypto_test
+	This command is used to test the encryption/decryption API of the firmware.
+
+	Usage:
+		mlanutl mlanX hostcmd config/crypto_test.conf crypto_test
+
+hostcmd nat_keep_alive
+	This command is used to configures the Frame Auto Transmission parameters.
+	nat_keep_alive: register to firmware for sending NAT Keep Alive packet
+
+	Usage:
+		mlanutl mlanX hostcmd config/auto_tx.conf nat_keep_alive
+
+hostcmd pad_cfg_get
+hostcmd pad_cfg_set
+	This command is used to set/get the configuration data for PAD OR.
+
+	Usage:
+		mlanutl mlanX hostcmd config/pad_cfg.conf pad_cfg_get
+		mlanutl mlanX hostcmd config/pad_cfg.conf pad_cfg_set
+
+hostcmd requesttpc
+	This command is used to request 802.11H TPC info.
+
+	Usage:
+		mlanutl mlanX hostcmd config/requesttpc.conf requesttpc
+
+hostcmd mode_get
+hostcmd mode_timeshare
+hostcmd mode_spatial
+hostcmd mode_none
+	This command is used to get/set Robust BT Coex.
+	mode_get:       get the current mode
+	mode_timeshare: set Robust BT Coex to timeshare mode  (default on 1x1 chips)
+	mode_spatial:   set Robust BT Coex to spatial mode    (only for, and default on 2x2 chips)
+	mode_none:      set Robust BT Coex to mode none       (only for, and default on 2x2_3Antenna chips)
+
+	Usage:
+		mlanutl mlanX hostcmd config/robust_btc.conf mode_get
+		mlanutl mlanX hostcmd config/robust_btc.conf mode_timeshare
+		mlanutl mlanX hostcmd config/robust_btc.conf mode_spatial
+		mlanutl mlanX hostcmd config/robust_btc.conf mode_none
+
+hostcmd gpio_cfg
+	This command is used to enable/disable GPIO cfg.
+	gpio_cfg:       enable/disable GPIO cfg for external bt request  (default is enable with High Polarity)
+
+	Usage:
+		mlanutl mlanX hostcmd config/robust_btc.conf gpio_cfg
+
+hostcmd generictime
+hostcmd a2dptime
+hostcmd inquirytime
+hostcmd ap_generictime
+hostcmd ap_a2dptime
+hostcmd ap_inquirytime
+        This command is used to configure the time slice of COEX (only works in timeshare mode)
+        generictime:       configure the Bttime and Wlantime in Station Generic case
+        a2dptime:          configure the Bttime and Wlantime in Station A2DP case
+        inquirytime:       configure the Bttime and Wlantime in Station Inquiry case
+        ap_generictime:    configure the Bttime and Wlantime in Ap Generic case
+        ap_a2dptime:       configure the Bttime and Wlantime in Ap A2DP case
+        ap_inquirytime:    configure the Bttime and Wlantime in Ap Inquiry case
+
+    Usage:
+                mlanutl mlanX hostcmd config/robust_btc.conf generictime
+                mlanutl mlanX hostcmd config/robust_btc.conf a2dptime
+                mlanutl mlanX hostcmd config/robust_btc.conf inquirytim
+                mlanutl mlanX hostcmd config/robust_btc.conf ap_generictime
+                mlanutl mlanX hostcmd config/robust_btc.conf ap_a2dptime
+                mlanutl mlanX hostcmd config/robust_btc.conf ap_inquirytime
+
+hostcmd sdio_pulldown_get
+hostcmd sdio_pulldown_set
+hostcmd sdio_pulldown_disable
+	This command is used to set/get the settings of pulling up and
+	pulling down of SDIO lines.
+
+	Usage:
+		mlanutl mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_get
+		mlanutl mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_set
+		mlanutl mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_disable
+
+hostcmd subevent_get
+hostcmd subevent_set
+	This command is used to get/set the configurations for event descriptor
+	interface command.
+	subsvent_get: get subscribed event parameters
+	subsvent_set: set subscribed event parameters
+
+	Usage:
+		mlanutl mlanX hostcmd config/subevent.conf subevent_get
+		mlanutl mlanX hostcmd config/subevent.conf subevent_set
+
+hostcmd txpwrlimit_2g_cfg_set
+hostcmd txpwrlimit_5g_cfg_set
+hostcmd txpwrlimit_cfg_get
+	This command is used to set/get the configuration data of Tx power limitation.
+	Note: The configuration set should be issued when STA is disconnected.
+
+	Usage:
+		mlanutl mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_cfg_get
+		mlanutl mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_2g_cfg_set
+		mlanutl mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_5g_cfg_set
+
+hostcmd txrate_cfg_get
+hostcmd txrate_cfg_set_bg
+hostcmd txrate_cfg_set_bgn
+	This command is used to set/get the transmit data rate.
+
+	Usage:
+		mlanutl mlanX hostcmd config/txrate_cfg.conf txrate_cfg_get
+		mlanutl mlanX hostcmd config/txrate_cfg.conf txrate_cfg_set_bg
+		mlanutl mlanX hostcmd config/txrate_cfg.conf txrate_cfg_set_bgn
+
+hostcmd generate_raw
+	This command is used to generate the raw data(hostcommand block) for
+	hostcommand in <conf_file> and write that to file <raw_data_file>
+
+	Usage:
+		mlanutl mlanX hostcmd <conf_file> generate_raw <raw_data_file>
+
+hostcmd fwdump
+	This command is used to trigger firmware dump
+
+	Usage:
+		mlanutl mlanX hostcmd <fwdump.conf> fwdump
+
+hotspotcfg
+	This command is used to get/set the HotSpot configuration.
+
+	Usage:
+		mlanutl mlanX hotspotcfg [<bitmap>]
+
+	Where the parameter is:
+		<bitmap> : configuration bitset
+			 : Bit 31-10 - Reserved set to 0
+			 : Bit 9 - TDLS support indication enable/disable
+			 : Bit 8 - Interworking indication enable/disable
+			 : Bit 7-1 - Reserved set to 0
+			 : Bit 0 - HotSpot feature enable/disable
+
+	Examples:
+		mlanutl mlan0 hotspotcfg        : Get present remote address mode
+		mlanutl mlan0 hotspotcfg 0x301  : Turn on HotSpot2.0 and enable TDLS support and interworking indication
+		mlanutl mlan0 hotspotcfg 0      : Turn off HotSpot2.0 and disable TDLS support and interworking indication
+
+hscfg
+	This command is used to configure the host sleep parameters.
+
+	Usage:
+		mlanutl mlanX hscfg [condition [[GPIO# [gap]]]] (optional)[type ind_GPIO# [level]] (optional)[type event_force_ignore event_use_ext_gap ext_gap [gpio_wave]]
+
+	This command takes one (condition), two (condition and GPIO#) or three
+	(condition, GPIO# and gap). If more than three parameters, it can set different or multiple features indicating by type(this is optional):
+
+	If type=1, it will set indication gpio and its level. And the parameter format will be (condition, GPIO#,gap and type,ind_GPIO#) or
+	(condition, GPIO#, gap, type, ind_GPIO# and level).
+
+	If type=2, it will set extend hscfg wakup method. And the parameter format will be (condition, GPIO#, gap, type, force_ignore,
+	use_ext_gap, ext_gap [gpio_wave]). gpio_wave parameter is optional and default value is 0(falling edge). Each bit of
+	event_force_ignore and event_use_ext_gap will be defined to one same event, and set one same event(same bit) in those two
+	parameters is not allowed. Set bit(s) in event_force_ignore means the event(s) will be forced ignore in firmware silently.
+	Set bit(s) in event_use_ext_gap mean the event(s) will use extend gap to inform host. Not set means not handle.
+
+	If type=3, it will set hs_wakeup_interval.
+
+        If no parameter provided, get is performed.
+
+	The usages of parameters for "hscfg" are the same as that for "hssetpara" command.
+
+hssetpara
+	This command is used to set host sleep parameters.
+
+	Usage:
+		mlanutl mlanX hssetpara condition [GPIO# [gap]] (optional)[type ind_GPIO# [level]] (optional)[type event_force_ignore event_use_ext_gap ext_gap [gpio_wave]] (optional)[type hs_wakeup_interval]
+
+	This command takes one (condition), two (condition and GPIO#) or three
+	(condition, GPIO# and gap).If more than three parameters, it can set different or multiple features indicating by type and
+        the detailed usage is the same as hscfg above.
+
+	where Condition is:
+		bit 0 = 1   -- broadcast data
+		bit 1 = 1   -- unicast data
+		bit 2 = 1   -- mac event
+		bit 3 = 1   -- multicast data
+		bit 6 = 1  --  Wakeup when mgmt frame received.
+		Bit 31 = 1  --  Don't wakeup when IPV6 packet received.
+
+	The host sleep mode will be canceled if condition is set to -1. The default is 0x7.
+
+	where GPIO is the pin number of GPIO used to wakeup the host. It could be any valid
+	GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO will be used instead).
+	The default is 0xff.
+
+	where Gap is the gap in milliseconds between wakeup signal and wakeup event or 0xff
+	for special setting (host acknowledge required) when GPIO is used to wakeup host.
+	The default is 200.
+
+	The host sleep set except for cancellation will be blocked if host sleep is
+	already activated.
+
+	where ind_GPIO# is the pin number of GPIO used to indicate wakeup source. The level on
+	this GPIO will indicate normal wakeup source or abnormal wakeup source.
+
+	where level is used to set level(0/1) on ind_GPIO# pin for indication normal wakeup source.
+	The opposite level will indicate abnormal wakeup source. The default value is 0.
+
+	where event_force_ignore is a bitmap,each bit represent one wakeup reason event. Set the bit means this
+	wakeup reason should be force ignored in firmware. Reset the bit means do not handle this reason.
+
+	where event_use_ext_gap is a bitmap, each bit represent one wakeup reason event. Set the bit means this
+	wakeup reason should use ext_gap to indicate host. Reset the bit means do not handle this reason.
+
+	where event_force_ignore and event_use_ext_gap have the same wakeup reason event definition of each bit:
+		bit 0  = 1     --Disconnect
+		bit 1  = 1     --GTK/iGTK rekey failure
+		bit 2  = 1     --Eapol
+		other bits     --Reserved
+	They should not set both for one same wakeup reason.
+
+	where ext_gap is the extend gap based on third parameter Gap. Only valid when use_ext_gap is used.
+	The total gap is (Gap + (x+1)*ext_gap). x means the bit number(start from 0) of this reason in use_ext_gap.
+
+	where gpio_wave is used to set GPIO wave level for hscfg extend. 0 means falling edge, 1 means rising edge.
+	This parameter is optional and default value is 0.
+
+	where hs_wakeup_interval is used to set host sleep wakeup interval and the type must set to 3 to indicate
+	this feature. And the value will round to the nearest multiple dtim*beacon_interval in fw. The unit is milliseconds.
+
+	Examples:
+		mlanutl mlan0 hssetpara -1           : Cancel host sleep mode
+		mlanutl mlan0 hssetpara 3            : Broadcast and unicast data
+		                                       Use GPIO and gap set previously
+		mlanutl mlan0 hssetpara 2 3          : Unicast data
+		                                       Use GPIO 3 and gap set previously
+		mlanutl mlan0 hssetpara 2 1 0xa0     : Unicast data
+		                                       Use GPIO 1 and gap 160 ms
+		mlanutl mlan0 hssetpara 2 0xff       : Unicast data
+		                                       Use interface (e.g. SDIO)
+		                                       Use gap set previously
+		mlanutl mlan0 hssetpara 4 3 0xff     : MAC event
+		                                       Use GPIO 3
+		                                       Special host sleep mode
+		mlanutl mlan0 hssetpara 1 0xff 0xff  : Broadcast data
+
+		mlanutl mlan0 hssetpara 2 1 0xa0 1 5 1 : Unicast data
+		                                       Use GPIO 1
+                                                       Gap 160 ms
+                                                       type=1 to set indication GPIO feature
+                                                       Use GPIO 5 to indicate wakeup source
+                                                       High level on GPIO 5 means this is a normal wakeup
+		mlanutl mlan0 hssetpara 2 1 0xa0 1 5   : Unicast data
+		                                       Use GPIO 1
+                                                       Gap 160 ms
+                                                       type=1 to set indication GPIO feature
+                                                       Use GPIO 5 to indicate wakeup source
+                                                       Use level set previously.
+
+		mlanutl mlan0 hssetpara 2 1 0xa0 2 0 0x1 10 1: Unicast data
+		                                       Use GPIO 1
+                                                       Gap 160 ms
+                                                       type=2 to set extend hscfg feature
+                                                       Force_ignore not used
+                                                       Disconnect will use extend gap to indicate host
+                                                       Use gap 170.
+                                                       Rising edge
+		mlanutl mlan0 hssetpara 2 1 0xa0 2 0x1 0 0 0: Unicast data
+		                                       Use GPIO 1
+                                                       Gap 160 ms
+                                                       type=2 to set extend hscfg feature
+                                                       Falling edge
+                                                       Force ignore Disconnect
+                                                       Extend gap not used
+                                                       Not used.
+                                                       Falling edge
+		mlanutl mlan0 hssetpara 2 1 0xa0 3 400:  Unicast data
+		                                       Use GPIO 1
+                                                       Gap 160 ms
+                                                       type=3 to set hs_wakeup_interval feature
+                                                       hs_wakeup_interval set to 400ms
+
+	Note: The parameters will be saved in the driver and be used when host suspends.
+              The ind_GPIO# and level parameters only work with specific board and firmware.
+
+mgmtfilter
+        This command is used to set management frame to wake up host when host suspend.
+
+        Usage:
+                mlanutl mlanX mgmtfilter <mgmtfilter.conf>
+
+        where <mgmtfilter.conf>
+                This conf file will set management frame catagory, action and frame mask.
+
+        Examples:
+                mlanutl mlan0 mgmtfilter mgmtfilter.conf
+
+auto_arp
+        This command is used to enable/disable auto arp response in host sleep mode.
+        No argument is used to get.
+
+        where value of n is:
+                0   -- Disable
+                1   -- Enable
+
+        Examples:
+                mlanutl mlan0 auto_arp 0           : Disable auto arp response from FW
+                mlanutl mlan0 auto_arp             : Get auto arp configuration status
+
+htcapinfo
+	This command is used to configure some of parameters in HTCapInfo IE
+	(such as Short GI, Channel BW, and Green field support)
+
+	where <m> is <capinfo>
+	<capinfo> - This is a bitmap and should be used as following
+		Bit 29: Green field enable/disable
+		Bit 26: Rx STBC Support enable/disable. (As we support
+			single spatial stream only 1 bit is used for Rx STBC)
+		Bit 25: Tx STBC support enable/disable.
+		Bit 24: Short GI in 40 Mhz enable/disable
+		Bit 23: Short GI in 20 Mhz enable/disable
+		Bit 22: Rx LDPC enable/disable
+		Bit 17: 20/40 Mhz enable disable.
+		Bit  8: Enable/disable 40Mhz Intolarent bit in ht capinfo.
+		        0 will reset this bit and 1 will set this bit in
+		        htcapinfo attached in assoc request.
+		All others are reserved and should be set to 0.
+
+	Setting of any other bits will return error.
+
+	where <n> is <band>
+	<band> - This is the band info for <capinfo> settings.
+		0: Settings for both 2.4G and 5G bands
+		1: Settings for 2.4G band
+		2: Settings for 5G band
+
+	Example:
+		mlanutl mlanX htcapinfo
+		This will display HT capabilties information.
+		If the information for 2.4G and 5G is different,
+		the first value is for 2.4G and the second value is for 5G.
+		Otherwise, it will display a single value for both bands.
+
+		mlanutl mlanX htcapinfo 0x1820000
+		This will enable Short GI, Channel BW to 20/40 and disable Green field support for 2.4G and 5G band.
+
+		mlanutl mlanX htcapinfo 0x800000 2
+		This will enable Short GI, Channel BW to 20 only, No Rx STBC support and disable Green field support for 5G band.
+
+	The default value is 0x4800000 for 2.4G and 0x5820000 for 5G.
+
+	Note:- This command can be issued any time but it will only come to effect from
+	next association. (as HTCapInfo is sent only during Association).
+
+htstreamcfg
+	This command is used to set/get HT stream configuration.
+	The setting only takes effect in next association.
+
+	Usage:
+		mlanutl mlanX htstreamcfg [n]
+
+	where <n>
+		0x11: HT stream 1x1 mode
+		0x22: HT stream 2x2 mode
+
+	Examples:
+		mlanutl mlan0 htstreamcfg        : Get current setting
+		mlanutl mlan0 htstreamcfg 0x11   : Set HT stream 1x1 mode
+		mlanutl mlan0 htstreamcfg 0x22   : Set HT stream 2x2 mode
+
+httxbfcap
+	This command is used to set/get the TX beamforming capabilities.
+
+	Usage:
+		mlanutl mlanX httxbfcap [cap]
+
+	where the parameters are,
+		cap: TX beamforming capabilities
+			 Bit 0    : Implicit TX BF receiving capable
+			 Bit 1    : RX staggered sounding capable
+			 Bit 2    : TX staggered sounding capable
+			 Bit 3    : RX NDP capable
+			 Bit 4    : TX NDP capable
+			 Bit 5    : Implicit TX BF capable
+			 Bit 6-7  : Calibration
+			         0: - not supported
+			         1: - STA can respond to a calibration request using
+			              the CSI Report, but cannot initiate calibration
+			         2: - reserved
+			         3: - STA can both initiate and respond to a calibration request
+			 Bit 8    : Explicit CSI TX BF capable
+			 Bit 9    : Explicit non-compressed steering capable
+			 Bit 10   : Explicit compressed steering capable
+			 Bit 11-12: Explicit TX BF CSI feedback
+			         0: - not supported
+			         1: - delayed feedback
+			         2: - immediate feedback
+			         3: - delayed and immediate feedback
+			 Bit 13-14: Explicit non-compressed BF feedback capable
+			         0: - not supported
+			         1: - delayed feedback
+			         2: - immediate feedback
+			         3: - delayed and immediate feedback
+			 Bit 15-16: Explicit compressed BF feedback capable
+			         0: - not supported
+			         1: - delayed feedback
+			         2: - immediate feedback
+			         3: - delayed and immediate feedback
+			 Bit 17-18: Minimal grouping
+			         0: - no grouping (STA supports groups of 1)
+			         1: - groups of 1, 2
+			         2: - groups of 1, 4
+			         3: - groups of 1, 2, 4
+			 Bit 19-20: CSI number of beamformer antennas supported
+			         0: - single TX antenna sounding
+			         1: - 2 TX antenna sounding
+			         2: - 3 TX antenna sounding
+			         3: - 4 TX antenna sounding
+			 Bit 21-22: Non-compressed steering number of beamformer antennas supported
+			         0: - single TX antenna sounding
+			         1: - 2 TX antenna sounding
+			         2: - 3 TX antenna sounding
+			         3: - 4 TX antenna sounding
+			 Bit 23-24: Compressed steering number of beamformer antennas supported
+			         0: - single TX antenna sounding
+			         1: - 2 TX antenna sounding
+			         2: - 3 TX antenna sounding
+			         3: - 4 TX antenna sounding
+			 Bit 25-26: CSI max number of rows beamformer supported
+			         0: - single row of CSI
+			         1: - 2 rows of CSI
+			         2: - 3 rows of CSI
+			         3: - 4 rows of CSI
+			 Bit 27-28: Channel estimation capability
+			         0: - 1 space time stream
+			         1: - 2 space time streams
+			         2: - 3 space time streams
+			         3: - 4 space time streams
+			 Bit 29-31: Reserved
+
+	Examples:
+		mlanutl mlan0 httxbfcap             : Get the current TX BF capabilities
+		mlanutl mlan0 httxbfcap 0x0000001F  : Set the TX BF capabilities of the
+		                                     Implicit TX BF receiving capable,
+		                                     RX staggered sounding capable,
+		                                     TX staggered sounding capable,
+		                                     RX NDP capable and TX NDP capable
+
+httxbfcfg
+	This command is used to configure the TX beamforming options.
+	Note: Any new subcommand should be inserted in the second
+		argument and each argument of the sub command should be
+		separated by semicolon. For global configuration, the
+		arguments should be separated by space.
+
+	Usage:
+		mlanutl mlanX httxbfcfg "<action>[;GlobalData/tsData/interval/txPeerData/snrData/txSounding]"
+
+	where the parameters are,
+		action: TX beamforming action
+			0: Control global parameters for beamforming
+		        1: Performs NDP Sounding for PEER
+		        2: TX BF interval in milliseconds
+		        3: Enable/Disable beamforming/sounding for a particular peer
+		        4: TX BF SNR Threshold for peer
+		        .. <for new subcommand>
+		GlobalData: Global parameter arguments.
+		    It contains beamforming enable, sounding enable, FB type, snr_threshold
+		    sounding interval, Beamformig mode values seperated by space.
+		    Syntax:
+			mlanutl mlanX httxbfcfg <action>;<beamforming enable> <sounding enable> <FB type>
+			                       <snr_threshold>  <sounding interval> <Beamforming mode>
+		tsData: Trigger sounding for PEER specific arguments,
+		        it contains PEER MAC and status
+		interval: TX BF interval in milliseconds
+		txPeerData: Enable/Disable beamforming/sounding for the indicated peer,
+		          it contains PEER MAC, sounding, beamfoming options and FB type;
+		snrData: TX BF SNR Threshold for peer, it contains PEER MAC and SNR
+
+	Examples:
+		mlanutl mlan0 httxbfcfg "0"                          : Get current global configuration parameter
+		mlanutl mlan0 httxbfcfg "2;00:50:43:20:BF:64"        : Get the TX BF periodicity for a given peer
+		mlanutl mlan0 httxbfcfg "3"                          : Get the list of MAC addresses that have
+		                                                      beamforming and/or sounding enabled
+		mlanutl mlan0 httxbfcfg "4"                          : Get the list of PEER MAC, SNR tuples
+		                                                      programmed into the firmware.
+		mlanutl mlan0 httxbfcfg "0;0 0 3 10 500 5"           : Disable beamforming, sounding, set FB type
+		                                                      to 3, snr threshold to 10, sounding interval
+		                                                      to 500 ms and beamforming mode to 5
+		mlanutl mlan0 httxbfcfg "1;00:50:43:20:BF:64"        : Perform NDP Trigger sounding to peer
+		                                                      00:50:43:20:BF:64
+		mlanutl mlan0 httxbfcfg "2;00:50:43:20:BF:64;500"    : Set TX BF periodicity for peer 00:50:43:20:BF:64
+		                                                      to 500 milliseconds
+		mlanutl mlan0 httxbfcfg "3;00:50:43:20:BF:43;1;0;3"  : Enable beamforming, disable sounding and set
+		                                                      FB type to 3 for peer 00:50:43:20:BF:43
+		mlanutl mlan0 httxbfcfg "4;00:50:43:20:BF:24;43"     : Set TX BF SNR threshold to peer
+		                                                      00:50:43:20:BF:24 with SNR 43
+
+httxcfg
+	This command is used to configure various 11n specific configuration
+	for transmit (such as Short GI, Channel BW and Green field support)
+
+	where <m> is <txcfg>
+	This is a bitmap and should be used as following
+		Bit 15-10: Reserved set to 0
+        Bit 9-8: Rx STBC set to 0x01
+        BIT9 BIT8  Description
+         0    0     No spatial streams
+         0    1     One spatial streams supported
+         1    0     Reserved
+         1    1     Reserved
+		Bit 7: STBC enable/disable
+		Bit 6: Short GI in 40 Mhz enable/disable
+		Bit 5: Short GI in 20 Mhz enable/disable
+		Bit 4: Green field enable/disable
+		Bit 3-2: Reserved set to 1
+		Bit 1: 20/40 Mhz enable disable.
+		Bit 0: LDPC enable/disable
+
+	When Bit 1 is set then firmware could transmit in 20Mhz or 40Mhz based
+	on rate adaptation. When this bit is reset then firmware will only
+	transmit in 20Mhz.
+
+	where <n> is <band>
+	<band> - This is the band info for <txcfg> settings.
+		0: Settings for both 2.4G and 5G bands
+		1: Settings for 2.4G band
+		2: Settings for 5G band
+
+	Example:
+		mlanutl mlanX httxcfg
+		This will display HT Tx configuration for 2.4G and 5G band.
+
+		mlanutl mlanX httxcfg 0x62
+		This will enable 20/40 and Short GI but will disable Green field for 2.4G and 5G band.
+
+		mlanutl mlanX httxcfg 0x30 1
+		This will enable Short GI 20 Mhz and Green field for 2.4G band.
+
+	The default value is 0x20 for 2.4G and 0x62 for 5G.
+
+	Note:- If 20/40 MHz support is disabled in htcapinfo, device will not transmit
+	in 40 MHz even 20/40 MHz is enabled in httxcfg.
+
+inactivityto
+	This command is used to set/get the inactivity timeout value, which specifies
+	when WLAN device is put to sleep.
+
+	Usage:
+		mlanutl mlanX inactivityto <n> <m> <l> [k]
+
+	where the parameter are:
+		<n>: timeout unit in microseconds.
+		<m>: Inactivity timeout for unicast data.
+		<l>: Inactivity timeout for multicast data.
+		[k]: Inactivity timeout for new Rx traffic after PS notification to AP.
+
+	Examples:
+		mlanutl mlan0 inactivityto           : Get the timeout value
+		mlanutl mlan0 inactivityto 1000 2 3  : Set timeout unit to 1000 us (1 ms),
+		                                      inactivity timeout for unicast data is 2 ms,
+		                                      inactivity timeout for multicast data is 3 ms
+
+ipaddr
+	This command is used to set/get IP address.
+
+	Usage:
+		mlanutl mlanX ipaddr ["<op>;<ipaddr>"]
+
+	where <op>
+		0: Remove the IP address
+		bit 0: Set IP address for broadcast ARP filter, which will be auto enabled
+		       in next host sleep configuration
+		bit 1: Set IP address for auto broadcast ARP response
+
+	Examples:
+		mlanutl mlan0 ipaddr                 : Get current settings
+		mlanutl mlan0 ipaddr "0"             : Remove IP address
+		mlanutl mlan0 ipaddr "1;192.168.0.5" : Set IP address for ARP filter
+		mlanutl mlan0 ipaddr "3;192.168.0.6" : Set IP address for ARP filter
+		                                     : and auto ARP response
+
+linkstats
+	This command is used to get the link statistics from the firmware.
+
+	Usage:
+		mlanutl mlanX linkstats
+
+listeninterval
+	This command is used to set/get listen interval in assoc request.
+
+	Usage:
+		mlanutl mlanX listeninterval [l]
+
+	where the parameter:
+		[l]: Value of listen interval [Default 10]
+
+	Examples:
+		mlanutl mlan0 listeninterval     : Display Listen interval
+		mlanutl mlan0 listeninterval 1   : Set Listen interval to 1.
+
+macctrl
+	This command is used to set/get MAC control.
+	It's recommended to read the current setting first to avoid override issue.
+
+	Usage:
+		mlanutl mlanX macctrl [n]
+
+	where <n>
+		bit 0:  Rx enabled
+		bit 1:  Directed Filter enabled
+		bit 2:  LoopBack enabled
+		bit 3:  WEP enabled
+		bit 4:  EthernetII enabled
+		bit 5:  MultiCast enabled
+		bit 6:  BroadCast enabled
+		bit 7:  Promiscuous enabled
+		bit 8:  All MultiCast enabled
+		bit 9:  RTS/CTS enabled (0: CTS to self)
+		bit 10: Enforce Protection enabled
+		bit 11: Force 11N Protection enabled
+		bit 12: Rx 802.11 Packets enabled
+		bit 13: Ad-hoc g Protection enabled
+		bit 14: Reserved
+		bit 15: WEP Type
+		bit 16: BandWidth Indication in RTS enabled
+		bit 17: Dynamic BandWidth Indication Mode in RTS enabled
+		bit 18-31: Reserved
+
+	Examples:
+		mlanutl mlan0 macctrl           : Get current MAC control
+		mlanutl mlan0 macctrl 0x13      : Set Rx enabled and Directed Filter enabled and EthernetII enabled
+		mlanutl mlan0 macctrl 0x813     : Set Rx enabled and Directed Filter enabled and EthernetII enabled
+		                                  Force 11N Protection enabled
+
+mefcfg
+	This command is used to set MEF settings.
+
+	Usage:
+		mlanutl mlanX mefcfg <mef.conf>
+
+	Where the parameter is:
+		mef.conf : The configuration file specifying the MEF settings.
+
+	Example:
+		mlanutl mlan0 mefcfg config/mef.conf
+
+memrdwr
+	This command is used to read/write the adapter memory.
+
+	Usage:
+		mlanutl mlanX memrdwr <address> [value]
+
+	where the parameters are,
+		<address>:  memory address
+		[value]:    value to be written
+
+	Examples:
+		mlanutl mlan0 memrdwr 0x4cf70    : Read memory address 0x4cf70
+		mlanutl mlan0 memrdwr 0x80000000 0xffffffff
+		                                 : Write 0xffffffff to memory address 0x80000000
+
+miracastcfg
+	This command is used to set/get the miracast configuration.
+
+	Usage:
+	mlanutl mlanX miracastcfg [l] [m] [n]
+
+	where the parameters are,
+		[l]:    miracast mode
+				0: Disable
+				1: Source
+				2: Sink
+		[m]:    scan time per channel, in ms
+		[n]:    gap during two scans, in ms
+
+	Examples:
+		mlanutl mlan0 miracastcfg           : Get miracast configuration
+		mlanutl mlan0 miracastcfg 0         : Disable miracast configuration
+		mlanutl mlan0 miracastcfg 1 20 40   : Set miracast mode as source, with scan time
+	                                          20ms per channel and gap during two scans 40ms
+
+mgmtframectrl
+	This command is used to set/get registered frame type to passthrough.
+
+	Usage:
+		mlanutl mlanX mgmtframectrl [<mask>]
+		mlanutl uapX mgmtframectrl [<mask>]
+
+	Where the parameter is:
+		<mask>  : the bit mask of management frame reception.
+			: Bit 0 - Association Request
+			: Bit 1 - Association Response
+			: Bit 2 - Re-Association Request
+			: Bit 3 - Re-Association Response
+			: Bit 4 - Probe Request
+			: Bit 5 - Probe Response
+			: Bit 8 - Beacon Frames
+
+	Examples:
+		mlanutl mlan0 mgmtframectrl        : Get present mask
+		mlanutl mlan0 mgmtframectrl 0x0020 : Bit 5 is set, Forward probe response frames to application layer
+
+mgmtframetx
+	This command is used to send management frame.
+
+	Usage:
+		mlanutl mlanX mgmtframetx <mgmt_frame.conf>
+
+	Where the parameter is:
+		mgmt_frame.conf : The configuration file contains the management frame.
+
+	Examples:
+		mlanutl mlan0 mgmtframetx config/mgmt_frame.conf
+
+mpactrl
+	This command is used to set/get the Tx, Rx SDIO aggregation parameters.
+	Note: The parameters can be set only in disconnected state.
+
+	Usage:
+		mlanutl mlanX mpactrl [tx_ena] [rx_ena] [tx_size] [rx_size] [tx_ports] [rx_ports]
+
+	where the parameter are:
+		[tx_ena]: Enable/disable (1/0) Tx MP-A
+		[rx_ena]: Enable/disable (1/0) Rx MP-A
+		[tx_size]: Size of Tx MP-A buffer
+		[rx_size]: Size of Rx MP-A buffer
+		[tx_ports]: Max ports (1-16) for Tx MP-A
+		[rx_ports]: Max ports (1-16) for Rx MP-A
+	default values are 1 1 16384 32768 16 16
+	The MP-A may be disabled by default at build time if the MMC driver byte mode patch
+	is not available in kernel.
+
+	Examples:
+		mlanutl mlan0 mpactrl       : Get MP aggregation parameters
+		mlanutl mlan0 mpactrl 0 0
+		                           : Disable MP aggregation for Tx, Rx respectively
+		mlanutl mlan0 mpactrl 1 1 8192 8192 8 8
+		                           : Enable MP aggregation for Tx, Rx
+		                           : Set Tx, Rx buffer size to 8192 bytes
+		                           : Set maximum Tx, Rx ports to 8
+
+netmon
+    This command is used to set/get sniffer mode configuration.
+    Note: The channel and band config is optional. If not specified, or if
+    any STA/uAP/STA+uAP connection is active, sniffer activity will be started
+    on the current config set in the FW.
+    'rtap' monitor interface will be created on enabling sniffer activity and
+    should be made 'up' for capturing in a sniffer app.
+
+    Usage:
+        mlanutl <interface> netmon [<act> [<filter>]]
+        mlanutl mlanX netmon [<act> [<filter>] [<band> <chan> [offset]]]
+
+    Where the parameters are:
+        <interface> : mlanX, uapX
+        <act>	: (0) disable sniffer activity
+                : (1) enable sniffer activity
+
+        <filter> : network monitor fitler flag
+            bit 0: (1/0) enable/disable management frame
+            bit 1: (1/0) enable/disable control frame
+            bit 2: (1/0) enable/disable data frame
+            bit 3: (1/0) enable/disable frames destined to active connection only
+            bit 4: (1/0) enable/disable decrypted unicast data/mgmt frames
+
+        <band>   : 802.11 band
+            bit 0: B
+            bit 1: G
+            bit 2: A
+            bit 3: GN
+            bit 4: AN
+            bit 5: AC 2.4G
+            bit 6: AC 5G
+        <chan>   : channel to monitor
+
+        [offset] : secondary channel bandwidth
+            0 - Bandwidth 20Mhz
+            1 - HT Bandwidth 40Mhz sec channel above
+            3 - HT Bandwidth 40Mhz sec channel below
+            4 - VHT Bandwidth 80Mhz
+
+	Examples:
+        mlanutl mlan0 netmon             : Get the current sniffer mode configuration
+        mlanutl mlan0 netmon 0           : Disable network monitor activity
+        mlanutl uap0 netmon 1 7			 : Enable sniffer activity on current channel set in FW,
+                                           set filter data, control, management frame.
+        mlanutl mlan0 netmon 1 4 11 6    : Enable sniffer activity in absence of active connection,
+                                           set filter data frame, band B/G/GN and channel 6
+        mlanutl mlan0 netmon 1 7 20 64 1 : Enable sniffer activity in absence of active connection
+                                           set filter management, control and data frame, band A/AN,
+                                           channel 64 and secondary channel above
+        mlanutl uap0 netmon 1 0x0c	      : Enable sniffer activity, set filter data frames
+                                            destined to the active uAP connection only
+
+        mlanutl mlan0 netmon 1 0x1d       : Enable sniffer activity, set filter decrypted data and
+                                            management frames destined to the active STA connection
+                                            only
+
+offchannel
+	This command is used to set/cancel the offchannel configuration.
+	Note: This command only can be used when cfg80211 is enabled during load time.
+
+	Usage:
+		mlanutl mlanX offchannel [<l> <m> <n>]
+
+	where
+		<l>
+			0 : Cancel the offchannel configuration
+			1 : Set the offchannel configuration
+		<m>
+			The channel to configure
+		<n>
+			The duration for which to configure
+
+	Examples:
+		mlanutl mlan0 offchannel           : Get current offchannel status.
+		mlanutl mlan0 offchannel 0         : Cancel the offchannel configuration.
+		mlanutl mlan0 offchannel 1 3 5     : Configure channel 3 for 5 milliseconds.
+		mlanutl mlan0 offchannel 1 36 5000 : Configure channel 36 for 5000 milliseconds.
+
+otpuserdata
+	This command is used to get the OTP user data.
+
+	Where
+	<l> is <user_data_length>
+	<user_data_length> - This parameter specifies the length of OTP user data to be read
+
+	Examples:
+		mlanutl mlan0 otpuserdata 10         : Get the 10-byte OTP user data
+
+passphrase
+	This command is used to set/get passphrase for WPA-PSK/WPA2-PSK mode.
+
+	Where <l>
+		ASCII string for ssid/passphrase/psk.
+
+	1) "0;<ssid=valid ssid>" - This will get the passphrase, AKMP
+	   for specified ssid, if none specified then it will get all.
+
+	Example:
+		mlanutl mlan0 passphrase "0;ssid=marvell"
+
+	2) "1;<psk=64 byte hexpsk>;<passphrase=1-63 byte passphare>
+	   <ssid=valid ssid>" - Passphrase and psk cannot be provided for the same SSID.
+	   This command takes only one SSID at a time, If ssid= is present it should contain
+	   a passphrase or psk. If no arguments are provided then AKMP=802.1x, and passphrase
+	   should be provided after association.
+	   End of each parameter should be followed by a ';'(except for the last parameter)
+	   as the delimiter. If ';' or '/' has to be used in an SSID then a '/' should be preceded
+	   to ';' or '/' as a escape.
+
+	Examples:
+		mlanutl mlan0 passphrase "1;ssid=mrvlAP;passphrase=abcdefgd"
+		mlanutl mlan0 passphrase "1;ssid=mrvl AP;psk=<64 bytes hexpsk>"
+
+		If user wants to input the ssid as "mrvl; AP" then command has to be
+		mlanutl mlan0 passphrase "1;ssid=mrvl/; AP;passphrase=abcdefgh"
+
+		If user wants to input the ssid as "//;" then command has to be
+		mlanutl mlan0 passphrase "1;ssid=/////;;passphrase=abcdefgh"
+
+	3) "2;<ssid=valid ssid>" - This will clear the passphrase
+	   for specified ssid, if none specified then it will clear all.
+
+	Examples:
+		mlanutl mlan0 passphrase "2;ssid=marvell"
+		mlanutl mlan0 passphrase "2"     : Clear all profiles and disable embedded supplicant
+
+pb_bypass
+	This command is used to get the By-passed TX packet from upper layer.
+
+	Usage:
+
+	mlanutl mlanX pb_bypass [data_1, data_2, ... data_n]
+
+	where value of data_1, data_2, ... data_n isBypass TX Data
+
+pmfcfg
+	This command is used to set/get management frame protection parameters.
+
+	Usage:
+		mlanutl mlanX pmfcfg <m> <n>
+
+	where
+		<m>: Management Frame Protection Capable (MFPC)
+			1: Management Frame Protection Capable
+			0: Management Frame Protection not Capable
+		<n>: Management Frame Protection Required (MFPR)
+			1: Management Frame Protection Required
+			0: Management Frame Protection Optional
+		Default setting is PMF not capable.
+		m = 0, n = 1 is an invalid combination
+
+        Examples:
+                mlanutl mlan0 pmfcfg            : Get PMF parameters
+                mlanutl mlan0 pmfcfg 1 0        : Set MFPC and make MFPR optional
+
+port_ctrl
+	This command is used to Set/Get Port Control mode. No argument is used to get.
+
+	where value of n is:
+		0   -- Disable
+		1   -- Enable
+
+	Examples:
+		mlanutl mlan0 port_ctrl 1           : Enable Port Control mode
+		mlanutl mlan0 port_ctrl             : Get Port Control mode status
+
+powercons
+	This command is used to set the local transmit power constraint.
+	Value is in dbm unit. This command is only used for ad-hoc start.
+
+	Usage:
+		mlanutl mlanX powercons [n]
+
+	Examples:
+		mlanutl mlanX powercons          : get the current setting
+		mlanutl mlanX powercons 12       : set local power constraint to 12 dbm
+
+pscfg
+	This command is used to set/get PS configuration parameters.
+
+	Usage:
+		mlanutl mlanX pscfg [k] [d] [l] ...
+
+	Where the parameters:
+		[k]: Keep alive null packet interval (0: Unchanged, -1: Disable, n: Interval in seconds)
+		[d]: DTIM interval (    0: Unchanged,
+		                      1-5: Value,
+		                    65534: DTIM will be ignored, listen interval will be used,
+		                    65533: Closest DTIM to the listen interval period will be used )
+		[l]: Local listen interval (     0: Unchanged,
+		                                -1: Disable,
+		                              1-49: Value in beacon intervals,
+		                             >= 50: Value in TUs )
+		[a]: Ad-hoc awake period (0: Unchanged, 1-31: Beacon interval, 255: Firmware
+		                          will go to sleep after beacon send out)
+		[b]: Beacon miss timeout (0: Unchanged, 1-50: Value in milliseconds, 65535: Disable)
+		[p]: Delay to PS (0-65535: Value in milliseconds, default 1000ms)
+		[m]: PS mode (0: Unchanged, 1: Auto mode, 2: PS-Poll mode, 3: PS Null mode)
+	No change if parameters are not provided.
+
+	Examples:
+		mlanutl mlan0 pscfg              : Get all the current PS configuration settings
+		mlanutl mlan0 pscfg 3 4          : Set PS keep alive null packet interval to 3 seconds
+		                                   and DTIM interval to 4, all the other configurations
+		                                   are unchanged
+		mlanutl mlan0 pscfg 0 0xfffe 10 0 20
+		                                : Disable DTIM interval, set local listen interval to
+		                                  10 beacon intervals and beacon miss interval to 20,
+		                                  all the other configurations are unchanged
+		mlanutl mlan0 pscfg 0 0 0 0 0 50 : Set delay to PS to 50 ms, keep the others unchanged
+
+bcntimeoutcfg
+	This command is used to set Beacon timeout parameters.
+
+	Usage:
+		mlanutl mlanX bcntimeoutcfg [l] [m] [o] [p]
+
+	Where the parameters:
+		[l]: Beacon miss timeout period Rx window (in ms)
+		[m]: Beacon miss timeout period (unit in beacon interval)
+		[o]: Beacon reacquire timeout period Rx window (unit in beacon interval)
+		[p]: Beacon reacquire timeout period (unit in beacon interval)
+		Please note that it would be better [m]+[p] not exceed 64.
+	Examples:
+		mlanutl mlan0 bcntimeoutcfg 10 30 2 30    : Set beacon timeout configure to
+		                                            Beacon miss timeout period Rx window      : 10 (ms)
+		                                            Beacon miss timeout period                : 30 (Beacon Interval)
+		                                            Beacon reacquire timeout period Rx window : 2  (Beacon Interval)
+		                                            Beacon reacquire timeout period           : 30 (Beacon Interval)
+
+psmode
+	This command is used to set/get the IEEE PS mode configuration.
+
+	Usage:
+		mlanutl mlanX psmode [l]
+
+	where the parameter:
+		[l]
+			0 : Disable IEEE PS mode
+			1 : Enable IEEE PS mode
+			<none>: Get IEEE PS mode
+
+	Examples:
+		mlanutl mlan0 psmode   : Get IEEE PS mode.
+		mlanutl mlan0 psmode 1 : Enable IEEE PS mode.
+
+qconfig
+	Send a WMM AC Queue configuration command to get/set/default params
+
+	Configure or get the parameters of a WMM AC queue. The command takes
+	an optional Queue Id as a last parameter.  Without the queue id, all
+	queues will be acted upon.
+
+	Usage:
+		mlanutl mlanX qconfig def [Queue Id: 0-3]
+		mlanutl mlanX qconfig get [Queue Id: 0-3]
+		mlanutl mlanX qconfig set msdu <lifetime in TUs> [Queue Id: 0-3]
+
+qoscfg
+	This command sets WMM IE QOS info when an argument is given, and gets current WMM
+	IE QOS info when no argument is given.
+
+	Examples:
+		mlanutl mlanX qoscfg 0x0f        : Set WMM IE QOS info to 0x0f
+		mlanutl mlanX qoscfg             : Get WMM IE QOS info
+
+qstatus
+	This command retrieves the current status of the WMM queues. If WMM
+	  is enabled then it displays the information for each AC in a table.
+
+	Usage:
+		mlanutl mlanX qstatus
+
+radioctrl
+	This command is used to turn on/off the radio.
+	Note: The radio can be disabled only in disconnected state.
+
+	where value of n is:
+		0   -- Disable
+		1   -- Enable
+
+	Examples:
+		mlanutl mlan0 radioctrl 1        : Turn the radio on
+		mlanutl mlan0 radioctrl          : Get radio status
+
+rdeeprom
+	This command is used to read the EEPROM contents of the card.
+
+	Usage:
+		mlanutl mlanX rdeeprom <offset> <length>
+
+	where the parameters are,
+		<offset>:   multiples of 4
+		<length>:   4-20, multiples of 4
+
+	Example:
+		mlanutl mlan0 rdeeprom 0 20      : Read 20 bytes of EEPROM data from offset 0
+
+reassoctrl
+	This command is used to turn on/off re-association in driver.
+
+	Usage:
+		mlanutl mlanX reassoctrl [n]
+
+	Where value of n is:
+		0   -- Disable
+		1   -- Enable
+
+	Examples:
+		mlanutl mlan0 reassoctrl         : Get re-association status
+		mlanutl mlan0 reassoctrl 1       : Turn re-association on
+
+regioncode
+	This command is used to set/get the region code in the station.
+	Note: This command should be issued at beginning before band/channel selection
+	and association.
+
+	where value is 'region code' for various regions like
+	USA FCC, Canada IC, Europe ETSI, Japan ...
+	The special code (0xff) is used for Japan to support channel 1-14 in B/G/N mode.
+
+	Examples:
+		mlanutl mlan0 regioncode         : Get region code
+		mlanutl mlan0 regioncode 0x10    : Set region code to USA (0x10)
+	Note : in some case regioncode will be 0 after updated countycode or 80211d
+		i.e. mlanutl mlanX countrycode  (CA, JP, CN, DE, ES AT, BR, RU)
+		or uaputl.exe sys_cfg_80211d state 1 country (CA, JP, CN, DE, ES AT, BR, RU)
+		Please use cfp instead of it.
+
+regrdwr
+	This command is used to read/write the adapter register.
+
+	Usage:
+		mlanutl mlanX regrdwr <type> <offset> [value]
+
+	where the parameters are,
+		<type>:     1:MAC/SOC, 2:BBP, 3:RF, 5:CAU, 6:PSU
+		<offset>:   offset of register
+		[value]:    value to be written
+		Note:
+		BBP reg (type 2) 0xXZZZ:
+		X: 0=BBUD, 8=BBUA.
+		ZZZ: offset (0-0xFFF).
+		RF reg (type 3) 0xXYZZ:
+                 X   = Path ID (0-1)
+           	 Y   = Page Number (0-6) in selected Path
+           	 ZZ  = Register offset in selected path/page
+
+	Examples:
+		mlanutl mlan0 regrdwr 1 0xa060   : Read the MAC register
+		mlanutl mlan0 regrdwr 1 0xa794 0x80000000
+		                                 : Write 0x80000000 to MAC register
+
+rejectaddbareq
+	This command is used to set/get the conditions of rejecting addba request.
+
+	Usage:
+		mlanutl mlanX rejectaddbareq [conditions]
+		mlanutl uapX rejectaddbareq [conditions]
+
+	Where conditions are:
+		bit 0 = 1   -- reject the addba request when host sleep activated
+		others      -- reserved
+
+	Examples:
+		mlanutl mlan0 rejectaddbareq      : Get the reject addba request conditions
+		mlanutl mlan0 rejectaddbareq 0x1  : Reject the addba request
+		                                    when host sleep activated
+		mlanutl uap0 rejectaddbareq 0x1   : Reject the addba request
+		                                    when host sleep activated
+
+scancfg
+	This command is used to set/get scan configuration parameters.
+
+	Usage:
+		mlanutl mlanX scancfg [t] [m] [p] [s] [a] [b] [ext]
+
+	where the parameters:
+		[t]: Scan Type (0: Unchanged, 1: Active, 2: Passive, default Active)
+		[m]: Scan Mode (0: Unchanged, 1: BSS, 2: IBSS, 3: Any, default Any)
+		[p]: Scan Probes (0: Unchanged, 1-4: Number of probes per channel, default 4)
+		[s]: Specific Scan Time (0: Unchanged, n: Value in ms, default 110 ms, max 500 ms)
+		[a]: Active Scan Time (0: Unchanged, n: Value in ms, default 200 ms, max 500 ms)
+		[b]: Passive Scan Time (0: Unchanged, n: Value in ms, default 200 ms, max 2000 ms)
+		[ext]: Extended scan (0: Legacy scan, 1: Extended scan)
+	No change if the parameter is 0 or the parameter is not provided.
+
+	Examples:
+		mlanutl mlan0 scancfg            : Get all the current scan configuration settings
+		mlanutl mlan0 scancfg 1 3        : Set scan type to active and scan mode to any,
+		                                   all the other scan configurations are unchanged
+		mlanutl mlan0 scancfg 0 1 2 200  : Set scan mode to BSS, number of probes to 2 and
+		                                   specific scan time to 200 ms, all the other scan
+		                                   configurations are unchanged
+
+sdcmd52rw
+	This command is used to read/write a controller register in
+	Secure Digital I/O Interfaces.
+
+	Usage:
+		mlanutl mlanX sdcmd52rw <function number> <register address> [value]
+
+	For SDIO MMC driver, only function 0 and 1 access is allowed. And there
+	is a limitation for function 0 write, only vendor specific CCCR registers
+	(0xf0 -0xff) are permiited.
+
+	Examples:
+		mlanutl mlan0 sdcmd52rw 1 3      : Read SDIO function 1 register 3
+		mlanutl mlan0 sdcmd52rw 1 1 0x3f : Write 0x3f to SDIO function 1 register 1
+
+sdcmd53rw
+	This command is used to issue a CMD53 read/write data in
+	Secure Digital I/O Interfaces.
+
+	Usage:
+		mlanutl mlanX sdcmd53rw <func> <address> <mode> <blksize> <blknum> [data1] ... [dataN]
+
+	where the parameters are,
+		<func>:     function number (0/1/2/..)
+		<address>:  data address
+		<mode>:     byte mode/block mode (0/1)
+		<blksize>:  block size (32/64/../512, NA for byte mode)
+		<blknum>:   block number or byte number
+		<data1> ... <dataN>:  data for write
+
+	Note: The total data length is block size * block number for block mode
+	or byte number for byte mode. The max data length is 2000-byte.
+	For write the data pattern will be duplicated to data buffer.
+
+	Examples:
+		mlanutl mlan0 sdcmd53rw 0 0x8000 1 0x40 2
+		mlanutl mlan0 sdcmd53rw 1 0x10000 0 1 5 0x0a 0x0b 0x0c 0x0d 0x0e
+
+sdioclock
+	Turn On(1) or Off(0) the SDIO clock.
+
+	Usage:
+		mlanutl mlanX sdioclock 1 (on)
+		mlanutl mlanX sdioclock 0 (off)
+		mlanutl mlanX sdioclock (get the current clock state)
+
+setuserscan
+	Initiate a customized scan and retrieve the results
+
+	Usage:
+		mlanutl mlanX setuserscan [ARGS]
+
+	Where [ARGS]:
+	  ssid="[SSID]"            specify a SSID filter for the scan
+	  chan=[chan#][band][mode] where band is [a,b,g,n] and mode is
+	                           blank for unchange, or 'c' for active or 'p' for passive
+	  bssid=xx:xx:xx:xx:xx:xx  specify a BSSID filter for the scan
+	  wc="[WILDCARD SSID]"     specify a UNIX pattern matching filter (using *
+	                           and ?) for SSIDs found in a broadcast probe
+	  keep=[0 or 1]            keep the previous scan results (1), discard (0)
+	  dur=[scan time]          time to scan for each channel in milliseconds
+	  gap=[gap time]           Time gap between two scans in milliseconds
+	  probes=[#]               number of probe requests to send on each chan
+	                           for each broadcast probe required and each SSID
+	                           specific probe required (1-4)
+	  bss_type=[1,2,3]         BSS type: 1 (Infra), 2(Adhoc), 3(Any)
+          sort_by_ch               Sort by channel number in ascending order.
+                                   Default mode: Sort by Signal Strength in descending order.
+
+	Any combination of the above arguments can be supplied on the command line.
+	If the chan token is absent, a full channel scan will be completed by driver.
+	If the dur or probes tokens are absent, the driver default setting will be
+	used. The bssid and ssid fields, if blank, will produce an unfiltered scan.
+	It's allowed to input multiple ssid/wc entries, the max entry number is 10.
+	The type field will default to 3 (Any) and the keep field will default to 0
+	(Discard).
+
+	Examples:
+	1) Perform an active scan on channels 1, 6, and 11 in the 'g' band:
+		setuserscan chan=1g,6g,11g
+
+	2) Perform a passive scan on channel 11 for 20 ms:
+		setuserscan chan=11gp dur=20
+
+	3) Perform an active scan on channels 1, 6, and 11; and a passive scan on
+	   channel 36 in the 'a' band:
+		setuserscan chan=1g,6g,11g,36ap
+
+	4) Perform an active scan on channel 6 and 36 for specific SSID:
+		setuserscan chan=6g,36a ssid=TestAP1 ssid=TestAP2
+
+	5) Scan all available channels (B/G/N, A bands) for a specific BSSID, keep
+	   the current scan table intact, update existing or append new scan data:
+		setuserscan bssid=00:50:43:20:12:82 keep=1
+
+	6) Scan channel 6, for all infrastructure networks, sending two probe
+	   requests.  Keep the previous scan table intact. Update any duplicate
+	   BSSID/SSID matches with the new scan data:
+		setuserscan chan=6g bss_type=1 probes=2 keep=1
+
+	7) Scan channel 1 and 6, for all networks matching the Mrvl*AP
+	   or AP*Mrvl? patterns and for MrvlTst SSID.  Generate 3 broadcast
+	   probes for the patterns and 3 SSID specific probes for MrvlTst on
+	   both channel 1 and channel 6.
+		setuserscan chan=1g,6g probes=3 wc="Mrvl*AP" wc="AP*Mrvl?" ssid="MrvlTst"
+
+	8) Scan all the channels for specified band.
+		setuserscan chan=0g
+
+	9) Scan channel 1 and 6, send 3 probe requests, scan each channel for 40 ms
+	   with time gap of 50ms between 2 scans
+		setuserscan chan=1g,6g probes=3 dur=40 gap=50
+
+	All entries in the scan table (not just the new scan data when keep=1)
+	will be displayed upon completion by use of the getscantable ioctl.
+cancelscan
+	This command is used to cancel scan
+	Usage:
+		mlanutl mlanX cancelscan
+sleepparams
+	This command is used to set the sleepclock configurations
+
+	Usage:
+		mlanutl mlanX sleepparams [<p1> <p2> <p3> <p4> <p5> <p6>]
+
+	where:
+		p1 is Sleep clock error in ppm (0-65535)
+		p2 is Wakeup offset in usec (0-65535)
+		p3 is Clock stabilization time in usec (0-65535)
+		p4 is Control periodic calibration (0-2)
+		p5 is Control the use of external sleep clock (0-2)
+		p6 is reserved for debug (0-65535)
+
+	Examples:
+		mlanutl mlan0 sleepparams                      : Get current sleepclock configuration
+		mlanutl mlan0 sleepparams 10 1000 2000 1 0 128 : Set sleepclock configuration
+
+sleeppd
+	This command is used to configure the sleep period of the WLAN device.
+
+	Usage:
+		mlanutl mlanX sleeppd [<period>]
+
+	Where the parameter is:
+		period: sleep period in milliseconds. Range 10~60. 0 for disable.
+
+	Examples:
+		mlanutl mlan0 sleeppd            : Get sleep period configuration
+		mlanutl mlan0 sleeppd 10         : Set sleep period to 10 ms
+
+sysclock
+	This command is used to set/get system clocks in MHz.
+	The current system clock, configurable system clocks and all of the
+	supported system clocks will be returned if no parameter provided.
+
+	Examples:
+		mlanutl mlan0 sysclock           : Get system clocks
+		80 80 128 128 128 5 11 16 20 22 32 40 44 64 80 106 128 160 ...
+		(The current system clock is 80 MHz.
+		 The configurable system clocks of non-security, security, non-security
+		 A-MPDU and security A-MPDU are 80 MHz, 128 MHz, 128 MHz and 128 MHz.
+		 The supported system clocks are 5 MHz, 11 MHz, ..., 160 MHz, 182 MHz,
+		 213 MHz, 256 MHz, 320 Mhz, 366 MHz , ... . the Max system clocks is different
+		 for different chips, you could use this command to get the supported system clock)
+
+		mlanutl mlanX sysclock 80        : Set system clock in non-security mode
+		                                  to 80 MHz, no change for others
+		mlanutl mlanX sysclock 0 0 128   : Set system clock in non-security A-MPDU
+		                                  mode to 128 MHz, no changes for others
+
+host_tdls_config
+	This command is used to support channel switch and uapsd for host based tdls
+
+	Usage:
+		mlanutl mlanX host_tdls_config <host_tdls.conf>
+
+
+	Where the parameter is:
+		host_tdls.conf: The configuration file specifying to enable/disable uapsd/cs and related parameters.
+
+	Examples:
+		mlanutl mlan0 host_tdls_config config/host_tdls.conf
+			: enable or disable uapsd/cs, config the channel related ie, based on the configuration file.
+tdls_channel_switch
+	This command is used to send TDLS channel switch request.
+
+	Usage:
+		mlanutl mlanX tdls_channel_switch <tdls.conf>
+
+	Where the parameter is:
+		tdls.conf: The configuration file for sending TDLS channel switch command.
+
+	Examples:
+		mlanutl mlan0 tdls_channel_switch config/tdls.conf
+			: Send TDLS channel switch command, based on the configuration file.
+
+tdls_config
+	This command is used to enable/disable TDLS on device.
+
+	Usage:
+		mlanutl mlanX tdls_config <0/1>
+
+	Where the parameter is:
+		0: Enable TDLS.
+		1: Disable TDLS.
+
+	Examples:
+		mlanutl mlan0 tdls_config 0      : Disable TDLS
+		mlanutl mlan0 tdls_config 1      : Enable TDLS
+
+tdls_cs_params
+	This command is used to set TDLS channel switch params
+
+	Usage:
+		mlanutl mlanX tdls_cs_params <tdls.conf>
+
+	Where the parameter is:
+		tdls.conf: The configuration file specifying the TDLS channel switch params.
+
+	Examples:
+		mlanutl mlan0 tdls_cs_params config/tdls.conf
+			: Set TDLS channel switch params, based on the configuration file.
+
+tdls_debug
+	This command is used for FW debug functionality and tests.
+
+tdls_disable_cs
+	This command is used to disable TDLS channel switch
+
+	Usage:
+		mlanutl mlanX tdls_disable_cs <tdls.conf>
+
+	Where the parameter is:
+		tdls.conf: The configuration file to disable TDLS channel switch.
+
+	Examples:
+		mlanutl mlan0 tdls_disable_cs config/tdls.conf
+			: Disable TDLS channel switch, based on the configuration file.
+
+tdls_discovery
+	This command is used to request TDLS discovery.
+
+	Usage:
+		mlanutl mlanX tdls_discovery <tdls.conf>
+
+	Where the parameter is:
+		tdls.conf: The configuration file to request TDLS discovery.
+
+	Examples:
+		mlanutl mlan0 tdls_discovery config/tdls.conf
+			: Request TDLS discovery based on the configuration file.
+
+tdls_link_status [peer_mac_address]
+	This command is used to get link information about TDLS links or
+    a TDLS link correponding to peer mac address.
+
+	Usage:
+		mlanutl mlanX tdls_link_status <tdls.conf>
+
+	Where the parameter is:
+		tdls.conf: The configuration file to send TDLS command to get current link status.
+
+	Examples:
+		mlanutl mlan0 tdls_link_status config/tdls.conf
+			: Send TDLS command to get current link status based on the configuration file.
+
+tdls_powermode
+	This command is used to send TDLS powermode request.
+
+	Usage:
+		mlanutl mlanX tdls_powermode <tdls.conf>
+
+	Where the parameter is:
+		tdls.conf: The configuration file for sending TDLS powermode command.
+
+	Examples:
+		mlanutl mlan0 tdls_powermode config/tdls.conf
+			: Send TDLS powermode (either 0:Active, 1:PowerSave) command, based on the configuration file.
+
+tdls_setinfo
+	This command is used for setting the capabilities of the TDLS station.
+
+	Usage:
+		mlanutl mlanX tdls_setinfo <tdls.conf>
+
+	Where the parameter is:
+		tdls.conf: The configuration file specifying the capabilities of the TDLS station.
+
+	Examples:
+		mlanutl mlan0 tdls_setinfo config/tdls.conf
+			: Set capabilities of the TDLS station, based on the configuration file.
+
+tdls_setup
+	This command is used to send TDLS setup request.
+
+	Usage:
+		mlanutl mlanX tdls_setup <tdls.conf>
+
+	Where the parameter is:
+		tdls.conf: The configuration file for sending TDLS setup request.
+
+	Examples:
+		mlanutl mlan0 tdls_setup config/tdls.conf
+			: Send TDLS setup request, based on the configuration file.
+
+tdls_stop_channel_switch
+	This command is used to send stop TDLS channel switch request.
+
+	Usage:
+		mlanutl mlanX tdls_stop_channel_switch <tdls.conf>
+
+	Where the parameter is:
+		tdls.conf: The configuration file for sending stop TDLS channel switch command.
+
+	Examples:
+		mlanutl mlan0 tdls_stop_channel_switch config/tdls.conf
+			: Send stop TDLS channel switch command, based on the configuration file.
+
+tdls_teardown
+	This command is used to send TDLS teardown request.
+
+	Usage:
+		mlanutl mlanX tdls_teardown <tdls.conf>
+
+	Where the parameter is:
+		tdls.conf: The configuration file for requesting teardown of TDLS link.
+
+	Examples:
+		mlanutl mlan0 tdls_teardown config/tdls.conf
+			: Request teardown of TDLS link, based on the configuration file.
+
+thermal
+	This command is used to get the current thermal reading.
+
+	Examples:
+		mlanutl mlan0 thermal            : Get thermal reading
+
+ts_status
+	This command queries the FW for the status of TSIDs 0 through 7
+	  configured via call admission control and displays the results in a
+	  table.
+
+	Usage:
+		mlanutl mlanX ts_status
+
+tsf
+	Get the TSF timer value for the station. Station maintains a TSF timer with
+	modulus 2^64 counting in increments of microseconds.
+
+	Usage:
+		mlanutl mlanX tsf
+
+txaggrctrl
+	This command is used to enable/disable TX AMPDU on infra link when TDLS link is established
+
+	Usage:
+		mlanutl mlanX txaggrctrl [m]
+
+	Where:
+		[m]: 1 to enable TX AMPDU on infra link; 0 to disable TX AMPDU on infra link
+
+	Examples:
+		mlanutl mlan0 txaggrctrl	: Get current TX AMPDU status on infra link
+		mlanutl mlan0 txaggrctrl 0	: Disable TX AMPDU on infra link
+		mlanutl mlan0 txaggrctrl 1	: Enable TX AMPDU on infra link
+
+	Note:
+		The set command only works when TDLS link is established.
+
+txbufcfg
+	This command can be used to get current buffer size.
+
+	eg:
+	mlanutl mlanX txbufcfg 	  - This will display the current buffer size.
+
+	Note:- The actual tx buf size will depends on AP's capability and max transmit buffer size.
+
+txratecfg
+	This command is used to set/get the transmit data rate.
+
+	Note:
+	1) The data rate can be set only after association.
+
+	2) If the reassoc is OFF driver reset the data rate to auto if the connection state is disconnected.
+	Please note that user has to re-issue the set data rate command if the driver is disconnected.
+
+	3) If the reassoc is ON driver remembers the data rate set by the user, if the driver is
+	disconnected user does not have to re-issue the set data rate again.
+
+	Where
+	[l] is <format>
+	<format> - This parameter specifies the data rate format used in this command
+		0:    LG
+		1:    HT
+		2:    VHT
+		0xff: Auto
+
+	[m] is <index>
+	<index> - This parameter specifies the rate or MCS index
+	If <format> is 0 (LG),
+		0	1 Mbps
+		1	2 Mbps
+		2	5.5 Mbps
+		3	11 Mbps
+		4	6 Mbps
+		5	9 Mbps
+		6	12 Mbps
+		7	18 Mbps
+		8	24 Mbps
+		9	36 Mbps
+		10	48 Mbps
+		11	54 Mbps
+	If <format> is 1 (HT),
+		0	MCS0
+		1	MCS1
+		2	MCS2
+		3	MCS3
+		4	MCS4
+		5	MCS5
+		6	MCS6
+		7	MCS7
+		8	MCS8
+		9	MCS9
+		10	MCS10
+		11	MCS11
+		12	MCS12
+		13	MCS13
+		14	MCS14
+		15	MCS15
+	If <format> is 2 (VHT),
+		0	MCS0
+		1	MCS1
+		2	MCS2
+		3	MCS3
+		4	MCS4
+		5	MCS5
+		6	MCS6
+		7	MCS7
+		8	MCS8
+		9	MCS9
+	[n] is <nss>
+	<nss> - This parameter specifies the NSS. It is valid only for VHT
+	If <format> is 2 (VHT),
+		1	NSS1
+		2	NSS2
+
+	Examples:
+		mlanutl mlan0 txratecfg          : Read the current data rate setting
+		mlanutl mlan0 txratecfg 0 3      : Set fixed Tx rate to 11 Mbps
+		mlanutl mlan0 txratecfg 0 11     : Set fixed Tx rate to 54 Mbps
+		mlanutl mlan0 txratecfg 1 3      : Set fixed Tx rate to MCS3
+		mlanutl mlan0 txratecfg 2 3 2    : Set fixed Tx rate to MCS3 for NSS2
+		mlanutl mlan0 txratecfg 0xff     : Disable fixed rate and uses auto rate
+
+verext
+	Retrieve and display an extended version string from the firmware
+
+	Usage:
+		mlanutl mlanX verext [#]
+
+	where [#] is an optional argument to retrieve a specific version string,
+	omission of the argument retrieves the 0 indexed string.
+
+version
+	This is used to get the current version of the driver and the firmware.
+
+vhtcfg
+	This command is used to set and get various 11ac specific configuration
+	for transmission and reception. For the SET operation, all paramaters
+	may be applied. For the GET operation, only the first two parameters are applied.
+	The 6th argument "rx_mcs_set" can be used to disbale/enable 802.11ac.
+
+	where <j> is <band>
+	<band> - This is the band setting for the vhtcfg
+		0: Settings for both 2.4G and 5G bands (for SET operation, 11N BW only)
+		1: Settings for 2.4G band (for 11N BW only)
+		2: Settings for 5G band
+
+	where <k> is <txrx>
+	<txrx> - This parameter specifies the configuration of VHT operation for TX or/and VHT capabilities
+		0: Unspecified
+		1: configuration of VHT capabilities for Tx operations (STA only)
+		2: configuration of VHT capabilities for association (STA only)
+		3: configuration of VHT capabilities (uAP only)
+	Note: For the STA, the VHT capabilities configuration is applied in association,
+	      whereas the VHT operations configuration is actually used in Tx.
+
+	where [l] is <bwcfg>
+	<bwcfg> - This parameter specifies the bandwidth (BW) configuration
+		  applied to the vhtcfg.
+	If <txrx> is 1/3 (Tx operations),
+		0: Tx BW follows the BW (20/40 MHz) from 11N CFG
+		1: Tx BW follows the BW (80/160/80+80 MHz) from VHT Capabilities
+		   defined in <vhtcap> below for 5G band.
+	If <txrx> is 2 (association),
+		0: Rx BW follows the BW (20/40 MHz) from 11N CFG
+		1: Rx BW follows the BW (80/160/80+80 MHz) from VHT Capabilities
+		   defined in <vhtcap> below for 5G band.
+
+	where [m] is <vhtcap>
+	<vhtcap> - This parameter specifies the VHT capabilities info if <txrx> is 2 (association)
+		   or the VHT Tx operations if <txrx> is 1 (Tx operations).
+		   The VHT Tx operation should be a subset of VHT capabilities for association.
+		   It is a bitmap and should be used as follows:
+
+		Bit 31-30: Reserved and set to 0
+		Bit 29:    TX antenna pattern consistency
+			   1: antenna pattern does not change
+			   0: antenna pattern might change
+		Bit 28:    RX antenna pattern consistency
+			   1: antenna pattern does not change
+			   0: antenna pattern might change
+		Bit 27-26: VHT link adaptation capable
+			   0: no feedback of VHT MFB from the STA
+			   1: unsolicted feedback of VHT MFB from the STA
+			   2: both response and unsolicted feedback of VHT MFB
+			      from the STA
+			   3: reserved and set to 0
+		Bit 25-23: Maximum A-MPDU length exponent
+		Bit 22:    +HTC-VHT capable (1: enable. 0 disable)
+		Bit 21:    VHT TXOP PS
+		Bit 20:    MU beamformee capable (1: enable. 0 disable)
+		Bit 19:    MU beamformer capable (1: enable. 0 disable)
+		Bit 18-16: Number of sounding dimensions (set to maximum-1
+			   if Bit 11 is 1. Otherwise, reserved and set to 0)
+		Bit 15-13: Compressed steering number of beamformer
+			   antennas supported (set to maximum-1 if Bit 12 is 1.
+			   Otherwise, reserved and set to 0)
+		Bit 12:    SU beamformee capable (1: enable. 0 disable)
+		Bit 11:    SU beamformer capable (1: enable. 0 disable)
+		Bit 10-8:  Rx STBC
+			   0: no support
+			   1: support of 1 spatial stream
+			   2: support of 1-2 streams
+			   3: support of 1-3 spatial streams
+			   4: support of 1-4 spatial streams
+			   5-7: reserved and set to 0
+		Bit 7:     TX STBC (1: enable. 0 disable)
+		Bit 6:     Short GI for 160 and 80+80 MHz (1: enable. 0 disable)
+		Bit 5:     Short GI for 80 MHz (1: enable. 0 disable)
+		Bit 4:     Rx LDPC (1: enable. 0 disable)
+		Bit 3-2:   Supported channel width set.
+			   0: no support of either 160 or 80+80 MHz.
+			   1: support of 160 MHz
+			   2: support of both 160 and 80+80 MHz.
+			   3: reserved and set to 0.
+		Bit 1-0:   Maximum MPDU length
+			   0: 3895 octets.
+			   1: 7991 octets.
+			   2: 11454 octets.
+			   3: reserved and set to 0.
+
+	Note: for the STA, if <txrx> is 1 (Tx operations), the bitmap for <vhtcap> may be simplied as follows:
+		Bit 31-8: Reserved and set to 0
+		Bit 7:    Tx STBC (1: enable. 0 disable)
+		Bit 6:    Reserved and set to 0
+		Bit 5:    Short GI for 80 Mhz (1: enable. 0 disable)
+		Bit 4:    LDPC (1: enable. 0 disable)
+		Bit 3-0:  Reserved and set to 0
+
+	where [n] is <tx_mcs_map>,
+	<tx_mcs_map> - This parameter specifies the TX MCS map. It may not be used for the STA if <txrx> is 1 (Tx operations).
+		It is a bitmap and should be used as following
+		Bit 15-0:  MCS map, which is defined as folows:
+			Bit 15-14: Max MCS for 8 SS
+			Bit 13-12: Max MCS for 7 SS
+			Bit 11-10: Max MCS for 6 SS
+			Bit 9-8:   Max MCS for 5 SS
+			Bit 7-6:   Max MCS for 4 SS
+			Bit 5-4:   Max MCS for 3 SS
+			Bit 3-2:   Max MCS for 2 SS
+			Bit 1-0:   Max MCS for 1 SS
+
+	where [o] is <rx_mcs_map>.
+	<rx_mcs_map> - This parameter specifies the RX MCS map. It may not be used for the STA if <txrx> is 1 (Tx operations).
+		It is a bitmap with the same sructure as for <tx_mcs_map>
+		rx_mcs_map = 0xffff : FW will disable 802.11ac
+                rx_mcs_map = others : FW will enable 802.11ac
+
+	Note: The user setting of vhtcap may be overwritten by the driver
+	      if the setting of those fields is beyond the hardware capabilities.
+
+	Examples:
+		mlanutl mlan0 vhtcfg 2 1            : Get current VHT configuration in 5GHz for the STA.
+		mlanutl mlan0 vhtcfg 2 2            : Get maximum VHT configuration in 5GHz for the STA.
+		mlanutl mlan0 vhtcfg 2 1 1 0x000001f0
+		    : Set the Tx operations configuration in 5GHz for the STA,
+		      Tx BW follows the VHT Capabilities.
+		mlanutl mlan0 vhtcfg 2 2 0 0x000001f0 0xfff5 0xfffa
+		    : Set the VHT capabilities configuration in 5GHz for the STA,
+		      the Tx supports MCS 0-8 for both 1 and 2 spatial streams,
+		      while the Rx supports MCS 0-9 for both 1 and 2 spatial streams.
+		mlanutl uap0 vhtcfg 2 3 0 0x000001f0 0xfffa 0xfffa
+		    : Set the current/maximum VHT configuration in 5GHz for the uAP.
+		      Both Tx and Rx supports MCS 0-9 for both 1 and 2 spatial streams.
+		mlanutl uap0 vhtcfg 2 3 0 0x000001b0
+		    : Set the VHT capability information in 5GHz for the uAP, and keep the Tx/Rx MCS Map same as before.
+
+opermodecfg
+	This command is used to set and get 11ac Operating Mode Notification configuration.
+
+	where <m> is <bw>
+	<bw> - This is the channel width setting for the opermodecfg
+		1: 20MHz
+		2: 40MHz
+		3: 80MHz
+		4: 160MHz or 80+80MHz
+
+	where <n> is <nss>
+	<nss> - This parameter specifies the nss that the STA can receive.
+		1: NSS1
+		2: NSS2
+		3: NSS3
+		4: NSS4
+		5: NSS5
+		6: NSS6
+		7: NSS7
+		8: NSS8
+
+wakeupreason
+	This command is used to get the host sleep wakeup reason.
+
+	Usage:
+		mlanutl mlanX wakeupreason
+		mlanutl uapX wakeupreason
+	Examples:
+		mlanutl mlan0 wakeupreason        : Get the host sleep wakeup reason
+		mlanutl uap0 wakeupreason         : Get the host sleep wakeup reason
+		0:  unknown
+		1:  Broadcast data matched
+		2:  Multicast data matched
+		3:  Unicast data matched
+		4:  Maskable event matched
+		5.  Non-maskable event matched
+		6:  Non-maskable condition matched (EAPoL rekey)
+		7:  Magic pattern matched
+		8:  Control frame matched
+		9:  Management frame matched
+		Others: reserved. (0)
+
+warmreset
+	This command is used for warm reset of the interface.
+
+	Usage:
+		mlanutl mlanX warmreset
+
+wpssession
+	This command is used to control wps session. No argument is used to get.
+
+	where value of n is:
+		0   -- Disable
+		1   -- Enable
+
+	Examples:
+		mlanutl mlan0 wpssession 1           : Enable wpssession
+		mlanutl mlan0 wpssession             : Get wpssession status
+
+wmmcfg
+	This command is used to control WMM. No argument is used to get.
+
+	where value of n is:
+		0   -- Disable
+		1   -- Enable
+
+	Examples:
+		mlanutl mlan0 wmmcfg 1           : Enable WMM
+		mlanutl mlan0 wmmcfg             : Get WMM status
+
+wmmparamcfg
+	This command is used to configure WMM paramameters.
+
+	Usage:
+		mlanutl mlanX wmmparamcfg [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP]
+                                          [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP]
+                                          [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP]
+                                          [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP]
+
+	The supported option are:
+           AC_BE: 0
+           AC_BK: 1
+           AC_VI: 2
+           AC_V0: 3
+           AIFSN: AIFSN value
+           ECW_MAX: ECW max
+           ECW_MIN: ECW min
+           TX_OP: TXOP Limit
+           empty - Get current WMM parameters
+
+        Example:
+        mlanutl mlanX wmmparamcfg 0 3 10 4 0
+           Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+
+        mlanutl mlanX wmmparamcfg 1 7 10 4 0
+           Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+
+        mlanutl mlanX wmmparamcfg 2 2 4 3 94
+           Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94
+
+        mlanutl mlanX wmmparamcfg 3 2 3 2 47
+           Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47
+
+        mlanutl mlanX wmmparamcfg
+            Get current WMM parameters
+
+        mlanutl mlanX wmmparamcfg 0 3 10 4 0 1 7 10 4 0 2 2 4 3 94 3 2 3 2 47
+            Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+            Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+            Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94
+            Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47
+
+wwscfg
+	This command is used to set/get the WWS (World Wide Safe) mode.
+
+	where value of m is:
+		0       -- Disable WWS mode (default)
+		1       -- Enable WWS mode
+
+	Examples:
+		mlanutl mlan0 wwscfg             : Get WWS mode
+		mlanutl mlan0 wwscfg 1           : Enable WWS mode
+		mlanutl mlan0 wwscfg 0           : Disable WWS mode
+
+mc_cfg
+	This command is used to set/get the channel time.
+
+	Usage:
+		mlanutl mlanX mc_cfg [n]
+
+	where <n> :  Channel time in microseconds.
+
+	Examples:
+		mlanutl mlanX mc_cfg			: Get Channel time and buffer weight.
+		mlanutl mlanX mc_cfg 10000		: Set Channel time to 10000us.
+
+mc_policy
+        This command is used to set/get the multi-channel policy.
+	Note: This is a device specific command. Hence, setting on one interface is
+		reflected on all other interfaces.
+
+        Usage:
+                mlanutl mlanX mc_policy [n]
+
+        where <n> :  Multi-channel policy
+
+        Examples:
+                mlanutl mlanX mc_policy			: Get multi-channel policy setting.
+                mlanutl mlanX mc_policy 1		: Set multi-channel policy to 1.
+                mlanutl mlanX mc_policy 0		: Disable multi-channel policy
+
+mc_cfg_ext
+    This command is used to set/get the drcs parameters.
+
+    Usage:
+        mlanutl mlanX mc_cfg_ext [c] [s] [u] [m] <a> <b> <d> <e>
+
+    where:
+    channel index0:
+        [c] :  chantime(in TU)
+        [s] :  switchtime(in TU)
+        [u] :  undozetime(in TU)
+        [m] :  mode :0x0 --- PM1(default)
+                     0x1 --- Null2Self
+    channel index1:
+        <a> :  chantime(in TU)
+        <b> :  switchtime(in TU)
+        <d> :  undozetime(in TU)
+        <e> :  mode :0x0 --- PM1(default)
+                     0x1 --- Null2Self
+    Note:
+        channel index0: the first channel
+        channel index1: the second channel
+        undozetime should be less than other channel's switchtime
+        If want to set two channels the same parameters, just ignore the last four parameters and
+        use [c] [s] [u] [m] to set.
+    Examples:
+        mlanutl mlanX mc_cfg_ext                        : Get the drcs parameters for two channels.
+        mlanutl mlanX mc_cfg_ext 15 10 5 0              : Set two channels:channeltime 15TU, switchtime 10TU, undozetime 5TU, mode PM1
+        mlanutl mlanX mc_cfg_ext 15 10 5 0 25 15 9 0    : Set channel index0: channeltime 17TU, switchtime 10TU, undozetime 5TU, mode PM1;
+                                                        set channel index1: channeltime 25TU, switchtime 15TU, undozetime 9TU, mode PM1.
+
+cfg_noa
+	This is used to get/set P2P NoA (Notice of Absence) parameters only for P2P GO.
+
+	Usage:
+		mlanutl p2pX cfg_noa [h] [i] [j] [k] [l]
+
+	where:
+		[h] : noa_enable : 1/0 Set to 1 to enable NoA, 0 to disable NoA.
+		[i] : index      : 0 - 255 Identifies an instance of NoA timing.
+		[j] : count      : 1 - 255 Indicates the number of absence intervals.
+				   255 means a continuous schedule.
+		[k] : duration   : Indicates the maximum duration in units of microseconds
+				   that P2P GO can remain absent following the start of
+				   a NoA interval.
+		[l] : interval   : Indicates the length of the NoA interval in units of
+				   microseconds.
+
+        Examples:
+                mlanutl p2pX cfg_noa						: Get noa configuration.
+                mlanutl p2pX cfg_noa 1 1 255 50 100			: Set noa configuration.
+
+cfg_opp_ps
+	This is used to get/set P2P OPP-PS parameters only for P2P GO.
+
+	Usage:
+		mlanutl p2pX cfg_opp_ps [m] [n]
+
+	where:
+		[m] : ps_enable  : 1/0 Set to 1 to indicate P2P GO is using opportunistic
+				   power save. Set to 0 if opportunistic power save is disabled.
+		[n] : ct_window  : A period of time in TU after a TBTT during which P2P GO
+				   is present. 0 indicates that there shall be no
+				   CTWindow (Client Traffic Window).
+
+        Examples:
+                mlanutl p2pX cfg_opp_ps					: Get noa configuration.
+                mlanutl p2pX cfg_opp_ps 1 7				: Set noa configuration.
+
+rxpktcoal_cfg
+	This is used to get/set RX packet coalescing paramters
+	Usage:
+		mlanutl mlanX rxpktcoal_cfg [m] [n]
+
+	where:
+        [m]: pkt_threshold: count after which packets would be sent to host. Valid values 1-7
+        [n]: delay: timeout in ms after which packets would be sent to host. Valid values 1-4
+        Coalescing is disabled if both or either of packet_thershold and delay is zero
+
+        RX packet coalescing parameters can be changed only when device is in
+        idle state i.e. all interfaces are disconnected.
+
+get_sensor_temp
+        This command is used to get SOC temperature
+        Usage:
+                mlanutl mlanX get_sensor_temp
+
+indrstcfg
+    This command is used to set/ get independent reset mode configuration
+
+    Usage :
+            mlanutl <interface> indrstcfg <ir_mode> [gpio_pin]
+
+            interface : mlanX, uapX
+            ir_mode   : 0 -- Disable
+                        1 -- Enable out band reset, disable in band
+                        2 -- Enable in band, disable out band
+            gpio_pin  : 255 -- Default pin for reset
+                        any other number for changing the gpio for reset.
+
+   Example :
+            mlanutl mlan0 indrstcfg 1 255   : Set default pin on interface mlan0 as reset pin
+            mlanutl mlan0 indrstcfg 0       : Disable the gpio 17 as reset pin on interface mlan0
+            mlanutl mlan0 indrstcfg         : Get the status and the pin used for reset pin
+            mlanutl mlan0 indrstcfg 2       : Enable in band reset mode
+
+    This command is used to set FW wakeup method and GPIO pin
+    Usage :
+            mlanutl <interface> fwwakeupmethod <method> <GPIO_pin>
+            interface : mlanX
+            method:
+                        1       -- Firmware wakeup through the interface command interrupt
+                                -- (default setting for SDIO/PCIe/USB)
+                        2       -- Firmware wakeup through the GPIO pin
+            GPIO_pin:  If firware wakeup throug GPIO pin, [g] is GPIO pin number
+    Example :
+            mlanutl mlan0 fwwakeupmethod     : Get current wakeup method
+            mlanutl mlan0 fwwakeupmethod 1   : Set wakeup method is interface method
+            mlanutl mlan0 fwwakeupmethod 2 5 : Set wakeup method is GPIO method and GPIO pin is 5.
+
+robustcoex
+    This command is used to set robust coex.
+
+    Usage :
+            mlanutl <interface> robustcoex <gpiocfg> [Enable/Disable] [gpionum] [gpiopolarity]
+            Enable/Disable : 0 -- Disable ; 1 -- Enable
+            gpionum : Number of gpio
+            gpiopolarity : polarity of gpio
+
+    Example :
+            mlanutl mlan0 robustcoex gpiocfg 1 4 1 : Enable robustcoex gpio, set gpionum to 4 and gpiopolarity to 1
+            mlanutl mlan0 robustcoex gpiocfg 0     : Disable robustcoex gpio
+
+keep_connect
+    This command is used to refuse disconnect
+
+    Usage :
+            mlanutl <interface> keep_connect [Enable/Disable]
+            Enable/Disable : 0 -- Disable ; 1 -- Enable
+
+    Example :
+            mlanutl mlan0 keep_connect 1 : Enable keep connect
+            mlanutl mlan0 keep_connect 0 : Disable keep connect
+ctrldeauth
+    This command is used to set/get firmware ctrldeauth setting
+    Usage :
+            mlanutl uapX ctrldeauth <n>
+
+    Where value of n is :
+            0 -- Firmware will use default behavior
+            1 -- Firmware will not send deauth packet when uap move to another channel.
+
+    Example :
+            mlanutl uap0 ctrldeauth   : Get current setting
+            mlanutl uap0 ctrldeauth   : Firmware will not send deauth packet when uap move to different channel.
+
+bootsleep
+    This command is used to set and get boot sleep configure.
+
+    Usage :
+            mlanutl mlanX/uapX bootsleep <enable>
+            <enable> :  enable boot sleep
+                     :  0 - disable boot sleep
+                     :  1 - enable boot sleep
+
+    Example :
+            mlanutl mlan0/uap0 bootsleep 1       : Enable boot sleep
+            mlanutl mlan0/uap0 bootsleep         : Get boot sleep configure
+
+
+===============================================================================
diff --git a/wlan_sd8897/README_OPENWRT b/wlan_sd8897/README_OPENWRT
new file mode 100644
index 0000000..90a6722
--- /dev/null
+++ b/wlan_sd8897/README_OPENWRT
@@ -0,0 +1,356 @@
+=================================================================================
+        U S E R  M A N U A L  F O R  OpenWrt
+
+This section describes detailed steps to add Marvell Wireless NIC (sdio/usb/pcie)
+driver to OpenWrt build system.
+
+Add marvell driver (sdio/usb/pcie) to OpenWrt build system.
+
+1. Go to the openwrt source code folder
+2. make menuconfig
+	choose x86 platform
+	other general openwrt configurations please refer to:
+	https://wiki.openwrt.org/doc/howto/build
+3. After make menuconfig
+	make sure that following configurations are set in .config
+	CONFIG_PACKAGE_hostapd=y
+	CONFIG_PACKAGE_hostapd-common=y
+	CONFIG_PACKAGE_kmod-cfg80211=y
+4. Go to the openwrt source code folder and Compile openwrt BSP
+	make V=s [-j[number]]
+5. After the first time compiling, openwrt/dl folder will be created
+   , related packages will be downloaded during compiling, so you need
+   network access as long as you need to compile openwrt BSP.
+6. Go to the openwrt source code folder: openwrt/dl/
+7. Find compat-wireless-xxxx-xx-xx.tar.bz2, and uncompress the package
+8. Go to compat-wireless-xxxx-xx-xx/drivers/net/wireless/marvell/
+9. Modify compat-wireless-xxxx-xx-xx/drivers/net/wireless/marvell/Kconfig
+
+	source "drivers/net/wireless/marvell/libertas/Kconfig"
+	source "drivers/net/wireless/marvell/libertas_tf/Kconfig"
+	source "drivers/net/wireless/marvell/mwifiex/Kconfig"
+	+source "drivers/net/wireless/marvell/mrvl-pcie/Kconfig"
+	+source "drivers/net/wireless/marvell/mrvl-sd8xxx/Kconfig"
+	+source "drivers/net/wireless/marvell/mrvl-usb/Kconfig"
+
+10. Modify compat-wireless-xxxx-xx-xx/drivers/net/wireless/marvell/Makefile
+
+	obj-$(CPTCFG_LIBERTAS_THINFIRM)	+= libertas_tf/
+	obj-$(CPTCFG_MWIFIEX)	+= mwifiex/
+	+obj-$(CPTCFG_MRVL_PCIE)  += mrvl-pcie/
+	+obj-$(CPTCFG_MRVL_SD8XXX) += mrvl-sd8xxx/
+	+obj-$(CPTCFG_MRVL_USB) += mrvl-usb/
+
+11. Go to compat-wireless-xxxx-xx-xx/drivers/net/wireless/marvell/
+12. mkdir (mrvl-pcie/mrvl-sd8xxx/mrvl-usb)
+13. copy (pcie/sdio/usb)/wlan_src/* to (mrvl-pcie/mrvl-sd8xxx/mrvl-usb)
+14. Go to mrvl-pcie/mrvl-sd8xxx/mrvl-usb
+15. Add new Kconfig:
+	mrvl-pcie/Kconfig
+
+	+config MRVL_PCIE
+	+	tristate "Marvell Wireless Driver for PCIE 8997"
+	+	depends on m
+	+	depends on PCI
+	+	depends on CFG80211
+	+	depends on WIRELESS_EXT
+	+	---help---
+	+	  This adds support for wireless adapters based on Marvell
+	+	  pcie 8997 chipsets with PCIe interface.
+	+
+	+	  If you choose to build it as a module, it will be called
+	+	  pcie8xxx.
+
+	mrvl-sd8xxx/Kconfig
+
+	+config MRVL_SD8XXX
+	+	tristate "Marvell sdio 802.11n/802.11ac Wireless cards"
+	+	depends on m
+	+	depends on MMC
+	+	depends on CFG80211
+	+	depends on WIRELESS_EXT
+	+	---help---
+	+	  A driver for Marvell sdio 802.11n/802.11ac Wireless cards.
+
+	mrvl-usb/Kconfig
+
+	+config MRVL_USB
+	+	tristate "Marvell sdio 802.11n/802.11ac Wireless cards"
+	+	depends on m
+	+	depends on USB
+	+	depends on CFG80211
+	+	depends on WIRELESS_EXT
+	+	---help---
+	+	A driver for Marvell sdio 802.11n/802.11ac Wireless cards.
+
+16. Open moal_main.h under (mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/mlinux/
+
+	find #define COMPAT_VERSION_CODE KERNEL_VERSION_CODE(x, x, x)
+	modify (x, x, x) to the kernel version code of backports/compat-wireless package
+
+	backported kernel version code could be found in
+	compat-wireless-xxxx-xx-xx/version as following
+
+	'BACKPORTED_KERNEL_VERSION="v4.4-rc5-1913-gc8fdf68"'
+	v4.4 means KERNEL_VERSION(4, 4, 0)
+
+17. Modify (mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/Makefile
+
+	# OpenWrt support
+	-CONFIG_OPENWRT_SUPPORT=n
+	+CONFIG_OPENWRT_SUPPORT=y
+
+	...
+
+	#############################################################################
+	# Select Platform Tools
+	#############################################################################
+
+	MODEXT = ko
+	-ccflags-y += -I$(M)/mlan
+	+ccflags-y += -I$(M)/drivers/net/wireless/marvell/(mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/mlan
+	ccflags-y += -DLINUX
+
+
+	ifeq ($(CONFIG_EMBEDDED_SUPP_AUTH), y)
+	-ccflags-y += -I$(M)/mlan/esa
+	-ccflags-y += -I$(M)/mlan/esa/common
+	+ccflags-y += -I$(M)/drivers/net/wireless/marvell/(mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/mlan/esa
+	+ccflags-y += -I$(M)/drivers/net/wireless/marvell/(mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/mlan/esa/common
+	endif
+
+18. compress compat-wireless-xxxx-xx-xx to its original .tar.bz2 format
+19. Go to the openwrt source code folder: openwrt/package/kernel/mac80211/
+20. Modify openwrt/package/kernel/mac80211/Makefile.
+
+	PKG_DRIVERS = \
+		adm8211 \
+		ath ath5k ath9k ath9k-common ath9k-htc ath10k \
+		b43 b43legacy \
+		carl9170 \
+		hermes hermes-pci hermes-pcmcia hermes-plx\
+		iwl-legacy iwl3945 iwl4965 iwlwifi \
+		lib80211 \
+		libipw ipw2100 ipw2200 \
+		libertas-sdio libertas-usb libertas-spi \
+		mac80211-hwsim \
+		mt7601u \
+		mwl8k mwifiex-pcie \
+		+mrvl-pcie \
+		+mrvl-sd8xxx \
+		+mrvl-usb \
+		p54-common p54-pci p54-spi p54-usb \
+		rt2x00-lib rt2x00-pci rt2x00-usb \
+		rt2400-pci rt2500-pci rt2500-usb \
+
+	...
+
+	+define KernelPackage/mrvl-pcie
+	+  $(call KernelPackage/mac80211/Default)
+	+  TITLE:=Marvell pcie wireless driver
+	+  URL:=http://wireless.kernel.org/en/users/Drivers/mwifiex
+	+  DEPENDS+= @PCI_SUPPORT +kmod-mac80211 +@DRIVER_11N_SUPPORT
+	+  FILES:= \
+	+	$(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-pcie/mlan.ko \
+	+	$(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-pcie/pcie8xxx.ko
+	+  AUTOLOAD:=$(call AutoProbe,mrvl-pcie)
+	+endef
+
+	+define KernelPackage/mrvl-pcie/description
+	+ Kernel modules for Marvell pcie 802.11n/802.11ac PCIe Wireless cards
+	+endef
+
+	+define KernelPackage/mrvl-sd8xxx
+	+  $(call KernelPackage/mac80211/Default)
+	+  DEPENDS+= +kmod-cfg80211 +kmod-lib80211 +kmod-mmc +@DRIVER_WEXT_SUPPORT
+	+  TITLE:=Marvell sdio Wireless Driver
+	+  FILES:= \
+	+	$(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-sd8xxx/mlan.ko \
+	+	$(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-sd8xxx/sd8xxx.ko
+	+  AUTOLOAD:=$(call AutoProbe, mrvl-sd8xxx)
+	+endef
+
+	+define KernelPackage/mrvl-sd8xxx/description
+	+ Kernel modules for Marvell sdio 802.11n/802.11ac Wireless cards
+	+endef
+
+	+define KernelPackage/mrvl-usb
+	+  $(call KernelPackage/mac80211/Default)
+	+  DEPENDS+= @USB_SUPPORT +kmod-cfg80211 +kmod-usb-core +kmod-lib80211 +@DRIVER_WEXT_SUPPORT
+	+  TITLE:=Marvell usb Wireless Driver
+	+  FILES:= \
+	+	$(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-usb/mlan.ko \
+	+	$(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-usb/usb8xxx.ko
+	+  AUTOLOAD:=$(call AutoProbe, mrvl-usb)
+	+endef
+
+	+define KernelPackage/mrvl-usb/description
+	+ Kernel modules for Marvell usb 802.11n/802.11ac Wireless cards
+	+endef
+
+	...
+
+	config-$(call config_package,mwl8k) += MWL8K
+	config-$(call config_package,mwifiex-pcie) += MWIFIEX MWIFIEX_PCIE
+	+config-$(call config_package,mrvl-pcie) += MRVL_PCIE
+	+config-$(call config_package,mrvl-sd8xxx) += MRVL_SD8XXX
+	+config-$(call config_package,mrvl-usb) += MRVL_USB
+	config-$(call config_package,rtl8180) += RTL8180
+	config-$(call config_package,rtl8187) += RTL8187
+
+	...
+
+	$(eval $(call KernelPackage,mwl8k))
+	$(eval $(call KernelPackage,mwifiex-pcie))
+	+$(eval $(call KernelPackage,mrvl-pcie))
+	+$(eval $(call KernelPackage,mrvl-sd8xxx))
+	+$(eval $(call KernelPackage,mrvl-usb))
+	$(eval $(call KernelPackage,p54-common))
+
+OpenWrt configuration
+21. Go to OpenWrt srouce code folder
+22. Make menuconfig
+
+   select one of the newly added marvell driver
+	│-> Kernel modules
+	│	-> Wireless Drivers
+	|		->mrvl-pcie
+	|		->mrvl-sd8xxx
+	|		->mrvl-usb
+
+23. Other general configurations, please refer to the https://wiki.openwrt.org/doc/howto/build
+24. after menucofig make soure that following configurations are seletect
+
+	CONFIG_PACKAGE_hostapd=y
+	CONFIG_PACKAGE_hostapd-common=y
+	CONFIG_PACKAGE_kmod-cfg80211=y
+
+	Go to openwrt folder and re-build openwrt BSP
+	make V=s [-j[number]]
+
+	After openwrt BSP is successfully build, kernel modules could be found
+	both in the build folder and the running OS once openwrt is boot up.
+	kernel modules will be written to u-disk while openwrt img is burned to u-disk.
+
+	kernel modules in openwrt build dir:
+	openwrt/build_dir/target-x86_64_uClibc-0.9.33.2/linux-x86_64/compat-wireless-2016-01-10/
+	ipkg-x86_64/kmod-mrvl-(usb/sd8xxx/pcie)/lib/modules/3.18.45/
+
+	kernel modules in openwrt running OS:
+	/lib/modules/xx.xx.xx/
+
+25. To support SDIO card in OpenWrt x86 platforma
+	Add following kernel configurations for OpenWrt
+	Go to OpenWrt source code foler
+
+	make kernel_menuconfig
+
+	Device Drivers---->
+		--- MMC/SD/SDIO card support
+                  [ ]   MMC debugging
+                  [ ]   MMC host clock gating
+                  *** MMC/SD/SDIO Card Drivers ***
+                  < >   MMC block device driver
+                  < >   SDIO UART/GPS class support
+                  < >   MMC host test driver
+                  *** MMC/SD/SDIO Host Controller Drivers ***
+                  <*>   Secure Digital Host Controller Interface support
+                  <*>   SDHCI support on PCI bus
+                  [ ]     Ricoh MMC Controller Disabler
+                  < >   SDHCI platform and OF driver helper
+                  < >   Winbond W83L51xD SD/MMC Card Interface support
+                  < >   TI Flash Media MMC/SD Interface support
+                  < >   ENE CB710 MMC/SD Interface support
+                  < >   VIA SD/MMC Card Reader Driver
+                  < >   Renesas USDHI6ROL0 SD/SDIO Host Controller support
+
+	Compile OpenWrt using Make V=s, then write openwrt img to u-disk and boot up
+
+26. Bring up marvell wireless NIC on OpenWrt system
+
+	copy pcie/sdio/usb firmware to /lib/firmware/mrvl folder
+	On OpenWrt running OS
+	scp usrname@<target_pc ip address>:/path/firmware.bin /lib/firmware/mrvl
+
+	Another way is to add firmware binary to OpenWrt source code
+
+27. load marvell pcie/sdio/usb driver modules
+
+    End with Marvell Wirelss NIC card bring up on OpenWrt
+
+=======================================================================================================
+If firmware is copied to OpenWrt via scp, following steps could be ignored.
+28. Add firmware binary in OpenWrt
+	Copy pcie/usb/sdio firmware to folder:
+	openwrt/build_dir/target-x86_64_uClibc-0.9.33.2/linux-firmware-52442afee9907bc32a058f22bb3295d040677c26/mrvl/
+29. Go to penwrt/package/firmware/linux-firmware
+	Modify marvell.mk as following
+
+	+Package/mrvl-pcie-firmware = $(call Package/firmware-default,Marvell PCIE8997 firmware)
+	+define Package/mrvl-pcie-firmware/install
+	+	$(INSTALL_DIR) $(1)/lib/firmware/mrvl
+	+	$(INSTALL_DATA) \
+	+		$(PKG_BUILD_DIR)/mrvl/pcieusb8997_combo_v4.bin \
+	+		$(1)/lib/firmware/mrvl/
+	+endef
+	+$(eval $(call BuildPackage,mrvl-pcie-firmware))
+
+	+Package/mrvl-sd8xxx-firmware = $(call Package/firmware-default,Marvell SDIO8997 firmware)
+	+define Package/mrvl-sd8xxx-firmware/install
+	+	$(INSTALL_DIR) $(1)/lib/firmware/mrvl
+	+	$(INSTALL_DATA) \
+	+		$(PKG_BUILD_DIR)/mrvl/sdsd8997_combo_v4.bin \
+	+		$(1)/lib/firmware/mrvl/
+	+endef
+	+$(eval $(call BuildPackage,mrvl-sd8xxx-firmware))
+
+	+Package/mrvl-usb-firmware = $(call Package/firmware-default,Marvell SDIO8997 firmware)
+	+define Package/mrvl-usb-firmware/install
+	+	$(INSTALL_DIR) $(1)/lib/firmware/mrvl
+	+	$(INSTALL_DATA) \
+	+		$(PKG_BUILD_DIR)/mrvl/usbusb8997_combo_v4.bin \
+	+		$(1)/lib/firmware/mrvl/
+	+endef
+	+$(eval $(call BuildPackage,mrvl-usb-firmware))
+
+30. re-modify openwrt/package/kernel/mac80211/Makefile
+
+	for PCIE
+	+  DEPENDS+= @PCI_SUPPORT +kmod-mac80211 +@DRIVER_11N_SUPPORT +mrvl-pcie-firmware
+	for SDIO
+	+  DEPENDS+= +kmod-cfg80211 +kmod-lib80211 +kmod-mmc +@DRIVER_WEXT_SUPPORT +mrvl-sd8xxx-firmware
+	for USB
+	+  DEPENDS+= @USB_SUPPORT +kmod-cfg80211 +kmod-usb-core +kmod-lib80211 +@DRIVER_WEXT_SUPPORT +mrvl-usb-firmware
+31. Go to step 25.
+=======================================================================================================
+To support SDIO, USB and PCIE in one OpenWrt BSP
+if no need to support multi bus types in one BSP, ignore the following steps
+32. Re-modify Modify openwrt/package/kernel/mac80211/Makefile.
+	for PCIE
+        +       $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-pcie/mlan-pcie.ko \
+	for SDIO
+	+       $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-sd8xxx/mlan-sdio.ko \
+	for USB
+	+       $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-usb/mlan-usb.ko \
+33. Re-modify (mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/Makefile
+	for PCIE
+
+	-obj-m := mlan.o
+	-mlan-objs := $(MLANOBJS)
+	+obj-m := mlan-pcie.o
+	+mlan-pcie-objs := $(MLANOBJS)
+
+	for SDIO
+
+	-obj-m := mlan.o
+	-mlan-objs := $(MLANOBJS)
+	+obj-m := mlan-sdio.o
+	+mlan-sdio-objs := $(MLANOBJS)
+
+	for USB
+
+	-obj-m := mlan.o
+	-mlan-objs := $(MLANOBJS)
+	+obj-m := mlan-usb.o
+	+mlan-usb-objs := $(MLANOBJS)
+34. Go to step 25.
diff --git a/wlan_sd8897/README_RBC b/wlan_sd8897/README_RBC
new file mode 100644
index 0000000..c849287
--- /dev/null
+++ b/wlan_sd8897/README_RBC
@@ -0,0 +1,103 @@
+===============================================================================
+            U S E R  M A N U A L for Robust BT-WLAN coex (RBC)
+
+ Copyright (C) 2014-2018, Marvell International Ltd.
+ All Rights Reserved
+===============================================================================
+
+###############################
+
+# Abbreviations and acronyms
+
+###############################
+RBC - Robust BT-WLAN co-existence
+TMD - Time Distribute
+SMPS - Spatial Multiplexing Power Save
+
+###############################
+
+# Default RBC modes:
+
+###############################
+1.  For systems where BT and WLAN have seperate antennas, RBC mode is
+    automatically disabled in FW after detecting this from FEM cal data. e.g.
+    For 1x1 Dual-Ant system and 2x2 3-Ant system RBC mode is disabled.
+2.  For systems where BT and WLAN share an antenna, RBC mode is automatically
+    enabled in FW after detecting this from FEM cal data.
+    Default modes:
+    1x1 single antenna system: TMD RBC
+    2x2 2-antenna system: 1x1 SMPS RBC
+
+
+#################################
+
+# 2x2 2-antenna system RBC modes
+
+#################################
+There are three mutually exclusive RBC modes for a 2x2 system.
+The default RBC mode is 1x1 SMPS RBC. The required RBC mode must be
+configured before starting uAP or associating in-STA. The mode cannot be
+changed dynamically when any of these connections is active. The modes are
+described below:
+1.  1x1 SMPS RBC mode: WLAN switches to 1x1 FEM when BT is turned on. Also,
+    in-STA goes to SMPS mode w.r.t. ext-AP. When BT is turned off, WLAN
+    switches back to 2x2 FEM setting and in-STA moves out of SMPS.
+    uAP starts bss with only 1-stream even BT is off because it cannot
+    dynamically move between 1-stream and 2-stream rates like in-STA. To start
+    uAP with 2-stream, RBC mode has to be disabled.
+2.  1x2 SMPS RBC mode: Similar as 1x1 SMPS RBC mode. WLAN switches to 1x2 FEM
+    when BT is turned on. In this mode, it is expected that when BT is not
+    actively transmitting, WLAN can receive on both the antennas to enhance
+    the range. Note that 1-stream rates are used for receive and transmit.
+3.  2x2 TMD RBC mode: WLAN uses 2x2 antenna setting and timeshares the antenna
+    with BT.
+
+###############################
+
+# RBC mode select:
+
+###############################
+
+User can use robust_btc.conf and hostcmd to select different RBC mode:
+
+hostcmd mode_get
+hostcmd mode_timeshare
+hostcmd mode_spatial
+    This command is used to get/set Robust BT Coex.
+    mode_get:       get the current mode
+    mode_timeshare: set Robust BT Coex to timeshare mode  (default on 1x1 chips)
+    mode_spatial:   set Robust BT Coex to spatial mode    (only for, and default on 2x2 chips)
+
+    Usage:
+        mlanconfig mlanX hostcmd config/robust_btc.conf mode_get
+        mlanconfig mlanX hostcmd config/robust_btc.conf mode_timeshare
+        mlanconfig mlanX hostcmd config/robust_btc.conf mode_spatial
+
+hostcmd gpio_cfg
+    This command is used to enable/disable GPIO cfg.
+    gpio_cfg: enable/disable GPIO cfg for external bt request  (default is enable with High Polarity)
+
+    Usage:
+        mlanconfig mlanX hostcmd config/robust_btc.conf gpio_cfg
+
+hostcmd generictime
+hostcmd a2dptime
+hostcmd inquirytime
+hostcmd ap_generictime
+hostcmd ap_a2dptime
+hostcmd ap_inquirytime
+        This command is used to configure the time slice of COEX (only works in timeshare mode)
+        generictime:       configure the Bttime and Wlantime in Station Generic case
+        a2dptime:          configure the Bttime and Wlantime in Station A2DP case
+        inquirytime:       configure the Bttime and Wlantime in Station Inquiry case
+        ap_generictime:    configure the Bttime and Wlantime in Ap Generic case
+        ap_a2dptime:       configure the Bttime and Wlantime in Ap A2DP case
+        ap_inquirytime:    configure the Bttime and Wlantime in Ap Inquiry case
+
+    Usage:
+                mlanutl mlanX hostcmd config/robust_btc.conf generictime
+                mlanutl mlanX hostcmd config/robust_btc.conf a2dptime
+                mlanutl mlanX hostcmd config/robust_btc.conf inquirytim
+                mlanutl mlanX hostcmd config/robust_btc.conf ap_generictime
+                mlanutl mlanX hostcmd config/robust_btc.conf ap_a2dptime
+                mlanutl mlanX hostcmd config/robust_btc.conf ap_inquirytime
diff --git a/wlan_sd8897/README_UAP b/wlan_sd8897/README_UAP
new file mode 100644
index 0000000..b05fe8a
--- /dev/null
+++ b/wlan_sd8897/README_UAP
@@ -0,0 +1,2715 @@
+===============================================================================
+			U S E R  M A N U A L
+
+ Copyright (C) 2009-2018, Marvell International Ltd.
+ All Rights Reserved
+
+1) FOR DRIVER BUILD
+
+	Goto source code directory wlan_src.
+		make [clean] build
+	The driver binaries can be found in ../bin_xxxx directory.
+	The driver code supports Linux kernel up to 4.14.
+
+2) FOR DRIVER INSTALL
+
+	a) Copy sd8786_uapsta.bin | sd8787_uapsta.bin | ... to /lib/firmware/mrvl/ directory,
+	   create the directory if it doesn't exist.
+	b) Install uAP driver,
+	   There are drv_mode, max_sta_bss, max_uap_bss etc. module parameters.
+		The bit settings of drv_mode are,
+			Bit 0 :  STA
+			Bit 1 :  uAP
+			Bit 2 :  WIFIDIRECT
+		The default drv_mode is 7.
+			Bit 4 :  NAN
+
+		max_uap_bss: Maximum number of uAP BSS (default 1, max 2)
+		uap_name: Name of the uAP interface (default: "uap")
+	   For example, to install SD8787 driver,
+	   To load driver in uAP only mode,
+		insmod mlan.ko
+		insmod sd8787.ko drv_mode=2 [fw_name=mrvl/sd8787_uapsta.bin]
+	   To switch mode between STA only, uAP only and uAPSTA in run time,
+		echo drv_mode=1 > /proc/mwlan/config		// STA mode
+		echo drv_mode=2 > /proc/mwlan/config		// uAP mode
+		echo drv_mode=3 > /proc/mwlan/config		// uAPSTA mode
+	c) Uninstall uAP driver,
+		ifconfig uapX down
+		rmmod sd8xxx
+		rmmod mlan
+
+	To load driver with MFG firmware file, use mfg_mode=1 when insmod WLAN driver and
+	specify MFG firmware name if needed.
+
+	There are some other parameters for debugging purpose etc. Use modinfo to check details.
+	  drvdbg=<bit mask of driver debug message control>
+	  dev_cap_mask=<Bit mask of the device capability>
+	  mac_addr=xx:xx:xx:xx:xx:xx <override the MAC address (in hex)>
+	  auto_ds=0|1|2 <use MLAN default | enable auto deepsleep | disable auto deepsleep>
+	  ps_mode=0|1|2 <use MLAN default | enable IEEE PS mode | disable IEEE PS mode>
+	  max_tx_buf=2048|4096|8192 <maximum AMSDU Tx buffer size>
+	  pm_keep_power=1|0 <PM keep power in suspend (default) | PM no power in suspend>
+	  shutdown_hs=1|0 <Enable HS when shutdown | No HS when shutdown (default)>
+	  cfg_11d=0|1|2 <use MLAN default | enable 11d | disable 11d>
+	  dts_enable=0|1 <Disable DTS | Enable DTS (default)>
+	  hw_test=0|1 <Disable hardware test (default) | Enable hardware test>
+	  fw_serial=0|1 <support parallel download FW | support serial download FW (default)>
+	  req_fw_nowait=0|1 <use request_firmware API (default) | use request_firmware_nowait API>
+	  init_cfg=<init config (MAC addresses, registers etc.) file name>
+		e.g. copy init_cfg.conf to firmware directory, init_cfg=mrvl/init_cfg.conf
+	  cal_data_cfg=<CAL data config file name>
+		e.g. copy cal_data.conf to firmware directory, cal_data_cfg=mrvl/cal_data.conf
+	  txpwrlimit_cfg=<Tx power limit config file name>
+		e.g. copy txpwrlimit_cfg_set.conf to firmware directory, txpwrlimit_cfg=mrvl/txpwrlimit_cfg_set.conf
+	  init_hostcmd_cfg=<init hostcmd config file name>
+		e.g. copy init_hostcmd_cfg.conf to firmware directory, init_hostcmd_cfg=mrvl/init_hostcmd_cfg.conf
+	  cfg80211_wext=<bit mask of CFG80211 and WEXT control>
+		Bit 0: STA WEXT
+		Bit 1: uAP WEXT
+		Bit 2: STA CFG80211
+		Bit 3: uAP CFG80211
+	  wq_sched_prio: Priority for work queue
+	  wq_sched_policy: Scheduling policy for work queue
+		(0: SCHED_NORMAL, 1: SCHED_FIFO, 2: SCHED_RR, 3: SCHED_BATCH, 5: SCHED_IDLE)
+		Please note that, both wq_sched_prio and wq_sched_policy should be provided
+		as module parameters. If wq_sched_policy is (0, 3 or 5), then wq_sched_prio
+		must be 0. wq_sched_prio should be 1 to 99 otherwise.
+	  rx_work=0|1|2 <default | Enable rx_work_queue | Disable rx_work_queue>
+	  low_power_mode_enable=0|1 <disable low power mode (default)| enable low power mode>
+	  When low power mode is enabled, the output power will be clipped at ~+10dBm and the
+	  expected PA current is expected to be in the 80-90 mA range for b/g/n modes
+
+	Note: On some platforms (e.g. PXA910/920) double quotation marks ("") need to used
+	for module parameters.
+		insmod sd8xxx.ko "<para1> <para2> ..."
+
+3) FOR DRIVER PROC & DEBUG
+	The following info are provided in /proc/mwlan/uapX/info.
+
+	driver_name = "uap"
+	driver_version = <driver version>
+	InterfaceName= "uapX"
+	State= "Disconnected" | "Connected"
+	MACAddress= <6-byte adapter MAC address>
+	MCCount= <multicast address count>
+	num_tx_bytes = <number of bytes sent to device>
+	num_rx_bytes = <number of bytes received from device and sent to kernel>
+	num_tx_pkts = <number of packets sent to device>
+	num_rx_pkts = <number of packets received from device and sent to kernel>
+	num_tx_pkts_dropped = <number of tx packets dropped by driver>
+	num_rx_pkts_dropped = <number of rx packets dropped by driver>
+	num_tx_pkts_err = <number of tx packets failed to send to device>
+	num_rx_pkts_err = <number of rx packets failed to receive from device>
+	num_tx_timeout =  <number of tx timeout>
+	carrier "on" | "off"
+	tx queue "stopped" | "started"
+
+	The following debug info are provided in /proc/mwlan/uapX/debug.
+
+	drvdbg = <bit masks of driver debug message control>
+		bit 0:  MMSG  		PRINTM(MMSG,...)
+		bit 1:  MFATAL		PRINTM(MFATAL,...)
+		bit 2:  MERROR		PRINTM(MERROR,...)
+		bit 3:  MDATA 		PRINTM(MDATA,...)
+		bit 4:  MCMND 		PRINTM(MCMND,...)
+		bit 5:  MEVENT		PRINTM(MEVENT,...)
+		bit 6:  MINTR 		PRINTM(MINTR,...)
+		bit 7:  MIOCTL 		PRINTM(MIOCTL,...)
+		...
+		bit 16: MDAT_D		PRINTM(MDAT_D,...), DBG_HEXDUMP(MDAT_D,...)
+		bit 17: MCMD_D		PRINTM(MCMD_D,...), DBG_HEXDUMP(MCMD_D,...)
+		bit 18: MEVT_D		PRINTM(MEVT_D,...), DBG_HEXDUMP(MEVT_D,...)
+		bit 19: MFW_D		PRINTM(MFW_D,...),  DBG_HEXDUMP(MFW_D,...)
+		bit 20: MIF_D		PRINTM(MIF_D,...),  DBG_HEXDUMP(MIF_D,...)
+		...
+		bit 28: MENTRY		PRINTM(MENTRY,...), ENTER(), LEAVE()
+		bit 29: MWARN 		PRINTM(MWARN,...)
+		bit 30: MINFO 		PRINTM(MINFO,...)
+	wmm_ac_vo = <number of packets sent to device from WMM AcVo queue>
+	wmm_ac_vi = <number of packets sent to device from WMM AcVi queue>
+	wmm_ac_be = <number of packets sent to device from WMM AcBE queue>
+	wmm_ac_bk = <number of packets sent to device from WMM AcBK queue>
+	max_tx_buf_size = <maximum Tx buffer size>
+	tx_buf_size = <current Tx buffer size>
+	curr_tx_buf_size = <current Tx buffer size in FW>
+	ps_mode = <0/1, CAM mode/PS mode>
+	ps_state = <0/1/2/3, awake state/pre-sleep state/sleep-confirm state/sleep state>
+	wakeup_dev_req = <0/1, wakeup device not required/required>
+	wakeup_tries = <wakeup device count, cleared when device awake>
+	hs_configured = <0/1, host sleep not configured/configured>
+	hs_activated = <0/1, extended host sleep not activated/activated>
+	tx_pkts_queued = <number of Tx packets queued>
+	num_bridge_pkts = <number of bridged packets>
+	num_drop_pkts = <number of dropped packets>
+	num_tx_timeout = <number of Tx timeout>
+	num_cmd_timeout = <number of timeout commands>
+	timeout_cmd_id = <command id of the last timeout command>
+	timeout_cmd_act = <command action of the last timeout command>
+	last_cmd_id = <command id of the last several commands sent to device>
+	last_cmd_act = <command action of the last several commands sent to device>
+	last_cmd_index = <0 based last command index>
+	last_cmd_resp_id = <command id of the last several command responses received from device>
+	last_cmd_resp_index = <0 based last command response index>
+	last_event = <event id of the last several events received from device>
+	last_event_index = <0 based last event index>
+	num_cmd_h2c_fail = <number of commands failed to send to device>
+	num_cmd_sleep_cfm_fail = <number of sleep confirm failed to send to device>
+	num_tx_h2c_fail = <number of data packets failed to send to device>
+	num_cmdevt_c2h_fail = <number of commands/events failed to receive from device>
+	num_rx_c2h_fail = <number of data packets failed to receive from device>
+	num_int_read_fail = <number of interrupt read failures>
+	last_int_status = <last interrupt status>
+	cmd_sent = <0/1, send command resources available/sending command to device>
+	data_sent = <0/1, send data resources available/sending data to device>
+	mp_rd_bitmap = <SDIO multi-port read bitmap>
+	curr_rd_port = <SDIO multi-port current read port>
+	mp_wr_bitmap = <SDIO multi-port write bitmap>
+	curr_wr_port = <SDIO multi-port current write port>
+	cmd_resp_received = <0/1, no cmd response to process/response received and yet to process>
+	event_received = <0/1, no event to process/event received and yet to process>
+	ioctl_pending = <number of ioctl pending>
+	tx_pending = <number of Tx packet pending>
+	rx_pending = <number of Rx packet pending>
+	lock_count = <number of lock used>
+	malloc_count = <number of malloc done>
+	mbufalloc_count = <number of mlan_buffer allocated>
+	main_state = <current state of the main process>
+	sdiocmd53w = <SDIO Cmd53 write status>
+	sdiocmd53r = <SDIO Cmd52 read status>
+	hs_skip_count = <number of skipped suspends>
+	hs_force_count = <number of forced suspends>
+
+	Example:
+		echo "drvdbg=0x7" > /proc/mwlan/uapX/debug	#enable MMSG,MFATAL,MERROR messages
+
+	Use dmesg or cat /var/log/debug to check driver debug messages.
+
+	To log driver debug messages to file,
+	a) Edit /etc/rsyslog.conf, add one line "*.debug		/var/log/debug"
+	b) touch /var/log/debug (if the file doesn't exist)
+	c) service rsyslog restart
+
+4) SOFT_RESET command
+   This command is used to perform a "soft reset" on the module.
+   The FW code will disable hardware and jump to boot code.
+   Host software will then need to re-download firmware if required.
+
+   Usage:
+	echo "soft_reset=1" > /proc/mwlan/config
+
+===============================================================================
+
+		U S E R  M A N U A L  F O R  UAPUTL
+
+NAME
+uaputl.exe [options] <command> [command parameters]]
+
+Options:
+	--help 	Display help
+	-v    	Display version
+	-i <interface>
+	-d <debug_level=0|1|2>
+
+Example:
+	./uaputl.exe --help
+		"display help for uaputl"
+
+	./uaputl.exe sys_config --help
+		"display help for sys_config command"
+
+This tool can be used to set/get uAP's settings. To change AP settings, you might
+need to issue "bss_stop" command to stop AP before making change and issue "bss_start"
+command to restart the AP after making change.
+
+------------------
+Supported Commands
+------------------
+version
+debug_level
+sys_config [CONFIG_FILE_NAME]
+bss_config [CONFIG_FILE_NAME]
+sys_info
+sys_reset
+bss_start
+bss_stop
+sta_list
+sta_deauth <STA_MAC_ADDRESS>
+sta_deauth_ext <STA_MAC_ADDRESS> <REASON_CODE>
+radioctrl [0|1]
+txratecfg [l] [m] [n]
+antcfg [m] [n]
+pscfg [MODE] [CTRL INACTTO MIN_SLEEP MAX_SLEEP MIN_AWAKE MAX_AWAKE]
+mic_err <STA_MAC_ADDRESS>
+key_material <MAC_ADDRESS> <KEY> [KEY_ID]
+sys_cfg_custom_ie [INDEX] [MASK] [IEBuffer]
+coex_config [CONFIG_FILE_NAME]
+hscfg [condition [[GPIO# [gap]]]]
+hssetpara condition [[GPIO# [gap]]]
+sys_cfg_wmm  [qosinfo=<qosinfo>]
+             [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP]
+             [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP]
+             [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP]
+             [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP]
+sys_cfg_ap_wmm [0]
+               [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP]
+               [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP]
+               [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP]
+               [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP]
+sys_cfg_11n [ENABLE] [HTCAP] [AMPDU] [TXBFCAP] [HT_MCS_MAP]
+addbapara [timeout txwinsize rxwinsize txamsdu rxamsdu]
+aggrpriotbl <m0> <n0> <m1> <n1> ... <m7> <n7>
+addbareject <m0> <m1> ... <m7>
+httxbfcfg <ACTION> [ACT_DATA]
+httxcfg [<m>] [<n>]
+htstreamcfg [n]
+deepsleep [MODE] [IDLE_TIME]
+sdcmd52rw <FN no.> <address> [data]
+hostcmd <txpwrlimit_cfg.conf> txpwrlimit_cfg_get
+hostcmd <txpwrlimit_cfg.conf> txpwrlimit_2g_cfg_set
+hostcmd <txpwrlimit_cfg.conf> txpwrlimit_5g_cfg_set
+tx_data_pause [ENABLE][TX_BUF_CNT]
+vhtcfg <j> <k> [l] [m] [n] [o]
+dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>]
+cscount [<channel_switch_count>]
+mgmtframectrl [MASK]
+
+-------------------------------------------------------------------
+The following commands can be issued individually for debug purpose
+-------------------------------------------------------------------
+sys_cfg_ap_mac_address [AP_MAC_ADDRESS]
+sys_cfg_ssid [SSID]
+sys_cfg_beacon_period [BEACON_PERIOD]
+sys_cfg_dtim_period [DTIM_PERIOD]
+sys_cfg_channel [CHANNEL] [MODE]
+sys_cfg_channel_ext [CHANNEL] [BAND] [MODE]
+sys_cfg_scan_channels [CHANNEL[.BAND]]
+sys_cfg_rates [RATES]
+sys_cfg_rates_ext [rates RATES] [mbrate RATE]
+sys_cfg_tx_power [TX_POWER]
+sys_cfg_bcast_ssid_ctl [0|1|2]
+sys_cfg_preamble_ctl
+sys_cfg_bss_status
+sys_cfg_rts_threshold [RTS_THRESHOLD]
+sys_cfg_frag_threshold [FRAG_THRESHOLD]
+sys_cfg_rsn_replay_prot [1|0]
+sys_cfg_tx_beacon_rate [TX_BEACON_RATE]
+sys_cfg_mcbc_data_rate [MCBC_DATA_RATE]
+sys_cfg_pkt_fwd_ctl [PKT_FWD_CTRL]
+sys_cfg_sta_ageout_timer [STA_AGEOUT_TIMER]
+sys_cfg_ps_sta_ageout_timer [PS_STA_AGEOUT_TIMER]
+sys_cfg_auth [AUTH_MODE]
+sys_cfg_protocol [PROTOCOL] [AKM_SUITE]
+sys_cfg_pmf [MFPC] [MFPR]
+sys_cfg_wep_key [INDEX ISDEFAULT KEY]
+sys_cfg_cipher [PAIRWISE_CIPHER GROUP_CIPHER]
+sys_cfg_pwk_cipher [<PROTOCOL>] [PAIRWISE_CIPHER]
+sys_cfg_gwk_cipher [GROUP_CIPHER]
+sys_cfg_group_rekey_timer [GROUP_REKEY_TIMER]
+sys_cfg_wpa_passphrase [PASSPHRASE]
+sys_cfg_max_sta_num [STA_NUM]
+sys_cfg_retry_limit [RETRY_LIMIT]
+sys_cfg_sticky_tim_config [ENABLE] [<DURATION> <STICKY_BIT_MASK>]
+sys_cfg_sticky_tim_sta_mac_addr [CONTROL] [STA_MAC_ADDRESS]
+sys_cfg_2040_coex [ENABLE]
+sys_cfg_eapol_pwk_hsk [<TIMEOUT> <RETRIES>]
+sys_cfg_eapol_gwk_hsk [<TIMEOUT> <RETRIES>]
+sta_filter_table <FILTERMODE> <MACADDRESS_LIST>
+regrdwr <TYPE> <OFFSET> [value]
+memaccess <ADDR> [value]
+rdeeprom <offset> <byteCount>
+cfg_data <type> [*.conf]
+sys_cfg_80211d [state STATE] [country COUNTRY]
+uap_stats
+sys_cfg_tdls_ext_cap [CONFIG_FILE]
+sys_cfg_restrict_client_mode [<ENABLE> [MODE_CONFIG]]
+
+-------------------
+Details of Commands
+-------------------
+
+version
+-------
+    "./uaputl.exe -v"
+
+    This command prints the uAP utility version information.
+
+debug_level
+-----------
+    "./uaputl.exe -d <debug_level>"
+
+    The supported debug_level are:
+        0     - no debug
+        1     - enable MSG_DEBUG
+        2     - enable all the debug
+    This command use to control the debug level of uaputl.exe.
+
+    Example:
+	./uaputl.exe -d 2 sys_config
+		Enable all the debug in uaputl.exe
+
+sys_config
+----------
+    "./uaputl.exe sys_config [CONFIG_FILE]"
+    This command is used to set or get the current settings of the Micro AP.
+
+    The supported options are:
+        CONFIG_FILE is file contain all the Micro AP settings.
+        empty - Get current Micro AP settings
+
+    Example:
+	./uaputl.exe sys_config
+		Get current settings of the Micro AP.
+
+	./uaputl.exe sys_config config/uaputl.conf
+		Load Micro AP's settings from uaputl.conf file and set.
+
+bss_config
+----------
+    "./uaputl.exe bss_config [CONFIG_FILE]"
+    This command is used to set or get the current settings of the BSS.
+
+    The supported options are:
+        CONFIG_FILE is file contain all the BSS settings.
+        empty - Get current BSS settings
+
+    Example:
+	./uaputl.exe bss_config
+		Get current settings of the BSS.
+
+	./uaputl.exe bss_config config/uaputl.conf
+		Load BSS settings from uaputl.conf file and set.
+
+sys_info
+--------
+    "./uaputl.exe sys_info"
+
+    This command returns system information such as firmware version number
+    and HW information.
+
+sys_reset
+---------
+    "./uaputl.exe sys_reset"
+
+    This command is used to reset the Micro AP back to its initial state.
+    For example, this can be used to recover from a serious error, or before
+    creating a new BSS.
+
+    This command has the following effects:
+        1. The WLAN hardware MAC is reset.
+        2. All MIB variables are initialized to their respective default
+           values.
+        3. The firmware internal variables are reset to their respective
+           default values.
+        4. The firmware state machines are reset to their respective initial
+           states.
+
+bss_start
+---------
+    "./uaputl.exe bss_start"
+
+    This command starts the BSS.
+    There is no error for redundant bss_start command.
+
+bss_stop
+--------
+    "./uaputl.exe bss_stop"
+
+    This command stops the BSS. The command causes the firmware to:
+        1. Deauthenticate all associated client stations.
+        2. Turn off the radio (hence stopping beaconing).
+    There is no error for redundant bss_stop command.
+
+sta_list
+--------
+    "./uaputl.exe sta_list"
+
+    This command returns the list of client stations that are currently
+    associated with the AP.
+
+    The output is formatted as shown below, for each STA:
+    "STA <STA_NUM> information:
+     ==========================
+     MAC Address: <STA MAC address>
+     Power mfg status: active|power save
+     Rssi:  <RSSI_VALUE>"
+
+sta_deauth
+----------
+    "./uaputl.exe sta_deauth <STA_MAC_ADDRESS>"
+
+    This command is used to de-authentciate a client station for any reason.
+
+radioctrl
+----------
+    "./uaputl.exe radioctrl [0|1]"
+
+    This command is used to set or get the radio settings.
+    The supported options are:
+        1     - Turn radio on
+        0     - Turn radio off
+        empty - Get current radio setting
+
+txratecfg
+----------
+    "./uaputl.exe txratecfg [l] [m] [n]"
+
+    This command is used to set/get the transmit data rate.
+
+    Where
+        [l] is <format>
+        <format> - This parameter specifies the data rate format used in this command
+            0:    LG
+            1:    HT
+            2:    VHT
+            0xff: Auto
+
+        [m] is <index>
+        <index> - This parameter specifies the rate or MCS index
+        If <format> is 0 (LG),
+            0    1 Mbps
+            1    2 Mbps
+            2    5.5 Mbps
+            3    11 Mbps
+            4    6 Mbps
+            5    9 Mbps
+            6    12 Mbps
+            7    18 Mbps
+            8    24 Mbps
+            9    36 Mbps
+            10   48 Mbps
+            11   54 Mbps
+        If <format> is 1 (HT),
+            0    MCS0
+            1    MCS1
+            2    MCS2
+            3    MCS3
+            4    MCS4
+            5    MCS5
+            6    MCS6
+            7    MCS7
+            8    MCS8
+            9    MCS9
+            10   MCS10
+            11   MCS11
+            12   MCS12
+            13   MCS13
+            14   MCS14
+            15   MCS15
+            32   MCS32
+        If <format> is 2 (VHT),
+            0    MCS0
+            1    MCS1
+            2    MCS2
+            3    MCS3
+            4    MCS4
+            5    MCS5
+            6    MCS6
+            7    MCS7
+            8    MCS8
+            9    MCS9
+        [n] is <nss>
+        <nss> - This parameter specifies the NSS. It is valid only for VHT
+        If <format> is 2 (VHT),
+            1    NSS1
+            2    NSS2
+
+    Examples:
+        ./uaputl.exe txratecfg 0 3        : Set fixed Tx rate to 11 Mbps
+        ./uaputl.exe txratecfg 0 11       : Set fixed Tx rate to 54 Mbps
+        ./uaputl.exe txratecfg 1 3        : Set fixed Tx rate to MCS3
+        ./uaputl.exe txratecfg 2 3 2    : Set fixed Tx rate to MCS3 for NSS2
+        ./uaputl.exe txratecfg 0xff     : Disable fixed rate and uses auto rate
+        ./uaputl.exe txratecfg          : Read the current data rate setting
+
+antcfg
+----------
+    "./uaputl.exe antcfg [m] [n]"
+
+    This command is used to set/get the transmit and receive antenna.
+    where value of m is:
+        Bit 0   -- Tx Path A
+        Bit 1   -- Tx Path B
+        Bit 0-1 -- Tx Path A+B
+
+    where value of n is:
+        Bit 0   -- Rx Path A
+        Bit 1   -- Rx Path B
+        Bit 0-1 -- Rx Path A+B
+    The Tx path setting (m) is used if Rx path (n) is not provided.
+
+    Examples:
+        ./uaputl.exe antcfg             : Get Tx and Rx path
+        ./uaputl.exe antcfg 3           : Set Tx and Rx path to A+B
+        ./uaputl.exe antcfg 2 3         : Set Tx path to B and Rx path to A+B
+
+sys_cfg_ap_mac_address
+----------------------
+    "./uaputl.exe sys_cfg_ap_mac_address [AP_MAC_ADDRESS]"
+
+    This command is used to set or get the AP MAC address.
+
+    If no arguments are given, this command returns the current AP MAC
+    address.
+    Otherwise, this MAC address becomes the BSSID of the infrastructure
+    network created by the AP.
+
+    Example:
+	./uaputl.exe sys_cfg_ap_mac_address 00:50:43:20:aa:bb
+		Set AP MAC address to 00:50:43:20:aa:bb
+
+	./uaputl.exe sys_cfg_ap_mac_address
+		Get AP MAC address"
+
+sys_cfg_ssid
+------------
+    "./uaputl.exe sys_cfg_ssid [SSID]"
+
+    This command is used to set or get the AP SSID.
+
+    If no arguments are given, this command returns the current AP SSID.
+    While setting, the maximum length of the SSID can be 32 characters.
+
+    Example:
+	./uaputl.exe sys_cfg_ssid microap
+		Set AP ssid to "microap"
+
+	./uaputl.exe sys_cfg_ssid
+		Get AP ssid
+
+sys_cfg_beacon_period
+---------------------
+    "./uaputl.exe sys_cfg_beacon_period [BEACON_PERIOD]"
+
+    This command is used to set or get the AP beacon period.
+
+    If no arguments are given, this command returns the current AP beacon
+    period.
+
+    Beacon period is represented in milliseconds.
+
+    Example:
+	./uaputl.exe sys_cfg_beacon_period 100
+		Set AP beacon period to 100 TU
+
+	./uaputl.exe sys_cfg_beacon_period
+		Get AP beacon period
+
+sys_cfg_dtim_period
+-------------------
+    "./uaputl.exe sys_cfg_dtim_period [DTIM_PERIOD]
+
+    This command is used to set or get the AP DTIM period.
+
+    If no arguments are given, this command returns the current AP DTIM
+    period.
+
+    Example:
+	./uaputl.exe sys_cfg_dtim_period 3
+		Set AP DTIM period to 3
+
+	./uaputl.exe sys_cfg_dtim_period
+		Get AP DTIM period
+
+sys_cfg_scan_channels
+---------------------
+    "./uaputl.exe sys_cfg_scan_channels [CHANNEL[.BAND]]"
+
+    This command is used to set or get the AP's scan channel list.
+
+    If no arguments are given, this command returns the scan channel list.
+    If BAND is 0, channel is set in 2.4 GHz band and if BAND is 1, channel is set to 5GHz.
+    Channels from only one of the bands should be specified.
+    Each CHANNEL.BAND pair must be separated by a space. BAND parameter is optional.
+
+    Example:
+	./uaputl.exe sys_cfg_scan_channels 1 11 6
+		Set AP scan channel list to 1 11 6
+
+	./uaputl.exe sys_cfg_scan_channels 11.0 6.0
+		Set AP scan channel list to 11 6
+
+	./uaputl.exe sys_cfg_scan_channels
+		Get AP scan channel list
+
+        ./uaputl.exe sys_cfg_scan_channels 8.1 16.1 34
+                Set AP scan channel list to 8 16 and 34 in 5GHz band.
+
+sys_cfg_channel
+---------------
+    "./uaputl.exe sys_cfg_channel [CHANNEL] [MODE]"
+
+    This command is used to set or get the AP radio channel.
+
+    If no arguments are given, this command returns the current AP radio
+    channel.
+
+    MODE: band config mode.
+          Bit 0:  automatic channel selection (ACS) enable/disable
+          Bit 1:  secondary channel is above primary channel enable/disable(only allow for channel 1-7)
+          Bit 2:  secondary channel is below primary channel enable/disable(only allow for channel 5-11)
+          For 'a' band channel:
+          Bit 1:  secondary channel is above primary channel enable/disable
+          Bit 2:  secondary channel is below primary channel enable/disable
+          Only following pairs of channels are valid for secondary channel setting in 5GHz band.
+          36, 40
+          44, 48
+          52, 56
+          60, 64
+          100, 104
+          108, 112
+          116, 120
+          124, 128
+          132, 136
+          149, 153
+          157, 161
+
+    Example:
+        ./uaputl.exe sys_cfg_channel 6
+                Set AP radio channel to 6, and no secondary channel.
+
+        ./uaputl.exe sys_cfg_channel 11 0
+                Set AP radio channel to 11 with Manual Channel Select.
+
+        ./uaputl.exe sys_cfg_channel 0 1
+                Set AP to ACS.
+
+        ./uaputl.exe sys_cfg_channel
+                Get AP radio channel
+
+        ./uaputl.exe sys_cfg_channel 6 2
+                Set AP primary radio channel to 6, and secondary channel is above.
+        ./uaputl.exe sys_cfg_channel 6 4
+                Set AP primary radio channel to 6, and secondary channel is below
+        ./uaputl.exe sys_cfg_channel 0 3
+                Set AP to ACS mode, and secondary channel is above.
+        ./uaputl.exe sys_cfg_channel 0 5
+                Set AP to ACS mode, and secondary channel is below.
+        ./uaputl.exe sys_cfg_channel 36 2
+                Set AP primary radio channel to 36, and secondary channel is above.
+        ./uaputl.exe sys_cfg_channel 40 4
+                Set AP primary radio channel to 40, and secondary channel is below.
+
+sys_cfg_channel_ext
+---------------
+    "./uaputl.exe sys_cfg_channel_ext [CHANNEL] [BAND] [MODE]"
+
+    This command is used to set or get the AP radio channel.
+
+    If no arguments are given, this command returns the current AP radio
+    channel.
+
+    BAND: 0 : 2.4GHz operation
+          1 : 5GHz operation
+    MODE: band config mode.
+          Bit 0:  automatic channel selection (ACS) enable/disable
+          Bit 1:  secondary channel is above primary channel enable/disable(only allow for channel 1-7)
+          Bit 2:  secondary channel is below primary channel enable/disable(only allow for channel 5-11)
+          For 'a' band channel:
+          Bit 1:  secondary channel is above primary channel enable/disable
+          Bit 2:  secondary channel is below primary channel enable/disable
+          Only following pairs of channels are valid for secondary channel setting in 5GHz band.
+          36, 40
+          44, 48
+          52, 56
+          60, 64
+          100, 104
+          108, 112
+          116, 120
+          124, 128
+          132, 136
+          149, 153
+          157, 161
+
+    Example:
+        ./uaputl.exe sys_cfg_channel_ext 6
+Set AP radio channel to 6, and no secondary channel.
+
+        ./uaputl.exe sys_cfg_channel_ext 11 0 0
+                Set AP radio channel to 11 in 2.4GHz band with Manual Channel Select.
+
+        ./uaputl.exe sys_cfg_channel_ext 0 0 1
+                Set AP to ACS mode and 2.4GHz band.
+
+        ./uaputl.exe sys_cfg_channel_ext 8 0
+                Set AP to channel 8 and 2.4GHz band.
+
+        ./uaputl.exe sys_cfg_channel_ext 8 1
+                Set AP to channel 8 and 5GHz band.
+
+        ./uaputl.exe sys_cfg_channel_ext 36 1
+                Set AP to channel 36 and 5GHZ band.
+
+        ./uaputl.exe sys_cfg_channel_ext
+                Get AP radio channel, band and mode.
+
+        ./uaputl.exe sys_cfg_channel_ext 6 0 2
+                Set AP primary radio channel to 6, band to 2.4GHz and secondary channel is above.
+        ./uaputl.exe sys_cfg_channel_ext 6 0 4
+                Set AP primary radio channel to 6, band to 2.4GHz and secondary channel is below
+        ./uaputl.exe sys_cfg_channel_ext 0 0 3
+                Set AP to ACS mode, band to 2.4GHz and secondary channel is above.
+        ./uaputl.exe sys_cfg_channel_ext 0 0 5
+                Set AP to ACS mode, band to 2.4GHz and secondary channel is below.
+        ./uaputl.exe sys_cfg_channel_ext 36 1 2
+                Set AP primary radio channel to 36, band to 5GHz and secondary channel is above.
+        ./uaputl.exe sys_cfg_channel_ext 40 1 4
+                Set AP primary radio channel to 40, band to 5GHz and secondary channel is below.
+
+sys_cfg_rates
+-------------
+    "./uaputl.exe sys_cfg_rates [RATES]"
+
+    If 'Rate' provided, a 'set' is performed else a 'get' is performed
+    RATES is provided as a set of data rates, in unit of 500 kilobits
+    A rate with MSB bit is basic rate, i.e 0x82 is basic rate.
+
+    'set' will not allowed after bss start.
+
+    Valid rates: 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108
+    Non-Basic rates: 0x02, 0x04, 0x0b, 0x16, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c
+    Basic rates: 0x82, 0x84, 0x8b, 0x96, 0x8C, 0x92, 0x98, 0xA4, 0xB0, 0xC8, 0xE0, 0xEc
+
+    Each rate must be separated by a space.
+
+    Example:
+    ./uaputl.exe sys_cfg_rates 0x82 0x84 0x96 0x0c 0x12 0x18
+    ./uaputl.exe sys_cfg_rates
+
+sys_cfg_rates_ext
+-----------------
+    "./uaputl.exe sys_cfg_rates_ext [rates RATES] [mbrate RATE]"
+
+    If 'Rate' provided, a 'set' is performed else a 'get' is performed.
+    RATES is provided as a set of data rates, in unit of 500 kilobits
+    A rate with MSB bit is basic rate, i.e 0x82 is basic rate.
+    If only operational rates is provided, MCBC rate and unicast rate will be set to auto.
+
+    Valid rates: 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108
+    Non-Basic rates: 0x02, 0x04, 0x0b, 0x16, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c
+    Basic rates: 0x82, 0x84, 0x8b, 0x96, 0x8C, 0x92, 0x98, 0xA4, 0xB0, 0xC8, 0xE0, 0xEc
+    Rates 2, 4, 11 and 22 (in units of 500 Kbps) must be present in either of
+    basic or non-basic rates. If OFDM rates are enabled then 12, 24 and 48 (in
+    units of 500 Kbps) must be present in either basic or non-basic rates.
+
+    Each rate must be separated by a space.
+
+    rates followed by RATES for setting operational rates.
+    mbrate followed by RATE for setting multicast and broadcast rate.
+
+    operational rates only allow to set before bss start.
+
+    Example:
+    ./uaputl.exe sys_cfg_rates_ext rates 0x82 0x04 11 0x96 12 24 48 mbrate 0x16
+        Set AP operation rates to 0x82,0x04,11,0x96,12,24,48, multicast rate to 0x16
+    ./uaputl.exe sys_cfg_rates_ext rates 0x82 0x04 11 0x96 12 24 48
+		Set AP operation rates to 0x82,0x04,11,0x96,12,24,48.
+
+sys_cfg_tx_power
+----------------
+    "./uaputl.exe sys_cfg_tx_power [TX_POWER]"
+
+    This command is used to set or get the AP Tx power.
+
+    If no arguments are given, this command returns the current AP Tx power.
+
+    Tx power level is represented in dBm.
+
+    Example:
+	./uaputl.exe sys_cfg_tx_power 13
+		Set AP Tx power to 13 dBm
+
+	./uaputl.exe sys_cfg_tx_power
+		Get AP Tx power
+
+sys_cfg_bcast_ssid_ctl
+----------------------
+    "./uaputl.exe sys_cfg_bcast_ssid_ctl [0|1|2]"
+
+    This command is used to set or get the SSID broadcast feature setting.
+
+    The supported options are:
+        0     - Disable SSID broadcast, send empty SSID (length=0) in beacon.
+        1     - Enable SSID broadcast
+        2     - Disable SSID broadcast, clear SSID (ACSII 0) in beacon, but keep the original length
+        empty - Get current SSID broadcast setting
+
+    When broadcast SSID is enabled, the AP responds to probe requests from
+    client stations that contain null SSID.
+
+    When broadcast SSID is disabled (sys_cfg_bcast_ssid_ctl = 0/2), the AP:
+        1. Does not respond to probe requests that contain null SSID.
+        2. when sys_cfg_bcast_ssid_ctl = 0, generates beacons that contain null SSID (length=0).
+        3. when sys_cfg_bcast_ssid_ctl = 2, clear SSID (ACSII 0) in beacon and keep the original length
+
+   Example:
+        ./uaputl.exe sys_cfg_bcast_ssid_ctl 0
+                Disable SSID broadcast, send empty SSID (length=0) in beacon.
+
+	./uaputl.exe sys_cfg_bcast_ssid_ctl 1
+		Enable SSID broadcast
+
+        ./uaputl.exe sys_cfg_bcast_ssid_ctl 2
+                Disable SSID broadcast, clear SSID (ACSII 0) in beacon, but keep the original length
+
+	./uaputl.exe sys_cfg_bcast_ssid_ctl
+		Get SSID broadcast setting
+
+sys_cfg_preamble_ctl
+--------------------
+    "./uaputl.exe sys_cfg_preamble_ctl"
+
+    This command is used to get type of preamble.
+
+    Example:
+	./uaputl.exe sys_cfg_preamble_ctl
+		Get AP preamble setting
+
+sys_cfg_bss_status
+--------------------
+    "./uaputl.exe sys_cfg_bss_status"
+
+    This command is used to get current BSS status.
+
+    Example:
+	./uaputl.exe sys_cfg_bss_status
+		Get current BSS status
+
+sys_cfg_rts_threshold
+---------------------
+    "./uaputl.exe sys_cfg_rts_threshold [RTS_THRESHOLD]"
+
+    This command is used to set or get the RTS threshold value.
+
+    If no arguments are given, this command returns the current RTS threshold
+    value.
+
+    Example:
+	./uaputl.exe sys_cfg_rts_threshold 2347
+		Set AP RTS threshold to 2347
+
+	./uaputl.exe sys_cfg_rts_threshold
+		Get AP RTS threshold
+
+sys_cfg_frag_threshold
+----------------------
+    "./uaputl.exe sys_cfg_frag_threshold [FRAG_THRESHOLD]"
+
+    This command is used to set or get the Fragmentation threshold value.
+
+    If no arguments are given, this command returns the current Fragmentation threshold
+    value.
+
+    Example:
+	./uaputl.exe sys_cfg_frag_threshold 2346
+		Set AP Fragmentation threshold to 2346
+
+	./uaputl.exe sys_cfg_frag_threshold
+		Get AP Fragmentation threshold
+
+    Note: Please use aggrpriotbl command to disable the AMPDU/AMSDU aggregation when frag_threshold is set.
+
+sys_cfg_rsn_replay_prot
+-----------------------
+    "./uaputl.exe sys_cfg_rsn_replay_prot [1|0]"
+
+    This command is used to enable or disable RSN replay protection.
+
+    The supported options are:
+        0     - Disable RSN replay protection
+        1     - Enable RSN replay protection
+        empty - Get current RSN replay protection setting
+
+   Example:
+	./uaputl.exe sys_cfg_rsn_replay_prot 1
+		Enable RSN replay protection
+
+	./uaputl.exe sys_cfg_rsn_replay_prot
+		Get RSN replay protection setting
+
+sys_cfg_tx_beacon_rate
+--------------------
+    "./uaputl.exe sys_cfg_tx_beacon_rate [TX_BEACON_RATE]"
+
+    This command is used to set or get the Tx beacon rate settings.
+
+    The supported options are:
+        0     - Auto rate
+        >0    - Set specified beacon rate
+        empty - Get current beacon rate
+
+    Tx beacon rate is represented in units of 500 kbps. While setting Tx beacon
+    rates, only zero or rates currently configured are allowed.
+
+	Following is the list of supported rates in units of 500 Kbps:
+	    2,    4,    11,   22,   12,   18,   24,   36,   48,   72,   96,   108
+	    0x02, 0x04, 0x0b, 0x16, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c
+
+    Example:
+	./uaputl.exe sys_cfg_tx_beacon_rate 0x04
+		Set AP Tx beacon rate to 2 M
+
+   	./uaputl.exe sys_cfg_tx_beacon_rate 4
+		Set AP Tx data beacon to 2 M
+
+	./uaputl.exe sys_cfg_tx_beacon_rate
+		Get AP Tx beacon rate
+
+sys_cfg_mcbc_data_rate
+----------------------
+    "./uaputl.exe sys_cfg_mcbc_data_rate [MCBC_DATA_RATE]"
+
+    This command is used to set or get the MCBC data rate to use for multicast
+    or broadcast packet transmission.
+
+    The supported options are:
+        0     - Auto rate
+        >0    - Set specified MCBC data rate
+        empty - Get current MCBC data rate
+
+    MCBC data rate is represented in units of 500 kbps. While setting MCBC data
+    rates, only zero or one of rates currently configured as basic rates are allowed.
+
+    For example:  If current basic rates is "0x82 0x84 0x8b 0x96", then the allowed
+                  values for MCBC data rate will be "0x2 0x4 0xb 0x16".
+
+    Example:
+	./uaputl.exe sys_cfg_mcbc_data_rate 22
+		Set AP MCBC data rate to 11 M
+
+   	./uaputl.exe sys_cfg_mcbc_data_rate 0
+		Set AP MCBC data rate to auto
+
+	./uaputl.exe sys_cfg_mcbc_data_rate
+		Get AP MCBC data rate
+
+sys_cfg_pkt_fwd_ctl
+-------------------
+    "./uaputl.exe sys_cfg_pkt_fwd_ctl [PKT_FWD_CTRL]"
+
+    This command is used to set or get the packet forwarding control
+    settings.
+
+    where PKT_FWD_CTRL is:
+        bit 0 -- Packet forwarding handled by Host (0) or Firmware (1)
+        bit 1 -- Intra-BSS broadcast packets are allowed (0) or denied (1)
+        bit 2 -- Intra-BSS unicast packets are allowed (0) or denied (1)
+        bit 3 -- Inter-BSS unicast packets are allowed (0) or denied (1)
+        empty - Get current packet forwarding setting
+
+    Example:
+	./uaputl.exe sys_cfg_pkt_fwd_ctl 1
+		Set AP packet forwarding control in firmware to allow all packets
+
+	./uaputl.exe sys_cfg_pkt_fwd_ctl 6
+		Set AP packet forwarding control in Host, only allow Inter-BSS unicast packets forwarding.
+
+	./uaputl.exe sys_cfg_pkt_fwd_ctl 8
+		Set AP packet forwarding control in Host, only allow Intra-BSS packets forwarding.
+
+	./uaputl.exe sys_cfg_pkt_fwd_ctl 0
+		Set AP packet forwarding control in Host, allow Intra-BSS packets and
+	            Inter-BSS unicast packets forwarding.
+
+	./uaputl.exe sys_cfg_pkt_fwd_ctl
+		Get AP packet forwarding control
+
+sys_cfg_sta_ageout_timer
+------------------------
+    "./uaputl.exe sys_cfg_sta_ageout_timer [STA_AGEOUT_TIMER]"
+
+    This command is used to set or get the STA ageout value.
+
+    Value of 0 will mean that stations will never be aged out.
+
+    Minimum value for this is 100. Maximum allowed setting should be 864000.
+
+    If no arguments are given, this command returns the current STA ageout
+    value.
+
+    Ageout timer value is represented in units of 100 ms.
+
+    Example:
+	./uaputl.exe sys_cfg_sta_ageout_timer 1800
+		Set AP STA ageout time to 180000 ms
+
+	./uaputl.exe sys_cfg_sta_ageout_timer
+		Get AP STA ageout time
+
+sys_cfg_ps_sta_ageout_timer
+---------------------------
+	"./uaputl.exe sys_cfg_ps_sta_ageout_timer [PS_STA_AGEOUT_TIMER]"
+
+	This command is used to set or get the PS STA ageout value.
+
+	Value of 0 will mean that stations will never be aged out.
+
+	Minimum value for this is 100. Maximum allowed setting should be 864000.
+
+	If no arguments are given, this command returns the current	PS STA ageout
+	value.
+
+	Ageout timer value is represented in units of 100 ms.
+
+	Example:
+	./uaputl.exe sys_cfg_ps_sta_ageout_timer 1800
+		Set AP PS STA ageout time to 180000 ms
+
+	./uaputl.exe sys_cfg_ps_sta_ageout_timer
+		Get AP PS STA ageout time
+
+sys_cfg_auth
+------------
+    "./uaputl.exe sys_cfg_auth [AUTHMODE]"
+
+    This command is used to set or get the AP authentication mode.
+
+    The supported options are:
+        AUTHMODE :     0 - Open authentication
+                       1 - Shared key authentication
+                       255 - Auto (Open and Shared key) authentication
+    empty - Get current authentication mode
+
+    Example:
+	./uaputl.exe sys_cfg_auth 0
+		Set AP authentication mode to Open.
+
+	./uaputl.exe sys_cfg_auth
+		Get AP authentication mode.
+
+sys_cfg_protocol
+----------------
+    "./uaputl.exe sys_cfg_protocol [PROTOCOL] [AKM_SUITE]"
+
+    This command is used to set or get the encryption protocol.
+
+    The supported options are:
+	PROTOCOL:
+			1		 No RSN
+			2		WEP Static
+			8		  WPA
+			32		  WPA2
+			40		 WPA, WPA2 Mixed Mode
+    empty - Get current encryption protocol
+
+    AKM_SUITE:
+            bit 0   KEY_MGMT_EAP
+            bit 1   KEY_MGMT_PSK
+            bit 2   KEY_MGMT_NONE
+            bit 8   KEY_MGMT_PSK_SHA256
+
+    Example:
+	./uaputl.exe sys_cfg_protocol 2
+		Set AP encryption protocol to static WEP.
+
+	./uaputl.exe sys_cfg_protocol
+		Get AP encryption protocol.
+
+    ./uaputl.exe sys_cfg_protocol 32 0x102
+        Set AP encryption protocol to WPA2 and AKM SUITE to PSK and PSK SHA256
+
+    ./uaputl.exe sys_cfg_protocol 40 0x100
+        Set AP encryption protocol to WPA+WPA2 and AKM SUITE to PSK SHA256
+
+sys_cfg_pmf
+-----------
+    "./uaputl.exe sys_cfg_pmf [MFPC] [MFPR]"
+
+    This command is used to set or get the PMF settings.
+
+    The supported options are:
+        MFPC:       0 - Mgmt frame protection not capable
+                    1 - Mgmt frame protection capable
+
+        MFPR:       0 - Mgmt frame protection not required
+                    1 - Mgmt frame protection required
+                    x - don't care if MFPC = 0
+
+    Example:
+    ./uaputl.exe sys_cfg_pmf 1 1
+        Set AP's PMF params to Mgmt frames protection capable and required.
+
+    ./uaputl.exe sys_cfg_pmf
+        Get AP's PMF params settings.
+
+sys_cfg_wep_key
+---------------
+    "./uaputl.exe sys_cfg_wep_key [INDEX ISDEFAULT Key_0]
+                                  [INDEX ISDEFAULT Key_1]
+                                  [INDEX ISDEFAULT Key_2]
+                                  [INDEX ISDEFAULT Key_3]
+                                  [INDEX]"
+
+    This command is used to set or get the WEP key settings.
+
+    The supported options are:
+        INDEX:       0 - KeyIndex is 0
+                     1 - KeyIndex is 1
+                     2 - KeyIndex is 2
+                     3 - KeyIndex is 3
+	ISDEFAULT:   0: KeyIndex is not the default
+		     1: KeyIndex is the default transmit key
+
+        KEY_* :      Key value.
+        empty - Get current WEP key settings for all the keys
+        INDEX - Only INDEX will get the key setting for the particular
+                KeyIndex.
+
+    Example:
+	./uaputl.exe sys_cfg_wep_key 0 1 55555
+		Set AP's default transmit key to "55555", key index is 0.
+
+    ./uaputl.exe sys_cfg_wep_key 0 1 12345678901234567890123456
+        Set AP's default transmit key to "12345678901234567890123456", key index is 0.
+
+	./uaputl.exe sys_cfg_wep_key
+		Get AP all the WEP keys settings.
+
+	./uaputl.exe sys_cfg_wep_key 1
+		Get WEP key setting for the KeyIndex = 1.
+
+sys_cfg_cipher
+--------------
+    "./uaputl.exe sys_cfg_cipher [PAIRWISE_CIPHER GROUP_CIPHER]"
+
+    This command is used to set or get the key types for the pairwise and group key.
+
+    The supported options are:
+	PAIRWISE_CIPHER:
+			0		  None
+			4		  TKIP
+			8		 AES CCMP
+			12		 AES CCMP + TKIP
+        GROUP_CIPHER:
+			0		  None
+			4		  TKIP
+			8		 AES CCMP
+    	empty - Get current key types
+
+	Valid combinations of [PAIRWISE_CIPHER GROUP_CIPHER] are:
+		[0 0], [4 4], [8 8], [12 4].
+
+    Example:
+	./uaputl.exe sys_cfg_cipher 4 4
+		Set AP's pairwise and group key's type to TKIP.
+
+	./uaputl.exe sys_cfg_cipher
+		Get AP's key types for the pairwise and group key.
+
+sys_cfg_pwk_cipher
+------------------
+    "./uaputl.exe sys_cfg_pwk_cipher [<PROTOCOL>] [PAIRWISE_CIPHER]"
+
+    This command is used to set or get protocol and corresponding pairwise cipher settings.
+
+    The supported options are:
+        PROTOCOL:
+                        0                None
+                        8                 WPA
+                        32                WPA2
+        PAIRWISE_CIPHER:
+                        0                 None
+                        4                 TKIP
+                        8                AES CCMP
+                        12               AES CCMP + TKIP
+        WPA/TKIP cipher cannot be used when uAP operates in 802.11n mode.
+        If only PROTOCOL is provided, pairwise cipher for that protocol is displayed.
+        empty - Get protocol and corresponding pairwise cipher settings.
+
+    Example:
+        ./uaputl.exe sys_cfg_pwk_cipher 8 4
+                Set AP's pairwise cipher to TKIP for WPA protocol.
+
+        ./uaputl.exe sys_cfg_pwk_cipher 32
+                Get AP's pairwise cipher for WPA2 protocol.
+
+        ./uaputl.exe sys_cfg_pwk_cipher
+                Get AP's protocol and corresponding pairwise cipher settings.
+
+sys_cfg_gwk_cipher
+------------------
+    "./uaputl.exe sys_cfg_gwk_cipher [GROUP_CIPHER]"
+
+    This command is used to set or get group cipher.
+
+    The supported options are:
+        GROUP_CIPHER:
+                        0                 None
+                        4                 TKIP
+                        8                AES CCMP
+        empty - Get group cipher settings.
+
+    Example:
+        ./uaputl.exe sys_cfg_gwk_cipher 8
+                Set AP's group cipher to AES CCMP.
+
+        ./uaputl.exe sys_cfg_gwk_cipher
+                Get AP's group cipher settings.
+
+sys_cfg_group_rekey_timer
+-------------------------
+    "./uaputl.exe sys_cfg_group_rekey_timer [GROUP_REKEY_TIMER]"
+
+    This command is used to set or get the AP group re-key time interval, in seconds.
+
+    The supported options are:
+        GROUP_REKEY_TIMER is represented in seconds. This is only applicable
+        		 if the protocol is WPA or WPA2. Value of 0 will disable group re-key.
+
+        empty - Get current group rekey timer
+
+    Example:
+	./uaputl.exe sys_cfg_group_rekey_timer 1800
+		Set AP's group re-key time interval to 1800 s
+
+	./uaputl.exe sys_cfg_group_rekey_timer
+		Get AP's group re-key time interval.
+
+sys_cfg_wpa_passphrase
+----------------------
+    "./uaputl.exe sys_cfg_wpa_passphrase [PASSPHRASE]"
+
+    This command is used to set or get the WPA or WPA2 passphrase.
+
+    If no arguments are given, this command returns the current WPA or WPA2
+    passphrase.
+    While setting, the maximum length of the passphrase can be 64 characters.
+
+    Example:
+	./uaputl.exe sys_cfg_wpa_passphrase 1234567890
+		Set AP's WPA or WPA2 passphrase to "1234567890"
+
+	./uaputl.exe sys_cfg_wpa_passphrase
+		Get AP's WPA or WPA2 passphrase.
+
+sys_cfg_max_sta_num
+-------------------
+    "./uaputl.exe sys_cfg_max_sta_num [STA_NUM]"
+
+    This command is used to set or get the maximum number of stations allowed to connect to uAP.
+
+    If no arguments are given, this command returns the configured maximum number of stations
+		allowed to connect to uAP and maximum number of stations supported.
+
+    Example:
+	./uaputl.exe sys_cfg_max_sta_num 2
+		Set AP's maximum station number to 2
+
+	./uaputl.exe sys_cfg_max_sta_num
+		Get AP's maximum station number configured and maximum station number supported.
+
+sys_cfg_retry_limit
+-------------------
+    "./uaputl.exe sys_cfg_retry_limit [RETRY_LIMIT]"
+
+    This command is used to set or get the retry limit to use for packet transmissions.
+
+    The maximum retry_limit allowed is 14.
+
+    If no arguments are given, this command returns the current retry limit value.
+
+    Example:
+	./uaputl.exe sys_cfg_retry_limit 2
+		Set AP's retry limit value to 2
+
+	./uaputl.exe sys_cfg_retry_limit
+		Get AP's retry limit value
+
+sys_cfg_sticky_tim_config
+-------------------------
+    "./uaputl.exe sys_cfg_sticky_tim_config [ENABLE] [<DURATION> <STICKY_BIT_MASK>]"
+
+    This command is used to set or get sticky TIM configuration.
+
+    ENABLE is used to enable or disable sticky TIM feature.
+    Following are the valid values of ENABLE
+    0- disable SticktyTIM
+    1- enable StickyTIM  (Both DURATION in beacons and STICKY_BIT_MASK must be provided)
+    2- enable StickyTIM  (enable sticky TIM without changing previous values of
+       DURATION and STICKY_BIT_MASK)
+
+    When bit 0 of STICKY_BIT_MASK is set, TIM bit is made sticky and when
+    cleared, normal TIM bit updates resume.
+    When bit 1 of STICKY_BIT_MASK is set, MoreData bit is made sticky and when
+    cleared, normal MoreData bit updates resume.
+    STICKY_BIT_MASK = 0 is NOT a valid configuration value.
+
+    If no argument is given, this command returns current sticky TIM configuration.
+
+    Example:
+    ./uaputl.exe sys_cfg_sticky_tim_config
+                Get sticky TIM configuration.
+
+    ./uaputl.exe sys_cfg_sticky_tim_config 0
+                Disable sticky TIM feature.
+
+    ./uaputl.exe sys_cfg_sticky_tim_config 1 30 1
+                Enable sticky TIM feature with DURATION of 30 beacons and
+                STICKY_BIT_MASK set to 1.
+
+sys_cfg_sticky_tim_sta_mac_addr
+-------------------------------
+    "./uaputl.exe sys_cfg_sticky_tim_sta_mac_addr [CONTROL] [STA_MAC_ADDRESS]"
+
+    This command is used to set or get sticky TIM control parameter for associated station.
+
+    CONTROL when set to 1, sticky TIM bit for that station is activated.
+    When set to 0, sticky TIM bit for that station is deactivated.
+
+    If no argument is given, it returns sticky TIM configuration for all associated stations.
+    If only STA_MAC_ADDRESS is provided, it returns sticky TIM configartion for that station.
+
+    Example:
+    ./uaputl.exe sys_cfg_sticky_tim_sta_mac_addr
+            Get sticky TIM configuration for all associated stations.
+
+    ./uaputl.exe sys_cfg_sticky_tim_sta_mac_addr 00:50:43:20:11:22
+            Get control parameter for station 00:50:43:20:11:22.
+
+    ./uaputl.exe sys_cfg_sticky_tim_sta_mac_addr 1 00:50:43:20:11:22
+            Set control parameter for station with MAC address 00:50:43:20:11:22 to 1.
+
+    Note:TIM bit for an associated STA is made sticky only if both below mentioned
+         conditions are satisfied
+         1.Enable = 1 or 2 in most recently received sys_cfg_sticky_tim_config command, and
+         2.Control = 1 in most recently received sys_cfg_sticky_tim_sta_mac_addr
+           with this station MAC address.
+
+sys_cfg_2040_coex
+-------------------------------
+    "./uaputl.exe sys_cfg_2040_coex [ENABLE]"
+
+    This command is used to set or get 20/40 BSS coexistence configuration.
+
+    ENABLE when set to 0, disables 20/40 coex.
+    When set to 1, enables 20/40 coex.
+
+    If no argument is given, it returns 20/40 BSS coex configuration.
+
+    Example:
+    ./uaputl.exe sys_cfg_2040_coex
+            Get 20/40 BSS coexistence configuration.
+
+    ./uaputl.exe sys_cfg_2040_coex 0
+            Disable 20/40 BSS coexistence.
+
+    ./uaputl.exe sys_cfg_2040_coex 1
+            Enable 20/40 BSS coexistence.
+
+    Note:20/40 BSS coex configuration can be set only before starting BSS.
+
+sys_cfg_eapol_pwk_hsk
+---------------------
+    "./uaputl.exe sys_cfg_eapol_pwk_hsk [<TIMEOUT> <RETRIES>]"
+
+	This command is used to set or get pairwise handshake update timeout and
+	number of retries.
+
+	Both TIMEOUT and number of RETRIES should be provided for a 'set'.
+
+	If no arguments are given, this command returns timeout value and number
+	of
+	retries for pairwise key.
+
+	Example:
+	./uaputl.exe sys_cfg_eapol_pwk_hsk 50 2
+		Set AP's pairwise key timeout to 50ms and number of retries to 2.
+
+	./uaputl.exe sys_cfg_eapol_pwk_hsk
+		Get AP's pairwise key timeout and number of retries.
+
+sys_cfg_eapol_gwk_hsk
+---------------------
+	"./uaputl.exe sys_cfg_eapol_gwk_hsk [<TIMEOUT> <RETRIES>]"
+
+	This command is used to set or get groupwise handshake update timeout and
+	number of retries.
+
+	Both TIMEOUT and number of RETRIES should be provided for a	'set'.
+
+	If no arguments are given, this command returns timeout	value and number
+	of retries for groupwise key.
+
+	Example:
+	./uaputl.exe sys_cfg_eapol_gwk_hsk 50 2
+	Set AP's groupwise key timeout to 50ms and number of retries to 2.
+
+	./uaputl.exe sys_cfg_eapol_gwk_hsk
+	Get AP's groupwise key timeout and number of retries.
+
+sta_filter_table
+----------------
+    "./uaputl.exe sta_filter_table <FILTERMODE> [<MACADDRESS_LIST>]"
+
+    This command is used to get or set the client station MAC address
+    filter table.
+
+    The supported options are:
+    FILTERMODE : 0 - Disable filter table
+                 1 - Allow mac address specified in the allowed list
+                 2 - Block MAC addresses specified in the  banned list
+    MACADDRESS_LIST is the list of MAC addresses to be acted upon. Each
+    MAC address must be separated with a space. Maximum of 16 MAC addresses
+    are supported.
+
+    empty - Get current client station MAC address filter table.
+
+    Example:
+	./uaputl.exe sta_filter_table 0
+		Disable filter table
+
+	./uaputl.exe sta_filter_table 1 00:50:43:20:aa:bb
+		Set AP's filter mode to allow, only MAC address "00:50:43:ab:bb" will be allowed.
+
+	./uaputl.exe sta_filter_table
+		Get AP's filter table settings.
+
+regrdwr
+-------
+     "./uaputl.exe regrdwr <TYPE> <OFFSET> [value]"
+
+     These commands are used to read the MAC, BBP and RF registers from the card.
+     TYPE can take 3 values, 1 - read/write MAC register
+                             2 - read/write BBP register
+                             3 - read/write RF  register
+
+     OFFSET specifies the offset location that is to be read.
+     This parameter can be specified either in decimal or in hexadecimal (by preceding the number with a "0x").
+
+     value if specified, then that value will be written to that offset in the specified register. Value should be
+     specified in hexadecimal.
+
+     Example:
+	./uaputl.exe regrdwr 1 0xa123
+		read MAC register 0xa123
+
+	./uaputl.exe regrdwr 1 0xa123 0xaa
+		write 0xaa to MAC register 0xa123
+
+        ./uaputl.exe regrdwr 2 0x0123
+		read BBP register 0x0123
+
+	./uaputl.exe regrdwr 2 0x0123 0xaa
+		write 0xaa to BBP register 0x0123
+
+        ./uaputl.exe regrdwr 3 0x0123
+		read RF register 0x0123
+
+	./uaputl.exe regrdwr 3 0x0123 0xaa
+		write 0xaa to RF register 0x0123
+
+memaccess
+---------
+ 	"./uaputl.exe memaccess <ADDR> [value]"
+	This commands is used to read/write to a memory address
+
+	ADDR specifies the address of the location that is to be read/write
+	This parameter can be specified either in decimal or in hexadecimal (by preceding the number with a "0x").
+
+	value if specified, then that value will be written to that address in the specified register.
+
+	Example:
+		./uaputl.exe memaccess 0xc00153e4
+			read contents of memory location 0xc00153e4
+
+
+		./uaputl.exe memaccess 0xc00153e4 0xaabbccdd
+			write value 0xaabbccdd to  memory location 0xc00153e4
+
+rdeeprom
+--------
+    "./uaputl.exe rdeeprom <offset> <bytecount>"
+
+    This command is used to read bytes from offset location on
+    EEPROM
+
+    offset: 0,4,8,..., multiple of 4
+    bytecount: 4-20, multiple of 4
+
+   	Example:
+    ./uaputl.exe rdeeprom 200 12
+        read 12 bytes from offset 200 ON EEPROM
+
+cfg_data
+--------
+ 	"./uaputl.exe cfg_data <type> [*.conf]"
+
+    This command is used to set/get the configuration data to/from the firmware.
+
+    type: 2 -- cal data
+
+   	Example:
+        ./uaputl.exe cfg_data 2 cal_data.conf
+            read cal_data from cal_data.conf and download to firmware.
+        ./uaputl.exe cfg_data 2
+            read cal_data from firmware
+
+sys_cfg_80211d
+--------------
+    "./uaputl.exe sys_cfg_80211d [state STATE] [country COUNTRY]"
+    This command is used to set/get 802.11D specific parameters.
+    If no parameters are provided, this command returns state, country and
+    domain information.
+
+    Allowed values for state are 0 for disable and 1 for enable.
+    COUNTRY is a two letter string input (derived from ISO 3166 code;
+    http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm
+    )
+
+    Countries are mapped with specific domain in file "80211d_domain.conf". In
+    order to set customize band setting, user can modify 80211d_domain.conf
+    file.
+
+    Example:
+    ./uaputl.exe sys_cfg_80211d state 0
+        To-disable
+
+    ./uaputl.exe sys_cfg_80211d state 1
+        To-enable
+
+    ./uaputl.exe sys_cfg_80211d country IN
+        for using country as INDIA
+
+    ./uaputl.exe sys_cfg_80211d state 1 country US
+        for enabling and setting country in single command.
+
+uap_stats
+---------
+    "./uaputl.exe uap_stats"
+    This command is used to get uAP statistics.
+
+    Example:
+    ./uaputl.exe uap_stats
+
+pscfg
+---------
+    "./uaputl.exe pscfg [MODE] [CTRL INACTTO MIN_SLEEP MAX_SLEEP MIN_AWAKE MAX_AWAKE]
+
+    This command is used to set or get the AP's power mode and power save params.
+
+    The supported options are:
+        MODE :     0 - disable power mode
+                   2 - enable inactivity based power save mode
+
+        PS PARAMS:
+            CTRL:  0 - disable protection frame Tx before PS
+                   1 - enable protection frame Tx before PS
+            INACTTO: Inactivity timeout in microseconds, default value is 200000 us
+            MIN_SLEEP: Minimum sleep duration in microseconds, default value 17000 us
+            MAX_SLEEP: Maximum sleep duration in microseconds, default value 17000 us
+			The value of MIN_SLEEP should be >= 5000 us.
+			The value of MAX_SLEEP should be <= beacon interval(when ctrl: 0).
+			The value of MAX_SLEEP should be <= 32000 us(when ctrl: 1).
+
+            MIN_AWAKE: Minimum awake duration in microseconds, default value is 2000 us
+            MAX_AWAKE: Maximum awake duration in microseconds, default value is 2000 us
+			The value of MIN_AWAKE should be >= 2000 us.
+            MIN_AWAKE,MAX_AWAKE only valid when MODE is set to inactivity based power save mode.
+
+
+	empty - Get current power mode and power save params.
+
+    Example:
+	./uaputl.exe pscfg 0
+		Disable AP's power mode.
+
+   	./uaputl.exe pscfg 2
+		Enable inactivity based power save mode.
+
+   	./uaputl.exe pscfg 2 1 400000 20000 20000 10000 10000
+		Enable inactivity based power save mode, enable protection, set inactivity timeout 400000 us
+        set minimum sleep duration to 20000 us, maximum sleep duration to 20000 us
+        and set minimum awake duration to 10000us, maximum awake duration to 10000 us
+
+	./uaputl.exe pscfg
+		Get current AP's power mode and power save params.
+
+hscfg
+-----
+    ./uaputl.exe hscfg [condition [[GPIO# [gap]]]]
+	This command is used to configure the host sleep parameters.
+
+	This command takes one (condition), two (condition and GPIO#) or three
+	(condition, GPIO# and gap) parameters for set. If no parameter provided,
+	get is performed.
+
+	where Condition is:
+		bit 0 = 1   -- broadcast data
+		bit 1 = 1   -- unicast data
+		bit 2 = 1   -- mac event
+		bit 3 = 1   -- multicast data
+		bit 6 = 1  --  Wakeup when mgmt frame received.
+
+	The host sleep mode will be cancelled if condition is set to 0xffff.
+	The default is 0x7.
+
+	where GPIO is the pin number of GPIO used to wakeup the host. It could be any valid
+	GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO will be used instead).
+	The default is 0xff.
+
+	where Gap is the gap in milliseconds between wakeup signal and wakeup event or 0xff
+	for special setting (host acknowledge required) when GPIO is used to wakeup host.
+	The default is 200.
+
+	Examples:
+		./uaputl.exe hscfg              : Get current host sleep mode
+		./uaputl.exe hscfg 0xffff       : Cancel host sleep mode
+		./uaputl.exe hscfg 3            : Broadcast and unicast data
+		                                  Use GPIO and gap set previously
+		./uaputl.exe hscfg 2 3          : Unicast data
+		                                  Use GPIO 3 and gap set previously
+		./uaputl.exe hscfg 2 1 0xa0     : Unicast data
+		                                  Use GPIO 1 and gap 160 ms
+		./uaputl.exe hscfg 2 0xff       : Unicast data
+		                                  Use interface (e.g. SDIO)
+		                                  Use gap set previously
+		./uaputl.exe hscfg 4 3 0xff     : MAC event
+		                                  Use GPIO 3
+		                                  Special host sleep mode
+		./uaputl.exe hscfg 1 0xff 0xff  : Broadcast data
+		                                  Use interface (e.g. SDIO)
+		                                  Use gap 255ms
+
+hssetpara
+---------
+    ./uaputl.exe hssetpara condition [[GPIO# [gap]]]
+    This command is used to configure the host sleep parameters.
+
+    Note:
+    1) The usages of parameters are the same as "hscfg" command.
+    2) The parameters will be saved in the driver and be used when host suspends.
+
+sta_deauth_ext
+--------------
+    "./uaputl.exe sta_deauth_ext <STA_MAC_ADDRESS><REASON_CODE>"
+
+    This command is used to de-authenticate a client station with specific reason code.
+
+    Example:
+        ./uaputl.exe sta_deauth_ext 00:50:43:20:34:58  4
+            deauth station 00:50:43:20:34:58 with IEEE reason code 4 (Disassociated due to inactivity)
+
+mic_err
+-------
+    "./uaputl.exe mic_err <STA_MAC_ADDRESS>"
+
+    This command is used to report station mic error.
+
+    Example:
+        ./uaputl.exe mic_err 00:50:43:20:34:58
+            report mic err for station 00:50:43:20:34:58
+
+key_material
+------------
+    "./uaputl.exe key_material <MAC_ADDRESS> <KEY> [KEY_ID]"
+
+    This command is used to set key. Get operation is not supported.
+
+        MAC_ADDRESS: station mac address while setting station unicast key
+                     ff:ff:ff:ff:ff:ff while setting multicast key
+
+        KEY: hex string, allowed length should be 32 or 64.
+        KEY_ID: default value is 0.
+
+    Example:
+        ./uaputl.exe key_material ff:ff:ff:ff:ff:ff 12345678901234567890123456789012 1
+            set group key, key value is "12345678901234567890123456789012", key id is 1
+
+        ./uaputl.exe key_material 00:50:43:20:34:58 1234567890123456789012345678901234567890123456789012345678901234
+            set station 00:50:43:20:34:58 unicast key,
+            key value is "1234567890123456789012345678901234567890123456789012345678901234"
+
+sys_cfg_custom_ie
+-----------------
+    "./uaputl.exe sys_cfg_custom_ie [INDEX] [MASK] [IEBuffer]"
+
+    This command is used to set or get custom IEs for management frames.
+
+    The supported options are:
+        INDEX:       0 - IE Index is 0
+                     1 - IE Index is 1
+                     2 - IE Index is 2
+                     MAX IE Index depends on device memory.
+
+               	    -1 - Append/Delete IE automatically
+                         Delete will delete the IE from the matching IE buffer
+                         Append will append the IE to the buffer with the same mask
+        MASK :       Management subtype mask value as per bit definitions
+	                   :  Bit 0 - Association request.
+	                   :  Bit 1 - Association response.
+	                   :  Bit 2 - Reassociation request.
+	                   :  Bit 3 - Reassociation response.
+	                   :  Bit 4 - Probe request.
+	                   :  Bit 5 - Probe response.
+	                   :  Bit 8 - Beacon.
+        MASK :       MASK = 0 to clear the mask and the IE buffer
+
+        IEBuffer:    IE buffer to set in hexadecimal bytes.
+	                  The Buffer should not be space separated.
+	                  ( Maximum length = 256 bytes )
+        empty - Get IE buffer, subtype mask settings for all the indices [0-3].
+        INDEX - Only INDEX will get the IE buffer configured for the particular
+                Index.
+
+    Example:
+	./uaputl.exe sys_cfg_custom_ie
+		Get IE buffer, subtype mask settings for all indices.
+
+	./uaputl.exe sys_cfg_custom_ie 1
+		Get IE buffer and subtype mask WEP key setting for the Index = 1.
+
+	./uaputl.exe sys_cfg_custom_ie 2 0
+		Clear IE buffer and mask value for Index = 2.
+
+	./uaputl.exe sys_cfg_custom_ie 3 0x101 0xdd051234567890
+		Set IE buffer and mask value for Index = 3.
+
+	./uaputl.exe sys_cfg_custom_ie -1 0x101 0xdd051234567890
+		Append the specified IEBuffer at index with mask value of 0x101
+
+	./uaputl.exe sys_cfg_custom_ie -1 0 0xdd051234567890
+		Delete the specified IEBuffer from all the IEs.
+
+	./uaputl.exe sys_cfg_custom_ie 2 0 0xdd051234567890
+		Delete the specified IEBuffer from the IEs at index 2.
+
+coex_config
+-----------
+    "./uaputl.exe coex_config [CONFIG_FILE]"
+    This command is used to set or get the BT coex configuration settings.
+
+    The supported options are:
+        CONFIG_FILE is file contain all the BT coex settings.
+        empty - Get current BT coex settings
+
+    Example:
+	./uaputl.exe coex_config
+		Get current BT coex settings.
+
+	./uaputl.exe coex_config uapcoex.conf
+		Load BT coex configuration settings from uapcoex.conf file and set.
+
+sys_cfg_wmm
+-----------
+    "./uaputl.exe sys_cfg_wmm [qosinfo=<qosinfo>]
+                              [0]
+                              [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP]
+                              [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP]
+                              [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP]
+                              [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP]"
+
+    This command can be used set/get beacon WMM parameters
+
+    The supported option are:
+        qosinfo: qos information. User can set only MSB. Valid values are 0x80 and 0x00.
+        Lower 4 bits are managed by FW. Hence, value read for qosinfo may have
+        lower 4 bits non-zero.
+
+        AC_BE: 0
+        AC_BK: 1
+        AC_VI: 2
+        AC_V0: 3
+        AIFSN: AIFSN value
+        ECW_MAX: ECW max
+        ECW_MIN: ECW min
+        TX_OP: TXOP Limit
+        empty - Get current WMM parameters
+        When all the parameter are 0, wmm will be disabled.
+
+        Example:
+        ./uaputl.exe sys_cfg_wmm 0 3 10 4 0
+           Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+
+        ./uaputl.exe sys_cfg_wmm 1 7 10 4 0
+           Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+
+        ./uaputl.exe sys_cfg_wmm 2 2 4 3 94
+           Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94
+
+        ./uaputl.exe sys_cfg_wmm 3 2 3 2 47
+           Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47
+
+        ./uaputl.exe sys_cfg_wmm
+            Get current wmm parameters
+
+        ./uaputl.exe sys_cfg_wmm 0 3 10 4 0 1 7 10 4 0 2 2 4 3 94 3 2 3 2 47
+            Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+            Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+            Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94
+            Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47
+
+        ./uaputl.exe sys_cfg_wmm qosinfo=0x80 0 3 10 4 0 1 7 10 4 0 2 2 4 3 94 3 2 3 2 47
+            Enable wmm PS mode.
+            Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+            Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+            Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94
+            Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47
+
+        ./uaputl.exe sys_cfg_wmm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+        ./uaputl.exe sys_cfg_wmm 0
+            Disable wmm
+
+sys_cfg_ap_wmm
+-----------
+    "./uaputl.exe sys_cfg_ap wmm [0]
+                                 [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP]
+                                 [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP]
+                                 [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP]
+                                 [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP]"
+
+    This command can be used set/get AP WMM parameters
+
+    The supported option are:
+        AC_BE: 0
+        AC_BK: 1
+        AC_VI: 2
+        AC_V0: 3
+        AIFSN: AIFSN value
+        ECW_MAX: ECW max
+        ECW_MIN: ECW min
+        TX_OP: TXOP Limit
+        empty - Get current AP WMM parameters
+        When all the parameter are 0, AP wmm will be disabled.
+
+        Example:
+        ./uaputl.exe sys_cfg_ap_wmm 0 3 10 4 0
+           Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+
+        ./uaputl.exe sys_cfg_ap_wmm 1 7 10 4 0
+           Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+
+        ./uaputl.exe sys_cfg_ap_wmm 2 2 4 3 94
+           Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94
+
+        ./uaputl.exe sys_cfg_ap_wmm 3 2 3 2 47
+           Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47
+
+        ./uaputl.exe sys_cfg_ap_wmm
+            Get current AP wmm parameters
+
+        ./uaputl.exe sys_cfg_ap_wmm 0 3 10 4 0 1 7 10 4 0 2 2 4 3 94 3 2 3 2 47
+            Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+            Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0
+            Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94
+            Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47
+
+        ./uaputl.exe sys_cfg_ap_wmm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+        ./uaputl.exe sys_cfg_ap_wmm 0
+            Disable AP wmm
+
+addbapara
+---------
+    "./uaputl.exe addbapara [timeout txwinsize rxwinsize txamsdu rxamsdu]"
+	This command can be used to update the default ADDBA parameters.
+
+	The supported options are:
+	    timeout - This is the block ack timeout for ADDBA request.
+		    0 : Disable (recommended for throughput test)
+		    1 - 65535 : Block Ack Timeout in TU
+	    txwinsize - Buffer size for ADDBA request. (32 is default value)
+	    rxwinsize - Buffer size for ADDBA response. (16 is default value)
+	    txamsdu - amsdu support for ADDBA request. (1 is default value)
+		    0 : Disable amsdu in ADDBA request.
+		    1 - Enable amsdu in ADDBA request.
+	    rxamsdu - amsdu support for ADDBA response. (1 is default value)
+		    0 : Disable amsdu in ADDBA response.
+		    1 - Enable amsdu in ADDBA response.
+        empty - Get current ADDBA parameters.
+
+	Current window size limit for Tx as well as Rx is 1023.
+
+	Example:
+	./uaputl.exe addbapara
+        Get the current addba params
+	./uaputl.exe addbaparam 1000 64 8 0 0
+        This will change the ADDBA timeout to (1000 * 1024) us,	txwinsize to 64 and rxwinsize to 8
+        and disable AMSDU in ADDBA request/response.
+
+	In case the ADDBA timeout value is updated then a ADDBA is sent for all streams
+	to update the timeout value.
+
+	In case txwinsize and/or rxwinsize is updated, the effect could only be seen on
+	next ADDBA request/response. The current streams will not be affected with this
+	change.
+
+	In case of txamdsdu/rxamsdu is updated, the effect could only be seen on
+	next ADDBA request/response. The current streams will not be affected with this
+	change. AMSDU in AMPDU stream will be enabled when AP support this feature
+	and AMSDU is enabled in aggrpriotbl.
+
+sys_cfg_11n
+-----------
+    "./uaputl.exe sys_cfg_11n [ENABLE] [HTCAP] [AMPDU] [TXBFCAP] [HT_MCS_CAP]"
+    This command can be used set/get 802.11n parameters.
+
+    The supported option are:
+        ENABLE: 0 - disable 802.11n in uap
+                1 - enable 802.11n in uap
+		Note: If 802.11n is disabled, 802.11ac MUST be disabled at the same time
+        HTCAP: HT Capabilities info (default value is 0x111c)
+               Bit 15-13: Reserved set to 0
+               Bit 12: DSS/CCK mode in 40MHz enable/disable
+               Bit 11-10: Reserved set to 0
+               Bit 9-8: Reserved set to 0x01
+               Bit 7: STBC enable/disable
+               Bit 6: Short GI in 40 Mhz enable/disable
+               Bit 5: Short GI in 20 Mhz enable/disable
+               Bit 4: Green field enable/disable
+               Bit 3-2: Reserved set to 1
+               Bit 1: 20/40 Mhz enable disable.
+               Bit 0: LDPC enable/disable
+        AMPDU: A-MPDU Parameter (default value is 0x03)
+               Bit 7-5: Reserved set to 0
+               Bit 4-2: Minimum MPDU Start spacing
+                        Set to 0 for no restriction
+                        Set to 1 for 1/4 us
+                        Set to 2 for 1/2 us
+                        Set to 3 for 1 us
+                        Set to 4 for 2 us
+                        Set to 5 for 4 us
+                        Set to 6 for 8 us
+                        Set to 7 for 16 us
+               Bit 1-0: Max A-MPDU length
+        TXBFCAP: TX Beamforming capabilities
+               Bit 0    : Implicit TX BF receiving capable
+               Bit 1    : RX staggered sounding capable
+               Bit 2    : TX staggered sounding capable
+               Bit 3    : RX NDP capable
+               Bit 4    : TX NDP capable
+               Bit 5    : Implicit TX BF capable
+               Bit 6-7  : Calibration
+                       0: - not supported
+                       1: - STA can respond to a calibration request using
+                            the CSI Report, but cannot initiate calibration
+                       2: - reserved
+                       3: - STA can both initiate and respond to a calibration request
+               Bit 8    : Explicit CSI TX BF capable
+               Bit 9    : Explicit non-compressed steering capable
+               Bit 10   : Explicit compressed steering capable
+               Bit 11-12: Explicit TX BF CSI feedback
+                       0: - not supported
+                       1: - delayed feedback
+                       2: - immediate feedback
+                       3: - delayed and immediate feedback
+               Bit 13-14: Explicit non-compressed BF feedback capable
+                       0: - not supported
+                       1: - delayed feedback
+                       2: - immediate feedback
+                       3: - delayed and immediate feedback
+               Bit 15-16: Explicit compressed BF feedback capable
+                       0: - not supported
+                       1: - delayed feedback
+                       2: - immediate feedback
+                       3: - delayed and immediate feedback
+               Bit 17-18: Minimal grouping
+                       0: - no grouping (STA supports groups of 1)
+                       1: - groups of 1, 2
+                       2: - groups of 1, 4
+                       3: - groups of 1, 2, 4
+               Bit 19-20: CSI number of beamformer antennas supported
+                       0: - single TX antenna sounding
+                       1: - 2 TX antenna sounding
+                       2: - 3 TX antenna sounding
+                       3: - 4 TX antenna sounding
+               Bit 21-22: Non-compressed steering number of beamformer antennas supported
+                       0: - single TX antenna sounding
+                       1: - 2 TX antenna sounding
+                       2: - 3 TX antenna sounding
+                       3: - 4 TX antenna sounding
+               Bit 23-24: Compressed steering number of beamformer antennas supported
+                       0: - single TX antenna sounding
+                       1: - 2 TX antenna sounding
+                       2: - 3 TX antenna sounding
+                       3: - 4 TX antenna sounding
+               Bit 25-26: CSI max number of rows beamformer supported
+                       0: - single row of CSI
+                       1: - 2 rows of CSI
+                       2: - 3 rows of CSI
+                       3: - 4 rows of CSI
+               Bit 27-28: Channel estimation capability
+                       0: - 1 space time stream
+                       1: - 2 space time streams
+                       2: - 3 space time streams
+                       3: - 4 space time streams
+               Bit 29-31: Reserved
+        HT_MCS_MAP: MCS rate bitmap
+               Bit 0-7   : MCS_SET_0
+               Bit 15-8  : MCS_SET_1
+        empty - Get current 802.11n parameters.
+
+        Example:
+        ./uaputl.exe sys_cfg_11n 1 0x117e 3
+            enable 802.11n, and set HT Capabilities info to 0x117e, and A-MPDU Parameter to 0x03
+        ./uaputl.exe sys_cfg_11n 1 0x117e 3 0xFFFFFFFF
+            enable 802.11n, and set HT Capabilities info to 0x117e, A-MPDU
+            Parameter to 0x03 and TX Beamforming capabilities to 0xFFFFFFFF
+        ./uaputl.exe sys_cfg_11n 1 0x117e 3 0xFFFFFFFF 0x00100023
+            enable 802.11n, and set HT Capabilities info to 0x117e, A-MPDU
+            Parameter to 0x03, TX Beamforming capabilities to 0xFFFFFFFF and
+            MCS rate bitmap to 0x00100023.
+        ./uaputl.exe sys_cfg_11n 0
+            disable 802.11n in uap
+        ./uaputl.exe sys_cfg_11n
+            Get current 802.11n parameters
+
+aggrpriotbl
+-----------
+        "./uaputl.exe aggrpriotbl <m0> <n0> <m1> <n1> ... <m7> <n7>"
+        This command is used set/get the priority table for AMPDU/AMSDU traffic per tid.
+	    This command can also be used to disable AMPDU/AMSDU for a given tid.
+	    In case of AMPDU this priority table will be used to setup block ack (to make
+	    sure the highest priority tid always uses AMPDU as we have limited AMPDU streams)
+
+        The supported option are:
+            <m0> <n0> <m1> <n1> ... <m7> <n7>
+
+	    <mx> - This is priority for Tid0 for AMPDU packet. A priority could be any
+                values between 0 - 7, 0xff to disable aggregation.
+	    <nx> - This is priority for Tid0 for AMSDU packet. A priority could be any
+		        values between 0 - 7, 0xff to disable aggregation.
+
+        empty - Get current priority table for AMPDU/AMSDU traffic.
+
+        Example:
+        ./uaputl.exe aggrpriotbl
+            This command will get the current Priority table for AMPDU and AMSDU.
+			<2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255>. This is read as
+			<"Prio for AMPDU for Tid0" "Prio for AMSDU for Tid0"
+			"Prio for AMPDU for Tid1" "Prio for AMSDU for Tid1" and so on
+
+	    ./uaputl.exe aggrpriotbl 2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255
+            This will set the priority table for AMPDU and AMSDU
+			Priority for Tid0/AMPDU = 2, Tid0/AMSDU = 2, Tid1/AMPDU = 0, Tid1/AMSDU = 0
+			and so on. Aggregation for Tid6 and Tid7 are disabled.
+			Here higher the priority number, higher the priority (i.e. 7
+			has higher priority than 6). Similarly for AMSDU.
+
+	    ./uaputl.exe aggrpriotbl 0xff 2 0xff 0 0xff 1 0xff 3 0xff 4 0xff 5 0xff 0xff 0xff 0xff
+            This will disable AMPDU for all the TIDs but will still keep AMSDU enabled to Tid0 to Tid5
+
+	    The default setting is 2 255 0 255 1 255 3 255 4 255 5 255 255 255 255 255.
+
+	    A delBA should be seen in case a disable happens on a TID for which AMPDU stream
+	        is currently setup.
+
+        Note:- This command should only be issued in disconnected state.
+
+addbareject
+-----------
+        "./uaputl.exe addbareject <m0> <m1> ... <m7>"
+        This command is used set/get the addbareject table for all the TIDs.
+	    This command can also be used to enable rejection of ADDBA requests for a given tid.
+
+        The supported option are:
+            <m0> <m1> ... <m7>
+        <mX> - This can be 0/1 for TidX. 1 enables rejection of ADDBA request for TidX and
+		   0 would accept any ADDBAs for TidX.
+        empty - Get current addbareject table for all the TIDs.
+
+        Example:
+        ./uaputl.exe addbareject
+            This command will get the current table.
+	        [0 0 0 0 0 0 1 1]. ADDBA would be accepted for all TIDs except for TID [6,7].
+	        This is the default state.
+
+        ./uaputl.exe addbareject 0 0 1 1 0 0 0 0
+            This command will accept ADDBA requests for Tid [0,1,4,5,6,7] and reject ADDBA requests for Tid [2,3]
+
+	    ./uaputl.exe addbareject 1 1 1 1 1 1 1 1
+            This will enable rejection of ADDBA requests for all Tids.
+
+        Note:- This command should only be issued in disconnected state.
+
+sys_cfg_tdls_ext_cap
+--------------------
+
+    "./uaputl.exe sys_cfg_tdls_ext_cap [CONFIG_FILE]"
+
+    This command is used to set/get TDLS extended capability settings.
+
+    If CONFIG_FILE is provided, a SET is performed , else a GET is performed.
+
+    Examples:
+        ./uaputl.exe sys_cfg_tdls_ext_cap config/tdls_ext_cap.conf
+        Set TDLS extended capability parameters in the config file.
+        ./uaputl.exe sys_cfg_tdls_ext_cap
+        Get TDLS extended capability parameters.
+
+httxbfcfg
+---------
+	"./uaputl.exe httxbfcfg <ACTION> [ACT_DATA]"
+
+	This command is used to configure the TX beamforming options.
+
+	The supported options are:
+		ACTION: 0 - Control global parameters for beamforming
+                1 - Performs NDP Sounding for PEER
+		        2 - TX BF interval in milliseconds
+		        3 - Enable/Disable beamforming/sounding for the indicated peer.
+		        4 - TX BF SNR Threshold for peer
+		ACT_DATA: Specific data for the above actions
+                  For ACTION 0 - Beamforming enable/disable, sounding enable/disable,
+                                 FB type, snr_threshold, sounding interval, Beamformig mode
+		          For ACTION 1 - PEER MAC and status
+		          For ACTION 2 - TX BF interval
+		          For ACTION 3 - PEER MAC
+		          For ACTION 4 - PEER MAC and SNR
+		          empty - Get action specific settings
+
+	Examples:
+		./uaputl.exe httxbfcfg 0                          : Get current global configuration parameter
+		./uaputl.exe httxbfcfg 2 00:50:43:20:BF:64        : Get the TX BF periodicity for a given peer
+		./uaputl.exe httxbfcfg 3                          : Get the list of MAC addresses that have
+		                                                      beamforming and/or sounding enabled
+		./uaputl.exe httxbfcfg 4                          : Get the list of PEER MAC, SNR tuples
+		                                                      programmed into the firmware.
+		./uaputl.exe httxbfcfg 0 0 0 3 10 500 5           : Disable beamforming, sounding, set FB type
+		                                                      to 3, snr threshold to 10, sounding interval
+		                                                      to 500 ms and beamforming mode to 5
+		./uaputl.exe httxbfcfg 1 00:50:43:20:BF:64        : Perform NDP Trigger sounding to peer
+		                                                      00:50:43:20:BF:64
+		./uaputl.exe httxbfcfg 2 00:50:43:20:BF:64 500    : Set TX BF periodicity for peer 00:50:43:20:BF:64
+		                                                      to 500 milliseconds
+		./uaputl.exe httxbfcfg 3 00:50:43:20:BF:43 1 0 3  : Enable beamforming, disable sounding and set
+		                                                      FB type to 3 for peer 00:50:43:20:BF:43
+		./uaputl.exe httxbfcfg 4 00:50:43:20:BF:24 43     : Set TX BF SNR threshold to peer
+
+
+httxcfg
+	This command is used to configure various 11n specific configuration
+	for transmit (such as Short GI, Channel BW and Green field support)
+
+	where <m> is <txcfg>
+	This is a bitmap and should be used as following
+		Bit 15-10: Reserved set to 0
+        Bit 9-8: Rx STBC set to 0x01
+        BIT9 BIT8  Description
+        0    0     No spatial streams
+        0    1     One spatial streams supported
+        1    0     Reserved
+        1    1     Reserved
+		Bit 7: STBC enable/disable
+		Bit 6: Short GI in 40 Mhz enable/disable
+		Bit 5: Short GI in 20 Mhz enable/disable
+		Bit 4: Green field enable/disable
+		Bit 3-2: Reserved set to 1
+		Bit 1: 20/40 Mhz enable disable.
+		Bit 0: LDPC enable/disable
+
+	When Bit 1 is set then firmware could transmit in 20Mhz or 40Mhz based
+	on rate adaptation. When this bit is reset then firmware will only
+	transmit in 20Mhz.
+
+	where <n> is <band>
+	<band> - This is the band info for <txcfg> settings.
+		0: Settings for both 2.4G and 5G bands
+		1: Settings for 2.4G band
+		2: Settings for 5G band
+
+	Example:
+		./uaputl.exe -i uapX httxcfg
+		This will display HT Tx configuration for 2.4G and 5G band.
+
+		./uaputl.exe -i uapX httxcfg 0x62
+		This will enable 20/40 and Short GI but will disable Green field for 2.4G and 5G band.
+
+		./uaputl.exe -i uapX httxcfg 0x30 1
+		This will enable Short GI 20 Mhz and Green field for 2.4G band.
+
+	The default value is 0x20 for 2.4G and 0x62 for 5G.
+
+	Note:- If 20/40 MHz support is disabled in htcapinfo, device will not transmit
+	in 40 MHz even 20/40 MHz is enabled in httxcfg.
+
+htstreamcfg
+	This command is used to set/get HT stream configuration.
+	The setting only takes effect in next association.
+
+	Usage:
+		./uaputl.exe -i uapX htstreamcfg [n]
+
+	where <n>
+		0x11: HT stream 1x1 mode
+		0x22: HT stream 2x2 mode
+
+	Examples:
+		./uaputl.exe -i uapX htstreamcfg        : Get current setting
+		./uaputl.exe -i uapX htstreamcfg 0x11   : Set HT stream 1x1 mode
+		./uaputl.exe -i uapX htstreamcfg 0x22   : Set HT stream 2x2 mode
+
+deepsleep
+---------
+    "./uaputl.exe deepsleep [MODE] [IDLE_TIME]"
+    This command is used to set/get auto deep sleep mode.
+
+    The supported option are:
+        [MODE]: Enable/disable auto deep sleep mode (1/0)
+        [IDLE_TIME]: Idle time in milliseconds after which firmware will put the device
+                    in deep sleep mode. Default value is 100 ms.
+        empty - Get current deep sleep mode.
+
+    Example:
+    ./uaputl.exe deepsleep          : Display auto deep sleep mode
+    ./uaputl.exe deepsleep 1        : Enable auto deep sleep mode, idle time unchanged
+    ./uaputl.exe deepsleep 0        : Disable auto deep sleep mode
+    ./uaputl.exe deepsleep 1 500    : Enable auto deep sleep mode with idle time 500 ms
+
+    Note:
+            Deepsleep must be disabled before changing idle time.
+
+sdcmd52rw
+	This command is used to read/write a controller register in
+	Secure Digital I/O Interfaces.
+
+	Usage:
+    "./uaputl.exe sdcmd52rw <function number> <register address> [value]"
+
+	For SDIO MMC driver, only function 0 and 1 access is allowed. And there
+	is a limitation for function 0 write, only vendor specific CCCR registers
+	(0xf0 -0xff) are permiited.
+
+	Examples:
+    ./uaputl.exe sdcmd52rw 1 3          : Issue cmd52 to read function 1, register 3.
+    ./uaputl.exe sdcmd52rw 1 1 0x3f     : Issue cmd52 to write function 1, register 1
+                                          with value 0x3f.
+
+txpwrlimit_cfg_get
+txpwrlimit_2g_cfg_set
+txpwrlimit_5g_cfg_set
+------------------
+    This command is used to set/get the configuration data of Tx power limitation.
+    Note: The configuration set should be issued when no STA is connected.
+
+    Examples:
+        ./uaputl.exe hostcmd config/txpwrlimit_cfg.conf txpwrlimit_cfg_get
+        ./uaputl.exe hostcmd config/txpwrlimit_cfg.conf txpwrlimit_2g_cfg_set
+        ./uaputl.exe hostcmd config/txpwrlimit_cfg.conf txpwrlimit_5g_cfg_set
+
+rxpktcoal_cfg
+-------------
+   "./uaputl.exe rxpktcoal_cfg [PKT-THRESHOLD] [TIMEOUT]"
+
+	This is used to get/set RX packet coalescing paramters
+
+    The supported option are:
+        [PKT-THRESHOLD]: count after which packets would be sent to host. Valid values 1-7
+        [TIMEOUT]: timeout in ms after which packets would be sent to host. Valid values 1-4
+        Coalescing is disabled if both or either of packet_thershold and delay is zero
+
+        RX packet coalescing parameters can be changed only when device is in
+        idle state i.e. all interfaces are disconnected.
+
+    Example:
+    ./uaputl.exe rxpktcoal_cfg      : Display RX packet coalescing settings
+    ./uaputl.exe rxpktcoal_cfg 5 1  : Enable RX packet coalescing: packet count 5, delay 1.
+    ./uaputl.exe rxpktcoal_cfg 0 0  : Disable RX packet coalescing.
+
+tx_data_pause
+-------------
+  "./uaputl.exe tx_data_pause [ENABLE][TX_BUF_CNT]"
+
+    This command is used to set/get tx data pause settings.
+
+    The supported option are:
+        [ENABLE]: Enable/disable pause tx events from firmware to host.
+        [TX_BUF_CNT]: Max number of TX buffers allowed for all PS clients
+        empty - Get current tx data pause settings
+
+    Example:
+    ./uaputl.exe tx_data_pause      : Display tx data pause settings
+    ./uaputl.exe tx_data_pause 1    : Enable pause tx event, max tx buffer number unchanged.
+    ./uaputl.exe tx_data_pause 0    : Disable pasue tx event, max tx buffer number unchanged.
+    ./uaputl.exe tx_data_pause 1 15 : Enable pause tx event, with max tx buffer number 15.
+
+vhtcfg
+	This command is used to set and get various 11ac specific configuration
+	for transmission and reception. For the SET operation, all paramaters
+	may be applied.	For the GET operation, only the first two parameters are applied.
+	The 6th argument "rx_mcs_set" can be used to disbale/enable 802.11ac.
+
+	where <j> is <band>
+	<band> - This is the band setting for the vhtcfg
+		0: Settings for both 2.4G and 5G bands (for SET operation, 11N BW only)
+		1: Settings for 2.4G band (for 11N BW only)
+		2: Settings for 5G band
+
+	where <k> is <txrx>
+	<txrx> - This parameter specifies the configuration of VHT operation for TX or/and VHT capabilities
+		3: configuration of VHT capabilities (uAP only)
+	Note: For the UAP, only 3 is supported for txrx.
+
+	where [l] is <bwcfg>
+	<bwcfg> - This parameter specifies the bandwidth (BW) configuration
+		  applied to the vhtcfg.
+	If <txrx> is 3 (For uAP),
+		0: Tx BW follows the BW (20/40 MHz) from 11N CFG
+		1: Tx BW follows the BW (80/160/80+80 MHz) from VHT Capabilities
+		   defined in <vhtcap> below for 5G band.
+
+	where [m] is <vhtcap>
+	<vhtcap> - This parameter specifies the VHT capabilities info if <txrx> is 2 (association)
+		   or the VHT Tx operations if <txrx> is 1 (Tx operations).
+		   The VHT Tx operation should be a subset of VHT capabilities for association.
+		   It is a bitmap and should be used as follows:
+
+		Bit 31-30: Reserved and set to 0
+		Bit 29:    TX antenna pattern consistency
+			   1: antenna pattern does not change
+			   0: antenna pattern might change
+		Bit 28:    RX antenna pattern consistency
+			   1: antenna pattern does not change
+			   0: antenna pattern might change
+		Bit 27-26: VHT link adaptation capable
+			   0: no feedback of VHT MFB from the STA
+			   1: unsolicted feedback of VHT MFB from the STA
+			   2: both response and unsolicted feedback of VHT MFB
+			      from the STA
+			   3: reserved and set to 0
+		Bit 25-23: Maximum A-MPDU length exponent
+		Bit 22:    +HTC-VHT capable (1: enable. 0 disable)
+		Bit 21:    VHT TXOP PS
+		Bit 20:    MU beamformee capable (1: enable. 0 disable)
+		Bit 19:    MU beamformer capable (1: enable. 0 disable)
+		Bit 18-16: Number of sounding dimensions (set to maximum-1
+			   if Bit 11 is 1. Otherwise, reserved and set to 0)
+		Bit 15-13: Compressed steering number of beamformer
+			   antennas supported (set to maximum-1 if Bit 12 is 1.
+			   Otherwise, reserved and set to 0)
+		Bit 12:    SU beamformee capable (1: enable. 0 disable)
+		Bit 11:    SU beamformer capable (1: enable. 0 disable)
+		Bit 10-8:  Rx STBC
+			   0: no support
+			   1: support of 1 spatial stream
+			   2: support of 1-2 streams
+			   3: support of 1-3 spatial streams
+			   4: support of 1-4 spatial streams
+			   5-7: reserved and set to 0
+		Bit 7:     TX STBC (1: enable. 0 disable)
+		Bit 6:     Short GI for 160 and 80+80 MHz (1: enable. 0 disable)
+		Bit 5:     Short GI for 80 MHz (1: enable. 0 disable)
+		Bit 4:     Rx LDPC (1: enable. 0 disable)
+		Bit 3-2:   Supported channel width set.
+			   0: no support of either 160 or 80+80 MHz.
+			   1: support of 160 MHz
+			   2: support of both 160 and 80+80 MHz.
+			   3: reserved and set to 0.
+		Bit 1-0:   Maximum MPDU length
+			   0: 3895 octets.
+			   1: 7991 octets.
+			   2: 11454 octets.
+			   3: reserved and set to 0.
+
+	where [n] is <tx_mcs_map>,
+	<tx_mcs_map> - This parameter specifies the TX MCS map. It may not be used for the STA if <txrx> is 1 (Tx operations).
+		It is a bitmap and should be used as following
+		Bit 15-0:  MCS map, which is defined as folows:
+			Bit 15-14: Max MCS for 8 SS
+			Bit 13-12: Max MCS for 7 SS
+			Bit 11-10: Max MCS for 6 SS
+			Bit 9-8:   Max MCS for 5 SS
+			Bit 7-6:   Max MCS for 4 SS
+			Bit 5-4:   Max MCS for 3 SS
+			Bit 3-2:   Max MCS for 2 SS
+			Bit 1-0:   Max MCS for 1 SS
+
+	where [o] is <rx_mcs_map>.
+	<rx_mcs_map> - This parameter specifies the RX MCS map. It may not be used for the STA if <txrx> is 1 (Tx operations).
+		It is a bitmap with the same sructure as for <tx_mcs_map>
+		rx_mcs_map = 0xffff : FW will disable 802.11ac
+                rx_mcs_map = others : FW will enable 802.11ac
+
+	Note: The user setting of vhtcap may be overwritten by the driver
+	      if the setting of those fields is beyond the hardware capabilities.
+
+	Examples:
+		./uaputl.exe -i uapX vhtcfg 2 3            : Get current VHT configuration in 5GHz for the uAP.
+		./uaputl.exe -i uapX vhtcfg 2 3 0 0x000001f0 0xfffa 0xfffa
+		    : Set the current/maximum VHT configuration in 5GHz for the uAP.
+		      Both Tx and Rx supports MCS 0-9 for both 1 and 2 spatial streams.
+		./uaputl.exe -i uapX vhtcfg 2 3 0 0x000001b0
+		    : Set the VHT capability information in 5GHz for the uAP, and keep the Tx/Rx MCS Map same as before.
+
+dfstesting
+----------
+  "./uaputl.exe dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>]"
+
+    This command is used to set/get DFS testing settings.
+
+    The supported option are:
+        <user_cac_pd>: user-configured Channel Availability Check in msec
+                       0 = disable, use default period (60000)
+                       1-65535 = enable with that period
+        <user_nop_pd>: user-configured Non-Occupancy Period in sec
+                       0 = disable, use default period (1800)
+                       1-65535 = enable with that period
+        <no_chan_change>: enable/disable no channel change on radar
+                          0 = disable, 1 = enable (overrides below)
+        <fixed_chan_num>: user-configured channel to change to on radar
+                          0 = disable, 1-255 = enable with that channel
+                          (channel validity for region, etc. is not checked)
+                          (only takes effect if no_chan_change = 0)
+
+    Example:
+    ./uaputl.exe dfstesting             : Get current dfstesting settings
+    ./uaputl.exe dfstesting 2000 0 0 0  : user_cac=2sec, others disabled/default
+    ./uaputl.exe dfstesting 0 0 1 0     : only no_chan_change enabled
+    ./uaputl.exe dfstesting 0 120 0 64  : user_nop=2min, force chan 64 on radar
+
+cscount
+---------------
+
+  "./uaputl.exe cscount [<channel_switch_count>]"
+
+    This command is used to configure the number of beacons AP sends with channel switch IE, before channel change due to
+    radar detection. If not configured, <channel_switch_count> is set to 5 by default.
+
+    The supported options are:
+        <channel_switch_count>: user configured channel switch count
+                                5-20 = configure with that value
+
+   Example:
+   ./uaputl.exe cscount         : Get user configured channel switch count value
+   ./uaputl.exe cscount 10      : Set channel switch count to 10
+
+
+mgmtframectrl
+-------------
+  "./uaputl.exe mgmtframectrl [MASK]"
+
+    This command is used to set/get management frame control mask.
+
+    where the parameter [MASK] is the bit mask of management frame reception.
+        Following are the bit definitions.
+        Bit 0 : Association Request
+        Bit 1 : Association Response
+        Bit 2 : Re-Association Request
+        Bit 3 : Re-Association Response
+        Bit 4 : Probe Request
+        Bit 5 : Probe Response
+        Bit 8 : Beacon Frames
+
+    Example:
+    ./uaputl.exe mgmtframectrl 0x101
+    Set management frame control mask to 0x101.
+
+restrict_client_mode
+--------------------
+	"./uaputl.exe restrict_client_mode [<ENABLE> [MODE_CONFIG]]"
+
+	This command is used to set/get the mode in which the ex-Stations can connect to the uAP.
+	If no arguments are given, this command returns the current configuration.
+	By default this feature will be disabled.
+	[ENABLE]:
+		1: Enable the feature.
+		0: Disable the feature.
+	[MODE_CONFIG]: config mode.
+		Bit 0:	B only Mode.
+		Bit 1:	A only Mode.
+		Bit 2:	G only Mode.
+		Bit 3:	N only Mode.
+		Bit 4:	AC only Mode.
+
+	Example:
+	./uaputl.exe restrict_client_mode 1 0x8.
+	Only N mode clients will be able to connect. Association will fail for other clients.
+
+	./uaputl.exe restrict_client_mode 0.
+	This feature is disabled.
+
+	Note:
+	The set operation should be performed before bss is started.
+	User should make sure that the mode advertized by uAP in beacons
+	(i.e. combination of supported/extended rates IE and capability IEs)
+	is superset of the mode allowed for ex-STA associations using this TLV.
+
+uap_oper_ctrl
+-------------
+	"./uaputl.exe uap_oper_ctrl [control] [chanopt] [bandcfg] [channel]"
+
+	This command is used to set/get uAP operation control when in-STA disconnected with ext-AP.
+	If no arguments are given, this command returns the current configuration.
+
+	The supported options are:
+		<control> : 0  default, do nothing
+				   2  uAP stops and restart automatically
+		<chanopt> Specify which channel should be used when uap restarts automatically
+				   1: uap restarts on default 2.4G/channel 6
+				   2: uap restart on band/channel configured by driver previously
+				   3: uap restart on band/channel configured by parameter bandcfg/channel
+		<bandcfg> This parameter specifies the bandwidth when chanopt is 3
+				   0: 20Mhz
+				   2: 40Mhz
+				   3: 80Mhz
+		<channel> This parameter specifies the channel will be used when chanopt is 3
+
+		Example:
+		./uaputl.exe uap_oper_ctrl  2 1
+		uap stops and restart automatically when in-STA disconnects with ext-AP,
+		and restart on default 2.4G/channel 6.
+
+		./uaputl.exe uap_oper_ctrl  2 2
+		uap stops and restart automatically when in-STA disconnects with ext-AP,
+		and restart on band/channel configured by driver previously.
+
+		./uaputl.exe uap_oper_ctrl  2 3 2 36
+		uap stops and restart automatically when in-STA disconnects with ext-AP,
+		and restart on channel 36, bandwidth 40.
+
+		./uaputl.exe uap_oper_ctrl  0
+		when in-STA disconnects with ext-AP, uap will stay on current operation channel.
+
+		./uaputl.exe uap_oper_ctrl
+		Get current uap operation control setting.
+===============================================================================
+        U S E R  M A N U A L  F O R  MLANEVENT
+
+NAME
+mlanevent.exe
+
+This tool can be used to listen for and obtain events from the uAP driver
+through the netlink layer.
+
+----------------
+Supported events
+----------------
+STA_DEAUTH
+STA_ASSOC
+BSS_START
+BSS_IDLE
+BSS_ACTIVE
+
+-----------------
+Details of events
+-----------------
+
+STA_DEAUTH
+----------
+    For this event, the following information is shown:
+        + Deauthenticated STA MAC address.
+        + Reason for deauthentication.
+
+STA_ASSOC
+---------
+    For this event, the following information is shown:
+        + STA MAC address.
+
+BSS_START
+---------
+    For this event, the following information is shown:
+        + AP MAC address.
+
+BSS_IDLE
+--------
+    For this event, there is no associated information.
+
+BSS_ACTIVE
+----------
+    For this event, there is no associated information.
+
+===============================================================================
+        U S E R  M A N U A L  F O R  IWPRIV
+
+NAME
+	This manual describes the usage of private commands used in Marvell MLAN
+	Linux UAP Driver.
+
+	To use parameters as hex format, a '0x' must precede it for the parameters to
+	be parsed properly.
+
+SYNOPSIS
+	iwpriv <uapX> <command> [sub-command] ...
+
+	iwpriv uapX version
+	iwpriv uapX verext
+	iwpriv uapX start
+	iwpriv uapX stop
+	iwpriv uapX bssstart
+	iwpriv uapX bssstop
+	iwpriv uapX fwreload <path>
+	iwpriv uapX apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP,[SEC,][KEY,]
+	                   [CHANNEL,][PREAMBLE,][MAX_SCB,][END]"
+
+DESCRIPTION
+	Those commands are used to send additional commands to the Marvell MLAN
+	card via the Linux device driver.
+
+	The uapX parameter specifies the network device that is to be used to
+	perform this command on. It could be uap0, uap1 etc.
+
+version
+	This is used to get the current version of the driver and the firmware.
+
+verext
+	Retrieve and display an extended version string from the firmware
+
+	Usage:
+		iwpriv uapX verext [#]
+
+	where [#] is an optional argument to retrieve a specific version string,
+	omission of the argument retrieves the 0 indexed string.
+
+start
+	Start the uAP driver.
+
+	Usage:
+		iwpriv uapX start
+
+stop
+	Stop the uAP driver.
+
+	Usage:
+		iwpriv uapX stop
+
+bssstart
+	Start the AP mode, so that uAP will start transmitting beacon.
+
+	Usage:
+		iwpriv uapX bssstart
+
+bssstop
+	Stop the AP mode and disconnect all stations. uAP will stop
+	transmitting beacon as well.
+
+	Usage:
+		iwpriv uapX bssstop
+
+fwreload
+	Reload the firmware. Here string "FW_PATH=" in the path
+	argument is mandatory part.
+
+	Usage:
+		iwpriv uapX fwreload <path>
+
+apcfg
+	This command is used to set the AP configurations. Here string
+	"ASCII_CMD=AP_CFG" is minimum requirement in the ASCII string.
+	Note: BSS will be stopped then restarted if BSS is already started
+	when the command is received.
+
+	Usage:
+		iwpriv uapX apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP,[SEC=sec,]
+			[KEY=key,][CHANNEL=channel,][MAX_SCB=max_scb,][END]"
+
+	Where the parameters are:
+		SSID:       Set SSID to be used in beacon and probe response
+		[SEC]:      Security modes - open, wep128, wpa-psk or wpa2-psk
+		            11n will be auto disabled in wep128 and wpa-psk mode
+		[KEY]:      Encrypted key for wep128, wpa-psk or wpa2-psk, minimum 8 chars
+		[CHANNEL]:  Channel to be selected
+		[MAX_SCB]:  Maximum STA number
+		[END]:      Optional termination in ASCII string
+
+	Examples:
+		iwpriv uap0 apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP"
+                                        : Set AP SSID to "TEST_uAP"
+		iwpriv uap0 apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP,SEC=open"
+                                        : Set AP SSID to "TEST_uAP" and
+                                          security mode is disabled
+		iwpriv uap0 apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP,SEC=WPA2-PSK,KEY=ecbe5facdbfe234a"
+                                        : Set AP SSID to "TEST_uAP" and security mode
+                                          to WPA2-SPK and encrypted key ecbe5facdbfe234a
+		iwpriv uap0 apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP,CHANNEL=8"
+                                        : Set AP SSID to "TEST_uAP" and
+                                          set the AP channel to 8
+
+===============================================================================
diff --git a/wlan_sd8897/README_WIFIDIRECT b/wlan_sd8897/README_WIFIDIRECT
new file mode 100644
index 0000000..b56602a
--- /dev/null
+++ b/wlan_sd8897/README_WIFIDIRECT
@@ -0,0 +1,448 @@
+
+===============================================================================
+
+        S E T U P  I N S T R U C T I O N S F O R  *WifiDirect*
+
+Driver,FW release:
+
+1. Make sure, bg_scan.conf,uaputl.conf has SSID starting with "DIRECT-"
+2. Download uAP configuration and BG scan configuration.
+3. This version of wifidirectutl breaks the backward compatibility and will work
+with following releases -
+    8797 >= 14.xx.16.p15
+         >= 14.xx.11.p138
+    8766 >= 14.xx.11.p138
+    8787 >= 14.xx.9.p89
+
+    However, In case one needs to run the utility in backward compatibility mode
+    he can do so by running following command before running the utility -
+
+    # export WIFIDIR_USE_FIXED_IE_INDICES=1
+
+    To disable backward comaptibility mode run following command -
+
+    # export WIFIDIR_USE_FIXED_IE_INDICES=0
+
+WPSWIFIDIRECT release:
+
+1. Modify the wifidirect.conf file to specify the correct HW addresses of
+   devices. The DUT mac address and peer mac address needs to be correctly
+   entered.
+
+===============================================================================
+        U S E R  M A N U A L  F O R  WIFIDIRECTUTL
+
+NAME
+	wifidirectutl
+
+This tool can be used to configure WifiDirect parameters.
+
+------------------
+Supported Commands
+------------------
+wifidirect_mode [MODE]
+wifidirect_config [*.conf]
+wifidirect_params_config [*.conf]
+wifidirect_action_frame <*.conf> | [<PeerAddr> <Category> <OuiSubtype> <DialogToken>]
+wifidirect_discovery_request <*.conf>
+wifidirect_discovery_response <*.conf>
+
+wifidirect_cfg_discovery_period [<MinDiscPeriod> <MaxDiscPeriod>]
+wifidirect_cfg_intent [IntentValue]
+wifidirect_cfg_capability [<DeviceCapability> <GroupCapability>]
+wifidirect_cfg_noa <enable|disable> <index> [<counttype> <duration> <interval>]
+wifidirect_cfg_opp_ps <enable|disable> [<CTWindow>]
+wifidirect_cfg_invitation_list [mac_addr]
+wifidirect_cfg_listen_channel [ListenChannel]
+wifidirect_cfg_op_channel [OperatingChannel]
+wifidirect_cfg_persistent_group_record [index] [role]
+           [<groupbss> <deviceId> <ssid> <psk>] [peermac1] [peermac2]
+wifidirect_cfg_persistent_group_invoke [index] | <cancel>
+wifidirect_cfg_presence_req_params [<type> <duration> <interval>]
+wifidirect_cfg_ext_listen_time [<duration> <interval>]
+wifidirect_cfg_provisioning_params [<action> <configMethods> <devicePassword>]
+wifidirect_cfg_wps_params [<action>]
+
+wifidirect_mode [mode]
+----------
+    "./wifidirectutl <iface> wifidirect_mode [mode]"
+
+    This command is used to setup various modes for wifidirect device.
+    The mode 2 can be used only when wifidirect is started using mode 1.
+    The mode 3 should not be used for uAP.
+
+    The supported options are:
+        mode :     0 - stop wifidirect mode
+                   1 - start wifidirect mode
+                   2 - start wifidirect group owner mode
+                   3 - start wifidirect client mode
+                   4 - start wifidirect find phase
+                   5 - stop wifidirect find phase
+        empty - Get current wifidirect mode
+
+    Example:
+        ./wifidirectutl <iface> wifidirect_mode 0
+                Stop wifidirect mode.
+
+        ./wifidirectutl <iface> wifidirect_mode 1
+                Start wifidirect mode.
+
+        ./wifidirectutl <iface> wifidirect_mode
+                Get current WIFIDIRECT start/stop mode.
+
+wifidirect_config
+----------
+    "./wifidirectutl <iface> wifidirect_config [*.conf]"
+
+    This command is used to set/get the wifidirect configuration.
+
+        Example:
+        ./wifidirectutl <iface> wifidirect_config wifidirect.conf
+            Read configuration from wifidirect.conf and apply it.
+        ./wifidirectutl <iface> wifidirect_config
+            Read existing wifidirect configuration and display it.
+
+wifidirect_params_config
+----------
+    "./wifidirectutl <iface> wifidirect_params_config [*.conf]"
+
+    This command is used to set/get the wifidirect parameters configuration.
+
+        Example:
+        ./wifidirectutl <iface> wifidirect_params_config wifidirect.conf
+            Read parameter configuration from wifidirect.conf and apply it.
+        ./wifidirectutl <iface> wifidirect_params_config
+            Read existing wifidirect parameters's configuration and display it.
+
+wifidirect_action_frame
+----------
+    "./wifidirectutl <iface> wifidirect_action_frame <*.conf> | <PeerAddr> <Category> <OUISubtype> <DialogToken>"
+
+    This command is used to send action frames as specified in config file or on command line.
+
+        Example:
+        ./wifidirectutl <iface> wifidirect_action_frame wifidirect.conf
+            Read action_frame from wifidirect.conf and send to peer.
+        ./wifidirectutl <iface> wifidirect_action_frame <PeerAddr> <Category> <OUISubtype> <DialogToken>
+            Read action frame parameters from command line and send to peer.
+
+wifidirect_discovery_request
+----------
+    "./wifidirectutl <iface> wifidirect_discovery_request <*.conf>"
+
+    This command is used to send wifidirect discovery request packet.
+
+        Example:
+        ./wifidirectutl <iface> wifidirect_discovery_request wifidirect.conf
+            Read discovery packet from wifidirect.conf and send to peer.
+
+wifidirect_discovery_response
+----------
+    "./wifidirectutl <iface> wifidirect_discovery_response <*.conf>"
+
+    This command is used to send wifidirect discovery response packet.
+
+        Example:
+        ./wifidirectutl <iface> wifidirect_discovery_response wifidirect.conf
+            Read discovery packet from wifidirect.conf and send to peer.
+
+wifidirect_cfg_discovery_period
+----------
+	"./wifidirectutl <iface> wifidirect_cfg_discovery_period [<MinDiscPeriod> <MaxDiscPeriod>]"
+
+	This command is used to set or get minimum and maximum discovery period.
+
+		Example:
+		./wifidirectutl <iface> wifidirect_cfg_discovery_period 10 20
+		Set minimum discovery interval to 10 and maximum discovery
+		interval to	20.
+
+		./wifidirectutl <iface> wifidirect_cfg_discovery_period
+		Get minimum and maximum discovery interval.
+
+wifidirect_cfg_intent
+----------
+	"./wifidirectutl <iface> wifidirect_cfg_intent [IntentValue]"
+
+	This command is used to set or get intent value.
+
+		Example:
+		./wifidirectutl <iface> wifidirect_cfg_intent 12
+		Set intent value to 12.
+
+		./wifidirectutl <iface> wifidirect_cfg_intent
+		Get Group Owner Intent.
+
+wifidirect_cfg_capability
+----------
+	"./wifidirectutl <iface> wifidirect_cfg_capability [<DeviceCapability> <GroupCapability>]"
+
+	This command is used to set or get device capability and group capability.
+
+		Example:
+		./wifidirectutl <iface> wifidirect_cfg_capability 10 20
+		Set Device capability to 10 and group capability to 20
+
+		./wifidirectutl <iface> wifidirect_cfg_capability
+		Get Device capability and group capability.
+
+wifidirect_cfg_noa
+----------
+	"./wifidirectutl <iface> wifidirect_cfg_noa <enable|disable> <index> [<counttype> <duration> <interval>]"
+
+	This command is used to set or get NoA parameters like count_type, duration and
+	interval in ms when NoA is enabled. Valid value of index is [0, 1].
+
+		Example:
+		./wifidirectutl <iface> wifidirect_cfg_noa enable 0 10 50 100
+		Enable NoA and set count to 10, duration to 50ms and interval to 100 ms
+            for index 0.
+
+		./wifidirectutl <iface> wifidirect_cfg_noa disable 1
+		Disable NoA at index 1.
+
+		./wifidirectutl <iface> wifidirect_cfg_noa
+		Get NoA settings.
+
+wifidirect_cfg_opp_ps
+----------
+	"./wifidirectutl <iface> wifidirect_cfg_opp_ps <enable|disable> [<CTWindow>]"
+
+	This command is used to set or get Opportunistic power save and CT window.
+
+		Example:
+		./wifidirectutl <iface> wifidirect_cfg_opp_ps enable 50
+		Set OppPS and CTwindow value to 50.
+
+		./wifidirectutl <iface> wifidirect_cfg_opp_ps disable
+		Disable OppPS.
+
+		./wifidirectutl <iface> wifidirect_cfg_opp_ps
+		Get OppPS and CT window.
+
+wifidirect_cfg_invitation_list
+----------
+	"./wifidirectutl <iface> wifidirect_cfg_invitation_list [mac_addr]"
+
+	This command is used to set or get invitation list of peers.
+
+		Example:
+		./wifidirectutl <iface> wifidirect_cfg_invitation_list 00:50:43:20:23:34
+		Set 00:50:43:20:23:34 in invitation list of peers.
+
+		./wifidirectutl <iface> wifidirect_cfg_invitation_list
+		Get Invitation list of peers.
+
+wifidirect_cfg_listen_channel
+----------
+	"./wifidirectutl <iface> wifidirect_cfg_listen_channel [ListenChannel]"
+
+	This command is used to set or get Listen channel.
+
+		Example:
+		./wifidirectutl <iface> wifidirect_cfg_listen_channel 11
+		Set Listen channel to 11.
+
+		./wifidirectutl <iface> wifidirect_cfg_listen_channel
+		Get Listen channel.
+
+wifidirect_cfg_op_channel
+----------
+		"./wifidirectutl <iface> wifidirect_cfg_op_channel [OperatingChannel]"
+
+		This command is used to set or get Operating channel.
+
+		Example:
+		./wifidirectutl <iface> wifidirect_cfg_op_channel 11
+		Set Operating channel to 11.
+
+		./wifidirectutl <iface> wifidirect_cfg_op_channel
+		Get Operating channel.
+
+wifidirect_cfg_persistent_group_record
+----------
+		"./wifidirectutl <iface> [index] [role]
+                 [<groupbss> <deviceId> <ssid> <psk>] [peermac1] [peermac2]"
+
+		This command is used to set or get the persistent group record
+        maintained in the device. Valid index is from 0 to 3. The role should be
+        0 for client, 1 for group-owner.
+
+		Example:
+		./wifidirectutl <iface> wifidirect_cfg_persistent_group_record 0 1
+        00:50:43:12:24:36 00:50:43:13:26:39 "DIRECT-" "1234567890"
+        00:50:43:20:c2:d0
+		Set persistent group record with index 0, role as group owner, bssId and
+        device Id, ssid="DIRECT-", passphrase="1234567890", and peer mac address.
+        The passphrase get converted to PSK.
+
+		./wifidirectutl <iface> wifidirect_cfg_persistent_group_record 1 1
+        00:50:43:12:24:36 00:50:43:13:26:39 "DIRECT-" "1234567890"
+        0x1234567890123456789012345678901234567890123456789012345678901234
+		Set persistent group record with index 1, role as group owner, bssId and
+        device Id, ssid="DIRECT-", passphrase="1234567890", and peer mac address.
+        The passphrase get converted to PSK.
+        PSK is specified with "0x" prefix and 32 bytes (64 characters).
+
+		./wifidirectutl <iface> wifidirect_cfg_persistent_group_record 1
+		Get persistent group record with index 1.
+
+		./wifidirectutl <iface> wifidirect_cfg_persistent_group_record
+		Get persistent group record for all indices.
+
+wifidirect_cfg_persistent_group_invoke
+----------
+		"./wifidirectutl <iface> wifidirect_cfg_persistent_group_invoke [index] | <cancel>"
+
+		This command is used to invoke a particular persistent group record
+        from the list. Valid index is from 0 to 3.
+
+		./wifidirectutl <iface> wifidirect_cfg_persistent_group_invoke 2
+		Invoke persistent group record with index 1.
+
+		./wifidirectutl <iface> wifidirect_cfg_persistent_group_invoke cancel
+		Cancel invokation of persistent groups.
+
+wifidirect_cfg_presence_req_params
+----------
+		"./wifidirectutl <iface> wifidirect_cfg_presence_req_params [<type> <duration> <interval>]"
+
+		This command is used to set/get presence request parameters. Type should
+        be 1 for preferred values and 2 for acceptable values.
+
+		./wifidirectutl <iface> wifidirect_cfg_presence_req_params 1 50 100
+		Set duration to 50ms and interval to 100ms.
+
+		./wifidirectutl <iface> wifidirect_cfg_presence_req_params
+		Get presence request parameters.
+
+wifidirect_cfg_ext_listen_time
+----------
+		"./wifidirectutl <iface> wifidirect_cfg_ext_listen_time [<duration> <interval>]"
+
+		This command is used to set/get extended listen timing parameters.
+
+		./wifidirectutl <iface> wifidirect_cfg_ext_listen_time 1200 1300
+		Set availability period to 1200ms and availability interval to 1300 ms.
+
+		./wifidirectutl <iface> wifidirect_cfg_ext_listen_time
+		Get extended listen timing parameters.
+
+wifidirect_cfg_provisioning_params
+----------
+		"./wifidirectutl <iface> wifidirect_cfg_provisioning_params [<action> <configMethod> <devicePassword>]"
+
+		This command is used to set/get provisioning protocol parameters. Action should
+        be 1 for request parameters and 2 for response parameters.
+
+		./wifidirectutl <iface> wifidirect_cfg_provisioning_params 1 0x80 0x04
+		Set config method to 0x86 and device password to 0x04.
+
+		./wifidirectutl <iface> wifidirect_cfg_provisioning_params
+		Get Provision protocol parameters.
+
+wifidirect_cfg_wps_params
+----------
+		"./wifidirectutl <iface> wifidirect_cfg_wps_params [<action>]"
+
+		This command is used to set WPS action. action can be "pin" (pin entered),
+        "pbc"(button pressed) or "none".
+
+		Example:
+		./wifidirectutl <iface> wifidirect_cfg_wps_params pin
+		Indicates pin operation
+
+		./wifidirectutl <iface> wifidirect_cfg_wps_params none
+		Indicates no operation
+
+===============================================================================
+        U S E R  M A N U A L  F O R  WIFIDISPLAY
+
+wifidisplay_mode
+-----------
+            "./wifidirectutl <iface> wifidisplay_mode [<action>]"
+            This command is used to enable or disable wifi-display. The possible values are either 1 or 0.
+		Example:
+		./wifidirectutl <iface> wifidisplay_mode 1
+		Indicates enable wifi_display
+
+		./wifidirectutl <iface> wifidisplay_mode 0
+		Indicates disable wifi_display
+
+wifidisplay_config
+-----------
+            "./wifidirectutl <iface> wifidisplay_config [*.conf]"
+
+            This command is used to set/get the wifidisplay configuration.
+
+             Example:
+             ./wifidirectutl <iface> wifidisplay_config wifidisplay.conf
+             Read configuration from wifidisplay.conf and apply it.
+             ./wifidirectutl <iface> wifidisplay_config
+             Read existing wifidisplay configuration and display it.
+
+wifidisplay_update_devinfo
+----------
+            "./wifidirectutl <iface> wifidisplay_update_devinfo [value]"
+
+            This command is used to set the device information of wifidisplay device information subelement.
+            This command will overwrite the new device information with user defined value.
+
+             Example:
+             ./wifidirectutl <iface> wifidisplay_update_devinfo 10
+             Update device information programmed with new value 10 by overwritting existing value.
+
+wifidisplay_discovery_request
+----------
+            "./wifidirectutl <iface> wifidisplay_discovery_request [*.config]"
+
+            This command is used to send wifi display service discovery request.
+
+            Example:
+            ./wifidirectutl <iface> wifidisplay_discovery_request wifidisplay.conf
+            Read discovery packet from wifidisplay.conf and send to peer.
+
+wifidisplay_discovery_response
+----------
+           "./wifidirectutl <iface> wifidisplay_discovery_response <*.conf>"
+
+           This command is used to send wifidisplay discovery response packet.
+
+           Example:
+           ./wifidirectutl <iface> wifidisplay_discovery_response wifidisplay.conf
+           Read discovery packet from wifidisplay.conf and send to peer.
+
+===============================================================================
+        U S E R  M A N U A L  F O R  MLANEVENT
+
+NAME
+mlanevent.exe
+
+This tool can be used to listen for and obtain events from the driver
+through the netlink layer. This is only used for display/debugging purpose.
+
+----------------
+Supported events
+----------------
+WIFIDIRECT_GENERIC_EVENT
+WIFIDIRECT_SERVICE_DISCOVERY
+
+-----------------
+Details of events
+-----------------
+
+WIFIDIRECT_GENERIC_EVENT
+-----------------
+    For this event, the following information is shown:
+        + Event length.
+        + Event Type indicating Negociation, Invitation, Discoverability,
+		Provision discovery related Request or Response.
+        + Event SubType indicating Client or Group owner role.
+        + Peer MAC address.
+        + Associated WIFIDIRECT IE elements.
+
+WIFIDIRECT_SERVICE_DISCOVERY
+---------------------
+    For this event, the following information is shown:
+        + Peer MAC address.
+        + Service discovery packet details.
diff --git a/wlan_sd8897/gpl-2.0.txt b/wlan_sd8897/gpl-2.0.txt
new file mode 100644
index 0000000..2c62266
--- /dev/null
+++ b/wlan_sd8897/gpl-2.0.txt
@@ -0,0 +1,339 @@
+              GNU GENERAL PUBLIC LICENSE
+ 		       Version 2, June 1991
+
+  Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+  Everyone is permitted to copy and distribute verbatim copies
+  of this license document, but changing it is not allowed.
+
+ 			    Preamble
+
+   The licenses for most software are designed to take away your
+ freedom to share and change it.  By contrast, the GNU General Public
+ License is intended to guarantee your freedom to share and change free
+ software--to make sure the software is free for all its users.  This
+ General Public License applies to most of the Free Software
+ Foundation's software and to any other program whose authors commit to
+ using it.  (Some other Free Software Foundation software is covered by
+ the GNU Lesser General Public License instead.)  You can apply it to
+ your programs, too.
+
+   When we speak of free software, we are referring to freedom, not
+ price.  Our General Public Licenses are designed to make sure that you
+ have the freedom to distribute copies of free software (and charge for
+ this service if you wish), that you receive source code or can get it
+ if you want it, that you can change the software or use pieces of it
+ in new free programs; and that you know you can do these things.
+
+   To protect your rights, we need to make restrictions that forbid
+ anyone to deny you these rights or to ask you to surrender the rights.
+ These restrictions translate to certain responsibilities for you if you
+ distribute copies of the software, or if you modify it.
+
+   For example, if you distribute copies of such a program, whether
+ gratis or for a fee, you must give the recipients all the rights that
+ you have.  You must make sure that they, too, receive or can get the
+ source code.  And you must show them these terms so they know their
+ rights.
+
+   We protect your rights with two steps: (1) copyright the software, and
+ (2) offer you this license which gives you legal permission to copy,
+ distribute and/or modify the software.
+
+   Also, for each author's protection and ours, we want to make certain
+ that everyone understands that there is no warranty for this free
+ software.  If the software is modified by someone else and passed on, we
+ want its recipients to know that what they have is not the original, so
+ that any problems introduced by others will not reflect on the original
+ authors' reputations.
+
+   Finally, any free program is threatened constantly by software
+ patents.  We wish to avoid the danger that redistributors of a free
+ program will individually obtain patent licenses, in effect making the
+ program proprietary.  To prevent this, we have made it clear that any
+ patent must be licensed for everyone's free use or not licensed at all.
+
+   The precise terms and conditions for copying, distribution and
+ modification follow.
+
+ 		    GNU GENERAL PUBLIC LICENSE
+    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+   0. This License applies to any program or other work which contains
+ a notice placed by the copyright holder saying it may be distributed
+ under the terms of this General Public License.  The "Program", below,
+ refers to any such program or work, and a "work based on the Program"
+ means either the Program or any derivative work under copyright law:
+ that is to say, a work containing the Program or a portion of it,
+ either verbatim or with modifications and/or translated into another
+ language.  (Hereinafter, translation is included without limitation in
+ the term "modification".)  Each licensee is addressed as "you".
+
+ Activities other than copying, distribution and modification are not
+ covered by this License; they are outside its scope.  The act of
+ running the Program is not restricted, and the output from the Program
+ is covered only if its contents constitute a work based on the
+ Program (independent of having been made by running the Program).
+ Whether that is true depends on what the Program does.
+
+   1. You may copy and distribute verbatim copies of the Program's
+ source code as you receive it, in any medium, provided that you
+ conspicuously and appropriately publish on each copy an appropriate
+ copyright notice and disclaimer of warranty; keep intact all the
+ notices that refer to this License and to the absence of any warranty;
+ and give any other recipients of the Program a copy of this License
+ along with the Program.
+
+ You may charge a fee for the physical act of transferring a copy, and
+ you may at your option offer warranty protection in exchange for a fee.
+
+   2. You may modify your copy or copies of the Program or any portion
+ of it, thus forming a work based on the Program, and copy and
+ distribute such modifications or work under the terms of Section 1
+ above, provided that you also meet all of these conditions:
+
+     a) You must cause the modified files to carry prominent notices
+     stating that you changed the files and the date of any change.
+
+     b) You must cause any work that you distribute or publish, that in
+     whole or in part contains or is derived from the Program or any
+     part thereof, to be licensed as a whole at no charge to all third
+     parties under the terms of this License.
+
+     c) If the modified program normally reads commands interactively
+     when run, you must cause it, when started running for such
+     interactive use in the most ordinary way, to print or display an
+     announcement including an appropriate copyright notice and a
+     notice that there is no warranty (or else, saying that you provide
+     a warranty) and that users may redistribute the program under
+     these conditions, and telling the user how to view a copy of this
+     License.  (Exception: if the Program itself is interactive but
+     does not normally print such an announcement, your work based on
+     the Program is not required to print an announcement.)
+
+ These requirements apply to the modified work as a whole.  If
+ identifiable sections of that work are not derived from the Program,
+ and can be reasonably considered independent and separate works in
+ themselves, then this License, and its terms, do not apply to those
+ sections when you distribute them as separate works.  But when you
+ distribute the same sections as part of a whole which is a work based
+ on the Program, the distribution of the whole must be on the terms of
+ this License, whose permissions for other licensees extend to the
+ entire whole, and thus to each and every part regardless of who wrote it.
+
+ Thus, it is not the intent of this section to claim rights or contest
+ your rights to work written entirely by you; rather, the intent is to
+ exercise the right to control the distribution of derivative or
+ collective works based on the Program.
+
+ In addition, mere aggregation of another work not based on the Program
+ with the Program (or with a work based on the Program) on a volume of
+ a storage or distribution medium does not bring the other work under
+ the scope of this License.
+
+   3. You may copy and distribute the Program (or a work based on it,
+ under Section 2) in object code or executable form under the terms of
+ Sections 1 and 2 above provided that you also do one of the following:
+
+     a) Accompany it with the complete corresponding machine-readable
+     source code, which must be distributed under the terms of Sections
+     1 and 2 above on a medium customarily used for software interchange; or,
+
+     b) Accompany it with a written offer, valid for at least three
+     years, to give any third party, for a charge no more than your
+     cost of physically performing source distribution, a complete
+     machine-readable copy of the corresponding source code, to be
+     distributed under the terms of Sections 1 and 2 above on a medium
+     customarily used for software interchange; or,
+
+     c) Accompany it with the information you received as to the offer
+     to distribute corresponding source code.  (This alternative is
+     allowed only for noncommercial distribution and only if you
+     received the program in object code or executable form with such
+     an offer, in accord with Subsection b above.)
+
+ The source code for a work means the preferred form of the work for
+ making modifications to it.  For an executable work, complete source
+ code means all the source code for all modules it contains, plus any
+ associated interface definition files, plus the scripts used to
+ control compilation and installation of the executable.  However, as a
+ special exception, the source code distributed need not include
+ anything that is normally distributed (in either source or binary
+ form) with the major components (compiler, kernel, and so on) of the
+ operating system on which the executable runs, unless that component
+ itself accompanies the executable.
+
+ If distribution of executable or object code is made by offering
+ access to copy from a designated place, then offering equivalent
+ access to copy the source code from the same place counts as
+ distribution of the source code, even though third parties are not
+ compelled to copy the source along with the object code.
+
+   4. You may not copy, modify, sublicense, or distribute the Program
+ except as expressly provided under this License.  Any attempt
+ otherwise to copy, modify, sublicense or distribute the Program is
+ void, and will automatically terminate your rights under this License.
+ However, parties who have received copies, or rights, from you under
+ this License will not have their licenses terminated so long as such
+ parties remain in full compliance.
+
+   5. You are not required to accept this License, since you have not
+ signed it.  However, nothing else grants you permission to modify or
+ distribute the Program or its derivative works.  These actions are
+ prohibited by law if you do not accept this License.  Therefore, by
+ modifying or distributing the Program (or any work based on the
+ Program), you indicate your acceptance of this License to do so, and
+ all its terms and conditions for copying, distributing or modifying
+ the Program or works based on it.
+
+   6. Each time you redistribute the Program (or any work based on the
+ Program), the recipient automatically receives a license from the
+ original licensor to copy, distribute or modify the Program subject to
+ these terms and conditions.  You may not impose any further
+ restrictions on the recipients' exercise of the rights granted herein.
+ You are not responsible for enforcing compliance by third parties to
+ this License.
+
+   7. If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License.  If you cannot
+ distribute so as to satisfy simultaneously your obligations under this
+ License and any other pertinent obligations, then as a consequence you
+ may not distribute the Program at all.  For example, if a patent
+ license would not permit royalty-free redistribution of the Program by
+ all those who receive copies directly or indirectly through you, then
+ the only way you could satisfy both it and this License would be to
+ refrain entirely from distribution of the Program.
+
+ If any portion of this section is held invalid or unenforceable under
+ any particular circumstance, the balance of the section is intended to
+ apply and the section as a whole is intended to apply in other
+ circumstances.
+
+ It is not the purpose of this section to induce you to infringe any
+ patents or other property right claims or to contest validity of any
+ such claims; this section has the sole purpose of protecting the
+ integrity of the free software distribution system, which is
+ implemented by public license practices.  Many people have made
+ generous contributions to the wide range of software distributed
+ through that system in reliance on consistent application of that
+ system; it is up to the author/donor to decide if he or she is willing
+ to distribute software through any other system and a licensee cannot
+ impose that choice.
+
+ This section is intended to make thoroughly clear what is believed to
+ be a consequence of the rest of this License.
+
+   8. If the distribution and/or use of the Program is restricted in
+ certain countries either by patents or by copyrighted interfaces, the
+ original copyright holder who places the Program under this License
+ may add an explicit geographical distribution limitation excluding
+ those countries, so that distribution is permitted only in or among
+ countries not thus excluded.  In such case, this License incorporates
+ the limitation as if written in the body of this License.
+
+   9. The Free Software Foundation may publish revised and/or new versions
+ of the General Public License from time to time.  Such new versions will
+ be similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+
+ Each version is given a distinguishing version number.  If the Program
+ specifies a version number of this License which applies to it and "any
+ later version", you have the option of following the terms and conditions
+ either of that version or of any later version published by the Free
+ Software Foundation.  If the Program does not specify a version number of
+ this License, you may choose any version ever published by the Free Software
+ Foundation.
+
+   10. If you wish to incorporate parts of the Program into other free
+ programs whose distribution conditions are different, write to the author
+ to ask for permission.  For software which is copyrighted by the Free
+ Software Foundation, write to the Free Software Foundation; we sometimes
+ make exceptions for this.  Our decision will be guided by the two goals
+ of preserving the free status of all derivatives of our free software and
+ of promoting the sharing and reuse of software generally.
+
+ 			    NO WARRANTY
+
+   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+ REPAIR OR CORRECTION.
+
+   12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGES.
+
+ 		     END OF TERMS AND CONDITIONS
+
+ 	    How to Apply These Terms to Your New Programs
+
+   If you develop a new program, and you want it to be of the greatest
+ possible use to the public, the best way to achieve this is to make it
+ free software which everyone can redistribute and change under these terms.
+
+   To do so, attach the following notices to the program.  It is safest
+ to attach them to the start of each source file to most effectively
+ convey the exclusion of warranty; and each file should have at least
+ the "copyright" line and a pointer to where the full notice is found.
+
+     <one line to give the program's name and a brief idea of what it does.>
+     Copyright (C) <year>  <name of author>
+
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+     (at your option) any later version.
+
+     This program is distributed in the hope that it will be useful,
+     but WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+     GNU General Public License for more details.
+
+     You should have received a copy of the GNU General Public License along
+     with this program; if not, write to the Free Software Foundation, Inc.,
+     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ Also add information on how to contact you by electronic and paper mail.
+
+ If the program is interactive, make it output a short notice like this
+ when it starts in an interactive mode:
+
+     Gnomovision version 69, Copyright (C) year name of author
+     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+     This is free software, and you are welcome to redistribute it
+     under certain conditions; type `show c' for details.
+
+ The hypothetical commands `show w' and `show c' should show the appropriate
+ parts of the General Public License.  Of course, the commands you use may
+ be called something other than `show w' and `show c'; they could even be
+ mouse-clicks or menu items--whatever suits your program.
+
+ You should also get your employer (if you work as a programmer) or your
+ school, if any, to sign a "copyright disclaimer" for the program, if
+ necessary.  Here is a sample; alter the names:
+
+   Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+   `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+   <signature of Ty Coon>, 1 April 1989
+   Ty Coon, President of Vice
+
+ This General Public License does not permit incorporating your program into
+ proprietary programs.  If your program is a subroutine library, you may
+ consider it more useful to permit linking proprietary applications with the
+ library.  If this is what you want to do, use the GNU Lesser General
+ Public License instead of this License.
diff --git a/wlan_sd8897/mapp/mlan2040coex/Makefile b/wlan_sd8897/mapp/mlan2040coex/Makefile
new file mode 100644
index 0000000..0d94daf
--- /dev/null
+++ b/wlan_sd8897/mapp/mlan2040coex/Makefile
@@ -0,0 +1,48 @@
+#
+# File : mlan2040coex/Makefile
+#
+# Copyright (C) 2009-2017, Marvell International Ltd. All Rights Reserved
+
+# Path to the top directory of the mlandriver distribution
+PATH_TO_TOP = ../..
+
+# Determine how we should copy things to the install directory
+ABSPATH := $(filter /%, $(INSTALLDIR))
+RELPATH := $(filter-out /%, $(INSTALLDIR))
+INSTALLPATH := $(ABSPATH)
+ifeq ($(strip $(INSTALLPATH)),)
+INSTALLPATH := $(PATH_TO_TOP)/$(RELPATH)
+endif
+
+# Override CFLAGS for application sources, remove __ kernel namespace defines
+CFLAGS := $(filter-out -D__%, $(ccflags-y))
+# remove KERNEL include dir
+CFLAGS := $(filter-out -I$(KERNELDIR)%, $(CFLAGS))
+
+#
+# List of application executables to create
+#
+libobjs:= mlan2040coex.o mlan2040misc.o
+exectarget=mlan2040coex
+TARGETS := $(exectarget)
+
+#
+# Make target rules
+#
+
+# All rule compiles list of TARGETS using builtin program target from src rule
+all :
+$(exectarget): $(libobjs)
+	$(CC) $(CFLAGS) $(libobjs) -o $(exectarget)
+
+# Update any needed TARGETS and then copy to the install path
+build install: $(TARGETS)
+	@cp -f $(exectarget) $(INSTALLPATH)
+
+clean:
+	@rm -f $(exectarget)
+	@rm -f *.o
+
+distclean: clean
+	@rm -f *~ core
+	@rm -f tags
diff --git a/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.c b/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.c
new file mode 100644
index 0000000..aa2703b
--- /dev/null
+++ b/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.c
@@ -0,0 +1,1323 @@
+/** @file  mlan2040coex.c
+  *
+  * @brief 11n 20/40 MHz Coex application
+  *
+  * Copyright (C) 2009-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     06/24/2009: initial version
+************************************************************************/
+
+#include    <stdio.h>
+#include    <ctype.h>
+#include    <unistd.h>
+#include    <string.h>
+#include    <signal.h>
+#include    <fcntl.h>
+#include    <stdlib.h>
+#include    <errno.h>
+#include    <sys/socket.h>
+#include    <sys/ioctl.h>
+#include    <linux/if.h>
+#include    <linux/wireless.h>
+#include    <linux/netlink.h>
+#include    <linux/rtnetlink.h>
+#ifdef ANDROID
+#include    <net/if_ether.h>
+#else
+#include    <net/ethernet.h>
+#endif
+#include    "mlan2040coex.h"
+#include    "mlan2040misc.h"
+
+/** coex application's version number */
+#define COEX_VER "M2.0"
+
+/** Initial number of total private ioctl calls */
+#define IW_INIT_PRIV_NUM    128
+/** Maximum number of total private ioctl calls supported */
+#define IW_MAX_PRIV_NUM     1024
+
+/********************************************************
+		Local Variables
+********************************************************/
+
+static char *usage[] = {
+	"Usage: ",
+	"	mlan2040coex [-i <intfname>] [-hvB] ",
+	"	-h = help",
+	"	-v = version",
+	"	-B = run the process in background.",
+	"	(if intfname not present then mlan0 assumed)"
+};
+
+t_s32 sockfd = 0;  /**< socket descriptor */
+char dev_name[IFNAMSIZ + 1];   /**< device name */
+
+/** Flag: is 2040coex command required */
+int coex_cmd_req_flag = FALSE;
+/** Flag: is associated */
+int assoc_flag = FALSE;
+/** Flag: is HT AP */
+int is_ht_ap = FALSE;
+/** terminate flag */
+int terminate_flag = FALSE;
+
+/********************************************************
+		Global Variables
+********************************************************/
+/** OBSS scan parameter of associated AP */
+OBSSScanParam_t cur_obss_scan_param;
+
+/********************************************************
+		Local Functions
+********************************************************/
+
+/**
+ *  @brief Prepare command buffer
+ *  @param buffer   Command buffer to be filled
+ *  @param cmd      Command id
+ *  @param num      Number of arguments
+ *  @param args     Arguments list
+ *  @return         MLAN_STATUS_SUCCESS
+ */
+static int
+prepare_buffer(t_u8 *buffer, char *cmd, t_u32 num, char *args[])
+{
+	t_u8 *pos = NULL;
+	unsigned int i = 0;
+
+	memset(buffer, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+
+	/* Flag it for our use */
+	pos = buffer;
+	strncpy((char *)pos, CMD_MARVELL, strlen(CMD_MARVELL));
+	pos += (strlen(CMD_MARVELL));
+
+	/* Insert command */
+	strncpy((char *)pos, (char *)cmd, strlen(cmd));
+	pos += (strlen(cmd));
+
+	/* Insert arguments */
+	for (i = 0; i < num; i++) {
+		strncpy((char *)pos, args[i], strlen(args[i]));
+		pos += strlen(args[i]);
+		if (i < (num - 1)) {
+			strncpy((char *)pos, " ", strlen(" "));
+			pos += 1;
+		}
+	}
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process OBSS scan table
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_scantable(void)
+{
+	char ssid[MRVDRV_MAX_SSID_LENGTH + 1] = { 0 };
+	unsigned int scan_start;
+	int idx, i = 0, j, already_listed, ssid_len = 0, ssid_idx;
+
+	t_u8 *pcurrent;
+	t_u8 *pnext;
+	IEEEtypes_ElementId_e *pelement_id;
+	t_u8 *pelement_len, ht_cap_present, intol_bit_is_set;
+	int ret = MLAN_STATUS_SUCCESS;
+	t_s32 bss_info_len = 0;
+	t_u32 fixed_field_length = 0;
+
+	IEEEtypes_CapInfo_t cap_info;
+	t_u8 tsf[8];
+	t_u16 beacon_interval;
+	IEEEtypes_HTCap_t *pht_cap;
+
+	wlan_ioctl_get_scan_table_info *prsp_info;
+	wlan_get_scan_table_fixed fixed_fields;
+
+	t_u8 *buffer = NULL;
+	struct eth_priv_cmd *cmd = NULL;
+	struct ifreq ifr;
+
+	/* Start preparing the buffer */
+	/* Initialize buffer */
+	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (!buffer) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+
+	memset(&cap_info, 0x00, sizeof(cap_info));
+	memset(leg_ap_chan_list, 0, sizeof(leg_ap_chan_list));
+	num_leg_ap_chan = 0;
+
+	scan_start = 1;
+
+	do {
+		/* buffer = CMD_MARVELL + <cmd_string> */
+		prepare_buffer(buffer, "getscantable", 0, NULL);
+		prsp_info =
+			(wlan_ioctl_get_scan_table_info *)(buffer +
+							   strlen(CMD_MARVELL) +
+							   strlen
+							   ("getscantable"));
+
+		prsp_info->scan_number = scan_start;
+
+		/* Perform IOCTL */
+		memset(&ifr, 0, sizeof(struct ifreq));
+		strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+		ifr.ifr_ifru.ifru_data = (void *)cmd;
+
+		if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+			if (errno == EAGAIN) {
+				ret = -EAGAIN;
+			} else {
+				perror("mlan2040coex");
+				fprintf(stderr,
+					"mlan2040coex: getscantable fail\n");
+				ret = MLAN_STATUS_FAILURE;
+			}
+			goto done;
+		}
+
+		prsp_info = (wlan_ioctl_get_scan_table_info *)buffer;
+		pcurrent = 0;
+		pnext = prsp_info->scan_table_entry_buf;
+
+		if (scan_start == 1) {
+			printf("----------------------------------------------\n");
+		}
+
+		for (idx = 0; (unsigned int)idx < prsp_info->scan_number; idx++) {
+
+			/*
+			 * Set pcurrent to pnext in case pad bytes are at the end
+			 * of the last IE we processed.
+			 */
+			pcurrent = pnext;
+
+			memcpy((t_u8 *)&fixed_field_length,
+			       (t_u8 *)pcurrent, sizeof(fixed_field_length));
+			pcurrent += sizeof(fixed_field_length);
+
+			memcpy((t_u8 *)&bss_info_len,
+			       (t_u8 *)pcurrent, sizeof(bss_info_len));
+			pcurrent += sizeof(bss_info_len);
+
+			memcpy((t_u8 *)&fixed_fields,
+			       (t_u8 *)pcurrent, sizeof(fixed_fields));
+			pcurrent += fixed_field_length;
+
+			pnext = pcurrent + bss_info_len;
+
+			if (bss_info_len >= (sizeof(tsf)
+					     + sizeof(beacon_interval) +
+					     sizeof(cap_info))) {
+				pcurrent +=
+					(sizeof(tsf) + sizeof(beacon_interval) +
+					 sizeof(cap_info));
+				bss_info_len -=
+					(sizeof(tsf) + sizeof(beacon_interval) +
+					 sizeof(cap_info));
+			}
+			ht_cap_present = FALSE;
+			intol_bit_is_set = FALSE;
+			memset(ssid, 0, MRVDRV_MAX_SSID_LENGTH + 1);
+			ssid_len = 0;
+			while (bss_info_len >= 2) {
+				pelement_id = (IEEEtypes_ElementId_e *)pcurrent;
+				pelement_len = pcurrent + 1;
+				pcurrent += 2;
+
+				switch (*pelement_id) {
+				case SSID:
+					if (*pelement_len &&
+					    *pelement_len <=
+					    MRVDRV_MAX_SSID_LENGTH) {
+						memcpy(ssid, pcurrent,
+						       *pelement_len);
+						ssid_len = *pelement_len;
+					}
+					break;
+
+				case HT_CAPABILITY:
+					pht_cap =
+						(IEEEtypes_HTCap_t *)
+						pelement_id;
+					ht_cap_present = TRUE;
+					if (IS_INTOL_BIT_SET
+					    (le16_to_cpu
+					     (pht_cap->ht_cap.ht_cap_info))) {
+						intol_bit_is_set = TRUE;
+					}
+					break;
+				default:
+					break;
+				}
+				pcurrent += *pelement_len;
+				bss_info_len -= (2 + *pelement_len);
+			}
+			if (!ht_cap_present || intol_bit_is_set) {
+				printf("%s AP found on channel number: %-3d ",
+				       intol_bit_is_set ? "40 MHZ intolerant" :
+				       "Legacy", fixed_fields.channel);
+				if (ssid_len) {
+					printf("SSID: ");
+					/* Print out the ssid or the hex values if non-printable */
+					for (ssid_idx = 0; ssid_idx < ssid_len;
+					     ssid_idx++) {
+						if (isprint(ssid[ssid_idx])) {
+							printf("%c",
+							       ssid[ssid_idx]);
+						} else {
+							printf("\\%02x",
+							       ssid[ssid_idx]);
+						}
+					}
+				}
+				printf("\n");
+
+				/* Verify that the channel is already listed or not */
+				already_listed = FALSE;
+				for (j = 0; j < i; j++) {
+					if (leg_ap_chan_list[j].chan_num ==
+					    fixed_fields.channel) {
+						already_listed = TRUE;
+						if (intol_bit_is_set)
+							leg_ap_chan_list[j].
+								is_intol_set =
+								intol_bit_is_set;
+						break;
+					}
+				}
+				if (!already_listed) {
+					/* add the channel in list */
+					leg_ap_chan_list[i].chan_num =
+						fixed_fields.channel;
+					leg_ap_chan_list[i].is_intol_set =
+						intol_bit_is_set;
+					i++;
+					coex_cmd_req_flag = TRUE;
+					num_leg_ap_chan++;
+				}
+			}
+		}
+		scan_start += prsp_info->scan_number;
+
+	} while (prsp_info->scan_number);
+
+done:
+	if (cmd)
+		free(cmd);
+	if (buffer)
+		free(buffer);
+	return ret;
+}
+
+/** BSS Mode any (both infra and adhoc) */
+#define BSS_MODE_ANY 3
+
+/** current scan config parameters */
+#define SCAN_CFG_PARAMS 7
+
+/** Active : 1 , Passive : 2 */
+#define SCAN_TYPE_ACTIVE 1
+
+/**
+ *  @brief Issue get scan type command
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+get_scan_cfg(int *scan_param)
+{
+	t_u8 *buffer = NULL;
+	struct eth_priv_cmd *cmd = NULL;
+	struct ifreq ifr;
+	int ret = MLAN_STATUS_SUCCESS;
+
+	/* Start preparing the buffer */
+	/* Initialize buffer */
+	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (!buffer) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		return MLAN_STATUS_FAILURE;
+	}
+
+	/* buffer = CMD_MARVELL + <cmd_string> */
+	prepare_buffer(buffer, "scancfg", 0, NULL);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+
+	/* Perform IOCTL */
+	memset(&ifr, 0, sizeof(struct ifreq));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("mlan2040coex");
+		fprintf(stderr, "mlan2040coex: get_scan_cfg fail\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+	scan_param = (int *)(buffer);
+done:
+	if (cmd)
+		free(cmd);
+	if (buffer)
+		free(buffer);
+
+	return ret;
+}
+
+/**
+ *  @brief Issue OBSS scan command
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_setuserscan(void)
+{
+	wlan_ioctl_user_scan_cfg scan_req;
+	int scan_cfg[SCAN_CFG_PARAMS];
+	t_u8 *buffer = NULL, *pos = NULL;
+	struct eth_priv_cmd *cmd = NULL;
+	struct ifreq ifr;
+	int status = 0;
+
+	memset(&scan_req, 0x00, sizeof(scan_req));
+	memset(scan_cfg, 0x00, SCAN_CFG_PARAMS);
+	coex_cmd_req_flag = FALSE;
+
+	if (get_scan_cfg(scan_cfg) != MLAN_STATUS_SUCCESS) {
+		printf("mlan2040coex: scancfg ioctl failure");
+		return -EFAULT;
+	}
+
+	if (scan_cfg[0] == SCAN_TYPE_ACTIVE)
+		scan_req.chan_list[0].scan_time =
+			(t_u32)le16_to_cpu(cur_obss_scan_param.
+					   obss_scan_active_dwell);
+	else
+		scan_req.chan_list[0].scan_time =
+			(t_u32)le16_to_cpu(cur_obss_scan_param.
+					   obss_scan_passive_total);
+	scan_req.bss_mode = (scan_cfg[1]) ? scan_cfg[1] : BSS_MODE_ANY;
+
+	/* Start preparing the buffer */
+	/* Initialize buffer */
+	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (!buffer) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		return MLAN_STATUS_FAILURE;
+	}
+
+	/* buffer = CMD_MARVELL + <cmd_string> */
+	prepare_buffer(buffer, "setuserscan", 0, NULL);
+	pos = buffer + strlen(CMD_MARVELL) + strlen("setuserscan");
+
+	/* buffer = buffer + 'scan_req' */
+	memcpy(pos, &scan_req, sizeof(wlan_ioctl_user_scan_cfg));
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		free(buffer);
+		return MLAN_STATUS_FAILURE;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+
+	/* Perform IOCTL */
+	memset(&ifr, 0, sizeof(struct ifreq));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("mlan2040coex");
+		fprintf(stderr, "mlan2040coex: setuserscan fail\n");
+		if (cmd)
+			free(cmd);
+		if (buffer)
+			free(buffer);
+		return MLAN_STATUS_FAILURE;
+	}
+
+    /** process scan results */
+	do {
+		status = process_scantable();
+	} while (status == -EAGAIN);
+
+	if (cmd)
+		free(cmd);
+	if (buffer)
+		free(buffer);
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Display usage
+ *
+ *  @return       NA
+ */
+static t_void
+display_usage(t_void)
+{
+	t_u32 i;
+	for (i = 0; i < NELEMENTS(usage); i++)
+		fprintf(stderr, "%s\n", usage[i]);
+}
+
+/**
+ *  @brief              get connection status
+ *
+ *  @param data         Pointer to the output buffer holding connection status
+ *  @return             MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+int
+get_connstatus(int *data)
+{
+	struct ether_addr apaddr;
+	struct ether_addr etherzero = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} };
+	t_u8 *buffer = NULL;
+	struct eth_priv_cmd *cmd = NULL;
+	struct ifreq ifr;
+
+	/* Initialize buffer */
+	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (!buffer) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		return MLAN_STATUS_FAILURE;
+	}
+
+	/* buffer = CMD_MARVELL + <cmd_string> */
+	prepare_buffer(buffer, "getwap", 0, NULL);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		free(buffer);
+		return MLAN_STATUS_FAILURE;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+
+	/* Perform IOCTL */
+	memset(&ifr, 0, sizeof(struct ifreq));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("mlan2040coex");
+		fprintf(stderr, "mlan2040coex: getwap fail\n");
+		if (cmd)
+			free(cmd);
+		if (buffer)
+			free(buffer);
+		return MLAN_STATUS_FAILURE;
+	}
+
+	memset(&apaddr, 0, sizeof(struct ether_addr));
+	memcpy(&apaddr, (struct ether_addr *)(buffer),
+	       sizeof(struct ether_addr));
+
+	if (!memcmp(&apaddr, &etherzero, sizeof(struct ether_addr))) {
+		/* not associated */
+		*data = FALSE;
+	} else {
+		/* associated */
+		*data = TRUE;
+	}
+
+	if (buffer)
+		free(buffer);
+	if (cmd)
+		free(cmd);
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Print connect and disconnect event related information
+ *
+ *  @param buffer   Pointer to received event buffer
+ *  @param size     Length of the received event
+ *
+ *  @return         N/A
+ */
+void
+print_event_drv_connected(t_u8 *buffer, t_u16 size)
+{
+	struct ether_addr *wap;
+	struct ether_addr etherzero = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} };
+	char buf[32];
+
+	wap = (struct ether_addr *)(buffer + strlen(CUS_EVT_AP_CONNECTED));
+
+	if (!memcmp(wap, &etherzero, sizeof(struct ether_addr))) {
+		printf("---< Disconnected from AP >---\n");
+		memset(&cur_obss_scan_param, 0, sizeof(cur_obss_scan_param));
+		assoc_flag = FALSE;
+		is_ht_ap = FALSE;
+	} else {
+		memset(buf, 0, sizeof(buf));
+		snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X",
+			 wap->ether_addr_octet[0],
+			 wap->ether_addr_octet[1],
+			 wap->ether_addr_octet[2],
+			 wap->ether_addr_octet[3],
+			 wap->ether_addr_octet[4], wap->ether_addr_octet[5]);
+		printf("---< Connected to AP: %s >---\n", buf);
+	/** set TRUE, if connected */
+		assoc_flag = TRUE;
+	}
+}
+
+/**
+ *  @brief Parse and print received event information
+ *
+ *  @param event    Pointer to received event
+ *  @param size     Length of the received event
+ *  @param evt_conn     A pointer to a output buffer. It sets TRUE when it gets
+ *  					the event CUS_EVT_OBSS_SCAN_PARAM, otherwise FALSE
+ *  @param if_name  The interface name
+ *
+ *  @return         N/A
+ */
+void
+print_event(event_header *event, t_u16 size, int *evt_conn, char *if_name)
+{
+	if (!strncmp
+	    (CUS_EVT_AP_CONNECTED, (char *)event,
+	     strlen(CUS_EVT_AP_CONNECTED))) {
+		if (strlen(if_name))
+			printf("EVENT for interface %s\n", if_name);
+		print_event_drv_connected((t_u8 *)event, size);
+		return;
+	}
+	if (!strncmp
+	    (CUS_EVT_OBSS_SCAN_PARAM, (char *)event,
+	     strlen(CUS_EVT_OBSS_SCAN_PARAM))) {
+		if (strlen(if_name))
+			printf("EVENT for interface %s\n", if_name);
+		printf("---< %s >---\n", CUS_EVT_OBSS_SCAN_PARAM);
+		memset(&cur_obss_scan_param, 0, sizeof(cur_obss_scan_param));
+		memcpy(&cur_obss_scan_param,
+		       ((t_u8 *)event + strlen(CUS_EVT_OBSS_SCAN_PARAM) + 1),
+		       sizeof(cur_obss_scan_param));
+	/** set TRUE, since it is an HT AP */
+		is_ht_ap = TRUE;
+		*evt_conn = TRUE;
+		return;
+	}
+	if (!strncmp
+	    (CUS_EVT_BW_CHANGED, (char *)event, strlen(CUS_EVT_BW_CHANGED))) {
+		if (strlen(if_name))
+			printf("EVENT for interface %s\n", if_name);
+		printf("---< %s >---\n", CUS_EVT_BW_CHANGED);
+		return;
+	}
+}
+
+/**
+ *  @brief              This function parses for NETLINK events
+ *
+ *  @param nlh          Pointer to Netlink message header
+ *  @param bytes_read   Number of bytes to be read
+ *  @param evt_conn     A pointer to a output buffer. It sets TRUE when it gets
+ *  					the event CUS_EVT_OBSS_SCAN_PARAM, otherwise FALSE
+ *  @return             MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+static int
+drv_nlevt_handler(struct nlmsghdr *nlh, int bytes_read, int *evt_conn)
+{
+	int len, plen;
+	t_u8 *buffer = NULL;
+	t_u32 event_id = 0;
+	event_header *event = NULL;
+	char if_name[IFNAMSIZ + 1];
+
+	/* Initialize receive buffer */
+	buffer = (t_u8 *)malloc(NL_MAX_PAYLOAD);
+	if (!buffer) {
+		printf("ERR: Could not alloc buffer\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	memset(buffer, 0, NL_MAX_PAYLOAD);
+
+	*evt_conn = FALSE;
+	while ((unsigned int)bytes_read >= NLMSG_HDRLEN) {
+		len = nlh->nlmsg_len;	/* Length of message including header */
+		plen = len - NLMSG_HDRLEN;
+		if (len > bytes_read || plen < 0) {
+			/* malformed netlink message */
+			return MLAN_STATUS_FAILURE;
+		}
+		if ((unsigned int)len > NLMSG_SPACE(NL_MAX_PAYLOAD)) {
+			printf("ERR:Buffer overflow!\n");
+			return MLAN_STATUS_FAILURE;
+		}
+		memset(buffer, 0, NL_MAX_PAYLOAD);
+		memcpy(buffer, NLMSG_DATA(nlh), plen);
+
+		if (NLMSG_OK(nlh, len)) {
+			memcpy(&event_id, buffer, sizeof(event_id));
+
+			if (((event_id & 0xFF000000) == 0x80000000) ||
+			    ((event_id & 0xFF000000) == 0)) {
+				event = (event_header *)buffer;
+			} else {
+				memset(if_name, 0, IFNAMSIZ + 1);
+				memcpy(if_name, buffer, IFNAMSIZ);
+				event = (event_header *)(buffer + IFNAMSIZ);
+			}
+		}
+
+		print_event(event, bytes_read, evt_conn, if_name);
+		len = NLMSG_ALIGN(len);
+		bytes_read -= len;
+		nlh = (struct nlmsghdr *)((char *)nlh + len);
+	}
+	return MLAN_STATUS_SUCCESS;
+}
+
+/** Maximum event message length */
+#define MAX_MSG_LENGTH	1024
+
+/**
+ *  @brief Configure and read event data from netlink socket
+ *
+ *  @param nl_sk        Netlink socket handler
+ *  @param msg          Pointer to message header
+ *  @param ptv          Pointer to struct timeval
+ *
+ *  @return             Number of bytes read or MLAN_STATUS_FAILURE
+ */
+int
+read_event(int nl_sk, struct msghdr *msg, struct timeval *ptv)
+{
+	int count = -1;
+	int ret = MLAN_STATUS_FAILURE;
+	fd_set rfds;
+
+	/* Setup read fds and initialize event buffers */
+	FD_ZERO(&rfds);
+	FD_SET(nl_sk, &rfds);
+
+	/* Wait for reply */
+	ret = select(nl_sk + 1, &rfds, NULL, NULL, ptv);
+
+	if (ret == MLAN_STATUS_FAILURE) {
+		/* Error */
+		terminate_flag++;
+		ptv->tv_sec = DEFAULT_SCAN_INTERVAL;
+		ptv->tv_usec = 0;
+		goto done;
+	} else if (!ret) {
+		if (assoc_flag && is_ht_ap) {
+	    /** Issue OBSS scan */
+			process_setuserscan();
+	    /** Invoke 2040coex command, if any legacy AP found or
+             *  any AP has 40MHz intolarent bit set */
+			if (coex_cmd_req_flag)
+				invoke_coex_command();
+		}
+		if (assoc_flag && is_ht_ap) {
+			/* Timeout. Try again after BSS channel width triger scan
+			   interval when the STA is connected with a HT AP */
+			ptv->tv_sec =
+				(t_u32)le16_to_cpu(cur_obss_scan_param.
+						   bss_chan_width_trigger_scan_int);
+		} else {
+			/* Timeout. Try again after default duration when the STA is
+			   not connected with a HT AP */
+			ptv->tv_sec = DEFAULT_SCAN_INTERVAL;
+		}
+		ptv->tv_usec = 0;
+		goto done;
+	}
+
+	if (!FD_ISSET(nl_sk, &rfds)) {
+		/* Unexpected error. Try again */
+		ptv->tv_sec = DEFAULT_SCAN_INTERVAL;
+		ptv->tv_usec = 0;
+		goto done;
+	}
+	/* Success */
+	count = recvmsg(nl_sk, msg, 0);
+
+done:
+	return count;
+}
+
+/** Maximum event message length */
+#define MAX_MSG_LENGTH	1024
+/**
+ *  @brief Run the application
+ *
+ *  @param nl_sk    Netlink socket
+ *
+ *  @return         N/A
+ */
+void
+run_app(int nl_sk)
+{
+	struct timeval tv;
+	int bytes_read, evt_conn;
+	struct msghdr msg;
+	struct sockaddr_nl dest_addr;
+	struct nlmsghdr *nlh;
+	struct iovec iov;
+
+    /** Get connection status */
+	if (get_connstatus(&assoc_flag) == MLAN_STATUS_FAILURE)
+		return;
+
+	/* Initialize timeout value */
+	tv.tv_sec = DEFAULT_SCAN_INTERVAL;
+	tv.tv_usec = 0;
+
+	/* Initialize netlink header */
+	nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(NL_MAX_PAYLOAD));
+	if (!nlh) {
+		printf("ERR: Could not allocate space for netlink header\n");
+		goto done;
+	}
+	memset(nlh, 0, NLMSG_SPACE(NL_MAX_PAYLOAD));
+	/* Fill the netlink message header */
+	nlh->nlmsg_len = NLMSG_SPACE(NL_MAX_PAYLOAD);
+	nlh->nlmsg_pid = getpid();	/* self pid */
+	nlh->nlmsg_flags = 0;
+
+	/* Initialize I/O vector */
+	memset(&iov, 0, sizeof(struct iovec));
+	iov.iov_base = (void *)nlh;
+	iov.iov_len = nlh->nlmsg_len;
+
+	/* Set destination address */
+	memset(&dest_addr, 0, sizeof(struct sockaddr_nl));
+	dest_addr.nl_family = AF_NETLINK;
+	dest_addr.nl_pid = 0;	/* Kernel */
+	dest_addr.nl_groups = NL_MULTICAST_GROUP;
+
+	/* Initialize message header */
+	memset(&msg, 0, sizeof(struct msghdr));
+	msg.msg_name = (void *)&dest_addr;
+	msg.msg_namelen = sizeof(dest_addr);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	while (!terminate_flag) {
+		/* event buffer is received for all the interfaces */
+		bytes_read = read_event(nl_sk, &msg, &tv);
+		/* handle only NETLINK events here */
+		drv_nlevt_handler((struct nlmsghdr *)nlh, bytes_read,
+				  &evt_conn);
+		if (assoc_flag && is_ht_ap) {
+	    /** If the event is connected with an HT AP then issue OBSS scan immediately */
+			if (evt_conn) {
+		/** Issue OBSS scan */
+				process_setuserscan();
+		/** Invoke 2040coex command, if any legacy AP found or
+                 *  any AP has 40MHz intolarent bit set */
+				if (coex_cmd_req_flag)
+					invoke_coex_command();
+			}
+			tv.tv_sec =
+				(t_u32)le16_to_cpu(cur_obss_scan_param.
+						   bss_chan_width_trigger_scan_int);
+		} else {
+			tv.tv_sec = DEFAULT_SCAN_INTERVAL;
+		}
+		tv.tv_usec = 0;
+	}
+
+done:
+	if (nl_sk > 0)
+		close(nl_sk);
+	if (nlh)
+		free(nlh);
+	return;
+}
+
+/**
+ *  @brief Determine the netlink number
+ *
+ *  @return         Netlink number to use
+ */
+static int
+get_netlink_num(int dev_index)
+{
+	FILE *fp = NULL;
+	int netlink_num = NETLINK_MARVELL;
+	char str[64];
+	char *srch = "netlink_num";
+	char filename[64];
+
+	if (dev_index == 0) {
+		strcpy(filename, "/proc/mwlan/config");
+	} else if (dev_index > 0) {
+		sprintf(filename, "/proc/mwlan/config%d", dev_index);
+	}
+	/* Try to open /proc/mwlan/config$ */
+	fp = fopen(filename, "r");
+
+	if (fp) {
+		while (!feof(fp)) {
+			fgets(str, sizeof(str), fp);
+			if (strncmp(str, srch, strlen(srch)) == 0) {
+				netlink_num = atoi(str + strlen(srch) + 1);
+				break;
+			}
+		}
+		fclose(fp);
+	} else {
+		return -1;
+	}
+
+	printf("Netlink number = %d\n", netlink_num);
+	return netlink_num;
+}
+
+/**
+ *  @brief opens netlink socket to receive NETLINK events
+ *  @return  socket id --success, otherwise--MLAN_STATUS_FAILURE
+ */
+int
+open_netlink(int dev_index)
+{
+	int sk = -1;
+	struct sockaddr_nl src_addr;
+	int netlink_num = 0;
+
+	netlink_num = get_netlink_num(dev_index);
+	if (netlink_num < 0) {
+		printf("ERR:Could not get netlink socket. Invalid device number.\n");
+		return sk;
+	}
+
+	/* Open netlink socket */
+	sk = socket(PF_NETLINK, SOCK_RAW, netlink_num);
+	if (sk < 0) {
+		printf("ERR:Could not open netlink socket.\n");
+		return sk;
+	}
+
+	/* Set source address */
+	memset(&src_addr, 0, sizeof(src_addr));
+	src_addr.nl_family = AF_NETLINK;
+	src_addr.nl_pid = getpid();
+	src_addr.nl_groups = NL_MULTICAST_GROUP;
+
+	/* Bind socket with source address */
+	if (bind(sk, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) {
+		printf("ERR:Could not bind socket!\n");
+		close(sk);
+		return -1;
+	}
+	return sk;
+}
+
+/**
+ *  @brief Terminate signal handler
+ *  @param signal   Signal to handle
+ *  @return         NA
+ */
+static t_void
+terminate_handler(int signal)
+{
+	printf("Stopping application.\n");
+#if DEBUG
+	printf("Process ID of process killed = %d\n", getpid());
+#endif
+	terminate_flag = TRUE;
+}
+
+/********************************************************
+		Global Functions
+********************************************************/
+/**
+ *  @brief Process host command
+ *  @param hostcmd_idx  Host command index
+ *  @param chan_list    A pointer to a channel list
+ *  @param chan_num     Number of channels in the channel list
+ *  @param reg_class    Regulatory class of the channels
+ *  @param is_intol_ap_present Flag:is there any 40 MHz intolerant AP is present or not
+ *
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_host_cmd(int hostcmd_idx, t_u8 *chan_list, t_u8 chan_num,
+		 t_u8 reg_class, t_u8 is_intol_ap_present)
+{
+	int ret = MLAN_STATUS_SUCCESS;
+	t_u8 *buffer = NULL;
+	struct eth_priv_cmd *cmd = NULL;
+	struct ifreq ifr;
+
+	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (!buffer) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	prepare_buffer(buffer, "hostcmd", 0, NULL);
+	switch (hostcmd_idx) {
+	case CMD_2040COEX:
+		prepare_coex_cmd_buff(buffer + strlen(CMD_MARVELL) +
+				      strlen("hostcmd"), chan_list, chan_num,
+				      reg_class, is_intol_ap_present);
+		break;
+	default:
+		break;
+	}
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+
+	/* Perform IOCTL */
+	memset(&ifr, 0, sizeof(struct ifreq));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("mlan2040coex");
+		fprintf(stderr, "mlan2040coex: hostcmd fail\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	ret = process_host_cmd_resp("hostcmd", buffer);
+
+done:
+	if (cmd)
+		free(cmd);
+	if (buffer)
+		free(buffer);
+	return ret;
+}
+
+/**
+ *  @brief Check the STA is 40 MHz intolerant or not
+ *  @param intol	Flag: TRUE when the STA is 40 MHz intolerant, otherwise FALSE
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+is_intolerant_sta(int *intol)
+{
+	t_u8 *buffer = NULL;
+	struct eth_priv_cmd *cmd = NULL;
+	struct ifreq ifr;
+	int htcap_info, ret = MLAN_STATUS_SUCCESS;
+
+	*intol = FALSE;
+
+	/* Initialize buffer */
+	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (!buffer) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* buffer = CMD_MARVELL + <cmd_string> */
+	prepare_buffer(buffer, "htcapinfo", 0, NULL);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+
+	/* Perform IOCTL */
+	memset(&ifr, 0, sizeof(struct ifreq));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("mlan2040coex");
+		fprintf(stderr, "mlan2040coex: htcapinfo fail\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	htcap_info = *((int *)(buffer));
+
+	if (htcap_info & MBIT(8))
+		*intol = TRUE;
+
+done:
+	if (cmd)
+		free(cmd);
+	if (buffer)
+		free(buffer);
+	return ret;
+}
+
+/**
+ *  @brief get region code
+ *  @param reg_code	Pointer to region code
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+get_region_code(int *reg_code)
+{
+	t_u8 *buffer = NULL;
+	struct eth_priv_cmd *cmd = NULL;
+	struct ifreq ifr;
+	int ret = MLAN_STATUS_SUCCESS;
+
+	/* Initialize buffer */
+	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (!buffer) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* buffer = CMD_MARVELL + <cmd_string> */
+	prepare_buffer(buffer, "regioncode", 0, NULL);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+
+	/* Perform IOCTL */
+	memset(&ifr, 0, sizeof(struct ifreq));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("mlan2040coex");
+		fprintf(stderr, "mlan2040coex: regioncode fail\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	memcpy(reg_code, buffer, sizeof(int));
+done:
+	if (cmd)
+		free(cmd);
+	if (buffer)
+		free(buffer);
+	return ret;
+}
+
+/** No option */
+#define NO_OPTION -1
+
+/**
+ *  @brief Entry function for coex
+ *  @param argc		number of arguments
+ *  @param argv     A pointer to arguments array
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+main(int argc, char *argv[])
+{
+	char ifname[IFNAMSIZ + 1] = "mlan0";
+	int c, daemonize = FALSE;
+	t_s32 nl_sk;
+		  /**< netlink socket descriptor to receive an event */
+	int dev_index = 0;	     /** initialise with -1 to open multiple NETLINK Sockets */
+
+	char temp[2];
+	int arg_count = 0;
+	int ifname_given = FALSE;
+
+	for (;;) {
+		c = getopt(argc, argv, "Bhi:vd:");
+		/* check if all command-line options have been parsed */
+		if (c == NO_OPTION)
+			break;
+
+		switch (c) {
+		case 'B':
+			daemonize = TRUE;
+			break;
+		case 'h':
+			display_usage();
+			return MLAN_STATUS_SUCCESS;
+		case 'v':
+			fprintf(stdout,
+				"Marvell 20/40coex application version %s\n",
+				COEX_VER);
+			return MLAN_STATUS_SUCCESS;
+		case 'i':
+			ifname_given = TRUE;
+			if (strlen(optarg) < IFNAMSIZ)
+				strncpy(ifname, optarg, IFNAMSIZ - 1);
+			else {
+				fprintf(stdout, "Interface name too long\n");
+				display_usage();
+				return MLAN_STATUS_SUCCESS;
+			}
+			arg_count += 1;
+			break;
+		case 'd':
+			strncpy(temp, optarg, strlen(optarg));
+			if (isdigit(temp[0]))
+				dev_index = atoi(temp);
+			arg_count += 1;
+			break;
+		default:
+			fprintf(stdout, "Invalid argument\n");
+			display_usage();
+			return MLAN_STATUS_SUCCESS;
+		}
+	}
+
+	if (!ifname_given) {
+		sprintf(ifname, "mlan%d", dev_index);
+	}
+
+	if (optind < argc) {
+		fputs("Too many arguments.\n", stderr);
+		display_usage();
+		goto done;
+	}
+
+	strncpy(dev_name, ifname, IFNAMSIZ - 1);
+
+	/* create a socket */
+	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		fprintf(stderr, "mlan2040coex: Cannot open socket.\n");
+		goto done;
+	}
+
+	/* create netlink sockets and bind them with app side addr */
+	nl_sk = open_netlink(dev_index);
+
+	if (nl_sk < 0) {
+		fprintf(stderr, "mlan2040coex: Cannot open netlink socket.\n");
+		goto done;
+	}
+
+	signal(SIGHUP, terminate_handler);	/* catch hangup signal */
+	signal(SIGTERM, terminate_handler);	/* catch kill signal */
+	signal(SIGINT, terminate_handler);	/* catch kill signal */
+	signal(SIGALRM, terminate_handler);	/* catch kill signal */
+
+    /** Make the process background-process */
+	if (daemonize) {
+		if (daemon(0, 0))
+			fprintf(stderr, "mlan2040coex: Cannot start daemon\n");
+	}
+
+    /** run the application */
+	run_app(nl_sk);
+
+done:
+	if (sockfd > 0)
+		close(sockfd);
+	if (nl_sk > 0)
+		close(nl_sk);
+
+	return MLAN_STATUS_SUCCESS;
+}
diff --git a/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.h b/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.h
new file mode 100644
index 0000000..01339d8
--- /dev/null
+++ b/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.h
@@ -0,0 +1,233 @@
+/** @file  mlan2040coex.h
+  *
+  * @brief This file contains definitions for application
+  *
+  * Copyright (C) 2009-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     06/24/2009: initial version
+************************************************************************/
+#ifndef _COEX_H_
+#define _COEX_H_
+
+/** Marvell private command identifier */
+#define CMD_MARVELL "MRVL_CMD"
+
+/** IOCTL number */
+#define MLAN_ETH_PRIV       (SIOCDEVPRIVATE + 14)
+
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#undef BIG_ENDIAN_SUPPORT
+#endif
+
+/** 16 bits byte swap */
+#define swap_byte_16(x) \
+((t_u16)((((t_u16)(x) & 0x00ffU) << 8) | \
+         (((t_u16)(x) & 0xff00U) >> 8)))
+
+/** 32 bits byte swap */
+#define swap_byte_32(x) \
+((t_u32)((((t_u32)(x) & 0x000000ffUL) << 24) | \
+         (((t_u32)(x) & 0x0000ff00UL) <<  8) | \
+         (((t_u32)(x) & 0x00ff0000UL) >>  8) | \
+         (((t_u32)(x) & 0xff000000UL) >> 24)))
+
+/** 64 bits byte swap */
+#define swap_byte_64(x) \
+  ((t_u64)((t_u64)(((t_u64)(x) & 0x00000000000000ffULL) << 56) | \
+         (t_u64)(((t_u64)(x) & 0x000000000000ff00ULL) << 40) | \
+         (t_u64)(((t_u64)(x) & 0x0000000000ff0000ULL) << 24) | \
+         (t_u64)(((t_u64)(x) & 0x00000000ff000000ULL) <<  8) | \
+         (t_u64)(((t_u64)(x) & 0x000000ff00000000ULL) >>  8) | \
+         (t_u64)(((t_u64)(x) & 0x0000ff0000000000ULL) >> 24) | \
+         (t_u64)(((t_u64)(x) & 0x00ff000000000000ULL) >> 40) | \
+         (t_u64)(((t_u64)(x) & 0xff00000000000000ULL) >> 56) ))
+
+/** Convert to correct endian format */
+#ifdef 	BIG_ENDIAN_SUPPORT
+/** CPU to little-endian convert for 16-bit */
+#define 	cpu_to_le16(x)	swap_byte_16(x)
+/** CPU to little-endian convert for 32-bit */
+#define		cpu_to_le32(x)  swap_byte_32(x)
+/** CPU to little-endian convert for 64-bit */
+#define     cpu_to_le64(x)  swap_byte_64(x)
+/** Little-endian to CPU convert for 16-bit */
+#define 	le16_to_cpu(x)	swap_byte_16(x)
+/** Little-endian to CPU convert for 32-bit */
+#define		le32_to_cpu(x)  swap_byte_32(x)
+/** Little-endian to CPU convert for 64-bit */
+#define     le64_to_cpu(x)  swap_byte_64(x)
+#else
+/** Do nothing */
+#define		cpu_to_le16(x)	(x)
+/** Do nothing */
+#define		cpu_to_le32(x)  (x)
+/** Do nothing */
+#define		cpu_to_le64(x)  (x)
+/** Do nothing */
+#define 	le16_to_cpu(x)	(x)
+/** Do nothing */
+#define 	le32_to_cpu(x)	(x)
+/** Do nothing */
+#define 	le64_to_cpu(x)	(x)
+#endif
+
+#ifdef __GNUC__
+/** Structure packing begins */
+#define PACK_START
+/** Structure packeing end */
+#define PACK_END  __attribute__ ((packed))
+#else
+/** Structure packing begins */
+#define PACK_START   __packed
+/** Structure packeing end */
+#define PACK_END
+#endif
+
+/** Character, 1 byte */
+typedef signed char t_s8;
+/** Unsigned character, 1 byte */
+typedef unsigned char t_u8;
+
+/** Short integer */
+typedef signed short t_s16;
+/** Unsigned short integer */
+typedef unsigned short t_u16;
+
+/** Integer */
+typedef signed int t_s32;
+/** Unsigned integer */
+typedef unsigned int t_u32;
+
+/** Long long integer */
+typedef signed long long t_s64;
+/** Unsigned long long integer */
+typedef unsigned long long t_u64;
+
+/** Void pointer (4-bytes) */
+typedef void t_void;
+
+#ifdef FALSE
+#undef FALSE
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+
+#ifndef MIN
+/** Find minimum value */
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
+
+/** Type definition: boolean */
+typedef enum { FALSE, TRUE } boolean;
+
+/** Find number of elements */
+#define NELEMENTS(x) (sizeof(x)/sizeof(x[0]))
+/** Success */
+#define MLAN_STATUS_SUCCESS         (0)
+/** Failure */
+#define MLAN_STATUS_FAILURE         (-1)
+
+/** Enumeration for host-command index */
+enum COMMANDS {
+	CMD_2040COEX = 1,
+};
+/** Maximum number of channels that can be sent in a setuserscan ioctl */
+#define WLAN_IOCTL_USER_SCAN_CHAN_MAX  50
+
+#ifndef ETH_ALEN
+/** MAC address length */
+#define ETH_ALEN    6
+#endif
+
+/** Netlink protocol number */
+#define NETLINK_MARVELL         (MAX_LINKS - 1)
+/** Netlink maximum payload size */
+#define NL_MAX_PAYLOAD          1024
+/** Netlink multicast group number */
+#define NL_MULTICAST_GROUP      RTMGRP_LINK
+/** Default wait time in seconds for events */
+#define UAP_RECV_WAIT_DEFAULT   10
+#ifndef NLMSG_HDRLEN
+/** NL message header length */
+#define NLMSG_HDRLEN            ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#endif
+
+/** Event header */
+typedef PACK_START struct _event_header {
+    /** Event ID */
+	t_u32 event_id;
+    /** Event data */
+	t_u8 event_data[0];
+} PACK_END event_header;
+
+/** Event ID length */
+#define EVENT_ID_LEN    4
+
+/** Custom events definitions */
+/** AP connected event */
+#define CUS_EVT_AP_CONNECTED        "EVENT=AP_CONNECTED"
+
+/** Custom event : BW changed */
+#define CUS_EVT_BW_CHANGED		    "EVENT=BW_CHANGED"
+/** Custom event : OBSS scan parameter */
+#define CUS_EVT_OBSS_SCAN_PARAM		"EVENT=OBSS_SCAN_PARAM"
+
+/** Custom events definitions end */
+
+/** Structure defination of chan_intol_t*/
+typedef struct _chan_intol_t {
+    /** Channel numer */
+	t_u8 chan_num;
+    /** Flag: Is any 40MHz intolerant AP found in this channel */
+	t_u8 is_intol_set;
+} chan_intol_t;
+
+/** Private command structure */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+struct eth_priv_cmd {
+    /** Command buffer pointer */
+	t_u64 buf;
+    /** buffer updated by driver */
+	int used_len;
+    /** buffer sent by application */
+	int total_len;
+} __ATTRIB_PACK__;
+#else
+struct eth_priv_cmd {
+    /** Command buffer */
+	t_u8 *buf;
+    /** Used length */
+	int used_len;
+    /** Total length */
+	int total_len;
+};
+#endif
+
+/** Legacy APs channel list */
+chan_intol_t leg_ap_chan_list[WLAN_IOCTL_USER_SCAN_CHAN_MAX];
+/** Total number of channel present in Legacy APs channel list */
+t_u8 num_leg_ap_chan;
+int get_region_code(int *reg_code);
+int process_host_cmd(int cmd, t_u8 *chan_list, t_u8 chan_num, t_u8 reg_class,
+		     t_u8 is_intol_ap_present);
+int is_intolerant_sta(int *intol);
+
+#endif /* _COEX_H_ */
diff --git a/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.c b/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.c
new file mode 100644
index 0000000..004e09b
--- /dev/null
+++ b/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.c
@@ -0,0 +1,280 @@
+/** @file  mlan2040misc.c
+  *
+  * @brief This file contains helper functions for coex application
+  *
+  * Copyright (C) 2009-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     06/24/2009: initial version
+************************************************************************/
+#include    <stdio.h>
+#include    <ctype.h>
+#include    <string.h>
+#include    <stdlib.h>
+#include    "mlan2040coex.h"
+#include    "mlan2040misc.h"
+
+/********************************************************
+		Local Variables
+********************************************************/
+/** Regulatory class and Channel mapping for various regions */
+static class_chan_t us_class_chan_t[] = {
+	{32, {1, 2, 3, 4, 5, 6, 7}, 7},
+	{33, {5, 6, 7, 8, 9, 10, 11}, 7}
+};
+
+static class_chan_t europe_class_chan_t[] = {
+	{11, {1, 2, 3, 4, 5, 6, 7, 8, 9}, 9},
+	{12, {5, 6, 7, 8, 9, 10, 11, 12, 13}, 9}
+};
+
+static class_chan_t japan_class_chan_t[] = {
+	{56, {1, 2, 3, 4, 5, 6, 7, 8, 9}, 9},
+	{57, {5, 6, 7, 8, 9, 10, 11, 12, 13}, 9},
+	{58, {14}, 1}
+};
+
+/** Region-code(Regulatory domain) and Class-channel table mapping */
+static region_class_chan_t region_class_chan_table[] = {
+	{0x10, us_class_chan_t, sizeof(us_class_chan_t) / sizeof(class_chan_t)}	/* US */
+	,
+	{0x20, us_class_chan_t, sizeof(us_class_chan_t) / sizeof(class_chan_t)}	/* CANADA */
+	,
+	{0x30, europe_class_chan_t, sizeof(europe_class_chan_t) / sizeof(class_chan_t)}	/* EUROPE */
+	,
+	{0x32, europe_class_chan_t, sizeof(europe_class_chan_t) / sizeof(class_chan_t)}	/* FRANCE */
+	,
+	{0x40, japan_class_chan_t, sizeof(japan_class_chan_t) / sizeof(class_chan_t)}	/* JAPAN */
+	,
+	{0x41, japan_class_chan_t, sizeof(japan_class_chan_t) / sizeof(class_chan_t)}	/* JAPAN */
+	,
+	{0x50, europe_class_chan_t, sizeof(europe_class_chan_t) / sizeof(class_chan_t)}	/* CHINA */
+};
+
+/********************************************************
+		Global Variables
+********************************************************/
+
+/********************************************************
+		Local Functions
+********************************************************/
+/**
+ *  @brief  This function prepares the channel list for a particular
+ *  		regulatory class from channel number for legacy AP
+ *  @param cur_class_chan_table  A pointer to the class_chan_t
+ *  @param num_entry             Number of entry in cur_class_chan_table table
+ *  @param chan_list             A pointer to the output channel list
+ *  @param chan_num	             total number of channel in output channel list
+ *  @param reg_domain            regulatory domain
+ *  @param reg_class             regulatory class
+ *  @param is_intol_ap_present   It sets TRUE when 40MHz intolerant AP is found
+ *  						     otherwise FALSE
+ *  @return      	  None
+ */
+static void
+get_channels_for_specified_reg_class(class_chan_t *cur_class_chan_table,
+				     int num_entry, t_u8 *chan_list,
+				     t_u8 *chan_num, t_u8 reg_domain,
+				     t_u8 reg_class, t_u8 *is_intol_ap_present)
+{
+	int i, j, k, idx = 0;
+
+	*is_intol_ap_present = FALSE;
+
+	/* For each regulatory class */
+	for (i = 0; i < num_entry; i++) {
+		if (cur_class_chan_table[i].reg_class == reg_class) {
+			/* For each channel of the regulatory class */
+			for (j = 0; j < cur_class_chan_table[i].total_chan; j++) {
+				for (k = 0; k < num_leg_ap_chan; k++) {
+
+					if (cur_class_chan_table[i].
+					    channels[j] ==
+					    leg_ap_chan_list[k].chan_num) {
+						*(chan_list + idx) =
+							leg_ap_chan_list[k].
+							chan_num;
+						idx++;
+						if (leg_ap_chan_list[k].
+						    is_intol_set)
+							*is_intol_ap_present =
+								TRUE;
+					}
+				}
+			}
+			break;
+		}
+	}
+	/* update the total number of channel */
+	*chan_num = idx--;
+	return;
+}
+
+/********************************************************
+		Global Functions
+********************************************************/
+/**
+ *  @brief Prepare 2040 coex command buffer
+ *  @param buf		   A pointer to the command buffer
+ *  @param chan_list   Channel list
+ *  @param num_of_chan Number of channel present in channel list
+ *  @param reg_class   Regulatory class
+ *  @param is_intol_ap_present   Flag: is any 40 MHz intolerant AP
+ *  				   is present in these chaanel set
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+void
+prepare_coex_cmd_buff(t_u8 *buf, t_u8 *chan_list, t_u8 num_of_chan,
+		      t_u8 reg_class, t_u8 is_intol_ap_present)
+{
+	HostCmd_DS_GEN *hostcmd;
+	MrvlIETypes_2040COEX_t *coex_ie = NULL;
+	MrvlIETypes_2040BssIntolerantChannelReport_t *bss_intol_ie = NULL;
+	t_u8 *pos = NULL;
+	int intol;
+
+	hostcmd = (HostCmd_DS_GEN *)(buf + sizeof(t_u32));
+	hostcmd->command = cpu_to_le16(HostCmd_CMD_11N_2040COEX);
+	hostcmd->size = S_DS_GEN;
+	pos = buf + sizeof(t_u32) + S_DS_GEN;
+	{
+		coex_ie = (MrvlIETypes_2040COEX_t *)pos;
+		coex_ie->header.element_id = TLV_ID_2040COEX;
+		coex_ie->header.len = sizeof(coex_ie->coex_elem);
+		/* Check STA is 40 MHz intolerant or not */
+		is_intolerant_sta(&intol);
+		if (intol)
+			coex_ie->coex_elem |= MBIT(1);
+
+		if (is_intol_ap_present)
+			coex_ie->coex_elem |= MBIT(2);
+		pos += sizeof(MrvlIETypes_2040COEX_t);
+		hostcmd->size += sizeof(MrvlIETypes_2040COEX_t);
+	}
+	{
+		bss_intol_ie =
+			(MrvlIETypes_2040BssIntolerantChannelReport_t *)pos;
+		bss_intol_ie->header.element_id =
+			TLV_ID_2040BSS_INTOL_CHAN_REPORT;
+		hostcmd->size +=
+			sizeof(MrvlIETypes_2040BssIntolerantChannelReport_t) -
+			sizeof(bss_intol_ie->chan_num);
+		bss_intol_ie->reg_class = reg_class;
+		memcpy(bss_intol_ie->chan_num, chan_list, num_of_chan);
+		bss_intol_ie->header.len =
+			sizeof(bss_intol_ie->reg_class) + num_of_chan;
+		hostcmd->size += num_of_chan;
+	}
+	hostcmd->size = cpu_to_le16(hostcmd->size);
+	return;
+}
+
+/**
+ *  @brief Invoke multiple 2040Coex commands for multiple regulatory classes
+ *
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+invoke_coex_command(void)
+{
+	int cur_reg_domain;
+	t_u8 chan_list[MAX_CHAN], is_intol_ap_present;
+	t_u8 num_of_chan;
+	int i, num_entry, ret = MLAN_STATUS_SUCCESS;
+	class_chan_t *cur_class_chan_table = NULL;
+
+    /** get region code */
+	ret = get_region_code(&cur_reg_domain);
+	if (ret != MLAN_STATUS_SUCCESS)
+		return ret;
+    /** Find region_class_chan_table for this region */
+	for (i = 0;
+	     (unsigned int)i <
+	     (sizeof(region_class_chan_table) / sizeof(region_class_chan_t));
+	     i++) {
+		if (region_class_chan_table[i].reg_domain == cur_reg_domain) {
+			cur_class_chan_table =
+				region_class_chan_table[i].class_chan_list;
+			num_entry =
+				region_class_chan_table[i].num_class_chan_entry;
+			break;
+		}
+	}
+	if (cur_class_chan_table == NULL) {
+		printf("No region_class_chan table found for this region\n");
+		return MLAN_STATUS_FAILURE;
+	}
+
+	for (i = 0; i < num_entry; i++) {
+	/** Get channels for the specified regulatory class */
+		get_channels_for_specified_reg_class(cur_class_chan_table,
+						     num_entry, chan_list,
+						     &num_of_chan,
+						     cur_reg_domain,
+						     cur_class_chan_table[i].
+						     reg_class,
+						     &is_intol_ap_present);
+
+	/** If any channel found for this regulatory class, then invoke the 2040coex command */
+		if (num_of_chan > 0) {
+			ret = process_host_cmd(CMD_2040COEX, chan_list,
+					       num_of_chan,
+					       cur_class_chan_table[i].
+					       reg_class, is_intol_ap_present);
+			if (ret)
+				break;
+		}
+	}
+	return ret;
+}
+
+/**
+ *  @brief Process host_cmd response
+ *
+ *  @param cmd_name	The command string
+ *  @param buf		A pointer to the response buffer
+ *
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_host_cmd_resp(char *cmd_name, t_u8 *buf)
+{
+	t_u32 hostcmd_size = 0;
+	HostCmd_DS_GEN *hostcmd = NULL;
+	int ret = MLAN_STATUS_SUCCESS;
+
+	buf += strlen(CMD_MARVELL) + strlen(cmd_name);
+	memcpy((t_u8 *)&hostcmd_size, buf, sizeof(t_u32));
+	buf += sizeof(t_u32);
+
+	hostcmd = (HostCmd_DS_GEN *)buf;
+	hostcmd->command = le16_to_cpu(hostcmd->command);
+	hostcmd->size = le16_to_cpu(hostcmd->size);
+
+	hostcmd->command &= ~HostCmd_RET_BIT;
+	if (!le16_to_cpu(hostcmd->result)) {
+		switch (hostcmd->command) {
+		}
+	} else {
+		printf("HOSTCMD failed: ReturnCode=%#04x, Result=%#04x\n",
+		       le16_to_cpu(hostcmd->command),
+		       le16_to_cpu(hostcmd->result));
+		ret = MLAN_STATUS_FAILURE;
+	}
+	return ret;
+}
diff --git a/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.h b/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.h
new file mode 100644
index 0000000..d264913
--- /dev/null
+++ b/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.h
@@ -0,0 +1,443 @@
+/** @file  mlan2040misc.h
+  *
+  * @brief This file contains command definitions for application
+  *
+  * Copyright (C) 2009-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     06/24/2009: initial version
+************************************************************************/
+#ifndef _COEX_MISC_H_
+#define _COEX_MISC_H_
+
+/** MLAN MAC Address Length */
+#define MLAN_MAC_ADDR_LENGTH     (6)
+/** Size of command buffer */
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+
+/** Command RET code, MSB is set to 1 */
+#define HostCmd_RET_BIT                 0x8000
+/** General purpose action : Get */
+#define HostCmd_ACT_GEN_GET             0x0000
+/** General purpose action : Set */
+#define HostCmd_ACT_GEN_SET             0x0001
+/** TLV Id for 2040Coex IE */
+#define TLV_ID_2040COEX                    0x48
+/** TLV Id for 2040BSS intolarent channel report IE */
+#define TLV_ID_2040BSS_INTOL_CHAN_REPORT   0x49
+/** Host-command for 2040coex command */
+#define HostCmd_CMD_11N_2040COEX           0x00e9
+/** Maximum scan response buffer size */
+#define SCAN_RESP_BUF_SIZE 2000
+
+/** Maximum length of SSID */
+#define MRVDRV_MAX_SSID_LENGTH          32
+
+/** Length of ethernet address */
+#ifndef ETH_ALEN
+#define ETH_ALEN            6
+#endif
+/** Maximum length of SSID list */
+#define MRVDRV_MAX_SSID_LIST_LENGTH         10
+/** Default scan interval in second*/
+#define DEFAULT_SCAN_INTERVAL 300
+
+/** BIT value */
+#define MBIT(x)    (((t_u32)1) << (x))
+
+/** Check intolerent bit set */
+#define IS_INTOL_BIT_SET(cap_info) (cap_info & MBIT(14))
+
+/** Check OBSS non-HT STAs present bit set */
+#define IS_NON_HT_STA_PRESENT(ht_info) (ht_info.field3 & MBIT(4))
+
+/** IEEE Type definitions  */
+typedef enum _IEEEtypes_ElementId_e {
+	SSID = 0,
+	SUPPORTED_RATES = 1,
+	FH_PARAM_SET = 2,
+	DS_PARAM_SET = 3,
+	CF_PARAM_SET = 4,
+	IBSS_PARAM_SET = 6,
+	HT_CAPABILITY = 45,
+	HT_OPERATION = 61,
+	BSSCO_2040 = 72,
+	OVERLAPBSSSCANPARAM = 74,
+	EXT_CAPABILITY = 127,
+	ERP_INFO = 42,
+	EXTENDED_SUPPORTED_RATES = 50,
+	VENDOR_SPECIFIC_221 = 221,
+	WMM_IE = VENDOR_SPECIFIC_221,
+	RSN_IE = 48,
+} __attribute__ ((packed))
+     IEEEtypes_ElementId_e;
+
+/** HT Capabilities Data */
+     typedef struct _HTCap_t {
+    /** HT Capabilities Info field */
+	     t_u16 ht_cap_info;
+    /** A-MPDU Parameters field */
+	     t_u8 ampdu_param;
+    /** Supported MCS Set field */
+	     t_u8 supported_mcs_set[16];
+    /** HT Extended Capabilities field */
+	     t_u16 ht_ext_cap;
+    /** Transmit Beamforming Capabilities field */
+	     t_u32 tx_bf_cap;
+    /** Antenna Selection Capability field */
+	     t_u8 asel;
+    /** Reserved set to 0 */
+	     t_u16 reserved;
+     } __attribute__ ((packed))
+     HTCap_t, *pHTCap_t;
+
+/** HT Information Data */
+     typedef struct _HTInfo_t {
+    /** Primary channel */
+	     t_u8 pri_chan;
+    /** Field 2 */
+	     t_u8 field2;
+    /** Field 3 */
+	     t_u16 field3;
+    /** Field 4 */
+	     t_u16 field4;
+    /** Bitmap indicating MCSs supported by all HT STAs in the BSS */
+	     t_u8 basic_mcs_set[16];
+    /** Reserved set to 0 */
+	     t_u16 reserved;
+     } __attribute__ ((packed))
+     HTInfo_t, *pHTInfo_t;
+
+/** 20/40 BSS Coexistence Data */
+     typedef struct _BSSCo2040_t {
+    /** 20/40 BSS Coexistence value */
+	     t_u8 bss_co_2040_value;
+    /** Reserve field */
+	     t_u8 reserved[3];
+     } __attribute__ ((packed))
+     BSSCo2040_t, *pBSSCo2040_t;
+
+/** Extended Capabilities Data */
+     typedef struct _ExtCap_t {
+    /** Extended Capabilities value */
+	     t_u8 ext_cap_value;
+    /** Reserved field */
+	     t_u8 reserved[3];
+     } __attribute__ ((packed))
+     ExtCap_t, *pExtCap_t;
+
+/** Overlapping BSS Scan Parameters Data */
+     typedef struct _OverlapBSSScanParam_t {
+    /** OBSS Scan Passive Dwell */
+	     t_u16 obss_scan_passive_dwell;
+    /** OBSS Scan Active Dwell */
+	     t_u16 obss_scan_active_dwell;
+    /** BSS Channel Width Trigger Scan Interval */
+	     t_u16 bss_chan_width_trigger_scan_int;
+    /** OBSS Scan Passive Total Per Channel */
+	     t_u16 obss_scan_passive_total;
+    /** OBSS Scan Active Total Per Channel */
+	     t_u16 obss_scan_active_total;
+    /** BSS Width Channel Transition Delay Factor */
+	     t_u16 bss_width_chan_trans_delay;
+    /** OBSS Scan Activity Threshold */
+	     t_u16 obss_scan_active_threshold;
+     } __attribute__ ((packed))
+     OBSSScanParam_t, *pOBSSScanParam_t;
+
+/** IEEEtypes_CapInfo_t structure*/
+     typedef struct _IEEEtypes_CapInfo_t {
+    /** Capability Bit Map : ESS */
+	     t_u8 ess:1;
+    /** Capability Bit Map : IBSS */
+	     t_u8 ibss:1;
+    /** Capability Bit Map : CF pollable */
+	     t_u8 cf_pollable:1;
+    /** Capability Bit Map : CF poll request */
+	     t_u8 cf_poll_rqst:1;
+    /** Capability Bit Map : privacy */
+	     t_u8 privacy:1;
+    /** Capability Bit Map : Short preamble */
+	     t_u8 short_preamble:1;
+    /** Capability Bit Map : PBCC */
+	     t_u8 pbcc:1;
+    /** Capability Bit Map : Channel agility */
+	     t_u8 chan_agility:1;
+    /** Capability Bit Map : Spectrum management */
+	     t_u8 spectrum_mgmt:1;
+    /** Capability Bit Map : Reserved */
+	     t_u8 rsrvd3:1;
+    /** Capability Bit Map : Short slot time */
+	     t_u8 short_slot_time:1;
+    /** Capability Bit Map : APSD */
+	     t_u8 apsd:1;
+    /** Capability Bit Map : Reserved */
+	     t_u8 rsvrd2:1;
+    /** Capability Bit Map : DSS OFDM */
+	     t_u8 dsss_ofdm:1;
+    /** Capability Bit Map : Reserved */
+	     t_u8 rsrvd1:2;
+     } __attribute__ ((packed))
+     IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t;
+
+     typedef struct {
+	     t_u8 chan_number;
+		       /**< Channel Number to scan */
+	     t_u8 radio_type;
+		       /**< Radio type: 'B/G' Band = 0, 'A' Band = 1 */
+	     t_u8 scan_type;
+		       /**< Scan type: Active = 1, Passive = 2 */
+	     t_u8 reserved;
+		      /**< Reserved */
+	     t_u32 scan_time;
+		       /**< Scan duration in milliseconds; if 0 default used */
+     } __attribute__ ((packed))
+     wlan_ioctl_user_scan_chan;
+
+     typedef struct {
+	     char ssid[MRVDRV_MAX_SSID_LENGTH + 1];
+					    /**< SSID */
+	     t_u8 max_len;		       /**< Maximum length of SSID */
+     } __attribute__ ((packed))
+     wlan_ioctl_user_scan_ssid;
+
+     typedef struct {
+
+    /** Flag set to keep the previous scan table intact */
+	     t_u8 keep_previous_scan;	/* Do not erase the existing scan results */
+
+    /** BSS mode to be sent in the firmware command */
+	     t_u8 bss_mode;
+
+    /** Configure the number of probe requests for active chan scans */
+	     t_u8 num_probes;
+
+    /** Reserved */
+	     t_u8 reserved;
+
+    /** BSSID filter sent in the firmware command to limit the results */
+	     t_u8 specific_bssid[ETH_ALEN];
+    /** SSID filter list used in the to limit the scan results */
+	     wlan_ioctl_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH];
+
+    /** Variable number (fixed maximum) of channels to scan up */
+	     wlan_ioctl_user_scan_chan chan_list[WLAN_IOCTL_USER_SCAN_CHAN_MAX];
+
+     } __attribute__ ((packed))
+     wlan_ioctl_user_scan_cfg;
+
+/** IEEE IE header */
+     typedef struct _IEEEtypes_Header_t {
+    /** Element ID */
+	     t_u8 element_id;
+    /** Length */
+	     t_u8 len;
+     } __attribute__ ((packed))
+     IEEEtypes_Header_t, *pIEEEtypes_Header_t;
+
+/** HT Capabilities IE */
+     typedef struct _IEEEtypes_HTCap_t {
+    /** Generic IE header */
+	     IEEEtypes_Header_t ieee_hdr;
+    /** HTCap struct */
+	     HTCap_t ht_cap;
+     } __attribute__ ((packed))
+     IEEEtypes_HTCap_t, *pIEEEtypes_HTCap_t;
+
+/** HT Information IE */
+     typedef struct _IEEEtypes_HTInfo_t {
+    /** Generic IE header */
+	     IEEEtypes_Header_t ieee_hdr;
+    /** HTInfo struct */
+	     HTInfo_t ht_info;
+     } __attribute__ ((packed))
+     IEEEtypes_HTInfo_t, *pIEEEtypes_HTInfo_t;
+
+/** 20/40 BSS Coexistence IE */
+     typedef struct _IEEEtypes_2040BSSCo_t {
+    /** Generic IE header */
+	     IEEEtypes_Header_t ieee_hdr;
+    /** BSSCo2040_t struct */
+	     BSSCo2040_t bss_co_2040;
+     } __attribute__ ((packed))
+     IEEEtypes_2040BSSCo_t, *pIEEEtypes_2040BSSCo_t;
+
+/** Extended Capabilities IE */
+     typedef struct _IEEEtypes_ExtCap_t {
+    /** Generic IE header */
+	     IEEEtypes_Header_t ieee_hdr;
+    /** ExtCap_t struct */
+	     ExtCap_t ext_cap;
+     } __attribute__ ((packed))
+     IEEEtypes_ExtCap_t, *pIEEEtypes_ExtCap_t;
+
+/** Overlapping BSS Scan Parameters IE */
+     typedef struct _IEEEtypes_OverlapBSSScanParam_t {
+    /** Generic IE header */
+	     IEEEtypes_Header_t ieee_hdr;
+    /** OBSSScanParam_t struct */
+	     OBSSScanParam_t obss_scan_param;
+     } __attribute__ ((packed))
+     IEEEtypes_OverlapBSSScanParam_t, *pIEEEtypes_OverlapBSSScanParam_t;
+
+     typedef struct _wlan_get_scan_table_fixed {
+    /** BSSID of this network */
+	     t_u8 bssid[MLAN_MAC_ADDR_LENGTH];
+    /** Channel this beacon/probe response was detected */
+	     t_u8 channel;
+    /** RSSI for the received packet */
+	     t_u8 rssi;
+    /** TSF value from the firmware at packet reception */
+	     t_u64 network_tsf;
+     } wlan_get_scan_table_fixed;
+
+/**
+ *  Structure passed in the wlan_ioctl_get_scan_table_info for each
+ *    BSS returned in the WLAN_GET_SCAN_RESP IOCTL
+ */
+     typedef struct _wlan_ioctl_get_scan_table_entry {
+    /**
+     *  Fixed field length included in the response.
+     *
+     *  Length value is included so future fixed fields can be added to the
+     *   response without breaking backwards compatibility.  Use the length
+     *   to find the offset for the bssInfoLength field, not a sizeof() calc.
+     */
+	     t_u32 fixed_field_length;
+
+    /**
+     *  Length of the BSS Information (probe resp or beacon) that
+     *    follows starting at bssInfoBuffer
+     */
+	     t_u32 bss_info_length;
+
+    /**
+     *  Always present, fixed length data fields for the BSS
+     */
+	     wlan_get_scan_table_fixed fixed_fields;
+
+	     /*
+	      *  Probe response or beacon scanned for the BSS.
+	      *
+	      *  Field layout:
+	      *   - TSF              8 octets
+	      *   - Beacon Interval  2 octets
+	      *   - Capability Info  2 octets
+	      *
+	      *   - IEEE Infomation Elements; variable number & length per 802.11 spec
+	      */
+	     /* t_u8 bss_info_buffer[1]; */
+     } wlan_ioctl_get_scan_table_entry;
+
+/**
+ *  Sructure to retrieve the scan table
+ */
+     typedef struct {
+    /**
+     *  - Zero based scan entry to start retrieval in command request
+     *  - Number of scans entries returned in command response
+     */
+	     t_u32 scan_number;
+    /**
+     * Buffer marker for multiple wlan_ioctl_get_scan_table_entry structures.
+     *   Each struct is padded to the nearest 32 bit boundary.
+     */
+	     t_u8 scan_table_entry_buf[1];
+     } wlan_ioctl_get_scan_table_info;
+
+/* Define general hostcmd data structure */
+/** HostCmd_DS_GEN */
+     typedef struct _HostCmd_DS_GEN {
+    /** Command */
+	     t_u16 command;
+    /** Size */
+	     t_u16 size;
+    /** Sequence number */
+	     t_u16 seq_num;
+    /** Result */
+	     t_u16 result;
+     } __attribute__ ((packed))
+     HostCmd_DS_GEN;
+
+/** Size of HostCmd_DS_GEN */
+#define S_DS_GEN    sizeof(HostCmd_DS_GEN)
+
+/** TLV related data structures*/
+/** MrvlIEtypesHeader_t */
+     typedef struct _MrvlIEtypesHeader {
+    /** Header type */
+	     t_u16 type;
+    /** Header length */
+	     t_u16 len;
+     } __attribute__ ((packed))
+     MrvlIEtypesHeader_t;
+
+/** _MrvlIETypes_2040BssIntolerantChannelReport_t */
+     typedef struct _MrvlIETypes_2040BssIntolerantChannelReport_t {
+    /** Header */
+	     IEEEtypes_Header_t header;
+    /** regulatory class */
+	     t_u8 reg_class;
+    /** channel numbers for legacy AP */
+	     t_u8 chan_num[1];
+     } __attribute__ ((packed))
+     MrvlIETypes_2040BssIntolerantChannelReport_t;
+
+/** MrvlIETypes_2040COEX_t */
+     typedef struct _MrvlIETypes_2040COEX_t {
+    /** Header */
+	     IEEEtypes_Header_t header;
+    /** 2040 coex element */
+	     t_u8 coex_elem;
+     } __attribute__ ((packed))
+     MrvlIETypes_2040COEX_t;
+
+     typedef struct _HostCmd_DS_CMD_11N_2040COEX {
+	/** 2040 coex element */
+	     MrvlIETypes_2040COEX_t coex;
+	/** 2040 BSS intolerant channel report*/
+	     MrvlIETypes_2040BssIntolerantChannelReport_t chan_intol_report;
+     } __attribute__ ((packed))
+     HostCmd_DS_CMD_11N_2040COEX;
+
+/** Maximum number of channel per regulatory class */
+#define MAX_CHAN 20
+     typedef struct _class_chan_t {
+	/** Regulatory class */
+	     t_u8 reg_class;
+	/** Channel numbers */
+	     t_u8 channels[MAX_CHAN];
+	/** Total number of channels */
+	     t_u8 total_chan;
+     } class_chan_t;
+
+     typedef struct _region_class_chan_t {
+    /** Regulatory domain */
+	     int reg_domain;
+    /** Channel numbers */
+	     class_chan_t *class_chan_list;
+    /** Number of class channel table entry */
+	     int num_class_chan_entry;
+     } region_class_chan_t;
+
+     int process_host_cmd_resp(char *cmd_name, t_u8 *buf);
+     void prepare_coex_cmd_buff(t_u8 *buf, t_u8 *chan_list, t_u8 num_of_chan,
+				t_u8 reg_class, t_u8 is_intol_ap_present);
+     int invoke_coex_command(void);
+
+#endif /* _COEX_MISC_H_ */
diff --git a/wlan_sd8897/mapp/mlanconfig/Makefile b/wlan_sd8897/mapp/mlanconfig/Makefile
new file mode 100644
index 0000000..ba6794a
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/Makefile
@@ -0,0 +1,48 @@
+#
+# File : mlanconfig/Makefile
+#
+# Copyright (C) 2008-2017, Marvell International Ltd. All Rights Reserved
+
+# Path to the top directory of the mlandriver distribution
+PATH_TO_TOP = ../..
+
+# Determine how we should copy things to the install directory
+ABSPATH := $(filter /%, $(INSTALLDIR))
+RELPATH := $(filter-out /%, $(INSTALLDIR))
+INSTALLPATH := $(ABSPATH)
+ifeq ($(strip $(INSTALLPATH)),)
+INSTALLPATH := $(PATH_TO_TOP)/$(RELPATH)
+endif
+
+# Override CFLAGS for application sources, remove __ kernel namespace defines
+CFLAGS := $(filter-out -D__%, $(ccflags-y))
+# remove KERNEL include dir
+CFLAGS := $(filter-out -I$(KERNELDIR)%, $(CFLAGS))
+
+#
+# List of application executables to create
+#
+libobjs:= mlanconfig.o mlanhostcmd.o mlanmisc.o
+exectarget=mlanconfig
+TARGETS := $(exectarget)
+
+#
+# Make target rules
+#
+
+# All rule compiles list of TARGETS using builtin program target from src rule
+all :
+$(exectarget): $(libobjs)
+	$(CC) $(CFLAGS) $(libobjs) -o $(exectarget)
+
+# Update any needed TARGETS and then copy to the install path
+build install: $(TARGETS)
+	@cp -rf config $(INSTALLPATH)
+
+clean:
+	@rm -f $(exectarget)
+	@rm -f *.o
+
+distclean: clean
+	@rm -f *~ core
+	@rm -f tags
diff --git a/wlan_sd8897/mapp/mlanconfig/config/11n_2040coex.conf b/wlan_sd8897/mapp/mlanconfig/config/11n_2040coex.conf
new file mode 100644
index 0000000..1e8cda7
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/11n_2040coex.conf
@@ -0,0 +1,21 @@
+#	File : 11n_2040coex.conf
+
+######################### 20/40 Coex command ##################
+2040coex={
+    CmdCode=0x00e9  # do NOT change this line
+
+    2040CoexTlvType:1=0x48
+    2040CoexTlvLen:1={
+        2040CoexElement:1=0x04
+    }
+
+    2040BssIntlChanTlvType:1=0x49
+    2040BssIntlChanTlvLen:1={
+        RegulatoryDomain:1=32   # USA: 32 (1-7), 33 (5-11)
+        ChannelNum:1=1
+        ChannelNum:1=2
+        #  ...
+    }
+}
+
+##################################################################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/arpfilter.conf b/wlan_sd8897/mapp/mlanconfig/config/arpfilter.conf
new file mode 100644
index 0000000..a50b089
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/arpfilter.conf
@@ -0,0 +1,29 @@
+#	File : arpfilter.conf
+
+######################### Host Sleep ARP/IP filtering command ##################
+# add arp filter
+# firmware supports 8 entries of ARP_FILTER. Each entry has 8 bytes.
+# must not exceed 8x8+4=68 bytes
+
+arpfilter={
+   TlvType:2=0x0115
+   TlvLength:2={
+      AddrType:2=3              # multicast
+      EthType:2=0xffff          # Any
+      Ipv4Addr:4=0xffffffff     # Any
+      AddrType:2=1              # broadcast
+      EthType:2=0x0608          # ARP: 0x0806
+      Ipv4Addr:4=0x6200a8c0     # 192.168.0.98
+      AddrType:2=2              # unicast
+      EthType:2=0xffff          # Any
+      Ipv4Addr:4=0xffffffff     # Any
+    }
+}
+
+# remove arp filter
+#arpfilter={
+#	TlvType:2=0x0115
+#	TlvLength:2={
+#	}
+#}
+######################### Host Sleep ARP/IP filtering command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/auto_tx.conf b/wlan_sd8897/mapp/mlanconfig/config/auto_tx.conf
new file mode 100644
index 0000000..6c35540
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/auto_tx.conf
@@ -0,0 +1,54 @@
+#	File : auto_tx.conf
+
+######################### Auto-TX command ##################
+auto_tx_get={
+	CmdCode=0x0082	# do NOT change this line
+
+	Action:2=0		# GET
+}
+
+auto_tx_unreg={
+	CmdCode=0x0082	# do NOT change this line
+
+	Action:2=1		# SET
+}
+
+nat_keep_alive={
+	CmdCode=0x0082	# do NOT change this line
+
+	Action:2=1		# SET
+
+	AutoTxTlvType:2=0x0118
+	AutoTxTlvLength:2={		# 58 = 6 + 52 (FrameLen)
+		Interval:2=2		# 1 - 3600 seconds
+		Priority:1=7		# Priority, ignored if non-WMM
+		Reserved:1=0
+		FrameLength:2={		# 52 = 6 (DA) + 6 (SA) + 2 + 38 (Length)
+			DestMacAddr:6='0x00,0x40,0xf4,0xbf,0x24,0xee'
+			SrcMacAddr:6='0x00,0x00,0x00,0x00,0x00,0x00'
+			Length:2='0x00,38'	# 38 = 8 (SNAP hdr) + 29 (IP) + 1 (padding)
+			DSAP:1=0xaa			# SNAP header
+			SSAP:1=0xaa
+			Ctrl:1=0x03
+			SNAP_OUI:3='0x00,0x00,0x00'
+			SNAP_PID:2='0x08,0x00'		# IP Packet
+			IPv4:1=0x45
+			IP_TOS:1=0x00
+			IP_LEN:2='0x00,29'		# IP hdr 20 + payload 9 = 29
+			IP_ID:2=0xefbe
+			IP_Flag_FragOffset:2=0x0000
+			IP_TTL:1=128
+			IP_Prot:1=17		  		# UDP protocol
+			IPhdr_cksum:2=0xc5f9		# may need re-calculation if changed
+			IPsrcAddr:4='192,168,0,201'	# 192.168.0.201
+			IPdstAddr:4='192,168,0,1'	# 192.168.0.1
+			UDPsrcPort:2='0x11,0x94'	# 4500
+			UDPdstPort:2='0x11,0x94'	# 4500
+			UDPlength:2='0x00,9'		# UDP hdr 8 + payload 1 = 9
+			UDPcksum:2=0x985b			# may need re-calculation if changed
+			UDPpayload:1=0xff
+			padding:1=0					# MAC Padding for 32bit alignment, set to 0
+		}
+	}
+}
+######################### Auto-TX command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/bg_scan.conf b/wlan_sd8897/mapp/mlanconfig/config/bg_scan.conf
new file mode 100644
index 0000000..e174936
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/bg_scan.conf
@@ -0,0 +1,157 @@
+#	File : bg_scan.conf
+
+######################### BG Scan Configuration command ##################
+########### Sample configuration for Get BG Scan Configuration #####################
+#bgscfg={
+#	CmdCode=0x006b          # do NOT change this line
+#	Action:1=0              # 0- Get, 1- Set
+#	ConfigType:1=0          # 0- normal BG Scan config, 1-PPS or UAPSD BG Scan config
+#	Enable:1=1              # 0- Disable, 1-Enable
+#	BssType:1=0             # 1 - Infrastructure,2 - IBSS,3 - Any
+#	ChannelsPerScan:1=0     # Number of Channel to scan at one scan; maximum 14
+#	Reserved1:3=0
+#	ScanInterval:4=0        # Interval between consecutive scan (in milliseconds)
+#	Reserved2:4=0
+#	ReportConditions:4=0    # bit0  - SSID match
+	                        # bit1  - SNR above SNR threshold
+	                        # bit2  - RSSI above RSSI threshold
+	                        # bit31 - All channels scanned at least once
+#	Reserved3:2=0
+#}
+
+########### SET BG Scan Configuration #####################
+bgscfg={
+	CmdCode=0x006b          # do NOT change this line
+	Action:1=1              # 0- Get, 1- Set
+	ConfigType:1=0          # 0- normal BG Scan config, 1-PPS or UAPSD BG Scan config
+	Enable:1=1              # 0- Disable, 1-Enable
+	BssType:1=3             # 1 - Infrastructure,2 - IBSS,3 - Any
+	ChannelsPerScan:1=14    # Number of Channel to scan at one scan; maximum 14
+	Reserved1:3=0
+	ScanInterval:4=1000     # Interval between consecutive scan (in milliseconds)
+	Reserved2:4=0
+	ReportConditions:4=1  	# bit0  - SSID match
+	                      	# bit1  - SNR above SNR threshold
+	                      	# bit2  - RSSI above RSSI threshold
+	                      	# bit31 - All channels scanned at least once
+	Reserved3:2=0
+
+	# SSID parameter set:
+	#
+	# MaxSSIDLen entries:
+	#
+	# 1. MaxSSIDLen:1=0x00      - to denote match AP name exactly,
+	#                             generate SSID specific probes
+	# 2. MaxSSIDLen:1=maxlen    - to denote AP name will be use to base match the
+	#                             SSID and SSID's max length is 'maxlen',
+	#                             do not generate SSID specific probes
+	# 3. MaxSSIDLen:1=wildcard match char ('*' or '?')
+	#                           - to denote wildcard AP name will be use to match the SSID
+	# 4. MaxSSIDLen:1=0xff      - to denote unix pattern matching
+	#
+	# SSID entries:
+	#
+	# SSID="AP_NAME"            - to mention the SSID to match
+
+	# SSID Examples:
+	#
+	#
+	# Match SSID name "MarvellAP" exactly, generate SSID specific probes
+	#
+	SSIDHeaderType:2=0x0112
+	SSIDHeaderLen:2={
+		MaxSSIDLen:1=0x00
+		SSID:9="MarvellAP"
+	}
+
+	#
+	# MarvellAP will be use to base match the SSID and SSID's max length is 12
+	#
+#	SSIDHeaderType:2=0x0112
+#	SSIDHeaderLen:2={
+#		MaxSSIDLen:1=0x0c
+#		SSID:9="MarvellAP"
+#	}
+
+	#
+	# Match "MarvellAP*" where '*' is a single char
+	#
+#	SSIDHeaderType:2=0x0112
+#	SSIDHeaderLen:2={
+#		MaxSSIDLen:1='*'
+#		SSID:10="MarvellAP*"
+#	}
+
+	#
+	# Match "Mar?ell*" with unix pattern matching
+	#
+#	SSIDHeaderType:2=0x0112
+#	SSIDHeaderLen:2={
+#		MaxSSIDLen:1=0xff     # For unix pattern matching
+#		SSID:8="Mar?ell*"
+#	}
+
+	# Number Probe requests to be sent for broadcast and
+	# for each SSID specific scan required.
+	#
+	# If any SSID in the list has a non-zero modifier (wildcard match char,
+	# unix pattern match, maxlen), "Numprobes" of broadcast probe requests
+	# will be transmitted once per channel and the results matched against
+	# all entries.
+	#
+	# Set to 0 to use global scan probes setting
+	#
+	ProbeHeaderType:2=0x0102
+	ProbeHeaderLen:2={
+		NumProbes:2=2
+	}
+
+	# ChannelList contains the channels to scan
+	# The ChannelList should be specified in the form of
+	#
+	#     RadioType, ChanNumber, ScanType, MinScanTime, ScanTime;
+	#
+	# RadioType - 0 [B/G Band], 1 [A Band]
+	# ScanType  - 2 [Active],   3 [Passive]
+	#
+
+	ChannHeaderType:2=0x0101
+	ChannHeaderLen:2={
+		Chan1_RadioType:1=0
+		Chan1_ChanNumber:1=10
+		Chan1_ScanType:1=2
+		Chan1_MinScanTime:2=10
+		Chan1_ScanTime:2=100
+
+		Chan2_RadioType:1=0
+		Chan2_ChanNumber:1=6
+		Chan2_ScanType:1=3
+		Chan2_MinScanTime:2=10
+		Chan2_ScanTime:2=100
+	}
+
+	# SNR threshold used when ReportConditions bit1 is set
+	SNRHeaderType:2=0x0105
+	SNRHeaderLen:2={
+		SNRValue:1=40 	#SNR Thereshold Value
+		SNRFreq:1=0
+	}
+
+	# RSSI threshold used when ReportConditions bit2 is set
+	#
+	# Threshold is absolute value and match value would
+	#   therefore be less than or equal to trigger a report
+	RSSIHeaderType:2=0x0104
+	RSSIHeaderLen:2={
+		RSSIValue:1=50 	#RSSI Thereshold Value
+		RSSIFreq:1=0
+	}
+
+	# StartLaterValue: 0 - BGScan start immediately
+	# 1 - BGScan will start later after "Scan Interval"
+	StartLaterHeaderType:2=0x011e
+	StartLaterHeaderLen:2={
+		StartLaterValue:2=0
+	}
+}
+######################### BG Scan Configuration command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/bg_scan_wifidirect.conf b/wlan_sd8897/mapp/mlanconfig/config/bg_scan_wifidirect.conf
new file mode 100644
index 0000000..f480a9c
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/bg_scan_wifidirect.conf
@@ -0,0 +1,88 @@
+#	File : bg_scan_wifidirect.conf
+
+######################### BG Scan Configuration command ##################
+########### SET BG Scan Configuration #####################
+bgscfg={
+	CmdCode=0x006b          # do NOT change this line
+	Action:1=1              # 0- Get, 1- Set
+	ConfigType:1=0          # 0- normal BG Scan config, 1-PPS or UAPSD BG Scan config
+	Enable:1=1              # 0- Disable, 1-Enable
+	BssType:1=3             # 1 - Infrastructure,2 - IBSS,3 - Any
+	ChannelsPerScan:1=3     # Number of Channel to scan at one scan; maximum 14
+	Reserved1:3=0
+	ScanInterval:4=1000     # Interval between consecutive scan (in milliseconds)
+	StoreCondition:4=1      # 1 - SSID match (bit 0)
+	                        # 2 - SSID match AND SNR above SNR threshold (bit 1)
+	ReportConditions:4=1  	# 1 - SSID match (bit 0)
+	                        # 2 - SSID match AND SNR above SNR threshold (bit 1)
+	Reserved3:2=0
+
+	# SSID parameter set:
+	#
+	SSIDHeaderType:2=0x0112
+	SSIDHeaderLen:2={
+		MaxSSIDLen:1=0x00
+		SSID:7="DIRECT-"
+	}
+
+	# Number Probe requests to be sent for broadcast and
+	# for each SSID specific scan required.
+	#
+	# If any SSID in the list has a non-zero modifier (wildcard match char,
+	# unix pattern match, maxlen), "Numprobes" of broadcast probe requests
+	# will be transmitted once per channel and the results matched against
+	# all entries.
+	#
+	# Set to 0 to use global scan probes setting
+	#
+	ProbeHeaderType:2=0x0102
+	ProbeHeaderLen:2={
+		NumProbes:2=2
+	}
+
+	# ChannelList contains the channels to scan
+	# The ChannelList should be specified in the form of
+	#
+	#     RadioType, ChanNumber, ScanType, MinScanTime, ScanTime;
+	#
+	# RadioType - 0 [B/G Band], 1 [A Band]
+	# ScanType  - 2 [Active],   3 [Passive]
+	#
+
+	ChannHeaderType:2=0x0101
+	ChannHeaderLen:2={
+		Chan1_RadioType:1=0
+		Chan1_ChanNumber:1=1
+		Chan1_ScanType:1=2
+		Chan1_MinScanTime:2=10
+		Chan1_ScanTime:2=100
+
+		Chan2_RadioType:1=0
+		Chan2_ChanNumber:1=6
+		Chan2_ScanType:1=2
+		Chan2_MinScanTime:2=10
+		Chan2_ScanTime:2=100
+
+		Chan3_RadioType:1=0
+		Chan3_ChanNumber:1=11
+		Chan3_ScanType:1=2
+		Chan3_MinScanTime:2=10
+		Chan3_ScanTime:2=100
+	}
+
+	# SNR threshold to match, when StoreCondition
+	#     or ReportConditions been set to 2
+	SNRHeaderType:2=0x0105
+	SNRHeaderLen:2={
+		SNRValue:1=40 	#SNR Thereshold Value
+		SNRFreq:1=0
+	}
+
+	# StartLaterValue: 0 - BGScan start immediately
+	# 1 - BGScan will start later after "Scan Interval"
+	StartLaterHeaderType:2=0x011e
+	StartLaterHeaderLen:2={
+		StartLaterValue:2=0
+	}
+}
+######################### BG Scan Configuration command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/cal_data.conf b/wlan_sd8897/mapp/mlanconfig/config/cal_data.conf
new file mode 100644
index 0000000..e37f674
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/cal_data.conf
@@ -0,0 +1,42 @@
+01 00 0c 00 58 02
+00 40 68 0c 00 00 00 40 00 00 00 00 00 11 00 00
+00 11 00 10 00 00 00 00 00 00 00 00 00 00 00 00
+10 12 00 10 10 86 40 89 01 03 02 00 01 02 05 00
+01 03 05 00 17 17 00 05 00 00 00 00 00 00 00 00
+00 30 1f 11 00 00 00 70 00 00 00 00 13 00 1e 01
+00 1e 5e 15 29 5e 15 13 5c 1d 0d 0b 1d 0d 0b 29
+0d 0b 29 5c 0b 29 5c 1d 00 5c 1d 0d 00 00 00 00
+00 5c c0 0e 00 00 00 cc 00 5f 00 00 07 01 04 00
+00 00 0e 0d 00 00 00 00 00 00 00 00 00 00 00 ff
+00 00 00 01 00 00 00 00 00 00 00 ff 00 00 00 01
+00 00 00 00 00 00 00 ff 00 00 00 01 00 00 00 00
+00 00 00 ff 00 00 00 01 00 00 00 00 06 3c 06 3d
+00 00 00 00 00 00 00 00 00 00 00 00
+00 5c dc 25 00 00 01 28 00 6f 00 00 07 01 04 00
+00 00 0e 0d 00 00 00 00 00 00 00 00 00 08 00 07
+00 00 00 09 00 00 00 00 00 08 00 07 00 00 00 09
+00 00 00 00 00 08 00 07 00 00 00 09 00 00 00 00
+00 08 00 07 00 00 00 09 00 00 00 00 06 3c 06 3d
+00 00 00 00 00 00 00 00 00 00 00 00
+00 14 9f 1f 00 00 01 3c 03 00 00 00 00 f1 0a f1
+00 fb 0d fb
+00 20 dd 28 00 00 01 5c 08 86 00 88 ff 06 b1 05
+24 24 3c 42 00 00 24 18 a4 24 bc bc 3d 00 a0 8f
+00 14 00 2a 00 00 01 70 00 00 30 00 01 05 1b 00
+00 00 00 01
+00 74 2c 10 00 00 01 e4 00 00 00 00 09 6a 09 b0
+0b 12 00 6c 04 0a 00 6c 03 03 00 6c 03 03 00 6c
+3f ff ff 00 3f ff ff 01 3f ff ff 02 3f ff ff 03
+15 00 00 04 17 00 00 05 19 00 00 06 1b 00 00 07
+1d 00 00 08 1f 00 00 09 21 00 00 0a 23 00 00 0b
+25 00 00 0c 28 00 00 0d 2a 00 00 0e 2d 00 00 0f
+2f 00 00 10 32 00 00 11 34 00 00 12 3f ff ff 13
+3f ff ff 14
+00 74 84 10 ff ff ff ff 01 00 00 00 09 b0 09 ba
+0a 0f 00 6c 04 09 00 6c 03 03 00 6c 03 03 00 6c
+3f ff ff 00 3f ff ff 01 3f ff ff 02 3f ff ff 03
+15 00 00 04 17 00 00 05 1a 00 00 06 1c 00 00 07
+1f 00 00 08 21 00 00 09 23 00 00 0a 26 00 00 0b
+2a 00 00 0c 2d 00 00 0d 31 00 00 0e 34 00 00 0f
+3f ff ff 10 3f ff ff 11 3f ff ff 12 3f ff ff 13
+3f ff ff 14
diff --git a/wlan_sd8897/mapp/mlanconfig/config/crypto_test.conf b/wlan_sd8897/mapp/mlanconfig/config/crypto_test.conf
new file mode 100644
index 0000000..9b911d2
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/crypto_test.conf
@@ -0,0 +1,57 @@
+#	File : crypto_test.conf
+
+######################### crypto_test command configuration ##################
+
+crypto_test={
+	CmdCode=0x0078          # do NOT change this line
+	#EncDec: 0-Decrypt, 1-Encrypt
+	EncDec:2=0
+	#Algorithm: 1-RC4, 2-AES, 3-AES_KEY_WRAP
+	Algorithm:2=1
+	#KeyIVLength: Length of KeyIV (bytes)
+	KeyIVLength:2=8
+	#KeyIV: Key IV
+	KeyIV:32='0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11'
+	#KeyLength: Length of Key (bytes)
+	KeyLength:2=16
+	#Key: Key
+	Key:32='0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22'
+	#DataType: DataType
+	DataType:2=0x0111
+	#DataLength: Data Length
+	DataLength:2={
+		#Data: Data
+		Data:8='0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33'
+	}
+}
+
+#####Sample crypto_test command configuration for AES-CCM algorithm #########
+
+#crypto_test={
+#	CmdCode=0x0078          # do NOT change this line
+#	#EncDec: 0-Decrypt, 1-Encrypt
+#	EncDec:2=1
+#	#Algorithm: 4-AES-CCM
+#	Algorithm:2=4
+#	#KeyLength: Length of Key (bytes)
+#	KeyLength:2=16
+#	#Key: Key
+#	Key:32='0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22'
+#	#NonceLength: Length of Nonce (bytes)
+#	NonceLength:2=10
+#	#Nonce: Nonce
+#	Nonce:14='0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11'
+#	#AADLength: Length of AAD (bytes)
+#	AADLength:2=12
+#	#AAD: AAD
+#	AAD:32='0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33'
+#	#DataType: DataType
+#	DataType:2=0x0111
+#	#DataLength: Data Length
+#	DataLength:2={
+#		#Data: Data
+#		Data:8='0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33'
+#	}
+#}
+
+######################### End of crypto_test configuration command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/cwmode.conf b/wlan_sd8897/mapp/mlanconfig/config/cwmode.conf
new file mode 100644
index 0000000..1f245a2
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/cwmode.conf
@@ -0,0 +1,51 @@
+#CW_MODE settings configuration
+#
+CW_MODE={
+#Mode of operation 0: Disable current Tx Mode, 1: Continuous Tx Packet, 2: Continuous Wave/Tone
+Mode=1
+#Channel number
+Channel=6
+#channel Info
+#bit [3:0] - channel offset 0: no secondary channel, 1: secondary channel above, 2: reserved, 3: secondary channel below
+#bit [4:7] - channel band   0: 2.4G, 1: 5G, 2: 4G, 3: reserved
+Chaninfo=0x00
+#Tx power for Cont Tx in dbm
+TxPower=12
+# Packet Length for Cont Tx
+# legacy rates: upto 2312, HT/VHT rates: upto 8K
+PktLength=2048
+#RateInfo for Cont Tx
+RateInfo=0x0000
+#rateInfo = 0x0732 :  format = VHT, Bandwidth = 80MHz, datarate/MCS index = MCS7
+#rateInfo = 0x0000 :  format = legacy, Bandwidth = 20MHz, datarate/MCS index = 1Mbps
+#rateInfo = 0x0521 :  format = HT, Bandwidth = 40MHz, datarate/MCS index = MCS5
+#rateInfo = 0x4521 :  format = HT, Bandwidth = 40MHz, datarate/MCS index = MCS5, NSS = 1
+#rateInfo = 0x8921 :  format = HT, Bandwidth = 40MHz, datarate/MCS index = MCS9, NSS = 2
+#bit [1:0]  - Format 0: legacy, 1: 11n (HT), 2: 11ac (VHT)
+#bit 2      - STBC (Not used for Cont Tx test)
+#bit 3      - Beamforming (Not used for Cont Tx test)
+#bit [5:4]  - Bandwidth 0: 20MHz, 2: 40MHz, 3: 80MHz
+#bit [7:6]  - reserved
+#bit [13:8] - index : data rate or MCS
+#bit [15:14]- NSS (Used for 2x2 chips). Used in FW only for 2x2 chips.
+#<index> can take following values.
+#If <format> is 0 (LG),  #If <format> is 1 (HT)   #If <format> is 2 (VHT)
+#                            NSS = 1                 NSS = 1 or 2
+#    0   1 Mbps              0   MCS0                0   MCS0
+#    1   2 Mbps              1   MCS1                1   MCS1
+#    2   5.5 Mbps            2   MCS2                2   MCS2
+#    3   11 Mbps             3   MCS3                3   MCS3
+#    5   6 Mbps              4   MCS4                4   MCS4
+#    6   9 Mbps              5   MCS5                5   MCS5
+#    7   12 Mbps             6   MCS6                6   MCS6
+#    8   18 Mbps             7   MCS7                7   MCS7
+#                           Used only for NSS = 2
+#    9   24 Mbps             8   MCS8
+#    10  36 Mbps             9   MCS9
+#    11  48 Mbps            10   MCS10
+#    12  54 Mbps            11   MCS11
+#                           12   MCS12
+#                           13   MCS13
+#                           14   MCS14
+#                           15   MCS15
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/ed_mac_ctrl.conf b/wlan_sd8897/mapp/mlanconfig/config/ed_mac_ctrl.conf
new file mode 100644
index 0000000..ae0f750
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/ed_mac_ctrl.conf
@@ -0,0 +1,74 @@
+#       File : ed_mac_ctrl.conf
+#
+#   ./mlanutl mlan0 hostcmd config/ed_mac_ctrl.conf ed_mac_ctrl
+#
+#   ed_mac_ctrl_v2 should be used for KF/CAC
+#   ./mlanutl mlan0 hostcmd config/ed_mac_ctrl.conf ed_mac_ctrl_v2
+#
+#   ed_mac_ctrl_v3 should be used for KF2/RB3/CA2
+#   ./mlanutl mlan0 hostcmd config/ed_mac_ctrl.conf ed_mac_ctrl_v3
+#
+## Set ED MAC control
+ed_mac_ctrl={
+        CmdCode=0x0124          # do NOT change this line
+        Enable:2=0x0            # 0       - disable, use default
+                                # 1       - enable
+
+        Offset:2=0x0            # 0       - no offset
+                                # 0xffff  - offset set to -1
+                                # 0x1     - offset set to 1
+}
+
+ed_mac_ctrl_v2={
+        CmdCode=0x0130                  #Command code, do NOT change this tile
+        ed_ctrl_2g.enable:2=0x00        #0      - disable, all other parameters are ignored in 2.4G band
+                                        #1      - enable
+                                        #0xff   - use default OR value
+        ed_ctrl_2g.offset:2=0x0         #offset - value rabge: -128~127
+        ed_ctrl_2g.on_period:2=0x12     #time in millisecond to keep ED control enabled in 2.4g
+        ed_ctrl_2g.off_period:2=0x0     #time in milliseconf to keep ED control disabled in 2.4g
+        ed_ctrl_2g.bitmap:2=0x1         #Factors that affect 2.4G band ED control behavior
+                                        #Bit0: 0=If IEEE power save mode is on, disable EDCtrl2g
+                                        #      1=Neglect IEEE power save mode
+                                        #Bit8: 0=If IEEE power save mode is on EDCtrl2g is 1,
+                                        #        always keep EDCtrl2g enabled as if EDOffPeriod2g is 0
+                                        #      1=Neglect IEEE power save mode
+
+        ed_ctrl_5g.enable:2=0x0         #0      - disable, all other parameters are ignored in 2.4G band
+                                        #1      - enable
+                                        #0xff   - use default OR value
+        ed_ctrl_5g.offset:2=0x0         #offset - value rabge: -128~127
+        ed_ctrl_5g.on_period:2=0x12     #time in millisecond to keep ED control enabled in 5g
+        ed_ctrl_5g.off_period:2=0x0     #time in milliseconf to keep ED control disabled in 5g
+        ed_ctrl_5g.bitmap:2=0x1         #Factors that affect 5G band ED control behavior
+                                        #Bit0: 0=If IEEE power save mode is on, disable EDCtrl5g
+                                        #      1=Neglect IEEE power save mode
+                                        #Bit8: 0=If IEEE power save mode is on EDCtrl5g is 1,
+                                        #        always keep EDCtrl2g enabled as if EDOffPeriod5g is 0
+                                        #      1=Neglect IEEE power save mode
+}
+
+ed_mac_ctrl_v3={
+        CmdCode=0x0130                  #Command code, do NOT change this tile
+        ed_ctrl_2g.enable:2=0x01        #0      - disable EDMAC feature in 2.4G band
+                                        #1      - enable
+        ed_ctrl_2g.offset:2=0x0         #offset - value range: -128~127
+					                    #this offset will be added to the EDMAC thresholds
+					                    #effective_thr = default_thr + offset
+        ed_ctrl_5g.enable:2=0x01        #0      - disable EDMAC feature in 5G band
+                                        #1      - enable
+        ed_ctrl_5g.offset:2=0x0         #offset - value range: -128~127
+					                    #this offset will be added to the EDMAC thresholds
+					                    #effective_thr = default_thr + offset
+        ed_ctrl_txq_lock:4=0xFF         #Bitmap to control which queues to lock when ED busy is high
+                                        #The bitmap controls the following Tx when ED is high:
+                                        #Bit23 - PS queue
+                                        #Bit22 - CF queue
+                                        #Bit21 - MC queue
+                                        #Bit20 - TS queue
+                                        #Bit10 - Beacon TBTT
+                                        #Bit9 - BWQ
+                                        #Bit8 - CMQ
+                                        #Bit7:0 - TCQ7:0
+					                    #Respective Tx is blocked when the bit is set.
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/host_tdls.conf b/wlan_sd8897/mapp/mlanconfig/config/host_tdls.conf
new file mode 100644
index 0000000..3316d79
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/host_tdls.conf
@@ -0,0 +1,22 @@
+#	File : host_tdls.conf
+
+######################### HOST TDLS commands configuration ##################
+# starts with a command name with parameters embedded inside
+
+host_tdls_config={
+    uapsd_support=1  # 1: suport uapsd, 0: uapsd not support
+    cs_support=1     # 1: support channel switch, 0: don't support channel switch
+    SupportedChannels={
+        FirstChannelNo=1
+        NumberofSubBandChannels=11
+        # multiple instances of First Channel No, Number of SubBand Channels
+    }
+    SupportedRegulatoryClasses={
+        CurrentRegulatoryClass=1
+        NumofRegulatoryClasses=15
+        ListOfRegulatoryClasses=1,2,3,4,12,22,23,24,25,27,28,29,30,32,33
+    }
+
+}
+
+######################### HOST TDLS commands configuration ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/init_cfg.conf b/wlan_sd8897/mapp/mlanconfig/config/init_cfg.conf
new file mode 100644
index 0000000..d37cc4d
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/init_cfg.conf
@@ -0,0 +1,11 @@
+#	File : init_cfg.conf
+
+# MAC address (interface: address)
+mac_addr=mlan0: 00:50:43:20:12:34
+mac_addr=uap0: 00:50:43:20:12:35
+mac_addr=wfd0: 00:50:43:20:12:36
+
+# Register (type, offset, value)
+# type 1:MAC/SOC, 2:BBP, 3:RF, 5:CAU
+wlan_reg=1,0xA794,0x55FF55FF
+
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mef.conf b/wlan_sd8897/mapp/mlanconfig/config/mef.conf
new file mode 100644
index 0000000..37d19a4
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/mef.conf
@@ -0,0 +1,167 @@
+#	File : mef.conf
+
+######################### MEF Configuration command ##################
+mefcfg={
+	#Criteria: bit0-broadcast, bit1-unicast, bit3-multicast
+	Criteria=2 		# Unicast frames are received during hostsleepmode
+	NumEntries=1		# Number of activated MEF entries
+	#mef_entry_0: example filters to match TCP destination port 80 send by 192.168.0.88 pkt or magic pkt.
+	mef_entry_0={
+		#mode: bit0--hostsleep mode, bit1--non hostsleep mode
+		mode=1		# HostSleep mode
+		#action: 0--discard and not wake host, 1--discard and wake host 3--allow and wake host
+		action=3	# Allow and Wake host
+		filter_num=3    # Number of filter
+		#RPN only support "&&" and "||" operator,space can not be removed between operator.
+		RPN=Filter_0 && Filter_1 || Filter_2
+		#Byte comparison filter's type is 0x41,Decimal comparison filter's type is 0x42,
+		#Bit comparison filter's type is  0x43
+		#Filter_0 is decimal comparison filter, it always with type=0x42
+		#Decimal filter always has type, pattern, offset, numbyte 4 field
+                #Filter_0 will match rx pkt with TCP destination port 80
+		Filter_0={
+			type=0x42	# decimal comparison filter
+			pattern=80	# 80 is the decimal constant to be compared
+			offset=44	# 44 is the byte offset of the field in RX pkt to be compare
+			numbyte=2       # 2 is the number of bytes of the field
+		}
+		#Filter_1 is Byte comparison filter, it always with type=0x41
+		#Byte filter always has type, byte, repeat, offset 4 filed
+		#Filter_1 will match rx pkt send by IP address 192.168.0.88
+		Filter_1={
+			type=0x41          	# Byte comparison filter
+			repeat=1                # 1 copies of 'c0:a8:00:58'
+			byte=c0:a8:00:58	# 'c0:a8:00:58' is the byte sequence constant with each byte
+						# in hex format, with ':' as delimiter between two byte.
+			offset=34               # 34 is the byte offset of the equal length field of rx'd pkt.
+		}
+		#Filter_2 is Magic packet, it will looking for 16 contiguous copies of '00:50:43:20:01:02' from
+		# the rx pkt's offset 14
+		Filter_2={
+			type=0x41		# Byte comparison filter
+			repeat=16               # 16 copies of '00:50:43:20:01:02'
+			byte=00:50:43:20:01:02  # '00:50:43:20:01:02' is the byte sequence constant
+			offset=14		# 14 is the byte offset of the equal length field of rx'd pkt.
+		}
+	}
+}
+
+
+#--------------------------examples for MEF filters--------------------------------
+#	example: filters to match ARP packet with protocol addr 192.168.0.104
+#	mef_entry_0={
+#		mode=1			# HostSleep mode
+#		action=3		# Allow and Wake host
+#		filter_num=3		# Number of filter
+#		RPN=Filter_0 && Filter_1 && Filter_2
+#		#Filter_0 looking for rx pkt with DA is broadcast address
+#		Filter_0={
+#			type=0x41
+#			repeat=6
+#			byte=ff
+#			offset=0
+#		}
+#		#Filter_1 looking for rx pkt with EtherType is 0x0806(ARP)
+#		Filter_1={
+#			type=0x41
+#			repeat=1
+#			byte=08:06
+#			offset=20
+#		}
+#		#Filter_2 looking for rx pkt with ARP target protocol addr 192.168.0.104
+#		Filter_2={
+#			type=0x41
+#			repeat=1
+#			byte=c0:a8:00:68
+#			offset=46
+#		}
+#	}
+#-------------------------------------------------------------------------------------
+#	example: filter to check if the destination MAC address is unicast pkt
+#	mef_entry_0={
+#		mode=1			# HostSleep mode
+#		action=3		# Allow and Wake host
+#		filter_num=3		# Number of filter
+#		RPN=Filter_0
+#		#Filter_0 is Bit comparison filter, it always with type=0x43
+#		#Byte filter always has type, byte, mask, offset 4 filed
+#		#"byte" is the byte sequence constant with each byte in hex format, with ':' as delimiter between two byte
+#		#"mask" is also with each byte in hex format, with ':' as delimiter between two byte
+#		#"byte" should has the same length as "mask"
+#		#Filter_0 will check if the destination MAC address is unicast pkt
+#		Filter_0={
+#			type=0x43	#Bit comparison filter
+#			byte=00		#00 is the 1-byte sequence constant
+#			offset=0        #0 is the byte offset of the rx pkt
+#			mask=01		#1 is the 1-byte mask
+#		}
+#	}
+#--------------------------------------------------------------------------------------------------
+#	example: Disable MEF filters
+#	mefcfg={
+#		#Criteria: bit0-broadcast, bit1-unicast, bit3-multicast
+#		Criteria=2 		# Unicast frames are received during hostsleepmode
+#		NumEntries=0		# Number of activated MEF entries
+#	}
+#--------------------------------------------------------------------------------------------------
+#	example: Test MEF filters
+#       mefcfg={
+#		Criteria=1
+#		NumEntries=1
+#		mef_entry_0={
+#			mode=4         # Test Mode
+#			action=16      # Invoke Test
+#			filter_num=0
+#		}
+#	}
+#-----------------------------------------------------------------------------------------------------
+#	example: Test MEF filters
+#       mefcfg={
+#		Criteria=1
+#		NumEntries=1
+#		mef_entry_0={
+#			mode=4
+#			action=0
+#			filter_num=1
+#			RPN=Filter_0
+#			Filter_0={
+#				type=0x44		# test filter
+#				repeat=2                # 2 copies of 'BE:EF'
+#				byte=BE:EF		# 'BE:EF' is the byte sequence constant
+#				offset=18		# 18 is the byte offset of the equal length field of rx'd pkt.
+#				dest=00:50:43:20:5a:82  # '00:50:43:20:5a:82' is the byte sequence constant
+#			}
+#		}
+#	}
+#----------------------------------------------------------------------------------------------------
+#example: Filter broadcast/ipv4 multicast/ipv6 multicast packets in non hostsleep mode
+#mefcfg={
+#	Criteria=9		# broadcast and multicast frames
+#	NumEntries=1	# Number of activated MEF entries
+#	mef_entry_0={
+#		mode=2		# non HostSleep mode
+#		action=0	# discard and not wake host
+#		filter_num=3    # Number of filter
+#		RPN=Filter_0 || Filter_1 || Filter_2
+#		Filter_0={              # IPV4 multicast
+#			type=0x41	        # byte comparison filter
+#			byte=01:00:5e		# 01:00:5e is the byte constant to be compared
+#			offset=0	        # 0 is the byte offset of the equal length field of rx'd pkt.
+#			repeat=1		    #
+#		}
+#		Filter_1={              # broadcast packet check
+#			type=0x41          	# Byte comparison filter
+#			repeat=6            # 6 copies of 'ff', means broadcast
+#			byte=ff			    # 'ff'(0xff) is the byte sequence constant with each byte
+#						        # in hex format, with ':' as delimiter between two byte.
+#			offset=0            # 0 is the byte offset of the equal length field of rx'd pkt.
+#		}
+#		Filter_2={              # IPV6 multicast
+#			type=0x41	        # byte comparison filter
+#			byte=33:33		    # 33:33 is the byte constant to be compared
+#			offset=0	        # 0 is the byte offset of the equal length field of rx'd pkt.
+#			repeat=1		    #
+#		}
+#	}
+#}
+#------------------------------------------------------------------------------------------------------
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mef_mdns.conf b/wlan_sd8897/mapp/mlanconfig/config/mef_mdns.conf
new file mode 100644
index 0000000..568ca6f
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/mef_mdns.conf
@@ -0,0 +1,204 @@
+#	File : mef_mdns.conf
+
+######################### MEF Configuration command ##################
+mefcfg={
+	#Criteria: bit0-broadcast, bit1-unicast, bit3-multicast
+	Criteria=8 		# Multicast frames are received during hostsleepmode
+	NumEntries=2		# Number of activated MEF entries
+	#mef_entry_0: example filters to match WS-Discovery pkt for IPv4.
+	mef_entry_0={
+		#mode: bit0--hostsleep mode, bit1--non hostsleep mode
+		mode=1		# HostSleep mode
+		#action: 0--discard and not wake host, 1--discard and wake host 3--allow and wake host
+		action=3	# Allow and Wake host
+		filter_num=4    # Number of filter
+		#RPN only support "&&" and "||" operator,space can not be removed between operator.
+		RPN=Filter_0 && Filter_1 && Filter_2 && Filter_3
+		#Filter_0 will match IPv4 protocol packet
+		Filter_0={
+			type=0x41
+			repeat=1
+			byte=08:00
+			offset=20
+		}
+		#Filter_1 will match dest multicast IPv4 address 224.0.0.251
+		Filter_1={
+			type=0x41
+			repeat=1
+			byte=e0:00:00:fb
+			offset=38
+		}
+		#Filter_2 will match UDP packet
+		Filter_2={
+			type=0x42
+			pattern=17
+			offset=31
+			numbyte=1
+		}
+		#Filter_3 will match UDP port 5353
+		Filter_3={
+			type=0x42
+			pattern=5353
+			offset=44
+			numbyte=2
+		}
+	}
+	#mef_entry_1: example filters to match WS-Discovery pkt for IPv6.
+	mef_entry_1={
+		#mode: bit0--hostsleep mode, bit1--non hostsleep mode
+		mode=1		# HostSleep mode
+		#action: 0--discard and not wake host, 1--discard and wake host 3--allow and wake host
+		action=3	# Allow and Wake host
+		filter_num=4    # Number of filter
+		#RPN only support "&&" and "||" operator,space can not be removed between operator.
+		RPN=Filter_0 && Filter_1 && Filter_2 && Filter_3
+		#Filter_0 will match IPv4 protocol packet
+		Filter_0={
+			type=0x41
+			repeat=1
+			byte=86:dd
+			offset=20
+		}
+		#Filter_1 will match dest multicast IPv6 address FF02::FB
+		Filter_1={
+			type=0x41
+			repeat=1
+			byte=ff:02:00:00:00:00:00:00:00:00:00:00:00:00:00:fb
+			offset=46
+		}
+		#Filter_2 will match UDP packet
+		Filter_2={
+			type=0x42
+			pattern=17
+			offset=28
+			numbyte=1
+		}
+		#Filter_3 will match UDP port 5353
+		Filter_3={
+			type=0x42
+			pattern=5353
+			offset=64
+			numbyte=2
+		}
+	}
+}
+
+
+#--------------------------examples for MEF filters--------------------------------
+#	example: filters to match ARP packet with protocol addr 192.168.0.104
+#	mef_entry_0={
+#		mode=1			# HostSleep mode
+#		action=3		# Allow and Wake host
+#		filter_num=3		# Number of filter
+#		RPN=Filter_0 && Filter_1 && Filter_2
+#		#Filter_0 looking for rx pkt with DA is broadcast address
+#		Filter_0={
+#			type=0x41
+#			repeat=6
+#			byte=ff
+#			offset=0
+#		}
+#		#Filter_1 looking for rx pkt with EtherType is 0x0806(ARP)
+#		Filter_1={
+#			type=0x41
+#			repeat=1
+#			byte=08:06
+#			offset=20
+#		}
+#		#Filter_2 looking for rx pkt with ARP target protocol addr 192.168.0.104
+#		Filter_2={
+#			type=0x41
+#			repeat=1
+#			byte=c0:a8:00:68
+#			offset=46
+#		}
+#	}
+#-------------------------------------------------------------------------------------
+#	example: filter to check if the destination MAC address is unicast pkt
+#	mef_entry_0={
+#		mode=1			# HostSleep mode
+#		action=3		# Allow and Wake host
+#		filter_num=3		# Number of filter
+#		RPN=Filter_0
+#		#Filter_0 is Bit comparison filter, it always with type=0x43
+#		#Byte filter always has type, byte, mask, offset 4 filed
+#		#"byte" is the byte sequence constant with each byte in hex format, with ':' as delimiter between two byte
+#		#"mask" is also with each byte in hex format, with ':' as delimiter between two byte
+#		#"byte" should has the same length as "mask"
+#		#Filter_0 will check if the destination MAC address is unicast pkt
+#		Filter_0={
+#			type=0x43	#Bit comparison filter
+#			byte=00		#00 is the 1-byte sequence constant
+#			offset=0        #0 is the byte offset of the rx pkt
+#			mask=01		#1 is the 1-byte mask
+#		}
+#	}
+#--------------------------------------------------------------------------------------------------
+#	example: Disable MEF filters
+#	mefcfg={
+#		#Criteria: bit0-broadcast, bit1-unicast, bit3-multicast
+#		Criteria=2 		# Unicast frames are received during hostsleepmode
+#		NumEntries=0		# Number of activated MEF entries
+#	}
+#--------------------------------------------------------------------------------------------------
+#	example: Test MEF filters
+#       mefcfg={
+#		Criteria=1
+#		NumEntries=1
+#		mef_entry_0={
+#			mode=4         # Test Mode
+#			action=16      # Invoke Test
+#			filter_num=0
+#		}
+#	}
+#-----------------------------------------------------------------------------------------------------
+#	example: Test MEF filters
+#       mefcfg={
+#		Criteria=1
+#		NumEntries=1
+#		mef_entry_0={
+#			mode=4
+#			action=0
+#			filter_num=1
+#			RPN=Filter_0
+#			Filter_0={
+#				type=0x44		# test filter
+#				repeat=2                # 2 copies of 'BE:EF'
+#				byte=BE:EF		# 'BE:EF' is the byte sequence constant
+#				offset=18		# 18 is the byte offset of the equal length field of rx'd pkt.
+#				dest=00:50:43:20:5a:82  # '00:50:43:20:5a:82' is the byte sequence constant
+#			}
+#		}
+#	}
+#----------------------------------------------------------------------------------------------------
+#example: Filter broadcast/ipv4 multicast/ipv6 multicast packets in non hostsleep mode
+#mefcfg={
+#	Criteria=9		# broadcast and multicast frames
+#	NumEntries=1	# Number of activated MEF entries
+#	mef_entry_0={
+#		mode=2		# non HostSleep mode
+#		action=0	# discard and not wake host
+#		filter_num=3    # Number of filter
+#		RPN=Filter_0 || Filter_1 || Filter_2
+#		Filter_0={              # IPV4 multicast
+#			type=0x41	        # byte comparison filter
+#			byte=01:00:5e		# 01:00:5e is the byte constant to be compared
+#			offset=0	        # 0 is the byte offset of the equal length field of rx'd pkt.
+#			repeat=1		    #
+#		}
+#		Filter_1={              # broadcast packet check
+#			type=0x41          	# Byte comparison filter
+#			repeat=6            # 6 copies of 'ff', means broadcast
+#			byte=ff			    # 'ff'(0xff) is the byte sequence constant with each byte
+#						        # in hex format, with ':' as delimiter between two byte.
+#			offset=0            # 0 is the byte offset of the equal length field of rx'd pkt.
+#		}
+#		Filter_2={              # IPV6 multicast
+#			type=0x41	        # byte comparison filter
+#			byte=33:33		    # 33:33 is the byte constant to be compared
+#			offset=0	        # 0 is the byte offset of the equal length field of rx'd pkt.
+#			repeat=1		    #
+#		}
+#	}
+#}
+#------------------------------------------------------------------------------------------------------
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mef_ws_discovery.conf b/wlan_sd8897/mapp/mlanconfig/config/mef_ws_discovery.conf
new file mode 100644
index 0000000..518a240
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/mef_ws_discovery.conf
@@ -0,0 +1,204 @@
+#	File : mef_ws_discovery.conf
+
+######################### MEF Configuration command ##################
+mefcfg={
+	#Criteria: bit0-broadcast, bit1-unicast, bit3-multicast
+	Criteria=8 		# Multicast frames are received during hostsleepmode
+	NumEntries=2		# Number of activated MEF entries
+	#mef_entry_0: example filters to match WS-Discovery pkt for IPv4.
+	mef_entry_0={
+		#mode: bit0--hostsleep mode, bit1--non hostsleep mode
+		mode=1		# HostSleep mode
+		#action: 0--discard and not wake host, 1--discard and wake host 3--allow and wake host
+		action=3	# Allow and Wake host
+		filter_num=4    # Number of filter
+		#RPN only support "&&" and "||" operator,space can not be removed between operator.
+		RPN=Filter_0 && Filter_1 && Filter_2 && Filter_3
+		#Filter_0 will match IPv4 protocol packet
+		Filter_0={
+			type=0x41
+			repeat=1
+			byte=08:00
+			offset=20
+		}
+		#Filter_1 will match dest multicast IPv4 address 239.255.255.250
+		Filter_1={
+			type=0x41
+			repeat=1
+			byte=ef:ff:ff:fa
+			offset=38
+		}
+		#Filter_2 will match UDP packet
+		Filter_2={
+			type=0x42
+			pattern=17
+			offset=31
+			numbyte=1
+		}
+		#Filter_3 will match UDP port 3702
+		Filter_3={
+			type=0x42
+			pattern=3702
+			offset=44
+			numbyte=2
+		}
+	}
+	#mef_entry_1: example filters to match WS-Discovery pkt for IPv6.
+	mef_entry_1={
+		#mode: bit0--hostsleep mode, bit1--non hostsleep mode
+		mode=1		# HostSleep mode
+		#action: 0--discard and not wake host, 1--discard and wake host 3--allow and wake host
+		action=3	# Allow and Wake host
+		filter_num=4    # Number of filter
+		#RPN only support "&&" and "||" operator,space can not be removed between operator.
+		RPN=Filter_0 && Filter_1 && Filter_2 && Filter_3
+		#Filter_0 will match IPv4 protocol packet
+		Filter_0={
+			type=0x41
+			repeat=1
+			byte=86:dd
+			offset=20
+		}
+		#Filter_1 will match dest multicast IPv6 address FF02::C
+		Filter_1={
+			type=0x41
+			repeat=1
+			byte=ff:02:00:00:00:00:00:00:00:00:00:00:00:00:00:0c
+			offset=46
+		}
+		#Filter_2 will match UDP packet
+		Filter_2={
+			type=0x42
+			pattern=17
+			offset=28
+			numbyte=1
+		}
+		#Filter_3 will match UDP port 3702
+		Filter_3={
+			type=0x42
+			pattern=3702
+			offset=64
+			numbyte=2
+		}
+	}
+}
+
+
+#--------------------------examples for MEF filters--------------------------------
+#	example: filters to match ARP packet with protocol addr 192.168.0.104
+#	mef_entry_0={
+#		mode=1			# HostSleep mode
+#		action=3		# Allow and Wake host
+#		filter_num=3		# Number of filter
+#		RPN=Filter_0 && Filter_1 && Filter_2
+#		#Filter_0 looking for rx pkt with DA is broadcast address
+#		Filter_0={
+#			type=0x41
+#			repeat=6
+#			byte=ff
+#			offset=0
+#		}
+#		#Filter_1 looking for rx pkt with EtherType is 0x0806(ARP)
+#		Filter_1={
+#			type=0x41
+#			repeat=1
+#			byte=08:06
+#			offset=20
+#		}
+#		#Filter_2 looking for rx pkt with ARP target protocol addr 192.168.0.104
+#		Filter_2={
+#			type=0x41
+#			repeat=1
+#			byte=c0:a8:00:68
+#			offset=46
+#		}
+#	}
+#-------------------------------------------------------------------------------------
+#	example: filter to check if the destination MAC address is unicast pkt
+#	mef_entry_0={
+#		mode=1			# HostSleep mode
+#		action=3		# Allow and Wake host
+#		filter_num=3		# Number of filter
+#		RPN=Filter_0
+#		#Filter_0 is Bit comparison filter, it always with type=0x43
+#		#Byte filter always has type, byte, mask, offset 4 filed
+#		#"byte" is the byte sequence constant with each byte in hex format, with ':' as delimiter between two byte
+#		#"mask" is also with each byte in hex format, with ':' as delimiter between two byte
+#		#"byte" should has the same length as "mask"
+#		#Filter_0 will check if the destination MAC address is unicast pkt
+#		Filter_0={
+#			type=0x43	#Bit comparison filter
+#			byte=00		#00 is the 1-byte sequence constant
+#			offset=0        #0 is the byte offset of the rx pkt
+#			mask=01		#1 is the 1-byte mask
+#		}
+#	}
+#--------------------------------------------------------------------------------------------------
+#	example: Disable MEF filters
+#	mefcfg={
+#		#Criteria: bit0-broadcast, bit1-unicast, bit3-multicast
+#		Criteria=2 		# Unicast frames are received during hostsleepmode
+#		NumEntries=0		# Number of activated MEF entries
+#	}
+#--------------------------------------------------------------------------------------------------
+#	example: Test MEF filters
+#       mefcfg={
+#		Criteria=1
+#		NumEntries=1
+#		mef_entry_0={
+#			mode=4         # Test Mode
+#			action=16      # Invoke Test
+#			filter_num=0
+#		}
+#	}
+#-----------------------------------------------------------------------------------------------------
+#	example: Test MEF filters
+#       mefcfg={
+#		Criteria=1
+#		NumEntries=1
+#		mef_entry_0={
+#			mode=4
+#			action=0
+#			filter_num=1
+#			RPN=Filter_0
+#			Filter_0={
+#				type=0x44		# test filter
+#				repeat=2                # 2 copies of 'BE:EF'
+#				byte=BE:EF		# 'BE:EF' is the byte sequence constant
+#				offset=18		# 18 is the byte offset of the equal length field of rx'd pkt.
+#				dest=00:50:43:20:5a:82  # '00:50:43:20:5a:82' is the byte sequence constant
+#			}
+#		}
+#	}
+#----------------------------------------------------------------------------------------------------
+#example: Filter broadcast/ipv4 multicast/ipv6 multicast packets in non hostsleep mode
+#mefcfg={
+#	Criteria=9		# broadcast and multicast frames
+#	NumEntries=1	# Number of activated MEF entries
+#	mef_entry_0={
+#		mode=2		# non HostSleep mode
+#		action=0	# discard and not wake host
+#		filter_num=3    # Number of filter
+#		RPN=Filter_0 || Filter_1 || Filter_2
+#		Filter_0={              # IPV4 multicast
+#			type=0x41	        # byte comparison filter
+#			byte=01:00:5e		# 01:00:5e is the byte constant to be compared
+#			offset=0	        # 0 is the byte offset of the equal length field of rx'd pkt.
+#			repeat=1		    #
+#		}
+#		Filter_1={              # broadcast packet check
+#			type=0x41          	# Byte comparison filter
+#			repeat=6            # 6 copies of 'ff', means broadcast
+#			byte=ff			    # 'ff'(0xff) is the byte sequence constant with each byte
+#						        # in hex format, with ':' as delimiter between two byte.
+#			offset=0            # 0 is the byte offset of the equal length field of rx'd pkt.
+#		}
+#		Filter_2={              # IPV6 multicast
+#			type=0x41	        # byte comparison filter
+#			byte=33:33		    # 33:33 is the byte constant to be compared
+#			offset=0	        # 0 is the byte offset of the equal length field of rx'd pkt.
+#			repeat=1		    #
+#		}
+#	}
+#}
+#------------------------------------------------------------------------------------------------------
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mem.conf b/wlan_sd8897/mapp/mlanconfig/config/mem.conf
new file mode 100644
index 0000000..cf649c9
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/mem.conf
@@ -0,0 +1,17 @@
+#File : memory/register update
+# memory/register update file
+mem_set={
+	CmdCode=0x0086	#do NOT change this line
+	Action:2=1	# 1 - SET
+	Rsrvd:2=0
+	Addr:4=0x8000a584	# memory/register address
+	Value:4=0x16161669 #Value
+}
+
+mem_get={
+	CmdCode=0x0086	#do NOT change this line
+	Action:2=0	# 1 - SET
+	Rsrvd:2=0
+	Addr:4=0x8000a584	# memory/register address
+	Result:4=0
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mgmt_frame.conf b/wlan_sd8897/mapp/mlanconfig/config/mgmt_frame.conf
new file mode 100644
index 0000000..22b19b1
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/mgmt_frame.conf
@@ -0,0 +1,30 @@
+# Format of the packet data to be sent.
+# Update the TxControl value for TxPD, else zero is sent.
+# all packet data is written under "Data" block
+
+PktType=0x00          # should be zero for MGMT frames
+PktSubType=0x05
+                      # Assoc Request       0
+                      # Assoc Response       1
+                      # Re-Assoc Request     2
+                      # Re-Assoc Response    3
+                      # Probe Request        4
+                      # Probe Response       5
+                      # Beacon               8
+                      # Atim                 9
+                      # Dis-assoc            10
+                      # Auth                 11
+                      # Deauth               12
+                      # Action Frame         13
+
+FromDS=0
+ToDS=0
+
+Addr1=00:50:43:27:B0:41   # Destination address
+Addr2=00:50:43:21:0F:84   # Source address
+Addr3=00:50:43:21:0F:84   # BSSID
+
+Data=0x01,0x01,0x00,0x0c,0x00,0x58,0x02,0x40
+
+SeqNum=0
+FragNum=0
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mgmtfilter.conf b/wlan_sd8897/mapp/mlanconfig/config/mgmtfilter.conf
new file mode 100644
index 0000000..8ee0298
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/mgmtfilter.conf
@@ -0,0 +1,15 @@
+##############management frame filter to wake up host ###########
+##### support two entries currently ######
+mgmtfilter={
+	entry_num=1
+	entry_0={
+		action=1      # discard and wakeup host
+		type=0x1      # p2p frames
+		frame_mask=0x7 # Go neg req & rsp & cfm frame
+	}
+#	entry_1={
+#		action=0      # discard and not wakeup host
+#		type=0xff     # management frames
+#		frame_mask=0x3  # assoc req & rsp frame
+#	}
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/or_data.conf b/wlan_sd8897/mapp/mlanconfig/config/or_data.conf
new file mode 100644
index 0000000..99154f5
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/or_data.conf
@@ -0,0 +1,7 @@
+ 07 01 03 3A 80 00 3A 00 ff ff 00 17 03 00 07 03
+ 09 00 0e 10 16 02 19 25 1a 04 1c ff 32 5e 33 15
+ 35 29 36 17 4b 74 4c 64 4d 3b 50 27 61 d6 62 98
+ 6b ae 6f 5b 77 f2 79 ff 7f 2d
+ 07 01 12 16 c0 00 ff ff ff ff 00 05 03 10 32 5c
+ 33 1a 6b a2 7f 20
+
diff --git a/wlan_sd8897/mapp/mlanconfig/config/pad_cfg.conf b/wlan_sd8897/mapp/mlanconfig/config/pad_cfg.conf
new file mode 100644
index 0000000..80e2788
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/pad_cfg.conf
@@ -0,0 +1,11 @@
+#       File : pad_cfg.conf
+
+## Get CFG data for PAD OR
+pad_cfg_get={
+        CmdCode=0x008f          # do NOT change this line
+        Action:2=0              # 0 - GET
+        Type:2=5                # 5 - optimized pad reg
+        CfgLen:2={
+        }
+}
+
diff --git a/wlan_sd8897/mapp/mlanconfig/config/requesttpc.conf b/wlan_sd8897/mapp/mlanconfig/config/requesttpc.conf
new file mode 100644
index 0000000..690c30d
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/requesttpc.conf
@@ -0,0 +1,15 @@
+#	File : requesttpc.conf
+
+######################### requesttpc command configuration ##################
+
+requesttpc={
+	CmdCode=0x0060  # do NOT change this line
+	#DestMac: Destination STA address
+	DestMac:6='0x02,0x04,0x0e,0x06,0x01,0x12'
+	#RateIndex: IEEE Rate index to send request
+	RateIndex:1=22
+	#Timeout: Response timeout in ms
+	Timeout:2=10
+}
+
+######################### End of requesttpc command configuration ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/robust_btc.conf b/wlan_sd8897/mapp/mlanconfig/config/robust_btc.conf
new file mode 100644
index 0000000..7d5cb9b
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/robust_btc.conf
@@ -0,0 +1,189 @@
+#	File : robust_btc.conf
+
+######################### Robust Coex command ###############
+mode_get={
+    CmdCode=0x00e0		# do NOT change this line
+    Action:2=0			# GET
+    RSVD:2=0
+
+    # Robust Coex Mode TLV
+    RobustCoexTlvType:2=0x0160
+    RobustCoexTlvLength:2={
+        Enable:1=0x00	# Read-back Coex mode(s)
+        Reserved:3=0
+    }
+}
+
+mode_timeshare={
+    CmdCode=0x00e0		# do NOT change this line
+    Action:2=1			# SET
+    RSVD:2=0
+
+    # Robust Coex Mode TLV
+    RobustCoexTlvType:2=0x0160
+    RobustCoexTlvLength:2={
+        # All the modes below are mutually exclusive of each other;
+        Enable:1=0x01       # Bit0: Enable 2x2 or 1x1 Time Distribute(TMD)
+                            #  Robust Coex(RBC) mode, when uAP bss start,
+                            #  uAP TMD RBC scheme is enabled,
+                            #  STA TMD RBC scheme is disabled.
+        Reserved:3=0
+    }
+}
+
+mode_spatial={
+    CmdCode=0x00e0		# do NOT change this line
+    Action:2=1			# SET
+    RSVD:2=0
+
+    # Robust Coex Mode TLV
+    RobustCoexTlvType:2=0x0160
+    RobustCoexTlvLength:2={
+        # All the modes below are mutually exclusive of each other;
+        Enable:1=0x82       # Bit1: Enable 1x1 SMPS Spatial RBC Mode, e.g. 0x02
+                            # Bit7: Enable uAP+STA SMPS RBC Mode,
+                            #  when uAP bss start, uAP SMPS RBC scheme enable,
+                            #  must combined with BIT1 or BIT2, e.g. 0x82, 0x84.
+        Reserved:3=0
+    }
+}
+
+mode_none={
+    CmdCode=0x00e0		# do NOT change this line
+    Action:2=1			# SET
+    RSVD:2=0
+
+    # Robust Coex Mode TLV
+    RobustCoexTlvType:2=0x0160
+    RobustCoexTlvLength:2={
+        Enable:1=0          # Concurrent Coex mode. Used for chips which has
+                            # separate antenna for BT
+        Reserved:3=0
+    }
+}
+
+mode_2={
+    CmdCode=0x00e0          # do NOT change this line
+    Action:2=1              # SET
+    RSVD:2=0
+
+    # Robust Coex Mode TLV
+    RobustCoexTlvType:2=0x0160
+    RobustCoexTlvLength:2={
+        Enable:1=0x20          # Concurrent Coex mode with Tx power control and Rx De-sense.
+                               # Used for chips which has separate antenna for BT
+        Reserved:3=0
+    }
+}
+
+gpio_cfg={
+    CmdCode=0x00e0          # do NOT change this line
+    Action:2=1              # SET
+    RSVD:2=0
+
+    # Robust Coex Mode TLV
+    RobustCoexTlvType:2=0x021B
+    RobustCoexTlvLength:2={
+        Enable:1=0x1        # enable GPIO cfg for external bt request
+        gpionum:1=4         # gpio 4
+        gpiopolarity:1=1    # Polarity High
+    }
+}
+
+#In Station generic case
+#BT time is set as BTTime
+#Wlan time is set as Wlan Time
+generictime={
+    CmdCode=0x00e0
+    Action:2=1
+    RSVD:2=0
+    RobustCoexTlvType:2=0x0390
+    RobustCoexTlvLength:2={
+        Enable:2=0x01
+        BtTime:2=10000           #(10ms) BTTime must be less than 65535
+        WlanTime:2=40000         #(40ms) WlanTime must be less than 65535
+     }
+}
+
+#In Station A2DP case
+#BT time is set as BTTime
+#Wlan time is set as Wlan Time
+a2dptime={
+    CmdCode=0x00e0
+    Action:2=1
+    RSVD:2=0
+    RobustCoexTlvType:2=0x0391
+    RobustCoexTlvLength:2={
+        Enable:2=0x01
+        BtTime:2=10000           #(10ms)   BTTime must be less than 65535
+        WlanTime:2=39500         #(39.5ms) WlanTime must be less than 65535
+     }
+}
+
+#In Station inquiry case
+#BT time is set as BTTime
+#Wlan time is set as Wlan Time
+inquirytime={
+    CmdCode=0x00e0
+    Action:2=1
+    RSVD:2=0
+    RobustCoexTlvType:2=0x0392
+    RobustCoexTlvLength:2={
+        Enable:2=0x01
+        BtTime:2=21215           #(21.215ms) BTTime must be less than 65535
+        WlanTime:2=11000         #(11ms)     WlanTime must be less than 65535
+     }
+}
+
+#In Ap generic case
+#BT time is BTTimeBusy when BT has traffic
+#BT time is BTTimeIdle when BT is idle
+#Wlan time is WlanTimeBusy when Wlan has traffic
+#Wlan time is WlanTimeIdle when Wlan is idle
+ap_generictime={
+    CmdCode=0x00e0
+    Action:2=1
+    RSVD:2=0
+    RobustCoexTlvType:2=0x0393
+    RobustCoexTlvLength:2={
+       Enable:2=0x01
+       BtTime_MAX:2=23000         #(23ms)   BTTime(BT Busy) must be less than 28767
+       BtTime_MIN:2=6500          #(6.5ms)  BTTime(BT Idle) must be less than 28767
+       WlanTime_MAX:2=18000       #(18ms)   WlanTime(Wlan Busy) must be less than 32767
+       WlanTime_MIN:2=5750        #(5.75ms) WlanTime(Wlan Idle) must be less than 32767
+    }
+}
+
+#In Ap A2DP case
+#BT time is change from BTTimeMax to BTTimeMin
+#Wlan time is change from WlanTimeMax to WlanTimeMin
+ap_a2dptime={
+    CmdCode=0x00e0
+    Action:2=1
+    RSVD:2=0
+    RobustCoexTlvType:2=0x0394
+    RobustCoexTlvLength:2={
+        Enable:2=0x01
+        BtTimebusy:2=23000           #(23ms)   Maximum BTTime must be less than 32767
+        BtTimeidle:2=6500            #(6.5ms)  Minimum BTTime must be less than 32767
+        WlanTimebusy:2=18000         #(18ms)   Maximum WlanTime must be less than 32767
+        WlanTimeidle:2=5750          #(5.75ms) Minimum WlanTime must be less than 32767
+        }
+}
+
+#In Ap inquiry case
+#BT time is set as BTTime
+#Wlan time is set as Wlan Time
+ap_inquirytime={
+    CmdCode=0x00e0
+    Action:2=1
+    RSVD:2=0
+    RobustCoexTlvType:2=0x0395
+    RobustCoexTlvLength:2={
+        Enable:2=0x01
+        BtTime:2=28750               #(28.75ms) BTTime must less than 32767
+        WlanTime:2=20000             #(20ms)    WlanTime must be less than 32767
+        }
+}
+
+######################### Robust Coex command ###############
diff --git a/wlan_sd8897/mapp/mlanconfig/config/sdio_pulldown.conf b/wlan_sd8897/mapp/mlanconfig/config/sdio_pulldown.conf
new file mode 100644
index 0000000..745978b
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/sdio_pulldown.conf
@@ -0,0 +1,27 @@
+#	File : sdio_pulldown.conf
+
+######################### SDIO Pulldown command ###############
+sdio_pulldown_get={
+	CmdCode=0x0093		# do NOT change this line
+
+	Action:2=0			# GET
+	PullUpDelay:2=0
+	PullDownDelay:2=0
+}
+
+sdio_pulldown_set={
+	CmdCode=0x0093		# do NOT change this line
+
+	Action:2=1			# SET
+	PullUpDelay:2=0
+	PullDownDelay:2=0
+}
+
+sdio_pulldown_disable={
+	CmdCode=0x0093		# do NOT change this line
+
+	Action:2=1			# SET
+	PullUpDelay:2=0xffff
+	PullDownDelay:2=0xffff
+}
+######################### SDIO Pulldown command ###############
diff --git a/wlan_sd8897/mapp/mlanconfig/config/small_debug.conf b/wlan_sd8897/mapp/mlanconfig/config/small_debug.conf
new file mode 100644
index 0000000..1457426
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/small_debug.conf
@@ -0,0 +1,62 @@
+#	File : small_debug.conf
+
+######################### DBG Config command ###############
+small_debug_enable={
+    CmdCode=0x008b      # do NOT change this line
+
+    Dst:1=0x06          # dbgs_cfg_t
+    Air_Chan:1=0x00
+    RSVD:1=0
+    Num_Entries:1=0x81  # one entry global enable
+
+    # dbgs_t
+    En_Mask:2=0x0000    # en_mask_id
+    BaseId:2=0x0000     # base_id
+}
+
+small_debug_disable={
+    CmdCode=0x008b      # do NOT change this line
+
+    Dst:1=0x00
+    Air_Chan:1=0x00
+    RSVD:1=0
+    Num_Entries:1=0x00
+}
+
+######################### DBG Get Trace Memory ###############
+# Disable Air and Host destinations to disable dbgs_drain
+# Enable only the Power save ID - Host interface is a course enable - not on specific event
+trace_enable={
+    CmdCode=0x008b      # do NOT change this line
+
+    Dst:1=0x00          # dbgs_cfg_t - don't drain debug
+    Air_Chan:1=0x00
+    RSVD:1=0
+    Num_Entries:1=0x02  # 2 entry
+
+    # dbgs_t
+    En_Mask:2=0x5fff    # en_mask_id - enable single ID=0x80
+    BaseId:2=0x0020     # base_id
+    En_Mask:2=0x5fff    # en_mask_id - enable single ID=0x80
+    BaseId:2=0x0021     # base_id
+}
+
+get_trace={
+    CmdCode=0x008c      # do NOT change this line
+
+    startAddress:4=0x02 # read trace memeory - not a generic memory read
+    len:2=0x0000        # First read=0 - for next reads use the returned next start
+}
+
+trace_disable={
+    CmdCode=0x008b      # do NOT change this line
+
+    Dst:1=0x00          # dbgs_cfg_t - don't drain debug
+    Air_Chan:1=0x00
+    RSVD:1=0
+    Num_Entries:1=0x01  # one entry
+
+    # dbgs_t
+    En_Mask:2=0x0000    # en_mask_id
+    BaseId:2=0x0000     # base_id
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/smc.conf b/wlan_sd8897/mapp/mlanconfig/config/smc.conf
new file mode 100644
index 0000000..9bead4c
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/smc.conf
@@ -0,0 +1,124 @@
+#	File : smc.conf
+
+smc_get={
+	CmdCode=0x012d	#do not change this
+
+	Action:2=0	# GET
+}
+
+smc_set={
+	CmdCode=0x012d	#do not change this
+
+	Action:2=1	# SET
+
+# SSID
+	SSIDHeaderType:2=0x0000
+	SSIDHeaderLen:2={
+		SSID:10="Marvell_SMC_1"
+	}
+
+# Beacon Period.
+# This should be smaller than than MinScanTime.
+	BeaconHeaderType:2=0x012c
+	BeaconHeaderLen:2={
+		beaconPeriod:2=30
+	}
+
+# Channel list.
+# ChanNumber, MinScanTime and ScanTime are mandatory.
+# MinScanTime is minimum dwelling time for ChanNumber channel.
+# ScanTime is maximum dwelling time for ChanNumber channel.
+	ChannHeaderType:2=0x0101
+	ChannHeaderLen:2={
+
+# Following four line define one channel.
+# Please add similar four lines with different channel number for new channel.
+		Chan1_RadioType:1=0
+		Chan1_ChanNumber:1=1
+		Chan1_ScanType:1=2
+		Chan1_MinScanTime:2=40
+		Chan1_ScanTime:2=200
+
+		Chan2_RadioType:1=0
+		Chan2_ChanNumber:1=6
+		Chan2_ScanType:1=3
+		Chan2_MinScanTime:2=40
+		Chan2_ScanTime:2=200
+
+		Chan3_RadioType:1=0
+		Chan3_ChanNumber:1=9
+		Chan3_ScanType:1=2
+		Chan3_MinScanTime:2=40
+		Chan3_ScanTime:2=100
+
+		Chan2_RadioType:1=0
+		Chan2_ChanNumber:1=11
+		Chan2_ScanType:1=3
+		Chan2_MinScanTime:2=40
+		Chan2_ScanTime:2=200
+	}
+
+#Custom IE
+#Currently max size of custom IE supported is 50 bytes.
+#
+#	CustomHeaderType:2=0x010a
+#	CustomHeaderLen:2={
+#		start:1=0xdd
+#		start:1=0x10
+#		start:1=0x00
+#		start:1=0x01
+#		start:1=0x02
+#		start:1=0x03
+#		start:1=0x04
+#		start:1=0x05
+#		start:1=0x06
+#		start:1=0x07
+#		start:1=0x08
+#		start:1=0x09
+#		start:1=0x0a
+#		start:1=0x0b
+#		start:1=0x0c
+#		start:1=0x0d
+#		start:1=0x0e
+#		start:1=0x0f
+#	}
+
+#Multicast mac filtering address
+#All the multicast packets from starting mac address to ending mac address would
+#be captured and sent to the host.
+	MACHeaderType:2=0x01cc
+	MACHeaderLen:2={
+#Staring Multicast mac address
+		start:1=0x01
+		start:1=0x00
+		start:1=0x5e
+		start:1=0x00
+		start:1=0x00
+		start:1=0x01
+
+#Ending Multicast mac address
+		end:1=0x01
+		end:1=0x00
+		end:1=0x5e
+		end:1=0x7f
+		end:1=0xff
+		end:1=0xff
+
+#FilterType
+# 1 for RX AP frames
+# 2 for RX STA frames
+# 3 for both
+		Filter:2=0x3
+	}
+
+}
+smc_start={
+	CmdCode=0x012d	#do not change this
+
+	Action:2=2	# START
+}
+smc_stop={
+	CmdCode=0x012d	#do not change this
+
+	Action:2=3	# STOP
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/subevent.conf b/wlan_sd8897/mapp/mlanconfig/config/subevent.conf
new file mode 100644
index 0000000..ac7f333
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/subevent.conf
@@ -0,0 +1,103 @@
+#	File : subevent.conf
+
+######################### Subscribe Events command ##################
+subevent_get={
+	CmdCode=0x0075	# do NOT change this line
+
+	Action:2=0		# GET
+	Events:2=0
+}
+
+subevent_set={
+	CmdCode=0x0075	# do NOT change this line
+
+	Action:2=1		# SET
+	Events:2=0xbc8  # bit0     - Beacon RSSI_LOW
+	                # bit1     - Beacon SNR_LOW
+	                # bit2     - FAILED_COUNT
+	                # bit3     - Beacon Missed
+	                # bit4     - Beacon RSSI_HIGH
+	                # bit5     - Beacon SNR_HIGH
+	                # bit6     - Data RSSI_LOW
+	                # bit7     - Data SNR_LOW
+	                # bit8     - Data RSSI_HIGH
+	                # bit9     - Data SNR_HIGH
+	                # bit10    - LINK_QUALITY
+	                # bit11    - PRE_BCN_LOST
+	                # bit12-15 - Reserved
+
+	LowRssiTlvType:2=0x0104
+	LowRssiTlvLength:2={
+		Threshold:1=70
+		ReportingFreq:1=0
+	}
+
+	LowSnrTlvType:2=0x0105
+	LowSnrTlvLength:2={
+		Threshold:1=56
+		ReportingFreq:1=0
+	}
+
+	FailedCountTlvType:2=0x0106
+	FailedCountTlvLength:2={
+		Threshold:1=5
+		ReportingFreq:1=0
+	}
+
+	BeaconMissTlvType:2=0x0107
+	BeaconMissTlvLength:2={
+		BeaconMissed:1=60
+		Reserved:1=0
+	}
+
+	HighRssiTlvType:2=0x0116
+	HighRssiTlvLength:2={
+		Threshold:1=40
+		ReportingFreq:1=0
+	}
+
+	HighSnrTlvType:2=0x0117
+	HighSnrTlvLength:2={
+		Threshold:1=86
+		ReportingFreq:1=0
+	}
+
+	DataLowRssiTlvType:2=0x0126
+	DataLowRssiTlvLength:2={
+		Threshold:1=10
+		ReportingFreq:1=0
+	}
+
+	DataLowSnrTlvType:2=0x0127
+	DataLowSnrTlvLength:2={
+		Threshold:1=66
+		ReportingFreq:1=0
+	}
+
+	DataHighRssiTlvType:2=0x0128
+	DataHighRssiTlvLength:2={
+		Threshold:1=50
+		ReportingFreq:1=0
+	}
+
+	DataHighSnrTlvType:2=0x0129
+	DataHighSnrTlvLength:2={
+		Threshold:1=96
+		ReportingFreq:1=1
+	}
+	LinkQualityTlvType:2=0x0124
+	LinkQualityTlvType:2={
+		LinkSNRThreshold:2=0x0056
+		LinkSNRFrequency:2=0x0003
+		MinRateVal:2=0x0014
+		MinRateFreq:2=0x0003
+		TxLatencyVal:4=0x00C8
+		TxLatencyThreshold:4=0x0003
+	}
+	PreBcnLostTlvType:2=0x0149
+	PreBcnLostTlvLength:2={
+		PreBeaconCnt:1=30
+		Reserved:1=0
+	}
+}
+######################### Subscribe Events command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/tdls.conf b/wlan_sd8897/mapp/mlanconfig/config/tdls.conf
new file mode 100644
index 0000000..d7369cf
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/tdls.conf
@@ -0,0 +1,118 @@
+#	File : tdls.conf
+
+######################### TDLS commands configuration ##################
+# starts with a command name with parameters embedded inside
+
+tdls_setinfo={
+    CapInfo=0x2421
+    Rate=0x02,0x04,0x0b,0x16,0x0C,0x12,0x18,0x24,0x30,0x48,0x60,0x6c
+    QosInfo=0x0F # 1 byte qos capability field. Valid values 0x00 or 0x0F
+                 # For AMPDU over direct link, this should be 0x00
+    ExtendCapabilities=0x00,0x00,0x00,0x50,0x20 # Enable Peer U-APSD(Bit28), TDLS channel switch(Bit30), TDLS(Bit37)
+    HTCapability={
+        HTCapabilityInfo=0x62 # 2 byte HT capability field.
+        AMPDUParam=0x3
+        SupportedMCSSet=0xFF,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+        HTExtCapability=0x00
+        TxBfCapability=0x00
+        AntennaSel=0x00
+    }
+    HTInformation={
+        PrimaryChannel=1
+        Field2=0x0
+        Field3=0x5
+        Field4=0x0
+        BasicMCSSet=0xFF,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
+        # 16 elements as per the structure.
+    }
+    2040BSSCoex=0x01 # 1 byte 2040 Coex field.
+    VHTCapability={
+        VHTCapabilityInfo=0x33C00000 # 4 byte VHT capability field.
+	RxMCSMap=0xFFFC
+	RxMaxRate=0x0000   #bit 29-31 reserved
+	TxMCSMap=0xFFFC
+	TxMaxRate=0x0000  #bit 61-63 reserved
+    }
+    VHTOper={
+	ChanWidth=0x01;
+	ChanCF1=0x01;
+	ChanCF2=0x01;
+	BasicMCSMap=0xFF, 0XFF;    #Basic MCS set map, each 2 bits stands for a Nss
+    }
+#   Uncomment the RSNInfo while testing open-secure network.
+    RSNInfo={
+        GroupCipherSuite=0x00,0x0F,0xAC,0x07
+        PairwiseCipherCount=1
+        PairwiseCipherSuite=0x00,0x0F,0xAC,0x04 # CCMP
+        #PairwiseCipherSuite=0x00,0x0F,0xAC,0x02 # TKIP multiple Pairwise Cipher Suite if count > 1.
+        AKMSuiteCount=1
+        AKMSuite=0x00,0x0F,0xAC,0x07 # multiple AKM Suite if count > 1.
+        #AKMSuite=0x00,0x0F,0xAC,0x07 # multiple AKM Suite if count > 1.
+        RSNCapability=0x200   # Enable PeerKey
+        PMKIDCount=0
+    }
+    SupportedChannels={
+        FirstChannelNo=1
+        NumberofSubBandChannels=11
+        # multiple instances of First Channel No, Number of SubBand Channels
+    }
+    SupportedRegulatoryClasses={
+        CurrentRegulatoryClass=1
+        NumofRegulatoryClasses=15
+        ListOfRegulatoryClasses=1,2,3,4,12,22,23,24,25,27,28,29,30,32,33
+    }
+
+#    CountryInfo={
+#        CountryString="US"         # 3 byte country string
+#        FirstChannel=1
+#        NumberofChannels=11
+#        TxPower=30
+#        # multiple instances of First Channel, Number of Channels, Tx Power
+#    }
+}
+
+tdls_discovery={
+   PeerMAC=00:50:43:27:B0:41        # MAC address of Peer
+}
+
+tdls_setup={
+   PeerMAC=00:50:43:27:B0:41        # MAC address of Peer
+   WaitTimems=3000                  # Time in milliseconds to wait
+                                    # for setup response from peer
+   KeyLifetime=301                   # Lifetime of security key in seconds.
+}
+
+tdls_teardown={
+   PeerMAC=00:50:43:27:B0:41        # MAC address of Peer
+   ReasonCode=26                    # Tear down reason code
+}
+
+tdls_channel_switch={
+   PeerMAC=00:50:43:27:B0:41        # MAC address of Peer
+   Band=0                           # Band
+   PrimaryChannel=6                 # Primary channel no for channel switching
+   SecondaryChannelOffset=0         # Secondary channel offset
+   Periodicity=1                    # Channel switch periodicity
+   ChannelSwitchTime=10              # Channel switch time in ms.
+   ChannelSwitchTimeout=14          # Channel switch timeout in ms
+   RegulatoryClass=12               # Target regulatory class
+}
+
+tdls_cs_params={
+   UnitTime=2                       # Unit time, multiples of 10 ms
+   ThresholdOtherLink=10            # Threshold for other link
+   ThresholdDirectLink=1           # Threshold for direct link
+}
+
+
+tdls_stop_channel_switch={
+   PeerMAC=00:50:43:27:B0:41        # MAC address of Peer
+}
+
+tdls_powermode={
+   PeerMAC=00:50:43:27:B0:41        # MAC address of Peer
+   PowerMode=1                      # 0: Active, 1 : Power Save
+}
+
+
+######################### TDLS commands configuration ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/tspecs.conf b/wlan_sd8897/mapp/mlanconfig/config/tspecs.conf
new file mode 100644
index 0000000..86c1ac6
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/tspecs.conf
@@ -0,0 +1,100 @@
+# TSPEC contents for TID=0, UserPriority = 6
+[tspec0]
+# Element ID
+dd
+# Length
+3d
+# OUI
+00 50 f2
+# OUI Type
+02
+# OUI SubType
+02
+# Version
+01
+# TSInfo
+e0 34 00
+# Nominal MSDU Size
+d0 80
+# Maximum MSDU Size
+d0 00
+# Min Service Interval
+20 4e 00 00
+# Max Service Interval
+20 4e 00 00
+# Inactivity Interval
+80 96 98 00
+# Suspension Interval
+ff ff ff ff
+# Service Start Time
+00 00 00 00
+# Minimum Data Rate
+00 45 01 00
+# Mean Data Rate
+00 45 01 00
+# Peak Data Rate
+00 45 01 00
+# Max Burst Size
+00 00 00 00
+# Delay Bound
+00 00 00 00
+# Min PHY Rate
+00 1b b7 00
+# Surplus Bandwidth Allowance
+00 30
+# Medium Time
+00 00
+# Extra Data Bytes
+[/tspec0]
+
+
+# TSPEC contents for TID=1, UserPriority = 4
+[tspec1]
+# Element ID
+dd
+# Length
+3d
+# OUI
+00 50 f2
+# OUI Type
+02
+# OUI SubType
+02
+# Version
+01
+# TSInfo
+e3 20 00
+# Nominal MSDU Size
+96 00
+# Maximum MSDU Size
+dc 05
+# Min Service Interval
+00 00 00 00
+# Max Service Interval
+00 00 00 00
+# Inactivity Interval
+00 00 00 00
+# Suspension Interval
+ff ff ff ff
+# Service Start Time
+00 00 00 00
+# Minimum Data Rate
+a0 00 00 00
+# Mean Data Rate
+a0 00 00 00
+# Peak Data Rate
+a0 00 00 00
+# Max Burst Size
+00 00 00 00
+# Delay Bound
+00 00 00 00
+# Min PHY Rate
+80 8d 5b 00
+# Surplus Bandwidth Allowance
+00 30
+# Medium Time
+00 00
+# Extra Data Bytes
+dd 04 00 50 43 ff
+[/tspec1]
+
diff --git a/wlan_sd8897/mapp/mlanconfig/config/turbo_mode.conf b/wlan_sd8897/mapp/mlanconfig/config/turbo_mode.conf
new file mode 100644
index 0000000..8a92f2f
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/turbo_mode.conf
@@ -0,0 +1,9 @@
+#File : turbo_mode.conf
+## WMM turbo mode config command
+turbo_mode_set={
+    CmdCode=0x0016          # do NOT change this line
+    Action:2=1              # 1 - SET
+    OID:2=0x27              # OID_WMM_TURBO_MODE
+    Size:2=128
+    Value:128=5             # 1 -- Enable turbo_mode_1; 2 -- Enable turbo_mode_2; otherwise, disable turbo_mode
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/txpwrlimit_cfg.conf b/wlan_sd8897/mapp/mlanconfig/config/txpwrlimit_cfg.conf
new file mode 100644
index 0000000..2192fd5
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/txpwrlimit_cfg.conf
@@ -0,0 +1,610 @@
+#       File : txpwrlimit_cfg.conf
+## Get CFG data for Tx power limitation
+txpwrlimit_2g_cfg_get={
+        CmdCode=0x00fb          # do NOT change this line
+        Action:2=0              # 0 - GET
+        SubBand:2=0x00          # 0x00 2G subband  (2.4G: channel 1-14)
+                                # 0x10 5G subband0 (5G: channel 36,40,44,48,
+                                #                               52,56,60,64)
+                                # 0x11 5G subband1 (5G: channel 100,104,108,112,
+                                #                               116,120,124,128,
+                                #                               132,136,140,144)
+                                # 0x12 5G subband2 (5G: channel 149,153,157,161,165,172)
+                                # 0x13 5G subband3 (5G: channel 183,184,185,187,188,
+                                #                               189, 192,196;
+                                #                   5G: channel 7,8,11,12,16,34)
+}
+
+
+txpwrlimit_5g_cfg_get_sub0={
+        CmdCode=0x00fb          # do NOT change this line
+        Action:2=0              # 0 - GET
+        SubBand:2=0x10          # 0x00 2G subband  (2.4G: channel 1-14)
+                                # 0x10 5G subband0 (5G: channel 36,40,44,48,
+                                #                               52,56,60,64)
+                                # 0x11 5G subband1 (5G: channel 100,104,108,112,
+                                #                               116,120,124,128,
+                                #                               132,136,140,144)
+                                # 0x12 5G subband2 (5G: channel 149,153,157,161,165,172)
+                                # 0x13 5G subband3 (5G: channel 183,184,185,187,188,
+                                #                               189, 192,196;
+                                #                   5G: channel 7,8,11,12,16,34)
+}
+
+
+txpwrlimit_5g_cfg_get_sub1={
+        CmdCode=0x00fb          # do NOT change this line
+        Action:2=0              # 0 - GET
+        SubBand:2=0x11          # 0x00 2G subband  (2.4G: channel 1-14)
+                                # 0x10 5G subband0 (5G: channel 36,40,44,48,
+                                #                               52,56,60,64)
+                                # 0x11 5G subband1 (5G: channel 100,104,108,112,
+                                #                               116,120,124,128,
+                                #                               132,136,140,144)
+                                # 0x12 5G subband2 (5G: channel 149,153,157,161,165,172)
+                                # 0x13 5G subband3 (5G: channel 183,184,185,187,188,
+                                #                               189, 192,196;
+                                #                   5G: channel 7,8,11,12,16,34)
+}
+
+
+txpwrlimit_5g_cfg_get_sub2={
+        CmdCode=0x00fb          # do NOT change this line
+        Action:2=0              # 0 - GET
+        SubBand:2=0x12          # 0x00 2G subband  (2.4G: channel 1-14)
+                                # 0x10 5G subband0 (5G: channel 36,40,44,48,
+                                #                               52,56,60,64)
+                                # 0x11 5G subband1 (5G: channel 100,104,108,112,
+                                #                               116,120,124,128,
+                                #                               132,136,140,144)
+                                # 0x12 5G subband2 (5G: channel 149,153,157,161,165,172)
+                                # 0x13 5G subband3 (5G: channel 183,184,185,187,188,
+                                #                               189, 192,196;
+                                #                   5G: channel 7,8,11,12,16,34)
+}
+
+
+txpwrlimit_5g_cfg_get_sub3={
+        CmdCode=0x00fb          # do NOT change this line
+        Action:2=0              # 0 - GET
+        SubBand:2=0x13          # 0x00 2G subband  (2.4G: channel 1-14)
+                                # 0x10 5G subband0 (5G: channel 36,40,44,48,
+                                #                               52,56,60,64)
+                                # 0x11 5G subband1 (5G: channel 100,104,108,112,
+                                #                               116,120,124,128,
+                                #                               132,136,140,144)
+                                # 0x12 5G subband2 (5G: channel 149,153,157,161,165,172)
+                                # 0x13 5G subband3 (5G: channel 183,184,185,187,188,
+                                #                               189, 192,196;
+                                #                   5G: channel 7,8,11,12,16,34)
+}
+
+## Set CFG data for Tx power limitation
+##
+## TLVStartFreq: Starting Frequency of the band for this channel
+##                 2407, 2414 or 2400 for 2.4 GHz
+##                 5000
+##                 4000
+## TLVChanWidth: Channel Width
+##                 20
+## TLVChanNum  : Channel Number
+## TLVPwr[]    : ModulationGroup
+##                 0: CCK (1,2,5.5,11 Mbps)
+##                 1: OFDM (6,9,12,18 Mbps)
+##                 2: OFDM (24,36 Mbps)
+##                 3: OFDM (48,54 Mbps)
+##                 4: HT20 (MCS0,1,2)
+##                 5: HT20 (MCS3,4)
+##                 6: HT20 (MCS5,6,7)
+##                 7: HT40 (MCS0,1,2)
+##                 8: HT40 (MCS3,4)
+##                 9: HT40 (MCS5,6,7)
+##                 10: HT2_20 (MCS8,9,10)
+##                 11: HT2_20 (MCS11,12)
+##                 12: HT2_20 (MCS13,14,15)
+##                 13: HT2_40 (MCS8,9,10)
+##                 14: HT2_40 (MCS11,12)
+##                 15: HT2_40 (MCS13,14,15)
+##                 16: VHT_QAM256 (MCS8)
+##                 17: VHT_40_QAM256 (MCS8,9)
+##                 18: VHT_80_PSK (MCS0,1,2)
+##                 19: VHT_80_QAM16 (MCS3,4)
+##                 20: VHT_80_QAM64 (MCS5,6,7)
+##                 21: VHT_80_QAM256 (MCS8,9)
+##                 22: VHT2_20_QAM256 (MCS8,9)
+##                 23: VHT2_40_QAM256 (MCS8,9)
+##                 24: VHT2_80_PSK (MCS0, 1, 2)
+##                 25: VHT2_80_QAM16 (MCS3,4)
+##                 26: VHT2_80_QAM64 (MCS5,6,7)
+##                 27: VHT2_80_QAM256 (MCS8,9)
+##               Power Limit in dBm
+##
+## Note: For KF, add VHT 20/40/80 1SS/2SS mod group.
+
+## 2G subband0 Tx power limit CFG
+txpwrlimit_2g_cfg_set={
+        CmdCode=0x00fb          # do NOT change this line
+        Action:2=1              # 1 - SET
+        SubBand:2=0             # do NOT use this member in set cmd
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=1
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=2
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=3
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=4
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=5
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=6
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=7
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=8
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=9
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=10
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=11
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=12
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=13
+            TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15'
+        }
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=2407
+            TLVChanWidth:1=20
+            TLVChanNum:1=14
+            TLVPwr:32='0,12,1,12,2,12,3,12,4,12,5,12,6,12,7,12,8,12,9,12,10,12,11,12,12,12,13,12,14,12,15,12'
+        }
+}
+
+
+## 5G subband1 Tx power limit CFG
+txpwrlimit_5g_cfg_set_sub0={
+        CmdCode=0x00fb          # do NOT change this line
+        Action:2=1              # 1 - SET
+        SubBand:2=0             # do NOT use this member in set cmd
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=36
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=40
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=44
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=48
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=52
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=56
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=60
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=64
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+}
+
+## 5G subband2 Tx power limit CFG
+txpwrlimit_5g_cfg_set_sub1={
+        CmdCode=0x00fb          # do NOT change this line
+        Action:2=1              # 1 - SET
+        SubBand:2=0             # do NOT use this member in set cmd
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=100
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=104
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=108
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=112
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=116
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=120
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=124
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=128
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=132
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=136
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=140
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=144
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+}
+
+
+## 5G subband3 Tx power limit CFG
+txpwrlimit_5g_cfg_set_sub2={
+        CmdCode=0x00fb          # do NOT change this line
+        Action:2=1              # 1 - SET
+        SubBand:2=0             # do NOT use this member in set cmd
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=149
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=153
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=157
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=161
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=165
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+}
+
+
+## 5G subband4 Tx power limit CFG
+txpwrlimit_5g_cfg_set_sub3={
+        CmdCode=0x00fb          # do NOT change this line
+        Action:2=1              # 1 - SET
+        SubBand:2=0             # do NOT use this in set cmd
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=4000
+            TLVChanWidth:1=20
+            TLVChanNum:1=183
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=4000
+            TLVChanWidth:1=20
+            TLVChanNum:1=184
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=4000
+            TLVChanWidth:1=20
+            TLVChanNum:1=185
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=4000
+            TLVChanWidth:1=20
+            TLVChanNum:1=187
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=4000
+            TLVChanWidth:1=20
+            TLVChanNum:1=188
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=4000
+            TLVChanWidth:1=20
+            TLVChanNum:1=189
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=4000
+            TLVChanWidth:1=20
+            TLVChanNum:1=192
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=4000
+            TLVChanWidth:1=20
+            TLVChanNum:1=196
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=7
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=8
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=11
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=12
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=16
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+
+
+        ChanTRPC.TlvType:2=0x0189
+        ChanTRPC.TlvLength:2={
+            TLVStartFreq:2=5000
+            TLVChanWidth:1=20
+            TLVChanNum:1=34
+            TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10'
+        }
+}
+
diff --git a/wlan_sd8897/mapp/mlanconfig/config/txrate_cfg.conf b/wlan_sd8897/mapp/mlanconfig/config/txrate_cfg.conf
new file mode 100644
index 0000000..4d2f789
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/config/txrate_cfg.conf
@@ -0,0 +1,171 @@
+#	File : txrate_cfg.conf
+
+## Tx Rate Configuration command
+txrate_cfg_get={
+	CmdCode=0x00d6		# do NOT change this line
+        Action:2=0              # 0 - GET
+        Index:2=0               # do NOT change this line
+
+	TxRateScope.TlvType:2=0x0153
+	TxRateScope.TlvLength:2={
+	}
+}
+
+txrate_cfg_set_bg={
+	CmdCode=0x00d6		# do NOT change this line
+
+	Action:2=1		# 1 - SET
+	Index:2=0		# do NOT change this line
+
+	TxRateScope.TlvType:2=0x0153
+	TxRateScope.TlvLength:2={
+                      ################# TXRATE SCOPE ######################
+
+                                # The following table shows the bitmap of the rates:
+                                # (bit 0 is the least significant bit)
+                                #       Bit     Data rate
+                                #       0       1 Mbps
+                                #       1       2 Mbps
+                                #       2       5.5 Mbps
+                                #       3       11 Mbps
+                                #       4       Reserved
+                HRDSSS.RateScope:2=0x0000
+
+                                # The following table shows the bitmap of the rates:
+                                # (bit 0 is the least significant bit)
+                                #       Bit     Data rate
+                                #       0       6 Mbps
+                                #       1       9 Mbps
+                                #       2       12 Mbps
+                                #       3       18 Mbps
+                                #       4       24 Mbps
+                                #       5       36 Mbps
+                                #       6       48 Mbps
+                                #       7       54 Mbps
+                OFDM.RateScope:2=0x0080
+
+                                # The following table shows the bitmap of the rates:
+                                # (bit 0 is the least significant bit)
+                                #       Bit     Data rate
+                                #       0       MCS0
+                                #       1       MCS1
+                                #       2       MCS2
+                                #       3       MCS3
+                                #       4       MCS4
+                                #       5       MCS5
+                                #       6       MCS6
+                                #       7       MCS7
+                                #       32      MCS32
+                HT.RateScopeDword0:4=0x00000000
+                HT.RateScopeDword1:4=0x00000000
+                HT.RateScopeDword2:4=0x00000000
+                HT.RateScopeDword3:4=0x00000000
+	}
+
+        TxRateDrop.TlvType:2=0x0151
+        TxRateDrop.TlvLength:2={
+                RateDrop.Mode:4=0x00000001
+        }
+}
+
+txrate_cfg_set_bgn={
+	CmdCode=0x00d6		# do NOT change this line
+
+	Action:2=1		# 1 - SET
+	Index:2=0		# do NOT change this line
+
+	TxRateScope.TlvType:2=0x0153
+	TxRateScope.TlvLength:2={
+                      ################# TXRATE SCOPE ######################
+
+                                # The following table shows the bitmap of the rates:
+                                # (bit 0 is the least significant bit)
+                                #       Bit     Data rate
+                                #       0       1 Mbps
+                                #       1       2 Mbps
+                                #       2       5.5 Mbps
+                                #       3       11 Mbps
+                                #       4       Reserved
+                HRDSSS.RateScope:2=0x0000
+
+                                # The following table shows the bitmap of the rates:
+                                # (bit 0 is the least significant bit)
+                                #       Bit     Data rate
+                                #       0       6 Mbps
+                                #       1       9 Mbps
+                                #       2       12 Mbps
+                                #       3       18 Mbps
+                                #       4       24 Mbps
+                                #       5       36 Mbps
+                                #       6       48 Mbps
+                                #       7       54 Mbps
+                OFDM.RateScope:2=0x0000
+
+                                # The following table shows the bitmap of the rates:
+                                # (bit 0 is the least significant bit)
+                                #       Bit     Data rate
+                                #       0       MCS0
+                                #       1       MCS1
+                                #       2       MCS2
+                                #       3       MCS3
+                                #       4       MCS4
+                                #       5       MCS5
+                                #       6       MCS6
+                                #       7       MCS7
+                                #       32      MCS32
+                HT.RateScopeDword0:4=0x00000080
+                HT.RateScopeDword1:4=0x00000000
+                HT.RateScopeDword2:4=0x00000000
+                HT.RateScopeDword3:4=0x00000000
+	}
+
+        TxRateDrop.TlvType:2=0x0151
+        TxRateDrop.TlvLength:2={
+                RateDrop.Mode:4=0x00000001
+        }
+}
+
+########supported BasicRate setting###########
+basic_rate_get={
+    CmdCode=0x00d6    # do NOT change this line
+        Action:2=0    # 0 - GET
+         Index:2=0    # do NOT change this line
+
+    TlvType:2=0x21a
+    TlvLength:2={
+    }
+}
+
+basic_rate_set={
+    CmdCode=0x00d6 # do NOT change this line
+
+    Action:2=1     # 1 - SET
+    Index:2=0      # do NOT change this line
+
+    TlvType:2=0x21a
+    TlvLength:2={
+    BasicRateSupport:2=0x000f  #defalt value
+
+       # The following table shows the bitmap of the rates:
+       # (bit 0 is the least significant bit)
+       #       Bit     BasicRateSupport
+       #       0       DBPSK1Mbps
+       #       1       DQPSK2Mbps
+       #       2       CCK5_5Mbps
+       #       3       CCK11Mbps
+       #       4       Not used.
+       #       5       OFDM6Mbps
+       #       6       OFDM9Mbps
+       #       7       OFDM12Mbps
+       #       8       OFDM18Mbps
+       #       9       OFDM24Mbps
+       #      10       OFDM36Mbps
+       #      11       OFDM48Mbps
+       #      12       OFDM54Mbps
+       #     13-15     Reserved
+       #note: value0x0 represents no setting value
+       #For example:
+       #BasicRateSupport:2=0x3 set supported BasicRate to DBPSK1Mbps, DQPSK2Mbps
+       #BasicRateSupport:2=0x180 set supported BasicRate to OFDM18Mbps, OFDM12Mbps
+    }
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanconfig.c b/wlan_sd8897/mapp/mlanconfig/mlanconfig.c
new file mode 100644
index 0000000..b876af1
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/mlanconfig.c
@@ -0,0 +1,4842 @@
+/** @file  mlanconfig.c
+  *
+  * @brief Program to configure addition parameters into the mlandriver
+  *
+  * Copyright (C) 2008-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     11/26/2008: initial version
+     03/10/2009:  add setuserscan, getscantable etc. commands
+     08/11/2009:  add addts, regclass, setra, scanagent etc. commands
+************************************************************************/
+
+#include    "mlanconfig.h"
+#include    "mlanhostcmd.h"
+#include    "mlanmisc.h"
+
+/** mlanconfig version number */
+#define MLANCONFIG_VER "M2.0"
+
+/** Initial number of total private ioctl calls */
+#define IW_INIT_PRIV_NUM    128
+/** Maximum number of total private ioctl calls supported */
+#define IW_MAX_PRIV_NUM     1024
+
+/********************************************************
+		Local Variables
+********************************************************/
+
+/** Private ioctl commands */
+enum COMMANDS {
+	CMD_HOSTCMD,
+	CMD_MEFCFG,
+	CMD_ARPFILTER,
+	CMD_CFG_DATA,
+	CMD_CMD52RW,
+	CMD_CMD53RW,
+	CMD_GET_SCAN_RSP,
+	CMD_SET_USER_SCAN,
+	CMD_ADD_TS,
+	CMD_DEL_TS,
+	CMD_QCONFIG,
+	CMD_QSTATS,
+	CMD_TS_STATUS,
+	CMD_WMM_QSTATUS,
+	CMD_REGRW,
+	CMD_MEMRW,
+	CMD_STA_CUSTOM_IE,
+	CMD_STA_MGMT_FRAME_TX,
+	CMD_TDLS_CONF,
+	CMD_TDLS_INFO,
+	CMD_TDLS_DISCOVERY,
+	CMD_TDLS_SETUP,
+	CMD_TDLS_TEARDOWN,
+	CMD_TDLS_POWERMODE,
+	CMD_TDLS_LINK_STATUS,
+	CMD_TDLS_DEBUG,
+	CMD_TDLS_CHANNEL_SWITCH,
+	CMD_TDLS_STOP_CHAN_SWITCH,
+	CMD_TDLS_CS_PARAMS,
+	CMD_TDLS_CS_DISABLE,
+};
+
+static t_s8 *commands[] = {
+	"hostcmd",
+	"mefcfg",
+	"arpfilter",
+	"cfgdata",
+	"sdcmd52rw",
+	"sdcmd53rw",
+	"getscantable",
+	"setuserscan",
+	"addts",
+	"delts",
+	"qconfig",
+	"qstats",
+	"ts_status",
+	"qstatus",
+	"regrdwr",
+	"memrdwr",
+	"customie",
+	"mgmtframetx",
+	"tdls_config",
+	"tdls_setinfo",
+	"tdls_discovery",
+	"tdls_setup",
+	"tdls_teardown",
+	"tdls_powermode",
+	"tdls_link_status",
+	"tdls_debug",
+	"tdls_channel_switch",
+	"tdls_stop_channel_switch",
+	"tdls_cs_params",
+	"tdls_disable_cs",
+};
+
+static t_s8 *usage[] = {
+	"Usage: ",
+	"   mlanconfig -v  (version)",
+	"   mlanconfig <mlanX> <cmd> [...]",
+	"   where",
+	"   mlanX : wireless network interface",
+	"   cmd : hostcmd",
+	"     : mefcfg",
+	"     : customie",
+	"     : mgmtframetx",
+	"     : arpfilter",
+	"     : tdls_config",
+	"     : tdls_setinfo",
+	"     : tdls_discovery",
+	"     : tdls_setup",
+	"     : tdls_teardown",
+	"     : tdls_powermode",
+	"     : tdls_link_status",
+	"     : tdls_debug",
+	"     : tdls_channel_switch",
+	"     : tdls_stop_channel_switch",
+	"     : tdls_cs_params",
+	"     : tdls_disable_cs",
+	"     : cfgdata",
+	"     : sdcmd52rw, sdcmd53rw",
+	"     : getscantable, setuserscan",
+	"     : addts, delts, qconfig, qstats, ts_status, qstatus",
+	"     : regrdwr, memrdwr",
+	"     : additional parameter for hostcmd",
+	"     :   <filename> <cmd>",
+	" 	  : additional parameters for mefcfg are:",
+	"     :   <filename>",
+	"      : additional parameters for customie are:",
+	"      :  <index> <mask> <IE buffer>",
+	"      : additional parameters for mgmtframetx are:",
+	"      : <pkt file>",
+	"     : additional parameter for arpfilter",
+	"     :   <filename>",
+	"     : additional parameter for tdls_setinfo",
+	"     :   <filename>",
+	"     : additional parameter for tdls_discovery",
+	"     :   <filename>",
+	"     : additional parameter for tdls_setup",
+	"     :   <filename>",
+	"     : additional parameter for tdls_teardown",
+	"     :   <filename>",
+	"     : additional parameter for tdls_powermode",
+	"     :   <filename>",
+	"     : additional parameter for tdls_debug <parameters>",
+	"     : additional parameter for tdls_channel_switch",
+	"     :   <filename>",
+	"     : additional parameter for tdls_stop_channel_switch",
+	"     :   <filename>",
+	"     : additional parameter for tdls_cs_params",
+	"     :   <filename>",
+	"     : additional parameter for cfgdata",
+	"     :   <type> <filename>",
+	"     : additional parameter for sdcmd52rw",
+	"     :   <function> <reg addr.> <data>",
+	"     : additional parameter for sdcmd53rw",
+	"     :   <func> <addr> <mode> <blksiz> <blknum> <data1> ... ...<dataN> ",
+	"     : additional parameter for addts",
+	"     :   <filename.conf> <section# of tspec> <timeout in ms>",
+	"     : additional parameter for delts",
+	"     :   <filename.conf> <section# of tspec>",
+	"     : additional parameter for qconfig",
+	"     :   <[set msdu <lifetime in TUs> [Queue Id: 0-3]]",
+	"     :    [get [Queue Id: 0-3]] [def [Queue Id: 0-3]]>",
+	"     : additional parameter for qstats",
+	"     :   <[get [User Priority: 0-7]]>",
+	"     : additional parameter for regrdwr",
+	"     :   <type> <offset> [value]",
+	"     : additional parameter for memrdwr",
+	"     :   <address> [value]",
+};
+
+t_s32 sockfd;  /**< socket */
+t_s8 dev_name[IFNAMSIZ + 1];	/**< device name */
+static struct iw_priv_args *priv_args = NULL;	   /**< private args */
+static int we_version_compiled = 0;
+				  /**< version compiled */
+
+/********************************************************
+		Global Variables
+********************************************************/
+
+/********************************************************
+		Local Functions
+********************************************************/
+/**
+ *    @brief isdigit for String.
+ *
+ *    @param x            Char string
+ *    @return             MLAN_STATUS_FAILURE for non-digit.
+ *                        MLAN_STATUS_SUCCESS for digit
+ */
+static int
+ISDIGIT(t_s8 *x)
+{
+	unsigned int i;
+	for (i = 0; i < strlen(x); i++)
+		if (isdigit(x[i]) == 0)
+			return MLAN_STATUS_FAILURE;
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ * Check of decimal or hex string
+ * @param   num string
+ */
+#define IS_HEX_OR_DIGIT(num) \
+    (strncasecmp("0x", (num), 2)?ISDIGIT((num)):ishexstring((num)))
+
+/**
+ *  @brief Get private info.
+ *
+ *  @param ifname   A pointer to net name
+ *  @return 	    MLAN_STATUS_SUCCESS--success, otherwise --fail
+ */
+static int
+get_private_info(const t_s8 *ifname)
+{
+	/* This function sends the SIOCGIWPRIV command, which is
+	 * handled by the kernel and gets the total number of
+	 * private ioctl's available in the host driver.
+	 */
+	struct iwreq iwr;
+	int s, ret = MLAN_STATUS_SUCCESS;
+	struct iw_priv_args *ppriv = NULL;
+	struct iw_priv_args *new_priv;
+	int result = 0;
+	size_t size = IW_INIT_PRIV_NUM;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket[PF_INET,SOCK_DGRAM]");
+		return MLAN_STATUS_FAILURE;
+	}
+
+	memset(&iwr, 0, sizeof(iwr));
+	strncpy(iwr.ifr_name, ifname, IFNAMSIZ - 1);
+
+	do {
+		/* (Re)allocate the buffer */
+		new_priv = realloc(ppriv, size * sizeof(ppriv[0]));
+		if (new_priv == NULL) {
+			printf("Error: Buffer allocation failed\n");
+			ret = MLAN_STATUS_FAILURE;
+			break;
+		}
+		ppriv = new_priv;
+
+		iwr.u.data.pointer = (caddr_t) ppriv;
+		iwr.u.data.length = size;
+		iwr.u.data.flags = 0;
+
+		if (ioctl(s, SIOCGIWPRIV, &iwr) < 0) {
+			result = errno;
+			ret = MLAN_STATUS_FAILURE;
+			if (result == E2BIG) {
+				/* We need a bigger buffer. Check if kernel gave us any hints. */
+				if (iwr.u.data.length > size) {
+					/* Kernel provided required size */
+					size = iwr.u.data.length;
+				} else {
+					/* No hint from kernel, double the buffer size */
+					size *= 2;
+				}
+			} else {
+				/* ioctl error */
+				perror("ioctl[SIOCGIWPRIV]");
+				break;
+			}
+		} else {
+			/* Success. Return the number of private ioctls */
+			priv_args = ppriv;
+			ret = iwr.u.data.length;
+			break;
+		}
+	} while (size <= IW_MAX_PRIV_NUM);
+
+	if ((ret == MLAN_STATUS_FAILURE) && (ppriv))
+		free(ppriv);
+
+	close(s);
+
+	return ret;
+}
+
+/**
+ *  @brief Get Sub command ioctl number
+ *
+ *  @param i        command index
+ *  @param priv_cnt     Total number of private ioctls availabe in driver
+ *  @param ioctl_val    A pointer to return ioctl number
+ *  @param subioctl_val A pointer to return sub-ioctl number
+ *  @return 	        MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+static int
+marvell_get_subioctl_no(t_s32 i,
+			t_s32 priv_cnt, int *ioctl_val, int *subioctl_val)
+{
+	t_s32 j;
+
+	if (priv_args[i].cmd >= SIOCDEVPRIVATE) {
+		*ioctl_val = priv_args[i].cmd;
+		*subioctl_val = 0;
+		return MLAN_STATUS_SUCCESS;
+	}
+
+	j = -1;
+
+	/* Find the matching *real* ioctl */
+
+	while ((++j < priv_cnt)
+	       && ((priv_args[j].name[0] != '\0') ||
+		   (priv_args[j].set_args != priv_args[i].set_args) ||
+		   (priv_args[j].get_args != priv_args[i].get_args))) {
+	}
+
+	/* If not found... */
+	if (j == priv_cnt) {
+		printf("%s: Invalid private ioctl definition for: 0x%x\n",
+		       dev_name, priv_args[i].cmd);
+		return MLAN_STATUS_FAILURE;
+	}
+
+	/* Save ioctl numbers */
+	*ioctl_val = priv_args[j].cmd;
+	*subioctl_val = priv_args[i].cmd;
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Get ioctl number
+ *
+ *  @param ifname   	A pointer to net name
+ *  @param priv_cmd	A pointer to priv command buffer
+ *  @param ioctl_val    A pointer to return ioctl number
+ *  @param subioctl_val A pointer to return sub-ioctl number
+ *  @return 	        MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+static int
+marvell_get_ioctl_no(const t_s8 *ifname,
+		     const t_s8 *priv_cmd, int *ioctl_val, int *subioctl_val)
+{
+	t_s32 i;
+	t_s32 priv_cnt;
+	int ret = MLAN_STATUS_FAILURE;
+
+	priv_cnt = get_private_info(ifname);
+
+	/* Are there any private ioctls? */
+	if (priv_cnt <= 0 || priv_cnt > IW_MAX_PRIV_NUM) {
+		/* Could skip this message ? */
+		printf("%-8.8s  no private ioctls.\n", ifname);
+	} else {
+		for (i = 0; i < priv_cnt; i++) {
+			if (priv_args[i].name[0] &&
+			    !strcmp(priv_args[i].name, priv_cmd)) {
+				ret = marvell_get_subioctl_no(i, priv_cnt,
+							      ioctl_val,
+							      subioctl_val);
+				break;
+			}
+		}
+	}
+
+	if (priv_args) {
+		free(priv_args);
+		priv_args = NULL;
+	}
+
+	return ret;
+}
+
+/**
+ *  @brief Retrieve the ioctl and sub-ioctl numbers for the given ioctl string
+ *
+ *  @param ioctl_name   Private IOCTL string name
+ *  @param ioctl_val    A pointer to return ioctl number
+ *  @param subioctl_val A pointer to return sub-ioctl number
+ *
+ *  @return             MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+int
+get_priv_ioctl(char *ioctl_name, int *ioctl_val, int *subioctl_val)
+{
+	int retval;
+
+	retval = marvell_get_ioctl_no(dev_name,
+				      ioctl_name, ioctl_val, subioctl_val);
+
+	return retval;
+}
+
+/**
+ *  @brief Process host_cmd
+ *  @param argc		number of arguments
+ *  @param argv		A pointer to arguments array
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_host_cmd(int argc, char *argv[])
+{
+	t_s8 cmdname[256];
+	t_u8 *buf;
+	HostCmd_DS_GEN *hostcmd;
+	struct iwreq iwr;
+	int ret = MLAN_STATUS_SUCCESS;
+	int ioctl_val, subioctl_val;
+	FILE *fp = NULL;
+
+	if (get_priv_ioctl("hostcmd",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	if (argc < 5) {
+		printf("Error: invalid no of arguments\n");
+		printf("Syntax: ./mlanconfig mlanX hostcmd <hostcmd.conf> <cmdname>\n");
+		exit(1);
+	}
+
+	if ((fp = fopen(argv[3], "r")) == NULL) {
+		fprintf(stderr, "Cannot open file %s\n", argv[3]);
+		exit(1);
+	}
+
+	buf = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (buf == NULL) {
+		printf("Error: allocate memory for hostcmd failed\n");
+		fclose(fp);
+		return -ENOMEM;
+	}
+	snprintf(cmdname, sizeof(cmdname), "%s", argv[4]);
+	ret = prepare_host_cmd_buffer(fp, cmdname, buf);
+	fclose(fp);
+
+	if (ret == MLAN_STATUS_FAILURE)
+		goto _exit_;
+
+	hostcmd = (HostCmd_DS_GEN *)buf;
+	memset(&iwr, 0, sizeof(iwr));
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.pointer = (t_u8 *)hostcmd;
+	iwr.u.data.length = le16_to_cpu(hostcmd->size);
+
+	iwr.u.data.flags = 0;
+	if (ioctl(sockfd, ioctl_val, &iwr)) {
+		fprintf(stderr,
+			"mlanconfig: MLANHOSTCMD is not supported by %s\n",
+			dev_name);
+		ret = MLAN_STATUS_FAILURE;
+		goto _exit_;
+	}
+	ret = process_host_cmd_resp(buf);
+
+_exit_:
+	if (buf)
+		free(buf);
+
+	return ret;
+}
+
+/**
+ *  @brief  get range
+ *
+ *  @return	MLAN_STATUS_SUCCESS--success, otherwise --fail
+ */
+static int
+get_range(t_void)
+{
+	struct iw_range *range;
+	struct iwreq iwr;
+	size_t buf_len;
+
+	buf_len = sizeof(struct iw_range) + 500;
+	range = malloc(buf_len);
+	if (range == NULL) {
+		printf("Error: allocate memory for iw_range failed\n");
+		return -ENOMEM;
+	}
+	memset(range, 0, buf_len);
+	memset(&iwr, 0, sizeof(struct iwreq));
+	iwr.u.data.pointer = (caddr_t) range;
+	iwr.u.data.length = buf_len;
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+
+	if ((ioctl(sockfd, SIOCGIWRANGE, &iwr)) < 0) {
+		printf("Get Range Results Failed\n");
+		free(range);
+		return MLAN_STATUS_FAILURE;
+	}
+	we_version_compiled = range->we_version_compiled;
+	printf("Driver build with Wireless Extension %d\n",
+	       range->we_version_compiled);
+	free(range);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Display usage
+ *
+ *  @return       NA
+ */
+static t_void
+display_usage(t_void)
+{
+	t_u32 i;
+	for (i = 0; i < NELEMENTS(usage); i++)
+		fprintf(stderr, "%s\n", usage[i]);
+}
+
+/**
+ *  @brief Find command
+ *
+ *  @param maxcmds  max command number
+ *  @param cmds     A pointer to commands buffer
+ *  @param cmd      A pointer to command buffer
+ *  @return         index of command or MLAN_STATUS_FAILURE
+ */
+static int
+findcommand(t_s32 maxcmds, t_s8 *cmds[], t_s8 *cmd)
+{
+	t_s32 i;
+
+	for (i = 0; i < maxcmds; i++) {
+		if (!strcasecmp(cmds[i], cmd)) {
+			return i;
+		}
+	}
+
+	return MLAN_STATUS_FAILURE;
+}
+
+/**
+ *  @brief Process arpfilter
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_arpfilter(int argc, char *argv[])
+{
+	t_u8 *buf;
+	struct iwreq iwr;
+	t_u16 length = 0;
+	int ret = MLAN_STATUS_SUCCESS;
+	FILE *fp = NULL;
+	int ioctl_val, subioctl_val;
+
+	if (get_priv_ioctl("arpfilter",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	if (argc < 4) {
+		printf("Error: invalid no of arguments\n");
+		printf("Syntax: ./mlanconfig mlanX arpfilter <arpfilter.conf>\n");
+		exit(1);
+	}
+
+	if ((fp = fopen(argv[3], "r")) == NULL) {
+		fprintf(stderr, "Cannot open file %s\n", argv[3]);
+		return MLAN_STATUS_FAILURE;
+	}
+	buf = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (buf == NULL) {
+		printf("Error: allocate memory for arpfilter failed\n");
+		fclose(fp);
+		return -ENOMEM;
+	}
+	ret = prepare_arp_filter_buffer(fp, buf, &length);
+	fclose(fp);
+
+	if (ret == MLAN_STATUS_FAILURE)
+		goto _exit_;
+
+	memset(&iwr, 0, sizeof(iwr));
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.pointer = buf;
+	iwr.u.data.length = length;
+	iwr.u.data.flags = 0;
+	if (ioctl(sockfd, ioctl_val, &iwr)) {
+		fprintf(stderr,
+			"mlanconfig: arpfilter command is not supported by %s\n",
+			dev_name);
+		ret = MLAN_STATUS_FAILURE;
+		goto _exit_;
+	}
+
+_exit_:
+	if (buf)
+		free(buf);
+
+	return ret;
+}
+
+/**
+ *  @brief Process cfgdata
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_cfg_data(int argc, char *argv[])
+{
+	t_u8 *buf;
+	HostCmd_DS_GEN *hostcmd;
+	struct iwreq iwr;
+	int ret = MLAN_STATUS_SUCCESS;
+	int ioctl_val, subioctl_val;
+	FILE *fp = NULL;
+
+	if (get_priv_ioctl("hostcmd",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	if (argc < 4 || argc > 5) {
+		printf("Error: invalid no of arguments\n");
+		printf("Syntax: ./mlanconfig mlanX cfgdata <register type> <filename>\n");
+		exit(1);
+	}
+
+	if (argc == 5) {
+		if ((fp = fopen(argv[4], "r")) == NULL) {
+			fprintf(stderr, "Cannot open file %s\n", argv[3]);
+			exit(1);
+		}
+	}
+	buf = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (buf == NULL) {
+		printf("Error: allocate memory for hostcmd failed\n");
+		if (argc == 5)
+			fclose(fp);
+		return -ENOMEM;
+	}
+	ret = prepare_cfg_data_buffer(argc, argv, fp, buf);
+	if (argc == 5)
+		fclose(fp);
+
+	if (ret == MLAN_STATUS_FAILURE)
+		goto _exit_;
+
+	hostcmd = (HostCmd_DS_GEN *)buf;
+	memset(&iwr, 0, sizeof(iwr));
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.pointer = (t_u8 *)hostcmd;
+	iwr.u.data.length = le16_to_cpu(hostcmd->size);
+
+	iwr.u.data.flags = 0;
+	if (ioctl(sockfd, ioctl_val, &iwr)) {
+		fprintf(stderr,
+			"mlanconfig: MLANHOSTCMD is not supported by %s\n",
+			dev_name);
+		ret = MLAN_STATUS_FAILURE;
+		goto _exit_;
+	}
+	ret = process_host_cmd_resp(buf);
+
+_exit_:
+	if (buf)
+		free(buf);
+
+	return ret;
+}
+
+/**
+ *  @brief read current command
+ *  @param ptr      A pointer to data
+ *  @param curCmd   A pointer to the buf which will hold current command
+ *  @return         NULL or the pointer to the left command buf
+ */
+static t_s8 *
+readCurCmd(t_s8 *ptr, t_s8 *curCmd)
+{
+	t_s32 i = 0;
+#define MAX_CMD_SIZE 64	/**< Max command size */
+
+	while (*ptr != ']' && i < (MAX_CMD_SIZE - 1))
+		curCmd[i++] = *(++ptr);
+
+	if (*ptr != ']')
+		return NULL;
+
+	curCmd[i - 1] = '\0';
+
+	return ++ptr;
+}
+
+/**
+ *  @brief parse command and hex data
+ *  @param fp       A pointer to FILE stream
+ *  @param dst      A pointer to the dest buf
+ *  @param cmd      A pointer to command buf for search
+ *  @return         Length of hex data or MLAN_STATUS_FAILURE
+ */
+static int
+fparse_for_cmd_and_hex(FILE * fp, t_u8 *dst, t_u8 *cmd)
+{
+	t_s8 *ptr;
+	t_u8 *dptr;
+	t_s8 buf[256], curCmd[64];
+	t_s32 isCurCmd = 0;
+
+	dptr = dst;
+	while (fgets(buf, sizeof(buf), fp)) {
+		ptr = buf;
+
+		while (*ptr) {
+			/* skip leading spaces */
+			while (*ptr && isspace(*ptr))
+				ptr++;
+
+			/* skip blank lines and lines beginning with '#' */
+			if (*ptr == '\0' || *ptr == '#')
+				break;
+
+			if (*ptr == '[' && *(ptr + 1) != '/') {
+				if (!(ptr = readCurCmd(ptr, curCmd)))
+					return MLAN_STATUS_FAILURE;
+
+				if (strcasecmp(curCmd, (char *)cmd))	/* Not equal */
+					isCurCmd = 0;
+				else
+					isCurCmd = 1;
+			}
+
+			/* Ignore the rest if it is not correct cmd */
+			if (!isCurCmd)
+				break;
+
+			if (*ptr == '[' && *(ptr + 1) == '/')
+				return (dptr - dst);
+
+			if (isxdigit(*ptr)) {
+				ptr = convert2hex(ptr, dptr++);
+			} else {
+				/* Invalid character on data line */
+				ptr++;
+			}
+		}
+	}
+
+	return MLAN_STATUS_FAILURE;
+}
+
+/**
+ *  @brief Send an ADDTS command to the associated AP
+ *
+ *  Process a given conf file for a specific TSPEC data block.  Send the
+ *    TSPEC along with any other IEs to the driver/firmware for transmission
+ *    in an ADDTS request to the associated AP.
+ *
+ *  Return the execution status of the command as well as the ADDTS response
+ *    from the AP if any.
+ *
+ *  mlanconfig mlanX addts <filename.conf> <section# of tspec> <timeout in ms>
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_addts(int argc, char *argv[])
+{
+	int ioctl_val, subioctl_val;
+	struct iwreq iwr;
+	wlan_ioctl_wmm_addts_req_t addtsReq;
+
+	FILE *fp = NULL;
+	char filename[48];
+	char config_id[20];
+
+	memset(&addtsReq, 0x00, sizeof(addtsReq));
+	memset(filename, 0x00, sizeof(filename));
+
+	if (get_priv_ioctl("addts",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	if (argc != 6) {
+		fprintf(stderr, "Invalid number of parameters!\n");
+		return -EINVAL;
+	}
+
+	strncpy(filename, argv[3], MIN(sizeof(filename) - 1, strlen(argv[3])));
+	if ((fp = fopen(filename, "r")) == NULL) {
+		perror("fopen");
+		fprintf(stderr, "Cannot open file %s\n", argv[3]);
+		return -EFAULT;;
+	}
+
+	snprintf(config_id, sizeof(config_id), "tspec%d", atoi(argv[4]));
+
+	addtsReq.ieDataLen = fparse_for_cmd_and_hex(fp,
+						    addtsReq.ieData,
+						    (t_u8 *)config_id);
+
+	if (addtsReq.ieDataLen > 0) {
+		printf("Found %d bytes in the %s section of conf file %s\n",
+		       (int)addtsReq.ieDataLen, config_id, filename);
+	} else {
+		fprintf(stderr, "section %s not found in %s\n",
+			config_id, filename);
+		if (fp)
+			fclose(fp);
+		return -EFAULT;
+	}
+
+	addtsReq.timeout_ms = atoi(argv[5]);
+
+	printf("Cmd Input:\n");
+	hexdump(config_id, addtsReq.ieData, addtsReq.ieDataLen, ' ');
+
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.flags = subioctl_val;
+	iwr.u.data.pointer = (caddr_t) & addtsReq;
+	iwr.u.data.length = (sizeof(addtsReq)
+			     - sizeof(addtsReq.ieData)
+			     + addtsReq.ieDataLen);
+
+	if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+		perror("mlanconfig: addts ioctl");
+		if (fp)
+			fclose(fp);
+		return -EFAULT;
+	}
+
+	printf("Cmd Output:\n");
+	printf("ADDTS Command Result = %d\n", addtsReq.commandResult);
+	printf("ADDTS IEEE Status    = %d\n", addtsReq.ieeeStatusCode);
+	hexdump(config_id, addtsReq.ieData, addtsReq.ieDataLen, ' ');
+
+	if (fp)
+		fclose(fp);
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Send a DELTS command to the associated AP
+ *
+ *  Process a given conf file for a specific TSPEC data block.  Send the
+ *    TSPEC along with any other IEs to the driver/firmware for transmission
+ *    in a DELTS request to the associated AP.
+ *
+ *  Return the execution status of the command.  There is no response to a
+ *    DELTS from the AP.
+ *
+ *  mlanconfig mlanX delts <filename.conf> <section# of tspec>
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_delts(int argc, char *argv[])
+{
+	int ioctl_val, subioctl_val;
+	struct iwreq iwr;
+	wlan_ioctl_wmm_delts_req_t deltsReq;
+
+	FILE *fp = NULL;
+	char filename[48];
+	char config_id[20];
+
+	memset(&deltsReq, 0x00, sizeof(deltsReq));
+	memset(filename, 0x00, sizeof(filename));
+
+	if (get_priv_ioctl("delts",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	if (argc != 5) {
+		fprintf(stderr, "Invalid number of parameters!\n");
+		return -EINVAL;
+	}
+
+	strncpy(filename, argv[3], MIN(sizeof(filename) - 1, strlen(argv[3])));
+	if ((fp = fopen(filename, "r")) == NULL) {
+		perror("fopen");
+		fprintf(stderr, "Cannot open file %s\n", argv[3]);
+		return -EFAULT;
+	}
+
+	snprintf(config_id, sizeof(config_id), "tspec%d", atoi(argv[4]));
+
+	deltsReq.ieDataLen = fparse_for_cmd_and_hex(fp,
+						    deltsReq.ieData,
+						    (t_u8 *)config_id);
+
+	if (deltsReq.ieDataLen > 0) {
+		printf("Found %d bytes in the %s section of conf file %s\n",
+		       (int)deltsReq.ieDataLen, config_id, filename);
+	} else {
+		fprintf(stderr, "section %s not found in %s\n",
+			config_id, filename);
+		if (fp)
+			fclose(fp);
+		return -EFAULT;
+	}
+
+	printf("Cmd Input:\n");
+	hexdump(config_id, deltsReq.ieData, deltsReq.ieDataLen, ' ');
+
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.flags = subioctl_val;
+	iwr.u.data.pointer = (caddr_t) & deltsReq;
+	iwr.u.data.length = (sizeof(deltsReq)
+			     - sizeof(deltsReq.ieData)
+			     + deltsReq.ieDataLen);
+
+	if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+		perror("mlanconfig: delts ioctl");
+		if (fp)
+			fclose(fp);
+		return -EFAULT;
+	}
+
+	printf("Cmd Output:\n");
+	printf("DELTS Command Result = %d\n", deltsReq.commandResult);
+	if (fp)
+		fclose(fp);
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Send a WMM AC Queue configuration command to get/set/default params
+ *
+ *  Configure or get the parameters of a WMM AC queue. The command takes
+ *    an optional Queue Id as a last parameter.  Without the queue id, all
+ *    queues will be acted upon.
+ *
+ *  mlanconfig mlanX qconfig set msdu <lifetime in TUs> [Queue Id: 0-3]
+ *  mlanconfig mlanX qconfig get [Queue Id: 0-3]
+ *  mlanconfig mlanX qconfig def [Queue Id: 0-3]
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_qconfig(int argc, char *argv[])
+{
+	int ioctl_val, subioctl_val;
+	struct iwreq iwr;
+	wlan_ioctl_wmm_queue_config_t queue_config_cmd;
+	mlan_wmm_ac_e ac_idx;
+	mlan_wmm_ac_e ac_idx_start;
+	mlan_wmm_ac_e ac_idx_stop;
+
+	const char *ac_str_tbl[] = { "BK", "BE", "VI", "VO" };
+
+	if (argc < 4) {
+		fprintf(stderr, "Invalid number of parameters!\n");
+		return -EINVAL;
+	}
+
+	if (get_priv_ioctl("qconfig",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	memset(&queue_config_cmd, 0x00, sizeof(queue_config_cmd));
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.pointer = (caddr_t) & queue_config_cmd;
+	iwr.u.data.length = sizeof(queue_config_cmd);
+	iwr.u.data.flags = subioctl_val;
+
+	if (strcmp(argv[3], "get") == 0) {
+		/*    3     4    5   */
+		/* qconfig get [qid] */
+		if (argc == 4) {
+			ac_idx_start = WMM_AC_BK;
+			ac_idx_stop = WMM_AC_VO;
+		} else if (argc == 5) {
+			if (atoi(argv[4]) < WMM_AC_BK ||
+			    atoi(argv[4]) > WMM_AC_VO) {
+				fprintf(stderr, "ERROR: Invalid Queue ID!\n");
+				return -EINVAL;
+			}
+			ac_idx_start = atoi(argv[4]);
+			ac_idx_stop = ac_idx_start;
+		} else {
+			fprintf(stderr, "Invalid number of parameters!\n");
+			return -EINVAL;
+		}
+		queue_config_cmd.action = WMM_QUEUE_CONFIG_ACTION_GET;
+
+		for (ac_idx = ac_idx_start; ac_idx <= ac_idx_stop; ac_idx++) {
+			queue_config_cmd.accessCategory = ac_idx;
+			if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+				perror("qconfig ioctl");
+			} else {
+				printf("qconfig %s(%d): MSDU Lifetime GET = 0x%04x (%d)\n", ac_str_tbl[ac_idx], ac_idx, queue_config_cmd.msduLifetimeExpiry, queue_config_cmd.msduLifetimeExpiry);
+			}
+		}
+	} else if (strcmp(argv[3], "set") == 0) {
+		if ((argc >= 5) && strcmp(argv[4], "msdu") == 0) {
+			/*    3     4    5     6      7   */
+			/* qconfig set msdu <value> [qid] */
+			if (argc == 6) {
+				ac_idx_start = WMM_AC_BK;
+				ac_idx_stop = WMM_AC_VO;
+			} else if (argc == 7) {
+				if (atoi(argv[6]) < WMM_AC_BK ||
+				    atoi(argv[6]) > WMM_AC_VO) {
+					fprintf(stderr,
+						"ERROR: Invalid Queue ID!\n");
+					return -EINVAL;
+				}
+				ac_idx_start = atoi(argv[6]);
+				ac_idx_stop = ac_idx_start;
+			} else {
+				fprintf(stderr,
+					"Invalid number of parameters!\n");
+				return -EINVAL;
+			}
+			queue_config_cmd.action = WMM_QUEUE_CONFIG_ACTION_SET;
+			queue_config_cmd.msduLifetimeExpiry = atoi(argv[5]);
+
+			for (ac_idx = ac_idx_start; ac_idx <= ac_idx_stop;
+			     ac_idx++) {
+				queue_config_cmd.accessCategory = ac_idx;
+				if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+					perror("qconfig ioctl");
+				} else {
+					printf("qconfig %s(%d): MSDU Lifetime SET = 0x%04x (%d)\n", ac_str_tbl[ac_idx], ac_idx, queue_config_cmd.msduLifetimeExpiry, queue_config_cmd.msduLifetimeExpiry);
+				}
+			}
+		} else {
+			/* Only MSDU Lifetime provisioning accepted for now */
+			fprintf(stderr, "Invalid set parameter: s/b [msdu]\n");
+			return -EINVAL;
+		}
+	} else if (strncmp(argv[3], "def", strlen("def")) == 0) {
+		/*    3     4    5   */
+		/* qconfig def [qid] */
+		if (argc == 4) {
+			ac_idx_start = WMM_AC_BK;
+			ac_idx_stop = WMM_AC_VO;
+		} else if (argc == 5) {
+			if (atoi(argv[4]) < WMM_AC_BK ||
+			    atoi(argv[4]) > WMM_AC_VO) {
+				fprintf(stderr, "ERROR: Invalid Queue ID!\n");
+				return -EINVAL;
+			}
+			ac_idx_start = atoi(argv[4]);
+			ac_idx_stop = ac_idx_start;
+		} else {
+			fprintf(stderr, "Invalid number of parameters!\n");
+			return -EINVAL;
+		}
+		queue_config_cmd.action = WMM_QUEUE_CONFIG_ACTION_DEFAULT;
+
+		for (ac_idx = ac_idx_start; ac_idx <= ac_idx_stop; ac_idx++) {
+			queue_config_cmd.accessCategory = ac_idx;
+			if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+				perror("qconfig ioctl");
+			} else {
+				printf("qconfig %s(%d): MSDU Lifetime DEFAULT = 0x%04x (%d)\n", ac_str_tbl[ac_idx], ac_idx, queue_config_cmd.msduLifetimeExpiry, queue_config_cmd.msduLifetimeExpiry);
+			}
+		}
+	} else {
+		fprintf(stderr,
+			"Invalid qconfig command; s/b [set, get, default]\n");
+		return -EINVAL;
+	}
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Turn on/off or retrieve and clear the queue statistics for a UP
+ *
+ *  Turn the statistics collection on/off for a given UP or retrieve the
+ *    current accumulated stats and clear them from the firmware.  The command
+ *    takes an optional Queue Id as a last parameter.  Without the queue id,
+ *    all queues will be acted upon.
+ *
+ *  mlanconfig mlanX qstats get [User Priority: 0-7]
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_qstats(int argc, char *argv[])
+{
+	int ioctl_val, subioctl_val;
+	struct iwreq iwr;
+	wlan_ioctl_wmm_queue_stats_t queue_stats_cmd;
+	t_u8 up_idx;
+	t_u8 up_idx_start;
+	t_u8 up_idx_stop;
+	t_u16 usedTime[MAX_USER_PRIORITIES];
+	t_u16 policedTime[MAX_USER_PRIORITIES];
+
+	const char *ac_str_tbl[] = { "BE", "BK", "BK", "BE",
+		"VI", "VI", "VO", "VO"
+	};
+
+	if (argc < 3) {
+		fprintf(stderr, "Invalid number of parameters!\n");
+		return -EINVAL;
+	}
+
+	if (get_priv_ioctl("qstats",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	printf("\n");
+
+	memset(usedTime, 0x00, sizeof(usedTime));
+	memset(policedTime, 0x00, sizeof(policedTime));
+	memset(&queue_stats_cmd, 0x00, sizeof(queue_stats_cmd));
+
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.pointer = (caddr_t) & queue_stats_cmd;
+	iwr.u.data.length = sizeof(queue_stats_cmd);
+	iwr.u.data.flags = subioctl_val;
+
+	if ((argc > 3) && strcmp(argv[3], "on") == 0) {
+		if (argc == 4) {
+			up_idx_start = 0;
+			up_idx_stop = 7;
+		} else if (argc == 5) {
+			if (atoi(argv[4]) < 0 || atoi(argv[4]) > 7) {
+				fprintf(stderr,
+					"ERROR: Invalid User Priority!\n");
+				return -EINVAL;
+			}
+			up_idx_start = atoi(argv[4]);
+			up_idx_stop = up_idx_start;
+		} else {
+			fprintf(stderr, "Invalid number of parameters!\n");
+			return -EINVAL;
+		}
+		queue_stats_cmd.action = WMM_STATS_ACTION_START;
+		for (up_idx = up_idx_start; up_idx <= up_idx_stop; up_idx++) {
+			queue_stats_cmd.userPriority = up_idx;
+			if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+				perror("qstats ioctl");
+			} else {
+				printf("qstats UP%d, %s turned on\n",
+				       up_idx, ac_str_tbl[up_idx]);
+			}
+		}
+	} else if ((argc > 3) && strcmp(argv[3], "off") == 0) {
+		if (argc == 4) {
+			up_idx_start = 0;
+			up_idx_stop = 7;
+		} else if (argc == 5) {
+			if (atoi(argv[4]) < 0 || atoi(argv[4]) > 7) {
+				fprintf(stderr,
+					"ERROR: Invalid User Priority!\n");
+				return -EINVAL;
+			}
+			up_idx_start = atoi(argv[4]);
+			up_idx_stop = up_idx_start;
+		} else {
+			fprintf(stderr, "Invalid number of parameters!\n");
+			return -EINVAL;
+		}
+		queue_stats_cmd.action = WMM_STATS_ACTION_STOP;
+		for (up_idx = up_idx_start; up_idx <= up_idx_stop; up_idx++) {
+			queue_stats_cmd.userPriority = up_idx;
+			if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+				perror("qstats ioctl");
+			} else {
+				printf("qstats UP%d, %s turned off\n",
+				       up_idx, ac_str_tbl[up_idx]);
+			}
+		}
+	} else if ((argc >= 3) &&
+		   ((argc == 3) ? 1 : (strcmp(argv[3], "get") == 0))) {
+		/* If the user types: "mlanconfig mlanX qstats" without get argument.
+		 *   The mlanconfig application invokes "get" option for all UPs
+		 */
+		if ((argc == 4) || (argc == 3)) {
+			up_idx_start = 0;
+			up_idx_stop = 7;
+		} else if (argc == 5) {
+			if (atoi(argv[4]) < 0 || atoi(argv[4]) > 7) {
+				fprintf(stderr,
+					"ERROR: Invalid User Priority!\n");
+				return -EINVAL;
+			}
+			up_idx_start = atoi(argv[4]);
+			up_idx_stop = up_idx_start;
+		} else {
+			fprintf(stderr, "Invalid number of parameters!\n");
+			return -EINVAL;
+		}
+		printf("%s %6s %5s %8s %8s %6s %6s %6s %6s %6s %6s\n",
+		       "AC-UP", "Count", "Loss", "TxDly", "QDly",
+		       "<=5", "<=10", "<=20", "<=30", "<=50", ">50");
+		printf("----------------------------------"
+		       "---------------------------------------------\n");
+		queue_stats_cmd.action = WMM_STATS_ACTION_GET_CLR;
+
+		for (up_idx = up_idx_start; up_idx <= up_idx_stop; up_idx++) {
+			queue_stats_cmd.userPriority = up_idx;
+			if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+				perror("qstats ioctl");
+			} else {
+				printf(" %s-%d %6u %5u %8u %8u %6u %6u %6u %6u %6u %6u\n", ac_str_tbl[up_idx], up_idx, queue_stats_cmd.pktCount, queue_stats_cmd.pktLoss, (unsigned int)queue_stats_cmd.avgTxDelay, (unsigned int)queue_stats_cmd.avgQueueDelay, queue_stats_cmd.delayHistogram[0], queue_stats_cmd.delayHistogram[1], queue_stats_cmd.delayHistogram[2], queue_stats_cmd.delayHistogram[3], (queue_stats_cmd.delayHistogram[4] + queue_stats_cmd.delayHistogram[5]), queue_stats_cmd.delayHistogram[6]);
+
+				usedTime[up_idx] = queue_stats_cmd.usedTime;
+				policedTime[up_idx] =
+					queue_stats_cmd.policedTime;
+			}
+		}
+
+		printf("----------------------------------"
+		       "---------------------------------------------\n");
+		printf("\nAC-UP      UsedTime      PolicedTime\n");
+		printf("------------------------------------\n");
+
+		for (up_idx = up_idx_start; up_idx <= up_idx_stop; up_idx++) {
+			printf(" %s-%d        %6u           %6u\n",
+			       ac_str_tbl[up_idx],
+			       up_idx,
+			       (unsigned int)usedTime[up_idx],
+			       (unsigned int)policedTime[up_idx]);
+		}
+	} else {
+		fprintf(stderr, "Invalid qstats command;\n");
+		return -EINVAL;
+	}
+	printf("\n");
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Get the current status of the WMM Queues
+ *
+ *  Command: mlanconfig mlanX qstatus
+ *
+ *  Retrieve the following information for each AC if wmm is enabled:
+ *        - WMM IE ACM Required
+ *        - Firmware Flow Required
+ *        - Firmware Flow Established
+ *        - Firmware Queue Enabled
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_wmm_qstatus(int argc, char *argv[])
+{
+	int ioctl_val, subioctl_val;
+	struct iwreq iwr;
+	wlan_ioctl_wmm_queue_status_t qstatus;
+	mlan_wmm_ac_e acVal;
+
+	if (get_priv_ioctl("qstatus",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	memset(&qstatus, 0x00, sizeof(qstatus));
+
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.flags = subioctl_val;
+	iwr.u.data.pointer = (caddr_t) & qstatus;
+	iwr.u.data.length = (sizeof(qstatus));
+
+	if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+		perror("mlanconfig: qstatus ioctl");
+		return -EFAULT;
+	}
+
+	for (acVal = WMM_AC_BK; acVal <= WMM_AC_VO; acVal++) {
+		switch (acVal) {
+		case WMM_AC_BK:
+			printf("BK: ");
+			break;
+		case WMM_AC_BE:
+			printf("BE: ");
+			break;
+		case WMM_AC_VI:
+			printf("VI: ");
+			break;
+		case WMM_AC_VO:
+			printf("VO: ");
+			break;
+		default:
+			printf("??: ");
+		}
+
+		printf("ACM[%c], FlowReq[%c], FlowCreated[%c], Enabled[%c],"
+		       " DE[%c], TE[%c]\n",
+		       (qstatus.acStatus[acVal].wmmAcm ? 'X' : ' '),
+		       (qstatus.acStatus[acVal].flowRequired ? 'X' : ' '),
+		       (qstatus.acStatus[acVal].flowCreated ? 'X' : ' '),
+		       (qstatus.acStatus[acVal].disabled ? ' ' : 'X'),
+		       (qstatus.acStatus[acVal].deliveryEnabled ? 'X' : ' '),
+		       (qstatus.acStatus[acVal].triggerEnabled ? 'X' : ' '));
+	}
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Get the current status of the WMM Traffic Streams
+ *
+ *  Command: mlanconfig mlanX ts_status
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_wmm_ts_status(int argc, char *argv[])
+{
+	int ioctl_val, subioctl_val;
+	struct iwreq iwr;
+	wlan_ioctl_wmm_ts_status_t ts_status;
+	int tid;
+
+	const char *ac_str_tbl[] = { "BK", "BE", "VI", "VO" };
+
+	if (get_priv_ioctl("ts_status",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	printf("\nTID   Valid    AC   UP   PSB   FlowDir  MediumTime\n");
+	printf("---------------------------------------------------\n");
+
+	for (tid = 0; tid <= 7; tid++) {
+		memset(&ts_status, 0x00, sizeof(ts_status));
+		ts_status.tid = tid;
+
+		strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+		iwr.u.data.flags = subioctl_val;
+		iwr.u.data.pointer = (caddr_t) & ts_status;
+		iwr.u.data.length = (sizeof(ts_status));
+
+		if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+			perror("mlanconfig: ts_status ioctl");
+			return -EFAULT;
+		}
+
+		printf(" %02d     %3s    %2s    %u     %c    ",
+		       ts_status.tid,
+		       (ts_status.valid ? "Yes" : "No"),
+		       (ts_status.
+			valid ? ac_str_tbl[ts_status.accessCategory] : "--"),
+		       ts_status.userPriority, (ts_status.psb ? 'U' : 'L'));
+
+		if ((ts_status.flowDir & 0x03) == 0) {
+			printf("%s", " ---- ");
+		} else {
+			printf("%2s%4s",
+			       (ts_status.flowDir & 0x01 ? "Up" : ""),
+			       (ts_status.flowDir & 0x02 ? "Down" : ""));
+		}
+
+		printf("%12u\n", ts_status.mediumTime);
+	}
+
+	printf("\n");
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Provides interface to perform read/write operations on regsiter
+ *
+ *  Command: mlanconfig mlanX regrdwr <type> <offset> [value]
+ *
+ *  @param argc     Number of arguments
+ *  @param argv     Pointer to the arguments
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_regrdwr(int argc, char *argv[])
+{
+	struct iwreq iwr;
+	int ioctl_val, subioctl_val;
+	t_u32 type, offset, value;
+	t_u8 buf[100];
+	HostCmd_DS_GEN *hostcmd = (HostCmd_DS_GEN *)buf;
+	int ret = MLAN_STATUS_SUCCESS;
+
+	/* Check arguments */
+	if ((argc < 5) || (argc > 6)) {
+		printf("Parameters for regrdwr: <type> <offset> [value]\n");
+		return -EINVAL;
+	}
+
+	if (get_priv_ioctl("hostcmd",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	type = a2hex_or_atoi(argv[3]);
+	offset = a2hex_or_atoi(argv[4]);
+	if (argc > 5)
+		value = a2hex_or_atoi(argv[5]);
+	if ((ret = prepare_hostcmd_regrdwr(type, offset,
+					   (argc > 5) ? &value : NULL, buf))) {
+		return ret;
+	}
+
+	memset(&iwr, 0, sizeof(iwr));
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.pointer = buf;
+	iwr.u.data.length = le16_to_cpu(hostcmd->size);
+	iwr.u.data.flags = 0;
+
+	if (ioctl(sockfd, ioctl_val, &iwr)) {
+		fprintf(stderr,
+			"mlanconfig: MLANHOSTCMD is not supported by %s\n",
+			dev_name);
+		return MLAN_STATUS_FAILURE;
+	}
+	ret = process_host_cmd_resp(buf);
+
+	return ret;
+}
+
+/**
+ *  @brief Provides interface to perform read/write the adapter memory
+ *
+ *  Command: mlanconfig mlanX memrdwr <address> [value]
+ *
+ *  @param argc     Number of arguments
+ *  @param argv     Pointer to the arguments
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_memrdwr(int argc, char *argv[])
+{
+	struct iwreq iwr;
+	int ioctl_val, subioctl_val;
+	t_u32 address, value;
+	t_u8 buf[100] = { 0 };
+	HostCmd_DS_MEM *pmem;
+	HostCmd_DS_GEN *hostcmd = (HostCmd_DS_GEN *)buf;
+	int ret = MLAN_STATUS_SUCCESS;
+
+	pmem = (HostCmd_DS_MEM *)(buf + S_DS_GEN);
+
+	/* Check arguments */
+	if ((argc < 4) || (argc > 5)) {
+		printf("Parameters for memrdwr: <address> [value]\n");
+		return -EINVAL;
+	}
+
+	if (get_priv_ioctl("hostcmd",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	address = a2hex_or_atoi(argv[3]);
+	pmem->addr = cpu_to_le32(address);
+	if (argc > 4) {
+		pmem->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
+		value = a2hex_or_atoi(argv[4]);
+		pmem->value = cpu_to_le32(value);
+	} else {
+		pmem->action = cpu_to_le16(HostCmd_ACT_GEN_GET);
+		pmem->value = 0;
+	}
+	hostcmd->command = cpu_to_le16(HostCmd_CMD_MEM_ACCESS);
+	hostcmd->size = cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_MEM));
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+
+	memset(&iwr, 0, sizeof(iwr));
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.pointer = buf;
+	iwr.u.data.length = le16_to_cpu(hostcmd->size);
+	iwr.u.data.flags = 0;
+
+	if (ioctl(sockfd, ioctl_val, &iwr)) {
+		fprintf(stderr,
+			"mlanconfig: MLANHOSTCMD is not supported by %s\n",
+			dev_name);
+		return MLAN_STATUS_FAILURE;
+	}
+	ret = process_host_cmd_resp(buf);
+
+	return ret;
+}
+
+/** custom IE, auto mask value */
+#define	CUSTOM_IE_AUTO_MASK	0xffff
+
+/**
+ * @brief Show usage information for the customie
+ * command
+ *
+ * $return         N/A
+ **/
+void
+print_custom_ie_usage(void)
+{
+	printf("\nUsage : customie [INDEX] [MASK] [IEBuffer]");
+	printf("\n         empty - Get all IE settings\n");
+	printf("\n         INDEX:  0 - Get/Set IE index 0 setting");
+	printf("\n                 1 - Get/Set IE index 1 setting");
+	printf("\n                 2 - Get/Set IE index 2 setting");
+	printf("\n                 3 - Get/Set IE index 3 setting");
+	printf("\n                 .                             ");
+	printf("\n                 .                             ");
+	printf("\n                 .                             ");
+	printf("\n                -1 - Append/Delete IE automatically");
+	printf("\n                     Delete will delete the IE from the matching IE buffer");
+	printf("\n                     Append will append the IE to the buffer with the same mask");
+	printf("\n         MASK :  Management subtype mask value as per bit defintions");
+	printf("\n              :  Bit 0 - Association request.");
+	printf("\n              :  Bit 1 - Association response.");
+	printf("\n              :  Bit 2 - Reassociation request.");
+	printf("\n              :  Bit 3 - Reassociation response.");
+	printf("\n              :  Bit 4 - Probe request.");
+	printf("\n              :  Bit 5 - Probe response.");
+	printf("\n              :  Bit 8 - Beacon.");
+	printf("\n         MASK :  MASK = 0 to clear the mask and the IE buffer");
+	printf("\n         IEBuffer :  IE Buffer in hex (max 256 bytes)\n\n");
+	return;
+}
+
+/**
+ * @brief Converts a string to hex value
+ *
+ * @param str      A pointer to the string
+ * @param raw      A pointer to the raw data buffer
+ * @return         Number of bytes read
+ **/
+int
+string2raw(char *str, unsigned char *raw)
+{
+	int len = (strlen(str) + 1) / 2;
+
+	do {
+		if (!isxdigit(*str)) {
+			return -1;
+		}
+		*str = toupper(*str);
+		*raw = CHAR2INT(*str) << 4;
+		++str;
+		*str = toupper(*str);
+		if (*str == '\0')
+			break;
+		*raw |= CHAR2INT(*str);
+		++raw;
+	} while (*++str != '\0');
+	return len;
+}
+
+/**
+ * @brief Creates a hostcmd request for custom IE settings
+ * and sends to the driver
+ *
+ * Usage: "customie [INDEX] [MASK] [IEBuffer]"
+ *
+ * Options: INDEX :      0 - Get/Delete IE index 0 setting
+ *                       1 - Get/Delete IE index 1 setting
+ *                       2 - Get/Delete IE index 2 setting
+ *                       3 - Get/Delete IE index 3 setting
+ *                       .
+ *                       .
+ *                       .
+ *                      -1 - Append IE at the IE buffer with same MASK
+ *          MASK  :      Management subtype mask value
+ *          IEBuffer:    IE Buffer in hex
+ *   					       empty - Get all IE settings
+ *
+ * @param argc     Number of arguments
+ * @param argv     Pointer to the arguments
+ * @return         N/A
+ **/
+static int
+process_custom_ie(int argc, char *argv[])
+{
+	tlvbuf_custom_ie *tlv = NULL;
+	tlvbuf_max_mgmt_ie *max_mgmt_ie_tlv = NULL;
+	custom_ie *ie_ptr = NULL;
+	t_u8 *buffer = NULL;
+	t_u16 buf_len = 0;
+	t_u16 mgmt_subtype_mask = 0;
+	int ie_buf_len = 0, ie_len = 0, i = 0;
+	struct ifreq ifr;
+
+	/* mlanconfig mlan0 customie idx flag buf */
+	if (argc > 6) {
+		printf("ERR:Too many arguments.\n");
+		print_custom_ie_usage();
+		return MLAN_STATUS_FAILURE;
+	}
+	/* Error checks and initialize the command length */
+	if (argc > 3) {
+		if (((IS_HEX_OR_DIGIT(argv[3]) == MLAN_STATUS_FAILURE) &&
+		     (atoi(argv[3]) != -1)) || (atoi(argv[3]) < -1)) {
+			printf("ERR:Illegal index %s\n", argv[3]);
+			print_custom_ie_usage();
+			return MLAN_STATUS_FAILURE;
+		}
+	}
+	switch (argc) {
+	case 3:
+		buf_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+		break;
+	case 4:
+		if (atoi(argv[3]) < 0) {
+			printf("ERR:Illegal index %s. Must be either greater than or equal to 0 for Get Operation \n", argv[3]);
+			print_custom_ie_usage();
+			return MLAN_STATUS_FAILURE;
+		}
+		buf_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+		break;
+	case 5:
+		if (MLAN_STATUS_FAILURE == ishexstring(argv[4]) ||
+		    A2HEXDECIMAL(argv[4]) != 0) {
+			printf("ERR: Mask value should be 0 to clear IEBuffers.\n");
+			print_custom_ie_usage();
+			return MLAN_STATUS_FAILURE;
+		}
+		if (atoi(argv[3]) == -1) {
+			printf("ERR: You must provide buffer for automatic deletion.\n");
+			print_custom_ie_usage();
+			return MLAN_STATUS_FAILURE;
+		}
+		buf_len = sizeof(tlvbuf_custom_ie) + sizeof(custom_ie);
+		break;
+	case 6:
+		/* This is to check negative numbers and special symbols */
+		if (MLAN_STATUS_FAILURE == IS_HEX_OR_DIGIT(argv[4])) {
+			printf("ERR:Mask value must be 0 or hex digits\n");
+			print_custom_ie_usage();
+			return MLAN_STATUS_FAILURE;
+		}
+		/* If above check is passed and mask is not hex, then it must be 0 */
+		if ((ISDIGIT(argv[4]) == MLAN_STATUS_SUCCESS) && atoi(argv[4])) {
+			printf("ERR:Mask value must be 0 or hex digits\n ");
+			print_custom_ie_usage();
+			return MLAN_STATUS_FAILURE;
+		}
+		if (MLAN_STATUS_FAILURE == ishexstring(argv[5])) {
+			printf("ERR:Only hex digits are allowed\n");
+			print_custom_ie_usage();
+			return MLAN_STATUS_FAILURE;
+		}
+		ie_buf_len = strlen(argv[5]);
+		if (!strncasecmp("0x", argv[5], 2)) {
+			ie_len = (ie_buf_len - 2 + 1) / 2;
+			argv[5] += 2;
+		} else
+			ie_len = (ie_buf_len + 1) / 2;
+		if (ie_len > MAX_IE_BUFFER_LEN) {
+			printf("ERR:Incorrect IE length %d\n", ie_buf_len);
+			print_custom_ie_usage();
+			return MLAN_STATUS_FAILURE;
+		}
+		mgmt_subtype_mask = (t_u16)A2HEXDECIMAL(argv[4]);
+		buf_len = sizeof(tlvbuf_custom_ie) + sizeof(custom_ie) + ie_len;
+		break;
+	}
+	/* Initialize the command buffer */
+	buffer = (t_u8 *)malloc(buf_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	memset(buffer, 0, buf_len);
+	tlv = (tlvbuf_custom_ie *)buffer;
+	tlv->tag = MRVL_MGMT_IE_LIST_TLV_ID;
+	if (argc == 3 || argc == 4) {
+		if (argc == 3)
+			tlv->length = 0;
+		else {
+			tlv->length = sizeof(t_u16);
+			ie_ptr = (custom_ie *)(tlv->ie_data);
+			ie_ptr->ie_index = (t_u16)(atoi(argv[3]));
+		}
+	} else {
+		/* Locate headers */
+		ie_ptr = (custom_ie *)(tlv->ie_data);
+		/* Set TLV fields */
+		tlv->length = sizeof(custom_ie) + ie_len;
+		ie_ptr->ie_index = atoi(argv[3]);
+		ie_ptr->mgmt_subtype_mask = mgmt_subtype_mask;
+		ie_ptr->ie_length = ie_len;
+		if (argc == 6)
+			string2raw(argv[5], ie_ptr->ie_buffer);
+	}
+	/* Initialize the ifr structure */
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)buffer;
+	/* Perform ioctl */
+	if (ioctl(sockfd, CUSTOM_IE_CFG, &ifr)) {
+		perror("ioctl[CUSTOM_IE_CFG]");
+		printf("ERR:Command sending failed!\n");
+		if (buffer)
+			free(buffer);
+		return MLAN_STATUS_FAILURE;
+	}
+	/* Print response */
+	if (argc > 4) {
+		printf("custom IE setting successful\n");
+	} else {
+		printf("Querying custom IE successful\n");
+		tlv = (tlvbuf_custom_ie *)buffer;
+		ie_len = tlv->length;
+		ie_ptr = (custom_ie *)(tlv->ie_data);
+		while ((unsigned int)ie_len >= sizeof(custom_ie)) {
+			printf("Index [%d]\n", ie_ptr->ie_index);
+			if (ie_ptr->ie_length)
+				printf("Management Subtype Mask = 0x%02x\n",
+				       (ie_ptr->mgmt_subtype_mask) == 0 ?
+				       CUSTOM_IE_AUTO_MASK :
+				       (ie_ptr->mgmt_subtype_mask));
+			else
+				printf("Management Subtype Mask = 0x%02x\n",
+				       (ie_ptr->mgmt_subtype_mask));
+			hexdump("IE Buffer", (void *)ie_ptr->ie_buffer,
+				ie_ptr->ie_length, ' ');
+			ie_len -= sizeof(custom_ie) + ie_ptr->ie_length;
+			ie_ptr = (custom_ie *)((t_u8 *)ie_ptr +
+					       sizeof(custom_ie) +
+					       ie_ptr->ie_length);
+		}
+	}
+	max_mgmt_ie_tlv =
+		(tlvbuf_max_mgmt_ie *)(buffer + sizeof(tlvbuf_custom_ie) +
+				       tlv->length);
+	if (max_mgmt_ie_tlv) {
+		if (max_mgmt_ie_tlv->tag == MRVL_MAX_MGMT_IE_TLV_ID) {
+			for (i = 0; i < max_mgmt_ie_tlv->count; i++) {
+				printf("buf%d_size = %d\n", i,
+				       max_mgmt_ie_tlv->info[i].buf_size);
+				printf("number of buffers = %d\n",
+				       max_mgmt_ie_tlv->info[i].buf_count);
+				printf("\n");
+			}
+		}
+	}
+	if (buffer)
+		free(buffer);
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/********************************************************
+		Global Functions
+********************************************************/
+/**
+ *  @brief Get one line from the File
+ *
+ *  @param fp       File handler
+ *  @param str	    Storage location for data.
+ *  @param size 	Maximum number of characters to read.
+ *  @param lineno	A pointer to return current line number
+ *  @return         returns string or NULL
+ */
+char *
+mlan_config_get_line(FILE * fp, t_s8 *str, t_s32 size, int *lineno)
+{
+	char *start, *end;
+	int out, next_line;
+
+	if (!fp || !str)
+		return NULL;
+
+	do {
+read_line:
+		if (!fgets(str, size, fp))
+			break;
+		start = str;
+		start[size - 1] = '\0';
+		end = start + strlen(str);
+		(*lineno)++;
+
+		out = 1;
+		while (out && (start < end)) {
+			next_line = 0;
+			/* Remove empty lines and lines starting with # */
+			switch (start[0]) {
+			case ' ':	/* White space */
+			case '\t':	/* Tab */
+				start++;
+				break;
+			case '#':
+			case '\n':
+			case '\0':
+				next_line = 1;
+				break;
+			case '\r':
+				if (start[1] == '\n')
+					next_line = 1;
+				else
+					start++;
+				break;
+			default:
+				out = 0;
+				break;
+			}
+			if (next_line)
+				goto read_line;
+		}
+
+		/* Remove # comments unless they are within a double quoted
+		 * string. Remove trailing white space. */
+		if ((end = strstr(start, "\""))) {
+			if (!(end = strstr(end + 1, "\"")))
+				end = start;
+		} else
+			end = start;
+
+		if ((end = strstr(end + 1, "#")))
+			*end-- = '\0';
+		else
+			end = start + strlen(start) - 1;
+
+		out = 1;
+		while (out && (start < end)) {
+			switch (*end) {
+			case ' ':	/* White space */
+			case '\t':	/* Tab */
+			case '\n':
+			case '\r':
+				*end = '\0';
+				end--;
+				break;
+			default:
+				out = 0;
+				break;
+			}
+		}
+
+		if (start == '\0')
+			continue;
+
+		return start;
+	} while (1);
+
+	return NULL;
+}
+
+/**
+ *  @brief parse hex data
+ *  @param fp       File handler
+ *  @param dst      A pointer to receive hex data
+ *  @return         length of hex data
+ */
+int
+fparse_for_hex(FILE * fp, t_u8 *dst)
+{
+	t_s8 *ptr;
+	t_u8 *dptr;
+	t_s8 buf[256];
+
+	dptr = dst;
+	while (fgets(buf, sizeof(buf), fp)) {
+		ptr = buf;
+
+		while (*ptr) {
+			/* skip leading spaces */
+			while (*ptr && (isspace(*ptr) || *ptr == '\t'))
+				ptr++;
+
+			/* skip blank lines and lines beginning with '#' */
+			if (*ptr == '\0' || *ptr == '#')
+				break;
+
+			if (isxdigit(*ptr)) {
+				ptr = convert2hex(ptr, dptr++);
+			} else {
+				/* Invalid character on data line */
+				ptr++;
+			}
+		}
+	}
+
+	return (dptr - dst);
+}
+
+#define STACK_NBYTES            	100	/**< Number of bytes in stack */
+#define MAX_BYTESEQ 		       	6	/**< Maximum byte sequence */
+#define TYPE_DNUM           		1 /**< decimal number */
+#define TYPE_BYTESEQ        		2 /**< byte sequence */
+#define MAX_OPERAND         		0x40	/**< Maximum operands */
+#define TYPE_EQ         		(MAX_OPERAND+1)	/**< byte comparison:    == operator */
+#define TYPE_EQ_DNUM    		(MAX_OPERAND+2)	/**< decimal comparison: =d operator */
+#define TYPE_EQ_BIT     		(MAX_OPERAND+3)	/**< bit comparison:     =b operator */
+#define TYPE_AND        		(MAX_OPERAND+4)	/**< && operator */
+#define TYPE_OR         		(MAX_OPERAND+5)	/**< || operator */
+
+typedef struct {
+	t_u16 sp;		      /**< Stack pointer */
+	t_u8 byte[STACK_NBYTES];      /**< Stack */
+} mstack_t;
+
+typedef struct {
+	t_u8 type;		  /**< Type */
+	t_u8 reserve[3];   /**< so 4-byte align val array */
+	/* byte sequence is the largest among all the operands and operators. */
+	/* byte sequence format: 1 byte of num of bytes, then variable num bytes */
+	t_u8 val[MAX_BYTESEQ + 1];/**< Value */
+} op_t;
+
+/**
+ *  @brief push data to stack
+ *
+ *  @param s			a pointer to mstack_t structure
+ *
+ *  @param nbytes		number of byte to push to stack
+ *
+ *  @param val			a pointer to data buffer
+ *
+ *  @return			TRUE-- sucess , FALSE -- fail
+ *
+ */
+static int
+push_n(mstack_t * s, t_u8 nbytes, t_u8 *val)
+{
+	if ((s->sp + nbytes) < STACK_NBYTES) {
+		memcpy((void *)(s->byte + s->sp), (const void *)val,
+		       (size_t) nbytes);
+		s->sp += nbytes;
+		/* printf("push: n %d sp %d\n", nbytes, s->sp); */
+		return TRUE;
+	} else			/* stack full */
+		return FALSE;
+}
+
+/**
+ *  @brief push data to stack
+ *
+ *  @param s			a pointer to mstack_t structure
+ *
+ *  @param op			a pointer to op_t structure
+ *
+ *  @return			TRUE-- sucess , FALSE -- fail
+ *
+ */
+static int
+push(mstack_t * s, op_t * op)
+{
+	t_u8 nbytes;
+	switch (op->type) {
+	case TYPE_DNUM:
+		if (push_n(s, 4, op->val))
+			return (push_n(s, 1, &op->type));
+		return FALSE;
+	case TYPE_BYTESEQ:
+		nbytes = op->val[0];
+		if (push_n(s, nbytes, op->val + 1) &&
+		    push_n(s, 1, op->val) && push_n(s, 1, &op->type))
+			return TRUE;
+		return FALSE;
+	default:
+		return (push_n(s, 1, &op->type));
+	}
+}
+
+/**
+ *  @brief parse RPN string
+ *
+ *  @param s			a pointer to Null-terminated string to scan.
+ *
+ *  @param first_time		a pointer to return first_time
+ *
+ *  @return			A pointer to the last token found in string.
+ *  				NULL is returned when there are no more tokens to be found.
+ *
+ */
+static char *
+getop(char *s, int *first_time)
+{
+	const char delim[] = " \t\n";
+	char *p;
+	if (*first_time) {
+		p = strtok(s, delim);
+		*first_time = FALSE;
+	} else {
+		p = strtok(NULL, delim);
+	}
+	return (p);
+}
+
+/**
+ *  @brief Verify hex digit.
+ *
+ *  @param c			input ascii char
+ *  @param h			a pointer to return integer value of the digit char.
+ *  @return			TURE -- c is hex digit, FALSE -- c is not hex digit.
+ */
+static int
+ishexdigit(char c, t_u8 *h)
+{
+	if (c >= '0' && c <= '9') {
+		*h = c - '0';
+		return (TRUE);
+	} else if (c >= 'a' && c <= 'f') {
+		*h = c - 'a' + 10;
+		return (TRUE);
+	} else if (c >= 'A' && c <= 'F') {
+		*h = c - 'A' + 10;
+		return (TRUE);
+	}
+	return (FALSE);
+}
+
+/**
+ *  @brief convert hex string to integer.
+ *
+ *  @param s			A pointer to hex string, string length up to 2 digits.
+ *  @return			integer value.
+ */
+static t_u8
+hex_atoi(char *s)
+{
+	int i;
+	t_u8 digit;		/* digital value */
+	t_u8 t = 0;		/* total value */
+
+	for (i = 0, t = 0; ishexdigit(s[i], &digit) && i < 2; i++)
+		t = 16 * t + digit;
+	return (t);
+}
+
+/**
+ *  @brief Parse byte sequence in hex format string to a byte sequence.
+ *
+ *  @param opstr		A pointer to byte sequence in hex format string, with ':' as delimiter between two byte.
+ *  @param val			A pointer to return byte sequence string
+ *  @return			NA
+ */
+static void
+parse_hex(char *opstr, t_u8 *val)
+{
+	char delim = ':';
+	char *p;
+	char *q;
+	t_u8 i;
+
+	/* +1 is for skipping over the preceding h character. */
+	p = opstr + 1;
+
+	/* First byte */
+	val[1] = hex_atoi(p++);
+
+	/* Parse subsequent bytes. */
+	/* Each byte is preceded by the : character. */
+	for (i = 1; *p; i++) {
+		q = strchr(p, delim);
+		if (!q)
+			break;
+		p = q + 1;
+		val[i + 1] = hex_atoi(p);
+	}
+	/* Set num of bytes */
+	val[0] = i;
+}
+
+/**
+ *  @brief str2bin, convert RPN string to binary format
+ *
+ *  @param str			A pointer to rpn string
+ *  @param stack		A pointer to mstack_t structure
+ *  @return			MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+str2bin(char *str, mstack_t * stack)
+{
+	int first_time;
+	char *opstr;
+	op_t op;		/* operator/operand */
+	int dnum;
+	int ret = MLAN_STATUS_SUCCESS;
+
+	memset(stack, 0, sizeof(mstack_t));
+	first_time = TRUE;
+	while ((opstr = getop(str, &first_time)) != NULL) {
+		if (isdigit(*opstr)) {
+			op.type = TYPE_DNUM;
+			dnum = cpu_to_le32(atoi(opstr));
+			memcpy((t_u8 *)op.val, &dnum, sizeof(dnum));
+			if (!push(stack, &op)) {
+				printf("push decimal number failed\n");
+				ret = MLAN_STATUS_FAILURE;
+				break;
+			}
+		} else if (*opstr == 'h') {
+			op.type = TYPE_BYTESEQ;
+			parse_hex(opstr, op.val);
+			if (!push(stack, &op)) {
+				printf("push byte sequence failed\n");
+				ret = MLAN_STATUS_FAILURE;
+				break;
+			}
+		} else if (!strcmp(opstr, "==")) {
+			op.type = TYPE_EQ;
+			if (!push(stack, &op)) {
+				printf("push byte cmp operator failed\n");
+				ret = MLAN_STATUS_FAILURE;
+				break;
+			}
+		} else if (!strcmp(opstr, "=d")) {
+			op.type = TYPE_EQ_DNUM;
+			if (!push(stack, &op)) {
+				printf("push decimal cmp operator failed\n");
+				ret = MLAN_STATUS_FAILURE;
+				break;
+			}
+		} else if (!strcmp(opstr, "=b")) {
+			op.type = TYPE_EQ_BIT;
+			if (!push(stack, &op)) {
+				printf("push bit cmp operator failed\n");
+				ret = MLAN_STATUS_FAILURE;
+				break;
+			}
+		} else if (!strcmp(opstr, "&&")) {
+			op.type = TYPE_AND;
+			if (!push(stack, &op)) {
+				printf("push AND operator failed\n");
+				ret = MLAN_STATUS_FAILURE;
+				break;
+			}
+		} else if (!strcmp(opstr, "||")) {
+			op.type = TYPE_OR;
+			if (!push(stack, &op)) {
+				printf("push OR operator failed\n");
+				ret = MLAN_STATUS_FAILURE;
+				break;
+			}
+		} else {
+			printf("Unknown operand\n");
+			ret = MLAN_STATUS_FAILURE;
+			break;
+		}
+	}
+	return ret;
+}
+
+/**
+ *  @brief Converts colon separated MAC address to hex value
+ *
+ *  @param mac      A pointer to the colon separated MAC string
+ *  @param raw      A pointer to the hex data buffer
+ *  @return         MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ *                  MAC_BROADCAST   - if breadcast mac
+ *                  MAC_MULTICAST   - if multicast mac
+ */
+static int
+mac2raw(char *mac, t_u8 *raw)
+{
+	unsigned int temp_raw[ETH_ALEN];
+	int num_tokens = 0;
+	int i;
+
+	if (strlen(mac) != ((2 * ETH_ALEN) + (ETH_ALEN - 1))) {
+		return MLAN_STATUS_FAILURE;
+	}
+	num_tokens = sscanf(mac, "%2x:%2x:%2x:%2x:%2x:%2x",
+			    temp_raw + 0, temp_raw + 1, temp_raw + 2,
+			    temp_raw + 3, temp_raw + 4, temp_raw + 5);
+	if (num_tokens != ETH_ALEN) {
+		return MLAN_STATUS_FAILURE;
+	}
+	for (i = 0; i < num_tokens; i++)
+		raw[i] = (t_u8)temp_raw[i];
+
+	if (memcmp(raw, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) {
+		return MAC_BROADCAST;
+	} else if (raw[0] & 0x01) {
+		return MAC_MULTICAST;
+	}
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief          Parse function for a configuration line
+ *
+ *  @param s        Storage buffer for data
+ *  @param size     Maximum size of data
+ *  @param stream   File stream pointer
+ *  @param line     Pointer to current line within the file
+ *  @param _pos     Output string or NULL
+ *  @return         String or NULL
+ */
+static char *
+config_get_line(char *s, int size, FILE * stream, int *line, char **_pos)
+{
+	*_pos = mlan_config_get_line(stream, s, size, line);
+	return *_pos;
+}
+
+/**
+ *  @brief Parses a command line
+ *
+ *  @param line     The line to parse
+ *  @param args     Pointer to the argument buffer to be filled in
+ *  @return         Number of arguments in the line or EOF
+ */
+static int
+parse_line(char *line, char *args[])
+{
+	int arg_num = 0;
+	int is_start = 0;
+	int is_quote = 0;
+	int length = 0;
+	int i = 0;
+
+	arg_num = 0;
+	length = strlen(line);
+	/* Process line */
+
+	/* Find number of arguments */
+	is_start = 0;
+	is_quote = 0;
+	for (i = 0; i < length; i++) {
+		/* Ignore leading spaces */
+		if (is_start == 0) {
+			if (line[i] == ' ') {
+				continue;
+			} else if (line[i] == '\t') {
+				continue;
+			} else if (line[i] == '\n') {
+				break;
+			} else {
+				is_start = 1;
+				args[arg_num] = &line[i];
+				arg_num++;
+			}
+		}
+		if (is_start == 1) {
+			/* Ignore comments */
+			if (line[i] == '#') {
+				if (is_quote == 0) {
+					line[i] = '\0';
+					arg_num--;
+				}
+				break;
+			}
+			/* Separate by '=' */
+			if (line[i] == '=') {
+				line[i] = '\0';
+				is_start = 0;
+				continue;
+			}
+			/* Separate by ',' */
+			if (line[i] == ',') {
+				line[i] = '\0';
+				is_start = 0;
+				continue;
+			}
+			/* Change ',' to ' ', but not inside quotes */
+			if ((line[i] == ',') && (is_quote == 0)) {
+				line[i] = ' ';
+				continue;
+			}
+		}
+		/* Remove newlines */
+		if (line[i] == '\n') {
+			line[i] = '\0';
+		}
+		/* Check for quotes */
+		if (line[i] == '"') {
+			is_quote = (is_quote == 1) ? 0 : 1;
+			continue;
+		}
+		if (((line[i] == ' ') || (line[i] == '\t')) && (is_quote == 0)) {
+			line[i] = '\0';
+			is_start = 0;
+			continue;
+		}
+	}
+	return arg_num;
+}
+
+#define FILTER_BYTESEQ 		TYPE_EQ	/**< byte sequence */
+#define FILTER_DNUM    		TYPE_EQ_DNUM /**< decimal number */
+#define FILTER_BITSEQ		TYPE_EQ_BIT /**< bit sequence */
+#define FILTER_TEST		FILTER_BITSEQ+1	/**< test */
+
+#define NAME_TYPE		1	    /**< Field name 'type' */
+#define NAME_PATTERN		2	/**< Field name 'pattern' */
+#define NAME_OFFSET		3	    /**< Field name 'offset' */
+#define NAME_NUMBYTE		4	/**< Field name 'numbyte' */
+#define NAME_REPEAT		5	    /**< Field name 'repeat' */
+#define NAME_BYTE		6	    /**< Field name 'byte' */
+#define NAME_MASK		7	    /**< Field name 'mask' */
+#define NAME_DEST		8	    /**< Field name 'dest' */
+
+static struct mef_fields {
+	t_s8 *name;
+	      /**< Name */
+	t_s8 nameid;
+		/**< Name Id. */
+} mef_fields[] = {
+	{
+	"type", NAME_TYPE}, {
+	"pattern", NAME_PATTERN}, {
+	"offset", NAME_OFFSET}, {
+	"numbyte", NAME_NUMBYTE}, {
+	"repeat", NAME_REPEAT}, {
+	"byte", NAME_BYTE}, {
+	"mask", NAME_MASK}, {
+	"dest", NAME_DEST}
+};
+
+/**
+ *  @brief get filter data
+ *
+ *  @param fp			A pointer to file stream
+ *  @param ln			A pointer to line number
+ *  @param buf			A pointer to hostcmd data
+ *  @param size			A pointer to the return size of hostcmd buffer
+ *  @return			MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+mlan_get_filter_data(FILE * fp, int *ln, t_u8 *buf, t_u16 *size)
+{
+	t_s32 errors = 0, i;
+	t_s8 line[256], *pos, *pos1;
+	t_u16 type = 0;
+	t_u32 pattern = 0;
+	t_u16 repeat = 0;
+	t_u16 offset = 0;
+	t_s8 byte_seq[50];
+	t_s8 mask_seq[50];
+	t_u16 numbyte = 0;
+	t_s8 type_find = 0;
+	t_s8 pattern_find = 0;
+	t_s8 offset_find = 0;
+	t_s8 numbyte_find = 0;
+	t_s8 repeat_find = 0;
+	t_s8 byte_find = 0;
+	t_s8 mask_find = 0;
+	t_s8 dest_find = 0;
+	t_s8 dest_seq[50];
+
+	*size = 0;
+	while ((pos = mlan_config_get_line(fp, line, sizeof(line), ln))) {
+		if (strcmp(pos, "}") == 0) {
+			break;
+		}
+		pos1 = strchr(pos, '=');
+		if (pos1 == NULL) {
+			printf("Line %d: Invalid mef_filter line '%s'\n", *ln,
+			       pos);
+			errors++;
+			continue;
+		}
+		*pos1++ = '\0';
+		for (i = 0; (t_u32)i < NELEMENTS(mef_fields); i++) {
+			if (strncmp
+			    (pos, mef_fields[i].name,
+			     strlen(mef_fields[i].name)) == 0) {
+				switch (mef_fields[i].nameid) {
+				case NAME_TYPE:
+					type = a2hex_or_atoi(pos1);
+					if ((type != FILTER_DNUM) &&
+					    (type != FILTER_BYTESEQ)
+					    && (type != FILTER_BITSEQ) &&
+					    (type != FILTER_TEST)) {
+						printf("Invalid filter type:%d\n", type);
+						return MLAN_STATUS_FAILURE;
+					}
+					type_find = 1;
+					break;
+				case NAME_PATTERN:
+					pattern = a2hex_or_atoi(pos1);
+					pattern_find = 1;
+					break;
+				case NAME_OFFSET:
+					offset = a2hex_or_atoi(pos1);
+					offset_find = 1;
+					break;
+				case NAME_NUMBYTE:
+					numbyte = a2hex_or_atoi(pos1);
+					numbyte_find = 1;
+					break;
+				case NAME_REPEAT:
+					repeat = a2hex_or_atoi(pos1);
+					repeat_find = 1;
+					break;
+				case NAME_BYTE:
+					memset(byte_seq, 0, sizeof(byte_seq));
+					strncpy(byte_seq, pos1,
+						(sizeof(byte_seq) - 1));
+					byte_find = 1;
+					break;
+				case NAME_MASK:
+					memset(mask_seq, 0, sizeof(mask_seq));
+					strncpy(mask_seq, pos1,
+						(sizeof(mask_seq) - 1));
+					mask_find = 1;
+					break;
+				case NAME_DEST:
+					memset(dest_seq, 0, sizeof(dest_seq));
+					strncpy(dest_seq, pos1,
+						(sizeof(dest_seq) - 1));
+					dest_find = 1;
+					break;
+				}
+				break;
+			}
+		}
+		if (i == NELEMENTS(mef_fields)) {
+			printf("Line %d: unknown mef field '%s'.\n",
+			       *line, pos);
+			errors++;
+		}
+	}
+	if (type_find == 0) {
+		printf("Can not find filter type\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	switch (type) {
+	case FILTER_DNUM:
+		if (!pattern_find || !offset_find || !numbyte_find) {
+			printf("Missing field for FILTER_DNUM: pattern=%d,offset=%d,numbyte=%d\n", pattern_find, offset_find, numbyte_find);
+			return MLAN_STATUS_FAILURE;
+		}
+		memset(line, 0, sizeof(line));
+		snprintf(line, sizeof(line), "%d %d %d =d ", pattern, offset,
+			 numbyte);
+		break;
+	case FILTER_BYTESEQ:
+		if (!byte_find || !offset_find || !repeat_find) {
+			printf("Missing field for FILTER_BYTESEQ: byte=%d,offset=%d,repeat=%d\n", byte_find, offset_find, repeat_find);
+			return MLAN_STATUS_FAILURE;
+		}
+		memset(line, 0, sizeof(line));
+		snprintf(line, sizeof(line), "%d h%s %d == ", repeat, byte_seq,
+			 offset);
+		break;
+	case FILTER_BITSEQ:
+		if (!byte_find || !offset_find || !mask_find) {
+			printf("Missing field for FILTER_BITSEQ: byte=%d,offset=%d,mask_find=%d\n", byte_find, offset_find, mask_find);
+			return MLAN_STATUS_FAILURE;
+		}
+		if (strlen(byte_seq) != strlen(mask_seq)) {
+			printf("byte string's length is different with mask's length!\n");
+			return MLAN_STATUS_FAILURE;
+		}
+		memset(line, 0, sizeof(line));
+		snprintf(line, sizeof(line), "h%s %d h%s =b ", byte_seq, offset,
+			 mask_seq);
+		break;
+	case FILTER_TEST:
+		if (!byte_find || !offset_find || !repeat_find || !dest_find) {
+			printf("Missing field for FILTER_TEST: byte=%d,offset=%d,repeat=%d,dest=%d\n", byte_find, offset_find, repeat_find, dest_find);
+			return MLAN_STATUS_FAILURE;
+		}
+		memset(line, 0, sizeof(line));
+		snprintf(line, sizeof(line), "h%s %d h%s %d ", dest_seq, repeat,
+			 byte_seq, offset);
+		break;
+	}
+	memcpy(buf, line, strlen(line));
+	*size = strlen(line);
+	return MLAN_STATUS_SUCCESS;
+}
+
+#define NAME_MODE	1	/**< Field name 'mode' */
+#define NAME_ACTION	2	/**< Field name 'action' */
+#define NAME_FILTER_NUM	3   /**< Field name 'filter_num' */
+#define NAME_RPN	4	/**< Field name 'RPN' */
+static struct mef_entry_fields {
+	t_s8 *name;
+	      /**< Name */
+	t_s8 nameid;
+		/**< Name id */
+} mef_entry_fields[] = {
+	{
+	"mode", NAME_MODE}, {
+	"action", NAME_ACTION}, {
+	"filter_num", NAME_FILTER_NUM}, {
+"RPN", NAME_RPN},};
+
+typedef struct _MEF_ENTRY {
+    /** Mode */
+	t_u8 Mode;
+    /** Size */
+	t_u8 Action;
+    /** Size of expression */
+	t_u16 ExprSize;
+} MEF_ENTRY;
+
+/**
+ *  @brief get mef_entry data
+ *
+ *  @param fp			A pointer to file stream
+ *  @param ln			A pointer to line number
+ *  @param buf			A pointer to hostcmd data
+ *  @param size			A pointer to the return size of hostcmd buffer
+ *  @return			MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+mlan_get_mef_entry_data(FILE * fp, int *ln, t_u8 *buf, t_u16 *size)
+{
+	t_s8 line[256], *pos, *pos1;
+	t_u8 mode, action, filter_num = 0;
+	t_s8 rpn[256];
+	t_s8 mode_find = 0;
+	t_s8 action_find = 0;
+	t_s8 filter_num_find = 0;
+	t_s8 rpn_find = 0;
+	t_s8 rpn_str[256];
+	int rpn_len = 0;
+	t_s8 filter_name[50];
+	t_s8 name_found = 0;
+	t_u16 len = 0;
+	int i;
+	int first_time = TRUE;
+	char *opstr;
+	t_s8 filter_action[10];
+	t_s32 errors = 0;
+	MEF_ENTRY *pMefEntry = (MEF_ENTRY *) buf;
+	mstack_t stack;
+	while ((pos = mlan_config_get_line(fp, line, sizeof(line), ln))) {
+		if (strcmp(pos, "}") == 0) {
+			break;
+		}
+		pos1 = strchr(pos, '=');
+		if (pos1 == NULL) {
+			printf("Line %d: Invalid mef_entry line '%s'\n", *ln,
+			       pos);
+			errors++;
+			continue;
+		}
+		*pos1++ = '\0';
+		if (!mode_find || !action_find || !filter_num_find || !rpn_find) {
+			for (i = 0;
+			     (unsigned int)i < NELEMENTS(mef_entry_fields);
+			     i++) {
+				if (strncmp
+				    (pos, mef_entry_fields[i].name,
+				     strlen(mef_entry_fields[i].name)) == 0) {
+					switch (mef_entry_fields[i].nameid) {
+					case NAME_MODE:
+						mode = a2hex_or_atoi(pos1);
+						if (mode & ~0x7) {
+							printf("invalid mode=%d\n", mode);
+							return MLAN_STATUS_FAILURE;
+						}
+						pMefEntry->Mode = mode;
+						mode_find = 1;
+						break;
+					case NAME_ACTION:
+						action = a2hex_or_atoi(pos1);
+						if (action & ~0xff) {
+							printf("invalid action=%d\n", action);
+							return MLAN_STATUS_FAILURE;
+						}
+						pMefEntry->Action = action;
+						action_find = 1;
+						break;
+					case NAME_FILTER_NUM:
+						filter_num =
+							a2hex_or_atoi(pos1);
+						filter_num_find = 1;
+						break;
+					case NAME_RPN:
+						memset(rpn, 0, sizeof(rpn));
+						strncpy(rpn, pos1,
+							(sizeof(rpn) - 1));
+						rpn_find = 1;
+						break;
+					}
+					break;
+				}
+			}
+			if (i == NELEMENTS(mef_fields)) {
+				printf("Line %d: unknown mef_entry field '%s'.\n", *line, pos);
+				return MLAN_STATUS_FAILURE;
+			}
+		}
+		if (mode_find && action_find && filter_num_find && rpn_find) {
+			for (i = 0; i < filter_num; i++) {
+				opstr = getop(rpn, &first_time);
+				if (opstr == NULL)
+					break;
+				snprintf(filter_name, sizeof(filter_name),
+					 "%s={", opstr);
+				name_found = 0;
+				while ((pos =
+					mlan_config_get_line(fp, line,
+							     sizeof(line),
+							     ln))) {
+					if (strncmp
+					    (pos, filter_name,
+					     strlen(filter_name)) == 0) {
+						name_found = 1;
+						break;
+					}
+				}
+				if (!name_found) {
+					fprintf(stderr,
+						"mlanconfig: %s not found in file\n",
+						filter_name);
+					return MLAN_STATUS_FAILURE;
+				}
+				if (MLAN_STATUS_FAILURE ==
+				    mlan_get_filter_data(fp, ln,
+							 (t_u8 *)(rpn_str +
+								  rpn_len),
+							 &len))
+					break;
+				rpn_len += len;
+				if (i > 0) {
+					memcpy(rpn_str + rpn_len, filter_action,
+					       strlen(filter_action));
+					rpn_len += strlen(filter_action);
+				}
+				opstr = getop(rpn, &first_time);
+				if (opstr == NULL)
+					break;
+				memset(filter_action, 0, sizeof(filter_action));
+				snprintf(filter_action, sizeof(filter_action),
+					 "%s ", opstr);
+			}
+			/* Remove the last space */
+			if (rpn_len > 0) {
+				rpn_len--;
+				rpn_str[rpn_len] = 0;
+			}
+			if (MLAN_STATUS_FAILURE == str2bin(rpn_str, &stack)) {
+				printf("Fail on str2bin!\n");
+				return MLAN_STATUS_FAILURE;
+			}
+			*size = sizeof(MEF_ENTRY);
+			pMefEntry->ExprSize = cpu_to_le16(stack.sp);
+			memmove(buf + sizeof(MEF_ENTRY), stack.byte, stack.sp);
+			*size += stack.sp;
+			break;
+		} else if (mode_find && action_find && filter_num_find &&
+			   (filter_num == 0)) {
+			pMefEntry->ExprSize = 0;
+			*size = sizeof(MEF_ENTRY);
+			break;
+		}
+	}
+	return MLAN_STATUS_SUCCESS;
+}
+
+#define MEFCFG_CMDCODE	0x009a
+/**
+ *  @brief Process mef cfg
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_mef_cfg(int argc, char *argv[])
+{
+	int ioctl_val, subioctl_val;
+	t_s8 line[256], cmdname[256], *pos;
+	int cmdname_found = 0, name_found = 0;
+	int ln = 0;
+	int ret = MLAN_STATUS_SUCCESS;
+	int i;
+	t_u8 *buf;
+	t_u16 buf_len = 0;
+	t_u16 len;
+	struct iwreq iwr;
+	HostCmd_DS_MEF_CFG *mefcmd;
+	HostCmd_DS_GEN *hostcmd;
+	FILE *fp = NULL;
+
+	if (argc < 4) {
+		printf("Error: invalid no of arguments\n");
+		printf("Syntax: ./mlanconfig eth1 mefcfg <mef.conf>\n");
+		exit(1);
+	}
+	if (get_priv_ioctl("hostcmd",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+	snprintf(cmdname, sizeof(cmdname), "%s={", argv[2]);
+	cmdname_found = 0;
+	if ((fp = fopen(argv[3], "r")) == NULL) {
+		fprintf(stderr, "Cannot open file %s\n", argv[4]);
+		exit(1);
+	}
+
+	buf = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (buf == NULL) {
+		fclose(fp);
+		fprintf(stderr, "Cannot alloc memory\n");
+		exit(1);
+	}
+	memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+
+	hostcmd = (HostCmd_DS_GEN *)(buf);
+	hostcmd->command = cpu_to_le16(MEFCFG_CMDCODE);
+	mefcmd = (HostCmd_DS_MEF_CFG *)(buf + S_DS_GEN);
+	buf_len = sizeof(HostCmd_DS_MEF_CFG) + S_DS_GEN;
+
+	while ((pos = mlan_config_get_line(fp, line, sizeof(line), &ln))) {
+		if (strcmp(pos, cmdname) == 0) {
+			cmdname_found = 1;
+			snprintf(cmdname, sizeof(cmdname), "Criteria=");
+			name_found = 0;
+			while ((pos =
+				mlan_config_get_line(fp, line, sizeof(line),
+						     &ln))) {
+				if (strncmp(pos, cmdname, strlen(cmdname)) == 0) {
+					name_found = 1;
+					mefcmd->Criteria =
+						a2hex_or_atoi(pos +
+							      strlen(cmdname));
+					break;
+				}
+			}
+			if (!name_found) {
+				fprintf(stderr,
+					"mlanconfig: criteria not found in file '%s'\n",
+					argv[3]);
+				break;
+			}
+			snprintf(cmdname, sizeof(cmdname), "NumEntries=");
+			name_found = 0;
+			while ((pos =
+				mlan_config_get_line(fp, line, sizeof(line),
+						     &ln))) {
+				if (strncmp(pos, cmdname, strlen(cmdname)) == 0) {
+					name_found = 1;
+					mefcmd->NumEntries =
+						a2hex_or_atoi(pos +
+							      strlen(cmdname));
+					break;
+				}
+			}
+			if (!name_found) {
+				fprintf(stderr,
+					"mlanconfig: NumEntries not found in file '%s'\n",
+					argv[3]);
+				break;
+			}
+			for (i = 0; i < mefcmd->NumEntries; i++) {
+				snprintf(cmdname, sizeof(cmdname),
+					 "mef_entry_%d={", i);
+				name_found = 0;
+				while ((pos =
+					mlan_config_get_line(fp, line,
+							     sizeof(line),
+							     &ln))) {
+					if (strncmp
+					    (pos, cmdname,
+					     strlen(cmdname)) == 0) {
+						name_found = 1;
+						break;
+					}
+				}
+				if (!name_found) {
+					fprintf(stderr,
+						"mlanconfig: %s not found in file '%s'\n",
+						cmdname, argv[3]);
+					break;
+				}
+				if (MLAN_STATUS_FAILURE ==
+				    mlan_get_mef_entry_data(fp, &ln,
+							    buf + buf_len,
+							    &len)) {
+					ret = MLAN_STATUS_FAILURE;
+					break;
+				}
+				buf_len += len;
+			}
+			break;
+		}
+	}
+	fclose(fp);
+	/* hexdump("mef_cfg",buf,buf_len, ' '); */
+	if (!cmdname_found)
+		fprintf(stderr,
+			"mlanconfig: cmdname '%s' not found in file '%s'\n",
+			argv[4], argv[3]);
+
+	if (!cmdname_found || !name_found) {
+		ret = MLAN_STATUS_FAILURE;
+		goto mef_exit;
+	}
+	hostcmd->size = cpu_to_le16(buf_len);
+	mefcmd->Criteria = cpu_to_le32(mefcmd->Criteria);
+	mefcmd->NumEntries = cpu_to_le16(mefcmd->NumEntries);
+
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.pointer = buf;
+	iwr.u.data.length = buf_len;
+	iwr.u.data.flags = 0;
+	if (ioctl(sockfd, ioctl_val, &iwr)) {
+		fprintf(stderr, "mlanconfig: MEFCFG is not supported by %s\n",
+			dev_name);
+		ret = MLAN_STATUS_FAILURE;
+		goto mef_exit;
+	}
+	ret = process_host_cmd_resp(buf);
+
+mef_exit:
+	if (buf)
+		free(buf);
+	return ret;
+
+}
+
+/**
+ *  @brief Process transmission of mgmt frames
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_mgmt_frame_tx(int argc, char *argv[])
+{
+	struct ifreq ifr;
+	char *line = NULL;
+	FILE *config_file = NULL;
+	int li = 0, arg_num = 0, ret = 0, i = 0;
+	char *args[100], *pos = NULL, mac_addr[20];
+	t_u8 peer_mac[ETH_ALEN];
+	t_u16 data_len = 0, subtype = 0;
+	wlan_mgmt_frame_tx *pmgmt_frame;
+	t_u8 *buffer = NULL;
+	pkt_header *hdr = NULL;
+
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX mgmtframetx <config/pkt.conf>\n");
+		exit(1);
+	}
+
+	data_len = sizeof(wlan_mgmt_frame_tx);
+
+	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+	hdr = (pkt_header *)buffer;
+	pmgmt_frame = (wlan_mgmt_frame_tx *)(buffer + sizeof(pkt_header));
+
+	/* Check if file exists */
+	config_file = fopen(argv[3], "r");
+	if (config_file == NULL) {
+		printf("\nERR:Config file can not open.\n");
+		goto done;
+	}
+	line = (char *)malloc(MAX_CONFIG_LINE);
+	if (!line) {
+		printf("ERR:Cannot allocate memory for line\n");
+		goto done;
+	}
+	memset(line, 0, MAX_CONFIG_LINE);
+
+	/* Parse file and process */
+	while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) {
+		arg_num = parse_line(line, args);
+		if (strcmp(args[0], "PktSubType") == 0) {
+			subtype = (t_u16)A2HEXDECIMAL(args[1]);
+			pmgmt_frame->frm_ctl |= subtype << 4;
+		} else if (strncmp(args[0], "Addr", 4) == 0) {
+			strncpy(mac_addr, args[1], 20);
+			if ((ret =
+			     mac2raw(mac_addr,
+				     peer_mac)) != MLAN_STATUS_SUCCESS) {
+				printf("ERR: %s Address \n",
+				       ret ==
+				       MLAN_STATUS_FAILURE ? "Invalid MAC" : ret
+				       ==
+				       MAC_BROADCAST ? "Broadcast" :
+				       "Multicast");
+				goto done;
+			}
+			i = atoi(args[0] + 4);
+			switch (i) {
+			case 1:
+				memcpy(pmgmt_frame->addr1, peer_mac, ETH_ALEN);
+				break;
+			case 2:
+				memcpy(pmgmt_frame->addr2, peer_mac, ETH_ALEN);
+				break;
+			case 3:
+				memcpy(pmgmt_frame->addr3, peer_mac, ETH_ALEN);
+				break;
+			case 4:
+				memcpy(pmgmt_frame->addr4, peer_mac, ETH_ALEN);
+				break;
+			}
+		} else if (strcmp(args[0], "Data") == 0) {
+			for (i = 0; i < arg_num - 1; i++)
+				pmgmt_frame->payload[i] =
+					(t_u8)A2HEXDECIMAL(args[i + 1]);
+			data_len += arg_num - 1;
+		}
+	}
+	pmgmt_frame->frm_len = data_len - sizeof(pmgmt_frame->frm_len);
+#define MRVL_PKT_TYPE_MGMT_FRAME 0xE5
+	hdr->pkt_len = data_len;
+	hdr->TxPktType = MRVL_PKT_TYPE_MGMT_FRAME;
+	hdr->TxControl = 0;
+	hexdump("Frame Tx", buffer, data_len + sizeof(pkt_header), ' ');
+	/* Send collective command */
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)buffer;
+
+	/* Perform ioctl */
+	if (ioctl(sockfd, FRAME_TX_IOCTL, &ifr)) {
+		perror("");
+		printf("ERR:Could not send management frame.\n");
+	} else {
+		printf("Mgmt Frame sucessfully sent.\n");
+	}
+
+done:
+	if (config_file)
+		fclose(config_file);
+	if (buffer)
+		free(buffer);
+	if (line)
+		free(line);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Performs the ioctl operation to send the command to
+ *  the driver.
+ *
+ *  @param cmd_buf       Pointer to the command buffer
+ *  @param buf_size      Size of the allocated command buffer
+ *  @return              MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+static int
+tdls_ioctl(t_u8 *cmd_buf, t_u16 buf_size)
+{
+	struct ifreq ifr;
+
+	/* Initialize the ifr structure */
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd_buf;
+
+	/* Perform ioctl */
+	if (ioctl(sockfd, TDLS_IOCTL, &ifr)) {
+		perror("");
+		return MLAN_STATUS_FAILURE;
+	}
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief enable/disable tdls config
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_config(int argc, char *argv[])
+{
+
+	tdls_config *param_buf = NULL;
+	int ret = 0;
+	t_u16 cmd_len = 0;
+	t_u8 *buffer = NULL;
+
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_config <0/1>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_config);
+
+	buffer = (t_u8 *)malloc(cmd_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, cmd_len);
+	param_buf = (tdls_config *) buffer;
+	param_buf->action = ACTION_TDLS_CONFIG;
+
+	param_buf->data = (t_u16)A2HEXDECIMAL(argv[3]);
+	if ((param_buf->data != 0) && (param_buf->data != 1)) {
+		printf("ERR:Incorrect arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_config <0/1>\n");
+		goto done;
+	}
+	hexdump("tdls_config ", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		printf("TDLS %s successful.\n",
+		       (param_buf->data) ? "enable" : "disable");
+	} else {
+		printf("ERR:TDLS %s failed.\n",
+		       (param_buf->data) ? "enable" : "disable");
+	}
+
+done:
+	if (buffer)
+		free(buffer);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process tdls_setinfo
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_setinfo(int argc, char *argv[])
+{
+	tdls_setinfo *param_buf = NULL;
+	char *line = NULL;
+	FILE *config_file = NULL;
+	int li = 0, arg_num = 0, ret = 0, i = 0, cmd_found = 0, pairwise_index =
+		0, akm_index = 0, pmkid_index = 0;
+	char *args[30], *pos = NULL;
+	t_u16 cmd_len = 0, tlv_len = 0, tlv_len_rsn = 0, tlv_len_supp_chan =
+		0, tlv_len_domain = 0;
+	t_u16 no_of_sub_band = 0, no_of_supp_chan_sub_band =
+		0, pairwise_offset = 0, akm_offset =
+		0, num_of_regulatory_class = 0, tlv_len_reg_class;
+	t_u16 akm_count = 0, pmk_count = 0, rsn_cap = 0;
+	t_u8 *buffer = NULL;
+	char country[COUNTRY_CODE_LEN];
+	tlvbuf_DomainParamSet_t *domain = NULL;
+	tlvbuf_SupportedChannels_t *supp_chan = NULL;
+	tlvbuf_RegulatoryClass_t *reg_class = NULL;
+	tlvbuf_HTCap_t *tlv_ht_cap = NULL;
+	tlvbuf_RsnParamSet_t *rsn_ie = NULL;
+	tlvbuf_HTInfo_t *tlv_ht_info = NULL;
+	t_u8 pairwise_cipher_suite[PAIRWISE_CIPHER_SUITE_LEN];
+	t_u8 akm_suite[AKM_SUITE_LEN];
+	t_u8 pmkid[PMKID_LEN];
+	tlvbuf_VHTCap_t *tlv_vht_cap = NULL;
+	tlvbuf_VHTOpra_t *tlv_vht_oper = NULL;
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_setinfo <config/tdls.conf>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_setinfo);
+
+	buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+	param_buf = (tdls_setinfo *)buffer;
+	param_buf->action = ACTION_TDLS_SETINFO;
+
+	/* Check if file exists */
+	config_file = fopen(argv[3], "r");
+	if (config_file == NULL) {
+		printf("\nERR:Config file can not open.\n");
+		goto done;
+	}
+	line = (char *)malloc(MAX_CONFIG_LINE);
+	if (!line) {
+		printf("ERR:Cannot allocate memory for line\n");
+		goto done;
+	}
+	memset(line, 0, MAX_CONFIG_LINE);
+
+	/* Parse file and process */
+	while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) {
+		arg_num = parse_line(line, args);
+		if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0])))
+			continue;
+
+		cmd_found = 1;
+		if (strcmp(args[0], "CapInfo") == 0) {
+			param_buf->cap_info = (t_u16)A2HEXDECIMAL(args[1]);
+			param_buf->cap_info = cpu_to_le16(param_buf->cap_info);
+		} else if (strcmp(args[0], "Rate") == 0) {
+			tlvbuf_RatesParamSet_t *tlv = NULL;
+			/* Append a new TLV */
+			tlv_len = sizeof(tlvbuf_RatesParamSet_t) + arg_num - 1;
+			tlv = (tlvbuf_RatesParamSet_t *)(buffer + cmd_len);
+			cmd_len += tlv_len;
+			/* Set TLV fields */
+			tlv->tag = TLV_TYPE_RATES;
+			tlv->length = arg_num - 1;
+			for (i = 0; i < tlv->length; i++) {
+				tlv->rates[i] = (t_u8)A2HEXDECIMAL(args[i + 1]);
+			}
+			endian_convert_tlv_header_out(tlv);
+		} else if (strcmp(args[0], "QosInfo") == 0) {
+			tlvbuf_QosInfo_t *tlv = NULL;
+			/* Append a new TLV */
+			tlv_len = sizeof(tlvbuf_QosInfo_t);
+			tlv = (tlvbuf_QosInfo_t *)(buffer + cmd_len);
+			cmd_len += tlv_len;
+			/* Set TLV fields */
+			tlv->tag = TLV_TYPE_QOSINFO;
+			tlv->length = sizeof(tlvbuf_QosInfo_t) - TLVHEADER_LEN;
+			tlv->u.qos_info_byte = (t_u8)A2HEXDECIMAL(args[1]);
+			if ((tlv->u.qos_info_byte != 0) &&
+			    (tlv->u.qos_info_byte != 0x0F)) {
+				printf("Invalid QosInfo. Should be 0x00 or 0x0F.\n");
+				goto done;
+			}
+			endian_convert_tlv_header_out(tlv);
+		} else if (strcmp(args[0], "ExtendCapabilities") == 0) {
+			tlvbuf_ExtCap_t *tlv = NULL;
+			/* Append a new TLV */
+			tlv_len = sizeof(tlvbuf_ExtCap_t) + arg_num - 1;
+			tlv = (tlvbuf_ExtCap_t *)(buffer + cmd_len);
+			cmd_len += tlv_len;
+			/* Set TLV fields */
+			tlv->tag = TLV_TYPE_EXTCAP;
+			tlv->length = arg_num - 1;
+			for (i = 0; i < tlv->length; i++) {
+				tlv->ext_cap[i] =
+					(t_u8)A2HEXDECIMAL(args[i + 1]);
+			}
+			endian_convert_tlv_header_out(tlv);
+		} else if (strcmp(args[0], "HTCapability") == 0) {
+			/* Append a new TLV */
+			tlv_ht_cap = (tlvbuf_HTCap_t *)(buffer + cmd_len);
+			tlv_len = sizeof(tlvbuf_HTCap_t);
+			tlv_ht_cap->tag = TLV_TYPE_HT_CAP;
+			tlv_ht_cap->length =
+				sizeof(tlvbuf_HTCap_t) - TLVHEADER_LEN;
+			cmd_len += tlv_len;
+			endian_convert_tlv_header_out(tlv_ht_cap);
+		} else if (strcmp(args[0], "HTCapabilityInfo") == 0) {
+			tlv_ht_cap->ht_cap.ht_cap_info =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			tlv_ht_cap->ht_cap.ht_cap_info =
+				cpu_to_le16(tlv_ht_cap->ht_cap.ht_cap_info);
+		} else if (strcmp(args[0], "AMPDUParam") == 0) {
+			tlv_ht_cap->ht_cap.ampdu_param =
+				(t_u8)A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "SupportedMCSSet") == 0) {
+			for (i = 0; i < MCS_SET_LEN; i++)
+				tlv_ht_cap->ht_cap.supported_mcs_set[i] =
+					(t_u8)A2HEXDECIMAL(args[i + 1]);
+		} else if (strcmp(args[0], "HTExtCapability") == 0) {
+			tlv_ht_cap->ht_cap.ht_ext_cap =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			tlv_ht_cap->ht_cap.ht_ext_cap =
+				cpu_to_le16(tlv_ht_cap->ht_cap.ht_ext_cap);
+		} else if (strcmp(args[0], "TxBfCapability") == 0) {
+			tlv_ht_cap->ht_cap.tx_bf_cap =
+				(t_u32)A2HEXDECIMAL(args[1]);
+			tlv_ht_cap->ht_cap.tx_bf_cap =
+				cpu_to_le32(tlv_ht_cap->ht_cap.tx_bf_cap);
+		} else if (strcmp(args[0], "AntennaSel") == 0) {
+			tlv_ht_cap->ht_cap.asel = (t_u8)A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "HTInformation") == 0) {
+			/* Append a new TLV */
+			tlv_ht_info = (tlvbuf_HTInfo_t *)(buffer + cmd_len);
+			tlv_len = sizeof(tlvbuf_HTInfo_t);
+			tlv_ht_info->tag = TLV_TYPE_HT_INFO;
+			tlv_ht_info->length =
+				sizeof(tlvbuf_HTInfo_t) - TLVHEADER_LEN;
+			cmd_len += tlv_len;
+			endian_convert_tlv_header_out(tlv_ht_info);
+		} else if (strcmp(args[0], "PrimaryChannel") == 0) {
+			tlv_ht_info->ht_info.pri_chan = A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "Field2") == 0) {
+			tlv_ht_info->ht_info.field2 = A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "Field3") == 0) {
+			tlv_ht_info->ht_info.field3 =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			tlv_ht_info->ht_info.field3 =
+				cpu_to_le16(tlv_ht_info->ht_info.field3);
+		} else if (strcmp(args[0], "Field4") == 0) {
+			tlv_ht_info->ht_info.field4 =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			tlv_ht_info->ht_info.field4 =
+				cpu_to_le16(tlv_ht_info->ht_info.field4);
+		} else if (strcmp(args[0], "BasicMCSSet") == 0) {
+			if ((arg_num - 1) != MCS_SET_LEN) {
+				printf("Incorrect number of arguments for BasicMCSSet.\n");
+				goto done;
+			}
+			for (i = 0; i < MCS_SET_LEN; i++)
+				tlv_ht_info->ht_info.basic_mcs_set[i] =
+					(t_u8)A2HEXDECIMAL(args[i + 1]);
+		} else if (strcmp(args[0], "2040BSSCoex") == 0) {
+			tlvbuf_2040BSSCo_t *tlv = NULL;
+			/* Append a new TLV */
+			tlv_len = sizeof(tlvbuf_2040BSSCo_t);
+			tlv = (tlvbuf_2040BSSCo_t *)(buffer + cmd_len);
+			cmd_len += tlv_len;
+			/* Set TLV fields */
+			tlv->tag = TLV_TYPE_2040BSS_COEXISTENCE;
+			tlv->length =
+				sizeof(tlvbuf_2040BSSCo_t) - TLVHEADER_LEN;
+			tlv->bss_co_2040.bss_co_2040_value =
+				(t_u8)A2HEXDECIMAL(args[1]);
+			endian_convert_tlv_header_out(tlv);
+		} else if (strcmp(args[0], "RSNInfo") == 0) {
+			/* Append a new TLV */
+			rsn_ie = (tlvbuf_RsnParamSet_t *)(buffer + cmd_len);
+			tlv_len_rsn = sizeof(tlvbuf_RsnParamSet_t);
+			rsn_ie->tag = TLV_TYPE_RSN_IE;
+			rsn_ie->version = VERSION_RSN_IE;
+			rsn_ie->version = cpu_to_le16(rsn_ie->version);
+			cmd_len += tlv_len_rsn;
+		} else if (strcmp(args[0], "GroupCipherSuite") == 0) {
+			for (i = 0; i < GROUP_CIPHER_SUITE_LEN; i++)
+				rsn_ie->group_cipher_suite[i] =
+					(t_u8)A2HEXDECIMAL(args[i + 1]);
+		} else if (strcmp(args[0], "PairwiseCipherCount") == 0) {
+			rsn_ie->pairwise_cipher_count = (t_u16)atoi(args[1]);
+			rsn_ie->pairwise_cipher_count =
+				cpu_to_le16(rsn_ie->pairwise_cipher_count);
+		} else if (strncmp(args[0], "PairwiseCipherSuite", 19) == 0) {
+			if (pairwise_index > MAX_PAIRWISE_CIPHER_SUITE_COUNT) {
+				printf("PairwiseCipherSuite exceeds max count\n");
+				goto done;
+			}
+			tlv_len_rsn += PAIRWISE_CIPHER_SUITE_LEN;
+			cmd_len += PAIRWISE_CIPHER_SUITE_LEN;
+			for (i = 0; i < PAIRWISE_CIPHER_SUITE_LEN; i++) {
+				pairwise_cipher_suite[i] =
+					(t_u8)A2HEXDECIMAL(args[i + 1]);
+			}
+			memcpy((t_u8 *)(rsn_ie->pairwise_cipher_suite +
+					(pairwise_index *
+					 PAIRWISE_CIPHER_SUITE_LEN)),
+			       pairwise_cipher_suite,
+			       PAIRWISE_CIPHER_SUITE_LEN);
+			pairwise_index++;
+			pairwise_offset =
+				pairwise_index * PAIRWISE_CIPHER_SUITE_LEN;
+		} else if (strcmp(args[0], "AKMSuiteCount") == 0) {
+			akm_count = (t_u16)atoi(args[1]);
+			akm_count = cpu_to_le16(akm_count);
+			memcpy((((t_u8 *)(&rsn_ie->akm_suite_count)) +
+				pairwise_offset), &akm_count, sizeof(t_u16));
+		} else if (strncmp(args[0], "AKMSuite", 8) == 0) {
+			if (akm_index > MAX_AKM_SUITE_COUNT) {
+				printf("AKMSuite exceeds max count\n");
+				goto done;
+			}
+			tlv_len_rsn += AKM_SUITE_LEN;
+			cmd_len += AKM_SUITE_LEN;
+			for (i = 0; i < AKM_SUITE_LEN; i++) {
+				akm_suite[i] = (t_u8)A2HEXDECIMAL(args[i + 1]);
+			}
+			memcpy((t_u8 *)(rsn_ie->akm_suite +
+					(akm_index * AKM_SUITE_LEN)
+					+ pairwise_offset), akm_suite,
+			       AKM_SUITE_LEN);
+			akm_index++;
+			akm_offset = akm_index * AKM_SUITE_LEN;
+		} else if (strcmp(args[0], "RSNCapability") == 0) {
+			rsn_cap = (t_u16)A2HEXDECIMAL(args[1]);
+			rsn_cap = cpu_to_le16(rsn_cap);
+			memcpy(((t_u8 *)(&(rsn_ie->rsn_capability))) +
+			       pairwise_offset + akm_offset, &rsn_cap,
+			       sizeof(t_u16));
+		} else if (strcmp(args[0], "PMKIDCount") == 0) {
+			pmk_count = (t_u16)atoi(args[1]);
+			pmk_count = cpu_to_le16(pmk_count);
+			memcpy((((t_u8 *)(&rsn_ie->pmkid_count)) +
+				pairwise_offset + akm_offset), &pmk_count,
+			       sizeof(t_u16));
+			rsn_ie->length = tlv_len_rsn - TLVHEADER_LEN;
+			endian_convert_tlv_header_out(rsn_ie);
+		} else if (strncmp(args[0], "PMKIDList", 9) == 0) {
+			if (pmkid_index > MAX_PMKID_COUNT) {
+				printf("PMKIDSuite exceeds max count\n");
+				goto done;
+			}
+			for (i = 0; i < PMKID_LEN; i++)
+				pmkid[i] = (t_u8)A2HEXDECIMAL(args[i + 1]);
+			memcpy((t_u8 *)(rsn_ie->pmkid_list +
+					(pmkid_index * PMKID_LEN) +
+					pairwise_offset + akm_offset), pmkid,
+			       PMKID_LEN);
+			pmkid_index++;
+			tlv_len_rsn += PMKID_LEN;
+			cmd_len += PMKID_LEN;
+			/* undo conversion done in PMKIDCount */
+			endian_convert_tlv_header_in(rsn_ie);
+			rsn_ie->length = tlv_len_rsn - TLVHEADER_LEN;
+			endian_convert_tlv_header_out(rsn_ie);
+		} else if (strcmp(args[0], "SupportedChannels") == 0) {
+			/* Append a new TLV */
+			supp_chan =
+				(tlvbuf_SupportedChannels_t *)(buffer +
+							       cmd_len);
+			supp_chan->tag = TLV_TYPE_SUPPORTED_CHANNELS;
+			supp_chan->length = sizeof(tlvbuf_SupportedChannels_t)
+				- TLVHEADER_LEN;
+			tlv_len_supp_chan = sizeof(tlvbuf_SupportedChannels_t);
+			cmd_len += tlv_len_supp_chan;
+		} else if (strncmp(args[0], "FirstChannelNo", 14) == 0) {
+			supp_chan->subband[no_of_supp_chan_sub_band].
+				start_chan = atoi(args[1]);
+		} else if (strcmp(args[0], "NumberofSubBandChannels") == 0) {
+			supp_chan->subband[no_of_supp_chan_sub_band].num_chans =
+				atoi(args[1]);
+			no_of_supp_chan_sub_band++;
+			tlv_len_supp_chan +=
+				sizeof(IEEEtypes_SupportChan_Subband_t);
+			supp_chan->length +=
+				sizeof(IEEEtypes_SupportChan_Subband_t);
+			cmd_len += sizeof(IEEEtypes_SupportChan_Subband_t);
+			endian_convert_tlv_header_out(supp_chan);
+		} else if (strcmp(args[0], "SupportedRegulatoryClasses") == 0) {
+			/* Append a new TLV */
+			reg_class =
+				(tlvbuf_RegulatoryClass_t *)(buffer + cmd_len);
+			tlv_len_reg_class = sizeof(tlvbuf_RegulatoryClass_t);
+			reg_class->tag = TLV_TYPE_REGULATORY_CLASSES;
+			cmd_len += tlv_len_reg_class;
+		} else if (strcmp(args[0], "CurrentRegulatoryClass") == 0) {
+			reg_class->regulatory_class.cur_regulatory_class =
+				atoi(args[1]);
+			reg_class->length = 1;
+		} else if (strcmp(args[0], "NumofRegulatoryClasses") == 0) {
+			num_of_regulatory_class = atoi(args[1]);
+			reg_class->length += num_of_regulatory_class;
+			cmd_len += num_of_regulatory_class;
+			endian_convert_tlv_header_out(reg_class);
+		} else if (strcmp(args[0], "ListOfRegulatoryClasses") == 0) {
+			for (i = 0; i < num_of_regulatory_class; i++)
+				reg_class->regulatory_class.
+					regulatory_classes_list[i] =
+					(t_u8)A2HEXDECIMAL(args[i + 1]);
+		} else if (strcmp(args[0], "CountryInfo") == 0) {
+			/* Append a new TLV */
+			domain = (tlvbuf_DomainParamSet_t *)(buffer + cmd_len);
+			domain->tag = TLV_TYPE_DOMAIN;
+			domain->length = sizeof(tlvbuf_DomainParamSet_t)
+				- TLVHEADER_LEN;
+			tlv_len_domain = sizeof(tlvbuf_DomainParamSet_t);
+			cmd_len += tlv_len_domain;
+		} else if (strcmp(args[0], "CountryString") == 0) {
+			strncpy(country, args[1] + 1, strlen(args[1]) - 2);
+			country[strlen(args[1]) - 2] = '\0';
+			for (i = 1; (unsigned int)i < strlen(country) - 2; i++) {
+				if ((country[i] < 'A') || (country[i] > 'z')) {
+					printf("Invalid Country Code\n");
+					goto done;
+				}
+				if (country[i] > 'Z')
+					country[i] = country[i] - 'a' + 'A';
+			}
+			memset(domain->country_code, ' ',
+			       sizeof(domain->country_code));
+			memcpy(domain->country_code, country, strlen(country));
+		} else if (strncmp(args[0], "FirstChannel", 12) == 0) {
+			domain->sub_band[no_of_sub_band].first_chan =
+				atoi(args[1]);
+		} else if (strncmp(args[0], "NumberofChannels", 16) == 0) {
+			domain->sub_band[no_of_sub_band].no_of_chan =
+				atoi(args[1]);
+		} else if (strncmp(args[0], "TxPower", 7) == 0) {
+			domain->sub_band[no_of_sub_band].max_tx_pwr =
+				atoi(args[1]);
+			no_of_sub_band++;
+			domain->length += sizeof(IEEEtypes_SubbandSet_t);
+			tlv_len_domain += sizeof(IEEEtypes_SubbandSet_t);
+			cmd_len += sizeof(IEEEtypes_SubbandSet_t);
+			endian_convert_tlv_header_out(domain);
+		} else if (strcmp(args[0], "VHTCapability") == 0) {
+			/* Append a new TLV */
+			tlv_vht_cap = (tlvbuf_VHTCap_t *)(buffer + cmd_len);
+			tlv_len = sizeof(tlvbuf_VHTCap_t);
+			tlv_vht_cap->tag = TLV_TYPE_VHT_CAP;
+			tlv_vht_cap->length =
+				sizeof(tlvbuf_VHTCap_t) - TLVHEADER_LEN;
+			cmd_len += tlv_len;
+			endian_convert_tlv_header_out(tlv_vht_cap);
+		} else if (strcmp(args[0], "VHTCapabilityInfo") == 0) {
+			tlv_vht_cap->vht_cap.vht_cap_info =
+				(t_u32)A2HEXDECIMAL(args[1]);
+			tlv_vht_cap->vht_cap.vht_cap_info =
+				cpu_to_le16(tlv_vht_cap->vht_cap.vht_cap_info);
+		} else if (strcmp(args[0], "RxMCSMap") == 0) {
+			tlv_vht_cap->vht_cap.mcs_sets.rx_mcs_map =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			tlv_vht_cap->vht_cap.mcs_sets.rx_mcs_map =
+				cpu_to_le16(tlv_vht_cap->vht_cap.mcs_sets.
+					    rx_mcs_map);
+		} else if (strcmp(args[0], "TxMCSMap") == 0) {
+			tlv_vht_cap->vht_cap.mcs_sets.tx_mcs_map =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			tlv_vht_cap->vht_cap.mcs_sets.tx_mcs_map =
+				cpu_to_le16(tlv_vht_cap->vht_cap.mcs_sets.
+					    tx_mcs_map);
+		} else if (strcmp(args[0], "RxMaxRate") == 0) {
+			tlv_vht_cap->vht_cap.mcs_sets.rx_max_rate =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			tlv_vht_cap->vht_cap.mcs_sets.rx_max_rate =
+				cpu_to_le16(tlv_vht_cap->vht_cap.mcs_sets.
+					    rx_max_rate);
+		} else if (strcmp(args[0], "TxMaxRate") == 0) {
+			tlv_vht_cap->vht_cap.mcs_sets.tx_max_rate =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			tlv_vht_cap->vht_cap.mcs_sets.tx_max_rate =
+				cpu_to_le16(tlv_vht_cap->vht_cap.mcs_sets.
+					    tx_max_rate);
+		} else if (strcmp(args[0], "VHTOper") == 0) {
+			/* Append a new TLV */
+			tlv_vht_oper = (tlvbuf_VHTOpra_t *)(buffer + cmd_len);
+			tlv_len = sizeof(tlvbuf_VHTOpra_t);
+			tlv_vht_oper->tag = TLV_TYPE_VHT_OPER;
+			tlv_vht_oper->length =
+				sizeof(tlvbuf_VHTOpra_t) - TLVHEADER_LEN;
+			cmd_len += tlv_len;
+			endian_convert_tlv_header_out(tlv_vht_oper);
+		} else if (strcmp(args[0], "ChanWidth") == 0) {
+			tlv_vht_oper->chan_width = A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "ChanCF1") == 0) {
+			tlv_vht_oper->chan_cf1 = A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "ChanCF2") == 0) {
+			tlv_vht_oper->chan_cf2 = (t_u16)A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "BasicMCSMap") == 0) {
+			if ((arg_num - 1) != VHT_MCS_MAP_LEN) {
+				printf("Incorrect number of arguments for BasicMCSMap.\n");
+				goto done;
+			}
+			for (i = 0; i < VHT_MCS_MAP_LEN; i++)
+				tlv_vht_oper->basic_mcs_map[i] =
+					(t_u8)A2HEXDECIMAL(args[i + 1]);
+		}
+	}
+	/* adjust for size of action and tlv_len, capInfo */
+	param_buf->tlv_len = cmd_len - sizeof(tdls_setinfo);
+
+	hexdump("tdls_setinfo", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		printf("TDLS Info settings sucessfully set.\n");
+	} else {
+		printf("ERR:Could not set TDLS info configuration.\n");
+	}
+
+done:
+	if (config_file)
+		fclose(config_file);
+	if (buffer)
+		free(buffer);
+	if (line)
+		free(line);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process tdls_discovery
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_discovery(int argc, char *argv[])
+{
+	tdls_discovery *param_buf = NULL;
+	tdls_discovery_resp *resp_buf = NULL;
+	char *line = NULL;
+	FILE *config_file = NULL;
+	int li = 0, ret = 0, cmd_found = 0, rssi = 0;
+	char *args[30], *pos = NULL, mac_addr[20];
+	t_u8 peer_mac[ETH_ALEN];
+	t_u16 cmd_len = 0, buf_len = 0, resp_len = 0;
+	t_u8 *buffer = NULL, *raw = NULL;
+	IEEEtypes_Header_t *tlv = NULL;
+
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_discovery <config/tdls.conf>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_discovery);
+	buf_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+
+	buffer = (t_u8 *)malloc(buf_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, buf_len);
+	param_buf = (tdls_discovery *)buffer;
+	param_buf->action = ACTION_TDLS_DISCOVERY;
+
+	/* Check if file exists */
+	config_file = fopen(argv[3], "r");
+	if (config_file == NULL) {
+		printf("\nERR:Config file can not open.\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	line = (char *)malloc(MAX_CONFIG_LINE);
+	if (!line) {
+		printf("ERR:Cannot allocate memory for line\n");
+		goto done;
+	}
+	memset(line, 0, MAX_CONFIG_LINE);
+
+	/* Parse file and process */
+	while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) {
+		if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0])))
+			continue;
+
+		cmd_found = 1;
+		if (strcmp(args[0], "PeerMAC") == 0) {
+			strncpy(mac_addr, args[1], 20);
+			if ((ret =
+			     mac2raw(mac_addr,
+				     peer_mac)) != MLAN_STATUS_SUCCESS) {
+				printf("ERR: %s Address \n",
+				       ret ==
+				       MLAN_STATUS_FAILURE ? "Invalid MAC" : ret
+				       ==
+				       MAC_BROADCAST ? "Broadcast" :
+				       "Multicast");
+				goto done;
+			}
+			memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN);
+		} else if (strcmp(args[0], "}") == 0 && cmd_found) {
+			break;
+		}
+	}
+	hexdump("tdls_discovery", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		hexdump("tdls_response", buffer, 0x40, ' ');
+		printf("TDLS discovery done.\n");
+		resp_buf = (tdls_discovery_resp *)buffer;
+		resp_len = resp_buf->payload_len;
+		printf("Response Length = %d\n", resp_len);
+		if (resp_len > 0) {
+			/* MAC */
+			raw = resp_buf->peer_mac;
+			printf("\tPeer - %02x:%02x:%02x:%02x:%02x:%02x\n",
+			       (unsigned int)raw[0], (unsigned int)raw[1],
+			       (unsigned int)raw[2], (unsigned int)raw[3],
+			       (unsigned int)raw[4], (unsigned int)raw[5]);
+
+			/* RSSI, CapInfo */
+			rssi = (int)(resp_buf->rssi);
+			if (rssi > 0x7f)
+				rssi = -(256 - rssi);
+			printf("\tRssi : %d dBm\n", rssi);
+			printf("\tCapInfo = 0x%02X\n", resp_buf->cap_info);
+
+			resp_len -= ETH_ALEN + sizeof(resp_buf->rssi) +
+				sizeof(resp_buf->cap_info);
+
+			/* TLVs */
+			tlv = (IEEEtypes_Header_t *)&resp_buf->tlv_buffer;
+			while (resp_len > IEEE_HEADER_LEN) {
+				switch (tlv->element_id) {
+				case TLV_TYPE_RATES:
+					printf("\tRates : ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+				case TLV_EXTENDED_SUPPORTED_RATES:
+					printf("\tExtended Rates : ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+				case TLV_TYPE_QOSINFO:
+					printf("\tQosInfo ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+
+				case TLV_TYPE_EXTCAP:
+					printf("\tExtended Cap ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+
+				case TLV_TYPE_HT_CAP:
+					printf("\tHT Cap ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+
+				case TLV_TYPE_HT_INFO:
+					printf("\tHT Info");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+
+				case TLV_TYPE_2040BSS_COEXISTENCE:
+					printf("\t2040 BSS Coex ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+
+				case TLV_TYPE_RSN_IE:
+					printf("\tRSN IE ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+
+				case TLV_TYPE_SUPPORTED_CHANNELS:
+					printf("\tSupported Channels ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+
+				case TLV_TYPE_DOMAIN:
+					printf("\tDomain Info ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+				case TLV_LINK_IDENTIFIER:
+					printf("\tLink identifier : ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+				case TLV_TIMEOUT_INTERVAL:
+					printf("\tTimeout interval : ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+				case TLV_TYPE_REGULATORY_CLASSES:
+					printf("\t Regulatory classes : ");
+					hexdump(NULL,
+						((t_u8 *)tlv) + IEEE_HEADER_LEN,
+						tlv->len, ' ');
+					break;
+				default:
+					printf("Unknown TLV\n");
+					hexdump(NULL, ((t_u8 *)tlv),
+						IEEE_HEADER_LEN + tlv->len,
+						' ');
+					break;
+				}
+				resp_len -= tlv->len + IEEE_HEADER_LEN;
+				tlv = (IEEEtypes_Header_t *)((t_u8 *)tlv +
+							     tlv->len +
+							     IEEE_HEADER_LEN);
+			}
+		}
+
+	} else {
+		printf("ERR:Command response = Fail!\n");
+	}
+done:
+	fclose(config_file);
+	if (buffer)
+		free(buffer);
+	if (line)
+		free(line);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process tdls_setup
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_setup(int argc, char *argv[])
+{
+	tdls_setup *param_buf = NULL;
+	char *line = NULL;
+	FILE *config_file = NULL;
+	int li = 0, ret = 0, cmd_found = 0;
+	char *args[30], *pos = NULL, mac_addr[20];
+	t_u8 peer_mac[ETH_ALEN];
+	t_u16 cmd_len = 0;
+	t_u8 *buffer = NULL;
+
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_setup <config/tdls.conf>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_setup);
+
+	buffer = (t_u8 *)malloc(cmd_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, cmd_len);
+	param_buf = (tdls_setup *) buffer;
+	param_buf->action = ACTION_TDLS_SETUP;
+
+	/* Check if file exists */
+	config_file = fopen(argv[3], "r");
+	if (config_file == NULL) {
+		printf("\nERR:Config file can not open.\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	line = (char *)malloc(MAX_CONFIG_LINE);
+	if (!line) {
+		printf("ERR:Cannot allocate memory for line\n");
+		goto done;
+	}
+	memset(line, 0, MAX_CONFIG_LINE);
+
+	/* Parse file and process */
+	while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) {
+		if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0])))
+			continue;
+
+		cmd_found = 1;
+
+		if (strcmp(args[0], "PeerMAC") == 0) {
+			strncpy(mac_addr, args[1], 20);
+			if ((ret =
+			     mac2raw(mac_addr,
+				     peer_mac)) != MLAN_STATUS_SUCCESS) {
+				printf("ERR: %s Address \n",
+				       ret ==
+				       MLAN_STATUS_FAILURE ? "Invalid MAC" : ret
+				       ==
+				       MAC_BROADCAST ? "Broadcast" :
+				       "Multicast");
+				goto done;
+			}
+			memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN);
+		} else if (strcmp(args[0], "WaitTimems") == 0) {
+			param_buf->wait_time = (t_u32)A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "KeyLifetime") == 0) {
+			param_buf->key_life_time = (t_u32)A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "}") == 0 && cmd_found) {
+			break;
+		}
+	}
+	hexdump("tdls_setup", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		printf("TDLS setup request successful.\n");
+	} else {
+		printf("ERR:TDLS setup request failed.\n");
+	}
+
+done:
+	fclose(config_file);
+	if (buffer)
+		free(buffer);
+	if (line)
+		free(line);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process tdls_teardown
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_teardown(int argc, char *argv[])
+{
+	tdls_teardown *param_buf = NULL;
+	char *line = NULL;
+	FILE *config_file = NULL;
+	int li = 0, ret = 0, cmd_found = 0;
+	char *args[30], *pos = NULL, mac_addr[20];
+	t_u8 peer_mac[ETH_ALEN];
+	t_u16 cmd_len = 0;
+	t_u8 *buffer = NULL;
+
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_teardown <config/tdls.conf>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_teardown);
+
+	buffer = (t_u8 *)malloc(cmd_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, cmd_len);
+	param_buf = (tdls_teardown *)buffer;
+	param_buf->action = ACTION_TDLS_TEARDOWN;
+
+	/* Check if file exists */
+	config_file = fopen(argv[3], "r");
+	if (config_file == NULL) {
+		printf("\nERR:Config file can not open.\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	line = (char *)malloc(MAX_CONFIG_LINE);
+	if (!line) {
+		printf("ERR:Cannot allocate memory for line\n");
+		goto done;
+	}
+	memset(line, 0, MAX_CONFIG_LINE);
+
+	/* Parse file and process */
+	while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) {
+		if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0])))
+			continue;
+
+		cmd_found = 1;
+
+		if (strcmp(args[0], "PeerMAC") == 0) {
+			strncpy(mac_addr, args[1], 20);
+			if ((ret =
+			     mac2raw(mac_addr,
+				     peer_mac)) != MLAN_STATUS_SUCCESS) {
+				printf("ERR: %s Address \n",
+				       ret ==
+				       MLAN_STATUS_FAILURE ? "Invalid MAC" : ret
+				       ==
+				       MAC_BROADCAST ? "Broadcast" :
+				       "Multicast");
+				goto done;
+			}
+			memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN);
+		} else if (strcmp(args[0], "ReasonCode") == 0) {
+			param_buf->reason_code = (t_u16)A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "}") == 0 && cmd_found) {
+			break;
+		}
+	}
+	hexdump("tdls_teardown", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		printf("TDLS teardown request successful.\n");
+	} else {
+		printf("ERR:TDLS teardown request failed.\n");
+	}
+
+done:
+	fclose(config_file);
+	if (buffer)
+		free(buffer);
+	if (line)
+		free(line);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process tdls_powermode
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_powermode(int argc, char *argv[])
+{
+	tdls_powermode *param_buf = NULL;
+	char *line = NULL;
+	FILE *config_file = NULL;
+	int li = 0, ret = 0, cmd_found = 0;
+	char *args[30], *pos = NULL, mac_addr[20];
+	t_u8 peer_mac[ETH_ALEN];
+	t_u16 cmd_len = 0;
+	t_u8 *buffer = NULL;
+
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_powermode <config/tdls.conf>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_powermode);
+
+	buffer = (t_u8 *)malloc(cmd_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, cmd_len);
+	param_buf = (tdls_powermode *)buffer;
+	param_buf->action = ACTION_TDLS_POWER_MODE;
+
+	/* Check if file exists */
+	config_file = fopen(argv[3], "r");
+	if (config_file == NULL) {
+		printf("\nERR:Config file can not open.\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	line = (char *)malloc(MAX_CONFIG_LINE);
+	if (!line) {
+		printf("ERR:Cannot allocate memory for line\n");
+		goto done;
+	}
+	memset(line, 0, MAX_CONFIG_LINE);
+
+	/* Parse file and process */
+	while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) {
+		if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0])))
+			continue;
+
+		cmd_found = 1;
+
+		if (strcmp(args[0], "PeerMAC") == 0) {
+			strncpy(mac_addr, args[1], 20);
+			if ((ret =
+			     mac2raw(mac_addr,
+				     peer_mac)) != MLAN_STATUS_SUCCESS) {
+				printf("ERR: %s Address \n",
+				       ret ==
+				       MLAN_STATUS_FAILURE ? "Invalid MAC" : ret
+				       ==
+				       MAC_BROADCAST ? "Broadcast" :
+				       "Multicast");
+				goto done;
+			}
+			memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN);
+		} else if (strcmp(args[0], "PowerMode") == 0) {
+			param_buf->power_mode = (t_u16)A2HEXDECIMAL(args[1]);
+			if (param_buf->power_mode > 1) {
+				printf("ERR: Incorrect PowerMode value %s\n",
+				       args[1]);
+				goto done;
+			}
+		} else if (strcmp(args[0], "}") == 0 && cmd_found) {
+			break;
+		}
+	}
+	hexdump("tdls_powermode", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		printf("TDLS powermode request successful.\n");
+	} else {
+		printf("ERR:TDLS powermode request failed.\n");
+	}
+
+done:
+	fclose(config_file);
+	if (buffer)
+		free(buffer);
+	if (line)
+		free(line);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process tdls_link_status
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_link_status(int argc, char *argv[])
+{
+	int ret = 0;
+	tdls_link_status *param_buf = NULL;
+	tdls_link_status_resp *resp_buf = NULL;
+	t_u16 cmd_len = 0, buf_len = 0, resp_len = 0, curr_link_len = 0;
+	t_u8 no_of_links = 0, peer_mac[ETH_ALEN];
+	t_u8 *buffer = NULL, *raw = NULL;
+	tdls_each_link_status *link_ptr = NULL;
+
+	/* Check arguments */
+	if (argc != 3 && argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_link_status [peer_mac_addr]\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_link_status);
+	buf_len = MRVDRV_SIZE_OF_CMD_BUFFER;
+
+	buffer = (t_u8 *)malloc(buf_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, buf_len);
+	param_buf = (tdls_link_status *)buffer;
+	param_buf->action = ACTION_TDLS_LINK_STATUS;
+
+	if (argc == 4) {
+		if ((ret = mac2raw(argv[3], peer_mac)) != MLAN_STATUS_SUCCESS) {
+			printf("ERR: %s Address \n",
+			       ret ==
+			       MLAN_STATUS_FAILURE ? "Invalid MAC" : ret ==
+			       MAC_BROADCAST ? "Broadcast" : "Multicast");
+			goto done;
+		}
+		if (memcmp(peer_mac, "\x00\x00\x00\x00\x00\x00", ETH_ALEN)) {
+			memcpy(buffer + cmd_len, peer_mac, ETH_ALEN);
+			cmd_len += ETH_ALEN;
+		}
+	}
+
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		hexdump("tdls_response", buffer, 0x60, ' ');
+		printf("TDLS Link Status - .\n");
+		resp_buf = (tdls_link_status_resp *)buffer;
+		resp_len = resp_buf->payload_len;
+		printf("Response Length = %d\n", resp_len);
+		no_of_links = resp_buf->active_links;
+		printf("No of active links = %d\n", no_of_links);
+		resp_len--;
+		link_ptr = resp_buf->link_stats;
+		while (resp_len > 0 && no_of_links > 0) {
+			curr_link_len = 0;
+			/* MAC */
+			raw = link_ptr->peer_mac;
+			printf("\tPeer - %02x:%02x:%02x:%02x:%02x:%02x\n",
+			       (unsigned int)raw[0], (unsigned int)raw[1],
+			       (unsigned int)raw[2], (unsigned int)raw[3],
+			       (unsigned int)raw[4], (unsigned int)raw[5]);
+
+			printf("\t %s initiated link.\n",
+			       (link_ptr->link_flags & 0x01) ? "Self" : "Peer");
+			printf("\t Security %s.\n",
+			       (link_ptr->
+				link_flags & 0x02) ? "Enabled" : "Disabled");
+			printf("\t Self PS status = %s.\n",
+			       (link_ptr->
+				link_flags & 0x04) ? "Sleep" : "Active");
+			printf("\t Peer PS status = %s.\n",
+			       (link_ptr->
+				link_flags & 0x08) ? "Sleep" : "Active");
+			printf("\t Channel switch is %ssupported\n",
+			       (link_ptr->link_flags & 0x10) ? "" : "NOT ");
+			printf("\t Current Channel %s\n",
+			       (link_ptr->link_flags & 0x20) ? "off" : "base");
+
+			if (link_ptr->traffic_status) {
+				printf("\t Buffered traffic for");
+				printf("%s",
+				       (link_ptr->
+					traffic_status & 0x01) ? "AC_BK, " :
+				       "");
+				printf("%s",
+				       (link_ptr->
+					traffic_status & 0x02) ? "AC_BE, " :
+				       "");
+				printf("%s",
+				       (link_ptr->
+					traffic_status & 0x04) ? "AC_VI, " :
+				       "");
+				printf("%s",
+				       (link_ptr->
+					traffic_status & 0x08) ? "AC_VO" : "");
+				printf(".\n");
+			}
+			printf("\t Successive Tx Failure count = %d\n",
+			       link_ptr->tx_fail_count);
+			printf("\t Active channel number = %d\n",
+			       link_ptr->active_channel);
+			printf("\t Last Data RSSI        = %d dBm\n",
+			       link_ptr->data_rssi_last);
+			printf("\t Last Data NF          = %d dBm\n",
+			       link_ptr->data_nf_last);
+			printf("\t Average Data RSSI     = %d dBm\n",
+			       link_ptr->data_rssi_avg);
+			printf("\t Average Data NF       = %d dBm\n",
+			       link_ptr->data_nf_avg);
+			printf("\t Tx data rate          = %d Mbps\n",
+			       link_ptr->u.final_data_rate);
+
+			/* size of unsecure structure */
+			curr_link_len = sizeof(tdls_each_link_status) -
+				(sizeof(t_u32) + sizeof(t_u8) + sizeof(t_u8));
+
+			if (link_ptr->link_flags & 0x02) {
+				/* security details */
+				printf("\t Security Method = %s\n",
+				       (link_ptr->security_method ==
+					1) ? "AES" : "None");
+				printf("\t Key Lifetime = %d ms\n\t",
+				       link_ptr->key_lifetime);
+				hexdump("Key", ((t_u8 *)link_ptr->key),
+					link_ptr->key_length, ' ');
+				curr_link_len +=
+					sizeof(t_u32) + sizeof(t_u8) +
+					sizeof(t_u8)
+					+ link_ptr->key_length;
+			}
+			resp_len -= curr_link_len;
+			link_ptr =
+				(tdls_each_link_status *)(((t_u8 *)link_ptr) +
+							  curr_link_len);
+			printf(".\n");
+		}
+
+	} else {
+		printf("ERR:Command response = Fail!\n");
+	}
+done:
+	if (buffer)
+		free(buffer);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process tdls_channel_swtich
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_channel_switch(int argc, char *argv[])
+{
+
+	tdls_channel_switch *param_buf = NULL;
+	char *line = NULL;
+	FILE *config_file = NULL;
+	int li = 0, ret = 0, cmd_found = 0;
+	char *args[30], *pos = NULL, mac_addr[20];
+	t_u8 peer_mac[ETH_ALEN];
+	t_u16 cmd_len = 0;
+	t_u8 *buffer = NULL;
+
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_channel_switch <config/tdls.conf>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_channel_switch);
+
+	buffer = (t_u8 *)malloc(cmd_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, cmd_len);
+	param_buf = (tdls_channel_switch *)buffer;
+	param_buf->action = ACTION_TDLS_INIT_CHAN_SWITCH;
+
+	/* Check if file exists */
+	config_file = fopen(argv[3], "r");
+	if (config_file == NULL) {
+		printf("\nERR:Config file can not open.\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	line = (char *)malloc(MAX_CONFIG_LINE);
+	if (!line) {
+		printf("ERR:Cannot allocate memory for line\n");
+		goto done;
+	}
+	memset(line, 0, MAX_CONFIG_LINE);
+
+	/* Parse file and process */
+	while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) {
+		if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0])))
+			continue;
+
+		cmd_found = 1;
+
+		if (strcmp(args[0], "PeerMAC") == 0) {
+			strncpy(mac_addr, args[1], 20);
+			if ((ret =
+			     mac2raw(mac_addr,
+				     peer_mac)) != MLAN_STATUS_SUCCESS) {
+				printf("ERR: %s Address \n",
+				       ret ==
+				       MLAN_STATUS_FAILURE ? "Invalid MAC" : ret
+				       ==
+				       MAC_BROADCAST ? "Broadcast" :
+				       "Multicast");
+				goto done;
+			}
+			memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN);
+		} else if (strcmp(args[0], "Band") == 0) {
+			param_buf->band = (t_u16)A2HEXDECIMAL(args[1]);
+			if (param_buf->band != BAND_BG &&
+			    param_buf->band != BAND_A) {
+				printf("ERR: Incorrect Band value %s\n",
+				       args[1]);
+				goto done;
+			}
+		} else if (strcmp(args[0], "RegulatoryClass") == 0) {
+			param_buf->regulatory_class =
+				(t_u16)A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "PrimaryChannel") == 0) {
+			param_buf->primary_channel =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			if (param_buf->band == BAND_BG &&
+			    param_buf->primary_channel < MIN_BG_CHANNEL &&
+			    param_buf->primary_channel > MAX_BG_CHANNEL) {
+				printf("ERR: Incorrect Primary Channel value  %s\n", args[1]);
+				goto done;
+			} else if (param_buf->band == BAND_A
+				   && param_buf->primary_channel < MIN_A_CHANNEL
+				   && param_buf->primary_channel >
+				   MAX_A_CHANNEL) {
+				printf("ERR: Incorrect Primary Channel value  %s\n", args[1]);
+				goto done;
+			}
+		} else if (strcmp(args[0], "SecondaryChannelOffset") == 0) {
+			param_buf->secondary_channel_offset =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			if (param_buf->secondary_channel_offset != 0 &&
+			    param_buf->secondary_channel_offset !=
+			    SECOND_CHANNEL_ABOVE &&
+			    param_buf->secondary_channel_offset !=
+			    SECOND_CHANNEL_BELOW) {
+				printf("ERR: Incorrect Secondary Channel Offset value  %s\n", args[1]);
+				goto done;
+			}
+		} else if (strcmp(args[0], "ChannelSwitchTime") == 0) {
+			param_buf->switch_time = (t_u16)A2HEXDECIMAL(args[1]);
+			if (param_buf->switch_time == 0) {
+				printf("ERR: Incorrect Channel Switch time  %s\n", args[1]);
+				goto done;
+			}
+		} else if (strcmp(args[0], "ChannelSwitchTimeout") == 0) {
+			param_buf->switch_timeout =
+				(t_u16)A2HEXDECIMAL(args[1]);
+			if (param_buf->switch_timeout == 0) {
+				printf("ERR: Incorrect Channel Switch timeout  %s\n", args[1]);
+				goto done;
+			}
+		} else if (strcmp(args[0], "Periodicity") == 0) {
+			param_buf->periodicity = (t_u16)A2HEXDECIMAL(args[1]);
+			if (param_buf->periodicity != NO_PERIODIC_SWITCH
+			    && param_buf->periodicity !=
+			    ENABLE_PERIODIC_SWITCH) {
+				printf("ERR: Incorrect Periodicity value %s\n",
+				       args[1]);
+				goto done;
+			}
+		} else if (strcmp(args[0], "}") == 0 && cmd_found) {
+			break;
+		}
+	}
+	hexdump("tdls_channel_switch", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		printf("TDLS channel switch request successful.\n");
+	} else {
+		printf("ERR:TDLS channel switch request failed.\n");
+	}
+
+done:
+	fclose(config_file);
+	if (buffer)
+		free(buffer);
+	if (line)
+		free(line);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief disable tdls_channel_swtich
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_disable_channel_switch(int argc, char *argv[])
+{
+
+	tdls_disable_cs *param_buf = NULL;
+	int ret = 0;
+	t_u16 cmd_len = 0;
+	t_u8 *buffer = NULL;
+
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_disable_cs <0/1>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_disable_cs);
+
+	buffer = (t_u8 *)malloc(cmd_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, cmd_len);
+	param_buf = (tdls_disable_cs *) buffer;
+	param_buf->action = ACTION_TDLS_CS_DISABLE;
+
+	param_buf->data = (t_u16)A2HEXDECIMAL(argv[3]);
+	if ((param_buf->data != 0) && (param_buf->data != 1)) {
+		printf("ERR:Incorrect arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_disable_cs <0/1>\n");
+		goto done;
+	}
+	hexdump("tdls_disable_cs", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		printf("TDLS disable channel switch successful.\n");
+	} else {
+		printf("ERR:TDLS disable channel switch failed.\n");
+	}
+
+done:
+	if (buffer)
+		free(buffer);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process tdls_stop_channel_switch
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_stop_channel_switch(int argc, char *argv[])
+{
+	tdls_stop_chan_switch *param_buf = NULL;
+	char *line = NULL;
+	FILE *config_file = NULL;
+	int li = 0, ret = 0, cmd_found = 0;
+	char *args[30], *pos = NULL, mac_addr[20];
+	t_u8 peer_mac[ETH_ALEN];
+	t_u16 cmd_len = 0;
+	t_u8 *buffer = NULL;
+
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_stop_channel_switch <config/tdls.conf>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_stop_chan_switch);
+
+	buffer = (t_u8 *)malloc(cmd_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, cmd_len);
+	param_buf = (tdls_stop_chan_switch *) buffer;
+	param_buf->action = ACTION_TDLS_STOP_CHAN_SWITCH;
+
+	/* Check if file exists */
+	config_file = fopen(argv[3], "r");
+	if (config_file == NULL) {
+		printf("\nERR:Config file can not open.\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	line = (char *)malloc(MAX_CONFIG_LINE);
+	if (!line) {
+		printf("ERR:Cannot allocate memory for line\n");
+		goto done;
+	}
+	memset(line, 0, MAX_CONFIG_LINE);
+
+	/* Parse file and process */
+	while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) {
+		if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0])))
+			continue;
+
+		cmd_found = 1;
+
+		if (strcmp(args[0], "PeerMAC") == 0) {
+			strncpy(mac_addr, args[1], 20);
+			if ((ret =
+			     mac2raw(mac_addr,
+				     peer_mac)) != MLAN_STATUS_SUCCESS) {
+				printf("ERR: %s Address \n",
+				       ret ==
+				       MLAN_STATUS_FAILURE ? "Invalid MAC" : ret
+				       ==
+				       MAC_BROADCAST ? "Broadcast" :
+				       "Multicast");
+				goto done;
+			}
+			memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN);
+		} else if (strcmp(args[0], "}") == 0 && cmd_found) {
+			break;
+		}
+	}
+	hexdump("tdls_stop_channel_switch", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		printf("TDLS stop channel switch successful.\n");
+	} else {
+		printf("ERR:TDLS stop channel switch failed.\n");
+	}
+
+done:
+	fclose(config_file);
+	if (buffer)
+		free(buffer);
+	if (line)
+		free(line);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process tdls_cs_params
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_cs_params(int argc, char *argv[])
+{
+	tdls_cs_params *param_buf = NULL;
+	char *line = NULL;
+	FILE *config_file = NULL;
+	int li = 0, ret = 0, cmd_found = 0;
+	char *args[30], *pos = NULL;
+	t_u16 cmd_len = 0;
+	t_u8 *buffer = NULL;
+
+	/* Check arguments */
+	if (argc != 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_cs_params <config/tdls.conf>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_cs_params);
+
+	buffer = (t_u8 *)malloc(cmd_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		goto done;
+	}
+	memset(buffer, 0, cmd_len);
+	param_buf = (tdls_cs_params *) buffer;
+	param_buf->action = ACTION_TDLS_CS_PARAMS;
+
+	/* Check if file exists */
+	config_file = fopen(argv[3], "r");
+	if (config_file == NULL) {
+		printf("\nERR:Config file can not open.\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	line = (char *)malloc(MAX_CONFIG_LINE);
+	if (!line) {
+		printf("ERR:Cannot allocate memory for line\n");
+		goto done;
+	}
+	memset(line, 0, MAX_CONFIG_LINE);
+
+	/* Parse file and process */
+	while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) {
+		if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0])))
+			continue;
+
+		cmd_found = 1;
+
+		if (strcmp(args[0], "UnitTime") == 0) {
+			param_buf->unit_time = (t_u8)A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "ThresholdOtherLink") == 0) {
+			param_buf->threshold_otherlink =
+				(t_u8)A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "ThresholdDirectLink") == 0) {
+			param_buf->threshold_directlink =
+				(t_u8)A2HEXDECIMAL(args[1]);
+		} else if (strcmp(args[0], "}") == 0 && cmd_found) {
+			break;
+		}
+	}
+	hexdump("tdls_cs_params", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		printf("TDLS set channel switch parameters successful.\n");
+	} else {
+		printf("ERR:TDLS set channel switch parameters failed.\n");
+	}
+done:
+	fclose(config_file);
+	if (buffer)
+		free(buffer);
+	if (line)
+		free(line);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process tdls_debug
+ *  @param argc   number of arguments
+ *  @param argv   A pointer to arguments array
+ *  @return     MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+process_tdls_debug(int argc, char *argv[])
+{
+	int ret = 0;
+	tdls_debug *param_buf = NULL;
+	t_u16 cmd_len = 0;
+	t_u8 *buffer = NULL;
+	t_u16 action = 0, value = 0;
+
+	/* Check arguments */
+	if (argc < 4) {
+		printf("ERR:Incorrect number of arguments.\n");
+		printf("Syntax: ./mlanconfig mlanX tdls_debug <options>\n");
+		exit(1);
+	}
+
+	cmd_len = sizeof(tdls_debug);
+
+	/* wrong_bss */
+	if (!strcmp(argv[3], "wrong_bss")) {
+		cmd_len += sizeof(t_u16);
+		action = ACTION_TDLS_DEBUG_WRONG_BSS;
+		if (argc < 5) {
+			printf("ERR:Incorrect number of arguments.\n");
+			printf("Syntax: ./mlanconfig mlanX tdls_debug wrong_bss <0/1>\n");
+			exit(1);
+		}
+		value = (t_u16)A2HEXDECIMAL(argv[4]);
+	}
+	/* same link */
+	else if (!strcmp(argv[3], "setup_existing_link")) {
+		cmd_len += sizeof(t_u16);
+		action = ACTION_TDLS_DEBUG_SETUP_SAME_LINK;
+		if (argc < 5) {
+			printf("ERR:Incorrect number of arguments.\n");
+			printf("Syntax: ./mlanconfig mlanX tdls_debug setup_existing_link <0/1>\n");
+			exit(1);
+		}
+		value = (t_u16)A2HEXDECIMAL(argv[4]);
+	}
+	/* fail_setup_confirm */
+	else if (!strcmp(argv[3], "fail_setup_confirm")) {
+		cmd_len += sizeof(t_u16);
+		action = ACTION_TDLS_DEBUG_FAIL_SETUP_CONFIRM;
+		if (argc < 5) {
+			printf("ERR:Incorrect number of arguments.\n");
+			printf("Syntax: ./mlanconfig mlanX tdls_debug fail_setup_confirm <0/1>\n");
+			exit(1);
+		}
+		value = (t_u16)A2HEXDECIMAL(argv[4]);
+	}
+	/* setup prohibited */
+	else if (!strcmp(argv[3], "setup_with_prohibited")) {
+		cmd_len += sizeof(t_u16);
+		action = ACTION_TDLS_DEBUG_SETUP_PROHIBITED;
+		if (argc < 5) {
+			printf("ERR:Incorrect number of arguments.\n");
+			printf("Syntax: ./mlanconfig mlanX tdls_debug setup_with_prohibited <0/1>\n");
+			exit(1);
+		}
+		value = (t_u16)A2HEXDECIMAL(argv[4]);
+	}
+	/* setup higher/lower mac */
+	else if (!strcmp(argv[3], "higher_lower_mac")) {
+		cmd_len += sizeof(t_u16);
+		action = ACTION_TDLS_DEBUG_HIGHER_LOWER_MAC;
+		if (argc < 5) {
+			printf("ERR:Incorrect number of arguments.\n");
+			printf("Syntax: ./mlanconfig mlanX tdls_debug higher_lower_mac <0/1>\n");
+			exit(1);
+		}
+		value = (t_u16)A2HEXDECIMAL(argv[4]);
+	}
+	/* ignore key lifetime expiry */
+	else if (!strcmp(argv[3], "ignore_key_expiry")) {
+		cmd_len += sizeof(t_u16);
+		action = ACTION_TDLS_DEBUG_IGNORE_KEY_EXPIRY;
+		if (argc < 5) {
+			printf("ERR:Incorrect number of arguments.\n");
+			printf("Syntax: ./mlanconfig mlanX tdls_debug ignore_key_expiry <0/1>\n");
+			exit(1);
+		}
+		value = (t_u16)A2HEXDECIMAL(argv[4]);
+	}
+	/* allow weak security */
+	else if (!strcmp(argv[3], "allow_weak_security")) {
+		cmd_len += sizeof(t_u16);
+		action = ACTION_TDLS_DEBUG_ALLOW_WEAK_SECURITY;
+		if (argc < 5) {
+			printf("ERR:Incorrect number of arguments.\n");
+			printf("Syntax: ./mlanconfig mlanX tdls_debug allow_weak_security <0/1>\n");
+			exit(1);
+		}
+		value = (t_u16)A2HEXDECIMAL(argv[4]);
+	}
+	/* stop RX */
+	else if (!strcmp(argv[3], "stop_rx")) {
+		cmd_len += sizeof(t_u16);
+		action = ACTION_TDLS_DEBUG_STOP_RX;
+		if (argc < 5) {
+			printf("ERR:Incorrect number of arguments.\n");
+			printf("Syntax: ./mlanconfig mlanX tdls_debug stop_rx <0/1>\n");
+			exit(1);
+		}
+		value = (t_u16)A2HEXDECIMAL(argv[4]);
+	}
+	/* Immediate return */
+	else if (!strcmp(argv[3], "cs_im_return")) {
+		cmd_len += sizeof(t_u16);
+		action = ACTION_TDLS_DEBUG_CS_RET_IM;
+		if (argc < 5) {
+			printf("ERR:Incorrect number of arguments.\n");
+			printf("Syntax: ./mlanconfig mlanX tdls_debug cs_im_return <0/1>\n");
+			exit(1);
+		}
+		value = (t_u16)A2HEXDECIMAL(argv[4]);
+	} else {
+		printf("ERR:Incorrect command!\n");
+		exit(1);
+	}
+
+	buffer = (t_u8 *)malloc(cmd_len);
+	if (!buffer) {
+		printf("ERR:Cannot allocate memory!\n");
+		return -1;
+	}
+	memset(buffer, 0, cmd_len);
+	param_buf = (tdls_debug *)buffer;
+	param_buf->action = action;
+	memcpy(param_buf->data, &value, sizeof(value));
+
+	hexdump("tdls_debug", buffer, cmd_len, ' ');
+	/* Send collective command */
+	ret = tdls_ioctl((t_u8 *)buffer, cmd_len);
+
+	/* Process response */
+	if (ret == MLAN_STATUS_SUCCESS) {
+		printf("TDLS debug request successful.\n");
+	} else {
+		printf("ERR:TDLS debug request failed.\n");
+	}
+
+	if (buffer)
+		free(buffer);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Entry function for mlanconfig
+ *  @param argc		number of arguments
+ *  @param argv     A pointer to arguments array
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+main(int argc, char *argv[])
+{
+	t_s32 cmd;
+
+	if ((argc == 2) && (strcmp(argv[1], "-v") == 0)) {
+		fprintf(stdout, "Marvell mlanconfig version %s\n",
+			MLANCONFIG_VER);
+		exit(0);
+	}
+	if (argc < 3) {
+		fprintf(stderr, "Invalid number of parameters!\n");
+		display_usage();
+		exit(1);
+	}
+
+	strncpy(dev_name, argv[1], IFNAMSIZ - 1);
+
+	/*
+	 * create a socket
+	 */
+	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		fprintf(stderr, "mlanconfig: Cannot open socket.\n");
+		exit(1);
+	}
+	if (get_range() < 0) {
+		fprintf(stderr, "mlanconfig: Cannot get range.\n");
+		close(sockfd);
+		exit(1);
+	}
+	switch ((cmd = findcommand(NELEMENTS(commands), commands, argv[2]))) {
+	case CMD_HOSTCMD:
+		process_host_cmd(argc, argv);
+		break;
+	case CMD_MEFCFG:
+		process_mef_cfg(argc, argv);
+		break;
+	case CMD_ARPFILTER:
+		process_arpfilter(argc, argv);
+		break;
+	case CMD_CFG_DATA:
+		process_cfg_data(argc, argv);
+		break;
+	case CMD_CMD52RW:
+		process_sdcmd52rw(argc, argv);
+		break;
+	case CMD_CMD53RW:
+		process_sdcmd53rw(argc, argv);
+		break;
+	case CMD_GET_SCAN_RSP:
+		process_getscantable(argc, argv);
+		break;
+	case CMD_SET_USER_SCAN:
+		process_setuserscan(argc, argv);
+		break;
+	case CMD_ADD_TS:
+		process_addts(argc, argv);
+		break;
+	case CMD_DEL_TS:
+		process_delts(argc, argv);
+		break;
+	case CMD_QCONFIG:
+		process_qconfig(argc, argv);
+		break;
+	case CMD_QSTATS:
+		process_qstats(argc, argv);
+		break;
+	case CMD_TS_STATUS:
+		process_wmm_ts_status(argc, argv);
+		break;
+	case CMD_WMM_QSTATUS:
+		process_wmm_qstatus(argc, argv);
+		break;
+	case CMD_REGRW:
+		process_regrdwr(argc, argv);
+		break;
+	case CMD_MEMRW:
+		process_memrdwr(argc, argv);
+		break;
+	case CMD_STA_CUSTOM_IE:
+		process_custom_ie(argc, argv);
+		break;
+	case CMD_STA_MGMT_FRAME_TX:
+		process_mgmt_frame_tx(argc, argv);
+		break;
+	case CMD_TDLS_CONF:
+		process_tdls_config(argc, argv);
+		break;
+	case CMD_TDLS_INFO:
+		process_tdls_setinfo(argc, argv);
+		break;
+	case CMD_TDLS_DISCOVERY:
+		process_tdls_discovery(argc, argv);
+		break;
+	case CMD_TDLS_SETUP:
+		process_tdls_setup(argc, argv);
+		break;
+	case CMD_TDLS_TEARDOWN:
+		process_tdls_teardown(argc, argv);
+		break;
+	case CMD_TDLS_POWERMODE:
+		process_tdls_powermode(argc, argv);
+		break;
+	case CMD_TDLS_LINK_STATUS:
+		process_tdls_link_status(argc, argv);
+		break;
+	case CMD_TDLS_CHANNEL_SWITCH:
+		process_tdls_channel_switch(argc, argv);
+		break;
+	case CMD_TDLS_STOP_CHAN_SWITCH:
+		process_tdls_stop_channel_switch(argc, argv);
+		break;
+	case CMD_TDLS_CS_PARAMS:
+		process_tdls_cs_params(argc, argv);
+		break;
+	case CMD_TDLS_CS_DISABLE:
+		process_tdls_disable_channel_switch(argc, argv);
+		break;
+	case CMD_TDLS_DEBUG:
+		process_tdls_debug(argc, argv);
+		break;
+	default:
+		fprintf(stderr, "Invalid command specified!\n");
+		display_usage();
+		close(sockfd);
+		exit(1);
+	}
+
+	close(sockfd);
+	return MLAN_STATUS_SUCCESS;
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanconfig.h b/wlan_sd8897/mapp/mlanconfig/mlanconfig.h
new file mode 100644
index 0000000..3d1508c
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/mlanconfig.h
@@ -0,0 +1,791 @@
+/** @file  mlanconfig.h
+  *
+  * @brief This file contains definitions for application
+  *
+  * Copyright (C) 2008-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     11/26/2008: initial version
+************************************************************************/
+#ifndef _MLANCONFIG_H_
+#define _MLANCONFIG_H_
+
+/** Include header files */
+#include    <stdio.h>
+#include    <ctype.h>
+#include    <unistd.h>
+#include    <string.h>
+#include    <stdlib.h>
+#include    <sys/socket.h>
+#include    <sys/ioctl.h>
+#include    <errno.h>
+#include    <linux/if.h>
+#include    <linux/wireless.h>
+#include    <sys/types.h>
+#include    <linux/if_ether.h>
+#include    <time.h>
+
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#undef BIG_ENDIAN_SUPPORT
+#endif
+
+/** Type definition: boolean */
+typedef enum { FALSE, TRUE } boolean;
+
+/**
+ * This macro specifies the attribute pack used for structure packing
+ */
+#ifndef __ATTRIB_PACK__
+#define __ATTRIB_PACK__  __attribute__((packed))
+#endif
+
+/** 16 bits byte swap */
+#define swap_byte_16(x) \
+((t_u16)((((t_u16)(x) & 0x00ffU) << 8) | \
+         (((t_u16)(x) & 0xff00U) >> 8)))
+
+/** 32 bits byte swap */
+#define swap_byte_32(x) \
+((t_u32)((((t_u32)(x) & 0x000000ffUL) << 24) | \
+         (((t_u32)(x) & 0x0000ff00UL) <<  8) | \
+         (((t_u32)(x) & 0x00ff0000UL) >>  8) | \
+         (((t_u32)(x) & 0xff000000UL) >> 24)))
+
+/** Convert to correct endian format */
+#ifdef 	BIG_ENDIAN_SUPPORT
+/** CPU to little-endian convert for 16-bit */
+#define 	cpu_to_le16(x)	swap_byte_16(x)
+/** CPU to little-endian convert for 32-bit */
+#define		cpu_to_le32(x)  swap_byte_32(x)
+/** Little-endian to CPU convert for 16-bit */
+#define 	le16_to_cpu(x)	swap_byte_16(x)
+/** Little-endian to CPU convert for 32-bit */
+#define		le32_to_cpu(x)  swap_byte_32(x)
+#else
+/** Do nothing */
+#define		cpu_to_le16(x)	(x)
+/** Do nothing */
+#define		cpu_to_le32(x)  (x)
+/** Do nothing */
+#define 	le16_to_cpu(x)	(x)
+/** Do nothing */
+#define 	le32_to_cpu(x)	(x)
+#endif
+
+/** Character, 1 byte */
+typedef char t_s8;
+/** Unsigned character, 1 byte */
+typedef unsigned char t_u8;
+
+/** Short integer */
+typedef signed short t_s16;
+/** Unsigned short integer */
+typedef unsigned short t_u16;
+
+/** Integer */
+typedef signed int t_s32;
+/** Unsigned integer */
+typedef unsigned int t_u32;
+
+/** Long long integer */
+typedef signed long long t_s64;
+/** Unsigned long long integer */
+typedef unsigned long long t_u64;
+
+/** Void pointer (4-bytes) */
+typedef void t_void;
+
+/** Success */
+#define MLAN_STATUS_SUCCESS         (0)
+/** Failure */
+#define MLAN_STATUS_FAILURE         (-1)
+
+t_s8 *mlan_config_get_line(FILE * fp, t_s8 *s, t_s32 size, int *line);
+int get_priv_ioctl(char *ioctl_name, int *ioctl_val, int *subioctl_val);
+int fparse_for_hex(FILE * fp, t_u8 *dst);
+
+/**
+ * Hex or Decimal to Integer
+ * @param   num string to convert into decimal or hex
+ */
+#define A2HEXDECIMAL(num)  \
+    (strncasecmp("0x", (num), 2)?(unsigned int) strtoll((num),NULL,0):a2hex((num)))
+
+/** Convert character to integer */
+#define CHAR2INT(x) (((x) >= 'A') ? ((x) - 'A' + 10) : ((x) - '0'))
+
+/** Convert TLV header from little endian format to CPU format */
+#define endian_convert_tlv_header_in(x)            \
+    {                                               \
+        (x)->tag = le16_to_cpu((x)->tag);       \
+        (x)->length = le16_to_cpu((x)->length); \
+    }
+
+/** Convert TLV header to little endian format from CPU format */
+#define endian_convert_tlv_header_out(x)            \
+    {                                               \
+        (x)->tag = cpu_to_le16((x)->tag);       \
+        (x)->length = cpu_to_le16((x)->length); \
+    }
+/** Private command ID to pass custom IE list */
+#define CUSTOM_IE_CFG          (SIOCDEVPRIVATE + 13)
+/* TLV Definitions */
+/** TLV header */
+#define TLVHEADER       /** Tag */      \
+                        t_u16 tag;      \
+                        /** Length */   \
+                        t_u16 length
+
+/** Maximum IE buffer length */
+#define MAX_IE_BUFFER_LEN 256
+
+/** TLV: Management IE list */
+#define MRVL_MGMT_IE_LIST_TLV_ID          (PROPRIETARY_TLV_BASE_ID + 0x69)	//0x0169
+
+/** TLV: Max Management IE */
+#define MRVL_MAX_MGMT_IE_TLV_ID           (PROPRIETARY_TLV_BASE_ID + 0xaa)	//0x01aa
+
+/** custom IE info */
+typedef struct _custom_ie_info {
+    /** size of buffer */
+	t_u16 buf_size;
+    /** no of buffers of buf_size */
+	t_u16 buf_count;
+} __ATTRIB_PACK__ custom_ie_info;
+
+/** TLV buffer : custom IE */
+typedef struct _tlvbuf_max_mgmt_ie {
+    /** Header */
+	TLVHEADER;
+    /** No of tuples */
+	t_u16 count;
+    /** custom IE info tuples */
+	custom_ie_info info[0];
+} __ATTRIB_PACK__ tlvbuf_max_mgmt_ie;
+
+/** custom IE */
+typedef struct _custom_ie {
+    /** IE Index */
+	t_u16 ie_index;
+    /** Mgmt Subtype Mask */
+	t_u16 mgmt_subtype_mask;
+    /** IE Length */
+	t_u16 ie_length;
+    /** IE buffer */
+	t_u8 ie_buffer[0];
+} __ATTRIB_PACK__ custom_ie;
+
+/** TLV buffer : custom IE */
+typedef struct _tlvbuf_custom_ie {
+    /** Header */
+	TLVHEADER;
+    /** custom IE data */
+	custom_ie ie_data[0];
+} __ATTRIB_PACK__ tlvbuf_custom_ie;
+
+/** Maximum length of lines in configuration file */
+#define MAX_CONFIG_LINE                 1024
+/** Ethernet address length */
+#define ETH_ALEN                        6
+/** MAC BROADCAST */
+#define MAC_BROADCAST   0x1FF
+/** MAC MULTICAST */
+#define MAC_MULTICAST   0x1FE
+
+/** pkt_header */
+typedef struct _pkt_header {
+    /** pkt_len */
+	t_u32 pkt_len;
+    /** pkt_type */
+	t_u32 TxPktType;
+    /** tx control */
+	t_u32 TxControl;
+} pkt_header;
+
+/** wlan_802_11_header packet from FW with length */
+typedef struct _wlan_mgmt_frame_tx {
+    /** Packet Length */
+	t_u16 frm_len;
+    /** Frame Control */
+	t_u16 frm_ctl;
+    /** Duration ID */
+	t_u16 duration_id;
+    /** Address1 */
+	t_u8 addr1[ETH_ALEN];
+    /** Address2 */
+	t_u8 addr2[ETH_ALEN];
+    /** Address3 */
+	t_u8 addr3[ETH_ALEN];
+    /** Sequence Control */
+	t_u16 seq_ctl;
+    /** Address4 */
+	t_u8 addr4[ETH_ALEN];
+    /** Frame payload */
+	t_u8 payload[0];
+} __ATTRIB_PACK__ wlan_mgmt_frame_tx;
+
+/** frame tx ioctl number */
+#define FRAME_TX_IOCTL               (SIOCDEVPRIVATE + 12)
+
+/** band BG */
+#define BAND_BG        0
+/** band A */
+#define BAND_A         1
+/** secondary channel is above */
+#define SECOND_CHANNEL_ABOVE    0x1
+/** secondary channel is below */
+#define SECOND_CHANNEL_BELOW    0x3
+/** NO PERIODIC SWITCH */
+#define NO_PERIODIC_SWITCH      0
+/** Enable periodic channel switch */
+#define ENABLE_PERIODIC_SWITCH  1
+/** Min channel value for BG band */
+#define MIN_BG_CHANNEL 1
+/** Max channel value for BG band */
+#define MAX_BG_CHANNEL 14
+/** Max channel value for A band */
+#define MIN_A_CHANNEL 36
+/** Max channel value for A band */
+#define MAX_A_CHANNEL 252
+
+/** Host Command ioctl number */
+#define TDLS_IOCTL               (SIOCDEVPRIVATE + 5)
+/** TDLS action definitions */
+/** Action ID for TDLS config */
+#define ACTION_TDLS_CONFIG       0x0000
+/** Action ID for TDLS setinfo request */
+#define ACTION_TDLS_SETINFO      0x0001
+/** Action ID for TDLS Discovery request */
+#define ACTION_TDLS_DISCOVERY    0x0002
+/** Action ID for TDLS setup request */
+#define ACTION_TDLS_SETUP        0x0003
+/** Action ID for TDLS Teardown request */
+#define ACTION_TDLS_TEARDOWN     0x0004
+/** Action ID for TDLS power mode */
+#define ACTION_TDLS_POWER_MODE   0x0005
+/**Action ID for init TDLS Channel Switch*/
+#define ACTION_TDLS_INIT_CHAN_SWITCH     0x06
+/** Action ID for stop TDLS Channel Switch */
+#define ACTION_TDLS_STOP_CHAN_SWITCH     0x07
+/** Action ID for configure CS related parameters */
+#define ACTION_TDLS_CS_PARAMS            0x08
+/** Action ID for TDLS Disable Channel switch */
+#define ACTION_TDLS_CS_DISABLE          0x09
+/** Action ID for TDLS Link status */
+#define ACTION_TDLS_LINK_STATUS  0x000A
+/** Action ID for TDLS CS immediate return */
+#define ACTION_TDLS_DEBUG_CS_RET_IM 0xFFF7
+/** Action ID for TDLS Stop RX */
+#define ACTION_TDLS_DEBUG_STOP_RX 0xFFF8
+/** Action ID for TDLS Allow weak security */
+#define ACTION_TDLS_DEBUG_ALLOW_WEAK_SECURITY 0xFFF9
+/** Action ID for TDLS Ignore key lifetime expiry */
+#define ACTION_TDLS_DEBUG_IGNORE_KEY_EXPIRY 0xFFFA
+/** Action ID for TDLS Higher/Lower mac Test */
+#define ACTION_TDLS_DEBUG_HIGHER_LOWER_MAC 0xFFFB
+/** Action ID for TDLS Prohibited Test */
+#define ACTION_TDLS_DEBUG_SETUP_PROHIBITED 0xFFFC
+/** Action ID for TDLS Existing link Test */
+#define ACTION_TDLS_DEBUG_SETUP_SAME_LINK 0xFFFD
+/** Action ID for TDLS Fail Setup Confirm */
+#define ACTION_TDLS_DEBUG_FAIL_SETUP_CONFIRM 0xFFFE
+/** Action ID for TDLS WRONG BSS Test */
+#define ACTION_TDLS_DEBUG_WRONG_BSS 0xFFFF
+
+/** TLV type : Rates */
+#define TLV_TYPE_RATES                          0x0001
+/** TLV type : Domain */
+#define TLV_TYPE_DOMAIN                         0x0007
+/** TLV type : Supported channels */
+#define TLV_TYPE_SUPPORTED_CHANNELS             0x0024
+/** TLV type : HT Capabilities */
+#define TLV_TYPE_HT_CAP                         0x002d
+/** TLV type : Qos Info */
+#define TLV_TYPE_QOSINFO                        0x002e
+/** TLV type : RSN IE */
+#define TLV_TYPE_RSN_IE                         0x0030
+/** TLV type : extended supported rates */
+#define TLV_EXTENDED_SUPPORTED_RATES            0x0032
+/** TLV type :  timeout interval    */
+#define TLV_TIMEOUT_INTERVAL                    0x0038
+/** TLV type : Regulatory classes */
+#define TLV_TYPE_REGULATORY_CLASSES             0x003b
+/** TLV type : HT Information */
+#define TLV_TYPE_HT_INFO                        0x003d
+/** TLV type : 20/40 BSS Coexistence */
+#define TLV_TYPE_2040BSS_COEXISTENCE            0x0048
+/** TLv Type : Link identifier */
+#define TLV_LINK_IDENTIFIER                     0x0065
+/** TLV type : Extended capabilities */
+#define TLV_TYPE_EXTCAP                         0x007f
+
+/** TLV type : VHT capabilities */
+#define TLV_TYPE_VHT_CAP		        0x00BF
+/** TLV type : VHT operations */
+#define TLV_TYPE_VHT_OPER	                0x00C0
+/** Length of Basic MCS MAP */
+#define VHT_MCS_MAP_LEN    			2
+
+/** Country code length */
+#define COUNTRY_CODE_LEN                        3
+/** Length of Group Cipher Suite */
+#define GROUP_CIPHER_SUITE_LEN     4
+/** Length of Pairwise Cipher Suite */
+#define PAIRWISE_CIPHER_SUITE_LEN  4
+/** Length of AKM Suite */
+#define AKM_SUITE_LEN          4
+/** PMKID length */
+#define PMKID_LEN              16
+/** Maximum number of pairwise_cipher_suite */
+#define MAX_PAIRWISE_CIPHER_SUITE_COUNT    2
+/** Maximum number of AKM suite */
+#define MAX_AKM_SUITE_COUNT    2
+/** Maximum number of PMKID list count */
+#define MAX_PMKID_COUNT    2
+/** Length of MCS set */
+#define MCS_SET_LEN    16
+/** Version in RSN IE */
+#define VERSION_RSN_IE    0x0001
+
+/** tdls setinfo */
+typedef struct _tdls_setinfo {
+    /** Action */
+	t_u16 action;
+    /** (TLV + capInfo) length */
+	t_u16 tlv_len;
+    /** Capability Info */
+	t_u16 cap_info;
+    /** tdls info */
+	t_u8 tlv_buffer[0];
+} __ATTRIB_PACK__ tdls_setinfo;
+
+/** tdls discovery */
+typedef struct _tdls_discovery {
+    /** Action */
+	t_u16 action;
+    /** Peer MAC address */
+	t_u8 peer_mac[ETH_ALEN];
+} __ATTRIB_PACK__ tdls_discovery;
+
+/** tdls link status */
+typedef struct _tdls_links_status {
+    /** Action */
+	t_u16 action;
+} __ATTRIB_PACK__ tdls_link_status;
+
+/** tdls discovery response */
+typedef struct _tdls_discovery_resp {
+    /** Action */
+	t_u16 action;
+    /** payload length */
+	t_u16 payload_len;
+    /** peer mac Address */
+	t_u8 peer_mac[ETH_ALEN];
+    /** RSSI */
+	signed char rssi;
+    /** Cap Info */
+	t_u16 cap_info;
+    /** TLV buffer */
+	t_u8 tlv_buffer[0];
+} __ATTRIB_PACK__ tdls_discovery_resp;
+
+/** tdls each link rate information */
+typedef struct _tdls_link_rate_info {
+    /** Tx Data Rate */
+	t_u8 tx_data_rate;
+    /** Tx Rate HT info*/
+	t_u8 tx_rate_htinfo;
+} __ATTRIB_PACK__ tdls_link_rate_info;
+
+/** tdls each link status */
+typedef struct _tdls_each_link_status {
+    /** peer mac Address */
+	t_u8 peer_mac[ETH_ALEN];
+    /** Link Flags */
+	t_u8 link_flags;
+    /** Traffic Status */
+	t_u8 traffic_status;
+    /** Tx Failure Count */
+	t_u8 tx_fail_count;
+    /** Channel Number */
+	t_u32 active_channel;
+    /** Last Data RSSI in dBm */
+	t_s16 data_rssi_last;
+    /** Last Data NF in dBm */
+	t_s16 data_nf_last;
+    /** AVG DATA RSSI in dBm */
+	t_s16 data_rssi_avg;
+    /** AVG DATA NF in dBm */
+	t_s16 data_nf_avg;
+	union {
+	/** tdls rate info */
+		tdls_link_rate_info rate_info;
+	/** final rate value*/
+		t_u16 final_data_rate;
+	} u;
+    /** Security Method */
+	t_u8 security_method;
+    /** Key Lifetime */
+	t_u32 key_lifetime;
+    /** Key Length */
+	t_u8 key_length;
+    /** actual key */
+	t_u8 key[0];
+} __ATTRIB_PACK__ tdls_each_link_status;
+
+/** tdls link status response */
+typedef struct _tdls_link_status_resp {
+    /** Action */
+	t_u16 action;
+    /** payload length */
+	t_u16 payload_len;
+    /** number of links */
+	t_u8 active_links;
+    /** structure for link status */
+	tdls_each_link_status link_stats[1];
+} __ATTRIB_PACK__ tdls_link_status_resp;
+
+/** tdls setup */
+typedef struct _tdls_setup {
+    /** Action */
+	t_u16 action;
+    /** Peer MAC address */
+	t_u8 peer_mac[ETH_ALEN];
+    /** Time to wait for response from peer*/
+	t_u32 wait_time;
+    /** Key Life Time */
+	t_u32 key_life_time;
+} __ATTRIB_PACK__ tdls_setup;
+
+/** tdls tear down */
+typedef struct _tdls_tear_down {
+    /** Action */
+	t_u16 action;
+    /** Peer MAC address */
+	t_u8 peer_mac[ETH_ALEN];
+    /** Reason code */
+	t_u16 reason_code;
+} __ATTRIB_PACK__ tdls_teardown;
+
+/** tdls power mode */
+typedef struct _tdls_power_mode {
+    /** Action */
+	t_u16 action;
+    /** Peer MAC address */
+	t_u8 peer_mac[ETH_ALEN];
+    /** Power mode */
+	t_u16 power_mode;
+} __ATTRIB_PACK__ tdls_powermode;
+
+/** tdls channel switch info */
+typedef struct _tdls_channel_switch {
+    /** Action */
+	t_u16 action;
+    /** peer mac Address */
+	t_u8 peer_mac[ETH_ALEN];
+    /** Channel Switch primary channel no */
+	t_u8 primary_channel;
+    /** Channel Switch secondary channel offset */
+	t_u8 secondary_channel_offset;
+    /** Channel Switch Band */
+	t_u8 band;
+    /** Channel Switch time*/
+	t_u16 switch_time;
+    /** Channel Switch timeout*/
+	t_u16 switch_timeout;
+    /** Channel Regulatory class*/
+	t_u8 regulatory_class;
+    /** Channel Switch periodicity*/
+	t_u8 periodicity;
+} __ATTRIB_PACK__ tdls_channel_switch;
+
+/** tdls stop channel switch */
+typedef struct _tdls_stop_chan_switch {
+    /** Action */
+	t_u16 action;
+    /** Peer MAC address */
+	t_u8 peer_mac[ETH_ALEN];
+} __ATTRIB_PACK__ tdls_stop_chan_switch;
+
+/** tdls disable channel switch */
+typedef struct _tdls_disable_cs {
+    /** Action */
+	t_u16 action;
+    /** Data*/
+	t_u16 data;
+} __ATTRIB_PACK__ tdls_disable_cs, tdls_config;
+
+/** tdls channel switch parameters */
+typedef struct _tdls_cs_params {
+    /** Action */
+	t_u16 action;
+    /** unit time, multiples of 10ms */
+	t_u8 unit_time;
+    /** threshold for other link */
+	t_u8 threshold_otherlink;
+    /** threshold for direct link */
+	t_u8 threshold_directlink;
+} __ATTRIB_PACK__ tdls_cs_params;
+
+/** tdls debug */
+typedef struct _tdls_debug {
+    /** Action */
+	t_u16 action;
+    /** Data */
+	t_u8 data[0];
+} __ATTRIB_PACK__ tdls_debug;
+
+/** TLV header */
+#define TLVHEADER       /** Tag */      \
+                        t_u16 tag;      \
+                        /** Length */   \
+                        t_u16 length
+
+/** Length of TLV header */
+#define TLVHEADER_LEN  4
+
+/** Data structure for subband set */
+typedef struct _IEEEtypes_SubbandSet_t {
+    /** First channel */
+	t_u8 first_chan;
+    /** Number of channels */
+	t_u8 no_of_chan;
+    /** Maximum Tx power */
+	t_u8 max_tx_pwr;
+} __ATTRIB_PACK__ IEEEtypes_SubbandSet_t, *pIEEEtypes_SubbandSet_t;
+
+/** tlvbuf_DomainParamSet_t */
+typedef struct _tlvbuf_DomainParamSet {
+    /** Header */
+	TLVHEADER;
+    /** Country code */
+	t_u8 country_code[COUNTRY_CODE_LEN];
+    /** Set of subbands */
+	IEEEtypes_SubbandSet_t sub_band[0];
+} __ATTRIB_PACK__ tlvbuf_DomainParamSet_t;
+
+/** Data structure of WMM QoS information */
+typedef struct _IEEEtypes_WmmQosInfo_t {
+#ifdef BIG_ENDIAN_SUPPORT
+    /** QoS UAPSD */
+	t_u8 qos_uapsd:1;
+    /** Reserved */
+	t_u8 reserved:3;
+    /** Parameter set count */
+	t_u8 para_set_count:4;
+#else
+    /** Parameter set count */
+	t_u8 para_set_count:4;
+    /** Reserved */
+	t_u8 reserved:3;
+    /** QoS UAPSD */
+	t_u8 qos_uapsd:1;
+#endif				/* BIG_ENDIAN_SUPPORT */
+} __ATTRIB_PACK__ IEEEtypes_WmmQosInfo_t;
+
+/** Qos Info TLV */
+typedef struct _tlvbuf_QosInfo_t {
+    /** Header */
+	TLVHEADER;
+    /** QosInfo */
+	union {
+	/** QosInfo bitfield */
+		IEEEtypes_WmmQosInfo_t qos_info;
+	/** QosInfo byte */
+		t_u8 qos_info_byte;
+	} u;
+} __ATTRIB_PACK__ tlvbuf_QosInfo_t;
+
+/** HT Capabilities Data */
+typedef struct _HTCap_t {
+    /** HT Capabilities Info field */
+	t_u16 ht_cap_info;
+    /** A-MPDU Parameters field */
+	t_u8 ampdu_param;
+    /** Supported MCS Set field */
+	t_u8 supported_mcs_set[16];
+    /** HT Extended Capabilities field */
+	t_u16 ht_ext_cap;
+    /** Transmit Beamforming Capabilities field */
+	t_u32 tx_bf_cap;
+    /** Antenna Selection Capability field */
+	t_u8 asel;
+} __ATTRIB_PACK__ HTCap_t, *pHTCap_t;
+
+/** HT Information Data */
+typedef struct _HTInfo_t {
+    /** Primary channel */
+	t_u8 pri_chan;
+    /** Field 2 */
+	t_u8 field2;
+    /** Field 3 */
+	t_u16 field3;
+    /** Field 4 */
+	t_u16 field4;
+    /** Bitmap indicating MCSs supported by all HT STAs in the BSS */
+	t_u8 basic_mcs_set[16];
+} __ATTRIB_PACK__ HTInfo_t, *pHTInfo_t;
+
+/** 20/40 BSS Coexistence Data */
+typedef struct _BSSCo2040_t {
+    /** 20/40 BSS Coexistence value */
+	t_u8 bss_co_2040_value;
+} __ATTRIB_PACK__ BSSCo2040_t, *pBSSCo2040_t;
+
+/** HT Capabilities element */
+typedef struct _tlvbuf_HTCap_t {
+    /** Header */
+	TLVHEADER;
+    /** HTCap struct */
+	HTCap_t ht_cap;
+} __ATTRIB_PACK__ tlvbuf_HTCap_t;
+
+/** HT Information element */
+typedef struct _tlvbuf_HTInfo_t {
+    /** Header */
+	TLVHEADER;
+
+    /** HTInfo struct */
+	HTInfo_t ht_info;
+} __ATTRIB_PACK__ tlvbuf_HTInfo_t;
+
+/** VHT MCS rate set field, refer to 802.11ac */
+typedef struct _VHT_MCS_set {
+	t_u16 rx_mcs_map;
+	t_u16 rx_max_rate;	/* bit 29-31 reserved */
+	t_u16 tx_mcs_map;
+	t_u16 tx_max_rate;	/* bit 61-63 reserved */
+} __ATTRIB_PACK__ VHT_MCS_set_t, *pVHT_MCS_set_t;
+
+typedef struct _VHT_capa {
+	t_u32 vht_cap_info;
+	VHT_MCS_set_t mcs_sets;
+} __ATTRIB_PACK__ VHT_capa_t, *pVHT_capa_t;
+
+/** VHT Capabilities IE */
+typedef struct _IEEEtypes_VHTCap_t {
+    /** Header */
+	TLVHEADER;
+    /** VHTInfo struct */
+	VHT_capa_t vht_cap;
+} __ATTRIB_PACK__ tlvbuf_VHTCap_t, *ptlvbuf_VHTCap_t;
+
+/** VHT Operations IE */
+typedef struct _IEEEtypes_VHTOprat_t {
+    /** Header */
+	TLVHEADER;
+    /** VHTOpra struct */
+	t_u8 chan_width;
+	t_u8 chan_cf1;
+	t_u8 chan_cf2;
+    /** Basic MCS set map, each 2 bits stands for a Nss */
+	t_u8 basic_mcs_map[VHT_MCS_MAP_LEN];
+} __ATTRIB_PACK__ tlvbuf_VHTOpra_t, *ptlvbuf_VHTOpra_t;
+
+/** 20/40 BSS Coexistence element */
+typedef struct _tlvbuf_2040BSSCo_t {
+    /** Header */
+	TLVHEADER;
+
+    /** BSSCo2040_t struct */
+	BSSCo2040_t bss_co_2040;
+} __ATTRIB_PACK__ tlvbuf_2040BSSCo_t;
+
+/** Extended Capabilities element */
+typedef struct _tlvbuf_ExtCap_t {
+    /** Header */
+	TLVHEADER;
+    /** ExtCap_t struct */
+	t_u8 ext_cap[0];
+} __ATTRIB_PACK__ tlvbuf_ExtCap_t;
+
+/** tlvbuf_RatesParamSet_t */
+typedef struct _tlvbuf_RatesParamSet_t {
+    /** Header */
+	TLVHEADER;
+    /** Rates */
+	t_u8 rates[0];
+} __ATTRIB_PACK__ tlvbuf_RatesParamSet_t;
+
+/*  IEEE Supported Channel sub-band description (7.3.2.19) */
+/*  Sub-band description used in the supported channels element. */
+typedef struct _IEEEtypes_SupportChan_Subband_t {
+	t_u8 start_chan;/**< Starting channel in the subband */
+	t_u8 num_chans;	/**< Number of channels in the subband */
+
+} __ATTRIB_PACK__ IEEEtypes_SupportChan_Subband_t;
+
+/*  IEEE Supported Channel element (7.3.2.19) */
+/**
+ *  Sent in association requests. Details the sub-bands and number
+ *    of channels supported in each subband
+ */
+typedef struct _tlvbuf_SupportedChannels_t {
+    /** Header */
+	TLVHEADER;
+		 /**< IEEE Element ID = 36 */
+    /** Configured sub-bands information in the element */
+	IEEEtypes_SupportChan_Subband_t subband[0];
+} __ATTRIB_PACK__ tlvbuf_SupportedChannels_t;
+
+/*  IEEE Supported Regulatory Classes description (7.3.2.54) */
+typedef struct _IEEEtypes_RegulatoryClass_t {
+    /** current regulatory class */
+	t_u8 cur_regulatory_class;
+    /** supported regulatory class list */
+	t_u8 regulatory_classes_list[0];
+
+} __ATTRIB_PACK__ IEEEtypes_RegulatoryClass_t;
+
+/*  IEEE Supported Regulatory Classes TLV (7.3.2.54) */
+typedef struct _tlvbuf_RegulatoryClass_t {
+    /** Header */
+	TLVHEADER;
+		 /**< IEEE Element ID = 59 */
+    /** supported regulatory class */
+	IEEEtypes_RegulatoryClass_t regulatory_class;
+} __ATTRIB_PACK__ tlvbuf_RegulatoryClass_t;
+
+/** tlvbuf_RsnParamSet_t */
+typedef struct _tlvbuf_RsnParamSet_t {
+    /** Header */
+	TLVHEADER;
+    /** Version */
+	t_u16 version;
+    /** Group Cipher Suite */
+	t_u8 group_cipher_suite[4];
+    /** Pairwise Cipher Suite count */
+	t_u16 pairwise_cipher_count;
+    /** Pairwise Cipher Suite */
+	t_u8 pairwise_cipher_suite[0];
+    /** AKM Suite Sount */
+	t_u16 akm_suite_count;
+    /** AKM Suite */
+	t_u8 akm_suite[0];
+    /** RSN Capability */
+	t_u16 rsn_capability;
+    /** PMKID Count */
+	t_u16 pmkid_count;
+    /** PMKID list */
+	t_u8 pmkid_list[0];
+} __ATTRIB_PACK__ tlvbuf_RsnParamSet_t;
+
+extern t_s32 sockfd;  /**< socket */
+extern t_s8 dev_name[IFNAMSIZ + 1];   /**< device name */
+
+#endif /* _MLANCONFIG_H_ */
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.c b/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.c
new file mode 100644
index 0000000..7ff73bc
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.c
@@ -0,0 +1,920 @@
+/** @file  mlanhostcmd.c
+  *
+  * @brief This file contains mlanconfig helper functions
+  *
+  * Copyright (C) 2008-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     11/26/2008: initial version
+************************************************************************/
+
+#include	"mlanconfig.h"
+#include	"mlanhostcmd.h"
+
+#ifndef MIN
+/** Find minimum value */
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
+
+/********************************************************
+		Local Variables
+********************************************************/
+
+/********************************************************
+		Global Variables
+********************************************************/
+
+/********************************************************
+		Local Functions
+********************************************************/
+/**
+ *  @brief get hostcmd data
+ *
+ *  @param ln			A pointer to line number
+ *  @param buf			A pointer to hostcmd data
+ *  @param size			A pointer to the return size of hostcmd buffer
+ *  @return      		MLAN_STATUS_SUCCESS
+ */
+static int
+mlan_get_hostcmd_data(FILE * fp, int *ln, t_u8 *buf, t_u16 *size)
+{
+	t_s32 errors = 0, i;
+	t_s8 line[256], *pos, *pos1, *pos2, *pos3;
+	t_u16 len;
+
+	while ((pos = mlan_config_get_line(fp, line, sizeof(line), ln))) {
+		(*ln)++;
+		if (strcmp(pos, "}") == 0) {
+			break;
+		}
+
+		pos1 = strchr(pos, ':');
+		if (pos1 == NULL) {
+			printf("Line %d: Invalid hostcmd line '%s'\n", *ln,
+			       pos);
+			errors++;
+			continue;
+		}
+		*pos1++ = '\0';
+
+		pos2 = strchr(pos1, '=');
+		if (pos2 == NULL) {
+			printf("Line %d: Invalid hostcmd line '%s'\n", *ln,
+			       pos);
+			errors++;
+			continue;
+		}
+		*pos2++ = '\0';
+
+		len = a2hex_or_atoi(pos1);
+		if (len < 1 || len > MRVDRV_SIZE_OF_CMD_BUFFER) {
+			printf("Line %d: Invalid hostcmd line '%s'\n", *ln,
+			       pos);
+			errors++;
+			continue;
+		}
+
+		*size += len;
+
+		if (*pos2 == '"') {
+			pos2++;
+			if ((pos3 = strchr(pos2, '"')) == NULL) {
+				printf("Line %d: invalid quotation '%s'\n", *ln,
+				       pos);
+				errors++;
+				continue;
+			}
+			*pos3 = '\0';
+			memset(buf, 0, len);
+			memmove(buf, pos2, MIN(strlen(pos2), len));
+			buf += len;
+		} else if (*pos2 == '\'') {
+			pos2++;
+			if ((pos3 = strchr(pos2, '\'')) == NULL) {
+				printf("Line %d: invalid quotation '%s'\n", *ln,
+				       pos);
+				errors++;
+				continue;
+			}
+			*pos3 = ',';
+			for (i = 0; i < len; i++) {
+				if ((pos3 = strchr(pos2, ',')) != NULL) {
+					*pos3 = '\0';
+					*buf++ = (t_u8)a2hex_or_atoi(pos2);
+					pos2 = pos3 + 1;
+				} else
+					*buf++ = 0;
+			}
+		} else if (*pos2 == '{') {
+			t_u16 tlvlen = 0, tmp_tlvlen;
+			mlan_get_hostcmd_data(fp, ln, buf + len, &tlvlen);
+			tmp_tlvlen = tlvlen;
+			while (len--) {
+				*buf++ = (t_u8)(tmp_tlvlen & 0xff);
+				tmp_tlvlen >>= 8;
+			}
+			*size += tlvlen;
+			buf += tlvlen;
+		} else {
+			t_u32 value = a2hex_or_atoi(pos2);
+			while (len--) {
+				*buf++ = (t_u8)(value & 0xff);
+				value >>= 8;
+			}
+		}
+	}
+	return MLAN_STATUS_SUCCESS;
+}
+
+/********************************************************
+		Global Functions
+********************************************************/
+/**
+ *  @brief convert char to hex integer
+ *
+ *  @param chr 		char to convert
+ *  @return      	hex integer or 0
+ */
+int
+hexval(t_s32 chr)
+{
+	if (chr >= '0' && chr <= '9')
+		return chr - '0';
+	if (chr >= 'A' && chr <= 'F')
+		return chr - 'A' + 10;
+	if (chr >= 'a' && chr <= 'f')
+		return chr - 'a' + 10;
+
+	return 0;
+}
+
+/**
+ *  @brief Hump hex data
+ *
+ *  @param prompt	A pointer prompt buffer
+ *  @param p		A pointer to data buffer
+ *  @param len		the len of data buffer
+ *  @param delim	delim char
+ *  @return            	hex integer
+ */
+t_void
+hexdump(t_s8 *prompt, t_void *p, t_s32 len, t_s8 delim)
+{
+	t_s32 i;
+	t_u8 *s = p;
+
+	if (prompt) {
+		printf("%s: len=%d\n", prompt, (int)len);
+	}
+	for (i = 0; i < len; i++) {
+		if (i != len - 1)
+			printf("%02x%c", *s++, delim);
+		else
+			printf("%02x\n", *s);
+		if ((i + 1) % 16 == 0)
+			printf("\n");
+	}
+	printf("\n");
+}
+
+/**
+ *  @brief convert char to hex integer
+ *
+ *  @param chr		char
+ *  @return            	hex integer
+ */
+t_u8
+hexc2bin(t_s8 chr)
+{
+	if (chr >= '0' && chr <= '9')
+		chr -= '0';
+	else if (chr >= 'A' && chr <= 'F')
+		chr -= ('A' - 10);
+	else if (chr >= 'a' && chr <= 'f')
+		chr -= ('a' - 10);
+
+	return chr;
+}
+
+/**
+ *  @brief convert string to hex integer
+ *
+ *  @param s		A pointer string buffer
+ *  @return            	hex integer
+ */
+t_u32
+a2hex(t_s8 *s)
+{
+	t_u32 val = 0;
+
+	if (!strncasecmp("0x", s, 2)) {
+		s += 2;
+	}
+
+	while (*s && isxdigit(*s)) {
+		val = (val << 4) + hexc2bin(*s++);
+	}
+
+	return val;
+}
+
+/*
+ *  @brief convert String to integer
+ *
+ *  @param value	A pointer to string
+ *  @return             integer
+ */
+t_u32
+a2hex_or_atoi(t_s8 *value)
+{
+	if (value[0] == '0' && (value[1] == 'X' || value[1] == 'x')) {
+		return a2hex(value + 2);
+	} else if (isdigit(*value)) {
+		return atoi(value);
+	} else {
+		return *value;
+	}
+}
+
+/**
+ *  @brief convert string to hex
+ *
+ *  @param ptr		A pointer to data buffer
+ *  @param chr 		A pointer to return integer
+ *  @return      	A pointer to next data field
+ */
+t_s8 *
+convert2hex(t_s8 *ptr, t_u8 *chr)
+{
+	t_u8 val;
+
+	for (val = 0; *ptr && isxdigit(*ptr); ptr++) {
+		val = (val * 16) + hexval(*ptr);
+	}
+
+	*chr = val;
+
+	return ptr;
+}
+
+/**
+ *  @brief Check the Hex String
+ *  @param s  A pointer to the string
+ *  @return   MLAN_STATUS_SUCCESS --HexString, MLAN_STATUS_FAILURE --not HexString
+ */
+int
+ishexstring(t_s8 *s)
+{
+	int ret = MLAN_STATUS_FAILURE;
+	t_s32 tmp;
+
+	if (!strncasecmp("0x", s, 2)) {
+		s += 2;
+	}
+	while (*s) {
+		tmp = toupper(*s);
+		if (((tmp >= 'A') && (tmp <= 'F')) ||
+		    ((tmp >= '0') && (tmp <= '9'))) {
+			ret = MLAN_STATUS_SUCCESS;
+		} else {
+			ret = MLAN_STATUS_FAILURE;
+			break;
+		}
+		s++;
+	}
+
+	return ret;
+}
+
+/**
+ *  @brief Convert String to Integer
+ *  @param buf      A pointer to the string
+ *  @return         Integer
+ */
+int
+atoval(t_s8 *buf)
+{
+	if (!strncasecmp(buf, "0x", 2))
+		return a2hex(buf + 2);
+	else if (!ishexstring(buf))
+		return a2hex(buf);
+	else
+		return atoi(buf);
+}
+
+/**
+ *  @brief Prepare host-command buffer
+ *  @param fp		File handler
+ *  @param cmd_name	Command name
+ *  @param buf		A pointer to comand buffer
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+prepare_host_cmd_buffer(FILE * fp, char *cmd_name, t_u8 *buf)
+{
+	t_s8 line[256], cmdname[256], *pos, cmdcode[10];
+	HostCmd_DS_GEN *hostcmd;
+	int ln = 0;
+	int cmdname_found = 0, cmdcode_found = 0;
+
+	memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+	hostcmd = (HostCmd_DS_GEN *)buf;
+	hostcmd->command = 0xffff;
+
+	snprintf(cmdname, sizeof(cmdname), "%s={", cmd_name);
+	cmdname_found = 0;
+	while ((pos = mlan_config_get_line(fp, line, sizeof(line), &ln))) {
+		if (strcmp(pos, cmdname) == 0) {
+			cmdname_found = 1;
+			snprintf(cmdcode, sizeof(cmdcode), "CmdCode=");
+			cmdcode_found = 0;
+			while ((pos =
+				mlan_config_get_line(fp, line, sizeof(line),
+						     &ln))) {
+				if (strncmp(pos, cmdcode, strlen(cmdcode)) == 0) {
+					cmdcode_found = 1;
+					hostcmd->command =
+						a2hex_or_atoi(pos +
+							      strlen(cmdcode));
+					hostcmd->size = S_DS_GEN;
+					mlan_get_hostcmd_data(fp, &ln,
+							      buf +
+							      hostcmd->size,
+							      &hostcmd->size);
+					break;
+				}
+			}
+			if (!cmdcode_found) {
+				fprintf(stderr,
+					"mlanconfig: CmdCode not found in conf file\n");
+				return MLAN_STATUS_FAILURE;
+			}
+			break;
+		}
+	}
+
+	if (!cmdname_found) {
+		fprintf(stderr,
+			"mlanconfig: cmdname '%s' is not found in conf file\n",
+			cmd_name);
+		return MLAN_STATUS_FAILURE;
+	}
+
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+	hostcmd->command = cpu_to_le16(hostcmd->command);
+	hostcmd->size = cpu_to_le16(hostcmd->size);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/** Config data header length */
+#define CFG_DATA_HEADER_LEN 6
+
+/**
+ *  @brief Prepare cfg-data buffer
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *  @param fp       File handler
+ *  @param buf      A pointer to comand buffer
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+prepare_cfg_data_buffer(int argc, char *argv[], FILE * fp, t_u8 *buf)
+{
+	int ln = 0, type;
+	HostCmd_DS_GEN *hostcmd;
+	HostCmd_DS_802_11_CFG_DATA *pcfg_data;
+
+	memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+	hostcmd = (HostCmd_DS_GEN *)buf;
+	hostcmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA);
+	pcfg_data = (HostCmd_DS_802_11_CFG_DATA *)(buf + S_DS_GEN);
+	pcfg_data->action =
+		(argc == 4) ? HostCmd_ACT_GEN_GET : HostCmd_ACT_GEN_SET;
+	type = atoi(argv[3]);
+	if ((type < 1) || (type > 2)) {
+		fprintf(stderr, "mlanconfig: Invalid register type\n");
+		return MLAN_STATUS_FAILURE;
+	} else {
+		pcfg_data->type = type;
+	}
+	if (argc == 5) {
+		ln = fparse_for_hex(fp, pcfg_data->data);
+	}
+	pcfg_data->data_len = ln;
+	hostcmd->size =
+		cpu_to_le16(pcfg_data->data_len + S_DS_GEN +
+			    CFG_DATA_HEADER_LEN);
+	pcfg_data->data_len = cpu_to_le16(pcfg_data->data_len);
+	pcfg_data->type = cpu_to_le16(pcfg_data->type);
+	pcfg_data->action = cpu_to_le16(pcfg_data->action);
+
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process host_cmd response
+ *  @param buf		A pointer to the response buffer
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_host_cmd_resp(t_u8 *buf)
+{
+	HostCmd_DS_GEN *hostcmd = (HostCmd_DS_GEN *)buf;
+	int ret = MLAN_STATUS_SUCCESS;
+
+	hostcmd->command = le16_to_cpu(hostcmd->command);
+	hostcmd->size = le16_to_cpu(hostcmd->size);
+	hostcmd->seq_num = le16_to_cpu(hostcmd->seq_num);
+	hostcmd->result = le16_to_cpu(hostcmd->result);
+
+	hostcmd->command &= ~HostCmd_RET_BIT;
+	if (!hostcmd->result) {
+		switch (hostcmd->command) {
+		case HostCmd_CMD_CFG_DATA:
+			{
+				HostCmd_DS_802_11_CFG_DATA *pstcfgData =
+					(HostCmd_DS_802_11_CFG_DATA *)(buf +
+								       S_DS_GEN);
+				pstcfgData->data_len =
+					le16_to_cpu(pstcfgData->data_len);
+				pstcfgData->action =
+					le16_to_cpu(pstcfgData->action);
+
+				if (pstcfgData->action == HostCmd_ACT_GEN_GET) {
+					hexdump("cfgdata", pstcfgData->data,
+						pstcfgData->data_len, ' ');
+				}
+				break;
+			}
+		case HostCmd_CMD_802_11_TPC_ADAPT_REQ:
+			{
+				mlan_ioctl_11h_tpc_resp *tpcIoctlResp =
+					(mlan_ioctl_11h_tpc_resp *)(buf +
+								    S_DS_GEN);
+				if (tpcIoctlResp->status_code == 0) {
+					printf("tpcrequest:  txPower(%d), linkMargin(%d), rssi(%d)\n", tpcIoctlResp->tx_power, tpcIoctlResp->link_margin, tpcIoctlResp->rssi);
+				} else {
+					printf("tpcrequest:  failure, status = %d\n", tpcIoctlResp->status_code);
+				}
+				break;
+			}
+		case HostCmd_CMD_802_11_CRYPTO:
+			{
+				t_u16 alg =
+					le16_to_cpu((t_u16)
+						    *(buf + S_DS_GEN +
+						      sizeof(t_u16)));
+				if (alg != CIPHER_TEST_AES_CCM) {
+					HostCmd_DS_802_11_CRYPTO *cmd =
+						(HostCmd_DS_802_11_CRYPTO *)(buf
+									     +
+									     S_DS_GEN);
+					cmd->encdec = le16_to_cpu(cmd->encdec);
+					cmd->algorithm =
+						le16_to_cpu(cmd->algorithm);
+					cmd->key_IV_length =
+						le16_to_cpu(cmd->key_IV_length);
+					cmd->key_length =
+						le16_to_cpu(cmd->key_length);
+					cmd->data.header.type =
+						le16_to_cpu(cmd->data.header.
+							    type);
+					cmd->data.header.len =
+						le16_to_cpu(cmd->data.header.
+							    len);
+
+					printf("crypto_result: encdec=%d algorithm=%d,KeyIVLen=%d," " KeyLen=%d,dataLen=%d\n", cmd->encdec, cmd->algorithm, cmd->key_IV_length, cmd->key_length, cmd->data.header.len);
+					hexdump("KeyIV", cmd->keyIV,
+						cmd->key_IV_length, ' ');
+					hexdump("Key", cmd->key,
+						cmd->key_length, ' ');
+					hexdump("Data", cmd->data.data,
+						cmd->data.header.len, ' ');
+				} else {
+					HostCmd_DS_802_11_CRYPTO_AES_CCM
+						*cmd_aes_ccm =
+						(HostCmd_DS_802_11_CRYPTO_AES_CCM
+						 *)(buf + S_DS_GEN);
+
+					cmd_aes_ccm->encdec
+						=
+						le16_to_cpu(cmd_aes_ccm->
+							    encdec);
+					cmd_aes_ccm->algorithm =
+						le16_to_cpu(cmd_aes_ccm->
+							    algorithm);
+					cmd_aes_ccm->key_length =
+						le16_to_cpu(cmd_aes_ccm->
+							    key_length);
+					cmd_aes_ccm->nonce_length =
+						le16_to_cpu(cmd_aes_ccm->
+							    nonce_length);
+					cmd_aes_ccm->AAD_length =
+						le16_to_cpu(cmd_aes_ccm->
+							    AAD_length);
+					cmd_aes_ccm->data.header.type =
+						le16_to_cpu(cmd_aes_ccm->data.
+							    header.type);
+					cmd_aes_ccm->data.header.len =
+						le16_to_cpu(cmd_aes_ccm->data.
+							    header.len);
+
+					printf("crypto_result: encdec=%d algorithm=%d, KeyLen=%d," " NonceLen=%d,AADLen=%d,dataLen=%d\n", cmd_aes_ccm->encdec, cmd_aes_ccm->algorithm, cmd_aes_ccm->key_length, cmd_aes_ccm->nonce_length, cmd_aes_ccm->AAD_length, cmd_aes_ccm->data.header.len);
+
+					hexdump("Key", cmd_aes_ccm->key,
+						cmd_aes_ccm->key_length, ' ');
+					hexdump("Nonce", cmd_aes_ccm->nonce,
+						cmd_aes_ccm->nonce_length, ' ');
+					hexdump("AAD", cmd_aes_ccm->AAD,
+						cmd_aes_ccm->AAD_length, ' ');
+					hexdump("Data", cmd_aes_ccm->data.data,
+						cmd_aes_ccm->data.header.len,
+						' ');
+				}
+				break;
+			}
+		case HostCmd_CMD_802_11_AUTO_TX:
+			{
+				HostCmd_DS_802_11_AUTO_TX *at =
+					(HostCmd_DS_802_11_AUTO_TX *)(buf +
+								      S_DS_GEN);
+
+				if (le16_to_cpu(at->action) ==
+				    HostCmd_ACT_GEN_GET) {
+					if (S_DS_GEN + sizeof(at->action) ==
+					    hostcmd->size) {
+						printf("auto_tx not configured\n");
+
+					} else {
+						MrvlIEtypesHeader_t *header =
+							&at->auto_tx.header;
+
+						header->type =
+							le16_to_cpu(header->
+								    type);
+						header->len =
+							le16_to_cpu(header->
+								    len);
+
+						if ((S_DS_GEN +
+						     sizeof(at->action)
+						     +
+						     sizeof(MrvlIEtypesHeader_t)
+						     + header->len ==
+						     hostcmd->size) &&
+						    (header->type ==
+						     TLV_TYPE_AUTO_TX)) {
+
+							AutoTx_MacFrame_t *atmf
+								=
+								&at->auto_tx.
+								auto_tx_mac_frame;
+
+							printf("Interval: %d second(s)\n", le16_to_cpu(atmf->interval));
+							printf("Priority: %#x\n", atmf->priority);
+							printf("Frame Length: %d\n", le16_to_cpu(atmf->frame_len));
+							printf("Dest Mac Address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", atmf->dest_mac_addr[0], atmf->dest_mac_addr[1], atmf->dest_mac_addr[2], atmf->dest_mac_addr[3], atmf->dest_mac_addr[4], atmf->dest_mac_addr[5]);
+							printf("Src Mac Address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", atmf->src_mac_addr[0], atmf->src_mac_addr[1], atmf->src_mac_addr[2], atmf->src_mac_addr[3], atmf->src_mac_addr[4], atmf->src_mac_addr[5]);
+
+							hexdump("Frame Payload",
+								atmf->payload,
+								le16_to_cpu
+								(atmf->
+								 frame_len)
+								-
+								MLAN_MAC_ADDR_LENGTH
+								* 2, ' ');
+						} else {
+							printf("incorrect auto_tx command response\n");
+						}
+					}
+				}
+				break;
+			}
+		case HostCmd_CMD_802_11_SUBSCRIBE_EVENT:
+			{
+				HostCmd_DS_802_11_SUBSCRIBE_EVENT *se =
+					(HostCmd_DS_802_11_SUBSCRIBE_EVENT
+					 *)(buf + S_DS_GEN);
+				if (le16_to_cpu(se->action) ==
+				    HostCmd_ACT_GEN_GET) {
+					int len =
+						S_DS_GEN +
+						sizeof
+						(HostCmd_DS_802_11_SUBSCRIBE_EVENT);
+					printf("\nEvent\t\tValue\tFreq\tsubscribed\n\n");
+					while (len < hostcmd->size) {
+						MrvlIEtypesHeader_t *header =
+							(MrvlIEtypesHeader_t
+							 *)(buf + len);
+						switch (le16_to_cpu
+							(header->type)) {
+						case TLV_TYPE_RSSI_LOW:
+							{
+								MrvlIEtypes_RssiThreshold_t
+									*low_rssi
+									=
+									(MrvlIEtypes_RssiThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Beacon Low RSSI\t%d\t%d\t%s\n", low_rssi->RSSI_value, low_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0001) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_SNR_LOW:
+							{
+								MrvlIEtypes_SnrThreshold_t
+									*low_snr
+									=
+									(MrvlIEtypes_SnrThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Beacon Low SNR\t%d\t%d\t%s\n", low_snr->SNR_value, low_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0002) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_FAILCOUNT:
+							{
+								MrvlIEtypes_FailureCount_t
+									*failure_count
+									=
+									(MrvlIEtypes_FailureCount_t
+									 *)(buf
+									    +
+									    len);
+								printf("Failure Count\t%d\t%d\t%s\n", failure_count->fail_value, failure_count->fail_freq, (le16_to_cpu(se->events) & 0x0004) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_BCNMISS:
+							{
+								MrvlIEtypes_BeaconsMissed_t
+									*bcn_missed
+									=
+									(MrvlIEtypes_BeaconsMissed_t
+									 *)(buf
+									    +
+									    len);
+								printf("Beacon Missed\t%d\tN/A\t%s\n", bcn_missed->beacon_missed, (le16_to_cpu(se->events) & 0x0008) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_RSSI_HIGH:
+							{
+								MrvlIEtypes_RssiThreshold_t
+									*high_rssi
+									=
+									(MrvlIEtypes_RssiThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Bcn High RSSI\t%d\t%d\t%s\n", high_rssi->RSSI_value, high_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0010) ? "yes" : "no");
+								break;
+							}
+
+						case TLV_TYPE_SNR_HIGH:
+							{
+								MrvlIEtypes_SnrThreshold_t
+									*high_snr
+									=
+									(MrvlIEtypes_SnrThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Beacon High SNR\t%d\t%d\t%s\n", high_snr->SNR_value, high_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0020) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_RSSI_LOW_DATA:
+							{
+								MrvlIEtypes_RssiThreshold_t
+									*low_rssi
+									=
+									(MrvlIEtypes_RssiThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Data Low RSSI\t%d\t%d\t%s\n", low_rssi->RSSI_value, low_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0040) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_SNR_LOW_DATA:
+							{
+								MrvlIEtypes_SnrThreshold_t
+									*low_snr
+									=
+									(MrvlIEtypes_SnrThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Data Low SNR\t%d\t%d\t%s\n", low_snr->SNR_value, low_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0080) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_RSSI_HIGH_DATA:
+							{
+								MrvlIEtypes_RssiThreshold_t
+									*high_rssi
+									=
+									(MrvlIEtypes_RssiThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Data High RSSI\t%d\t%d\t%s\n", high_rssi->RSSI_value, high_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0100) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_SNR_HIGH_DATA:
+							{
+								MrvlIEtypes_SnrThreshold_t
+									*high_snr
+									=
+									(MrvlIEtypes_SnrThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Data High SNR\t%d\t%d\t%s\n", high_snr->SNR_value, high_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0200) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_LINK_QUALITY:
+							{
+								MrvlIEtypes_LinkQuality_t
+									*link_qual
+									=
+									(MrvlIEtypes_LinkQuality_t
+									 *)(buf
+									    +
+									    len);
+								printf("Link Quality Parameters:\n");
+								printf("------------------------\n");
+								printf("Link Quality Event Subscribed\t%s\n", (le16_to_cpu(se->events) & 0x0400) ? "yes" : "no");
+								printf("Link SNR Threshold   = %d\n", le16_to_cpu(link_qual->link_SNR_thrs));
+								printf("Link SNR Frequency   = %d\n", le16_to_cpu(link_qual->link_SNR_freq));
+								printf("Min Rate Value       = %d\n", le16_to_cpu(link_qual->min_rate_val));
+								printf("Min Rate Frequency   = %d\n", le16_to_cpu(link_qual->min_rate_freq));
+								printf("Tx Latency Value     = %d\n", le32_to_cpu(link_qual->tx_latency_val));
+								printf("Tx Latency Threshold = %d\n", le32_to_cpu(link_qual->tx_latency_thrs));
+
+								break;
+							}
+						case TLV_TYPE_PRE_BEACON_LOST:
+							{
+								MrvlIEtypes_PreBeaconLost_t
+									*pre_bcn_lost
+									=
+									(MrvlIEtypes_PreBeaconLost_t
+									 *)(buf
+									    +
+									    len);
+								printf("------------------------\n");
+								printf("Pre-Beacon Lost Event Subscribed\t%s\n", (le16_to_cpu(se->events) & 0x0800) ? "yes" : "no");
+								printf("Pre-Beacon Lost: %d\n", pre_bcn_lost->pre_beacon_lost);
+								break;
+							}
+						default:
+							printf("Unknown subscribed event TLV Type=%#x," " Len=%d\n", le16_to_cpu(header->type), le16_to_cpu(header->len));
+							break;
+						}
+
+						len += (sizeof
+							(MrvlIEtypesHeader_t)
+							+
+							le16_to_cpu(header->
+								    len));
+					}
+				}
+				break;
+			}
+		case HostCmd_CMD_MAC_REG_ACCESS:
+		case HostCmd_CMD_BBP_REG_ACCESS:
+		case HostCmd_CMD_RF_REG_ACCESS:
+		case HostCmd_CMD_CAU_REG_ACCESS:
+			{
+				HostCmd_DS_REG *preg =
+					(HostCmd_DS_REG *)(buf + S_DS_GEN);
+				preg->action = le16_to_cpu(preg->action);
+				if (preg->action == HostCmd_ACT_GEN_GET) {
+					preg->value = le32_to_cpu(preg->value);
+					printf("value = 0x%08x\n", preg->value);
+				}
+				break;
+			}
+		case HostCmd_CMD_MEM_ACCESS:
+			{
+				HostCmd_DS_MEM *pmem =
+					(HostCmd_DS_MEM *)(buf + S_DS_GEN);
+				pmem->action = le16_to_cpu(pmem->action);
+				if (pmem->action == HostCmd_ACT_GEN_GET) {
+					pmem->value = le32_to_cpu(pmem->value);
+					printf("value = 0x%08x\n", pmem->value);
+				}
+				break;
+			}
+		default:
+			printf("HOSTCMD_RESP: CmdCode=%#04x, Size=%#04x,"
+			       " SeqNum=%#04x, Result=%#04x\n",
+			       hostcmd->command, hostcmd->size,
+			       hostcmd->seq_num, hostcmd->result);
+			hexdump("payload",
+				(t_void *)(buf + S_DS_GEN),
+				hostcmd->size - S_DS_GEN, ' ');
+			break;
+		}
+	} else {
+		printf("HOSTCMD failed: CmdCode=%#04x, Size=%#04x,"
+		       " SeqNum=%#04x, Result=%#04x\n",
+		       hostcmd->command, hostcmd->size,
+		       hostcmd->seq_num, hostcmd->result);
+	}
+	return ret;
+}
+
+/**
+ *  @brief Prepare ARP filter buffer
+ *  @param fp		File handler
+ *  @param buf		A pointer to the buffer
+ *  @param length	A pointer to the length of buffer
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+prepare_arp_filter_buffer(FILE * fp, t_u8 *buf, t_u16 *length)
+{
+	t_s8 line[256], *pos;
+	int ln = 0;
+	int ret = MLAN_STATUS_SUCCESS;
+	int arpfilter_found = 0;
+
+	memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+	while ((pos = mlan_config_get_line(fp, line, sizeof(line), &ln))) {
+		if (strcmp(pos, "arpfilter={") == 0) {
+			arpfilter_found = 1;
+			mlan_get_hostcmd_data(fp, &ln, buf, length);
+			break;
+		}
+	}
+	if (!arpfilter_found) {
+		fprintf(stderr,
+			"mlanconfig: 'arpfilter' not found in conf file");
+		ret = MLAN_STATUS_FAILURE;
+	}
+	return ret;
+}
+
+/**
+ *  @brief Prepare the hostcmd for register access
+ *  @param type     Register type
+ *  @param offset   Register offset
+ *  @param value    Pointer to value (NULL for read)
+ *  @param buf      Pointer to hostcmd buffer
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+prepare_hostcmd_regrdwr(t_u32 type, t_u32 offset, t_u32 *value, t_u8 *buf)
+{
+	HostCmd_DS_GEN *hostcmd;
+	HostCmd_DS_REG *preg;
+
+	hostcmd = (HostCmd_DS_GEN *)buf;
+	switch (type) {
+	case 1:
+		hostcmd->command = cpu_to_le16(HostCmd_CMD_MAC_REG_ACCESS);
+		break;
+	case 2:
+		hostcmd->command = cpu_to_le16(HostCmd_CMD_BBP_REG_ACCESS);
+		break;
+	case 3:
+		hostcmd->command = cpu_to_le16(HostCmd_CMD_RF_REG_ACCESS);
+		break;
+	case 5:
+		hostcmd->command = cpu_to_le16(HostCmd_CMD_CAU_REG_ACCESS);
+		break;
+	default:
+		printf("Invalid register set specified\n");
+		return -EINVAL;
+	}
+	preg = (HostCmd_DS_REG *)(buf + S_DS_GEN);
+	preg->action = (value) ? HostCmd_ACT_GEN_SET : HostCmd_ACT_GEN_GET;
+	preg->action = cpu_to_le16(preg->action);
+	preg->offset = cpu_to_le16((t_u16)offset);
+	if (value)
+		preg->value = cpu_to_le32(*value);
+	else
+		preg->value = 0;
+	hostcmd->size = cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_REG));
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+
+	return MLAN_STATUS_SUCCESS;
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.h b/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.h
new file mode 100644
index 0000000..09d72ab
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.h
@@ -0,0 +1,355 @@
+/** @file  mlanhostcmd.h
+  *
+  * @brief This file contains command structures for mlanconfig application
+  *
+  * Copyright (C) 2008-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     11/26/2008: initial version
+************************************************************************/
+#ifndef _MLANHOSTCMD_H_
+#define _MLANHOSTCMD_H_
+
+/** Find number of elements */
+#define NELEMENTS(x) (sizeof(x)/sizeof(x[0]))
+
+/** MLAN MAC Address Length */
+#define MLAN_MAC_ADDR_LENGTH     (6)
+/** Size of command buffer */
+#define MRVDRV_SIZE_OF_CMD_BUFFER		(2 * 1024)
+
+/** Command RET code, MSB is set to 1 */
+#define HostCmd_RET_BIT					0x8000
+/** General purpose action : Get */
+#define HostCmd_ACT_GEN_GET				0x0000
+/** General purpose action : Set */
+#define HostCmd_ACT_GEN_SET				0x0001
+/** General purpose action : Clear */
+#define HostCmd_ACT_GEN_CLEAR				0x0004
+/** General purpose action : Remove */
+#define HostCmd_ACT_GEN_REMOVE          0x0004
+
+/** Host Command ID : Memory access */
+#define HostCmd_CMD_MEM_ACCESS                0x0086
+
+/** Pre-Authenticate - 11r only */
+#define HostCmd_CMD_802_11_AUTHENTICATE       0x0011
+
+/** Read/Write Mac register */
+#define HostCmd_CMD_MAC_REG_ACCESS            0x0019
+/** Read/Write BBP register */
+#define HostCmd_CMD_BBP_REG_ACCESS            0x001a
+/** Read/Write RF register */
+#define HostCmd_CMD_RF_REG_ACCESS             0x001b
+/** Get TX Power data */
+#define HostCmd_CMD_802_11_RF_TX_POWER        0x001e
+/** Get the current TSF */
+#define HostCmd_CMD_GET_TSF                   0x0080
+/** Host Command ID : CAU register access */
+#define HostCmd_CMD_CAU_REG_ACCESS            0x00ed
+
+/** Host Command ID : 802.11 BG scan configuration */
+#define HostCmd_CMD_802_11_BG_SCAN_CONFIG     0x006b
+/** Host Command ID : Configuration data */
+#define HostCmd_CMD_CFG_DATA                  0x008f
+/** Host Command ID : 802.11 TPC adapt req */
+#define HostCmd_CMD_802_11_TPC_ADAPT_REQ      0x0060
+/** Host Command ID : 802.11 crypto */
+#define HostCmd_CMD_802_11_CRYPTO             0x0078
+/** Host Command ID : 802.11 auto Tx */
+#define HostCmd_CMD_802_11_AUTO_TX      	0x0082
+
+/** Host Command ID : 802.11 subscribe event */
+#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT       0x0075
+
+/** Host Command ID : Channel TRPC config */
+#define HostCmd_CMD_CHAN_TRPC_CONFIG                0x00fb
+
+/** TLV  type ID definition */
+#define PROPRIETARY_TLV_BASE_ID     0x0100
+/** TLV type : Beacon RSSI low */
+#define TLV_TYPE_RSSI_LOW           (PROPRIETARY_TLV_BASE_ID + 0x04)	//0x0104
+/** TLV type : Beacon SNR low */
+#define TLV_TYPE_SNR_LOW            (PROPRIETARY_TLV_BASE_ID + 0x05)	//0x0105
+/** TLV type : Fail count */
+#define TLV_TYPE_FAILCOUNT          (PROPRIETARY_TLV_BASE_ID + 0x06)	//0x0106
+/** TLV type : BCN miss */
+#define TLV_TYPE_BCNMISS            (PROPRIETARY_TLV_BASE_ID + 0x07)	//0x0107
+/** TLV type : Beacon RSSI high */
+#define TLV_TYPE_RSSI_HIGH          (PROPRIETARY_TLV_BASE_ID + 0x16)	//0x0116
+/** TLV type : Beacon SNR high */
+#define TLV_TYPE_SNR_HIGH           (PROPRIETARY_TLV_BASE_ID + 0x17)	//0x0117
+/** TLV type : Auto Tx */
+#define TLV_TYPE_AUTO_TX            (PROPRIETARY_TLV_BASE_ID + 0x18)	//0x0118
+/** TLV type :Link Quality */
+#define TLV_TYPE_LINK_QUALITY       (PROPRIETARY_TLV_BASE_ID + 0x24)	//0x0124
+/** TLV type : Data RSSI low */
+#define TLV_TYPE_RSSI_LOW_DATA      (PROPRIETARY_TLV_BASE_ID + 0x26)	//0x0126
+/** TLV type : Data SNR low */
+#define TLV_TYPE_SNR_LOW_DATA       (PROPRIETARY_TLV_BASE_ID + 0x27)	//0x0127
+/** TLV type : Data RSSI high */
+#define TLV_TYPE_RSSI_HIGH_DATA     (PROPRIETARY_TLV_BASE_ID + 0x28)	//0x0128
+/** TLV type : Data SNR high */
+#define TLV_TYPE_SNR_HIGH_DATA      (PROPRIETARY_TLV_BASE_ID + 0x29)	//0x0129
+/** TLV type: Pre-Beacon Lost */
+#define TLV_TYPE_PRE_BEACON_LOST    (PROPRIETARY_TLV_BASE_ID + 0x49)	//0x0149
+
+/** TLV type : Channel TRPC */
+#define TLV_TYPE_CHAN_TRPC              (PROPRIETARY_TLV_BASE_ID + 0x89)	//0x0189
+
+/* Define general hostcmd data structure */
+/** HostCmd_DS_GEN */
+typedef struct MAPP_HostCmd_DS_GEN {
+    /** Command */
+	t_u16 command;
+    /** Size */
+	t_u16 size;
+    /** Sequence number */
+	t_u16 seq_num;
+    /** Result */
+	t_u16 result;
+} __ATTRIB_PACK__ HostCmd_DS_GEN;
+
+typedef struct _HostCmd_DS_MEF_CFG {
+    /** Criteria */
+	t_u32 Criteria;
+    /** Number of entries */
+	t_u16 NumEntries;
+} __ATTRIB_PACK__ HostCmd_DS_MEF_CFG;
+
+typedef struct _MEF_CFG_DATA {
+    /** Size */
+	t_u16 size;
+    /** Data */
+	HostCmd_DS_MEF_CFG data;
+} __ATTRIB_PACK__ MEF_CFG_DATA;
+
+/** Size of HostCmd_DS_GEN */
+#define S_DS_GEN    sizeof(HostCmd_DS_GEN)
+
+/** HostCmd_DS_REG */
+typedef struct MAPP_HostCmd_DS_REG {
+    /** Read or write */
+	t_u16 action;
+    /** Register offset */
+	t_u16 offset;
+    /** Value */
+	t_u32 value;
+} __ATTRIB_PACK__ HostCmd_DS_REG;
+
+/** HostCmd_DS_MEM */
+typedef struct MAPP_HostCmd_DS_MEM {
+    /** Read or write */
+	t_u16 action;
+    /** Reserved */
+	t_u16 reserved;
+    /** Address */
+	t_u32 addr;
+    /** Value */
+	t_u32 value;
+} __ATTRIB_PACK__ HostCmd_DS_MEM;
+
+/** HostCmd_DS_802_11_CFG_DATA */
+typedef struct MAPP_HostCmd_DS_802_11_CFG_DATA {
+    /** Action */
+	t_u16 action;
+    /** Type */
+	t_u16 type;
+    /** Data length */
+	t_u16 data_len;
+    /** Data */
+	t_u8 data[1];
+} __ATTRIB_PACK__ HostCmd_DS_802_11_CFG_DATA;
+
+/** mlan_ioctl_11h_tpc_resp */
+typedef struct {
+	int status_code;
+		     /**< Firmware command result status code */
+	int tx_power;/**< Reported TX Power from the TPC Report */
+	int link_margin;
+		     /**< Reported Link margin from the TPC Report */
+	int rssi;    /**< RSSI of the received TPC Report frame */
+} __ATTRIB_PACK__ mlan_ioctl_11h_tpc_resp;
+
+/** MrvlIEtypesHeader_t */
+typedef struct MrvlIEtypesHeader {
+    /** Header type */
+	t_u16 type;
+    /** Header length */
+	t_u16 len;
+} __ATTRIB_PACK__ MrvlIEtypesHeader_t;
+
+/** MrvlIEtypes_Data_t */
+typedef struct MrvlIEtypes_Data_t {
+    /** Header */
+	MrvlIEtypesHeader_t header;
+    /** Data */
+	t_u8 data[1];
+} __ATTRIB_PACK__ MrvlIEtypes_Data_t;
+
+/** HostCmd_DS_802_11_CRYPTO */
+typedef struct MAPP_HostCmd_DS_802_11_CRYPTO {
+	t_u16 encdec;	  /**< Decrypt=0, Encrypt=1 */
+	t_u16 algorithm;  /**< RC4=1 AES=2 , AES_KEY_WRAP=3 */
+	t_u16 key_IV_length;/**< Length of Key IV (bytes)   */
+	t_u8 keyIV[32];	  /**< Key IV */
+	t_u16 key_length;  /**< Length of Key (bytes) */
+	t_u8 key[32];	  /**< Key */
+	MrvlIEtypes_Data_t data;   /**< Plain text if encdec=Encrypt, Ciphertext data if encdec=Decrypt*/
+} __ATTRIB_PACK__ HostCmd_DS_802_11_CRYPTO;
+
+/** HostCmd_DS_802_11_CRYPTO_AES_CCM */
+typedef struct MAPP_HostCmd_DS_802_11_CRYPTO_AES_CCM {
+	t_u16 encdec;	  /**< Decrypt=0, Encrypt=1 */
+	t_u16 algorithm;  /**< AES_CCM=4 */
+	t_u16 key_length;  /**< Length of Key (bytes)  */
+	t_u8 key[32];	  /**< Key  */
+	t_u16 nonce_length;/**< Length of Nonce (bytes) */
+	t_u8 nonce[14];	  /**< Nonce */
+	t_u16 AAD_length;  /**< Length of AAD (bytes) */
+	t_u8 AAD[32];	  /**< AAD */
+	MrvlIEtypes_Data_t data;   /**< Plain text if encdec=Encrypt, Ciphertext data if encdec=Decrypt*/
+} __ATTRIB_PACK__ HostCmd_DS_802_11_CRYPTO_AES_CCM;
+
+/** AES CCM cipher test */
+#define CIPHER_TEST_AES_CCM (4)
+/** AutoTx_MacFrame_t */
+typedef struct AutoTx_MacFrame {
+	t_u16 interval;	      /**< in seconds */
+	t_u8 priority;	      /**< User Priority: 0~7, ignored if non-WMM */
+	t_u8 reserved;	      /**< set to 0 */
+	t_u16 frame_len;       /**< Length of MAC frame payload */
+	t_u8 dest_mac_addr[MLAN_MAC_ADDR_LENGTH];	/**< Destination MAC address */
+	t_u8 src_mac_addr[MLAN_MAC_ADDR_LENGTH];	/**< Source MAC address */
+	t_u8 payload[];			  /**< Payload */
+} __ATTRIB_PACK__ AutoTx_MacFrame_t;
+
+/** MrvlIEtypes_AutoTx_t */
+typedef struct MrvlIEtypes_AutoTx {
+	MrvlIEtypesHeader_t header;	    /**< Header */
+	AutoTx_MacFrame_t auto_tx_mac_frame;  /**< Auto Tx MAC frame */
+} __ATTRIB_PACK__ MrvlIEtypes_AutoTx_t;
+
+/** HostCmd_DS_802_11_AUTO_TX */
+typedef struct MAPP_HostCmd_DS_802_11_AUTO_TX {
+    /** Action */
+	t_u16 action;		/* 0 = ACT_GET; 1 = ACT_SET; */
+	MrvlIEtypes_AutoTx_t auto_tx;	 /**< Auto Tx */
+} __ATTRIB_PACK__ HostCmd_DS_802_11_AUTO_TX;
+
+/** HostCmd_DS_802_11_SUBSCRIBE_EVENT */
+typedef struct MAPP_HostCmd_DS_802_11_SUBSCRIBE_EVENT {
+    /** Action */
+	t_u16 action;
+    /** Events */
+	t_u16 events;
+} __ATTRIB_PACK__ HostCmd_DS_802_11_SUBSCRIBE_EVENT;
+
+/** MrvlIEtypes_RssiParamSet_t */
+typedef struct MrvlIEtypes_RssiThreshold {
+    /** Header */
+	MrvlIEtypesHeader_t header;
+    /** RSSI value */
+	t_u8 RSSI_value;
+    /** RSSI frequency */
+	t_u8 RSSI_freq;
+} __ATTRIB_PACK__ MrvlIEtypes_RssiThreshold_t;
+
+/** MrvlIEtypes_SnrThreshold_t */
+typedef struct MrvlIEtypes_SnrThreshold {
+    /** Header */
+	MrvlIEtypesHeader_t header;
+    /** SNR value */
+	t_u8 SNR_value;
+    /** SNR frequency */
+	t_u8 SNR_freq;
+} __ATTRIB_PACK__ MrvlIEtypes_SnrThreshold_t;
+
+/** MrvlIEtypes_FailureCount_t */
+typedef struct MrvlIEtypes_FailureCount {
+    /** Header */
+	MrvlIEtypesHeader_t header;
+    /** Failure value */
+	t_u8 fail_value;
+    /** Failure frequency */
+	t_u8 fail_freq;
+} __ATTRIB_PACK__ MrvlIEtypes_FailureCount_t;
+
+/** MrvlIEtypes_BeaconsMissed_t */
+typedef struct MrvlIEtypes_BeaconsMissed {
+    /** Header */
+	MrvlIEtypesHeader_t header;
+    /** Number of beacons missed */
+	t_u8 beacon_missed;
+    /** Reserved */
+	t_u8 reserved;
+} __ATTRIB_PACK__ MrvlIEtypes_BeaconsMissed_t;
+
+/** MrvlIEtypes_LinkQuality_t */
+typedef struct MrvlIEtypes_LinkQuality {
+    /** Header */
+	MrvlIEtypesHeader_t header;
+    /** Link SNR threshold */
+	t_u16 link_SNR_thrs;
+    /** Link SNR frequency */
+	t_u16 link_SNR_freq;
+    /** Minimum rate value */
+	t_u16 min_rate_val;
+    /** Minimum rate frequency */
+	t_u16 min_rate_freq;
+    /** Tx latency value */
+	t_u32 tx_latency_val;
+    /** Tx latency threshold */
+	t_u32 tx_latency_thrs;
+} __ATTRIB_PACK__ MrvlIEtypes_LinkQuality_t;
+
+/** MrvlIEtypes_PreBeaconLost_t */
+typedef struct MrvlIEtypes_PreBeaconLost {
+    /** Header */
+	MrvlIEtypesHeader_t header;
+    /** Pre-Beacon Lost */
+	t_u8 pre_beacon_lost;
+    /** Reserved */
+	t_u8 reserved;
+} __ATTRIB_PACK__ MrvlIEtypes_PreBeaconLost_t;
+
+/* String helper functions */
+/** Convert char to hex integer */
+int hexval(t_s32 chr);
+/** Convert char to hex integer */
+t_u8 hexc2bin(t_s8 chr);
+/** Convert string to hex integer */
+t_u32 a2hex(t_s8 *s);
+/** Check the Hex String */
+int ishexstring(t_s8 *s);
+/** Convert String to integer */
+t_u32 a2hex_or_atoi(t_s8 *value);
+/** Convert String to Integer */
+int atoval(t_s8 *buf);
+/** Hump hex data */
+void hexdump(t_s8 *prompt, void *p, t_s32 len, t_s8 delim);
+/** Convert String to Hex */
+t_s8 *convert2hex(t_s8 *ptr, t_u8 *chr);
+
+int process_host_cmd_resp(t_u8 *buf);
+int prepare_host_cmd_buffer(FILE * fp, char *cmd_name, t_u8 *buf);
+int prepare_arp_filter_buffer(FILE * fp, t_u8 *buf, t_u16 *length);
+int prepare_cfg_data_buffer(int argc, char *argv[], FILE * fp, t_u8 *buf);
+int prepare_hostcmd_regrdwr(t_u32 type, t_u32 offset, t_u32 *value, t_u8 *buf);
+
+#endif /* _MLANHOSTCMD_H_ */
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanmisc.c b/wlan_sd8897/mapp/mlanconfig/mlanmisc.c
new file mode 100644
index 0000000..8d3399f
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/mlanmisc.c
@@ -0,0 +1,1161 @@
+/** @file  mlanmisc.c
+  *
+  * @brief Program to prepare command buffer
+  *
+  * Copyright (C) 2008-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+
+/************************************************************************
+Change log:
+     03/10/2009: initial version
+************************************************************************/
+
+#include    "mlanconfig.h"
+#include    "mlanhostcmd.h"
+#include    "mlanmisc.h"
+
+/********************************************************
+		Local Variables
+********************************************************/
+
+/********************************************************
+		Global Variables
+********************************************************/
+
+/********************************************************
+		Local Functions
+********************************************************/
+
+/**
+ *  @brief Helper function for process_getscantable_idx
+ *
+ *  @param pbuf     A pointer to the buffer
+ *  @param buf_len  buffer length
+ *
+ *  @return         NA
+ *
+ */
+static void
+dump_scan_elems(const t_u8 *pbuf, uint buf_len)
+{
+	uint idx;
+	uint marker = 2 + pbuf[1];
+
+	for (idx = 0; idx < buf_len; idx++) {
+		if (idx % 0x10 == 0) {
+			printf("\n%04x: ", idx);
+		}
+
+		if (idx == marker) {
+			printf("|");
+			marker = idx + pbuf[idx + 1] + 2;
+		} else {
+			printf(" ");
+		}
+
+		printf("%02x ", pbuf[idx]);
+	}
+
+	printf("\n");
+}
+
+/**
+ *  @brief Helper function for process_getscantable_idx
+ *  Find next element
+ *
+ *  @param pp_ie_out    pointer of a IEEEtypes_Generic_t structure pointer
+ *  @param p_buf_left   integer pointer, which contains the number of left p_buf
+ *
+ *  @return             MLAN_STATUS_SUCCESS on success, otherwise MLAN_STATUS_FAILURE
+ */
+static int
+scantable_elem_next(IEEEtypes_Generic_t **pp_ie_out, int *p_buf_left)
+{
+	IEEEtypes_Generic_t *pie_gen;
+	t_u8 *p_next;
+
+	if (*p_buf_left < 2) {
+		return MLAN_STATUS_FAILURE;
+	}
+
+	pie_gen = *pp_ie_out;
+
+	p_next = (t_u8 *)pie_gen + (pie_gen->ieee_hdr.len
+				    + sizeof(pie_gen->ieee_hdr));
+	*p_buf_left -= (p_next - (t_u8 *)pie_gen);
+
+	*pp_ie_out = (IEEEtypes_Generic_t *)p_next;
+
+	if (*p_buf_left <= 0) {
+		return MLAN_STATUS_FAILURE;
+	}
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+ /**
+  *  @brief Helper function for process_getscantable_idx
+  *         scantable find element
+  *
+  *  @param ie_buf       pointer of the IE buffer
+  *  @param ie_buf_len   IE buffer length
+  *  @param ie_type      IE type
+  *  @param ppie_out     pointer to the IEEEtypes_Generic_t structure pointer
+  *  @return             MLAN_STATUS_SUCCESS on success, otherwise MLAN_STATUS_FAILURE
+  */
+static int
+scantable_find_elem(t_u8 *ie_buf,
+		    unsigned int ie_buf_len,
+		    IEEEtypes_ElementId_e ie_type,
+		    IEEEtypes_Generic_t **ppie_out)
+{
+	int found;
+	unsigned int ie_buf_left;
+
+	ie_buf_left = ie_buf_len;
+
+	found = FALSE;
+
+	*ppie_out = (IEEEtypes_Generic_t *)ie_buf;
+
+	do {
+		found = ((*ppie_out)->ieee_hdr.element_id == ie_type);
+
+	} while (!found &&
+		 (scantable_elem_next(ppie_out, (int *)&ie_buf_left) == 0));
+
+	if (!found) {
+		*ppie_out = NULL;
+	}
+
+	return (found ? MLAN_STATUS_SUCCESS : MLAN_STATUS_FAILURE);
+}
+
+ /**
+  *  @brief Helper function for process_getscantable_idx
+  *         It gets SSID from IE
+  *
+  *  @param ie_buf       IE buffer
+  *  @param ie_buf_len   IE buffer length
+  *  @param pssid        SSID
+  *  @param ssid_buf_max size of SSID
+  *  @return             MLAN_STATUS_SUCCESS on success, otherwise MLAN_STATUS_FAILURE
+  */
+static int
+scantable_get_ssid_from_ie(t_u8 *ie_buf,
+			   unsigned int ie_buf_len,
+			   t_u8 *pssid, unsigned int ssid_buf_max)
+{
+	int retval;
+	IEEEtypes_Generic_t *pie_gen;
+
+	retval = scantable_find_elem(ie_buf, ie_buf_len, SSID, &pie_gen);
+
+	memcpy(pssid, pie_gen->data, MIN(pie_gen->ieee_hdr.len, ssid_buf_max));
+
+	return retval;
+}
+
+/**
+ *  @brief Display detailed information for a specific scan table entry
+ *
+ *  @param prsp_info_req    Scan table entry request structure
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_getscantable_idx(wlan_ioctl_get_scan_table_info *prsp_info_req)
+{
+	int ioctl_val, subioctl_val;
+	struct iwreq iwr;
+	t_u8 *pcurrent;
+	int ret = 0;
+	char ssid[33];
+	t_u16 tmp_cap;
+	t_u8 tsf[8];
+	t_u16 beacon_interval;
+	t_u8 *scan_rsp_buf = NULL;
+	t_u16 cap_info;
+	wlan_ioctl_get_scan_table_info *prsp_info;
+
+	wlan_get_scan_table_fixed fixed_fields;
+	t_u32 fixed_field_length;
+	t_u32 bss_info_length;
+
+	scan_rsp_buf = (t_u8 *)malloc(SCAN_RESP_BUF_SIZE);
+	if (scan_rsp_buf == NULL) {
+		printf("Error: allocate memory for scan_rsp_buf failed\n");
+		return -ENOMEM;
+	}
+	memset(ssid, 0x00, sizeof(ssid));
+
+	prsp_info = (wlan_ioctl_get_scan_table_info *)scan_rsp_buf;
+
+	memcpy(prsp_info, prsp_info_req,
+	       sizeof(wlan_ioctl_get_scan_table_info));
+
+	if (get_priv_ioctl("getscantable",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		ret = -EOPNOTSUPP;
+		goto done;
+	}
+
+	/*
+	 * Set up and execute the ioctl call
+	 */
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.pointer = (caddr_t) prsp_info;
+	iwr.u.data.length = SCAN_RESP_BUF_SIZE;
+	iwr.u.data.flags = subioctl_val;
+
+	if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+		perror("mlanconfig: getscantable ioctl");
+		ret = -EFAULT;
+		goto done;
+	}
+
+	if (prsp_info->scan_number == 0) {
+		printf("mlanconfig: getscantable ioctl - index out of range\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pcurrent = prsp_info->scan_table_entry_buf;
+
+	memcpy((t_u8 *)&fixed_field_length,
+	       (t_u8 *)pcurrent, sizeof(fixed_field_length));
+	pcurrent += sizeof(fixed_field_length);
+
+	memcpy((t_u8 *)&bss_info_length,
+	       (t_u8 *)pcurrent, sizeof(bss_info_length));
+	pcurrent += sizeof(bss_info_length);
+
+	memcpy((t_u8 *)&fixed_fields, (t_u8 *)pcurrent, sizeof(fixed_fields));
+	pcurrent += fixed_field_length;
+
+	/* time stamp is 8 byte long */
+	memcpy(tsf, pcurrent, sizeof(tsf));
+	pcurrent += sizeof(tsf);
+	bss_info_length -= sizeof(tsf);
+
+	/* beacon interval is 2 byte long */
+	memcpy(&beacon_interval, pcurrent, sizeof(beacon_interval));
+	pcurrent += sizeof(beacon_interval);
+	bss_info_length -= sizeof(beacon_interval);
+
+	/* capability information is 2 byte long */
+	memcpy(&cap_info, pcurrent, sizeof(cap_info));
+	pcurrent += sizeof(cap_info);
+	bss_info_length -= sizeof(cap_info);
+
+	scantable_get_ssid_from_ie(pcurrent,
+				   bss_info_length, (t_u8 *)ssid, sizeof(ssid));
+
+	printf("\n*** [%s], %02x:%02x:%02x:%02x:%02x:%2x\n",
+	       ssid,
+	       fixed_fields.bssid[0],
+	       fixed_fields.bssid[1],
+	       fixed_fields.bssid[2],
+	       fixed_fields.bssid[3],
+	       fixed_fields.bssid[4], fixed_fields.bssid[5]);
+	memcpy(&tmp_cap, &cap_info, sizeof(tmp_cap));
+	printf("Channel = %d, SS = %d, CapInfo = 0x%04x, BcnIntvl = %d\n",
+	       fixed_fields.channel,
+	       255 - fixed_fields.rssi, tmp_cap, beacon_interval);
+
+	printf("TSF Values: AP(0x%02x%02x%02x%02x%02x%02x%02x%02x), ",
+	       tsf[7], tsf[6], tsf[5], tsf[4], tsf[3], tsf[2], tsf[1], tsf[0]);
+
+	printf("Network(0x%016llx)\n", fixed_fields.network_tsf);
+	printf("\n");
+	printf("Element Data (%d bytes)\n", (int)bss_info_length);
+	printf("------------");
+	dump_scan_elems(pcurrent, bss_info_length);
+	printf("\n");
+
+done:
+
+	if (scan_rsp_buf)
+		free(scan_rsp_buf);
+
+	return ret;
+}
+
+/********************************************************
+		Global Functions
+********************************************************/
+/** Maximum SDIO command-52 buffer length */
+#define CMD52_BUF_LEN    3
+
+/**
+ *  @brief SD comand52 read/write
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_sdcmd52rw(int argc, char *argv[])
+{
+	struct iwreq iwr;
+	int ioctl_val, subioctl_val, buf[CMD52_BUF_LEN];
+
+	if (get_priv_ioctl("sdcmd52rw",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	if (argc == 5) {
+		/* CMD52 read */
+		iwr.u.data.length = CMD52_BUF_LEN - 1;
+	} else if (argc == 6) {
+		/* CMD52 write */
+		buf[2] = atoval(argv[5]);	/* data */
+		iwr.u.data.length = CMD52_BUF_LEN;
+	} else {
+		fprintf(stderr, "Invalid number of parameters!\n");
+		return MLAN_STATUS_FAILURE;
+	}
+	buf[0] = atoval(argv[3]);	/* func */
+	buf[1] = atoval(argv[4]);	/* reg */
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.flags = subioctl_val;
+	iwr.u.data.pointer = (caddr_t) buf;
+
+	if (ioctl(sockfd, ioctl_val, &iwr)) {
+		perror("mlanconfig");
+		fprintf(stderr,
+			"mlanconfig: CMD52 R/W not supported by "
+			"interface %s\n", dev_name);
+		return MLAN_STATUS_FAILURE;
+	}
+	printf("sdcmd52rw returns 0x%02X\n", buf[0]);
+
+	return MLAN_STATUS_SUCCESS;
+}
+
+/** Maximum SDIO command-53 buffer length */
+#define CMD53_BUF_LEN    2000
+
+/**
+ *  @brief SD comand53 read/write
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_sdcmd53rw(int argc, char *argv[])
+{
+	struct iwreq iwr;
+	t_s8 *buf = NULL;
+	int addr, mode, blklen, blknum, i, rw;
+	int ioctl_val, subioctl_val;
+
+	if (get_priv_ioctl("sdcmd53rw",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	if (argc < 8) {
+		fprintf(stderr, "Invalid number of parameters!\n");
+		return MLAN_STATUS_FAILURE;
+	}
+
+	if (!(buf = malloc(CMD53_BUF_LEN)))
+		return -ENOMEM;
+	memset(buf, 0, CMD53_BUF_LEN);
+
+	if (argc == 8) {
+		rw = buf[0] = 0;	/* CMD53 read */
+	} else {
+		rw = buf[0] = 1;	/* CMD53 write */
+	}
+	buf[1] = atoval(argv[3]);	/* func */
+	addr = atoval(argv[4]);	/* address */
+	buf[2] = addr & 0xff;
+	buf[3] = (addr >> 8) & 0xff;
+	buf[4] = (addr >> 16) & 0xff;
+	buf[5] = (addr >> 24) & 0xff;
+	mode = atoval(argv[5]);	/* mode */
+	buf[6] = (t_u8)mode;
+	blklen = atoval(argv[6]);	/* block size */
+	buf[7] = blklen & 0xff;
+	buf[8] = (blklen >> 8) & 0xff;
+	blknum = atoval(argv[7]);	/* block number or byte number */
+	buf[9] = blknum & 0xff;
+	buf[10] = (blknum >> 8) & 0xff;
+	iwr.u.data.length = 11;
+	if (buf[0]) {
+		for (i = 0; i < (argc - 8); i++)
+			buf[11 + i] = atoval(argv[8 + i]);
+		iwr.u.data.length += (argc - 8);
+	}
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.flags = subioctl_val;
+	iwr.u.data.pointer = (caddr_t) buf;
+
+	if (ioctl(sockfd, ioctl_val, &iwr)) {
+		perror("mlanconfig");
+		fprintf(stderr,
+			"mlanconfig: CMD53 R/W not supported by "
+			"interface %s\n", dev_name);
+		free(buf);
+		return MLAN_STATUS_FAILURE;
+	}
+
+	if (mode) {
+		fprintf(stderr, "CMD53rw blklen = %d, blknum = %d\n", blklen,
+			blknum);
+	} else {
+		blklen = 1;
+		fprintf(stderr, "CMD53rw bytelen = %d\n", blknum);
+	}
+	if (!rw)
+		hexdump("data", buf, blklen * blknum, ' ');
+
+	free(buf);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Retrieve and display the contents of the driver scan table.
+ *
+ *  The ioctl to retrieve the scan table contents will be invoked, and portions
+ *   of the scan data will be displayed on stdout.  The entire beacon or
+ *   probe response is also retrieved (if available in the driver).  This
+ *   data would be needed in case the application was explicitly controlling
+ *   the association (inserting IEs, TLVs, etc).
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_getscantable(int argc, char *argv[])
+{
+	int ioctl_val, subioctl_val;
+	struct iwreq iwr;
+	t_u8 *scan_rsp_buf = NULL;
+
+	struct wlan_ioctl_get_scan_list *scan_list_head = NULL;
+	struct wlan_ioctl_get_scan_list *scan_list_node = NULL;
+	struct wlan_ioctl_get_scan_list *curr = NULL, *prev = NULL, *next =
+		NULL;
+
+	t_u32 total_scan_res = 0;
+
+	unsigned int scan_start;
+	int idx, ret = 0;
+
+	t_u8 *pcurrent;
+	t_u8 *pnext;
+	IEEEtypes_ElementId_e *pelement_id;
+	t_u8 *pelement_len;
+	int ssid_idx;
+	t_u8 *pbyte;
+	t_u16 new_ss;
+	t_u16 curr_ss;
+
+	IEEEtypes_VendorSpecific_t *pwpa_ie;
+	const t_u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 };
+
+	IEEEtypes_WmmParameter_t *pwmm_ie;
+	const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 };
+	IEEEtypes_VendorSpecific_t *pwps_ie;
+	const t_u8 wps_oui[4] = { 0x00, 0x50, 0xf2, 0x04 };
+
+	int displayed_info;
+
+	wlan_ioctl_get_scan_table_info rspInfoReq;
+	wlan_ioctl_get_scan_table_info *prsp_info;
+
+	wlan_get_scan_table_fixed fixed_fields;
+	t_u32 fixed_field_length;
+	t_u32 bss_info_length;
+	wlan_ioctl_get_bss_info *bss_info;
+
+	scan_rsp_buf = (t_u8 *)malloc(SCAN_RESP_BUF_SIZE);
+	if (scan_rsp_buf == NULL) {
+		printf("Error: allocate memory for scan_rsp_buf failed\n");
+		return -ENOMEM;
+	}
+	prsp_info = (wlan_ioctl_get_scan_table_info *)scan_rsp_buf;
+
+	if (get_priv_ioctl("getscantable",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		ret = -EOPNOTSUPP;
+		goto done;
+	}
+
+	if (argc > 3 && (strcmp(argv[3], "tsf") != 0)
+	    && (strcmp(argv[3], "help") != 0)) {
+
+		idx = strtol(argv[3], NULL, 10);
+
+		if (idx >= 0) {
+			if (scan_rsp_buf) {
+				free(scan_rsp_buf);
+			}
+			rspInfoReq.scan_number = idx;
+			return process_getscantable_idx(&rspInfoReq);
+		}
+	}
+
+	displayed_info = FALSE;
+	scan_start = 1;
+
+	do {
+		prsp_info->scan_number = scan_start;
+
+		/*
+		 * Set up and execute the ioctl call
+		 */
+		strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+		iwr.u.data.pointer = (caddr_t) prsp_info;
+		iwr.u.data.length = SCAN_RESP_BUF_SIZE;
+		iwr.u.data.flags = subioctl_val;
+
+		if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+			perror("mlanconfig: getscantable ioctl");
+			ret = -EFAULT;
+			goto done;
+		}
+		total_scan_res += prsp_info->scan_number;
+
+		pcurrent = 0;
+		pnext = prsp_info->scan_table_entry_buf;
+
+		for (idx = 0; (unsigned int)idx < prsp_info->scan_number; idx++) {
+
+			/* Alloc memory for new node for next BSS */
+			scan_list_node = (struct wlan_ioctl_get_scan_list *)
+				malloc(sizeof(struct wlan_ioctl_get_scan_list));
+			if (scan_list_node == NULL) {
+				printf("Error: allocate memory for scan_list_head failed\n");
+				return -ENOMEM;
+			}
+			memset(scan_list_node, 0,
+			       sizeof(struct wlan_ioctl_get_scan_list));
+
+			/*
+			 * Set pcurrent to pnext in case pad bytes are at the end
+			 *   of the last IE we processed.
+			 */
+			pcurrent = pnext;
+
+			/* Start extracting each BSS to prepare a linked list */
+			memcpy((t_u8 *)&fixed_field_length,
+			       (t_u8 *)pcurrent, sizeof(fixed_field_length));
+			pcurrent += sizeof(fixed_field_length);
+
+			memcpy((t_u8 *)&bss_info_length,
+			       (t_u8 *)pcurrent, sizeof(bss_info_length));
+			pcurrent += sizeof(bss_info_length);
+
+			memcpy((t_u8 *)&fixed_fields,
+			       (t_u8 *)pcurrent, sizeof(fixed_fields));
+			pcurrent += fixed_field_length;
+
+			scan_list_node->fixed_buf.fixed_field_length =
+				fixed_field_length;
+			scan_list_node->fixed_buf.bss_info_length =
+				bss_info_length;
+			scan_list_node->fixed_buf.fixed_fields = fixed_fields;
+
+			bss_info = &scan_list_node->bss_info_buf;
+
+			/* Set next to be the start of the next scan entry */
+			pnext = pcurrent + bss_info_length;
+
+			if (bss_info_length >=
+			    (sizeof(bss_info->tsf) +
+			     sizeof(bss_info->beacon_interval) +
+			     sizeof(bss_info->cap_info))) {
+
+				/* time stamp is 8 byte long */
+				memcpy(bss_info->tsf, pcurrent,
+				       sizeof(bss_info->tsf));
+				pcurrent += sizeof(bss_info->tsf);
+				bss_info_length -= sizeof(bss_info->tsf);
+
+				/* beacon interval is 2 byte long */
+				memcpy(&bss_info->beacon_interval, pcurrent,
+				       sizeof(bss_info->beacon_interval));
+				pcurrent += sizeof(bss_info->beacon_interval);
+				bss_info_length -=
+					sizeof(bss_info->beacon_interval);
+
+				/* capability information is 2 byte long */
+				memcpy(&bss_info->cap_info, pcurrent,
+				       sizeof(bss_info->cap_info));
+				pcurrent += sizeof(bss_info->cap_info);
+				bss_info_length -= sizeof(bss_info->cap_info);
+			}
+
+			bss_info->wmm_cap = ' ';	/* M (WMM), C (WMM-Call Admission Control) */
+			bss_info->wps_cap = ' ';	/* "S" */
+			bss_info->dot11k_cap = ' ';	/* "K" */
+			bss_info->dot11r_cap = ' ';	/* "R" */
+			bss_info->ht_cap = ' ';	/* "N" */
+
+			/* "P" for Privacy (WEP) since "W" is WPA, and "2" is RSN/WPA2 */
+			bss_info->priv_cap =
+				bss_info->cap_info.privacy ? 'P' : ' ';
+
+			memset(bss_info->ssid, 0, MRVDRV_MAX_SSID_LENGTH + 1);
+			bss_info->ssid_len = 0;
+
+			while (bss_info_length >= 2) {
+				pelement_id = (IEEEtypes_ElementId_e *)pcurrent;
+				pelement_len = pcurrent + 1;
+				pcurrent += 2;
+
+				switch (*pelement_id) {
+
+				case SSID:
+					if (*pelement_len &&
+					    *pelement_len <=
+					    MRVDRV_MAX_SSID_LENGTH) {
+						memcpy(bss_info->ssid, pcurrent,
+						       *pelement_len);
+						bss_info->ssid_len =
+							*pelement_len;
+					}
+					break;
+
+				case WPA_IE:
+					pwpa_ie =
+						(IEEEtypes_VendorSpecific_t *)
+						pelement_id;
+					if ((memcmp
+					     (pwpa_ie->vend_hdr.oui, wpa_oui,
+					      sizeof(pwpa_ie->vend_hdr.oui)) ==
+					     0)
+					    && (pwpa_ie->vend_hdr.oui_type ==
+						wpa_oui[3])) {
+						/* WPA IE found, 'W' for WPA */
+						bss_info->priv_cap = 'W';
+					} else {
+						pwmm_ie =
+							(IEEEtypes_WmmParameter_t
+							 *)pelement_id;
+						if ((memcmp
+						     (pwmm_ie->vend_hdr.oui,
+						      wmm_oui,
+						      sizeof(pwmm_ie->vend_hdr.
+							     oui)) == 0)
+						    && (pwmm_ie->vend_hdr.
+							oui_type ==
+							wmm_oui[3])) {
+							/* Check the subtype: 1 == parameter, 0 == info */
+							if ((pwmm_ie->vend_hdr.
+							     oui_subtype == 1)
+							    && pwmm_ie->
+							    ac_params
+							    [WMM_AC_VO].
+							    aci_aifsn.acm) {
+								/* Call admission on VO; 'C' for CAC */
+								bss_info->
+									wmm_cap
+									= 'C';
+							} else {
+								/* No CAC; 'M' for uh, WMM */
+								bss_info->
+									wmm_cap
+									= 'M';
+							}
+						} else {
+							pwps_ie =
+								(IEEEtypes_VendorSpecific_t
+								 *)pelement_id;
+							if ((memcmp
+							     (pwps_ie->vend_hdr.
+							      oui, wps_oui,
+							      sizeof(pwps_ie->
+								     vend_hdr.
+								     oui)) == 0)
+							    && (pwps_ie->
+								vend_hdr.
+								oui_type ==
+								wps_oui[3])) {
+								bss_info->
+									wps_cap
+									= 'S';
+							}
+						}
+					}
+					break;
+
+				case RSN_IE:
+					/* RSN IE found; '2' for WPA2 (RSN) */
+					bss_info->priv_cap = '2';
+					break;
+				case HT_CAPABILITY:
+					bss_info->ht_cap = 'N';
+					break;
+				case VHT_CAPABILITY:
+					bss_info->vht_cap[0] = 'A';
+					bss_info->vht_cap[1] = 'C';
+					break;
+				default:
+					break;
+				}
+
+				pcurrent += *pelement_len;
+				bss_info_length -= (2 + *pelement_len);
+			}
+
+			/* Create a sorted list of BSS using Insertion Sort.
+			   Sort as per Signal Strength (descending order) */
+			new_ss = 255 - fixed_fields.rssi;
+
+			if (scan_list_head == NULL) {
+				// Node is the first element in the list.
+				scan_list_head = scan_list_node;
+				scan_list_node->next = NULL;
+			} else {
+
+				curr_ss =
+					255 -
+					scan_list_head->fixed_buf.fixed_fields.
+					rssi;
+
+				if (new_ss > curr_ss) {
+					// Insert the node to head of the list
+					scan_list_node->next = scan_list_head;
+					scan_list_head = scan_list_node;
+				} else {
+
+					for (curr = scan_list_head;
+					     curr != NULL; curr = curr->next) {
+
+						curr_ss =
+							255 -
+							curr->fixed_buf.
+							fixed_fields.rssi;
+						if (new_ss > curr_ss) {
+							// Insert the node to current position in list
+							scan_list_node->next =
+								curr;
+							prev->next =
+								scan_list_node;
+							break;
+						}
+						prev = curr;
+					}
+					if (curr == NULL) {
+
+						// Insert the node to tail of the list
+						prev->next = scan_list_node;
+						scan_list_node->next = NULL;
+					}
+				}
+			}
+		}
+
+		scan_start += prsp_info->scan_number;
+	} while (prsp_info->scan_number);
+
+	// Display scan results
+	printf("---------------------------------------");
+	printf("---------------------------------------\n");
+	printf("# | ch  | ss  |       bssid       |   cap    |   SSID \n");
+	printf("---------------------------------------");
+	printf("---------------------------------------\n");
+
+	for (curr = scan_list_head, idx = 0;
+	     (curr != NULL) && ((unsigned int)idx < total_scan_res);
+	     curr = curr->next, idx++) {
+
+		fixed_fields = curr->fixed_buf.fixed_fields;
+		bss_info = &curr->bss_info_buf;
+
+		printf("%02u| %03d | %03d | %02x:%02x:%02x:%02x:%02x:%02x |",
+		       idx,
+		       fixed_fields.channel,
+		       255 - fixed_fields.rssi,
+		       fixed_fields.bssid[0],
+		       fixed_fields.bssid[1],
+		       fixed_fields.bssid[2],
+		       fixed_fields.bssid[3],
+		       fixed_fields.bssid[4], fixed_fields.bssid[5]);
+
+		displayed_info = TRUE;
+
+		/* "A" for Adhoc
+		 * "I" for Infrastructure,
+		 * "D" for DFS (Spectrum Mgmt)
+		 */
+		printf(" %c%c%c%c%c%c%c%c%c%c | ", bss_info->cap_info.ibss ? 'A' : 'I', bss_info->priv_cap,	/* P (WEP), W (WPA), 2 (WPA2) */
+		       bss_info->cap_info.spectrum_mgmt ? 'D' : ' ', bss_info->wmm_cap,	/* M (WMM), C (WMM-Call Admission Control) */
+		       bss_info->dot11k_cap,	/* K */
+		       bss_info->dot11r_cap,	/* R */
+		       bss_info->wps_cap,	/* S */
+		       bss_info->ht_cap,	/* N */
+		       bss_info->vht_cap[0],	/* AC */
+		       bss_info->vht_cap[1]);
+
+		/* Print out the ssid or the hex values if non-printable */
+		for (ssid_idx = 0; ssid_idx < bss_info->ssid_len; ssid_idx++) {
+			if (isprint(bss_info->ssid[ssid_idx])) {
+				printf("%c", bss_info->ssid[ssid_idx]);
+			} else {
+				printf("\\%02x", bss_info->ssid[ssid_idx]);
+			}
+		}
+
+		printf("\n");
+
+		if (argc > 3 && strcmp(argv[3], "tsf") == 0) {
+			/* TSF is a u64, some formatted printing libs have trouble
+			   printing long longs, so cast and dump as bytes */
+			pbyte = (t_u8 *)&fixed_fields.network_tsf;
+			printf("    TSF=%02x%02x%02x%02x%02x%02x%02x%02x\n",
+			       pbyte[7], pbyte[6], pbyte[5], pbyte[4],
+			       pbyte[3], pbyte[2], pbyte[1], pbyte[0]);
+		}
+	}
+
+	if (displayed_info == TRUE) {
+		if (argc > 3 && strcmp(argv[3], "help") == 0) {
+			printf("\n\n"
+			       "Capability Legend (Not all may be supported)\n"
+			       "-----------------\n"
+			       " I [ Infrastructure ]\n"
+			       " A [ Ad-hoc ]\n"
+			       " W [ WPA IE ]\n"
+			       " 2 [ WPA2/RSN IE ]\n"
+			       " M [ WMM IE ]\n"
+			       " C [ Call Admission Control - WMM IE, VO ACM set ]\n"
+			       " D [ Spectrum Management - DFS (11h) ]\n"
+			       " K [ 11k ]\n"
+			       " R [ 11r ]\n"
+			       " S [ WPS ]\n"
+			       " N [ HT (11n) ]\n"
+			       " AC [VHT (11ac) ]\n" "\n\n");
+		}
+	} else {
+		printf("< No Scan Results >\n");
+	}
+
+done:
+	if (scan_rsp_buf)
+		free(scan_rsp_buf);
+	for (curr = scan_list_head; curr != NULL; curr = next) {
+		next = curr->next;
+		free(curr);
+	}
+	return ret;
+}
+
+/** Maximum channel scratch */
+#define MAX_CHAN_SCRATCH  100
+
+/** Maximum channel number for b/g band */
+#define MAX_CHAN_BG_BAND  14
+
+/** Maximum number of probes to send on each channel */
+#define MAX_PROBES        4
+
+/** Scan all the channels in specified band */
+#define BAND_SPECIFIED    0x80
+/**
+ *  @brief Request a scan from the driver and display the scan table afterwards
+ *
+ *  Command line interface for performing a specific immediate scan based
+ *    on the following keyword parsing:
+ *
+ *     chan=[chan#][band][mode] where band is [a,b,g,n] and mode is
+ *                              blank for active or 'p' for passive
+ *     bssid=xx:xx:xx:xx:xx:xx  specify a BSSID filter for the scan
+ *     ssid="[SSID]"            specify a SSID filter for the scan
+ *     keep=[0 or 1]            keep the previous scan results (1), discard (0)
+ *     dur=[scan time]          time to scan for each channel in milliseconds
+ *     probes=[#]               number of probe requests to send on each chan
+ *     type=[1,2,3]             BSS type: 1 (Infra), 2(Adhoc), 3(Any)
+ *
+ *  Any combination of the above arguments can be supplied on the command line.
+ *    If the chan token is absent, a full channel scan will be completed by
+ *    the driver.  If the dur or probes tokens are absent, the drivers default
+ *    setting will be used.  The bssid and ssid fields, if blank,
+ *    will produce an unfiltered scan. The type field will default to 3 (Any)
+ *    and the keep field will default to 0 (Discard).
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_setuserscan(int argc, char *argv[])
+{
+	wlan_ioctl_user_scan_cfg scan_req;
+	int ioctl_val, subioctl_val;
+	struct iwreq iwr;
+	char *parg_tok;
+	char *pchan_tok;
+	char *parg_cookie;
+	char *pchan_cookie;
+	int arg_idx;
+	int chan_parse_idx;
+	int chan_cmd_idx;
+	char chan_scratch[MAX_CHAN_SCRATCH];
+	char *pscratch;
+	int tmp_idx;
+	int scan_time;
+	int num_ssid;
+	int is_radio_set;
+	unsigned int mac[ETH_ALEN];
+
+	memset(&scan_req, 0x00, sizeof(scan_req));
+	chan_cmd_idx = 0;
+	scan_time = 0;
+	num_ssid = 0;
+
+	if (get_priv_ioctl("setuserscan",
+			   &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) {
+		return -EOPNOTSUPP;
+	}
+
+	for (arg_idx = 0; arg_idx < argc; arg_idx++) {
+
+		if (strncmp(argv[arg_idx], "ssid=", strlen("ssid=")) == 0) {
+			/*
+			 *  "ssid" token string handler
+			 */
+			if (num_ssid < MRVDRV_MAX_SSID_LIST_LENGTH) {
+				strncpy(scan_req.ssid_list[num_ssid].ssid,
+					argv[arg_idx] + strlen("ssid="),
+					sizeof(scan_req.ssid_list[num_ssid].
+					       ssid));
+
+				scan_req.ssid_list[num_ssid].max_len = 0;
+
+				num_ssid++;
+			}
+		} else if (strncmp(argv[arg_idx], "bssid=", strlen("bssid=")) ==
+			   0) {
+			/*
+			 *  "bssid" token string handler
+			 */
+			sscanf(argv[arg_idx] + strlen("bssid="),
+			       "%2x:%2x:%2x:%2x:%2x:%2x", mac + 0, mac + 1,
+			       mac + 2, mac + 3, mac + 4, mac + 5);
+
+			for (tmp_idx = 0;
+			     (unsigned int)tmp_idx < NELEMENTS(mac);
+			     tmp_idx++) {
+				scan_req.specific_bssid[tmp_idx] =
+					(t_u8)mac[tmp_idx];
+			}
+		} else if (strncmp(argv[arg_idx], "chan=", strlen("chan=")) ==
+			   0) {
+			/*
+			 *  "chan" token string handler
+			 */
+			parg_tok = argv[arg_idx] + strlen("chan=");
+
+			if (strlen(parg_tok) > MAX_CHAN_SCRATCH) {
+				printf("Error: Specified channels exceeds max limit\n");
+				return MLAN_STATUS_FAILURE;
+			}
+			is_radio_set = FALSE;
+
+			while ((parg_tok =
+				strtok_r(parg_tok, ",",
+					 &parg_cookie)) != NULL) {
+
+				memset(chan_scratch, 0x00,
+				       sizeof(chan_scratch));
+				pscratch = chan_scratch;
+
+				for (chan_parse_idx = 0;
+				     (unsigned int)chan_parse_idx <
+				     strlen(parg_tok); chan_parse_idx++) {
+					if (isalpha
+					    (*(parg_tok + chan_parse_idx))) {
+						*pscratch++ = ' ';
+					}
+
+					*pscratch++ =
+						*(parg_tok + chan_parse_idx);
+				}
+				*pscratch = 0;
+				parg_tok = NULL;
+
+				pchan_tok = chan_scratch;
+
+				while ((pchan_tok = strtok_r(pchan_tok, " ",
+							     &pchan_cookie)) !=
+				       NULL) {
+					if (isdigit(*pchan_tok)) {
+						scan_req.
+							chan_list[chan_cmd_idx].
+							chan_number =
+							atoi(pchan_tok);
+						if (scan_req.
+						    chan_list[chan_cmd_idx].
+						    chan_number >
+						    MAX_CHAN_BG_BAND)
+							scan_req.
+								chan_list
+								[chan_cmd_idx].
+								radio_type = 1;
+					} else {
+						switch (toupper(*pchan_tok)) {
+						case 'A':
+							scan_req.
+								chan_list
+								[chan_cmd_idx].
+								radio_type = 1;
+							is_radio_set = TRUE;
+							break;
+						case 'B':
+						case 'G':
+							scan_req.
+								chan_list
+								[chan_cmd_idx].
+								radio_type = 0;
+							is_radio_set = TRUE;
+							break;
+						case 'N':
+							break;
+						case 'P':
+							scan_req.
+								chan_list
+								[chan_cmd_idx].
+								scan_type =
+								MLAN_SCAN_TYPE_PASSIVE;
+							break;
+						default:
+							printf("Error: Band type not supported!\n");
+							return -EOPNOTSUPP;
+						}
+						if (!chan_cmd_idx &&
+						    !scan_req.
+						    chan_list[chan_cmd_idx].
+						    chan_number && is_radio_set)
+							scan_req.
+								chan_list
+								[chan_cmd_idx].
+								radio_type |=
+								BAND_SPECIFIED;
+					}
+					pchan_tok = NULL;
+				}
+				chan_cmd_idx++;
+			}
+		} else if (strncmp(argv[arg_idx], "keep=", strlen("keep=")) ==
+			   0) {
+			/*
+			 *  "keep" token string handler
+			 */
+			scan_req.keep_previous_scan =
+				atoi(argv[arg_idx] + strlen("keep="));
+		} else if (strncmp(argv[arg_idx], "dur=", strlen("dur=")) == 0) {
+			/*
+			 *  "dur" token string handler
+			 */
+			scan_time = atoi(argv[arg_idx] + strlen("dur="));
+			scan_req.chan_list[0].scan_time = scan_time;
+
+		} else if (strncmp(argv[arg_idx], "wc=", strlen("wc=")) == 0) {
+
+			if (num_ssid < MRVDRV_MAX_SSID_LIST_LENGTH) {
+				/*
+				 *  "wc" token string handler
+				 */
+				pscratch = strrchr(argv[arg_idx], ',');
+
+				if (pscratch) {
+					*pscratch = 0;
+					pscratch++;
+
+					if (isdigit(*pscratch)) {
+						scan_req.ssid_list[num_ssid].
+							max_len =
+							atoi(pscratch);
+					} else {
+						scan_req.ssid_list[num_ssid].
+							max_len = *pscratch;
+					}
+				} else {
+					/* Standard wildcard matching */
+					scan_req.ssid_list[num_ssid].max_len =
+						0xFF;
+				}
+
+				strncpy(scan_req.ssid_list[num_ssid].ssid,
+					argv[arg_idx] + strlen("wc="),
+					sizeof(scan_req.ssid_list[num_ssid].
+					       ssid));
+
+				num_ssid++;
+			}
+		} else if (strncmp(argv[arg_idx], "probes=", strlen("probes="))
+			   == 0) {
+			/*
+			 *  "probes" token string handler
+			 */
+			scan_req.num_probes =
+				atoi(argv[arg_idx] + strlen("probes="));
+			if (scan_req.num_probes > MAX_PROBES) {
+				fprintf(stderr, "Invalid probes (> %d)\n",
+					MAX_PROBES);
+				return -EOPNOTSUPP;
+			}
+		} else if (strncmp(argv[arg_idx], "type=", strlen("type=")) ==
+			   0) {
+			/*
+			 *  "type" token string handler
+			 */
+			scan_req.bss_mode =
+				atoi(argv[arg_idx] + strlen("type="));
+			switch (scan_req.bss_mode) {
+			case MLAN_SCAN_MODE_BSS:
+			case MLAN_SCAN_MODE_IBSS:
+				break;
+			case MLAN_SCAN_MODE_ANY:
+			default:
+				/* Set any unknown types to ANY */
+				scan_req.bss_mode = MLAN_SCAN_MODE_ANY;
+				break;
+			}
+		}
+	}
+
+	/*
+	 * Update all the channels to have the same scan time
+	 */
+	for (tmp_idx = 1; tmp_idx < chan_cmd_idx; tmp_idx++) {
+		scan_req.chan_list[tmp_idx].scan_time = scan_time;
+	}
+
+	strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1);
+	iwr.u.data.pointer = (caddr_t) & scan_req;
+	iwr.u.data.length = sizeof(scan_req);
+	iwr.u.data.flags = subioctl_val;
+
+	if (ioctl(sockfd, ioctl_val, &iwr) < 0) {
+		perror("mlanconfig: setuserscan ioctl");
+		return -EFAULT;
+	}
+
+	process_getscantable(0, 0);
+
+	return MLAN_STATUS_SUCCESS;
+}
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanmisc.h b/wlan_sd8897/mapp/mlanconfig/mlanmisc.h
new file mode 100644
index 0000000..076ce6b
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanconfig/mlanmisc.h
@@ -0,0 +1,683 @@
+/** @file  mlanmisc.h
+  *
+  * @brief This file contains command definitions for application
+  *
+  * Copyright (C) 2008-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     03/10/2009: initial version
+************************************************************************/
+
+#ifndef _MLANMISC_H_
+#define _MLANMISC_H_
+
+/** Maximum size of IEEE Information Elements */
+#define IEEE_MAX_IE_SIZE  256
+
+/** Maximum scan response buffer size */
+#define SCAN_RESP_BUF_SIZE 2000
+
+#ifdef FALSE
+#undef FALSE
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+
+#ifndef MIN
+/** Find minimum value */
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
+
+#ifndef MAX
+/** Find maximum value */
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif /* MAX */
+
+/** Type enumeration of WMM AC_QUEUES */
+typedef enum _mlan_wmm_ac_e {
+	WMM_AC_BK,
+	WMM_AC_BE,
+	WMM_AC_VI,
+	WMM_AC_VO
+} __ATTRIB_PACK__ mlan_wmm_ac_e;
+
+/** Maximum length of SSID */
+#define MRVDRV_MAX_SSID_LENGTH          32
+
+/** Enumeration for scan mode */
+enum {
+	MLAN_SCAN_MODE_UNCHANGED = 0,
+	MLAN_SCAN_MODE_BSS,
+	MLAN_SCAN_MODE_IBSS,
+	MLAN_SCAN_MODE_ANY
+};
+
+/** Enumeration for scan type */
+enum {
+	MLAN_SCAN_TYPE_UNCHANGED = 0,
+	MLAN_SCAN_TYPE_ACTIVE,
+	MLAN_SCAN_TYPE_PASSIVE
+};
+
+/** Length of ethernet address */
+#ifndef ETH_ALEN
+#define ETH_ALEN            6
+#endif
+/** Maximum length of SSID list */
+#define MRVDRV_MAX_SSID_LIST_LENGTH         10
+
+/** Maximum number of channels that can be sent in a setuserscan ioctl */
+#define WLAN_IOCTL_USER_SCAN_CHAN_MAX  50
+
+/** IEEE Type definitions  */
+typedef enum _IEEEtypes_ElementId_e {
+	SSID = 0,
+	SUPPORTED_RATES = 1,
+	FH_PARAM_SET = 2,
+	DS_PARAM_SET = 3,
+	CF_PARAM_SET = 4,
+
+	IBSS_PARAM_SET = 6,
+
+	COUNTRY_INFO = 7,
+
+	POWER_CONSTRAINT = 32,
+	POWER_CAPABILITY = 33,
+	TPC_REQUEST = 34,
+	TPC_REPORT = 35,
+	SUPPORTED_CHANNELS = 36,
+	CHANNEL_SWITCH_ANN = 37,
+	QUIET = 40,
+	IBSS_DFS = 41,
+	HT_CAPABILITY = 45,
+	HT_OPERATION = 61,
+	BSSCO_2040 = 72,
+	OVERLAPBSSSCANPARAM = 74,
+	EXT_CAPABILITY = 127,
+
+	VHT_CAPABILITY = 191,
+	VHT_OPERATION = 192,
+	EXT_BSS_LOAD = 193,
+	BW_CHANNEL_SWITCH = 194,
+	VHT_TX_POWER_ENV = 195,
+	EXT_POWER_CONSTR = 196,
+	AID_INFO = 197,
+	QUIET_CHAN = 198,
+
+	ERP_INFO = 42,
+	EXTENDED_SUPPORTED_RATES = 50,
+
+	VENDOR_SPECIFIC_221 = 221,
+	WMM_IE = VENDOR_SPECIFIC_221,
+
+	WPS_IE = VENDOR_SPECIFIC_221,
+
+	WPA_IE = VENDOR_SPECIFIC_221,
+	RSN_IE = 48,
+} __ATTRIB_PACK__ IEEEtypes_ElementId_e;
+
+/** Capability Bit Map*/
+#ifdef BIG_ENDIAN_SUPPORT
+typedef struct _IEEEtypes_CapInfo_t {
+	t_u8 rsrvd1:2;
+	t_u8 dsss_ofdm:1;
+	t_u8 rsvrd2:2;
+	t_u8 short_slot_time:1;
+	t_u8 rsrvd3:1;
+	t_u8 spectrum_mgmt:1;
+	t_u8 chan_agility:1;
+	t_u8 pbcc:1;
+	t_u8 short_preamble:1;
+	t_u8 privacy:1;
+	t_u8 cf_poll_rqst:1;
+	t_u8 cf_pollable:1;
+	t_u8 ibss:1;
+	t_u8 ess:1;
+} __ATTRIB_PACK__ IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t;
+#else
+typedef struct _IEEEtypes_CapInfo_t {
+    /** Capability Bit Map : ESS */
+	t_u8 ess:1;
+    /** Capability Bit Map : IBSS */
+	t_u8 ibss:1;
+    /** Capability Bit Map : CF pollable */
+	t_u8 cf_pollable:1;
+    /** Capability Bit Map : CF poll request */
+	t_u8 cf_poll_rqst:1;
+    /** Capability Bit Map : privacy */
+	t_u8 privacy:1;
+    /** Capability Bit Map : Short preamble */
+	t_u8 short_preamble:1;
+    /** Capability Bit Map : PBCC */
+	t_u8 pbcc:1;
+    /** Capability Bit Map : Channel agility */
+	t_u8 chan_agility:1;
+    /** Capability Bit Map : Spectrum management */
+	t_u8 spectrum_mgmt:1;
+    /** Capability Bit Map : Reserved */
+	t_u8 rsrvd3:1;
+    /** Capability Bit Map : Short slot time */
+	t_u8 short_slot_time:1;
+    /** Capability Bit Map : APSD */
+	t_u8 apsd:1;
+    /** Capability Bit Map : Reserved */
+	t_u8 rsvrd2:1;
+    /** Capability Bit Map : DSS OFDM */
+	t_u8 dsss_ofdm:1;
+    /** Capability Bit Map : Reserved */
+	t_u8 rsrvd1:2;
+} __ATTRIB_PACK__ IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t;
+#endif /* BIG_ENDIAN_SUPPORT */
+
+/** IEEE IE header */
+typedef struct _IEEEtypes_Header_t {
+    /** Element ID */
+	t_u8 element_id;
+    /** Length */
+	t_u8 len;
+} __ATTRIB_PACK__ IEEEtypes_Header_t, *pIEEEtypes_Header_t;
+
+/** IEEE IE header */
+#define IEEE_HEADER_LEN   sizeof(IEEEtypes_Header_t)
+
+/** Vendor specific IE header */
+typedef struct _IEEEtypes_VendorHeader_t {
+    /** Element ID */
+	t_u8 element_id;
+    /** Length */
+	t_u8 len;
+    /** OUI */
+	t_u8 oui[3];
+    /** OUI type */
+	t_u8 oui_type;
+    /** OUI subtype */
+	t_u8 oui_subtype;
+    /** Version */
+	t_u8 version;
+} __ATTRIB_PACK__ IEEEtypes_VendorHeader_t, *pIEEEtypes_VendorHeader_t;
+
+/** Vendor specific IE */
+typedef struct _IEEEtypes_VendorSpecific_t {
+    /** Vendor specific IE header */
+	IEEEtypes_VendorHeader_t vend_hdr;
+    /** IE Max - size of previous fields */
+	t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_VendorHeader_t)];
+} __ATTRIB_PACK__ IEEEtypes_VendorSpecific_t, *pIEEEtypes_VendorSpecific_t;
+
+/** IEEE IE */
+typedef struct _IEEEtypes_Generic_t {
+    /** Generic IE header */
+	IEEEtypes_Header_t ieee_hdr;
+    /** IE Max - size of previous fields */
+	t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_Header_t)];
+} __ATTRIB_PACK__ IEEEtypes_Generic_t, *pIEEEtypes_Generic_t;
+
+/** Size of a TSPEC.  Used to allocate necessary buffer space in commands */
+#define WMM_TSPEC_SIZE              63
+
+/** Maximum number of AC QOS queues available in the driver/firmware */
+#define MAX_AC_QUEUES               4
+
+/** Maximum number of User Priorities */
+#define MAX_USER_PRIORITIES         8
+
+/** Extra IE bytes allocated in messages for appended IEs after a TSPEC */
+#define WMM_ADDTS_EXTRA_IE_BYTES    256
+
+/**
+ *  @brief Enumeration for the command result from an ADDTS or DELTS command
+ */
+typedef enum {
+	TSPEC_RESULT_SUCCESS = 0,
+	TSPEC_RESULT_EXEC_FAILURE = 1,
+	TSPEC_RESULT_TIMEOUT = 2,
+	TSPEC_RESULT_DATA_INVALID = 3,
+} __ATTRIB_PACK__ mlan_wmm_tspec_result_e;
+
+/**
+ *  @brief Enumeration for the action field in the Queue configure command
+ */
+typedef enum {
+	WMM_QUEUE_CONFIG_ACTION_GET = 0,
+	WMM_QUEUE_CONFIG_ACTION_SET = 1,
+	WMM_QUEUE_CONFIG_ACTION_DEFAULT = 2,
+
+	WMM_QUEUE_CONFIG_ACTION_MAX
+} __ATTRIB_PACK__ mlan_wmm_queue_config_action_e;
+
+/**
+ *   @brief Enumeration for the action field in the queue stats command
+ */
+typedef enum {
+	WMM_STATS_ACTION_START = 0,
+	WMM_STATS_ACTION_STOP = 1,
+	WMM_STATS_ACTION_GET_CLR = 2,
+	WMM_STATS_ACTION_SET_CFG = 3,	/* Not currently used */
+	WMM_STATS_ACTION_GET_CFG = 4,	/* Not currently used */
+
+	WMM_STATS_ACTION_MAX
+} __ATTRIB_PACK__ mlan_wmm_stats_action_e;
+
+/** Data structure of WMM Aci/Aifsn */
+typedef struct _IEEEtypes_WmmAciAifsn_t {
+#ifdef BIG_ENDIAN_SUPPORT
+    /** Reserved */
+	t_u8 reserved:1;
+    /** Aci */
+	t_u8 aci:2;
+    /** Acm */
+	t_u8 acm:1;
+    /** Aifsn */
+	t_u8 aifsn:4;
+#else
+    /** Aifsn */
+	t_u8 aifsn:4;
+    /** Acm */
+	t_u8 acm:1;
+    /** Aci */
+	t_u8 aci:2;
+    /** Reserved */
+	t_u8 reserved:1;
+#endif
+} __ATTRIB_PACK__ IEEEtypes_WmmAciAifsn_t, *pIEEEtypes_WmmAciAifsn_t;
+
+/** Data structure of WMM ECW */
+typedef struct _IEEEtypes_WmmEcw_t {
+#ifdef BIG_ENDIAN_SUPPORT
+    /** Maximum Ecw */
+	t_u8 ecw_max:4;
+    /** Minimum Ecw */
+	t_u8 ecw_min:4;
+#else
+    /** Minimum Ecw */
+	t_u8 ecw_min:4;
+    /** Maximum Ecw */
+	t_u8 ecw_max:4;
+#endif
+} __ATTRIB_PACK__ IEEEtypes_WmmEcw_t, *pIEEEtypes_WmmEcw_t;
+
+/** Data structure of WMM AC parameters  */
+typedef struct _IEEEtypes_WmmAcParameters_t {
+	IEEEtypes_WmmAciAifsn_t aci_aifsn;  /**< AciAifSn */
+	IEEEtypes_WmmEcw_t ecw;		    /**< Ecw */
+	t_u16 tx_op_limit;		    /**< Tx op limit */
+} __ATTRIB_PACK__ IEEEtypes_WmmAcParameters_t, *pIEEEtypes_WmmAcParameters_t;
+
+/** Data structure of WMM Info IE  */
+typedef struct _IEEEtypes_WmmInfo_t {
+
+    /**
+     * WMM Info IE - Vendor Specific Header:
+     *   element_id  [221/0xdd]
+     *   Len         [7]
+     *   Oui         [00:50:f2]
+     *   OuiType     [2]
+     *   OuiSubType  [0]
+     *   Version     [1]
+     */
+	IEEEtypes_VendorHeader_t vend_hdr;
+
+    /** QoS information */
+	IEEEtypes_WmmQosInfo_t qos_info;
+
+} __ATTRIB_PACK__ IEEEtypes_WmmInfo_t, *pIEEEtypes_WmmInfo_t;
+
+/** Data structure of WMM parameter IE  */
+typedef struct _IEEEtypes_WmmParameter_t {
+    /**
+     * WMM Parameter IE - Vendor Specific Header:
+     *   element_id  [221/0xdd]
+     *   Len         [24]
+     *   Oui         [00:50:f2]
+     *   OuiType     [2]
+     *   OuiSubType  [1]
+     *   Version     [1]
+     */
+	IEEEtypes_VendorHeader_t vend_hdr;
+
+    /** QoS information */
+	IEEEtypes_WmmQosInfo_t qos_info;
+    /** Reserved */
+	t_u8 reserved;
+
+    /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */
+	IEEEtypes_WmmAcParameters_t ac_params[MAX_AC_QUEUES];
+
+} __ATTRIB_PACK__ IEEEtypes_WmmParameter_t, *pIEEEtypes_WmmParameter_t;
+
+/**
+ *  @brief IOCTL structure to send an ADDTS request and retrieve the response.
+ *
+ *  IOCTL structure from the application layer relayed to firmware to
+ *    instigate an ADDTS management frame with an appropriate TSPEC IE as well
+ *    as any additional IEs appended in the ADDTS Action frame.
+ *
+ *  @sa wlan_wmm_addts_req_ioctl
+ */
+typedef struct {
+	mlan_wmm_tspec_result_e commandResult;
+					   /**< Firmware execution result */
+
+	t_u32 timeout_ms;		/**< Timeout value in milliseconds */
+	t_u8 ieeeStatusCode;		/**< IEEE status code */
+
+	t_u32 ieDataLen;
+	t_u8 ieData[WMM_TSPEC_SIZE
+				 /**< TSPEC to send in the ADDTS */
+		    + WMM_ADDTS_EXTRA_IE_BYTES];
+					     /**< ADDTS extra IE buf */
+} wlan_ioctl_wmm_addts_req_t;
+
+/**
+ *  @brief IOCTL structure to send a DELTS request.
+ *
+ *  IOCTL structure from the application layer relayed to firmware to
+ *    instigate an DELTS management frame with an appropriate TSPEC IE.
+ *
+ *  @sa wlan_wmm_delts_req_ioctl
+ */
+typedef struct {
+	mlan_wmm_tspec_result_e commandResult;
+					    /**< Firmware execution result */
+	t_u8 ieeeReasonCode;	  /**< IEEE reason code sent, unused for WMM */
+
+	t_u32 ieDataLen;
+	t_u8 ieData[WMM_TSPEC_SIZE];
+				  /**< TSPEC to send in the DELTS */
+} wlan_ioctl_wmm_delts_req_t;
+
+/**
+ *  @brief IOCTL structure to configure a specific AC Queue's parameters
+ *
+ *  IOCTL structure from the application layer relayed to firmware to
+ *    get, set, or default the WMM AC queue parameters.
+ *
+ *  - msduLifetimeExpiry is ignored if set to 0 on a set command
+ *
+ *  @sa wlan_wmm_queue_config_ioctl
+ */
+typedef struct {
+	mlan_wmm_queue_config_action_e action;
+					   /**< Set, Get, or Default */
+	mlan_wmm_ac_e accessCategory;	   /**< WMM_AC_BK(0) to WMM_AC_VO(3) */
+	t_u16 msduLifetimeExpiry;	   /**< lifetime expiry in TUs */
+	t_u8 supportedRates[10];	   /**< Not supported yet */
+} wlan_ioctl_wmm_queue_config_t;
+
+/** Number of bins in the histogram for the HostCmd_DS_WMM_QUEUE_STATS */
+#define WMM_STATS_PKTS_HIST_BINS  7
+
+/**
+ *  @brief IOCTL structure to start, stop, and get statistics for a WMM AC
+ *
+ *  IOCTL structure from the application layer relayed to firmware to
+ *    start or stop statistical collection for a given AC.  Also used to
+ *    retrieve and clear the collected stats on a given AC.
+ *
+ *  @sa wlan_wmm_queue_stats_ioctl
+ */
+typedef struct {
+	mlan_wmm_stats_action_e action;
+				     /**< Start, Stop, or Get  */
+	t_u8 userPriority;
+			 /**< User Priority (0 to 7) */
+	t_u16 pktCount;	 /**< Number of successful packets transmitted */
+	t_u16 pktLoss;	 /**< Packets lost; not included in pktCount   */
+	t_u32 avgQueueDelay;
+			 /**< Average Queue delay in microseconds */
+	t_u32 avgTxDelay;/**< Average Transmission delay in microseconds */
+	t_u16 usedTime;	 /**< Calculated used time - units of 32 microsec */
+	t_u16 policedTime;
+			 /**< Calculated policed time - units of 32 microsec */
+
+    /** @brief Queue Delay Histogram; number of packets per queue delay range
+     *
+     *  [0] -  0ms <= delay < 5ms
+     *  [1] -  5ms <= delay < 10ms
+     *  [2] - 10ms <= delay < 20ms
+     *  [3] - 20ms <= delay < 30ms
+     *  [4] - 30ms <= delay < 40ms
+     *  [5] - 40ms <= delay < 50ms
+     *  [6] - 50ms <= delay < msduLifetime (TUs)
+     */
+	t_u16 delayHistogram[WMM_STATS_PKTS_HIST_BINS];
+} wlan_ioctl_wmm_queue_stats_t;
+
+/**
+ *  @brief IOCTL and command sub structure for a Traffic stream status.
+ */
+typedef struct {
+	t_u8 tid;	    /**< TSID: Range: 0->7 */
+	t_u8 valid;	    /**< TSID specified is valid  */
+	t_u8 accessCategory;/**< AC TSID is active on */
+	t_u8 userPriority;  /**< UP specified for the TSID */
+
+	t_u8 psb;	    /**< Power save mode for TSID: 0 (legacy), 1 (UAPSD) */
+	t_u8 flowDir;	    /**< Upstream (0), Downlink(1), Bidirectional(3) */
+	t_u16 mediumTime;   /**< Medium time granted for the TSID */
+} __ATTRIB_PACK__ HostCmd_DS_WMM_TS_STATUS,
+	wlan_ioctl_wmm_ts_status_t, wlan_cmd_wmm_ts_status_t;
+
+/**
+ *  @brief IOCTL sub structure for a specific WMM AC Status
+ */
+typedef struct {
+    /** WMM Acm */
+	t_u8 wmmAcm;
+    /** Flow required flag */
+	t_u8 flowRequired;
+    /** Flow created flag */
+	t_u8 flowCreated;
+    /** Disabled flag */
+	t_u8 disabled;
+    /** delivery enabled */
+	t_u8 deliveryEnabled;
+    /** trigger enabled */
+	t_u8 triggerEnabled;
+} wlan_ioctl_wmm_queue_status_ac_t;
+
+/**
+ *  @brief IOCTL structure to retrieve the WMM AC Queue status
+ *
+ *  IOCTL structure from the application layer to retrieve:
+ *     - ACM bit setting for the AC
+ *     - Firmware status (flow required, flow created, flow disabled)
+ *
+ *  @sa wlan_wmm_queue_status_ioctl
+ */
+typedef struct {
+    /** WMM AC queue status */
+	wlan_ioctl_wmm_queue_status_ac_t acStatus[MAX_AC_QUEUES];
+} wlan_ioctl_wmm_queue_status_t;
+
+typedef struct _wlan_get_scan_table_fixed {
+    /** BSSID of this network */
+	t_u8 bssid[MLAN_MAC_ADDR_LENGTH];
+    /** Channel this beacon/probe response was detected */
+	t_u8 channel;
+    /** RSSI for the received packet */
+	t_u8 rssi;
+    /** TSF value from the firmware at packet reception */
+	t_u64 network_tsf;
+} wlan_get_scan_table_fixed;
+
+/**
+ *  Structure passed in the wlan_ioctl_get_scan_table_info for each
+ *    BSS returned in the WLAN_GET_SCAN_RESP IOCTL
+ */
+typedef struct _wlan_ioctl_get_scan_table_entry {
+    /**
+     *  Fixed field length included in the response.
+     *
+     *  Length value is included so future fixed fields can be added to the
+     *   response without breaking backwards compatibility.  Use the length
+     *   to find the offset for the bssInfoLength field, not a sizeof() calc.
+     */
+	t_u32 fixed_field_length;
+
+    /**
+     *  Length of the BSS Information (probe resp or beacon) that
+     *    follows after the fixed_field_length
+     */
+	t_u32 bss_info_length;
+
+    /**
+     *  Always present, fixed length data fields for the BSS
+     */
+	wlan_get_scan_table_fixed fixed_fields;
+
+	/*
+	 *  Probe response or beacon scanned for the BSS.
+	 *
+	 *  Field layout:
+	 *   - TSF              8 octets
+	 *   - Beacon Interval  2 octets
+	 *   - Capability Info  2 octets
+	 *
+	 *   - IEEE Infomation Elements; variable number & length per 802.11 spec
+	 */
+	/* t_u8  bss_info_buffer[1]; */
+} wlan_ioctl_get_scan_table_entry;
+
+/**
+ *  Structure to store BSS info (probe resp or beacon) & IEEE IE info for each
+ *  BSS returned in WLAN_GET_SCAN_RESP IOCTL
+ */
+typedef struct _wlan_ioctl_get_bss_info {
+	/**
+	 *  Length of the BSS Information (probe resp or beacon) that
+	 *    follows after the fixed_field
+	 */
+	t_u32 bss_info_length;
+
+	/**
+	 *  Probe response or beacon scanned for the BSS.
+	 *
+	 *  Field layout:
+	 */
+	/** TSF              8 octets */
+	t_u8 tsf[8];
+	/** Beacon Interval  2 octets */
+	t_u16 beacon_interval;
+	/** Capability Info  2 octets */
+	IEEEtypes_CapInfo_t cap_info;
+
+	/**
+	 *  IEEE Infomation Elements; variable number & length per 802.11 spec
+	 */
+	/** SSID */
+	char ssid[MRVDRV_MAX_SSID_LENGTH + 1];
+	/** SSID Length */
+	t_u32 ssid_len;
+	/** WMM Capability */
+	char wmm_cap;
+	/** WPS Capability */
+	char wps_cap;
+	/** Privacy Capability - WEP/WPA/RSN */
+	char priv_cap;
+	/** HT (11N) Capability */
+	char ht_cap;
+	/** VHT (11AC) Capability */
+	char vht_cap[2];
+	/* 802.11k Capability */
+	char dot11k_cap;
+	/** 802.11r Capability */
+	char dot11r_cap;
+} wlan_ioctl_get_bss_info;
+
+/**
+ *  Structure to save of scan table info for each BSS returned
+ * in WLAN_GET_SCAN_RESP IOCTL
+ */
+struct wlan_ioctl_get_scan_list {
+	/** fixed info */
+	wlan_ioctl_get_scan_table_entry fixed_buf;
+	/** variable info - BSS info (probe resp or beacon) & IEEE IE info */
+	wlan_ioctl_get_bss_info bss_info_buf;
+	/** pointer to next node in list */
+	struct wlan_ioctl_get_scan_list *next;
+};
+
+/**
+ *  Sructure to retrieve the scan table
+ */
+typedef struct {
+    /**
+     *  - Zero based scan entry to start retrieval in command request
+     *  - Number of scans entries returned in command response
+     */
+	t_u32 scan_number;
+    /**
+     * Buffer marker for multiple wlan_ioctl_get_scan_table_entry structures.
+     *   Each struct is padded to the nearest 32 bit boundary.
+     */
+	t_u8 scan_table_entry_buf[1];
+
+} wlan_ioctl_get_scan_table_info;
+
+typedef struct {
+	t_u8 chan_number;
+		       /**< Channel Number to scan */
+	t_u8 radio_type;
+		       /**< Radio type: 'B/G' Band = 0, 'A' Band = 1 */
+	t_u8 scan_type;/**< Scan type: Active = 1, Passive = 2 */
+	t_u8 reserved;/**< Reserved */
+	t_u32 scan_time;
+		       /**< Scan duration in milliseconds; if 0 default used */
+} __ATTRIB_PACK__ wlan_ioctl_user_scan_chan;
+
+typedef struct {
+	char ssid[MRVDRV_MAX_SSID_LENGTH + 1];
+					    /**< SSID */
+	t_u8 max_len;			       /**< Maximum length of SSID */
+} __ATTRIB_PACK__ wlan_ioctl_user_scan_ssid;
+
+typedef struct {
+
+    /** Flag set to keep the previous scan table intact */
+	t_u8 keep_previous_scan;	/* Do not erase the existing scan results */
+
+    /** BSS mode to be sent in the firmware command */
+	t_u8 bss_mode;
+
+    /** Configure the number of probe requests for active chan scans */
+	t_u8 num_probes;
+
+    /** Reserved */
+	t_u8 reserved;
+
+    /** BSSID filter sent in the firmware command to limit the results */
+	t_u8 specific_bssid[ETH_ALEN];
+
+    /** SSID filter list used in the to limit the scan results */
+	wlan_ioctl_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH];
+
+    /** Variable number (fixed maximum) of channels to scan up */
+	wlan_ioctl_user_scan_chan chan_list[WLAN_IOCTL_USER_SCAN_CHAN_MAX];
+
+} __ATTRIB_PACK__ wlan_ioctl_user_scan_cfg;
+
+int process_sdcmd52rw(int argc, char *argv[]);
+int process_sdcmd53rw(int argc, char *argv[]);
+int process_setuserscan(int argc, char *argv[]);
+int process_getscantable(int argc, char *argv[]);
+int process_getscantable_idx(wlan_ioctl_get_scan_table_info *prsp_info_req);
+
+#endif /* _MLANMISC_H_ */
diff --git a/wlan_sd8897/mapp/mlanevent/Makefile b/wlan_sd8897/mapp/mlanevent/Makefile
new file mode 100644
index 0000000..ae2a463
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanevent/Makefile
@@ -0,0 +1,51 @@
+# File : mlanevent/Makefile
+#
+# Copyright (C) 2008-2017, Marvell International Ltd. All Rights Reserved
+
+# Path to the top directory of the wlan distribution
+PATH_TO_TOP = ../..
+
+# Determine how we should copy things to the install directory
+ABSPATH := $(filter /%, $(INSTALLDIR))
+RELPATH := $(filter-out /%, $(INSTALLDIR))
+INSTALLPATH := $(ABSPATH)
+ifeq ($(strip $(INSTALLPATH)),)
+INSTALLPATH := $(PATH_TO_TOP)/$(RELPATH)
+endif
+
+# Override CFLAGS for application sources, remove __ kernel namespace defines
+CFLAGS := $(filter-out -D__%, $(ccflags-y))
+# remove KERNEL include dir
+CFLAGS := $(filter-out -I$(KERNELDIR)%, $(CFLAGS))
+
+
+#CFLAGS += -DAP22 -fshort-enums
+CFLAGS += -Wall
+#ECHO = @
+LIBS = -lrt
+
+.PHONY: default tags all
+
+OBJECTS = mlanevent.o
+HEADERS = mlanevent.h
+
+TARGET = mlanevent.exe
+
+build default: $(TARGET)
+	@cp -f $(TARGET) $(INSTALLPATH)
+
+all : tags default
+
+$(TARGET): $(OBJECTS) $(HEADERS)
+	$(ECHO)$(CC) $(LIBS) -o $@ $(OBJECTS)
+
+%.o: %.c $(HEADERS)
+	$(ECHO)$(CC) $(CFLAGS) -c -o $@ $<
+
+tags:
+	ctags -R -f tags.txt
+
+distclean clean:
+	$(ECHO)$(RM) $(OBJECTS) $(TARGET)
+	$(ECHO)$(RM) tags.txt
+
diff --git a/wlan_sd8897/mapp/mlanevent/mlanevent.c b/wlan_sd8897/mapp/mlanevent/mlanevent.c
new file mode 100644
index 0000000..5397f54
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanevent/mlanevent.c
@@ -0,0 +1,2716 @@
+/** @file  mlanevent.c
+ *
+ *  @brief Program to receive events from the driver/firmware of the uAP
+ *         driver.
+ *
+ *  Copyright (C) 2008-2017, Marvell International Ltd.
+ *
+ *  This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ *  the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *  02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+/****************************************************************************
+Change log:
+    03/18/08: Initial creation
+****************************************************************************/
+
+/****************************************************************************
+        Header files
+****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/time.h>
+#include <getopt.h>
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/if.h>
+#include "mlanevent.h"
+#ifdef WIFI_DIRECT_SUPPORT
+#include <arpa/inet.h>
+#endif
+
+/****************************************************************************
+        Definitions
+****************************************************************************/
+/** Enable or disable debug outputs */
+#define DEBUG   0
+
+/****************************************************************************
+        Global variables
+****************************************************************************/
+/** Termination flag */
+int terminate_flag = 0;
+
+/****************************************************************************
+        Local functions
+****************************************************************************/
+/**
+ *  @brief Signal handler
+ *
+ *  @param sig      Received signal number
+ *  @return         N/A
+ */
+void
+sig_handler(int sig)
+{
+	printf("Stopping application.\n");
+#if DEBUG
+	printf("Process ID of process killed = %d\n", getpid());
+#endif
+	terminate_flag = 1;
+}
+
+/**
+ *  @brief Dump hex data
+ *
+ *  @param p        A pointer to data buffer
+ *  @param len      The len of data buffer
+ *  @param delim    Deliminator character
+ *  @return         Hex integer
+ */
+static void
+hexdump(void *p, t_s32 len, char delim)
+{
+	t_s32 i;
+	t_u8 *s = p;
+	for (i = 0; i < len; i++) {
+		if (i != len - 1)
+			printf("%02x%c", *s++, delim);
+		else
+			printf("%02x\n", *s);
+		if ((i + 1) % 16 == 0)
+			printf("\n");
+	}
+}
+
+/**
+ *  @brief      Hex to number
+ *
+ *  @param c    Hex value
+ *  @return     Integer value or -1
+ */
+int
+hex2num(char c)
+{
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	if (c >= 'a' && c <= 'f')
+		return c - 'a' + 10;
+	if (c >= 'A' && c <= 'F')
+		return c - 'A' + 10;
+
+	return -1;
+}
+
+/**
+ *  @brief      Check hex string
+ *
+ *  @param hex      A pointer to hex string
+ *  @return         0 or MLAN_EVENT_FAILURE
+ */
+int
+ishexstring(void *hex)
+{
+	int i, a;
+	char *p = hex;
+	int len = strlen(p);
+	if (!strncasecmp("0x", p, 2)) {
+		p += 2;
+		len -= 2;
+	}
+	for (i = 0; i < len; i++) {
+		a = hex2num(*p);
+		if (a < 0)
+			return MLAN_EVENT_FAILURE;
+		p++;
+	}
+	return 0;
+}
+
+/**
+ *    @brief Convert char to hex integer
+ *
+ *    @param chr          Char
+ *    @return             Hex integer
+ */
+unsigned char
+hexc2bin(char chr)
+{
+	if (chr >= '0' && chr <= '9')
+		chr -= '0';
+	else if (chr >= 'A' && chr <= 'F')
+		chr -= ('A' - 10);
+	else if (chr >= 'a' && chr <= 'f')
+		chr -= ('a' - 10);
+
+	return chr;
+}
+
+/**
+ *    @brief Convert string to hex integer
+ *
+ *    @param s            A pointer string buffer
+ *    @return             Hex integer
+ */
+unsigned int
+a2hex(char *s)
+{
+	unsigned int val = 0;
+	if (!strncasecmp("0x", s, 2)) {
+		s += 2;
+	}
+	while (*s && isxdigit(*s)) {
+		val = (val << 4) + hexc2bin(*s++);
+	}
+	return val;
+}
+
+/**
+ *  @brief Prints a MAC address in colon separated form from raw data
+ *
+ *  @param raw      A pointer to the hex data buffer
+ *  @return         N/A
+ */
+void
+print_mac(t_u8 *raw)
+{
+	printf("%02x:%02x:%02x:%02x:%02x:%02x", (unsigned int)raw[0],
+	       (unsigned int)raw[1], (unsigned int)raw[2], (unsigned int)raw[3],
+	       (unsigned int)raw[4], (unsigned int)raw[5]);
+	return;
+}
+
+/**
+ *  @brief Print usage information
+ *
+ *  @return         N/A
+ */
+void
+print_usage(void)
+{
+	printf("\n");
+	printf("Usage : mlanevent.exe [-v] [-h]\n");
+	printf("    -v               : Print version information\n");
+	printf("    -h               : Print help information\n");
+	printf("    -i               : Specify device number from 0 to %d\n",
+	       MAX_NO_OF_DEVICES - 1);
+	printf("                       0xff for all devices\n");
+	printf("\n");
+}
+
+/**
+ *  @brief Parse and print STA deauthentication event data
+ *
+ *  @param buffer   Pointer to received event buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_event_sta_deauth(t_u8 *buffer, t_u16 size)
+{
+	eventbuf_sta_deauth *event_body = NULL;
+
+	if (size < sizeof(eventbuf_sta_deauth)) {
+		printf("ERR:Event buffer too small!\n");
+		return;
+	}
+	event_body = (eventbuf_sta_deauth *)buffer;
+	event_body->reason_code = uap_le16_to_cpu(event_body->reason_code);
+	printf("EVENT: STA_DEAUTH\n");
+	printf("Deauthenticated STA MAC: ");
+	print_mac(event_body->sta_mac_address);
+	printf("\nReason: ");
+	switch (event_body->reason_code) {
+	case 1:
+		printf("Client station leaving the network\n");
+		break;
+	case 2:
+		printf("Client station aged out\n");
+		break;
+	case 3:
+		printf("Client station deauthenticated by user's request\n");
+		break;
+	case 4:
+		printf("Client station authentication failure\n");
+		break;
+	case 5:
+		printf("Client station association failure\n");
+		break;
+	case 6:
+		printf("Client mac address is blocked by ACL filter\n");
+		break;
+	case 7:
+		printf("Client station table is full\n");
+		break;
+	case 8:
+		printf("Client 4-way handshake timeout\n");
+		break;
+	case 9:
+		printf("Client group key handshake timeout\n");
+		break;
+	default:
+		printf("Unspecified\n");
+		break;
+	}
+	return;
+}
+
+/**
+ *  @brief Parse and print WEP ICV error event data
+ *
+ *  @param buffer   Pointer to received event buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_event_wep_icv_error(t_u8 *buffer, t_u16 size)
+{
+	int i = 0;
+	eventbuf_wep_icv_error *event_body = NULL;
+
+	if (size < sizeof(eventbuf_wep_icv_error)) {
+		printf("ERR:Event buffer too small!\n");
+		return;
+	}
+	event_body = (eventbuf_wep_icv_error *)buffer;
+	event_body->reason_code = uap_le16_to_cpu(event_body->reason_code);
+	printf("EVENT: WEP_ICV_ERROR\n");
+	printf("Deauthenticated STA MAC: ");
+	print_mac(event_body->sta_mac_address);
+	printf("WEP key index = %d\n", event_body->wep_key_index);
+	printf("WEP key length = %d\n", event_body->wep_key_length);
+	printf("WEP key : \n");
+	for (i = 0; i < event_body->wep_key_length; i++) {
+		printf("%02x ", event_body->wep_key[i]);
+	}
+	printf("\n");
+	return;
+}
+
+/**
+ *  @brief Prints mgmt frame
+ *
+ *  @param mgmt_tlv A pointer to mgmt_tlv
+ *  @param tlv_len  Length of tlv payload
+ *  @return         N/A
+ */
+void
+print_mgmt_frame(MrvlIETypes_MgmtFrameSet_t *mgmt_tlv, int tlv_len)
+{
+	IEEEtypes_AssocRqst_t *assoc_req = NULL;
+	IEEEtypes_ReAssocRqst_t *reassoc_req = NULL;
+	IEEEtypes_AssocRsp_t *assoc_resp = NULL;
+	t_u16 frm_ctl = 0;
+	printf("\nMgmt Frame:\n");
+	memcpy(&frm_ctl, &mgmt_tlv->frame_control, sizeof(t_u16));
+	printf("FrameControl: 0x%x\n", frm_ctl);
+	if (mgmt_tlv->frame_control.type != 0) {
+		printf("Frame type=%d subtype=%d:\n",
+		       mgmt_tlv->frame_control.type,
+		       mgmt_tlv->frame_control.sub_type);
+		hexdump(mgmt_tlv->frame_contents, tlv_len - sizeof(t_u16), ' ');
+		return;
+	}
+	switch (mgmt_tlv->frame_control.sub_type) {
+	case SUBTYPE_ASSOC_REQUEST:
+		printf("Assoc Request:\n");
+		assoc_req = (IEEEtypes_AssocRqst_t *)mgmt_tlv->frame_contents;
+		printf("CapInfo: 0x%x  ListenInterval: 0x%x \n",
+		       uap_le16_to_cpu(assoc_req->cap_info),
+		       uap_le16_to_cpu(assoc_req->listen_interval));
+		printf("AssocReqIE:\n");
+		hexdump(assoc_req->ie_buffer,
+			tlv_len - sizeof(IEEEtypes_AssocRqst_t)
+			- sizeof(IEEEtypes_FrameCtl_t), ' ');
+		break;
+	case SUBTYPE_REASSOC_REQUEST:
+		printf("ReAssoc Request:\n");
+		reassoc_req =
+			(IEEEtypes_ReAssocRqst_t *)mgmt_tlv->frame_contents;
+		printf("CapInfo: 0x%x  ListenInterval: 0x%x \n",
+		       uap_le16_to_cpu(reassoc_req->cap_info),
+		       uap_le16_to_cpu(reassoc_req->listen_interval));
+		printf("Current AP address: ");
+		print_mac(reassoc_req->current_ap_addr);
+		printf("\nReAssocReqIE:\n");
+		hexdump(reassoc_req->ie_buffer,
+			tlv_len - sizeof(IEEEtypes_ReAssocRqst_t)
+			- sizeof(IEEEtypes_FrameCtl_t), ' ');
+		break;
+	case SUBTYPE_ASSOC_RESPONSE:
+	case SUBTYPE_REASSOC_RESPONSE:
+		if (mgmt_tlv->frame_control.sub_type == SUBTYPE_ASSOC_RESPONSE)
+			printf("Assoc Response:\n");
+		else
+			printf("ReAssoc Response:\n");
+		assoc_resp = (IEEEtypes_AssocRsp_t *)mgmt_tlv->frame_contents;
+		printf("CapInfo: 0x%x  StatusCode: %d  AID: 0x%x \n",
+		       uap_le16_to_cpu(assoc_resp->cap_info),
+		       (int)(uap_le16_to_cpu(assoc_resp->status_code)),
+		       uap_le16_to_cpu(assoc_resp->aid) & 0x3fff);
+		break;
+	default:
+		printf("Frame subtype = %d:\n",
+		       mgmt_tlv->frame_control.sub_type);
+		hexdump(mgmt_tlv->frame_contents, tlv_len - sizeof(t_u16), ' ');
+		break;
+	}
+	return;
+}
+
+/**
+ *  @brief Parse and print RSN connect event data
+ *
+ *  @param buffer   Pointer to received buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_event_rsn_connect(t_u8 *buffer, t_u16 size)
+{
+	int tlv_buf_left = size;
+	t_u16 tlv_type, tlv_len;
+	tlvbuf_header *tlv = NULL;
+	eventbuf_rsn_connect *event_body = NULL;
+	if (size < sizeof(eventbuf_rsn_connect)) {
+		printf("ERR:Event buffer too small!\n");
+		return;
+	}
+	event_body = (eventbuf_rsn_connect *)buffer;
+	printf("EVENT: RSN_CONNECT\n");
+	printf("Station MAC: ");
+	print_mac(event_body->sta_mac_address);
+	printf("\n");
+	tlv_buf_left = size - sizeof(eventbuf_rsn_connect);
+	if (tlv_buf_left < (int)sizeof(tlvbuf_header))
+		return;
+	tlv = (tlvbuf_header *)(buffer + sizeof(eventbuf_rsn_connect));
+
+	while (tlv_buf_left >= (int)sizeof(tlvbuf_header)) {
+		tlv_type = uap_le16_to_cpu(tlv->type);
+		tlv_len = uap_le16_to_cpu(tlv->len);
+		if ((sizeof(tlvbuf_header) + tlv_len) >
+		    (unsigned int)tlv_buf_left) {
+			printf("wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", tlv_len,
+			       tlv_buf_left);
+			break;
+		}
+		switch (tlv_type) {
+		case IEEE_WPA_IE:
+			printf("WPA IE:\n");
+			hexdump((t_u8 *)tlv + sizeof(tlvbuf_header), tlv_len,
+				' ');
+			break;
+		case IEEE_RSN_IE:
+			printf("RSN IE:\n");
+			hexdump((t_u8 *)tlv + sizeof(tlvbuf_header), tlv_len,
+				' ');
+			break;
+		default:
+			printf("unknown tlv: %d\n", tlv_type);
+			break;
+		}
+		tlv_buf_left -= (sizeof(tlvbuf_header) + tlv_len);
+		tlv = (tlvbuf_header *)((t_u8 *)tlv + tlv_len +
+					sizeof(tlvbuf_header));
+	}
+	return;
+}
+
+/**
+ *  @brief Parse and print STA associate event data
+ *
+ *  @param buffer   Pointer to received buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_event_sta_assoc(t_u8 *buffer, t_u16 size)
+{
+	int tlv_buf_left = size;
+	t_u16 tlv_type, tlv_len;
+	tlvbuf_header *tlv = NULL;
+	MrvlIEtypes_WapiInfoSet_t *wapi_tlv = NULL;
+	MrvlIETypes_MgmtFrameSet_t *mgmt_tlv = NULL;
+	eventbuf_sta_assoc *event_body = NULL;
+	if (size < sizeof(eventbuf_sta_assoc)) {
+		printf("ERR:Event buffer too small!\n");
+		return;
+	}
+	event_body = (eventbuf_sta_assoc *)buffer;
+	printf("EVENT: STA_ASSOCIATE\n");
+	printf("Associated STA MAC: ");
+	print_mac(event_body->sta_mac_address);
+	printf("\n");
+	tlv_buf_left = size - sizeof(eventbuf_sta_assoc);
+	if (tlv_buf_left < (int)sizeof(tlvbuf_header))
+		return;
+	tlv = (tlvbuf_header *)(buffer + sizeof(eventbuf_sta_assoc));
+
+	while (tlv_buf_left >= (int)sizeof(tlvbuf_header)) {
+		tlv_type = uap_le16_to_cpu(tlv->type);
+		tlv_len = uap_le16_to_cpu(tlv->len);
+		if ((sizeof(tlvbuf_header) + tlv_len) >
+		    (unsigned int)tlv_buf_left) {
+			printf("wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", tlv_len,
+			       tlv_buf_left);
+			break;
+		}
+		switch (tlv_type) {
+		case MRVL_WAPI_INFO_TLV_ID:
+			wapi_tlv = (MrvlIEtypes_WapiInfoSet_t *)tlv;
+			printf("WAPI Multicast PN:\n");
+			hexdump(wapi_tlv->multicast_PN, tlv_len, ' ');
+			break;
+		case MRVL_MGMT_FRAME_TLV_ID:
+			mgmt_tlv = (MrvlIETypes_MgmtFrameSet_t *)tlv;
+			print_mgmt_frame(mgmt_tlv, tlv_len);
+			break;
+		default:
+			printf("unknown tlv: %d\n", tlv_type);
+			break;
+		}
+		tlv_buf_left -= (sizeof(tlvbuf_header) + tlv_len);
+		tlv = (tlvbuf_header *)((t_u8 *)tlv + tlv_len +
+					sizeof(tlvbuf_header));
+	}
+	return;
+}
+
+/**
+ *  @brief Parse and print BSS start event data
+ *
+ *  @param buffer   Pointer to received buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_event_bss_start(t_u8 *buffer, t_u16 size)
+{
+	eventbuf_bss_start *event_body = NULL;
+	int tlv_buf_left = size;
+	t_u16 tlv_type, tlv_len;
+	tlvbuf_header *tlv = NULL;
+	tlvbuf_channel_config *channel_tlv = NULL;
+
+	if (size < sizeof(eventbuf_bss_start)) {
+		printf("ERR:Event buffer too small!\n");
+		return;
+	}
+	event_body = (eventbuf_bss_start *)buffer;
+	printf("EVENT: BSS_START ");
+	printf("BSS MAC: ");
+	print_mac(event_body->ap_mac_address);
+	printf("\n");
+	tlv_buf_left = size - sizeof(eventbuf_bss_start);
+	if (tlv_buf_left < (int)sizeof(tlvbuf_header))
+		return;
+	tlv = (tlvbuf_header *)(buffer + sizeof(eventbuf_bss_start));
+
+	while (tlv_buf_left >= (int)sizeof(tlvbuf_header)) {
+		tlv_type = uap_le16_to_cpu(tlv->type);
+		tlv_len = uap_le16_to_cpu(tlv->len);
+		if ((sizeof(tlvbuf_header) + tlv_len) >
+		    (unsigned int)tlv_buf_left) {
+			printf("wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", tlv_len,
+			       tlv_buf_left);
+			break;
+		}
+		switch (tlv_type) {
+		case MRVL_CHANNELCONFIG_TLV_ID:
+			channel_tlv = (tlvbuf_channel_config *)tlv;
+			printf("Channel = %d\n", channel_tlv->chan_number);
+			printf("Band = %s\n",
+			       (channel_tlv->bandcfg.chanBand ==
+				BAND_5GHZ) ? "5GHz" : "2.4GHz");
+			printf("Channel Select Mode = %s\n",
+			       (channel_tlv->bandcfg.scanMode ==
+				SCAN_MODE_ACS) ? "ACS" : "Manual");
+			if (channel_tlv->bandcfg.chan2Offset == SEC_CHAN_NONE)
+				printf("no secondary channel\n");
+			else if (channel_tlv->bandcfg.chan2Offset ==
+				 SEC_CHAN_ABOVE)
+				printf("secondary channel is above primary channel\n");
+			else if (channel_tlv->bandcfg.chan2Offset ==
+				 SEC_CHAN_BELOW)
+				printf("secondary channel is below primary channel\n");
+			break;
+		default:
+#if DEBUG
+			printf("unknown tlv: %d\n", tlv_type);
+#endif
+			break;
+		}
+		tlv_buf_left -= (sizeof(tlvbuf_header) + tlv_len);
+		tlv = (tlvbuf_header *)((t_u8 *)tlv + tlv_len +
+					sizeof(tlvbuf_header));
+	}
+
+	return;
+}
+
+#ifdef WIFI_DIRECT_SUPPORT
+/**
+ *  @brief Print WIFI_WPS IE elements from event payload
+ *
+ *  @param buffer   Pointer to received buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_wifi_wps_ie_elements(t_u8 *buffer, t_u16 size)
+{
+	t_u8 *ptr = buffer;
+	t_u8 *array_ptr;
+	int i;
+	t_u16 wps_len = 0, wps_type = 0;
+	t_u16 ie_len_wps = size;
+
+	while (ie_len_wps > sizeof(tlvbuf_wps_ie)) {
+		memcpy(&wps_type, ptr, sizeof(t_u16));
+		memcpy(&wps_len, ptr + 2, sizeof(t_u16));
+		endian_convert_tlv_wps_header_in(wps_type, wps_len);
+		switch (wps_type) {
+		case SC_Version:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				printf("\t WPS Version = 0x%2x\n",
+				       *(wps_tlv->data));
+			}
+			break;
+		case SC_Simple_Config_State:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				printf("\t WPS setupstate = 0x%x\n",
+				       *(wps_tlv->data));
+			}
+			break;
+		case SC_Request_Type:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				printf("\t WPS RequestType = 0x%x\n",
+				       *(wps_tlv->data));
+			}
+			break;
+		case SC_Response_Type:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				printf("\t WPS ResponseType = 0x%x\n",
+				       *(wps_tlv->data));
+			}
+			break;
+		case SC_Config_Methods:
+			{
+				t_u16 wps_config_methods = 0;
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				memcpy(&wps_config_methods, wps_tlv->data,
+				       sizeof(t_u16));
+				wps_config_methods = ntohs(wps_config_methods);
+				printf("\t WPS SpecConfigMethods = 0x%x\n",
+				       wps_config_methods);
+			}
+			break;
+		case SC_UUID_E:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				array_ptr = wps_tlv->data;
+				printf("\t WPS UUID = ");
+				for (i = 0; i < wps_len; i++)
+					printf("0x%02X ", *array_ptr++);
+				printf("\n");
+			}
+			break;
+		case SC_Primary_Device_Type:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				array_ptr = wps_tlv->data;
+				printf("\t WPS Primary Device Type = ");
+				for (i = 0; i < wps_len; i++)
+					printf("0x%02X ", *array_ptr++);
+				printf("\n");
+			}
+			break;
+		case SC_RF_Band:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				printf("\t WPS RF Band = 0x%x\n",
+				       *(wps_tlv->data));
+			}
+			break;
+		case SC_Association_State:
+			{
+				t_u16 wps_association_state = 0;
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				memcpy(&wps_association_state, wps_tlv->data,
+				       sizeof(t_u16));
+				wps_association_state =
+					ntohs(wps_association_state);
+				printf("\t WPS Association State = 0x%x\n",
+				       wps_association_state);
+			}
+			break;
+		case SC_Configuration_Error:
+			{
+				t_u16 wps_configuration_error = 0;
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				memcpy(&wps_configuration_error, wps_tlv->data,
+				       sizeof(t_u16));
+				wps_configuration_error =
+					ntohs(wps_configuration_error);
+				printf("\t WPS Configuration Error = 0x%x\n",
+				       wps_configuration_error);
+			}
+			break;
+		case SC_Device_Password_ID:
+			{
+				t_u16 wps_device_password_id = 0;
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				memcpy(&wps_device_password_id, wps_tlv->data,
+				       sizeof(t_u16));
+				wps_device_password_id =
+					ntohs(wps_device_password_id);
+				printf("\t WPS Device Password ID = 0x%x\n",
+				       wps_device_password_id);
+			}
+			break;
+		case SC_Device_Name:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				array_ptr = wps_tlv->data;
+				printf("\t WPS Device Name = ");
+				for (i = 0; i < wps_len; i++)
+					printf("%c", *array_ptr++);
+				printf("\n");
+			}
+			break;
+		case SC_Manufacturer:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				array_ptr = wps_tlv->data;
+				printf("\t WPS Manufacturer = ");
+				for (i = 0; i < wps_len; i++)
+					printf("%c", *array_ptr++);
+				printf("\n");
+			}
+			break;
+		case SC_Model_Name:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				array_ptr = wps_tlv->data;
+				printf("\t WPS Model Name = ");
+				for (i = 0; i < wps_len; i++)
+					printf("%c", *array_ptr++);
+				printf("\n");
+			}
+			break;
+		case SC_Model_Number:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				array_ptr = wps_tlv->data;
+				printf("\t WPS Model Number = ");
+				for (i = 0; i < wps_len; i++)
+					printf("%c", *array_ptr++);
+				printf("\n");
+			}
+			break;
+		case SC_Serial_Number:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				array_ptr = wps_tlv->data;
+				printf("\t WPS Serial Number = ");
+				for (i = 0; i < wps_len; i++)
+					printf("%c", *array_ptr++);
+				printf("\n");
+			}
+			break;
+		case SC_Vendor_Extension:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				array_ptr = wps_tlv->data;
+				printf("\t WPS 2.0 Vendor Extension = ");
+				for (i = 0; i < wps_len; i++)
+					printf("%x", *array_ptr++);
+				printf("\n");
+			}
+			break;
+		case SC_Selected_Registrar:
+			{
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				printf("\t Selected Registrar = %d\n",
+				       *(wps_tlv->data));
+			}
+			break;
+		case SC_SelectedRegistrarConfigMethods:
+			{
+				t_u16 sr_config_methods = 0;
+				tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr;
+				memcpy(&sr_config_methods, wps_tlv->data,
+				       sizeof(t_u16));
+				sr_config_methods = ntohs(sr_config_methods);
+				printf("\t Selected Registrar Configuration Methods = 0x%x\n", sr_config_methods);
+			}
+			break;
+		default:
+			printf("unknown ie=0x%x, len=%d\n", wps_type, wps_len);
+			break;
+		}
+		ptr += wps_len + sizeof(tlvbuf_wps_ie);
+		/* Take care of error condition */
+		if (wps_len + sizeof(tlvbuf_wps_ie) <= ie_len_wps)
+			ie_len_wps -= wps_len + sizeof(tlvbuf_wps_ie);
+		else
+			ie_len_wps = 0;
+	}
+}
+
+/**
+ *  @brief Print WIFIDIRECT IE elements from event payload
+ *
+ *  @param buffer   Pointer to received buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_wifidirect_ie_elements(t_u8 *buffer, t_u16 size)
+{
+	t_u8 *ptr = buffer;
+	t_u8 *array_ptr, *orig_ptr = NULL;
+	int i;
+	static t_u16 len = 0;
+	static t_u16 saved_len = 0;
+	static t_u16 pending_len = 0;
+	static t_u8 type = 0;
+	static t_u8 next_byte = WIFIDIRECT_OVERLAP_TYPE;
+	static t_u8 saved_data[WIFI_IE_MAX_PAYLOAD] = { 0 };
+	t_u16 temp;
+	t_u16 left_len = size;
+
+	while (left_len > 0) {
+		if (next_byte == WIFIDIRECT_OVERLAP_TYPE) {
+			type = *ptr;
+			next_byte = WIFIDIRECT_OVERLAP_LEN;
+			left_len--;
+			ptr++;
+		}
+		if (left_len >= sizeof(len) &&
+		    next_byte == WIFIDIRECT_OVERLAP_LEN) {
+			memcpy(&len, ptr, sizeof(t_u16));
+			len = uap_le16_to_cpu(len);
+			next_byte = WIFIDIRECT_OVERLAP_DATA;
+			left_len -= sizeof(t_u16);
+			ptr += sizeof(t_u16);
+
+			/* case when Type, Len in one frame and data in next */
+			if (left_len == 0) {
+				memcpy(saved_data,
+				       ptr - WIFIDIRECT_IE_HEADER_LEN,
+				       WIFIDIRECT_IE_HEADER_LEN);
+				saved_len = WIFIDIRECT_IE_HEADER_LEN;
+				pending_len = len;
+			}
+		}
+		if (left_len > 0 && next_byte == WIFIDIRECT_OVERLAP_DATA) {
+			/* copy next data */
+			if (pending_len > 0 &&
+			    (left_len <= (WIFI_IE_MAX_PAYLOAD - saved_len))) {
+				memcpy(saved_data + saved_len, ptr,
+				       pending_len);
+				orig_ptr = ptr;
+				ptr = saved_data;
+			} else {
+				ptr -= WIFIDIRECT_IE_HEADER_LEN;
+			}
+
+			if (!pending_len && !orig_ptr && left_len < len) {
+				/* save along with type and len */
+				memcpy(saved_data,
+				       ptr - WIFIDIRECT_IE_HEADER_LEN,
+				       left_len + WIFIDIRECT_IE_HEADER_LEN);
+				saved_len = left_len + WIFIDIRECT_IE_HEADER_LEN;
+				pending_len = len - left_len;
+				break;
+			}
+			switch (type) {
+			case TLV_TYPE_WIFIDIRECT_DEVICE_ID:
+				{
+					tlvbuf_wifidirect_device_id
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_device_id *)
+						ptr;
+					printf("\t Device ID - ");
+					print_mac(wifidirect_tlv->
+						  dev_mac_address);
+					printf("\n");
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_CAPABILITY:
+				{
+					tlvbuf_wifidirect_capability
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_capability *)
+						ptr;
+					printf("\t Device capability = %d\n",
+					       (int)wifidirect_tlv->
+					       dev_capability);
+					printf("\t Group capability = %d\n",
+					       (int)wifidirect_tlv->
+					       group_capability);
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_GROUPOWNER_INTENT:
+				{
+					tlvbuf_wifidirect_group_owner_intent
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_group_owner_intent
+						 *)ptr;
+					printf("\t Group owner intent = %d\n",
+					       (int)wifidirect_tlv->dev_intent);
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_MANAGEABILITY:
+				{
+					tlvbuf_wifidirect_manageability
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_manageability
+						 *)ptr;
+					printf("\t Manageability = %d\n",
+					       (int)wifidirect_tlv->
+					       manageability);
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_INVITATION_FLAG:
+				{
+					tlvbuf_wifidirect_invitation_flag
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_invitation_flag
+						 *)ptr;
+					printf("\t Invitation Flag = %d\n",
+					       (int)wifidirect_tlv->
+					       invitation_flag &
+					       INVITATION_FLAG_MASK);
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_CHANNEL_LIST:
+				{
+					tlvbuf_wifidirect_channel_list
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_channel_list
+						 *)ptr;
+					chan_entry *temp_ptr;
+					printf("\t Country String %c%c",
+					       wifidirect_tlv->
+					       country_string[0],
+					       wifidirect_tlv->
+					       country_string[1]);
+					if (isalpha
+					    (wifidirect_tlv->country_string[2]))
+						printf("%c",
+						       wifidirect_tlv->
+						       country_string[2]);
+					printf("\n");
+					temp_ptr =
+						(chan_entry *)wifidirect_tlv->
+						wifidirect_chan_entry_list;
+					temp = uap_le16_to_cpu(wifidirect_tlv->
+							       length) -
+						(sizeof
+						 (tlvbuf_wifidirect_channel_list)
+						 - WIFIDIRECT_IE_HEADER_LEN);
+					while (temp) {
+						printf("\t Regulatory_class = %d\n", (int)(temp_ptr->regulatory_class));
+						printf("\t No of channels = %d\n", (int)temp_ptr->num_of_channels);
+						printf("\t Channel list = ");
+						for (i = 0;
+						     i <
+						     temp_ptr->num_of_channels;
+						     i++) {
+							printf("%d ",
+							       *(temp_ptr->
+								 chan_list +
+								 i));
+						}
+						printf("\n");
+						temp -= sizeof(chan_entry) +
+							temp_ptr->
+							num_of_channels;
+						temp_ptr =
+							(chan_entry *)((t_u8 *)
+								       temp_ptr
+								       +
+								       sizeof
+								       (chan_entry)
+								       +
+								       temp_ptr->
+								       num_of_channels);
+					}
+					printf("\n");
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_NOTICE_OF_ABSENCE:
+				{
+					tlvbuf_wifidirect_notice_of_absence
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_notice_of_absence
+						 *)ptr;
+					noa_descriptor *temp_ptr;
+					printf("\t Instance of Notice of absence timing %d\n", (int)wifidirect_tlv->noa_index);
+					printf("\t CTWindow and Opportunistic power save parameters %d\n", (int)wifidirect_tlv->ctwindow_opp_ps);
+					temp_ptr =
+						(noa_descriptor *)
+						wifidirect_tlv->
+						wifidirect_noa_descriptor_list;
+					temp = uap_le16_to_cpu(wifidirect_tlv->
+							       length) -
+						(sizeof
+						 (tlvbuf_wifidirect_notice_of_absence)
+						 - WIFIDIRECT_IE_HEADER_LEN);
+					while (temp) {
+						printf("\t Count or Type = %d\n", (int)temp_ptr->count_type);
+						printf("\t Duration = %dms\n",
+						       uap_le32_to_cpu
+						       (temp_ptr->duration));
+						printf("\t Interval = %dms\n",
+						       uap_le32_to_cpu
+						       (temp_ptr->interval));
+						printf("\t Start Time = %d\n",
+						       uap_le32_to_cpu
+						       (temp_ptr->start_time));
+						printf("\n");
+						temp_ptr +=
+							sizeof(noa_descriptor);
+						temp -= sizeof(noa_descriptor);
+					}
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_DEVICE_INFO:
+				{
+					tlvbuf_wifidirect_device_info
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_device_info
+						 *)ptr;
+					printf("\t Device address - ");
+					print_mac(wifidirect_tlv->dev_address);
+					printf("\n");
+					printf("\t Config methods - 0x%02X\n",
+					       ntohs(wifidirect_tlv->
+						     config_methods));
+					printf("\t Primay device type = %02d-%02X%02X%02X%02X-%02d\n", (int)ntohs(wifidirect_tlv->primary_category), (int)wifidirect_tlv->primary_oui[0], (int)wifidirect_tlv->primary_oui[1], (int)wifidirect_tlv->primary_oui[2], (int)wifidirect_tlv->primary_oui[3], (int)ntohs(wifidirect_tlv->primary_subcategory));
+					printf("\t Secondary Device Count = %d\n", (int)wifidirect_tlv->secondary_dev_count);
+					array_ptr =
+						wifidirect_tlv->
+						secondary_dev_info;
+					for (i = 0;
+					     i <
+					     wifidirect_tlv->
+					     secondary_dev_count; i++) {
+						memcpy(&temp, array_ptr,
+						       sizeof(t_u16));
+						printf("\t Secondary device type = %02d-", ntohs(temp));
+						array_ptr += sizeof(temp);
+						printf("%02X%02X%02X%02X",
+						       array_ptr[0],
+						       array_ptr[1],
+						       array_ptr[2],
+						       array_ptr[3]);
+						array_ptr += 4;
+						memcpy(&temp, array_ptr,
+						       sizeof(t_u16));
+						printf("-%02d\n", ntohs(temp));
+						array_ptr += sizeof(temp);
+					}
+					/* display device name */
+					array_ptr =
+						wifidirect_tlv->device_name +
+						wifidirect_tlv->
+						secondary_dev_count *
+						WPS_DEVICE_TYPE_LEN;
+					if (*(t_u16 *)
+					    (((t_u8 *)(&wifidirect_tlv->
+						       device_name_len)) +
+					     wifidirect_tlv->
+					     secondary_dev_count *
+					     WPS_DEVICE_TYPE_LEN))
+						printf("\t Device Name =  ");
+					memcpy(&temp,
+					       (((t_u8 *)(&wifidirect_tlv->
+							  device_name_len)) +
+						wifidirect_tlv->
+						secondary_dev_count *
+						WPS_DEVICE_TYPE_LEN),
+					       sizeof(t_u16));
+					temp = ntohs(temp);
+					for (i = 0; i < temp; i++)
+						printf("%c", *array_ptr++);
+					printf("\n");
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_GROUP_INFO:
+				{
+					tlvbuf_wifidirect_group_info
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_group_info *)
+						ptr;
+					t_u8 wifidirect_client_dev_length;
+					wifidirect_client_dev_info *temp_ptr;
+					temp_ptr =
+						(wifidirect_client_dev_info *)
+						wifidirect_tlv->
+						wifidirect_client_dev_list;
+					if (temp_ptr == NULL)
+						break;
+					wifidirect_client_dev_length =
+						temp_ptr->dev_length;
+					temp = uap_le16_to_cpu(wifidirect_tlv->
+							       length) -
+						wifidirect_client_dev_length;
+					while (temp) {
+
+						printf("\t Group WifiDirect Client Device address - ");
+						print_mac(temp_ptr->
+							  wifidirect_dev_address);
+						printf("\n");
+						printf("\t Group WifiDirect Client Interface address - ");
+						print_mac(temp_ptr->
+							  wifidirect_intf_address);
+						printf("\n");
+						printf("\t Group WifiDirect Client Device capability = %d\n", (int)temp_ptr->wifidirect_dev_capability);
+						printf("\t Group WifiDirect Client Config methods - 0x%02X\n", ntohs(temp_ptr->config_methods));
+						printf("\t Group WifiDirect Client Primay device type = %02d-%02X%02X%02X%02X-%02d\n", (int)ntohs(temp_ptr->primary_category), (int)temp_ptr->primary_oui[0], (int)temp_ptr->primary_oui[1], (int)temp_ptr->primary_oui[2], (int)temp_ptr->primary_oui[3], (int)ntohs(temp_ptr->primary_subcategory));
+						printf("\t Group WifiDirect Client Secondary Device Count = %d\n", (int)temp_ptr->wifidirect_secondary_dev_count);
+						array_ptr =
+							temp_ptr->
+							wifidirect_secondary_dev_info;
+						for (i = 0;
+						     i <
+						     temp_ptr->
+						     wifidirect_secondary_dev_count;
+						     i++) {
+							memcpy(&temp, array_ptr,
+							       sizeof(t_u16));
+							printf("\t Group WifiDirect Client Secondary device type = %02d-", ntohs(temp));
+							array_ptr +=
+								sizeof(temp);
+							printf("%02X%02X%02X%02X", array_ptr[0], array_ptr[1], array_ptr[2], array_ptr[3]);
+							array_ptr += 4;
+							memcpy(&temp, array_ptr,
+							       sizeof(t_u16));
+							printf("-%02d\n",
+							       ntohs(temp));
+							array_ptr +=
+								sizeof(temp);
+						}
+						/* display device name */
+						array_ptr =
+							temp_ptr->
+							wifidirect_device_name +
+							temp_ptr->
+							wifidirect_secondary_dev_count
+							* WPS_DEVICE_TYPE_LEN;
+						printf("\t Group WifiDirect Device Name =  ");
+						memcpy(&temp,
+						       (((t_u8 *)(&temp_ptr->
+								  wifidirect_device_name_len))
+							+
+							temp_ptr->
+							wifidirect_secondary_dev_count
+							* WPS_DEVICE_TYPE_LEN),
+						       sizeof(t_u16));
+						temp = ntohs(temp);
+						for (i = 0; i < temp; i++)
+							printf("%c",
+							       *array_ptr++);
+						printf("\n");
+						temp_ptr +=
+							wifidirect_client_dev_length;
+						temp -= wifidirect_client_dev_length;
+						if (temp_ptr)
+							wifidirect_client_dev_length
+								=
+								temp_ptr->
+								dev_length;
+					}
+					printf("\n");
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_GROUP_ID:
+				{
+					tlvbuf_wifidirect_group_id
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_group_id *)
+						ptr;
+					printf("\t Group address - ");
+					print_mac(wifidirect_tlv->
+						  group_address);
+					printf("\n");
+					printf("\t Group ssid =  ");
+					for (i = 0;
+					     (unsigned int)i <
+					     uap_le16_to_cpu(wifidirect_tlv->
+							     length)
+					     -
+					     (sizeof(tlvbuf_wifidirect_group_id)
+					      - WIFIDIRECT_IE_HEADER_LEN); i++)
+						printf("%c",
+						       wifidirect_tlv->
+						       group_ssid[i]);
+					printf("\n");
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_GROUP_BSS_ID:
+				{
+					tlvbuf_wifidirect_group_bss_id
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_group_bss_id
+						 *)ptr;
+					printf("\t Group BSS Id - ");
+					print_mac(wifidirect_tlv->group_bssid);
+					printf("\n");
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_INTERFACE:
+				{
+					tlvbuf_wifidirect_interface
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_interface *)
+						ptr;
+					printf("\t Interface Id - ");
+					print_mac(wifidirect_tlv->interface_id);
+					printf("\t Interface count = %d",
+					       (int)wifidirect_tlv->
+					       interface_count);
+					for (i = 0;
+					     i <
+					     wifidirect_tlv->interface_count;
+					     i++) {
+						printf("\n\t Interface address [%d]", i + 1);
+						print_mac(&wifidirect_tlv->
+							  interface_idlist[i *
+									   ETH_ALEN]);
+					}
+					printf("\n");
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_CHANNEL:
+				{
+					tlvbuf_wifidirect_channel
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_channel *)
+						ptr;
+					printf("\t Listen Channel Country String %c%c", wifidirect_tlv->country_string[0], wifidirect_tlv->country_string[1]);
+					if (isalpha
+					    (wifidirect_tlv->country_string[2]))
+						printf("%c",
+						       wifidirect_tlv->
+						       country_string[2]);
+					printf("\n");
+					printf("\t Listen Channel regulatory class = %d\n", (int)wifidirect_tlv->regulatory_class);
+					printf("\t Listen Channel number = %d\n", (int)wifidirect_tlv->channel_number);
+				}
+				break;
+
+			case TLV_TYPE_WIFIDIRECT_OPCHANNEL:
+				{
+					tlvbuf_wifidirect_channel
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_channel *)
+						ptr;
+					printf("\t Operating Channel Country String %c%c", wifidirect_tlv->country_string[0], wifidirect_tlv->country_string[1]);
+					if (isalpha
+					    (wifidirect_tlv->country_string[2]))
+						printf("%c",
+						       wifidirect_tlv->
+						       country_string[2]);
+					printf("\n");
+					printf("\t Operating Channel regulatory class = %d\n", (int)wifidirect_tlv->regulatory_class);
+					printf("\t Operating Channel number = %d\n", (int)wifidirect_tlv->channel_number);
+				}
+				break;
+
+			case TLV_TYPE_WIFIDIRECT_CONFIG_TIMEOUT:
+				{
+					tlvbuf_wifidirect_config_timeout
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_config_timeout
+						 *)ptr;
+					printf("\t GO configuration timeout = %d msec\n", (int)wifidirect_tlv->group_config_timeout * 10);
+					printf("\t Client configuration timeout = %d msec\n", (int)wifidirect_tlv->device_config_timeout * 10);
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_EXTENDED_LISTEN_TIME:
+				{
+					tlvbuf_wifidirect_ext_listen_time
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_ext_listen_time
+						 *)ptr;
+					printf("\t Availability Period = %d msec\n", (int)wifidirect_tlv->availability_period);
+					printf("\t Availability Interval = %d msec\n", (int)wifidirect_tlv->availability_interval);
+				}
+				break;
+			case TLV_TYPE_WIFIDIRECT_INTENDED_ADDRESS:
+				{
+					tlvbuf_wifidirect_intended_addr
+						*wifidirect_tlv =
+						(tlvbuf_wifidirect_intended_addr
+						 *)ptr;
+					printf("\t Intended Interface Address - ");
+					print_mac(wifidirect_tlv->
+						  group_address);
+					printf("\n");
+				}
+				break;
+
+			case TLV_TYPE_WIFIDIRECT_STATUS:
+				{
+					tlvbuf_wifidirect_status *wifidirect_tlv
+						=
+						(tlvbuf_wifidirect_status *)ptr;
+					printf("\t Status = %d\n",
+					       wifidirect_tlv->status_code);
+				}
+				break;
+
+			default:
+				printf("unknown ie=0x%x, len=%d\n", type, len);
+				break;
+			}
+			next_byte = WIFIDIRECT_OVERLAP_TYPE;
+			if (orig_ptr)
+				ptr = orig_ptr + pending_len;
+		}
+		ptr += len + WIFIDIRECT_IE_HEADER_LEN;
+		left_len -= len;
+	}
+	printf("\n");
+	return;
+}
+
+/**
+ *  @brief Parse and print WIFIDIRECT generic event data
+ *
+ *  @param buffer   Pointer to received buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_event_wifidirect_generic(t_u8 *buffer, t_u16 size)
+{
+	const t_u8 wifi_oui[3] = { 0x50, 0x6F, 0x9A };
+	const t_u8 wps_oui[3] = { 0x00, 0x50, 0xF2 };
+	apeventbuf_wifidirect_generic *wifidirect_event;
+	wifidirect_ie_header *wifidirect_wps_header;
+	t_u16 wifidirect_wps_len = 0, type, sub_type;
+	printf("EVENT: WIFIDIRECT \n");
+	wifidirect_event = (apeventbuf_wifidirect_generic *)(buffer);
+	printf("Event length = %d\n",
+	       uap_le16_to_cpu(wifidirect_event->event_length));
+	printf("Event Type = ");
+	type = uap_le16_to_cpu(wifidirect_event->event_type);
+	switch (type) {
+	case 0:
+		printf("Negotiation Request\n");
+		break;
+	case 1:
+		printf("Negotiation Response\n");
+		break;
+	case 2:
+		printf("Negotiation Result\n");
+		break;
+	case 3:
+		printf("Invitation Request\n");
+		break;
+	case 4:
+		printf("Invitation Response\n");
+		break;
+	case 5:
+		printf("Discoverability Request\n");
+		break;
+	case 6:
+		printf("Discoverability Response\n");
+		break;
+	case 7:
+		printf("Provision Discovery Request\n");
+		break;
+	case 8:
+		printf("Provision Discovery Response\n");
+		break;
+	case 9:
+		printf("GO Negotiation response Tx Event\n");
+		break;
+	case 10:
+		printf("GO Negotiation confirm Tx Event\n");
+		break;
+	case 14:
+		printf("Peer Detected event\n");
+		break;
+	case 15:
+		printf("Client associated event\n");
+		break;
+	case 16:
+		printf("FW debug event: %s\n",
+		       wifidirect_event->entire_ie_list);
+		return;
+	default:
+		printf("Unknown\n");
+		break;
+	}
+	sub_type = uap_le16_to_cpu(wifidirect_event->event_sub_type);
+	if (type == 2) {
+		switch (sub_type) {
+		case 0:
+			printf("Event SubType = No Role\n");
+			break;
+		case 1:
+			printf("Event SubType = Group Owner Role\n");
+			break;
+		case 2:
+			printf("Event SubType = Client Role\n");
+			break;
+		default:
+			printf("Event SubType = %d\n", sub_type);
+			break;
+		}
+	} else if (type == 3 || type == 4) {
+		switch (sub_type & 0x03) {	/* lower 2 bits */
+		case 0:
+			printf("Event SubType = No Role\n");
+			break;
+		case 1:
+			printf("Event SubType = Group Owner Role\n");
+			break;
+		case 2:
+			printf("Event SubType = Client Role\n");
+			break;
+		}
+		switch ((sub_type & 0x1C) >> 2) {	/* next 3 bits */
+		case 0:
+			printf("Packet processing state = None\n");
+			break;
+		case 1:
+			printf("Packet processing state = Processing\n");
+			break;
+		case 2:
+			printf("Packet processing state = Insufficient information, Dropped\n");
+			break;
+		case 3:
+			printf("Packet processing state = Success\n");
+			break;
+		case 4:
+			printf("Packet processing state = Fail\n");
+			break;
+		default:
+			printf("Event SubType = %d\n", sub_type);
+			break;
+		}
+	} else if (type == 7 || type == 8) {
+		switch (sub_type) {
+		case 0:
+			printf("Event SubType = No Config Method\n");
+			break;
+		case 8:
+			printf("Event SubType = Config Method Display\n");
+			break;
+		case 0x80:
+			printf("Event SubType = Config Method Push Button\n");
+			break;
+		case 0x100:
+			printf("Event SubType = Config Method Keypad\n");
+			break;
+		default:
+			printf("Event SubType = %d\n", sub_type);
+			break;
+		}
+	}
+	printf("Peer Mac Address - ");
+	print_mac(wifidirect_event->peer_mac_addr);
+	printf("\n");
+	/* Print rest of IE elements */
+	wifidirect_wps_header =
+		(wifidirect_ie_header *)(wifidirect_event->entire_ie_list);
+	wifidirect_wps_len = uap_le16_to_cpu(wifidirect_event->event_length)
+		- sizeof(apeventbuf_wifidirect_generic);
+
+	while (wifidirect_wps_len >= sizeof(wifidirect_ie_header)) {
+		if (!memcmp
+		    (wifidirect_wps_header->oui, wifi_oui, sizeof(wifi_oui)) ||
+		    !(memcmp
+		      (wifidirect_wps_header->oui, wps_oui, sizeof(wps_oui)))) {
+			switch (wifidirect_wps_header->oui_type) {
+			case WIFIDIRECT_OUI_TYPE:
+				print_wifidirect_ie_elements
+					(wifidirect_wps_header->ie_list,
+					 wifidirect_wps_header->ie_length -
+					 sizeof(wifidirect_wps_header->oui)
+					 -
+					 sizeof(wifidirect_wps_header->
+						oui_type));
+				printf("\n");
+				break;
+			case WIFI_WPS_OUI_TYPE:
+				print_wifi_wps_ie_elements
+					(wifidirect_wps_header->ie_list,
+					 wifidirect_wps_header->ie_length -
+					 sizeof(wifidirect_wps_header->oui)
+					 -
+					 sizeof(wifidirect_wps_header->
+						oui_type));
+				printf("\n");
+				break;
+			}
+		}
+		wifidirect_wps_len -=
+			wifidirect_wps_header->ie_length + IE_HEADER_LEN;
+		wifidirect_wps_header =
+			(wifidirect_ie_header *)(((t_u8 *)wifidirect_wps_header)
+						 +
+						 wifidirect_wps_header->
+						 ie_length + IE_HEADER_LEN);
+	}
+}
+
+/**
+ *  @brief Parse and print WIFIDIRECT service discovery event data
+ *
+ *  @param buffer   Pointer to received buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_event_wifidirect_service_discovery(t_u8 *buffer, t_u16 size)
+{
+	unsigned int i;
+	t_u16 event_len = 0;
+	t_u16 dns_len = 0, dns_type;
+	t_u8 action = 0;	/* req = 0, resp = 1 */
+	apeventbuf_wifidirect_discovery_request *wifidirect_disc_req;
+	apeventbuf_wifidirect_discovery_response *wifidirect_disc_resp;
+	printf("EVENT: WIFIDIRECT SERVICE DISCOVERY\n");
+	memcpy(&event_len, buffer, sizeof(t_u16));
+	event_len = uap_le16_to_cpu(event_len);
+	printf("Event length = %d\n", event_len);
+	printf("Service Discovery packet:\n");
+	/* check request /response Byte at offset 2+6+2 */
+	action = *(buffer + sizeof(t_u16) + ETH_ALEN + sizeof(t_u8));
+	if (action == WIFIDIRECT_DISCOVERY_REQUEST_ACTION) {
+		wifidirect_disc_req =
+			(apeventbuf_wifidirect_discovery_request *)(buffer +
+								    sizeof
+								    (t_u16));
+		printf("\t Peer Mac Address - ");
+		print_mac(wifidirect_disc_req->peer_mac_addr);
+		printf("\n\t Category = %d\n", wifidirect_disc_req->category);
+		printf("\t Action = %d\n", wifidirect_disc_req->action);
+		printf("\t Dialog taken = %d\n",
+		       wifidirect_disc_req->dialog_taken);
+		printf("\t Advertize protocol IE - 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", wifidirect_disc_req->advertize_protocol_ie[0], wifidirect_disc_req->advertize_protocol_ie[1], wifidirect_disc_req->advertize_protocol_ie[2], wifidirect_disc_req->advertize_protocol_ie[3]);
+		printf("\t Request query length = %d\n",
+		       uap_le16_to_cpu(wifidirect_disc_req->query_len));
+		printf("\t Information Id - 0x%02x, 0x%02x\n",
+		       wifidirect_disc_req->info_id[0],
+		       wifidirect_disc_req->info_id[1]);
+		printf("\t Request length = %d\n",
+		       uap_le16_to_cpu(wifidirect_disc_req->request_len));
+		printf("\t OUI - 0x%02x, 0x%02x, 0x%02x\n",
+		       wifidirect_disc_req->oui[0], wifidirect_disc_req->oui[1],
+		       wifidirect_disc_req->oui[2]);
+		printf("\t OUI sub type = %d\n",
+		       wifidirect_disc_req->oui_sub_type);
+		printf("\t Service update Indicator = %d\n",
+		       uap_le16_to_cpu(wifidirect_disc_req->
+				       service_update_indicator));
+		printf("\t Vendor length = %d\n",
+		       uap_le16_to_cpu(wifidirect_disc_req->vendor_len));
+		printf("\t Service protocol = %d\n",
+		       wifidirect_disc_req->service_protocol);
+		printf("\t Service transaction Id = %d\n",
+		       wifidirect_disc_req->service_transaction_id);
+		printf("\t Query Data = ");
+		if (wifidirect_disc_req->service_protocol == 1) {
+			printf(" * Bonjour * \n");
+			printf("\t\t DNS = ");
+			dns_len =
+				uap_le16_to_cpu(wifidirect_disc_req->
+						vendor_len) -
+				WIFIDIRECT_DISCOVERY_BONJOUR_FIXED_LEN;
+			for (i = 0; i < dns_len; i++)
+				printf("%02x ",
+				       (int)*(wifidirect_disc_req->disc_query.u.
+					      bonjour.dns + i));
+			memcpy(&dns_type,
+			       (&wifidirect_disc_req->disc_query.u.bonjour.
+				dns_type + dns_len), sizeof(dns_type));
+			dns_type = uap_le16_to_cpu(dns_type);
+			printf("\n\t\t DNS Type = %d\n", dns_type);
+			printf("\t\t Version = %d\n",
+			       *(&wifidirect_disc_req->disc_query.u.bonjour.
+				 version + dns_len));
+		} else if (wifidirect_disc_req->service_protocol == 2) {
+			printf(" * uPnP * \n");
+			printf("\t\t Version = %d\n",
+			       wifidirect_disc_req->disc_query.u.upnp.version);
+			dns_len =
+				uap_le16_to_cpu(wifidirect_disc_req->
+						vendor_len) -
+				WIFIDIRECT_DISCOVERY_UPNP_FIXED_LEN;
+			printf("\t\t Value = ");
+			for (i = 0; i < dns_len; i++)
+				printf("%02x ",
+				       (int)*(wifidirect_disc_req->disc_query.u.
+					      upnp.value + i));
+		}
+		printf("\n");
+	} else if (action == WIFIDIRECT_DISCOVERY_RESPONSE_ACTION) {
+		wifidirect_disc_resp =
+			(apeventbuf_wifidirect_discovery_response *)(buffer +
+								     sizeof
+								     (t_u16));
+		printf("\t Peer Mac Address - ");
+		print_mac(wifidirect_disc_resp->peer_mac_addr);
+		printf("\n\t Category = %d\n", wifidirect_disc_resp->category);
+		printf("\t Action = %d\n", wifidirect_disc_resp->action);
+		printf("\t Dialog taken = %d\n",
+		       wifidirect_disc_resp->dialog_taken);
+		printf("\t Status code = %d\n",
+		       wifidirect_disc_resp->status_code);
+		printf("\t GAS reply - 0x%02x\n",
+		       uap_le16_to_cpu(wifidirect_disc_resp->gas_reply));
+		printf("\t Advertize protocol IE - 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", wifidirect_disc_resp->advertize_protocol_ie[0], wifidirect_disc_resp->advertize_protocol_ie[1], wifidirect_disc_resp->advertize_protocol_ie[2], wifidirect_disc_resp->advertize_protocol_ie[3]);
+		printf("\t Response query length = %d\n",
+		       uap_le16_to_cpu(wifidirect_disc_resp->query_len));
+		printf("\t Information Id - 0x%02x, 0x%02x\n",
+		       wifidirect_disc_resp->info_id[0],
+		       wifidirect_disc_resp->info_id[1]);
+		printf("\t Response length = %d\n",
+		       uap_le16_to_cpu(wifidirect_disc_resp->response_len));
+		printf("\t OUI - 0x%02x, 0x%02x, 0x%02x\n",
+		       wifidirect_disc_resp->oui[0],
+		       wifidirect_disc_resp->oui[1],
+		       wifidirect_disc_resp->oui[2]);
+		printf("\t OUI sub type = %d\n",
+		       wifidirect_disc_resp->oui_sub_type);
+		printf("\t Service update Indicator = %d\n",
+		       uap_le16_to_cpu(wifidirect_disc_resp->
+				       service_update_indicator));
+		printf("\t Vendor length = %d\n",
+		       uap_le16_to_cpu(wifidirect_disc_resp->vendor_len));
+		printf("\t Service protocol = %d\n",
+		       wifidirect_disc_resp->service_protocol);
+		printf("\t Service transaction Id = %d\n",
+		       wifidirect_disc_resp->service_transaction_id);
+		printf("\t Status Code = %d\n",
+		       wifidirect_disc_resp->disc_status_code);
+		printf("\t Response Data = ");
+		if (wifidirect_disc_resp->service_protocol == 1) {
+			printf(" * Bonjour * \n");
+			/*
+			   printf("\t\t DNS = ");
+			   dns_len = uap_le16_to_cpu(wifidirect_disc_resp->vendor_len) -
+			   (WIFIDIRECT_DISCOVERY_BONJOUR_FIXED_LEN + 1);
+			   for( i=0; i < dns_len; i++)
+			   printf("%c",*(wifidirect_disc_resp->disc_resp.u.bonjour.dns + i));
+			   memcpy(&dns_type, (&wifidirect_disc_req->disc_query.u.bonjour.dns_type
+			   + dns_len), sizeof(dns_type));
+			   dns_type = uap_le16_to_cpu(dns_type);
+			   printf("\n\t\t DNS Type = %d\n", dns_type);
+			   printf("\t\t Version = %d\n", *(&wifidirect_disc_resp->disc_resp.u.bonjour.version
+			   + dns_len));
+			 */
+		} else if (wifidirect_disc_resp->service_protocol == 2) {
+			printf(" * uPnP * \n");
+			printf("\t\t Version = %d\n",
+			       wifidirect_disc_resp->disc_resp.u.upnp.version);
+			dns_len =
+				uap_le16_to_cpu(wifidirect_disc_resp->
+						vendor_len) -
+				WIFIDIRECT_DISCOVERY_UPNP_FIXED_LEN;
+			printf("\t\t Value = ");
+			for (i = 0; i < dns_len; i++)
+				printf("%02x ",
+				       (int)*(wifidirect_disc_resp->disc_resp.u.
+					      upnp.value + i));
+		}
+		printf("\n");
+	}
+}
+#endif
+
+/**
+ *  @brief Parse and print TDLS generic event data
+ *
+ *  @param buffer   Pointer to received buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_event_tdls_generic(t_u8 *buffer, t_u16 size)
+{
+	eventbuf_tdls_generic *tdls_event;
+	eventbuf_tdls_debug *tdls_debug;
+	eventbuf_tdls_packet *tdls_pkt;
+	printf("EVENT: TDLS\n");
+	tdls_event = (eventbuf_tdls_generic *)(buffer);
+	printf("Event Type = ");
+	switch (uap_le16_to_cpu(tdls_event->event_type)) {
+	case TDLS_EVENT_TYPE_SETUP_FAILURE:
+		printf("TDLS setup failure\n");
+		break;
+	case TDLS_EVENT_TYPE_SETUP_REQ_RCVD:
+		printf("TDLS setup request received from peer\n");
+		break;
+	case TDLS_EVENT_TYPE_LINK_TORN_DOWN:
+		printf("TDLS link torn down\n");
+		break;
+	case TDLS_EVENT_TYPE_LINK_ESTABLISHED:
+		printf("TDLS link established\n");
+		break;
+	case TDLS_EVENT_TYPE_DEBUG:
+		tdls_debug = (eventbuf_tdls_debug *)(buffer);
+		printf("TDLS debug event, ");
+		printf("TSF  = %lld\n", uap_le64_to_cpu(tdls_debug->tsf));
+		printf("String = ");
+		printf((char *)(tdls_debug->string),
+		       uap_le32_to_cpu(tdls_debug->first_arg),
+		       uap_le32_to_cpu(tdls_debug->second_arg),
+		       uap_le32_to_cpu(tdls_debug->third_arg));
+		printf("\n");
+		return;
+	case TDLS_EVENT_TYPE_PACKET:
+		tdls_pkt = (eventbuf_tdls_packet *)(buffer);
+		printf("TDLS packet len=%d\n", tdls_pkt->length);
+		hexdump((t_u8 *)tdls_pkt->data, tdls_pkt->length, ' ');
+		return;
+	case TDLS_EVENT_TYPE_CHAN_SWITCH_RESULT:
+		if (tdls_event->u.switch_result.status) {
+			printf("TDLS Channel Switch Failed! current channel=%d Reasoncode %d\n", tdls_event->u.switch_result.current_channel, tdls_event->u.switch_result.reason);
+		} else {
+			printf("TDLS Channel Switch Successful. current channel=%d\n", tdls_event->u.switch_result.current_channel);
+		}
+		break;
+	case TDLS_EVENT_TYPE_START_CHAN_SWITCH:
+		printf("TDLS Start channel switch...\n");
+		break;
+	case TDLS_EVENT_TYPE_CHAN_SWITCH_STOPPED:
+		printf("TDLS Channel Switch stopped! reason=%d\n",
+		       tdls_event->u.cs_stop_reason);
+		break;
+	default:
+		printf("Unknown\n");
+		break;
+	}
+	printf("Peer Mac Address - ");
+	print_mac(tdls_event->peer_mac_addr);
+	printf("\n");
+	if (uap_le16_to_cpu(tdls_event->event_type) ==
+	    TDLS_EVENT_TYPE_SETUP_FAILURE) {
+		switch (uap_le16_to_cpu(tdls_event->u.reason_code)) {
+		case 1:
+			printf("Reason Code = Internal error\n");
+			break;
+		case 7:
+			printf("Reason Code = Timeout waiting for response\n");
+			break;
+		case 8:
+			printf("Reason Code = Non-zero setup response status\n");
+			break;
+		default:
+			printf("Reason Code = %d\n",
+			       uap_le16_to_cpu(tdls_event->u.reason_code));
+			break;
+		}
+	} else if (uap_le16_to_cpu(tdls_event->event_type) ==
+		   TDLS_EVENT_TYPE_LINK_TORN_DOWN) {
+		switch (uap_le16_to_cpu(tdls_event->u.reason_code)) {
+		case 3:
+			printf("Reason Code = Peer station is leaving BSS\n");
+			break;
+		case 25:
+			printf("Reason Code = Peer station is unreachable on direct link\n");
+			break;
+		case 26:
+			printf("Reason Code = Unspecified\n");
+			break;
+		case 211:
+			printf("Reason Code = Setup received from TDLS peer\n");
+			break;
+		case 212:
+			printf("Reason Code = Clearing TDLS connections due to deauth/disassoc\n");
+			break;
+		default:
+			printf("Reason Code = %d\n",
+			       uap_le16_to_cpu(tdls_event->u.reason_code));
+			break;
+		}
+	} else if (uap_le16_to_cpu(tdls_event->event_type) ==
+		   TDLS_EVENT_TYPE_LINK_ESTABLISHED) {
+		int ie_len = uap_le16_to_cpu(tdls_event->u.ie_length);
+		if (ie_len) {
+			printf("Peer IE len=%d\n", ie_len);
+			hexdump((t_u8 *)tdls_event->ie_ptr, ie_len, ' ');
+		}
+	}
+}
+
+/**
+ *  @brief Prints station reject state
+ *
+ *  @param state    Fail state
+ *  @return         N/A
+ */
+void
+print_reject_state(t_u8 state)
+{
+	switch (state) {
+	case REJECT_STATE_FAIL_EAPOL_2:
+		printf("Reject state: FAIL_EAPOL_2\n");
+		break;
+	case REJECT_STATE_FAIL_EAPOL_4:
+		printf("Reject state: FAIL_EAPOL_4:\n");
+		break;
+	case REJECT_STATE_FAIL_EAPOL_GROUP_2:
+		printf("Reject state: FAIL_EAPOL_GROUP_2\n");
+		break;
+	default:
+		printf("ERR: unknown reject state %d\n", state);
+		break;
+	}
+	return;
+}
+
+/**
+ *  @brief Prints station reject reason
+ *
+ *  @param reason   Reason code
+ *  @return         N/A
+ */
+void
+print_reject_reason(t_u16 reason)
+{
+	switch (reason) {
+	case IEEEtypes_REASON_INVALID_IE:
+		printf("Reject reason: Invalid IE\n");
+		break;
+	case IEEEtypes_REASON_MIC_FAILURE:
+		printf("Reject reason: Mic Failure\n");
+		break;
+	default:
+		printf("Reject reason: %d\n", reason);
+		break;
+	}
+	return;
+}
+
+/**
+ *  @brief Prints EAPOL state
+ *
+ *  @param state    Eapol state
+ *  @return         N/A
+ */
+void
+print_eapol_state(t_u8 state)
+{
+	switch (state) {
+	case EAPOL_START:
+		printf("Eapol state: EAPOL_START\n");
+		break;
+	case EAPOL_WAIT_PWK2:
+		printf("Eapol state: EAPOL_WAIT_PWK2\n");
+		break;
+	case EAPOL_WAIT_PWK4:
+		printf("Eapol state: EAPOL_WAIT_PWK4\n");
+		break;
+	case EAPOL_WAIT_GTK2:
+		printf("Eapol state: EAPOL_WAIT_GTK2\n");
+		break;
+	case EAPOL_END:
+		printf("Eapol state: EAPOL_END\n");
+		break;
+	default:
+		printf("ERR: unknow eapol state%d\n", state);
+		break;
+	}
+	return;
+}
+
+/**
+ *  @brief Parse and print debug event data
+ *
+ *  @param buffer   Pointer to received buffer
+ *  @param size     Length of the received event data
+ *  @return         N/A
+ */
+void
+print_event_debug(t_u8 *buffer, t_u16 size)
+{
+	eventbuf_debug *event_body = NULL;
+	if (size < sizeof(eventbuf_debug)) {
+		printf("ERR:Event buffer too small!\n");
+		return;
+	}
+	event_body = (eventbuf_debug *)buffer;
+	printf("Debug Event Type: %s\n",
+	       (event_body->debug_type == 0) ? "EVENT" : "INFO");
+	printf("%s log:\n",
+	       (uap_le32_to_cpu(event_body->debug_id_major) ==
+		DEBUG_ID_MAJ_AUTHENTICATOR) ? "Authenticator" : "Assoc_agent");
+	if (uap_le32_to_cpu(event_body->debug_id_major) ==
+	    DEBUG_ID_MAJ_AUTHENTICATOR) {
+		switch (uap_le32_to_cpu(event_body->debug_id_minor)) {
+		case DEBUG_MAJ_AUTH_MIN_PWK1:
+			printf("EAPOL Key message 1 (PWK):\n");
+			hexdump((t_u8 *)&event_body->info.eapol_pwkmsg,
+				sizeof(eapol_keymsg_debug_t), ' ');
+			break;
+		case DEBUG_MAJ_AUTH_MIN_PWK2:
+			printf("EAPOL Key message 2 (PWK):\n");
+			hexdump((t_u8 *)&event_body->info.eapol_pwkmsg,
+				sizeof(eapol_keymsg_debug_t), ' ');
+			break;
+		case DEBUG_MAJ_AUTH_MIN_PWK3:
+			printf("EAPOL Key message 3 (PWK):\n");
+			hexdump((t_u8 *)&event_body->info.eapol_pwkmsg,
+				sizeof(eapol_keymsg_debug_t), ' ');
+			break;
+		case DEBUG_MAJ_AUTH_MIN_PWK4:
+			printf("EAPOL Key message 4: (PWK)\n");
+			hexdump((t_u8 *)&event_body->info.eapol_pwkmsg,
+				sizeof(eapol_keymsg_debug_t), ' ');
+			break;
+		case DEBUG_MAJ_AUTH_MIN_GWK1:
+			printf("EAPOL Key message 1: (GWK)\n");
+			hexdump((t_u8 *)&event_body->info.eapol_pwkmsg,
+				sizeof(eapol_keymsg_debug_t), ' ');
+			break;
+		case DEBUG_MAJ_AUTH_MIN_GWK2:
+			printf("EAPOL Key message 2: (GWK)\n");
+			hexdump((t_u8 *)&event_body->info.eapol_pwkmsg,
+				sizeof(eapol_keymsg_debug_t), ' ');
+			break;
+		case DEBUG_MAJ_AUTH_MIN_STA_REJ:
+			printf("Reject STA MAC: ");
+			print_mac(event_body->info.sta_reject.sta_mac_addr);
+			printf("\n");
+			print_reject_state(event_body->info.sta_reject.
+					   reject_state);
+			print_reject_reason(uap_le16_to_cpu
+					    (event_body->info.sta_reject.
+					     reject_reason));
+			break;
+		case DEBUG_MAJ_AUTH_MIN_EAPOL_TR:
+			printf("STA MAC: ");
+			print_mac(event_body->info.eapol_state.sta_mac_addr);
+			printf("\n");
+			print_eapol_state(event_body->info.eapol_state.
+					  eapol_state);
+			break;
+		default:
+			printf("ERR: unknow debug_id_minor: %d\n",
+			       (int)uap_le32_to_cpu(event_body->
+						    debug_id_minor));
+			hexdump(buffer, size, ' ');
+			return;
+		}
+	} else if (uap_le32_to_cpu(event_body->debug_id_major) ==
+		   DEBUG_ID_MAJ_ASSOC_AGENT) {
+		switch (uap_le32_to_cpu(event_body->debug_id_minor)) {
+		case DEBUG_ID_MAJ_ASSOC_MIN_WPA_IE:
+			printf("STA MAC: ");
+			print_mac(event_body->info.wpaie.sta_mac_addr);
+			printf("\n");
+			printf("wpa ie:\n");
+			hexdump(event_body->info.wpaie.wpa_ie, MAX_WPA_IE_LEN,
+				' ');
+			break;
+		case DEBUG_ID_MAJ_ASSOC_MIN_STA_REJ:
+			printf("Reject STA MAC: ");
+			print_mac(event_body->info.sta_reject.sta_mac_addr);
+			printf("\n");
+			print_reject_state(event_body->info.sta_reject.
+					   reject_state);
+			print_reject_reason(uap_le16_to_cpu
+					    (event_body->info.sta_reject.
+					     reject_reason));
+			break;
+		default:
+			printf("ERR: unknow debug_id_minor: %d\n",
+			       (int)uap_le32_to_cpu(event_body->
+						    debug_id_minor));
+			hexdump(buffer, size, ' ');
+			return;
+		}
+	}
+	return;
+}
+
+/**
+ *  @brief Parse and print received nlist event information
+ *
+ *  @param buffer   Pointer to the data buffer
+ *  @param size     Length of the received event
+ *  @return         N/A
+ */
+void
+print_event_11k_nlist_report(t_u8 *buffer, t_u16 size)
+{
+	int tlv_buf_left = 0;
+	tlvbuf_header *tlv;
+	nlist_entry_tlv *nlist;
+	int entry_num = 0;
+	t_u16 tlv_type, tlv_len;
+
+	if (size < 2) {
+		printf("No neighbor AP list found: %d\n", size);
+		return;
+	}
+	tlv_buf_left = *(t_u16 *)buffer;
+	tlv_buf_left = uap_le16_to_cpu(tlv_buf_left);
+	if (tlv_buf_left < (int)sizeof(tlvbuf_header))
+		return;
+	printf("neighbor AP list tlv len: %d\n", tlv_buf_left);
+	tlv = (tlvbuf_header *)(buffer + 2);
+	while (tlv_buf_left >= (int)sizeof(tlvbuf_header)) {
+		tlv_type = uap_le16_to_cpu(tlv->type);
+		tlv_len = uap_le16_to_cpu(tlv->len);
+		if ((sizeof(tlvbuf_header) + tlv_len) >
+		    (unsigned int)tlv_buf_left) {
+			printf("wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", tlv_len,
+			       tlv_buf_left);
+			break;
+		}
+		switch (tlv_type) {
+		case MRVL_NEIGHBOR_REPORT_TLV_ID:
+			nlist = (nlist_entry_tlv *) tlv;
+			printf("Neighbor entry %d:\n", entry_num);
+			printf("BSSID: %02x:%02x:%02x:%02x:%02x:%02x \n",
+			       nlist->bssid[0], nlist->bssid[1],
+			       nlist->bssid[2], nlist->bssid[3],
+			       nlist->bssid[4], nlist->bssid[5]);
+			printf("BSSID Info: %02x:%02x:%02x:%02x \n",
+			       nlist->bssid_info[0], nlist->bssid_info[1],
+			       nlist->bssid_info[2], nlist->bssid_info[3]);
+			printf("reg class: %d, chan: %d, phy: %d \n",
+			       nlist->reg_class, nlist->chan, nlist->phy_type);
+			if (tlv_len > sizeof(nlist_entry_tlv)) {
+				printf("sub IE:\n");
+				hexdump((t_u8 *)tlv + sizeof(nlist_entry_tlv),
+					tlv_len - sizeof(nlist_entry_tlv) +
+					sizeof(tlvbuf_header), ' ');
+			}
+			entry_num++;
+			break;
+		default:
+#if DEBUG
+			printf("unknown tlv: %d\n", tlv_type);
+#endif
+			break;
+		}
+		tlv_buf_left -= (sizeof(tlvbuf_header) + tlv_len);
+		tlv = (tlvbuf_header *)((t_u8 *)tlv + tlv_len +
+					sizeof(tlvbuf_header));
+	}
+	return;
+}
+
+/**
+ *  @brief Parse and print received event information
+ *
+ *  @param event    Pointer to received event
+ *  @param size     Length of the received event
+ *  @return         N/A
+ */
+void
+print_event(event_header *event, t_u16 size)
+{
+	t_u32 event_id = event->event_id;
+	switch (event_id) {
+	case MICRO_AP_EV_ID_STA_DEAUTH:
+		print_event_sta_deauth(event->event_data, size - EVENT_ID_LEN);
+		break;
+	case MICRO_AP_EV_ID_STA_ASSOC:
+		print_event_sta_assoc(event->event_data, size - EVENT_ID_LEN);
+		break;
+	case MICRO_AP_EV_ID_BSS_START:
+		print_event_bss_start(event->event_data, size - EVENT_ID_LEN);
+		break;
+	case MICRO_AP_EV_ID_DEBUG:
+		print_event_debug(event->event_data, size - EVENT_ID_LEN);
+		break;
+	case MICRO_AP_EV_BSS_IDLE:
+		printf("EVENT: BSS_IDLE\n");
+		break;
+	case MICRO_AP_EV_BSS_ACTIVE:
+		printf("EVENT: BSS_ACTIVE\n");
+		break;
+	case MICRO_AP_EV_RSN_CONNECT:
+		print_event_rsn_connect(event->event_data, size - EVENT_ID_LEN);
+		break;
+#ifdef WIFI_DIRECT_SUPPORT
+	case EVENT_WIFIDIRECT_GENERIC:
+		print_event_wifidirect_generic(event->event_data,
+					       size - EVENT_ID_LEN);
+		break;
+	case EVENT_WIFIDIRECT_SERVICE_DISCOVERY:
+		print_event_wifidirect_service_discovery(event->event_data,
+							 size - EVENT_ID_LEN);
+		break;
+#endif
+
+	case EVENT_TDLS_GENERIC:
+		print_event_tdls_generic(event->event_data,
+					 size - EVENT_ID_LEN);
+		break;
+	case UAP_EVENT_ID_DRV_HS_ACTIVATED:
+		printf("EVENT: uAP HS_ACTIVATED\n");
+		break;
+	case UAP_EVENT_ID_DRV_HS_DEACTIVATED:
+		printf("EVENT: uAP HS_DEACTIVATED\n");
+		break;
+	case UAP_EVENT_ID_HS_WAKEUP:
+		printf("EVENT: uAP HS_WAKEUP\n");
+		break;
+	case UAP_EVENT_HOST_SLEEP_AWAKE:
+		break;
+	case UAP_EVENT_ID_DRV_MGMT_FRAME:
+		printf("EVENT: Mgmt frame from FW\n");
+		hexdump((void *)event, size, ' ');
+		break;
+	case MICRO_AP_EV_WMM_STATUS_CHANGE:
+		printf("EVENT: WMM_STATUS_CHANGE\n");
+		break;
+	case EVENT_RADAR_DETECTED:
+		printf("EVENT: RADAR_DETECTED\n");
+		break;
+	case EVENT_CHANNEL_REPORT_RDY:
+		printf("EVENT: CHANNEL_REPORT_READY\n");
+		hexdump((void *)event, size, ' ');
+		break;
+	case EVENT_WEP_ICV_ERROR:
+		print_event_wep_icv_error(event->event_data,
+					  size - EVENT_ID_LEN);
+		break;
+	case EVENT_ID_DRV_SCAN_REPORT:
+		printf("Scan request completed.\n");
+		break;
+	case EVENT_NLIST_REPORT:
+		printf("EVENT: 11k neighbor AP list report\n");
+		print_event_11k_nlist_report(event->event_data,
+					     size - EVENT_ID_LEN);
+		break;
+
+	default:
+		/* Handle string based events */
+#define CUS_EVT_PORT_RELEASE           "EVENT=PORT_RELEASE"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_PORT_RELEASE,
+		     strlen(CUS_EVT_PORT_RELEASE))) {
+			printf("EVENT: PORT_RELEASE.\n");
+			break;
+		}
+#define CUS_EVT_TDLS_CONNECTED           "EVENT=TDLS_CONNECTED"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_TDLS_CONNECTED,
+		     strlen(CUS_EVT_TDLS_CONNECTED))) {
+			printf("EVENT: TDLS_CONNECTED\n");
+			print_mac((t_u8 *)event +
+				  strlen(CUS_EVT_TDLS_CONNECTED));
+			printf("\n");
+			break;
+		}
+#define CUS_EVT_TDLS_TEARDOWN            "EVENT=TDLS_TEARDOWN"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_TDLS_TEARDOWN,
+		     strlen(CUS_EVT_TDLS_TEARDOWN))) {
+			printf("EVENT: TDLS_TEARDOWN\n");
+			print_mac((t_u8 *)event +
+				  strlen(CUS_EVT_TDLS_TEARDOWN));
+			printf("\n");
+			break;
+		}
+#define CUS_EVT_STA_CONNECTED           "EVENT=STA_CONNECTED"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_STA_CONNECTED,
+		     strlen(CUS_EVT_STA_CONNECTED))) {
+			printf("EVENT: STA_CONNECTED\n");
+			print_mac((t_u8 *)event +
+				  strlen(CUS_EVT_STA_CONNECTED) + 1);
+			printf("\n");
+			break;
+		}
+#define CUS_EVT_STA_DISCONNECTED           "EVENT=STA_DISCONNECTED"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_STA_DISCONNECTED,
+		     strlen(CUS_EVT_STA_DISCONNECTED))) {
+			printf("EVENT: STA_DISCONNECTED\n");
+			break;
+		}
+#define CUS_EVT_AP_CONNECTED           "EVENT=AP_CONNECTED"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_AP_CONNECTED,
+		     strlen(CUS_EVT_AP_CONNECTED))) {
+			printf("EVENT: AP_CONNECTED\n");
+			print_mac((t_u8 *)event + strlen(CUS_EVT_AP_CONNECTED));
+			printf("\n");
+			break;
+		}
+#define CUS_EVT_ADHOC_LINK_SENSED   "EVENT=ADHOC_LINK_SENSED"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_ADHOC_LINK_SENSED,
+		     strlen(CUS_EVT_ADHOC_LINK_SENSED))) {
+			printf("EVENT: ADHOC_LINK_SENSED\n");
+			break;
+		}
+#define CUS_EVT_ADHOC_LINK_LOST     "EVENT=ADHOC_LINK_LOST"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_ADHOC_LINK_LOST,
+		     strlen(CUS_EVT_ADHOC_LINK_LOST))) {
+			printf("EVENT: ADHOC_LINK_LOST\n");
+			break;
+		}
+#define CUS_EVT_OBSS_SCAN_PARAM     "EVENT=OBSS_SCAN_PARAM"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_OBSS_SCAN_PARAM,
+		     strlen(CUS_EVT_OBSS_SCAN_PARAM))) {
+			printf("EVENT: OBSS_SCAN_PARAM\n");
+			break;
+		}
+#define CUS_EVT_BW_CHANGED      "EVENT=BW_CHANGED"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_BW_CHANGED,
+		     strlen(CUS_EVT_BW_CHANGED))) {
+			printf("EVENT: BW_CHANGED\n");
+			break;
+		}
+#define CUS_EVT_MLME_MIC_ERR_UNI    "MLME-MICHAELMICFAILURE.indication unicast"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_MLME_MIC_ERR_UNI,
+		     strlen(CUS_EVT_MLME_MIC_ERR_UNI))) {
+			printf("EVENT: MLME-MICHAELMICFAILURE.indication unicast\n");
+			break;
+		}
+#define CUS_EVT_MLME_MIC_ERR_MUL    "MLME-MICHAELMICFAILURE.indication multicast"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_MLME_MIC_ERR_MUL,
+		     strlen(CUS_EVT_MLME_MIC_ERR_MUL))) {
+			printf("EVENT: MLME-MICHAELMICFAILURE.indication multicast\n");
+			break;
+		}
+#define CUS_EVT_BEACON_RSSI_LOW     "EVENT=BEACON_RSSI_LOW"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_BEACON_RSSI_LOW,
+		     strlen(CUS_EVT_BEACON_RSSI_LOW))) {
+			printf("EVENT: BEACON_RSSI_LOW\n");
+			break;
+		}
+#define CUS_EVT_BEACON_RSSI_HIGH    "EVENT=BEACON_RSSI_HIGH"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_BEACON_RSSI_HIGH,
+		     strlen(CUS_EVT_BEACON_RSSI_HIGH))) {
+			printf("EVENT: BEACON_RSSI_HIGH\n");
+			break;
+		}
+#define CUS_EVT_BEACON_SNR_LOW      "EVENT=BEACON_SNR_LOW"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_BEACON_SNR_LOW,
+		     strlen(CUS_EVT_BEACON_SNR_LOW))) {
+			printf("EVENT: BEACON_SNR_LOW\n");
+			break;
+		}
+#define CUS_EVT_BEACON_SNR_HIGH     "EVENT=BEACON_SNR_HIGH"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_BEACON_SNR_HIGH,
+		     strlen(CUS_EVT_BEACON_SNR_HIGH))) {
+			printf("EVENT: BEACON_SNR_HIGH\n");
+			break;
+		}
+#define CUS_EVT_MAX_FAIL        "EVENT=MAX_FAIL"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_MAX_FAIL,
+		     strlen(CUS_EVT_MAX_FAIL))) {
+			printf("EVENT: MAX_FAIL\n");
+			break;
+		}
+#define CUS_EVT_DATA_RSSI_LOW       "EVENT=DATA_RSSI_LOW"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_DATA_RSSI_LOW,
+		     strlen(CUS_EVT_DATA_RSSI_LOW))) {
+			printf("EVENT: DATA_RSSI_LOW\n");
+			break;
+		}
+#define CUS_EVT_DATA_SNR_LOW        "EVENT=DATA_SNR_LOW"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_DATA_SNR_LOW,
+		     strlen(CUS_EVT_DATA_SNR_LOW))) {
+			printf("EVENT: DATA_SNR_LOW\n");
+			break;
+		}
+#define CUS_EVT_DATA_RSSI_HIGH      "EVENT=DATA_RSSI_HIGH"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_DATA_RSSI_HIGH,
+		     strlen(CUS_EVT_DATA_RSSI_HIGH))) {
+			printf("EVENT: DATA_RSSI_HIGH\n");
+			break;
+		}
+#define CUS_EVT_DATA_SNR_HIGH       "EVENT=DATA_SNR_HIGH"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_DATA_SNR_HIGH,
+		     strlen(CUS_EVT_DATA_SNR_HIGH))) {
+			printf("EVENT: DATA_SNR_HIGH\n");
+			break;
+		}
+#define CUS_EVT_LINK_QUALITY        "EVENT=LINK_QUALITY"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_LINK_QUALITY,
+		     strlen(CUS_EVT_LINK_QUALITY))) {
+			printf("EVENT: LINK_QUALITY\n");
+			break;
+		}
+#define CUS_EVT_WEP_ICV_ERR     "EVENT=WEP_ICV_ERR"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_WEP_ICV_ERR,
+		     strlen(CUS_EVT_WEP_ICV_ERR))) {
+			printf("EVENT: WEP_ICV_ERR\n");
+			break;
+		}
+#define CUS_EVT_CHANNEL_SWITCH_ANN  "EVENT=CHANNEL_SWITCH_ANN"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_CHANNEL_SWITCH_ANN,
+		     strlen(CUS_EVT_CHANNEL_SWITCH_ANN))) {
+			printf("EVENT: CHANNEL_SWITCH_ANN\n");
+			break;
+		}
+#define CUS_EVT_HS_WAKEUP       "HS_WAKEUP"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_HS_WAKEUP,
+		     strlen(CUS_EVT_HS_WAKEUP))) {
+			printf("EVENT: HS_WAKEUP\n");
+			break;
+		}
+#define CUS_EVT_HS_ACTIVATED        "HS_ACTIVATED"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_HS_ACTIVATED,
+		     strlen(CUS_EVT_HS_ACTIVATED))) {
+			printf("EVENT: HS_ACTIVATED\n");
+			break;
+		}
+#define CUS_EVT_HS_DEACTIVATED      "HS_DEACTIVATED"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_HS_DEACTIVATED,
+		     strlen(CUS_EVT_HS_DEACTIVATED))) {
+			printf("EVENT: HS_DEACTIVATED\n");
+			break;
+		}
+/** Custom indication message sent to the application layer for WMM changes */
+#define WMM_CONFIG_CHANGE_INDICATION  "WMM_CONFIG_CHANGE.indication"
+		if (!strncmp
+		    ((char *)event, WMM_CONFIG_CHANGE_INDICATION,
+		     strlen(WMM_CONFIG_CHANGE_INDICATION))) {
+			printf("EVENT: STA_DISCONNECTED\n");
+			break;
+		}
+#define CUS_EVT_DRIVER_HANG	            "EVENT=DRIVER_HANG"
+		if (!strncmp
+		    ((char *)event, CUS_EVT_DRIVER_HANG,
+		     strlen(CUS_EVT_DRIVER_HANG))) {
+			printf("EVENT: DRIVER_HANG\n");
+			break;
+		}
+		printf("ERR:Undefined event type (0x%X). Dumping event buffer:\n", (unsigned int)event_id);
+		hexdump((void *)event, size, ' ');
+		break;
+	}
+	return;
+}
+
+/**
+ *  @brief Read event data from netlink socket
+ *
+ *  @param sk_fd    Netlink socket handler
+ *  @param buffer   Pointer to the data buffer
+ *  @param nlh      Pointer to netlink message header
+ *  @param msg      Pointer to message header
+ *  @return         Number of bytes read or MLAN_EVENT_FAILURE
+ */
+int
+read_event_netlink_socket(int sk_fd, unsigned char *buffer,
+			  struct nlmsghdr *nlh, struct msghdr *msg)
+{
+	int count = -1;
+	count = recvmsg(sk_fd, msg, 0);
+#if DEBUG
+	printf("DBG:Waiting for message from NETLINK.\n");
+#endif
+	if (count < 0) {
+		printf("ERR:NETLINK read failed!\n");
+		terminate_flag++;
+		return MLAN_EVENT_FAILURE;
+	}
+#if DEBUG
+	printf("DBG:Received message payload (%d)\n", count);
+#endif
+	if (count > NLMSG_SPACE(NL_MAX_PAYLOAD)) {
+		printf("ERR:Buffer overflow!\n");
+		return MLAN_EVENT_FAILURE;
+	}
+	memset(buffer, 0, NL_MAX_PAYLOAD);
+	memcpy(buffer, NLMSG_DATA(nlh), count - NLMSG_HDRLEN);
+#if DEBUG
+	hexdump(buffer, count - NLMSG_HDRLEN, ' ');
+#endif
+	return count - NLMSG_HDRLEN;
+}
+
+/**
+ *  @brief Configure and read event data from netlink socket
+ *
+ *  @param sk_fd    Array of netlink sockets
+ *  @param no_of_sk Number of netlink sockets opened
+ *  @param recv_buf Pointer to the array of evt_buf structures
+ *  @param timeout  Socket listen timeout value
+ *  @param nlh      Pointer to netlink message header
+ *  @param msg      Pointer to message header
+ *  @return         Number of bytes read or MLAN_EVENT_FAILURE
+ */
+int
+read_event(int *sk_fd, int no_of_sk, evt_buf *recv_buf, int timeout,
+	   struct nlmsghdr *nlh, struct msghdr *msg)
+{
+	struct timeval tv;
+	fd_set rfds;
+	int i = 0, max_sk_fd = sk_fd[0];
+	int ret = MLAN_EVENT_FAILURE;
+
+	/* Setup read fds */
+	FD_ZERO(&rfds);
+	for (i = 0; i < no_of_sk; i++) {
+		if (sk_fd[i] > max_sk_fd)
+			max_sk_fd = sk_fd[i];
+
+		if (sk_fd[i] > 0)
+			FD_SET(sk_fd[i], &rfds);
+	}
+
+	/* Initialize timeout value */
+	if (timeout != 0)
+		tv.tv_sec = timeout;
+	else
+		tv.tv_sec = UAP_RECV_WAIT_DEFAULT;
+	tv.tv_usec = 0;
+
+	/* Wait for reply */
+	ret = select(max_sk_fd + 1, &rfds, NULL, NULL, &tv);
+	if (ret == -1) {
+		/* Error */
+		terminate_flag++;
+		return MLAN_EVENT_FAILURE;
+	} else if (!ret) {
+		/* Timeout. Try again */
+		return MLAN_EVENT_FAILURE;
+	}
+	for (i = 0; i < no_of_sk; i++) {
+		if (sk_fd[i] > 0) {
+			if (FD_ISSET(sk_fd[i], &rfds)) {
+				/* Success */
+				recv_buf[i].flag = 1;
+				recv_buf[i].length =
+					read_event_netlink_socket(sk_fd[i],
+								  recv_buf[i].
+								  buffer, nlh,
+								  msg);
+				ret += recv_buf[i].length;
+			}
+		}
+	}
+	return ret;
+}
+
+/* Command line options */
+static const struct option long_opts[] = {
+	{"help", no_argument, NULL, 'h'},
+	{"version", no_argument, NULL, 'v'},
+	{NULL, 0, NULL, 0}
+};
+
+/**
+ *  @brief Determine the netlink number
+ *
+ *  @param i socket number
+ *
+ *  @return         Netlink number to use
+ */
+static int
+get_netlink_num(int i)
+{
+	FILE *fp;
+	int netlink_num = NETLINK_MARVELL;
+	char str[64];
+	char *srch = "netlink_num";
+	char filename[64];
+
+	if (i == 0) {
+		strcpy(filename, "/proc/mwlan/config");
+	} else if (i > 0) {
+		sprintf(filename, "/proc/mwlan/config%d", i);
+	}
+	/* Try to open /proc/mwlan/config$ */
+	fp = fopen(filename, "r");
+	if (fp) {
+		while (!feof(fp)) {
+			fgets(str, sizeof(str), fp);
+			if (strncmp(str, srch, strlen(srch)) == 0) {
+				netlink_num = atoi(str + strlen(srch) + 1);
+				break;
+			}
+		}
+		fclose(fp);
+	} else {
+		return -1;
+	}
+	printf("Netlink number = %d\n", netlink_num);
+	return netlink_num;
+}
+
+/****************************************************************************
+        Global functions
+****************************************************************************/
+/**
+ *  @brief The main function
+ *
+ *  @param argc     Number of arguments
+ *  @param argv     Pointer to the arguments
+ *  @return         0 or 1
+ */
+int
+main(int argc, char *argv[])
+{
+	int opt;
+	int nl_sk[MAX_NO_OF_DEVICES];
+	struct nlmsghdr *nlh = NULL;
+	struct sockaddr_nl src_addr, dest_addr;
+	struct msghdr msg;
+	struct iovec iov;
+	evt_buf evt_recv_buf[MAX_NO_OF_DEVICES];
+	struct timeval current_time;
+	struct tm *timeinfo;
+	int num_events = 0;
+	event_header *event = NULL;
+	int ret = MLAN_EVENT_FAILURE;
+	int netlink_num[MAX_NO_OF_DEVICES];
+	char if_name[IFNAMSIZ + 1];
+	t_u32 event_id = 0;
+	int i = 0, no_of_sk = 0, dev_index = -1;
+
+	/* Check command line options */
+	while ((opt = getopt_long(argc, argv, "hvti:", long_opts, NULL)) > 0) {
+		switch (opt) {
+		case 'h':
+			print_usage();
+			return 0;
+		case 'v':
+			printf("mlanevent version : %s\n", MLAN_EVENT_VERSION);
+			return 0;
+			break;
+		case 'i':
+			if ((IS_HEX_OR_DIGIT(argv[2]) == MLAN_EVENT_FAILURE) ||
+			    (A2HEXDECIMAL(argv[2]) < 0)
+			    || ((A2HEXDECIMAL(argv[2]) >= MAX_NO_OF_DEVICES) &&
+				(A2HEXDECIMAL(argv[2]) != 0xff))) {
+				print_usage();
+				return 1;
+			} else {
+				dev_index = A2HEXDECIMAL(argv[2]);
+				argc -= optind;
+				argv += optind;
+			}
+			break;
+		default:
+			print_usage();
+			return 1;
+		}
+	}
+
+	if (optind < argc) {
+		fputs("Too many arguments.\n", stderr);
+		print_usage();
+		return 1;
+	}
+	if ((dev_index >= 0) && (dev_index < MAX_NO_OF_DEVICES)) {
+		no_of_sk = 1;
+	} else {
+		/* Currently, we support maximum 4 devices */
+		/* TODO: determine no_of_sk at run time */
+		no_of_sk = MAX_NO_OF_DEVICES;
+	}
+
+	for (i = 0; i < no_of_sk; i++) {
+		/* Initialise */
+		nl_sk[i] = -1;
+		if (no_of_sk == 1) {
+			netlink_num[i] = get_netlink_num(dev_index);
+			if (netlink_num[i] < 0) {
+				printf("ERR:Could not get netlink socket. Invalid device number.\n");
+				ret = MLAN_EVENT_FAILURE;
+				goto done;
+			}
+		} else {
+			netlink_num[i] = get_netlink_num(i);
+		}
+		if (netlink_num[i] >= 0) {
+			/* Open netlink socket */
+			nl_sk[i] = socket(PF_NETLINK, SOCK_RAW, netlink_num[i]);
+			if (nl_sk[i] < 0) {
+				printf("ERR:Could not open netlink socket.\n");
+				ret = MLAN_EVENT_FAILURE;
+				goto done;
+			}
+
+			/* Set source address */
+			memset(&src_addr, 0, sizeof(src_addr));
+			src_addr.nl_family = AF_NETLINK;
+			src_addr.nl_pid = getpid();	/* Our PID */
+			src_addr.nl_groups = NL_MULTICAST_GROUP;
+
+			/* Bind socket with source address */
+			if (bind
+			    (nl_sk[i], (struct sockaddr *)&src_addr,
+			     sizeof(src_addr)) < 0) {
+				printf("ERR:Could not bind socket!\n");
+				ret = MLAN_EVENT_FAILURE;
+				goto done;
+			}
+
+			/* Set destination address */
+			memset(&dest_addr, 0, sizeof(dest_addr));
+			dest_addr.nl_family = AF_NETLINK;
+			dest_addr.nl_pid = 0;	/* Kernel */
+			dest_addr.nl_groups = NL_MULTICAST_GROUP;
+
+			/* Initialize netlink header */
+			nlh = (struct nlmsghdr *)
+				malloc(NLMSG_SPACE(NL_MAX_PAYLOAD));
+			if (!nlh) {
+				printf("ERR: Could not alloc buffer\n");
+				ret = MLAN_EVENT_FAILURE;
+				goto done;
+			}
+			memset(nlh, 0, NLMSG_SPACE(NL_MAX_PAYLOAD));
+
+			/* Initialize I/O vector */
+			iov.iov_base = (void *)nlh;
+			iov.iov_len = NLMSG_SPACE(NL_MAX_PAYLOAD);
+
+			/* Initialize message header */
+			memset(&msg, 0, sizeof(struct msghdr));
+			msg.msg_name = (void *)&dest_addr;
+			msg.msg_namelen = sizeof(dest_addr);
+			msg.msg_iov = &iov;
+			msg.msg_iovlen = 1;
+
+			memset(&evt_recv_buf[i], 0, sizeof(evt_buf));
+		}
+	}
+	gettimeofday(&current_time, NULL);
+
+	printf("\n");
+	printf("**********************************************\n");
+	if ((timeinfo = localtime(&(current_time.tv_sec))))
+		printf("mlanevent start time : %s", asctime(timeinfo));
+	printf("                      %u usecs\n",
+	       (unsigned int)current_time.tv_usec);
+	printf("**********************************************\n");
+
+	signal(SIGTERM, sig_handler);
+	signal(SIGINT, sig_handler);
+	signal(SIGALRM, sig_handler);
+	while (1) {
+		if (terminate_flag) {
+			printf("Stopping!\n");
+			break;
+		}
+		ret = read_event(nl_sk, no_of_sk, evt_recv_buf, 0, nlh, &msg);
+
+		/* No result. Loop again */
+		if (ret == MLAN_EVENT_FAILURE) {
+			continue;
+		}
+		if (ret == 0) {
+			/* Zero bytes received */
+			printf("ERR:Received zero bytes!\n");
+			continue;
+		}
+		for (i = 0; i < no_of_sk; i++) {
+			if (evt_recv_buf[i].flag == 1) {
+				num_events++;
+				gettimeofday(&current_time, NULL);
+				printf("\n");
+				printf("============================================\n");
+				printf("Received event");
+				if ((timeinfo =
+				     localtime(&(current_time.tv_sec))))
+					printf(": %s", asctime(timeinfo));
+				printf("                     %u usecs\n",
+				       (unsigned int)current_time.tv_usec);
+				printf("============================================\n");
+
+				memcpy(&event_id, evt_recv_buf[i].buffer,
+				       sizeof(event_id));
+				if (((event_id & 0xFF000000) == 0x80000000) ||
+				    ((event_id & 0xFF000000) == 0)) {
+					event = (event_header
+						 *)(evt_recv_buf[i].buffer);
+				} else {
+					memset(if_name, 0, IFNAMSIZ + 1);
+					memcpy(if_name, evt_recv_buf[i].buffer,
+					       IFNAMSIZ);
+					printf("EVENT for interface %s\n",
+					       if_name);
+					event = (event_header
+						 *)((t_u8 *)(evt_recv_buf[i].
+							     buffer) +
+						    IFNAMSIZ);
+					ret -= IFNAMSIZ;
+					evt_recv_buf[i].length -= IFNAMSIZ;
+				}
+#if DEBUG
+				printf("DBG:Received buffer =\n");
+				hexdump(evt_recv_buf[i].buffer,
+					evt_recv_buf[i].length + IFNAMSIZ, ' ');
+#endif
+				print_event(event, evt_recv_buf[i].length);
+				/* Reset event flag after reading */
+				evt_recv_buf[i].flag = 0;
+			}
+		}
+		fflush(stdout);
+	}
+	gettimeofday(&current_time, NULL);
+	printf("\n");
+	printf("*********************************************\n");
+	if ((timeinfo = localtime(&(current_time.tv_sec))))
+		printf("mlanevent end time  : %s", asctime(timeinfo));
+	printf("                     %u usecs\n",
+	       (unsigned int)current_time.tv_usec);
+	printf("Total events       : %u\n", num_events);
+	printf("*********************************************\n");
+done:
+	for (i = 0; i < no_of_sk; i++) {
+		if (nl_sk[i] > 0)
+			close(nl_sk[i]);
+	}
+	if (nlh)
+		free(nlh);
+	return 0;
+}
diff --git a/wlan_sd8897/mapp/mlanevent/mlanevent.h b/wlan_sd8897/mapp/mlanevent/mlanevent.h
new file mode 100644
index 0000000..c3b2c67
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanevent/mlanevent.h
@@ -0,0 +1,1375 @@
+/** @file  mlanevent.h
+ *
+ *  @brief Header file for mlanevent application
+ *
+ * Copyright (C) 2008-2017, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell 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 along with the File in the gpl.txt file or by writing to
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.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.
+ *
+ */
+/************************************************************************
+Change log:
+    03/18/08: Initial creation
+************************************************************************/
+
+#ifndef _MLAN_EVENT_H
+#define _MLAN_EVENT_H
+
+/** Character, 1 byte */
+typedef signed char t_s8;
+/** Unsigned character, 1 byte */
+typedef unsigned char t_u8;
+
+/** Short integer */
+typedef signed short t_s16;
+/** Unsigned short integer */
+typedef unsigned short t_u16;
+
+/** Integer */
+typedef signed int t_s32;
+/** Unsigned integer */
+typedef unsigned int t_u32;
+
+/** Long long integer */
+typedef long long t_s64;
+/** Unsigned long long integer */
+typedef unsigned long long t_u64;
+
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#undef BIG_ENDIAN_SUPPORT
+#endif
+
+/** 16 bits byte swap */
+#define swap_byte_16(x) \
+  ((t_u16)((((t_u16)(x) & 0x00ffU) << 8) | \
+         (((t_u16)(x) & 0xff00U) >> 8)))
+
+/** 32 bits byte swap */
+#define swap_byte_32(x) \
+  ((t_u32)((((t_u32)(x) & 0x000000ffUL) << 24) | \
+         (((t_u32)(x) & 0x0000ff00UL) <<  8) | \
+         (((t_u32)(x) & 0x00ff0000UL) >>  8) | \
+         (((t_u32)(x) & 0xff000000UL) >> 24)))
+
+/** 64 bits byte swap */
+#define swap_byte_64(x) \
+  ((t_u64)((t_u64)(((t_u64)(x) & 0x00000000000000ffULL) << 56) | \
+         (t_u64)(((t_u64)(x) & 0x000000000000ff00ULL) << 40) | \
+         (t_u64)(((t_u64)(x) & 0x0000000000ff0000ULL) << 24) | \
+         (t_u64)(((t_u64)(x) & 0x00000000ff000000ULL) <<  8) | \
+         (t_u64)(((t_u64)(x) & 0x000000ff00000000ULL) >>  8) | \
+         (t_u64)(((t_u64)(x) & 0x0000ff0000000000ULL) >> 24) | \
+         (t_u64)(((t_u64)(x) & 0x00ff000000000000ULL) >> 40) | \
+         (t_u64)(((t_u64)(x) & 0xff00000000000000ULL) >> 56) ))
+
+#ifdef BIG_ENDIAN_SUPPORT
+/** Convert from 16 bit little endian format to CPU format */
+#define uap_le16_to_cpu(x) swap_byte_16(x)
+/** Convert from 32 bit little endian format to CPU format */
+#define uap_le32_to_cpu(x) swap_byte_32(x)
+/** Convert from 64 bit little endian format to CPU format */
+#define uap_le64_to_cpu(x) swap_byte_64(x)
+/** Convert to 16 bit little endian format from CPU format */
+#define uap_cpu_to_le16(x) swap_byte_16(x)
+/** Convert to 32 bit little endian format from CPU format */
+#define uap_cpu_to_le32(x) swap_byte_32(x)
+/** Convert to 64 bit little endian format from CPU format */
+#define uap_cpu_to_le64(x) swap_byte_64(x)
+#else /* BIG_ENDIAN_SUPPORT */
+/** Do nothing */
+#define uap_le16_to_cpu(x) x
+/** Do nothing */
+#define uap_le32_to_cpu(x) x
+/** Do nothing */
+#define uap_le64_to_cpu(x) x
+/** Do nothing */
+#define uap_cpu_to_le16(x) x
+/** Do nothing */
+#define uap_cpu_to_le32(x) x
+/** Do nothing */
+#define uap_cpu_to_le64(x) x
+#endif /* BIG_ENDIAN_SUPPORT */
+
+/** Convert WPS TLV header from network to host order */
+#define endian_convert_tlv_wps_header_in(t,l)       \
+    {                                               \
+        (t) = ntohs(t);       \
+        (l) = ntohs(l);       \
+    }
+
+/**
+ * Hex or Decimal to Integer
+ * @param   num string to convert into decimal or hex
+ */
+#define A2HEXDECIMAL(num)  \
+    (strncasecmp("0x", (num), 2)?(unsigned int) strtoll((num),NULL,0):a2hex((num)))\
+
+/**
+ * Check of decimal or hex string
+ * @param   num string
+ */
+#define IS_HEX_OR_DIGIT(num) \
+    (strncasecmp("0x", (num), 2)?ISDIGIT((num)):ishexstring((num)))\
+
+/** MLan Event application version string */
+#define MLAN_EVENT_VERSION         "MlanEvent 2.0"
+
+/** Failure */
+#define MLAN_EVENT_FAILURE     -1
+
+#ifdef __GNUC__
+/** Structure packing begins */
+#define PACK_START
+/** Structure packeing end */
+#define PACK_END  __attribute__ ((packed))
+#else
+/** Structure packing begins */
+#define PACK_START   __packed
+/** Structure packeing end */
+#define PACK_END
+#endif
+
+#ifndef ETH_ALEN
+/** MAC address length */
+#define ETH_ALEN    6
+#endif
+
+/** Netlink protocol number */
+#define NETLINK_MARVELL         (MAX_LINKS - 1)
+/** Netlink maximum payload size */
+#define NL_MAX_PAYLOAD          1024
+/** Netlink multicast group number */
+#define NL_MULTICAST_GROUP      1
+/** Default wait time in seconds for events */
+#define UAP_RECV_WAIT_DEFAULT   10
+/** Maximum number of devices */
+#define MAX_NO_OF_DEVICES       4
+
+#ifndef NLMSG_HDRLEN
+/** NL message header length */
+#define NLMSG_HDRLEN            ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#endif
+
+/** Event ID mask */
+#define EVENT_ID_MASK                   0xffff
+
+/** BSS number mask */
+#define BSS_NUM_MASK                    0xf
+
+/** Get BSS number from event cause (bit 23:16) */
+#define EVENT_GET_BSS_NUM(event_cause)          \
+    (((event_cause) >> 16) & BSS_NUM_MASK)
+
+/** Invitation Flag mask */
+#define INVITATION_FLAG_MASK            0x01
+
+/* Event buffer */
+typedef PACK_START struct _evt_buf {
+    /** Flag to check if event data is present in the buffer or not  */
+	int flag;
+    /** Event length */
+	int length;
+    /** Event data */
+	t_u8 buffer[NL_MAX_PAYLOAD];
+} PACK_END evt_buf;
+
+/** Event header */
+typedef PACK_START struct _event_header {
+    /** Event ID */
+	t_u32 event_id;
+    /** Event data */
+	t_u8 event_data[0];
+} PACK_END event_header;
+
+/** Event ID length */
+#define EVENT_ID_LEN    4
+
+/** Event ID : WMM status change */
+#define MICRO_AP_EV_WMM_STATUS_CHANGE       0x00000017
+
+/** Event ID: STA deauth */
+#define MICRO_AP_EV_ID_STA_DEAUTH           0x0000002c
+
+/** Event ID: STA associated */
+#define MICRO_AP_EV_ID_STA_ASSOC            0x0000002d
+
+/** Event ID: BSS started */
+#define MICRO_AP_EV_ID_BSS_START            0x0000002e
+
+/** Event ID: Debug event */
+#define MICRO_AP_EV_ID_DEBUG                0x00000036
+
+/** Event ID: BSS idle event */
+#define MICRO_AP_EV_BSS_IDLE                0x00000043
+
+/** Event ID: BSS active event */
+#define MICRO_AP_EV_BSS_ACTIVE              0x00000044
+
+/** Event ID: WEP ICV error */
+#define EVENT_WEP_ICV_ERROR                 0x00000046
+
+#ifdef WIFI_DIRECT_SUPPORT
+/** Event ID: UAP,STA wifidirect generic event */
+#define EVENT_WIFIDIRECT_GENERIC                   0x00000049
+
+/** Event ID: UAP,STA wifidirect service discovery event */
+#define EVENT_WIFIDIRECT_SERVICE_DISCOVERY         0x0000004a
+#endif
+
+/** Event ID: RSN Connect event */
+#define MICRO_AP_EV_RSN_CONNECT             0x00000051
+
+/** Event ID: TDLS generic event */
+#define EVENT_TDLS_GENERIC                  0x00000052
+
+/** event type for tdls setup failure */
+#define TDLS_EVENT_TYPE_SETUP_FAILURE         1
+/** event type for tdls setup request received */
+#define TDLS_EVENT_TYPE_SETUP_REQ_RCVD        2
+/** event type for tdls link torn down */
+#define TDLS_EVENT_TYPE_LINK_TORN_DOWN        3
+/** event type for tdls link established */
+#define TDLS_EVENT_TYPE_LINK_ESTABLISHED      4
+/** event type for tdls debug */
+#define TDLS_EVENT_TYPE_DEBUG                 5
+/** event type for tdls packet */
+#define TDLS_EVENT_TYPE_PACKET                6
+/** event type for channel switch result */
+#define TDLS_EVENT_TYPE_CHAN_SWITCH_RESULT    7
+/** event type for start channel switch */
+#define TDLS_EVENT_TYPE_START_CHAN_SWITCH     8
+/** event type for stop channel switch */
+#define TDLS_EVENT_TYPE_CHAN_SWITCH_STOPPED    9
+
+/** Event ID: Radar Detected */
+#define EVENT_RADAR_DETECTED                0x00000053
+/** Event ID: Channel Report Ready */
+#define EVENT_CHANNEL_REPORT_RDY            0x00000054
+
+/** Event ID: NLIST REPORT */
+#define EVENT_NLIST_REPORT                0x00000079
+/** NLIST_REPORT TLV */
+#define MRVL_NEIGHBOR_REPORT_TLV_ID   	  0x1de
+
+/** HS WAKE UP event id */
+#define UAP_EVENT_ID_HS_WAKEUP             0x80000001
+/** HS_ACTIVATED event id */
+#define UAP_EVENT_ID_DRV_HS_ACTIVATED      0x80000002
+/** HS DEACTIVATED event id */
+#define UAP_EVENT_ID_DRV_HS_DEACTIVATED    0x80000003
+/** HOST SLEEP AWAKE  event id in legacy PS*/
+#define UAP_EVENT_HOST_SLEEP_AWAKE         0x00000012
+
+/** HS WAKE UP event id */
+#define UAP_EVENT_ID_DRV_MGMT_FRAME        0x80000005
+
+/** SCAN REPORT Event id */
+#define EVENT_ID_DRV_SCAN_REPORT           0x80000009
+
+/** WPA IE Tag */
+#define  IEEE_WPA_IE                 221
+/** RSN IE Tag */
+#define  IEEE_RSN_IE                 48
+
+/** TLV ID : WAPI Information */
+#define MRVL_WAPI_INFO_TLV_ID        0x0167
+
+/** TLV ID : Management Frame */
+#define MRVL_MGMT_FRAME_TLV_ID       0x0168
+
+/** TLV Id : Channel Config */
+#define MRVL_CHANNELCONFIG_TLV_ID    0x012a
+
+/** Assoc Request */
+#define SUBTYPE_ASSOC_REQUEST        0
+/** Assoc Response */
+#define SUBTYPE_ASSOC_RESPONSE       1
+/** ReAssoc Request */
+#define SUBTYPE_REASSOC_REQUEST      2
+/** ReAssoc Response */
+#define SUBTYPE_REASSOC_RESPONSE     3
+/** WEP key user input length */
+#define WEP_KEY_USER_INPUT           13
+
+/** TLV buffer header*/
+typedef PACK_START struct _tlvbuf_header {
+    /** Header type */
+	t_u16 type;
+    /** Header length */
+	t_u16 len;
+} PACK_END tlvbuf_header;
+
+/** Event body : STA deauth */
+typedef PACK_START struct _eventbuf_sta_deauth {
+    /** Deauthentication reason */
+	t_u16 reason_code;
+    /** MAC address of deauthenticated STA */
+	t_u8 sta_mac_address[ETH_ALEN];
+} PACK_END eventbuf_sta_deauth;
+
+/** Event body : WEP ICV error */
+typedef PACK_START struct _eventbuf_wep_icv_error {
+    /** Deauthentication reason */
+	t_u16 reason_code;
+    /** MAC address of deauthenticated STA */
+	t_u8 sta_mac_address[ETH_ALEN];
+    /** WEP key index */
+	t_u8 wep_key_index;
+    /** WEP key length */
+	t_u8 wep_key_length;
+    /** WEP key */
+	t_u8 wep_key[WEP_KEY_USER_INPUT];
+} PACK_END eventbuf_wep_icv_error;
+
+/** Event body : STA associated */
+typedef PACK_START struct _eventbuf_sta_assoc {
+    /** Reserved */
+	t_u8 reserved[2];
+    /** MAC address of associated STA */
+	t_u8 sta_mac_address[ETH_ALEN];
+    /** Assoc request/response buffer */
+	t_u8 assoc_payload[0];
+} PACK_END eventbuf_sta_assoc;
+
+/** Event body : RSN Connect */
+typedef PACK_START struct _eventbuf_rsn_connect {
+    /** Reserved */
+	t_u8 reserved[2];
+    /** MAC address of Station */
+	t_u8 sta_mac_address[ETH_ALEN];
+    /** WPA/WPA2 TLV IEs */
+	t_u8 tlv_list[0];
+} PACK_END eventbuf_rsn_connect;
+
+/** Event body : BSS started */
+typedef PACK_START struct _eventbuf_bss_start {
+    /** Reserved */
+	t_u8 reserved[2];
+    /** MAC address of BSS */
+	t_u8 ap_mac_address[ETH_ALEN];
+} PACK_END eventbuf_bss_start;
+
+/**
+ *                 IEEE 802.11 MAC Message Data Structures
+ *
+ * Each IEEE 802.11 MAC message includes a MAC header, a frame body (which
+ * can be empty), and a frame check sequence field. This section gives the
+ * structures that used for the MAC message headers and frame bodies that
+ * can exist in the three types of MAC messages - 1) Control messages,
+ * 2) Data messages, and 3) Management messages.
+ */
+#ifdef BIG_ENDIAN_SUPPORT
+typedef PACK_START struct _IEEEtypes_FrameCtl_t {
+    /** Order */
+	t_u8 order:1;
+    /** Wep */
+	t_u8 wep:1;
+    /** More Data */
+	t_u8 more_data:1;
+    /** Power Mgmt */
+	t_u8 pwr_mgmt:1;
+    /** Retry */
+	t_u8 retry:1;
+    /** More Frag */
+	t_u8 more_frag:1;
+    /** From DS */
+	t_u8 from_ds:1;
+    /** To DS */
+	t_u8 to_ds:1;
+    /** Sub Type */
+	t_u8 sub_type:4;
+    /** Type */
+	t_u8 type:2;
+    /** Protocol Version */
+	t_u8 protocol_version:2;
+} PACK_END IEEEtypes_FrameCtl_t;
+#else
+typedef PACK_START struct _IEEEtypes_FrameCtl_t {
+    /** Protocol Version */
+	t_u8 protocol_version:2;
+    /** Type */
+	t_u8 type:2;
+    /** Sub Type */
+	t_u8 sub_type:4;
+    /** To DS */
+	t_u8 to_ds:1;
+    /** From DS */
+	t_u8 from_ds:1;
+    /** More Frag */
+	t_u8 more_frag:1;
+    /** Retry */
+	t_u8 retry:1;
+    /** Power Mgmt */
+	t_u8 pwr_mgmt:1;
+    /** More Data */
+	t_u8 more_data:1;
+    /** Wep */
+	t_u8 wep:1;
+    /** Order */
+	t_u8 order:1;
+} PACK_END IEEEtypes_FrameCtl_t;
+#endif
+
+/** IEEEtypes_AssocRqst_t */
+typedef PACK_START struct _IEEEtypes_AssocRqst_t {
+    /** Capability Info */
+	t_u16 cap_info;
+    /** Listen Interval */
+	t_u16 listen_interval;
+    /** IE Buffer */
+	t_u8 ie_buffer[0];
+} PACK_END IEEEtypes_AssocRqst_t;
+
+/** IEEEtypes_AssocRsp_t */
+typedef PACK_START struct _IEEEtypes_AssocRsp_t {
+    /** Capability Info */
+	t_u16 cap_info;
+    /** Status Code */
+	t_u16 status_code;
+    /** AID */
+	t_u16 aid;
+} PACK_END IEEEtypes_AssocRsp_t;
+
+/** IEEEtypes_ReAssocRqst_t */
+typedef PACK_START struct _IEEEtypes_ReAssocRqst_t {
+    /** Capability Info */
+	t_u16 cap_info;
+    /** Listen Interval */
+	t_u16 listen_interval;
+    /** Current AP Address */
+	t_u8 current_ap_addr[ETH_ALEN];
+    /** IE Buffer */
+	t_u8 ie_buffer[0];
+} PACK_END IEEEtypes_ReAssocRqst_t;
+
+/** channel band */
+enum {
+	BAND_2GHZ = 0,
+	BAND_5GHZ = 1,
+	BAND_4GHZ = 2,
+};
+
+/** channel offset */
+enum {
+	SEC_CHAN_NONE = 0,
+	SEC_CHAN_ABOVE = 1,
+	SEC_CHAN_5MHZ = 2,
+	SEC_CHAN_BELOW = 3
+};
+
+/** channel bandwidth */
+enum {
+	CHAN_BW_20MHZ = 0,
+	CHAN_BW_10MHZ,
+	CHAN_BW_40MHZ,
+	CHAN_BW_80MHZ,
+};
+
+/** scan mode */
+enum {
+	SCAN_MODE_MANUAL = 0,
+	SCAN_MODE_ACS,
+	SCAN_MODE_USER,
+};
+
+/** Band_Config_t */
+typedef PACK_START struct _Band_Config_t {
+#ifdef BIG_ENDIAN_SUPPORT
+    /** Channel Selection Mode - (00)=manual, (01)=ACS,  (02)=user*/
+	t_u8 scanMode:2;
+    /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */
+	t_u8 chan2Offset:2;
+    /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */
+	t_u8 chanWidth:2;
+    /** Band Info - (00)=2.4GHz, (01)=5GHz */
+	t_u8 chanBand:2;
+#else
+    /** Band Info - (00)=2.4GHz, (01)=5GHz */
+	t_u8 chanBand:2;
+    /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */
+	t_u8 chanWidth:2;
+    /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */
+	t_u8 chan2Offset:2;
+    /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=Adoption mode*/
+	t_u8 scanMode:2;
+#endif
+} PACK_END Band_Config_t;
+
+/** TLV buffer : Channel Config */
+typedef PACK_START struct _tlvbuf_channel_config {
+    /** Type */
+	t_u16 type;
+    /** Length */
+	t_u16 len;
+    /** Band Configuration */
+	Band_Config_t bandcfg;
+    /** Channel number */
+	t_u8 chan_number;
+} PACK_END tlvbuf_channel_config;
+
+/** MrvlIEtypes_WapiInfoSet_t */
+typedef PACK_START struct _MrvlIEtypes_WapiInfoSet_t {
+    /** Type */
+	t_u16 type;
+    /** Length */
+	t_u16 len;
+    /** Multicast PN */
+	t_u8 multicast_PN[16];
+} PACK_END MrvlIEtypes_WapiInfoSet_t;
+
+/** MrvlIETypes_MgmtFrameSet_t */
+typedef PACK_START struct _MrvlIETypes_MgmtFrameSet_t {
+    /** Type */
+	t_u16 type;
+    /** Length */
+	t_u16 len;
+    /** Frame Control */
+	IEEEtypes_FrameCtl_t frame_control;
+    /** Frame Contents */
+	t_u8 frame_contents[0];
+} PACK_END MrvlIETypes_MgmtFrameSet_t;
+
+/** Debug Type : Event */
+#define DEBUG_TYPE_EVENT    0
+/** Debug Type : Info */
+#define DEBUG_TYPE_INFO     1
+
+/** Major debug id: Authenticator */
+#define DEBUG_ID_MAJ_AUTHENTICATOR      1
+/** Minor debug id: PWK1 */
+#define DEBUG_MAJ_AUTH_MIN_PWK1         0
+/** Minor debug id: PWK2 */
+#define DEBUG_MAJ_AUTH_MIN_PWK2         1
+/** Minor debug id: PWK3 */
+#define DEBUG_MAJ_AUTH_MIN_PWK3         2
+/** Minor debug id: PWK4 */
+#define DEBUG_MAJ_AUTH_MIN_PWK4         3
+/** Minor debug id: GWK1 */
+#define DEBUG_MAJ_AUTH_MIN_GWK1         4
+/** Minor debug id: GWK2 */
+#define DEBUG_MAJ_AUTH_MIN_GWK2         5
+/** Minor debug id: station reject */
+#define DEBUG_MAJ_AUTH_MIN_STA_REJ      6
+/** Minor debug id: EAPOL_TR */
+#define DEBUG_MAJ_AUTH_MIN_EAPOL_TR     7
+
+/** Major debug id: Assoicate agent */
+#define DEBUG_ID_MAJ_ASSOC_AGENT        2
+/** Minor debug id: WPA IE*/
+#define DEBUG_ID_MAJ_ASSOC_MIN_WPA_IE   0
+/** Minor debug id: station reject */
+#define DEBUG_ID_MAJ_ASSOC_MIN_STA_REJ  1
+
+/** ether_hdr */
+typedef PACK_START struct {
+    /** Dest address */
+	t_u8 da[ETH_ALEN];
+    /** Src address */
+	t_u8 sa[ETH_ALEN];
+    /** Header type */
+	t_u16 type;
+} PACK_END ether_hdr_t;
+
+/** 8021x header */
+typedef PACK_START struct {
+    /** Protocol version*/
+	t_u8 protocol_ver;
+    /** Packet type*/
+	t_u8 pckt_type;
+    /** Packet len */
+	t_u8 pckt_body_len;
+} PACK_END hdr_8021x_t;
+
+/** Nonce size */
+#define NONCE_SIZE      32
+/** Max WPA IE len */
+#define MAX_WPA_IE_LEN  64
+/** EAPOL mic size */
+#define EAPOL_MIC_SIZE  16
+
+/** EAPOL key message */
+typedef PACK_START struct {
+    /** Ether header */
+	ether_hdr_t ether_hdr;
+    /** 8021x header */
+	hdr_8021x_t hdr_8021x;
+    /** desc_type */
+	t_u8 desc_type;
+    /** Key info */
+	t_u16 k;
+    /** Key length */
+	t_u16 key_length;
+    /** Replay count */
+	t_u32 replay_cnt[2];
+    /** Key nonce */
+	t_u8 key_nonce[NONCE_SIZE];
+    /** Key IV */
+	t_u8 eapol_key_iv[16];
+    /** Key RSC */
+	t_u8 key_rsc[8];
+    /** Key ID */
+	t_u8 key_id[8];
+    /** Key MIC */
+	t_u8 key_mic[EAPOL_MIC_SIZE];
+    /** Key len */
+	t_u16 key_material_len;
+    /** Key data */
+	t_u8 key_data[MAX_WPA_IE_LEN];
+} PACK_END eapol_keymsg_debug_t;
+
+/** Failure after receive EAPOL MSG2 PMK */
+#define REJECT_STATE_FAIL_EAPOL_2       1
+/** Failure after receive EAPOL MSG4 PMK*/
+#define REJECT_STATE_FAIL_EAPOL_4       2
+/** Failure after receive EAPOL Group MSG2 GWK */
+#define REJECT_STATE_FAIL_EAPOL_GROUP_2 3
+
+/** Fail reason: Invalid ie */
+#define IEEEtypes_REASON_INVALID_IE     13
+/** Fail reason: Mic failure */
+#define IEEEtypes_REASON_MIC_FAILURE    14
+
+/** Station reject */
+typedef PACK_START struct {
+    /** Reject state */
+	t_u8 reject_state;
+    /** Reject reason */
+	t_u16 reject_reason;
+    /** Station mac address */
+	t_u8 sta_mac_addr[ETH_ALEN];
+} PACK_END sta_reject_t;
+
+/** wpa_ie */
+typedef PACK_START struct {
+    /** Station mac address */
+	t_u8 sta_mac_addr[ETH_ALEN];
+    /** WPA IE */
+	t_u8 wpa_ie[MAX_WPA_IE_LEN];
+} PACK_END wpaie_t;
+
+/** Initial state of the state machine */
+#define EAPOL_START         1
+/** Sent eapol msg1, wait for msg2 from the client */
+#define EAPOL_WAIT_PWK2     2
+/** Sent eapol msg3, wait for msg4 from the client */
+#define EAPOL_WAIT_PWK4     3
+/** Sent eapol group key msg1, wait for group msg2 from the client */
+#define EAPOL_WAIT_GTK2     4
+/** Eapol handshake complete */
+#define EAPOL_END           5
+
+#ifdef WIFI_DIRECT_SUPPORT
+/** TLV : WifiDirect status */
+#define TLV_TYPE_WIFIDIRECT_STATUS               0x0000
+/** TLV : WifiDirect param capability */
+#define TLV_TYPE_WIFIDIRECT_CAPABILITY           0x0002
+/** TLV : WifiDirect param device Id */
+#define TLV_TYPE_WIFIDIRECT_DEVICE_ID            0x0003
+/** TLV : WifiDirect param group owner intent */
+#define TLV_TYPE_WIFIDIRECT_GROUPOWNER_INTENT    0x0004
+/** TLV : WifiDirect param config timeout */
+#define TLV_TYPE_WIFIDIRECT_CONFIG_TIMEOUT       0x0005
+/** TLV : WifiDirect param channel */
+#define TLV_TYPE_WIFIDIRECT_CHANNEL              0x0006
+/** TLV : WifiDirect param group bssId */
+#define TLV_TYPE_WIFIDIRECT_GROUP_BSS_ID         0x0007
+/** TLV : WifiDirect param extended listen time */
+#define TLV_TYPE_WIFIDIRECT_EXTENDED_LISTEN_TIME 0x0008
+/** TLV : WifiDirect param intended address */
+#define TLV_TYPE_WIFIDIRECT_INTENDED_ADDRESS     0x0009
+/** TLV : WifiDirect param manageability */
+#define TLV_TYPE_WIFIDIRECT_MANAGEABILITY        0x000a
+/** TLV : WifiDirect param channel list */
+#define TLV_TYPE_WIFIDIRECT_CHANNEL_LIST         0x000b
+/** TLV : WifiDirect Notice of Absence */
+#define TLV_TYPE_WIFIDIRECT_NOTICE_OF_ABSENCE    0x000c
+/** TLV : WifiDirect param device Info */
+#define TLV_TYPE_WIFIDIRECT_DEVICE_INFO          0x000d
+/** TLV : WifiDirect param Group Info */
+#define TLV_TYPE_WIFIDIRECT_GROUP_INFO           0x000e
+/** TLV : WifiDirect param group Id */
+#define TLV_TYPE_WIFIDIRECT_GROUP_ID             0x000f
+/** TLV : WifiDirect param interface */
+#define TLV_TYPE_WIFIDIRECT_INTERFACE            0x0010
+/** TLV : WifiDirect param operating channel */
+#define TLV_TYPE_WIFIDIRECT_OPCHANNEL            0x0011
+/** TLV : WifiDirect param invitation flag */
+#define TLV_TYPE_WIFIDIRECT_INVITATION_FLAG      0x0012
+
+/** WPS Device Info OUI+Type+SubType Length */
+#define WPS_DEVICE_TYPE_LEN             8
+
+/** IE header len */
+#define IE_HEADER_LEN                   2
+
+/** WIFIDIRECT IE header len */
+#define WIFIDIRECT_IE_HEADER_LEN               3
+
+/** OUI Type WIFIDIRECT */
+#define WIFIDIRECT_OUI_TYPE                    9
+/** OUI Type WPS */
+#define WIFI_WPS_OUI_TYPE               4
+
+/*
+ * To handle overlapping WIFIDIRECT IEs
+ */
+/** IE next byte type */
+#define WIFIDIRECT_OVERLAP_TYPE                1
+/** IE next byte length */
+#define WIFIDIRECT_OVERLAP_LEN                 2
+/** IE next byte data */
+#define WIFIDIRECT_OVERLAP_DATA                3
+
+/** Max payload for IE buffer  */
+#define WIFI_IE_MAX_PAYLOAD             256
+
+/** Fixed length fields in bonjour payload query data */
+#define WIFIDIRECT_DISCOVERY_BONJOUR_FIXED_LEN  5
+
+/** Fixed length fields in uPnP payload query data */
+#define WIFIDIRECT_DISCOVERY_UPNP_FIXED_LEN     3
+
+/** Action field for discovery request */
+#define WIFIDIRECT_DISCOVERY_REQUEST_ACTION     10
+
+/** Action field for discovery response */
+#define WIFIDIRECT_DISCOVERY_RESPONSE_ACTION    11
+
+/** TLV buffer : WifiDirect Status */
+typedef PACK_START struct _tlvbuf_wifidirect_status {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT status code */
+	t_u8 status_code;
+} PACK_END tlvbuf_wifidirect_status;
+
+/** TLV buffer : wifidirect IE device Id */
+typedef PACK_START struct _tlvbuf_wifidirect_device_id {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT device MAC address */
+	t_u8 dev_mac_address[ETH_ALEN];
+} PACK_END tlvbuf_wifidirect_device_id;
+
+/** TLV buffer : wifidirect IE capability */
+typedef PACK_START struct _tlvbuf_wifidirect_capability {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT device capability */
+	t_u8 dev_capability;
+    /** WIFIDIRECT group capability */
+	t_u8 group_capability;
+} PACK_END tlvbuf_wifidirect_capability;
+
+/** TLV buffer : wifidirect IE Group owner intent */
+typedef PACK_START struct _tlvbuf_wifidirect_group_owner_intent {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT device group owner intent */
+	t_u8 dev_intent;
+} PACK_END tlvbuf_wifidirect_group_owner_intent;
+
+/** TLV buffer : WifiDirect IE Manageability */
+typedef PACK_START struct _tlvbuf_wifidirect_manageability {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT Manageability */
+	t_u8 manageability;
+} PACK_END tlvbuf_wifidirect_manageability;
+
+/** TLV buffer : WifiDirect IE Invitation Flag */
+typedef PACK_START struct _tlvbuf_wifidirect_invitation_flag {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT Manageability */
+	t_u8 invitation_flag;
+} PACK_END tlvbuf_wifidirect_invitation_flag;
+
+/** TLV buffer : wifidirect IE capability */
+typedef PACK_START struct _tlvbuf_wifidirect_channel {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT country string */
+	t_u8 country_string[3];
+    /** WIFIDIRECT regulatory class */
+	t_u8 regulatory_class;
+    /** WIFIDIRECT channel number */
+	t_u8 channel_number;
+} PACK_END tlvbuf_wifidirect_channel;
+
+/** Channel Entry */
+typedef PACK_START struct _chan_entry {
+    /** WIFIDIRECT regulatory class */
+	t_u8 regulatory_class;
+    /** WIFIDIRECT no of channels */
+	t_u8 num_of_channels;
+    /** WIFIDIRECT channel number */
+	t_u8 chan_list[0];
+} PACK_END chan_entry;
+
+/** TLV buffer : wifidirect IE channel list */
+typedef PACK_START struct _tlvbuf_wifidirect_channel_list {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT country string */
+	t_u8 country_string[3];
+    /** WIFIDIRECT channel entries */
+	chan_entry wifidirect_chan_entry_list[0];
+} PACK_END tlvbuf_wifidirect_channel_list;
+
+/** NoA Descriptor */
+typedef PACK_START struct _noa_descriptor {
+    /** WIFIDIRECT count OR type */
+	t_u8 count_type;
+    /** WIFIDIRECT duration */
+	t_u32 duration;
+    /** WIFIDIRECT interval */
+	t_u32 interval;
+    /** WIFIDIRECT start time */
+	t_u32 start_time;
+} PACK_END noa_descriptor;
+
+/** TLV buffer : WifiDirect IE Notice of Absence */
+typedef PACK_START struct _tlvbuf_wifidirect_notice_of_absence {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT NoA Index */
+	t_u8 noa_index;
+    /** WIFIDIRECT CTWindow and OppPS parameters */
+	t_u8 ctwindow_opp_ps;
+    /** WIFIDIRECT NoA Descriptor list */
+	noa_descriptor wifidirect_noa_descriptor_list[0];
+} PACK_END tlvbuf_wifidirect_notice_of_absence;
+
+/** TLV buffer : wifidirect IE device Info */
+typedef PACK_START struct _tlvbuf_wifidirect_device_info {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT device address */
+	t_u8 dev_address[ETH_ALEN];
+    /** WPS config methods */
+	t_u16 config_methods;
+    /** Primary device type : category */
+	t_u16 primary_category;
+    /** Primary device type : OUI */
+	t_u8 primary_oui[4];
+    /** Primary device type : sub-category */
+	t_u16 primary_subcategory;
+    /** Secondary Device Count */
+	t_u8 secondary_dev_count;
+    /** Secondary Device Info */
+	t_u8 secondary_dev_info[0];
+    /** WPS Device Name Tag */
+	t_u16 device_name_type;
+    /** WPS Device Name Length */
+	t_u16 device_name_len;
+    /** Device name */
+	t_u8 device_name[0];
+} PACK_END tlvbuf_wifidirect_device_info;
+
+typedef PACK_START struct _wifidirect_client_dev_info {
+    /** Length of each device */
+	t_u8 dev_length;
+    /** WIFIDIRECT device address */
+	t_u8 wifidirect_dev_address[ETH_ALEN];
+    /** WIFIDIRECT Interface  address */
+	t_u8 wifidirect_intf_address[ETH_ALEN];
+    /** WIFIDIRECT Device capability*/
+	t_u8 wifidirect_dev_capability;
+    /** WPS config methods */
+	t_u16 config_methods;
+    /** Primary device type : category */
+	t_u16 primary_category;
+    /** Primary device type : OUI */
+	t_u8 primary_oui[4];
+    /** Primary device type : sub-category */
+	t_u16 primary_subcategory;
+    /** Secondary Device Count */
+	t_u8 wifidirect_secondary_dev_count;
+    /** Secondary Device Info */
+	t_u8 wifidirect_secondary_dev_info[0];
+    /** WPS WIFIDIRECT Device Name Tag */
+	t_u16 wifidirect_device_name_type;
+    /** WPS WIFIDIRECT Device Name Length */
+	t_u16 wifidirect_device_name_len;
+    /** WIFIDIRECT Device name */
+	t_u8 wifidirect_device_name[0];
+} PACK_END wifidirect_client_dev_info;
+
+typedef PACK_START struct _tlvbuf_wifidirect_group_info {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** Secondary Device Info */
+	t_u8 wifidirect_client_dev_list[0];
+} PACK_END tlvbuf_wifidirect_group_info;
+
+/** TLV buffer : wifidirect IE group Id */
+typedef PACK_START struct _tlvbuf_wifidirect_group_id {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT group MAC address */
+	t_u8 group_address[ETH_ALEN];
+    /** WIFIDIRECT group SSID */
+	t_u8 group_ssid[0];
+} PACK_END tlvbuf_wifidirect_group_id;
+
+/** TLV buffer : wifidirect IE group BSS Id */
+typedef PACK_START struct _tlvbuf_wifidirect_group_bss_id {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT group Bss Id */
+	t_u8 group_bssid[ETH_ALEN];
+} PACK_END tlvbuf_wifidirect_group_bss_id;
+
+/** TLV buffer : wifidirect IE Interface */
+typedef PACK_START struct _tlvbuf_wifidirect_interface {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT interface Id */
+	t_u8 interface_id[ETH_ALEN];
+    /** WIFIDIRECT interface count */
+	t_u8 interface_count;
+    /** WIFIDIRECT interface addresslist */
+	t_u8 interface_idlist[0];
+} PACK_END tlvbuf_wifidirect_interface;
+
+/** TLV buffer : WifiDirect configuration timeout */
+typedef PACK_START struct _tlvbuf_wifidirect_config_timeout {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** Group configuration timeout */
+	t_u8 group_config_timeout;
+    /** Device configuration timeout */
+	t_u8 device_config_timeout;
+} PACK_END tlvbuf_wifidirect_config_timeout;
+
+/** TLV buffer : WifiDirect extended listen time */
+typedef PACK_START struct _tlvbuf_wifidirect_ext_listen_time {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** Availability period */
+	t_u16 availability_period;
+    /** Availability interval */
+	t_u16 availability_interval;
+} PACK_END tlvbuf_wifidirect_ext_listen_time;
+
+/** TLV buffer : WifiDirect Intended Interface Address */
+typedef PACK_START struct _tlvbuf_wifidirect_intended_addr {
+    /** TLV Header tag */
+	t_u8 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFIDIRECT Group interface address */
+	t_u8 group_address[ETH_ALEN];
+} PACK_END tlvbuf_wifidirect_intended_addr;
+
+/** TLV buffer : Wifi WPS IE */
+typedef PACK_START struct _tlvbuf_wifi_wps_ie {
+    /** TLV Header tag */
+	t_u16 tag;
+    /** TLV Header length */
+	t_u16 length;
+    /** WIFI WPS IE data */
+	t_u8 data[0];
+} PACK_END tlvbuf_wps_ie;
+
+/** WifiDirect IE Header */
+typedef PACK_START struct _wifidirect_ie_header {
+    /** Element ID */
+	t_u8 element_id;
+    /** IE Length */
+	t_u8 ie_length;
+    /** OUI */
+	t_u8 oui[3];
+    /** OUI type */
+	t_u8 oui_type;
+    /** IE List of TLV */
+	t_u8 ie_list[0];
+} PACK_END wifidirect_ie_header;
+
+/** Event : WifiDirect Generic event */
+typedef PACK_START struct _apeventbuf_wifidirect_generic {
+    /** Event Length */
+	t_u16 event_length;
+    /** Event Type */
+	t_u16 event_type;
+    /** Event SubType */
+	t_u16 event_sub_type;
+    /** Peer mac address */
+	t_u8 peer_mac_addr[ETH_ALEN];
+    /** IE List of TLV */
+	t_u8 entire_ie_list[0];
+} PACK_END apeventbuf_wifidirect_generic;
+
+/** Internal WIFIDIRECT structure for Query Data */
+typedef PACK_START struct wifidirect_query_data {
+	union {
+		PACK_START struct upnp_specific_query {
+	    /** version field */
+			t_u8 version;
+	    /** value */
+			t_u8 value[0];
+		} PACK_END upnp;
+
+		PACK_START struct bonjour_specific_query {
+	    /** DNS name */
+			t_u8 dns[0];
+	    /** DNS type */
+			t_u8 dns_type;
+	    /** version field */
+			t_u8 version;
+		} PACK_END bonjour;
+	} u;
+} PACK_END wifidirect_query_data;
+
+/** Internal WIFIDIRECT structure for Response Data */
+typedef PACK_START struct wifidirect_Response_data {
+	union {
+		PACK_START struct upnp_specific_response {
+	    /** version field */
+			t_u8 version;
+	    /** value */
+			t_u8 value[0];
+		} PACK_END upnp;
+
+		PACK_START struct bonjour_specific_response {
+	    /** DNS name */
+			t_u8 dns[0];
+	    /** DNS type */
+			t_u8 dns_type;
+	    /** version field */
+			t_u8 version;
+	    /** DNS name */
+			t_u8 record[0];
+		} PACK_END bonjour;
+	} u;
+} PACK_END wifidirect_response_data;
+
+/** Event : Service Discovery request */
+typedef PACK_START struct _apeventbuf_wifidirect_discovery_request {
+    /** Peer mac address */
+	t_u8 peer_mac_addr[ETH_ALEN];
+    /** Category */
+	t_u8 category;
+    /** Action */
+	t_u8 action;
+    /** Dialog taken */
+	t_u8 dialog_taken;
+    /** Advertize protocol IE */
+	t_u8 advertize_protocol_ie[4];
+    /** Query request Length */
+	t_u16 query_len;
+    /** Information identifier */
+	t_u8 info_id[2];
+    /** Request Length */
+	t_u16 request_len;
+    /** OUI */
+	t_u8 oui[3];
+    /** OUI sub type */
+	t_u8 oui_sub_type;
+    /** Service update indicator */
+	t_u16 service_update_indicator;
+    /** Vendor Length */
+	t_u16 vendor_len;
+    /** Service protocol */
+	t_u8 service_protocol;
+    /** Service transaction Id */
+	t_u8 service_transaction_id;
+    /** Query Data */
+	wifidirect_query_data disc_query;
+} PACK_END apeventbuf_wifidirect_discovery_request;
+
+/** HostCmd_CMD_WIFIDIRECT_SERVICE_DISCOVERY response */
+typedef PACK_START struct _apeventbuf_wifidirect_discovery_response {
+    /** Peer mac address */
+	t_u8 peer_mac_addr[ETH_ALEN];
+    /** Category */
+	t_u8 category;
+    /** Action */
+	t_u8 action;
+    /** Dialog taken */
+	t_u8 dialog_taken;
+    /** Status code */
+	t_u8 status_code;
+    /** GAS comback reply */
+	t_u16 gas_reply;
+    /** Advertize protocol IE */
+	t_u8 advertize_protocol_ie[4];
+    /** Query response Length */
+	t_u16 query_len;
+    /** Information identifier */
+	t_u8 info_id[2];
+    /** Response Length */
+	t_u16 response_len;
+    /** OUI */
+	t_u8 oui[3];
+    /** OUI sub type */
+	t_u8 oui_sub_type;
+    /** Service update indicator */
+	t_u16 service_update_indicator;
+    /** Vendor Length */
+	t_u16 vendor_len;
+    /** Service protocol */
+	t_u8 service_protocol;
+    /** Service transaction Id */
+	t_u8 service_transaction_id;
+    /** Discovery status code */
+	t_u8 disc_status_code;
+    /** Response Data */
+	wifidirect_response_data disc_resp;
+} PACK_END apeventbuf_wifidirect_discovery_response;
+
+/** enum : WPS attribute type */
+typedef enum {
+	SC_AP_Channel = 0x1001,
+	SC_Association_State = 0x1002,
+	SC_Authentication_Type = 0x1003,
+	SC_Authentication_Type_Flags = 0x1004,
+	SC_Authenticator = 0x1005,
+	SC_Config_Methods = 0x1008,
+	SC_Configuration_Error = 0x1009,
+	SC_Confirmation_URL4 = 0x100A,
+	SC_Confirmation_URL6 = 0x100B,
+	SC_Connection_Type = 0x100C,
+	SC_Connection_Type_Flags = 0x100D,
+	SC_Credential = 0x100E,
+	SC_Device_Name = 0x1011,
+	SC_Device_Password_ID = 0x1012,
+	SC_E_Hash1 = 0x1014,
+	SC_E_Hash2 = 0x1015,
+	SC_E_SNonce1 = 0x1016,
+	SC_E_SNonce2 = 0x1017,
+	SC_Encrypted_Settings = 0x1018,
+	SC_Encryption_Type = 0X100F,
+	SC_Encryption_Type_Flags = 0x1010,
+	SC_Enrollee_Nonce = 0x101A,
+	SC_Feature_ID = 0x101B,
+	SC_Identity = 0X101C,
+	SC_Identity_Proof = 0X101D,
+	SC_Key_Wrap_Authenticator = 0X101E,
+	SC_Key_Identifier = 0X101F,
+	SC_MAC_Address = 0x1020,
+	SC_Manufacturer = 0x1021,
+	SC_Message_Type = 0x1022,
+	SC_Model_Name = 0x1023,
+	SC_Model_Number = 0x1024,
+	SC_Network_Index = 0x1026,
+	SC_Network_Key = 0x1027,
+	SC_Network_Key_Index = 0x1028,
+	SC_New_Device_Name = 0x1029,
+	SC_New_Password = 0x102A,
+	SC_OOB_Device_Password = 0X102C,
+	SC_OS_Version = 0X102D,
+	SC_Power_Level = 0X102F,
+	SC_PSK_Current = 0x1030,
+	SC_PSK_Max = 0x1031,
+	SC_Public_Key = 0x1032,
+	SC_Radio_Enabled = 0x1033,
+	SC_Reboot = 0x1034,
+	SC_Registrar_Current = 0x1035,
+	SC_Registrar_Established = 0x1036,
+	SC_Registrar_List = 0x1037,
+	SC_Registrar_Max = 0x1038,
+	SC_Registrar_Nonce = 0x1039,
+	SC_Request_Type = 0x103A,
+	SC_Response_Type = 0x103B,
+	SC_RF_Band = 0X103C,
+	SC_R_Hash1 = 0X103D,
+	SC_R_Hash2 = 0X103E,
+	SC_R_SNonce1 = 0X103F,
+	SC_R_SNonce2 = 0x1040,
+	SC_Selected_Registrar = 0x1041,
+	SC_Serial_Number = 0x1042,
+	SC_Simple_Config_State = 0x1044,
+	SC_SSID = 0x1045,
+	SC_Total_Networks = 0x1046,
+	SC_UUID_E = 0x1047,
+	SC_UUID_R = 0x1048,
+	SC_Vendor_Extension = 0x1049,
+	SC_Version = 0x104A,
+	SC_X_509_Certificate_Request = 0x104B,
+	SC_X_509_Certificate = 0x104C,
+	SC_EAP_Identity = 0x104D,
+	SC_Message_Counter = 0x104E,
+	SC_Public_Key_Hash = 0x104F,
+	SC_Rekey_Key = 0x1050,
+	SC_Key_Lifetime = 0x1051,
+	SC_Permitted_Config_Methods = 0x1052,
+	SC_SelectedRegistrarConfigMethods = 0x1053,
+	SC_Primary_Device_Type = 0x1054,
+	SC_Secondary_Device_Type_List = 0x1055,
+	SC_Portable_Device = 0x1056,
+	SC_AP_Setup_Locked = 0x1057,
+	SC_Application_List = 0x1058,
+	SC_EAP_Type = 0x1059,
+	SC_Initialization_Vector = 0x1060,
+	SC_Key_Provided_Auto = 0x1061,
+	SC_8021x_Enabled = 0x1062,
+	SC_App_Session_key = 0x1063,
+	SC_WEP_Transmit_Key = 0x1064,
+} wps_simple_config_attribute;
+#endif
+
+/** structure for channel switch result from TDLS FW */
+typedef PACK_START struct _Channel_switch_result {
+    /** current channel, 0 - base channel, 1 - off channel*/
+	t_u8 current_channel;
+     /** channel switch status*/
+	t_u8 status;
+     /** channel switch fauilure reason code*/
+	t_u8 reason;
+} PACK_END Channel_switch_result;
+
+/** Event : TDLS Generic event */
+typedef PACK_START struct _eventbuf_tdls_generic {
+    /** Event Type */
+	t_u16 event_type;
+    /** Peer mac address */
+	t_u8 peer_mac_addr[ETH_ALEN];
+	union {
+	/** channel switch result structure*/
+		Channel_switch_result switch_result;
+	/** channel switch stop reason */
+		t_u8 cs_stop_reason;
+	/** Reason code */
+		t_u16 reason_code;
+	/** IE Length */
+		t_u16 ie_length;
+	} u;
+    /** IE pointer */
+	t_u8 ie_ptr[0];
+} PACK_END eventbuf_tdls_generic;
+
+/** Event : TDLS Debug event */
+typedef PACK_START struct _eventbuf_tdls_debug {
+    /** Event Type */
+	t_u16 event_type;
+    /** TimeStamp value */
+	t_u64 tsf;
+    /** First argument */
+	t_u32 first_arg;
+    /** Second argument */
+	t_u32 second_arg;
+    /** Third argument */
+	t_u32 third_arg;
+    /** Third argument */
+	t_u16 string_len;
+    /** Third argument */
+	t_u8 string[0];
+} PACK_END eventbuf_tdls_debug;
+
+/** Event : TDLS packet event */
+typedef PACK_START struct _eventbuf_tdls_packet {
+    /** Event Type */
+	t_u16 event_type;
+    /** Length */
+	t_u16 length;
+    /** packet data */
+	t_u8 data[0];
+} PACK_END eventbuf_tdls_packet;
+
+/** Eapol state */
+typedef PACK_START struct {
+    /** Eapol state*/
+	t_u8 eapol_state;
+    /** Station address*/
+	t_u8 sta_mac_addr[ETH_ALEN];
+} PACK_END eapol_state_t;
+
+/** Debug Info */
+typedef PACK_START union {
+    /** Eapol key message */
+	eapol_keymsg_debug_t eapol_pwkmsg;
+    /** Station reject*/
+	sta_reject_t sta_reject;
+    /** WPA IE */
+	wpaie_t wpaie;
+    /** Eapol state */
+	eapol_state_t eapol_state;
+} PACK_END d_info;
+
+/** Event body : Debug */
+typedef PACK_START struct _eventbuf_debug {
+    /** Debug type */
+	t_u8 debug_type;
+    /** Major debug id */
+	t_u32 debug_id_major;
+    /** Minor debug id */
+	t_u32 debug_id_minor;
+    /** Debug Info */
+	d_info info;
+} PACK_END eventbuf_debug;
+
+/** Event body : 11K NLIST */
+typedef PACK_START struct _nlist_entry_tlv {
+	tlvbuf_header hdr;
+	t_u8 bssid[6];
+	t_u8 bssid_info[4];
+	t_u8 reg_class;
+	t_u8 chan;
+	t_u8 phy_type;
+} PACK_END nlist_entry_tlv;
+
+int ishexstring(void *hex);
+unsigned int a2hex(char *s);
+/**
+ *    @brief isdigit for String.
+ *
+ *    @param x            Char string
+ *    @return             MLAN_EVENT_FAILURE for non-digit.
+ *                        0 for digit
+ */
+static inline int
+ISDIGIT(char *x)
+{
+	unsigned int i;
+	for (i = 0; i < strlen(x); i++)
+		if (isdigit(x[i]) == 0)
+			return MLAN_EVENT_FAILURE;
+	return 0;
+}
+
+#endif /* _MLAN_EVENT_H */
diff --git a/wlan_sd8897/mapp/mlanutl/Makefile b/wlan_sd8897/mapp/mlanutl/Makefile
new file mode 100644
index 0000000..b166f79
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanutl/Makefile
@@ -0,0 +1,54 @@
+# File : mlanutl/Makefile
+#
+# Copyright (C) 2011-2017, Marvell International Ltd. All Rights Reserved
+
+# Path to the top directory of the wlan distribution
+PATH_TO_TOP = ../..
+
+# Determine how we should copy things to the install directory
+ABSPATH := $(filter /%, $(INSTALLDIR))
+RELPATH := $(filter-out /%, $(INSTALLDIR))
+INSTALLPATH := $(ABSPATH)
+ifeq ($(strip $(INSTALLPATH)),)
+INSTALLPATH := $(PATH_TO_TOP)/$(RELPATH)
+endif
+
+# Override CFLAGS for application sources, remove __ kernel namespace defines
+CFLAGS := $(filter-out -D__%, $(ccflags-y))
+# remove KERNEL include dir
+CFLAGS := $(filter-out -I$(KERNELDIR)%, $(CFLAGS))
+
+
+#CFLAGS += -DAP22 -fshort-enums
+CFLAGS += -Wall
+#ECHO = @
+LIBS = -lrt
+
+.PHONY: default tags all
+
+OBJECTS = mlanutl.o
+HEADERS = mlanutl.h
+
+
+
+exectarget=mlanutl
+TARGET := $(exectarget)
+
+build default: $(TARGET)
+	@cp -f $(TARGET) $(INSTALLPATH)
+
+all : tags default
+
+$(TARGET): $(OBJECTS) $(HEADERS)
+	$(ECHO)$(CC) $(LIBS) -o $@ $(OBJECTS)
+
+%.o: %.c $(HEADERS)
+	$(ECHO)$(CC) $(CFLAGS) -c -o $@ $<
+
+tags:
+	ctags -R -f tags.txt
+
+distclean clean:
+	$(ECHO)$(RM) $(OBJECTS) $(TARGET)
+	$(ECHO)$(RM) tags.txt
+
diff --git a/wlan_sd8897/mapp/mlanutl/mlanhostcmd.c b/wlan_sd8897/mapp/mlanutl/mlanhostcmd.c
new file mode 100644
index 0000000..e61220f
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanutl/mlanhostcmd.c
@@ -0,0 +1,921 @@
+/** @file  mlanhostcmd.c
+  *
+  * @brief This file contains mlanutl helper functions
+  *
+  * Copyright (C) 2008-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     11/26/2008: initial version
+************************************************************************/
+
+#include	"mlanhostcmd.h"
+
+#ifndef MIN
+/** Find minimum value */
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
+
+/********************************************************
+		Local Variables
+********************************************************/
+
+/********************************************************
+		Global Variables
+********************************************************/
+
+/********************************************************
+		Local Functions
+********************************************************/
+/**
+ *  @brief get hostcmd data
+ *
+ *  @param ln			A pointer to line number
+ *  @param buf			A pointer to hostcmd data
+ *  @param size			A pointer to the return size of hostcmd buffer
+ *  @return      		MLAN_STATUS_SUCCESS
+ */
+static int
+mlan_get_hostcmd_data(FILE * fp, int *ln, t_u8 *buf, t_u16 *size)
+{
+	t_s32 errors = 0, i;
+	t_s8 line[256], *pos, *pos1, *pos2, *pos3;
+	t_u16 len;
+
+	while ((pos = mlan_config_get_line(fp, line, sizeof(line), ln))) {
+		(*ln)++;
+		if (strcmp(pos, "}") == 0) {
+			break;
+		}
+
+		pos1 = strchr(pos, ':');
+		if (pos1 == NULL) {
+			printf("Line %d: Invalid hostcmd line '%s'\n", *ln,
+			       pos);
+			errors++;
+			continue;
+		}
+		*pos1++ = '\0';
+
+		pos2 = strchr(pos1, '=');
+		if (pos2 == NULL) {
+			printf("Line %d: Invalid hostcmd line '%s'\n", *ln,
+			       pos);
+			errors++;
+			continue;
+		}
+		*pos2++ = '\0';
+
+		len = a2hex_or_atoi(pos1);
+		if (len < 1 || len > MRVDRV_SIZE_OF_CMD_BUFFER) {
+			printf("Line %d: Invalid hostcmd line '%s'\n", *ln,
+			       pos);
+			errors++;
+			continue;
+		}
+
+		*size += len;
+
+		if (*pos2 == '"') {
+			pos2++;
+			pos3 = strchr(pos2, '"');
+			if (pos3 == NULL) {
+				printf("Line %d: invalid quotation '%s'\n", *ln,
+				       pos);
+				errors++;
+				continue;
+			}
+			*pos3 = '\0';
+			memset(buf, 0, len);
+			memmove(buf, pos2, MIN(strlen(pos2), len));
+			buf += len;
+		} else if (*pos2 == '\'') {
+			pos2++;
+			pos3 = strchr(pos2, '\'');
+			if (pos3 == NULL) {
+				printf("Line %d: invalid quotation '%s'\n", *ln,
+				       pos);
+				errors++;
+				continue;
+			}
+			*pos3 = ',';
+			for (i = 0; i < len; i++) {
+				pos3 = strchr(pos2, ',');
+				if (pos3 != NULL) {
+					*pos3 = '\0';
+					*buf++ = (t_u8)a2hex_or_atoi(pos2);
+					pos2 = pos3 + 1;
+				} else
+					*buf++ = 0;
+			}
+		} else if (*pos2 == '{') {
+			t_u16 tlvlen = 0, tmp_tlvlen;
+			mlan_get_hostcmd_data(fp, ln, buf + len, &tlvlen);
+			tmp_tlvlen = tlvlen;
+			while (len--) {
+				*buf++ = (t_u8)(tmp_tlvlen & 0xff);
+				tmp_tlvlen >>= 8;
+			}
+			*size += tlvlen;
+			buf += tlvlen;
+		} else {
+			t_u32 value = a2hex_or_atoi(pos2);
+			while (len--) {
+				*buf++ = (t_u8)(value & 0xff);
+				value >>= 8;
+			}
+		}
+	}
+	return MLAN_STATUS_SUCCESS;
+}
+
+/********************************************************
+		Global Functions
+********************************************************/
+/**
+ *  @brief convert char to hex integer
+ *
+ *  @param chr 		char to convert
+ *  @return      	hex integer or 0
+ */
+int
+hexval(t_s32 chr)
+{
+	if (chr >= '0' && chr <= '9')
+		return chr - '0';
+	if (chr >= 'A' && chr <= 'F')
+		return chr - 'A' + 10;
+	if (chr >= 'a' && chr <= 'f')
+		return chr - 'a' + 10;
+
+	return 0;
+}
+
+/**
+ *  @brief Hump hex data
+ *
+ *  @param prompt	A pointer prompt buffer
+ *  @param p		A pointer to data buffer
+ *  @param len		the len of data buffer
+ *  @param delim	delim char
+ *  @return            	hex integer
+ */
+t_void
+hexdump(t_s8 *prompt, t_void *p, t_s32 len, t_s8 delim)
+{
+	t_s32 i;
+	t_u8 *s = p;
+
+	if (prompt) {
+		printf("%s: len=%d\n", prompt, (int)len);
+	}
+	for (i = 0; i < len; i++) {
+		if (i != len - 1)
+			printf("%02x%c", *s++, delim);
+		else
+			printf("%02x\n", *s);
+		if ((i + 1) % 16 == 0)
+			printf("\n");
+	}
+	printf("\n");
+}
+
+/**
+ *  @brief convert char to hex integer
+ *
+ *  @param chr		char
+ *  @return            	hex integer
+ */
+t_u8
+hexc2bin(t_s8 chr)
+{
+	if (chr >= '0' && chr <= '9')
+		chr -= '0';
+	else if (chr >= 'A' && chr <= 'F')
+		chr -= ('A' - 10);
+	else if (chr >= 'a' && chr <= 'f')
+		chr -= ('a' - 10);
+
+	return chr;
+}
+
+/**
+ *  @brief convert string to hex integer
+ *
+ *  @param s		A pointer string buffer
+ *  @return            	hex integer
+ */
+t_u32
+a2hex(t_s8 *s)
+{
+	t_u32 val = 0;
+
+	if (!strncasecmp("0x", s, 2)) {
+		s += 2;
+	}
+
+	while (*s && isxdigit(*s)) {
+		val = (val << 4) + hexc2bin(*s++);
+	}
+
+	return val;
+}
+
+/*
+ *  @brief convert String to integer
+ *
+ *  @param value	A pointer to string
+ *  @return             integer
+ */
+t_u32
+a2hex_or_atoi(t_s8 *value)
+{
+	if (value[0] == '0' && (value[1] == 'X' || value[1] == 'x')) {
+		return a2hex(value + 2);
+	} else if (isdigit(*value)) {
+		return atoi(value);
+	} else {
+		return *value;
+	}
+}
+
+/**
+ *  @brief convert string to hex
+ *
+ *  @param ptr		A pointer to data buffer
+ *  @param chr 		A pointer to return integer
+ *  @return      	A pointer to next data field
+ */
+t_s8 *
+convert2hex(t_s8 *ptr, t_u8 *chr)
+{
+	t_u8 val;
+
+	for (val = 0; *ptr && isxdigit(*ptr); ptr++) {
+		val = (val * 16) + hexval(*ptr);
+	}
+
+	*chr = val;
+
+	return ptr;
+}
+
+/**
+ *  @brief Check the Hex String
+ *  @param s  A pointer to the string
+ *  @return   MLAN_STATUS_SUCCESS --HexString, MLAN_STATUS_FAILURE --not HexString
+ */
+int
+ishexstring(t_s8 *s)
+{
+	int ret = MLAN_STATUS_FAILURE;
+	t_s32 tmp;
+
+	if (!strncasecmp("0x", s, 2)) {
+		s += 2;
+	}
+	while (*s) {
+		tmp = toupper(*s);
+		if (((tmp >= 'A') && (tmp <= 'F')) ||
+		    ((tmp >= '0') && (tmp <= '9'))) {
+			ret = MLAN_STATUS_SUCCESS;
+		} else {
+			ret = MLAN_STATUS_FAILURE;
+			break;
+		}
+		s++;
+	}
+
+	return ret;
+}
+
+/**
+ *  @brief Convert String to Integer
+ *  @param buf      A pointer to the string
+ *  @return         Integer
+ */
+int
+atoval(t_s8 *buf)
+{
+	if (!strncasecmp(buf, "0x", 2))
+		return a2hex(buf + 2);
+	else if (!ishexstring(buf))
+		return a2hex(buf);
+	else
+		return atoi(buf);
+}
+
+/**
+ *  @brief Prepare host-command buffer
+ *  @param fp		File handler
+ *  @param cmd_name	Command name
+ *  @param buf		A pointer to comand buffer
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+prepare_host_cmd_buffer(FILE * fp, char *cmd_name, t_u8 *buf)
+{
+	t_s8 line[256], cmdname[256], *pos, cmdcode[10];
+	HostCmd_DS_GEN *hostcmd;
+	int ln = 0;
+	int cmdname_found = 0, cmdcode_found = 0;
+
+	memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+	hostcmd = (HostCmd_DS_GEN *)buf;
+	hostcmd->command = 0xffff;
+
+	snprintf(cmdname, sizeof(cmdname), "%s={", cmd_name);
+	cmdname_found = 0;
+	while ((pos = mlan_config_get_line(fp, line, sizeof(line), &ln))) {
+		if (strcmp(pos, cmdname) == 0) {
+			cmdname_found = 1;
+			snprintf(cmdcode, sizeof(cmdcode), "CmdCode=");
+			cmdcode_found = 0;
+			while ((pos =
+				mlan_config_get_line(fp, line, sizeof(line),
+						     &ln))) {
+				if (strncmp(pos, cmdcode, strlen(cmdcode)) == 0) {
+					cmdcode_found = 1;
+					hostcmd->command =
+						a2hex_or_atoi(pos +
+							      strlen(cmdcode));
+					hostcmd->size = S_DS_GEN;
+					mlan_get_hostcmd_data(fp, &ln,
+							      buf +
+							      hostcmd->size,
+							      &hostcmd->size);
+					break;
+				}
+			}
+			if (!cmdcode_found) {
+				fprintf(stderr,
+					"mlanutl: CmdCode not found in conf file\n");
+				return MLAN_STATUS_FAILURE;
+			}
+			break;
+		}
+	}
+
+	if (!cmdname_found) {
+		fprintf(stderr,
+			"mlanutl: cmdname '%s' is not found in conf file\n",
+			cmd_name);
+		return MLAN_STATUS_FAILURE;
+	}
+
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+	hostcmd->command = cpu_to_le16(hostcmd->command);
+	hostcmd->size = cpu_to_le16(hostcmd->size);
+	return MLAN_STATUS_SUCCESS;
+}
+
+/** Config data header length */
+#define CFG_DATA_HEADER_LEN 6
+
+/**
+ *  @brief Prepare cfg-data buffer
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *  @param fp       File handler
+ *  @param buf      A pointer to comand buffer
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+prepare_cfg_data_buffer(int argc, char *argv[], FILE * fp, t_u8 *buf)
+{
+	int ln = 0, type;
+	HostCmd_DS_GEN *hostcmd;
+	HostCmd_DS_802_11_CFG_DATA *pcfg_data;
+
+	memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+	hostcmd = (HostCmd_DS_GEN *)buf;
+	hostcmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA);
+	pcfg_data = (HostCmd_DS_802_11_CFG_DATA *)(buf + S_DS_GEN);
+	pcfg_data->action =
+		(argc == 4) ? HostCmd_ACT_GEN_GET : HostCmd_ACT_GEN_SET;
+	type = atoi(argv[3]);
+	if ((type < 1) || (type > 2)) {
+		fprintf(stderr, "mlanutl: Invalid register type\n");
+		return MLAN_STATUS_FAILURE;
+	} else {
+		pcfg_data->type = type;
+	}
+	if (argc == 5) {
+		ln = fparse_for_hex(fp, pcfg_data->data);
+	}
+	pcfg_data->data_len = ln;
+	hostcmd->size =
+		cpu_to_le16(pcfg_data->data_len + S_DS_GEN +
+			    CFG_DATA_HEADER_LEN);
+	pcfg_data->data_len = cpu_to_le16(pcfg_data->data_len);
+	pcfg_data->type = cpu_to_le16(pcfg_data->type);
+	pcfg_data->action = cpu_to_le16(pcfg_data->action);
+
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Process host_cmd response
+ *  @param buf		A pointer to the response buffer
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_host_cmd_resp(t_u8 *buf)
+{
+	HostCmd_DS_GEN *hostcmd = (HostCmd_DS_GEN *)buf;
+	int ret = MLAN_STATUS_SUCCESS;
+
+	hostcmd->command = le16_to_cpu(hostcmd->command);
+	hostcmd->size = le16_to_cpu(hostcmd->size);
+	hostcmd->seq_num = le16_to_cpu(hostcmd->seq_num);
+	hostcmd->result = le16_to_cpu(hostcmd->result);
+
+	hostcmd->command &= ~HostCmd_RET_BIT;
+	if (!hostcmd->result) {
+		switch (hostcmd->command) {
+		case HostCmd_CMD_CFG_DATA:
+			{
+				HostCmd_DS_802_11_CFG_DATA *pstcfgData =
+					(HostCmd_DS_802_11_CFG_DATA *)(buf +
+								       S_DS_GEN);
+				pstcfgData->data_len =
+					le16_to_cpu(pstcfgData->data_len);
+				pstcfgData->action =
+					le16_to_cpu(pstcfgData->action);
+
+				if (pstcfgData->action == HostCmd_ACT_GEN_GET) {
+					hexdump("cfgdata", pstcfgData->data,
+						pstcfgData->data_len, ' ');
+				}
+				break;
+			}
+		case HostCmd_CMD_802_11_TPC_ADAPT_REQ:
+			{
+				mlan_ioctl_11h_tpc_resp *tpcIoctlResp =
+					(mlan_ioctl_11h_tpc_resp *)(buf +
+								    S_DS_GEN);
+				if (tpcIoctlResp->status_code == 0) {
+					printf("tpcrequest:  txPower(%d), linkMargin(%d), rssi(%d)\n", tpcIoctlResp->tx_power, tpcIoctlResp->link_margin, tpcIoctlResp->rssi);
+				} else {
+					printf("tpcrequest:  failure, status = %d\n", tpcIoctlResp->status_code);
+				}
+				break;
+			}
+		case HostCmd_CMD_802_11_CRYPTO:
+			{
+				t_u16 alg =
+					le16_to_cpu((t_u16)
+						    *(buf + S_DS_GEN +
+						      sizeof(t_u16)));
+				if (alg != CIPHER_TEST_AES_CCM) {
+					HostCmd_DS_802_11_CRYPTO *cmd =
+						(HostCmd_DS_802_11_CRYPTO *)(buf
+									     +
+									     S_DS_GEN);
+					cmd->encdec = le16_to_cpu(cmd->encdec);
+					cmd->algorithm =
+						le16_to_cpu(cmd->algorithm);
+					cmd->key_IV_length =
+						le16_to_cpu(cmd->key_IV_length);
+					cmd->key_length =
+						le16_to_cpu(cmd->key_length);
+					cmd->data.header.type =
+						le16_to_cpu(cmd->data.header.
+							    type);
+					cmd->data.header.len =
+						le16_to_cpu(cmd->data.header.
+							    len);
+
+					printf("crypto_result: encdec=%d algorithm=%d,KeyIVLen=%d," " KeyLen=%d,dataLen=%d\n", cmd->encdec, cmd->algorithm, cmd->key_IV_length, cmd->key_length, cmd->data.header.len);
+					hexdump("KeyIV", cmd->keyIV,
+						cmd->key_IV_length, ' ');
+					hexdump("Key", cmd->key,
+						cmd->key_length, ' ');
+					hexdump("Data", cmd->data.data,
+						cmd->data.header.len, ' ');
+				} else {
+					HostCmd_DS_802_11_CRYPTO_AES_CCM
+						*cmd_aes_ccm =
+						(HostCmd_DS_802_11_CRYPTO_AES_CCM
+						 *)(buf + S_DS_GEN);
+
+					cmd_aes_ccm->encdec
+						=
+						le16_to_cpu(cmd_aes_ccm->
+							    encdec);
+					cmd_aes_ccm->algorithm =
+						le16_to_cpu(cmd_aes_ccm->
+							    algorithm);
+					cmd_aes_ccm->key_length =
+						le16_to_cpu(cmd_aes_ccm->
+							    key_length);
+					cmd_aes_ccm->nonce_length =
+						le16_to_cpu(cmd_aes_ccm->
+							    nonce_length);
+					cmd_aes_ccm->AAD_length =
+						le16_to_cpu(cmd_aes_ccm->
+							    AAD_length);
+					cmd_aes_ccm->data.header.type =
+						le16_to_cpu(cmd_aes_ccm->data.
+							    header.type);
+					cmd_aes_ccm->data.header.len =
+						le16_to_cpu(cmd_aes_ccm->data.
+							    header.len);
+
+					printf("crypto_result: encdec=%d algorithm=%d, KeyLen=%d," " NonceLen=%d,AADLen=%d,dataLen=%d\n", cmd_aes_ccm->encdec, cmd_aes_ccm->algorithm, cmd_aes_ccm->key_length, cmd_aes_ccm->nonce_length, cmd_aes_ccm->AAD_length, cmd_aes_ccm->data.header.len);
+
+					hexdump("Key", cmd_aes_ccm->key,
+						cmd_aes_ccm->key_length, ' ');
+					hexdump("Nonce", cmd_aes_ccm->nonce,
+						cmd_aes_ccm->nonce_length, ' ');
+					hexdump("AAD", cmd_aes_ccm->AAD,
+						cmd_aes_ccm->AAD_length, ' ');
+					hexdump("Data", cmd_aes_ccm->data.data,
+						cmd_aes_ccm->data.header.len,
+						' ');
+				}
+				break;
+			}
+		case HostCmd_CMD_802_11_AUTO_TX:
+			{
+				HostCmd_DS_802_11_AUTO_TX *at =
+					(HostCmd_DS_802_11_AUTO_TX *)(buf +
+								      S_DS_GEN);
+
+				if (le16_to_cpu(at->action) ==
+				    HostCmd_ACT_GEN_GET) {
+					if (S_DS_GEN + sizeof(at->action) ==
+					    hostcmd->size) {
+						printf("auto_tx not configured\n");
+
+					} else {
+						MrvlIEtypesHeader_t *header =
+							&at->auto_tx.header;
+
+						header->type =
+							le16_to_cpu(header->
+								    type);
+						header->len =
+							le16_to_cpu(header->
+								    len);
+
+						if ((S_DS_GEN +
+						     sizeof(at->action)
+						     +
+						     sizeof(MrvlIEtypesHeader_t)
+						     + header->len ==
+						     hostcmd->size) &&
+						    (header->type ==
+						     TLV_TYPE_AUTO_TX)) {
+
+							AutoTx_MacFrame_t *atmf
+								=
+								&at->auto_tx.
+								auto_tx_mac_frame;
+
+							printf("Interval: %d second(s)\n", le16_to_cpu(atmf->interval));
+							printf("Priority: %#x\n", atmf->priority);
+							printf("Frame Length: %d\n", le16_to_cpu(atmf->frame_len));
+							printf("Dest Mac Address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", atmf->dest_mac_addr[0], atmf->dest_mac_addr[1], atmf->dest_mac_addr[2], atmf->dest_mac_addr[3], atmf->dest_mac_addr[4], atmf->dest_mac_addr[5]);
+							printf("Src Mac Address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", atmf->src_mac_addr[0], atmf->src_mac_addr[1], atmf->src_mac_addr[2], atmf->src_mac_addr[3], atmf->src_mac_addr[4], atmf->src_mac_addr[5]);
+
+							hexdump("Frame Payload",
+								atmf->payload,
+								le16_to_cpu
+								(atmf->
+								 frame_len)
+								-
+								MLAN_MAC_ADDR_LENGTH
+								* 2, ' ');
+						} else {
+							printf("incorrect auto_tx command response\n");
+						}
+					}
+				}
+				break;
+			}
+		case HostCmd_CMD_802_11_SUBSCRIBE_EVENT:
+			{
+				HostCmd_DS_802_11_SUBSCRIBE_EVENT *se =
+					(HostCmd_DS_802_11_SUBSCRIBE_EVENT
+					 *)(buf + S_DS_GEN);
+				if (le16_to_cpu(se->action) ==
+				    HostCmd_ACT_GEN_GET) {
+					int len =
+						S_DS_GEN +
+						sizeof
+						(HostCmd_DS_802_11_SUBSCRIBE_EVENT);
+					printf("\nEvent\t\tValue\tFreq\tsubscribed\n\n");
+					while (len < hostcmd->size) {
+						MrvlIEtypesHeader_t *header =
+							(MrvlIEtypesHeader_t
+							 *)(buf + len);
+						switch (le16_to_cpu
+							(header->type)) {
+						case TLV_TYPE_RSSI_LOW:
+							{
+								MrvlIEtypes_RssiThreshold_t
+									*low_rssi
+									=
+									(MrvlIEtypes_RssiThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Beacon Low RSSI\t%d\t%d\t%s\n", low_rssi->RSSI_value, low_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0001) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_SNR_LOW:
+							{
+								MrvlIEtypes_SnrThreshold_t
+									*low_snr
+									=
+									(MrvlIEtypes_SnrThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Beacon Low SNR\t%d\t%d\t%s\n", low_snr->SNR_value, low_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0002) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_FAILCOUNT:
+							{
+								MrvlIEtypes_FailureCount_t
+									*failure_count
+									=
+									(MrvlIEtypes_FailureCount_t
+									 *)(buf
+									    +
+									    len);
+								printf("Failure Count\t%d\t%d\t%s\n", failure_count->fail_value, failure_count->fail_freq, (le16_to_cpu(se->events) & 0x0004) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_BCNMISS:
+							{
+								MrvlIEtypes_BeaconsMissed_t
+									*bcn_missed
+									=
+									(MrvlIEtypes_BeaconsMissed_t
+									 *)(buf
+									    +
+									    len);
+								printf("Beacon Missed\t%d\tN/A\t%s\n", bcn_missed->beacon_missed, (le16_to_cpu(se->events) & 0x0008) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_RSSI_HIGH:
+							{
+								MrvlIEtypes_RssiThreshold_t
+									*high_rssi
+									=
+									(MrvlIEtypes_RssiThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Bcn High RSSI\t%d\t%d\t%s\n", high_rssi->RSSI_value, high_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0010) ? "yes" : "no");
+								break;
+							}
+
+						case TLV_TYPE_SNR_HIGH:
+							{
+								MrvlIEtypes_SnrThreshold_t
+									*high_snr
+									=
+									(MrvlIEtypes_SnrThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Beacon High SNR\t%d\t%d\t%s\n", high_snr->SNR_value, high_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0020) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_RSSI_LOW_DATA:
+							{
+								MrvlIEtypes_RssiThreshold_t
+									*low_rssi
+									=
+									(MrvlIEtypes_RssiThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Data Low RSSI\t%d\t%d\t%s\n", low_rssi->RSSI_value, low_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0040) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_SNR_LOW_DATA:
+							{
+								MrvlIEtypes_SnrThreshold_t
+									*low_snr
+									=
+									(MrvlIEtypes_SnrThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Data Low SNR\t%d\t%d\t%s\n", low_snr->SNR_value, low_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0080) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_RSSI_HIGH_DATA:
+							{
+								MrvlIEtypes_RssiThreshold_t
+									*high_rssi
+									=
+									(MrvlIEtypes_RssiThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Data High RSSI\t%d\t%d\t%s\n", high_rssi->RSSI_value, high_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0100) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_SNR_HIGH_DATA:
+							{
+								MrvlIEtypes_SnrThreshold_t
+									*high_snr
+									=
+									(MrvlIEtypes_SnrThreshold_t
+									 *)(buf
+									    +
+									    len);
+								printf("Data High SNR\t%d\t%d\t%s\n", high_snr->SNR_value, high_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0200) ? "yes" : "no");
+								break;
+							}
+						case TLV_TYPE_LINK_QUALITY:
+							{
+								MrvlIEtypes_LinkQuality_t
+									*link_qual
+									=
+									(MrvlIEtypes_LinkQuality_t
+									 *)(buf
+									    +
+									    len);
+								printf("Link Quality Parameters:\n");
+								printf("------------------------\n");
+								printf("Link Quality Event Subscribed\t%s\n", (le16_to_cpu(se->events) & 0x0400) ? "yes" : "no");
+								printf("Link SNR Threshold   = %d\n", le16_to_cpu(link_qual->link_SNR_thrs));
+								printf("Link SNR Frequency   = %d\n", le16_to_cpu(link_qual->link_SNR_freq));
+								printf("Min Rate Value       = %d\n", le16_to_cpu(link_qual->min_rate_val));
+								printf("Min Rate Frequency   = %d\n", le16_to_cpu(link_qual->min_rate_freq));
+								printf("Tx Latency Value     = %d\n", le32_to_cpu(link_qual->tx_latency_val));
+								printf("Tx Latency Threshold = %d\n", le32_to_cpu(link_qual->tx_latency_thrs));
+
+								break;
+							}
+						case TLV_TYPE_PRE_BEACON_LOST:
+							{
+								MrvlIEtypes_PreBeaconLost_t
+									*pre_bcn_lost
+									=
+									(MrvlIEtypes_PreBeaconLost_t
+									 *)(buf
+									    +
+									    len);
+								printf("------------------------\n");
+								printf("Pre-Beacon Lost Event Subscribed\t%s\n", (le16_to_cpu(se->events) & 0x0800) ? "yes" : "no");
+								printf("Pre-Beacon Lost: %d\n", pre_bcn_lost->pre_beacon_lost);
+								break;
+							}
+						default:
+							printf("Unknown subscribed event TLV Type=%#x," " Len=%d\n", le16_to_cpu(header->type), le16_to_cpu(header->len));
+							break;
+						}
+
+						len += (sizeof
+							(MrvlIEtypesHeader_t)
+							+
+							le16_to_cpu(header->
+								    len));
+					}
+				}
+				break;
+			}
+		case HostCmd_CMD_MAC_REG_ACCESS:
+		case HostCmd_CMD_BBP_REG_ACCESS:
+		case HostCmd_CMD_RF_REG_ACCESS:
+		case HostCmd_CMD_CAU_REG_ACCESS:
+			{
+				HostCmd_DS_REG *preg =
+					(HostCmd_DS_REG *)(buf + S_DS_GEN);
+				preg->action = le16_to_cpu(preg->action);
+				if (preg->action == HostCmd_ACT_GEN_GET) {
+					preg->value = le32_to_cpu(preg->value);
+					printf("value = 0x%08x\n", preg->value);
+				}
+				break;
+			}
+		case HostCmd_CMD_MEM_ACCESS:
+			{
+				HostCmd_DS_MEM *pmem =
+					(HostCmd_DS_MEM *)(buf + S_DS_GEN);
+				pmem->action = le16_to_cpu(pmem->action);
+				if (pmem->action == HostCmd_ACT_GEN_GET) {
+					pmem->value = le32_to_cpu(pmem->value);
+					printf("value = 0x%08x\n", pmem->value);
+				}
+				break;
+			}
+		default:
+			printf("HOSTCMD_RESP: CmdCode=%#04x, Size=%#04x,"
+			       " SeqNum=%#04x, Result=%#04x\n",
+			       hostcmd->command, hostcmd->size,
+			       hostcmd->seq_num, hostcmd->result);
+			hexdump("payload",
+				(t_void *)(buf + S_DS_GEN),
+				hostcmd->size - S_DS_GEN, ' ');
+			break;
+		}
+	} else {
+		printf("HOSTCMD failed: CmdCode=%#04x, Size=%#04x,"
+		       " SeqNum=%#04x, Result=%#04x\n",
+		       hostcmd->command, hostcmd->size,
+		       hostcmd->seq_num, hostcmd->result);
+	}
+	return ret;
+}
+
+/**
+ *  @brief Prepare ARP filter buffer
+ *  @param fp		File handler
+ *  @param buf		A pointer to the buffer
+ *  @param length	A pointer to the length of buffer
+ *  @return      	MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+prepare_arp_filter_buffer(FILE * fp, t_u8 *buf, t_u16 *length)
+{
+	t_s8 line[256], *pos;
+	int ln = 0;
+	int ret = MLAN_STATUS_SUCCESS;
+	int arpfilter_found = 0;
+
+	memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+	while ((pos = mlan_config_get_line(fp, line, sizeof(line), &ln))) {
+		if (strcmp(pos, "arpfilter={") == 0) {
+			arpfilter_found = 1;
+			mlan_get_hostcmd_data(fp, &ln, buf, length);
+			break;
+		}
+	}
+	if (!arpfilter_found) {
+		fprintf(stderr, "mlanutl: 'arpfilter' not found in conf file");
+		ret = MLAN_STATUS_FAILURE;
+	}
+	return ret;
+}
+
+/**
+ *  @brief Prepare the hostcmd for register access
+ *  @param type     Register type
+ *  @param offset   Register offset
+ *  @param value    Pointer to value (NULL for read)
+ *  @param buf      Pointer to hostcmd buffer
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+prepare_hostcmd_regrdwr(t_u32 type, t_u32 offset, t_u32 *value, t_u8 *buf)
+{
+	HostCmd_DS_GEN *hostcmd;
+	HostCmd_DS_REG *preg;
+
+	hostcmd = (HostCmd_DS_GEN *)buf;
+	switch (type) {
+	case 1:
+		hostcmd->command = cpu_to_le16(HostCmd_CMD_MAC_REG_ACCESS);
+		break;
+	case 2:
+		hostcmd->command = cpu_to_le16(HostCmd_CMD_BBP_REG_ACCESS);
+		break;
+	case 3:
+		hostcmd->command = cpu_to_le16(HostCmd_CMD_RF_REG_ACCESS);
+		break;
+	case 5:
+		hostcmd->command = cpu_to_le16(HostCmd_CMD_CAU_REG_ACCESS);
+		break;
+	default:
+		printf("Invalid register set specified\n");
+		return -EINVAL;
+	}
+	preg = (HostCmd_DS_REG *)(buf + S_DS_GEN);
+	preg->action = (value) ? HostCmd_ACT_GEN_SET : HostCmd_ACT_GEN_GET;
+	preg->action = cpu_to_le16(preg->action);
+	preg->offset = cpu_to_le16((t_u16)offset);
+	if (value)
+		preg->value = cpu_to_le32(*value);
+	else
+		preg->value = 0;
+	hostcmd->size = cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_REG));
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+
+	return MLAN_STATUS_SUCCESS;
+}
diff --git a/wlan_sd8897/mapp/mlanutl/mlanhostcmd.h b/wlan_sd8897/mapp/mlanutl/mlanhostcmd.h
new file mode 100644
index 0000000..47a4cf9
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanutl/mlanhostcmd.h
@@ -0,0 +1,119 @@
+/** @file  mlanhostcmd.h
+  *
+  * @brief This file contains command structures for mlanutl application
+  *
+  * Copyright (C) 2008-2017, Marvell International Ltd.
+  *
+  * This software file (the "File") is distributed by Marvell 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.
+  *
+  */
+/************************************************************************
+Change log:
+     11/26/2008: initial version
+************************************************************************/
+#ifndef _MLANHOSTCMD_H_
+#define _MLANHOSTCMD_H_
+
+/** Find number of elements */
+#define NELEMENTS(x) (sizeof(x)/sizeof(x[0]))
+
+/** Size of command buffer */
+#define MRVDRV_SIZE_OF_CMD_BUFFER		(2 * 1024)
+
+/** Host Command ID : Memory access */
+#define HostCmd_CMD_MEM_ACCESS                0x0086
+
+/** Pre-Authenticate - 11r only */
+#define HostCmd_CMD_802_11_AUTHENTICATE       0x0011
+
+/** Read/Write Mac register */
+#define HostCmd_CMD_MAC_REG_ACCESS            0x0019
+/** Read/Write BBP register */
+#define HostCmd_CMD_BBP_REG_ACCESS            0x001a
+/** Read/Write RF register */
+#define HostCmd_CMD_RF_REG_ACCESS             0x001b
+/** Get TX Power data */
+#define HostCmd_CMD_802_11_RF_TX_POWER        0x001e
+/** Host Command ID : CAU register access */
+#define HostCmd_CMD_CAU_REG_ACCESS            0x00ed
+
+/** Host Command ID : 802.11 BG scan configuration */
+#define HostCmd_CMD_802_11_BG_SCAN_CONFIG     0x006b
+/** Host Command ID : Configuration data */
+#define HostCmd_CMD_CFG_DATA                  0x008f
+/** Host Command ID : 802.11 TPC adapt req */
+#define HostCmd_CMD_802_11_TPC_ADAPT_REQ      0x0060
+/** Host Command ID : 802.11 crypto */
+#define HostCmd_CMD_802_11_CRYPTO             0x0078
+/** Host Command ID : 802.11 auto Tx */
+#define HostCmd_CMD_802_11_AUTO_TX      	0x0082
+
+/** Host Command ID : 802.11 subscribe event */
+#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT       0x0075
+
+/** Host Command ID : Channel TRPC config */
+#define HostCmd_CMD_CHAN_TRPC_CONFIG                0x00fb
+
+/** TLV  type ID definition */
+#define PROPRIETARY_TLV_BASE_ID     0x0100
+/** TLV type : Beacon RSSI low */
+#define TLV_TYPE_RSSI_LOW           (PROPRIETARY_TLV_BASE_ID + 0x04)	/* 0x0104 */
+/** TLV type : Beacon SNR low */
+#define TLV_TYPE_SNR_LOW            (PROPRIETARY_TLV_BASE_ID + 0x05)	/* 0x0105 */
+/** TLV type : Fail count */
+#define TLV_TYPE_FAILCOUNT          (PROPRIETARY_TLV_BASE_ID + 0x06)	/* 0x0106 */
+/** TLV type : BCN miss */
+#define TLV_TYPE_BCNMISS            (PROPRIETARY_TLV_BASE_ID + 0x07)	/* 0x0107 */
+/** TLV type : Beacon RSSI high */
+#define TLV_TYPE_RSSI_HIGH          (PROPRIETARY_TLV_BASE_ID + 0x16)	/* 0x0116 */
+/** TLV type : Beacon SNR high */
+#define TLV_TYPE_SNR_HIGH           (PROPRIETARY_TLV_BASE_ID + 0x17)	/* 0x0117 */
+/** TLV type : Auto Tx */
+#define TLV_TYPE_AUTO_TX            (PROPRIETARY_TLV_BASE_ID + 0x18)	/* 0x0118 */
+/** TLV type :Link Quality */
+#define TLV_TYPE_LINK_QUALITY       (PROPRIETARY_TLV_BASE_ID + 0x24)	/* 0x0124 */
+/** TLV type : Data RSSI low */
+#define TLV_TYPE_RSSI_LOW_DATA      (PROPRIETARY_TLV_BASE_ID + 0x26)	/* 0x0126 */
+/** TLV type : Data SNR low */
+#define TLV_TYPE_SNR_LOW_DATA       (PROPRIETARY_TLV_BASE_ID + 0x27)	/* 0x0127 */
+/** TLV type : Data RSSI high */
+#define TLV_TYPE_RSSI_HIGH_DATA     (PROPRIETARY_TLV_BASE_ID + 0x28)	/* 0x0128 */
+/** TLV type : Data SNR high */
+#define TLV_TYPE_SNR_HIGH_DATA      (PROPRIETARY_TLV_BASE_ID + 0x29)	/* 0x0129 */
+/** TLV type: Pre-Beacon Lost */
+#define TLV_TYPE_PRE_BEACON_LOST    (PROPRIETARY_TLV_BASE_ID + 0x49)	/* 0x0149 */
+
+/** TLV type : Channel TRPC */
+#define TLV_TYPE_CHAN_TRPC              (PROPRIETARY_TLV_BASE_ID + 0x89)	/* 0x0189 */
+
+/** mlan_ioctl_11h_tpc_resp */
+typedef struct {
+	int status_code;
+		     /**< Firmware command result status code */
+	int tx_power;/**< Reported TX Power from the TPC Report */
+	int link_margin;
+		     /**< Reported Link margin from the TPC Report */
+	int rssi;    /**< RSSI of the received TPC Report frame */
+} __ATTRIB_PACK__ mlan_ioctl_11h_tpc_resp;
+
+/* Define general hostcmd data structure */
+
+/** Convert String to integer */
+t_u32 a2hex_or_atoi(char *value);
+char *mlan_config_get_line(FILE * fp, char *str, t_s32 size, int *lineno);
+
+int prepare_host_cmd_buffer(FILE * fp, char *cmd_name, t_u8 *buf);
+int prepare_hostcmd_regrdwr(t_u32 type, t_u32 offset, t_u32 *value, t_u8 *buf);
+
+#endif /* _MLANHOSTCMD_H_ */
diff --git a/wlan_sd8897/mapp/mlanutl/mlanoffload.c b/wlan_sd8897/mapp/mlanutl/mlanoffload.c
new file mode 100644
index 0000000..ffb3880
--- /dev/null
+++ b/wlan_sd8897/mapp/mlanutl/mlanoffload.c
@@ -0,0 +1,2519 @@
+/** @file  mlanoffload.c
+ *
+ * @brief This files contains mlanutl offload command handling.
+ *
+ * Copyright (C) 2008-2017, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell 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.
+ *
+ */
+/************************************************************************
+Change log:
+     08/11/2009: initial version
+************************************************************************/
+
+#include    "mlanutl.h"
+#include    "mlanhostcmd.h"
+#include    "mlanoffload.h"
+
+/********************************************************
+				Local Variables
+********************************************************/
+
+t_void hexdump(char *prompt, t_void *p, t_s32 len, t_s8 delim);
+
+/********************************************************
+				Global Variables
+********************************************************/
+
+/********************************************************
+				Local Functions
+********************************************************/
+
+/**
+ *  @brief Remove unwanted spaces, tabs from a line
+ *
+ *  @param data     A pointer to the starting of the line
+ *  @return         NA
+ */
+static void
+profile_param_polish(char *data)
+{
+	t_u8 i, j, len = 0;
+	char *ptr;
+	ptr = strrchr(data, '\r');
+	if (ptr == NULL) {
+		ptr = strrchr(data, '\n');
+		if (ptr == NULL) {
+			return;
+		}
+	}
+	len = ptr - data;
+	for (i = 0; i < len; i++) {
+		if ((*(data + i) == ' ') || (*(data + i) == '\t')) {
+			for (j = i; j < len; j++) {
+				data[j] = data[j + 1];
+			}
+			i--;
+			len--;
+		}
+	}
+}
+
+static int
+ascii_value(char letter)
+{
+	if (letter >= '0' && letter <= '9')
+		return letter - '0';
+	if (letter >= 'a' && letter <= 'f')
+		return letter - 'a' + 10;
+	if (letter >= 'A' && letter <= 'F')
+		return letter - 'A' + 10;
+	return -1;
+}
+
+static int
+twodigit_ascii(const char *nibble)
+{
+	int a, b;
+	a = ascii_value(*nibble++);
+	if (a < 0)
+		return -1;
+	b = ascii_value(*nibble++);
+	if (b < 0)
+		return -1;
+	return (a << 4) | b;
+}
+
+/**
+ *  @brief Read a network block from the profile configuration file
+ *
+ *  @param fp       file pointer of the configuration file
+ *  @param p_head   profile head
+ *  @return         MLAN_STATUS_SUCCESS
+ */
+static int
+profile_read_block(FILE * fp, profile_entry_t *p_head)
+{
+	char line[0x100];
+	char *ptr, *eptr;
+	t_u8 key_cnt = 0;
+	t_u8 i, wep_len;
+	int byte;
+	int tmpIdx;
+	unsigned int mac[ETH_ALEN];
+
+	while (fgets(line, sizeof(line), fp)) {
+		/* call function to remove spaces, tabs */
+		profile_param_polish(line);
+
+		if (strstr(line, "}") != NULL) {
+			ptr = strstr(line, "}");
+			/* end of network */
+			break;
+
+		} else if (line[0] == '#') {
+			/* comments go ahead */
+			continue;
+
+		} else if (strstr(line, "bssid=") != NULL) {
+			ptr = strstr(line, "bssid=");
+			ptr = ptr + strlen("bssid=");
+			sscanf(ptr, "%2x:%2x:%2x:%2x:%2x:%2x",
+			       mac + 0, mac + 1, mac + 2, mac + 3, mac + 4,
+			       mac + 5);
+			for (tmpIdx = 0; (unsigned int)tmpIdx < NELEMENTS(mac);
+			     tmpIdx++) {
+				p_head->bssid[tmpIdx] = (t_u8)mac[tmpIdx];
+			}
+
+		} else if (strstr(line, "ssid=") != NULL) {
+
+			ptr = strstr(line, "ssid=");
+			ptr = ptr + strlen("ssid=");
+			eptr = strrchr(ptr + 1, '"');
+
+			if ((*ptr != '"') || (strrchr(ptr + 1, '"') == NULL)) {
+
+				fprintf(stderr, "ssid not within quotes\n");
+				break;
+			}
+
+			p_head->ssid_len =
+				MIN(IW_ESSID_MAX_SIZE, eptr - ptr - 1);
+			strncpy((char *)p_head->ssid, ptr + 1,
+				p_head->ssid_len);
+			p_head->ssid[p_head->ssid_len] = '\0';
+
+		} else if (strstr(line, "psk=") != NULL) {
+			ptr = strstr(line, "psk=");
+			ptr = ptr + strlen("psk=");
+			if (*ptr != '"') {
+				p_head->psk_config = 1;
+				strncpy((char *)p_head->psk, ptr, KEY_LEN);
+			} else {
+				eptr = strrchr(ptr + 1, '"');
+				if (eptr == NULL) {
+					fprintf(stderr,
+						"passphrase not within quotes.\n");
+					break;
+				}
+				p_head->passphrase_len =
+					MIN(PHRASE_LEN, eptr - ptr - 1);
+				strncpy((char *)p_head->passphrase, ptr + 1,
+					p_head->passphrase_len);
+			}
+		} else if (strstr(line, "wep_key") != NULL) {
+			ptr = strstr(line, "wep_key");
+			ptr = ptr + strlen("wep_key");
+			key_cnt = atoi(ptr);
+			ptr++;
+			if (*ptr != '=') {
+				fprintf(stderr,
+					"invalid wep_key, missing =.\n");
+				break;
+			}
+			eptr = strrchr(ptr + 1, '\r');
+			if (eptr == NULL) {
+				eptr = strrchr(ptr + 1, '\n');
+				if (eptr == NULL) {
+					fprintf(stderr,
+						"missing EOL from the wep_key config\n");
+					break;
+				}
+			}
+			ptr++;
+			if (*ptr == '"') {
+				eptr = strrchr(ptr + 1, '"');
+				if (eptr == NULL) {
+					fprintf(stderr,
+						"wep key does not end with quote.\n");
+					break;
+				}
+				*eptr = '\0';
+				p_head->wep_key_len[key_cnt] = eptr - ptr - 1;
+				strncpy((char *)p_head->wep_key[key_cnt],
+					ptr + 1, p_head->wep_key_len[key_cnt]);
+			} else {
+				while (*eptr == '\r' || *eptr == '\n')
+					eptr--;
+				*(eptr + 1) = '\0';
+				wep_len = strlen(ptr);
+				if (wep_len & 0x01) {
+					fprintf(stderr,
+						"incorrect wep key %s.\n", ptr);
+					break;
+				}
+				p_head->wep_key_len[key_cnt] = wep_len / 2;
+				for (i = 0; i < wep_len / 2; i++) {
+					byte = twodigit_ascii(ptr);
+					if (byte == -1) {
+						fprintf(stderr,
+							"incorrect wep key %s.\n",
+							ptr);
+						break;
+					}
+					*(p_head->wep_key[key_cnt] + i) =
+						(t_u8)byte;
+					ptr += 2;
+				}
+			}
+		} else if (strstr(line, "key_mgmt=") != NULL) {
+			ptr = strstr(line, "key_mgmt=");
+			ptr = ptr + strlen("key_mgmt=");
+			eptr = strstr(ptr, "WPA-EAP");
+			if (eptr != NULL) {
+				p_head->key_mgmt |=
+					PROFILE_DB_KEY_MGMT_IEEE8021X;
+			}
+			eptr = strstr(ptr, "WPA-PSK");
+			if (eptr != NULL) {
+				p_head->key_mgmt |= PROFILE_DB_KEY_MGMT_PSK;
+			}
+			eptr = strstr(ptr, "FT-EAP");
+			if (eptr != NULL) {
+				p_head->key_mgmt |=
+					PROFILE_DB_KEY_MGMT_FT_IEEE8021X;
+			}
+			eptr = strstr(ptr, "FT-PSK");
+			if (eptr != NULL) {
+				p_head->key_mgmt |= PROFILE_DB_KEY_MGMT_FT_PSK;
+			}
+			eptr = strstr(ptr, "WPA-EAP-SHA256");
+			if (eptr != NULL) {
+				p_head->key_mgmt |=
+					PROFILE_DB_KEY_MGMT_SHA256_IEEE8021X;
+			}
+			eptr = strstr(ptr, "WPA-PSK-SHA256");
+			if (eptr != NULL) {
+				p_head->key_mgmt |=
+					PROFILE_DB_KEY_MGMT_SHA256_PSK;
+			}
+			eptr = strstr(ptr, "CCKM");
+			if (eptr != NULL) {
+				p_head->key_mgmt |= PROFILE_DB_KEY_MGMT_CCKM;
+			}
+			eptr = strstr(ptr, "NONE");
+			if (eptr != NULL) {
+				p_head->key_mgmt |= PROFILE_DB_KEY_MGMT_NONE;
+			}
+		} else if (strstr(line, "proto=") != NULL) {
+			ptr = strstr(line, "proto=");
+			ptr = ptr + strlen("proto=");
+			eptr = strstr(ptr, "WPA");
+			if (eptr != NULL) {
+				p_head->protocol |= PROFILE_DB_PROTO_WPA;
+			}
+
+			eptr = strstr(ptr, "RSN");
+			if (eptr != NULL) {
+				p_head->protocol |= PROFILE_DB_PROTO_WPA2;
+
+			}
+		} else if (strstr(line, "pairwise=") != NULL) {
+			ptr = strstr(line, "pairwise=");
+			ptr = ptr + strlen("pairwise=");
+			eptr = strstr(ptr, "CCMP");
+			if (eptr != NULL) {
+				p_head->pairwise_cipher |=
+					PROFILE_DB_CIPHER_CCMP;
+			}
+			eptr = strstr(ptr, "TKIP");
+			if (eptr != NULL) {
+				p_head->pairwise_cipher |=
+					PROFILE_DB_CIPHER_TKIP;
+			}
+		} else if (strstr(line, "groupwise=") != NULL) {
+			ptr = strstr(line, "groupwise=");
+			ptr = ptr + strlen("groupwise=");
+			eptr = strstr(ptr, "CCMP");
+			if (eptr != NULL) {
+				p_head->groupwise_cipher |=
+					PROFILE_DB_CIPHER_CCMP;
+			}
+			eptr = strstr(ptr, "TKIP");
+			if (eptr != NULL) {
+				p_head->groupwise_cipher |=
+					PROFILE_DB_CIPHER_TKIP;
+			}
+		} else if (strstr(line, "wep_tx_keyidx=") != NULL) {
+			ptr = strstr(line, "wep_tx_keyidx=");
+			ptr = ptr + strlen("wep_tx_keyidx=");
+			p_head->wep_key_idx = atoi(ptr);
+		} else if (strstr(line, "roaming=") != NULL) {
+			ptr = strstr(line, "roaming=");
+			ptr = ptr + strlen("roaming=");
+			p_head->roaming = atoi(ptr);
+		} else if (strstr(line, "ccx=") != NULL) {
+			ptr = strstr(line, "ccx=");
+			ptr = ptr + strlen("ccx=");
+			p_head->ccx = atoi(ptr);
+		} else if (strstr(line, "mode=") != NULL) {
+			ptr = strstr(line, "mode=");
+			ptr = ptr + strlen("mode=");
+			p_head->mode = atoi(ptr);
+		}
+	}
+	return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ *  @brief Issue profile command to add new profile to FW
+ *
+ *  @param filename     Name of profile file
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+static int
+profile_read_download(char *filename)
+{
+	int ret = MLAN_STATUS_SUCCESS;
+	struct ifreq ifr;
+	t_u8 *buffer = NULL, *pos = NULL;
+	int i = 0;
+	t_u16 temp, tempc;
+	t_u32 cmd_len = 0, cmd_header_len;
+	struct eth_priv_cmd *cmd = NULL;
+	HostCmd_DS_GEN *hostcmd = NULL;
+	profile_entry_t *p_head = NULL;
+	FILE *fp;
+	char line[0x100];
+
+	fp = fopen(filename, "r");
+	if (fp == NULL) {
+		perror("fopen");
+		fprintf(stderr, "Cannot open file %s\n", filename);
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD);
+
+	buffer = (t_u8 *)malloc(BUFFER_LENGTH);
+	if (buffer == NULL) {
+		fprintf(stderr, "Cannot alloc memory\n");
+		ret = ENOMEM;
+		goto done;
+	}
+	memset(buffer, 0, BUFFER_LENGTH);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		free(buffer);
+		ret = ENOMEM;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = BUFFER_LENGTH;
+
+	/* buffer = MRVL_CMD<cmd> */
+	strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL));
+	strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD));
+
+	/* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */
+	hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32));
+
+	pos = (t_u8 *)hostcmd;
+	while (fgets(line, sizeof(line), fp)) {
+		/* call function to remove spaces, tabs */
+		profile_param_polish(line);
+		if ((strstr(line, "network={") == NULL) || (line[0] == '#')) {
+			continue;
+		}
+		/*
+		 * Memory allocation of every network block
+		 */
+		p_head = (profile_entry_t *)malloc(sizeof(profile_entry_t));
+		if (p_head == NULL) {
+			fprintf(stderr, "Memory error.\n");
+			ret = MLAN_STATUS_FAILURE;
+			goto done;
+		}
+		memset(p_head, 0x00, sizeof(profile_entry_t));
+
+		ret = profile_read_block(fp, p_head);
+		if (ret || p_head->ssid_len == 0) {
+			free(p_head);
+			continue;
+		}
+
+		/*
+		 * Put all the ssid parameters in the buffer
+		 */
+		memset(pos, 0,
+		       (BUFFER_LENGTH - cmd_header_len - sizeof(t_u32)));
+
+		/* Cmd Header : Command */
+		hostcmd->command = cpu_to_le16(HostCmd_CMD_PROFILE_DB);
+		cmd_len = sizeof(HostCmd_DS_GEN);
+
+		/* set action as set */
+		tempc = cpu_to_le16(HostCmd_ACT_GEN_SET);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+
+		/* ssid */
+		tempc = cpu_to_le16(TLV_TYPE_SSID);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		temp = strlen((char *)p_head->ssid);
+		tempc = cpu_to_le16(temp);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		memcpy((void *)(pos + cmd_len), p_head->ssid, temp);
+		cmd_len += temp;
+
+		if (memcmp(p_head->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN)) {
+			/* bssid */
+			tempc = cpu_to_le16(TLV_TYPE_BSSID);
+			memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+			cmd_len += 2;
+			temp = ETH_ALEN;
+			tempc = cpu_to_le16(temp);
+			memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+			cmd_len += 2;
+			memcpy((void *)(pos + cmd_len), p_head->bssid, temp);
+			cmd_len += temp;
+		}
+
+		/* proto */
+		if (p_head->protocol == 0) {
+			p_head->protocol = 0xFFFF;
+		}
+
+		tempc = cpu_to_le16(TLV_TYPE_PROTO);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		temp = 2;
+		tempc = cpu_to_le16(temp);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		memcpy((pos + cmd_len), &(p_head->protocol), temp);
+		cmd_len += temp;
+
+		/* key_mgmt */
+		if (p_head->key_mgmt == 0) {
+			p_head->key_mgmt = 0xFFFF;
+		}
+
+		tempc = cpu_to_le16(TLV_TYPE_AKMP);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		temp = 2;
+		tempc = cpu_to_le16(temp);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		memcpy((pos + cmd_len), &(p_head->key_mgmt), temp);
+		cmd_len += temp;
+
+		/* pairwise */
+		if (p_head->pairwise_cipher == 0) {
+			p_head->pairwise_cipher = 0xFF;
+		}
+
+		/* groupwise */
+		if (p_head->groupwise_cipher == 0) {
+			p_head->groupwise_cipher = 0xFF;
+		}
+
+		tempc = cpu_to_le16(TLV_TYPE_CIPHER);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		temp = 2;
+		tempc = cpu_to_le16(temp);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		pos[cmd_len] = p_head->pairwise_cipher;
+		cmd_len += 1;
+		pos[cmd_len] = p_head->groupwise_cipher;
+		cmd_len += 1;
+
+		if (p_head->passphrase_len) {
+			/* passphrase */
+			tempc = cpu_to_le16(TLV_TYPE_PASSPHRASE);
+			memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+			cmd_len += 2;
+			temp = p_head->passphrase_len;
+			tempc = cpu_to_le16(temp);
+			memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+			cmd_len += 2;
+			memcpy((void *)(pos + cmd_len), p_head->passphrase,
+			       temp);
+			cmd_len += temp;
+		}
+
+		if (p_head->psk_config) {
+			/* psk method */
+			tempc = cpu_to_le16(TLV_TYPE_PMK);
+			memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+			cmd_len += 2;
+			temp = 32;
+			tempc = cpu_to_le16(temp);
+			memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+			cmd_len += 2;
+			memcpy((void *)(pos + cmd_len), p_head->psk, temp);
+			cmd_len += temp;
+		}
+
+		for (i = 0; i < WEP_KEY_CNT; i++) {
+			if (p_head->wep_key_len[i]) {
+				/* TAG_WEP_KEY */
+				tempc = cpu_to_le16(TLV_TYPE_WEP_KEY);
+				memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+				cmd_len += 2;
+				/* wep_key_len + sizeof(keyIndex) + sizeof(IsDefault) */
+				tempc = cpu_to_le16(p_head->wep_key_len[i] + 1 +
+						    1);
+				memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+				cmd_len += 2;
+				*(pos + cmd_len) = i;
+				cmd_len += 1;
+				*(pos + cmd_len) = (i == p_head->wep_key_idx);
+				cmd_len += 1;
+				temp = p_head->wep_key_len[i];
+				memcpy((void *)(pos + cmd_len),
+				       p_head->wep_key[i], temp);
+				cmd_len += temp;
+			}
+		}
+
+		if (p_head->roaming | p_head->ccx) {
+			tempc = cpu_to_le16(TLV_TYPE_OFFLOAD_ENABLE);
+			memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+			cmd_len += 2;
+			temp = 2;
+			tempc = cpu_to_le16(temp);
+			memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+			cmd_len += 2;
+			tempc = 0;
+			if (p_head->roaming)
+				tempc |= PROFILE_DB_FEATURE_ROAMING;
+			if (p_head->ccx)
+				tempc |= PROFILE_DB_FEATURE_CCX;
+			tempc = cpu_to_le16(tempc);
+			memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+			cmd_len += temp;
+		}
+
+		/* Put buffer length */
+		memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32));
+		hostcmd->size = cpu_to_le16(cmd_len);
+
+		fprintf(stdout, "Downloading profile: %s ... ", p_head->ssid);
+		fflush(stdout);
+
+		/* Initialize the ifr structure */
+		memset(&ifr, 0, sizeof(ifr));
+		strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+		ifr.ifr_ifru.ifru_data = (void *)cmd;
+		/* Perform ioctl */
+		if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+			perror("ioctl[profiledb ioctl]");
+			printf("ERR:Command sending failed!\n");
+			ret = -EFAULT;
+			goto done;
+		} else {
+			hostcmd->result = le16_to_cpu(hostcmd->result);
+			if (hostcmd->result != 0) {
+				printf("hostcmd : profiledb ioctl failure, code %d\n", hostcmd->result);
+				ret = -EFAULT;
+				goto done;
+			}
+		}
+
+		fprintf(stdout, "done.\n");
+
+		if (p_head)
+			free(p_head);
+	}
+
+done:
+	if (buffer)
+		free(buffer);
+	if (cmd)
+		free(cmd);
+	if (fp)
+		fclose(fp);
+	return ret;
+}
+
+/********************************************************
+				Global Functions
+********************************************************/
+
+/**
+ *  @brief Process sub command
+ *
+ *  @param sub_cmd      Sub command
+ *  @param num_sub_cmds Number of subcommands
+ *  @param argc         Number of arguments
+ *  @param argv         A pointer to arguments array
+ *
+ *  @return             MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_sub_cmd(sub_cmd_exec_t *sub_cmd, int num_sub_cmds,
+		int argc, char *argv[])
+{
+	int idx;
+	boolean invalid_cmd = TRUE;
+	int ret = MLAN_STATUS_FAILURE;
+
+	if (argv[3]) {
+		for (idx = 0; idx < num_sub_cmds; idx++) {
+			if (strncmp(argv[3],
+				    sub_cmd[idx].str,
+				    sub_cmd[idx].match_len) == 0) {
+				invalid_cmd = FALSE;
+				ret = sub_cmd[idx].callback(argc - 4, argv + 4);
+				break;
+			}
+		}
+	}
+
+	if (invalid_cmd) {
+		printf("\nUnknown %s command. Valid subcmds:\n", argv[2]);
+		for (idx = 0; idx < num_sub_cmds; idx++) {
+			if (sub_cmd[idx].display) {
+				printf("  - %s\n", sub_cmd[idx].str);
+			}
+		}
+		printf("\n");
+	}
+
+	return ret;
+}
+
+/**
+ *  @brief  select the table's regclass
+ *
+ *  @param table_str  Reg channel table type
+ *  @param pTable     Pointer to the Reg channel table
+ *
+ *  @return           TRUE if success otherwise FALSE
+ */
+boolean
+reg_class_table_select(char *table_str, reg_chan_table_e *pTable)
+{
+	boolean retval = TRUE;
+
+	if (strcmp(table_str, "user") == 0) {
+		*pTable = REGTABLE_USER;
+	} else if ((strcmp(table_str, "md") == 0) ||
+		   (strncmp(table_str, "multidomain", 5) == 0)) {
+		*pTable = REGTABLE_MULTIDOMAIN;
+	} else if (strcmp(table_str, "ess") == 0) {
+		*pTable = REGTABLE_ESS;
+	} else if (strcmp(table_str, "default") == 0) {
+		*pTable = REGTABLE_DEFAULT;
+	} else {		/* If no option/wrong option set to default */
+		*pTable = REGTABLE_DEFAULT;
+	}
+
+	return retval;
+}
+
+/**
+ *  @brief Issue a measurement timing command
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_measurement(int argc, char *argv[])
+{
+	int ret = 0;
+	struct ifreq ifr;
+	t_u8 *buffer = NULL, *pos = NULL;
+	t_u32 cmd_len = 0, cmd_header_len;
+	struct eth_priv_cmd *cmd = NULL;
+	HostCmd_DS_GEN *hostcmd = NULL;
+	HostCmd_DS_MEASUREMENT_Timing *timing_cmd = NULL;
+	MrvlIETypes_MeasTiming_t *timing_tlv = NULL;
+	int idx, rsp_len;
+	t_u8 sel = 0;
+	t_u16 tlv_len = 0;
+	timing_sel_t sel_str[] = { {"disconnected", 1},
+	{"adhoc", 1},
+	{"fullpower", 1},
+	{"ieeeps", 1},
+	{"periodic", 1}
+	};
+
+	if ((argc < 4) || strncmp(argv[3], "timing",
+				  MAX(strlen("timing"), strlen(argv[3])))) {
+		printf("\nUnknown %s command. Valid subcmd: timing \n",
+		       argv[2]);
+		return MLAN_STATUS_FAILURE;
+	}
+
+	cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD);
+	cmd_len = S_DS_GEN + sizeof(t_u16);
+
+	buffer = (t_u8 *)malloc(BUFFER_LENGTH);
+	if (buffer == NULL) {
+		fprintf(stderr, "Cannot alloc memory\n");
+		return -ENOMEM;
+	}
+	memset(buffer, 0, BUFFER_LENGTH);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		free(buffer);
+		return -ENOMEM;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = BUFFER_LENGTH;
+
+	/* buffer = MRVL_CMD<cmd> */
+	strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL));
+	strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD));
+
+	/* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */
+	hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32));
+	hostcmd->command = cpu_to_le16(HostCmd_CMD_MEASUREMENT_TIMING_CONFIG);
+	hostcmd->size = cmd_len;
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+
+	/* Point after host command header */
+	pos = (t_u8 *)hostcmd + S_DS_GEN;
+
+	timing_cmd = (HostCmd_DS_MEASUREMENT_Timing *)pos;
+	timing_cmd->action = cpu_to_le16(HostCmd_ACT_GEN_GET);
+	timing_tlv = (MrvlIETypes_MeasTiming_t *)timing_cmd->tlv_buffer;
+
+	if (argc == 7) {
+		timing_cmd->action = cpu_to_le16(HostCmd_ACT_GEN_SET);
+		timing_tlv->header.type =
+			cpu_to_le16(TLV_TYPE_MEASUREMENT_TIMING);
+		timing_tlv->header.len =
+			cpu_to_le16(sizeof(MrvlIETypes_MeasTiming_t)
+				    - sizeof(timing_tlv->header));
+
+		for (idx = 1; (unsigned int)idx < NELEMENTS(sel_str); idx++) {
+			if (strncmp
+			    (argv[4], sel_str[idx].str,
+			     sel_str[idx].match_len) == 0) {
+				sel = idx + 1;
+				break;
+			}
+		}
+
+		if (idx == NELEMENTS(sel_str)) {
+			printf("Wrong argument for mode selected \"%s\"\n",
+			       argv[4]);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		timing_tlv->mode = cpu_to_le32(sel);
+		timing_tlv->max_off_channel = cpu_to_le32(atoi(argv[5]));
+		timing_tlv->max_on_channel = cpu_to_le32(atoi(argv[6]));
+		cmd_len += sizeof(MrvlIETypes_MeasTiming_t);
+	}
+
+	/* Put buffer length */
+	memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32));
+	hostcmd->size = cpu_to_le16(cmd_len);
+
+	/* Initialize the ifr structure */
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+
+	/* Perform ioctl */
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("ioctl[measurement timing ioctl]");
+		printf("ERR:Command sending failed!\n");
+		ret = -EFAULT;
+		goto done;
+	}
+
+	printf("--------------------------------------------------\n");
+	printf("%44s\n", "Measurement Timing Profiles (in ms)");
+	printf("--------------------------------------------------\n");
+	printf("     Profile    |  MaxOffChannel |  MaxOnChannel\n");
+	printf("--------------------------------------------------\n");
+
+	/* Changed to TLV parsing */
+	rsp_len = le16_to_cpu(hostcmd->size);
+	rsp_len -= (S_DS_GEN + sizeof(t_u16));
+	pos = (t_u8 *)hostcmd + S_DS_GEN + sizeof(t_u16);
+	while ((unsigned int)rsp_len > sizeof(MrvlIEtypesHeader_t)) {
+		switch (le16_to_cpu(*(t_u16 *)(pos))) {
+		case TLV_TYPE_MEASUREMENT_TIMING:
+			timing_tlv = (MrvlIETypes_MeasTiming_t *)pos;
+			tlv_len = le16_to_cpu(timing_tlv->header.len);
+			printf("%15s | %14d | %13d\n",
+			       sel_str[le32_to_cpu(timing_tlv->mode) - 1].str,
+			       (int)le32_to_cpu(timing_tlv->max_off_channel),
+			       (int)le32_to_cpu(timing_tlv->max_on_channel));
+			break;
+		}
+		pos += tlv_len + sizeof(MrvlIEtypesHeader_t);
+		rsp_len -= tlv_len + sizeof(MrvlIEtypesHeader_t);
+		rsp_len = (rsp_len > 0) ? rsp_len : 0;
+	}
+	printf("\n");
+
+done:
+	if (buffer)
+		free(buffer);
+	if (cmd)
+		free(cmd);
+	return ret;
+}
+
+/**
+ *  @brief Issue a profile command
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_profile_entry(int argc, char *argv[])
+{
+	int ret = MLAN_STATUS_SUCCESS;
+	struct ifreq ifr;
+	unsigned int mac[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+	int idx;
+	t_u16 temp, tempc;
+	char *ssid = NULL;
+	t_u8 *buffer = NULL, *pos = NULL;
+	t_u32 cmd_len = 0, cmd_header_len;
+	struct eth_priv_cmd *cmd = NULL;
+	HostCmd_DS_GEN *hostcmd = NULL;
+
+	cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD);
+
+	if (argc < 4) {
+		fprintf(stderr, "Invalid number of argument!\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	if (!strncmp(argv[3], "delete", sizeof("delete"))) {
+		if (argc > 4) {
+			if (strncmp(argv[4], "bssid=", strlen("bssid=")) == 0) {
+				/* "bssid" token string handler */
+				sscanf(argv[4] + strlen("bssid="),
+				       "%2x:%2x:%2x:%2x:%2x:%2x", mac + 0,
+				       mac + 1, mac + 2, mac + 3, mac + 4,
+				       mac + 5);
+			} else if (strncmp(argv[4], "ssid=", strlen("ssid=")) ==
+				   0) {
+				/* "ssid" token string handler */
+				ssid = argv[4] + strlen("ssid=");
+			} else {
+				printf("Error: missing required option for command (ssid, bssid)\n");
+				ret = -ENOMEM;
+				goto done;
+			}
+			printf("Driver profile delete request\n");
+		} else {
+			printf("Error: missing required option for command (ssid, bssid)\n");
+			ret = -ENOMEM;
+			goto done;
+		}
+	} else if (!strncmp(argv[3], "flush", sizeof("flush"))) {
+		printf("Driver profile flush request\n");
+	} else {
+		ret = profile_read_download(argv[3]);
+		goto done;
+	}
+
+	buffer = (t_u8 *)malloc(BUFFER_LENGTH);
+	if (buffer == NULL) {
+		fprintf(stderr, "Cannot alloc memory\n");
+		ret = -ENOMEM;
+		goto done;
+	}
+	memset(buffer, 0, BUFFER_LENGTH);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = BUFFER_LENGTH;
+
+	/* buffer = MRVL_CMD<cmd> */
+	strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL));
+	strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD));
+
+	/* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */
+	hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32));
+	hostcmd->command = cpu_to_le16(HostCmd_CMD_PROFILE_DB);
+	hostcmd->size = 0;
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+
+	/* Point after host command header */
+	pos = (t_u8 *)hostcmd;
+	cmd_len = S_DS_GEN;
+
+	/* set action as del */
+	tempc = cpu_to_le16(HostCmd_ACT_GEN_REMOVE);
+	memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+	cmd_len += 2;
+
+	/* ssid */
+	if (ssid) {
+		printf("For ssid %s\n", ssid);
+		tempc = cpu_to_le16(TLV_TYPE_SSID);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		temp = strlen((char *)ssid);
+		tempc = cpu_to_le16(temp);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		memcpy((void *)(pos + cmd_len), ssid, temp);
+		cmd_len += temp;
+	} else {
+		/* bssid */
+		if (mac[0] != 0xFF) {
+			printf("For bssid %02x:%02x:%02x:%02x:%02x:%02x\n",
+			       mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+		}
+		tempc = cpu_to_le16(TLV_TYPE_BSSID);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		temp = ETH_ALEN;
+		tempc = cpu_to_le16(temp);
+		memcpy((pos + cmd_len), &tempc, sizeof(t_u16));
+		cmd_len += 2;
+		for (idx = 0; (unsigned int)idx < NELEMENTS(mac); idx++) {
+			pos[cmd_len + idx] = (t_u8)mac[idx];
+		}
+		cmd_len += temp;
+	}
+
+	/* Put buffer length */
+	memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32));
+	hostcmd->size = cpu_to_le16(cmd_len);
+
+	/* Initialize the ifr structure */
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+	/* Perform ioctl */
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("ioctl[profiledb ioctl]");
+		printf("ERR:Command sending failed!\n");
+		ret = -EFAULT;
+		goto done;
+	} else {
+		hostcmd->result = le16_to_cpu(hostcmd->result);
+		if (hostcmd->result != 0) {
+			printf("hostcmd : profiledb ioctl failure, code %d\n",
+			       hostcmd->result);
+			ret = -EFAULT;
+			goto done;
+		}
+	}
+
+done:
+	if (buffer)
+		free(buffer);
+	if (cmd)
+		free(cmd);
+	return ret;
+}
+
+/**
+ *  @brief Issue a chan report command
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_chanrpt(int argc, char *argv[])
+{
+	int ret = MLAN_STATUS_SUCCESS;
+	int respLen;
+	struct ifreq ifr;
+	t_u8 *buffer = NULL, *pos = NULL;
+	t_u32 cmd_len = 0, cmd_header_len;
+	struct eth_priv_cmd *cmd = NULL;
+	t_u8 *pByte;
+	t_u8 numBins;
+	t_u8 idx;
+	MrvlIEtypes_Data_t *pTlvHdr;
+	HostCmd_DS_GEN *hostcmd;
+	HostCmd_DS_CHAN_RPT_RSP *pChanRptRsp;
+	HostCmd_DS_CHAN_RPT_REQ *pChanRptReq;
+
+	MrvlIEtypes_ChanRptBcn_t *pBcnRpt;
+	MrvlIEtypes_ChanRptChanLoad_t *pLoadRpt;
+	MrvlIEtypes_ChanRptNoiseHist_t *pNoiseRpt;
+	MrvlIEtypes_ChanRpt11hBasic_t *pBasicRpt;
+	MrvlIEtypes_ChanRptFrame_t *pFrameRpt;
+
+	cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD);
+
+	buffer = (t_u8 *)malloc(BUFFER_LENGTH);
+	if (buffer == NULL) {
+		fprintf(stderr, "Cannot alloc memory\n");
+		ret = ENOMEM;
+		goto done;
+	}
+	memset(buffer, 0, BUFFER_LENGTH);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = ENOMEM;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = BUFFER_LENGTH;
+
+	/* buffer = MRVL_CMD<cmd> */
+	strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL));
+	strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD));
+
+	/* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */
+	hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32));
+
+	/* Point after host command header */
+	pos = (t_u8 *)hostcmd + S_DS_GEN;
+
+	cmd_len = S_DS_GEN + sizeof(HostCmd_DS_CHAN_RPT_REQ);
+
+	hostcmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST);
+	hostcmd->size = cpu_to_le16(cmd_len);
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+
+	pChanRptReq = (HostCmd_DS_CHAN_RPT_REQ *)pos;
+	pChanRptRsp = (HostCmd_DS_CHAN_RPT_RSP *)pos;
+
+	memset((void *)pChanRptReq, 0x00, sizeof(HostCmd_DS_CHAN_RPT_REQ));
+
+	if ((argc != 5) && (argc != 6)) {
+		printf("\nchanrpt syntax: chanrpt <chan#> <millisecs> [sFreq]\n\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	pChanRptReq->chanDesc.chanNum = atoi(argv[3]);
+	pChanRptReq->millisecDwellTime = cpu_to_le32(atoi(argv[4]));
+
+	if (argc == 6) {
+		pChanRptReq->chanDesc.startFreq = cpu_to_le16(atoi(argv[5]));
+	}
+
+	/* Put buffer length */
+	memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32));
+
+	/* Initialize the ifr structure */
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+	/* Perform ioctl */
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("ioctl[chanrpt hostcmd]");
+		printf("ERR:Command sending failed!\n");
+		ret = -EFAULT;
+		goto done;
+	}
+
+	/* TSF is a t_u64, some formatted printing libs have
+	 *   trouble printing long longs, so cast and dump as bytes
+	 */
+	pByte = (t_u8 *)&pChanRptRsp->startTsf;
+
+	printf("\n");
+	printf("[%03d]      TSF: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+	       atoi(argv[3]),
+	       pByte[7], pByte[6], pByte[5], pByte[4],
+	       pByte[3], pByte[2], pByte[1], pByte[0]);
+	printf("[%03d]    Dwell: %u us\n",
+	       atoi(argv[3]), (unsigned int)le32_to_cpu(pChanRptRsp->duration));
+
+	pByte = pChanRptRsp->tlvBuffer;
+
+	respLen = le16_to_cpu(hostcmd->size) - sizeof(HostCmd_DS_GEN);
+
+	respLen -= sizeof(pChanRptRsp->commandResult);
+	respLen -= sizeof(pChanRptRsp->startTsf);
+	respLen -= sizeof(pChanRptRsp->duration);
+
+	pByte = pChanRptRsp->tlvBuffer;
+
+	while ((unsigned int)respLen >= sizeof(pTlvHdr->header)) {
+		pTlvHdr = (MrvlIEtypes_Data_t *)pByte;
+		pTlvHdr->header.len = le16_to_cpu(pTlvHdr->header.len);
+
+		switch (le16_to_cpu(pTlvHdr->header.type)) {
+		case TLV_TYPE_CHANRPT_BCN:
+			pBcnRpt = (MrvlIEtypes_ChanRptBcn_t *)pTlvHdr;
+			printf("[%03d]   Beacon: scanReqId = %d\n",
+			       atoi(argv[3]), pBcnRpt->scanReqId);
+
+			break;
+
+		case TLV_TYPE_CHANRPT_CHAN_LOAD:
+			pLoadRpt = (MrvlIEtypes_ChanRptChanLoad_t *)pTlvHdr;
+			printf("[%03d] ChanLoad: %d%%\n",
+			       atoi(argv[3]),
+			       (pLoadRpt->ccaBusyFraction * 100) / 255);
+			break;
+
+		case TLV_TYPE_CHANRPT_NOISE_HIST:
+			pNoiseRpt = (MrvlIEtypes_ChanRptNoiseHist_t *)pTlvHdr;
+			numBins =
+				pNoiseRpt->header.len - sizeof(pNoiseRpt->anpi);
+			printf("[%03d]     ANPI: %d dB\n", atoi(argv[3]),
+			       le16_to_cpu(pNoiseRpt->anpi));
+			printf("[%03d] NoiseHst:", atoi(argv[3]));
+			for (idx = 0; idx < numBins; idx++) {
+				printf(" %03d", pNoiseRpt->rpiDensities[idx]);
+			}
+			printf("\n");
+			break;
+
+		case TLV_TYPE_CHANRPT_11H_BASIC:
+			pBasicRpt = (MrvlIEtypes_ChanRpt11hBasic_t *)pTlvHdr;
+			printf("[%03d] 11hBasic: BSS(%d), OFDM(%d), UnId(%d), Radar(%d): " "[0x%02x]\n", atoi(argv[3]), pBasicRpt->map.BSS, pBasicRpt->map.OFDM_Preamble, pBasicRpt->map.Unidentified, pBasicRpt->map.Radar, *(t_u8 *)&pBasicRpt->map);
+			break;
+
+		case TLV_TYPE_CHANRPT_FRAME:
+			pFrameRpt = (MrvlIEtypes_ChanRptFrame_t *)pTlvHdr;
+			printf("[%03d]    Frame: %02x:%02x:%02x:%02x:%02x:%02x  " "%02x:%02x:%02x:%02x:%02x:%02x  %3d   %02d\n", atoi(argv[3]), pFrameRpt->sourceAddr[0], pFrameRpt->sourceAddr[1], pFrameRpt->sourceAddr[2], pFrameRpt->sourceAddr[3], pFrameRpt->sourceAddr[4], pFrameRpt->sourceAddr[5], pFrameRpt->bssid[0], pFrameRpt->bssid[1], pFrameRpt->bssid[2], pFrameRpt->bssid[3], pFrameRpt->bssid[4], pFrameRpt->bssid[5], pFrameRpt->rssi, pFrameRpt->frameCnt);
+			break;
+
+		default:
+			printf("[%03d] Other: Id=0x%x, Size = %d\n",
+			       atoi(argv[3]),
+			       pTlvHdr->header.type, pTlvHdr->header.len);
+
+			break;
+		}
+
+		pByte += (pTlvHdr->header.len + sizeof(pTlvHdr->header));
+		respLen -= (pTlvHdr->header.len + sizeof(pTlvHdr->header));
+		respLen = (respLen > 0) ? respLen : 0;
+	}
+
+	printf("\n");
+
+done:
+	if (buffer)
+		free(buffer);
+	if (cmd)
+		free(cmd);
+	return ret;
+}
+
+/**
+ *  @brief Issue a assoc timing command
+ *
+ *  @param argc     number of arguments
+ *  @param argv     A pointer to arguments array
+ *
+ *  @return         MLAN_STATUS_SUCCESS--success, otherwise--fail
+ */
+int
+process_assoc_timing(int argc, char *argv[])
+{
+	int ret = MLAN_STATUS_SUCCESS;
+	struct ifreq ifr;
+	t_u8 *buffer = NULL, *pos = NULL;
+	t_u32 cmd_len = 0, cmd_header_len;
+	struct eth_priv_cmd *cmd = NULL;
+	HostCmd_DS_GEN *hostcmd;
+	HostCmd_DS_AssociationTiming_t *assoctiming;
+
+	cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD);
+
+	buffer = (t_u8 *)malloc(BUFFER_LENGTH);
+	if (buffer == NULL) {
+		fprintf(stderr, "Cannot alloc memory\n");
+		ret = ENOMEM;
+		goto done;
+	}
+	memset(buffer, 0, BUFFER_LENGTH);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = ENOMEM;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = BUFFER_LENGTH;
+
+	/* buffer = MRVL_CMD<cmd> */
+	strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL));
+	strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD));
+
+	/* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */
+	hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32));
+
+	/* Point after host command header */
+	pos = (t_u8 *)hostcmd + S_DS_GEN;
+
+	cmd_len = S_DS_GEN + sizeof(HostCmd_DS_AssociationTiming_t);
+
+	hostcmd->command = cpu_to_le16(HostCmd_CMD_ASSOCIATION_TIMING);
+	hostcmd->size = cpu_to_le16(cmd_len);
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+
+	assoctiming = (HostCmd_DS_AssociationTiming_t *)pos;
+	assoctiming->Action = cpu_to_le16(HostCmd_ACT_GEN_GET);
+
+	/* Put buffer length */
+	memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32));
+
+	/* Initialize the ifr structure */
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+	/* Perform ioctl */
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("ioctl[hostcmd]");
+		printf("ERR:Command sending failed!\n");
+		ret = -EFAULT;
+		goto done;
+	}
+
+	if (argc > 3) {
+		assoctiming->Action = cpu_to_le16(HostCmd_ACT_GEN_SET);
+		switch (argc) {
+		case 9:
+			assoctiming->ReassocDiscMax =
+				cpu_to_le16(atoi(argv[8]));
+			/* No break, do everything below as well */
+		case 8:
+			assoctiming->PriorApDeauthDelay =
+				cpu_to_le16(atoi(argv[7]));
+			/* No break, do everything below as well */
+		case 7:
+			assoctiming->FrameExchangeTimeout =
+				cpu_to_le16(atoi(argv[6]));
+			/* No break, do everything below as well */
+		case 6:
+			assoctiming->HandShakeTimeout =
+				cpu_to_le16(atoi(argv[5]));
+			/* No break, do everything below as well */
+		case 5:
+			assoctiming->ReassocTimeout =
+				cpu_to_le16(atoi(argv[4]));
+			/* No break, do everything below as well */
+		case 4:
+			assoctiming->AssocTimeout = cpu_to_le16(atoi(argv[3]));
+			break;
+		}
+	}
+
+	/* Put buffer length */
+	memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32));
+
+	/* Initialize the ifr structure */
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+	/* Perform ioctl */
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("ioctl[hostcmd]");
+		printf("ERR:Command sending failed!\n");
+		ret = -EFAULT;
+		goto done;
+	}
+
+	puts("");
+	printf("------------------------------------------------\n");
+	printf("        Association Timing Parameters\n");
+	printf("------------------------------------------------\n");
+
+	printf("Association Timeout     %5u ms\n"
+	       "Reassociation Timeout   %5u ms\n"
+	       "Handshake Timeout       %5u ms\n"
+	       "Frame Exchange Timeout  %5u ms\n"
+	       "Prior AP Deauth Delay   %5u ms\n"
+	       "Reassoc Disconnect Max  %5u ms\n",
+	       le16_to_cpu(assoctiming->AssocTimeout),
+	       le16_to_cpu(assoctiming->ReassocTimeout),
+	       le16_to_cpu(assoctiming->HandShakeTimeout),
+	       le16_to_cpu(assoctiming->FrameExchangeTimeout),
+	       le16_to_cpu(assoctiming->PriorApDeauthDelay),
+	       le16_to_cpu(assoctiming->ReassocDiscMax));
+	puts("");
+
+done:
+	if (buffer)
+		free(buffer);
+	if (cmd)
+		free(cmd);
+	return ret;
+}
+
+/**
+ *  @brief Retrieve the association response from the driver
+ *
+ *  Retrieve the buffered (re)association management frame from the driver.
+ *    The response is identical to the one received from the AP and conforms
+ *    to the IEEE specification.
+ *
+ *  @return MLAN_STATUS_SUCCESS or ioctl error code
+ */
+int
+process_get_assocrsp(int argc, char *argv[])
+{
+	int ret = 0;
+	t_u8 *buffer = NULL;
+	struct eth_priv_cmd *cmd = NULL;
+	struct ifreq ifr;
+	IEEEtypes_AssocRsp_t *pAssocRsp = NULL;
+
+	/* Initialize buffer */
+	buffer = (t_u8 *)malloc(BUFFER_LENGTH);
+	if (!buffer) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		return MLAN_STATUS_FAILURE;
+	}
+
+	pAssocRsp = (IEEEtypes_AssocRsp_t *)buffer;
+
+	/* buffer = MRVL_CMD<cmd> */
+	strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL));
+	strncpy((char *)buffer + strlen(CMD_MARVELL), argv[2], strlen(argv[2]));
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		free(buffer);
+		return MLAN_STATUS_FAILURE;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = BUFFER_LENGTH;
+
+	/* Perform IOCTL */
+	memset(&ifr, 0, sizeof(struct ifreq));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("mlanutl");
+		fprintf(stderr, "mlanutl: version fail\n");
+		ret = MLAN_STATUS_FAILURE;
+		goto done;
+	}
+
+	if (cmd->used_len) {
+		printf("getassocrsp: Status[%d], Cap[0x%04x]:\n",
+		       pAssocRsp->StatusCode,
+		       le16_to_cpu(*(t_u16 *)&pAssocRsp->Capability));
+		hexdump(NULL, buffer, cmd->used_len, ' ');
+	} else {
+		printf("getassocrsp: <empty>\n");
+	}
+
+done:
+	if (buffer)
+		free(buffer);
+	if (cmd)
+		free(cmd);
+
+	return ret;
+}
+
+/*
+**  Process mlanutl fcontrol command:
+**
+**    mlanutl mlanX fcontrol %d [0xAA 0xBB... ]
+**
+**  Sets and/or retrieves the feature control settings for a specific
+**    control set (argv[3] decimal argument).
+**
+*/
+int
+process_fcontrol(int argc, char *argv[])
+{
+	int ret = MLAN_STATUS_SUCCESS;
+	struct ifreq ifr;
+	t_u8 *buffer = NULL, *pos = NULL;
+	t_u32 cmd_len = 0, cmd_header_len;
+	struct eth_priv_cmd *cmd = NULL;
+	t_u8 idx;
+	HostCmd_DS_GEN *hostcmd;
+	HostCmd_DS_OFFLOAD_FEATURE_CONTROL *pFcontrol;
+
+	cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD);
+
+	buffer = (t_u8 *)malloc(BUFFER_LENGTH);
+	if (buffer == NULL) {
+		fprintf(stderr, "Cannot alloc memory\n");
+		ret = ENOMEM;
+		goto done;
+	}
+	memset(buffer, 0, BUFFER_LENGTH);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = ENOMEM;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd));
+	memcpy(&cmd->buf, &buffer, sizeof(buffer));
+#else
+	cmd->buf = buffer;
+#endif
+	cmd->used_len = 0;
+	cmd->total_len = BUFFER_LENGTH;
+
+	/* buffer = MRVL_CMD<cmd> */
+	strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL));
+	strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD));
+
+	/* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */
+	hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32));
+
+	/* Point after host command header */
+	pos = (t_u8 *)hostcmd + S_DS_GEN;
+
+	pFcontrol = (HostCmd_DS_OFFLOAD_FEATURE_CONTROL *)pos;
+
+	if (argc < 4) {
+		printf("Wrong number of arguments\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pFcontrol->controlSelect = atoi(argv[3]);
+	cmd_len = S_DS_GEN + sizeof(pFcontrol->controlSelect);
+
+	for (idx = 4; idx < argc; idx++) {
+		pFcontrol->controlBitmap[idx - 4] = a2hex_or_atoi(argv[idx]);
+		cmd_len++;
+	}
+
+	hostcmd->command = cpu_to_le16(HostCmd_CMD_OFFLOAD_FEATURE_CONTROL);
+	hostcmd->size = cpu_to_le16(cmd_len);
+	hostcmd->seq_num = 0;
+	hostcmd->result = 0;
+
+	/* Put buffer length */
+	memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32));
+
+	/* Initialize the ifr structure */
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name));
+	ifr.ifr_ifru.ifru_data = (void *)cmd;
+	/* Perform ioctl */
+	if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) {
+		perror("ioctl[fcontrol hostcmd]");
+		printf("ERR:Command sending failed!\n");
+		ret = -EFAULT;
+		goto done;
+	}
+
+	cmd_len = (le16_to_cpu(hostcmd->size) - sizeof(HostCmd_DS_GEN));
+
+	printf("Control[%d]", pFcontrol->controlSelect);
+	cmd_len--;
+
+	for (idx = 0; idx < cmd_len; idx++) {
+		printf("\t0x%02x", pFcontrol->controlBitmap[idx]);
+	}
+
+	printf("\n");
+
+done:
+	if (buffer)
+		free(buffer);
+	if (cmd)
+		free(cmd);
+	return ret;
+}
+
+/*
+**  Process mlanutl iapp command:
+**
+**    mlanutl mlanX iapp <timeout> 0xAA 0xBB [0x... 0x.. ]
+**
+**    0xAA = IAPP type
+**    0xBB = IAPP subtype
+**    0x.. = Remaning bytes are iapp data
+**
+*/
+int
+process_iapp(int argc, char *argv[])
+{
+	int ret = MLAN_STATUS_SUCCESS;
+	struct ifreq ifr;
+	t_u8 *buffer = NULL, *pos = NULL;
+	t_u32 cmd_len = 0, cmd_header_len;
+	struct eth_priv_cmd *cmd = NULL;
+	t_u8 idx;
+	t_u8 fixlen;
+	HostCmd_DS_GEN *hostcmd;
+	HostCmd_DS_IAPP_PROXY *pIappProxy;
+
+	cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD);
+
+	buffer = (t_u8 *)malloc(BUFFER_LENGTH);
+	if (buffer == NULL) {
+		fprintf(stderr, "Cannot alloc memory\n");
+		ret = ENOMEM;
+		goto done;
+	}
+	memset(buffer, 0, BUFFER_LENGTH);
+
+	cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd));
+	if (!cmd) {
+		printf("ERR:Cannot allocate buffer for command!\n");
+		ret = ENOMEM;
+		goto done;
+	}
+
+	/* Fill up buffer */
+#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT
+	memset(cmd, 0, sizeof(struct eth_priv_cmd))