From 13f56edc1debd9380bcd9668874d30771f403993 Mon Sep 17 00:00:00 2001
From: Ondrej Jirman <megi@xff.cz>
Date: Sat, 29 Jul 2023 17:47:26 +0200
Subject: [PATCH 001/480] bes2600: Add the wifi driver for Pinetab2 WiFi chip
 (0.3.10_2022.1125)

Import from https://gitlab.com/TuxThePenguin0/bes2600

Version 0.3.10_2022.1125 from PineTab2 Android 11 RK BSP release.

Signed-off-by: Ondrej Jirman <megi@xff.cz>
---
 drivers/staging/bes2600/Kconfig               |  119 +
 drivers/staging/bes2600/Makefile              |  321 ++
 drivers/staging/bes2600/ap.c                  | 1984 ++++++++
 drivers/staging/bes2600/ap.h                  |   58 +
 drivers/staging/bes2600/bes2600.h             |  850 ++++
 drivers/staging/bes2600/bes2600_btusb.c       |  633 +++
 drivers/staging/bes2600/bes2600_cfgvendor.c   | 1063 ++++
 drivers/staging/bes2600/bes2600_cfgvendor.h   |  218 +
 drivers/staging/bes2600/bes2600_driver_mode.h |   65 +
 drivers/staging/bes2600/bes2600_factory.c     |  930 ++++
 drivers/staging/bes2600/bes2600_factory.h     |  208 +
 drivers/staging/bes2600/bes2600_log.c         |  152 +
 drivers/staging/bes2600/bes2600_log.h         |  161 +
 drivers/staging/bes2600/bes2600_plat.h        |   51 +
 drivers/staging/bes2600/bes2600_sdio.c        | 2563 ++++++++++
 drivers/staging/bes2600/bes2600_spi.c         | 1803 +++++++
 drivers/staging/bes2600/bes2600_usb.c         | 1554 ++++++
 drivers/staging/bes2600/bes_chardev.c         | 1280 +++++
 drivers/staging/bes2600/bes_chardev.h         |   70 +
 drivers/staging/bes2600/bes_fw.c              | 1511 ++++++
 drivers/staging/bes2600/bes_fw_common.c       |  125 +
 drivers/staging/bes2600/bes_fw_common.h       |  103 +
 drivers/staging/bes2600/bes_fw_usb.c          |  412 ++
 .../bes2600/bes_nl80211_testmode_msg.h        |  258 +
 drivers/staging/bes2600/bes_pwr.c             | 1406 +++++
 drivers/staging/bes2600/bes_pwr.h             |  167 +
 drivers/staging/bes2600/bh.c                  | 1654 ++++++
 drivers/staging/bes2600/bh.h                  |   53 +
 drivers/staging/bes2600/debug.c               |  784 +++
 drivers/staging/bes2600/debug.h               |  191 +
 drivers/staging/bes2600/epta_coex.c           |  584 +++
 drivers/staging/bes2600/epta_coex.h           |   86 +
 drivers/staging/bes2600/epta_request.c        |  650 +++
 drivers/staging/bes2600/epta_request.h        |   52 +
 drivers/staging/bes2600/fwio.c                |   55 +
 drivers/staging/bes2600/fwio.h                |   48 +
 drivers/staging/bes2600/ht.h                  |   43 +
 drivers/staging/bes2600/hwio.c                |  368 ++
 drivers/staging/bes2600/hwio.h                |  364 ++
 drivers/staging/bes2600/itp.c                 |  744 +++
 drivers/staging/bes2600/itp.h                 |  152 +
 drivers/staging/bes2600/main.c                | 1101 ++++
 drivers/staging/bes2600/pm.c                  |  474 ++
 drivers/staging/bes2600/pm.h                  |   35 +
 drivers/staging/bes2600/queue.c               |  931 ++++
 drivers/staging/bes2600/queue.h               |  173 +
 drivers/staging/bes2600/sbus.h                |  105 +
 drivers/staging/bes2600/scan.c                | 1180 +++++
 drivers/staging/bes2600/scan.h                |   72 +
 drivers/staging/bes2600/sta.c                 | 4514 +++++++++++++++++
 drivers/staging/bes2600/sta.h                 |  146 +
 drivers/staging/bes2600/tx_loop.c             |  530 ++
 drivers/staging/bes2600/tx_loop.h             |   33 +
 drivers/staging/bes2600/txrx.c                | 1995 ++++++++
 drivers/staging/bes2600/txrx.h                |   99 +
 drivers/staging/bes2600/txrx_opt.c            |  467 ++
 drivers/staging/bes2600/txrx_opt.h            |   31 +
 drivers/staging/bes2600/wifi_testmode_cmd.c   |  143 +
 drivers/staging/bes2600/wsm.c                 | 3132 ++++++++++++
 drivers/staging/bes2600/wsm.h                 | 2211 ++++++++
 60 files changed, 41265 insertions(+)
 create mode 100644 drivers/staging/bes2600/Kconfig
 create mode 100644 drivers/staging/bes2600/Makefile
 create mode 100644 drivers/staging/bes2600/ap.c
 create mode 100644 drivers/staging/bes2600/ap.h
 create mode 100644 drivers/staging/bes2600/bes2600.h
 create mode 100644 drivers/staging/bes2600/bes2600_btusb.c
 create mode 100644 drivers/staging/bes2600/bes2600_cfgvendor.c
 create mode 100644 drivers/staging/bes2600/bes2600_cfgvendor.h
 create mode 100644 drivers/staging/bes2600/bes2600_driver_mode.h
 create mode 100644 drivers/staging/bes2600/bes2600_factory.c
 create mode 100644 drivers/staging/bes2600/bes2600_factory.h
 create mode 100644 drivers/staging/bes2600/bes2600_log.c
 create mode 100644 drivers/staging/bes2600/bes2600_log.h
 create mode 100644 drivers/staging/bes2600/bes2600_plat.h
 create mode 100644 drivers/staging/bes2600/bes2600_sdio.c
 create mode 100644 drivers/staging/bes2600/bes2600_spi.c
 create mode 100644 drivers/staging/bes2600/bes2600_usb.c
 create mode 100644 drivers/staging/bes2600/bes_chardev.c
 create mode 100644 drivers/staging/bes2600/bes_chardev.h
 create mode 100644 drivers/staging/bes2600/bes_fw.c
 create mode 100644 drivers/staging/bes2600/bes_fw_common.c
 create mode 100644 drivers/staging/bes2600/bes_fw_common.h
 create mode 100644 drivers/staging/bes2600/bes_fw_usb.c
 create mode 100644 drivers/staging/bes2600/bes_nl80211_testmode_msg.h
 create mode 100644 drivers/staging/bes2600/bes_pwr.c
 create mode 100644 drivers/staging/bes2600/bes_pwr.h
 create mode 100644 drivers/staging/bes2600/bh.c
 create mode 100644 drivers/staging/bes2600/bh.h
 create mode 100644 drivers/staging/bes2600/debug.c
 create mode 100644 drivers/staging/bes2600/debug.h
 create mode 100644 drivers/staging/bes2600/epta_coex.c
 create mode 100644 drivers/staging/bes2600/epta_coex.h
 create mode 100644 drivers/staging/bes2600/epta_request.c
 create mode 100644 drivers/staging/bes2600/epta_request.h
 create mode 100644 drivers/staging/bes2600/fwio.c
 create mode 100644 drivers/staging/bes2600/fwio.h
 create mode 100644 drivers/staging/bes2600/ht.h
 create mode 100644 drivers/staging/bes2600/hwio.c
 create mode 100644 drivers/staging/bes2600/hwio.h
 create mode 100644 drivers/staging/bes2600/itp.c
 create mode 100644 drivers/staging/bes2600/itp.h
 create mode 100644 drivers/staging/bes2600/main.c
 create mode 100644 drivers/staging/bes2600/pm.c
 create mode 100644 drivers/staging/bes2600/pm.h
 create mode 100644 drivers/staging/bes2600/queue.c
 create mode 100644 drivers/staging/bes2600/queue.h
 create mode 100644 drivers/staging/bes2600/sbus.h
 create mode 100644 drivers/staging/bes2600/scan.c
 create mode 100644 drivers/staging/bes2600/scan.h
 create mode 100644 drivers/staging/bes2600/sta.c
 create mode 100644 drivers/staging/bes2600/sta.h
 create mode 100644 drivers/staging/bes2600/tx_loop.c
 create mode 100644 drivers/staging/bes2600/tx_loop.h
 create mode 100644 drivers/staging/bes2600/txrx.c
 create mode 100644 drivers/staging/bes2600/txrx.h
 create mode 100644 drivers/staging/bes2600/txrx_opt.c
 create mode 100644 drivers/staging/bes2600/txrx_opt.h
 create mode 100644 drivers/staging/bes2600/wifi_testmode_cmd.c
 create mode 100644 drivers/staging/bes2600/wsm.c
 create mode 100644 drivers/staging/bes2600/wsm.h

diff --git a/drivers/staging/bes2600/Kconfig b/drivers/staging/bes2600/Kconfig
new file mode 100644
index 000000000000..374c15db823f
--- /dev/null
+++ b/drivers/staging/bes2600/Kconfig
@@ -0,0 +1,119 @@
+config BES2600
+	tristate "BES2600 WLAN support"
+	select MAC80211
+	select CFG80211
+	select NL80211_TESTMODE
+	default m
+	---help---
+		This is an experimental driver for the bes2600 chip-set.
+		Enabling this option enables the generic driver without
+		any platform support.
+
+if BES2600
+
+config BES2600_WLAN_SDIO
+	bool "SDIO bus interface support"
+	depends on MMC
+	default y
+	---help---
+		This option enables the SDIO bus interface support for Bes2600
+		WLAN driver. Say Y if you want to use SDIO.
+
+config BES2600_WLAN_USB
+	bool "USB bus interface support"
+	depends on USB
+	default n
+	---help---
+		This option enables the USB bus interface support for Bes2600
+		WLAN driver. Say Y if you want to use USB.
+
+config BES2600_WLAN_SPI
+	bool "SPI bus interface support"
+	depends on SPI
+	default n
+	---help---
+		This option enables the SPI bus interface support for Bes2600
+		WLAN driver. Say Y if you want to use USB.
+
+config BES2600_USE_GPIO_IRQ
+	bool "Use GPIO interrupt"
+	default n
+	---help---
+		Say Y here if you want to include GPIO IRQ support instead of SDIO IRQ.
+		If unsure, say N.
+
+config BES2600_5GHZ_SUPPORT
+	bool "5GHz band support"
+	default y
+	---help---
+		Say Y if your device supports 5GHz band.
+		If unsure, say N.
+
+config BES2600_WAPI_SUPPORT
+	bool "WAPI support"
+	default y
+	---help---
+		Say Y if your compat-wireless support WAPI.
+		If unsure, say N.
+
+config BES2600_USE_STE_EXTENSIONS
+	bool "STE extensions"
+	default n
+	---help---
+		Say Y if you want to include STE extensions.
+		If unsure, say N.
+
+config BES2600_DISABLE_BEACON_HINTS
+	bool "Disable 11d beacon hints"
+	default n
+	---help---
+		Say Y if you want to disable 11d beacon hints.
+		If unsure, say N.
+
+config BES2600_TESTMODE
+	bool "bes2600 testmode support"
+	select NL80211_TESTMODE
+	default n
+	---help---
+		Say Y if you want to enable bes2600 testmode.
+		If unsure, say N.
+
+menu "Driver debug features"
+
+config BES2600_DEBUGFS
+	bool "Expose driver internals to DebugFS (DEVELOPMENT)"
+	default y
+
+config BES2600_BH_DEBUG
+	bool "Enable low-level device communication logs (DEVELOPMENT)"
+
+config BES2600_WSM_DEBUG
+	bool "Enable WSM API debug messages (DEVELOPMENT)"
+
+config BES2600_WSM_DUMPS
+	bool "Verbose WSM API logging (DEVELOPMENT)"
+
+config BES2600_WSM_DUMPS_SHORT
+	bool "Dump only first x bytes (default 20) (DEVELOPMENT)"
+
+config BES2600_TXRX_DEBUG
+	bool "Enable TX/RX debug messages (DEVELOPMENT)"
+
+config BES2600_TX_POLICY_DEBUG
+	bool "Enable TX policy debug (DEVELOPMENT)"
+
+config BES2600_STA_DEBUG
+	bool "Enable STA/AP debug (DEVELOPMENT)"
+
+config BES2600_DUMP_ON_ERROR
+	bool "Dump kernel in case of critical error (DEVELOPMENT)"
+
+config BES2600_ITP
+	bool "Enable ITP DebugFS"
+	---help---
+		Say Y if you want to include ITP code.
+		If unsure, say N.
+
+endmenu
+
+endif # BES2600
diff --git a/drivers/staging/bes2600/Makefile b/drivers/staging/bes2600/Makefile
new file mode 100644
index 000000000000..103519907e4f
--- /dev/null
+++ b/drivers/staging/bes2600/Makefile
@@ -0,0 +1,321 @@
+# feature option
+BES2600                                 ?= m
+
+CONFIG_BES2600_WLAN_SDIO                ?= y
+CONFIG_BES2600_WLAN_SPI                 ?= n
+CONFIG_BES2600_WLAN_USB                 ?= n
+
+CONFIG_BES2600_VENDOR_CMD               ?= y
+ifneq ($(CONFIG_BES2600_WLAN_USB),y)
+CONFIG_BES2600_TESTMODE                 ?= y
+endif
+
+CONFIG_BES2600_WAPI_SUPPORT             ?= y
+CONFIG_BES2600_5GHZ_SUPPORT             ?= y
+CONFIG_BES2600_STA_DEBUG                ?= y
+CONFIG_BES2600_STATIC_SDD               ?= y
+P2P_MULTIVIF                            ?= y
+AP_AGGREGATE_FW_FIX                     ?= y
+CONFIG_BES2600_BT                       ?= n
+WIFI_BT_COEXIST_EPTA_ENABLE             ?= y
+WIFI_BT_COEXIST_EPTA_FDD                ?= n
+ifneq ($(CONFIG_BES2600_WLAN_USB),y)
+CONFIG_BES2600_WOWLAN                   ?= y
+endif
+CONFIG_BES2600_LISTEN_INTERVAL          ?= 3
+BSS_LOSS_CHECK                          ?= y
+
+CONFIG_BES2600_DEBUGFS                  ?= y
+CONFIG_BES2600_ITP                      ?= n
+
+CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES ?= n
+CONFIG_BES2600_CALIB_FROM_LINUX         ?= y
+
+CONFIG_BES2600_WIFI_BOOT_ON             ?= n
+CONFIG_BES2600_BT_BOOT_ON               ?= n
+
+PLAT_ALLWINNER_T507                     ?= n
+PLAT_ALLWINNER_R329                     ?= n
+PLAT_ROCKCHIP                           ?= y
+PLAT_UBUNTU_VUB300                      ?= n
+PLAT_QCOM_QM215                         ?= n
+
+BES2600_GPIO_WAKEUP_AP                  ?= y
+BES2600_WRITE_DPD_TO_FILE               ?= n
+
+# bes evb
+BES2600_INDEPENDENT_EVB                 ?= n
+# wifi module for allwinner made by bes
+BES2600_INTEGRATED_MODULE_V1            ?= n
+# xiaomi R329 wifi module
+BES2600_INTEGRATED_MODULE_V2            ?= n
+# sicun QM215 wifi module
+BES2600_INTEGRATED_MODULE_V3            ?= n
+
+# 0: use dynamic_ps_timeout value
+# other: override dynamic_ps_timeout value
+BES2600_FASTPS_IDLE_TIME		?= 0
+
+#1.rock api(fixed macaddr)
+#2.read from file(macaddr of customers)
+#3.read from file(macaddr template)
+#4.random macaddr
+GET_MAC_ADDR_METHOD                     ?= 4
+
+ifeq ($(CONFIG_BES2600_DEBUGFS),y)
+BES2600_DUMP_FW_DPD_LOG                 ?= n
+endif
+
+ifeq ($(CONFIG_BES2600_TESTMODE),y)
+CONFIG_BES2600_KEEP_ALIVE               ?= n
+ifeq ($(CONFIG_BES2600_KEEP_ALIVE)_$(PLAT_ALLWINNER_R329),y_y)
+ccflags-y += -DVENDOR_XM_KEEPALIVE
+endif
+endif
+
+BES2600_DRV_VERSION := bes2600_0.3.10_2022.1125
+
+ifeq ($(GET_MAC_ADDR_METHOD)_$(PLAT_ROCKCHIP),1_n)
+ $(error Parameter setting error, GET_MAC_ADDR_METHOD can only be set to 1 on rockchip platform)
+endif
+
+ifeq ($(BES2600_GPIO_WAKEUP_AP)_$(CONFIG_BES2600_WLAN_SDIO),y_n)
+ $(error Parameter setting error, BES2600_GPIO_WAKEUP_AP can only be set to y when CONFIG_BES2600_WLAN_SDIO set to y)
+endif
+
+ifeq ($(GET_MAC_ADDR_METHOD)_$(PLAT_ALLWINNER_R329),2_y)
+PATH_WIFI_MACADDR ?= /data/wifi/wifimac.txt
+endif
+
+ifeq ($(CONFIG_BES2600_CALIB_FROM_LINUX),y)
+FACTORY_CRC_CHECK                    ?= n
+FACTORY_SAVE_MULTI_PATH              ?= n
+ifeq ($(FACTORY_SAVE_MULTI_PATH),y)
+FACTORY_PATH                         ?= /data/cfg/bes2600_factory.txt
+FACTORY_DEFAULT_PATH                 ?= /lib/firmware/bes2600_factory.txt
+else ifneq ($(filter y,$(PLAT_ALLWINNER_R329) $(PLAT_QCOM_QM215)),)
+FACTORY_PATH ?= /data/wifi/bes2600_factory.txt
+else ifeq ($(PLAT_ALLWINNER_T507),y)
+FACTORY_PATH ?= /vendor/firmware/bes2600_factory.txt
+endif
+endif
+
+FACTORY_PATH ?= /vendor/lib/firmware/bes2600_factory.txt
+
+# basic function
+define boolen_flag
+$(strip $(if $(findstring $($(1)),$(2)),-D$(1)))
+endef
+
+define string_flag
+$(strip $(if $($(1)),-D$(1)=\"$($(1))\"))
+endef
+
+define value_flag
+$(strip $(if $($(1)),-D$(1)=$($(1))))
+endef
+
+ccflags-y += -Werror
+
+ifneq ($(filter y,$(PLAT_ALLWINNER_R329) $(PLAT_ALLWINNER_T507)),)
+ccflags-y += -DPLAT_ALLWINNER
+endif
+
+ifeq ($(CONFIG_BES2600_WLAN_SDIO),y)
+FW_DOWNLOAD_BY_SDIO                     ?= y
+SDIO_HOST_ADMA_SUPPORT                  ?= y
+ccflags-y += -DCONFIG_BES2600_WLAN_BES
+ccflags-y += -DBES_SDIO_RXTX_TOGGLE
+ccflags-y += -DBES_SDIO_TX_MULTIPLE_ENABLE
+ccflags-y += -DBES_SDIO_RX_MULTIPLE_ENABLE
+ccflags-y += -DBES_UNIFIED_PM
+ccflags-y += -DBES_SDIO_OPTIMIZED_LEN
+ccflags-y += -DBES2600_HOST_TIMESTAMP_DEBUG
+#specific for xiaomi R329
+#ccflags-y += -DBES2600_BOOT_UART_TO_SDIO
+endif # CONFIG_BES2600_WLAN_SDIO
+
+ifeq ($(CONFIG_BES2600_WLAN_SPI),y)
+FW_DOWNLOAD_BY_UART                     ?= y
+ccflags-y += -DCONFIG_BES2600_WLAN_BES
+#ccflags-y += -DCONFIG_BES2600_SPI_THROUGHPUT_TEST
+#ccflags-y += -DFW_DOWNLOAD_MANULLY
+ccflags-y += -DBES_UNIFIED_PM
+endif # CONFIG_BES2600_WLAN_SPI
+
+ifeq ($(CONFIG_BES2600_WLAN_USB),y)
+FW_DOWNLOAD_BY_USB                     ?= n
+ccflags-y += -DCONFIG_BES2600_WLAN_BES
+endif # CONFIG_BES2600_WLAN_USB
+
+ifeq ($(FW_DOWNLOAD_BY_UART)_$(PLAT_ALLWINNER_T507),y_y)
+BES2600_LOAD_FW_TOOL_PATH   ?= /system/bin/bes_fw_download
+BES2600_LOAD_FW_TOOL_DEVICE ?= /dev/ttyS2
+endif
+
+ifeq ($(FW_DOWNLOAD_BY_UART)_$(PLAT_QCOM_QM215),y_y)
+FW_DOWNLOAD_UART_DAEMON         ?= y
+BES2600_LOAD_FW_TOOL_PATH   ?= /system/bin/bes_fw_download
+BES2600_LOAD_FW_TOOL_DEVICE ?= /dev/ttyHS0
+endif
+
+ifeq ($(BES2600_WRITE_DPD_TO_FILE),y)
+BES2600_DPD_PATH ?= /data/cfg/bes2600_dpd.bin
+BES2600_DEFAULT_DPD_PATH ?= /lib/firmware/bes2600_dpd.bin
+BES2600_DPD_GOLDEN_PATH ?= /data/cfg/bes2600_dpd_golden.bin
+endif
+
+ifeq ($(BES2600_DUMP_FW_DPD_LOG),y)
+BES2600_DPD_LOG_PATH ?= /data/applog/bes2600_dpd_log.log
+endif
+
+# compilation macros setting
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_WLAN_SDIO,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_WLAN_SPI,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_WLAN_USB,y)
+
+ccflags-y += $(call boolen_flag,PLAT_ALLWINNER_R329,y)
+ccflags-y += $(call boolen_flag,PLAT_ALLWINNER_T507,y)
+ccflags-y += $(call boolen_flag,PLAT_UBUNTU_VUB300,y)
+ccflags-y += $(call boolen_flag,PLAT_ROCKCHIP,y)
+ccflags-y += $(call boolen_flag,PLAT_QCOM_QM215,y)
+
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_VENDOR_CMD,y)
+
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_5GHZ_SUPPORT,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_STATIC_SDD,y)
+ccflags-y += $(call boolen_flag,P2P_MULTIVIF,y)
+ccflags-y += $(call boolen_flag,AP_AGGREGATE_FW_FIX,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_BT,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_WAPI_SUPPORT,y)
+ccflags-y += $(call boolen_flag,WIFI_BT_COEXIST_EPTA_ENABLE,y)
+ccflags-y += $(call boolen_flag,WIFI_BT_COEXIST_EPTA_FDD,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_DISABLE_BEACON_HINTS,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES,y)
+
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_ITP,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_DEBUGFS,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_BH_DEBUG,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_WSM_DEBUG,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_WSM_DUMPS,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_WSM_DUMPS_SHORT,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_TXRX_DEBUG,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_TX_POLICY_DEBUG,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_STA_DEBUG,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_DUMP_ON_ERROR,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_WOWLAN,y)
+ccflags-y += $(call value_flag,CONFIG_BES2600_LISTEN_INTERVAL)
+ccflags-y += $(call value_flag,BES2600_FASTPS_IDLE_TIME)
+ccflags-y += $(call boolen_flag,BSS_LOSS_CHECK,y)
+
+ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_PATH)
+ccflags-y += $(call string_flag,BES2600_LOAD_FW_TOOL_DEVICE)
+ccflags-y += $(call string_flag,BES2600_DRV_VERSION)
+ccflags-y += $(call string_flag,BES2600_DPD_PATH)
+ccflags-y += $(call string_flag,BES2600_DEFAULT_DPD_PATH)
+ccflags-y += $(call string_flag,BES2600_DPD_GOLDEN_PATH)
+ccflags-y += $(call boolen_flag,FW_DOWNLOAD_UART_DAEMON,y)
+
+ccflags-y += $(call boolen_flag,BES2600_INDEPENDENT_EVB,y)
+ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V1,y)
+ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V2,y)
+ccflags-y += $(call boolen_flag,BES2600_INTEGRATED_MODULE_V3,y)
+ccflags-y += $(call boolen_flag,FW_DOWNLOAD_BY_SDIO,y)
+ccflags-y += $(call boolen_flag,SDIO_HOST_ADMA_SUPPORT,y)
+ccflags-y += $(call boolen_flag,FW_DOWNLOAD_BY_UART,y)
+ccflags-y += $(call boolen_flag,FW_DOWNLOAD_BY_USB,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_TESTMODE,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_KEEP_ALIVE,y)
+
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_WIFI_BOOT_ON,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_BT_BOOT_ON,y)
+ccflags-y += $(call boolen_flag,CONFIG_BES2600_CALIB_FROM_LINUX,y)
+
+ccflags-y += $(call value_flag,GET_MAC_ADDR_METHOD)
+ccflags-y += $(call string_flag,PATH_WIFI_MACADDR)
+ccflags-y += $(call string_flag,FACTORY_PATH)
+ccflags-y += $(call string_flag,FACTORY_DEFAULT_PATH)
+ccflags-y += $(call boolen_flag,FACTORY_SAVE_MULTI_PATH,y)
+ccflags-y += $(call boolen_flag,FACTORY_CRC_CHECK,y)
+
+ccflags-y += $(call boolen_flag,BES2600_GPIO_WAKEUP_AP,y)
+ccflags-y += $(call boolen_flag,BES2600_WRITE_DPD_TO_FILE,y)
+
+ccflags-y += $(call boolen_flag,BES2600_DUMP_FW_DPD_LOG,y)
+ccflags-y += $(call string_flag,BES2600_DPD_LOG_PATH)
+
+
+# internal feature options
+ccflags-y += -DAP_HT_CAP_UPDATE
+ccflags-y += -DBES2600_RX_IN_BH
+ccflags-y += -DDCW1260_DETECTION_LOGIC
+
+# sdd options
+ccflags-y += -DTEST_11B=0
+ccflags-y += -DDPD_CALI=0
+ccflags-y += -DDPD_CALI=0
+ccflags-y += -DALI_CONFG=0
+ccflags-y += -DCHIP_WIFI_ROM_VER=1
+ccflags-y += -DWIFI_OUT_FEM=0
+ccflags-y += -DRF_TX_CONTROL_IO=16
+
+#ccflags-y += -DP2P_STA_COEX
+#ccflags-y += -DCUSTOM_FEATURE
+#ccflags-y += -DMCAST_FWDING
+#ccflags-y += -DAP_AGGREGATE_FW_FIX
+# Extra IE for probe response from upper layer is needed in P2P GO
+# For offloading probe response to FW, the extra IE must be included
+# in the probe response template
+#ccflags-y += -DPROBE_RESP_EXTRA_IE
+#ccflags-y += -DIPV6_FILTERING
+
+# basic files for building module
+bes2600-y := \
+	fwio.o \
+	txrx.o \
+	main.o \
+	queue.o \
+	hwio.o \
+	bh.o \
+	wsm.o \
+	sta.o \
+	ap.o \
+	scan.o \
+	txrx_opt.o \
+	bes_chardev.o \
+	tx_loop.o \
+	bes2600_log.o
+
+# compilation files select
+bes2600-$(CONFIG_BES2600_DEBUGFS)       += debug.o
+bes2600-$(CONFIG_BES2600_ITP)           += itp.o
+bes2600-$(CONFIG_PM)                    += pm.o
+bes2600-$(CONFIG_BES2600_BT)            += bes2600_btusb.o
+bes2600-$(CONFIG_BES2600_TESTMODE)      += wifi_testmode_cmd.o
+bes2600-$(CONFIG_BES2600_WLAN_SDIO)     += bes2600_sdio.o
+bes2600-$(CONFIG_BES2600_WLAN_SPI)      += bes2600_spi.o
+bes2600-$(CONFIG_BES2600_WLAN_USB)      += bes2600_usb.o
+bes2600-$(CONFIG_BES2600_VENDOR_CMD)    += bes2600_cfgvendor.o
+
+bes2600-$(FW_DOWNLOAD_BY_SDIO)          += bes_fw.o bes_fw_common.o
+bes2600-$(FW_DOWNLOAD_BY_UART)          += bes_fw.o bes_fw_common.o
+bes2600-$(FW_DOWNLOAD_BY_USB)           += bes_fw_usb.o bes_fw_common.o
+bes2600-$(WIFI_BT_COEXIST_EPTA_ENABLE)  += epta_coex.o epta_request.o
+bes2600-$(CONFIG_BES2600_WOWLAN)        += bes_pwr.o
+
+ifneq ($(CONFIG_BES2600_CALIB_FROM_LINUX)_$(PLAT_ALLWINNER_R329),n_n)
+bes2600-y += bes2600_factory.o
+endif
+
+obj-$(BES2600) += bes2600.o
+
+ifeq ($(KERNELRELEASE),)
+KVERSION = $(shell uname -r)
+all:
+	make -C /lib/modules/$(KVERSION)/build M=$(shell pwd) modules
+clean:
+	make -C /lib/modules/$(KVERSION)/build M=$(shell pwd) clean
+endif
+
+clean:
+	rm -rf *.o .*.o.d *.ko *.o.ur-safe .*.cmd *.mod *.mod.c .cache.mk modules.order Module.symvers .tmp_versions
diff --git a/drivers/staging/bes2600/ap.c b/drivers/staging/bes2600/ap.c
new file mode 100644
index 000000000000..2126e7b516da
--- /dev/null
+++ b/drivers/staging/bes2600/ap.c
@@ -0,0 +1,1984 @@
+/*
+ * mac80211 STA and AP API for mac80211 BES2600 drivers
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "bes2600.h"
+#include "sta.h"
+#include "ap.h"
+#include "bh.h"
+#include "net/mac80211.h"
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+#include <linux/umh.h>
+#endif
+#include "epta_request.h"
+#include "epta_coex.h"
+#include "txrx_opt.h"
+
+#ifdef AP_HT_CAP_UPDATE
+#define HT_INFO_OFFSET 4
+#define HT_INFO_MASK 0x0011
+#define HT_INFO_IE_LEN 22
+#endif
+#define BES2600_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
+
+#define BES2600_ENABLE_ARP_FILTER_OFFLOAD	3
+
+#define BES2600_KEEP_ALIVE_PERIOD	(20)
+
+#ifndef ERP_INFO_BYTE_OFFSET
+#define ERP_INFO_BYTE_OFFSET 2
+#endif
+
+#ifdef IPV6_FILTERING
+#define BES2600_ENABLE_NDP_FILTER_OFFLOAD	3
+#endif /*IPV6_FILTERING*/
+
+static int bes2600_upload_beacon(struct bes2600_vif *priv);
+#ifdef PROBE_RESP_EXTRA_IE
+static int bes2600_upload_proberesp(struct bes2600_vif *priv);
+#endif
+static int bes2600_upload_pspoll(struct bes2600_vif *priv);
+static int bes2600_upload_null(struct bes2600_vif *priv);
+static int bes2600_upload_qosnull(struct bes2600_vif *priv);
+static int bes2600_start_ap(struct bes2600_vif *priv);
+static int bes2600_update_beaconing(struct bes2600_vif *priv);
+/*
+static int bes2600_enable_beaconing(struct bes2600_vif *priv,
+				   bool enable);
+*/
+static void __bes2600_sta_notify(struct bes2600_vif *priv,
+				enum sta_notify_cmd notify_cmd,
+				int link_id);
+
+/* ******************************************************************** */
+/* AP API								*/
+
+int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		   struct ieee80211_sta *sta)
+{
+	struct bes2600_sta_priv *sta_priv =
+			(struct bes2600_sta_priv *)&sta->drv_priv;
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct bes2600_link_entry *entry;
+	struct sk_buff *skb;
+	struct bes2600_common *hw_priv = hw->priv;
+
+#ifdef P2P_MULTIVIF
+	WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
+#endif
+
+	bes2600_info(BES2600_DBG_AP, "%s mode:%u, MFP:%u\n",
+		__FUNCTION__, priv->mode, sta->mfp);
+
+	if (priv->mode == NL80211_IFTYPE_STATION)
+		priv->pmf = sta->mfp;
+
+	if (priv->mode != NL80211_IFTYPE_AP)
+		return 0;
+
+	sta_priv->priv = priv;
+	sta_priv->link_id = bes2600_find_link_id(priv, sta->addr);
+	if (WARN_ON(!sta_priv->link_id)) {
+		/* Impossible error */
+		wiphy_info(priv->hw->wiphy,
+			"[AP] No more link IDs available.\n");
+		return -ENOENT;
+	}
+
+	entry = &priv->link_id_db[sta_priv->link_id - 1];
+	spin_lock_bh(&priv->ps_state_lock);
+	if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) ==
+					IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+		priv->sta_asleep_mask |= BIT(sta_priv->link_id);
+	entry->status = BES2600_LINK_HARD;
+	while ((skb = skb_dequeue(&entry->rx_queue)))
+		ieee80211_rx_irqsafe(priv->hw, skb);
+	spin_unlock_bh(&priv->ps_state_lock);
+#ifdef AP_AGGREGATE_FW_FIX
+	hw_priv->connected_sta_cnt++;
+	if(hw_priv->connected_sta_cnt>1) {
+			wsm_lock_tx(hw_priv);
+			WARN_ON(wsm_set_block_ack_policy(hw_priv,
+					BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
+					BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
+					priv->if_id));
+			wsm_unlock_tx(hw_priv);
+	}
+#endif
+
+	return 0;
+}
+
+int bes2600_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		      struct ieee80211_sta *sta)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	struct bes2600_sta_priv *sta_priv =
+			(struct bes2600_sta_priv *)&sta->drv_priv;
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct bes2600_link_entry *entry;
+
+#ifdef P2P_MULTIVIF
+	WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
+#endif
+
+	bes2600_info(BES2600_DBG_AP, "%s mode:%u\n", __FUNCTION__, priv->mode);
+
+	if (priv->mode == NL80211_IFTYPE_STATION)
+		priv->pmf = false;
+
+	if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
+		return 0;
+
+	entry = &priv->link_id_db[sta_priv->link_id - 1];
+	spin_lock_bh(&priv->ps_state_lock);
+	entry->status = BES2600_LINK_RESERVE;
+	entry->timestamp = jiffies;
+	wsm_lock_tx_async(hw_priv);
+	if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0)
+		wsm_unlock_tx(hw_priv);
+	spin_unlock_bh(&priv->ps_state_lock);
+	flush_workqueue(hw_priv->workqueue);
+#ifdef AP_AGGREGATE_FW_FIX
+	hw_priv->connected_sta_cnt--;
+	if(hw_priv->connected_sta_cnt <= 1) {
+		if ((priv->if_id != 1) ||
+			((priv->if_id == 1) && hw_priv->is_go_thru_go_neg)) {
+			wsm_lock_tx(hw_priv);
+			WARN_ON(wsm_set_block_ack_policy(hw_priv,
+						BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
+						BES2600_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
+						priv->if_id));
+			wsm_unlock_tx(hw_priv);
+		}
+	}
+#endif
+
+	return 0;
+}
+
+static void __bes2600_sta_notify(struct bes2600_vif *priv,
+				enum sta_notify_cmd notify_cmd,
+				int link_id)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	u32 bit, prev;
+
+	/* Zero link id means "for all link IDs" */
+	if (link_id)
+		bit = BIT(link_id);
+	else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
+		bit = 0;
+	else
+		bit = priv->link_id_map;
+	prev = priv->sta_asleep_mask & bit;
+
+	switch (notify_cmd) {
+	case STA_NOTIFY_SLEEP:
+		if (!prev) {
+			if (priv->buffered_multicasts &&
+					!priv->sta_asleep_mask)
+				queue_work(hw_priv->workqueue,
+					&priv->multicast_start_work);
+			priv->sta_asleep_mask |= bit;
+		}
+		break;
+	case STA_NOTIFY_AWAKE:
+		if (prev) {
+			priv->sta_asleep_mask &= ~bit;
+			priv->pspoll_mask &= ~bit;
+			if (priv->tx_multicast && link_id &&
+					!priv->sta_asleep_mask)
+				queue_work(hw_priv->workqueue,
+					&priv->multicast_stop_work);
+			bes2600_bh_wakeup(hw_priv);
+		}
+		break;
+	}
+}
+
+void bes2600_sta_notify(struct ieee80211_hw *dev,
+		       struct ieee80211_vif *vif,
+		       enum sta_notify_cmd notify_cmd,
+		       struct ieee80211_sta *sta)
+{
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct bes2600_sta_priv *sta_priv =
+		(struct bes2600_sta_priv *)&sta->drv_priv;
+
+#ifdef P2P_MULTIVIF
+	WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
+#endif
+	spin_lock_bh(&priv->ps_state_lock);
+	__bes2600_sta_notify(priv, notify_cmd, sta_priv->link_id);
+	spin_unlock_bh(&priv->ps_state_lock);
+}
+
+static void bes2600_ps_notify(struct bes2600_vif *priv,
+		      int link_id, bool ps)
+{
+	if (link_id > CW1250_MAX_STA_IN_AP_MODE)
+		return;
+
+	bes2600_dbg(BES2600_DBG_TXRX, "%s for LinkId: %d. STAs asleep: %.8X\n",
+			ps ? "Stop" : "Start",
+			link_id, priv->sta_asleep_mask);
+
+	/* TODO:COMBO: __bes2600_sta_notify changed. */
+	__bes2600_sta_notify(priv,
+		ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
+}
+
+static int bes2600_set_tim_impl(struct bes2600_vif *priv, bool aid0_bit_set)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct sk_buff *skb;
+	struct wsm_update_ie update_ie = {
+		.what = WSM_UPDATE_IE_BEACON,
+		.count = 1,
+	};
+	u16 tim_offset, tim_length;
+
+	bes2600_dbg(BES2600_DBG_AP, "[AP] %s mcast: %s.\n",
+		__func__, aid0_bit_set ? "ena" : "dis");
+
+	skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+			&tim_offset, &tim_length);
+	if (!skb) {
+		if (!__bes2600_flush(hw_priv, true, priv->if_id))
+			wsm_unlock_tx(hw_priv);
+		return -ENOENT;
+	}
+
+	if (tim_offset && tim_length >= 6) {
+		/* Ignore DTIM count from mac80211:
+		 * firmware handles DTIM internally. */
+		skb->data[tim_offset + 2] = 0;
+
+		/* Set/reset aid0 bit */
+		if (aid0_bit_set)
+			skb->data[tim_offset + 4] |= 1;
+		else
+			skb->data[tim_offset + 4] &= ~1;
+	}
+
+	update_ie.ies = &skb->data[tim_offset];
+	update_ie.length = tim_length;
+	WARN_ON(wsm_update_ie(hw_priv, &update_ie, priv->if_id));
+
+	dev_kfree_skb(skb);
+
+	return 0;
+}
+
+void bes2600_set_tim_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, set_tim_work);
+	(void)bes2600_set_tim_impl(priv, priv->aid0_bit_set);
+}
+
+int bes2600_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+		   bool set)
+{
+	struct bes2600_sta_priv *sta_priv =
+		(struct bes2600_sta_priv *)&sta->drv_priv;
+	struct bes2600_vif *priv = sta_priv->priv;
+
+#ifdef P2P_MULTIVIF
+	WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
+#endif
+	WARN_ON(priv->mode != NL80211_IFTYPE_AP);
+	queue_work(priv->hw_priv->workqueue, &priv->set_tim_work);
+	return 0;
+}
+
+void bes2600_set_cts_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, set_cts_work.work);
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0};
+	struct wsm_update_ie update_ie = {
+		.what = WSM_UPDATE_IE_BEACON,
+		.count = 1,
+		.ies = erp_ie,
+		.length = 3,
+	};
+	u32 erp_info;
+	__le32 use_cts_prot;
+	down(&hw_priv->conf_lock);
+	erp_info = priv->erp_info;
+	up(&hw_priv->conf_lock);
+	use_cts_prot =
+		erp_info & WLAN_ERP_USE_PROTECTION ?
+		__cpu_to_le32(1) : 0;
+
+	erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
+
+	bes2600_dbg(BES2600_DBG_AP, "[STA] ERP information 0x%x\n", erp_info);
+
+	/* TODO:COMBO: If 2 interfaces are on the same channel they share
+	the same ERP values */
+	WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_NON_ERP_PROTECTION,
+				&use_cts_prot, sizeof(use_cts_prot),
+				priv->if_id));
+	/* If STA Mode update_ie is not required */
+	if (priv->mode != NL80211_IFTYPE_STATION) {
+		WARN_ON(wsm_update_ie(hw_priv, &update_ie, priv->if_id));
+	}
+
+	return;
+}
+
+static int bes2600_set_btcoexinfo(struct bes2600_vif *priv)
+{
+	struct wsm_override_internal_txrate arg;
+	int ret = 0;
+
+	if (priv->mode == NL80211_IFTYPE_STATION) {
+		/* Plumb PSPOLL and NULL template */
+		WARN_ON(bes2600_upload_pspoll(priv));
+		WARN_ON(bes2600_upload_null(priv));
+	} else {
+		return 0;
+	}
+
+	memset(&arg, 0, sizeof(struct wsm_override_internal_txrate));
+
+	if (!priv->vif->p2p) {
+		/* STATION mode */
+		if (priv->bss_params.operationalRateSet & ~0xF) {
+			bes2600_dbg(BES2600_DBG_AP, "[STA] STA has ERP rates\n");
+			/* G or BG mode */
+			arg.internalTxRate = (__ffs(
+			priv->bss_params.operationalRateSet & ~0xF));
+		} else {
+			bes2600_dbg(BES2600_DBG_AP, "[STA] STA has non ERP rates\n");
+			/* B only mode */
+			arg.internalTxRate = (__ffs(
+			priv->association_mode.basicRateSet));
+		}
+		arg.nonErpInternalTxRate = (__ffs(
+			priv->association_mode.basicRateSet));
+	} else {
+		/* P2P mode */
+		arg.internalTxRate = (__ffs(
+			priv->bss_params.operationalRateSet & ~0xF));
+		arg.nonErpInternalTxRate = (__ffs(
+			priv->bss_params.operationalRateSet & ~0xF));
+	}
+
+	bes2600_dbg(BES2600_DBG_AP, "[STA] BTCOEX_INFO"
+		"MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
+		priv->mode,
+		arg.internalTxRate,
+		arg.nonErpInternalTxRate);
+
+	ret = WARN_ON(wsm_write_mib(cw12xx_vifpriv_to_hwpriv(priv),
+		WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+		&arg, sizeof(arg), priv->if_id));
+
+	return ret;
+}
+
+void bes2600_bss_info_changed(struct ieee80211_hw *dev,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_bss_conf *info,
+			     u32 changed)
+{
+	struct bes2600_common *hw_priv = dev->priv;
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct ieee80211_conf *conf = &dev->conf;
+	const u8 override_fpsm_timeout = BES2600_FASTPS_IDLE_TIME;
+
+#ifdef P2P_MULTIVIF
+	if (priv->if_id == CW12XX_GENERIC_IF_ID)
+		return;
+#endif
+	bes2600_info(BES2600_DBG_AP, "BSS CHANGED:	%08x\n", changed);
+	down(&hw_priv->conf_lock);
+	if (changed & BSS_CHANGED_BSSID) {
+#ifdef CONFIG_BES2600_TESTMODE
+		spin_lock_bh(&hw_priv->tsm_lock);
+		if (hw_priv->tsm_info.sta_associated) {
+			unsigned now = jiffies;
+			hw_priv->tsm_info.sta_roamed = 1;
+			if ((now - hw_priv->tsm_info.txconf_timestamp_vo) >
+			    (now - hw_priv->tsm_info.rx_timestamp_vo))
+				hw_priv->tsm_info.use_rx_roaming = 1;
+		} else {
+			hw_priv->tsm_info.sta_associated = 1;
+		}
+		spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_BES2600_TESTMODE*/
+		memcpy(priv->bssid, info->bssid, ETH_ALEN);
+		bes2600_setup_mac_pvif(priv);
+	}
+
+	/* TODO: BSS_CHANGED_IBSS */
+	if (changed & BSS_CHANGED_ARP_FILTER) {
+
+		struct wsm_arp_ipv4_filter filter = {0};
+		int i;
+		bes2600_dbg(BES2600_DBG_AP, "[STA] BSS_CHANGED_ARP_FILTER cnt: %d",
+				     info->arp_addr_cnt);
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+		if (info->arp_addr_cnt > 0) {
+			bwifi_change_current_status(hw_priv, BWIFI_STATUS_GOT_IP);
+		}
+#endif
+		/* Currently only one IP address is supported by firmware.
+		 * In case of more IPs arp filtering will be disabled. */
+		if (info->arp_addr_cnt > 0 &&
+		    info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
+			for (i = 0; i < info->arp_addr_cnt; i++) {
+				filter.ipv4Address[i] = info->arp_addr_list[i];
+				bes2600_dbg(BES2600_DBG_AP, "[STA] addr[%d]: 0x%X\n",
+					  i, filter.ipv4Address[i]);
+			}
+			if (vif->type == NL80211_IFTYPE_STATION)
+			        filter.enable = (u32)BES2600_ENABLE_ARP_FILTER_OFFLOAD;
+			else if (priv->join_status == BES2600_JOIN_STATUS_AP)
+			        filter.enable = (u32)(1<<1);
+			else
+			        filter.enable = 0;
+		} else
+			filter.enable = 0;
+
+		bes2600_dbg(BES2600_DBG_AP, "[STA] arp ip filter enable: %d\n",
+			  __le32_to_cpu(filter.enable));
+
+		if (filter.enable)
+			bes2600_set_arpreply(dev, vif);
+
+		priv->filter4 = filter;
+
+		if (filter.enable &&
+			(priv->join_status == BES2600_JOIN_STATUS_STA)) {
+			/* Firmware requires that value for this 1-byte field must
+			 * be specified in units of 500us. Values above the 128ms
+			 * threshold are not supported. */
+			if (conf->dynamic_ps_timeout >= 0x80)
+				priv->powersave_mode.fastPsmIdlePeriod = 0xFF;
+			else
+				priv->powersave_mode.fastPsmIdlePeriod =
+					conf->dynamic_ps_timeout << 1;
+
+			/* override fast psm idle time */
+			if (override_fpsm_timeout) {
+				priv->powersave_mode.fastPsmIdlePeriod =
+					override_fpsm_timeout << 1;
+			}
+
+			if (priv->setbssparams_done) {
+				if (priv->user_power_set_true)
+					priv->powersave_mode.pmMode = priv->user_pm_mode;
+				else if (!priv->power_set_true)
+					priv->powersave_mode.pmMode = WSM_PSM_FAST_PS;
+			} else
+				priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
+			priv->power_set_true = 0;
+			priv->user_power_set_true = 0;
+
+			if(!info->ps) {
+				bes2600_pwr_set_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE);
+			}
+		}
+
+		bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_GET_IP);
+	}
+
+#ifdef IPV6_FILTERING
+	if (changed & BSS_CHANGED_NDP_FILTER) {
+		struct wsm_ndp_ipv6_filter filter = {0};
+		int i;
+		u16 *ipv6addr = NULL;
+
+		bes2600_dbg(BES2600_DBG_AP, "[STA] BSS_CHANGED_NDP_FILTER "
+				     "enabled: %d, cnt: %d\n",
+				     info->ndp_filter_enabled,
+				     info->ndp_addr_cnt);
+
+		if (info->ndp_filter_enabled) {
+			if (vif->type == NL80211_IFTYPE_STATION)
+				filter.enable = (u32)BES2600_ENABLE_NDP_FILTER_OFFLOAD;
+			else if ((vif->type == NL80211_IFTYPE_AP))
+				filter.enable = (u32)(1<<1);
+			else
+				filter.enable = 0;
+		}
+
+		/* Currently only one IP address is supported by firmware.
+		 * In case of more IPs ndp filtering will be disabled. */
+		if (info->ndp_addr_cnt > 0 &&
+		    info->ndp_addr_cnt <= WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES) {
+			for (i = 0; i < info->ndp_addr_cnt; i++) {
+				priv->filter6.ipv6Address[i] = filter.ipv6Address[i] = info->ndp_addr_list[i];
+				ipv6addr = (u16 *)(&filter.ipv6Address[i]);
+				bes2600_dbg(BES2600_DBG_AP, "[STA] ipv6 addr[%d]: %x:%x:%x:%x:%x:%x:%x:%x\n", \
+									i, cpu_to_be16(*(ipv6addr + 0)), cpu_to_be16(*(ipv6addr + 1)), \
+									cpu_to_be16(*(ipv6addr + 2)), cpu_to_be16(*(ipv6addr + 3)), \
+									cpu_to_be16(*(ipv6addr + 4)), cpu_to_be16(*(ipv6addr + 5)), \
+									cpu_to_be16(*(ipv6addr + 6)), cpu_to_be16(*(ipv6addr + 7)));
+			}
+		} else {
+			filter.enable = 0;
+			for (i = 0; i < info->ndp_addr_cnt; i++) {
+				ipv6addr = (u16 *)(&info->ndp_addr_list[i]);
+				bes2600_dbg(BES2600_DBG_AP, "[STA] ipv6 addr[%d]: %x:%x:%x:%x:%x:%x:%x:%x\n", \
+									i, cpu_to_be16(*(ipv6addr + 0)), cpu_to_be16(*(ipv6addr + 1)), \
+									cpu_to_be16(*(ipv6addr + 2)), cpu_to_be16(*(ipv6addr + 3)), \
+									cpu_to_be16(*(ipv6addr + 4)), cpu_to_be16(*(ipv6addr + 5)), \
+									cpu_to_be16(*(ipv6addr + 6)), cpu_to_be16(*(ipv6addr + 7)));
+			}
+		}
+
+		bes2600_dbg(BES2600_DBG_AP, "[STA] ndp ip filter enable: %d\n",
+			  __le32_to_cpu(filter.enable));
+
+		if (filter.enable)
+			bes2600_set_na(dev, vif);
+
+		priv->filter6.enable = filter.enable;
+
+		if (wsm_set_ndp_ipv6_filter(hw_priv, &filter, priv->if_id))
+			WARN_ON(1);
+#if 0 /*Commented out to disable Power Save in IPv6*/
+		if (filter.enable && (priv->join_status == BES2600_JOIN_STATUS_STA) && (priv->vif->p2p) &&
+				!(priv->firmware_ps_mode.pmMode & WSM_PSM_FAST_PS)) {
+			if(priv->setbssparams_done) {
+				struct wsm_set_pm pm = priv->powersave_mode;
+				int ret = 0;
+
+				priv->powersave_mode.pmMode = WSM_PSM_FAST_PS;
+				ret = bes2600_set_pm(priv, &priv->powersave_mode);
+				if(ret) {
+					priv->powersave_mode = pm;
+				}
+			} else {
+				priv->powersave_mode.pmMode = WSM_PSM_FAST_PS;
+			}
+		}
+#endif
+	}
+#endif /*IPV6_FILTERING*/
+
+	if (changed & BSS_CHANGED_BEACON) {
+		bes2600_dbg(BES2600_DBG_AP, "BSS_CHANGED_BEACON\n");
+#ifdef HIDDEN_SSID
+		if (priv->join_status != BES2600_JOIN_STATUS_AP)
+		{
+			priv->hidden_ssid = info->hidden_ssid;
+			priv->ssid_length = info->ssid_len;
+			memcpy(priv->ssid, info->ssid, info->ssid_len);
+		}
+		else
+			bes2600_dbg(BES2600_DBG_AP, "priv->join_status=%d\n",priv->join_status);
+#endif
+		WARN_ON(bes2600_upload_beacon(priv));
+		WARN_ON(bes2600_update_beaconing(priv));
+	}
+
+	if (changed & BSS_CHANGED_BEACON_ENABLED) {
+		bes2600_dbg(BES2600_DBG_AP, "BSS_CHANGED_BEACON_ENABLED dummy\n");
+		priv->enable_beacon = info->enable_beacon;
+	}
+
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		bes2600_dbg(BES2600_DBG_AP, "CHANGED_BEACON_INT\n");
+		/* Restart AP only when connected */
+		if (priv->join_status == BES2600_JOIN_STATUS_AP)
+			WARN_ON(bes2600_update_beaconing(priv));
+	}
+
+	if (changed & BSS_CHANGED_ASSOC) {
+		wsm_lock_tx(hw_priv);
+		priv->wep_default_key_id = -1;
+		wsm_unlock_tx(hw_priv);
+
+		if (!info->assoc /* && !info->ibss_joined */) {
+			#ifdef P2P_STA_COEX
+			priv->cqm_link_loss_count = 400;
+			priv->cqm_beacon_loss_count = 200;
+			#else
+			priv->cqm_link_loss_count = 100;
+			priv->cqm_beacon_loss_count = 50;
+			#endif
+			priv->cqm_tx_failure_thold = 0;
+		}
+		priv->cqm_tx_failure_count = 0;
+	}
+
+	if (changed &
+	    (BSS_CHANGED_ASSOC |
+	     BSS_CHANGED_BASIC_RATES |
+	     BSS_CHANGED_ERP_PREAMBLE |
+	     BSS_CHANGED_HT |
+	     BSS_CHANGED_ERP_SLOT)) {
+		int is_combo = 0;
+		int i;
+		struct bes2600_vif *tmp_priv;
+		bes2600_info(BES2600_DBG_AP, "BSS_CHANGED_ASSOC assoc: %d.\n", info->assoc);
+		if (info->assoc) { /* TODO: ibss_joined */
+			struct ieee80211_sta *sta = NULL;
+			if (info->dtim_period)
+				priv->join_dtim_period = info->dtim_period;
+			priv->beacon_int = info->beacon_int;
+
+			/* Associated: kill join timeout */
+			if(changed & BSS_CHANGED_ASSOC) {
+				cancel_delayed_work_sync(&priv->join_timeout);
+				bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN);
+				bes2600_pwr_set_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_GET_IP);
+				txrx_opt_timer_init(priv->hw_priv);
+			}
+
+			rcu_read_lock();
+			if (info->bssid)
+				sta = ieee80211_find_sta(vif, info->bssid);
+			if (sta) {
+				/* TODO:COMBO:Change this once
+				* mac80211 changes are available */
+				enum nl80211_channel_type ch_type;
+				BUG_ON(!hw_priv->channel);
+				hw_priv->ht_info.ht_cap = sta->ht_cap;
+				priv->bss_params.operationalRateSet =
+					__cpu_to_le32(
+					bes2600_rate_mask_to_wsm(hw_priv,
+					sta->supp_rates[
+						hw_priv->channel->band]));
+				rcu_read_unlock();
+				ch_type = cfg80211_get_chandef_type(&info->chandef);
+				bes2600_dbg(BES2600_DBG_AP, "[STA] ch %d, type: %d, HT oper mode: %d\n",
+					  hw_priv->channel->hw_value,
+					  ch_type, info->ht_operation_mode);
+				if ((changed & BSS_CHANGED_ASSOC) &&
+					priv->join_status == BES2600_JOIN_STATUS_STA) {
+					struct wsm_switch_channel channel;
+
+					hw_priv->ht_info.channel_type = ch_type;
+					channel.channelMode = hw_priv->ht_info.channel_type << 4;
+					channel.channelSwitchCount = 0;
+					channel.newChannelNumber = hw_priv->channel->hw_value;
+					wsm_switch_channel(hw_priv, &channel, priv->if_id);
+					bes2600_info(BES2600_DBG_AP, "channel type changed to %d !!!\n", hw_priv->ht_info.channel_type);
+				}
+				if (hw_priv->ht_info.operation_mode != info->ht_operation_mode)
+					hw_priv->ht_info.operation_mode = info->ht_operation_mode;
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+				if (changed & BSS_CHANGED_ASSOC) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+					if (hw_priv->channel->band != NL80211_BAND_2GHZ)
+#else
+					if (hw_priv->channel->band != IEEE80211_BAND_2GHZ)
+#endif
+						bwifi_change_current_status(hw_priv, BWIFI_STATUS_CONNECTED_5G);
+					else
+						bwifi_change_current_status(hw_priv, BWIFI_STATUS_CONNECTED);
+				}
+#endif
+			} else {
+				rcu_read_unlock();
+
+				memset(&hw_priv->ht_info, 0, sizeof(hw_priv->ht_info));
+				priv->bss_params.operationalRateSet = -1;
+			}
+			//rcu_read_unlock();
+			priv->htcap = (sta && bes2600_is_ht(&hw_priv->ht_info));
+			bes2600_for_each_vif(hw_priv, tmp_priv, i) {
+#ifdef P2P_MULTIVIF
+				if ((i == (CW12XX_MAX_VIFS - 1)) || !tmp_priv)
+#else
+				if (!tmp_priv)
+#endif
+					continue;
+				if (tmp_priv->join_status >= BES2600_JOIN_STATUS_STA)
+					is_combo++;
+			}
+
+			if (is_combo > 1) {
+				hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
+				hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
+				bes2600_info(BES2600_DBG_AP, "ASSOC is_combo %d\n",hw_priv->vif0_throttle);
+			} else if ((priv->join_status == BES2600_JOIN_STATUS_STA) &&
+					priv->htcap) {
+				hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11N_THROTTLE;
+				hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11N_THROTTLE;
+				bes2600_info(BES2600_DBG_AP, "ASSOC HTCAP 11N %d\n",hw_priv->vif0_throttle);
+			} else {
+				hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
+				hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
+				bes2600_info(BES2600_DBG_AP, "ASSOC not_combo 11BG %d\n",hw_priv->vif0_throttle);
+			}
+
+			if (sta) {
+				__le32 val = 0;
+				if (hw_priv->ht_info.operation_mode &
+				IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) {
+					bes2600_info(BES2600_DBG_AP, "[STA]"
+						" Non-GF STA present\n");
+					/* Non Green field capable STA */
+					val = __cpu_to_le32(BIT(1));
+				}
+				WARN_ON(wsm_write_mib(hw_priv,
+					WSM_MID_ID_SET_HT_PROTECTION,
+					&val, sizeof(val), priv->if_id));
+			}
+
+			priv->association_mode.greenfieldMode =
+				bes2600_ht_greenfield(&hw_priv->ht_info);
+			priv->association_mode.flags =
+				WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES |
+				WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE |
+				WSM_ASSOCIATION_MODE_USE_HT_MODE |
+				WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET |
+				WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING;
+			priv->association_mode.preambleType =
+				info->use_short_preamble ?
+				WSM_JOIN_PREAMBLE_SHORT :
+				WSM_JOIN_PREAMBLE_LONG;
+			priv->association_mode.basicRateSet = __cpu_to_le32(
+				bes2600_rate_mask_to_wsm(hw_priv,
+				info->basic_rates));
+			priv->association_mode.mpduStartSpacing =
+				bes2600_ht_ampdu_density(&hw_priv->ht_info);
+
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+			priv->cqm_beacon_loss_count =
+					info->cqm_beacon_miss_thold;
+			priv->cqm_tx_failure_thold =
+					info->cqm_tx_fail_thold;
+			priv->cqm_tx_failure_count = 0;
+			cancel_delayed_work_sync(&priv->bss_loss_work);
+			cancel_delayed_work_sync(&priv->connection_loss_work);
+#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+
+			priv->bss_params.beaconLostCount =
+					priv->cqm_beacon_loss_count ?
+					priv->cqm_beacon_loss_count :
+					priv->cqm_link_loss_count;
+
+			priv->bss_params.aid = info->aid;
+
+			if (priv->join_dtim_period < 1)
+				priv->join_dtim_period = 1;
+
+			bes2600_dbg(BES2600_DBG_AP, "[STA] DTIM %d, interval: %d\n",
+				priv->join_dtim_period, priv->beacon_int);
+			bes2600_dbg(BES2600_DBG_AP, "[STA] Preamble: %d, " \
+				"Greenfield: %d, Aid: %d, " \
+				"Rates: 0x%.8X, Basic: 0x%.8X\n",
+				priv->association_mode.preambleType,
+				priv->association_mode.greenfieldMode,
+				priv->bss_params.aid,
+				priv->bss_params.operationalRateSet,
+				priv->association_mode.basicRateSet);
+			WARN_ON(wsm_set_association_mode(hw_priv,
+				&priv->association_mode, priv->if_id));
+			WARN_ON(wsm_keep_alive_period(hw_priv,
+				BES2600_KEEP_ALIVE_PERIOD /* sec */,
+				priv->if_id));
+			WARN_ON(wsm_set_bss_params(hw_priv, &priv->bss_params,
+				priv->if_id));
+			priv->setbssparams_done = true;
+			WARN_ON(wsm_set_beacon_wakeup_period(hw_priv,
+				    priv->beacon_int * priv->join_dtim_period >
+				    MAX_BEACON_SKIP_TIME_MS ? 1 :
+				    priv->join_dtim_period, 0, priv->if_id));
+			if (priv->htcap) {
+				wsm_lock_tx(hw_priv);
+				/* Statically enabling block ack for TX/RX */
+				WARN_ON(wsm_set_block_ack_policy(hw_priv,
+					hw_priv->ba_tid_mask, hw_priv->ba_tid_mask,
+						priv->if_id));
+				wsm_unlock_tx(hw_priv);
+			}
+
+			if (priv->vif->p2p) {
+				bes2600_dbg(BES2600_DBG_AP,
+					"[STA] Setting p2p powersave "
+					"configuration.\n");
+				WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv,
+					&priv->p2p_ps_modeinfo, priv->if_id));
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+				bes2600_notify_noa(priv, BES2600_NOA_NOTIFICATION_DELAY);
+#endif
+			}
+
+			if (priv->mode == NL80211_IFTYPE_STATION)
+				WARN_ON(bes2600_upload_qosnull(priv));
+
+			if (hw_priv->is_BT_Present)
+				WARN_ON(bes2600_set_btcoexinfo(priv));
+
+			atomic_set(&priv->connect_in_process, 0);
+			if (priv->delayed_unjoin) {
+				priv->delayed_unjoin = false;
+				bes2600_info(BES2600_DBG_AP, "queue delayed unjoin work for connect\n");
+				if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
+					wsm_unlock_tx(hw_priv);
+			}
+
+#if 0
+			/* It's better to override internal TX rete; otherwise
+			 * device sends RTS at too high rate. However device
+			 * can't receive CTS at 1 and 2 Mbps. Well, 5.5 is a
+			 * good choice for RTS/CTS, but that means PS poll
+			 * will be sent at the same rate - impact on link
+			 * budget. Not sure what is better.. */
+
+			/* Update: internal rate selection algorythm is not
+			 * bad: if device is not receiving CTS at high rate,
+			 * it drops RTS rate.
+			 * So, conclusion: if-0 the code. Keep code just for
+			 * information:
+			 * Do not touch WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE! */
+
+			/* ~3 is a bug in device: RTS/CTS is not working at
+			 * low rates */
+
+			__le32 internal_tx_rate = __cpu_to_le32(__ffs(
+				priv->association_mode.basicRateSet & ~3));
+			WARN_ON(wsm_write_mib(priv,
+				WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+				&internal_tx_rate,
+				sizeof(internal_tx_rate)));
+#endif
+		} else {
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+			if (changed & BSS_CHANGED_ASSOC)
+				bwifi_change_current_status(hw_priv, BWIFI_STATUS_DISCONNECTED);
+#endif
+			memset(&hw_priv->ht_info, 0,
+				sizeof(hw_priv->ht_info));
+			memset(&priv->association_mode, 0,
+				sizeof(priv->association_mode));
+			memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+		}
+	}
+
+	if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT)) {
+		u32 prev_erp_info = priv->erp_info;
+
+		if (priv->join_status == BES2600_JOIN_STATUS_AP) {
+			if (info->use_cts_prot)
+				priv->erp_info |= WLAN_ERP_USE_PROTECTION;
+			else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT))
+				priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
+
+			if (prev_erp_info != priv->erp_info)
+				queue_delayed_work(hw_priv->workqueue,
+						&priv->set_cts_work, 0*HZ);
+		}
+	}
+
+	if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) {
+		__le32 slot_time = info->use_short_slot ?
+			__cpu_to_le32(9) : __cpu_to_le32(20);
+		bes2600_dbg(BES2600_DBG_AP, "[STA] Slot time :%d us.\n",
+			__le32_to_cpu(slot_time));
+		WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_SLOT_TIME,
+			&slot_time, sizeof(slot_time), priv->if_id));
+	}
+
+	if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
+		struct wsm_rcpi_rssi_threshold threshold = {
+			.rollingAverageCount = 8,
+		};
+
+#if 0
+		/* For verification purposes */
+		info->cqm_rssi_thold = -50;
+		info->cqm_rssi_hyst = 4;
+#endif /* 0 */
+
+		bes2600_dbg(BES2600_DBG_AP, "[CQM] RSSI threshold "
+			"subscribe: %d +- %d\n",
+			info->cqm_rssi_thold, info->cqm_rssi_hyst);
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+		bes2600_dbg(BES2600_DBG_AP, "[CQM] Beacon loss subscribe: %d\n",
+			info->cqm_beacon_miss_thold);
+		bes2600_dbg(BES2600_DBG_AP, "[CQM] TX failure subscribe: %d\n",
+			info->cqm_tx_fail_thold);
+		priv->cqm_rssi_thold = info->cqm_rssi_thold;
+		priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
+#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+		if (info->cqm_rssi_thold || info->cqm_rssi_hyst) {
+			/* RSSI subscription enabled */
+			/* TODO: It's not a correct way of setting threshold.
+			 * Upper and lower must be set equal here and adjusted
+			 * in callback. However current implementation is much
+			 * more relaible and stable. */
+			if (priv->cqm_use_rssi) {
+				threshold.upperThreshold =
+					info->cqm_rssi_thold +
+					info->cqm_rssi_hyst;
+				threshold.lowerThreshold =
+					info->cqm_rssi_thold;
+			} else {
+				/* convert RSSI to RCPI
+				 * RCPI = (RSSI + 110) * 2 */
+				threshold.upperThreshold =
+					(info->cqm_rssi_thold +
+					 info->cqm_rssi_hyst + 110) * 2;
+				threshold.lowerThreshold =
+					(info->cqm_rssi_thold + 110) * 2;
+			}
+			threshold.rssiRcpiMode |=
+				WSM_RCPI_RSSI_THRESHOLD_ENABLE;
+		} else {
+			/* There is a bug in FW, see sta.c. We have to enable
+			 * dummy subscription to get correct RSSI values. */
+			threshold.rssiRcpiMode |=
+				WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+				WSM_RCPI_RSSI_DONT_USE_UPPER |
+				WSM_RCPI_RSSI_DONT_USE_LOWER;
+		}
+		WARN_ON(wsm_set_rcpi_rssi_threshold(hw_priv, &threshold,
+					priv->if_id));
+
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+		priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold;
+		priv->cqm_tx_failure_count = 0;
+
+		if (priv->cqm_beacon_loss_count !=
+				info->cqm_beacon_miss_thold) {
+			priv->cqm_beacon_loss_count =
+				info->cqm_beacon_miss_thold;
+			priv->bss_params.beaconLostCount =
+				priv->cqm_beacon_loss_count ?
+				priv->cqm_beacon_loss_count :
+				priv->cqm_link_loss_count;
+			/* Make sure we are associated before sending
+			 * set_bss_params to firmware */
+			if (priv->bss_params.aid) {
+				WARN_ON(wsm_set_bss_params(hw_priv,
+							   &priv->bss_params,
+							   priv->if_id));
+				priv->setbssparams_done = true;
+			}
+		}
+#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+	}
+
+	if (changed & BSS_CHANGED_BANDWIDTH) {
+		enum nl80211_channel_type ch_type = cfg80211_get_chandef_type(&info->chandef);
+
+		if (info->assoc &&
+		    hw_priv->ht_info.channel_type != ch_type &&
+		    priv->join_status == BES2600_JOIN_STATUS_STA) {
+			struct wsm_switch_channel channel;
+
+			hw_priv->ht_info.channel_type = ch_type;
+			channel.channelMode = hw_priv->ht_info.channel_type << 4;
+			channel.channelSwitchCount = 0;
+			channel.newChannelNumber = hw_priv->channel->hw_value;
+			wsm_switch_channel(hw_priv, &channel, priv->if_id);
+			bes2600_info(BES2600_DBG_AP, "channel type changed to %d !!!\n", hw_priv->ht_info.channel_type);
+		}
+	}
+
+	if (changed & BSS_CHANGED_PS) {
+		if (info->ps == false)
+			priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
+		else if (conf->dynamic_ps_timeout <= 0)
+			priv->powersave_mode.pmMode = WSM_PSM_PS;
+		else
+			priv->powersave_mode.pmMode = WSM_PSM_FAST_PS;
+
+		/* set/clear ps active power busy event */
+		if(priv->join_status == BES2600_JOIN_STATUS_STA) {
+			if(!info->ps) {
+				bes2600_pwr_set_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE);
+			} else {
+				bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE);
+			}
+		}
+
+		bes2600_info(BES2600_DBG_AP, "[STA] Aid: %d, Joined: %s, Powersave: %s\n",
+			priv->bss_params.aid,
+			priv->join_status == BES2600_JOIN_STATUS_STA ? "yes" : "no",
+			priv->powersave_mode.pmMode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" :
+			priv->powersave_mode.pmMode == WSM_PSM_PS ? "WSM_PSM_PS" :
+			priv->powersave_mode.pmMode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" :
+			"UNKNOWN");
+
+		/* Firmware requires that value for this 1-byte field must
+		 * be specified in units of 500us. Values above the 128ms
+		 * threshold are not supported. */
+		if (conf->dynamic_ps_timeout >= 0x80)
+			priv->powersave_mode.fastPsmIdlePeriod = 0xFF;
+		else
+			priv->powersave_mode.fastPsmIdlePeriod =
+					conf->dynamic_ps_timeout << 1;
+
+		/* override fast psm idle time */
+		if (override_fpsm_timeout) {
+			priv->powersave_mode.fastPsmIdlePeriod =
+				override_fpsm_timeout << 1;
+		}
+
+		if (priv->join_status == BES2600_JOIN_STATUS_STA &&
+		    priv->bss_params.aid &&
+		    priv->setbssparams_done &&
+		    priv->filter4.enable &&
+		    priv->powersave_mode.pmMode == WSM_PSM_ACTIVE) {
+			bes2600_set_pm(priv, &priv->powersave_mode);
+		} else {
+			priv->power_set_true = 1;
+		}
+	}
+
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	if (changed & BSS_CHANGED_P2P_PS) {
+		struct wsm_p2p_ps_modeinfo *modeinfo;
+		modeinfo = &priv->p2p_ps_modeinfo;
+		bes2600_dbg(BES2600_DBG_AP, "[AP] BSS_CHANGED_P2P_PS\n");
+		bes2600_dbg(BES2600_DBG_AP, "[AP] Legacy PS: %d for AID %d "
+			"in %d mode.\n", info->p2p_ps.legacy_ps,
+			priv->bss_params.aid, priv->join_status);
+
+		if (info->p2p_ps.legacy_ps >= 0) {
+			if (info->p2p_ps.legacy_ps > 0)
+				priv->powersave_mode.pmMode = WSM_PSM_PS;
+			else
+				priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
+
+			if(info->p2p_ps.ctwindow && info->p2p_ps.opp_ps)
+				priv->powersave_mode.pmMode = WSM_PSM_PS;
+			if (priv->join_status == BES2600_JOIN_STATUS_STA)
+				bes2600_set_pm(priv, &priv->powersave_mode);
+		}
+
+		bes2600_dbg(BES2600_DBG_AP, "[AP] CTWindow: %d\n",
+			info->p2p_ps.ctwindow);
+		if (info->p2p_ps.ctwindow >= 128)
+			modeinfo->oppPsCTWindow = 127;
+		else if (info->p2p_ps.ctwindow >= 0)
+			modeinfo->oppPsCTWindow = info->p2p_ps.ctwindow;
+
+		bes2600_dbg(BES2600_DBG_AP, "[AP] Opportunistic: %d\n",
+			info->p2p_ps.opp_ps);
+		switch (info->p2p_ps.opp_ps) {
+		case 0:
+			modeinfo->oppPsCTWindow &= ~(BIT(7));
+			break;
+		case 1:
+			modeinfo->oppPsCTWindow |= BIT(7);
+			break;
+		default:
+			break;
+		}
+
+		bes2600_dbg(BES2600_DBG_AP, "[AP] NOA: %d, %d, %d, %d\n",
+			info->p2p_ps.count,
+			info->p2p_ps.start,
+			info->p2p_ps.duration,
+			info->p2p_ps.interval);
+		/* Notice of Absence */
+		modeinfo->count = info->p2p_ps.count;
+
+		if (info->p2p_ps.count) {
+			/* In case P2P_GO we need some extra time to be sure
+			 * we will update beacon/probe_resp IEs correctly */
+#define NOA_DELAY_START_MS	300
+			if (priv->join_status == BES2600_JOIN_STATUS_AP)
+				modeinfo->startTime =
+					__cpu_to_le32(info->p2p_ps.start +
+						      NOA_DELAY_START_MS);
+			else
+				modeinfo->startTime =
+					__cpu_to_le32(info->p2p_ps.start);
+			modeinfo->duration =
+				__cpu_to_le32(info->p2p_ps.duration);
+			modeinfo->interval =
+				 __cpu_to_le32(info->p2p_ps.interval);
+			modeinfo->dtimCount = 1;
+			modeinfo->reserved = 0;
+		} else {
+			modeinfo->dtimCount = 0;
+			modeinfo->startTime = 0;
+			modeinfo->reserved = 0;
+			modeinfo->duration = 0;
+			modeinfo->interval = 0;
+		}
+
+#if defined(CONFIG_BES2600_STA_DEBUG)
+		print_hex_dump_bytes("p2p_set_ps_modeinfo: ",
+				     DUMP_PREFIX_NONE,
+				     (u8 *)modeinfo,
+				     sizeof(*modeinfo));
+#endif /* CONFIG_BES2600_STA_DEBUG */
+		if (priv->join_status == BES2600_JOIN_STATUS_STA ||
+		    priv->join_status == BES2600_JOIN_STATUS_AP) {
+			WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv, modeinfo,
+				priv->if_id));
+		}
+
+		/* Temporary solution while firmware don't support NOA change
+		 * notification yet */
+		bes2600_notify_noa(priv, 10);
+	}
+#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+
+	up(&hw_priv->conf_lock);
+}
+
+void bes2600_multicast_start_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, multicast_start_work);
+	long tmo = priv->join_dtim_period *
+			(priv->beacon_int + 20) * HZ / 1024;
+
+	cancel_work_sync(&priv->multicast_stop_work);
+
+	if (!priv->aid0_bit_set) {
+		wsm_lock_tx(priv->hw_priv);
+		bes2600_set_tim_impl(priv, true);
+		priv->aid0_bit_set = true;
+		mod_timer(&priv->mcast_timeout, jiffies + tmo);
+		wsm_unlock_tx(priv->hw_priv);
+	}
+}
+
+void bes2600_multicast_stop_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, multicast_stop_work);
+
+	if (priv->aid0_bit_set) {
+		del_timer_sync(&priv->mcast_timeout);
+		wsm_lock_tx(priv->hw_priv);
+		priv->aid0_bit_set = false;
+		bes2600_set_tim_impl(priv, false);
+		wsm_unlock_tx(priv->hw_priv);
+	}
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+void bes2600_mcast_timeout(struct timer_list *t)
+{
+	struct bes2600_vif *priv = from_timer(priv, t, mcast_timeout);
+
+	wiphy_warn(priv->hw->wiphy,
+		"Multicast delivery timeout.\n");
+	spin_lock_bh(&priv->ps_state_lock);
+	priv->tx_multicast = priv->aid0_bit_set &&
+			priv->buffered_multicasts;
+	if (priv->tx_multicast)
+		bes2600_bh_wakeup(cw12xx_vifpriv_to_hwpriv(priv));
+	spin_unlock_bh(&priv->ps_state_lock);
+}
+#else
+void bes2600_mcast_timeout(unsigned long arg)
+{
+	struct bes2600_vif *priv = (struct bes2600_vif *)arg;
+
+	wiphy_warn(priv->hw->wiphy,
+		"Multicast delivery timeout.\n");
+	spin_lock_bh(&priv->ps_state_lock);
+	priv->tx_multicast = priv->aid0_bit_set &&
+			priv->buffered_multicasts;
+	if (priv->tx_multicast)
+		bes2600_bh_wakeup(cw12xx_vifpriv_to_hwpriv(priv));
+	spin_unlock_bh(&priv->ps_state_lock);
+}
+#endif
+
+int bes2600_ampdu_action(struct ieee80211_hw *hw,
+			struct ieee80211_vif *vif,
+			struct ieee80211_ampdu_params *params)
+{
+	/* Aggregation is implemented fully in firmware,
+	 * including block ack negotiation.
+	 * In case of AMPDU aggregation in RX direction
+	 * re-ordering of packets takes place on host. mac80211
+	 * needs the ADDBA Request to setup reodering.mac80211 also
+	 * sends ADDBA Response which is discarded in the driver as
+	 * FW generates the ADDBA Response on its own.*/
+	int ret;
+
+	switch (params->action) {
+	case IEEE80211_AMPDU_RX_START:
+	case IEEE80211_AMPDU_RX_STOP:
+		/* Just return OK to mac80211 */
+		ret = 0;
+		break;
+	default:
+		ret = -ENOTSUPP;
+	}
+	return ret;
+}
+
+/* ******************************************************************** */
+/* WSM callback								*/
+void bes2600_suspend_resume(struct bes2600_vif *priv,
+			   struct wsm_suspend_resume *arg)
+{
+	struct bes2600_common *hw_priv =
+		cw12xx_vifpriv_to_hwpriv(priv);
+
+	bes2600_dbg(BES2600_DBG_AP, "[AP] %s: %s\n",
+			arg->stop ? "stop" : "start",
+			arg->multicast ? "broadcast" : "unicast");
+
+	if (arg->multicast) {
+		bool cancel_tmo = false;
+		spin_lock_bh(&priv->ps_state_lock);
+		if (arg->stop) {
+			priv->tx_multicast = false;
+		} else {
+			/* Firmware sends this indication every DTIM if there
+			 * is a STA in powersave connected. There is no reason
+			 * to suspend, following wakeup will consume much more
+			 * power than it could be saved. */
+			u32 timeout = priv->join_dtim_period *
+				(priv->beacon_int + 20) * HZ / 1024;
+			timeout = jiffies_to_msecs(timeout);
+			bes2600_pwr_set_busy_event_with_timeout_async(hw_priv,
+				BES_PWR_LOCK_ON_MUL_REQ, timeout);
+
+			priv->tx_multicast = priv->aid0_bit_set &&
+					priv->buffered_multicasts;
+			if (priv->tx_multicast) {
+				cancel_tmo = true;
+				bes2600_bh_wakeup(hw_priv);
+			}
+		}
+		spin_unlock_bh(&priv->ps_state_lock);
+		if (cancel_tmo)
+			del_timer_sync(&priv->mcast_timeout);
+	} else {
+		spin_lock_bh(&priv->ps_state_lock);
+		bes2600_ps_notify(priv, arg->link_id, arg->stop);
+		spin_unlock_bh(&priv->ps_state_lock);
+		if (!arg->stop)
+			bes2600_bh_wakeup(hw_priv);
+	}
+	return;
+}
+
+/* ******************************************************************** */
+/* AP privates								*/
+
+static int bes2600_upload_beacon(struct bes2600_vif *priv)
+{
+	int ret = 0;
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct wsm_template_frame frame = {
+		.frame_type = WSM_FRAME_TYPE_BEACON,
+	};
+	struct ieee80211_mgmt *mgmt;
+	u8 *erp_inf, *ies, *ht_info;
+	u32 ies_len;
+
+	if (priv->vif->p2p ||
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+			hw_priv->channel->band == NL80211_BAND_5GHZ)
+#else
+			hw_priv->channel->band == IEEE80211_BAND_5GHZ)
+#endif
+		frame.rate = WSM_TRANSMIT_RATE_6;
+
+	frame.skb = ieee80211_beacon_get(priv->hw, priv->vif);
+	if (WARN_ON(!frame.skb))
+		return -ENOMEM;
+
+	mgmt = (void *)frame.skb->data;
+	ies = mgmt->u.beacon.variable;
+	ies_len = frame.skb->len - (u32)(ies - (u8 *)mgmt);
+
+	ht_info = (u8 *)cfg80211_find_ie( WLAN_EID_HT_OPERATION, ies, ies_len);
+        if (ht_info) {
+		/* Enable RIFS*/
+		ht_info[3] |= 8;
+        }
+
+	erp_inf = (u8 *)cfg80211_find_ie(WLAN_EID_ERP_INFO, ies, ies_len);
+	if (erp_inf) {
+		if (erp_inf[ERP_INFO_BYTE_OFFSET]
+				& WLAN_ERP_BARKER_PREAMBLE)
+			priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE;
+		else
+			priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE;
+
+		if (erp_inf[ERP_INFO_BYTE_OFFSET]
+				& WLAN_ERP_NON_ERP_PRESENT) {
+			priv->erp_info |= WLAN_ERP_USE_PROTECTION;
+			priv->erp_info |= WLAN_ERP_NON_ERP_PRESENT;
+		} else {
+			priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
+			priv->erp_info &= ~WLAN_ERP_NON_ERP_PRESENT;
+		}
+	}
+
+#ifdef HIDDEN_SSID
+	if (priv->hidden_ssid) {
+		u8 *ssid_ie;
+		u8 ssid_len;
+
+		bes2600_dbg(BES2600_DBG_AP, "%s: hidden_ssid set\n", __func__);
+		ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
+		WARN_ON(!ssid_ie);
+		ssid_len = ssid_ie[1];
+		if (ssid_len) {
+			bes2600_dbg(BES2600_DBG_AP, "%s: hidden_ssid with zero content "
+					"ssid\n", __func__);
+			ssid_ie[1] = 0;
+			memmove(ssid_ie + 2, ssid_ie + 2 + ssid_len,
+					(ies + ies_len -
+					 (ssid_ie + 2 + ssid_len)));
+			frame.skb->len -= ssid_len;
+		} else {
+			pbes2600_dbg(BES2600_DBG_AP, "%s: hidden ssid with ssid len 0"
+					" not supported", __func__);
+			dev_kfree_skb(frame.skb);
+			return -1;
+		}
+	}
+#endif
+
+	ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
+	if (!ret) {
+#ifdef PROBE_RESP_EXTRA_IE
+		ret = bes2600_upload_proberesp(priv);
+#else
+		/* TODO: Distille probe resp; remove TIM
+		 * and other beacon-specific IEs */
+		*(__le16 *)frame.skb->data =
+			__cpu_to_le16(IEEE80211_FTYPE_MGMT |
+				      IEEE80211_STYPE_PROBE_RESP);
+		frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE;
+		/* TODO: Ideally probe response template should separately
+		   configured by supplicant through openmac. This is a
+		   temporary work-around known to fail p2p group info
+		   attribute related tests
+		   */
+		if (0 /* priv->vif->p2p */)
+			ret = wsm_set_probe_responder(priv, true);
+		else {
+			ret = wsm_set_template_frame(hw_priv, &frame,
+					priv->if_id);
+			WARN_ON(wsm_set_probe_responder(priv, false));
+		}
+#endif
+	}
+	dev_kfree_skb(frame.skb);
+
+	return ret;
+}
+
+#ifdef PROBE_RESP_EXTRA_IE
+static int bes2600_upload_proberesp(struct bes2600_vif *priv)
+{
+	int ret = 0;
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct wsm_template_frame frame = {
+		.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE,
+	};
+#ifdef HIDDEN_SSID
+	u8 *ssid_ie;
+#endif
+	if (priv->vif->p2p || hw_priv->channel->band == NL80211_BAND_5GHZ)
+		frame.rate = WSM_TRANSMIT_RATE_6;
+
+	frame.skb = ieee80211_proberesp_get(priv->hw, priv->vif);
+	if (WARN_ON(!frame.skb))
+		return -ENOMEM;
+
+#ifdef HIDDEN_SSID
+	if (priv->hidden_ssid) {
+		int offset;
+		u8 ssid_len;
+		/* we are assuming beacon from upper layer will always contain
+		   zero filled ssid for hidden ap. The beacon shall never have
+		   ssid len = 0.
+		  */
+
+		offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+		ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID,
+				frame.skb->data + offset,
+				frame.skb->len - offset);
+		ssid_len = ssid_ie[1];
+		if (ssid_len && (ssid_len == priv->ssid_length)) {
+			memcpy(ssid_ie + 2, priv->ssid, ssid_len);
+		} else {
+			bes2600_dbg(BES2600_DBG_AP, "%s: hidden ssid with mismatched ssid_len %d\n",
+					__func__, ssid_len);
+			dev_kfree_skb(frame.skb);
+			return -1;
+		}
+	}
+#endif
+	ret = wsm_set_template_frame(hw_priv, &frame,  priv->if_id);
+	WARN_ON(wsm_set_probe_responder(priv, false));
+
+	dev_kfree_skb(frame.skb);
+
+	return ret;
+}
+#endif
+
+static int bes2600_upload_pspoll(struct bes2600_vif *priv)
+{
+	int ret = 0;
+	struct wsm_template_frame frame = {
+		.frame_type = WSM_FRAME_TYPE_PS_POLL,
+		.rate = 0xFF,
+	};
+
+
+	frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
+	if (WARN_ON(!frame.skb))
+		return -ENOMEM;
+
+	ret = wsm_set_template_frame(cw12xx_vifpriv_to_hwpriv(priv), &frame,
+				priv->if_id);
+
+	dev_kfree_skb(frame.skb);
+
+	return ret;
+}
+
+static int bes2600_upload_null(struct bes2600_vif *priv)
+{
+	int ret = 0;
+	struct wsm_template_frame frame = {
+		.frame_type = WSM_FRAME_TYPE_NULL,
+		.rate = 0xFF,
+	};
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+	frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false);
+#else
+	frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+#endif
+	if (WARN_ON(!frame.skb))
+		return -ENOMEM;
+
+	ret = wsm_set_template_frame(cw12xx_vifpriv_to_hwpriv(priv),
+				&frame, priv->if_id);
+
+	dev_kfree_skb(frame.skb);
+
+	return ret;
+}
+
+static int bes2600_upload_qosnull(struct bes2600_vif *priv)
+{
+	int ret = 0;
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct wsm_template_frame frame = {
+		.frame_type = WSM_FRAME_TYPE_QOS_NULL,
+		.rate = 0xFF,
+	};
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+	frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif, true);
+#else
+	frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+#endif
+
+	if (WARN_ON(!frame.skb))
+		return -ENOMEM;
+
+	ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
+
+	dev_kfree_skb(frame.skb);
+
+	return ret;
+}
+
+/* This API is nolonegr present in WSC */
+#if 0
+static int bes2600_enable_beaconing(struct bes2600_vif *priv,
+				   bool enable)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct wsm_beacon_transmit transmit = {
+		.enableBeaconing = enable,
+	};
+
+	return wsm_beacon_transmit(hw_priv, &transmit, priv->if_id);
+}
+#endif
+
+static int start_dhcpd(void)
+{
+#if 0
+	volatile int result=0;
+	char cmdPath[]="/bin/bash";
+	char cmdText[128];
+	char* cmdArgv[]={cmdPath, "-c", cmdText, NULL};
+	char* cmdArgv2[]={cmdPath, "-c", "/usr/sbin/dhcpd >> /tmp/log", NULL};
+	char* cmdEnvp[]={"HOME=/", "PATH=/sbin:/bin:/usr/bin:/usr/sbin", NULL};
+	static int ifname_id = 0;
+
+	snprintf(cmdText, 128, "/sbin/ifconfig p2p-wlan0-%d 192.168.50.1/24 up >> /tmp/log2", ifname_id);
+	ifname_id++;
+
+	result=call_usermodehelper(cmdPath,cmdArgv,cmdEnvp, UMH_WAIT_EXEC);
+	msleep(100);
+	result=call_usermodehelper(cmdPath,cmdArgv2,cmdEnvp, UMH_WAIT_EXEC);
+	//msleep(100);
+	//result=call_usermodehelper(cmdPath,cmdArgv3,cmdEnvp, UMH_WAIT_EXEC);
+#else
+	volatile int result=0;
+	char cmdPath[]="/bin/bash";
+	char* cmdArgv[]={cmdPath, "-c", "/usr/bin/start_dhcpd > /tmp/log", NULL};
+	char* cmdEnvp[]={"HOME=/", "PATH=/sbin:/bin:/usr/bin:/usr/sbin", NULL};
+
+	result=call_usermodehelper(cmdPath,cmdArgv,cmdEnvp, UMH_WAIT_EXEC);
+#endif
+	bes2600_info(BES2600_DBG_AP, "testDriver1 _init exec! The result of call_usermodehelper is %d\n",result);
+	bes2600_info(BES2600_DBG_AP, "testDriver1 _init exec! The process is \"%s\",pid is %d\n",current->comm,current->pid);
+
+	return 0;
+}
+
+
+static int bes2600_start_ap(struct bes2600_vif *priv)
+{
+	int ret;
+#ifndef HIDDEN_SSID
+	const u8 *ssidie;
+	struct sk_buff *skb;
+	int offset;
+#endif
+	struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct wsm_start start = {
+		.mode = priv->vif->p2p ?
+				WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
+		/* TODO:COMBO:Change once mac80211 support is available */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+		.band = (hw_priv->channel->band == NL80211_BAND_5GHZ) ?
+#else
+		.band = (hw_priv->channel->band == IEEE80211_BAND_5GHZ) ?
+#endif
+				WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
+		.channelNumber = hw_priv->channel->hw_value,
+		.beaconInterval = conf->beacon_int,
+		.DTIMPeriod = conf->dtim_period,
+		.preambleType = conf->use_short_preamble ?
+				WSM_JOIN_PREAMBLE_SHORT :
+				WSM_JOIN_PREAMBLE_LONG,
+		.probeDelay = 100,
+		.basicRateSet = bes2600_rate_mask_to_wsm(hw_priv,
+				conf->basic_rates),
+#ifdef P2P_MULTIVIF
+		.CTWindow = priv->if_id ? 0xFFFFFFFF : 0, /* it makes addr1's byte4 ^0x80 in firmware,
+			which matches with driver's addr2 */
+#endif
+	};
+
+	struct wsm_switch_channel channel = {
+		.channelMode = (cfg80211_get_chandef_type(&conf->chandef)) << 4,
+		.channelSwitchCount = 0,
+		.newChannelNumber = hw_priv->channel->hw_value,
+	};
+
+	struct wsm_inactivity inactivity = {
+		.min_inactivity = 9,
+		.max_inactivity = 1,
+	};
+
+	if (priv->vif->p2p) {
+		start_dhcpd();
+		hw_priv->ht_info.channel_type = cfg80211_get_chandef_type(&conf->chandef);
+	}
+
+	if (priv->if_id)
+		start.mode |= WSM_FLAG_MAC_INSTANCE_1;
+	else
+		start.mode &= ~WSM_FLAG_MAC_INSTANCE_1;
+
+	hw_priv->connected_sta_cnt = 0;
+
+#ifndef HIDDEN_SSID
+	/* Get SSID */
+	skb = ieee80211_beacon_get(priv->hw, priv->vif);
+	if (WARN_ON(!skb))
+		return -ENOMEM;
+
+	offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+	ssidie = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
+				  skb->len - offset);
+
+	memset(priv->ssid, 0, sizeof(priv->ssid));
+	if (ssidie) {
+		priv->ssid_length = ssidie[1];
+		if (WARN_ON(priv->ssid_length > sizeof(priv->ssid)))
+			priv->ssid_length = sizeof(priv->ssid);
+		memcpy(priv->ssid, &ssidie[2], priv->ssid_length);
+	} else {
+		priv->ssid_length = 0;
+	}
+	dev_kfree_skb(skb);
+#endif
+
+	priv->beacon_int = conf->beacon_int;
+	priv->join_dtim_period = conf->dtim_period;
+
+	start.ssidLength = priv->ssid_length;
+	memcpy(&start.ssid[0], priv->ssid, start.ssidLength);
+
+	memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
+
+	bes2600_info(BES2600_DBG_AP, "[AP] ch: %d(%d), bcn: %d(%d), "
+		"brt: 0x%.8X, ssid: %.*s.\n",
+		start.channelNumber, start.band,
+		start.beaconInterval, start.DTIMPeriod,
+		start.basicRateSet,
+		start.ssidLength, start.ssid);
+	ret = WARN_ON(wsm_start(hw_priv, &start, priv->if_id));
+	if (!ret)
+		ret = wsm_switch_channel(hw_priv, &channel, priv->if_id);
+
+	if (!ret && priv->vif->p2p) {
+		bes2600_dbg(BES2600_DBG_AP, "[AP] Setting p2p powersave "
+			"configuration.\n");
+		WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv,
+			&priv->p2p_ps_modeinfo, priv->if_id));
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+		bes2600_notify_noa(priv, BES2600_NOA_NOTIFICATION_DELAY);
+#endif
+	}
+
+	/*Set Inactivity time*/
+	if (!(strstr(&start.ssid[0], "6.1.12"))) {
+		wsm_set_inactivity(hw_priv, &inactivity, priv->if_id);
+	}
+	if (!ret) {
+#ifndef AP_AGGREGATE_FW_FIX
+		WARN_ON(wsm_set_block_ack_policy(hw_priv,
+			BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
+			BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID, priv->if_id));
+#else
+		if ((priv->if_id ==2) && !hw_priv->is_go_thru_go_neg)
+			WARN_ON(wsm_set_block_ack_policy(hw_priv,
+				BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
+				BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
+				priv->if_id));
+		else
+		WARN_ON(wsm_set_block_ack_policy(hw_priv,
+			BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
+			BES2600_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
+			priv->if_id));
+#endif
+		priv->join_status = BES2600_JOIN_STATUS_AP;
+		bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_AP);
+		/* bes2600_update_filtering(priv); */
+	}
+	hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
+	hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
+	bes2600_info(BES2600_DBG_AP, "AP/GO mode BG THROTTLE %d\n", hw_priv->vif0_throttle);
+	return ret;
+}
+
+static int bes2600_update_beaconing(struct bes2600_vif *priv)
+{
+	struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct wsm_reset reset = {
+		.link_id = 0,
+		.reset_statistics = true,
+	};
+
+	if (priv->mode == NL80211_IFTYPE_AP) {
+		/* TODO: check if changed channel, band */
+		if (priv->join_status != BES2600_JOIN_STATUS_AP ||
+		    priv->beacon_int != conf->beacon_int) {
+			bes2600_dbg(BES2600_DBG_AP, "ap restarting\n");
+			wsm_lock_tx(hw_priv);
+			if (priv->join_status != BES2600_JOIN_STATUS_PASSIVE)
+				WARN_ON(wsm_reset(hw_priv, &reset,
+						  priv->if_id));
+			priv->join_status = BES2600_JOIN_STATUS_PASSIVE;
+			WARN_ON(bes2600_start_ap(priv));
+			wsm_unlock_tx(hw_priv);
+		} else
+			bes2600_dbg(BES2600_DBG_AP, "ap started join_status: %d\n",
+				  priv->join_status);
+	}
+	return 0;
+}
+
+int bes2600_find_link_id(struct bes2600_vif *priv, const u8 *mac)
+{
+	int i, ret = 0;
+	spin_lock_bh(&priv->ps_state_lock);
+
+	for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE; ++i) {
+		if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) &&
+				priv->link_id_db[i].status) {
+			priv->link_id_db[i].timestamp = jiffies;
+			ret = i + 1;
+			break;
+		}
+	}
+	spin_unlock_bh(&priv->ps_state_lock);
+	return ret;
+}
+
+int bes2600_alloc_link_id(struct bes2600_vif *priv, const u8 *mac)
+{
+	int i, ret = 0;
+	unsigned long max_inactivity = 0;
+	unsigned long now = jiffies;
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	spin_lock_bh(&priv->ps_state_lock);
+	for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE; ++i) {
+		if (!priv->link_id_db[i].status) {
+			ret = i + 1;
+			break;
+		} else if (priv->link_id_db[i].status != BES2600_LINK_HARD &&
+			!hw_priv->tx_queue_stats.link_map_cache[i + 1]) {
+
+			unsigned long inactivity =
+					now - priv->link_id_db[i].timestamp;
+			if (inactivity < max_inactivity)
+				continue;
+			max_inactivity = inactivity;
+			ret = i + 1;
+		}
+	}
+	if (ret) {
+		struct bes2600_link_entry *entry = &priv->link_id_db[ret - 1];
+		bes2600_dbg(BES2600_DBG_AP, "[AP] STA added, link_id: %d\n",
+			ret);
+		entry->status = BES2600_LINK_RESERVE;
+		memcpy(&entry->mac, mac, ETH_ALEN);
+		memset(&entry->buffered, 0, BES2600_MAX_TID);
+		skb_queue_head_init(&entry->rx_queue);
+		wsm_lock_tx_async(hw_priv);
+		if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0)
+			wsm_unlock_tx(hw_priv);
+	} else {
+		wiphy_info(priv->hw->wiphy,
+			"[AP] Early: no more link IDs available.\n");
+	}
+
+	spin_unlock_bh(&priv->ps_state_lock);
+	return ret;
+}
+
+void bes2600_link_id_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, link_id_work);
+	struct bes2600_common *hw_priv = priv->hw_priv;
+
+	wsm_flush_tx(hw_priv);
+	bes2600_link_id_gc_work(&priv->link_id_gc_work.work);
+	wsm_unlock_tx(hw_priv);
+}
+
+void bes2600_link_id_gc_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, link_id_gc_work.work);
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct wsm_map_link map_link = {
+		.link_id = 0,
+	};
+	unsigned long now = jiffies;
+	unsigned long next_gc = -1;
+	long ttl;
+	bool need_reset;
+	u32 mask;
+	int i;
+
+	if (priv->join_status != BES2600_JOIN_STATUS_AP)
+		return;
+
+	wsm_lock_tx(hw_priv);
+	spin_lock_bh(&priv->ps_state_lock);
+	for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE; ++i) {
+		need_reset = false;
+		mask = BIT(i + 1);
+		if (priv->link_id_db[i].status == BES2600_LINK_RESERVE ||
+			(priv->link_id_db[i].status == BES2600_LINK_HARD &&
+			 !(priv->link_id_map & mask))) {
+			if (priv->link_id_map & mask) {
+				priv->sta_asleep_mask &= ~mask;
+				priv->pspoll_mask &= ~mask;
+				need_reset = true;
+			}
+			priv->link_id_map |= mask;
+			if (priv->link_id_db[i].status != BES2600_LINK_HARD)
+				priv->link_id_db[i].status = BES2600_LINK_SOFT;
+			memcpy(map_link.mac_addr, priv->link_id_db[i].mac,
+					ETH_ALEN);
+			spin_unlock_bh(&priv->ps_state_lock);
+			if (need_reset) {
+				WARN_ON(cw12xx_unmap_link(priv, i + 1));
+			}
+			map_link.link_id = i + 1;
+			WARN_ON(wsm_map_link(hw_priv, &map_link, priv->if_id));
+			next_gc = min(next_gc, BES2600_LINK_ID_GC_TIMEOUT);
+			spin_lock_bh(&priv->ps_state_lock);
+		} else if (priv->link_id_db[i].status == BES2600_LINK_SOFT) {
+			ttl = priv->link_id_db[i].timestamp - now +
+					BES2600_LINK_ID_GC_TIMEOUT;
+			if (ttl <= 0) {
+				need_reset = true;
+				priv->link_id_db[i].status = BES2600_LINK_OFF;
+				priv->link_id_map &= ~mask;
+				priv->sta_asleep_mask &= ~mask;
+				priv->pspoll_mask &= ~mask;
+				memset(map_link.mac_addr, 0, ETH_ALEN);
+				spin_unlock_bh(&priv->ps_state_lock);
+				WARN_ON(cw12xx_unmap_link(priv, i + 1));
+				spin_lock_bh(&priv->ps_state_lock);
+			} else {
+				next_gc = min_t(unsigned long, next_gc, ttl);
+			}
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+		} else if (priv->link_id_db[i].status == BES2600_LINK_RESET ||
+				priv->link_id_db[i].status ==
+				BES2600_LINK_RESET_REMAP) {
+			int status = priv->link_id_db[i].status;
+			priv->link_id_db[i].status = BES2600_LINK_OFF;
+			priv->link_id_db[i].timestamp = now;
+			spin_unlock_bh(&priv->ps_state_lock);
+			WARN_ON(cw12xx_unmap_link(priv, i + 1));
+			if (status == BES2600_LINK_RESET_REMAP) {
+				memcpy(map_link.mac_addr,
+						priv->link_id_db[i].mac,
+						ETH_ALEN);
+				map_link.link_id = i + 1;
+				WARN_ON(wsm_map_link(hw_priv, &map_link,
+						     priv->if_id));
+				next_gc = min(next_gc,
+					      BES2600_LINK_ID_GC_TIMEOUT);
+				priv->link_id_db[i].status =
+						priv->link_id_db[i].prev_status;
+			}
+			spin_lock_bh(&priv->ps_state_lock);
+#endif
+		}
+		if (need_reset) {
+			skb_queue_purge(&priv->link_id_db[i].rx_queue);
+			bes2600_dbg(BES2600_DBG_AP, "[AP] STA removed, link_id: %d\n",
+					i + 1);
+		}
+	}
+	spin_unlock_bh(&priv->ps_state_lock);
+	if (next_gc != -1)
+		queue_delayed_work(hw_priv->workqueue,
+				&priv->link_id_gc_work, next_gc);
+	wsm_unlock_tx(hw_priv);
+}
+
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+void bes2600_notify_noa(struct bes2600_vif *priv, int delay)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct cfg80211_p2p_ps p2p_ps = {0};
+	struct wsm_p2p_ps_modeinfo *modeinfo;
+	modeinfo = &priv->p2p_ps_modeinfo;
+
+	bes2600_dbg(BES2600_DBG_AP, "[AP]: %s called\n", __func__);
+
+	if (priv->join_status != BES2600_JOIN_STATUS_AP)
+		return;
+
+	if (delay)
+		msleep(delay);
+
+	if (!WARN_ON(wsm_get_p2p_ps_modeinfo(hw_priv, modeinfo))) {
+#if defined(CONFIG_BES2600_STA_DEBUG)
+		print_hex_dump_bytes("[AP] p2p_get_ps_modeinfo: ",
+				     DUMP_PREFIX_NONE,
+				     (u8 *)modeinfo,
+				     sizeof(*modeinfo));
+#endif /* CONFIG_BES2600_STA_DEBUG */
+		p2p_ps.opp_ps = !!(modeinfo->oppPsCTWindow & BIT(7));
+		p2p_ps.ctwindow = modeinfo->oppPsCTWindow & (~BIT(7));
+		p2p_ps.count = modeinfo->count;
+		p2p_ps.start = __le32_to_cpu(modeinfo->startTime);
+		p2p_ps.duration = __le32_to_cpu(modeinfo->duration);
+		p2p_ps.interval = __le32_to_cpu(modeinfo->interval);
+		p2p_ps.index = modeinfo->reserved;
+
+		ieee80211_p2p_noa_notify(priv->vif,
+					 &p2p_ps,
+					 GFP_KERNEL);
+	}
+}
+#endif
+
+int cw12xx_unmap_link(struct bes2600_vif *priv, int link_id)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	int ret = 0;
+
+	if (is_hardware_cw1250(hw_priv) || is_hardware_cw1260(hw_priv)) {
+		struct wsm_map_link maplink = {
+			.link_id = link_id,
+			.unmap = true,
+		};
+		if (link_id)
+			memcpy(&maplink.mac_addr[0],
+				priv->link_id_db[link_id - 1].mac, ETH_ALEN);
+		return wsm_map_link(hw_priv, &maplink, priv->if_id);
+	} else {
+		struct wsm_reset reset = {
+			.link_id = link_id,
+			.reset_statistics = true,
+		};
+		ret = wsm_reset(hw_priv, &reset, priv->if_id);
+		return ret;
+	}
+}
+#ifdef AP_HT_CAP_UPDATE
+void bes2600_ht_info_update_work(struct work_struct *work)
+{
+        struct sk_buff *skb;
+        struct ieee80211_mgmt *mgmt;
+        u8 *ht_info, *ies;
+        u32 ies_len;
+        struct bes2600_vif *priv =
+                container_of(work, struct bes2600_vif, ht_info_update_work);
+        struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+        struct wsm_update_ie update_ie = {
+                .what = WSM_UPDATE_IE_BEACON,
+                .count = 1,
+        };
+
+        skb = ieee80211_beacon_get(priv->hw, priv->vif);
+	if (WARN_ON(!skb))
+		return;
+
+        mgmt = (void *)skb->data;
+        ies = mgmt->u.beacon.variable;
+        ies_len = skb->len - (u32)(ies - (u8 *)mgmt);
+        ht_info= (u8 *)cfg80211_find_ie( WLAN_EID_HT_OPERATION, ies, ies_len);
+        if(ht_info && priv->ht_info == HT_INFO_MASK) {
+
+        ht_info[HT_INFO_OFFSET] |= 0x11;
+        update_ie.ies = ht_info;
+        update_ie.length = HT_INFO_IE_LEN;
+
+        WARN_ON(wsm_update_ie(hw_priv, &update_ie, priv->if_id));
+
+
+        }
+	dev_kfree_skb(skb);
+}
+#endif
+
diff --git a/drivers/staging/bes2600/ap.h b/drivers/staging/bes2600/ap.h
new file mode 100644
index 000000000000..f9d85d847397
--- /dev/null
+++ b/drivers/staging/bes2600/ap.h
@@ -0,0 +1,58 @@
+/*
+ * mac80211 STA and AP API for mac80211 BES2600 drivers
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/version.h>
+#ifndef AP_H_INCLUDED
+#define AP_H_INCLUDED
+
+#define BES2600_NOA_NOTIFICATION_DELAY 10
+
+int bes2600_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+		   bool set);
+int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		   struct ieee80211_sta *sta);
+int bes2600_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		      struct ieee80211_sta *sta);
+void bes2600_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+		       enum sta_notify_cmd notify_cmd,
+		       struct ieee80211_sta *sta);
+void bes2600_bss_info_changed(struct ieee80211_hw *dev,
+			     struct ieee80211_vif *vif,
+			     struct ieee80211_bss_conf *info,
+			     u32 changed);
+
+int bes2600_ampdu_action(struct ieee80211_hw *hw,
+			struct ieee80211_vif *vif,
+			struct ieee80211_ampdu_params *params);
+
+void bes2600_suspend_resume(struct bes2600_vif *priv,
+			  struct wsm_suspend_resume *arg);
+void bes2600_set_tim_work(struct work_struct *work);
+void bes2600_set_cts_work(struct work_struct *work);
+void bes2600_multicast_start_work(struct work_struct *work);
+void bes2600_multicast_stop_work(struct work_struct *work);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+void bes2600_mcast_timeout(struct timer_list *t);
+#else
+void bes2600_mcast_timeout(unsigned long arg);
+#endif
+int bes2600_find_link_id(struct bes2600_vif *priv, const u8 *mac);
+int bes2600_alloc_link_id(struct bes2600_vif *priv, const u8 *mac);
+void bes2600_link_id_work(struct work_struct *work);
+void bes2600_link_id_gc_work(struct work_struct *work);
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+void bes2600_notify_noa(struct bes2600_vif *priv, int delay);
+#endif
+int cw12xx_unmap_link(struct bes2600_vif *priv, int link_id);
+#ifdef AP_HT_CAP_UPDATE
+void bes2600_ht_info_update_work(struct work_struct *work);
+#endif
+
+#endif
diff --git a/drivers/staging/bes2600/bes2600.h b/drivers/staging/bes2600/bes2600.h
new file mode 100644
index 000000000000..ecfe154a0283
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600.h
@@ -0,0 +1,850 @@
+/*
+ * Common private data for BES2600 drivers
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * Based on the mac80211 Prism54 code, which is
+ * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
+ *
+ * Based on the islsm (softmac prism54) driver, which is:
+ * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES2600_H
+#define BES2600_H
+
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/atomic.h>
+#include <linux/usb.h>
+#include <net/mac80211.h>
+#ifdef P2P_MULTIVIF
+#define CW12XX_MAX_VIFS			(3)
+#else
+#define CW12XX_MAX_VIFS			(2)
+#endif
+#define CW12XX_GENERIC_IF_ID		(2)
+#define CW12XX_HOST_VIF0_11N_THROTTLE	(63)
+#define CW12XX_HOST_VIF1_11N_THROTTLE	(63)
+#define CW12XX_HOST_VIF0_11BG_THROTTLE	(15)
+#define CW12XX_HOST_VIF1_11BG_THROTTLE	(15)
+#if 0
+#define CW12XX_FW_VIF0_THROTTLE		(15)
+#define CW12XX_FW_VIF1_THROTTLE		(15)
+#endif
+#define CW12XX_MAX_QUEUE_SZ		(128)
+
+#define IEEE80211_FCTL_WEP      0x4000
+#define IEEE80211_QOS_DATAGRP   0x0080
+#define WSM_KEY_MAX_IDX		20
+
+#include "queue.h"
+#include "wsm.h"
+#include "scan.h"
+#include "txrx.h"
+#include "ht.h"
+#include "pm.h"
+#include "fwio.h"
+#include "bes2600_log.h"
+#include "bes_pwr.h"
+#include "tx_loop.h"
+#ifdef CONFIG_BES2600_TESTMODE
+#include "bes_nl80211_testmode_msg.h"
+#endif /*CONFIG_BES2600_TESTMODE*/
+
+
+/* extern */ struct sbus_ops;
+/* extern */ struct task_struct;
+/* extern */ struct bes2600_debug_priv;
+/* extern */ struct bes2600_debug_common;
+/* extern */ struct firmware;
+
+/* #define ROC_DEBUG */
+
+/* hidden ssid is only supported when separate probe resp IE
+   configuration is supported */
+#ifdef PROBE_RESP_EXTRA_IE
+#define HIDDEN_SSID	1
+#endif
+
+#if defined(CONFIG_BES2600_TXRX_DEBUG)
+#define txrx_printk(...) printk(__VA_ARGS__)
+#else
+#define txrx_printk(...)
+#endif
+
+#define BES2600_MAX_CTRL_FRAME_LEN	(0x1000)
+
+#define CW1250_MAX_STA_IN_AP_MODE	(14)
+#define WLAN_LINK_ID_MAX		(CW1250_MAX_STA_IN_AP_MODE + 3)
+
+#define BES2600_MAX_STA_IN_AP_MODE	(5)
+#define BES2600_MAX_REQUEUE_ATTEMPTS	(5)
+#define BES2600_LINK_ID_UNMAPPED		(15)
+
+#define BES2600_MAX_TID			(8)
+
+#define BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID         (0x3F)
+#define BES2600_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID         (0x3F)
+#define BES2600_RX_BLOCK_ACK_ENABLED_FOR_BE_TID \
+	(BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID & 0x01)
+#define BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID	(0)
+#define BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID	(0)
+
+#define BES2600_BLOCK_ACK_CNT		(30)
+#define BES2600_BLOCK_ACK_THLD		(800)
+#define BES2600_BLOCK_ACK_HIST		(3)
+#define BES2600_BLOCK_ACK_INTERVAL	(1 * HZ / BES2600_BLOCK_ACK_HIST)
+#define CW12XX_ALL_IFS			(-1)
+#ifdef ROAM_OFFLOAD
+#define BES2600_SCAN_TYPE_ACTIVE 0x1000
+#define BES2600_SCAN_BAND_5G 0X2000
+#endif /*ROAM_OFFLOAD*/
+
+#define IEEE80211_FCTL_WEP      0x4000
+#define IEEE80211_QOS_DATAGRP   0x0080
+#ifdef CONFIG_BES2600_TESTMODE
+#define BES2600_SCAN_MEASUREMENT_PASSIVE (0)
+#define BES2600_SCAN_MEASUREMENT_ACTIVE  (1)
+#endif
+
+#ifdef MCAST_FWDING
+#define WSM_MAX_BUF		30
+#endif
+
+#ifdef BSS_LOSS_CHECK
+#define BSS_LOSS_CK_THR		1
+#define BSS_LOSS_CK_INV		2000
+#define BSS_LOSS_CFM_THR	1
+#define BSS_LOSS_CFM_INV	200
+#else
+#define BSS_LOSS_CFM_INV  	0
+#endif
+
+/* Please keep order */
+enum bes2600_join_status {
+	BES2600_JOIN_STATUS_PASSIVE = 0,
+	BES2600_JOIN_STATUS_MONITOR,
+	BES2600_JOIN_STATUS_STA,
+	BES2600_JOIN_STATUS_AP,
+};
+
+enum bes2600_link_status {
+	BES2600_LINK_OFF,
+	BES2600_LINK_RESERVE,
+	BES2600_LINK_SOFT,
+	BES2600_LINK_HARD,
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	BES2600_LINK_RESET,
+	BES2600_LINK_RESET_REMAP,
+#endif
+};
+
+enum bes2600_bss_loss_status {
+	BES2600_BSS_LOSS_NONE,
+	BES2600_BSS_LOSS_CHECKING,
+	BES2600_BSS_LOSS_CONFIRMING,
+	BES2600_BSS_LOSS_CONFIRMED,
+};
+
+struct bes2600_link_entry {
+	unsigned long			timestamp;
+	enum bes2600_link_status		status;
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	enum bes2600_link_status		prev_status;
+#endif
+	u8				mac[ETH_ALEN];
+	u8				buffered[BES2600_MAX_TID];
+	struct sk_buff_head		rx_queue;
+};
+
+#if defined(ROAM_OFFLOAD) || defined(CONFIG_BES2600_TESTMODE)
+struct bes2600_testframe {
+	u8 len;
+	u8 *data;
+};
+#endif
+#ifdef CONFIG_BES2600_TESTMODE
+struct advance_scan_elems {
+	u8 scanMode;
+	u16 duration;
+};
+/**
+ * bes2600_tsm_info - Keeps information about ongoing TSM collection
+ * @ac: Access category for which metrics to be collected
+ * @use_rx_roaming: Use received voice packets to compute roam delay
+ * @sta_associated: Set to 1 after association
+ * @sta_roamed: Set to 1 after successful roaming
+ * @roam_delay: Roam delay
+ * @rx_timestamp_vo: Timestamp of received voice packet
+ * @txconf_timestamp_vo: Timestamp of received tx confirmation for
+ * successfully transmitted VO packet
+ * @sum_pkt_q_delay: Sum of packet queue delay
+ * @sum_media_delay: Sum of media delay
+ *
+ */
+struct bes2600_tsm_info {
+	u8 ac;
+	u8 use_rx_roaming;
+	u8 sta_associated;
+	u8 sta_roamed;
+	u16 roam_delay;
+	u32 rx_timestamp_vo;
+	u32 txconf_timestamp_vo;
+	u32 sum_pkt_q_delay;
+	u32 sum_media_delay;
+};
+
+/**
+ * bes2600_start_stop_tsm - To start or stop collecting TSM metrics in
+ * bes2600 driver
+ * @start: To start or stop collecting TSM metrics
+ * @up: up for which metrics to be collected
+ * @packetization_delay: Packetization delay for this TID
+ *
+ */
+struct bes2600_start_stop_tsm {
+	u8 start;       /*1: To start, 0: To stop*/
+	u8 up;
+	u16 packetization_delay;
+};
+
+#endif /* CONFIG_BES2600_TESTMODE */
+
+/*
+ * tcp & udp alive
+ */
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+#define IP_KEEPALIVE_MAX_LEN	(256 + 8)
+#define AES_KEY_IV_LEN			(16 + 16)
+#define AES_KEY_LEN				(16)
+#define AES_IV_LEN				(16)
+#define NUM_IP_FRAMES			8
+#define TCP_PROTO				6
+#define UDP_PROTO 				17
+
+#define KLV_VENDOR_DEFAULT		0
+#define KLV_VENDOR_XM			1
+#define WEBSOCKET_HD_LEN		6
+
+#ifdef P2P_MULTIVIF
+#define NET_DEVICE_NUM (3)
+#else
+#define NET_DEVICE_NUM (2)
+#endif
+
+struct ip_header {
+	/* version / header length */
+	uint8_t _v_hl;
+	/* type of service */
+	uint8_t _tos;
+	/* total length */
+	uint16_t _len;
+	/* identification */
+	uint16_t _id;
+	/* fragment offset field */
+	uint16_t _offset;
+	/* time to live */
+	uint8_t _ttl;
+	/* protocol*/
+	uint8_t _proto;
+	/* checksum */
+	uint16_t _chksum;
+	/* source and destination IP addresses */
+	uint32_t src;
+	uint32_t dest;
+} ;
+
+struct tcp_header {
+	uint16_t src;
+	uint16_t dest;
+	uint32_t seqno;
+	uint32_t ackno;
+	uint16_t _hdrlen_rsvd_flags;
+	uint16_t wnd;
+	uint16_t chksum;
+	uint16_t urgp;
+};
+
+struct udp_header {
+	uint16_t src;
+	uint16_t dest;
+	uint16_t len;
+	uint16_t chksum;
+};
+
+struct ip_alive_info {
+	uint8_t idx_used;
+	uint8_t proto; /* 0 for udp and 1 for tcp; */
+	uint16_t src_port;
+	uint16_t dest_port;
+	uint32_t src_ip;
+	uint32_t dest_ip;
+	uint16_t len;
+	uint32_t next_seqno;
+	uint8_t payload[IP_KEEPALIVE_MAX_LEN];
+	uint8_t dest_mac[6];
+};
+
+struct ip_alive_cfg {
+	struct ip_header iphd;
+	struct tcp_header tcphd;
+	struct udp_header udphd;
+	struct ip_alive_info bd;
+	uint8_t aes_key[AES_KEY_LEN];
+	uint8_t aes_iv[AES_IV_LEN];
+	uint8_t klv_vendor; /* stands for different vendor's keep-alive resolution; */
+};
+#endif /* CONFIG_BES2600_KEEP_ALIVE */
+
+struct bes2600_common {
+	struct bes2600_debug_common	*debug;
+	struct bes2600_queue		tx_queue[4];
+	struct bes2600_queue_stats	tx_queue_stats;
+
+	struct ieee80211_hw		*hw;
+	struct mac_address		addresses[CW12XX_MAX_VIFS];
+
+	/*Will be a pointer to a list of VIFs - Dynamically allocated */
+	struct ieee80211_vif		*vif_list[CW12XX_MAX_VIFS];
+	atomic_t			num_vifs;
+	atomic_t			netdevice_start;
+	spinlock_t			vif_list_lock;
+	u32				if_id_slot;
+	struct device			*pdev;
+	struct workqueue_struct		*workqueue;
+
+	struct semaphore		conf_lock;
+
+	const struct sbus_ops		*sbus_ops;
+	struct sbus_priv		*sbus_priv;
+
+	/* HW/FW type (HIF_...) */
+	int				hw_type;
+	int				hw_revision;
+	int				fw_revision;
+
+	/* firmware/hardware info */
+	unsigned int tx_hdr_len;
+
+	/* Radio data */
+	int output_power;
+	int noise;
+
+	/* calibration, output power limit and rssi<->dBm conversation data */
+
+	/* BBP/MAC state */
+	const struct firmware		*sdd;
+	struct ieee80211_rate		*rates;
+	struct ieee80211_rate		*mcs_rates;
+	u8 mac_addr[ETH_ALEN];
+	/*TODO:COMBO: To be made per VIFF after mac80211 support */
+	struct ieee80211_channel	*channel;
+	int				channel_switch_in_progress;
+	wait_queue_head_t		channel_switch_done;
+	u8				long_frame_max_tx_count;
+	u8				short_frame_max_tx_count;
+	/* TODO:COMBO: According to Hong aggregation will happen per VIFF.
+	* Keeping in common structure for the time being. Will be moved to VIFF
+	* after the mechanism is clear */
+	u8				ba_tid_mask;
+	int				ba_acc; /*TODO: Same as above */
+	int				ba_cnt; /*TODO: Same as above */
+	int				ba_cnt_rx; /*TODO: Same as above */
+	int				ba_acc_rx; /*TODO: Same as above */
+	int				ba_hist; /*TODO: Same as above */
+	struct timer_list		ba_timer;/*TODO: Same as above */
+	spinlock_t			ba_lock; /*TODO: Same as above */
+	bool				ba_ena; /*TODO: Same as above */
+	struct work_struct              ba_work; /*TODO: Same as above */
+	bool				is_BT_Present;
+	bool				is_go_thru_go_neg;
+	u8				conf_listen_interval;
+
+	/* BH */
+	atomic_t			bh_rx;
+	atomic_t			bh_tx;
+	atomic_t			bh_term;
+	atomic_t			bh_suspend;
+
+	struct workqueue_struct         *bh_workqueue;
+	struct work_struct              bh_work;
+
+	int				bh_error;
+	wait_queue_head_t		bh_wq;
+	wait_queue_head_t		bh_evt_wq;
+	int				buf_id_tx;	/* byte */
+	int				buf_id_rx;	/* byte */
+	int				wsm_rx_seq[2];	/* idx 0: Normal tx/rx, idx 1: special cmd */
+	int				wsm_tx_seq[2];	/* idx 0: Normal tx/rx, idx 1: special cmd */
+	int				wsm_tx_pending[2];	/* idx 0: Normal tx/rx, idx 1: special cmd */
+	struct timer_list		mcu_mon_timer;
+	struct timer_list		lmac_mon_timer;
+	int				hw_bufs_used;
+	int				hw_bufs_used_vif[CW12XX_MAX_VIFS];
+	struct sk_buff			*skb_cache;
+	/* Keep bes2600 awake (WUP = 1) 1 second after each scan to avoid
+	 * FW issue with sleeping/waking up. */
+	atomic_t			recent_scan;
+
+	/* WSM */
+	struct wsm_caps			wsm_caps;
+	struct semaphore		wsm_cmd_sema;
+	struct wsm_buf			wsm_cmd_buf;
+	struct wsm_cmd			wsm_cmd;
+	wait_queue_head_t		wsm_cmd_wq;
+	wait_queue_head_t		wsm_startup_done;
+	struct wsm_cbc			wsm_cbc;
+	atomic_t			tx_lock;
+	u32				pending_frame_id;
+#ifdef CONFIG_BES2600_TESTMODE
+	/* Device Power Range */
+	struct wsm_tx_power_range       txPowerRange[2];
+	/* Advance Scan */
+	struct advance_scan_elems	advanceScanElems;
+	bool				enable_advance_scan;
+	struct delayed_work		advance_scan_timeout;
+#endif /* CONFIG_BES2600_TESTMODE */
+
+	/* WSM debug */
+	int				wsm_enable_wsm_dumps;
+	u32				wsm_dump_max_size;
+
+	/* Scan status */
+	struct bes2600_scan scan;
+
+	/* TX/RX */
+	unsigned long		rx_timestamp;
+
+	/* Scan Timestamp */
+	unsigned long		scan_timestamp;
+
+	/* WSM events */
+	spinlock_t		event_queue_lock;
+	struct list_head	event_queue;
+	struct work_struct	event_handler;
+
+	/* TX rate policy cache */
+	struct tx_policy_cache tx_policy_cache;
+	struct work_struct tx_policy_upload_work;
+
+	/* cryptographic engine information */
+
+	/* bit field of glowing LEDs */
+	u16 softled_state;
+
+	/* statistics */
+	struct ieee80211_low_level_stats stats;
+
+	struct bes2600_ht_info		ht_info;
+	int				tx_burst_idx;
+
+	struct ieee80211_iface_limit		if_limits1[2];
+	struct ieee80211_iface_limit		if_limits2[2];
+	struct ieee80211_iface_limit		if_limits3[2];
+	struct ieee80211_iface_combination	if_combs[3];
+
+	struct semaphore		wsm_oper_lock;
+	struct delayed_work		rem_chan_timeout;
+	MIB_TXRX_OPT_PARAM		txrx_opt_param;
+	u32 					rtsvalue;
+	spinlock_t				rtsvalue_lock;
+	struct timer_list		txrx_opt_timer;
+	struct work_struct		dynamic_opt_txrx_work;
+	atomic_t			remain_on_channel;
+	int				roc_if_id;
+	u64				roc_cookie;
+	wait_queue_head_t		offchannel_wq;
+	u16				offchannel_done;
+	u16				prev_channel;
+	int				if_id_selected;
+	u32				key_map;
+	struct wsm_add_key		keys[WSM_KEY_MAX_INDEX + 1];
+#ifdef MCAST_FWDING
+	struct wsm_buf		wsm_release_buf[WSM_MAX_BUF];
+	u8			buf_released;
+#endif
+#ifdef ROAM_OFFLOAD
+	u8				auto_scanning;
+	u8				frame_rcvd;
+	u8				num_scanchannels;
+	u8				num_2g_channels;
+	u8				num_5g_channels;
+	struct wsm_scan_ch		scan_channels[48];
+	struct sk_buff 			*beacon;
+	struct sk_buff 			*beacon_bkp;
+	struct bes2600_testframe 	testframe;
+#endif /*ROAM_OFFLOAD*/
+#ifdef CONFIG_BES2600_TESTMODE
+	struct bes2600_testframe test_frame;
+	struct bes_tsm_stats		tsm_stats;
+	struct bes2600_tsm_info		tsm_info;
+	spinlock_t			tsm_lock;
+	struct bes2600_start_stop_tsm	start_stop_tsm;
+#endif /* CONFIG_BES2600_TESTMODE */
+	u8      connected_sta_cnt;
+	u16     vif0_throttle;
+	u16     vif1_throttle;
+	int     scan_switch_if_id;
+#ifdef CONFIG_BES2600_WAPI_SUPPORT
+	int     last_ins_wapi_usk_id;
+	int     last_del_wapi_usk_id;
+#endif
+#ifdef CONFIG_BES2600_TESTMODE
+	struct semaphore        vendor_rf_cmd_replay_sema;
+#endif
+
+	/* member for coexistence */
+	struct work_struct coex_work;
+	struct list_head coex_event_list;
+	spinlock_t coex_event_lock;
+
+	/* member for low power */
+	struct bes2600_pwr_t bes_power;
+
+	/* member for tx loop */
+	struct bes2600_tx_loop tx_loop;
+
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+	struct ip_alive_cfg iac[NUM_IP_FRAMES];
+#endif
+};
+
+/* Virtual Interface State. One copy per VIF */
+struct bes2600_vif {
+	atomic_t			enabled;
+	spinlock_t			vif_lock;
+	int				if_id;
+	/*TODO: Split into Common and VIF parts */
+	struct bes2600_debug_priv	*debug;
+	/* BBP/MAC state */
+	u8 bssid[ETH_ALEN];
+	struct wsm_edca_params		edca;
+	struct wsm_tx_queue_params	tx_queue_params;
+	struct wsm_association_mode	association_mode;
+	struct wsm_set_bss_params	bss_params;
+	struct wsm_set_pm		powersave_mode;
+	struct wsm_set_pm		firmware_ps_mode;
+	int				power_set_true;
+	int				user_power_set_true;
+	u8				user_pm_mode;
+	int				cqm_rssi_thold;
+	unsigned			cqm_rssi_hyst;
+	unsigned			cqm_tx_failure_thold;
+	unsigned			cqm_tx_failure_count;
+	unsigned			cmq_tx_success_count;
+	bool				cqm_use_rssi;
+	int				cqm_link_loss_count;
+	int				cqm_beacon_loss_count;
+	int				mode;
+	bool				enable_beacon;
+	int				beacon_int;
+	size_t				ssid_length;
+	u8				ssid[IEEE80211_MAX_SSID_LEN];
+#ifdef HIDDEN_SSID
+	bool				hidden_ssid;
+#endif
+	bool				listening;
+	struct wsm_rx_filter		rx_filter;
+	struct wsm_beacon_filter_table	bf_table;
+	struct wsm_beacon_filter_control bf_control;
+	struct wsm_multicast_filter	multicast_filter;
+	bool				has_multicast_subscription;
+	struct wsm_broadcast_addr_filter	broadcast_filter;
+	bool				disable_beacon_filter;
+	struct wsm_arp_ipv4_filter      filter4;
+#ifdef IPV6_FILTERING
+	struct wsm_ndp_ipv6_filter 	filter6;
+#endif /*IPV6_FILTERING*/
+	struct work_struct		update_filtering_work;
+	struct work_struct		set_beacon_wakeup_period_work;
+	struct bes2600_pm_state_vif	pm_state_vif;
+	/*TODO: Add support in mac80211 for psmode info per VIF */
+	struct wsm_p2p_ps_modeinfo	p2p_ps_modeinfo;
+	struct wsm_uapsd_info		uapsd_info;
+	bool				setbssparams_done;
+	u32				listen_interval;
+	u32				erp_info;
+	bool				powersave_enabled;
+
+	/* WSM Join */
+	enum bes2600_join_status	join_status;
+	u8			join_bssid[ETH_ALEN];
+	struct work_struct	join_work;
+	struct delayed_work	join_timeout;
+	struct work_struct	unjoin_work;
+	struct work_struct	offchannel_work;
+	int			join_dtim_period;
+	bool			delayed_unjoin;
+	atomic_t		connect_in_process;
+
+	/* Security */
+	s8			wep_default_key_id;
+	struct work_struct	wep_key_work;
+        unsigned long           rx_timestamp;
+        u32                     cipherType;
+
+
+	/* AP powersave */
+	u32			link_id_map;
+	u32			max_sta_ap_mode;
+	u32			link_id_after_dtim;
+	u32			link_id_uapsd;
+	u32			link_id_max;
+	u32			wsm_key_max_idx;
+	struct bes2600_link_entry link_id_db[CW1250_MAX_STA_IN_AP_MODE];
+	struct work_struct	link_id_work;
+	struct delayed_work	link_id_gc_work;
+	u32			sta_asleep_mask;
+	u32			pspoll_mask;
+	bool			aid0_bit_set;
+	spinlock_t		ps_state_lock;
+	bool			buffered_multicasts;
+	bool			tx_multicast;
+	struct work_struct	set_tim_work;
+	struct delayed_work	set_cts_work;
+	struct work_struct	multicast_start_work;
+	struct work_struct	multicast_stop_work;
+	struct timer_list	mcast_timeout;
+
+	/* CQM Implementation */
+	struct delayed_work	bss_loss_work;
+	struct delayed_work	connection_loss_work;
+	struct work_struct	tx_failure_work;
+	int			delayed_link_loss;
+	spinlock_t		bss_loss_lock;
+	int			bss_loss_status;
+	int			bss_loss_confirm_id;
+
+	struct ieee80211_vif	*vif;
+	struct bes2600_common	*hw_priv;
+	struct ieee80211_hw	*hw;
+
+	/* ROC implementation */
+	struct delayed_work		pending_offchanneltx_work;
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	/* Workaround for WFD testcase 6.1.10*/
+	struct work_struct	linkid_reset_work;
+	u8			action_frame_sa[ETH_ALEN];
+	u8			action_linkid;
+#endif
+	bool			htcap;
+#ifdef  AP_HT_CAP_UPDATE
+        u16                     ht_info;
+        struct work_struct      ht_info_update_work;
+#endif
+	bool pmf;
+
+    u32 hw_value;
+    /* dot11CountersTable */
+    u32 dot11TransmittedFragmentCount;
+    u32 dot11MulticastTransmittedFrameCount;
+    u32 dot11FailedCount;
+    u32 dot11RetryCount;
+    u32 dot11MultipleRetryCount;
+    u32 dot11FrameDuplicateCount;
+    u32 dot11ReceivedFragmentCount;
+    u32 dot11RxReorderLeakCount;
+    u32 dot11ReceivedBytes;
+    u32 dot11ReceivedDataBytes;
+    u32 dot11MulticastReceivedFrameCount;
+    u32 dot11TransmittedFrameCount;
+    u32 dot11TransmittedBytes;
+    u32 dot11TransmittedDataBytes;
+    u32 dot11Txbps;
+    u32 dot11Rxbps;
+};
+struct bes2600_sta_priv {
+	int link_id;
+	struct bes2600_vif *priv;
+};
+enum bes2600_data_filterid {
+	IPV4ADDR_FILTER_ID = 0,
+#ifdef IPV6_FILTERING
+	IPV6ADDR_FILTER_ID,
+#endif /*IPV6_FILTERING*/
+};
+
+static inline
+struct bes2600_common *cw12xx_vifpriv_to_hwpriv(struct bes2600_vif *priv)
+{
+	return priv->hw_priv;
+}
+
+
+static inline
+struct bes2600_vif *cw12xx_get_vif_from_ieee80211(struct ieee80211_vif *vif)
+{
+	return  (struct bes2600_vif *)vif->drv_priv;
+}
+
+static inline
+struct bes2600_vif *cw12xx_hwpriv_to_vifpriv(struct bes2600_common *hw_priv,
+						int if_id)
+{
+	struct bes2600_vif *vif;
+
+	if (WARN_ON((-1 == if_id) || (if_id > CW12XX_MAX_VIFS)))
+		return NULL;
+	/* TODO:COMBO: During scanning frames can be received
+	 * on interface ID 3 */
+	spin_lock(&hw_priv->vif_list_lock);
+	if (!hw_priv->vif_list[if_id]) {
+		spin_unlock(&hw_priv->vif_list_lock);
+		return NULL;
+	}
+
+	vif = cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[if_id]);
+	WARN_ON(!vif);
+	if (vif)
+		spin_lock(&vif->vif_lock);
+	spin_unlock(&hw_priv->vif_list_lock);
+	return vif;
+}
+
+static inline
+struct bes2600_vif *__cw12xx_hwpriv_to_vifpriv(struct bes2600_common *hw_priv,
+					      int if_id)
+{
+	WARN_ON((-1 == if_id) || (if_id > CW12XX_MAX_VIFS));
+	/* TODO:COMBO: During scanning frames can be received
+	 * on interface ID 3 */
+	if (!hw_priv->vif_list[if_id]) {
+		return NULL;
+	}
+
+	return cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[if_id]);
+}
+
+static inline
+struct bes2600_vif *cw12xx_get_activevif(struct bes2600_common *hw_priv)
+{
+	return cw12xx_hwpriv_to_vifpriv(hw_priv, ffs(hw_priv->if_id_slot)-1);
+}
+
+static inline bool is_hardware_cw1250(struct bes2600_common *hw_priv)
+{
+	return (hw_priv->hw_revision == BES2600_HW_REV_CUT20);
+}
+
+static inline bool is_hardware_cw1260(struct bes2600_common *hw_priv)
+{
+	return (hw_priv->hw_revision == BES2600_HW_REV_CUT10);
+}
+
+static inline int cw12xx_get_nr_hw_ifaces(struct bes2600_common *hw_priv)
+{
+	switch(hw_priv->hw_revision) {
+		case BES2600_HW_REV_CUT10:
+		case BES2600_HW_REV_CUT11:
+		case BES2600_HW_REV_CUT20:
+		case BES2600_HW_REV_CUT22:
+			return 1;
+		case CW1250_HW_REV_CUT10:
+			return 3;
+		default:
+			return 1;
+	}
+}
+
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+/* IPV4 host addr info */
+struct ipv4_addr_info {
+	u8 filter_mode;
+	u8 address_mode;
+	u8 ipv4[4];
+};
+
+/* tcp keep alive test period */
+struct MIB_TCP_KEEP_ALIVE_PERIOD {
+	u16 TcpKeepAlivePeriod; /* in seconds */
+	u8  EncrType; /* (ex. WSM_KEY_TYPE_WEP_DEFAULT) */
+	u8  Reserved;
+};
+
+#ifdef VENDOR_XM_KEEPALIVE
+struct ip_alive_satus {
+	bool udp;
+	bool tcp;
+};
+
+void bes2600_get_keepalive_info(struct bes2600_common *hw_priv, struct ip_alive_satus *status);
+#endif
+
+int bes2600_set_ip_offload(struct bes2600_common *hw_priv,
+					    struct bes2600_vif *priv,
+					    struct ip_alive_cfg *tac,
+					    u16 idx);
+
+int bes2600_del_ip_offload(struct bes2600_common *hw_priv,
+					    struct bes2600_vif *priv,
+					    u8 stream_idx);
+
+int bes2600_en_ip_offload(struct bes2600_common *hw_priv,
+					    struct bes2600_vif *priv,
+					    u16 period_in_s);
+int bes2600_set_ipv4addrfilter(struct bes2600_common *hw_priv, u8 *data, int if_id);
+#endif /* CONFIG_BES2600_KEEP_ALIVE */
+
+#ifdef IPV6_FILTERING
+/* IPV6 host addr info */
+struct ipv6_addr_info {
+	u8 filter_mode;
+	u8 address_mode;
+	u16 ipv6[8];
+};
+#endif /*IPV6_FILTERING*/
+
+/* interfaces for the drivers */
+int bes2600_core_probe(const struct sbus_ops *sbus_ops,
+		      struct sbus_priv *sbus,
+		      struct device *pdev,
+		      struct bes2600_common **pself);
+void bes2600_core_release(struct bes2600_common *self);
+
+static inline void bes2600_tx_queues_lock(struct bes2600_common *hw_priv)
+{
+	int i;
+	for (i = 0; i < 4; ++i)
+		bes2600_queue_lock(&hw_priv->tx_queue[i]);
+}
+
+static inline void bes2600_tx_queues_unlock(struct bes2600_common *hw_priv)
+{
+	int i;
+	for (i = 0; i < 4; ++i)
+		bes2600_queue_unlock(&hw_priv->tx_queue[i]);
+}
+
+/* Datastructure for LLC-SNAP HDR */
+#define P80211_OUI_LEN  3
+
+struct ieee80211_snap_hdr {
+        u8    dsap;   /* always 0xAA */
+        u8    ssap;   /* always 0xAA */
+        u8    ctrl;   /* always 0x03 */
+        u8    oui[P80211_OUI_LEN];    /* organizational universal id */
+} __packed;
+
+#define bes2600_for_each_vif(_hw_priv, _priv, _i)			\
+for (									\
+	_i = 0;								\
+	(_i < CW12XX_MAX_VIFS) && \
+	(_priv = hw_priv->vif_list[_i] ? 				\
+	cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[_i]) : NULL);	\
+	_i++								\
+)
+
+#ifdef CONFIG_BES2600_BT
+int bes2600_btusb_setup_pipes(struct sbus_priv *sbus_priv);
+void bes2600_btusb_uninit(struct usb_interface *interface);
+#endif
+
+#endif /* BES2600_H */
diff --git a/drivers/staging/bes2600/bes2600_btusb.c b/drivers/staging/bes2600/bes2600_btusb.c
new file mode 100644
index 000000000000..621065e2a18d
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_btusb.c
@@ -0,0 +1,633 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <linux/usb.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include "bes2600.h"
+
+#define BES2600_BTUSB_MAX_BULK_TX	2
+#define BES2600_BTUSB_MAX_BULK_RX	2
+
+enum {
+    CQ_OK = 0,
+    CQ_ERR,
+};
+
+typedef unsigned char CQItemType;
+
+typedef struct __CQueue
+{
+    int read;
+    int write;
+    int size;
+    int len;
+    CQItemType *base;
+}CQueue;
+struct bes2600_btusb_data {
+	struct usb_device	*udev;
+	struct bes2600_usb_pipe* rx_pipes;
+	struct bes2600_usb_pipe* tx_pipes;
+	struct hci_dev		*hdev;
+
+	unsigned long		state;
+
+	rwlock_t		lock;
+
+	struct sk_buff_head	transmit_q;
+	struct work_struct tx_work;
+
+	atomic_t		pending_tx;
+	struct sk_buff_head	pending_q;
+	CQueue bt_rx_queue;
+	unsigned char * rx_buffer;
+};
+
+struct bes2600_btusb_data_scb {
+	struct urb *urb;
+};
+
+static int bes2600_btusb_rx_submit(struct bes2600_btusb_data *data, struct urb *urb);
+
+int InitCQueue(CQueue *Q, unsigned int size, CQItemType *buf)
+{
+    Q->size = size;
+    Q->base = buf;
+    Q->len  = 0;
+    if (!Q->base)
+        return CQ_ERR;
+
+    Q->read = Q->write = 0;
+    return CQ_OK;
+}
+
+int IsEmptyCQueue(CQueue *Q)
+{
+    if (Q->len == 0)
+        return CQ_OK;
+    else
+        return CQ_ERR;
+}
+
+int LengthOfCQueue(CQueue *Q)
+{
+    return Q->len;
+}
+
+int AvailableOfCQueue(CQueue *Q)
+{
+    return (Q->size - Q->len);
+}
+
+int EnCQueue(CQueue *Q, CQItemType *e, unsigned int len)
+{
+    if (AvailableOfCQueue(Q) < len) {
+        return CQ_ERR;
+    }
+
+    Q->len += len;
+
+    uint32_t bytesToTheEnd = Q->size - Q->write;
+    if (bytesToTheEnd > len)
+    {
+        memcpy((uint8_t *)&Q->base[Q->write], (uint8_t *)e, len);
+        Q->write += len;
+    }
+    else
+    {
+        memcpy((uint8_t *)&Q->base[Q->write], (uint8_t *)e, bytesToTheEnd);
+        memcpy((uint8_t *)&Q->base[0], (((uint8_t *)e)+bytesToTheEnd), len-bytesToTheEnd);
+        Q->write = len-bytesToTheEnd;
+    }
+
+    return CQ_OK;
+}
+
+int PeekCQueue(CQueue *Q, unsigned int len_want, CQItemType **e1, unsigned int *len1, CQItemType **e2, unsigned int *len2)
+{
+    if(LengthOfCQueue(Q) < len_want) {
+        return CQ_ERR;
+    }
+
+    *e1 = &(Q->base[Q->read]);
+    if((Q->write > Q->read) || (Q->size - Q->read >= len_want)) {
+        *len1 = len_want;
+        *e2   = NULL;
+        *len2 = 0;
+        return CQ_OK;
+    }
+    else {
+        *len1 = Q->size - Q->read;
+        *e2   = &(Q->base[0]);
+        *len2 = len_want - *len1;
+        return CQ_OK;
+    }
+
+    return CQ_ERR;
+}
+
+int PeekCQueueToBuf(CQueue *Q, CQItemType *e, unsigned int len)
+{
+    int status = CQ_OK;
+    unsigned char *e1 = NULL, *e2 = NULL;
+    unsigned int len1 = 0, len2 = 0;
+
+    status = PeekCQueue(Q, len, &e1, &len1, &e2, &len2);
+
+    if(status == CQ_OK) {
+        if (len == (len1 + len2)) {
+            memcpy(e, e1, len1);
+            memcpy(e + len1, e2, len2);
+        } else {
+            status = CQ_ERR;
+        }
+    }
+
+    return status;
+}
+
+int DeCQueue(CQueue *Q, CQItemType *e, unsigned int len)
+{
+    if(LengthOfCQueue(Q) < len)
+        return CQ_ERR;
+
+    Q->len -= len;
+
+    if(e != NULL)
+    {
+        uint32_t bytesToTheEnd = Q->size - Q->read;
+        if (bytesToTheEnd > len)
+        {
+            memcpy((uint8_t *)e, (uint8_t *)&Q->base[Q->read], len);
+            Q->read += len;
+        }
+        else
+        {
+            memcpy((uint8_t *)e, (uint8_t *)&Q->base[Q->read], bytesToTheEnd);
+            memcpy((((uint8_t *)e)+bytesToTheEnd), (uint8_t *)&Q->base[0], len-bytesToTheEnd);
+            Q->read = len-bytesToTheEnd;
+        }
+    }
+    else
+    {
+        if (0 < Q->size)
+        {
+            Q->read = (Q->read+len)%Q->size;
+        }
+        else
+        {
+            Q->read = 0;
+        }
+    }
+
+    return CQ_OK;
+}
+
+static int bes2600_btusb_recv_bulk(struct bes2600_btusb_data *data, void *buffer, int count)
+{
+	struct sk_buff *skb = NULL;
+	int err = 0;
+	unsigned char bt_rx_hdr[5];
+	unsigned short frame_len = 0;
+	unsigned char head_length = 0;
+	unsigned char * raw_data = NULL;
+	{
+		EnCQueue(&data->bt_rx_queue, (CQItemType*)buffer, count);
+
+		while(1){
+			frame_len = 0;
+			head_length = 0;
+			if(LengthOfCQueue(&data->bt_rx_queue) >= 4){
+				PeekCQueueToBuf(&data->bt_rx_queue, (CQItemType*)bt_rx_hdr, 4);
+				if(bt_rx_hdr[0] == HCI_EVENT_PKT){
+					head_length = 3;
+					frame_len = bt_rx_hdr[2] + head_length;
+				}else if(bt_rx_hdr[0] == HCI_ACLDATA_PKT){
+					head_length = 5;
+				if(LengthOfCQueue(&data->bt_rx_queue) >= head_length){
+					PeekCQueueToBuf(&data->bt_rx_queue, (CQItemType*)(bt_rx_hdr), head_length);
+				}else{
+					break;
+				}
+					frame_len = (bt_rx_hdr[3] | (bt_rx_hdr[4] << 8)) + head_length;
+				}else if(bt_rx_hdr[0] == HCI_SCODATA_PKT){
+					head_length = 4;
+					frame_len = bt_rx_hdr[3] + head_length;
+				}else{
+					bes2600_err(BES2600_DBG_BT, "Invalid packet type:%02x", bt_rx_hdr[0]);
+				}
+			}else{
+				break;
+			}
+
+			bes2600_dbg(BES2600_DBG_BT, "btrx flen:%d", frame_len);
+
+			if(LengthOfCQueue(&data->bt_rx_queue) >= frame_len){
+				skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+				if (!skb) {
+					err = -ENOMEM;
+					break;
+				}
+				hci_skb_pkt_type(skb) = bt_rx_hdr[0];
+				raw_data = skb_put(skb, frame_len);
+				PeekCQueueToBuf(&data->bt_rx_queue, (CQItemType*)raw_data, frame_len);
+				//jump packet type
+				skb_pull(skb, 1);
+                		DeCQueue(&data->bt_rx_queue, NULL, frame_len);
+				bes2600_dbg_dump(BES2600_DBG_BT, "btrx flen:", raw_data, 8);
+				/* Complete frame */
+				hci_recv_frame(data->hdev, skb);
+			}else{
+				break;
+			}
+		}
+	}
+
+	return err;
+}
+
+
+static void bes2600_btusb_rx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct bes2600_btusb_data *data = (struct bes2600_btusb_data *) skb->dev;
+	int count = urb->actual_length;
+	int err;
+
+	bes2600_dbg(BES2600_DBG_BT, "btusb_rx_complete %p urb %p skb %p len %d %d", data, urb, skb, skb->len, urb->actual_length);
+	read_lock(&data->lock);
+
+	if (!test_bit(HCI_RUNNING, &data->hdev->flags)){
+		bes2600_dbg(BES2600_DBG_BT, "rx_complete data->hdev->flags=%x", data->hdev->flags);
+		goto unlock;
+	}
+
+	if (urb->status || !count){
+		bes2600_dbg(BES2600_DBG_BT, "rx_complete urb->status:%d count:%d");
+		goto resubmit;
+	}
+
+	data->hdev->stat.byte_rx += count;
+
+	bes2600_btusb_recv_bulk(data, urb->transfer_buffer, urb->actual_length);
+
+	skb_unlink(skb, &data->pending_q);
+	kfree_skb(skb);
+
+	bes2600_btusb_rx_submit(data, urb);
+
+	read_unlock(&data->lock);
+
+	return;
+
+resubmit:
+	urb->dev = data->udev;
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		bes2600_err(BES2600_DBG_BT, "%s bulk resubmit failed urb %p err %d",
+					data->hdev->name, urb, err);
+	}
+
+unlock:
+	read_unlock(&data->lock);
+}
+
+
+static int bes2600_btusb_rx_submit(struct bes2600_btusb_data *data, struct urb *urb)
+{
+	struct bes2600_btusb_data_scb *scb;
+	struct sk_buff *skb;
+	struct bes2600_usb_pipe* recv_pipe = NULL;
+	int err, size = HCI_MAX_FRAME_SIZE + 32;
+
+	bes2600_dbg(BES2600_DBG_BT, "btusb_rx_submit %p urb %p", data, urb);
+
+	if (!urb) {
+		urb = usb_alloc_urb(0, GFP_ATOMIC);
+		if (!urb)
+			return -ENOMEM;
+	}
+
+	skb = bt_skb_alloc(size, GFP_ATOMIC);
+	if (!skb) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	skb->dev = (void *) data;
+
+	scb = (struct bes2600_btusb_data_scb *) skb->cb;
+	scb->urb = urb;
+
+	recv_pipe = data->rx_pipes;
+	usb_fill_bulk_urb(urb, data->udev, data->rx_pipes->usb_pipe_handle, skb->data, size,
+			bes2600_btusb_rx_complete, skb);
+
+	bes2600_dbg(BES2600_DBG_BT, "btusb_rx: bulk recv submit:%d, 0x%X (ep:0x%2.2X), %d bytes buf:0x%p\n",
+		   recv_pipe->logical_pipe_num,
+		   recv_pipe->usb_pipe_handle, recv_pipe->ep_address,
+		   size, skb);
+	skb_queue_tail(&data->pending_q, skb);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		bes2600_err(BES2600_DBG_BT, "%s bulk rx submit failed urb %p err %d",
+					data->hdev->name, urb, err);
+		skb_unlink(skb, &data->pending_q);
+		kfree_skb(skb);
+		usb_free_urb(urb);
+	}
+
+	return err;
+}
+
+static int bes2600_btusb_open(struct hci_dev *hdev)
+{
+	struct bes2600_btusb_data *data = hci_get_drvdata(hdev);
+	unsigned long flags;
+	int i, err;
+
+	write_lock_irqsave(&data->lock, flags);
+
+	err = bes2600_btusb_rx_submit(data, NULL);
+	if (!err) {
+		for (i = 1; i < BES2600_BTUSB_MAX_BULK_RX; i++)
+			bes2600_btusb_rx_submit(data, NULL);
+	}
+
+	write_unlock_irqrestore(&data->lock, flags);
+	return err;
+}
+
+static void bes2600_btusb_unlink_urbs(struct bes2600_btusb_data *data)
+{
+	struct sk_buff *skb;
+	struct urb *urb;
+
+	while ((skb = skb_dequeue(&data->pending_q))) {
+		urb = ((struct bes2600_btusb_data_scb *) skb->cb)->urb;
+		usb_kill_urb(urb);
+		kfree_skb(skb);
+	}
+}
+
+static int bes2600_btusb_flush(struct hci_dev *hdev)
+{
+	struct bes2600_btusb_data *data = hci_get_drvdata(hdev);
+
+	bes2600_dbg(BES2600_DBG_BT, "btusb_flush hdev %p btusb %p", hdev, data);
+	read_lock(&data->lock);
+	skb_queue_purge(&data->transmit_q);
+	read_unlock(&data->lock);
+
+	return 0;
+}
+
+static int bes2600_btusb_close(struct hci_dev *hdev)
+{
+	struct bes2600_btusb_data *data = hci_get_drvdata(hdev);
+	unsigned long flags;
+
+	bes2600_dbg(BES2600_DBG_BT, "btusb_close hdev %p btusb %p", hdev, data);
+	write_lock_irqsave(&data->lock, flags);
+	write_unlock_irqrestore(&data->lock, flags);
+
+	bes2600_btusb_unlink_urbs(data);
+	bes2600_btusb_flush(hdev);
+
+	return 0;
+}
+
+static int bes2600_btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	char *pkt_type = NULL;
+	struct bes2600_btusb_data *data = hci_get_drvdata(hdev);
+
+	bes2600_dbg(BES2600_DBG_BT, "btusb_send_frame hdev %p skb %p type %d len %d\n", hdev, skb,
+	       hci_skb_pkt_type(skb), skb->len);
+	bes2600_dbg_dump(BES2600_DBG_BT, "Frame Content:", skb->data, 9)
+
+	switch (hci_skb_pkt_type(skb)) {
+	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;
+	}
+	pkt_type = skb_push(skb, 1);
+	pkt_type[0] =  hci_skb_pkt_type(skb);
+	read_lock(&data->lock);
+	skb_queue_tail(&data->transmit_q, skb);
+	read_unlock(&data->lock);
+
+	schedule_work(&data->tx_work);
+	return 0;
+}
+
+static void  bes2600_btusb_notify(struct hci_dev *hdev, unsigned int evt)
+{
+	struct bes2600_btusb_data *data = hci_get_drvdata(hdev);
+
+	bes2600_dbg(BES2600_DBG_BT, "btusb_notify %s evt %d", hdev->name, evt);
+	if(evt == HCI_NOTIFY_CONN_ADD){
+
+	}else if(evt == HCI_NOTIFY_CONN_DEL){
+
+	}else if(evt == HCI_NOTIFY_VOICE_SETTING){
+
+	}
+}
+
+static inline int bes2600_btusb_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+	bes2600_dbg(BES2600_DBG_BT, "bes2600_btusb_set_bdaddr");
+	return -EOPNOTSUPP;
+}
+
+static void bes2600_btusb_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb = (struct sk_buff *) urb->context;
+	struct bes2600_btusb_data *data = (struct bes2600_btusb_data *) skb->dev;
+
+	bes2600_dbg(BES2600_DBG_BT, "btusb_tx_complete %p urb %p skb %p len %d", data, urb, skb, skb->len);
+	atomic_dec(&data->pending_tx);
+
+	if (!test_bit(HCI_RUNNING, &data->hdev->flags))
+		return;
+
+	if (!urb->status)
+		data->hdev->stat.byte_tx += skb->len;
+	else
+		data->hdev->stat.err_tx++;
+
+	read_lock(&data->lock);
+	skb_unlink(skb, &data->pending_q);
+	read_unlock(&data->lock);
+	kfree_skb(skb);
+
+	if(skb_queue_len(&data->transmit_q))
+		schedule_work(&data->tx_work);
+
+}
+
+static int bes2600_btusb_send_bulk(struct bes2600_btusb_data *data, struct sk_buff *skb)
+{
+	struct bes2600_btusb_data_scb *scb = (void *) skb->cb;
+	struct urb *urb = NULL;
+	int err;
+	struct bes2600_usb_pipe* tx_pipe = data->tx_pipes;
+
+	urb = usb_alloc_urb(0, GFP_ATOMIC);
+	if (!urb)
+		return -ENOMEM;
+
+	skb->dev = (void*)data;
+	usb_fill_bulk_urb(urb, data->udev, tx_pipe->usb_pipe_handle, skb->data, skb->len,
+			bes2600_btusb_tx_complete, skb);
+
+	bes2600_dbg(BES2600_DBG_BT, "btusb_tx: bulk tx submit:%d, 0x%X (ep:0x%2.2X), %d bytes buf:0x%p\n",
+		   tx_pipe->logical_pipe_num,
+		   tx_pipe->usb_pipe_handle, tx_pipe->ep_address,
+		   skb->len, skb);
+
+	scb->urb = urb;
+
+	skb_queue_tail(&data->pending_q, skb);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		bes2600_err(BES2600_DBG_BT, "%s bulk tx submit failed urb %p err %d",
+					data->hdev->name, urb, err);
+		skb_unlink(skb, &data->pending_q);
+	} else
+		atomic_inc(&data->pending_tx);
+
+	usb_free_urb(urb);
+	return err;
+}
+
+static void bes2600_btusb_work(struct work_struct *work)
+{
+	struct bes2600_btusb_data *data = container_of(work, struct bes2600_btusb_data, tx_work);
+	struct sk_buff *skb;
+	read_lock(&data->lock);
+	while ((atomic_read(&data->pending_tx) < BES2600_BTUSB_MAX_BULK_TX) &&
+			(skb = skb_dequeue(&data->transmit_q))) {
+		if (bes2600_btusb_send_bulk(data, skb) < 0) {
+			skb_queue_head(&data->transmit_q, skb);
+			break;
+		}
+	}
+	read_unlock(&data->lock);
+}
+
+int bes2600_btusb_setup_pipes(struct sbus_priv *ar_usb)
+{
+	struct hci_dev *hdev;
+	struct bes2600_btusb_data *data;
+
+	/* Initialize control structure */
+	data = kzalloc(sizeof(struct bes2600_btusb_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->rx_buffer = kzalloc(8*1024, GFP_KERNEL);
+	InitCQueue(&data->bt_rx_queue, 8*1024, data->rx_buffer);
+
+	rwlock_init(&data->lock);
+
+	skb_queue_head_init(&data->transmit_q);
+	skb_queue_head_init(&data->pending_q);
+
+	/* Initialize and register HCI device */
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		bes2600_err(BES2600_DBG_BT, "bes2600 Can't allocate HCI device");
+		goto done;
+	}
+
+	data->udev = ar_usb->udev;
+	data->rx_pipes = &ar_usb->pipes[BES2600_USB_PIPE_BT_RX_DATA];
+	data->tx_pipes = &ar_usb->pipes[BES2600_USB_PIPE_BT_TX_DATA];
+	data->hdev = hdev;
+	INIT_WORK(&data->tx_work, bes2600_btusb_work);
+
+	hdev->bus = HCI_USB;
+	hdev->dev_type = HCI_PRIMARY;
+	hci_set_drvdata(hdev, data);
+	SET_HCIDEV_DEV(hdev, &ar_usb->interface->dev);
+	hdev->open	= bes2600_btusb_open;
+	hdev->close = bes2600_btusb_close;
+	hdev->flush = bes2600_btusb_flush;
+	hdev->send	= bes2600_btusb_send_frame;
+	hdev->notify = bes2600_btusb_notify;
+	hdev->set_bdaddr = bes2600_btusb_set_bdaddr;
+
+	//set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
+	set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
+
+	if (hci_register_dev(hdev) < 0) {
+		bes2600_err(BES2600_DBG_BT, "Can't register HCI device");
+		hci_free_dev(hdev);
+		goto done;
+	}
+	bes2600_info(BES2600_DBG_BT, "register HCI device ok:%p %p",hdev, data);
+	ar_usb->btdev = data;
+	return 0;
+
+done:
+	return -EIO;
+}
+
+void bes2600_btusb_uninit(struct usb_interface *interface)
+{
+	struct bes2600_btusb_data *data = NULL;
+	struct sbus_priv *ar_usb = NULL;
+	struct hci_dev *hdev = NULL;
+
+	ar_usb = usb_get_intfdata(interface);
+	if (ar_usb == NULL)
+		return;
+
+	data = (struct bes2600_btusb_data*)ar_usb->btdev;
+	if(!data)
+		return;
+
+	hdev = data->hdev;
+	cancel_work_sync(&data->tx_work);
+	if (!hdev)
+		return;
+
+	bes2600_btusb_close(hdev);
+	kfree(data->rx_buffer);
+	kfree(data);
+	hci_unregister_dev(hdev);
+	hci_free_dev(hdev);
+}
+
+
diff --git a/drivers/staging/bes2600/bes2600_cfgvendor.c b/drivers/staging/bes2600/bes2600_cfgvendor.c
new file mode 100644
index 000000000000..220f86018a46
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_cfgvendor.c
@@ -0,0 +1,1063 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+#include <net/cfg80211.h>
+#include <linux/ctype.h>
+#include <linux/rtnetlink.h>
+#include <net/netlink.h>
+#include <linux/netlink.h>
+#include "bes2600_cfgvendor.h"
+
+void bes2600_reg_notifier(struct wiphy *wiphy,
+                              struct regulatory_request *request)
+{
+	const struct ieee80211_regdomain *tmp = NULL;
+
+	/* If wiphy->regd is not cleared, reopening the SoftAP after the sta disconnects from an AP will fail */
+	if(request->initiator == NL80211_REGDOM_SET_BY_CORE &&
+	   !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)) {
+		tmp = rtnl_dereference(wiphy->regd);
+		if(tmp) {
+			rcu_assign_pointer(wiphy->regd, NULL);
+			kfree_rcu((struct ieee80211_regdomain *)tmp, rcu_head);
+			bes2600_info(BES2600_DBG_ANDROID, "clear regdom when sta disconnects from an ap.\n");
+		}
+	}
+}
+
+static int bes2600_set_country_code(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+#define CNTRY_BUF_SZ	4	/* Country string is 3 bytes + NUL */
+	int rem, type;
+	char country_code[CNTRY_BUF_SZ] = {0};
+	const struct nlattr *iter;
+
+	nla_for_each_attr(iter, data, len, rem) {
+		type = nla_type(iter);
+		switch (type) {
+			case ANDR_WIFI_ATTRIBUTE_COUNTRY:
+				memcpy(country_code, nla_data(iter),
+					MIN(nla_len(iter), CNTRY_BUF_SZ));
+				break;
+			default:
+				return -EINVAL;
+		}
+	}
+
+	/* check whether the country is valid or not */
+	if(!isalpha(country_code[0]) ||
+	   !isalpha(country_code[1])) {
+		return -EINVAL;
+	}
+
+	/* notify cfg80211 to update database */
+	return regulatory_hint(wiphy, country_code);
+}
+
+#if 0
+static int bes2600_cfgvendor_gscan_get_capabilities(struct wiphy *wiphy,
+		struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+	dhd_pno_gscan_capabilities_t *reply = NULL;
+	unsigned int reply_len = 0;
+
+    // TODO:do something
+
+	if (unlikely(err))
+	// TODO:do something
+
+	kfree(reply);
+	return err;
+}
+
+
+static int bes2600_cfgvendor_set_scan_cfg(struct wiphy *wiphy,
+		     struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+	// TODO:do something
+
+}
+
+
+static int bes2600_cfgvendor_set_batch_scan_cfg(struct wiphy *wiphy,
+		struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0, tmp, type;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+	gscan_batch_params_t batch_param;
+	const struct nlattr *iter;
+
+	batch_param.mscan = batch_param.bestn = 0;
+	batch_param.buffer_threshold = GSCAN_BATCH_NO_THR_SET;
+
+	nla_for_each_attr(iter, data, len, tmp) {
+		type = nla_type(iter);
+
+
+	}
+	// TODO:do something
+	return err;
+}
+
+
+static int bes2600_cfgvendor_initiate_gscan(struct wiphy *wiphy,
+		       struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+	int type, tmp = len;
+	int run = 0xFF;
+	int flush = 0;
+	const struct nlattr *iter;
+
+	nla_for_each_attr(iter, data, len, tmp) {
+		type = nla_type(iter);
+	}
+	// TODO:do something
+		return err;
+
+}
+
+
+static int bes2600_cfgvendor_enable_full_scan_result(struct wiphy *wiphy,
+		struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+	int type;
+	bool real_time = FALSE;
+
+	type = nla_type(data);
+
+	// TODO:do something
+
+	return err;
+}
+
+
+static int bes2600_cfgvendor_hotlist_cfg(struct wiphy *wiphy,
+		    struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+	// gscan_hotlist_scan_params_t *hotlist_params;
+	int tmp, tmp1, tmp2, type, j = 0, dummy;
+	const struct nlattr *outer, *inner, *iter;
+	unsigned char flush = 0;
+	struct bssid_t *pbssid;
+
+	// TODO:do something
+
+	nla_for_each_attr(iter, data, len, tmp2) {
+		type = nla_type(iter);
+		switch (type) {
+
+	}
+	// TODO:do something
+		goto exit;
+	}
+exit:
+
+	return err;
+}
+
+
+static int bes2600_cfgvendor_significant_change_cfg(struct wiphy *wiphy,
+		struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+	// gscan_swc_params_t *significant_params;
+	int tmp, tmp1, tmp2, type, j = 0;
+	const struct nlattr *outer, *inner, *iter;
+	unsigned char flush = 0;
+	// wl_pfn_significant_bssid_t *pbssid;
+
+	// TODO:do something
+
+	nla_for_each_attr(iter, data, len, tmp2) {
+		type = nla_type(iter);
+
+		switch (type) {
+
+	}
+}
+
+	// TODO:do something
+exit:
+
+	return err;
+}
+
+
+static int bes2600_cfgvendor_gscan_get_batch_results(struct wiphy *wiphy,
+		struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+	// gscan_results_cache_t *results, *iter;
+	unsigned int reply_len, complete = 0, num_results_iter;
+	int mem_needed;
+	// wifi_gscan_result_t *ptr;
+	unsigned short num_scan_ids, num_results;
+	struct sk_buff *skb;
+	struct nlattr *scan_hdr;
+
+	// TODO:do something
+	num_scan_ids = reply_len & 0xFFFF;
+	num_results = (reply_len & 0xFFFF0000) >> 16;
+
+	 // TODO:do something
+	 return err;
+}
+
+
+static int bes2600_cfgvendor_gscan_get_channel_list(struct wiphy *wiphy,
+		struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0, type, band;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+	unsigned short *reply = NULL;
+	unsigned int reply_len = 0, num_channels, mem_needed;
+	struct sk_buff *skb;
+
+	type = nla_type(data);
+
+	// TODO:do something
+
+	if (!reply) {
+	// TODO:do something
+		err = -EINVAL;
+		return err;
+	}
+
+	// TODO:do something
+	if (unlikely(!skb)) {
+	// TODO:do something
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	// TODO:do something
+
+	if (unlikely(err))
+	// TODO:do something
+exit:
+	kfree(reply);
+	return err;
+}
+
+
+static int bes2600_cfgvendor_rtt_set_config(struct wiphy *wiphy, struct wireless_dev *wdev,
+				       const void *data, int len)
+{
+	int err = 0, rem, rem1, rem2, type;
+
+	const struct nlattr *iter, *iter1, *iter2;
+
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+
+	if (err < 0) {
+	// TODO:do something
+		goto exit;
+	}
+
+	nla_for_each_attr(iter, data, len, rem) {
+		type = nla_type(iter);
+		switch (type) {
+	// TODO:do something
+		}
+	}
+
+exit:
+	return err;
+}
+
+
+static int bes2600_cfgvendor_rtt_cancel_config(struct wiphy *wiphy, struct wireless_dev *wdev,
+		const void *data, int len)
+{
+	int err = 0, rem, type, target_cnt = 0;
+	const struct nlattr *iter;
+	struct ether_addr *mac_list = NULL, *mac_addr = NULL;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+
+	nla_for_each_attr(iter, data, len, rem) {
+		type = nla_type(iter);
+		switch (type) {
+
+		}
+	}
+exit:
+	if (mac_list)
+		kfree(mac_list);
+	return err;
+}
+
+
+static int bes2600_cfgvendor_rtt_get_capability(struct wiphy *wiphy, struct wireless_dev *wdev,
+		const void *data, int len)
+{
+	int err = 0;
+	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
+
+	if (unlikely(err)) {
+	// TODO:do something
+		goto exit;
+	}
+	// TODO:do something
+
+	if (unlikely(err))
+	// TODO:do something
+exit:
+	return err;
+}
+
+
+static int bes2600_cfgvendor_lstats_get_info(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+
+	char *output;
+
+	// TODO:do something
+	if (output == NULL) {
+	// TODO:do something
+	}
+
+	return err;
+}
+
+
+static int bes2600_cfgvendor_lstats_set_info(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	// TODO:do something
+	return err;
+}
+
+
+static int bes2600_cfgvendor_lstats_clear_info(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	// TODO:do something
+	return err;
+}
+
+
+static int bes2600_cfgvendor_set_rssi_monitor(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+    // TODO:do something
+
+	int err = 0, rem, type;
+        const struct nlattr *iter;
+
+    // TODO:do something
+
+	nla_for_each_attr(iter, data, len, rem) {
+		type = nla_type(iter);
+
+		switch (type) {
+    // TODO:do something
+		}
+	}
+
+	return err;
+}
+
+
+static int bes2600_cfgvendor_logger_start_logging(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+	int ret = 0, rem, type;
+	char ring_name[32] = {0};
+	int log_level = 0, flags = 0, time_intval = 0, threshold = 0;
+	const struct nlattr *iter;
+
+	nla_for_each_attr(iter, data, len, rem) {
+		type = nla_type(iter);
+		switch (type) {
+	// TODO:do something
+		}
+	}
+
+exit:
+	return ret;
+}
+
+
+static int bes2600_cfgvendor_logger_get_feature(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void *data, int len)
+{
+	int err = 0;
+	u32 supported_features = 0;
+
+	if (unlikely(err))
+	// TODO:do something
+
+	return err;
+}
+
+
+static int bes2600_cfgvendor_logger_get_version(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void *data, int len)
+{
+
+	int ret = 0, rem, type;
+	int buf_len = 1024;
+	char *buf_ptr;
+	const struct nlattr *iter;
+	gfp_t kflags;
+
+	kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
+	buf_ptr = kzalloc(buf_len, kflags);
+	if (!buf_ptr) {
+	// TODO:do something
+		ret = -ENOMEM;
+		goto exit;
+	}
+	nla_for_each_attr(iter, data, len, rem) {
+		type = nla_type(iter);
+		switch (type) {
+	// TODO:do something
+		}
+	}
+	if (ret < 0) {
+	// TODO:do something
+		goto exit;
+	}
+
+
+	// TODO:do something
+exit:
+	kfree(buf_ptr);
+	return ret;
+}
+
+
+static int bes2600_cfgvendor_logger_get_ring_status(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+	int ret = 0;
+	int ring_id;
+	char ring_buf_name[] = "bes2600_RING_BUFFER";
+
+	struct sk_buff *skb;
+
+	// TODO:do something
+	/* Alloc the SKB for vendor_event */
+
+	if (!skb) {
+	// TODO:do something
+		ret = FAIL;
+		goto exit;
+	}
+
+	ret = cfg80211_vendor_cmd_reply(skb);
+
+	if (ret) {
+	// TODO:do something
+	}
+exit:
+	return ret;
+}
+
+
+static int bes2600_cfgvendor_logger_get_ring_data(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+	int ret = 0, rem, type;
+	char ring_name[32] = {0};
+	const struct nlattr *iter;
+
+	nla_for_each_attr(iter, data, len, rem) {
+		type = nla_type(iter);
+		switch (type) {
+
+	// TODO:do something
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+
+static int bes2600_cfgvendor_logger_get_firmware_memory_dump(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+	int ret = WIFI_ERROR_NOT_SUPPORTED;
+
+	return ret;
+}
+
+
+static int bes2600_cfgvendor_logger_start_pkt_fate_monitoring(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+	int ret = WIFI_SUCCESS;
+
+	return ret;
+}
+
+
+static int bes2600_cfgvendor_logger_get_tx_pkt_fates(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+	int ret = WIFI_SUCCESS;
+
+	return ret;
+}
+
+
+static int bes2600_cfgvendor_logger_get_rx_pkt_fates(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void  *data, int len)
+{
+	int ret = WIFI_SUCCESS;
+
+	return ret;
+}
+
+
+static int bes2600_cfgvendor_set_rand_mac_oui(struct wiphy *wiphy,
+		struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+
+	void *devaddr;
+	struct net_device *netdev;
+	int type, mac_len;
+	u8 pno_random_mac_oui[3];
+	u8 mac_addr[ETH_ALEN] = {0};
+	struct pwrctrl_priv *pwrctl;
+	// TODO:do something
+
+	type = nla_type(data);
+	mac_len = nla_len(data);
+	if (mac_len != 3) {
+	// TODO:do something
+		return -1;
+	}
+
+	if (type == ANDR_WIFI_ATTRIBUTE_RANDOM_MAC_OUI) {
+		memcpy(pno_random_mac_oui, nla_data(data), 3);
+		print_hex_dump(KERN_DEBUG, "pno_random_mac_oui: ",
+			       DUMP_PREFIX_OFFSET, 16, 1, pno_random_mac_oui,
+			       3, 1);
+
+ 	// TODO:do something
+	} else {
+	// TODO:do something
+		err = -1;
+	}
+
+
+	return err;
+}
+
+
+static int bes2600_cfgvendor_set_nodfs_flag(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void *data, int len)
+{
+	int err = 0;
+	int type;
+	u32 nodfs = 0;
+
+	// TODO:do something
+
+	type = nla_type(data);
+	if (type == ANDR_WIFI_ATTRIBUTE_NODFS_SET) {
+		nodfs = nla_get_u32(data);
+
+	} else {
+		err = -EINVAL;
+	}
+
+	// TODO:do something
+
+	return err;
+}
+
+
+static int bes2600_cfgvendor_set_nd_offload(struct wiphy *wiphy,
+	struct wireless_dev *wdev, const void *data, int len)
+{
+	int err = 0;
+	int type;
+	u8 nd_en = 0;
+
+	// TODO:do something
+
+	type = nla_type(data);
+	if (type == ANDR_WIFI_ATTRIBUTE_ND_OFFLOAD_VALUE) {
+		nd_en = nla_get_u8(data);
+		/* ND has been enabled when wow is enabled */
+	} else {
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+
+static int bes2600_cfgvendor_get_feature_set(struct wiphy *wiphy,
+		struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	int reply;
+
+	// TODO:do something
+	if (unlikely(err))
+	// TODO:do something
+
+	return err;
+}
+
+
+static int bes2600_cfgvendor_get_feature_set_matrix(struct wiphy *wiphy,
+		struct wireless_dev *wdev, const void  *data, int len)
+{
+	int err = 0;
+	struct sk_buff *skb;
+	int *reply;
+	int num, mem_needed, i;
+
+ 	// TODO:do something
+
+	if (!reply) {
+	// TODO:do something
+		err = -EINVAL;
+		return err;
+	}
+
+	/* Alloc the SKB for vendor_event */
+	// TODO:do something
+	if (unlikely(!skb)) {
+	// TODO:do something
+		err = -ENOMEM;
+		goto exit;
+	}
+
+	nla_put_u32(skb, ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET, num);
+	for (i = 0; i < num; i++)
+		nla_put_u32(skb, ANDR_WIFI_ATTRIBUTE_FEATURE_SET, reply[i]);
+
+	// TODO:do something
+
+	if (unlikely(err))
+	// TODO:do something
+exit:
+	// TODO:do something
+	return err;
+}
+#endif
+
+static const struct wiphy_vendor_command bes2600_own_commands[] = {
+	{
+		{
+		.vendor_id = OUI_GOOGLE,
+		.subcmd = WIFI_SUBCMD_SET_COUNTRY_CODE
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_set_country_code,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+
+    },
+	#if 0
+    {
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = GSCAN_SUBCMD_GET_CAPABILITIES
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_gscan_get_capabilities
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = GSCAN_SUBCMD_SET_CONFIG
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_set_scan_cfg
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = GSCAN_SUBCMD_SET_SCAN_CONFIG
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_set_batch_scan_cfg
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = GSCAN_SUBCMD_ENABLE_GSCAN
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_initiate_gscan
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_enable_full_scan_result
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = GSCAN_SUBCMD_SET_HOTLIST
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_hotlist_cfg
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_significant_change_cfg
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = GSCAN_SUBCMD_GET_SCAN_RESULTS
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_gscan_get_batch_results
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = GSCAN_SUBCMD_GET_CHANNEL_LIST
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_gscan_get_channel_list
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = RTT_SUBCMD_SET_CONFIG
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_rtt_set_config
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = RTT_SUBCMD_CANCEL_CONFIG
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_rtt_cancel_config
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = RTT_SUBCMD_GETCAPABILITY
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_rtt_get_capability
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LSTATS_SUBCMD_GET_INFO
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_lstats_get_info
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LSTATS_SUBCMD_SET_INFO
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_lstats_set_info
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LSTATS_SUBCMD_CLEAR_INFO
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_lstats_clear_info
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+    {
+        {
+            .vendor_id = OUI_GOOGLE,
+            .subcmd = WIFI_SUBCMD_SET_RSSI_MONITOR
+        },
+        .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+        .doit = bes2600_cfgvendor_set_rssi_monitor
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+    },
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LOGGER_START_LOGGING
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_logger_start_logging
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LOGGER_GET_FEATURE
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_logger_get_feature
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LOGGER_GET_VER
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_logger_get_version
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LOGGER_GET_RING_STATUS
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_logger_get_ring_status
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LOGGER_GET_RING_DATA
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_logger_get_ring_data
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LOGGER_TRIGGER_MEM_DUMP
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_logger_get_firmware_memory_dump
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LOGGER_START_PKT_FATE_MONITORING
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_logger_start_pkt_fate_monitoring
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LOGGER_GET_TX_PKT_FATES
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_logger_get_tx_pkt_fates
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = LOGGER_GET_RX_PKT_FATES
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_logger_get_rx_pkt_fates
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_set_rand_mac_oui
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = WIFI_SUBCMD_NODFS_SET
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_set_nodfs_flag
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = WIFI_SUBCMD_CONFIG_ND_OFFLOAD
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_set_nd_offload
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = WIFI_SUBCMD_GET_FEATURE_SET
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_get_feature_set
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	},
+	{
+		{
+			.vendor_id = OUI_GOOGLE,
+			.subcmd = WIFI_SUBCMD_GET_FEATURE_SET_MATRIX
+		},
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV,
+		.doit = bes2600_cfgvendor_get_feature_set_matrix
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,3,0))
+		.policy = VENDOR_CMD_RAW_DATA,
+#endif
+	}
+	#endif
+};
+
+static const struct  nl80211_vendor_cmd_info bes2600_own_events[] = {
+	{ OUI_GOOGLE, GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS },
+	{ OUI_GOOGLE, GSCAN_EVENT_HOTLIST_RESULTS_FOUND },
+	{ OUI_GOOGLE, GSCAN_EVENT_SCAN_RESULTS_AVAILABLE },
+	{ OUI_GOOGLE, GSCAN_EVENT_FULL_SCAN_RESULTS },
+	{ OUI_GOOGLE, RTT_EVENT_COMPLETE },
+	{ OUI_GOOGLE, GOOGLE_RSSI_MONITOR_EVENT },
+	{ OUI_GOOGLE, GSCAN_EVENT_COMPLETE_SCAN },
+	{ OUI_GOOGLE, GSCAN_EVENT_HOTLIST_RESULTS_LOST }
+
+};
+
+
+int bes2600_set_vendor_command(struct wiphy *wiphy)
+{
+
+	wiphy->vendor_commands = bes2600_own_commands;
+	wiphy->n_vendor_commands = ARRAY_SIZE(bes2600_own_commands);
+	wiphy->vendor_events	= bes2600_own_events;
+	wiphy->n_vendor_events	= ARRAY_SIZE(bes2600_own_events);
+
+	return 0;
+}
+
+int bes2600_vendor_command_detach(struct wiphy *wiphy)
+{
+
+	wiphy->vendor_commands  = NULL;
+	wiphy->vendor_events    = NULL;
+	wiphy->n_vendor_commands = 0;
+	wiphy->n_vendor_events  = 0;
+
+	return 0;
+}
+
+
+
+
diff --git a/drivers/staging/bes2600/bes2600_cfgvendor.h b/drivers/staging/bes2600/bes2600_cfgvendor.h
new file mode 100644
index 000000000000..66d400d12316
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_cfgvendor.h
@@ -0,0 +1,218 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef BES2600_CFGVENDOR_H
+#define BES2600_CFGVENDOR_H
+
+#include "bes2600.h"
+#include "txrx.h"
+#include "sbus.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "bh.h"
+#include "sta.h"
+#include "ap.h"
+#include "scan.h"
+#include "debug.h"
+#include "pm.h"
+
+#define OUI_GOOGLE  0x001A11
+#define GSCAN_BATCH_NO_THR_SET    101
+#define FALSE		0
+#define FAIL	(-1)
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+
+typedef enum bes2600_own_event {
+    BES2600_RESERVED1,
+    BES2600_RESERVED2,
+    GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS ,
+    GSCAN_EVENT_HOTLIST_RESULTS_FOUND,
+    GSCAN_EVENT_SCAN_RESULTS_AVAILABLE,
+    GSCAN_EVENT_FULL_SCAN_RESULTS,
+    RTT_EVENT_COMPLETE,
+    GSCAN_EVENT_COMPLETE_SCAN,
+    GSCAN_EVENT_HOTLIST_RESULTS_LOST,
+    GSCAN_EVENT_EPNO_EVENT,
+    GOOGLE_DEBUG_RING_EVENT,
+    GOOGLE_DEBUG_MEM_DUMP_EVENT,
+    GSCAN_EVENT_ANQPO_HOTSPOT_MATCH,
+    GOOGLE_RSSI_MONITOR_EVENT
+} bes2600_own_event_t;
+
+enum andr_wifi_feature_set_attr {
+	ANDR_WIFI_ATTRIBUTE_NUM_FEATURE_SET,
+	ANDR_WIFI_ATTRIBUTE_FEATURE_SET,
+	ANDR_WIFI_ATTRIBUTE_RANDOM_MAC_OUI,
+	ANDR_WIFI_ATTRIBUTE_NODFS_SET,
+	ANDR_WIFI_ATTRIBUTE_COUNTRY,
+	ANDR_WIFI_ATTRIBUTE_ND_OFFLOAD_VALUE
+	// Add more attribute here
+};
+
+typedef enum {
+    /* don't use 0 as a valid subcommand */
+    VENDOR_NL80211_SUBCMD_UNSPECIFIED,
+
+    /* define all vendor startup commands between 0x0 and 0x0FFF */
+    VENDOR_NL80211_SUBCMD_RANGE_START = 0x0001,
+    VENDOR_NL80211_SUBCMD_RANGE_END   = 0x0FFF,
+
+    /* define all GScan related commands between 0x1000 and 0x10FF */
+    ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START = 0x1000,
+    ANDROID_NL80211_SUBCMD_GSCAN_RANGE_END   = 0x10FF,
+
+    /* define all NearbyDiscovery related commands between 0x1100 and 0x11FF */
+    ANDROID_NL80211_SUBCMD_NBD_RANGE_START = 0x1100,
+    ANDROID_NL80211_SUBCMD_NBD_RANGE_END   = 0x11FF,
+
+    /* define all RTT related commands between 0x1100 and 0x11FF */
+    ANDROID_NL80211_SUBCMD_RTT_RANGE_START = 0x1100,
+    ANDROID_NL80211_SUBCMD_RTT_RANGE_END   = 0x11FF,
+
+    ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START = 0x1200,
+    ANDROID_NL80211_SUBCMD_LSTATS_RANGE_END   = 0x12FF,
+
+    /* define all Logger related commands between 0x1400 and 0x14FF */
+    ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START = 0x1400,
+    ANDROID_NL80211_SUBCMD_DEBUG_RANGE_END   = 0x14FF,
+
+    /* define all wifi offload related commands between 0x1600 and 0x16FF */
+    ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_START = 0x1600,
+    ANDROID_NL80211_SUBCMD_WIFI_OFFLOAD_RANGE_END   = 0x16FF,
+
+    /* define all NAN related commands between 0x1700 and 0x17FF */
+    ANDROID_NL80211_SUBCMD_NAN_RANGE_START = 0x1700,
+    ANDROID_NL80211_SUBCMD_NAN_RANGE_END   = 0x17FF,
+
+    /* define all Android Packet Filter related commands between 0x1800 and 0x18FF */
+    ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START = 0x1800,
+    ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_END   = 0x18FF,
+
+    /* This is reserved for future usage */
+
+} ANDROID_VENDOR_SUB_COMMAND;
+
+enum {
+    LSTATS_SUBCMD_GET_INFO = ANDROID_NL80211_SUBCMD_LSTATS_RANGE_START,
+	LSTATS_SUBCMD_SET_INFO,
+	LSTATS_SUBCMD_CLEAR_INFO,
+};
+
+enum bes2600_vendor_subcmd {
+    GSCAN_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_GSCAN_RANGE_START,
+
+    GSCAN_SUBCMD_SET_CONFIG,                            /* 0x1001 */
+
+    GSCAN_SUBCMD_SET_SCAN_CONFIG,                       /* 0x1002 */
+    GSCAN_SUBCMD_ENABLE_GSCAN,                          /* 0x1003 */
+    GSCAN_SUBCMD_GET_SCAN_RESULTS,                      /* 0x1004 */
+    GSCAN_SUBCMD_SCAN_RESULTS,                          /* 0x1005 */
+
+    GSCAN_SUBCMD_SET_HOTLIST,                           /* 0x1006 */
+
+    GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG,         /* 0x1007 */
+    GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS,              /* 0x1008 */
+    GSCAN_SUBCMD_GET_CHANNEL_LIST,                       /* 0x1009 */
+
+    WIFI_SUBCMD_GET_FEATURE_SET,                         /* 0x100A */
+    WIFI_SUBCMD_GET_FEATURE_SET_MATRIX,                  /* 0x100B */
+    WIFI_SUBCMD_SET_PNO_RANDOM_MAC_OUI,                  /* 0x100C */
+    WIFI_SUBCMD_NODFS_SET,                               /* 0x100D */
+    WIFI_SUBCMD_SET_COUNTRY_CODE,                             /* 0x100E */
+    /* Add more sub commands here */
+    GSCAN_SUBCMD_SET_EPNO_SSID,                          /* 0x100F */
+
+    WIFI_SUBCMD_SET_SSID_WHITE_LIST,                    /* 0x1010 */
+    WIFI_SUBCMD_SET_ROAM_PARAMS,                        /* 0x1011 */
+    WIFI_SUBCMD_ENABLE_LAZY_ROAM,                       /* 0x1012 */
+    WIFI_SUBCMD_SET_BSSID_PREF,                         /* 0x1013 */
+    WIFI_SUBCMD_SET_BSSID_BLACKLIST,                     /* 0x1014 */
+
+    GSCAN_SUBCMD_ANQPO_CONFIG,                          /* 0x1015 */
+    WIFI_SUBCMD_SET_RSSI_MONITOR,                       /* 0x1016 */
+    WIFI_SUBCMD_CONFIG_ND_OFFLOAD,                      /* 0x1017 */
+    /* Add more sub commands here */
+
+    GSCAN_SUBCMD_MAX,
+
+	RTT_SUBCMD_SET_CONFIG = ANDROID_NL80211_SUBCMD_RTT_RANGE_START,
+	RTT_SUBCMD_CANCEL_CONFIG,
+	RTT_SUBCMD_GETCAPABILITY,
+
+    APF_SUBCMD_GET_CAPABILITIES = ANDROID_NL80211_SUBCMD_PKT_FILTER_RANGE_START,
+    APF_SUBCMD_SET_FILTER,
+
+    LOGGER_START_LOGGING = ANDROID_NL80211_SUBCMD_DEBUG_RANGE_START,
+    LOGGER_TRIGGER_MEM_DUMP,
+    LOGGER_GET_MEM_DUMP,
+    LOGGER_GET_VER,
+    LOGGER_GET_RING_STATUS,
+    LOGGER_GET_RING_DATA,
+    LOGGER_GET_FEATURE,
+    LOGGER_RESET_LOGGING,
+    LOGGER_TRIGGER_DRIVER_MEM_DUMP,
+    LOGGER_GET_DRIVER_MEM_DUMP,
+    LOGGER_START_PKT_FATE_MONITORING,
+    LOGGER_GET_TX_PKT_FATES,
+    LOGGER_GET_RX_PKT_FATES,
+
+	VENDOR_SUBCMD_MAX
+};
+
+typedef enum {
+    WIFI_SUCCESS = 0,
+    WIFI_ERROR_NONE = 0,
+    WIFI_ERROR_UNKNOWN = -1,
+    WIFI_ERROR_UNINITIALIZED = -2,
+    WIFI_ERROR_NOT_SUPPORTED = -3,
+    WIFI_ERROR_NOT_AVAILABLE = -4,              // Not available right now, but try later
+    WIFI_ERROR_INVALID_ARGS = -5,
+    WIFI_ERROR_INVALID_REQUEST_ID = -6,
+    WIFI_ERROR_TIMED_OUT = -7,
+    WIFI_ERROR_TOO_MANY_REQUESTS = -8,          // Too many instances of this request
+    WIFI_ERROR_OUT_OF_MEMORY = -9,
+    WIFI_ERROR_BUSY = -10,
+} wifi_error;
+
+enum _IFACE_ID {
+	IFACE_ID0, /*PRIMARY_ADAPTER*/
+	IFACE_ID1,
+	IFACE_ID2,
+	IFACE_ID3,
+	IFACE_ID4,
+	IFACE_ID5,
+	IFACE_ID6,
+	IFACE_ID7,
+	IFACE_ID_MAX,
+};
+
+typedef struct dhd_pno_gscan_capabilities {
+	int max_scan_cache_size;
+	int max_scan_buckets;
+	int max_ap_cache_per_scan;
+	int max_rssi_sample_size;
+	int max_scan_reporting_threshold;
+	int max_hotlist_aps;
+	int max_significant_wifi_change_aps;
+	int max_epno_ssid_crc32;
+	int max_epno_hidden_ssid;
+	int max_white_list_ssid;
+} dhd_pno_gscan_capabilities_t;
+
+typedef struct gscan_batch_params {
+	unsigned char bestn;
+	unsigned char mscan;
+	unsigned char buffer_threshold;
+} gscan_batch_params_t;
+
+void bes2600_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request);
+int bes2600_set_vendor_command(struct wiphy *wiphy);
+int bes2600_vendor_command_detach(struct wiphy *wiphy);
+#endif
\ No newline at end of file
diff --git a/drivers/staging/bes2600/bes2600_driver_mode.h b/drivers/staging/bes2600/bes2600_driver_mode.h
new file mode 100644
index 000000000000..a65aa1a8e78a
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_driver_mode.h
@@ -0,0 +1,65 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef _BES2600_DRIVER_MODE_CMD_
+#define _BES2600_DRIVER_MODE_CMD_
+
+#ifdef FW_DOWNLOAD_BY_UART
+#if defined(PLAT_ALLWINNER_T507)
+#if defined(CONFIG_BES2600_WLAN_SDIO)
+#define BES2600_LOAD_BOOT_NAME          "/vendor/firmware/best2002_fw_boot_sdio.bin"
+#define BES2600_LOAD_FW_NAME            "/vendor/firmware/best2002_fw_sdio.bin"
+#define BES2600_LOAD_NOSIGNAL_FW_NAME       "/vendor/firmware/best2002_fw_sdio_nosignal_uart.bin"
+#define BES2600_LOAD_BTRF_FW_NAME       "/vendor/firmware/best2002_fw_sdio_btrf.bin"
+
+#elif defined(CONFIG_BES2600_WLAN_SPI)
+#define BES2600_LOAD_BOOT_NAME     "/vendor/firmware/best2002_fw_boot_spi.bin"
+#define BES2600_LOAD_FW_NAME        "/vendor/firmware/best2002_fw_spi.bin"
+#define BES2600_LOAD_NOSIGNAL_FW_NAME   "/vendor/firmware/best2002_fw_spi_nosignal.bin"
+#define BES2600_LOAD_BTRF_FW_NAME   "/vendor/firmware/best2002_fw_spi_btrf.bin"
+#endif
+#endif
+
+#if defined(PLAT_QCOM_QM215)
+#define BES2600_LOAD_BOOT_NAME           "/data/wifi/best2002_fw_boot_spi.bin"
+#define BES2600_LOAD_FW_NAME             "/data/wifi/best2002_fw_spi.bin"
+#define BES2600_LOAD_NOSIGNAL_FW_NAME    "/data/wifi/best2002_fw_spi_nosignal.bin"
+#define BES2600_LOAD_BTRF_FW_NAME        "/data/wifi/best2002_fw_spi_btrf.bin"
+#endif
+#endif
+
+#ifdef FW_DOWNLOAD_BY_SDIO
+#ifdef CONFIG_FW_LOADER
+#define BES2600_LOAD_BOOT_NAME      "best2002_fw_boot_sdio.bin"
+#define BES2600_LOAD_FW_NAME        "best2002_fw_sdio.bin"
+#define BES2600_LOAD_NOSIGNAL_FW_NAME   "best2002_fw_sdio_nosignal.bin"
+#define BES2600_LOAD_BTRF_FW_NAME   "best2002_fw_sdio_btrf.bin"
+#else
+#define BES2600_LOAD_BOOT_NAME      "/lib/firmware/best2002_fw_boot_sdio.bin"
+#define BES2600_LOAD_FW_NAME        "/lib/firmware/best2002_fw_sdio.bin"
+#define BES2600_LOAD_NOSIGNAL_FW_NAME   "/lib/firmware/best2002_fw_sdio_nosignal.bin"
+#define BES2600_LOAD_BTRF_FW_NAME   "/lib/firmware/best2002_fw_sdio_btrf.bin"
+#endif
+#ifdef BES2600_BOOT_UART_TO_SDIO
+#define BES2600_LOAD_FW_TOOL_PATH   "/usr/bin/bes_fw_download"
+#define BES2600_LOAD_FW_TOOL_DEVICE "/dev/ttyS3"
+#define BES2600_LOAD_BOOT_PATCH_NAME  "/lib/firmware/bes2600_boot_patch.bin"
+#endif
+#endif
+
+#ifdef FW_DOWNLOAD_BY_USB
+#define BES2600_LOAD_BOOT_NAME      "best2002_fw_boot_usb.bin"
+#define BES2600_LOAD_FW_NAME        "best2002_fw_usb.bin"
+#define BES2600_LOAD_NOSIGNAL_FW_NAME   "best2002_fw_usb_nosignal.bin"
+#define BES2600_LOAD_BTRF_FW_NAME   "best2002_fw_usb_btrf.bin"
+#endif
+
+
+#endif /* _BES2600_DRIVER_MODE_CMD_ */
diff --git a/drivers/staging/bes2600/bes2600_factory.c b/drivers/staging/bes2600/bes2600_factory.c
new file mode 100644
index 000000000000..c525311c2a30
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_factory.c
@@ -0,0 +1,930 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/crc32.h>
+#include <linux/version.h>
+#include "bes2600_factory.h"
+#include "bes2600_log.h"
+#include "bes_chardev.h"
+
+#define LE_CPU_TRANS(val, cvt)		(val = cvt(val))
+
+#define TRANS_LE16(val) LE_CPU_TRANS(val, __cpu_to_le16)
+#define TRANS_LE32(val) LE_CPU_TRANS(val, __cpu_to_le32)
+
+#define TRANS_CPU16(val) LE_CPU_TRANS(val, __le16_to_cpu)
+#define TRANS_CPU32(val) LE_CPU_TRANS(val, __le32_to_cpu)
+
+static DEFINE_MUTEX(factory_lock);
+
+/*
+ * It is only used for temporary storage.
+ * Every time get the factory, it will read from the
+ * file and overwrite the original value.
+ */
+static struct factory_t factory_cali_data;
+static struct factory_t *factory_p = NULL;
+
+void bes2600_factory_lock(void)
+{
+	mutex_lock(&factory_lock);
+}
+
+void bes2600_factory_unlock(void)
+{
+	mutex_unlock(&factory_lock);
+}
+
+u8* bes2600_factory_get_file_buffer(void)
+{
+	u8 *file_buffer = NULL;
+
+	file_buffer = kmalloc(FACTORY_MAX_SIZE, GFP_KERNEL);
+	if (!file_buffer) {
+		return NULL;
+	}
+
+	return file_buffer;
+}
+
+void bes2600_factory_free_file_buffer(u8 *file_buffer)
+{
+	if (file_buffer)
+		kfree(file_buffer);
+}
+
+static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p);
+
+static inline uint32_t factory_crc32(const uint8_t *data, uint32_t len)
+{
+	u32 crc_le = 0;
+	crc_le = crc32_le(0xffffffffL, (uint8_t *)data, len);
+	crc_le ^= 0xffffffffL;
+	return crc_le;
+}
+
+static int bes2600_factory_head_info_check(struct factory_t *factory_data)
+{
+	if (!factory_data) {
+		bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer err\n", __func__);
+		return -1;
+	}
+
+	if (factory_data->head.magic != NVREC_DEV_MAGIC) {
+		return -EBADMSG;
+	}
+
+	if ((factory_data->head.version < NVREC_MINI_VERSION) ||
+		(factory_data->head.version > NVREC_CURRENT_VERSION)) {
+		bes2600_err(BES2600_DBG_FACTORY, "factory version error:%d", factory_data->head.version);
+		return -EBADMSG;
+	}
+
+	return 0;
+}
+
+static int bes2600_factory_crc_check(struct factory_t *factory_data)
+{
+#ifdef FACTORY_CRC_CHECK
+	u32 cal_crc = 0;
+
+	if (!factory_data) {
+		bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer err \n", __func__);
+		return -1;
+	}
+
+	cal_crc = factory_crc32((uint8_t *)(&(factory_data->data)), sizeof(factory_data_t));
+	if (factory_data->head.crc != cal_crc) {
+		bes2600_err(BES2600_DBG_CHARDEV,
+			"bes2600 factory check failed, calc_crc:0x%08x factory_crc: 0x%08x\n",
+			cal_crc, factory_data->head.crc);
+		return -1;
+	}
+	return 0;
+
+#else
+	return 0;
+#endif
+}
+
+/**
+ * factory_section_read_file - Read data of specified length from file
+ * @path:	path of the file
+ * @buffer:	storage of read data
+ *
+ * The maximum file length allowed is 600 bytes.
+ * This function does not do crc verification to take into 
+ * account different storage requirements.
+ * 
+ * Return: length on success, negative error code otherwise.
+ */
+static int factory_section_read_file(char *path, void *buffer)
+{
+	int ret = 0;
+	struct file *fp;
+
+	if (!path || !buffer) {
+		bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer err\n", __func__);
+		return -1;
+	}
+
+	bes2600_info(BES2600_DBG_FACTORY, "reading %s \n", path);
+
+	fp = filp_open(path, O_RDONLY, 0); //S_IRUSR
+	if (IS_ERR(fp)) {
+		bes2600_info(BES2600_DBG_FACTORY, "BES2600 : can't open %s\n",path);
+		return -1;
+	}
+
+	if (fp->f_inode->i_size <= 0 || fp->f_inode->i_size > FACTORY_MAX_SIZE) {
+		bes2600_err(BES2600_DBG_FACTORY,
+			"bes2600_factory.txt size check failed, read_size: %lld max_size: %d\n",
+			fp->f_inode->i_size, FACTORY_MAX_SIZE);
+		filp_close(fp, NULL);
+		return -1;
+	}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
+	ret = kernel_read(fp, buffer, fp->f_inode->i_size, &fp->f_pos);
+#else
+	ret = kernel_read(fp, fp->f_pos, buffer, fp->f_inode->i_size);
+#endif
+
+	filp_close(fp, NULL);
+
+	if (ret != fp->f_inode->i_size) {
+		bes2600_err(BES2600_DBG_FACTORY, "bes2600_factory.txt read fail\n");
+		ret = -1;
+	}
+
+	return ret;
+}
+
+/**
+ * factory_section_write_file - Write data of specified length to file
+ * @path:	path of the file
+ * @buffer:	storage of write data
+ * @size:	length of data to write
+ *
+ * Return: length on success, negative error code otherwise.
+ */
+static int factory_section_write_file(char *path, void *buffer, int size)
+{
+	int ret = 0;
+	struct file *fp;
+
+	bes2600_info(BES2600_DBG_FACTORY, "writing %s \n", path);
+
+	fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
+	if (IS_ERR(fp)) {
+		bes2600_info(BES2600_DBG_FACTORY, "BES2600 : can't open %s\n",path);
+		return -1;
+	}
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
+	ret = kernel_write(fp, buffer, size, &fp->f_pos);
+#else
+	ret = kernel_write(fp, buffer, size, fp->f_pos);
+#endif
+
+	filp_close(fp,NULL);
+
+	return ret;
+}
+
+static inline int factory_parse(uint8_t *source_buf, struct factory_t *factory)
+{
+	int ret = 0;
+
+	if (!source_buf || !factory) {
+		bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer err\n", __func__);
+		return -1;
+	}
+
+	ret = sscanf(source_buf, STANDARD_FACTORY,\
+		&factory->head.magic,\
+		&factory->head.version,\
+		&factory->head.crc,\
+		&factory->data.iQ_offset,\
+		&factory->data.freq_cal,\
+		&factory->data.freq_cal_flags,\
+		&factory->data.tx_power_ch[0],\
+		&factory->data.tx_power_ch[1],\
+		&factory->data.tx_power_ch[2],\
+		&factory->data.tx_power_type,\
+		&factory->data.temperature,\
+		&factory->data.tx_power_ch_5G[0],\
+		&factory->data.tx_power_ch_5G[1],\
+		&factory->data.tx_power_ch_5G[2],\
+		&factory->data.tx_power_ch_5G[3],\
+		&factory->data.tx_power_ch_5G[4],\
+		&factory->data.tx_power_ch_5G[5],\
+		&factory->data.tx_power_ch_5G[6],\
+		&factory->data.tx_power_ch_5G[7],\
+		&factory->data.tx_power_ch_5G[8],\
+		&factory->data.tx_power_ch_5G[9],\
+		&factory->data.tx_power_ch_5G[10],\
+		&factory->data.tx_power_ch_5G[11],\
+		&factory->data.tx_power_ch_5G[12],\
+		&factory->data.tx_power_flags_5G,\
+		&factory->data.temperature_5G,\
+		&factory->data.bt_tx_power[0],\
+		&factory->data.bt_tx_power[1],\
+		&factory->data.bt_tx_power[2],\
+		&factory->data.bt_tx_power[3]);
+
+	if (ret != 30) {
+		bes2600_err(BES2600_DBG_FACTORY, "bes2600_factory.txt parse fail\n");
+		return -1;
+	}
+
+	return ret;
+}
+
+static int factory_section_read_and_check_file(u8 *file_buf, char* path)
+{
+	int ret = 0;
+
+	if (!file_buf || !path) {
+		bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer err\n", __func__);
+		return -1;
+	}
+
+	ret = factory_section_read_file(path, file_buf);
+	if (ret < 0)
+		return ret;
+
+	memset(&factory_cali_data, 0, sizeof(struct factory_t));
+	ret = factory_parse(file_buf, &factory_cali_data);
+	if (ret < 0)
+		return ret;
+
+	ret = bes2600_factory_head_info_check(&factory_cali_data);
+	if (ret < 0)
+		return ret;
+
+	ret = bes2600_factory_crc_check(&factory_cali_data);
+	if (ret < 0)
+		return ret;
+
+	factory_p = &factory_cali_data;
+
+	bes2600_info(BES2600_DBG_FACTORY, "open wifi factory section success");
+
+	return 0;
+}
+
+static void factory_section_wifi_tx_power_check(struct factory_t *factory_data)
+{
+	int i;
+	bool inval_v = false;
+
+	if (!factory_data)
+		return ;
+
+	/* only check cali channel, 11n ch1, ch7, ch13 */
+	for (i = 0; i < ARRAY_SIZE(factory_data->data.tx_power_ch); ++i) {
+		if (factory_data->data.tx_power_ch[i] == 0x0 ||
+			factory_data->data.tx_power_ch[i] > 0x3fff) {
+			inval_v = true;
+		}
+	}
+
+	bes2600_warn_with_cond(inval_v, BES2600_DBG_FACTORY, "tx_power_ch_2g cali, inval calibration value\n");
+	bes2600_info_dump(BES2600_DBG_FACTORY, "tx_power_ch_2g dump:", factory_data->data.tx_power_ch,
+					sizeof(factory_data->data.tx_power_ch));
+
+}
+
+
+static void factory_section_wifi_tx_power_5G_check(struct factory_t *factory_data)
+{
+	int i;
+	bool inval_v = false;
+
+	if (!factory_data)
+		return ;
+
+	/* only check cali channel */
+	for (i = 0; i < ARRAY_SIZE(factory_data->data.tx_power_ch_5G); ++i) {
+		if (factory_data->data.tx_power_ch_5G[i] == 0x0 ||
+			factory_data->data.tx_power_ch_5G[i] > 0x3fff) {
+			inval_v = true;
+		}
+	}
+
+	bes2600_warn_with_cond(inval_v, BES2600_DBG_FACTORY, "tx_power_ch_5g cali, inval calibration value\n");
+	bes2600_info_dump(BES2600_DBG_FACTORY, "tx_power_ch_5g dump:", factory_data->data.tx_power_ch_5G,
+					sizeof(factory_data->data.tx_power_ch_5G));
+
+}
+
+static void factory_section_wifi_freq_cali_check(struct factory_t *factory_data)
+{
+	if (!factory_data)
+		return ;
+
+	if (factory_data->data.freq_cal == 0x0 ||
+		factory_data->data.freq_cal > 0x1ff) {
+			bes2600_warn(BES2600_DBG_FACTORY, "freq cali, inval calibration value\n");
+		}
+
+	bes2600_info_dump(BES2600_DBG_FACTORY, "wifi freq cali dump:", &factory_data->data.freq_cal,
+					sizeof(factory_data->data.freq_cal));
+
+}
+
+static void factory_section_bt_tx_power_check(struct factory_t *factory_data)
+{
+	int i;
+	bool inval_v = false;
+
+	if (!factory_data)
+		return ;
+
+	/* bt only check bdr & edr power, (bdr/edr: div, powerlevel) */
+	for (i = 0; i < ARRAY_SIZE(factory_data->data.bt_tx_power) - 1; i += 2) {
+		if (factory_data->data.bt_tx_power[i] != 0x05) {
+			inval_v = true;
+		}
+
+		if (factory_data->data.bt_tx_power[i + 1] == 0x0 ||
+			factory_data->data.bt_tx_power[i + 1] > 0x20) {
+			inval_v = true;
+		}
+	}
+
+	bes2600_warn_with_cond(inval_v, BES2600_DBG_FACTORY, "bt tx power cali, inval calibration value\n");
+	bes2600_info_dump(BES2600_DBG_FACTORY, "bt tx power cali dump:", factory_data->data.bt_tx_power,
+					sizeof(factory_data->data.bt_tx_power));
+
+}
+
+void factory_little_endian_cvrt(u8 *data)
+{
+	int i;
+	struct factory_t *trans_data = NULL;
+	if (!data)
+		return ;
+
+	trans_data = (struct factory_t *)data;
+
+	TRANS_LE16(trans_data->head.magic);
+	TRANS_LE16(trans_data->head.version);
+	TRANS_LE32(trans_data->head.crc);
+
+	TRANS_LE16(trans_data->data.freq_cal);
+	TRANS_LE32(trans_data->data.iQ_offset);
+
+	for (i = 0; i < ARRAY_SIZE(trans_data->data.tx_power_ch); i++)
+		TRANS_LE16(trans_data->data.tx_power_ch[i]);
+
+	TRANS_LE16(trans_data->data.temperature);
+
+	for (i = 0; i < ARRAY_SIZE(trans_data->data.bt_tx_power); i++)
+		TRANS_LE32(trans_data->data.bt_tx_power[i]);
+
+	for (i = 0; i < ARRAY_SIZE(trans_data->data.tx_power_ch_5G); i++)
+		TRANS_LE16(trans_data->data.tx_power_ch_5G[i]);
+
+	TRANS_LE16(trans_data->data.tx_power_flags_5G);
+	TRANS_LE16(trans_data->data.temperature_5G);
+
+}
+
+void bes2600_factory_data_check(u8* data)
+{
+	struct factory_t *factory_data = NULL;
+
+	if (!data)
+		return ;
+
+	factory_data = (struct factory_t *)data;
+
+	factory_section_wifi_tx_power_check(factory_data);
+	factory_section_wifi_tx_power_5G_check(factory_data);
+	factory_section_bt_tx_power_check(factory_data);
+	factory_section_wifi_freq_cali_check(factory_data);
+
+	/* In order to support manual value change, recalculate crc before sending */
+	factory_data->head.crc =
+		factory_crc32((uint8_t *)(&(factory_data->data)), sizeof(factory_data_t));
+
+}
+
+/*
+ * get factory data from file each time, and update factory_p.
+ */
+u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path)
+{
+	u8 *ret_p = NULL;
+
+	if (!file_buffer || !path || !data_len) {
+		bes2600_err(BES2600_DBG_FACTORY, "%s NULL pointer\n", __func__);
+		return NULL;
+	}
+
+	/* reset factory_p */
+	factory_p = NULL;
+
+	*data_len = sizeof(struct factory_t);
+	if (factory_section_read_and_check_file(file_buffer, path) < 0) {
+		bes2600_err(BES2600_DBG_FACTORY, "read and check %s error\n", path);
+		*data_len = 0;
+		return NULL;
+	}
+
+	if (!factory_p) {
+		*data_len = 0;
+		return NULL;
+	}
+
+	ret_p = (u8 *)factory_p;
+
+	return ret_p;
+}
+
+/**
+ * When the calibration file does not exist, a new file is automatically created when
+ * the calibration value is written. After writing, update factory_p, if the update is successful,
+ * it means the writing is successful, otherwise it fails. At the same time, subsequent calibration
+ * values are saved on this basis to avoid duplicating file creation and flushing out previously saved values.
+ */
+static bool bes2600_factory_file_status_read(u8 *file_buffer)
+{
+	u8 *factory_temp = NULL;
+	uint32_t len;
+	bool ret = true;
+
+#ifdef FACTORY_SAVE_MULTI_PATH
+	factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_PATH);
+	if (!factory_temp) {
+		bes2600_warn(BES2600_DBG_FACTORY, "get factory cali from first path fali\n");
+		factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_DEFAULT_PATH);
+		/* clear the flag of the file in the default path, and then create a new file */
+		if (factory_temp) {
+			((struct factory_t *)factory_temp)->data.tx_power_type = 0xff;
+			((struct factory_t *)factory_temp)->data.freq_cal_flags = 0;
+			((struct factory_t *)factory_temp)->data.tx_power_flags_5G = 0;
+		}
+	}
+#else
+	factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_PATH);
+#endif
+	if (!factory_temp) {
+		bes2600_warn(BES2600_DBG_FACTORY, "get factory data fali, check whether the file exists\n");
+		ret = false;
+	}
+
+	return ret;
+}
+
+/* create a new factory.txt file, and set default value */
+static int bes2600_factory_cali_file_hdr_fill(struct factory_t **factory_head)
+{
+	u16 tx_power_type = 0xff;
+	int i;
+
+	if (!factory_head)
+		return -1;
+
+	*factory_head = &factory_cali_data;
+	memset(*factory_head, 0, sizeof(struct factory_t));
+	(*factory_head)->data.tx_power_type = tx_power_type;
+	(*factory_head)->head.magic = 0xba80;
+	(*factory_head)->head.version = 2;
+
+	(*factory_head)->data.freq_cal = 0xa0;
+
+	for (i = 0; i < 3; ++i) {
+		(*factory_head)->data.tx_power_ch[i] = 0x1400;
+	}
+
+	for (i = 0; i < 13; ++i) {
+		(*factory_head)->data.tx_power_ch_5G[i] = 0x1400;
+	}
+
+	(*factory_head)->data.bt_tx_power[0] = 0x05;
+	(*factory_head)->data.bt_tx_power[1] = 0x10;
+	(*factory_head)->data.bt_tx_power[2] = 0x05;
+	(*factory_head)->data.bt_tx_power[3] = 0x15;
+
+	return 0;
+}
+
+
+int16_t bes2600_wifi_power_cali_table_write(struct wifi_power_cali_save_t *data_cali)
+{
+	u16 mode, band, ch, power_cali, bandwidth;
+	int power_index = 0;
+	struct factory_t *factory_power_p = NULL;
+	u8 *file_buffer = NULL;
+	int16_t ret = 0;
+
+	if (!data_cali) {
+		bes2600_warn(BES2600_DBG_FACTORY, "%s: power cali save pointer is NULL\n", __func__);
+		return -FACTORY_GET_INPUT_NULL_POINTER;
+	}
+
+	if (!(file_buffer = bes2600_factory_get_file_buffer()))
+		return -FACTORY_GET_INPUT_NULL_POINTER;
+
+	bes2600_factory_lock();
+
+	/**
+	 * When it returns true, it means that the factory file has been read.
+	 * When it returns false, it means that the factory file does not exist,
+	 * or the operation of reading the text file fails. At this time, a new factory file will be created.
+	 */
+	if (bes2600_factory_file_status_read(file_buffer)) {
+		factory_power_p = factory_p;
+	} else {
+		if (bes2600_factory_cali_file_hdr_fill(&factory_power_p)) {
+			bes2600_warn(BES2600_DBG_FACTORY, "%s, create bes2600_factory.txt fail.", __func__);
+			ret = -FACTORY_FACTORY_TXT_CREATE_FAIL;
+			goto err;
+		}
+	}
+
+	mode = data_cali->mode;
+	bandwidth = data_cali->bandwidth;
+	band = data_cali->band;
+	ch = data_cali->ch;
+	power_cali = data_cali->power_cali;
+
+	bes2600_dbg(BES2600_DBG_FACTORY, "%s: mode = %u, bandwidth = %u,, band = %u, ch = %u, power_cali = 0x%04x\n",
+				__func__, mode, bandwidth, band, ch, power_cali);
+
+	/* only in 802.11n 20M msc7 mode, the power calibration value is saved */
+	if (bandwidth != 0 || mode != WIFI_RF_11N_MODE) {
+		ret = -FACTORY_SAVE_MODE_ERR;
+		goto err;
+	}
+
+	/* powerlevel value range: 0 ~ 0x3fff */
+	if (power_cali == 0 || power_cali > 0x3fff) {
+		ret = -FACTORY_SAVE_POWER_ERR;
+		goto err;
+	}
+
+
+	if (band == BAND_2G4) {
+		switch (ch) {
+		case 1:
+			power_index = 0;
+			break;
+		case 7:
+			power_index = 1;
+			break;
+		case 13:
+			power_index = 2;
+			break;
+		default:
+			ret = -FACTORY_SAVE_CH_ERR;
+			goto err;
+			break;
+		}
+		factory_power_p->data.tx_power_ch[power_index] = power_cali;
+	} else if (band == BAND_5G) {
+		switch (ch) {
+		case 36:
+		case 38:
+		case 40:
+			power_index = 0;
+			break;
+		case 44:
+		case 46:
+		case 48:
+			power_index = 1;
+			break;
+		case 52:
+		case 54:
+		case 56:
+			power_index = 2;
+			break;
+		case 60:
+		case 62:
+		case 64:
+			power_index = 3;
+			break;
+		case 100:
+		case 102:
+		case 104:
+			power_index = 4;
+			break;
+		case 108:
+		case 110:
+		case 112:
+			power_index = 5;
+			break;
+		case 116:
+		case 114:
+		case 120:
+			power_index = 6;
+			break;
+		case 124:
+		case 126:
+		case 128:
+			power_index = 7;
+			break;
+		case 132:
+		case 134:
+		case 136:
+			power_index = 8;
+			break;
+		case 140:
+		case 142:
+		case 144:
+			power_index = 9;
+			break;
+		case 149:
+		case 151:
+		case 153:
+			power_index = 10;
+			break;
+		case 157:
+		case 159:
+		case 161:
+			power_index = 11;
+			break;
+		case 165:
+		case 169:
+			power_index = 12;
+			break;
+		default:
+			ret = -FACTORY_SAVE_CH_ERR;
+			goto err;
+			break;
+		}
+		factory_power_p->data.tx_power_ch_5G[power_index] = power_cali;
+	}
+
+	/* save to file */
+	if (bes2600_wifi_cali_table_save(file_buffer, factory_power_p)) {
+		ret = -FACTORY_SAVE_WRITE_ERR;
+		goto err;
+	}
+
+err:
+	bes2600_factory_free_file_buffer(file_buffer);
+	bes2600_factory_unlock();
+	return ret;
+
+}
+
+int16_t bes2600_wifi_cali_freq_write(struct wifi_freq_cali_t *data_cali)
+{
+	u16 freq_cali;
+	struct factory_t *factory_freq_p = NULL;
+	u8 *file_buffer = NULL;
+	int16_t ret = 0;
+
+	if (!data_cali) {
+		bes2600_warn(BES2600_DBG_FACTORY, "%s: freq cali save pointer is NULL\n", __func__);
+		return -FACTORY_GET_INPUT_NULL_POINTER;
+	}
+
+	if (!(file_buffer = bes2600_factory_get_file_buffer()))
+		return -FACTORY_GET_INPUT_NULL_POINTER;
+
+	bes2600_factory_lock();
+
+	/**
+	 * When it returns true, it means that the factory file has been read.
+	 * When it returns false, it means that the factory file does not exist,
+	 * or the operation of reading the text file fails. At this time, a new factory file will be created.
+	 */
+	if (bes2600_factory_file_status_read(file_buffer)) {
+		factory_freq_p = factory_p;
+	} else {
+		if (bes2600_factory_cali_file_hdr_fill(&factory_freq_p)) {
+			bes2600_warn(BES2600_DBG_FACTORY, "%s, create bes2600_factory.txt fail.", __func__);
+			ret = -FACTORY_FACTORY_TXT_CREATE_FAIL;
+			goto err;
+		}
+	}
+
+	freq_cali = data_cali->freq_cali;
+	data_cali->cali_flag = 1;
+
+	/* freqOffset value range: 0 ~ 0x1ff */
+	if (freq_cali == 0 || freq_cali > 0x1ff) {
+		ret = -FACTORY_SAVE_FREQ_ERR;
+		goto err;
+	}
+
+	factory_freq_p->data.freq_cal = freq_cali;
+	factory_freq_p->data.freq_cal_flags = (u8)(data_cali->cali_flag);
+	bes2600_dbg(BES2600_DBG_FACTORY, "%s: freq_cali = 0x%04x\n", __func__, data_cali->freq_cali);
+
+	/* save to file */
+	if (bes2600_wifi_cali_table_save(file_buffer, factory_freq_p)) {
+		ret = -FACTORY_SAVE_WRITE_ERR;
+		goto err;
+	}
+
+err:
+	bes2600_factory_free_file_buffer(file_buffer);
+	bes2600_factory_unlock();
+	return ret;
+}
+
+int16_t vendor_set_power_cali_flag(struct wifi_power_cali_flag_t *cali_flag)
+{
+	struct factory_t *factory_power_flag_p = NULL;
+	u8 *file_buffer = NULL;
+	u16 calied_flag_5g = 1;
+	u16 calied_flag_2g = 0;
+	int16_t ret = 0;
+
+	if (!cali_flag) {
+		bes2600_warn(BES2600_DBG_FACTORY, "%s: power cali flag save pointer is NULL\n", __func__);
+		return -FACTORY_GET_INPUT_NULL_POINTER;
+	}
+
+	if (cali_flag->band != BAND_2G4 && cali_flag->band != BAND_5G) {
+		bes2600_warn(BES2600_DBG_FACTORY, "%s: power cali flag save band err\n", __func__);
+		return -FACTORY_SET_POWER_CALI_FLAG_ERR;
+	}
+
+	if (!(file_buffer = bes2600_factory_get_file_buffer()))
+		return -FACTORY_GET_INPUT_NULL_POINTER;
+
+	bes2600_factory_lock();
+
+	if (bes2600_factory_file_status_read(file_buffer)) {
+		factory_power_flag_p = factory_p;
+	} else {
+		bes2600_warn(BES2600_DBG_FACTORY, "%s: factory cali data is not exist\n", __func__);
+		ret = -FACTORY_SAVE_FILE_NOT_EXIST;
+		goto err;
+	}
+
+	if (cali_flag->band == BAND_2G4) {
+		factory_power_flag_p->data.tx_power_type = calied_flag_2g;
+	} else {
+		factory_power_flag_p->data.tx_power_flags_5G = calied_flag_5g;
+	}
+
+	/* save to file */
+	if (bes2600_wifi_cali_table_save(file_buffer, factory_power_flag_p)) {
+		ret = -FACTORY_SET_POWER_CALI_FLAG_ERR;
+		goto err;
+	}
+
+err:
+	bes2600_factory_free_file_buffer(file_buffer);
+	bes2600_factory_unlock();
+	return ret;
+
+}
+
+static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory)
+{
+	return snprintf(dest_buf, FACTORY_MAX_SIZE, STANDARD_FACTORY,\
+		factory->head.magic,\
+		factory->head.version,\
+		factory->head.crc,\
+		factory->data.iQ_offset,\
+		factory->data.freq_cal,\
+		factory->data.freq_cal_flags,\
+		factory->data.tx_power_ch[0],\
+		factory->data.tx_power_ch[1],\
+		factory->data.tx_power_ch[2],\
+		factory->data.tx_power_type,\
+		factory->data.temperature,\
+		factory->data.tx_power_ch_5G[0],\
+		factory->data.tx_power_ch_5G[1],\
+		factory->data.tx_power_ch_5G[2],\
+		factory->data.tx_power_ch_5G[3],\
+		factory->data.tx_power_ch_5G[4],\
+		factory->data.tx_power_ch_5G[5],\
+		factory->data.tx_power_ch_5G[6],\
+		factory->data.tx_power_ch_5G[7],\
+		factory->data.tx_power_ch_5G[8],\
+		factory->data.tx_power_ch_5G[9],\
+		factory->data.tx_power_ch_5G[10],\
+		factory->data.tx_power_ch_5G[11],\
+		factory->data.tx_power_ch_5G[12],\
+		factory->data.tx_power_flags_5G,\
+		factory->data.temperature_5G,\
+		factory->data.bt_tx_power[0],\
+		factory->data.bt_tx_power[1],\
+		factory->data.bt_tx_power[2],\
+		factory->data.bt_tx_power[3]);
+}
+
+static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p)
+{
+	int ret = 0;
+	int w_size;
+
+	bes2600_info(BES2600_DBG_FACTORY, "enter %s\n", __func__);
+
+	if (!file_buffer) {
+		return -ENOMEM;
+	}
+
+	if (!factory_save_p) {
+		return -ENOENT;
+	}
+
+	/* All initialized to space */
+	memset(file_buffer, 32, FACTORY_MAX_SIZE);
+	file_buffer[FACTORY_MAX_SIZE - 1] = '\n';
+
+	factory_save_p->head.crc =
+		factory_crc32((uint8_t *)(&(factory_save_p->data)), sizeof(factory_data_t));
+
+	w_size = factory_build(file_buffer, factory_save_p);
+
+	if (w_size < 0 || w_size > FACTORY_MAX_SIZE) {
+		bes2600_err(BES2600_DBG_FACTORY, "%s: build failed! ret = %d.", __func__, ret);
+		return -ETXTBSY;
+	}
+
+#ifdef FACTORY_SAVE_MULTI_PATH
+	/* avoid trailing characters '\0' */
+	file_buffer[w_size] = 32;
+	ret = factory_section_write_file(FACTORY_PATH, file_buffer, FACTORY_MAX_SIZE);
+#else
+	ret = factory_section_write_file(FACTORY_PATH, file_buffer, w_size);
+#endif
+	if(ret < 0) {
+		bes2600_err(BES2600_DBG_FACTORY, "%s: write failed! ret = %d.", __func__, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int16_t vendor_get_power_cali(struct wifi_get_power_cali_t *power_cali)
+{
+	u8 *file_buffer = NULL;
+	int16_t ret = 0;
+
+	if (!(file_buffer = bes2600_factory_get_file_buffer()))
+		return -FACTORY_GET_INPUT_NULL_POINTER;
+
+	bes2600_factory_lock();
+
+	if (!power_cali) {
+		ret = -FACTORY_GET_INPUT_NULL_POINTER;
+		goto err;
+	}
+
+	if (!bes2600_factory_file_status_read(file_buffer)) {
+		ret = -FACTORY_SAVE_FILE_NOT_EXIST;
+		goto err;
+	}
+
+	memcpy(power_cali->tx_power_ch, factory_p->data.tx_power_ch, sizeof(power_cali->tx_power_ch));
+	memcpy(power_cali->tx_power_ch_5G, factory_p->data.tx_power_ch_5G, sizeof(power_cali->tx_power_ch_5G));
+
+err:
+	bes2600_factory_free_file_buffer(file_buffer);
+	bes2600_factory_unlock();
+	return ret;
+}
+
+
+int16_t vendor_get_freq_cali(struct wifi_freq_cali_t *vendor_freq)
+{
+	u8 *file_buffer = NULL;
+	int16_t ret = 0;
+
+	if (!vendor_freq)
+		return -FACTORY_GET_INPUT_NULL_POINTER;
+
+	if (!(file_buffer = bes2600_factory_get_file_buffer()))
+		return -FACTORY_GET_INPUT_NULL_POINTER;
+
+	bes2600_factory_lock();
+
+	if (!bes2600_factory_file_status_read(file_buffer)) {
+		ret = -FACTORY_SAVE_FILE_NOT_EXIST;
+		goto err;
+	}
+
+	vendor_freq->status = 0;
+	vendor_freq->freq_cali = factory_p->data.freq_cal;
+	vendor_freq->cali_flag = factory_p->data.freq_cal_flags;
+
+err:
+	bes2600_factory_free_file_buffer(file_buffer);
+	bes2600_factory_unlock();
+	return ret;
+}
+
diff --git a/drivers/staging/bes2600/bes2600_factory.h b/drivers/staging/bes2600/bes2600_factory.h
new file mode 100644
index 000000000000..473840282a82
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_factory.h
@@ -0,0 +1,208 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __FACTORY_H__
+#define __FACTORY_H__
+
+#include "bes2600.h"
+#include "wsm.h"
+
+#define NVREC_MINI_VERSION		1
+#define NVREC_DEV_MAGIC			0xba80
+#define NVREC_CURRENT_VERSION	2
+
+#define FACTORY_MAX_SIZE 600
+#define STANDARD_FACTORY "##head\n\
+magic:0x%hx\n\
+version:0x%hx\n\
+crc:0x%x\n\
+##iq&xtal\n\
+iQ_offset:0x%x\n\
+freq_cal:0x%hx\n\
+freq_cal_flags:0x%hhx\n\
+##2.4g_power_11n\n\
+ch1:0x%hx\n\
+ch7:0x%hx\n\
+ch13:0x%hx\n\
+tx_power_type:0x%hhx\n\
+temperature:0x%hx\n\
+##5g_power_11n\n\
+ch36-40:0x%hx\n\
+ch44-48:0x%hx\n\
+ch52-56:0x%hx\n\
+ch60-64:0x%hx\n\
+ch100-104:0x%hx\n\
+ch108-112:0x%hx\n\
+ch116-120:0x%hx\n\
+ch124-128:0x%hx\n\
+ch132-136:0x%hx\n\
+ch140-144:0x%hx\n\
+ch149-153:0x%hx\n\
+ch157-161:0x%hx\n\
+ch165-169:0x%hx\n\
+tx_power_flags_5G:0x%hx\n\
+temperature_5G:0x%hx\n\
+##bt\n\
+bdr_div:0x%x\n\
+bdr_power:0x%x\n\
+edr_div:0x%x\n\
+edr_power:0x%x\n\
+%%%%\n"
+
+typedef struct {
+	uint16_t magic;
+	uint16_t version;
+	uint32_t crc;
+} factory_head_t;
+
+typedef struct {
+	uint32_t iQ_offset;
+	uint16_t freq_cal;
+	/**
+	 * index 0-2
+	 * 11n ch1, 11n ch7, 11n ch13
+	 */
+	uint16_t tx_power_ch[3];
+
+	/**
+	 * freq_cal_flags 0 - chip not calibrated
+	 * freq_cal_flags 1 - chip has been calibrated
+	 */
+	uint8_t freq_cal_flags;
+
+	/**
+	 * tx_power_type 0 - save bgn 1,7,13 power
+	 * tx_power_type 1 - save bgn 1-13 power
+	 * tx_power_type 0xff - not calibration
+	 */
+	uint8_t tx_power_type;
+	uint16_t temperature;
+
+	/**
+	 * 11n
+	 * 0   36~40:1    44~48:2    52~56:3    60~64;
+	 * 4   100~104:5    108~112:6    116~120;
+	 * 7   124~128:8    132~136:9    140~144
+	 * 10    149~153; 11   157~161:12   165~169
+	 */
+	uint16_t tx_power_ch_5G[13];
+	/**
+	 * 0- it means that power not calib
+	 * 1- it means that power have clibrated
+	 */
+	uint16_t tx_power_flags_5G;
+
+
+	uint32_t bt_tx_power[4];
+	/* The temperature after 5G clibrating. */
+	uint16_t temperature_5G;
+} factory_data_t;
+
+struct factory_t {
+	factory_head_t head;
+	factory_data_t data;
+};
+
+
+enum band_type {
+	BAND_2G4,
+	BAND_5G,
+};
+
+struct wifi_get_power_cali_t {
+	uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */
+	uint16_t tx_power_ch[3];
+	uint16_t tx_power_ch_5G[13];
+	int16_t status; /* 0: success, != 0: fial */
+};
+
+struct wifi_power_cali_save_t {
+	uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */
+	uint16_t mode;
+	uint16_t bandwidth;
+	uint16_t band;
+	uint16_t ch;
+	uint16_t power_cali;
+	int16_t status; /* 0: success, != 0: fial */
+};
+
+struct wifi_freq_cali_t {
+	uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */
+	uint16_t freq_cali;
+	int16_t status; /* 0: success, != 0: fial */
+	uint16_t cali_flag;
+};
+
+struct wifi_power_cali_flag_t {
+	uint16_t save_type; /* enmu RF_FACTORY_CALI_DATA_SAVE_TYPE */
+	uint16_t band;
+	int16_t status; /* 0: success, != 0: fial */
+};
+
+
+/**
+ * fatory cali data save type
+ * @RF_CALIB_DATA_IN_LINUX - save to linux file
+ * @RF_CALIB_DATA_IN_EFUSE - save to efuse
+ * @RF_CALIB_DATA_IN_FLASH - save to flash
+ * @RF_CALIB_DATA_TYPE_MAX - save type num
+ */
+enum RF_FACTORY_CALI_DATA_SAVE_TYPE {
+	RF_CALIB_DATA_IN_LINUX = 0,
+	RF_CALIB_DATA_IN_EFUSE,
+	RF_CALIB_DATA_IN_FLASH,
+	RF_CALIB_DATA_TYPE_MAX,
+};
+
+/* fatory power & freq cali save status code */
+enum factory_cali_status {
+	FACTORY_SAVE_SUCCESS				= 0,
+	FACTORY_SAVE_FILE_NOT_EXIST			= 1,
+	FACTORY_SAVE_MODE_ERR				= 2,
+	FACTORY_SAVE_CH_ERR					= 3,
+	FACTORY_SAVE_POWER_ERR				= 4,
+	FACTORY_SAVE_FREQ_ERR				= 5,
+	FACTORY_SAVE_EFUSE_CALIED			= 6,
+	FACTORY_SAVE_WRITE_ERR				= 7,
+	FACTORY_GET_CALIB_FROM_EFUSE_ERR	= 8,
+	FACTORY_GET_FREQ_FROM_EFUSE_ERR		= 9,
+	FACTORY_GET_POWER_FROM_EFUSE_ERR	= 10,
+	FACTORY_GET_POWER_FROM_FLASH_ERR	= 11,
+	FACTORY_GET_FREQ_FROM_FLASH_ERR		= 12,
+	FACTORY_SET_POWER_CALI_FLAG_ERR		= 13,
+	FACTORY_SET_FREQ_CALI_FLAG_ERR		= 14,
+	FACTORY_SAVE_READ_ERR				= 15,
+	FACTORY_GET_INPUT_NULL_POINTER		= 16,
+	FACTORY_FACTORY_TXT_CREATE_FAIL		= 17,
+	/* add new here, and numbered sequentially */
+};
+
+
+
+/* just calibrate 11n, other protocols are automatically mapped */
+#define WIFI_RF_11N_MODE 0x15
+
+/* read wifi & bt factory cali value*/
+u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path);
+void factory_little_endian_cvrt(u8 *data);
+void bes2600_factory_data_check(u8* data);
+void bes2600_factory_lock(void);
+void bes2600_factory_unlock(void);
+u8* bes2600_factory_get_file_buffer(void);
+void bes2600_factory_free_file_buffer(u8 *file_buffer);
+
+/* read & write wifi cali value */
+int16_t bes2600_wifi_power_cali_table_write(struct wifi_power_cali_save_t *data_cali);
+int16_t bes2600_wifi_cali_freq_write(struct wifi_freq_cali_t *data_cali);
+int16_t vendor_get_freq_cali(struct wifi_freq_cali_t *vendor_freq);
+int16_t vendor_get_power_cali(struct wifi_get_power_cali_t *power_cali);
+int16_t vendor_set_power_cali_flag(struct wifi_power_cali_flag_t *cali_flag);
+
+#endif
diff --git a/drivers/staging/bes2600/bes2600_log.c b/drivers/staging/bes2600/bes2600_log.c
new file mode 100644
index 000000000000..28f6ed1a1ada
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_log.c
@@ -0,0 +1,152 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "bes2600_log.h"
+#include "bes2600.h"
+
+struct bes2600_dbg_info {
+	int dbg_lvl;
+	char *module_name;
+};
+
+struct bes2600_dbg_info bes2600_dbg_tbl[BES2600_DBG_MAX] = {
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "download"},       /* BES2600_DBG_DOWNLOAD */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "niy"},            /* BES2600_DBG_NIY */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "sbus"},           /* BES2600_DBG_SBUS */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "init"},           /* BES2600_DBG_INIT */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "txrx_opt"},       /* BES2600_DBG_TXRX_OPT */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "txrx"},           /* BES2600_DBG_TXRX */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "queue"},          /* BES2600_DBG_QUEUE */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "spi"},            /* BES2600_DBG_SPI */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "sdio"},           /* BES2600_DBG_SDIO */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "usb"},            /* BES2600_DBG_USB */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "pm"},             /* BES2600_DBG_PM */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "sys"},            /* BES2600_DBG_SYS */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "bt"},             /* BES2600_DBG_BT */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "android"},        /* BES2600_DBG_ANDROID */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "bh"},             /* BES2600_DBG_BH */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "ap"},             /* BES2600_DBG_AP */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "sta"},            /* BES2600_DBG_STA */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "scan"},           /* BES2600_DBG_SCAN */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "itp"},            /* BES2600_DBG_ITP */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "test_mode"},      /* BES2600_DBG_TEST_MODE */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "tx_policy"},      /* BES2600_DBG_TX_POLICY */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "wsm"},            /* BES2600_DBG_WSM */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "roc"},            /* BES2600_DBG_ROC */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "chardev"},        /* BES2600_DBG_CHARDEV */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "factory"},        /* BES2600_DBG_FACTORY */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "epta"},           /* BES2600_DBG_EPTA */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "bes_pwr"},        /* BES2600_DBG_PWR */
+	{.dbg_lvl = BES2600_LOG_INFO, .module_name = "tx_loop"},        /* BES2600_DBG_TXLOOP */
+};
+
+int bes2600_get_dbg_lvl(int module)
+{
+	if(module >= ARRAY_SIZE(bes2600_dbg_tbl))
+		return BES2600_LOG_NONE;
+
+	return bes2600_dbg_tbl[module].dbg_lvl;
+}
+
+int bes2600_log_control_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+ssize_t bes2600_log_control_write(struct file *file,const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	u32 i;
+	char *buf = NULL;
+	char *module, *level;
+	unsigned long log_level;
+	char *str_none = "NONE";
+	char *str_err = "ERR";
+	char *str_warn = "WARN";
+	char *str_info = "INFO";
+	char *str_dbg = "DBG";
+
+	buf = kmalloc(count + 1, GFP_KERNEL);
+	if (copy_from_user(buf, user_buf, count))
+		return -EFAULT;
+	buf[count] = '\0';
+
+	module = strstr(buf, "module:");
+	level = strstr(buf, "level:");
+	if (module == NULL || level == NULL) {
+		bes2600_err(BES2600_DBG_CHARDEV, "module or level error. %s\n", buf);
+		goto out;
+	}
+	module += 7;
+	level += 6;
+	if (strncasecmp(str_none, level, strlen(str_none)) == 0)
+		log_level = BES2600_LOG_NONE;
+	else if(strncasecmp(str_err, level, strlen(str_err)) == 0)
+		log_level = BES2600_LOG_ERROR;
+	else if(strncasecmp(str_warn, level, strlen(str_warn)) == 0)
+		log_level = BES2600_LOG_WARN;
+	else if(strncasecmp(str_info, level, strlen(str_info)) == 0)
+		log_level = BES2600_LOG_INFO;
+	else if(strncasecmp(str_dbg, level, strlen(str_dbg)) == 0)
+		log_level = BES2600_LOG_DBG;
+	else {
+		bes2600_err(BES2600_DBG_CHARDEV, "module level error. %s\n", __func__);
+		goto out;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(bes2600_dbg_tbl); ++i) {
+		if (strncasecmp(bes2600_dbg_tbl[i].module_name, module, strlen(bes2600_dbg_tbl[i].module_name)) == 0) {
+			bes2600_dbg_tbl[i].dbg_lvl = log_level;
+			break;
+		}
+	}
+
+out:
+	kfree(buf);
+	return count;
+}
+
+ssize_t bes2600_log_control_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos)
+{
+	char *buf = NULL;
+	int i,ret;
+	int used_len = 0;
+	char str[10];
+
+	if (!count)
+		return -EINVAL;
+	buf = kmalloc(1024, GFP_KERNEL);
+	if(!buf) {
+		bes2600_err(BES2600_DBG_CHARDEV, "kmalloc error. %s\n", __func__);
+		return -ENOMEM;
+	}
+	for (i = 0; i < ARRAY_SIZE(bes2600_dbg_tbl); ++i) {
+		if(bes2600_dbg_tbl[i].dbg_lvl == BES2600_LOG_NONE) 
+			strcpy(str, "NONE");
+		else if (bes2600_dbg_tbl[i].dbg_lvl == BES2600_LOG_ERROR)
+			strcpy(str, "ERR");
+		else if (bes2600_dbg_tbl[i].dbg_lvl == BES2600_LOG_WARN)
+			strcpy(str, "WARN");
+		else if (bes2600_dbg_tbl[i].dbg_lvl == BES2600_LOG_INFO)
+			strcpy(str, "INFO");
+		else if (bes2600_dbg_tbl[i].dbg_lvl == BES2600_LOG_DBG)
+			strcpy(str, "DBG");
+		else
+			return -ENOMEM;
+		used_len += snprintf(buf + used_len, (1024 - used_len), "%-16s%s\n", bes2600_dbg_tbl[i].module_name, str);
+	}
+	ret = simple_read_from_buffer(user_buf, count, ppos, buf, used_len);
+	kfree(buf);
+	return ret;
+}
diff --git a/drivers/staging/bes2600/bes2600_log.h b/drivers/staging/bes2600/bes2600_log.h
new file mode 100644
index 000000000000..14f36810b072
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_log.h
@@ -0,0 +1,161 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __BES2600_LOG_H__
+#define __BES2600_LOG_H__
+
+/* Log Level Control */
+#define BES2600_LOG_NONE		0
+#define BES2600_LOG_ERROR		1
+#define BES2600_LOG_WARN		2
+#define BES2600_LOG_INFO		3
+#define BES2600_LOG_DBG			4
+
+/* Module Log Level Control */
+enum BES2600_DBG {
+	BES2600_DBG_DOWNLOAD = 0,
+	BES2600_DBG_NIY,
+	BES2600_DBG_SBUS,
+	BES2600_DBG_INIT,
+	BES2600_DBG_TXRX_OPT,
+	BES2600_DBG_TXRX,
+	BES2600_DBG_QUEUE,
+	BES2600_DBG_SPI,
+	BES2600_DBG_SDIO,
+	BES2600_DBG_USB,
+	BES2600_DBG_PM,
+	BES2600_DBG_SYS,
+	BES2600_DBG_BT,
+	BES2600_DBG_ANDROID,
+	BES2600_DBG_BH,
+	BES2600_DBG_AP,
+	BES2600_DBG_STA,
+	BES2600_DBG_SCAN,
+	BES2600_DBG_ITP,
+	BES2600_DBG_TEST_MODE,
+	BES2600_DBG_TX_POLICY,
+	BES2600_DBG_WSM,
+	BES2600_DBG_ROC,
+	BES2600_DBG_CHARDEV,
+	BES2600_DBG_FACTORY,
+	BES2600_DBG_EPTA,
+	BES2600_DBG_PWR,
+	BES2600_DBG_TXLOOP,
+
+	BES2600_DBG_MAX,
+};
+
+int bes2600_get_dbg_lvl(int module);
+
+int bes2600_log_control_open(struct inode *inode, struct file *file);
+
+ssize_t bes2600_log_control_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos);
+
+ssize_t bes2600_log_control_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos);
+
+#define GET_LOG_LVL(module)		bes2600_get_dbg_lvl(module)
+
+#define bes2600_dbg(module, ...)				\
+	do {							\
+		if (GET_LOG_LVL(module) >= BES2600_LOG_DBG)	\
+			printk(KERN_INFO __VA_ARGS__);		\
+	} while (0)
+
+#define bes2600_dbg_with_cond(cond, module, ...)		\
+	do {							\
+		if ((0 != (cond)) && 				\
+		    GET_LOG_LVL(module) >= BES2600_LOG_DBG)	\
+			printk(KERN_DEBUG __VA_ARGS__);		\
+	} while (0)
+
+#define bes2600_dbg_dump(module, desc, array, len)		\
+	do {							\
+		if (GET_LOG_LVL(module) >= BES2600_LOG_DBG)	\
+			print_hex_dump(KERN_DEBUG, 		\
+				desc, DUMP_PREFIX_NONE,		\
+				16, 1, array, 			\
+				len, false);			\
+	} while(0)
+
+#define bes2600_info(module, ...)				\
+	do {							\
+		if (GET_LOG_LVL(module) >= BES2600_LOG_INFO)	\
+			printk(KERN_INFO __VA_ARGS__);		\
+	} while (0)
+
+#define bes2600_info_with_cond(cond, module, ...)		\
+	do {							\
+		if ((0 != (cond)) && 				\
+		    GET_LOG_LVL(module) >= BES2600_LOG_INFO)	\
+			printk(KERN_INFO __VA_ARGS__);		\
+	} while (0)
+
+#define bes2600_info_dump(module, desc, array, len)		\
+	do {							\
+		if (GET_LOG_LVL(module) >= BES2600_LOG_INFO)	\
+			print_hex_dump(KERN_INFO, 		\
+				desc, DUMP_PREFIX_NONE,		\
+				16, 1, array, 			\
+				len, false);			\
+	} while(0)
+
+
+#define bes2600_warn(module, ...)				\
+	do {							\
+		if (GET_LOG_LVL(module) >= BES2600_LOG_WARN)	\
+			printk(KERN_WARNING __VA_ARGS__);	\
+	} while (0)
+
+#define bes2600_warn_with_cond(cond, module, ...)		\
+	do {							\
+		if ((0 != (cond)) && 				\
+		    GET_LOG_LVL(module) >= BES2600_LOG_WARN)	\
+			printk(KERN_WARNING __VA_ARGS__);	\
+	} while (0)
+
+#define bes2600_warn_dump(module, desc, array, len)		\
+	do {							\
+		if (GET_LOG_LVL(module) >= BES2600_LOG_WARN)	\
+			print_hex_dump(KERN_WARNING, 		\
+				desc, DUMP_PREFIX_NONE,		\
+				16, 1, array, 			\
+				len, false);			\
+	} while(0)
+
+#define bes2600_err(module, ...)				\
+	do {							\
+		if (GET_LOG_LVL(module) >= BES2600_LOG_ERROR)	\
+			printk(KERN_ERR __VA_ARGS__);		\
+	} while (0)
+
+#define bes2600_err_with_cond(cond, module, ...)		\
+	do {							\
+		if ((0 != (cond)) && 				\
+		    GET_LOG_LVL(module) >= BES2600_LOG_ERROR)	\
+			printk(KERN_ERR __VA_ARGS__);		\
+	} while (0)
+
+#define bes2600_err_dump(module, desc, array, len)		\
+	do {							\
+		if (GET_LOG_LVL(module) >= BES2600_LOG_ERROR)	\
+			print_hex_dump(KERN_ERR, 		\
+				desc, DUMP_PREFIX_NONE,		\
+				16, 1, array, 			\
+				len, false);			\
+	} while(0)
+
+#define STUB()							\
+	do {							\
+		bes2600_dbg(BES2600_DBG_NIY,			\
+			   "%s: STUB at line %d.\n",		\
+			   __func__, __LINE__);			\
+	} while (0)
+
+#endif
diff --git a/drivers/staging/bes2600/bes2600_plat.h b/drivers/staging/bes2600/bes2600_plat.h
new file mode 100644
index 000000000000..5838435b9315
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_plat.h
@@ -0,0 +1,51 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef BES2600_PLAT_H_INCLUDED
+#define BES2600_PLAT_H_INCLUDED
+
+#include <linux/ioport.h>
+
+struct bes2600_platform_data_spi {
+	u8 spi_bits_per_word;           /* REQUIRED */
+
+	/* All others are optional */
+	bool have_5ghz;
+	int reset;                     /* GPIO to RSTn signal (0 disables) */
+	int powerup;                   /* GPIO to POWERUP signal (0 disables) */
+	int irq_gpio;                  /* GPIO IRQ */
+	int host_wakeup_wlan;          /* GPIO to wakeup wlan */
+	const u8 *macaddr;  /* if NULL, use bes2600_mac_template module parameter */
+	const char *sdd_file;  /* if NULL, will use default for detected hw type */
+	void *priv;
+};
+
+struct bes2600_platform_data_sdio {
+	u16 ref_clk;                    /* REQUIRED (in KHz) */
+
+	/* All others are optional */
+	bool have_5ghz;
+	bool no_nptb;       /* SDIO hardware does not support non-power-of-2-blocksizes */
+	int reset;          /* GPIO to RSTn signal (0 disables) */
+	int powerup;        /* GPIO to POWERUP signal (0 disables) */
+	int wakeup;         /* GPIO to WAKEUP signal (0 disables) */
+	int host_wakeup;    /* wifi GPIO to WAKEUP host signal (0 disables) */
+	bool wlan_bt_hostwake_registered;/* wifi request_irq success or not */
+	int gpio_irq;       /* IRQ line or 0 to use SDIO IRQ */
+	int (*power_ctrl)(const struct bes2600_platform_data_sdio *pdata,
+			  bool enable); /* Control 3v3 / 1v8 supply */
+	int (*clk_ctrl)(const struct bes2600_platform_data_sdio *pdata,
+			bool enable); /* Control CLK32K */
+	const u8 *macaddr;  /* if NULL, use bes2600_mac_template module parameter */
+	const char *sdd_file;  /* if NULL, will use default for detected hw type */
+	bool wakeup_source; /* marks whether bes2600 is the wakeup souce or not */
+};
+
+#endif /* BES2600_PLAT_H_INCLUDED */
diff --git a/drivers/staging/bes2600/bes2600_sdio.c b/drivers/staging/bes2600/bes2600_sdio.c
new file mode 100644
index 000000000000..5252951cc3bb
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_sdio.c
@@ -0,0 +1,2563 @@
+/*
+ * Mac80211 SDIO driver for BES2600 device
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+ #define DEBUG 1
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/spinlock.h>
+#include <net/mac80211.h>
+#include <linux/scatterlist.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/version.h>
+#include <linux/of_gpio.h>
+
+#include "bes2600.h"
+#include "sbus.h"
+#include "bes2600_plat.h"
+#include "hwio.h"
+#include "bes2600_driver_mode.h"
+#include "bes_chardev.h"
+
+#ifdef PLAT_ROCKCHIP
+#include <linux/rfkill-wlan.h>
+#endif
+
+#ifdef PLAT_ALLWINNER
+#include <linux/sunxi-gpio.h>
+
+extern int sunxi_wlan_get_bus_index(void);
+extern void sunxi_mmc_rescan_card(unsigned id);
+extern void sw_mci_rescan_card(unsigned id, unsigned insert);
+extern void sunxi_wlan_set_power(bool on_off);
+#endif
+
+#if defined(BES2600_BOOT_UART_TO_SDIO)
+static struct sbus_ops bes2600_sdio_sbus_ops;
+extern int bes2600_boot_uart_to_sdio(struct sbus_ops *ops);
+#endif
+static void sdio_scan_work(struct work_struct *work);
+static void bes2600_sdio_power_down(struct sbus_priv *self);
+struct bes2600_platform_data_sdio *bes2600_get_platform_data(void);
+int bes2600_register_net_dev(struct sbus_priv *bus_priv);
+int bes2600_unregister_net_dev(struct sbus_priv *bus_priv);
+static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int falg);
+static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int falg);
+
+MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>");
+MODULE_DESCRIPTION("mac80211 BES2600 SDIO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("bes2600_wlan");
+
+struct sbus_priv {
+	struct sdio_func	*func;
+	struct bes2600_common	*core;
+	const struct bes2600_platform_data_sdio *pdata;
+	spinlock_t		lock;
+	sbus_irq_handler	irq_handler;
+	void			*irq_priv;
+	struct work_struct sdio_scan_work;
+	struct device *dev;
+	struct workqueue_struct *sdio_wq;
+	bool fw_started;
+	struct mutex io_mutex;
+	long unsigned int gpio_wakup_flags;
+	struct mutex sbus_mutex;
+	bool retune_protected;
+#ifdef BES_SDIO_RXTX_TOGGLE
+	u8 next_toggle;
+	int tx_data_toggle;
+	int rx_data_toggle;
+#endif
+#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
+	spinlock_t rx_queue_lock;
+	struct sk_buff_head rx_queue;
+	u8 *rx_buffer;
+	struct work_struct rx_work;
+	u32 rx_last_ctrl;
+	u32 rx_valid_ctrl;
+	u32 rx_total_ctrl_cnt;
+	u32 rx_continuous_ctrl_cnt;
+	u32 rx_zero_ctrl_cnt;
+	u32 rx_remain_ctrl_cnt;
+	u32 rx_data_cnt;
+	u32 rx_xfer_cnt;
+	u32 rx_proc_cnt;
+	long unsigned int last_irq_timestamp;
+	long unsigned int last_rx_data_timestamp;
+#endif
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
+	u8 *tx_buffer;
+	struct list_head tx_bufferlist;
+	struct kmem_cache *tx_bufferlistpool;
+	spinlock_t tx_bufferlock;
+	struct work_struct tx_work;
+	struct scatterlist tx_sg[BES_SDIO_TX_MULTIPLE_NUM + 1];
+	struct scatterlist tx_sg_nosignal[BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL + 1];
+	u32 tx_data_cnt;
+	u32 tx_xfer_cnt;
+	u32 tx_proc_cnt;
+	long unsigned int last_tx_data_timestamp;
+#endif
+#ifndef SDIO_HOST_ADMA_SUPPORT
+	u8 *single_gathered_buffer;
+#endif
+	bool unregister_in_process;
+};
+
+#define IS_DRIVER_VENDOR_CMD(X) ((X & 0x0C00) == 0x0C00)
+struct HI_MSG_HDR {
+	uint16_t MsgLen;
+	uint16_t MsgId;
+};
+
+enum DRIVER_TO_MCU_MSG_ST {
+	ST_ENTER,
+	ST_EXIT,
+};
+
+#define BES_VENDOR_ID	0xbe57
+#define BES_DEVICE_ID_2002	0x2002
+
+static const struct sdio_device_id bes2600_sdio_ids[] = {
+	{ SDIO_DEVICE(BES_VENDOR_ID, BES_DEVICE_ID_2002) },
+	{ /* end: all zeroes */			},
+};
+
+#ifdef BES2600_GPIO_WAKEUP_AP
+#ifdef PLAT_ALLWINNER
+extern int sunxi_wlan_get_oob_irq_flags(void);
+extern int sunxi_wlan_get_oob_irq(void);
+#endif
+static int bes2600_gpio_wakeup_ap_config(struct sbus_priv *priv);
+#endif
+
+/* sbus_ops implemetation */
+
+#ifdef CONFIG_BES2600_WLAN_BES
+static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
+{
+	unsigned mval = min(func->card->host->max_seg_size, func->card->host->max_blk_size);
+
+	if (func->card->quirks & MMC_QUIRK_BLKSZ_FOR_BYTE_MODE)
+		mval = min(mval, func->cur_blksize);
+	else
+		mval = min(mval, func->max_blksize);
+
+	return min(mval, 512u);
+}
+
+static int bes_sdio_memcpy_io_helper(struct sdio_func *func, int write, void *data_buf, unsigned size)
+{
+	int ret = 0;
+	unsigned remainder = size;
+	unsigned max_blocks, align_blocks, pads;
+
+#ifdef BES_SDIO_RXTX_TOGGLE
+	struct sbus_priv *self = NULL;
+#endif
+
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_data data;
+	struct scatterlist sg[2];
+
+#ifdef SDIO_HOST_ADMA_SUPPORT
+	struct scatterlist *next;
+#endif
+
+#ifdef SDIO_HOST_ADMA_SUPPORT
+	u8 *pad_buf = (u8 *)kmalloc(func->cur_blksize, GFP_KERNEL);
+	if (!pad_buf)
+		return -ENOMEM;
+#endif
+
+	if (!func || (func->num > 7) || (!data_buf) || (!size)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+#ifdef BES_SDIO_RXTX_TOGGLE
+	self = sdio_get_drvdata(func);
+	BUG_ON(!self);
+#endif
+
+	if (func->card->cccr.multi_block && size > sdio_max_byte_size(func) ) {
+		max_blocks = min(func->card->host->max_blk_count, 511u);
+		align_blocks = (size + func->cur_blksize - 1) / func->cur_blksize;
+		if (align_blocks > max_blocks) {
+			/* to be simplified, consider this should not
+			 * happen, and to be continued;
+			 */
+			bes2600_dbg(BES2600_DBG_SDIO, "%s warning to be continued, align=%d max=%d", __func__, align_blocks, max_blocks);
+			ret = -EINVAL;
+			goto out;
+		}
+		pads = align_blocks * func->cur_blksize - size;
+		bes2600_dbg(BES2600_DBG_SDIO, "%s sz=%u blk=%u pad=%u,dir=%d", __func__, size, align_blocks, pads, write);
+
+		memset(&mrq, 0, sizeof(mrq));
+		memset(&cmd, 0, sizeof(cmd));
+		memset(&data, 0, sizeof(data));
+
+		mrq.cmd = &cmd;
+		mrq.data = &data;
+
+		cmd.opcode = SD_IO_RW_EXTENDED;
+		cmd.arg = write ? 0x80000000 : 0x00000000;
+		cmd.arg |= func->num << 28;
+		cmd.arg |= 0x04000000;
+		cmd.arg |= size << 9;
+
+#ifdef BES_SDIO_RXTX_TOGGLE
+		if (likely(self->fw_started == true)) {
+			cmd.arg &= ~(1 << 25);
+			if (write) {
+				cmd.arg |= ((self->tx_data_toggle & 0x1) << 25);
+				++self->tx_data_toggle;
+			} else {
+				cmd.arg |= ((self->rx_data_toggle & 0x1) << 25);
+				++self->rx_data_toggle;
+			}
+		}
+#endif
+
+		cmd.arg |= (0x08000000 | align_blocks);
+		cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+
+		data.blksz = func->cur_blksize;
+		data.blocks = align_blocks;
+		data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+
+		data.sg = sg;
+		data.sg_len = 1;
+#ifdef SDIO_HOST_ADMA_SUPPORT
+		sg_init_table(sg, 2);
+		sg_set_buf(sg, data_buf, size);
+		if (pads) {
+			next = sg_next(sg);
+			sg_set_buf(next, pad_buf, pads);
+			data.sg_len = 2;
+		}
+#else
+		sg_init_table(sg, 1);
+		if (unlikely(pads)) {
+			if (unlikely(size > MAX_SDIO_TRANSFER_LEN)) {
+				WARN_ON(1);
+				return -EINVAL;
+			}
+			if (write)
+				memcpy(self->single_gathered_buffer, data_buf, size);
+			sg_set_buf(sg, self->single_gathered_buffer, size + pads);
+		} else {
+			sg_set_buf(sg, data_buf, size);
+		}
+#endif
+
+		mmc_set_data_timeout(&data, func->card);
+
+		mmc_wait_for_req(func->card->host, &mrq);
+
+		if (cmd.error){
+			ret = cmd.error;
+			goto out;
+		}
+		if (data.error) {
+			ret = data.error;
+			goto out;
+		}
+		if (cmd.resp[0] & R5_ERROR) {
+			ret = -EIO;
+			goto out;
+		}
+		if (cmd.resp[0] & R5_FUNCTION_NUMBER) {
+			ret = -EINVAL;
+			goto out;
+		}
+		if (cmd.resp[0] & R5_OUT_OF_RANGE) {
+			ret = -ERANGE;
+			goto out;
+		}
+
+#ifndef SDIO_HOST_ADMA_SUPPORT
+		if (pads && (!write)) {
+			memcpy(data_buf, self->single_gathered_buffer, size);
+		}
+#endif
+
+	} else {
+		while (remainder) {
+			size = min(remainder, sdio_max_byte_size(func));
+
+			bes2600_dbg(BES2600_DBG_SDIO, "%s size=%d dir=%d", __func__, size, write);
+			if (write) {
+#ifndef BES_SDIO_RXTX_TOGGLE
+				ret = sdio_memcpy_toio(func, size, data_buf, size);
+#else
+				if (likely(self->fw_started == true)) {
+					ret = sdio_memcpy_toio(func, size | ((self->tx_data_toggle & 0x1) << 16), data_buf, size);
+					++self->tx_data_toggle;
+				} else {
+					ret = sdio_memcpy_toio(func, size, data_buf, size);
+				}
+#endif
+			} else {
+#ifndef BES_SDIO_RXTX_TOGGLE
+				ret = sdio_memcpy_fromio(func, data_buf, size, size);
+#else
+				if (likely(self->fw_started == true)) {
+					ret = sdio_memcpy_fromio(func, data_buf, size | ((self->rx_data_toggle & 0x1) << 16), size);
+					++self->rx_data_toggle;
+				} else {
+					ret = sdio_memcpy_fromio(func, data_buf, size, size);
+				}
+#endif
+			}
+			if (ret)
+				goto out;
+
+			remainder -= size;
+			data_buf += size;
+		}
+	}
+out:
+#ifdef SDIO_HOST_ADMA_SUPPORT
+	kfree(pad_buf);
+#endif
+	if (ret) {
+		bes2600_err(BES2600_DBG_SDIO, "%s, err=%d(%d:%p:%d)",
+				__func__, ret, func->num, data_buf, size);
+#ifdef BES_SDIO_RXTX_TOGGLE
+				if (self && self->fw_started == true) {
+					if (write)
+						--self->tx_data_toggle;
+					else
+						--self->rx_data_toggle;
+					bes2600_err(BES2600_DBG_SDIO, "%s,toggle count:%u,%u\n", __func__, self->tx_data_toggle, self->rx_data_toggle);
+				}
+#endif
+	}
+	return ret;
+}
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,19,56))
+static void sdio_retune_hold_now(struct sdio_func *func)
+{
+	func->card->host->retune_now = 0;
+	func->card->host->hold_retune += 1;
+}
+
+static void sdio_retune_release(struct sdio_func *func)
+{
+	if (func->card->host->hold_retune)
+		func->card->host->hold_retune -= 1;
+	else
+		WARN_ON(1);
+}
+#endif
+
+static int bes2600_sdio_memcpy_fromio(struct sbus_priv *self,
+				     unsigned int addr,
+				     void *dst, int count)
+{
+	return bes_sdio_memcpy_io_helper(self->func, 0, dst, count);
+}
+
+static int bes2600_sdio_memcpy_toio(struct sbus_priv *self,
+				   unsigned int addr,
+				   const void *src, int count)
+{
+	return bes_sdio_memcpy_io_helper(self->func, 1, (void *)src, count);
+}
+
+static void bes2600_sdio_lock(struct sbus_priv *self)
+{
+	sdio_claim_host(self->func);
+}
+
+static void bes2600_sdio_unlock(struct sbus_priv *self)
+{
+	sdio_release_host(self->func);
+}
+
+/* bes sdio slave regs can only be accessed by command52
+ * if a WORD or DWORD reg wants to be accessed,
+ * please combine the results of multiple command52
+ */
+static int bes2600_sdio_reg_read(struct sbus_priv *self, u32 reg,
+					void *dst, int count)
+{
+	int ret = 0;
+	if (count <= 0 || !dst)
+		return -EINVAL;
+	while(count && !ret) {
+		*(u8 *)dst = sdio_readb(self->func, reg, &ret);
+		dst ++;
+		reg ++;
+		count--;
+	}
+	return ret;
+
+}
+
+static int bes2600_sdio_reg_write(struct sbus_priv *self, u32 reg,
+					const void *src, int count)
+{
+	int ret = 0;
+	if (count <= 0 || !src)
+		return -EINVAL;
+	while (count && !ret) {
+		sdio_writeb(self->func, *(u8 *)src, reg, &ret);
+		src ++;
+		reg ++;
+		count --;
+	}
+	return ret;
+}
+
+#ifndef CONFIG_BES2600_USE_GPIO_IRQ
+static void bes2600_sdio_irq_handler(struct sdio_func *func)
+{
+	struct sbus_priv *self = sdio_get_drvdata(func);
+	unsigned long flags;
+	bes2600_dbg(BES2600_DBG_SDIO, "\n %s called, fw_started:%d \n",
+			 __func__, self->fw_started);
+
+	if (WARN_ON(!self)) {
+		return;
+	}
+
+	if (likely(self->fw_started && self->core)) {
+		queue_work(self->sdio_wq, &self->rx_work);
+		self->last_irq_timestamp = jiffies;
+	} else if(self->irq_handler) {
+		spin_lock_irqsave(&self->lock, flags);
+		self->irq_handler(self->irq_priv);
+		spin_unlock_irqrestore(&self->lock, flags);
+	}
+}
+#else /* CONFIG_BES2600_USE_GPIO_IRQ */
+static u32 bes2600_gpio_irq_handler(void *dev_id)
+{
+	struct sbus_priv *self = (struct sbus_priv *)dev_id;
+
+	bes2600_dbg(BES2600_DBG_SDIO, "\n %s called \n", __func__);
+	BUG_ON(!self);
+	if (self->irq_handler)
+		self->irq_handler(self->irq_priv);
+	return 0;
+}
+
+static int bes2600_request_irq(struct sbus_priv *self,
+			      u32 handler)
+{
+	int ret = 0;
+	int func_num;
+	const struct resource *irq = self->pdata->irq;
+	u8 cccr;
+	int ret0 = 0;
+
+#ifdef PLAT_ALLWINNER_SUN6I   // for Allwinner we define plat specific API to allocate IRQ line
+		aw_gpio_irq_handle = sw_gpio_irq_request(irq->start, TRIG_EDGE_POSITIVE, (peint_handle)handler, self);
+		if (aw_gpio_irq_handle == 0) {
+			bes2600_err(BES2600_DBG_SDIO, "[%s]  err sw_gpio_irq_request..   :%d\n", __func__,aw_gpio_irq_handle);
+			return -1;
+		} else
+		{
+			ret = 0;
+		}
+#endif  // PLAT_ALLWINNER_SUN6I
+
+	/* Hack to access Fuction-0 */
+	func_num = self->func->num;
+	self->func->num = 0;
+
+	cccr = sdio_readb(self->func, SDIO_CCCR_IENx, &ret);
+	if (WARN_ON(ret))
+		goto set_func;
+
+	/* Master interrupt enable ... */
+	cccr |= BIT(0);
+
+	/* ... for our function */
+	cccr |= BIT(func_num);
+
+	sdio_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
+	if (WARN_ON(ret))
+		goto set_func;
+
+	/* Restore the WLAN function number */
+	self->func->num = func_num;
+	return 0;
+
+set_func:
+	//AW judge sdio read write timeout, 1s
+	ret0 = sw_mci_check_r1_ready(self->func->card->host, 1000);
+	if (ret0 != 0)
+		bes2600_err(BES2600_DBG_SDIO, ("%s data timeout.\n", __FUNCTION__));
+
+	self->func->num = func_num;
+#ifdef PLAT_ALLWINNER_SUN6I
+	sw_gpio_irq_free(aw_gpio_irq_handle);
+	aw_gpio_irq_handle = 0;
+#endif
+	bes2600_err(BES2600_DBG_SDIO, "[%s]  fail exiting sw_gpio_irq_request..   :%d\n",__func__, ret);
+	return ret;
+}
+#endif /* CONFIG_BES2600_USE_GPIO_IRQ */
+
+static int bes2600_sdio_irq_subscribe(struct sbus_priv *self,
+				     sbus_irq_handler handler,
+				     void *priv)
+{
+	int ret;
+	unsigned long flags;
+
+	if (!handler)
+		return -EINVAL;
+
+	spin_lock_irqsave(&self->lock, flags);
+	self->irq_priv = priv;
+	self->irq_handler = handler;
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	bes2600_dbg(BES2600_DBG_SDIO,  "SW IRQ subscribe\n");
+	sdio_claim_host(self->func);
+#ifndef CONFIG_BES2600_USE_GPIO_IRQ
+	ret = sdio_claim_irq(self->func, bes2600_sdio_irq_handler);
+#else
+	mdelay(10);
+	ret = bes2600_request_irq(self, bes2600_gpio_irq_handler);
+#endif
+	sdio_release_host(self->func);
+	return ret;
+}
+
+static int bes2600_sdio_irq_unsubscribe(struct sbus_priv *self)
+{
+	int ret = 0;
+	unsigned long flags;
+#ifdef CONFIG_BES2600_USE_GPIO_IRQ
+	const struct resource *irq = self->pdata->irq;
+#ifdef PLAT_ALLWINNER_SUN6I
+	sw_gpio_irq_free(aw_gpio_irq_handle);
+	aw_gpio_irq_handle = 0;
+#endif
+#endif
+
+	WARN_ON(!self->irq_handler);
+	if (!self->irq_handler)
+		return 0;
+
+	bes2600_dbg(BES2600_DBG_SDIO, "SW IRQ unsubscribe\n");
+
+/*
+#ifndef CONFIG_BES2600_USE_GPIO_IRQ
+	sdio_claim_host(self->func);
+	ret = sdio_release_irq(self->func);
+	sdio_release_host(self->func);
+#else
+	free_irq(irq->start, self);
+#endif  //CONFIG_BES2600_USE_GPIO_IRQ
+*/
+
+	spin_lock_irqsave(&self->lock, flags);
+	self->irq_priv = NULL;
+	self->irq_handler = NULL;
+	spin_unlock_irqrestore(&self->lock, flags);
+
+	return ret;
+}
+
+static int bes2600_sdio_off(const struct bes2600_platform_data_sdio *pdata)
+{
+	bes2600_info(BES2600_DBG_SDIO, "%s enter\n", __func__);
+
+#if defined(PLAT_ALLWINNER)
+	sunxi_wlan_set_power(false);
+#endif
+
+#if defined(PLAT_ROCKCHIP)
+	rockchip_wifi_set_carddetect(0);
+	rockchip_wifi_power(0);
+#endif
+
+	if (pdata == NULL)
+		return 0;
+
+#ifdef BES2600_INDEPENDENT_EVB
+	if (gpio_is_valid(pdata->powerup)) {
+		gpio_direction_output(pdata->powerup, 0);
+	}
+#endif
+
+	return 0;
+}
+
+static int bes2600_sdio_on(const struct bes2600_platform_data_sdio *pdata)
+{
+
+	bes2600_info(BES2600_DBG_SDIO, "%s enter\n", __func__);
+
+#if defined(PLAT_ALLWINNER)
+	sunxi_wlan_set_power(true);
+#endif
+
+#ifdef PLAT_ROCKCHIP
+	rockchip_wifi_power(0);
+	rockchip_wifi_power(1);
+	bes2600_chrdev_start_bus_probe();
+	rockchip_wifi_set_carddetect(1);
+#endif
+
+	if (pdata != NULL) {
+#ifdef BES2600_INDEPENDENT_EVB
+		if (gpio_is_valid(pdata->powerup)) {
+			gpio_direction_output(pdata->powerup, 1);
+		}
+#endif
+	}
+
+#if defined(BES2600_BOOT_UART_TO_SDIO)
+	return bes2600_boot_uart_to_sdio(&bes2600_sdio_sbus_ops);
+#endif
+
+	return 0;
+}
+
+static size_t bes2600_sdio_align_size(struct sbus_priv *self, size_t size)
+{
+	size_t aligned  = size;
+	if (self->func->cur_blksize > size)
+	       aligned = sdio_align_size(self->func, size);
+	else
+		aligned = (aligned + 3) & (~3);
+
+	return aligned;
+}
+
+int bes2600_sdio_set_block_size(struct sbus_priv *self, size_t size)
+{
+	return sdio_set_block_size(self->func, size);
+}
+
+void sdio_work_debug(struct sbus_priv *self)
+{
+	u8 cfg;
+	int ret;
+	bes2600_err(BES2600_DBG_SDIO, "%s now=%u last irq timestamp=%u\n", __func__,
+			(u32)jiffies_to_msecs(jiffies), jiffies_to_msecs(self->last_irq_timestamp));
+	bes2600_err(BES2600_DBG_SDIO, "%s rx ctrl: total=%u continuous=%u xfer=%u remain=%u zero=%u last=%x(%x) next=%d\n", __func__,
+			self->rx_total_ctrl_cnt, self->rx_continuous_ctrl_cnt, self->rx_xfer_cnt, self->rx_remain_ctrl_cnt, self->rx_zero_ctrl_cnt,
+			self->rx_last_ctrl, self->rx_valid_ctrl, self->next_toggle);
+	bes2600_err(BES2600_DBG_SDIO, "%s rx: last timestamp=%u, total=%u(%u), proc=%u\n", __func__,
+			(u32)jiffies_to_msecs(self->last_rx_data_timestamp),
+			self->rx_data_cnt, self->rx_xfer_cnt, self->rx_proc_cnt);
+	bes2600_err(BES2600_DBG_SDIO, "%s tx: last timestamp=%u, total=%u,%u, proc=%u\n", __func__,
+			(u32)jiffies_to_msecs(self->last_tx_data_timestamp),
+			self->tx_data_cnt, self->tx_xfer_cnt, self->tx_proc_cnt);
+	mutex_lock(&self->sbus_mutex);
+	sdio_claim_host(self->func);
+	bes2600_sdio_reg_read(self, BES_TX_CTRL_REG_ID + 1, &cfg, 1);
+	bes2600_err(BES2600_DBG_SDIO, "realtime ctrl=%x\n", cfg);
+	cfg = BES_HOST_INT | BES_SUBSYSTEM_WIFI_DEBUG;
+	sdio_writeb(self->func, 0, BES_HOST_INT_REG_ID + 1, &ret);
+	sdio_writeb(self->func, cfg, BES_HOST_INT_REG_ID, &ret);
+	sdio_release_host(self->func);
+	mutex_unlock(&self->sbus_mutex);
+}
+
+#ifndef BES_SDIO_OPTIMIZED_LEN
+static u8 const crc8_table[256] = {
+	0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
+	0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
+	0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
+	0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
+	0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
+	0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
+	0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
+	0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
+	0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
+	0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
+	0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
+	0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
+	0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
+	0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
+	0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
+	0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
+	0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
+	0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
+	0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
+	0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
+	0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
+	0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
+	0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
+	0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
+	0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
+	0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
+	0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
+	0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
+	0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
+	0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
+	0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
+	0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
+};
+
+static u8 bes_crc8(const u8 *data, unsigned len)
+{
+	u8 crc = 0;
+
+	while(len--)
+		crc = crc8_table[crc ^ *data++];
+
+	return crc;
+}
+#endif
+
+int bes2600_sdio_read_ctrl(struct sbus_priv *self, u32 *ctrl_reg)
+{
+	u8 data[4];
+	#ifndef BES_SDIO_OPTIMIZED_LEN
+	u8 check;
+	u16 pkts, len;
+	#endif
+	int ret = 0, again = 0;
+	*ctrl_reg = 0;
+
+	/* clear sdio slave gen interrupt */
+	ret = bes2600_sdio_reg_read(self, BES_TX_CTRL_REG_ID + 1, data, 1);
+	#ifndef BES_SDIO_OPTIMIZED_LEN
+	ret = bes2600_sdio_reg_read(self, BES_TX_NEXT_LEN_REG_ID, data, 4);
+	#endif
+	if (unlikely(ret)) {
+		bes2600_err(BES2600_DBG_SDIO, "[SBUS] Failed(%d) to read control register.\n", ret);
+		return ret;
+	}
+	self->rx_total_ctrl_cnt++;
+
+	#ifndef BES_SDIO_OPTIMIZED_LEN
+	check = bes_crc8((const u8 *)data, 3);
+	if (data[3] == check) {
+		/* length field crc8 pass */
+		*ctrl_reg = *(u32 *)data;
+		if (((data[2] >> 7) & 0x1) == self->next_toggle) {
+			/* toggle valid */
+			*ctrl_reg &= (~0xff800000);
+		} else {
+			/* last toggle */
+			*ctrl_reg = 0;
+			again = 1;
+		}
+
+		if (*ctrl_reg) {
+			/* length field valid */
+			again = 1;
+			pkts = ((*ctrl_reg) >> 16) & 0x7f;
+			len = (*ctrl_reg) & 0xffff;
+			if (pkts && len) {
+				self->next_toggle_debug = *(u32 *)data;
+				self->next_toggle ^= 1;
+			} else {
+				*ctrl_reg = 0;
+			}
+		}
+	} else {
+		/* length field crc fail */
+		//pr_err("%s, crc err:%x,%x,%x,%x,%x\n", __func__, data[0], data[1], data[2], data[3], check);
+		//msleep(1);
+		*ctrl_reg = 0;
+		again = 1;
+	}
+	#else
+	if (data[0] & 0x7f) {
+		/* length field valid */
+		again = 1;
+		if (((data[0] >> 7) & 0x1) == self->next_toggle) {
+			*ctrl_reg = (data[0] & 0x7f) << 9;
+			self->rx_valid_ctrl = data[0];
+			if (self->rx_last_ctrl && (((self->rx_last_ctrl >> 7) & 0x1) != self->next_toggle))
+				self->rx_continuous_ctrl_cnt++;
+			self->next_toggle ^= 1;
+		} else {
+			self->rx_remain_ctrl_cnt++;
+		}
+	} else {
+		/* distinguish zero true or false */
+		self->rx_zero_ctrl_cnt++;
+		ret = bes2600_sdio_reg_read(self, BES_TX_CTRL_REG_ID, &data[1], 1);
+		if (!ret && (data[1] & 0x01))
+			again = 0;
+		else
+			again = 1;
+	}
+	self->rx_last_ctrl = data[0];
+	#endif
+	return again;
+}
+
+#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
+
+static int bes2600_sdio_packets_check(u32 ctrl_reg, u8 *packets)
+{
+	int i;
+	u16 single, total_cal = 0;
+	u16 packets_length;
+	struct HI_MSG_HDR *pMsg;
+
+	/* bit 23-16 indicate count of packets */
+	u32 packets_cnt;
+	#ifndef BES_SDIO_OPTIMIZED_LEN
+	packets_cnt = PACKET_COUNT(ctrl_reg);
+	if (WARN_ON(packets_cnt > BES_SDIO_RX_MULTIPLE_NUM))
+		return -200;
+	#else
+	packets_cnt = BES_SDIO_RX_MULTIPLE_NUM;
+	#endif
+
+	/* bit 15-0 indicate totoal length of packets */
+	packets_length = PACKET_TOTAL_LEN(ctrl_reg);
+
+	/* first 32-bit: addr in mcu;
+	 * second 32-bit: packet length;
+	 * next: data
+	 */
+	for (i = 0; i < packets_cnt; i++) {
+		pMsg = (struct HI_MSG_HDR *)(packets + total_cal);
+		bes2600_dbg(BES2600_DBG_SDIO, "%s, %x,%x\n", __func__, pMsg->MsgId, pMsg->MsgLen);
+		single = pMsg->MsgLen;
+		single = (single + 3) & (~0x3);
+		if (unlikely(single > 1632)) {
+			bes2600_warn(BES2600_DBG_SDIO, "%s %d,len=%u,%dth,total=%u,%u\n", __func__, __LINE__, single, i,
+					packets_length, total_cal);
+			if (i >= 1) {
+				return -201;
+			}
+		}
+		total_cal += single;
+		#ifdef BES_SDIO_OPTIMIZED_LEN
+		if ((!pMsg->MsgLen) || (total_cal == packets_length)) {
+			//pr_info("%s, contain %d packets\n", __func__, i);
+			break;
+		}
+		#endif
+	}
+	bes2600_dbg(BES2600_DBG_SDIO, "%s, %d,%u,%u\n", __func__, packets_cnt, packets_length, total_cal);
+
+	#ifndef BES_SDIO_OPTIMIZED_LEN
+	if (WARN_ON(packets_length != total_cal)) {
+		return -202;
+	}
+	#else
+	if (packets_length < total_cal) {
+		pr_err("%s,%d pkt len=%u, total len=%u", __func__, __LINE__, packets_length, total_cal);
+		return -202;
+	}
+	#endif
+
+	return 0;
+}
+
+static int bes2600_sdio_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8 *data)
+{
+	int i, alloc_retry = 0;
+	#ifndef BES_SDIO_OPTIMIZED_LEN
+	u8 packets_cnt = PACKET_COUNT(ctrl_reg);
+	#else
+	u8 packets_cnt = BES_SDIO_RX_MULTIPLE_NUM;
+	#endif
+	u16 packet_len, pos = 0;
+	struct sk_buff *skb;
+
+	for (i = 0; i < packets_cnt; i++) {
+		packet_len = ((struct HI_MSG_HDR *)&(data[pos]))->MsgLen;
+		#ifdef BES_SDIO_OPTIMIZED_LEN
+		if (!packet_len)
+			break;
+		#endif
+		do {
+			skb = dev_alloc_skb(packet_len);
+			if (likely(skb))
+				break;
+			bes2600_warn(BES2600_DBG_SDIO, "%s,%d no memory and sleep\n", __func__, __LINE__);
+			msleep(100);
+			++alloc_retry;
+		} while(alloc_retry < 10);
+		if (WARN_ON(!skb)) {
+			return -ENOMEM;
+		}
+		skb_trim(skb, 0);
+		skb_put(skb, packet_len);
+		memcpy(skb->data, &data[pos], packet_len);
+		bes2600_dbg(BES2600_DBG_SDIO, "%s, %d,%d\n", __func__, packet_len, pos);
+		spin_lock(&self->rx_queue_lock);
+		skb_queue_tail(&self->rx_queue, skb);
+		self->rx_data_cnt++;
+		spin_unlock(&self->rx_queue_lock);
+		packet_len = (packet_len + 3) & (~0x3);
+		pos += packet_len;
+		#ifdef BES_SDIO_OPTIMIZED_LEN
+		if (pos == PACKET_TOTAL_LEN(ctrl_reg))
+			break;
+		#endif
+	}
+	return 0;
+}
+
+static void sdio_rx_work(struct work_struct *work)
+{
+	int ret, again = 0, retry = 0, crc_retry = 0;
+	u32 ctrl_reg = 0;
+	int total_len;
+	struct sbus_priv *self = container_of(work, struct sbus_priv, rx_work);
+	u8 *buf = self->rx_buffer;
+
+	/* don't read/write sdio when sdio error */
+	if(bes2600_chrdev_is_bus_error())
+		return;
+
+	bes2600_gpio_wakeup_mcu(self, GPIO_WAKE_FLAG_SDIO_RX);
+
+	do {
+		bes2600_sdio_lock(self);
+		again = bes2600_sdio_read_ctrl(self, &ctrl_reg);
+
+		if(again == -EBUSY || again == -ETIMEDOUT) {
+			bes2600_err(BES2600_DBG_SDIO, "%s sdio read error\n", __func__);
+			bes2600_sdio_unlock(self);
+			goto failed;
+		}
+
+		total_len = PACKET_TOTAL_LEN(ctrl_reg);
+		if (!total_len) {
+			bes2600_sdio_unlock(self);
+			if ((again == 1) && retry <= 5) {
+				retry++;
+				continue;
+			} else {
+				break;
+			}
+		}
+
+		do {
+			ret = bes2600_sdio_memcpy_fromio(self, 0, buf, total_len);
+			if (likely(ret != -84)) {
+				crc_retry = 0;
+				break;
+			} else {
+				crc_retry++;
+				bes2600_err(BES2600_DBG_SDIO, "%s sdio read crc error(%d)\n", __func__, crc_retry);
+			}
+		} while (crc_retry <= 10);
+		if (self->retune_protected == true) {
+			sdio_retune_release(self->func);
+			self->retune_protected = false;
+		}
+		bes2600_sdio_unlock(self);
+		if (ret) {
+			bes2600_err(BES2600_DBG_SDIO, "%s,%d error=%d\n", __func__, __LINE__, ret);
+			sdio_work_debug(self);
+			goto failed;
+		}
+		retry = 0;
+		self->rx_xfer_cnt++;
+		self->last_rx_data_timestamp = jiffies;
+
+		if ((ret = bes2600_sdio_packets_check(ctrl_reg, buf))) {
+			bes2600_err(BES2600_DBG_SDIO, "%s,%d error=%d\n", __func__, __LINE__, ret);
+			sdio_work_debug(self);
+			goto failed;
+		}
+
+		if ((ret = bes2600_sdio_extract_packets(self, ctrl_reg, buf))) {
+			bes2600_err(BES2600_DBG_SDIO, "%s,%d error=%d\n", __func__, __LINE__, ret);
+			goto failed;
+		}
+
+		ctrl_reg = 0;
+
+		if (likely(self->irq_handler)) {
+			self->irq_handler(self->irq_priv);
+		} else {
+			bes2600_err(BES2600_DBG_SDIO, "%s,%d\n", __func__, __LINE__);
+			goto failed;
+		}
+
+	} while (again);
+
+	bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX);
+	return;
+
+failed:
+	bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_RX);
+	bes2600_chrdev_wifi_force_close(self->core, false);
+	WARN_ON(1);
+}
+
+static void sdio_scan_work(struct work_struct *work)
+{
+#ifdef PLAT_ALLWINNER
+	//sw_mci_rescan_card(AW_SDIOID, 1);
+	sunxi_mmc_rescan_card(sunxi_wlan_get_bus_index());
+#endif
+
+#ifdef PLAT_ROCKCHIP
+	rockchip_wifi_set_carddetect(1);
+#endif
+	bes2600_info(BES2600_DBG_SDIO, "%s: power down, rescan card\n", __FUNCTION__);
+}
+
+static void *bes2600_sdio_pipe_read(struct sbus_priv *self)
+{
+	struct sk_buff *skb;
+
+	if(bes2600_chrdev_is_bus_error()) {
+		return bes2600_tx_loop_read(self->core);
+	}
+
+	spin_lock(&self->rx_queue_lock);
+	skb = skb_dequeue(&self->rx_queue);
+	if (skb)
+		self->rx_proc_cnt++;
+	spin_unlock(&self->rx_queue_lock);
+	if (likely(self->fw_started == true &&
+		!bes2600_pwr_device_is_idle(self->core) &&
+		self->core->hw_bufs_used > 0))
+		if (!skb)
+			queue_work(self->sdio_wq, &self->rx_work);
+	return skb;
+}
+
+#endif
+
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
+
+struct bes_sdio_tx_list_t {
+	struct list_head node;
+	u8 *buf;
+	u32 len;
+};
+
+static int bes_sdio_memcpy_to_io_helper(struct sdio_func *func, unsigned origin_size, struct scatterlist *sg, u32 sg_num)
+{
+	int ret = 0;
+	u32 align_blocks;
+	u32 compensate;
+	unsigned size = origin_size & (~0x3);
+
+#ifdef BES_SDIO_RXTX_TOGGLE
+	struct sbus_priv *self = NULL;
+#endif
+
+#ifdef SDIO_HOST_ADMA_SUPPORT
+	u32 sg_compensate_num = sg_num;
+#else
+	int i, already, separate;
+	struct scatterlist sg_copy, *element;
+#endif
+
+	struct mmc_request mrq;
+	struct mmc_command cmd;
+	struct mmc_data data;
+
+#ifdef BES_SDIO_RXTX_TOGGLE
+	self = sdio_get_drvdata(func);
+#endif
+
+	if (size && func->card->cccr.multi_block) {
+
+		align_blocks = (size + func->cur_blksize - 1) / func->cur_blksize;
+		bes2600_dbg(BES2600_DBG_SDIO, "%s sz=%u blk=%u", __func__, size, align_blocks);
+		compensate = align_blocks * func->cur_blksize - size;
+#ifdef SDIO_HOST_ADMA_SUPPORT
+		if (compensate) {
+			sg_set_buf(&sg[sg_num], self->tx_buffer, compensate);
+			sg_compensate_num = sg_num + 1;
+		}
+		sg_mark_end(&sg[sg_compensate_num - 1]);
+#else
+		if (WARN_ON(size + compensate > MAX_SDIO_TRANSFER_LEN))
+			return -EINVAL;
+		already = 0;
+		separate = origin_size & 3;
+		if (separate == 3)
+			separate = 1632;
+		else
+			separate = (separate + 1) * 512;
+		for (i = 0; i < sg_num; i++) {
+			element = &sg[i];
+			memcpy(&self->single_gathered_buffer[already], page_address(sg_page(element)) + element->offset,
+					element->length);
+			already += separate;
+		}
+		sg_set_buf(&sg_copy, self->single_gathered_buffer, size + compensate);
+#endif
+
+		memset(&mrq, 0, sizeof(mrq));
+		memset(&cmd, 0, sizeof(cmd));
+		memset(&data, 0, sizeof(data));
+
+		mrq.cmd = &cmd;
+		mrq.data = &data;
+
+		cmd.opcode = SD_IO_RW_EXTENDED;
+		cmd.arg = 0x80000000;
+		cmd.arg |= func->num << 28;
+		cmd.arg |= 0x04000000;
+		cmd.arg |= (origin_size) << 9;
+
+#ifdef BES_SDIO_RXTX_TOGGLE
+		if (likely(self->fw_started == true)) {
+			cmd.arg &= ~(1 << 25);
+			cmd.arg |= ((self->tx_data_toggle & 0x1) << 25);
+			++self->tx_data_toggle;
+		}
+#endif
+
+		cmd.arg |= 0x08000000 | align_blocks;
+		cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+
+		data.blksz = func->cur_blksize;
+		data.blocks = align_blocks;
+		data.flags = MMC_DATA_WRITE;
+
+#ifdef SDIO_HOST_ADMA_SUPPORT
+		data.sg = sg;
+		data.sg_len = sg_compensate_num;
+#else
+		data.sg = &sg_copy;
+		data.sg_len = 1;
+#endif
+
+		mmc_set_data_timeout(&data, func->card);
+
+		mmc_wait_for_req(func->card->host, &mrq);
+
+		if (cmd.error){
+			ret = cmd.error;
+			goto out;
+		}
+		if (data.error) {
+			ret = data.error;
+			goto out;
+		}
+		if (cmd.resp[0] & R5_ERROR) {
+			ret = -EIO;
+			goto out;
+		}
+		if (cmd.resp[0] & R5_FUNCTION_NUMBER) {
+			ret = -EINVAL;
+			goto out;
+		}
+		if (cmd.resp[0] & R5_OUT_OF_RANGE) {
+			ret = -ERANGE;
+			goto out;
+		}
+	} else {
+		bes2600_err(BES2600_DBG_SDIO, "%s,%d (%u)\n", __func__, __LINE__, size);
+		ret = -EINVAL;
+	}
+out:
+#ifdef BES_SDIO_RXTX_TOGGLE
+	if (unlikely(ret))
+		self->tx_data_toggle--;
+#endif
+	return ret;
+}
+
+static void sdio_tx_work(struct work_struct *work)
+{
+	int ret, crc_retry = 0;
+	u32 blks, cur_blk = 0, align, total_len = 0, scatters = 0;
+	struct list_head proc_list;
+	struct bes_sdio_tx_list_t *tx_buffer, *temp;
+	struct sbus_priv *self = container_of(work, struct sbus_priv, tx_work);
+	struct scatterlist *sg = NULL;
+	int bes_sdio_tx_multiple_num;
+	struct HI_MSG_HDR *pMsg;
+	enum DRIVER_TO_MCU_MSG_ST driver_to_mcu = ST_EXIT;
+
+	/* don't read/write sdio when sdio error */
+	if(bes2600_chrdev_is_bus_error())
+		return;
+
+	if (bes2600_chrdev_is_signal_mode()) {
+		sg = self->tx_sg;
+		bes_sdio_tx_multiple_num = BES_SDIO_TX_MULTIPLE_NUM;
+	} else {
+		sg = self->tx_sg_nosignal;
+		bes_sdio_tx_multiple_num = BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL;
+	}
+
+	INIT_LIST_HEAD(&proc_list);
+
+	for (;;) {
+		spin_lock(&self->tx_bufferlock);
+		list_splice_tail_init(&self->tx_bufferlist, &proc_list);
+		spin_unlock(&self->tx_bufferlock);
+		if (list_empty(&proc_list))
+			break;
+		sg_init_table(sg, bes_sdio_tx_multiple_num + 1);
+		list_for_each_entry_safe(tx_buffer, temp, &proc_list, node) {
+			blks = (tx_buffer->len + self->func->cur_blksize - 1) / self->func->cur_blksize;
+			align = blks * self->func->cur_blksize;
+			if (blks >= 4) {
+				align = 1632;
+			}
+			if (unlikely(blks >= 5)) {
+				bes2600_err(BES2600_DBG_SDIO, "%s,%d skip error-len packet:%u,%d\n", __func__, __LINE__, tx_buffer->len, blks);
+				list_del_init(&tx_buffer->node);
+				kmem_cache_free(self->tx_bufferlistpool, tx_buffer);
+				continue;
+			}
+			bes2600_dbg(BES2600_DBG_SDIO, "%s,%p,%u->%u\n", __func__, tx_buffer->buf, tx_buffer->len, align);
+			if (!cur_blk)
+				cur_blk = blks;
+			else if (cur_blk != blks)
+				goto flush_previous;
+
+			pMsg = (struct HI_MSG_HDR *)tx_buffer->buf;
+			if (unlikely(IS_DRIVER_VENDOR_CMD(pMsg->MsgId))) {
+				if (driver_to_mcu == ST_EXIT) {
+					driver_to_mcu = ST_ENTER;
+					goto flush_previous;
+				}
+			}
+
+			sg_set_buf(&sg[scatters], tx_buffer->buf, align);
+			total_len += align;
+			++scatters;
+/*del_node:*/
+			list_del_init(&tx_buffer->node);
+			kmem_cache_free(self->tx_bufferlistpool, tx_buffer);
+			self->tx_proc_cnt++;
+			if (unlikely(IS_DRIVER_VENDOR_CMD(pMsg->MsgId))) {
+				if (driver_to_mcu == ST_ENTER) {
+					driver_to_mcu = ST_EXIT;
+					break;
+				}
+			}
+			if (scatters >= bes_sdio_tx_multiple_num) {
+				break;
+			}
+		}
+flush_previous:
+		if (likely(scatters)) {
+			if (WARN_ON(total_len & 0x3))
+				break;
+			else
+				total_len |= (cur_blk - 1);
+			sdio_claim_host(self->func);
+			if (self->retune_protected == false) {
+				sdio_retune_hold_now(self->func);
+				self->retune_protected = true;
+			}
+			do {
+				ret = bes_sdio_memcpy_to_io_helper(self->func, total_len, sg, scatters);
+				if (likely(ret != -84)) {
+					crc_retry = 0;
+					break;
+				} else {
+					crc_retry++;
+					bes2600_err(BES2600_DBG_SDIO, "%s sdio write crc error(%d)\n", __func__, crc_retry);
+				}
+			} while (crc_retry <= 10);
+			sdio_release_host(self->func);
+			queue_work(self->sdio_wq, &self->rx_work);
+			if (ret) {
+				bes2600_err(BES2600_DBG_SDIO, "%s,%d err=%d,%d,%d\n", __func__, __LINE__, ret, scatters, cur_blk);
+				sdio_work_debug(self);
+				bes2600_chrdev_wifi_force_close(self->core, false);
+			}
+			scatters = 0;
+			total_len = 0;
+			cur_blk = 0;
+			self->tx_xfer_cnt++;
+			self->last_tx_data_timestamp = jiffies;
+		}
+	}
+}
+
+static int bes2600_sdio_pipe_send(struct sbus_priv *self, u8 pipe, u32 len, u8 *buf)
+{
+	struct bes_sdio_tx_list_t * desc = NULL;
+
+	if(bes2600_chrdev_is_bus_error()) {
+		bes2600_tx_loop_pipe_send(self->core, buf, len);
+		return 0;
+	}
+
+	desc = kmem_cache_alloc(self->tx_bufferlistpool, GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+	INIT_LIST_HEAD(&desc->node);
+	desc->buf = buf;
+	desc->len = len;
+	if (!buf || !len)
+		return -EINVAL;
+
+	spin_lock(&self->tx_bufferlock);
+	list_add_tail(&desc->node, &self->tx_bufferlist);
+	self->tx_data_cnt++;
+	spin_unlock(&self->tx_bufferlock);
+	queue_work(self->sdio_wq, &self->tx_work);
+	return 0;
+
+}
+#endif
+
+static int bes2600_sdio_misc_init(struct sbus_priv *self, struct bes2600_common *ar)
+{
+#ifdef BES_SDIO_RXTX_TOGGLE
+	self->rx_data_toggle = 0;
+	self->tx_data_toggle = 0;
+	self->next_toggle = 0;
+#endif
+#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
+	spin_lock_init(&self->rx_queue_lock);
+	skb_queue_head_init(&self->rx_queue);
+	self->rx_buffer = (u8 *)__get_dma_pages(GFP_KERNEL, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM));
+	if (!self->rx_buffer)
+		return -ENOMEM;
+	INIT_WORK(&self->rx_work, sdio_rx_work);
+#endif
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
+	INIT_LIST_HEAD(&self->tx_bufferlist);
+	spin_lock_init(&self->tx_bufferlock);
+	self->tx_buffer = (u8 *)kmalloc(512, GFP_KERNEL);
+	if (!self->tx_buffer) {
+		goto err2;
+	}
+	self->tx_bufferlistpool = kmem_cache_create("sdio_tx_bufferlistpool", sizeof(struct bes_sdio_tx_list_t), 0, SLAB_HWCACHE_ALIGN, NULL);
+	if (!self->tx_bufferlistpool)
+		goto err1;
+	self->sdio_wq = alloc_workqueue("bes_sdio", WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_CPU_INTENSIVE, 2);
+	if (!self->sdio_wq)
+		goto err0;
+	INIT_WORK(&self->tx_work, sdio_tx_work);
+	return 0;
+err0:
+	kmem_cache_destroy(self->tx_bufferlistpool);
+err1:
+	kfree(self->tx_buffer);
+err2:
+	free_pages((unsigned long)self->rx_buffer, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM));
+	return -ENOMEM;
+#endif
+	return 0;
+}
+
+#if defined(PLAT_ALLWINNER)|| defined (PLAT_ROCKCHIP)
+static struct bes2600_platform_data_sdio bes_sdio_plat_data = {
+#if defined(BES2600_INDEPENDENT_EVB)
+	.reset = GPIOA(9),
+	.powerup = GPIOC(3),
+	.wakeup = -1,
+#elif defined(BES2600_INTEGRATED_MODULE_V1)
+	.reset = GPIOA(0),
+	.powerup = -1,
+	.wakeup = -1,
+#elif defined(BES2600_INTEGRATED_MODULE_V2)
+	.reset = GPIOM(2),
+	.powerup = -1,
+	.wakeup = GPIOM(5),
+#elif defined(PLAT_ROCKCHIP)
+	.reset = -1,
+	.powerup = -1,
+	.wakeup = -1,
+#endif
+};
+#endif
+
+struct bes2600_platform_data_sdio *bes2600_get_platform_data(void)
+{
+#if defined(PLAT_ALLWINNER)|| defined (PLAT_ROCKCHIP)
+	return &bes_sdio_plat_data;
+#else
+	return NULL;
+#endif
+}
+
+static void bes2600_get_gpio_from_dts(int *gpio_num, const char *gpio_name)
+{
+	int wakeup_gpio;
+	enum of_gpio_flags flags;
+	struct device_node *wireless_node;
+	wireless_node = of_find_node_with_property(NULL, gpio_name);
+	if(wireless_node != NULL){
+		wakeup_gpio = of_get_named_gpio_flags(wireless_node, gpio_name, 0, &flags);
+		if (gpio_is_valid(wakeup_gpio))
+			*gpio_num = wakeup_gpio;
+	}else{
+		bes2600_err(BES2600_DBG_SDIO, "find node for %s failed\n", gpio_name);
+	}
+}
+
+static int bes2600_platform_data_init(void)
+{
+	int ret = 0;
+	struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+	if (pdata == NULL)
+		return 0;
+
+		/* Ensure I/Os are pulled low */
+	if (gpio_is_valid(pdata->reset)) {
+		ret = gpio_request(pdata->reset, "bes2600_wlan_reset");
+		if (ret) {
+			bes2600_err(BES2600_DBG_SDIO, "can't reqest reset_gpio:%d\n", ret);
+			goto exit;
+		} else {
+			gpio_direction_output(pdata->reset, 0);
+		}
+	} else {
+		bes2600_err(BES2600_DBG_SDIO, "reset is invalid\n");
+	}
+
+	if (gpio_is_valid(pdata->powerup)) {
+		ret = gpio_request(pdata->powerup, "bes2600_wlan_powerup");
+		if (ret) {
+			bes2600_err(BES2600_DBG_SDIO, "can't request powerup_gpio:%d\n", ret);
+			goto exit;
+		} else {
+			gpio_direction_output(pdata->powerup, 0);
+		}
+	} else {
+		bes2600_err(BES2600_DBG_SDIO, "powerup is invalid\n");
+	}
+
+	bes2600_get_gpio_from_dts(&pdata->wakeup, "WIFI,host_wakeup_wifi");
+	if (gpio_is_valid(pdata->wakeup)) {
+		ret = gpio_request(pdata->wakeup, "bes2600_wakeup");
+		if (ret) {
+			bes2600_err(BES2600_DBG_SDIO, "can't request wakeup_gpio:%d\n", ret);
+			goto exit;
+		} else {
+			gpio_direction_output(pdata->wakeup, 0);
+		}
+	} else {
+		bes2600_err(BES2600_DBG_SDIO, "wakeup is invalid\n");
+	}
+
+	bes2600_get_gpio_from_dts(&pdata->host_wakeup, "WIFI,host_wake_irq");
+	if (gpio_is_valid(pdata->host_wakeup)) {
+		ret = gpio_request(pdata->host_wakeup, "bes2600_host_irq");
+		if (ret) {
+			bes2600_err(BES2600_DBG_SDIO, "can't reqest host_wake_gpio:%d\n", ret);
+			goto exit;
+		} else {
+			gpio_direction_input(pdata->host_wakeup);
+		}
+	} else {
+		bes2600_err(BES2600_DBG_SDIO, "host_wakeup is invalid\n");
+	}
+
+	pdata->wlan_bt_hostwake_registered = false;
+exit:
+	return ret;
+}
+
+static void bes2600_platform_data_deinit(void)
+{
+	const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+	if (pdata == NULL) {
+		return;
+	}
+
+	if (gpio_is_valid(pdata->reset)) {
+		gpio_free(pdata->reset);
+	}
+	if (gpio_is_valid(pdata->powerup)) {
+		gpio_free(pdata->powerup);
+	}
+	if (gpio_is_valid(pdata->wakeup)) {
+		gpio_free(pdata->wakeup);
+	}
+	if (gpio_is_valid(pdata->host_wakeup)) {
+		gpio_free(pdata->host_wakeup);
+	}
+}
+
+static int bes2600_sdio_reset(struct sbus_priv *self)
+{
+	const struct bes2600_platform_data_sdio *plat_data = bes2600_get_platform_data();
+
+	bes2600_info(BES2600_DBG_SDIO, "%s ...\n", __func__);
+
+	if (plat_data == NULL)
+		return 0;
+
+	if (plat_data->reset) {
+		gpio_set_value(plat_data->reset, 1);
+		mdelay(50);
+		gpio_set_value(plat_data->reset, 0);
+	}
+	return 0;
+}
+
+static int bes2600_sdio_readb_safe(struct sdio_func *func, unsigned int addr)
+{
+	int ret = 0;
+	u8 val = 0;
+	u8 retry = 0;
+
+	do {
+		val = sdio_readb(func, addr, &ret);
+	} while((ret < 0) && ++retry < 30);
+
+	bes2600_err_with_cond(ret, BES2600_DBG_SDIO, "%s failed, ret:%d\n", __func__, ret);
+
+	return (ret < 0) ? ret : val;
+}
+
+static int bes2600_sdio_writeb_safe(struct sdio_func *func, unsigned int addr, u8 val)
+{
+	int ret;
+	u8 retry = 0;
+
+	do {
+		sdio_writeb(func, val, addr, &ret);
+	} while((ret < 0) && ++retry < 30);
+
+	bes2600_err_with_cond(ret, BES2600_DBG_SDIO, "%s failed, ret:%d\n", __func__, ret);
+
+	return ret;
+}
+
+static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int flag)
+{
+	bool gpio_wakeup = false;
+	const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+	if (pdata == NULL)
+		return;
+
+	bes2600_dbg(BES2600_DBG_SDIO, "%s with %d\n", __func__, flag);
+
+	mutex_lock(&self->io_mutex);
+
+	/* error check */
+	if((self->gpio_wakup_flags & BIT(flag)) != 0) {
+		bes2600_err(BES2600_DBG_SDIO,
+			"repeat set gpio_wake_flag, sub_sys:%d", flag);
+		mutex_unlock(&self->io_mutex);
+		return;
+	}
+
+	/* check if this is the first subsystem that need mcu to keep awake */
+	gpio_wakeup = (self->gpio_wakup_flags == 0);
+
+	/* do wakeup mcu operation */
+	if(gpio_wakeup) {
+		bes2600_dbg(BES2600_DBG_SDIO, "pull high gpio by flag:%d\n", flag);
+		if (gpio_is_valid(pdata->wakeup)) {
+			gpio_set_value(pdata->wakeup, 1);
+			msleep(10);
+		} else {
+			bes2600_err(BES2600_DBG_SDIO,
+				"%s, wakeup gpio is invalid\n", __func__);
+		}
+	}
+
+	/* set flag of gpio_wakeup_flags */
+	self->gpio_wakup_flags |= BIT(flag);
+
+	mutex_unlock(&self->io_mutex);
+}
+
+static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int flag)
+{
+	bool gpio_sleep = false;
+	const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+	if (pdata == NULL)
+		return;
+
+	bes2600_dbg(BES2600_DBG_SDIO, "%s with %d\n", __func__, flag);
+
+	mutex_lock(&self->io_mutex);
+
+	/* error check */
+	if((self->gpio_wakup_flags & BIT(flag)) == 0) {
+		bes2600_err(BES2600_DBG_SDIO,
+			"repeat clear gpio_wake_flag, sub_sys:%d", flag);
+		mutex_unlock(&self->io_mutex);
+		return;
+	}
+
+	/* clear flag of gpio_wakeup_flags */
+	self->gpio_wakup_flags &= ~BIT(flag);
+
+	/* check if this is the last subsystem that need mcu to keep awake */
+	gpio_sleep = (self->gpio_wakup_flags == 0);
+
+	/* do wakeup mcu operation */
+	if(gpio_sleep) {
+		bes2600_dbg(BES2600_DBG_SDIO, "pull low gpio by flag:%d\n", flag);
+		if (gpio_is_valid(pdata->wakeup)) {
+			gpio_set_value(pdata->wakeup, 0);
+		} else {
+			bes2600_err(BES2600_DBG_SDIO,
+				"%s, wakeup gpio is invalid\n", __func__);
+		}
+	}
+
+	mutex_unlock(&self->io_mutex);
+}
+
+int bes2600_sdio_active(struct sbus_priv *self, int sub_system)
+{
+	u16 cfg;
+	u8 cfm = 0;
+	int ret = 0, retries = 0;
+	u8 tmp_val = 0;
+	u32 cnt = 0;
+	u32 delay_cnt = 2;
+
+	/* nosignal mode only allow SUBSYSTEM_WIFI */
+	if (!bes2600_chrdev_is_signal_mode() && sub_system != SUBSYSTEM_WIFI)
+		return -EINVAL;
+
+	/* don't read/write sdio when sdio error */
+	if(bes2600_chrdev_is_bus_error())
+		return 0;
+
+	/* prevent concurrent access */
+	mutex_lock(&self->sbus_mutex);
+
+	/* set config and confirm value */
+	if (sub_system == SUBSYSTEM_MCU) {
+		cfg = BES_HOST_INT | BES_SUBSYSTEM_MCU_ACTIVE;
+		cfm = BES_SLAVE_STATUS_MCU_WAKEUP_READY;
+		delay_cnt = 2;
+	} else if (sub_system == SUBSYSTEM_WIFI) {
+		cfg = BES_HOST_INT | BES_SUBSYSTEM_WIFI_ACTIVE;
+		cfm = BES_SLAVE_STATUS_WIFI_READY;
+		delay_cnt = 25;
+	} else if(sub_system == SUBSYSTEM_BT) {
+		cfg = BES_HOST_INT | BES_SUBSYSTEM_BT_ACTIVE;
+		cfm = BES_SLAVE_STATUS_BT_READY;
+		delay_cnt = 25;
+	} else if(sub_system == SUBSYSTEM_BT_LP) {
+		cfg = BES_HOST_INT | BES_SUBSYSTEM_BT_WAKEUP;
+		cfm = BES_SLAVE_STATUS_BT_WAKE_READY;
+		delay_cnt = 2;
+	} else {
+		mutex_unlock(&self->sbus_mutex);
+		return -EINVAL;
+	}
+
+	/* set fw_started flag in advance */
+	if(sub_system == SUBSYSTEM_WIFI) {
+		self->fw_started = true;
+	}
+
+	/* wait until device ready */
+	do {
+		sdio_claim_host(self->func);
+		ret = bes2600_sdio_readb_safe(self->func, BES_SLAVE_STATUS_REG_ID);
+		sdio_release_host(self->func);
+		bes2600_dbg(BES2600_DBG_SDIO, "active wait mcu ready cnt:%d, reg:%d\n", cnt++, ret);
+		if(ret < 0) {
+			goto err;
+		}
+	} while((ret & BES_SLAVE_STATUS_MCU_READY) == 0);
+
+	do {
+		/* claim sdio host */
+		sdio_claim_host(self->func);
+
+		/* write first segment */
+		tmp_val = (cfg >> 8) & 0xff;
+		ret = bes2600_sdio_writeb_safe(self->func, BES_HOST_INT_REG_ID + 1, tmp_val);
+		if(ret < 0) {
+			sdio_release_host(self->func);
+			bes2600_err(BES2600_DBG_SDIO, "active write 1st seg failed\n");
+			goto err;
+		}
+
+		/* write second segment */
+		tmp_val = cfg & 0xff;
+		ret = bes2600_sdio_writeb_safe(self->func, BES_HOST_INT_REG_ID, tmp_val);
+		if(ret < 0) {
+			sdio_release_host(self->func);
+			bes2600_err(BES2600_DBG_SDIO, "active write 2nd seg failed\n");
+			goto err;
+		}
+
+		/* release sdio host */
+		sdio_release_host(self->func);
+
+		/* wait device to response */
+		msleep(delay_cnt);
+
+		/* read device response result */
+		sdio_claim_host(self->func);
+		ret = bes2600_sdio_readb_safe(self->func, BES_SLAVE_STATUS_REG_ID);
+		sdio_release_host(self->func);
+		if(ret < 0) {
+			bes2600_err(BES2600_DBG_SDIO, "active read response failed\n");
+			goto err;
+		}
+		bes2600_dbg(BES2600_DBG_SDIO, "active resp cnt:%d, reg:%d, sub_sys:%d\n", retries, ret, sub_system);
+	} while ((cfm != 0) && (ret & cfm) == 0 && ++retries <= 200);	// check if cfm bit is set
+
+	if (retries > 200) {
+		bes2600_err(BES2600_DBG_SDIO, "bes2600_sdio_active failed, subsys:%d\n", sub_system);
+		/* open wifi failed, restore fw_started flag */
+		if(sub_system == SUBSYSTEM_WIFI) {
+			self->fw_started = false;
+		}
+
+		mutex_unlock(&self->sbus_mutex);
+		return -EFAULT;
+	} else {
+		ret = 0;
+	}
+
+#ifdef BES2600_GPIO_WAKEUP_AP
+	if (sub_system == SUBSYSTEM_WIFI ||
+		sub_system == SUBSYSTEM_BT)
+		ret = bes2600_gpio_wakeup_ap_config(self);
+#else
+	ret = 0;
+#endif
+	/* prevent concurrent access */
+	mutex_unlock(&self->sbus_mutex);
+
+	return ret;
+err:
+	mutex_unlock(&self->sbus_mutex);
+	bes2600_chrdev_wifi_force_close(self->core, false);
+	return -ENODEV;
+}
+
+static void bes2600_sdio_empty_work(struct sbus_priv *self)
+{
+#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
+	struct sk_buff *skb;
+#endif
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
+	struct bes_sdio_tx_list_t *tx_buffer, *temp;
+#endif
+
+#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
+	cancel_work_sync(&self->rx_work);
+	while (1) {
+		skb = skb_dequeue(&self->rx_queue);
+		if (skb)
+			dev_kfree_skb(skb);
+		else
+			break;
+	}
+	self->rx_last_ctrl = 0;
+	self->rx_total_ctrl_cnt = 0;
+	self->rx_continuous_ctrl_cnt = 0;
+	self->rx_remain_ctrl_cnt = 0;
+	self->rx_zero_ctrl_cnt = 0;
+	self->rx_data_cnt = 0;
+	self->rx_xfer_cnt = 0;
+	self->rx_proc_cnt = 0;
+#endif
+
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
+	cancel_work_sync(&self->tx_work);
+	list_for_each_entry_safe(tx_buffer, temp, &self->tx_bufferlist, node) {
+		list_del_init(&tx_buffer->node);
+		kmem_cache_free(self->tx_bufferlistpool, tx_buffer);
+	}
+	self->tx_data_cnt = 0;
+	self->tx_xfer_cnt = 0;
+	self->tx_proc_cnt = 0;
+#endif
+
+#ifdef BES_SDIO_RXTX_TOGGLE
+	self->rx_data_toggle = 0;
+	self->tx_data_toggle = 0;
+	self->next_toggle = 0;
+#endif
+}
+
+#ifdef BES2600_GPIO_WAKEUP_AP
+static void bes2600_wlan_bt_hostwake_unregister(void);
+#endif
+
+int bes2600_sdio_deactive(struct sbus_priv *self, int sub_system)
+{
+	u16 cfg = 0;
+	u8 cfm = 0;
+	u8 tmp_val = 0;
+	u16 retries = 0;
+	u32 cnt = 0;
+	u32 delay_cnt = 2;
+	int ret;
+
+	/* don't read/write sdio when sdio error */
+	if(bes2600_chrdev_is_bus_error())
+		return 0;
+
+	/* notify device deactive event */
+	if (bes2600_chrdev_is_signal_mode()) {
+		/* prevent concurrent access */
+		mutex_lock(&self->sbus_mutex);
+
+		/* set config and confirm value */
+		if (sub_system == SUBSYSTEM_MCU) {
+			cfg = BES_HOST_INT | BES_SUBSYSTEM_MCU_DEACTIVE;
+			cfm = BES_SLAVE_STATUS_MCU_WAKEUP_READY;
+		} else if (sub_system == SUBSYSTEM_WIFI) {
+			cfg = BES_HOST_INT | BES_SUBSYSTEM_WIFI_DEACTIVE;
+			cfm = BES_SLAVE_STATUS_WIFI_READY;
+		} else if(sub_system == SUBSYSTEM_BT) {
+			cfg = BES_HOST_INT | BES_SUBSYSTEM_BT_DEACTIVE;
+			cfm = BES_SLAVE_STATUS_BT_READY;
+		} else if(sub_system == SUBSYSTEM_BT_LP) {
+			cfg = BES_HOST_INT | BES_SUBSYSTEM_BT_SLEEP;
+			cfm = BES_SLAVE_STATUS_BT_WAKE_READY;
+		} else {
+			mutex_unlock(&self->sbus_mutex);
+			return -EINVAL;
+		}
+
+		/* wait until device ready */
+		do {
+			sdio_claim_host(self->func);
+			ret = bes2600_sdio_readb_safe(self->func, BES_SLAVE_STATUS_REG_ID);
+			sdio_release_host(self->func);
+			bes2600_dbg(BES2600_DBG_SDIO, "deactive wait mcu ready cnt:%d, reg:%d\n", cnt++, ret);
+
+			if(ret < 0) {
+				goto err;
+			}
+		} while((ret & BES_SLAVE_STATUS_MCU_READY) == 0);
+
+		do {
+			/* claim sdio host */
+			sdio_claim_host(self->func);
+
+			/* write first segment */
+			tmp_val = (cfg >> 8) & 0xff;
+			ret = bes2600_sdio_writeb_safe(self->func, BES_HOST_INT_REG_ID + 1, tmp_val);
+			if(ret < 0) {
+				sdio_release_host(self->func);
+				bes2600_err(BES2600_DBG_SDIO, "deactive write 1st seg failed\n");
+				goto err;
+			}
+
+			/* write second segment */
+			tmp_val = cfg & 0xff;
+			ret = bes2600_sdio_writeb_safe(self->func, BES_HOST_INT_REG_ID, tmp_val);
+			if(ret < 0) {
+				sdio_release_host(self->func);
+				bes2600_err(BES2600_DBG_SDIO, "deactive write 2nd seg failed\n");
+				goto err;
+			}
+
+			/* release sdio host */
+			sdio_release_host(self->func);
+
+			/* wait device to response */
+			msleep(delay_cnt);
+
+			/* read device response result */
+			sdio_claim_host(self->func);
+			ret = bes2600_sdio_readb_safe(self->func, BES_SLAVE_STATUS_REG_ID);
+			sdio_release_host(self->func);
+			if(ret < 0) {
+				bes2600_err(BES2600_DBG_SDIO, "deactive read response failed\n");
+				if (sub_system == SUBSYSTEM_MCU) {
+					/* cmd52 may return error when 2600 is sleeping */
+					ret = 0;
+					break;
+				} else {
+					goto err;
+				}
+			}
+			bes2600_dbg(BES2600_DBG_SDIO, "deactive resp cnt:%d, reg:%d, sub_sys:%d\n", retries, ret, sub_system);
+		} while((cfm != 0) && (ret & cfm) != 0 && ++retries < 200);
+
+		/* set fw_started flag to false */
+		if(bes2600_chrdev_is_signal_mode()
+		   && sub_system == SUBSYSTEM_WIFI)
+			self->fw_started = false;
+
+		/* reset sdio send and receive control variable */
+		if(sub_system == SUBSYSTEM_WIFI) {
+			bes2600_sdio_empty_work(self);
+		}
+
+		/* prevent concurrent access */
+		mutex_unlock(&self->sbus_mutex);
+
+		return (ret < 0) ? ret : 0;
+	} else {
+		return 0;
+	}
+
+err:
+	mutex_unlock(&self->sbus_mutex);
+	bes2600_chrdev_wifi_force_close(self->core, false);
+	return -ENODEV;
+}
+
+static int bes2600_sdio_power_up(void)
+{
+	const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+	int ret;
+
+	ret = bes2600_sdio_on(pdata);
+	if (ret)
+		goto err_on;
+
+
+#ifdef PLAT_ALLWINNER
+	mdelay(10);
+	bes2600_chrdev_start_bus_probe();
+	//sw_mci_rescan_card(AW_SDIOID, 1);
+	sunxi_mmc_rescan_card(sunxi_wlan_get_bus_index());
+	bes2600_info(BES2600_DBG_SDIO, "%s: power up, rescan card.\n", __FUNCTION__);
+#endif
+
+	return 0;
+
+err_on:
+	return ret;
+}
+
+static void bes2600_sdio_power_down(struct sbus_priv *self)
+{
+#ifdef POWER_DOWN_BY_MSG
+	u32 cfg = BES_HOST_INT | BES_SUBSYSTEM_SYSTEM_CLOSE;
+	u8 tmp_val = 0;
+	int ret = 0;
+
+	sdio_claim_host(self->func);
+	tmp_val = (cfg >> 8) & 0xff;
+	sdio_writeb(self->func, tmp_val, BES_HOST_INT_REG_ID + 1, &ret);
+	tmp_val = cfg & 0xff;
+	sdio_writeb(self->func, tmp_val, BES_HOST_INT_REG_ID, &ret);
+	sdio_release_host(self->func);
+#else
+#if defined(PLAT_ROCKCHIP)
+	rockchip_wifi_power(0);
+#endif
+
+#if defined(PLAT_ALLWINNER)
+	sunxi_wlan_set_power(false);
+#endif
+#endif
+
+	msleep(10);
+
+	self->func->card->host->caps &= ~MMC_CAP_NONREMOVABLE;
+	schedule_work(&self->sdio_scan_work);
+
+}
+
+static int bes2600_sdio_power_switch(struct sbus_priv *self, int on)
+{
+	int ret = 0;
+	if(on) {
+		ret = bes2600_sdio_power_up();
+	} else {
+		bes2600_sdio_power_down(self);
+	}
+
+	return ret;
+}
+
+static void bes2600_sdio_halt_device(struct sbus_priv *self)
+{
+	sdio_work_debug(self);
+}
+
+static bool bes2600_sdio_wakeup_source(struct sbus_priv *self)
+{
+	struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+
+	if (!pdata)
+		return false;
+
+	if(pdata->wakeup_source) {
+		pdata->wakeup_source = false;
+		return true;
+	}
+
+	return false;
+}
+
+static struct sbus_ops bes2600_sdio_sbus_ops = {
+	.sbus_memcpy_fromio	= bes2600_sdio_memcpy_fromio,
+	.sbus_memcpy_toio	= bes2600_sdio_memcpy_toio,
+	.lock			= bes2600_sdio_lock,
+	.unlock			= bes2600_sdio_unlock,
+	.irq_subscribe		= bes2600_sdio_irq_subscribe,
+	.irq_unsubscribe	= bes2600_sdio_irq_unsubscribe,
+	.reset			= bes2600_sdio_reset,
+	.align_size		= bes2600_sdio_align_size,
+	.set_block_size		= bes2600_sdio_set_block_size,
+	.sbus_reg_read		= bes2600_sdio_reg_read,
+	.sbus_reg_write		= bes2600_sdio_reg_write,
+	.init			= bes2600_sdio_misc_init,
+#ifdef BES_SDIO_RX_MULTIPLE_ENABLE
+	.pipe_read		= bes2600_sdio_pipe_read,
+#endif
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
+	.pipe_send		= bes2600_sdio_pipe_send,
+#endif
+	.sbus_active		= bes2600_sdio_active,
+	.sbus_deactive		= bes2600_sdio_deactive,
+	.power_switch		= bes2600_sdio_power_switch,
+	.gpio_wake		= bes2600_gpio_wakeup_mcu,
+	.gpio_sleep		= bes2600_gpio_allow_mcu_sleep,
+	.halt_device		= bes2600_sdio_halt_device,
+	.wakeup_source		= bes2600_sdio_wakeup_source,
+};
+
+static void bes2600_sdio_en_lp_cb(struct bes2600_common *hw_priv)
+{
+	long unsigned int old_ts, new_ts;
+	struct sbus_priv *self = hw_priv->sbus_priv;
+
+	do {
+		old_ts = self->last_irq_timestamp;
+		flush_work(&self->rx_work);
+		new_ts = self->last_irq_timestamp;
+	} while(old_ts != new_ts);
+}
+
+/* Probe Function to be called by SDIO stack when device is discovered */
+static int bes2600_sdio_probe(struct sdio_func *func,
+			      const struct sdio_device_id *id)
+{
+	struct sbus_priv *self;
+	int status;
+
+	bes2600_info(BES2600_DBG_SDIO, "Probe called:%p,%d\n", func, func->num);
+	if (func->num > 1)
+		return 0;
+
+	func->card->host->caps |= MMC_CAP_NONREMOVABLE;
+	bes2600_chrdev_bus_probe_notify();
+
+	self = kzalloc(sizeof(*self), GFP_KERNEL);
+	if (!self) {
+		bes2600_dbg(BES2600_DBG_SDIO, "Can't allocate SDIO sbus_priv.");
+		return -ENOMEM;
+	}
+#ifdef PLAT_ALLWINNER_SUN6I
+	aw_gpio_irq_handle = 0;
+#endif
+	spin_lock_init(&self->lock);
+	self->pdata = bes2600_get_platform_data();
+	self->func = func;
+	self->dev = &func->dev;
+	self->gpio_wakup_flags = 0;
+	self->retune_protected = false;
+	self->unregister_in_process = false;
+	mutex_init(&self->io_mutex);
+	mutex_init(&self->sbus_mutex);
+	INIT_WORK(&self->sdio_scan_work, sdio_scan_work);
+#ifndef SDIO_HOST_ADMA_SUPPORT
+	if ((MAX_SDIO_TRANSFER_LEN < 1632 * BES_SDIO_RX_MULTIPLE_NUM) ||
+			(MAX_SDIO_TRANSFER_LEN < 1632 * BES_SDIO_TX_MULTIPLE_NUM)) {
+		bes2600_err(BES2600_DBG_SDIO, "gathered buffer is too small.\n");
+		return -EINVAL;
+	}
+	self->single_gathered_buffer = (u8 *)__get_dma_pages(GFP_KERNEL, get_order(MAX_SDIO_TRANSFER_LEN));
+	if (!self->single_gathered_buffer)
+		return -ENOMEM;
+#endif
+#ifdef BES_SDIO_RXTX_TOGGLE
+	self->fw_started = false;
+#endif
+	bes2600_gpio_wakeup_mcu(self, GPIO_WAKE_FLAG_SDIO_PROBE);
+
+	sdio_set_drvdata(func, self);
+	sdio_claim_host(func);
+	sdio_enable_func(func);
+	sdio_release_host(func);
+
+	bes2600_reg_set_object(&bes2600_sdio_sbus_ops, self);
+	status = bes2600_load_firmware(&bes2600_sdio_sbus_ops, self);
+	bes2600_info_with_cond((status > 0), BES2600_DBG_SDIO,
+			"interrupt init process beacuse device be closed.\n");
+	if(status > 0)	// for wifi closed case
+		goto out;
+	else if(status < 0)	// for download fail case
+		goto err;
+
+	status = bes2600_register_net_dev(self);
+	if (status) {
+		goto err;
+	}
+
+out:
+	bes2600_chrdev_set_sbus_priv_data(self, false);
+	bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_PROBE);
+	return 0;
+
+err:
+	bes2600_err(BES2600_DBG_SDIO, "%s failed, func:%d\n", __func__, func->num);
+	func->card->host->caps &= ~MMC_CAP_NONREMOVABLE;
+	sdio_claim_host(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+	bes2600_gpio_allow_mcu_sleep(self, GPIO_WAKE_FLAG_SDIO_PROBE);
+	sdio_set_drvdata(func, NULL);
+	bes2600_reg_set_object(NULL, NULL);
+	bes2600_chrdev_set_sbus_priv_data(NULL, true);
+	kfree(self);
+	return 0;
+}
+
+int bes2600_register_net_dev(struct sbus_priv *bus_priv)
+{
+	int status = 0;
+	BUG_ON(!bus_priv);
+	status = bes2600_core_probe(&bes2600_sdio_sbus_ops,
+			      bus_priv, bus_priv->dev, &bus_priv->core);
+	if(!status)
+		bes2600_pwr_register_en_lp_cb(bus_priv->core, bes2600_sdio_en_lp_cb);
+
+	return status;
+}
+
+int bes2600_unregister_net_dev(struct sbus_priv *bus_priv)
+{
+	BUG_ON(!bus_priv);
+	if (bus_priv->core && !bus_priv->unregister_in_process) {
+		bus_priv->unregister_in_process = true;
+		bes2600_core_release(bus_priv->core);
+		bes2600_pwr_unregister_en_lp_cb(bus_priv->core, bes2600_sdio_en_lp_cb);
+		bus_priv->core = NULL;
+
+		if (bus_priv->sdio_wq) {
+			flush_workqueue(bus_priv->sdio_wq);
+			destroy_workqueue(bus_priv->sdio_wq);
+			bus_priv->sdio_wq = NULL;
+		}
+
+		if (bus_priv->rx_buffer) {
+			free_pages((unsigned long)bus_priv->rx_buffer, get_order(1632 * BES_SDIO_RX_MULTIPLE_NUM));
+			bus_priv->rx_buffer = NULL;
+		}
+
+#ifdef BES_SDIO_TX_MULTIPLE_ENABLE
+		if (bus_priv->tx_buffer) {
+			kfree(bus_priv->tx_buffer);
+			bus_priv->tx_buffer = NULL;
+		}
+
+		if (bus_priv->tx_bufferlistpool) {
+			kmem_cache_destroy(bus_priv->tx_bufferlistpool);
+			bus_priv->tx_bufferlistpool = NULL;
+		}
+#endif
+		bus_priv->unregister_in_process = false;
+	}
+	return 0;
+}
+
+bool bes2600_is_net_dev_created(struct sbus_priv *bus_priv)
+{
+	BUG_ON(!bus_priv);
+	return (bus_priv->core != NULL);
+}
+
+/* Disconnect Function to be called by SDIO stack when
+ * device is disconnected */
+static void bes2600_sdio_disconnect(struct sdio_func *func)
+{
+	struct sbus_priv *self = sdio_get_drvdata(func);
+	const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+
+	func->card->host->caps &= ~MMC_CAP_NONREMOVABLE;
+	bes2600_info(BES2600_DBG_SDIO, "%s called:%p,%d\n", __func__, func, func->num);
+
+	if (self) {
+		bes2600_unregister_net_dev(self);
+#ifndef CONFIG_BES2600_USE_GPIO_IRQ
+		sdio_claim_host(func);
+		sdio_release_irq(func);
+		sdio_release_host(func);
+#else
+		free_irq(irq->start, self);
+#endif  //CONFIG_BES2600_USE_GPIO_IRQ
+		sdio_claim_host(func);
+		sdio_disable_func(func);
+		sdio_release_host(func);
+		bes2600_sdio_off(pdata);
+		bes2600_reg_set_object(NULL, NULL);
+		bes2600_chrdev_set_sbus_priv_data(NULL, false);
+		sdio_set_drvdata(func, NULL);
+		if (self->retune_protected == true) {
+			sdio_retune_release(func);
+		}
+#ifndef SDIO_HOST_ADMA_SUPPORT
+		if (self->single_gathered_buffer) {
+			free_pages((unsigned long)self->single_gathered_buffer, get_order(MAX_SDIO_TRANSFER_LEN));
+		}
+#endif
+		kfree(self);
+	}
+}
+
+#ifdef BES2600_GPIO_WAKEUP_AP
+
+#if defined(PLAT_ALLWINNER)
+extern int sunxi_wlan_get_oob_irq_flags(void);
+extern int sunxi_wlan_get_oob_irq(void);
+#elif defined(PLAT_ROCKCHIP)
+extern int rockchip_wifi_get_oob_irq_flag(void);
+extern int rockchip_wifi_get_oob_irq(void);
+#endif
+
+static irqreturn_t bes2600_wlan_bt_hostwake_thread(int irq, void *dev_id)
+{
+	struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+
+	bes2600_info(BES2600_DBG_SDIO, "bes2600_wlan_hostwake:%d\n", dev_id == (void *)pdata);
+
+	if (dev_id == (void *)pdata) {
+		pdata->wakeup_source = true;
+		disable_irq_nosync(irq);
+		return IRQ_HANDLED;
+	} else {
+		return IRQ_NONE;
+	}
+}
+
+static int bes2600_wlan_bt_hostwake_register(void)
+{
+	int ret = 0;
+	struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+
+#if defined(PLAT_ALLWINNER)
+	int irq_flags = sunxi_wlan_get_oob_irq_flags();
+	int irq = sunxi_wlan_get_oob_irq();
+#elif defined(PLAT_ROCKCHIP)
+	int irq_flags = rockchip_wifi_get_oob_irq_flag();
+	int irq = rockchip_wifi_get_oob_irq();
+#endif
+
+	if (pdata->wlan_bt_hostwake_registered == true) {
+		bes2600_err(BES2600_DBG_SDIO, "wlan hostwake register repeatedly.\n");
+		return -1;
+	}
+
+#if defined(PLAT_ALLWINNER) || defined(PLAT_ROCKCHIP)
+	irq_flags |= IRQF_ONESHOT;
+	irq_flags &= (~IRQF_NO_SUSPEND);
+	if (((ret = request_threaded_irq(irq, NULL, bes2600_wlan_bt_hostwake_thread,
+		irq_flags, "bes2600_wlan_hostwake", pdata)) != 0) ||
+	    ((ret = enable_irq_wake(irq)) != 0)) {
+		bes2600_err(BES2600_DBG_SDIO, "request_irq or irq_wake failed with %d\n", ret);
+		return ret;
+	}
+#endif
+
+	pdata->wlan_bt_hostwake_registered = true;
+	pdata->wakeup_source = false;
+
+	return ret;
+}
+
+static void bes2600_wlan_bt_hostwake_unregister(void)
+{
+	struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+
+	int ret = 0;
+#if defined(PLAT_ALLWINNER)
+	int irq = sunxi_wlan_get_oob_irq();
+#elif defined(PLAT_ROCKCHIP)
+	int irq = rockchip_wifi_get_oob_irq();
+#endif
+
+	if (pdata->wlan_bt_hostwake_registered == false)
+		return;
+
+	pdata->wlan_bt_hostwake_registered = false;
+#if defined(PLAT_ALLWINNER) || defined(PLAT_ROCKCHIP)
+	ret = disable_irq_wake(irq);
+	if (ret) {
+		bes2600_err(BES2600_DBG_SDIO, "disable_irq_wake failed with %d\n", ret);
+	}
+	free_irq(irq, pdata);
+#endif
+}
+
+static int bes2600_gpio_wakeup_ap_config(struct sbus_priv *self)
+{
+	u8 wakeup_cfg = 0;
+	int ret = 0, irq_flags = 0, irq = 0;
+
+    if (!bes2600_chrdev_is_signal_mode())
+        return 0;
+
+#if defined(PLAT_ALLWINNER)
+	irq_flags = sunxi_wlan_get_oob_irq_flags();
+	irq = sunxi_wlan_get_oob_irq();
+#elif defined(PLAT_ROCKCHIP)
+	irq_flags = rockchip_wifi_get_oob_irq_flag();
+	irq = rockchip_wifi_get_oob_irq();
+#endif
+
+	if (irq_flags & IRQF_TRIGGER_HIGH) {
+		wakeup_cfg = BES_AP_WAKEUP_GPIO_HIGH | BES_AP_WAKEUP_CFG_VALID;
+	} else if (irq_flags & IRQF_TRIGGER_LOW) {
+		wakeup_cfg = BES_AP_WAKEUP_GPIO_LOW | BES_AP_WAKEUP_CFG_VALID;
+	} else if (irq_flags & IRQF_TRIGGER_RISING) {
+		wakeup_cfg = BES_AP_WAKEUP_GPIO_RISE | BES_AP_WAKEUP_CFG_VALID;
+	} else if (irq_flags & IRQF_TRIGGER_FALLING) {
+		wakeup_cfg = BES_AP_WAKEUP_GPIO_FALL | BES_AP_WAKEUP_CFG_VALID;
+	}
+
+	if (wakeup_cfg & BES_AP_WAKEUP_CFG_VALID)
+		wakeup_cfg |= (BES_AP_WAKEUP_TYPE_GPIO << BES_AP_WAKEUP_TYPE_SHIFT);
+
+	bes2600_info(BES2600_DBG_SDIO, "%s config:%x\n", __func__, wakeup_cfg);
+
+	sdio_claim_host(self->func);
+	sdio_writeb(self->func, wakeup_cfg, BES_AP_WAKEUP_REG_ID, &ret);
+	if (!ret) {
+		sdio_writeb(self->func, 0, BES_HOST_INT_REG_ID + 1, &ret);
+	}
+	if (!ret) {
+		sdio_writeb(self->func, (BES_HOST_INT | BES_AP_WAKEUP_CFG), BES_HOST_INT_REG_ID, &ret);
+	}
+	sdio_release_host(self->func);
+	if (ret) {
+		bes2600_err(BES2600_DBG_SDIO, "%s failed:%d\n", __func__, ret);
+		free_irq(irq, &bes_sdio_plat_data);
+		return ret;
+	}
+
+	return 0;
+}
+#endif
+
+int bes2600_sdio_prepare(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	struct sbus_priv *self = sdio_get_drvdata(func);
+
+	bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num);
+
+	if (func->num > 1)
+		return 0;
+
+	if(bes2600_sdio_sbus_ops.gpio_wake)
+		bes2600_sdio_sbus_ops.gpio_wake(self, GPIO_WAKE_FLAG_HOST_SUSPEND);
+
+	return 0;
+}
+
+static int bes2600_sdio_suspend(struct device *dev)
+{
+	int ret;
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	struct sbus_priv *self = sdio_get_drvdata(func);
+
+	bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num);
+	if (func->num > 1)
+		return 0;
+
+#ifndef CONFIG_BES2600_WOWLAN
+	if(bes2600_chrdev_check_system_close() == false)
+		return -EBUSY;
+#endif
+
+	/* Notify SDIO that BES2600 will remain powered during suspend */
+	ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+	if (ret) {
+		bes2600_err(BES2600_DBG_PM, "Error setting SDIO pm flags: %i\n", ret);
+		return ret;
+	}
+
+	if (bes2600_chrdev_is_bt_opened() == true) {
+		if ((ret = bes2600_sdio_deactive(self, SUBSYSTEM_BT_LP))) {
+			bes2600_err(BES2600_DBG_PM, "bt sleep in suspend failed:%d.\n", ret);
+			return ret;
+		}
+	}
+
+#ifdef BES2600_GPIO_WAKEUP_AP
+	return bes2600_wlan_bt_hostwake_register();
+#endif
+
+	return 0;
+}
+
+static int bes2600_sdio_suspend_noirq(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	struct sbus_priv *self = sdio_get_drvdata(func);
+
+	bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num);
+
+	if (func->num > 1)
+		return 0;
+
+	if(self->core &&
+	   (work_pending(&self->rx_work) || atomic_read(&self->core->bh_rx))) {
+		bes2600_info(BES2600_DBG_SDIO, "%s: Suspend interrupted.\n", __func__);
+		return -EAGAIN;
+	}
+
+	if(bes2600_sdio_sbus_ops.gpio_sleep)
+		bes2600_sdio_sbus_ops.gpio_sleep(self, GPIO_WAKE_FLAG_HOST_SUSPEND);
+
+	if (self->retune_protected == true)
+		bes2600_warn(BES2600_DBG_SDIO, "retune is closed while ap sleep.\n");
+
+	return 0;
+}
+
+int bes2600_sdio_resume_noirq(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	struct sbus_priv *self = sdio_get_drvdata(func);
+
+	bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num);
+
+	if (func->num > 1)
+		return 0;
+
+	if(bes2600_sdio_sbus_ops.gpio_wake)
+		bes2600_sdio_sbus_ops.gpio_wake(self, GPIO_WAKE_FLAG_HOST_RESUME);
+
+	return 0;
+}
+
+static int bes2600_sdio_resume(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+
+	bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num);
+
+	if (func->num > 1)
+		return 0;
+
+#ifdef BES2600_GPIO_WAKEUP_AP
+	bes2600_wlan_bt_hostwake_unregister();
+#endif
+
+	return 0;
+}
+
+static void bes2600_sdio_complete(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	struct sbus_priv *self = sdio_get_drvdata(func);
+
+	bes2600_info(BES2600_DBG_SDIO, "%s (%p,%d)enter\n", __func__, func, func->num);
+
+	if (func->num > 1)
+		return;
+
+	/* wakeup bt if bt is on */
+	bes2600_chrdev_wakeup_bt();
+
+	/* clear resume gpio wake flag */
+	if(bes2600_sdio_sbus_ops.gpio_sleep)
+		bes2600_sdio_sbus_ops.gpio_sleep(self, GPIO_WAKE_FLAG_HOST_RESUME);
+}
+
+static const struct dev_pm_ops bes2600_pm_ops = {
+	.prepare = bes2600_sdio_prepare,
+	.suspend = bes2600_sdio_suspend,
+	.suspend_noirq = bes2600_sdio_suspend_noirq,
+	.resume_noirq = bes2600_sdio_resume_noirq,
+	.resume = bes2600_sdio_resume,
+	.complete = bes2600_sdio_complete,
+};
+
+static struct sdio_driver sdio_driver = {
+	.name		= "bes2600_wlan",
+	.id_table	= bes2600_sdio_ids,
+	.probe		= bes2600_sdio_probe,
+	.remove		= bes2600_sdio_disconnect,
+	.drv = {
+		.pm = &bes2600_pm_ops,
+	}
+};
+
+/* Init Module function -> Called by insmod */
+static int __init bes2600_sdio_init(void)
+{
+	int ret;
+	const struct bes2600_platform_data_sdio *pdata = NULL;
+
+	bes2600_info(BES2600_DBG_SDIO, "------Driver: bes2600.ko version :%s\n", BES2600_DRV_VERSION);
+
+	bes2600_chrdev_update_signal_mode();
+	bes2600_dbg(BES2600_DBG_SDIO, "%s type:%d sig_mode:%d\n", __func__,
+			bes2600_chrdev_get_fw_type(), bes2600_chrdev_is_signal_mode());
+
+	if ((ret = bes2600_platform_data_init()))
+		goto exit;
+
+	pdata = bes2600_get_platform_data();
+
+	ret = bes2600_chrdev_init(&bes2600_sdio_sbus_ops);
+	if(ret)
+		goto err_chardev;
+
+	ret = bes2600_sdio_on(pdata);
+	if (ret)
+		goto err_on;
+
+#ifdef PLAT_ALLWINNER
+	mdelay(10);
+	bes2600_chrdev_start_bus_probe();
+	//sw_mci_rescan_card(AW_SDIOID, 1);
+	sunxi_mmc_rescan_card(sunxi_wlan_get_bus_index());
+	bes2600_info(BES2600_DBG_SDIO, "%s: power up, rescan card.\n", __FUNCTION__);
+#endif
+
+	ret = sdio_register_driver(&sdio_driver);
+	if (ret)
+		goto err_reg;
+
+	return 0;
+
+err_reg:
+	bes2600_sdio_off(pdata);
+err_on:
+	bes2600_chrdev_free();
+err_chardev:
+	bes2600_platform_data_deinit();
+exit:
+	return ret;
+}
+
+/* Called at Driver Unloading */
+static void __exit bes2600_sdio_exit(void)
+{
+	const struct bes2600_platform_data_sdio *pdata = bes2600_get_platform_data();
+	struct sbus_priv *priv =  bes2600_chrdev_get_sbus_priv_data();
+	bes2600_info(BES2600_DBG_SDIO, "%s called\n", __func__);
+
+	sdio_unregister_driver(&sdio_driver);
+	bes2600_chrdev_free();
+	if(!priv) {
+		bes2600_sdio_off(pdata);
+	}
+	bes2600_platform_data_deinit();
+}
+
+module_init(bes2600_sdio_init);
+module_exit(bes2600_sdio_exit);
diff --git a/drivers/staging/bes2600/bes2600_spi.c b/drivers/staging/bes2600/bes2600_spi.c
new file mode 100644
index 000000000000..a818bc9f2106
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_spi.c
@@ -0,0 +1,1803 @@
+/*
+ * Mac80211 SPI driver for BES2600 device
+ *
+ * Based on bes2600_sdio.c
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <net/mac80211.h>
+
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+
+#include "bes2600.h"
+#include "sbus.h"
+#include "wsm.h"
+#include "bes2600_plat.h"
+#include "hwio.h"
+
+#include <linux/sunxi-gpio.h>
+#include <linux/of_gpio.h>
+#include "bes2600_driver_mode.h"
+#include "bes_chardev.h"
+
+#if defined(FW_DOWNLOAD_BY_UART)
+extern int bes2600_load_firmware_uart(struct bes2600_common *hw_priv);
+#endif
+
+MODULE_AUTHOR("Solomon Peachy <speachy@sagrad.com>");
+MODULE_DESCRIPTION("mac80211 BES2600 SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:bes2600_wlan_spi");
+
+/* #define SPI_DEBUG */
+
+struct sbus_priv {
+	struct spi_device	*func;
+	struct bes2600_common	*core;
+	const struct bes2600_platform_data_spi *pdata;
+	struct device *dev;
+	spinlock_t		lock; /* Serialize all bus operations */
+	wait_queue_head_t       wq;
+	int claimed;
+	struct spi_message m;
+	struct spi_transfer t_cmd;
+	struct spi_transfer t_data;
+	u8 *tx_dummy_buf;
+	u8 *rx_dummy_buf;
+	u8 *rx_offline_packets;
+	struct sk_buff_head rx_queue;
+	int gpio_irq;
+	u32 lmac_ptr_pool[BES_LMAC_BUF_NUMS];
+	u32 lmac_ptr_top;
+	u32 lmac_ptr;
+	u32 lmac_addr_min;
+	u32 lmac_addr_max;
+	u32 packets_upload;
+	bool spi_active;
+	long unsigned int gpio_wakup_flags;
+};
+
+/* Notes on byte ordering:
+   LE:  B0 B1 B2 B3
+   BE:  B3 B2 B1 B0
+
+   Hardware expects 32-bit data to be written as 16-bit BE words:
+
+   B1 B0 B3 B2
+*/
+
+static int bes2600_spi_memcpy_fromio(struct sbus_priv *self,
+				     unsigned int addr,
+				     void *dst, int count)
+{
+	int ret;
+	//u32 cfg_addr, cfg_val;
+	u32 reg_addr, done_addr, done_val;
+	struct spi_transfer *t_cmd;
+	struct spi_transfer *t_data;
+	struct spi_message *m;
+
+	bes2600_dbg(BES2600_DBG_SPI, "%s, count=%d,addr=%x\n", __func__, count, addr);
+	if (!dst || (count & 0x3) || ((addr != BES_TX_DATA_ADDR) && (addr != BES_CALI_DATA_ADDR) && (addr < BES_LMAC_BUF_DESC
+					|| addr > BES_LMAC_BUF_DESC + BES_LMAC_BUF_NUMS * 4))) {
+		bes2600_err(BES2600_DBG_SPI, "%s,%d %x:%p:%d", __func__, __LINE__, addr, dst, count);
+		return -EINVAL;
+	}
+
+	BUG_ON(!self);
+	BUG_ON(!self->tx_dummy_buf);
+	BUG_ON(!self->rx_dummy_buf);
+	t_cmd = &self->t_cmd;
+	t_data = &self->t_data;
+	m = &self->m;
+
+	//cfg_addr = SPI_WR_ADDR(SPI_RD_CFG_REG_ID);
+	//cfg_val = SPI_CONTINUOUS_CFG_VAL;
+	reg_addr = SPI_RD_ADDR(addr);
+	done_addr = SPI_WR_ADDR(BES_HOST_INT_REG_ID);
+	done_val = BES_HOST_INT_RD_DONE;
+
+#if defined(__LITTLE_ENDIAN)
+	if (likely(self->func->bits_per_word == 8)) {
+		//cfg_addr = swab32(cfg_addr);
+		//cfg_val = swab32(cfg_val);
+		reg_addr = swab32(reg_addr);
+		done_addr = swab32(done_addr);
+		done_val = swab32(done_val);
+	}
+#endif
+
+	memset(m, 0, sizeof(*m));
+	memset(t_cmd, 0, sizeof(*t_cmd));
+	memset(t_data, 0, sizeof(*t_data));
+	//memcpy(self->tx_dummy_buf, &cfg_addr, 4);
+	//memcpy(self->tx_dummy_buf + 4, &cfg_val, 4);
+	memcpy(self->tx_dummy_buf + 8, &reg_addr, 4);
+	memcpy(self->tx_dummy_buf + 16 + count, &done_addr, 4);
+	memcpy(self->tx_dummy_buf + 20 + count, &done_val, 4);
+	//t_cmd->tx_buf = self->tx_dummy_buf;
+	//t_cmd->rx_buf = self->rx_dummy_buf;
+	//t_cmd->len = 4 + 4;
+	//spi_message_init(m);
+	//spi_message_add_tail(t_cmd, m);
+	//ret = spi_sync(self->func, m);
+	//if (ret) {
+		//bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret);
+		//goto exit;
+	//}
+
+	t_cmd->tx_buf = &self->tx_dummy_buf[8];
+	t_cmd->rx_buf = self->rx_dummy_buf;
+	t_cmd->len = 4 + 4;
+	t_cmd->speed_hz = self->func->max_speed_hz;
+	t_data->tx_buf = &self->tx_dummy_buf[16];
+	t_data->rx_buf = dst;
+	t_data->len = count;
+	t_data->speed_hz = self->func->max_speed_hz;
+	spi_message_init(m);
+	spi_message_add_tail(t_cmd, m);
+	spi_message_add_tail(t_data, m);
+
+	ret = spi_sync(self->func, m);
+	if (!ret) {
+#if 0
+		bes2600_dbg(BES2600_DBG_SPI, "READ LENGTH:%d,%d,%u\n", m->actual_length, count, ++packets_upload);
+		bes2600_dbg_dump(BES2600_DBG_SPI, "read_data:", dst, 8);
+		if (PACKET_COUNT(ctrl_reg) >= 2)
+			bes2600_dbg_dump(BES2600_DBG_SPI, "read_data1:", self->rx_offline_packets[0], 8);
+		if (PACKET_COUNT(ctrl_reg) >= 3)
+			bes2600_dbg_dump(BES2600_DBG_SPI, "read_data2:", self->rx_offline_packets[1], 8);
+#endif
+	} else {
+		bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret);
+		goto exit;
+	}
+
+	if (addr != BES_TX_DATA_ADDR && addr != BES_CALI_DATA_ADDR) {
+#if defined(__LITTLE_ENDIAN)
+	if (likely(self->func->bits_per_word == 8)) {
+		u32 *buf_u32 = (u32 *)dst;
+		int i;
+		for(i = 0; i < (count >> 2); i++) {
+			buf_u32[i] = swab32(buf_u32[i]);
+		}
+	}
+#endif
+		return ret;
+	}
+
+	t_cmd->tx_buf = &self->tx_dummy_buf[count + 16];
+	t_cmd->rx_buf = self->rx_dummy_buf;
+	t_cmd->len = 4 + 4;
+	t_cmd->speed_hz = self->func->max_speed_hz;
+	spi_message_init(m);
+	spi_message_add_tail(t_cmd, m);
+	ret = spi_sync(self->func, m);
+	if (ret) {
+		bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret);
+	}
+	self->packets_upload ++;
+
+	bes2600_dbg_dump(BES2600_DBG_SPI, "read_done:", &self->tx_dummy_buf[count + 16], 8);
+exit:
+	return ret;
+}
+
+static int bes2600_spi_addr_update(struct sbus_priv *self);
+
+static int bes2600_spi_memcpy_toio(struct sbus_priv *self,
+				   unsigned int addr,
+				   const void *src, int count)
+{
+	int ret, i;
+	u32 reg_addr, reg_use, src_pos = 0, buf_pos = 4;
+	u32 done_addr, done_val;
+	u8 *src_u8;
+	u32 *src_u32, *buf_u32;
+	struct spi_transfer *t_cmd;
+	struct spi_transfer *t_data;
+	struct spi_message *m;
+
+	if (!src || count <= 0 || ((!addr) && (count >= 1632))) {
+		bes2600_err(BES2600_DBG_SPI, "%s,%d err=%p:%d\n", __func__, __LINE__, src, count);
+		return -EINVAL;
+	}
+
+	BUG_ON(!self);
+	BUG_ON(!self->tx_dummy_buf);
+	BUG_ON(!self->rx_dummy_buf);
+	m = &self->m;
+	t_cmd = &self->t_cmd;
+	t_data = &self->t_data;
+
+	if (likely(!addr)) {
+		if (bes2600_chrdev_is_signal_mode()) {
+			ret = bes2600_spi_addr_update(self);
+			if (WARN_ON(ret)) {
+				return ret;
+			}
+			reg_use = self->lmac_ptr_pool[self->lmac_ptr & (BES_LMAC_BUF_NUMS - 1)];
+			if (WARN_ON(reg_use > self->lmac_addr_max || reg_use < self->lmac_addr_min)) {
+				bes2600_err(BES2600_DBG_SPI, "%s, addr err:%x,%x,%x\n", __func__, reg_use, self->lmac_addr_max, self->lmac_addr_min);
+				return -EIO;
+			}
+			self->lmac_ptr++;
+		} else {
+			reg_use = BES_MISC_DATA_ADDR;
+		}
+	} else {
+		reg_use = addr;
+	}
+
+	reg_addr = SPI_WR_ADDR(reg_use);
+	if (addr != BES_MISC_DATA_ADDR) {
+		done_val = BES_HOST_INT_WR_DONE;
+		done_addr = SPI_WR_ADDR(BES_HOST_INT_REG_ID);
+	} else {
+		done_val = BES_MISC_DATA_DONE;
+		done_addr = SPI_WR_ADDR(BES_HOST_SUBINT_REG_ID);
+	}
+
+	/* We have to byteswap if the SPI bus is limited to 8b operation
+	   or we are running on a Big Endian system
+	*/
+#if defined(__LITTLE_ENDIAN)
+	bes2600_dbg(BES2600_DBG_SPI, "write_addr:%x(%u)\n", reg_use, self->lmac_ptr);
+	bes2600_dbg_dump(BES2600_DBG_SPI, "wirte_data:", src, 8);
+	if (likely(self->func->bits_per_word == 8))
+	{
+		reg_addr = swab32(reg_addr);
+		done_addr = swab32(done_addr);
+		done_val = swab32(done_val);
+		buf_pos = 1;
+		src_pos = 0;
+		src_u32 = (u32 *)src;
+		buf_u32 = (u32 *)self->tx_dummy_buf;
+		for (i = 0; i < (count >> 2); i++) {
+			buf_u32[buf_pos] = swab32(src_u32[src_pos]);
+			buf_pos ++;
+			src_pos ++;
+		}
+
+		buf_pos = buf_pos << 2;
+		src_pos = src_pos << 2;
+
+		if (count & 0x3) {
+
+			src_u8 = (u8 *)src;
+			if ((count % 4) == 1) {
+				self->tx_dummy_buf[buf_pos ++] = 0;
+				self->tx_dummy_buf[buf_pos ++] = 0;
+				self->tx_dummy_buf[buf_pos ++] = 0;
+				self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos ++];
+			} else if ((count % 4) == 2) {
+				self->tx_dummy_buf[buf_pos ++] = 0;
+				self->tx_dummy_buf[buf_pos ++] = 0;
+				self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos + 1];
+				self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos];
+				src_pos += 2;
+			} else {
+				self->tx_dummy_buf[buf_pos ++] = 0;
+				self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos + 2];
+				self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos + 1];
+				self->tx_dummy_buf[buf_pos ++] = src_u8[src_pos];
+				src_pos += 3;
+			}
+		}
+	}
+	bes2600_dbg(BES2600_DBG_SPI, "transver %u,%u(%d)\n", buf_pos, src_pos, count);
+#endif
+
+	bes2600_dbg(BES2600_DBG_SPI, "WRITE %d:%x(%x)\n", count, reg_use, reg_addr);
+
+	memset(t_cmd, 0, sizeof(*t_cmd));
+	memcpy(self->tx_dummy_buf, &reg_addr, 4);
+	memcpy(&self->tx_dummy_buf[buf_pos], &done_addr, 4);
+	memcpy(&self->tx_dummy_buf[buf_pos + 4], &done_val, 4);
+	t_cmd->tx_buf = self->tx_dummy_buf;
+	t_cmd->rx_buf = self->rx_dummy_buf;
+	t_cmd->len = buf_pos;
+	t_cmd->speed_hz = self->func->max_speed_hz;
+
+	bes2600_dbg_dump(BES2600_DBG_SPI, "WRITE:", (u8 *)t_cmd->tx_buf, t_cmd->len);
+
+	spi_message_init(m);
+	spi_message_add_tail(t_cmd, m);
+	if (!src_pos) {
+		memset(t_data, 0, sizeof(*t_data));
+		t_data->tx_buf = src;
+		t_data->rx_buf = self->rx_dummy_buf;
+		t_data->len = count;
+		t_data->speed_hz = self->func->max_speed_hz;
+		spi_message_add_tail(t_data, m);
+	}
+	ret = spi_sync(self->func, m);
+	if (ret) {
+		bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret);
+	}
+
+	if ((!addr) || (addr == BES_MISC_DATA_ADDR)) {
+		t_cmd->tx_buf = &self->tx_dummy_buf[buf_pos];
+		t_cmd->rx_buf = self->rx_dummy_buf;
+		t_cmd->len = 8;
+		t_cmd->speed_hz = self->func->max_speed_hz;
+		spi_message_init(m);
+		spi_message_add_tail(t_cmd, m);
+		ret = spi_sync(self->func, m);
+		if (ret)
+			bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d.\n", __func__, __LINE__, ret);
+	}
+
+	if (addr == BES_MISC_DATA_ADDR) {
+		done_val = BES_HOST_INT_SUBINT;
+		done_addr = SPI_WR_ADDR(BES_HOST_INT_REG_ID);
+		done_val = swab32(done_val);
+		done_addr = swab32(done_addr);
+		memcpy(&self->tx_dummy_buf[buf_pos], &done_addr, 4);
+		memcpy(&self->tx_dummy_buf[buf_pos + 4], &done_val, 4);
+		t_cmd->tx_buf = &self->tx_dummy_buf[buf_pos];
+		t_cmd->rx_buf = self->rx_dummy_buf;
+		t_cmd->len = 8;
+		t_cmd->speed_hz = self->func->max_speed_hz;
+		spi_message_init(m);
+		spi_message_add_tail(t_cmd, m);
+		ret = spi_sync(self->func, m);
+		if (ret)
+			bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d.\n", __func__, __LINE__, ret);
+	}
+
+	//print_hex_dump(KERN_NOTICE, "write_done  ", 0, 16, 1, &self->tx_dummy_buf[buf_pos], 8, false);
+	//temp_test(self);
+
+	return ret;
+}
+
+static void bes2600_spi_lock(struct sbus_priv *self)
+{
+	unsigned long flags;
+
+	DECLARE_WAITQUEUE(wait, current);
+
+	might_sleep();
+
+	add_wait_queue(&self->wq, &wait);
+	spin_lock_irqsave(&self->lock, flags);
+	while (1) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		if (!self->claimed)
+			break;
+		spin_unlock_irqrestore(&self->lock, flags);
+		schedule();
+		spin_lock_irqsave(&self->lock, flags);
+	}
+	set_current_state(TASK_RUNNING);
+	self->claimed = 1;
+	spin_unlock_irqrestore(&self->lock, flags);
+	remove_wait_queue(&self->wq, &wait);
+
+	return;
+}
+
+static void bes2600_spi_unlock(struct sbus_priv *self)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&self->lock, flags);
+	self->claimed = 0;
+	spin_unlock_irqrestore(&self->lock, flags);
+	wake_up(&self->wq);
+
+	return;
+}
+
+static irqreturn_t bes2600_spi_irq_handler(int irq, void *dev_id)
+{
+	struct sbus_priv *self = dev_id;
+
+	if (self->core) {
+		//bes2600_spi_lock(self);
+		bes2600_irq_handler(self->core);
+		//bes2600_spi_unlock(self);
+		return IRQ_HANDLED;
+	} else {
+		return IRQ_NONE;
+	}
+}
+
+struct bes2600_platform_data_spi *bes2600_get_platform_data(void);
+static int bes2600_spi_irq_subscribe(struct sbus_priv *self, sbus_irq_handler handler,
+				void *priv)
+{
+	int ret, irq;
+
+	bes2600_info(BES2600_DBG_SPI, "SW IRQ subscribe\n");
+
+	if (self->pdata && gpio_is_valid(self->pdata->irq_gpio)) {
+		self->gpio_irq = gpio_to_irq(self->pdata->irq_gpio);
+		bes2600_info(BES2600_DBG_SPI, "gpio for irq detected:%d,%d\n", self->pdata->irq_gpio, self->gpio_irq);
+		irq = self->gpio_irq;
+	} else {
+		irq = self->func->irq;
+	}
+
+	ret = request_threaded_irq(irq, NULL,
+				bes2600_spi_irq_handler,
+				IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				"bes2600_wlan_irq", self);
+
+	if (WARN_ON(ret < 0))
+		goto exit;
+
+	ret = enable_irq_wake(irq);
+	if (WARN_ON(ret))
+		goto free_irq;
+
+	return 0;
+
+free_irq:
+	free_irq(irq, self);
+
+exit:
+	return ret;
+}
+
+static int bes2600_spi_irq_unsubscribe(struct sbus_priv *self)
+{
+	int ret = 0, irq;
+
+	bes2600_info(BES2600_DBG_SPI, "SW IRQ unsubscribe\n");
+
+	if (!IS_ERR_VALUE((uintptr_t)self->gpio_irq))
+		irq = self->gpio_irq;
+	else
+		irq = self->func->irq;
+
+	disable_irq_wake(irq);
+	free_irq(irq, self);
+
+	return ret;
+}
+
+static int bes2600_spi_off(const struct bes2600_platform_data_spi *pdata)
+{
+	bes2600_info(BES2600_DBG_SPI, "%s enter\n", __func__);
+
+	if (pdata == NULL)
+		return 0;
+
+#if defined(BES2600_INDEPENDENT_EVB) || defined(BES2600_INTEGRATED_MODULE_V3)
+	if (gpio_is_valid(pdata->powerup)) {
+		bes2600_info(BES2600_DBG_SPI, "bes2600 powerdown.\n");
+		gpio_direction_output(pdata->powerup, 0);
+	}
+#endif
+
+	return 0;
+}
+
+static int bes2600_spi_on(const struct bes2600_platform_data_spi *pdata)
+{
+	bes2600_info(BES2600_DBG_SPI, "%s enter\n", __func__);
+
+	if (pdata == NULL)
+		return 0;
+
+#if defined(BES2600_INDEPENDENT_EVB) || defined(BES2600_INTEGRATED_MODULE_V3)
+	if (gpio_is_valid(pdata->powerup)) {
+		bes2600_info(BES2600_DBG_SPI, "bes2600 powerup.\n");
+		gpio_direction_output(pdata->powerup, 1);
+	}
+#endif
+
+	return 0;
+}
+
+static size_t bes2600_spi_align_size(struct sbus_priv *self, size_t size)
+{
+#if 0
+	return ((size + 3) & (~0x3));
+#else
+	return size;
+#endif
+}
+
+int bes2600_spi_set_block_size(struct sbus_priv *self, size_t size)
+{
+	return 0;
+}
+
+int bes2600_spi_send(struct bes2600_common *ar, u8 pipe, u32 len, u8 *buf)
+{
+	return 0;
+}
+
+void * bes2600_spi_read(struct bes2600_common *ar)
+{
+	return 0;
+}
+
+static int bes2600_spi_reg_write(struct sbus_priv *self, u32 reg,
+					const void *src, int count);
+static int bes2600_spi_init(struct sbus_priv *self, struct bes2600_common *ar)
+{
+	int ret = 0;
+	//u32 cfg_val;
+
+	if (self->tx_dummy_buf) {
+		bes2600_err(BES2600_DBG_SPI, "bes2600 sbus has been initialized ???\n");
+		return -1;
+	}
+
+	/* 4byte for read cfg_addr
+	 * 4byte for read cfg_val
+	 * 4byte for read addr
+	 * 4byte for status followed read addr
+	 * 1632byte for data
+	 * 4byte for done addr
+	 * 4byte for done val
+	 */
+#if 0
+	self->tx_dummy_buf = kmalloc(1632 + 24, GFP_KERNEL);
+	if (!self->tx_dummy_buf)
+		return -ENOMEM;
+
+	self->rx_dummy_buf = kmalloc(1632 + 24, GFP_KERNEL);
+	if (!self->rx_dummy_buf) {
+		ret = -ENOMEM;
+		goto free_tx;
+	}
+#else
+	self->tx_dummy_buf = (u8 *)__get_dma_pages(GFP_KERNEL, get_order((1632 + 24) * MAX_SEND_PACKETS_NUM));
+	if (!self->tx_dummy_buf)
+		return -ENOMEM;
+	self->rx_dummy_buf = (u8 *)__get_dma_pages(GFP_KERNEL, get_order((1632 + 24) * MAX_SEND_PACKETS_NUM));
+	if (!self->rx_dummy_buf)
+		goto free_tx;
+	bes2600_info(BES2600_DBG_SPI, "%s, buf_addr:%p:%p(%d)\n", __func__,
+			self->tx_dummy_buf, self->rx_dummy_buf, get_order((1632 + 24) * MAX_SEND_PACKETS_NUM));
+#endif
+	self->core = ar;
+
+	self->lmac_ptr_top = 0;
+	self->lmac_ptr = 0;
+	self->lmac_addr_min = 0;
+	self->lmac_addr_max = 0;
+	self->packets_upload = 0;
+
+	skb_queue_head_init(&self->rx_queue);
+
+#if 0
+	cfg_val = SPI_CONTINUOUS_CFG_VAL;
+	if ((ret = bes2600_spi_reg_write(self, SPI_RD_CFG_REG_ID, &cfg_val, 4))) {
+		bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret);
+		goto free_rx;
+	}
+#endif
+
+	return 0;
+
+#if 0
+free_rx:
+	free_pages((unsigned long)self->rx_dummy_buf, get_order((1632 + 24) * MAX_SEND_PACKETS_NUM));
+#endif
+
+free_tx:
+	free_pages((unsigned long)self->tx_dummy_buf, get_order((1632 + 24) * MAX_SEND_PACKETS_NUM));
+
+	return ret;
+}
+
+/* bes spi slave regs can only be accessed as DWORD(32-bit)
+ * if BYTE or WORD(16-bit) reg wants to be accessed,
+ * please extract form a DWORD read or write operation and this is not implemented yet
+ */
+static int bes2600_spi_reg_read(struct sbus_priv *self, u32 reg,
+					void *dst, int count)
+{
+	int ret = 0;
+	u32 reg_addr, cfg_val;
+	//u32 cfg_addr;
+	struct spi_message *m;
+	struct spi_transfer *t_cmd;
+
+	if (!dst || count != 4) {
+		bes2600_err(BES2600_DBG_SPI, "%s,%d %p:%d\n", __func__, __LINE__, dst, count);
+		return -EINVAL;
+	}
+	BUG_ON(!self);
+	BUG_ON(!self->tx_dummy_buf);
+	BUG_ON(!self->rx_dummy_buf);
+
+	m = &self->m;
+	t_cmd = &self->t_cmd;
+
+	//cfg_addr = SPI_WR_ADDR(SPI_RD_CFG_REG_ID);
+	//cfg_val = SPI_NCONTINUOUS_CFG_VAL;
+	reg_addr = SPI_RD_ADDR(reg);
+	//bes2600_info(BES2600_DBG_SPI, "TX %x:%x:%x(%x)\n", cfg_addr, cfg_val, reg_addr, reg);
+
+#if defined(__LITTLE_ENDIAN)
+	if (likely(self->func->bits_per_word == 8)) {
+		//cfg_addr = swab32(cfg_addr);
+		//cfg_val = swab32(cfg_val);
+		reg_addr = swab32(reg_addr);
+	}
+#endif
+
+	memset(t_cmd, 0, sizeof(*t_cmd));
+	//memcpy(self->tx_dummy_buf, &cfg_addr, 4);
+	//memcpy(self->tx_dummy_buf + 4, &cfg_val, 4);
+	memcpy(self->tx_dummy_buf + 8, &reg_addr, 4);
+	//t_cmd->tx_buf = &self->tx_dummy_buf;
+	//t_cmd->rx_buf = self->rx_dummy_buf;
+	//t_cmd->len = 4 + 4;
+	//spi_message_init(m);
+	//spi_message_add_tail(t_cmd, m);
+	//ret = spi_sync(self->func, m);
+	//if (ret) {
+		//bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret);
+		//goto exit;
+	//}
+
+	t_cmd->tx_buf = &self->tx_dummy_buf[8];
+	t_cmd->rx_buf = self->rx_dummy_buf;
+	t_cmd->len = 4 + 4 + 4;
+	t_cmd->speed_hz = self->func->max_speed_hz;
+
+	spi_message_init(m);
+	spi_message_add_tail(t_cmd, m);
+	ret = spi_sync(self->func, m);
+	if (!ret){
+
+		bes2600_dbg_dump(BES2600_DBG_SPI, "READ:", (u8 *)t_cmd->rx_buf, t_cmd->len);
+		cfg_val = *(u32 *)(t_cmd->rx_buf + 8);
+		cfg_val = swab32(cfg_val);
+		memcpy(dst, &cfg_val, 4);
+		//bes2600_info(BES2600_DBG_SPI, "reg_read val:%x\n", cfg_val);
+	}
+
+//exit:
+	return ret;
+}
+
+static int bes2600_spi_reg_write(struct sbus_priv *self, u32 reg,
+					const void *src, int count)
+{
+	int ret = 0;
+	u32 reg_addr, wr_val;
+	struct spi_message *m;
+	struct spi_transfer *t_cmd;
+
+	if (!src || count != 4) {
+		bes2600_err(BES2600_DBG_SPI, "%s,%d %p:%d\n", __func__, __LINE__, src, count);
+		return -EINVAL;
+	}
+	BUG_ON(!self);
+	BUG_ON(!self->tx_dummy_buf);
+	BUG_ON(!self->rx_dummy_buf);
+	m = &self->m;
+	t_cmd = &self->t_cmd;
+
+	reg_addr = SPI_WR_ADDR(reg);
+	memcpy(&wr_val, src, 4);
+	//bes2600_info(BES2600_DBG_SPI, "TX_CMD %08x(%08x):%08x\n", reg_addr, reg, wr_val);
+
+#if defined(__LITTLE_ENDIAN)
+	if (likely(self->func->bits_per_word == 8)) {
+		reg_addr = swab32(reg_addr);
+		wr_val = swab32(wr_val);
+		//bes2600_info(BES2600_DBG_SPI, "TRANSVERT_TX_CMD %08x:%08x\n", reg_addr, wr_val);
+	}
+#endif
+
+	memset(m, 0, sizeof(*m));
+	memset(t_cmd, 0, sizeof(*t_cmd));
+	memcpy(self->tx_dummy_buf, &reg_addr, 4);
+	memcpy(self->tx_dummy_buf + 4, &wr_val, 4);
+
+	t_cmd->tx_buf = self->tx_dummy_buf;
+	t_cmd->rx_buf = self->rx_dummy_buf;
+	t_cmd->len = 4 + 4;
+	t_cmd->speed_hz = self->func->max_speed_hz;
+
+	bes2600_dbg_dump(BES2600_DBG_SPI, "WRITE:", (u8 *)t_cmd->tx_buf,t_cmd->len);
+
+	spi_message_init(m);
+	spi_message_add_tail(t_cmd, m);
+	ret = spi_sync(self->func, m);
+	if (!ret){
+		//bes2600_info(BES2600_DBG_SPI, "reg_write:%x", *(u32 *)src);
+	} else {
+		bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d", __func__, __LINE__, ret);
+	}
+	return ret;
+}
+
+static int bes2600_spi_addr_update(struct sbus_priv *self)
+{
+	int ret;
+	u32 pos;
+	u32 diff = (u32)(self->lmac_ptr_top - self->lmac_ptr);
+
+	if (diff > 0 && diff < BES_LMAC_BUF_NUMS)
+		return 0;
+
+	if ((ret = bes2600_spi_reg_read(self, BES_LMAC_BUF_TOTAL, &self->lmac_ptr_top, 4))) {
+		return ret;
+	}
+
+	diff = (u32)(self->lmac_ptr_top - self->lmac_ptr);
+	BUG_ON(diff > BES_LMAC_BUF_NUMS);
+
+	pos = self->lmac_ptr & (BES_LMAC_BUF_NUMS - 1);
+	//bes2600_info(BES2600_DBG_SPI, "%s, %u %u %u\n", __func__, diff, pos, self->lmac_ptr_top);
+	if (BES_LMAC_BUF_NUMS - pos >= diff)
+		ret = bes2600_spi_memcpy_fromio(self, BES_LMAC_BUF_DESC + pos * 4, &self->lmac_ptr_pool[pos], diff << 2);
+	else {
+		ret = bes2600_spi_memcpy_fromio(self, BES_LMAC_BUF_DESC, self->lmac_ptr_pool, BES_LMAC_BUF_NUMS << 2);
+	}
+
+	if (unlikely(!self->lmac_addr_min)) {
+		self->lmac_addr_min = self->lmac_ptr_pool[0];
+		/* temporary workaround */
+		self->lmac_addr_max = self->lmac_ptr_pool[self->lmac_ptr_top - 1] + 1632 + 1632;
+	}
+
+	return ret;
+}
+
+static int bes2600_spi_packets_check(u32 ctrl_reg, u8 *packets)
+{
+	int i;
+	u32 single, total_cal = 0;
+	u32 packets_length;
+	u32 *buf_u32;
+
+	/* bit 31-28 indicate count of packets */
+	u32 packets_cnt = PACKET_COUNT_V2(ctrl_reg);
+	if (WARN_ON(packets_cnt > MAX_SEND_PACKETS_NUM))
+		return -200;
+
+	/* bit 27-0 indicate totoal length of packets */
+	packets_length = PACKET_TOTAL_LEN_V2(ctrl_reg);
+
+	/* first 32-bit: addr in mcu;
+	 * second 32-bit: packet length;
+	 * next: data
+	 */
+	for (i = 0; i < packets_cnt; i++) {
+		buf_u32 = (u32 *)(packets + total_cal);
+		//bes2600_info(BES2600_DBG_SPI, "%s, %x,%x,%x\n", __func__, buf_u32[0], buf_u32[1], ctrl_reg);
+		single = buf_u32[1];
+		if (WARN_ON(single > 1632)) {
+			return -201;
+		}
+		total_cal += single;
+		total_cal += 8;
+	}
+	//bes2600_info(BES2600_DBG_SPI, "%s, %d,%u,%u\n", __func__, packets_cnt, packets_length, total_cal);
+
+	if (WARN_ON(packets_length != total_cal)) {
+		return -202;
+	}
+
+	return 0;
+}
+
+static int bes2600_spi_extract_packets(struct sbus_priv *self, u32 ctrl_reg, u8 *data)
+{
+	int i;
+	u32 packets_cnt = PACKET_COUNT_V2(ctrl_reg);
+	u32 packet_len, pos = 0;
+	struct sk_buff *skb;
+	for (i = 0; i < packets_cnt; i++) {
+		packet_len = *(u32 *)&(data[pos + 4]);
+		skb = dev_alloc_skb(packet_len);
+		if (WARN_ON(!skb)) {
+			return -ENOMEM;
+		}
+		skb_trim(skb, 0);
+		skb_put(skb, packet_len);
+		memcpy(skb->data, &data[pos + 8], packet_len);
+		//bes2600_info(BES2600_DBG_SPI, "%s, %d,%d\n", __func__, packet_len, skb->len);
+		skb_queue_tail(&self->rx_queue, skb);
+		pos += packet_len;
+		pos += 8;
+	}
+	return 0;
+}
+
+static void *bes2600_spi_pipe_read(struct sbus_priv *self)
+{
+	int ret;
+	u32 ctrl_reg;
+	int total_len;
+	u8 *buf = &self->rx_dummy_buf[8];
+	struct sk_buff *skb;
+
+	skb = skb_dequeue(&self->rx_queue);
+	if (skb != NULL)
+		return skb;
+
+	bes2600_spi_lock(self);
+
+	if (unlikely(self->spi_active == false)) {
+		goto null_exit;
+	}
+
+	ret = bes2600_spi_reg_read(self, BES_TX_CTRL_REG_ID, &ctrl_reg, 4);
+	if (WARN_ON(ret))
+		goto null_exit;
+
+	if (!ctrl_reg)
+		goto null_exit;
+
+	total_len = PACKET_TOTAL_LEN_V2(ctrl_reg);
+	ret = bes2600_spi_memcpy_fromio(self, BES_TX_DATA_ADDR, buf, total_len);
+	if (WARN_ON(ret))
+		goto null_exit;
+
+	if ((ret = bes2600_spi_packets_check(ctrl_reg, buf)))
+		goto null_exit;
+
+	if ((ret = bes2600_spi_extract_packets(self, ctrl_reg, buf)))
+		goto null_exit;
+
+	bes2600_spi_unlock(self);
+
+	return skb_dequeue(&self->rx_queue);
+
+null_exit:
+	bes2600_spi_unlock(self);
+	return NULL;
+}
+
+void bes2600_sbus_dump(void *priv)
+{
+	struct sbus_priv *sbus_priv = (struct sbus_priv *)priv;
+	bes2600_info(BES2600_DBG_SPI, "%s, upload=%u\n", __func__, sbus_priv->packets_upload);
+}
+
+#if defined(CONFIG_BES2600_SPI_THROUGHPUT_TEST)
+#include <linux/proc_fs.h>
+static struct proc_dir_entry *bes_wlan_dir;
+#endif
+
+#ifdef CONFIG_BES2600_SPI_THROUGHPUT_TEST
+static struct proc_dir_entry *spi_throughput_entry;
+static struct spi_throughput_struct {
+	struct task_struct *thread;
+	wait_queue_head_t test_wq;
+	unsigned long test_count, test_packet_size;
+	unsigned long read_packets, irq_enters;
+	unsigned long average_time, max_time, min_time;
+	unsigned long total_time;
+	atomic_t rx;
+	void *bus_data;
+	char read_buf[512];
+} *spi_throughput_data;
+static int spi_throughput_test(void *arg);
+static const struct sbus_ops bes2600_spi_sbus_ops;
+
+static int spi_throughput_open(struct inode *inode, struct file *filp)
+{
+	bes2600_info(BES2600_DBG_SPI, "%s\n", __func__);
+	return 0;
+}
+
+static int spi_throughput_close(struct inode *inode, struct file *filp)
+{
+	bes2600_info(BES2600_DBG_SPI, "%s\n", __func__);
+	return 0;
+}
+
+static ssize_t spi_throughput_read(struct file *filp, char __user *buf,
+				size_t size, loff_t *ppos)
+{
+	bes2600_info(BES2600_DBG_SPI, "%s, %ld\n", __func__, size);
+	return 0;
+}
+
+static ssize_t spi_throughput_write(struct file *filp, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	char data[512];
+	char cnt_delimiter[] = "count=";
+	char size_delimiter[] = "size=";
+	char *cnt_p, *size_p;
+	unsigned long len;
+	char *end;
+	memset(data, 0, 512);
+	len = copy_from_user(data, buf, count < 511 ? count : 511);
+	bes2600_info(BES2600_DBG_SPI, "%s, %s\n", __func__, data);
+	if (strncmp(data, "start", 5) == 0) {
+		cnt_p = strstr(data, cnt_delimiter);
+		size_p = strstr(data, size_delimiter);
+		if (!cnt_p || !size_p)
+			goto exit;
+		bes2600_info(BES2600_DBG_SPI, "%s\n", cnt_p);
+		bes2600_info(BES2600_DBG_SPI, "%s\n", size_p);
+		if (cnt_p > (data + 511) || size_p > (data + 511))
+			goto exit;
+		spi_throughput_data->test_count = simple_strtoul(cnt_p + strlen(cnt_delimiter), &end, 10);
+		spi_throughput_data->test_packet_size = simple_strtoul(size_p + strlen(size_delimiter), &end, 10);
+		if(spi_throughput_data->test_packet_size > PAGE_SIZE) {
+			bes2600_info(BES2600_DBG_SPI, "%s, correct test_size %ld-->%ld\n", __func__, spi_throughput_data->test_packet_size,
+					PAGE_SIZE);
+			spi_throughput_data->test_packet_size = PAGE_SIZE;
+		}
+		spi_throughput_data->thread = kthread_run(spi_throughput_test, spi_throughput_data, "spi_throughput");
+	}
+
+	if (strncmp(data, "stop", 4) == 0) {
+		if (spi_throughput_data->thread)
+			kthread_stop(spi_throughput_data->thread);
+	}
+
+exit:
+	return count;
+}
+
+static const struct file_operations spi_throughput_ops = {
+	.owner = THIS_MODULE,
+	.read = spi_throughput_read,
+	.write = spi_throughput_write,
+	.open = spi_throughput_open,
+	.release = spi_throughput_close,
+};
+
+static int create_spi_throughput_entry(struct sbus_priv *self)
+{
+	if (!bes_wlan_dir) {
+		bes_wlan_dir = proc_mkdir_data("bes_wlan", 0666, NULL, NULL);
+		if (!bes_wlan_dir)
+			return -ENOMEM;
+	}
+
+	spi_throughput_data = kzalloc(sizeof(struct spi_throughput_struct), GFP_KERNEL);
+	if (!spi_throughput_data)
+		return -ENOMEM;
+
+	spi_throughput_entry = proc_create_data("spi_throughput", 0666, bes_wlan_dir, &spi_throughput_ops, NULL);
+	if (!spi_throughput_entry)
+		return -ENOMEM;
+
+	spi_throughput_data->bus_data = self;
+
+	return 0;
+}
+
+static int destory_spi_throughput_entry(void)
+{
+	if (spi_throughput_entry) {
+		if (spi_throughput_data)
+			kfree(spi_throughput_data);
+		proc_remove(spi_throughput_entry);
+	}
+	if (bes_wlan_dir) {
+		proc_remove(bes_wlan_dir);
+	}
+	return 0;
+}
+
+static irqreturn_t bes2600_spi_irq_handler_test(int irq, void *dev_id)
+{
+	struct spi_throughput_struct *test = dev_id;
+
+	test->irq_enters ++;
+	if (atomic_inc_return(&test->rx) == 1) {
+		wake_up(&test->test_wq);
+	}
+	return IRQ_HANDLED;
+}
+
+static int bes2600_spi_irq_subscribe_test(struct sbus_priv *self, void *priv)
+{
+	int ret, irq;
+
+	bes2600_info(BES2600_DBG_SPI, "%s\n", __func__);
+
+	if (!IS_ERR_VALUE((uintptr_t)self->gpio_irq))
+		irq = self->gpio_irq;
+	else
+		irq = self->func->irq;
+
+	ret = request_threaded_irq(irq, NULL,
+				bes2600_spi_irq_handler_test,
+				IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				"bes2600_wlan_irq", priv);
+
+	if (WARN_ON(ret < 0))
+		goto exit;
+
+	ret = enable_irq_wake(irq);
+	if (WARN_ON(ret))
+		goto free_irq;
+
+	return 0;
+
+free_irq:
+	free_irq(irq, self);
+
+exit:
+	return ret;
+}
+
+static int bes2600_spi_irq_unsubscribe_test(struct sbus_priv *self, void *priv)
+{
+	int ret = 0, irq;
+
+	bes2600_info(BES2600_DBG_SPI, "%s\n", __func__);
+
+	if (!IS_ERR_VALUE((uintptr_t)self->gpio_irq))
+		irq = self->gpio_irq;
+	else
+		irq = self->func->irq;
+
+	free_irq(irq, priv);
+
+	return ret;
+}
+
+static int spi_throughput_test(void *arg)
+{
+	u8 *test_data;
+	int ret, rx;
+	long status;
+	u32 next_len;
+	u32 val;
+	struct timeval time_before, time_after;
+	unsigned long jiffies_start = jiffies;
+	unsigned long interval;
+	struct spi_throughput_struct *test = spi_throughput_data;
+		struct sbus_priv *sbus_priv = test->bus_data;
+	bes2600_info(BES2600_DBG_SPI, "%s packets=%ld, packet_size=%ld\n", __func__, test->test_count, test->test_packet_size);
+
+	test_data = (u8 *)__get_free_page(GFP_KERNEL);
+	if (!test_data) {
+		bes2600_err(BES2600_DBG_SPI, "%s alloc page failed\n", __func__);
+		goto exit;
+	}
+
+	init_waitqueue_head(&test->test_wq);
+	bes2600_spi_sbus_ops.init(sbus_priv, NULL);
+	test->irq_enters = 0;
+	atomic_set(&test->rx, 0);
+	test->max_time = 0;
+	test->min_time = 100 * 1000000;
+	test->average_time = 0;
+	test->total_time = 0;
+
+	allow_signal(SIGKILL | SIGINT | SIGTERM);
+
+	bes2600_spi_irq_subscribe_test(sbus_priv, test);
+
+	bes2600_spi_sbus_ops.lock(sbus_priv);
+	val = test->test_count;
+	bes2600_spi_sbus_ops.sbus_reg_write(sbus_priv, BES_LMAC_BUF_USE, &val, 4);
+	val = BES_HOST_SLAVE_SYNC;
+	bes2600_spi_sbus_ops.sbus_reg_write(sbus_priv, BES_HOST_SYNC_REG_ID, &val, 4);
+	bes2600_spi_sbus_ops.unlock(sbus_priv);
+
+	do {
+		do_gettimeofday(&time_before);
+		status = wait_event_interruptible_timeout(test->test_wq,
+			({rx = atomic_xchg(&test->rx,  0); (rx);}), MAX_SCHEDULE_TIMEOUT);
+
+		if (status < 0 && status != -ERESTARTSYS) {
+			bes2600_err(BES2600_DBG_SPI, "%s,%d err=%ld\n", __func__, __LINE__, status);
+			break;
+		}
+
+		bes2600_spi_sbus_ops.lock(sbus_priv);
+		ret = bes2600_spi_sbus_ops.sbus_reg_read(sbus_priv, BES_TX_CTRL_REG_ID, &next_len, 4);
+		bes2600_spi_sbus_ops.unlock(sbus_priv);
+		if (ret) {
+			bes2600_err(BES2600_DBG_SPI, "%s,%d err=%d\n", __func__, __LINE__, ret);
+			break;
+		}
+
+rx_again:
+		if (next_len) {
+			bes2600_spi_sbus_ops.lock(sbus_priv);
+			ret = bes2600_spi_sbus_ops.sbus_memcpy_fromio(sbus_priv,
+					BES_TX_DATA_ADDR, test_data, next_len);
+			bes2600_spi_sbus_ops.unlock(sbus_priv);
+			if (ret)
+				break;
+			do_gettimeofday(&time_after);
+			interval = (time_after.tv_sec * 1000000 + time_after.tv_usec) -
+					(time_before.tv_sec * 1000000 + time_before.tv_usec);
+			if (test->max_time < interval)
+				test->max_time = interval;
+			if (test->min_time > interval)
+				test->min_time = interval;
+			if (test->average_time) {
+				test->average_time += interval;
+				test->average_time /= 2;
+			} else {
+				test->average_time = interval;
+			}
+			test->read_packets ++;
+			if (test->read_packets >= test->test_count) {
+				test->total_time = jiffies - jiffies_start;
+				bes2600_info(BES2600_DBG_SPI, "%s packets/irq=%ld %ld\n", __func__, test->read_packets, test->irq_enters);
+				break;
+			}
+		}
+
+		if (next_len & 0xffff) {
+			next_len &= 0xffff0000;
+			goto rx_again;
+		}
+
+	} while (!kthread_should_stop());
+	free_page((unsigned long)test_data);
+	bes2600_spi_irq_unsubscribe_test(sbus_priv, test);
+exit:
+	test->thread = NULL;
+	bes2600_info(BES2600_DBG_SPI, "%s exit ...\n", __func__);
+	bes2600_info(BES2600_DBG_SPI, "test results:%ld %ld %ld %ld\n", test->total_time, test->average_time,
+			test->max_time, test->min_time);
+	return 0;
+}
+
+#endif
+
+#if defined(PLAT_ALLWINNER) || defined(PLAT_QCOM_QM215)
+static struct bes2600_platform_data_spi bes_spi_plat_data = {
+	.spi_bits_per_word = 8,
+};
+#endif
+
+struct bes2600_platform_data_spi *bes2600_get_platform_data(void)
+{
+#if defined(PLAT_ALLWINNER) || defined(PLAT_QCOM_QM215)
+	return &bes_spi_plat_data;
+#else
+	return NULL;
+#endif
+}
+
+static int bes2600_platform_data_init(struct device *dev)
+{
+	int ret = 0;
+	struct device_node *np = dev->of_node;
+	struct gpio_config config;
+	struct bes2600_platform_data_spi *pdata = bes2600_get_platform_data();
+
+	if (!pdata)
+		return -ENODEV;
+
+	pdata->spi_bits_per_word = 8;
+	pdata->priv = to_spi_device(dev);
+
+	pdata->reset = of_get_named_gpio_flags(np, "reset",
+			0, (enum of_gpio_flags *)&config);
+		/* Ensure I/Os are pulled low */
+	if (gpio_is_valid(pdata->reset)) {
+		ret = gpio_request(pdata->reset, "bes2600_wlan_reset");
+		if (ret) {
+			bes2600_err(BES2600_DBG_SPI, "can't request reset_gpio:%d\n", ret);
+			pdata->reset = -1;
+			goto exit;
+		} else {
+			gpio_direction_output(pdata->reset, 0);
+		}
+	} else {
+		bes2600_err(BES2600_DBG_SPI, "reset is invalid\n");
+	}
+
+	pdata->powerup = of_get_named_gpio_flags(np, "powerup",
+			0, (enum of_gpio_flags *)&config);
+	if (gpio_is_valid(pdata->powerup)) {
+		ret = gpio_request(pdata->powerup, "bes2600_wlan_powerup");
+		if (ret) {
+			bes2600_err(BES2600_DBG_SPI, "can't request powerup_gpio:%d\n", ret);
+			pdata->powerup = -1;
+			goto exit;
+		} else {
+			gpio_direction_output(pdata->powerup, 0);
+		}
+	} else {
+		bes2600_err(BES2600_DBG_SPI, "powerup is invalid\n");
+	}
+
+	pdata->irq_gpio = of_get_named_gpio_flags(np, "irq_gpio", 0, (enum of_gpio_flags *)&config);
+	if (gpio_is_valid(pdata->irq_gpio)) {
+		ret = gpio_request(pdata->irq_gpio, "bes2600_wlan_irq_gpio");
+		if (ret) {
+			bes2600_err(BES2600_DBG_SPI, "can't request irq gpio:%d\n", ret);
+			goto exit;
+		} else {
+			ret = gpio_direction_input(pdata->irq_gpio);
+			if (ret < 0) {
+				bes2600_err(BES2600_DBG_SPI, "can't config irq_gpio input\n");
+				pdata->irq_gpio = -1;
+				goto exit;
+			}
+		}
+	} else {
+		bes2600_err(BES2600_DBG_SPI, "irq gpio is invalid\n");
+	}
+
+	pdata->host_wakeup_wlan = of_get_named_gpio_flags(np, "host_wakeup_wlan", 0, (enum of_gpio_flags *)&config);
+	if (gpio_is_valid(pdata->host_wakeup_wlan)) {
+		ret = gpio_request(pdata->host_wakeup_wlan, "bes2600_host_wakeup_wlan");
+		if (ret) {
+			bes2600_err(BES2600_DBG_SPI, "can't request host_wakeup_wlan gpio:%d\n", ret);
+			goto exit;
+		} else {
+			gpio_direction_output(pdata->host_wakeup_wlan, 0);
+		}
+	} else {
+		bes2600_err(BES2600_DBG_SPI, "irq gpio is invalid\n");
+	}
+
+exit:
+	return ret;
+}
+
+static void bes2600_platform_data_deinit(void)
+{
+	const struct bes2600_platform_data_spi *pdata = bes2600_get_platform_data();
+
+	if (pdata == NULL) {
+		return;
+	}
+
+	if (gpio_is_valid(pdata->reset)) {
+		gpio_free(pdata->reset);
+	}
+
+	if (gpio_is_valid(pdata->powerup)) {
+		gpio_free(pdata->powerup);
+	}
+
+	if (gpio_is_valid(pdata->irq_gpio)) {
+		gpio_free(pdata->irq_gpio);
+	}
+
+	if (gpio_is_valid(pdata->host_wakeup_wlan)) {
+		gpio_free(pdata->host_wakeup_wlan);
+	}
+
+}
+
+static int bes2600_spi_reset(struct sbus_priv *self)
+{
+	struct bes2600_platform_data_spi *plat_data = bes2600_get_platform_data();
+
+	bes2600_info(BES2600_DBG_SPI, "%s ...\n", __func__);
+
+	if (plat_data == NULL)
+		return 0;
+
+	if (gpio_is_valid(plat_data->reset)) {
+		gpio_set_value(plat_data->reset, 1);
+		mdelay(50);
+		gpio_set_value(plat_data->reset, 0);
+	}
+	return 0;
+}
+
+static int bes2600_spi_awake(struct sbus_priv *self)
+{
+	int retries = 0;
+	u32 sync_header;
+
+	bes2600_spi_lock(self);
+
+#if 0
+	if (gpio_is_valid(pdata->host_wakeup_wlan)) {
+		bes2600_dbg(BES2600_DBG_SPI, "toggle wake up line\n");
+		gpio_set_value(pdata->host_wakeup_wlan, 1);
+		msleep(20);
+		gpio_set_value(pdata->host_wakeup_wlan, 0);
+	} else {
+		bes2600_err(BES2600_DBG_SPI, "toggle wake up line failed\n");
+	}
+#endif
+
+	do {
+		bes2600_spi_reg_read(self, BES_HOST_SYNC_REG_ID, &sync_header, 4);
+		if (sync_header == BES_SLAVE_SYNC_HEADER) {
+			self->spi_active = true;
+			break;
+		} else {
+			msleep(10);
+		}
+		++retries;
+	} while (retries < 5);
+	bes2600_spi_unlock(self);
+
+	if (retries >= 5) {
+		bes2600_err(BES2600_DBG_SPI, "%s spi sync failed(%x)\n", __func__, sync_header);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void bes2600_gpio_wakeup_mcu(struct sbus_priv *self, int falg)
+{
+	unsigned long flags;
+	bool gpio_wakeup = false;
+	const struct bes2600_platform_data_spi *pdata = bes2600_get_platform_data();
+	if (pdata == NULL)
+		return;
+
+	bes2600_info(BES2600_DBG_SPI, "%s with %d\n", __func__, falg);
+
+	/* error check */
+	if(test_bit(falg, &self->gpio_wakup_flags)) {
+		bes2600_err(BES2600_DBG_SPI,
+			"repeat set gpio_wake_flag, sub_sys:%d", falg);
+		return;
+	}
+
+	/* check if this is the first subsystem that need mcu to keep awake */
+	local_irq_save(flags);
+	gpio_wakeup = (self->gpio_wakup_flags == 0);
+	local_irq_restore(flags);
+
+	/* do wakeup mcu operation */
+	if(gpio_wakeup) {
+		bes2600_info(BES2600_DBG_SPI, "pull high gpio by flag:%d\n", falg);
+		if (gpio_is_valid(pdata->host_wakeup_wlan)) {
+			gpio_set_value(pdata->host_wakeup_wlan, 1);
+			msleep(1);
+		} else {
+			bes2600_err(BES2600_DBG_SPI,
+				"%s, wakeup gpio is invalid\n", __func__);
+		}
+	}
+
+	/* set flag of gpio_wakeup_flags */
+	set_bit(falg, &self->gpio_wakup_flags);
+}
+
+static void bes2600_gpio_allow_mcu_sleep(struct sbus_priv *self, int falg)
+{
+	unsigned long flags;
+	bool gpio_sleep = false;
+	const struct bes2600_platform_data_spi *pdata = bes2600_get_platform_data();
+	if (pdata == NULL)
+		return;
+
+	bes2600_info(BES2600_DBG_SPI, "%s with %d\n", __func__, falg);
+
+	/* error check */
+	if(test_bit(falg, &self->gpio_wakup_flags) == 0) {
+		bes2600_err(BES2600_DBG_SPI,
+			"repeat clear gpio_wake_flag, sub_sys:%d", falg);
+		return;
+	}
+
+
+	/* clear flag of gpio_wakeup_flags */
+	clear_bit(falg, &self->gpio_wakup_flags);
+
+	/* check if this is the last subsystem that need mcu to keep awake */
+	local_irq_save(flags);
+	gpio_sleep = (self->gpio_wakup_flags == 0);
+	local_irq_restore(flags);
+
+	/* do wakeup mcu operation */
+	if(gpio_sleep) {
+		bes2600_info(BES2600_DBG_SPI, "pull low gpio by flag:%d\n", falg);
+		if (gpio_is_valid(pdata->host_wakeup_wlan)) {
+			gpio_set_value(pdata->host_wakeup_wlan, 0);
+		} else {
+			bes2600_err(BES2600_DBG_SPI,
+				"%s, wakeup gpio is invalid\n", __func__);
+		}
+	}
+}
+
+int bes2600_spi_active(struct sbus_priv *self, int sub_system)
+{
+	int ret = 0;
+
+	if (bes2600_chrdev_is_signal_mode() || sub_system == SUBSYSTEM_WIFI) {
+		uint32_t subint = 0, int_type = BES_HOST_INT_SUBINT, status, cfm;
+		int retries;
+
+		if (sub_system == SUBSYSTEM_MCU) {
+			subint |= BES_SUBSYSTEM_MCU_ACTIVE;
+			cfm = BES_SLAVE_STATUS_MCU_WAKEUP_READY;
+		} else if (sub_system == SUBSYSTEM_WIFI) {
+			subint |= BES_SUBSYSTEM_WIFI_ACTIVE;
+			cfm = BES_SLAVE_STATUS_WIFI_OPEN_READY;
+		} else if (sub_system == SUBSYSTEM_BT) {
+			subint |= BES_SUBSYSTEM_BT_ACTIVE;
+			cfm = BES_SLAVE_STATUS_BT_OPEN_READY;
+		} else {
+			return -EINVAL;
+		}
+
+		if ((ret = bes2600_spi_awake(self)))
+			return ret;
+
+		bes2600_spi_lock(self);
+
+		retries = 0;
+		do {
+			if ((ret = bes2600_spi_reg_write(self, BES_HOST_SUBINT_REG_ID, &subint, 4)))
+				goto exit;
+			if ((ret = bes2600_spi_reg_write(self, BES_HOST_INT_REG_ID, &int_type, 4)))
+				goto exit;
+			msleep(25);
+			if ((ret = bes2600_spi_reg_read(self, BES_SLAVE_STATUS_REG_ID, &status, 4)))
+				goto exit;
+			if (status & cfm)
+				break;
+			retries++;
+		} while (retries < 80);
+
+		if (retries >= 80) {
+			bes2600_err(BES2600_DBG_SPI, "bes2600_spi_active(%d) failed(0x%x)\n", sub_system, status);
+			ret = -110;
+			goto exit;
+		}
+exit:
+		bes2600_spi_unlock(self);
+	}
+
+	return ret;
+}
+
+static void bes2600_spi_work_empty(struct sbus_priv *self)
+{
+	struct sk_buff *skb;
+
+	while ((skb = skb_dequeue(&self->rx_queue))) {
+		dev_kfree_skb(skb);
+	}
+
+	self->lmac_ptr_top = 0;
+	self->lmac_ptr = 0;
+	self->lmac_addr_min = 0;
+	self->lmac_addr_max = 0;
+	self->packets_upload = 0;
+}
+
+int bes2600_spi_deactive(struct sbus_priv *self, int sub_system)
+{
+	int ret;
+
+	if (bes2600_chrdev_is_signal_mode()) {
+		u32 subint, cfm, status, int_type = BES_HOST_INT_SUBINT, sync = 0;
+		int retries;
+
+		if (sub_system == SUBSYSTEM_MCU) {
+			subint = BES_SUBSYSTEM_MCU_DEACTIVE;
+			cfm = BES_SLAVE_STATUS_MCU_WAKEUP_READY;
+		} else if (sub_system == SUBSYSTEM_WIFI) {
+			subint = BES_SUBSYSTEM_WIFI_DEACTIVE;
+			cfm = BES_SLAVE_STATUS_WIFI_OPEN_READY;
+		} else if (sub_system == SUBSYSTEM_BT) {
+			subint = BES_SUBSYSTEM_BT_DEACTIVE;
+			cfm = BES_SLAVE_STATUS_BT_OPEN_READY;
+		} else {
+			return -EINVAL;
+		}
+
+		bes2600_spi_lock(self);
+
+		retries = 0;
+		do {
+			if ((ret = bes2600_spi_reg_write(self, BES_HOST_SUBINT_REG_ID, &subint, 4)))
+				goto exit;
+			if ((ret = bes2600_spi_reg_write(self, BES_HOST_INT_REG_ID, &int_type, 4)))
+				goto exit;
+			msleep(10);
+			bes2600_spi_reg_read(self, BES_HOST_SYNC_REG_ID, &sync, 4);
+			if ((ret = bes2600_spi_reg_read(self, BES_SLAVE_STATUS_REG_ID, &status, 4)))
+				goto exit;
+			if ( (sync != BES_SLAVE_SYNC_HEADER) || !(cfm & status) ) {
+				break;
+			}
+			retries++;
+		} while (retries < 5);
+
+		if (retries >= 5) {
+			ret = -EIO;
+			bes2600_err(BES2600_DBG_SPI, "bes2600 spi deactive(%d) failed\n", sub_system);
+			goto exit;
+		}
+
+		if (sub_system == SUBSYSTEM_MCU)
+			self->spi_active = false;
+
+		if (sub_system == SUBSYSTEM_WIFI)
+			bes2600_spi_work_empty(self);
+
+	} else {
+		return 0;
+	}
+
+exit:
+	bes2600_spi_unlock(self);
+	return ret;
+}
+
+static int bes2600_spi_power_switch (struct sbus_priv *self, int on);
+static struct sbus_ops bes2600_spi_sbus_ops = {
+	.init = bes2600_spi_init,
+	.sbus_memcpy_fromio	= bes2600_spi_memcpy_fromio,
+	.sbus_memcpy_toio	= bes2600_spi_memcpy_toio,
+	.lock			= bes2600_spi_lock,
+	.unlock			= bes2600_spi_unlock,
+	.irq_subscribe		= bes2600_spi_irq_subscribe,
+	.irq_unsubscribe	= bes2600_spi_irq_unsubscribe,
+	.align_size		= bes2600_spi_align_size,
+	.set_block_size	= bes2600_spi_set_block_size,
+	.sbus_reg_read = bes2600_spi_reg_read,
+	.sbus_reg_write = bes2600_spi_reg_write,
+	.pipe_read = bes2600_spi_pipe_read,
+	.reset = bes2600_spi_reset,
+	.sbus_active = bes2600_spi_active,
+	.sbus_deactive = bes2600_spi_deactive,
+	.power_switch = bes2600_spi_power_switch,
+	.gpio_wake	    = bes2600_gpio_wakeup_mcu,
+	.gpio_sleep         = bes2600_gpio_allow_mcu_sleep,
+};
+
+/* Probe Function to be called by SPI stack when device is discovered */
+static int bes2600_spi_probe(struct spi_device *func)
+{
+	int ret;
+	const struct bes2600_platform_data_spi *plat_data;
+	struct sbus_priv *self;
+
+	if ((ret = bes2600_platform_data_init(&func->dev)))
+		return ret;
+
+	plat_data = bes2600_get_platform_data();
+	if ((ret = bes2600_spi_on(plat_data)))
+		goto err_on;
+
+	/* Sanity check speed */
+#ifdef PLAT_ALLWINNER_T507
+	if (!func->master->max_speed_hz || func->master->max_speed_hz > 48000000)
+		func->max_speed_hz = 48000000;
+	else
+		func->max_speed_hz = func->master->max_speed_hz;
+#endif
+
+	/* Fix up transfer size */
+	if (plat_data && plat_data->spi_bits_per_word)
+		func->bits_per_word = plat_data->spi_bits_per_word;
+	if (!func->bits_per_word)
+		func->bits_per_word = 16;
+
+	/* And finally.. */
+	func->mode = SPI_MODE_3;
+
+	bes2600_info(BES2600_DBG_SPI, "bes2600_wlan_spi: Probe called (FUNC %p CS %d M %d BPW %d CLK %d).\n",
+		func, func->chip_select, func->mode, func->bits_per_word,
+		func->max_speed_hz);
+
+	if ((ret = spi_setup(func))) {
+		bes2600_err(BES2600_DBG_SPI, "spi_setup() failed(%d)!\n", ret);
+		goto err_setup;
+	}
+
+	self = devm_kzalloc(&func->dev, sizeof(*self), GFP_KERNEL);
+	if (!self) {
+		bes2600_err(BES2600_DBG_SPI, "Can't allocate SPI sbus_priv.");
+		ret = -ENOMEM;
+		goto err_nomem;
+	}
+	pr_info("%s self=%p\n", __func__, self);
+
+	self->pdata = plat_data;
+	self->func = func;
+	self->dev = &func->dev;
+	self->gpio_wakup_flags = 0;
+	if (bes2600_chrdev_is_signal_mode())
+		self->spi_active = false;
+	else
+		self->spi_active = true;
+
+	spin_lock_init(&self->lock);
+
+	spi_set_drvdata(func, self);
+
+	init_waitqueue_head(&self->wq);
+
+	bes2600_reg_set_object(&bes2600_spi_sbus_ops, self);
+
+	if ((ret = bes2600_load_firmware(&bes2600_spi_sbus_ops, self)) < 0) {
+		bes2600_err(BES2600_DBG_SPI, "bes2600_load_firmware failed(%d)\n", ret);
+		goto err_probe;
+	}
+
+	if (ret) {
+		bes2600_chrdev_set_sbus_priv_data(NULL, true);
+		bes2600_spi_off(plat_data);
+		goto out;
+	}
+
+#ifdef CONFIG_BES2600_SPI_THROUGHPUT_TEST
+	return create_spi_throughput_entry(self);
+#endif
+	if ((ret = bes2600_core_probe(&bes2600_spi_sbus_ops,
+			      self, &func->dev, &self->core))) {
+		bes2600_err(BES2600_DBG_SPI, "bes2600_core_probe failed(%d)\n", ret);
+		goto err_probe;
+	}
+	bes2600_chrdev_set_sbus_priv_data(self, false);
+
+out:
+	return 0;
+
+err_probe:
+	bes2600_reg_set_object(NULL, NULL);
+err_nomem:
+err_setup:
+	bes2600_spi_off(plat_data);
+err_on:
+	bes2600_platform_data_deinit();
+	return ret;
+}
+
+int bes2600_register_net_dev(struct sbus_priv *bus_priv)
+{
+	int status = 0;
+	BUG_ON(!bus_priv);
+	status = bes2600_core_probe(&bes2600_spi_sbus_ops,
+			      bus_priv, bus_priv->dev, &bus_priv->core);
+
+	return status;
+}
+
+int bes2600_unregister_net_dev(struct sbus_priv *bus_priv)
+{
+	BUG_ON(!bus_priv);
+	if (bus_priv->core) {
+		bes2600_core_release(bus_priv->core);
+		bus_priv->core = NULL;
+	}
+	return 0;
+}
+
+bool bes2600_is_net_dev_created(struct sbus_priv *bus_priv)
+{
+	BUG_ON(!bus_priv);
+	return (bus_priv->core != NULL);
+}
+
+static int bes2600_spi_power_switch (struct sbus_priv *null_self, int on)
+{
+	int ret;
+	struct bes2600_platform_data_spi *pdata = bes2600_get_platform_data();
+	struct sbus_priv *self = spi_get_drvdata((struct spi_device *)pdata->priv);
+	pr_info("self:%p pdata:%p\n", self, pdata);
+	if (on) {
+		ret = bes2600_spi_on(pdata);
+		if ((ret = bes2600_load_firmware(&bes2600_spi_sbus_ops, self)) < 0) {
+			bes2600_err(BES2600_DBG_SPI, "%s bes2600_load_firmware failed(%d)\n", __func__, ret);
+			goto off;
+		}
+		if ((ret = bes2600_register_net_dev(self)))
+			goto off;
+		bes2600_chrdev_set_sbus_priv_data(self, false);
+		return 0;
+	} else {
+		bes2600_unregister_net_dev(self);
+		bes2600_chrdev_set_sbus_priv_data(NULL, false);
+	}
+
+off:
+	ret = bes2600_spi_off(self->pdata);
+	return ret;
+}
+
+/* Disconnect Function to be called by SPI stack when device is disconnected */
+static int bes2600_spi_disconnect(struct spi_device *func)
+{
+	struct sbus_priv *self = spi_get_drvdata(func);
+
+	bes2600_info(BES2600_DBG_SPI, "%s\n", __func__);
+
+#ifdef CONFIG_BES2600_SPI_THROUGHPUT_TEST
+	destory_spi_throughput_entry();
+#endif
+
+	if (self) {
+		//bes2600_spi_irq_unsubscribe(self);
+		if (self->core) {
+			bes2600_core_release(self->core);
+			bes2600_spi_off(self->pdata);
+			if (self->tx_dummy_buf)
+				free_pages((unsigned long)self->tx_dummy_buf, get_order((1632 + 24)) * MAX_SEND_PACKETS_NUM);
+			self->core = NULL;
+		}
+	}
+
+	bes2600_reg_set_object(NULL, NULL);
+	bes2600_platform_data_deinit();
+
+	return 0;
+}
+
+static int __maybe_unused bes2600_spi_suspend(struct device *dev)
+{
+	int ret = 0;
+	struct sbus_priv *self = spi_get_drvdata(to_spi_device(dev));
+
+	bes2600_info(BES2600_DBG_SPI, "%s enter\n", __func__);
+
+	bes2600_spi_lock(self);
+	if (self->spi_active == true)
+		ret = -EBUSY;
+	bes2600_spi_unlock(self);
+
+	/* XXX notify host that we have to keep BES2600 powered on? */
+	return ret;
+}
+
+static int __maybe_unused bes2600_spi_resume(struct device *dev)
+{
+	/* SPI master shoud be reinitialize? */
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(bes2600_pm_ops, bes2600_spi_suspend, bes2600_spi_resume);
+
+static const struct of_device_id bes_spi_dev_ids[] = {
+	{.compatible = "bes,wlan_2002"},
+	{},
+};
+
+static struct spi_driver spi_driver = {
+	.probe		= bes2600_spi_probe,
+	.remove		= bes2600_spi_disconnect,
+	.driver = {
+		.name		= "bes2600_wlan_spi",
+		.pm		= IS_ENABLED(CONFIG_PM) ? &bes2600_pm_ops : NULL,
+		.of_match_table	= of_match_ptr(bes_spi_dev_ids),
+	},
+};
+
+
+/* Init Module function -> Called by insmod */
+static int __init bes2600_spi_module_init(void)
+{
+	int ret;
+
+	bes2600_info(BES2600_DBG_SPI, "------Driver: bes2600.ko version :%s\n", BES2600_DRV_VERSION);
+
+	bes2600_chrdev_update_signal_mode();
+
+	ret = bes2600_chrdev_init(&bes2600_spi_sbus_ops);
+	if(ret)
+		return ret;
+
+	ret = spi_register_driver(&spi_driver);
+
+	return ret;
+}
+
+/* Called at Driver Unloading */
+static void __exit bes2600_spi_module_exit(void)
+{
+	spi_unregister_driver(&spi_driver);
+	bes2600_chrdev_free();
+}
+
+module_init(bes2600_spi_module_init);
+module_exit(bes2600_spi_module_exit);
diff --git a/drivers/staging/bes2600/bes2600_usb.c b/drivers/staging/bes2600/bes2600_usb.c
new file mode 100644
index 000000000000..e56750c13fd6
--- /dev/null
+++ b/drivers/staging/bes2600/bes2600_usb.c
@@ -0,0 +1,1554 @@
+#include <linux/module.h>
+#include <linux/usb.h>
+#include "bes2600.h"
+#include "sbus.h"
+#include "wsm.h"
+#include "bes2600_driver_mode.h"
+#include "bes_chardev.h"
+#define BES2600_USB_PIPE_INVALID BES2600_USB_PIPE_MAX
+
+struct sbus_priv;
+/* usb device object */
+struct bes2600_usb_pipe {
+	struct list_head urb_list_head;
+	struct usb_anchor urb_submitted;
+	u32 urb_alloc;
+	u32 urb_cnt;
+	u32 urb_cnt_thresh;
+	atomic_t urb_pending_cnt;
+	unsigned int usb_pipe_handle;
+	u32 flags;
+	u8 ep_address;
+	u8 logical_pipe_num;
+	struct sbus_priv *ar_usb;
+	u16 max_packet_size;
+	struct work_struct io_complete_work;
+	struct sk_buff_head io_comp_queue;
+	struct usb_endpoint_descriptor *ep_desc;
+};
+
+struct sbus_priv {
+	/* protects pipe->urb_list_head and  pipe->urb_cnt */
+	spinlock_t cs_lock;
+
+	struct usb_device *udev;
+	struct usb_interface *interface;
+	struct bes2600_usb_pipe pipes[BES2600_USB_PIPE_MAX];
+	u8 *diag_cmd_buffer;
+	u8 *diag_resp_buffer;
+	struct bes2600_common *ar;
+	spinlock_t rx_queue_lock;
+	struct sk_buff_head rx_queue;
+	void * btdev;
+
+	spinlock_t status_lock;
+	sbus_irq_handler usb_irq_handler;
+	void *irq_data;
+	u32 int_control_reg;
+	u32 int_status_reg;
+};
+
+#define BES2600_USB_PIPE_FLAG_TX    (1 << 0)
+
+/* usb urb object */
+struct bes2600_urb_context {
+	struct list_head link;
+	struct bes2600_usb_pipe *pipe;
+	struct sk_buff *skb;
+	struct urb *urb;
+	struct bes2600_common *ar;
+};
+
+/* constants */
+#define TX_URB_COUNT            49
+#define RX_URB_COUNT            32
+#define BES2600_USB_RX_BUFFER_SIZE  8192
+
+
+/* USB endpoint definitions */
+#define BES2600_USB_EP_ADDR_CTRL_IN             0x81
+#define BES2600_USB_EP_ADDR_WLAN_IN             0x82
+#define BES2600_USB_EP_ADDR_BT_IN               0x83
+
+#define BES2600_USB_EP_ADDR_CTRL_OUT            0x01
+#define BES2600_USB_EP_ADDR_WLAN_OUT            0x02
+#define BES2600_USB_EP_ADDR_BT_OUT              0x03
+
+/* diagnostic command defnitions */
+#define BES2600_USB_CONTROL_REQ_SEND_BMI_CMD        1
+#define BES2600_USB_CONTROL_REQ_RECV_BMI_RESP       2
+#define BES2600_USB_CONTROL_REQ_DIAG_CMD            3
+#define BES2600_USB_CONTROL_REQ_DIAG_RESP           4
+
+#define BES2600_USB_CTRL_DIAG_CC_READ               0
+#define BES2600_USB_CTRL_DIAG_CC_WRITE              1
+
+struct bes2600_usb_ctrl_diag_cmd_write {
+	__le32 cmd;
+	__le32 address;
+	__le32 value;
+	__le32 _pad[1];
+} __packed;
+
+struct bes2600_usb_ctrl_diag_cmd_read {
+	__le32 cmd;
+	__le32 address;
+} __packed;
+
+struct bes2600_usb_ctrl_diag_resp_read {
+	__le32 value;
+} __packed;
+
+/* function declarations */
+static void bes2600_usb_recv_complete(struct urb *urb);
+
+#define BES2600_USB_IS_BULK_EP(attr) (((attr) & 3) == 0x02)
+#define BES2600_USB_IS_INT_EP(attr)  (((attr) & 3) == 0x03)
+#define BES2600_USB_IS_ISOC_EP(attr)  (((attr) & 3) == 0x01)
+#define BES2600_USB_IS_DIR_IN(addr)  ((addr) & 0x80)
+
+static void bes2600_usb_irq_handler(struct sbus_priv *ar_usb)
+{
+	sbus_irq_handler handler = NULL;
+	void *priv_data = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ar_usb->status_lock,flags);
+	handler = ar_usb->usb_irq_handler;
+	priv_data = ar_usb->irq_data;
+	spin_unlock_irqrestore(&ar_usb->status_lock,flags);
+
+	if(handler != 0)
+		handler(priv_data);
+}
+
+static int bes2600_usb_test_control_reg(struct sbus_priv *ar_usb, int bitval)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ar_usb->status_lock,flags);
+	ret = ar_usb->int_control_reg & bitval;
+	spin_unlock_irqrestore(&ar_usb->status_lock,flags);
+	return ret;
+}
+
+static void bes2600_usb_set_status_reg(struct sbus_priv *ar_usb, int bitval)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ar_usb->status_lock,flags);
+	ret = ar_usb->int_status_reg |= bitval;
+	spin_unlock_irqrestore(&ar_usb->status_lock,flags);
+}
+
+/* pipe/urb operations */
+static struct bes2600_urb_context *
+bes2600_usb_alloc_urb_from_pipe(struct bes2600_usb_pipe *pipe)
+{
+	struct bes2600_urb_context *urb_context = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags);
+	if (!list_empty(&pipe->urb_list_head)) {
+		urb_context =
+		    list_first_entry(&pipe->urb_list_head,
+				     struct bes2600_urb_context, link);
+		list_del(&urb_context->link);
+		pipe->urb_cnt--;
+	}
+	spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags);
+
+	return urb_context;
+}
+
+static void bes2600_usb_free_urb_to_pipe(struct bes2600_usb_pipe *pipe,
+					struct bes2600_urb_context *urb_context)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags);
+	pipe->urb_cnt++;
+
+	list_add(&urb_context->link, &pipe->urb_list_head);
+	spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags);
+}
+
+static void bes2600_usb_cleanup_recv_urb(struct bes2600_urb_context *urb_context)
+{
+	if (urb_context->skb) {
+		dev_kfree_skb(urb_context->skb);
+		urb_context->skb = NULL;
+	}
+	bes2600_usb_free_urb_to_pipe(urb_context->pipe, urb_context);
+}
+
+static inline struct sbus_priv *bes2600_usb_priv(struct bes2600_common *ar)
+{
+	return (struct sbus_priv *)ar->sbus_priv;
+}
+
+/* pipe resource allocation/cleanup */
+static int bes2600_usb_alloc_pipe_resources(struct bes2600_usb_pipe *pipe,
+					   int urb_cnt)
+{
+	struct bes2600_urb_context *urb_context;
+	int status = 0, i;
+
+	INIT_LIST_HEAD(&pipe->urb_list_head);
+	init_usb_anchor(&pipe->urb_submitted);
+
+	for (i = 0; i < urb_cnt; i++) {
+		urb_context = kzalloc(sizeof(struct bes2600_urb_context),
+				      GFP_KERNEL);
+		if (urb_context == NULL) {
+			status = -ENOMEM;
+			goto fail_alloc_pipe_resources;
+		}
+
+		urb_context->urb = usb_alloc_urb(0, GFP_ATOMIC);
+		if (urb_context->urb == NULL) {
+			kfree(urb_context);
+			status = -ENOMEM;
+			goto fail_alloc_pipe_resources;
+		}
+		urb_context->pipe = pipe;
+
+		/*
+		 * we are only allocate the urb contexts here, the actual URB
+		 * is allocated from the kernel as needed to do a transaction
+		 */
+		pipe->urb_alloc++;
+		bes2600_usb_free_urb_to_pipe(pipe, urb_context);
+	}
+
+	bes2600_dbg(BES2600_DBG_USB,
+		   "bes2600 usb: alloc resources lpipe:%d hpipe:0x%X urbs:%d\n",
+		   pipe->logical_pipe_num, pipe->usb_pipe_handle,
+		   pipe->urb_alloc);
+
+fail_alloc_pipe_resources:
+	return status;
+}
+
+static void bes2600_usb_free_pipe_resources(struct bes2600_usb_pipe *pipe)
+{
+	struct bes2600_urb_context *urb_context;
+
+	if (pipe->ar_usb == NULL) {
+		/* nothing allocated for this pipe */
+		return;
+	}
+
+	bes2600_dbg(BES2600_DBG_USB,
+		   "bes2600 usb: free resources lpipe:%d"
+		   "hpipe:0x%X urbs:%d avail:%d\n",
+		   pipe->logical_pipe_num, pipe->usb_pipe_handle,
+		   pipe->urb_alloc, pipe->urb_cnt);
+
+	if (pipe->urb_alloc != pipe->urb_cnt) {
+		bes2600_dbg(BES2600_DBG_USB,
+			   "bes2600 usb: urb leak! lpipe:%d"
+			   "hpipe:0x%X urbs:%d avail:%d\n",
+			   pipe->logical_pipe_num, pipe->usb_pipe_handle,
+			   pipe->urb_alloc, pipe->urb_cnt);
+	}
+
+	while (true) {
+		urb_context = bes2600_usb_alloc_urb_from_pipe(pipe);
+		if (urb_context == NULL)
+			break;
+		usb_free_urb(urb_context->urb);
+		urb_context->urb = NULL;
+		kfree(urb_context);
+	}
+}
+
+static void bes2600_usb_cleanup_pipe_resources(struct sbus_priv *ar_usb)
+{
+	int i;
+
+	for (i = 0; i < BES2600_USB_PIPE_MAX; i++){
+		if((i != BES2600_USB_PIPE_RX_BT) &&
+		   (i != BES2600_USB_PIPE_TX_BT))
+			bes2600_usb_free_pipe_resources(&ar_usb->pipes[i]);
+	}
+}
+
+static u8 bes2600_usb_get_logical_pipe_num(struct sbus_priv *ar_usb,
+					  u8 ep_address, int *urb_count)
+{
+	u8 pipe_num = BES2600_USB_PIPE_INVALID;
+
+	switch (ep_address) {
+	case BES2600_USB_EP_ADDR_CTRL_IN:
+		pipe_num = BES2600_USB_PIPE_RX_CTRL;
+		*urb_count = RX_URB_COUNT;
+		break;
+	case BES2600_USB_EP_ADDR_WLAN_IN:
+		pipe_num = BES2600_USB_PIPE_RX_WLAN;
+		*urb_count = RX_URB_COUNT;
+		break;
+	case BES2600_USB_EP_ADDR_BT_IN:
+		pipe_num = BES2600_USB_PIPE_RX_BT;
+		*urb_count = RX_URB_COUNT;
+		break;
+	case BES2600_USB_EP_ADDR_CTRL_OUT:
+		pipe_num = BES2600_USB_PIPE_TX_CTRL;
+		*urb_count = TX_URB_COUNT;
+		break;
+	case BES2600_USB_EP_ADDR_WLAN_OUT:
+		pipe_num = BES2600_USB_PIPE_TX_WLAN;
+		*urb_count = TX_URB_COUNT;
+		break;
+	case BES2600_USB_EP_ADDR_BT_OUT:
+		pipe_num = BES2600_USB_PIPE_TX_BT;
+		*urb_count = TX_URB_COUNT;
+		break;
+	default:
+		/* note: there may be endpoints not currently used */
+		break;
+	}
+
+	return pipe_num;
+}
+
+static int bes2600_usb_setup_pipe_resources(struct sbus_priv *ar_usb)
+{
+	struct usb_interface *interface = ar_usb->interface;
+	struct usb_host_interface *iface_desc = interface->cur_altsetting;
+	struct usb_endpoint_descriptor *endpoint;
+	struct bes2600_usb_pipe *pipe;
+	int i, urbcount, status = 0;
+	u8 pipe_num;
+
+	bes2600_dbg(BES2600_DBG_USB, "setting up USB Pipes using interface\n");
+
+	/* walk decriptors and setup pipes */
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+
+		if (BES2600_USB_IS_BULK_EP(endpoint->bmAttributes)) {
+			bes2600_dbg(BES2600_DBG_USB,
+				   "%s Bulk Ep:0x%2.2X maxpktsz:%d\n",
+				   BES2600_USB_IS_DIR_IN
+				   (endpoint->bEndpointAddress) ?
+				   "RX" : "TX", endpoint->bEndpointAddress,
+				   le16_to_cpu(endpoint->wMaxPacketSize));
+		} else if (BES2600_USB_IS_INT_EP(endpoint->bmAttributes)) {
+			bes2600_dbg(BES2600_DBG_USB,
+				   "%s Int Ep:0x%2.2X maxpktsz:%d interval:%d\n",
+				   BES2600_USB_IS_DIR_IN
+				   (endpoint->bEndpointAddress) ?
+				   "RX" : "TX", endpoint->bEndpointAddress,
+				   le16_to_cpu(endpoint->wMaxPacketSize),
+				   endpoint->bInterval);
+		} else if (BES2600_USB_IS_ISOC_EP(endpoint->bmAttributes)) {
+			/* TODO for ISO */
+			bes2600_dbg(BES2600_DBG_USB,
+				   "%s ISOC Ep:0x%2.2X maxpktsz:%d interval:%d\n",
+				   BES2600_USB_IS_DIR_IN
+				   (endpoint->bEndpointAddress) ?
+				   "RX" : "TX", endpoint->bEndpointAddress,
+				   le16_to_cpu(endpoint->wMaxPacketSize),
+				   endpoint->bInterval);
+		}
+		urbcount = 0;
+
+		pipe_num =
+		    bes2600_usb_get_logical_pipe_num(ar_usb,
+						    endpoint->bEndpointAddress,
+						    &urbcount);
+		if (pipe_num == BES2600_USB_PIPE_INVALID)
+			continue;
+
+		pipe = &ar_usb->pipes[pipe_num];
+		if (pipe->ar_usb != NULL) {
+			/* hmmm..pipe was already setup */
+			continue;
+		}
+
+		atomic_set(&pipe->urb_pending_cnt, 0);
+		pipe->ar_usb = ar_usb;
+		pipe->logical_pipe_num = pipe_num;
+		pipe->ep_address = endpoint->bEndpointAddress;
+		pipe->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize);
+
+		if (BES2600_USB_IS_BULK_EP(endpoint->bmAttributes)) {
+			if (BES2600_USB_IS_DIR_IN(pipe->ep_address)) {
+				pipe->usb_pipe_handle =
+				    usb_rcvbulkpipe(ar_usb->udev,
+						    pipe->ep_address);
+			} else {
+				pipe->usb_pipe_handle =
+				    usb_sndbulkpipe(ar_usb->udev,
+						    pipe->ep_address);
+			}
+		} else if (BES2600_USB_IS_INT_EP(endpoint->bmAttributes)) {
+			if (BES2600_USB_IS_DIR_IN(pipe->ep_address)) {
+				pipe->usb_pipe_handle =
+				    usb_rcvintpipe(ar_usb->udev,
+						   pipe->ep_address);
+			} else {
+				pipe->usb_pipe_handle =
+				    usb_sndintpipe(ar_usb->udev,
+						   pipe->ep_address);
+			}
+		} else if (BES2600_USB_IS_ISOC_EP(endpoint->bmAttributes)) {
+			/* TODO for ISO */
+			if (BES2600_USB_IS_DIR_IN(pipe->ep_address)) {
+				pipe->usb_pipe_handle =
+				    usb_rcvisocpipe(ar_usb->udev,
+						    pipe->ep_address);
+			} else {
+				pipe->usb_pipe_handle =
+				    usb_sndisocpipe(ar_usb->udev,
+						    pipe->ep_address);
+			}
+		}
+
+		pipe->ep_desc = endpoint;
+
+		if (!BES2600_USB_IS_DIR_IN(pipe->ep_address))
+			pipe->flags |= BES2600_USB_PIPE_FLAG_TX;
+
+		if((pipe_num != BES2600_USB_PIPE_RX_BT) &&
+			(pipe_num !=  BES2600_USB_PIPE_TX_BT)){
+			status = bes2600_usb_alloc_pipe_resources(pipe, urbcount);
+			if (status != 0)
+				break;
+		}
+	}
+#ifdef CONFIG_BES2600_BT
+	status = bes2600_btusb_setup_pipes(ar_usb);
+#endif
+
+	return status;
+}
+
+/* pipe operations */
+static void bes2600_usb_post_recv_transfers(struct bes2600_usb_pipe *recv_pipe,
+					   int buffer_length)
+{
+	struct bes2600_urb_context *urb_context;
+	struct urb *urb;
+	int usb_status;
+
+	while (true) {
+		urb_context = bes2600_usb_alloc_urb_from_pipe(recv_pipe);
+		if (urb_context == NULL)
+			break;
+
+		urb_context->skb = dev_alloc_skb(buffer_length);
+		if (urb_context->skb == NULL)
+			goto err_cleanup_urb;
+
+		urb = urb_context->urb;
+		BUG_ON(!urb);
+		usb_fill_bulk_urb(urb,
+				  recv_pipe->ar_usb->udev,
+				  recv_pipe->usb_pipe_handle,
+				  urb_context->skb->data,
+				  buffer_length,
+				  bes2600_usb_recv_complete, urb_context);
+
+		bes2600_dbg(BES2600_DBG_USB,
+			   "bes2600 usb: bulk recv submit:%d, 0x%X (ep:0x%2.2X), %d bytes buf:0x%p\n",
+			   recv_pipe->logical_pipe_num,
+			   recv_pipe->usb_pipe_handle, recv_pipe->ep_address,
+			   buffer_length, urb_context->skb);
+
+		usb_anchor_urb(urb, &recv_pipe->urb_submitted);
+		usb_status = usb_submit_urb(urb, GFP_ATOMIC);
+		if (usb_status) {
+			bes2600_dbg(BES2600_DBG_USB,
+				   "bes2600 usb : usb bulk recv failed %d\n",
+				   usb_status);
+			usb_unanchor_urb(urb);
+			goto err_cleanup_urb;
+		}
+	}
+	return;
+
+err_cleanup_urb:
+	bes2600_usb_cleanup_recv_urb(urb_context);
+	return;
+}
+
+static void bes2600_usb_flush_all(struct sbus_priv *ar_usb)
+{
+	int i;
+
+	for (i = 0; i < BES2600_USB_PIPE_MAX; i++) {
+		if((i != BES2600_USB_PIPE_RX_BT) &&
+		   (i !=  BES2600_USB_PIPE_TX_BT)){
+			if (ar_usb->pipes[i].ar_usb != NULL)
+				usb_kill_anchored_urbs(&ar_usb->pipes[i].urb_submitted);
+		}
+	}
+
+	/*
+	 * Flushing any pending I/O may schedule work this call will block
+	 * until all scheduled work runs to completion.
+	 */
+	flush_scheduled_work();
+}
+
+static void bes2600_usb_start_recv_pipes(struct sbus_priv *ar_usb)
+{
+	ar_usb->pipes[BES2600_USB_PIPE_RX_WLAN].urb_cnt_thresh = 1;
+	bes2600_usb_post_recv_transfers(&ar_usb->pipes[BES2600_USB_PIPE_RX_WLAN], BES2600_USB_RX_BUFFER_SIZE);
+}
+
+void bes2600_recv_buf_put(struct sbus_priv *ar_usb, struct sk_buff *skb)
+{
+	unsigned long flags;
+	if (!skb)
+		return;
+
+	spin_lock_irqsave(&ar_usb->rx_queue_lock, flags);
+	skb_queue_tail(&ar_usb->rx_queue, skb);
+	spin_unlock_irqrestore(&ar_usb->rx_queue_lock, flags);
+}
+
+struct sk_buff *bes2600_recv_buf_get(struct sbus_priv *ar_usb)
+{
+	unsigned long flags;
+	struct sk_buff *skb = NULL;
+
+	spin_lock_irqsave(&ar_usb->rx_queue_lock, flags);
+	skb = skb_dequeue(&ar_usb->rx_queue);
+	spin_unlock_irqrestore(&ar_usb->rx_queue_lock, flags);
+
+	return skb;
+}
+/* hif usb rx/tx completion functions */
+static void bes2600_usb_recv_complete(struct urb *urb)
+{
+	struct bes2600_urb_context *urb_context = urb->context;
+	struct bes2600_usb_pipe *pipe = urb_context->pipe;
+	struct sk_buff *skb = NULL;
+	int status = 0;
+
+	bes2600_dbg(BES2600_DBG_USB,
+		   "%s: recv pipe: %d, stat:%d, len:%d urb:0x%p\n", __func__,
+		   pipe->logical_pipe_num, urb->status, urb->actual_length,
+		   urb);
+
+	if (urb->status != 0) {
+		status = -EIO;
+		switch (urb->status) {
+		case -ECONNRESET:
+		case -ENOENT:
+		case -ESHUTDOWN:
+			/*
+			 * no need to spew these errors when device
+			 * removed or urb killed due to driver shutdown
+			 */
+			status = -ECANCELED;
+			break;
+		default:
+			bes2600_dbg(BES2600_DBG_USB,
+				   "%s recv pipe: %d (ep:0x%2.2X), failed:%d\n",
+				   __func__, pipe->logical_pipe_num,
+				   pipe->ep_address, urb->status);
+			break;
+		}
+		goto cleanup_recv_urb;
+	}
+
+	if (urb->actual_length == 0)
+		goto cleanup_recv_urb;
+
+	skb = urb_context->skb;
+	skb_put(skb, urb->actual_length);
+
+	/* we are going to pass it up */
+	urb_context->skb = NULL;
+
+	// set virtual register
+	if(bes2600_usb_test_control_reg(pipe->ar_usb, BES_USB_FW_RX_INDICATION))
+		bes2600_usb_set_status_reg(pipe->ar_usb, BES_USB_FW_RX_INDICATION);
+
+	/* note: queue implements a lock */
+#ifndef BES2600_RX_IN_BH
+	skb_queue_tail(&pipe->io_comp_queue, skb);
+	schedule_work(&pipe->io_complete_work);
+#else
+	bes2600_recv_buf_put(pipe->ar_usb, skb);
+	bes2600_usb_irq_handler(pipe->ar_usb);
+#endif
+cleanup_recv_urb:
+	bes2600_usb_cleanup_recv_urb(urb_context);
+	usb_unanchor_urb(urb);
+
+	if (status == 0 &&
+	    pipe->urb_cnt >= pipe->urb_cnt_thresh) {
+		/* our free urbs are piling up, post more transfers */
+		bes2600_usb_post_recv_transfers(pipe, BES2600_USB_RX_BUFFER_SIZE);
+	}
+}
+
+static void bes2600_usb_transmit_complete(struct urb *urb)
+{
+	struct bes2600_urb_context *urb_context = urb->context;
+	struct bes2600_usb_pipe *pipe = urb_context->pipe;
+	struct sk_buff *skb;
+
+	bes2600_dbg(BES2600_DBG_USB, "%s: pipe: %d, stat:%d, len:%d\n",
+		   __func__, pipe->logical_pipe_num, urb->status, urb->actual_length);
+
+	if (urb->status != 0) {
+		bes2600_err(BES2600_DBG_USB,
+			   "%s:  pipe: %d, failed:%d\n",
+			   __func__, pipe->logical_pipe_num, urb->status);
+	}
+
+	skb = urb_context->skb;
+	urb_context->skb = NULL;
+	bes2600_usb_free_urb_to_pipe(urb_context->pipe, urb_context);
+	usb_unanchor_urb(urb);
+
+	atomic_sub(1, &pipe->urb_pending_cnt);
+
+	// set virtual register
+	if(!urb->status && bes2600_usb_test_control_reg(pipe->ar_usb, BES_USB_FW_TX_DONE))
+		bes2600_usb_set_status_reg(pipe->ar_usb, BES_USB_FW_TX_DONE);
+
+	// notify tx done event
+	bes2600_usb_irq_handler(pipe->ar_usb);
+
+	/* note: queue implements a lock */
+	//skb_queue_tail(&pipe->io_comp_queue, skb);
+	//schedule_work(&pipe->io_complete_work);
+}
+int wsm_release_tx_buffer(struct bes2600_common *priv, int count);
+void bes2600_bh_wakeup(struct bes2600_common *hw_priv);
+
+void bes2600_usb_rx_complete(struct bes2600_common *priv, struct sk_buff *skb)
+{
+	static u8 data[1600];
+	static u32 cnt = 0;
+
+	struct wsm_hdr *wsm;
+	size_t wsm_len;
+	u16 wsm_id;
+	u8 wsm_seq;
+	u32 confirm_label = 0x0; /* wsm to mcu cmd cnfirm label */
+
+	wsm = (struct wsm_hdr *)skb->data;
+	wsm_len = __le16_to_cpu(wsm->len);
+	if (WARN_ON(wsm_len > skb->len))
+		goto err;
+
+	if (priv->wsm_enable_wsm_dumps)
+		bes2600_dbg_dump(BES2600_DBG_SPI, "<--", skb->data, wsm_len);
+
+	wsm_id  = __le16_to_cpu(wsm->id) & 0xFFF;
+	wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
+
+	skb_trim(skb, wsm_len);
+
+	if (wsm_id == 0x0800) {
+		wsm_handle_exception(priv,
+				     &skb->data[sizeof(*wsm)],
+				     wsm_len - sizeof(*wsm));
+		bes2600_err(BES2600_DBG_SYS, "wsm exception.!\n");
+		goto err;
+	} else if ((wsm_seq != priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)])) {
+		bes2600_err(BES2600_DBG_SYS, "seq error %u. %u. 0x%x.", wsm_seq, priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)], wsm_id);
+		goto err;
+	}
+	priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)] = (wsm_seq + 1) & 7;
+
+	if (IS_DRIVER_TO_MCU_CMD(wsm_id))
+		confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label);
+
+	if (WSM_CONFIRM_CONDITION(wsm_id, confirm_label)) {
+		int rc = wsm_release_tx_buffer(priv, 1);
+		if (WARN_ON(rc < 0))
+			return;
+	}
+
+	bes2600_bh_wakeup(priv);
+
+	/* bes2600_wsm_rx takes care on SKB livetime */
+	//if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb)))
+	if ((wsm_handle_rx(priv, wsm_id, wsm, &skb)))
+		goto err;
+
+	if (skb) {
+		dev_kfree_skb(skb);
+		skb = NULL;
+	}
+	return;
+err:
+	if (skb) {
+		dev_kfree_skb(skb);
+		skb = NULL;
+	}
+
+	if(cnt++ >= 0)
+		priv->sbus_ops->pipe_send(priv->sbus_priv, BES2600_USB_PIPE_TX_WLAN, 200, data);
+	return;
+}
+
+void bes2600_tx_complete(struct bes2600_common *ar, struct sk_buff *skb)
+{
+	//bes2600_htc_tx_complete(ar, skb);
+}
+EXPORT_SYMBOL(bes2600_tx_complete);
+
+void bes2600_rx_complete(struct bes2600_common *ar, struct sk_buff *skb, u8 pipe)
+{
+	//bes2600_htc_rx_complete(ar, skb, pipe);
+	bes2600_usb_rx_complete(ar, skb);
+}
+EXPORT_SYMBOL(bes2600_rx_complete);
+
+
+static void bes2600_usb_io_comp_work(struct work_struct *work)
+{
+	struct bes2600_usb_pipe *pipe = container_of(work,
+						    struct bes2600_usb_pipe,
+						    io_complete_work);
+	struct sbus_priv *ar_usb;
+	struct sk_buff *skb;
+
+	ar_usb = pipe->ar_usb;
+
+	while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+		if (pipe->flags & BES2600_USB_PIPE_FLAG_TX) {
+			bes2600_dbg(BES2600_DBG_USB,
+				   "bes2600 usb xmit callback buf:0x%p\n", skb);
+			bes2600_tx_complete(ar_usb->ar, skb);
+		} else {
+			bes2600_dbg(BES2600_DBG_USB,
+				   "bes2600 usb recv callback buf:0x%p\n", skb);
+			bes2600_rx_complete(ar_usb->ar, skb,
+						pipe->logical_pipe_num);
+		}
+	}
+}
+
+#define BES2600_USB_MAX_DIAG_CMD (sizeof(struct bes2600_usb_ctrl_diag_cmd_write))
+#define BES2600_USB_MAX_DIAG_RESP (sizeof(struct bes2600_usb_ctrl_diag_resp_read))
+
+static int bes2600_usb_send(struct sbus_priv *self, u8 PipeID, u32 len, u8 *data)
+{
+	struct sbus_priv *device = self;
+	struct bes2600_usb_pipe *pipe = &device->pipes[PipeID];
+	struct bes2600_urb_context *urb_context;
+	int usb_status, status = 0;
+	struct urb *urb;
+	int send_cnt = atomic_read(&pipe->urb_pending_cnt);
+
+	bes2600_dbg(BES2600_DBG_USB, "+%s pipe : %d, buf:0x%p, send_cnt:%d.\n",
+		  	 __func__, PipeID, data, send_cnt);
+
+
+	urb_context = bes2600_usb_alloc_urb_from_pipe(pipe);
+
+	if (urb_context == NULL) {
+		/*
+		 * TODO: it is possible to run out of urbs if
+		 * 2 endpoints map to the same pipe ID
+		 */
+		bes2600_err(BES2600_DBG_USB, "%s pipe:%d no urbs left. URB Cnt : %d\n",
+			   	__func__, PipeID, pipe->urb_cnt);
+		status = -ENOMEM;
+		goto fail_hif_send;
+	}
+
+	urb_context->skb = (struct sk_buff *)data;
+	urb = urb_context->urb;
+	BUG_ON(!urb);
+	usb_fill_bulk_urb(urb,
+			  device->udev,
+			  pipe->usb_pipe_handle,
+			  data,
+			  len,
+			  bes2600_usb_transmit_complete, urb_context);
+
+	if ((len % pipe->max_packet_size) == 0) {
+		/* hit a max packet boundary on this pipe */
+		urb->transfer_flags |= URB_ZERO_PACKET;
+	}
+
+	bes2600_dbg(BES2600_DBG_USB,
+		   "athusb bulk send submit:%d, 0x%X (ep:0x%2.2X), %d bytes\n",
+		   pipe->logical_pipe_num, pipe->usb_pipe_handle,
+		   pipe->ep_address, len);
+
+	usb_anchor_urb(urb, &pipe->urb_submitted);
+	usb_status = usb_submit_urb(urb, GFP_ATOMIC);
+
+	atomic_add(1, &pipe->urb_pending_cnt);
+
+	if (usb_status) {
+		bes2600_err(BES2600_DBG_USB,
+			   "bes2600 usb : usb bulk transmit failed %d\n",
+			   usb_status);
+		usb_unanchor_urb(urb);
+		bes2600_usb_free_urb_to_pipe(urb_context->pipe,
+					    urb_context);
+		atomic_sub(1, &pipe->urb_pending_cnt);
+		status = -EINVAL;
+	}
+fail_hif_send:
+	return status;
+}
+
+static void * bes2600_usb_read(struct sbus_priv *self)
+{
+	return (void *)bes2600_recv_buf_get(self);
+}
+
+#if 0
+
+static void hif_stop(struct bes2600_common *ar)
+{
+	struct sbus_priv *device = bes2600_usb_priv(ar);
+
+	bes2600_usb_flush_all(device);
+}
+
+static void hif_detach_htc(struct bes2600_common *ar)
+{
+	struct sbus_priv *device = bes2600_usb_priv(ar);
+
+	bes2600_usb_flush_all(device);
+}
+
+/* exported hif usb APIs for htc pipe */
+static void hif_start(struct bes2600_common *ar)
+{
+	struct sbus_priv *device = bes2600_usb_priv(ar);
+	int i;
+
+	bes2600_usb_start_recv_pipes(device);
+
+	/* set the TX resource avail threshold for each TX pipe */
+	for (i = BES2600_USB_PIPE_TX_CTRL;
+	     i <= BES2600_USB_PIPE_BT_TX_DATA; i++) {
+		device->pipes[i].urb_cnt_thresh =
+		    device->pipes[i].urb_alloc / 2;
+	}
+}
+
+static int bes2600_usb_power_on(struct bes2600_common *ar)
+{
+	hif_start(ar);
+	return 0;
+}
+
+static int bes2600_usb_power_off(struct bes2600_common *ar)
+{
+	hif_detach_htc(ar);
+	return 0;
+}
+#endif
+static void bes2600_usb_destroy(struct sbus_priv *ar_usb)
+{
+	bes2600_usb_flush_all(ar_usb);
+
+	bes2600_usb_cleanup_pipe_resources(ar_usb);
+
+	usb_set_intfdata(ar_usb->interface, NULL);
+
+	kfree(ar_usb->diag_cmd_buffer);
+	kfree(ar_usb->diag_resp_buffer);
+
+	kfree(ar_usb);
+}
+
+static struct sbus_priv *bes2600_usb_create(struct usb_interface *interface)
+{
+	struct usb_device *dev = interface_to_usbdev(interface);
+	struct sbus_priv *ar_usb;
+	struct bes2600_usb_pipe *pipe;
+	int status = 0;
+	int i;
+
+	ar_usb = kzalloc(sizeof(struct sbus_priv), GFP_KERNEL);
+	if (ar_usb == NULL)
+		goto fail_bes2600_usb_create;
+
+	usb_set_intfdata(interface, ar_usb);
+	spin_lock_init(&(ar_usb->cs_lock));
+	ar_usb->udev = dev;
+	ar_usb->interface = interface;
+	ar_usb->ar = NULL;
+
+	for (i = 0; i < BES2600_USB_PIPE_MAX; i++) {
+		if((i != BES2600_USB_PIPE_RX_BT) &&
+		   (i != BES2600_USB_PIPE_TX_BT)){
+			pipe = &ar_usb->pipes[i];
+			INIT_WORK(&pipe->io_complete_work,
+				  bes2600_usb_io_comp_work);
+			skb_queue_head_init(&pipe->io_comp_queue);
+		}
+	}
+
+	spin_lock_init(&ar_usb->rx_queue_lock);
+	skb_queue_head_init(&ar_usb->rx_queue);
+
+	spin_lock_init(&ar_usb->status_lock);
+	ar_usb->usb_irq_handler = NULL;
+	ar_usb->irq_data = NULL;
+	ar_usb->int_control_reg = 0;
+	ar_usb->int_status_reg = 0;
+
+	ar_usb->diag_cmd_buffer = kzalloc(BES2600_USB_MAX_DIAG_CMD, GFP_KERNEL);
+	if (ar_usb->diag_cmd_buffer == NULL) {
+		status = -ENOMEM;
+		goto fail_bes2600_usb_create;
+	}
+
+	ar_usb->diag_resp_buffer = kzalloc(BES2600_USB_MAX_DIAG_RESP,
+					   GFP_KERNEL);
+	if (ar_usb->diag_resp_buffer == NULL) {
+		status = -ENOMEM;
+		goto fail_bes2600_usb_create;
+	}
+
+	status = bes2600_usb_setup_pipe_resources(ar_usb);
+
+fail_bes2600_usb_create:
+	if (status != 0) {
+		bes2600_usb_destroy(ar_usb);
+		ar_usb = NULL;
+	}
+	return ar_usb;
+}
+void bes2600_core_release(struct bes2600_common *self);
+
+static void bes2600_usb_device_detached(struct usb_interface *interface)
+{
+	struct sbus_priv *self = usb_get_intfdata(interface);
+	if (self) {
+		if (self->ar) {
+			bes2600_core_release(self->ar);
+			self->ar = NULL;
+		}
+		bes2600_usb_destroy(self);
+	}
+}
+
+#if 0
+static void bes2600_usb_get_default_pipe(struct bes2600_common *ar,
+					u8 *ul_pipe, u8 *dl_pipe)
+{
+	*ul_pipe = BES2600_USB_PIPE_TX_CTRL;
+	*dl_pipe = BES2600_USB_PIPE_RX_CTRL;
+}
+
+static int bes2600_usb_map_service_pipe(struct bes2600_common *ar, u16 svc_id,
+				       u8 *ul_pipe, u8 *dl_pipe)
+{
+	int status = 0;
+
+	switch (svc_id) {
+	case HTC_CTRL_RSVD_SVC:
+	case WMI_CONTROL_SVC:
+		*ul_pipe = BES2600_USB_PIPE_TX_CTRL;
+		/* due to large control packets, shift to data pipe */
+		*dl_pipe = BES2600_USB_PIPE_RX_DATA;
+		break;
+	case WMI_DATA_BE_SVC:
+	case WMI_DATA_BK_SVC:
+		*ul_pipe = BES2600_USB_PIPE_TX_DATA_LP;
+		/*
+		* Disable rxdata2 directly, it will be enabled
+		* if FW enable rxdata2
+		*/
+		*dl_pipe = BES2600_USB_PIPE_RX_DATA;
+		break;
+	case WMI_DATA_VI_SVC:
+
+		if (test_bit(BES2600_FW_CAPABILITY_MAP_LP_ENDPOINT,
+			     ar->fw_capabilities))
+			*ul_pipe = BES2600_USB_PIPE_TX_DATA_LP;
+		else
+			*ul_pipe = BES2600_USB_PIPE_TX_DATA_MP;
+		/*
+		* Disable rxdata2 directly, it will be enabled
+		* if FW enable rxdata2
+		*/
+		*dl_pipe = BES2600_USB_PIPE_RX_DATA;
+		break;
+	case WMI_DATA_VO_SVC:
+
+		if (test_bit(BES2600_FW_CAPABILITY_MAP_LP_ENDPOINT,
+			     ar->fw_capabilities))
+			*ul_pipe = BES2600_USB_PIPE_TX_DATA_LP;
+		else
+			*ul_pipe = BES2600_USB_PIPE_TX_DATA_MP;
+		/*
+		* Disable rxdata2 directly, it will be enabled
+		* if FW enable rxdata2
+		*/
+		*dl_pipe = BES2600_USB_PIPE_RX_DATA;
+		break;
+	default:
+		status = -EPERM;
+		break;
+	}
+
+	return status;
+}
+
+static u16 bes2600_usb_get_free_queue_number(struct bes2600_common *ar, u8 pipe_id)
+{
+	struct sbus_priv *device = bes2600_usb_priv(ar);
+
+	return device->pipes[pipe_id].urb_cnt;
+}
+
+static int bes2600_usb_submit_ctrl_out(struct sbus_priv *ar_usb,
+				   u8 req, u16 value, u16 index, void *data,
+				   u32 size)
+{
+	u8 *buf = NULL;
+	int ret;
+
+	if (size > 0) {
+		buf = kmemdup(data, size, GFP_KERNEL);
+		if (buf == NULL)
+			return -ENOMEM;
+	}
+
+	/* note: if successful returns number of bytes transfered */
+	ret = usb_control_msg(ar_usb->udev,
+			      usb_sndctrlpipe(ar_usb->udev, 0),
+			      req,
+			      USB_DIR_OUT | USB_TYPE_VENDOR |
+			      USB_RECIP_DEVICE, value, index, buf,
+			      size, 1000);
+
+	if (ret < 0) {
+		bes2600_warn("Failed to submit usb control message: %d\n", ret);
+		kfree(buf);
+		return ret;
+	}
+
+	kfree(buf);
+
+	return 0;
+}
+
+static int bes2600_usb_submit_ctrl_in(struct sbus_priv *ar_usb,
+				  u8 req, u16 value, u16 index, void *data,
+				  u32 size)
+{
+	u8 *buf = NULL;
+	int ret;
+
+	if (size > 0) {
+		buf = kmalloc(size, GFP_KERNEL);
+		if (buf == NULL)
+			return -ENOMEM;
+	}
+
+	/* note: if successful returns number of bytes transfered */
+	ret = usb_control_msg(ar_usb->udev,
+				 usb_rcvctrlpipe(ar_usb->udev, 0),
+				 req,
+				 USB_DIR_IN | USB_TYPE_VENDOR |
+				 USB_RECIP_DEVICE, value, index, buf,
+				 size, 2 * HZ);
+
+	if (ret < 0) {
+		bes2600_warn("Failed to read usb control message: %d\n", ret);
+		kfree(buf);
+		return ret;
+	}
+
+	memcpy((u8 *) data, buf, size);
+
+	kfree(buf);
+
+	return 0;
+}
+
+static int bes2600_usb_ctrl_msg_exchange(struct sbus_priv *ar_usb,
+				     u8 req_val, u8 *req_buf, u32 req_len,
+				     u8 resp_val, u8 *resp_buf, u32 *resp_len)
+{
+	int ret;
+
+	/* send command */
+	ret = bes2600_usb_submit_ctrl_out(ar_usb, req_val, 0, 0,
+					 req_buf, req_len);
+
+	if (ret != 0)
+		return ret;
+
+	if (resp_buf == NULL) {
+		/* no expected response */
+		return ret;
+	}
+
+	/* get response */
+	ret = bes2600_usb_submit_ctrl_in(ar_usb, resp_val, 0, 0,
+					resp_buf, *resp_len);
+
+	return ret;
+}
+
+static int bes2600_usb_diag_read32(struct bes2600_common *ar, u32 address, u32 *data)
+{
+	struct sbus_priv *ar_usb = ar->sbus_priv;
+	struct bes2600_usb_ctrl_diag_resp_read *resp;
+	struct bes2600_usb_ctrl_diag_cmd_read *cmd;
+	u32 resp_len;
+	int ret;
+
+	cmd = (struct bes2600_usb_ctrl_diag_cmd_read *) ar_usb->diag_cmd_buffer;
+
+	memset(cmd, 0, sizeof(*cmd));
+	cmd->cmd = BES2600_USB_CTRL_DIAG_CC_READ;
+	cmd->address = cpu_to_le32(address);
+	resp_len = sizeof(*resp);
+
+	ret = bes2600_usb_ctrl_msg_exchange(ar_usb,
+				BES2600_USB_CONTROL_REQ_DIAG_CMD,
+				(u8 *) cmd,
+				sizeof(struct bes2600_usb_ctrl_diag_cmd_write),
+				BES2600_USB_CONTROL_REQ_DIAG_RESP,
+				ar_usb->diag_resp_buffer, &resp_len);
+
+	if (ret) {
+		bes2600_warn("diag read32 failed: %d\n", ret);
+		return ret;
+	}
+
+	resp = (struct bes2600_usb_ctrl_diag_resp_read *)
+		ar_usb->diag_resp_buffer;
+
+	*data = le32_to_cpu(resp->value);
+
+	return ret;
+}
+
+static int bes2600_usb_diag_write32(struct bes2600_common *ar, u32 address, __le32 data)
+{
+	struct sbus_priv *ar_usb = ar->sbus_priv;
+	struct bes2600_usb_ctrl_diag_cmd_write *cmd;
+	int ret;
+
+	cmd = (struct bes2600_usb_ctrl_diag_cmd_write *) ar_usb->diag_cmd_buffer;
+
+	memset(cmd, 0, sizeof(struct bes2600_usb_ctrl_diag_cmd_write));
+	cmd->cmd = cpu_to_le32(BES2600_USB_CTRL_DIAG_CC_WRITE);
+	cmd->address = cpu_to_le32(address);
+	cmd->value = data;
+
+	ret = bes2600_usb_ctrl_msg_exchange(ar_usb,
+					   BES2600_USB_CONTROL_REQ_DIAG_CMD,
+					   (u8 *) cmd,
+					   sizeof(*cmd),
+					   0, NULL, NULL);
+	if (ret) {
+		bes2600_warn("diag_write32 failed: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int bes2600_usb_bmi_read(struct bes2600_common *ar, u8 *buf, u32 len)
+{
+	struct sbus_priv *ar_usb = ar->sbus_priv;
+	int ret;
+
+	/* get response */
+	ret = bes2600_usb_submit_ctrl_in(ar_usb,
+					BES2600_USB_CONTROL_REQ_RECV_BMI_RESP,
+					0, 0, buf, len);
+	if (ret) {
+		bes2600_err("Unable to read the bmi data from the device: %d\n",
+			   ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int bes2600_usb_bmi_write(struct bes2600_common *ar, u8 *buf, u32 len)
+{
+	struct sbus_priv *ar_usb = ar->sbus_priv;
+	int ret;
+
+	/* send command */
+	ret = bes2600_usb_submit_ctrl_out(ar_usb,
+					 BES2600_USB_CONTROL_REQ_SEND_BMI_CMD,
+					 0, 0, buf, len);
+	if (ret) {
+		bes2600_err("unable to send the bmi data to the device: %d\n",
+			   ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void bes2600_usb_stop(struct bes2600_common *ar)
+{
+	hif_stop(ar);
+}
+
+static void bes2600_usb_cleanup_scatter(struct bes2600_common *ar)
+{
+	/*
+	 * USB doesn't support it. Just return.
+	 */
+	return;
+}
+
+static int bes2600_usb_suspend(struct bes2600_common *ar, struct cfg80211_wowlan *wow)
+{
+	/*
+	 * cfg80211 suspend/WOW currently not supported for USB.
+	 */
+	return 0;
+}
+
+static int bes2600_usb_resume(struct bes2600_common *ar)
+{
+	/*
+	 * cfg80211 resume currently not supported for USB.
+	 */
+	return 0;
+}
+#endif
+
+static int bes2600_usb_init(struct sbus_priv *self, struct bes2600_common *ar)
+{
+	int queue_empty = -1;
+
+	self->ar = ar;
+	spin_lock_bh(&self->status_lock);
+	self->int_status_reg = 0;
+	self->int_control_reg = 0;
+	spin_unlock_bh(&self->status_lock);
+
+	spin_lock_bh(&self->rx_queue_lock);
+	queue_empty = skb_queue_empty(&self->rx_queue);
+	spin_unlock_bh(&self->rx_queue_lock);
+
+	/* revoke rx work if firmware ready arrived before bes2600 probe done */
+	if(!queue_empty) {
+		bes2600_irq_handler(ar);
+	}
+
+	return 0;
+}
+
+static int bes2600_usb_memcpy_fromio(struct sbus_priv *self,
+				     unsigned int addr,
+				     void *dst, int count)
+{
+	return 0;
+}
+
+static int bes2600_usb_memcpy_toio(struct sbus_priv *self,
+				   unsigned int addr,
+				   const void *src, int count)
+{
+	return 0;
+}
+
+static void bes2600_usb_lock(struct sbus_priv *self)
+{
+}
+
+static void bes2600_usb_unlock(struct sbus_priv *self)
+{
+}
+
+static size_t bes2600_usb_align_size(struct sbus_priv *self, size_t size)
+{
+	return size;
+}
+
+int bes2600_usb_irq_subscribe(struct sbus_priv *self, sbus_irq_handler handler, void *priv)
+{
+	spin_lock_bh(&self->status_lock);
+	self->usb_irq_handler = handler;
+	self->irq_data = priv;
+	spin_unlock_bh(&self->status_lock);
+	return 0;
+}
+
+int bes2600_usb_irq_unsubscribe(struct sbus_priv *self)
+{
+	spin_lock_bh(&self->status_lock);
+	self->usb_irq_handler = NULL;
+	self->irq_data = NULL;
+	spin_unlock_bh(&self->status_lock);
+	return 0;
+}
+
+int bes2600_usb_reset(struct sbus_priv *self)
+{
+	self->ar = NULL;
+	spin_lock_bh(&self->status_lock);
+	self->int_status_reg = 0;
+	self->int_control_reg = 0;
+	spin_unlock_bh(&self->status_lock);
+	return 0;
+}
+
+int bes2600_usb_set_block_size(struct sbus_priv *self, size_t size)
+{
+	return 0;
+}
+
+static int bes2600_usb_reg_read(struct sbus_priv *self, u32 reg, void *dst, int count)
+{
+	int ret = 0;
+	unsigned long flags;
+	if(reg == BES_USB_CONTROL_REG) {
+		spin_lock_irqsave(&self->status_lock,flags);
+		*((u32 *)dst) = self->int_control_reg;
+		spin_unlock_irqrestore(&self->status_lock,flags);
+	}
+	else if(reg == BES_USB_STATUS_REG) {
+		spin_lock_irqsave(&self->status_lock,flags);
+		*((u32 *)dst) = self->int_status_reg;
+		spin_unlock_irqrestore(&self->status_lock,flags);
+	}
+	else
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static int bes2600_usb_reg_write(struct sbus_priv *self, u32 reg, const void *src, int count)
+{
+	int ret = 0;
+	unsigned long flags;
+	if(reg == BES_USB_CONTROL_REG) {
+		spin_lock_irqsave(&self->status_lock,flags);
+		self->int_control_reg = *((u32 *)src);
+		spin_unlock_irqrestore(&self->status_lock,flags);
+	}
+	else if(reg == BES_USB_STATUS_REG) {
+		spin_lock_irqsave(&self->status_lock,flags);
+		self->int_status_reg = *((u32 *)src);
+		spin_unlock_irqrestore(&self->status_lock,flags);
+	}
+	else
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static struct sbus_ops bes2600_usb_ops = {
+	#if 0
+	.diag_read32 = bes2600_usb_diag_read32,
+	.diag_write32 = bes2600_usb_diag_write32,
+	.bmi_read = bes2600_usb_bmi_read,
+	.bmi_write = bes2600_usb_bmi_write,
+	.power_on = bes2600_usb_power_on,
+	.power_off = bes2600_usb_power_off,
+	.stop = bes2600_usb_stop,
+
+	.pipe_get_default = bes2600_usb_get_default_pipe,
+	.pipe_map_service = bes2600_usb_map_service_pipe,
+	.pipe_get_free_queue_number = bes2600_usb_get_free_queue_number,
+	.cleanup_scatter = bes2600_usb_cleanup_scatter,
+	.suspend = bes2600_usb_suspend,
+	.resume = bes2600_usb_resume,
+	#endif
+	.init				= bes2600_usb_init,
+	.sbus_memcpy_fromio	= bes2600_usb_memcpy_fromio,
+	.sbus_memcpy_toio	= bes2600_usb_memcpy_toio,
+	.lock			= bes2600_usb_lock,
+	.unlock			= bes2600_usb_unlock,
+	.irq_subscribe		= bes2600_usb_irq_subscribe,
+	.irq_unsubscribe	= bes2600_usb_irq_unsubscribe,
+	.reset			= bes2600_usb_reset,
+	.align_size		= bes2600_usb_align_size,
+	.set_block_size	= bes2600_usb_set_block_size,
+	.pipe_send = bes2600_usb_send,
+	.pipe_read = bes2600_usb_read,
+	.sbus_reg_read = bes2600_usb_reg_read,
+	.sbus_reg_write = bes2600_usb_reg_write,
+};
+
+
+/* bes2600 usb driver registered functions */
+static int bes2600_usb_probe(struct usb_interface *interface,
+			    const struct usb_device_id *id)
+{
+	struct usb_device *dev = NULL;
+	struct bes2600_common *ar;
+	struct sbus_priv *self = NULL;
+	int vendor_id, product_id;
+	int ret = 0;
+
+	bes2600_chrdev_update_signal_mode();
+
+	dev = interface_to_usbdev(interface);
+	usb_get_dev(dev);
+
+	vendor_id = le16_to_cpu(dev->descriptor.idVendor);
+	product_id = le16_to_cpu(dev->descriptor.idProduct);
+	bes2600_dbg(BES2600_DBG_USB, "vendor_id = %04x\n", vendor_id);
+	bes2600_dbg(BES2600_DBG_USB, "product_id = %04x\n", product_id);
+
+	if (interface->cur_altsetting)
+		bes2600_dbg(BES2600_DBG_USB, "USB Interface %d\n",
+			   interface->cur_altsetting->desc.bInterfaceNumber);
+
+
+	if (dev->speed == USB_SPEED_HIGH)
+		bes2600_dbg(BES2600_DBG_USB, "USB 2.0 Host\n");
+	else
+		bes2600_dbg(BES2600_DBG_USB, "USB 1.1 Host\n");
+
+	self = bes2600_usb_create(interface);
+
+	if (self == NULL) {
+		ret = -ENOMEM;
+		bes2600_dbg(BES2600_DBG_USB, "USB create failed!\n");
+		goto err_usb_put;
+	}
+
+	bes2600_usb_start_recv_pipes(self);
+
+	bes2600_chrdev_update_signal_mode();
+	bes2600_dbg(BES2600_DBG_USB, "%s type:%d sig_mode:%d\n", __func__,
+			bes2600_chrdev_get_fw_type(), bes2600_chrdev_is_signal_mode());
+
+	//bes2600_reg_set_object(&bes2600_usb_ops, self);
+	if ((ret = bes2600_load_firmware(&bes2600_usb_ops, self)) < 0) {
+		bes2600_err(BES2600_DBG_USB, "bes2600_load_firmware failed(%d)\n", ret);
+		goto err_core_free;
+	}
+
+	ret = bes2600_core_probe(&bes2600_usb_ops, (struct sbus_priv*)self, &self->udev->dev, &ar);
+	if (ret) {
+		bes2600_err(BES2600_DBG_USB, "Failed to init bes2600 core: %d\n", ret);
+		goto err_core_free;
+	}
+
+	return ret;
+
+err_core_free:
+#ifdef CONFIG_BES2600_BT
+	bes2600_btusb_uninit(interface);
+#endif
+	bes2600_usb_destroy(self);
+err_usb_put:
+	usb_put_dev(dev);
+
+	return ret;
+}
+
+int bes2600_register_net_dev(struct sbus_priv *bus_priv)
+{
+	int status = 0;
+	BUG_ON(!bus_priv);
+	status = bes2600_core_probe(&bes2600_usb_ops,
+			      bus_priv, &bus_priv->udev->dev, &bus_priv->ar);
+	return status;
+}
+
+int bes2600_unregister_net_dev(struct sbus_priv *bus_priv)
+{
+	BUG_ON(!bus_priv);
+	if (bus_priv->ar) {
+		bes2600_core_release(bus_priv->ar);
+		bus_priv->ar = NULL;
+	}
+	return 0;
+}
+
+bool bes2600_is_net_dev_created(struct sbus_priv *bus_priv)
+{
+	BUG_ON(!bus_priv);
+	return (bus_priv->ar != NULL);
+}
+
+static void bes2600_usb_remove(struct usb_interface *interface)
+{
+#ifdef CONFIG_BES2600_BT
+	bes2600_btusb_uninit(interface);
+#endif
+	usb_put_dev(interface_to_usbdev(interface));
+	bes2600_usb_device_detached(interface);
+}
+
+#ifdef CONFIG_PM
+static int bes2600_usb_pm_suspend(struct usb_interface *interface,
+			      pm_message_t message)
+{
+	struct sbus_priv *device;
+	device = usb_get_intfdata(interface);
+
+	bes2600_usb_flush_all(device);
+	return 0;
+}
+static int bes2600_usb_pm_resume(struct usb_interface *interface)
+{
+	struct sbus_priv *device;
+	device = usb_get_intfdata(interface);
+
+	bes2600_usb_post_recv_transfers(&device->pipes[BES2600_USB_PIPE_RX_WLAN],
+				       BES2600_USB_RX_BUFFER_SIZE);
+
+	return 0;
+}
+#else
+#define bes2600_usb_pm_suspend NULL
+#define bes2600_usb_pm_resume NULL
+#endif
+
+/* table of devices that work with this driver */
+static const struct usb_device_id bes2600_usb_ids[] = {
+	{USB_DEVICE(0xBE57, 0x0104)},
+	{USB_DEVICE(0xBE57, 0x2002)},
+	{USB_DEVICE(0xBE57, 0x2003)},
+	{ /* Terminating entry */ },
+};
+
+MODULE_DEVICE_TABLE(usb, bes2600_usb_ids);
+
+
+
+static struct usb_driver bes2600_usb_driver = {
+	.name = "bes2600_usb",
+	.probe = bes2600_usb_probe,
+	.suspend = bes2600_usb_pm_suspend,
+	.resume = bes2600_usb_pm_resume,
+	.disconnect = bes2600_usb_remove,
+	.id_table = bes2600_usb_ids,
+	.supports_autosuspend = true,
+	.disable_hub_initiated_lpm = 1,
+};
+
+static int __init bes2600_usb_module_init(void)
+{
+	bes2600_info(BES2600_DBG_USB, "------Driver: bes2600.ko version :%s\n", BES2600_DRV_VERSION);
+	return usb_register_driver(&bes2600_usb_driver , THIS_MODULE, "BES2600");
+}
+
+static void __exit bes2600_usb_module_exit(void)
+{
+	usb_deregister(&bes2600_usb_driver);
+}
+
+module_init(bes2600_usb_module_init);
+module_exit(bes2600_usb_module_exit);
+
+MODULE_AUTHOR("Bestechnic, Inc.");
+MODULE_DESCRIPTION("Driver support for BEST2000 wireless USB devices");
+MODULE_LICENSE("Dual BSD/GPL");
+
+
+
+
diff --git a/drivers/staging/bes2600/bes_chardev.c b/drivers/staging/bes2600/bes_chardev.c
new file mode 100644
index 000000000000..77573e59952e
--- /dev/null
+++ b/drivers/staging/bes2600/bes_chardev.c
@@ -0,0 +1,1280 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include<linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/atomic.h>
+#include <linux/wait.h>
+#include <linux/crc32.h>
+#include <linux/version.h>
+#include "bes2600.h"
+#include "sbus.h"
+#include "hwio.h"
+#include "fwio.h"
+#include "bes_chardev.h"
+#include "tx_loop.h"
+
+enum wait_state {
+	BES2600_BOOT_WAIT_NONE = 0,
+	BES2600_BOOT_WAIT_PROBE_DONE,
+	BES2600_BOOT_WAIT_CLOSE,
+};
+
+enum bus_probe_state {
+	BES2600_BUS_PROBE_NONE = 0,
+	BES2600_BUS_PROBE_START,
+	BES2600_BUS_PROBE_OK,
+	BES2600_BUS_PROBE_TIMEOUT,
+};
+
+struct bes_cdev
+{
+	struct cdev cdev;
+	dev_t dev_id;
+	int major;
+	int minor;
+	struct class *class;
+	struct device *device;
+	atomic_t num_proc;
+	wait_queue_head_t open_wq;
+	spinlock_t status_lock;
+	bool wifi_opened;
+	bool bt_opened;
+	bool bton_pending;
+	bool dpd_calied;
+	u8 *dpd_data;
+	u32 dpd_len;
+	enum wait_state wait_state;
+	wait_queue_head_t probe_done_wq;
+	const struct sbus_ops *sbus_ops;
+	struct sbus_priv *sbus_priv;
+	bool sig_mode;
+	int fw_type;
+	bool bus_error;
+	bool halt_dev;
+	struct delayed_work probe_timeout_work;
+	enum bus_probe_state bus_probe;
+	struct work_struct wifi_force_close_work;
+#ifdef BES2600_WRITE_DPD_TO_FILE
+	int no_dpd;
+#endif
+	enum pend_read_op read_flag;
+	u32 wakeup_state; /* for userspace check wakeup reason */
+#ifdef BES2600_DUMP_FW_DPD_LOG
+	u8 *dpd_log;
+	u16 dpd_log_len;
+#endif
+};
+
+struct bes2600_op_map
+{
+	char op[20];	// operation
+	int op_len;	// operation length, used for effiency
+	int (*handler) (const char *cmd); // handler
+};
+
+static struct bes_cdev bes2600_cdev;
+module_param_named(fw_type, bes2600_cdev.fw_type, int, 0644);
+#ifdef BES2600_WRITE_DPD_TO_FILE
+module_param_named(no_dpd, bes2600_cdev.no_dpd, int, 0644);
+#endif
+
+extern int bes2600_register_net_dev(struct sbus_priv *bus_priv);
+extern int bes2600_unregister_net_dev(struct sbus_priv *bus_priv);
+extern bool bes2600_is_net_dev_created(struct sbus_priv *bus_priv);
+
+static bool bes2600_bootup_end(void)
+{
+	bool end;
+
+	spin_lock(&bes2600_cdev.status_lock);
+	end = (bes2600_cdev.bus_probe == BES2600_BUS_PROBE_TIMEOUT ||
+	       bes2600_cdev.sbus_priv != NULL ||
+	       bes2600_cdev.bus_error);
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	return end;
+}
+
+static int bes2600_chrdev_switch_subsys(int wake_flag, int subsys, bool active)
+{
+	int ret = 0;
+
+	if(bes2600_cdev.sbus_priv == NULL)
+		return -EFAULT;
+
+	if(active) {
+		if(bes2600_cdev.sbus_ops->gpio_wake)
+			bes2600_cdev.sbus_ops->gpio_wake(bes2600_cdev.sbus_priv, wake_flag);
+
+		if(bes2600_cdev.sbus_ops->sbus_active)
+			ret = bes2600_cdev.sbus_ops->sbus_active(bes2600_cdev.sbus_priv, subsys);
+
+		if(bes2600_cdev.sbus_ops->gpio_sleep)
+			bes2600_cdev.sbus_ops->gpio_sleep(bes2600_cdev.sbus_priv, wake_flag);
+	} else {
+		if(bes2600_cdev.sbus_ops->gpio_wake)
+			bes2600_cdev.sbus_ops->gpio_wake(bes2600_cdev.sbus_priv, wake_flag);
+
+		if(bes2600_cdev.sbus_ops->sbus_deactive)
+			ret = bes2600_cdev.sbus_ops->sbus_deactive(bes2600_cdev.sbus_priv, subsys);
+
+		if(bes2600_cdev.sbus_ops->gpio_sleep)
+			bes2600_cdev.sbus_ops->gpio_sleep(bes2600_cdev.sbus_priv, wake_flag);
+	}
+
+	return ret;
+}
+
+static int bes2600_switch_wifi(bool on)
+{
+	int ret = 0;
+	long status = 0;
+
+	if(bes2600_cdev.wifi_opened == on)
+		return 0;
+
+	if(on) {
+		if(bes2600_chrdev_check_system_close()) {
+			bes2600_info(BES2600_DBG_CHARDEV, "power up bes2600 when active wifi.\n");
+			/* reset bus error status when restart bes2600 */
+			spin_lock(&bes2600_cdev.status_lock);
+			bes2600_cdev.bus_error = false;
+			bes2600_cdev.halt_dev = false;
+			bes2600_cdev.bus_probe = BES2600_BUS_PROBE_NONE;
+			spin_unlock(&bes2600_cdev.status_lock);
+
+			/* power up bes2600, trigger system to execute probe function */
+			bes2600_cdev.wifi_opened = true;
+			bes2600_cdev.sbus_ops->power_switch(NULL, 1);
+
+			/* wait probe done event */
+			status = wait_event_timeout(bes2600_cdev.probe_done_wq, 
+					bes2600_bootup_end(), HZ * 8);
+			WARN_ON(status <= 0);
+			ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
+		} else {
+			/* bes2600 is already powered up, we just need to create net device */
+			if(bes2600_cdev.sbus_priv) {
+				if(!bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
+					ret = bes2600_register_net_dev(bes2600_cdev.sbus_priv);
+				}
+			} else {
+				ret = -EFAULT;
+			}
+		}
+	} else {
+		if(bes2600_cdev.sbus_priv && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
+			bes2600_unregister_net_dev(bes2600_cdev.sbus_priv);
+		}
+	}
+
+	if(!ret) {
+		bes2600_cdev.wifi_opened = on;
+	} else {
+		bes2600_cdev.wifi_opened = false;
+		bes2600_info_with_cond(on, BES2600_DBG_CHARDEV, "open wifi failed\n");
+	}
+
+	return ret;
+}
+
+static int bes2600_switch_bt(bool on)
+{
+	int ret = 0;
+	long status = 0;
+
+	if(bes2600_cdev.bt_opened == on)
+		return 0;
+
+	if(on) {
+		if(bes2600_chrdev_check_system_close()) {
+			bes2600_info(BES2600_DBG_CHARDEV, "power up bes2600 when active bt.\n");
+			/* reset bus error status when restart bes2600 */
+			spin_lock(&bes2600_cdev.status_lock);
+			bes2600_cdev.bus_error = false;
+			bes2600_cdev.halt_dev = false;
+			bes2600_cdev.bus_probe = BES2600_BUS_PROBE_NONE;
+			spin_unlock(&bes2600_cdev.status_lock);
+
+			/* set opend state in advance */
+			bes2600_cdev.bt_opened = true;
+			bes2600_cdev.bton_pending = true;
+
+			/* power up bes2600, trigger system to execute probe function */
+			bes2600_cdev.sbus_ops->power_switch(NULL, 1);
+
+			/* wait bootup process end */
+			status = wait_event_timeout(bes2600_cdev.probe_done_wq,
+				bes2600_bootup_end(), HZ * 8);
+			WARN_ON(status <= 0);
+
+			/* check if there is a error when bootup */
+			ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
+		} else {
+			bes2600_info(BES2600_DBG_CHARDEV, "bes2600 activate bt.\n");
+			ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_ON, SUBSYSTEM_BT, true);
+		}
+	} else {
+		bes2600_info(BES2600_DBG_CHARDEV, "bes2600 deactivate bt.\n");
+		bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_OFF, SUBSYSTEM_BT, false);
+	}
+
+	if(!ret) {
+		bes2600_cdev.bt_opened = on;
+	} else {
+		bes2600_cdev.bt_opened = false;
+		bes2600_cdev.bton_pending = false;
+		bes2600_info_with_cond(ret, BES2600_DBG_CHARDEV, "open bt failed\n");
+	}
+
+	return ret;
+}
+
+static int bes2600_get_cmd_and_ifname(const char *str, char **result)
+{
+	int cmd_len = 0;
+	int ifname_len = 0;
+	char *sp = NULL;
+	char *tmp_ptr = NULL;
+	char *cmd_ptr = NULL;
+
+	/* check if input arguments is valid */
+	if(!str || strncmp(str, "ifname:", 7) != 0)
+		return -1;
+
+	sp = strchr(str, ' ');
+	if(strncmp(sp + 1, "cmd:", 4) != 0)
+		return -1;
+
+	/* extract interface name */
+	ifname_len = sp - str - 7;
+	tmp_ptr = kmalloc(ifname_len + 1, GFP_KERNEL);
+	if(!tmp_ptr) {
+		return -2;
+	}
+
+	strncpy(tmp_ptr, str+7, ifname_len);
+	tmp_ptr[ifname_len] = '\0';
+	result[0] = tmp_ptr;
+
+	/* get command length */
+	cmd_ptr = strstr(str, "cmd:");
+	cmd_ptr += 4;
+	sp = strchr(cmd_ptr, ' ');
+	if(!sp) {	/* the command don't have any parameter */
+		cmd_len = strlen(cmd_ptr);
+		if(cmd_ptr[cmd_len - 1] == '\n')
+			--cmd_len;
+	} else {	/* the command have one or more parameter */
+		cmd_len = sp - cmd_ptr;
+	}
+
+	/* copy command to out buffer */
+	tmp_ptr = kmalloc( cmd_len + 1, GFP_KERNEL);
+	if(!tmp_ptr) {
+		kfree(result[0]);
+		result[0] = NULL;
+		return -3;
+	}
+
+	strncpy(tmp_ptr, cmd_ptr, cmd_len);
+	tmp_ptr[cmd_len] = '\0';
+	result[1] = tmp_ptr;
+
+	return 0;
+}
+
+static void bes2600_recyle_cmd_and_ifname_mem(char **info)
+{
+	if(info[0]) {
+		kfree(info[0]);
+		info[0] = NULL;
+	}
+
+	if(info[1]) {
+		kfree(info[1]);
+		info[1] = NULL;
+	}
+
+}
+
+static int bes2600_op_default_handler(const char *str)
+{
+	char *info[2] = {0};
+
+	if(bes2600_get_cmd_and_ifname(str, info) == 0) {
+		bes2600_info(BES2600_DBG_CHARDEV, "cmd(%s) on %s not handled\n", info[1], info[0]);
+	} else {
+		bes2600_err(BES2600_DBG_CHARDEV, "%s get command fail, the origin string is %s\n", __func__, str);
+	}
+
+	bes2600_recyle_cmd_and_ifname_mem(info);
+
+	return 0;
+}
+
+static int bes2600_op_wifi_bt_on_off(const char *str)
+{
+	char *info[2] = {0};
+	int ret = 0;
+	enum wait_state wait_state;
+	enum bus_probe_state probe_state;
+	unsigned long status = 0;
+
+	spin_lock(&bes2600_cdev.status_lock);
+	probe_state = bes2600_cdev.bus_probe;
+	wait_state = bes2600_cdev.wait_state;
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	/* only work for wifi signal mode */
+	if(bes2600_cdev.fw_type != BES2600_FW_TYPE_WIFI_SIGNAL)
+		return -EFAULT;
+
+	/* wait bus probe operation end */
+	if(probe_state == BES2600_BUS_PROBE_START) {
+		bes2600_info(BES2600_DBG_CHARDEV, "wait bus probe operation end\n");
+		status = wait_event_timeout(bes2600_cdev.probe_done_wq,
+					(bes2600_cdev.bus_probe > BES2600_BUS_PROBE_START),
+					HZ);
+		WARN_ON(status <= 0);
+	}
+
+	/* must wait previous operation end in critical section */
+	if(wait_state != BES2600_BOOT_WAIT_NONE) {
+		bes2600_info(BES2600_DBG_CHARDEV, "wait previous operation end\n");
+		status = wait_event_timeout(bes2600_cdev.probe_done_wq,
+					(bes2600_cdev.wait_state == BES2600_BOOT_WAIT_NONE),
+					HZ * 8);
+		WARN_ON(status <= 0);
+	}
+
+	/* if dpd calibration is doing, modify wifi and bt state directly */
+	spin_lock(&bes2600_cdev.status_lock);
+	if(bes2600_cdev.bus_probe == BES2600_BUS_PROBE_OK && !bes2600_cdev.dpd_calied) {
+		if(bes2600_get_cmd_and_ifname(str, info) == 0) {
+			if(strncmp(info[1], "WIFI_ON", 7) == 0) {
+				bes2600_cdev.wifi_opened = true;
+			} else if(strncmp(info[1], "WIFI_OFF", 8) == 0) {
+				bes2600_cdev.wifi_opened = false;
+			} else if(strncmp(info[1], "BT_ON", 5) == 0) {
+				bes2600_cdev.bt_opened = true;
+				bes2600_cdev.bton_pending = true;
+			} else if(strncmp(info[1], "BT_OFF", 6) == 0) {
+				bes2600_cdev.bt_opened = false;
+				bes2600_cdev.bton_pending = false;
+			}
+		}
+		bes2600_recyle_cmd_and_ifname_mem(info);
+		spin_unlock(&bes2600_cdev.status_lock);
+
+		/* wait probe done event */
+		status = wait_event_timeout(bes2600_cdev.probe_done_wq,
+				bes2600_bootup_end(), HZ * 8);
+		WARN_ON(status <= 0);
+
+		return (status <= 0 || bes2600_chrdev_is_bus_error()) ? -EFAULT : 0;
+	}
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	/* process wifi/bt on/off operation */
+	if(bes2600_get_cmd_and_ifname(str, info) == 0) {
+		if(strncmp(info[1], "WIFI_ON", 7) == 0) {
+			ret = bes2600_switch_wifi(1);
+		} else if(strncmp(info[1], "WIFI_OFF", 8) == 0) {
+			ret = bes2600_switch_wifi(0);
+		} else if(strncmp(info[1], "BT_ON", 5) == 0) {
+			ret = bes2600_switch_bt(1);
+		} else if(strncmp(info[1], "BT_OFF", 6) == 0) {
+			ret = bes2600_switch_bt(0);
+		}
+	}
+
+	if(!ret && bes2600_chrdev_check_system_close())
+		ret = bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
+						bes2600_cdev.sbus_priv);
+
+	bes2600_recyle_cmd_and_ifname_mem(info);
+
+	return ret ;
+}
+
+
+static int bes2600_op_change_fw_type(const char *str)
+{
+	int ret = 0;
+	int temp = 0;
+	long status = 0;
+	char *cmd_ptr = NULL;
+	char fw_type[5] = {0};
+	bool sys_closed = bes2600_chrdev_check_system_close();
+
+	bes2600_dbg(BES2600_DBG_CHARDEV, "%s is called, arg:%s\n", __func__, str);
+
+	/* check if user input is valid */
+	cmd_ptr = strstr(str, "CHANGE_FW_TYPE ");
+	if(strlen(str) < 16 || !cmd_ptr) {
+		bes2600_err(BES2600_DBG_CHARDEV, "the format of \"%s\" is error\n", str);
+		return -EINVAL;
+	}
+
+	/* convert fw_type from string to int */
+	strncpy(fw_type, cmd_ptr + 14, 4);
+	fw_type[0] = '+';
+	ret = kstrtoint(fw_type, 10, &temp);
+	if(ret < 0) {
+		bes2600_err(BES2600_DBG_CHARDEV, "%s parse error\n", __func__);
+		return -EINVAL;
+	}
+
+	/* no need to realod firmware if new fw_type is equal to the old */
+	if(temp == bes2600_cdev.fw_type ) {
+		bes2600_info(BES2600_DBG_CHARDEV, "fw type is equal\n");
+		return 0;
+	}
+
+	/* close wifi net device */
+	if(bes2600_cdev.sbus_priv
+	    && bes2600_is_net_dev_created(bes2600_cdev.sbus_priv)) {
+		bes2600_unregister_net_dev(bes2600_cdev.sbus_priv);
+	}
+
+	/* update firmware type */
+	bes2600_cdev.fw_type = temp;
+	bes2600_chrdev_update_signal_mode();
+
+	if(!sys_closed) {
+		/* close device to call disconnect function */
+		bes2600_cdev.sbus_ops->power_switch(bes2600_cdev.sbus_priv, 0);
+	}
+
+	/* wait disconnect event */
+	status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 10);
+	WARN_ON(status <= 0);
+
+
+	if(bes2600_cdev.dpd_calied
+	   && bes2600_chrdev_check_system_close()) {
+		bes2600_info(BES2600_DBG_CHARDEV, "no need to reload firmware\n");
+		return 0;
+	}
+
+
+	/* power on device to call probe function */
+	bes2600_info(BES2600_DBG_CHARDEV, "reload firmware...\n");
+	bes2600_cdev.sbus_ops->power_switch(NULL, 1);
+	/* wait probe done event */
+	status = wait_event_timeout(bes2600_cdev.probe_done_wq,
+			bes2600_bootup_end(), HZ * 10);
+	WARN_ON(status <= 0);
+
+	ret = (status <= 0 || bes2600_chrdev_is_bus_error()) ? -1 : 0;
+
+
+	return ret;
+}
+
+static int bes2600_op_bt_wakeup(const char *str)
+{
+	int ret = 0;
+	unsigned long status = 0;
+
+	spin_lock(&bes2600_cdev.status_lock);
+	if(!bes2600_cdev.bt_opened) {
+		spin_unlock(&bes2600_cdev.status_lock);
+		return -EFAULT;
+	}
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	/* wait probe done event */
+	status = wait_event_timeout(bes2600_cdev.probe_done_wq,
+			bes2600_bootup_end(), HZ * 8);
+	if(status <= 0 || bes2600_chrdev_is_bus_error())
+		return -EFAULT;
+
+	bes2600_info(BES2600_DBG_CHARDEV, "bes2600 wakeup bt.\n");
+	ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true);
+
+	return ret;
+}
+
+static int bes2600_op_bt_sleep(const char *str)
+{
+	int ret = 0;
+	unsigned long status = 0;
+
+	spin_lock(&bes2600_cdev.status_lock);
+	if(!bes2600_cdev.bt_opened) {
+		spin_unlock(&bes2600_cdev.status_lock);
+		return -EFAULT;
+	}
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	/* wait probe done event */
+	status = wait_event_timeout(bes2600_cdev.probe_done_wq,
+			bes2600_bootup_end(), HZ * 8);
+	if(status <= 0 || bes2600_chrdev_is_bus_error())
+		return -EFAULT;
+
+	bes2600_info(BES2600_DBG_CHARDEV, "bes2600 allow bt sleep.\n");
+	ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_OFF, SUBSYSTEM_BT_LP, false);
+
+	return ret;
+}
+
+static int bes2600_op_set_wakeup_read_flag(const char *str)
+{
+	bes2600_dbg(BES2600_DBG_CHARDEV, "%s is called, arg:%s\n", __func__, str);
+	spin_lock(&bes2600_cdev.status_lock);
+	bes2600_cdev.read_flag = BES_CDEV_READ_WAKEUP_STATE;
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	return 0;
+}
+
+#ifdef FW_DOWNLOAD_UART_DAEMON
+int bes2600_load_uevent(char *env[])
+{
+	return kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
+}
+#endif
+
+static struct bes2600_op_map bes2600_op_map_tab[] ={
+	/*op			op_len	handler				*/
+	{"P2P_SET_NOA", 	11,	bes2600_op_default_handler},
+	{"P2P_SET_PS", 		10,	bes2600_op_default_handler},
+	{"SET_AP_WPS_P2P_IE",	17, 	bes2600_op_default_handler},
+	{"LINKSPEED", 		9,	bes2600_op_default_handler},
+	{"RSSI",		4,	bes2600_op_default_handler},
+	{"GETBAND", 		7,	bes2600_op_default_handler},
+	{"WLS_BATCHING", 	12,	bes2600_op_default_handler},
+	{"MACADDR",		7,	bes2600_op_default_handler},
+	{"RXFILTER-START",	14,	bes2600_op_default_handler},
+	{"RXFILTER-STOP",	13,	bes2600_op_default_handler},
+	{"RXFILTER-ADD",	12,	bes2600_op_default_handler},
+	{"RXFILTER-REMOVE",	15,	bes2600_op_default_handler},
+	{"BTCOEXMODE",		10,	bes2600_op_default_handler},
+	{"BTCOEXSCAN-START",	16,	bes2600_op_default_handler},
+	{"BTCOEXSCAN-STOP",	15,	bes2600_op_default_handler},
+	{"SETSUSPENDMODE",	14,	bes2600_op_default_handler},
+	{"COUNTRY",		7,	bes2600_op_default_handler},
+	{"WIFI_ON", 		7,	bes2600_op_wifi_bt_on_off},
+	{"WIFI_OFF", 		8,	bes2600_op_wifi_bt_on_off},
+	{"BT_ON", 		5,	bes2600_op_wifi_bt_on_off},
+	{"BT_OFF", 		6,	bes2600_op_wifi_bt_on_off},
+	{"CHANGE_FW_TYPE",	14,	bes2600_op_change_fw_type},
+	{"BT_WAKEUP",		9,	bes2600_op_bt_wakeup},
+	{"BT_SLEEP",		8,	bes2600_op_bt_sleep},
+	{"WAKEUP_STATE",	12,	bes2600_op_set_wakeup_read_flag},
+};
+
+static int bes2600_chrdev_check_system_close_internal(void)
+{
+	return (bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL)
+		&&(bes2600_cdev.bt_opened == false)
+		&& (bes2600_cdev.wifi_opened == false);
+}
+
+static int bes2600_chrdev_open(struct inode *inode, struct file *filp)
+{
+	if(atomic_read(&bes2600_cdev.num_proc) > 0) {
+		wait_event_timeout(bes2600_cdev.open_wq, 
+			(atomic_read(&bes2600_cdev.num_proc) == 0),
+			MAX_SCHEDULE_TIMEOUT);
+	}
+
+	bes2600_dbg(BES2600_DBG_CHARDEV, "bes2600 char device is opened\n");
+	atomic_inc(&bes2600_cdev.num_proc);
+
+        return 0;
+}
+
+static ssize_t bes2600_chrdev_read(struct file *file, char __user *user_buf,
+			     size_t count, loff_t *ppos)
+{
+	char buf[64] = {0};
+	unsigned int len;
+
+	switch (bes2600_cdev.read_flag) {
+	case BES_CDEV_READ_WAKEUP_STATE:
+		len = sprintf(buf, "wakeup_state:%u\n", bes2600_cdev.wakeup_state);
+		break;
+	default:
+		len = sprintf(buf, "dpd_calied:%d wifi_opened:%d bt_opened:%d fw_type:%d\n",
+				bes2600_cdev.dpd_calied,
+				bes2600_cdev.wifi_opened,
+				bes2600_cdev.bt_opened,
+				bes2600_cdev.fw_type);
+		break;
+	}
+
+	len = sizeof(buf);
+	/* reset read flag */
+	spin_lock(&bes2600_cdev.status_lock);
+	bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX;
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t bes2600_chrdev_write(struct file *file,
+		 const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	int i = 0;
+	int cmd_num = ARRAY_SIZE(bes2600_op_map_tab);
+	int cmd_len = 0;
+	int ret = 0;
+	char *info[2] = {0};
+	char *buf = NULL;
+
+	/* copy content from user space to kernel */
+	/* message format:"ifname:wlanx cmd:xxx arg1 arg2 ..." */
+	buf = kmalloc(count + 1, GFP_KERNEL);
+	if (copy_from_user(buf, user_buf, count))
+		return -EFAULT;
+
+	/* add terminal character */
+	buf[count] = '\0';
+
+	/* extract comand and interface */
+	if(bes2600_get_cmd_and_ifname(buf, info) != 0) {
+		bes2600_err(BES2600_DBG_CHARDEV, "%s get command fail, the origin string is %s\n", __func__, buf);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	/* match operation item and execure its handler */
+	cmd_len = strlen(info[1]);
+	for(i = 0; i < cmd_num; i++) {
+		if(cmd_len < bes2600_op_map_tab[i].op_len)
+			continue;
+
+		if(strncasecmp(info[1], bes2600_op_map_tab[i].op, bes2600_op_map_tab[i].op_len) == 0) {
+			ret = bes2600_op_map_tab[i].handler(buf);
+			break;
+		}
+	}
+
+	/* operation item mismatch */
+	if(i == cmd_num) {
+		bes2600_err(BES2600_DBG_CHARDEV, "cmd(%s) mismatch\n", info[1]);
+	}
+
+	bes2600_recyle_cmd_and_ifname_mem(info);
+	kfree(buf);
+
+	return (ret == 0) ? count : ret;
+}
+
+static int bes2600_chrdev_release (struct inode *inode, struct file *file)
+{
+	if(atomic_dec_and_test(&bes2600_cdev.num_proc)) {
+		wake_up(&bes2600_cdev.open_wq);
+	}
+
+	bes2600_dbg(BES2600_DBG_CHARDEV, "bes2600 char device is closed\n");
+
+	return 0;
+}
+
+static struct file_operations bes2600_chardev_fops =
+{
+	.owner = THIS_MODULE,
+	.open = bes2600_chrdev_open,
+	.read = bes2600_chrdev_read,
+	.write = bes2600_chrdev_write,
+	.release = bes2600_chrdev_release,
+};
+
+#ifdef BES2600_WRITE_DPD_TO_FILE
+static int bes2600_chrdev_write_dpd_data_to_file(const char *path, void *buffer, int size)
+{
+	int ret = 0;
+	struct file *fp;
+
+	if(buffer == NULL || size == 0)
+		return 0;
+
+	fp = filp_open(path, O_TRUNC | O_CREAT | O_RDWR, S_IRUSR);
+	if (IS_ERR(fp)) {
+		bes2600_err(BES2600_DBG_CHARDEV, "BES2600 : can't open %s\n",path);
+		return -1;
+	}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
+	ret = kernel_write(fp, buffer, size, &fp->f_pos);
+#else
+	ret = kernel_write(fp, buffer, size, fp->f_pos);
+#endif
+	bes2600_err_with_cond(ret < 0, BES2600_DBG_CHARDEV, "write dpd to file failed\n");
+
+	filp_close(fp,NULL);
+
+	bes2600_info(BES2600_DBG_CHARDEV, "write dpd to %s\n", path);
+
+	return ret;
+}
+
+static int bes2600_chrdev_read_and_check_dpd_data(const char *file, u8 **data, u32 *len)
+{
+	int ret = 0;
+	u32 read_len = 0;
+	u8* read_data = NULL;
+	u32 cal_crc = 0;
+	u32 dpd_crc = 0;
+	struct file *fp;
+
+	/* open file */
+	fp = filp_open(file, O_RDONLY, 0);//S_IRUSR
+	if (IS_ERR(fp)) {
+		bes2600_info(BES2600_DBG_CHARDEV, "BES2600 : can't open %s\n",file);
+		return -1;
+	}
+
+#ifdef BES2600_WRITE_DPD_TO_FILE
+	if (fp->f_inode->i_size != DPD_BIN_FILE_SIZE) {
+		bes2600_err(BES2600_DBG_CHARDEV,
+			"bes2600 dpd data file size check failed, read_size: %lld file_size: %d\n",
+			fp->f_inode->i_size, DPD_BIN_FILE_SIZE);
+		filp_close(fp, NULL);
+		return -1;
+	}
+#endif
+
+	/* allocate memory for storing reading data */
+	read_data = kmalloc(fp->f_inode->i_size, GFP_KERNEL);
+	if(read_data == NULL) {
+		bes2600_info(BES2600_DBG_CHARDEV, "%s alloc mem fail\n", __func__);
+		goto err1;
+	}
+
+	/* read data  from file */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
+	ret = kernel_read(fp, read_data, fp->f_inode->i_size, &fp->f_pos);
+#else
+	ret = kernel_read(fp, fp->f_pos, read_data, fp->f_inode->i_size);
+#endif
+	if(ret < DPD_BIN_SIZE) {
+		bes2600_err(BES2600_DBG_CHARDEV, "%s read fail, ret=%d\n", __func__, ret);
+		goto err2;
+	}
+
+	/* calculate crc value */
+	read_len = DPD_BIN_SIZE;
+	dpd_crc = *((u32 *)read_data);
+	cal_crc ^= 0xffffffffL;
+	cal_crc = crc32_le(cal_crc, read_data + 4, read_len - 4);
+	cal_crc ^= 0xffffffffL;
+
+	/* check if the dpd data is valid */
+	if(cal_crc != dpd_crc) {
+		bes2600_err(BES2600_DBG_CHARDEV,
+			"bes2600 dpd data from file check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n",
+			cal_crc, dpd_crc);
+		goto err2;
+	}
+
+	/* close file */
+	filp_close(fp, NULL);
+
+	/* copy data to external */
+	*data = read_data;
+	*len = read_len;
+
+	/* output debug information */
+	bes2600_info(BES2600_DBG_CHARDEV, "read dpd data from %s\n", file);
+
+	return 0;
+
+err2:
+	kfree(read_data);
+err1:
+	filp_close(fp, NULL);
+	*data = NULL;
+	*len = 0;
+	return -1;
+}
+#endif
+
+const u8* bes2600_chrdev_get_dpd_data(u32 *len)
+{
+#ifdef BES2600_WRITE_DPD_TO_FILE
+	if(!bes2600_cdev.dpd_calied && bes2600_cdev.no_dpd) {
+		/* read dpd data from file that stores factory dpd calibration data */
+		if((bes2600_chrdev_read_and_check_dpd_data(BES2600_DPD_GOLDEN_PATH,
+		   	&bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0) &&
+		   (bes2600_chrdev_read_and_check_dpd_data(BES2600_DEFAULT_DPD_PATH,
+			&bes2600_cdev.dpd_data, &bes2600_cdev.dpd_len) < 0)) {
+			bes2600_err(BES2600_DBG_CHARDEV, "%s read dpd data fail\n", __func__);
+			return NULL;
+		} else {
+			bes2600_cdev.dpd_calied = true;
+		}
+	}
+#endif
+
+	if(!bes2600_cdev.dpd_calied)
+		return NULL;
+	if(len)
+		*len = bes2600_cdev.dpd_len;
+
+	return bes2600_cdev.dpd_data;
+}
+
+u8* bes2600_chrdev_get_dpd_buffer(u32 size)
+{
+	if(bes2600_cdev.dpd_data)
+		kfree(bes2600_cdev.dpd_data);
+
+	bes2600_cdev.dpd_data = kmalloc(size, GFP_KERNEL);
+	if(!bes2600_cdev.dpd_data) {
+		return NULL;
+	}
+
+	bes2600_cdev.dpd_len = DPD_BIN_SIZE;
+
+	return bes2600_cdev.dpd_data;
+}
+
+void bes2600_chrdev_free_dpd_data(void)
+{
+	if(bes2600_cdev.dpd_data)
+		kfree(bes2600_cdev.dpd_data);
+
+	bes2600_cdev.dpd_data = NULL;
+	bes2600_cdev.dpd_len = 0;
+}
+
+int bes2600_chrdev_update_dpd_data(void)
+{
+	u32 cal_crc = 0;
+	u32 dpd_crc = *((u32 *)bes2600_cdev.dpd_data);
+
+	/* check if the dpd data is valid */
+	cal_crc ^= 0xffffffffL;
+	cal_crc = crc32_le(cal_crc, bes2600_cdev.dpd_data + 4, bes2600_cdev.dpd_len - 4);
+	cal_crc ^= 0xffffffffL;
+	if(cal_crc != dpd_crc) {
+		bes2600_err(BES2600_DBG_CHARDEV,
+			"bes2600 dpd data check failed, calc_crc:0x%08x dpd_crc: 0x%08x\n",
+			cal_crc, dpd_crc);
+		return -1;
+	}
+
+	bes2600_info(BES2600_DBG_CHARDEV, "bes2600 dpd cali pass.\n");
+
+	/* update dpd calibration and wait state */
+	spin_lock(&bes2600_cdev.status_lock);
+	bes2600_cdev.dpd_calied = true;
+	if(bes2600_chrdev_check_system_close_internal()) {
+		bes2600_cdev.wait_state = BES2600_BOOT_WAIT_CLOSE;
+	} else {
+		bes2600_cdev.wait_state = BES2600_BOOT_WAIT_PROBE_DONE;
+	}
+	spin_unlock(&bes2600_cdev.status_lock);
+
+#ifdef BES2600_WRITE_DPD_TO_FILE
+	/* write dpd data to file */
+	memset(bes2600_cdev.dpd_data + DPD_BIN_SIZE, 0, DPD_BIN_FILE_SIZE - DPD_BIN_SIZE);
+	bes2600_chrdev_write_dpd_data_to_file(BES2600_DPD_PATH,
+		bes2600_cdev.dpd_data, DPD_BIN_FILE_SIZE);
+#endif
+
+
+	return 0;
+}
+
+#ifdef BES2600_DUMP_FW_DPD_LOG
+void bes2600_free_dpd_log_buffer(void)
+{
+	if (bes2600_cdev.dpd_log)
+		kfree(bes2600_cdev.dpd_log);
+
+	bes2600_cdev.dpd_log_len = 0;
+	bes2600_cdev.dpd_log = NULL;
+}
+
+u8* bes2600_alloc_dpd_log_buffer(u16 len)
+{
+	bes2600_cdev.dpd_log_len = len;
+
+	if (bes2600_cdev.dpd_log)
+		kfree(bes2600_cdev.dpd_log);
+
+	bes2600_cdev.dpd_log = kzalloc(len, GFP_KERNEL);
+	if (!bes2600_cdev.dpd_log) {
+		bes2600_cdev.dpd_log_len = 0;
+		return NULL;
+	}
+
+	return bes2600_cdev.dpd_log;
+}
+
+void bes2600_get_dpd_log(char **data, size_t *len)
+{
+	if (!bes2600_cdev.dpd_log) {
+		*data = NULL;
+		*len = 0;
+	} else {
+		*data = bes2600_cdev.dpd_log;
+		*len = (size_t)bes2600_cdev.dpd_log_len;
+	}
+}
+#endif /* BES2600_DUMP_FW_DPD_LOG */
+
+void bes2600_chrdev_set_sbus_priv_data(struct sbus_priv *priv, bool error)
+{
+	bes2600_cdev.sbus_priv = priv;
+	if(priv) {
+		if(bes2600_cdev.bton_pending) {
+			bes2600_info(BES2600_DBG_CHARDEV, "execute pending bt on operation.\n");
+			bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_ON, SUBSYSTEM_BT, true);
+
+			bes2600_cdev.bton_pending = false;
+		}
+
+		spin_lock(&bes2600_cdev.status_lock);
+		if(bes2600_cdev.wait_state == BES2600_BOOT_WAIT_PROBE_DONE) {
+			bes2600_cdev.wait_state = BES2600_BOOT_WAIT_NONE;
+		}
+		spin_unlock(&bes2600_cdev.status_lock);
+
+		bes2600_info(BES2600_DBG_CHARDEV, "wakup proc on wq of probe_done.\n");
+	} else {
+		spin_lock(&bes2600_cdev.status_lock);
+		bes2600_cdev.wait_state = BES2600_BOOT_WAIT_NONE;
+		bes2600_cdev.bus_error = error;
+		if(bes2600_cdev.bus_error) {
+			bes2600_cdev.wifi_opened = false;
+			bes2600_cdev.bt_opened = false;
+			bes2600_cdev.bton_pending = false;
+			bes2600_cdev.bus_probe = BES2600_BUS_PROBE_NONE;
+		}
+		spin_unlock(&bes2600_cdev.status_lock);
+		bes2600_info(BES2600_DBG_CHARDEV, "wakup proc on wq of disconnect_done.\n");
+	}
+
+	wake_up(&bes2600_cdev.probe_done_wq);
+}
+
+struct sbus_priv * bes2600_chrdev_get_sbus_priv_data(void)
+{
+	return bes2600_cdev.sbus_priv;
+}
+
+int bes2600_chrdev_check_system_close(void)
+{
+	bool sys_closed = false;
+
+	spin_lock(&bes2600_cdev.status_lock);
+	sys_closed = bes2600_chrdev_check_system_close_internal();
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	return sys_closed;
+}
+
+int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv)
+{
+	int ret = 0;
+	long status = 0;
+
+	if(!sbus_ops || !priv) {
+		bes2600_warn(BES2600_DBG_CHARDEV, "abort power down device.\n");
+		return 0;
+	}
+
+	bes2600_dbg(BES2600_DBG_CHARDEV, "power down bes2600.\n");
+
+	/* trigger system to execute disconnect function */
+	ret = sbus_ops->power_switch(priv, 0);
+
+	/* wait disconnect event */
+	status = wait_event_timeout(bes2600_cdev.probe_done_wq, (bes2600_cdev.sbus_priv == NULL), HZ * 3);
+	WARN_ON(status <= 0);
+
+
+	return ret;
+}
+
+bool bes2600_chrdev_is_wifi_opened(void)
+{
+	bool wifi_opened = false;
+
+	spin_lock(&bes2600_cdev.status_lock);
+	wifi_opened = bes2600_cdev.wifi_opened;
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	if(bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL)
+		return true;
+	else if(bes2600_cdev.fw_type == BES2600_FW_TYPE_BT)
+		return false;
+	else if(bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL)
+		return wifi_opened;
+
+	return false;
+}
+
+bool bes2600_chrdev_is_bt_opened(void)
+{
+	bool bt_opened = false;
+
+	spin_lock(&bes2600_cdev.status_lock);
+	bt_opened = bes2600_cdev.bt_opened;
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	if(bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL)
+		return false;
+	else if(bes2600_cdev.fw_type == BES2600_FW_TYPE_BT)
+		return true;
+	else if(bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL)
+		return bt_opened;
+
+	return false;
+}
+
+void bes2600_chrdev_wakeup_bt(void)
+{
+	int ret = 0;
+
+	if(bes2600_cdev.bt_opened && bes2600_cdev.sbus_priv) {
+		bes2600_info(BES2600_DBG_PM, "wakeup bt in resume flow\n");
+		ret = bes2600_chrdev_switch_subsys(GPIO_WAKE_FLAG_BT_LP_ON, SUBSYSTEM_BT_LP, true);
+
+		bes2600_err_with_cond(ret, BES2600_DBG_PM, "Wakeup BT fail in resume\n");
+	}
+}
+
+int bes2600_chrdev_get_fw_type(void)
+{
+	return bes2600_cdev.fw_type;
+}
+
+bool bes2600_chrdev_is_signal_mode(void)
+{
+	return bes2600_cdev.sig_mode;
+}
+
+bool bes2600_chrdev_is_bus_error(void)
+{
+	bool error = false;
+
+	spin_lock(&bes2600_cdev.status_lock);
+	error = (bes2600_cdev.bus_error || bes2600_cdev.bus_probe != BES2600_BUS_PROBE_OK);
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	return error;
+}
+
+void bes2600_chrdev_update_signal_mode(void)
+{
+	if(bes2600_cdev.fw_type >= BES2600_FW_TYPE_MAX_NUM) {
+		bes2600_cdev.fw_type = BES2600_FW_TYPE_WIFI_SIGNAL;
+		bes2600_warn(BES2600_DBG_CHARDEV, "unexpected fw type, switch to wifi signal mode\n");
+	}
+
+	if (bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_SIGNAL) {
+		bes2600_cdev.sig_mode = true;
+	} else if ((bes2600_cdev.fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL)
+		|| (bes2600_cdev.fw_type == BES2600_FW_TYPE_BT)) {
+		bes2600_cdev.sig_mode = false;
+	}
+}
+
+static void bes2600_chrdev_wifi_force_close_work(struct work_struct *work)
+{
+	char wifi_state[15];
+	char bt_state[15];
+	char fw_type[15];
+	char *env[] = { wifi_state, bt_state, fw_type, NULL };
+	int ret;
+
+	if(bes2600_chrdev_is_wifi_opened()) {
+		bes2600_info(BES2600_DBG_CHARDEV, "system exeception, force wifi down\n");
+
+		/* halt device if needed */
+		if(bes2600_cdev.halt_dev && bes2600_cdev.sbus_ops->halt_device) {
+			bes2600_cdev.sbus_ops->halt_device(bes2600_cdev.sbus_priv);
+		}
+
+		/* unregister wifi */
+		bes2600_switch_wifi(0);
+
+		/* power down device if wifi is only opened */
+		if(bes2600_chrdev_check_system_close()) {
+			bes2600_chrdev_do_system_close(bes2600_cdev.sbus_ops,
+						bes2600_cdev.sbus_priv);
+		}
+
+		/* notify userspace */
+		snprintf(wifi_state, sizeof(wifi_state), "WIFI_OPENED=%d", bes2600_cdev.wifi_opened);
+		snprintf(bt_state, sizeof(bt_state), "BT_OPENED=%d", bes2600_cdev.bt_opened);
+		snprintf(fw_type, sizeof(fw_type), "FW_TYPE=%d", bes2600_cdev.fw_type);
+		ret = kobject_uevent_env(&bes2600_cdev.device->kobj, KOBJ_CHANGE, env);
+		bes2600_err_with_cond(ret != 0, BES2600_DBG_CHARDEV, "bes2600 notify userspace failed\n");
+	}
+}
+
+void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev)
+{
+	if(hw_priv == NULL)
+		return;
+
+	if(bes2600_chrdev_is_wifi_opened() &&
+	   !work_pending(&bes2600_cdev.wifi_force_close_work)) {
+		spin_lock(&bes2600_cdev.status_lock);
+		bes2600_cdev.bus_error = true;
+		bes2600_cdev.halt_dev = halt_dev;
+		spin_unlock(&bes2600_cdev.status_lock);
+
+		bes2600_tx_loop_set_enable(hw_priv);
+		schedule_work(&bes2600_cdev.wifi_force_close_work);
+	}
+}
+
+static void bes2600_probe_timeout_work(struct work_struct *work)
+{
+	spin_lock(&bes2600_cdev.status_lock);
+	bes2600_cdev.bus_probe = BES2600_BUS_PROBE_TIMEOUT;
+	bes2600_cdev.wifi_opened = false;
+	bes2600_cdev.bt_opened = false;
+	bes2600_cdev.bton_pending = false;
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	bes2600_info(BES2600_DBG_CHARDEV, "bus probe timeout\n");
+	wake_up(&bes2600_cdev.probe_done_wq);
+}
+
+void bes2600_chrdev_start_bus_probe(void)
+{
+	spin_lock(&bes2600_cdev.status_lock);
+	bes2600_cdev.bus_probe = BES2600_BUS_PROBE_START;
+	spin_unlock(&bes2600_cdev.status_lock);
+
+	cancel_delayed_work_sync(&bes2600_cdev.probe_timeout_work);
+	schedule_delayed_work(&bes2600_cdev.probe_timeout_work, (HZ * 8) / 10);
+}
+
+void bes2600_chrdev_bus_probe_notify(void)
+{
+	spin_lock(&bes2600_cdev.status_lock);
+	bes2600_cdev.bus_probe = BES2600_BUS_PROBE_OK;
+	spin_unlock(&bes2600_cdev.status_lock);
+	cancel_delayed_work_sync(&bes2600_cdev.probe_timeout_work);
+
+	wake_up(&bes2600_cdev.probe_done_wq);
+}
+
+void bes2600_chrdev_wifi_update_wakeup_reason(u32 val)
+{
+	spin_lock(&bes2600_cdev.status_lock);
+	bes2600_cdev.wakeup_state = val;
+	spin_unlock(&bes2600_cdev.status_lock);
+}
+
+int bes2600_chrdev_init(struct sbus_ops *ops)
+{
+	int ret = 0;
+
+	/* allocate devide id */
+	ret = alloc_chrdev_region(&bes2600_cdev.dev_id, 0, 1, "bes2600_chrdev");
+	if(ret < 0){
+		bes2600_err(BES2600_DBG_CHARDEV, "bes2600 alloc device id fail\n");
+		ret =  -EFAULT;
+		goto fail;
+	}
+
+	/* extract major and minor device id */
+	bes2600_cdev.major = MAJOR(bes2600_cdev.dev_id);
+	bes2600_cdev.minor = MINOR(bes2600_cdev.dev_id);
+
+	/* add char device and bind operation function */
+	bes2600_cdev.cdev.owner = THIS_MODULE;
+	cdev_init(&bes2600_cdev.cdev, &bes2600_chardev_fops);
+	ret = cdev_add(&bes2600_cdev.cdev, bes2600_cdev.dev_id, 1);
+	if(ret < 0){
+		bes2600_err(BES2600_DBG_CHARDEV, "bes2600 char device add fail\n");
+		ret =  -EFAULT;
+		goto fail1;
+	}
+
+	/* create class for creating device node */
+	bes2600_cdev.class = class_create(THIS_MODULE, "bes2600_chrdev");
+	if (IS_ERR(bes2600_cdev.class)){
+		bes2600_err(BES2600_DBG_CHARDEV, "bes2600 char device add fail\n");
+		ret = -EFAULT;
+		goto fail2;
+	}
+
+	/* get char device pointer */
+	bes2600_cdev.device = device_create(bes2600_cdev.class, NULL, bes2600_cdev.dev_id, NULL, "bes2600");
+	if(IS_ERR(bes2600_cdev.device)){
+		bes2600_err(BES2600_DBG_CHARDEV, "bes2600 char device create fail\n");
+		ret =  -EFAULT;
+		goto fail3;
+	}
+
+	/* initialise global variable */
+	atomic_set(&bes2600_cdev.num_proc, 0);
+	init_waitqueue_head(&bes2600_cdev.open_wq);
+	spin_lock_init(&bes2600_cdev.status_lock);
+	init_waitqueue_head(&bes2600_cdev.probe_done_wq);
+	INIT_WORK(&bes2600_cdev.wifi_force_close_work, bes2600_chrdev_wifi_force_close_work);
+	INIT_DELAYED_WORK(&bes2600_cdev.probe_timeout_work, bes2600_probe_timeout_work);
+#ifdef CONFIG_BES2600_WIFI_BOOT_ON
+	bes2600_cdev.wifi_opened = true;
+#else
+	bes2600_cdev.wifi_opened = false;
+#endif
+#ifdef CONFIG_BES2600_BT_BOOT_ON
+	bes2600_cdev.bt_opened = true;
+	bes2600_cdev.bton_pending = true;
+#else
+	bes2600_cdev.bt_opened = false;
+	bes2600_cdev.bton_pending = false;
+#endif
+	bes2600_cdev.dpd_calied = false;
+	bes2600_cdev.wait_state = BES2600_BOOT_WAIT_NONE;
+	bes2600_cdev.sbus_ops = ops;
+	bes2600_cdev.bus_error = false;
+	bes2600_cdev.halt_dev = false;
+	bes2600_cdev.read_flag = BES_CDEV_READ_NUM_MAX;
+	bes2600_info(BES2600_DBG_CHARDEV, "%s done\n", __func__);
+
+	return 0;
+
+fail3:
+	class_destroy(bes2600_cdev.class);
+fail2:
+	cdev_del(&bes2600_cdev.cdev);
+fail1:
+	unregister_chrdev_region(bes2600_cdev.dev_id, 1);
+fail:
+	return ret;
+}
+
+void bes2600_chrdev_free(void)
+{
+#ifdef BES2600_DUMP_FW_DPD_LOG
+	bes2600_free_dpd_log_buffer();
+#endif
+	bes2600_chrdev_free_dpd_data();
+	cdev_del(&bes2600_cdev.cdev);
+	unregister_chrdev_region(bes2600_cdev.dev_id, 1);
+	device_destroy(bes2600_cdev.class, bes2600_cdev.dev_id);
+	class_destroy(bes2600_cdev.class);
+	bes2600_info(BES2600_DBG_CHARDEV, "%s done\n", __func__);
+}
diff --git a/drivers/staging/bes2600/bes_chardev.h b/drivers/staging/bes2600/bes_chardev.h
new file mode 100644
index 000000000000..8c7257c087a4
--- /dev/null
+++ b/drivers/staging/bes2600/bes_chardev.h
@@ -0,0 +1,70 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __BES_CHARDEV_H__
+#define __BES_CHARDEV_H__
+
+#define BES2600_FW_TYPE_WIFI_SIGNAL	0
+#define BES2600_FW_TYPE_WIFI_NO_SIGNAL	1
+#define BES2600_FW_TYPE_BT		2
+#define BES2600_FW_TYPE_MAX_NUM     3
+
+#define DPD_BIN_SIZE            0x3AF8
+#define DPD_BIN_FILE_SIZE       0x4000
+
+enum pend_read_op {
+	BES_CDEV_READ_WAKEUP_STATE = 0,
+	/* add new here */
+
+	BES_CDEV_READ_NUM_MAX,
+};
+
+/* dpd management */
+u8* bes2600_chrdev_get_dpd_buffer(u32 size);
+int bes2600_chrdev_update_dpd_data(void);
+const u8* bes2600_chrdev_get_dpd_data(u32 *len);
+void bes2600_chrdev_free_dpd_data(void);
+
+/* get/set subs_priv instance from/to bes_chrdev module */
+void bes2600_chrdev_set_sbus_priv_data(struct sbus_priv *priv, bool error);
+struct sbus_priv *bes2600_chrdev_get_sbus_priv_data(void);
+
+/* used to control device power down */
+int bes2600_chrdev_check_system_close(void);
+int bes2600_chrdev_do_system_close(const struct sbus_ops *sbus_ops, struct sbus_priv *priv);
+void bes2600_chrdev_wakeup_bt(void);
+void bes2600_chrdev_wifi_force_close(struct bes2600_common *hw_priv, bool halt_dev);
+
+/* get and set internal state */
+bool bes2600_chrdev_is_wifi_opened(void);
+bool bes2600_chrdev_is_bt_opened(void);
+int bes2600_chrdev_get_fw_type(void);
+bool bes2600_chrdev_is_signal_mode(void);
+void bes2600_chrdev_update_signal_mode(void);
+bool bes2600_chrdev_is_bus_error(void);
+
+/* bus probe check */
+void bes2600_chrdev_start_bus_probe(void);
+void bes2600_chrdev_bus_probe_notify(void);
+
+/* set wifi wakeup state */
+void bes2600_chrdev_wifi_update_wakeup_reason(u32 val);
+
+/* init and deinit module */
+int bes2600_chrdev_init(struct sbus_ops *ops);
+void bes2600_chrdev_free(void);
+
+#ifdef BES2600_DUMP_FW_DPD_LOG
+void bes2600_free_dpd_log_buffer(void);
+u8* bes2600_alloc_dpd_log_buffer(u16 len);
+void bes2600_get_dpd_log(char **data, size_t *len);
+#endif
+
+#endif
\ No newline at end of file
diff --git a/drivers/staging/bes2600/bes_fw.c b/drivers/staging/bes2600/bes_fw.c
new file mode 100644
index 000000000000..67f361c0431f
--- /dev/null
+++ b/drivers/staging/bes2600/bes_fw.c
@@ -0,0 +1,1511 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "bes_fw_common.h"
+#include "bes2600.h"
+#include "hwio.h"
+#include "sbus.h"
+#include "bes2600_driver_mode.h"
+#include "bes_chardev.h"
+#include <linux/string.h>
+#include "bes2600_factory.h"
+
+#if defined(FW_DOWNLOAD_BY_SDIO)
+//#define BES_FW_BUILTIN
+#ifdef BES_FW_BUILTIN
+#include "bes_firmware.h"
+#endif
+
+struct platform_fw_t {
+	struct delayed_work work_data;
+	struct sdio_func *func;
+	struct completion completion_data;
+	const struct sbus_ops *sbus_ops;
+	struct sbus_priv *sbus_priv;
+};
+
+static void bes_fw_irq_handler(void *priv)
+{
+	struct platform_fw_t *fw_data = (struct platform_fw_t *)priv;
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s\n", __func__);
+	complete(&fw_data->completion_data);
+}
+
+//#define BES_SLAVE_RX_DOUBLE_CHECK
+static int bes_slave_rx_ready(struct platform_fw_t *fw_data, u8* buf_cnt,
+					u16* buf_len, int timeout)
+{
+	int ret;
+	unsigned long start = jiffies;
+
+	do {
+		ret = bes2600_reg_read(0x108, buf_cnt, 1);
+		if (!(ret || buf_cnt)) {
+			mdelay(50);
+			continue;
+		} else if (ret) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "%s,%d err=%d\n", __func__, __LINE__, ret);
+		} else {
+			ret = bes2600_reg_read_16(0x109, buf_len);
+		}
+		break;
+	} while(time_before(jiffies, start + timeout));
+
+	return ret;
+}
+
+//#define BES_SLAVE_TX_DOUBLE_CHECK
+//#define MISSED_INTERRUPT_WORKAROUND
+static int bes_slave_tx_ready(struct platform_fw_t *fw_data, u16 *tx_len, int timeout)
+{
+	int ret, retry = 0;
+
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s now=%lu\n", __func__, jiffies);
+
+	msleep(2);
+
+	ret = wait_for_completion_interruptible_timeout(&fw_data->completion_data, timeout);
+	if (ret > 0) {
+#ifdef MISSED_INTERRUPT_WORKAROUND
+test_read_tx:
+#endif
+		do {
+			ret = bes2600_reg_read_16(0, tx_len);
+			if (!ret && (*tx_len))
+				break;
+			else
+				bes2600_err(BES2600_DBG_DOWNLOAD,"%s,%d ret=%d tx_len=%x retry=%d\n",
+						__func__, __LINE__, ret, *tx_len, retry);
+			retry++;
+		} while(retry <= 5);
+		reinit_completion(&fw_data->completion_data);
+
+	} else if(!ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s now=%lu delta=%d\n", __func__, jiffies, timeout);
+#ifndef MISSED_INTERRUPT_WORKAROUND
+		ret = -110;
+#else
+		goto test_read_tx;
+#endif
+	} else {
+		// ret = -ERESTARTSYS, to be continued;
+	}
+
+	return ret;
+}
+
+int bes_host_slave_sync(struct bes2600_common *hw_priv)
+{
+	u8 val;
+	int ret;
+
+	ret = bes2600_reg_read(BES_HOST_INT_REG_ID, &val, 1);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s,%d err=%d\n", __func__, __LINE__, ret);
+		return ret;
+	}
+
+	val |= BES_HOST_INT;
+	ret = bes2600_reg_write(BES_HOST_INT_REG_ID, &val, 1);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s,%d err=%d\n", __func__, __LINE__, ret);
+	}
+	return ret;
+}
+
+//#define DATA_DUMP_OBSERVE
+
+static int bes_firmware_download_write_reg(struct platform_fw_t *fw_data, u32 addr, u32 val)
+{
+	u8 frame_num = 0;
+	u8 buf_cnt = 0;
+	u16 tx_size = 0;
+	u16 rx_size = 0;
+	u32 length = 0;
+	u8 *short_buf;
+	int ret;
+
+	struct fw_msg_hdr_t header;
+	struct fw_info_t fw_info;
+	struct download_fw_t download_addr;
+
+	fw_info.addr = addr;
+	fw_info.len = 4;
+
+	ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ);
+	if (!ret) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave rx buf cnt:%d,buf len max:%d\n", buf_cnt, tx_size);
+	} else {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait bes sdio slave rx ready tiemout:%d\n", ret);
+		return ret;
+	}
+
+	short_buf = kzalloc(512, GFP_KERNEL);
+	if (!short_buf)
+		return -ENOMEM;
+
+	header.type = FRAME_HEADER_DOWNLOAD_INFO;
+	header.seq = frame_num;
+	header.len = sizeof(struct fw_info_t);
+	frame_num++;
+	memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&fw_info, sizeof(struct fw_info_t));
+	length = BES_FW_MSG_TOTAL_LEN(header);
+	length = length > 512 ? length : 512;
+	ret = bes2600_data_write(short_buf, length);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "tx download firmware info err:%d\n", ret);
+		goto err;
+	}
+
+	ret = bes_slave_tx_ready(fw_data, &rx_size, HZ);
+	if (!ret) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave tx ready %d bytes\n", rx_size);
+	} else {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process failed:%d\n", ret);
+		goto err;
+	}
+
+	ret = bes2600_data_read(short_buf, rx_size);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rx download firmware info rsp err:%d\n", ret);
+		goto err;
+	}
+
+	header.type = FRAME_HEADER_DOWNLOAD_DATA;
+	header.seq = frame_num;
+	header.len = 8;
+	frame_num++;
+
+	download_addr.addr = fw_info.addr;
+
+	memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t), &download_addr.addr, sizeof(struct download_fw_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t), &val, 4);
+	length = BES_FW_MSG_TOTAL_LEN(header);
+
+	length = length > 512 ? length : 512;
+	ret = bes2600_data_write(short_buf, length);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "tx download fw data err:%d\n", ret);
+		goto err;
+	}
+	ret = bes_slave_tx_ready(fw_data, &rx_size, HZ);
+	if (!ret) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size);
+	} else {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret);
+		goto err;
+	}
+
+	ret = bes2600_data_read(short_buf, rx_size);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rx tx download fw data rsp err:%d\n", ret);
+		goto err;
+	}
+
+err:
+	kfree(short_buf);
+	return ret;
+}
+
+static int bes_firmware_download_write_mem(struct platform_fw_t *fw_data, const u32 addr, const u8 *data, const  u32 len)
+{
+	u8 frame_num = 0;
+	u8 last_frame_num = 0;
+	u8 buf_cnt = 0;
+
+	u16 tx_size = 0;
+	u16 rx_size = 0;
+
+	u32 length = 0;
+	u32 code_length = len;
+	u32 retry_cnt = 0;
+	int ret;
+
+	const u8 *data_p;
+	u8 *short_buf, *long_buf;
+
+	struct fw_msg_hdr_t header;
+	struct fw_info_t fw_info;
+	struct download_fw_t download_addr;
+	struct fw_crc_t crc32_t;
+
+retry:
+	fw_info.addr = addr;
+	fw_info.len = len;
+	data_p = data;
+
+	crc32_t.crc32 = 0;
+	crc32_t.crc32 ^= 0xffffffffL;
+	crc32_t.crc32 = crc32_le(crc32_t.crc32, (u8 *)data, len);
+	crc32_t.crc32 ^= 0xffffffffL;
+
+	ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ);
+	if (!ret) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave rx buf cnt:%d,buf len max:%d\n", buf_cnt, tx_size);
+	} else {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "wait bes sdio slave rx ready tiemout:%d\n", ret);
+		return ret;
+	}
+
+	header.type = FRAME_HEADER_DOWNLOAD_INFO;
+	header.seq = frame_num;
+	header.len = sizeof(struct fw_info_t);
+	last_frame_num = frame_num;
+	frame_num++;
+
+	short_buf = kzalloc(512, GFP_KERNEL);
+	if (!short_buf)
+		return -ENOMEM;
+	memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&fw_info, sizeof(struct fw_info_t));
+	length = BES_FW_MSG_TOTAL_LEN(header);
+
+	if (tx_size > length) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s", "tx download firmware info\n");
+	} else {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "%s:%d bes slave has no enough buffer%d/%d\n", __func__, __LINE__, tx_size, length);
+		goto err1;
+	}
+
+	length = length > 512 ? length : 512;
+	ret = bes2600_data_write(short_buf, length);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "tx download firmware info err:%d\n", ret);
+		goto err1;
+	}
+
+	ret = bes_slave_tx_ready(fw_data, &rx_size, HZ);
+	if (!ret) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave tx ready %d bytes\n", rx_size);
+	} else {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "wait slave process failed:%d\n", ret);
+		goto err1;
+	}
+
+	ret = bes2600_data_read(short_buf, rx_size);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rx download firmware info rsp err:%d\n", ret);
+		goto err1;
+	}
+
+	//check device rx status
+	ret = bes_frame_rsp_check(short_buf, last_frame_num);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware info err:%d\n", ret);
+		goto err1;
+	}
+
+	//download firmware
+	long_buf = kmalloc(1024 * 32, GFP_KERNEL);
+	if (!long_buf) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d fw failed to allocate memory\n",__func__, __LINE__);
+		ret = -ENOMEM;
+		goto err1;
+	}
+	download_addr.addr = fw_info.addr;
+
+	while (code_length) {
+
+		ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ);
+		if (ret) {
+			goto err2;
+		} else {
+			bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes salve rx ready %d bytes\n", tx_size);
+		}
+
+
+		if ((tx_size < 4) || (tx_size % 4)) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d tx size=%d\n", __func__, __LINE__, tx_size);
+			ret = -203;
+			goto err2;
+		}
+
+		if ((code_length + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)) < tx_size) {
+			length = code_length + sizeof(struct download_fw_t);
+		} else {
+			length = tx_size - sizeof(struct fw_msg_hdr_t);
+		}
+
+		header.type = FRAME_HEADER_DOWNLOAD_DATA;
+		header.seq = frame_num;
+		header.len = length;
+		last_frame_num = frame_num;
+		frame_num++;
+
+		memcpy(long_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+		memcpy(long_buf + sizeof(struct fw_msg_hdr_t), &download_addr.addr, sizeof(struct download_fw_t));
+		length -= sizeof(struct download_fw_t);//real data length
+		memcpy(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t), data_p, length);
+
+		length += (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
+
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "tx_download_firmware_data:%x %d\n", download_addr.addr, length);
+
+		ret = bes2600_data_write(long_buf, length > 512 ? length : 512);
+		if (ret) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "tx download fw data err:%d\n", ret);
+			goto err2;
+		}
+		length -= (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
+
+		ret = bes_slave_tx_ready(fw_data, &rx_size, HZ);
+		if (!ret) {
+			bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size);
+		} else {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret);
+			goto err2;
+		}
+
+		ret = bes2600_data_read(short_buf, rx_size);
+		if (ret) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "rx tx download fw data rsp err:%d\n", ret);
+			goto err2;
+		}
+
+		//check device rx status
+		ret = bes_frame_rsp_check(short_buf, last_frame_num);
+		if (ret) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "rsp tx download fw err:%d\n", ret);
+			goto err2;
+		}
+
+		code_length -= length;
+		data_p += length;
+		download_addr.addr += length;
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "already tx fw size:%x/%x\n", download_addr.addr - fw_info.addr, fw_info.len);
+	}
+
+	//Notify Device:The firmware download is complete
+
+	ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ);
+	if (ret) {
+		goto err2;
+	} else {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes salve rx ready %d bytes\n", tx_size);
+	}
+
+	header.type = FRAME_HEADER_DOWNLOAD_END;
+	header.seq = frame_num;
+	header.len = sizeof(struct fw_crc_t);
+	last_frame_num = frame_num;
+	frame_num++;
+
+	memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&crc32_t.crc32, sizeof(struct fw_crc_t));
+	length = BES_FW_MSG_TOTAL_LEN(header);
+
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s", "tx download firmware complete command\n");
+
+	length = length > 512 ? length : 512;
+	ret = bes2600_data_write(short_buf, length);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "tx downlod firmware complete command err:%d\n", ret);
+		goto err2;
+	}
+
+	ret = bes_slave_tx_ready(fw_data, &rx_size, HZ);
+	if (!ret) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size);
+	} else {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret);
+		goto err2;
+	}
+
+	ret = bes2600_data_read(short_buf, rx_size);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "receive download firmware complete cmd rsp err:%d\n", ret);
+		goto err2;
+	}
+
+	//check device rx status
+	ret = bes_frame_rsp_check(short_buf, last_frame_num);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware complete err:%d\n", ret);
+		goto err2;
+	}
+err2:
+	kfree(long_buf);
+err1:
+	kfree(short_buf);
+
+	if (ret && retry_cnt < 3) {
+		retry_cnt++;
+		goto retry;
+	}
+	return ret;
+}
+
+int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_name, bool auto_run)
+{
+	u8 frame_num = 0;
+	u8 last_frame_num = 0;
+	u8 buf_cnt = 0;
+
+	u16 tx_size = 0;
+	u16 rx_size = 0;
+
+	u32 length = 0;
+	u32 code_length = 0;
+	u32 retry_cnt = 0;
+	int ret;
+	const u8 *fw_ver_ptr;
+	const u8 *data_p;
+	u8 *short_buf, *long_buf;
+
+#ifndef BES_FW_BUILTIN
+#ifdef CONFIG_FW_LOADER
+	const struct firmware *fw_bin;
+#else
+	struct my_firmware_t {
+		u8 *data;
+		size_t size;
+	} my_fw, *fw_bin;
+	struct file *my_fwp;
+#endif
+#endif
+
+#ifdef DATA_DUMP_OBSERVE
+	char *observe;
+	size_t observe_len;
+	loff_t observe_off = 0;
+	mm_segment_t old_fs;
+	struct file *observe_file = NULL;
+#endif
+
+	struct fw_msg_hdr_t header;
+	struct fw_info_t fw_info;
+	struct download_fw_t download_addr;
+	struct fw_crc_t crc32_t;
+	struct run_fw_t run_addr;
+
+retry:
+#ifndef BES_FW_BUILTIN
+#ifdef CONFIG_FW_LOADER
+	ret = request_firmware(&fw_bin, fw_name, NULL);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "request firmware err:%d\n", ret);
+		return ret;
+	}
+#else
+	my_fwp = filp_open(fw_name, O_RDONLY, 0);
+	if (IS_ERR(my_fwp)) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "firmware path invalid:%s,%ld.\n", fw_name, PTR_ERR(my_fwp));
+		return PTR_ERR(my_fwp);
+	}
+	fw_bin = &my_fw;
+	fw_bin->size = my_fwp->f_inode->i_size;
+	if (fw_bin->size <= 0) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wrong firmware size:%lu.\n", (long unsigned)fw_bin->size);
+		ret = -ENOENT;
+		goto close_fp;
+	}
+	fw_bin->data = kmalloc(fw_bin->size, GFP_KERNEL);
+	if (!fw_bin->data) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "kmalloc firmware buffer failed.\n");
+		ret = -ENOMEM;
+		goto close_fp;
+	}
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0))
+	ret = kernel_read(my_fwp, fw_bin->data, fw_bin->size, &my_fwp->f_pos);
+#else
+	ret = kernel_read(my_fwp, my_fwp->f_pos, fw_bin->data, fw_bin->size);
+#endif
+	if (ret != fw_bin->size) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "read firmware size error:%d,%lu.\n", ret, (long unsigned)fw_bin->size);
+		ret = -EIO;
+		goto free_mem;
+	}
+#endif
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s fw.size=%ld\n", __func__, (long)fw_bin->size);
+#endif
+
+#ifdef BES_FW_BUILTIN
+	bes_parse_fw_info((u8 *)firmware_device, FIRMWARE_SIZE	, &fw_info.addr, &crc32_t.crc32);
+#else
+	bes_parse_fw_info(fw_bin->data, fw_bin->size, &fw_info.addr, &crc32_t.crc32);
+#endif
+
+	fw_ver_ptr = bes2600_get_firmware_version_info(fw_bin->data, fw_bin->size);
+	if(fw_ver_ptr == NULL)
+		bes2600_err(BES2600_DBG_DOWNLOAD, "------Firmware version get failed\n");
+	else
+		bes2600_info(BES2600_DBG_DOWNLOAD, "------Firmware: %s version :%s\n", fw_name ,fw_ver_ptr);
+
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "------load addr  :0x%08X\n", fw_info.addr);
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "------data crc   :0x%08X\n", crc32_t.crc32);
+
+#ifdef BES_FW_BUILTIN
+	code_length = FIRMWARE_SIZE - CODE_DATA_USELESS_SIZE;
+#else
+	code_length = fw_bin->size - CODE_DATA_USELESS_SIZE;
+#endif
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "------code size  :%d\n", code_length);
+
+	fw_info.len = code_length;
+#ifdef BES_FW_BUILTIN
+	data_p = (u8 *)firmware_device;
+#else
+	data_p = fw_bin->data;
+#endif
+
+	ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ);
+	if (!ret) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave rx buf cnt:%d,buf len max:%d\n", buf_cnt, tx_size);
+	} else {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "wait bes sdio slave rx ready tiemout:%d\n", ret);
+		return ret;
+	}
+
+	header.type = FRAME_HEADER_DOWNLOAD_INFO;
+	header.seq = frame_num;
+	header.len = sizeof(struct fw_info_t);
+	last_frame_num = frame_num;
+	frame_num++;
+
+	short_buf = kzalloc(512, GFP_KERNEL);
+	if (!short_buf)
+		return -ENOMEM;
+	memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&fw_info, sizeof(struct fw_info_t));
+	length = BES_FW_MSG_TOTAL_LEN(header);
+
+	//mdelay(5000);
+	bes2600_dbg_dump(BES2600_DBG_DOWNLOAD, "Fw Info:", short_buf, length);
+
+	if (tx_size > length) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s", "tx download firmware info\n");
+	} else {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "%s:%d bes slave has no enough buffer%d/%d\n", __func__, __LINE__, tx_size, length);
+		goto err1;
+	}
+
+	length = length > 512 ? length : 512;
+	ret = bes2600_data_write(short_buf, length);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "tx download firmware info err:%d\n", ret);
+		goto err1;
+	}
+
+#if 1
+	ret = bes_slave_tx_ready(fw_data, &rx_size, HZ);
+	if (!ret) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "sdio slave tx ready %d bytes\n", rx_size);
+	} else {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "wait slave process failed:%d\n", ret);
+		goto err1;
+	}
+#ifdef BES_SLAVE_TX_DOUBLE_CHECK
+	if (rx_size != 8)
+		rx_size = 8;
+#endif
+#else
+	mdelay(100);
+	rx_size = 8;
+#endif
+
+	ret = bes2600_data_read(short_buf, rx_size);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rx download firmware info rsp err:%d\n", ret);
+		goto err1;
+	}
+
+	//check device rx status
+	ret = bes_frame_rsp_check(short_buf, last_frame_num);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware info err:%d\n", ret);
+		goto err1;
+	}
+
+	//download firmware
+	long_buf = kmalloc(1024 * 32, GFP_KERNEL);
+	if (!long_buf) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d fw failed to allocate memory\n",__func__, __LINE__);
+		ret = -ENOMEM;
+		goto err1;
+	}
+	download_addr.addr = fw_info.addr;
+
+#ifdef DATA_DUMP_OBSERVE
+	observe_file = filp_open("/lib/firmware/bes2002_fw_write.bin", O_CREAT | O_RDWR, 0);
+	if (IS_ERR(observe_file)) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "create data_dump file err:%ld\n", IS_ERR(observe_file));
+		observe_file = NULL;
+	}
+#endif
+
+	while (code_length) {
+
+#if 1
+		ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ);
+		if (ret) {
+			goto err2;
+		} else {
+			bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes salve rx ready %d bytes\n", tx_size);
+		}
+#endif
+#ifdef BES_SLAVE_RX_DOUBLE_CHECK
+		tx_size = 512;
+#endif
+
+		if ((tx_size < 4) || (tx_size % 4)) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d tx size=%d\n", __func__, __LINE__, tx_size);
+			ret = -203;
+			goto err2;
+		}
+
+		if ((code_length + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)) < tx_size) {
+			length = code_length + sizeof(struct download_fw_t);
+		} else {
+			length = tx_size - sizeof(struct fw_msg_hdr_t);
+		}
+
+#if 0 // for SDIO_USE_V2
+		if (length + sizeof(struct fw_msg_hdr_t) > func->cur_blksize) {
+			length = (length + sizeof(struct fw_msg_hdr_t)) / func->cur_blksize * func->cur_blksize;
+			length -= sizeof(struct fw_msg_hdr_t);
+		}
+#endif
+
+		header.type = FRAME_HEADER_DOWNLOAD_DATA;
+		header.seq = frame_num;
+		header.len = length;
+		last_frame_num = frame_num;
+		frame_num++;
+
+		memcpy(long_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+		memcpy(long_buf + sizeof(struct fw_msg_hdr_t), &download_addr.addr, sizeof(struct download_fw_t));
+		length -= sizeof(struct download_fw_t);//real data length
+		memcpy(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t), data_p, length);
+
+		length += (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
+
+		//mdelay(5000);
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "tx_download_firmware_data:%x %d\n", download_addr.addr, length);
+
+#ifdef DATA_DUMP_OBSERVE
+		if (observe_file) {
+			observe = (char *)(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
+			observe_len = length - sizeof(struct fw_msg_hdr_t) - sizeof(struct download_fw_t);
+			old_fs = get_fs();
+			set_fs(KERNEL_DS);
+			vfs_write(observe_file, observe, observe_len, &observe_off);
+			set_fs(old_fs);
+		}
+#endif
+
+		ret = bes2600_data_write(long_buf, length > 512 ? length : 512);
+		if (ret) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "tx download fw data err:%d\n", ret);
+			goto err2;
+		}
+		length -= (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
+
+#if 1
+		ret = bes_slave_tx_ready(fw_data, &rx_size, HZ);
+		if (!ret) {
+			bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size);
+		} else {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret);
+			goto err2;
+		}
+#ifdef BES_SLAVE_TX_DOUBLE_CHECK
+	if (rx_size != 8)
+		rx_size = 8;
+#endif
+#else
+		mdelay(100);
+		rx_size = 8;
+#endif
+
+		ret = bes2600_data_read(short_buf, rx_size);
+		if (ret) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "rx tx download fw data rsp err:%d\n", ret);
+			goto err2;
+		}
+
+		//check device rx status
+		ret = bes_frame_rsp_check(short_buf, last_frame_num);
+		if (ret) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "rsp tx download fw err:%d\n", ret);
+			goto err2;
+		}
+
+		code_length -= length;
+		data_p += length;
+		download_addr.addr += length;
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "already tx fw size:%x/%x\n", download_addr.addr - fw_info.addr, fw_info.len);
+	}
+
+	//Notify Device:The firmware download is complete
+
+#if 1
+	ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ);
+	if (ret) {
+		goto err2;
+	} else {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes salve rx ready %d bytes\n", tx_size);
+	}
+#endif
+#ifdef BES_SLAVE_RX_DOUBLE_CHECK
+		tx_size = 512;
+#endif
+
+	header.type = FRAME_HEADER_DOWNLOAD_END;
+	header.seq = frame_num;
+	header.len = sizeof(struct fw_crc_t);
+	last_frame_num = frame_num;
+	frame_num++;
+
+	memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&crc32_t.crc32, sizeof(struct fw_crc_t));
+	length = BES_FW_MSG_TOTAL_LEN(header);
+
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s", "tx download firmware complete command\n");
+
+	length = length > 512 ? length : 512;
+	ret = bes2600_data_write(short_buf, length);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "tx downlod firmware complete command err:%d\n", ret);
+		goto err2;
+	}
+
+#if 1
+	ret = bes_slave_tx_ready(fw_data, &rx_size, HZ);
+	if (!ret) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size);
+	} else {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret);
+		goto err2;
+	}
+#ifdef BES_SLAVE_TX_DOUBLE_CHECK
+	if (rx_size != 8)
+		rx_size = 8;
+#endif
+#else
+	mdelay(100);
+	rx_size = 8;
+	bes2600_info(BES2600_DBG_DOWNLOAD, "enter sdio irqs:%d", enter_sdio_irqs);
+#endif
+
+	ret = bes2600_data_read(short_buf, rx_size);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "receive download firmware complete cmd rsp err:%d\n", ret);
+		goto err2;
+	}
+
+	//check device rx status
+	ret = bes_frame_rsp_check(short_buf, last_frame_num);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware complete err:%d\n", ret);
+		goto err2;
+	}
+
+	if (auto_run == false) {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "partial firmware(%s) is downloaded successfully\n", fw_name);
+		goto err2;
+	}
+
+#if 1
+	ret = bes_slave_rx_ready(fw_data, &buf_cnt, &tx_size, HZ);
+	if (ret) {
+		goto err2;
+	} else {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes salve rx ready %d bytes\n", tx_size);
+	}
+#endif
+#ifdef BES_SLAVE_RX_DOUBLE_CHECK
+	tx_size = 512;
+#endif
+
+	//Notify Device:Run firmware
+	run_addr.addr = fw_info.addr;
+
+	header.type = FRAME_HEADER_RUN_CODE;
+	header.seq = frame_num;
+	header.len = sizeof(struct run_fw_t);
+	last_frame_num = frame_num;
+	frame_num++;
+
+	memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&run_addr.addr, sizeof(struct run_fw_t));
+	length = BES_FW_MSG_TOTAL_LEN(header);
+
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "tx run firmware command:0x%X\n", run_addr.addr);
+
+	length = length > 512 ? length : 512;
+	ret = bes2600_data_write(short_buf, length);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "tx run firmware command err:%d\n", ret);
+		goto err2;
+	}
+
+#if 1
+	ret = bes_slave_tx_ready(fw_data, &rx_size, HZ);
+	if (!ret) {
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes_slave ready tx %d bytes\n", rx_size);
+	} else {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process run fw cmd err:%d\n", ret);
+		goto err2;
+	}
+#ifdef BES_SLAVE_TX_DOUBLE_CHECK
+	if (rx_size != 8)
+		rx_size = 8;
+#endif
+#else
+	mdelay(100);
+	rx_size = 8;
+#endif
+
+	ret = bes2600_data_read(short_buf, rx_size);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rx run firmware command err:%d\n", ret);
+		goto err2;
+	}
+
+	//check device rx status
+	ret = bes_frame_rsp_check(short_buf, last_frame_num);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rsp run firmware command err:%d\n", ret);
+		goto err2;
+	}
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "%s", "firmware is downloaded successfully and is already running\n");
+	msleep(500);
+
+err2:
+	kfree(long_buf);
+#ifdef DATA_DUMP_OBSERVE
+	if (observe_file) {
+		filp_close(observe_file, NULL);
+	}
+#endif
+err1:
+	kfree(short_buf);
+#ifndef BES_FW_BUILTIN
+#ifdef CONFIG_FW_LOADER
+	release_firmware(fw_bin);
+#else
+free_mem:
+	kfree(fw_bin->data);
+close_fp:
+	filp_close(my_fwp, NULL);
+#endif
+#endif
+	if (ret && retry_cnt < 3) {
+		retry_cnt++;
+		goto retry;
+	}
+	return ret;
+}
+
+static int bes_read_dpd_data(struct platform_fw_t *fw_data)
+{
+	u16 dpd_size = 0;
+	int ret = 0;
+	u8 *dpd_buf = NULL;
+	u8 mcu_status = 0;
+	unsigned long wait_timeout;
+
+	/* wait for device ready */
+	wait_timeout = jiffies + 15 * HZ;
+	do {
+		msleep(100);
+		ret = bes2600_reg_read(BES_SLAVE_STATUS_REG_ID, &mcu_status, 1);
+	} while(((ret == 0) || (ret == -84)) &&
+	        !(mcu_status & BES_SLAVE_STATUS_DPD_READY) &&
+		time_before(jiffies, wait_timeout));
+
+	/* check if read dpd error */
+	if(ret < 0 || time_after(jiffies, wait_timeout)) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait dpd data ready failed:%d\n", ret);
+		return -1;
+	}
+
+	/* wait dpd read ready */
+	ret = bes_slave_tx_ready(fw_data, &dpd_size, HZ);
+	if (ret)  {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait dpd data failed:%d\n", ret);
+		return -1;
+	}
+
+	/* dpd size check */
+	if (dpd_size != DPD_BIN_SIZE) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "get dpd data size err:%u\n", dpd_size);
+		return -1;
+	}
+
+	/* read dpd data */
+	dpd_buf = bes2600_chrdev_get_dpd_buffer(DPD_BIN_FILE_SIZE);
+	if(!dpd_buf) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "allocate dpd buffer failed.\n");
+		return -1;
+	}
+
+	ret = bes2600_data_read(dpd_buf, dpd_size);
+	bes2600_info(BES2600_DBG_DOWNLOAD, "read dpd data size:%d\n", dpd_size);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "read dpd data failed:%d\n", ret);
+		bes2600_chrdev_free_dpd_data();
+		return -1;
+	}
+
+	/* update dpd data */
+	ret = bes2600_chrdev_update_dpd_data();
+
+	return ret;
+}
+
+#ifdef BES2600_DUMP_FW_DPD_LOG
+static int bes_read_dpd_log(struct platform_fw_t *fw_data)
+{
+	u16 dpd_log_size = 0;
+	int ret = 0;
+	u8 mcu_status = 0;
+	u8 *dpd_log = NULL;
+	unsigned long wait_timeout;
+
+	/* wait for device ready */
+	wait_timeout = jiffies + 5 * HZ;
+	do {
+		msleep(10);
+		ret = bes2600_reg_read(BES_SLAVE_STATUS_REG_ID, &mcu_status, 1);
+	} while(((ret == 0) || (ret == -84)) &&
+	        !(mcu_status & BES_SLAVE_STATUS_DPD_LOG_READY) &&
+		time_before(jiffies, wait_timeout));
+
+	if(ret < 0 || time_after(jiffies, wait_timeout)) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait dpd log ready failed:%d\n", ret);
+		return -1;
+	}
+
+	/* wait dpd log dump data ready */
+	ret = bes_slave_tx_ready(fw_data, &dpd_log_size, HZ);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait dpd log failed:%d\n", ret);
+		return -1;
+	}
+
+	dpd_log = bes2600_alloc_dpd_log_buffer((dpd_log_size + 3) & (~0x3));
+	if(!dpd_log) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "dpd log buffer alloc fail");
+		return -1;
+	}
+
+	ret = bes2600_data_read(dpd_log, (dpd_log_size + 3) & (~0x3));
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "read dpd log failed:%d\n", ret);
+		bes2600_free_dpd_log_buffer();
+		return -1;
+	}
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "read dpd log size: %u\n", dpd_log_size);
+
+	return ret;
+}
+#endif /* BES2600_DUMP_FW_DPD_LOG */
+
+static int bes2600_load_wifi_firmware(struct platform_fw_t *fw_data)
+{
+	int ret = 0;
+	const char *fw_name_tbl[3];
+	int fw_type = bes2600_chrdev_get_fw_type();
+
+	fw_name_tbl[0] = BES2600_LOAD_FW_NAME;
+	fw_name_tbl[1] = BES2600_LOAD_NOSIGNAL_FW_NAME;
+	fw_name_tbl[2] = BES2600_LOAD_BTRF_FW_NAME;
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 download cali and wifi signal firmware.\n");
+	ret = bes_firmware_download(fw_data, BES2600_LOAD_BOOT_NAME, true);
+	bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download dpd cali firmware failed\n");
+
+	if (!ret) {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 read dpd cali data.\n");
+		ret = bes_read_dpd_data(fw_data);
+		bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "read dpd data failed.\n");
+	}
+
+#ifdef BES2600_DUMP_FW_DPD_LOG
+	if (!ret) {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 read dpd log data.\n");
+		ret = bes_read_dpd_log(fw_data);
+		bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "dump dpd log failed.\n");
+	}
+#endif
+
+	/* for wifi non-signal mode, download second firmware directly */
+	if (!ret && bes2600_chrdev_check_system_close()) {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 device power down.\n");
+		ret = bes2600_chrdev_do_system_close(fw_data->sbus_ops, fw_data->sbus_priv);
+		bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "device down fail.\n");
+	} else if (!ret) {
+		ret = bes_firmware_download(fw_data, fw_name_tbl[fw_type], true);
+		bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download normal firmware failed.\n");
+	}
+
+	return ret;
+}
+
+static int bes2600_load_wifi_firmware_with_dpd(struct platform_fw_t *fw_data)
+{
+	int ret = 0;
+	u32 dpd_data_len = 0;
+	const u8 *dpd_data = NULL;
+	const char *fw_name_tbl[3];
+	int fw_type = bes2600_chrdev_get_fw_type();
+
+	fw_name_tbl[0] = BES2600_LOAD_FW_NAME;
+	fw_name_tbl[1] = BES2600_LOAD_NOSIGNAL_FW_NAME;
+	fw_name_tbl[2] = BES2600_LOAD_BTRF_FW_NAME;
+
+	dpd_data = bes2600_chrdev_get_dpd_data(&dpd_data_len);
+	BUG_ON(!dpd_data);
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 download firmware with dpd.\n");
+	ret = bes_firmware_download_write_mem(fw_data, BES2600_DPD_ADDR, dpd_data, dpd_data_len);
+	bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download dpd data failed.\n");
+	if (!ret) {
+		ret = bes_firmware_download(fw_data, fw_name_tbl[fw_type], true);
+		bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download firmware failed after dpd download.\n");
+	}
+
+	return ret;
+}
+
+static int bes2600_load_bt_firmware(struct platform_fw_t *fw_data)
+{
+	int ret = 0;
+
+	/* for bt mode, don't need to download dpd cali firmware*/
+	bes2600_info(BES2600_DBG_DOWNLOAD, "download bt test firmware.\n");
+	ret = bes_firmware_download(fw_data, BES2600_LOAD_BTRF_FW_NAME, true);
+	bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download normal firmware failed.\n");
+
+	return ret;
+
+}
+
+int bes2600_load_firmware_sdio(struct sbus_ops *ops, struct sbus_priv *priv)
+{
+	int ret = 0;
+	struct platform_fw_t *temp_fw_data;
+	u32 dpd_data_len = 0;
+	const u8 *dpd_data = NULL;
+	int fw_type = bes2600_chrdev_get_fw_type();
+#ifdef CONFIG_BES2600_CALIB_FROM_LINUX
+	u8 *factory_data = NULL;
+	u8 *file_buffer = NULL;
+	u32 factory_data_len = 0;
+#endif
+
+	temp_fw_data = kzalloc(sizeof(struct platform_fw_t), GFP_KERNEL);
+	if (!temp_fw_data)
+		return -ENOMEM;
+
+	init_completion(&temp_fw_data->completion_data);
+	temp_fw_data->sbus_ops = ops;
+	temp_fw_data->sbus_priv = priv;
+
+	temp_fw_data->sbus_ops->irq_subscribe(temp_fw_data->sbus_priv,
+			(sbus_irq_handler)bes_fw_irq_handler, temp_fw_data);
+
+	bes_firmware_download_write_reg(temp_fw_data, 0x40100000, 0x802006);
+	bes_firmware_download_write_reg(temp_fw_data, 0x4008602C, 0x3E00C000);
+
+#ifdef CONFIG_BES2600_CALIB_FROM_LINUX
+	if (!(file_buffer = bes2600_factory_get_file_buffer()))
+		return -ENOMEM;
+
+	bes2600_factory_lock();
+#ifdef FACTORY_SAVE_MULTI_PATH
+	if (!(factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_PATH)) &&
+		!(factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_DEFAULT_PATH))) {
+#else
+	if (!(factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_PATH))) {
+#endif
+		bes2600_warn(BES2600_DBG_DOWNLOAD, "factory cali data get failed.\n");
+	} else {
+		bes2600_factory_data_check(factory_data);
+		factory_little_endian_cvrt(factory_data);
+		ret = bes_firmware_download_write_mem(temp_fw_data, BES2600_FACTORY_ADDR, factory_data, factory_data_len);
+		bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download factory data failed.\n");
+	}
+
+	bes2600_factory_free_file_buffer(file_buffer);
+	bes2600_factory_unlock();
+#endif
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "%s fw_type:%d.\n", __func__, fw_type);
+	if(fw_type == BES2600_FW_TYPE_BT) {
+		ret = bes2600_load_bt_firmware(temp_fw_data);
+	} else {
+		dpd_data = bes2600_chrdev_get_dpd_data(&dpd_data_len);
+		if(dpd_data) {
+			ret = bes2600_load_wifi_firmware_with_dpd(temp_fw_data);
+		} else {
+			ret = bes2600_load_wifi_firmware(temp_fw_data);
+		}
+	}
+
+
+	/* don't register net device when wifi is closed */
+	if(!ret && !bes2600_chrdev_is_wifi_opened()) {
+		ret = 1;
+	}
+
+
+	temp_fw_data->sbus_ops->irq_unsubscribe(temp_fw_data->sbus_priv);
+	kfree(temp_fw_data);
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "download finished ,wifi_state:%d result is %d\n", bes2600_chrdev_is_wifi_opened(), ret);
+
+	return ret;
+}
+#endif
+
+#if defined(BES2600_BOOT_UART_TO_SDIO)
+static struct uart_dld_work_t {
+	struct work_struct dld_work;
+	struct completion completion_data;
+	void *priv;
+} uart_dld_data;
+
+void bes2600_load_firmware_uart_work(struct work_struct *work)
+{
+	int ret;
+#if defined(BES2600_LOAD_FW_TOOL_PATH) && defined(BES2600_LOAD_FW_TOOL_DEVICE) && defined(BES2600_LOAD_BOOT_PATCH_NAME)
+	char cmd_path[] = BES2600_LOAD_FW_TOOL_PATH;
+	char *cmd_argv[] = {cmd_path, BES2600_LOAD_FW_TOOL_DEVICE, BES2600_LOAD_BOOT_PATCH_NAME, NULL};
+	char *cmd_envp[] = {"HOME=/", "PATH=/sbin:/bin:/user/bin:/system/bin:/usr/bin", NULL};
+#else
+	#error "BES uart download should specify fimrware load tool"
+#endif
+	complete(&uart_dld_data.completion_data);
+	if ((ret = call_usermodehelper(cmd_path, cmd_argv, cmd_envp, UMH_WAIT_PROC))) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "call_usermodehelper failed:%d\n", ret);
+	} else {
+		msleep(200);
+		complete(&uart_dld_data.completion_data);
+	}
+}
+
+int bes2600_boot_uart_to_sdio(struct sbus_ops *ops)
+{
+	int ret, retry = 0;
+
+uart_dld:
+	memset(&uart_dld_data, 0, sizeof(uart_dld_data));
+	init_completion(&uart_dld_data.completion_data);
+	INIT_WORK(&uart_dld_data.dld_work, bes2600_load_firmware_uart_work);
+	schedule_work(&uart_dld_data.dld_work);
+	ret = wait_for_completion_interruptible_timeout(&uart_dld_data.completion_data, HZ);
+	if (ret <= 0) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s uart dld boot patch start failed:%d\n",
+				__func__, ret);
+		return -110;
+	}
+
+	if (ops->reset)
+		ops->reset(NULL);
+
+	ret = wait_for_completion_interruptible_timeout(&uart_dld_data.completion_data, 10 * HZ);
+	if (ret <= 0) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s uart dld boot patch end failed:%d(%d)\n",
+				__func__, ret, retry);
+		cancel_work_sync(&uart_dld_data.dld_work);
+		retry++;
+		if (retry < 10)
+			goto uart_dld;
+		else
+			return -110;
+	} else {
+		return 0;
+	}
+
+}
+#endif
+
+#if defined(FW_DOWNLOAD_BY_UART)
+static struct uart_dld_work_t {
+	struct work_struct dld_work;
+	struct completion completion_data;
+	char *fw_name;
+} uart_dld_data;
+
+void bes2600_load_firmware_uart_work(struct work_struct *work)
+{
+	int ret;
+#if defined(BES2600_LOAD_FW_TOOL_PATH) && defined(BES2600_LOAD_FW_TOOL_DEVICE)
+	char cmd_path[] = BES2600_LOAD_FW_TOOL_PATH;
+	char *cmd_argv[] = {cmd_path, BES2600_LOAD_FW_TOOL_DEVICE, uart_dld_data.fw_name, NULL};
+	char *cmd_envp[] = {"HOME=/", "PATH=/sbin:/bin:/user/bin:/system/bin:/usr/bin", NULL};
+#else
+	#error "BES uart download should specify fimrware load tool"
+#endif
+	complete(&uart_dld_data.completion_data);
+	if ((ret = call_usermodehelper(cmd_path, cmd_argv, cmd_envp, UMH_WAIT_PROC))) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "call_usermodehelper failed:%d\n", ret);
+	} else {
+		msleep(200);
+		complete(&uart_dld_data.completion_data);
+	}
+}
+
+#ifdef FW_DOWNLOAD_UART_DAEMON
+extern int bes2600_load_uevent(char *evn[]);
+#endif
+
+static int bes2600_load_firmware_uart_wrapper(struct sbus_ops *ops, char *name)
+{
+	int ret = 0;
+#ifdef FW_DOWNLOAD_UART_DAEMON
+	char driver[] = "DRIVER=bes2600_wlan";
+	char vendor[100] = "VENDOR_DESC=";
+	char *env[] = {driver, vendor, NULL};
+#endif
+	memset(&uart_dld_data, 0, sizeof(uart_dld_data));
+	init_completion(&uart_dld_data.completion_data);
+#ifndef FW_DOWNLOAD_UART_DAEMON
+	uart_dld_data.fw_name = name;
+#else
+	memcpy(vendor + strlen(vendor), name, strlen(name) + 1);
+#endif
+	INIT_WORK(&uart_dld_data.dld_work, bes2600_load_firmware_uart_work);
+
+#ifndef FW_DOWNLOAD_UART_DAEMON
+	schedule_work(&uart_dld_data.dld_work);
+	ret = wait_for_completion_interruptible_timeout(&uart_dld_data.completion_data, HZ);
+	if (ret <= 0) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s uart dld firmware start failed:%d\n",
+				__func__, ret);
+		return -110;
+	}
+
+	if (ops->reset) {
+		ops->reset(NULL);
+	}
+
+	ret = wait_for_completion_interruptible_timeout(&uart_dld_data.completion_data, 10 * HZ);
+	if (ret <= 0) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s uart dld firmware end failed:%d\n",
+				__func__, ret);
+		return -110;
+	} else {
+		return 0;
+	}
+
+#else
+
+	if (ops->reset) {
+		ops->reset(NULL);
+	}
+
+	if ((ret = bes2600_load_uevent(env))) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s uart dld firmware start failed:%d\n",
+				__func__, ret);
+	}
+	return ret;
+#endif
+}
+
+static int bes_read_dpd_data(struct sbus_ops *ops, struct sbus_priv *priv)
+{
+	int ret = 0, i;
+	u32 dpd_data_len, dpd_ready = 0;
+	u8 *dpd_data;
+	u32 *temp;
+	unsigned long dpd_started = jiffies;
+
+	do {
+		bes2600_reg_read(BES_SLAVE_STATUS_REG_ID, &dpd_ready, 4);
+		if (dpd_ready & BES_SLAVE_STATUS_WIFI_CALI_READY)
+			break;
+		else
+			msleep(1000);
+	} while (time_before(jiffies, dpd_started + 10 * HZ));
+	if (!(dpd_ready & BES_SLAVE_STATUS_WIFI_CALI_READY)) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait wifi cali timeout(%x)\n", dpd_ready);
+		return -110;
+	}
+
+	if ( (ret = bes2600_reg_read(BES_TX_CTRL_REG_ID, &dpd_data_len, 4))) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s get dpd data len failed(%d)\n", __func__, dpd_data_len);
+		goto exit;
+	}
+
+	dpd_data = bes2600_chrdev_get_dpd_buffer(dpd_data_len);
+	if (!dpd_data) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	ops->lock(priv);
+	ret = ops->sbus_memcpy_fromio(priv, BES_CALI_DATA_ADDR, dpd_data, dpd_data_len);
+	ops->unlock(priv);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s get dpd data failed(%d)\n", __func__, ret);
+		goto free_dpd;
+	} else {
+		temp = (u32 *)dpd_data;
+		for (i = 0; i < (dpd_data_len >> 2); i++) {
+			temp[i] = swab32(temp[i]);
+		}
+		ret = bes2600_chrdev_update_dpd_data();
+	}
+	return ret;
+free_dpd:
+	bes2600_chrdev_free_dpd_data();
+exit:
+	return ret;
+}
+
+static int bes_write_dpd_data(struct sbus_ops *ops, struct sbus_priv *priv)
+{
+	int ret;
+	u32 dpd_data_len = 0, cfg = BES_DLD_DPD_DATA_DONE;
+	const u8 *dpd_data = NULL;
+
+	dpd_data = bes2600_chrdev_get_dpd_data(&dpd_data_len);
+	BUG_ON(!dpd_data);
+
+	ops->lock(priv);
+	ret = ops->sbus_memcpy_toio(priv, BES_CALI_DATA_ADDR, dpd_data, dpd_data_len);
+	ops->unlock(priv);
+	if (ret) {
+		bes2600_err(BES2600_DBG_SPI, "%s rewrite dpd data failed:%d\n", __func__, ret);
+		return ret;
+	} else {
+		bes2600_err(BES2600_DBG_SPI, "%s rewrite dpd data success\n", __func__);
+	}
+	return bes2600_reg_write(BES_HOST_SUBINT_REG_ID, &cfg, 4);
+}
+
+#ifdef CONFIG_BES2600_CALIB_FROM_LINUX
+static int bes_write_factory_data(struct sbus_ops *ops, struct sbus_priv *priv)
+{
+	int ret = 0;
+	u32 factory_data_len = 0, cfg = BES_DLD_FACTORY_DATA_DONE;
+	u8 *factory_data = NULL;
+	u8 *file_buffer = NULL;
+
+	if (!(file_buffer = bes2600_factory_get_file_buffer()))
+		return -ENOMEM;
+
+	bes2600_factory_lock();
+	factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_PATH);
+	if (!factory_data) {
+		bes2600_warn(BES2600_DBG_DOWNLOAD, "factory cali data get failed.\n");
+	} else {
+		bes2600_factory_data_check(factory_data);
+		factory_little_endian_cvrt(factory_data);
+		ops->lock(priv);
+		ret = ops->sbus_memcpy_toio(priv, BES_FACTORY_DATA_ADDR, factory_data, factory_data_len);
+		ops->unlock(priv);
+		if (ret) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "download factory data failed(%d)\n", ret);
+			return ret;
+		} else {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "download factory data success:%x\n", *(u32 *)factory_data);
+		}
+	}
+	bes2600_factory_free_file_buffer(file_buffer);
+	bes2600_factory_unlock();
+
+	return bes2600_reg_write(BES_HOST_SUBINT_REG_ID, &cfg, 4);
+}
+#endif /* CONFIG_BES2600_CALIB_FROM_LINUX */
+
+static int bes_slave_sync(struct sbus_ops *ops, struct sbus_priv *priv)
+{
+	u32 sync_header;
+	u32 cfg = SPI_CONTINUOUS_CFG_VAL;
+	unsigned long sync_started = jiffies;
+
+	do {
+		bes2600_reg_read(BES_HOST_SYNC_REG_ID, &sync_header, 4);
+		if (sync_header != BES_SLAVE_SYNC_HEADER)
+			msleep(100);
+		else
+			break;
+	} while (time_before(jiffies, sync_started + 5 * HZ));
+
+	if (sync_header != BES_SLAVE_SYNC_HEADER) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "spi slave sync failed(%x)\n", sync_header);
+		return -110;
+	}
+
+	return bes2600_reg_write(SPI_RD_CFG_REG_ID, &cfg, 4);
+}
+
+int bes2600_load_firmware_uart(struct sbus_ops *ops, struct sbus_priv *priv)
+{
+	int ret;
+	u32 dpd_data_len = 0;
+	const u8 *dpd_data = NULL;
+	int fw_type = bes2600_chrdev_get_fw_type();
+	char *fw_name;
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "%s fw_type:%d.\n", __func__, fw_type);
+
+	if (fw_type == BES2600_FW_TYPE_BT) {
+		ret = bes2600_load_firmware_uart_wrapper(ops, BES2600_LOAD_BTRF_FW_NAME);
+	} else {
+reload:
+		dpd_data = bes2600_chrdev_get_dpd_data(&dpd_data_len);
+		if (!dpd_data) {
+			bes2600_info(BES2600_DBG_DOWNLOAD, "%s boot bin\n", __func__);
+			ret = bes2600_load_firmware_uart_wrapper(ops, BES2600_LOAD_BOOT_NAME);
+			if (ret) {
+				bes2600_err(BES2600_DBG_DOWNLOAD, "%s download boot failed:%d\n", __func__, ret);
+				goto exit;
+			}
+
+			if (ops->init)
+				ops->init(priv, NULL);
+
+			if ((ret = bes_slave_sync(ops, priv)))
+				goto exit;
+
+			if ((ret = bes_read_dpd_data(ops, priv)))
+				goto exit;
+
+			/* wifi nosignal: load best2002_fw_spi_nosignal.bin */
+			if (fw_type == BES2600_FW_TYPE_WIFI_NO_SIGNAL)
+				goto reload;
+
+			/* normal: judge whether load best2002_fw_spi.bin or not*/
+			if (!bes2600_chrdev_is_wifi_opened())
+				ret = 1;
+			else
+				goto reload;
+		} else {
+			fw_name = (fw_type == BES2600_FW_TYPE_WIFI_SIGNAL) ? BES2600_LOAD_FW_NAME : BES2600_LOAD_NOSIGNAL_FW_NAME;
+			if ((ret = bes2600_load_firmware_uart_wrapper(ops, fw_name))) {
+				bes2600_err(BES2600_DBG_SPI, "%s download normal fw failed:%d\n", __func__, ret);
+				goto exit;
+			}
+
+			if ((ret = bes_slave_sync(ops, priv)))
+				goto exit;
+
+#ifdef CONFIG_BES2600_CALIB_FROM_LINUX
+			if ((ret = bes_write_factory_data(ops, priv)))
+				goto exit;
+#endif
+			if ((ret = bes_write_dpd_data(ops, priv)))
+				goto exit;
+		}
+	}
+
+exit:
+	return ret;
+}
+#endif
diff --git a/drivers/staging/bes2600/bes_fw_common.c b/drivers/staging/bes2600/bes_fw_common.c
new file mode 100644
index 000000000000..5b79cdfe85f7
--- /dev/null
+++ b/drivers/staging/bes2600/bes_fw_common.c
@@ -0,0 +1,125 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "bes_fw_common.h"
+
+//#define BES_CRC32_DOUBLE_CHECK
+#ifdef BES_CRC32_DOUBLE_CHECK
+extern u32 bes_crc32_c(u32 crc, const u8 *data, u32 data_len);
+#endif
+
+#if defined(FW_DOWNLOAD_BY_SDIO) || defined(FW_DOWNLOAD_BY_USB)
+void bes_parse_fw_info(const u8 *data, u32 data_len, u32 *load_addr, u32 *crc32)
+{
+	u8 buffer[16];
+	struct exec_struct_t exec_struct;
+	u32 exec_addr_last4byte;
+	u32 crc_le = 0;
+#ifdef BES_CRC32_DOUBLE_CHECK
+	u32 crc_bes = 0;
+	u32 crc_be = 0;
+#endif
+	crc_le = crc32_le(0xffffffffL, (u8 *)data, data_len - CODE_DATA_USELESS_SIZE);
+	crc_le ^= 0xffffffffL;
+
+#ifdef BES_CRC32_DOUBLE_CHECK
+	crc_be = crc32_be(0xffffffffL, (u8 *)data, data_len - CODE_DATA_USELESS_SIZE);
+	crc_be ^= 0xffffffffL;
+	crc_bes = bes_crc32_c(crc_bes, (u8 *)data, data_len - CODE_DATA_USELESS_SIZE);
+#endif
+
+	//read entry,param,sp,exec_addr
+	memcpy((u8 *)buffer, (u8 *)data, sizeof(exec_struct));
+	exec_struct.entry       = ((struct exec_struct_t *)buffer)->entry;//PC
+	exec_struct.param       = ((struct exec_struct_t *)buffer)->param;
+	exec_struct.sp          = ((struct exec_struct_t *)buffer)->sp;
+	exec_struct.exec_addr   = ((struct exec_struct_t *)buffer)->exec_addr;//load addr
+
+
+#ifdef BES_CRC32_DOUBLE_CHECK
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "crc32 %x(le) %x(be) %x(bes)\n", crc_le, crc_be, crc_bes);
+#else
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "crc32                :0x%08X\n", crc_le);
+#endif
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_struct.entry    :0x%08X\n", exec_struct.entry);
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_struct.param    :0x%08X\n", exec_struct.param);
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_struct.sp       :0x%08X\n", exec_struct.sp);
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_struct.exec_addr:0x%08X\n", exec_struct.exec_addr);
+
+	exec_addr_last4byte = (*((u32 *)(data + data_len - 4)));
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_addr_last4byte  :0x%08X\n", exec_addr_last4byte);
+	if ((!exec_struct.exec_addr) || (exec_struct.exec_addr != exec_addr_last4byte && exec_addr_last4byte)) {
+		exec_struct.exec_addr = exec_addr_last4byte;
+		bes2600_dbg(BES2600_DBG_DOWNLOAD, "exec_addr_last4byte covered exec_struct.exec_addr\n");
+	}
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "final exec_struct.exec_addr:0x%08X\n", exec_struct.exec_addr);
+
+	*load_addr = exec_struct.exec_addr;
+
+#ifndef BES_CRC32_DOUBLE_CHECK
+	*crc32 = crc_le;
+#else
+	*crc32 = crc_bes;
+#endif
+}
+
+int bes_frame_rsp_check(void *rsp, u8 frame_num)
+{
+	int ret = 0;
+	struct frame_struct_t *pframe = (struct frame_struct_t *)rsp;
+	if (pframe->type == FRAME_HEADER_REPLY) {
+		if (pframe->frame_num == frame_num) {
+			if (pframe->len == 4) {
+				if (pframe->payload == ERR_NONE) {
+					bes2600_dbg(BES2600_DBG_DOWNLOAD, "bes slave  download firmware is ready\n");
+				} else {
+					bes2600_err(BES2600_DBG_DOWNLOAD, "frame payload=0x%x\n", pframe->payload);
+					ret = -200;
+				}
+			} else {
+				bes2600_err(BES2600_DBG_DOWNLOAD, "payload len error:%u\n", pframe->len);
+				ret = -201;
+			}
+		} else {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "frame num err. 0x%x != 0x%x. len:%u\n",
+				pframe->frame_num, frame_num, pframe->len);
+			ret = -202;
+		}
+	} else {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "frame type err. type 0x%x num=0x%x(0x%x), len:%u\n",
+			pframe->type, pframe->frame_num, frame_num, pframe->len);
+		ret = -203;
+	}
+	return ret;
+}
+
+const u8* bes2600_get_firmware_version_info(const u8 *data, u32 count)
+{
+        int i = 0;
+        const u8 *tmp_ptr = NULL;
+        const char month[12][4] = {
+                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+        };
+
+        if(!data || count < 4)
+                return NULL;
+
+        for(tmp_ptr = data + count - 3; tmp_ptr > data; tmp_ptr -= 1) {
+                for(i = 0; i < 12; i++) {
+                        if(memcmp(tmp_ptr, month[i], 3) == 0) {
+                                return tmp_ptr;
+                        }
+                }
+        }
+
+        return NULL;
+}
+#endif
\ No newline at end of file
diff --git a/drivers/staging/bes2600/bes_fw_common.h b/drivers/staging/bes2600/bes_fw_common.h
new file mode 100644
index 000000000000..05251aab410f
--- /dev/null
+++ b/drivers/staging/bes2600/bes_fw_common.h
@@ -0,0 +1,103 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __BES_FW_COMMON_H__
+#define __BES_FW_COMMON_H__
+
+#include <linux/types.h>
+#include <linux/printk.h>
+#include <linux/string.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/wait.h>
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/version.h>
+#include "bes2600.h"
+
+/***** firmware macro *****/
+#define BUF_SIZE        49152
+#define RETRY_CNT_MAX   3
+#define TIMEOUT_TIME    20
+#define FRAME_HEADER_SIZE           0x04
+#define CODE_DATA_USELESS_SIZE      0x04
+/****frame header code****/
+#define FRAME_HEADER_REPLY          0xB0
+#define FRAME_HEADER_DOWNLOAD_INFO  0xB1
+#define FRAME_HEADER_DOWNLOAD_DATA  0xB2
+#define FRAME_HEADER_DOWNLOAD_END   0xB3
+#define FRAME_HEADER_RUN_CODE       0xB4
+
+/****frame length get****/
+#define BES_FW_MSG_TOTAL_LEN(msg)  (sizeof(struct fw_msg_hdr_t) + ((struct fw_msg_hdr_t )(msg)).len)
+
+/****frame length get****/
+#define BES2600_DPD_ADDR	0x2008C000
+#define BES2600_FACTORY_ADDR	0x2008B000
+
+/***** bes fw error code *****/
+enum ERR_CODE {
+	ERR_NONE = 0x00,
+	ERR_LEN = 0x01,
+};
+
+/***** data struct *****/
+struct frame_struct_t {
+    u8 type;
+    u8 frame_num;
+    u16 len;
+    u32 payload;
+};
+
+struct fw_msg_hdr_t {
+    u8 type;
+    u8 seq;
+    u16 len;
+};
+
+struct fw_msg_replay_t {
+    u32 replay;
+};
+
+struct fw_info_t {
+    u32 len;
+    u32 addr;
+};
+
+struct download_fw_t {
+    u32 addr;
+    u8 data[0];
+};
+
+struct fw_crc_t {
+    u32 crc32;
+};
+
+struct run_fw_t {
+    u32 addr;
+};
+
+struct exec_struct_t {
+	u32 entry;
+	u32 param;
+	u32 sp;
+	u32 exec_addr;
+};
+
+#if defined(FW_DOWNLOAD_BY_SDIO) || defined(FW_DOWNLOAD_BY_USB)
+void bes_parse_fw_info(const u8 *data, u32 data_len, u32 *load_addr, u32 *crc32);
+int bes_frame_rsp_check(void *rsp, u8 frame_num);
+const u8* bes2600_get_firmware_version_info(const u8 *data, u32 count);
+#endif
+
+#endif
\ No newline at end of file
diff --git a/drivers/staging/bes2600/bes_fw_usb.c b/drivers/staging/bes2600/bes_fw_usb.c
new file mode 100644
index 000000000000..1685bbc4ad24
--- /dev/null
+++ b/drivers/staging/bes2600/bes_fw_usb.c
@@ -0,0 +1,412 @@
+/*
+* Mac80211 driver for BES2600 device
+*
+* Copyright (c) 2022, Bestechnic
+* Author:
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*/
+#include "bes_fw_common.h"
+#include "bes2600.h"
+#include "hwio.h"
+#include "sbus.h"
+#include "bes2600_driver_mode.h"
+#include <linux/string.h>
+#include "bes_chardev.h"
+struct platform_fw_t {
+	struct completion completion_tx;
+	struct completion completion_rx;
+	const struct sbus_ops *sbus_ops;
+	struct sbus_priv *sbus_priv;
+};
+
+static void bes_fw_irq_handler(void *priv)
+{
+	struct platform_fw_t *fw_data = (struct platform_fw_t *)priv;
+	u32 ctrl_reg, status_reg;
+
+	// read status and control register
+	fw_data->sbus_ops->sbus_reg_read(fw_data->sbus_priv, BES_USB_CONTROL_REG, &ctrl_reg, 1);
+	fw_data->sbus_ops->sbus_reg_read(fw_data->sbus_priv, BES_USB_STATUS_REG, &status_reg, 1);
+	bes2600_dbg(BES2600_DBG_DOWNLOAD, "%s ctrl_reg:0x%08x status_reg:0x%08x\n", __func__, ctrl_reg, status_reg);
+
+	// notify tx done event
+	if((ctrl_reg & BES_USB_FW_TX_DONE) != 0 &&
+	(status_reg & BES_USB_FW_TX_DONE) != 0) {
+		status_reg &= ~BES_USB_FW_TX_DONE;
+		fw_data->sbus_ops->sbus_reg_write(fw_data->sbus_priv, BES_USB_STATUS_REG, &status_reg, 1);
+		complete(&fw_data->completion_tx);
+	}
+
+	// notify rx indication event
+	if((ctrl_reg & BES_USB_FW_RX_INDICATION) != 0 &&
+	(status_reg & BES_USB_FW_RX_INDICATION) != 0) {
+		status_reg &= ~BES_USB_FW_RX_INDICATION;
+		fw_data->sbus_ops->sbus_reg_write(fw_data->sbus_priv, BES_USB_STATUS_REG, &status_reg, 1);
+		complete(&fw_data->completion_rx);
+	}
+}
+
+static int bes_usb_fw_write(struct platform_fw_t *fw_data, u8* data, size_t len)
+{
+	size_t length = 0;
+	int ret = 0;
+	long time_left;
+	u32 control_reg = 0;
+
+	// align data size, makes it suite for usb transfer
+	length = fw_data->sbus_ops->align_size(fw_data->sbus_priv, len);
+
+	// enable tx done notification
+	fw_data->sbus_ops->sbus_reg_read(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1);
+	control_reg |= BES_USB_FW_TX_DONE;
+	fw_data->sbus_ops->sbus_reg_write(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1);
+
+	// send firmware data to device
+	ret = fw_data->sbus_ops->pipe_send(fw_data->sbus_priv, BES2600_USB_PIPE_TX_WLAN, length, data);
+	if(ret < 0) {
+		fw_data->sbus_ops->sbus_reg_read(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1);
+		control_reg &= ~BES_USB_FW_TX_DONE;
+		fw_data->sbus_ops->sbus_reg_write(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1);
+		return ret;
+	}
+
+	// wait for sending done of firmware data
+	time_left = wait_for_completion_interruptible_timeout(&fw_data->completion_tx, 5*HZ);
+	if(time_left == 0)
+		ret = -ETIMEDOUT;
+	else if(time_left < 0)
+		ret = time_left;
+
+	// turn off tx done notification
+	fw_data->sbus_ops->sbus_reg_read(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1);
+	control_reg &= ~BES_USB_FW_TX_DONE;
+	fw_data->sbus_ops->sbus_reg_write(fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1);
+
+	return ret;
+}
+
+static int bes_firmware_download(struct platform_fw_t *fw_data, const char *fw_name, bool auto_run)
+{
+	u8 frame_num = 0;
+	u8 last_frame_num = 0;
+
+	u16 tx_size = 8192;
+
+	u32 length = 0;
+	u32 code_length = 0;
+
+	int ret;
+
+	const u8 *fw_ver_ptr;
+	const u8 *data_p;
+	u8 *short_buf, *long_buf;
+	const struct firmware *fw_bin;
+	struct sk_buff *skb = NULL;
+
+	struct fw_msg_hdr_t header;
+	struct fw_info_t fw_info;
+	struct download_fw_t download_addr;
+	struct fw_crc_t crc32_t;
+	struct run_fw_t run_addr;
+
+	// get firmware data
+	ret = request_firmware(&fw_bin, fw_name, NULL);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "request firmware err:%d\n", ret);
+		return ret;
+	}
+
+	// parse firmware information
+	bes2600_info(BES2600_DBG_DOWNLOAD, "%s fw.size=%ld\n", __func__, fw_bin->size);
+	bes_parse_fw_info(fw_bin->data, fw_bin->size, &fw_info.addr, &crc32_t.crc32);
+	bes2600_info(BES2600_DBG_DOWNLOAD, "------load addr  :0x%08X\n", fw_info.addr);
+	bes2600_info(BES2600_DBG_DOWNLOAD, "------data crc   :0x%08X\n", crc32_t.crc32);
+	code_length = fw_bin->size - CODE_DATA_USELESS_SIZE;
+	bes2600_info(BES2600_DBG_DOWNLOAD, "------code size  :%d\n", code_length);
+
+	fw_ver_ptr = bes2600_get_firmware_version_info(fw_bin->data, fw_bin->size);
+	if(fw_ver_ptr == NULL)
+		bes2600_err(BES2600_DBG_DOWNLOAD, "------Firmware version get failed\n");
+	else
+		bes2600_info(BES2600_DBG_DOWNLOAD, "------Firmware: %s version :%s\n", fw_name ,fw_ver_ptr);
+
+	fw_info.len = code_length;
+	data_p = fw_bin->data;
+
+	// construct download information frame
+	header.type = FRAME_HEADER_DOWNLOAD_INFO;
+	header.seq = frame_num;
+	header.len = sizeof(struct fw_info_t);
+	last_frame_num = frame_num;
+	frame_num++;
+
+	short_buf = kzalloc(512, GFP_KERNEL);
+	if (!short_buf)
+		return -ENOMEM;
+	memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&fw_info, sizeof(struct fw_info_t));
+	length = BES_FW_MSG_TOTAL_LEN(header);
+
+	if (tx_size > length) {
+		bes2600_info(BES2600_DBG_DOWNLOAD, "%s", "tx download firmware info\n");
+	} else {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d bes slave has no enough buffer%d/%d\n", __func__, __LINE__, tx_size, length);
+		goto err1;
+	}
+
+	// send firmware information frame to device and wait tx done
+	ret = bes_usb_fw_write(fw_data, short_buf, length);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "tx download firmware info err:%d\n", ret);
+		goto err1;
+	}
+
+	// wait rx indication
+	ret = wait_for_completion_interruptible_timeout(&fw_data->completion_rx, HZ * 5);
+	if(ret <= 0) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "usb receive download firmware info response timeout or interrupt\n");
+		ret = - ETIMEDOUT;
+		goto err1;
+	}
+
+	// read device response
+	skb = fw_data->sbus_ops->pipe_read(fw_data->sbus_priv);
+	WARN_ON(!skb);
+
+	//check device rx status
+	ret = bes_frame_rsp_check(skb->data, last_frame_num);
+	dev_kfree_skb(skb);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware info err:%d\n", ret);
+		goto err1;
+	}
+
+	// allocate memory for store download data frame
+	long_buf = kmalloc(1024 * 32, GFP_KERNEL);
+	if (!long_buf) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "%s:%d fw failed to allocate memory\n",__func__, __LINE__);
+		ret = -ENOMEM;
+		goto err1;
+	}
+	download_addr.addr = fw_info.addr;
+
+	// download firmware data
+	while (code_length) {
+		if ((code_length + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t)) < tx_size) {
+			length = code_length + sizeof(struct download_fw_t);
+		} else {
+			length = tx_size - sizeof(struct fw_msg_hdr_t);
+		}
+
+		header.type = FRAME_HEADER_DOWNLOAD_DATA;
+		header.seq = frame_num;
+		header.len = length;
+		last_frame_num = frame_num;
+		frame_num++;
+
+		memcpy(long_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+		memcpy(long_buf + sizeof(struct fw_msg_hdr_t), &download_addr.addr, sizeof(struct download_fw_t));
+		length -= sizeof(struct download_fw_t);//real data length
+		memcpy(long_buf + sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t), data_p, length);
+
+		length += (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
+
+		bes2600_info(BES2600_DBG_DOWNLOAD, "tx_download_firmware_data:%x %d\n", download_addr.addr, length);
+
+
+		ret = bes_usb_fw_write(fw_data, long_buf, length);
+		if (ret) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "tx download fw data err:%d\n", ret);
+			goto err2;
+		}
+		length -= (sizeof(struct fw_msg_hdr_t) + sizeof(struct download_fw_t));
+
+		ret = wait_for_completion_interruptible_timeout(&fw_data->completion_rx, HZ * 5);
+		if(ret <= 0) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "usb receive download data response timeout or interrupt\n");
+			ret = - ETIMEDOUT;
+			goto err2;
+		}
+
+		skb = fw_data->sbus_ops->pipe_read(fw_data->sbus_priv);
+		WARN_ON(!skb);
+
+		//check device rx status
+		ret = bes_frame_rsp_check(skb->data, last_frame_num);
+		dev_kfree_skb(skb);
+		if (ret) {
+			bes2600_err(BES2600_DBG_DOWNLOAD, "rsp tx download fw err:%d\n", ret);
+			goto err2;
+		}
+
+		code_length -= length;
+		data_p += length;
+		download_addr.addr += length;
+		bes2600_info(BES2600_DBG_DOWNLOAD, "already tx fw size:%x/%x\n", download_addr.addr - fw_info.addr, fw_info.len);
+	}
+
+	//Notify Device:The firmware download is complete
+	// construct firmware download end frame
+	header.type = FRAME_HEADER_DOWNLOAD_END;
+	header.seq = frame_num;
+	header.len = sizeof(struct fw_crc_t);
+	last_frame_num = frame_num;
+	frame_num++;
+
+	memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&crc32_t.crc32, sizeof(struct fw_crc_t));
+	length = BES_FW_MSG_TOTAL_LEN(header);
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "tx download firmware complete command\n");
+
+	// send frimware download end frame to device
+	ret = bes_usb_fw_write(fw_data, short_buf, length);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "tx downlod firmware complete command err:%d\n", ret);
+		goto err2;
+	}
+
+	// wait device response
+	ret = wait_for_completion_interruptible_timeout(&fw_data->completion_rx, HZ * 5);
+	if (ret <= 0) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait slave process download fw data err:%d\n", ret);
+		ret = - ETIMEDOUT;
+		goto err2;
+	}
+
+	// read response data
+	skb = fw_data->sbus_ops->pipe_read(fw_data->sbus_priv);
+	WARN_ON(!skb);
+
+	//check device rx status
+	ret = bes_frame_rsp_check(skb->data, last_frame_num);
+	dev_kfree_skb(skb);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rsp download firmware complete err:%d\n", ret);
+		goto err2;
+	}
+
+	if (auto_run == false) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "partial firmware(%s) is downloaded successfully\n", fw_name);
+		goto err2;
+	}
+
+	//Notify Device:Run firmware
+	// construct run code frame
+	run_addr.addr = fw_info.addr;
+
+	header.type = FRAME_HEADER_RUN_CODE;
+	header.seq = frame_num;
+	header.len = sizeof(struct run_fw_t);
+	last_frame_num = frame_num;
+	frame_num++;
+
+	memcpy(short_buf, (u8 *)&header, sizeof(struct fw_msg_hdr_t));
+	memcpy(short_buf + sizeof(struct fw_msg_hdr_t), (u8 *)&run_addr.addr, sizeof(struct run_fw_t));
+	length = BES_FW_MSG_TOTAL_LEN(header);
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "tx run firmware command:0x%X\n", run_addr.addr);
+
+	// send run code frame to device
+	ret = bes_usb_fw_write(fw_data, short_buf, length);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "tx run firmware command err:%d\n", ret);
+		goto err2;
+	}
+
+	// wait device response
+	ret = wait_for_completion_interruptible_timeout(&fw_data->completion_rx, HZ * 5);
+	if (ret <= 0) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "wait run code err:%d\n", ret);
+		ret = - ETIMEDOUT;
+		goto err2;
+	}
+
+	// read response data
+	skb = fw_data->sbus_ops->pipe_read(fw_data->sbus_priv);
+	WARN_ON(!skb);
+
+	//check device rx status
+	ret = bes_frame_rsp_check(skb->data, last_frame_num);
+	dev_kfree_skb(skb);
+	if (ret) {
+		bes2600_err(BES2600_DBG_DOWNLOAD, "rsp run firmware command err:%d\n", ret);
+		goto err2;
+	}
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "firmware is downloaded successfully and is already running\n");
+
+err2:
+	kfree(long_buf);
+
+err1:
+	kfree(short_buf);
+	release_firmware(fw_bin);
+
+	return ret;
+}
+
+static int bes2600_load_wifi_firmware(struct platform_fw_t *fw_data)
+{
+	int ret = 0;
+	const char *fw_name_tbl[3];
+	int fw_type = bes2600_chrdev_get_fw_type();
+
+	fw_name_tbl[0] = BES2600_LOAD_FW_NAME;
+	fw_name_tbl[1] = BES2600_LOAD_NOSIGNAL_FW_NAME;
+	fw_name_tbl[2] = BES2600_LOAD_BTRF_FW_NAME;
+
+	bes2600_info(BES2600_DBG_DOWNLOAD, "bes2600 download cali and wifi signal firmware.\n");
+	ret = bes_firmware_download(fw_data, BES2600_LOAD_BOOT_NAME, true);
+	bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "download dpd cali firmware failed\n");
+
+	if (!ret) {
+		//force wait for wifi dpd calib
+		msleep(5000);
+		ret = bes_firmware_download(fw_data, fw_name_tbl[fw_type], true);
+	}
+	return ret;
+}
+
+int bes2600_load_firmware_usb(struct sbus_ops *ops, struct sbus_priv *priv)
+{
+	int ret;
+	struct platform_fw_t *temp_fw_data;
+	u32 control_reg = 0;
+	int fw_type = bes2600_chrdev_get_fw_type();
+	temp_fw_data = kzalloc(sizeof(struct platform_fw_t), GFP_KERNEL);
+	if (!temp_fw_data)
+		return -ENOMEM;
+
+	init_completion(&temp_fw_data->completion_rx);
+	init_completion(&temp_fw_data->completion_tx);
+	temp_fw_data->sbus_ops = ops;
+	temp_fw_data->sbus_priv = priv;
+
+	// enable rx indication
+	temp_fw_data->sbus_ops->sbus_reg_read(temp_fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1);
+	control_reg |= BES_USB_FW_RX_INDICATION;
+	temp_fw_data->sbus_ops->sbus_reg_write(temp_fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1);
+
+	// subscribe irq handler
+	temp_fw_data->sbus_ops->irq_subscribe(temp_fw_data->sbus_priv,
+			(sbus_irq_handler)bes_fw_irq_handler, temp_fw_data);
+
+	// download firmware
+	bes2600_info(BES2600_DBG_DOWNLOAD, "%s fw_type:%d.\n", __func__, fw_type);
+	ret = bes2600_load_wifi_firmware(temp_fw_data);
+	// unsubscribe irq handler
+	temp_fw_data->sbus_ops->irq_unsubscribe(temp_fw_data->sbus_priv);
+
+	// disable rx indication
+	temp_fw_data->sbus_ops->sbus_reg_read(temp_fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1);
+	control_reg &= ~BES_USB_FW_RX_INDICATION;
+	temp_fw_data->sbus_ops->sbus_reg_write(temp_fw_data->sbus_priv, BES_USB_CONTROL_REG, &control_reg, 1);
+
+	kfree(temp_fw_data);
+
+	return ret;
+}
\ No newline at end of file
diff --git a/drivers/staging/bes2600/bes_nl80211_testmode_msg.h b/drivers/staging/bes2600/bes_nl80211_testmode_msg.h
new file mode 100644
index 000000000000..a05f9f862704
--- /dev/null
+++ b/drivers/staging/bes2600/bes_nl80211_testmode_msg.h
@@ -0,0 +1,258 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES_NL80211_TESTMODE_MSG_H
+#define BES_NL80211_TESTMODE_MSG_H
+
+/* example command structure for test purposes */
+struct bes_msg_test_t {
+	int dummy;
+};
+
+/* example reply structure for test purposes */
+struct bes_reply_test_t {
+	int dummy;
+};
+
+/* example event structure for test purposes */
+struct bes_event_test_t {
+	int dummy;
+};
+
+/* vendor to mcu cmd msg reply structure */
+struct vendor_rf_cmd_msg_reply {
+	u32 id;
+	u32 msg_len;
+	char ret_msg[1028];
+};
+
+/* rf cmd msg reply assembly */
+void bes2600_rf_cmd_msg_assembly(u32 cmd_type, void *data, u32 msg_len);
+/* do rf cmd msg */
+int bes2600_vendor_rf_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len);
+
+enum bes_msg_id {
+	BES_MSG_TEST = 0,	/* for test purposes */
+	BES_MSG_EVENT_TEST,	/* for test purposes */
+	BES_MSG_SET_SNAP_FRAME,
+	BES_MSG_EVENT_FRAME_DATA,
+#ifdef CONFIG_BES2600_TESTMODE
+	BES_MSG_GET_TX_POWER_LEVEL,
+	BES_MSG_GET_TX_POWER_RANGE,
+	BES_MSG_SET_ADVANCE_SCAN_ELEMS,
+	BES_MSG_SET_TX_QUEUE_PARAMS,
+	BES_MSG_START_STOP_TSM,
+	BES_MSG_GET_TSM_PARAMS,
+	BES_MSG_GET_ROAM_DELAY,
+#endif /*CONFIG_BES2600_TESTMODE*/
+	BES_MSG_SET_POWER_SAVE,
+#ifdef ROAM_OFFLOAD
+	BES_MSG_NEW_SCAN_RESULTS,
+#endif /* ROAM_OFFLOAD */
+	BES_MSG_ADD_IP_OFFLOAD,
+	BES_MSG_DEL_IP_OFFLOAD,
+	BES_MSG_SET_IP_OFFLOAD_PERIOD,
+	BES_MSG_VENDOR_RF_CMD,
+	BES_MSG_SAVE_CALI_TXT_TO_FLASH,
+	BES_MSG_EPTA_PARM_CONFIG,
+	BES_MSG_GET_KEEP_ALIVE_STREAM,
+	BES_MSG_MCU_CPUUSAGE,
+	/* Add new IDs here */
+
+	BES_MSG_ID_MAX,
+};
+
+enum vendor_rf_cmd_type {
+	VENDOR_RF_SIGNALING_CMD = 0,
+	VENDOR_RF_NOSIGNALING_CMD,
+	VENDOR_RF_GET_SAVE_FREQOFFSET_CMD,
+	VENDOR_RF_GET_SAVE_POWERLEVEL_CMD,
+	VENDOR_RF_SAVE_FREQOFFSET_CMD,
+	VENDOR_RF_SAVE_POWERLEVEL_CMD,
+	VENDOR_RF_POWER_CALIB_FINISH,
+	/* add new here */
+
+	VENDOR_RF_CMD_MAX,
+
+	VENDOR_RF_SIG_NOSIG_MIX = 0xFFFFFFFF,
+};
+
+enum bes_nl80211_testmode_data_attributes {
+	BES_TM_MSG_ID = 0x0001,	/* u32 type containing the BES message ID */
+	BES_TM_MSG_DATA,	/* message payload */
+
+	/* Max indicator so module test may add its own attributes */
+	BES_TM_MSG_ATTR_MAX,
+};
+
+/**
+ * bes_msg_set_snap_frame - set SNAP frame format
+ * @len: length of SNAP frame, if 0 SNAP frame disabled
+ * @frame: SNAP frame format
+ *
+ * In this structure is difference between user space because
+ * format and length have to be hidden
+ *
+ */
+struct bes_msg_set_snap_frame {
+	u8 len;
+	u8 frame[0];
+};
+
+struct vendor_epta_parm {
+	int wlan_duration;
+	int bt_duration;
+	int hw_epta_enable;
+};
+
+#ifdef CONFIG_BES2600_TESTMODE
+/**
+ * bes_msg_set_txqueue_params - store Tx queue params
+ * @user_priority: User priority for which TSPEC negotiated
+ * @medium_time: Allowed medium time
+ * @expiry_time: The expiry time of MSDU
+ *
+ */
+struct bes_msg_set_txqueue_params {
+	u8 user_priority;
+	u16 medium_time;
+	u16 expiry_time;
+};
+
+/**
+ * bes_tsm_stats - To retrieve the Transmit Stream Measurement stats
+ * @actual_msrmt_start_time: The TSF at the time at which the measurement
+ * started
+ * @msrmt_duration: Duration for measurement
+ * @peer_sta_addr: Peer STA address
+ * @tid: TID for which measurements were made
+ * @reporting_reason: Reason for report sent
+ * @txed_msdu_count: The number of MSDUs transmitted for the specified TID
+ * @msdu_discarded_count: The number of discarded MSDUs for the specified TID
+ * @msdu_failed_count: The number of failed MSDUs for the specified TID
+ * @multi_retry_count: The number of MSDUs which were retried
+ * @qos_cfpolls_lost_count: The number of QOS CF polls frames lost
+ * @avg_q_delay: Average queue delay
+ * @avg_transmit_delay: Average transmit delay
+ * @bin0_range: Delay range of the first bin (Bin 0)
+ * @bin0: bin0 transmit delay histogram
+ * @bin1: bin1 transmit delay histogram
+ * @bin2: bin2 transmit delay histogram
+ * @bin3: bin3 transmit delay histogram
+ * @bin4: bin4 transmit delay histogram
+ * @bin5: bin5 transmit delay histogram
+ *
+ */
+struct bes_tsm_stats {
+	u64 actual_msrmt_start_time;
+	u16 msrmt_duration;
+	u8 peer_sta_addr[6];
+	u8 tid;
+	u8 reporting_reason;
+	u32 txed_msdu_count;
+	u32 msdu_discarded_count;
+	u32 msdu_failed_count;
+	u32 multi_retry_count;
+	u32 qos_cfpolls_lost_count;
+	u32 avg_q_delay;
+	u32 avg_transmit_delay;
+	u8 bin0_range;
+	u32 bin0;
+	u32 bin1;
+	u32 bin2;
+	u32 bin3;
+	u32 bin4;
+	u32 bin5;
+} __packed;
+
+
+/**
+ * bes_msg_set_start_stop_tsm - To start or stop collecting TSM metrics in
+ * bes2600 driver
+ * @start: To start or stop collecting TSM metrics
+ * @up: up for which metrics to be collected
+ * @packetization_delay: Packetization period for this TID
+ *
+ */
+struct bes_msg_start_stop_tsm {
+	u8 start;	/*1: To start, 0: To stop*/
+	u8 up;
+	u16 packetization_delay;
+};
+
+/**
+ * power_save_elems - To enable/disable legacy power Save
+ */
+struct power_save_elems {
+	int powerSave;
+};
+
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+/*
+ * ip keep alive feature's parameters
+ * @idx add(idx=15) a tcp/udp keep alive stream, or the idx number(idx= 0-7) when reconfig one;
+ *      set payload_len to 0 and payload to NULL when deleting one stream.
+ * @klv_vendor different number stands for different vendor's keepalive configuration
+ * @period: alive period
+ * @proto 0 for udp and 1 for tcp
+ * @src_port local port
+ * @dst_port tcp server's listen port
+ * @src_ip local ip address
+ * @dst_ip tcp server's ip address
+ * @key: key
+ * @iv: iv
+ * @payload payload of the keep alive packet
+ * @payload_len length of the payload
+ */
+struct ip_alive_paras {
+	uint16_t idx;
+	uint8_t klv_vendor;
+	uint8_t period;
+	uint8_t proto;
+	uint16_t src_port;
+	uint16_t dst_port;
+	uint32_t src_ip;
+	uint32_t dst_ip;
+	uint8_t key[16];
+	uint8_t iv[16];
+	uint16_t payload_len;
+	uint8_t payload[0];
+};
+
+/**
+ * ip_alive_tac_idx - idx of tcp & udp alive stream
+ */
+struct ip_alive_iac_idx {
+	int idx;
+};
+
+/**
+ * ip_alive_period - tcp & udp alive period
+ */
+struct ip_alive_period {
+	int period;
+};
+
+/*
+ * err_code, 0: success, -1: cmd with idx err,
+ * -2: get stream fail, -3: playload err
+ */
+struct ip_stream_cfg {
+	int err_code;
+	int idx;
+};
+#endif /* CONFIG_BES2600_KEEP_ALIVE */
+
+#endif /* CONFIG_BES2600_TESTMODE */
+
+#define BES_TM_MAX_ATTRIB_SIZE 1024
+
+#endif /* BES_NL80211_TESTMODE_MSG_H*/
diff --git a/drivers/staging/bes2600/bes_pwr.c b/drivers/staging/bes2600/bes_pwr.c
new file mode 100644
index 000000000000..19d7ed26fde8
--- /dev/null
+++ b/drivers/staging/bes2600/bes_pwr.c
@@ -0,0 +1,1406 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/list.h>
+#include <linux/pm.h>
+#include "bes2600.h"
+#include "sbus.h"
+#include "bes_pwr.h"
+#include "sta.h"
+#include "bes_chardev.h"
+
+static void bes2600_add_power_delay_event(struct bes2600_pwr_t *bes_pwr, u32 event, u32 timeout);
+
+static void bes2600_dump_power_busy_event(struct bes2600_pwr_t *bes_pwr, char* location)
+{
+	struct bes2600_pwr_event_t *item = NULL;
+	char *async_dump_str = NULL;
+	char *pending_dump_str = NULL;
+	char *free_dump_str = NULL;
+	int used_len = 0;
+	bool dump_async_ok = true;
+	bool dump_pending_ok = true;
+	bool dump_free_ok = true;
+	bool async_have = false;
+	bool pending_have = false;
+	bool free_have = false;
+	const int single_buffer_len = 4096;
+	const int all_bufer_len = single_buffer_len * 3;
+
+	if(bes2600_get_dbg_lvl(BES2600_DBG_PWR) < BES2600_LOG_DBG) {
+		return;
+	}
+
+	bes2600_dbg(BES2600_DBG_PWR, "power busy event dump at %s\n", location);
+
+	async_dump_str = kzalloc(all_bufer_len, GFP_ATOMIC);
+	if(async_dump_str == NULL) {
+		bes2600_err(BES2600_DBG_PWR, "%s alloc buffer failed\n", __func__);
+		return;
+	}
+
+	used_len = snprintf(async_dump_str, single_buffer_len, "async list[");
+	if(!list_empty(&bes_pwr->async_timeout_list)) {
+		list_for_each_entry(item, &bes_pwr->async_timeout_list, link) {
+			if(used_len + 10 < single_buffer_len) {
+				async_have = true;
+				used_len += snprintf(async_dump_str + used_len, 10, "%d(%d) ",
+					BES_PWR_EVENT_NUMBER(item->event), (item->event >> 31));
+			} else {
+				dump_async_ok = false;
+				goto out;
+			}
+		}
+	}
+	if(used_len + 3 < single_buffer_len) {
+		used_len = async_have ? used_len  - 1 : used_len;
+		used_len += sprintf(async_dump_str + used_len, "]\n");
+	} else {
+		dump_async_ok = false;
+		goto out;
+	}
+
+	pending_dump_str = async_dump_str + single_buffer_len;
+	used_len = snprintf(pending_dump_str, single_buffer_len, "pending list[");
+	if(!list_empty(&bes_pwr->pending_event_list)) {
+		list_for_each_entry(item, &bes_pwr->pending_event_list, link) {
+			if(used_len + 10 < single_buffer_len) {
+				pending_have = true;
+				used_len += snprintf(pending_dump_str + used_len, 10, "%d(%d) ",
+					BES_PWR_EVENT_NUMBER(item->event), (item->event >> 31));
+			} else {
+				dump_pending_ok = false;
+				goto out;
+			}
+		}
+	}
+	if(used_len + 3 < single_buffer_len) {
+		used_len = pending_have ? used_len  - 1 : used_len;
+		used_len += sprintf(pending_dump_str + used_len, "]\n");
+	} else {
+		dump_pending_ok = false;
+		goto out;
+	}
+
+	free_dump_str = pending_dump_str + single_buffer_len;
+	used_len = snprintf(free_dump_str, single_buffer_len, "free list[");
+	if(!list_empty(&bes_pwr->free_event_list)) {
+		list_for_each_entry(item, &bes_pwr->free_event_list, link) {
+			if(used_len + 10 < single_buffer_len) {
+				free_have = true;
+				used_len += snprintf(free_dump_str + used_len, 10, "%d(%d) ",
+					BES_PWR_EVENT_NUMBER(item->event), (item->event >> 31));
+			} else {
+				dump_free_ok = false;
+				goto out;
+			}
+		}
+	}
+	if(used_len + 3 < single_buffer_len) {
+		used_len = free_have ? used_len  - 1 : used_len;
+		used_len += sprintf(free_dump_str + used_len, "]\n");
+	} else {
+		dump_free_ok = false;
+		goto out;
+	}
+out:
+	if(!dump_async_ok) {
+		bes2600_err(BES2600_DBG_PWR, "buffer is not enough for dumping async event\n");
+	} else {
+		bes2600_dbg(BES2600_DBG_PWR, "%s", async_dump_str);
+	}
+
+	if(!dump_pending_ok) {
+		bes2600_err(BES2600_DBG_PWR, "buffer is not enough for dumping pending event\n");
+	} else {
+		bes2600_dbg(BES2600_DBG_PWR, "%s", pending_dump_str);
+	}
+
+	if(!dump_free_ok) {
+		bes2600_err(BES2600_DBG_PWR, "buffer is not enough for dumping free event\n");
+	} else {
+		bes2600_dbg(BES2600_DBG_PWR, "%s", free_dump_str);
+	}
+
+	kfree(async_dump_str);
+}
+
+static char *bes2600_get_ps_mode_str(u8 mode)
+{
+	char *ps_mode_str = NULL;
+
+	ps_mode_str = (mode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" :
+		mode == WSM_PSM_PS ? "WSM_PSM_PS" :
+		mode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" :
+		"UNKNOWN");
+
+	return ps_mode_str;
+}
+
+static char *bes2600_get_mac_str(char *buffer, u32 ip)
+{
+	sprintf(buffer, "%d.%d.%d.%d",
+			(ip & 0xff),((ip >> 8) & 0xff),
+			((ip >> 16) & 0xff), ((ip >> 24) & 0xff));
+
+	return buffer;
+}
+
+static char *bes2600_get_pwr_busy_event_name(struct bes2600_pwr_event_t *item)
+{
+	char *name = NULL;
+
+	switch(BES_PWR_EVENT_NUMBER(item->event)) {
+		case BES_PWR_LOCK_ON_SCAN:      name = "SCAN";          break;
+		case BES_PWR_LOCK_ON_JOIN:      name = "JOIN";          break;
+		case BES_PWR_LOCK_ON_TX:        name = "TX";            break;
+		case BES_PWR_LOCK_ON_RX:        name = "RX";            break;
+		case BES_PWR_LOCK_ON_FLUSH:     name = "FLUSH";         break;
+		case BES_PWR_LOCK_ON_ROC:       name = "ROC";           break;
+		case BES_PWR_LOCK_ON_WSM_TX:    name = "WSM_TX";        break;
+		case BES_PWR_LOCK_ON_WSM_OPER:  name = "WSM_OPER";      break;
+		case BES_PWR_LOCK_ON_BSS_LOST:  name = "BSS_LOST";      break;
+		case BES_PWR_LOCK_ON_GET_IP:    name = "GET_IP";        break;
+		case BES_PWR_LOCK_ON_PS_ACTIVE: name = "PS_ACTIVE";     break;
+		case BES_PWR_LOCK_ON_LMAC_RSP:  name = "LMAC_RSP";      break;
+		case BES_PWR_LOCK_ON_AP:        name = "AP";            break;
+		case BES_PWR_LOCK_ON_TEST_CMD:  name = "TEST_CMD";      break;
+		case BES_PWR_LOCK_ON_MUL_REQ:   name = "MUL_REQ";       break;
+		case BES_PWR_LOCK_ON_ADV_SCAN:  name = "ADV_SCAN";      break;
+		case BES_PWR_LOCK_ON_DISCON:    name = "DISCON";        break;
+		case BES_PWR_LOCK_ON_QUEUE_GC:  name = "QUEUE_GC";      break;
+		case BES_PWR_LOCK_ON_AP_LP_BAD: name = "AP_LP_BAD";     break;
+		default:                        name = "UNKNOW";        break;
+	}
+
+	return name;
+}
+
+static unsigned long bes2600_get_pwr_busy_event_timeout(struct bes2600_pwr_event_t *item)
+{
+	unsigned long timeout = 0;
+
+	if(BES_PWR_IS_CONSTANT_EVENT(item->event))
+		return 0;
+
+	if(time_after(jiffies, item->timeout))
+		return 0;
+
+	timeout = (item->timeout >= jiffies) ?
+		(item->timeout - jiffies) : (ULONG_MAX - jiffies + item->timeout);
+
+	return timeout;
+}
+
+static bool bes2600_update_power_delay_events(struct bes2600_pwr_t *bes_pwr, unsigned long *timeout)
+{
+	struct bes2600_pwr_event_t *item = NULL, *temp = NULL;
+	unsigned long max_timeout = 0;
+	bool constant_event_exist = false;
+
+	/* move event from async_timeout_list to pending_event_list */
+	if(!list_empty(&bes_pwr->async_timeout_list)) {
+		list_for_each_entry_safe(item, temp, &bes_pwr->async_timeout_list, link) {
+			bes2600_add_power_delay_event(bes_pwr, item->event, item->delay);
+			list_move_tail(&item->link, &bes_pwr->free_event_list);
+		}
+	}
+
+	/* age power event and get max timeout */
+	list_for_each_entry_safe(item, temp, &bes_pwr->pending_event_list, link) {
+		if(BES_PWR_IS_CONSTANT_EVENT(item->event)) {
+		       constant_event_exist = true;
+		       continue;
+		}
+		if(time_after(jiffies, item->timeout)) {
+			bes2600_dbg(BES2600_DBG_PWR, "power busy event:0x%08x timeout\n", item->event);
+			list_move_tail(&item->link, &bes_pwr->free_event_list);
+		} else {
+			if(max_timeout == 0) {
+				max_timeout = item->timeout;
+			} else {
+				max_timeout = time_after(item->timeout, max_timeout)
+						? item->timeout : max_timeout;
+			}
+		}
+	}
+
+	bes2600_dump_power_busy_event(bes_pwr, "refresh");
+
+	if(timeout) {
+		*timeout = max_timeout;
+	}
+
+	return constant_event_exist;
+}
+
+static void bes2600_add_async_timeout_power_delay_event(struct bes2600_pwr_t *bes_pwr, u32 event, u32 timeout)
+{
+	struct bes2600_pwr_event_t *item = NULL;
+	unsigned long max_timeout = 0;
+	bool match = false;
+
+	/* check if the event is already in pending list */
+	if (!list_empty(&bes_pwr->async_timeout_list)) {
+		list_for_each_entry(item, &bes_pwr->async_timeout_list, link) {
+			if(item->event == event) {
+				match = true;
+				break;
+			}
+		}
+	}
+
+	/* update event information */
+	if (match && (item != NULL)) {
+		item->delay = timeout;
+	} else {
+		/* delete expired event if free_event_list is empty */
+		if(list_empty(&bes_pwr->free_event_list)) {
+			bes2600_info(BES2600_DBG_PWR, "%s, update delay event\n", __func__);
+			bes2600_update_power_delay_events(bes_pwr, &max_timeout);
+		}
+
+		/* throw out exception if free_event_list is empty */
+		BUG_ON(list_empty(&bes_pwr->free_event_list));
+
+		/* add event instance to pending list */
+		bes2600_dbg(BES2600_DBG_PWR, "%s, add async event:%d(%d) timeout:%d\n",
+			__func__, BES_PWR_EVENT_NUMBER(event),  event >> 31, timeout);
+		item = list_first_entry(&bes_pwr->free_event_list,
+			struct bes2600_pwr_event_t, link);
+		list_move_tail(&item->link, &bes_pwr->async_timeout_list);
+		item->event = event;
+		item->delay = timeout;
+	}
+}
+
+static void bes2600_add_power_delay_event(struct bes2600_pwr_t *bes_pwr, u32 event, u32 timeout)
+{
+	struct bes2600_pwr_event_t *item = NULL;
+	unsigned long max_timeout = 0;
+	bool match = false;
+
+	/* check if the event is already in pending list */
+	if(!list_empty(&bes_pwr->pending_event_list)) {
+		list_for_each_entry(item, &bes_pwr->pending_event_list, link) {
+			if(item->event == event) {
+				match = true;
+				break;
+			}
+		}
+	}
+
+	/* update event or add a new event */
+	if(match && (item != NULL)) {
+		/* duplicate event */
+		item->timeout = jiffies + (timeout * HZ + HZ * BES2600_POWER_DOWN_DELAY) / 1000;
+		bes2600_dbg(BES2600_DBG_PWR, "%s, update event:%d(%d) timeout:%d\n",
+			__func__, BES_PWR_EVENT_NUMBER(event),  event >> 31, timeout);
+	} else {
+		/* delete expired event if free_event_list is empty */
+		if(list_empty(&bes_pwr->free_event_list)) {
+			bes2600_info(BES2600_DBG_PWR, "%s, update delay event\n", __func__);
+			bes2600_update_power_delay_events(bes_pwr, &max_timeout);
+		}
+
+		/* throw out exception if free_event_list is empty */
+		BUG_ON(list_empty(&bes_pwr->free_event_list));
+
+		/* add event instance to pending list */
+		bes2600_dbg(BES2600_DBG_PWR, "%s, add event:%d(%d) timeout:%d\n",
+			__func__, BES_PWR_EVENT_NUMBER(event),  event >> 31, timeout);
+		item = list_first_entry(&bes_pwr->free_event_list,
+			struct bes2600_pwr_event_t, link);
+		list_move_tail(&item->link, &bes_pwr->pending_event_list);
+		item->event = event;
+		item->timeout = jiffies + (timeout * HZ + HZ * BES2600_POWER_DOWN_DELAY) / 1000;
+		bes2600_dump_power_busy_event(bes_pwr, "add new event");
+	}
+}
+
+static bool bes2600_del_pending_event(struct bes2600_pwr_t *bes_pwr, u32 event)
+{
+	struct bes2600_pwr_event_t *item = NULL, *temp = NULL;
+	bool matched = false;
+
+	list_for_each_entry_safe(item, temp, &bes_pwr->pending_event_list, link) {
+		if(event == item->event) {
+			matched = true;
+			list_move_tail(&item->link, &bes_pwr->free_event_list);
+			bes2600_dbg(BES2600_DBG_PWR, "delete pending event:%d(%d)\n",
+				BES_PWR_EVENT_NUMBER(event), event >> 31);
+			break;
+		}
+	}
+
+	bes2600_dump_power_busy_event(bes_pwr, "delete");
+
+	return matched;
+}
+
+static void bes2600_flush_pending_events(struct bes2600_pwr_t *bes_pwr)
+{
+	struct bes2600_pwr_event_t *item = NULL, *temp = NULL;
+
+	list_for_each_entry_safe(item, temp, &bes_pwr->pending_event_list, link) {
+		list_move_tail(&item->link, &bes_pwr->free_event_list);
+		bes2600_dbg(BES2600_DBG_PWR, "flush pending event:%d(%d)\n",
+				BES_PWR_EVENT_NUMBER(item->event), item->event >> 31);
+	}
+	bes2600_dump_power_busy_event(bes_pwr, "flush");
+}
+
+static void bes2600_trigger_power_delay_down(struct bes2600_pwr_t *bes_pwr, unsigned long max_timeout)
+{
+	/* get the gap of the two timestamp */
+	max_timeout = (max_timeout >= jiffies) ?
+		(max_timeout - jiffies) : (ULONG_MAX - jiffies + max_timeout);
+
+	bes2600_dbg(BES2600_DBG_PWR, "restart delayed work, timeout:%lu\n", max_timeout);
+	queue_delayed_work(bes_pwr->hw_priv->workqueue, &bes_pwr->power_down_work, max_timeout);
+}
+
+static void bes2600_pwr_lock_tx(struct bes2600_common *hw_priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	if(!hw_priv->bes_power.pending_lock) {
+		hw_priv->bes_power.pending_lock = true;
+		bes2600_dbg(BES2600_DBG_PWR, "bes pwr lock tx\n");
+		wsm_lock_tx_async(hw_priv);
+	}
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+}
+
+static void bes2600_pwr_unlock_tx(struct bes2600_common *hw_priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	if(hw_priv->bes_power.pending_lock) {
+		hw_priv->bes_power.pending_lock = false;
+		bes2600_dbg(BES2600_DBG_PWR, "bes pwr unlock tx\n");
+		wsm_unlock_tx(hw_priv);
+	}
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+}
+
+static void bes2600_pwr_call_enter_lp_cb(struct bes2600_common *hw_priv)
+{
+	struct bes2600_pwr_enter_cb_item *item = NULL;
+
+	mutex_lock(&hw_priv->bes_power.pwr_cb_mutex);
+	if (!list_empty(&hw_priv->bes_power.enter_cb_list)) {
+		list_for_each_entry(item, &hw_priv->bes_power.enter_cb_list, link) {
+			if(item->cb != NULL)
+				item->cb(hw_priv);
+		}
+	}
+	mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex);
+}
+
+static void bes2600_pwr_call_exit_lp_cb(struct bes2600_common *hw_priv)
+{
+	struct bes2600_pwr_exit_cb_item *item = NULL;
+
+	mutex_lock(&hw_priv->bes_power.pwr_cb_mutex);
+	if (!list_empty(&hw_priv->bes_power.exit_cb_list)) {
+		list_for_each_entry(item, &hw_priv->bes_power.exit_cb_list, link) {
+			if(item->cb != NULL)
+				item->cb(hw_priv);
+		}
+	}
+	mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex);
+}
+
+static void bes2600_pwr_delete_all_cb(struct bes2600_common *hw_priv)
+{
+	struct bes2600_pwr_enter_cb_item *item = NULL, *temp = NULL;
+	struct bes2600_pwr_exit_cb_item *item1 = NULL, *temp1 = NULL;
+
+	mutex_lock(&hw_priv->bes_power.pwr_cb_mutex);
+
+	/* delete all cb in enter_cb_list */
+	list_for_each_entry_safe(item, temp, &hw_priv->bes_power.enter_cb_list, link) {
+		list_del(&item->link);
+		kfree(item);
+	}
+
+	/* delete all cb in exit_cb_list */
+	list_for_each_entry_safe(item1, temp1, &hw_priv->bes_power.exit_cb_list, link) {
+		list_del(&item->link);
+		kfree(item);
+	}
+
+	mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex);
+}
+
+static void bes2600_pwr_device_enter_lp_mode(struct bes2600_common *hw_priv)
+{
+	int ret = 0;
+	struct wsm_operational_mode mode = {
+		.power_mode = wsm_power_mode_quiescent,
+		.disableMoreFlagUsage = true,
+	};
+
+	bes2600_dbg(BES2600_DBG_PWR, "host unlock lmac\n");
+	ret = wsm_set_operational_mode(hw_priv, &mode, 0);
+	bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set operation mode fail\n", __func__);
+
+	/* wait other module to finish work */
+	bes2600_pwr_call_enter_lp_cb(hw_priv);
+
+	if(hw_priv->sbus_ops->sbus_deactive) {
+		ret = hw_priv->sbus_ops->sbus_deactive(hw_priv->sbus_priv, SUBSYSTEM_MCU);
+		bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, deactive mcu fail\n", __func__);
+	}
+
+	if(hw_priv->sbus_ops->gpio_sleep)
+		hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU);
+	bes2600_info(BES2600_DBG_PWR, "device enter sleep\n");
+}
+
+static int bes2600_pwr_enter_lp_mode(struct bes2600_common *hw_priv)
+{
+	int i = 0;
+	struct bes2600_vif *priv;
+	int ret = 0;
+	char ip_str[20];
+	unsigned long status = 0;
+
+	/* set interface low power configuration */
+	bes2600_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+		if (i == (CW12XX_MAX_VIFS - 1))
+			continue;
+#endif
+		if (!priv)
+			continue;
+
+		if (priv->join_status == BES2600_JOIN_STATUS_STA &&
+		    priv->bss_params.aid &&
+		    priv->setbssparams_done &&
+		    priv->filter4.enable) {
+			/* enable arp filter */
+			bes2600_dbg(BES2600_DBG_PWR, "%s, arp filter, enable:%d addr:%s\n",
+				__func__, priv->filter4.enable, bes2600_get_mac_str(ip_str, priv->filter4.ipv4Address[0]));
+			ret = wsm_set_arp_ipv4_filter(hw_priv, &priv->filter4, priv->if_id);
+			bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set arp filter failed\n", __func__);
+
+			/* skip beacon receive if applications don't have muticast service */
+			if(priv->join_dtim_period && !priv->has_multicast_subscription) {
+				unsigned listen_interval = 1;
+				if(priv->join_dtim_period >= CONFIG_BES2600_LISTEN_INTERVAL) {
+					listen_interval = priv->join_dtim_period;
+				} else {
+					listen_interval = CONFIG_BES2600_LISTEN_INTERVAL / priv->join_dtim_period;
+				}
+				ret = wsm_set_beacon_wakeup_period(hw_priv, 1, listen_interval, priv->if_id);
+				bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set wakeup period failed\n", __func__);
+			}
+
+			/* Set Enable Broadcast Address Filter */
+			priv->broadcast_filter.action_mode = WSM_FILTER_ACTION_FILTER_OUT;
+			if (priv->join_status == BES2600_JOIN_STATUS_AP)
+				priv->broadcast_filter.address_mode = WSM_FILTER_ADDR_MODE_A3;
+			ret = bes2600_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter);
+			bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set bc filter failed\n", __func__);
+
+			/* enter low power mode */
+			if(!hw_priv->bes_power.ap_lp_bad) {
+				bes2600_dbg(BES2600_DBG_PWR, "%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n",
+						__func__, bes2600_get_ps_mode_str(priv->powersave_mode.pmMode), priv->powersave_mode.fastPsmIdlePeriod,
+						priv->powersave_mode.apPsmChangePeriod, priv->powersave_mode.minAutoPsPollPeriod);
+				atomic_set(&hw_priv->bes_power.pm_set_in_process, 1);
+				ret = bes2600_set_pm(priv, &priv->powersave_mode);
+				if (ret) {
+					atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
+					bes2600_err(BES2600_DBG_PWR, "%s, set operation mode fail\n", __func__);
+				}
+
+				/* wait power save mode changed indication */
+				status = wait_for_completion_timeout(&hw_priv->bes_power.pm_enter_cmpl, 5 * HZ);
+				atomic_set(&hw_priv->bes_power.pm_set_in_process, 0);
+				reinit_completion(&hw_priv->bes_power.pm_enter_cmpl);
+				bes2600_err_with_cond(!status, BES2600_DBG_PWR, "%s, wait pm ind timeout\n", __func__);
+			} else {
+				bes2600_info(BES2600_DBG_PWR, "skip enter lp mode\n");
+			}
+		}
+	}
+
+	/* set device low power configuration */
+	bes2600_pwr_device_enter_lp_mode(hw_priv);
+
+	return ret;
+}
+
+static void bes2600_pwr_device_exit_lp_mode(struct bes2600_common *hw_priv)
+{
+	int ret = 0;
+	struct wsm_operational_mode mode = {
+		.power_mode = wsm_power_mode_active,
+		.disableMoreFlagUsage = true,
+	};
+
+	bes2600_dbg(BES2600_DBG_PWR, "host lock lmac\n");
+	if(hw_priv->sbus_ops->gpio_wake)
+		hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU);
+
+	if(hw_priv->sbus_ops->sbus_active) {
+		ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU);
+		bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, active mcu fail\n", __func__);
+	}
+
+	ret = wsm_set_operational_mode(hw_priv, &mode, 0);
+	bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set operation mode fail\n", __func__);
+	bes2600_info(BES2600_DBG_PWR, "device exit sleep\n");
+}
+
+static int bes2600_pwr_exit_lp_mode(struct bes2600_common *hw_priv)
+{
+	int i = 0, ret = 0;
+	struct bes2600_vif *priv;
+	struct wsm_arp_ipv4_filter filter;
+	struct wsm_set_pm pm;
+	char ip_str[20];
+
+	/* set device low power configuration */
+	bes2600_pwr_device_exit_lp_mode(hw_priv);
+
+	/* set interface low power configutation */
+	bes2600_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+		if (i == (CW12XX_MAX_VIFS - 1))
+			continue;
+#endif
+		if (!priv)
+			continue;
+
+		if (priv->join_status == BES2600_JOIN_STATUS_STA &&
+		    priv->bss_params.aid &&
+		    priv->setbssparams_done &&
+		    priv->filter4.enable) {
+			/* enable arp filter */
+			filter = priv->filter4;
+			filter.enable = false;
+			bes2600_dbg(BES2600_DBG_PWR, "%s, arp filter, enable:%d addr:%s\n",
+				__func__, filter.enable, bes2600_get_mac_str(ip_str, filter.ipv4Address[0]));
+			ret = wsm_set_arp_ipv4_filter(hw_priv, &filter, priv->if_id);
+			bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set arp filter failed\n", __func__);
+
+			/* set wakeup perioid */
+			wsm_set_beacon_wakeup_period(hw_priv, priv->join_dtim_period, 0, priv->if_id);
+			bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set wakeup period failed\n", __func__);
+
+			/* Set Enable Broadcast Address Filter */
+			priv->broadcast_filter.action_mode = WSM_FILTER_ACTION_IGNORE;
+			if (priv->join_status == BES2600_JOIN_STATUS_AP)
+				priv->broadcast_filter.address_mode = WSM_FILTER_ADDR_MODE_NONE;
+			bes2600_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter);
+			bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set bc filter failed\n", __func__);
+
+			/* exit low power mode */
+			if(!hw_priv->bes_power.ap_lp_bad) {
+				pm = priv->powersave_mode;
+				pm.pmMode = WSM_PSM_ACTIVE;
+				bes2600_dbg(BES2600_DBG_PWR, "%s, psMode:%s, fastPsmIdlePeriod:%d apPsmChangePeriod:%d minAutoPsPollPeriod:%d\n",
+						__func__, bes2600_get_ps_mode_str(pm.pmMode), pm.fastPsmIdlePeriod, pm.apPsmChangePeriod, pm.minAutoPsPollPeriod);
+				ret = bes2600_set_pm(priv, &pm);
+				bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, set operation mode fail\n", __func__);
+			} else {
+				bes2600_info(BES2600_DBG_PWR, "skip exit lp mode\n");
+			}
+		}
+	}
+
+	/* call all exit lower power callback */
+	bes2600_pwr_call_exit_lp_cb(hw_priv);
+
+	return ret;
+}
+
+static void bes2600_pwr_unlock_device(struct bes2600_common *hw_priv)
+{
+	unsigned long flags;
+
+	/* set device to low power mode */
+	mutex_lock(&hw_priv->bes_power.pwr_mutex);
+
+	bes2600_pwr_lock_tx(hw_priv);
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKED) {
+		hw_priv->bes_power.power_state = POWER_DOWN_STATE_UNLOCKING;
+		hw_priv->bes_power.power_down_task = current;
+		spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+		bes2600_pwr_enter_lp_mode(hw_priv);
+
+		spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+		hw_priv->bes_power.power_down_task = NULL;
+		hw_priv->bes_power.power_state = POWER_DOWN_STATE_UNLOCKED;
+		spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+	} else {
+		spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+	}
+	mutex_unlock(&hw_priv->bes_power.pwr_mutex);
+
+        /* allow system to enter suspend mode */
+        // pm_relax(hw_priv->pdev);
+}
+
+static void bes2600_pwr_lock_device(struct bes2600_common *hw_priv)
+{
+        unsigned long flags;
+
+        /* prevent system from entering suspend mode */
+        // pm_stay_awake(hw_priv->pdev);
+
+        /* wakeup device from low power mode */
+        mutex_lock(&hw_priv->bes_power.pwr_mutex);
+        spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+        if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED) {
+                hw_priv->bes_power.power_state = POWER_DOWN_STATE_LOCKING;
+                hw_priv->bes_power.power_up_task = current;
+                spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+                bes2600_pwr_exit_lp_mode(hw_priv);
+
+                spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+                hw_priv->bes_power.power_up_task = NULL;
+                hw_priv->bes_power.power_state = POWER_DOWN_STATE_LOCKED;
+                spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+        } else {
+                spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+        }
+
+	bes2600_pwr_unlock_tx(hw_priv);
+
+	mutex_unlock(&hw_priv->bes_power.pwr_mutex);
+}
+
+static void bes2600_pwr_trigger_delayed_work(struct bes2600_common *hw_priv)
+{
+	unsigned long max_timeout = 0;
+	bool constant_event_exist = false;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout);
+
+	if(!constant_event_exist && max_timeout > 0) {
+		bes2600_trigger_power_delay_down(&hw_priv->bes_power, max_timeout);
+	}
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+}
+
+static void bes2600_power_down_work(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, bes_power.power_down_work.work);
+
+        unsigned long max_timeout = 0;
+        bool constant_event_exist = false;
+        unsigned long flags;
+
+        spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+        constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout);
+        spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+        if(!constant_event_exist && max_timeout == 0) {
+                /* no pending event, unlock device */
+                bes2600_dbg(BES2600_DBG_PWR, "%s no pending event\n", __func__);
+                bes2600_pwr_unlock_device(hw_priv);
+                wake_up(&hw_priv->bes_power.dev_lp_wq);
+        } else {
+                /* have power busy event, lock device */
+                bes2600_dbg(BES2600_DBG_PWR, "%s have pending event\n", __func__);
+                bes2600_pwr_lock_device(hw_priv);
+
+                /* only have delayed power busy event, restart delayed work */
+                if(!constant_event_exist && max_timeout > 0) {
+                        bes2600_dbg(BES2600_DBG_PWR, "%s restart delayed work\n", __func__);
+                        spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+                        bes2600_trigger_power_delay_down(&hw_priv->bes_power, max_timeout);
+                        spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+                }
+        }
+}
+
+static void bes2600_power_async_work(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, bes_power.power_async_work);
+
+	bes2600_power_down_work(&hw_priv->bes_power.power_down_work.work);
+}
+
+static void bes2600_power_mcu_down_work(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, bes_power.power_mcu_down_work);
+	unsigned long max_timeout = 0;
+	bool constant_event_exist = false;
+	unsigned long flags;
+	enum power_down_state power_state;
+	int ret = 0;
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout);
+	power_state = hw_priv->bes_power.power_state;
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	if(!constant_event_exist &&
+	   max_timeout == 0 &&
+	   power_state == POWER_DOWN_STATE_UNLOCKED) {
+		bes2600_info(BES2600_DBG_PWR, "mcu sleep directly");
+		mutex_lock(&hw_priv->bes_power.pwr_mutex);
+
+		if(hw_priv->sbus_ops->gpio_wake)
+			hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU);
+
+		if(hw_priv->sbus_ops->sbus_active) {
+			ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_MCU);
+			bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, active mcu fail\n", __func__);
+		}
+
+		 if(hw_priv->sbus_ops->sbus_deactive) {
+			ret = hw_priv->sbus_ops->sbus_deactive(hw_priv->sbus_priv, SUBSYSTEM_MCU);
+			bes2600_err_with_cond(ret, BES2600_DBG_PWR, "%s, deactive mcu fail\n", __func__);
+		}
+
+		if(hw_priv->sbus_ops->gpio_sleep)
+			hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU);
+		mutex_unlock(&hw_priv->bes_power.pwr_mutex);
+	}
+}
+
+void bes2600_pwr_init(struct bes2600_common *hw_priv)
+{
+        int i = 0;
+
+        hw_priv->bes_power.power_state = POWER_DOWN_STATE_UNLOCKED;
+        hw_priv->bes_power.hw_priv = hw_priv;
+        spin_lock_init(&hw_priv->bes_power.pwr_lock);
+        INIT_DELAYED_WORK(&hw_priv->bes_power.power_down_work, bes2600_power_down_work);
+        INIT_WORK(&hw_priv->bes_power.power_async_work, bes2600_power_async_work);
+        INIT_WORK(&hw_priv->bes_power.power_mcu_down_work, bes2600_power_mcu_down_work);
+        INIT_LIST_HEAD(&hw_priv->bes_power.async_timeout_list);
+        INIT_LIST_HEAD(&hw_priv->bes_power.pending_event_list);
+        INIT_LIST_HEAD(&hw_priv->bes_power.free_event_list);
+        mutex_init(&hw_priv->bes_power.pwr_cb_mutex);
+        INIT_LIST_HEAD(&hw_priv->bes_power.enter_cb_list);
+        INIT_LIST_HEAD(&hw_priv->bes_power.exit_cb_list);
+        hw_priv->bes_power.pending_lock = false;
+        hw_priv->bes_power.power_down_task = NULL;
+        hw_priv->bes_power.power_up_task = NULL;
+        mutex_init(&hw_priv->bes_power.pwr_mutex);
+        atomic_set(&hw_priv->bes_power.dev_state, 0);
+        init_completion(&hw_priv->bes_power.pm_enter_cmpl);
+        sema_init(&hw_priv->bes_power.sync_lock, 1);
+        device_set_wakeup_capable(hw_priv->pdev, true);
+        init_waitqueue_head(&hw_priv->bes_power.dev_lp_wq);
+
+        for(i = 0; i < BES2600_DELAY_EVENT_NUM; i++) {
+                hw_priv->bes_power.pwr_events[i].idx = i;
+                list_add_tail(&hw_priv->bes_power.pwr_events[i].link, &hw_priv->bes_power.free_event_list);
+        }
+}
+
+void bes2600_pwr_exit(struct bes2600_common *hw_priv)
+{
+	bes2600_pwr_delete_all_cb(hw_priv);
+}
+
+void bes2600_pwr_prepare(struct bes2600_common *hw_priv)
+{
+	/* wait stop operation end */
+	down(&hw_priv->bes_power.sync_lock);
+}
+
+
+void bes2600_pwr_complete(struct bes2600_common *hw_priv)
+{
+	/* notify stop operation end */
+	up(&hw_priv->bes_power.sync_lock);
+}
+
+void bes2600_pwr_start(struct bes2600_common *hw_priv)
+{
+	unsigned long flags;
+
+	if (!bes2600_chrdev_is_signal_mode())
+		return ;
+
+	/* start power management state machine */
+	atomic_set(&hw_priv->bes_power.dev_state, 1);
+	bes2600_dbg(BES2600_DBG_PWR, "start power management.\n");
+
+	/* enable device wakeup function */
+	device_wakeup_enable(hw_priv->pdev);
+
+	/* set power_state to busy and clear state */
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	hw_priv->bes_power.power_state = POWER_DOWN_STATE_LOCKED;
+	hw_priv->bes_power.power_down_task = NULL;
+	hw_priv->bes_power.power_up_task = NULL;
+	hw_priv->bes_power.sys_suspend_task = NULL;
+	hw_priv->bes_power.sys_resume_task = NULL;
+	hw_priv->bes_power.pending_lock = false;
+	hw_priv->bes_power.ap_lp_bad = false;
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	/* set gpio and prevent device from entering sleep mode */
+	if(hw_priv->sbus_ops->gpio_wake)
+		hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_MCU);
+
+	/* start idle timer */
+	queue_delayed_work(hw_priv->workqueue,
+			&hw_priv->bes_power.power_down_work, (HZ * BES2600_POWER_DOWN_DELAY) / 1000);
+}
+
+void bes2600_pwr_stop(struct bes2600_common *hw_priv)
+{
+	unsigned long flags;
+	unsigned long max_timeout;
+
+	if (!bes2600_chrdev_is_signal_mode())
+		return ;
+
+	/* stop power management state machine */
+	atomic_set(&hw_priv->bes_power.dev_state, 0);
+	bes2600_info(BES2600_DBG_PWR, "stop power management.\n");
+
+	/* cancel pending work */
+	cancel_delayed_work_sync(&hw_priv->bes_power.power_down_work);
+	flush_delayed_work(&hw_priv->bes_power.power_down_work);
+	cancel_work_sync(&hw_priv->bes_power.power_async_work);
+	flush_work(&hw_priv->bes_power.power_async_work);
+	cancel_work_sync(&hw_priv->bes_power.power_mcu_down_work);
+	flush_work(&hw_priv->bes_power.power_mcu_down_work);
+
+	/* delete all pending event and clear state */
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout);
+	bes2600_flush_pending_events(&hw_priv->bes_power);
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	/* allow mcu sleep */
+	bes2600_power_down_work(&hw_priv->bes_power.power_down_work.work);
+	bes2600_warn_with_cond(hw_priv->bes_power.power_state != POWER_DOWN_STATE_UNLOCKED,
+				BES2600_DBG_PWR, "power state is not unlocked when stop.\n");
+
+	/* unlock tx if tx is locked */
+	bes2600_pwr_unlock_tx(hw_priv);
+
+	/* disable device wakeup function */
+	device_wakeup_disable(hw_priv->pdev);
+}
+
+bool bes2600_pwr_constant_event_is_pending(struct bes2600_common *hw_priv, u32 event)
+{
+        unsigned long max_timeout = 0;
+        bool constant_event_exist = false;
+        unsigned long flags;
+        bool pending = false;
+        struct bes2600_pwr_event_t *item = NULL;
+
+        spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+        constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout);
+
+        if(constant_event_exist) {
+                BES_PWR_EVENT_SET_CONSTANT(event);
+                if(!list_empty(&hw_priv->bes_power.pending_event_list)) {
+                        list_for_each_entry(item, &hw_priv->bes_power.pending_event_list, link) {
+                                if(item->event == event) {
+                                        pending = true;
+                                        break;
+                                }
+                        }
+                }
+        }
+
+        spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+        return pending;
+}
+
+int bes2600_pwr_set_busy_event(struct bes2600_common *hw_priv, u32 event)
+{
+	int ret = 0;
+	bool need_lock = false;
+	bool need_wait = false;
+	unsigned long flags;
+
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+	       return -1;
+	}
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+
+	/* don't set busy event if the command is for unlocking device */
+	if((event == BES_PWR_LOCK_ON_WSM_TX)
+	   && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING)) {
+		if(hw_priv->bes_power.power_down_task == current) {
+			/* BES_PWR_LOCK_ON_WSM_TX is from power down work */
+			spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+			return 0;
+		}
+	}
+
+	/* don't set busy event also if the command is for locking device */
+	if((event == BES_PWR_LOCK_ON_WSM_TX)
+	   && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKING)) {
+		if(hw_priv->bes_power.power_up_task == current) {
+			/* BES_PWR_LOCK_ON_WSM_TX is from power down work */
+			spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+			return 0;
+		}
+	}
+
+	/* don't set busy event if the command is for suspend/resume */
+	if((event == BES_PWR_LOCK_ON_WSM_TX)
+	   && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED)) {
+		if(hw_priv->bes_power.sys_suspend_task == current ||
+		   hw_priv->bes_power.sys_resume_task == current) {
+			/* BES_PWR_LOCK_ON_WSM_TX is from suspend/resume work */
+			spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+			return 0;
+		}
+	}
+
+	/* set busy event to pending_event_list */
+	BES_PWR_EVENT_SET_CONSTANT(event);
+	bes2600_add_power_delay_event(&hw_priv->bes_power, event, 0);
+
+	/* execute lock device operation or wait lock operation finish */
+	if((hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED)
+	   || (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING)) {
+		need_lock = true;
+	} else if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKING
+	   && (hw_priv->bes_power.power_up_task != current)) {
+		need_wait = true;
+	}
+
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	if(need_wait) {
+		/* lock device is doing, wait operation done */
+		bes2600_info(BES2600_DBG_PWR, "%s wait lock device done, event:%d\n", __func__, BES_PWR_EVENT_NUMBER(event));
+		mutex_lock(&hw_priv->bes_power.pwr_mutex);
+		mutex_unlock(&hw_priv->bes_power.pwr_mutex);
+	}
+
+	if(need_lock) {
+		/* cancel delayed work */
+		cancel_delayed_work_sync(&hw_priv->bes_power.power_down_work);
+		flush_delayed_work(&hw_priv->bes_power.power_down_work);
+
+		bes2600_info(BES2600_DBG_PWR, "%s lock device by event:%d\n", __func__, BES_PWR_EVENT_NUMBER(event));
+		bes2600_pwr_lock_device(hw_priv);
+	}
+
+       return ret;
+}
+
+int bes2600_pwr_set_busy_event_async(struct bes2600_common *hw_priv, u32 event)
+{
+	bool need_lock = false;
+	unsigned long flags;
+
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+		return -1;
+	}
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	BES_PWR_EVENT_SET_CONSTANT(event);
+	bes2600_add_power_delay_event(&hw_priv->bes_power, event, 0);
+	if(((hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED)
+	   || (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING))) {
+		need_lock = true;
+	}
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	if(need_lock && !work_pending(&hw_priv->bes_power.power_async_work)) {
+		bes2600_info(BES2600_DBG_PWR, "%s lock device by event:%d\n", __func__, BES_PWR_EVENT_NUMBER(event));
+		queue_work(hw_priv->workqueue, &hw_priv->bes_power.power_async_work);
+	}
+
+	return 0;
+
+}
+
+int bes2600_pwr_set_busy_event_with_timeout(struct bes2600_common *hw_priv, u32 event, u32 timeout)
+{
+	int ret = 0;
+	bool need_lock = false;
+	bool need_wait = false;
+	unsigned long flags;
+
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+		return -1;
+	}
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+
+	/* add delayed event to pending_event_list */
+	bes2600_add_power_delay_event(&hw_priv->bes_power, event, timeout);
+
+	/* execute lock device operation or wait lock operation finish */
+	if((hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED)
+	   || (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING)) {
+		need_lock = true;
+	} else if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKING
+	   && (hw_priv->bes_power.power_up_task != current)) {
+		need_wait = true;
+	}
+
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	if(need_wait) {
+		/* lock device is doing, wait operation done */
+		bes2600_info(BES2600_DBG_PWR, "%s wait lock device done, event:%d\n", __func__, event);
+		mutex_lock(&hw_priv->bes_power.pwr_mutex);
+		mutex_unlock(&hw_priv->bes_power.pwr_mutex);
+	}
+
+	if(need_lock) {
+		/* cancel delayed work */
+		bes2600_info(BES2600_DBG_PWR, "%s lock device by event:%d\n", __func__, event);
+		cancel_delayed_work_sync(&hw_priv->bes_power.power_down_work);
+		flush_delayed_work(&hw_priv->bes_power.power_down_work);
+
+		bes2600_pwr_lock_device(hw_priv);
+	}
+
+	if(!delayed_work_pending(&hw_priv->bes_power.power_down_work)) {
+		bes2600_pwr_trigger_delayed_work(hw_priv);
+	}
+
+       return ret;
+}
+
+int bes2600_pwr_set_busy_event_with_timeout_async(struct bes2600_common *hw_priv, u32 event, u32 timeout)
+{
+	int ret = 0;
+	bool need_lock = false;
+	unsigned long flags;
+
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+		return -1;
+	}
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	if ((hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED) ||
+	   (hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKED)) {
+		bes2600_add_power_delay_event(&hw_priv->bes_power, event, timeout);
+		if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED) {
+			need_lock = true;
+		}
+	} else if ((hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKING) ||
+	   (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING)) {
+		bes2600_add_async_timeout_power_delay_event(&hw_priv->bes_power, event, timeout);
+		if(hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING) {
+			need_lock = true;
+		}
+	}
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	if(need_lock && !work_pending(&hw_priv->bes_power.power_async_work)) {
+		bes2600_info(BES2600_DBG_PWR, "%s lock device by event:%d\n", __func__, event);
+		queue_work(hw_priv->workqueue, &hw_priv->bes_power.power_async_work);
+	}
+
+       return ret;
+}
+
+
+int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event)
+{
+	int ret = 0;
+	u32 constant_event = event;
+	unsigned long flags;
+
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+		return -1;
+	}
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+
+	/* don't need to clear busy event if the command is for unlocking device */
+	if((event == BES_PWR_LOCK_ON_WSM_TX)
+	   && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKING)) {
+		if(hw_priv->bes_power.power_down_task == current) {
+			/* BES_PWR_LOCK_ON_WSM_TX is from power down work */
+			spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+			return 0;
+		}
+	}
+
+	/* don't need also to clear busy event if the command is for locking device */
+	if((event == BES_PWR_LOCK_ON_WSM_TX)
+	   && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_LOCKING)) {
+		if(hw_priv->bes_power.power_up_task == current) {
+			/* BES_PWR_LOCK_ON_WSM_TX is for powering up operation */
+			spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+			return 0;
+		}
+	}
+
+	 /* don't set busy event if the command is for suspend/resume */
+	if((event == BES_PWR_LOCK_ON_WSM_TX)
+	   && (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED)) {
+		if(hw_priv->bes_power.sys_suspend_task == current ||
+		   hw_priv->bes_power.sys_resume_task == current) {
+			/* BES_PWR_LOCK_ON_WSM_TX is from suspend/resume work */
+			spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+			return 0;
+		}
+	}
+
+	/* change constant event to delay event */
+	BES_PWR_EVENT_SET_CONSTANT(constant_event);
+	if(bes2600_del_pending_event(&hw_priv->bes_power, constant_event)) {
+		bes2600_add_power_delay_event(&hw_priv->bes_power, event, 0);
+	}
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	/* trigger power down delayed work */
+	if(!delayed_work_pending(&hw_priv->bes_power.power_down_work)) {
+		bes2600_dbg(BES2600_DBG_PWR, "%s restart delayed work\n", __func__);
+		bes2600_pwr_trigger_delayed_work(hw_priv);
+	}
+
+	return ret;
+}
+
+void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode)
+{
+	if((psmode & 0x01) != WSM_PSM_ACTIVE) {
+		bes2600_dbg(BES2600_DBG_PWR, "complete pm_enter_cmpl\n");
+		complete(&hw_priv->bes_power.pm_enter_cmpl);
+	}
+}
+
+bool bes2600_pwr_device_is_idle(struct bes2600_common *hw_priv)
+{
+    bool idle = false;
+    unsigned long flags;
+
+    if (!bes2600_chrdev_is_signal_mode())
+		return false;
+
+    spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+    idle = (hw_priv->bes_power.power_state == POWER_DOWN_STATE_UNLOCKED);
+    spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+    return idle;
+}
+
+void bes2600_pwr_register_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb)
+{
+	struct bes2600_pwr_enter_cb_item *item = NULL;
+
+	mutex_lock(&hw_priv->bes_power.pwr_cb_mutex);
+	list_for_each_entry(item, &hw_priv->bes_power.enter_cb_list, link) {
+		if(item->cb == cb) {
+			bes2600_warn(BES2600_DBG_PWR, "the enter cb is already exist\n");
+			mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex);
+			return;
+		}
+	}
+
+	item = kzalloc(sizeof(struct bes2600_pwr_enter_cb_item), GFP_KERNEL);
+	if(item) {
+		item->cb = cb;
+		list_add_tail(&item->link, &hw_priv->bes_power.enter_cb_list);
+	}
+	mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex);
+
+	bes2600_err_with_cond(item == NULL, BES2600_DBG_PWR, "register en_lp_cb fail.\n");
+}
+
+void bes2600_pwr_unregister_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb)
+{
+	struct bes2600_pwr_enter_cb_item *item = NULL, *temp = NULL;
+
+	mutex_lock(&hw_priv->bes_power.pwr_cb_mutex);
+	list_for_each_entry_safe(item, temp, &hw_priv->bes_power.enter_cb_list, link) {
+		if(cb == item->cb) {
+			list_del(&item->link);
+			kfree(item);
+			break;
+		}
+	}
+	mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex);
+}
+
+void bes2600_pwr_register_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb)
+{
+	struct bes2600_pwr_exit_cb_item *item = NULL;
+
+	mutex_lock(&hw_priv->bes_power.pwr_cb_mutex);
+	list_for_each_entry(item, &hw_priv->bes_power.exit_cb_list, link) {
+		if(item->cb == cb) {
+			mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex);
+			bes2600_warn(BES2600_DBG_PWR, "the exit cb is already exist\n");
+			return;
+		}
+	}
+
+	item = kzalloc(sizeof(struct bes2600_pwr_exit_cb_item), GFP_KERNEL);
+	if(item) {
+		item->cb = cb;
+		list_add_tail(&item->link, &hw_priv->bes_power.exit_cb_list);
+	}
+	mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex);
+
+	bes2600_err_with_cond(item == NULL, BES2600_DBG_PWR, "register en_lp_cb fail.\n");
+}
+
+void bes2600_pwr_unregister_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb)
+{
+	struct bes2600_pwr_exit_cb_item *item = NULL, *temp = NULL;
+
+	mutex_lock(&hw_priv->bes_power.pwr_cb_mutex);
+	list_for_each_entry_safe(item, temp, &hw_priv->bes_power.exit_cb_list, link) {
+		if(cb == item->cb) {
+			list_del(&item->link);
+			kfree(item);
+			break;
+		}
+	}
+	mutex_unlock(&hw_priv->bes_power.pwr_cb_mutex);
+}
+
+void bes2600_pwr_suspend_start(struct bes2600_common *hw_priv)
+{
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+		return;
+	}
+
+        mutex_lock(&hw_priv->bes_power.pwr_mutex);
+        hw_priv->bes_power.sys_suspend_task = current;
+        bes2600_pwr_device_exit_lp_mode(hw_priv);
+        mutex_unlock(&hw_priv->bes_power.pwr_mutex);
+}
+
+void bes2600_pwr_suspend_end(struct bes2600_common *hw_priv)
+{
+	unsigned long max_timeout = 0;
+	bool constant_event_exist = false;
+	unsigned long flags;
+
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+		return;
+	}
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout);
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	mutex_lock(&hw_priv->bes_power.pwr_mutex);
+	if(!constant_event_exist && max_timeout == 0)
+		bes2600_pwr_device_enter_lp_mode(hw_priv);
+	hw_priv->bes_power.sys_suspend_task = NULL;
+	mutex_unlock(&hw_priv->bes_power.pwr_mutex);
+}
+
+void bes2600_pwr_resume_start(struct bes2600_common *hw_priv)
+{
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+		return;
+	}
+
+        mutex_lock(&hw_priv->bes_power.pwr_mutex);
+        hw_priv->bes_power.sys_resume_task = current;
+        bes2600_pwr_device_exit_lp_mode(hw_priv);
+        mutex_unlock(&hw_priv->bes_power.pwr_mutex);
+}
+
+void bes2600_pwr_resume_end(struct bes2600_common *hw_priv)
+{
+	unsigned long max_timeout = 0;
+	bool constant_event_exist = false;
+	unsigned long flags;
+
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+		return ;
+	}
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	constant_event_exist = bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout);
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	mutex_lock(&hw_priv->bes_power.pwr_mutex);
+	if(!constant_event_exist && max_timeout == 0)
+		bes2600_pwr_device_enter_lp_mode(hw_priv);
+	hw_priv->bes_power.sys_resume_task = NULL;
+	mutex_unlock(&hw_priv->bes_power.pwr_mutex);
+}
+
+void bes2600_pwr_mcu_sleep_directly(struct bes2600_common *hw_priv)
+{
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+		return;
+	}
+
+	if (bes2600_pwr_device_is_idle(hw_priv)) {
+		if(!work_pending(&hw_priv->bes_power.power_mcu_down_work)) {
+			queue_work(hw_priv->workqueue, &hw_priv->bes_power.power_mcu_down_work);
+		}
+	}
+}
+
+void bes2600_pwr_mark_ap_lp_bad(struct bes2600_common *hw_priv)
+{
+	if(atomic_read(&hw_priv->bes_power.dev_state) == 0) {
+		return;
+	}
+
+	bes2600_pwr_set_busy_event_with_timeout(hw_priv, BES_PWR_LOCK_ON_AP_LP_BAD, 100);
+	hw_priv->bes_power.ap_lp_bad = true;
+	bes2600_info(BES2600_DBG_PWR, "mark ap_lp_bad flag\n");
+}
+
+void bes2600_pwr_clear_ap_lp_bad_mark(struct bes2600_common *hw_priv)
+{
+	hw_priv->bes_power.ap_lp_bad = false;
+	bes2600_info(BES2600_DBG_PWR, "clear ap_lp_bad flag\n");
+}
+
+int bes2600_pwr_busy_event_dump(struct bes2600_common *hw_priv, char *buffer, u32 buf_len)
+{
+	unsigned long max_timeout = 0;
+	int used_len = 0;
+	struct bes2600_pwr_event_t *item = NULL;
+	unsigned long flags;
+
+	if(!buffer) {
+		return -1;
+	}
+
+	spin_lock_irqsave(&hw_priv->bes_power.pwr_lock, flags);
+	bes2600_update_power_delay_events(&hw_priv->bes_power, &max_timeout);
+	used_len = snprintf(buffer, buf_len, "Event    \t\tFlag\t\ttimeout(ticks)\n");
+	if(!list_empty(&hw_priv->bes_power.pending_event_list)) {
+		list_for_each_entry(item, &hw_priv->bes_power.pending_event_list, link) {
+					used_len += snprintf(buffer + used_len, buf_len - used_len, "%9s\t\t%s\t\t%lu\n",
+					bes2600_get_pwr_busy_event_name(item),
+					BES_PWR_IS_CONSTANT_EVENT(item->event) ? "C" : "D",
+					bes2600_get_pwr_busy_event_timeout(item));
+		}
+	}
+	spin_unlock_irqrestore(&hw_priv->bes_power.pwr_lock, flags);
+
+	return 0;
+}
diff --git a/drivers/staging/bes2600/bes_pwr.h b/drivers/staging/bes2600/bes_pwr.h
new file mode 100644
index 000000000000..2c27f4293a55
--- /dev/null
+++ b/drivers/staging/bes2600/bes_pwr.h
@@ -0,0 +1,167 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __BES_PWR_H__
+#define __BES_PWR_H__
+
+#include "bes2600.h"
+
+enum bes2600_pwr_event_type {
+        BES_PWR_LOCK_ON_SCAN = 1,
+        BES_PWR_LOCK_ON_JOIN,
+        BES_PWR_LOCK_ON_TX,
+        BES_PWR_LOCK_ON_RX,
+        BES_PWR_LOCK_ON_FLUSH,
+        BES_PWR_LOCK_ON_ROC,
+        BES_PWR_LOCK_ON_WSM_TX,
+        BES_PWR_LOCK_ON_WSM_OPER,
+        BES_PWR_LOCK_ON_BSS_LOST,
+        BES_PWR_LOCK_ON_GET_IP,
+        BES_PWR_LOCK_ON_PS_ACTIVE,
+        BES_PWR_LOCK_ON_LMAC_RSP,
+        BES_PWR_LOCK_ON_AP,
+        BES_PWR_LOCK_ON_TEST_CMD,
+        BES_PWR_LOCK_ON_MUL_REQ,
+        BES_PWR_LOCK_ON_ADV_SCAN,
+        BES_PWR_LOCK_ON_DISCON,
+        BES_PWR_LOCK_ON_QUEUE_GC,
+        BES_PWR_LOCK_ON_AP_LP_BAD,
+        /* add new lock event here */
+        BES_PWR_LOCK_ON_EVENT_MAX,
+};
+
+#define BES_PWR_IS_CONSTANT_EVENT(x)    ((x) & 0x80000000)
+#define BES_PWR_EVENT_SET_CONSTANT(x)   (x = (x | 0x80000000))
+#define BES_PWR_EVENT_NUMBER(x)         ((x) & 0x7fffffff)
+
+#define BES2600_POWER_DOWN_DELAY        50       // unit in millisecond
+#define BES2600_DELAY_EVENT_NUM         (BES_PWR_LOCK_ON_EVENT_MAX << 1)
+
+#define BES_PWR_EVENT_TX_TIMEOUT        1500       // unit in millisecond
+#define BES_PWR_EVENT_RX_TIMEOUT        100        // unit in millisecond
+
+struct bes2600_pwr_event_t
+{
+        struct list_head link;
+        unsigned long timeout;
+        unsigned long delay;
+        u8 idx;
+        enum bes2600_pwr_event_type event;
+};
+
+enum power_down_state
+{
+        POWER_DOWN_STATE_LOCKED = 0,
+        POWER_DOWN_STATE_LOCKING,
+        POWER_DOWN_STATE_UNLOCKING,
+        POWER_DOWN_STATE_UNLOCKED,
+};
+
+typedef void (*bes_pwr_enter_lp_cb)(struct bes2600_common *hw_priv);
+typedef void (*bes_pwr_exit_lp_cb)(struct bes2600_common *hw_priv);
+
+struct bes2600_pwr_enter_cb_item
+{
+        struct list_head link;
+        bes_pwr_enter_lp_cb cb;
+};
+
+struct bes2600_pwr_exit_cb_item
+{
+        struct list_head link;
+        bes_pwr_exit_lp_cb cb;
+};
+
+struct bes2600_pwr_t
+{
+        spinlock_t pwr_lock;
+        struct delayed_work power_down_work;
+        struct work_struct power_async_work;
+        struct work_struct power_mcu_down_work;
+        struct list_head async_timeout_list;
+        struct list_head pending_event_list;
+        struct list_head free_event_list;
+        enum power_down_state power_state;
+        struct task_struct *power_down_task;
+        struct task_struct *power_up_task;
+        struct task_struct *sys_suspend_task;
+        struct task_struct *sys_resume_task;
+        struct semaphore sync_lock;
+        bool pending_lock;
+        atomic_t dev_state;
+        struct mutex pwr_mutex;
+        struct bes2600_common *hw_priv;
+        struct completion pm_enter_cmpl;
+        struct mutex pwr_cb_mutex;
+        struct list_head enter_cb_list;
+        struct list_head exit_cb_list;
+        wait_queue_head_t dev_lp_wq;
+        bool ap_lp_bad;
+        struct bes2600_pwr_event_t pwr_events[BES2600_DELAY_EVENT_NUM];
+        atomic_t pm_set_in_process;
+};
+
+#ifdef CONFIG_BES2600_WOWLAN
+void bes2600_pwr_init(struct bes2600_common *hw_priv);
+void bes2600_pwr_exit(struct bes2600_common *hw_priv);
+void bes2600_pwr_prepare(struct bes2600_common *hw_priv);
+void bes2600_pwr_complete(struct bes2600_common *hw_priv);
+void bes2600_pwr_start(struct bes2600_common *hw_priv);
+void bes2600_pwr_stop(struct bes2600_common *hw_priv);
+bool bes2600_pwr_constant_event_is_pending(struct bes2600_common *hw_priv, u32 event);
+int bes2600_pwr_set_busy_event(struct bes2600_common *hw_priv, u32 event);
+int bes2600_pwr_set_busy_event_async(struct bes2600_common *hw_priv, u32 event);
+int bes2600_pwr_set_busy_event_with_timeout(struct bes2600_common *hw_priv, u32 event, u32 timeout);
+int bes2600_pwr_set_busy_event_with_timeout_async(struct bes2600_common *hw_priv, u32 event, u32 timeout);
+int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event);
+void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode);
+bool bes2600_pwr_device_is_idle(struct bes2600_common *hw_priv);
+void bes2600_pwr_register_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb);
+void bes2600_pwr_unregister_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb);
+void bes2600_pwr_register_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb);
+void bes2600_pwr_unregister_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb);
+void bes2600_pwr_suspend_start(struct bes2600_common *hw_priv);
+void bes2600_pwr_suspend_end(struct bes2600_common *hw_priv);
+void bes2600_pwr_resume_start(struct bes2600_common *hw_priv);
+void bes2600_pwr_resume_end(struct bes2600_common *hw_priv);
+void bes2600_pwr_mcu_sleep_directly(struct bes2600_common *hw_priv);
+void bes2600_pwr_mark_ap_lp_bad(struct bes2600_common *hw_priv);
+void bes2600_pwr_clear_ap_lp_bad_mark(struct bes2600_common *hw_priv);
+int bes2600_pwr_busy_event_dump(struct bes2600_common *hw_priv, char *buffer, u32 buf_len);
+#else
+static inline void bes2600_pwr_init(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_exit(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_prepare(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_complete(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_start(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_stop(struct bes2600_common *hw_priv) { }
+static inline bool bes2600_pwr_constant_event_is_pending(struct bes2600_common *hw_priv, u32 event) { return false; }
+static inline int bes2600_pwr_set_busy_event(struct bes2600_common *hw_priv, u32 event) { return 0; }
+static inline int bes2600_pwr_set_busy_event_async(struct bes2600_common *hw_priv, u32 event) {return 0; }
+static inline int bes2600_pwr_set_busy_event_with_timeout(struct bes2600_common *hw_priv, u32 event, u32 timeout) { return 0; }
+static inline int bes2600_pwr_set_busy_event_with_timeout_async(struct bes2600_common *hw_priv, u32 event, u32 timeout) { return 0; }
+static inline int bes2600_pwr_clear_busy_event(struct bes2600_common *hw_priv, u32 event) { return 0; }
+static inline void bes2600_pwr_notify_ps_changed(struct bes2600_common *hw_priv, u8 psmode) { }
+static inline bool bes2600_pwr_device_is_idle(struct bes2600_common *hw_priv) { return false; }
+static inline void bes2600_pwr_register_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { }
+static inline void bes2600_pwr_unregister_en_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { }
+static inline void bes2600_pwr_register_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { }
+static inline void bes2600_pwr_unregister_exit_lp_cb(struct bes2600_common *hw_priv, bes_pwr_enter_lp_cb cb) { }
+static inline void bes2600_pwr_suspend_start(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_suspend_end(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_resume_start(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_resume_end(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_mcu_sleep_directly(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_mark_ap_lp_bad(struct bes2600_common *hw_priv) { }
+static inline void bes2600_pwr_clear_ap_lp_bad_mark(struct bes2600_common *hw_priv) { }
+static inline int bes2600_pwr_busy_event_dump(struct bes2600_common *hw_priv, char *buffer, u32 buf_len) { return 0; }
+#endif
+
+#endif
\ No newline at end of file
diff --git a/drivers/staging/bes2600/bh.c b/drivers/staging/bes2600/bh.c
new file mode 100644
index 000000000000..fed5101d42c6
--- /dev/null
+++ b/drivers/staging/bes2600/bh.c
@@ -0,0 +1,1654 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <net/mac80211.h>
+#include <linux/kthread.h>
+#include <uapi/linux/ip.h>
+#include <uapi/linux/tcp.h>
+#include <uapi/linux/udp.h>
+
+#include "bes2600.h"
+#include "bh.h"
+#include "hwio.h"
+#include "wsm.h"
+#include "sbus.h"
+#include "debug.h"
+#include "epta_coex.h"
+#include "bes_chardev.h"
+#include "txrx_opt.h"
+#include "sta.h"
+
+static int bes2600_bh(void *arg);
+
+#ifdef CONFIG_BES2600_WLAN_SDIO
+extern void sdio_work_debug(struct sbus_priv *self);
+#endif
+
+/* TODO: Verify these numbers with WSM specification. */
+#define DOWNLOAD_BLOCK_SIZE_WR	(0x1000 - 4)
+/* an SPI message cannot be bigger than (2"12-1)*2 bytes
+ * "*2" to cvt to bytes */
+#define MAX_SZ_RD_WR_BUFFERS	(DOWNLOAD_BLOCK_SIZE_WR*2)
+#define PIGGYBACK_CTRL_REG	(2)
+#define EFFECTIVE_BUF_SIZE	(MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
+
+/* Suspend state privates */
+enum bes2600_bh_pm_state {
+	BES2600_BH_RESUMED = 0,
+	BES2600_BH_SUSPEND,
+	BES2600_BH_SUSPENDED,
+	BES2600_BH_RESUME,
+};
+
+typedef int (*bes2600_wsm_handler)(struct bes2600_common *hw_priv,
+	u8 *data, size_t size);
+
+#ifdef MCAST_FWDING
+int wsm_release_buffer_to_fw(struct bes2600_vif *priv, int count);
+#endif
+
+static void bes2600_bh_work(struct work_struct *work)
+{
+	struct bes2600_common *priv =
+	container_of(work, struct bes2600_common, bh_work);
+	bes2600_bh(priv);
+}
+
+int bes2600_register_bh(struct bes2600_common *hw_priv)
+{
+	int err = 0;
+	/* Realtime workqueue */
+	hw_priv->bh_workqueue = alloc_workqueue("bes2600_bh",
+				WQ_MEM_RECLAIM | WQ_HIGHPRI
+				| WQ_CPU_INTENSIVE, 1);
+
+	if (!hw_priv->bh_workqueue)
+		return -ENOMEM;
+
+	INIT_WORK(&hw_priv->bh_work, bes2600_bh_work);
+
+	bes2600_info(BES2600_DBG_BH, "[BH] register.\n");
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+#ifdef WIFI_BT_COEXIST_EPTA_FDD
+	coex_init_mode(hw_priv, WIFI_COEX_MODE_FDD_BIT);
+#else
+	coex_init_mode(hw_priv, 0);
+#endif
+#endif
+	atomic_set(&hw_priv->bh_rx, 0);
+	atomic_set(&hw_priv->bh_tx, 0);
+	atomic_set(&hw_priv->bh_term, 0);
+	atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED);
+	hw_priv->buf_id_tx = 0;
+	hw_priv->buf_id_rx = 0;
+	init_waitqueue_head(&hw_priv->bh_wq);
+	init_waitqueue_head(&hw_priv->bh_evt_wq);
+
+	err = !queue_work(hw_priv->bh_workqueue, &hw_priv->bh_work);
+	WARN_ON(err);
+	return err;
+}
+
+void bes2600_unregister_bh(struct bes2600_common *hw_priv)
+{
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	coex_deinit_mode(hw_priv);
+#endif
+
+	atomic_add(1, &hw_priv->bh_term);
+	wake_up(&hw_priv->bh_wq);
+
+	flush_workqueue(hw_priv->bh_workqueue);
+
+	destroy_workqueue(hw_priv->bh_workqueue);
+	hw_priv->bh_workqueue = NULL;
+
+	bes2600_info(BES2600_DBG_BH, "[BH] unregistered.\n");
+}
+
+void bes2600_irq_handler(struct bes2600_common *hw_priv)
+{
+	bes2600_dbg(BES2600_DBG_BH, "[BH] irq.\n");
+	if(!hw_priv) {
+		bes2600_warn(BES2600_DBG_BH, "%s hw private data is null", __func__);
+		return;
+	}
+
+	if (hw_priv->bh_error) {
+		bes2600_err(BES2600_DBG_BH, "%s bh error", __func__);
+		return;
+	}
+
+	if (atomic_add_return(1, &hw_priv->bh_rx) == 1)
+		wake_up(&hw_priv->bh_wq);
+}
+EXPORT_SYMBOL(bes2600_irq_handler);
+
+void bes2600_bh_wakeup(struct bes2600_common *hw_priv)
+{
+	bes2600_dbg(BES2600_DBG_BH,  "[BH] wakeup.\n");
+	if (WARN_ON(hw_priv->bh_error))
+		return;
+
+	if (atomic_add_return(1, &hw_priv->bh_tx) == 1)
+		wake_up(&hw_priv->bh_wq);
+}
+EXPORT_SYMBOL(bes2600_bh_wakeup);
+
+int bes2600_bh_suspend(struct bes2600_common *hw_priv)
+{
+#ifdef MCAST_FWDING
+	int i =0;
+	struct bes2600_vif *priv = NULL;
+#endif
+
+	bes2600_dbg(BES2600_DBG_BH, "[BH] suspend.\n");
+	if (hw_priv->bh_error) {
+		wiphy_warn(hw_priv->hw->wiphy, "BH error -- can't suspend\n");
+		return -EINVAL;
+	}
+
+#ifdef MCAST_FWDING
+ 	bes2600_for_each_vif(hw_priv, priv, i) {
+		if (!priv)
+			continue;
+		if ( (priv->multicast_filter.enable)
+			&& (priv->join_status == BES2600_JOIN_STATUS_AP) ) {
+			wsm_release_buffer_to_fw(priv,
+				(hw_priv->wsm_caps.numInpChBufs - 1));
+			break;
+		}
+	}
+#endif
+
+	atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPEND);
+	wake_up(&hw_priv->bh_wq);
+	return wait_event_timeout(hw_priv->bh_evt_wq, hw_priv->bh_error ||
+		(BES2600_BH_SUSPENDED == atomic_read(&hw_priv->bh_suspend)),
+		 1 * HZ) ? 0 : -ETIMEDOUT;
+}
+EXPORT_SYMBOL(bes2600_bh_suspend);
+
+int bes2600_bh_resume(struct bes2600_common *hw_priv)
+{
+	int ret;
+
+#ifdef MCAST_FWDING
+	int i =0;
+	struct bes2600_vif *priv = NULL;
+#endif
+
+	bes2600_dbg(BES2600_DBG_BH, "[BH] resume.\n");
+	if (hw_priv->bh_error) {
+		wiphy_warn(hw_priv->hw->wiphy, "BH error -- can't resume\n");
+		return -EINVAL;
+	}
+
+	atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUME);
+	wake_up(&hw_priv->bh_wq);
+	ret = wait_event_timeout(hw_priv->bh_evt_wq, hw_priv->bh_error ||
+		(BES2600_BH_RESUMED == atomic_read(&hw_priv->bh_suspend)),
+		1 * HZ) ? 0 : -ETIMEDOUT;
+
+#ifdef MCAST_FWDING
+	bes2600_for_each_vif(hw_priv, priv, i) {
+		if (!priv)
+			continue;
+		if ((priv->join_status == BES2600_JOIN_STATUS_AP)
+				&& (priv->multicast_filter.enable)) {
+			u8 count = 0;
+			WARN_ON(wsm_request_buffer_request(priv, &count));
+			bes2600_dbg(BES2600_DBG_BH, "[BH] BH resume. Reclaim Buff %d \n",count);
+			break;
+		}
+	}
+#endif
+
+	return ret;
+}
+EXPORT_SYMBOL(bes2600_bh_resume);
+
+static inline void wsm_alloc_tx_buffer(struct bes2600_common *hw_priv)
+{
+	++hw_priv->hw_bufs_used;
+}
+
+int wsm_release_tx_buffer(struct bes2600_common *hw_priv, int count)
+{
+	int ret = 0;
+	int hw_bufs_used = hw_priv->hw_bufs_used;
+
+	hw_priv->hw_bufs_used -= count;
+
+	if (WARN_ON(hw_priv->hw_bufs_used < 0))
+		ret = -1;
+	/* Tx data patch stops when all but one hw buffers are used.
+	   So, re-start tx path in case we find hw_bufs_used equals
+	   numInputChBufs - 1.
+	 */
+	else if (hw_bufs_used >= (hw_priv->wsm_caps.numInpChBufs - 1))
+		ret = 1;
+	if (!hw_priv->hw_bufs_used) {
+		bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_LMAC_RSP);
+		wake_up(&hw_priv->bh_evt_wq);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(wsm_release_tx_buffer);
+
+int wsm_release_vif_tx_buffer(struct bes2600_common *hw_priv, int if_id,
+				int count)
+{
+	int ret = 0;
+
+	hw_priv->hw_bufs_used_vif[if_id] -= count;
+
+	if (!hw_priv->hw_bufs_used_vif[if_id])
+		wake_up(&hw_priv->bh_evt_wq);
+
+	if (WARN_ON(hw_priv->hw_bufs_used_vif[if_id] < 0))
+		ret = -1;
+	return ret;
+}
+#ifdef MCAST_FWDING
+int wsm_release_buffer_to_fw(struct bes2600_vif *priv, int count)
+{
+	int i;
+	u8 flags;
+	struct wsm_buf *buf;
+	size_t buf_len;
+	struct wsm_hdr *wsm;
+	struct bes2600_common *hw_priv = priv->hw_priv;
+
+#if 1
+	if (priv->join_status != BES2600_JOIN_STATUS_AP) {
+		return 0;
+	}
+#endif
+	bes2600_dbg(BES2600_DBG_BH, "Rel buffer to FW %d, %d\n", count, hw_priv->hw_bufs_used);
+
+	for (i = 0; i < count; i++) {
+		if ((hw_priv->hw_bufs_used + 1) < hw_priv->wsm_caps.numInpChBufs) {
+			flags = i ? 0: 0x1;
+
+			wsm_alloc_tx_buffer(hw_priv);
+
+			buf = &hw_priv->wsm_release_buf[i];
+			buf_len = buf->data - buf->begin;
+
+			/* Add sequence number */
+			wsm = (struct wsm_hdr *)buf->begin;
+			BUG_ON(buf_len < sizeof(*wsm));
+
+			wsm->id &= __cpu_to_le32(
+				~WSM_TX_SEQ(WSM_TX_SEQ_MAX));
+			wsm->id |= cpu_to_le32(
+				WSM_TX_SEQ(hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)]));
+
+			bes2600_dbg(BES2600_DBG_BH, "REL %d\n", hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)]);
+			if (WARN_ON(bes2600_data_write(hw_priv,
+				buf->begin, buf_len))) {
+				break;
+			}
+			hw_priv->buf_released = 1;
+			hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)] =
+				(hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)] + 1) & WSM_TX_SEQ_MAX;
+		} else
+			break;
+	}
+
+	if (i == count) {
+		return 0;
+	}
+
+	/* Should not be here */
+	bes2600_err(BES2600_DBG_BH, "[BH] Less HW buf %d,%d.\n", hw_priv->hw_bufs_used,
+			hw_priv->wsm_caps.numInpChBufs);
+	WARN_ON(1);
+
+	return -1;
+}
+#endif
+
+#if 0
+static struct sk_buff *bes2600_get_skb(struct bes2600_common *hw_priv, size_t len)
+{
+	struct sk_buff *skb;
+	size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE;
+
+	if (len > SDIO_BLOCK_SIZE || !hw_priv->skb_cache) {
+		skb = dev_alloc_skb(alloc_len
+				+ WSM_TX_EXTRA_HEADROOM
+				+ 8  /* TKIP IV */
+				+ 12 /* TKIP ICV + MIC */
+				- 2  /* Piggyback */);
+		/* In AP mode RXed SKB can be looped back as a broadcast.
+		 * Here we reserve enough space for headers. */
+		skb_reserve(skb, WSM_TX_EXTRA_HEADROOM
+				+ 8 /* TKIP IV */
+				- WSM_RX_EXTRA_HEADROOM);
+	} else {
+		skb = hw_priv->skb_cache;
+		hw_priv->skb_cache = NULL;
+	}
+	return skb;
+}
+
+static void bes2600_put_skb(struct bes2600_common *hw_priv, struct sk_buff *skb)
+{
+	if (hw_priv->skb_cache)
+		dev_kfree_skb(skb);
+	else
+		hw_priv->skb_cache = skb;
+}
+
+static int bes2600_bh_read_ctrl_reg(struct bes2600_common *hw_priv,
+					  u16 *ctrl_reg)
+{
+	int ret;
+
+	ret = bes2600_reg_read_16(hw_priv,
+			ST90TDS_CONTROL_REG_ID, ctrl_reg);
+	if (ret) {
+		ret = bes2600_reg_read_16(hw_priv,
+				ST90TDS_CONTROL_REG_ID, ctrl_reg);
+		if (ret)
+			bes2600_err(BES2600_DBG_BH, "[BH] Failed to read control register.\n");
+	}
+
+	return ret;
+}
+
+static int bes2600_device_wakeup(struct bes2600_common *hw_priv)
+{
+	u16 ctrl_reg;
+	int ret;
+
+	bes2600_dbg(BES2600_DBG_BH, "[BH] Device wakeup.\n");
+
+	/* To force the device to be always-on, the host sets WLAN_UP to 1 */
+	ret = bes2600_reg_write_16(hw_priv, ST90TDS_CONTROL_REG_ID,
+			ST90TDS_CONT_WUP_BIT);
+	if (WARN_ON(ret))
+		return ret;
+
+	ret = bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
+	if (WARN_ON(ret))
+		return ret;
+
+	/* If the device returns WLAN_RDY as 1, the device is active and will
+	 * remain active. */
+	if (ctrl_reg & ST90TDS_CONT_RDY_BIT) {
+		bes2600_dbg(BES2600_DBG_BH, "[BH] Device awake.\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+#endif
+
+/* Must be called from BH thraed. */
+void bes2600_enable_powersave(struct bes2600_vif *priv,
+			     bool enable)
+{
+	bes2600_dbg(BES2600_DBG_BH, "[BH] Powerave is %s.\n",
+			enable ? "enabled" : "disabled");
+	priv->powersave_enabled = enable;
+}
+
+#if 0
+#define INTERRUPT_WORKAROUND
+static int bes2600_bh(void *arg)
+{
+	struct bes2600_common *hw_priv = arg;
+	struct bes2600_vif *priv = NULL;
+	struct sk_buff *skb_rx = NULL;
+	size_t read_len = 0;
+	int rx, tx, term, suspend;
+	struct wsm_hdr *wsm;
+	size_t wsm_len;
+	int wsm_id;
+	u8 wsm_seq;
+	int rx_resync = 1;
+	u16 ctrl_reg = 0;
+	int tx_allowed;
+	int pending_tx = 0;
+	int tx_burst;
+	int rx_burst = 0;
+	long status;
+#if defined(CONFIG_BES2600_WSM_DUMPS)
+	size_t wsm_dump_max = -1;
+#endif
+	u32 dummy;
+	bool powersave_enabled;
+	int i;
+	int vif_selected;
+
+	for (;;) {
+		powersave_enabled = 1;
+		spin_lock(&hw_priv->vif_list_lock);
+		bes2600_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+			if ((i = (CW12XX_MAX_VIFS - 1)) || !priv)
+#else
+			if (!priv)
+#endif
+				continue;
+			powersave_enabled &= !!priv->powersave_enabled;
+		}
+		spin_unlock(&hw_priv->vif_list_lock);
+		if (!hw_priv->hw_bufs_used
+				&& powersave_enabled
+				&& !hw_priv->device_can_sleep
+				&& !atomic_read(&hw_priv->recent_scan)) {
+			status = HZ/8;
+			bes2600_dbg(BES2600_DBG_BH, "[BH] No Device wakedown.\n");
+#ifndef FPGA_SETUP
+			WARN_ON(bes2600_reg_write_16(hw_priv,
+						ST90TDS_CONTROL_REG_ID, 0));
+			hw_priv->device_can_sleep = true;
+#endif
+		} else if (hw_priv->hw_bufs_used)
+			/* Interrupt loss detection */
+			status = HZ/8;
+		else
+			status = HZ/8;
+
+		/* Dummy Read for SDIO retry mechanism*/
+		if (((atomic_read(&hw_priv->bh_rx) == 0) &&
+				(atomic_read(&hw_priv->bh_tx) == 0)))
+			bes2600_reg_read(hw_priv, ST90TDS_CONFIG_REG_ID,
+					&dummy, sizeof(dummy));
+#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT)
+		wsm_dump_max = hw_priv->wsm_dump_max_size;
+#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */
+
+#ifdef INTERRUPT_WORKAROUND
+				/* If a packet has already been txed to the device then read the
+				   control register for a probable interrupt miss before going
+				   further to wait for interrupt; if the read length is non-zero
+				   then it means there is some data to be received */
+				if (hw_priv->hw_bufs_used) {
+					bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
+					if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
+					{
+						rx = 1;
+						goto test;
+					}
+				}
+#endif
+
+		status = wait_event_interruptible_timeout(hw_priv->bh_wq, ({
+				rx = atomic_xchg(&hw_priv->bh_rx, 0);
+				tx = atomic_xchg(&hw_priv->bh_tx, 0);
+				term = atomic_xchg(&hw_priv->bh_term, 0);
+				suspend = pending_tx ?
+					0 : atomic_read(&hw_priv->bh_suspend);
+				(rx || tx || term || suspend || hw_priv->bh_error);
+			}), status);
+
+		if (status < 0 || term || hw_priv->bh_error)
+			break;
+
+#ifdef INTERRUPT_WORKAROUND
+		if (!status) {
+			bes2600_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
+			if(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
+			{
+				bes2600_err(BES2600_DBG_BH, "MISS 1\n");
+				rx = 1;
+				goto test;
+			}
+		}
+#endif
+		if (!status && hw_priv->hw_bufs_used) {
+			unsigned long timestamp = jiffies;
+			long timeout;
+			bool pending = false;
+			int i;
+
+			wiphy_warn(hw_priv->hw->wiphy, "Missed interrupt?\n");
+			rx = 1;
+
+			/* Get a timestamp of "oldest" frame */
+			for (i = 0; i < 4; ++i)
+				pending |= bes2600_queue_get_xmit_timestamp(
+						&hw_priv->tx_queue[i],
+						&timestamp, -1,
+						hw_priv->pending_frame_id);
+
+			/* Check if frame transmission is timed out.
+			 * Add an extra second with respect to possible
+			 * interrupt loss. */
+			timeout = timestamp +
+					WSM_CMD_LAST_CHANCE_TIMEOUT +
+					1 * HZ  -
+					jiffies;
+
+			/* And terminate BH tread if the frame is "stuck" */
+			if (pending && timeout < 0) {
+				//wiphy_warn(priv->hw->wiphy,
+				//	"Timeout waiting for TX confirm.\n");
+				bes2600_info(BES2600_DBG_BH, "bes2600_bh: Timeout waiting for TX confirm.\n");
+				break;
+			}
+
+#if defined(CONFIG_BES2600_DUMP_ON_ERROR)
+			BUG_ON(1);
+#endif /* CONFIG_BES2600_DUMP_ON_ERROR */
+		} else if (!status) {
+			if (!hw_priv->device_can_sleep
+					&& !atomic_read(&hw_priv->recent_scan)) {
+				bes2600_dbg(BES2600_DBG_BH, "[BH] Device wakedown. Timeout.\n");
+#ifndef FPGA_SETUP
+				WARN_ON(bes2600_reg_write_16(hw_priv,
+						ST90TDS_CONTROL_REG_ID, 0));
+				hw_priv->device_can_sleep = true;
+#endif
+			}
+			continue;
+		} else if (suspend) {
+			bes2600_dbg(BES2600_DBG_BH, "[BH] Device suspend.\n");
+			powersave_enabled = 1;
+			spin_lock(&hw_priv->vif_list_lock);
+			bes2600_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+				if ((i = (CW12XX_MAX_VIFS - 1)) || !priv)
+#else
+				if (!priv)
+#endif
+					continue;
+				powersave_enabled &= !!priv->powersave_enabled;
+			}
+			spin_unlock(&hw_priv->vif_list_lock);
+			if (powersave_enabled) {
+				bes2600_dbg(BES2600_DBG_BH, "[BH] No Device wakedown. Suspend.\n");
+#ifndef FPGA_SETUP
+				WARN_ON(bes2600_reg_write_16(hw_priv,
+						ST90TDS_CONTROL_REG_ID, 0));
+				hw_priv->device_can_sleep = true;
+#endif
+			}
+
+			atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPENDED);
+			wake_up(&hw_priv->bh_evt_wq);
+			status = wait_event_interruptible(hw_priv->bh_wq,
+					BES2600_BH_RESUME == atomic_read(
+						&hw_priv->bh_suspend));
+			if (status < 0) {
+				wiphy_err(hw_priv->hw->wiphy,
+					"%s: Failed to wait for resume: %ld.\n",
+					__func__, status);
+				break;
+			}
+			bes2600_dbg(BES2600_DBG_BH, "[BH] Device resume.\n");
+			atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED);
+			wake_up(&hw_priv->bh_evt_wq);
+			atomic_add(1, &hw_priv->bh_rx);
+			continue;
+		}
+
+test:
+		tx += pending_tx;
+		pending_tx = 0;
+
+		if (rx) {
+			size_t alloc_len;
+			u8 *data;
+
+#ifdef INTERRUPT_WORKAROUND
+			if(!(ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK))
+#endif
+			if (WARN_ON(bes2600_bh_read_ctrl_reg(
+					hw_priv, &ctrl_reg)))
+				break;
+rx:
+			read_len = (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK) * 2;
+			if (!read_len) {
+				rx_burst = 0;
+				goto tx;
+			}
+
+			if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
+					(read_len > EFFECTIVE_BUF_SIZE))) {
+				bes2600_dbg(BES2600_DBG_BH, "Invalid read len: %d",
+					read_len);
+				break;
+			}
+
+			/* Add SIZE of PIGGYBACK reg (CONTROL Reg)
+			 * to the NEXT Message length + 2 Bytes for SKB */
+			read_len = read_len + 2;
+
+#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES)
+			alloc_len = hw_priv->sbus_ops->align_size(
+					hw_priv->sbus_priv, read_len);
+#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
+			/* Platform's SDIO workaround */
+			alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1);
+			if (read_len & (SDIO_BLOCK_SIZE - 1))
+				alloc_len += SDIO_BLOCK_SIZE;
+#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
+
+			/* Check if not exceeding BES2600 capabilities */
+			if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
+				bes2600_dbg(BES2600_DBG_BH, "Read aligned len: %d\n",
+					alloc_len);
+			}
+
+			skb_rx = bes2600_get_skb(hw_priv, alloc_len);
+			if (WARN_ON(!skb_rx))
+				break;
+
+			skb_trim(skb_rx, 0);
+			skb_put(skb_rx, read_len);
+			data = skb_rx->data;
+			if (WARN_ON(!data))
+				break;
+
+			if (WARN_ON(bes2600_data_read(hw_priv, data, alloc_len)))
+				break;
+
+			/* Piggyback */
+			ctrl_reg = __le16_to_cpu(
+				((__le16 *)data)[alloc_len / 2 - 1]);
+
+			wsm = (struct wsm_hdr *)data;
+			wsm_len = __le32_to_cpu(wsm->len);
+			if (WARN_ON(wsm_len > read_len))
+				break;
+
+#if defined(CONFIG_BES2600_WSM_DUMPS)
+			if (unlikely(hw_priv->wsm_enable_wsm_dumps)) {
+				u16 msgid, ifid;
+				u16 *p = (u16 *)data;
+				msgid = (*(p + 1)) & 0xC3F;
+				ifid  = (*(p + 1)) >> 6;
+				ifid &= 0xF;
+				bes2600_dbg(BES2600_DBG_BH, "[DUMP] <<< msgid 0x%.4X ifid %d len %d\n",
+							msgid, ifid, *p);
+				print_hex_dump_bytes("<-- ",
+					DUMP_PREFIX_NONE,
+					data, min(wsm_len, wsm_dump_max));
+			}
+#endif /* CONFIG_BES2600_WSM_DUMPS */
+
+			wsm_id  = __le32_to_cpu(wsm->id) & 0xFFF;
+			wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7;
+
+			skb_trim(skb_rx, wsm_len);
+
+			if (unlikely(wsm_id == 0x0800)) {
+				wsm_handle_exception(hw_priv,
+					 &data[sizeof(*wsm)],
+					wsm_len - sizeof(*wsm));
+				break;
+			} else if (unlikely(!rx_resync)) {
+				if (WARN_ON(wsm_seq != hw_priv->wsm_rx_seq)) {
+#if defined(CONFIG_BES2600_DUMP_ON_ERROR)
+					BUG_ON(1);
+#endif /* CONFIG_BES2600_DUMP_ON_ERROR */
+					break;
+				}
+			}
+			hw_priv->wsm_rx_seq = (wsm_seq + 1) & 7;
+			rx_resync = 0;
+
+			if (wsm_id & 0x0400) {
+				int rc = wsm_release_tx_buffer(hw_priv, 1);
+				if (WARN_ON(rc < 0))
+					break;
+				else if (rc > 0)
+					tx = 1;
+			}
+
+			/* bes2600_wsm_rx takes care on SKB livetime */
+			if (WARN_ON(wsm_handle_rx(hw_priv, wsm_id, wsm,
+						  &skb_rx)))
+				break;
+
+			if (skb_rx) {
+				bes2600_put_skb(hw_priv, skb_rx);
+				skb_rx = NULL;
+			}
+
+			read_len = 0;
+
+			if (rx_burst) {
+				bes2600_debug_rx_burst(hw_priv);
+				--rx_burst;
+				goto rx;
+			}
+		}
+
+tx:
+		BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs);
+		tx_burst = hw_priv->wsm_caps.numInpChBufs -
+			hw_priv->hw_bufs_used;
+		tx_allowed = tx_burst > 0;
+		if (tx && tx_allowed) {
+			size_t tx_len;
+			u8 *data;
+			int ret;
+
+			if (hw_priv->device_can_sleep) {
+				ret = bes2600_device_wakeup(hw_priv);
+				if (WARN_ON(ret < 0))
+					break;
+				else if (ret)
+					hw_priv->device_can_sleep = false;
+				else {
+					/* Wait for "awake" interrupt */
+					pending_tx = tx;
+					continue;
+				}
+			}
+
+			wsm_alloc_tx_buffer(hw_priv);
+			ret = wsm_get_tx(hw_priv, &data, &tx_len, &tx_burst,
+						&vif_selected);
+			if (ret <= 0) {
+				wsm_release_tx_buffer(hw_priv, 1);
+				if (WARN_ON(ret < 0))
+					break;
+			} else {
+				wsm = (struct wsm_hdr *)data;
+				BUG_ON(tx_len < sizeof(*wsm));
+				BUG_ON(__le32_to_cpu(wsm->len) != tx_len);
+
+#if 0 /* count is not implemented */
+				if (ret > 1)
+					atomic_add(1, &hw_priv->bh_tx);
+#else
+				atomic_add(1, &hw_priv->bh_tx);
+#endif
+
+#if defined(CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES)
+				if (tx_len <= 8)
+					tx_len = 16;
+				tx_len = hw_priv->sbus_ops->align_size(
+						hw_priv->sbus_priv, tx_len);
+#else /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
+				/* HACK!!! Platform limitation.
+				* It is also supported by upper layer:
+				* there is always enough space at the
+				* end of the buffer. */
+				if (tx_len & (SDIO_BLOCK_SIZE - 1)) {
+					tx_len &= ~(SDIO_BLOCK_SIZE - 1);
+					tx_len += SDIO_BLOCK_SIZE;
+				}
+#endif /* CONFIG_BES2600_NON_POWER_OF_TWO_BLOCKSIZES */
+
+				/* Check if not exceeding BES2600
+				    capabilities */
+				if (WARN_ON_ONCE(
+				    tx_len > EFFECTIVE_BUF_SIZE)) {
+					bes2600_dbg(BES2600_DBG_BH, "Write aligned len:"
+					" %d\n", tx_len);
+				}
+
+				wsm->id &= __cpu_to_le32(
+						~WSM_TX_SEQ(WSM_TX_SEQ_MAX));
+				wsm->id |= cpu_to_le32(WSM_TX_SEQ(
+						hw_priv->wsm_tx_seq));
+
+				if (WARN_ON(bes2600_data_write(hw_priv,
+				    data, tx_len))) {
+					wsm_release_tx_buffer(hw_priv, 1);
+					break;
+				}
+
+				if (vif_selected != -1) {
+					hw_priv->hw_bufs_used_vif[
+							vif_selected]++;
+				}
+
+#if defined(CONFIG_BES2600_WSM_DUMPS)
+				if (unlikely(hw_priv->wsm_enable_wsm_dumps)) {
+					u16 msgid, ifid;
+					u16 *p = (u16 *)data;
+					msgid = (*(p + 1)) & 0x3F;
+					ifid  = (*(p + 1)) >> 6;
+					ifid &= 0xF;
+					if (msgid == 0x0006) {
+						bes2600_dbg(BES2600_DBG_BH, "[DUMP] >>> "
+								"msgid 0x%.4X "
+								"ifid %d len %d"
+								" MIB 0x%.4X\n",
+								msgid, ifid,
+								*p, *(p + 2));
+					} else {
+						bes2600_dbg(BES2600_DBG_BH, "[DUMP] >>> "
+								"msgid 0x%.4X "
+								"ifid %d "
+								"len %d\n",
+								msgid, ifid,
+								*p);
+					}
+					print_hex_dump_bytes("--> ",
+						DUMP_PREFIX_NONE,
+						data,
+						min(__le32_to_cpu(wsm->len),
+						 wsm_dump_max));
+				}
+#endif /* CONFIG_BES2600_WSM_DUMPS */
+
+				wsm_txed(hw_priv, data);
+				hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1)
+						& WSM_TX_SEQ_MAX;
+
+				if (tx_burst > 1) {
+					bes2600_debug_tx_burst(hw_priv);
+					++rx_burst;
+					goto tx;
+				}
+			}
+		}
+
+		if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
+			goto rx;
+	}
+
+	if (skb_rx) {
+		bes2600_put_skb(hw_priv, skb_rx);
+		skb_rx = NULL;
+	}
+
+
+	if (!term) {
+		bes2600_dbg(BES2600_DBG_ERROR, "[BH] Fatal error, exitting.\n");
+#if defined(CONFIG_BES2600_DUMP_ON_ERROR)
+		BUG_ON(1);
+#endif /* CONFIG_BES2600_DUMP_ON_ERROR */
+		hw_priv->bh_error = 1;
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+		spin_lock(&hw_priv->vif_list_lock);
+		bes2600_for_each_vif(hw_priv, priv, i) {
+			if (!priv)
+				continue;
+			ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL);
+		}
+		spin_unlock(&hw_priv->vif_list_lock);
+		bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ);
+#endif
+		/* TODO: schedule_work(recovery) */
+#ifndef HAS_PUT_TASK_STRUCT
+		/* The only reason of having this stupid code here is
+		 * that __put_task_struct is not exported by kernel. */
+		for (;;) {
+			int status = wait_event_interruptible(hw_priv->bh_wq, ({
+				term = atomic_xchg(&hw_priv->bh_term, 0);
+				(term);
+				}));
+
+			if (status || term)
+				break;
+		}
+#endif
+	}
+	return 0;
+}
+#else
+
+#ifdef CONFIG_BES2600_WLAN_SDIO
+extern int bes2600_bh_read_ctrl_reg(struct bes2600_common *priv, u32 *ctrl_reg);
+#endif
+
+static void bes2600_bh_parse_ipv4_data(struct iphdr *ip)
+{
+	u8 *tmp_ptr = (u8 *)ip;
+
+	bes2600_info(BES2600_DBG_BH, "IP Addr src:0x%08x dst:0x%08x\n", 
+		__be32_to_cpu(ip->saddr), __be32_to_cpu(ip->daddr));
+
+	if (ip->protocol == IPPROTO_TCP) {
+		struct tcphdr *tcp = (struct tcphdr *)(tmp_ptr + ip->ihl * 4);
+		bes2600_info(BES2600_DBG_BH, "TCP Port src:%d dst:%d\n",
+			__be16_to_cpu(tcp->source), __be16_to_cpu(tcp->dest));
+	} else if (ip->protocol == IPPROTO_UDP) {
+		struct udphdr *udp = (struct udphdr *)(tmp_ptr + ip->ihl * 4);
+		bes2600_info(BES2600_DBG_BH, "UDP Port src:%d dst:%d\n",
+			__be16_to_cpu(udp->source), __be16_to_cpu(udp->dest));
+	}
+}
+
+static void bes2600_bh_parse_data_pkt(struct bes2600_common *hw_priv, struct sk_buff *skb)
+{
+	struct wsm_hdr *wsm = (struct wsm_hdr *)skb->data;
+	u16 wsm_id = __le16_to_cpu(wsm->id) & 0xFFF;
+	int if_id = (wsm_id >> 6) & 0x0F;
+	u8 *data_ptr = (u8 *)&wsm[1];
+	struct ieee80211_hdr *i80211_ptr = (struct ieee80211_hdr *)(data_ptr + 28 /* radio header */);
+	__le16 fctl = *(__le16 *)i80211_ptr;
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[if_id]);
+	u32 encry_hdr_len = bes2600_bh_get_encry_hdr_len(priv->cipherType);
+	u32 i80211_len = ieee80211_hdrlen(fctl);
+	u8 *tmp_ptr = (u8 *)i80211_ptr;
+	u16 *eth_type_ptr = (u16 *)(tmp_ptr + i80211_len + encry_hdr_len + ETH_ALEN);
+	u16 eth_type = __be16_to_cpu(*eth_type_ptr);
+
+	bes2600_info(BES2600_DBG_BH, "Host was waked by data:\n");
+	bes2600_info(BES2600_DBG_BH, "RA:%pM\n", ieee80211_get_DA(i80211_ptr));
+	bes2600_info(BES2600_DBG_BH, "ETH_TYPE:0x%04x\n", eth_type);
+
+	if (eth_type == ETH_P_IP) {
+		struct iphdr *ip = (struct iphdr *)&eth_type_ptr[1];
+		bes2600_info(BES2600_DBG_BH, "IPVersion:%d\n", ip->version);
+		bes2600_info(BES2600_DBG_BH, "IPProtol:%d\n", ip->protocol);
+
+		if (ip->version == 4) {
+			bes2600_bh_parse_ipv4_data(ip);
+		}
+	}
+}
+
+static void bes2600_bh_parse_wakeup_event(struct bes2600_common *hw_priv, struct sk_buff *skb)
+{
+	struct wsm_hdr *wsm = (struct wsm_hdr *)skb->data;
+	u16 wsm_id = __le16_to_cpu(wsm->id) & 0xFFF;
+
+	if (hw_priv->sbus_ops->wakeup_source &&
+	    hw_priv->sbus_ops->wakeup_source(hw_priv->sbus_priv)) {
+		if (wsm_id == 0x0804) {
+			u8 *data_ptr = (u8 *)&wsm[1];
+			u8 *i80211_ptr = data_ptr + 28/* radio header */;
+			__le16 fctl = *(__le16 *)i80211_ptr;
+
+			if (ieee80211_is_mgmt(fctl)) {
+				u16 type = (fctl & cpu_to_le16(IEEE80211_FCTL_FTYPE)) >> 2;
+				u16 stype = (fctl & cpu_to_le16(IEEE80211_FCTL_STYPE)) >> 4;
+				bes2600_info(BES2600_DBG_BH, "Host was waked by mgmt, type:%d(%d)\n", type, stype);
+			} else if(ieee80211_is_data(fctl)){
+				bes2600_bh_parse_data_pkt(hw_priv, skb);
+			} else {
+				bes2600_info(BES2600_DBG_BH, "Host was waked by unexpected frame, fctl:0x%04x\n", fctl);
+			}
+		} else {
+			bes2600_info(BES2600_DBG_BH, "Host was waked by event:0x%04x.\n", wsm_id);
+		}
+	}
+}
+
+static int bes2600_bh_rx_helper(struct bes2600_common *priv, int *tx)
+{
+	struct sk_buff *skb = NULL;
+	struct wsm_hdr *wsm;
+	size_t wsm_len;
+	u16 wsm_id;
+	u8 wsm_seq;
+	int rx = 0;
+	u32 confirm_label = 0x0; /* wsm to mcu cmd cnfirm label */
+
+#if defined(CONFIG_BES2600_WLAN_USB) || defined(CONFIG_BES2600_WLAN_SPI) || defined(BES_SDIO_RX_MULTIPLE_ENABLE)
+	skb = (struct sk_buff *)priv->sbus_ops->pipe_read(priv->sbus_priv);
+	if (!skb)
+		return 0;
+	rx = 1; // always consider rx pipe not empty
+#else
+	u32 ctrl_reg = 0;
+	size_t read_len = 0;
+//	int rx_resync = 1;
+	size_t alloc_len;
+	u8 *data;
+
+	bes2600_bh_read_ctrl_reg(priv, &ctrl_reg);
+
+	read_len = (ctrl_reg & BES_TX_NEXT_LEN_MASK);
+
+	if (!read_len)
+		return 0; /* No more work */
+
+	if (WARN_ON((read_len < sizeof(struct wsm_hdr)) ||
+		    (read_len > EFFECTIVE_BUF_SIZE))) {
+		bes2600_err(BES2600_DBG_BH, "Invalid read len: %zu (%04x)",
+			 read_len, ctrl_reg);
+		goto err;
+	}
+
+	/* more 2 byte is not needed ? */
+#if 0
+	/* Add SIZE of PIGGYBACK reg (CONTROL Reg)
+	 * to the NEXT Message length + 2 Bytes for SKB
+	 */
+	read_len = read_len + 2;
+#endif
+
+	alloc_len = priv->sbus_ops->align_size(
+		priv->sbus_priv, read_len);
+
+	/* Check if not exceeding BES2600 capabilities */
+	if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
+		bes2600_dbg(BES2600_DBG_BH, "Read aligned len: %zu\n",
+			 alloc_len);
+	}
+
+	skb = dev_alloc_skb(alloc_len);
+	if (WARN_ON(!skb))
+		goto err;
+
+	skb_trim(skb, 0);
+	skb_put(skb, read_len);
+	data = skb->data;
+	if (WARN_ON(!data))
+		goto err;
+
+	if (WARN_ON(bes2600_data_read(priv, data, alloc_len))) {
+		bes2600_err(BES2600_DBG_BH, "rx blew up, len %zu\n", alloc_len);
+		goto err;
+	}
+
+	/* piggyback is not implemented,
+	 * and only recieve data once
+	 */
+#if 0
+	/* Piggyback */
+	ctrl_reg = __le16_to_cpu(
+		((__le16 *)data)[alloc_len / 2 - 1]);
+
+	/* check if more data need to recv */
+	if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
+		rx = 1;
+#else
+	rx = 0;
+#endif
+
+#endif
+
+	wsm = (struct wsm_hdr *)skb->data;
+	wsm_len = __le16_to_cpu(wsm->len);
+	if (WARN_ON(wsm_len > skb->len)) {
+		bes2600_err(BES2600_DBG_BH, "wsm_len err %d %d\n", (int)wsm_len, (int)skb->len);
+		goto err;
+	}
+
+	if (priv->wsm_enable_wsm_dumps)
+		print_hex_dump_bytes("<-- ",
+				     DUMP_PREFIX_NONE,
+				     skb->data, wsm_len);
+
+	wsm_id  = __le16_to_cpu(wsm->id) & 0xFFF;
+	wsm_seq = (__le16_to_cpu(wsm->id) >> 13) & 7;
+	bes2600_dbg(BES2600_DBG_BH, "bes2600_bh_rx_helper wsm_id:0x%04x seq:%d\n", wsm_id, wsm_seq);
+
+	skb_trim(skb, wsm_len);
+
+	if (wsm_id == 0x0800) {
+		wsm_handle_exception(priv,
+				     &skb->data[sizeof(*wsm)],
+				     wsm_len - sizeof(*wsm));
+		bes2600_err(BES2600_DBG_BH, "wsm exception.!\n");
+		goto err;
+	} else if ((wsm_seq != priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)])) {
+		bes2600_err(BES2600_DBG_BH, "seq error! %u. %u. 0x%x.", wsm_seq, priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)], wsm_id);
+		goto err;
+	}
+
+	bes2600_bh_parse_wakeup_event(priv, skb);
+
+	priv->wsm_rx_seq[WSM_TXRX_SEQ_IDX(wsm_id)] = (wsm_seq + 1) & 7;
+
+	if (IS_DRIVER_TO_MCU_CMD(wsm_id))
+		confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label);
+
+	if (WSM_CONFIRM_CONDITION(wsm_id, confirm_label)) {
+		int rc = wsm_release_tx_buffer(priv, 1);
+		bes2600_bh_dec_pending_count(priv, WSM_TXRX_SEQ_IDX(wsm->id));
+
+		if (WARN_ON(rc < 0))
+			return rc;
+		else if (rc > 0)
+			*tx = 1;
+	}
+
+	/* bes2600_wsm_rx takes care on SKB livetime */
+	//if (WARN_ON(wsm_handle_rx(priv, wsm_id, wsm, &skb)))
+	if ((wsm_handle_rx(priv, wsm_id, wsm, &skb))) {
+		bes2600_err(BES2600_DBG_BH, "wsm_handle_rx fail\n");
+		goto err;
+	}
+
+	if (skb) {
+		dev_kfree_skb(skb);
+		skb = NULL;
+	}
+	return rx;
+
+err:
+	bes2600_err(BES2600_DBG_BH, "bes2600_bh_rx_helper err\n");
+	if (skb) {
+		dev_kfree_skb(skb);
+		skb = NULL;
+	}
+	return -1;
+}
+
+static int bes2600_bh_tx_helper(struct bes2600_common *hw_priv,
+			       int *pending_tx,
+			       int *tx_burst)
+{
+	size_t tx_len;
+	u8 *data;
+	int ret;
+	struct wsm_hdr *wsm;
+	int vif_selected;
+
+#ifdef CONFIG_BES2600_WLAN_USB
+	u32 packet_id;
+	u8 queueId;
+	struct wsm_tx *wsm_info;
+	struct bes2600_queue *queue;
+#endif
+
+	wsm_alloc_tx_buffer(hw_priv);
+	ret = wsm_get_tx(hw_priv, &data, &tx_len, tx_burst, &vif_selected);
+	if (ret <= 0) {
+		wsm_release_tx_buffer(hw_priv, 1);
+		if (WARN_ON(ret < 0)) {
+			bes2600_err(BES2600_DBG_BH, "bh get tx failed.\n");
+			return ret; /* Error */
+		}
+		return 0; /* No work */
+	}
+
+	wsm = (struct wsm_hdr *)data;
+	BUG_ON(tx_len < sizeof(*wsm));
+	BUG_ON(__le16_to_cpu(wsm->len) != tx_len);
+#ifdef BES2600_HOST_TIMESTAMP_DEBUG
+	tx_len += 4;
+#endif
+
+	atomic_add(1, &hw_priv->bh_tx);
+
+	tx_len = hw_priv->sbus_ops->align_size(
+		hw_priv->sbus_priv, tx_len);
+
+	/* Check if not exceeding BES2600 capabilities */
+	if (WARN_ON_ONCE(tx_len > EFFECTIVE_BUF_SIZE))
+		bes2600_err(BES2600_DBG_BH,  "Write aligned len: %zu\n", tx_len);
+
+	wsm->id &= __cpu_to_le16(0xffff ^ WSM_TX_SEQ(WSM_TX_SEQ_MAX));
+	wsm->id |= __cpu_to_le16(WSM_TX_SEQ(hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)]));
+
+	//bes2600_dbg(BES2600_DBG_BH, "usb send buff len:%u.priv->hw_bufs_used:%d.\n", tx_len, priv->hw_bufs_used);
+	bes2600_dbg(BES2600_DBG_BH, "%s id:0x%04x seq:%d\n", __func__,
+		wsm->id, hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)]);
+#ifdef CONFIG_BES2600_WLAN_USB
+	ret = hw_priv->sbus_ops->pipe_send(hw_priv->sbus_priv, 1, tx_len, data);
+	#if 0
+	int count = 0;
+	while(priv->hw_bufs_used > 1)
+	{
+		mdelay(1);
+		count++;
+		if(count>2)
+			break;
+	}
+	#endif
+	if(ret < 0) {
+		/* requeue packet when send fail */
+		wsm_info = (struct wsm_tx *)data;
+		if (data == hw_priv->wsm_cmd.ptr) {
+			spin_lock(&hw_priv->wsm_cmd.lock);
+			hw_priv->wsm_cmd.ret = 1;
+			hw_priv->wsm_cmd.done = 1;
+			hw_priv->wsm_cmd.arg = NULL;
+			hw_priv->wsm_cmd.ptr = NULL;
+			spin_unlock(&hw_priv->wsm_cmd.lock);
+			wake_up(&hw_priv->wsm_cmd_wq);
+		} else {
+			packet_id =  __le32_to_cpu(wsm_info->packetID);
+			queueId = bes2600_queue_get_queue_id(packet_id);
+			queue = &hw_priv->tx_queue[queueId];
+#ifdef CONFIG_BES2600_TESTMODE
+			bes2600_queue_requeue(hw_priv, queue, packet_id, false);
+#else
+			bes2600_queue_requeue(queue, packet_id, false);
+#endif
+		}
+		wsm_release_tx_buffer(hw_priv, 1);
+		return 0;
+	} else if (vif_selected != -1) {
+		hw_priv->hw_bufs_used_vif[vif_selected]++;
+	}
+#else
+#ifndef BES_SDIO_TX_MULTIPLE_ENABLE
+	if (WARN_ON(bes2600_data_write(data, tx_len))) {
+#else
+	if (WARN_ON(hw_priv->sbus_ops->pipe_send(hw_priv->sbus_priv, 1, tx_len, data))) {
+#endif
+		bes2600_err(BES2600_DBG_BH,  "tx blew up, len %zu\n", tx_len);
+		wsm_release_tx_buffer(hw_priv, 1);
+		return -1; /* Error */
+	}
+
+	if (vif_selected != -1)
+		hw_priv->hw_bufs_used_vif[vif_selected] ++;
+#endif
+
+	if (hw_priv->wsm_enable_wsm_dumps)
+		print_hex_dump_bytes("--> ",
+				     DUMP_PREFIX_NONE,
+				     data,
+				     __le16_to_cpu(wsm->len));
+
+	wsm_txed(hw_priv, data);
+
+	hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)] =
+		(hw_priv->wsm_tx_seq[WSM_TXRX_SEQ_IDX(wsm->id)] + 1) & WSM_TX_SEQ_MAX;
+	bes2600_bh_inc_pending_count(hw_priv, WSM_TXRX_SEQ_IDX(wsm->id));
+
+	if (*tx_burst > 1) {
+		bes2600_debug_tx_burst(hw_priv);
+		return 1; /* Work remains */
+	}
+
+	return 0;
+}
+
+#ifdef KEY_FRAME_SW_RETRY
+
+static inline bool
+ieee80211_is_tcp_pkt(struct sk_buff *skb)
+{
+
+	if (!skb) {
+		return false;
+	}
+
+	if (skb->protocol == cpu_to_be16(ETH_P_IP)) {
+		struct iphdr *iph = (struct iphdr *)skb_network_header(skb);
+		if (iph->protocol == IPPROTO_TCP) { // TCP
+			bes2600_dbg(BES2600_DBG_BH, "################ %s line =%d.\n",__func__,__LINE__);
+			return true;
+		}
+	}
+	return false;
+}
+
+
+static int bes2600_need_retry_type(struct sk_buff *skb, int status)
+{
+	int ret = 0;
+	if (!skb) {
+		bes2600_info(BES2600_DBG_BH, "################ %s line =%d.\n",__func__,__LINE__);
+		return -1;
+	}
+
+	if (skb->protocol == cpu_to_be16(ETH_P_IP)) {
+		if (ieee80211_is_tcp_pkt(skb)) {
+			ret = 1;
+		}
+	}
+	if (status !=  WSM_STATUS_RETRY_EXCEEDED)
+		ret = 0;
+	return ret;
+}
+
+int bes2600_bh_sw_process(struct bes2600_common *hw_priv,
+			 struct wsm_tx_confirm *tx_confirm)
+{
+	struct bes2600_txpriv *txpriv;
+	struct sk_buff *skb = NULL;
+	unsigned long timestamp = 0;
+	struct bes2600_queue *queue;
+	u8 queue_id, queue_gen;
+
+#ifdef KEY_FRAME_SW_RETRY
+	long delta_time;
+#endif
+	if (!tx_confirm) {
+		bes2600_err(BES2600_DBG_BH,  "%s tx_confirm is NULL\n", __func__);
+		return 0;
+	}
+	queue_id = bes2600_queue_get_queue_id(tx_confirm->packetID);
+	queue = &hw_priv->tx_queue[queue_id];
+
+	if (!queue) {
+		bes2600_err(BES2600_DBG_BH,  "%s queue is NULL\n", __func__);
+		return 0;
+	}
+
+	/* don't retry if the connection is already disconnected */
+	queue_gen = bes2600_queue_get_generation(tx_confirm->packetID);
+	if(queue_gen != queue->generation)
+		return -1;
+
+	bes2600_queue_get_skb_and_timestamp(queue, tx_confirm->packetID,
+						&skb, &txpriv, &timestamp);
+	if (skb == NULL) {
+		bes2600_err(BES2600_DBG_BH,  "%s skb is NULL\n", __func__);
+		return -1;
+	}
+	if (timestamp > jiffies)
+		delta_time = jiffies + ((unsigned long)0xffffffff - timestamp);
+	else
+		delta_time =  jiffies - timestamp;
+	bes2600_add_tx_delta_time(delta_time);
+	bes2600_add_tx_ac_delta_time(queue_id, delta_time);
+
+	if (bes2600_need_retry_type(skb, tx_confirm->status) == 0)
+		return -1;
+
+	if (delta_time > 1000)
+		return -1;
+
+	if (txpriv->retry_count < CW1200_MAX_SW_RETRY_CNT ) {
+		struct bes2600_vif *priv =
+		__cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id);
+		txpriv->retry_count++;
+
+		bes2600_tx_status(priv,skb);
+
+		bes2600_pwr_set_busy_event_with_timeout_async(
+			hw_priv, BES_PWR_LOCK_ON_TX, BES_PWR_EVENT_TX_TIMEOUT);
+
+		bes2600_sw_retry_requeue(hw_priv, queue, tx_confirm->packetID, true);
+		return 0;
+	} else {
+		txpriv->retry_count = 0;
+	}
+
+	return -1;
+}
+#endif
+
+void bes2600_bh_inc_pending_count(struct bes2600_common *hw_priv, int idx)
+{
+	struct timer_list *timer = (idx == 0) ? &hw_priv->lmac_mon_timer
+	                                      : &hw_priv->mcu_mon_timer;
+
+	if(hw_priv->wsm_tx_pending[idx]++ == 0) {
+		bes2600_dbg(BES2600_DBG_BH,  "start timer in tx, idx:%d\n", idx);
+		mod_timer(timer, jiffies + 3 * HZ);
+	}
+}
+
+void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx)
+{
+	struct timer_list *timer = (idx == 0) ? &hw_priv->lmac_mon_timer
+	                                      : &hw_priv->mcu_mon_timer;
+
+	if(hw_priv->wsm_tx_pending[idx] == 0) {
+		bes2600_err(BES2600_DBG_TXRX, "tx pending count error, idx:%d\n", idx);
+		return;
+	}
+
+	if(--hw_priv->wsm_tx_pending[idx] == 0) {
+		del_timer_sync(timer);
+	} else {
+		mod_timer(timer, jiffies + 3 * HZ);
+	}
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+void bes2600_bh_mcu_active_monitor(struct timer_list* t)
+{
+	struct bes2600_common *hw_priv = from_timer(hw_priv, t, mcu_mon_timer);
+
+	bes2600_err(BES2600_DBG_TXRX, "link break between mcu and host, hw_buf_used:%d pending:%d\n", 
+				hw_priv->hw_bufs_used, hw_priv->wsm_tx_pending[1]);
+	bes2600_chrdev_wifi_force_close(hw_priv, true);
+}
+#else
+void bes2600_bh_mcu_active_monitor(unsigned long data)
+{
+	struct bes2600_common *hw_priv = (struct bes2600_common *)data;
+
+	bes2600_err(BES2600_DBG_TXRX, "link break between mcu and host, hw_buf_used:%d pending:%d\n", 
+				hw_priv->hw_bufs_used, hw_priv->wsm_tx_pending[1]);
+	bes2600_chrdev_wifi_force_close(hw_priv, true);
+}
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+void bes2600_bh_lmac_active_monitor(struct timer_list* t)
+{
+	struct bes2600_common *hw_priv = from_timer(hw_priv, t, lmac_mon_timer);
+
+	bes2600_err(BES2600_DBG_TXRX, "link break between lmac and host, hw_buf_used:%d pending:%d\n", 
+				hw_priv->hw_bufs_used, hw_priv->wsm_tx_pending[0]);
+	bes2600_chrdev_wifi_force_close(hw_priv, true);
+}
+#else
+void bes2600_bh_lmac_active_monitor(unsigned long data)
+{
+	struct bes2600_common *hw_priv = (struct bes2600_common *)data;
+
+	bes2600_err(BES2600_DBG_TXRX, "link break between lmac and host, hw_buf_used:%d pending:%d\n", 
+				hw_priv->hw_bufs_used, hw_priv->wsm_tx_pending[0]);
+	bes2600_chrdev_wifi_force_close(hw_priv, true);
+}
+#endif
+
+
+#define BH_RX_CONT_LIMIT	3
+#define BH_TX_CONT_LIMIT	20
+static int bes2600_bh(void *arg)
+{
+	struct bes2600_common *hw_priv = arg;
+	int rx, tx, term, suspend;
+	int tx_allowed;
+	int pending_tx = 0;
+	int tx_burst;
+	long status;
+	int ret;
+
+	int tx_cont = 0;
+	int rx_cont = 0;
+
+	for (;;) {
+		rx_cont = 0;
+		tx_cont = 0;
+
+		if (!hw_priv->hw_bufs_used &&
+		    !bes2600_pwr_device_is_idle(hw_priv) &&
+		    !atomic_read(&hw_priv->recent_scan)) {
+			status = 5 * HZ;
+		} else if (hw_priv->hw_bufs_used) {
+			/* Interrupt loss detection */
+			status = 5 * HZ;
+		} else {
+			status = MAX_SCHEDULE_TIMEOUT;
+		}
+
+		status = wait_event_interruptible_timeout(hw_priv->bh_wq, ({
+				rx = atomic_xchg(&hw_priv->bh_rx, 0);
+				tx = atomic_xchg(&hw_priv->bh_tx, 0);
+				term = atomic_xchg(&hw_priv->bh_term, 0);
+				suspend = pending_tx ?
+					0 : atomic_read(&hw_priv->bh_suspend);
+				(rx || tx || term || suspend || hw_priv->bh_error);
+			}), status);
+
+		//bes2600_err(BES2600_DBG_BH,  "[BH] - rx: %d, tx: %d, term: %d, bh_err: %d, suspend: %d, bufused: %d, status: %ld\n",
+				//rx, tx, term, suspend, hw_priv->bh_error, hw_priv->hw_bufs_used, status);
+
+		/* Did an error occur? */
+		if ((status < 0 && status != -ERESTARTSYS) ||
+		    term || hw_priv->bh_error) {
+			break;
+		}
+		if (!status) {  /* wait_event timed out */
+			#ifdef CONFIG_BES2600_WLAN_BES
+			unsigned long timestamp = jiffies;
+			long timeout;
+			int pending = 0;
+			int i;
+			#endif
+			/* Check to see if we have any outstanding frames */
+			if (hw_priv->hw_bufs_used && (!rx || !tx)) {
+				bes2600_err(BES2600_DBG_BH,  "usedbuf:%u. rx:%u. tx:%u.\n", hw_priv->hw_bufs_used, rx, tx);
+				#ifdef CONFIG_BES2600_WLAN_SDIO
+				sdio_work_debug(hw_priv->sbus_priv);
+				#endif
+				#ifdef CONFIG_BES2600_WLAN_BES
+				bes2600_err(BES2600_DBG_BH,  "Missed interrupt? (%d frames outstanding)\n",
+					   hw_priv->hw_bufs_used);
+				rx = 1;
+
+				/* Get a timestamp of "oldest" frame */
+				for (i = 0; i < 4; ++i)
+					pending += bes2600_queue_get_xmit_timestamp(
+						&hw_priv->tx_queue[i],
+						&timestamp, i,
+						hw_priv->pending_frame_id);
+
+				/* Check if frame transmission is timed out.
+				 * Add an extra second with respect to possible
+				 * interrupt loss.
+				 */
+				timeout = timestamp +
+					WSM_CMD_LAST_CHANCE_TIMEOUT +
+					1 * HZ  -
+					jiffies;
+
+				/* And terminate BH thread if the frame is "stuck" */
+				if (pending && timeout < 0) {
+					wiphy_warn(hw_priv->hw->wiphy,
+						   "Timeout waiting for TX confirm (%d/%d pending, %ld vs %lu).\n",
+						   hw_priv->hw_bufs_used, pending,
+						   timestamp, jiffies);
+				}
+				#endif
+
+				bes2600_chrdev_wifi_force_close(hw_priv, false);
+			}
+#ifdef BES2600_RX_IN_BH
+			goto rx;
+#else
+			goto done;
+#endif
+		} else if (suspend) {
+			bes2600_dbg(BES2600_DBG_BH,  "[BH] Device suspend.\n");
+
+			atomic_set(&hw_priv->bh_suspend, BES2600_BH_SUSPENDED);
+			wake_up(&hw_priv->bh_evt_wq);
+			status = wait_event_interruptible(hw_priv->bh_wq,
+							  BES2600_BH_RESUME == atomic_read(&hw_priv->bh_suspend));
+			if (status < 0) {
+				wiphy_err(hw_priv->hw->wiphy,
+					  "Failed to wait for resume: %ld.\n",
+					  status);
+				break;
+			}
+			bes2600_dbg(BES2600_DBG_BH,  "[BH] Device resume.\n");
+			atomic_set(&hw_priv->bh_suspend, BES2600_BH_RESUMED);
+			wake_up(&hw_priv->bh_evt_wq);
+			atomic_add(1, &hw_priv->bh_rx);
+			goto done;
+		}
+
+	rx:
+		tx += pending_tx;
+		pending_tx = 0;
+#ifdef BES2600_RX_IN_BH
+#ifdef CONFIG_BES2600_WLAN_SPI
+		if (rx) {
+#endif
+		ret = bes2600_bh_rx_helper(hw_priv, &tx);
+		if (ret < 0) {
+			bes2600_err(BES2600_DBG_BH, "bes2600_bh_rx_helper fail\n");
+			#ifdef CONFIG_BES2600_WLAN_SDIO
+			sdio_work_debug(hw_priv->sbus_priv);
+			#endif
+			// break; // rx error
+			bes2600_chrdev_wifi_force_close(hw_priv, false);
+		}
+		else if (ret == 1) {
+			rx = 1; // continue rx
+			rx_cont++;
+		}
+		else
+			rx = 0; // wait for a new rx event
+		if (rx && (rx_cont < BH_RX_CONT_LIMIT))
+			goto rx;
+		rx_cont = 0;
+#ifdef CONFIG_BES2600_WLAN_SPI
+		}
+#endif
+#endif
+	tx:
+		if (1) {
+
+			tx = 0;
+
+			BUG_ON(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs);
+			tx_burst = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used;
+			tx_allowed = tx_burst > 0;
+
+			if (!tx_allowed) {
+				/* Buffers full.  Ensure we process tx
+				 * after we handle rx..
+				 */
+				#ifndef CONFIG_BES2600_WLAN_SDIO
+				bes2600_err(BES2600_DBG_BH,  "bh tx not allowed.\n");
+				#endif
+				pending_tx = tx;
+				goto done_rx;
+			}
+			ret = bes2600_bh_tx_helper(hw_priv, &pending_tx, &tx_burst);
+			if (ret < 0) {
+				bes2600_err(BES2600_DBG_BH, "bes2600_bh_tx_helper fail\n");
+				#ifdef CONFIG_BES2600_WLAN_SDIO
+				sdio_work_debug(hw_priv->sbus_priv);
+				#endif
+				break;
+			}
+			if (ret > 0) {
+				/* More to transmit */
+				tx_cont++;
+				tx = ret;
+			}
+
+			if (tx && tx_cont < BH_TX_CONT_LIMIT)
+				goto tx;
+			tx_cont = 0;
+#if 0
+			/* Re-read ctrl reg */
+			if (bes2600_bh_read_ctrl_reg(priv, &ctrl_reg))
+				break;
+#endif
+		}
+
+	done_rx:
+		if (hw_priv->bh_error)
+			break;
+		//if (ctrl_reg & ST90TDS_CONT_NEXT_LEN_MASK)
+		if (rx)
+			goto rx;
+		if (tx)
+			goto tx;
+
+	done:
+		/* Re-enable device interrupts */
+		//hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+		//__bes2600_irq_enable(1);
+		//hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
+		asm volatile ("nop");
+	}
+
+	/* Explicitly disable device interrupts */
+	hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+	__bes2600_irq_enable(0);
+	hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
+
+	if (!term) {
+		bes2600_err(BES2600_DBG_BH,  "[BH] Fatal error, exiting.\n");
+		#ifdef CONFIG_BES2600_WLAN_SDIO
+		sdio_work_debug(hw_priv->sbus_priv);
+		#endif
+		hw_priv->bh_error = 1;
+		/* TODO: schedule_work(recovery) */
+	}
+	return 0;
+}
+#endif
diff --git a/drivers/staging/bes2600/bh.h b/drivers/staging/bes2600/bh.h
new file mode 100644
index 000000000000..d43f758e53a0
--- /dev/null
+++ b/drivers/staging/bes2600/bh.h
@@ -0,0 +1,53 @@
+/*
+ * Device handling thread interface for mac80211 BES2600 drivers
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES2600_BH_H
+#define BES2600_BH_H
+
+#include <linux/version.h>
+
+/* extern */ struct bes2600_common;
+
+#define SDIO_BLOCK_SIZE (528)
+
+#define KEY_FRAME_SW_RETRY
+#ifdef  KEY_FRAME_SW_RETRY
+#define CW1200_MAX_SW_RETRY_CNT		(2)
+#endif
+
+
+int bes2600_register_bh(struct bes2600_common *hw_priv);
+void bes2600_unregister_bh(struct bes2600_common *hw_priv);
+void bes2600_irq_handler(struct bes2600_common *hw_priv);
+void bes2600_bh_wakeup(struct bes2600_common *hw_priv);
+int bes2600_bh_suspend(struct bes2600_common *hw_priv);
+int bes2600_bh_resume(struct bes2600_common *hw_priv);
+/* Must be called from BH thread. */
+void bes2600_enable_powersave(struct bes2600_vif *priv,
+			     bool enable);
+int wsm_release_tx_buffer(struct bes2600_common *hw_priv, int count);
+int wsm_release_vif_tx_buffer(struct bes2600_common *hw_priv, int if_id,
+				int count);
+int bes2600_bh_sw_process(struct bes2600_common *hw_priv,
+			 struct wsm_tx_confirm *tx_confirm);
+
+void bes2600_bh_inc_pending_count(struct bes2600_common *hw_priv, int idx);
+void bes2600_bh_dec_pending_count(struct bes2600_common *hw_priv, int idx);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+void bes2600_bh_mcu_active_monitor(struct timer_list* t);
+void bes2600_bh_lmac_active_monitor(struct timer_list* t);
+#else
+void bes2600_bh_mcu_active_monitor(unsigned long data);
+void bes2600_bh_lmac_active_monitor(unsigned long data);
+#endif
+
+#endif /* BES2600_BH_H */
diff --git a/drivers/staging/bes2600/debug.c b/drivers/staging/bes2600/debug.c
new file mode 100644
index 000000000000..c5bba0789a3c
--- /dev/null
+++ b/drivers/staging/bes2600/debug.c
@@ -0,0 +1,784 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*Linux version 3.4.0 compilation*/
+//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
+#include<linux/module.h>
+//#endif
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include "bes2600.h"
+#include "debug.h"
+#ifdef CONFIG_BES2600_DEBUGFS
+/* join_status */
+static const char * const bes2600_debug_join_status[] = {
+	"passive",
+	"monitor",
+	"station",
+	"access point",
+};
+
+/* WSM_JOIN_PREAMBLE_... */
+static const char * const bes2600_debug_preamble[] = {
+	"long",
+	"short",
+	"long on 1 and 2 Mbps",
+};
+
+static const char * const bes2600_debug_fw_types[] = {
+	"ETF",
+	"WFM",
+	"WSM",
+	"HI test",
+	"Platform test",
+};
+
+static const char * const bes2600_debug_link_id[] = {
+	"OFF",
+	"REQ",
+	"SOFT",
+	"HARD",
+};
+
+static const char *bes2600_debug_mode(int mode)
+{
+	switch (mode) {
+	case NL80211_IFTYPE_UNSPECIFIED:
+		return "unspecified";
+	case NL80211_IFTYPE_MONITOR:
+		return "monitor";
+	case NL80211_IFTYPE_STATION:
+		return "station";
+	case NL80211_IFTYPE_ADHOC:
+		return "ad-hok";
+	case NL80211_IFTYPE_MESH_POINT:
+		return "mesh point";
+	case NL80211_IFTYPE_AP:
+		return "access point";
+	case NL80211_IFTYPE_P2P_CLIENT:
+		return "p2p client";
+	case NL80211_IFTYPE_P2P_GO:
+		return "p2p go";
+	default:
+		return "unsupported";
+	}
+}
+
+static void bes2600_queue_status_show(struct seq_file *seq,
+				     struct bes2600_queue *q)
+{
+	int i, if_id;
+	seq_printf(seq, "Queue       %d:\n", (int)q->queue_id);
+	seq_printf(seq, "  capacity: %ld\n", (long)q->capacity);
+	seq_printf(seq, "  queued:   %ld\n", (long)q->num_queued);
+	seq_printf(seq, "  pending:  %ld\n", (long)q->num_pending);
+	seq_printf(seq, "  sent:     %ld\n", (long)q->num_sent);
+	seq_printf(seq, "  locked:   %s\n", q->tx_locked_cnt ? "yes" : "no");
+	seq_printf(seq, "  overfull: %s\n", q->overfull ? "yes" : "no");
+	seq_puts(seq,   "  link map: 0-> ");
+	for (if_id = 0; if_id < CW12XX_MAX_VIFS; if_id++) {
+		for (i = 0; i < q->stats->map_capacity; ++i)
+			seq_printf(seq, "%.2d ", q->link_map_cache[if_id][i]);
+		seq_printf(seq, "<-%ld\n", (long)q->stats->map_capacity);
+	}
+}
+
+static void bes2600_debug_print_map(struct seq_file *seq,
+				   struct bes2600_vif *priv,
+				   const char *label,
+				   u32 map)
+{
+	int i;
+	seq_printf(seq, "%s0-> ", label);
+	for (i = 0; i < priv->hw_priv->tx_queue_stats.map_capacity; ++i)
+		seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : "..");
+	seq_printf(seq, "<-%ld\n",
+		   (long)priv->hw_priv->tx_queue_stats.map_capacity - 1);
+}
+
+static int bes2600_status_show_common(struct seq_file *seq, void *v)
+{
+	int i;
+	struct list_head *item;
+	struct bes2600_common *hw_priv = seq->private;
+	struct bes2600_debug_common *d = hw_priv->debug;
+	int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0;
+	bool ba_ena;
+
+	spin_lock_bh(&hw_priv->ba_lock);
+	ba_cnt = hw_priv->debug->ba_cnt;
+	ba_acc = hw_priv->debug->ba_acc;
+	ba_cnt_rx = hw_priv->debug->ba_cnt_rx;
+	ba_acc_rx = hw_priv->debug->ba_acc_rx;
+	ba_ena = hw_priv->ba_ena;
+	if (ba_cnt)
+		ba_avg = ba_acc / ba_cnt;
+	if (ba_cnt_rx)
+		ba_avg_rx = ba_acc_rx / ba_cnt_rx;
+	spin_unlock_bh(&hw_priv->ba_lock);
+
+	seq_puts(seq,   "BES2600 Wireless LAN driver status\n");
+	seq_printf(seq, "Hardware:   %d.%d\n",
+		hw_priv->wsm_caps.hardwareId,
+		hw_priv->wsm_caps.hardwareSubId);
+	seq_printf(seq, "Firmware:   %s %d.%d\n",
+		bes2600_debug_fw_types[hw_priv->wsm_caps.firmwareType],
+		hw_priv->wsm_caps.firmwareVersion,
+		hw_priv->wsm_caps.firmwareBuildNumber);
+	seq_printf(seq, "FW API:     %d\n",
+		hw_priv->wsm_caps.firmwareApiVer);
+	seq_printf(seq, "FW caps:    0x%.4X\n",
+		hw_priv->wsm_caps.firmwareCap);
+	if (hw_priv->channel)
+		seq_printf(seq, "Channel:    %d%s\n",
+			hw_priv->channel->hw_value,
+			hw_priv->channel_switch_in_progress ?
+			" (switching)" : "");
+	seq_printf(seq, "HT:         %s\n",
+		bes2600_is_ht(&hw_priv->ht_info) ? "on" : "off");
+	if (bes2600_is_ht(&hw_priv->ht_info)) {
+		seq_printf(seq, "Greenfield: %s\n",
+			bes2600_ht_greenfield(&hw_priv->ht_info) ? "yes" : "no");
+		seq_printf(seq, "AMPDU dens: %d\n",
+			bes2600_ht_ampdu_density(&hw_priv->ht_info));
+	}
+	spin_lock_bh(&hw_priv->tx_policy_cache.lock);
+	i = 0;
+	list_for_each(item, &hw_priv->tx_policy_cache.used)
+		++i;
+	spin_unlock_bh(&hw_priv->tx_policy_cache.lock);
+	seq_printf(seq, "RC in use:  %d\n", i);
+	seq_printf(seq, "BA stat:    %d, %d (%d)\n",
+		ba_cnt, ba_acc, ba_avg);
+	seq_printf(seq, "BA RX stat:    %d, %d (%d)\n",
+		ba_cnt_rx, ba_acc_rx, ba_avg_rx);
+	seq_printf(seq, "Block ACK:  %s\n", ba_ena ? "on" : "off");
+
+	seq_puts(seq, "\n");
+	for (i = 0; i < 4; ++i) {
+		bes2600_queue_status_show(seq, &hw_priv->tx_queue[i]);
+		seq_puts(seq, "\n");
+	}
+	seq_printf(seq, "TX burst:   %d\n",
+		d->tx_burst);
+	seq_printf(seq, "RX burst:   %d\n",
+		d->rx_burst);
+	seq_printf(seq, "TX miss:    %d\n",
+		d->tx_cache_miss);
+	seq_printf(seq, "Long retr:  %d\n",
+		hw_priv->long_frame_max_tx_count);
+	seq_printf(seq, "Short retr: %d\n",
+		hw_priv->short_frame_max_tx_count);
+
+	seq_printf(seq, "BH status:  %s\n",
+		atomic_read(&hw_priv->bh_term) ? "terminated" : "alive");
+	seq_printf(seq, "Pending RX: %d\n",
+		atomic_read(&hw_priv->bh_rx));
+	seq_printf(seq, "Pending TX: %d\n",
+		atomic_read(&hw_priv->bh_tx));
+	if (hw_priv->bh_error)
+		seq_printf(seq, "BH errcode: %d\n",
+			hw_priv->bh_error);
+	seq_printf(seq, "TX bufs:    %d x %d bytes\n",
+		hw_priv->wsm_caps.numInpChBufs,
+		hw_priv->wsm_caps.sizeInpChBuf);
+	seq_printf(seq, "Used bufs:  %d\n",
+		hw_priv->hw_bufs_used);
+	seq_printf(seq, "Device:     %s\n",
+		bes2600_pwr_device_is_idle(hw_priv) ? "alseep" : "awake");
+
+	spin_lock(&hw_priv->wsm_cmd.lock);
+	seq_printf(seq, "WSM status: %s\n",
+		hw_priv->wsm_cmd.done ? "idle" : "active");
+	seq_printf(seq, "WSM cmd:    0x%.4X (%ld bytes)\n",
+		hw_priv->wsm_cmd.cmd, (long)hw_priv->wsm_cmd.len);
+	seq_printf(seq, "WSM retval: %d\n",
+		hw_priv->wsm_cmd.ret);
+	spin_unlock(&hw_priv->wsm_cmd.lock);
+
+	seq_printf(seq, "Datapath:   %s\n",
+		atomic_read(&hw_priv->tx_lock) ? "locked" : "unlocked");
+	if (atomic_read(&hw_priv->tx_lock))
+		seq_printf(seq, "TXlock cnt: %d\n",
+			atomic_read(&hw_priv->tx_lock));
+
+	seq_printf(seq, "Scan:       %s\n",
+		atomic_read(&hw_priv->scan.in_progress) ? "active" : "idle");
+	seq_printf(seq, "Led state:  0x%.2X\n",
+		hw_priv->softled_state);
+
+	return 0;
+}
+
+static int bes2600_status_open_common(struct inode *inode, struct file *file)
+{
+	return single_open(file, &bes2600_status_show_common,
+		inode->i_private);
+}
+
+static const struct file_operations fops_status_common = {
+	.open = bes2600_status_open_common,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static int bes2600_counters_show(struct seq_file *seq, void *v)
+{
+	int ret;
+	struct bes2600_common *hw_priv = seq->private;
+	struct wsm_counters_table counters;
+
+	ret = wsm_get_counters_table(hw_priv, &counters);
+	if (ret)
+		return ret;
+
+#define CAT_STR(x, y) x ## y
+#define PUT_COUNTER(tab, name) \
+	seq_printf(seq, "%s:" tab "%d\n", #name, \
+		__le32_to_cpu(counters.CAT_STR(count, name)))
+
+	PUT_COUNTER("\t\t", PlcpErrors);
+	PUT_COUNTER("\t\t", FcsErrors);
+	PUT_COUNTER("\t\t", TxPackets);
+	PUT_COUNTER("\t\t", RxPackets);
+	PUT_COUNTER("\t\t", RxPacketErrors);
+	PUT_COUNTER("\t",   RxDecryptionFailures);
+	PUT_COUNTER("\t\t", RxMicFailures);
+	PUT_COUNTER("\t",   RxNoKeyFailures);
+	PUT_COUNTER("\t",   TxMulticastFrames);
+	PUT_COUNTER("\t",   TxFramesSuccess);
+	PUT_COUNTER("\t",   TxFrameFailures);
+	PUT_COUNTER("\t",   TxFramesRetried);
+	PUT_COUNTER("\t",   TxFramesMultiRetried);
+	PUT_COUNTER("\t",   RxFrameDuplicates);
+	PUT_COUNTER("\t\t", RtsSuccess);
+	PUT_COUNTER("\t\t", RtsFailures);
+	PUT_COUNTER("\t\t", AckFailures);
+	PUT_COUNTER("\t",   RxMulticastFrames);
+	PUT_COUNTER("\t",   RxFramesSuccess);
+	PUT_COUNTER("\t",   RxCMACICVErrors);
+	PUT_COUNTER("\t\t", RxCMACReplays);
+	PUT_COUNTER("\t",   RxMgmtCCMPReplays);
+
+#undef PUT_COUNTER
+#undef CAT_STR
+
+	return 0;
+}
+
+static int bes2600_counters_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, &bes2600_counters_show,
+		inode->i_private);
+}
+
+static const struct file_operations fops_counters = {
+	.open = bes2600_counters_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static int bes2600_power_busy_event_show(struct seq_file *seq, void *v)
+{
+	struct bes2600_common *hw_priv = seq->private;
+	char *buffer = NULL;
+
+	buffer = kmalloc(8192, GFP_KERNEL);
+	if(!buffer)
+		return -ENOMEM;
+
+	if(bes2600_pwr_busy_event_dump(hw_priv, buffer, 8192) == 0) {
+		seq_printf(seq, "%s", buffer);
+	} else {
+		return -EFBIG;
+	}
+
+	kfree(buffer);
+
+	return 0;
+}
+
+static int bes2600_power_busy_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, &bes2600_power_busy_event_show,
+		inode->i_private);
+}
+
+static const struct file_operations fops_power_busy_events = {
+	.open = bes2600_power_busy_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+static int bes2600_generic_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t bes2600_11n_read(struct file *file,
+	char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct bes2600_common *hw_priv = file->private_data;
+	struct ieee80211_supported_band *band =
+		hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ];
+	return simple_read_from_buffer(user_buf, count, ppos,
+		band->ht_cap.ht_supported ? "1\n" : "0\n", 2);
+}
+
+static ssize_t bes2600_11n_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct bes2600_common *hw_priv = file->private_data;
+	struct ieee80211_supported_band *band[2] = {
+		hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ],
+		hw_priv->hw->wiphy->bands[NL80211_BAND_5GHZ],
+	};
+	char buf[1];
+	int ena = 0;
+
+	if (!count)
+		return -EINVAL;
+	if (copy_from_user(buf, user_buf, 1))
+		return -EFAULT;
+	if (buf[0] == 1)
+		ena = 1;
+
+	band[0]->ht_cap.ht_supported = ena;
+#ifdef CONFIG_BES2600_5GHZ_SUPPORT
+	band[1]->ht_cap.ht_supported = ena;
+#endif /* CONFIG_BES2600_5GHZ_SUPPORT */
+
+	return count;
+}
+
+static const struct file_operations fops_bes2600_log_control = {
+	.open = bes2600_log_control_open,
+	.read = bes2600_log_control_read,
+	.write = bes2600_log_control_write,
+	.llseek = default_llseek,
+};
+
+static const struct file_operations fops_11n = {
+	.open = bes2600_generic_open,
+	.read = bes2600_11n_read,
+	.write = bes2600_11n_write,
+	.llseek = default_llseek,
+};
+
+static ssize_t bes2600_wsm_dumps(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct bes2600_common *hw_priv = file->private_data;
+	char buf[1];
+
+	if (!count)
+		return -EINVAL;
+	if (copy_from_user(buf, user_buf, 1))
+		return -EFAULT;
+
+	if (buf[0] == '1')
+		hw_priv->wsm_enable_wsm_dumps = 1;
+	else
+		hw_priv->wsm_enable_wsm_dumps = 0;
+
+	return count;
+}
+
+static const struct file_operations fops_wsm_dumps = {
+	.open = bes2600_generic_open,
+	.write = bes2600_wsm_dumps,
+	.llseek = default_llseek,
+};
+
+#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT)
+static ssize_t bes2600_short_dump_read(struct file *file,
+	char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct bes2600_common *hw_priv = file->private_data;
+	char buf[20];
+	size_t size = 0;
+
+	sprintf(buf, "Size: %u\n", hw_priv->wsm_dump_max_size);
+	size = strlen(buf);
+
+	return simple_read_from_buffer(user_buf, count, ppos,
+					buf, size);
+}
+
+static ssize_t bes2600_short_dump_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct bes2600_common *priv = file->private_data;
+	char buf[20];
+	unsigned long dump_size = 0;
+
+	if (!count || count > 20)
+		return -EINVAL;
+	if (copy_from_user(buf, user_buf, count))
+		return -EFAULT;
+
+	if (kstrtoul(buf, 10, &dump_size))
+		return -EINVAL;
+
+	priv->wsm_dump_max_size = dump_size;
+
+	return count;
+}
+
+static const struct file_operations fops_short_dump = {
+	.open = bes2600_generic_open,
+	.write = bes2600_short_dump_write,
+	.read = bes2600_short_dump_read,
+	.llseek = default_llseek,
+};
+#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */
+
+#ifdef BES2600_DUMP_FW_DPD_LOG
+extern void bes2600_get_dpd_log(char **data, size_t *len);
+static ssize_t bes2600_dpd_log_read(struct file *file,
+	char __user *user_buf, size_t count, loff_t *ppos)
+{
+	char empty[] = {'\n', '\0'};
+	char *data;
+	size_t size;
+
+	bes2600_get_dpd_log(&data, &size);
+	if (!data) {
+		data = empty;
+		size = sizeof(empty);
+	}
+
+	return simple_read_from_buffer(user_buf, count, ppos,
+	                               data, size);
+}
+
+static const struct file_operations dpd_log_dump = {
+	.open = bes2600_generic_open,
+	.read = bes2600_dpd_log_read,
+	.llseek = default_llseek,
+};
+#endif /* BES2600_DUMP_FW_DPD_LOG */
+
+int bes2600_debug_init_common(struct bes2600_common *hw_priv)
+{
+	int ret = -ENOMEM;
+	struct bes2600_debug_common *d =
+		kzalloc(sizeof(struct bes2600_debug_common), GFP_KERNEL);
+	hw_priv->debug = d;
+	if (!d)
+		return ret;
+
+	d->debugfs_phy = debugfs_create_dir("bes2600",
+			hw_priv->hw->wiphy->debugfsdir);
+	if (!d->debugfs_phy)
+		goto err;
+
+	if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
+			hw_priv, &fops_status_common))
+		goto err;
+
+	if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
+			hw_priv, &fops_counters))
+		goto err;
+
+	if (!debugfs_create_file("11n", S_IRUSR | S_IWUSR,
+			d->debugfs_phy, hw_priv, &fops_11n))
+		goto err;
+
+	if (!debugfs_create_file("bes2600_log_control", S_IRUSR | S_IWUSR,
+			d->debugfs_phy, hw_priv, &fops_bes2600_log_control))
+		goto err;
+
+	if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
+			hw_priv, &fops_wsm_dumps))
+		goto err;
+
+#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT)
+	if (!debugfs_create_file("wsm_dump_size", S_IRUSR | S_IWUSR,
+			d->debugfs_phy, hw_priv, &fops_short_dump))
+		goto err;
+#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */
+
+#ifdef CONFIG_BES2600_WOWLAN
+	if (!debugfs_create_file("power_events", S_IRUSR, d->debugfs_phy,
+			hw_priv, &fops_power_busy_events))
+		goto err;
+#endif
+
+#ifdef BES2600_DUMP_FW_DPD_LOG
+	if (!debugfs_create_file("dpd_log", S_IRUSR, d->debugfs_phy,
+			hw_priv, &dpd_log_dump))
+		goto err;
+#endif
+
+	ret = bes2600_itp_init(hw_priv);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	hw_priv->debug = NULL;
+	debugfs_remove_recursive(d->debugfs_phy);
+	kfree(d);
+	return ret;
+}
+
+void bes2600_debug_release_common(struct bes2600_common *hw_priv)
+{
+	struct bes2600_debug_common *d = hw_priv->debug;
+	if (d) {
+		bes2600_itp_release(hw_priv);
+		hw_priv->debug = NULL;
+		kfree(d);
+	}
+}
+
+static int bes2600_status_show_priv(struct seq_file *seq, void *v)
+{
+	int i;
+	struct bes2600_vif *priv = seq->private;
+	struct bes2600_debug_priv *d = priv->debug;
+
+	seq_printf(seq, "Mode:       %s%s\n",
+		bes2600_debug_mode(priv->mode),
+		priv->listening ? " (listening)" : "");
+	seq_printf(seq, "Assoc:      %s\n",
+		bes2600_debug_join_status[priv->join_status]);
+	if (priv->rx_filter.promiscuous)
+		seq_puts(seq,   "Filter:     promisc\n");
+	else if (priv->rx_filter.fcs)
+		seq_puts(seq,   "Filter:     fcs\n");
+	if (priv->rx_filter.bssid)
+		seq_puts(seq,   "Filter:     bssid\n");
+	if (priv->bf_control.bcn_count)
+		seq_puts(seq,   "Filter:     beacons\n");
+
+	if (priv->enable_beacon ||
+			priv->mode == NL80211_IFTYPE_AP ||
+			priv->mode == NL80211_IFTYPE_ADHOC ||
+			priv->mode == NL80211_IFTYPE_MESH_POINT ||
+			priv->mode == NL80211_IFTYPE_P2P_GO)
+		seq_printf(seq, "Beaconing:  %s\n",
+			priv->enable_beacon ?
+			"enabled" : "disabled");
+	if (priv->ssid_length ||
+			priv->mode == NL80211_IFTYPE_AP ||
+			priv->mode == NL80211_IFTYPE_ADHOC ||
+			priv->mode == NL80211_IFTYPE_MESH_POINT ||
+			priv->mode == NL80211_IFTYPE_P2P_GO)
+		seq_printf(seq, "SSID:       %.*s\n",
+			(int)priv->ssid_length, priv->ssid);
+
+	for (i = 0; i < 4; ++i) {
+		seq_printf(seq, "EDCA(%d):    %d, %d, %d, %d, %d\n", i,
+			priv->edca.params[i].cwMin,
+			priv->edca.params[i].cwMax,
+			priv->edca.params[i].aifns,
+			priv->edca.params[i].txOpLimit,
+			priv->edca.params[i].maxReceiveLifetime);
+	}
+	if (priv->join_status == BES2600_JOIN_STATUS_STA) {
+		static const char *pmMode = "unknown";
+		switch (priv->powersave_mode.pmMode) {
+		case WSM_PSM_ACTIVE:
+			pmMode = "off";
+			break;
+		case WSM_PSM_PS:
+			pmMode = "on";
+			break;
+		case WSM_PSM_FAST_PS:
+			pmMode = "dynamic";
+			break;
+		}
+		seq_printf(seq, "Preamble:   %s\n",
+			bes2600_debug_preamble[
+			priv->association_mode.preambleType]);
+		seq_printf(seq, "AMPDU spcn: %d\n",
+			priv->association_mode.mpduStartSpacing);
+		seq_printf(seq, "Basic rate: 0x%.8X\n",
+			le32_to_cpu(priv->association_mode.basicRateSet));
+		seq_printf(seq, "Bss lost:   %d beacons\n",
+			priv->bss_params.beaconLostCount);
+		seq_printf(seq, "AID:        %d\n",
+			priv->bss_params.aid);
+		seq_printf(seq, "Rates:      0x%.8X\n",
+			priv->bss_params.operationalRateSet);
+		seq_printf(seq, "Powersave:  %s\n", pmMode);
+	}
+	seq_printf(seq, "RSSI thold: %d\n",
+		priv->cqm_rssi_thold);
+	seq_printf(seq, "RSSI hyst:  %d\n",
+		priv->cqm_rssi_hyst);
+	seq_printf(seq, "TXFL thold: %d\n",
+		priv->cqm_tx_failure_thold);
+	seq_printf(seq, "Linkloss:   %d\n",
+		priv->cqm_link_loss_count);
+	seq_printf(seq, "Bcnloss:    %d\n",
+		priv->cqm_beacon_loss_count);
+
+	bes2600_debug_print_map(seq, priv, "Link map:   ",
+		priv->link_id_map);
+	bes2600_debug_print_map(seq, priv, "Asleep map: ",
+		priv->sta_asleep_mask);
+	bes2600_debug_print_map(seq, priv, "PSPOLL map: ",
+		priv->pspoll_mask);
+
+	seq_puts(seq, "\n");
+
+	for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE; ++i) {
+		if (priv->link_id_db[i].status) {
+			seq_printf(seq, "Link %d:     %s, %pM\n",
+				i + 1, bes2600_debug_link_id[
+				priv->link_id_db[i].status],
+				priv->link_id_db[i].mac);
+		}
+	}
+
+	seq_puts(seq, "\n");
+
+	seq_printf(seq, "Powermgmt:  %s\n",
+		priv->powersave_enabled ? "on" : "off");
+
+	seq_printf(seq, "TXed:       %d\n",
+		d->tx);
+	seq_printf(seq, "AGG TXed:   %d\n",
+		d->tx_agg);
+	seq_printf(seq, "MULTI TXed: %d (%d)\n",
+		d->tx_multi, d->tx_multi_frames);
+	seq_printf(seq, "RXed:       %d\n",
+		d->rx);
+	seq_printf(seq, "AGG RXed:   %d\n",
+		d->rx_agg);
+	seq_printf(seq, "TX align:   %d\n",
+		d->tx_align);
+	seq_printf(seq, "TX TTL:     %d\n",
+		d->tx_ttl);
+	return 0;
+}
+
+static int bes2600_status_open_priv(struct inode *inode, struct file *file)
+{
+	return single_open(file, &bes2600_status_show_priv,
+		inode->i_private);
+}
+
+static const struct file_operations fops_status_priv = {
+	.open = bes2600_status_open_priv,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.owner = THIS_MODULE,
+};
+
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+
+static ssize_t bes2600_hang_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct bes2600_vif *priv = file->private_data;
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	char buf[1];
+
+	if (!count)
+		return -EINVAL;
+	if (copy_from_user(buf, user_buf, 1))
+		return -EFAULT;
+
+	if (priv->vif) {
+		bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ);
+		ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL);
+	} else
+		return -ENODEV;
+
+	return count;
+}
+
+static const struct file_operations fops_hang = {
+	.open = bes2600_generic_open,
+	.write = bes2600_hang_write,
+	.llseek = default_llseek,
+};
+#endif
+
+#define VIF_DEBUGFS_NAME_S 10
+int bes2600_debug_init_priv(struct bes2600_common *hw_priv,
+			   struct bes2600_vif *priv)
+{
+	int ret = -ENOMEM;
+	struct bes2600_debug_priv *d;
+	char name[VIF_DEBUGFS_NAME_S];
+
+	if (WARN_ON(!hw_priv))
+		return ret;
+
+	if (WARN_ON(!hw_priv->debug))
+		return ret;
+
+	d = kzalloc(sizeof(struct bes2600_debug_priv), GFP_KERNEL);
+	priv->debug = d;
+	if (WARN_ON(!d))
+		return ret;
+
+	memset(name, 0, VIF_DEBUGFS_NAME_S);
+	ret = snprintf(name, VIF_DEBUGFS_NAME_S, "vif_%d", priv->if_id);
+	if (WARN_ON(ret < 0))
+		goto err;
+
+	d->debugfs_phy = debugfs_create_dir(name,
+					    hw_priv->debug->debugfs_phy);
+	if (WARN_ON(!d->debugfs_phy))
+		goto err;
+
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	if (WARN_ON(!debugfs_create_file("hang", S_IWUSR, d->debugfs_phy,
+			priv, &fops_hang)))
+		goto err;
+#endif
+
+	if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
+			priv, &fops_status_priv))
+		goto err;
+
+	return 0;
+err:
+	priv->debug = NULL;
+	debugfs_remove_recursive(d->debugfs_phy);
+	kfree(d);
+	return ret;
+
+}
+
+void bes2600_debug_release_priv(struct bes2600_vif *priv)
+{
+	struct bes2600_debug_priv *d = priv->debug;
+	if (d) {
+		debugfs_remove_recursive(priv->debug->debugfs_phy);
+		priv->debug = NULL;
+		kfree(d);
+	}
+}
+
+int bes2600_print_fw_version(struct bes2600_common *hw_priv, u8* buf, size_t len)
+{
+	return snprintf(buf, len, "%s %d.%d",
+			bes2600_debug_fw_types[hw_priv->wsm_caps.firmwareType],
+			hw_priv->wsm_caps.firmwareVersion,
+			hw_priv->wsm_caps.firmwareBuildNumber);
+}
+#endif
diff --git a/drivers/staging/bes2600/debug.h b/drivers/staging/bes2600/debug.h
new file mode 100644
index 000000000000..371457755669
--- /dev/null
+++ b/drivers/staging/bes2600/debug.h
@@ -0,0 +1,191 @@
+/*
+ * DebugFS code for BES2600 mac80211 driver
+ *
+ * Copyright (c) 2011, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES2600_DEBUG_H_INCLUDED
+#define BES2600_DEBUG_H_INCLUDED
+
+#include "itp.h"
+
+struct cw200_common;
+
+struct bes2600_debug_common {
+	struct dentry *debugfs_phy;
+	int tx_cache_miss;
+	int tx_burst;
+	int rx_burst;
+	int ba_cnt;
+	int ba_acc;
+	int ba_cnt_rx;
+	int ba_acc_rx;
+#ifdef CONFIG_BES2600_ITP
+	struct bes2600_itp itp;
+#endif /* CONFIG_BES2600_ITP */
+};
+
+struct bes2600_debug_priv {
+	struct dentry *debugfs_phy;
+	int tx;
+	int tx_agg;
+	int rx;
+	int rx_agg;
+	int tx_multi;
+	int tx_multi_frames;
+	int tx_align;
+	int tx_ttl;
+};
+
+#ifdef CONFIG_BES2600_DEBUGFS
+int bes2600_debug_init_common(struct bes2600_common *hw_priv);
+int bes2600_debug_init_priv(struct bes2600_common *hw_priv,
+			   struct bes2600_vif *priv);
+void bes2600_debug_release_common(struct bes2600_common *hw_priv);
+void bes2600_debug_release_priv(struct bes2600_vif *priv);
+
+static inline void bes2600_debug_txed(struct bes2600_vif *priv)
+{
+	++priv->debug->tx;
+}
+
+static inline void bes2600_debug_txed_agg(struct bes2600_vif *priv)
+{
+	++priv->debug->tx_agg;
+}
+
+static inline void bes2600_debug_txed_multi(struct bes2600_vif *priv,
+					   int count)
+{
+	++priv->debug->tx_multi;
+	priv->debug->tx_multi_frames += count;
+}
+
+static inline void bes2600_debug_rxed(struct bes2600_vif *priv)
+{
+	++priv->debug->rx;
+}
+
+static inline void bes2600_debug_rxed_agg(struct bes2600_vif *priv)
+{
+	++priv->debug->rx_agg;
+}
+
+static inline void bes2600_debug_tx_cache_miss(struct bes2600_common *common)
+{
+	++common->debug->tx_cache_miss;
+}
+
+static inline void bes2600_debug_tx_align(struct bes2600_vif *priv)
+{
+	++priv->debug->tx_align;
+}
+
+static inline void bes2600_debug_tx_ttl(struct bes2600_vif *priv)
+{
+	++priv->debug->tx_ttl;
+}
+
+static inline void bes2600_debug_tx_burst(struct bes2600_common *hw_priv)
+{
+	++hw_priv->debug->tx_burst;
+}
+
+static inline void bes2600_debug_rx_burst(struct bes2600_common *hw_priv)
+{
+	++hw_priv->debug->rx_burst;
+}
+
+static inline void bes2600_debug_ba(struct bes2600_common *hw_priv,
+				   int ba_cnt, int ba_acc, int ba_cnt_rx,
+				   int ba_acc_rx)
+{
+	hw_priv->debug->ba_cnt = ba_cnt;
+	hw_priv->debug->ba_acc = ba_acc;
+	hw_priv->debug->ba_cnt_rx = ba_cnt_rx;
+	hw_priv->debug->ba_acc_rx = ba_acc_rx;
+}
+
+int bes2600_print_fw_version(struct bes2600_common *hw_priv, u8* buf, size_t len);
+
+#else /* CONFIG_BES2600_DEBUGFS */
+
+static inline int bes2600_debug_init_common(struct bes2600_common *hw_priv)
+{
+	return 0;
+}
+
+static inline int bes2600_debug_init_priv(struct bes2600_common *hw_priv,
+			   struct bes2600_vif *priv)
+{
+	return 0;
+}
+
+static inline void bes2600_debug_release_common(struct bes2600_common *hw_priv)
+{
+}
+
+static inline void bes2600_debug_release_priv(struct bes2600_vif *priv)
+{
+}
+
+static inline void bes2600_debug_txed(struct bes2600_vif *priv)
+{
+}
+
+static inline void bes2600_debug_txed_agg(struct bes2600_vif *priv)
+{
+}
+
+static inline void bes2600_debug_txed_multi(struct bes2600_vif *priv,
+					   int count)
+{
+}
+
+static inline void bes2600_debug_rxed(struct bes2600_vif *priv)
+{
+}
+
+static inline void bes2600_debug_rxed_agg(struct bes2600_vif *priv)
+{
+}
+
+static inline void bes2600_debug_tx_cache_miss(struct bes2600_vif *priv)
+{
+}
+
+static inline void bes2600_debug_tx_align(struct bes2600_vif *priv)
+{
+}
+
+static inline void bes2600_debug_tx_ttl(struct bes2600_vif *priv)
+{
+}
+
+static inline void bes2600_debug_tx_burst(struct bes2600_common *hw_priv)
+{
+}
+
+static inline void bes2600_debug_rx_burst(struct bes2600_common *hw_priv)
+{
+}
+
+static inline void bes2600_debug_ba(struct bes2600_common *hw_priv,
+				   int ba_cnt, int ba_acc, int ba_cnt_rx,
+				   int ba_acc_rx)
+{
+}
+
+static inline int bes2600_print_fw_version(struct bes2600_common *hw_priv, u8* buf, size_t len)
+{
+	return 0;
+}
+
+#endif /* CONFIG_BES2600_DEBUGFS */
+
+#endif /* BES2600_DEBUG_H_INCLUDED */
diff --git a/drivers/staging/bes2600/epta_coex.c b/drivers/staging/bes2600/epta_coex.c
new file mode 100644
index 000000000000..25690f38e0a0
--- /dev/null
+++ b/drivers/staging/bes2600/epta_coex.c
@@ -0,0 +1,584 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include "bes2600.h"
+#include "epta_coex.h"
+#include "epta_request.h"
+
+static bool coex_ps_en;
+static bool coex_fdd_mode;  /* fdd or fdd hybrid */
+static uint8_t epta_conn_state; /* dafault invalid stat */
+static uint32_t epta_freeze_bitmap;  /*bit0: conn, bit1: tp, bit2: tts */
+static int epta_freezed_wlan_duration[EPTA_FREEZE_MAX] = {0};
+static int epta_freezed_wlan_duration_cfg = 0;
+static int wlan_duration_cfg;
+static int bt_duration_cfg;
+static int hw_epta_enable_cfg; /* bit0~bit1: epta mode, bit7: prot flag, other bits: reserved */
+static int epta_ps_mode;	/* 0 - normal mode, 1 - power save mode */
+static int epta_adjust_cnt;
+static int coex_bt_state;
+static uint32_t g_coex_mode;
+
+#define EPTA_MODE_CFG (hw_epta_enable_cfg & 0x03)
+
+/*
+ * This function will send wsm command to set epta module with inputing parameters.
+ * The inputing parameters may not be used really if epta_freeze_bitmap is not zero.
+ * @hw_epta_enable:  when hw_epta_enable is 1, use hardware pta, epta does not work,
+	wlan_duration and bt_duration mean nothing.
+ * @wlan_duration and bt_duration:  need set hw_epta_enable to 0, use software epta.
+	Allow 10~100000 us.
+ * e.g.
+ * coex_set_epta_params(20000, 20000, 0);  means use software epta, wlan get 20ms, bt get 20ms.
+ * coex_set_epta_params(20000, 20000, 1);  means use hardware pta, hardware arbitrate.
+ */
+int coex_set_epta_params(struct bes2600_common *hw_priv, int wlan_duration, int bt_duration, int hw_epta_enable)
+{
+	struct wsm_epta_msg msg;
+
+	if (hw_priv == NULL) {
+		bes2600_dbg(BES2600_DBG_EPTA, "hw_priv is NULL\n");
+		return -1;
+	}
+
+	bes2600_dbg(BES2600_DBG_EPTA, "set epta w:%d hw:%d wc:%d fbit:%x fw:%d\n", wlan_duration, hw_epta_enable,
+		   wlan_duration_cfg, epta_freeze_bitmap, epta_freezed_wlan_duration_cfg);
+
+	if (wlan_duration_cfg == wlan_duration && bt_duration_cfg == bt_duration && hw_epta_enable_cfg == hw_epta_enable) {
+		bes2600_dbg(BES2600_DBG_EPTA, "same epta params\n");
+		return 0;
+	}
+
+	wlan_duration_cfg = wlan_duration;
+	bt_duration_cfg = bt_duration;
+	hw_epta_enable_cfg = hw_epta_enable;
+
+	/* If in freeze mode, use freezed wlan duration; */
+	if (epta_freeze_bitmap) {
+		if (epta_freeze_bitmap & EPTA_FREEZE_SCANNING) {
+			bes2600_info(BES2600_DBG_EPTA, "skip bt state in wifi scanning");
+			return 0;
+		}
+
+		/*
+		 if new wlan duration is more than freezed one, update fw settings with new duration
+		 if new wlan duration is less than freezed one, update fw settings with freezed duration
+		*/
+		if (wlan_duration < epta_freezed_wlan_duration_cfg) {
+			bes2600_info(BES2600_DBG_EPTA, "epta_freeze_bitmap:0x%x, wlan:%u < %u",
+				epta_freeze_bitmap, wlan_duration, epta_freezed_wlan_duration_cfg);
+			wlan_duration = epta_freezed_wlan_duration_cfg;
+			bt_duration = EPTA_FREEZE_TDD_PERIOD - epta_freezed_wlan_duration_cfg;
+		}
+	}
+
+	/* epta disconnect status,force use HW epta */
+	if (epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED && hw_epta_enable == 0)
+		hw_epta_enable = 1;
+
+	msg.wlan_duration = wlan_duration;
+	msg.bt_duration = bt_duration;
+	msg.hw_epta_enable = hw_epta_enable;
+
+	return wsm_epta_cmd(hw_priv, &msg);
+}
+
+static int coex_epta_freeze_update(struct bes2600_common *hw_priv)
+{
+	struct wsm_epta_msg msg;
+	int i = 0;
+	int max_freeze = 0;
+
+	if (hw_priv == NULL) {
+		bes2600_dbg(BES2600_DBG_EPTA, "hw_priv is NULL\n");
+		return -1;
+	}
+
+	bes2600_dbg(BES2600_DBG_EPTA, "epta update. bitmap:0x%x. fw:%d",
+		epta_freeze_bitmap, epta_freezed_wlan_duration_cfg);
+
+	bes2600_dbg(BES2600_DBG_EPTA, "%s, %d", __FUNCTION__, hw_epta_enable_cfg);
+
+	/* Exit freeze mode and recover the configuration of epta module; */
+	if (epta_freeze_bitmap == 0) {
+		epta_freezed_wlan_duration_cfg = 0;
+		return -1;
+	}
+
+	/* Enter freeze mode and set the configuration of the epta module; */
+	/* select max wifi freeze value */
+	for (i = 0; i < EPTA_FREEZE_MAX; i++) {
+		if (max_freeze < epta_freezed_wlan_duration[i])
+			max_freeze = epta_freezed_wlan_duration[i];
+	}
+
+	/* same cfg don't change it */
+	if (max_freeze == epta_freezed_wlan_duration_cfg)
+		return 0;
+
+	epta_freezed_wlan_duration_cfg = max_freeze;
+
+	/* only support epta_freezed_wlan_duration_cfg > wlan_duration_cfg */
+	// wlan_duration_cfg may be override by wsm_epta_cmd
+	// if (max_freeze <= wlan_duration_cfg) {
+	// 	return 0;
+	// }
+	msg.wlan_duration = max_freeze;
+	msg.bt_duration = EPTA_FREEZE_TDD_PERIOD - max_freeze;
+	msg.hw_epta_enable = 0x00;
+
+	return wsm_epta_cmd(hw_priv, &msg);
+}
+
+/*
+ * coex_epta_freeze
+ * freeze coex duration for specific senario
+ * @wlan_duration: 20~100ms
+ * @type:
+ *  EPTA_FREEZE_CONENCTING,
+ *  EPTA_FREEZE_TTS,
+ *  EPTA_FREEZE_THP,
+ * note: the value of wlan_duration should be less than 100000.
+ */
+int coex_epta_freeze(struct bes2600_common *hw_priv, int wlan_duration, uint32_t type)
+{
+	uint32_t i;
+
+	epta_freeze_bitmap |= type;
+
+	for (i = 0; i < EPTA_FREEZE_MAX; ++i) {
+		if (type & (1 << i))
+			epta_freezed_wlan_duration[i] = wlan_duration;
+	}
+	/* reset epta value */
+	epta_ps_mode = 0;
+	epta_adjust_cnt = 0;
+
+	return coex_epta_freeze_update(hw_priv);
+}
+
+static int coex_epta_recover_update(struct bes2600_common *hw_priv)
+{
+	struct wsm_epta_msg msg;
+	int i = 0;
+	int max_freeze = 0;
+
+	if (hw_priv == NULL) {
+		bes2600_dbg(BES2600_DBG_EPTA, "hw_priv is NULL\n");
+		return -1;
+	}
+
+	bes2600_dbg(BES2600_DBG_EPTA, "epta update. bitmap:0x%x. fw:%d",
+		epta_freeze_bitmap, epta_freezed_wlan_duration_cfg);
+
+	bes2600_dbg(BES2600_DBG_EPTA, "%s, %d", __FUNCTION__, hw_epta_enable_cfg);
+
+	/* Exit freeze mode and recover the configuration of epta module; */
+	if (epta_freeze_bitmap == 0) {
+		if (epta_freezed_wlan_duration_cfg == 0)
+			return -1;
+		epta_freezed_wlan_duration_cfg = 0;
+		// coex_set_epta_params will skip epta:0 after got_ip(use connected epta3)
+		msg.wlan_duration = wlan_duration_cfg;
+		msg.bt_duration = bt_duration_cfg;
+		msg.hw_epta_enable = hw_epta_enable_cfg;
+		return wsm_epta_cmd(hw_priv, &msg);
+	}
+
+	/* Enter freeze mode and set the configuration of the epta module; */
+	/* select max wifi freeze value */
+	for (i = 0; i < EPTA_FREEZE_MAX; i++) {
+		if (max_freeze < epta_freezed_wlan_duration[i])
+			max_freeze = epta_freezed_wlan_duration[i];
+	}
+
+	epta_freezed_wlan_duration_cfg = max_freeze;
+	msg.wlan_duration = max_freeze;
+	msg.bt_duration = EPTA_FREEZE_TDD_PERIOD - max_freeze;
+	msg.hw_epta_enable = hw_epta_enable_cfg;
+
+	return wsm_epta_cmd(hw_priv, &msg);
+}
+
+int coex_epta_recover(struct bes2600_common *hw_priv, uint32_t type)
+{
+	uint32_t i;
+
+	if (epta_freeze_bitmap == 0)
+		return 0;
+
+	if (type == EPTA_FREEZE_ALL) {
+		for (i = 0; i < EPTA_FREEZE_MAX; i++) {
+			epta_freezed_wlan_duration[i] = 0;
+		}
+		epta_freeze_bitmap = 0;
+	} else {
+		epta_freeze_bitmap &= (~type);
+		for (i = 0; i < EPTA_FREEZE_MAX; ++i) {
+			if (type & (1 << i))
+				epta_freezed_wlan_duration[i] = 0;
+		}
+	}
+	return coex_epta_recover_update(hw_priv);
+}
+
+int coex_epta_ps(struct bes2600_common *hw_priv, uint8_t enable)
+{
+	struct wsm_epta_msg msg;
+	if (hw_priv == NULL) {
+		bes2600_dbg(BES2600_DBG_EPTA, "hw_priv is NULL\n");
+		return -1;
+	}
+
+	if (enable && !epta_ps_mode) {
+		epta_ps_mode = 1;
+		msg.wlan_duration = EPTA_PS_WLAN_DURATION;
+		msg.bt_duration = EPTA_PS_BT_DURATION;
+		msg.hw_epta_enable = 0;
+		return wsm_epta_cmd(hw_priv, &msg);
+	} else if (!enable && epta_ps_mode) {
+		epta_ps_mode = 0;
+		msg.wlan_duration = wlan_duration_cfg;
+		msg.bt_duration = bt_duration_cfg;
+		msg.hw_epta_enable = 0;
+		return wsm_epta_cmd(hw_priv, &msg);
+	}
+
+	return 0;
+}
+
+int coex_epta_set_connect(struct bes2600_common *hw_priv, int wlan_duration, int bt_duration, int epta)
+{
+	struct wsm_epta_msg msg;
+	if (!hw_priv) {
+		bes2600_dbg(BES2600_DBG_EPTA, "hw_priv is NULL\n");
+		return -1;
+	}
+	if (epta != 4) {
+		msg.wlan_duration = (wlan_duration_cfg >= wlan_duration) ? wlan_duration_cfg : wlan_duration;
+		msg.bt_duration = (wlan_duration_cfg >= wlan_duration) ? bt_duration_cfg : bt_duration;
+	} else {
+		msg.wlan_duration = wlan_duration;
+		msg.bt_duration = bt_duration;
+	}
+	msg.hw_epta_enable = epta;
+	return wsm_epta_cmd(hw_priv, &msg);
+}
+
+static void coex_set_wifi_state_to_fw(struct bes2600_common *hw_priv, uint8_t epta_conn_state)
+{
+	static uint8_t last_state;
+
+	switch (epta_conn_state) {
+	case EPTA_STATE_WIFI_DISCONNECTED:
+	case EPTA_STATE_WIFI_CONNECTED:
+		if (last_state != epta_conn_state) {
+			last_state = epta_conn_state;
+			break;
+		} else {
+			return;
+		}
+	default:
+		return;
+	}
+
+	bes2600_dbg(BES2600_DBG_EPTA, "%s state%u\r", __func__, last_state);
+
+	switch (last_state) {
+	case EPTA_STATE_WIFI_DISCONNECTED:
+		wsm_wifi_status_cmd(hw_priv, (uint32_t)BWIFI_STATUS_IDLE);
+		break;
+	case EPTA_STATE_WIFI_CONNECTED:
+		wsm_wifi_status_cmd(hw_priv, (uint32_t)BWIFI_STATUS_CONNECTED);
+		break;
+	default:
+		break;
+	}
+}
+
+/* TDD mode conn status change */
+void coex_set_wifi_conn(struct bes2600_common *hw_priv, uint8_t connect_status)
+{
+	if (connect_status != EPTA_STATE_WIFI_SCAN_COMP) {
+		if (epta_conn_state == connect_status) {
+			bes2600_dbg(BES2600_DBG_EPTA, "same connect_status:%d\r", connect_status);
+			return;
+		}
+	}
+
+	if (connect_status == EPTA_STATE_WIFI_GOT_IP) {
+		if (epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED || epta_conn_state == EPTA_STATE_WIFI_CONNECTING) {
+			bes2600_dbg(BES2600_DBG_EPTA, "ignore got ip in disconnected\r");
+			return;
+		}
+	}
+	epta_conn_state = connect_status;
+
+	bes2600_dbg(BES2600_DBG_EPTA, "%s connect_status=%d coex_mode=%d\r", __func__, connect_status, g_coex_mode);
+
+	coex_set_wifi_state_to_fw(hw_priv, epta_conn_state);
+
+	if (g_coex_mode &  WIFI_COEX_MODE_FDD_BIT) {
+		if (g_coex_mode &  WIFI_COEX_MODE_FDD_HYBRID_BIT) {
+			if (connect_status == EPTA_STATE_WIFI_CONNECTED) {
+				coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 3);
+			} else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) {
+				coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests
+				/* HYBRID MODE: if wlan is disconnect, default use hw epta */
+				coex_epta_set_connect(hw_priv, 100000, 0, 1);
+			}
+		} else {
+			if (connect_status == EPTA_STATE_WIFI_CONNECTED) {
+				// set bt bad channel according to wifi channel
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+				if (hw_priv->channel->band == NL80211_BAND_2GHZ)
+#else
+				if (hw_priv->channel->band == IEEE80211_BAND_2GHZ)
+#endif
+					wsm_epta_wifi_chan_cmd(hw_priv, hw_priv->channel->hw_value, hw_priv->ht_info.channel_type);
+			} else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) {
+				coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests
+				coex_epta_set_connect(hw_priv, 100000, 0, 1);
+				wsm_epta_wifi_chan_cmd(hw_priv, 0, 0);
+			}
+		}
+	} else {
+		int wlan_tdd_duration = EPTA_TDD_CONNECT_WIFI;
+		int bt_tdd_duration = EPTA_TDD_CONNECT_BT;
+
+		/* if wlan_duration_cfg>　EPTA_TDD_CONNECT_WIFI; chose wlan_duration_cfg, in TDD mode */
+		if (wlan_duration_cfg > EPTA_TDD_CONNECT_WIFI) {
+			wlan_tdd_duration = wlan_duration_cfg;
+			bt_tdd_duration = bt_duration_cfg;
+		}
+		if (connect_status == EPTA_STATE_WIFI_CONNECTED) {
+			coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 3);
+		} else if (connect_status == EPTA_STATE_WIFI_SCANNING) {
+			/* in scan status, only valid param 0, others wlan_tdd_duration,bt_tdd_duratio is invalid */
+			coex_epta_freeze(hw_priv, wlan_duration_cfg, EPTA_FREEZE_SCANNING);
+		} else if (connect_status == EPTA_STATE_WIFI_SCAN_COMP) {
+			coex_epta_set_connect(hw_priv, wlan_duration_cfg, bt_duration_cfg, 0);
+		} else if (connect_status == EPTA_STATE_WIFI_GOT_IP) {
+			coex_epta_set_connect(hw_priv, 20000, 80000, 3);
+			coex_epta_recover(hw_priv, EPTA_FREEZE_SCANNING | EPTA_FREEZE_CONNECTING);
+		} else if (connect_status == EPTA_STATE_WIFI_CONNECTING) {
+			coex_epta_freeze(hw_priv, wlan_tdd_duration, EPTA_FREEZE_CONNECTING);
+			if (coex_fdd_mode == 0 && bt_duration_cfg > 50000) //bt audio
+				coex_epta_set_connect(hw_priv, 30000, 15000, 4);
+		} else if (connect_status == EPTA_STATE_WIFI_DISCONNECTED) {
+			coex_epta_recover(hw_priv, EPTA_FREEZE_ALL); // wifi disconect need to recover all requests
+			/* TDD MODE: if wlan is disconnect, default use hw epta */
+			// coex_epta_set_connect(hw_priv, 100000, 0, 1);
+		} else {
+			/* do nothing */
+		}
+	}
+}
+
+bool coex_is_wifi_inactive()
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s, epta_conn_state:%d", __FUNCTION__, epta_conn_state);
+	return epta_conn_state == EPTA_STATE_WIFI_DISCONNECTED;
+}
+/*
+ * coex_set_epta_tts
+ * @tts_state: 0 - tts start, 1 - tts end
+ */
+void coex_set_epta_tts(struct bes2600_common *hw_priv, uint32_t tts_state)
+{
+	if (EPTA_MODE_CFG == 0) {
+		if (tts_state) {
+			coex_epta_recover(hw_priv, EPTA_FREEZE_TTS);
+		} else {
+			coex_epta_freeze(hw_priv, 50000, EPTA_FREEZE_TTS);
+		}
+	}
+}
+
+void coex_set_epta_thp(struct bes2600_common *hw_priv, uint32_t total_bps)
+{
+	int epta_adjust_inv = 3;
+	int wlan_tp_low;
+
+	if (coex_fdd_mode && epta_conn_state >= EPTA_STATE_WIFI_GOT_IP)
+		return;
+
+	//bes2600_dbg(BES2600_DBG_EPTA, "coex_set_epta_thp %d bt_state %d epta_freeze_bitmap=%d epta_adjust_cnt=%d coex_ps_en=%d fw=%d\n",
+	//       total_bps, coex_bt_state, epta_freeze_bitmap, epta_adjust_cnt, coex_ps_en, epta_freezed_wlan_duration_cfg);
+
+	/* sniffer mode always work with ble and tts, need adjust more quickly */
+	// wlan_tp_low = netdev_sniffer_get_stat() ? EPTA_WLAN_TP_LOW : 500;
+	wlan_tp_low = 500;
+
+	/* only need adjust in sw epta mode */
+	if (EPTA_MODE_CFG != 0)
+		return;
+
+	if ((coex_bt_state == 0) && (total_bps > EPTA_WLAN_TP_HIGH)) {
+		coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_HIGH, EPTA_FREEZE_THP);
+	} else if (total_bps > EPTA_WLAN_TP_MEDIUM) {
+		coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_MEDIUM, EPTA_FREEZE_THP);
+	} else if (total_bps > wlan_tp_low) {
+		coex_epta_freeze(hw_priv, EPTA_ADJUST_WLAN_DURATION_LOW, EPTA_FREEZE_THP);
+	} else {
+		if ((epta_freeze_bitmap & (1 << EPTA_FREEZE_THP)) &&
+			(((++epta_adjust_cnt) % epta_adjust_inv) == 0)) {
+			// recover if wifi idle 3 secs
+			epta_adjust_cnt = 0;
+			coex_epta_recover(hw_priv, EPTA_FREEZE_THP);
+		} else if (coex_ps_en && !epta_freeze_bitmap) {
+			if ((total_bps > EPTA_WLAN_TP_PS)) {
+				coex_epta_ps(hw_priv, 0);
+				epta_adjust_cnt = 0;
+			} else if ((total_bps < EPTA_WLAN_TP_PS) &&
+				   (((++epta_adjust_cnt) % epta_adjust_inv) == 0)) {
+				coex_epta_ps(hw_priv, 1);
+			}
+		}
+	}
+}
+
+// void coex_band_update(struct bes2600_common *hw_priv, enum nl80211_band band)
+// {
+// 	bes2600_info(BES2600_DBG_EPTA, "coex_band_update band:%u\n", (uint32_t)band);
+// }
+
+void coex_rssi_update(struct bes2600_common *hw_priv, int rssi, int channel, int connected)
+{
+	bool fdd_en = 0;
+
+	if (g_coex_mode == 0 || // tdd
+		g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) {
+
+		bes2600_info(BES2600_DBG_EPTA, "coex_rssi_update rssi:%d, ch:%d, con:%d\n",
+			rssi, channel, connected);
+		if (channel > 14) {
+			fdd_en = 1;
+		///TODO: HYBRID mode
+		// } else if (rssi >= COEX_FDD_RSSI_THR) {
+		// 	fdd_en = 1;
+		// } else if (rssi < COEX_TDD_RSSI_THR) {
+		// 	fdd_en = 0;
+		}
+
+		if (fdd_en != coex_fdd_mode) {
+			coex_fdd_mode = fdd_en;
+			coex_set_epta_params(hw_priv, wlan_duration_cfg, bt_duration_cfg, hw_epta_enable_cfg);
+		}
+	}
+}
+
+void coex_set_bt_state(struct bes2600_common *hw_priv, int state)
+{
+	bes2600_info(BES2600_DBG_EPTA, "coex_set_bt_state %d\n", state);
+	coex_bt_state = state;
+}
+
+void coex_peroid_handle(struct bes2600_common *hw_priv, int connected, int rssi, int channel, uint32_t tp)
+{
+	if (connected) {
+		/*
+		 * Adjust wifi/bt duration dynamically according to throughput for a better performance;
+		 */
+		coex_set_epta_thp(hw_priv, tp);
+	}
+
+	if (g_coex_mode == 0 || // tdd
+		g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) {
+		coex_rssi_update(hw_priv, rssi, channel, connected);
+	}
+}
+
+/*
+ * set fdd or fdd hybrid
+ * 1: fdd
+ * 0: fddhybrid
+ */
+void coex_set_fdd_mode(bool fdd_mode)
+{
+	if (g_coex_mode == 0 || // tdd
+		g_coex_mode == (WIFI_COEX_MODE_FDD_HYBRID_BIT | WIFI_COEX_MODE_FDD_BIT)) { //hybrid
+		bes2600_dbg(BES2600_DBG_EPTA, "%s, %d", __FUNCTION__, fdd_mode);
+		coex_fdd_mode = fdd_mode;
+	}
+}
+
+bool coex_is_fdd_mode()
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s, %d", __FUNCTION__, coex_fdd_mode);
+	return coex_fdd_mode;
+}
+
+bool coex_is_bt_a2dp()
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s, coex_bt_state:%d", __FUNCTION__, coex_bt_state);
+	return coex_bt_state == 3;
+}
+
+bool coex_is_bt_inactive()
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s, coex_bt_state:%d", __FUNCTION__, coex_bt_state);
+	return coex_bt_state == 0;
+}
+
+int coex_init_mode(struct bes2600_common *hw_priv, int coex_mode)
+{
+	bes2600_info(BES2600_DBG_EPTA, "coex_init_mode coex_mode %d\n", coex_mode);
+
+	g_coex_mode = coex_mode;
+
+	if (coex_mode & WIFI_COEX_MODE_FDD_BIT)
+		coex_fdd_mode = true;
+	else
+		coex_fdd_mode = false;
+
+	coex_wifi_bt_ts_thread_init(hw_priv);
+	return 0;
+}
+
+int coex_deinit_mode(struct bes2600_common *hw_priv)
+{
+	bes2600_info(BES2600_DBG_EPTA, "coex_deinit_mode\n");
+
+	coex_wifi_bt_ts_thread_deinit(hw_priv);
+
+	return 0;
+}
+
+int coex_start(struct bes2600_common *hw_priv)
+{
+	bes2600_info(BES2600_DBG_EPTA, "%s\n", __FUNCTION__);
+
+	coex_ps_en = false;
+	if (g_coex_mode & WIFI_COEX_MODE_FDD_BIT)
+		coex_fdd_mode = true;
+	else
+		coex_fdd_mode = false;
+	epta_conn_state = 0xff;
+	epta_freeze_bitmap = 0;
+	epta_freezed_wlan_duration_cfg = 0;
+	wlan_duration_cfg = 20000;
+	bt_duration_cfg = 80000;
+	hw_epta_enable_cfg = 0;
+	epta_ps_mode = 0;
+	epta_adjust_cnt = 0;
+	coex_bt_state = 0;
+
+	return 0;
+}
+
+int coex_stop(struct bes2600_common *hw_priv)
+{
+	bes2600_info(BES2600_DBG_EPTA, "%s\n", __FUNCTION__);
+	return 0;
+}
diff --git a/drivers/staging/bes2600/epta_coex.h b/drivers/staging/bes2600/epta_coex.h
new file mode 100644
index 000000000000..bc9eed6cc794
--- /dev/null
+++ b/drivers/staging/bes2600/epta_coex.h
@@ -0,0 +1,86 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __EPTA_COEX_H__
+#define __EPTA_COEX_H__
+
+#include <linux/types.h>
+#include <linux/nl80211.h>
+
+#include "bes2600.h"
+
+#define WIFI_COEX_MODE_FDD_BIT          (1<<0)
+#define WIFI_COEX_MODE_FDD_HYBRID_BIT   (1<<1)
+
+enum bwifi_epta_state {
+	EPTA_STATE_WIFI_DISCONNECTED     = 0,
+	EPTA_STATE_WIFI_SCANNING         = 1,
+	EPTA_STATE_WIFI_SCAN_COMP		 = 2,
+	EPTA_STATE_WIFI_CONNECTING       = 3,
+	EPTA_STATE_WIFI_CONNECTED        = 4,
+	EPTA_STATE_WIFI_GOT_IP           = 5,
+	EPTA_STATE_WIFI_TTS_START        = 6,
+	EPTA_STATE_WIFI_TTS_END          = 7,
+
+	EPTA_STATE_NUM
+};
+
+/* max FREEZE BIT */
+#define EPTA_FREEZE_MAX 32
+typedef enum {
+	EPTA_FREEZE_SCANNING = 1 << 0,
+	EPTA_FREEZE_CONNECTING = 1 << 1,
+	EPTA_FREEZE_TTS = 1 << 2,
+	EPTA_FREEZE_THP = 1 << 3,
+	/* clear all FREEZE BIT */
+	EPTA_FREEZE_ALL
+} EPTA_FREEZE_TYPE_T;
+
+#ifndef COEX_TDD_RSSI_THR
+#define COEX_TDD_RSSI_THR                 (-15)
+#endif
+#ifndef COEX_FDD_RSSI_THR
+#define COEX_FDD_RSSI_THR                 (-10)
+#endif
+
+#define EPTA_PS_WLAN_DURATION             (20000)
+#define EPTA_PS_BT_DURATION               (80000)
+#define EPTA_ADJUST_WLAN_DURATION_HIGH    (80000)
+#define EPTA_ADJUST_WLAN_DURATION_MEDIUM  (55000)
+#define EPTA_ADJUST_WLAN_DURATION_LOW     (40000)
+#define EPTA_WLAN_TP_HIGH                 (4000)
+#define EPTA_WLAN_TP_MEDIUM               (2000)
+#define EPTA_WLAN_TP_LOW                  (100)
+#define EPTA_WLAN_TP_PS                   (50)
+#define EPTA_FREEZE_TDD_PERIOD	          (102400)
+#define EPTA_TDD_CONNECT_WIFI             (50000)
+#define EPTA_TDD_CONNECT_BT               (50000)
+
+int coex_set_epta_params(struct bes2600_common *hw_priv, int wlan_duraiton, int bt_duration, int hw_epta_enable);
+void coex_peroid_handle(struct bes2600_common *hw_priv, int connected, int rssi, int channel, uint32_t tp);
+void coex_set_bt_state(struct bes2600_common *hw_priv, int state);
+void coex_set_wifi_conn(struct bes2600_common *hw_priv, uint8_t connect_status);
+void coex_set_epta_tts(struct bes2600_common *hw_priv, uint32_t tts_state);
+int coex_init_mode(struct bes2600_common *hw_priv, int coex_mode);
+int coex_deinit_mode(struct bes2600_common *hw_priv);
+int coex_start(struct bes2600_common *hw_priv);
+int coex_stop(struct bes2600_common *hw_priv);
+
+void coex_rssi_update(struct bes2600_common *hw_priv, int rssi, int channel, int connected);
+// void coex_band_update(struct bes2600_common *hw_priv, enum nl80211_band band);
+bool coex_is_fdd_mode(void);
+void coex_set_fdd_mode(bool fdd_mode);
+bool coex_is_bt_a2dp(void);
+bool coex_is_bt_inactive(void);
+bool coex_is_wifi_inactive(void);
+
+#endif
+
+
diff --git a/drivers/staging/bes2600/epta_request.c b/drivers/staging/bes2600/epta_request.c
new file mode 100644
index 000000000000..44676dcbc6d7
--- /dev/null
+++ b/drivers/staging/bes2600/epta_request.c
@@ -0,0 +1,650 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+
+#include "epta_coex.h"
+#include "epta_request.h"
+
+#define EPTA_PERIOD_TIME					102400
+
+#define BT_TIME_MAX							80000
+
+#define BT_SHUTDOWN_TIME					0
+#define BT_SHUTDOWN_MIN_TIME				0
+#define BT_DISCONNECTED_TIME				0
+#define BT_DISCONNECTED_MIN_TIME			0
+#define BT_CONNECTING_TIME					60000
+#define BT_CONNECTING_MIN_TIME				0
+#define BT_CONNECTED_TIME					0
+#define BT_CONNECTED_MIN_TIME				25000
+#define BT_CONNECTED_SNIFF_TIME				0
+#define BT_CONNECTED_SNIFF_MIN_TIME			20000
+
+#define BT_BOTHSCAN_DISABLE_TIME			0
+#define BT_BOTHSCAN_DISABLE_MIN_TIME		0
+#define BT_BOTHSCAN_ENABLE_TIME				0
+#define BT_BOTHSCAN_ENABLE_MIN_TIME			20000
+#define BT_PSCAN_ENABLE_TIME				0
+#define BT_PSCAN_ENABLE_MIN_TIME			20000
+#define BT_ISCAN_ENABLE_TIME				0
+#define BT_ISCAN_ENABLE_MIN_TIME			20000
+
+#define BT_AUDIO_NONE_TIME					0
+#define BT_AUDIO_NONE_MIN_TIME				0
+#define BT_AUDIO_A2DP_TIME					60000
+#define BT_AUDIO_A2DP_MIN_TIME				0
+#define BT_AUDIO_SCO_TIME					80000
+#define BT_AUDIO_SCO_MIN_TIME				0
+
+#define BT_INQ_START_TIME					20000
+#define BT_INQ_START_MIN_TIME				30000
+#define BT_INQ_STOP_TIME					0
+#define BT_INQ_STOP_MIN_TIME				0
+
+#define BT_LE_SCAN_START_TIME				20000
+#define BT_LE_SCAN_START_MIN_TIME			30000
+#define BT_LE_SCAN_STOP_TIME				0
+#define BT_LE_SCAN_STOP_MIN_TIME			0
+
+#define BT_LE_ADV_START_TIME				20000
+#define BT_LE_ADV_START_MIN_TIME			40000
+#define BT_LE_ADV_STOP_TIME					0
+#define BT_LE_ADV_STOP_MIN_TIME				0
+
+#define BT_LE_CONNECTED_TIME				0
+#define BT_LE_CONNECTED_MIN_TIME			50000
+#define BT_LE_DISCONNECTED_TIME				0
+#define BT_LE_DISCONNECTED_MIN_TIME			0
+
+// BIT[2:0]
+#define BT_REQUEST_STATUS_SHIFT       0
+#define BT_REQUEST_STATUS_MASK        (0x7 << BT_REQUEST_STATUS_SHIFT)
+#define BT_REQUEST_STATUS_VALUE(n)    (((n) & BT_REQUEST_STATUS_MASK) >> BT_REQUEST_STATUS_SHIFT)
+
+// BIT[5:3]
+#define BT_REQUEST_SCAN_SHIFT         3
+#define BT_REQUEST_SCAN_MASK          (0x7 << BT_REQUEST_SCAN_SHIFT)
+#define BT_REQUEST_SCAN_VALUE(n)      (((n) & BT_REQUEST_SCAN_MASK) >> BT_REQUEST_SCAN_SHIFT)
+
+// BIT[8:6]
+#define BT_REQUEST_AUDIO_SHIFT        6
+#define BT_REQUEST_AUDIO_MASK         (0x7 << BT_REQUEST_AUDIO_SHIFT)
+#define BT_REQUEST_AUDIO_VALUE(n)     (((n) & BT_REQUEST_AUDIO_MASK) >> BT_REQUEST_AUDIO_SHIFT)
+
+// BIT[10:9]
+#define BT_REQUEST_INQ_SHIFT          9
+#define BT_REQUEST_INQ_MASK           (0x3 << BT_REQUEST_INQ_SHIFT)
+#define BT_REQUEST_INQ_VALUE(n)       (((n) & BT_REQUEST_INQ_MASK) >> BT_REQUEST_INQ_SHIFT)
+
+// BIT[12:11]
+#define BT_REQUEST_LE_SCAN_SHIFT      11
+#define BT_REQUEST_LE_SCAN_MASK       (0x3 << BT_REQUEST_LE_SCAN_SHIFT)
+#define BT_REQUEST_LE_SCAN_VALUE(n)   (((n) & BT_REQUEST_LE_SCAN_MASK) >> BT_REQUEST_LE_SCAN_SHIFT)
+
+// BIT[15:13]
+#define BT_REQUEST_LE_ADV_SHIFT       13
+#define BT_REQUEST_LE_ADV_MASK        (0x7 << BT_REQUEST_LE_ADV_SHIFT)
+#define BT_REQUEST_LE_ADV_VALUE(n)    (((n) & BT_REQUEST_LE_ADV_MASK) >> BT_REQUEST_LE_ADV_SHIFT)
+
+// BIT[17:16]
+#define BT_REQUEST_LE_STATUS_SHIFT    16
+#define BT_REQUEST_LE_STATUS_MASK     (0x3 << BT_REQUEST_LE_STATUS_SHIFT)
+#define BT_REQUEST_LE_STATUS_VALUE(n) (((n) & BT_REQUEST_LE_STATUS_MASK) >> BT_REQUEST_LE_STATUS_SHIFT)
+
+typedef enum {
+	BWIFI_BT_STATUS_SHUTDOWN          = 0,
+	BWIFI_BT_STATUS_DISCONNECTED      = 1,
+	BWIFI_BT_STATUS_CONNECTING        = 2,
+	BWIFI_BT_STATUS_CONNECTED_SNIFF   = 3,//
+	BWIFI_BT_STATUS_CONNECTED         = 4,//
+} BWIFI_BT_STATUS_T;
+
+typedef enum {
+	BWIFI_BT_BOTHSCAN_DISABLE         = 0,
+	BWIFI_BT_BOTHSCAN_ENABLE          = 1,
+	BWIFI_BT_PSCAN_ENABLE             = 2,
+	BWIFI_BT_ISCAN_ENABLE             = 3,
+} BWIFI_BT_SCAN_T;
+
+typedef enum {
+	BWIFI_BT_AUDIO_NONE               = 0,
+	BWIFI_BT_AUDIO_A2DP               = 1,
+	BWIFI_BT_AUDIO_SCO                = 2,
+} BWIFI_BT_AUDIO_T;
+
+typedef enum {
+	BWIFI_BT_INQ_STOP                 = 0,//
+	BWIFI_BT_INQ_START                = 1,//
+} BWIFI_BT_INQ_T;
+
+typedef enum {
+	BWIFI_LE_SCAN_STOP                = 0,//
+	BWIFI_LE_SCAN_START               = 1,//
+} BWIFI_BT_LE_SCAN_T;
+
+typedef enum {
+	BWIFI_LE_ADV_STOP                 = 0,//
+	BWIFI_LE_ADV_START                = 1,//
+} BWIFI_BT_LE_ADV_T;
+
+typedef enum {
+	BWIFI_LE_DISCONNECTED             = 0,//
+	BWIFI_LE_CONNECTED                = 1,//
+} BWIFI_BT_LE_STATUS_T;
+
+enum COEX_BT_OPER_T {
+	COEX_BT_OPER_STATUS,
+	COEX_BT_OPER_SCAN,
+	COEX_BT_OPER_AUDIO,
+	COEX_BT_OPER_INQ,
+	COEX_BT_OPER_LE_SCAN,
+	COEX_BT_OPER_LE_ADV,
+	COEX_BT_OPER_LE_STATUS,
+
+	COEX_BT_OPER_NUM,
+};
+
+union COEX_BT_OPER_TYPE_T {
+	BWIFI_BT_STATUS_T status;
+	BWIFI_BT_SCAN_T scan;
+	BWIFI_BT_AUDIO_T audio;
+	BWIFI_BT_INQ_T inq;
+	BWIFI_BT_LE_SCAN_T le_scan;
+	BWIFI_BT_LE_ADV_T le_adv;
+	BWIFI_BT_LE_STATUS_T le_status;
+};
+
+struct COEX_BT_OPER_TIME_T {
+	enum COEX_BT_OPER_T oper;
+	union COEX_BT_OPER_TYPE_T type;
+	uint32_t time;
+	uint32_t min_time;
+};
+
+struct COEX_BT_OPER_TIME_T g_coex_bt_oper[COEX_BT_OPER_NUM];
+
+static void coex_bt_time_init(void)
+{
+	memset(g_coex_bt_oper, 0, sizeof(g_coex_bt_oper));
+	g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_SHUTDOWN;
+	g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_BOTHSCAN_DISABLE;
+	g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE;
+	g_coex_bt_oper[COEX_BT_OPER_INQ].type.inq = BWIFI_BT_INQ_STOP;
+	g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].type.le_scan = BWIFI_LE_SCAN_STOP;
+	g_coex_bt_oper[COEX_BT_OPER_LE_ADV].type.le_adv = BWIFI_LE_ADV_STOP;
+	g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].type.le_status = BWIFI_LE_DISCONNECTED;
+}
+
+static uint32_t coex_calc_bt_time(void)
+{
+	uint32_t i;
+	uint32_t time = 0, min_time = 0;
+
+	for (i = 0; i < COEX_BT_OPER_NUM; ++i) {
+		time += g_coex_bt_oper[i].time;
+		if (min_time < g_coex_bt_oper[i].min_time)
+			min_time = g_coex_bt_oper[i].min_time;
+	}
+
+	bes2600_dbg(BES2600_DBG_EPTA, "%s time:%u, min_time:%u", __func__, time, min_time);
+	time = time < min_time ? min_time : time;
+	return time < BT_TIME_MAX ? time : BT_TIME_MAX;
+}
+
+void coex_calc_wifi_scan_time(uint32_t *min_chan, uint32_t *max_chan)
+{
+	uint32_t time = coex_calc_bt_time();
+
+	if (time == 0) {
+		*min_chan = 90;
+		*max_chan = 90;
+	} else if (time < 40000) {
+		*min_chan = 50;
+		*max_chan = 110;
+	} else if (time < 60000) {
+		*min_chan = 40;
+		*max_chan = 110;
+	} else if (time < 80000) {
+		*min_chan = 30;
+		*max_chan = 120;
+	} else {
+		*min_chan = 30;
+		*max_chan = 130;
+	}
+}
+
+static void coex_bt_state_notify(struct bes2600_common *hw_priv)
+{
+	int32_t wifi_dur, bt_dur, mode;
+
+	bt_dur = coex_calc_bt_time();
+	wifi_dur = EPTA_PERIOD_TIME - bt_dur;
+	mode = 0;
+	coex_set_epta_params(hw_priv, wifi_dur, bt_dur, mode);
+}
+
+static void coex_bt_oper_status(struct bes2600_common *hw_priv, BWIFI_BT_STATUS_T type)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type);
+
+	switch (type) {
+	case BWIFI_BT_STATUS_SHUTDOWN:
+		coex_bt_time_init();
+		break;
+	case BWIFI_BT_STATUS_DISCONNECTED:
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_DISCONNECTED;
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_DISCONNECTED_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_DISCONNECTED_MIN_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_NONE_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_NONE_MIN_TIME;
+		break;
+	case BWIFI_BT_STATUS_CONNECTING:
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_CONNECTING;
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_CONNECTING_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_CONNECTING_MIN_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_NONE_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_NONE_MIN_TIME;
+		break;
+	case BWIFI_BT_STATUS_CONNECTED:
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_CONNECTED;
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_CONNECTED_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_CONNECTED_MIN_TIME;
+		break;
+	case BWIFI_BT_STATUS_CONNECTED_SNIFF:
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].type.status = BWIFI_BT_STATUS_CONNECTED_SNIFF;
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].time = BT_CONNECTED_SNIFF_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_STATUS].min_time = BT_CONNECTED_SNIFF_MIN_TIME;
+		break;
+	default:
+		bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type);
+		break;
+	}
+}
+
+static void coex_bt_oper_scan(struct bes2600_common *hw_priv, BWIFI_BT_SCAN_T type)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type);
+
+	switch (type) {
+	case BWIFI_BT_BOTHSCAN_DISABLE:
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_BOTHSCAN_DISABLE;
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_BOTHSCAN_DISABLE_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_BOTHSCAN_DISABLE_MIN_TIME;
+		break;
+	case BWIFI_BT_BOTHSCAN_ENABLE:
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_BOTHSCAN_ENABLE;
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_BOTHSCAN_ENABLE_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_BOTHSCAN_ENABLE_MIN_TIME;
+		break;
+	case BWIFI_BT_PSCAN_ENABLE:
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_PSCAN_ENABLE;
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_PSCAN_ENABLE_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_PSCAN_ENABLE_MIN_TIME;
+		break;
+	case BWIFI_BT_ISCAN_ENABLE:
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].type.scan = BWIFI_BT_ISCAN_ENABLE;
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].time = BT_ISCAN_ENABLE_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_SCAN].min_time = BT_ISCAN_ENABLE_MIN_TIME;
+		break;
+	default:
+		bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type);
+		break;
+	}
+}
+
+static void coex_bt_oper_audio(struct bes2600_common *hw_priv, BWIFI_BT_AUDIO_T type)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type);
+
+	switch (type) {
+	case BWIFI_BT_AUDIO_NONE:
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_NONE;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_NONE_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_NONE_MIN_TIME;
+		break;
+	case BWIFI_BT_AUDIO_A2DP:
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_A2DP;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_A2DP_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_A2DP_MIN_TIME;
+		break;
+	case BWIFI_BT_AUDIO_SCO:
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].type.audio = BWIFI_BT_AUDIO_SCO;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].time = BT_AUDIO_SCO_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_AUDIO].min_time = BT_AUDIO_SCO_MIN_TIME;
+		break;
+	default:
+		bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type);
+		break;
+	}
+}
+
+static void coex_bt_oper_inq(struct bes2600_common *hw_priv, BWIFI_BT_INQ_T type)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type);
+
+	switch (type) {
+	case BWIFI_BT_INQ_START:
+		g_coex_bt_oper[COEX_BT_OPER_INQ].type.inq = BWIFI_BT_INQ_START;
+		g_coex_bt_oper[COEX_BT_OPER_INQ].time = BT_INQ_START_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_INQ].min_time = BT_INQ_START_MIN_TIME;
+		break;
+	case BWIFI_BT_INQ_STOP:
+		g_coex_bt_oper[COEX_BT_OPER_INQ].type.inq = BWIFI_BT_INQ_STOP;
+		g_coex_bt_oper[COEX_BT_OPER_INQ].time = BT_INQ_STOP_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_INQ].min_time = BT_INQ_STOP_MIN_TIME;
+		break;
+	default:
+		bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type);
+		break;
+	}
+}
+
+static void coex_bt_oper_le_scan(struct bes2600_common *hw_priv, BWIFI_BT_LE_SCAN_T type)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type);
+
+	switch (type) {
+	case BWIFI_LE_SCAN_START:
+		g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].type.le_scan = BWIFI_LE_SCAN_START;
+		g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].time = BT_LE_SCAN_START_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].min_time = BT_LE_SCAN_START_MIN_TIME;
+		break;
+	case BWIFI_LE_SCAN_STOP:
+		g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].type.le_scan = BWIFI_LE_SCAN_STOP;
+		g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].time = BT_LE_SCAN_STOP_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_LE_SCAN].min_time = BT_LE_SCAN_STOP_MIN_TIME;
+		break;
+	default:
+		bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type);
+		break;
+	}
+}
+
+static void coex_bt_oper_le_adv(struct bes2600_common *hw_priv, BWIFI_BT_LE_ADV_T type)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type);
+
+	switch (type) {
+	case BWIFI_LE_ADV_START:
+		g_coex_bt_oper[COEX_BT_OPER_LE_ADV].type.le_adv = BWIFI_LE_ADV_START;
+		g_coex_bt_oper[COEX_BT_OPER_LE_ADV].time = BT_LE_ADV_START_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_LE_ADV].min_time = BT_LE_ADV_START_MIN_TIME;
+		break;
+	case BWIFI_LE_ADV_STOP:
+		g_coex_bt_oper[COEX_BT_OPER_LE_ADV].type.le_adv = BWIFI_LE_ADV_STOP;
+		g_coex_bt_oper[COEX_BT_OPER_LE_ADV].time = BT_LE_ADV_STOP_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_LE_ADV].min_time = BT_LE_ADV_STOP_MIN_TIME;
+		break;
+	default:
+		bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type);
+		break;
+	}
+}
+
+static void coex_bt_oper_le_status(struct bes2600_common *hw_priv, BWIFI_BT_LE_STATUS_T type)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s type:%d", __func__, type);
+
+	switch (type) {
+	case BWIFI_LE_CONNECTED:
+		g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].type.le_status = BWIFI_LE_CONNECTED;
+		g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].time = BT_LE_CONNECTED_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].min_time = BT_LE_CONNECTED_MIN_TIME;
+		break;
+	case BWIFI_LE_DISCONNECTED:
+		g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].type.le_status = BWIFI_LE_DISCONNECTED;
+		g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].time = BT_LE_DISCONNECTED_TIME;
+		g_coex_bt_oper[COEX_BT_OPER_LE_STATUS].min_time = BT_LE_DISCONNECTED_MIN_TIME;
+		break;
+	default:
+		bes2600_err(BES2600_DBG_EPTA, "%s type error:%d", __func__, type);
+		break;
+	}
+}
+
+static int coex_wifi_state_notify(struct bes2600_common *hw_priv, enum bwifi_epta_state state)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s state:%d", __func__, state);
+	switch (state) {
+	case EPTA_STATE_WIFI_DISCONNECTED:
+		coex_set_wifi_conn(hw_priv, state);
+		break;
+	case EPTA_STATE_WIFI_SCANNING:
+		coex_set_wifi_conn(hw_priv, state);
+		break;
+	case EPTA_STATE_WIFI_SCAN_COMP:
+		coex_set_wifi_conn(hw_priv, state);
+		break;
+	case EPTA_STATE_WIFI_CONNECTING:
+		coex_set_wifi_conn(hw_priv, state);
+		break;
+	case EPTA_STATE_WIFI_CONNECTED:
+		coex_set_wifi_conn(hw_priv, state);
+		break;
+	case EPTA_STATE_WIFI_GOT_IP:
+		coex_set_wifi_conn(hw_priv, state);
+		break;
+	case EPTA_STATE_WIFI_TTS_START:
+		coex_set_epta_tts(hw_priv, 0);
+		break;
+	case EPTA_STATE_WIFI_TTS_END:
+		coex_set_epta_tts(hw_priv, 1);
+		break;
+	default:
+		return -1;
+	}
+	return 0;
+}
+
+static void coex_wifi_idle(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+	coex_set_fdd_mode(false);
+	coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_DISCONNECTED);
+}
+
+static void coex_wifi_scanning(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+	coex_set_fdd_mode(false); //scan use tdd
+	coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_SCANNING);
+}
+
+static void coex_wifi_scan_comp(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+	coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_SCAN_COMP);
+}
+
+static void coex_wifi_connecting(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+	coex_set_fdd_mode(false);
+	coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTING);
+}
+
+static void coex_wifi_connecting_5g(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+	coex_set_fdd_mode(true);
+	coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTING);
+}
+
+static void coex_wifi_connected(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+	coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTED);
+}
+
+static void coex_wifi_connected_5g(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+	coex_set_fdd_mode(true);
+	coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_CONNECTED);
+}
+
+static void coex_wifi_got_ip(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+	coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_GOT_IP);
+}
+
+static void coex_wifi_got_ip_5g(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+	coex_set_fdd_mode(true); // used for scan -> got ip 5g
+	coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_GOT_IP);
+}
+
+static void coex_wifi_disconnecting(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+}
+
+static void coex_wifi_disconnected(struct bes2600_common *hw_priv, BWIFI_STATUS_T value)
+{
+	bes2600_dbg(BES2600_DBG_EPTA, "%s", __func__);
+	coex_set_fdd_mode(false);
+	coex_wifi_state_notify(hw_priv, EPTA_STATE_WIFI_DISCONNECTED);
+}
+
+static int coex_wifi_bt_ts_request(struct bes2600_common *hw_priv, COEX_TS_TYPE_T type, uint32_t value)
+{
+	COEX_WIFI_BT_TS_T *wifi_bt_ts_event;
+
+	bes2600_info(BES2600_DBG_EPTA, "%s type:%u, value:0x%x", __func__, type, value);
+
+	if (atomic_read(&hw_priv->netdevice_start) == 0) {
+		bes2600_info(BES2600_DBG_EPTA, "net down. skip");
+		return 0;
+	}
+
+	/* called from spin lock vif_lock context */
+	wifi_bt_ts_event = kmalloc(sizeof(COEX_WIFI_BT_TS_T), GFP_ATOMIC);
+	if (wifi_bt_ts_event == NULL) {
+		bes2600_err(BES2600_DBG_EPTA, "ts_event: malloc fail");
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&wifi_bt_ts_event->node);
+	wifi_bt_ts_event->type = type;
+	wifi_bt_ts_event->value = value;
+
+	spin_lock(&hw_priv->coex_event_lock);
+	list_add_tail(&wifi_bt_ts_event->node, &hw_priv->coex_event_list);
+	spin_unlock(&hw_priv->coex_event_lock);
+
+	schedule_work(&hw_priv->coex_work);
+
+	return 0;
+}
+
+void bbt_change_current_status(struct bes2600_common *hw_priv, uint32_t new_status)
+{
+	coex_wifi_bt_ts_request(hw_priv, COEX_TS_TYPE_BT, new_status);
+}
+
+void bwifi_change_current_status(struct bes2600_common *hw_priv, BWIFI_STATUS_T new_status)
+{
+	coex_wifi_bt_ts_request(hw_priv, COEX_TS_TYPE_WIFI, new_status);
+}
+
+static void coex_wifi_bt_ts_cb(struct bes2600_common *hw_priv, COEX_WIFI_BT_TS_T *evt)
+{
+	if (evt->type == COEX_TS_TYPE_BT) {
+		coex_bt_oper_scan(hw_priv, (BWIFI_BT_SCAN_T)BT_REQUEST_SCAN_VALUE(evt->value));
+		coex_bt_oper_audio(hw_priv, (BWIFI_BT_AUDIO_T)BT_REQUEST_AUDIO_VALUE(evt->value));
+		coex_bt_oper_inq(hw_priv, (BWIFI_BT_INQ_T)BT_REQUEST_INQ_VALUE(evt->value));
+		coex_bt_oper_le_scan(hw_priv, (BWIFI_BT_LE_SCAN_T)BT_REQUEST_LE_SCAN_VALUE(evt->value));
+		coex_bt_oper_le_adv(hw_priv, (BWIFI_BT_LE_ADV_T)BT_REQUEST_LE_ADV_VALUE(evt->value));
+		coex_bt_oper_le_status(hw_priv, (BWIFI_BT_LE_STATUS_T)BT_REQUEST_LE_STATUS_VALUE(evt->value));
+		// process BT STATUS in the end
+		coex_bt_oper_status(hw_priv, (BWIFI_BT_STATUS_T)BT_REQUEST_STATUS_VALUE(evt->value));
+		coex_bt_state_notify(hw_priv);
+	} else if (evt->type == COEX_TS_TYPE_WIFI) {
+		switch (evt->value) {
+		case BWIFI_STATUS_IDLE:
+			coex_wifi_idle(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		case BWIFI_STATUS_SCANNING:
+			coex_wifi_scanning(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		case BWIFI_STATUS_SCANNING_COMP:
+			coex_wifi_scan_comp(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		case BWIFI_STATUS_CONNECTING:
+			coex_wifi_connecting(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		case BWIFI_STATUS_CONNECTING_5G:
+			coex_wifi_connecting_5g(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		case BWIFI_STATUS_CONNECTED:
+			coex_wifi_connected(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		case BWIFI_STATUS_CONNECTED_5G:
+			coex_wifi_connected_5g(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		case BWIFI_STATUS_GOT_IP:
+			coex_wifi_got_ip(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		case BWIFI_STATUS_GOT_IP_5G:
+			coex_wifi_got_ip_5g(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		case BWIFI_STATUS_DISCONNECTING:
+			coex_wifi_disconnecting(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		case BWIFI_STATUS_DISCONNECTED:
+			coex_wifi_disconnected(hw_priv, (BWIFI_STATUS_T)evt->value);
+			break;
+		default:
+			bes2600_err(BES2600_DBG_EPTA, "UNKNOWN WIFI type %d", evt->value);
+			break;
+		}
+	} else {
+		bes2600_err(BES2600_DBG_EPTA, "UNKNOWN EPTA type %d, %d", evt->type, evt->value);
+	}
+}
+
+static void coex_wifi_bt_ts_thread(struct work_struct *work)
+{
+	COEX_WIFI_BT_TS_T *coex_event;
+	struct bes2600_common *hw_priv = container_of(work, struct bes2600_common, coex_work);
+
+	spin_lock(&hw_priv->coex_event_lock);
+	while (!list_empty(&hw_priv->coex_event_list)) {
+		coex_event = list_first_entry(&hw_priv->coex_event_list, COEX_WIFI_BT_TS_T, node);
+		spin_unlock(&hw_priv->coex_event_lock);
+		coex_wifi_bt_ts_cb(hw_priv, coex_event);
+		list_del(&coex_event->node);
+		kfree(coex_event);
+
+		spin_lock(&hw_priv->coex_event_lock);
+	}
+	spin_unlock(&hw_priv->coex_event_lock);
+}
+
+void coex_wifi_bt_ts_thread_init(struct bes2600_common *hw_priv)
+{
+	coex_bt_time_init();
+
+	INIT_WORK(&hw_priv->coex_work, coex_wifi_bt_ts_thread);
+	INIT_LIST_HEAD(&hw_priv->coex_event_list);
+	spin_lock_init(&hw_priv->coex_event_lock);
+}
+
+void coex_wifi_bt_ts_thread_deinit(struct bes2600_common *hw_priv)
+{
+	cancel_work_sync(&hw_priv->coex_work);
+}
diff --git a/drivers/staging/bes2600/epta_request.h b/drivers/staging/bes2600/epta_request.h
new file mode 100644
index 000000000000..f0217c2c859d
--- /dev/null
+++ b/drivers/staging/bes2600/epta_request.h
@@ -0,0 +1,52 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef EPTA_REQUEST_H
+#define EPTA_REQUEST_H
+
+#include "linux/list.h"
+#include "bes2600.h"
+
+typedef enum {
+	BWIFI_STATUS_IDLE                 = 0,
+	BWIFI_STATUS_DISCONNECTING        = 1,
+	BWIFI_STATUS_SCANNING             = 2,
+	BWIFI_STATUS_CONNECTING           = 3,
+	BWIFI_STATUS_WPS_CONNECTING       = 4,
+	BWIFI_STATUS_CONNECTED            = 5,
+	BWIFI_STATUS_DHCPING              = 6,
+	BWIFI_STATUS_GOT_IP               = 7,
+	/* Warning: don't change enum value above, it's aligned with fw */
+
+	BWIFI_STATUS_CONNECTING_5G        = 8,
+	BWIFI_STATUS_CONNECTED_5G         = 9,
+	BWIFI_STATUS_DISCONNECTED         = 10,
+	BWIFI_STATUS_GOT_IP_5G            = 11,
+	BWIFI_STATUS_SCANNING_5G          = 12,
+	BWIFI_STATUS_SCANNING_COMP        = 13,
+} BWIFI_STATUS_T;
+
+typedef enum {
+	COEX_TS_TYPE_BT,
+	COEX_TS_TYPE_WIFI,
+} COEX_TS_TYPE_T;
+
+typedef struct {
+	struct list_head node;
+	COEX_TS_TYPE_T type;
+	uint32_t value;
+} COEX_WIFI_BT_TS_T;
+
+void bbt_change_current_status(struct bes2600_common *hw_priv, uint32_t new_status);
+void bwifi_change_current_status(struct bes2600_common *hw_priv, BWIFI_STATUS_T new_status);
+void coex_wifi_bt_ts_thread_init(struct bes2600_common *hw_priv);
+void coex_wifi_bt_ts_thread_deinit(struct bes2600_common *hw_priv);
+void coex_calc_wifi_scan_time(uint32_t *min_chan, uint32_t *max_chan);
+#endif /*EPTA_REQUEST_H*/
\ No newline at end of file
diff --git a/drivers/staging/bes2600/fwio.c b/drivers/staging/bes2600/fwio.c
new file mode 100644
index 000000000000..05763fe73b06
--- /dev/null
+++ b/drivers/staging/bes2600/fwio.c
@@ -0,0 +1,55 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "bes2600.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "sbus.h"
+#include "bh.h"
+
+#ifdef FW_DOWNLOAD_BY_SDIO
+extern int bes2600_load_firmware_sdio(struct sbus_ops *ops, struct sbus_priv *priv);
+#endif
+
+#ifdef FW_DOWNLOAD_BY_USB
+extern int bes2600_load_firmware_usb(struct sbus_ops *ops, struct sbus_priv *priv);
+#endif
+
+#ifdef FW_DOWNLOAD_BY_UART
+extern int bes2600_load_firmware_uart(struct sbus_ops *ops, struct sbus_priv *priv);
+#endif
+
+int bes2600_load_firmware(struct sbus_ops *ops, struct sbus_priv *priv)
+{
+	int ret = 0;
+
+#if defined(FW_DOWNLOAD_BY_SDIO)
+	if ((ret = bes2600_load_firmware_sdio(ops, priv)))
+		return ret;
+#endif
+
+#if defined(FW_DOWNLOAD_BY_USB)
+	if ((ret = bes2600_load_firmware_usb(ops, priv)))
+		return ret;
+#endif
+
+#if defined(FW_DOWNLOAD_BY_UART)
+	if ((ret = bes2600_load_firmware_uart(ops, priv)))
+		return ret;
+#endif
+
+	return ret;
+}
diff --git a/drivers/staging/bes2600/fwio.h b/drivers/staging/bes2600/fwio.h
new file mode 100644
index 000000000000..a4afb7ab17e2
--- /dev/null
+++ b/drivers/staging/bes2600/fwio.h
@@ -0,0 +1,48 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FWIO_H_INCLUDED
+#define FWIO_H_INCLUDED
+
+#define FIRMWARE_1250_CUT11	("wsm_5011.bin")
+#define FIRMWARE_CUT22		("wsm_22.bin")
+#define FIRMWARE_CUT20		("wsm_20.bin")
+#define FIRMWARE_CUT11		("wsm_11.bin")
+#define FIRMWARE_CUT10		("wsm_10.bin")
+#if defined(BES2600_DETECTION_LOGIC)
+#define FIRMWARE_1260_CUT10	("wsm_6010.bin")
+#endif
+#define SDD_FILE_1250_11	("sdd_5011.bin")
+#define SDD_FILE_22		("sdd_22.bin")
+#define SDD_FILE_20		("sdd_20.bin")
+#define SDD_FILE_11		("sdd_11.bin")
+#define SDD_FILE_10		("sdd_10.bin")
+#if defined(BES2600_DETECTION_LOGIC)
+#define SDD_FILE_1260_10	("sdd_6010.bin")
+#endif
+#if defined(BES2600_DETECTION_LOGIC)
+#define BOOTLOADER_FILE_1260 	("bootloader_1260.bin")
+#endif
+
+#define BES2600_HW_REV_CUT10	(10)
+#define BES2600_HW_REV_CUT11	(11)
+#define BES2600_HW_REV_CUT20	(20)
+#define BES2600_HW_REV_CUT22	(22)
+#define CW1250_HW_REV_CUT10	(110)
+#define CW1250_HW_REV_CUT11	(5011)
+#if defined(BES2600_DETECTION_LOGIC)
+#define BES2600_HW_REV_CUT10	(6010)
+#endif
+struct sbus_ops;
+struct sbus_priv;
+
+int bes2600_load_firmware(struct sbus_ops *ops, struct sbus_priv *priv);
+
+#endif
diff --git a/drivers/staging/bes2600/ht.h b/drivers/staging/bes2600/ht.h
new file mode 100644
index 000000000000..b5caa291936a
--- /dev/null
+++ b/drivers/staging/bes2600/ht.h
@@ -0,0 +1,43 @@
+/*
+ *  HT-related code for BES2600 driver
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES2600_HT_H_INCLUDED
+#define BES2600_HT_H_INCLUDED
+
+#include <net/mac80211.h>
+
+struct bes2600_ht_info {
+	struct ieee80211_sta_ht_cap	ht_cap;
+	enum nl80211_channel_type	channel_type;
+	u16				operation_mode;
+};
+
+static inline int bes2600_is_ht(const struct bes2600_ht_info *ht_info)
+{
+	return ht_info->channel_type != NL80211_CHAN_NO_HT;
+}
+
+static inline int bes2600_ht_greenfield(const struct bes2600_ht_info *ht_info)
+{
+	return bes2600_is_ht(ht_info) &&
+		(ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
+		!(ht_info->operation_mode &
+			IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+}
+
+static inline int bes2600_ht_ampdu_density(const struct bes2600_ht_info *ht_info)
+{
+	if (!bes2600_is_ht(ht_info))
+		return 0;
+	return ht_info->ht_cap.ampdu_density;
+}
+
+#endif /* BES2600_HT_H_INCLUDED */
diff --git a/drivers/staging/bes2600/hwio.c b/drivers/staging/bes2600/hwio.c
new file mode 100644
index 000000000000..17ed27adc555
--- /dev/null
+++ b/drivers/staging/bes2600/hwio.c
@@ -0,0 +1,368 @@
+/*
+ * Low-level device IO routines for BES2600 drivers
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+
+#include "bes2600.h"
+#include "hwio.h"
+#include "sbus.h"
+
+ /* Sdio addr is 4*spi_addr */
+#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
+#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
+				((((buf_id)    & 0x1F) << 7) \
+				| (((mpf)        & 1) << 6) \
+				| (((rfu)        & 1) << 5) \
+				| (((reg_id_ofs) & 0x1F) << 0))
+#define MAX_RETRY		3
+
+static struct sbus_ops *bes2600_subs_ops = NULL;
+static struct sbus_priv *bes2600_sbus_priv = NULL;
+
+static int __bes2600_reg_read(u16 addr, void *buf, size_t buf_len, int buf_id)
+{
+	u16 addr_sdio;
+	u32 sdio_reg_addr_17bit ;
+
+	/* Check if buffer is aligned to 4 byte boundary */
+	if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
+		bes2600_err(BES2600_DBG_SBUS,
+			   "%s: buffer is not aligned.\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Convert to SDIO Register Address */
+	addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+	sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+	BUG_ON(!bes2600_subs_ops);
+	return bes2600_subs_ops->sbus_memcpy_fromio(bes2600_sbus_priv,
+					sdio_reg_addr_17bit,
+					buf, buf_len);
+}
+
+static int __bes2600_reg_write(u16 addr, const void *buf, size_t buf_len, int buf_id)
+{
+	u16 addr_sdio;
+	u32 sdio_reg_addr_17bit ;
+
+#if 0
+	/* Check if buffer is aligned to 4 byte boundary */
+	if (WARN_ON(((unsigned long)buf & 3) && (buf_len > 4))) {
+		bes2600_dbg(BES2600_DBG_SBUS, "%s: buffer is not aligned.\n",
+				__func__);
+		return -EINVAL;
+	}
+#endif
+
+	/* Convert to SDIO Register Address */
+	addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+	sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+	BUG_ON(!bes2600_subs_ops);
+	return bes2600_subs_ops->sbus_memcpy_toio(bes2600_sbus_priv,
+					sdio_reg_addr_17bit,
+					buf, buf_len);
+}
+
+static inline int __bes2600_reg_read_32(u16 addr, u32 *val)
+{
+	return __bes2600_reg_read(addr, val, sizeof(val), 0);
+}
+
+static inline int __bes2600_reg_write_32(u16 addr, u32 val)
+{
+	return __bes2600_reg_write(addr, &val, sizeof(val), 0);
+}
+
+void bes2600_reg_set_object(struct sbus_ops *ops, struct sbus_priv *priv)
+{
+	bes2600_subs_ops = ops;
+	bes2600_sbus_priv = priv;
+}
+
+int bes2600_reg_read(u32 addr, void *buf, size_t buf_len)
+{
+	int ret;
+	BUG_ON(!bes2600_subs_ops);
+	bes2600_subs_ops->lock(bes2600_sbus_priv);
+	ret = bes2600_subs_ops->sbus_reg_read(bes2600_sbus_priv, addr, buf, buf_len);
+	bes2600_subs_ops->unlock(bes2600_sbus_priv);
+	return ret;
+}
+
+int bes2600_reg_write(u32 addr, const void *buf, size_t buf_len)
+{
+	int ret;
+	BUG_ON(!bes2600_subs_ops);
+	bes2600_subs_ops->lock(bes2600_sbus_priv);
+	ret = bes2600_subs_ops->sbus_reg_write(bes2600_sbus_priv, addr, buf, buf_len);
+	bes2600_subs_ops->unlock(bes2600_sbus_priv);
+	return ret;
+}
+
+int bes2600_data_read(void *buf, size_t buf_len)
+{
+	int ret, retry = 1;
+	BUG_ON(!bes2600_subs_ops);
+	bes2600_subs_ops->lock(bes2600_sbus_priv);
+#ifndef CONFIG_BES2600_WLAN_BES
+	{
+		int buf_id_rx = hw_priv->buf_id_rx;
+		while (retry <= MAX_RETRY) {
+			ret = __bes2600_reg_read(hw_priv,
+					ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
+					buf_len, buf_id_rx + 1);
+			if (!ret) {
+				buf_id_rx = (buf_id_rx + 1) & 3;
+				hw_priv->buf_id_rx = buf_id_rx;
+				break;
+			} else {
+				retry++;
+				mdelay(1);
+				bes2600_err(BES2600_DBG_SBUS, "%s,error :[%d]\n",
+						__func__, ret);
+			}
+		}
+	}
+#else
+	while (retry <= MAX_RETRY) {
+		ret = bes2600_subs_ops->sbus_memcpy_fromio(bes2600_sbus_priv,
+				BES_TX_DATA_ADDR, buf, buf_len);
+		if (ret) {
+			retry ++;
+			mdelay(1);
+			bes2600_dbg(BES2600_DBG_SBUS, "%s error :[%d]\n",
+					__func__, ret);
+		} else {
+			break;
+		}
+	}
+#endif
+	bes2600_subs_ops->unlock(bes2600_sbus_priv);
+	return ret;
+}
+
+int bes2600_data_write(const void *buf, size_t buf_len)
+{
+	int ret, retry = 1;
+	u32 addr = 0;
+#ifdef CONFIG_BES2600_WLAN_SPI
+	struct HI_MSG_HDR {
+		u16 MsgLen;
+		u16 MsgId;
+	} *pMsg;
+#endif
+
+	BUG_ON(!bes2600_subs_ops);
+	bes2600_subs_ops->lock(bes2600_sbus_priv);
+#ifndef CONFIG_BES2600_WLAN_BES
+	{
+		int buf_id_tx = hw_priv->buf_id_tx;
+		while (retry <= MAX_RETRY) {
+			ret = __bes2600_reg_write(hw_priv,
+					ST90TDS_IN_OUT_QUEUE_REG_ID, buf,
+					buf_len, buf_id_tx);
+			if (!ret) {
+				buf_id_tx = (buf_id_tx + 1) & 31;
+				hw_priv->buf_id_tx = buf_id_tx;
+				break;
+			} else {
+				retry++;
+				mdelay(1);
+				bes2600_err(BES2600_DBG_SBUS, "%s,error :[%d]\n",
+						__func__, ret);
+			}
+		}
+	}
+#else
+#ifdef CONFIG_BES2600_WLAN_SPI
+	#define IS_DRIVER_VENDOR_CMD(X) ((X & 0x0C00) == 0x0C00)
+	pMsg = (struct HI_MSG_HDR *)buf;
+	if (IS_DRIVER_VENDOR_CMD(pMsg->MsgId)) {
+		addr = BES_MISC_DATA_ADDR;
+		pr_err("mcu message detected, %x\n", BES_MISC_DATA_ADDR);
+	}
+#endif
+	while (retry <= MAX_RETRY) {
+		ret = bes2600_subs_ops->sbus_memcpy_toio(bes2600_sbus_priv, addr, buf, buf_len);
+		if (ret) {
+			retry++;
+			mdelay(1);
+			bes2600_dbg(BES2600_DBG_SBUS, "%s,error :[%d]\n",
+					__func__, ret);
+		} else {
+			break;
+		}
+	}
+#endif
+	bes2600_subs_ops->unlock(bes2600_sbus_priv);
+	return ret;
+}
+
+int bes2600_indirect_read(u32 addr, void *buf, size_t buf_len, u32 prefetch, u16 port_addr)
+{
+	u32 val32 = 0;
+	int i, ret;
+
+	if ((buf_len / 2) >= 0x1000) {
+		bes2600_err(BES2600_DBG_SBUS,
+				"%s: Can't read more than 0xfff words.\n",
+				__func__);
+		WARN_ON(1);
+		return -EINVAL;
+		goto out;
+	}
+
+	bes2600_subs_ops->lock(bes2600_sbus_priv);
+	/* Write address */
+	ret = __bes2600_reg_write_32(ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+	if (ret < 0) {
+		bes2600_err(BES2600_DBG_SBUS,
+				"%s: Can't write address register.\n",
+				__func__);
+		goto out;
+	}
+
+	/* Read CONFIG Register Value - We will read 32 bits */
+	ret = __bes2600_reg_read_32(ST90TDS_CONFIG_REG_ID, &val32);
+	if (ret < 0) {
+		bes2600_err(BES2600_DBG_SBUS,
+				"%s: Can't read config register.\n",
+				__func__);
+		goto out;
+	}
+
+	/* Set PREFETCH bit */
+	ret = __bes2600_reg_write_32(ST90TDS_CONFIG_REG_ID, val32 | prefetch);
+	if (ret < 0) {
+		bes2600_err(BES2600_DBG_SBUS,
+				"%s: Can't write prefetch bit.\n",
+				__func__);
+		goto out;
+	}
+
+	/* Check for PRE-FETCH bit to be cleared */
+	for (i = 0; i < 20; i++) {
+		ret = __bes2600_reg_read_32(ST90TDS_CONFIG_REG_ID, &val32);
+		if (ret < 0) {
+			bes2600_err(BES2600_DBG_SBUS,
+					"%s: Can't check prefetch bit.\n",
+					__func__);
+			goto out;
+		}
+		if (!(val32 & prefetch))
+			break;
+
+		mdelay(i);
+	}
+
+	if (val32 & prefetch) {
+		bes2600_err(BES2600_DBG_SBUS,
+				"%s: Prefetch bit is not cleared.\n",
+				__func__);
+		goto out;
+	}
+
+	/* Read data port */
+	ret = __bes2600_reg_read(port_addr, buf, buf_len, 0);
+	if (ret < 0) {
+		bes2600_err(BES2600_DBG_SBUS,
+				"%s: Can't read data port.\n",
+				__func__);
+		goto out;
+	}
+
+out:
+	bes2600_subs_ops->unlock(bes2600_sbus_priv);
+	return ret;
+}
+
+int bes2600_apb_write(u32 addr, const void *buf, size_t buf_len)
+{
+	int ret;
+
+	if ((buf_len / 2) >= 0x1000) {
+		bes2600_err(BES2600_DBG_SBUS,
+				"%s: Can't wrire more than 0xfff words.\n",
+				__func__);
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	bes2600_subs_ops->lock(bes2600_sbus_priv);
+
+	/* Write address */
+	ret = __bes2600_reg_write_32(ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+	if (ret < 0) {
+		bes2600_err(BES2600_DBG_SBUS,
+				"%s: Can't write address register.\n",
+				__func__);
+		goto out;
+	}
+
+	/* Write data port */
+	ret = __bes2600_reg_write(ST90TDS_SRAM_DPORT_REG_ID, buf, buf_len, 0);
+	if (ret < 0) {
+		bes2600_err(BES2600_DBG_SBUS, "%s: Can't write data port.\n",
+				__func__);
+		goto out;
+	}
+
+out:
+	bes2600_subs_ops->unlock(bes2600_sbus_priv);
+	return ret;
+}
+
+#if defined(BES2600_DETECTION_LOGIC)
+int bes2600_ahb_write(u32 addr, const void *buf, size_t buf_len)
+{
+        int ret;
+	bes2600_info(BES2600_DBG_SBUS,"%s: ENTER\n",__func__);
+        if ((buf_len / 2) >= 0x1000) {
+                bes2600_dbg(BES2600_DBG_SBUS,
+                                "%s: Can't wrire more than 0xfff words.\n",
+                                __func__);
+                WARN_ON(1);
+		bes2600_info(BES2600_DBG_SBUS, "%s:EXIT (1) \n",__func__);
+                return -EINVAL;
+        }
+
+        bes2600_subs_ops->lock(bes2600_sbus_priv);
+
+        /* Write address */
+        ret = __bes2600_reg_write_32(priv, ST90TDS_SRAM_BASE_ADDR_REG_ID, addr);
+        if (ret < 0) {
+                bes2600_dbg(BES2600_DBG_SBUS,
+                                "%s: Can't write address register.\n",
+                                __func__);
+                goto out;
+        }
+
+        /* Write data port */
+        ret = __bes2600_reg_write(priv, ST90TDS_AHB_DPORT_REG_ID,
+                                        buf, buf_len, 0);
+        if (ret < 0) {
+                bes2600_dbg(BES2600_DBG_SBUS, "%s: Can't write data port.\n",
+                                __func__);
+                goto out;
+        }
+
+out:
+        bes2600_subs_ops->unlock(priv->bes2600_sbus_priv);
+        return ret;
+}
+#endif
+
+int __bes2600_irq_enable(int enable)
+{
+	return 0;
+}
diff --git a/drivers/staging/bes2600/hwio.h b/drivers/staging/bes2600/hwio.h
new file mode 100644
index 000000000000..aa29ca449ebd
--- /dev/null
+++ b/drivers/staging/bes2600/hwio.h
@@ -0,0 +1,364 @@
+/*
+ * Low-level API for mac80211 BES2600 drivers
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * Based on:
+ * UMAC BES2600 driver which is
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES2600_HWIO_H_INCLUDED
+#define BES2600_HWIO_H_INCLUDED
+
+/* extern */ struct sbus_ops;
+/* extern */ struct sbus_priv;
+
+/* DPLL initial values */
+#define DPLL_INIT_VAL_9000		(0x00000191)
+#define DPLL_INIT_VAL_BES2600		(0x0EC4F121)
+
+/* Hardware Type Definitions */
+#define HIF_8601_VERSATILE		(0)
+#define HIF_8601_SILICON		(1)
+#define HIF_9000_SILICON_VERSTAILE	(2)
+
+#define BES2600_CUT_11_ID_STR		(0x302E3830)
+#define BES2600_CUT_22_ID_STR1		(0x302e3132)
+#define BES2600_CUT_22_ID_STR2		(0x32302e30)
+#define BES2600_CUT_22_ID_STR3		(0x3335)
+#define CW1250_CUT_11_ID_STR1		(0x302e3033)
+#define CW1250_CUT_11_ID_STR2		(0x33302e32)
+#define CW1250_CUT_11_ID_STR3		(0x3535)
+#define BES2600_CUT_ID_ADDR		(0xFFF17F90)
+#define BES2600_CUT2_ID_ADDR		(0xFFF1FF90)
+
+/* Download control area */
+/* boot loader start address in SRAM */
+#define DOWNLOAD_BOOT_LOADER_OFFSET	(0x00000000)
+/* 32K, 0x4000 to 0xDFFF */
+#define DOWNLOAD_FIFO_OFFSET		(0x00004000)
+/* 32K */
+#define DOWNLOAD_FIFO_SIZE		(0x00008000)
+/* 128 bytes, 0xFF80 to 0xFFFF */
+#define DOWNLOAD_CTRL_OFFSET		(0x0000FF80)
+#define DOWNLOAD_CTRL_DATA_DWORDS	(32-6)
+
+struct download_cntl_t {
+	/* size of whole firmware file (including Cheksum), host init */
+	u32 ImageSize;
+	/* downloading flags */
+	u32 Flags;
+	/* No. of bytes put into the download, init & updated by host */
+	u32 Put;
+	/* last traced program counter, last ARM reg_pc */
+	u32 TracePc;
+	/* No. of bytes read from the download, host init, device updates */
+	u32 Get;
+	/* r0, boot losader status, host init to pending, device updates */
+	u32 Status;
+	/* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
+	u32 DebugData[DOWNLOAD_CTRL_DATA_DWORDS];
+};
+
+#define	DOWNLOAD_IMAGE_SIZE_REG		\
+	(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, ImageSize))
+#define	DOWNLOAD_FLAGS_REG		\
+	(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Flags))
+#define DOWNLOAD_PUT_REG		\
+	(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Put))
+#define DOWNLOAD_TRACE_PC_REG		\
+	(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, TracePc))
+#define	DOWNLOAD_GET_REG		\
+	(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Get))
+#define	DOWNLOAD_STATUS_REG		\
+	(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Status))
+#define DOWNLOAD_DEBUG_DATA_REG		\
+	(DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, DebugData))
+#define DOWNLOAD_DEBUG_DATA_LEN		(108)
+
+#define DOWNLOAD_BLOCK_SIZE		(1024)
+
+/* For boot loader detection */
+#define DOWNLOAD_ARE_YOU_HERE		(0x87654321)
+#define DOWNLOAD_I_AM_HERE		(0x12345678)
+
+/* Download error code */
+#define DOWNLOAD_PENDING		(0xFFFFFFFF)
+#define DOWNLOAD_SUCCESS		(0)
+#define DOWNLOAD_EXCEPTION		(1)
+#define DOWNLOAD_ERR_MEM_1		(2)
+#define DOWNLOAD_ERR_MEM_2		(3)
+#define DOWNLOAD_ERR_SOFTWARE		(4)
+#define DOWNLOAD_ERR_FILE_SIZE		(5)
+#define DOWNLOAD_ERR_CHECKSUM		(6)
+#define DOWNLOAD_ERR_OVERFLOW		(7)
+#define DOWNLOAD_ERR_IMAGE		(8)
+#define DOWNLOAD_ERR_HOST		(9)
+#define DOWNLOAD_ERR_ABORT		(10)
+
+
+#define SYS_BASE_ADDR_SILICON		(0)
+#define PAC_BASE_ADDRESS_SILICON	(SYS_BASE_ADDR_SILICON + 0x09000000)
+#define PAC_SHARED_MEMORY_SILICON	(PAC_BASE_ADDRESS_SILICON)
+
+#define BES26000_APB(addr)		(PAC_SHARED_MEMORY_SILICON + (addr))
+
+/* ***************************************************************
+*Device register definitions
+*************************************************************** */
+/* WBF - SPI Register Addresses */
+#define ST90TDS_ADDR_ID_BASE		(0x0000)
+/* 16/32 bits */
+#define ST90TDS_CONFIG_REG_ID		(0x0000)
+/* 16/32 bits */
+#define ST90TDS_CONTROL_REG_ID		(0x0001)
+/* 16 bits, Q mode W/R */
+#define ST90TDS_IN_OUT_QUEUE_REG_ID	(0x0002)
+/* 32 bits, AHB bus R/W */
+#define ST90TDS_AHB_DPORT_REG_ID	(0x0003)
+/* 16/32 bits */
+#define ST90TDS_SRAM_BASE_ADDR_REG_ID   (0x0004)
+/* 32 bits, APB bus R/W */
+#define ST90TDS_SRAM_DPORT_REG_ID	(0x0005)
+/* 32 bits, t_settle/general */
+#define ST90TDS_TSET_GEN_R_W_REG_ID	(0x0006)
+/* 16 bits, Q mode read, no length */
+#define ST90TDS_FRAME_OUT_REG_ID	(0x0007)
+#define ST90TDS_ADDR_ID_MAX		(ST90TDS_FRAME_OUT_REG_ID)
+
+/* WBF - Control register bit set */
+/* next o/p length, bit 11 to 0 */
+#define ST90TDS_CONT_NEXT_LEN_MASK	(0x0FFF)
+#define ST90TDS_CONT_WUP_BIT		(BIT(12))
+#define ST90TDS_CONT_RDY_BIT		(BIT(13))
+#define ST90TDS_CONT_IRQ_ENABLE		(BIT(14))
+#define ST90TDS_CONT_RDY_ENABLE		(BIT(15))
+#define ST90TDS_CONT_IRQ_RDY_ENABLE	(BIT(14)|BIT(15))
+
+/* SPI Config register bit set */
+#define ST90TDS_CONFIG_FRAME_BIT	(BIT(2))
+#define ST90TDS_CONFIG_WORD_MODE_BITS	(BIT(3)|BIT(4))
+#define ST90TDS_CONFIG_WORD_MODE_1	(BIT(3))
+#define ST90TDS_CONFIG_WORD_MODE_2	(BIT(4))
+#define ST90TDS_CONFIG_ERROR_0_BIT	(BIT(5))
+#define ST90TDS_CONFIG_ERROR_1_BIT	(BIT(6))
+#define ST90TDS_CONFIG_ERROR_2_BIT	(BIT(7))
+/* TBD: Sure??? */
+#define ST90TDS_CONFIG_CSN_FRAME_BIT	(BIT(7))
+#define ST90TDS_CONFIG_ERROR_3_BIT	(BIT(8))
+#define ST90TDS_CONFIG_ERROR_4_BIT	(BIT(9))
+/* QueueM */
+#define ST90TDS_CONFIG_ACCESS_MODE_BIT	(BIT(10))
+/* AHB bus */
+#define ST90TDS_CONFIG_AHB_PFETCH_BIT	(BIT(11))
+#define ST90TDS_CONFIG_CPU_CLK_DIS_BIT	(BIT(12))
+/* APB bus */
+#define ST90TDS_CONFIG_PFETCH_BIT	(BIT(13))
+/* cpu reset */
+#define ST90TDS_CONFIG_CPU_RESET_BIT	(BIT(14))
+#define ST90TDS_CONFIG_CLEAR_INT_BIT	(BIT(15))
+
+/* For BES2600 the IRQ Enable and Ready Bits are in CONFIG register */
+#define ST90TDS_CONF_IRQ_RDY_ENABLE	(BIT(16)|BIT(17))
+
+void bes2600_reg_set_object(struct sbus_ops *ops, struct sbus_priv *priv);
+int bes2600_data_read(void *buf, size_t buf_len);
+int bes2600_data_write(const void *buf, size_t buf_len);
+
+int bes2600_reg_read(u32 addr, void *buf, size_t buf_len);
+int bes2600_reg_write(u32 addr, const void *buf, size_t buf_len);
+
+static inline int bes2600_reg_read_16(u16 addr, u16 *val)
+{
+	return bes2600_reg_read(addr, val, sizeof(*val));
+}
+
+static inline int bes2600_reg_write_16(u16 addr, u16 val)
+{
+	return bes2600_reg_write(addr, &val, sizeof(val));
+}
+
+static inline int bes2600_reg_read_32(u16 addr, u32 *val)
+{
+	return bes2600_reg_read(addr, val, sizeof(val));
+}
+
+static inline int bes2600_reg_write_32(u16 addr, u32 val)
+{
+	return bes2600_reg_write(addr, &val, sizeof(val));
+}
+
+int bes2600_indirect_read(u32 addr, void *buf, size_t buf_len, u32 prefetch, u16 port_addr);
+int bes2600_apb_write(u32 addr, const void *buf, size_t buf_len);
+int bes2600_ahb_write(u32 addr, const void *buf, size_t buf_len);
+
+static inline int bes2600_apb_read(u32 addr, void *buf, size_t buf_len)
+{
+	return bes2600_indirect_read(addr, buf, buf_len,
+		ST90TDS_CONFIG_PFETCH_BIT, ST90TDS_SRAM_DPORT_REG_ID);
+}
+
+static inline int bes2600_ahb_read(u32 addr, void *buf, size_t buf_len)
+{
+	return bes2600_indirect_read(addr, buf, buf_len,
+		ST90TDS_CONFIG_AHB_PFETCH_BIT, ST90TDS_AHB_DPORT_REG_ID);
+}
+
+static inline int bes2600_apb_read_32(u32 addr, u32 *val)
+{
+	return bes2600_apb_read(addr, val, sizeof(val));
+}
+
+static inline int bes2600_apb_write_32(u32 addr, u32 val)
+{
+	return bes2600_apb_write(addr, &val, sizeof(val));
+}
+
+static inline int bes2600_ahb_read_32(u32 addr, u32 *val)
+{
+	return bes2600_ahb_read(addr, val, sizeof(val));
+}
+
+#if defined(BES2600_DETECTION_LOGIC)
+static inline int bes2600_ahb_write_32(u32 addr, u32 val)
+{
+	return bes2600_ahb_write(addr, &val, sizeof(val));
+}
+#endif /*BES2600_DETECTION_LOGIC*/
+
+#ifdef CONFIG_BES2600_WLAN_USB
+#define BES_TX_DATA_ADDR	(0x0)
+#endif
+
+#ifdef CONFIG_BES2600_WLAN_SDIO
+#define SDIO_DEVICE_SEND_INT_LEN_SEPARATE
+
+#define BES_TX_CTRL_REG_ID	(0x0)
+
+#ifdef SDIO_DEVICE_SEND_INT_LEN_SEPARATE
+#define BES_TX_NEXT_LEN_REG_ID	(0x104)
+#else
+#define BES_TX_NEXT_LEN_REG_ID	BES_TX_CTRL_REG_ID
+#endif
+
+#define BES_TX_NEXT_LEN_MASK	(0xffff)
+#define BES_TX_DATA_ADDR	(0x0)
+
+#define BES_HOST_INT_REG_ID		(0x120)
+#define BES_HOST_INT			(1 << 0)
+#define BES_AP_WAKEUP_CFG		(1 << 1)
+#define BES_SUBSYSTEM_MCU_DEACTIVE	(1 << 2)
+#define BES_SUBSYSTEM_MCU_ACTIVE	(1 << 3)
+#define BES_SUBSYSTEM_WIFI_DEACTIVE	(1 << 4)
+#define BES_SUBSYSTEM_WIFI_ACTIVE	(1 << 5)
+#define BES_SUBSYSTEM_WIFI_DEBUG	(1 << 6)
+#define BES_SUBSYSTEM_BT_DEACTIVE	(1 << 7)
+#define BES_SUBSYSTEM_BT_ACTIVE		(1 << 8)
+#define BES_SUBSYSTEM_SYSTEM_CLOSE	(1 << 9)
+#define BES_SUBSYSTEM_BT_WAKEUP		(1 << 10)
+#define BES_SUBSYSTEM_BT_SLEEP		(1 << 11)
+
+#define BES_AP_WAKEUP_TYPE_MASK		0xC
+#define BES_AP_WAKEUP_TYPE_SHIFT	2
+#define BES_AP_WAKEUP_TYPE_GPIO		0
+#define BES_AP_WAKEUP_TYPE_IF		1
+
+#define BES_AP_WAKEUP_REG_ID		(0x124)
+#define BES_AP_WAKEUP_CFG_VALID		(0x80)
+
+#define BES_AP_WAKEUP_GPIO_MASK		(0x3)
+#define BES_AP_WAKEUP_GPIO_HIGH		(0x0)
+#define BES_AP_WAKEUP_GPIO_LOW		(0x1)
+#define BES_AP_WAKEUP_GPIO_RISE		(0x2)
+#define BES_AP_WAKEUP_GPIO_FALL		(0x3)
+
+#define BES_SLAVE_STATUS_REG_ID			(0x10c)
+#define BES_SLAVE_STATUS_MCU_READY		(1 << 0)
+#define BES_SLAVE_STATUS_DPD_READY		(1 << 1)
+#define BES_SLAVE_STATUS_WIFI_READY		(1 << 2)
+#define BES_SLAVE_STATUS_BT_READY		(1 << 3)
+#define BES_SLAVE_STATUS_MCU_WAKEUP_READY	(1 << 4)
+#define BES_SLAVE_STATUS_BT_WAKE_READY		(1 << 5)
+#define BES_SLAVE_STATUS_DPD_LOG_READY		(1 << 6)
+
+#define PACKET_TOTAL_LEN(len) 		((len) & 0xffff)
+#define PACKET_COUNT(len) 		(((len) >> 16) & 0xff)
+#define PAKCET_CRC8(len) 		(((len) >> 24) & 0xff)
+
+#define BES_SDIO_RX_MULTIPLE_NUM (16)
+#define BES_SDIO_TX_MULTIPLE_NUM (16)
+#define BES_SDIO_TX_MULTIPLE_NUM_NOSIGNAL (1)
+
+#define MAX_SDIO_TRANSFER_LEN (32768)
+#endif
+
+#ifdef CONFIG_BES2600_WLAN_SPI
+
+#define SPI_RD_CFG_REG_ID	(0xfffc0004)
+#define SPI_NCONTINUOUS_CFG_VAL	(0x0)
+#define SPI_CONTINUOUS_CFG_VAL	(0x1)
+#define SPI_RD_ADDR(X)	((X >> 2) | (1 << 30))
+#define SPI_WR_ADDR(X)	((X >> 2) | (0 << 30))
+
+#define SPI_MASTER_SECTION_BASE (0x20083000)
+
+#define BES_HOST_SYNC_REG_ID    (SPI_MASTER_SECTION_BASE)
+#define BES_SLAVE_SYNC_HEADER   (0xbe572002)
+
+#define BES_HOST_SUBINT_REG_ID  (SPI_MASTER_SECTION_BASE + 0x4)
+#define BES_SLAVE_STATUS_REG_ID (SPI_MASTER_SECTION_BASE + 0X8)
+
+#define BES_LMAC_BUF_NUMS	    (64)
+#define BES_LMAC_BUF_TOTAL      (SPI_MASTER_SECTION_BASE + 0xC)
+
+#define BES_TX_CTRL_REG_ID      (SPI_MASTER_SECTION_BASE + 0x10)
+#define BES_TX_NEXT_LEN_MASK    (0xffffffff)
+
+#define BES_LMAC_BUF_DESC       (SPI_MASTER_SECTION_BASE + 0x14)
+
+#define BES_TX_DATA_ADDR        (SPI_MASTER_SECTION_BASE + 0x114)
+
+#define MAX_SEND_PACKETS_NUM    (8)
+#define BES_MISC_DATA_ADDR      (BES_TX_DATA_ADDR + (1632 + 4 + 4) * MAX_SEND_PACKETS_NUM)
+
+#define BES_CALI_DATA_ADDR      (0x2008c000)
+#define BES_FACTORY_DATA_ADDR   (0x2008b000)
+
+
+#define BES_HOST_INT_REG_ID                     (0x40000098)
+#define BES_HOST_INT_RD_DONE                    (1 << 0)
+#define BES_HOST_INT_WR_DONE                    (1 << 1)
+#define BES_HOST_INT_SUBINT                     (1 << 2)
+
+#define BES_AP_WAKEUP_CFG                       (1 << 1)
+#define BES_SUBSYSTEM_MCU_DEACTIVE              (1 << 2)
+#define BES_SUBSYSTEM_MCU_ACTIVE                (1 << 3)
+#define BES_SUBSYSTEM_WIFI_DEACTIVE             (1 << 4)
+#define BES_SUBSYSTEM_WIFI_ACTIVE               (1 << 5)
+#define BES_SUBSYSTEM_WIFI_DEBUG                (1 << 6)
+#define BES_SUBSYSTEM_BT_DEACTIVE               (1 << 7)
+#define BES_SUBSYSTEM_BT_ACTIVE                 (1 << 8)
+#define BES_SUBSYSTEM_SYSTEM_CLOSE              (1 << 9)
+#define BES_DLD_FACTORY_DATA_DONE               (1 << 12)
+#define BES_DLD_DPD_DATA_DONE                   (1 << 13)
+#define BES_MISC_DATA_DONE                      (1 << 14)
+
+#define BES_SLAVE_STATUS_MCU_READY              (1 << 0)
+#define BES_SLAVE_STATUS_WIFI_CALI_READY        (1 << 1)
+#define BES_SLAVE_STATUS_WIFI_OPEN_READY        (1 << 2)
+#define BES_SLAVE_STATUS_BT_OPEN_READY          (1 << 3)
+#define BES_SLAVE_STATUS_MCU_WAKEUP_READY       (1 << 4)
+
+#define PACKET_TOTAL_LEN_V2(len)                ((len) & 0xfffffff)
+#define PACKET_COUNT_V2(len)                    (((len) >> 28) & 0xf)
+
+#endif
+
+#endif /* BES2600_HWIO_H_INCLUDED */
diff --git a/drivers/staging/bes2600/itp.c b/drivers/staging/bes2600/itp.c
new file mode 100644
index 000000000000..e5c2958b5b96
--- /dev/null
+++ b/drivers/staging/bes2600/itp.c
@@ -0,0 +1,744 @@
+/*
+ * mac80211 glue code for mac80211 BES2600 drivers
+ * ITP code
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/poll.h>
+#include <linux/time.h>
+#include <linux/kallsyms.h>
+#include <net/mac80211.h>
+#include "bes2600.h"
+#include "debug.h"
+#include "itp.h"
+#include "sta.h"
+
+static int __bes2600_itp_open(struct bes2600_common *priv);
+static int __bes2600_itp_close(struct bes2600_common *priv);
+static void bes2600_itp_rx_start(struct bes2600_common *priv);
+static void bes2600_itp_rx_stop(struct bes2600_common *priv);
+static void bes2600_itp_rx_stats(struct bes2600_common *priv);
+static void bes2600_itp_rx_reset(struct bes2600_common *priv);
+static void bes2600_itp_tx_stop(struct bes2600_common *priv);
+static void bes2600_itp_handle(struct bes2600_common *priv,
+			      struct sk_buff *skb);
+static void bes2600_itp_err(struct bes2600_common *priv,
+			   int err,
+			   int arg);
+static void __bes2600_itp_tx_stop(struct bes2600_common *priv);
+
+static ssize_t bes2600_itp_read(struct file *file,
+	char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct bes2600_common *priv = file->private_data;
+	struct bes2600_itp *itp = &priv->debug->itp;
+	struct sk_buff *skb;
+	int ret;
+
+	if (skb_queue_empty(&itp->log_queue))
+		return 0;
+
+	skb = skb_dequeue(&itp->log_queue);
+	ret = copy_to_user(user_buf, skb->data, skb->len);
+	*ppos += skb->len;
+	skb->data[skb->len] = 0;
+	bes2600_dbg(BES2600_DBG_ITP, "[ITP] >>> %s", skb->data);
+	consume_skb(skb);
+
+	return skb->len - ret;
+}
+
+static ssize_t bes2600_itp_write(struct file *file,
+	const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	struct bes2600_common *priv = file->private_data;
+	struct sk_buff *skb;
+
+	if (!count || count > 1024)
+		return -EINVAL;
+	skb = dev_alloc_skb(count + 1);
+	if (!skb)
+		return -ENOMEM;
+	skb_trim(skb, 0);
+	skb_put(skb, count + 1);
+	if (copy_from_user(skb->data, user_buf, count)) {
+		kfree_skb(skb);
+		return -EFAULT;
+	}
+	skb->data[count] = 0;
+
+	bes2600_itp_handle(priv, skb);
+	consume_skb(skb);
+	return count;
+}
+
+static unsigned int bes2600_itp_poll(struct file *file, poll_table *wait)
+{
+	struct bes2600_common *priv = file->private_data;
+	struct bes2600_itp *itp = &priv->debug->itp;
+	unsigned int mask = 0;
+
+	poll_wait(file, &itp->read_wait, wait);
+
+	if (!skb_queue_empty(&itp->log_queue))
+		mask |= POLLIN | POLLRDNORM;
+
+	mask |= POLLOUT | POLLWRNORM;
+
+	return mask;
+}
+
+static int bes2600_itp_open(struct inode *inode, struct file *file)
+{
+	struct bes2600_common *priv = inode->i_private;
+	struct bes2600_itp *itp = &priv->debug->itp;
+	int ret = 0;
+
+	file->private_data = priv;
+	if (atomic_inc_return(&itp->open_count) == 1) {
+		ret = __bes2600_itp_open(priv);
+		if (ret && !atomic_dec_return(&itp->open_count))
+			__bes2600_itp_close(priv);
+	} else {
+		atomic_dec(&itp->open_count);
+		ret = -EBUSY;
+	}
+
+	return ret;
+}
+
+static int bes2600_itp_close(struct inode *inode, struct file *file)
+{
+	struct bes2600_common *priv = file->private_data;
+	struct bes2600_itp *itp = &priv->debug->itp;
+	if (!atomic_dec_return(&itp->open_count)) {
+		__bes2600_itp_close(priv);
+		wake_up(&itp->close_wait);
+	}
+	return 0;
+}
+
+static const struct file_operations fops_itp = {
+	.open = bes2600_itp_open,
+	.read = bes2600_itp_read,
+	.write = bes2600_itp_write,
+	.poll = bes2600_itp_poll,
+	.release = bes2600_itp_close,
+	.llseek = default_llseek,
+	.owner = THIS_MODULE,
+};
+
+static void bes2600_itp_fill_pattern(u8 *data, int size,
+		enum bes2600_itp_data_modes mode)
+{
+	u8 *p = data;
+
+	if (size <= 0)
+		return;
+
+	switch (mode) {
+	default:
+	case ITP_DATA_ZEROS:
+		memset(data, 0x0, size);
+		break;
+	case ITP_DATA_ONES:
+		memset(data, 0xff, size);
+		break;
+	case ITP_DATA_ZERONES:
+		memset(data, 0x55, size);
+		break;
+	case ITP_DATA_RANDOM:
+		while (p < data+size-sizeof(u32)) {
+			(*(u32 *)p) = random32();
+			p += sizeof(u32);
+		}
+		while (p < data+size) {
+			(*p) = random32() & 0xFF;
+			p++;
+		}
+		break;
+	}
+	return;
+}
+
+static void bes2600_itp_tx_work(struct work_struct *work)
+{
+	struct bes2600_itp *itp = container_of(work, struct bes2600_itp,
+		    tx_work.work);
+	struct bes2600_common *priv = itp->priv;
+	atomic_set(&priv->bh_tx, 1);
+	wake_up(&priv->bh_wq);
+}
+
+static void bes2600_itp_tx_finish(struct work_struct *work)
+{
+	struct bes2600_itp *itp = container_of(work, struct bes2600_itp,
+		    tx_finish.work);
+	__bes2600_itp_tx_stop(itp->priv);
+}
+
+int bes2600_itp_init(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+
+	itp->priv = priv;
+	atomic_set(&itp->open_count, 0);
+	atomic_set(&itp->stop_tx, 0);
+	atomic_set(&itp->awaiting_confirm, 0);
+	skb_queue_head_init(&itp->log_queue);
+	spin_lock_init(&itp->tx_lock);
+	init_waitqueue_head(&itp->read_wait);
+	init_waitqueue_head(&itp->write_wait);
+	init_waitqueue_head(&itp->close_wait);
+	INIT_DELAYED_WORK(&itp->tx_work, bes2600_itp_tx_work);
+	INIT_DELAYED_WORK(&itp->tx_finish, bes2600_itp_tx_finish);
+	itp->data = NULL;
+	itp->hdr_len = WSM_TX_EXTRA_HEADROOM +
+			sizeof(struct ieee80211_hdr_3addr);
+	itp->id = 0;
+
+	if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR,
+			priv->debug->debugfs_phy, priv, &fops_itp))
+		return -ENOMEM;
+
+	return 0;
+}
+
+void bes2600_itp_release(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+
+	wait_event_interruptible(itp->close_wait,
+			!atomic_read(&itp->open_count));
+
+	WARN_ON(atomic_read(&itp->open_count));
+
+	skb_queue_purge(&itp->log_queue);
+	bes2600_itp_tx_stop(priv);
+}
+
+static int __bes2600_itp_open(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+
+	if (!priv->vif)
+		return -EINVAL;
+	if (priv->join_status)
+		return -EINVAL;
+	itp->saved_channel = priv->channel;
+	if (!priv->channel)
+		priv->channel = &priv->hw->
+			wiphy->bands[NL80211_BAND_2GHZ]->channels[0];
+	wsm_set_bssid_filtering(priv, false);
+	bes2600_itp_rx_reset(priv);
+	return 0;
+}
+
+static int __bes2600_itp_close(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST)
+		bes2600_itp_rx_stop(priv);
+	bes2600_itp_tx_stop(priv);
+	bes2600_disable_listening(priv);
+	bes2600_update_filtering(priv);
+	priv->channel = itp->saved_channel;
+	return 0;
+}
+
+bool bes2600_is_itp(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	return atomic_read(&itp->open_count) != 0;
+}
+
+static void bes2600_itp_rx_reset(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	itp->rx_cnt = 0;
+	itp->rx_rssi = 0;
+	itp->rx_rssi_max = -1000;
+	itp->rx_rssi_min = 1000;
+}
+
+static void bes2600_itp_rx_start(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+
+	bes2600_dbg(BES2600_DBG_ITP, "[ITP] RX start, band = %d, ch = %d\n",
+			itp->band, itp->ch);
+	atomic_set(&itp->test_mode, TEST_MODE_RX_TEST);
+	bes2600_disable_listening(priv, false);
+	priv->channel = &priv->hw->
+		wiphy->bands[itp->band]->channels[itp->ch];
+	bes2600_enable_listening(priv, priv->channel);
+	wsm_set_bssid_filtering(priv, false);
+}
+
+static void bes2600_itp_rx_stop(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	bes2600_dbg(BES2600_DBG_ITP, "[ITP] RX stop\n");
+	atomic_set(&itp->test_mode, TEST_MODE_NO_TEST);
+	bes2600_itp_rx_reset(priv);
+}
+
+static void bes2600_itp_rx_stats(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	struct sk_buff *skb;
+	char buf[128];
+	int len, ret;
+	struct wsm_counters_table counters;
+
+	ret = wsm_get_counters_table(priv, &counters);
+
+	if (ret)
+		bes2600_itp_err(priv, -EBUSY, 20);
+
+	if (!itp->rx_cnt)
+		len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n",
+				counters.countRxPacketErrors);
+	else
+		len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n",
+			itp->rx_cnt,
+			itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0,
+			itp->rx_rssi_min, itp->rx_rssi_max,
+			counters.countRxPacketErrors);
+
+	if (len <= 0) {
+		bes2600_itp_err(priv, -EBUSY, 21);
+		return;
+	}
+
+	skb = dev_alloc_skb(len);
+	if (!skb) {
+		bes2600_itp_err(priv, -ENOMEM, 22);
+		return;
+	}
+
+	itp->rx_cnt = 0;
+	itp->rx_rssi = 0;
+	itp->rx_rssi_max = -1000;
+	itp->rx_rssi_min = 1000;
+
+	skb_trim(skb, 0);
+	skb_put(skb, len);
+
+	memcpy(skb->data, buf, len);
+	skb_queue_tail(&itp->log_queue, skb);
+	wake_up(&itp->read_wait);
+}
+
+static void bes2600_itp_tx_start(struct bes2600_common *priv)
+{
+	struct wsm_tx *tx;
+	struct ieee80211_hdr_3addr *hdr;
+	struct bes2600_itp *itp = &priv->debug->itp;
+	struct wsm_association_mode assoc_mode = {
+		.flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE,
+		.preambleType = itp->preamble,
+	};
+	int len;
+	u8 da_addr[6] = ITP_DEFAULT_DA_ADDR;
+
+	/* Rates index 4 and 5 are not supported */
+	if (itp->rate > 3)
+		itp->rate += 2;
+
+	bes2600_dbg(BES2600_DBG_ITP, "[ITP] TX start: band = %d, ch = %d, rate = %d,"
+			" preamble = %d, number = %d, data_mode = %d,"
+			" interval = %d, power = %d, data_len = %d\n",
+			itp->band, itp->ch, itp->rate, itp->preamble,
+			itp->number, itp->data_mode, itp->interval_us,
+			itp->power, itp->data_len);
+
+	len = itp->hdr_len + itp->data_len;
+
+	itp->data = kmalloc(len, GFP_KERNEL);
+	tx = (struct wsm_tx *)itp->data;
+	tx->hdr.len = itp->data_len + itp->hdr_len;
+	tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6);
+	tx->maxTxRate = itp->rate;
+	tx->queueId = 3;
+	tx->more = 0;
+	tx->flags = 0xc;
+	tx->packetID = 0;
+	tx->reserved = 0;
+	tx->expireTime = 0;
+
+	if (itp->preamble == ITP_PREAMBLE_GREENFIELD)
+		tx->htTxParameters = WSM_HT_TX_GREENFIELD;
+	else if (itp->preamble == ITP_PREAMBLE_MIXED)
+		tx->htTxParameters = WSM_HT_TX_MIXED;
+
+	hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)];
+	memset(hdr, 0, sizeof(*hdr));
+	hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+			IEEE80211_FCTL_TODS);
+	memcpy(hdr->addr1, da_addr, ETH_ALEN);
+	memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN);
+	memcpy(hdr->addr3, da_addr, ETH_ALEN);
+
+	bes2600_itp_fill_pattern(&itp->data[itp->hdr_len],
+			itp->data_len, itp->data_mode);
+
+	bes2600_disable_listening(priv);
+	priv->channel = &priv->hw->
+		wiphy->bands[itp->band]->channels[itp->ch];
+	WARN_ON(wsm_set_output_power(priv, itp->power));
+	if (itp->preamble == ITP_PREAMBLE_SHORT ||
+			itp->preamble == ITP_PREAMBLE_LONG)
+		WARN_ON(wsm_set_association_mode(priv,
+					&assoc_mode));
+	wsm_set_bssid_filtering(priv, false);
+	bes2600_enable_listening(priv, priv->channel);
+
+	spin_lock_bh(&itp->tx_lock);
+	atomic_set(&itp->test_mode, TEST_MODE_TX_TEST);
+	atomic_set(&itp->awaiting_confirm, 0);
+	atomic_set(&itp->stop_tx, 0);
+	atomic_set(&priv->bh_tx, 1);
+	ktime_get_ts(&itp->last_sent);
+	wake_up(&priv->bh_wq);
+	spin_unlock_bh(&itp->tx_lock);
+}
+
+void __bes2600_itp_tx_stop(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	spin_lock_bh(&itp->tx_lock);
+	kfree(itp->data);
+	itp->data = NULL;
+	atomic_set(&itp->test_mode, TEST_MODE_NO_TEST);
+	spin_unlock_bh(&itp->tx_lock);
+}
+
+static void bes2600_itp_tx_stop(struct bes2600_common *priv)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	bes2600_dbg(BES2600_DBG_ITP, "[ITP] TX stop\n");
+	atomic_set(&itp->stop_tx, 1);
+	flush_workqueue(priv->workqueue);
+
+	/* time for FW to confirm all tx requests */
+	msleep(500);
+
+	__bes2600_itp_tx_stop(priv);
+}
+
+static void bes2600_itp_get_version(struct bes2600_common *priv,
+		enum bes2600_itp_version_type type)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	struct sk_buff *skb;
+	char buf[ITP_BUF_SIZE];
+	size_t size = 0;
+	int len;
+	bes2600_dbg(BES2600_DBG_ITP, "[ITP] print %s version\n", type == ITP_CHIP_ID ?
+			"chip" : "firmware");
+
+	len = snprintf(buf, ITP_BUF_SIZE, "2,");
+	if (len <= 0) {
+		bes2600_itp_err(priv, -EINVAL, 40);
+		return;
+	}
+	size += len;
+
+	switch (type) {
+	case ITP_CHIP_ID:
+		len = bes2600_print_fw_version(priv, buf+size,
+				ITP_BUF_SIZE - size);
+
+		if (len <= 0) {
+			bes2600_itp_err(priv, -EINVAL, 41);
+			return;
+		}
+		size += len;
+		break;
+	case ITP_FW_VER:
+		len = snprintf(buf+size, ITP_BUF_SIZE - size,
+				"%d.%d", priv->wsm_caps.hardwareId,
+				priv->wsm_caps.hardwareSubId);
+		if (len <= 0) {
+			bes2600_itp_err(priv, -EINVAL, 42);
+			return;
+		}
+		size += len;
+		break;
+	default:
+		bes2600_itp_err(priv, -EINVAL, 43);
+		break;
+	}
+
+	len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n");
+	if (len <= 0) {
+		bes2600_itp_err(priv, -EINVAL, 44);
+		return;
+	}
+	size += len;
+
+	skb = dev_alloc_skb(size);
+	if (!skb) {
+		bes2600_itp_err(priv, -ENOMEM, 45);
+		return;
+	}
+
+	skb_trim(skb, 0);
+	skb_put(skb, size);
+
+	memcpy(skb->data, buf, size);
+	skb_queue_tail(&itp->log_queue, skb);
+	wake_up(&itp->read_wait);
+}
+
+int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data,
+		size_t *tx_len, int *burst)
+{
+	struct bes2600_itp *itp;
+	struct wsm_tx *tx;
+	struct timespec now;
+	int time_left_us;
+
+	if (!priv->debug)
+		return 0;
+
+	itp	= &priv->debug->itp;
+
+	if (!itp)
+		return 0;
+
+	spin_lock_bh(&itp->tx_lock);
+	if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST)
+		goto out;
+
+	if (atomic_read(&itp->stop_tx))
+		goto out;
+
+	if (itp->number == 0) {
+		atomic_set(&itp->stop_tx, 1);
+		queue_delayed_work(priv->workqueue, &itp->tx_finish,
+				HZ/10);
+		goto out;
+	}
+
+	if (!itp->data)
+		goto out;
+
+	if (priv->hw_bufs_used >= 2) {
+		if (!atomic_read(&priv->bh_rx))
+				atomic_set(&priv->bh_rx, 1);
+		atomic_set(&priv->bh_tx, 1);
+		goto out;
+	}
+
+	ktime_get_ts(&now);
+	time_left_us = (itp->last_sent.tv_sec -
+			now.tv_sec)*1000000 +
+		(itp->last_sent.tv_nsec - now.tv_nsec)/1000
+		+ itp->interval_us;
+
+	if (time_left_us > ITP_TIME_THRES_US) {
+		queue_delayed_work(priv->workqueue, &itp->tx_work,
+				ITP_US_TO_MS(time_left_us)*HZ/1000);
+		goto out;
+	}
+
+	if (time_left_us > 50)
+		udelay(time_left_us);
+
+	if (itp->number > 0)
+		itp->number--;
+
+	*data = itp->data;
+	*tx_len = itp->data_len + itp->hdr_len;
+
+	if (itp->data_mode == ITP_DATA_RANDOM)
+		bes2600_itp_fill_pattern(&itp->data[itp->hdr_len],
+				itp->data_len, itp->data_mode);
+
+	tx = (struct wsm_tx *)itp->data;
+	tx->packetID = __cpu_to_le32(itp->id++);
+	*burst = 2;
+	atomic_set(&priv->bh_tx, 1);
+	ktime_get_ts(&itp->last_sent);
+	atomic_add(1, &itp->awaiting_confirm);
+	spin_unlock_bh(&itp->tx_lock);
+	return 1;
+
+out:
+	spin_unlock_bh(&itp->tx_lock);
+	return 0;
+}
+
+bool bes2600_itp_rxed(struct bes2600_common *priv, struct sk_buff *skb)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb);
+	int signal;
+
+	if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST)
+		return bes2600_is_itp(priv);
+	if (rx->freq != priv->channel->center_freq)
+		return true;
+
+	signal = rx->signal;
+	itp->rx_cnt++;
+	itp->rx_rssi += signal;
+	if (itp->rx_rssi_min > rx->signal)
+		itp->rx_rssi_min = rx->signal;
+	if (itp->rx_rssi_max < rx->signal)
+		itp->rx_rssi_max = rx->signal;
+
+	return true;
+}
+
+void bes2600_itp_wake_up_tx(struct bes2600_common *priv)
+{
+	wake_up(&priv->debug->itp.write_wait);
+}
+
+bool bes2600_itp_tx_running(struct bes2600_common *priv)
+{
+	if (atomic_read(&priv->debug->itp.awaiting_confirm) ||
+			atomic_read(&priv->debug->itp.test_mode) ==
+			TEST_MODE_TX_TEST) {
+		atomic_sub(1, &priv->debug->itp.awaiting_confirm);
+		return true;
+	}
+	return false;
+}
+
+static void bes2600_itp_handle(struct bes2600_common *priv,
+			      struct sk_buff *skb)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	const struct wiphy *wiphy = priv->hw->wiphy;
+	int cmd;
+	int ret;
+
+	bes2600_dbg(BES2600_DBG_ITP, "[ITP] <<< %s", skb->data);
+	if (sscanf(skb->data, "%d", &cmd) != 1) {
+		bes2600_itp_err(priv, -EINVAL, 1);
+		return;
+	}
+
+	switch (cmd) {
+	case 1: /* RX test */
+		if (atomic_read(&itp->test_mode)) {
+			bes2600_itp_err(priv, -EBUSY, 0);
+			return;
+		}
+		ret = sscanf(skb->data, "%d,%d,%d",
+				&cmd, &itp->band, &itp->ch);
+		if (ret != 3) {
+			bes2600_itp_err(priv, -EINVAL, ret + 1);
+			return;
+		}
+		if (itp->band >= 2)
+			bes2600_itp_err(priv, -EINVAL, 2);
+		else if (!wiphy->bands[itp->band])
+			bes2600_itp_err(priv, -EINVAL, 2);
+		else if (itp->ch >=
+				wiphy->bands[itp->band]->n_channels)
+			bes2600_itp_err(priv, -EINVAL, 3);
+		else {
+			bes2600_itp_rx_stats(priv);
+			bes2600_itp_rx_start(priv);
+		}
+		break;
+	case 2: /* RX stat */
+		bes2600_itp_rx_stats(priv);
+		break;
+	case 3: /* RX/TX stop */
+		if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) {
+			bes2600_itp_rx_stats(priv);
+			bes2600_itp_rx_stop(priv);
+		} else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) {
+			bes2600_itp_tx_stop(priv);
+		} else
+			bes2600_itp_err(priv, -EBUSY, 0);
+		break;
+	case 4: /* TX start */
+		if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) {
+			bes2600_itp_err(priv, -EBUSY, 0);
+			return;
+		}
+		ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
+				&cmd, &itp->band, &itp->ch, &itp->rate,
+				&itp->preamble, &itp->number, &itp->data_mode,
+				&itp->interval_us, &itp->power, &itp->data_len);
+		if (ret != 10) {
+			bes2600_itp_err(priv, -EINVAL, ret + 1);
+			return;
+		}
+		if (itp->band >= 2)
+			bes2600_itp_err(priv, -EINVAL, 2);
+		else if (!wiphy->bands[itp->band])
+			bes2600_itp_err(priv, -EINVAL, 2);
+		else if (itp->ch >=
+				wiphy->bands[itp->band]->n_channels)
+			bes2600_itp_err(priv, -EINVAL, 3);
+		else if (itp->rate >= 20)
+			bes2600_itp_err(priv, -EINVAL, 4);
+		else if (itp->preamble >= ITP_PREAMBLE_MAX)
+			bes2600_itp_err(priv, -EINVAL, 5);
+		else if (itp->data_mode >= ITP_DATA_MAX_MODE)
+			bes2600_itp_err(priv, -EINVAL, 7);
+		else if (itp->data_len < ITP_MIN_DATA_SIZE ||
+				itp->data_len > priv->wsm_caps.sizeInpChBuf -
+				itp->hdr_len)
+			bes2600_itp_err(priv, -EINVAL, 8);
+		else {
+		    bes2600_itp_tx_start(priv);
+		}
+		break;
+	case 5:
+		bes2600_itp_get_version(priv, ITP_CHIP_ID);
+		break;
+	case 6:
+		bes2600_itp_get_version(priv, ITP_FW_VER);
+		break;
+
+	}
+}
+
+static void bes2600_itp_err(struct bes2600_common *priv,
+			   int err, int arg)
+{
+	struct bes2600_itp *itp = &priv->debug->itp;
+	struct sk_buff *skb;
+	static char buf[255];
+	int len;
+
+	len = snprintf(buf, sizeof(buf), "%d,%d\n",
+		err, arg);
+	if (len <= 0)
+		return;
+
+	skb = dev_alloc_skb(len);
+	if (!skb)
+		return;
+
+	skb_trim(skb, 0);
+	skb_put(skb, len);
+
+	memcpy(skb->data, buf, len);
+	skb_queue_tail(&itp->log_queue, skb);
+	wake_up(&itp->read_wait);
+
+	len = sprint_symbol(buf,
+			(unsigned long)__builtin_return_address(0));
+	if (len <= 0)
+		return;
+	bes2600_dbg(BES2600_DBG_ITP, "[ITP] error %d,%d from %s\n",
+			err, arg, buf);
+}
diff --git a/drivers/staging/bes2600/itp.h b/drivers/staging/bes2600/itp.h
new file mode 100644
index 000000000000..5cfba46896eb
--- /dev/null
+++ b/drivers/staging/bes2600/itp.h
@@ -0,0 +1,152 @@
+/*
+ * ITP code for BES2600 mac80211 driver
+ *
+ * Copyright (c) 2011, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES2600_ITP_H_INCLUDED
+#define BES2600_ITP_H_INCLUDED
+
+struct cw200_common;
+struct wsm_tx_confirm;
+struct dentry;
+
+#ifdef CONFIG_BES2600_ITP
+
+/*extern*/ struct ieee80211_channel;
+
+#define TEST_MODE_NO_TEST	(0)
+#define TEST_MODE_RX_TEST	(1)
+#define TEST_MODE_TX_TEST	(2)
+
+#define itp_printk(...) printk(__VA_ARGS__)
+#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+#define ITP_MIN_DATA_SIZE 6
+#define ITP_MAX_DATA_SIZE 1600
+#define ITP_TIME_THRES_US 10000
+#define ITP_US_TO_MS(x) ((x)/1000)
+#define ITP_MS_TO_US(x) ((x)*1000)
+#if ((ITP_US_TO_MS(ITP_TIME_THRES_US))*HZ/1000) < 1
+#warning not able to achieve non-busywaiting ITP_TIME_THRES_US\
+precision with current HZ value !
+#endif
+#define ITP_BUF_SIZE 255
+
+
+enum bes2600_itp_data_modes {
+	ITP_DATA_ZEROS,
+	ITP_DATA_ONES,
+	ITP_DATA_ZERONES,
+	ITP_DATA_RANDOM,
+	ITP_DATA_MAX_MODE,
+};
+
+enum bes2600_itp_version_type {
+	ITP_CHIP_ID,
+	ITP_FW_VER,
+};
+
+enum bes2600_itp_preamble_type {
+	ITP_PREAMBLE_LONG,
+	ITP_PREAMBLE_SHORT,
+	ITP_PREAMBLE_OFDM,
+	ITP_PREAMBLE_MIXED,
+	ITP_PREAMBLE_GREENFIELD,
+	ITP_PREAMBLE_MAX,
+};
+
+
+struct bes2600_itp {
+	struct bes2600_common	*priv;
+	atomic_t		open_count;
+	atomic_t		awaiting_confirm;
+	struct sk_buff_head	log_queue;
+	wait_queue_head_t	read_wait;
+	wait_queue_head_t	write_wait;
+	wait_queue_head_t	close_wait;
+	struct ieee80211_channel *saved_channel;
+	atomic_t		stop_tx;
+	struct delayed_work	tx_work;
+	struct delayed_work	tx_finish;
+	spinlock_t		tx_lock;
+	struct timespec		last_sent;
+	atomic_t		test_mode;
+	int			rx_cnt;
+	long			rx_rssi;
+	int			rx_rssi_max;
+	int			rx_rssi_min;
+	unsigned		band;
+	unsigned		ch;
+	unsigned		rate;
+	unsigned		preamble;
+	unsigned int		number;
+	unsigned		data_mode;
+	int			interval_us;
+	int			power;
+	u8			*data;
+	int			hdr_len;
+	int			data_len;
+	int			id;
+};
+
+int bes2600_itp_init(struct bes2600_common *priv);
+void bes2600_itp_release(struct bes2600_common *priv);
+
+bool bes2600_is_itp(struct bes2600_common *priv);
+bool bes2600_itp_rxed(struct bes2600_common *priv, struct sk_buff *skb);
+void bes2600_itp_wake_up_tx(struct bes2600_common *priv);
+int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data,
+		size_t *tx_len, int *burst);
+bool bes2600_itp_tx_running(struct bes2600_common *priv);
+
+#else /* CONFIG_BES2600_ITP */
+
+static inline int
+bes2600_itp_init(struct bes2600_common *priv)
+{
+	return 0;
+}
+
+static inline void bes2600_itp_release(struct bes2600_common *priv)
+{
+}
+
+static inline bool bes2600_is_itp(struct bes2600_common *priv)
+{
+	return false;
+}
+
+static inline bool bes2600_itp_rxed(struct bes2600_common *priv,
+		struct sk_buff *skb)
+{
+	return false;
+}
+
+
+static inline void bes2600_itp_consume_txed(struct bes2600_common *priv)
+{
+}
+
+static inline void bes2600_itp_wake_up_tx(struct bes2600_common *priv)
+{
+}
+
+static inline int bes2600_itp_get_tx(struct bes2600_common *priv, u8 **data,
+		size_t *tx_len, int *burst)
+{
+	return 0;
+}
+
+static inline bool bes2600_itp_tx_running(struct bes2600_common *priv)
+{
+	return false;
+}
+
+#endif /* CONFIG_BES2600_ITP */
+
+#endif /* BES2600_ITP_H_INCLUDED */
diff --git a/drivers/staging/bes2600/main.c b/drivers/staging/bes2600/main.c
new file mode 100644
index 000000000000..675839d7177a
--- /dev/null
+++ b/drivers/staging/bes2600/main.c
@@ -0,0 +1,1101 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*Linux version 3.4.0 compilation*/
+//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
+#include<linux/module.h>
+//#endif
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+
+#include "bes2600.h"
+#include "txrx.h"
+#include "sbus.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "bh.h"
+#include "sta.h"
+#include "ap.h"
+#include "scan.h"
+#include "debug.h"
+#include "pm.h"
+#include "bes2600_cfgvendor.h"
+#include "bes2600_driver_mode.h"
+#include "bes2600_factory.h"
+#include "bes_chardev.h"
+#include "txrx_opt.h"
+
+#ifdef PLAT_ROCKCHIP
+#include <linux/rfkill-wlan.h>
+#endif
+
+MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>");
+MODULE_DESCRIPTION("Softmac BES2600 common code");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("bes2600");
+
+static u8 bes2600_mac_template[ETH_ALEN] = {
+#if (GET_MAC_ADDR_METHOD == 2)||(GET_MAC_ADDR_METHOD == 3)
+	0x00, 0x12, 0x34, 0x00, 0x00, 0x00
+#else
+	0x02, 0x80, 0xe1, 0x00, 0x00, 0x00 /* To use macaddr of customers */
+#endif
+};
+
+#if (GET_MAC_ADDR_METHOD == 2) /* To use macaddr and PS Mode of customers */
+#ifndef PATH_WIFI_MACADDR
+#define PATH_WIFI_MACADDR		"/efs/wifi/.mac.info"
+#endif
+#elif (GET_MAC_ADDR_METHOD == 3)
+#define PATH_WIFI_MACADDR_TMP	"/data/.mac.info"
+#endif
+
+#ifdef CUSTOM_FEATURE
+#define PATH_WIFI_PSM_INFO		"/data/.psm.info"
+static int savedpsm = 0;
+#endif
+
+#if defined(CUSTOM_FEATURE) ||(GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3)
+int access_file(char *path, char *buffer, int size, int isRead);
+#endif
+
+/* TODO: use rates and channels from the device */
+#define RATETAB_ENT(_rate, _rateid, _flags)		\
+	{						\
+		.bitrate	= (_rate),		\
+		.hw_value	= (_rateid),		\
+		.flags		= (_flags),		\
+	}
+
+static struct ieee80211_rate bes2600_rates[] = {
+	RATETAB_ENT(10,  0,   0),
+	RATETAB_ENT(20,  1,   0),
+	RATETAB_ENT(55,  2,   0),
+	RATETAB_ENT(110, 3,   0),
+	RATETAB_ENT(60,  6,  0),
+	RATETAB_ENT(90,  7,  0),
+	RATETAB_ENT(120, 8,  0),
+	RATETAB_ENT(180, 9,  0),
+	RATETAB_ENT(240, 10, 0),
+	RATETAB_ENT(360, 11, 0),
+	RATETAB_ENT(480, 12, 0),
+	RATETAB_ENT(540, 13, 0),
+};
+
+static struct ieee80211_rate bes2600_mcs_rates[] = {
+	RATETAB_ENT(65,  14, IEEE80211_TX_RC_MCS),
+	RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
+	RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
+	RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
+	RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
+	RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
+	RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
+	RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
+};
+
+#define bes2600_a_rates		(bes2600_rates + 4)
+#define bes2600_a_rates_size	(ARRAY_SIZE(bes2600_rates) - 4)
+#define bes2600_g_rates		(bes2600_rates + 0)
+#define bes2600_g_rates_size	(ARRAY_SIZE(bes2600_rates))
+#define bes2600_n_rates		(bes2600_mcs_rates)
+#define bes2600_n_rates_size	(ARRAY_SIZE(bes2600_mcs_rates))
+
+
+#define CHAN2G(_channel, _freq, _flags) {			\
+	.band			= NL80211_BAND_2GHZ,		\
+	.center_freq		= (_freq),			\
+	.hw_value		= (_channel),			\
+	.flags			= (_flags),			\
+	.max_antenna_gain	= 0,				\
+	.max_power		= 30,				\
+}
+
+#define CHAN5G(_channel, _flags) {				\
+	.band			= NL80211_BAND_5GHZ,		\
+	.center_freq	= 5000 + (5 * (_channel)),		\
+	.hw_value		= (_channel),			\
+	.flags			= (_flags),			\
+	.max_antenna_gain	= 0,				\
+	.max_power		= 30,				\
+}
+
+static struct ieee80211_channel bes2600_2ghz_chantable[] = {
+	CHAN2G(1, 2412, 0),
+	CHAN2G(2, 2417, 0),
+	CHAN2G(3, 2422, 0),
+	CHAN2G(4, 2427, 0),
+	CHAN2G(5, 2432, 0),
+	CHAN2G(6, 2437, 0),
+	CHAN2G(7, 2442, 0),
+	CHAN2G(8, 2447, 0),
+	CHAN2G(9, 2452, 0),
+	CHAN2G(10, 2457, 0),
+	CHAN2G(11, 2462, 0),
+	CHAN2G(12, 2467, 0),
+	CHAN2G(13, 2472, 0),
+	CHAN2G(14, 2484, 0),
+};
+
+#ifdef CONFIG_BES2600_5GHZ_SUPPORT
+#if 0
+static struct ieee80211_channel bes2600_5ghz_chantable[] = {
+	CHAN5G(34, 0),		CHAN5G(36, 0),
+	CHAN5G(38, 0),		CHAN5G(40, 0),
+	CHAN5G(42, 0),		CHAN5G(44, 0),
+	CHAN5G(46, 0),		CHAN5G(48, 0),
+	CHAN5G(52, 0),		CHAN5G(56, 0),
+	CHAN5G(60, 0),		CHAN5G(64, 0),
+	CHAN5G(100, 0),		CHAN5G(104, 0),
+	CHAN5G(108, 0),		CHAN5G(112, 0),
+	CHAN5G(116, 0),		CHAN5G(120, 0),
+	CHAN5G(124, 0),		CHAN5G(128, 0),
+	CHAN5G(132, 0),		CHAN5G(136, 0),
+	CHAN5G(140, 0),		CHAN5G(149, 0),
+	CHAN5G(153, 0),		CHAN5G(157, 0),
+	CHAN5G(161, 0),		CHAN5G(165, 0),
+	CHAN5G(184, 0),		CHAN5G(188, 0),
+	CHAN5G(192, 0),		CHAN5G(196, 0),
+	CHAN5G(200, 0),		CHAN5G(204, 0),
+	CHAN5G(208, 0),		CHAN5G(212, 0),
+	CHAN5G(216, 0),
+};
+#else
+/* comply with china regulation on 5G */
+static struct ieee80211_channel bes2600_5ghz_chantable[] = {
+	CHAN5G(36, 0),
+	CHAN5G(40, 0),
+	CHAN5G(44, 0),
+	CHAN5G(48, 0),
+	CHAN5G(52, 0),
+	CHAN5G(56, 0),
+	CHAN5G(60, 0),
+	CHAN5G(64, 0),
+	CHAN5G(149, 0),
+	CHAN5G(153, 0),
+	CHAN5G(157, 0),
+	CHAN5G(161, 0),
+	CHAN5G(165, 0),
+};
+#endif
+#endif
+
+static struct ieee80211_supported_band bes2600_band_2ghz = {
+	.channels = bes2600_2ghz_chantable,
+	.n_channels = ARRAY_SIZE(bes2600_2ghz_chantable),
+	.bitrates = bes2600_g_rates,
+	.n_bitrates = bes2600_g_rates_size,
+	.ht_cap = {
+		.cap = IEEE80211_HT_CAP_GRN_FLD |
+			(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
+			IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+			IEEE80211_HT_CAP_SGI_20 |
+			IEEE80211_HT_CAP_SGI_40 |
+			IEEE80211_HT_CAP_MAX_AMSDU,
+		.ht_supported = 1,
+		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K,
+		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+		.mcs = {
+			.rx_mask[0] = 0xFF,
+			.rx_highest = __cpu_to_le16(0),
+			.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		},
+	},
+};
+
+#ifdef CONFIG_BES2600_5GHZ_SUPPORT
+static struct ieee80211_supported_band bes2600_band_5ghz = {
+	.channels = bes2600_5ghz_chantable,
+	.n_channels = ARRAY_SIZE(bes2600_5ghz_chantable),
+	.bitrates = bes2600_a_rates,
+	.n_bitrates = bes2600_a_rates_size,
+	.ht_cap = {
+		.cap = IEEE80211_HT_CAP_GRN_FLD |
+			(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
+			IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+			IEEE80211_HT_CAP_SGI_20 |
+			IEEE80211_HT_CAP_SGI_40 |
+			IEEE80211_HT_CAP_MAX_AMSDU,
+		.ht_supported = 1,
+		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K,
+		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+		.mcs = {
+			.rx_mask[0] = 0xFF,
+			.rx_highest = __cpu_to_le16(0x41),
+			.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+		},
+	},
+};
+#endif /* CONFIG_BES2600_5GHZ_SUPPORT */
+
+static const unsigned long bes2600_ttl[] = {
+	1 * HZ,	/* VO */
+	2 * HZ,	/* VI */
+	5 * HZ, /* BE */
+	10 * HZ	/* BK */
+};
+
+static const struct ieee80211_iface_limit bes2600_if_limits[] = {
+	{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION) },
+	{ .max = 1, .types = BIT(NL80211_IFTYPE_AP) |
+			     BIT(NL80211_IFTYPE_P2P_CLIENT) |
+			     BIT(NL80211_IFTYPE_P2P_GO) },
+#ifdef P2P_MULTIVIF
+	{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
+#endif
+};
+
+
+
+static const struct ieee80211_iface_combination bes2600_if_comb[] = {
+	{
+		.limits = bes2600_if_limits,
+		.n_limits = ARRAY_SIZE(bes2600_if_limits),
+		.max_interfaces = CW12XX_MAX_VIFS,
+		.num_different_channels = 1,
+	},
+};
+
+
+static const struct ieee80211_ops bes2600_ops = {
+	.start			= bes2600_start,
+	.stop			= bes2600_stop,
+	.add_interface		= bes2600_add_interface,
+	.remove_interface	= bes2600_remove_interface,
+	.change_interface	= bes2600_change_interface,
+	.tx			= bes2600_tx,
+	.hw_scan		= bes2600_hw_scan,
+	.cancel_hw_scan         = bes2600_cancel_hw_scan,
+#ifdef ROAM_OFFLOAD
+	.sched_scan_start	= bes2600_hw_sched_scan_start,
+	.sched_scan_stop	= bes2600_hw_sched_scan_stop,
+#endif /*ROAM_OFFLOAD*/
+	.set_tim		= bes2600_set_tim,
+	.sta_notify		= bes2600_sta_notify,
+	.sta_add		= bes2600_sta_add,
+	.sta_remove		= bes2600_sta_remove,
+	.set_key		= bes2600_set_key,
+	.set_rts_threshold	= bes2600_set_rts_threshold,
+	.config			= bes2600_config,
+	.bss_info_changed	= bes2600_bss_info_changed,
+	.prepare_multicast	= bes2600_prepare_multicast,
+	.configure_filter	= bes2600_configure_filter,
+	.conf_tx		= bes2600_conf_tx,
+	.get_stats		= bes2600_get_stats,
+	.ampdu_action		= bes2600_ampdu_action,
+	.flush			= bes2600_flush,
+#ifdef CONFIG_PM
+	.suspend		= bes2600_wow_suspend,
+	.resume			= bes2600_wow_resume,
+#endif
+	/* Intentionally not offloaded:					*/
+	/*.channel_switch	= bes2600_channel_switch,		*/
+	.remain_on_channel	= bes2600_remain_on_channel,
+	.cancel_remain_on_channel = bes2600_cancel_remain_on_channel,
+#ifdef IPV6_FILTERING
+	//.set_data_filter        = bes2600_set_data_filter,
+#endif /*IPV6_FILTERING*/
+#ifdef CONFIG_BES2600_TESTMODE
+    .testmode_cmd  = bes2600_testmode_cmd,
+#endif
+};
+
+#ifdef CONFIG_PM
+static const struct wiphy_wowlan_support bes2600_wowlan_support = {
+	/* Support only for limited wowlan functionalities */
+	.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
+};
+#endif
+
+#ifdef CONFIG_BES2600_WAPI_SUPPORT
+static void bes2600_init_wapi_cipher(struct ieee80211_hw *hw)
+{
+	static struct ieee80211_cipher_scheme wapi_cs = {
+		.cipher = WLAN_CIPHER_SUITE_SMS4,
+		.iftype = BIT(NL80211_IFTYPE_STATION),
+		.hdr_len = 18,
+		.pn_len = 16,
+		.pn_off = 2,
+		.key_idx_off = 0,
+		.key_idx_mask = 0x01,
+		.key_idx_shift = 0,
+		.mic_len = 16
+	};
+
+	hw->cipher_schemes = &wapi_cs;
+	hw->n_cipher_schemes = 1;
+}
+#endif
+
+static void bes2600_get_base_mac(struct bes2600_common *hw_priv)
+{
+#if (GET_MAC_ADDR_METHOD == 1)
+	u8 fixed_mac[ETH_ALEN];
+#endif
+#if (GET_MAC_ADDR_METHOD == 2)||(GET_MAC_ADDR_METHOD == 3) /* To use macaddr of customers */
+	char readmac[17+1]={0,};
+#endif
+	memcpy(hw_priv->addresses[0].addr, bes2600_mac_template, ETH_ALEN);
+
+#if (GET_MAC_ADDR_METHOD == 1)
+	rockchip_wifi_mac_addr(fixed_mac);
+	memcpy(hw_priv->addresses[0].addr, fixed_mac, ETH_ALEN * sizeof(u8));
+	bes2600_info(BES2600_DBG_INIT, "get fixed mac address from flash=[%02x:%02x:%02x:%02x:%02x:%02x]\n", fixed_mac[0], fixed_mac[1],
+				fixed_mac[2], fixed_mac[3], fixed_mac[4], fixed_mac[5]);
+	if(fixed_mac[0] & (0x01)){
+		bes2600_warn(BES2600_DBG_INIT, "The MAC address is not suitable for unicast, change to random MAC\n");
+		memcpy(hw_priv->addresses[0].addr, bes2600_mac_template, ETH_ALEN);
+	}
+
+#elif (GET_MAC_ADDR_METHOD == 2) /* To use macaddr of customers */
+	if(access_file(PATH_WIFI_MACADDR,readmac,17,1) > 0) {
+		sscanf(readmac,"%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+								(u8 *)&hw_priv->addresses[0].addr[0],
+								(u8 *)&hw_priv->addresses[0].addr[1],
+								(u8 *)&hw_priv->addresses[0].addr[2],
+								(u8 *)&hw_priv->addresses[0].addr[3],
+								(u8 *)&hw_priv->addresses[0].addr[4],
+								(u8 *)&hw_priv->addresses[0].addr[5]);
+	}
+#elif (GET_MAC_ADDR_METHOD == 3)
+	if(access_file(PATH_WIFI_MACADDR_TMP,readmac,17,1) > 0) {
+		sscanf(readmac,"%02X:%02X:%02X:%02X:%02X:%02X",
+								(u8 *)&hw_priv->addresses[0].addr[0],
+								(u8 *)&hw_priv->addresses[0].addr[1],
+								(u8 *)&hw_priv->addresses[0].addr[2],
+								(u8 *)&hw_priv->addresses[0].addr[3],
+								(u8 *)&hw_priv->addresses[0].addr[4],
+								(u8 *)&hw_priv->addresses[0].addr[5]);
+	}
+#endif
+	if (hw_priv->addresses[0].addr[3] == 0 &&
+	    hw_priv->addresses[0].addr[4] == 0 &&
+	    hw_priv->addresses[0].addr[5] == 0)
+		get_random_bytes(&hw_priv->addresses[0].addr[3], 3);
+}
+
+static void bes2600_derive_mac(struct bes2600_common *hw_priv)
+{
+	memcpy(hw_priv->addresses[1].addr, hw_priv->addresses[0].addr, ETH_ALEN);
+	hw_priv->addresses[1].addr[5] =
+			hw_priv->addresses[0].addr[5] + 1;
+
+#ifdef P2P_MULTIVIF
+	memcpy(hw_priv->addresses[2].addr, hw_priv->addresses[1].addr,
+	       ETH_ALEN);
+	hw_priv->addresses[2].addr[4] ^= 0x80;
+#endif
+}
+
+struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len)
+{
+	int i;
+	struct ieee80211_hw *hw;
+	struct bes2600_common *hw_priv;
+	struct ieee80211_supported_band *sband;
+	int band;
+
+	hw = ieee80211_alloc_hw(hw_priv_data_len, &bes2600_ops);
+	if (!hw)
+		return NULL;
+
+	hw_priv = hw->priv;
+	/* TODO:COMBO this debug message can be removed */
+	bes2600_err(BES2600_DBG_INIT, "Allocated hw_priv @ %p\n", hw_priv);
+	hw_priv->if_id_slot = 0;
+	hw_priv->roc_if_id = -1;
+	hw_priv->scan_switch_if_id = -1;
+	atomic_set(&hw_priv->num_vifs, 0);
+	atomic_set(&hw_priv->netdevice_start, 0);
+
+	bes2600_get_base_mac(hw_priv);
+	bes2600_derive_mac(hw_priv);
+
+	hw_priv->hw = hw;
+	hw_priv->rates = bes2600_rates; /* TODO: fetch from FW */
+	hw_priv->mcs_rates = bes2600_n_rates;
+#ifdef ROAM_OFFLOAD
+	hw_priv->auto_scanning = 0;
+	hw_priv->frame_rcvd = 0;
+	hw_priv->num_scanchannels = 0;
+	hw_priv->num_2g_channels = 0;
+	hw_priv->num_5g_channels = 0;
+#endif /*ROAM_OFFLOAD*/
+#ifdef AP_AGGREGATE_FW_FIX
+	/* Enable block ACK for 4 TID (BE,VI,VI,VO). */
+	/*due to HW limitations*/
+	hw_priv->ba_tid_mask = 0xB1;
+#else
+	/* Enable block ACK for every TID but voice. */
+	hw_priv->ba_tid_mask = 0xFF;//0x3F;
+#endif
+
+	/* Init tx retry limit */
+#ifdef BES2600_TX_RX_OPT
+	hw_priv->long_frame_max_tx_count = 31;
+	hw_priv->short_frame_max_tx_count = 31;
+#else
+	hw_priv->long_frame_max_tx_count = 7;
+	hw_priv->short_frame_max_tx_count = 15;
+#endif
+	hw_priv->hw->max_rate_tries = hw_priv->short_frame_max_tx_count;
+
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, SUPPORTS_PS);
+	ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+	ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
+	ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
+	ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
+	ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+	ieee80211_hw_set(hw, CONNECTION_MONITOR);
+	ieee80211_hw_set(hw, MFP_CAPABLE);
+
+	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+					  BIT(NL80211_IFTYPE_ADHOC) |
+					  BIT(NL80211_IFTYPE_AP) |
+					  BIT(NL80211_IFTYPE_MESH_POINT) |
+					  BIT(NL80211_IFTYPE_P2P_CLIENT) |
+					  BIT(NL80211_IFTYPE_P2P_GO);
+#ifdef P2P_MULTIVIF
+	hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
+#endif
+
+	/* Support only for limited wowlan functionalities */
+#ifdef CONFIG_PM
+	hw->wiphy->wowlan = &bes2600_wowlan_support;
+#endif
+
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+
+#if defined(CONFIG_BES2600_DISABLE_BEACON_HINTS)
+	hw->wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS;
+#endif
+	hw->wiphy->n_addresses = CW12XX_MAX_VIFS;
+	hw->wiphy->addresses = hw_priv->addresses;
+	hw->wiphy->max_remain_on_channel_duration = 500;
+	hw->wiphy->reg_notifier = bes2600_reg_notifier;
+	//hw->channel_change_time = 500;	/* TODO: find actual value */
+	/* hw_priv->beacon_req_id = cpu_to_le32(0); */
+	hw->queues = 4;
+	hw_priv->noise = -94;
+
+	hw->max_rates = 8;
+	hw->max_rate_tries = 15;
+	hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
+		8  /* TKIP IV */ +
+		12 /* TKIP ICV and MIC */;
+
+	hw->sta_data_size = sizeof(struct bes2600_sta_priv);
+	hw->vif_data_size = sizeof(struct bes2600_vif);
+
+	hw->wiphy->bands[NL80211_BAND_2GHZ] = &bes2600_band_2ghz;
+#ifdef CONFIG_BES2600_5GHZ_SUPPORT
+	hw->wiphy->bands[NL80211_BAND_5GHZ] = &bes2600_band_5ghz;
+#endif /* CONFIG_BES2600_5GHZ_SUPPORT */
+
+	/* Channel params have to be cleared before registering wiphy again */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+	for (band = 0; band < NUM_NL80211_BANDS; band++) {
+#else
+	for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+#endif
+		sband = hw->wiphy->bands[band];
+		if (!sband)
+			continue;
+		for (i = 0; i < sband->n_channels; i++) {
+			sband->channels[i].flags = 0;
+			sband->channels[i].max_antenna_gain = 0;
+			sband->channels[i].max_power = 30;
+		}
+	}
+
+	hw->wiphy->max_scan_ssids = WSM_SCAN_MAX_NUM_OF_SSIDS;
+	hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+
+	hw->wiphy->iface_combinations = bes2600_if_comb;
+	hw->wiphy->n_iface_combinations = ARRAY_SIZE(bes2600_if_comb);
+
+#ifdef CONFIG_BES2600_WAPI_SUPPORT
+	hw_priv->last_ins_wapi_usk_id = -1;
+	hw_priv->last_del_wapi_usk_id = -1;
+	bes2600_init_wapi_cipher(hw);
+#endif
+
+	SET_IEEE80211_PERM_ADDR(hw, hw_priv->addresses[0].addr);
+
+	spin_lock_init(&hw_priv->vif_list_lock);
+	sema_init(&hw_priv->wsm_cmd_sema, 1);
+	sema_init(&hw_priv->conf_lock, 1);
+	sema_init(&hw_priv->wsm_oper_lock, 1);
+#ifdef CONFIG_BES2600_TESTMODE
+	spin_lock_init(&hw_priv->tsm_lock);
+#endif /*CONFIG_BES2600_TESTMODE*/
+	hw_priv->workqueue = create_singlethread_workqueue("bes2600_wq");
+	sema_init(&hw_priv->scan.lock, 1);
+	INIT_WORK(&hw_priv->scan.work, bes2600_scan_work);
+#ifdef ROAM_OFFLOAD
+	INIT_WORK(&hw_priv->scan.swork, bes2600_sched_scan_work);
+#endif /*ROAM_OFFLOAD*/
+	INIT_DELAYED_WORK(&hw_priv->scan.probe_work, bes2600_probe_work);
+	INIT_DELAYED_WORK(&hw_priv->scan.timeout, bes2600_scan_timeout);
+#ifdef CONFIG_BES2600_TESTMODE
+	INIT_DELAYED_WORK(&hw_priv->advance_scan_timeout,
+		 bes2600_advance_scan_timeout);
+#endif
+	INIT_DELAYED_WORK(&hw_priv->rem_chan_timeout, bes2600_rem_chan_timeout);
+	hw_priv->rtsvalue = 0;
+	spin_lock_init(&hw_priv->rtsvalue_lock);
+	INIT_WORK(&hw_priv->dynamic_opt_txrx_work, bes2600_dynamic_opt_txrx_work);
+	INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work);
+	spin_lock_init(&hw_priv->event_queue_lock);
+	INIT_LIST_HEAD(&hw_priv->event_queue);
+	INIT_WORK(&hw_priv->event_handler, bes2600_event_handler);
+	INIT_WORK(&hw_priv->ba_work, bes2600_ba_work);
+	spin_lock_init(&hw_priv->ba_lock);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+	timer_setup(&hw_priv->ba_timer, bes2600_ba_timer, 0);
+#else
+	setup_timer(&hw_priv->ba_timer, bes2600_ba_timer, (unsigned long)hw_priv);
+#endif
+
+	if (unlikely(bes2600_queue_stats_init(&hw_priv->tx_queue_stats,
+			WLAN_LINK_ID_MAX,
+			bes2600_skb_dtor,
+			hw_priv))) {
+		ieee80211_free_hw(hw);
+		return NULL;
+	}
+
+	for (i = 0; i < 4; ++i) {
+		if (unlikely(bes2600_queue_init(&hw_priv->tx_queue[i],
+				&hw_priv->tx_queue_stats, i, CW12XX_MAX_QUEUE_SZ,
+				bes2600_ttl[i]))) {
+			for (; i > 0; i--)
+				bes2600_queue_deinit(&hw_priv->tx_queue[i - 1]);
+			bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats);
+			ieee80211_free_hw(hw);
+			return NULL;
+		}
+	}
+
+	init_waitqueue_head(&hw_priv->channel_switch_done);
+	init_waitqueue_head(&hw_priv->wsm_cmd_wq);
+	init_waitqueue_head(&hw_priv->wsm_startup_done);
+	init_waitqueue_head(&hw_priv->offchannel_wq);
+	hw_priv->offchannel_done = 0;
+	wsm_buf_init(&hw_priv->wsm_cmd_buf);
+	spin_lock_init(&hw_priv->wsm_cmd.lock);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+	timer_setup(&hw_priv->mcu_mon_timer, bes2600_bh_mcu_active_monitor, 0);
+	timer_setup(&hw_priv->lmac_mon_timer, bes2600_bh_lmac_active_monitor, 0);
+#else
+	setup_timer(&hw_priv->mcu_mon_timer, bes2600_bh_mcu_active_monitor,(unsigned long)hw_priv);
+	setup_timer(&hw_priv->lmac_mon_timer, bes2600_bh_lmac_active_monitor,(unsigned long)hw_priv);
+#endif
+
+	bes2600_tx_loop_init(hw_priv);
+
+#ifdef CONFIG_BES2600_TESTMODE
+	hw_priv->test_frame.data = NULL;
+	hw_priv->test_frame.len = 0;
+#endif /* CONFIG_BES2600_TESTMODE */
+
+#ifdef CONFIG_BES2600_VENDOR_CMD
+	bes2600_set_vendor_command(hw->wiphy);
+#endif
+
+#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT)
+	hw_priv->wsm_dump_max_size = 20;
+#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */
+
+	for (i = 0; i < CW12XX_MAX_VIFS; i++)
+		hw_priv->hw_bufs_used_vif[i] = 0;
+
+#ifdef MCAST_FWDING
+       for (i = 0; i < WSM_MAX_BUF; i++)
+               wsm_init_release_buffer_request(hw_priv, i);
+       hw_priv->buf_released = 0;
+#endif
+	hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
+	hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
+	return hw;
+}
+EXPORT_SYMBOL_GPL(bes2600_init_common);
+
+
+int bes2600_register_common(struct ieee80211_hw *dev)
+{
+	struct bes2600_common *hw_priv = dev->priv;
+	int err;
+
+	err = ieee80211_register_hw(dev);
+	if (err) {
+		bes2600_err(BES2600_DBG_INIT, "Cannot register device (%d).\n",
+				err);
+		return err;
+	}
+
+#ifdef CONFIG_BES2600_LEDS
+	err = bes2600_init_leds(priv);
+	if (err) {
+		bes2600_pm_deinit(&hw_priv->pm_state);
+		ieee80211_unregister_hw(dev);
+		return err;
+	}
+#endif /* CONFIG_BES2600_LEDS */
+
+	bes2600_debug_init_common(hw_priv);
+
+	bes2600_info(BES2600_DBG_INIT, "is registered as '%s'\n",
+			wiphy_name(dev->wiphy));
+	return 0;
+}
+EXPORT_SYMBOL_GPL(bes2600_register_common);
+
+void bes2600_free_common(struct ieee80211_hw *dev)
+{
+	/* struct bes2600_common *hw_priv = dev->priv; */
+#ifdef CONFIG_BES2600_TESTMODE
+	struct bes2600_common *hw_priv = dev->priv;
+	kfree(hw_priv->test_frame.data);
+#endif /* CONFIG_BES2600_TESTMODE */
+
+#ifdef CONFIG_BES2600_VENDOR_CMD
+	bes2600_vendor_command_detach(dev->wiphy);
+#endif
+
+	/* unsigned int i; */
+
+	ieee80211_free_hw(dev);
+}
+EXPORT_SYMBOL_GPL(bes2600_free_common);
+
+void bes2600_unregister_common(struct ieee80211_hw *dev)
+{
+	struct bes2600_common *hw_priv = dev->priv;
+	int i;
+
+	ieee80211_unregister_hw(dev);
+
+	del_timer_sync(&hw_priv->ba_timer);
+
+	hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv);
+	bes2600_unregister_bh(hw_priv);
+
+	bes2600_debug_release_common(hw_priv);
+
+#ifdef CONFIG_BES2600_LEDS
+	bes2600_unregister_leds(hw_priv);
+#endif /* CONFIG_BES2600_LEDS */
+
+	wsm_buf_deinit(&hw_priv->wsm_cmd_buf);
+	destroy_workqueue(hw_priv->workqueue);
+	hw_priv->workqueue = NULL;
+	if (hw_priv->skb_cache) {
+		dev_kfree_skb(hw_priv->skb_cache);
+		hw_priv->skb_cache = NULL;
+	}
+	if (hw_priv->sdd) {
+#ifndef CONFIG_BES2600_STATIC_SDD
+		release_firmware(hw_priv->sdd);
+#endif
+		hw_priv->sdd = NULL;
+	}
+	for (i = 0; i < 4; ++i)
+		bes2600_queue_deinit(&hw_priv->tx_queue[i]);
+	bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats);
+	for (i = 0; i < CW12XX_MAX_VIFS; i++) {
+		kfree(hw_priv->vif_list[i]);
+		hw_priv->vif_list[i] = NULL;
+	}
+
+	bes2600_pwr_exit(hw_priv);
+}
+EXPORT_SYMBOL_GPL(bes2600_unregister_common);
+
+#if 0
+static void cw12xx_set_ifce_comb(struct bes2600_common *hw_priv,
+				 struct ieee80211_hw *hw)
+{
+#ifdef P2P_MULTIVIF
+	hw_priv->if_limits1[0].max = 2;
+#else
+	hw_priv->if_limits1[0].max = 1;
+#endif
+
+	hw_priv->if_limits1[0].types = BIT(NL80211_IFTYPE_STATION);
+	hw_priv->if_limits1[1].max = 1;
+	hw_priv->if_limits1[1].types = BIT(NL80211_IFTYPE_AP);
+
+#ifdef P2P_MULTIVIF
+	hw_priv->if_limits2[0].max = 3;
+#else
+	hw_priv->if_limits2[0].max = 2;
+#endif
+	hw_priv->if_limits2[0].types = BIT(NL80211_IFTYPE_STATION);
+
+#ifdef P2P_MULTIVIF
+       hw_priv->if_limits3[0].max = 2;
+#else
+	hw_priv->if_limits3[0].max = 1;
+#endif
+
+	hw_priv->if_limits3[0].types = BIT(NL80211_IFTYPE_STATION);
+	hw_priv->if_limits3[1].max = 1;
+	hw_priv->if_limits3[1].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+				      BIT(NL80211_IFTYPE_P2P_GO);
+
+	/* TODO:COMBO: mac80211 doesn't yet support more than 1
+	 * different channel */
+	hw_priv->if_combs[0].num_different_channels = 1;
+#ifdef P2P_MULTIVIF
+        hw_priv->if_combs[0].max_interfaces = 3;
+#else
+	hw_priv->if_combs[0].max_interfaces = 2;
+#endif
+	hw_priv->if_combs[0].limits = hw_priv->if_limits1;
+	hw_priv->if_combs[0].n_limits = 2;
+
+	hw_priv->if_combs[1].num_different_channels = 1;
+
+#ifdef P2P_MULTIVIF
+        hw_priv->if_combs[1].max_interfaces = 3;
+#else
+	hw_priv->if_combs[1].max_interfaces = 2;
+#endif
+	hw_priv->if_combs[1].limits = hw_priv->if_limits2;
+	hw_priv->if_combs[1].n_limits = 1;
+
+	hw_priv->if_combs[2].num_different_channels = 1;
+#ifdef P2P_MULTIVIF
+        hw_priv->if_combs[2].max_interfaces = 3;
+#else
+	hw_priv->if_combs[2].max_interfaces = 2;
+#endif
+	hw_priv->if_combs[2].limits = hw_priv->if_limits3;
+	hw_priv->if_combs[2].n_limits = 2;
+
+	hw->wiphy->iface_combinations = &hw_priv->if_combs[0];
+	hw->wiphy->n_iface_combinations = 3;
+
+}
+#endif
+static int bes2600_sbus_comm_init(struct bes2600_common *hw_priv)
+{
+	int ret = 0;
+
+#if defined(FW_DOWNLOAD_BY_USB)
+	if (hw_priv->sbus_ops->reset)
+		hw_priv->sbus_ops->reset(hw_priv->sbus_priv);
+#endif
+
+#ifndef CONFIG_BES2600_WLAN_SPI
+	if (hw_priv->sbus_ops->init)
+		hw_priv->sbus_ops->init(hw_priv->sbus_priv, hw_priv);
+#endif
+
+	/* Register Interrupt Handler */
+	hw_priv->sbus_ops->irq_subscribe(hw_priv->sbus_priv,
+	        (sbus_irq_handler)bes2600_irq_handler, hw_priv);
+	hw_priv->hw_type = HIF_8601_SILICON;
+	hw_priv->hw_revision = BES2600_HW_REV_CUT10;
+
+	return ret;
+}
+
+int bes2600_core_probe(const struct sbus_ops *sbus_ops,
+		      struct sbus_priv *sbus,
+		      struct device *pdev,
+		      struct bes2600_common **pself)
+{
+	int err = -ENOMEM;
+	//u16 ctrl_reg;
+	struct ieee80211_hw *dev;
+	struct bes2600_common *hw_priv;
+
+#if defined(CONFIG_BES2600_WLAN_USB)
+	int if_id;
+
+#ifdef CUSTOM_FEATURE/* To control ps mode */
+	struct wsm_operational_mode mode = {
+		.power_mode = wsm_power_mode_quiescent,
+		.disableMoreFlagUsage = true,
+	};
+	char buffer[2];
+    savedpsm = mode.power_mode;
+	if(access_file(PATH_WIFI_PSM_INFO,buffer,2,1) > 0) {
+		if(buffer[0] == 0x30) {
+			mode.power_mode = wsm_power_mode_active;
+		}
+		else
+		{
+			if(savedpsm)
+				mode.power_mode = savedpsm;
+			else /* Set default */
+				mode.power_mode = wsm_power_mode_quiescent;
+		}
+		bes2600_info(BES2600_DBG_INIT, "BES2600 : PSM changed to %d\n",mode.power_mode);
+	}
+	else {
+		bes2600_info(BES2600_DBG_INIT, "BES2600 : Using default PSM %d\n",mode.power_mode);
+	}
+#endif
+#endif
+
+	dev = bes2600_init_common(sizeof(struct bes2600_common));
+	if (!dev)
+		goto err;
+
+	hw_priv = dev->priv;
+	hw_priv->sbus_ops = sbus_ops;
+	hw_priv->sbus_priv = sbus;
+	hw_priv->pdev = pdev;
+	SET_IEEE80211_DEV(hw_priv->hw, pdev);
+
+	/* WSM callbacks. */
+	hw_priv->wsm_cbc.scan_complete = bes2600_scan_complete_cb;
+	hw_priv->wsm_cbc.tx_confirm = bes2600_tx_confirm_cb;
+	hw_priv->wsm_cbc.rx = bes2600_rx_cb;
+	hw_priv->wsm_cbc.suspend_resume = bes2600_suspend_resume;
+	/* hw_priv->wsm_cbc.set_pm_complete = bes2600_set_pm_complete_cb; */
+	hw_priv->wsm_cbc.channel_switch = bes2600_channel_switch_cb;
+
+	bes2600_pwr_init(hw_priv);
+
+	err = bes2600_register_bh(hw_priv);
+	if (err)
+		goto err1;
+
+	err = bes2600_sbus_comm_init(hw_priv);
+	if (err)
+		goto err2;
+
+	if (bes2600_chrdev_get_fw_type() == BES2600_FW_TYPE_BT) {
+	} else if (bes2600_chrdev_get_fw_type() == BES2600_FW_TYPE_WIFI_NO_SIGNAL) {
+#ifdef CONFIG_BES2600_WLAN_BES
+		*pself = dev->priv;
+#endif
+#if defined(CONFIG_BES2600_WLAN_SDIO) || defined(CONFIG_BES2600_WLAN_SPI)
+		if (bes2600_wifi_start(hw_priv))
+			goto err3;
+#else
+		mdelay(2000);
+		if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done,
+				hw_priv->wsm_caps.firmwareReady, 10*HZ) <= 0) {
+			bes2600_info(BES2600_DBG_INIT, "startup timeout!!!\n");
+			err = -ENODEV;
+			goto err3;
+		}
+#endif
+#if defined(CONFIG_BES2600_WLAN_USB)
+	} else {
+		mdelay(2000);
+
+	/*
+		hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+		WARN_ON(hw_priv->sbus_ops->set_block_size(hw_priv->sbus_priv,
+				SDIO_BLOCK_SIZE));
+		hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
+
+		cw12xx_set_ifce_comb(hw_priv, dev);
+
+		hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+		WARN_ON(hw_priv->sbus_ops->set_block_size(hw_priv->sbus_priv,
+			SDIO_BLOCK_SIZE));
+		hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
+	*/
+
+		if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done,
+					hw_priv->wsm_caps.firmwareReady, 10*HZ) <= 0) {
+
+			/* TODO: Needs to find how to reset device */
+			/*       in QUEUE mode properly.           */
+			bes2600_info(BES2600_DBG_INIT, "startup timeout!!!\n");
+			err = -ENODEV;
+			goto err3;
+		}
+	/*
+		WARN_ON(bes2600_reg_write_16(hw_priv, ST90TDS_CONTROL_REG_ID,
+						ST90TDS_CONT_WUP_BIT));
+
+		if (bes2600_reg_read_16(hw_priv,ST90TDS_CONTROL_REG_ID, &ctrl_reg))
+			WARN_ON(bes2600_reg_read_16(hw_priv,ST90TDS_CONTROL_REG_ID,
+							&ctrl_reg));
+
+		WARN_ON(!(ctrl_reg & ST90TDS_CONT_RDY_BIT));
+	*/
+		for (if_id = 0; if_id < 2; if_id++) {
+			/* Set low-power mode. */
+			/* Enable multi-TX confirmation */
+			WARN_ON(wsm_use_multi_tx_conf(hw_priv, true, if_id));
+		}
+#endif
+	}
+
+	err = bes2600_register_common(dev);
+	if (err) {
+		goto err3;
+	}
+
+	*pself = dev->priv;
+	return err;
+
+err3:
+	hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv);
+	if (sbus_ops->reset)
+		sbus_ops->reset(sbus);
+err2:
+	bes2600_unregister_bh(hw_priv);
+err1:
+	bes2600_free_common(dev);
+err:
+	return err;
+}
+
+void bes2600_core_release(struct bes2600_common *self)
+{
+	bes2600_unregister_common(self->hw);
+	bes2600_free_common(self->hw);
+	return;
+}
+
+#if defined(CUSTOM_FEATURE) ||(GET_MAC_ADDR_METHOD == 2) || (GET_MAC_ADDR_METHOD == 3) /* To use macaddr and ps mode of customers */
+int access_file(char *path, char *buffer, int size, int isRead)
+{
+	int ret=0;
+	struct file *fp;
+	mm_segment_t old_fs = get_fs();
+
+	if(isRead)
+		fp = filp_open(path,O_RDONLY,S_IRUSR);
+	else
+		fp = filp_open(path,O_CREAT|O_WRONLY,S_IRUSR);
+
+	if (IS_ERR(fp)) {
+		bes2600_err(BES2600_DBG_INIT, "BES2600 : can't open %s\n",path);
+		return -1;
+	}
+
+	if(isRead)
+	{
+			fp->f_pos = 0;
+			set_fs(KERNEL_DS);
+			ret = vfs_read(fp,buffer,size,&fp->f_pos);
+			set_fs(old_fs);
+	}
+	else
+	{
+			fp->f_pos = 0;
+			set_fs(KERNEL_DS);
+			ret = vfs_write(fp,buffer,size,&fp->f_pos);
+			set_fs(old_fs);
+	}
+	filp_close(fp,NULL);
+
+	bes2600_info(BES2600_DBG_INIT, "BES2600 : access_file return code(%d)\n",ret);
+	return ret;
+}
+#endif
+
+#ifdef CONFIG_BES2600_WLAN_BES
+int bes2600_wifi_start(struct bes2600_common *hw_priv)
+{
+	int ret, if_id;
+#ifndef CONFIG_BES2600_WLAN_USB
+	if(hw_priv->sbus_ops->gpio_wake) {
+		hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_ON);
+	}
+
+	if (hw_priv->sbus_ops->sbus_active &&
+		WARN_ON((ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_WIFI))))
+		goto err;
+#endif
+	if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done,
+			hw_priv->wsm_caps.firmwareReady, 10*HZ) <= 0) {
+
+		/* TODO: Needs to find how to reset device */
+		/*       in QUEUE mode properly.           */
+		bes2600_info(BES2600_DBG_INIT, "startup timeout!!!\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	if (bes2600_chrdev_is_signal_mode()) {
+		for (if_id = 0; if_id < 2; if_id++) {
+			/* Enable multi-TX confirmation */
+			if (WARN_ON((ret = wsm_use_multi_tx_conf(hw_priv, true, if_id)))) {
+				goto err;
+			}
+		}
+	}
+	bes2600_pwr_start(hw_priv);
+
+err:
+	if(hw_priv->sbus_ops->gpio_sleep) {
+		hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_ON);
+	}
+
+	return ret;
+}
+
+int bes2600_wifi_stop(struct bes2600_common *hw_priv)
+{
+	int ret;
+	unsigned long status = 0;
+
+	status = wait_event_timeout(hw_priv->bh_evt_wq, (!hw_priv->hw_bufs_used), 3 * HZ);
+	bes2600_err_with_cond((!status), BES2600_DBG_INIT, "communication exception!");
+
+	if(hw_priv->sbus_ops->gpio_wake) {
+		hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF);
+	}
+
+	bes2600_pwr_stop(hw_priv);
+
+	if (hw_priv->sbus_ops->sbus_deactive &&
+		WARN_ON(ret = hw_priv->sbus_ops->sbus_deactive(hw_priv->sbus_priv, SUBSYSTEM_WIFI))) {
+		goto err;
+	}
+
+	if(hw_priv->sbus_ops->gpio_sleep) {
+		hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF);
+	}
+
+	memset(&hw_priv->wsm_caps, 0, sizeof(hw_priv->wsm_caps));
+	hw_priv->wsm_rx_seq[0] = 0;
+	hw_priv->wsm_rx_seq[1] = 0;
+	hw_priv->wsm_tx_seq[0] = 0;
+	hw_priv->wsm_tx_seq[1] = 0;
+	hw_priv->wsm_tx_pending[0] = 0;
+	hw_priv->wsm_tx_pending[1] = 0;
+	del_timer_sync(&hw_priv->mcu_mon_timer);
+	del_timer_sync(&hw_priv->lmac_mon_timer);
+#ifdef CONFIG_BES2600_STATIC_SDD
+	hw_priv->sdd = NULL;
+#else
+	#error "TO BE CONTINUED: release SDD file"
+#endif
+	return ret;
+
+err:
+	if(hw_priv->sbus_ops->gpio_sleep) {
+		hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF);
+	}
+
+	return ret;
+}
+#endif
diff --git a/drivers/staging/bes2600/pm.c b/drivers/staging/bes2600/pm.c
new file mode 100644
index 000000000000..fbd6a8867332
--- /dev/null
+++ b/drivers/staging/bes2600/pm.c
@@ -0,0 +1,474 @@
+/*
+ * Mac80211 power management API for BES2600 drivers
+ *
+ * Copyright (c) 2011, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/if_ether.h>
+#include "bes2600.h"
+#include "pm.h"
+#include "sta.h"
+#include "bh.h"
+#include "sbus.h"
+#include "bes2600_driver_mode.h"
+#include "bes_chardev.h"
+
+#define BES2600_BEACON_SKIPPING_MULTIPLIER 3
+
+struct bes2600_udp_port_filter {
+	struct wsm_udp_port_filter_hdr hdr;
+	struct wsm_udp_port_filter dhcp;
+	struct wsm_udp_port_filter upnp;
+	struct wsm_udp_port_filter mdns;
+} __packed;
+
+struct bes2600_ether_type_filter {
+	struct wsm_ether_type_filter_hdr hdr;
+	struct wsm_ether_type_filter ip;
+	struct wsm_ether_type_filter pae;
+	struct wsm_ether_type_filter wapi;
+} __packed;
+
+static struct bes2600_udp_port_filter bes2600_udp_port_filter_on = {
+	.hdr.nrFilters = 3,
+	.dhcp = {
+		.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
+		.portType = WSM_FILTER_PORT_TYPE_DST,
+		.udpPort = __cpu_to_le16(67),
+	},
+	.upnp = {
+		.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
+		.portType = WSM_FILTER_PORT_TYPE_DST,
+		.udpPort = __cpu_to_le16(1900),
+	},
+	.mdns = {
+		.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
+		.portType = WSM_FILTER_PORT_TYPE_DST,
+		.udpPort = __cpu_to_le16(5353),
+	},
+	/* Please add other known ports to be filtered out here and
+	 * update nrFilters field in the header.
+	 * Up to 4 filters are allowed. */
+};
+
+static struct wsm_udp_port_filter_hdr bes2600_udp_port_filter_off = {
+	.nrFilters = 0,
+};
+
+#ifndef ETH_P_WAPI
+#define ETH_P_WAPI     0x88B4
+#endif
+
+static struct bes2600_ether_type_filter bes2600_ether_type_filter_on = {
+	.hdr.nrFilters = 3,
+	.ip = {
+		.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
+		.etherType = __cpu_to_le16(ETH_P_IPV6),
+	},
+	.pae = {
+		.filterAction = WSM_FILTER_ACTION_FILTER_IN,
+		.etherType = __cpu_to_le16(ETH_P_PAE),
+	},
+	.wapi = {
+		.filterAction = WSM_FILTER_ACTION_FILTER_IN,
+		.etherType = __cpu_to_le16(ETH_P_WAPI),
+	},
+	/* Please add other known ether types to be filtered out here and
+	 * update nrFilters field in the header.
+	 * Up to 4 filters are allowed. */
+};
+
+static struct wsm_ether_type_filter_hdr bes2600_ether_type_filter_off = {
+	.nrFilters = 0,
+};
+
+static int __bes2600_wow_suspend(struct bes2600_vif *priv,
+				struct cfg80211_wowlan *wowlan);
+static int __bes2600_wow_resume(struct bes2600_vif *priv);
+
+
+/* private */
+struct bes2600_suspend_state {
+	unsigned long bss_loss_tmo;
+	unsigned long connection_loss_tmo;
+	unsigned long join_tmo;
+	unsigned long direct_probe;
+	unsigned long link_id_gc;
+};
+
+static long bes2600_suspend_work(struct delayed_work *work)
+{
+	int ret = cancel_delayed_work(work);
+	long tmo;
+	if (ret > 0) {
+		/* Timer is pending */
+		tmo = work->timer.expires - jiffies;
+		if (tmo < 0)
+			tmo = 0;
+	} else {
+		tmo = -1;
+	}
+	return tmo;
+}
+
+static int bes2600_resume_work(struct bes2600_common *hw_priv,
+			       struct delayed_work *work,
+			       unsigned long tmo)
+{
+	if ((long)tmo < 0)
+		return 1;
+
+	return queue_delayed_work(hw_priv->workqueue, work, tmo);
+}
+
+int bes2600_can_suspend(struct bes2600_common *priv)
+{
+	if (atomic_read(&priv->bh_rx)) {
+		wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
+		return 0;
+	}
+	return 1;
+}
+EXPORT_SYMBOL_GPL(bes2600_can_suspend);
+
+int bes2600_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	struct bes2600_vif *priv;
+	int i, ret = 0;
+	unsigned long begin, end, diff;
+
+	bes2600_info(BES2600_DBG_PM, "bes2600_wow_suspend enter\n");
+
+	WARN_ON(!atomic_read(&hw_priv->num_vifs));
+
+	/* reset wakeup reason to default */
+	bes2600_chrdev_wifi_update_wakeup_reason(0);
+
+#ifdef ROAM_OFFLOAD
+	bes2600_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+		if ((i == (CW12XX_MAX_VIFS - 1)) || !priv)
+#else
+		if (!priv)
+#endif
+			continue;
+		if((priv->vif->type == NL80211_IFTYPE_STATION)
+		&& (priv->join_status == BES2600_JOIN_STATUS_STA)) {
+			down(&hw_priv->scan.lock);
+			hw_priv->scan.if_id = priv->if_id;
+			bes2600_sched_scan_work(&hw_priv->scan.swork);
+		}
+	}
+#endif /*ROAM_OFFLOAD*/
+
+	/* Do not suspend when datapath is not idle */
+	if (hw_priv->tx_queue_stats.num_queued[0]
+			+ hw_priv->tx_queue_stats.num_queued[1])
+		return -EBUSY;
+
+
+	/* Make sure there is no configuration requests in progress. */
+	if (down_trylock(&hw_priv->conf_lock))
+		return -EBUSY;
+
+	/* Do not suspend when scanning or ROC*/
+	if (down_trylock(&hw_priv->scan.lock))
+		goto revert1;
+
+	/* Do not suspend when probe is doing */
+	if (delayed_work_pending(&hw_priv->scan.probe_work))
+		goto revert2;
+
+	/* record suspend start time */
+	begin = jiffies;
+
+	/* wait uitil bes2600 finish current pending operation */
+	if (!bes2600_pwr_device_is_idle(hw_priv)) {
+		/* clear power busy event */
+		bes2600_pwr_set_busy_event_with_timeout(hw_priv, BES_PWR_LOCK_ON_TX, 10);
+
+		/* wait device enter lp mode */
+		if (wait_event_timeout(hw_priv->bes_power.dev_lp_wq,
+			bes2600_pwr_device_is_idle(hw_priv), HZ * 5) <= 0) {
+			bes2600_err(BES2600_DBG_PM, "wait device idle timeout\n");
+			goto revert2;
+		}
+	}
+
+	/* Lock TX. */
+	wsm_lock_tx_async(hw_priv);
+
+	/* mark suspend start to avoid device to exit ps mode when setting device */
+	bes2600_pwr_suspend_start(hw_priv);
+
+	/* set filters and offload based on interface */
+	bes2600_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+		if ((i == (CW12XX_MAX_VIFS - 1)) || !priv)
+#else
+		if (!priv)
+#endif
+			continue;
+
+		ret = __bes2600_wow_suspend(priv,
+						wowlan);
+		if (ret) {
+			for (; i >= 0; i--) {
+				if (!hw_priv->vif_list[i])
+					continue;
+				priv = (struct bes2600_vif *)
+					hw_priv->vif_list[i]->drv_priv;
+				__bes2600_wow_resume(priv);
+			}
+			goto revert3;
+		}
+	}
+
+	/* mark suspend end */
+	bes2600_pwr_suspend_end(hw_priv);
+
+	/* Stop serving thread */
+	if (bes2600_bh_suspend(hw_priv)) {
+		bes2600_err(BES2600_DBG_PM, "%s: bes2600_bh_suspend failed\n",
+				__func__);
+		bes2600_wow_resume(hw);
+		return -EBUSY;
+	}
+
+	/* Force resume if event is coming from the device. */
+	if (atomic_read(&hw_priv->bh_rx)) {
+		bes2600_info(BES2600_DBG_PM, "%s: incoming event present - resume\n",
+				__func__);
+		bes2600_wow_resume(hw);
+		return -EAGAIN;
+	}
+
+	/* calculate the time consumed by bes2600 suspend flow */
+	end = jiffies;
+	diff = end - begin;
+	bes2600_info(BES2600_DBG_PM, "%s consume %d ms\n", __func__, jiffies_to_msecs(diff));
+
+	return 0;
+
+revert3:
+	bes2600_pwr_suspend_end(hw_priv);
+	wsm_unlock_tx(hw_priv);
+revert2:
+	up(&hw_priv->scan.lock);
+revert1:
+	up(&hw_priv->conf_lock);
+	return -EBUSY;
+}
+
+static void bes2600_set_ehter_and_udp_filter(struct bes2600_common *hw_priv,
+				struct wsm_ether_type_filter_hdr *ether_type, struct wsm_udp_port_filter_hdr *udp_type,
+				int if_id)
+{
+	/* Set UDP filter */
+	wsm_set_udp_port_filter(hw_priv, udp_type, if_id);
+
+	/* Set ethernet frame type filter */
+	wsm_set_ether_type_filter(hw_priv, ether_type, if_id);
+}
+
+static int __bes2600_wow_suspend(struct bes2600_vif *priv,
+				struct cfg80211_wowlan *wowlan)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct bes2600_pm_state_vif *pm_state_vif = &priv->pm_state_vif;
+	struct bes2600_suspend_state *state;
+	int ret;
+
+#ifdef MCAST_FWDING
+        struct wsm_forwarding_offload fwdoffload = {
+                .fwenable = 0x1,
+                .flags = 0x1,
+        };
+#endif
+
+	/* Do not suspend when join work is scheduled */
+	if (work_pending(&priv->join_work))
+		goto revert1;
+
+	bes2600_set_ehter_and_udp_filter(hw_priv, &bes2600_ether_type_filter_on.hdr,
+				&bes2600_udp_port_filter_on.hdr, priv->if_id);
+
+  	if (priv->join_status == BES2600_JOIN_STATUS_AP)
+		WARN_ON(wsm_set_keepalive_filter(priv, true));
+
+	/* Set Multicast Address Filter */
+	if (priv->multicast_filter.numOfAddresses) {
+		priv->multicast_filter.enable = __cpu_to_le32(2);
+		wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
+       }
+
+#ifdef MCAST_FWDING
+	if (priv->join_status == BES2600_JOIN_STATUS_AP)
+		WARN_ON(wsm_set_forwarding_offlad(hw_priv,
+				&fwdoffload,priv->if_id));
+#endif
+
+	/* Allocate state */
+	state = kzalloc(sizeof(struct bes2600_suspend_state), GFP_KERNEL);
+	if (!state)
+		goto revert2;
+
+	/* Store delayed work states. */
+	state->bss_loss_tmo =
+		bes2600_suspend_work(&priv->bss_loss_work);
+	state->connection_loss_tmo =
+		bes2600_suspend_work(&priv->connection_loss_work);
+	state->join_tmo =
+		bes2600_suspend_work(&priv->join_timeout);
+	state->link_id_gc =
+		bes2600_suspend_work(&priv->link_id_gc_work);
+
+	ret = timer_pending(&priv->mcast_timeout);
+	if (ret)
+		goto revert3;
+
+	/* Store suspend state */
+	pm_state_vif->suspend_state = state;
+
+	return 0;
+
+revert3:
+	bes2600_resume_work(hw_priv, &priv->bss_loss_work,
+			state->bss_loss_tmo);
+	bes2600_resume_work(hw_priv, &priv->connection_loss_work,
+			state->connection_loss_tmo);
+	bes2600_resume_work(hw_priv, &priv->join_timeout,
+			state->join_tmo);
+	bes2600_resume_work(hw_priv, &priv->link_id_gc_work,
+			state->link_id_gc);
+	kfree(state);
+revert2:
+	wsm_set_udp_port_filter(hw_priv, &bes2600_udp_port_filter_off,
+				priv->if_id);
+	wsm_set_ether_type_filter(hw_priv, &bes2600_ether_type_filter_off,
+				  priv->if_id);
+
+	if (priv->join_status == BES2600_JOIN_STATUS_AP)
+		WARN_ON(wsm_set_keepalive_filter(priv, false));
+
+	/* Set Multicast Address Filter */
+	if (priv->multicast_filter.numOfAddresses) {
+		priv->multicast_filter.enable = __cpu_to_le32(1);
+		wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
+       }
+
+
+#ifdef MCAST_FWDING
+	fwdoffload.flags = 0x0;
+	if (priv->join_status == BES2600_JOIN_STATUS_AP)
+		WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id));
+#endif
+revert1:
+	up(&hw_priv->conf_lock);
+	return -EBUSY;
+}
+
+int bes2600_wow_resume(struct ieee80211_hw *hw)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	struct bes2600_vif *priv;
+	int i, ret = 0;
+
+	bes2600_info(BES2600_DBG_PM, "bes2600_wow_resume enter\n");
+	WARN_ON(!atomic_read(&hw_priv->num_vifs));
+
+	up(&hw_priv->scan.lock);
+
+	/* Resume BH thread */
+	WARN_ON(bes2600_bh_resume(hw_priv));
+
+	/* mark resume start to avoid device to exit ps mode when setting device */
+	bes2600_pwr_resume_start(hw_priv);
+
+	/* set filters and offload based on interface */
+	bes2600_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+		if ((i == (CW12XX_MAX_VIFS - 1)) || !priv)
+#else
+		if (!priv)
+#endif
+			continue;
+		ret = __bes2600_wow_resume(priv);
+		if (ret)
+			break;
+	}
+
+	/* mark resume end */
+	bes2600_pwr_resume_end(hw_priv);
+
+	wsm_unlock_tx(hw_priv);
+	/* Unlock configuration mutex */
+	up(&hw_priv->conf_lock);
+
+	return ret;
+}
+
+static int __bes2600_wow_resume(struct bes2600_vif *priv)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct bes2600_pm_state_vif *pm_state_vif = &priv->pm_state_vif;
+	struct bes2600_suspend_state *state;
+
+#ifdef MCAST_FWDING
+        struct wsm_forwarding_offload fwdoffload = {
+                .fwenable = 0x1,
+                .flags = 0x0,
+        };
+#endif
+	state = pm_state_vif->suspend_state;
+	pm_state_vif->suspend_state = NULL;
+
+#ifdef ROAM_OFFLOAD
+	if((priv->vif->type == NL80211_IFTYPE_STATION)
+	&& (priv->join_status == BES2600_JOIN_STATUS_STA))
+		bes2600_hw_sched_scan_stop(hw_priv);
+#endif /*ROAM_OFFLOAD*/
+
+	if (priv->join_status == BES2600_JOIN_STATUS_AP)
+		WARN_ON(wsm_set_keepalive_filter(priv, false));
+
+	/* Set Multicast Address Filter */
+	if (priv->multicast_filter.numOfAddresses) {
+		priv->multicast_filter.enable = __cpu_to_le32(1);
+		wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
+       }
+
+#ifdef MCAST_FWDING
+	if (priv->join_status == BES2600_JOIN_STATUS_AP)
+		WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id));
+#endif
+
+	/* Resume delayed work */
+	bes2600_resume_work(hw_priv, &priv->bss_loss_work,
+			state->bss_loss_tmo);
+	bes2600_resume_work(hw_priv, &priv->connection_loss_work,
+			state->connection_loss_tmo);
+	bes2600_resume_work(hw_priv, &priv->join_timeout,
+			state->join_tmo);
+	bes2600_resume_work(hw_priv, &priv->link_id_gc_work,
+			state->link_id_gc);
+
+	/* Remove UDP port filter */
+	wsm_set_udp_port_filter(hw_priv, &bes2600_udp_port_filter_off,
+				priv->if_id);
+
+	/* Remove ethernet frame type filter */
+	wsm_set_ether_type_filter(hw_priv, &bes2600_ether_type_filter_off,
+				  priv->if_id);
+	/* Free memory */
+	kfree(state);
+
+	return 0;
+}
diff --git a/drivers/staging/bes2600/pm.h b/drivers/staging/bes2600/pm.h
new file mode 100644
index 000000000000..dbf887367877
--- /dev/null
+++ b/drivers/staging/bes2600/pm.h
@@ -0,0 +1,35 @@
+/*
+ * Mac80211 power management interface for BES2600 mac80211 drivers
+ *
+ * Copyright (c) 2011, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef PM_H_INCLUDED
+#define PM_H_INCLUDED
+
+#ifdef CONFIG_PM
+
+/* extern */  struct bes2600_common;
+/* private */ struct bes2600_suspend_state;
+
+struct bes2600_pm_state_vif {
+	struct bes2600_suspend_state *suspend_state;
+};
+
+int bes2600_can_suspend(struct bes2600_common *priv);
+int bes2600_wow_suspend(struct ieee80211_hw *hw,
+		       struct cfg80211_wowlan *wowlan);
+int bes2600_wow_resume(struct ieee80211_hw *hw);
+#else
+static inline int bes2600_can_suspend(struct bes2600_common *priv)
+{
+	return 0;
+}
+#endif /* CONFIG_PM */
+
+#endif
diff --git a/drivers/staging/bes2600/queue.c b/drivers/staging/bes2600/queue.c
new file mode 100644
index 000000000000..de8edb2a3433
--- /dev/null
+++ b/drivers/staging/bes2600/queue.c
@@ -0,0 +1,931 @@
+/*
+ * O(1) TX queue with built-in allocator for BES2600 drivers
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include "bes2600.h"
+#include "queue.h"
+#include "debug.h"
+#ifdef CONFIG_BES2600_TESTMODE
+#include <linux/time.h>
+#endif /*CONFIG_BES2600_TESTMODE*/
+
+/* private */ struct bes2600_queue_item
+{
+	struct list_head	head;
+	struct sk_buff		*skb;
+	u32			packetID;
+	unsigned long		queue_timestamp;
+	unsigned long		xmit_timestamp;
+#ifdef CONFIG_BES2600_TESTMODE
+	unsigned long		mdelay_timestamp;
+	unsigned long		qdelay_timestamp;
+#endif /*CONFIG_BES2600_TESTMODE*/
+	struct bes2600_txpriv	txpriv;
+	u8			generation;
+};
+
+int bes2600_queue_get_skb_and_timestamp(struct bes2600_queue *queue, u32 packetID,
+			struct sk_buff **skb, struct bes2600_txpriv **txpriv,
+			unsigned long *timestamp)
+{
+	int ret = 0;
+	u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
+	struct bes2600_queue_item *item;
+	bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
+				&item_generation, &item_id, &if_id, &link_id);
+	if (unlikely(item_id >= (u8) queue->capacity))
+		return -EINVAL;
+	item = &queue->pool[item_id];
+	spin_lock_bh(&queue->lock);
+	BUG_ON(queue_id != queue->queue_id);
+	if (unlikely(queue_generation != queue->generation)) {
+		WARN(1, "queue generation mismatch, %u, expect %u, if_id: %u.\n",
+		queue_generation, queue->generation, if_id);
+		ret = -ENOENT;
+	} else if (unlikely(item_generation != item->generation)) {
+		WARN(1, "item generation mismatch, %u, expect %u.\n",
+		item_generation, item->generation);
+		ret = -ENOENT;
+	} else if (unlikely(WARN_ON(!item->skb))) {
+		ret = -ENOENT;
+	} else {
+		*skb = item->skb;
+		*txpriv = &item->txpriv;
+		*timestamp = item->xmit_timestamp;
+	}
+	spin_unlock_bh(&queue->lock);
+	return ret;
+}
+
+
+static inline void __bes2600_queue_lock(struct bes2600_queue *queue)
+{
+	struct bes2600_queue_stats *stats = queue->stats;
+	if (queue->tx_locked_cnt++ == 0) {
+		bes2600_dbg(BES2600_DBG_TXRX, "[TX] Queue %d is locked.\n",
+				queue->queue_id);
+		ieee80211_stop_queue(stats->hw_priv->hw, queue->queue_id);
+	}
+}
+
+static inline void __bes2600_queue_unlock(struct bes2600_queue *queue)
+{
+	struct bes2600_queue_stats *stats = queue->stats;
+	BUG_ON(!queue->tx_locked_cnt);
+	if (--queue->tx_locked_cnt == 0) {
+		bes2600_dbg(BES2600_DBG_TXRX, "[TX] Queue %d is unlocked.\n",
+				queue->queue_id);
+		ieee80211_wake_queue(stats->hw_priv->hw, queue->queue_id);
+	}
+}
+
+
+
+static inline u32 bes2600_queue_make_packet_id(u8 queue_generation, u8 queue_id,
+						u8 item_generation, u8 item_id,
+						u8 if_id, u8 link_id)
+{
+	/*TODO:COMBO: Add interfaceID to the packetID */
+	return ((u32)item_id << 0) |
+		((u32)item_generation << 8) |
+		((u32)queue_id << 16) |
+		((u32)if_id << 20) |
+		((u32)link_id << 24) |
+		((u32)queue_generation << 28);
+}
+
+static void bes2600_queue_post_gc(struct bes2600_queue_stats *stats,
+				 struct list_head *gc_list)
+{
+	struct bes2600_queue_item *item;
+
+	while (!list_empty(gc_list)) {
+		item = list_first_entry(
+			gc_list, struct bes2600_queue_item, head);
+		list_del(&item->head);
+		stats->skb_dtor(stats->hw_priv, item->skb, &item->txpriv);
+		kfree(item);
+	}
+}
+
+static void bes2600_queue_register_post_gc(struct list_head *gc_list,
+				     struct bes2600_queue_item *item)
+{
+	struct bes2600_queue_item *gc_item;
+	gc_item = kmalloc(sizeof(struct bes2600_queue_item),
+			GFP_ATOMIC);
+	BUG_ON(!gc_item);
+	memcpy(gc_item, item, sizeof(struct bes2600_queue_item));
+	list_add_tail(&gc_item->head, gc_list);
+}
+
+static void __bes2600_queue_gc(struct bes2600_queue *queue,
+			      struct list_head *head,
+			      bool unlock)
+{
+	struct bes2600_queue_stats *stats = queue->stats;
+	struct bes2600_queue_item *item = NULL;
+	struct bes2600_vif *priv;
+	int if_id;
+	bool wakeup_stats = false;
+
+	while (!list_empty(&queue->queue)) {
+		struct bes2600_txpriv *txpriv;
+		item = list_first_entry(
+			&queue->queue, struct bes2600_queue_item, head);
+		if (jiffies - item->queue_timestamp < queue->ttl)
+			break;
+
+		txpriv = &item->txpriv;
+		if_id = txpriv->if_id;
+		--queue->num_queued;
+		--queue->num_queued_vif[if_id];
+		--queue->link_map_cache[if_id][txpriv->link_id];
+		spin_lock_bh(&stats->lock);
+		--stats->num_queued[if_id];
+		if (!--stats->link_map_cache[if_id][txpriv->link_id])
+			wakeup_stats = true;
+		spin_unlock_bh(&stats->lock);
+		priv = cw12xx_hwpriv_to_vifpriv(stats->hw_priv, if_id);
+		if (priv) {
+			bes2600_debug_tx_ttl(priv);
+			spin_unlock(&priv->vif_lock);
+		}
+		bes2600_queue_register_post_gc(head, item);
+		item->skb = NULL;
+		list_move_tail(&item->head, &queue->free_pool);
+	}
+
+	if (wakeup_stats)
+		wake_up(&stats->wait_link_id_empty);
+
+	if (queue->overfull) {
+		if (queue->num_queued <= ((stats->hw_priv->vif0_throttle +
+						stats->hw_priv->vif1_throttle + 2)/2)) {
+			queue->overfull = false;
+			if (unlock)
+				__bes2600_queue_unlock(queue);
+		} else if (item) {
+			unsigned long tmo = item->queue_timestamp + queue->ttl;
+			mod_timer(&queue->gc, tmo);
+			bes2600_pwr_set_busy_event_with_timeout_async(stats->hw_priv,
+				BES_PWR_LOCK_ON_QUEUE_GC, jiffies_to_msecs(tmo - jiffies));
+		}
+	}
+}
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+static void bes2600_queue_gc(struct timer_list *t)
+{
+	LIST_HEAD(list);
+	struct bes2600_queue *queue = from_timer(queue, t, gc);
+
+	spin_lock_bh(&queue->lock);
+	__bes2600_queue_gc(queue, &list, true);
+	spin_unlock_bh(&queue->lock);
+	bes2600_queue_post_gc(queue->stats, &list);
+}
+#else
+static void bes2600_queue_gc(unsigned long arg)
+{
+	LIST_HEAD(list);
+	struct bes2600_queue *queue = (struct bes2600_queue *)arg;
+
+	spin_lock_bh(&queue->lock);
+	__bes2600_queue_gc(queue, &list, true);
+	spin_unlock_bh(&queue->lock);
+	bes2600_queue_post_gc(queue->stats, &list);
+}
+#endif
+
+int bes2600_queue_stats_init(struct bes2600_queue_stats *stats,
+			    size_t map_capacity,
+			    bes2600_queue_skb_dtor_t skb_dtor,
+			    struct bes2600_common *hw_priv)
+{
+	int i;
+
+	memset(stats, 0, sizeof(*stats));
+	stats->map_capacity = map_capacity;
+	stats->skb_dtor = skb_dtor;
+	stats->hw_priv = hw_priv;
+	spin_lock_init(&stats->lock);
+	init_waitqueue_head(&stats->wait_link_id_empty);
+	for (i = 0; i < CW12XX_MAX_VIFS; i++) {
+		stats->link_map_cache[i] = kzalloc(map_capacity * sizeof(int),
+			GFP_KERNEL);
+		if (!stats->link_map_cache[i]) {
+			for (; i >= 0; i--)
+				kfree(stats->link_map_cache[i]);
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+int bes2600_queue_init(struct bes2600_queue *queue,
+		      struct bes2600_queue_stats *stats,
+		      u8 queue_id,
+		      size_t capacity,
+		      unsigned long ttl)
+{
+	int i;
+
+	memset(queue, 0, sizeof(*queue));
+	queue->stats = stats;
+	queue->capacity = capacity;
+	queue->queue_id = queue_id;
+	queue->ttl = ttl;
+	INIT_LIST_HEAD(&queue->queue);
+	INIT_LIST_HEAD(&queue->pending);
+	INIT_LIST_HEAD(&queue->free_pool);
+	spin_lock_init(&queue->lock);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+	timer_setup(&queue->gc, bes2600_queue_gc, 0);
+#else
+	setup_timer(&queue->gc, bes2600_queue_gc,(unsigned long)queue);
+#endif
+	queue->pool = kzalloc(sizeof(struct bes2600_queue_item) * capacity,
+			GFP_KERNEL);
+	if (!queue->pool)
+		return -ENOMEM;
+
+	for (i = 0; i < CW12XX_MAX_VIFS; i++) {
+		queue->link_map_cache[i] =
+				kzalloc(stats->map_capacity * sizeof(int),
+			GFP_KERNEL);
+		if (!queue->link_map_cache[i]) {
+			for (; i >= 0; i--)
+				kfree(queue->link_map_cache[i]);
+			kfree(queue->pool);
+			queue->pool = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	for (i = 0; i < capacity; ++i)
+		list_add_tail(&queue->pool[i].head, &queue->free_pool);
+
+	return 0;
+}
+
+/* TODO:COMBO: Flush only a particular interface specific parts */
+int bes2600_queue_clear(struct bes2600_queue *queue, int if_id)
+{
+	int i, cnt, iter;
+	struct bes2600_queue_stats *stats = queue->stats;
+	LIST_HEAD(gc_list);
+
+	cnt = 0;
+	spin_lock_bh(&queue->lock);
+	queue->generation++;
+	queue->generation &= 0xf;
+	list_splice_tail_init(&queue->queue, &queue->pending);
+	while (!list_empty(&queue->pending)) {
+		struct bes2600_queue_item *item = list_first_entry(
+			&queue->pending, struct bes2600_queue_item, head);
+		WARN_ON(!item->skb);
+		if (CW12XX_ALL_IFS == if_id || item->txpriv.if_id == if_id) {
+			bes2600_queue_register_post_gc(&gc_list, item);
+			item->skb = NULL;
+			list_move_tail(&item->head, &queue->free_pool);
+			cnt++;
+		}
+	}
+	queue->num_queued -= cnt;
+	queue->num_pending -= cnt;
+	if (CW12XX_ALL_IFS != if_id) {
+		queue->num_queued_vif[if_id] = 0;
+		queue->num_pending_vif[if_id] = 0;
+	} else {
+		for (iter = 0; iter < CW12XX_MAX_VIFS; iter++) {
+			queue->num_queued_vif[iter] = 0;
+			queue->num_pending_vif[iter] = 0;
+		}
+	}
+	spin_lock_bh(&stats->lock);
+	if (CW12XX_ALL_IFS != if_id) {
+		for (i = 0; i < stats->map_capacity; ++i) {
+			stats->num_queued[if_id] -=
+				queue->link_map_cache[if_id][i];
+			stats->link_map_cache[if_id][i] -=
+				queue->link_map_cache[if_id][i];
+			queue->link_map_cache[if_id][i] = 0;
+		}
+	} else {
+		for (iter = 0; iter < CW12XX_MAX_VIFS; iter++) {
+			for (i = 0; i < stats->map_capacity; ++i) {
+				stats->num_queued[iter] -=
+					queue->link_map_cache[iter][i];
+				stats->link_map_cache[iter][i] -=
+					queue->link_map_cache[iter][i];
+				queue->link_map_cache[iter][i] = 0;
+			}
+		}
+	}
+	spin_unlock_bh(&stats->lock);
+	if (unlikely(queue->overfull)) {
+		queue->overfull = false;
+		__bes2600_queue_unlock(queue);
+	}
+	spin_unlock_bh(&queue->lock);
+	wake_up(&stats->wait_link_id_empty);
+	bes2600_queue_post_gc(stats, &gc_list);
+	return 0;
+}
+
+void bes2600_queue_stats_deinit(struct bes2600_queue_stats *stats)
+{
+	int i;
+
+	for (i = 0; i < CW12XX_MAX_VIFS ; i++) {
+		kfree(stats->link_map_cache[i]);
+		stats->link_map_cache[i] = NULL;
+	}
+}
+
+void bes2600_queue_deinit(struct bes2600_queue *queue)
+{
+	int i;
+
+	bes2600_queue_clear(queue, CW12XX_ALL_IFS);
+	del_timer_sync(&queue->gc);
+	INIT_LIST_HEAD(&queue->free_pool);
+	kfree(queue->pool);
+	for (i = 0; i < CW12XX_MAX_VIFS; i++) {
+		kfree(queue->link_map_cache[i]);
+		queue->link_map_cache[i] = NULL;
+	}
+	queue->pool = NULL;
+	queue->capacity = 0;
+}
+
+size_t bes2600_queue_get_num_queued(struct bes2600_vif *priv,
+				   struct bes2600_queue *queue,
+				   u32 link_id_map)
+{
+	size_t ret;
+	int i, bit;
+	size_t map_capacity = queue->stats->map_capacity;
+
+	if (!link_id_map)
+		return 0;
+
+	spin_lock_bh(&queue->lock);
+	if (likely(link_id_map == (u32) -1)) {
+		ret = queue->num_queued_vif[priv->if_id] -
+			queue->num_pending_vif[priv->if_id];
+	} else {
+		ret = 0;
+		for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
+			if (link_id_map & bit)
+				ret +=
+				queue->link_map_cache[priv->if_id][i];
+		}
+	}
+	spin_unlock_bh(&queue->lock);
+	return ret;
+}
+
+int bes2600_queue_put(struct bes2600_queue *queue,
+		     struct sk_buff *skb,
+		     struct bes2600_txpriv *txpriv)
+{
+	int ret = 0;
+#ifdef CONFIG_BES2600_TESTMODE
+	struct timeval tmval;
+#endif /*CONFIG_BES2600_TESTMODE*/
+
+	LIST_HEAD(gc_list);
+	struct bes2600_queue_stats *stats = queue->stats;
+	/* TODO:COMBO: Add interface ID info to queue item */
+
+	if (txpriv->link_id >= queue->stats->map_capacity)
+		return -EINVAL;
+
+	spin_lock_bh(&queue->lock);
+	if (!WARN_ON(list_empty(&queue->free_pool))) {
+		struct bes2600_queue_item *item = list_first_entry(
+			&queue->free_pool, struct bes2600_queue_item, head);
+		BUG_ON(item->skb);
+
+		list_move_tail(&item->head, &queue->queue);
+		item->skb = skb;
+		item->txpriv = *txpriv;
+		item->generation = 0;
+		item->packetID = bes2600_queue_make_packet_id(
+			queue->generation, queue->queue_id,
+			item->generation, item - queue->pool,
+			txpriv->if_id, txpriv->raw_link_id);
+		item->queue_timestamp = jiffies;
+#ifdef BES2600_HOST_TIMESTAMP_DEBUG
+		if (skb_tailroom(skb) >= 4) {
+			u32 *extra_data;
+			extra_data = (u32 *)skb_tail_pointer(skb);
+			*extra_data = (u32)jiffies_to_msecs(item->queue_timestamp);
+		}
+#endif
+#ifdef CONFIG_BES2600_TESTMODE
+		do_gettimeofday(&tmval);
+		item->qdelay_timestamp = tmval.tv_usec;
+#endif /*CONFIG_BES2600_TESTMODE*/
+
+		++queue->num_queued;
+		++queue->num_queued_vif[txpriv->if_id];
+		++queue->link_map_cache[txpriv->if_id][txpriv->link_id];
+
+		spin_lock_bh(&stats->lock);
+		++stats->num_queued[txpriv->if_id];
+		++stats->link_map_cache[txpriv->if_id][txpriv->link_id];
+		spin_unlock_bh(&stats->lock);
+
+		/*
+		 * TX may happen in parallel sometimes.
+		 * Leave extra queue slots so we don't overflow.
+		 */
+		if (queue->overfull == false &&
+				queue->num_queued >=
+		((stats->hw_priv->vif0_throttle +
+			stats->hw_priv->vif1_throttle + 2)
+				- (num_present_cpus() - 1))) {
+			queue->overfull = true;
+			__bes2600_queue_lock(queue);
+			mod_timer(&queue->gc, jiffies);
+		}
+	} else {
+		ret = -ENOENT;
+	}
+#if 0
+	bes2600_dbg(BES2600_DBG_TXRX, "queue_put queue %d, %d, %d\n",
+		queue->num_queued,
+		queue->link_map_cache[txpriv->if_id][txpriv->link_id],
+		queue->num_pending);
+	bes2600_dbg(BES2600_DBG_TXRX, "queue_put stats %d, %d\n", stats->num_queued,
+		stats->link_map_cache[txpriv->if_id][txpriv->link_id]);
+#endif
+	spin_unlock_bh(&queue->lock);
+	return ret;
+}
+
+int bes2600_queue_get(struct bes2600_queue *queue,
+			int if_id,
+		     u32 link_id_map,
+		     struct wsm_tx **tx,
+		     struct ieee80211_tx_info **tx_info,
+		     struct bes2600_txpriv **txpriv)
+{
+	int ret = -ENOENT;
+	struct bes2600_queue_item *item = NULL;
+	struct bes2600_queue_stats *stats = queue->stats;
+	bool wakeup_stats = false;
+#ifdef CONFIG_BES2600_TESTMODE
+	struct timeval tmval;
+#endif /*CONFIG_BES2600_TESTMODE*/
+
+	spin_lock_bh(&queue->lock);
+	list_for_each_entry(item, &queue->queue, head) {
+		if ((item->txpriv.if_id == if_id) &&
+			(link_id_map & BIT(item->txpriv.link_id))) {
+			ret = 0;
+			break;
+		}
+	}
+
+	if (!ret) {
+		*tx = (struct wsm_tx *)item->skb->data;
+		*tx_info = IEEE80211_SKB_CB(item->skb);
+		*txpriv = &item->txpriv;
+		(*tx)->packetID = __cpu_to_le32(item->packetID);
+		list_move_tail(&item->head, &queue->pending);
+		++queue->num_pending;
+		++queue->num_pending_vif[item->txpriv.if_id];
+		--queue->link_map_cache[item->txpriv.if_id]
+				[item->txpriv.link_id];
+		item->xmit_timestamp = jiffies;
+#ifdef CONFIG_BES2600_TESTMODE
+		do_gettimeofday(&tmval);
+		item->mdelay_timestamp = tmval.tv_usec;
+#endif /*CONFIG_BES2600_TESTMODE*/
+
+		spin_lock_bh(&stats->lock);
+		--stats->num_queued[item->txpriv.if_id];
+		if (!--stats->link_map_cache[item->txpriv.if_id]
+					[item->txpriv.link_id])
+			wakeup_stats = true;
+
+		spin_unlock_bh(&stats->lock);
+#if 0
+		bes2600_dbg(BES2600_DBG_TXRX, "queue_get queue %d, %d, %d\n",
+		queue->num_queued,
+		queue->link_map_cache[item->txpriv.if_id][item->txpriv.link_id],
+		queue->num_pending);
+		bes2600_dbg(BES2600_DBG_TXRX, "queue_get stats %d, %d\n", stats->num_queued,
+		stats->link_map_cache[item->txpriv.if_id]
+		[item->txpriv.link_id]);
+#endif
+	} else {
+		bes2600_warn(BES2600_DBG_TXRX, "%s: queue get failed, if_id = %d, link_id_map = %u\n", __func__, if_id, link_id_map);
+	}
+
+	spin_unlock_bh(&queue->lock);
+	if (wakeup_stats)
+		wake_up(&stats->wait_link_id_empty);
+	return ret;
+}
+
+#ifdef CONFIG_BES2600_TESTMODE
+int bes2600_queue_requeue(struct bes2600_common *hw_priv,
+	struct bes2600_queue *queue, u32 packetID, bool check)
+#else
+int bes2600_queue_requeue(struct bes2600_queue *queue, u32 packetID, bool check)
+#endif
+{
+	int ret = 0;
+	u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
+	struct bes2600_queue_item *item;
+	struct bes2600_queue_stats *stats = queue->stats;
+
+	bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
+				&item_generation, &item_id, &if_id, &link_id);
+
+	item = &queue->pool[item_id];
+#ifdef P2P_MULTIVIF
+	if (check && item->txpriv.if_id == CW12XX_GENERIC_IF_ID) {
+#else
+	if (check && item->txpriv.offchannel_if_id == CW12XX_GENERIC_IF_ID) {
+#endif
+		bes2600_dbg(BES2600_DBG_TXRX, "Requeued frame dropped for "
+						"generic interface id.\n");
+#ifdef CONFIG_BES2600_TESTMODE
+		bes2600_queue_remove(hw_priv, queue, packetID);
+#else
+		bes2600_queue_remove(queue, packetID);
+#endif
+		return 0;
+	}
+
+#ifndef P2P_MULTIVIF
+	if (!check)
+		item->txpriv.offchannel_if_id = CW12XX_GENERIC_IF_ID;
+#endif
+
+	/*if_id = item->txpriv.if_id;*/
+
+	spin_lock_bh(&queue->lock);
+	BUG_ON(queue_id != queue->queue_id);
+	if (unlikely(queue_generation != queue->generation)) {
+		bes2600_info(BES2600_DBG_TXRX, "%s, Queue Generation is not equal\n", __func__);
+		ret = 0;
+	} else if (unlikely(item_id >= (unsigned) queue->capacity)) {
+		WARN_ON(1);
+		ret = -EINVAL;
+	} else if (unlikely(item->generation != item_generation)) {
+		WARN_ON(1);
+		ret = -ENOENT;
+	} else {
+		--queue->num_pending;
+		--queue->num_pending_vif[if_id];
+		++queue->link_map_cache[if_id][item->txpriv.link_id];
+
+		spin_lock_bh(&stats->lock);
+		++stats->num_queued[item->txpriv.if_id];
+		++stats->link_map_cache[if_id][item->txpriv.link_id];
+		spin_unlock_bh(&stats->lock);
+
+		item->generation = ++item_generation;
+		item->packetID = bes2600_queue_make_packet_id(
+			queue_generation, queue_id, item_generation, item_id,
+			if_id, link_id);
+		list_move(&item->head, &queue->queue);
+#if 0
+		bes2600_dbg(BES2600_DBG_TXRX, "queue_requeue queue %d, %d, %d\n",
+		queue->num_queued,
+		queue->link_map_cache[if_id][item->txpriv.link_id],
+		queue->num_pending);
+		bes2600_dbg(BES2600_DBG_TXRX, "queue_requeue stats %d, %d\n",
+		stats->num_queued,
+		stats->link_map_cache[if_id][item->txpriv.link_id]);
+#endif
+	}
+	spin_unlock_bh(&queue->lock);
+	return ret;
+}
+
+int bes2600_sw_retry_requeue(struct bes2600_common *hw_priv,
+	struct bes2600_queue *queue, u32 packetID, bool check)
+{
+	int ret = 0;
+	u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
+	struct bes2600_queue_item *item;
+	struct bes2600_queue_stats *stats = queue->stats;
+	bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
+				&item_generation, &item_id, &if_id, &link_id);
+	item = &queue->pool[item_id];
+#ifdef P2P_MULTIVIF
+	if (check && item->txpriv.if_id == CW12XX_GENERIC_IF_ID) {
+#else
+	if (check && item->txpriv.offchannel_if_id == CW12XX_GENERIC_IF_ID) {
+#endif
+		bes2600_dbg(BES2600_DBG_TXRX, "Requeued frame dropped for "
+						"generic interface id.\n");
+#ifdef CONFIG_BES2600_TESTMODE
+		bes2600_queue_remove(hw_priv, queue, packetID);
+#else
+		bes2600_queue_remove(queue, packetID);
+#endif
+		return 0;
+	}
+
+#ifndef P2P_MULTIVIF
+	if (!check)
+		item->txpriv.offchannel_if_id = CW12XX_GENERIC_IF_ID;
+#endif
+	/*if_id = item->txpriv.if_id;*/
+	spin_lock_bh(&queue->lock);
+	BUG_ON(queue_id != queue->queue_id);
+	if (unlikely(queue_generation != queue->generation)) {
+		bes2600_info(BES2600_DBG_TXRX, "%s, Queue Generation is not equal\n", __func__);
+		ret = 0;
+	} else if (unlikely(item_id >= (unsigned) queue->capacity)) {
+		WARN_ON(1);
+		ret = -EINVAL;
+	} else if (unlikely(item->generation != item_generation)) {
+		WARN_ON(1);
+		ret = -ENOENT;
+	} else {
+		--queue->num_pending;
+		--queue->num_pending_vif[if_id];
+		++queue->link_map_cache[if_id][item->txpriv.link_id];
+		spin_lock_bh(&stats->lock);
+		++stats->num_queued[item->txpriv.if_id];
+		++stats->link_map_cache[if_id][item->txpriv.link_id];
+		spin_unlock_bh(&stats->lock);
+		item->packetID = bes2600_queue_make_packet_id(
+			queue_generation, queue_id, item_generation, item_id,
+			if_id, link_id);
+
+		list_move(&item->head, &queue->queue);
+
+	}
+
+	spin_unlock_bh(&queue->lock);
+	return ret;
+}
+
+
+int bes2600_queue_requeue_all(struct bes2600_queue *queue)
+{
+	struct bes2600_queue_stats *stats = queue->stats;
+	spin_lock_bh(&queue->lock);
+	while (!list_empty(&queue->pending)) {
+		struct bes2600_queue_item *item = list_entry(
+			queue->pending.prev, struct bes2600_queue_item, head);
+
+		--queue->num_pending;
+		--queue->num_pending_vif[item->txpriv.if_id];
+		++queue->link_map_cache[item->txpriv.if_id]
+				[item->txpriv.link_id];
+
+		spin_lock_bh(&stats->lock);
+		++stats->num_queued[item->txpriv.if_id];
+		++stats->link_map_cache[item->txpriv.if_id]
+				[item->txpriv.link_id];
+		spin_unlock_bh(&stats->lock);
+
+		++item->generation;
+		item->packetID = bes2600_queue_make_packet_id(
+			queue->generation, queue->queue_id,
+			item->generation, item - queue->pool,
+			item->txpriv.if_id, item->txpriv.raw_link_id);
+		list_move(&item->head, &queue->queue);
+	}
+	spin_unlock_bh(&queue->lock);
+
+	return 0;
+}
+#ifdef CONFIG_BES2600_TESTMODE
+int bes2600_queue_remove(struct bes2600_common *hw_priv,
+				struct bes2600_queue *queue, u32 packetID)
+#else
+int bes2600_queue_remove(struct bes2600_queue *queue, u32 packetID)
+#endif /*CONFIG_BES2600_TESTMODE*/
+{
+	int ret = 0;
+	u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
+	struct bes2600_queue_item *item;
+	struct bes2600_queue_stats *stats = queue->stats;
+	struct sk_buff *gc_skb = NULL;
+	struct bes2600_txpriv gc_txpriv;
+
+	bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
+				&item_generation, &item_id, &if_id, &link_id);
+
+	item = &queue->pool[item_id];
+
+	spin_lock_bh(&queue->lock);
+	BUG_ON(queue_id != queue->queue_id);
+	/*TODO:COMBO:Add check for interface ID also */
+	if (unlikely(queue_generation != queue->generation)) {
+		bes2600_info(BES2600_DBG_TXRX, "%s, Queue Generation is not equal\n", __func__);
+		ret = 0;
+	} else if (unlikely(item_id >= (unsigned) queue->capacity)) {
+		WARN_ON(1);
+		ret = -EINVAL;
+	} else if (unlikely(item->generation != item_generation)) {
+		WARN_ON(1);
+		ret = -ENOENT;
+	} else {
+		gc_txpriv = item->txpriv;
+		gc_skb = item->skb;
+		item->skb = NULL;
+		--queue->num_pending;
+		--queue->num_pending_vif[if_id];
+		--queue->num_queued;
+		--queue->num_queued_vif[if_id];
+		++queue->num_sent;
+		++item->generation;
+#ifdef CONFIG_BES2600_TESTMODE
+		spin_lock_bh(&hw_priv->tsm_lock);
+		if (hw_priv->start_stop_tsm.start) {
+			if (queue_id == hw_priv->tsm_info.ac) {
+				struct timeval tmval;
+				unsigned long queue_delay;
+				unsigned long media_delay;
+				do_gettimeofday(&tmval);
+
+				if (tmval.tv_usec > item->qdelay_timestamp)
+					queue_delay = tmval.tv_usec -
+						item->qdelay_timestamp;
+				else
+					queue_delay = tmval.tv_usec +
+					1000000 - item->qdelay_timestamp;
+
+				if (tmval.tv_usec > item->mdelay_timestamp)
+					media_delay = tmval.tv_usec -
+						item->mdelay_timestamp;
+				else
+					media_delay = tmval.tv_usec +
+					1000000 - item->mdelay_timestamp;
+				hw_priv->tsm_info.sum_media_delay +=
+							media_delay;
+				hw_priv->tsm_info.sum_pkt_q_delay += queue_delay;
+				if (queue_delay <= 10000)
+					hw_priv->tsm_stats.bin0++;
+				else if (queue_delay <= 20000)
+					hw_priv->tsm_stats.bin1++;
+				else if (queue_delay <= 40000)
+					hw_priv->tsm_stats.bin2++;
+				else
+					hw_priv->tsm_stats.bin3++;
+			}
+		}
+		spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_BES2600_TESTMODE*/
+		/* Do not use list_move_tail here, but list_move:
+		 * try to utilize cache row.
+		 */
+		list_move(&item->head, &queue->free_pool);
+
+		if (unlikely(queue->overfull) &&
+		    (queue->num_queued <= ((stats->hw_priv->vif0_throttle + stats->hw_priv->vif1_throttle + 2) / 2))) {
+			queue->overfull = false;
+			__bes2600_queue_unlock(queue);
+		}
+	}
+	spin_unlock_bh(&queue->lock);
+
+#if 0
+	bes2600_dbg(BES2600_DBG_TXRX, "queue_drop queue %d, %d, %d\n",
+		queue->num_queued, queue->link_map_cache[if_id][0],
+		queue->num_pending);
+	bes2600_dbg(BES2600_DBG_TXRX, "queue_drop stats %d, %d\n", stats->num_queued,
+		stats->link_map_cache[if_id][0]);
+#endif
+	if (gc_skb)
+		stats->skb_dtor(stats->hw_priv, gc_skb, &gc_txpriv);
+
+	return ret;
+}
+
+int bes2600_queue_get_skb(struct bes2600_queue *queue, u32 packetID,
+			 struct sk_buff **skb,
+			 const struct bes2600_txpriv **txpriv)
+{
+	int ret = 0;
+	u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
+	struct bes2600_queue_item *item;
+
+	bes2600_queue_parse_id(packetID, &queue_generation, &queue_id,
+				&item_generation, &item_id, &if_id, &link_id);
+
+	item = &queue->pool[item_id];
+
+	spin_lock_bh(&queue->lock);
+	BUG_ON(queue_id != queue->queue_id);
+	/* TODO:COMBO: Add check for interface ID here */
+	if (unlikely(queue_generation != queue->generation)) {
+		bes2600_info(BES2600_DBG_TXRX, "%s, Queue Generation is not equal\n", __func__);
+		ret = -EINVAL;
+	} else if (unlikely(item_id >= (unsigned) queue->capacity)) {
+		WARN_ON(1);
+		ret = -EINVAL;
+	} else if (unlikely(item->generation != item_generation)) {
+		bes2600_info(BES2600_DBG_TXRX, "%s, item_generation =%u, item_id =%u link_id=%u queue_generation =%u packetID =%u\n",
+			__func__, item_generation, item_id, link_id, queue_generation, packetID);
+		WARN_ON(1);
+		ret = -ENOENT;
+	} else {
+		*skb = item->skb;
+		*txpriv = &item->txpriv;
+	}
+	spin_unlock_bh(&queue->lock);
+	return ret;
+}
+
+void bes2600_queue_lock(struct bes2600_queue *queue)
+{
+	spin_lock_bh(&queue->lock);
+	__bes2600_queue_lock(queue);
+	spin_unlock_bh(&queue->lock);
+}
+
+void bes2600_queue_unlock(struct bes2600_queue *queue)
+{
+	spin_lock_bh(&queue->lock);
+	__bes2600_queue_unlock(queue);
+	spin_unlock_bh(&queue->lock);
+}
+
+bool bes2600_queue_get_xmit_timestamp(struct bes2600_queue *queue,
+				     unsigned long *timestamp, int if_id,
+				     u32 pending_frameID)
+{
+	struct bes2600_queue_item *item;
+	bool ret;
+
+	spin_lock_bh(&queue->lock);
+	ret = !list_empty(&queue->pending);
+	if (ret) {
+		list_for_each_entry(item, &queue->pending, head) {
+			if (((if_id == CW12XX_GENERIC_IF_ID) ||
+				(if_id == CW12XX_ALL_IFS) ||
+					(item->txpriv.if_id == if_id)) &&
+					(item->packetID != pending_frameID)) {
+				if (time_before(item->xmit_timestamp,
+							*timestamp))
+					*timestamp = item->xmit_timestamp;
+			}
+		}
+	}
+	spin_unlock_bh(&queue->lock);
+	return ret;
+}
+
+bool bes2600_queue_stats_is_empty(struct bes2600_queue_stats *stats,
+				 u32 link_id_map, int if_id)
+{
+	bool empty = true;
+
+	spin_lock_bh(&stats->lock);
+	if (link_id_map == (u32)-1)
+		empty = stats->num_queued[if_id] == 0;
+	else {
+		int i, if_id;
+		for (if_id = 0; if_id < CW12XX_MAX_VIFS; if_id++) {
+			for (i = 0; i < stats->map_capacity; ++i) {
+				if (link_id_map & BIT(i)) {
+					if (stats->link_map_cache[if_id][i]) {
+						empty = false;
+						break;
+					}
+				}
+			}
+		}
+	}
+	spin_unlock_bh(&stats->lock);
+
+	return empty;
+}
+
+void bes2600_queue_iterate_pending_packet(struct bes2600_queue *queue,
+	void (*iterate_cb)(struct bes2600_common *hw_priv, struct sk_buff *skb))
+{
+	struct bes2600_queue_item *item = NULL;
+
+	if(list_empty(&queue->pending))
+		return;
+
+	list_for_each_entry(item, &queue->pending, head) {
+		iterate_cb(queue->stats->hw_priv, item->skb);
+	}
+}
\ No newline at end of file
diff --git a/drivers/staging/bes2600/queue.h b/drivers/staging/bes2600/queue.h
new file mode 100644
index 000000000000..803ceacff178
--- /dev/null
+++ b/drivers/staging/bes2600/queue.h
@@ -0,0 +1,173 @@
+/*
+ * O(1) TX queue with built-in allocator for BES2600 drivers
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES2600_QUEUE_H_INCLUDED
+#define BES2600_QUEUE_H_INCLUDED
+
+/* private */ struct bes2600_queue_item;
+
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct bes2600_common;
+/* extern */ struct bes2600_vif;
+/* extern */ struct ieee80211_tx_queue_stats;
+/* extern */ struct bes2600_txpriv;
+
+/* forward */ struct bes2600_queue_stats;
+
+typedef void (*bes2600_queue_skb_dtor_t)(struct bes2600_common *priv,
+					struct sk_buff *skb,
+					const struct bes2600_txpriv *txpriv);
+
+struct bes2600_queue {
+	struct bes2600_queue_stats *stats;
+	size_t			capacity;
+	size_t			num_queued;
+	size_t			num_queued_vif[CW12XX_MAX_VIFS];
+	size_t			num_pending;
+	size_t			num_pending_vif[CW12XX_MAX_VIFS];
+	size_t			num_sent;
+	struct bes2600_queue_item *pool;
+	struct list_head	queue;
+	struct list_head	free_pool;
+	struct list_head	pending;
+	int			tx_locked_cnt;
+	int			*link_map_cache[CW12XX_MAX_VIFS];
+	bool			overfull;
+	spinlock_t		lock;
+	u8			queue_id;
+	u8			generation;
+	struct timer_list	gc;
+	unsigned long		ttl;
+};
+
+struct bes2600_queue_stats {
+	spinlock_t		lock;
+	int			*link_map_cache[CW12XX_MAX_VIFS];
+	int			num_queued[CW12XX_MAX_VIFS];
+	size_t			map_capacity;
+	wait_queue_head_t	wait_link_id_empty;
+	bes2600_queue_skb_dtor_t	skb_dtor;
+	struct bes2600_common	*hw_priv;
+};
+
+struct bes2600_txpriv {
+	u8 link_id;
+	u8 raw_link_id;
+	u8 tid;
+	u8 rate_id;
+	u8 offset;
+	u8 if_id;
+#ifndef P2P_MULTIVIF
+	u8 offchannel_if_id;
+#else
+	u8 raw_if_id;
+#endif
+	u8 retry_count;
+};
+
+int bes2600_queue_stats_init(struct bes2600_queue_stats *stats,
+			    size_t map_capacity,
+			    bes2600_queue_skb_dtor_t skb_dtor,
+			    struct bes2600_common *priv);
+int bes2600_queue_init(struct bes2600_queue *queue,
+		      struct bes2600_queue_stats *stats,
+		      u8 queue_id,
+		      size_t capacity,
+		      unsigned long ttl);
+int bes2600_queue_clear(struct bes2600_queue *queue, int if_id);
+void bes2600_queue_stats_deinit(struct bes2600_queue_stats *stats);
+void bes2600_queue_deinit(struct bes2600_queue *queue);
+
+size_t bes2600_queue_get_num_queued(struct bes2600_vif *priv,
+				   struct bes2600_queue *queue,
+				   u32 link_id_map);
+int bes2600_queue_put(struct bes2600_queue *queue,
+		     struct sk_buff *skb,
+		     struct bes2600_txpriv *txpriv);
+int bes2600_queue_get(struct bes2600_queue *queue,
+			int if_id,
+		     u32 link_id_map,
+		     struct wsm_tx **tx,
+		     struct ieee80211_tx_info **tx_info,
+		     struct bes2600_txpriv **txpriv);
+#ifdef CONFIG_BES2600_TESTMODE
+int bes2600_queue_requeue(struct bes2600_common *hw_priv,
+			struct bes2600_queue *queue,
+			u32 packetID, bool check);
+#else
+int bes2600_queue_requeue(struct bes2600_queue *queue, u32 packetID, bool check);
+#endif
+int bes2600_queue_requeue_all(struct bes2600_queue *queue);
+#ifdef CONFIG_BES2600_TESTMODE
+int bes2600_queue_remove(struct bes2600_common *hw_priv,
+			struct bes2600_queue *queue,
+			u32 packetID);
+#else
+int bes2600_queue_remove(struct bes2600_queue *queue,
+			u32 packetID);
+#endif /*CONFIG_BES2600_TESTMODE*/
+int bes2600_queue_get_skb(struct bes2600_queue *queue, u32 packetID,
+			 struct sk_buff **skb,
+			 const struct bes2600_txpriv **txpriv);
+void bes2600_queue_lock(struct bes2600_queue *queue);
+void bes2600_queue_unlock(struct bes2600_queue *queue);
+bool bes2600_queue_get_xmit_timestamp(struct bes2600_queue *queue,
+				     unsigned long *timestamp, int if_id,
+				     u32 pending_frameID);
+
+
+bool bes2600_queue_stats_is_empty(struct bes2600_queue_stats *stats,
+				 u32 link_id_map, int if_id);
+
+static inline void bes2600_queue_parse_id(u32 packetID, u8 *queue_generation,
+        u8 *queue_id,
+         u8 *item_generation,
+        u8 *item_id,
+        u8 *if_id,
+        u8 *link_id)
+{
+    *item_id        = (packetID >>  0) & 0xFF;
+    *item_generation    = (packetID >>  8) & 0xFF;
+    *queue_id       = (packetID >> 16) & 0xF;
+    *if_id          = (packetID >> 20) & 0xF;
+    *link_id        = (packetID >> 24) & 0xF;
+    *queue_generation   = (packetID >> 28) & 0xF;
+}
+static inline u8 bes2600_queue_get_queue_id(u32 packetID)
+{
+	return (packetID >> 16) & 0xF;
+}
+
+static inline u8 bes2600_queue_get_if_id(u32 packetID)
+{
+	return (packetID >> 20) & 0xF;
+}
+
+static inline u8 bes2600_queue_get_link_id(u32 packetID)
+{
+	return (packetID >> 24) & 0xF;
+}
+
+static inline u8 bes2600_queue_get_generation(u32 packetID)
+{
+	return (packetID >>  8) & 0xFF;
+}
+
+int bes2600_queue_get_skb_and_timestamp(struct bes2600_queue *queue, u32 packetID,
+			struct sk_buff **skb, struct bes2600_txpriv **txpriv,
+			unsigned long *timestamp);
+int bes2600_sw_retry_requeue(struct bes2600_common *hw_priv,
+	struct bes2600_queue *queue, u32 packetID, bool check);
+void bes2600_queue_iterate_pending_packet(struct bes2600_queue *queue,
+	void (*iterate_cb)(struct bes2600_common *hw_priv, struct sk_buff *skb));
+
+#endif /* BES2600_QUEUE_H_INCLUDED */
diff --git a/drivers/staging/bes2600/sbus.h b/drivers/staging/bes2600/sbus.h
new file mode 100644
index 000000000000..d14c76698c60
--- /dev/null
+++ b/drivers/staging/bes2600/sbus.h
@@ -0,0 +1,105 @@
+/*
+ * Common sbus abstraction layer interface for bes2600 wireless driver
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES2600_SBUS_H
+#define BES2600_SBUS_H
+
+/*
+ * sbus priv forward definition.
+ * Implemented and instantiated in particular modules.
+ */
+struct sbus_priv;
+struct bes2600_common;
+
+typedef void (*sbus_irq_handler)(void *priv);
+
+enum SUBSYSTEM {
+	SUBSYSTEM_MCU = 0,
+	SUBSYSTEM_WIFI,
+	SUBSYSTEM_BT,
+	SUBSYSTEM_BT_LP,
+};
+
+enum GPIO_WAKE_FLAG
+{
+	GPIO_WAKE_FLAG_MCU = 0,
+	GPIO_WAKE_FLAG_WIFI_ON,
+	GPIO_WAKE_FLAG_WIFI_OFF,
+	GPIO_WAKE_FLAG_BT_ON,
+	GPIO_WAKE_FLAG_BT_OFF,
+	GPIO_WAKE_FLAG_BT_LP_ON,
+	GPIO_WAKE_FLAG_BT_LP_OFF,
+	GPIO_WAKE_FLAG_HOST_SUSPEND,
+	GPIO_WAKE_FLAG_HOST_RESUME,
+	GPIO_WAKE_FLAG_SDIO_RX,
+	GPIO_WAKE_FLAG_SDIO_PROBE,
+};
+
+struct sbus_ops {
+	int (*init)(struct sbus_priv *self, struct bes2600_common *ar);
+	int (*sbus_memcpy_fromio)(struct sbus_priv *self, unsigned int addr,
+					void *dst, int count);
+	int (*sbus_memcpy_toio)(struct sbus_priv *self, unsigned int addr,
+					const void *src, int count);
+	void (*lock)(struct sbus_priv *self);
+	void (*unlock)(struct sbus_priv *self);
+	int (*irq_subscribe)(struct sbus_priv *self, sbus_irq_handler handler,
+				void *priv);
+	int (*irq_unsubscribe)(struct sbus_priv *self);
+	int (*reset)(struct sbus_priv *self);
+	size_t (*align_size)(struct sbus_priv *self, size_t size);
+	int (*set_block_size)(struct sbus_priv *self, size_t size);
+	int (*pipe_send)(struct sbus_priv *self, u8 pipe, u32 len, u8 *buf);
+	void * (*pipe_read)(struct sbus_priv *self);
+	int (*sbus_reg_read)(struct sbus_priv *self, u32 reg,
+					void *buf, int count);
+	int (*sbus_reg_write)(struct sbus_priv *self, u32 reg,
+					const void *buf, int count);
+
+	/* sub_system: 0 for mcu, 1 for wifi, 2 for bt, ... */
+	int (*sbus_active)(struct sbus_priv *self, int sub_system);
+	int (*sbus_deactive)(struct sbus_priv *self, int sub_system);
+	int (*power_switch)(struct sbus_priv *self, int on);
+	/* gpio wake, beacuse bes2600 sdio can't wakeup mcu, so add the two of interfaces */
+	void (*gpio_wake)(struct sbus_priv *self, int falg);
+	void (*gpio_sleep)(struct sbus_priv *self, int falg);
+	/* halt device to get debug information */
+	void (*halt_device)(struct sbus_priv *self);
+	bool (*wakeup_source)(struct sbus_priv *self);
+};
+
+#ifdef CONFIG_BES2600_WLAN_USB
+/* tx/rx pipes for usb */
+enum BES2600_USB_PIPE_ID {
+	BES2600_USB_PIPE_TX_CTRL = 0,
+	BES2600_USB_PIPE_TX_WLAN,
+	BES2600_USB_PIPE_TX_BT,
+	BES2600_USB_PIPE_RX_CTRL,
+	BES2600_USB_PIPE_RX_WLAN,
+	BES2600_USB_PIPE_RX_BT,
+	BES2600_USB_PIPE_MAX
+};
+
+// virtual register definition
+#define BES_USB_CONTROL_REG	0
+#define BES_USB_STATUS_REG	1
+
+// virtual register bits definition
+#define BES_USB_FW_TX_DONE              BIT(0)
+#define BES_USB_FW_RX_INDICATION        BIT(1)
+#endif
+
+void bes2600_irq_handler(struct bes2600_common *priv);
+
+/* This MUST be wrapped with hwbus_ops->lock/unlock! */
+int __bes2600_irq_enable(int enable);
+
+#endif /* BES2600_SBUS_H */
diff --git a/drivers/staging/bes2600/scan.c b/drivers/staging/bes2600/scan.c
new file mode 100644
index 000000000000..e0e13b3e5a0f
--- /dev/null
+++ b/drivers/staging/bes2600/scan.c
@@ -0,0 +1,1180 @@
+/*
+ * Scan implementation for BES2600 mac80211 drivers
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/sched.h>
+#include "bes2600.h"
+#include "scan.h"
+#include "sta.h"
+#include "pm.h"
+#include "epta_request.h"
+#include "bes_pwr.h"
+
+static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
+
+#ifdef CONFIG_BES2600_TESTMODE
+static int bes2600_advance_scan_start(struct bes2600_common *hw_priv)
+{
+	int tmo = 0;
+	tmo += hw_priv->advanceScanElems.duration;
+	bes2600_pwr_set_busy_event_with_timeout(hw_priv, BES_PWR_LOCK_ON_ADV_SCAN, tmo);
+	/* Invoke Advance Scan Duration Timeout Handler */
+	queue_delayed_work(hw_priv->workqueue,
+		&hw_priv->advance_scan_timeout, tmo * HZ / 1000);
+	return 0;
+}
+#endif
+
+static void bes2600_remove_wps_p2p_ie(struct wsm_template_frame *frame)
+{
+	u8 *ies;
+	u32 ies_len;
+	u32 ie_len;
+	u32 p2p_ie_len = 0;
+	u32 wps_ie_len = 0;
+
+	ies = &frame->skb->data[sizeof(struct ieee80211_hdr_3addr)];
+	ies_len = frame->skb->len - sizeof(struct ieee80211_hdr_3addr);
+
+	while (ies_len >= 6) {
+		ie_len = ies[1] + 2;
+		if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC)
+			&& (ies[2] == 0x00 && ies[3] == 0x50 && ies[4] == 0xf2 && ies[5] == 0x04)) {
+			wps_ie_len = ie_len;
+			memmove(ies, ies + ie_len, ies_len);
+			ies_len -= ie_len;
+
+		}
+		else if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC) &&
+			(ies[2] == 0x50 && ies[3] == 0x6f && ies[4] == 0x9a && ies[5] == 0x09)) {
+			p2p_ie_len = ie_len;
+			memmove(ies, ies + ie_len, ies_len);
+			ies_len -= ie_len;
+		} else {
+			ies += ie_len;
+			ies_len -= ie_len;
+		}
+	}
+
+	if (p2p_ie_len || wps_ie_len) {
+		skb_trim(frame->skb, frame->skb->len - (p2p_ie_len + wps_ie_len));
+	}
+}
+
+#ifdef CONFIG_BES2600_TESTMODE
+static int bes2600_disable_filtering(struct bes2600_vif *priv)
+{
+	int ret = 0;
+	bool bssid_filtering = 0;
+	struct wsm_rx_filter rx_filter;
+	struct wsm_beacon_filter_control bf_control;
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	/* RX Filter Disable */
+	rx_filter.promiscuous = 0;
+	rx_filter.bssid = 0;
+	rx_filter.fcs = 0;
+	rx_filter.probeResponder = 0;
+	rx_filter.keepalive = 0;
+	ret = wsm_set_rx_filter(hw_priv, &rx_filter,
+			priv->if_id);
+
+	/* Beacon Filter Disable */
+	bf_control.enabled = 0;
+	bf_control.bcn_count = 1;
+	if (!ret)
+		ret = wsm_beacon_filter_control(hw_priv, &bf_control,
+					priv->if_id);
+
+	/* BSSID Filter Disable */
+	if (!ret)
+		ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering,
+					 priv->if_id);
+
+	return ret;
+}
+#endif
+
+static int bes2600_scan_get_first_active_if(struct bes2600_common *hw_priv)
+{
+	int i = 0;
+	struct bes2600_vif *vif;
+
+	bes2600_for_each_vif(hw_priv, vif, i) {
+		if (vif->join_status > BES2600_JOIN_STATUS_PASSIVE)
+			return i;
+	}
+
+	return -1;
+}
+
+static int bes2600_scan_start(struct bes2600_vif *priv, struct wsm_scan *scan)
+{
+	int ret, i;
+#ifdef FPGA_SETUP
+	int tmo = 5000;
+#else
+	int tmo = 5000;
+#endif
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+
+	if (hw_priv->scan_switch_if_id == -1 &&
+		hw_priv->ht_info.channel_type > NL80211_CHAN_HT20 &&
+		priv->if_id >= 0) {
+		hw_priv->scan_switch_if_id = bes2600_scan_get_first_active_if(hw_priv);
+		if(hw_priv->scan_switch_if_id >= 0) {
+			struct wsm_switch_channel channel;
+			channel.channelMode = 0 << 4;
+			channel.channelSwitchCount = 0;
+			channel.newChannelNumber = hw_priv->channel->hw_value;
+			wsm_switch_channel(hw_priv, &channel, hw_priv->scan_switch_if_id);
+			bes2600_info(BES2600_DBG_SCAN, "scan start channel type %d num %d\n", hw_priv->ht_info.channel_type, channel.newChannelNumber);
+		}
+	}
+	for (i = 0; i < scan->numOfChannels; ++i)
+		tmo += scan->ch[i].maxChannelTime + 10;
+	atomic_set(&hw_priv->scan.in_progress, 1);
+	atomic_set(&hw_priv->recent_scan, 1);
+	queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout,
+		tmo * HZ / 1000);
+#ifdef P2P_MULTIVIF
+	ret = wsm_scan(hw_priv, scan, 0);
+#else
+	ret = wsm_scan(hw_priv, scan, priv->if_id);
+#endif
+	if (unlikely(ret)) {
+		atomic_set(&hw_priv->scan.in_progress, 0);
+		cancel_delayed_work_sync(&hw_priv->scan.timeout);
+		bes2600_scan_restart_delayed(priv);
+	}
+	return ret;
+}
+
+#ifdef ROAM_OFFLOAD
+static int bes2600_sched_scan_start(struct bes2600_vif *priv, struct wsm_scan *scan)
+{
+	int ret;
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	ret = wsm_scan(hw_priv, scan, priv->if_id);
+	if (unlikely(ret)) {
+		atomic_set(&hw_priv->scan.in_progress, 0);
+	}
+	return ret;
+}
+#endif /*ROAM_OFFLOAD*/
+
+int bes2600_hw_scan(struct ieee80211_hw *hw,
+		   struct ieee80211_vif *vif,
+		   struct ieee80211_scan_request *hw_req)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct cfg80211_scan_request *req = &hw_req->req;
+	struct wsm_template_frame frame = {
+		.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+	};
+	int i;
+	/* Scan when P2P_GO corrupt firmware MiniAP mode */
+	if (priv->join_status == BES2600_JOIN_STATUS_AP)
+		return -EOPNOTSUPP;
+#if 0
+	if (work_pending(&priv->offchannel_work) ||
+			(hw_priv->roc_if_id != -1)) {
+		wiphy_dbg(hw_priv->hw->wiphy, "[SCAN] Offchannel work pending, "
+				"ignoring scan work %d\n",  hw_priv->roc_if_id);
+		return -EBUSY;
+	}
+#endif
+	if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
+		req->n_ssids = 0;
+
+	wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
+		req->n_ssids);
+
+	if (req->n_ssids > hw->wiphy->max_scan_ssids)
+		return -EINVAL;
+
+	bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN);
+
+	frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
+		req->ie_len);
+	if (!frame.skb)
+		return -ENOMEM;
+
+	if (req->ie_len)
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+		skb_put_data(frame.skb, req->ie, req->ie_len);
+#else
+		memcpy(skb_put(frame.skb, req->ie_len), req->ie, req->ie_len);
+#endif
+
+	/* will be unlocked in bes2600_scan_work() */
+	down(&hw_priv->scan.lock);
+	down(&hw_priv->conf_lock);
+
+	if (frame.skb) {
+		int ret;
+		//if (priv->if_id == 0)
+		//	bes2600_remove_wps_p2p_ie(&frame);
+#ifdef P2P_MULTIVIF
+		ret = wsm_set_template_frame(hw_priv, &frame, 0);
+#else
+		ret = wsm_set_template_frame(hw_priv, &frame,
+				priv->if_id);
+#endif
+#if 0
+		if (!ret) {
+		/* Host want to be the probe responder. */
+				ret = wsm_set_probe_responder(priv, true);
+		}
+#endif
+		if (ret) {
+			up(&hw_priv->conf_lock);
+			up(&hw_priv->scan.lock);
+			dev_kfree_skb(frame.skb);
+			return ret;
+		}
+	}
+
+	wsm_vif_lock_tx(priv);
+
+	BUG_ON(hw_priv->scan.req);
+	hw_priv->scan.req = req;
+	hw_priv->scan.n_ssids = 0;
+	hw_priv->scan.status = 0;
+	hw_priv->scan.begin = &req->channels[0];
+	hw_priv->scan.curr = hw_priv->scan.begin;
+	hw_priv->scan.end = &req->channels[req->n_channels];
+	hw_priv->scan.output_power = hw_priv->output_power;
+	hw_priv->scan.if_id = priv->if_id;
+	/* TODO:COMBO: Populate BIT4 in scanflags to decide on which MAC
+	 * address the SCAN request will be sent */
+	bes2600_info(BES2600_DBG_SCAN, "%s %d if_id:%d,num_channel:%d.\n", __func__, __LINE__, priv->if_id, req->n_channels);
+
+	for (i = 0; i < req->n_ssids; ++i) {
+		struct wsm_ssid *dst =
+			&hw_priv->scan.ssids[hw_priv->scan.n_ssids];
+		BUG_ON(req->ssids[i].ssid_len > sizeof(dst->ssid));
+		memcpy(&dst->ssid[0], req->ssids[i].ssid,
+			sizeof(dst->ssid));
+		dst->length = req->ssids[i].ssid_len;
+		++hw_priv->scan.n_ssids;
+	}
+
+	up(&hw_priv->conf_lock);
+
+	if (frame.skb)
+		dev_kfree_skb(frame.skb);
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING);
+#endif
+	queue_work(hw_priv->workqueue, &hw_priv->scan.work);
+
+	return 0;
+}
+
+#ifdef ROAM_OFFLOAD
+int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
+		   struct ieee80211_vif *vif,
+		   struct cfg80211_sched_scan_request *req,
+		   struct ieee80211_sched_scan_ies *ies)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct wsm_template_frame frame = {
+		.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+	};
+	int i;
+
+	wiphy_warn(hw->wiphy, "[SCAN] Scheduled scan request-->.\n");
+
+	if (!priv->vif)
+		return -EINVAL;
+
+	/* Scan when P2P_GO corrupt firmware MiniAP mode */
+	if (priv->join_status == BES2600_JOIN_STATUS_AP)
+		return -EOPNOTSUPP;
+
+	wiphy_warn(hw->wiphy, "[SCAN] Scheduled scan: n_ssids %d, ssid[0].len = %d\n", req->n_ssids, req->ssids[0].ssid_len);
+	if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
+		req->n_ssids = 0;
+
+	wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
+		req->n_ssids);
+
+	if (req->n_ssids > hw->wiphy->max_scan_ssids)
+		return -EINVAL;
+
+	frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
+		req->ie_len);
+	if (!frame.skb)
+		return -ENOMEM;
+
+	/* will be unlocked in bes2600_scan_work() */
+	down(&hw_priv->scan.lock);
+	down(&hw_priv->conf_lock);
+	if (frame.skb) {
+		int ret;
+		if (priv->if_id == 0)
+			bes2600_remove_wps_p2p_ie(&frame);
+		ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
+		if (0 == ret) {
+			/* Host want to be the probe responder. */
+			ret = wsm_set_probe_responder(priv, true);
+		}
+		if (ret) {
+			up(&hw_priv->conf_lock);
+			up(&hw_priv->scan.lock);
+			dev_kfree_skb(frame.skb);
+			return ret;
+		}
+	}
+
+	wsm_lock_tx(hw_priv);
+
+	BUG_ON(hw_priv->scan.req);
+	hw_priv->scan.sched_req = req;
+	hw_priv->scan.n_ssids = 0;
+	hw_priv->scan.status = 0;
+	hw_priv->scan.begin = &req->channels[0];
+	hw_priv->scan.curr = hw_priv->scan.begin;
+	hw_priv->scan.end = &req->channels[req->n_channels];
+	hw_priv->scan.output_power = hw_priv->output_power;
+
+	for (i = 0; i < req->n_ssids; ++i) {
+		struct wsm_ssid *dst =
+			&hw_priv->scan.ssids[hw_priv->scan.n_ssids];
+		BUG_ON(req->ssids[i].ssid_len > sizeof(dst->ssid));
+		memcpy(&dst->ssid[0], req->ssids[i].ssid,
+			sizeof(dst->ssid));
+		dst->length = req->ssids[i].ssid_len;
+		++hw_priv->scan.n_ssids;
+		{
+			u8 j;
+			wiphy_warn(hw->wiphy, "[SCAN] SSID %d\n",i);
+			for(j=0; j<req->ssids[i].ssid_len; j++)
+				wiphy_warn(priv->hw->wiphy, "[SCAN] 0x%x\n", req->ssids[i].ssid[j]);
+		}
+	}
+
+	up(&hw_priv->conf_lock);
+
+	if (frame.skb)
+		dev_kfree_skb(frame.skb);
+	queue_work(hw_priv->workqueue, &hw_priv->scan.swork);
+	wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n");
+	return 0;
+}
+#endif /*ROAM_OFFLOAD*/
+void bes2600_scan_work(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv = container_of(work,
+						struct bes2600_common,
+						scan.work);
+	struct bes2600_vif *priv, *vif;
+	struct ieee80211_channel **it;
+	struct wsm_scan scan = {
+		.scanType = WSM_SCAN_TYPE_FOREGROUND,
+		.scanFlags = 0, /* TODO:COMBO */
+		//.scanFlags = WSM_SCAN_FLAG_SPLIT_METHOD, /* TODO:COMBO */
+	};
+	bool first_run;
+	int i;
+	const u32 ProbeRequestTime  = 2;
+	const u32 ChannelRemainTime = 15;
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	u32 minChannelTime;
+#endif
+	u32 maxChannelTime;
+#ifdef CONFIG_BES2600_TESTMODE
+	int ret = 0;
+	u16 advance_scan_req_channel = hw_priv->scan.begin[0]->hw_value;
+#endif
+	priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id);
+
+	/*TODO: COMBO: introduce locking so vif is not removed in meanwhile */
+	if (!priv) {
+		wiphy_warn(hw_priv->hw->wiphy, "[SCAN] interface removed, "
+			   "ignoring scan work\n");
+		return;
+	}
+
+	if (priv->if_id)
+		scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1;
+	else
+		scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1;
+
+	bes2600_for_each_vif(hw_priv, vif, i) {
+#ifdef P2P_MULTIVIF
+		if ((i == (CW12XX_MAX_VIFS - 1)) || !vif)
+#else
+		if (!vif)
+#endif
+			continue;
+		if (vif->bss_loss_status > BES2600_BSS_LOSS_NONE)
+			scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND;
+	}
+	first_run = hw_priv->scan.begin == hw_priv->scan.curr &&
+			hw_priv->scan.begin != hw_priv->scan.end;
+
+	if (first_run) {
+		/* Firmware gets crazy if scan request is sent
+		 * when STA is joined but not yet associated.
+		 * Force unjoin in this case. */
+		if (cancel_delayed_work_sync(&priv->join_timeout) > 0) {
+			bes2600_join_timeout(&priv->join_timeout.work);
+		}
+	}
+	down(&hw_priv->conf_lock);
+	if (first_run) {
+#ifdef CONFIG_BES2600_TESTMODE
+		/* Passive Scan - Serving Channel Request Handling */
+		if (hw_priv->enable_advance_scan &&
+			(hw_priv->advanceScanElems.scanMode ==
+				BES2600_SCAN_MEASUREMENT_PASSIVE) &&
+			(priv->join_status == BES2600_JOIN_STATUS_STA) &&
+			(hw_priv->channel->hw_value ==
+				advance_scan_req_channel)) {
+			/* If Advance Scan Request is for Serving Channel Device
+			 * should be Active and Filtering Should be Disable */
+			if (priv->powersave_mode.pmMode & WSM_PSM_PS) {
+				struct wsm_set_pm pm = priv->powersave_mode;
+				pm.pmMode = WSM_PSM_ACTIVE;
+				wsm_set_pm(hw_priv, &pm, priv->if_id);
+			}
+			/* Disable Rx Beacon and Bssid filter */
+			ret = bes2600_disable_filtering(priv);
+			if (ret)
+				wiphy_err(hw_priv->hw->wiphy,
+				"%s: Disable BSSID or Beacon filtering failed: %d.\n",
+				__func__, ret);
+		} else if (hw_priv->enable_advance_scan &&
+			(hw_priv->advanceScanElems.scanMode ==
+				BES2600_SCAN_MEASUREMENT_PASSIVE) &&
+			(priv->join_status == BES2600_JOIN_STATUS_STA)) {
+				if (priv->join_status == BES2600_JOIN_STATUS_STA &&
+					!(priv->powersave_mode.pmMode & WSM_PSM_PS)) {
+					struct wsm_set_pm pm = priv->powersave_mode;
+					pm.pmMode = WSM_PSM_PS;
+					bes2600_set_pm(priv, &pm);
+				}
+		} else {
+#endif
+#if 0
+			if (priv->join_status == BES2600_JOIN_STATUS_STA &&
+					!(priv->powersave_mode.pmMode & WSM_PSM_PS)) {
+				struct wsm_set_pm pm = priv->powersave_mode;
+				pm.pmMode = WSM_PSM_PS;
+				bes2600_set_pm(priv, &pm);
+			} else
+#endif
+			if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) {
+				/* FW bug: driver has to restart p2p-dev mode
+				 * after scan */
+				bes2600_disable_listening(priv);
+			}
+#ifdef CONFIG_BES2600_TESTMODE
+		}
+#endif
+	}
+
+	if (!hw_priv->scan.req || (hw_priv->scan.curr == hw_priv->scan.end)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)
+		struct cfg80211_scan_info info = {
+			.aborted = hw_priv->scan.status ? 1 : 0,
+		};
+#else
+		bool aborted = hw_priv->scan.status ? true : false;
+#endif
+
+#ifdef CONFIG_BES2600_TESTMODE
+		if (hw_priv->enable_advance_scan &&
+			(hw_priv->advanceScanElems.scanMode ==
+				BES2600_SCAN_MEASUREMENT_PASSIVE) &&
+			(priv->join_status == BES2600_JOIN_STATUS_STA) &&
+			(hw_priv->channel->hw_value ==
+				advance_scan_req_channel)) {
+			/* WSM Lock should be held here for WSM APIs */
+			wsm_vif_lock_tx(priv);
+			/* wsm_lock_tx(priv); */
+			/* Once Duration is Over, enable filtering
+			 * and Revert Back Power Save */
+			if (priv->powersave_mode.pmMode & WSM_PSM_PS)
+				wsm_set_pm(hw_priv, &priv->powersave_mode,
+					priv->if_id);
+			bes2600_update_filtering(priv);
+		} else {
+			if (!hw_priv->enable_advance_scan) {
+#endif
+				if (hw_priv->scan.output_power != hw_priv->output_power)
+				/* TODO:COMBO: Change when mac80211 implementation
+				 * is available for output power also */
+#ifdef P2P_MULTIVIF
+					WARN_ON(wsm_set_output_power(hw_priv,
+						hw_priv->output_power * 10,
+						priv->if_id ? 0 : 0));
+#else
+					WARN_ON(wsm_set_output_power(hw_priv,
+						hw_priv->output_power * 10,
+						priv->if_id));
+#endif
+#ifdef CONFIG_BES2600_TESTMODE
+			}
+		}
+#endif
+#if 0
+		if (priv->join_status == BES2600_JOIN_STATUS_STA &&
+				!(priv->powersave_mode.pmMode & WSM_PSM_PS))
+			bes2600_set_pm(priv, &priv->powersave_mode);
+#endif
+		if (hw_priv->scan.status < 0)
+			wiphy_info(priv->hw->wiphy,
+					"[SCAN] Scan failed (%d).\n",
+					hw_priv->scan.status);
+		else if (hw_priv->scan.req)
+			wiphy_info(priv->hw->wiphy,
+					"[SCAN] Scan completed.\n");
+		else
+			wiphy_info(priv->hw->wiphy,
+					"[SCAN] Scan canceled.\n");
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+		if (priv->join_status == BES2600_JOIN_STATUS_STA) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+			if (hw_priv->channel->band != NL80211_BAND_2GHZ)
+#else
+			if (hw_priv->channel->band != IEEE80211_BAND_2GHZ)
+#endif
+				bwifi_change_current_status(hw_priv, BWIFI_STATUS_GOT_IP_5G);
+			else
+				bwifi_change_current_status(hw_priv, BWIFI_STATUS_GOT_IP);
+		} else {
+			bwifi_change_current_status(hw_priv, BWIFI_STATUS_IDLE);
+		}
+#endif
+		bes2600_info(BES2600_DBG_SCAN, "%s %d %d.", __func__, __LINE__, hw_priv->ht_info.channel_type);
+		/* switch to previous channel and bw mode after scan done */
+		if (hw_priv->scan_switch_if_id >= 0) {
+			struct wsm_switch_channel channel;
+			channel.channelMode = hw_priv->ht_info.channel_type << 4;
+			channel.channelSwitchCount = 0;
+			channel.newChannelNumber = hw_priv->channel->hw_value;
+			wsm_switch_channel(hw_priv, &channel, hw_priv->scan_switch_if_id);
+			hw_priv->scan_switch_if_id = -1;
+			bes2600_info(BES2600_DBG_SCAN, "scan done channel type %d num %d\n", hw_priv->ht_info.channel_type, channel.newChannelNumber);
+		}
+
+		hw_priv->scan.req = NULL;
+		bes2600_scan_restart_delayed(priv);
+#ifdef CONFIG_BES2600_TESTMODE
+		hw_priv->enable_advance_scan = false;
+#endif /* CONFIG_BES2600_TESTMODE */
+		wsm_unlock_tx(hw_priv);
+		up(&hw_priv->conf_lock);
+		bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)
+		ieee80211_scan_completed(hw_priv->hw, &info);
+#else
+		ieee80211_scan_completed(hw_priv->hw, aborted);
+#endif
+		up(&hw_priv->scan.lock);
+		return;
+	} else {
+		struct ieee80211_channel *first = *hw_priv->scan.curr;
+		for (it = hw_priv->scan.curr + 1, i = 1;
+		     it != hw_priv->scan.end &&
+				i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
+		     ++it, ++i) {
+			if ((*it)->band != first->band)
+				break;
+			// Doen't split scan req in case of EPTA error after scan req
+			// if (((*it)->flags ^ first->flags) &
+			// 		IEEE80211_CHAN_NO_IR)
+			// 	break;
+			// if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
+			// 	(*it)->max_power != first->max_power)
+			// 	break;
+		}
+		scan.band = first->band;
+
+		if (hw_priv->scan.req->no_cck)
+			scan.maxTransmitRate = WSM_TRANSMIT_RATE_6;
+		else
+			scan.maxTransmitRate = WSM_TRANSMIT_RATE_1;
+#ifdef CONFIG_BES2600_TESTMODE
+		if (hw_priv->enable_advance_scan) {
+			if (hw_priv->advanceScanElems.scanMode ==
+				BES2600_SCAN_MEASUREMENT_PASSIVE)
+				scan.numOfProbeRequests = 0;
+			else
+				scan.numOfProbeRequests = 2;
+		} else {
+#endif
+			/* TODO: Is it optimal? */
+			scan.numOfProbeRequests =
+				(first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
+#ifdef CONFIG_BES2600_TESTMODE
+		}
+#endif /* CONFIG_BES2600_TESTMODE */
+		scan.numOfSSIDs = hw_priv->scan.n_ssids;
+		scan.ssids = &hw_priv->scan.ssids[0];
+		scan.numOfChannels = it - hw_priv->scan.curr;
+		/* TODO: Is it optimal? */
+		scan.probeDelay = 100;
+		/* It is not stated in WSM specification, however
+		 * FW team says that driver may not use FG scan
+		 * when joined. */
+		if (priv->join_status == BES2600_JOIN_STATUS_STA) {
+			scan.scanType = WSM_SCAN_TYPE_BACKGROUND;
+			scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+		}
+		scan.ch = kzalloc((it - hw_priv->scan.curr) *
+			sizeof(struct wsm_scan_ch), GFP_KERNEL);
+		if (!scan.ch) {
+			hw_priv->scan.status = -ENOMEM;
+			goto fail;
+		}
+		maxChannelTime = (scan.numOfSSIDs * scan.numOfProbeRequests *
+			ProbeRequestTime) + ChannelRemainTime;
+		maxChannelTime = (maxChannelTime < 35) ? 35 : maxChannelTime;
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+		if (scan.band == NL80211_BAND_2GHZ) {
+			coex_calc_wifi_scan_time(&minChannelTime, &maxChannelTime);
+		} else {
+			minChannelTime = 90;
+			maxChannelTime = 90;
+		}
+#endif
+
+		for (i = 0; i < scan.numOfChannels; ++i) {
+			scan.ch[i].number = hw_priv->scan.curr[i]->hw_value;
+#ifdef CONFIG_BES2600_TESTMODE
+			if (hw_priv->enable_advance_scan) {
+				scan.ch[i].minChannelTime =
+					hw_priv->advanceScanElems.duration;
+				scan.ch[i].maxChannelTime =
+					hw_priv->advanceScanElems.duration;
+			} else {
+#endif
+
+#ifndef WIFI_BT_COEXIST_EPTA_ENABLE
+				if (hw_priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) {
+					scan.ch[i].minChannelTime = 40;
+					scan.ch[i].maxChannelTime = 100;
+				}
+				else {
+					//TODO: modify maxChannelTime
+					scan.ch[i].minChannelTime = 15;
+					scan.ch[i].maxChannelTime = maxChannelTime;
+				}
+#else
+				scan.ch[i].minChannelTime = minChannelTime;
+				scan.ch[i].maxChannelTime = maxChannelTime;
+#endif
+
+#ifdef CONFIG_BES2600_TESTMODE
+			}
+#endif
+		}
+#ifdef CONFIG_BES2600_TESTMODE
+		if (!hw_priv->enable_advance_scan) {
+#endif
+			if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
+			    hw_priv->scan.output_power != first->max_power) {
+				hw_priv->scan.output_power = first->max_power;
+				/* TODO:COMBO: Change after mac80211 implementation
+			 	* complete */
+#ifdef P2P_MULTIVIF
+				WARN_ON(wsm_set_output_power(hw_priv,
+						hw_priv->scan.output_power * 10,
+						priv->if_id ? 0 : 0));
+#else
+				WARN_ON(wsm_set_output_power(hw_priv,
+						hw_priv->scan.output_power * 10,
+						priv->if_id));
+#endif
+			}
+#ifdef CONFIG_BES2600_TESTMODE
+		}
+#endif
+#ifdef CONFIG_BES2600_TESTMODE
+		if (hw_priv->enable_advance_scan &&
+			(hw_priv->advanceScanElems.scanMode ==
+				BES2600_SCAN_MEASUREMENT_PASSIVE) &&
+			(priv->join_status == BES2600_JOIN_STATUS_STA) &&
+			(hw_priv->channel->hw_value == advance_scan_req_channel)) {
+				/* Start Advance Scan Timer */
+				hw_priv->scan.status = bes2600_advance_scan_start(hw_priv);
+				wsm_unlock_tx(hw_priv);
+		} else
+#endif
+			hw_priv->scan.status = bes2600_scan_start(priv, &scan);
+		kfree(scan.ch);
+		if (WARN_ON(hw_priv->scan.status))
+			goto fail;
+		hw_priv->scan.curr = it;
+	}
+	up(&hw_priv->conf_lock);
+	return;
+
+fail:
+	hw_priv->scan.curr = hw_priv->scan.end;
+	up(&hw_priv->conf_lock);
+	queue_work(hw_priv->workqueue, &hw_priv->scan.work);
+	return;
+}
+
+#ifdef ROAM_OFFLOAD
+void bes2600_sched_scan_work(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv = container_of(work, struct bes2600_common,
+		scan.swork);
+	struct wsm_scan scan;
+	struct wsm_ssid scan_ssid;
+	int i;
+	struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv,
+					hw_priv->scan.if_id);
+	if (unlikely(!priv)) {
+		WARN_ON(1);
+		return;
+	}
+
+	spin_unlock(&priv->vif_lock);
+
+	/* Firmware gets crazy if scan request is sent
+	 * when STA is joined but not yet associated.
+	 * Force unjoin in this case. */
+	if (cancel_delayed_work_sync(&priv->join_timeout) > 0) {
+		bes2600_join_timeout(&priv->join_timeout.work);
+	}
+	down(&hw_priv->conf_lock);
+	hw_priv->auto_scanning = 1;
+
+	scan.band = 0;
+
+	if (priv->join_status == BES2600_JOIN_STATUS_STA)
+		scan.scanType = 3; /* auto background */
+	else
+		scan.scanType = 2; /* auto foreground */
+
+	scan.scanFlags = 0x01; /* bit 0 set => forced background scan */
+	scan.maxTransmitRate = WSM_TRANSMIT_RATE_6;
+	scan.autoScanInterval = (0xba << 24)|(30 * 1024); /* 30 seconds, -70 rssi */
+	scan.numOfProbeRequests = 2;
+	//scan.numOfChannels = 11;
+	scan.numOfChannels = hw_priv->num_scanchannels;
+	scan.numOfSSIDs = 1;
+	scan.probeDelay = 100;
+	scan_ssid.length = priv->ssid_length;
+	memcpy(scan_ssid.ssid, priv->ssid, priv->ssid_length);
+	scan.ssids = &scan_ssid;
+
+	scan.ch = kzalloc(
+		sizeof(struct wsm_scan_ch[scan.numOfChannels]),
+		GFP_KERNEL);
+	if (!scan.ch) {
+		hw_priv->scan.status = -ENOMEM;
+		goto fail;
+	}
+
+	for (i = 0; i < scan.numOfChannels; i++) {
+		scan.ch[i].number = hw_priv->scan_channels[i].number;
+		scan.ch[i].minChannelTime = hw_priv->scan_channels[i].minChannelTime;
+		scan.ch[i].maxChannelTime = hw_priv->scan_channels[i].maxChannelTime;
+		scan.ch[i].txPowerLevel = hw_priv->scan_channels[i].txPowerLevel;
+	}
+
+#if 0
+	for (i = 1; i <= scan.numOfChannels; i++) {
+		scan.ch[i-1].number = i;
+		scan.ch[i-1].minChannelTime = 10;
+		scan.ch[i-1].maxChannelTime = 40;
+	}
+#endif
+
+	hw_priv->scan.status = bes2600_sched_scan_start(priv, &scan);
+	kfree(scan.ch);
+	if (hw_priv->scan.status)
+		goto fail;
+	up(&hw_priv->conf_lock);
+	return;
+
+fail:
+	up(&hw_priv->conf_lock);
+	queue_work(hw_priv->workqueue, &hw_priv->scan.swork);
+	return;
+}
+
+void bes2600_hw_sched_scan_stop(struct bes2600_common *hw_priv)
+{
+	struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv,
+					hw_priv->scan.if_id);
+	if (unlikely(!priv))
+		return;
+	spin_unlock(&priv->vif_lock);
+
+	wsm_stop_scan(hw_priv, priv->if_id);
+
+	return;
+}
+#endif /*ROAM_OFFLOAD*/
+
+
+static void bes2600_scan_restart_delayed(struct bes2600_vif *priv)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	if (priv->delayed_link_loss) {
+		int tmo = priv->cqm_beacon_loss_count;
+
+		if (hw_priv->scan.direct_probe)
+			tmo = 0;
+
+		priv->delayed_link_loss = 0;
+		/* Restart beacon loss timer and requeue
+		   BSS loss work. */
+		wiphy_dbg(priv->hw->wiphy,
+				"[CQM] Requeue BSS loss in %d "
+				"beacons.\n", tmo);
+		spin_lock(&priv->bss_loss_lock);
+		priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
+		spin_unlock(&priv->bss_loss_lock);
+		cancel_delayed_work_sync(&priv->bss_loss_work);
+		queue_delayed_work(hw_priv->workqueue,
+				&priv->bss_loss_work,
+				tmo * HZ / 10);
+	}
+
+	/* FW bug: driver has to restart p2p-dev mode after scan. */
+	if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) {
+		/*bes2600_enable_listening(priv);*/
+		// WARN_ON(1);
+		bes2600_dbg(BES2600_DBG_SCAN, "scan complete join_status is monitor");
+		bes2600_update_filtering(priv);
+	}
+
+	if (priv->delayed_unjoin) {
+		priv->delayed_unjoin = false;
+		if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
+			wsm_unlock_tx(hw_priv);
+	}
+}
+
+static void bes2600_scan_complete(struct bes2600_common *hw_priv, int if_id)
+{
+	struct bes2600_vif *priv;
+	atomic_xchg(&hw_priv->recent_scan, 0);
+
+	if (hw_priv->scan.direct_probe) {
+		down(&hw_priv->conf_lock);
+		priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
+		if (priv) {
+			wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe "
+				  "complete.\n");
+			bes2600_scan_restart_delayed(priv);
+		} else {
+			wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe "
+				  "complete without interface!\n");
+		}
+		up(&hw_priv->conf_lock);
+		hw_priv->scan.direct_probe = 0;
+		up(&hw_priv->scan.lock);
+		wsm_unlock_tx(hw_priv);
+	} else {
+		bes2600_scan_work(&hw_priv->scan.work);
+	}
+}
+
+void bes2600_scan_complete_cb(struct bes2600_common *hw_priv,
+				struct wsm_scan_complete *arg)
+{
+	struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv,
+					hw_priv->scan.if_id);
+
+	if (unlikely(!priv))
+		return;
+
+#ifdef ROAM_OFFLOAD
+	if (hw_priv->auto_scanning)
+		queue_delayed_work(hw_priv->workqueue,
+				&hw_priv->scan.timeout, 0);
+#endif /*ROAM_OFFLOAD*/
+
+	if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
+		/* STA is stopped. */
+		spin_unlock(&priv->vif_lock);
+		return;
+	}
+	spin_unlock(&priv->vif_lock);
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	// recover EPTA timer after scan wsm msg complete, in case of epta state error
+	// bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING_COMP);
+#endif
+	wiphy_info(hw_priv->hw->wiphy, "bes2600_scan_complete_cb status: %u", arg->status);
+
+	if(hw_priv->scan.status == -ETIMEDOUT)
+		wiphy_warn(hw_priv->hw->wiphy,
+			"Scan timeout already occured. Don't cancel work");
+	if ((hw_priv->scan.status != -ETIMEDOUT) &&
+		(cancel_delayed_work_sync(&hw_priv->scan.timeout) > 0)) {
+		hw_priv->scan.status = 1;
+		queue_delayed_work(hw_priv->workqueue,
+				&hw_priv->scan.timeout, 0);
+	}
+}
+
+void bes2600_scan_timeout(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, scan.timeout.work);
+
+	if (likely(atomic_xchg(&hw_priv->scan.in_progress, 0))) {
+		if (hw_priv->scan.status > 0)
+			hw_priv->scan.status = 0;
+		else if (!hw_priv->scan.status) {
+			wiphy_warn(hw_priv->hw->wiphy,
+				"Timeout waiting for scan "
+				"complete notification.\n");
+			hw_priv->scan.status = -ETIMEDOUT;
+			hw_priv->scan.curr = hw_priv->scan.end;
+			WARN_ON(wsm_stop_scan(hw_priv,
+						hw_priv->scan.if_id ? 1 : 0));
+		}
+		bes2600_scan_complete(hw_priv, hw_priv->scan.if_id);
+#ifdef ROAM_OFFLOAD
+	} else if (hw_priv->auto_scanning) {
+		hw_priv->auto_scanning = 0;
+		ieee80211_sched_scan_results(hw_priv->hw);
+#endif /*ROAM_OFFLOAD*/
+	}
+}
+
+#ifdef CONFIG_BES2600_TESTMODE
+void bes2600_advance_scan_timeout(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, advance_scan_timeout.work);
+
+	struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv,
+					hw_priv->scan.if_id);
+	if (WARN_ON(!priv))
+		return;
+	spin_unlock(&priv->vif_lock);
+
+	hw_priv->scan.status = 0;
+	if (hw_priv->advanceScanElems.scanMode ==
+		BES2600_SCAN_MEASUREMENT_PASSIVE) {
+		/* Passive Scan on Serving Channel
+		 * Timer Expire */
+		bes2600_scan_complete(hw_priv, hw_priv->scan.if_id);
+	} else {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)
+		struct cfg80211_scan_info info = {
+			.aborted = hw_priv->scan.status ? 1 : 0,
+		};
+#else
+		bool aborted = hw_priv->scan.status ? true : false;
+#endif
+		/* Active Scan on Serving Channel
+		 * Timer Expire */
+		down(&hw_priv->conf_lock);
+		//wsm_lock_tx(priv);
+		wsm_vif_lock_tx(priv);
+		/* Once Duration is Over, enable filtering
+		 * and Revert Back Power Save */
+		if ((priv->powersave_mode.pmMode & WSM_PSM_PS))
+			wsm_set_pm(hw_priv, &priv->powersave_mode,
+				priv->if_id);
+		hw_priv->scan.req = NULL;
+		bes2600_update_filtering(priv);
+		hw_priv->enable_advance_scan = false;
+		wsm_unlock_tx(hw_priv);
+		up(&hw_priv->conf_lock);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,8,0)
+		ieee80211_scan_completed(hw_priv->hw, &info);
+#else
+		ieee80211_scan_completed(hw_priv->hw, aborted);
+#endif
+		up(&hw_priv->scan.lock);
+	}
+}
+#endif
+
+void bes2600_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	if(hw_priv->scan.if_id == priv->if_id) {
+		bes2600_dbg(BES2600_DBG_SCAN, "cancel hw_scan on intf:%d\n", priv->if_id);
+
+		down(&hw_priv->conf_lock);
+		hw_priv->scan.req = NULL;
+		up(&hw_priv->conf_lock);
+
+		/* cancel scan operation */
+		wsm_stop_scan(hw_priv, priv->if_id);
+
+		/* wait scan operation end */
+		down(&hw_priv->scan.lock);
+		up(&hw_priv->scan.lock);
+	}
+}
+
+void bes2600_probe_work(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, scan.probe_work.work);
+	struct bes2600_vif *priv, *vif;
+	u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id);
+	struct bes2600_queue *queue = &hw_priv->tx_queue[queueId];
+	const struct bes2600_txpriv *txpriv;
+	struct wsm_tx *wsm;
+	struct wsm_template_frame frame = {
+		.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+	};
+	struct wsm_ssid ssids[1] = {{
+		.length = 0,
+	} };
+	struct wsm_scan_ch ch[1] = {{
+		.minChannelTime = 0,
+		.maxChannelTime = 10,
+	} };
+	struct wsm_scan scan = {
+		.scanType = WSM_SCAN_TYPE_FOREGROUND,
+		.numOfProbeRequests = 2,
+		.probeDelay = 0,
+		.numOfChannels = 1,
+		.ssids = ssids,
+		.ch = ch,
+	};
+	u8 *ies;
+	size_t ies_len;
+	int ret = 1;
+	int i;
+	wiphy_info(hw_priv->hw->wiphy, "[SCAN] Direct probe work.\n");
+
+	BUG_ON(queueId >= 4);
+	BUG_ON(!hw_priv->channel);
+
+	down(&hw_priv->conf_lock);
+	if (unlikely(down_trylock(&hw_priv->scan.lock))) {
+		/* Scan is already in progress. Requeue self. */
+		schedule();
+		queue_delayed_work(hw_priv->workqueue,
+					&hw_priv->scan.probe_work, HZ / 10);
+		up(&hw_priv->conf_lock);
+		return;
+	}
+
+	if (bes2600_queue_get_skb(queue,	hw_priv->pending_frame_id,
+			&frame.skb, &txpriv)) {
+		up(&hw_priv->scan.lock);
+		up(&hw_priv->conf_lock);
+		wsm_unlock_tx(hw_priv);
+		return;
+	}
+	priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id);
+	if (!priv) {
+		up(&hw_priv->scan.lock);
+		up(&hw_priv->conf_lock);
+		return;
+	}
+	wsm = (struct wsm_tx *)frame.skb->data;
+	scan.maxTransmitRate = wsm->maxTxRate;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+	scan.band = (hw_priv->channel->band == NL80211_BAND_5GHZ) ?
+#else
+	scan.band = (hw_priv->channel->band == IEEE80211_BAND_5GHZ) ?
+#endif
+		WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+	if (priv->join_status == BES2600_JOIN_STATUS_STA) {
+		scan.scanType = WSM_SCAN_TYPE_BACKGROUND;
+		scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+		if (priv->if_id)
+			scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1;
+		else
+			scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1;
+	}
+	bes2600_for_each_vif(hw_priv, vif, i) {
+		if (!vif)
+			continue;
+		if (vif->bss_loss_status > BES2600_BSS_LOSS_NONE)
+			scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND;
+	}
+	ch[0].number = hw_priv->channel->hw_value;
+
+	skb_pull(frame.skb, txpriv->offset);
+
+	ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
+	ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
+
+	if (ies_len) {
+		u8 *ssidie =
+			(u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
+		if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
+			u8 *nextie = &ssidie[2 + ssidie[1]];
+			/* Remove SSID from the IE list. It has to be provided
+			 * as a separate argument in bes2600_scan_start call */
+
+			/* Store SSID localy */
+			ssids[0].length = ssidie[1];
+			memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
+			scan.numOfSSIDs = 1;
+
+			/* Remove SSID from IE list */
+			ssidie[1] = 0;
+			memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
+			skb_trim(frame.skb, frame.skb->len - ssids[0].length);
+		}
+	}
+
+	if (priv->if_id == 0)
+		bes2600_remove_wps_p2p_ie(&frame);
+
+	/* FW bug: driver has to restart p2p-dev mode after scan */
+	if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) {
+		WARN_ON(1);
+		/*bes2600_disable_listening(priv);*/
+	}
+	ret = WARN_ON(wsm_set_template_frame(hw_priv, &frame,
+				priv->if_id));
+
+	hw_priv->scan.direct_probe = 1;
+	hw_priv->scan.if_id = priv->if_id;
+	if (!ret) {
+		wsm_flush_tx(hw_priv);
+		ret = WARN_ON(bes2600_scan_start(priv, &scan));
+	}
+	up(&hw_priv->conf_lock);
+
+	skb_push(frame.skb, txpriv->offset);
+	if (!ret)
+		IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
+#ifdef CONFIG_BES2600_TESTMODE
+		BUG_ON(bes2600_queue_remove(hw_priv, queue,
+				hw_priv->pending_frame_id));
+#else
+		BUG_ON(bes2600_queue_remove(queue, hw_priv->pending_frame_id));
+#endif
+
+	if (ret) {
+		hw_priv->scan.direct_probe = 0;
+		up(&hw_priv->scan.lock);
+		wsm_unlock_tx(hw_priv);
+	}
+
+	return;
+}
+
diff --git a/drivers/staging/bes2600/scan.h b/drivers/staging/bes2600/scan.h
new file mode 100644
index 000000000000..e50fa363b84f
--- /dev/null
+++ b/drivers/staging/bes2600/scan.h
@@ -0,0 +1,72 @@
+/*
+ * Scan interface for BES2600 mac80211 drivers
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef SCAN_H_INCLUDED
+#define SCAN_H_INCLUDED
+
+#include <linux/semaphore.h>
+#include "wsm.h"
+
+/* external */ struct sk_buff;
+/* external */ struct cfg80211_scan_request;
+/* external */ struct ieee80211_channel;
+/* external */ struct ieee80211_hw;
+/* external */ struct work_struct;
+
+struct bes2600_scan {
+	struct semaphore lock;
+	struct work_struct work;
+#ifdef ROAM_OFFLOAD
+	struct work_struct swork; /* scheduled scan work */
+	struct cfg80211_sched_scan_request *sched_req;
+#endif /*ROAM_OFFLOAD*/
+	struct delayed_work timeout;
+	struct cfg80211_scan_request *req;
+	struct ieee80211_channel **begin;
+	struct ieee80211_channel **curr;
+	struct ieee80211_channel **end;
+	struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS];
+	int output_power;
+	int n_ssids;
+	int status;
+	atomic_t in_progress;
+	/* Direct probe requests workaround */
+	struct delayed_work probe_work;
+	int direct_probe;
+	u8 if_id;
+};
+
+int bes2600_hw_scan(struct ieee80211_hw *hw,
+		   struct ieee80211_vif *vif,
+		   struct ieee80211_scan_request *hw_req);
+#ifdef ROAM_OFFLOAD
+int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
+			struct ieee80211_vif *vif,
+			struct cfg80211_sched_scan_request *req,
+			struct ieee80211_sched_scan_ies *ies);
+void bes2600_hw_sched_scan_stop(struct bes2600_common *priv);
+void bes2600_sched_scan_work(struct work_struct *work);
+#endif /*ROAM_OFFLOAD*/
+void bes2600_scan_work(struct work_struct *work);
+void bes2600_scan_timeout(struct work_struct *work);
+void bes2600_scan_complete_cb(struct bes2600_common *priv,
+				struct wsm_scan_complete *arg);
+void bes2600_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+
+/* ******************************************************************** */
+/* Raw probe requests TX workaround					*/
+void bes2600_probe_work(struct work_struct *work);
+#ifdef CONFIG_BES2600_TESTMODE
+/* Advance Scan Timer							*/
+void bes2600_advance_scan_timeout(struct work_struct *work);
+#endif
+
+#endif
diff --git a/drivers/staging/bes2600/sta.c b/drivers/staging/bes2600/sta.c
new file mode 100644
index 000000000000..eed1c85fc67c
--- /dev/null
+++ b/drivers/staging/bes2600/sta.c
@@ -0,0 +1,4514 @@
+/*
+ * Mac80211 STA API for BES2600 drivers
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/if_arp.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+#include <net/ndisc.h>
+
+#include "bes2600.h"
+#include "sta.h"
+#include "ap.h"
+#include "fwio.h"
+#include "bh.h"
+#include "debug.h"
+#include "wsm.h"
+#ifdef ROAM_OFFLOAD
+#include <net/netlink.h>
+#endif /*ROAM_OFFLOAD*/
+#ifdef CONFIG_BES2600_TESTMODE
+#include "bes_nl80211_testmode_msg.h"
+#include <net/netlink.h>
+#endif /* CONFIG_BES2600_TESTMODE */
+#include "net/mac80211.h"
+#include "bes2600_driver_mode.h"
+#include "bes_chardev.h"
+
+#include "epta_request.h"
+#include "epta_coex.h"
+
+#ifdef PLAT_ALLWINNER_R329
+#include "bes2600_factory.h"
+#endif
+
+#include "txrx_opt.h"
+
+#define WEP_ENCRYPT_HDR_SIZE    4
+#define WEP_ENCRYPT_TAIL_SIZE   4
+#define WPA_ENCRYPT_HDR_SIZE    8
+#define WPA_ENCRYPT_TAIL_SIZE   12
+#define WPA2_ENCRYPT_HDR_SIZE   8
+#define WPA2_ENCRYPT_TAIL_SIZE  8
+#define WAPI_ENCRYPT_HDR_SIZE   18
+#define WAPI_ENCRYPT_TAIL_SIZE  16
+#define MAX_ARP_REPLY_TEMPLATE_SIZE     120
+#define MAX_TCP_ALIVE_TEMPLATE_SIZE     256
+#ifdef CONFIG_BES2600_TESTMODE
+const int bes2600_1d_to_ac[8] = {
+	IEEE80211_AC_BE,
+	IEEE80211_AC_BK,
+	IEEE80211_AC_BK,
+	IEEE80211_AC_BE,
+	IEEE80211_AC_VI,
+	IEEE80211_AC_VI,
+	IEEE80211_AC_VO,
+	IEEE80211_AC_VO
+};
+
+/**
+ * enum bes2600_ac_numbers - AC numbers as used in bes2600
+ * @BES2600_AC_VO: voice
+ * @BES2600_AC_VI: video
+ * @BES2600_AC_BE: best effort
+ * @BES2600_AC_BK: background
+ */
+enum bes2600_ac_numbers {
+	BES2600_AC_VO	= 0,
+	BES2600_AC_VI	= 1,
+	BES2600_AC_BE	= 2,
+	BES2600_AC_BK	= 3,
+};
+
+int bes2600_testmode_reply(struct wiphy *wiphy, const void *data, int len);
+#endif /*CONFIG_BES2600_TESTMODE*/
+
+#ifdef IPV6_FILTERING
+#define MAX_NEIGHBOR_ADVERTISEMENT_TEMPLATE_SIZE 144
+#endif /*IPV6_FILTERING*/
+
+static inline void __bes2600_free_event_queue(struct list_head *list)
+{
+	while (!list_empty(list)) {
+		struct bes2600_wsm_event *event =
+			list_first_entry(list, struct bes2600_wsm_event,
+			link);
+		list_del(&event->link);
+		kfree(event);
+	}
+}
+
+#ifdef CONFIG_BES2600_TESTMODE
+/* User priority to WSM queue mapping */
+const int bes2600_priority_to_queueId[8] = {
+	WSM_QUEUE_BEST_EFFORT,
+	WSM_QUEUE_BACKGROUND,
+	WSM_QUEUE_BACKGROUND,
+	WSM_QUEUE_BEST_EFFORT,
+	WSM_QUEUE_VIDEO,
+	WSM_QUEUE_VIDEO,
+	WSM_QUEUE_VOICE,
+	WSM_QUEUE_VOICE
+};
+#endif /*CONFIG_BES2600_TESTMODE*/
+static inline void __bes2600_bf_configure(struct bes2600_vif *priv)
+{
+	priv->bf_table.numOfIEs = __cpu_to_le32(3);
+	priv->bf_table.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC;
+	priv->bf_table.entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+					WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+					WSM_BEACON_FILTER_IE_HAS_APPEARED;
+	priv->bf_table.entry[0].oui[0] = 0x50;
+	priv->bf_table.entry[0].oui[1] = 0x6F;
+	priv->bf_table.entry[0].oui[2] = 0x9A;
+
+	priv->bf_table.entry[1].ieId = WLAN_EID_ERP_INFO;
+	priv->bf_table.entry[1].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+					WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+					WSM_BEACON_FILTER_IE_HAS_APPEARED;
+
+	priv->bf_table.entry[2].ieId = WLAN_EID_HT_OPERATION;
+	priv->bf_table.entry[2].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+					WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+					WSM_BEACON_FILTER_IE_HAS_APPEARED;
+
+	priv->bf_control.enabled = WSM_BEACON_FILTER_ENABLE;
+}
+
+/* ******************************************************************** */
+/* STA API								*/
+
+int bes2600_start(struct ieee80211_hw *dev)
+{
+	struct bes2600_common *hw_priv = dev->priv;
+	int ret = 0;
+
+	bes2600_info(BES2600_DBG_STA, "bes2600_start\n");
+
+	if (!bes2600_chrdev_is_signal_mode()) {
+		return -1;
+	}
+
+	bes2600_pwr_prepare(hw_priv);
+
+	/* Assign Max SSIDs supported based on the firmware revision*/
+	if (hw_priv->hw_revision <= BES2600_HW_REV_CUT22) {
+		dev->wiphy->max_scan_ssids = 2; /* for backward compatibility */
+	}
+	down(&hw_priv->conf_lock);
+
+#ifndef CONFIG_BES2600_WLAN_USB
+#ifdef CONFIG_BES2600_WLAN_BES
+	ret = bes2600_wifi_start(hw_priv);
+	if (ret)
+		goto out;
+#endif
+#endif
+	tx_policy_init(hw_priv);
+
+
+#ifdef CONFIG_BES2600_TESTMODE
+	spin_lock_bh(&hw_priv->tsm_lock);
+	memset(&hw_priv->tsm_stats, 0, sizeof(struct bes_tsm_stats));
+	memset(&hw_priv->tsm_info, 0, sizeof(struct bes2600_tsm_info));
+	spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_BES2600_TESTMODE*/
+	memcpy(hw_priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
+	hw_priv->softled_state = 0;
+
+	atomic_inc(&hw_priv->netdevice_start);
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	coex_start(hw_priv);
+#endif
+
+	bes2600_info(BES2600_DBG_STA, "%s %pM.\n", __func__, hw_priv->mac_addr);
+	ret = bes2600_setup_mac(hw_priv);
+	if (WARN_ON(ret))
+		goto out;
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	bwifi_change_current_status(hw_priv, BWIFI_STATUS_IDLE);
+#endif
+
+out:
+	up(&hw_priv->conf_lock);
+	if(ret != 0)
+		bes2600_pwr_complete(hw_priv);
+	return ret;
+}
+
+void bes2600_stop(struct ieee80211_hw *dev)
+{
+	struct bes2600_common *hw_priv = dev->priv;
+	struct bes2600_vif *priv = NULL;
+	LIST_HEAD(list);
+	int i;
+
+	bes2600_info(BES2600_DBG_STA, "bes2600_stop\n");
+
+	atomic_dec(&hw_priv->netdevice_start);
+
+	wsm_lock_tx(hw_priv);
+
+	while (down_trylock(&hw_priv->scan.lock)) {
+		/* Scan is in progress. Force it to stop. */
+		hw_priv->scan.req = NULL;
+		schedule();
+	}
+	up(&hw_priv->scan.lock);
+
+	cancel_delayed_work_sync(&hw_priv->scan.probe_work);
+	cancel_delayed_work_sync(&hw_priv->scan.timeout);
+
+#ifdef CONFIG_BES2600_TESTMODE
+	cancel_delayed_work_sync(&hw_priv->advance_scan_timeout);
+#endif
+	flush_workqueue(hw_priv->workqueue);
+	del_timer_sync(&hw_priv->ba_timer);
+
+	down(&hw_priv->conf_lock);
+
+	hw_priv->softled_state = 0;
+	/* bes2600_set_leds(hw_priv); */
+
+	spin_lock(&hw_priv->event_queue_lock);
+	list_splice_init(&hw_priv->event_queue, &list);
+	spin_unlock(&hw_priv->event_queue_lock);
+	__bes2600_free_event_queue(&list);
+
+	for (i = 0; i < 4; i++)
+		bes2600_queue_clear(&hw_priv->tx_queue[i], CW12XX_ALL_IFS);
+
+	/* HACK! */
+	if (atomic_xchg(&hw_priv->tx_lock, 1) != 1)
+		bes2600_dbg(BES2600_DBG_STA, "[STA] TX is force-unlocked "
+			"due to stop request.\n");
+
+	bes2600_for_each_vif(hw_priv, priv, i) {
+		if (!priv)
+			continue;
+		priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+		priv->listening = false;
+		priv->delayed_link_loss = 0;
+		priv->join_status = BES2600_JOIN_STATUS_PASSIVE;
+		cancel_delayed_work_sync(&priv->join_timeout);
+		cancel_delayed_work_sync(&priv->bss_loss_work);
+		cancel_delayed_work_sync(&priv->connection_loss_work);
+		cancel_delayed_work_sync(&priv->link_id_gc_work);
+		del_timer_sync(&priv->mcast_timeout);
+	}
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	cancel_work_sync(&hw_priv->coex_work);
+	coex_stop(hw_priv);
+#endif
+
+#ifndef CONFIG_BES2600_WLAN_USB
+#ifdef CONFIG_BES2600_WLAN_BES
+	bes2600_wifi_stop(hw_priv);
+#endif
+#endif
+
+	tx_policy_deinit(hw_priv);
+
+	wsm_unlock_tx(hw_priv);
+
+	up(&hw_priv->conf_lock);
+	bes2600_pwr_complete(hw_priv);
+}
+
+
+int bes2600_add_interface(struct ieee80211_hw *dev,
+			 struct ieee80211_vif *vif)
+{
+	int ret;
+	struct bes2600_common *hw_priv = dev->priv;
+	struct bes2600_vif *priv;
+	struct bes2600_vif **drv_priv = (void *)vif->drv_priv;
+#ifndef P2P_MULTIVIF
+	int i;
+
+	if (atomic_read(&hw_priv->num_vifs) >= CW12XX_MAX_VIFS) {
+		bes2600_err(BES2600_DBG_STA, "%s %d.",__func__, __LINE__);
+		return -EOPNOTSUPP;
+	}
+#endif
+	bes2600_err(BES2600_DBG_STA, " !!! %s: type %d p2p %d addr %pM\n",
+		__func__, vif->type, vif->p2p, vif->addr);
+
+	priv = cw12xx_get_vif_from_ieee80211(vif);
+	atomic_set(&priv->enabled, 0);
+
+	*drv_priv = priv;
+	/* __le32 auto_calibration_mode = __cpu_to_le32(1); */
+
+	down(&hw_priv->conf_lock);
+
+	priv->mode = vif->type;
+
+	spin_lock(&hw_priv->vif_list_lock);
+	if (atomic_read(&hw_priv->num_vifs) < CW12XX_MAX_VIFS) {
+#ifdef P2P_MULTIVIF
+		if (!memcmp(vif->addr, hw_priv->addresses[0].addr, ETH_ALEN)) {
+			priv->if_id = 0;
+		} else if (!memcmp(vif->addr, hw_priv->addresses[1].addr,
+			ETH_ALEN)) {
+			priv->if_id = 2;
+		} else if (!memcmp(vif->addr, hw_priv->addresses[2].addr,
+			ETH_ALEN)) {
+			priv->if_id = 1;
+		}
+		bes2600_info(BES2600_DBG_STA, "%s: if_id %d mac %pM\n",
+				__func__, priv->if_id, vif->addr);
+#else
+		for (i = 0; i < CW12XX_MAX_VIFS; i++)
+			if (!memcmp(vif->addr, hw_priv->addresses[i].addr,
+						ETH_ALEN))
+				break;
+		if (i == CW12XX_MAX_VIFS) {
+			spin_unlock(&hw_priv->vif_list_lock);
+			up(&hw_priv->conf_lock);
+			return -EINVAL;
+		}
+		priv->if_id = i;
+#endif
+		priv->hw_priv = hw_priv;
+		priv->hw = dev;
+		priv->vif = vif;
+	} else {
+		spin_unlock(&hw_priv->vif_list_lock);
+		up(&hw_priv->conf_lock);
+		return -EOPNOTSUPP;
+	}
+	spin_unlock(&hw_priv->vif_list_lock);
+	/* TODO:COMBO :Check if MAC address matches the one expected by FW */
+	memcpy(hw_priv->mac_addr, vif->addr, ETH_ALEN);
+
+	/* Enable auto-calibration */
+	/* Exception in subsequent channel switch; disabled.
+	WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
+		&auto_calibration_mode, sizeof(auto_calibration_mode)));
+	*/
+	bes2600_info(BES2600_DBG_STA, "[STA] Interface ID:%d of type:%d added\n",
+		   priv->if_id, priv->mode);
+
+	up(&hw_priv->conf_lock);
+
+	bes2600_vif_setup(priv);
+
+	ret = WARN_ON(bes2600_setup_mac_pvif(priv));
+
+	return ret;
+}
+
+void bes2600_remove_interface(struct ieee80211_hw *dev,
+			     struct ieee80211_vif *vif)
+{
+	struct bes2600_common *hw_priv = dev->priv;
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct wsm_reset reset = {
+		.reset_statistics = true,
+	};
+	int i;
+	bool is_htcapie = false;
+	struct bes2600_vif *tmp_priv;
+
+	bes2600_info(BES2600_DBG_STA, " !!! %s: type %d p2p %d addr %pM\n",
+		__func__, vif->type, vif->p2p, vif->addr);
+	atomic_set(&priv->enabled, 0);
+	down(&hw_priv->scan.lock);
+	down(&hw_priv->conf_lock);
+	bes2600_tx_queues_lock(hw_priv);
+	wsm_lock_tx(hw_priv);
+	switch (priv->join_status) {
+	case BES2600_JOIN_STATUS_STA:
+		wsm_lock_tx(hw_priv);
+		if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
+			wsm_unlock_tx(hw_priv);
+		break;
+	case BES2600_JOIN_STATUS_AP:
+		for (i = 0; priv->link_id_map; ++i) {
+			if (priv->link_id_map & BIT(i)) {
+				cw12xx_unmap_link(priv, i);
+				priv->link_id_map &= ~BIT(i);
+			}
+		}
+		memset(priv->link_id_db, 0,
+				sizeof(priv->link_id_db));
+		priv->sta_asleep_mask = 0;
+		priv->enable_beacon = false;
+		priv->tx_multicast = false;
+		priv->aid0_bit_set = false;
+		priv->buffered_multicasts = false;
+		priv->pspoll_mask = 0;
+		reset.link_id = 0;
+		wsm_reset(hw_priv, &reset, priv->if_id);
+		bes2600_for_each_vif(hw_priv, tmp_priv, i) {
+#ifdef P2P_MULTIVIF
+			if ((i == (CW12XX_MAX_VIFS - 1)) || !tmp_priv)
+#else
+			if (!tmp_priv)
+#endif
+				continue;
+			if ((tmp_priv->join_status == BES2600_JOIN_STATUS_STA) && tmp_priv->htcap)
+				is_htcapie = true;
+		}
+
+		if (is_htcapie) {
+			hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11N_THROTTLE;
+			hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11N_THROTTLE;
+			bes2600_info(BES2600_DBG_STA, "AP REMOVE HTCAP 11N %d\n",hw_priv->vif0_throttle);
+		} else {
+			hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
+			hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
+			bes2600_info(BES2600_DBG_STA, "AP REMOVE 11BG %d\n",hw_priv->vif0_throttle);
+		}
+		bes2600_pwr_clear_busy_event(hw_priv, BES2600_JOIN_STATUS_AP);
+		break;
+	case BES2600_JOIN_STATUS_MONITOR:
+		bes2600_disable_listening(priv);
+		break;
+	default:
+		break;
+	}
+	/* TODO:COMBO: Change Queue Module */
+	if (!__bes2600_flush(hw_priv, false, priv->if_id))
+		wsm_unlock_tx(hw_priv);
+
+	cancel_delayed_work_sync(&priv->bss_loss_work);
+	cancel_delayed_work_sync(&priv->connection_loss_work);
+	cancel_delayed_work_sync(&priv->link_id_gc_work);
+	cancel_delayed_work_sync(&priv->join_timeout);
+	cancel_delayed_work_sync(&priv->set_cts_work);
+	cancel_delayed_work_sync(&priv->pending_offchanneltx_work);
+
+	del_timer_sync(&priv->mcast_timeout);
+	/* TODO:COMBO: May be reset of these variables "delayed_link_loss and
+	 * join_status to default can be removed as dev_priv will be freed by
+	 * mac80211 */
+	priv->delayed_link_loss = 0;
+	priv->join_status = BES2600_JOIN_STATUS_PASSIVE;
+	wsm_unlock_tx(hw_priv);
+
+	if ((priv->if_id ==1) && (priv->mode == NL80211_IFTYPE_AP
+		|| priv->mode == NL80211_IFTYPE_P2P_GO)) {
+		hw_priv->is_go_thru_go_neg = false;
+	}
+	spin_lock(&hw_priv->vif_list_lock);
+	spin_lock(&priv->vif_lock);
+	hw_priv->vif_list[priv->if_id] = NULL;
+	hw_priv->if_id_slot &= (~BIT(priv->if_id));
+	atomic_dec(&hw_priv->num_vifs);
+	if (atomic_read(&hw_priv->num_vifs) == 0) {
+		bes2600_free_keys(hw_priv);
+		memset(hw_priv->mac_addr, 0, ETH_ALEN);
+	}
+	spin_unlock(&priv->vif_lock);
+	spin_unlock(&hw_priv->vif_list_lock);
+	priv->listening = false;
+
+	bes2600_debug_release_priv(priv);
+
+	bes2600_tx_queues_unlock(hw_priv);
+	up(&hw_priv->conf_lock);
+
+	if (atomic_read(&hw_priv->num_vifs) == 0)
+		flush_workqueue(hw_priv->workqueue);
+	memset(priv, 0, sizeof(struct bes2600_vif));
+	up(&hw_priv->scan.lock);
+}
+
+int bes2600_change_interface(struct ieee80211_hw *dev,
+				struct ieee80211_vif *vif,
+				enum nl80211_iftype new_type,
+				bool p2p)
+{
+	int ret = 0;
+	bes2600_info(BES2600_DBG_STA, " !!! %s: type %d (%d), p2p %d (%d)\n",
+		__func__, new_type, vif->type, p2p, vif->p2p);
+	if (new_type != vif->type || vif->p2p != p2p) {
+		bes2600_remove_interface(dev, vif);
+		vif->type = new_type;
+		vif->p2p = p2p;
+		ret = bes2600_add_interface(dev, vif);
+	}
+
+	return ret;
+}
+
+int bes2600_config(struct ieee80211_hw *dev, u32 changed)
+{
+	int ret = 0;
+	struct bes2600_common *hw_priv = dev->priv;
+	struct ieee80211_conf *conf = &dev->conf;
+#ifdef CONFIG_BES2600_TESTMODE
+	int max_power_level = 0;
+	int min_power_level = 0;
+#endif
+	/* TODO:COMBO: adjust to multi vif interface
+	 * IEEE80211_CONF_CHANGE_IDLE is still handled per bes2600_vif*/
+	int if_id = 0;
+	struct bes2600_vif *priv;
+
+	bes2600_info(BES2600_DBG_STA, "CONFIG CHANGED:	%08x\n", changed);
+
+	if (changed &
+		(IEEE80211_CONF_CHANGE_MONITOR|IEEE80211_CONF_CHANGE_IDLE)) {
+		/* TBD: It looks like it's transparent
+		 * there's a monitor interface present -- use this
+		 * to determine for example whether to calculate
+		 * timestamps for packets or not, do not use instead
+		 * of filter flags! */
+		bes2600_info(BES2600_DBG_STA,
+				"ignore IEEE80211_CONF_CHANGE_MONITOR (%d)"
+				"IEEE80211_CONF_CHANGE_IDLE (%d)\n",
+			(changed & IEEE80211_CONF_CHANGE_MONITOR) ? 1 : 0,
+			(changed & IEEE80211_CONF_CHANGE_IDLE) ? 1 : 0);
+		return ret;
+	}
+
+	down(&hw_priv->scan.lock);
+	down(&hw_priv->conf_lock);
+	priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id);
+	/* TODO: IEEE80211_CONF_CHANGE_QOS */
+	/* TODO:COMBO:Change when support is available mac80211*/
+	if (changed & IEEE80211_CONF_CHANGE_POWER) {
+		/*hw_priv->output_power = conf->power_level;*/
+		bes2600_info(BES2600_DBG_STA, "Output power ++%d\n",conf->power_level);
+		hw_priv->output_power = 20;
+		bes2600_info(BES2600_DBG_STA, "Output power --%d\n",hw_priv->output_power);
+#ifdef CONFIG_BES2600_TESTMODE
+		/* Testing if Power Level to set is out of device power range */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+		if (conf->chandef.chan->band == NL80211_BAND_2GHZ) {
+#else
+		if (conf->chandef.chan->band == IEEE80211_BAND_2GHZ) {
+#endif
+			max_power_level = hw_priv->txPowerRange[0].max_power_level;
+			min_power_level = hw_priv->txPowerRange[0].min_power_level;
+		} else {
+			max_power_level = hw_priv->txPowerRange[1].max_power_level;
+			min_power_level = hw_priv->txPowerRange[1].min_power_level;
+		}
+		if (hw_priv->output_power > max_power_level)
+			hw_priv->output_power = max_power_level;
+		else if (hw_priv->output_power < min_power_level)
+			hw_priv->output_power = min_power_level;
+#endif /* CONFIG_BES2600_TESTMODE */
+		bes2600_info(BES2600_DBG_STA, "[STA] TX power: %d\n",
+				hw_priv->output_power);
+		WARN_ON(wsm_set_output_power(hw_priv,
+					     hw_priv->output_power * 10,
+					     if_id));
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+		/* Switch Channel commented for CC Mode */
+		struct ieee80211_channel *ch = conf->chandef.chan;
+
+		bes2600_info(BES2600_DBG_STA, "[STA] Freq %d (wsm ch: %d, type: %d).\n",
+			   ch->center_freq, ch->hw_value,
+			   cfg80211_get_chandef_type(&conf->chandef));
+		/* Earlier there was a call to __bes2600_flush().
+		   Removed as deemed unnecessary */
+
+		hw_priv->channel = ch;
+
+	}
+
+	if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+		bes2600_info(BES2600_DBG_STA, "[STA] Retry limits: %d (long), %d (short).\n",
+			 conf->long_frame_max_tx_count,
+			 conf->short_frame_max_tx_count);
+		spin_lock_bh(&hw_priv->tx_policy_cache.lock);
+		hw_priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
+		hw_priv->short_frame_max_tx_count =
+			(conf->short_frame_max_tx_count < 0x0F) ?
+			conf->short_frame_max_tx_count : 0x0F;
+		hw_priv->hw->max_rate_tries = hw_priv->short_frame_max_tx_count;
+		spin_unlock_bh(&hw_priv->tx_policy_cache.lock);
+	}
+	up(&hw_priv->conf_lock);
+	up(&hw_priv->scan.lock);
+
+	return ret;
+}
+
+void bes2600_update_filtering(struct bes2600_vif *priv)
+{
+	int ret;
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	bool bssid_filtering = !priv->rx_filter.bssid;
+	static struct wsm_beacon_filter_control bf_disabled = {
+		.enabled = 0,
+		.bcn_count = 1,
+	};
+	bool ap_mode = 0;
+	static struct wsm_beacon_filter_table bf_table_auto = {
+		.numOfIEs = __cpu_to_le32(2),
+		.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC,
+		.entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+					WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+					WSM_BEACON_FILTER_IE_HAS_APPEARED,
+		.entry[0].oui[0] = 0x50,
+		.entry[0].oui[1] = 0x6F,
+		.entry[0].oui[2] = 0x9A,
+
+		.entry[1].ieId = WLAN_EID_HT_OPERATION,
+		.entry[1].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+					WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+					WSM_BEACON_FILTER_IE_HAS_APPEARED,
+	};
+	static struct wsm_beacon_filter_control bf_auto = {
+		.enabled = WSM_BEACON_FILTER_ENABLE |
+			WSM_BEACON_FILTER_AUTO_ERP,
+		.bcn_count = 1,
+	};
+	bf_auto.bcn_count = priv->bf_control.bcn_count;
+
+	if (priv->join_status == BES2600_JOIN_STATUS_PASSIVE)
+		return;
+	else if (priv->join_status == BES2600_JOIN_STATUS_MONITOR)
+		bssid_filtering = false;
+
+	if (priv->vif && (priv->vif->type == NL80211_IFTYPE_AP))
+		ap_mode = true;
+	/*
+	* When acting as p2p client being connected to p2p GO, in order to
+	* receive frames from a different p2p device, turn off bssid filter.
+	*
+	* WARNING: FW dependency!
+	* This can only be used with FW WSM371 and its successors.
+	* In that FW version even with bssid filter turned off,
+	* device will block most of the unwanted frames.
+	*/
+	if (priv->vif && priv->vif->p2p)
+		bssid_filtering = false;
+
+	ret = wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id);
+	if (!ret && !ap_mode) {
+		if (priv->vif) {
+			if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type)
+				ret = wsm_set_beacon_filter_table(hw_priv, &priv->bf_table,
+							priv->if_id);
+			else
+				ret = wsm_set_beacon_filter_table(hw_priv, &bf_table_auto,
+							priv->if_id);
+		} else
+			WARN_ON(1);
+	}
+	if (!ret && !ap_mode) {
+		if (priv->disable_beacon_filter)
+			ret = wsm_beacon_filter_control(hw_priv,
+					&bf_disabled, priv->if_id);
+		else {
+			if (priv->vif) {
+				if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type)
+					ret = wsm_beacon_filter_control(hw_priv,
+						&priv->bf_control, priv->if_id);
+				else
+					ret = wsm_beacon_filter_control(hw_priv,
+						&bf_auto, priv->if_id);
+			} else
+				WARN_ON(1);
+		}
+	}
+
+	if (!ret)
+		ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering,
+					priv->if_id);
+
+	if (!ret) {
+		ret = wsm_set_multicast_filter(hw_priv, &priv->multicast_filter,
+						priv->if_id);
+	}
+
+	if (ret)
+		wiphy_err(priv->hw->wiphy,
+				"%s: Update filtering failed: %d.\n",
+				__func__, ret);
+	return;
+}
+
+void bes2600_update_filtering_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif,
+		update_filtering_work);
+
+	bes2600_update_filtering(priv);
+}
+
+void bes2600_set_beacon_wakeup_period_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif,
+		set_beacon_wakeup_period_work);
+
+	WARN_ON(wsm_set_beacon_wakeup_period(priv->hw_priv,
+		priv->beacon_int * priv->join_dtim_period >
+		MAX_BEACON_SKIP_TIME_MS ? 1 :
+		priv->join_dtim_period, 0, priv->if_id));
+}
+
+u64 bes2600_prepare_multicast(struct ieee80211_hw *hw,
+			     struct netdev_hw_addr_list *mc_list)
+{
+	static u8 broadcast_ipv6[ETH_ALEN] = {
+		0x33, 0x33, 0x00, 0x00, 0x00, 0x01
+	};
+	static u8 broadcast_ipv4[ETH_ALEN] = {
+		0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
+	};
+	struct bes2600_common *hw_priv = hw->priv;
+	struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
+	struct netdev_hw_addr *ha;
+	int count = 0;
+
+	if(priv == NULL){
+		bes2600_info(BES2600_DBG_STA, "wlan0 removed before p2p-device\n");
+		return netdev_hw_addr_list_count(mc_list);
+	}
+
+	/* Disable multicast filtering */
+	priv->has_multicast_subscription = false;
+	memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
+
+	if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
+		return 0;
+
+	/* Enable if requested */
+	netdev_hw_addr_list_for_each(ha, mc_list) {
+		bes2600_info(BES2600_DBG_STA, "[STA] multicast: %pM\n", ha->addr);
+		memcpy(&priv->multicast_filter.macAddress[count],
+		       ha->addr, ETH_ALEN);
+		if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) &&
+				memcmp(ha->addr, broadcast_ipv6, ETH_ALEN))
+			priv->has_multicast_subscription = true;
+		count++;
+	}
+
+	if (count) {
+		priv->multicast_filter.enable = __cpu_to_le32(1);
+		priv->multicast_filter.numOfAddresses = __cpu_to_le32(count);
+	}
+
+	return netdev_hw_addr_list_count(mc_list);
+}
+
+void bes2600_configure_filter(struct ieee80211_hw *hw,
+			     unsigned int changed_flags,
+			     unsigned int *total_flags,
+			     u64 multicast)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
+#if 0
+	bool listening = !!(*total_flags &
+			(FIF_PROMISC_IN_BSS |
+			 FIF_OTHER_BSS |
+			 FIF_BCN_PRBRESP_PROMISC |
+			 FIF_PROBE_REQ));
+#endif
+	*total_flags &= FIF_OTHER_BSS |
+			FIF_FCSFAIL |
+			FIF_BCN_PRBRESP_PROMISC |
+			FIF_PROBE_REQ;
+
+	if(priv != NULL){
+		down(&hw_priv->scan.lock);
+		down(&hw_priv->conf_lock);
+
+		priv->rx_filter.promiscuous = 0;
+		priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS |
+				FIF_PROBE_REQ)) ? 1 : 0;
+		priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
+		priv->bf_control.bcn_count = (*total_flags &
+				(FIF_BCN_PRBRESP_PROMISC | FIF_PROBE_REQ)) ? 1 : 0;
+#if 0
+		if (priv->listening ^ listening) {
+			priv->listening = listening;
+			wsm_lock_tx(hw_priv);
+			bes2600_update_listening(priv, listening);
+			wsm_unlock_tx(hw_priv);
+		}
+#endif
+		bes2600_update_filtering(priv);
+		up(&hw_priv->conf_lock);
+		up(&hw_priv->scan.lock);
+	}
+}
+
+int bes2600_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+		u16 queue, const struct ieee80211_tx_queue_params *params)
+{
+	struct bes2600_common *hw_priv = dev->priv;
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	int ret = 0;
+	/* To prevent re-applying PM request OID again and again*/
+	bool old_uapsdFlags;
+
+	if (WARN_ON(!priv))
+		return -EOPNOTSUPP;
+
+#ifdef P2P_MULTIVIF
+	if (priv->if_id == CW12XX_GENERIC_IF_ID)
+		return 0;
+#endif
+	down(&hw_priv->conf_lock);
+
+	if (queue < dev->queues) {
+		old_uapsdFlags = priv->uapsd_info.uapsdFlags;
+
+		WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
+		ret = wsm_set_tx_queue_params(hw_priv,
+				&priv->tx_queue_params.params[queue],
+				queue, priv->if_id);
+		if (ret) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+                WSM_EDCA_SET(&priv->edca, queue, params->aifs,
+                                params->cw_min, params->cw_max, params->txop, 0xc8,
+                                params->uapsd);
+		ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id);
+		if (ret) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		if (priv->mode == NL80211_IFTYPE_STATION) {
+			ret = bes2600_set_uapsd_param(priv, &priv->edca);
+			#if 0
+			if (!ret && priv->setbssparams_done &&
+				(priv->join_status == BES2600_JOIN_STATUS_STA) &&
+				(old_uapsdFlags != priv->uapsd_info.uapsdFlags))
+				bes2600_set_pm(priv, &priv->powersave_mode);
+			#endif
+		}
+	} else
+		ret = -EINVAL;
+
+out:
+	up(&hw_priv->conf_lock);
+
+	return ret;
+}
+
+int bes2600_get_stats(struct ieee80211_hw *dev,
+		     struct ieee80211_low_level_stats *stats)
+{
+	struct bes2600_common *hw_priv = dev->priv;
+
+	memcpy(stats, &hw_priv->stats, sizeof(*stats));
+	return 0;
+}
+
+/*
+int bes2600_get_tx_stats(struct ieee80211_hw *dev,
+			struct ieee80211_tx_queue_stats *stats)
+{
+	int i;
+	struct bes2600_common *priv = dev->priv;
+
+	for (i = 0; i < dev->queues; ++i)
+		bes2600_queue_get_stats(&priv->tx_queue[i], &stats[i]);
+
+	return 0;
+}
+*/
+
+int bes2600_set_pm(struct bes2600_vif *priv, const struct wsm_set_pm *arg)
+{
+	struct wsm_set_pm pm = *arg;
+
+	if (priv->uapsd_info.uapsdFlags != 0)
+		pm.pmMode &= ~WSM_PSM_FAST_PS_FLAG;
+
+	if (memcmp(&pm, &priv->firmware_ps_mode,
+			sizeof(struct wsm_set_pm))) {
+		priv->firmware_ps_mode = pm;
+		return wsm_set_pm(priv->hw_priv, &pm,
+				priv->if_id);
+	} else {
+		return 0;
+	}
+}
+
+int bes2600_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+		   struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+		   struct ieee80211_key_conf *key)
+{
+	int ret = -EOPNOTSUPP;
+	struct bes2600_common *hw_priv = dev->priv;
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct wsm_protected_mgmt_policy mgmt_policy;
+
+#ifdef P2P_MULTIVIF
+	WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
+#endif
+	memset(&mgmt_policy, 0, sizeof(mgmt_policy));
+	down(&hw_priv->conf_lock);
+	bes2600_info(BES2600_DBG_STA, "%s, cmd:%d cipher:0x%08x idx:%d\n",
+			__func__, cmd, key->cipher, key->keyidx);
+
+	if (cmd == SET_KEY) {
+		u8 *peer_addr = NULL;
+		int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+			1 : 0;
+		int idx = bes2600_alloc_key(hw_priv);
+		struct wsm_add_key *wsm_key = &hw_priv->keys[idx];
+
+		if (idx < 0) {
+			ret = -EINVAL;
+			goto finally;
+		}
+
+		BUG_ON(pairwise && !sta);
+		if (sta)
+			peer_addr = sta->addr;
+
+		key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE |
+			      IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
+
+		/* only need to update cipher type of pairwise key */
+		if (pairwise)
+			priv->cipherType = key->cipher;
+
+		switch (key->cipher) {
+		case WLAN_CIPHER_SUITE_WEP40:
+		case WLAN_CIPHER_SUITE_WEP104:
+			if (key->keylen > 16) {
+				bes2600_free_key(hw_priv, idx);
+				ret = -EINVAL;
+				goto finally;
+			}
+
+			if (pairwise) {
+				wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
+				memcpy(wsm_key->wepPairwiseKey.peerAddress,
+					 peer_addr, ETH_ALEN);
+				memcpy(wsm_key->wepPairwiseKey.keyData,
+					&key->key[0], key->keylen);
+				wsm_key->wepPairwiseKey.keyLength = key->keylen;
+			} else {
+				wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
+				memcpy(wsm_key->wepGroupKey.keyData,
+					&key->key[0], key->keylen);
+				wsm_key->wepGroupKey.keyLength = key->keylen;
+				wsm_key->wepGroupKey.keyId = key->keyidx;
+			}
+			break;
+		case WLAN_CIPHER_SUITE_TKIP:
+			if (pairwise) {
+				wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
+				memcpy(wsm_key->tkipPairwiseKey.peerAddress,
+					peer_addr, ETH_ALEN);
+				memcpy(wsm_key->tkipPairwiseKey.tkipKeyData,
+					&key->key[0],  16);
+				memcpy(wsm_key->tkipPairwiseKey.txMicKey,
+					&key->key[16],  8);
+				memcpy(wsm_key->tkipPairwiseKey.rxMicKey,
+					&key->key[24],  8);
+			} else {
+				size_t mic_offset =
+					(priv->mode == NL80211_IFTYPE_AP) ?
+					16 : 24;
+				wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
+				memcpy(wsm_key->tkipGroupKey.tkipKeyData,
+					&key->key[0],  16);
+				memcpy(wsm_key->tkipGroupKey.rxMicKey,
+					&key->key[mic_offset],  8);
+
+				/* TODO: Where can I find TKIP SEQ? */
+				memset(wsm_key->tkipGroupKey.rxSeqCounter,
+					0,		8);
+				wsm_key->tkipGroupKey.keyId = key->keyidx;
+
+			}
+			break;
+		case WLAN_CIPHER_SUITE_CCMP:
+			if (pairwise) {
+				wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
+				memcpy(wsm_key->aesPairwiseKey.peerAddress,
+					peer_addr, ETH_ALEN);
+				memcpy(wsm_key->aesPairwiseKey.aesKeyData,
+					&key->key[0],  16);
+			} else {
+				wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
+				memcpy(wsm_key->aesGroupKey.aesKeyData,
+					&key->key[0],  16);
+				/* TODO: Where can I find AES SEQ? */
+				memset(wsm_key->aesGroupKey.rxSeqCounter,
+					0,              8);
+				wsm_key->aesGroupKey.keyId = key->keyidx;
+			}
+			break;
+		case WLAN_CIPHER_SUITE_AES_CMAC:
+			wsm_key->type = WSM_KEY_TYPE_IGTK_GROUP;
+			memcpy(wsm_key->igtkGroupKey.IGTKKeyData, &key->key[0], 16);
+			memset(wsm_key->igtkGroupKey.IPN, 0, 8);
+			wsm_key->igtkGroupKey.keyId = key->keyidx;
+			mgmt_policy.protectedMgmtEnable = 1;
+			wsm_set_protected_mgmt_policy(hw_priv, &mgmt_policy, priv->if_id);
+			break;
+#ifdef CONFIG_BES2600_WAPI_SUPPORT
+		case WLAN_CIPHER_SUITE_SMS4:
+			if (pairwise) {
+				wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
+				memcpy(wsm_key->wapiPairwiseKey.peerAddress,
+					peer_addr, ETH_ALEN);
+				memcpy(wsm_key->wapiPairwiseKey.wapiKeyData,
+					&key->key[0],  16);
+				memcpy(wsm_key->wapiPairwiseKey.micKeyData,
+					&key->key[16], 16);
+				wsm_key->wapiPairwiseKey.keyId = key->keyidx;
+			} else {
+				wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
+				memcpy(wsm_key->wapiGroupKey.wapiKeyData,
+					&key->key[0],  16);
+				memcpy(wsm_key->wapiGroupKey.micKeyData,
+					&key->key[16], 16);
+				wsm_key->wapiGroupKey.keyId = key->keyidx;
+			}
+			break;
+#endif /* CONFIG_BES2600_WAPI_SUPPORT */
+		default:
+			WARN_ON(1);
+			bes2600_free_key(hw_priv, idx);
+			ret = -EOPNOTSUPP;
+			goto finally;
+		}
+
+#ifdef CONFIG_BES2600_WAPI_SUPPORT
+		/* workaround: beacuse bes2600 can't install two unicast keys at the same time,
+		 * remove last unicast key when install new unicast key.
+		 */
+		if(key->cipher == WLAN_CIPHER_SUITE_SMS4 &&
+			pairwise && hw_priv->last_ins_wapi_usk_id != -1) {
+			struct wsm_remove_key last_wsm_key = {
+				.entryIndex = hw_priv->last_ins_wapi_usk_id,
+			};
+			bes2600_free_key(hw_priv, last_wsm_key.entryIndex);
+			wsm_remove_key(hw_priv, &last_wsm_key, priv->if_id);
+			hw_priv->last_del_wapi_usk_id = hw_priv->last_ins_wapi_usk_id;
+		}
+#endif
+
+		ret = WARN_ON(wsm_add_key(hw_priv, wsm_key, priv->if_id));
+		if (!ret) {
+			key->hw_key_idx = idx;
+#ifdef CONFIG_BES2600_WAPI_SUPPORT
+			if(wsm_key->type == WSM_KEY_TYPE_WAPI_PAIRWISE)
+				hw_priv->last_ins_wapi_usk_id = idx;
+#endif
+		} else {
+			bes2600_free_key(hw_priv, idx);
+		}
+
+		if (!ret && (pairwise
+			|| wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT)
+			&& (priv->filter4.enable & 0x2))
+				bes2600_set_arpreply(dev, vif);
+#ifdef IPV6_FILTERING
+        	if (!ret && (pairwise
+			|| wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT)
+			&& (priv->filter6.enable & 0x2))
+				bes2600_set_na(dev, vif);
+#endif /*IPV6_FILTERING*/
+
+	} else if (cmd == DISABLE_KEY) {
+		struct wsm_remove_key wsm_key = {
+			.entryIndex = key->hw_key_idx,
+		};
+
+		if (wsm_key.entryIndex > WSM_KEY_MAX_IDX) {
+			ret = -EINVAL;
+			goto finally;
+		}
+
+#ifdef CONFIG_BES2600_WAPI_SUPPORT
+		/* workaround: beacuse bes2600 can't install two unicast keys at the same time,
+		 * remove last unicast key when install new unicast key.
+		 */
+		if(hw_priv->last_del_wapi_usk_id == key->hw_key_idx) {
+			hw_priv->last_del_wapi_usk_id = -1;
+			ret = 0;
+			goto finally;
+		} else if (hw_priv->last_ins_wapi_usk_id == key->hw_key_idx) {
+			hw_priv->last_ins_wapi_usk_id = -1;
+		}
+#endif
+
+		bes2600_free_key(hw_priv, wsm_key.entryIndex);
+		ret = wsm_remove_key(hw_priv, &wsm_key, priv->if_id);
+	} else {
+		BUG_ON("Unsupported command");
+	}
+
+finally:
+	up(&hw_priv->conf_lock);
+	return ret;
+}
+
+void bes2600_wep_key_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif , wep_key_work);
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id);
+	struct bes2600_queue *queue = &hw_priv->tx_queue[queueId];
+	__le32 wep_default_key_id = __cpu_to_le32(
+		priv->wep_default_key_id);
+
+	BUG_ON(queueId >= 4);
+
+	bes2600_dbg(BES2600_DBG_STA, "[STA] Setting default WEP key: %d\n",
+		priv->wep_default_key_id);
+	wsm_flush_tx(hw_priv);
+	WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
+		&wep_default_key_id, sizeof(wep_default_key_id), priv->if_id));
+#ifdef CONFIG_BES2600_TESTMODE
+	bes2600_queue_requeue(hw_priv, queue,
+		hw_priv->pending_frame_id, true);
+#else
+	bes2600_queue_requeue(queue, hw_priv->pending_frame_id, true);
+#endif
+	wsm_unlock_tx(hw_priv);
+}
+
+int bes2600_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	int ret;
+	__le32 val32;
+	bes2600_dbg(BES2600_DBG_TXRX_OPT,"set RTS threshold = %d\n\r", hw_priv->rtsvalue);
+
+	spin_lock(&hw_priv->rtsvalue_lock);
+	if (hw_priv->rtsvalue != value) {
+		hw_priv->rtsvalue = value;
+		//bes2600_dbg(BES2600_DBG_TXRX_OPT,"set RTS value = %d\n\r", hw_priv->rtsvalue);
+	} else {
+		spin_unlock(&hw_priv->rtsvalue_lock);
+		return 0;
+	}
+	spin_unlock(&hw_priv->rtsvalue_lock);
+
+	if (value != (u32) -1)
+		val32 = __cpu_to_le32(value);
+	else
+		val32 = 0; /* disabled */
+	/* mutex_lock(&priv->conf_mutex); */
+	ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
+		&val32, sizeof(val32), 0));
+	/* mutex_unlock(&priv->conf_mutex); */
+
+	return ret;
+}
+
+/* TODO: COMBO: Flush only a particular interface specific parts */
+int __bes2600_flush(struct bes2600_common *hw_priv, bool drop, int if_id)
+{
+	int i, ret;
+	struct bes2600_vif *priv =
+		__cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
+
+	/* clear tx queue directly if there is a bus error */
+	if(hw_priv->bh_error)
+		drop = true;
+
+	for (;;) {
+		/* TODO: correct flush handling is required when dev_stop.
+		 * Temporary workaround: 2s
+		 */
+		if (drop) {
+			for (i = 0; i < 4; ++i)
+				bes2600_queue_clear(&hw_priv->tx_queue[i],
+					if_id);
+		} else {
+			ret = wait_event_timeout(
+				hw_priv->tx_queue_stats.wait_link_id_empty,
+				bes2600_queue_stats_is_empty(
+					&hw_priv->tx_queue_stats, -1, if_id),
+#ifdef FPGA_SETUP
+				10 * HZ);
+#else
+				2 * HZ);
+#endif
+		}
+
+		if (!drop && unlikely(ret <= 0)) {
+			bes2600_err(BES2600_DBG_STA, "__bes2600_flush: ETIMEDOUT.....\n");
+			ret = -ETIMEDOUT;
+			break;
+		} else {
+			ret = 0;
+		}
+
+		wsm_vif_lock_tx(priv);
+		if (unlikely(!bes2600_queue_stats_is_empty(
+				&hw_priv->tx_queue_stats, -1, if_id))) {
+			/* Highly unlekely: WSM requeued frames. */
+			wsm_unlock_tx(hw_priv);
+			continue;
+		}
+		break;
+	}
+	return ret;
+}
+
+void bes2600_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		  u32 queues, bool drop)
+{
+	struct bes2600_vif *priv = NULL;
+	struct bes2600_common *hw_priv = hw->priv;
+	unsigned int ret = 0;
+	int i;
+
+	/* get queue status */
+	if (vif) {
+		priv = cw12xx_get_vif_from_ieee80211(vif);
+		ret |= !bes2600_queue_stats_is_empty(
+				&hw_priv->tx_queue_stats, -1, priv->if_id);
+	} else {
+		bes2600_for_each_vif(hw_priv, priv, i) {
+			if (!priv)
+				continue;
+			if (!(hw_priv->if_id_slot & BIT(priv->if_id)))
+				return;
+			ret |= !bes2600_queue_stats_is_empty(
+				&hw_priv->tx_queue_stats, -1, priv->if_id);
+		}
+	}
+
+	/* no need to do the next work if queue was already clear */
+	if(ret == 0) {
+		bes2600_info(BES2600_DBG_STA, "no need to flush\n");
+		return;
+	}
+
+	/* do flush operation */
+	bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_FLUSH);
+	if (vif) {
+		priv = cw12xx_get_vif_from_ieee80211(vif);
+		if (!(hw_priv->if_id_slot & BIT(priv->if_id)))
+			return;
+		if (!WARN_ON(__bes2600_flush(hw_priv, drop, priv->if_id)))
+			wsm_unlock_tx(hw_priv);
+	} else {
+		bes2600_for_each_vif(hw_priv, priv, i) {
+			if (!priv)
+				continue;
+			if (!(hw_priv->if_id_slot & BIT(priv->if_id)))
+				return;
+			if (!WARN_ON(__bes2600_flush(hw_priv, drop, priv->if_id)))
+				wsm_unlock_tx(hw_priv);
+		}
+	}
+	bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_FLUSH);
+
+	return;
+}
+
+int bes2600_remain_on_channel(struct ieee80211_hw *hw,
+				 struct ieee80211_vif *vif,
+				 struct ieee80211_channel *chan,
+				 int duration,
+				 enum ieee80211_roc_type type)
+{
+	int ret;
+	struct bes2600_common *hw_priv = hw->priv;
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	int if_id = priv->if_id;
+
+	bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_ROC);
+	down(&hw_priv->scan.lock);
+	down(&hw_priv->conf_lock);
+
+
+	bes2600_info(BES2600_DBG_ROC, "ROC IN %d ch %d\n", priv->if_id, chan->hw_value);
+	hw_priv->roc_if_id = priv->if_id;
+	ret = WARN_ON(__bes2600_flush(hw_priv, false, if_id));
+	wsm_unlock_tx(hw_priv);
+	ret = bes2600_enable_listening(priv, chan);
+
+	if (!ret) {
+		atomic_set(&hw_priv->remain_on_channel, 1);
+		queue_delayed_work(hw_priv->workqueue,
+				   &hw_priv->rem_chan_timeout,
+				   duration * HZ / 1000);
+		priv->join_status = BES2600_JOIN_STATUS_MONITOR;
+		ieee80211_ready_on_channel(hw);
+	} else {
+		hw_priv->roc_if_id = -1;
+		up(&hw_priv->scan.lock);
+		bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_ROC);
+	}
+
+	bes2600_info(BES2600_DBG_ROC, "ROC OUT %d\n", priv->if_id);
+
+	/* set the channel to supplied ieee80211_channel pointer, if it
+        is not set. This is to remove the crash while sending a probe res
+        in listen state. Later channel will updated on
+        IEEE80211_CONF_CHANGE_CHANNEL event*/
+	if(!hw_priv->channel) {
+		hw_priv->channel = chan;
+	}
+	//hw_priv->roc_cookie = cookie;
+	up(&hw_priv->conf_lock);
+	return ret;
+}
+
+int bes2600_cancel_remain_on_channel(struct ieee80211_hw *hw
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
+	, struct ieee80211_vif *vif
+#endif
+	)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+
+	bes2600_info(BES2600_DBG_STA, "[STA] Cancel remain on channel\n");
+
+	if (atomic_read(&hw_priv->remain_on_channel))
+		cancel_delayed_work_sync(&hw_priv->rem_chan_timeout);
+
+	if (atomic_read(&hw_priv->remain_on_channel))
+		bes2600_rem_chan_timeout(&hw_priv->rem_chan_timeout.work);
+
+	return 0;
+}
+
+/* ******************************************************************** */
+/* WSM callbacks							*/
+
+void bes2600_channel_switch_cb(struct bes2600_common *hw_priv)
+{
+	//wsm_unlock_tx(hw_priv);  /* already unlock in wsm_channel_switch_indication */
+}
+
+void bes2600_free_event_queue(struct bes2600_common *hw_priv)
+{
+	LIST_HEAD(list);
+
+	spin_lock(&hw_priv->event_queue_lock);
+	list_splice_init(&hw_priv->event_queue, &list);
+	spin_unlock(&hw_priv->event_queue_lock);
+
+	__bes2600_free_event_queue(&list);
+}
+
+void bes2600_event_handler(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, event_handler);
+	struct bes2600_vif *priv;
+	struct bes2600_wsm_event *event;
+	LIST_HEAD(list);
+
+	spin_lock(&hw_priv->event_queue_lock);
+	list_splice_init(&hw_priv->event_queue, &list);
+	spin_unlock(&hw_priv->event_queue_lock);
+
+	down(&hw_priv->conf_lock);
+	list_for_each_entry(event, &list, link) {
+		priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, event->if_id);
+		if (!priv) {
+			bes2600_dbg(BES2600_DBG_STA, "[CQM] Event for non existing "
+				   "interface, ignoring.\n");
+			continue;
+		}
+		switch (event->evt.eventId) {
+			case WSM_EVENT_ERROR:
+				/* I even don't know what is it about.. */
+				//STUB();
+				break;
+			case WSM_EVENT_BSS_LOST:
+			{
+				spin_lock(&priv->bss_loss_lock);
+				if (priv->bss_loss_status > BES2600_BSS_LOSS_NONE) {
+					spin_unlock(&priv->bss_loss_lock);
+					break;
+				}
+				priv->bss_loss_status = BES2600_BSS_LOSS_CHECKING;
+				spin_unlock(&priv->bss_loss_lock);
+				bes2600_info(BES2600_DBG_STA, "[CQM] BSS lost.\n");
+				bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_BSS_LOST);
+				cancel_delayed_work_sync(&priv->bss_loss_work);
+				cancel_delayed_work_sync(&priv->connection_loss_work);
+				if (!down_trylock(&hw_priv->scan.lock)) {
+					up(&hw_priv->scan.lock);
+					priv->delayed_link_loss = 0;
+					queue_delayed_work(hw_priv->workqueue,
+							&priv->bss_loss_work, 0);
+				} else {
+					/* Scan is in progress. Delay reporting. */
+					/* Scan complete will trigger bss_loss_work */
+					priv->delayed_link_loss = 1;
+					/* Also we're starting watchdog. */
+					queue_delayed_work(hw_priv->workqueue,
+							&priv->bss_loss_work, 10 * HZ);
+				}
+				break;
+			}
+			case WSM_EVENT_BSS_REGAINED:
+			{
+				bes2600_info(BES2600_DBG_STA, "[CQM] BSS regained.\n");
+				priv->delayed_link_loss = 0;
+				spin_lock(&priv->bss_loss_lock);
+				priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
+				spin_unlock(&priv->bss_loss_lock);
+				bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_BSS_LOST);
+				cancel_delayed_work_sync(&priv->bss_loss_work);
+				cancel_delayed_work_sync(&priv->connection_loss_work);
+				break;
+			}
+			case WSM_EVENT_RADAR_DETECTED:
+				//STUB();
+				break;
+			case WSM_EVENT_RCPI_RSSI:
+			{
+				/* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+				 * RSSI = RCPI / 2 - 110
+				 */
+				int rcpi_rssi = (int)(event->evt.eventData & 0xFF);
+				int cqm_evt;
+				if (priv->cqm_use_rssi)
+					rcpi_rssi = (s8)rcpi_rssi;
+				else
+					rcpi_rssi =  rcpi_rssi / 2 - 110;
+
+				cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ?
+					NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+					NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+				bes2600_info(BES2600_DBG_STA, "[CQM] RSSI event: %d.\n", rcpi_rssi);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+				ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, rcpi_rssi, GFP_KERNEL);
+#else
+				ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, GFP_KERNEL);
+#endif
+				break;
+			}
+			case WSM_EVENT_BT_INACTIVE:
+				//STUB();
+				break;
+			case WSM_EVENT_BT_ACTIVE:
+				//STUB();
+				break;
+			case WSM_EVENT_INACTIVITY:
+			{
+				int link_id = ffs((u32)(event->evt.eventData)) - 1;
+				struct sk_buff *skb;
+				struct ieee80211_mgmt *deauth;
+				struct bes2600_link_entry *entry = NULL;
+
+				bes2600_info(BES2600_DBG_STA, "Inactivity Event Recieved for "
+						"link_id %d\n", link_id);
+				cw12xx_unmap_link(priv, link_id);
+
+				skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64);
+				skb_reserve(skb, 64);
+				deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt));
+				WARN_ON(!deauth);
+				entry = &priv->link_id_db[link_id - 1];
+				deauth->duration = 0;
+				memcpy(deauth->da, priv->vif->addr, ETH_ALEN);
+				memcpy(deauth->sa, entry->mac/*priv->link_id_db[i].mac*/, ETH_ALEN);
+				memcpy(deauth->bssid, priv->vif->addr, ETH_ALEN);
+				deauth->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+						IEEE80211_STYPE_DEAUTH | IEEE80211_FCTL_TODS);
+				deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING;
+				deauth->seq_ctrl = 0;
+				ieee80211_rx_irqsafe(priv->hw, skb);
+				bes2600_info(BES2600_DBG_STA, " Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da);
+				queue_work(priv->hw_priv->workqueue, &priv->set_tim_work);
+				break;
+			}
+		case WSM_EVENT_PS_MODE_ERROR:
+			{
+				bes2600_err(BES2600_DBG_STA, " PS Mode Error, Reason:%u\n", event->evt.eventData);
+
+				if (event->evt.eventData != WSM_PS_ERROR_AP_NO_DATA_AFTER_TIM &&
+				    !priv->uapsd_info.uapsdFlags &&
+				    (priv->user_pm_mode != WSM_PSM_PS))
+				{
+					bes2600_pwr_mark_ap_lp_bad(hw_priv);
+				 	priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
+				}
+				break;
+			}
+		case WSM_EVENT_WAKEUP_EVENT:
+			bes2600_info(BES2600_DBG_STA, "wifi wakeup, Reason:%u\n", event->evt.eventData);
+			bes2600_chrdev_wifi_update_wakeup_reason(event->evt.eventData);
+			break;
+		}
+	}
+	up(&hw_priv->conf_lock);
+	__bes2600_free_event_queue(&list);
+}
+
+void bes2600_bss_loss_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, bss_loss_work.work);
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	int timeout; /* in beacons */
+#ifdef BSS_LOSS_CHECK
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *info;
+	static int bl_ck_cnt = 0;
+	static int bl_cfm_cnt = 0;
+#endif
+
+	timeout = priv->cqm_link_loss_count -
+		priv->cqm_beacon_loss_count;
+
+	/* Skip the confimration procedure in P2P case */
+	if (priv->vif->p2p)
+		goto report;
+
+	spin_lock(&priv->bss_loss_lock);
+#ifdef BSS_LOSS_CHECK
+	if (!priv->vif->bss_conf.assoc) {
+		priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
+		spin_unlock(&priv->bss_loss_lock);
+		bl_ck_cnt = 0;
+		bl_cfm_cnt = 0;
+		return;
+	}
+#endif
+	if (priv->bss_loss_status == BES2600_BSS_LOSS_CHECKING) {
+#ifdef BSS_LOSS_CHECK
+		spin_unlock(&priv->bss_loss_lock);
+		bes2600_info(BES2600_DBG_STA, "bl checking\n");
+		priv->cmq_tx_success_count = 0;
+		bl_ck_cnt = 0;
+		bl_cfm_cnt = 0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+		skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false);
+#else
+		skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+#endif
+		if (!(WARN_ON(!skb))) {
+			info = IEEE80211_SKB_CB(skb);
+			info->control.vif = priv->vif;
+			bes2600_tx(priv->hw, NULL, skb);
+			/* Start watchdog -- if nullfunc TX doesn't fail
+			 * in 1 sec, forward event to upper layers */
+			queue_delayed_work(hw_priv->workqueue,
+					   &priv->bss_loss_work, 1 * HZ);
+		}
+		return;
+#else
+		priv->bss_loss_status = BES2600_BSS_LOSS_CONFIRMED;
+#endif
+	} else if (priv->bss_loss_status == BES2600_BSS_LOSS_CONFIRMING) {
+#ifdef BSS_LOSS_CHECK
+		/* reset cca to workaround rx stuck issue */
+		// bes2600_cca_soft_reset();
+
+		/* succeeded to send last null frame */
+		bl_cfm_cnt = 0;
+		/* keep checking to wait bss regain*/
+		bes2600_info(BES2600_DBG_STA, "bl ck %d\n", bl_ck_cnt);
+		if (bl_ck_cnt++ < BSS_LOSS_CK_THR) {
+			spin_unlock(&priv->bss_loss_lock);
+			priv->bss_loss_status = BES2600_BSS_LOSS_CHECKING;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+			skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false);
+#else
+			skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+#endif
+			if (!(WARN_ON(!skb))) {
+				info = IEEE80211_SKB_CB(skb);
+				info->control.vif = priv->vif;
+				bes2600_tx(priv->hw, NULL, skb);
+				/* detect every 3s until receive bss regain event */
+				queue_delayed_work(hw_priv->workqueue,
+						   &priv->bss_loss_work, BSS_LOSS_CK_INV * HZ / 1000);
+			}
+			return;
+		} else {
+			bl_ck_cnt = 0;
+		}
+#else
+		priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
+		spin_unlock(&priv->bss_loss_lock);
+		return;
+#endif
+	} else if (priv->bss_loss_status == BES2600_BSS_LOSS_CONFIRMED) {
+#ifdef BSS_LOSS_CHECK
+		/* failed to send last null frame */
+		/* check if continous failures occur */
+		if (priv->cmq_tx_success_count != 0) {
+			bes2600_info(BES2600_DBG_STA, "bl reset ck cfm s_cnt=%d\n", priv->cmq_tx_success_count);
+			bl_ck_cnt = 0;
+			bl_cfm_cnt = 0;
+			priv->cmq_tx_success_count = 0;
+		}
+		bes2600_info(BES2600_DBG_STA, "bl cfm %d\n", bl_cfm_cnt);
+		if (bl_cfm_cnt++ < BSS_LOSS_CFM_THR) {
+			spin_unlock(&priv->bss_loss_lock);
+			priv->bss_loss_status = BES2600_BSS_LOSS_CHECKING;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+			skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false);
+#else
+			skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+#endif
+			if (!(WARN_ON(!skb))) {
+				info = IEEE80211_SKB_CB(skb);
+				info->control.vif = priv->vif;
+				bes2600_tx(priv->hw, NULL, skb);
+				queue_delayed_work(hw_priv->workqueue,
+						   &priv->bss_loss_work, BSS_LOSS_CK_INV * HZ / 1000);
+			}
+			return;
+		} else {
+			bl_cfm_cnt = 0;
+		}
+#else
+		priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
+		spin_unlock(&priv->bss_loss_lock);
+		return;
+#endif
+	}
+	spin_unlock(&priv->bss_loss_lock);
+
+report:
+	if (priv->cqm_beacon_loss_count) {
+		bes2600_info(BES2600_DBG_STA, "[CQM] Beacon loss.\n");
+		if (timeout <= 0)
+			timeout = 0;
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+		ieee80211_cqm_beacon_miss_notify(priv->vif, GFP_KERNEL);
+#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+	} else {
+		timeout = 0;
+	}
+
+	cancel_delayed_work_sync(&priv->connection_loss_work);
+	queue_delayed_work(hw_priv->workqueue,
+		&priv->connection_loss_work,
+		timeout * HZ / 10);
+
+	spin_lock(&priv->bss_loss_lock);
+	priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
+	spin_unlock(&priv->bss_loss_lock);
+}
+
+void bes2600_connection_loss_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif,
+				connection_loss_work.work);
+	bes2600_info(BES2600_DBG_STA, "[CQM] Reporting connection loss.\n");
+	bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_BSS_LOST);
+	ieee80211_connection_loss(priv->vif);
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	// set disconnected in BSS_CHANGED_ASSOC
+	// bwifi_change_current_status(hw_priv, BWIFI_STATUS_DISCONNECTED);
+#endif
+}
+
+void bes2600_tx_failure_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, tx_failure_work);
+	bes2600_info(BES2600_DBG_STA, "[CQM] Reporting TX failure.\n");
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	ieee80211_cqm_tx_fail_notify(priv->vif, GFP_KERNEL);
+#else /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+	(void)priv;
+#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+}
+
+#ifdef CONFIG_BES2600_TESTMODE
+/**
+ * bes2600_device_power_calc- Device power calculation
+ * from values fetch from SDD File.
+ *
+ * @priv: the private structure
+ * @Max_output_power: Power fetch from SDD
+ * @fe_cor: front-end loss correction
+ * @band: Either 2GHz or 5GHz
+ *
+ */
+void bes2600_device_power_calc(struct bes2600_common *hw_priv,
+		s16 max_output_power, s16 fe_cor, u32 band)
+{
+	s16 power_calc;
+
+	power_calc = max_output_power - fe_cor;
+	if ((power_calc % 16) != 0)
+		power_calc += 16;
+
+	hw_priv->txPowerRange[band].max_power_level = power_calc/16;
+	/*
+	 * 12dBm is control range supported by firmware.
+	 * This means absolute min power is
+	 * max_power_level - 12.
+	 */
+	hw_priv->txPowerRange[band].min_power_level =
+		hw_priv->txPowerRange[band].max_power_level - 12;
+	hw_priv->txPowerRange[band].stepping = 1;
+
+}
+#endif
+/* ******************************************************************** */
+/* Internal API								*/
+
+
+
+/*
+* This function is called to Parse the SDD file
+ *to extract listen_interval and PTA related information
+*/
+int bes2600_parse_SDD_file(struct bes2600_common *hw_priv)
+{
+	u8 *sdd_data = (u8 *)hw_priv->sdd->data;
+#ifdef CONFIG_BES2600_TESTMODE
+	s16 max_output_power_2G = 0;
+	s16 max_output_power_5G = 0;
+	s16 fe_cor_2G = 0;
+	s16 fe_cor_5G = 0;
+	int i;
+#endif
+	struct bes2600_sdd {
+		u8 id ;
+		u8 length ;
+		u8 data[] ;
+	} *pElement;
+	int parsedLength = 0;
+	#define SDD_PTA_CFG_ELT_ID 0xEB
+#ifdef CONFIG_BES2600_TESTMODE
+	#define SDD_MAX_OUTPUT_POWER_2G4_ELT_ID 0xE3
+	#define SDD_MAX_OUTPUT_POWER_5G_ELT_ID  0xE4
+	#define SDD_FE_COR_2G4_ELT_ID   0x30
+	#define SDD_FE_COR_5G_ELT_ID    0x31
+	#define MIN(x, y, z) (x < y ? (x < z ? x : z) : (y < z ? y : z))
+#endif
+	#define FIELD_OFFSET(type, field) ((u8 *)&((type *)0)->field - (u8 *)0)
+
+	hw_priv->is_BT_Present = false;
+
+	pElement = (struct bes2600_sdd *)sdd_data;
+
+	pElement = (struct bes2600_sdd *)((u8 *)pElement +
+		FIELD_OFFSET(struct bes2600_sdd, data) + pElement->length);
+
+	parsedLength += (FIELD_OFFSET(struct bes2600_sdd, data) +
+			pElement->length);
+
+	while (parsedLength <= hw_priv->sdd->size) {
+		switch (pElement->id) {
+		case SDD_PTA_CFG_ELT_ID:
+		{
+			hw_priv->conf_listen_interval =
+				(*((u16 *)pElement->data+1) >> 7) & 0x1F;
+			hw_priv->is_BT_Present = true;
+			bes2600_dbg(BES2600_DBG_STA, "PTA element found.\n");
+			bes2600_dbg(BES2600_DBG_STA, "Listen Interval %d\n",
+						hw_priv->conf_listen_interval);
+		}
+		break;
+#ifdef CONFIG_BES2600_TESTMODE
+		case SDD_MAX_OUTPUT_POWER_2G4_ELT_ID:
+		{
+			max_output_power_2G =
+				*((s16 *)pElement->data);
+		}
+		break;
+		case SDD_FE_COR_2G4_ELT_ID:
+		{
+			fe_cor_2G =
+				*((s16 *)pElement->data);
+		}
+		break;
+		case SDD_MAX_OUTPUT_POWER_5G_ELT_ID:
+		{
+			max_output_power_5G =
+				*((s16 *)(pElement->data + 4));
+		}
+		break;
+		case SDD_FE_COR_5G_ELT_ID:
+		{
+			fe_cor_5G = MIN(
+				*((s16 *)pElement->data),
+				*((s16 *)(pElement->data + 2)),
+				*((s16 *)(pElement->data + 4)));
+
+			fe_cor_5G = MIN(
+				fe_cor_5G,
+				*((s16 *)(pElement->data + 6)),
+				*((s16 *)(pElement->data + 8)));
+		}
+		break;
+#endif
+
+		default:
+		break;
+		}
+
+		pElement = (struct bes2600_sdd *)
+			((u8 *)pElement + FIELD_OFFSET(struct bes2600_sdd, data)
+					+ pElement->length);
+		parsedLength +=
+		(FIELD_OFFSET(struct bes2600_sdd, data) + pElement->length);
+	}
+
+	if (hw_priv->is_BT_Present == false) {
+		bes2600_dbg(BES2600_DBG_STA, "PTA element NOT found.\n");
+		hw_priv->conf_listen_interval = 0;
+	}
+#ifdef CONFIG_BES2600_TESTMODE
+	/* Max/Min Power Calculation for 2.4G */
+	bes2600_device_power_calc(hw_priv, max_output_power_2G,
+		fe_cor_2G, NL80211_BAND_2GHZ);
+	/* Max/Min Power Calculation for 5G */
+	bes2600_device_power_calc(hw_priv, max_output_power_5G,
+		fe_cor_5G, NL80211_BAND_5GHZ);
+
+	for (i = 0; i < 2; ++i) {
+		bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] Power Values Read from SDD %s:"
+			"min_power_level[%d]: %d max_power_level[%d]:"
+			"%d stepping[%d]: %d\n", __func__, i,
+			hw_priv->txPowerRange[i].min_power_level, i,
+			hw_priv->txPowerRange[i].max_power_level, i,
+			hw_priv->txPowerRange[i].stepping);
+	}
+
+
+	bes2600_dbg(BES2600_DBG_TEST_MODE, "%s output power before %d\n",__func__,hw_priv->output_power);
+        /*BUG:TX output power is not set untill config_bes2600 is called*/
+        /*This would lead to 0 power set in fw and would effect scan & p2p-find*/
+        /*Setting to default value here from sdd which would be overwritten when*/
+        /*we make connection to AP.This value is used only during scan & p2p-ops*/
+        /*untill AP connection is made*/
+        if (!hw_priv->output_power)
+                hw_priv->output_power=hw_priv->txPowerRange[NL80211_BAND_2GHZ].max_power_level;
+
+        bes2600_dbg(BES2600_DBG_TEST_MODE, "%s output power after %d\n",__func__,hw_priv->output_power);
+#else
+        bes2600_dbg(BES2600_DBG_STA, "%s output power before %d\n",__func__,hw_priv->output_power);
+        /*BUG:TX output power: Hardcoding to 20dbm if CCX is not enabled*/
+        /*TODO: This might change*/
+        if (!hw_priv->output_power)
+                hw_priv->output_power=20;
+        bes2600_dbg(BES2600_DBG_STA, "%s output power after %d\n",__func__,hw_priv->output_power);
+#endif
+	return 0;
+
+	#undef SDD_PTA_CFG_ELT_ID
+	#undef FIELD_OFFSET
+}
+
+#ifndef USE_FEM
+#define USE_FEM 0
+#endif
+const uint8_t sdd_22[] =
+{
+	0xc0, 0x02, 0x02, 0x03,
+	0xc1, 0x06, 0x00, 0x01, 0x00, 0x00, 0x4c, 0x00,
+	0xc4, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xc5, 0x02, 0x90, 0x65,
+	0xc7, 0x02, 0x05, 0x00,
+	0xe9, 0x02, 0xB8, 0x0B,	// SddWakeupDriftInUsPerSecond=3000
+	0xea, 0x0a, 0x00, 0x00, 0x38, 0x00, 0x80, 0x00, 0x78, 0x00, 0x80, 0x00,
+	0xeb, 0x1e, 0x06, 0x00, 0x81, 0x02, 0x01, 0x34, 0x0d, 0x21, 0x11, 0x1d, 0x7c, 0x85, 0x6b, 0x6e, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xfe, 0x02, 0x00, 0x00,
+	0xc1, 0x06, 0x01, 0x01, 0x00, 0x00, 0x30, 0x00,
+	0xf0, 0x22, 0x41, 0x44, 0x42, 0x5f, 0x32, 0x39, 0x30, 0x35, 0x5f, 0x31, 0x32, 0x35, 0x30, 0x5f, 0x52, 0x32, 0x30, 0x5f, 0x4c, 0x6f, 0x77, 0x5f, 0x53, 0x4d, 0x50, 0x53, 0x5f, 0x76, 0x37, 0x2e, 0x30, 0x31, 0x00, 0x00,
+	0xfe, 0x02, 0x00, 0x00,
+	0xc1, 0x06, 0x03, 0x01, 0x00, 0x00, 0xb4, 0x02,
+//2 TODO: we must make sure the function bes2600_parse_SDD_file's array & sdd values the same(0xec& 0xed)
+#if TEST_11B //2020-2-4
+	0xec, 0x12, 0x05, 0x00, 0x01, 0x78, 0x78, 0x02, 0x78, 0x78, 0x0b, 0x78, 0x78, 0x0c, 0x78, 0x78, 0x0d, 0x78, 0x78, 0x00,
+#else
+	0xec, 0x12, 0x05, 0x00, 0x01, 0xc0, 0xc0, 0x02, 0xc0, 0xc0, 0x0b, 0xc0, 0xc0, 0x0c, 0xc0, 0xc0, 0x0d, 0xc0, 0xc0, 0x00,
+#endif
+	0xed, 0x0e, 0x05, 0x00, 0xb8, 0x78, 0x22, 0x78, 0x34, 0x78, 0x64, 0x78, 0x95, 0x78, 0x00, 0x00,
+	0xe3, 0x1a, 0x1c, 0x01, 0x1c, 0x01, 0x18, 0x01, 0x18, 0x01, 0x18, 0x01, 0x18, 0x01, 0x18, 0x01, 0x10, 0x01, 0x00, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0xe4, 0x1a, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x14, 0x01, 0x04, 0x01, 0xf4, 0x00, 0xe4, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x20, 0x1a, 0x8d, 0x03, 0x73, 0x02, 0x73, 0x03, 0x40, 0x03, 0xb3, 0x02, 0x40, 0x04, 0xcd, 0x04, 0x5f, 0x00, 0x00, 0x05, 0x5f, 0x00, 0x33, 0x05, 0x5f, 0x00, 0x00, 0x00,
+	0x21, 0x1a, 0x73, 0x03, 0x73, 0x02, 0x8d, 0x02, 0x40, 0x03, 0x1a, 0x02, 0x40, 0x04, 0x00, 0x04, 0x38, 0x00, 0xcd, 0x04, 0x66, 0x00, 0x00, 0x05, 0x57, 0x00, 0x00, 0x00,
+	0x22, 0x02, 0x07, 0x00,
+	0x23, 0x02, 0x07, 0x00,
+	0x30, 0x02, 0x08, 0x00,
+	0x31, 0x02, 0x08, 0x00,
+	0xe0, 0x02, 0x3a, 0x00,
+	0xe1, 0x02, 0x32, 0x00,
+	0x40, 0x0a, 0xf4, 0xff, 0x42, 0x00, 0xdd, 0xff, 0x3f, 0x00, 0x00, 0x00,
+	0x41, 0x0a, 0x80, 0xff, 0x63, 0x00, 0xd8, 0xff, 0x20, 0x00, 0x00, 0x00,
+	0x46, 0x22, 0x00, 0x00, 0x04, 0x1a, 0x08, 0x05, 0x01, 0x9f, 0x00, 0x00, 0x9a, 0xfd, 0x92, 0xff, 0x0a, 0x0a, 0x00, 0x00, 0xc0, 0x02, 0x6c, 0x00, 0x7c, 0xf5, 0x00, 0x00, 0x64, 0xfe, 0xca, 0xff, 0x78, 0x06, 0x00, 0x00,
+	0x47, 0x22, 0x00, 0x00, 0xbe, 0xf6, 0xe4, 0x00, 0x10, 0x05, 0x00, 0x00, 0x27, 0x01, 0x5c, 0x01, 0x44, 0x00, 0x00, 0x00, 0x14, 0xfe, 0x93, 0xff, 0xb0, 0x00, 0x00, 0x00, 0x70, 0x01, 0x4a, 0x00, 0x6f, 0xff, 0x00, 0x00,
+	0x44, 0x22, 0x00, 0x00, 0xb0, 0x03, 0x53, 0xfc, 0xf9, 0x05, 0x00, 0x00, 0xf6, 0xfe, 0x84, 0x00, 0x36, 0xff, 0x00, 0x00, 0x3b, 0x01, 0x68, 0xff, 0xe8, 0x00, 0x00, 0x00, 0x78, 0xfe, 0x7d, 0x00, 0x51, 0xff, 0x00, 0x00,
+	0x45, 0x22, 0x00, 0x00, 0xda, 0x03, 0xf0, 0xff, 0x34, 0xff, 0x00, 0x00, 0x9e, 0xff, 0xfd, 0xff, 0x0d, 0x00, 0x00, 0x00, 0xa8, 0x00, 0xe0, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x7e, 0xff, 0x29, 0x00, 0xf0, 0xff, 0x00, 0x00,
+	0x42, 0x22, 0x00, 0x00, 0x08, 0xf8, 0xf0, 0xff, 0x58, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x89, 0xff, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0xd2, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00,
+	0x43, 0x22, 0x00, 0x00, 0xac, 0x00, 0x0a, 0xff, 0x63, 0x03, 0x00, 0x00, 0x31, 0xff, 0xf2, 0x00, 0xed, 0xfe, 0x00, 0x00, 0x58, 0x00, 0xa2, 0xff, 0x93, 0x00, 0x00, 0x00, 0xb4, 0xff, 0x65, 0x00, 0x74, 0xff, 0x00, 0x00,
+#if DPD_CALI
+#if ALI_CONFG
+	//2020-2-4
+#if TEST_11B
+	0x48, 0x16, 0x20, 0x01, 0x20, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01,
+#else
+	0x48, 0x16, 0x00, 0x02, 0x00, 0x02, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xa0, 0x01, 0xa0, 0x01, 0xa0, 0x01,
+#endif
+#else
+	0x48, 0x16, 0x00, 0x01, 0x00, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01,
+#endif
+#else
+	0x48, 0x16, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01,
+#endif
+#if (CHIP_WIFI_ROM_VER == 2)
+	0x49, 0x16, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x4c, 0x01, 0x2d, 0x01, 0x10, 0x01, 0xf5, 0x00, 0xde, 0x00,
+#else
+	0x49, 0x16, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01,
+#endif
+	0x50, 0x06, 0xe4, 0x48, 0x87, 0x43, 0xef, 0xe8,
+	0x51, 0x06, 0x9b, 0x0c, 0x67, 0xef, 0x4f, 0x16,
+	0x52, 0x1a, 0x00, 0x00, 0xb4, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x53, 0x1a, 0x00, 0x00, 0x9a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x54, 0x1a, 0x00, 0x00, 0x06, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x55, 0x1a, 0x00, 0x00, 0x10, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x56, 0x1a, 0x00, 0x00, 0xf3, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x57, 0x1a, 0x00, 0x00, 0xc0, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+#if WIFI_OUT_FEM
+	0xc6, 0x22, 0,  0,  0,  8,  0, 16,  0, 16,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  8,  0, 16,  0, 16,  0,  0,  0,  8,  0, 16,  0, 16, 0, 0,
+#else
+
+#ifdef CHIP_BEST2002
+    //bes EVB sw gpio20
+    0xc6, 0x22, 1,  1,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0, 0, 0,
+#else //2003
+//gpio24 io number = 2*8+4=20
+#if (20 == RF_TX_CONTROL_IO )
+	//0xc6 eco proprietary; bailu EVB sw gpio24
+	0xc6, 0x22, 0,  0,  16,  16,  0,  0,  0,  0,  0,  0,  16,  16,  0,  0,  0,  0,  0,  0,  16,  16,  0,  0,  0,  0,  0,  0,  16,  16,  0,  0,  0,  0, 0, 0,
+#elif(12 == RF_TX_CONTROL_IO )
+    //0xc6 eco proprietary; yunque EVB sw gpio14 or bes EVB sw gpio20
+    0xc6, 0x22, 1,  1,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0, 0, 0,
+#elif(2 == RF_TX_CONTROL_IO )
+    0xc6, 0x22, 0,  2,  0,  2,  0, 16,  0, 16,  0,  2,  0,  2,  0,  0,  0,  0,  0,  2,  0,  2,  0,  0,  0,  0,  0,  2,  0,  2,  0,  0,  0,  0, 0, 0,
+#else
+	//0xc6 eco proprietary; yunque EVB sw gpio14 or bes EVB sw gpio20
+	0xc6, 0x22, 1,  1,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0, 0, 0,
+#endif
+#endif
+
+#endif
+
+	0xfe, 0x02, 0x00, 0x00,
+	0xc1, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+struct firmware g_sdd;
+
+int bes2600_setup_mac(struct bes2600_common *hw_priv)
+{
+	int ret = 0, if_id;
+	if (!hw_priv->sdd) {
+		const char *sdd_path = NULL;
+		struct wsm_configuration cfg = {
+			.dot11StationId = &hw_priv->mac_addr[0],
+		};
+
+		switch (hw_priv->hw_revision) {
+		case BES2600_HW_REV_CUT10:
+			sdd_path = SDD_FILE_10;
+			break;
+		case BES2600_HW_REV_CUT11:
+			sdd_path = SDD_FILE_11;
+			break;
+		case BES2600_HW_REV_CUT20:
+			sdd_path = SDD_FILE_20;
+			break;
+		case BES2600_HW_REV_CUT22:
+			sdd_path = SDD_FILE_22;
+			break;
+		case CW1250_HW_REV_CUT11:
+			sdd_path = SDD_FILE_1250_11;
+			break;
+#if defined(BES2600_DETECTION_LOGIC)
+		case BES2600_HW_REV_CUT10:
+			sdd_path = SDD_FILE_1260_10;
+			break;
+#endif/*BES2600_DETECTION_LOGIC*/
+		default:
+			BUG_ON(1);
+		}
+
+#ifdef CONFIG_BES2600_STATIC_SDD
+		g_sdd.data = sdd_22;
+		g_sdd.size = sizeof(sdd_22);
+		hw_priv->sdd = &g_sdd;
+		cfg.dpdData = sdd_22;
+		cfg.dpdData_size = g_sdd.size;
+#else
+		ret = request_firmware(&hw_priv->sdd,
+			sdd_path, hw_priv->pdev);
+
+		if (unlikely(ret)) {
+			bes2600_dbg(BES2600_DBG_ERROR,
+				"%s: can't load sdd file %s.\n",
+				__func__, sdd_path);
+			return ret;
+		}
+
+		cfg.dpdData = hw_priv->sdd->data;
+		cfg.dpdData_size = hw_priv->sdd->size;
+#endif
+
+		for (if_id = 0; if_id < 2;
+		     if_id++) {
+			/* Set low-power mode. */
+			ret |= WARN_ON(wsm_configuration(hw_priv, &cfg,
+				       if_id));
+		}
+		/* Parse SDD file for PTA element */
+		//bes2600_parse_SDD_file(hw_priv);
+	}
+
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void bes2600_pending_offchanneltx_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+	container_of(work, struct bes2600_vif, pending_offchanneltx_work.work);
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	down(&hw_priv->conf_lock);
+
+	bes2600_dbg(BES2600_DBG_ROC, "OFFCHAN PEND IN\n");
+	bes2600_disable_listening(priv);
+	hw_priv->roc_if_id = -1;
+	bes2600_dbg(BES2600_DBG_ROC, "OFFCHAN PEND OUT\n");
+
+	up(&hw_priv->scan.lock);
+	up(&hw_priv->conf_lock);
+}
+
+void bes2600_offchannel_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, offchannel_work);
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id);
+	struct bes2600_queue *queue = &hw_priv->tx_queue[queueId];
+
+	BUG_ON(queueId >= 4);
+	BUG_ON(!hw_priv->channel);
+
+	if (unlikely(down_trylock(&hw_priv->scan.lock))) {
+		int ret = 0;
+		bes2600_dbg(BES2600_DBG_STA, "bes2600_offchannel_work***** drop frame\n");
+#ifdef CONFIG_BES2600_TESTMODE
+		bes2600_queue_remove(hw_priv, queue,
+				hw_priv->pending_frame_id);
+#else
+		ret = bes2600_queue_remove(queue, hw_priv->pending_frame_id);
+#endif
+		if (ret)
+			bes2600_err(BES2600_DBG_STA, "bes2600_offchannel_work: "
+				       "queue_remove failed %d\n", ret);
+		wsm_unlock_tx(hw_priv);
+		return;
+	}
+	down(&hw_priv->conf_lock);
+	bes2600_dbg(BES2600_DBG_ROC, "OFFCHAN WORK IN %d\n", priv->if_id);
+	hw_priv->roc_if_id = priv->if_id;
+	if (likely(!priv->join_status)) {
+		wsm_vif_flush_tx(priv);
+		bes2600_enable_listening(priv, hw_priv->channel);
+		/* bes2600_update_filtering(priv); */
+	}
+	if (unlikely(!priv->join_status))
+#ifdef CONFIG_BES2600_TESTMODE
+		bes2600_queue_remove(hw_priv, queue,
+				hw_priv->pending_frame_id);
+#else
+		bes2600_queue_remove(queue, hw_priv->pending_frame_id);
+#endif /*CONFIG_BES2600_TESTMODE*/
+	else
+#ifdef CONFIG_BES2600_TESTMODE
+		bes2600_queue_requeue(hw_priv, queue,
+			hw_priv->pending_frame_id, false);
+#else
+		bes2600_queue_requeue(queue, hw_priv->pending_frame_id, false);
+#endif
+
+	queue_delayed_work(hw_priv->workqueue,
+			&priv->pending_offchanneltx_work, 204 * HZ/1000);
+	bes2600_dbg(BES2600_DBG_ROC, "OFFCHAN WORK OUT %d\n", priv->if_id);
+	up(&hw_priv->conf_lock);
+	wsm_unlock_tx(hw_priv);
+}
+
+void bes2600_join_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, join_work);
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id);
+	struct bes2600_queue *queue = &hw_priv->tx_queue[queueId];
+	const struct bes2600_txpriv *txpriv = NULL;
+	struct sk_buff *skb = NULL;
+	const struct wsm_tx *wsm;
+	const struct ieee80211_hdr *frame;
+	const u8 *bssid;
+	struct cfg80211_bss *bss;
+	const u8 *ssidie;
+	const u8 *dtimie;
+	const struct ieee80211_tim_ie *tim = NULL;
+	struct wsm_protected_mgmt_policy mgmt_policy;
+	struct ieee80211_conf *conf = &hw_priv->hw->conf;
+	struct wsm_template_frame probe_tmp = {
+		.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+	};
+	/*struct wsm_reset reset = {
+		.reset_statistics = true,
+	};*/
+
+
+	BUG_ON(queueId >= 4);
+	if (bes2600_queue_get_skb(queue, hw_priv->pending_frame_id,
+			&skb, &txpriv)) {
+		wsm_unlock_tx(hw_priv);
+		return;
+	}
+	wsm = (struct wsm_tx *)&skb->data[0];
+	frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset];
+	bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */
+
+	BUG_ON(!wsm);
+	BUG_ON(!hw_priv->channel);
+
+	if (unlikely(priv->join_status)) {
+		atomic_set(&priv->connect_in_process, 0);
+		wsm_lock_tx(hw_priv);
+		bes2600_unjoin_work(&priv->unjoin_work);
+	}
+
+	cancel_delayed_work_sync(&priv->join_timeout);
+
+	bss = cfg80211_get_bss(hw_priv->hw->wiphy, hw_priv->channel, bssid, NULL, 0,
+			       IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
+
+	if (!bss) {
+#ifdef CONFIG_BES2600_TESTMODE
+		bes2600_queue_remove(hw_priv, queue, hw_priv->pending_frame_id);
+#else
+		bes2600_queue_remove(queue, hw_priv->pending_frame_id);
+#endif /*CONFIG_BES2600_TESTMODE*/
+		wsm_unlock_tx(hw_priv);
+		return;
+	}
+	ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
+	dtimie = ieee80211_bss_get_ie(bss, WLAN_EID_TIM);
+	if (dtimie)
+		tim = (struct ieee80211_tim_ie *)&dtimie[2];
+
+	down(&hw_priv->conf_lock);
+	{
+		struct wsm_switch_channel channel;
+		struct wsm_join join = {
+			.mode = (bss->capability & WLAN_CAPABILITY_IBSS) ?
+				WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
+			.preambleType = WSM_JOIN_PREAMBLE_SHORT,
+			.probeForJoin = 1,
+			/* dtimPeriod will be updated after association */
+			.dtimPeriod = 1,
+			.beaconInterval = bss->beacon_interval,
+		};
+
+		if (priv->if_id)
+			join.flags |= WSM_FLAG_MAC_INSTANCE_1;
+		else
+			join.flags &= ~WSM_FLAG_MAC_INSTANCE_1;
+
+		/* BT Coex related changes */
+		if (hw_priv->is_BT_Present) {
+			if (((hw_priv->conf_listen_interval * 100) %
+					bss->beacon_interval) == 0)
+				priv->listen_interval =
+					((hw_priv->conf_listen_interval * 100) /
+					bss->beacon_interval);
+			else
+				priv->listen_interval =
+					((hw_priv->conf_listen_interval * 100) /
+					bss->beacon_interval + 1);
+		}
+
+		if (tim && tim->dtim_period > 1) {
+			join.dtimPeriod = tim->dtim_period;
+			priv->join_dtim_period = tim->dtim_period;
+		}
+		priv->beacon_int = bss->beacon_interval;
+		bes2600_info(BES2600_DBG_STA, "[STA] Join DTIM: %d, interval: %d\n",
+				join.dtimPeriod, priv->beacon_int);
+
+		hw_priv->is_go_thru_go_neg = false;
+		join.channelNumber = hw_priv->channel->hw_value;
+
+		/* basicRateSet will be updated after association.
+		Currently these values are hardcoded */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+		if (hw_priv->channel->band == NL80211_BAND_5GHZ) {
+#else
+		if (hw_priv->channel->band == IEEE80211_BAND_5GHZ) {
+#endif
+			join.band = WSM_PHY_BAND_5G;
+			join.basicRateSet = 64; /*6 mbps*/
+		}else{
+			join.band = WSM_PHY_BAND_2_4G;
+			join.basicRateSet = 7; /*1, 2, 5.5 mbps*/
+		}
+		memcpy(&join.bssid[0], bssid, sizeof(join.bssid));
+		memcpy(&priv->join_bssid[0], bssid, sizeof(priv->join_bssid));
+
+		if (ssidie) {
+			join.ssidLength = ssidie[1];
+			if (WARN_ON(join.ssidLength > sizeof(join.ssid)))
+				join.ssidLength = sizeof(join.ssid);
+			memcpy(&join.ssid[0], &ssidie[2], join.ssidLength);
+			if(strstr(&join.ssid[0],"5.1.4"))
+				msleep(200);
+#ifdef ROAM_OFFLOAD
+			if((priv->vif->type == NL80211_IFTYPE_STATION)) {
+				priv->ssid_length = join.ssidLength;
+				memcpy(priv->ssid, &join.ssid[0], priv->ssid_length);
+			}
+#endif /*ROAM_OFFLOAD*/
+		}
+
+		if (priv->vif->p2p) {
+			join.flags |= WSM_JOIN_FLAGS_P2P_GO;
+#ifdef P2P_MULTIVIF
+			join.flags |= (1 << 6);
+#endif
+			join.basicRateSet =
+				bes2600_rate_mask_to_wsm(hw_priv, 0xFF0);
+		}
+
+		bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_JOIN);
+		wsm_flush_tx(hw_priv);
+
+		/* Queue unjoin if not associated in 3 sec. */
+		queue_delayed_work(hw_priv->workqueue,
+			&priv->join_timeout, 3 * HZ);
+
+		bes2600_disable_listening(priv);
+
+		//WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id));
+		WARN_ON(wsm_set_block_ack_policy(hw_priv,
+			0, hw_priv->ba_tid_mask, priv->if_id));
+		spin_lock_bh(&hw_priv->ba_lock);
+		hw_priv->ba_ena = false;
+		hw_priv->ba_cnt = 0;
+		hw_priv->ba_acc = 0;
+		hw_priv->ba_hist = 0;
+		hw_priv->ba_cnt_rx = 0;
+		hw_priv->ba_acc_rx = 0;
+		spin_unlock_bh(&hw_priv->ba_lock);
+
+		mgmt_policy.protectedMgmtEnable = 0;
+		mgmt_policy.unprotectedMgmtFramesAllowed = 1;
+		mgmt_policy.encryptionForAuthFrame = 1;
+		wsm_set_protected_mgmt_policy(hw_priv, &mgmt_policy,
+					      priv->if_id);
+
+		/* need to switch channel before join */
+		channel.channelMode = NL80211_CHAN_NO_HT << 4;
+		channel.channelSwitchCount = 0;
+		channel.newChannelNumber = conf->chandef.chan->hw_value;
+		wsm_switch_channel(hw_priv, &channel,  priv->if_id);
+
+		/* avoid lmac assert when wpa_supplicant connect to ap without scan */
+		probe_tmp.skb = ieee80211_probereq_get(hw_priv->hw, priv->vif->addr, NULL, 0, 0);
+		if (probe_tmp.skb) {
+#ifdef P2P_MULTIVIF
+			wsm_set_template_frame(hw_priv, &probe_tmp, 0);
+#else
+			wsm_set_template_frame(hw_priv, &probe_tmp, priv->if_id);
+#endif
+		}
+
+		if (wsm_join(hw_priv, &join, priv->if_id)) {
+			memset(&priv->join_bssid[0],
+				0, sizeof(priv->join_bssid));
+#ifdef CONFIG_BES2600_TESTMODE
+			bes2600_queue_remove(hw_priv, queue,
+						hw_priv->pending_frame_id);
+#else
+			bes2600_queue_remove(queue, hw_priv->pending_frame_id);
+#endif /*CONFIG_BES2600_TESTMODE*/
+			cancel_delayed_work_sync(&priv->join_timeout);
+			bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN);
+		} else {
+			/* Upload keys */
+#ifdef CONFIG_BES2600_TESTMODE
+			bes2600_queue_requeue(hw_priv, queue,
+				hw_priv->pending_frame_id, true);
+#else
+			bes2600_queue_requeue(queue, hw_priv->pending_frame_id,
+						true);
+#endif
+			priv->join_status = BES2600_JOIN_STATUS_STA;
+			atomic_set(&priv->connect_in_process, 1);
+
+			/* Due to beacon filtering it is possible that the
+			 * AP's beacon is not known for the mac80211 stack.
+			 * Disable filtering temporary to make sure the stack
+			 * receives at least one */
+			priv->disable_beacon_filter = true;
+
+		}
+		bes2600_update_filtering(priv);
+	}
+	up(&hw_priv->conf_lock);
+	if (bss)
+		cfg80211_put_bss(hw_priv->hw->wiphy, bss);
+	wsm_unlock_tx(hw_priv);
+}
+
+void bes2600_join_timeout(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, join_timeout.work);
+	bes2600_info(BES2600_DBG_STA, "[WSM] Issue unjoin command (TMO).\n");
+	bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN);
+	atomic_set(&priv->connect_in_process, 0);
+	wsm_lock_tx(priv->hw_priv);
+	bes2600_unjoin_work(&priv->unjoin_work);
+}
+
+void bes2600_unjoin_work(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, unjoin_work);
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	struct wsm_reset reset = {
+		.reset_statistics = true,
+	};
+	bool is_htcapie = false;
+	int i;
+	struct bes2600_vif *tmp_priv;
+
+	del_timer_sync(&hw_priv->ba_timer);
+	down(&hw_priv->conf_lock);
+	if (unlikely(atomic_read(&hw_priv->scan.in_progress)
+		|| atomic_read(&priv->connect_in_process))) {
+		if (priv->delayed_unjoin) {
+			wiphy_dbg(priv->hw->wiphy,
+				"%s: Delayed unjoin "
+				"is already scheduled.\n",
+				__func__);
+			wsm_unlock_tx(hw_priv);
+		} else {
+			bes2600_info(BES2600_DBG_AP, "delay unjoin work, scan:%d connect:%d\n",
+				atomic_read(&hw_priv->scan.in_progress), atomic_read(&priv->connect_in_process));
+			priv->delayed_unjoin = true;
+		}
+		up(&hw_priv->conf_lock);
+		return;
+	}
+
+	if (priv->join_status &&
+			priv->join_status > BES2600_JOIN_STATUS_STA) {
+		wiphy_err(priv->hw->wiphy,
+				"%s: Unexpected: join status: %d\n",
+				__func__, priv->join_status);
+		BUG_ON(1);
+	}
+	if (priv->join_status) {
+		cancel_work_sync(&priv->update_filtering_work);
+		cancel_work_sync(&priv->set_beacon_wakeup_period_work);
+		memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid));
+#ifdef BES2600_TX_RX_OPT
+		txrx_opt_timer_exit(hw_priv);
+#endif
+		bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE);
+		bes2600_pwr_clear_ap_lp_bad_mark(hw_priv);
+		priv->join_status = BES2600_JOIN_STATUS_PASSIVE;
+
+		/* Unjoin is a reset. */
+		wsm_flush_tx(hw_priv);
+		WARN_ON(wsm_keep_alive_period(hw_priv, 0, priv->if_id));
+		WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id));
+		WARN_ON(wsm_set_output_power(hw_priv,
+			hw_priv->output_power * 10, priv->if_id));
+		priv->join_dtim_period = 0;
+		priv->cipherType = 0;
+		WARN_ON(bes2600_setup_mac_pvif(priv));
+		bes2600_free_event_queue(hw_priv);
+		cancel_work_sync(&hw_priv->event_handler);
+		cancel_delayed_work_sync(&priv->connection_loss_work);
+		WARN_ON(wsm_set_block_ack_policy(hw_priv,
+			0, hw_priv->ba_tid_mask, priv->if_id));
+		priv->disable_beacon_filter = false;
+		bes2600_update_filtering(priv);
+		priv->setbssparams_done = false;
+		memset(&priv->association_mode, 0,
+			sizeof(priv->association_mode));
+		memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+		memset(&priv->firmware_ps_mode, 0,
+			sizeof(priv->firmware_ps_mode));
+		priv->htcap = false;
+		bes2600_for_each_vif(hw_priv, tmp_priv, i) {
+#ifdef P2P_MULTIVIF
+			if ((i == (CW12XX_MAX_VIFS - 1)) || !tmp_priv)
+#else
+			if (!tmp_priv)
+#endif
+				continue;
+			if ((tmp_priv->join_status == BES2600_JOIN_STATUS_STA) && tmp_priv->htcap)
+				is_htcapie = true;
+		}
+
+		if (is_htcapie) {
+			hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11N_THROTTLE;
+			hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11N_THROTTLE;
+			bes2600_info(BES2600_DBG_STA, "UNJOIN HTCAP 11N %d\n",hw_priv->vif0_throttle);
+		} else {
+			hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
+			hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
+			bes2600_info(BES2600_DBG_STA, "UNJOIN 11BG %d\n",hw_priv->vif0_throttle);
+		}
+		bes2600_info(BES2600_DBG_STA, "[STA] Unjoin.\n");
+	}
+
+	up(&hw_priv->conf_lock);
+	wsm_unlock_tx(hw_priv);
+}
+
+int bes2600_enable_listening(struct bes2600_vif *priv,
+				struct ieee80211_channel *chan)
+{
+	/* TODO:COMBO: Channel is common to HW currently in mac80211.
+	Change the code below once channel is made per VIF */
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct wsm_start start = {
+#ifdef P2P_MULTIVIF
+		.mode = WSM_START_MODE_P2P_DEV | (priv->if_id ? (5 << 4) : 0),
+#else
+		.mode = WSM_START_MODE_P2P_DEV | (priv->if_id << 4),
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+		.band = (chan->band == NL80211_BAND_5GHZ) ?
+#else
+		.band = (chan->band == IEEE80211_BAND_5GHZ) ?
+#endif
+				WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
+		.channelNumber = chan->hw_value,
+		.beaconInterval = 100,
+		.DTIMPeriod = 1,
+		.probeDelay = 0,
+		.basicRateSet = 0x0F,
+	};
+	if(priv->if_id != 2) {
+		//WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR);
+		return -EOPNOTSUPP;
+	}
+	if (priv->join_status == BES2600_JOIN_STATUS_MONITOR)
+		return 0;
+	if (priv->join_status == BES2600_JOIN_STATUS_PASSIVE)
+		priv->join_status = BES2600_JOIN_STATUS_MONITOR;
+
+	WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR);
+	bes2600_info(BES2600_DBG_STA, "bes2600_enable_listening if_id:%d\n", priv->if_id);
+	return wsm_start(hw_priv, &start, CW12XX_GENERIC_IF_ID);
+}
+
+int bes2600_disable_listening(struct bes2600_vif *priv)
+{
+	int ret;
+	struct wsm_reset reset = {
+		.reset_statistics = true,
+	};
+	if(priv->if_id != 2) {
+		WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR);
+        return 0;
+	}
+	priv->join_status = BES2600_JOIN_STATUS_PASSIVE;
+
+	WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR);
+
+	if (priv->hw_priv->roc_if_id == -1)
+		return 0;
+	bes2600_info(BES2600_DBG_STA, "bes2600_disable_listening if_id:%d\n", priv->if_id);
+	ret = wsm_reset(priv->hw_priv, &reset, CW12XX_GENERIC_IF_ID);
+	return ret;
+}
+
+/* TODO:COMBO:UAPSD will be supported only on one interface */
+int bes2600_set_uapsd_param(struct bes2600_vif *priv,
+				const struct wsm_edca_params *arg)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	int ret;
+	u16 uapsdFlags = 0;
+
+	/* Here's the mapping AC [queue, bit]
+	VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]*/
+
+	if (arg->params[0].uapsdEnable)
+		uapsdFlags |= 1 << 3;
+
+	if (arg->params[1].uapsdEnable)
+		uapsdFlags |= 1 << 2;
+
+	if (arg->params[2].uapsdEnable)
+		uapsdFlags |= 1 << 1;
+
+	if (arg->params[3].uapsdEnable)
+		uapsdFlags |= 1;
+
+	/* Currently pseudo U-APSD operation is not supported, so setting
+	* MinAutoTriggerInterval, MaxAutoTriggerInterval and
+	* AutoTriggerStep to 0 */
+
+	priv->uapsd_info.uapsdFlags = cpu_to_le16(uapsdFlags);
+	priv->uapsd_info.minAutoTriggerInterval = 0;
+	priv->uapsd_info.maxAutoTriggerInterval = 0;
+	priv->uapsd_info.autoTriggerStep = 0;
+
+	ret = wsm_set_uapsd_info(hw_priv, &priv->uapsd_info,
+				 priv->if_id);
+	return ret;
+}
+
+void bes2600_ba_work(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, ba_work);
+	u8 tx_ba_tid_mask;
+
+	/* TODO:COMBO: reenable this part of code */
+/*	if (priv->join_status != BES2600_JOIN_STATUS_STA)
+		return;
+	if (!priv->setbssparams_done)
+		return;*/
+
+	bes2600_info(BES2600_DBG_STA, "BA work****\n");
+	spin_lock_bh(&hw_priv->ba_lock);
+//	tx_ba_tid_mask = hw_priv->ba_ena ? hw_priv->ba_tid_mask : 0;
+	tx_ba_tid_mask = hw_priv->ba_tid_mask;
+	spin_unlock_bh(&hw_priv->ba_lock);
+
+	wsm_lock_tx(hw_priv);
+
+	WARN_ON(wsm_set_block_ack_policy(hw_priv,
+		tx_ba_tid_mask, hw_priv->ba_tid_mask, -1)); /*TODO:COMBO*/
+
+	wsm_unlock_tx(hw_priv);
+}
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+void bes2600_ba_timer(struct timer_list *t)
+{
+	bool ba_ena;
+	struct bes2600_common *hw_priv = from_timer(hw_priv, t, ba_timer);
+
+	spin_lock_bh(&hw_priv->ba_lock);
+	bes2600_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc,
+			hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx);
+
+	if (atomic_read(&hw_priv->scan.in_progress)) {
+		hw_priv->ba_cnt = 0;
+		hw_priv->ba_acc = 0;
+		hw_priv->ba_cnt_rx = 0;
+		hw_priv->ba_acc_rx = 0;
+		goto skip_statistic_update;
+	}
+
+	if (hw_priv->ba_cnt >= BES2600_BLOCK_ACK_CNT &&
+		(hw_priv->ba_acc / hw_priv->ba_cnt >= BES2600_BLOCK_ACK_THLD ||
+		(hw_priv->ba_cnt_rx >= BES2600_BLOCK_ACK_CNT &&
+		hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >=
+			BES2600_BLOCK_ACK_THLD)))
+		ba_ena = true;
+	else
+		ba_ena = false;
+
+	hw_priv->ba_cnt = 0;
+	hw_priv->ba_acc = 0;
+	hw_priv->ba_cnt_rx = 0;
+	hw_priv->ba_acc_rx = 0;
+
+	if (ba_ena != hw_priv->ba_ena) {
+		if (ba_ena || ++hw_priv->ba_hist >= BES2600_BLOCK_ACK_HIST) {
+			hw_priv->ba_ena = ba_ena;
+			hw_priv->ba_hist = 0;
+#if 0
+			bes2600_dbg(BES2600_DBG_STA, "[STA] %s block ACK:\n",
+				ba_ena ? "enable" : "disable");
+			queue_work(hw_priv->workqueue, &hw_priv->ba_work);
+#endif
+		}
+	} else if (hw_priv->ba_hist)
+		--hw_priv->ba_hist;
+
+skip_statistic_update:
+	spin_unlock_bh(&hw_priv->ba_lock);
+}
+
+#else
+void bes2600_ba_timer(unsigned long arg)
+{
+	bool ba_ena;
+	struct bes2600_common *hw_priv = (struct bes2600_common *)arg;
+
+	spin_lock_bh(&hw_priv->ba_lock);
+	bes2600_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc,
+			hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx);
+
+	if (atomic_read(&hw_priv->scan.in_progress)) {
+		hw_priv->ba_cnt = 0;
+		hw_priv->ba_acc = 0;
+		hw_priv->ba_cnt_rx = 0;
+		hw_priv->ba_acc_rx = 0;
+		goto skip_statistic_update;
+	}
+
+	if (hw_priv->ba_cnt >= BES2600_BLOCK_ACK_CNT &&
+		(hw_priv->ba_acc / hw_priv->ba_cnt >= BES2600_BLOCK_ACK_THLD ||
+		(hw_priv->ba_cnt_rx >= BES2600_BLOCK_ACK_CNT &&
+		hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >=
+			BES2600_BLOCK_ACK_THLD)))
+		ba_ena = true;
+	else
+		ba_ena = false;
+
+	hw_priv->ba_cnt = 0;
+	hw_priv->ba_acc = 0;
+	hw_priv->ba_cnt_rx = 0;
+	hw_priv->ba_acc_rx = 0;
+
+	if (ba_ena != hw_priv->ba_ena) {
+		if (ba_ena || ++hw_priv->ba_hist >= BES2600_BLOCK_ACK_HIST) {
+			hw_priv->ba_ena = ba_ena;
+			hw_priv->ba_hist = 0;
+#if 0
+			bes2600_dbg(BES2600_DBG_STA, "[STA] %s block ACK:\n",
+				ba_ena ? "enable" : "disable");
+			queue_work(hw_priv->workqueue, &hw_priv->ba_work);
+#endif
+		}
+	} else if (hw_priv->ba_hist)
+		--hw_priv->ba_hist;
+
+skip_statistic_update:
+	spin_unlock_bh(&hw_priv->ba_lock);
+}
+#endif
+int bes2600_vif_setup(struct bes2600_vif *priv)
+{
+	struct bes2600_common *hw_priv = priv->hw_priv;
+	int ret = 0;
+
+	/* Setup per vif workitems and locks */
+	spin_lock_init(&priv->vif_lock);
+	INIT_WORK(&priv->join_work, bes2600_join_work);
+	INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout);
+	INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work);
+	INIT_WORK(&priv->wep_key_work, bes2600_wep_key_work);
+	INIT_WORK(&priv->offchannel_work, bes2600_offchannel_work);
+	INIT_DELAYED_WORK(&priv->bss_loss_work, bes2600_bss_loss_work);
+	INIT_DELAYED_WORK(&priv->connection_loss_work,
+			  bes2600_connection_loss_work);
+	spin_lock_init(&priv->bss_loss_lock);
+	INIT_WORK(&priv->tx_failure_work, bes2600_tx_failure_work);
+	spin_lock_init(&priv->ps_state_lock);
+	INIT_DELAYED_WORK(&priv->set_cts_work, bes2600_set_cts_work);
+	INIT_WORK(&priv->set_tim_work, bes2600_set_tim_work);
+	INIT_WORK(&priv->multicast_start_work, bes2600_multicast_start_work);
+	INIT_WORK(&priv->multicast_stop_work, bes2600_multicast_stop_work);
+	INIT_WORK(&priv->link_id_work, bes2600_link_id_work);
+	INIT_DELAYED_WORK(&priv->link_id_gc_work, bes2600_link_id_gc_work);
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	INIT_WORK(&priv->linkid_reset_work, bes2600_link_id_reset);
+#endif
+	INIT_WORK(&priv->update_filtering_work, bes2600_update_filtering_work);
+	INIT_DELAYED_WORK(&priv->pending_offchanneltx_work,
+			bes2600_pending_offchanneltx_work);
+	INIT_WORK(&priv->set_beacon_wakeup_period_work,
+		bes2600_set_beacon_wakeup_period_work);
+#ifdef AP_HT_CAP_UPDATE
+        INIT_WORK(&priv->ht_info_update_work, bes2600_ht_info_update_work);
+#endif
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+	timer_setup(&priv->mcast_timeout, bes2600_mcast_timeout, 0);
+#else
+	setup_timer(&priv->mcast_timeout, bes2600_mcast_timeout, (unsigned long)priv);
+#endif
+
+	priv->setbssparams_done = false;
+	priv->power_set_true = 0;
+	priv->user_power_set_true = 0;
+	priv->user_pm_mode = 0;
+	ret = bes2600_debug_init_priv(hw_priv, priv);
+	if (WARN_ON(ret))
+		goto out;
+
+	/* Initialising the broadcast filter */
+	memset(priv->broadcast_filter.MacAddr, 0xFF, ETH_ALEN);
+	priv->broadcast_filter.nummacaddr = 1;
+	priv->broadcast_filter.address_mode = WSM_FILTER_ADDR_MODE_A1;
+	priv->broadcast_filter.filter_mode = WSM_FILTER_ACTION_FILTER_OUT;
+	priv->htcap = false;
+
+	bes2600_info(BES2600_DBG_STA, "%s: enabling priv\n", __func__);
+	atomic_set(&priv->enabled, 1);
+
+	spin_lock(&hw_priv->vif_list_lock);
+	hw_priv->if_id_slot |= BIT(priv->if_id);
+	hw_priv->vif_list[priv->if_id] = priv->vif;
+	atomic_inc(&hw_priv->num_vifs);
+	spin_unlock(&hw_priv->vif_list_lock);
+	atomic_set(&priv->connect_in_process, 0);
+
+#ifdef P2P_MULTIVIF
+	if (priv->if_id < 2) {
+#endif
+		/* default EDCA */
+		WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007,
+				47, 0xc8, false);
+		WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f,
+				94, 0xc8, false);
+			WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff,
+				0, 0xc8, false);
+		WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff,
+				0, 0xc8, false);
+		ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id);
+		if (WARN_ON(ret))
+			goto out;
+
+		ret = bes2600_set_uapsd_param(priv, &priv->edca);
+		if (WARN_ON(ret))
+			goto out;
+
+		memset(priv->bssid, ~0, ETH_ALEN);
+		priv->wep_default_key_id = -1;
+		priv->cipherType = 0;
+		#ifdef P2P_STA_COEX
+		priv->cqm_link_loss_count = 400;
+		priv->cqm_beacon_loss_count = 200;
+		#else
+		priv->cqm_link_loss_count = 100;
+		priv->cqm_beacon_loss_count = 50;
+		#endif
+
+		/* Temporary configuration - beacon filter table */
+		__bes2600_bf_configure(priv);
+#ifdef P2P_MULTIVIF
+	}
+#endif
+out:
+	return ret;
+}
+
+int bes2600_setup_mac_pvif(struct bes2600_vif *priv)
+{
+	int ret = 0;
+	/* NOTE: There is a bug in FW: it reports signal
+	* as RSSI if RSSI subscription is enabled.
+	* It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */
+	/* NOTE2: RSSI based reports have been switched to RCPI, since
+	* FW has a bug and RSSI reported values are not stable,
+	* what can leads to signal level oscilations in user-end applications */
+	struct wsm_rcpi_rssi_threshold threshold = {
+		.rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+		WSM_RCPI_RSSI_DONT_USE_UPPER |
+		WSM_RCPI_RSSI_DONT_USE_LOWER,
+		.rollingAverageCount = 16,
+	};
+
+	/* Remember the decission here to make sure, we will handle
+	 * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS */
+	if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
+		priv->cqm_use_rssi = true;
+
+
+	/* Configure RSSI/SCPI reporting as RSSI. */
+#ifdef P2P_MULTIVIF
+	ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold,
+					priv->if_id ? 0 : 0);
+#else
+	ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold,
+					priv->if_id);
+#endif
+	return ret;
+}
+
+void bes2600_rem_chan_timeout(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, rem_chan_timeout.work);
+	int ret, if_id;
+	struct bes2600_vif *priv;
+
+	if (atomic_read(&hw_priv->remain_on_channel) == 0) {
+		return;
+	}
+	ieee80211_remain_on_channel_expired(hw_priv->hw);
+
+	down(&hw_priv->conf_lock);
+	if_id = hw_priv->roc_if_id;
+	bes2600_dbg(BES2600_DBG_ROC, "ROC TO IN %d\n", if_id);
+	priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
+	ret = WARN_ON(__bes2600_flush(hw_priv, false, if_id));
+
+	if (!ret) {
+		wsm_unlock_tx(hw_priv);
+		bes2600_disable_listening(priv);
+	}
+	atomic_set(&hw_priv->remain_on_channel, 0);
+	hw_priv->roc_if_id = -1;
+
+	bes2600_dbg(BES2600_DBG_ROC, "ROC TO OUT %d\n", if_id);
+
+	up(&hw_priv->conf_lock);
+	up(&hw_priv->scan.lock);
+	bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_ROC);
+}
+
+void bes2600_dynamic_opt_txrx_work(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, dynamic_opt_txrx_work);
+	struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
+	bes2600_dynamic_opt_rxtx(hw_priv,priv, 0);
+	bes2600_dbg(BES2600_DBG_STA, "bes2600_dynamic_opt_txrx_work called\n");
+}
+
+
+const u8 *bes2600_get_ie(u8 *start, size_t len, u8 ie)
+{
+	u8 *end, *pos;
+
+	pos = start;
+	if (pos == NULL)
+		return NULL;
+	end = pos + len;
+
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == ie)
+			return pos;
+		pos += 2 + pos[1];
+	}
+
+	return NULL;
+}
+
+/**
+ * bes2600_set_macaddrfilter -called when tesmode command
+ * is for setting mac address filter
+ *
+ * @hw: the hardware
+ * @data: incoming data
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int bes2600_set_macaddrfilter(struct bes2600_common *hw_priv, struct bes2600_vif *priv, u8 *data)
+{
+	struct wsm_mac_addr_filter *mac_addr_filter =  NULL;
+	struct wsm_mac_addr_info *addr_info = NULL;
+	u8 action_mode = 0, no_of_mac_addr = 0, i = 0;
+	int ret = 0;
+	u16 macaddrfiltersize = 0;
+
+	/* Retrieving Action Mode */
+	action_mode = data[0];
+	/* Retrieving number of address entries */
+	no_of_mac_addr = data[1];
+
+	addr_info = (struct wsm_mac_addr_info *)&data[2];
+
+	/* Computing sizeof Mac addr filter */
+	macaddrfiltersize =  sizeof(*mac_addr_filter) + \
+			(no_of_mac_addr * sizeof(struct wsm_mac_addr_info));
+
+	mac_addr_filter = kzalloc(macaddrfiltersize, GFP_KERNEL);
+	if (!mac_addr_filter) {
+		ret = -ENOMEM;
+		goto exit_p;
+	}
+	mac_addr_filter->action_mode = action_mode;
+	mac_addr_filter->numfilter = no_of_mac_addr;
+
+	for (i = 0; i < no_of_mac_addr; i++) {
+		mac_addr_filter->macaddrfilter[i].address_mode = \
+						addr_info[i].address_mode;
+		memcpy(mac_addr_filter->macaddrfilter[i].MacAddr, \
+				addr_info[i].MacAddr , ETH_ALEN);
+		mac_addr_filter->macaddrfilter[i].filter_mode = \
+						addr_info[i].filter_mode;
+	}
+	ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_MAC_ADDR_FILTER, \
+					 mac_addr_filter, macaddrfiltersize, priv->if_id));
+
+	kfree(mac_addr_filter);
+exit_p:
+	return ret;
+}
+
+#if 0
+/**
+ * bes2600_set_multicastaddrfilter -called when tesmode command
+ * is for setting the ipv4 address filter
+ *
+ * @hw: the hardware
+ * @data: incoming data
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int bes2600_set_multicastfilter(struct bes2600_common *hw_priv, struct bes2600_vif *priv, u8 *data)
+{
+	u8 i = 0;
+	int ret = 0;
+
+	memset(&priv->multicast_filter, 0, sizeof(priv->multicast_filter));
+	priv->multicast_filter.enable = (u32)data[0];
+	priv->multicast_filter.numOfAddresses = (u32)data[1];
+
+	for (i = 0; i < priv->multicast_filter.numOfAddresses; i++) {
+		memcpy(&priv->multicast_filter.macAddress[i], \
+			   &data[2+(i*ETH_ALEN)], ETH_ALEN);
+	}
+	/* Configure the multicast mib in case of drop all multicast */
+	if (priv->multicast_filter.enable != 2)
+		return ret;
+
+	ret = wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, \
+		&priv->multicast_filter, sizeof(priv->multicast_filter), priv->if_id);
+
+	return ret;
+}
+#endif
+
+#ifdef IPV6_FILTERING
+/**
+ * bes2600_set_ipv6addrfilter -called when tesmode command
+ * is for setting the ipv6 address filter
+ *
+ * @hw: the hardware
+ * @data: incoming data
+ * @if_id: interface id
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int bes2600_set_ipv6addrfilter(struct ieee80211_hw *hw,
+				     u8 *data, int if_id)
+{
+	struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv;
+	struct wsm_ipv6_filter  *ipv6_filter =  NULL;
+	struct ipv6_addr_info *ipv6_info = NULL;
+	u8 action_mode = 0, no_of_ip_addr = 0, i = 0, ret = 0;
+	u16 ipaddrfiltersize = 0;
+
+	/* Retrieving Action Mode */
+	action_mode = data[0];
+	/* Retrieving number of ipv4 address entries */
+	no_of_ip_addr = data[1];
+
+	ipv6_info = (struct ipv6_addr_info *)&data[2];
+
+	/* Computing sizeof Mac addr filter */
+	ipaddrfiltersize =  sizeof(*ipv6_filter) + \
+			(no_of_ip_addr * sizeof(struct wsm_ip6_addr_info));
+
+
+	ipv6_filter = kzalloc(ipaddrfiltersize, GFP_KERNEL);
+	if (!ipv6_filter) {
+		ret = -ENOMEM;
+		goto exit_p;
+	}
+	ipv6_filter->action_mode = action_mode;
+	ipv6_filter->numfilter = no_of_ip_addr;
+
+	for (i = 0; i < no_of_ip_addr; i++) {
+		ipv6_filter->ipv6filter[i].address_mode = \
+					ipv6_info[i].address_mode;
+		ipv6_filter->ipv6filter[i].filter_mode = \
+					ipv6_info[i].filter_mode;
+		memcpy(ipv6_filter->ipv6filter[i].ipv6, \
+					(u8 *)(ipv6_info[i].ipv6), 16);
+	}
+
+	ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_IP_IPV6_ADDR_FILTER, \
+					 ipv6_filter, ipaddrfiltersize, \
+					 if_id));
+
+	kfree(ipv6_filter);
+exit_p:
+	return ret;
+}
+#endif /*IPV6_FILTERING*/
+
+/**
+ * bes2600_set_data_filter -configure data filter in device
+*
+ * @hw: the hardware
+ * @vif: vif
+ * @data: incoming data
+ * @len: incoming data length
+ *
+ */
+void bes2600_set_data_filter(struct ieee80211_hw *hw,
+			   struct ieee80211_vif *vif,
+			   void *data, int len)
+{
+	int ret = 0;
+	//struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	int filter_id;
+
+	if (!data) {
+		ret = -EINVAL;
+		goto exit_p;
+	}
+	filter_id=*((enum bes2600_data_filterid*)data);
+
+	switch (filter_id) {
+#ifdef IPV6_FILTERING
+	case IPV6ADDR_FILTER_ID:
+		ret = bes2600_set_ipv6addrfilter(hw, \
+			&((u8 *)data)[4], priv->if_id);
+		break;
+#endif /*IPV6_FILTERING*/
+	default:
+		ret = -EINVAL;
+		break;
+	}
+exit_p:
+
+	 return ;
+}
+
+u32 bes2600_bh_get_encry_hdr_len(u32 cipherType)
+{
+	u32 encrypthdr = 0;
+
+	switch (cipherType) {
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		encrypthdr = WEP_ENCRYPT_HDR_SIZE;
+		break;
+	case WLAN_CIPHER_SUITE_TKIP:
+		encrypthdr = WPA_ENCRYPT_HDR_SIZE;
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		encrypthdr = WPA2_ENCRYPT_HDR_SIZE;
+		break;
+	case WLAN_CIPHER_SUITE_SMS4:
+		encrypthdr = WAPI_ENCRYPT_HDR_SIZE;
+		break;
+	default:
+		encrypthdr = 0;
+		break;
+	}
+
+	return encrypthdr;
+}
+
+/**
+ * bes2600_set_arpreply -called for creating and
+ * configuring arp response template frame
+ *
+ * @hw: the hardware
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int bes2600_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct bes2600_common *hw_priv = (struct bes2600_common *)hw->priv;
+        u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0;
+        bool encrypt = false;
+        int ret = 0;
+        u8 *template_frame = NULL;
+        struct ieee80211_hdr_3addr *dot11hdr = NULL;
+        struct ieee80211_snap_hdr *snaphdr = NULL;
+        struct arphdr *arp_hdr = NULL;
+
+        template_frame = kzalloc(MAX_ARP_REPLY_TEMPLATE_SIZE, GFP_ATOMIC);
+        if (!template_frame) {
+                bes2600_err(BES2600_DBG_STA, "[STA] Template frame memory failed\n");
+                ret = -ENOMEM;
+                goto exit_p;
+        }
+        dot11hdr = (struct ieee80211_hdr_3addr *)&template_frame[4];
+
+        framehdrlen = sizeof(*dot11hdr);
+        if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p)
+                priv->cipherType = WLAN_CIPHER_SUITE_CCMP;
+        switch (priv->cipherType) {
+
+        case WLAN_CIPHER_SUITE_WEP40:
+        case WLAN_CIPHER_SUITE_WEP104:
+                bes2600_dbg(BES2600_DBG_STA, "[STA] WEP\n");
+                encrypthdr = WEP_ENCRYPT_HDR_SIZE;
+                encrypttailsize = WEP_ENCRYPT_TAIL_SIZE;
+                encrypt = 1;
+                break;
+
+
+        case WLAN_CIPHER_SUITE_TKIP:
+                bes2600_dbg(BES2600_DBG_STA, "[STA] WPA\n");
+                encrypthdr = WPA_ENCRYPT_HDR_SIZE;
+                encrypttailsize = WPA_ENCRYPT_TAIL_SIZE;
+                encrypt = 1;
+                break;
+
+        case WLAN_CIPHER_SUITE_CCMP:
+                bes2600_dbg(BES2600_DBG_STA, "[STA] WPA2\n");
+                encrypthdr = WPA2_ENCRYPT_HDR_SIZE;
+                encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE;
+                encrypt = 1;
+                break;
+
+        case WLAN_CIPHER_SUITE_SMS4:
+                bes2600_dbg(BES2600_DBG_STA, "[STA] WAPI\n");
+                encrypthdr = WAPI_ENCRYPT_HDR_SIZE;
+                encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE;
+                encrypt = 1;
+                break;
+
+        default:
+                encrypthdr = 0;
+                encrypttailsize = 0;
+                encrypt = 0;
+                break;
+        }
+
+        framehdrlen += encrypthdr;
+
+        /* Filling the 802.11 Hdr */
+        dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA);
+        if (priv->vif->type == NL80211_IFTYPE_STATION)
+                dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS);
+        else
+                dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+
+        if (encrypt)
+                dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP);
+
+        if (priv->vif->bss_conf.qos) {
+                bes2600_dbg(BES2600_DBG_STA, "[STA] QOS Enabled\n");
+                dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP);
+                 *(u16 *)(dot11hdr + 1) = 0x0;
+                 framehdrlen += 2;
+        } else {
+                dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA);
+        }
+
+        memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN);
+        memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN);
+        memcpy(dot11hdr->addr3, priv->vif->bss_conf.bssid, ETH_ALEN);
+
+        /* Filling the LLC/SNAP Hdr */
+        snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen);
+        memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \
+                sizeof(*snaphdr));
+        *(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_ARP);
+        /* Updating the framebdylen with snaphdr and LLC hdr size */
+        framebdylen = sizeof(*snaphdr) + 2;
+
+        /* Filling the ARP Reply Payload */
+        arp_hdr = (struct arphdr *)((u8 *)dot11hdr + framehdrlen + framebdylen);
+        arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER);
+        arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP);
+        arp_hdr->ar_hln = ETH_ALEN;
+        arp_hdr->ar_pln = 4;
+        arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);
+
+        /* Updating the frmbdylen with Arp Reply Hdr and Arp payload size(20) */
+        framebdylen += sizeof(*arp_hdr) + 20;
+
+        /* Updating the framebdylen with Encryption Tail Size */
+        framebdylen += encrypttailsize;
+
+        /* Filling the Template Frame Hdr */
+        template_frame[0] = WSM_FRAME_TYPE_ARP_REPLY; /* Template frame type */
+        template_frame[1] = 0xFF; /* Rate to be fixed */
+        ((u16 *)&template_frame[2])[0] = framehdrlen + framebdylen;
+
+        ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, \
+                                template_frame, (framehdrlen+framebdylen+4), priv->if_id));
+        kfree(template_frame);
+exit_p:
+        return ret;
+}
+
+#ifdef IPV6_FILTERING
+/**
+ * bes2600_set_na -called for creating and
+ * configuring NDP Neighbor Advertisement (NA) template frame
+ *
+ * @hw: the hardware
+ * @vif: vif
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int bes2600_set_na(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
+	struct bes2600_common *hw_priv = (struct bes2600_common *)hw->priv;
+	u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0;
+	bool encrypt = false;
+	int ret = 0;
+	u8 *template_frame = NULL;
+	struct ieee80211_hdr_3addr *dot11hdr = NULL;
+	struct ieee80211_snap_hdr *snaphdr = NULL;
+	struct ipv6hdr *ipv6_hdr = NULL;
+	struct icmp6hdr *icmp6_hdr = NULL;
+	struct nd_msg *na = NULL;
+	struct nd_opt_hdr *opt_hdr = NULL;
+
+	template_frame = kzalloc(MAX_NEIGHBOR_ADVERTISEMENT_TEMPLATE_SIZE, GFP_ATOMIC);
+	if (!template_frame) {
+		bes2600_err(BES2600_DBG_STA, "[STA] Template frame memory failed\n");
+		ret = -ENOMEM;
+		goto exit_p;
+	}
+	dot11hdr = (struct ieee80211_hdr_3addr *)&template_frame[4];
+
+	framehdrlen = sizeof(*dot11hdr);
+        if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p)
+		priv->cipherType = WLAN_CIPHER_SUITE_CCMP;
+	switch (priv->cipherType) {
+
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		bes2600_dbg(BES2600_DBG_STA, "[STA] WEP\n");
+		encrypthdr = WEP_ENCRYPT_HDR_SIZE;
+		encrypttailsize = WEP_ENCRYPT_TAIL_SIZE;
+		encrypt = 1;
+		break;
+
+
+	case WLAN_CIPHER_SUITE_TKIP:
+		bes2600_dbg(BES2600_DBG_STA, "[STA] WPA\n");
+		encrypthdr = WPA_ENCRYPT_HDR_SIZE;
+		encrypttailsize = WPA_ENCRYPT_TAIL_SIZE;
+		encrypt = 1;
+		break;
+
+	case WLAN_CIPHER_SUITE_CCMP:
+		bes2600_dbg(BES2600_DBG_STA, "[STA] WPA2\n");
+		encrypthdr = WPA2_ENCRYPT_HDR_SIZE;
+		encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE;
+		encrypt = 1;
+		break;
+
+	case WLAN_CIPHER_SUITE_SMS4:
+		bes2600_dbg(BES2600_DBG_STA, "[STA] WAPI\n");
+		encrypthdr = WAPI_ENCRYPT_HDR_SIZE;
+		encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE;
+		encrypt = 1;
+		break;
+
+	default:
+		encrypthdr = 0;
+		encrypttailsize = 0;
+		encrypt = 0;
+		break;
+	}
+
+	framehdrlen += encrypthdr;
+
+	/* Filling the 802.11 Hdr */
+	dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA);
+	if (priv->vif->type == NL80211_IFTYPE_STATION)
+		dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS);
+	else
+		dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+
+	if (encrypt)
+		dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP);
+
+	if (priv->vif->bss_conf.qos) {
+		bes2600_dbg(BES2600_DBG_STA, "[STA] QOS Enabled\n");
+		dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP);
+		/* Filling QOS Control Field */
+		 *(u16 *)(dot11hdr + 1) = 0x0;
+		 framehdrlen += 2;
+	} else {
+		dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA);
+	}
+
+	memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN);
+	memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN);
+	memcpy(dot11hdr->addr3, priv->vif->bss_conf.bssid, ETH_ALEN);
+
+	/* Filling the LLC/SNAP Hdr */
+	snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen);
+	memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \
+		sizeof(*snaphdr));
+	*(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_IPV6);
+	/* Updating the framebdylen with snaphdr and LLC hdr size */
+	framebdylen = sizeof(*snaphdr) + 2;
+
+	/* Filling the ipv6 header */
+	ipv6_hdr = (struct ipv6hdr *)((u8 *)dot11hdr + framehdrlen + framebdylen);
+	ipv6_hdr->version = 6;
+	ipv6_hdr->priority = 0;
+	ipv6_hdr->payload_len = cpu_to_be16(32); /* ??? check the be or le ??? whether to use cpu_to_be16(32)*/
+	ipv6_hdr->nexthdr = 58;
+	ipv6_hdr->hop_limit = 255;
+
+	/* Updating the framebdylen with ipv6 Hdr */
+	framebdylen += sizeof(*ipv6_hdr);
+
+	/* Filling the Neighbor Advertisement */
+	na = (struct nd_msg *)((u8 *)dot11hdr + framehdrlen + framebdylen);
+	icmp6_hdr = (struct icmp6hdr *)(&na->icmph);
+	icmp6_hdr->icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+	icmp6_hdr->icmp6_code = 0;
+	/* checksum (2 bytes), RSO fields (4 bytes) and target IP address (16 bytes) shall be filled by firmware */
+
+	/* Filling the target link layer address in the optional field */
+	opt_hdr = (struct nd_opt_hdr *)(&na->opt[0]);
+	opt_hdr->nd_opt_type = 2;
+	opt_hdr->nd_opt_len = 1;
+	/* optional target link layer address (6 bytes) shall be filled by firmware */
+
+	/* Updating the framebdylen with the ipv6 payload length */
+	framebdylen += 32;
+
+	/* Updating the framebdylen with Encryption Tail Size */
+	framebdylen += encrypttailsize;
+
+	/* Filling the Template Frame Hdr */
+	template_frame[0] = WSM_FRAME_TYPE_NA; /* Template frame type */
+	template_frame[1] = 0xFF; /* Rate to be fixed */
+	((u16 *)&template_frame[2])[0] = framehdrlen + framebdylen;
+
+	ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, \
+				template_frame, (framehdrlen+framebdylen+4), \
+				priv->if_id));
+
+	kfree(template_frame);
+
+exit_p:
+	return ret;
+}
+#endif /*IPV6_FILTERING*/
+
+#ifdef CONFIG_BES2600_TESTMODE
+/**
+ * bes2600_set_snap_frame -Set SNAP frame format
+ *
+ * @hw: the hardware
+ * @data: data frame
+ * @len: data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int bes2600_set_snap_frame(struct ieee80211_hw *hw,
+				 u8 *data, int len)
+{
+	struct bes_msg_set_snap_frame *snap_frame =
+		(struct bes_msg_set_snap_frame *) data;
+	struct bes2600_common *priv = (struct bes2600_common *) hw->priv;
+	u8 frame_len = snap_frame->len;
+	u8 *frame = &snap_frame->frame[0];
+
+	/*
+	 * Check length of incoming frame format:
+	 * SNAP + SNAP_LEN (u8)
+	 */
+	if (frame_len + sizeof(snap_frame->len) != len)
+		return -EINVAL;
+
+	if (frame_len > 0) {
+		priv->test_frame.data = (u8 *) krealloc(priv->test_frame.data,
+						sizeof(u8) * frame_len,
+						GFP_KERNEL);
+		if (priv->test_frame.data == NULL) {
+			bes2600_dbg(BES2600_DBG_TEST_MODE, "bes2600_set_snap_frame memory" \
+					 "allocation failed");
+			priv->test_frame.len = 0;
+			return -EINVAL;
+		}
+		memcpy(priv->test_frame.data, frame, frame_len);
+	} else {
+		kfree(priv->test_frame.data);
+		priv->test_frame.data = NULL;
+	}
+	priv->test_frame.len = frame_len;
+	return 0;
+}
+
+
+/**
+ * bes2600_set_txqueue_params -Set txqueue params after successful TSPEC negotiation
+ *
+ * @hw: the hardware
+ * @data: data frame
+ * @len: data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int bes2600_set_txqueue_params(struct ieee80211_hw *hw,
+				     u8 *data, int len)
+{
+	struct bes_msg_set_txqueue_params *txqueue_params =
+		(struct bes_msg_set_txqueue_params *) data;
+	struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv;
+	struct bes2600_vif *priv;
+	/* Interface ID is hard coded here, as interface is not
+         * passed in testmode command.
+         * Also it is assumed here that STA will be on interface
+         * 0 always.
+         */
+
+	int if_id = 0;
+	u16 queueId = bes2600_priority_to_queueId[txqueue_params->user_priority];
+
+	priv = cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
+
+	if (unlikely(!priv)) {
+		bes2600_err(BES2600_DBG_TEST_MODE, "[STA] %s: Warning Priv is Null\n",
+			   __func__);
+		return 0;
+	}
+	spin_unlock(&priv->vif_lock);
+
+	/* Default Ack policy is WSM_ACK_POLICY_NORMAL */
+	WSM_TX_QUEUE_SET(&priv->tx_queue_params,
+			queueId,
+			WSM_ACK_POLICY_NORMAL,
+			txqueue_params->medium_time,
+			txqueue_params->expiry_time);
+	return WARN_ON(wsm_set_tx_queue_params(hw_priv,
+			&priv->tx_queue_params.params[queueId], queueId,
+			priv->if_id));
+}
+
+/**
+ * bes2600_testmode_reply -called inside a testmode command
+ * handler to send a response to user space
+ *
+ * @wiphy: the wiphy
+ * @data: data to be send to user space
+ * @len: data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int bes2600_testmode_reply(struct wiphy *wiphy,
+				const void *data, int len)
+{
+	int ret = 0;
+	struct sk_buff *skb = cfg80211_testmode_alloc_reply_skb(wiphy,
+		nla_total_size(len));
+
+	bes2600_dbg(BES2600_DBG_TEST_MODE, "%s\n", __func__);
+	if (!skb)
+		return -ENOMEM;
+
+	ret = nla_put(skb, BES_TM_MSG_DATA, len, data);
+	if (ret) {
+		kfree_skb(skb);
+		return ret;
+	}
+
+	return cfg80211_testmode_reply(skb);
+}
+
+/**
+ * bes2600_testmode_event -send asynchronous event
+ * to userspace
+ *
+ * @wiphy: the wiphy
+ * @msg_id: BES msg ID
+ * @data: data to be sent
+ * @len: data length
+ * @gfp: allocation flag
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int bes2600_testmode_event(struct wiphy *wiphy, const u32 msg_id,
+			 const void *data, int len, gfp_t gfp)
+{
+	struct sk_buff *skb = cfg80211_testmode_alloc_event_skb(wiphy,
+		nla_total_size(len+sizeof(msg_id)), gfp);
+
+	if (!skb)
+		return -ENOMEM;
+
+	if (nla_put_u32(skb, BES_TM_MSG_ID, msg_id))
+		goto nla_put_failure;
+	if (data)
+		if (nla_put(skb, BES_TM_MSG_DATA, len, data))
+			goto nla_put_failure;
+
+	cfg80211_testmode_event(skb, gfp);
+	return 0;
+nla_put_failure:
+	kfree_skb(skb);
+	return -ENOBUFS;
+}
+
+/**
+ * example function for test purposes
+ * sends both: synchronous reply and asynchronous event
+ */
+static int bes2600_test(struct ieee80211_hw *hw,
+		       void *data, int len)
+{
+	struct bes_msg_test_t *test_p;
+	struct bes_reply_test_t reply;
+	struct bes_event_test_t event;
+
+	if (sizeof(struct bes_msg_test_t)  != len)
+		return -EINVAL;
+
+	test_p = (struct bes_msg_test_t *) data;
+
+	reply.dummy = test_p->dummy + 10;
+
+	event.dummy = test_p->dummy + 20;
+
+	if (bes2600_testmode_event(hw->wiphy, BES_MSG_EVENT_TEST,
+		&event, sizeof(event), GFP_KERNEL))
+		return -1;
+
+	return bes2600_testmode_reply(hw->wiphy, &reply, sizeof(reply));
+}
+
+/**
+ * bes2600_get_tx_power_level - send tx power level
+ * to userspace
+ *
+ * @hw: the hardware
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int bes2600_get_tx_power_level(struct ieee80211_hw *hw)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	int get_power = 0;
+	get_power = hw_priv->output_power;
+	bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] %s: Power set on Device : %d",
+		__func__, get_power);
+	return bes2600_testmode_reply(hw->wiphy, &get_power, sizeof(get_power));
+}
+
+/**
+ * bes2600_get_tx_power_range- send tx power range
+ * to userspace for each band
+ *
+ * @hw: the hardware
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int bes2600_get_tx_power_range(struct ieee80211_hw *hw)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	struct wsm_tx_power_range txPowerRange[2];
+	size_t len = sizeof(txPowerRange);
+	memcpy(txPowerRange, hw_priv->txPowerRange, len);
+	return bes2600_testmode_reply(hw->wiphy, txPowerRange, len);
+}
+
+/**
+ * bes2600_set_advance_scan_elems -Set Advcance Scan
+ * elements
+ * @hw: the hardware
+ * @data: data frame
+ * @len: data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int bes2600_set_advance_scan_elems(struct ieee80211_hw *hw,
+				 u8 *data, int len)
+{
+	struct advance_scan_elems *scan_elems =
+		(struct advance_scan_elems *) data;
+	struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv;
+	size_t elems_len = sizeof(struct advance_scan_elems);
+
+	if (elems_len != len)
+		return -EINVAL;
+
+	scan_elems = (struct advance_scan_elems *) data;
+
+	/* Locks required to prevent simultaneous scan */
+	down(&hw_priv->scan.lock);
+	down(&hw_priv->conf_lock);
+
+	hw_priv->advanceScanElems.scanMode = scan_elems->scanMode;
+	hw_priv->advanceScanElems.duration = scan_elems->duration;
+	hw_priv->enable_advance_scan = true;
+
+	up(&hw_priv->conf_lock);
+	up(&hw_priv->scan.lock);
+
+	return 0;
+}
+
+/**
+ * bes2600_set_power_save -Set Power Save
+ * elements
+ * @hw: the hardware
+ * @data: data frame
+ * @len: data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int bes2600_set_power_save(struct ieee80211_hw *hw,
+				 u8 *data, int len)
+{
+	struct power_save_elems *ps_elems =
+		(struct power_save_elems *) data;
+	struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv;
+	size_t elems_len = sizeof(struct power_save_elems);
+	struct bes2600_vif *priv;
+	int if_id = 0;
+	/* Interface ID is hard coded here, as interface is not
+         * passed in testmode command.
+         * Also it is assumed here that STA will be on interface
+         * 0 always.
+         */
+
+	if (elems_len != len)
+		return -EINVAL;
+
+	priv = cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
+
+	if (unlikely(!priv)) {
+		bes2600_err(BES2600_DBG_TEST_MODE, "[STA] %s: Warning Priv is Null\n",
+			   __func__);
+		return 0;
+	}
+
+	spin_unlock(&priv->vif_lock);
+	down(&hw_priv->conf_lock);
+
+	ps_elems = (struct power_save_elems *) data;
+
+	if (ps_elems->powerSave == 1)
+		priv->user_pm_mode = WSM_PSM_PS;
+	else
+		priv->user_pm_mode = WSM_PSM_FAST_PS;
+
+	bes2600_info(BES2600_DBG_TEST_MODE, "[STA] Aid: %d, Joined: %s, Powersave: %s\n",
+		priv->bss_params.aid,
+		priv->join_status == BES2600_JOIN_STATUS_STA ? "yes" : "no",
+		priv->user_pm_mode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" :
+		priv->user_pm_mode == WSM_PSM_PS ? "WSM_PSM_PS" :
+		priv->user_pm_mode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" :
+		"UNKNOWN");
+	if (priv->join_status == BES2600_JOIN_STATUS_STA &&
+			priv->bss_params.aid &&
+			priv->setbssparams_done &&
+			priv->filter4.enable) {
+		priv->powersave_mode.pmMode = priv->user_pm_mode;
+		bes2600_set_pm(priv, &priv->powersave_mode);
+	}
+	else
+		priv->user_power_set_true = ps_elems->powerSave;
+	up(&hw_priv->conf_lock);
+	return 0;
+}
+/**
+ * bes2600_start_stop_tsm - starts/stops collecting TSM
+ *
+ * @hw: the hardware
+ * @data: data frame
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
+{
+	struct bes_msg_start_stop_tsm *start_stop_tsm =
+		(struct bes_msg_start_stop_tsm *) data;
+	struct bes2600_common *hw_priv = hw->priv;
+	hw_priv->start_stop_tsm.start = start_stop_tsm->start;
+	hw_priv->start_stop_tsm.up = start_stop_tsm->up;
+	hw_priv->start_stop_tsm.packetization_delay =
+		start_stop_tsm->packetization_delay;
+	bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: start : %u: up : %u",
+		__func__, hw_priv->start_stop_tsm.start,
+		hw_priv->start_stop_tsm.up);
+	hw_priv->tsm_info.ac = bes2600_1d_to_ac[start_stop_tsm->up];
+
+	if (!hw_priv->start_stop_tsm.start) {
+		spin_lock_bh(&hw_priv->tsm_lock);
+		memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats));
+		memset(&hw_priv->tsm_info, 0, sizeof(hw_priv->tsm_info));
+		spin_unlock_bh(&hw_priv->tsm_lock);
+	}
+	return 0;
+}
+
+/**
+ * bes2600_get_tsm_params - Retrieves TSM parameters
+ *
+ * @hw: the hardware
+ *
+ * Returns: TSM parameters collected
+ */
+int bes2600_get_tsm_params(struct ieee80211_hw *hw)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	struct bes_tsm_stats tsm_stats;
+	u32 pkt_count;
+	spin_lock_bh(&hw_priv->tsm_lock);
+	pkt_count = hw_priv->tsm_stats.txed_msdu_count -
+			 hw_priv->tsm_stats.msdu_discarded_count;
+	if (pkt_count) {
+		hw_priv->tsm_stats.avg_q_delay =
+			hw_priv->tsm_info.sum_pkt_q_delay/(pkt_count * 1000);
+		hw_priv->tsm_stats.avg_transmit_delay =
+			hw_priv->tsm_info.sum_media_delay/pkt_count;
+	} else {
+		hw_priv->tsm_stats.avg_q_delay = 0;
+		hw_priv->tsm_stats.avg_transmit_delay = 0;
+	}
+	bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Txed MSDU count : %u",
+		__func__, hw_priv->tsm_stats.txed_msdu_count);
+	bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Average queue delay : %u",
+			__func__, hw_priv->tsm_stats.avg_q_delay);
+	bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Average transmit delay : %u",
+			__func__, hw_priv->tsm_stats.avg_transmit_delay);
+	memcpy(&tsm_stats, &hw_priv->tsm_stats, sizeof(hw_priv->tsm_stats));
+	/* Reset the TSM statistics */
+	memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats));
+	hw_priv->tsm_info.sum_pkt_q_delay = 0;
+	hw_priv->tsm_info.sum_media_delay = 0;
+	spin_unlock_bh(&hw_priv->tsm_lock);
+	return bes2600_testmode_reply(hw->wiphy, &tsm_stats,
+				     sizeof(hw_priv->tsm_stats));
+}
+
+/**
+ * bes2600_get_roam_delay - Retrieves roam delay
+ *
+ * @hw: the hardware
+ *
+ * Returns: Returns the last measured roam delay
+ */
+int bes2600_get_roam_delay(struct ieee80211_hw *hw)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
+	bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Roam delay : %u",
+		__func__, roam_delay);
+	spin_lock_bh(&hw_priv->tsm_lock);
+	hw_priv->tsm_info.roam_delay = 0;
+	hw_priv->tsm_info.use_rx_roaming = 0;
+	spin_unlock_bh(&hw_priv->tsm_lock);
+	return bes2600_testmode_reply(hw->wiphy, &roam_delay, sizeof(u16));
+}
+
+/* tcp & udp alive test */
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+
+int bes2600_set_ipv4addrfilter(struct bes2600_common *hw_priv, u8 *data, int if_id)
+{
+	struct wsm_ipv4_filter  *ipv4_filter =  NULL;
+	struct ipv4_addr_info *ipv4_info = NULL;
+	u8 action_mode = 0, no_of_ip_addr = 0, i = 0, ret = 0;
+	u16 ipaddrfiltersize = 0;
+
+	/* Retrieving Action Mode */
+	action_mode = data[0];
+	/* Retrieving number of ipv4 address entries */
+	no_of_ip_addr = data[1];
+
+	ipv4_info = (struct ipv4_addr_info *)&data[2];
+
+	/* Computing sizeof Mac addr filter */
+	ipaddrfiltersize =  sizeof(*ipv4_filter) + \
+			    (no_of_ip_addr * sizeof(struct wsm_ip4_addr_info));
+
+
+	ipv4_filter = kzalloc(ipaddrfiltersize, GFP_KERNEL);
+	if (!ipv4_filter) {
+		ret = -ENOMEM;
+		goto exit_p;
+	}
+	ipv4_filter->action_mode = action_mode;
+	ipv4_filter->numfilter = no_of_ip_addr;
+
+	for (i = 0; i < no_of_ip_addr; i++) {
+		ipv4_filter->ipv4filter[i].address_mode = \
+							  ipv4_info[i].address_mode;
+		ipv4_filter->ipv4filter[i].filter_mode = \
+							 ipv4_info[i].filter_mode;
+		memcpy(ipv4_filter->ipv4filter[i].ipv4, \
+		       (u8 *)(ipv4_info[i].ipv4), 4);
+	}
+
+	ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_IPV4_ADDR_FILTERING, \
+				    ipv4_filter, ipaddrfiltersize, \
+				    if_id));
+
+	kfree(ipv4_filter);
+exit_p:
+	return ret;
+}
+
+
+/* Checksum a block of data */
+static uint16_t csum(uint16_t *packet, int packlen)
+{
+	uint32_t sum = 0;
+
+	while (packlen > 1) {
+		sum += *(packet++);
+		packlen -= 2;
+	}
+
+	if (packlen > 0)
+		sum += *(unsigned char *)packet;
+
+	while (sum >> 16)
+		sum = (sum & 0xffff) + (sum >> 16);
+
+	return (uint16_t) ~sum;
+}
+
+
+static void tcpcsum(struct ip_header *ip,
+                       struct tcp_header *tcp,
+                       uint8_t *data,
+                       uint16_t data_len)
+{
+	uint8_t tcphd_len = sizeof(struct tcp_header);
+	uint16_t *buf = kzalloc(12 + tcphd_len + data_len, GFP_KERNEL);
+	uint8_t *tempbuf = (uint8_t *)buf;
+
+	if(tempbuf == NULL) {
+		bes2600_err(BES2600_DBG_TEST_MODE, "Out of memory: TCP checksum not computed\n");
+		return;
+	}
+
+	tcp->chksum = 0;
+	/* Set up the pseudo header */
+	memcpy(tempbuf, &(ip->src), sizeof(uint32_t));
+	memcpy(&(tempbuf[4]), &(ip->dest), sizeof(uint32_t));
+	tempbuf[8] = 0;
+	tempbuf[9] = (uint16_t)ip->_proto;
+	tempbuf[10] = (uint16_t)((tcphd_len + data_len) & 0xFF00) >> 8;
+	tempbuf[11] = (uint16_t)((tcphd_len + data_len) & 0x00FF);
+	/* Copy the TCP header and data */
+	memcpy(tempbuf + 12, (uint8_t *)tcp, tcphd_len);
+	memcpy(tempbuf + 12 + tcphd_len, data, data_len);
+	/* CheckSum it */
+	tcp->chksum = csum(buf, 12 + tcphd_len + data_len);
+	kfree(buf);
+}
+
+int bes2600_set_ip_offload(struct bes2600_common *hw_priv,
+                            struct bes2600_vif *priv,
+                            struct ip_alive_cfg *iac,
+                            u16 idx)
+{
+	u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0, tu_len, tu_proto;
+	bool encrypt = false;
+	int ret = 0;
+	u8 *tmp_frame = NULL;
+	u8 *ptr = NULL;
+	struct ieee80211_hdr_3addr *dot11hdr = NULL;
+	struct ieee80211_snap_hdr *snaphdr = NULL;
+	struct ip_header *ipheader = NULL;
+	struct tcp_header *tcpheader = NULL;
+	struct udp_header *udpheader = NULL;
+	u8 *tcpbody = NULL;
+	u8 *udpbody = NULL;
+	u8 EncrType;
+
+	u8 *payload = iac->bd.payload;
+	u32 bodylen = iac->bd.len;
+	tmp_frame = kzalloc(MAX_TCP_ALIVE_TEMPLATE_SIZE, GFP_ATOMIC);
+	if (!tmp_frame) {
+		bes2600_err(BES2600_DBG_TEST_MODE, "[STA] Template frame memory failed\n");
+		ret = -ENOMEM;
+		goto exit_p;
+	}
+	dot11hdr = (struct ieee80211_hdr_3addr *)&tmp_frame[4];
+
+	framehdrlen = sizeof(*dot11hdr);
+	if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p)
+		priv->cipherType = WLAN_CIPHER_SUITE_CCMP;
+	switch (priv->cipherType) {
+
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] WEP\n");
+		encrypthdr = WEP_ENCRYPT_HDR_SIZE;
+		encrypttailsize = WEP_ENCRYPT_TAIL_SIZE;
+		encrypt = 1;
+		EncrType = 1;
+		break;
+
+	case WLAN_CIPHER_SUITE_TKIP:
+		bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] WPA\n");
+		encrypthdr = WPA_ENCRYPT_HDR_SIZE;
+		encrypttailsize = WPA_ENCRYPT_TAIL_SIZE;
+		encrypt = 1;
+		EncrType = 3;
+		break;
+
+	case WLAN_CIPHER_SUITE_CCMP:
+		bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] WPA2\n");
+		encrypthdr = WPA2_ENCRYPT_HDR_SIZE;
+		encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE;
+		encrypt = 1;
+		EncrType = 5;
+		break;
+
+	case WLAN_CIPHER_SUITE_SMS4:
+		bes2600_dbg(BES2600_DBG_TEST_MODE, "[STA] WAPI\n");
+		encrypthdr = WAPI_ENCRYPT_HDR_SIZE;
+		encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE;
+		encrypt = 1;
+		EncrType = 7;
+		break;
+
+	default:
+		encrypthdr = 0;
+		encrypttailsize = 0;
+		encrypt = 0;
+		EncrType = 8;
+		break;
+	}
+
+	framehdrlen += encrypthdr;
+
+	/* Filling the 802.11 Hdr */
+	dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA);
+	if (priv->vif->type == NL80211_IFTYPE_STATION)
+		dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS);
+	else
+		dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+
+	if (encrypt)
+		dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP);
+
+	if (priv->vif->bss_conf.qos) {
+		bes2600_info(BES2600_DBG_TEST_MODE, "[STA] QOS Enabled\n");
+		dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP);
+		*(u16 *)(dot11hdr + 1) = 0x0;
+		framehdrlen += 2;
+	} else {
+		dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA);
+	}
+
+	memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN);
+	memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN);
+	memcpy(dot11hdr->addr3, iac->bd.dest_mac, ETH_ALEN);
+
+	/* Filling the LLC/SNAP Hdr */
+	snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen);
+	memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \
+	       sizeof(*snaphdr));
+	*(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_IP);
+	/* Updating the framebdylen with snaphdr and LLC hdr size */
+	framebdylen = sizeof(*snaphdr) + 2;
+
+	/* proto == 0 for udp stream or 1 for tcp stream; */
+	(iac->bd.proto) ? (tu_len = sizeof(struct tcp_header)):(tu_len = sizeof(struct udp_header));
+	(iac->bd.proto) ? (tu_proto = TCP_PROTO):(tu_proto = UDP_PROTO);
+
+	/* Filling the ip header */
+	ptr = (u8 *)dot11hdr + framehdrlen + framebdylen;
+	ipheader = (struct ip_header *)kzalloc(128, GFP_ATOMIC);
+	ipheader->_v_hl = 0x45;
+	ipheader->_tos = 0x00;
+	ipheader->_len = __swab16(sizeof(*ipheader) + tu_len + bodylen);
+	ipheader->_id = 0;
+	ipheader->_offset = 0;
+	ipheader->_ttl = 0xff;
+	ipheader->_proto = tu_proto;
+	ipheader->_chksum = 0;
+	ipheader->src = __swab32(iac->iphd.src);
+	ipheader->dest = __swab32(iac->iphd.dest);
+
+	/* Calculate ip header's checksum value; */
+	ipheader->_chksum = (csum((uint16_t *)ipheader, sizeof(*ipheader)));
+
+	/* Updating the frmbdylen with ip Hdr */
+	framebdylen += sizeof(*ipheader);
+
+	if (tu_proto == TCP_PROTO) {
+		/* Filling the tcp header */
+		tcpheader = (struct tcp_header *)((u8 *)ipheader + sizeof(struct ip_header));
+		tcpheader->src = __swab16(iac->tcphd.src);
+		tcpheader->dest = __swab16(iac->tcphd.dest);
+		tcpheader->seqno = __swab32(iac->bd.next_seqno);
+		tcpheader->ackno = __swab32(iac->tcphd.ackno);
+		tcpheader->_hdrlen_rsvd_flags = 0x1050;
+		tcpheader->wnd = 0xFFFF;
+		tcpheader->chksum = 0;
+		tcpheader->urgp = 0;
+
+		/* Updating the frmbdylen with tcp Hdr */
+		framebdylen += sizeof(*tcpheader);
+
+		/* Filling the tcp body */
+		tcpbody = (u8 *)dot11hdr + framehdrlen + framebdylen;
+		memcpy(tcpbody, payload, bodylen);
+
+		/* Calculate tcp's checksum value; */
+		tcpcsum(ipheader, tcpheader, payload, bodylen);
+	} else if (tu_proto == UDP_PROTO) {
+		/* Filling the udp header */
+		udpheader = (struct udp_header *)((u8 *)ipheader + sizeof(struct ip_header));
+		udpheader->src = __swab16(iac->udphd.src);
+		udpheader->dest = __swab16(iac->udphd.dest);
+		udpheader->len = __swab16(sizeof(struct udp_header) + bodylen);
+		udpheader->chksum = 0;
+
+		/* Updating the frmbdylen with udp Hdr */
+		framebdylen += sizeof(*udpheader);
+
+		/* Filling the tcp body */
+		udpbody = (u8 *)dot11hdr + framehdrlen + framebdylen;
+		memcpy(udpbody, payload, bodylen);
+	}
+
+	memcpy(ptr, ipheader, sizeof(struct ip_header) + tu_len);
+	kfree(ipheader);
+
+	/* Updating the frmbdylen with tcp payload */
+	framebdylen += bodylen;
+
+	/* Updating the framebdylen with Encryption Tail Size */
+	framebdylen += encrypttailsize;
+
+	/* Filling the Template Frame Hdr.
+	 * Bit7 of the first idx stards for tcp/udp protocol,0 for udp and 1 for tcp;
+	 * Bit6 indicates if send the heartbeat or not. 1 to send and 0 not.
+	 * Bits 3-5 reserve for use.
+	 * Bit 0-2 stands for stream index.
+	 */
+	if (iac->klv_vendor == KLV_VENDOR_DEFAULT) {
+		tmp_frame[0] = idx | (1 << 6) | (iac->bd.proto << 7);
+	} else {
+		tmp_frame[0] = idx | (1 << 6) | (iac->bd.proto << 7);
+	}
+	tmp_frame[1] = 0xFF; /* Fixed to 0xFF */
+	((u16 *)&tmp_frame[2])[0] = framehdrlen + framebdylen + AES_KEY_IV_LEN + 1;
+	memcpy(&tmp_frame[framehdrlen + framebdylen + 4], iac->aes_key, AES_KEY_LEN);
+	memcpy(&tmp_frame[framehdrlen + framebdylen + 20], iac->aes_iv, AES_IV_LEN);
+	/*
+	 *bit0-bit3 stands for encrypt type,
+	 *bit4-bit7 stands for klv_vendor;
+	 */
+	tmp_frame[framehdrlen + framebdylen + 4 + AES_KEY_IV_LEN] = (EncrType | (iac->klv_vendor << 4));
+
+	ret = WARN_ON(wsm_write_mib(hw_priv,
+                                WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_FRAME,
+                                tmp_frame,
+                                (framehdrlen + framebdylen + 4 + AES_KEY_IV_LEN + 1),
+                                priv->if_id));
+	kfree(tmp_frame);
+
+exit_p:
+	return ret;
+}
+
+int bes2600_del_ip_offload(struct bes2600_common *hw_priv,
+                            struct bes2600_vif *priv,
+                            u8 stream_idx)
+{
+	u8 tmp_frame[8];
+	int ret;
+
+	/* Filling the Template Frame Hdr */
+	tmp_frame[0] = stream_idx;
+	tmp_frame[1] = 0xFF; /* Fixed to 0xFF */
+	((u16 *)&tmp_frame[2])[0] = 0;
+
+	ret = WARN_ON(wsm_write_mib(hw_priv,
+                                WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_FRAME,
+                                tmp_frame,
+                                4,
+                                priv->if_id));
+
+	return ret;
+}
+
+int bes2600_en_ip_offload(struct bes2600_common *hw_priv,
+                           struct bes2600_vif *priv,
+                           u16 period_in_s)
+{
+	int ret;
+	struct MIB_TCP_KEEP_ALIVE_PERIOD period;
+	u8 EncrType;
+
+	if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p)
+		priv->cipherType = WLAN_CIPHER_SUITE_CCMP;
+	switch (priv->cipherType) {
+
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		EncrType = 1;
+		break;
+
+	case WLAN_CIPHER_SUITE_TKIP:
+		EncrType = 3;
+		break;
+
+	case WLAN_CIPHER_SUITE_CCMP:
+		EncrType = 5;
+		break;
+
+	case WLAN_CIPHER_SUITE_SMS4:
+		EncrType = 7;
+		break;
+
+	default:
+		EncrType = 8;
+		break;
+	}
+
+	/* Send mib WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_PERIOD */
+	period.TcpKeepAlivePeriod = period_in_s;
+	period.EncrType = EncrType;
+	period.Reserved = 0;
+	ret = WARN_ON(wsm_write_mib(hw_priv,
+                                WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_PERIOD,
+                                (u8 *)&period,
+                                sizeof(period),
+                                priv->if_id));
+
+	return ret;
+}
+
+static int search_for_free_stream(struct bes2600_common *hw_priv, uint8_t proto,
+					uint16_t src_port, uint16_t dst_port,
+					uint32_t src_ip, uint32_t dst_ip)
+{
+	int i = 0, free_idx = -1;
+	struct ip_alive_cfg *iac = hw_priv->iac;
+
+	/* If port matches, overwrites the configuration; */
+	for (i = 0; i < NUM_IP_FRAMES; i++) {
+		if (iac[i].bd.idx_used == 1 &&
+		    iac[i].bd.proto == proto &&
+		    iac[i].bd.src_port == src_port &&
+		    iac[i].bd.dest_port == dst_port &&
+		    iac[i].bd.src_ip == src_ip &&
+		    iac[i].bd.dest_ip == dst_ip) {
+			free_idx = i;
+			break;
+		} else if (free_idx == -1 && iac[i].bd.idx_used == 0) {
+			free_idx = i;
+			iac[i].bd.idx_used = 1;
+		}
+	}
+
+	return free_idx;
+}
+
+static int get_keep_alive_used_stream(struct ieee80211_hw *hw)
+{
+	int i;
+	struct ip_alive_cfg *iac = ((struct bes2600_common *)hw->priv)->iac;
+	u8 stream = 0;
+
+	for (i = 0; i < NUM_IP_FRAMES; i++) {
+		if (iac[i].bd.idx_used == 1)
+			stream |= (1 << i);
+	}
+	return bes2600_testmode_reply(hw->wiphy, &stream, sizeof(u8));
+}
+
+#ifdef VENDOR_XM_KEEPALIVE
+void bes2600_get_keepalive_info(struct bes2600_common *hw_priv, struct ip_alive_satus *status)
+{
+	int i;
+	struct ip_alive_cfg *iac = hw_priv->iac;
+
+	status->udp = false;
+	status->tcp = false;
+
+	for (i = 0; i < NUM_IP_FRAMES; i++) {
+		if (iac[i].bd.idx_used == 1) {
+			if (iac[i].bd.proto == 0)
+				status->udp = true;
+			else if (iac[i].bd.proto == 1)
+				status->tcp = true;
+		}
+	}
+}
+#endif
+
+static int net_device_add_ip_offload(struct ieee80211_hw *hw, u8 *data, int len)
+{
+	struct ip_alive_paras *paras = (struct ip_alive_paras *)data;
+	struct ip_alive_cfg *iac = ((struct bes2600_common *)hw->priv)->iac;
+	int idx_check;
+	struct ip_stream_cfg idx_cfg = {
+		.err_code = 0,
+		.idx = -1,
+	};
+
+	/* only allowed paras->idx == 0xF */
+	if (paras->idx != 0xF) {
+		idx_cfg.err_code = -1; /* cmd with idx err */
+		bes2600_info(BES2600_DBG_TEST_MODE, "cmd with idx err\n");
+		goto err_idx;
+	}
+
+	if (paras->payload_len <= IP_KEEPALIVE_MAX_LEN) {
+		/* idx == 0xF, find a free one. */
+		idx_check = search_for_free_stream(hw->priv, paras->proto,
+		                                    paras->src_port,
+		                                    paras->dst_port,
+		                                    paras->src_ip,
+		                                    paras->dst_ip);
+		if (idx_check < 0 || idx_check >= NUM_IP_FRAMES) {
+			paras->idx = 0xF;
+			idx_cfg.err_code = -2; /* get stream fail */
+			bes2600_info(BES2600_DBG_TEST_MODE, "Exceeded maximum number of keepalive streams\n");
+			goto err_idx;
+		} else {
+			paras->idx = idx_check;
+		}
+
+		iac[paras->idx].bd.proto = paras->proto;
+		iac[paras->idx].klv_vendor = paras->klv_vendor;
+		iac[paras->idx].bd.src_port = paras->src_port;
+		iac[paras->idx].bd.dest_port = paras->dst_port;
+		iac[paras->idx].iphd.src = iac[paras->idx].bd.src_ip = paras->src_ip;
+		iac[paras->idx].iphd.dest = iac[paras->idx].bd.dest_ip = paras->dst_ip;
+		if (paras->proto) { //tcp
+			iac[paras->idx].tcphd.src = paras->src_port;
+			iac[paras->idx].tcphd.dest = paras->dst_port;
+		} else { //udp
+			iac[paras->idx].udphd.src = paras->src_port;
+			iac[paras->idx].udphd.dest = paras->dst_port;
+		}
+
+		if (iac[paras->idx].klv_vendor == KLV_VENDOR_DEFAULT) {
+			iac[paras->idx].bd.len = paras->payload_len;
+		} else if (iac[paras->idx].klv_vendor == KLV_VENDOR_XM) {
+			iac[paras->idx].bd.len = paras->payload_len;
+			if (paras->proto) { //tcp
+				//Reserve space for pkcs5 padding and base64 coding in firmware;
+				iac[paras->idx].bd.len = (iac[paras->idx].bd.len + (AES_KEY_LEN -
+											(iac[paras->idx].bd.len % AES_KEY_LEN))) * 8 / 6;
+				//Reserve space for websocket frame's head,
+				iac[paras->idx].bd.len += WEBSOCKET_HD_LEN;
+			}
+		}
+
+		memset(iac[paras->idx].bd.payload, 0, IP_KEEPALIVE_MAX_LEN);
+		memcpy(iac[paras->idx].bd.payload, paras->payload, paras->payload_len);
+		memcpy(iac[paras->idx].aes_key, paras->key, AES_KEY_LEN);
+		memcpy(iac[paras->idx].aes_iv, paras->iv, AES_IV_LEN);
+
+		bes2600_info(BES2600_DBG_TEST_MODE, "idx = %d, len=%d\n", paras->idx, iac[paras->idx].bd.len);
+		idx_cfg.idx = paras->idx;
+	} else {
+		bes2600_err(BES2600_DBG_TEST_MODE, "payload_len = %d, too long!\n", paras->payload_len);
+		idx_cfg.err_code = -3; /* playload err */
+	}
+
+err_idx:
+	return bes2600_testmode_reply(hw->wiphy, &idx_cfg, sizeof(struct ip_stream_cfg));
+
+}
+
+static int net_device_del_ip_offload(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len)
+{
+	struct ip_alive_iac_idx *alive_iac_idx = (struct ip_alive_iac_idx *)data;
+	int idx = alive_iac_idx->idx;
+	struct bes2600_common *hw_priv = hw->priv;
+	struct ip_alive_cfg *iac = hw_priv->iac;
+	struct bes2600_vif *priv;
+
+	if (vif) {
+		priv = cw12xx_get_vif_from_ieee80211(vif);
+	} else {
+		bes2600_err(BES2600_DBG_TEST_MODE, "failed to get vif\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (idx <= NUM_IP_FRAMES && idx >= 0) {
+		iac[idx].bd.idx_used = 0;
+		bes2600_del_ip_offload(hw_priv, priv, idx);
+		bes2600_info(BES2600_DBG_TEST_MODE, "delete idx = %d\n", idx);
+	} else {
+		bes2600_err(BES2600_DBG_TEST_MODE, "wrong idx = %d!\n", idx);
+		idx = -1; /* invalid idx */
+	}
+	return bes2600_testmode_reply(hw->wiphy, &idx, sizeof(int));
+}
+
+
+static int net_device_en_ip_offload(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len)
+{
+	struct ip_alive_period *alive_period = (struct ip_alive_period *)data;
+	int period = alive_period->period;
+	struct bes2600_common *hw_priv = hw->priv;
+	struct ip_alive_cfg *iac = hw_priv->iac;
+	struct bes2600_vif *priv;
+	u8 idx;
+	u8 ip_ack_filter[10] = {
+	1, // action mode: 0 disable, 1 filter out, 2 filter in
+	1, // filter number
+	1, // filter_mode: 0 disable, 1 filter out, 2 filter in
+	3, // address_mode: 1, src addr, 2 dest addr, 3 tcp ack
+	};
+
+	if (vif) {
+		priv = cw12xx_get_vif_from_ieee80211(vif);
+	} else {
+		bes2600_err(BES2600_DBG_TEST_MODE, "failed to get vif\n");
+		return -EOPNOTSUPP;
+	}
+
+	for (idx = 0; idx < NUM_IP_FRAMES; idx++) {
+		if (iac[idx].bd.idx_used) {
+			bes2600_set_ip_offload(hw_priv, priv, &iac[idx], idx);
+			bes2600_set_ipv4addrfilter(hw_priv, ip_ack_filter, priv->if_id);
+		}
+	}
+
+	bes2600_en_ip_offload(hw_priv, priv, period);
+	bes2600_info(BES2600_DBG_TEST_MODE, "en period = %d\n", period);
+
+	return 0;
+}
+#endif /* CONFIG_BES2600_KEEP_ALIVE */
+
+#ifdef PLAT_ALLWINNER_R329
+static int bes2600_factory_cali_to_flash(struct ieee80211_hw *hw)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	u8 *factory_data = NULL;
+	u8 *file_buffer = NULL;
+	u32 factory_data_len = 0;
+	int ret;
+
+	if (bes2600_chrdev_is_signal_mode())
+		return -EOPNOTSUPP;
+
+	if (!(file_buffer = bes2600_factory_get_file_buffer()))
+		return -ENOMEM;
+
+	bes2600_factory_lock();
+	factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_PATH);
+	if (!factory_data) {
+		bes2600_warn(BES2600_DBG_DOWNLOAD, "factory cali data get failed.\n");
+		ret = -ENOENT;
+	} else {
+		bes2600_factory_data_check(factory_data);
+		factory_little_endian_cvrt(factory_data);
+		ret = wsm_save_factory_txt_to_flash(hw_priv, factory_data, 0);
+		bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "save factory data to flash failed.\n");
+	}
+	bes2600_factory_free_file_buffer(file_buffer);
+	bes2600_factory_unlock();
+
+	if (ret >= 0)
+		return bes2600_testmode_reply(hw->wiphy, &ret, sizeof(int));
+
+	return ret;
+
+}
+#endif
+
+static int bes2600_vendor_cpu_usage(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	return wsm_cpu_usage_cmd(hw->priv);
+}
+
+static int bes2600_vendor_epta_parm_config(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len)
+{
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	struct bes2600_common *hw_priv = hw->priv;
+	struct vendor_epta_parm *epta_para = (struct vendor_epta_parm *)data;
+
+	return coex_set_epta_params(hw_priv, epta_para->wlan_duration,
+				epta_para->bt_duration, epta_para->hw_epta_enable);
+#else
+	return -EOPNOTSUPP;
+#endif
+}
+
+/**
+ * bes2600_testmode_cmd -called when tesmode command
+ * reaches bes2600
+ *
+ * @hw: the hardware
+ * @data: incoming data
+ * @len: incoming data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int bes2600_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len)
+{
+	int ret = 0;
+	struct nlattr *type_p = nla_find(data, len, BES_TM_MSG_ID);
+	struct nlattr *data_p = nla_find(data, len, BES_TM_MSG_DATA);
+
+	if (!type_p || !data_p)
+		return -EINVAL;
+
+	bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: type: %i\n",
+		__func__, nla_get_u32(type_p));
+
+	switch (nla_get_u32(type_p)) {
+	case BES_MSG_TEST:
+		ret = bes2600_test(hw,
+			nla_data(data_p), nla_len(data_p));
+		break;
+	case BES_MSG_SET_SNAP_FRAME:
+		ret = bes2600_set_snap_frame(hw, (u8 *) nla_data(data_p),
+			nla_len(data_p));
+		break;
+	case BES_MSG_GET_TX_POWER_LEVEL:
+		ret = bes2600_get_tx_power_level(hw);
+		break;
+	case BES_MSG_GET_TX_POWER_RANGE:
+		ret = bes2600_get_tx_power_range(hw);
+		break;
+	case BES_MSG_SET_ADVANCE_SCAN_ELEMS:
+		ret = bes2600_set_advance_scan_elems(hw, (u8 *) nla_data(data_p),
+			nla_len(data_p));
+		break;
+	case BES_MSG_SET_TX_QUEUE_PARAMS:
+		ret = bes2600_set_txqueue_params(hw, (u8 *) nla_data(data_p),
+			nla_len(data_p));
+		break;
+	case BES_MSG_GET_TSM_PARAMS:
+		ret = bes2600_get_tsm_params(hw);
+		break;
+	case BES_MSG_START_STOP_TSM:
+		ret = bes2600_start_stop_tsm(hw, (u8 *) nla_data(data_p));
+		break;
+	case BES_MSG_GET_ROAM_DELAY:
+		ret = bes2600_get_roam_delay(hw);
+		break;
+	case BES_MSG_SET_POWER_SAVE:
+		ret = bes2600_set_power_save(hw, (u8 *) nla_data(data_p),
+			nla_len(data_p));
+		break;
+	case BES_MSG_ADD_IP_OFFLOAD:
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+		ret = net_device_add_ip_offload(hw, (u8 *) nla_data(data_p),
+			nla_len(data_p));
+#else
+		ret = -EPERM;
+#endif
+		break;
+	case BES_MSG_DEL_IP_OFFLOAD:
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+		ret = net_device_del_ip_offload(hw, vif, (u8 *) nla_data(data_p),
+			nla_len(data_p));
+#else
+		ret = -EPERM;
+#endif
+		break;
+	case BES_MSG_SET_IP_OFFLOAD_PERIOD:
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+		ret = net_device_en_ip_offload(hw, vif, (u8 *) nla_data(data_p),
+			nla_len(data_p));
+#else
+		ret = -EPERM;
+#endif
+		break;
+	case BES_MSG_GET_KEEP_ALIVE_STREAM:
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+		ret = get_keep_alive_used_stream(hw);
+#else
+		ret = -EPERM;
+#endif
+		break;
+	case BES_MSG_SAVE_CALI_TXT_TO_FLASH:
+#ifdef PLAT_ALLWINNER_R329
+		ret = bes2600_factory_cali_to_flash(hw);
+#else
+		ret = -EPERM;
+#endif
+		break;
+	case BES_MSG_VENDOR_RF_CMD:
+		ret = bes2600_vendor_rf_cmd(hw, vif, (u8 *) nla_data(data_p),
+			nla_len(data_p));
+		break;
+	case BES_MSG_EPTA_PARM_CONFIG:
+		ret = bes2600_vendor_epta_parm_config(hw, vif, (u8 *) nla_data(data_p),
+			nla_len(data_p));
+		break;
+	case BES_MSG_MCU_CPUUSAGE:
+		ret = bes2600_vendor_cpu_usage(hw, vif);
+		break;
+	default:
+		break;
+	}
+	return ret;
+}
+#endif /* CONFIG_BES2600_TESTMODE */
diff --git a/drivers/staging/bes2600/sta.h b/drivers/staging/bes2600/sta.h
new file mode 100644
index 000000000000..f97e8fc1bb21
--- /dev/null
+++ b/drivers/staging/bes2600/sta.h
@@ -0,0 +1,146 @@
+/*
+ * Mac80211 STA interface for BES2600 mac80211 drivers
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/version.h>
+#ifndef STA_H_INCLUDED
+#define STA_H_INCLUDED
+
+/* ******************************************************************** */
+/* mac80211 API								*/
+
+int bes2600_start(struct ieee80211_hw *dev);
+void bes2600_stop(struct ieee80211_hw *dev);
+int bes2600_add_interface(struct ieee80211_hw *dev,
+			 struct ieee80211_vif *vif);
+void bes2600_remove_interface(struct ieee80211_hw *dev,
+			     struct ieee80211_vif *vif);
+int bes2600_change_interface(struct ieee80211_hw *dev,
+				struct ieee80211_vif *vif,
+				enum nl80211_iftype new_type,
+				bool p2p);
+
+int bes2600_config(struct ieee80211_hw *dev, u32 changed);
+int bes2600_change_interface(struct ieee80211_hw *dev,
+                                struct ieee80211_vif *vif,
+                                enum nl80211_iftype new_type,
+                                bool p2p);
+void bes2600_configure_filter(struct ieee80211_hw *dev,
+			     unsigned int changed_flags,
+			     unsigned int *total_flags,
+			     u64 multicast);
+int bes2600_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+		u16 queue, const struct ieee80211_tx_queue_params *params);
+int bes2600_get_stats(struct ieee80211_hw *dev,
+		     struct ieee80211_low_level_stats *stats);
+/* Not more a part of interface?
+int bes2600_get_tx_stats(struct ieee80211_hw *dev,
+			struct ieee80211_tx_queue_stats *stats);
+*/
+int bes2600_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+		   struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+		   struct ieee80211_key_conf *key);
+
+int bes2600_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
+
+void bes2600_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		  u32 queues, bool drop);
+
+int bes2600_remain_on_channel(struct ieee80211_hw *hw,
+				 struct ieee80211_vif *vif,
+				 struct ieee80211_channel *chan,
+				 int duration,
+				 enum ieee80211_roc_type type);
+
+int bes2600_cancel_remain_on_channel(struct ieee80211_hw *hw
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)
+	, struct ieee80211_vif *vif
+#endif
+	);
+
+int bes2600_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+
+u64 bes2600_prepare_multicast(struct ieee80211_hw *hw,
+			     struct netdev_hw_addr_list *mc_list);
+
+int bes2600_set_pm(struct bes2600_vif *priv, const struct wsm_set_pm *arg);
+
+void bes2600_set_data_filter(struct ieee80211_hw *hw,
+			   struct ieee80211_vif *vif,
+			   void *data,
+			   int len);
+
+u32 bes2600_bh_get_encry_hdr_len(u32 cipherType);
+/* ******************************************************************** */
+/* WSM callbacks							*/
+
+/* void bes2600_set_pm_complete_cb(struct bes2600_common *hw_priv,
+	struct wsm_set_pm_complete *arg); */
+void bes2600_channel_switch_cb(struct bes2600_common *hw_priv);
+
+/* ******************************************************************** */
+/* WSM events								*/
+
+void bes2600_free_event_queue(struct bes2600_common *hw_priv);
+void bes2600_event_handler(struct work_struct *work);
+void bes2600_bss_loss_work(struct work_struct *work);
+void bes2600_connection_loss_work(struct work_struct *work);
+void bes2600_keep_alive_work(struct work_struct *work);
+void bes2600_tx_failure_work(struct work_struct *work);
+void bes2600_dynamic_opt_txrx_work(struct work_struct *work);
+
+/* ******************************************************************** */
+/* Internal API								*/
+
+int bes2600_setup_mac(struct bes2600_common *hw_priv);
+void bes2600_join_work(struct work_struct *work);
+void bes2600_join_timeout(struct work_struct *work);
+void bes2600_unjoin_work(struct work_struct *work);
+void bes2600_offchannel_work(struct work_struct *work);
+void bes2600_wep_key_work(struct work_struct *work);
+void bes2600_update_filtering(struct bes2600_vif *priv);
+void bes2600_update_filtering_work(struct work_struct *work);
+int __bes2600_flush(struct bes2600_common *hw_priv, bool drop, int if_id);
+void bes2600_set_beacon_wakeup_period_work(struct work_struct *work);
+int bes2600_enable_listening(struct bes2600_vif *priv,
+			struct ieee80211_channel *chan);
+int bes2600_disable_listening(struct bes2600_vif *priv);
+int bes2600_set_uapsd_param(struct bes2600_vif *priv,
+				const struct wsm_edca_params *arg);
+void bes2600_ba_work(struct work_struct *work);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+void bes2600_ba_timer(struct timer_list *t);
+#else
+void bes2600_ba_timer(unsigned long arg);
+#endif
+const u8 *bes2600_get_ie(u8 *start, size_t len, u8 ie);
+int bes2600_vif_setup(struct bes2600_vif *priv);
+int bes2600_setup_mac_pvif(struct bes2600_vif *priv);
+void bes2600_iterate_vifs(void *data, u8 *mac,
+			 struct ieee80211_vif *vif);
+void bes2600_rem_chan_timeout(struct work_struct *work);
+int bes2600_set_macaddrfilter(struct bes2600_common *hw_priv, struct bes2600_vif *priv, u8 *data);
+#ifdef IPV6_FILTERING
+int bes2600_set_na(struct ieee80211_hw *hw,
+			struct ieee80211_vif *vif);
+#endif /*IPV6_FILTERING*/
+#ifdef CONFIG_BES2600_TESTMODE
+void bes2600_device_power_calc(struct bes2600_common *priv,
+			      s16 max_output_power, s16 fe_cor, u32 band);
+int bes2600_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len);
+int bes2600_testmode_event(struct wiphy *wiphy, const u32 msg_id,
+			 const void *data, int len, gfp_t gfp);
+int bes2600_get_tx_power_range(struct ieee80211_hw *hw);
+int bes2600_get_tx_power_level(struct ieee80211_hw *hw);
+#endif /* CONFIG_BES2600_TESTMODE */
+#ifdef CONFIG_BES2600_WLAN_BES
+int bes2600_wifi_start(struct bes2600_common *hw_priv);
+int bes2600_wifi_stop(struct bes2600_common *hw_priv);
+#endif
+#endif /* STA_H_INCLUDED */
diff --git a/drivers/staging/bes2600/tx_loop.c b/drivers/staging/bes2600/tx_loop.c
new file mode 100644
index 000000000000..dbd5c37c6b9d
--- /dev/null
+++ b/drivers/staging/bes2600/tx_loop.c
@@ -0,0 +1,530 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "bes2600.h"
+#include "wsm.h"
+#include "queue.h"
+
+struct tx_loop_table
+{
+        u16 cmd;
+        void (*proc)(struct bes2600_common *hw_priv, u8 *buf, u32 len);
+};
+
+static void bes2600_tx_loop_build_lmac_tx_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len);
+static void bes2600_tx_loop_build_scan_compl_ind(struct bes2600_common *hw_priv);
+static void bes2600_tx_loop_build_pm_ind(struct bes2600_common *hw_priv);
+
+void bes2600_tx_loop_init(struct bes2600_common *hw_priv)
+{
+        hw_priv->tx_loop.enabled = false;
+        hw_priv->tx_loop.start_lmac_seq = 0;
+        hw_priv->tx_loop.start_mcu_seq = 0;
+        spin_lock_init(&hw_priv->tx_loop.tx_loop_lock);
+        skb_queue_head_init(&hw_priv->tx_loop.rx_queue);
+}
+
+struct sk_buff *bes2600_tx_loop_read(struct bes2600_common *hw_priv)
+{
+        struct sk_buff *skb;
+        struct wsm_hdr *wsm;
+
+        if(hw_priv == NULL)
+                return NULL;
+
+	skb = skb_dequeue(&hw_priv->tx_loop.rx_queue);
+        if(skb != NULL) {
+                wsm = (struct wsm_hdr *)skb->data;
+                bes2600_dbg(BES2600_DBG_TXLOOP, "tx loop pipe read msg_id:0x%04x seq:%d\n",
+                                WSM_MSG_ID_GET(wsm->id), WSM_MSG_SEQ_GET(wsm->id));
+        }
+
+	return skb;
+}
+
+static void bes2600_tx_loop_item_pending_item(struct bes2600_common *hw_priv, struct sk_buff *skb)
+{
+        bes2600_info(BES2600_DBG_TXLOOP, "tx loop confirm pending skb.\n");
+        bes2600_tx_loop_build_lmac_tx_cfm(hw_priv, skb->data, skb->data_len);
+}
+
+void bes2600_tx_loop_record_wsm_cmd(struct bes2600_common *hw_priv, u8 *wsm_cmd)
+{
+        hw_priv->tx_loop.wsm_cmd_ptr = wsm_cmd;
+}
+
+void bes2600_tx_loop_clear_wsm_cmd(struct bes2600_common *hw_priv)
+{
+        hw_priv->tx_loop.wsm_cmd_ptr = NULL;
+}
+
+void bes2600_tx_loop_set_enable(struct bes2600_common *hw_priv)
+{
+        int i = 0;
+        u16 cmd_id = -1;
+
+        if(hw_priv == NULL)
+                return;
+
+        if(hw_priv->tx_loop.enabled)
+                return;
+
+        WARN_ON(1);
+        hw_priv->tx_loop.enabled = true;
+        hw_priv->tx_loop.start_lmac_seq = hw_priv->wsm_rx_seq[0];
+        hw_priv->tx_loop.start_mcu_seq = hw_priv->wsm_rx_seq[1];
+
+        if(hw_priv->tx_loop.wsm_cmd_ptr) {
+                struct wsm_hdr *tx_wsm = (struct wsm_hdr *)hw_priv->tx_loop.wsm_cmd_ptr;
+                cmd_id = tx_wsm->id & WSM_MSG_ID_MASK;
+
+                if(cmd_id == 0x0005 || cmd_id == 0x0006){
+                        struct wsm_mib *mib = (struct wsm_mib *)hw_priv->wsm_cmd.arg;
+                        u16 mib_id = mib->mibId;
+                        bes2600_info(BES2600_DBG_TXLOOP, "pending cmd:0x%04x seq:%d mib_id:0x%04x\n",
+                                                        cmd_id, WSM_MSG_SEQ_GET(tx_wsm->id), mib_id);
+                } else {
+                        bes2600_info(BES2600_DBG_TXLOOP, "pending cmd:0x%04x seq:%d\n",
+                                                        cmd_id, WSM_MSG_SEQ_GET(tx_wsm->id));
+                }
+
+                bes2600_tx_loop_pipe_send(hw_priv, hw_priv->tx_loop.wsm_cmd_ptr, 8);
+        }
+
+        if (atomic_read(&hw_priv->scan.in_progress) && cmd_id != 0x0007) {
+                bes2600_tx_loop_build_scan_compl_ind(hw_priv);
+        } else if (atomic_read(&hw_priv->bes_power.pm_set_in_process) && cmd_id != 0x0010) {
+                bes2600_tx_loop_build_pm_ind(hw_priv);
+        }
+
+        for (i = 0; i < 4; i++) {
+                bes2600_queue_iterate_pending_packet(&hw_priv->tx_queue[i],
+				                bes2600_tx_loop_item_pending_item);
+        }
+
+        if (atomic_read(&hw_priv->bh_rx) > 0)
+		wake_up(&hw_priv->bh_wq);
+
+}
+
+static void bes2600_tx_loop_build_lmac_generic_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
+{
+        struct sk_buff *out_skb;
+        struct wsm_hdr *rx_wsm, *tx_wsm;
+        u32 msg_len = sizeof(struct wsm_hdr) + 4;
+        u16 msg_id = 0;
+
+        tx_wsm = (struct wsm_hdr *)buf;
+
+        out_skb = dev_alloc_skb(msg_len);
+        if(IS_ERR_OR_NULL(out_skb)) {
+                bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__);
+                return;
+        }
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n",
+                                __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
+        skb_put(out_skb, msg_len);
+        rx_wsm = (struct wsm_hdr *)out_skb->data;
+        msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
+        msg_id |= 0x400;        // set confirm flag
+        msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
+        rx_wsm->id = __cpu_to_le16(msg_id);
+        rx_wsm->len = __cpu_to_le16(msg_len);
+        *((u32 *)&rx_wsm[1]) = __cpu_to_le32(WSM_STATUS_SUCCESS);
+
+        spin_lock(&hw_priv->tx_loop.tx_loop_lock);
+        hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
+        skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
+        spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
+
+        atomic_add_return(1, &hw_priv->bh_rx);
+}
+
+static void bes2600_tx_loop_build_lmac_tx_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
+{
+        struct sk_buff *out_skb;
+        struct wsm_hdr *rx_wsm;
+        struct wsm_tx *tx_wsm;
+        struct wsm_tx_confirm *cfm;
+        u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_tx_confirm);
+        u16 msg_id = 0;
+
+        tx_wsm = (struct wsm_tx *)buf;
+
+        out_skb = dev_alloc_skb(msg_len);
+        if(IS_ERR_OR_NULL(out_skb)) {
+                bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__);
+                return;
+        }
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n",
+                                __func__, (tx_wsm->hdr.id & WSM_MSG_ID_MASK), msg_len);
+        skb_put(out_skb, msg_len);
+        rx_wsm = (struct wsm_hdr *)out_skb->data;
+        msg_id = (tx_wsm->hdr.id & WSM_MSG_ID_MASK);
+        msg_id |= 0x400;        // set confirm flag
+        msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
+        rx_wsm->id = __cpu_to_le16(msg_id);
+        rx_wsm->len = __cpu_to_le16(msg_len);
+
+        cfm = (struct wsm_tx_confirm *)&rx_wsm[1];
+        cfm->packetID = tx_wsm->packetID;
+        cfm->status = WSM_STATUS_SUCCESS;
+        cfm->txedRate = tx_wsm->maxTxRate;
+        cfm->ackFailures = 0;
+        cfm->flags = WSM_TX_STATUS_NORMAL_ACK;
+        cfm->txQueueDelay = 5;
+        cfm->mediaDelay = 3;
+
+        spin_lock(&hw_priv->tx_loop.tx_loop_lock);
+        hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
+        skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
+        spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
+
+        atomic_add_return(1, &hw_priv->bh_rx);
+}
+
+static void bes2600_tx_loop_build_config_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
+{
+        struct sk_buff *out_skb;
+        struct wsm_hdr *rx_wsm;
+        struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
+        u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_configuration) + 4;
+        u16 msg_id = 0;
+
+        out_skb = dev_alloc_skb(msg_len);
+        if(IS_ERR_OR_NULL(out_skb)) {
+                bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__);
+                return;
+        }
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n",
+                                __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
+        skb_put(out_skb, msg_len);
+        rx_wsm = (struct wsm_hdr *)out_skb->data;
+        msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
+        msg_id |= 0x400;        // set confirm flag
+        msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
+        rx_wsm->id = __cpu_to_le16(msg_id);
+        rx_wsm->len = __cpu_to_le16(msg_len);
+        *(u32 *)&rx_wsm[1] = WSM_STATUS_SUCCESS;
+
+        spin_lock(&hw_priv->tx_loop.tx_loop_lock);
+        hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
+        skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
+        spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
+
+        atomic_add_return(1, &hw_priv->bh_rx);
+}
+
+static void bes2600_tx_loop_build_read_mib_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
+{
+        struct sk_buff *out_skb;
+        struct wsm_hdr *rx_wsm;
+        struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
+        struct wsm_mib *mib = (struct wsm_mib *)hw_priv->wsm_cmd.arg;
+        u32 *status;
+        u16 *mib_id, *size;
+        u32 msg_len = sizeof(struct wsm_hdr) + 4 /* status */ + 2 /* mib_id */ + mib->buf_size;
+        u16 msg_id = 0;
+
+        out_skb = dev_alloc_skb(msg_len);
+        if(IS_ERR_OR_NULL(out_skb)) {
+                bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__);
+                return;
+        }
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n",
+                                __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
+        skb_put(out_skb, msg_len);
+        rx_wsm = (struct wsm_hdr *)out_skb->data;
+        msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
+        msg_id |= 0x400;        // set confirm flag
+        msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
+        rx_wsm->id = __cpu_to_le16(msg_id);
+        rx_wsm->len = __cpu_to_le16(msg_len);
+
+        status = (u32 *)&rx_wsm[1];
+        *status = WSM_STATUS_SUCCESS;
+
+        mib_id = (u16 *)&status[1];
+        *mib_id = mib->mibId;
+
+        size = (u16 *)&mib_id[1];
+        *size = mib->buf_size;
+
+        spin_lock(&hw_priv->tx_loop.tx_loop_lock);
+        hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
+        skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
+        spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
+
+        atomic_add_return(1, &hw_priv->bh_rx);
+}
+
+static void bes2600_tx_loop_build_join_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
+{
+        struct sk_buff *out_skb;
+        struct wsm_hdr *rx_wsm;
+        struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
+        u16 msg_id = 0;
+        u32 msg_len = sizeof(struct wsm_hdr) + 4 /* status */ + 8 /* power_level */;
+
+        out_skb = dev_alloc_skb(msg_len);
+        if(IS_ERR_OR_NULL(out_skb)) {
+                bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__);
+                return;
+        }
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n",
+                                __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
+        skb_put(out_skb, msg_len);
+        rx_wsm = (struct wsm_hdr *)out_skb->data;
+        msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
+        msg_id |= 0x400;        // set confirm flag
+        msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
+        rx_wsm->id = __cpu_to_le16(msg_id);
+        rx_wsm->len = __cpu_to_le16(msg_len);
+        *(u32 *)&rx_wsm[1] = WSM_STATUS_SUCCESS;
+
+        spin_lock(&hw_priv->tx_loop.tx_loop_lock);
+        hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
+        skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
+        spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
+
+        atomic_add_return(1, &hw_priv->bh_rx);
+}
+
+static void bes2600_tx_loop_build_rfcmd_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
+{
+        struct sk_buff *out_skb;
+        struct wsm_mcu_hdr *rx_wsm;
+        struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
+        u16 msg_id = 0;
+        u32 msg_len = sizeof(struct wsm_mcu_hdr);
+
+        out_skb = dev_alloc_skb(msg_len);
+        if(IS_ERR_OR_NULL(out_skb)) {
+                bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__);
+                return;
+        }
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n",
+                                        __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
+        skb_put(out_skb, msg_len);
+        rx_wsm = (struct wsm_mcu_hdr *)out_skb->data;
+        msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
+        msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_mcu_seq);
+        rx_wsm->hdr.id = __cpu_to_le16(msg_id);
+        rx_wsm->hdr.len = __cpu_to_le16(msg_len);
+        rx_wsm->handle_label = WSM_TO_MCU_CMD_CONFIRM_LABEL;
+        rx_wsm->cmd_type = (tx_wsm->id & WSM_MSG_ID_MASK);
+
+        spin_lock(&hw_priv->tx_loop.tx_loop_lock);
+        hw_priv->tx_loop.start_mcu_seq = (hw_priv->tx_loop.start_mcu_seq + 1) & 7;
+        skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
+        spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
+
+        atomic_add_return(1, &hw_priv->bh_rx);
+}
+
+static void bes2600_tx_loop_build_driver_cmd_cfm(struct bes2600_common *hw_priv, u8 *buf, u32 len)
+{
+        struct sk_buff *out_skb;
+        struct wsm_mcu_hdr *rx_wsm;
+        struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
+        u16 msg_id = 0;
+        u32 msg_len = sizeof(struct wsm_mcu_hdr) + 4;
+
+        out_skb = dev_alloc_skb(msg_len);
+        if(IS_ERR_OR_NULL(out_skb)) {
+                bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__);
+                return;
+        }
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "%s cmd 0x%04x len:%d\n",
+                                        __func__, (tx_wsm->id & WSM_MSG_ID_MASK), msg_len);
+        skb_put(out_skb, msg_len);
+        rx_wsm = (struct wsm_mcu_hdr *)out_skb->data;
+        msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
+        msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_mcu_seq);
+        rx_wsm->hdr.id = __cpu_to_le16(msg_id);
+        rx_wsm->hdr.len = __cpu_to_le16(msg_len);
+        rx_wsm->handle_label = WSM_TO_MCU_CMD_CONFIRM_LABEL;
+        rx_wsm->cmd_type = (tx_wsm->id & WSM_MSG_ID_MASK);
+        *(uint32_t *)(rx_wsm + 1) = 0;
+
+        spin_lock(&hw_priv->tx_loop.tx_loop_lock);
+        hw_priv->tx_loop.start_mcu_seq = (hw_priv->tx_loop.start_mcu_seq + 1) & 7;
+        skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
+        spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
+
+        atomic_add_return(1, &hw_priv->bh_rx);
+}
+
+static struct tx_loop_table tx_loop_tbl[] = {
+        {.cmd = 0x0004, .proc = bes2600_tx_loop_build_lmac_tx_cfm},
+        {.cmd = 0x0009, .proc = bes2600_tx_loop_build_config_cfm},
+        {.cmd = 0x0005, .proc = bes2600_tx_loop_build_read_mib_cfm},
+        {.cmd = 0x0006, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x000B, .proc = bes2600_tx_loop_build_join_cfm},
+        {.cmd = 0x0007, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0008, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x000A, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x000C, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x000D, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0010, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0011, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0012, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0013, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0016, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0017, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0018, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0019, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x001A, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x001B, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x001C, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0029, .proc = bes2600_tx_loop_build_lmac_generic_cfm},
+        {.cmd = 0x0C25, .proc = bes2600_tx_loop_build_rfcmd_cfm},
+        {.cmd = 0x0C27, .proc = bes2600_tx_loop_build_driver_cmd_cfm},
+};
+
+static void bes2600_tx_loop_build_scan_compl_ind(struct bes2600_common *hw_priv)
+{
+        struct sk_buff *out_skb;
+        struct wsm_hdr *rx_wsm;
+        struct wsm_scan_complete *scan_compl;
+        u16 msg_id = 0;
+        u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_scan_complete);
+
+        out_skb = dev_alloc_skb(msg_len);
+        if(IS_ERR_OR_NULL(out_skb)) {
+                bes2600_info(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__);
+                return;
+        }
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "%s len:%d\n", __func__, msg_len);
+        skb_put(out_skb, msg_len);
+        rx_wsm = (struct wsm_hdr *)out_skb->data;
+        msg_id |= 0x806;        // set indication flag
+        msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
+        rx_wsm->id = __cpu_to_le16(msg_id);
+        rx_wsm->len = __cpu_to_le16(msg_len);
+
+        scan_compl = (struct wsm_scan_complete *)&rx_wsm[1];
+        scan_compl->status = WSM_STATUS_SUCCESS;
+        scan_compl->psm = WSM_PSM_PS;
+        scan_compl->numChannels = 2;
+
+        spin_lock(&hw_priv->tx_loop.tx_loop_lock);
+        hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
+        skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
+        spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
+
+        atomic_add_return(1, &hw_priv->bh_rx);
+}
+
+static void bes2600_tx_loop_build_pm_ind(struct bes2600_common *hw_priv)
+{
+        struct sk_buff *out_skb;
+        struct wsm_hdr *rx_wsm;
+        struct wsm_set_pm_complete *pm_compl;
+        u16 msg_id = 0;
+        u32 msg_len = sizeof(struct wsm_hdr) + sizeof(struct wsm_set_pm_complete);
+
+        out_skb = dev_alloc_skb(msg_len);
+        if(IS_ERR_OR_NULL(out_skb)) {
+                bes2600_info(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__);
+                return;
+        }
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "%s len:%d\n", __func__, msg_len);
+        skb_put(out_skb, msg_len);
+        rx_wsm = (struct wsm_hdr *)out_skb->data;
+        msg_id |= 0x809;        // set indication flag
+        msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_lmac_seq);
+        rx_wsm->id = __cpu_to_le16(msg_id);
+        rx_wsm->len = __cpu_to_le16(msg_len);
+
+        pm_compl = (struct wsm_set_pm_complete *)&rx_wsm[1];
+        pm_compl->status = WSM_STATUS_SUCCESS;
+        pm_compl->psm = WSM_PSM_PS;
+
+        spin_lock(&hw_priv->tx_loop.tx_loop_lock);
+        hw_priv->tx_loop.start_lmac_seq = (hw_priv->tx_loop.start_lmac_seq + 1) & 7;
+        skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
+        spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
+
+        atomic_add_return(1, &hw_priv->bh_rx);
+}
+
+static void bes2600_tx_loop_build_rfcmd_ind(struct bes2600_common *hw_priv, u8 *buf, u32 len)
+{
+        struct sk_buff *out_skb;
+        struct wsm_mcu_hdr *rx_wsm;
+        struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
+        u16 msg_id = 0;
+        u32 msg_len = sizeof(struct wsm_mcu_hdr);
+
+        out_skb = dev_alloc_skb(msg_len);
+        if(IS_ERR_OR_NULL(out_skb)) {
+                bes2600_err(BES2600_DBG_TXLOOP, "%s, alloc mem fail.\n", __func__);
+                return;
+        }
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "%s len:%d\n",__func__, msg_len);
+        skb_put(out_skb, msg_len);
+        rx_wsm = (struct wsm_mcu_hdr *)out_skb->data;
+        msg_id = (tx_wsm->id & WSM_MSG_ID_MASK);
+        msg_id |= WSM_TX_SEQ(hw_priv->tx_loop.start_mcu_seq);
+        rx_wsm->hdr.id = __cpu_to_le16(msg_id);
+        rx_wsm->hdr.len = __cpu_to_le16(msg_len);
+        rx_wsm->handle_label = WSM_TO_MCU_CMD_INDICATION_LABEL;
+        rx_wsm->cmd_type = (tx_wsm->id & WSM_MSG_ID_MASK);
+
+        spin_lock(&hw_priv->tx_loop.tx_loop_lock);
+        hw_priv->tx_loop.start_mcu_seq = (hw_priv->tx_loop.start_mcu_seq + 1) & 7;
+        skb_queue_tail(&hw_priv->tx_loop.rx_queue, out_skb);
+        spin_unlock(&hw_priv->tx_loop.tx_loop_lock);
+
+        atomic_add_return(1, &hw_priv->bh_rx);
+}
+
+void bes2600_tx_loop_pipe_send(struct bes2600_common *hw_priv, u8 *buf, u32 len)
+{
+        int i = 0;
+        int tbl_size = ARRAY_SIZE(tx_loop_tbl);
+        struct wsm_hdr *tx_wsm = (struct wsm_hdr *)buf;
+        u16 cmd_id = tx_wsm->id & WSM_MSG_ID_MASK;
+
+        /* don't need to tx loop if wifi is unregistered */
+        if(hw_priv == NULL)
+                return;
+
+        bes2600_dbg(BES2600_DBG_TXLOOP, "tx loop pipe send cmd:0x%04x seq:%d\n",
+                                                cmd_id, WSM_MSG_SEQ_GET(tx_wsm->id));
+
+        /* select build confirm function based on command id */
+        for(i = 0; i < tbl_size; i++) {
+                if(cmd_id == tx_loop_tbl[i].cmd) {
+                        tx_loop_tbl[i].proc(hw_priv, buf, len);
+                        break;
+                }
+        }
+
+        /* build indication for special command */
+        if(cmd_id == 0x0007) {
+                bes2600_tx_loop_build_scan_compl_ind(hw_priv);
+        } else if(cmd_id == 0x0010) {
+                bes2600_tx_loop_build_pm_ind(hw_priv);
+        } else if(cmd_id == 0x0C25) {
+                bes2600_tx_loop_build_rfcmd_ind(hw_priv, buf, len);
+        }
+}
\ No newline at end of file
diff --git a/drivers/staging/bes2600/tx_loop.h b/drivers/staging/bes2600/tx_loop.h
new file mode 100644
index 000000000000..7294025d8eca
--- /dev/null
+++ b/drivers/staging/bes2600/tx_loop.h
@@ -0,0 +1,33 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __TX_LOOP_H__
+#define __TX_LOOP_H__
+
+#include "bes2600.h"
+
+struct bes2600_tx_loop
+{
+        bool enabled;
+        spinlock_t tx_loop_lock;
+        u8 start_lmac_seq;
+        u8 start_mcu_seq;
+        struct sk_buff_head rx_queue;
+        u8 *wsm_cmd_ptr;
+};
+
+void bes2600_tx_loop_init(struct bes2600_common *hw_priv);
+void bes2600_tx_loop_record_wsm_cmd(struct bes2600_common *hw_priv, u8 *wsm_cmd);
+void bes2600_tx_loop_clear_wsm_cmd(struct bes2600_common *hw_priv);
+struct sk_buff *bes2600_tx_loop_read(struct bes2600_common *hw_priv);
+void bes2600_tx_loop_set_enable(struct bes2600_common *hw_priv);
+void bes2600_tx_loop_pipe_send(struct bes2600_common *hw_priv, u8 *buf, u32 len);
+
+#endif
\ No newline at end of file
diff --git a/drivers/staging/bes2600/txrx.c b/drivers/staging/bes2600/txrx.c
new file mode 100644
index 000000000000..d9202bce7116
--- /dev/null
+++ b/drivers/staging/bes2600/txrx.c
@@ -0,0 +1,1995 @@
+/*
+ * Datapath implementation for BES2600 mac80211 drivers
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "bes2600.h"
+#include "wsm.h"
+#include "bh.h"
+#include "ap.h"
+#include "debug.h"
+#include "sta.h"
+#include "sbus.h"
+#include "txrx_opt.h"
+
+#define BES2600_INVALID_RATE_ID (0xFF)
+
+#ifdef CONFIG_BES2600_TESTMODE
+#include "bes_nl80211_testmode_msg.h"
+#endif /* CONFIG_BES2600_TESTMODE */
+static const struct ieee80211_rate *
+bes2600_get_tx_rate(const struct bes2600_common *hw_priv,
+		   const struct ieee80211_tx_rate *rate);
+
+/* ******************************************************************** */
+/* TX policy cache implementation					*/
+
+static void tx_policy_dump(struct tx_policy *policy)
+{
+	bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX policy] "
+		"%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X"
+		"%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X"
+		"%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n",
+		policy->raw[0] & 0x0F,  policy->raw[0] >> 4,
+		policy->raw[1] & 0x0F,  policy->raw[1] >> 4,
+		policy->raw[2] & 0x0F,  policy->raw[2] >> 4,
+		policy->raw[3] & 0x0F,  policy->raw[3] >> 4,
+		policy->raw[4] & 0x0F,  policy->raw[4] >> 4,
+		policy->raw[5] & 0x0F,  policy->raw[5] >> 4,
+		policy->raw[6] & 0x0F,  policy->raw[6] >> 4,
+		policy->raw[7] & 0x0F,  policy->raw[7] >> 4,
+		policy->raw[8] & 0x0F,  policy->raw[8] >> 4,
+		policy->raw[9] & 0x0F,  policy->raw[9] >> 4,
+		policy->raw[10] & 0x0F,  policy->raw[10] >> 4,
+		policy->raw[11] & 0x0F,  policy->raw[11] >> 4,
+		policy->defined);
+}
+
+static void bes2600_check_go_neg_conf_success(struct bes2600_common *hw_priv,
+						u8 *action)
+{
+	if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A &&
+		action[5] == 0x09 && action[6] == 0x02) {
+		if(action[17] == 0) {
+			hw_priv->is_go_thru_go_neg = true;
+		}
+		else {
+			hw_priv->is_go_thru_go_neg = false;
+		}
+	}
+}
+
+static void bes2600_check_prov_desc_req(struct bes2600_common *hw_priv,
+                                                u8 *action)
+{
+	if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A &&
+                action[5] == 0x09 && action[6] == 0x07) {
+                        hw_priv->is_go_thru_go_neg = false;
+        }
+}
+
+static void tx_policy_build(const struct bes2600_common *hw_priv,
+	/* [out] */ struct tx_policy *policy,
+	struct ieee80211_tx_rate *rates, size_t count)
+{
+	int i, j;
+	unsigned limit = hw_priv->short_frame_max_tx_count;
+	unsigned total = 0;
+	static int tx_rate_idx;
+
+	BUG_ON(rates[0].idx < 0);
+	memset(policy, 0, sizeof(*policy));
+
+	/* minstrel is buggy a little bit, so distille
+	 * incoming rates first. */
+	/* Sort rates in descending order. */
+	for (i = 0; i < count; i++) {
+		if (rates[i].idx < 0) {
+			count = i;
+			break;
+		}
+		for (j= 0; j < count; j++) {
+			if (rates[j].idx < 0) {
+				continue;
+			}
+
+			if (rates[j].idx < rates[j + 1].idx) {
+				struct ieee80211_tx_rate tmp = rates[j];
+				rates[j] = rates[j + 1];
+				rates[j + 1] = tmp;
+			}
+		}
+	}
+	/* enhance throughput, more tx retry rate */
+#ifdef BES2600_TX_RX_OPT
+	if (rates[0].flags & IEEE80211_TX_RC_MCS) {
+		static int min_rate_index = 1; //min rate index  is mcs1
+		static u8 last_rate_tx_cnt = 7;
+
+		count = IEEE80211_TX_MAX_RATES;
+		/* if idx < min rate index, set min rate index */
+		rates[0].count = 6;
+		if (rates[0].idx <= min_rate_index) {
+			rates[0].idx = min_rate_index;
+		}
+
+		for (i = 1; i < count; ++i) {
+			/* only one rate try 2 times*/
+			if (rates[i].count > 6)
+				rates[i].count = 6;
+			if (rates[i - 1].idx > min_rate_index) {
+				rates[i].idx = (rates[i - 1].idx - 1);
+				rates[i].count = 6;
+				rates[i].flags = rates[i - 1].flags;
+			} else if (rates[i - 1].idx <= min_rate_index) {
+				rates[i].idx = -1;
+				rates[i].count = 0;
+				rates[i].flags = 0;
+				break;
+			}
+		}
+		/* update the last  rate index  tx cnt */
+		rates[i - 1].count = last_rate_tx_cnt;
+		count = i;
+	}
+#endif
+	/* Eliminate duplicates. */
+	total = rates[0].count;
+	for (i = 0, j = 1; j < count; ++j) {
+		if (rates[j].idx == rates[i].idx) {
+			rates[i].count += rates[j].count;
+		} else if (rates[j].idx > rates[i].idx) {
+			break;
+		} else {
+			++i;
+			if (i != j)
+				rates[i] = rates[j];
+		}
+		total += rates[j].count;
+	}
+	count = i + 1;
+
+	/* Re-fill policy trying to keep every requested rate and with
+	 * respect to the global max tx retransmission count. */
+	if (limit < count)
+		limit = count;
+	if (total > limit) {
+		for (i = 0; i < count; ++i) {
+			int left = count - i - 1;
+			if (rates[i].count > limit - left)
+				rates[i].count = limit - left;
+			limit -= rates[i].count;
+		}
+	}
+
+	/* HACK!!! Device has problems (at least) switching from
+	 * 54Mbps CTS to 1Mbps. This switch takes enormous amount
+	 * of time (100-200 ms), leading to valuable throughput drop.
+	 * As a workaround, additional g-rates are injected to the
+	 * policy.
+	 */
+	if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) &&
+			rates[0].idx > 4 && rates[0].count > 2 &&
+			rates[1].idx < 2) {
+		/* ">> 1" is an equivalent of "/ 2", but faster */
+		int mid_rate = (rates[0].idx + 4) >> 1;
+
+		/* Decrease number of retries for the initial rate */
+		rates[0].count -= 2;
+
+		if (mid_rate != 4) {
+			/* Keep fallback rate at 1Mbps. */
+			rates[3] = rates[1];
+
+			/* Inject 1 transmission on lowest g-rate */
+			rates[2].idx = 4;
+			rates[2].count = 1;
+			rates[2].flags = rates[1].flags;
+
+			/* Inject 1 transmission on mid-rate */
+			rates[1].idx = mid_rate;
+			rates[1].count = 1;
+
+			/* Fallback to 1 Mbps is a really bad thing,
+			 * so let's try to increase probability of
+			 * successful transmission on the lowest g rate
+			 * even more */
+			if (rates[0].count >= 3) {
+				--rates[0].count;
+				++rates[2].count;
+			}
+
+			/* Adjust amount of rates defined */
+			count += 2;
+		} else {
+			/* Keep fallback rate at 1Mbps. */
+			rates[2] = rates[1];
+
+			/* Inject 2 transmissions on lowest g-rate */
+			rates[1].idx = 4;
+			rates[1].count = 2;
+
+			/* Adjust amount of rates defined */
+			count += 1;
+		}
+	}
+
+	policy->defined = bes2600_get_tx_rate(hw_priv, &rates[0])->hw_value + 1;
+#if 1 //add min basic rate in the tx path, driver should set wifi_Hook_cfg->new_run_flag |= RETRY_1M_RATE;
+	if (rates[0].flags & IEEE80211_TX_RC_MCS) {
+		int low_rate_idx = 0;  /* set default 11b 1M */
+		int low_rate_count = 1;
+		register unsigned rateid, off, shift, retries;
+		/* if the mcs < mcs3, more retry 2 times with low rate. */
+		if (rates[0].idx < 3) {
+			low_rate_count += 2;
+		}
+		/* if the tx rate is mcs0, more retry 2 times with low rate.
+		 * sample tx rate count is 1 in the minstrel_ht_set_rate.*/
+		if (rates[0].idx == 0 || (rates[1].idx == 0 && rates[0].count == 1)) {
+			low_rate_count += 2;
+			/* smaple and mcs0 tx rate count set 2. */
+			for (i = 0; i < count; i++) {
+				rates[0].count = (i + 1);
+			}
+		}
+		if (hw_priv->channel != NULL && hw_priv->channel->hw_value > 14)
+			low_rate_idx = 6;  /* set default 11a 6M */
+
+		rateid = low_rate_idx;
+		off = rateid >> 3;      /* eq. rateid / 8 */
+		shift = (rateid & 0x07) << 2;   /* eq. (rateid % 8) * 4 */
+		retries = low_rate_count;
+		policy->tbl[off] |= __cpu_to_le32(retries << shift);
+		policy->retry_count += retries;
+	}
+#endif
+
+	for (i = 0; i < count; ++i) {
+		register unsigned rateid, off, shift, retries;
+
+		rateid = bes2600_get_tx_rate(hw_priv, &rates[i])->hw_value;
+		off = rateid >> 3;		/* eq. rateid / 8 */
+		shift = (rateid & 0x07) << 2;	/* eq. (rateid % 8) * 4 */
+
+		retries = rates[i].count;
+		if (unlikely(retries > 0x0F))
+			rates[i].count = retries = 0x0F;
+		policy->tbl[off] |= __cpu_to_le32(retries << shift);
+		policy->retry_count += retries;
+	}
+
+
+	if (rates[0].idx !=  tx_rate_idx) {
+		tx_rate_idx = rates[0].idx;
+		bes2600_dbg(BES2600_DBG_TXRX_OPT, "[TX policy] Policy (%lu): " \
+			    "%d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n",
+			    (long unsigned)count,
+			    rates[0].idx, rates[0].count,
+			    rates[1].idx, rates[1].count,
+			    rates[2].idx, rates[2].count,
+			    rates[3].idx, rates[3].count,
+			    rates[4].idx, rates[4].count);
+	}
+}
+
+static inline bool tx_policy_is_equal(const struct tx_policy *wanted,
+					const struct tx_policy *cached)
+{
+	size_t count = wanted->defined >> 1;
+	if (wanted->defined > cached->defined)
+		return false;
+	if (count) {
+		if (memcmp(wanted->raw, cached->raw, count))
+			return false;
+	}
+	if (wanted->defined & 1) {
+		if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F))
+			return false;
+	}
+	return true;
+}
+
+static int tx_policy_find(struct tx_policy_cache *cache,
+				const struct tx_policy *wanted)
+{
+	/* O(n) complexity. Not so good, but there's only 8 entries in
+	 * the cache.
+	 * Also lru helps to reduce search time. */
+	struct tx_policy_cache_entry *it;
+	/* Search for policy in "used" list */
+	list_for_each_entry(it, &cache->used, link) {
+		if (tx_policy_is_equal(wanted, &it->policy))
+			return it - cache->cache;
+	}
+	/* Then - in "free list" */
+	list_for_each_entry(it, &cache->free, link) {
+		if (tx_policy_is_equal(wanted, &it->policy))
+			return it - cache->cache;
+	}
+	return -1;
+}
+
+static inline void tx_policy_use(struct tx_policy_cache *cache,
+				 struct tx_policy_cache_entry *entry)
+{
+	++entry->policy.usage_count;
+	list_move(&entry->link, &cache->used);
+}
+
+static inline int tx_policy_release(struct tx_policy_cache *cache,
+				    struct tx_policy_cache_entry *entry)
+{
+	int ret = --entry->policy.usage_count;
+	if (!ret)
+		list_move(&entry->link, &cache->free);
+	return ret;
+}
+
+/* ******************************************************************** */
+/* External TX policy cache API						*/
+
+void tx_policy_init(struct bes2600_common *hw_priv)
+{
+	struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
+	int i;
+
+	bes2600_dbg(BES2600_DBG_TXRX_OPT, "tx_policy_init\n\r");
+
+	memset(cache, 0, sizeof(*cache));
+
+	spin_lock_init(&cache->lock);
+	INIT_LIST_HEAD(&cache->used);
+	INIT_LIST_HEAD(&cache->free);
+
+	for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i)
+		list_add(&cache->cache[i].link, &cache->free);
+}
+
+void tx_policy_deinit(struct bes2600_common *hw_priv)
+{
+	bes2600_dbg(BES2600_DBG_TXRX_OPT, "tx_policy_deinit\n\r");
+}
+
+static int tx_policy_get(struct bes2600_common *hw_priv,
+		  struct ieee80211_tx_rate *rates,
+		  size_t count, bool *renew)
+{
+	int idx;
+	struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
+	struct tx_policy wanted;
+
+	tx_policy_build(hw_priv, &wanted, rates, count);
+
+	spin_lock_bh(&cache->lock);
+	if (WARN_ON_ONCE(list_empty(&cache->free))) {
+		spin_unlock_bh(&cache->lock);
+		return BES2600_INVALID_RATE_ID;
+	}
+	idx = tx_policy_find(cache, &wanted);
+	if (idx >= 0) {
+		bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX policy] Used TX policy: %d\n",
+					idx);
+		*renew = false;
+	} else {
+		struct tx_policy_cache_entry *entry;
+		*renew = true;
+		/* If policy is not found create a new one
+		 * using the oldest entry in "free" list */
+		entry = list_entry(cache->free.prev,
+			struct tx_policy_cache_entry, link);
+		entry->policy = wanted;
+		idx = entry - cache->cache;
+		bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX policy] New TX policy: %d\n",
+					idx);
+		tx_policy_dump(&entry->policy);
+	}
+	tx_policy_use(cache, &cache->cache[idx]);
+	if (unlikely(list_empty(&cache->free))) {
+		/* Lock TX queues. */
+		bes2600_tx_queues_lock(hw_priv);
+	}
+	spin_unlock_bh(&cache->lock);
+	return idx;
+}
+
+static void tx_policy_put(struct bes2600_common *hw_priv, int idx)
+{
+	int usage, locked;
+	struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
+
+	spin_lock_bh(&cache->lock);
+	locked = list_empty(&cache->free);
+	usage = tx_policy_release(cache, &cache->cache[idx]);
+	if (unlikely(locked) && !usage) {
+		/* Unlock TX queues. */
+		bes2600_tx_queues_unlock(hw_priv);
+	}
+	spin_unlock_bh(&cache->lock);
+}
+
+/*
+bool tx_policy_cache_full(struct bes2600_common *hw_priv)
+{
+	bool ret;
+	struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
+	spin_lock_bh(&cache->lock);
+	ret = list_empty(&cache->free);
+	spin_unlock_bh(&cache->lock);
+	return ret;
+}
+*/
+
+static int tx_policy_upload(struct bes2600_common *hw_priv)
+{
+	struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
+	int i;
+	struct wsm_set_tx_rate_retry_policy arg = {
+		.hdr = {
+			.numTxRatePolicies = 0,
+		}
+	};
+	int if_id = 0;
+	spin_lock_bh(&cache->lock);
+
+	/* Upload only modified entries. */
+	for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) {
+		struct tx_policy *src = &cache->cache[i].policy;
+		if (src->retry_count && !src->uploaded) {
+			struct wsm_set_tx_rate_retry_policy_policy *dst =
+				&arg.tbl[arg.hdr.numTxRatePolicies];
+			dst->policyIndex = i;
+			dst->shortRetryCount =
+				hw_priv->short_frame_max_tx_count;
+			dst->longRetryCount = hw_priv->long_frame_max_tx_count;
+
+			/* BIT(2) - Terminate retries when Tx rate retry policy
+			 *          finishes.
+			 * BIT(3) - Count initial frame transmission as part of
+			 *          rate retry counting but not as a retry
+			 *          attempt */
+			dst->policyFlags = BIT(2) | BIT(3);
+
+			memcpy(dst->rateCountIndices, src->tbl,
+					sizeof(dst->rateCountIndices));
+			src->uploaded = 1;
+			++arg.hdr.numTxRatePolicies;
+		}
+	}
+	spin_unlock_bh(&cache->lock);
+	//bes2600_debug_tx_cache_miss(hw_priv);
+	bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX policy] Upload %d policies\n",
+				arg.hdr.numTxRatePolicies);
+	/*TODO: COMBO*/
+	return wsm_set_tx_rate_retry_policy(hw_priv, &arg, if_id);
+}
+
+void tx_policy_upload_work(struct work_struct *work)
+{
+	struct bes2600_common *hw_priv =
+		container_of(work, struct bes2600_common, tx_policy_upload_work);
+
+	bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX] TX policy upload.\n");
+	WARN_ON(tx_policy_upload(hw_priv));
+
+	wsm_unlock_tx(hw_priv);
+	bes2600_tx_queues_unlock(hw_priv);
+}
+
+/* ******************************************************************** */
+/* bes2600 TX implementation						*/
+
+struct bes2600_txinfo {
+	struct sk_buff *skb;
+	unsigned queue;
+	struct ieee80211_tx_info *tx_info;
+	const struct ieee80211_rate *rate;
+	struct ieee80211_hdr *hdr;
+	size_t hdrlen;
+	const u8 *da;
+	struct bes2600_sta_priv *sta_priv;
+	struct ieee80211_sta *sta;
+	struct bes2600_txpriv txpriv;
+};
+
+u32 bes2600_rate_mask_to_wsm(struct bes2600_common *hw_priv, u32 rates)
+{
+	u32 ret = 0;
+	int i;
+	struct ieee80211_rate * bitrates =
+		hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates;
+	for (i = 0; i < 32; ++i) {
+		if (rates & BIT(i))
+			ret |= BIT(bitrates[i].hw_value);
+	}
+	return ret;
+}
+
+static const struct ieee80211_rate *
+bes2600_get_tx_rate(const struct bes2600_common *hw_priv,
+		   const struct ieee80211_tx_rate *rate)
+{
+	if (rate->idx < 0)
+		return NULL;
+	if (rate->flags & IEEE80211_TX_RC_MCS)
+		return &hw_priv->mcs_rates[rate->idx];
+	return &hw_priv->hw->wiphy->bands[hw_priv->channel->band]->
+		bitrates[rate->idx];
+}
+
+static int
+bes2600_tx_h_calc_link_ids(struct bes2600_vif *priv,
+			  struct bes2600_txinfo *t)
+{
+#ifndef P2P_MULTIVIF
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	if ((t->tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
+			(hw_priv->roc_if_id == priv->if_id))
+		t->txpriv.offchannel_if_id = 2;
+	else
+		t->txpriv.offchannel_if_id = 0;
+#endif
+
+	if (likely(t->sta && t->sta_priv->link_id))
+		t->txpriv.raw_link_id =
+				t->txpriv.link_id =
+				t->sta_priv->link_id;
+	else if (priv->mode != NL80211_IFTYPE_AP)
+		t->txpriv.raw_link_id =
+				t->txpriv.link_id = 0;
+	else if (is_multicast_ether_addr(t->da)) {
+		if (priv->enable_beacon) {
+			t->txpriv.raw_link_id = 0;
+			t->txpriv.link_id = priv->link_id_after_dtim;
+		} else {
+			t->txpriv.raw_link_id = 0;
+			t->txpriv.link_id = 0;
+		}
+	} else {
+		t->txpriv.link_id =
+			bes2600_find_link_id(priv, t->da);
+		/* Do not assign valid link id for deauth/disassoc frame being
+		transmitted to an unassociated STA */
+		if (!(t->txpriv.link_id) &&
+			(ieee80211_is_deauth(t->hdr->frame_control) ||
+			ieee80211_is_disassoc(t->hdr->frame_control))) {
+					t->txpriv.link_id = 0;
+		} else {
+			if (!t->txpriv.link_id)
+				t->txpriv.link_id = bes2600_alloc_link_id(priv, t->da);
+			if (!t->txpriv.link_id) {
+				wiphy_err(priv->hw->wiphy,
+					"%s: No more link IDs available.\n",
+					__func__);
+				return -ENOENT;
+			}
+		}
+		t->txpriv.raw_link_id = t->txpriv.link_id;
+	}
+	if (t->txpriv.raw_link_id)
+		priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp =
+				jiffies;
+
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	if (t->tx_info->sta &&
+			(t->tx_info->sta->uapsd_queues & BIT(t->queue)))
+		t->txpriv.link_id = priv->link_id_uapsd;
+#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+	return 0;
+}
+
+static void
+bes2600_tx_h_pm(struct bes2600_vif *priv,
+	       struct bes2600_txinfo *t)
+{
+	if (unlikely(ieee80211_is_auth(t->hdr->frame_control))) {
+		u32 mask = ~BIT(t->txpriv.raw_link_id);
+		spin_lock_bh(&priv->ps_state_lock);
+		priv->sta_asleep_mask &= mask;
+		priv->pspoll_mask &= mask;
+		spin_unlock_bh(&priv->ps_state_lock);
+	}
+}
+
+static void
+bes2600_tx_h_calc_tid(struct bes2600_vif *priv,
+		     struct bes2600_txinfo *t)
+{
+	if (ieee80211_is_data_qos(t->hdr->frame_control)) {
+		u8 *qos = ieee80211_get_qos_ctl(t->hdr);
+		t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK;
+	} else if (ieee80211_is_data(t->hdr->frame_control)) {
+		t->txpriv.tid = 0;
+	}
+}
+
+#ifdef CONFIG_BES2600_WAPI_SUPPORT
+static void bes2600_tx_wapi_shrink_iv_space(struct bes2600_vif *priv,
+		     struct bes2600_txinfo *t)
+{
+	int hdrlen;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) t->skb->data;
+	u8 *pos, iv_len;
+
+	if (!t->tx_info->control.hw_key ||
+		!ieee80211_has_protected(t->hdr->frame_control))
+		return;
+
+	iv_len = t->tx_info->control.hw_key->iv_len;
+	hdrlen = ieee80211_hdrlen(t->hdr->frame_control);
+	iv_len = t->tx_info->control.hw_key->iv_len;
+
+	if(ieee80211_is_mgmt(t->hdr->frame_control) &&
+		t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_SMS4 &&
+		(t->tx_info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
+		pos = t->skb->data;
+		skb_put(t->skb, iv_len);
+		memmove(pos + iv_len, pos, hdrlen);
+		skb_pull(t->skb, iv_len);
+		hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+		t->tx_info->control.hw_key = NULL;
+		t->hdr = (struct ieee80211_hdr *)t->skb->data;
+	}
+}
+#endif
+
+#if 0
+/* IV/ICV injection. */
+/* TODO: Quite unoptimal. It's better co modify mac80211
+ * to reserve space for IV */
+static int
+bes2600_tx_h_crypt(struct bes2600_vif *priv,
+		  struct bes2600_txinfo *t)
+{
+	size_t iv_len;
+	size_t icv_len;
+	u8 *icv;
+	u8 *newhdr;
+
+	if (!t->tx_info->control.hw_key ||
+	    !(t->hdr->frame_control &
+	     __cpu_to_le32(IEEE80211_FCTL_PROTECTED)))
+		return 0;
+
+	iv_len = t->tx_info->control.hw_key->iv_len;
+	icv_len = t->tx_info->control.hw_key->icv_len;
+
+	if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+		icv_len += 8; /* MIC */
+
+	if ((skb_headroom(t->skb) + skb_tailroom(t->skb) <
+			 iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) ||
+			(skb_headroom(t->skb) <
+			 iv_len + WSM_TX_EXTRA_HEADROOM)) {
+		wiphy_err(priv->hw->wiphy,
+			"Bug: no space allocated for crypto headers.\n"
+			"headroom: %d, tailroom: %d, "
+			"req_headroom: %d, req_tailroom: %d\n"
+			"Please fix it in bes2600_get_skb().\n",
+			skb_headroom(t->skb), skb_tailroom(t->skb),
+			iv_len + WSM_TX_EXTRA_HEADROOM, icv_len);
+		return -ENOMEM;
+	} else if (skb_tailroom(t->skb) < icv_len) {
+		size_t offset = icv_len - skb_tailroom(t->skb);
+		u8 *p;
+		wiphy_warn(priv->hw->wiphy,
+			"Slowpath: tailroom is not big enough. "
+			"Req: %d, got: %d.\n",
+			icv_len, skb_tailroom(t->skb));
+
+		p = skb_push(t->skb, offset);
+		memmove(p, &p[offset], t->skb->len - offset);
+		skb_trim(t->skb, t->skb->len - offset);
+	}
+
+	newhdr = skb_push(t->skb, iv_len);
+	memmove(newhdr, newhdr + iv_len, t->hdrlen);
+	t->hdr = (struct ieee80211_hdr *) newhdr;
+	t->hdrlen += iv_len;
+	icv = skb_put(t->skb, icv_len);
+
+	return 0;
+}
+#else
+static int
+bes2600_tx_h_crypt(struct bes2600_vif *priv,
+		  struct bes2600_txinfo *t)
+{
+	if (!t->tx_info->control.hw_key ||
+		!ieee80211_has_protected(t->hdr->frame_control) ||
+		(ieee80211_is_mgmt(t->hdr->frame_control) &&
+		t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_SMS4))
+		return 0;
+
+	t->hdrlen += t->tx_info->control.hw_key->iv_len;
+	skb_put(t->skb, t->tx_info->control.hw_key->icv_len);
+
+	if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+		skb_put(t->skb, 8); /* MIC space */
+
+	return 0;
+}
+#endif
+
+static int
+bes2600_tx_h_align(struct bes2600_vif *priv,
+		  struct bes2600_txinfo *t,
+		  u8 *flags)
+{
+	size_t offset = (size_t)t->skb->data & 3;
+
+	if (!offset)
+		return 0;
+
+	if (offset & 1) {
+		wiphy_err(priv->hw->wiphy,
+			"Bug: attempt to transmit a frame "
+			"with wrong alignment: %ld\n",
+			(long)offset);
+		return -EINVAL;
+	}
+
+	if (skb_headroom(t->skb) < offset) {
+		wiphy_err(priv->hw->wiphy,
+			"Bug: no space allocated "
+			"for DMA alignment.\n"
+			"headroom: %d\n",
+			skb_headroom(t->skb));
+		return -ENOMEM;
+	}
+	skb_push(t->skb, offset);
+	t->hdrlen += offset;
+	t->txpriv.offset += offset;
+	*flags |= WSM_TX_2BYTES_SHIFT;
+	bes2600_debug_tx_align(priv);
+	return 0;
+}
+
+static int
+bes2600_tx_h_action(struct bes2600_vif *priv,
+		   struct bes2600_txinfo *t)
+{
+	struct ieee80211_mgmt *mgmt =
+		(struct ieee80211_mgmt *)t->hdr;
+
+	if (ieee80211_is_action(t->hdr->frame_control) &&
+			mgmt->u.action.category == WLAN_CATEGORY_BACK)
+		return 1;
+	else
+		return 0;
+}
+
+/* Add WSM header */
+static struct wsm_tx *
+bes2600_tx_h_wsm(struct bes2600_vif *priv,
+		struct bes2600_txinfo *t)
+{
+	struct wsm_tx *wsm;
+
+	if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) {
+		wiphy_err(priv->hw->wiphy,
+			"Bug: no space allocated "
+			"for WSM header.\n"
+			"headroom: %d\n",
+			skb_headroom(t->skb));
+		return NULL;
+	}
+
+	wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx));
+	t->txpriv.offset += sizeof(struct wsm_tx);
+	memset(wsm, 0, sizeof(*wsm));
+	wsm->hdr.len = __cpu_to_le16(t->skb->len);
+	wsm->hdr.id = __cpu_to_le16(0x0004);
+	wsm->queueId =
+		(t->txpriv.raw_link_id << 2) | wsm_queue_id_to_wsm(t->queue);
+	return wsm;
+}
+
+/* BT Coex specific handling */
+static void
+bes2600_tx_h_bt(struct bes2600_vif *priv,
+	       struct bes2600_txinfo *t,
+	       struct wsm_tx *wsm)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	u8 priority = 0;
+
+	if (!hw_priv->is_BT_Present)
+		return;
+
+	if (unlikely(ieee80211_is_nullfunc(t->hdr->frame_control)))
+		priority = WSM_EPTA_PRIORITY_MGT;
+	else if (ieee80211_is_data(t->hdr->frame_control)) {
+		/* Skip LLC SNAP header (+6) */
+		u8 *payload = &t->skb->data[t->hdrlen];
+		u16 *ethertype = (u16 *) &payload[6];
+		if (unlikely(*ethertype == __be16_to_cpu(ETH_P_PAE)))
+			priority = WSM_EPTA_PRIORITY_EAPOL;
+	} else if (unlikely(ieee80211_is_assoc_req(t->hdr->frame_control) ||
+		ieee80211_is_reassoc_req(t->hdr->frame_control))) {
+		struct ieee80211_mgmt *mgt_frame =
+				(struct ieee80211_mgmt *)t->hdr;
+
+		if (mgt_frame->u.assoc_req.listen_interval <
+						priv->listen_interval) {
+			bes2600_dbg(BES2600_DBG_TX_POLICY,
+				"Modified Listen Interval to %d from %d\n",
+				priv->listen_interval,
+				mgt_frame->u.assoc_req.listen_interval);
+			/* Replace listen interval derieved from
+			 * the one read from SDD */
+			mgt_frame->u.assoc_req.listen_interval =
+				priv->listen_interval;
+		}
+	}
+
+	if (likely(!priority)) {
+		if (ieee80211_is_action(t->hdr->frame_control))
+			priority = WSM_EPTA_PRIORITY_ACTION;
+		else if (ieee80211_is_mgmt(t->hdr->frame_control))
+			priority = WSM_EPTA_PRIORITY_MGT;
+		else if (wsm->queueId == WSM_QUEUE_VOICE)
+			priority = WSM_EPTA_PRIORITY_VOICE;
+		else if (wsm->queueId == WSM_QUEUE_VIDEO)
+			priority = WSM_EPTA_PRIORITY_VIDEO;
+		else
+			priority = WSM_EPTA_PRIORITY_DATA;
+	}
+
+	bes2600_dbg(BES2600_DBG_TXRX, "[TX] EPTA priority %d.\n",
+		priority);
+
+	wsm->flags |= priority << 1;
+}
+
+static int
+bes2600_tx_h_rate_policy(struct bes2600_common *hw_priv,
+			struct bes2600_txinfo *t,
+			struct wsm_tx *wsm)
+{
+	bool tx_policy_renew = false;
+	struct bes2600_vif *priv =
+				cw12xx_get_vif_from_ieee80211(t->tx_info->control.vif);
+
+	t->txpriv.rate_id = tx_policy_get(hw_priv,
+		t->tx_info->control.rates, IEEE80211_TX_MAX_RATES,
+		&tx_policy_renew);
+	if (t->txpriv.rate_id == BES2600_INVALID_RATE_ID)
+		return -EFAULT;
+
+	wsm->flags |= t->txpriv.rate_id << 4;
+
+	t->rate = bes2600_get_tx_rate(hw_priv,
+		&t->tx_info->control.rates[0]),
+	wsm->maxTxRate = t->rate->hw_value;
+	priv->hw_value = wsm->maxTxRate;
+	if (t->rate->flags & IEEE80211_TX_RC_MCS) {
+		if (priv->association_mode.greenfieldMode)
+			wsm->htTxParameters |=
+				__cpu_to_le32(WSM_HT_TX_GREENFIELD);
+		else
+			wsm->htTxParameters |=
+				__cpu_to_le32(WSM_HT_TX_MIXED);
+	}
+
+	if (tx_policy_renew) {
+		bes2600_dbg(BES2600_DBG_TX_POLICY, "[TX] TX policy renew.\n");
+		/* It's not so optimal to stop TX queues every now and then.
+		 * Maybe it's better to reimplement task scheduling with
+		 * a counter. */
+		/* bes2600_tx_queues_lock(priv); */
+		/* Definetly better. TODO. */
+		wsm_lock_tx_async(hw_priv);
+		bes2600_tx_queues_lock(hw_priv);
+		if (queue_work(hw_priv->workqueue,
+				&hw_priv->tx_policy_upload_work) <= 0) {
+			bes2600_tx_queues_unlock(hw_priv);
+			wsm_unlock_tx(hw_priv);
+		}
+	}
+	return 0;
+}
+
+static bool
+bes2600_tx_h_pm_state(struct bes2600_vif *priv,
+		     struct bes2600_txinfo *t)
+{
+	int was_buffered = 1;
+
+	if (t->txpriv.link_id == priv->link_id_after_dtim &&
+			!priv->buffered_multicasts) {
+		priv->buffered_multicasts = true;
+		if (priv->sta_asleep_mask)
+			queue_work(priv->hw_priv->workqueue,
+				&priv->multicast_start_work);
+	}
+
+	if (t->txpriv.raw_link_id && t->txpriv.tid < BES2600_MAX_TID)
+		was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1]
+				.buffered[t->txpriv.tid]++;
+
+	return !was_buffered;
+}
+
+static void
+bes2600_tx_h_ba_stat(struct bes2600_vif *priv,
+		    struct bes2600_txinfo *t)
+{
+	struct bes2600_common *hw_priv = priv->hw_priv;
+
+	if (priv->join_status != BES2600_JOIN_STATUS_STA)
+		return;
+	if (!bes2600_is_ht(&hw_priv->ht_info))
+		return;
+	if (!priv->setbssparams_done)
+		return;
+	if (!ieee80211_is_data(t->hdr->frame_control))
+		return;
+
+	spin_lock_bh(&hw_priv->ba_lock);
+	hw_priv->ba_acc += t->skb->len - t->hdrlen;
+	if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
+		mod_timer(&hw_priv->ba_timer,
+			jiffies + BES2600_BLOCK_ACK_INTERVAL);
+	}
+	hw_priv->ba_cnt++;
+	spin_unlock_bh(&hw_priv->ba_lock);
+}
+
+static int
+bes2600_tx_h_skb_pad(struct bes2600_common *priv,
+		    struct wsm_tx *wsm,
+		    struct sk_buff *skb)
+{
+	size_t len = __le16_to_cpu(wsm->hdr.len);
+	size_t padded_len = priv->sbus_ops->align_size(priv->sbus_priv, len);
+
+	if (WARN_ON(skb_padto(skb, padded_len) != 0)) {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+static uint16_t find_idx_by_matched_paras(struct bes2600_common *hw_priv, uint8_t proto, uint16_t src_port,
+                                         uint16_t dst_port, uint32_t src_ip, uint32_t dst_ip)
+{
+	uint16_t idx;
+	struct ip_alive_cfg *iac = hw_priv->iac;
+
+	for (idx = 0; idx < NUM_IP_FRAMES; idx++) {
+		if (iac[idx].bd.idx_used &&
+		    (proto == iac[idx].bd.proto) &&
+		    (src_port == iac[idx].bd.src_port) &&
+		    (dst_port == iac[idx].bd.dest_port)&&
+		    (src_ip == iac[idx].bd.src_ip) &&
+		    (dst_ip == iac[idx].bd.dest_ip)) {
+			break;
+		}
+	}
+
+	return idx;
+}
+
+static int extract_ip_headers_info(struct bes2600_common *hw_priv, struct bes2600_vif *priv, uint8_t *iphdr)
+{
+	uint16_t _v_hl, _proto, src_port, dst_port, idx, tcp_bd_len;
+	uint8_t offset;
+	uint32_t src_ip, dst_ip;
+	struct ip_alive_cfg *iac = hw_priv->iac;
+
+	bes2600_dbg_dump(BES2600_DBG_TEST_MODE, "iphdr:", iphdr, 64);
+
+	/* Frame Control Flags, bit6: 1 for protected frame and 0 for non-protected frame; */
+	((iphdr[1] >> 6) & 0x1) ? (offset = 40) : (offset = 32);
+
+	_v_hl = iphdr[offset + 2];
+	_proto = iphdr[offset + 11];
+
+	/*
+	 * _v_hl == 0x45 for ip packet;
+	 * _proto == 6 for tcp protocol and 17 for udp protocol;
+	 */
+	if (_v_hl == 0x45) {
+		if (_proto == TCP_PROTO) {
+			/* obtain destination's listening port. */
+			src_port = (iphdr[offset+22]<<8) + iphdr[offset+23];
+			dst_port = (iphdr[offset+24]<<8) + iphdr[offset+25];
+			src_ip = (iphdr[offset+14]<<24) + (iphdr[offset+15]<<16) + (iphdr[offset+16]<<8) + iphdr[offset+17];
+			dst_ip = (iphdr[offset+18]<<24) + (iphdr[offset+19]<<16) + (iphdr[offset+20]<<8) + iphdr[offset+21];
+			idx = find_idx_by_matched_paras(hw_priv, 1, src_port, dst_port, src_ip, dst_ip);
+			/* if port matches */
+			if (idx < NUM_IP_FRAMES) {
+				iac[idx].iphd._len = (iphdr[offset+4]<<8) + iphdr[offset+5];
+				iac[idx].iphd._proto = _proto;
+				iac[idx].tcphd._hdrlen_rsvd_flags = (iphdr[offset+34]<<8) + iphdr[offset+35];
+				iac[idx].tcphd.seqno = (iphdr[offset+26]<<24) + (iphdr[offset+27]<<16) + (iphdr[offset+28]<<8) + iphdr[offset+29];
+				iac[idx].tcphd.ackno = (iphdr[offset+30]<<24) + (iphdr[offset+31]<<16) + (iphdr[offset+32]<<8) + iphdr[offset+33];
+				tcp_bd_len = iac[idx].iphd._len - sizeof(struct ip_header) - sizeof(struct tcp_header);
+				iac[idx].bd.next_seqno = iac[idx].tcphd.seqno + tcp_bd_len;
+				memcpy(iac[idx].bd.dest_mac, ((struct ieee80211_hdr *)iphdr)->addr3, 6);
+
+				bes2600_dbg(BES2600_DBG_TEST_MODE, "src-ip:%x dest-ip:%x src-port:%d dest-port:%d\n",
+				                   iac[idx].iphd.src,
+				                   iac[idx].iphd.dest,
+				                   iac[idx].tcphd.src,
+				                   iac[idx].tcphd.dest);
+				bes2600_dbg(BES2600_DBG_TEST_MODE, "seqno:%u ackno:%u\n", iac[idx].tcphd.seqno, iac[idx].tcphd.ackno);
+			}
+		}
+		else if (_proto == UDP_PROTO) {
+			/* obtain destination's listening port. */
+			src_port = (iphdr[offset+22]<<8) + iphdr[offset+23];
+			dst_port = (iphdr[offset+24]<<8) + iphdr[offset+25];
+			src_ip = (iphdr[offset+14]<<24) + (iphdr[offset+15]<<16) + (iphdr[offset+16]<<8) + iphdr[offset+17];
+			dst_ip = (iphdr[offset+18]<<24) + (iphdr[offset+19]<<16) + (iphdr[offset+20]<<8) + iphdr[offset+21];
+			idx = find_idx_by_matched_paras(hw_priv, 0, src_port, dst_port, src_ip, dst_ip);
+			/* if port matches */
+			if (idx < NUM_IP_FRAMES) {
+				iac[idx].iphd._len = (iphdr[offset+4]<<8) + iphdr[offset+5];
+				iac[idx].iphd._proto = _proto;
+				memcpy(iac[idx].bd.dest_mac, ((struct ieee80211_hdr *)iphdr)->addr3, 6);
+			}
+		}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_BES2600_KEEP_ALIVE */
+
+/* ******************************************************************** */
+
+void bes2600_tx(struct ieee80211_hw *dev,
+			struct ieee80211_tx_control *control,
+			struct sk_buff *skb)
+{
+	struct bes2600_common *hw_priv = dev->priv;
+	struct bes2600_txinfo t = {
+		.skb = skb,
+		.queue = skb_get_queue_mapping(skb),
+		.tx_info = IEEE80211_SKB_CB(skb),
+		.hdr = (struct ieee80211_hdr *)skb->data,
+		.txpriv.tid = BES2600_MAX_TID,
+		.txpriv.rate_id = BES2600_INVALID_RATE_ID,
+#ifdef P2P_MULTIVIF
+		.txpriv.raw_if_id = 0,
+#endif
+	};
+	struct ieee80211_sta *sta;
+	struct wsm_tx *wsm;
+	bool tid_update = 0;
+	u8 flags = 0;
+	int ret;
+	struct bes2600_vif *priv;
+	struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
+        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+
+	if (!skb->data)
+		BUG_ON(1);
+
+	if (!(t.tx_info->control.vif)) {
+	        goto drop;
+	}
+	priv = cw12xx_get_vif_from_ieee80211(t.tx_info->control.vif);
+	if (!priv)
+		goto drop;
+
+	if (atomic_read(&priv->enabled) == 0)
+		goto drop;
+
+	/* wake up device if device is in low power mode */
+	bes2600_pwr_set_busy_event_with_timeout_async(
+		hw_priv, BES_PWR_LOCK_ON_TX, BES_PWR_EVENT_TX_TIMEOUT);
+
+#ifdef CONFIG_BES2600_KEEP_ALIVE
+	/* extract ip & tcp header's information; */
+	extract_ip_headers_info(hw_priv, priv, skb->data);
+#endif
+
+#ifdef CONFIG_BES2600_TESTMODE
+	spin_lock_bh(&hw_priv->tsm_lock);
+	if (hw_priv->start_stop_tsm.start) {
+		if (hw_priv->tsm_info.ac == t.queue)
+			hw_priv->tsm_stats.txed_msdu_count++;
+	}
+	spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_BES2600_TESTMODE*/
+
+	if ((ieee80211_is_action(frame->frame_control))
+			&& (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+		u8 *action = (u8*)&mgmt->u.action.category;
+		bes2600_check_go_neg_conf_success(hw_priv, action);
+		bes2600_check_prov_desc_req(hw_priv, action);
+	}
+
+	t.txpriv.if_id = priv->if_id;
+	t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control);
+	t.da = ieee80211_get_DA(t.hdr);
+	if (control) {
+		t.sta = control->sta;
+		t.sta_priv = (struct bes2600_sta_priv *)&t.sta->drv_priv;
+	}
+
+	if (WARN_ON(t.queue >= 4))
+		goto drop;
+
+	/*
+		should not drop packets here, it may cause tx rate decreasing
+		tx flow control will be handled in bes2600_queue_put
+	*/
+#if 0
+	spin_lock_bh(&hw_priv->tx_queue[t.queue].lock);
+
+	if ((priv->if_id == 0) &&
+		(hw_priv->tx_queue[t.queue].num_queued_vif[0] >=
+			hw_priv->vif0_throttle)) {
+		spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock);
+		goto drop;
+	} else if ((priv->if_id == 1) &&
+		(hw_priv->tx_queue[t.queue].num_queued_vif[1] >=
+			hw_priv->vif1_throttle)) {
+		spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock);
+		goto drop;
+	}
+
+	spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock);
+#endif
+
+	ret = bes2600_tx_h_calc_link_ids(priv, &t);
+	if (ret)
+		goto drop;
+
+	bes2600_dbg(BES2600_DBG_TXRX, "[TX] TX %d bytes (if_id: %d,"
+			" queue: %d, link_id: %d (%d)).\n",
+			skb->len, priv->if_id, t.queue, t.txpriv.link_id,
+			t.txpriv.raw_link_id);
+
+	bes2600_tx_h_pm(priv, &t);
+	bes2600_tx_h_calc_tid(priv, &t);
+#ifdef CONFIG_BES2600_WAPI_SUPPORT
+	bes2600_tx_wapi_shrink_iv_space(priv, &t);
+#endif
+	ret = bes2600_tx_h_crypt(priv, &t);
+	if (ret)
+		goto drop;
+	ret = bes2600_tx_h_align(priv, &t, &flags);
+	if (ret)
+		goto drop;
+	ret = bes2600_tx_h_action(priv, &t);
+	if (ret)
+		goto drop;
+	wsm = bes2600_tx_h_wsm(priv, &t);
+	if (!wsm) {
+		ret = -ENOMEM;
+		goto drop;
+	}
+#ifdef CONFIG_BES2600_TESTMODE
+	flags |= WSM_TX_FLAG_EXPIRY_TIME;
+#endif /*CONFIG_BES2600_TESTMODE*/
+	wsm->flags |= flags;
+	bes2600_tx_h_bt(priv, &t, wsm);
+	ret = bes2600_tx_h_rate_policy(hw_priv, &t, wsm);
+	if (ret)
+		goto drop;
+
+	ret = bes2600_tx_h_skb_pad(hw_priv, wsm, skb);
+	if (ret)
+		goto drop;
+	rcu_read_lock();
+	sta = rcu_dereference(t.sta);
+
+	bes2600_tx_h_ba_stat(priv, &t);
+
+	spin_lock_bh(&priv->ps_state_lock);
+	{
+		tid_update = bes2600_tx_h_pm_state(priv, &t);
+		BUG_ON(bes2600_queue_put(&hw_priv->tx_queue[t.queue],
+				t.skb, &t.txpriv));
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0))
+		if (skb->sk)
+			sk_pacing_shift_update(skb->sk, 7);
+#endif
+
+		bes2600_dbg(BES2600_DBG_ROC, "QPUT %x, %pM, if_id - %d\n",
+			t.hdr->frame_control, t.da, priv->if_id);
+	}
+	spin_unlock_bh(&priv->ps_state_lock);
+
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	if (tid_update && sta)
+		ieee80211_sta_set_buffered(sta,
+				t.txpriv.tid, true);
+#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+
+	rcu_read_unlock();
+
+	bes2600_bh_wakeup(hw_priv);
+
+	return;
+
+drop:
+	bes2600_skb_dtor(hw_priv, skb, &t.txpriv);
+	return;
+}
+
+/* ******************************************************************** */
+
+static int bes2600_handle_pspoll(struct bes2600_vif *priv,
+				struct sk_buff *skb)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct ieee80211_sta *sta;
+	struct ieee80211_pspoll *pspoll =
+		(struct ieee80211_pspoll *) skb->data;
+	int link_id = 0;
+	u32 pspoll_mask = 0;
+	int drop = 1;
+	int i;
+
+	if (priv->join_status != BES2600_JOIN_STATUS_AP)
+		goto done;
+	if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN))
+		goto done;
+
+	rcu_read_lock();
+	sta = ieee80211_find_sta(priv->vif, pspoll->ta);
+	if (sta) {
+		struct bes2600_sta_priv *sta_priv;
+		sta_priv = (struct bes2600_sta_priv *)&sta->drv_priv;
+		link_id = sta_priv->link_id;
+		pspoll_mask = BIT(sta_priv->link_id);
+	}
+	rcu_read_unlock();
+	if (!link_id)
+		goto done;
+
+	priv->pspoll_mask |= pspoll_mask;
+	drop = 0;
+
+	/* Do not report pspols if data for given link id is
+	 * queued already. */
+	for (i = 0; i < 4; ++i) {
+		if (bes2600_queue_get_num_queued(priv,
+				&hw_priv->tx_queue[i],
+				pspoll_mask)) {
+			bes2600_bh_wakeup(hw_priv);
+			drop = 1;
+			break;
+		}
+	}
+	bes2600_info(BES2600_DBG_TXRX, "[RX] PSPOLL: %s\n", drop ? "local" : "fwd");
+done:
+	return drop;
+}
+
+/* ******************************************************************** */
+
+void bes2600_tx_confirm_cb(struct bes2600_common *hw_priv,
+			  struct wsm_tx_confirm *arg)
+{
+	u8 queue_id = bes2600_queue_get_queue_id(arg->packetID);
+	struct bes2600_queue *queue = &hw_priv->tx_queue[queue_id];
+	struct sk_buff *skb;
+	const struct bes2600_txpriv *txpriv;
+	struct bes2600_vif *priv;
+#ifdef CONFIG_BES2600_TESTMODE
+	u16 pkt_delay;
+#endif
+
+	bes2600_dbg(BES2600_DBG_TXRX, "[TX] TX confirm: %d, %d.\n",
+		arg->status, arg->ackFailures);
+
+	if (unlikely(bes2600_itp_tx_running(hw_priv)))
+		return;
+
+	priv = cw12xx_hwpriv_to_vifpriv(hw_priv, arg->if_id);
+	if (unlikely(!priv))
+		return;
+	if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
+		/* STA is stopped. */
+		spin_unlock(&priv->vif_lock);
+		return;
+	}
+
+	if (WARN_ON(queue_id >= 4)) {
+		spin_unlock(&priv->vif_lock);
+		return;
+	}
+
+	if (arg->status)
+		bes2600_dbg(BES2600_DBG_TXRX, "TX failed: %d.\n",
+				arg->status);
+
+#ifdef CONFIG_BES2600_TESTMODE
+	spin_lock_bh(&hw_priv->tsm_lock);
+	if ((arg->status == WSM_STATUS_RETRY_EXCEEDED) ||
+	    (arg->status == WSM_STATUS_TX_LIFETIME_EXCEEDED)) {
+		hw_priv->tsm_stats.msdu_discarded_count++;
+	} else if ((hw_priv->start_stop_tsm.start) &&
+		(arg->status == WSM_STATUS_SUCCESS)) {
+		if (queue_id == hw_priv->tsm_info.ac) {
+			struct timeval tmval;
+			do_gettimeofday(&tmval);
+			pkt_delay = hw_priv->start_stop_tsm.packetization_delay;
+			if (hw_priv->tsm_info.sta_roamed &&
+			    !hw_priv->tsm_info.use_rx_roaming) {
+				hw_priv->tsm_info.roam_delay = tmval.tv_usec -
+				hw_priv->tsm_info.txconf_timestamp_vo;
+				if (hw_priv->tsm_info.roam_delay > pkt_delay)
+					hw_priv->tsm_info.roam_delay -= pkt_delay;
+				bes2600_info(BES2600_DBG_TEST_MODE, "[TX] txConf"
+				"Roaming: roam_delay = %u\n",
+				hw_priv->tsm_info.roam_delay);
+				hw_priv->tsm_info.sta_roamed = 0;
+			}
+			hw_priv->tsm_info.txconf_timestamp_vo = tmval.tv_usec;
+		}
+	}
+	spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_BES2600_TESTMODE*/
+	if ((arg->status == WSM_REQUEUE) &&
+	    (arg->flags & WSM_TX_STATUS_REQUEUE)) {
+		/* "Requeue" means "implicit suspend" */
+		struct wsm_suspend_resume suspend = {
+			.link_id = arg->link_id,
+			.stop = 1,
+			.multicast = !arg->link_id,
+			.if_id = arg->if_id,
+		};
+		bes2600_suspend_resume(priv, &suspend);
+		wiphy_warn(priv->hw->wiphy, "Requeue for link_id %d (try %d)."
+			" STAs asleep: 0x%.8X\n",
+			arg->link_id,
+			bes2600_queue_get_generation(arg->packetID) + 1,
+			priv->sta_asleep_mask);
+#ifdef CONFIG_BES2600_TESTMODE
+		WARN_ON(bes2600_queue_requeue(hw_priv, queue,
+				arg->packetID, true));
+#else
+		WARN_ON(bes2600_queue_requeue(queue,
+				arg->packetID, true));
+#endif
+		spin_lock_bh(&priv->ps_state_lock);
+		if (!arg->link_id) {
+			priv->buffered_multicasts = true;
+			if (priv->sta_asleep_mask) {
+				queue_work(hw_priv->workqueue,
+					&priv->multicast_start_work);
+			}
+		}
+		spin_unlock_bh(&priv->ps_state_lock);
+		spin_unlock(&priv->vif_lock);
+	} else if (!(bes2600_queue_get_skb(
+			queue, arg->packetID, &skb, &txpriv))) {
+		struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb);
+		int tx_count = arg->ackFailures;
+		u8 ht_flags = 0;
+		int i;
+
+#ifndef P2P_MULTIVIF
+		if (txpriv->offchannel_if_id)
+			bes2600_dbg(BES2600_DBG_ROC, "TX CONFIRM %x - %d - %d\n",
+				skb->data[txpriv->offset],
+				txpriv->offchannel_if_id, arg->status);
+#endif
+		if (priv->association_mode.greenfieldMode)
+			ht_flags |= IEEE80211_TX_RC_GREEN_FIELD;
+
+		if (likely(!arg->status)) {
+			tx->flags |= IEEE80211_TX_STAT_ACK;
+			priv->cqm_tx_failure_count = 0;
+			++tx_count;
+			bes2600_debug_txed(priv);
+			if (arg->flags & WSM_TX_STATUS_AGGREGATION) {
+				/* Do not report aggregation to mac80211:
+				 * it confuses minstrel a lot. */
+				/* tx->flags |= IEEE80211_TX_STAT_AMPDU; */
+				bes2600_debug_txed_agg(priv);
+			}
+		} else {
+			spin_lock(&priv->bss_loss_lock);
+			if (priv->bss_loss_status == BES2600_BSS_LOSS_CONFIRMING &&
+					priv->bss_loss_confirm_id == arg->packetID) {
+				priv->bss_loss_status = BES2600_BSS_LOSS_CONFIRMED;
+				spin_unlock(&priv->bss_loss_lock);
+				cancel_delayed_work(&priv->bss_loss_work);
+				queue_delayed_work(hw_priv->workqueue,
+						&priv->bss_loss_work, BSS_LOSS_CFM_INV * HZ / 1000);
+			} else {
+				spin_unlock(&priv->bss_loss_lock);
+			}
+
+			/* TODO: Update TX failure counters */
+			if (unlikely(priv->cqm_tx_failure_thold &&
+			     (++priv->cqm_tx_failure_count >
+			      priv->cqm_tx_failure_thold))) {
+				priv->cqm_tx_failure_thold = 0;
+				queue_work(hw_priv->workqueue,
+						&priv->tx_failure_work);
+			}
+			if (tx_count)
+				++tx_count;
+		}
+		spin_unlock(&priv->vif_lock);
+
+		for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
+			if (tx->status.rates[i].count >= tx_count) {
+				tx->status.rates[i].count = tx_count;
+				break;
+			}
+			tx_count -= tx->status.rates[i].count;
+			if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS)
+				tx->status.rates[i].flags |= ht_flags;
+		}
+
+		for (++i; i < IEEE80211_TX_MAX_RATES; ++i) {
+			tx->status.rates[i].count = 0;
+			tx->status.rates[i].idx = -1;
+		}
+#ifdef KEY_FRAME_SW_RETRY
+		if (bes2600_bh_sw_process(hw_priv, arg) == 0) {
+			return;
+		}
+#endif
+
+#ifdef CONFIG_BES2600_TESTMODE
+		bes2600_queue_remove(hw_priv, queue, arg->packetID);
+#else
+		bes2600_queue_remove(queue, arg->packetID);
+#endif /*CONFIG_BES2600_TESTMODE*/
+	}else {
+		spin_unlock(&priv->vif_lock);
+		return;
+	}
+}
+
+static void bes2600_notify_buffered_tx(struct bes2600_vif *priv,
+			       struct sk_buff *skb, int link_id, int tid)
+{
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	struct ieee80211_sta *sta;
+	struct ieee80211_hdr *hdr;
+	u8 *buffered;
+	u8 still_buffered = 0;
+
+	if (link_id && tid < BES2600_MAX_TID) {
+		buffered = priv->link_id_db
+				[link_id - 1].buffered;
+
+		spin_lock_bh(&priv->ps_state_lock);
+		if (!WARN_ON(!buffered[tid]))
+			still_buffered = --buffered[tid];
+		spin_unlock_bh(&priv->ps_state_lock);
+
+		if (!still_buffered && tid < BES2600_MAX_TID) {
+			hdr = (struct ieee80211_hdr *) skb->data;
+			rcu_read_lock();
+			sta = ieee80211_find_sta(priv->vif, hdr->addr1);
+			if (sta)
+				ieee80211_sta_set_buffered(sta, tid, false);
+			rcu_read_unlock();
+		}
+	}
+#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
+}
+
+void bes2600_skb_dtor(struct bes2600_common *hw_priv,
+		     struct sk_buff *skb,
+		     const struct bes2600_txpriv *txpriv)
+{
+	struct bes2600_vif *priv =
+		__cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id);
+
+	skb_pull(skb, txpriv->offset);
+	if (priv && txpriv->rate_id != BES2600_INVALID_RATE_ID) {
+		bes2600_notify_buffered_tx(priv, skb,
+				txpriv->raw_link_id, txpriv->tid);
+		tx_policy_put(hw_priv, txpriv->rate_id);
+	}
+	if (likely(!bes2600_is_itp(hw_priv))) {
+		ieee80211_tx_status(hw_priv->hw, skb);
+		bes2600_tx_status(priv, skb);
+	}
+
+}
+#ifdef CONFIG_BES2600_TESTMODE
+/* TODO It should be removed before official delivery */
+static void frame_hexdump(char *prefix, u8 *data, int len)
+{
+	bes2600_dbg_dump(BES2600_DBG_TXRX, prefix, data, len);
+}
+/**
+ * bes2600_tunnel_send_testmode_data - Send test frame to the driver
+ *
+ * @priv: pointer to bes2600 private structure
+ * @skb: skb with frame
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int bes2600_tunnel_send_testmode_data(struct bes2600_common *hw_priv,
+					    struct sk_buff *skb)
+{
+	if (bes2600_testmode_event(hw_priv->hw->wiphy, BES_MSG_EVENT_FRAME_DATA,
+				 skb->data, skb->len, GFP_ATOMIC))
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * bes2600_frame_test_detection - Detection frame_test
+ *
+ * @priv: pointer to bes2600 vif structure
+ * @frame: ieee80211 header
+ * @skb: skb with frame
+ *
+ * Returns: 1 - frame test detected, 0 - not detected
+ */
+static int bes2600_frame_test_detection(struct bes2600_vif *priv,
+				       struct ieee80211_hdr *frame,
+				       struct sk_buff *skb)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	int hdrlen = ieee80211_hdrlen(frame->frame_control);
+	int detected = 0;
+	int ret;
+
+	if (hdrlen + hw_priv->test_frame.len <= skb->len &&
+	    memcmp(skb->data + hdrlen, hw_priv->test_frame.data,
+		   hw_priv->test_frame.len) == 0) {
+		detected = 1;
+		bes2600_dbg(BES2600_DBG_TEST_MODE, "TEST FRAME detected");
+		frame_hexdump("TEST FRAME original:", skb->data, skb->len);
+		ret = ieee80211_data_to_8023(skb, hw_priv->mac_addr,
+				priv->mode);
+		if (!ret) {
+			frame_hexdump("FRAME 802.3:", skb->data, skb->len);
+			ret = bes2600_tunnel_send_testmode_data(hw_priv, skb);
+		}
+		if (ret)
+			bes2600_err(BES2600_DBG_TEST_MODE, "Send TESTFRAME failed(%d)", ret);
+	}
+	return detected;
+}
+#endif /* CONFIG_BES2600_TESTMODE */
+
+
+static void
+bes2600_rx_h_ba_stat(struct bes2600_vif *priv,
+		    size_t hdrlen, size_t skb_len )
+{
+	struct bes2600_common *hw_priv = priv->hw_priv;
+	if (priv->join_status != BES2600_JOIN_STATUS_STA)
+		return;
+	if (!bes2600_is_ht(&hw_priv->ht_info))
+		return;
+	if (!priv->setbssparams_done)
+		return;
+
+	spin_lock_bh(&hw_priv->ba_lock);
+	hw_priv->ba_acc_rx += skb_len - hdrlen;
+	if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
+		mod_timer(&hw_priv->ba_timer,
+			jiffies + BES2600_BLOCK_ACK_INTERVAL);
+	}
+	hw_priv->ba_cnt_rx++;
+	spin_unlock_bh(&hw_priv->ba_lock);
+}
+
+void bes2600_rx_cb(struct bes2600_vif *priv,
+		  struct wsm_rx *arg,
+		  struct sk_buff **skb_p)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	struct sk_buff *skb = *skb_p;
+	struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
+	struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
+	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+	struct bes2600_link_entry *entry = NULL;
+	bool early_data = false;
+	size_t hdrlen = 0;
+
+	hdr->flag = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+	hdr->boottime_ns = ktime_get_boot_ns();
+#endif
+	if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
+		/* STA is stopped. */
+		goto drop;
+	}
+
+	/* wakeup device based on frame type */
+	if (!is_multicast_ether_addr(ieee80211_get_DA(frame))) {
+		/* for unicast, wakeup device directly */
+		bes2600_pwr_set_busy_event_with_timeout_async(
+	 			hw_priv, BES_PWR_LOCK_ON_RX, BES_PWR_EVENT_RX_TIMEOUT);
+	}
+
+	if ((ieee80211_is_action(frame->frame_control))
+                        && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+		u8 *action = (u8*)&mgmt->u.action.category;
+		bes2600_check_go_neg_conf_success(hw_priv, action);
+	}
+
+#ifdef CONFIG_BES2600_TESTMODE
+	spin_lock_bh(&hw_priv->tsm_lock);
+	if (hw_priv->start_stop_tsm.start) {
+		unsigned queue_id = skb_get_queue_mapping(skb);
+		if (queue_id == 0) {
+			struct timeval tmval;
+			do_gettimeofday(&tmval);
+			if (hw_priv->tsm_info.sta_roamed &&
+			    hw_priv->tsm_info.use_rx_roaming) {
+				hw_priv->tsm_info.roam_delay = tmval.tv_usec -
+					hw_priv->tsm_info.rx_timestamp_vo;
+				bes2600_dbg(BES2600_DBG_TEST_MODE, "[RX] RxInd Roaming:"
+				"roam_delay = %u\n", hw_priv->tsm_info.roam_delay);
+				hw_priv->tsm_info.sta_roamed = 0;
+			}
+			hw_priv->tsm_info.rx_timestamp_vo = tmval.tv_usec;
+		}
+	}
+	spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_BES2600_TESTMODE*/
+	if (arg->link_id && (arg->link_id != BES2600_LINK_ID_UNMAPPED)
+			&& (arg->link_id <= BES2600_MAX_STA_IN_AP_MODE)) {
+		entry =	&priv->link_id_db[arg->link_id - 1];
+		if (entry->status == BES2600_LINK_SOFT &&
+				ieee80211_is_data(frame->frame_control))
+			early_data = true;
+		entry->timestamp = jiffies;
+	}
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	else if ((arg->link_id == BES2600_LINK_ID_UNMAPPED)
+			&& (priv->vif->p2p == WSM_START_MODE_P2P_GO)
+			&& ieee80211_is_action(frame->frame_control)
+			&& (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+		bes2600_dbg(BES2600_DBG_TXRX, "[RX] Going to MAP&RESET link ID\n");
+
+		if (work_pending(&priv->linkid_reset_work))
+			WARN_ON(1);
+
+		memcpy(&priv->action_frame_sa[0],
+				ieee80211_get_SA(frame), ETH_ALEN);
+		priv->action_linkid = 0;
+		schedule_work(&priv->linkid_reset_work);
+	}
+
+	if (arg->link_id && (arg->link_id != BES2600_LINK_ID_UNMAPPED)
+			&& (priv->vif->p2p == WSM_START_MODE_P2P_GO)
+			&& ieee80211_is_action(frame->frame_control)
+			&& (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+		/* Link ID already exists for the ACTION frame.
+		 * Reset and Remap */
+		if (work_pending(&priv->linkid_reset_work))
+			WARN_ON(1);
+		memcpy(&priv->action_frame_sa[0],
+				ieee80211_get_SA(frame), ETH_ALEN);
+		priv->action_linkid = arg->link_id;
+		schedule_work(&priv->linkid_reset_work);
+	}
+#endif
+	if (unlikely(arg->status)) {
+		if (arg->status == WSM_STATUS_MICFAILURE) {
+			bes2600_warn(BES2600_DBG_TXRX, "[RX] MIC failure.\n");
+			hdr->flag |= RX_FLAG_MMIC_ERROR;
+		} else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
+			bes2600_dbg(BES2600_DBG_TXRX, "[RX] No key found.\n");
+			goto drop;
+		} else {
+			bes2600_warn(BES2600_DBG_TXRX, "[RX] Receive failure: %d.\n",
+				arg->status);
+			goto drop;
+		}
+	}
+
+	if (skb->len < sizeof(struct ieee80211_pspoll)) {
+		wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. "
+				"Size is lesser than IEEE header.\n");
+		goto drop;
+	}
+
+	if (unlikely(ieee80211_is_pspoll(frame->frame_control)))
+		if (bes2600_handle_pspoll(priv, skb))
+			goto drop;
+
+	hdr->mactime = 0; /* Not supported by WSM */
+	hdr->band = (arg->channelNumber > 14) ?
+			NL80211_BAND_5GHZ : NL80211_BAND_2GHZ;
+	hdr->freq = ieee80211_channel_to_frequency(
+			arg->channelNumber,
+			hdr->band);
+
+	if (arg->rxedRate >= 14) {
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0))
+		hdr->encoding |= RX_ENC_HT;
+#else
+		hdr->flag |= RX_FLAG_HT;
+#endif
+		hdr->rate_idx = arg->rxedRate - 14;
+	} else if (arg->rxedRate >= 4) {
+		if (hdr->band == NL80211_BAND_5GHZ)
+			hdr->rate_idx = arg->rxedRate - 6;
+		else
+			hdr->rate_idx = arg->rxedRate - 2;
+	} else {
+		hdr->rate_idx = arg->rxedRate;
+	}
+
+	hdr->signal = (s8)arg->rcpiRssi;
+	hdr->antenna = 0;
+
+	hdrlen = ieee80211_hdrlen(frame->frame_control);
+	if (WSM_RX_STATUS_ENCRYPTION(arg->flags) == WSM_RX_STATUS_DECRYPTED) {
+		/* decrypted frame with iv/icv stripped */
+		hdr->flag |= RX_FLAG_DECRYPTED;
+		hdr->flag |= RX_FLAG_IV_STRIPPED;
+	}
+	else if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+		size_t iv_len = 0, icv_len = 0;
+
+		hdr->flag |= RX_FLAG_DECRYPTED;
+
+		/* Oops... There is no fast way to ask mac80211 about
+		 * IV/ICV lengths. Even defineas are not exposed.*/
+		switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+		case WSM_RX_STATUS_WEP:
+			iv_len = 4 /* WEP_IV_LEN */;
+			icv_len = 4 /* WEP_ICV_LEN */;
+			break;
+		case WSM_RX_STATUS_TKIP:
+			iv_len = 8 /* TKIP_IV_LEN */;
+			icv_len = 4 /* TKIP_ICV_LEN */
+				+ 8 /*MICHAEL_MIC_LEN*/;
+			hdr->flag |= RX_FLAG_MMIC_STRIPPED;
+			break;
+		case WSM_RX_STATUS_AES:
+			iv_len = 8 /* CCMP_HDR_LEN */;
+			icv_len = 8 /* CCMP_MIC_LEN */;
+			break;
+		case WSM_RX_STATUS_WAPI:
+			iv_len = 18 /* WAPI_HDR_LEN */;
+			icv_len = 16 /* WAPI_MIC_LEN */;
+			hdr->flag |= RX_FLAG_IV_STRIPPED;
+			break;
+		default:
+			WARN_ON("Unknown encryption type");
+			goto drop;
+		}
+
+		/* Firmware strips ICV in case of MIC failure. */
+		if (arg->status == WSM_STATUS_MICFAILURE) {
+			icv_len = 0;
+			hdr->flag |= RX_FLAG_IV_STRIPPED;
+		}
+
+		if (skb->len < hdrlen + iv_len + icv_len) {
+			wiphy_warn(priv->hw->wiphy, "Mailformed SDU rx'ed. "
+				"Size is lesser than crypto headers.\n");
+			goto drop;
+		}
+
+		/* Protocols not defined in mac80211 should be
+		stripped/crypted in driver/firmware */
+		if (WSM_RX_STATUS_ENCRYPTION(arg->flags) ==
+						WSM_RX_STATUS_WAPI) {
+			/* Remove IV, ICV and MIC */
+			skb_trim(skb, skb->len - icv_len);
+			memmove(skb->data + iv_len, skb->data, hdrlen);
+			skb_pull(skb, iv_len);
+		}
+
+	}
+
+	bes2600_debug_rxed(priv);
+	if (arg->flags & WSM_RX_STATUS_AGGREGATE)
+		bes2600_debug_rxed_agg(priv);
+
+	if (ieee80211_is_beacon(frame->frame_control) &&
+			!arg->status &&
+			!memcmp(ieee80211_get_SA(frame), priv->join_bssid,
+				ETH_ALEN)) {
+		const u8 *tim_ie;
+		u8 *ies;
+		size_t ies_len;
+		priv->disable_beacon_filter = false;
+		queue_work(hw_priv->workqueue, &priv->update_filtering_work);
+		ies = ((struct ieee80211_mgmt *)
+			  (skb->data))->u.beacon.variable;
+		ies_len = skb->len - (ies - (u8 *)(skb->data));
+
+		tim_ie = bes2600_get_ie(ies, ies_len, WLAN_EID_TIM);
+		if (tim_ie) {
+			struct ieee80211_tim_ie *tim =
+				(struct ieee80211_tim_ie *)&tim_ie[2];
+
+			if (priv->join_dtim_period != tim->dtim_period) {
+				priv->join_dtim_period = tim->dtim_period;
+				queue_work(hw_priv->workqueue,
+					&priv->set_beacon_wakeup_period_work);
+			}
+		}
+		if (unlikely(priv->disable_beacon_filter)) {
+			priv->disable_beacon_filter = false;
+			queue_work(hw_priv->workqueue,
+				&priv->update_filtering_work);
+		}
+	}
+#ifdef AP_HT_CAP_UPDATE
+        if (priv->mode == NL80211_IFTYPE_AP &&
+                        ieee80211_is_beacon(frame->frame_control) &&
+                        !arg->status){
+
+                u8 *ies;
+                size_t ies_len;
+                const u8 *ht_cap;
+                ies = ((struct ieee80211_mgmt *)
+                          (skb->data))->u.beacon.variable;
+                ies_len = skb->len - (ies - (u8 *)(skb->data));
+                ht_cap = bes2600_get_ie(ies, ies_len, WLAN_EID_HT_CAPABILITY);
+                if(!ht_cap){
+                        priv->ht_info |= 0x0011;
+                }
+                queue_work(hw_priv->workqueue,
+                                &priv->ht_info_update_work);
+
+        }
+#endif
+
+#ifdef ROAM_OFFLOAD
+	if ((ieee80211_is_beacon(frame->frame_control)||ieee80211_is_probe_resp(frame->frame_control)) &&
+			!arg->status ) {
+		if (hw_priv->auto_scanning && !atomic_read(&hw_priv->scan.in_progress))
+			hw_priv->frame_rcvd = 1;
+
+		if (!memcmp(ieee80211_get_SA(frame), priv->join_bssid, ETH_ALEN)) {
+			if (hw_priv->beacon)
+				dev_kfree_skb(hw_priv->beacon);
+			hw_priv->beacon = skb_copy(skb, GFP_ATOMIC);
+			if (!hw_priv->beacon)
+				bes2600_err(BES2600_DBG_TXRX, "bes2600: sched_scan: own beacon storing failed\n");
+		}
+	}
+#endif /*ROAM_OFFLOAD*/
+
+	if (ieee80211_is_deauth(frame->frame_control) ||
+	    ieee80211_is_disassoc(frame->frame_control))
+	    bes2600_pwr_set_busy_event_with_timeout_async(hw_priv,
+	    		BES_PWR_LOCK_ON_DISCON, 1500);
+
+
+	if (ieee80211_is_data(frame->frame_control)) {
+		bes2600_rx_h_ba_stat(priv, hdrlen, skb->len);
+		bes2600_rx_status(priv, skb);
+	}
+
+#ifdef CONFIG_BES2600_TESTMODE
+	if (hw_priv->test_frame.len > 0 &&
+		priv->mode == NL80211_IFTYPE_STATION) {
+		if (bes2600_frame_test_detection(priv, frame, skb) == 1) {
+			consume_skb(skb);
+			*skb_p = NULL;
+			return;
+		}
+	}
+#endif /* CONFIG_BES2600_TESTMODE */
+
+	if (unlikely(bes2600_itp_rxed(hw_priv, skb)))
+		consume_skb(skb);
+	else if (unlikely(early_data)) {
+		spin_lock_bh(&priv->ps_state_lock);
+		/* Double-check status with lock held */
+		if (entry->status == BES2600_LINK_SOFT)
+			skb_queue_tail(&entry->rx_queue, skb);
+		else
+			ieee80211_rx_irqsafe(priv->hw, skb);
+		spin_unlock_bh(&priv->ps_state_lock);
+	} else {
+		ieee80211_rx_irqsafe(priv->hw, skb);
+	}
+	*skb_p = NULL;
+
+	return;
+
+drop:
+	/* TODO: update failure counters */
+	return;
+}
+
+/* ******************************************************************** */
+/* Security								*/
+
+int bes2600_alloc_key(struct bes2600_common *hw_priv)
+{
+	int idx;
+
+	idx = ffs(~hw_priv->key_map) - 1;
+	if (idx < 0 || idx > WSM_KEY_MAX_INDEX)
+		return -1;
+
+	hw_priv->key_map |= BIT(idx);
+	hw_priv->keys[idx].entryIndex = idx;
+	return idx;
+}
+
+void bes2600_free_key(struct bes2600_common *hw_priv, int idx)
+{
+	BUG_ON(!(hw_priv->key_map & BIT(idx)));
+	memset(&hw_priv->keys[idx], 0, sizeof(hw_priv->keys[idx]));
+	hw_priv->key_map &= ~BIT(idx);
+}
+
+void bes2600_free_keys(struct bes2600_common *hw_priv)
+{
+	memset(&hw_priv->keys, 0, sizeof(hw_priv->keys));
+	hw_priv->key_map = 0;
+}
+
+int bes2600_upload_keys(struct bes2600_vif *priv)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	int idx, ret = 0;
+
+	for (idx = 0; idx <= WSM_KEY_MAX_IDX; ++idx)
+		if (hw_priv->key_map & BIT(idx)) {
+			ret = wsm_add_key(hw_priv, &hw_priv->keys[idx], priv->if_id);
+			if (ret < 0)
+				break;
+		}
+	return ret;
+}
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+/* Workaround for WFD test case 6.1.10 */
+void bes2600_link_id_reset(struct work_struct *work)
+{
+	struct bes2600_vif *priv =
+		container_of(work, struct bes2600_vif, linkid_reset_work);
+	struct bes2600_common *hw_priv = priv->hw_priv;
+	int temp_linkid;
+
+	if (!priv->action_linkid) {
+		/* In GO mode we can receive ACTION frames without a linkID */
+		temp_linkid = bes2600_alloc_link_id(priv,
+				&priv->action_frame_sa[0]);
+		WARN_ON(!temp_linkid);
+		if (temp_linkid) {
+			/* Make sure we execute the WQ */
+			flush_workqueue(hw_priv->workqueue);
+			/* Release the link ID */
+			spin_lock_bh(&priv->ps_state_lock);
+			priv->link_id_db[temp_linkid - 1].prev_status =
+				priv->link_id_db[temp_linkid - 1].status;
+			priv->link_id_db[temp_linkid - 1].status =
+				BES2600_LINK_RESET;
+			spin_unlock_bh(&priv->ps_state_lock);
+			wsm_lock_tx_async(hw_priv);
+			if (queue_work(hw_priv->workqueue,
+				       &priv->link_id_work) <= 0)
+				wsm_unlock_tx(hw_priv);
+		}
+	} else {
+		spin_lock_bh(&priv->ps_state_lock);
+		priv->link_id_db[priv->action_linkid - 1].prev_status =
+			priv->link_id_db[priv->action_linkid - 1].status;
+		priv->link_id_db[priv->action_linkid - 1].status =
+			BES2600_LINK_RESET_REMAP;
+		spin_unlock_bh(&priv->ps_state_lock);
+		wsm_lock_tx_async(hw_priv);
+		if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0)
+				wsm_unlock_tx(hw_priv);
+		flush_workqueue(hw_priv->workqueue);
+	}
+}
+#endif
diff --git a/drivers/staging/bes2600/txrx.h b/drivers/staging/bes2600/txrx.h
new file mode 100644
index 000000000000..0825f44f4d35
--- /dev/null
+++ b/drivers/staging/bes2600/txrx.h
@@ -0,0 +1,99 @@
+/*
+ * Datapath interface for BES2600 mac80211 drivers
+ *
+ * Copyright (c) 2010, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES2600_TXRX_H
+#define BES2600_TXRX_H
+
+#include <linux/list.h>
+
+/* extern */ struct ieee80211_hw;
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct wsm_rx;
+/* extern */ struct wsm_tx_confirm;
+/* extern */ struct bes2600_txpriv;
+/* extern */ struct bes2600_vif;
+
+struct tx_policy {
+	union {
+		__le32 tbl[3];
+		u8 raw[12];
+	};
+	u8  defined;		/* TODO: u32 or u8, profile and select best */
+	u8  usage_count;	/* --// -- */
+	u8  retry_count;	/* --// -- */
+	u8  uploaded;
+};
+
+struct tx_policy_cache_entry {
+	struct tx_policy policy;
+	struct list_head link;
+};
+
+#define TX_POLICY_CACHE_SIZE	(8)
+struct tx_policy_cache {
+	struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE];
+	struct list_head used;
+	struct list_head free;
+	spinlock_t lock;
+};
+
+/* ******************************************************************** */
+/* TX policy cache							*/
+/* Intention of TX policy cache is an overcomplicated WSM API.
+ * Device does not accept per-PDU tx retry sequence.
+ * It uses "tx retry policy id" instead, so driver code has to sync
+ * linux tx retry sequences with a retry policy table in the device.
+ */
+void tx_policy_init(struct bes2600_common *hw_priv);
+void tx_policy_deinit(struct bes2600_common *hw_priv);
+void tx_policy_upload_work(struct work_struct *work);
+
+/* ******************************************************************** */
+/* TX implementation							*/
+
+u32 bes2600_rate_mask_to_wsm(struct bes2600_common *hw_priv,
+			       u32 rates);
+void bes2600_tx(struct ieee80211_hw *dev,
+			struct ieee80211_tx_control *control,
+			struct sk_buff *skb);
+void bes2600_skb_dtor(struct bes2600_common *hw_priv,
+		     struct sk_buff *skb,
+		     const struct bes2600_txpriv *txpriv);
+
+/* ******************************************************************** */
+/* WSM callbacks							*/
+
+void bes2600_tx_confirm_cb(struct bes2600_common *hw_priv,
+			  struct wsm_tx_confirm *arg);
+void bes2600_rx_cb(struct bes2600_vif *priv,
+		  struct wsm_rx *arg,
+		  struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* Timeout								*/
+
+void bes2600_tx_timeout(struct work_struct *work);
+
+/* ******************************************************************** */
+/* Security								*/
+int bes2600_alloc_key(struct bes2600_common *hw_priv);
+void bes2600_free_key(struct bes2600_common *hw_priv, int idx);
+void bes2600_free_keys(struct bes2600_common *hw_priv);
+int bes2600_upload_keys(struct bes2600_vif *priv);
+
+/* ******************************************************************** */
+/* Workaround for WFD test case 6.1.10					*/
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+void bes2600_link_id_reset(struct work_struct *work);
+#endif
+
+#endif /* BES2600_TXRX_H */
diff --git a/drivers/staging/bes2600/txrx_opt.c b/drivers/staging/bes2600/txrx_opt.c
new file mode 100644
index 000000000000..15f6abbb666f
--- /dev/null
+++ b/drivers/staging/bes2600/txrx_opt.c
@@ -0,0 +1,467 @@
+/***************************************************************************
+ *
+ * Copyright 2015-2022 BES.
+ * All rights reserved. All unpublished rights reserved.
+ *
+ * No part of this work may be used or reproduced in any form or by any
+ * means, or stored in a database or retrieval system, without prior written
+ * permission of BES.
+ *
+ * Use of this work is governed by a license granted by BES.
+ * This work contains confidential and proprietary information of
+ * BES. which is protected by copyright, trade secret,
+ * trademark and other intellectual property rights.
+ *
+ ****************************************************************************/
+#include <net/mac80211.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+
+#include "bes2600.h"
+#include "wsm.h"
+#include "bh.h"
+#include "ap.h"
+#include "debug.h"
+#include "sta.h"
+#include "sbus.h"
+#include "bes2600_log.h"
+#include "bes_pwr.h"
+
+#define TXRX_OPT_CLOSE_EDCA     0
+#define TXRX_OPT_EDCA_MAX_LEVEL 4
+#define TX_AVG_TIME_COUNT       10
+#define TXRX_OPT_PEROID         500
+#define TXRX_OPT_DEBUG          1
+
+#define TXRX_HIGH_TP_THRESHOLD_2G4    30000   //unit is kbps
+#define TXRX_HIGH_TP_THRESHOLD_5G    40000    //unit is kbps
+#define TXRX_HIGH_TP_DELTA_TIME_2G4   8       //unit ms
+#define TXRX_HIGH_TP_DELTA_TIME_5G   6       //unit ms
+
+static uint32_t tx_delta_time_arr[4][TX_AVG_TIME_COUNT];
+static uint32_t tx_queue_arr[4] = {0};
+static uint32_t tx_delta_time_total = 0;
+static uint32_t tx_delta_time_total_cnt = 0;
+
+void bes2600_add_tx_delta_time(uint32_t tx_delta_time)
+{
+	tx_delta_time_total += tx_delta_time;
+	tx_delta_time_total_cnt++;
+}
+
+uint32_t bes2600_get_tx_delta_time(void)
+{
+	if (tx_delta_time_total_cnt != 0)
+		return tx_delta_time_total / tx_delta_time_total_cnt;
+	else
+		return 0;
+}
+
+void bes2600_clear_tx_delta_time(void)
+{
+	tx_delta_time_total_cnt = 0;
+	tx_delta_time_total = 0;
+	return ;
+}
+
+uint32_t bes2600_get_tx_ac_delta_time(int ac)
+{
+	uint32_t avg_time = 0;
+	int i = 0;
+	for (i = 0; i < TX_AVG_TIME_COUNT; i++) {
+		avg_time += tx_delta_time_arr[ac][i];
+	}
+	return avg_time / TX_AVG_TIME_COUNT;
+}
+
+void bes2600_clear_tx_ac_delta_time(int ac)
+{
+	int i = 0;
+	for (i = 0; i < TX_AVG_TIME_COUNT; i++) {
+		tx_delta_time_arr[ac][i] = 0;
+	}
+	return ;
+}
+
+void bes2600_add_tx_ac_delta_time(int ac, uint32_t del_time)
+{
+#if 0
+	if (tx_queue_arr[ac] >= (TX_AVG_TIME_COUNT - 1)) {
+		static int num = 0;
+		if ((num ++ % 10) == 0)
+			bes2600_info(BES2600_DBG_TXRX_LVL, "%s %d %d %d %d %d del=%d\n\r", __func__, tx_delta_time_arr[ac][0],
+				     tx_delta_time_arr[ac][2],  tx_delta_time_arr[ac][4],  tx_delta_time_arr[ac][6],
+				     tx_delta_time_arr[ac][8], del_time);
+	}
+#endif
+	tx_delta_time_arr[ac][tx_queue_arr[ac]] = del_time;
+	tx_queue_arr[ac] = (tx_queue_arr[ac] >= (TX_AVG_TIME_COUNT - 1)) ? 0 : (tx_queue_arr[ac] + 1);
+
+}
+
+
+int bes2600_set_txrx_opt_param(struct bes2600_common *hw_priv,
+			       struct bes2600_vif *priv,
+			       MIB_TXRX_OPT_PARAM  *para)
+{
+
+	int ret = 0;
+	ret = WARN_ON(wsm_write_mib(hw_priv,
+				    WSM_MIB_ID_EXT_TXRX_OPT_PARAM,
+				    (u8 *)para,
+				    sizeof(MIB_TXRX_OPT_PARAM),
+				    priv->if_id));
+	return ret;
+}
+
+int bes2600_enable_tx_shortgi(struct bes2600_common *hw_priv,
+			      struct bes2600_vif *priv,
+			      u8 onoff)
+{
+	int ret = 0;
+	static u8 en = 0xff;
+
+	bes2600_dbg(BES2600_DBG_TXRX_OPT, "%s onoff=%d\n\r", __func__, onoff);
+
+	if (en != onoff) {
+		en = onoff;
+		ret = WARN_ON(wsm_write_mib(hw_priv,
+					    WSM_MIB_ID_EXT_TX_SHORT_GI_ENABLED,
+					    (u8 *)&onoff,
+					    sizeof(onoff),
+					    priv->if_id));
+	}
+	return ret;
+}
+
+void bes2600_rx_status(struct bes2600_vif *priv, struct sk_buff *skb)
+{
+	priv->dot11ReceivedFragmentCount++;
+	priv->dot11ReceivedDataBytes += skb->len;
+}
+
+void bes2600_tx_status(struct bes2600_vif *priv, struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+	__le16 fc;
+	int i;
+	int retry_count = -1;
+	fc = hdr->frame_control;
+
+	if (ieee80211_is_data(fc)) {
+		for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+			if (info->status.rates[i].idx < 0) {
+				break;
+			}
+			retry_count += info->status.rates[i].count;
+		}
+		if (retry_count < 0)
+			retry_count = 0;
+
+		if (info->flags & IEEE80211_TX_STAT_ACK) {
+			priv->dot11TransmittedFrameCount++;
+			priv->dot11TransmittedDataBytes += (skb->len + 4);
+			if (retry_count > 0)
+				priv->dot11RetryCount += retry_count;
+
+		} else {
+			/* tx fail.*/
+			priv->dot11FailedCount++;
+		}
+	}
+
+}
+void bes2600_set_default_params(struct bes2600_common *hw_priv, struct bes2600_vif *priv);
+
+int bes2600_set_high_edca_params(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int level)
+{
+	struct wsm_edca_params arg;
+	int i = 0;
+	static int lev = 0;
+	bes2600_dbg(BES2600_DBG_TXRX_OPT, "set edca level=%d\n\r", level);
+
+	if (lev == level)
+		return 0;
+
+	memcpy(&arg, &(priv->edca), sizeof(struct wsm_edca_params));
+
+
+	if (level == 0) {
+		bes2600_set_default_params(hw_priv, priv);
+		return 0;
+	} else if (level == 1) {
+		for ( i = 0; i < 4; i++) {
+			arg.params[i].aifns = 2;
+			arg.params[i].cwMax = 7;
+			arg.params[i].cwMin = 3;
+			/*
+			 * tx op must set 0
+			 * set other, some AP may not response BA when rx data.
+			*/
+			arg.params[i].txOpLimit = 0;
+			arg.params[i].maxReceiveLifetime = 0xc8;
+		}
+	} else if (level == 2) {
+		for ( i = 0; i < 4; i++) {
+			arg.params[i].aifns = 2;
+			arg.params[i].cwMax = 5;
+			arg.params[i].cwMin = 1;
+			arg.params[i].txOpLimit = 0;
+			arg.params[i].maxReceiveLifetime = 0xc8;
+		}
+	} else if (level == 3) {
+		for ( i = 0; i < 4; i++) {
+			arg.params[i].aifns = 2;
+			arg.params[i].cwMax = 3;
+			arg.params[i].cwMin = 1;
+			arg.params[i].txOpLimit = 0;
+			arg.params[i].maxReceiveLifetime = 0xc8;
+		}
+	} else if (level == 4) {
+		for ( i = 0; i < 4; i++) {
+			arg.params[i].aifns = 1;
+			arg.params[i].cwMax = 3;
+			arg.params[i].cwMin = 1;
+			arg.params[i].txOpLimit = 0;
+			arg.params[i].maxReceiveLifetime = 0xc8;
+		}
+	}
+	wsm_set_edca_params(hw_priv, &arg, priv->if_id);
+	return 0;
+}
+void bes2600_set_default_params(struct bes2600_common *hw_priv, struct bes2600_vif *priv)
+{
+	bes2600_dbg(BES2600_DBG_TXRX_OPT, "set edca default\n\r");
+	wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id);
+}
+void bes2600_set_cca_method(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int value)
+{
+// todo set cca alg
+}
+
+void bes2600_set_dynamic_agc(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int value)
+{
+// todo set agc alg 
+}
+int bes2600_get_tx_av_max_delta_time(void)
+{
+	int max_avg = 0;
+	int i = 0;
+
+	for (i = 0; i < 4; i++) {
+		if (max_avg < bes2600_get_tx_ac_delta_time(i)) {
+			max_avg = bes2600_get_tx_ac_delta_time(i);
+		}
+		//bes2600_clear_tx_ac_delta_time(i);
+	}
+	return max_avg;
+}
+
+bool bes2600_station_is_ap_ht40(struct bes2600_common *hw_priv)
+{
+
+	if (hw_priv->hw) {
+		struct ieee80211_conf *conf = &hw_priv->hw->conf;
+		if (conf !=  NULL)
+			if (conf->chandef.width == NL80211_CHAN_WIDTH_40)
+				return true;
+	}
+	return false;
+}
+
+void bes2600_dynamic_opt_rxtx(struct bes2600_common *hw_priv, struct bes2600_vif *priv, int rssi)
+{
+	u32 succPro = 0, tx_cnt, tx_retry, rx_cnt, tx_fail;
+	static u32 l_tx_cnt = 0, l_tx_fail = 0, l_tx_retry = 0, l_rx_cnt = 0;
+	static u32 tx_bps = 0, rx_bps = 0;
+	u32 total_kbps = 0;
+	static int level;
+	int rts_value = 2347;
+	int short_gi = 0;
+
+	/* calculate real time throughput */
+	if (hw_priv == NULL || priv == NULL) {
+		return;
+	}
+	tx_bps = abs (priv->dot11TransmittedDataBytes - tx_bps);
+	rx_bps = abs (priv->dot11ReceivedDataBytes  - rx_bps);
+	total_kbps = (tx_bps / 128 + rx_bps / 128);
+
+	total_kbps *= 1000;
+	total_kbps /= TXRX_OPT_PEROID;
+
+	/*  if tx/rx < 100k/s, close*/
+	if (total_kbps < 100) {
+		level = 0;
+		goto txrx_opt_clear;
+	}
+
+	/* calculate tx_cnt, tx_retry, rx_cnt */
+	tx_cnt = (priv->dot11TransmittedFrameCount - l_tx_cnt);
+	tx_fail = (priv->dot11FailedCount - l_tx_fail);
+	tx_retry = (priv->dot11RetryCount - l_tx_retry);
+	rx_cnt = (priv->dot11ReceivedFragmentCount - l_rx_cnt);
+	( (tx_cnt + tx_retry) > 0 ) ? (succPro = tx_cnt * 100 / (tx_cnt + tx_retry)) : (succPro = 0);
+
+	if (succPro != 0) {
+		/* hw value 21 is mcs7, dynamic set short GI */
+		if (priv->hw_value == 21 && succPro > 90)
+			short_gi = 1;
+		else
+			short_gi = 0;
+
+		/* dynamic set RTS/CTS */
+		if (succPro  < 80)
+			rts_value = 512;
+	}
+
+	/* dynamic set edca param */
+	if (succPro != 0) {
+		if (bes2600_station_is_ap_ht40(hw_priv)) {
+			if (bes2600_get_tx_delta_time() > 8 || bes2600_get_tx_av_max_delta_time() > 8) {
+				if (level < 4)
+					level++;
+			} else {
+				if (level > 0)
+					level--;
+			}
+			/* high throughput force level = 0 */
+			if (total_kbps > TXRX_HIGH_TP_THRESHOLD_5G && level > 0 && priv->hw_value > 19) {
+				level = 0;
+			}
+
+		} else {//shiled room 13, office 8
+			if (bes2600_get_tx_delta_time() > (TXRX_HIGH_TP_DELTA_TIME_5G + total_kbps / 8000)
+			    || bes2600_get_tx_av_max_delta_time() > (TXRX_HIGH_TP_DELTA_TIME_5G + total_kbps / 8000)) {
+				if (level < 4)
+					level++;
+			} else {
+				if (level > 0)
+					level--;
+			}
+			/* high throughput force level = 0 */
+			if (total_kbps > TXRX_HIGH_TP_THRESHOLD_2G4 && level > 0) {
+				level = 0;
+			}
+
+		}
+	}
+#if TXRX_OPT_CLOSE_EDCA
+	level = 0;
+#endif
+	if (level > TXRX_OPT_EDCA_MAX_LEVEL)
+		level = TXRX_OPT_EDCA_MAX_LEVEL;
+
+	bes2600_dbg(BES2600_DBG_TXRX_OPT, "txrx_opt: tx(cnt=%d retry=%d psr=%d tx_fail=%d (wsm rts=%d shortgi=%d level=%d) tx=%dk/s)\n\r",
+	       tx_cnt, tx_retry, succPro, tx_fail, rts_value, short_gi, level, tx_bps / 128);
+	bes2600_dbg(BES2600_DBG_TXRX_OPT, "txrx_opt: rx(cnt=%d  rx=%dk/s) total=%dk/s\n\r", rx_cnt, rx_bps / 128, total_kbps);
+	bes2600_dbg(BES2600_DBG_TXRX_OPT, "txrx_opt: tx_delta_time=%d [%d %d %d %d] hw_value=%d ht=%d maxtxcnt=%d\n\r",
+	       bes2600_get_tx_delta_time(), bes2600_get_tx_ac_delta_time(0), bes2600_get_tx_ac_delta_time(1),
+	       bes2600_get_tx_ac_delta_time(2), bes2600_get_tx_ac_delta_time(3), priv->hw_value,
+	       bes2600_station_is_ap_ht40(hw_priv), hw_priv->long_frame_max_tx_count);
+
+
+	/* dynamic set cca */
+	bes2600_set_cca_method(hw_priv, priv, 0);
+	/* dynamic set agc */
+	bes2600_set_dynamic_agc(hw_priv, priv, 0);
+txrx_opt_clear:
+	bes2600_set_rts_threshold(priv->hw, rts_value);
+	bes2600_enable_tx_shortgi(hw_priv, priv, short_gi);
+	bes2600_set_high_edca_params(hw_priv, priv, level);
+	bes2600_clear_tx_delta_time();
+	bes2600_clear_tx_ac_delta_time(0);
+	bes2600_clear_tx_ac_delta_time(1);
+	bes2600_clear_tx_ac_delta_time(2);
+	bes2600_clear_tx_ac_delta_time(3);
+	tx_bps = priv->dot11TransmittedDataBytes;
+	rx_bps = priv->dot11ReceivedDataBytes;
+	l_tx_cnt = priv->dot11TransmittedFrameCount;
+	l_tx_fail = priv->dot11FailedCount;
+	l_tx_retry = priv->dot11RetryCount;
+	l_rx_cnt = priv->dot11ReceivedFragmentCount;
+	return ;
+}
+
+static struct bes2600_common *txrx_hw_priv = NULL;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,15,0)
+static void txrx_opt_timer_callback(struct timer_list* data)
+#else
+static void txrx_opt_timer_callback(unsigned long data)
+#endif
+{
+	bes2600_dbg(BES2600_DBG_TXRX, "####Timer callback function Called time = %lu\n", jiffies);
+	queue_work(txrx_hw_priv->workqueue, &txrx_hw_priv->dynamic_opt_txrx_work);
+	mod_timer(&txrx_hw_priv->txrx_opt_timer, jiffies + msecs_to_jiffies(TXRX_OPT_PEROID));
+}
+
+static void txrx_opt_timer_start(struct bes2600_common *hw_priv)
+{
+	mod_timer(&hw_priv->txrx_opt_timer, jiffies + msecs_to_jiffies(TXRX_OPT_PEROID));
+}
+
+static void txrx_opt_timer_stop(struct bes2600_common *hw_priv)
+{
+	del_timer_sync(&hw_priv->txrx_opt_timer);
+}
+
+int bes2600_set_txrx_opt_default_param(struct bes2600_common * hw_priv)
+{
+	MIB_TXRX_OPT_PARAM g_txrx_param = {2, (PROCTECT_MODE_RTS_CTS | PROCTECT_MODE_RTS_CTS_RETRY), 2002};
+	struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
+	if (priv == NULL)
+		return 0;
+	memcpy(&hw_priv->txrx_opt_param, &g_txrx_param, sizeof(MIB_TXRX_OPT_PARAM));
+	bes2600_set_txrx_opt_param(hw_priv, priv, &hw_priv->txrx_opt_param);
+	return 0;
+}
+
+int bes2600_set_txrx_opt_unjoin_param(struct bes2600_common * hw_priv)
+{
+	MIB_TXRX_OPT_PARAM g_txrx_param = {1, 0, 2002};
+	struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
+	if (priv == NULL)
+		return 0;
+	memcpy(&hw_priv->txrx_opt_param, &g_txrx_param, sizeof(MIB_TXRX_OPT_PARAM));
+	bes2600_set_txrx_opt_param(hw_priv, priv, &hw_priv->txrx_opt_param);
+	return 0;
+}
+
+int txrx_opt_timer_init(struct bes2600_common *hw_priv)
+{
+	bes2600_info(BES2600_DBG_TXRX_OPT, "txrx_opt_timer_init:%p", txrx_hw_priv);
+	if (!txrx_hw_priv) {
+		txrx_hw_priv = hw_priv;
+		bes2600_info(BES2600_DBG_TXRX, "####Timer init hw_priv = %p\n", txrx_hw_priv);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0)
+		timer_setup(&hw_priv->txrx_opt_timer, txrx_opt_timer_callback, 0);
+#else
+		init_timer(&hw_priv->txrx_opt_timer);
+		hw_priv->txrx_opt_timer.function = txrx_opt_timer_callback;
+		hw_priv->txrx_opt_timer.data = 0;
+#endif
+		bes2600_set_txrx_opt_default_param(hw_priv);
+	}
+
+	mod_timer(&hw_priv->txrx_opt_timer, jiffies + msecs_to_jiffies(TXRX_OPT_PEROID));
+	bes2600_pwr_register_en_lp_cb(hw_priv, txrx_opt_timer_stop);
+	bes2600_pwr_register_exit_lp_cb(hw_priv, txrx_opt_timer_start);
+	return 0;
+}
+
+void txrx_opt_timer_exit(struct bes2600_common *hw_priv)
+{
+	bes2600_info(BES2600_DBG_TXRX_OPT, "txrx_opt_timer_exit");
+
+	del_timer_sync(&hw_priv->txrx_opt_timer);
+	cancel_work_sync(&hw_priv->dynamic_opt_txrx_work);
+	bes2600_pwr_unregister_en_lp_cb(hw_priv, txrx_opt_timer_stop);
+	bes2600_pwr_unregister_exit_lp_cb(hw_priv, txrx_opt_timer_start);
+	txrx_hw_priv = NULL;
+	bes2600_set_txrx_opt_unjoin_param(hw_priv);
+}
+
+
diff --git a/drivers/staging/bes2600/txrx_opt.h b/drivers/staging/bes2600/txrx_opt.h
new file mode 100644
index 000000000000..582ff7fb5221
--- /dev/null
+++ b/drivers/staging/bes2600/txrx_opt.h
@@ -0,0 +1,31 @@
+
+/***************************************************************************
+ *
+ * Copyright 2015-2022 BES.
+ * All rights reserved. All unpublished rights reserved.
+ *
+ * No part of this work may be used or reproduced in any form or by any
+ * means, or stored in a database or retrieval system, without prior written
+ * permission of BES.
+ *
+ * Use of this work is governed by a license granted by BES.
+ * This work contains confidential and proprietary information of
+ * BES. which is protected by copyright, trade secret,
+ * trademark and other intellectual property rights.
+ *
+ ****************************************************************************/
+#ifndef bes2600_TXRX_OPT_H
+#define bes2600_TXRX_OPT_H
+#include <linux/list.h>
+/* open it for enhance wifi throughput */
+#define BES2600_TX_RX_OPT   1
+void bes2600_add_tx_ac_delta_time(int ac, uint32_t del_time);
+void bes2600_add_tx_delta_time(uint32_t tx_time);
+void bes2600_rx_status(struct bes2600_vif *priv, struct sk_buff *skb);
+void bes2600_tx_status(struct bes2600_vif *priv, struct sk_buff *skb);
+void bes2600_dynamic_opt_rxtx(struct bes2600_common *hw_priv,struct bes2600_vif *priv, int rssi);
+int txrx_opt_timer_init(struct bes2600_common *hw_priv);
+int txrx_opt_timer_exit(struct bes2600_common *hw_priv);
+
+#endif
+
diff --git a/drivers/staging/bes2600/wifi_testmode_cmd.c b/drivers/staging/bes2600/wifi_testmode_cmd.c
new file mode 100644
index 000000000000..3b3583a6db3d
--- /dev/null
+++ b/drivers/staging/bes2600/wifi_testmode_cmd.c
@@ -0,0 +1,143 @@
+/*
+ * Mac80211 driver for BES2600 device
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifdef CONFIG_BES2600_TESTMODE
+#include <net/netlink.h>
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+#include <linux/time.h>
+#include <linux/string.h>
+
+#include "wsm.h"
+#include "bes2600.h"
+#include "bes_nl80211_testmode_msg.h"
+#include "bes2600_driver_mode.h"
+#include "bes_chardev.h"
+#include "bes2600_factory.h"
+
+
+/* store the reply message of the rf cmd */
+static struct vendor_rf_cmd_msg_reply vendor_rf_cmd_reply;
+
+extern int bes2600_testmode_reply(struct wiphy *wiphy, const void *data, int len);
+
+void bes2600_rf_cmd_msg_assembly(u32 cmd_type, void *data, u32 msg_len)
+{
+	vendor_rf_cmd_reply.id = cmd_type;
+	vendor_rf_cmd_reply.msg_len = msg_len + 2 * sizeof(u32);
+
+	if (msg_len)
+		memcpy(vendor_rf_cmd_reply.ret_msg, (u8 *)data, msg_len);
+	else
+		vendor_rf_cmd_reply.ret_msg[0] = '\0';
+
+}
+
+/**
+ * bes2600_vendor_rf_cmd, signaling cmd & rf nosignaling cmd
+ * reaches bes2600
+ *
+ * @hw: the hardware
+ * @vif: vif
+ * @data: incoming data
+ * @len: incoming data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int bes2600_vendor_rf_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len)
+{
+	struct bes2600_common *hw_priv = hw->priv;
+	int ret = 0;
+	int recv_timeout = 1500; /*waiting for reply message*/
+	struct vendor_rf_cmd_t *vendor_rf_cmd = (struct vendor_rf_cmd_t *)data;
+	//struct timeval tstart, tend;
+	int if_id = 0;
+	u32 count, cmd_len, cmd_type;
+
+	count = vendor_rf_cmd->cmd_argc;
+	cmd_len = vendor_rf_cmd->cmd_len;
+	cmd_type = vendor_rf_cmd->cmd_type;
+
+	if (count == 0 || cmd_len == 0 ||
+		strlen(vendor_rf_cmd->cmd) + 1 != cmd_len) {
+		bes2600_err(BES2600_DBG_TEST_MODE, "%s line: %d, "
+			"vendor rf cmd parsing failed\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	bes2600_info(BES2600_DBG_TEST_MODE, "vendor cmd = %s\n", vendor_rf_cmd->cmd);
+
+
+	/**
+	 * signaling mode does not support the saving of calibration files,
+	 * but the saved calibration values can be read.
+	 *
+	 */
+	if (bes2600_chrdev_is_signal_mode()) {
+		if (cmd_type == VENDOR_RF_SAVE_FREQOFFSET_CMD ||
+			cmd_type == VENDOR_RF_SAVE_POWERLEVEL_CMD ||
+			cmd_type == VENDOR_RF_POWER_CALIB_FINISH)
+			return -EOPNOTSUPP;
+	}
+
+	if (cmd_type == VENDOR_RF_SIG_NOSIG_MIX) {
+		if (bes2600_chrdev_is_signal_mode()) {
+			vendor_rf_cmd->cmd_type = VENDOR_RF_SIGNALING_CMD;
+			cmd_type = VENDOR_RF_SIGNALING_CMD;
+		} else {
+			vendor_rf_cmd->cmd_type = VENDOR_RF_NOSIGNALING_CMD;
+			cmd_type = VENDOR_RF_NOSIGNALING_CMD;
+		}
+	}
+
+	switch (cmd_type) {
+	case VENDOR_RF_POWER_CALIB_FINISH:
+	case VENDOR_RF_GET_SAVE_FREQOFFSET_CMD:
+	case VENDOR_RF_GET_SAVE_POWERLEVEL_CMD:
+	case VENDOR_RF_SIGNALING_CMD:
+	case VENDOR_RF_NOSIGNALING_CMD:
+	case VENDOR_RF_SAVE_FREQOFFSET_CMD:
+	case VENDOR_RF_SAVE_POWERLEVEL_CMD:
+		sema_init(&hw_priv->vendor_rf_cmd_replay_sema, 0);
+		ret = wsm_vendor_rf_cmd(hw_priv, if_id, vendor_rf_cmd);
+
+		if (ret) {
+			bes2600_err(BES2600_DBG_TEST_MODE, "vendor rf cmd send error code = %d\n", ret);
+			return ret;
+		}
+
+		//do_gettimeofday(&tstart);
+		if (down_timeout(&hw_priv->vendor_rf_cmd_replay_sema, recv_timeout)) {
+			bes2600_err(BES2600_DBG_TEST_MODE, "vendor rf cmd failed to receive reply message\n");
+			return -ENOMSG;
+		}
+		//do_gettimeofday(&tend);
+		//bes2600_dbg(BES2600_DBG_TEST_MODE,"recv time: %ldms\n",
+		//1000 * (tend.tv_sec - tstart.tv_sec) + (tend.tv_usec - tstart.tv_usec) / 1000);
+
+		ret = bes2600_testmode_reply(hw->wiphy, &vendor_rf_cmd_reply, vendor_rf_cmd_reply.msg_len);
+		vendor_rf_cmd_reply.ret_msg[0] = '\0';
+		break;
+	case VENDOR_RF_SIG_NOSIG_MIX:
+		ret = 0;
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_BES2600_TESTMODE */
diff --git a/drivers/staging/bes2600/wsm.c b/drivers/staging/bes2600/wsm.c
new file mode 100644
index 000000000000..8ba5ce0a0bb3
--- /dev/null
+++ b/drivers/staging/bes2600/wsm.c
@@ -0,0 +1,3132 @@
+/*
+ * WSM host interface (HI) implementation for
+ * BES2600 mac80211 drivers.
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+#include <linux/etherdevice.h>
+
+#include "bes2600.h"
+#include "wsm.h"
+#include "bh.h"
+#include "debug.h"
+#include "itp.h"
+#ifdef ROAM_OFFLOAD
+#include "sta.h"
+#endif /*ROAM_OFFLOAD*/
+#ifdef CONFIG_BES2600_TESTMODE
+#include "bes_nl80211_testmode_msg.h"
+#endif
+#include "bes2600_driver_mode.h"
+#include "bes_chardev.h"
+#include "bes2600_factory.h"
+#include "epta_coex.h"
+#include "epta_request.h"
+
+#define WSM_CMD_TIMEOUT		(6 * HZ) /* With respect to interrupt loss */
+#define WSM_CMD_JOIN_TIMEOUT	(7 * HZ) /* Join timeout is 5 sec. in FW   */
+#define WSM_CMD_START_TIMEOUT	(7 * HZ)
+#define WSM_CMD_RESET_TIMEOUT	(7 * HZ) /* 2 sec. timeout was observed.   */
+#define WSM_CMD_DEFAULT_TIMEOUT	(7 * HZ)
+#define WSM_SKIP(buf, size)						\
+	do {								\
+		if (unlikely((buf)->data + size > (buf)->end))		\
+			goto underflow;					\
+		(buf)->data += size;					\
+	} while (0)
+
+#define WSM_GET(buf, ptr, size)						\
+	do {								\
+		if (unlikely((buf)->data + size > (buf)->end))		\
+			goto underflow;					\
+		memcpy(ptr, (buf)->data, size);				\
+		(buf)->data += size;					\
+	} while (0)
+
+#define __WSM_GET(buf, type, cvt)					\
+	({								\
+		type val;						\
+		if (unlikely((buf)->data + sizeof(type) > (buf)->end))	\
+			goto underflow;					\
+		val = cvt(*(type *)(buf)->data);			\
+		(buf)->data += sizeof(type);				\
+		val;							\
+	})
+
+#define WSM_GET8(buf)  __WSM_GET(buf, u8, (u8))
+#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu)
+#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu)
+
+#define WSM_PUT(buf, ptr, size)						\
+	do {								\
+		if (unlikely((buf)->data + size > (buf)->end))		\
+			if (unlikely(wsm_buf_reserve((buf), size)))	\
+				goto nomem;				\
+		memcpy((buf)->data, ptr, size);				\
+		(buf)->data += size;					\
+	} while (0)
+
+#define __WSM_PUT(buf, val, type, cvt)					\
+	do {								\
+		if (unlikely((buf)->data + sizeof(type) > (buf)->end))	\
+			if (unlikely(wsm_buf_reserve((buf), sizeof(type)))) \
+				goto nomem;				\
+		*(type *)(buf)->data = cvt(val);			\
+		(buf)->data += sizeof(type);				\
+	} while (0)
+
+#define WSM_PUT8(buf, val)  __WSM_PUT(buf, val, u8, (u8))
+#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16)
+#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32)
+
+static void wsm_buf_reset(struct wsm_buf *buf);
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size);
+static int get_interface_id_scanning(struct bes2600_common *hw_priv);
+
+static int wsm_cmd_send(struct bes2600_common *hw_priv,
+			struct wsm_buf *buf,
+			void *arg, u16 cmd, long tmo, int if_id);
+
+static struct bes2600_vif
+	*wsm_get_interface_for_tx(struct bes2600_common *hw_priv);
+
+static inline void wsm_cmd_lock(struct bes2600_common *hw_priv)
+{
+	bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_WSM_TX);
+	down(&hw_priv->wsm_cmd_sema);
+}
+
+static inline void wsm_cmd_unlock(struct bes2600_common *hw_priv)
+{
+	up(&hw_priv->wsm_cmd_sema);
+	bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_WSM_TX);
+}
+
+static inline void wsm_oper_lock(struct bes2600_common *hw_priv)
+{
+	bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_WSM_OPER);
+	down(&hw_priv->wsm_oper_lock);
+}
+
+static inline void wsm_oper_unlock(struct bes2600_common *hw_priv)
+{
+	up(&hw_priv->wsm_oper_lock);
+	bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_WSM_OPER);
+}
+
+/* ******************************************************************** */
+/* WSM API implementation						*/
+
+static int wsm_generic_confirm(struct bes2600_common *hw_priv,
+				 void *arg,
+				 struct wsm_buf *buf)
+{
+	u32 status = WSM_GET32(buf);
+	if (WARN(status != WSM_STATUS_SUCCESS, "wsm_generic_confirm ret %u", status))
+		return -EINVAL;
+	return 0;
+
+underflow:
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+int wsm_configuration(struct bes2600_common *hw_priv,
+			  struct wsm_configuration *arg,
+			  int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime);
+	WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime);
+	WSM_PUT32(buf, arg->dot11RtsThreshold);
+
+	/* DPD block. */
+	WSM_PUT16(buf, arg->dpdData_size + 12);
+	WSM_PUT16(buf, 1); /* DPD version */
+	WSM_PUT(buf, arg->dot11StationId, ETH_ALEN);
+	WSM_PUT16(buf, 5); /* DPD flags */
+	WSM_PUT(buf, arg->dpdData, arg->dpdData_size);
+
+	ret = wsm_cmd_send(hw_priv, buf, arg, 0x0009, WSM_CMD_TIMEOUT, if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+static int wsm_configuration_confirm(struct bes2600_common *hw_priv,
+					 struct wsm_configuration *arg,
+					 struct wsm_buf *buf)
+{
+	int i;
+	int status;
+
+	status = WSM_GET32(buf);
+	if (WARN_ON(status != WSM_STATUS_SUCCESS))
+		return -EINVAL;
+
+	if (bes2600_chrdev_is_signal_mode()) {
+		WSM_GET(buf, arg->dot11StationId, ETH_ALEN);
+		arg->dot11FrequencyBandsSupported = WSM_GET8(buf);
+		WSM_SKIP(buf, 1);
+		arg->supportedRateMask = WSM_GET32(buf);
+		for (i = 0; i < 2; ++i) {
+			arg->txPowerRange[i].min_power_level = WSM_GET32(buf);
+			arg->txPowerRange[i].max_power_level = WSM_GET32(buf);
+			arg->txPowerRange[i].stepping = WSM_GET32(buf);
+		}
+	}
+	return 0;
+
+underflow:
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+//rf nosignaling tset
+#ifdef CONFIG_BES2600_TESTMODE
+
+int wsm_vendor_rf_cmd_confirm(struct bes2600_common *hw_priv, void *arg, struct wsm_buf *buf)
+{
+	return 0;
+}
+
+int wsm_vendor_rf_test_indication(struct bes2600_common *hw_priv, struct wsm_buf *buf)
+{
+	int i;
+	int16_t ret = 0;
+	u16 wsm_len;
+	u32 cmd_type;
+	struct wifi_power_cali_save_t power_cali_save;
+	struct wifi_freq_cali_t wifi_freq_cali;
+	struct wifi_get_power_cali_t power_cali_get;
+	struct wifi_power_cali_flag_t power_cali_flag;
+	struct wsm_mcu_hdr *msg_hdr = (struct wsm_mcu_hdr *)(buf->begin);
+
+	wsm_len = __le16_to_cpu(msg_hdr->hdr.len);
+	cmd_type = __le32_to_cpu(msg_hdr->cmd_type);
+	buf->data += sizeof(struct wsm_mcu_hdr) - sizeof(struct wsm_hdr);
+
+	switch (cmd_type) {
+	case VENDOR_RF_SAVE_FREQOFFSET_CMD:
+	case VENDOR_RF_GET_SAVE_FREQOFFSET_CMD:
+		wifi_freq_cali.save_type = WSM_GET16(buf);
+		wifi_freq_cali.freq_cali = WSM_GET16(buf);
+		wifi_freq_cali.status = -WSM_GET16(buf);
+		wifi_freq_cali.cali_flag = WSM_GET16(buf);
+		if (wifi_freq_cali.save_type == RF_CALIB_DATA_IN_LINUX) {
+			if (cmd_type == VENDOR_RF_SAVE_FREQOFFSET_CMD) {
+#ifdef CONFIG_BES2600_CALIB_FROM_LINUX
+				ret = bes2600_wifi_cali_freq_write(&wifi_freq_cali);
+#else
+				ret = -FACTORY_SAVE_FREQ_ERR;
+#endif
+			}
+
+			if (cmd_type == VENDOR_RF_GET_SAVE_FREQOFFSET_CMD) {
+#ifdef CONFIG_BES2600_CALIB_FROM_LINUX
+				ret = vendor_get_freq_cali(&wifi_freq_cali);
+#else
+				ret = -FACTORY_SAVE_FILE_NOT_EXIST;
+#endif
+			}
+			wifi_freq_cali.status = ret;
+		}
+		bes2600_rf_cmd_msg_assembly(cmd_type, &wifi_freq_cali,
+			sizeof(struct wifi_freq_cali_t));
+		break;
+	case VENDOR_RF_SAVE_POWERLEVEL_CMD:
+		power_cali_save.save_type = WSM_GET16(buf);
+		power_cali_save.mode = WSM_GET16(buf);
+		power_cali_save.bandwidth = WSM_GET16(buf);
+		power_cali_save.band = WSM_GET16(buf);
+		power_cali_save.ch = WSM_GET16(buf);
+		power_cali_save.power_cali = WSM_GET16(buf);
+		power_cali_save.status = -WSM_GET16(buf);
+		if (power_cali_save.save_type == RF_CALIB_DATA_IN_LINUX) {
+#ifdef CONFIG_BES2600_CALIB_FROM_LINUX
+			ret = bes2600_wifi_power_cali_table_write(&power_cali_save);
+#else
+			ret = -FACTORY_SAVE_POWER_ERR;
+#endif
+			power_cali_save.status = ret;
+		}
+		bes2600_rf_cmd_msg_assembly(cmd_type, &power_cali_save,
+			sizeof(struct wifi_power_cali_save_t));
+		break;
+	case VENDOR_RF_GET_SAVE_POWERLEVEL_CMD:
+		power_cali_get.save_type = WSM_GET16(buf);
+		if (power_cali_get.save_type == RF_CALIB_DATA_IN_LINUX) {
+#ifdef CONFIG_BES2600_CALIB_FROM_LINUX
+			ret = vendor_get_power_cali(&power_cali_get);
+#else
+			ret = -FACTORY_SAVE_FILE_NOT_EXIST;
+#endif
+			power_cali_get.status = ret;
+		} else {
+			/* 2.4G have 3 cali ch */
+			for (i = 0; i < 3; i++)
+				power_cali_get.tx_power_ch[i] = WSM_GET16(buf);
+			/* 5G have 13 cali ch */
+			for (i = 0; i < 13; i++)
+				power_cali_get.tx_power_ch_5G[i] = WSM_GET16(buf);
+			power_cali_get.status = -WSM_GET16(buf);
+		}
+		bes2600_rf_cmd_msg_assembly(cmd_type, &power_cali_get,
+			sizeof(struct wifi_get_power_cali_t));
+
+		break;
+	case VENDOR_RF_POWER_CALIB_FINISH:
+		power_cali_flag.save_type = WSM_GET16(buf);
+		power_cali_flag.band = WSM_GET16(buf);
+		power_cali_flag.status = -WSM_GET16(buf);
+		if (power_cali_flag.save_type == RF_CALIB_DATA_IN_LINUX) {
+#ifdef CONFIG_BES2600_CALIB_FROM_LINUX
+			ret = vendor_set_power_cali_flag(&power_cali_flag);
+#else
+			ret = -FACTORY_SET_POWER_CALI_FLAG_ERR;
+#endif
+			power_cali_flag.status = ret;
+		}
+		bes2600_rf_cmd_msg_assembly(cmd_type, &power_cali_flag,
+			sizeof(struct wifi_power_cali_flag_t));
+		break;
+	case VENDOR_RF_SIGNALING_CMD:
+	case VENDOR_RF_NOSIGNALING_CMD:
+		bes2600_rf_cmd_msg_assembly(cmd_type, buf->data, wsm_len - sizeof(struct wsm_mcu_hdr));
+		break;
+	default:
+		break;
+	}
+
+	up(&hw_priv->vendor_rf_cmd_replay_sema);
+
+	bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_TEST_CMD);
+
+	return 0;
+
+underflow:
+	return -EINVAL;
+}
+
+int wsm_vendor_rf_cmd(struct bes2600_common *hw_priv, int if_id,
+					  const struct vendor_rf_cmd_t *vendor_rf_cmd)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	/* the command need to wait complete indication */
+	bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_TEST_CMD);
+
+	WSM_PUT32(buf, vendor_rf_cmd->cmd_type);
+	WSM_PUT32(buf, vendor_rf_cmd->cmd_argc);
+	WSM_PUT32(buf, vendor_rf_cmd->cmd_len);
+	WSM_PUT(buf, vendor_rf_cmd->cmd, vendor_rf_cmd->cmd_len);
+
+	/**
+	 * vendor signaling and nosignaling use id 0x0C25.
+	 */
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0C25, WSM_CMD_TIMEOUT, if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+#endif /* CONFIG_BES2600_TESTMODE */
+
+/* ******************************************************************** */
+ // wifi cpu sleep control
+ #ifdef BES_UNIFIED_PM
+struct wsm_sleep_ctrl {
+	u16 msgid;
+	u16 msglen;
+	u32 disable;
+};
+
+int wsm_sleep_ctrl(struct bes2600_common *hw_priv, u32 disable, int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	if (if_id != 0) {
+		WARN_ON(1);
+		return -EBUSY;
+	}
+
+	wsm_cmd_lock(hw_priv);
+	WSM_PUT32(buf, disable);
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0024, WSM_CMD_TIMEOUT, if_id);
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+#endif
+
+/* ******************************************************************** */
+
+int wsm_reset(struct bes2600_common *hw_priv, const struct wsm_reset *arg,
+		int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+	u16 cmd = 0x000A | WSM_TX_LINK_ID(arg->link_id);
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT32(buf, arg->reset_statistics ? 0 : 1);
+	ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT,
+				if_id);
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_read_mib(struct bes2600_common *hw_priv, u16 mibId, void *_buf,
+			size_t buf_size)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+	struct wsm_mib mib_buf = {
+		.mibId = mibId,
+		.buf = _buf,
+		.buf_size = buf_size,
+	};
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT16(buf, mibId);
+	WSM_PUT16(buf, 0);
+
+	ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0005, WSM_CMD_TIMEOUT, -1);
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+static int wsm_read_mib_confirm(struct bes2600_common *hw_priv,
+				struct wsm_mib *arg,
+				struct wsm_buf *buf)
+{
+	u16 size;
+	if (WARN_ON(WSM_GET32(buf) != WSM_STATUS_SUCCESS))
+		return -EINVAL;
+
+	if (WARN_ON(WSM_GET16(buf) != arg->mibId))
+		return -EINVAL;
+
+	size = WSM_GET16(buf);
+	if (size > arg->buf_size)
+		size = arg->buf_size;
+
+	WSM_GET(buf, arg->buf, size);
+	arg->buf_size = size;
+	return 0;
+
+underflow:
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+int wsm_write_mib(struct bes2600_common *hw_priv, u16 mibId, void *_buf,
+			size_t buf_size, int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+	struct wsm_mib mib_buf = {
+		.mibId = mibId,
+		.buf = _buf,
+		.buf_size = buf_size,
+	};
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT16(buf, mibId);
+	WSM_PUT16(buf, buf_size);
+	WSM_PUT(buf, _buf, buf_size);
+
+	ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0006, WSM_CMD_TIMEOUT,
+			if_id);
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+static int wsm_write_mib_confirm(struct bes2600_common *hw_priv,
+				struct wsm_mib *arg,
+				struct wsm_buf *buf,
+				int interface_link_id)
+{
+	int ret;
+	struct bes2600_vif *priv;
+
+	if (!is_hardware_cw1250(hw_priv) || is_hardware_cw1260(hw_priv))
+		interface_link_id = 0;
+
+	ret = wsm_generic_confirm(hw_priv, arg, buf);
+	if (ret)
+		return ret;
+
+	if (arg->mibId == 0x1006) {
+		const char *p = arg->buf;
+
+		/* Power save is enabled before add_interface is called */
+		if (!hw_priv->vif_list[interface_link_id])
+			return 0;
+		/* OperationalMode: update PM status. */
+		priv = cw12xx_hwpriv_to_vifpriv(hw_priv,
+					interface_link_id);
+		if (!priv)
+			return 0;
+		bes2600_enable_powersave(priv,
+				(p[0] & 0x0F) ? true : false);
+		spin_unlock(&priv->vif_lock);
+	}
+	return 0;
+}
+
+/* ******************************************************************** */
+
+int wsm_scan(struct bes2600_common *hw_priv, const struct wsm_scan *arg,
+		int if_id)
+{
+	int i;
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	if (unlikely(arg->numOfChannels > 48))
+		return -EINVAL;
+
+	if (unlikely(arg->numOfSSIDs > WSM_SCAN_MAX_NUM_OF_SSIDS))
+		return -EINVAL;
+
+	if (unlikely(arg->band > 1))
+		return -EINVAL;
+
+	wsm_oper_lock(hw_priv);
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT8(buf, arg->band);
+	WSM_PUT8(buf, arg->scanType);
+	WSM_PUT8(buf, arg->scanFlags);
+	WSM_PUT8(buf, arg->maxTransmitRate);
+	WSM_PUT32(buf, arg->autoScanInterval);
+	WSM_PUT8(buf, arg->numOfProbeRequests);
+	WSM_PUT8(buf, arg->numOfChannels);
+	WSM_PUT8(buf, arg->numOfSSIDs);
+	WSM_PUT8(buf, arg->probeDelay);
+
+	for (i = 0; i < arg->numOfChannels; ++i) {
+		WSM_PUT16(buf, arg->ch[i].number);
+		WSM_PUT16(buf, 0);
+		WSM_PUT32(buf, arg->ch[i].minChannelTime);
+		WSM_PUT32(buf, arg->ch[i].maxChannelTime);
+		WSM_PUT32(buf, 0);
+	}
+
+	for (i = 0; i < arg->numOfSSIDs; ++i) {
+		WSM_PUT32(buf, arg->ssids[i].length);
+		WSM_PUT(buf, &arg->ssids[i].ssid[0],
+				sizeof(arg->ssids[i].ssid));
+	}
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0007, WSM_CMD_TIMEOUT,
+			   if_id);
+	wsm_cmd_unlock(hw_priv);
+	if (ret)
+		wsm_oper_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	wsm_oper_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_scan(struct bes2600_common *hw_priv, int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+	wsm_cmd_lock(hw_priv);
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0008, WSM_CMD_TIMEOUT,
+			   if_id);
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+}
+
+
+static int wsm_tx_confirm(struct bes2600_common *hw_priv,
+			  struct wsm_buf *buf,
+			  int interface_link_id)
+{
+	struct wsm_tx_confirm tx_confirm;
+
+	tx_confirm.packetID = WSM_GET32(buf);
+	tx_confirm.status = WSM_GET32(buf);
+	tx_confirm.txedRate = WSM_GET8(buf);
+	tx_confirm.ackFailures = WSM_GET8(buf);
+	tx_confirm.flags = WSM_GET16(buf);
+	tx_confirm.mediaDelay = WSM_GET32(buf);
+	tx_confirm.txQueueDelay = WSM_GET32(buf);
+
+	if (is_hardware_cw1250(hw_priv) || is_hardware_cw1260(hw_priv)) {
+		/* TODO:COMBO:linkID will be stored in packetID*/
+		/* TODO:COMBO: Extract traffic resumption map */
+		tx_confirm.if_id = bes2600_queue_get_if_id(tx_confirm.packetID);
+		tx_confirm.link_id = bes2600_queue_get_link_id(
+				tx_confirm.packetID);
+	} else {
+		tx_confirm.link_id = interface_link_id;
+		tx_confirm.if_id = 0;
+	}
+
+	wsm_release_vif_tx_buffer(hw_priv, tx_confirm.if_id, 1);
+
+	if (hw_priv->wsm_cbc.tx_confirm)
+		hw_priv->wsm_cbc.tx_confirm(hw_priv, &tx_confirm);
+	return 0;
+
+underflow:
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+static int wsm_multi_tx_confirm(struct bes2600_common *hw_priv,
+				struct wsm_buf *buf, int interface_link_id)
+{
+	struct bes2600_vif *priv;
+	int ret;
+	int count;
+	int i;
+
+	count = WSM_GET32(buf);
+	if (WARN_ON(count <= 0))
+		return -EINVAL;
+	else if (count > 1) {
+		ret = wsm_release_tx_buffer(hw_priv, count - 1);
+		if (ret < 0)
+			return ret;
+		else if (ret > 0)
+			bes2600_bh_wakeup(hw_priv);
+	}
+	priv = cw12xx_hwpriv_to_vifpriv(hw_priv, interface_link_id);
+	if (priv) {
+		bes2600_debug_txed_multi(priv, count);
+		spin_unlock(&priv->vif_lock);
+	}
+	for (i = 0; i < count; ++i) {
+		if(i < count - 1)
+			bes2600_bh_dec_pending_count(hw_priv, 0);
+
+		ret = wsm_tx_confirm(hw_priv, buf, interface_link_id);
+		if (ret)
+			return ret;
+	}
+	return ret;
+
+underflow:
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+static int wsm_join_confirm(struct bes2600_common *hw_priv,
+				struct wsm_join *arg,
+				struct wsm_buf *buf)
+{
+	u32 status = WSM_GET32(buf);
+
+	wsm_oper_unlock(hw_priv);
+
+	if (status != WSM_STATUS_SUCCESS) {
+		bes2600_warn(BES2600_DBG_WSM, "wsm_join_confirm ret %u\n", status);
+		return -EINVAL;
+	}
+
+	arg->minPowerLevel = WSM_GET32(buf);
+	arg->maxPowerLevel = WSM_GET32(buf);
+
+	return 0;
+
+underflow:
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+int wsm_join(struct bes2600_common *hw_priv, struct wsm_join *arg,
+		 int if_id)
+/*TODO: combo: make it work per vif.*/
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_oper_lock(hw_priv);
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT8(buf, arg->mode);
+	WSM_PUT8(buf, arg->band);
+	WSM_PUT16(buf, arg->channelNumber);
+	WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid));
+	WSM_PUT16(buf, arg->atimWindow);
+	WSM_PUT8(buf, arg->preambleType);
+	WSM_PUT8(buf, arg->probeForJoin);
+	WSM_PUT8(buf, arg->dtimPeriod);
+	WSM_PUT8(buf, arg->flags);
+	WSM_PUT32(buf, arg->ssidLength);
+	WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid));
+	WSM_PUT32(buf, arg->beaconInterval);
+	WSM_PUT32(buf, arg->basicRateSet);
+
+	hw_priv->tx_burst_idx = -1;
+	ret = wsm_cmd_send(hw_priv, buf, arg, 0x000B, WSM_CMD_JOIN_TIMEOUT,
+			   if_id);
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	wsm_oper_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_bss_params(struct bes2600_common *hw_priv,
+			const struct wsm_set_bss_params *arg,
+			int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT8(buf, 0);
+	WSM_PUT8(buf, arg->beaconLostCount);
+	WSM_PUT16(buf, arg->aid);
+	WSM_PUT32(buf, arg->operationalRateSet);
+	WSM_PUT32(buf, hw_priv->ht_info.ht_cap.mcs.rx_mask[0] << 14);
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0011, WSM_CMD_TIMEOUT,
+			if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_add_key(struct bes2600_common *hw_priv, const struct wsm_add_key *arg,
+			int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT(buf, arg, sizeof(*arg));
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000C, WSM_CMD_TIMEOUT,
+				if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_remove_key(struct bes2600_common *hw_priv,
+		   const struct wsm_remove_key *arg, int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT8(buf, arg->entryIndex);
+	WSM_PUT8(buf, 0);
+	WSM_PUT16(buf, 0);
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000D, WSM_CMD_TIMEOUT,
+			   if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_tx_queue_params(struct bes2600_common *hw_priv,
+				const struct wsm_set_tx_queue_params *arg,
+				u8 id, int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+	u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1};
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT8(buf, queue_id_to_wmm_aci[id]);
+	WSM_PUT8(buf, 0);
+	WSM_PUT8(buf, arg->ackPolicy);
+	WSM_PUT8(buf, 0);
+	WSM_PUT32(buf, arg->maxTransmitLifetime);
+	WSM_PUT16(buf, arg->allowedMediumTime);
+	WSM_PUT16(buf, 0);
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT, if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_edca_params(struct bes2600_common *hw_priv,
+				const struct wsm_edca_params *arg,
+				int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	/* Implemented according to specification. */
+
+	WSM_PUT16(buf, arg->params[3].cwMin);
+	WSM_PUT16(buf, arg->params[2].cwMin);
+	WSM_PUT16(buf, arg->params[1].cwMin);
+	WSM_PUT16(buf, arg->params[0].cwMin);
+
+	WSM_PUT16(buf, arg->params[3].cwMax);
+	WSM_PUT16(buf, arg->params[2].cwMax);
+	WSM_PUT16(buf, arg->params[1].cwMax);
+	WSM_PUT16(buf, arg->params[0].cwMax);
+
+	WSM_PUT8(buf, arg->params[3].aifns);
+	WSM_PUT8(buf, arg->params[2].aifns);
+	WSM_PUT8(buf, arg->params[1].aifns);
+	WSM_PUT8(buf, arg->params[0].aifns);
+
+	WSM_PUT16(buf, arg->params[3].txOpLimit);
+	WSM_PUT16(buf, arg->params[2].txOpLimit);
+	WSM_PUT16(buf, arg->params[1].txOpLimit);
+	WSM_PUT16(buf, arg->params[0].txOpLimit);
+
+	WSM_PUT32(buf, arg->params[3].maxReceiveLifetime);
+	WSM_PUT32(buf, arg->params[2].maxReceiveLifetime);
+	WSM_PUT32(buf, arg->params[1].maxReceiveLifetime);
+	WSM_PUT32(buf, arg->params[0].maxReceiveLifetime);
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0013, WSM_CMD_TIMEOUT, if_id);
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_switch_channel(struct bes2600_common *hw_priv,
+			   const struct wsm_switch_channel *arg,
+			   int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+	wsm_lock_tx(hw_priv);
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT8(buf, arg->channelMode | 0x80);
+	WSM_PUT8(buf, arg->channelSwitchCount);
+	WSM_PUT16(buf, arg->newChannelNumber);
+
+	hw_priv->channel_switch_in_progress = 1;
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0016, WSM_CMD_TIMEOUT, if_id);
+	wsm_cmd_unlock(hw_priv);
+	if (ret) {
+		wsm_unlock_tx(hw_priv);
+		hw_priv->channel_switch_in_progress = 0;
+	}
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	wsm_unlock_tx(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_pm(struct bes2600_common *hw_priv, const struct wsm_set_pm *arg,
+		int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT8(buf, arg->pmMode);
+	WSM_PUT8(buf, arg->fastPsmIdlePeriod);
+	WSM_PUT8(buf, arg->apPsmChangePeriod);
+	WSM_PUT8(buf, arg->minAutoPsPollPeriod);
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0010, WSM_CMD_TIMEOUT, if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_start(struct bes2600_common *hw_priv, const struct wsm_start *arg,
+		int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT8(buf, arg->mode);
+	WSM_PUT8(buf, arg->band);
+	WSM_PUT16(buf, arg->channelNumber);
+	WSM_PUT32(buf, arg->CTWindow);
+	WSM_PUT32(buf, arg->beaconInterval);
+	WSM_PUT8(buf, arg->DTIMPeriod);
+	WSM_PUT8(buf, arg->preambleType);
+	WSM_PUT8(buf, arg->probeDelay);
+	WSM_PUT8(buf, arg->ssidLength);
+	WSM_PUT(buf, arg->ssid, sizeof(arg->ssid));
+	WSM_PUT32(buf, arg->basicRateSet);
+
+	hw_priv->tx_burst_idx = -1;
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0017, WSM_CMD_START_TIMEOUT,
+			if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+#if 0
+/* This API is no longer present in WSC */
+/* ******************************************************************** */
+
+int wsm_beacon_transmit(struct bes2600_common *hw_priv,
+			const struct wsm_beacon_transmit *arg,
+			int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT32(buf, arg->enableBeaconing ? 1 : 0);
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0018, WSM_CMD_TIMEOUT, if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+#endif
+
+/* ******************************************************************** */
+
+int wsm_start_find(struct bes2600_common *hw_priv, int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT, if_id);
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_find(struct bes2600_common *hw_priv, int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT, if_id);
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_map_link(struct bes2600_common *hw_priv, const struct wsm_map_link *arg,
+		int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+	u16 cmd = 0x001C;
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr));
+
+	if (is_hardware_cw1250(hw_priv) || is_hardware_cw1260(hw_priv)) {
+		WSM_PUT8(buf, arg->unmap);
+		WSM_PUT8(buf, arg->link_id);
+	} else {
+		cmd |= WSM_TX_LINK_ID(arg->link_id);
+		WSM_PUT16(buf, 0);
+	}
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_TIMEOUT, if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_update_ie(struct bes2600_common *hw_priv,
+		  const struct wsm_update_ie *arg, int if_id)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	WSM_PUT16(buf, arg->what);
+	WSM_PUT16(buf, arg->count);
+	WSM_PUT(buf, arg->ies, arg->length);
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT, if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+
+}
+
+int wsm_epta_cmd(struct bes2600_common *hw_priv, struct wsm_epta_msg *arg)
+{
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+	static bool epta_lock_tx = false;
+
+	if (arg->hw_epta_enable & (1 << 11))
+		arg->hw_epta_enable &= ~(3 << 10); // force TDD
+	else if (coex_is_fdd_mode())
+		arg->hw_epta_enable |= (1 << 10); //LMAC_COEX_MODE_FDD
+
+	if (arg->hw_epta_enable != 3 || arg->hw_epta_enable != 4) { //use for wifi connect
+		///TODO: remove this hack. use hardware in disconnect mode
+		if (coex_is_wifi_inactive()) {
+			arg->wlan_duration = 20000;
+			arg->bt_duration = 80000;
+			arg->hw_epta_enable &= ~(0x3);
+		}
+		// if (coex_is_fdd_mode()) {
+		// 	arg->wlan_duration = 100000;
+		// 	arg->bt_duration = 0;
+		// 	arg->hw_epta_enable |= (1 << 10);
+		// } else {
+		// 	if (coex_is_bt_inactive()) {
+		// 		arg->wlan_duration = 100000;
+		// 		arg->bt_duration = 0;
+		// 		arg->hw_epta_enable = 0;
+		// 	}
+		// }
+	}
+
+	bes2600_info(BES2600_DBG_WSM, "epta cmd: wlan:%d bt:%d enable:%x",
+		arg->wlan_duration, arg->bt_duration, arg->hw_epta_enable);
+
+	/*
+	Should lock tx queue to avoid frame stuck in firmware if wlan_duration is zero.
+	There is no need to distinguish epta mode as the duration reflects requirements of scenarios
+	*/
+	if (arg->wlan_duration == 0 && !epta_lock_tx) {
+		wsm_lock_tx(hw_priv);
+		epta_lock_tx = true;
+	} else if (epta_lock_tx && arg->wlan_duration != 0) {
+		wsm_unlock_tx(hw_priv);
+		epta_lock_tx = false;
+	}
+
+	wsm_cmd_lock(hw_priv);
+	WSM_PUT32(buf, arg->wlan_duration);
+	WSM_PUT32(buf, arg->bt_duration);
+	WSM_PUT32(buf, arg->hw_epta_enable);
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0029, WSM_CMD_TIMEOUT, 0);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+#else
+	return 0;
+#endif
+}
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+int wsm_epta_wifi_chan_cmd(struct bes2600_common *hw_priv, uint32_t channel, uint32_t type)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	/* cmd type */
+	WSM_PUT32(buf, BES2600_RF_CMD_CH_INFO);
+
+	WSM_PUT32(buf, channel);
+	WSM_PUT16(buf, type);
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0C27, WSM_CMD_TIMEOUT, 0);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+#endif
+
+int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	/* cmd type */
+	WSM_PUT32(buf, BES2600_RF_CMD_WIFI_STATUS);
+	WSM_PUT32(buf, status);
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0C27, WSM_CMD_TIMEOUT, 0);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv)
+{
+	int ret;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	/* cmd type */
+	WSM_PUT32(buf, BES2600_RF_CMD_CPU_USAGE);
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0C27, WSM_CMD_TIMEOUT, 0);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+}
+
+#ifdef PLAT_ALLWINNER_R329
+int wsm_save_factory_txt_to_flash(struct bes2600_common *hw_priv, const u8 *data, int if_id)
+{
+	int ret, i;
+	const struct factory_t *factory_cali = (const struct factory_t *)data;
+	struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(hw_priv);
+
+	/* cmd type */
+	WSM_PUT32(buf, BES2600_RF_CMD_CALI_TXT_TO_FLASH);
+
+	WSM_PUT32(buf, factory_cali->data.iQ_offset);
+	WSM_PUT16(buf, factory_cali->data.freq_cal);
+
+	for (i = 0; i < 3; i++)
+		WSM_PUT16(buf, factory_cali->data.tx_power_ch[i]);
+
+	WSM_PUT8(buf, factory_cali->data.freq_cal_flags);
+	WSM_PUT8(buf, factory_cali->data.tx_power_type);
+	WSM_PUT16(buf, factory_cali->data.temperature);
+
+	for (i = 0; i < 13; i++)
+		WSM_PUT16(buf, factory_cali->data.tx_power_ch_5G[i]);
+
+	WSM_PUT16(buf, factory_cali->data.tx_power_flags_5G);
+
+	for (i = 0; i < 4; i++)
+		WSM_PUT32(buf, factory_cali->data.bt_tx_power[i]);
+
+	WSM_PUT16(buf, factory_cali->data.temperature_5G);
+
+	ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0C27, WSM_CMD_TIMEOUT, if_id);
+
+	wsm_cmd_unlock(hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(hw_priv);
+	return -ENOMEM;
+
+}
+#endif
+
+/* ******************************************************************** */
+#ifdef MCAST_FWDING
+/* 3.66 */
+static int wsm_give_buffer_confirm(struct bes2600_common *hw_priv,
+							struct wsm_buf *buf)
+{
+	bes2600_dbg(BES2600_DBG_WSM, "[WSM] HW Buf count %d\n", hw_priv->hw_bufs_used);
+	if (!hw_priv->hw_bufs_used)
+		wake_up(&hw_priv->bh_evt_wq);
+
+	return 0;
+}
+
+/* 3.65 */
+int wsm_init_release_buffer_request(struct bes2600_common *hw_priv, u8 index)
+{
+	struct wsm_buf *buf = &hw_priv->wsm_release_buf[index];
+	u16 cmd = 0x0022; /* Buffer Request */
+	u8 flags;
+	size_t buf_len;
+
+	wsm_buf_init(buf);
+
+	flags = index ? 0: 0x1;
+
+	WSM_PUT8(buf, flags);
+	WSM_PUT8(buf, 0);
+	WSM_PUT16(buf, 0);
+
+	buf_len = buf->data - buf->begin;
+
+	/* Fill HI message header */
+	((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len);
+	((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd);
+
+	return 0;
+nomem:
+	return -ENOMEM;
+}
+
+/* 3.68 */
+static int wsm_request_buffer_confirm(struct bes2600_vif *priv,
+							u8 *arg,
+							struct wsm_buf *buf)
+{
+	u8 count;
+	u32 sta_asleep_mask = 0;
+	int i;
+	u32 mask = 0;
+	u32 change_mask = 0;
+	struct bes2600_common *hw_priv = priv->hw_priv;
+
+	/* There is no status field in this message */
+	sta_asleep_mask = WSM_GET32(buf);
+	count = WSM_GET8(buf);
+	count -= 1; /* Current workaround for FW issue */
+
+	spin_lock_bh(&priv->ps_state_lock);
+	change_mask = (priv->sta_asleep_mask ^ sta_asleep_mask);
+	bes2600_dbg(BES2600_DBG_WSM, "CM %x, HM %x, FWM %x\n", change_mask,priv->sta_asleep_mask, sta_asleep_mask);
+	spin_unlock_bh(&priv->ps_state_lock);
+
+	if (change_mask) {
+		struct ieee80211_sta *sta;
+		int ret = 0;
+
+
+		for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE ; ++i) {
+
+			if(BES2600_LINK_HARD != priv->link_id_db[i].status)
+				continue;
+
+			mask = BIT(i + 1);
+
+			/* If FW state and host state for this link are different then notify OMAC */
+			if(change_mask & mask) {
+
+				bes2600_dbg(BES2600_DBG_WSM, "PS State Changed %d for sta %pM\n", (sta_asleep_mask & mask) ? 1:0, priv->link_id_db[i].mac);
+
+
+				rcu_read_lock();
+				sta = ieee80211_find_sta(priv->vif, priv->link_id_db[i].mac);
+				if (!sta) {
+					bes2600_err(BES2600_DBG_WSM, "[WSM] WRBC - could not find sta %pM\n",
+							priv->link_id_db[i].mac);
+				} else {
+					ret = ieee80211_sta_ps_transition_ni(sta, (sta_asleep_mask & mask) ? true: false);
+					bes2600_dbg(BES2600_DBG_WSM, "PS State NOTIFIED %d\n", ret);
+					WARN_ON(ret);
+				}
+				rcu_read_unlock();
+			}
+		}
+		/* Replace STA mask with one reported by FW */
+		spin_lock_bh(&priv->ps_state_lock);
+		priv->sta_asleep_mask = sta_asleep_mask;
+		spin_unlock_bh(&priv->ps_state_lock);
+	}
+
+	bes2600_dbg(BES2600_DBG_WSM, "[WSM] WRBC - HW Buf count %d SleepMask %d\n",
+					hw_priv->hw_bufs_used, sta_asleep_mask);
+	hw_priv->buf_released = 0;
+	WARN_ON(count != (hw_priv->wsm_caps.numInpChBufs - 1));
+
+	return 0;
+
+underflow:
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+/* 3.67 */
+int wsm_request_buffer_request(struct bes2600_vif *priv,
+				u8 *arg)
+{
+	int ret;
+	struct wsm_buf *buf = &priv->hw_priv->wsm_cmd_buf;
+
+	wsm_cmd_lock(priv->hw_priv);
+
+	WSM_PUT8(buf, (*arg));
+	WSM_PUT8(buf, 0);
+	WSM_PUT16(buf, 0);
+
+	ret = wsm_cmd_send(priv->hw_priv, buf, arg, 0x0023, WSM_CMD_JOIN_TIMEOUT,priv->if_id);
+
+	wsm_cmd_unlock(priv->hw_priv);
+	return ret;
+
+nomem:
+	wsm_cmd_unlock(priv->hw_priv);
+	return -ENOMEM;
+}
+
+#endif
+
+
+int wsm_set_keepalive_filter(struct bes2600_vif *priv, bool enable)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	priv->rx_filter.keepalive = enable;
+	return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id);
+}
+
+int wsm_set_probe_responder(struct bes2600_vif *priv, bool enable)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+
+	priv->rx_filter.probeResponder = enable;
+	return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id);
+}
+/* ******************************************************************** */
+/* WSM indication events implementation					*/
+
+static int wsm_startup_indication(struct bes2600_common *hw_priv,
+					struct wsm_buf *buf)
+{
+	u16 status;
+	char fw_label[129];
+	static const char * const fw_types[] = {
+		"ETF",
+		"WFM",
+		"WSM",
+		"HI test",
+		"Platform test"
+	};
+
+	hw_priv->wsm_caps.numInpChBufs	= WSM_GET16(buf);
+	hw_priv->wsm_caps.sizeInpChBuf	= WSM_GET16(buf);
+	hw_priv->wsm_caps.hardwareId	= WSM_GET16(buf);
+	hw_priv->wsm_caps.hardwareSubId	= WSM_GET16(buf);
+	status				= WSM_GET16(buf);
+	hw_priv->wsm_caps.firmwareCap	= WSM_GET16(buf);
+	hw_priv->wsm_caps.firmwareType	= WSM_GET16(buf);
+	hw_priv->wsm_caps.firmwareApiVer	= WSM_GET16(buf);
+	hw_priv->wsm_caps.firmwareBuildNumber = WSM_GET16(buf);
+	hw_priv->wsm_caps.firmwareVersion	= WSM_GET16(buf);
+	WSM_GET(buf, &fw_label[0], sizeof(fw_label) - 1);
+	fw_label[sizeof(fw_label) - 1] = 0; /* Do not trust FW too much. */
+
+	if (WARN_ON(status))
+		return -EINVAL;
+
+	if (WARN_ON(hw_priv->wsm_caps.firmwareType > 4))
+		return -EINVAL;
+
+	bes2600_dbg(BES2600_DBG_INIT, "BES2600 WSM init done.\n"
+		"   Input buffers: %d x %d bytes\n"
+		"   Hardware: %d.%d\n"
+		"   %s firmware [%s], ver: %d, build: %d,"
+			" api: %d, cap: 0x%.4X\n",
+		hw_priv->wsm_caps.numInpChBufs,
+		hw_priv->wsm_caps.sizeInpChBuf,
+		hw_priv->wsm_caps.hardwareId,
+		hw_priv->wsm_caps.hardwareSubId,
+		fw_types[hw_priv->wsm_caps.firmwareType],
+		&fw_label[0],
+		hw_priv->wsm_caps.firmwareVersion,
+		hw_priv->wsm_caps.firmwareBuildNumber,
+		hw_priv->wsm_caps.firmwareApiVer,
+		hw_priv->wsm_caps.firmwareCap);
+
+	hw_priv->wsm_caps.firmwareReady = 1;
+
+	wake_up(&hw_priv->wsm_startup_done);
+
+	return 0;
+
+underflow:
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+static int wsm_receive_indication(struct bes2600_common *hw_priv,
+					int interface_link_id,
+					struct wsm_buf *buf,
+					struct sk_buff **skb_p)
+{
+	struct bes2600_vif *priv;
+
+	hw_priv->rx_timestamp = jiffies;
+	if (hw_priv->wsm_cbc.rx) {
+		struct wsm_rx rx;
+		struct ieee80211_hdr *hdr;
+		size_t hdr_len;
+		__le16 fctl;
+
+		rx.status = WSM_GET32(buf);
+		rx.channelNumber = WSM_GET16(buf);
+		rx.rxedRate = WSM_GET8(buf);
+		rx.rcpiRssi = WSM_GET8(buf);
+		rx.flags = WSM_GET32(buf);
+		buf->data+=16; // 2019-10-25, for lmac wsm data ind struct update
+
+		/* TODO:COMBO: Frames received from scanning are received
+		* with interface ID == 2 */
+		if (is_hardware_cw1250(hw_priv) || is_hardware_cw1260(hw_priv)) {
+			if (interface_link_id == CW12XX_GENERIC_IF_ID) {
+				/* Frames received in response to SCAN
+				 * Request */
+				interface_link_id =
+					get_interface_id_scanning(hw_priv);
+				if (interface_link_id == -1) {
+					interface_link_id = hw_priv->roc_if_id;
+				}
+#ifdef ROAM_OFFLOAD
+				if (hw_priv->auto_scanning) {
+					interface_link_id = hw_priv->scan.if_id;
+				}
+#endif/*ROAM_OFFLOAD*/
+			}
+			/* linkid (peer sta id is encoded in bit 25-28 of
+			   flags field */
+			rx.link_id = ((rx.flags & (0xf << 25)) >> 25);
+			rx.if_id = interface_link_id;
+		} else {
+			rx.link_id = interface_link_id;
+			rx.if_id = 0;
+		}
+
+		if(rx.if_id == -1) {
+			bes2600_info(BES2600_DBG_WSM, "%s: intf is not match\n", __func__);
+			return 0;
+		}
+
+		priv = cw12xx_hwpriv_to_vifpriv(hw_priv, rx.if_id);
+		if (!priv) {
+			bes2600_info(BES2600_DBG_WSM, "%s: NULL priv drop frame\n", __func__);
+			return 0;
+		}
+
+		/* FW Workaround: Drop probe resp or
+		beacon when RSSI is 0 */
+		hdr = (struct ieee80211_hdr *) buf->data;
+
+		if (!rx.rcpiRssi &&
+			(ieee80211_is_probe_resp(hdr->frame_control) ||
+			ieee80211_is_beacon(hdr->frame_control))) {
+			spin_unlock(&priv->vif_lock);
+			return 0;
+		}
+
+		/* If no RSSI subscription has been made,
+		* convert RCPI to RSSI here */
+		if (!priv->cqm_use_rssi)
+			rx.rcpiRssi = rx.rcpiRssi / 2 - 110;
+
+		fctl = *(__le16 *)buf->data;
+		hdr_len = buf->data - buf->begin;
+		skb_pull(*skb_p, hdr_len);
+
+		if (!rx.status &&
+			unlikely(ieee80211_is_deauth(fctl) || ieee80211_is_disassoc(fctl))) {
+			bes2600_dbg(BES2600_DBG_WSM, "rx deauth or disassoc, priv->join_status:%u", priv->join_status);
+			if (priv->join_status == BES2600_JOIN_STATUS_STA &&
+				(ether_addr_equal(hdr->addr3, priv->join_bssid) ||
+				 ether_addr_equal(hdr->addr3, priv->bssid))) {
+				bool ignore = false;
+
+				if (is_multicast_ether_addr(hdr->addr1)) {
+					struct ieee80211_mmie *mmie;
+					bool has_mmie = false;
+					/*
+					 * We may receive broadcast/multicast robust management frame
+					 * when PMF is enabled, in that case, FW has checked the MMIE
+					 * and verified IPN and MIC.
+					 * However, some buggy AP, e.g. Tenda AC6, send broadcast deauth
+					 * with MMIE even when no management frame protection,
+					 * this frame should be ignored and dropped by mac80211.
+					 */
+					if ((*skb_p)->len >= 24 + sizeof(*mmie)) {
+						mmie = (struct ieee80211_mmie *)
+							   ((*skb_p)->data + (*skb_p)->len - sizeof(*mmie));
+						if (mmie->element_id == WLAN_EID_MMIE &&
+							mmie->length == sizeof(*mmie) - 2)
+							has_mmie = true;
+					}
+					bes2600_info(BES2600_DBG_WSM, "[WSM] RX broadcast/multicast deauth: len=%d, has_mmie:%u, pmf=%d\n",
+							(*skb_p)->len, has_mmie, priv->pmf);
+					if (has_mmie ^ priv->pmf)
+						ignore = true;
+				} else if (ether_addr_equal(hdr->addr1, hw_priv->addresses[rx.if_id].addr)) {
+					bool has_protected = ieee80211_has_protected(fctl);
+					bes2600_info(BES2600_DBG_WSM, "[WSM] RX unicast deauth: protected=%d, pmf=%d, connect_in_process=%d\n",
+							has_protected, priv->pmf, atomic_read(&priv->connect_in_process));
+					/*
+					 * We should report unprotected deauth to mac80211 for
+					 * SA query when PMF is enabled, so cannot unjoin here.
+					 * If PMF is disabled, it is unexpected to receive
+					 * a protected unicast deauth.
+					 */
+					if ((has_protected ^ priv->pmf) || atomic_read(&priv->connect_in_process))
+						ignore = true;
+				} else {
+					ignore = true;
+				}
+
+				if (!ignore) {
+					/* Schedule unjoin work */
+					bes2600_info(BES2600_DBG_WSM, "[WSM] Issue unjoin command (RX).\n");
+					wsm_lock_tx_async(hw_priv);
+					if (queue_work(hw_priv->workqueue,
+								   &priv->unjoin_work) <= 0)
+						wsm_unlock_tx(hw_priv);
+				}
+			}
+		}
+		hw_priv->wsm_cbc.rx(priv, &rx, skb_p);
+		if (*skb_p)
+			skb_push(*skb_p, hdr_len);
+		spin_unlock(&priv->vif_lock);
+	}
+	return 0;
+
+underflow:
+	return -EINVAL;
+}
+
+static int wsm_event_indication(struct bes2600_common *hw_priv,
+				struct wsm_buf *buf,
+				int interface_link_id)
+{
+	int first;
+	struct bes2600_wsm_event *event;
+	struct bes2600_vif *priv;
+
+	if (!is_hardware_cw1250(hw_priv) && !is_hardware_cw1260(hw_priv))
+		interface_link_id = 0;
+
+	priv = cw12xx_hwpriv_to_vifpriv(hw_priv, interface_link_id);
+
+	if (unlikely(!priv)) {
+		bes2600_info(BES2600_DBG_WSM, "[WSM] Not find corresponding interface\n");
+		return 0;
+	}
+
+	if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
+		/* STA is stopped. */
+		spin_unlock(&priv->vif_lock);
+		return 0;
+	}
+	spin_unlock(&priv->vif_lock);
+
+	event = kzalloc(sizeof(struct bes2600_wsm_event), GFP_KERNEL);
+
+	event->evt.eventId = __le32_to_cpu(WSM_GET32(buf));
+	event->evt.eventData = __le32_to_cpu(WSM_GET32(buf));
+	event->if_id = interface_link_id;
+
+	bes2600_dbg(BES2600_DBG_WSM, "[WSM] Event: %d(%d)\n",
+		event->evt.eventId, event->evt.eventData);
+
+	spin_lock(&hw_priv->event_queue_lock);
+	first = list_empty(&hw_priv->event_queue);
+	list_add_tail(&event->link, &hw_priv->event_queue);
+	spin_unlock(&hw_priv->event_queue_lock);
+
+	if (first)
+		queue_work(hw_priv->workqueue, &hw_priv->event_handler);
+
+	return 0;
+
+underflow:
+	kfree(event);
+	return -EINVAL;
+}
+
+/* TODO:COMBO:Make this perVIFF once mac80211 support is available */
+static int wsm_channel_switch_indication(struct bes2600_common *hw_priv,
+						struct wsm_buf *buf)
+{
+	wsm_unlock_tx(hw_priv); /* Re-enable datapath */
+	WARN_ON(WSM_GET32(buf));
+
+	hw_priv->channel_switch_in_progress = 0;
+	wake_up(&hw_priv->channel_switch_done);
+
+	if (hw_priv->wsm_cbc.channel_switch)
+		hw_priv->wsm_cbc.channel_switch(hw_priv);
+	return 0;
+
+underflow:
+	return -EINVAL;
+}
+
+static int wsm_set_pm_indication(struct bes2600_common *hw_priv,
+					struct wsm_buf *buf)
+{
+	struct wsm_set_pm_complete arg;
+
+	arg.status = WSM_GET32(buf);
+	arg.psm = WSM_GET8(buf);
+
+	if(arg.status == WSM_STATUS_SUCCESS) {
+		bes2600_pwr_notify_ps_changed(hw_priv, arg.psm);
+	} else {
+		bes2600_err(BES2600_DBG_WSM, "[WSM] PM Ind status:%d psm:%d\n", arg.status, arg.psm);
+	}
+
+	return 0;
+
+underflow:
+	return -EINVAL;
+}
+
+static int wsm_scan_complete_indication(struct bes2600_common *hw_priv,
+					struct wsm_buf *buf)
+{
+#ifdef ROAM_OFFLOAD
+	if(hw_priv->auto_scanning == 0)
+		wsm_oper_unlock(hw_priv);
+#else
+	wsm_oper_unlock(hw_priv);
+#endif /*ROAM_OFFLOAD*/
+
+	if (hw_priv->wsm_cbc.scan_complete) {
+		struct wsm_scan_complete arg;
+		arg.status = WSM_GET32(buf);
+		arg.psm = WSM_GET8(buf);
+		arg.numChannels = WSM_GET8(buf);
+		hw_priv->wsm_cbc.scan_complete(hw_priv, &arg);
+	}
+	return 0;
+
+underflow:
+	return -EINVAL;
+}
+
+static int wsm_find_complete_indication(struct bes2600_common *hw_priv,
+					struct wsm_buf *buf)
+{
+	/* TODO: Implement me. */
+	//STUB();
+	return 0;
+}
+
+static int wsm_suspend_resume_indication(struct bes2600_common *hw_priv,
+					 int interface_link_id,
+					 struct wsm_buf *buf)
+{
+	if (hw_priv->wsm_cbc.suspend_resume) {
+		u32 flags;
+		struct wsm_suspend_resume arg;
+		struct bes2600_vif *priv;
+
+		if (is_hardware_cw1250(hw_priv) ||
+				is_hardware_cw1260(hw_priv)) {
+			int i;
+			arg.if_id = interface_link_id;
+			/* TODO:COMBO: Extract bitmap from suspend-resume
+			* TX indication */
+			bes2600_for_each_vif(hw_priv, priv, i) {
+				if (!priv)
+					continue;
+				if (priv->join_status ==
+						BES2600_JOIN_STATUS_AP) {
+					 arg.if_id = priv->if_id;
+					 break;
+				}
+				arg.link_id = 0;
+			}
+		} else {
+			arg.if_id = 0;
+			arg.link_id = interface_link_id;
+		}
+
+		flags = WSM_GET32(buf);
+		arg.stop = !(flags & 1);
+		arg.multicast = !!(flags & 8);
+		arg.queue = (flags >> 1) & 3;
+
+		priv = cw12xx_hwpriv_to_vifpriv(hw_priv, arg.if_id);
+		if (unlikely(!priv)) {
+			bes2600_dbg(BES2600_DBG_WSM, "[WSM] suspend-resume indication"
+				   " for removed interface!\n");
+			return 0;
+		}
+		hw_priv->wsm_cbc.suspend_resume(priv, &arg);
+		spin_unlock(&priv->vif_lock);
+	}
+	return 0;
+
+underflow:
+	return -EINVAL;
+}
+
+/**
+ * signaling cmd confirm.
+ */
+
+int wsm_driver_rf_cmd_confirm(struct bes2600_common *hw_priv, void *arg, struct wsm_buf *buf)
+{
+	int ret = 0;
+	u32 cmd_type;
+	struct wsm_mcu_hdr *msg_hdr = (struct wsm_mcu_hdr *)(buf->begin);
+
+	cmd_type = __le32_to_cpu(msg_hdr->cmd_type);
+	buf->data += sizeof(struct wsm_mcu_hdr) - sizeof(struct wsm_hdr);
+
+	switch (cmd_type) {
+	case BES2600_RF_CMD_CALI_TXT_TO_FLASH:
+		ret = WSM_GET32(buf);
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+
+underflow:
+	return -EINVAL;
+}
+
+/* ******************************************************************** */
+/* WSM TX								*/
+
+int wsm_cmd_send(struct bes2600_common *hw_priv,
+		 struct wsm_buf *buf,
+		 void *arg, u16 cmd, long tmo, int if_id)
+{
+	size_t buf_len = buf->data - buf->begin;
+	int ret;
+
+	if (cmd == 0x0006) /* Write MIB */
+		bes2600_dbg(BES2600_DBG_WSM, "[WSM] >>> 0x%.4X [MIB: 0x%.4X] (%lu)\n",
+			cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]),
+			(long unsigned)buf_len);
+	else
+		bes2600_dbg(BES2600_DBG_WSM, "[WSM] >>> 0x%.4X (%lu)\n", cmd, (long unsigned)buf_len);
+
+	/* Fill HI message header */
+	/* BH will add sequence number */
+
+	/* TODO:COMBO: Add if_id from  to the WSM header */
+	/* if_id == -1 indicates that command is HW specific,
+	 * eg. wsm_configuration which is called during driver initialzation
+	 *  (mac80211 .start callback called when first ifce is created. )*/
+
+	/* send hw specific commands on if 0 */
+	if (if_id == -1)
+		if_id = 0;
+
+	((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len);
+	if (IS_DRIVER_TO_MCU_CMD(cmd))
+		((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd);
+	else
+		((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd |
+					((is_hardware_cw1250(hw_priv)|| is_hardware_cw1260(hw_priv)) ?
+						(if_id << 6) : 0));
+
+#ifdef BES2600_HOST_TIMESTAMP_DEBUG
+	if (buf->end >= buf->data + 4)
+		*(u32 *)buf->data = (u32)jiffies_to_msecs(jiffies);
+#endif
+
+	spin_lock(&hw_priv->wsm_cmd.lock);
+	BUG_ON(hw_priv->wsm_cmd.ptr);
+	hw_priv->wsm_cmd.done = 0;
+	hw_priv->wsm_cmd.ptr = buf->begin;
+	hw_priv->wsm_cmd.len = buf_len;
+	hw_priv->wsm_cmd.arg = arg;
+	hw_priv->wsm_cmd.cmd = cmd;
+	spin_unlock(&hw_priv->wsm_cmd.lock);
+	bes2600_tx_loop_record_wsm_cmd(hw_priv, hw_priv->wsm_cmd.ptr);
+
+	bes2600_bh_wakeup(hw_priv);
+
+	if (unlikely(hw_priv->bh_error)) {
+		/* Do not wait for timeout if BH is dead. Exit immediately. */
+		ret = 0;
+	} else {
+		long rx_timestamp;
+		long wsm_cmd_starttime = jiffies;
+		long wsm_cmd_runtime;
+		long wsm_cmd_max_tmo = WSM_CMD_DEFAULT_TIMEOUT;
+
+		/* Give start cmd a little more time */
+		if (tmo == WSM_CMD_START_TIMEOUT)
+			wsm_cmd_max_tmo = WSM_CMD_START_TIMEOUT;
+		/* Firmware prioritizes data traffic over control confirm.
+		 * Loop below checks if data was RXed and increases timeout
+		 * accordingly. */
+		do {
+			/* It's safe to use unprotected access to
+			 * wsm_cmd.done here */
+			ret = wait_event_timeout(
+					hw_priv->wsm_cmd_wq,
+					hw_priv->wsm_cmd.done, tmo);
+			rx_timestamp = jiffies - hw_priv->rx_timestamp;
+			wsm_cmd_runtime = jiffies - wsm_cmd_starttime;
+			if (unlikely(rx_timestamp < 0) || wsm_cmd_runtime < 0)
+				rx_timestamp = tmo + 1;
+		} while (!ret && rx_timestamp <= tmo &&
+					wsm_cmd_runtime < wsm_cmd_max_tmo);
+	}
+
+	if (unlikely(ret == 0)) {
+		u16 raceCheck;
+
+		spin_lock(&hw_priv->wsm_cmd.lock);
+		raceCheck = hw_priv->wsm_cmd.cmd;
+		hw_priv->wsm_cmd.arg = NULL;
+		hw_priv->wsm_cmd.ptr = NULL;
+		spin_unlock(&hw_priv->wsm_cmd.lock);
+
+		/* Race condition check to make sure _confirm is not called
+		 * after exit of _send */
+		if (raceCheck == 0xFFFF) {
+			/* If wsm_handle_rx got stuck in _confirm we will hang
+			 * system there. It's better than silently currupt
+			 * stack or heap, isn't it? */
+			BUG_ON(wait_event_timeout(
+					hw_priv->wsm_cmd_wq,
+					hw_priv->wsm_cmd.done,
+					WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0);
+		}
+
+		/* Kill BH thread to report the error to the top layer. */
+		//hw_priv->bh_error = 1;
+		wake_up(&hw_priv->bh_wq);
+		ret = -ETIMEDOUT;
+	} else {
+		spin_lock(&hw_priv->wsm_cmd.lock);
+		hw_priv->wsm_cmd.arg = NULL;
+		hw_priv->wsm_cmd.ptr = NULL;
+		BUG_ON(!hw_priv->wsm_cmd.done);
+		ret = hw_priv->wsm_cmd.ret;
+		spin_unlock(&hw_priv->wsm_cmd.lock);
+	}
+	bes2600_tx_loop_clear_wsm_cmd(hw_priv);
+	wsm_buf_reset(buf);
+	return ret;
+}
+
+/* ******************************************************************** */
+/* WSM TX port control							*/
+
+void wsm_lock_tx(struct bes2600_common *hw_priv)
+{
+	wsm_cmd_lock(hw_priv);
+	if (atomic_add_return(1, &hw_priv->tx_lock) == 1) {
+		if (wsm_flush_tx(hw_priv))
+			bes2600_dbg(BES2600_DBG_WSM, "[WSM] TX is locked.\n");
+	}
+	wsm_cmd_unlock(hw_priv);
+}
+
+void wsm_vif_lock_tx(struct bes2600_vif *priv)
+{
+	struct bes2600_common *hw_priv = priv->hw_priv;
+
+	wsm_cmd_lock(hw_priv);
+	if (atomic_add_return(1, &hw_priv->tx_lock) == 1) {
+		if (wsm_vif_flush_tx(priv))
+			bes2600_dbg(BES2600_DBG_WSM, "[WSM] TX is locked for"
+					" if_id %d.\n", priv->if_id);
+	}
+	wsm_cmd_unlock(hw_priv);
+}
+
+void wsm_lock_tx_async(struct bes2600_common *hw_priv)
+{
+	if (atomic_add_return(1, &hw_priv->tx_lock) == 1)
+		bes2600_dbg(BES2600_DBG_WSM, "[WSM] TX is locked (async).\n");
+}
+
+bool wsm_flush_tx(struct bes2600_common *hw_priv)
+{
+	unsigned long timestamp = jiffies;
+	bool pending = false;
+	long timeout;
+	int i;
+
+	/* Flush must be called with TX lock held. */
+	BUG_ON(!atomic_read(&hw_priv->tx_lock));
+
+	/* First check if we really need to do something.
+	 * It is safe to use unprotected access, as hw_bufs_used
+	 * can only decrements. */
+
+	if (!hw_priv->hw_bufs_used)
+		return true;
+
+	if (hw_priv->bh_error) {
+		/* In case of failure do not wait for magic. */
+		bes2600_err(BES2600_DBG_WSM, "[WSM] Fatal error occured, "
+				"will not flush TX.\n");
+		return false;
+	} else {
+		/* Get a timestamp of "oldest" frame */
+		for (i = 0; i < 4; ++i)
+			pending |= bes2600_queue_get_xmit_timestamp(
+					&hw_priv->tx_queue[i],
+					&timestamp, CW12XX_ALL_IFS,
+					0xffffffff);
+		/* It is allowed to lock TX with only a command in the pipe. */
+		if (!pending)
+			return true;
+
+		timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies;
+		if (timeout < 0 || wait_event_timeout(hw_priv->bh_evt_wq,
+				!hw_priv->hw_bufs_used,
+				timeout) <= 0) {
+			/* Hmmm... Not good. Frame had stuck in firmware. */
+			bes2600_chrdev_wifi_force_close(hw_priv, true);
+		}
+
+		/* Ok, everything is flushed. */
+		return true;
+	}
+}
+
+bool wsm_vif_flush_tx(struct bes2600_vif *priv)
+{
+	struct bes2600_common *hw_priv = priv->hw_priv;
+	unsigned long timestamp = jiffies;
+	long timeout;
+	int i;
+	int if_id = priv->if_id;
+
+
+	/* Flush must be called with TX lock held. */
+	BUG_ON(!atomic_read(&hw_priv->tx_lock));
+
+	/* First check if we really need to do something.
+	 * It is safe to use unprotected access, as hw_bufs_used
+	 * can only decrements. */
+
+	if (!hw_priv->hw_bufs_used_vif[priv->if_id])
+		return true;
+
+	if (hw_priv->bh_error) {
+		/* In case of failure do not wait for magic. */
+		bes2600_err(BES2600_DBG_WSM,  "[WSM] Fatal error occured, "
+				"will not flush TX.\n");
+		return false;
+	} else {
+		/* Get a timestamp of "oldest" frame */
+		for (i = 0; i < 4; ++i)
+			bes2600_queue_get_xmit_timestamp(
+					&hw_priv->tx_queue[i],
+					&timestamp, if_id,
+					0xffffffff);
+		/* It is allowed to lock TX with only a command in the pipe. */
+		if (!hw_priv->hw_bufs_used_vif[if_id])
+			return true;
+
+		timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies;
+		if (timeout < 0 || wait_event_timeout(hw_priv->bh_evt_wq,
+				!hw_priv->hw_bufs_used_vif[if_id],
+				timeout) <= 0) {
+
+			/* Hmmm... Not good. Frame had stuck in firmware. */
+			bes2600_chrdev_wifi_force_close(hw_priv, true);
+		}
+
+		/* Ok, everything is flushed. */
+		return true;
+	}
+}
+
+
+void wsm_unlock_tx(struct bes2600_common *hw_priv)
+{
+	int tx_lock;
+	if (hw_priv->bh_error)
+		bes2600_err(BES2600_DBG_WSM, "fatal error occured, unlock is unsafe\n");
+	else {
+		tx_lock = atomic_sub_return(1, &hw_priv->tx_lock);
+		if (tx_lock < 0) {
+			BUG_ON(1);
+		} else if (tx_lock == 0) {
+			bes2600_bh_wakeup(hw_priv);
+			bes2600_dbg(BES2600_DBG_WSM, "[WSM] TX is unlocked.\n");
+		}
+	}
+}
+
+/* ******************************************************************** */
+/* WSM RX								*/
+
+int wsm_handle_exception(struct bes2600_common *hw_priv, u8 *data, size_t len)
+{
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	struct bes2600_vif *priv = NULL;
+	int if_id = 0;
+#endif
+	struct wsm_buf buf;
+	u32 reason;
+	u32 reg[18];
+	char fname[48];
+	size_t i;
+
+	static const char * const reason_str[] = {
+		"undefined instruction",
+		"prefetch abort",
+		"data abort",
+		"unknown error",
+	};
+
+#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
+	/* Send the event upwards on the FW exception */
+	bes2600_pm_stay_awake(&hw_priv->pm_state, 3*HZ);
+
+	spin_lock(&hw_priv->vif_list_lock);
+	bes2600_for_each_vif(hw_priv, priv, if_id) {
+		if (!priv)
+			continue;
+		ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL);
+	}
+	spin_unlock(&hw_priv->vif_list_lock);
+#endif
+
+	buf.begin = buf.data = data;
+	buf.end = &buf.begin[len];
+
+	reason = WSM_GET32(&buf);
+	for (i = 0; i < ARRAY_SIZE(reg); ++i)
+		reg[i] = WSM_GET32(&buf);
+	WSM_GET(&buf, fname, sizeof(fname));
+
+	if (reason < 4)
+		wiphy_err(hw_priv->hw->wiphy,
+			"Firmware exception: %s.\n",
+			reason_str[reason]);
+	else
+		wiphy_err(hw_priv->hw->wiphy,
+			"Firmware assert at %.*s, line %d\n",
+			(int)sizeof(fname), fname, (int)reg[1]);
+
+	for (i = 0; i < 12; i += 4)
+		wiphy_err(hw_priv->hw->wiphy,
+			"R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n",
+			(int)i + 0, reg[i + 0], (int)i + 1, reg[i + 1],
+			(int)i + 2, reg[i + 2], (int)i + 3, reg[i + 3]);
+	wiphy_err(hw_priv->hw->wiphy,
+		"R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n",
+		reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]);
+	i += 4;
+	wiphy_err(hw_priv->hw->wiphy,
+		"CPSR: 0x%.8X, SPSR: 0x%.8X\n",
+		reg[i + 0], reg[i + 1]);
+
+	print_hex_dump_bytes("R1: ", DUMP_PREFIX_NONE,
+		fname, sizeof(fname));
+	return 0;
+
+underflow:
+	wiphy_err(hw_priv->hw->wiphy,
+		"Firmware exception.\n");
+	print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE,
+		data, len);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(wsm_handle_exception);
+
+int wsm_bt_ts_request(struct bes2600_common *hw_priv, struct wsm_buf *buf)
+{
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+	uint32_t type;
+
+	type = __le32_to_cpu(((struct wsm_mcu_hdr *)(buf->begin))->cmd_type);
+	bbt_change_current_status(hw_priv, type);
+	return 0;
+#else
+	return 0;
+#endif
+}
+
+int wsm_handle_rx(struct bes2600_common *hw_priv, int id,
+		  struct wsm_hdr *wsm, struct sk_buff **skb_p)
+{
+	int ret = 0;
+	struct wsm_buf wsm_buf;
+//	struct bes2600_vif *priv = NULL;
+//	int i = 0;
+	int interface_link_id = (id >> 6) & 0x0F;
+	u32 ind_confirm_label = 0x0;  /* wsm to mcu cmd ind & cnfirm label */
+
+#ifdef ROAM_OFFLOAD
+#if 0
+	struct bes2600_vif *priv;
+	priv = cw12xx_hwpriv_to_vifpriv(hw_priv, interface_link_id);
+	if (unlikely(!priv)) {
+		WARN_ON(1);
+		return 0;
+	}
+	spin_unlock(&priv->vif_lock);
+#endif
+#endif/*ROAM_OFFLOAD*/
+
+	/* Strip link id. */
+	id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+
+	wsm_buf.begin = (u8 *)&wsm[0];
+	wsm_buf.data = (u8 *)&wsm[1];
+	wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)];
+
+	bes2600_dbg(BES2600_DBG_WSM, "[WSM] <<< 0x%.4X (%ld)\n", id,
+			(long)(wsm_buf.end - wsm_buf.begin));
+
+	if (IS_DRIVER_TO_MCU_CMD(id))
+		ind_confirm_label = __le32_to_cpu(((struct wsm_mcu_hdr *)wsm)->handle_label);
+
+	if (id == 0x0C30) {
+		ret = wsm_bt_ts_request(hw_priv, &wsm_buf);
+	} else if (id == 0x404) {
+		ret = wsm_tx_confirm(hw_priv, &wsm_buf, interface_link_id);
+#ifdef MCAST_FWDING
+#if 1
+	} else if (id == 0x422) {
+		ret = wsm_give_buffer_confirm(hw_priv, &wsm_buf);
+#endif
+#endif
+
+	} else if (id == 0x41E) {
+		ret = wsm_multi_tx_confirm(hw_priv, &wsm_buf,
+					   interface_link_id);
+	} else if (WSM_CONFIRM_CONDITION(id, ind_confirm_label)) {
+		void *wsm_arg;
+		u16 wsm_cmd;
+
+		/* Do not trust FW too much. Protection against repeated
+		 * response and race condition removal (see above). */
+		spin_lock(&hw_priv->wsm_cmd.lock);
+		wsm_arg = hw_priv->wsm_cmd.arg;
+		wsm_cmd = hw_priv->wsm_cmd.cmd &
+				~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+		hw_priv->wsm_cmd.cmd = 0xFFFF;
+		spin_unlock(&hw_priv->wsm_cmd.lock);
+
+		if (((id & 0x0f00) == 0x0400) && WARN_ON((id & ~0x0400) != wsm_cmd)) {
+			/* Note that any non-zero is a fatal retcode. */
+			ret = -EINVAL;
+			goto out;
+		}
+
+		switch (id) {
+		case 0x0409:
+			/* Note that wsm_arg can be NULL in case of timeout in
+			 * wsm_cmd_send(). */
+			if (likely(wsm_arg))
+				ret = wsm_configuration_confirm(hw_priv,
+								wsm_arg,
+								&wsm_buf);
+			break;
+		case 0x0405:
+			if (likely(wsm_arg))
+				ret = wsm_read_mib_confirm(hw_priv, wsm_arg,
+								&wsm_buf);
+			break;
+		case 0x0406:
+			if (likely(wsm_arg))
+				ret = wsm_write_mib_confirm(hw_priv, wsm_arg,
+								&wsm_buf,
+								interface_link_id);
+			break;
+		case 0x040B:
+			if (likely(wsm_arg))
+				ret = wsm_join_confirm(hw_priv, wsm_arg,
+							   &wsm_buf);
+			break;
+
+#ifdef MCAST_FWDING
+		case 0x0423: /* req buffer cfm*/
+			if (likely(wsm_arg)){
+				bes2600_for_each_vif(hw_priv, priv, i) {
+					if (priv && (priv->join_status == BES2600_JOIN_STATUS_AP))
+						ret = wsm_request_buffer_confirm(priv,
+								wsm_arg, &wsm_buf);
+				}
+			}
+			break;
+#endif
+		case 0x0407: /* start-scan */
+#ifdef ROAM_OFFLOAD
+			if (hw_priv->auto_scanning) {
+				if (atomic_read(&hw_priv->scan.in_progress)) {
+					hw_priv->auto_scanning = 0;
+				}
+				else {
+					wsm_oper_unlock(hw_priv);
+					up(&hw_priv->scan.lock);
+				}
+			}
+#endif /*ROAM_OFFLOAD*/
+		case 0x0408: /* stop-scan */
+		case 0x040A: /* wsm_reset */
+		case 0x040C: /* add_key */
+		case 0x040D: /* remove_key */
+		case 0x0410: /* wsm_set_pm */
+		case 0x0411: /* set_bss_params */
+		case 0x0412: /* set_tx_queue_params */
+		case 0x0413: /* set_edca_params */
+		case 0x0416: /* switch_channel */
+		case 0x0417: /* start */
+		case 0x0418: /* beacon_transmit */
+		case 0x0419: /* start_find */
+		case 0x041A: /* stop_find */
+		case 0x041B: /* update_ie */
+		case 0x041C: /* map_link */
+		case 0x0429: /* epta */
+			WARN_ON(wsm_arg != NULL);
+			ret = wsm_generic_confirm(hw_priv, wsm_arg, &wsm_buf);
+			if (ret)
+				wiphy_warn(hw_priv->hw->wiphy,
+					"wsm_generic_confirm "
+					"failed for request 0x%.4X.\n",
+					id & ~0x0400);
+			break;
+#ifdef CONFIG_BES2600_TESTMODE
+		case 0x0C25:
+			ret = wsm_vendor_rf_cmd_confirm(hw_priv, wsm_arg, &wsm_buf);
+			break;
+		case 0x0C27:
+			ret = wsm_driver_rf_cmd_confirm(hw_priv, wsm_arg, &wsm_buf);
+			break;
+#endif /* CONFIG_BES2600_TESTMODE */
+#ifdef BES_UNIFIED_PM
+		case 0x0424: /* wifi sleep disable */
+			break;
+#endif
+		default:
+			BUG_ON(1);
+		}
+
+		spin_lock(&hw_priv->wsm_cmd.lock);
+		hw_priv->wsm_cmd.ret = ret;
+		hw_priv->wsm_cmd.done = 1;
+		spin_unlock(&hw_priv->wsm_cmd.lock);
+		ret = 0; /* Error response from device should ne stop BH. */
+
+		wake_up(&hw_priv->wsm_cmd_wq);
+	} else if ((id & 0x0f00) == 0x0800) {
+		switch (id) {
+		case 0x0801:
+			ret = wsm_startup_indication(hw_priv, &wsm_buf);
+			break;
+		case 0x0804:
+			ret = wsm_receive_indication(hw_priv, interface_link_id,
+					&wsm_buf, skb_p);
+			break;
+		case 0x0805:
+			ret = wsm_event_indication(hw_priv, &wsm_buf,
+					interface_link_id);
+			break;
+		case 0x080A:
+			ret = wsm_channel_switch_indication(hw_priv, &wsm_buf);
+			break;
+		case 0x0809:
+			ret = wsm_set_pm_indication(hw_priv, &wsm_buf);
+			break;
+		case 0x0806:
+#ifdef ROAM_OFFLOAD
+			if(hw_priv->auto_scanning && hw_priv->frame_rcvd) {
+				struct bes2600_vif *priv;
+				hw_priv->frame_rcvd = 0;
+				priv = cw12xx_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id);
+				if (unlikely(!priv)) {
+					WARN_ON(1);
+					return 0;
+				}
+					spin_unlock(&priv->vif_lock);
+				if (hw_priv->beacon) {
+					struct wsm_scan_complete *scan_cmpl = \
+						(struct wsm_scan_complete *) \
+						((u8 *)wsm + sizeof(struct wsm_hdr));
+					struct ieee80211_rx_status *rhdr = \
+						IEEE80211_SKB_RXCB(hw_priv->beacon);
+					rhdr->signal = (s8)scan_cmpl->reserved;
+					if (!priv->cqm_use_rssi) {
+						rhdr->signal = rhdr->signal / 2 - 110;
+					}
+					if (!hw_priv->beacon_bkp)
+						hw_priv->beacon_bkp = \
+						skb_copy(hw_priv->beacon, GFP_ATOMIC);
+					ieee80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon);
+					hw_priv->beacon = hw_priv->beacon_bkp;
+
+					hw_priv->beacon_bkp = NULL;
+				}
+				bes2600_dbg(BES2600_DBG_WSM, "[WSM] Send Testmode Event.\n");
+#ifdef CONFIG_BES2600_TESTMODE
+				bes2600_testmode_event(priv->hw->wiphy,
+					BES_MSG_NEW_SCAN_RESULTS, 0,
+					0, GFP_KERNEL);
+#endif
+
+			}
+#endif /*ROAM_OFFLOAD*/
+			ret = wsm_scan_complete_indication(hw_priv, &wsm_buf);
+			break;
+		case 0x080B:
+			ret = wsm_find_complete_indication(hw_priv, &wsm_buf);
+			break;
+		case 0x080C:
+			ret = wsm_suspend_resume_indication(hw_priv,
+					interface_link_id, &wsm_buf);
+			break;
+		default:
+			//STUB();
+			break;
+		}
+	} else if (WSM_TO_MCU_CMD_IND_CONDITION(id, ind_confirm_label)) {
+		switch (id) {
+#ifdef CONFIG_BES2600_TESTMODE
+		case 0x0C25:
+			ret = wsm_vendor_rf_test_indication(hw_priv, &wsm_buf);
+			break;
+#endif /* CONFIG_BES2600_TESTMODE */
+		default:
+			break;
+		}
+	} else {
+		WARN_ON(1);
+		ret = -EINVAL;
+	}
+out:
+	return ret;
+}
+EXPORT_SYMBOL(wsm_handle_rx);
+
+static bool wsm_handle_tx_data(struct bes2600_vif *priv,
+				   const struct wsm_tx *wsm,
+				   const struct ieee80211_tx_info *tx_info,
+				   struct bes2600_txpriv *txpriv,
+				   struct bes2600_queue *queue)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+#ifdef P2P_MULTIVIF
+	struct bes2600_vif *p2p_if_vif = NULL;
+#endif
+	bool handled = false;
+	const struct ieee80211_hdr *frame =
+		(struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset];
+	__le16 fctl = frame->frame_control;
+	enum {
+		doProbe,
+		doDrop,
+		doJoin,
+		doOffchannel,
+		doWep,
+		doTx,
+	} action = doTx;
+
+	hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+#ifdef P2P_MULTIVIF
+	if (priv->if_id == CW12XX_GENERIC_IF_ID)
+		p2p_if_vif = __cw12xx_hwpriv_to_vifpriv(hw_priv, 2);
+#endif
+	frame =  (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset];
+	fctl  = frame->frame_control;
+
+	switch (priv->mode) {
+	case NL80211_IFTYPE_P2P_DEVICE:
+	case NL80211_IFTYPE_STATION:
+		if (unlikely((priv->join_status == BES2600_JOIN_STATUS_STA) &&
+			ieee80211_is_nullfunc(fctl))) {
+			spin_lock(&priv->bss_loss_lock);
+			if (priv->bss_loss_status == BES2600_BSS_LOSS_CHECKING) {
+				priv->bss_loss_status = BES2600_BSS_LOSS_CONFIRMING;
+				priv->bss_loss_confirm_id = wsm->packetID;
+			}
+			spin_unlock(&priv->bss_loss_lock);
+		} else if (unlikely(
+			(priv->join_status <= BES2600_JOIN_STATUS_MONITOR) ||
+			memcmp(frame->addr1, priv->join_bssid,
+				sizeof(priv->join_bssid)))) {
+#ifdef P2P_MULTIVIF
+			if (p2p_if_vif && (p2p_if_vif->join_status >
+				BES2600_JOIN_STATUS_MONITOR)
+					&& (priv->join_status
+						< BES2600_JOIN_STATUS_MONITOR)) {
+				/*
+					* Post group formation, frame transmission on p2p0
+					* interafce should not use offchannel/generic channel.
+					* Instead, the frame should be transmitted on interafce
+					* 1. This is needed by wsc fw.
+					*/
+				action = doTx;
+				txpriv->raw_if_id = 0;
+			} else
+#endif
+			if (ieee80211_is_auth(fctl))
+				action = doJoin;
+			else if (ieee80211_is_probe_req(fctl))
+				action = doTx;
+			else if (memcmp(frame->addr1, priv->join_bssid,
+					sizeof(priv->join_bssid)) &&
+					(priv->join_status ==
+					BES2600_JOIN_STATUS_STA) &&
+					(ieee80211_is_data(fctl))) {
+				action = doDrop;
+			}
+			else if (priv->join_status >=
+					BES2600_JOIN_STATUS_MONITOR)
+				action = doTx;
+			else if (get_interface_id_scanning(hw_priv) != -1) {
+				wiphy_warn(priv->hw->wiphy,
+					"Scan ONGOING dropping offchannel"
+					" eligible frame.\n");
+				action = doDrop;
+			}
+			else
+				action = doTx;
+		}
+		break;
+	case NL80211_IFTYPE_AP:
+		if (unlikely(!priv->join_status))
+			action = doDrop;
+		else if (unlikely(!(BIT(txpriv->raw_link_id) &
+				(BIT(0) | priv->link_id_map)))) {
+			wiphy_warn(priv->hw->wiphy,
+					"A frame with expired link id "
+					"is dropped.\n");
+			action = doDrop;
+		}
+		if (bes2600_queue_get_generation(wsm->packetID) >
+				BES2600_MAX_REQUEUE_ATTEMPTS) {
+			/* HACK!!! WSM324 firmware has tendency to requeue
+				* multicast frames in a loop, causing performance
+				* drop and high power consumption of the driver.
+				* In this situation it is better just to drop
+				* the problematic frame. */
+			wiphy_warn(priv->hw->wiphy,
+					"Too many attempts "
+					"to requeue a frame. "
+					"Frame is dropped.\n");
+			action = doDrop;
+		}
+		break;
+	case NL80211_IFTYPE_ADHOC:
+	case NL80211_IFTYPE_MESH_POINT:
+		//STUB();
+	case NL80211_IFTYPE_MONITOR:
+	default:
+		action = doDrop;
+		break;
+	}
+
+	if (action == doTx) {
+		if (unlikely(ieee80211_is_probe_req(fctl))) {
+#ifdef CONFIG_BES2600_TESTMODE
+			if (hw_priv->enable_advance_scan &&
+				(priv->join_status == BES2600_JOIN_STATUS_STA) &&
+				(hw_priv->advanceScanElems.scanMode ==
+					BES2600_SCAN_MEASUREMENT_ACTIVE))
+				/* If Advance Scan is Requested on Active Scan
+				 * then transmit the Probe Request */
+				action = doTx;
+			else
+#endif
+			action = doProbe;
+		} else if ((fctl & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) &&
+			tx_info->control.hw_key &&
+			unlikely(tx_info->control.hw_key->keyidx !=
+					priv->wep_default_key_id) &&
+			(tx_info->control.hw_key->cipher ==
+					WLAN_CIPHER_SUITE_WEP40 ||
+			 tx_info->control.hw_key->cipher ==
+					WLAN_CIPHER_SUITE_WEP104))
+			action = doWep;
+	}
+
+	switch (action) {
+	case doProbe:
+	{
+		/* An interesting FW "feature". Device filters
+		 * probe responses.
+		 * The easiest way to get it back is to convert
+		 * probe request into WSM start_scan command. */
+		bes2600_dbg(BES2600_DBG_WSM,
+			"[WSM] Convert probe request to scan.\n");
+		wsm_lock_tx_async(hw_priv);
+		hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID);
+		queue_delayed_work(hw_priv->workqueue,
+				&hw_priv->scan.probe_work, 0);
+		handled = true;
+	}
+	break;
+	case doDrop:
+	{
+		/* See detailed description of "join" below.
+		 * We are dropping everything except AUTH in non-joined mode. */
+		bes2600_err(BES2600_DBG_WSM, "[WSM] Drop frame (0x%.4X).\n", fctl);
+#ifdef CONFIG_BES2600_TESTMODE
+		BUG_ON(bes2600_queue_remove(hw_priv, queue,
+			__le32_to_cpu(wsm->packetID)));
+#else
+		BUG_ON(bes2600_queue_remove(queue,
+			__le32_to_cpu(wsm->packetID)));
+#endif /*CONFIG_BES2600_TESTMODE*/
+		handled = true;
+	}
+	break;
+	case doJoin:
+	{
+		/* There is one more interesting "feature"
+		 * in FW: it can't do RX/TX before "join".
+		 * "Join" here is not an association,
+		 * but just a syncronization between AP and STA.
+		 * priv->join_status is used only in bh thread and does
+		 * not require protection */
+		bes2600_info(BES2600_DBG_WSM, "[WSM] Issue join command.\n");
+		wsm_lock_tx_async(hw_priv);
+		hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID);
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0)
+		if (hw_priv->channel->band != NL80211_BAND_2GHZ)
+#else
+		if (hw_priv->channel->band != IEEE80211_BAND_2GHZ)
+#endif
+			bwifi_change_current_status(hw_priv, BWIFI_STATUS_CONNECTING_5G);
+		else
+			bwifi_change_current_status(hw_priv, BWIFI_STATUS_CONNECTING);
+#endif
+		if (queue_work(hw_priv->workqueue, &priv->join_work) <= 0)
+			wsm_unlock_tx(hw_priv);
+		handled = true;
+	}
+	break;
+	case doOffchannel:
+	{
+		bes2600_info(BES2600_DBG_WSM, "[WSM] Offchannel TX request.\n");
+		wsm_lock_tx_async(hw_priv);
+		hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID);
+		if (queue_work(hw_priv->workqueue, &priv->offchannel_work) <= 0)
+			wsm_unlock_tx(hw_priv);
+		handled = true;
+	}
+	break;
+	case doWep:
+	{
+		bes2600_info(BES2600_DBG_WSM,  "[WSM] Issue set_default_wep_key.\n");
+		wsm_lock_tx_async(hw_priv);
+		priv->wep_default_key_id = tx_info->control.hw_key->keyidx;
+		hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID);
+		if (queue_work(hw_priv->workqueue, &priv->wep_key_work) <= 0)
+			wsm_unlock_tx(hw_priv);
+		handled = true;
+	}
+	break;
+	case doTx:
+	{
+#if 0
+		/* Kept for history. If you want to implement wsm->more,
+		 * make sure you are able to send a frame after that. */
+		wsm->more = (count > 1) ? 1 : 0;
+		if (wsm->more) {
+			/* HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+			 * It's undocumented in WSM spec, but BES2600 hangs
+			 * if 'more' is set and no TX is performed due to TX
+			 * buffers limitation. */
+			if (priv->hw_bufs_used + 1 ==
+					priv->wsm_caps.numInpChBufs)
+				wsm->more = 0;
+		}
+
+		/* BUG!!! FIXME: we can't use 'more' at all: we don't know
+		 * future. It could be a request from upper layer with TX lock
+		 * requirements (scan, for example). If "more" is set device
+		 * will not send data and wsm_tx_lock() will fail...
+		 * It's not obvious how to fix this deadlock. Any ideas?
+		 * As a workaround more is set to 0. */
+		wsm->more = 0;
+#endif /* 0 */
+
+		if (ieee80211_is_deauth(fctl) &&
+				priv->mode != NL80211_IFTYPE_AP) {
+			/* Shedule unjoin work */
+			bes2600_info(BES2600_DBG_WSM, "[WSM] Issue unjoin command (TX).\n");
+			atomic_set(&priv->connect_in_process, 0);
+#if 0
+			wsm->more = 0;
+#endif /* 0 */
+
+#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
+			bwifi_change_current_status(hw_priv, BWIFI_STATUS_DISCONNECTING);
+#endif
+			wsm_lock_tx_async(hw_priv);
+			if (queue_work(hw_priv->workqueue,
+					&priv->unjoin_work) <= 0)
+				wsm_unlock_tx(hw_priv);
+		}
+	}
+	break;
+	}
+	return handled;
+}
+
+static int bes2600_get_prio_queue(struct bes2600_vif *priv,
+				 u32 link_id_map, int *total)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	static u32 urgent;
+	struct wsm_edca_queue_params *edca;
+	unsigned score, best = -1;
+	int winner = -1;
+	int queued;
+	int i;
+	urgent = BIT(priv->link_id_after_dtim) | BIT(priv->link_id_uapsd);
+
+	/* search for a winner using edca params */
+	for (i = 0; i < 4; ++i) {
+		queued = bes2600_queue_get_num_queued(priv,
+				&hw_priv->tx_queue[i],
+				link_id_map);
+		if (!queued)
+			continue;
+		*total += queued;
+		edca = &priv->edca.params[i];
+		score = ((edca->aifns + edca->cwMin) << 16) +
+				(edca->cwMax - edca->cwMin) *
+				(get_random_int() & 0xFFFF);
+		if (score < best && (winner < 0 || i != 3)) {
+			best = score;
+			winner = i;
+		}
+	}
+
+	/* override winner if bursting */
+	if (winner >= 0 && hw_priv->tx_burst_idx >= 0 &&
+			winner != hw_priv->tx_burst_idx &&
+			!bes2600_queue_get_num_queued(priv,
+				&hw_priv->tx_queue[winner],
+				link_id_map & urgent) &&
+			bes2600_queue_get_num_queued(priv,
+				&hw_priv->tx_queue[hw_priv->tx_burst_idx],
+				link_id_map))
+		winner = hw_priv->tx_burst_idx;
+
+	return winner;
+}
+
+static int wsm_get_tx_queue_and_mask(struct bes2600_vif *priv,
+					 struct bes2600_queue **queue_p,
+					 u32 *tx_allowed_mask_p,
+					 bool *more)
+{
+	struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
+	int idx;
+	u32 tx_allowed_mask;
+	int total = 0;
+
+	/* Search for a queue with multicast frames buffered */
+	if (priv->tx_multicast) {
+		tx_allowed_mask = BIT(priv->link_id_after_dtim);
+		idx = bes2600_get_prio_queue(priv,
+				tx_allowed_mask, &total);
+		if (idx >= 0) {
+			*more = total > 1;
+			goto found;
+		}
+	}
+
+	/* Search for unicast traffic */
+	tx_allowed_mask = ~priv->sta_asleep_mask;
+	tx_allowed_mask |= BIT(priv->link_id_uapsd);
+	if (priv->sta_asleep_mask) {
+		tx_allowed_mask |= priv->pspoll_mask;
+		tx_allowed_mask &= ~BIT(priv->link_id_after_dtim);
+	} else {
+		tx_allowed_mask |= BIT(priv->link_id_after_dtim);
+	}
+	idx = bes2600_get_prio_queue(priv,
+			tx_allowed_mask, &total);
+	if (idx < 0)
+		return -ENOENT;
+
+found:
+	*queue_p = &hw_priv->tx_queue[idx];
+	*tx_allowed_mask_p = tx_allowed_mask;
+	return 0;
+}
+
+int wsm_get_tx(struct bes2600_common *hw_priv, u8 **data,
+		   size_t *tx_len, int *burst, int *vif_selected)
+{
+	struct wsm_tx *wsm = NULL;
+	struct ieee80211_tx_info *tx_info;
+	struct bes2600_queue *queue = NULL;
+	int queue_num;
+	u32 tx_allowed_mask = 0;
+	struct bes2600_txpriv *txpriv = NULL;
+#ifdef P2P_MULTIVIF
+	int first = 1;
+#endif
+	/*
+	 * Count was intended as an input for wsm->more flag.
+	 * During implementation it was found that wsm->more
+	 * is not usable, see details above. It is kept just
+	 * in case you would like to try to implement it again.
+	 */
+	int count = 0;
+#ifdef P2P_MULTIVIF
+	int if_pending = CW12XX_MAX_VIFS - 1;
+#else
+	int if_pending = 1;
+#endif
+
+	/* More is used only for broadcasts. */
+	bool more = false;
+
+	count = bes2600_itp_get_tx(hw_priv, data, tx_len, burst);
+	if (count)
+		return count;
+
+	if (hw_priv->wsm_cmd.ptr) {
+		++count;
+		spin_lock(&hw_priv->wsm_cmd.lock);
+		BUG_ON(!hw_priv->wsm_cmd.ptr);
+		*data = hw_priv->wsm_cmd.ptr;
+		*tx_len = hw_priv->wsm_cmd.len;
+		*burst = 1;
+		*vif_selected = -1;
+		spin_unlock(&hw_priv->wsm_cmd.lock);
+	} else {
+		for (;;) {
+			int ret;
+			struct bes2600_vif *priv;
+#if 0
+			int num_pending_vif0, num_pending_vif1;
+#endif
+			if (atomic_add_return(0, &hw_priv->tx_lock))
+				break;
+			/* Keep one buffer reserved for commands. Note
+			   that, hw_bufs_used has already been incremented
+			   before reaching here. */
+			if (hw_priv->hw_bufs_used >=
+					hw_priv->wsm_caps.numInpChBufs)
+				break;
+#ifdef P2P_MULTIVIF
+			if (first) {
+				first = 0;
+				hw_priv->if_id_selected = 0;
+			}
+#endif
+			priv = wsm_get_interface_for_tx(hw_priv);
+			/* go to next interface ID to select next packet */
+#ifdef P2P_MULTIVIF
+			hw_priv->if_id_selected++;
+			if(hw_priv->if_id_selected > 2)
+				hw_priv->if_id_selected = 0;
+#else
+				hw_priv->if_id_selected ^= 1;
+#endif
+
+			/* There might be no interface before add_interface
+			 * call */
+			if (!priv) {
+				if (if_pending) {
+#ifdef P2P_MULTIVIF
+					if_pending--;
+#else
+					if_pending = 0;
+#endif
+					continue;
+				}
+				break;
+			}
+
+#if 0
+			if (((priv->if_id == 0) &&
+			(hw_priv->hw_bufs_used_vif[0] >=
+						CW12XX_FW_VIF0_THROTTLE)) ||
+			((priv->if_id == 1) &&
+			(hw_priv->hw_bufs_used_vif[1] >=
+						CW12XX_FW_VIF1_THROTTLE))) {
+				spin_unlock(&priv->vif_lock);
+				if (if_pending) {
+					if_pending = 0;
+					continue;
+				}
+				break;
+			}
+#endif
+
+			/* This can be removed probably: bes2600_vif will not
+			 * be in hw_priv->vif_list (as returned from
+			 * wsm_get_interface_for_tx) until it's fully
+			 * enabled, so statement above will take case of that*/
+			if (!atomic_read(&priv->enabled)) {
+				spin_unlock(&priv->vif_lock);
+				break;
+			}
+
+			/* TODO:COMBO: Find the next interface for which
+			* packet needs to be found */
+			spin_lock_bh(&priv->ps_state_lock);
+			ret = wsm_get_tx_queue_and_mask(priv, &queue,
+					&tx_allowed_mask, &more);
+			queue_num = queue - hw_priv->tx_queue;
+
+			if (priv->buffered_multicasts &&
+					(ret || !more) &&
+					(priv->tx_multicast ||
+					 !priv->sta_asleep_mask)) {
+				priv->buffered_multicasts = false;
+				if (priv->tx_multicast) {
+					priv->tx_multicast = false;
+					queue_work(hw_priv->workqueue,
+						&priv->multicast_stop_work);
+				}
+			}
+
+			spin_unlock_bh(&priv->ps_state_lock);
+
+			if (ret) {
+				spin_unlock(&priv->vif_lock);
+#ifdef P2P_MULTIVIF
+				if (if_pending) {
+#else
+				if (if_pending == 1) {
+#endif
+#ifdef P2P_MULTIVIF
+					if_pending--;
+#else
+					if_pending = 0;
+#endif
+					continue;
+				}
+				break;
+			}
+
+			if (bes2600_queue_get(queue,
+					priv->if_id,
+					tx_allowed_mask,
+					&wsm, &tx_info, &txpriv)) {
+				spin_unlock(&priv->vif_lock);
+				if_pending = 0;
+				continue;
+			}
+#ifndef P2P_MULTIVIF
+			{
+				struct ieee80211_hdr *hdr =
+				(struct ieee80211_hdr *)
+					&((u8 *)wsm)[txpriv->offset];
+
+				bes2600_dbg(BES2600_DBG_WSM, "QGET-1 %x, off_id %d,"
+						   " if_id %d\n",
+						hdr->frame_control,
+						txpriv->offchannel_if_id,
+						priv->if_id);
+			}
+#endif
+			if (wsm_handle_tx_data(priv, wsm,
+					tx_info, txpriv, queue)) {
+				spin_unlock(&priv->vif_lock);
+				if_pending = 0;
+				continue;  /* Handled by WSM */
+			}
+
+			wsm->hdr.id &= __cpu_to_le16(
+					~WSM_TX_IF_ID(WSM_TX_IF_ID_MAX));
+#ifdef P2P_MULTIVIF
+			if (txpriv->raw_if_id)
+				wsm->hdr.id |= cpu_to_le16(
+					WSM_TX_IF_ID(txpriv->raw_if_id));
+#else
+			if (txpriv->offchannel_if_id)
+				wsm->hdr.id |= cpu_to_le16(
+					WSM_TX_IF_ID(txpriv->offchannel_if_id));
+#endif
+			else
+				wsm->hdr.id |= cpu_to_le16(
+					WSM_TX_IF_ID(priv->if_id));
+
+			*vif_selected = priv->if_id;
+#ifdef ROC_DEBUG
+			{
+				struct ieee80211_hdr *hdr =
+				(struct ieee80211_hdr *)
+					&((u8 *)wsm)[txpriv->offset];
+
+				bes2600_dbg(BES2600_DBG_WSM, "QGET-2 %x, off_id %d,"
+						   " if_id %d\n",
+						hdr->frame_control,
+						txpriv->offchannel_if_id,
+						priv->if_id);
+			}
+#endif
+
+			priv->pspoll_mask &= ~BIT(txpriv->raw_link_id);
+
+			*data = (u8 *)wsm;
+			*tx_len = __le16_to_cpu(wsm->hdr.len);
+
+			/* allow bursting if txop is set */
+			if (priv->edca.params[queue_num].txOpLimit)
+				*burst = min(*burst,
+					(int)bes2600_queue_get_num_queued(priv,
+						queue, tx_allowed_mask) + 1);
+			else
+				*burst = 1;
+
+			/* store index of bursting queue */
+			if (*burst > 1)
+				hw_priv->tx_burst_idx = queue_num;
+			else
+				hw_priv->tx_burst_idx = -1;
+
+			if (more) {
+				struct ieee80211_hdr *hdr =
+					(struct ieee80211_hdr *)
+					&((u8 *)wsm)[txpriv->offset];
+				if(strstr(&priv->ssid[0], "6.1.12")) {
+					if(hdr->addr1[0] & 0x01 ) {
+						hdr->frame_control |=
+						cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+					}
+				}
+				else {
+					/* more buffered multicast/broadcast frames
+					*  ==> set MoreData flag in IEEE 802.11 header
+					*  to inform PS STAs */
+					hdr->frame_control |=
+					cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+				}
+			}
+			bes2600_dbg(BES2600_DBG_WSM, "[WSM] >>> 0x%.4X (%lu) %p %c\n",
+				0x0004, (long unsigned)*tx_len, *data,
+				wsm->more ? 'M' : ' ');
+			++count;
+			spin_unlock(&priv->vif_lock);
+			break;
+		}
+	}
+
+	return count;
+}
+
+void wsm_txed(struct bes2600_common *hw_priv, u8 *data)
+{
+	if (data == hw_priv->wsm_cmd.ptr) {
+		spin_lock(&hw_priv->wsm_cmd.lock);
+		hw_priv->wsm_cmd.ptr = NULL;
+		spin_unlock(&hw_priv->wsm_cmd.lock);
+	} else {
+		bes2600_pwr_set_busy_event_async(hw_priv, BES_PWR_LOCK_ON_LMAC_RSP);
+	}
+}
+
+/* ******************************************************************** */
+/* WSM buffer								*/
+
+void wsm_buf_init(struct wsm_buf *buf)
+{
+	BUG_ON(buf->begin);
+	buf->begin = kmalloc(SDIO_BLOCK_SIZE, GFP_KERNEL | GFP_DMA);
+	buf->end = buf->begin ? &buf->begin[SDIO_BLOCK_SIZE] : buf->begin;
+	wsm_buf_reset(buf);
+}
+
+void wsm_buf_deinit(struct wsm_buf *buf)
+{
+	kfree(buf->begin);
+	buf->begin = buf->data = buf->end = NULL;
+}
+
+static void wsm_buf_reset(struct wsm_buf *buf)
+{
+	if (buf->begin) {
+		buf->data = &buf->begin[4];
+		*(u32 *)buf->begin = 0;
+	} else
+		buf->data = buf->begin;
+}
+
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size)
+{
+	size_t pos = buf->data - buf->begin;
+	size_t size = pos + extra_size;
+
+
+	if (size & (SDIO_BLOCK_SIZE - 1)) {
+		size &= SDIO_BLOCK_SIZE;
+		size += SDIO_BLOCK_SIZE;
+	}
+
+	buf->begin = krealloc(buf->begin, size, GFP_KERNEL | GFP_DMA);
+	if (buf->begin) {
+		buf->data = &buf->begin[pos];
+		buf->end = &buf->begin[size];
+		return 0;
+	} else {
+		buf->end = buf->data = buf->begin;
+		return -ENOMEM;
+	}
+}
+
+static struct bes2600_vif
+	*wsm_get_interface_for_tx(struct bes2600_common *hw_priv)
+{
+	struct bes2600_vif *priv = NULL, *i_priv;
+	int i = hw_priv->if_id_selected;
+
+	if (is_hardware_cw1250(hw_priv) || 1 /*TODO:COMBO*/) {
+		spin_lock(&hw_priv->vif_list_lock);
+#if 0
+		bes2600_for_each_vif(hw_priv, i_priv, i) {
+			if (i_priv) {
+				priv = i_priv;
+				spin_lock(&priv->vif_lock);
+				break;
+			}
+		}
+#endif
+		i_priv = hw_priv->vif_list[i] ?
+			cw12xx_get_vif_from_ieee80211(hw_priv->vif_list[i]) : NULL;
+		if (i_priv) {
+			priv = i_priv;
+			spin_lock(&priv->vif_lock);
+		}
+		/* TODO:COMBO:
+		* Find next interface based on TX bitmap announced by the FW
+		* Find next interface based on load balancing */
+		spin_unlock(&hw_priv->vif_list_lock);
+	} else {
+		priv = cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
+	}
+
+	return priv;
+}
+
+static inline int get_interface_id_scanning(struct bes2600_common *hw_priv)
+{
+	if (hw_priv->scan.req)
+		return hw_priv->scan.if_id;
+	else
+		return -1;
+}
diff --git a/drivers/staging/bes2600/wsm.h b/drivers/staging/bes2600/wsm.h
new file mode 100644
index 000000000000..5bb5bace57ff
--- /dev/null
+++ b/drivers/staging/bes2600/wsm.h
@@ -0,0 +1,2211 @@
+/*
+ * WSM host interface (HI) interface for BES2600 mac80211 drivers
+ *
+ * Copyright (c) 2022, Bestechnic
+ * Author:
+ *
+ * Based on BES2600 UMAC WSM API, which is
+ * Copyright (C) SA 2010
+ * Author: Stewart Mathers <stewart.mathers@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef BES2600_WSM_H_INCLUDED
+#define BES2600_WSM_H_INCLUDED
+
+#include <linux/spinlock.h>
+
+struct bes2600_common;
+struct bes2600_vif;
+
+/* Bands */
+/* Radio band 2.412 -2.484 GHz. */
+#define WSM_PHY_BAND_2_4G		(0)
+
+/* Radio band 4.9375-5.8250 GHz. */
+#define WSM_PHY_BAND_5G			(1)
+
+/* Transmit rates */
+/* 1   Mbps            ERP-DSSS */
+#define WSM_TRANSMIT_RATE_1		(0)
+
+/* 2   Mbps            ERP-DSSS */
+#define WSM_TRANSMIT_RATE_2		(1)
+
+/* 5.5 Mbps            ERP-CCK, ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_5		(2) */
+
+/* 11  Mbps            ERP-CCK, ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_11		(3) */
+
+/* 22  Mbps            ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_22		(4) */
+
+/* 33  Mbps            ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_33		(5) */
+
+/* 6   Mbps   (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_6		(6)
+
+/* 9   Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_9		(7)
+
+/* 12  Mbps  (6 Mbps)  ERP-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_12		(8)
+
+/* 18  Mbps  (9 Mbps)  ERP-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_18		(9)
+
+/* 24  Mbps (12 Mbps)  ERP-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_24		(10)
+
+/* 36  Mbps (18 Mbps)  ERP-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_36		(11)
+
+/* 48  Mbps (24 Mbps)  ERP-OFDM, 64QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_48		(12)
+
+/* 54  Mbps (27 Mbps)  ERP-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_54		(13)
+
+/* 6.5 Mbps            HT-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_6		(14)
+
+/* 13  Mbps            HT-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_13		(15)
+
+/* 19.5 Mbps           HT-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_19		(16)
+
+/* 26  Mbps            HT-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_26		(17)
+
+/* 39  Mbps            HT-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_39		(18)
+
+/* 52  Mbps            HT-OFDM, 64QAM coding rate 2/3 */
+#define WSM_TRANSMIT_RATE_HT_52		(19)
+
+/* 58.5 Mbps           HT-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_58		(20)
+
+/* 65  Mbps            HT-OFDM, 64QAM coding rate 5/6 */
+#define WSM_TRANSMIT_RATE_HT_65		(21)
+
+/* Scan types */
+/* Foreground scan */
+#define WSM_SCAN_TYPE_FOREGROUND	(0)
+
+/* Background scan */
+#define WSM_SCAN_TYPE_BACKGROUND	(1)
+
+/* Auto scan */
+#define WSM_SCAN_TYPE_AUTO		(2)
+
+/* Scan flags */
+/* Forced background scan means if the station cannot */
+/* enter the power-save mode, it shall force to perform a */
+/* background scan. Only valid when ScanType is */
+/* background scan. */
+#define WSM_SCAN_FLAG_FORCE_BACKGROUND	(BIT(0))
+
+/* The WLAN device scans one channel at a time so */
+/* that disturbance to the data traffic is minimized. */
+#define WSM_SCAN_FLAG_SPLIT_METHOD	(BIT(1))
+
+/* Preamble Type. Long if not set. */
+#define WSM_SCAN_FLAG_SHORT_PREAMBLE	(BIT(2))
+
+/* 11n Tx Mode. Mixed if not set. */
+#define WSM_SCAN_FLAG_11N_GREENFIELD	(BIT(3))
+
+#define WSM_FLAG_MAC_INSTANCE_1	(BIT(4))
+
+#define WSM_FLAG_MAC_INSTANCE_0	(~(BIT(4)))
+
+/* Scan constraints */
+/* Maximum number of channels to be scanned. */
+#define WSM_SCAN_MAX_NUM_OF_CHANNELS	(48)
+
+/* The maximum number of SSIDs that the device can scan for. */
+#define WSM_SCAN_MAX_NUM_OF_SSIDS	(2)
+#ifdef CONFIG_BES2600_TESTMODE
+/* Transmit flags */
+/* Start Expiry time from the receipt of tx request */
+#define WSM_TX_FLAG_EXPIRY_TIME		(BIT(0))
+#endif /*CONFIG_BES2600_TESTMODE*/
+
+/* Power management modes */
+/* 802.11 Active mode */
+#define WSM_PSM_ACTIVE			(0)
+
+/* 802.11 PS mode */
+#define WSM_PSM_PS			BIT(0)
+
+/* Fast Power Save bit */
+#define WSM_PSM_FAST_PS_FLAG		BIT(7)
+
+/* Dynamic aka Fast power save */
+#define WSM_PSM_FAST_PS			(BIT(0) | BIT(7))
+
+/* Undetermined */
+/* Note : Undetermined status is reported when the */
+/* NULL data frame used to advertise the PM mode to */
+/* the AP at Pre or Post Background Scan is not Acknowledged */
+#define WSM_PSM_UNKNOWN			BIT(1)
+
+/* Queue IDs */
+/* best effort/legacy */
+#define WSM_QUEUE_BEST_EFFORT		(0)
+
+/* background */
+#define WSM_QUEUE_BACKGROUND		(1)
+
+/* video */
+#define WSM_QUEUE_VIDEO			(2)
+
+/* voice */
+#define WSM_QUEUE_VOICE			(3)
+
+/* HT TX parameters */
+/* Non-HT */
+#define WSM_HT_TX_NON_HT		(0)
+
+/* Mixed format */
+#define WSM_HT_TX_MIXED			(1)
+
+/* Greenfield format */
+#define WSM_HT_TX_GREENFIELD		(2)
+
+/* STBC allowed */
+#define WSM_HT_TX_STBC			(BIT(7))
+
+/* EPTA prioirty flags for BT Coex */
+/* default epta priority */
+#define WSM_EPTA_PRIORITY_DEFAULT	4
+/* use for normal data */
+#define WSM_EPTA_PRIORITY_DATA		4
+/* use for connect/disconnect/roaming*/
+#define WSM_EPTA_PRIORITY_MGT		5
+/* use for action frames */
+#define WSM_EPTA_PRIORITY_ACTION	5
+/* use for AC_VI data */
+#define WSM_EPTA_PRIORITY_VIDEO		5
+/* use for AC_VO data */
+#define WSM_EPTA_PRIORITY_VOICE		6
+/* use for EAPOL exchange */
+#define WSM_EPTA_PRIORITY_EAPOL		7
+
+/* TX status */
+/* Frame was sent aggregated */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_AGGREGATION	(BIT(0))
+
+/* Host should requeue this frame later. */
+/* Valid only when status is WSM_REQUEUE. */
+#define WSM_TX_STATUS_REQUEUE		(BIT(1))
+
+/* Normal Ack */
+#define WSM_TX_STATUS_NORMAL_ACK	(0<<2)
+
+/* No Ack */
+#define WSM_TX_STATUS_NO_ACK		(1<<2)
+
+/* No explicit acknowledgement */
+#define WSM_TX_STATUS_NO_EXPLICIT_ACK	(2<<2)
+
+/* Block Ack */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_BLOCK_ACK		(3<<2)
+
+/* RX status */
+/* Unencrypted */
+#define WSM_RX_STATUS_UNENCRYPTED	(0<<0)
+
+/* WEP */
+#define WSM_RX_STATUS_WEP		(1<<0)
+
+/* TKIP */
+#define WSM_RX_STATUS_TKIP		(2<<0)
+
+/* AES */
+#define WSM_RX_STATUS_AES		(3<<0)
+
+/* WAPI */
+#define WSM_RX_STATUS_WAPI		(4<<0)
+
+/* DECRYPTED */
+#define WSM_RX_STATUS_DECRYPTED		(7<<0)
+
+/* Macro to fetch encryption subfield. */
+#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07)
+
+/* Frame was part of an aggregation */
+#define WSM_RX_STATUS_AGGREGATE		(BIT(3))
+
+/* Frame was first in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_FIRST	(BIT(4))
+
+/* Frame was last in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_LAST	(BIT(5))
+
+/* Indicates a defragmented frame */
+#define WSM_RX_STATUS_DEFRAGMENTED	(BIT(6))
+
+/* Indicates a Beacon frame */
+#define WSM_RX_STATUS_BEACON		(BIT(7))
+
+/* Indicates STA bit beacon TIM field */
+#define WSM_RX_STATUS_TIM		(BIT(8))
+
+/* Indicates Beacon frame's virtual bitmap contains multicast bit */
+#define WSM_RX_STATUS_MULTICAST		(BIT(9))
+
+/* Indicates frame contains a matching SSID */
+#define WSM_RX_STATUS_MATCHING_SSID	(BIT(10))
+
+/* Indicates frame contains a matching BSSI */
+#define WSM_RX_STATUS_MATCHING_BSSI	(BIT(11))
+
+/* Indicates More bit set in Framectl field */
+#define WSM_RX_STATUS_MORE_DATA		(BIT(12))
+
+/* Indicates frame received during a measurement process */
+#define WSM_RX_STATUS_MEASUREMENT	(BIT(13))
+
+/* Indicates frame received as an HT packet */
+#define WSM_RX_STATUS_HT		(BIT(14))
+
+/* Indicates frame received with STBC */
+#define WSM_RX_STATUS_STBC		(BIT(15))
+
+/* Indicates Address 1 field matches dot11StationId */
+#define WSM_RX_STATUS_ADDRESS1		(BIT(16))
+
+/* Indicates Group address present in the Address 1 field */
+#define WSM_RX_STATUS_GROUP		(BIT(17))
+
+/* Indicates Broadcast address present in the Address 1 field */
+#define WSM_RX_STATUS_BROADCAST		(BIT(18))
+
+/* Indicates group key used with encrypted frames */
+#define WSM_RX_STATUS_GROUP_KEY		(BIT(19))
+
+/* Macro to fetch encryption key index. */
+#define WSM_RX_STATUS_KEY_IDX(status)	(((status >> 20)) & 0x0F)
+
+/* Frame Control field starts at Frame offset + 2 */
+#define WSM_TX_2BYTES_SHIFT		(BIT(7))
+
+/* Join mode */
+/* IBSS */
+#define WSM_JOIN_MODE_IBSS		(0)
+
+/* BSS */
+#define WSM_JOIN_MODE_BSS		(1)
+
+/* PLCP preamble type */
+/* For long preamble */
+#define WSM_JOIN_PREAMBLE_LONG		(0)
+
+/* For short preamble (Long for 1Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT		(1)
+
+/* For short preamble (Long for 1 and 2Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT_2	(2)
+
+/* Join flags */
+/* Unsynchronized */
+#define WSM_JOIN_FLAGS_UNSYNCRONIZED	BIT(0)
+/* The BSS owner is a P2P GO */
+#define WSM_JOIN_FLAGS_P2P_GO		BIT(1)
+/* Force to join BSS with the BSSID and the
+ * SSID specified without waiting for beacons. The
+ * ProbeForJoin parameter is ignored. */
+#define WSM_JOIN_FLAGS_FORCE		BIT(2)
+/* Give probe request/response higher
+ * priority over the BT traffic */
+#define WSM_JOIN_FLAGS_PRIO		BIT(3)
+
+/* Key types */
+#define WSM_KEY_TYPE_WEP_DEFAULT	(0)
+#define WSM_KEY_TYPE_WEP_PAIRWISE	(1)
+#define WSM_KEY_TYPE_TKIP_GROUP		(2)
+#define WSM_KEY_TYPE_TKIP_PAIRWISE	(3)
+#define WSM_KEY_TYPE_AES_GROUP		(4)
+#define WSM_KEY_TYPE_AES_PAIRWISE	(5)
+#define WSM_KEY_TYPE_WAPI_GROUP		(6)
+#define WSM_KEY_TYPE_WAPI_PAIRWISE	(7)
+#define WSM_KEY_TYPE_IGTK_GROUP		(8)
+
+/* Key indexes */
+#define CW1250_WSM_KEY_MAX_INDEX	(15)
+#define WSM_KEY_MAX_INDEX		(10)
+
+/* ACK policy */
+#define WSM_ACK_POLICY_NORMAL		(0)
+#define WSM_ACK_POLICY_NO_ACK		(1)
+
+/* Start modes */
+#define WSM_START_MODE_AP		(0)	/* Mini AP */
+#define WSM_START_MODE_P2P_GO		(1)	/* P2P GO */
+#define WSM_START_MODE_P2P_DEV		(2)	/* P2P device */
+
+/* SetAssociationMode MIB flags */
+#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE		(BIT(0))
+#define WSM_ASSOCIATION_MODE_USE_HT_MODE		(BIT(1))
+#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET		(BIT(2))
+#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING	(BIT(3))
+#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES		(BIT(4))
+
+/* RcpiRssiThreshold MIB flags */
+#define WSM_RCPI_RSSI_THRESHOLD_ENABLE	(BIT(0))
+#define WSM_RCPI_RSSI_USE_RSSI		(BIT(1))
+#define WSM_RCPI_RSSI_DONT_USE_UPPER	(BIT(2))
+#define WSM_RCPI_RSSI_DONT_USE_LOWER	(BIT(3))
+
+/* Update-ie constants */
+#define WSM_UPDATE_IE_BEACON		(BIT(0))
+#define WSM_UPDATE_IE_PROBE_RESP	(BIT(1))
+#define WSM_UPDATE_IE_PROBE_REQ		(BIT(2))
+
+/* PS Mode Error */
+#define WSM_PS_ERROR_NO_ERROR	                    0
+#define WSM_PS_ERROR_AP_NOT_RESP_TO_POLL	    1
+#define WSM_PS_ERROR_AP_NOT_RESP_TO_UAPSD_TRIGGER   2
+#define WSM_PS_ERROR_AP_SENT_UNICAST_IN_DOZE        3
+#define WSM_PS_ERROR_AP_NO_DATA_AFTER_TIM           4
+
+/* WSM events */
+/* Error */
+#define WSM_EVENT_ERROR			(0)
+
+/* BSS lost */
+#define WSM_EVENT_BSS_LOST		(1)
+
+/* BSS regained */
+#define WSM_EVENT_BSS_REGAINED		(2)
+
+/* Radar detected */
+#define WSM_EVENT_RADAR_DETECTED	(3)
+
+/* RCPI or RSSI threshold triggered */
+#define WSM_EVENT_RCPI_RSSI		(4)
+
+/* BT inactive */
+#define WSM_EVENT_BT_INACTIVE		(5)
+
+/* BT active */
+#define WSM_EVENT_BT_ACTIVE		(6)
+
+#define WSM_EVENT_PS_MODE_ERROR         (7)
+
+#define WSM_EVENT_INACTIVITY		(9)
+
+#define WSM_EVENT_WAKEUP_EVENT		(10)
+
+/* MAC Addr Filter */
+#define WSM_MIB_ID_MAC_ADDR_FILTER	0x1030
+
+/* MIB IDs */
+/* 4.1  dot11StationId */
+#define WSM_MIB_ID_DOT11_STATION_ID		0x0000
+
+/* 4.2  dot11MaxtransmitMsduLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME	0x0001
+
+/* 4.3  dot11MaxReceiveLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME	0x0002
+
+/* 4.4  dot11SlotTime */
+#define WSM_MIB_ID_DOT11_SLOT_TIME		0x0003
+
+/* 4.5  dot11GroupAddressesTable */
+#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE	0x0004
+#define WSM_MAX_GRP_ADDRTABLE_ENTRIES		8
+
+/* 4.6  dot11WepDefaultKeyId */
+#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID	0x0005
+
+/* 4.7  dot11CurrentTxPowerLevel */
+#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL	0x0006
+
+/* 4.8  dot11RTSThreshold */
+#define WSM_MIB_ID_DOT11_RTS_THRESHOLD		0x0007
+
+/* 4.9  NonErpProtection */
+#define WSM_MIB_ID_NON_ERP_PROTECTION		0x1000
+
+/* 4.10 ArpIpAddressesTable */
+#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE	0x1001
+#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES	1
+
+/* 4.11 TemplateFrame */
+#define WSM_MIB_ID_TEMPLATE_FRAME		0x1002
+
+/* 4.12 RxFilter */
+#define WSM_MIB_ID_RX_FILTER			0x1003
+
+/* 4.13 BeaconFilterTable */
+#define WSM_MIB_ID_BEACON_FILTER_TABLE		0x1004
+
+/* 4.14 BeaconFilterEnable */
+#define WSM_MIB_ID_BEACON_FILTER_ENABLE		0x1005
+
+/* 4.15 OperationalPowerMode */
+#define WSM_MIB_ID_OPERATIONAL_POWER_MODE	0x1006
+
+/* 4.16 BeaconWakeUpPeriod */
+#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD		0x1007
+
+/* 4.17 RcpiRssiThreshold */
+#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD		0x1009
+
+/* 4.18 StatisticsTable */
+#define WSM_MIB_ID_STATISTICS_TABLE		0x100A
+
+/* 4.19 IbssPsConfig */
+#define WSM_MIB_ID_IBSS_PS_CONFIG		0x100B
+
+/* 4.20 CountersTable */
+#define WSM_MIB_ID_COUNTERS_TABLE		0x100C
+
+/* 4.21 BlockAckPolicy */
+#define WSM_MIB_ID_BLOCK_ACK_POLICY		0x100E
+
+/* 4.22 OverrideInternalTxRate */
+#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE	0x100F
+
+/* 4.23 SetAssociationMode */
+#define WSM_MIB_ID_SET_ASSOCIATION_MODE		0x1010
+
+/* 4.24 UpdateEptaConfigData */
+#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA	0x1011
+
+/* 4.25 SelectCcaMethod */
+#define WSM_MIB_ID_SELECT_CCA_METHOD		0x1012
+
+/* 4.26 SetUpasdInformation */
+#define WSM_MIB_ID_SET_UAPSD_INFORMATION	0x1013
+
+/* 4.27 SetAutoCalibrationMode  WBF00004073 */
+#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE	0x1015
+
+/* 4.28 SetTxRateRetryPolicy */
+#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY	0x1016
+
+/* 4.29 SetHostMessageTypeFilter */
+#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER	0x1017
+
+/* 4.30 P2PFindInfo */
+#define WSM_MIB_ID_P2P_FIND_INFO		0x1018
+
+/* 4.31 P2PPsModeInfo */
+#define WSM_MIB_ID_P2P_PS_MODE_INFO		0x1019
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A
+
+/* 4.33 SetUDPPortDataFrameFilter */
+#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER	0x101B
+
+/* 4.34 SetMagicDataFrameFilter */
+#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER	0x101C
+
+/* This is the end of specification. */
+
+/* 4.35 P2PDeviceInfo */
+#define WSM_MIB_ID_P2P_DEVICE_INFO		0x101D
+
+/* 4.36 SetWCDMABand */
+#define WSM_MIB_ID_SET_WCDMA_BAND		0x101E
+
+/* 4.37 GroupTxSequenceCounter */
+#define WSM_MIB_ID_GRP_SEQ_COUNTER		0x101F
+
+/* 4.38 ProtectedMgmtPolicy */
+#define WSM_MIB_ID_PROTECTED_MGMT_POLICY	0x1020
+
+/* 4.39 SetHtProtection */
+#define WSM_MID_ID_SET_HT_PROTECTION		0x1021
+
+/* 4.40 GPIO Command */
+#define WSM_MIB_ID_GPIO_COMMAND			0x1022
+
+/* 4.41 TSF Counter Value */
+#define WSM_MIB_ID_TSF_COUNTER			0x1023
+
+/* Test Purposes Only */
+#define WSM_MIB_ID_BLOCK_ACK_INFO		0x100D
+
+/* 4.42 UseMultiTxConfMessage */
+#define WSM_MIB_USE_MULTI_TX_CONF		0x1024
+
+/* 4.43 Keep-alive period */
+#define WSM_MIB_ID_KEEP_ALIVE_PERIOD		0x1025
+
+/* 4.44 Disable BSSID filter */
+#define WSM_MIB_ID_DISABLE_BSSID_FILTER		0x1026
+
+/* Inactivity */
+#define WSM_MIB_ID_SET_INACTIVITY		0x1035
+
+/* MAC Addr Filter */
+#define WSM_MIB_ID_MAC_ADDR_FILTER		0x1030
+
+/* IP Addr Filter */
+#define WSM_MIB_ID_IPV4_ADDR_FILTERING           0x1031
+
+#ifdef MCAST_FWDING
+/* 4.51 Set Forwarding Offload */
+#define WSM_MIB_ID_FORWARDING_OFFLOAD		0x1033
+#endif
+
+#define WSM_MIB_ID_EXT_BASE                         0x1040
+#define WSM_MIB_ID_EXT_RF_ENABLE                    (WSM_MIB_ID_EXT_BASE + 0)  /* Mib to set RF on/off */
+#define WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_FRAME         (WSM_MIB_ID_EXT_BASE + 1)  /* Mib to set tcp keep alive frame */
+#define WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_PERIOD        (WSM_MIB_ID_EXT_BASE + 2)  /* Mib to set tcp keep alive period */
+#define WSM_MIB_ID_EXT_TX_SHORT_GI_ENABLED          (WSM_MIB_ID_EXT_BASE + 3)  /* Mib to set tx short enabled */
+#define WSM_MIB_ID_EXT_TXRX_OPT_PARAM               (WSM_MIB_ID_EXT_BASE + 4)  /* Mib to set tx/rx opt param to enhance wifi throuhput */
+
+
+#ifdef IPV6_FILTERING
+/* IpV6 Addr Filter */
+/* 4.52 Neighbor solicitation IPv6 address table */
+#define WSM_MIB_IP_IPV6_ADDR_FILTER		0x1032
+#define WSM_MIB_ID_NS_IP_ADDRESSES_TABLE	0x1034
+#define WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES	1
+#endif /*IPV6_FILTERING*/
+
+/* Frame template types */
+#define WSM_FRAME_TYPE_PROBE_REQUEST	(0)
+#define WSM_FRAME_TYPE_BEACON		(1)
+#define WSM_FRAME_TYPE_NULL		(2)
+#define WSM_FRAME_TYPE_QOS_NULL		(3)
+#define WSM_FRAME_TYPE_PS_POLL		(4)
+#define WSM_FRAME_TYPE_PROBE_RESPONSE	(5)
+#define WSM_FRAME_TYPE_ARP_REPLY        (6)
+
+#ifdef IPV6_FILTERING
+#define WSM_FRAME_TYPE_NA               (7)
+#endif /*IPV6_FILTERING*/
+
+#define WSM_FRAME_GREENFIELD		(0x80)	/* See 4.11 */
+
+/* Status */
+/* The WSM firmware has completed a request */
+/* successfully. */
+#define WSM_STATUS_SUCCESS              (0)
+
+/* This is a generic failure code if other error codes do */
+/* not apply. */
+#define WSM_STATUS_FAILURE              (1)
+
+/* A request contains one or more invalid parameters. */
+#define WSM_INVALID_PARAMETER           (2)
+
+/* The request cannot perform because the device is in */
+/* an inappropriate mode. */
+#define WSM_ACCESS_DENIED               (3)
+
+/* The frame received includes a decryption error. */
+#define WSM_STATUS_DECRYPTFAILURE       (4)
+
+/* A MIC failure is detected in the received packets. */
+#define WSM_STATUS_MICFAILURE           (5)
+
+/* The transmit request failed due to retry limit being */
+/* exceeded. */
+#define WSM_STATUS_RETRY_EXCEEDED       (6)
+
+/* The transmit request failed due to MSDU life time */
+/* being exceeded. */
+#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7)
+
+/* The link to the AP is lost. */
+#define WSM_STATUS_LINK_LOST            (8)
+
+/* No key was found for the encrypted frame */
+#define WSM_STATUS_NO_KEY_FOUND         (9)
+
+/* Jammer was detected when transmitting this frame */
+#define WSM_STATUS_JAMMER_DETECTED      (10)
+
+/* The message should be requeued later. */
+/* This is applicable only to Transmit */
+#define WSM_REQUEUE                     (11)
+
+/* Advanced filtering options */
+#define WSM_MAX_FILTER_ELEMENTS		(4)
+
+#define WSM_FILTER_ACTION_IGNORE	(0)
+#define WSM_FILTER_ACTION_FILTER_OUT	(1)
+#define WSM_FILTER_ACTION_FILTER_IN	(2)
+
+#define WSM_FILTER_ADDR_MODE_NONE	(0)
+#define WSM_FILTER_ADDR_MODE_A1		(1)
+#define WSM_FILTER_ADDR_MODE_A2		(2)
+#define WSM_FILTER_ADDR_MODE_A3		(3)
+
+#define WSM_FILTER_PORT_TYPE_DST	(0)
+#define WSM_FILTER_PORT_TYPE_SRC	(1)
+
+
+
+struct wsm_hdr {
+	__le16 len;
+	__le16 id;
+};
+
+/* mcu -> driver confirm or indication hdr */
+struct wsm_mcu_hdr {
+	struct wsm_hdr hdr;
+	uint32_t m_reserve; /* reserved member variable */
+	uint32_t handle_label; /* confirm or indication */
+	uint32_t cmd_type;
+};
+
+#define WSM_MSG_ID_MASK		        (0x0C3F)
+#define WSM_MSG_ID_GET(x)		((x) & WSM_MSG_ID_MASK)
+#define WSM_TX_SEQ_MAX			(7)
+#define WSM_TX_SEQ(seq)			\
+		((seq & WSM_TX_SEQ_MAX) << 13)
+#define WSM_MSG_SEQ_GET(x)		(((x) >> 13) & WSM_TX_SEQ_MAX)
+#define WSM_BES2600_CMD_ID_LABLE		(0x0C00)
+#define IS_DRIVER_TO_MCU_CMD(id)		((id & WSM_BES2600_CMD_ID_LABLE) == WSM_BES2600_CMD_ID_LABLE)
+#define WSM_TXRX_SEQ_IDX(id)				\
+		(((id & 0xF00) == WSM_BES2600_CMD_ID_LABLE) ? 1 : 0)
+#define WSM_TO_MCU_CMD_CONFIRM_LABEL		(0x0f)
+#define WSM_TO_MCU_CMD_INDICATION_LABEL		(0xf0)
+
+#define WSM_CONFIRM_CONDITION(id, confirm_label) (((id & 0x0f00) == 0x0400 || \
+		(((id & 0x0f00) == WSM_BES2600_CMD_ID_LABLE) && confirm_label == WSM_TO_MCU_CMD_CONFIRM_LABEL)))
+
+#define WSM_TO_MCU_CMD_IND_CONDITION(id, ind_label) (((id & 0x0f00) == WSM_BES2600_CMD_ID_LABLE) && \
+		ind_label == WSM_TO_MCU_CMD_INDICATION_LABEL)
+
+#define WSM_TX_LINK_ID_MAX		(0x0F)
+#define WSM_TX_LINK_ID(link_id)		\
+		((link_id & WSM_TX_LINK_ID_MAX) << 6)
+
+#define WSM_TX_IF_ID_MAX		(0x0F)
+#define WSM_TX_IF_ID(if_id)		\
+		((if_id & WSM_TX_IF_ID_MAX) << 6)
+
+#define MAX_BEACON_SKIP_TIME_MS 1000
+
+#ifdef FPGA_SETUP
+#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 9 / 2)
+#else
+#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 20 / 2)
+#endif
+#define WSM_CMD_EXTENDED_TIMEOUT (HZ * 20 / 2)
+
+#define WSM_RI_GET_PEER_ID_FROM_FLAGS(_f)         (((_f)&(0xF<<25)>>25))
+
+
+/* ******************************************************************** */
+/* WSM capcbility							*/
+
+struct wsm_caps {
+	u16 numInpChBufs;
+	u16 sizeInpChBuf;
+	u16 hardwareId;
+	u16 hardwareSubId;
+	u16 firmwareCap;
+	u16 firmwareType;
+	u16 firmwareApiVer;
+	u16 firmwareBuildNumber;
+	u16 firmwareVersion;
+	int firmwareReady;
+};
+
+/* ******************************************************************** */
+/* WSM commands								*/
+
+struct wsm_tx_power_range {
+	int min_power_level;
+	int max_power_level;
+	u32 stepping;
+};
+
+/* 3.1 */
+struct wsm_configuration {
+	/* [in] */ u32 dot11MaxTransmitMsduLifeTime;
+	/* [in] */ u32 dot11MaxReceiveLifeTime;
+	/* [in] */ u32 dot11RtsThreshold;
+	/* [in, out] */ u8 *dot11StationId;
+	/* [in] */ const void *dpdData;
+	/* [in] */ size_t dpdData_size;
+	/* [out] */ u8 dot11FrequencyBandsSupported;
+	/* [out] */ u32 supportedRateMask;
+	/* [out] */ struct wsm_tx_power_range txPowerRange[2];
+};
+
+int wsm_configuration(struct bes2600_common *hw_priv,
+		      struct wsm_configuration *arg,
+		      int if_id);
+
+/* 3.3 */
+struct wsm_reset {
+	/* [in] */ int link_id;
+	/* [in] */ bool reset_statistics;
+};
+
+int wsm_reset(struct bes2600_common *hw_priv, const struct wsm_reset *arg,
+	      int if_id);
+
+/* 3.5 */
+int wsm_read_mib(struct bes2600_common *hw_priv, u16 mibId, void *buf,
+		 size_t buf_size);
+
+/* 3.7 */
+int wsm_write_mib(struct bes2600_common *hw_priv, u16 mibId, void *buf,
+		  size_t buf_size, int if_id);
+
+/* 3.9 */
+struct wsm_ssid {
+	u8 ssid[32];
+	u32 length;
+};
+
+struct wsm_scan_ch {
+	u16 number;
+	u32 minChannelTime;
+	u32 maxChannelTime;
+	u32 txPowerLevel;
+};
+
+/* 3.13 */
+struct wsm_scan_complete {
+	/* WSM_STATUS_... */
+	u32 status;
+
+	/* WSM_PSM_... */
+	u8 psm;
+
+	/* Number of channels that the scan operation completed. */
+	u8 numChannels;
+#ifdef ROAM_OFFLOAD
+	u16 reserved;
+#endif /*ROAM_OFFLOAD*/
+};
+
+typedef void (*wsm_scan_complete_cb) (struct bes2600_common *hw_priv,
+				      struct wsm_scan_complete *arg);
+
+/* 3.9 */
+struct wsm_scan {
+	/* WSM_PHY_BAND_... */
+	/* [in] */ u8 band;
+
+	/* WSM_SCAN_TYPE_... */
+	/* [in] */ u8 scanType;
+
+	/* WSM_SCAN_FLAG_... */
+	/* [in] */ u8 scanFlags;
+
+	/* WSM_TRANSMIT_RATE_... */
+	/* [in] */ u8 maxTransmitRate;
+
+	/* Interval period in TUs that the device shall the re- */
+	/* execute the requested scan. Max value supported by the device */
+	/* is 256s. */
+	/* [in] */ u32 autoScanInterval;
+
+	/* Number of probe requests (per SSID) sent to one (1) */
+	/* channel. Zero (0) means that none is send, which */
+	/* means that a passive scan is to be done. Value */
+	/* greater than zero (0) means that an active scan is to */
+	/* be done. */
+	/* [in] */ u32 numOfProbeRequests;
+
+	/* Number of channels to be scanned. */
+	/* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */
+	/* [in] */ u8 numOfChannels;
+
+	/* Number of SSID provided in the scan command (this */
+	/* is zero (0) in broadcast scan) */
+	/* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */
+	/* [in] */ u8 numOfSSIDs;
+
+	/* The delay time (in microseconds) period */
+	/* before sending a probe-request. */
+	/* [in] */ u8 probeDelay;
+
+	/* SSIDs to be scanned [numOfSSIDs]; */
+	/* [in] */ struct wsm_ssid *ssids;
+
+	/* Channels to be scanned [numOfChannels]; */
+	/* [in] */ struct wsm_scan_ch *ch;
+};
+
+int wsm_scan(struct bes2600_common *hw_priv, const struct wsm_scan *arg,
+			int if_id);
+
+/* 3.11 */
+int wsm_stop_scan(struct bes2600_common *hw_priv, int if_id);
+
+/* 3.14 */
+struct wsm_tx_confirm {
+	/* Packet identifier used in wsm_tx. */
+	/* [out] */ u32 packetID;
+
+	/* WSM_STATUS_... */
+	/* [out] */ u32 status;
+
+	/* WSM_TRANSMIT_RATE_... */
+	/* [out] */ u8 txedRate;
+
+	/* The number of times the frame was transmitted */
+	/* without receiving an acknowledgement. */
+	/* [out] */ u8 ackFailures;
+
+	/* WSM_TX_STATUS_... */
+	/* [out] */ u16 flags;
+
+	/* The total time in microseconds that the frame spent in */
+	/* the WLAN device before transmission as completed. */
+	/* [out] */ u32 mediaDelay;
+
+	/* The total time in microseconds that the frame spent in */
+	/* the WLAN device before transmission was started. */
+	/* [out] */ u32 txQueueDelay;
+
+	/* [out]*/ u32 link_id;
+
+	/*[out]*/ int if_id;
+};
+
+/* 3.15 */
+typedef void (*wsm_tx_confirm_cb) (struct bes2600_common *hw_priv,
+				   struct wsm_tx_confirm *arg);
+
+/* Note that ideology of wsm_tx struct is different against the rest of
+ * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input
+ * argument for WSM call, but a prepared bytestream to be sent to firmware.
+ * It is filled partly in bes2600_tx, partly in low-level WSM code.
+ * Please pay attention once again: ideology is different.
+ *
+ * Legend:
+ * - [in]: bes2600_tx must fill this field.
+ * - [wsm]: the field is filled by low-level WSM.
+ */
+struct wsm_tx {
+	/* common WSM header */
+	/* [in/wsm] */ struct wsm_hdr hdr;
+
+	/* Packet identifier that meant to be used in completion. */
+	/* [in] */ __le32 packetID;
+
+	/* WSM_TRANSMIT_RATE_... */
+	/* [in] */ u8 maxTxRate;
+
+	/* WSM_QUEUE_... */
+	/* [in] */ u8 queueId;
+
+	/* True: another packet is pending on the host for transmission. */
+	/* [wsm] */ u8 more;
+
+	/* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */
+	/* Bit 0 = 1 - Start expiry time from receipt of Tx Request */
+	/* Bits 3:1  - PTA Priority */
+	/* Bits 6:4  - Tx Rate Retry Policy */
+	/* Bit 7 - Reserved */
+	/* [in] */ u8 flags;
+
+	/* Should be 0. */
+	/* [in] */ __le32 reserved;
+
+	/* The elapsed time in TUs, after the initial transmission */
+	/* of an MSDU, after which further attempts to transmit */
+	/* the MSDU shall be terminated. Overrides the global */
+	/* dot11MaxTransmitMsduLifeTime setting [optional] */
+	/* Device will set the default value if this is 0. */
+	/* [wsm] */ __le32 expireTime;
+
+	/* WSM_HT_TX_... */
+	/* [in] */ __le32 htTxParameters;
+};
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */
+#define WSM_TX_EXTRA_HEADROOM (28)
+
+/* 3.16 */
+struct wsm_rx {
+	/* WSM_STATUS_... */
+	/* [out] */ u32 status;
+
+	/* Specifies the channel of the received packet. */
+	/* [out] */ u16 channelNumber;
+
+	/* WSM_TRANSMIT_RATE_... */
+	/* [out] */ u8 rxedRate;
+
+	/* This value is expressed in signed Q8.0 format for */
+	/* RSSI and unsigned Q7.1 format for RCPI. */
+	/* [out] */ u8 rcpiRssi;
+
+	/* WSM_RX_STATUS_... */
+	/* [out] */ u32 flags;
+
+	/* An 802.11 frame. */
+	/* [out] */ void *frame;
+
+	/* Size of the frame */
+	/* [out] */ size_t frame_size;
+
+	/* Link ID */
+	/* [out] */ int link_id;
+	/* [out] */ int if_id;
+};
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */
+#define WSM_RX_EXTRA_HEADROOM (16)
+
+typedef void (*wsm_rx_cb) (struct bes2600_vif *priv, struct wsm_rx *arg,
+			   struct sk_buff **skb_p);
+
+/* 3.17 */
+struct wsm_event {
+	/* WSM_STATUS_... */
+	/* [out] */ u32 eventId;
+
+	/* Indication parameters. */
+	/* For error indication, this shall be a 32-bit WSM status. */
+	/* For RCPI or RSSI indication, this should be an 8-bit */
+	/* RCPI or RSSI value. */
+	/* [out] */ u32 eventData;
+};
+
+struct bes2600_wsm_event {
+	struct list_head link;
+	struct wsm_event evt;
+	u8 if_id;
+};
+
+/* 3.18 - 3.22 */
+/* Measurement. Skipped for now. Irrelevent. */
+
+typedef void (*wsm_event_cb) (struct bes2600_common *hw_priv,
+			      struct wsm_event *arg);
+
+/* 3.23 */
+struct wsm_join {
+	/* WSM_JOIN_MODE_... */
+	/* [in] */ u8 mode;
+
+	/* WSM_PHY_BAND_... */
+	/* [in] */ u8 band;
+
+	/* Specifies the channel number to join. The channel */
+	/* number will be mapped to an actual frequency */
+	/* according to the band */
+	/* [in] */ u16 channelNumber;
+
+	/* Specifies the BSSID of the BSS or IBSS to be joined */
+	/* or the IBSS to be started. */
+	/* [in] */ u8 bssid[6];
+
+	/* ATIM window of IBSS */
+	/* When ATIM window is zero the initiated IBSS does */
+	/* not support power saving. */
+	/* [in] */ u16 atimWindow;
+
+	/* WSM_JOIN_PREAMBLE_... */
+	/* [in] */ u8 preambleType;
+
+	/* Specifies if a probe request should be send with the */
+	/* specified SSID when joining to the network. */
+	/* [in] */ u8 probeForJoin;
+
+	/* DTIM Period (In multiples of beacon interval) */
+	/* [in] */ u8 dtimPeriod;
+
+	/* WSM_JOIN_FLAGS_... */
+	/* [in] */ u8 flags;
+
+	/* Length of the SSID */
+	/* [in] */ u32 ssidLength;
+
+	/* Specifies the SSID of the IBSS to join or start */
+	/* [in] */ u8 ssid[32];
+
+	/* Specifies the time between TBTTs in TUs */
+	/* [in] */ u32 beaconInterval;
+
+	/* A bit mask that defines the BSS basic rate set. */
+	/* [in] */ u32 basicRateSet;
+
+	/* Minimum transmission power level in units of 0.1dBm */
+	/* [out] */ int minPowerLevel;
+
+	/* Maximum transmission power level in units of 0.1dBm */
+	/* [out] */ int maxPowerLevel;
+};
+
+int wsm_join(struct bes2600_common *hw_priv, struct wsm_join *arg, int if_id);
+
+/* 3.25 */
+struct wsm_set_pm {
+	/* WSM_PSM_... */
+	/* [in] */ u8 pmMode;
+
+	/* in unit of 500us; 0 to use default */
+	/* [in] */ u8 fastPsmIdlePeriod;
+
+	/* in unit of 500us; 0 to use default */
+	/* [in] */ u8 apPsmChangePeriod;
+
+	/* in unit of 500us; 0 to disable auto-pspoll */
+	/* [in] */ u8 minAutoPsPollPeriod;
+};
+
+int wsm_set_pm(struct bes2600_common *hw_priv, const struct wsm_set_pm *arg,
+	       int if_id);
+
+
+/* 3.27 */
+struct wsm_set_pm_complete {
+	/* WSM_STATUS_... */
+	/* [out] */ u32 status;
+
+	/* WSM_PSM_... */
+    	/* [out] */u8 psm;
+};
+
+typedef void (*wsm_set_pm_complete_cb) (struct bes2600_common *hw_priv,
+					struct wsm_set_pm_complete *arg);
+
+/* 3.28 */
+struct wsm_set_bss_params {
+	/* The number of lost consecutive beacons after which */
+	/* the WLAN device should indicate the BSS-Lost event */
+	/* to the WLAN host driver. */
+	u8 beaconLostCount;
+
+	/* The AID received during the association process. */
+	u16 aid;
+
+	/* The operational rate set mask */
+	u32 operationalRateSet;
+};
+
+int wsm_set_bss_params(struct bes2600_common *hw_priv,
+		       const struct wsm_set_bss_params *arg, int if_id);
+
+/* 3.30 */
+struct wsm_add_key {
+	u8 type;		/* WSM_KEY_TYPE_... */
+	u8 entryIndex;		/* Key entry index: 0 -- WSM_KEY_MAX_INDEX */
+	u16 reserved;
+	union {
+		struct {
+			u8 peerAddress[6];	/* MAC address of the
+						 * peer station */
+			u8 reserved;
+			u8 keyLength;		/* Key length in bytes */
+			u8 keyData[16];		/* Key data */
+		} __packed wepPairwiseKey;
+		struct {
+			u8 keyId;		/* Unique per key identifier
+						 * (0..3) */
+			u8 keyLength;		/* Key length in bytes */
+			u16 reserved;
+			u8 keyData[16];		/* Key data */
+		} __packed wepGroupKey;
+		struct {
+			u8 peerAddress[6];	/* MAC address of the
+						 * peer station */
+			u8 reserved[2];
+			u8 tkipKeyData[16];	/* TKIP key data */
+			u8 rxMicKey[8];		/* Rx MIC key */
+			u8 txMicKey[8];		/* Tx MIC key */
+		} __packed tkipPairwiseKey;
+		struct {
+			u8 tkipKeyData[16];	/* TKIP key data */
+			u8 rxMicKey[8];		/* Rx MIC key */
+			u8 keyId;		/* Key ID */
+			u8 reserved[3];
+			u8 rxSeqCounter[8];	/* Receive Sequence Counter */
+		} __packed tkipGroupKey;
+		struct {
+			u8 peerAddress[6];	/* MAC address of the
+						 * peer station */
+			u16 reserved;
+			u8 aesKeyData[16];	/* AES key data */
+		} __packed aesPairwiseKey;
+		struct {
+			u8 aesKeyData[16];	/* AES key data */
+			u8 keyId;		/* Key ID */
+			u8 reserved[3];
+			u8 rxSeqCounter[8];	/* Receive Sequence Counter */
+		} __packed aesGroupKey;
+		struct {
+			u8 peerAddress[6];	/* MAC address of the
+						 * peer station */
+			u8 keyId;		/* Key ID */
+			u8 reserved;
+			u8 wapiKeyData[16];	/* WAPI key data */
+			u8 micKeyData[16];	/* MIC key data */
+		} __packed wapiPairwiseKey;
+		struct {
+			u8 wapiKeyData[16];	/* WAPI key data */
+			u8 micKeyData[16];	/* MIC key data */
+			u8 keyId;		/* Key ID */
+			u8 reserved[3];
+		} __packed wapiGroupKey;
+		struct {
+			u8 IGTKKeyData[16];
+			u8 keyId;
+			u8 Reserved[3];
+			u8 IPN[8]; /* actual IPN is only 6 bytes long */
+		} __packed igtkGroupKey;
+	} __packed;
+} __packed;
+
+int wsm_add_key(struct bes2600_common *hw_priv, const struct wsm_add_key *arg,
+			int if_id);
+
+/* 3.32 */
+struct wsm_remove_key {
+	/* Key entry index : 0-10 */
+	u8 entryIndex;
+};
+
+int wsm_remove_key(struct bes2600_common *hw_priv,
+		   const struct wsm_remove_key *arg, int if_id);
+
+/* 3.34 */
+struct wsm_set_tx_queue_params {
+	/* WSM_ACK_POLICY_... */
+	u8 ackPolicy;
+
+	/* Medium Time of TSPEC (in 32us units) allowed per */
+	/* One Second Averaging Period for this queue. */
+	u16 allowedMediumTime;
+
+	/* dot11MaxTransmitMsduLifetime to be used for the */
+	/* specified queue. */
+	u32 maxTransmitLifetime;
+};
+
+struct wsm_tx_queue_params {
+	/* NOTE: index is a linux queue id. */
+	struct wsm_set_tx_queue_params params[4];
+};
+
+#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time,     \
+			 max_life_time)					    \
+do {									    \
+	struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \
+	p->ackPolicy = (ack_policy);				\
+	p->allowedMediumTime = (allowed_time);				\
+	p->maxTransmitLifetime = (max_life_time);			\
+} while (0)
+
+int wsm_set_tx_queue_params(struct bes2600_common *hw_priv,
+			    const struct wsm_set_tx_queue_params *arg,
+			    u8 id, int if_id);
+
+/* 3.36 */
+struct wsm_edca_queue_params {
+	/* CWmin (in slots) for the access class. */
+	/* [in] */ u16 cwMin;
+
+	/* CWmax (in slots) for the access class. */
+	/* [in] */ u16 cwMax;
+
+	/* AIFS (in slots) for the access class. */
+	/* [in] */ u8 aifns;
+
+	/* TX OP Limit (in microseconds) for the access class. */
+	/* [in] */ u16 txOpLimit;
+
+	/* dot11MaxReceiveLifetime to be used for the specified */
+	/* the access class. Overrides the global */
+	/* dot11MaxReceiveLifetime value */
+	/* [in] */ u32 maxReceiveLifetime;
+
+	/* UAPSD trigger support for the access class. */
+	/* [in] */ bool uapsdEnable;
+};
+
+struct wsm_edca_params {
+	/* NOTE: index is a linux queue id. */
+	struct wsm_edca_queue_params params[4];
+};
+
+#define TXOP_UNIT 32
+#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop, life_time,\
+		uapsd)	\
+	do {							\
+		struct wsm_edca_queue_params *p = &(edca)->params[queue]; \
+		p->cwMin = (cw_min);				\
+		p->cwMax = (cw_max);				\
+		p->aifns = (aifs);				\
+		p->txOpLimit = ((txop) * TXOP_UNIT);		\
+		p->maxReceiveLifetime = (life_time);		\
+		p->uapsdEnable = (uapsd);			\
+	} while (0)
+
+int wsm_set_edca_params(struct bes2600_common *hw_priv,
+			const struct wsm_edca_params *arg, int if_id);
+
+int wsm_set_uapsd_param(struct bes2600_common *hw_priv,
+			const struct wsm_edca_params *arg);
+
+/* 3.38 */
+/* Set-System info. Skipped for now. Irrelevent. */
+
+/* 3.40 */
+struct wsm_switch_channel {
+	/* 1 - means the STA shall not transmit any further */
+	/* frames until the channel switch has completed */
+	/* [in] */ u8 channelMode;
+
+	/* Number of TBTTs until channel switch occurs. */
+	/* 0 - indicates switch shall occur at any time */
+	/* 1 - occurs immediately before the next TBTT */
+	/* [in] */ u8 channelSwitchCount;
+
+	/* The new channel number to switch to. */
+	/* Note this is defined as per section 2.7. */
+	/* [in] */ u16 newChannelNumber;
+};
+
+int wsm_switch_channel(struct bes2600_common *hw_priv,
+		       const struct wsm_switch_channel *arg, int if_id);
+
+typedef void (*wsm_channel_switch_cb) (struct bes2600_common *hw_priv);
+
+struct wsm_start {
+	/* WSM_START_MODE_... */
+	/* [in] */ u8 mode;
+
+	/* WSM_PHY_BAND_... */
+	/* [in] */ u8 band;
+
+	/* Channel number */
+	/* [in] */ u16 channelNumber;
+
+	/* Client Traffic window in units of TU */
+	/* Valid only when mode == ..._P2P */
+	/* [in] */ u32 CTWindow;
+
+	/* Interval between two consecutive */
+	/* beacon transmissions in TU. */
+	/* [in] */ u32 beaconInterval;
+
+	/* DTIM period in terms of beacon intervals */
+	/* [in] */ u8 DTIMPeriod;
+
+	/* WSM_JOIN_PREAMBLE_... */
+	/* [in] */ u8 preambleType;
+
+	/* The delay time (in microseconds) period */
+	/* before sending a probe-request. */
+	/* [in] */ u8 probeDelay;
+
+	/* Length of the SSID */
+	/* [in] */ u8 ssidLength;
+
+	/* SSID of the BSS or P2P_GO to be started now. */
+	/* [in] */ u8 ssid[32];
+
+	/* The basic supported rates for the MiniAP. */
+	/* [in] */ u32 basicRateSet;
+};
+
+int wsm_start(struct bes2600_common *hw_priv, const struct wsm_start *arg,
+		int if_id);
+
+#if 0
+struct wsm_beacon_transmit {
+	/* 1: enable; 0: disable */
+	/* [in] */ u8 enableBeaconing;
+};
+
+int wsm_beacon_transmit(struct bes2600_common *hw_priv,
+			const struct wsm_beacon_transmit *arg,
+			int if_id);
+#endif
+
+int wsm_start_find(struct bes2600_common *hw_priv, int if_id);
+
+int wsm_stop_find(struct bes2600_common *hw_priv, int if_id);
+
+typedef void (*wsm_find_complete_cb) (struct bes2600_common *hw_priv,
+				      u32 status);
+
+struct wsm_suspend_resume {
+	/* See 3.52 */
+	/* Link ID */
+	/* [out] */ int link_id;
+	/* Stop sending further Tx requests down to device for this link */
+	/* [out] */ bool stop;
+	/* Transmit multicast Frames */
+	/* [out] */ bool multicast;
+	/* The AC on which Tx to be suspended /resumed. */
+	/* This is applicable only for U-APSD */
+	/* WSM_QUEUE_... */
+	/* [out] */ int queue;
+	/* [out] */ int if_id;
+};
+
+typedef void (*wsm_suspend_resume_cb) (struct bes2600_vif *priv,
+				       struct wsm_suspend_resume *arg);
+
+/* 3.54 Update-IE request. */
+struct wsm_update_ie {
+	/* WSM_UPDATE_IE_... */
+	/* [in] */ u16 what;
+	/* [in] */ u16 count;
+	/* [in] */ u8 *ies;
+	/* [in] */ size_t length;
+};
+
+int wsm_update_ie(struct bes2600_common *hw_priv,
+		  const struct wsm_update_ie *arg, int if_id);
+
+/* 3.56 */
+struct wsm_map_link {
+	/* MAC address of the remote device */
+	/* [in] */ u8 mac_addr[6];
+	/* [in] */ u8 unmap;
+	/* [in] */ u8 link_id;
+};
+
+int wsm_map_link(struct bes2600_common *hw_priv, const struct wsm_map_link *arg,
+		int if_id);
+
+struct wsm_cbc {
+	wsm_scan_complete_cb scan_complete;
+	wsm_tx_confirm_cb tx_confirm;
+	wsm_rx_cb rx;
+	wsm_event_cb event;
+	wsm_set_pm_complete_cb set_pm_complete;
+	wsm_channel_switch_cb channel_switch;
+	wsm_find_complete_cb find_complete;
+	wsm_suspend_resume_cb suspend_resume;
+};
+#ifdef MCAST_FWDING
+
+/* 3.65	Give Buffer Request */
+int wsm_init_release_buffer_request(struct bes2600_common *priv, u8 index);
+
+/* 3.67	Request Buffer Request */
+int wsm_request_buffer_request(struct bes2600_vif *priv,
+                                u8 *arg);
+#endif
+/* ******************************************************************** */
+/* MIB shortcats							*/
+struct wsm_mib {
+	u16 mibId;
+	void *buf;
+	size_t buf_size;
+};
+
+static inline int wsm_set_output_power(struct bes2600_common *hw_priv,
+				       int power_level, int if_id)
+{
+	__le32 val = __cpu_to_le32(power_level);
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL,
+			     &val, sizeof(val), if_id);
+}
+
+static inline int wsm_set_beacon_wakeup_period(struct bes2600_common *hw_priv,
+					       unsigned dtim_interval,
+					       unsigned listen_interval,
+					       int if_id)
+{
+	struct {
+		u8 numBeaconPeriods;
+		u8 reserved;
+		__le16 listenInterval;
+	} val = {
+	dtim_interval, 0, __cpu_to_le16(listen_interval)};
+	if (dtim_interval > 0xFF || listen_interval > 0xFFFF)
+		return -EINVAL;
+	else
+		return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD,
+				     &val, sizeof(val), if_id);
+}
+
+struct wsm_rcpi_rssi_threshold {
+	u8 rssiRcpiMode;	/* WSM_RCPI_RSSI_... */
+	u8 lowerThreshold;
+	u8 upperThreshold;
+	u8 rollingAverageCount;
+};
+
+static inline int wsm_set_rcpi_rssi_threshold(struct bes2600_common *hw_priv,
+					struct wsm_rcpi_rssi_threshold *arg,
+					int if_id)
+{
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg,
+			     sizeof(*arg), if_id);
+}
+
+struct wsm_counters_table {
+	__le32 countPlcpErrors;
+	__le32 countFcsErrors;
+	__le32 countTxPackets;
+	__le32 countRxPackets;
+	__le32 countRxPacketErrors;
+	__le32 countRxDecryptionFailures;
+	__le32 countRxMicFailures;
+	__le32 countRxNoKeyFailures;
+	__le32 countTxMulticastFrames;
+	__le32 countTxFramesSuccess;
+	__le32 countTxFrameFailures;
+	__le32 countTxFramesRetried;
+	__le32 countTxFramesMultiRetried;
+	__le32 countRxFrameDuplicates;
+	__le32 countRtsSuccess;
+	__le32 countRtsFailures;
+	__le32 countAckFailures;
+	__le32 countRxMulticastFrames;
+	__le32 countRxFramesSuccess;
+	__le32 countRxCMACICVErrors;
+	__le32 countRxCMACReplays;
+	__le32 countRxMgmtCCMPReplays;
+};
+
+static inline int wsm_get_counters_table(struct bes2600_common *hw_priv,
+					 struct wsm_counters_table *arg)
+{
+	return wsm_read_mib(hw_priv, WSM_MIB_ID_COUNTERS_TABLE,
+			arg, sizeof(*arg));
+}
+
+static inline int wsm_get_station_id(struct bes2600_common *hw_priv, u8 *mac)
+{
+	return wsm_read_mib(hw_priv, WSM_MIB_ID_DOT11_STATION_ID, mac,
+			    ETH_ALEN);
+}
+
+struct wsm_rx_filter {
+	bool promiscuous;
+	bool bssid;
+	bool fcs;
+	bool probeResponder;
+	bool keepalive;
+};
+
+static inline int wsm_set_rx_filter(struct bes2600_common *hw_priv,
+				    const struct wsm_rx_filter *arg,
+				    int if_id)
+{
+	__le32 val = 0;
+	if (arg->promiscuous)
+		val |= __cpu_to_le32(BIT(0));
+	if (arg->bssid)
+		val |= __cpu_to_le32(BIT(1));
+	if (arg->fcs)
+		val |= __cpu_to_le32(BIT(2));
+	if (arg->probeResponder)
+		val |= __cpu_to_le32(BIT(3));
+	if (arg->keepalive)
+		val |= __cpu_to_le32(BIT(4));
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val),
+			if_id);
+}
+
+int wsm_set_probe_responder(struct bes2600_vif *priv, bool enable);
+int wsm_set_keepalive_filter(struct bes2600_vif *priv, bool enable);
+
+#define WSM_BEACON_FILTER_IE_HAS_CHANGED	BIT(0)
+#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT	BIT(1)
+#define WSM_BEACON_FILTER_IE_HAS_APPEARED	BIT(2)
+
+struct wsm_beacon_filter_table_entry {
+	u8	ieId;
+	u8	actionFlags;
+	u8	oui[3];
+	u8	matchData[3];
+} __packed;
+
+struct wsm_beacon_filter_table {
+	__le32 numOfIEs;
+	struct wsm_beacon_filter_table_entry entry[10];
+} __packed;
+
+static inline int wsm_set_beacon_filter_table(struct bes2600_common *hw_priv,
+					struct wsm_beacon_filter_table *ft,
+					int if_id)
+{
+	size_t size = __le32_to_cpu(ft->numOfIEs) *
+		     sizeof(struct wsm_beacon_filter_table_entry) +
+		     sizeof(__le32);
+
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size,
+			if_id);
+}
+
+#define WSM_BEACON_FILTER_ENABLE	BIT(0) /* Enable/disable beacon filtering */
+#define WSM_BEACON_FILTER_AUTO_ERP	BIT(1) /* If 1 FW will handle ERP IE changes internally */
+
+struct wsm_beacon_filter_control {
+	int enabled;
+	int bcn_count;
+};
+
+static inline int wsm_beacon_filter_control(struct bes2600_common *hw_priv,
+					struct wsm_beacon_filter_control *arg,
+					int if_id)
+{
+	struct {
+		__le32 enabled;
+		__le32 bcn_count;
+	} val;
+	val.enabled = __cpu_to_le32(arg->enabled);
+	val.bcn_count = __cpu_to_le32(arg->bcn_count);
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val,
+			     sizeof(val), if_id);
+}
+
+enum wsm_power_mode {
+	wsm_power_mode_active = 0,
+	wsm_power_mode_doze = 1,
+	wsm_power_mode_quiescent = 2,
+};
+
+struct wsm_operational_mode {
+	enum wsm_power_mode power_mode;
+	int disableMoreFlagUsage;
+	int performAntDiversity;
+};
+
+static inline int wsm_set_operational_mode(struct bes2600_common *hw_priv,
+					const struct wsm_operational_mode *arg,
+					int if_id)
+{
+	u32 val = arg->power_mode;
+	if (arg->disableMoreFlagUsage)
+		val |= BIT(4);
+	if (arg->performAntDiversity)
+		val |= BIT(5);
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val,
+			     sizeof(val), if_id);
+}
+
+struct wsm_inactivity {
+	u8 max_inactivity;
+	u8 min_inactivity;
+};
+
+static inline int wsm_set_inactivity(struct bes2600_common *hw_priv,
+					const struct wsm_inactivity *arg,
+					int if_id)
+{
+	struct {
+	       u8	min_inactive;
+	       u8	max_inactive;
+	       u16	reserved;
+	} val;
+
+	val.max_inactive = arg->max_inactivity;
+	val.min_inactive = arg->min_inactivity;
+	val.reserved = 0;
+
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_INACTIVITY, &val,
+			     sizeof(val), if_id);
+}
+
+struct wsm_template_frame {
+	u8 frame_type;
+	u8 rate;
+	bool disable;
+	struct sk_buff *skb;
+};
+
+static inline int wsm_set_template_frame(struct bes2600_common *hw_priv,
+					 struct wsm_template_frame *arg,
+					 int if_id)
+{
+	int ret;
+	u8 *p = skb_push(arg->skb, 4);
+	p[0] = arg->frame_type;
+	p[1] = arg->rate;
+	if (arg->disable)
+		((u16 *) p)[1] = 0;
+	else
+		((u16 *) p)[1] = __cpu_to_le16(arg->skb->len - 4);
+	ret = wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, p,
+			    arg->skb->len, if_id);
+	skb_pull(arg->skb, 4);
+	return ret;
+}
+
+
+struct wsm_protected_mgmt_policy {
+	bool protectedMgmtEnable;
+	bool unprotectedMgmtFramesAllowed;
+	bool encryptionForAuthFrame;
+};
+
+static inline int
+wsm_set_protected_mgmt_policy(struct bes2600_common *hw_priv,
+			      struct wsm_protected_mgmt_policy *arg,
+			      int if_id)
+{
+	__le32 val = 0;
+	int ret;
+	if (arg->protectedMgmtEnable)
+		val |= __cpu_to_le32(BIT(0));
+	if (arg->unprotectedMgmtFramesAllowed)
+		val |= __cpu_to_le32(BIT(1));
+	if (arg->encryptionForAuthFrame)
+		val |= __cpu_to_le32(BIT(2));
+	ret = wsm_write_mib(hw_priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, &val,
+			    sizeof(val), if_id);
+	return ret;
+}
+
+static inline int wsm_set_block_ack_policy(struct bes2600_common *hw_priv,
+					   u8 blockAckTxTidPolicy,
+					   u8 blockAckRxTidPolicy,
+					   int if_id)
+{
+	struct {
+		u8 blockAckTxTidPolicy;
+		u8 reserved1;
+		u8 blockAckRxTidPolicy;
+		u8 reserved2;
+	} val = {
+		.blockAckTxTidPolicy = blockAckTxTidPolicy,
+		.blockAckRxTidPolicy = blockAckRxTidPolicy,
+	};
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val,
+			     sizeof(val), if_id);
+}
+
+struct wsm_association_mode {
+	u8 flags;		/* WSM_ASSOCIATION_MODE_... */
+	u8 preambleType;	/* WSM_JOIN_PREAMBLE_... */
+	u8 greenfieldMode;	/* 1 for greenfield */
+	u8 mpduStartSpacing;
+	__le32 basicRateSet;
+};
+
+static inline int wsm_set_association_mode(struct bes2600_common *hw_priv,
+					   struct wsm_association_mode *arg,
+					   int if_id)
+{
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg,
+			     sizeof(*arg), if_id);
+}
+
+struct wsm_set_tx_rate_retry_policy_header {
+	u8 numTxRatePolicies;
+	u8 reserved[3];
+} __packed;
+
+struct wsm_set_tx_rate_retry_policy_policy {
+	u8 policyIndex;
+	u8 shortRetryCount;
+	u8 longRetryCount;
+	u8 policyFlags;
+	u8 rateRecoveryCount;
+	u8 reserved[3];
+	__le32 rateCountIndices[3];
+} __packed;
+
+struct wsm_set_tx_rate_retry_policy {
+	struct wsm_set_tx_rate_retry_policy_header hdr;
+	struct wsm_set_tx_rate_retry_policy_policy tbl[8];
+} __packed;
+
+static inline int wsm_set_tx_rate_retry_policy(struct bes2600_common *hw_priv,
+				struct wsm_set_tx_rate_retry_policy *arg,
+				int if_id)
+{
+	size_t size = sizeof(struct wsm_set_tx_rate_retry_policy_header) +
+	    arg->hdr.numTxRatePolicies *
+	    sizeof(struct wsm_set_tx_rate_retry_policy_policy);
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg,
+			     size, if_id);
+}
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+struct wsm_ether_type_filter_hdr {
+	u8 nrFilters;		/* Up to WSM_MAX_FILTER_ELEMENTS */
+	u8 reserved[3];
+} __packed;
+
+struct wsm_ether_type_filter {
+	u8 filterAction;	/* WSM_FILTER_ACTION_XXX */
+	u8 reserved;
+	__le16 etherType;	/* Type of ethernet frame */
+} __packed;
+
+static inline int wsm_set_ether_type_filter(struct bes2600_common *hw_priv,
+				struct wsm_ether_type_filter_hdr *arg,
+				int if_id)
+{
+	size_t size = sizeof(struct wsm_ether_type_filter_hdr) +
+		arg->nrFilters * sizeof(struct wsm_ether_type_filter);
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER,
+		arg, size, if_id);
+}
+
+
+/* 4.33 SetUDPPortDataFrameFilter */
+struct wsm_udp_port_filter_hdr {
+	u8 nrFilters;		/* Up to WSM_MAX_FILTER_ELEMENTS */
+	u8 reserved[3];
+} __packed;
+
+struct wsm_udp_port_filter {
+	u8 filterAction;	/* WSM_FILTER_ACTION_XXX */
+	u8 portType;		/* WSM_FILTER_PORT_TYPE_XXX */
+	__le16 udpPort;		/* Port number */
+} __packed;
+
+static inline int wsm_set_udp_port_filter(struct bes2600_common *hw_priv,
+				struct wsm_udp_port_filter_hdr *arg,
+				int if_id)
+{
+	size_t size = sizeof(struct wsm_udp_port_filter_hdr) +
+		arg->nrFilters * sizeof(struct wsm_udp_port_filter);
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER,
+		arg, size, if_id);
+}
+
+/* Undocumented MIBs: */
+/* 4.35 P2PDeviceInfo */
+#define D11_MAX_SSID_LEN		(32)
+
+struct wsm_p2p_device_type {
+	__le16 categoryId;
+	u8 oui[4];
+	__le16 subCategoryId;
+} __packed;
+
+struct wsm_p2p_device_info {
+	struct wsm_p2p_device_type primaryDevice;
+	u8 reserved1[3];
+	u8 devNameSize;
+	u8 localDevName[D11_MAX_SSID_LEN];
+	u8 reserved2[3];
+	u8 numSecDevSupported;
+	struct wsm_p2p_device_type secondaryDevices[0];
+} __packed;
+
+/* 4.36 SetWCDMABand - WO */
+struct wsm_cdma_band {
+	u8 WCDMA_Band;
+	u8 reserved[3];
+} __packed;
+
+/* 4.37 GroupTxSequenceCounter - RO */
+struct wsm_group_tx_seq {
+	__le32 bits_47_16;
+	__le16 bits_15_00;
+	__le16 reserved;
+} __packed;
+
+/* 4.39 SetHtProtection - WO */
+#define WSM_DUAL_CTS_PROT_ENB		(1 << 0)
+#define WSM_NON_GREENFIELD_STA_PRESENT	(1 << 1)
+#define WSM_HT_PROT_MODE__NO_PROT	(0 << 2)
+#define WSM_HT_PROT_MODE__NON_MEMBER	(1 << 2)
+#define WSM_HT_PROT_MODE__20_MHZ	(2 << 2)
+#define WSM_HT_PROT_MODE__NON_HT_MIXED	(3 << 2)
+#define WSM_LSIG_TXOP_PROT_FULL		(1 << 4)
+#define WSM_LARGE_L_LENGTH_PROT		(1 << 5)
+
+struct wsm_ht_protection {
+	__le32 flags;
+} __packed;
+
+/* 4.40 GPIO Command - R/W */
+#define WSM_GPIO_COMMAND_SETUP	0
+#define WSM_GPIO_COMMAND_READ	1
+#define WSM_GPIO_COMMAND_WRITE	2
+#define WSM_GPIO_COMMAND_RESET	3
+#define WSM_GPIO_ALL_PINS	0xFF
+
+struct wsm_gpio_command {
+	u8 GPIO_Command;
+	u8 pin;
+	__le16 config;
+} __packed;
+
+/* 4.41 TSFCounter - RO */
+struct wsm_tsf_counter {
+	__le64 TSF_Counter;
+} __packed;
+
+/* 4.43 Keep alive period */
+struct wsm_keep_alive_period {
+	__le16 keepAlivePeriod;
+	u8 reserved[2];
+} __packed;
+
+static inline int wsm_keep_alive_period(struct bes2600_common *hw_priv,
+					int period, int if_id)
+{
+	struct wsm_keep_alive_period arg = {
+		.keepAlivePeriod = __cpu_to_le16(period),
+	};
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD,
+			&arg, sizeof(arg), if_id);
+};
+
+/* BSSID filtering */
+struct wsm_set_bssid_filtering {
+	u8 filter;
+	u8 reserved[3];
+} __packed;
+
+static inline int wsm_set_bssid_filtering(struct bes2600_common *hw_priv,
+					  bool enabled, int if_id)
+{
+	struct wsm_set_bssid_filtering arg = {
+		.filter = !enabled,
+	};
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_DISABLE_BSSID_FILTER,
+			&arg, sizeof(arg), if_id);
+}
+
+/* Multicat filtering - 4.5 */
+struct wsm_multicast_filter {
+	__le32 enable;
+	__le32 numOfAddresses;
+	u8 macAddress[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN];
+} __packed;
+
+/* Mac Addr Filter Info */
+struct wsm_mac_addr_info {
+	u8 filter_mode;
+	u8 address_mode;
+	u8 MacAddr[6];
+} __packed;
+
+/* Mac Addr Filter */
+struct wsm_mac_addr_filter {
+	u8 numfilter;
+	u8 action_mode;
+	u8 Reserved[2];
+	struct wsm_mac_addr_info macaddrfilter[0];
+} __packed;
+
+/* Broadcast Addr Filter */
+struct wsm_broadcast_addr_filter {
+	u8 action_mode;
+	u8 nummacaddr;
+	u8 filter_mode;
+	u8 address_mode;
+	u8 MacAddr[6];
+} __packed;
+
+static inline int wsm_set_multicast_filter(struct bes2600_common *hw_priv,
+					   struct wsm_multicast_filter *fp,
+					   int if_id)
+{
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE,
+			     fp, sizeof(*fp), if_id);
+}
+
+/* ARP IPv4 filtering - 4.10 */
+struct wsm_arp_ipv4_filter {
+	__le32 enable;
+	__be32 ipv4Address[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES];
+} __packed;
+
+#ifdef IPV6_FILTERING
+/* NDP IPv6 filtering */
+struct wsm_ndp_ipv6_filter {
+	__le32 enable;
+	struct in6_addr ipv6Address[WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES];
+} __packed;
+/* IPV6 Addr Filter Info */
+struct wsm_ip6_addr_info {
+	u8 filter_mode;
+	u8 address_mode;
+	u8 Reserved[2];
+	u8 ipv6[16];
+};
+
+/* IPV6 Addr Filter */
+struct wsm_ipv6_filter {
+	u8 numfilter;
+	u8 action_mode;
+	u8 Reserved[2];
+	struct wsm_ip6_addr_info ipv6filter[0];
+} __packed;
+#endif /*IPV6_FILTERING*/
+
+struct wsm_ip4_addr_info {
+    u8 filter_mode;
+    u8 address_mode;
+    u8 Reserved[2];
+    u8 ipv4[4];
+};
+
+/* IPV6 Addr Filter */
+struct wsm_ipv4_filter {
+    u8 numfilter;
+    u8 action_mode;
+    u8 Reserved[2];
+    struct wsm_ip4_addr_info ipv4filter[0];
+} __packed;
+
+static inline int wsm_set_arp_ipv4_filter(struct bes2600_common *hw_priv,
+					  struct wsm_arp_ipv4_filter *fp,
+					  int if_id)
+{
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE,
+			    fp, sizeof(*fp), if_id);
+}
+
+#ifdef IPV6_FILTERING
+static inline int wsm_set_ndp_ipv6_filter(struct bes2600_common *priv,
+					  struct wsm_ndp_ipv6_filter *fp,
+					  int if_id)
+{
+	return wsm_write_mib(priv, WSM_MIB_ID_NS_IP_ADDRESSES_TABLE,
+			    fp, sizeof(*fp), if_id);
+}
+#endif /*IPV6_FILTERING*/
+
+/* P2P Power Save Mode Info - 4.31 */
+struct wsm_p2p_ps_modeinfo {
+	u8	oppPsCTWindow;
+	u8	count;
+	u8	reserved;
+	u8	dtimCount;
+	__le32	duration;
+	__le32	interval;
+	__le32	startTime;
+} __packed;
+
+static inline int wsm_set_p2p_ps_modeinfo(struct bes2600_common *hw_priv,
+					  struct wsm_p2p_ps_modeinfo *mi,
+					  int if_id)
+{
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+			     mi, sizeof(*mi), if_id);
+}
+
+static inline int wsm_get_p2p_ps_modeinfo(struct bes2600_common *hw_priv,
+					  struct wsm_p2p_ps_modeinfo *mi)
+{
+	return wsm_read_mib(hw_priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+			    mi, sizeof(*mi));
+}
+
+/* UseMultiTxConfMessage */
+
+static inline int wsm_use_multi_tx_conf(struct bes2600_common *hw_priv,
+					bool enabled, int if_id)
+{
+	__le32 arg = enabled ? __cpu_to_le32(1) : 0;
+
+	return wsm_write_mib(hw_priv, WSM_MIB_USE_MULTI_TX_CONF,
+			&arg, sizeof(arg), if_id);
+}
+
+
+/* 4.26 SetUpasdInformation */
+struct wsm_uapsd_info {
+	__le16 uapsdFlags;
+	__le16 minAutoTriggerInterval;
+	__le16 maxAutoTriggerInterval;
+	__le16 autoTriggerStep;
+};
+
+static inline int wsm_set_uapsd_info(struct bes2600_common *hw_priv,
+				     struct wsm_uapsd_info *arg,
+				     int if_id)
+{
+	/* TODO:COMBO:UAPSD will be supported only on one interface */
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_UAPSD_INFORMATION,
+				arg, sizeof(*arg), if_id);
+}
+
+/* 4.22 OverrideInternalTxRate */
+struct wsm_override_internal_txrate {
+	u8 internalTxRate;
+	u8 nonErpInternalTxRate;
+	u8 reserved[2];
+} __packed;
+
+static inline int
+wsm_set_override_internal_txrate(struct bes2600_common *hw_priv,
+				     struct wsm_override_internal_txrate *arg,
+				     int if_id)
+{
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+				arg, sizeof(*arg), if_id);
+}
+#ifdef MCAST_FWDING
+/* 4.51 SetForwardingOffload */
+struct wsm_forwarding_offload {
+	u8 fwenable;
+	u8 flags;
+	u8 reserved[2];
+} __packed;
+
+static inline int wsm_set_forwarding_offlad(struct bes2600_common *hw_priv,
+				     struct wsm_forwarding_offload *arg,int if_id)
+{
+	return wsm_write_mib(hw_priv, WSM_MIB_ID_FORWARDING_OFFLOAD,
+				arg, sizeof(*arg),if_id);
+}
+
+#endif
+/* ******************************************************************** */
+/* WSM TX port control							*/
+
+void wsm_lock_tx(struct bes2600_common *hw_priv);
+void wsm_vif_lock_tx(struct bes2600_vif *priv);
+void wsm_lock_tx_async(struct bes2600_common *hw_priv);
+bool wsm_flush_tx(struct bes2600_common *hw_priv);
+bool wsm_vif_flush_tx(struct bes2600_vif *priv);
+void wsm_unlock_tx(struct bes2600_common *hw_priv);
+
+/* ******************************************************************** */
+/* WSM / BH API								*/
+
+int wsm_handle_exception(struct bes2600_common *hw_priv, u8 * data, size_t len);
+int wsm_handle_rx(struct bes2600_common *hw_priv, int id, struct wsm_hdr *wsm,
+		  struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* wsm_buf API								*/
+
+struct wsm_buf {
+	u8 *begin;
+	u8 *data;
+	u8 *end;
+};
+
+void wsm_buf_init(struct wsm_buf *buf);
+void wsm_buf_deinit(struct wsm_buf *buf);
+
+/* ******************************************************************** */
+/* wsm_cmd API								*/
+
+struct wsm_cmd {
+	spinlock_t lock;
+	int done;
+	u8 *ptr;
+	size_t len;
+	void *arg;
+	int ret;
+	u16 cmd;
+};
+
+/* ******************************************************************** */
+/* WSM TX buffer access							*/
+
+int wsm_get_tx(struct bes2600_common *hw_priv, u8 **data,
+	       size_t *tx_len, int *burst, int *vif_selected);
+void wsm_txed(struct bes2600_common *hw_priv, u8 *data);
+
+/* ******************************************************************** */
+/* Queue mapping: WSM <---> linux					*/
+/* Linux: VO VI BE BK							*/
+/* WSM:   BE BK VI VO							*/
+
+static inline u8 wsm_queue_id_to_linux(u8 queueId)
+{
+	static const u8 queue_mapping[] = {
+		2, 3, 1, 0
+	};
+	return queue_mapping[queueId];
+}
+
+static inline u8 wsm_queue_id_to_wsm(u8 queueId)
+{
+	static const u8 queue_mapping[] = {
+		3, 2, 0, 1
+	};
+	return queue_mapping[queueId];
+}
+
+#ifdef CONFIG_BES2600_TESTMODE
+/**
+ * include signaling and nosignaling mode
+ */
+struct vendor_rf_cmd_t {
+	u32 cmd_type;
+	u32 cmd_argc;
+	u32 cmd_len;
+	u8 cmd[0];
+};
+
+int wsm_vendor_rf_cmd(struct bes2600_common *hw_priv, int if_id,
+                      const struct vendor_rf_cmd_t *vendor_rf_cmd);
+int wsm_vendor_rf_cmd_confirm(struct bes2600_common *hw_priv,
+                              void *arg, struct wsm_buf *buf);
+int wsm_vendor_rf_test_indication(struct bes2600_common *hw_priv, struct wsm_buf *buf);
+
+#endif /* CONFIG_BES2600_TESTMODE */
+
+/**
+ *  bes2600 driver signaling and nosignaling cmd
+ */
+enum bes2600_rf_cmd_type {
+	BES2600_RF_CMD_CALI_TXT_TO_FLASH = 0,
+	BES2600_RF_CMD_CH_INFO           = 1,
+	BES2600_RF_CMD_CPU_USAGE         = 2,
+	BES2600_RF_CMD_WIFI_STATUS       = 3,
+	/* add new here */
+
+	BES2600_RF_CMD_MAX,
+};
+
+int wsm_driver_rf_cmd_confirm(struct bes2600_common *hw_priv,
+                              void *arg, struct wsm_buf *buf);
+
+struct wsm_epta_msg {
+    int    wlan_duration;
+    int    bt_duration;
+    int    hw_epta_enable;
+};
+
+#define PROCTECT_MODE_RTS_CTS                              0x1 /* Non retransmission frame with rts/cts procttion*/
+#define PROCTECT_MODE_CTS                                  0x2 /* Non retransmission frame with cts procttion */
+#define PROCTECT_MODE_RTS_CTS_RETRY                        0x4 /* retransmission frame with rts/cts procttion */
+
+typedef struct MIB_TXRX_OPT_PARAM_S
+{
+    /* if rts send ,no cts response, rts can retry max cnt = g_max_rts_retry_limit */
+    u8  rts_retry_limit;
+    /* bit0: Non retransmission rts/cts protion.
+     * bit1: ALL(Non retransmission or retransmission)frame with cts PROTECT.
+     * bit2: retransmission frame with rts/cts PROTECT.
+     * support case1:Non retransmission frame with rts/cts PROTECT
+     *      protect_mode = PROTECT_MODE_RTS_CTS
+     * support case2:ALL(Non retransmission or retransmission)frame with cts PROTECT
+     *      protect_mode = PROTECT_MODE_CTS
+     * support case3:ALL(Non retransmission or retransmission)frame with rts PROTECT
+     *      protect_mode = (PROTECT_MODE_RTS_CTS|PROTECT_MODE_RTS_CTS_RETRY)
+     * support case4:retransmission frame with rts/cts PROTECT
+     *      protect_mode = (PROTECT_MODE_RTS_CTS_RETRY)
+     */
+    u8  protect_mode;
+    u16 rts_duration;
+} MIB_TXRX_OPT_PARAM;
+
+
+int wsm_epta_cmd(struct bes2600_common *hw_priv, struct wsm_epta_msg *arg);
+
+int wsm_epta_wifi_chan_cmd(struct bes2600_common *hw_priv, uint32_t channel, uint32_t type);
+
+int wsm_cpu_usage_cmd(struct bes2600_common *hw_priv);
+
+int wsm_wifi_status_cmd(struct bes2600_common *hw_priv, uint32_t status);
+
+#ifdef PLAT_ALLWINNER_R329
+int wsm_save_factory_txt_to_flash(struct bes2600_common *hw_priv, const u8 *data, int if_id);
+#endif
+
+#endif /* BES2600_HWIO_H_INCLUDED */
-- 
2.49.0

