net:wireless:Support eswin usb wifi ECR6600U
authorJianlong Huang <jianlong.huang@starfivetech.com>
Mon, 31 Oct 2022 08:59:48 +0000 (16:59 +0800)
committerAndy Hu <andy.hu@starfivetech.com>
Fri, 30 Dec 2022 10:46:36 +0000 (18:46 +0800)
Add usb wifi ECR6600U driver

Signed-off-by: Jianlong Huang <jianlong.huang@starfivetech.com>
109 files changed:
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/eswin/Kconfig [new file with mode: 0644]
drivers/net/wireless/eswin/Makefile [new file with mode: 0644]
drivers/net/wireless/eswin/README.md [new file with mode: 0644]
drivers/net/wireless/eswin/ble_netconfig/btgatt-server.c [new file with mode: 0644]
drivers/net/wireless/eswin/compile_test.sh [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_bfmer.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_bfmer.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_cfgfile.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_cfgfile.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_cmds.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_cmds.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_compat.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_debug.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_debug.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_debugfs.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_debugfs.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_events.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_fw_dump.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_fw_trace.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_fw_trace.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_iwpriv.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_mod_params.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_mod_params.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_msg_rx.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_msg_rx.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_msg_tx.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_msg_tx.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_mu_group.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_mu_group.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_platform.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_platform.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_prof.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_radar.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_radar.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_strs.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_strs.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_testmode.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_testmode.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_txq.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_txq.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_utils.c [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_utils.h [new file with mode: 0644]
drivers/net/wireless/eswin/ecrnx_version.h [new file with mode: 0644]
drivers/net/wireless/eswin/eswin_port/eswin_utils.c [new file with mode: 0644]
drivers/net/wireless/eswin/eswin_port/eswin_utils.h [new file with mode: 0644]
drivers/net/wireless/eswin/feature_config/6600_config [new file with mode: 0644]
drivers/net/wireless/eswin/feature_config/6600u_config [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/Makefile [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_amt.c [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_amt.h [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.c [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.h [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.c [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.h [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.c [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.h [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_defs.h [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_main.c [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_main.h [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_mesh.c [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_mesh.h [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_p2p.c [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_p2p.h [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_rx.c [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_rx.h [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_tdls.c [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_tdls.h [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_tx.c [new file with mode: 0644]
drivers/net/wireless/eswin/fullmac/ecrnx_tx.h [new file with mode: 0644]
drivers/net/wireless/eswin/fw_head_check.c [new file with mode: 0644]
drivers/net/wireless/eswin/fw_head_check.h [new file with mode: 0644]
drivers/net/wireless/eswin/hal_desc.c [new file with mode: 0644]
drivers/net/wireless/eswin/hal_desc.h [new file with mode: 0644]
drivers/net/wireless/eswin/ipc_compat.h [new file with mode: 0644]
drivers/net/wireless/eswin/ipc_host.c [new file with mode: 0644]
drivers/net/wireless/eswin/ipc_host.h [new file with mode: 0644]
drivers/net/wireless/eswin/ipc_shared.h [new file with mode: 0644]
drivers/net/wireless/eswin/lmac_mac.h [new file with mode: 0644]
drivers/net/wireless/eswin/lmac_msg.h [new file with mode: 0644]
drivers/net/wireless/eswin/lmac_types.h [new file with mode: 0644]
drivers/net/wireless/eswin/reg_access.h [new file with mode: 0644]
drivers/net/wireless/eswin/reg_ipc_app.h [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/core.c [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/core.h [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/debug.c [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/debug.h [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/ecrnx_sdio.c [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/ecrnx_sdio.h [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/fw.c [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/fw.h [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/sdio.c [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/sdio.h [new file with mode: 0644]
drivers/net/wireless/eswin/sdio/sdio_host_interface.h [new file with mode: 0644]
drivers/net/wireless/eswin/slave_log_buf.c [new file with mode: 0644]
drivers/net/wireless/eswin/slave_log_buf.h [new file with mode: 0644]
drivers/net/wireless/eswin/usb/core.c [new file with mode: 0644]
drivers/net/wireless/eswin/usb/core.h [new file with mode: 0644]
drivers/net/wireless/eswin/usb/debug.c [new file with mode: 0644]
drivers/net/wireless/eswin/usb/debug.h [new file with mode: 0644]
drivers/net/wireless/eswin/usb/ecrnx_usb.c [new file with mode: 0644]
drivers/net/wireless/eswin/usb/ecrnx_usb.h [new file with mode: 0644]
drivers/net/wireless/eswin/usb/fw.c [new file with mode: 0644]
drivers/net/wireless/eswin/usb/fw.h [new file with mode: 0644]
drivers/net/wireless/eswin/usb/usb.c [new file with mode: 0644]
drivers/net/wireless/eswin/usb/usb.h [new file with mode: 0644]
drivers/net/wireless/eswin/usb/usb_host_interface.h [new file with mode: 0644]
drivers/net/wireless/eswin/wifi_ecr6600u.cfg [new file with mode: 0644]

index 7add200..6e0124a 100644 (file)
@@ -35,6 +35,7 @@ source "drivers/net/wireless/st/Kconfig"
 source "drivers/net/wireless/ti/Kconfig"
 source "drivers/net/wireless/zydas/Kconfig"
 source "drivers/net/wireless/quantenna/Kconfig"
+source "drivers/net/wireless/eswin/Kconfig"
 
 config PCMCIA_RAYCS
        tristate "Aviator/Raytheon 2.4GHz wireless support"
index 80b3244..3a8f727 100644 (file)
@@ -30,3 +30,4 @@ obj-$(CONFIG_USB_NET_RNDIS_WLAN)      += rndis_wlan.o
 obj-$(CONFIG_MAC80211_HWSIM)   += mac80211_hwsim.o
 
 obj-$(CONFIG_VIRT_WIFI)        += virt_wifi.o
+obj-$(CONFIG_USB_WIFI_ECR6600U) += eswin/
diff --git a/drivers/net/wireless/eswin/Kconfig b/drivers/net/wireless/eswin/Kconfig
new file mode 100644 (file)
index 0000000..94d445e
--- /dev/null
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config USB_WIFI_ECR6600U
+       bool "USB WIFI ECR6600U"
+       help
+         usb wifi ECR6600U.
+
diff --git a/drivers/net/wireless/eswin/Makefile b/drivers/net/wireless/eswin/Makefile
new file mode 100644 (file)
index 0000000..26959b9
--- /dev/null
@@ -0,0 +1,145 @@
+ECRNX_VERS_NUM=ECR6600U_V1.1.0B04P05
+
+# config_ceva_rtos = y use ceva rtos and add task_cli id
+# config_ceva_rtos = n use freertos and no task_cli id
+
+#export DRIVER_PATH ?= $(shell pwd)
+export DRIVER_PATH =  $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+DRIVER_PATH := $(DRIVER_PATH:/=)
+
+#if build the host driver within linux kernel, the DRIVER_PATH should be defined to absolutly path
+export product=6600u
+ifeq ($(product), 6600u)
+       -include $(DRIVER_PATH)/feature_config/6600u_config
+else
+       -include $(DRIVER_PATH)/feature_config/6600_config
+       ifeq ($(os), )
+               export CONFIG_CEVA_RTOS=y
+       else
+               export CONFIG_CEVA_RTOS=n
+       endif
+endif
+
+###################### Platform Related #######################
+CONFIG_PLATFORM_RTK_RTD2851D = n
+CONFIG_PLATFORM_MTK_MT9255 = n
+CONFIG_PLATFORM_RASPBERRY = y
+CONFIG_PLATFORM_X86 = n
+CONFIG_PLATFORM_AML_T963 = n
+###############################################################
+
+ifeq ($(CONFIG_PLATFORM_RTK_RTD2851D), y)
+export KERNELDIR=/work3/zhanghong/realtek/rtk10/vendor/realtek/tool/kernel/linux/linux-4.14/
+export KBUILDDIR=/work3/zhanghong/realtek/rtk10/vendor/realtek/tool/kernel/linux/linux-4.14/
+endif
+
+ifeq ($(CONFIG_PLATFORM_MTK_MT9255), y)
+export KERNELDIR= $(DRIVER_PATH)/../../../../../../kernel/fusion/4.9
+export KBUILDDIR= $(DRIVER_PATH)/../../../../../../kernel/fusion/4.9
+export CROSS_COMPILE:=$(DRIVER_PATH)/../../../../../../prebuilts/mtk_toolchain/gcc-arm-linux-gnu-5.5.0-ubuntu/x86_64/bin/arm-linux-gnueabi-
+endif
+
+ifeq ($(CONFIG_PLATFORM_RASPBERRY), y)
+export KERNELDIR=/lib/modules/$(shell uname -r)/build
+export KBUILDDIR=/lib/modules/$(shell uname -r)/build
+endif
+
+ifeq ($(CONFIG_PLATFORM_X86), y)
+export KERNELDIR=/lib/modules/$(shell uname -r)/build
+export KBUILDDIR=/lib/modules/$(shell uname -r)/build
+endif
+
+ifeq ($(CONFIG_PLATFORM_AML_T963), y)
+ifeq ($(DRIVER_DIR), )
+export DRIVER_PATH ?= $(shell pwd)
+else
+export DRIVER_PATH ?= $(DRIVER_DIR)
+endif
+export KERNELDIR ?= $(DRIVER_PATH)/../../../../../../out/target/product/T963/obj/KERNEL_OBJ
+export KBUILDDIR ?= $(DRIVER_PATH)/../../../../../../out/target/product/T963/obj/KERNEL_OBJ
+CROSS_COMPILE ?= /opt/gcc-linaro-6.3.1-2017.02-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-
+ARCH := arm
+endif
+#
+# } // WAITING FOR KCONFIG
+#
+
+subdir-ccflags-$(CONFIG_6600_HAL) += -DCONFIG_6600_HAL
+subdir-ccflags-$(CONFIG_DEBUG_FS) += -DCONFIG_ECRNX_DEBUGFS
+subdir-ccflags-$(CONFIG_DEBUG_FS) += -DCONFIG_ECRNX_UM_HELPER_DFLT=\"$(CONFIG_ECRNX_UM_HELPER_DFLT)\"
+subdir-ccflags-$(CONFIG_ECRNX_P2P_DEBUGFS) += -DCONFIG_ECRNX_P2P_DEBUGFS
+subdir-ccflags-$(CONFIG_CEVA_RTOS) += -DCONFIG_CEVA_RTOS
+subdir-ccflags-$(CONFIG_ECRNX_DBG) += -DCONFIG_ECRNX_DBG
+subdir-ccflags-$(CONFIG_ECRNX_KTHREAD) += -DCONFIG_ECRNX_KTHREAD
+subdir-ccflags-$(CONFIG_ECRNX_WORKQUEUE) += -DCONFIG_ECRNX_WORKQUEUE
+subdir-ccflags-$(CONFIG_ECRNX_TASKLET) += -DCONFIG_ECRNX_TASKLET
+subdir-ccflags-y += -DCONFIG_ECRNX_DBG_LEVEL=$(CONFIG_ECRNX_DBG_LEVEL)
+
+# FW VARS
+subdir-ccflags-y += -DNX_VIRT_DEV_MAX=$(NX_VIRT_DEV_MAX)
+subdir-ccflags-y += -DNX_REMOTE_STA_MAX=$(NX_REMOTE_STA_MAX)
+subdir-ccflags-y += -DNX_MU_GROUP_MAX=$(NX_MU_GROUP_MAX)
+subdir-ccflags-y += -DNX_TXDESC_CNT=$(NX_TXDESC_CNT)
+subdir-ccflags-y += -DNX_TX_MAX_RATES=$(NX_TX_MAX_RATES)
+subdir-ccflags-y += -DNX_CHAN_CTXT_CNT=$(NX_CHAN_CTXT_CNT)
+#subdir-ccflags-y += -DCONFIG_POWERKEY_GPIO=$(CONFIG_POWERKEY_GPIO)
+# FW ARCH:
+subdir-ccflags-$(CONFIG_ECRNX_SDM) += -DCONFIG_ECRNX_SDM
+subdir-ccflags-$(CONFIG_ECRNX_TL4) += -DCONFIG_ECRNX_TL4
+subdir-ccflags-$(CONFIG_ECRNX_OLD_IPC) += -DCONFIG_ECRNX_OLD_IPC
+
+#FW VER INFO
+DRIVER_BUILD_TIME = "$(shell TZ=CST date -u "+%Y-%m-%d %H:%M:%S CST")"
+subdir-ccflags-y += -DECRNX_VERS_MOD=\"$(ECRNX_VERS_NUM)\"
+subdir-ccflags-y += -DECRNX_VERS_BANNER=\"$(ECRNX_MODULE_NAME)-$(ECRNX_VERS_NUM)-build\:$(DRIVER_BUILD_TIME)\"
+
+ifeq ($(CONFIG_ECRNX_FULLMAC), m)
+MAC_SRC = fullmac
+else ifeq ($(CONFIG_ECRNX_SOFTMAC), m)
+MAC_SRC = softmac
+endif
+
+
+obj-m := $(ECRNX_MODULE_NAME).o
+
+ifneq ($(KERNELRELEASE),)
+include $(DRIVER_PATH)/$(MAC_SRC)/Makefile
+
+else
+all: modules
+
+.PHONY: modules clean copy strip
+
+modules:
+ifeq ($(product), 6600u)
+       $(warning "select chip is $(product).")
+else
+       $(warning "select chip is 6600.")
+endif
+       rm -rf *.ko
+ifeq ($(os), )
+       $(warning "select slave is used CEVA RTOS.")
+else
+       $(warning "select slave is not used CEVA_RTOS.")
+endif
+ifeq ($(CONFIG_PLATFORM_MTK_MT9255), y)
+       $(MAKE) -C $(KERNELDIR) CROSS_COMPILE=$(CROSS_COMPILE) O=$(KBUILDDIR) M=$(DRIVER_PATH) $@
+else ifeq ($(CONFIG_PLATFORM_AML_T963), y)
+       $(MAKE) -C $(KERNELDIR) CROSS_COMPILE=$(CROSS_COMPILE) ARCH=$(ARCH) O=$(KBUILDDIR) M=$(DRIVER_PATH) $@
+else
+       $(MAKE) -C $(KERNELDIR) O=$(KBUILDDIR) M=$(DRIVER_PATH) $@
+endif
+       rm -rf *.o
+       rm -rf *.mod *.mod.c
+
+copy:
+       cp -f $(DRIVER_PATH)/fullmac/$(ECRNX_MODULE_NAME).ko $(MODDESTDIR)
+
+strip:
+       $(CROSS_COMPILE)strip --strip-unneeded $(ECRNX_MODULE_NAME).ko
+clean:
+       rm -rf *.o
+       rm -rf *.ko *.mod *.mod.c
+       $(MAKE) -C $(KERNELDIR) O=$(KBUILDDIR) M=$(DRIVER_PATH) $@
+#      @$(DRIVER_PATH)/mklink.sh clean
+endif
diff --git a/drivers/net/wireless/eswin/README.md b/drivers/net/wireless/eswin/README.md
new file mode 100644 (file)
index 0000000..745e424
--- /dev/null
@@ -0,0 +1,119 @@
+## **Wifi host driver编译指南**
+
+## Wifi host driver框架
+
+Wifi host driver文件目录如下所示;
+
+- ├── ble_netconfig
+- ├── compile_test.sh
+- ├── ecrnx_bfmer.c
+- ├── ecrnx_bfmer.h
+- ├── ecrnx_cfgfile.c
+- ├── ecrnx_cfgfile.h
+- ├── ecrnx_cmds.c
+- ├── ecrnx_cmds.h
+- ├── ecrnx_compat.h
+- ├── ecrnx_debugfs.c
+- ├── ecrnx_debugfs.h
+- ├── ecrnx_events.h
+- ├── ecrnx_fw_dump.c
+- ├── ecrnx_fw_trace.c
+- ├── ecrnx_fw_trace.h
+- ├── ecrnx_iwpriv.c
+- ├── ecrnx_mod_params.c
+- ├── ecrnx_mod_params.h
+- ├── ecrnx_msg_rx.c
+- ├── ecrnx_msg_rx.h
+- ├── ecrnx_msg_tx.c
+- ├── ecrnx_msg_tx.h
+- ├── ecrnx_mu_group.c
+- ├── ecrnx_mu_group.h
+- ├── ecrnx_platform.c
+- ├── ecrnx_platform.h
+- ├── ecrnx_prof.h
+- ├── ecrnx_radar.c
+- ├── ecrnx_radar.h
+- ├── ecrnx_strs.c
+- ├── ecrnx_strs.h
+- ├── ecrnx_testmode.c
+- ├── ecrnx_testmode.h
+- ├── ecrnx_txq.c
+- ├── ecrnx_txq.h
+- ├── ecrnx_utils.c
+- ├── ecrnx_utils.h
+- ├── ecrnx_version.h
+- ├── eswin_port
+- ├── feature_config
+- ├── fullmac
+- ├── fw_head_check.c
+- ├── fw_head_check.h
+- ├── hal_desc.c
+- ├── hal_desc.h
+- ├── ipc_compat.h
+- ├── ipc_host.c
+- ├── ipc_host.h
+- ├── ipc_shared.h
+- ├── lmac_mac.h
+- ├── lmac_msg.h
+- ├── lmac_types.h
+- ├── Makefile
+- ├── reg_access.h
+- ├── reg_ipc_app.h
+- ├── sdio
+- ├── softmac
+- └── usb
+
+
+Wifi host driver 主要目录结构说明如下表所示;
+
+| **文件路径** | **说明**                          |
+| ------------ | :-------------------------------- |
+| eswin_port   | Host Driver相关的适配接口         |
+| fullmac      | fullmac模式下相关的配置文件和源码 |
+| softmac      | softmac模式下相关的配置文件和源码 |
+| sdio         | sdio相关的驱动文件                |
+| usb          | usb相关的驱动文件                 |
+
+## **Wifi host driver 编译参数说明**
+
+​     6600U host driver 目前时有两个参数可选,描述如下:
+
+​     product:product 参数分为 6600 和 6600u,用来区分芯片平台,默认为 6600;
+
+​     os:os 参数为 ceva,意思为是否使用 ceva 的 os,该参数只针对 6600 有效,6600u 不需要 os 参数;默认为不使用 ceva os;
+
+## **6600(SDIO)透传版本编译指令**
+
+​    slave 使用 6600 iot 仓库的 6600 透传版本时,host driver 编译命令如下:
+
+​    sudo make os=true(或者 sudo make product=6600 os=true)
+
+​    slave 使用 6600U 合仓仓库的 develop 分支时,host driver 的编译命令如下:
+
+​    sudo make(或者 sudo make product=6600)
+
+​    sdio 版本 host driver ko 加载时,如果需要下载固件的话,则要带下载固件和固件名称的参数(默认不下载固件), 如下所示:
+
+​    sudo insmod wlan_ecr6600.ko dl_fw=1 fw_name="transport.bin"
+
+
+
+## **6600U(USB)透传版本编译指令**
+
+​    6600U slave 侧统一使用 6600U 仓库下的 6600U 透传版本,host driver 编译命令如下:
+
+​    sudo make product=6600u (不需要带 os 参数,6600U 默认带 CEVA_OS)
+
+​    6600U host ko 加载命令如下所示:
+
+​    sudo insmod wlan_ecr6600u_usb.ko(6600U host ko 加载时默认会下载固件,默认加载固件名称为:ECR6600U_transport.bin,如果不需要固件下载则使用 dl_fw=0, 如果要自定义下载固件名称则使用:fw_name="filename.bin" );
+
+## cfg文件使用说明
+
+将 wifi_ecr6600u.cfg 拷贝到 /lib/firmware 路径下,修改 cfg 文件里的参数值,即可修改 host 和 slave 的相关参数,修改完成后重新卸载、加载 ko 即可生效;目前可支持的参数如下:
+
+DRIVER_LOG_LEVEL=3  //host driver log 等级,取值范围为 0-5
+
+FW_LOG_LEVEL=2  //slave log 等级,取值范围为 0-4
+
+FW_LOG_TYPE=0  // slave log 输出方式, 0 为通过 slave 端串口输出;1 为通过 host debugfs 保存; 2 为通过 host 侧文件保存;
\ No newline at end of file
diff --git a/drivers/net/wireless/eswin/ble_netconfig/btgatt-server.c b/drivers/net/wireless/eswin/ble_netconfig/btgatt-server.c
new file mode 100644 (file)
index 0000000..c986ee7
--- /dev/null
@@ -0,0 +1,1685 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2014  Google Inc.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+#include "lib/uuid.h"
+
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/att.h"
+#include "src/shared/queue.h"
+#include "src/shared/timeout.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+
+#define UUID_GAP                       0x1800
+#define UUID_GATT                      0x1801
+#define UUID_HEART_RATE                        0x180d
+#define UUID_HEART_RATE_MSRMT          0x2a37
+#define UUID_HEART_RATE_BODY           0x2a38
+#define UUID_HEART_RATE_CTRL           0x2a39
+
+#define ESWIN_TEST  1
+
+#ifdef ESWIN_TEST
+#define UUID_NETCFG         0x1920
+#define UUID_SSID           0x2b10
+#define UUID_PASSWORD       0x2b11
+#define UUID_NETSTATUS      0x2b12
+#define CFG_ITEM_LEN    40
+#define CFG_FLAG_SSID  0x01
+#define CFG_FLAG_PWD   0x02
+#define CFG_FLAG_ALL   0x03
+#endif
+
+#define ATT_CID 4
+
+#define PRLOG(...) \
+       do { \
+               printf(__VA_ARGS__); \
+               print_prompt(); \
+       } while (0)
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define COLOR_OFF      "\x1B[0m"
+#define COLOR_RED      "\x1B[0;91m"
+#define COLOR_GREEN    "\x1B[0;92m"
+#define COLOR_YELLOW   "\x1B[0;93m"
+#define COLOR_BLUE     "\x1B[0;94m"
+#define COLOR_MAGENTA  "\x1B[0;95m"
+#define COLOR_BOLDGRAY "\x1B[1;30m"
+#define COLOR_BOLDWHITE        "\x1B[1;37m"
+
+//static const char test_device_name[] = "Very Long Test Device Name For Testing "
+//                             "ATT Protocol Operations On GATT Server";
+static const char test_device_name[] = "BlueZTest";
+
+static bool verbose = false;
+
+#ifdef ESWIN_TEST
+static uint8_t netcfg_ssid[CFG_ITEM_LEN] = "";
+static uint8_t netcfg_pwd[CFG_ITEM_LEN] = "";
+static uint8_t net_status[CFG_ITEM_LEN] = "";
+static uint8_t ssid_len = 0;
+static uint8_t pwd_len = 0;
+static uint8_t netcfg_flag = 0;
+#endif
+
+
+struct server {
+       int fd;
+       struct bt_att *att;
+       struct gatt_db *db;
+       struct bt_gatt_server *gatt;
+
+       uint8_t *device_name;
+       size_t name_len;
+
+       uint16_t gatt_svc_chngd_handle;
+       bool svc_chngd_enabled;
+
+       uint16_t hr_handle;
+       uint16_t hr_msrmt_handle;
+       uint16_t hr_energy_expended;
+       bool hr_visible;
+       bool hr_msrmt_enabled;
+       int hr_ee_count;
+       unsigned int hr_timeout_id;
+#ifdef ESWIN_TEST
+       uint16_t netstatus_handle;
+       bool netstatus_enabled;
+       unsigned int netstatus_timeout_id;
+#endif
+};
+
+static void print_prompt(void)
+{
+       printf(COLOR_BLUE "[GATT server]" COLOR_OFF "# ");
+       fflush(stdout);
+}
+
+static void att_disconnect_cb(int err, void *user_data)
+{
+       printf("Device disconnected: %s\n", strerror(err));
+
+       mainloop_quit();
+}
+
+static void att_debug_cb(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       PRLOG(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, prefix,
+                                                                       str);
+}
+
+static void gatt_debug_cb(const char *str, void *user_data)
+{
+       const char *prefix = user_data;
+
+       PRLOG(COLOR_GREEN "%s%s\n" COLOR_OFF, prefix, str);
+}
+
+static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t error = 0;
+       size_t len = 0;
+       const uint8_t *value = NULL;
+
+       PRLOG("GAP Device Name Read called\n");
+
+       len = server->name_len;
+
+       if (offset > len) {
+               error = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       len -= offset;
+       value = len ? &server->device_name[offset] : NULL;
+
+done:
+       gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+static void gap_device_name_write_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t error = 0;
+
+       PRLOG("GAP Device Name Write called\n");
+
+       /* If the value is being completely truncated, clean up and return */
+       if (!(offset + len)) {
+               free(server->device_name);
+               server->device_name = NULL;
+               server->name_len = 0;
+               goto done;
+       }
+
+       /* Implement this as a variable length attribute value. */
+       if (offset > server->name_len) {
+               error = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (offset + len != server->name_len) {
+               uint8_t *name;
+
+               name = realloc(server->device_name, offset + len);
+               if (!name) {
+                       error = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+                       goto done;
+               }
+
+               server->device_name = name;
+               server->name_len = offset + len;
+       }
+
+       if (value)
+               memcpy(server->device_name + offset, value, len);
+
+done:
+       gatt_db_attribute_write_result(attrib, id, error);
+}
+
+static void gap_device_name_ext_prop_read_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       uint8_t value[2];
+
+       PRLOG("Device Name Extended Properties Read called\n");
+
+       value[0] = BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE;
+       value[1] = 0;
+
+       gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
+}
+
+static void gatt_service_changed_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       PRLOG("Service Changed Read called\n");
+
+       gatt_db_attribute_read_result(attrib, id, 0, NULL, 0);
+}
+
+static void gatt_svc_chngd_ccc_read_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t value[2];
+
+       PRLOG("Service Changed CCC Read called\n");
+
+       value[0] = server->svc_chngd_enabled ? 0x02 : 0x00;
+       value[1] = 0x00;
+
+       gatt_db_attribute_read_result(attrib, id, 0, value, sizeof(value));
+}
+
+static void gatt_svc_chngd_ccc_write_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t ecode = 0;
+
+       PRLOG("Service Changed CCC Write called\n");
+
+       if (!value || len != 2) {
+               ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+               goto done;
+       }
+
+       if (offset) {
+               ecode = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (value[0] == 0x00)
+               server->svc_chngd_enabled = false;
+       else if (value[0] == 0x02)
+               server->svc_chngd_enabled = true;
+       else
+               ecode = 0x80;
+
+       PRLOG("Service Changed Enabled: %s\n",
+                               server->svc_chngd_enabled ? "true" : "false");
+
+done:
+       gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void hr_msrmt_ccc_read_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t value[2];
+
+       value[0] = server->hr_msrmt_enabled ? 0x01 : 0x00;
+       value[1] = 0x00;
+
+       gatt_db_attribute_read_result(attrib, id, 0, value, 2);
+}
+
+static bool hr_msrmt_cb(void *user_data)
+{
+       struct server *server = user_data;
+       bool expended_present = !(server->hr_ee_count % 10);
+       uint16_t len = 2;
+       uint8_t pdu[4];
+       uint32_t cur_ee;
+
+       pdu[0] = 0x06;
+       pdu[1] = 90 + (rand() % 40);
+
+       if (expended_present) {
+               pdu[0] |= 0x08;
+               put_le16(server->hr_energy_expended, pdu + 2);
+               len += 2;
+       }
+
+       bt_gatt_server_send_notification(server->gatt,
+                                               server->hr_msrmt_handle,
+                                               pdu, len, false);
+
+
+       cur_ee = server->hr_energy_expended;
+       server->hr_energy_expended = MIN(UINT16_MAX, cur_ee + 10);
+       server->hr_ee_count++;
+
+       return true;
+}
+
+static void update_hr_msrmt_simulation(struct server *server)
+{
+       if (!server->hr_msrmt_enabled || !server->hr_visible) {
+               timeout_remove(server->hr_timeout_id);
+               return;
+       }
+
+       server->hr_timeout_id = timeout_add(1000, hr_msrmt_cb, server, NULL);
+}
+
+static void hr_msrmt_ccc_write_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t ecode = 0;
+
+       if (!value || len != 2) {
+               ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+               goto done;
+       }
+
+       if (offset) {
+               ecode = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (value[0] == 0x00)
+               server->hr_msrmt_enabled = false;
+       else if (value[0] == 0x01) {
+               if (server->hr_msrmt_enabled) {
+                       PRLOG("HR Measurement Already Enabled\n");
+                       goto done;
+               }
+
+               server->hr_msrmt_enabled = true;
+       } else
+               ecode = 0x80;
+
+       PRLOG("HR: Measurement Enabled: %s\n",
+                               server->hr_msrmt_enabled ? "true" : "false");
+
+       update_hr_msrmt_simulation(server);
+
+done:
+       gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void hr_control_point_write_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t ecode = 0;
+
+       if (!value || len != 1) {
+               ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+               goto done;
+       }
+
+       if (offset) {
+               ecode = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (value[0] == 1) {
+               PRLOG("HR: Energy Expended value reset\n");
+               server->hr_energy_expended = 0;
+       }
+
+done:
+       gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void confirm_write(struct gatt_db_attribute *attr, int err,
+                                                       void *user_data)
+{
+       if (!err)
+               return;
+
+       fprintf(stderr, "Error caching attribute %p - err: %d\n", attr, err);
+       exit(1);
+}
+
+#ifdef ESWIN_TEST
+static void modify_wpa_conf(void)
+{
+       FILE *fp;
+       char ssid_str[2*CFG_ITEM_LEN]={0};
+       char pwd_str[2*CFG_ITEM_LEN]={0};
+       PRLOG("enter\n");
+
+       fp = fopen("/etc/wpa_supplicant/wpa_supplicant.conf", "we");
+       if (!fp){
+               PRLOG("fopen fail\n");
+               return;
+       }
+       snprintf(ssid_str, sizeof(ssid_str), "ssid=\"%s\"\n", netcfg_ssid);
+       snprintf(pwd_str, sizeof(pwd_str), "psk=\"%s\"\n", netcfg_pwd);
+
+       fputs("ctrl_interface=DIR=/var/run/wpa_supplicant\n",fp);
+       fputs("update_config=1\n", fp);
+       fputs("network={\n", fp);
+       fputs(ssid_str, fp);
+       fputs(pwd_str, fp);
+       fputs("}\n", fp);
+       
+
+       fclose(fp);
+       PRLOG("exit\n");
+}
+
+static int wifi_run_cmd(char *cmd)
+{
+       int ret = 0;
+       ret = system(cmd);
+       if(ret < 0)     {
+               PRLOG("cmd=%s\t error:%s\n",cmd, strerror(errno));
+               return -1;
+       }
+       if(WIFEXITED(ret)){
+
+               PRLOG("success,cmd=%s,status=%d\n",cmd,WEXITSTATUS(ret));
+               return WEXITSTATUS(ret);
+       }
+
+       return -1;      
+
+}
+static void update_wpa_network(void)
+{
+       char sys_cmd[100] = {0};
+       char temp[CFG_ITEM_LEN] = {0};
+       int result = -1;
+          
+       sprintf(sys_cmd, "cd /wpa_supplicant-2.10/wpa_supplicant");
+       result = wifi_run_cmd(sys_cmd);
+       if(result < 0){
+               return;
+       }
+
+       sprintf(sys_cmd, "sudo killall wpa_supplicant");
+       result = wifi_run_cmd(sys_cmd);
+       if(result < 0){
+               return;
+       }
+
+       sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_supplicant -B -c/etc/wpa_supplicant/wpa_supplicant.conf -iwlan0 -Dnl80211,wext");
+       result = wifi_run_cmd(sys_cmd);
+       if(result < 0){
+               return;
+       }
+
+
+       sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 remove_network all");
+       result = wifi_run_cmd(sys_cmd);
+       if(result < 0){
+               return;
+       }
+       memset(sys_cmd, 0x00, 100);
+       sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 add_network");
+       result = wifi_run_cmd(sys_cmd);
+       if(result < 0){
+               return;
+       }
+       memset(sys_cmd, 0x00, 100);
+       sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 set_network 0 ssid ");
+       snprintf(temp, sizeof(temp),"'\"%s\"'", netcfg_ssid);
+       strcat(sys_cmd,temp);
+       result = wifi_run_cmd(sys_cmd);
+       if(result < 0){
+               return;
+       }
+
+       memset(sys_cmd, 0x00, 100);
+       memset(temp, 0x00, CFG_ITEM_LEN);
+       sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 set_network 0 psk ");
+       snprintf(temp, sizeof(temp),"'\"%s\"'", netcfg_pwd);
+       strcat(sys_cmd,temp);
+       result = wifi_run_cmd(sys_cmd);
+       if(result < 0){
+               return;
+       }
+
+       memset(sys_cmd, 0x00, 100);
+       sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 save_config");
+       result = wifi_run_cmd(sys_cmd);
+       if(result < 0){
+               return;
+       }
+
+       memset(sys_cmd, 0x00, 100);
+       sprintf(sys_cmd, "sudo /wpa_supplicant-2.10/wpa_supplicant/wpa_cli -i wlan0 select_network 0");
+       result = wifi_run_cmd(sys_cmd);
+       if(result < 0){
+               return;
+       }
+
+       PRLOG("update_wpa_network finish\n");
+}
+
+static void eswin_netcfg_wpa_conf(int flag)
+{
+       netcfg_flag |= flag;
+
+       if(netcfg_flag == CFG_FLAG_ALL){
+               netcfg_flag = 0;
+               //modify_wpa_conf();
+               update_wpa_network();
+       }
+}
+
+
+
+
+static void eswin_netcfg_ssid_read_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t error = 0;
+       size_t len = 0;
+       const uint8_t *value = NULL;
+
+       //PRLOG("ESWIN SSID Read called ssid_len=%d,offset=%d\n",ssid_len,offset);
+
+       if (offset > ssid_len) {
+               error = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       len = ssid_len - offset;
+       value = len ? &netcfg_ssid[offset] : NULL;
+
+done:
+       gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+
+static void eswin_netcfg_ssid_write_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t error = 0;
+
+       //PRLOG("ESWIN SSID write called offset=%d len=%d\n",offset, len);
+       
+       memset(netcfg_ssid, 0x00, CFG_ITEM_LEN);
+       ssid_len = 0;
+
+       if (offset+len >= CFG_ITEM_LEN) {
+               error = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (value)
+               memcpy(netcfg_ssid + offset, value, len);
+
+       ssid_len +=len;
+       eswin_netcfg_wpa_conf(CFG_FLAG_SSID);
+done:
+       gatt_db_attribute_write_result(attrib, id, error);
+       
+}
+
+
+static void eswin_netcfg_pwd_read_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t error = 0;
+       size_t len = 0;
+       const uint8_t *value = NULL;
+
+       //PRLOG("ESWIN pwd Read called pwd_len=%d,offset=%d\n",pwd_len,offset);
+
+       if (offset > pwd_len) {
+               error = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       len = pwd_len - offset;
+       value = len ? &netcfg_pwd[offset] : NULL;
+
+done:
+       gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+
+static void eswin_netcfg_pwd_write_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t error = 0;
+
+       //PRLOG("ESWIN pwd write called offset=%d len=%d\n",offset, len);
+       
+       memset(netcfg_pwd, 0x00, CFG_ITEM_LEN);
+       pwd_len = 0;
+
+       if (offset+len >= CFG_ITEM_LEN) {
+               error = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (value)
+               memcpy(netcfg_pwd + offset, value, len);
+
+       pwd_len +=len;
+       eswin_netcfg_wpa_conf(CFG_FLAG_PWD);
+done:
+       gatt_db_attribute_write_result(attrib, id, error);
+       
+}
+
+static void eswin_netstatus_ccc_read_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t value[2];
+
+       value[0] = server->netstatus_enabled ? 0x01 : 0x00;
+       value[1] = 0x00;
+
+       gatt_db_attribute_read_result(attrib, id, 0, value, 2);
+}
+
+#define WAP_STATE  "wpa_state="
+static bool netstatus_cb(void *user_data)
+{
+       struct server *server = user_data;
+       FILE *fp = NULL;
+       char buff[500] = {0};
+       char *ptr = NULL, *ptr_end = NULL;
+       int head_len = strlen(WAP_STATE);
+       uint8_t cur_status[CFG_ITEM_LEN] = "";
+
+       fp = popen("sudo wpa_cli -i wlan0 status", "r");
+
+       fread(buff, 1, 499, fp);
+       ptr = strstr(buff, WAP_STATE);
+       if (ptr == NULL) {
+               PRLOG("no wpa_state\n");
+               return false;
+       }
+
+       ptr_end = strchr(ptr, '\n');
+       if (ptr_end == NULL) {
+               PRLOG("no wpa_state end\n");
+               return false;
+       }
+       strncpy(cur_status, ptr+head_len, ptr_end-ptr-head_len);
+       PRLOG("%s\n", cur_status);
+
+       if(strcmp(cur_status, net_status) != 0) {
+               memset(net_status, 0x00, CFG_ITEM_LEN);
+               strcpy(net_status, cur_status);
+               PRLOG("bt_gatt_server_send_notification\n");
+
+               bt_gatt_server_send_notification(server->gatt,
+                                               server->netstatus_handle,
+                                               net_status, strlen(net_status), false);
+       }
+       return true;
+}
+
+static void update_netstatus_simulation(struct server *server)
+{
+       if (!server->netstatus_enabled) {
+               timeout_remove(server->netstatus_timeout_id);
+               return;
+       }
+
+       server->netstatus_timeout_id = timeout_add(1000, netstatus_cb, server, NULL);
+}
+
+
+static void eswin_netstatus_ccc_write_cb(struct gatt_db_attribute *attrib,
+                                       unsigned int id, uint16_t offset,
+                                       const uint8_t *value, size_t len,
+                                       uint8_t opcode, struct bt_att *att,
+                                       void *user_data)
+{
+       struct server *server = user_data;
+       uint8_t ecode = 0;
+
+       if (!value || len != 2) {
+               ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+               goto done;
+       }
+
+       if (offset) {
+               ecode = BT_ATT_ERROR_INVALID_OFFSET;
+               goto done;
+       }
+
+       if (value[0] == 0x00)
+               server->netstatus_enabled = false;
+       else if (value[0] == 0x01) {            
+               server->netstatus_enabled = true;               
+       } else
+               ecode = 0x80;
+
+       PRLOG("netstatus Enabled: %s\n",
+                               server->netstatus_enabled ? "true" : "false");
+       update_netstatus_simulation(server);
+
+done:
+       gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+#endif
+
+static void populate_gap_service(struct server *server)
+{
+       bt_uuid_t uuid;
+       struct gatt_db_attribute *service, *tmp;
+       uint16_t appearance;
+
+       /* Add the GAP service */
+       bt_uuid16_create(&uuid, UUID_GAP);
+       service = gatt_db_add_service(server->db, &uuid, true, 6);
+
+       /*
+        * Device Name characteristic. Make the value dynamically read and
+        * written via callbacks.
+        */
+       bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+       gatt_db_service_add_characteristic(service, &uuid,
+                                       BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+                                       BT_GATT_CHRC_PROP_READ |
+                                       BT_GATT_CHRC_PROP_EXT_PROP,
+                                       gap_device_name_read_cb,
+                                       gap_device_name_write_cb,
+                                       server);
+
+       bt_uuid16_create(&uuid, GATT_CHARAC_EXT_PROPER_UUID);
+       gatt_db_service_add_descriptor(service, &uuid, BT_ATT_PERM_READ,
+                                       gap_device_name_ext_prop_read_cb,
+                                       NULL, server);
+
+       /*
+        * Appearance characteristic. Reads and writes should obtain the value
+        * from the database.
+        */
+       bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+       tmp = gatt_db_service_add_characteristic(service, &uuid,
+                                                       BT_ATT_PERM_READ,
+                                                       BT_GATT_CHRC_PROP_READ,
+                                                       NULL, NULL, server);
+
+       /*
+        * Write the appearance value to the database, since we're not using a
+        * callback.
+        */
+       put_le16(128, &appearance);
+       gatt_db_attribute_write(tmp, 0, (void *) &appearance,
+                                                       sizeof(appearance),
+                                                       BT_ATT_OP_WRITE_REQ,
+                                                       NULL, confirm_write,
+                                                       NULL);
+
+       gatt_db_service_set_active(service, true);
+}
+
+static void populate_gatt_service(struct server *server)
+{
+       bt_uuid_t uuid;
+       struct gatt_db_attribute *service, *svc_chngd;
+
+       /* Add the GATT service */
+       bt_uuid16_create(&uuid, UUID_GATT);
+       service = gatt_db_add_service(server->db, &uuid, true, 4);
+
+       bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+       svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
+                       BT_ATT_PERM_READ,
+                       BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_INDICATE,
+                       gatt_service_changed_cb,
+                       NULL, server);
+       server->gatt_svc_chngd_handle = gatt_db_attribute_get_handle(svc_chngd);
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       gatt_db_service_add_descriptor(service, &uuid,
+                               BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+                               gatt_svc_chngd_ccc_read_cb,
+                               gatt_svc_chngd_ccc_write_cb, server);
+
+       gatt_db_service_set_active(service, true);
+}
+
+static void populate_hr_service(struct server *server)
+{
+       bt_uuid_t uuid;
+       struct gatt_db_attribute *service, *hr_msrmt, *body;
+       uint8_t body_loc = 1;  /* "Chest" */
+
+       /* Add Heart Rate Service */
+       bt_uuid16_create(&uuid, UUID_HEART_RATE);
+       service = gatt_db_add_service(server->db, &uuid, true, 8);
+       server->hr_handle = gatt_db_attribute_get_handle(service);
+
+       /* HR Measurement Characteristic */
+       bt_uuid16_create(&uuid, UUID_HEART_RATE_MSRMT);
+       hr_msrmt = gatt_db_service_add_characteristic(service, &uuid,
+                                               BT_ATT_PERM_NONE,
+                                               BT_GATT_CHRC_PROP_NOTIFY,
+                                               NULL, NULL, NULL);
+       server->hr_msrmt_handle = gatt_db_attribute_get_handle(hr_msrmt);
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       gatt_db_service_add_descriptor(service, &uuid,
+                                       BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+                                       hr_msrmt_ccc_read_cb,
+                                       hr_msrmt_ccc_write_cb, server);
+
+       /*
+        * Body Sensor Location Characteristic. Make reads obtain the value from
+        * the database.
+        */
+       bt_uuid16_create(&uuid, UUID_HEART_RATE_BODY);
+       body = gatt_db_service_add_characteristic(service, &uuid,
+                                               BT_ATT_PERM_READ,
+                                               BT_GATT_CHRC_PROP_READ,
+                                               NULL, NULL, server);
+       gatt_db_attribute_write(body, 0, (void *) &body_loc, sizeof(body_loc),
+                                                       BT_ATT_OP_WRITE_REQ,
+                                                       NULL, confirm_write,
+                                                       NULL);
+
+       /* HR Control Point Characteristic */
+       bt_uuid16_create(&uuid, UUID_HEART_RATE_CTRL);
+       gatt_db_service_add_characteristic(service, &uuid,
+                                               BT_ATT_PERM_WRITE,
+                                               BT_GATT_CHRC_PROP_WRITE,
+                                               NULL, hr_control_point_write_cb,
+                                               server);
+
+       if (server->hr_visible)
+               gatt_db_service_set_active(service, true);
+}
+
+#ifdef ESWIN_TEST
+static void populate_eswin_service(struct server *server)
+{
+       bt_uuid_t uuid;
+       struct gatt_db_attribute *service, *netstatus;
+
+       ssid_len = strlen(netcfg_ssid);
+       pwd_len = strlen(netcfg_pwd);
+       server->netstatus_enabled = true;
+
+       /* Add Net config Service */
+       bt_uuid16_create(&uuid, UUID_NETCFG);
+       service = gatt_db_add_service(server->db, &uuid, true, 10);
+
+       /* SSID Characteristic */
+       bt_uuid16_create(&uuid, UUID_SSID);
+       gatt_db_service_add_characteristic(service, &uuid,
+                                               BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+                                               BT_GATT_CHRC_PROP_READ |
+                                               BT_GATT_CHRC_PROP_WRITE,
+                                               eswin_netcfg_ssid_read_cb,
+                                               eswin_netcfg_ssid_write_cb,
+                                               server);
+       
+
+       /*
+        * PASSWORD Characteristic.
+        */
+       bt_uuid16_create(&uuid, UUID_PASSWORD);
+       gatt_db_service_add_characteristic(service, &uuid,
+                                               BT_ATT_PERM_READ| BT_ATT_PERM_WRITE,
+                                               BT_GATT_CHRC_PROP_READ |
+                                               BT_GATT_CHRC_PROP_WRITE,
+                                               eswin_netcfg_pwd_read_cb,
+                                               eswin_netcfg_pwd_write_cb,
+                                               server);
+
+       /*
+        * NETSTATUS Characteristic.
+        */
+       bt_uuid16_create(&uuid, UUID_NETSTATUS);
+       netstatus = gatt_db_service_add_characteristic(service, &uuid,
+                                               BT_ATT_PERM_NONE,
+                                               BT_GATT_CHRC_PROP_NOTIFY,
+                                               NULL, NULL, NULL);
+       server->netstatus_handle = gatt_db_attribute_get_handle(netstatus);
+
+       bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+       gatt_db_service_add_descriptor(service, &uuid,
+                                       BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+                                       eswin_netstatus_ccc_read_cb,
+                                       eswin_netstatus_ccc_write_cb, server);
+
+       gatt_db_service_set_active(service, true);
+}
+
+#endif
+
+static void populate_db(struct server *server)
+{
+       populate_gap_service(server);
+       populate_gatt_service(server);  
+       populate_hr_service(server);
+#ifdef ESWIN_TEST
+       populate_eswin_service(server);
+#endif
+}
+
+static struct server *server_create(int fd, uint16_t mtu, bool hr_visible)
+{
+       struct server *server;
+       size_t name_len = strlen(test_device_name);
+
+       server = new0(struct server, 1);
+       if (!server) {
+               fprintf(stderr, "Failed to allocate memory for server\n");
+               return NULL;
+       }
+
+       server->att = bt_att_new(fd, false);
+       if (!server->att) {
+               fprintf(stderr, "Failed to initialze ATT transport layer\n");
+               goto fail;
+       }
+
+       if (!bt_att_set_close_on_unref(server->att, true)) {
+               fprintf(stderr, "Failed to set up ATT transport layer\n");
+               goto fail;
+       }
+
+       if (!bt_att_register_disconnect(server->att, att_disconnect_cb, NULL,
+                                                                       NULL)) {
+               fprintf(stderr, "Failed to set ATT disconnect handler\n");
+               goto fail;
+       }
+
+       server->name_len = name_len + 1;
+       server->device_name = malloc(name_len + 1);
+       if (!server->device_name) {
+               fprintf(stderr, "Failed to allocate memory for device name\n");
+               goto fail;
+       }
+
+       memcpy(server->device_name, test_device_name, name_len);
+       server->device_name[name_len] = '\0';
+
+       server->fd = fd;
+       server->db = gatt_db_new();
+       if (!server->db) {
+               fprintf(stderr, "Failed to create GATT database\n");
+               goto fail;
+       }
+
+       server->gatt = bt_gatt_server_new(server->db, server->att, mtu, 0);
+       if (!server->gatt) {
+               fprintf(stderr, "Failed to create GATT server\n");
+               goto fail;
+       }
+
+       server->hr_visible = hr_visible;
+
+       if (verbose) {
+               bt_att_set_debug(server->att, BT_ATT_DEBUG_VERBOSE,
+                                               att_debug_cb, "att: ", NULL);
+               bt_gatt_server_set_debug(server->gatt, gatt_debug_cb,
+                                                       "server: ", NULL);
+       }
+
+       /* Random seed for generating fake Heart Rate measurements */
+       srand(time(NULL));
+
+       /* bt_gatt_server already holds a reference */
+       populate_db(server);
+
+       return server;
+
+fail:
+       gatt_db_unref(server->db);
+       free(server->device_name);
+       bt_att_unref(server->att);
+       free(server);
+
+       return NULL;
+}
+
+static void server_destroy(struct server *server)
+{
+       timeout_remove(server->hr_timeout_id);
+       bt_gatt_server_unref(server->gatt);
+       gatt_db_unref(server->db);
+}
+
+static void usage(void)
+{
+       printf("btgatt-server\n");
+       printf("Usage:\n\tbtgatt-server [options]\n");
+
+       printf("Options:\n"
+               "\t-i, --index <id>\t\tSpecify adapter index, e.g. hci0\n"
+               "\t-m, --mtu <mtu>\t\t\tThe ATT MTU to use\n"
+               "\t-s, --security-level <sec>\tSet security level (low|"
+                                                               "medium|high)\n"
+               "\t-t, --type [random|public] \t The source address type\n"
+               "\t-v, --verbose\t\t\tEnable extra logging\n"
+               "\t-r, --heart-rate\t\tEnable Heart Rate service\n"
+               "\t-h, --help\t\t\tDisplay help\n");
+}
+
+static struct option main_options[] = {
+       { "index",              1, 0, 'i' },
+       { "mtu",                1, 0, 'm' },
+       { "security-level",     1, 0, 's' },
+       { "type",               1, 0, 't' },
+       { "verbose",            0, 0, 'v' },
+       { "heart-rate",         0, 0, 'r' },
+       { "help",               0, 0, 'h' },
+       { }
+};
+
+static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec,
+                                                       uint8_t src_type)
+{
+       int sk, nsk;
+       struct sockaddr_l2 srcaddr, addr;
+       socklen_t optlen;
+       struct bt_security btsec;
+       char ba[18];
+
+       sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+       if (sk < 0) {
+               perror("Failed to create L2CAP socket");
+               return -1;
+       }
+
+       /* Set up source address */
+       memset(&srcaddr, 0, sizeof(srcaddr));
+       srcaddr.l2_family = AF_BLUETOOTH;
+       srcaddr.l2_cid = htobs(ATT_CID);
+       srcaddr.l2_bdaddr_type = src_type;
+       bacpy(&srcaddr.l2_bdaddr, src);
+
+       if (bind(sk, (struct sockaddr *) &srcaddr, sizeof(srcaddr)) < 0) {
+               perror("Failed to bind L2CAP socket");
+               goto fail;
+       }
+
+       /* Set the security level */
+       memset(&btsec, 0, sizeof(btsec));
+       btsec.level = sec;
+       if (setsockopt(sk, SOL_BLUETOOTH, BT_SECURITY, &btsec,
+                                                       sizeof(btsec)) != 0) {
+               fprintf(stderr, "Failed to set L2CAP security level\n");
+               goto fail;
+       }
+
+       if (listen(sk, 10) < 0) {
+               perror("Listening on socket failed");
+               goto fail;
+       }
+
+       printf("Started listening on ATT channel. Waiting for connections\n");
+
+       memset(&addr, 0, sizeof(addr));
+       optlen = sizeof(addr);
+       nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+       if (nsk < 0) {
+               perror("Accept failed");
+               goto fail;
+       }
+
+       ba2str(&addr.l2_bdaddr, ba);
+       printf("Connect from %s\n", ba);
+       close(sk);
+
+       return nsk;
+
+fail:
+       close(sk);
+       return -1;
+}
+
+static void notify_usage(void)
+{
+       printf("Usage: notify [options] <value_handle> <value>\n"
+                                       "Options:\n"
+                                       "\t -i, --indicate\tSend indication\n"
+                                       "e.g.:\n"
+                                       "\tnotify 0x0001 00 01 00\n");
+}
+
+static struct option notify_options[] = {
+       { "indicate",   0, 0, 'i' },
+       { }
+};
+
+static bool parse_args(char *str, int expected_argc,  char **argv, int *argc)
+{
+       char **ap;
+
+       for (ap = argv; (*ap = strsep(&str, " \t")) != NULL;) {
+               if (**ap == '\0')
+                       continue;
+
+               (*argc)++;
+               ap++;
+
+               if (*argc > expected_argc)
+                       return false;
+       }
+
+       return true;
+}
+
+static void conf_cb(void *user_data)
+{
+       PRLOG("Received confirmation\n");
+}
+
+static void cmd_notify(struct server *server, char *cmd_str)
+{
+       int opt, i;
+       char *argvbuf[516];
+       char **argv = argvbuf;
+       int argc = 1;
+       uint16_t handle;
+       char *endptr = NULL;
+       int length;
+       uint8_t *value = NULL;
+       bool indicate = false;
+
+       if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+               printf("Too many arguments\n");
+               notify_usage();
+               return;
+       }
+
+       optind = 0;
+       argv[0] = "notify";
+       while ((opt = getopt_long(argc, argv, "+i", notify_options,
+                                                               NULL)) != -1) {
+               switch (opt) {
+               case 'i':
+                       indicate = true;
+                       break;
+               default:
+                       notify_usage();
+                       return;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               notify_usage();
+               return;
+       }
+
+       handle = strtol(argv[0], &endptr, 16);
+       if (!endptr || *endptr != '\0' || !handle) {
+               printf("Invalid handle: %s\n", argv[0]);
+               return;
+       }
+
+       length = argc - 1;
+
+       if (length > 0) {
+               if (length > UINT16_MAX) {
+                       printf("Value too long\n");
+                       return;
+               }
+
+               value = malloc(length);
+               if (!value) {
+                       printf("Failed to construct value\n");
+                       return;
+               }
+
+               for (i = 1; i < argc; i++) {
+                       if (strlen(argv[i]) != 2) {
+                               printf("Invalid value byte: %s\n",
+                                                               argv[i]);
+                               goto done;
+                       }
+
+                       value[i-1] = strtol(argv[i], &endptr, 16);
+                       if (endptr == argv[i] || *endptr != '\0'
+                                                       || errno == ERANGE) {
+                               printf("Invalid value byte: %s\n",
+                                                               argv[i]);
+                               goto done;
+                       }
+               }
+       }
+
+       if (indicate) {
+               if (!bt_gatt_server_send_indication(server->gatt, handle,
+                                                       value, length,
+                                                       conf_cb, NULL, NULL))
+                       printf("Failed to initiate indication\n");
+       } else if (!bt_gatt_server_send_notification(server->gatt, handle,
+                                                       value, length, false))
+               printf("Failed to initiate notification\n");
+
+done:
+       free(value);
+}
+
+static void heart_rate_usage(void)
+{
+       printf("Usage: heart-rate on|off\n");
+}
+
+static void cmd_heart_rate(struct server *server, char *cmd_str)
+{
+       bool enable;
+       uint8_t pdu[4];
+       struct gatt_db_attribute *attr;
+
+       if (!cmd_str) {
+               heart_rate_usage();
+               return;
+       }
+
+       if (strcmp(cmd_str, "on") == 0)
+               enable = true;
+       else if (strcmp(cmd_str, "off") == 0)
+               enable = false;
+       else {
+               heart_rate_usage();
+               return;
+       }
+
+       if (enable == server->hr_visible) {
+               printf("Heart Rate Service already %s\n",
+                                               enable ? "visible" : "hidden");
+               return;
+       }
+
+       server->hr_visible = enable;
+       attr = gatt_db_get_attribute(server->db, server->hr_handle);
+       gatt_db_service_set_active(attr, server->hr_visible);
+       update_hr_msrmt_simulation(server);
+
+       if (!server->svc_chngd_enabled)
+               return;
+
+       put_le16(server->hr_handle, pdu);
+       put_le16(server->hr_handle + 7, pdu + 2);
+
+       server->hr_msrmt_enabled = false;
+       update_hr_msrmt_simulation(server);
+
+       bt_gatt_server_send_indication(server->gatt,
+                                               server->gatt_svc_chngd_handle,
+                                               pdu, 4, conf_cb, NULL, NULL);
+}
+
+static void print_uuid(const bt_uuid_t *uuid)
+{
+       char uuid_str[MAX_LEN_UUID_STR];
+       bt_uuid_t uuid128;
+
+       bt_uuid_to_uuid128(uuid, &uuid128);
+       bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));
+
+       printf("%s\n", uuid_str);
+}
+
+static void print_incl(struct gatt_db_attribute *attr, void *user_data)
+{
+       struct server *server = user_data;
+       uint16_t handle, start, end;
+       struct gatt_db_attribute *service;
+       bt_uuid_t uuid;
+
+       if (!gatt_db_attribute_get_incl_data(attr, &handle, &start, &end))
+               return;
+
+       service = gatt_db_get_attribute(server->db, start);
+       if (!service)
+               return;
+
+       gatt_db_attribute_get_service_uuid(service, &uuid);
+
+       printf("\t  " COLOR_GREEN "include" COLOR_OFF " - handle: "
+                                       "0x%04x, - start: 0x%04x, end: 0x%04x,"
+                                       "uuid: ", handle, start, end);
+       print_uuid(&uuid);
+}
+
+static void print_desc(struct gatt_db_attribute *attr, void *user_data)
+{
+       printf("\t\t  " COLOR_MAGENTA "descr" COLOR_OFF
+                                       " - handle: 0x%04x, uuid: ",
+                                       gatt_db_attribute_get_handle(attr));
+       print_uuid(gatt_db_attribute_get_type(attr));
+}
+
+static void print_chrc(struct gatt_db_attribute *attr, void *user_data)
+{
+       uint16_t handle, value_handle;
+       uint8_t properties;
+       uint16_t ext_prop;
+       bt_uuid_t uuid;
+
+       if (!gatt_db_attribute_get_char_data(attr, &handle,
+                                                               &value_handle,
+                                                               &properties,
+                                                               &ext_prop,
+                                                               &uuid))
+               return;
+
+       printf("\t  " COLOR_YELLOW "charac" COLOR_OFF
+                               " - start: 0x%04x, value: 0x%04x, "
+                               "props: 0x%02x, ext_prop: 0x%04x, uuid: ",
+                               handle, value_handle, properties, ext_prop);
+       print_uuid(&uuid);
+
+       gatt_db_service_foreach_desc(attr, print_desc, NULL);
+}
+
+static void print_service(struct gatt_db_attribute *attr, void *user_data)
+{
+       struct server *server = user_data;
+       uint16_t start, end;
+       bool primary;
+       bt_uuid_t uuid;
+
+       if (!gatt_db_attribute_get_service_data(attr, &start, &end, &primary,
+                                                                       &uuid))
+               return;
+
+       printf(COLOR_RED "service" COLOR_OFF " - start: 0x%04x, "
+                               "end: 0x%04x, type: %s, uuid: ",
+                               start, end, primary ? "primary" : "secondary");
+       print_uuid(&uuid);
+
+       gatt_db_service_foreach_incl(attr, print_incl, server);
+       gatt_db_service_foreach_char(attr, print_chrc, NULL);
+
+       printf("\n");
+}
+
+static void cmd_services(struct server *server, char *cmd_str)
+{
+       gatt_db_foreach_service(server->db, NULL, print_service, server);
+}
+
+static bool convert_sign_key(char *optarg, uint8_t key[16])
+{
+       int i;
+
+       if (strlen(optarg) != 32) {
+               printf("sign-key length is invalid\n");
+               return false;
+       }
+
+       for (i = 0; i < 16; i++) {
+               if (sscanf(optarg + (i * 2), "%2hhx", &key[i]) != 1)
+                       return false;
+       }
+
+       return true;
+}
+
+static void set_sign_key_usage(void)
+{
+       printf("Usage: set-sign-key [options]\nOptions:\n"
+               "\t -c, --sign-key <remote csrk>\tRemote CSRK\n"
+               "e.g.:\n"
+               "\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n");
+}
+
+static bool remote_counter(uint32_t *sign_cnt, void *user_data)
+{
+       static uint32_t cnt = 0;
+
+       if (*sign_cnt < cnt)
+               return false;
+
+       cnt = *sign_cnt;
+
+       return true;
+}
+
+static void cmd_set_sign_key(struct server *server, char *cmd_str)
+{
+       char *argv[3];
+       int argc = 0;
+       uint8_t key[16];
+
+       memset(key, 0, 16);
+
+       if (!parse_args(cmd_str, 2, argv, &argc)) {
+               set_sign_key_usage();
+               return;
+       }
+
+       if (argc != 2) {
+               set_sign_key_usage();
+               return;
+       }
+
+       if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) {
+               if (convert_sign_key(argv[1], key))
+                       bt_att_set_remote_key(server->att, key, remote_counter,
+                                                                       server);
+       } else
+               set_sign_key_usage();
+}
+
+static void cmd_help(struct server *server, char *cmd_str);
+
+typedef void (*command_func_t)(struct server *server, char *cmd_str);
+
+static struct {
+       char *cmd;
+       command_func_t func;
+       char *doc;
+} command[] = {
+       { "help", cmd_help, "\tDisplay help message" },
+       { "notify", cmd_notify, "\tSend handle-value notification" },
+       { "heart-rate", cmd_heart_rate, "\tHide/Unhide Heart Rate Service" },
+       { "services", cmd_services, "\tEnumerate all services" },
+       { "set-sign-key", cmd_set_sign_key,
+                       "\tSet remote signing key for signed write command"},
+       { }
+};
+
+static void cmd_help(struct server *server, char *cmd_str)
+{
+       int i;
+
+       printf("Commands:\n");
+       for (i = 0; command[i].cmd; i++)
+               printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
+}
+
+static void prompt_read_cb(int fd, uint32_t events, void *user_data)
+{
+       ssize_t read;
+       size_t len = 0;
+       char *line = NULL;
+       char *cmd = NULL, *args;
+       struct server *server = user_data;
+       int i;
+
+       if (events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
+               mainloop_quit();
+               return;
+       }
+
+       read = getline(&line, &len, stdin);
+       if (read < 0)
+               return;
+
+       if (read <= 1) {
+               cmd_help(server, NULL);
+               print_prompt();
+               return;
+       }
+
+       line[read-1] = '\0';
+       args = line;
+
+       while ((cmd = strsep(&args, " \t")))
+               if (*cmd != '\0')
+                       break;
+
+       if (!cmd)
+               goto failed;
+
+       for (i = 0; command[i].cmd; i++) {
+               if (strcmp(command[i].cmd, cmd) == 0)
+                       break;
+       }
+
+       if (command[i].cmd)
+               command[i].func(server, args);
+       else
+               fprintf(stderr, "Unknown command: %s\n", line);
+
+failed:
+       print_prompt();
+
+       free(line);
+}
+
+static void signal_cb(int signum, void *user_data)
+{
+       switch (signum) {
+       case SIGINT:
+       case SIGTERM:
+               mainloop_quit();
+               break;
+       default:
+               break;
+       }
+}
+
+int main(int argc, char *argv[])
+{
+       int opt;
+       bdaddr_t src_addr;
+       int dev_id = -1;
+       int fd;
+       int sec = BT_SECURITY_LOW;
+       uint8_t src_type = BDADDR_LE_PUBLIC;
+       uint16_t mtu = 0;
+       bool hr_visible = false;
+       struct server *server;
+
+       while ((opt = getopt_long(argc, argv, "+hvrs:t:m:i:",
+                                               main_options, NULL)) != -1) {
+               switch (opt) {
+               case 'h':
+                       usage();
+                       return EXIT_SUCCESS;
+               case 'v':
+                       verbose = true;
+                       break;
+               case 'r':
+                       hr_visible = true;
+                       break;
+               case 's':
+                       if (strcmp(optarg, "low") == 0)
+                               sec = BT_SECURITY_LOW;
+                       else if (strcmp(optarg, "medium") == 0)
+                               sec = BT_SECURITY_MEDIUM;
+                       else if (strcmp(optarg, "high") == 0)
+                               sec = BT_SECURITY_HIGH;
+                       else {
+                               fprintf(stderr, "Invalid security level\n");
+                               return EXIT_FAILURE;
+                       }
+                       break;
+               case 't':
+                       if (strcmp(optarg, "random") == 0)
+                               src_type = BDADDR_LE_RANDOM;
+                       else if (strcmp(optarg, "public") == 0)
+                               src_type = BDADDR_LE_PUBLIC;
+                       else {
+                               fprintf(stderr,
+                                       "Allowed types: random, public\n");
+                               return EXIT_FAILURE;
+                       }
+                       break;
+               case 'm': {
+                       int arg;
+
+                       arg = atoi(optarg);
+                       if (arg <= 0) {
+                               fprintf(stderr, "Invalid MTU: %d\n", arg);
+                               return EXIT_FAILURE;
+                       }
+
+                       if (arg > UINT16_MAX) {
+                               fprintf(stderr, "MTU too large: %d\n", arg);
+                               return EXIT_FAILURE;
+                       }
+
+                       mtu = (uint16_t) arg;
+                       break;
+               }
+               case 'i':
+                       dev_id = hci_devid(optarg);
+                       if (dev_id < 0) {
+                               perror("Invalid adapter");
+                               return EXIT_FAILURE;
+                       }
+
+                       break;
+               default:
+                       fprintf(stderr, "Invalid option: %c\n", opt);
+                       return EXIT_FAILURE;
+               }
+       }
+
+       argc -= optind;
+       argv -= optind;
+       optind = 0;
+
+       if (argc) {
+               usage();
+               return EXIT_SUCCESS;
+       }
+
+       if (dev_id == -1)
+               bacpy(&src_addr, BDADDR_ANY);
+       else if (hci_devba(dev_id, &src_addr) < 0) {
+               perror("Adapter not available");
+               return EXIT_FAILURE;
+       }
+
+       fd = l2cap_le_att_listen_and_accept(&src_addr, sec, src_type);
+       if (fd < 0) {
+               fprintf(stderr, "Failed to accept L2CAP ATT connection\n");
+               return EXIT_FAILURE;
+       }
+
+       mainloop_init();
+
+       server = server_create(fd, mtu, hr_visible);
+       if (!server) {
+               close(fd);
+               return EXIT_FAILURE;
+       }
+
+       if (mainloop_add_fd(fileno(stdin),
+                               EPOLLIN | EPOLLRDHUP | EPOLLHUP | EPOLLERR,
+                               prompt_read_cb, server, NULL) < 0) {
+               fprintf(stderr, "Failed to initialize console\n");
+               server_destroy(server);
+
+               return EXIT_FAILURE;
+       }
+
+       printf("Running GATT server\n");
+
+       print_prompt();
+
+       mainloop_run_with_signal(signal_cb, NULL);
+
+       printf("\n\nShutting down...\n");
+
+       server_destroy(server);
+
+       return EXIT_SUCCESS;
+}
diff --git a/drivers/net/wireless/eswin/compile_test.sh b/drivers/net/wireless/eswin/compile_test.sh
new file mode 100644 (file)
index 0000000..faa142f
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+linux_dir=/net/rwlab-srv1/nx_share/linux
+ARCH=${ARCH:-x86}
+CROSS_COMPILE=${CROSS_COMPILE:-x86_64-poky-linux-}
+error=0
+
+if [ ! -d $linux_dir ]
+then
+    echo "Invalid path: ${linux_dir}" >&2
+    exit 1
+fi
+
+for version in $(find $linux_dir -maxdepth 2 -type d -name cevav7 | sort)
+do
+    echo ""
+    echo "#####################################################"
+    echo "Testing $version"
+    echo "#####################################################"
+    ARCH=$ARCH CROSS_COMPILE=$CROSS_COMPILE KERNELDIR=$version \
+    CONFIG_ECRNX_SOFTMAC=m CONFIG_ECRNX_FULLMAC=m CONFIG_ECRNX_FHOST=m make -j 8
+
+    if [ $? -ne  0 ]
+    then
+       ((error++))
+    fi
+done
+
+exit $error
diff --git a/drivers/net/wireless/eswin/ecrnx_bfmer.c b/drivers/net/wireless/eswin/ecrnx_bfmer.c
new file mode 100644 (file)
index 0000000..467da01
--- /dev/null
@@ -0,0 +1,105 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_bfmer.c
+ *
+ * @brief VHT Beamformer function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include <linux/slab.h>
+#include "ecrnx_bfmer.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ******************************************************************************
+ */
+
+int ecrnx_bfmer_report_add(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+                          unsigned int length)
+{
+    gfp_t flags;
+    struct ecrnx_bfmer_report *bfm_report ;
+
+    if (in_softirq())
+        flags = GFP_ATOMIC;
+    else
+        flags = GFP_KERNEL;
+
+    /* Allocate a structure that will contain the beamforming report */
+    bfm_report = kmalloc(sizeof(*bfm_report) + length, flags);
+
+
+    /* Check report allocation */
+    if (!bfm_report) {
+        /* Do not use beamforming */
+        return -1;
+    }
+
+    /* Store report length */
+    bfm_report->length = length;
+
+    /*
+     * Need to provide a Virtual Address to the MAC so that it can
+     * upload the received Beamforming Report in driver memory
+     */
+    bfm_report->dma_addr = dma_map_single(ecrnx_hw->dev, &bfm_report->report[0],
+                                          length, DMA_FROM_DEVICE);
+
+    /* Check DMA mapping result */
+    if (dma_mapping_error(ecrnx_hw->dev, bfm_report->dma_addr)) {
+        /* Free allocated report */
+        kfree(bfm_report);
+        /* And leave */
+        return -1;
+    }
+
+    /* Store report structure */
+    ecrnx_sta->bfm_report = bfm_report;
+
+    return 0;
+}
+
+void ecrnx_bfmer_report_del(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta)
+{
+    /* Verify if a report has been allocated */
+    if (ecrnx_sta->bfm_report) {
+        struct ecrnx_bfmer_report *bfm_report = ecrnx_sta->bfm_report;
+
+        /* Unmap DMA region */
+        dma_unmap_single(ecrnx_hw->dev, bfm_report->dma_addr,
+                         bfm_report->length, DMA_BIDIRECTIONAL);
+
+        /* Free allocated report structure and clean the pointer */
+        kfree(bfm_report);
+        ecrnx_sta->bfm_report = NULL;
+    }
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+u8 ecrnx_bfmer_get_rx_nss(const struct ieee80211_vht_cap *vht_capa)
+{
+    int i;
+    u8 rx_nss = 0;
+    u16 rx_mcs_map = le16_to_cpu(vht_capa->supp_mcs.rx_mcs_map);
+
+    for (i = 7; i >= 0; i--) {
+        u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
+
+        if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+            rx_nss = i + 1;
+            break;
+        }
+    }
+
+    return rx_nss;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
diff --git a/drivers/net/wireless/eswin/ecrnx_bfmer.h b/drivers/net/wireless/eswin/ecrnx_bfmer.h
new file mode 100644 (file)
index 0000000..6bb30ef
--- /dev/null
@@ -0,0 +1,100 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_bfmer.h
+ *
+ * @brief VHT Beamformer function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_BFMER_H_
+#define _ECRNX_BFMER_H_
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include "ecrnx_defs.h"
+
+/**
+ * DEFINES
+ ******************************************************************************
+ */
+
+/// Maximal supported report length (in bytes)
+#define ECRNX_BFMER_REPORT_MAX_LEN     2048
+
+/// Size of the allocated report space (twice the maximum report length)
+#define ECRNX_BFMER_REPORT_SPACE_SIZE  (ECRNX_BFMER_REPORT_MAX_LEN * 2)
+
+/**
+ * TYPE DEFINITIONS
+ ******************************************************************************
+ */
+
+/*
+ * Structure used to store a beamforming report.
+ */
+struct ecrnx_bfmer_report {
+    dma_addr_t dma_addr;    /* Virtual address provided to MAC for
+                               DMA transfer of the Beamforming Report */
+    unsigned int length;    /* Report Length */
+    u8 report[1];           /* Report to be used for VHT TX Beamforming */
+};
+
+/**
+ * FUNCTION DECLARATIONS
+ ******************************************************************************
+ */
+
+/**
+ ******************************************************************************
+ * @brief Allocate memory aiming to contains the Beamforming Report received
+ * from a Beamformee capable capable.
+ * The providing length shall be large enough to contain the VHT Compressed
+ * Beaforming Report and the MU Exclusive part.
+ * It also perform a DMA Mapping providing an address to be provided to the HW
+ * responsible for the DMA transfer of the report.
+ * If successful a struct ecrnx_bfmer_report object is allocated, it's address
+ * is stored in ecrnx_sta->bfm_report.
+ *
+ * @param[in] ecrnx_hw   PHY Information
+ * @param[in] ecrnx_sta  Peer STA Information
+ * @param[in] length    Memory size to be allocated
+ *
+ * @return 0 if operation is successful, else -1.
+ ******************************************************************************
+ */
+int ecrnx_bfmer_report_add(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+                          unsigned int length);
+
+/**
+ ******************************************************************************
+ * @brief Free a previously allocated memory intended to be used for
+ * Beamforming Reports.
+ *
+ * @param[in] ecrnx_hw   PHY Information
+ * @param[in] ecrnx_sta  Peer STA Information
+ *
+ ******************************************************************************
+ */
+void ecrnx_bfmer_report_del(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta);
+
+#ifdef CONFIG_ECRNX_FULLMAC
+/**
+ ******************************************************************************
+ * @brief Parse a Rx VHT-MCS map in order to deduce the maximum number of
+ * Spatial Streams supported by a beamformee.
+ *
+ * @param[in] vht_capa  Received VHT Capability field.
+ *
+ ******************************************************************************
+ */
+u8 ecrnx_bfmer_get_rx_nss(const struct ieee80211_vht_cap *vht_capa);
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#endif /* _ECRNX_BFMER_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_cfgfile.c b/drivers/net/wireless/eswin/ecrnx_cfgfile.c
new file mode 100644 (file)
index 0000000..c753677
--- /dev/null
@@ -0,0 +1,288 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_configparse.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+#include <linux/firmware.h>
+#include <linux/if_ether.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_cfgfile.h"
+#include "ecrnx_debug.h"
+#include "ecrnx_debugfs_func.h"
+
+/**
+ *
+ */
+static const char *ecrnx_find_tag(const u8 *file_data, unsigned int file_size,
+                                 const char *tag_name, unsigned int tag_len)
+{
+    unsigned int curr, line_start = 0, line_size;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Walk through all the lines of the configuration file */
+    while (line_start < file_size) {
+        /* Search the end of the current line (or the end of the file) */
+        for (curr = line_start; curr < file_size; curr++)
+            if (file_data[curr] == '\n')
+                break;
+
+        /* Compute the line size */
+        line_size = curr - line_start;
+
+        /* Check if this line contains the expected tag */
+        if ((line_size == (strlen(tag_name) + tag_len)) &&
+            (!strncmp(&file_data[line_start], tag_name, strlen(tag_name))))
+            return (&file_data[line_start + strlen(tag_name)]);
+
+        /* Move to next line */
+        line_start = curr + 1;
+    }
+
+    /* Tag not found */
+    return NULL;
+}
+
+/**
+ * Parse the Config file used at init time
+ */
+int ecrnx_parse_configfile(struct ecrnx_hw *ecrnx_hw, const char *filename)
+{
+    const struct firmware *config_fw;
+    u8 dflt_mac[ETH_ALEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x5f };
+    int ret;
+    const u8 *tag_ptr;
+    bool mac_flag = false, dbg_level_flag = false, fw_log_lv_flag = false, fw_log_type_flag = false;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
+    ret = firmware_request_nowarn(&config_fw, filename, ecrnx_hw->dev); //avoid the files not exit error
+#else
+    ret = request_firmware(&config_fw, filename, ecrnx_hw->dev);
+#endif
+
+    if (ret == 0) {
+        /* Get MAC Address */
+        tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size, "MAC_ADDR=", strlen("00:00:00:00:00:00"));
+        if (tag_ptr != NULL) {
+            u8 *addr = ecrnx_hw->conf_param.mac_addr;
+            if (sscanf(tag_ptr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+                        addr + 0, addr + 1, addr + 2,
+                        addr + 3, addr + 4, addr + 5) == ETH_ALEN){
+                mac_flag = true;
+            }
+        }
+
+        tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size, "DRIVER_LOG_LEVEL=", strlen("0"));
+        if (tag_ptr != NULL){
+            if(sscanf(tag_ptr, "%hhx", &ecrnx_hw->conf_param.host_driver_log_level) == 1){
+                ecrnx_dbg_level = ecrnx_hw->conf_param.host_driver_log_level;
+                dbg_level_flag = true;
+            }
+        }
+
+        tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size, "FW_LOG_LEVEL=", strlen("0"));
+        if (tag_ptr != NULL){
+            if(sscanf(tag_ptr, "%hhx", &ecrnx_hw->conf_param.fw_log_level) == 1){
+                fw_log_lv_flag = true;
+            }
+        }
+
+        tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size, "FW_LOG_TYPE=", strlen("0"));
+        if (tag_ptr != NULL){
+            if(sscanf(tag_ptr, "%hhx", &ecrnx_hw->conf_param.fw_log_type) == 1){
+                fw_log_type_flag = true;
+            }
+        }
+
+        /* Release the configuration file */
+        release_firmware(config_fw);
+    }
+
+    if(!mac_flag){
+        memcpy(ecrnx_hw->conf_param.mac_addr, dflt_mac, ETH_ALEN);
+    }
+
+    if(!dbg_level_flag){
+        ecrnx_hw->conf_param.host_driver_log_level = ecrnx_dbg_level;
+    }
+
+    if(!fw_log_lv_flag){
+        ecrnx_hw->conf_param.fw_log_level = log_ctl.level;
+    }
+
+    if(!fw_log_type_flag){
+        ecrnx_hw->conf_param.fw_log_type = log_ctl.dir;
+    }
+
+    ECRNX_PRINT("MAC Address is:%pM\n", ecrnx_hw->conf_param.mac_addr);
+    ECRNX_PRINT("host driver log level is:%d \n", ecrnx_hw->conf_param.host_driver_log_level);
+    ECRNX_PRINT("firmware log level is:%d \n", ecrnx_hw->conf_param.fw_log_level);
+
+    if(0 == ecrnx_hw->conf_param.fw_log_type){
+        ECRNX_PRINT("firmware log level type:%d (print to chip's uart) \n", ecrnx_hw->conf_param.fw_log_type);
+    }else if(1 == ecrnx_hw->conf_param.fw_log_type){
+        ECRNX_PRINT("firmware log level type:%d (print to host debugfs) \n", ecrnx_hw->conf_param.fw_log_type);
+    }else if(2 == ecrnx_hw->conf_param.fw_log_type){
+        ECRNX_PRINT("firmware log level type:%d (print to host kernel) \n", ecrnx_hw->conf_param.fw_log_type);
+    }else{
+        ECRNX_ERR("firmware log level type error;\n");
+    }
+    return 0;
+}
+
+/**
+ * Parse the Config file used at init time
+ */
+int ecrnx_parse_phy_configfile(struct ecrnx_hw *ecrnx_hw, const char *filename,
+                              struct ecrnx_phy_conf_file *config, int path)
+{
+    const struct firmware *config_fw;
+    int ret;
+    const u8 *tag_ptr;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if ((ret = request_firmware(&config_fw, filename, ecrnx_hw->dev))) {
+        ECRNX_ERR(KERN_CRIT "%s: Failed to get %s (%d)\n", __func__, filename, ret);
+        return ret;
+    }
+
+    /* Get Trident path mapping */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "TRD_PATH_MAPPING=", strlen("00"));
+    if (tag_ptr != NULL) {
+        u8 val;
+        if (sscanf(tag_ptr, "%hhx", &val) == 1)
+            config->trd.path_mapping = val;
+        else
+            config->trd.path_mapping = path;
+    } else
+        config->trd.path_mapping = path;
+
+    ECRNX_DBG("Trident path mapping is: %d\n", config->trd.path_mapping);
+
+    /* Get DC offset compensation */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "TX_DC_OFF_COMP=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->trd.tx_dc_off_comp) != 1)
+            config->trd.tx_dc_off_comp = 0;
+    } else
+        config->trd.tx_dc_off_comp = 0;
+
+    ECRNX_DBG("TX DC offset compensation is: %08X\n", config->trd.tx_dc_off_comp);
+
+    /* Get Karst TX IQ compensation value for path0 on 2.4GHz */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_TX_IQ_COMP_2_4G_PATH_0=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_2_4G[0]) != 1)
+            config->karst.tx_iq_comp_2_4G[0] = 0x01000000;
+    } else
+        config->karst.tx_iq_comp_2_4G[0] = 0x01000000;
+
+    ECRNX_DBG("Karst TX IQ compensation for path 0 on 2.4GHz is: %08X\n", config->karst.tx_iq_comp_2_4G[0]);
+
+    /* Get Karst TX IQ compensation value for path1 on 2.4GHz */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_TX_IQ_COMP_2_4G_PATH_1=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_2_4G[1]) != 1)
+            config->karst.tx_iq_comp_2_4G[1] = 0x01000000;
+    } else
+        config->karst.tx_iq_comp_2_4G[1] = 0x01000000;
+
+    ECRNX_DBG("Karst TX IQ compensation for path 1 on 2.4GHz is: %08X\n", config->karst.tx_iq_comp_2_4G[1]);
+
+    /* Get Karst RX IQ compensation value for path0 on 2.4GHz */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_RX_IQ_COMP_2_4G_PATH_0=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_2_4G[0]) != 1)
+            config->karst.rx_iq_comp_2_4G[0] = 0x01000000;
+    } else
+        config->karst.rx_iq_comp_2_4G[0] = 0x01000000;
+
+    ECRNX_DBG("Karst RX IQ compensation for path 0 on 2.4GHz is: %08X\n", config->karst.rx_iq_comp_2_4G[0]);
+
+    /* Get Karst RX IQ compensation value for path1 on 2.4GHz */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_RX_IQ_COMP_2_4G_PATH_1=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_2_4G[1]) != 1)
+            config->karst.rx_iq_comp_2_4G[1] = 0x01000000;
+    } else
+        config->karst.rx_iq_comp_2_4G[1] = 0x01000000;
+
+    ECRNX_DBG("Karst RX IQ compensation for path 1 on 2.4GHz is: %08X\n", config->karst.rx_iq_comp_2_4G[1]);
+
+    /* Get Karst TX IQ compensation value for path0 on 5GHz */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_TX_IQ_COMP_5G_PATH_0=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_5G[0]) != 1)
+            config->karst.tx_iq_comp_5G[0] = 0x01000000;
+    } else
+        config->karst.tx_iq_comp_5G[0] = 0x01000000;
+
+    ECRNX_DBG("Karst TX IQ compensation for path 0 on 5GHz is: %08X\n", config->karst.tx_iq_comp_5G[0]);
+
+    /* Get Karst TX IQ compensation value for path1 on 5GHz */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_TX_IQ_COMP_5G_PATH_1=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.tx_iq_comp_5G[1]) != 1)
+            config->karst.tx_iq_comp_5G[1] = 0x01000000;
+    } else
+        config->karst.tx_iq_comp_5G[1] = 0x01000000;
+
+    ECRNX_DBG("Karst TX IQ compensation for path 1 on 5GHz is: %08X\n", config->karst.tx_iq_comp_5G[1]);
+
+    /* Get Karst RX IQ compensation value for path0 on 5GHz */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_RX_IQ_COMP_5G_PATH_0=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_5G[0]) != 1)
+            config->karst.rx_iq_comp_5G[0] = 0x01000000;
+    } else
+        config->karst.rx_iq_comp_5G[0] = 0x01000000;
+
+    ECRNX_DBG("Karst RX IQ compensation for path 0 on 5GHz is: %08X\n", config->karst.rx_iq_comp_5G[0]);
+
+    /* Get Karst RX IQ compensation value for path1 on 5GHz */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_RX_IQ_COMP_5G_PATH_1=", strlen("00000000"));
+    if (tag_ptr != NULL) {
+        if (sscanf(tag_ptr, "%08x", &config->karst.rx_iq_comp_5G[1]) != 1)
+            config->karst.rx_iq_comp_5G[1] = 0x01000000;
+    } else
+        config->karst.rx_iq_comp_5G[1] = 0x01000000;
+
+    ECRNX_DBG("Karst RX IQ compensation for path 1 on 5GHz is: %08X\n", config->karst.rx_iq_comp_5G[1]);
+
+    /* Get Karst default path */
+    tag_ptr = ecrnx_find_tag(config_fw->data, config_fw->size,
+                            "KARST_DEFAULT_PATH=", strlen("00"));
+    if (tag_ptr != NULL) {
+        u8 val;
+        if (sscanf(tag_ptr, "%hhx", &val) == 1)
+            config->karst.path_used = val;
+        else
+            config->karst.path_used = path;
+    } else
+        config->karst.path_used = path;
+
+    ECRNX_DBG("Karst default path is: %d\n", config->karst.path_used);
+
+    /* Release the configuration file */
+    release_firmware(config_fw);
+
+    return 0;
+}
+
diff --git a/drivers/net/wireless/eswin/ecrnx_cfgfile.h b/drivers/net/wireless/eswin/ecrnx_cfgfile.h
new file mode 100644 (file)
index 0000000..bdf7eaa
--- /dev/null
@@ -0,0 +1,37 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_cfgfile.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_CFGFILE_H_
+#define _ECRNX_CFGFILE_H_
+
+/*
+ * Structure used to retrieve information from the Config file used at Initialization time
+ */
+struct ecrnx_conf_file {
+    u8 mac_addr[ETH_ALEN];
+    u8 host_driver_log_level;
+    u8 fw_log_level;
+    u8 fw_log_type;
+};
+
+/*
+ * Structure used to retrieve information from the PHY Config file used at Initialization time
+ */
+struct ecrnx_phy_conf_file {
+    struct phy_trd_cfg_tag trd;
+    struct phy_karst_cfg_tag karst;
+    struct phy_cataxia_cfg_tag cataxia;
+};
+
+int ecrnx_parse_configfile(struct ecrnx_hw *ecrnx_hw, const char *filename);
+int ecrnx_parse_phy_configfile(struct ecrnx_hw *ecrnx_hw, const char *filename,
+                              struct ecrnx_phy_conf_file *config, int path);
+
+#endif /* _ECRNX_CFGFILE_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_cmds.c b/drivers/net/wireless/eswin/ecrnx_cmds.c
new file mode 100644 (file)
index 0000000..5e4781a
--- /dev/null
@@ -0,0 +1,325 @@
+/**
+ ******************************************************************************
+ *
+ * ecrnx_cmds.c
+ *
+ * Handles queueing (push to IPC, ack/cfm from IPC) of commands issued to
+ * LMAC FW
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/list.h>
+
+#include "ecrnx_cmds.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_strs.h"
+#define CREATE_TRACE_POINTS
+#include "ecrnx_events.h"
+
+/**
+ *
+ */
+static void cmd_dump(const struct ecrnx_cmd *cmd)
+{
+#ifndef CONFIG_ECRNX_FHOST
+    ECRNX_PRINT("tkn[%d]  flags:%04x  result:%3d  cmd:%4d-%-24s - reqcfm(%4d-%-s)\n",
+           cmd->tkn, cmd->flags, cmd->result, cmd->id, ECRNX_ID2STR(cmd->id),
+           cmd->reqid, cmd->reqid != (lmac_msg_id_t)-1 ? ECRNX_ID2STR(cmd->reqid) : "none");
+#endif
+}
+
+/**
+ *
+ */
+static void cmd_complete(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd *cmd)
+{
+    lockdep_assert_held(&cmd_mgr->lock);
+
+    list_del(&cmd->list);
+    cmd_mgr->queue_sz--;
+
+    cmd->flags |= ECRNX_CMD_FLAG_DONE;
+    if (cmd->flags & ECRNX_CMD_FLAG_NONBLOCK) {
+        kfree(cmd);
+    } else {
+        if (ECRNX_CMD_WAIT_COMPLETE(cmd->flags)) {
+            cmd->result = 0;
+            complete(&cmd->complete);
+        }
+    }
+}
+
+/**
+ *
+ */
+static int cmd_mgr_queue(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd *cmd)
+{
+    struct ecrnx_hw *ecrnx_hw = container_of(cmd_mgr, struct ecrnx_hw, cmd_mgr);
+    bool defer_push = false;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    trace_msg_send(cmd->id);
+
+    spin_lock_bh(&cmd_mgr->lock);
+
+    if (cmd_mgr->state == ECRNX_CMD_MGR_STATE_CRASHED) {
+        ECRNX_PRINT(KERN_CRIT"cmd queue crashed\n");
+        cmd->result = -EPIPE;
+        spin_unlock_bh(&cmd_mgr->lock);
+        return -EPIPE;
+    }
+
+    #ifndef CONFIG_ECRNX_FHOST
+    if (!list_empty(&cmd_mgr->cmds)) {
+        struct ecrnx_cmd *last;
+
+        if (cmd_mgr->queue_sz == cmd_mgr->max_queue_sz) {
+            ECRNX_ERR(KERN_CRIT"Too many cmds (%d) already queued\n",
+                   cmd_mgr->max_queue_sz);
+            cmd->result = -ENOMEM;
+            spin_unlock_bh(&cmd_mgr->lock);
+            return -ENOMEM;
+        }
+        last = list_entry(cmd_mgr->cmds.prev, struct ecrnx_cmd, list);
+        if (last->flags & (ECRNX_CMD_FLAG_WAIT_ACK | ECRNX_CMD_FLAG_WAIT_PUSH)) {
+#if 0 // queue even NONBLOCK command.
+            if (cmd->flags & ECRNX_CMD_FLAG_NONBLOCK) {
+                printk(KERN_CRIT"cmd queue busy\n");
+                cmd->result = -EBUSY;
+                spin_unlock_bh(&cmd_mgr->lock);
+                return -EBUSY;
+            }
+#endif
+            cmd->flags |= ECRNX_CMD_FLAG_WAIT_PUSH;
+            defer_push = true;
+        }
+    }
+    #endif
+
+    cmd->flags |= ECRNX_CMD_FLAG_WAIT_ACK;
+    if (cmd->flags & ECRNX_CMD_FLAG_REQ_CFM)
+        cmd->flags |= ECRNX_CMD_FLAG_WAIT_CFM;
+
+    cmd->tkn    = cmd_mgr->next_tkn++;
+    cmd->result = -EINTR;
+
+    if (!(cmd->flags & ECRNX_CMD_FLAG_NONBLOCK))
+        init_completion(&cmd->complete);
+
+    list_add_tail(&cmd->list, &cmd_mgr->cmds);
+    cmd_mgr->queue_sz++;
+    spin_unlock_bh(&cmd_mgr->lock);
+
+    if (!defer_push) {
+        ecrnx_ipc_msg_push(ecrnx_hw, cmd, ECRNX_CMD_A2EMSG_LEN(cmd->a2e_msg));
+        kfree(cmd->a2e_msg);
+    }
+
+    if (!(cmd->flags & ECRNX_CMD_FLAG_NONBLOCK)) {
+        #ifdef CONFIG_ECRNX_FHOST
+        if (wait_for_completion_killable(&cmd->complete)) {
+            if (cmd->flags & ECRNX_CMD_FLAG_WAIT_ACK)
+                up(&ecrnx_hw->term.fw_cmd);
+            cmd->result = -EINTR;
+            spin_lock_bh(&cmd_mgr->lock);
+            cmd_complete(cmd_mgr, cmd);
+            spin_unlock_bh(&cmd_mgr->lock);
+            /* TODO: kill the cmd at fw level */
+        } else {
+            if (cmd->flags & ECRNX_CMD_FLAG_WAIT_ACK)
+                up(&ecrnx_hw->term.fw_cmd);
+        }
+        #else
+        unsigned long tout = msecs_to_jiffies(ECRNX_80211_CMD_TIMEOUT_MS * cmd_mgr->queue_sz);
+        if (!wait_for_completion_killable_timeout(&cmd->complete, tout)) {
+            ECRNX_PRINT("cmd timed-out queue_sz:%d\n",cmd_mgr->queue_sz);
+            cmd_dump(cmd);
+            spin_lock_bh(&cmd_mgr->lock);
+            //cmd_mgr->state = ECRNX_CMD_MGR_STATE_CRASHED;
+            if (!(cmd->flags & ECRNX_CMD_FLAG_DONE)) {
+                cmd->result = -ETIMEDOUT;
+                cmd_complete(cmd_mgr, cmd);
+            }
+            spin_unlock_bh(&cmd_mgr->lock);
+        }
+        #endif
+    } else {
+        cmd->result = 0;
+    }
+
+    return 0;
+}
+
+/**
+ *
+ */
+static int cmd_mgr_llind(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd *cmd)
+{
+    struct ecrnx_cmd *cur, *acked = NULL, *next = NULL;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    spin_lock(&cmd_mgr->lock);
+    list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+        if (!acked) {
+            if (cur->tkn == cmd->tkn) {
+                if (WARN_ON_ONCE(cur != cmd)) {
+                    cmd_dump(cmd);
+                }
+                acked = cur;
+                continue;
+            }
+        }
+        if (cur->flags & ECRNX_CMD_FLAG_WAIT_PUSH) {
+                next = cur;
+                break;
+        }
+    }
+    if (!acked) {
+        ECRNX_PRINT(KERN_CRIT "Error: acked cmd not found\n");
+    } else {
+        cmd->flags &= ~ECRNX_CMD_FLAG_WAIT_ACK;
+        if (ECRNX_CMD_WAIT_COMPLETE(cmd->flags))
+            cmd_complete(cmd_mgr, cmd);
+    }
+    if (next) {
+        struct ecrnx_hw *ecrnx_hw = container_of(cmd_mgr, struct ecrnx_hw, cmd_mgr);
+        next->flags &= ~ECRNX_CMD_FLAG_WAIT_PUSH;
+        ecrnx_ipc_msg_push(ecrnx_hw, next, ECRNX_CMD_A2EMSG_LEN(next->a2e_msg));
+        kfree(next->a2e_msg);
+    }
+    spin_unlock(&cmd_mgr->lock);
+
+    return 0;
+}
+
+
+
+static int cmd_mgr_run_callback(struct ecrnx_hw *ecrnx_hw, struct ecrnx_cmd *cmd,
+                                struct ecrnx_cmd_e2amsg *msg, msg_cb_fct cb)
+{
+    int res;
+
+    if (! cb)
+        return 0;
+
+    spin_lock(&ecrnx_hw->cb_lock);
+    res = cb(ecrnx_hw, cmd, msg);
+    spin_unlock(&ecrnx_hw->cb_lock);
+
+    return res;
+}
+
+/**
+ *
+
+ */
+static int cmd_mgr_msgind(struct ecrnx_cmd_mgr *cmd_mgr, struct ecrnx_cmd_e2amsg *msg,
+                          msg_cb_fct cb)
+{
+    struct ecrnx_hw *ecrnx_hw = container_of(cmd_mgr, struct ecrnx_hw, cmd_mgr);
+    struct ecrnx_cmd *cmd;
+    bool found = false;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    trace_msg_recv(msg->id);
+
+    spin_lock(&cmd_mgr->lock);
+    list_for_each_entry(cmd, &cmd_mgr->cmds, list) {
+        if (cmd->reqid == msg->id &&
+            (cmd->flags & ECRNX_CMD_FLAG_WAIT_CFM)) {
+
+            if (!cmd_mgr_run_callback(ecrnx_hw, cmd, msg, cb)) {
+                found = true;
+                cmd->flags &= ~ECRNX_CMD_FLAG_WAIT_CFM;
+
+                if (WARN((msg->param_len > ECRNX_CMD_E2AMSG_LEN_MAX),
+                         "Unexpect E2A msg len %d > %d\n", msg->param_len,
+                         ECRNX_CMD_E2AMSG_LEN_MAX)) {
+                    msg->param_len = ECRNX_CMD_E2AMSG_LEN_MAX;
+                }
+
+                if (cmd->e2a_msg && msg->param_len)
+                    memcpy(cmd->e2a_msg, &msg->param, msg->param_len);
+
+                if (ECRNX_CMD_WAIT_COMPLETE(cmd->flags))
+                    cmd_complete(cmd_mgr, cmd);
+
+                break;
+            }
+        }
+    }
+    spin_unlock(&cmd_mgr->lock);
+
+    if (!found)
+        cmd_mgr_run_callback(ecrnx_hw, NULL, msg, cb);
+
+    ECRNX_DBG("%s exit!! \n", __func__);
+    return 0;
+}
+
+/**
+ *
+ */
+static void cmd_mgr_print(struct ecrnx_cmd_mgr *cmd_mgr)
+{
+    struct ecrnx_cmd *cur;
+
+    spin_lock_bh(&cmd_mgr->lock);
+    ECRNX_PRINT("q_sz/max: %2d / %2d - next tkn: %d\n",
+             cmd_mgr->queue_sz, cmd_mgr->max_queue_sz,
+             cmd_mgr->next_tkn);
+    list_for_each_entry(cur, &cmd_mgr->cmds, list) {
+        cmd_dump(cur);
+    }
+    spin_unlock_bh(&cmd_mgr->lock);
+}
+
+/**
+ *
+ */
+static void cmd_mgr_drain(struct ecrnx_cmd_mgr *cmd_mgr)
+{
+    struct ecrnx_cmd *cur, *nxt;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    spin_lock_bh(&cmd_mgr->lock);
+    list_for_each_entry_safe(cur, nxt, &cmd_mgr->cmds, list) {
+        list_del(&cur->list);
+        cmd_mgr->queue_sz--;
+        if (!(cur->flags & ECRNX_CMD_FLAG_NONBLOCK))
+            complete(&cur->complete);
+    }
+    spin_unlock_bh(&cmd_mgr->lock);
+}
+
+/**
+ *
+ */
+void ecrnx_cmd_mgr_init(struct ecrnx_cmd_mgr *cmd_mgr)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    INIT_LIST_HEAD(&cmd_mgr->cmds);
+    spin_lock_init(&cmd_mgr->lock);
+    cmd_mgr->max_queue_sz = ECRNX_CMD_MAX_QUEUED;
+    cmd_mgr->queue  = &cmd_mgr_queue;
+    cmd_mgr->print  = &cmd_mgr_print;
+    cmd_mgr->drain  = &cmd_mgr_drain;
+    cmd_mgr->llind  = &cmd_mgr_llind;
+    cmd_mgr->msgind = &cmd_mgr_msgind;
+}
+
+/**
+ *
+ */
+void ecrnx_cmd_mgr_deinit(struct ecrnx_cmd_mgr *cmd_mgr)
+{
+    cmd_mgr->print(cmd_mgr);
+    cmd_mgr->drain(cmd_mgr);
+    memset(cmd_mgr, 0, sizeof(*cmd_mgr));
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_cmds.h b/drivers/net/wireless/eswin/ecrnx_cmds.h
new file mode 100644 (file)
index 0000000..58533fc
--- /dev/null
@@ -0,0 +1,102 @@
+/**
+ ******************************************************************************
+ *
+ * ecrnx_cmds.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_CMDS_H_
+#define _ECRNX_CMDS_H_
+
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include "lmac_msg.h"
+
+#ifdef CONFIG_ECRNX_SDM
+#define ECRNX_80211_CMD_TIMEOUT_MS    (20 * 300)
+#elif defined(CONFIG_ECRNX_FHOST)
+#define ECRNX_80211_CMD_TIMEOUT_MS    (10000)
+#else
+#define ECRNX_80211_CMD_TIMEOUT_MS    (20 * 300) //300
+#endif
+
+#define ECRNX_CMD_FLAG_NONBLOCK      BIT(0)
+#define ECRNX_CMD_FLAG_REQ_CFM       BIT(1)
+#define ECRNX_CMD_FLAG_WAIT_PUSH     BIT(2)
+#define ECRNX_CMD_FLAG_WAIT_ACK      BIT(3)
+#define ECRNX_CMD_FLAG_WAIT_CFM      BIT(4)
+#define ECRNX_CMD_FLAG_DONE          BIT(5)
+/* ATM IPC design makes it possible to get the CFM before the ACK,
+ * otherwise this could have simply been a state enum */
+#define ECRNX_CMD_WAIT_COMPLETE(flags) \
+    (!(flags & (ECRNX_CMD_FLAG_WAIT_ACK | ECRNX_CMD_FLAG_WAIT_CFM)))
+
+#define ECRNX_CMD_MAX_QUEUED         8
+
+#ifdef CONFIG_ECRNX_FHOST
+#include "ipc_fhost.h"
+#define ecrnx_cmd_e2amsg ipc_fhost_msg
+#define ecrnx_cmd_a2emsg ipc_fhost_msg
+#define ECRNX_CMD_A2EMSG_LEN(m) (m->param_len)
+#define ECRNX_CMD_E2AMSG_LEN_MAX IPC_FHOST_MSG_BUF_SIZE
+struct ecrnx_term_stream;
+
+#else /* !CONFIG_ECRNX_FHOST*/
+#include "ipc_shared.h"
+#define ecrnx_cmd_e2amsg ipc_e2a_msg
+#define ecrnx_cmd_a2emsg lmac_msg
+#define ECRNX_CMD_A2EMSG_LEN(m) (sizeof(struct lmac_msg) + m->param_len)
+#define ECRNX_CMD_E2AMSG_LEN_MAX (IPC_E2A_MSG_PARAM_SIZE * 4)
+
+#endif /* CONFIG_ECRNX_FHOST*/
+
+struct ecrnx_hw;
+struct ecrnx_cmd;
+typedef int (*msg_cb_fct)(struct ecrnx_hw *ecrnx_hw, struct ecrnx_cmd *cmd,
+                          struct ecrnx_cmd_e2amsg *msg);
+
+enum ecrnx_cmd_mgr_state {
+    ECRNX_CMD_MGR_STATE_DEINIT,
+    ECRNX_CMD_MGR_STATE_INITED,
+    ECRNX_CMD_MGR_STATE_CRASHED,
+};
+
+struct ecrnx_cmd {
+    struct list_head list;
+    lmac_msg_id_t id;
+    lmac_msg_id_t reqid;
+    struct ecrnx_cmd_a2emsg *a2e_msg;
+    char *e2a_msg;
+    u32 tkn;
+    u16 flags;
+
+    struct completion complete;
+    u32 result;
+    #ifdef CONFIG_ECRNX_FHOST
+    struct ecrnx_term_stream *stream;
+    #endif
+};
+
+struct ecrnx_cmd_mgr {
+    enum ecrnx_cmd_mgr_state state;
+    spinlock_t lock;
+    u32 next_tkn;
+    u32 queue_sz;
+    u32 max_queue_sz;
+
+    struct list_head cmds;
+
+    int  (*queue)(struct ecrnx_cmd_mgr *, struct ecrnx_cmd *);
+    int  (*llind)(struct ecrnx_cmd_mgr *, struct ecrnx_cmd *);
+    int  (*msgind)(struct ecrnx_cmd_mgr *, struct ecrnx_cmd_e2amsg *, msg_cb_fct);
+    void (*print)(struct ecrnx_cmd_mgr *);
+    void (*drain)(struct ecrnx_cmd_mgr *);
+};
+
+void ecrnx_cmd_mgr_init(struct ecrnx_cmd_mgr *cmd_mgr);
+void ecrnx_cmd_mgr_deinit(struct ecrnx_cmd_mgr *cmd_mgr);
+
+#endif /* _ECRNX_CMDS_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_compat.h b/drivers/net/wireless/eswin/ecrnx_compat.h
new file mode 100644 (file)
index 0000000..beba2e7
--- /dev/null
@@ -0,0 +1,525 @@
+/**\r
+ ******************************************************************************\r
+ *\r
+ * @file ecrnx_compat.h\r
+ *\r
+ * Ensure driver compilation for linux 4.4 to 5.9\r
+ *\r
+ * To avoid too many #if LINUX_VERSION_CODE if the code, when prototype change\r
+ * between different kernel version:\r
+ * - For external function, define a macro whose name is the function name with\r
+ *   _compat suffix and prototype (actually the number of parameter) of the\r
+ *   latest version. Then latest version this macro simply call the function\r
+ *   and for older kernel version it call the function adapting the api.\r
+ * - For internal function (e.g. cfg80211_ops) do the same but the macro name\r
+ *   doesn't need to have the _compat suffix when the function is not used\r
+ *   directly by the driver\r
+ *\r
+ * Copyright (C) ESWIN 2020\r
+ *\r
+ ******************************************************************************\r
+ */\r
+#ifndef _ECRNX_COMPAT_H_\r
+#define _ECRNX_COMPAT_H_\r
+#include <linux/version.h>\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)\r
+#error "Minimum kernel version supported is 3.8"\r
+#endif\r
+\r
+/******************************************************************************\r
+ * Generic\r
+ *****************************************************************************/\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)\r
+#define __bf_shf(x) (__builtin_ffsll(x) - 1)\r
+#define FIELD_PREP(_mask, _val) \\r
+    (((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask))\r
+#else\r
+#include <linux/bitfield.h>\r
+#endif // 4.9\r
+\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(5,13,0)\r
+#define IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA IEEE80211_HE_PHY_CAP3_RX_PARTIAL_BW_SU_IN_20MHZ_MU\r
+#endif\r
+\r
+/******************************************************************************\r
+ * CFG80211\r
+ *****************************************************************************/\r
\r
+ #if LINUX_VERSION_CODE > KERNEL_VERSION(5, 12, 0)\r
+#define regulatory_set_wiphy_regd_sync_rtnl regulatory_set_wiphy_regd_sync\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 11, 0)\r
+#define cfg80211_ch_switch_started_notify(dev, chandef, count) \\r
+    cfg80211_ch_switch_started_notify(dev, chandef, count, 1)\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)\r
+#define WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT     0\r
+\r
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242                    0x00\r
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484                    0x40\r
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996                    0x80\r
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996                  0xc0\r
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK                   0xc0\r
+\r
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_0US           0x00\r
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_8US           0x40\r
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US          0x80\r
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_RESERVED      0xc0\r
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK          0xc0\r
+\r
+#endif // 5.1\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)\r
+#define cfg80211_notify_new_peer_candidate(dev, addr, ie, ie_len, sig_dbm, gfp) \\r
+    cfg80211_notify_new_peer_candidate(dev, addr, ie, ie_len, gfp)\r
+\r
+#define WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT    BIT(5)\r
+#define WLAN_EXT_CAPA10_TWT_RESPONDER_SUPPORT    BIT(6)\r
+\r
+#endif // 5.0\r
+\r
+#define WLAN_EXT_CAPA5_QOS_MAP_SUPPORT           BIT(0)\r
+\r
+\r
+struct ecrnx_element {\r
+    u8 id;\r
+    u8 datalen;\r
+    u8 data[];\r
+} __packed;\r
+\r
+\r
+#define for_each_ecrnx_element(_elem, _data, _datalen)                 \\r
+       for (_elem = (const struct ecrnx_element *)(_data);                     \\r
+            (const u8 *)(_data) + (_datalen) - (const u8 *)_elem >=    \\r
+               (int)sizeof(*_elem) &&                                  \\r
+            (const u8 *)(_data) + (_datalen) - (const u8 *)_elem >=    \\r
+               (int)sizeof(*_elem) + _elem->datalen;                   \\r
+            _elem = (const struct ecrnx_element *)(_elem->data + _elem->datalen))\r
+\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)\r
+#define IEEE80211_RADIOTAP_HE 23\r
+#define IEEE80211_RADIOTAP_HE_MU 24\r
+#endif\r
+\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,19,0)\r
+struct ieee80211_radiotap_he {\r
+       __le16 data1, data2, data3, data4, data5, data6;\r
+};\r
+\r
+enum ieee80211_radiotap_he_bits {\r
+    IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MASK            = 3,\r
+    IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU              = 0,\r
+    IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU  = 1,\r
+    IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU              = 2,\r
+    IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG            = 3,\r
+\r
+    IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN        = 0x0004,\r
+    IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN      = 0x0008,\r
+    IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN            = 0x0010,\r
+    IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN = 0x0020,\r
+    IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN = 0x0040,\r
+    IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN   = 0x0080,\r
+    IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN     = 0x0100,\r
+    IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN             = 0x0200,\r
+    IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN       = 0x0400,\r
+    IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN      = 0x0800,\r
+    IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN      = 0x1000,\r
+    IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN      = 0x2000,\r
+    IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN      = 0x4000,\r
+    IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN  = 0x8000,\r
+\r
+    IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN        = 0x0001,\r
+    IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN               = 0x0002,\r
+    IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN     = 0x0004,\r
+    IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN      = 0x0008,\r
+    IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN             = 0x0010,\r
+    IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN      = 0x0020,\r
+    IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN             = 0x0040,\r
+    IEEE80211_RADIOTAP_HE_DATA2_MIDAMBLE_KNOWN = 0x0080,\r
+    IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET              = 0x3f00,\r
+    IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN        = 0x4000,\r
+    IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC  = 0x8000,\r
+\r
+    IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR              = 0x003f,\r
+    IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE            = 0x0040,\r
+    IEEE80211_RADIOTAP_HE_DATA3_UL_DL          = 0x0080,\r
+    IEEE80211_RADIOTAP_HE_DATA3_DATA_MCS               = 0x0f00,\r
+    IEEE80211_RADIOTAP_HE_DATA3_DATA_DCM               = 0x1000,\r
+    IEEE80211_RADIOTAP_HE_DATA3_CODING         = 0x2000,\r
+    IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG   = 0x4000,\r
+    IEEE80211_RADIOTAP_HE_DATA3_STBC           = 0x8000,\r
+\r
+    IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE       = 0x000f,\r
+    IEEE80211_RADIOTAP_HE_DATA4_MU_STA_ID              = 0x7ff0,\r
+    IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1 = 0x000f,\r
+    IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2 = 0x00f0,\r
+    IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3 = 0x0f00,\r
+    IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4 = 0xf000,\r
+\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC       = 0x000f,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ = 0,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ = 1,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ = 2,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ        = 3,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_26T   = 4,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_52T   = 5,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_106T  = 6,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_242T  = 7,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_484T  = 8,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_996T  = 9,\r
+    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_2x996T        = 10,\r
+\r
+    IEEE80211_RADIOTAP_HE_DATA5_GI                     = 0x0030,\r
+    IEEE80211_RADIOTAP_HE_DATA5_GI_0_8                 = 0,\r
+    IEEE80211_RADIOTAP_HE_DATA5_GI_1_6                 = 1,\r
+    IEEE80211_RADIOTAP_HE_DATA5_GI_3_2                 = 2,\r
+\r
+    IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE               = 0x00c0,\r
+    IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN               = 0,\r
+    IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X                    = 1,\r
+    IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X                    = 2,\r
+    IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X                    = 3,\r
+    IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS   = 0x0700,\r
+    IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD            = 0x3000,\r
+    IEEE80211_RADIOTAP_HE_DATA5_TXBF           = 0x4000,\r
+    IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG            = 0x8000,\r
+\r
+    IEEE80211_RADIOTAP_HE_DATA6_NSTS           = 0x000f,\r
+    IEEE80211_RADIOTAP_HE_DATA6_DOPPLER                = 0x0010,\r
+    IEEE80211_RADIOTAP_HE_DATA6_TXOP           = 0x7f00,\r
+    IEEE80211_RADIOTAP_HE_DATA6_MIDAMBLE_PDCTY = 0x8000,\r
+    };\r
+\r
+struct ieee80211_radiotap_he_mu {\r
+       __le16 flags1, flags2;\r
+       u8 ru_ch1[4];\r
+       u8 ru_ch2[4];\r
+};\r
+\r
+enum ieee80211_radiotap_he_mu_bits {\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS          = 0x000f,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN            = 0x0010,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM          = 0x0020,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN            = 0x0040,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN       = 0x0080,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN               = 0x0100,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN               = 0x0200,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN       = 0x1000,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU             = 0x2000,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN   = 0x4000,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN     = 0x8000,\r
+\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW   = 0x0003,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_20MHZ     = 0x0000,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_40MHZ     = 0x0001,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_80MHZ     = 0x0002,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_160MHZ    = 0x0003,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN     = 0x0004,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP         = 0x0008,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS   = 0x00f0,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW = 0x0300,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN= 0x0400,\r
+    IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU             = 0x0800,\r
+};\r
+\r
+enum {\r
+    IEEE80211_HE_MCS_SUPPORT_0_7    = 0,\r
+    IEEE80211_HE_MCS_SUPPORT_0_9    = 1,\r
+    IEEE80211_HE_MCS_SUPPORT_0_11   = 2,\r
+    IEEE80211_HE_MCS_NOT_SUPPORTED  = 3,\r
+};\r
+#endif // 4.19\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)\r
+#define cfg80211_probe_status(ndev, addr, cookie, ack, ack_pwr, pwr_valid, gfp) \\r
+    cfg80211_probe_status(ndev, addr, cookie, ack, gfp)\r
+#endif // 4.17\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)\r
+#define cfg80211_disconnected(dev, reason, ie, len, local, gfp) \\r
+        cfg80211_disconnected(dev, reason, ie, len, gfp)\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)\r
+#define ieee80211_chandef_to_operating_class(chan_def, op_class) 0\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)\r
+#define ecrnx_cfg80211_add_iface(wiphy, name, name_assign_type, type, params) \\r
+    ecrnx_cfg80211_add_iface(wiphy, name, name_assign_type, type, u32 *flags, params)\r
+\r
+#define ecrnx_cfg80211_change_iface(wiphy, dev, type, params) \\r
+    ecrnx_cfg80211_change_iface(wiphy, dev, type, u32 *flags, params)\r
+\r
+#define CCFS0(vht) vht->center_freq_seg1_idx\r
+#define CCFS1(vht) vht->center_freq_seg2_idx\r
+\r
+#if 0\r
+#define nla_parse(tb, maxtype, head, len, policy, extack)       \\r
+    nla_parse(tb, maxtype, head, len, policy)\r
+#endif\r
+\r
+struct cfg80211_roam_info {\r
+       struct ieee80211_channel *channel;\r
+       struct cfg80211_bss *bss;\r
+       const u8 *bssid;\r
+       const u8 *req_ie;\r
+       size_t req_ie_len;\r
+       const u8 *resp_ie;\r
+       size_t resp_ie_len;\r
+};\r
+\r
+#define cfg80211_roamed(_dev, _info, _gfp) \\r
+    cfg80211_roamed(_dev, (_info)->channel, (_info)->bssid, (_info)->req_ie, \\r
+                    (_info)->req_ie_len, (_info)->resp_ie, (_info)->resp_ie_len, _gfp)\r
+\r
+#else // 4.12\r
+\r
+#define CCFS0(vht) vht->center_freq_seg0_idx\r
+#define CCFS1(vht) vht->center_freq_seg1_idx\r
+#endif // 4.12\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)\r
+#define ecrnx_cfg80211_cqm_rssi_notify(dev, event, level, gfp) \\r
+    cfg80211_cqm_rssi_notify(dev, event, gfp)\r
+#else\r
+#define ecrnx_cfg80211_cqm_rssi_notify(dev, event, level, gfp) \\r
+    cfg80211_cqm_rssi_notify(dev, event, level, gfp)\r
+#endif // 4.11\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)\r
+#define ieee80211_amsdu_to_8023s(skb, list, addr, iftype, extra_headroom, check_da, check_sa) \\r
+    ieee80211_amsdu_to_8023s(skb, list, addr, iftype, extra_headroom, false)\r
+#endif // 4.9\r
+\r
+#if LINUX_VERSION_CODE  < KERNEL_VERSION(4, 7, 0)\r
+#define NUM_NL80211_BANDS IEEE80211_NUM_BANDS\r
+#endif // 4.7\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)\r
+#define SURVEY_INFO_TIME          SURVEY_INFO_CHANNEL_TIME\r
+#define SURVEY_INFO_TIME_BUSY     SURVEY_INFO_CHANNEL_TIME_BUSY\r
+#define SURVEY_INFO_TIME_EXT_BUSY SURVEY_INFO_CHANNEL_TIME_EXT_BUSY\r
+#define SURVEY_INFO_TIME_RX       SURVEY_INFO_CHANNEL_TIME_RX\r
+#define SURVEY_INFO_TIME_TX       SURVEY_INFO_CHANNEL_TIME_TX\r
+\r
+#define SURVEY_TIME(s) s->channel_time\r
+#define SURVEY_TIME_BUSY(s) s->channel_time_busy\r
+#else\r
+#define SURVEY_TIME(s) s->time\r
+#define SURVEY_TIME_BUSY(s) s->time_busy\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)\r
+#define cfg80211_ch_switch_started_notify(dev, chandef, count)\r
+\r
+#define WLAN_BSS_COEX_INFORMATION_REQUEST      BIT(0)\r
+#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING   BIT(2)\r
+#define WLAN_EXT_CAPA4_TDLS_BUFFER_STA         BIT(4)\r
+#define WLAN_EXT_CAPA4_TDLS_PEER_PSM           BIT(5)\r
+#define WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH                BIT(6)\r
+#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED   BIT(7)\r
+#define NL80211_FEATURE_TDLS_CHANNEL_SWITCH     0\r
+\r
+#define STA_TDLS_INITIATOR(sta) 0\r
+\r
+#define REGULATORY_IGNORE_STALE_KICKOFF 0\r
+#else\r
+#define STA_TDLS_INITIATOR(sta) sta->tdls_initiator\r
+#endif\r
+\r
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))\r
+#define cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags)             \\r
+        cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags, GFP_ATOMIC)\r
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)\r
+#define cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, flags)             \\r
+        cfg80211_rx_mgmt(wdev, freq, rssi, buf, len, GFP_ATOMIC)\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)\r
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)\r
+#define rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, initiator, buf, len) \\r
+    rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, buf, len)\r
+#else\r
+#define rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, peer_capability, initiator, buf, len) \\r
+    rwnx_cfg80211_tdls_mgmt(wiphy, dev, peer, act, tok, status, buf, len)\r
+#endif\r
+\r
+#include <linux/types.h>\r
+\r
+struct ieee80211_wmm_ac_param {\r
+       u8 aci_aifsn; /* AIFSN, ACM, ACI */\r
+       u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */\r
+       __le16 txop_limit;\r
+} __packed;\r
+\r
+struct ieee80211_wmm_param_ie {\r
+       u8 element_id; /* Element ID: 221 (0xdd); */\r
+       u8 len; /* Length: 24 */\r
+       /* required fields for WMM version 1 */\r
+       u8 oui[3]; /* 00:50:f2 */\r
+       u8 oui_type; /* 2 */\r
+       u8 oui_subtype; /* 1 */\r
+       u8 version; /* 1 for WMM version 1.0 */\r
+       u8 qos_info; /* AP/STA specific QoS info */\r
+       u8 reserved; /* 0 */\r
+       /* AC_BE, AC_BK, AC_VI, AC_VO */\r
+       struct ieee80211_wmm_ac_param ac[4];\r
+} __packed;\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)\r
+/**\r
+ * struct cfg80211_update_ft_ies_params - FT IE Information\r
+ *\r
+ * This structure provides information needed to update the fast transition IE\r
+ *\r
+ * @md: The Mobility Domain ID, 2 Octet value\r
+ * @ie: Fast Transition IEs\r
+ * @ie_len: Length of ft_ie in octets\r
+ */\r
+struct cfg80211_update_ft_ies_params {\r
+       u16 md;\r
+       const u8 *ie;\r
+       size_t ie_len;\r
+};\r
+#endif\r
+\r
+/******************************************************************************\r
+ * MAC80211\r
+ *****************************************************************************/\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0)\r
+#define ecrnx_ops_cancel_remain_on_channel(hw, vif) \\r
+    ecrnx_ops_cancel_remain_on_channel(hw)\r
+#endif // 5.3\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)\r
+#define ecrnx_ops_mgd_prepare_tx(hw, vif, duration) \\r
+    ecrnx_ops_mgd_prepare_tx(hw, vif)\r
+#endif // 4.18\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)\r
+\r
+#define RX_ENC_HT(s) s->flag |= RX_FLAG_HT\r
+#define RX_ENC_HT_GF(s) s->flag |= (RX_FLAG_HT | RX_FLAG_HT_GF)\r
+#define RX_ENC_VHT(s) s->flag |= RX_FLAG_HT\r
+#define RX_ENC_HE(s) s->flag |= RX_FLAG_HT\r
+#define RX_ENC_FLAG_SHORT_GI(s) s->flag |= RX_FLAG_SHORT_GI\r
+#define RX_ENC_FLAG_SHORT_PRE(s) s->flag |= RX_FLAG_SHORTPRE\r
+#define RX_ENC_FLAG_LDPC(s) s->flag |= RX_FLAG_LDPC\r
+#define RX_BW_40MHZ(s) s->flag |= RX_FLAG_40MHZ\r
+#define RX_BW_80MHZ(s) s->vht_flag |= RX_VHT_FLAG_80MHZ\r
+#define RX_BW_160MHZ(s) s->vht_flag |= RX_VHT_FLAG_160MHZ\r
+#define RX_NSS(s) s->vht_nss\r
+\r
+#else\r
+#define RX_ENC_HT(s) s->encoding = RX_ENC_HT\r
+#define RX_ENC_HT_GF(s) { s->encoding = RX_ENC_HT;      \\r
+        s->enc_flags |= RX_ENC_FLAG_HT_GF; }\r
+#define RX_ENC_VHT(s) s->encoding = RX_ENC_VHT\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)\r
+#define RX_ENC_HE(s) s->encoding = RX_ENC_VHT\r
+#else\r
+#define RX_ENC_HE(s) s->encoding = RX_ENC_HE\r
+#endif\r
+#define RX_ENC_FLAG_SHORT_GI(s) s->enc_flags |= RX_ENC_FLAG_SHORT_GI\r
+#define RX_ENC_FLAG_SHORT_PRE(s) s->enc_flags |= RX_ENC_FLAG_SHORTPRE\r
+#define RX_ENC_FLAG_LDPC(s) s->enc_flags |= RX_ENC_FLAG_LDPC\r
+#define RX_BW_40MHZ(s) s->bw = RATE_INFO_BW_40\r
+#define RX_BW_80MHZ(s) s->bw = RATE_INFO_BW_80\r
+#define RX_BW_160MHZ(s) s->bw = RATE_INFO_BW_160\r
+#define RX_NSS(s) s->nss\r
+\r
+#endif // 4.12\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)\r
+#define ecrnx_ieee80211_cqm_rssi_notify(vif, event, level, gfp) \\r
+    ieee80211_cqm_rssi_notify(vif, event, gfp)\r
+#else\r
+#define ecrnx_ieee80211_cqm_rssi_notify(vif, event, level, gfp) \\r
+    ieee80211_cqm_rssi_notify(vif, event, level, gfp)    \r
+#endif // 4.11\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)\r
+#define RX_FLAG_MIC_STRIPPED 0\r
+#endif // 4.7\r
+\r
+#ifndef CONFIG_VENDOR_ECRNX_AMSDUS_TX\r
+#if  (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0))\r
+#define ecrnx_ops_ampdu_action(hw, vif, params) \\r
+    ecrnx_ops_ampdu_action(hw, vif, enum ieee80211_ampdu_mlme_action action, \\r
+                          struct ieee80211_sta *sta, u16 tid, u16 *ssn, u8 buf_size, \\r
+                          bool amsdu)\r
+#endif // 4.6\r
+#endif /* CONFIG_VENDOR_ECRNX_AMSDUS_TX */\r
+\r
+/******************************************************************************\r
+ * NET\r
+ *****************************************************************************/\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)\r
+#define rwnx_select_queue(dev, skb, sb_dev) \\r
+        rwnx_select_queue(dev, skb)\r
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)\r
+#define ecrnx_select_queue(dev, skb, sb_dev) \\r
+    ecrnx_select_queue(dev, skb, void *accel_priv, select_queue_fallback_t fallback)\r
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 0, 0)\r
+#define ecrnx_select_queue(dev, skb, sb_dev) \\r
+    ecrnx_select_queue(dev, skb, sb_dev, select_queue_fallback_t fallback)\r
+#endif //3.13\r
+\r
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) && !(defined CONFIG_VENDOR_ECRNX)\r
+#define sk_pacing_shift_update(sk, shift)\r
+#endif // 4.16\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)\r
+#define alloc_netdev_mqs(size, name, assign, setup, txqs, rxqs) \\r
+    alloc_netdev_mqs(size, name, setup, txqs, rxqs)\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)\r
+#define NET_NAME_UNKNOWN 0\r
+#endif\r
+\r
+/******************************************************************************\r
+ * TRACE\r
+ *****************************************************************************/\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)\r
+#define trace_print_symbols_seq ftrace_print_symbols_seq\r
+#endif // 4.2\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)\r
+#define trace_seq_buffer_ptr(p) p->buffer + p->len\r
+#endif\r
+\r
+/******************************************************************************\r
+ * TIME\r
+ *****************************************************************************/\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)\r
+#define time64_to_tm(t, o, tm) time_to_tm((time_t)t, o, tm)\r
+#endif // 4.8\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)\r
+#define ktime_get_real_seconds get_seconds\r
+#endif\r
+\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)\r
+typedef __s64 time64_t;\r
+#endif\r
+\r
+/******************************************************************************\r
+ * timer\r
+ *****************************************************************************/\r
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)\r
+#define from_timer(var, callback_timer, timer_fieldname) \\r
+       container_of(callback_timer, typeof(*var), timer_fieldname)\r
+\r
+#define timer_setup(timer, callback, flags) \\r
+    __setup_timer(timer, (void (*)(unsigned long))callback, (unsigned long)timer, flags)\r
+#endif // 4.14\r
+\r
+#endif /* _ECRNX_COMPAT_H_ */\r
diff --git a/drivers/net/wireless/eswin/ecrnx_debug.c b/drivers/net/wireless/eswin/ecrnx_debug.c
new file mode 100644 (file)
index 0000000..d3dcd61
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_debug.c
+ *
+ * @brief ecrnx driver debug functions;
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+#include <stdarg.h>
+#include <linux/init.h>
+#include "ecrnx_defs.h"
+#include "eswin_utils.h"
+
+#ifdef CONFIG_ECRNX_DBG_LEVEL
+int ecrnx_dbg_level = CONFIG_ECRNX_DBG_LEVEL; //defined in the 6600u_feature file
+#else
+int ecrnx_dbg_level = DRV_DBG_TYPE_NONE;
+#endif
+
+LOG_CTL_ST log_ctl={
+    .level = 2,
+    .dir = 0,
+};
+
+#ifndef CONFIG_ECRNX_DEBUGFS_CUSTOM
+int ecrnx_fw_log_level_set(u32 level, u32 dir)
+{
+    uint32_t dbg_info[3] = {0};
+
+    dbg_info[0] = 0x01; //SLAVE_LOG_LEVEL
+    dbg_info[1] = level;
+    dbg_info[2] = dir;
+
+    ECRNX_PRINT("%s: fstype:%d, level:%d, dir:%d \n", __func__, dbg_info[0], dbg_info[1], dbg_info[2]);
+    ECRNX_PRINT("info_len:%d \n", sizeof(dbg_info));
+    return host_send(dbg_info, sizeof(dbg_info), TX_FLAG_MSG_DEBUGFS_IE);
+}
+
+#endif
+
+// #endif
+
+
diff --git a/drivers/net/wireless/eswin/ecrnx_debug.h b/drivers/net/wireless/eswin/ecrnx_debug.h
new file mode 100644 (file)
index 0000000..b322487
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_debug.h
+ *
+ * @brief ecrnx driver debug structure declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef ECRNX_DEBUG_H_
+#define ECRNX_DEBUG_H_
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define FW_STR  "lmac"
+#elif defined CONFIG_ECRNX_FULLMAC
+#define FW_STR  "fmac"
+#endif
+#if 0
+#ifdef CONFIG_ECRNX_DBG
+/*  #define ECRNX_DBG(format, arg...) pr_warn(format, ## arg) */
+#define ECRNX_DBG printk
+#else
+#define ECRNX_DBG(a...) do {} while (0)
+#endif
+#endif
+
+#ifdef CONFIG_ECRNX_DBG
+
+#define ECRNX_FN_ENTRY_STR "%s() enter, line:%d\n", __func__, __LINE__
+#define DBG_PREFIX "[ecrnx] "
+#define DBG_PREFIX_IW_CFM "[ecrnx] iwpriv cfm:"
+#define DBG_PREFIX_PAT "[ecrnx] pattern error:"
+#define DBG_PREFIX_CRC_CHECK "[ecrnx] crc check:"
+#define DBG_PREFIX_SDIO_RX "[ecrnx] sdio rx:"
+#define DBG_PREFIX_SDIO_TX "[ecrnx] sdio tx:"
+
+/* driver log level*/
+enum ECRNX_DRV_DBG_TYEP{
+    DRV_DBG_TYPE_NONE,
+    DRV_DBG_TYPE_ALWAYS,
+    DRV_DBG_TYPE_ERR,
+    DRV_DBG_TYPE_WARNING,
+    DRV_DBG_TYPE_INFO,
+    DRV_DBG_TYPE_DEBUG,
+    DRV_DBG_TYPE_MAX,
+};
+
+#define ECRNX_PRINT(fmt, arg...)     \
+    do {\
+        if (DRV_DBG_TYPE_ALWAYS <= ecrnx_dbg_level) {\
+            printk(DBG_PREFIX fmt, ##arg);\
+        } \
+    } while (0)
+
+#define ECRNX_ERR(fmt, arg...)     \
+    do {\
+        if (DRV_DBG_TYPE_ERR <= ecrnx_dbg_level) {\
+            printk(DBG_PREFIX " ERROR " fmt, ##arg);\
+        } \
+    } while (0)
+
+#define ECRNX_WARN(fmt, arg...)     \
+    do {\
+        if (DRV_DBG_TYPE_WARNING <= ecrnx_dbg_level) {\
+            printk(DBG_PREFIX " WARN " fmt, ##arg);\
+        } \
+    } while (0)
+
+#define ECRNX_INFO(fmt, arg...)     \
+    do {\
+        if (DRV_DBG_TYPE_INFO <= ecrnx_dbg_level) {\
+            printk(DBG_PREFIX fmt, ##arg);\
+        } \
+    } while (0)
+
+#define ECRNX_DBG(fmt, arg...)     \
+    do {\
+        if (DRV_DBG_TYPE_DEBUG <= ecrnx_dbg_level) {\
+            printk(DBG_PREFIX fmt, ##arg);\
+        } \
+    } while (0)
+
+#else
+#define ECRNX_PRINT(...)
+#define ECRNX_ERR(...)
+#define ECRNX_WARN(...)
+#define ECRNX_INFO(...)
+#define ECRNX_DBG(...)
+#endif
+
+typedef struct {
+    u32 level;
+    u32 dir;
+} LOG_CTL_ST;
+
+extern int ecrnx_dbg_level;
+extern LOG_CTL_ST log_ctl;
+
+#ifndef CONFIG_ECRNX_DEBUGFS_CUSTOM
+int ecrnx_fw_log_level_set(u32 level, u32 dir);
+#endif
+
+#endif /* ECRNX_DEBUG_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_debugfs.c b/drivers/net/wireless/eswin/ecrnx_debugfs.c
new file mode 100644 (file)
index 0000000..a7b4232
--- /dev/null
@@ -0,0 +1,2576 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_debugfs.c
+ *
+ * @brief Definition of debugfs entries
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/kmod.h>
+#include <linux/debugfs.h>
+#include <linux/string.h>
+#include <linux/sort.h>
+
+#include "ecrnx_debugfs.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_radar.h"
+#include "ecrnx_tx.h"
+
+#define CONFIG_ECRNX_DBGFS_FW_TRACE 0
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+static ssize_t ecrnx_dbgfs_stats_read(struct file *file,
+                                     char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char *buf;
+    int per;
+    int ret;
+    int i, skipped;
+    ssize_t read;
+    int bufsz = (10 + NX_TX_PAYLOAD_MAX + NX_TXQ_CNT + IEEE80211_MAX_AMPDU_BUF) * 50;
+
+    buf = kmalloc(bufsz, GFP_ATOMIC);
+    if (buf == NULL)
+        return 0;
+
+    if (priv->stats.agg_done)
+        per = DIV_ROUND_UP((priv->stats.agg_retries + priv->stats.agg_died) *
+                           100, priv->stats.agg_done);
+    else
+        per = 0;
+
+    ret = scnprintf(buf, min_t(size_t, bufsz - 1, count),
+                    "agg_done         %10d\n"
+                    "agg_retries      %10d\n"
+                    "agg_retries_last %10d\n"
+                    "agg_died         %10d\n"
+                    "ampdu_all_ko     %10d\n"
+                    "agg_PER (%%)      %10d\n"
+                    "queues_stops     %10d\n\n",
+                    priv->stats.agg_done,
+                    priv->stats.agg_retries,
+                    priv->stats.agg_retries_last,
+                    priv->stats.agg_died,
+                    priv->stats.ampdu_all_ko,
+                    per,
+                    priv->stats.queues_stops);
+
+    ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+                     "TXQs CFM balances ");
+    for (i = 0; i < NX_TXQ_CNT; i++)
+        ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+                         "  [%1d]:%3d", i,
+                         priv->stats.cfm_balance[i]);
+
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+    ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+                     "\n\nAMSDU[len]             done   failed(%%)\n");
+    for (i = skipped = 0; i < NX_TX_PAYLOAD_MAX; i++) {
+        if (priv->stats.amsdus[i].done) {
+            per = DIV_ROUND_UP((priv->stats.amsdus[i].failed) *
+                               100, priv->stats.amsdus[i].done);
+        } else {
+            per = 0;
+            skipped = 1;
+            continue;
+        }
+        if (skipped) {
+            ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+                             "   * * *         %10d  %10d\n", 0, 0);
+            skipped = 0;
+        }
+
+        ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+                         "   [%1d]           %10d  %10d\n", i + 1,
+                         priv->stats.amsdus[i].done, per);
+    }
+    if (skipped)
+        ret += scnprintf(&buf[ret], min_t(size_t, bufsz - 1, count - ret),
+                         "   * * *         %10d  %10d\n", 0, 0);
+#endif
+
+    ret += scnprintf(&buf[ret], min_t(size_t, bufsz - ret - 1, count - ret),
+                     "\nIn-AMPDU     TX failures(%%)   RX counts\n");
+    for (i = skipped = 0; i < IEEE80211_MAX_AMPDU_BUF; i++) {
+        int failed;
+
+        if (priv->stats.in_ampdu[i].done) {
+            failed = DIV_ROUND_UP(priv->stats.in_ampdu[i].failed *
+                                  100, priv->stats.in_ampdu[i].done);
+        } else {
+            if (!priv->stats.rx_in_ampdu[i].cnt) {
+                skipped = 1;
+                continue;
+            }
+            failed = 0;
+        }
+        if (skipped) {
+            ret += scnprintf(&buf[ret],
+                             min_t(size_t, bufsz - ret - 1, count - ret),
+                             "   * * *         %10d  %10d\n", 0, 0);
+            skipped = 0;
+        }
+        ret += scnprintf(&buf[ret],
+                         min_t(size_t, bufsz - ret - 1, count - ret),
+                         "   mpdu#%2d       %10d  %10d\n", i, failed,
+                         priv->stats.rx_in_ampdu[i].cnt);
+
+    }
+    if (skipped)
+        ret += scnprintf(&buf[ret],
+                         min_t(size_t, bufsz - ret - 1, count - ret),
+                         "   * * *         %10d  %10d\n", 0, 0);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+    kfree(buf);
+
+    return read;
+}
+
+#else
+
+static ssize_t ecrnx_dbgfs_stats_read(struct file *file,
+                                     char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char *buf;
+    int ret;
+    int i, skipped;
+    ssize_t read;
+    int bufsz = (NX_TXQ_CNT) * 20 + (ARRAY_SIZE(priv->stats.amsdus_rx) + 1) * 40
+        + (ARRAY_SIZE(priv->stats.ampdus_tx) * 30);
+
+    if (*ppos)
+        return 0;
+
+    buf = kmalloc(bufsz, GFP_ATOMIC);
+    if (buf == NULL)
+        return 0;
+
+    ret = scnprintf(buf, bufsz, "TXQs CFM balances ");
+    for (i = 0; i < NX_TXQ_CNT; i++)
+        ret += scnprintf(&buf[ret], bufsz - ret,
+                         "  [%1d]:%3d", i,
+                         priv->stats.cfm_balance[i]);
+
+    ret += scnprintf(&buf[ret], bufsz - ret, "\n");
+
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+    int per = 0;
+    ret += scnprintf(&buf[ret], bufsz - ret,
+                     "\nAMSDU[len]       done         failed   received\n");
+    for (i = skipped = 0; i < NX_TX_PAYLOAD_MAX; i++) {
+        if (priv->stats.amsdus[i].done) {
+            per = DIV_ROUND_UP((priv->stats.amsdus[i].failed) *
+                               100, priv->stats.amsdus[i].done);
+        } else if (priv->stats.amsdus_rx[i]) {
+            per = 0;
+        } else {
+            per = 0;
+            skipped = 1;
+            continue;
+        }
+        if (skipped) {
+            ret += scnprintf(&buf[ret], bufsz - ret, "   ...\n");
+            skipped = 0;
+        }
+
+        ret += scnprintf(&buf[ret], bufsz - ret,
+                         "   [%2d]    %10d %8d(%3d%%) %10d\n",  i ? i + 1 : i,
+                         priv->stats.amsdus[i].done,
+                         priv->stats.amsdus[i].failed, per,
+                         priv->stats.amsdus_rx[i]);
+    }
+
+    for (; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
+        if (!priv->stats.amsdus_rx[i]) {
+            skipped = 1;
+            continue;
+        }
+        if (skipped) {
+            ret += scnprintf(&buf[ret], bufsz - ret, "   ...\n");
+            skipped = 0;
+        }
+
+        ret += scnprintf(&buf[ret], bufsz - ret,
+                         "   [%2d]                              %10d\n",
+                         i + 1, priv->stats.amsdus_rx[i]);
+    }
+#else
+    ret += scnprintf(&buf[ret], bufsz - ret,
+                     "\nAMSDU[len]   received\n");
+    for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.amsdus_rx); i++) {
+        if (!priv->stats.amsdus_rx[i]) {
+            skipped = 1;
+            continue;
+        }
+        if (skipped) {
+            ret += scnprintf(&buf[ret], bufsz - ret,
+                             "   ...\n");
+            skipped = 0;
+        }
+
+        ret += scnprintf(&buf[ret], bufsz - ret,
+                         "   [%2d]    %10d\n",
+                         i + 1, priv->stats.amsdus_rx[i]);
+    }
+
+#endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
+
+    ret += scnprintf(&buf[ret], bufsz - ret,
+                     "\nAMPDU[len]     done  received\n");
+    for (i = skipped = 0; i < ARRAY_SIZE(priv->stats.ampdus_tx); i++) {
+        if (!priv->stats.ampdus_tx[i] && !priv->stats.ampdus_rx[i]) {
+            skipped = 1;
+            continue;
+        }
+        if (skipped) {
+            ret += scnprintf(&buf[ret], bufsz - ret,
+                             "    ...\n");
+            skipped = 0;
+        }
+
+        ret += scnprintf(&buf[ret], bufsz - ret,
+                         "   [%2d]   %9d %9d\n", i ? i + 1 : i,
+                         priv->stats.ampdus_tx[i], priv->stats.ampdus_rx[i]);
+    }
+
+    ret += scnprintf(&buf[ret], bufsz - ret,
+                     "#mpdu missed        %9d\n",
+                     priv->stats.ampdus_rx_miss);
+
+    ret += scnprintf(&buf[ret], bufsz - ret,
+                     "\nmsg_tx:%d,%d; data_tx:%d,%d\n",
+                     priv->msg_tx, priv->msg_tx_done, priv->data_tx, priv->data_tx_done);
+    ret += scnprintf(&buf[ret], bufsz - ret,
+                     "usb_rx:%d, data_rx:%d, msg_rx:%d\n",
+                     priv->usb_rx, priv->data_rx, priv->msg_rx);
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+    kfree(buf);
+
+    return read;
+}
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+static ssize_t ecrnx_dbgfs_stats_write(struct file *file,
+                                      const char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+
+    /* Prevent from interrupt preemption as these statistics are updated under
+     * interrupt */
+    spin_lock_bh(&priv->tx_lock);
+
+    memset(&priv->stats, 0, sizeof(priv->stats));
+
+    spin_unlock_bh(&priv->tx_lock);
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(stats);
+
+#define TXQ_STA_PREF "tid|"
+#define TXQ_STA_PREF_FMT "%3d|"
+
+#ifdef CONFIG_ECRNX_FULLMAC
+#define TXQ_VIF_PREF "type|"
+#define TXQ_VIF_PREF_FMT "%4s|"
+#else
+#define TXQ_VIF_PREF "AC|"
+#define TXQ_VIF_PREF_FMT "%2s|"
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#define TXQ_HDR "idx|  status|credit|ready|retry|pushed"
+#define TXQ_HDR_FMT "%3d|%s%s%s%s%s%s%s%s|%6d|%5d|%5d|%6d"
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+#ifdef CONFIG_ECRNX_FULLMAC
+#define TXQ_HDR_SUFF "|amsdu"
+#define TXQ_HDR_SUFF_FMT "|%5d"
+#else
+#define TXQ_HDR_SUFF "|amsdu-ht|amdsu-vht"
+#define TXQ_HDR_SUFF_FMT "|%8d|%9d"
+#endif /* CONFIG_ECRNX_FULLMAC */
+#else
+#define TXQ_HDR_SUFF ""
+#define TXQ_HDR_SUF_FMT ""
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+#define TXQ_HDR_MAX_LEN (sizeof(TXQ_STA_PREF) + sizeof(TXQ_HDR) + sizeof(TXQ_HDR_SUFF) + 1)
+
+#ifdef CONFIG_ECRNX_FULLMAC
+#define PS_HDR  "Legacy PS: ready=%d, sp=%d / UAPSD: ready=%d, sp=%d"
+#define PS_HDR_LEGACY "Legacy PS: ready=%d, sp=%d"
+#define PS_HDR_UAPSD  "UAPSD: ready=%d, sp=%d"
+#define PS_HDR_MAX_LEN  sizeof("Legacy PS: ready=xxx, sp=xxx / UAPSD: ready=xxx, sp=xxx\n")
+#else
+#define PS_HDR ""
+#define PS_HDR_MAX_LEN 0
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#define STA_HDR "** STA %d (%pM)\n"
+#define STA_HDR_MAX_LEN sizeof("- STA xx (xx:xx:xx:xx:xx:xx)\n") + PS_HDR_MAX_LEN
+
+#ifdef CONFIG_ECRNX_FULLMAC
+#define VIF_HDR "* VIF [%d] %s\n"
+#define VIF_HDR_MAX_LEN sizeof(VIF_HDR) + IFNAMSIZ
+#else
+#define VIF_HDR "* VIF [%d]\n"
+#define VIF_HDR_MAX_LEN sizeof(VIF_HDR)
+#endif
+
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+
+#ifdef CONFIG_ECRNX_FULLMAC
+#define VIF_SEP "---------------------------------------\n"
+#else
+#define VIF_SEP "----------------------------------------------------\n"
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#else /* ! CONFIG_ECRNX_AMSDUS_TX */
+#define VIF_SEP "---------------------------------\n"
+#endif /* CONFIG_ECRNX_AMSDUS_TX*/
+
+#define VIF_SEP_LEN sizeof(VIF_SEP)
+
+#define CAPTION "status: L=in hwq list, F=stop full, P=stop sta PS, V=stop vif PS,\
+ C=stop channel, S=stop CSA, M=stop MU, N=Ndev queue stopped"
+#define CAPTION_LEN sizeof(CAPTION)
+
+#define STA_TXQ 0
+#define VIF_TXQ 1
+
+static int ecrnx_dbgfs_txq(char *buf, size_t size, struct ecrnx_txq *txq, int type, int tid, char *name)
+{
+    int res, idx = 0;
+    int i, pushed = 0;
+
+    if (type == STA_TXQ) {
+        res = scnprintf(&buf[idx], size, TXQ_STA_PREF_FMT, tid);
+        idx += res;
+        size -= res;
+    } else {
+        res = scnprintf(&buf[idx], size, TXQ_VIF_PREF_FMT, name);
+        idx += res;
+        size -= res;
+    }
+
+    for (i = 0; i < CONFIG_USER_MAX; i++) {
+        pushed += txq->pkt_pushed[i];
+    }
+
+    res = scnprintf(&buf[idx], size, TXQ_HDR_FMT, txq->idx,
+                    (txq->status & ECRNX_TXQ_IN_HWQ_LIST) ? "L" : " ",
+                    (txq->status & ECRNX_TXQ_STOP_FULL) ? "F" : " ",
+                    (txq->status & ECRNX_TXQ_STOP_STA_PS) ? "P" : " ",
+                    (txq->status & ECRNX_TXQ_STOP_VIF_PS) ? "V" : " ",
+                    (txq->status & ECRNX_TXQ_STOP_CHAN) ? "C" : " ",
+                    (txq->status & ECRNX_TXQ_STOP_CSA) ? "S" : " ",
+                    (txq->status & ECRNX_TXQ_STOP_MU_POS) ? "M" : " ",
+                    (txq->status & ECRNX_TXQ_NDEV_FLOW_CTRL) ? "N" : " ",
+                    txq->credits, skb_queue_len(&txq->sk_list),
+                    txq->nb_retry, pushed);
+    idx += res;
+    size -= res;
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    if (type == STA_TXQ) {
+        res = scnprintf(&buf[idx], size, TXQ_HDR_SUFF_FMT,
+#ifdef CONFIG_ECRNX_FULLMAC
+                        txq->amsdu_len
+#else
+                        txq->amsdu_ht_len_cap, txq->amsdu_vht_len_cap
+#endif /* CONFIG_ECRNX_FULLMAC */
+                        );
+        idx += res;
+        size -= res;
+    }
+#endif
+
+    res = scnprintf(&buf[idx], size, "\n");
+    idx += res;
+    size -= res;
+
+    return idx;
+}
+
+static int ecrnx_dbgfs_txq_sta(char *buf, size_t size, struct ecrnx_sta *ecrnx_sta,
+                              struct ecrnx_hw *ecrnx_hw)
+{
+    int tid, res, idx = 0;
+    struct ecrnx_txq *txq;
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ieee80211_sta *sta = ecrnx_to_ieee80211_sta(ecrnx_sta);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    res = scnprintf(&buf[idx], size, "\n" STA_HDR,
+                    ecrnx_sta->sta_idx,
+#ifdef CONFIG_ECRNX_SOFTMAC
+                    sta->addr
+#else
+                    ecrnx_sta->mac_addr
+#endif /* CONFIG_ECRNX_SOFTMAC */
+                    );
+    idx += res;
+    size -= res;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    if (ecrnx_sta->ps.active) {
+        if (ecrnx_sta->uapsd_tids &&
+            (ecrnx_sta->uapsd_tids == ((1 << NX_NB_TXQ_PER_STA) - 1)))
+            res = scnprintf(&buf[idx], size, PS_HDR_UAPSD "\n",
+                            ecrnx_sta->ps.pkt_ready[UAPSD_ID],
+                            ecrnx_sta->ps.sp_cnt[UAPSD_ID]);
+        else if (ecrnx_sta->uapsd_tids)
+            res = scnprintf(&buf[idx], size, PS_HDR "\n",
+                            ecrnx_sta->ps.pkt_ready[LEGACY_PS_ID],
+                            ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID],
+                            ecrnx_sta->ps.pkt_ready[UAPSD_ID],
+                            ecrnx_sta->ps.sp_cnt[UAPSD_ID]);
+        else
+            res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
+                            ecrnx_sta->ps.pkt_ready[LEGACY_PS_ID],
+                            ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID]);
+        idx += res;
+        size -= res;
+    } else {
+        res = scnprintf(&buf[idx], size, "\n");
+        idx += res;
+        size -= res;
+    }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+
+    res = scnprintf(&buf[idx], size, TXQ_STA_PREF TXQ_HDR TXQ_HDR_SUFF "\n");
+    idx += res;
+    size -= res;
+
+
+    foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+        res = ecrnx_dbgfs_txq(&buf[idx], size, txq, STA_TXQ, tid, NULL);
+        idx += res;
+        size -= res;
+    }
+
+    return idx;
+}
+
+static int ecrnx_dbgfs_txq_vif(char *buf, size_t size, struct ecrnx_vif *ecrnx_vif,
+                              struct ecrnx_hw *ecrnx_hw)
+{
+    int res, idx = 0;
+    struct ecrnx_txq *txq;
+    struct ecrnx_sta *ecrnx_sta;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    res = scnprintf(&buf[idx], size, VIF_HDR, ecrnx_vif->vif_index, ecrnx_vif->ndev->name);
+    idx += res;
+    size -= res;
+    if (!ecrnx_vif->up || ecrnx_vif->ndev == NULL)
+        return idx;
+
+#else
+    int ac;
+    char ac_name[2] = {'0', '\0'};
+
+    res = scnprintf(&buf[idx], size, VIF_HDR, ecrnx_vif->vif_index);
+    idx += res;
+    size -= res;
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    if (ECRNX_VIF_TYPE(ecrnx_vif) ==  NL80211_IFTYPE_AP ||
+        ECRNX_VIF_TYPE(ecrnx_vif) ==  NL80211_IFTYPE_P2P_GO ||
+        ECRNX_VIF_TYPE(ecrnx_vif) ==  NL80211_IFTYPE_MESH_POINT) {
+        res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
+        idx += res;
+        size -= res;
+        txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+        res = ecrnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "UNK");
+        idx += res;
+        size -= res;
+        txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+        res = ecrnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, "BCMC");
+        idx += res;
+        size -= res;
+        ecrnx_sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+        if (ecrnx_sta->ps.active) {
+            res = scnprintf(&buf[idx], size, PS_HDR_LEGACY "\n",
+                            ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID],
+                            ecrnx_sta->ps.sp_cnt[LEGACY_PS_ID]);
+            idx += res;
+            size -= res;
+        } else {
+            res = scnprintf(&buf[idx], size, "\n");
+            idx += res;
+            size -= res;
+        }
+
+        list_for_each_entry(ecrnx_sta, &ecrnx_vif->ap.sta_list, list) {
+            res = ecrnx_dbgfs_txq_sta(&buf[idx], size, ecrnx_sta, ecrnx_hw);
+            idx += res;
+            size -= res;
+        }
+    } else if (ECRNX_VIF_TYPE(ecrnx_vif) ==  NL80211_IFTYPE_STATION ||
+               ECRNX_VIF_TYPE(ecrnx_vif) ==  NL80211_IFTYPE_P2P_CLIENT) {
+        if (ecrnx_vif->sta.ap) {
+            res = ecrnx_dbgfs_txq_sta(&buf[idx], size, ecrnx_vif->sta.ap, ecrnx_hw);
+            idx += res;
+            size -= res;
+        }
+    }
+
+#else
+    res = scnprintf(&buf[idx], size, TXQ_VIF_PREF TXQ_HDR "\n");
+    idx += res;
+    size -= res;
+
+    foreach_vif_txq(ecrnx_vif, txq, ac) {
+        ac_name[0]++;
+        res = ecrnx_dbgfs_txq(&buf[idx], size, txq, VIF_TXQ, 0, ac_name);
+        idx += res;
+        size -= res;
+    }
+
+    list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
+        res = ecrnx_dbgfs_txq_sta(&buf[idx], size, ecrnx_sta, ecrnx_hw);
+        idx += res;
+        size -= res;
+    }
+#endif /* CONFIG_ECRNX_FULLMAC */
+    return idx;
+}
+
+static ssize_t ecrnx_dbgfs_txq_read(struct file *file ,
+                                   char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *ecrnx_hw = file->private_data;
+    struct ecrnx_vif *vif;
+    char *buf;
+    int idx, res;
+    ssize_t read;
+    size_t bufsz = ((NX_VIRT_DEV_MAX * (VIF_HDR_MAX_LEN + 2 * VIF_SEP_LEN)) +
+                    (NX_REMOTE_STA_MAX * STA_HDR_MAX_LEN) +
+                    ((NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX + NX_NB_TXQ) *
+                     TXQ_HDR_MAX_LEN) + CAPTION_LEN);
+
+    /* everything is read in one go */
+    if (*ppos)
+        return 0;
+
+    bufsz = min_t(size_t, bufsz, count);
+    buf = kmalloc(bufsz, GFP_ATOMIC);
+    if (buf == NULL)
+        return 0;
+
+    bufsz--;
+    idx = 0;
+
+    res = scnprintf(&buf[idx], bufsz, CAPTION);
+    idx += res;
+    bufsz -= res;
+
+    //spin_lock_bh(&ecrnx_hw->tx_lock);
+    list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+        res = scnprintf(&buf[idx], bufsz, "\n"VIF_SEP);
+        idx += res;
+        bufsz -= res;
+        res = ecrnx_dbgfs_txq_vif(&buf[idx], bufsz, vif, ecrnx_hw);
+        idx += res;
+        bufsz -= res;
+        res = scnprintf(&buf[idx], bufsz, VIF_SEP);
+        idx += res;
+        bufsz -= res;
+    }
+    //spin_unlock_bh(&ecrnx_hw->tx_lock);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
+    kfree(buf);
+
+    return read;
+}
+DEBUGFS_READ_FILE_OPS(txq);
+
+static ssize_t ecrnx_dbgfs_acsinfo_read(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    #ifdef CONFIG_ECRNX_SOFTMAC
+    struct wiphy *wiphy = priv->hw->wiphy;
+    #else //CONFIG_ECRNX_SOFTMAC
+    struct wiphy *wiphy = priv->wiphy;
+    #endif //CONFIG_ECRNX_SOFTMAC
+    ssize_t read;
+    char *buf = kmalloc((SCAN_CHANNEL_MAX + 1) * 43, GFP_ATOMIC);
+
+    //char buf[(SCAN_CHANNEL_MAX + 1) * 43];
+    int survey_cnt = 0;
+    int len = 0;
+    int band, chan_cnt;
+
+    if(!buf){
+        return 0;
+    }
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+
+    len += scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                     "FREQ    TIME(ms)    BUSY(ms)    NOISE(dBm)\n");
+
+#ifdef CONFIG_ECRNX_5G
+    for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_5GHZ; band++) {
+#else
+       for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_2GHZ; band++) {
+#endif
+        for (chan_cnt = 0; chan_cnt < wiphy->bands[band]->n_channels; chan_cnt++) {
+            struct ecrnx_survey_info *p_survey_info = &priv->survey[survey_cnt];
+            struct ieee80211_channel *p_chan = &wiphy->bands[band]->channels[chan_cnt];
+
+            if (p_survey_info->filled) {
+                len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - len - 1, count),
+                                 "%d    %03d         %03d         %d\n",
+                                 p_chan->center_freq,
+                                 p_survey_info->chan_time_ms,
+                                 p_survey_info->chan_time_busy_ms,
+                                 p_survey_info->noise_dbm);
+            } else {
+                len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) -len -1, count),
+                                 "%d    NOT AVAILABLE\n",
+                                 p_chan->center_freq);
+            }
+
+            survey_cnt++;
+        }
+    }
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+    kfree(buf);
+
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(acsinfo);
+
+static ssize_t ecrnx_dbgfs_fw_dbg_read(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+    char help[]="usage: [MOD:<ALL|KE|DBG|IPC|DMA|MM|TX|RX|PHY>]* "
+        "[DBG:<NONE|CRT|ERR|WRN|INF|VRB>]\n";
+
+    return simple_read_from_buffer(user_buf, count, ppos, help, sizeof(help));
+}
+
+
+static ssize_t ecrnx_dbgfs_fw_dbg_write(struct file *file,
+                                            const char __user *user_buf,
+                                            size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int idx = 0;
+    u32 mod = 0;
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+    if (copy_from_user(buf, user_buf, len))
+        return -EFAULT;
+    buf[len] = '\0';
+
+#define ECRNX_MOD_TOKEN(str, val)                                        \
+    if (strncmp(&buf[idx], str, sizeof(str) - 1 ) == 0) {               \
+        idx += sizeof(str) - 1;                                         \
+        mod |= val;                                                     \
+        continue;                                                       \
+    }
+
+#define ECRNX_DBG_TOKEN(str, val)                                \
+    if (strncmp(&buf[idx], str, sizeof(str) - 1) == 0) {        \
+        idx += sizeof(str) - 1;                                 \
+        dbg = val;                                              \
+        goto dbg_done;                                          \
+    }
+
+    while ((idx + 4) < len) {
+        if (strncmp(&buf[idx], "MOD:", 4) == 0) {
+            idx += 4;
+            ECRNX_MOD_TOKEN("ALL", 0xffffffff);
+            ECRNX_MOD_TOKEN("KE",  BIT(0));
+            ECRNX_MOD_TOKEN("DBG", BIT(1));
+            ECRNX_MOD_TOKEN("IPC", BIT(2));
+            ECRNX_MOD_TOKEN("DMA", BIT(3));
+            ECRNX_MOD_TOKEN("MM",  BIT(4));
+            ECRNX_MOD_TOKEN("TX",  BIT(5));
+            ECRNX_MOD_TOKEN("RX",  BIT(6));
+            ECRNX_MOD_TOKEN("PHY", BIT(7));
+            idx++;
+        } else if (strncmp(&buf[idx], "DBG:", 4) == 0) {
+            u32 dbg = 0;
+            idx += 4;
+            ECRNX_DBG_TOKEN("NONE", 0);
+            ECRNX_DBG_TOKEN("CRT",  1);
+            ECRNX_DBG_TOKEN("ERR",  2);
+            ECRNX_DBG_TOKEN("WRN",  3);
+            ECRNX_DBG_TOKEN("INF",  4);
+            ECRNX_DBG_TOKEN("VRB",  5);
+            idx++;
+            continue;
+          dbg_done:
+            ecrnx_send_dbg_set_sev_filter_req(priv, dbg);
+        } else {
+            idx++;
+        }
+    }
+
+    if (mod) {
+        ecrnx_send_dbg_set_mod_filter_req(priv, mod);
+    }
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg);
+
+static ssize_t ecrnx_dbgfs_sys_stats_read(struct file *file,
+                                         char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[3*64];
+    int len = 0;
+    ssize_t read;
+    int error = 0;
+    struct dbg_get_sys_stat_cfm cfm;
+    u32 sleep_int, sleep_frac, doze_int, doze_frac;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Get the information from the FW */
+    if ((error = ecrnx_send_dbg_get_sys_stat_req(priv, &cfm)))
+        return error;
+
+    if (cfm.stats_time == 0)
+        return 0;
+
+    sleep_int = ((cfm.cpu_sleep_time * 100) / cfm.stats_time);
+    sleep_frac = (((cfm.cpu_sleep_time * 100) % cfm.stats_time) * 10) / cfm.stats_time;
+    doze_int = ((cfm.doze_time * 100) / cfm.stats_time);
+    doze_frac = (((cfm.doze_time * 100) % cfm.stats_time) * 10) / cfm.stats_time;
+
+    len += scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                     "\nSystem statistics:\n");
+    len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - 1, count),
+                     "  CPU sleep [%%]: %d.%d\n", sleep_int, sleep_frac);
+    len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - 1, count),
+                     "  Doze      [%%]: %d.%d\n", doze_int, doze_frac);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(sys_stats);
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+static ssize_t ecrnx_dbgfs_mu_group_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *ecrnx_hw = file->private_data;
+    struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+    struct ecrnx_mu_group *group;
+    size_t bufsz = NX_MU_GROUP_MAX * sizeof("xx = (xx - xx - xx - xx)\n") + 50;
+    char *buf;
+    int j, res, idx = 0;
+
+    if (*ppos)
+        return 0;
+
+    buf = kmalloc(bufsz, GFP_ATOMIC);
+    if (buf == NULL)
+        return 0;
+
+    res = scnprintf(&buf[idx], bufsz, "MU Group list (%d groups, %d users max)\n",
+                    NX_MU_GROUP_MAX, CONFIG_USER_MAX);
+    idx += res;
+    bufsz -= res;
+
+    list_for_each_entry(group, &mu->active_groups, list) {
+        if (group->user_cnt) {
+            res = scnprintf(&buf[idx], bufsz, "%2d = (", group->group_id);
+            idx += res;
+            bufsz -= res;
+            for (j = 0; j < (CONFIG_USER_MAX - 1) ; j++) {
+                if (group->users[j])
+                    res = scnprintf(&buf[idx], bufsz, "%2d - ",
+                                    group->users[j]->sta_idx);
+                else
+                    res = scnprintf(&buf[idx], bufsz, ".. - ");
+
+                idx += res;
+                bufsz -= res;
+            }
+
+            if (group->users[j])
+                res = scnprintf(&buf[idx], bufsz, "%2d)\n",
+                                group->users[j]->sta_idx);
+            else
+                res = scnprintf(&buf[idx], bufsz, "..)\n");
+
+            idx += res;
+            bufsz -= res;
+        }
+    }
+
+    res = simple_read_from_buffer(user_buf, count, ppos, buf, idx);
+    kfree(buf);
+
+    return res;
+}
+
+DEBUGFS_READ_FILE_OPS(mu_group);
+#endif
+
+#ifdef CONFIG_ECRNX_P2P_DEBUGFS
+static ssize_t ecrnx_dbgfs_oppps_write(struct file *file,
+                                      const char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *rw_hw = file->private_data;
+    struct ecrnx_vif *rw_vif;
+    char buf[32];
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+    int ctw;
+
+    if (copy_from_user(buf, user_buf, len))
+        return -EFAULT;
+    buf[len] = '\0';
+
+    /* Read the written CT Window (provided in ms) value */
+    if (sscanf(buf, "ctw=%d", &ctw) > 0) {
+        /* Check if at least one VIF is configured as P2P GO */
+        list_for_each_entry(rw_vif, &rw_hw->vifs, list) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+            if ((ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_AP) && rw_vif->vif->p2p) {
+#else /* CONFIG_ECRNX_FULLMAC */
+            if (ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_P2P_GO) {
+#endif /* CONFIG_ECRNX_SOFTMAC */
+                struct mm_set_p2p_oppps_cfm cfm;
+
+                /* Forward request to the embedded and wait for confirmation */
+                ecrnx_send_p2p_oppps_req(rw_hw, rw_vif, (u8)ctw, &cfm);
+
+                break;
+            }
+        }
+    }
+
+    return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(oppps);
+
+static ssize_t ecrnx_dbgfs_noa_write(struct file *file,
+                                    const char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *rw_hw = file->private_data;
+    struct ecrnx_vif *rw_vif;
+    char buf[64];
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+    int noa_count, interval, duration, dyn_noa;
+
+    if (copy_from_user(buf, user_buf, len))
+        return -EFAULT;
+    buf[len] = '\0';
+
+    /* Read the written NOA information */
+    if (sscanf(buf, "count=%d interval=%d duration=%d dyn=%d",
+               &noa_count, &interval, &duration, &dyn_noa) > 0) {
+        /* Check if at least one VIF is configured as P2P GO */
+        list_for_each_entry(rw_vif, &rw_hw->vifs, list) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+            if ((ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_AP) && rw_vif->vif->p2p) {
+#else /* CONFIG_ECRNX_FULLMAC */
+            if (ECRNX_VIF_TYPE(rw_vif) == NL80211_IFTYPE_P2P_GO) {
+#endif /* CONFIG_ECRNX_SOFTMAC */
+                struct mm_set_p2p_noa_cfm cfm;
+
+                /* Forward request to the embedded and wait for confirmation */
+                ecrnx_send_p2p_noa_req(rw_hw, rw_vif, noa_count, interval,
+                                      duration, (dyn_noa > 0),  &cfm);
+
+                break;
+            }
+        }
+    }
+
+    return count;
+}
+
+DEBUGFS_WRITE_FILE_OPS(noa);
+#endif /* CONFIG_ECRNX_P2P_DEBUGFS */
+
+struct ecrnx_dbgfs_fw_trace {
+    struct ecrnx_fw_trace_local_buf lbuf;
+    struct ecrnx_fw_trace *trace;
+    struct ecrnx_hw *ecrnx_hw;
+};
+
+static int ecrnx_dbgfs_fw_trace_open(struct inode *inode, struct file *file)
+{
+    struct ecrnx_dbgfs_fw_trace *ltrace = kmalloc(sizeof(*ltrace), GFP_KERNEL);
+    struct ecrnx_hw *priv = inode->i_private;
+
+    if (!ltrace)
+        return -ENOMEM;
+
+    if (ecrnx_fw_trace_alloc_local(&ltrace->lbuf, 5120)) {
+        kfree(ltrace);
+    }
+
+    ltrace->trace = &priv->debugfs.fw_trace;
+    ltrace->ecrnx_hw = priv;
+    file->private_data = ltrace;
+    return 0;
+}
+
+static int ecrnx_dbgfs_fw_trace_release(struct inode *inode, struct file *file)
+{
+    struct ecrnx_dbgfs_fw_trace *ltrace = file->private_data;
+
+    if (ltrace) {
+        ecrnx_fw_trace_free_local(&ltrace->lbuf);
+        kfree(ltrace);
+    }
+
+    return 0;
+}
+
+static ssize_t ecrnx_dbgfs_fw_trace_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_dbgfs_fw_trace *ltrace = file->private_data;
+    bool dont_wait = ((file->f_flags & O_NONBLOCK) ||
+                      ltrace->ecrnx_hw->debugfs.unregistering);
+
+    return ecrnx_fw_trace_read(ltrace->trace, &ltrace->lbuf,
+                              dont_wait, user_buf, count);
+}
+
+static ssize_t ecrnx_dbgfs_fw_trace_write(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+    struct ecrnx_dbgfs_fw_trace *ltrace = file->private_data;
+    int ret;
+
+    ret = _ecrnx_fw_trace_reset(ltrace->trace, true);
+    if (ret)
+        return ret;
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_OPEN_RELEASE_FILE_OPS(fw_trace);
+
+static ssize_t ecrnx_dbgfs_fw_trace_level_read(struct file *file,
+                                              char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    return ecrnx_fw_trace_level_read(&priv->debugfs.fw_trace, user_buf,
+                                    count, ppos);
+}
+
+static ssize_t ecrnx_dbgfs_fw_trace_level_write(struct file *file,
+                                               const char __user *user_buf,
+                                               size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    return ecrnx_fw_trace_level_write(&priv->debugfs.fw_trace, user_buf, count);
+}
+DEBUGFS_READ_WRITE_FILE_OPS(fw_trace_level);
+
+
+#ifdef CONFIG_ECRNX_RADAR
+static ssize_t ecrnx_dbgfs_pulses_read(struct file *file,
+                                      char __user *user_buf,
+                                      size_t count, loff_t *ppos,
+                                      int rd_idx)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char *buf;
+    int len = 0;
+    int bufsz;
+    int i;
+    int index;
+    struct ecrnx_radar_pulses *p = &priv->radar.pulses[rd_idx];
+    ssize_t read;
+
+    if (*ppos != 0)
+        return 0;
+
+    /* Prevent from interrupt preemption */
+    spin_lock_bh(&priv->radar.lock);
+    bufsz = p->count * 34 + 51;
+    bufsz += ecrnx_radar_dump_pattern_detector(NULL, 0, &priv->radar, rd_idx);
+    buf = kmalloc(bufsz, GFP_ATOMIC);
+    if (buf == NULL) {
+        spin_unlock_bh(&priv->radar.lock);
+        return 0;
+    }
+
+    if (p->count) {
+        len += scnprintf(&buf[len], bufsz - len,
+                         " PRI     WIDTH     FOM     FREQ\n");
+        index = p->index;
+        for (i = 0; i < p->count; i++) {
+            struct radar_pulse *pulse;
+
+            if (index > 0)
+                index--;
+            else
+                index = ECRNX_RADAR_PULSE_MAX - 1;
+
+            pulse = (struct radar_pulse *) &p->buffer[index];
+
+            len += scnprintf(&buf[len], bufsz - len,
+                             "%05dus  %03dus     %2d%%    %+3dMHz\n", pulse->rep,
+                             2 * pulse->len, 6 * pulse->fom, 2*pulse->freq);
+        }
+    }
+
+    len += ecrnx_radar_dump_pattern_detector(&buf[len], bufsz - len,
+                                            &priv->radar, rd_idx);
+
+    spin_unlock_bh(&priv->radar.lock);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+    kfree(buf);
+
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_pulses_prim_read(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+    return ecrnx_dbgfs_pulses_read(file, user_buf, count, ppos, 0);
+}
+
+DEBUGFS_READ_FILE_OPS(pulses_prim);
+
+static ssize_t ecrnx_dbgfs_pulses_sec_read(struct file *file,
+                                          char __user *user_buf,
+                                          size_t count, loff_t *ppos)
+{
+    return ecrnx_dbgfs_pulses_read(file, user_buf, count, ppos, 1);
+}
+
+DEBUGFS_READ_FILE_OPS(pulses_sec);
+
+static ssize_t ecrnx_dbgfs_detected_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char *buf;
+    int bufsz,len = 0;
+    ssize_t read;
+
+    if (*ppos != 0)
+        return 0;
+
+    bufsz = 5; // RIU:\n
+    bufsz += ecrnx_radar_dump_radar_detected(NULL, 0, &priv->radar,
+                                            ECRNX_RADAR_RIU);
+
+    if (priv->phy.cnt > 1) {
+        bufsz += 5; // FCU:\n
+        bufsz += ecrnx_radar_dump_radar_detected(NULL, 0, &priv->radar,
+                                                ECRNX_RADAR_FCU);
+    }
+
+    buf = kmalloc(bufsz, GFP_KERNEL);
+    if (buf == NULL) {
+        return 0;
+    }
+
+    len = scnprintf(&buf[len], bufsz, "RIU:\n");
+    len += ecrnx_radar_dump_radar_detected(&buf[len], bufsz - len, &priv->radar,
+                                            ECRNX_RADAR_RIU);
+
+    if (priv->phy.cnt > 1) {
+        len += scnprintf(&buf[len], bufsz - len, "FCU:\n");
+        len += ecrnx_radar_dump_radar_detected(&buf[len], bufsz - len,
+                                              &priv->radar, ECRNX_RADAR_FCU);
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+    kfree(buf);
+
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(detected);
+
+static ssize_t ecrnx_dbgfs_enable_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int ret;
+    ssize_t read;
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "RIU=%d FCU=%d\n", priv->radar.dpd[ECRNX_RADAR_RIU]->enabled,
+                    priv->radar.dpd[ECRNX_RADAR_FCU]->enabled);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_enable_write(struct file *file,
+                                     const char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int val;
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+    if (copy_from_user(buf, user_buf, len))
+        return -EFAULT;
+
+    buf[len] = '\0';
+
+    if (sscanf(buf, "RIU=%d", &val) > 0)
+        ecrnx_radar_detection_enable(&priv->radar, val, ECRNX_RADAR_RIU);
+
+    if (sscanf(buf, "FCU=%d", &val) > 0)
+        ecrnx_radar_detection_enable(&priv->radar, val, ECRNX_RADAR_FCU);
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(enable);
+
+static ssize_t ecrnx_dbgfs_band_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int ret;
+    ssize_t read;
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "BAND=%d\n", priv->phy.sec_chan.band);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_band_write(struct file *file,
+                                     const char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int val;
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+    if (copy_from_user(buf, user_buf, len))
+        return -EFAULT;
+
+    buf[len] = '\0';
+
+#ifdef CONFIG_ECRNX_5G
+    if ((sscanf(buf, "%d", &val) > 0) && (val >= 0) && (val <= NL80211_BAND_5GHZ))
+#else
+       if ((sscanf(buf, "%d", &val) > 0) && (val >= 0) && (val <= NL80211_BAND_2GHZ))
+#endif
+        priv->phy.sec_chan.band = val;
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(band);
+
+static ssize_t ecrnx_dbgfs_type_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int ret;
+    ssize_t read;
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "TYPE=%d\n", priv->phy.sec_chan.type);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_type_write(struct file *file,
+                                     const char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int val;
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+    if (copy_from_user(buf, user_buf, len))
+        return -EFAULT;
+
+    buf[len] = '\0';
+
+    if ((sscanf(buf, "%d", &val) > 0) && (val >= PHY_CHNL_BW_20) &&
+        (val <= PHY_CHNL_BW_80P80))
+        priv->phy.sec_chan.type = val;
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(type);
+
+static ssize_t ecrnx_dbgfs_prim20_read(struct file *file,
+                                      char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int ret;
+    ssize_t read;
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "PRIM20=%dMHz\n", priv->phy.sec_chan.prim20_freq);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_prim20_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int val;
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+    if (copy_from_user(buf, user_buf, len))
+        return -EFAULT;
+
+    buf[len] = '\0';
+
+    if (sscanf(buf, "%d", &val) > 0)
+        priv->phy.sec_chan.prim20_freq = val;
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(prim20);
+
+static ssize_t ecrnx_dbgfs_center1_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int ret;
+    ssize_t read;
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "CENTER1=%dMHz\n", priv->phy.sec_chan.center1_freq);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_center1_write(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int val;
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+    if (copy_from_user(buf, user_buf, len))
+        return -EFAULT;
+
+    buf[len] = '\0';
+
+    if (sscanf(buf, "%d", &val) > 0)
+        priv->phy.sec_chan.center1_freq = val;
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(center1);
+
+static ssize_t ecrnx_dbgfs_center2_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int ret;
+    ssize_t read;
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "CENTER2=%dMHz\n", priv->phy.sec_chan.center2_freq);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_center2_write(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[32];
+    int val;
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+    if (copy_from_user(buf, user_buf, len))
+        return -EFAULT;
+
+    buf[len] = '\0';
+
+    if (sscanf(buf, "%d", &val) > 0)
+        priv->phy.sec_chan.center2_freq = val;
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(center2);
+
+
+static ssize_t ecrnx_dbgfs_set_read(struct file *file,
+                                   char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+    return 0;
+}
+
+static ssize_t ecrnx_dbgfs_set_write(struct file *file,
+                                    const char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+
+    ecrnx_send_set_channel(priv, 1, NULL);
+    ecrnx_radar_detection_enable(&priv->radar, ECRNX_RADAR_DETECT_ENABLE,
+                                ECRNX_RADAR_FCU);
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(set);
+#endif /* CONFIG_ECRNX_RADAR */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+#define LINE_MAX_SZ 150
+
+struct st {
+    char line[LINE_MAX_SZ + 1];
+    unsigned int r_idx;
+};
+
+static int compare_idx(const void *st1, const void *st2)
+{
+    int index1 = ((struct st *)st1)->r_idx;
+    int index2 = ((struct st *)st2)->r_idx;
+
+    if (index1 > index2) return 1;
+    if (index1 < index2) return -1;
+
+    return 0;
+}
+
+static const int ru_size[] =
+{
+    26,
+    52,
+    106,
+    242,
+    484,
+    996
+};
+
+static int print_rate(char *buf, int size, int format, int nss, int mcs, int bw,
+                      int sgi, int pre, int dcm, int *r_idx)
+{
+    int res = 0;
+    int bitrates_cck[4] = { 10, 20, 55, 110 };
+    int bitrates_ofdm[8] = { 6, 9, 12, 18, 24, 36, 48, 54};
+    char he_gi[3][4] = {"0.8", "1.6", "3.2"};
+
+    if (format < FORMATMOD_HT_MF) {
+        if (mcs < 4) {
+            if (r_idx) {
+                *r_idx = (mcs * 2) + pre;
+                res = scnprintf(buf, size - res, "%3d ", *r_idx);
+            }
+            res += scnprintf(&buf[res], size - res, "L-CCK/%cP          %2u.%1uM    ",
+                             pre > 0 ? 'L' : 'S',
+                             bitrates_cck[mcs] / 10,
+                             bitrates_cck[mcs] % 10);
+        } else {
+            mcs -= 4;
+            if (r_idx) {
+                *r_idx = N_CCK + mcs;
+                res = scnprintf(buf, size - res, "%3d ", *r_idx);
+            }
+            res += scnprintf(&buf[res], size - res, "L-OFDM            %2u.0M    ",
+                             bitrates_ofdm[mcs]);
+        }
+    } else if (format < FORMATMOD_VHT) {
+        if (r_idx) {
+            *r_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 + bw * 2 + sgi;
+            res = scnprintf(buf, size - res, "%3d ", *r_idx);
+        }
+        mcs += nss * 8;
+        res += scnprintf(&buf[res], size - res, "HT%d/%cGI           MCS%-2d   ",
+                         20 * (1 << bw), sgi ? 'S' : 'L', mcs);
+    } else if (format == FORMATMOD_VHT){
+        if (r_idx) {
+            *r_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
+            res = scnprintf(buf, size - res, "%3d ", *r_idx);
+        }
+        res += scnprintf(&buf[res], size - res, "VHT%d/%cGI%*cMCS%d/%1d  ",
+                         20 * (1 << bw), sgi ? 'S' : 'L', bw > 2 ? 9 : 10, ' ',
+                         mcs, nss + 1);
+    } else if (format == FORMATMOD_HE_SU){
+        if (r_idx) {
+            *r_idx = N_CCK + N_OFDM + N_HT + N_VHT + nss * 144 + mcs * 12 + bw * 3 + sgi;
+            res = scnprintf(buf, size - res, "%3d ", *r_idx);
+        }
+        res += scnprintf(&buf[res], size - res, "HE%d/GI%s%4s%*cMCS%d/%1d%*c",
+                         20 * (1 << bw), he_gi[sgi], dcm ? "/DCM" : "",
+                         bw > 2 ? 4 : 5, ' ', mcs, nss + 1, mcs > 9 ? 1 : 2, ' ');
+    } else {
+        if (r_idx) {
+            *r_idx = N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU + nss * 216 + mcs * 18 + bw * 3 + sgi;
+            res = scnprintf(buf, size - res, "%3d ", *r_idx);
+        }
+        res += scnprintf(&buf[res], size - res, "HEMU-%d/GI%s%*cMCS%d/%1d%*c",
+                         ru_size[bw], he_gi[sgi], bw > 1 ? 1 : 2, ' ',
+                         mcs, nss + 1, mcs > 9 ? 1 : 2, ' ');
+
+    }
+
+    return res;
+}
+
+static int print_rate_from_cfg(char *buf, int size, u32 rate_config, int *r_idx, int ru_size)
+{
+    union ecrnx_rate_ctrl_info *r_cfg = (union ecrnx_rate_ctrl_info *)&rate_config;
+    union ecrnx_mcs_index *mcs_index = (union ecrnx_mcs_index *)&rate_config;
+    unsigned int ft, pre, gi, bw, nss, mcs, dcm, len;
+
+    ft = r_cfg->formatModTx;
+    pre = r_cfg->giAndPreTypeTx >> 1;
+    gi = r_cfg->giAndPreTypeTx;
+    bw = r_cfg->bwTx;
+    dcm = 0;
+    if (ft == FORMATMOD_HE_MU) {
+        mcs = mcs_index->he.mcs;
+        nss = mcs_index->he.nss;
+        bw = ru_size;
+        dcm = r_cfg->dcmTx;
+    } else if (ft == FORMATMOD_HE_SU) {
+        mcs = mcs_index->he.mcs;
+        nss = mcs_index->he.nss;
+        dcm = r_cfg->dcmTx;
+    } else if (ft == FORMATMOD_VHT) {
+        mcs = mcs_index->vht.mcs;
+        nss = mcs_index->vht.nss;
+    } else if (ft >= FORMATMOD_HT_MF) {
+        mcs = mcs_index->ht.mcs;
+        nss = mcs_index->ht.nss;
+    } else {
+        mcs = mcs_index->legacy;
+        nss = 0;
+    }
+
+    len = print_rate(buf, size, ft, nss, mcs, bw, gi, pre, dcm, r_idx);
+    return len;
+}
+
+static void idx_to_rate_cfg(int idx, union ecrnx_rate_ctrl_info *r_cfg, int *ru_size)
+{
+    r_cfg->value = 0;
+    if (idx < N_CCK)
+    {
+        r_cfg->formatModTx = FORMATMOD_NON_HT;
+        r_cfg->giAndPreTypeTx = (idx & 1) << 1;
+        r_cfg->mcsIndexTx = idx / 2;
+    }
+    else if (idx < (N_CCK + N_OFDM))
+    {
+        r_cfg->formatModTx = FORMATMOD_NON_HT;
+        r_cfg->mcsIndexTx =  idx - N_CCK + 4;
+    }
+    else if (idx < (N_CCK + N_OFDM + N_HT))
+    {
+        union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
+
+        idx -= (N_CCK + N_OFDM);
+        r_cfg->formatModTx = FORMATMOD_HT_MF;
+        r->ht.nss = idx / (8*2*2);
+        r->ht.mcs = (idx % (8*2*2)) / (2*2);
+        r_cfg->bwTx = ((idx % (8*2*2)) % (2*2)) / 2;
+        r_cfg->giAndPreTypeTx = idx & 1;
+    }
+    else if (idx < (N_CCK + N_OFDM + N_HT + N_VHT))
+    {
+        union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
+
+        idx -= (N_CCK + N_OFDM + N_HT);
+        r_cfg->formatModTx = FORMATMOD_VHT;
+        r->vht.nss = idx / (10*4*2);
+        r->vht.mcs = (idx % (10*4*2)) / (4*2);
+        r_cfg->bwTx = ((idx % (10*4*2)) % (4*2)) / 2;
+        r_cfg->giAndPreTypeTx = idx & 1;
+    }
+    else if (idx < (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU))
+    {
+        union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
+
+        idx -= (N_CCK + N_OFDM + N_HT + N_VHT);
+        r_cfg->formatModTx = FORMATMOD_HE_SU;
+        r->vht.nss = idx / (12*4*3);
+        r->vht.mcs = (idx % (12*4*3)) / (4*3);
+        r_cfg->bwTx = ((idx % (12*4*3)) % (4*3)) / 3;
+        r_cfg->giAndPreTypeTx = idx % 3;
+    }
+    else
+    {
+        union ecrnx_mcs_index *r = (union ecrnx_mcs_index *)r_cfg;
+
+        BUG_ON(ru_size == NULL);
+
+        idx -= (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU);
+        r_cfg->formatModTx = FORMATMOD_HE_MU;
+        r->vht.nss = idx / (12*6*3);
+        r->vht.mcs = (idx % (12*6*3)) / (6*3);
+        *ru_size = ((idx % (12*6*3)) % (6*3)) / 3;
+        r_cfg->giAndPreTypeTx = idx % 3;
+        r_cfg->bwTx = 0;
+    }
+}
+
+static struct ecrnx_sta* ecrnx_dbgfs_get_sta(struct ecrnx_hw *ecrnx_hw,
+                                           char* mac_addr)
+{
+    u8 mac[6];
+
+    if (sscanf(mac_addr, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+        &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6)
+        return NULL;
+    return ecrnx_get_sta(ecrnx_hw, mac);
+}
+
+static ssize_t ecrnx_dbgfs_twt_request_read(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count,
+                                           loff_t *ppos)
+{
+    char buf[750];
+    ssize_t read;
+    struct ecrnx_hw *priv = file->private_data;
+    struct ecrnx_sta *sta = NULL;
+    int len;
+
+    /* Get the station index from MAC address */
+    sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+    if (sta == NULL)
+        return -EINVAL;
+    if (sta->twt_ind.sta_idx != ECRNX_INVALID_STA)
+    {
+        struct twt_conf_tag *conf = &sta->twt_ind.conf;
+        if (sta->twt_ind.resp_type == MAC_TWT_SETUP_ACCEPT)
+            len = scnprintf(buf, sizeof(buf) - 1, "Accepted configuration");
+        else if (sta->twt_ind.resp_type == MAC_TWT_SETUP_ALTERNATE)
+            len = scnprintf(buf, sizeof(buf) - 1, "Alternate configuration proposed by AP");
+        else if (sta->twt_ind.resp_type == MAC_TWT_SETUP_DICTATE)
+            len = scnprintf(buf, sizeof(buf) - 1, "AP dictates the following configuration");
+        else if (sta->twt_ind.resp_type == MAC_TWT_SETUP_REJECT)
+            len = scnprintf(buf, sizeof(buf) - 1, "AP rejects the following configuration");
+        else
+        {
+            len = scnprintf(buf, sizeof(buf) - 1, "Invalid response from the peer");
+            goto end;
+        }
+        len += scnprintf(&buf[len], sizeof(buf) - 1 - len,":\n"
+                         "flow_type = %d\n"
+                         "wake interval mantissa = %d\n"
+                         "wake interval exponent = %d\n"
+                         "wake interval = %d us\n"
+                         "nominal minimum wake duration = %d us\n",
+                         conf->flow_type, conf->wake_int_mantissa,
+                         conf->wake_int_exp,
+                         conf->wake_int_mantissa << conf->wake_int_exp,
+                         conf->wake_dur_unit ?
+                         conf->min_twt_wake_dur * 1024:
+                         conf->min_twt_wake_dur * 256);
+    }
+    else
+    {
+        len = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                        "setup_command = <0: request, 1: suggest, 2: demand>,"
+                        "flow_type = <0: announced, 1: unannounced>,"
+                        "wake_interval_mantissa = <0 if setup request and no constraints>,"
+                        "wake_interval_exp = <0 if setup request and no constraints>,"
+                        "nominal_min_wake_dur = <0 if setup request and no constraints>,"
+                        "wake_dur_unit = <0: 256us, 1: tu>");
+    }
+  end:
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_twt_request_write(struct file *file,
+                                            const char __user *user_buf,
+                                            size_t count,
+                                            loff_t *ppos)
+{
+    char *accepted_params[] = {"setup_command",
+                               "flow_type",
+                               "wake_interval_mantissa",
+                               "wake_interval_exp",
+                               "nominal_min_wake_dur",
+                               "wake_dur_unit",
+                               0
+                               };
+    struct twt_conf_tag twt_conf;
+    struct twt_setup_cfm twt_setup_cfm;
+    struct ecrnx_sta *sta = NULL;
+    struct ecrnx_hw *priv = file->private_data;
+    char param[30];
+    char *line;
+    int error = 1, i, val, setup_command = -1;
+    bool_l found;
+    char *buf = kmalloc(1024, GFP_ATOMIC);
+    size_t len = 1024 - 1;
+
+    if(!buf){
+        return 0;
+    }
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    /* Get the station index from MAC address */
+    sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+    if (sta == NULL){
+        kfree(buf);
+        return -EINVAL;
+    }
+
+    /* Get the content of the file */
+    if (copy_from_user(buf, user_buf, len)){
+        kfree(buf);
+        return -EFAULT;
+    }
+
+    buf[len] = '\0';
+    memset(&twt_conf, 0, sizeof(twt_conf));
+
+    line = buf;
+    /* Get the content of the file */
+    while (line != NULL)
+    {
+        if (sscanf(line, "%s = %d", param, &val) == 2)
+        {
+            i = 0;
+            found = false;
+            // Check if parameter is valid
+            while(accepted_params[i])
+            {
+                if (strcmp(accepted_params[i], param) == 0)
+                {
+                    found = true;
+                    break;
+                }
+                i++;
+            }
+
+            if (!found)
+            {
+                dev_err(priv->dev, "%s: parameter %s is not valid\n", __func__, param);
+                kfree(buf);
+                return -EINVAL;
+            }
+
+            if (!strcmp(param, "setup_command"))
+            {
+                setup_command = val;
+            }
+            else if (!strcmp(param, "flow_type"))
+            {
+                twt_conf.flow_type = val;
+            }
+            else if (!strcmp(param, "wake_interval_mantissa"))
+            {
+                twt_conf.wake_int_mantissa = val;
+            }
+            else if (!strcmp(param, "wake_interval_exp"))
+            {
+                twt_conf.wake_int_exp = val;
+            }
+            else if (!strcmp(param, "nominal_min_wake_dur"))
+            {
+                twt_conf.min_twt_wake_dur = val;
+            }
+            else if (!strcmp(param, "wake_dur_unit"))
+            {
+                twt_conf.wake_dur_unit = val;
+            }
+        }
+        else
+        {
+            dev_err(priv->dev, "%s: Impossible to read TWT configuration option\n", __func__);
+            kfree(buf);
+            return -EFAULT;
+        }
+        line = strchr(line, ',');
+        if(line == NULL)
+            break;
+        line++;
+    }
+
+    if (setup_command == -1)
+    {
+        dev_err(priv->dev, "%s: TWT missing setup command\n", __func__);
+        kfree(buf);
+        return -EFAULT;
+    }
+
+    // Forward the request to the LMAC
+    if ((error = ecrnx_send_twt_request(priv, setup_command, sta->vif_idx,
+                                       &twt_conf, &twt_setup_cfm)) != 0){
+        kfree(buf);
+        return error;
+        }
+
+    // Check the status
+    if (twt_setup_cfm.status != CO_OK){
+        kfree(buf);
+        return -EIO;
+    }
+
+    kfree(buf);
+    return count;
+}
+DEBUGFS_READ_WRITE_FILE_OPS(twt_request);
+
+static ssize_t ecrnx_dbgfs_twt_teardown_read(struct file *file,
+                                            char __user *user_buf,
+                                            size_t count,
+                                            loff_t *ppos)
+{
+    char buf[512];
+    int ret;
+    ssize_t read;
+
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "TWT teardown format:\n\n"
+                    "flow_id = <ID>\n");
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_twt_teardown_write(struct file *file,
+                                             const char __user *user_buf,
+                                             size_t count,
+                                             loff_t *ppos)
+{
+    struct twt_teardown_req twt_teardown;
+    struct twt_teardown_cfm twt_teardown_cfm;
+    struct ecrnx_sta *sta = NULL;
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[256];
+    char *line;
+    int error = 1;
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    /* Get the station index from MAC address */
+    sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+    if (sta == NULL)
+        return -EINVAL;
+
+    /* Get the content of the file */
+    if (copy_from_user(buf, user_buf, len))
+        return -EINVAL;
+
+    buf[len] = '\0';
+    memset(&twt_teardown, 0, sizeof(twt_teardown));
+
+    /* Get the content of the file */
+    line = buf;
+
+    if (sscanf(line, "flow_id = %d", (int *) &twt_teardown.id) != 1)
+    {
+        dev_err(priv->dev, "%s: Invalid TWT configuration\n", __func__);
+        return -EINVAL;
+    }
+
+    twt_teardown.neg_type = 0;
+    twt_teardown.all_twt = 0;
+    twt_teardown.vif_idx = sta->vif_idx;
+
+    // Forward the request to the LMAC
+    if ((error = ecrnx_send_twt_teardown(priv, &twt_teardown, &twt_teardown_cfm)) != 0)
+        return error;
+
+    // Check the status
+    if (twt_teardown_cfm.status != CO_OK)
+        return -EIO;
+
+    return count;
+}
+DEBUGFS_READ_WRITE_FILE_OPS(twt_teardown);
+
+static ssize_t ecrnx_dbgfs_rc_stats_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_sta *sta = NULL;
+    struct ecrnx_hw *priv = file->private_data;
+    char *buf;
+    int bufsz, len = 0;
+    ssize_t read;
+    int i = 0;
+    int error = 0;
+    struct me_rc_stats_cfm me_rc_stats_cfm;
+    unsigned int no_samples;
+    struct st *st;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* everything should fit in one call */
+    if (*ppos)
+        return 0;
+
+    /* Get the station index from MAC address */
+    sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+    if (sta == NULL)
+        return -EINVAL;
+
+    /* Forward the information to the LMAC */
+    if ((error = ecrnx_send_me_rc_stats(priv, sta->sta_idx, &me_rc_stats_cfm)))
+        return error;
+
+    no_samples = me_rc_stats_cfm.no_samples;
+    if (no_samples == 0)
+        return 0;
+
+    bufsz = no_samples * LINE_MAX_SZ + 500;
+
+    buf = kmalloc(bufsz + 1, GFP_ATOMIC);
+    if (buf == NULL)
+        return 0;
+
+    st = kmalloc(sizeof(struct st) * no_samples, GFP_ATOMIC);
+    if (st == NULL)
+    {
+        kfree(buf);
+        return 0;
+    }
+
+    for (i = 0; i < no_samples; i++)
+    {
+        unsigned int tp, eprob;
+        len = print_rate_from_cfg(st[i].line, LINE_MAX_SZ,
+                                  me_rc_stats_cfm.rate_stats[i].rate_config,
+                                  &st[i].r_idx, 0);
+
+        if (me_rc_stats_cfm.sw_retry_step != 0)
+        {
+            len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len,  "%c",
+                    me_rc_stats_cfm.retry_step_idx[me_rc_stats_cfm.sw_retry_step] == i ? '*' : ' ');
+        }
+        else
+        {
+            len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " ");
+        }
+        len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
+                me_rc_stats_cfm.retry_step_idx[0] == i ? 'T' : ' ');
+        len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c",
+                me_rc_stats_cfm.retry_step_idx[1] == i ? 't' : ' ');
+        len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, "%c ",
+                me_rc_stats_cfm.retry_step_idx[2] == i ? 'P' : ' ');
+
+        tp = me_rc_stats_cfm.tp[i] / 10;
+        len += scnprintf(&st[i].line[len], LINE_MAX_SZ - len, " %4u.%1u",
+                         tp / 10, tp % 10);
+
+        eprob = ((me_rc_stats_cfm.rate_stats[i].probability * 1000) >> 16) + 1;
+        len += scnprintf(&st[i].line[len],LINE_MAX_SZ - len,
+                         "  %4u.%1u %5u(%6u)  %6u",
+                         eprob / 10, eprob % 10,
+                         me_rc_stats_cfm.rate_stats[i].success,
+                         me_rc_stats_cfm.rate_stats[i].attempts,
+                         me_rc_stats_cfm.rate_stats[i].sample_skipped);
+    }
+    len = scnprintf(buf, bufsz ,
+                     "\nTX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
+                     sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2],
+                     sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5]);
+
+    len += scnprintf(&buf[len], bufsz - len,
+            " #  type               rate             tpt   eprob    ok(   tot)   skipped\n");
+
+    // add sorted statistics to the buffer
+    sort(st, no_samples, sizeof(st[0]), compare_idx, NULL);
+    for (i = 0; i < no_samples; i++)
+    {
+        len += scnprintf(&buf[len], bufsz - len, "%s\n", st[i].line);
+    }
+
+    // display HE TB statistics if any
+    if (me_rc_stats_cfm.rate_stats[RC_HE_STATS_IDX].rate_config != 0) {
+        unsigned int tp, eprob;
+        struct rc_rate_stats *rate_stats = &me_rc_stats_cfm.rate_stats[RC_HE_STATS_IDX];
+        int ru_index = rate_stats->ru_and_length & 0x07;
+        int ul_length = rate_stats->ru_and_length >> 3;
+
+        len += scnprintf(&buf[len], bufsz - len,
+                         "\nHE TB rate info:\n");
+
+        len += scnprintf(&buf[len], bufsz - len,
+                "    type               rate             tpt   eprob    ok(   tot)   ul_length\n    ");
+        len += print_rate_from_cfg(&buf[len], bufsz - len, rate_stats->rate_config,
+                                   NULL, ru_index);
+
+        tp = me_rc_stats_cfm.tp[RC_HE_STATS_IDX] / 10;
+        len += scnprintf(&buf[len], bufsz - len, "      %4u.%1u",
+                         tp / 10, tp % 10);
+
+        eprob = ((rate_stats->probability * 1000) >> 16) + 1;
+        len += scnprintf(&buf[len],bufsz - len,
+                         "  %4u.%1u %5u(%6u)  %6u\n",
+                         eprob / 10, eprob % 10,
+                         rate_stats->success,
+                         rate_stats->attempts,
+                         ul_length);
+    }
+
+    len += scnprintf(&buf[len], bufsz - len, "\n MPDUs AMPDUs AvLen trialP");
+    len += scnprintf(&buf[len], bufsz - len, "\n%6u %6u %3d.%1d %6u\n",
+                     me_rc_stats_cfm.ampdu_len,
+                     me_rc_stats_cfm.ampdu_packets,
+                     me_rc_stats_cfm.avg_ampdu_len >> 16,
+                     ((me_rc_stats_cfm.avg_ampdu_len * 10) >> 16) % 10,
+                     me_rc_stats_cfm.sample_wait);
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+    kfree(buf);
+    kfree(st);
+
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rc_stats);
+
+static ssize_t ecrnx_dbgfs_rc_fixed_rate_idx_write(struct file *file,
+                                                  const char __user *user_buf,
+                                                  size_t count, loff_t *ppos)
+{
+    struct ecrnx_sta *sta = NULL;
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[10];
+    int fixed_rate_idx = -1;
+    union ecrnx_rate_ctrl_info rate_config;
+    int error = 0;
+    size_t len = min_t(size_t, count, sizeof(buf) - 1);
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Get the station index from MAC address */
+    sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+    if (sta == NULL)
+        return -EINVAL;
+
+    /* Get the content of the file */
+    if (copy_from_user(buf, user_buf, len))
+        return -EFAULT;
+    buf[len] = '\0';
+    sscanf(buf, "%i\n", &fixed_rate_idx);
+
+    /* Convert rate index into rate configuration */
+    if ((fixed_rate_idx < 0) || (fixed_rate_idx >= (N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU)))
+    {
+        // disable fixed rate
+        rate_config.value = (u32)-1;
+    }
+    else
+    {
+        idx_to_rate_cfg(fixed_rate_idx, &rate_config, NULL);
+    }
+
+    // Forward the request to the LMAC
+    if ((error = ecrnx_send_me_rc_set_rate(priv, sta->sta_idx,
+                                          (u16)rate_config.value)) != 0)
+    {
+        return error;
+    }
+
+    priv->debugfs.rc_config[sta->sta_idx] = (int)rate_config.value;
+    return len;
+}
+
+DEBUGFS_WRITE_FILE_OPS(rc_fixed_rate_idx);
+
+static ssize_t ecrnx_dbgfs_last_rx_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+    struct ecrnx_sta *sta = NULL;
+    struct ecrnx_hw *priv = file->private_data;
+    struct ecrnx_rx_rate_stats *rate_stats;
+    char *buf;
+    int bufsz, i, len = 0;
+    ssize_t read;
+    unsigned int fmt, pre, bw, nss, mcs, gi, dcm = 0;
+    struct rx_vector_1 *last_rx;
+    char hist[] = "##################################################";
+    int hist_len = sizeof(hist) - 1;
+    u8 nrx;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* everything should fit in one call */
+    if (*ppos)
+        return 0;
+
+    /* Get the station index from MAC address */
+    sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+    if (sta == NULL)
+        return -EINVAL;
+
+    rate_stats = &sta->stats.rx_rate;
+    bufsz = (rate_stats->rate_cnt * ( 50 + hist_len) + 200);
+    buf = kmalloc(bufsz + 1, GFP_ATOMIC);
+    if (buf == NULL)
+        return 0;
+
+    // Get number of RX paths
+    nrx = (priv->version_cfm.version_phy_1 & MDM_NRX_MASK) >> MDM_NRX_LSB;
+
+    len += scnprintf(buf, bufsz,
+                     "\nRX rate info for %02X:%02X:%02X:%02X:%02X:%02X:\n",
+                     sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2],
+                     sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5]);
+
+    // Display Statistics
+    for (i = 0 ; i < rate_stats->size ; i++ )
+    {
+        if (rate_stats->table[i]) {
+            union ecrnx_rate_ctrl_info rate_config;
+            int percent = ((/*(u64)*/rate_stats->table[i]) * 1000) / rate_stats->cpt;
+            int p;
+            int ru_size;
+
+            idx_to_rate_cfg(i, &rate_config, &ru_size);
+            len += print_rate_from_cfg(&buf[len], bufsz - len,
+                                       rate_config.value, NULL, ru_size);
+            p = (percent * hist_len) / 1000;
+            len += scnprintf(&buf[len], bufsz - len, ": %9d(%2d.%1d%%)%.*s\n",
+                             rate_stats->table[i],
+                             percent / 10, percent % 10, p, hist);
+        }
+    }
+
+    // Display detailed info of the last received rate
+    last_rx = &sta->stats.last_rx.rx_vect1;
+
+    len += scnprintf(&buf[len], bufsz - len,"\nLast received rate\n"
+                     "  type         rate    LDPC STBC BEAMFM DCM DOPPLER %s\n",
+                     (nrx > 1) ? "rssi1(dBm) rssi2(dBm)" : "rssi(dBm)");
+
+    fmt = last_rx->format_mod;
+    bw = last_rx->ch_bw;
+    pre = last_rx->pre_type;
+    if (fmt >= FORMATMOD_HE_SU) {
+        mcs = last_rx->he.mcs;
+        nss = last_rx->he.nss;
+        gi = last_rx->he.gi_type;
+        if (fmt == FORMATMOD_HE_MU)
+            bw = last_rx->he.ru_size;
+        dcm = last_rx->he.dcm;
+    } else if (fmt == FORMATMOD_VHT) {
+        mcs = last_rx->vht.mcs;
+        nss = last_rx->vht.nss;
+        gi = last_rx->vht.short_gi;
+    } else if (fmt >= FORMATMOD_HT_MF) {
+        mcs = last_rx->ht.mcs % 8;
+        nss = last_rx->ht.mcs / 8;;
+        gi = last_rx->ht.short_gi;
+    } else {
+        BUG_ON((mcs = legrates_lut[last_rx->leg_rate].idx) == -1);
+        nss = 0;
+        gi = 0;
+    }
+
+    len += print_rate(&buf[len], bufsz - len, fmt, nss, mcs, bw, gi, pre, dcm, NULL);
+
+    /* flags for HT/VHT/HE */
+    if (fmt >= FORMATMOD_HE_SU) {
+        len += scnprintf(&buf[len], bufsz - len, "  %c    %c     %c    %c     %c",
+                         last_rx->he.fec ? 'L' : ' ',
+                         last_rx->he.stbc ? 'S' : ' ',
+                         last_rx->he.beamformed ? 'B' : ' ',
+                         last_rx->he.dcm ? 'D' : ' ',
+                         last_rx->he.doppler ? 'D' : ' ');
+    } else if (fmt == FORMATMOD_VHT) {
+        len += scnprintf(&buf[len], bufsz - len, "  %c    %c     %c           ",
+                         last_rx->vht.fec ? 'L' : ' ',
+                         last_rx->vht.stbc ? 'S' : ' ',
+                         last_rx->vht.beamformed ? 'B' : ' ');
+    } else if (fmt >= FORMATMOD_HT_MF) {
+        len += scnprintf(&buf[len], bufsz - len, "  %c    %c                  ",
+                         last_rx->ht.fec ? 'L' : ' ',
+                         last_rx->ht.stbc ? 'S' : ' ');
+    } else {
+        len += scnprintf(&buf[len], bufsz - len, "                         ");
+    }
+    if (nrx > 1) {
+        len += scnprintf(&buf[len], bufsz - len, "       %-4d       %d\n",
+                         last_rx->rssi1, last_rx->rssi1);
+    } else {
+        len += scnprintf(&buf[len], bufsz - len, "      %d\n", last_rx->rssi1);
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+
+    kfree(buf);
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_last_rx_write(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_sta *sta = NULL;
+    struct ecrnx_hw *priv = file->private_data;
+
+    /* Get the station index from MAC address */
+    sta = ecrnx_dbgfs_get_sta(priv, file->f_path.dentry->d_parent->d_parent->d_iname);
+    if (sta == NULL)
+        return -EINVAL;
+
+    /* Prevent from interrupt preemption as these statistics are updated under
+     * interrupt */
+    spin_lock_bh(&priv->tx_lock);
+    memset(sta->stats.rx_rate.table, 0,
+           sta->stats.rx_rate.size * sizeof(sta->stats.rx_rate.table[0]));
+    sta->stats.rx_rate.cpt = 0;
+    sta->stats.rx_rate.rate_cnt = 0;
+    spin_unlock_bh(&priv->tx_lock);
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(last_rx);
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/*
+ * trace helper
+ */
+void ecrnx_fw_trace_dump(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+    /* may be called before ecrnx_dbgfs_register */
+    if (ecrnx_hw->plat->enabled && !ecrnx_hw->debugfs.fw_trace.buf.data) {
+        ecrnx_fw_trace_buf_init(&ecrnx_hw->debugfs.fw_trace.buf,
+                               ecrnx_ipc_fw_trace_desc_get(ecrnx_hw));
+    }
+
+    if (!ecrnx_hw->debugfs.fw_trace.buf.data)
+        return;
+
+    _ecrnx_fw_trace_dump(&ecrnx_hw->debugfs.fw_trace.buf);
+#endif
+}
+
+void ecrnx_fw_trace_reset(struct ecrnx_hw *ecrnx_hw)
+{
+    _ecrnx_fw_trace_reset(&ecrnx_hw->debugfs.fw_trace, true);
+}
+
+void ecrnx_dbgfs_trigger_fw_dump(struct ecrnx_hw *ecrnx_hw, char *reason)
+{
+    ecrnx_send_dbg_trigger_req(ecrnx_hw, reason);
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+static void _ecrnx_dbgfs_register_sta(struct ecrnx_debugfs *ecrnx_debugfs, struct ecrnx_sta *sta)
+{
+    struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw, debugfs);
+    struct dentry *dir_sta;
+    char sta_name[18];
+    struct dentry *dir_rc;
+    struct dentry *file;
+    struct ecrnx_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
+    int nb_rx_rate = N_CCK + N_OFDM;
+    struct ecrnx_rc_config_save *rc_cfg, *next;
+
+    if (sta->sta_idx >= NX_REMOTE_STA_MAX) {
+        scnprintf(sta_name, sizeof(sta_name), "bc_mc");
+    } else {
+        scnprintf(sta_name, sizeof(sta_name), "%pM", sta->mac_addr);
+    }
+
+    if (!(dir_sta = debugfs_create_dir(sta_name, ecrnx_debugfs->dir_stas)))
+        goto error;
+    ecrnx_debugfs->dir_sta[sta->sta_idx] = dir_sta;
+
+    if (!(dir_rc = debugfs_create_dir("rc", ecrnx_debugfs->dir_sta[sta->sta_idx])))
+        goto error_after_dir;
+
+    ecrnx_debugfs->dir_rc_sta[sta->sta_idx] = dir_rc;
+
+    file = debugfs_create_file("stats", S_IRUSR, dir_rc, ecrnx_hw,
+                               &ecrnx_dbgfs_rc_stats_ops);
+    if (IS_ERR_OR_NULL(file))
+        goto error_after_dir;
+
+    file = debugfs_create_file("fixed_rate_idx", S_IWUSR , dir_rc, ecrnx_hw,
+                               &ecrnx_dbgfs_rc_fixed_rate_idx_ops);
+    if (IS_ERR_OR_NULL(file))
+        goto error_after_dir;
+
+    file = debugfs_create_file("rx_rate", S_IRUSR | S_IWUSR, dir_rc, ecrnx_hw,
+                               &ecrnx_dbgfs_last_rx_ops);
+    if (IS_ERR_OR_NULL(file))
+        goto error_after_dir;
+
+    if (ecrnx_hw->mod_params->ht_on)
+        nb_rx_rate += N_HT;
+
+    if (ecrnx_hw->mod_params->vht_on)
+        nb_rx_rate += N_VHT;
+
+    if (ecrnx_hw->mod_params->he_on)
+        nb_rx_rate += N_HE_SU + N_HE_MU;
+
+    rate_stats->table = kzalloc(nb_rx_rate * sizeof(rate_stats->table[0]),
+                                GFP_ATOMIC);
+    if (!rate_stats->table)
+        goto error_after_dir;
+
+    rate_stats->size = nb_rx_rate;
+    rate_stats->cpt = 0;
+    rate_stats->rate_cnt = 0;
+
+    /* By default enable rate contoller */
+    ecrnx_debugfs->rc_config[sta->sta_idx] = -1;
+
+    /* Unless we already fix the rate for this station */
+    list_for_each_entry_safe(rc_cfg, next, &ecrnx_debugfs->rc_config_save, list) {
+        if (jiffies_to_msecs(jiffies - rc_cfg->timestamp) > RC_CONFIG_DUR) {
+            list_del(&rc_cfg->list);
+            kfree(rc_cfg);
+        } else if (!memcmp(rc_cfg->mac_addr, sta->mac_addr, ETH_ALEN)) {
+            ecrnx_debugfs->rc_config[sta->sta_idx] = rc_cfg->rate;
+            list_del(&rc_cfg->list);
+            kfree(rc_cfg);
+            break;
+        }
+    }
+
+    if ((ecrnx_debugfs->rc_config[sta->sta_idx] >= 0) &&
+        ecrnx_send_me_rc_set_rate(ecrnx_hw, sta->sta_idx,
+                                 (u16)ecrnx_debugfs->rc_config[sta->sta_idx]))
+        ecrnx_debugfs->rc_config[sta->sta_idx] = -1;
+
+    if (ECRNX_VIF_TYPE(ecrnx_hw->vif_table[sta->vif_idx]) == NL80211_IFTYPE_STATION)
+    {
+        /* register the sta */
+        struct dentry *dir_twt;
+        struct dentry *file;
+
+        if (!(dir_twt = debugfs_create_dir("twt", ecrnx_debugfs->dir_sta[sta->sta_idx])))
+            goto error_after_dir;
+
+        ecrnx_debugfs->dir_twt_sta[sta->sta_idx] = dir_twt;
+
+        file = debugfs_create_file("request", S_IRUSR | S_IWUSR, dir_twt, ecrnx_hw,
+                                   &ecrnx_dbgfs_twt_request_ops);
+        if (IS_ERR_OR_NULL(file))
+            goto error_after_dir;
+
+        file = debugfs_create_file("teardown", S_IRUSR | S_IWUSR, dir_twt, ecrnx_hw,
+                                   &ecrnx_dbgfs_twt_teardown_ops);
+        if (IS_ERR_OR_NULL(file))
+            goto error_after_dir;
+
+        sta->twt_ind.sta_idx = ECRNX_INVALID_STA;
+    }
+    return;
+
+    error_after_dir:
+      debugfs_remove_recursive(ecrnx_debugfs->dir_sta[sta->sta_idx]);
+      ecrnx_debugfs->dir_sta[sta->sta_idx] = NULL;
+      ecrnx_debugfs->dir_rc_sta[sta->sta_idx] = NULL;
+      ecrnx_debugfs->dir_twt_sta[sta->sta_idx] = NULL;
+    error:
+      dev_err(ecrnx_hw->dev,
+              "Error while registering debug entry for sta %d\n", sta->sta_idx);
+}
+
+static void _ecrnx_dbgfs_unregister_sta(struct ecrnx_debugfs *ecrnx_debugfs, struct ecrnx_sta *sta)
+{
+    debugfs_remove_recursive(ecrnx_debugfs->dir_sta[sta->sta_idx]);
+    /* unregister the sta */
+    if (sta->stats.rx_rate.table) {
+        kfree(sta->stats.rx_rate.table);
+        sta->stats.rx_rate.table = NULL;
+    }
+    sta->stats.rx_rate.size = 0;
+    sta->stats.rx_rate.cpt  = 0;
+    sta->stats.rx_rate.rate_cnt = 0;
+
+    /* If fix rate was set for this station, save the configuration in case
+       we reconnect to this station within RC_CONFIG_DUR msec */
+    if (ecrnx_debugfs->rc_config[sta->sta_idx] >= 0) {
+        struct ecrnx_rc_config_save *rc_cfg;
+        rc_cfg = kmalloc(sizeof(*rc_cfg), GFP_ATOMIC);
+        if (rc_cfg) {
+            rc_cfg->rate = ecrnx_debugfs->rc_config[sta->sta_idx];
+            rc_cfg->timestamp = jiffies;
+            memcpy(rc_cfg->mac_addr, sta->mac_addr, ETH_ALEN);
+            list_add_tail(&rc_cfg->list, &ecrnx_debugfs->rc_config_save);
+        }
+    }
+
+    ecrnx_debugfs->dir_sta[sta->sta_idx] = NULL;
+    ecrnx_debugfs->dir_rc_sta[sta->sta_idx] = NULL;
+    ecrnx_debugfs->dir_twt_sta[sta->sta_idx] = NULL;
+    sta->twt_ind.sta_idx = ECRNX_INVALID_STA;
+}
+
+static void ecrnx_sta_work(struct work_struct *ws)
+{
+    struct ecrnx_debugfs *ecrnx_debugfs = container_of(ws, struct ecrnx_debugfs, sta_work);
+    struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw, debugfs);
+    struct ecrnx_sta *sta;
+    uint8_t sta_idx;
+
+    sta_idx = ecrnx_debugfs->sta_idx;
+    if (sta_idx > (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+        WARN(1, "Invalid sta index %d", sta_idx);
+        return;
+    }
+
+    ecrnx_debugfs->sta_idx = ECRNX_INVALID_STA;
+    sta = &ecrnx_hw->sta_table[sta_idx];
+    if (!sta) {
+        WARN(1, "Invalid sta %d", sta_idx);
+        return;
+    }
+
+    if (ecrnx_debugfs->dir_sta[sta_idx] == NULL)
+        _ecrnx_dbgfs_register_sta(ecrnx_debugfs, sta);
+    else
+        _ecrnx_dbgfs_unregister_sta(ecrnx_debugfs, sta);
+
+    return;
+}
+
+void _ecrnx_dbgfs_sta_write(struct ecrnx_debugfs *ecrnx_debugfs, uint8_t sta_idx)
+{
+    if (ecrnx_debugfs->unregistering)
+        return;
+
+    ecrnx_debugfs->sta_idx = sta_idx;
+    schedule_work(&ecrnx_debugfs->sta_work);
+}
+
+void ecrnx_dbgfs_unregister_sta(struct ecrnx_hw *ecrnx_hw,
+                               struct ecrnx_sta *sta)
+{
+    _ecrnx_dbgfs_sta_write(&ecrnx_hw->debugfs, sta->sta_idx);
+}
+
+void ecrnx_dbgfs_register_sta(struct ecrnx_hw *ecrnx_hw,
+                             struct ecrnx_sta *sta)
+{
+    _ecrnx_dbgfs_sta_write(&ecrnx_hw->debugfs, sta->sta_idx);
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+int ecrnx_dbgfs_register(struct ecrnx_hw *ecrnx_hw, const char *name)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct dentry *phyd = ecrnx_hw->hw->wiphy->debugfsdir;
+#else
+    struct dentry *phyd = ecrnx_hw->wiphy->debugfsdir;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+    struct ecrnx_debugfs *ecrnx_debugfs = &ecrnx_hw->debugfs;
+    struct dentry *dir_drv, *dir_diags, *dir_stas;
+
+    if (!(dir_drv = debugfs_create_dir(name, phyd)))
+        return -ENOMEM;
+
+    ecrnx_debugfs->dir = dir_drv;
+
+    if (!(dir_stas = debugfs_create_dir("stations", dir_drv)))
+        return -ENOMEM;
+
+    ecrnx_debugfs->dir_stas = dir_stas;
+    ecrnx_debugfs->unregistering = false;
+
+    if (!(dir_diags = debugfs_create_dir("diags", dir_drv)))
+        goto err;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    INIT_WORK(&ecrnx_debugfs->sta_work, ecrnx_sta_work);
+    INIT_LIST_HEAD(&ecrnx_debugfs->rc_config_save);
+    ecrnx_debugfs->sta_idx = ECRNX_INVALID_STA;
+#endif
+
+    DEBUGFS_ADD_U32(tcp_pacing_shift, dir_drv, &ecrnx_hw->tcp_pacing_shift,
+                    S_IWUSR | S_IRUSR);
+    DEBUGFS_ADD_FILE(stats, dir_drv, S_IWUSR | S_IRUSR);
+#if 0
+    DEBUGFS_ADD_FILE(sys_stats, dir_drv,  S_IRUSR);
+#endif
+#ifdef CONFIG_ECRNX_SOFTMAC
+    DEBUGFS_ADD_X64(rateidx, dir_drv, &ecrnx_hw->debugfs.rateidx);
+#endif
+    DEBUGFS_ADD_FILE(txq, dir_drv, S_IRUSR);
+    DEBUGFS_ADD_FILE(acsinfo, dir_drv, S_IRUSR);
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    DEBUGFS_ADD_FILE(mu_group, dir_drv, S_IRUSR);
+#endif
+
+#ifdef CONFIG_ECRNX_P2P_DEBUGFS
+    {
+        /* Create a p2p directory */
+        struct dentry *dir_p2p;
+        if (!(dir_p2p = debugfs_create_dir("p2p", dir_drv)))
+            goto err;
+
+        /* Add file allowing to control Opportunistic PS */
+        DEBUGFS_ADD_FILE(oppps, dir_p2p, S_IRUSR);
+        /* Add file allowing to control Notice of Absence */
+        DEBUGFS_ADD_FILE(noa, dir_p2p, S_IRUSR);
+    }
+#endif /* CONFIG_ECRNX_P2P_DEBUGFS */
+
+#if CONFIG_ECRNX_DBGFS_FW_TRACE
+    if (ecrnx_dbgfs_register_fw_dump(ecrnx_hw, dir_drv, dir_diags))
+        goto err;
+    DEBUGFS_ADD_FILE(fw_dbg, dir_diags, S_IWUSR | S_IRUSR);
+
+    if (!ecrnx_fw_trace_init(&ecrnx_hw->debugfs.fw_trace,
+                            ecrnx_ipc_fw_trace_desc_get(ecrnx_hw))) {
+        DEBUGFS_ADD_FILE(fw_trace, dir_diags, S_IWUSR | S_IRUSR);
+        if (ecrnx_hw->debugfs.fw_trace.buf.nb_compo)
+            DEBUGFS_ADD_FILE(fw_trace_level, dir_diags, S_IWUSR | S_IRUSR);
+    } else {
+        ecrnx_debugfs->fw_trace.buf.data = NULL;
+    }
+#endif
+
+#ifdef CONFIG_ECRNX_RADAR
+    {
+        struct dentry *dir_radar, *dir_sec;
+        if (!(dir_radar = debugfs_create_dir("radar", dir_drv)))
+            goto err;
+
+        DEBUGFS_ADD_FILE(pulses_prim, dir_radar, S_IRUSR);
+        DEBUGFS_ADD_FILE(detected,    dir_radar, S_IRUSR);
+        DEBUGFS_ADD_FILE(enable,      dir_radar, S_IRUSR);
+
+        if (ecrnx_hw->phy.cnt == 2) {
+            DEBUGFS_ADD_FILE(pulses_sec, dir_radar, S_IRUSR);
+
+            if (!(dir_sec = debugfs_create_dir("sec", dir_radar)))
+                goto err;
+
+            DEBUGFS_ADD_FILE(band,    dir_sec, S_IWUSR | S_IRUSR);
+            DEBUGFS_ADD_FILE(type,    dir_sec, S_IWUSR | S_IRUSR);
+            DEBUGFS_ADD_FILE(prim20,  dir_sec, S_IWUSR | S_IRUSR);
+            DEBUGFS_ADD_FILE(center1, dir_sec, S_IWUSR | S_IRUSR);
+            DEBUGFS_ADD_FILE(center2, dir_sec, S_IWUSR | S_IRUSR);
+            DEBUGFS_ADD_FILE(set,     dir_sec, S_IWUSR | S_IRUSR);
+        }
+    }
+#endif /* CONFIG_ECRNX_RADAR */
+    return 0;
+
+err:
+    ecrnx_dbgfs_unregister(ecrnx_hw);
+    return -ENOMEM;
+}
+
+void ecrnx_dbgfs_unregister(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_debugfs *ecrnx_debugfs = &ecrnx_hw->debugfs;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    struct ecrnx_rc_config_save *cfg, *next;
+    list_for_each_entry_safe(cfg, next, &ecrnx_debugfs->rc_config_save, list) {
+        list_del(&cfg->list);
+        kfree(cfg);
+    }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+    if (!ecrnx_hw->debugfs.dir)
+        return;
+
+    spin_lock_bh(&ecrnx_debugfs->umh_lock);
+    ecrnx_debugfs->unregistering = true;
+    spin_unlock_bh(&ecrnx_debugfs->umh_lock);
+    ecrnx_wait_um_helper(ecrnx_hw);
+#if CONFIG_ECRNX_DBGFS_FW_TRACE
+    ecrnx_fw_trace_deinit(&ecrnx_hw->debugfs.fw_trace);
+#endif
+#ifdef CONFIG_ECRNX_FULLMAC
+    flush_work(&ecrnx_debugfs->sta_work);
+#endif
+    debugfs_remove_recursive(ecrnx_hw->debugfs.dir);
+    ecrnx_hw->debugfs.dir = NULL;
+}
+
diff --git a/drivers/net/wireless/eswin/ecrnx_debugfs.h b/drivers/net/wireless/eswin/ecrnx_debugfs.h
new file mode 100644 (file)
index 0000000..da0338e
--- /dev/null
@@ -0,0 +1,198 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_debugfs.h
+ *
+ * @brief Miscellaneous utility function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+
+#ifndef _ECRNX_DEBUGFS_H_
+#define _ECRNX_DEBUGFS_H_
+
+#include <linux/workqueue.h>
+#include <linux/if_ether.h>
+#include "ecrnx_fw_trace.h"
+
+struct ecrnx_hw;
+struct ecrnx_sta;
+
+#define DEBUGFS_ADD_FILE(name, parent, mode) do {                  \
+        struct dentry *__tmp;                                      \
+        __tmp = debugfs_create_file(#name, mode, parent, ecrnx_hw,  \
+                                    &ecrnx_dbgfs_##name##_ops);     \
+        if (IS_ERR_OR_NULL(__tmp))                                 \
+            goto err;                                              \
+    } while (0)
+
+#define DEBUGFS_ADD_BOOL(name, parent, ptr) do {                            \
+        struct dentry *__tmp;                                               \
+        __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, parent, ptr); \
+        if (IS_ERR_OR_NULL(__tmp))                                          \
+            goto err;                                                       \
+    } while (0)
+
+#define DEBUGFS_ADD_X64(name, parent, ptr) do {                         \
+        debugfs_create_x64(#name, S_IWUSR | S_IRUSR,parent, ptr);       \
+    } while (0)
+
+#define DEBUGFS_ADD_U64(name, parent, ptr, mode) do {           \
+        debugfs_create_u64(#name, mode, parent, ptr);           \
+    } while (0)
+
+#define DEBUGFS_ADD_X32(name, parent, ptr) do {                         \
+        debugfs_create_x32(#name, S_IWUSR | S_IRUSR, parent, ptr);      \
+    } while (0)
+
+#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do {           \
+        debugfs_create_u32(#name, mode, parent, ptr);           \
+    } while (0)
+
+
+/* file operation */
+#define DEBUGFS_READ_FUNC(name)                                         \
+    static ssize_t ecrnx_dbgfs_##name##_read(struct file *file,          \
+                                            char __user *user_buf,      \
+                                            size_t count, loff_t *ppos);
+
+#define DEBUGFS_WRITE_FUNC(name)                                         \
+    static ssize_t ecrnx_dbgfs_##name##_write(struct file *file,          \
+                                             const char __user *user_buf,\
+                                             size_t count, loff_t *ppos);
+
+#define DEBUGFS_OPEN_FUNC(name)                              \
+    static int ecrnx_dbgfs_##name##_open(struct inode *inode, \
+                                        struct file *file);
+
+#define DEBUGFS_RELEASE_FUNC(name)                              \
+    static int ecrnx_dbgfs_##name##_release(struct inode *inode, \
+                                           struct file *file);
+
+#define DEBUGFS_READ_FILE_OPS(name)                             \
+    DEBUGFS_READ_FUNC(name);                                    \
+static const struct file_operations ecrnx_dbgfs_##name##_ops = { \
+    .read   = ecrnx_dbgfs_##name##_read,                         \
+    .open   = simple_open,                                      \
+    .llseek = generic_file_llseek,                              \
+};
+
+#define DEBUGFS_WRITE_FILE_OPS(name)                            \
+    DEBUGFS_WRITE_FUNC(name);                                   \
+static const struct file_operations ecrnx_dbgfs_##name##_ops = { \
+    .write  = ecrnx_dbgfs_##name##_write,                        \
+    .open   = simple_open,                                      \
+    .llseek = generic_file_llseek,                              \
+};
+
+#define DEBUGFS_READ_WRITE_FILE_OPS(name)                       \
+    DEBUGFS_READ_FUNC(name);                                    \
+    DEBUGFS_WRITE_FUNC(name);                                   \
+static const struct file_operations ecrnx_dbgfs_##name##_ops = { \
+    .write  = ecrnx_dbgfs_##name##_write,                        \
+    .read   = ecrnx_dbgfs_##name##_read,                         \
+    .open   = simple_open,                                      \
+    .llseek = generic_file_llseek,                              \
+};
+
+#define DEBUGFS_READ_WRITE_OPEN_RELEASE_FILE_OPS(name)              \
+    DEBUGFS_READ_FUNC(name);                                        \
+    DEBUGFS_WRITE_FUNC(name);                                       \
+    DEBUGFS_OPEN_FUNC(name);                                        \
+    DEBUGFS_RELEASE_FUNC(name);                                     \
+static const struct file_operations ecrnx_dbgfs_##name##_ops = {     \
+    .write   = ecrnx_dbgfs_##name##_write,                           \
+    .read    = ecrnx_dbgfs_##name##_read,                            \
+    .open    = ecrnx_dbgfs_##name##_open,                            \
+    .release = ecrnx_dbgfs_##name##_release,                         \
+    .llseek  = generic_file_llseek,                                 \
+};
+
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+
+struct ecrnx_debugfs {
+    unsigned long long rateidx;
+    struct dentry *dir;
+    struct dentry *dir_stas;
+    bool trace_prst;
+
+    char helper_cmd[64];
+    struct work_struct helper_work;
+    bool helper_scheduled;
+    spinlock_t umh_lock;
+    bool unregistering;
+
+#ifndef CONFIG_ECRNX_FHOST
+    struct ecrnx_fw_trace fw_trace;
+#endif /* CONFIG_ECRNX_FHOST */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    struct work_struct sta_work;
+    struct dentry *dir_sta[NX_REMOTE_STA_MAX];
+    uint8_t sta_idx;
+    struct dentry *dir_rc;
+    struct dentry *dir_rc_sta[NX_REMOTE_STA_MAX];
+    int rc_config[NX_REMOTE_STA_MAX];
+    struct list_head rc_config_save;
+    struct dentry *dir_twt;
+    struct dentry *dir_twt_sta[NX_REMOTE_STA_MAX];
+#endif
+};
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+// Max duration in msecs to save rate config for a sta after disconnection
+#define RC_CONFIG_DUR 600000
+
+struct ecrnx_rc_config_save {
+    struct list_head list;
+    unsigned long timestamp;
+    int rate;
+    u8 mac_addr[ETH_ALEN];
+};
+#endif
+
+int ecrnx_dbgfs_register(struct ecrnx_hw *ecrnx_hw, const char *name);
+void ecrnx_dbgfs_unregister(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_um_helper(struct ecrnx_debugfs *ecrnx_debugfs, const char *cmd);
+int ecrnx_trigger_um_helper(struct ecrnx_debugfs *ecrnx_debugfs);
+void ecrnx_wait_um_helper(struct ecrnx_hw *ecrnx_hw);
+#ifdef CONFIG_ECRNX_FULLMAC
+void ecrnx_dbgfs_register_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta);
+void ecrnx_dbgfs_unregister_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta);
+#endif
+
+int ecrnx_dbgfs_register_fw_dump(struct ecrnx_hw *ecrnx_hw,
+                                struct dentry *dir_drv,
+                                struct dentry *dir_diags);
+void ecrnx_dbgfs_trigger_fw_dump(struct ecrnx_hw *ecrnx_hw, char *reason);
+
+void ecrnx_fw_trace_dump(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_fw_trace_reset(struct ecrnx_hw *ecrnx_hw);
+
+#else
+
+struct ecrnx_debugfs {
+};
+
+static inline int ecrnx_dbgfs_register(struct ecrnx_hw *ecrnx_hw, const char *name) { return 0; }
+static inline void ecrnx_dbgfs_unregister(struct ecrnx_hw *ecrnx_hw) {}
+static inline int ecrnx_um_helper(struct ecrnx_debugfs *ecrnx_debugfs, const char *cmd) { return 0; }
+static inline int ecrnx_trigger_um_helper(struct ecrnx_debugfs *ecrnx_debugfs) {return 0;}
+static inline void ecrnx_wait_um_helper(struct ecrnx_hw *ecrnx_hw) {}
+#ifdef CONFIG_ECRNX_FULLMAC
+static inline void ecrnx_dbgfs_register_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta)  {}
+static inline void ecrnx_dbgfs_unregister_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta)  {}
+#endif
+
+static inline void ecrnx_fw_trace_dump(struct ecrnx_hw *ecrnx_hw) {}
+static inline void ecrnx_fw_trace_reset(struct ecrnx_hw *ecrnx_hw) {}
+
+#endif /* CONFIG_ECRNX_DEBUGFS */
+
+
+#endif /* _ECRNX_DEBUGFS_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_events.h b/drivers/net/wireless/eswin/ecrnx_events.h
new file mode 100644 (file)
index 0000000..32f19f2
--- /dev/null
@@ -0,0 +1,1308 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_events.h
+ *
+ * @brief Trace events definition
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM ecrnx
+
+#if !defined(_ECRNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _ECRNX_EVENTS_H
+
+#include <linux/tracepoint.h>
+#ifndef CONFIG_ECRNX_FHOST
+#include "ecrnx_tx.h"
+#endif
+#include "ecrnx_compat.h"
+
+/*****************************************************************************
+ * TRACE function for MGMT TX (FULLMAC)
+ ****************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+#include "linux/ieee80211.h"
+#if defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS)
+#include <linux/trace_seq.h>
+
+/* P2P Public Action Frames Definitions (see WiFi P2P Technical Specification, section 4.2.8) */
+/* IEEE 802.11 Public Action Usage Category - Define P2P public action frames */
+#define MGMT_ACTION_PUBLIC_CAT              (0x04)
+/* Offset of OUI Subtype field in P2P Action Frame format */
+#define MGMT_ACTION_OUI_SUBTYPE_OFFSET      (6)
+/* P2P Public Action Frame Types */
+enum p2p_action_type {
+    P2P_ACTION_GO_NEG_REQ   = 0,    /* GO Negociation Request */
+    P2P_ACTION_GO_NEG_RSP,          /* GO Negociation Response */
+    P2P_ACTION_GO_NEG_CFM,          /* GO Negociation Confirmation */
+    P2P_ACTION_INVIT_REQ,           /* P2P Invitation Request */
+    P2P_ACTION_INVIT_RSP,           /* P2P Invitation Response */
+    P2P_ACTION_DEV_DISC_REQ,        /* Device Discoverability Request */
+    P2P_ACTION_DEV_DISC_RSP,        /* Device Discoverability Response */
+    P2P_ACTION_PROV_DISC_REQ,       /* Provision Discovery Request */
+    P2P_ACTION_PROV_DISC_RSP,       /* Provision Discovery Response */
+};
+
+const char *ftrace_print_mgmt_info(struct trace_seq *p, u16 frame_control, u8 cat, u8 type, u8 p2p) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+    switch (frame_control & IEEE80211_FCTL_STYPE) {
+        case (IEEE80211_STYPE_ASSOC_REQ): trace_seq_printf(p, "Association Request"); break;
+        case (IEEE80211_STYPE_ASSOC_RESP): trace_seq_printf(p, "Association Response"); break;
+        case (IEEE80211_STYPE_REASSOC_REQ): trace_seq_printf(p, "Reassociation Request"); break;
+        case (IEEE80211_STYPE_REASSOC_RESP): trace_seq_printf(p, "Reassociation Response"); break;
+        case (IEEE80211_STYPE_PROBE_REQ): trace_seq_printf(p, "Probe Request"); break;
+        case (IEEE80211_STYPE_PROBE_RESP): trace_seq_printf(p, "Probe Response"); break;
+        case (IEEE80211_STYPE_BEACON): trace_seq_printf(p, "Beacon"); break;
+        case (IEEE80211_STYPE_ATIM): trace_seq_printf(p, "ATIM"); break;
+        case (IEEE80211_STYPE_DISASSOC): trace_seq_printf(p, "Disassociation"); break;
+        case (IEEE80211_STYPE_AUTH): trace_seq_printf(p, "Authentication"); break;
+        case (IEEE80211_STYPE_DEAUTH): trace_seq_printf(p, "Deauthentication"); break;
+        case (IEEE80211_STYPE_ACTION):
+            trace_seq_printf(p, "Action");
+            if (cat == MGMT_ACTION_PUBLIC_CAT && type == 0x9)
+                switch (p2p) {
+                    case (P2P_ACTION_GO_NEG_REQ): trace_seq_printf(p, ": GO Negociation Request"); break;
+                    case (P2P_ACTION_GO_NEG_RSP): trace_seq_printf(p, ": GO Negociation Response"); break;
+                    case (P2P_ACTION_GO_NEG_CFM): trace_seq_printf(p, ": GO Negociation Confirmation"); break;
+                    case (P2P_ACTION_INVIT_REQ): trace_seq_printf(p, ": P2P Invitation Request"); break;
+                    case (P2P_ACTION_INVIT_RSP): trace_seq_printf(p, ": P2P Invitation Response"); break;
+                    case (P2P_ACTION_DEV_DISC_REQ): trace_seq_printf(p, ": Device Discoverability Request"); break;
+                    case (P2P_ACTION_DEV_DISC_RSP): trace_seq_printf(p, ": Device Discoverability Response"); break;
+                    case (P2P_ACTION_PROV_DISC_REQ): trace_seq_printf(p, ": Provision Discovery Request"); break;
+                    case (P2P_ACTION_PROV_DISC_RSP): trace_seq_printf(p, ": Provision Discovery Response"); break;
+                    default: trace_seq_printf(p, "Unknown p2p %d", p2p); break;
+                }
+            else {
+                switch (cat) {
+                    case 0: trace_seq_printf(p, ":Spectrum %d", type); break;
+                    case 1: trace_seq_printf(p, ":QOS %d", type); break;
+                    case 2: trace_seq_printf(p, ":DLS %d", type); break;
+                    case 3: trace_seq_printf(p, ":BA %d", type); break;
+                    case 4: trace_seq_printf(p, ":Public %d", type); break;
+                    case 5: trace_seq_printf(p, ":Radio Measure %d", type); break;
+                    case 6: trace_seq_printf(p, ":Fast BSS %d", type); break;
+                    case 7: trace_seq_printf(p, ":HT Action %d", type); break;
+                    case 8: trace_seq_printf(p, ":SA Query %d", type); break;
+                    case 9: trace_seq_printf(p, ":Protected Public %d", type); break;
+                    case 10: trace_seq_printf(p, ":WNM %d", type); break;
+                    case 11: trace_seq_printf(p, ":Unprotected WNM %d", type); break;
+                    case 12: trace_seq_printf(p, ":TDLS %d", type); break;
+                    case 13: trace_seq_printf(p, ":Mesh %d", type); break;
+                    case 14: trace_seq_printf(p, ":MultiHop %d", type); break;
+                    case 15: trace_seq_printf(p, ":Self Protected %d", type); break;
+                    case 126: trace_seq_printf(p, ":Vendor protected"); break;
+                    case 127: trace_seq_printf(p, ":Vendor"); break;
+                    default: trace_seq_printf(p, ":Unknown category %d", cat); break;
+                }
+            }
+            break;
+        default: trace_seq_printf(p, "Unknown subtype %d", frame_control & IEEE80211_FCTL_STYPE); break;
+    }
+
+    trace_seq_putc(p, 0);
+
+    return ret;
+}
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS) */
+
+#undef __print_mgmt_info
+#define __print_mgmt_info(frame_control, cat, type, p2p) ftrace_print_mgmt_info(p, frame_control, cat, type, p2p)
+
+TRACE_EVENT(
+    roc,
+    TP_PROTO(u8 vif_idx, u16 freq, unsigned int duration),
+    TP_ARGS(vif_idx, freq, duration),
+    TP_STRUCT__entry(
+        __field(u8, vif_idx)
+        __field(u16, freq)
+        __field(unsigned int, duration)
+                     ),
+    TP_fast_assign(
+        __entry->vif_idx = vif_idx;
+        __entry->freq = freq;
+        __entry->duration = duration;
+                   ),
+    TP_printk("f=%d vif=%d dur=%d",
+            __entry->freq, __entry->vif_idx, __entry->duration)
+);
+
+TRACE_EVENT(
+    cancel_roc,
+    TP_PROTO(u8 vif_idx),
+    TP_ARGS(vif_idx),
+    TP_STRUCT__entry(
+        __field(u8, vif_idx)
+                     ),
+    TP_fast_assign(
+        __entry->vif_idx = vif_idx;
+                   ),
+    TP_printk("vif=%d", __entry->vif_idx)
+);
+
+TRACE_EVENT(
+    roc_exp,
+    TP_PROTO(u8 vif_idx),
+    TP_ARGS(vif_idx),
+    TP_STRUCT__entry(
+        __field(u8, vif_idx)
+                     ),
+    TP_fast_assign(
+        __entry->vif_idx = vif_idx;
+                   ),
+    TP_printk("vif=%d", __entry->vif_idx)
+);
+
+TRACE_EVENT(
+    switch_roc,
+    TP_PROTO(u8 vif_idx),
+    TP_ARGS(vif_idx),
+    TP_STRUCT__entry(
+        __field(u8, vif_idx)
+                     ),
+    TP_fast_assign(
+        __entry->vif_idx = vif_idx;
+                   ),
+    TP_printk("vif=%d", __entry->vif_idx)
+);
+
+DECLARE_EVENT_CLASS(
+    mgmt_template,
+    TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+    TP_ARGS(freq, vif_idx, sta_idx, mgmt),
+    TP_STRUCT__entry(
+        __field(u16, freq)
+        __field(u8, vif_idx)
+        __field(u8, sta_idx)
+        __field(u16, frame_control)
+        __field(u8, action_cat)
+        __field(u8, action_type)
+        __field(u8, action_p2p)
+                     ),
+    TP_fast_assign(
+        __entry->freq = freq;
+        __entry->vif_idx = vif_idx;
+        __entry->sta_idx = sta_idx;
+        __entry->frame_control = mgmt->frame_control;
+        __entry->action_cat = mgmt->u.action.category;
+        __entry->action_type = mgmt->u.action.u.wme_action.action_code;
+        __entry->action_p2p = *((u8 *)&mgmt->u.action.category
+                                 + MGMT_ACTION_OUI_SUBTYPE_OFFSET);
+                   ),
+    TP_printk("f=%d vif=%d sta=%d -> %s",
+            __entry->freq, __entry->vif_idx, __entry->sta_idx,
+              __print_mgmt_info(__entry->frame_control, __entry->action_cat,
+                                __entry->action_type, __entry->action_p2p))
+);
+
+DEFINE_EVENT(mgmt_template, mgmt_tx,
+             TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+             TP_ARGS(freq, vif_idx, sta_idx, mgmt));
+
+DEFINE_EVENT(mgmt_template, mgmt_rx,
+             TP_PROTO(u16 freq, u8 vif_idx, u8 sta_idx, struct ieee80211_mgmt *mgmt),
+             TP_ARGS(freq, vif_idx, sta_idx, mgmt));
+
+TRACE_EVENT(
+    mgmt_cfm,
+    TP_PROTO(u8 vif_idx, u8 sta_idx, bool acked),
+    TP_ARGS(vif_idx, sta_idx, acked),
+    TP_STRUCT__entry(
+        __field(u8, vif_idx)
+        __field(u8, sta_idx)
+        __field(bool, acked)
+                     ),
+    TP_fast_assign(
+        __entry->vif_idx = vif_idx;
+        __entry->sta_idx = sta_idx;
+        __entry->acked = acked;
+                   ),
+    TP_printk("vif=%d sta=%d ack=%d",
+            __entry->vif_idx, __entry->sta_idx, __entry->acked)
+);
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/*****************************************************************************
+ * TRACE function for TXQ
+ ****************************************************************************/
+#ifndef CONFIG_ECRNX_FHOST
+#if defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS)
+
+#include <linux/trace_seq.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+#include <linux/trace_events.h>
+#else
+#include <linux/ftrace_event.h>
+#endif
+
+const char *
+ftrace_print_txq(struct trace_seq *p, int txq_idx) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+    if (txq_idx == TXQ_INACTIVE) {
+        trace_seq_printf(p, "[INACTIVE]");
+    } else if (txq_idx < NX_FIRST_VIF_TXQ_IDX) {
+        trace_seq_printf(p, "[STA %d/%d]",
+                         txq_idx / NX_NB_TXQ_PER_STA,
+                         txq_idx % NX_NB_TXQ_PER_STA);
+#ifdef CONFIG_ECRNX_FULLMAC
+    } else if (txq_idx < NX_FIRST_UNK_TXQ_IDX) {
+        trace_seq_printf(p, "[BC/MC %d]",
+                         txq_idx - NX_FIRST_BCMC_TXQ_IDX);
+    } else if (txq_idx < NX_OFF_CHAN_TXQ_IDX) {
+        trace_seq_printf(p, "[UNKNOWN %d]",
+                         txq_idx - NX_FIRST_UNK_TXQ_IDX);
+    } else if (txq_idx == NX_OFF_CHAN_TXQ_IDX) {
+        trace_seq_printf(p, "[OFFCHAN]");
+#else
+    } else if (txq_idx < NX_NB_TXQ) {
+        txq_idx -= NX_FIRST_VIF_TXQ_IDX;
+        trace_seq_printf(p, "[VIF %d/%d]",
+                         txq_idx / NX_NB_TXQ_PER_VIF,
+                         txq_idx % NX_NB_TXQ_PER_VIF);
+#endif
+    } else {
+        trace_seq_printf(p, "[ERROR %d]", txq_idx);
+    }
+
+    trace_seq_putc(p, 0);
+
+    return ret;
+}
+
+const char *
+ftrace_print_sta(struct trace_seq *p, int sta_idx) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+    if (sta_idx < NX_REMOTE_STA_MAX) {
+        trace_seq_printf(p, "[STA %d]", sta_idx);
+    } else {
+        trace_seq_printf(p, "[BC/MC %d]", sta_idx - NX_REMOTE_STA_MAX);
+    }
+
+    trace_seq_putc(p, 0);
+
+    return ret;
+}
+
+const char *
+ftrace_print_hwq(struct trace_seq *p, int hwq_idx) {
+
+    static const struct trace_print_flags symbols[] =
+        {{ECRNX_HWQ_BK, "BK"},
+         {ECRNX_HWQ_BE, "BE"},
+         {ECRNX_HWQ_VI, "VI"},
+         {ECRNX_HWQ_VO, "VO"},
+#ifdef CONFIG_ECRNX_FULLMAC
+         {ECRNX_HWQ_BCMC, "BCMC"},
+#else
+         {ECRNX_HWQ_BCN, "BCN"},
+#endif
+         { -1, NULL }};
+    return trace_print_symbols_seq(p, hwq_idx, symbols);
+}
+
+const char *
+ftrace_print_hwq_cred(struct trace_seq *p, u8 *cred) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+#if CONFIG_USER_MAX == 1
+    trace_seq_printf(p, "%d", cred[0]);
+#else
+    int i;
+
+    for (i = 0; i < CONFIG_USER_MAX - 1; i++)
+        trace_seq_printf(p, "%d-", cred[i]);
+    trace_seq_printf(p, "%d", cred[i]);
+#endif
+
+    trace_seq_putc(p, 0);
+    return ret;
+}
+
+const char *
+ftrace_print_mu_info(struct trace_seq *p, u8 mu_info) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+    if (mu_info)
+        trace_seq_printf(p, "MU: %d-%d", (mu_info & 0x3f), (mu_info >> 6));
+
+    trace_seq_putc(p, 0);
+    return ret;
+}
+
+const char *
+ftrace_print_mu_group(struct trace_seq *p, int nb_user, u8 *users) {
+    const char *ret = trace_seq_buffer_ptr(p);
+    int i;
+
+    if (users[0] != 0xff)
+        trace_seq_printf(p, "(%d", users[0]);
+    else
+        trace_seq_printf(p, "(-");
+    for (i = 1; i < CONFIG_USER_MAX ; i++) {
+        if (users[i] != 0xff)
+            trace_seq_printf(p, ",%d", users[i]);
+        else
+            trace_seq_printf(p, ",-");
+    }
+
+    trace_seq_printf(p, ")");
+    trace_seq_putc(p, 0);
+    return ret;
+}
+
+const char *
+ftrace_print_amsdu(struct trace_seq *p, u16 nb_pkt) {
+    const char *ret = trace_seq_buffer_ptr(p);
+
+    if (nb_pkt > 1)
+        trace_seq_printf(p, "(AMSDU %d)", nb_pkt);
+
+    trace_seq_putc(p, 0);
+    return ret;
+}
+#endif /* defined(CONFIG_TRACEPOINTS) && defined(CREATE_TRACE_POINTS) */
+
+#undef __print_txq
+#define __print_txq(txq_idx) ftrace_print_txq(p, txq_idx)
+
+#undef __print_sta
+#define __print_sta(sta_idx) ftrace_print_sta(p, sta_idx)
+
+#undef __print_hwq
+#define __print_hwq(hwq) ftrace_print_hwq(p, hwq)
+
+#undef __print_hwq_cred
+#define __print_hwq_cred(cred) ftrace_print_hwq_cred(p, cred)
+
+#undef __print_mu_info
+#define __print_mu_info(mu_info) ftrace_print_mu_info(p, mu_info)
+
+#undef __print_mu_group
+#define __print_mu_group(nb, users) ftrace_print_mu_group(p, nb, users)
+
+#undef __print_amsdu
+#define __print_amsdu(nb_pkt) ftrace_print_amsdu(p, nb_pkt)
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+TRACE_EVENT(
+    txq_select,
+    TP_PROTO(int txq_idx, u16 pkt_ready_up, struct sk_buff *skb),
+    TP_ARGS(txq_idx, pkt_ready_up, skb),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u16, pkt_ready)
+        __field(struct sk_buff *, skb)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq_idx;
+        __entry->pkt_ready = pkt_ready_up;
+        __entry->skb = skb;
+                   ),
+    TP_printk("%s pkt_ready_up=%d skb=%p", __print_txq(__entry->txq_idx),
+              __entry->pkt_ready, __entry->skb)
+);
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+DECLARE_EVENT_CLASS(
+    hwq_template,
+    TP_PROTO(u8 hwq_idx),
+    TP_ARGS(hwq_idx),
+    TP_STRUCT__entry(
+        __field(u8, hwq_idx)
+                     ),
+    TP_fast_assign(
+        __entry->hwq_idx = hwq_idx;
+                   ),
+    TP_printk("%s", __print_hwq(__entry->hwq_idx))
+);
+
+DEFINE_EVENT(hwq_template, hwq_flowctrl_stop,
+             TP_PROTO(u8 hwq_idx),
+             TP_ARGS(hwq_idx));
+
+DEFINE_EVENT(hwq_template, hwq_flowctrl_start,
+             TP_PROTO(u8 hwq_idx),
+             TP_ARGS(hwq_idx));
+
+
+DECLARE_EVENT_CLASS(
+    txq_template,
+    TP_PROTO(struct ecrnx_txq *txq),
+    TP_ARGS(txq),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+                   ),
+    TP_printk("%s", __print_txq(__entry->txq_idx))
+);
+
+DEFINE_EVENT(txq_template, txq_add_to_hw,
+             TP_PROTO(struct ecrnx_txq *txq),
+             TP_ARGS(txq));
+
+DEFINE_EVENT(txq_template, txq_del_from_hw,
+             TP_PROTO(struct ecrnx_txq *txq),
+             TP_ARGS(txq));
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+DEFINE_EVENT(txq_template, txq_flowctrl_stop,
+             TP_PROTO(struct ecrnx_txq *txq),
+             TP_ARGS(txq));
+
+DEFINE_EVENT(txq_template, txq_flowctrl_restart,
+             TP_PROTO(struct ecrnx_txq *txq),
+             TP_ARGS(txq));
+
+#endif  /* CONFIG_ECRNX_FULLMAC */
+
+TRACE_EVENT(
+    process_txq,
+    TP_PROTO(struct ecrnx_txq *txq),
+    TP_ARGS(txq),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u16, len)
+        __field(u16, len_retry)
+        __field(s8, credit)
+        #ifdef CONFIG_ECRNX_FULLMAC
+        __field(u16, limit)
+        #endif /* CONFIG_ECRNX_FULLMAC*/
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->len = skb_queue_len(&txq->sk_list);
+        #ifdef CONFIG_MAC80211_TXQ
+        __entry->len += txq->nb_ready_mac80211;
+        #endif
+        __entry->len_retry = txq->nb_retry;
+        __entry->credit = txq->credits;
+        #ifdef CONFIG_ECRNX_FULLMAC
+        __entry->limit = txq->push_limit;
+        #endif /* CONFIG_ECRNX_FULLMAC*/
+                   ),
+
+    #ifdef CONFIG_ECRNX_FULLMAC
+    TP_printk("%s txq_credits=%d, len=%d, retry_len=%d, push_limit=%d",
+              __print_txq(__entry->txq_idx), __entry->credit,
+              __entry->len, __entry->len_retry, __entry->limit)
+    #else
+    TP_printk("%s txq_credits=%d, len=%d, retry_len=%d",
+              __print_txq(__entry->txq_idx), __entry->credit,
+              __entry->len, __entry->len_retry)
+    #endif /* CONFIG_ECRNX_FULLMAC*/
+);
+
+DECLARE_EVENT_CLASS(
+    txq_reason_template,
+    TP_PROTO(struct ecrnx_txq *txq, u16 reason),
+    TP_ARGS(txq, reason),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u16, reason)
+        __field(u16, status)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->reason = reason;
+        __entry->status = txq->status;
+                   ),
+    TP_printk("%s reason=%s status=%s",
+              __print_txq(__entry->txq_idx),
+              __print_symbolic(__entry->reason,
+                               {ECRNX_TXQ_STOP_FULL, "FULL"},
+                               {ECRNX_TXQ_STOP_CSA, "CSA"},
+                               {ECRNX_TXQ_STOP_STA_PS, "PS"},
+                               {ECRNX_TXQ_STOP_VIF_PS, "VPS"},
+                               {ECRNX_TXQ_STOP_CHAN, "CHAN"},
+                               {ECRNX_TXQ_STOP_MU_POS, "MU"}),
+              __print_flags(__entry->status, "|",
+                            {ECRNX_TXQ_IN_HWQ_LIST, "IN LIST"},
+                            {ECRNX_TXQ_STOP_FULL, "FULL"},
+                            {ECRNX_TXQ_STOP_CSA, "CSA"},
+                            {ECRNX_TXQ_STOP_STA_PS, "PS"},
+                            {ECRNX_TXQ_STOP_VIF_PS, "VPS"},
+                            {ECRNX_TXQ_STOP_CHAN, "CHAN"},
+                            {ECRNX_TXQ_STOP_MU_POS, "MU"},
+                            {ECRNX_TXQ_NDEV_FLOW_CTRL, "FLW_CTRL"}))
+);
+
+DEFINE_EVENT(txq_reason_template, txq_start,
+             TP_PROTO(struct ecrnx_txq *txq, u16 reason),
+             TP_ARGS(txq, reason));
+
+DEFINE_EVENT(txq_reason_template, txq_stop,
+             TP_PROTO(struct ecrnx_txq *txq, u16 reason),
+             TP_ARGS(txq, reason));
+
+
+TRACE_EVENT(
+    push_desc,
+    TP_PROTO(struct sk_buff *skb, struct ecrnx_sw_txhdr *sw_txhdr, int push_flags),
+
+    TP_ARGS(skb, sw_txhdr, push_flags),
+
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(unsigned int, len)
+        __field(u16, tx_queue)
+        __field(u8, hw_queue)
+        __field(u8, push_flag)
+        __field(u32, flag)
+        __field(s8, txq_cred)
+        __field(u8, hwq_cred)
+        __field(u8, txq_length)
+#ifdef CONFIG_ECRNX_SOFTMAC
+        __field(u16, sn)
+#endif
+        __field(u16, pkt_cnt)
+        __field(u8, mu_info)
+                     ),
+    TP_fast_assign(
+        __entry->skb = skb;
+        __entry->tx_queue = sw_txhdr->txq->idx;
+        __entry->push_flag = push_flags;
+        __entry->hw_queue = sw_txhdr->txq->hwq->id;
+        __entry->txq_cred = sw_txhdr->txq->credits;
+        __entry->hwq_cred = sw_txhdr->txq->hwq->credits[ECRNX_TXQ_POS_ID(sw_txhdr->txq)];
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+        __entry->pkt_cnt =  sw_txhdr->desc.host.packet_cnt;
+#endif
+        __entry->txq_length = skb_queue_len(&sw_txhdr->txq->sk_list);
+#ifdef CONFIG_ECRNX_FULLMAC
+        __entry->flag = sw_txhdr->desc.host.flags;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+        if (sw_txhdr->amsdu.len)
+            __entry->len = sw_txhdr->amsdu.len;
+        else
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+            __entry->len = sw_txhdr->desc.host.packet_len[0];
+#else
+        __entry->len = sw_txhdr->desc.host.packet_len;
+#endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
+
+#else /* !CONFIG_ECRNX_FULLMAC */
+        __entry->flag = sw_txhdr->desc.umac.flags;
+        __entry->len = sw_txhdr->frame_len;
+        __entry->sn = sw_txhdr->sn;
+#endif /* CONFIG_ECRNX_FULLMAC */
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+        __entry->mu_info = sw_txhdr->desc.host.mumimo_info;
+#else
+        __entry->mu_info = 0;
+#endif
+                   ),
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    TP_printk("%s skb=%p (len=%d) hw_queue=%s txq_length=%d cred_txq=%d cred_hwq=%d %s flag=%s %s%s%s",
+              __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
+              __print_hwq(__entry->hw_queue), __entry->txq_length,
+              __entry->txq_cred, __entry->hwq_cred,
+              __print_mu_info(__entry->mu_info),
+              __print_flags(__entry->flag, "|",
+                            {TXU_CNTRL_RETRY, "RETRY"},
+                            {TXU_CNTRL_MORE_DATA, "MOREDATA"},
+                            {TXU_CNTRL_MGMT, "MGMT"},
+                            {TXU_CNTRL_MGMT_NO_CCK, "NO_CCK"},
+                            {TXU_CNTRL_MGMT_ROBUST, "ROBUST"},
+                            {TXU_CNTRL_AMSDU, "AMSDU"},
+                            {TXU_CNTRL_USE_4ADDR, "4ADDR"},
+                            {TXU_CNTRL_EOSP, "EOSP"},
+                            {TXU_CNTRL_MESH_FWD, "MESH_FWD"},
+                            {TXU_CNTRL_TDLS, "TDLS"}),
+              (__entry->push_flag & ECRNX_PUSH_IMMEDIATE) ? "(IMMEDIATE)" : "",
+              (!(__entry->flag & TXU_CNTRL_RETRY) &&
+               (__entry->push_flag & ECRNX_PUSH_RETRY)) ? "(SW_RETRY)" : "",
+              __print_amsdu(__entry->pkt_cnt))
+#else
+    TP_printk("%s skb=%p (len=%d) hw_queue=%s txq_length=%d cred_txq=%d cred_hwq=%d %s flag=%x (%s) sn=%d %s",
+              __print_txq(__entry->tx_queue), __entry->skb, __entry->len,
+              __print_hwq(__entry->hw_queue), __entry->txq_length, __entry->txq_cred,
+              __entry->hwq_cred,
+              __print_mu_info(__entry->mu_info),
+              __entry->flag,
+              __print_flags(__entry->push_flag, "|",
+                            {ECRNX_PUSH_RETRY, "RETRY"},
+                            {ECRNX_PUSH_IMMEDIATE, "IMMEDIATE"}),
+              __entry->sn, __print_amsdu(__entry->pkt_cnt))
+#endif /* CONFIG_ECRNX_FULLMAC */
+);
+
+
+TRACE_EVENT(
+    txq_queue_skb,
+    TP_PROTO(struct sk_buff *skb, struct ecrnx_txq *txq, bool retry),
+    TP_ARGS(skb, txq, retry),
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(u16, txq_idx)
+        __field(s8, credit)
+        __field(u16, q_len)
+        __field(u16, q_len_retry)
+        __field(bool, retry)
+                     ),
+    TP_fast_assign(
+        __entry->skb = skb;
+        __entry->txq_idx = txq->idx;
+        __entry->credit = txq->credits;
+        __entry->q_len = skb_queue_len(&txq->sk_list);
+        __entry->q_len_retry = txq->nb_retry;
+        __entry->retry = retry;
+                   ),
+
+    TP_printk("%s skb=%p retry=%d txq_credits=%d queue_len=%d (retry = %d)",
+              __print_txq(__entry->txq_idx), __entry->skb, __entry->retry,
+              __entry->credit, __entry->q_len, __entry->q_len_retry)
+);
+TRACE_EVENT(
+    txq_drop_skb,
+    TP_PROTO(struct sk_buff *skb, struct ecrnx_txq *txq, unsigned long queued_time),
+    TP_ARGS(skb, txq, queued_time),
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(u16, txq_idx)
+        __field(unsigned long, queued_time)
+        __field(u16, q_len)
+        __field(u16, q_len_retry)
+                     ),
+    TP_fast_assign(
+        __entry->skb = skb;
+        __entry->txq_idx = txq->idx;
+        __entry->q_len = skb_queue_len(&txq->sk_list);
+        __entry->q_len_retry = txq->nb_retry;
+        __entry->queued_time = queued_time;
+                   ),
+    TP_printk("%s skb=%p time_queued=%dms queue_len=%d (retry = %d)",
+              __print_txq(__entry->txq_idx), __entry->skb,
+              jiffies_to_msecs(__entry->queued_time), __entry->q_len, __entry->q_len_retry)
+);
+
+#ifdef CONFIG_MAC80211_TXQ
+TRACE_EVENT(
+    txq_wake,
+    TP_PROTO(struct ecrnx_txq *txq),
+    TP_ARGS(txq),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u16, q_len)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->q_len = txq->nb_ready_mac80211;
+                   ),
+
+    TP_printk("%s mac80211_queue_len=%d", __print_txq(__entry->txq_idx), __entry->q_len)
+);
+
+TRACE_EVENT(
+    txq_drop,
+    TP_PROTO(struct ecrnx_txq *txq, unsigned long nb_drop),
+    TP_ARGS(txq, nb_drop),
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u16, nb_drop)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->nb_drop = nb_drop;
+                   ),
+
+    TP_printk("%s %u pkt have been dropped by codel in mac80211 txq",
+              __print_txq(__entry->txq_idx), __entry->nb_drop)
+);
+
+#endif
+
+
+DECLARE_EVENT_CLASS(
+    idx_template,
+    TP_PROTO(u16 idx),
+    TP_ARGS(idx),
+    TP_STRUCT__entry(
+        __field(u16, idx)
+                     ),
+    TP_fast_assign(
+        __entry->idx = idx;
+                   ),
+    TP_printk("idx=%d", __entry->idx)
+);
+
+
+DEFINE_EVENT(idx_template, txq_vif_start,
+             TP_PROTO(u16 idx),
+             TP_ARGS(idx));
+
+DEFINE_EVENT(idx_template, txq_vif_stop,
+             TP_PROTO(u16 idx),
+             TP_ARGS(idx));
+
+TRACE_EVENT(
+    process_hw_queue,
+    TP_PROTO(struct ecrnx_hwq *hwq),
+    TP_ARGS(hwq),
+    TP_STRUCT__entry(
+        __field(u16, hwq)
+        __array(u8, credits, CONFIG_USER_MAX)
+                     ),
+    TP_fast_assign(
+        int i;
+        __entry->hwq = hwq->id;
+        for (i=0; i < CONFIG_USER_MAX; i ++)
+            __entry->credits[i] = hwq->credits[i];
+                   ),
+    TP_printk("hw_queue=%s hw_credits=%s",
+              __print_hwq(__entry->hwq), __print_hwq_cred(__entry->credits))
+);
+
+DECLARE_EVENT_CLASS(
+    sta_idx_template,
+    TP_PROTO(u16 idx),
+    TP_ARGS(idx),
+    TP_STRUCT__entry(
+        __field(u16, idx)
+                     ),
+    TP_fast_assign(
+        __entry->idx = idx;
+                   ),
+    TP_printk("%s", __print_sta(__entry->idx))
+);
+
+DEFINE_EVENT(sta_idx_template, txq_sta_start,
+             TP_PROTO(u16 idx),
+             TP_ARGS(idx));
+
+DEFINE_EVENT(sta_idx_template, txq_sta_stop,
+             TP_PROTO(u16 idx),
+             TP_ARGS(idx));
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+DEFINE_EVENT(sta_idx_template, ps_disable,
+             TP_PROTO(u16 idx),
+             TP_ARGS(idx));
+
+#endif  /* CONFIG_ECRNX_FULLMAC */
+
+TRACE_EVENT(
+    skb_confirm,
+    TP_PROTO(struct sk_buff *skb, struct ecrnx_txq *txq, struct ecrnx_hwq *hwq,
+#ifdef CONFIG_ECRNX_FULLMAC
+             struct tx_cfm_tag *cfm
+#else
+             u8 cfm
+#endif
+             ),
+
+    TP_ARGS(skb, txq, hwq, cfm),
+
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(u16, txq_idx)
+        __field(u8, hw_queue)
+        __array(u8, hw_credit, CONFIG_USER_MAX)
+        __field(s8, sw_credit)
+        __field(s8, sw_credit_up)
+#ifdef CONFIG_ECRNX_FULLMAC
+        __field(u8, ampdu_size)
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+        __field(u16, amsdu)
+#endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
+        __field(u16, sn)
+#endif /* CONFIG_ECRNX_FULLMAC*/
+                     ),
+
+    TP_fast_assign(
+        int i;
+        __entry->skb = skb;
+        __entry->txq_idx = txq->idx;
+        __entry->hw_queue = hwq->id;
+        for (i = 0 ; i < CONFIG_USER_MAX ; i++)
+            __entry->hw_credit[i] = hwq->credits[i];
+        __entry->sw_credit = txq->credits;
+#if defined CONFIG_ECRNX_FULLMAC
+        __entry->sw_credit_up = cfm->credits;
+        __entry->ampdu_size = cfm->ampdu_size;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+        __entry->amsdu = cfm->amsdu_size;
+        __entry->sn = cfm->sn;
+#endif
+#else
+        __entry->sw_credit_up = cfm
+#endif /* CONFIG_ECRNX_FULLMAC */
+                   ),
+
+    TP_printk("%s skb=%p hw_queue=%s, hw_credits=%s, txq_credits=%d (+%d)"
+#ifdef CONFIG_ECRNX_FULLMAC
+              " sn=%u ampdu=%d"
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+              " amsdu=%u"
+#endif
+#endif
+              , __print_txq(__entry->txq_idx), __entry->skb,
+              __print_hwq(__entry->hw_queue),
+              __print_hwq_cred(__entry->hw_credit),
+               __entry->sw_credit, __entry->sw_credit_up
+#ifdef CONFIG_ECRNX_FULLMAC
+              , __entry->sn, __entry->ampdu_size
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+              , __entry->amsdu
+#endif
+#endif
+              )
+);
+
+TRACE_EVENT(
+    credit_update,
+    TP_PROTO(struct ecrnx_txq *txq, s8_l cred_up),
+
+    TP_ARGS(txq, cred_up),
+
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(u16, txq_idx)
+        __field(s8, sw_credit)
+        __field(s8, sw_credit_up)
+                     ),
+
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->sw_credit = txq->credits;
+        __entry->sw_credit_up = cred_up;
+                   ),
+
+    TP_printk("%s txq_credits=%d (%+d)", __print_txq(__entry->txq_idx),
+              __entry->sw_credit, __entry->sw_credit_up)
+)
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+DECLARE_EVENT_CLASS(
+    ps_template,
+    TP_PROTO(struct ecrnx_sta *sta),
+    TP_ARGS(sta),
+    TP_STRUCT__entry(
+        __field(u16, idx)
+        __field(u16, ready_ps)
+        __field(u16, sp_ps)
+        __field(u16, ready_uapsd)
+        __field(u16, sp_uapsd)
+                     ),
+    TP_fast_assign(
+        __entry->idx  = sta->sta_idx;
+        __entry->ready_ps = sta->ps.pkt_ready[LEGACY_PS_ID];
+        __entry->sp_ps = sta->ps.sp_cnt[LEGACY_PS_ID];
+        __entry->ready_uapsd = sta->ps.pkt_ready[UAPSD_ID];
+        __entry->sp_uapsd = sta->ps.sp_cnt[UAPSD_ID];
+                   ),
+
+    TP_printk("%s [PS] ready=%d sp=%d [UAPSD] ready=%d sp=%d",
+              __print_sta(__entry->idx), __entry->ready_ps, __entry->sp_ps,
+              __entry->ready_uapsd, __entry->sp_uapsd)
+);
+
+DEFINE_EVENT(ps_template, ps_queue,
+             TP_PROTO(struct ecrnx_sta *sta),
+             TP_ARGS(sta));
+
+DEFINE_EVENT(ps_template, ps_drop,
+             TP_PROTO(struct ecrnx_sta *sta),
+             TP_ARGS(sta));
+DEFINE_EVENT(ps_template, ps_push,
+             TP_PROTO(struct ecrnx_sta *sta),
+             TP_ARGS(sta));
+
+DEFINE_EVENT(ps_template, ps_enable,
+             TP_PROTO(struct ecrnx_sta *sta),
+             TP_ARGS(sta));
+
+TRACE_EVENT(
+    ps_traffic_update,
+    TP_PROTO(u16 sta_idx, u8 traffic, bool uapsd),
+
+    TP_ARGS(sta_idx, traffic, uapsd),
+
+    TP_STRUCT__entry(
+        __field(u16, sta_idx)
+        __field(u8, traffic)
+        __field(bool, uapsd)
+                     ),
+
+    TP_fast_assign(
+        __entry->sta_idx = sta_idx;
+        __entry->traffic = traffic;
+        __entry->uapsd = uapsd;
+                   ),
+
+    TP_printk("%s %s%s traffic available ", __print_sta(__entry->sta_idx),
+              __entry->traffic ? "" : "no more ",
+              __entry->uapsd ? "U-APSD" : "legacy PS")
+);
+
+TRACE_EVENT(
+    ps_traffic_req,
+    TP_PROTO(struct ecrnx_sta *sta, u16 pkt_req, u8 ps_id),
+    TP_ARGS(sta, pkt_req, ps_id),
+    TP_STRUCT__entry(
+        __field(u16, idx)
+        __field(u16, pkt_req)
+        __field(u8, ps_id)
+        __field(u16, ready)
+        __field(u16, sp)
+                     ),
+    TP_fast_assign(
+        __entry->idx  = sta->sta_idx;
+        __entry->pkt_req  = pkt_req;
+        __entry->ps_id  = ps_id;
+        __entry->ready = sta->ps.pkt_ready[ps_id];
+        __entry->sp = sta->ps.sp_cnt[ps_id];
+                   ),
+
+    TP_printk("%s %s traffic request %d pkt (ready=%d, sp=%d)",
+              __print_sta(__entry->idx),
+              __entry->ps_id == UAPSD_ID ? "U-APSD" : "legacy PS" ,
+              __entry->pkt_req, __entry->ready, __entry->sp)
+);
+
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+TRACE_EVENT(
+    amsdu_subframe,
+    TP_PROTO(struct ecrnx_sw_txhdr *sw_txhdr),
+    TP_ARGS(sw_txhdr),
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(u16, txq_idx)
+        __field(u8, nb)
+        __field(u32, len)
+                     ),
+    TP_fast_assign(
+        __entry->skb = sw_txhdr->skb;
+        __entry->nb = sw_txhdr->amsdu.nb;
+        __entry->len = sw_txhdr->amsdu.len;
+        __entry->txq_idx = sw_txhdr->txq->idx;
+                   ),
+
+    TP_printk("%s skb=%p %s nb_subframe=%d, len=%u",
+              __print_txq(__entry->txq_idx), __entry->skb,
+              (__entry->nb == 2) ? "Start new AMSDU" : "Add subframe",
+              __entry->nb, __entry->len)
+);
+TRACE_EVENT(
+    amsdu_dismantle,
+    TP_PROTO(struct ecrnx_sw_txhdr *sw_txhdr),
+    TP_ARGS(sw_txhdr),
+    TP_STRUCT__entry(
+        __field(struct sk_buff *, skb)
+        __field(u16, txq_idx)
+        __field(u8, nb)
+        __field(u32, len)
+                     ),
+    TP_fast_assign(
+        __entry->skb = sw_txhdr->skb;
+        __entry->nb = sw_txhdr->amsdu.nb;
+        __entry->len = sw_txhdr->amsdu.len;
+        __entry->txq_idx = sw_txhdr->txq->idx;
+                   ),
+    TP_printk("%s skb=%p nb_subframe=%d, len=%u",
+              __print_txq(__entry->txq_idx), __entry->skb,
+              __entry->nb, __entry->len)
+);
+TRACE_EVENT(
+    amsdu_len_update,
+    TP_PROTO(struct ecrnx_sta *sta, int amsdu_len),
+    TP_ARGS(sta, amsdu_len),
+    TP_STRUCT__entry(
+        __field(u8, sta_idx)
+        __field(u16, amsdu_len)
+                     ),
+    TP_fast_assign(
+        __entry->sta_idx = sta->sta_idx;
+        __entry->amsdu_len = amsdu_len;
+                   ),
+    TP_printk("[Sta %d] A-MSDU len = %d", __entry->sta_idx, __entry->amsdu_len)
+);
+#endif
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+TRACE_EVENT(
+    mu_group_update,
+    TP_PROTO(struct ecrnx_mu_group *group),
+    TP_ARGS(group),
+    TP_STRUCT__entry(
+        __field(u8, nb_user)
+        __field(u8, group_id)
+        __array(u8, users, CONFIG_USER_MAX)
+                     ),
+    TP_fast_assign(
+        int i;
+        __entry->nb_user = group->user_cnt;
+        for (i = 0; i < CONFIG_USER_MAX ; i++) {
+            if (group->users[i]) {
+                __entry->users[i] = group->users[i]->sta_idx;
+            } else {
+                __entry->users[i] = 0xff;
+            }
+        }
+
+        __entry->group_id = group->group_id;
+                   ),
+
+    TP_printk("Group-id = %d, Users = %s",
+              __entry->group_id,
+              __print_mu_group(__entry->nb_user, __entry->users))
+);
+
+TRACE_EVENT(
+    mu_group_delete,
+    TP_PROTO(int group_id),
+    TP_ARGS(group_id),
+    TP_STRUCT__entry(
+        __field(u8, group_id)
+                     ),
+    TP_fast_assign(
+        __entry->group_id = group_id;
+                   ),
+
+    TP_printk("Group-id = %d", __entry->group_id)
+);
+
+TRACE_EVENT(
+    mu_group_selection,
+    TP_PROTO(struct ecrnx_sta *sta, int group_id),
+    TP_ARGS(sta, group_id),
+    TP_STRUCT__entry(
+        __field(u8, sta_idx)
+        __field(u8, group_id)
+                     ),
+    TP_fast_assign(
+        __entry->sta_idx = sta->sta_idx;
+        __entry->group_id = group_id;
+                   ),
+
+    TP_printk("[Sta %d] Group-id = %d", __entry->sta_idx, __entry->group_id)
+);
+
+TRACE_EVENT(
+    txq_select_mu_group,
+    TP_PROTO(struct ecrnx_txq *txq, int group_id, int pos),
+
+    TP_ARGS(txq, group_id, pos),
+
+    TP_STRUCT__entry(
+        __field(u16, txq_idx)
+        __field(u8, group_id)
+        __field(u8, pos)
+                     ),
+    TP_fast_assign(
+        __entry->txq_idx = txq->idx;
+        __entry->group_id = group_id;
+        __entry->pos = pos;
+                   ),
+
+    TP_printk("%s: group=%d pos=%d", __print_txq(__entry->txq_idx),
+              __entry->group_id, __entry->pos)
+);
+
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+#endif /* ! CONFIG_ECRNX_FHOST */
+
+/*****************************************************************************
+ * TRACE functions for MESH
+ ****************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+DECLARE_EVENT_CLASS(
+    mesh_path_template,
+    TP_PROTO(struct ecrnx_mesh_path *mesh_path),
+    TP_ARGS(mesh_path),
+    TP_STRUCT__entry(
+        __field(u8, idx)
+        __field(u8, next_hop_sta)
+        __array(u8, tgt_mac, ETH_ALEN)
+                     ),
+
+    TP_fast_assign(
+        __entry->idx = mesh_path->path_idx;
+        memcpy(__entry->tgt_mac, &mesh_path->tgt_mac_addr, ETH_ALEN);
+        if (mesh_path->nhop_sta)
+            __entry->next_hop_sta = mesh_path->nhop_sta->sta_idx;
+        else
+            __entry->next_hop_sta = 0xff;
+                   ),
+
+    TP_printk("Mpath(%d): target=%pM next_hop=STA-%d",
+              __entry->idx, __entry->tgt_mac, __entry->next_hop_sta)
+);
+
+DEFINE_EVENT(mesh_path_template, mesh_create_path,
+             TP_PROTO(struct ecrnx_mesh_path *mesh_path),
+             TP_ARGS(mesh_path));
+
+DEFINE_EVENT(mesh_path_template, mesh_delete_path,
+             TP_PROTO(struct ecrnx_mesh_path *mesh_path),
+             TP_ARGS(mesh_path));
+
+DEFINE_EVENT(mesh_path_template, mesh_update_path,
+             TP_PROTO(struct ecrnx_mesh_path *mesh_path),
+             TP_ARGS(mesh_path));
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/*****************************************************************************
+ * TRACE functions for RADAR
+ ****************************************************************************/
+#ifdef CONFIG_ECRNX_RADAR
+TRACE_EVENT(
+    radar_pulse,
+    TP_PROTO(u8 chain, struct radar_pulse *pulse),
+    TP_ARGS(chain, pulse),
+    TP_STRUCT__entry(
+        __field(u8, chain)
+        __field(s16, freq)
+        __field(u16, pri)
+        __field(u8, len)
+        __field(u8, fom)
+                     ),
+    TP_fast_assign(
+        __entry->freq = pulse->freq * 2;
+        __entry->len = pulse->len * 2;
+        __entry->fom = pulse->fom * 6;
+        __entry->pri = pulse->rep;
+        __entry->chain = chain;
+                   ),
+
+    TP_printk("%s: PRI=%.5d LEN=%.3d FOM=%.2d%% freq=%dMHz ",
+              __print_symbolic(__entry->chain,
+                               {ECRNX_RADAR_RIU, "RIU"},
+                               {ECRNX_RADAR_FCU, "FCU"}),
+              __entry->pri, __entry->len, __entry->fom, __entry->freq)
+            );
+
+TRACE_EVENT(
+    radar_detected,
+    TP_PROTO(u8 chain, u8 region, s16 freq, u8 type, u16 pri),
+    TP_ARGS(chain, region, freq, type, pri),
+    TP_STRUCT__entry(
+        __field(u8, chain)
+        __field(u8, region)
+        __field(s16, freq)
+        __field(u8, type)
+        __field(u16, pri)
+                     ),
+    TP_fast_assign(
+        __entry->chain = chain;
+        __entry->region = region;
+        __entry->freq = freq;
+        __entry->type = type;
+        __entry->pri = pri;
+                   ),
+    TP_printk("%s: region=%s type=%d freq=%dMHz (pri=%dus)",
+              __print_symbolic(__entry->chain,
+                               {ECRNX_RADAR_RIU, "RIU"},
+                               {ECRNX_RADAR_FCU, "FCU"}),
+              __print_symbolic(__entry->region,
+                               {NL80211_DFS_UNSET, "UNSET"},
+                               {NL80211_DFS_FCC, "FCC"},
+                               {NL80211_DFS_ETSI, "ETSI"},
+                               {NL80211_DFS_JP, "JP"}),
+              __entry->type, __entry->freq, __entry->pri)
+);
+
+TRACE_EVENT(
+    radar_set_region,
+    TP_PROTO(u8 region),
+    TP_ARGS(region),
+    TP_STRUCT__entry(
+        __field(u8, region)
+                     ),
+    TP_fast_assign(
+        __entry->region = region;
+                   ),
+    TP_printk("region=%s",
+              __print_symbolic(__entry->region,
+                               {NL80211_DFS_UNSET, "UNSET"},
+                               {NL80211_DFS_FCC, "FCC"},
+                               {NL80211_DFS_ETSI, "ETSI"},
+                               {NL80211_DFS_JP, "JP"}))
+);
+
+TRACE_EVENT(
+    radar_enable_detection,
+    TP_PROTO(u8 region, u8 enable, u8 chain),
+    TP_ARGS(region, enable, chain),
+    TP_STRUCT__entry(
+        __field(u8, region)
+        __field(u8, chain)
+        __field(u8, enable)
+                     ),
+    TP_fast_assign(
+        __entry->chain = chain;
+        __entry->enable = enable;
+        __entry->region = region;
+                   ),
+    TP_printk("%s: %s radar detection %s",
+               __print_symbolic(__entry->chain,
+                               {ECRNX_RADAR_RIU, "RIU"},
+                               {ECRNX_RADAR_FCU, "FCU"}),
+              __print_symbolic(__entry->enable,
+                               {ECRNX_RADAR_DETECT_DISABLE, "Disable"},
+                               {ECRNX_RADAR_DETECT_ENABLE, "Enable (no report)"},
+                               {ECRNX_RADAR_DETECT_REPORT, "Enable"}),
+              __entry->enable == ECRNX_RADAR_DETECT_DISABLE ? "" :
+              __print_symbolic(__entry->region,
+                               {NL80211_DFS_UNSET, "UNSET"},
+                               {NL80211_DFS_FCC, "FCC"},
+                               {NL80211_DFS_ETSI, "ETSI"},
+                               {NL80211_DFS_JP, "JP"}))
+);
+#endif /* CONFIG_ECRNX_RADAR */
+
+/*****************************************************************************
+ * TRACE functions for IPC message
+ ****************************************************************************/
+#include "ecrnx_strs.h"
+
+DECLARE_EVENT_CLASS(
+    ipc_msg_template,
+    TP_PROTO(u16 id),
+    TP_ARGS(id),
+    TP_STRUCT__entry(
+        __field(u16, id)
+                     ),
+    TP_fast_assign(
+        __entry->id  = id;
+                   ),
+
+    TP_printk("%s (%d - %d)", ECRNX_ID2STR(__entry->id),
+              MSG_T(__entry->id), MSG_I(__entry->id))
+);
+
+DEFINE_EVENT(ipc_msg_template, msg_send,
+             TP_PROTO(u16 id),
+             TP_ARGS(id));
+
+DEFINE_EVENT(ipc_msg_template, msg_recv,
+             TP_PROTO(u16 id),
+             TP_ARGS(id));
+
+
+
+#endif /* !defined(_ECRNX_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ) */
+
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE ecrnx_events
+#include <trace/define_trace.h>
diff --git a/drivers/net/wireless/eswin/ecrnx_fw_dump.c b/drivers/net/wireless/eswin/ecrnx_fw_dump.c
new file mode 100644 (file)
index 0000000..97ca610
--- /dev/null
@@ -0,0 +1,573 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_fw_dump.c
+ *
+ * @brief Definition of debug fs entries to process fw dump
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+
+#include <linux/kmod.h>
+#include <linux/debugfs.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_debugfs.h"
+
+static ssize_t ecrnx_dbgfs_rhd_read(struct file *file,
+                                   char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos,
+                                   dump->rhd_mem,
+                                   dump->dbg_info.rhd_len);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rhd);
+
+static ssize_t ecrnx_dbgfs_rbd_read(struct file *file,
+                                   char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos,
+                                   dump->rbd_mem,
+                                   dump->dbg_info.rbd_len);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rbd);
+
+static ssize_t ecrnx_dbgfs_thdx_read(struct file *file, char __user *user_buf,
+                                    size_t count, loff_t *ppos, int idx)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos,
+                                   &dump->thd_mem[idx],
+                                   dump->dbg_info.thd_len[idx]);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return read;
+}
+
+static ssize_t ecrnx_dbgfs_thd0_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+    return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 0);
+}
+DEBUGFS_READ_FILE_OPS(thd0);
+
+static ssize_t ecrnx_dbgfs_thd1_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+    return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 1);
+}
+DEBUGFS_READ_FILE_OPS(thd1);
+
+static ssize_t ecrnx_dbgfs_thd2_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+    return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 2);
+}
+DEBUGFS_READ_FILE_OPS(thd2);
+
+static ssize_t ecrnx_dbgfs_thd3_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+    return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 3);
+}
+DEBUGFS_READ_FILE_OPS(thd3);
+
+#if (NX_TXQ_CNT == 5)
+static ssize_t ecrnx_dbgfs_thd4_read(struct file *file,
+                                    char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+    return ecrnx_dbgfs_thdx_read(file, user_buf, count, ppos, 4);
+}
+DEBUGFS_READ_FILE_OPS(thd4);
+#endif
+
+static ssize_t ecrnx_dbgfs_mactrace_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        char msg[64];
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        scnprintf(msg, sizeof(msg), "Force trigger\n");
+        ecrnx_dbgfs_trigger_fw_dump(priv, msg);
+
+        return 0;
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos,
+                                  dump->la_mem,
+                                  dump->dbg_info.la_conf.trace_len);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+
+    return read;
+}
+DEBUGFS_READ_FILE_OPS(mactrace);
+
+static ssize_t ecrnx_dbgfs_macdiags_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos,
+                                   dump->dbg_info.diags_mac,
+                                   DBG_DIAGS_MAC_MAX * 2);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(macdiags);
+
+static ssize_t ecrnx_dbgfs_phydiags_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos,
+                                   dump->dbg_info.diags_phy,
+                                   DBG_DIAGS_PHY_MAX * 2);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(phydiags);
+
+static ssize_t ecrnx_dbgfs_hwdiags_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    char buf[16];
+    int ret;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "%08X\n", dump->dbg_info.hw_diag);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(hwdiags);
+
+static ssize_t ecrnx_dbgfs_plfdiags_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    char buf[16];
+    int ret;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "%08X\n", dump->dbg_info.la_conf.diag_conf);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(plfdiags);
+
+static ssize_t ecrnx_dbgfs_swdiags_read(struct file *file,
+                                      char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos,
+                                   &dump->dbg_info.sw_diag,
+                                   dump->dbg_info.sw_diag_len);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(swdiags);
+
+static ssize_t ecrnx_dbgfs_error_read(struct file *file,
+                                     char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos,
+                                   dump->dbg_info.error,
+                                   strlen((char *)dump->dbg_info.error));
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(error);
+
+static ssize_t ecrnx_dbgfs_rxdesc_read(struct file *file,
+                                      char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    char buf[32];
+    int ret;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "%08X\n%08X\n", dump->dbg_info.rhd,
+                    dump->dbg_info.rbd);
+    read = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(rxdesc);
+
+static ssize_t ecrnx_dbgfs_txdesc_read(struct file *file,
+                                      char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    char buf[64];
+    int len = 0;
+    int i;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    for (i = 0; i < NX_TXQ_CNT; i++) {
+        len += scnprintf(&buf[len], min_t(size_t, sizeof(buf) - len - 1, count),
+                         "%08X\n", dump->dbg_info.thd[i]);
+    }
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+DEBUGFS_READ_FILE_OPS(txdesc);
+
+static ssize_t ecrnx_dbgfs_macrxptr_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos,
+                                   &dump->dbg_info.rhd_hw_ptr,
+                                   2 * sizeof(dump->dbg_info.rhd_hw_ptr));
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return read;
+}
+
+DEBUGFS_READ_FILE_OPS(macrxptr);
+
+static ssize_t ecrnx_dbgfs_lamacconf_read(struct file *file,
+                                         char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    ssize_t read;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    read = simple_read_from_buffer(user_buf, count, ppos,
+                                   dump->dbg_info.la_conf.conf,
+                                   LA_CONF_LEN * 4);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return read;
+}
+DEBUGFS_READ_FILE_OPS(lamacconf);
+
+static ssize_t ecrnx_dbgfs_chaninfo_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    struct dbg_debug_dump_tag *dump = priv->dbgdump_elem.buf.addr;
+    char buf[4 * 32];
+    int ret;
+
+    mutex_lock(&priv->dbgdump_elem.mutex);
+    if (!priv->debugfs.trace_prst) {
+        mutex_unlock(&priv->dbgdump_elem.mutex);
+        return 0;
+    }
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "type:          %d\n"
+                    "prim20_freq:   %d MHz\n"
+                    "center1_freq:  %d MHz\n"
+                    "center2_freq:  %d MHz\n",
+                    (dump->dbg_info.chan_info.info1 >> 8)  & 0xFF,
+                    (dump->dbg_info.chan_info.info1 >> 16) & 0xFFFF,
+                    (dump->dbg_info.chan_info.info2 >> 0)  & 0xFFFF,
+                    (dump->dbg_info.chan_info.info2 >> 16) & 0xFFFF);
+
+    mutex_unlock(&priv->dbgdump_elem.mutex);
+    return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+DEBUGFS_READ_FILE_OPS(chaninfo);
+
+static ssize_t ecrnx_dbgfs_um_helper_read(struct file *file,
+                                         char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    char buf[sizeof(priv->debugfs.helper_cmd)];
+    int ret;
+
+    ret = scnprintf(buf, min_t(size_t, sizeof(buf) - 1, count),
+                    "%s", priv->debugfs.helper_cmd);
+
+    return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+static ssize_t ecrnx_dbgfs_um_helper_write(struct file *file,
+                                          const char __user *user_buf,
+                                          size_t count, loff_t *ppos)
+{
+    struct ecrnx_hw *priv = file->private_data;
+    int eobuf = min_t(size_t, sizeof(priv->debugfs.helper_cmd) - 1, count);
+
+    priv->debugfs.helper_cmd[eobuf] = '\0';
+    if (copy_from_user(priv->debugfs.helper_cmd, user_buf, eobuf))
+        return -EFAULT;
+
+    return count;
+}
+
+DEBUGFS_READ_WRITE_FILE_OPS(um_helper);
+
+/*
+ * Calls a userspace pgm
+ */
+int ecrnx_um_helper(struct ecrnx_debugfs *ecrnx_debugfs, const char *cmd)
+{
+    struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw,
+                                           debugfs);
+    char *envp[] = { "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+    char **argv;
+    int argc, ret;
+
+    if (!ecrnx_debugfs->dir ||
+        !strlen((cmd = cmd ? cmd : ecrnx_debugfs->helper_cmd)))
+        return 0;
+    argv = argv_split(in_interrupt() ? GFP_ATOMIC : GFP_KERNEL, cmd, &argc);
+    if (!argc)
+        return PTR_ERR(argv);
+
+    if ((ret = call_usermodehelper(argv[0], argv, envp,
+                                   UMH_WAIT_PROC | UMH_KILLABLE)))
+        dev_err(ecrnx_hw->dev, "Failed to call %s (%s returned %d)\n",
+               argv[0], cmd, ret);
+    argv_free(argv);
+
+    return ret;
+}
+
+static void ecrnx_um_helper_work(struct work_struct *ws)
+{
+    struct ecrnx_debugfs *ecrnx_debugfs = container_of(ws, struct ecrnx_debugfs,
+                                                     helper_work);
+    struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw,
+                                           debugfs);
+    ecrnx_um_helper(ecrnx_debugfs, NULL);
+    if (!ecrnx_debugfs->unregistering)
+        ecrnx_umh_done(ecrnx_hw);
+    ecrnx_debugfs->helper_scheduled = false;
+}
+
+int ecrnx_trigger_um_helper(struct ecrnx_debugfs *ecrnx_debugfs)
+{
+    struct ecrnx_hw *ecrnx_hw = container_of(ecrnx_debugfs, struct ecrnx_hw,
+                                           debugfs);
+
+    if (ecrnx_debugfs->helper_scheduled == true) {
+        dev_err(ecrnx_hw->dev, "%s: Already scheduled\n", __func__);
+        return -EBUSY;
+    }
+
+    spin_lock_bh(&ecrnx_debugfs->umh_lock);
+    if (ecrnx_debugfs->unregistering) {
+        spin_unlock_bh(&ecrnx_debugfs->umh_lock);
+        dev_err(ecrnx_hw->dev, "%s: unregistering\n", __func__);
+        return -ENOENT;
+    }
+    ecrnx_debugfs->helper_scheduled = true;
+    schedule_work(&ecrnx_debugfs->helper_work);
+    spin_unlock_bh(&ecrnx_debugfs->umh_lock);
+
+    return 0;
+}
+void ecrnx_wait_um_helper(struct ecrnx_hw *ecrnx_hw)
+{
+    flush_work(&ecrnx_hw->debugfs.helper_work);
+}
+
+int ecrnx_dbgfs_register_fw_dump(struct ecrnx_hw *ecrnx_hw,
+                                struct dentry *dir_drv,
+                                struct dentry *dir_diags)
+{
+
+    struct ecrnx_debugfs *ecrnx_debugfs = &ecrnx_hw->debugfs;
+
+    BUILD_BUG_ON(sizeof(CONFIG_ECRNX_UM_HELPER_DFLT) >=
+                 sizeof(ecrnx_debugfs->helper_cmd));
+    strncpy(ecrnx_debugfs->helper_cmd,
+            CONFIG_ECRNX_UM_HELPER_DFLT, sizeof(ecrnx_debugfs->helper_cmd));
+    INIT_WORK(&ecrnx_debugfs->helper_work, ecrnx_um_helper_work);
+    DEBUGFS_ADD_FILE(um_helper, dir_drv, S_IWUSR | S_IRUSR);
+
+    ecrnx_debugfs->trace_prst = ecrnx_debugfs->helper_scheduled = false;
+    spin_lock_init(&ecrnx_debugfs->umh_lock);
+    DEBUGFS_ADD_FILE(rhd,       dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(rbd,       dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(thd0,      dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(thd1,      dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(thd2,      dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(thd3,      dir_diags, S_IRUSR);
+#if (NX_TXQ_CNT == 5)
+    DEBUGFS_ADD_FILE(thd4,      dir_diags, S_IRUSR);
+#endif
+    DEBUGFS_ADD_FILE(mactrace,  dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(macdiags,  dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(phydiags,  dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(plfdiags,  dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(hwdiags,   dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(swdiags,   dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(error,     dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(rxdesc,    dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(txdesc,    dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(macrxptr,  dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(lamacconf, dir_diags, S_IRUSR);
+    DEBUGFS_ADD_FILE(chaninfo,  dir_diags, S_IRUSR);
+
+    return 0;
+
+  err:
+    return -1;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_fw_trace.c b/drivers/net/wireless/eswin/ecrnx_fw_trace.c
new file mode 100644 (file)
index 0000000..a6428e5
--- /dev/null
@@ -0,0 +1,895 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_fw_trace.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include "ecrnx_fw_trace.h"
+#include "ecrnx_defs.h"
+
+#define ECRNX_FW_TRACE_HEADER_LEN 4
+#define ECRNX_FW_TRACE_HEADER_FMT "ts=%12u ID=%8d"
+#define ECRNX_FW_TRACE_HEADER_ASCII_LEN (3 + 12 + 4 + 8)
+#define ECRNX_FW_TRACE_PARAM_FMT ", %5d"
+#define ECRNX_FW_TRACE_PARAM_ASCII_LEN (7)
+
+#define ECRNX_FW_TRACE_NB_PARAM(a) ((*a >> 8) & 0xff)
+#define ECRNX_FW_TRACE_ID(a) (uint32_t)(((a[0] & 0xff) << 16) + a[1])
+#define ECRNX_FW_TRACE_ENTRY_SIZE(a) (ECRNX_FW_TRACE_NB_PARAM(a) + \
+                                     ECRNX_FW_TRACE_HEADER_LEN)
+
+#define ECRNX_FW_TRACE_READY  0x1234
+#define ECRNX_FW_TRACE_LOCKED 0xdead
+#define ECRNX_FW_TRACE_LOCKED_HOST 0x0230
+#define ECRNX_FW_TRACE_LAST_ENTRY 0xffff
+
+#define ECRNX_FW_TRACE_RESET "*** RESET ***\n"
+#define ECRNX_FW_TRACE_RESET_SIZE sizeof(ECRNX_FW_TRACE_RESET) - 1 // don't count '\0'
+
+static int trace_last_reset=0;
+
+static const int startup_max_to = 500;
+
+static uint32_t *saved_filters = NULL;
+static int saved_filters_cnt = 0;
+
+#define ECRNX_FW_TRACE_CHECK_INT_MS 1000
+
+
+/**
+ * ecrnx_fw_trace_work() - Work function to check for new traces
+ *                        process function for &struct ecrnx_fw_trace.work
+ *
+ * @ws: work structure
+ *
+ * Check if new traces are available in the shared buffer, by comparing current
+ * end index with end index in the last check. If so wake up pending threads,
+ * otherwise re-schedule the work is there are still some pending readers.
+ *
+ * Note: If between two check firmware exactly write one buffer of trace then
+ * those traces will be lost. Fortunately this is very unlikely to happen.
+ *
+ * Note: Even if wake_up doesn't actually wake up threads (because condition
+ * failed), calling waitqueue_active just after will still return false.
+ * Fortunately this should never happen (new trace should always trigger the
+ * waiting condition) otherwise it may be needed to re-schedule the work after
+ * wake_up.
+ */
+static void ecrnx_fw_trace_work(struct work_struct *ws)
+{
+    struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+    struct ecrnx_fw_trace *trace = container_of(dw, struct ecrnx_fw_trace, work);
+
+    if (trace->closing ||
+        (!ecrnx_fw_trace_empty(&trace->buf) &&
+         trace->last_read_index != *trace->buf.end)) {
+        trace->last_read_index = *trace->buf.end;
+        wake_up_interruptible(&trace->queue);
+        return;
+    }
+
+    if (waitqueue_active(&trace->queue) && !delayed_work_pending(dw)) {
+        schedule_delayed_work(dw, msecs_to_jiffies(ECRNX_FW_TRACE_CHECK_INT_MS));
+    }
+}
+
+/**
+ * ecrnx_fw_trace_buf_lock() - Lock trace buffer for firmware
+ *
+ * @shared_buf: Pointer to shared buffer
+ *
+ * Very basic synchro mechanism so that fw do not update trace buffer while host
+ * is reading it. Not safe to race condition if host and fw read lock value at
+ * the "same" time.
+ */
+static void ecrnx_fw_trace_buf_lock(struct ecrnx_fw_trace_buf *shared_buf)
+{
+  wait:
+    while(*shared_buf->lock == ECRNX_FW_TRACE_LOCKED) {}
+    *shared_buf->lock &= ECRNX_FW_TRACE_LOCKED_HOST;
+
+    /* re-read to reduce race condition window */
+    if (*shared_buf->lock == ECRNX_FW_TRACE_LOCKED)
+        goto wait;
+}
+
+/**
+ * ecrnx_fw_trace_buf_unlock() - Unlock trace buffer for firmware
+ *
+ * @shared_buf: Pointer to shared buffer
+ *
+ */
+static void ecrnx_fw_trace_buf_unlock(struct ecrnx_fw_trace_buf *shared_buf)
+{
+    *shared_buf->lock = ECRNX_FW_TRACE_READY;
+}
+
+/**
+ * ecrnx_fw_trace_buf_init() - Initialize ecrnx_fw_trace_buf structure
+ *
+ * @shared_buf: Structure to initialize
+ * @ipc: Pointer to IPC shard structure that contains trace buffer info
+ *
+ *
+ * Return: 0 if initialization succeed, <0 otherwise. It can only fail if
+ * trace feature is not enabled in the firmware (or buffer is corrupted).
+ */
+int ecrnx_fw_trace_buf_init(struct ecrnx_fw_trace_buf *shared_buf,
+                           struct ecrnx_fw_trace_ipc_desc *ipc)
+{
+    uint16_t lock_status = ipc->pattern;
+
+    if ((lock_status != ECRNX_FW_TRACE_READY &&
+         lock_status != ECRNX_FW_TRACE_LOCKED)) {
+        shared_buf->data = NULL;
+        return -ENOENT;
+    }
+
+    /* Buffer starts <offset> bytes from the location of ipc->offset */
+    shared_buf->data = (uint16_t *)((uint8_t *)(&ipc->offset) + ipc->offset);
+    shared_buf->lock = &ipc->pattern;
+    shared_buf->size = ipc->size;
+    shared_buf->start = &ipc->start;
+    shared_buf->end = &ipc->end;
+    shared_buf->reset_idx = ++trace_last_reset;
+
+    /* backward compatibilty with firmware without trace activation */
+    if ((ipc->nb_compo >> 16) == ECRNX_FW_TRACE_READY) {
+        shared_buf->nb_compo = ipc->nb_compo & 0xffff;
+        shared_buf->compo_table = (uint32_t *)((uint8_t *)(&ipc->offset_compo)
+                                               + ipc->offset_compo);
+    } else {
+        shared_buf->nb_compo = 0;
+        shared_buf->compo_table = NULL;
+    }
+
+    return 0;
+}
+
+/**
+ * ecrnx_fw_trace_init() - Initialize ecrnx_fw_trace structure
+ *
+ * @trace: Structure to initialize
+ * @ipc: Pointer to IPC shard structure that contains trace buffer info
+ *
+ * Return: 0 if initialization succeed, <0 otherwise. It can only fail if
+ * trace feature is not enabled in the firmware (or buffer is corrupted).
+ */
+int ecrnx_fw_trace_init(struct ecrnx_fw_trace *trace,
+                       struct ecrnx_fw_trace_ipc_desc *ipc)
+{
+    if (ecrnx_fw_trace_buf_init(&trace->buf, ipc))
+        return -ENOENT;
+
+    INIT_DELAYED_WORK(&trace->work, ecrnx_fw_trace_work);
+    init_waitqueue_head(&trace->queue);
+    mutex_init(&trace->mutex);
+    trace->closing = false;
+    return 0;
+}
+
+/**
+ * ecrnx_fw_trace_deinit() - De-initialization before releasing ecrnx_fw_trace
+ *
+ * @trace: fw trace control structure
+ */
+void ecrnx_fw_trace_deinit(struct ecrnx_fw_trace *trace)
+{
+    trace->closing = true;
+    flush_delayed_work(&trace->work);
+    trace->buf.data = NULL;
+}
+
+/**
+ * ecrnx_fw_trace_reset_local() - Reset local buffer pointer/status
+ *
+ * @local_buf: structure to reset
+ */
+static void ecrnx_fw_trace_reset_local(struct ecrnx_fw_trace_local_buf *local_buf)
+{
+    local_buf->read = local_buf->data;
+    local_buf->write = local_buf->data;
+    local_buf->nb_entries = 0;
+    local_buf->free_space = local_buf->size;
+    local_buf->last_read = NULL;
+    local_buf->reset_idx = 0;
+    local_buf->show_reset = NULL;
+}
+
+/**
+ * ecrnx_fw_trace_alloc_local() - Allocate a local buffer and initialize
+ * ecrnx_fw_trace_local_buf structure
+ *
+ * @local_buf: structure to initialize
+ * @size: Size of the buffer to allocate
+ *
+ * @local structure is initialized to use the allocated buffer.
+ *
+ * Return: 0 if allocation succeed and <0 otherwise.
+ */
+int ecrnx_fw_trace_alloc_local(struct ecrnx_fw_trace_local_buf *local_buf,
+                              int size)
+{
+    local_buf->data = kmalloc(size * sizeof(uint16_t), GFP_KERNEL);
+    if (!local_buf->data) {
+        return -ENOMEM;
+    }
+
+    local_buf->data_end = local_buf->data + size;
+    local_buf->size = size;
+    ecrnx_fw_trace_reset_local(local_buf);
+    return 0;
+}
+
+/**
+ * ecrnx_fw_trace_free_local() - Free local buffer
+ *
+ * @local_buf: structure containing buffer pointer to free.
+ */
+void ecrnx_fw_trace_free_local(struct ecrnx_fw_trace_local_buf *local_buf)
+{
+    if (local_buf->data)
+        kfree(local_buf->data);
+    local_buf->data = NULL;
+}
+
+/**
+ * ecrnx_fw_trace_strlen() - Return buffer size needed convert a trace entry into
+ * string
+ *
+ * @entry: Pointer on trace entry
+ *
+ */
+static inline int ecrnx_fw_trace_strlen(uint16_t *entry)
+{
+    return (ECRNX_FW_TRACE_HEADER_ASCII_LEN +
+            (ECRNX_FW_TRACE_NB_PARAM(entry) * ECRNX_FW_TRACE_PARAM_ASCII_LEN) +
+            1); /* for \n */
+}
+
+/**
+ * ecrnx_fw_trace_to_str() - Convert one trace entry to a string
+ *
+ * @trace: Poitner to the trace entry
+ * @buf: Buffer for the string
+ * @size: Size of the string buffer, updated with the actual string size
+ *
+ * Return: pointer to the next tag entry.
+ */
+static uint16_t *ecrnx_fw_trace_to_str(uint16_t *trace, char *buf, size_t *size)
+{
+    uint32_t ts, id;
+    int nb_param;
+    int res, buf_idx = 0, left = *size;
+
+    id = ECRNX_FW_TRACE_ID(trace);
+    nb_param = ECRNX_FW_TRACE_NB_PARAM(trace);
+
+    trace +=2;
+    ts = *trace++;
+    ts <<= 16;
+    ts += *trace++;
+
+    res = scnprintf(&buf[buf_idx], left, ECRNX_FW_TRACE_HEADER_FMT, ts, id);
+    buf_idx += res;
+    left    -= res;
+
+    while (nb_param > 0) {
+        res = scnprintf(&buf[buf_idx], left, ECRNX_FW_TRACE_PARAM_FMT, *trace++);
+        buf_idx += res;
+        left    -= res;
+        nb_param--;
+    }
+
+    res = scnprintf(&buf[buf_idx], left, "\n");
+    left -= res;
+    *size = (*size - left);
+
+    return trace;
+}
+
+/**
+ * ecrnx_fw_trace_copy_entry() - Copy one trace entry in a local buffer
+ *
+ * @local_buf: Local buffer to copy trace into
+ * @trace_entry: Pointer to the trace entry (in shared memory) to copy
+ * @size: Size, in 16bits words, of the trace entry
+ *
+ * It is assumed that local has enough contiguous free-space available in
+ * local buffer (i.e. from local_buf->write) to copy this trace.
+ */
+static void ecrnx_fw_trace_copy_entry(struct ecrnx_fw_trace_local_buf *local_buf,
+                                     uint16_t *trace_entry, int size)
+{
+    uint16_t *write = local_buf->write;
+    uint16_t *read = trace_entry;
+    int i;
+
+    for (i = 0; i < size; i++) {
+        *write++ = *read++;
+    }
+
+    if (write >= local_buf->data_end)
+        local_buf->write = local_buf->data;
+    else
+        local_buf->write = write;
+
+    local_buf->free_space -= size;
+    local_buf->last_read = trace_entry;
+    local_buf->last_read_value = *trace_entry;
+    local_buf->nb_entries++;
+}
+
+/**
+ * ecrnx_fw_trace_copy() - Copy trace entries from shared to local buffer
+ *
+ * @trace_buf: Pointer to shard buffer
+ * @local_buf: Pointer to local buffer
+ *
+ * Copy has many trace entry as possible from shared buffer to local buffer
+ * without overwriting traces in local buffer.
+ *
+ * Return: number of trace entries copied to local buffer
+ */
+static int ecrnx_fw_trace_copy(struct ecrnx_fw_trace *trace,
+                              struct ecrnx_fw_trace_local_buf *local_buf)
+{
+    struct ecrnx_fw_trace_buf *trace_buf = &trace->buf;
+    uint16_t *ptr, *ptr_end, *ptr_limit;
+    int entry_size, ret = 0;
+
+    if (mutex_lock_interruptible(&trace->mutex))
+        return 0;
+
+    /* reset last_read ptr if shared buffer has been reset */
+    if (local_buf->reset_idx != trace_buf->reset_idx) {
+        local_buf->show_reset = local_buf->write;
+        local_buf->reset_idx = trace_buf->reset_idx;
+        local_buf->last_read = NULL;
+    }
+
+    ecrnx_fw_trace_buf_lock(trace_buf);
+
+    ptr_end = trace_buf->data + *trace_buf->end;
+    if (ecrnx_fw_trace_empty(trace_buf) || (ptr_end == local_buf->last_read))
+        goto end;
+    ptr_limit = trace_buf->data + trace_buf->size;
+
+    if (local_buf->last_read &&
+        (local_buf->last_read_value == *local_buf->last_read)) {
+        ptr = local_buf->last_read;
+        ptr += ECRNX_FW_TRACE_ENTRY_SIZE(ptr);
+    } else {
+        ptr = trace_buf->data + *trace_buf->start;
+    }
+
+    while (1) {
+
+        if ((ptr == ptr_limit) || (*ptr == ECRNX_FW_TRACE_LAST_ENTRY))
+             ptr = trace_buf->data;
+
+        entry_size = ECRNX_FW_TRACE_ENTRY_SIZE(ptr);
+
+        if ((ptr + entry_size) > ptr_limit) {
+            ECRNX_ERR("Corrupted trace buffer\n");
+            _ecrnx_fw_trace_reset(trace, false);
+            break;
+        } else if (entry_size > local_buf->size) {
+            ECRNX_ERR("FW_TRACE local buffer too small, trace skipped");
+            goto next_entry;
+        }
+
+        if (local_buf->free_space >= entry_size) {
+            int contiguous = local_buf->data_end - local_buf->write;
+
+            if ((local_buf->write < local_buf->read) || contiguous >= entry_size) {
+                /* enough contiguous memory from local_buf->write */
+                ecrnx_fw_trace_copy_entry(local_buf, ptr, entry_size);
+                ret++;
+            } else if ((local_buf->free_space - contiguous) >= entry_size) {
+                /* not enough contiguous from local_buf->write but enough
+                   from local_buf->data */
+                *local_buf->write = ECRNX_FW_TRACE_LAST_ENTRY;
+                if (local_buf->show_reset == local_buf->write)
+                    local_buf->show_reset = local_buf->data;
+                local_buf->write = local_buf->data;
+                local_buf->free_space -= contiguous;
+                ecrnx_fw_trace_copy_entry(local_buf, ptr, entry_size);
+                ret++;
+            } else {
+                /* not enough contiguous memory */
+                goto end;
+            }
+        } else {
+            goto end;
+        }
+
+        if (ptr == ptr_end)
+            break;
+
+      next_entry:
+        ptr += entry_size;
+    }
+
+  end:
+    ecrnx_fw_trace_buf_unlock(trace_buf);
+    mutex_unlock(&trace->mutex);
+    return ret;
+}
+
+/**
+ * ecrnx_fw_trace_read_local() - Read trace from local buffer and convert it to
+ * string in a user buffer
+ *
+ * @local_buf: Pointer to local buffer
+ * @user_buf: Pointer to user buffer
+ * @size: Size of the user buffer
+ *
+ * Read traces from shared buffer to write them in the user buffer after string
+ * conversion. Stop when no more space in user buffer or no more trace to read.
+ *
+ * Return: The size written in the user buffer.
+ */
+static size_t ecrnx_fw_trace_read_local(struct ecrnx_fw_trace_local_buf *local_buf,
+                                       char __user *user_buf, size_t size)
+{
+    uint16_t *ptr;
+    char *str = NULL; // worst case 255 params
+    size_t str_size;
+    int entry_size;
+    size_t res = 0 , remain = size, not_cpy = 0;
+
+    if (!local_buf->nb_entries)
+        return res;
+
+    str = kmalloc(FW_TRACE_READ_DUMP_max_SIZE, GFP_ATOMIC);
+    if(!str){
+        return 0;
+    }
+
+    ptr = local_buf->read;
+    while(local_buf->nb_entries && !not_cpy) {
+
+        if (local_buf->show_reset == ptr) {
+            if (remain < ECRNX_FW_TRACE_RESET_SIZE)
+                break;
+
+            local_buf->show_reset = NULL;
+            not_cpy = copy_to_user(user_buf + res, ECRNX_FW_TRACE_RESET,
+                                   ECRNX_FW_TRACE_RESET_SIZE);
+            res += (ECRNX_FW_TRACE_RESET_SIZE - not_cpy);
+            remain -= (ECRNX_FW_TRACE_RESET_SIZE - not_cpy);
+        }
+
+        if (remain < ecrnx_fw_trace_strlen(ptr))
+            break;
+
+        entry_size = ECRNX_FW_TRACE_ENTRY_SIZE(ptr);
+        str_size = sizeof(str);
+        ptr = ecrnx_fw_trace_to_str(ptr, str, &str_size);
+        not_cpy = copy_to_user(user_buf + res, str, str_size);
+        str_size -= not_cpy;
+        res += str_size;
+        remain -= str_size;
+
+        local_buf->nb_entries--;
+        local_buf->free_space += entry_size;
+        if (ptr >= local_buf->data_end) {
+            ptr = local_buf->data;
+        } else if (*ptr == ECRNX_FW_TRACE_LAST_ENTRY) {
+            local_buf->free_space += local_buf->data_end - ptr;
+            ptr = local_buf->data;
+        }
+        local_buf->read = ptr;
+    }
+
+    /* read all entries reset pointer */
+    if ( !local_buf->nb_entries) {
+
+        local_buf->write = local_buf->read = local_buf->data;
+        local_buf->free_space = local_buf->size;
+    }
+
+    kfree(str);
+    return res;
+}
+
+/**
+ * ecrnx_fw_trace_read() - Update local buffer from shared buffer and convert
+ * local buffer to string in user buffer
+ *
+ * @trace: Fw trace control structure
+ * @local_buf: Local buffer to update and read from
+ * @dont_wait: Indicate whether function should wait or not for traces before
+ * returning
+ * @user_buf: Pointer to user buffer
+ * @size: Size of the user buffer
+ *
+ * Read traces from shared buffer to write them in the user buffer after string
+ * conversion. Stop when no more space in user buffer or no more trace to read.
+ *
+ * Return: The size written in the user buffer if > 0, -EAGAIN if there is no
+ * new traces and dont_wait is set and -ERESTARTSYS if signal has been
+ * received while waiting for new traces.
+ */
+size_t ecrnx_fw_trace_read(struct ecrnx_fw_trace *trace,
+                          struct ecrnx_fw_trace_local_buf *local_buf,
+                          bool dont_wait, char __user *user_buf, size_t size)
+{
+    size_t res = 0;
+
+    ecrnx_fw_trace_copy(trace, local_buf);
+
+    while(!local_buf->nb_entries) {
+        int last_index;
+
+        if (dont_wait)
+            return -EAGAIN;
+
+        /* no trace, schedule work to periodically check trace buffer */
+        if (!delayed_work_pending(&trace->work)) {
+            trace->last_read_index = *trace->buf.end;
+            schedule_delayed_work(&trace->work,
+                                  msecs_to_jiffies(ECRNX_FW_TRACE_CHECK_INT_MS));
+        }
+
+        /* and wait for traces */
+        last_index = *trace->buf.end;
+        if (wait_event_interruptible(trace->queue,
+                                     (trace->closing ||
+                                      (last_index != *trace->buf.end)))) {
+            return -ERESTARTSYS;
+        }
+
+        if (trace->closing)
+            return 0;
+
+        ecrnx_fw_trace_copy(trace, local_buf);
+    }
+
+    /* copy as many traces as possible in user buffer */
+    while (1) {
+        size_t read;
+        read = ecrnx_fw_trace_read_local(local_buf, user_buf + res, size - res);
+        res += read;
+        ecrnx_fw_trace_copy(trace, local_buf);
+        if (!read)
+            break;
+    }
+
+    return res;
+}
+
+
+/**
+ * _ecrnx_fw_trace_dump() - Dump shared trace buffer in kernel buffer
+ *
+ * @trace_buf: Pointer to shared trace buffer;
+ *
+ * Called when error is detected, output trace on dmesg directly read from
+ * shared memory
+ */
+void _ecrnx_fw_trace_dump(struct ecrnx_fw_trace_buf *trace_buf)
+{
+    uint16_t *ptr, *ptr_end, *ptr_limit, *next_ptr;
+    char *buf = NULL; // worst case 255 params
+    size_t size;
+
+    if (!trace_buf->data || ecrnx_fw_trace_empty(trace_buf))
+        return;
+
+    ecrnx_fw_trace_buf_lock(trace_buf);
+
+    ptr = trace_buf->data + *trace_buf->start;
+    ptr_end = trace_buf->data + *trace_buf->end;
+    ptr_limit = trace_buf->data + trace_buf->size;
+
+    buf = kmalloc(FW_TRACE_READ_DUMP_max_SIZE, GFP_ATOMIC);
+    while (1) {
+        size = FW_TRACE_READ_DUMP_max_SIZE;
+        next_ptr = ecrnx_fw_trace_to_str(ptr, buf, &size);
+        ECRNX_PRINT("%s", buf);
+
+        if (ptr == ptr_end) {
+            break;
+        } else if ((next_ptr == ptr_limit) ||
+                   (*next_ptr == ECRNX_FW_TRACE_LAST_ENTRY)) {
+            ptr = trace_buf->data;
+        } else if (next_ptr > ptr_limit) {
+            ECRNX_ERR("Corrupted trace buffer\n");
+            break;
+        } else {
+            ptr = next_ptr;
+        }
+    }
+
+    ecrnx_fw_trace_buf_unlock(trace_buf);
+    kfree(buf);
+}
+
+/**
+ * _ecrnx_fw_trace_reset() - Reset trace buffer at firmware level
+ *
+ * @trace: Pointer to shared trace buffer;
+ * @bool: Indicate if mutex must be aquired before
+ */
+int _ecrnx_fw_trace_reset(struct ecrnx_fw_trace *trace, bool lock)
+{
+    struct ecrnx_fw_trace_buf *trace_buf = &trace->buf;
+
+    if (lock && mutex_lock_interruptible(&trace->mutex))
+        return -ERESTARTSYS;
+
+    if (trace->buf.data) {
+        ecrnx_fw_trace_buf_lock(trace_buf);
+        *trace_buf->start = 0;
+        *trace_buf->end = trace_buf->size + 1;
+        trace_buf->reset_idx = ++trace_last_reset;
+        ecrnx_fw_trace_buf_unlock(trace_buf);
+    }
+
+    if (lock)
+        mutex_unlock(&trace->mutex);
+    return 0;
+}
+
+/**
+ * ecrnx_fw_trace_get_trace_level() - Get trace level for a given component
+ *
+ * @trace: Pointer to shared trace buffer;
+ * @compo_id: Index of the componetn in the table
+ *
+ * Return: The trace level set for the given component, 0 if component index
+ * is invalid.
+ */
+static uint32_t ecrnx_fw_trace_get_trace_level(struct ecrnx_fw_trace_buf *trace_buf,
+                                              unsigned int compo_id)
+{
+    if (compo_id >= trace_buf->nb_compo)
+        return 0;
+    return trace_buf->compo_table[compo_id];
+}
+
+/**
+ * ecrnx_fw_trace_set_trace_level() - Set trace level for a given component
+ *
+ * @trace_buf: Pointer to shared trace buffer;
+ * @compo_id: Index of the componetn in the table
+ * @level: Trace level to set
+ *
+ * Set all components if compo_id is equals to the number of component and
+ * does nothing if it is greater.
+ */
+static void ecrnx_fw_trace_set_trace_level(struct ecrnx_fw_trace_buf *trace_buf,
+                                          unsigned int compo_id, uint32_t level)
+{
+    if (compo_id > trace_buf->nb_compo)
+        return;
+
+    if (compo_id == trace_buf->nb_compo) {
+        int i;
+        for (i = 0; i < trace_buf->nb_compo; i++) {
+            trace_buf->compo_table[i] = level;
+        }
+    } else {
+        trace_buf->compo_table[compo_id] = level;
+    }
+}
+
+/**
+ * ecrnx_fw_trace_level_read() - Write current trace level in a user buffer
+ *                              as a string
+ *
+ * @trace: Fw trace control structure
+ * @user_buf: Pointer to user buffer
+ * @len: Size of the user buffer
+ * @ppos: position offset
+ *
+ * Return: Number of bytes written in user buffer if > 0, error otherwise
+ */
+size_t ecrnx_fw_trace_level_read(struct ecrnx_fw_trace *trace,
+                                char __user *user_buf, size_t len, loff_t *ppos)
+{
+    struct ecrnx_fw_trace_buf *trace_buf = &trace->buf;
+    size_t res = 0;
+    int i, size;
+    char *buf;
+
+    size = trace_buf->nb_compo * 16;
+    buf = kmalloc(size, GFP_KERNEL);
+    if (buf == NULL)
+        return 0;
+
+    if (mutex_lock_interruptible(&trace->mutex)) {
+        kfree(buf);
+        return -ERESTARTSYS;
+    }
+
+    for (i = 0 ; i < trace_buf->nb_compo ; i ++) {
+        res += scnprintf(&buf[res], size - res, "%3d:0x%08x\n", i,
+                         ecrnx_fw_trace_get_trace_level(trace_buf, i));
+    }
+    mutex_unlock(&trace->mutex);
+
+    res = simple_read_from_buffer(user_buf, len, ppos, buf, res);
+
+    kfree(buf);
+    return res;
+}
+
+/**
+ * ecrnx_fw_trace_level_write() - Read trace level from  a user buffer provided
+ *                               as a string and applyt them.
+ *
+ * @trace: Fw trace control structure
+ * @user_buf: Pointer to user buffer
+ * @len: Size of the user buffer
+ *
+ * trace level must be provided in the following form:
+ * <compo_id>:<trace_level> where <compo_id> is in decimal notation and
+ * <trace_level> in decical or hexadecimal notation.
+ * Several trace level can be provided, separated by space,tab or new line.
+ *
+ * Return: Number of bytes read form user buffer if > 0, error otherwise
+ */
+size_t ecrnx_fw_trace_level_write(struct ecrnx_fw_trace *trace,
+                                 const char __user *user_buf, size_t len)
+{
+    struct ecrnx_fw_trace_buf *trace_buf = &trace->buf;
+    char *buf, *token, *next;
+
+    buf = kmalloc(len + 1, GFP_KERNEL);
+    if (buf == NULL)
+        return -ENOMEM;
+
+    if (copy_from_user(buf, user_buf, len)) {
+        kfree(buf);
+        return -EFAULT;
+    }
+    buf[len] = '\0';
+
+    if (mutex_lock_interruptible(&trace->mutex)) {
+        kfree(buf);
+        return -ERESTARTSYS;
+    }
+
+    next = buf;
+    token = strsep(&next, " \t\n");
+    while (token) {
+        unsigned int compo, level;
+        if ((sscanf(token, "%d:0x%x", &compo, &level) == 2)||
+            (sscanf(token, "%d:%d", &compo, &level) == 2)) {
+            ecrnx_fw_trace_set_trace_level(trace_buf, compo, level);
+        }
+
+        token = strsep(&next, " \t");
+    }
+    mutex_unlock(&trace->mutex);
+
+    kfree(buf);
+    return len;
+}
+
+/**
+ * ecrnx_fw_trace_config_filters() - Update FW trace filters
+ *
+ * @trace_buf: Pointer to shared buffer
+ * @ipc: Pointer to IPC shared structure that contains trace buffer info
+ * @ftl: Firmware trace level
+ *
+ * Return: 0 if the trace filters are successfully updated, <0 otherwise.
+ */
+int ecrnx_fw_trace_config_filters(struct ecrnx_fw_trace_buf *trace_buf,
+                                 struct ecrnx_fw_trace_ipc_desc *ipc, char *ftl)
+{
+    int to;
+    char *next, *token;
+
+    to = 0;
+    while((ipc->pattern != ECRNX_FW_TRACE_READY) && (to < startup_max_to))
+    {
+        msleep(50);
+        to += 50;
+    }
+
+    if (ecrnx_fw_trace_buf_init(trace_buf, ipc))
+        return -ENOENT;
+
+    next = ftl;
+    token = strsep(&next, " ");
+    while(token)
+    {
+        unsigned int compo, ret, id, level = 0;
+        char action;
+
+        if ((sscanf(token, "%d%c0x%x", &compo, &action, &id) == 3)||
+            (sscanf(token, "%d%c%d", &compo, &action, &id) == 3))
+        {
+            if(action == '=')
+            {
+                level = id;
+            }
+            else
+            {
+                ret = ecrnx_fw_trace_get_trace_level(trace_buf, compo);
+                if(action == '+')
+                    level = (ret | id);
+                else if (action == '-')
+                    level = (ret & ~id);
+            }
+            ecrnx_fw_trace_set_trace_level(trace_buf, compo, level);
+        }
+
+        token = strsep(&next, " ");
+    }
+
+    return 0;
+}
+
+/**
+ * ecrnx_fw_trace_save_filters() - Save filters currently configured so that
+ * they can be restored with ecrnx_fw_trace_restore_filters()
+ *
+ * @trace: Fw trace control structure
+ * @return 0 if filters have been saved and != 0 in case on error
+ */
+int ecrnx_fw_trace_save_filters(struct ecrnx_fw_trace *trace)
+{
+    int i;
+
+    if (saved_filters)
+        kfree(saved_filters);
+
+    saved_filters_cnt = trace->buf.nb_compo;
+    saved_filters = kmalloc(saved_filters_cnt * sizeof(uint32_t), GFP_KERNEL);
+    if (!saved_filters)
+        return -1;
+
+    for (i = 0; i < saved_filters_cnt; i++) {
+        saved_filters[i] = ecrnx_fw_trace_get_trace_level(&trace->buf, i);
+    }
+
+    return 0;
+}
+
+/**
+ * ecrnx_fw_trace_restore_filters() - Restore filters previoulsy saved
+ * by ecrnx_fw_trace_save_filters()
+ *
+ * @trace: Fw trace control structure
+ * @return 0 if filters have been restored and != 0 in case on error
+ */
+int ecrnx_fw_trace_restore_filters(struct ecrnx_fw_trace *trace)
+{
+    int i;
+
+    if (!saved_filters || (trace->buf.data == NULL))
+        return -1;
+
+    if (saved_filters_cnt != trace->buf.nb_compo) {
+        pr_warn("Number of trace components change between saved and restore\n");
+        if (saved_filters_cnt > trace->buf.nb_compo) {
+            saved_filters_cnt = trace->buf.nb_compo;
+        }
+    }
+
+    for (i = 0; i < saved_filters_cnt; i++) {
+        ecrnx_fw_trace_set_trace_level(&trace->buf, i, saved_filters[i]);
+    }
+
+    kfree(saved_filters);
+    saved_filters = NULL;
+    saved_filters_cnt = 0;
+
+    return 0;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_fw_trace.h b/drivers/net/wireless/eswin/ecrnx_fw_trace.h
new file mode 100644 (file)
index 0000000..e7df3ad
--- /dev/null
@@ -0,0 +1,161 @@
+/**
+ ******************************************************************************
+ *
+ * ecrnx_fw_trace.h
+ *
+ * Copyright (C) RESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_FW_TRACE_H_
+#define _ECRNX_FW_TRACE_H_
+
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#define    FW_TRACE_READ_DUMP_max_SIZE 1824
+/**
+ * struct ecrnx_fw_trace_desc - Trace buffer info as provided by fw in ipc
+ *
+ * @pattern: Synchro pattern
+ * @start: Index of first entry in trace buffer
+ * @end: Index of last entry in trace buffer
+ * @size: Size, in 16bits words, od the trace buffer
+ * @offset: Offset, in bytest, to the start of the buffer from the address of
+ * this field.
+ * @nb_compo: Number of filterable component (16LSB) synchro pattern (16 MSB)
+ * @offset_compo: Offset, in bytest, to the start of the component activation
+ * table from the address of this field.
+ */
+struct ecrnx_fw_trace_ipc_desc {
+    volatile uint16_t pattern;
+    volatile uint32_t start;
+    volatile uint32_t end;
+    volatile uint32_t size;
+    volatile uint32_t offset;
+    volatile uint32_t nb_compo;
+    volatile uint32_t offset_compo;
+};
+
+/**
+ * struct ecrnx_fw_trace_buf - Info for trace buffer in shared memory
+ *
+ * @lock: Address of the synchro word
+ * @data: Address of the trace buffer
+ * @size: Size, in 16bits words, of the trace buffer
+ * @start: Address on the current index (in 16 bits words) of the first trace
+ * entry.
+ * @end: Address on the current index (in 16 bits words) of the last trace
+ * entry. If *end > size, it means that the trace buffer contains no traces.
+ * @reset_idx: Increased each time the trace buffer is reset
+ * (by calling _ecrnx_fw_trace_reset())
+ * @nb_compo: Size of the compo_table
+ * @compo_table: Table containing component filter status.
+ */
+struct ecrnx_fw_trace_buf {
+    volatile uint16_t *lock;
+    uint16_t *data;
+    uint32_t size;
+    volatile uint32_t *start;
+    volatile uint32_t *end;
+    int reset_idx;
+    unsigned int nb_compo;
+    uint32_t *compo_table;
+};
+
+/**
+ * struct ecrnx_fw_trace_local_buf - Info for a local trace buffer
+ *
+ * @data: Address of the local buffer
+ * @data_end: Address after the end of the local buffer
+ * @size: Size, in 16bits words, oth the local buffer
+ * @read: Pointer to the next trace entry to read
+ * @write: Pointer to the next entry to write
+ * @nb_entries: Number of trace entries ready to be read
+ * @free_space: Free space, in 16bits words, in the buffer.
+ * @last_read: Address of the last entry read in the shared buffer
+ * @last_read_value: First word of the last trace entry read.
+ * @reset_idx: Reset index. If it doesn't match share buffer index then it
+ * means that share buffer has been resetted since last read.
+ */
+struct ecrnx_fw_trace_local_buf {
+    uint16_t *data;
+    uint16_t *data_end;
+    uint32_t size;
+    uint16_t *read;
+    uint16_t *write;
+    uint16_t nb_entries;
+    uint32_t free_space;
+    uint16_t *last_read;
+    uint16_t last_read_value;
+    int reset_idx;
+    uint16_t * show_reset;
+};
+
+
+/**
+ * struct ecrnx_fw_trace - info to handle several reader of the shared buffer
+ *
+ * @buf: shared trace buffer.
+ * @mutex: mutex, used to prevent several reader updating shared buffer at the
+ * same time.
+ * @queue: queue, used to delay reader.
+ * @work: work used to periodically check for new traces in shared buffer.
+ * @last_read_index: Last read index from shared buffer
+ * @closing: Indicate whether is driver is being removed, meaning that reader
+ * should no longer wait no new traces
+ */
+struct ecrnx_fw_trace {
+    struct ecrnx_fw_trace_buf buf;
+    struct mutex mutex;
+    wait_queue_head_t queue;
+    struct delayed_work work;
+    int last_read_index;
+    bool closing;
+};
+
+int ecrnx_fw_trace_init(struct ecrnx_fw_trace *trace,
+                       struct ecrnx_fw_trace_ipc_desc *ipc);
+void ecrnx_fw_trace_deinit(struct ecrnx_fw_trace *trace);
+
+int ecrnx_fw_trace_buf_init(struct ecrnx_fw_trace_buf *shared_buf,
+                           struct ecrnx_fw_trace_ipc_desc *ipc);
+
+int _ecrnx_fw_trace_reset(struct ecrnx_fw_trace *trace, bool lock);
+void _ecrnx_fw_trace_dump(struct ecrnx_fw_trace_buf *trace);
+
+int ecrnx_fw_trace_alloc_local(struct ecrnx_fw_trace_local_buf *local,
+                              int size);
+void ecrnx_fw_trace_free_local(struct ecrnx_fw_trace_local_buf *local);
+
+
+size_t ecrnx_fw_trace_read(struct ecrnx_fw_trace *trace,
+                          struct ecrnx_fw_trace_local_buf *local_buf,
+                          bool dont_wait, char __user *user_buf, size_t size);
+
+
+size_t ecrnx_fw_trace_level_read(struct ecrnx_fw_trace *trace,
+                                char __user *user_buf, size_t len, loff_t *ppos);
+size_t ecrnx_fw_trace_level_write(struct ecrnx_fw_trace *trace,
+                                 const char __user *user_buf, size_t len);
+
+
+int ecrnx_fw_trace_config_filters(struct ecrnx_fw_trace_buf *trace_buf,
+                                 struct ecrnx_fw_trace_ipc_desc *ipc, char *ftl);
+
+int ecrnx_fw_trace_save_filters(struct ecrnx_fw_trace *trace);
+int ecrnx_fw_trace_restore_filters(struct ecrnx_fw_trace *trace);
+
+/**
+ * ecrnx_fw_trace_empty() - Check if shared buffer is empty
+ *
+ * @shared_buf: Pointer to shared buffer
+ */
+static inline bool ecrnx_fw_trace_empty(struct ecrnx_fw_trace_buf *shared_buf)
+{
+    return (*shared_buf->end >= shared_buf->size);
+}
+
+#endif /* _ECRNX_FW_TRACE_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_iwpriv.c b/drivers/net/wireless/eswin/ecrnx_iwpriv.c
new file mode 100644 (file)
index 0000000..037fe56
--- /dev/null
@@ -0,0 +1,396 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_iwpriv.c
+ *
+ * @brief iwpriv function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#include <net/cfg80211.h>
+#include <net/iw_handler.h>
+#include "ecrnx_defs.h"
+#include "eswin_utils.h"
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "ecrnx_amt.h"
+#include "core.h"
+#endif
+
+#ifdef CONFIG_WIRELESS_EXT
+/**
+ * FUNCTION DEFINITIONS
+ ******************************************************************************
+ */
+#define IN
+#define OUT
+
+#ifdef CONFIG_WEXT_PRIV
+ /* This may be wrong. When using the new SIOCIWFIRSTPRIV range, we probably
+  * should use only "GET" ioctls (last bit set to 1). "SET" ioctls are root
+  * only and don't return the modified struct ifreq to the application which
+  * is usually a problem. - Jean II */
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#define IOCTL_IWPRIV_AMT                (SIOCIWFIRSTPRIV + 1)
+#endif
+#define IOCTL_IWPRIV_WD                 (SIOCIWFIRSTPRIV + 3)
+
+#if 0
+static int priv_set_int(IN struct net_device *prNetDev,
+          IN struct iw_request_info *prIwReqInfo,
+          IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+    int *flag = (int*)pcExtra;
+    ECRNX_PRINT("cmd=%x, flags=%x\n",
+      prIwReqInfo->cmd, prIwReqInfo->flags);
+    ECRNX_PRINT("mode=%x, flags=%x\n",
+      prIwReqData->mode, prIwReqData->data.flags);
+    *flag = 0x1234;
+    prIwReqData->param.value = 0x1230;
+
+    return 1;
+}
+
+/*----------------------------------------------------------------------------*/
+/*!
+* \brief Private ioctl get int handler.
+*
+* \param[in] pDev Net device requested.
+* \param[out] pIwReq Pointer to iwreq structure.
+* \param[in] prIwReqData The ioctl req structure, use the field of sub-command.
+* \param[out] pcExtra The buffer with put the return value
+*
+* \retval 0 For success.
+* \retval -EOPNOTSUPP If cmd is not supported.
+* \retval -EFAULT For fail.
+*
+*/
+/*----------------------------------------------------------------------------*/
+static int priv_get_int(IN struct net_device *prNetDev,
+      IN struct iw_request_info *prIwReqInfo,
+      IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+    int status = 0;
+    prIwReqData->mode = 0xabcd;
+    return status;
+}               /* __priv_get_int */
+
+static int priv_set_struct(IN struct net_device *prNetDev,
+       IN struct iw_request_info *prIwReqInfo,
+       IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+  ECRNX_PRINT("cmd=%x, flags=%x\n",
+       prIwReqInfo->cmd, prIwReqInfo->flags);
+  ECRNX_PRINT("mode=%x, flags=%x\n",
+       prIwReqData->mode, prIwReqData->data.flags);
+
+  return 0;
+  //return compat_priv(prNetDev, prIwReqInfo,
+  //     prIwReqData, pcExtra, __priv_set_struct);
+}
+
+static int priv_get_struct(IN struct net_device *prNetDev,
+       IN struct iw_request_info *prIwReqInfo,
+       IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+    ECRNX_PRINT("cmd=%x, flags=%x\n",
+       prIwReqInfo->cmd, prIwReqInfo->flags);
+    ECRNX_PRINT("mode=%x, flags=%x\n",
+       prIwReqData->mode, prIwReqData->data.flags);
+
+    prIwReqData->data.length = 6;
+    memcpy(pcExtra, "ecrnx", 6);
+    return 0;
+
+}
+
+static int priv_get_mac(IN struct net_device *prNetDev,
+     IN struct iw_request_info *prIwReqInfo,
+     IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+    struct sockaddr *dst = (struct sockaddr *) pcExtra;
+    struct ecrnx_vif *vif;
+    dbg_req_t req;
+
+    req.dbg_level = DBG_TYPE_D;
+    req.direct = 0;
+
+    vif = netdev_priv(prNetDev);
+    //send cmd to slave
+    ECRNX_PRINT("priv_get_mac: send cmd to slave \n");
+    host_send(&req, sizeof(dbg_req_t), TX_FLAG_IWPRIV_IE);
+    //wait for slave confirm
+    vif->rxdatas = 0;
+    wait_event_interruptible_timeout(vif->rxdataq, vif->rxdatas, 2*HZ);
+
+    ECRNX_PRINT("priv_get_mac: rx_len:%d \n", vif->rxlen);
+    if (!vif->rxlen)
+        return -1;
+
+    prIwReqData->data.length = vif->rxlen;
+    memcpy(dst->sa_data, vif->rxdata, vif->rxlen);
+    dst->sa_family = 1;
+    prIwReqData->data.length = 1;
+
+    return 0;
+}
+
+static int priv_get_vers(IN struct net_device *prNetDev,
+      IN struct iw_request_info *prIwReqInfo,
+      IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+    ECRNX_PRINT("get vers cmd=%x, flags=%x\n", prIwReqInfo->cmd, prIwReqInfo->flags);
+    ECRNX_PRINT("mode=%x, flags=%x\n", prIwReqData->mode, prIwReqData->data.flags);
+
+   memcpy(pcExtra, "1.0.1", 6);
+   prIwReqData->data.length = 6;
+
+    return 0;
+}
+
+
+static int priv_set_debug_level(IN struct net_device *prNetDev,
+      IN struct iw_request_info *prIwReqInfo,
+      IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+    ECRNX_PRINT("priv_set_debug_level cmd=%x, flags=%x\n",
+    prIwReqInfo->cmd, prIwReqInfo->flags);
+    ECRNX_PRINT("mode=%x, flags=%x\n",
+    prIwReqData->mode, prIwReqData->data.flags);
+
+    ECRNX_PRINT("param_value:%d \n", prIwReqData->param.value);
+
+    ecrnx_dbg_level = prIwReqData->param.value;
+    return 0;
+}
+#endif
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+static int priv_amt(IN struct net_device *prNetDev,
+     IN struct iw_request_info *prIwReqInfo,
+     IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+    struct sockaddr *dst = (struct sockaddr *) pcExtra;
+
+       if (amt_mode == false) {
+               ECRNX_ERR(" The current mode does not support the AMT commands!!\n");
+               return -1;
+       }
+//     printk("buff:%s, len:%d\n", prIwReqData->data.pointer, prIwReqData->data.length);
+    //send cmd to slave
+    char *reqdata = kzalloc(prIwReqData->data.length, GFP_KERNEL);
+    if (!reqdata){
+        return 0;
+    }
+    if (copy_from_user(reqdata, prIwReqData->data.pointer, prIwReqData->data.length)) {
+        return 0;
+    }
+    host_send(reqdata, prIwReqData->data.length, TX_FLAG_AMT_IWPRIV_IE);
+    kfree(reqdata);
+
+    //wait for slave confirm
+    amt_vif.rxdatas = 0;
+       amt_vif.rxlen = 0;
+
+    wait_event_interruptible_timeout(amt_vif.rxdataq, amt_vif.rxdatas, 2*HZ);
+
+    ECRNX_PRINT("rxdatas: rx_len:%d, rxdata:[%s]\n", amt_vif.rxlen,amt_vif.rxdata);
+    if (!amt_vif.rxdatas){
+        return -1;
+    }
+
+       prIwReqData->data.length = amt_vif.rxlen;
+       memcpy(dst->sa_data, amt_vif.rxdata, amt_vif.rxlen);
+       dst->sa_family = 1;
+       memcpy(pcExtra, amt_vif.rxdata, amt_vif.rxlen);
+
+    return 0;
+}
+#endif
+
+static struct ecrnx_vif *get_priv_vif(struct ecrnx_hw *ecrnx_hw)
+{
+     return ecrnx_hw->vif_table[0];
+}
+
+void priv_copy_data_wakeup(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+    struct ecrnx_vif* ecrnx_vif = get_priv_vif(ecrnx_hw);
+
+    ECRNX_PRINT("iw_cfm vif_start:%d, vif_monitor:%d \n", ecrnx_hw->vif_started, ecrnx_hw->monitor_vif);
+    //print_hex_dump(KERN_INFO, DBG_PREFIX_IW_CFM, DUMP_PREFIX_ADDRESS, 32, 1, skb->data, skb->len, false);
+    if (ECRNX_RXSIZE > skb->len) {
+        ecrnx_vif->rxlen = skb->len;
+    } else {
+        ecrnx_vif->rxlen = ECRNX_RXSIZE;
+    }
+
+    memcpy(ecrnx_vif->rxdata, skb->data, ecrnx_vif->rxlen);
+    ecrnx_vif->rxdatas = 1;
+    wake_up(&ecrnx_vif->rxdataq);
+}
+
+static int priv_wd(IN struct net_device *prNetDev,
+     IN struct iw_request_info *prIwReqInfo,
+     IN union iwreq_data *prIwReqData, IN OUT char *pcExtra)
+{
+    struct sockaddr *dst = (struct sockaddr *) pcExtra;
+    struct ecrnx_vif *vif;
+
+    //printk("priv_wd:%s, len:%d\n", prIwReqData->data.pointer, prIwReqData->data.length);
+    //send cmd to slave
+    char *reqdata = kzalloc(prIwReqData->data.length, GFP_KERNEL);
+    if (!reqdata){
+        return 0;
+    }
+
+    if (copy_from_user(reqdata, prIwReqData->data.pointer, prIwReqData->data.length)) {
+        return 0;
+    }
+    host_send(reqdata, prIwReqData->data.length, TX_FLAG_IWPRIV_IE);
+    kfree(reqdata);
+
+    //wait for slave confirm
+    vif = netdev_priv(prNetDev);
+    vif = get_priv_vif(vif->ecrnx_hw);
+    vif->rxdatas = 0;
+    wait_event_interruptible_timeout(vif->rxdataq, vif->rxdatas, 2*HZ);
+
+    if (!vif->rxdatas)
+        return -1;
+    
+    ECRNX_PRINT("priv_wd: rx_len:%d rxdata:[%s]\n", vif->rxlen, vif->rxdata);
+    prIwReqData->data.length = vif->rxlen;
+    memcpy(dst->sa_data, vif->rxdata, vif->rxlen);
+    dst->sa_family = 1;
+    memcpy(pcExtra, vif->rxdata, vif->rxlen);
+
+    return 0;
+}
+
+/*
+ * Structures to export the Wireless Handlers
+ */
+static const struct iw_priv_args ecrnx_wext_private_args[] = {
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+       {IOCTL_IWPRIV_AMT, IW_PRIV_TYPE_CHAR | 2000, IW_PRIV_TYPE_CHAR | 2000, "amt"},
+#endif
+    {IOCTL_IWPRIV_WD, IW_PRIV_TYPE_CHAR | 2000, IW_PRIV_TYPE_CHAR | 2000, "wd"},
+};
+
+const iw_handler ecrnx_wext_private_handler[] = {
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+    [IOCTL_IWPRIV_AMT - SIOCIWFIRSTPRIV] = priv_amt,
+#endif
+    [IOCTL_IWPRIV_WD - SIOCIWFIRSTPRIV] = priv_wd,
+};
+
+#endif
+
+/*------------------------------------------------------------------*/
+/*
+* Commit handler : called after a bunch of SET operations
+*/
+static int ecrnx_wext_config_commit(struct net_device *dev,
+               struct iw_request_info *info, /* NULL */
+               void *zwrq,           /* NULL */
+               char *extra)          /* NULL */
+{
+    return 1;
+}
+
+static int ecrnx_wext_get_name(struct net_device *dev,
+        struct iw_request_info *info,
+        char *cwrq,
+        char *extra)
+{
+    strcpy(cwrq, "IEEE 802.11");
+    return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set frequency
+ */
+static int ecrnx_wext_set_freq(struct net_device *dev,
+             struct iw_request_info *info,
+             struct iw_freq *fwrq,
+             char *extra)
+{
+    int rc = -EINPROGRESS;      /* Call commit handler */
+    ECRNX_PRINT("fwrq->e:%d, fwrq->m:%d \n", fwrq->e, fwrq->m);
+    return rc;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get frequency
+ */
+static int ecrnx_wext_get_freq(struct net_device *dev,
+             struct iw_request_info *info,
+             struct iw_freq *fwrq,
+             char *extra)
+{
+     fwrq->m = 100000 *
+            2.412;
+     fwrq->e = 1;
+    return 0;
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : set Mode of Operation
+ */
+static int ecrnx_wext_set_mode(struct net_device *dev,
+             struct iw_request_info *info,
+             __u32 *uwrq,
+             char *extra)
+{
+   ECRNX_PRINT("*uwrq:%d \n", *uwrq);
+    return -EINPROGRESS;        /* Call commit handler */
+}
+
+/*------------------------------------------------------------------*/
+/*
+ * Wireless Handler : get Mode of Operation
+ */
+static int ecrnx_wext_get_mode(struct net_device *dev,
+             struct iw_request_info *info,
+             __u32 *uwrq,
+             char *extra)
+{
+   *uwrq = 0xFFEE;
+    return 0;
+}
+
+static const iw_handler     ecrnx_wext_handler[] =
+{
+    (iw_handler) ecrnx_wext_config_commit,    /* SIOCSIWCOMMIT */
+    (iw_handler) ecrnx_wext_get_name,     /* SIOCGIWNAME */
+     (iw_handler) NULL,          /* SIOCSIWNWID */
+    (iw_handler) NULL,          /* SIOCGIWNWID */
+    (iw_handler) ecrnx_wext_set_freq,     /* SIOCSIWFREQ */
+    (iw_handler) ecrnx_wext_get_freq,     /* SIOCGIWFREQ */
+    (iw_handler) ecrnx_wext_set_mode,     /* SIOCSIWMODE */
+    (iw_handler) ecrnx_wext_get_mode,     /* SIOCGIWMODE */
+};
+
+const struct iw_handler_def  ecrnx_wext_handler_def =
+{
+    .num_standard   = ARRAY_SIZE(ecrnx_wext_handler),
+    .standard   = ecrnx_wext_handler,
+#ifdef CONFIG_WEXT_PRIV
+    .num_private    = ARRAY_SIZE(ecrnx_wext_private_handler),
+    .num_private_args = ARRAY_SIZE(ecrnx_wext_private_args),
+    .private    = ecrnx_wext_private_handler,
+    .private_args   = ecrnx_wext_private_args,
+#endif
+};
+#endif
diff --git a/drivers/net/wireless/eswin/ecrnx_mod_params.c b/drivers/net/wireless/eswin/ecrnx_mod_params.c
new file mode 100644 (file)
index 0000000..5d6a722
--- /dev/null
@@ -0,0 +1,1408 @@
+/**
+******************************************************************************
+*
+* @file ecrnx_mod_params.c
+*
+* @brief Set configuration according to modules parameters
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+#include <linux/module.h>
+#include <linux/rtnetlink.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_tx.h"
+#include "hal_desc.h"
+#include "ecrnx_cfgfile.h"
+#include "reg_access.h"
+#include "ecrnx_compat.h"
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define COMMON_PARAM(name, default_softmac, default_fullmac)    \
+    .name = default_softmac,
+#define SOFTMAC_PARAM(name, default) .name = default,
+#define FULLMAC_PARAM(name, default)
+#else
+#define COMMON_PARAM(name, default_softmac, default_fullmac)    \
+    .name = default_fullmac,
+#define SOFTMAC_PARAM(name, default)
+#define FULLMAC_PARAM(name, default) .name = default,
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+struct ecrnx_mod_params ecrnx_mod_params = {
+    /* common parameters */
+    COMMON_PARAM(ht_on, true, true)
+    COMMON_PARAM(vht_on, false, false)
+    COMMON_PARAM(he_on, true, true)
+#ifdef CONFIG_6600_HAL
+    COMMON_PARAM(he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_7, IEEE80211_HE_MCS_SUPPORT_0_7)
+#else
+    COMMON_PARAM(he_mcs_map, IEEE80211_HE_MCS_SUPPORT_0_9, IEEE80211_HE_MCS_SUPPORT_0_9)
+#endif
+    COMMON_PARAM(he_ul_on, false, false)
+    COMMON_PARAM(ldpc_on, false, false)
+    COMMON_PARAM(stbc_on, false, false)
+    COMMON_PARAM(gf_rx_on, true, true)
+    COMMON_PARAM(phy_cfg, 0, 0)
+    COMMON_PARAM(uapsd_timeout, 300, 300)
+    COMMON_PARAM(ap_uapsd_on, true, true)
+    COMMON_PARAM(sgi, true, true)
+    COMMON_PARAM(sgi80, true, true)
+    COMMON_PARAM(use_2040, 1, 1)
+    COMMON_PARAM(nss, 1, 1)
+    COMMON_PARAM(amsdu_rx_max, 2, 2)
+    COMMON_PARAM(bfmee, true, true)
+    COMMON_PARAM(bfmer, true, true)
+    COMMON_PARAM(mesh, true, true)
+    COMMON_PARAM(murx, true, true)
+    COMMON_PARAM(mutx, true, true)
+    COMMON_PARAM(mutx_on, true, true)
+    COMMON_PARAM(use_80, false, false)
+    COMMON_PARAM(custregd, false, false)
+    COMMON_PARAM(custchan, false, false)
+    COMMON_PARAM(roc_dur_max, 500, 500)
+    COMMON_PARAM(listen_itv, 0, 0)
+    COMMON_PARAM(listen_bcmc, true, true)
+    COMMON_PARAM(lp_clk_ppm, 20, 20)
+    COMMON_PARAM(ps_on, true, true)
+    COMMON_PARAM(tx_lft, ECRNX_TX_LIFETIME_MS, ECRNX_TX_LIFETIME_MS)
+    COMMON_PARAM(amsdu_maxnb, NX_TX_PAYLOAD_MAX, NX_TX_PAYLOAD_MAX)
+    // By default, only enable UAPSD for Voice queue (see IEEE80211_DEFAULT_UAPSD_QUEUE comment)
+    COMMON_PARAM(uapsd_queues, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO, IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+    COMMON_PARAM(tdls, true, true)
+    COMMON_PARAM(uf, true, true)
+    COMMON_PARAM(ftl, "", "")
+    COMMON_PARAM(dpsm, true, true)
+    COMMON_PARAM(tx_to_bk, 0, 0)
+    COMMON_PARAM(tx_to_be, 0, 0)
+    COMMON_PARAM(tx_to_vi, 0, 0)
+    COMMON_PARAM(tx_to_vo, 0, 0)
+
+    /* SOFTMAC only parameters */
+    SOFTMAC_PARAM(mfp_on, false)
+    SOFTMAC_PARAM(gf_on, false)
+    SOFTMAC_PARAM(bwsig_on, true)
+    SOFTMAC_PARAM(dynbw_on, true)
+    SOFTMAC_PARAM(agg_tx, true)
+    SOFTMAC_PARAM(amsdu_force, 0)
+    SOFTMAC_PARAM(rc_probes_on, false)
+    SOFTMAC_PARAM(cmon, true)
+    SOFTMAC_PARAM(hwscan, true)
+    SOFTMAC_PARAM(autobcn, true)
+
+    /* FULLMAC only parameters */
+    FULLMAC_PARAM(ant_div, false)
+};
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+/* SOFTMAC specific parameters */
+module_param_named(mfp_on, ecrnx_mod_params.mfp_on, bool, S_IRUGO);
+MODULE_PARM_DESC(mfp_on, "Enable MFP (11w) (Default: 0)");
+
+module_param_named(gf_on, ecrnx_mod_params.gf_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(gf_on, "Try TXing Green Field if peer supports it (Default: 0)");
+
+module_param_named(bwsig_on, ecrnx_mod_params.bwsig_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(bwsig_on, "Enable bandwidth signaling (VHT tx) (Default: 1)");
+
+module_param_named(dynbw_on, ecrnx_mod_params.dynbw_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(dynbw_on, "Enable dynamic bandwidth (VHT tx) (Default: 1)");
+
+module_param_named(agg_tx, ecrnx_mod_params.agg_tx, bool, S_IRUGO);
+MODULE_PARM_DESC(agg_tx, "Use A-MPDU in TX (Default: 1)");
+
+module_param_named(amsdu_force, ecrnx_mod_params.amsdu_force, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(amsdu_force, "Use A-MSDU in TX: 0-if advertised, 1-yes, 2-no (Default: 0)");
+
+module_param_named(rc_probes_on, ecrnx_mod_params.rc_probes_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(rc_probes_on, "IEEE80211_TX_CTL_RATE_CTRL_PROBE is 1st in AMPDU (Default: 0)");
+
+module_param_named(cmon, ecrnx_mod_params.cmon, bool, S_IRUGO);
+MODULE_PARM_DESC(cmon, "Connection monitoring work handled by the FW (Default: 1)");
+
+module_param_named(hwscan, ecrnx_mod_params.hwscan, bool, S_IRUGO);
+MODULE_PARM_DESC(hwscan, "Scan work handled by the FW (Default: 1)");
+
+module_param_named(autobcn, ecrnx_mod_params.autobcn, bool, S_IRUGO);
+MODULE_PARM_DESC(autobcn, "Beacon transmission done autonomously by the FW (Default: 1)");
+
+#else
+/* FULLMAC specific parameters*/
+module_param_named(ant_div, ecrnx_mod_params.ant_div, bool, S_IRUGO);
+MODULE_PARM_DESC(ant_div, "Enable Antenna Diversity (Default: 0)");
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+module_param_named(ht_on, ecrnx_mod_params.ht_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ht_on, "Enable HT (Default: 1)");
+
+module_param_named(vht_on, ecrnx_mod_params.vht_on, bool, S_IRUGO);
+MODULE_PARM_DESC(vht_on, "Enable VHT (Default: 1)");
+
+module_param_named(he_on, ecrnx_mod_params.he_on, bool, S_IRUGO);
+MODULE_PARM_DESC(he_on, "Enable HE (Default: 1)");
+
+module_param_named(mcs_map, ecrnx_mod_params.mcs_map, int, S_IRUGO);
+MODULE_PARM_DESC(mcs_map,  "VHT MCS map value  0: MCS0_7, 1: MCS0_8, 2: MCS0_9"
+                 " (Default: 2)");
+
+module_param_named(he_mcs_map, ecrnx_mod_params.he_mcs_map, int, S_IRUGO);
+MODULE_PARM_DESC(he_mcs_map,  "HE MCS map value  0: MCS0_7, 1: MCS0_9, 2: MCS0_11"
+                 " (Default: 2)");
+
+module_param_named(he_ul_on, ecrnx_mod_params.he_ul_on, bool, S_IRUGO);
+MODULE_PARM_DESC(he_ul_on, "Enable HE OFDMA UL (Default: 0)");
+
+module_param_named(amsdu_maxnb, ecrnx_mod_params.amsdu_maxnb, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(amsdu_maxnb, "Maximum number of MSDUs inside an A-MSDU in TX: (Default: NX_TX_PAYLOAD_MAX)");
+
+module_param_named(ps_on, ecrnx_mod_params.ps_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ps_on, "Enable PowerSaving (Default: 1-Enabled)");
+
+module_param_named(tx_lft, ecrnx_mod_params.tx_lft, int, 0644);
+MODULE_PARM_DESC(tx_lft, "Tx lifetime (ms) - setting it to 0 disables retries "
+                 "(Default: "__stringify(ECRNX_TX_LIFETIME_MS)")");
+
+module_param_named(ldpc_on, ecrnx_mod_params.ldpc_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ldpc_on, "Enable LDPC (Default: 1)");
+
+module_param_named(stbc_on, ecrnx_mod_params.stbc_on, bool, S_IRUGO);
+MODULE_PARM_DESC(stbc_on, "Enable STBC in RX (Default: 1)");
+
+module_param_named(gf_rx_on, ecrnx_mod_params.gf_rx_on, bool, S_IRUGO);
+MODULE_PARM_DESC(gf_rx_on, "Enable HT greenfield in reception (Default: 1)");
+
+module_param_named(phycfg, ecrnx_mod_params.phy_cfg, int, S_IRUGO);
+MODULE_PARM_DESC(phycfg, "Main RF Path (Default: 0)");
+
+module_param_named(uapsd_timeout, ecrnx_mod_params.uapsd_timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uapsd_timeout,
+                 "UAPSD Timer timeout, in ms (Default: 300). If 0, UAPSD is disabled");
+
+module_param_named(uapsd_queues, ecrnx_mod_params.uapsd_queues, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uapsd_queues, "UAPSD Queues, integer value, must be seen as a bitfield\n"
+                 "        Bit 0 = VO\n"
+                 "        Bit 1 = VI\n"
+                 "        Bit 2 = BK\n"
+                 "        Bit 3 = BE\n"
+                 "     -> uapsd_queues=7 will enable uapsd for VO, VI and BK queues");
+
+module_param_named(ap_uapsd_on, ecrnx_mod_params.ap_uapsd_on, bool, S_IRUGO);
+MODULE_PARM_DESC(ap_uapsd_on, "Enable UAPSD in AP mode (Default: 1)");
+
+module_param_named(sgi, ecrnx_mod_params.sgi, bool, S_IRUGO);
+MODULE_PARM_DESC(sgi, "Advertise Short Guard Interval support (Default: 1)");
+
+module_param_named(sgi80, ecrnx_mod_params.sgi80, bool, S_IRUGO);
+MODULE_PARM_DESC(sgi80, "Advertise Short Guard Interval support for 80MHz (Default: 1)");
+
+module_param_named(use_2040, ecrnx_mod_params.use_2040, bool, S_IRUGO);
+MODULE_PARM_DESC(use_2040, "Enable 40MHz (Default: 1)");
+
+module_param_named(use_80, ecrnx_mod_params.use_80, bool, S_IRUGO);
+MODULE_PARM_DESC(use_80, "Enable 80MHz (Default: 1)");
+
+module_param_named(custregd, ecrnx_mod_params.custregd, bool, S_IRUGO);
+MODULE_PARM_DESC(custregd,
+                 "Use permissive custom regulatory rules (for testing ONLY) (Default: 0)");
+
+module_param_named(custchan, ecrnx_mod_params.custchan, bool, S_IRUGO);
+MODULE_PARM_DESC(custchan,
+                 "Extend channel set to non-standard channels (for testing ONLY) (Default: 0)");
+
+module_param_named(nss, ecrnx_mod_params.nss, int, S_IRUGO);
+MODULE_PARM_DESC(nss, "1 <= nss <= 2 : Supported number of Spatial Streams (Default: 2)");
+
+module_param_named(amsdu_rx_max, ecrnx_mod_params.amsdu_rx_max, int, S_IRUGO);
+MODULE_PARM_DESC(amsdu_rx_max, "0 <= amsdu_rx_max <= 2 : Maximum A-MSDU size supported in RX\n"
+                 "        0: 3895 bytes\n"
+                 "        1: 7991 bytes\n"
+                 "        2: 11454 bytes\n"
+                 "        This value might be reduced according to the FW capabilities.\n"
+                 "        Default: 2");
+
+module_param_named(bfmee, ecrnx_mod_params.bfmee, bool, S_IRUGO);
+MODULE_PARM_DESC(bfmee, "Enable Beamformee Capability (Default: 1-Enabled)");
+
+module_param_named(bfmer, ecrnx_mod_params.bfmer, bool, S_IRUGO);
+MODULE_PARM_DESC(bfmer, "Enable Beamformer Capability (Default: 1-Enabled)");
+
+module_param_named(mesh, ecrnx_mod_params.mesh, bool, S_IRUGO);
+MODULE_PARM_DESC(mesh, "Enable Meshing Capability (Default: 1-Enabled)");
+
+module_param_named(murx, ecrnx_mod_params.murx, bool, S_IRUGO);
+MODULE_PARM_DESC(murx, "Enable MU-MIMO RX Capability (Default: 1-Enabled)");
+
+module_param_named(mutx, ecrnx_mod_params.mutx, bool, S_IRUGO);
+MODULE_PARM_DESC(mutx, "Enable MU-MIMO TX Capability (Default: 1-Enabled)");
+
+module_param_named(mutx_on, ecrnx_mod_params.mutx_on, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(mutx_on, "Enable MU-MIMO transmissions (Default: 1-Enabled)");
+
+module_param_named(roc_dur_max, ecrnx_mod_params.roc_dur_max, int, S_IRUGO);
+MODULE_PARM_DESC(roc_dur_max, "Maximum Remain on Channel duration");
+
+module_param_named(listen_itv, ecrnx_mod_params.listen_itv, int, S_IRUGO);
+MODULE_PARM_DESC(listen_itv, "Maximum listen interval");
+
+module_param_named(listen_bcmc, ecrnx_mod_params.listen_bcmc, bool, S_IRUGO);
+MODULE_PARM_DESC(listen_bcmc, "Wait for BC/MC traffic following DTIM beacon");
+
+module_param_named(lp_clk_ppm, ecrnx_mod_params.lp_clk_ppm, int, S_IRUGO);
+MODULE_PARM_DESC(lp_clk_ppm, "Low Power Clock accuracy of the local device");
+
+module_param_named(tdls, ecrnx_mod_params.tdls, bool, S_IRUGO);
+MODULE_PARM_DESC(tdls, "Enable TDLS (Default: 1-Enabled)");
+
+module_param_named(uf, ecrnx_mod_params.uf, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(uf, "Enable Unsupported HT Frame Logging (Default: 1-Enabled)");
+
+module_param_named(ftl, ecrnx_mod_params.ftl, charp, S_IRUGO);
+MODULE_PARM_DESC(ftl, "Firmware trace level  (Default: \"\")");
+
+module_param_named(dpsm, ecrnx_mod_params.dpsm, bool, S_IRUGO);
+MODULE_PARM_DESC(dpsm, "Enable Dynamic PowerSaving (Default: 1-Enabled)");
+module_param_named(tx_to_bk, ecrnx_mod_params.tx_to_bk, int, S_IRUGO);
+MODULE_PARM_DESC(tx_to_bk,
+     "TX timeout for BK, in ms (Default: 0, Max: 65535). If 0, default value is applied");
+module_param_named(tx_to_be, ecrnx_mod_params.tx_to_be, int, S_IRUGO);
+MODULE_PARM_DESC(tx_to_be,
+     "TX timeout for BE, in ms (Default: 0, Max: 65535). If 0, default value is applied");
+module_param_named(tx_to_vi, ecrnx_mod_params.tx_to_vi, int, S_IRUGO);
+MODULE_PARM_DESC(tx_to_vi,
+     "TX timeout for VI, in ms (Default: 0, Max: 65535). If 0, default value is applied");
+module_param_named(tx_to_vo, ecrnx_mod_params.tx_to_vo, int, S_IRUGO);
+MODULE_PARM_DESC(tx_to_vo,
+     "TX timeout for VO, in ms (Default: 0, Max: 65535). If 0, default value is applied");
+
+/* Regulatory rules */
+static struct ieee80211_regdomain ecrnx_regdom = {
+    .n_reg_rules = 2,
+    .alpha2 = "99",
+    .reg_rules = {
+        REG_RULE(2390 - 10, 2510 + 10, 40, 0, 1000, 0),
+        REG_RULE(5150 - 10, 5970 + 10, 80, 0, 1000, 0),
+    }
+};
+
+static const int mcs_map_to_rate[4][3] = {
+    [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_7] = 65,
+    [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_8] = 78,
+    [PHY_CHNL_BW_20][IEEE80211_VHT_MCS_SUPPORT_0_9] = 78,
+    [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_7] = 135,
+    [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_8] = 162,
+    [PHY_CHNL_BW_40][IEEE80211_VHT_MCS_SUPPORT_0_9] = 180,
+    [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_7] = 292,
+    [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_8] = 351,
+    [PHY_CHNL_BW_80][IEEE80211_VHT_MCS_SUPPORT_0_9] = 390,
+    [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_7] = 585,
+    [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_8] = 702,
+    [PHY_CHNL_BW_160][IEEE80211_VHT_MCS_SUPPORT_0_9] = 780,
+};
+
+#define MAX_VHT_RATE(map, nss, bw) (mcs_map_to_rate[bw][map] * (nss))
+
+#if defined(CONFIG_ECRNX_HE)
+struct ieee80211_sta_he_cap ecrnx_he_cap;
+#endif
+
+/**
+ * Do some sanity check
+ *
+ */
+static int ecrnx_check_fw_hw_feature(struct ecrnx_hw *ecrnx_hw,
+                                    struct wiphy *wiphy)
+{
+    u32_l sys_feat = ecrnx_hw->version_cfm.features;
+    u32_l mac_feat = ecrnx_hw->version_cfm.version_machw_1;
+    u32_l phy_feat = ecrnx_hw->version_cfm.version_phy_1;
+    u32_l phy_vers = ecrnx_hw->version_cfm.version_phy_2;
+    u16_l max_sta_nb = ecrnx_hw->version_cfm.max_sta_nb;
+    u8_l max_vif_nb = ecrnx_hw->version_cfm.max_vif_nb;
+    int bw, res = 0;
+    int amsdu_rx;
+
+    if (!ecrnx_hw->mod_params->custregd)
+        ecrnx_hw->mod_params->custchan = false;
+
+    if (ecrnx_hw->mod_params->custchan) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+        ecrnx_hw->mod_params->autobcn = false;
+        ecrnx_hw->mod_params->hwscan = false;
+        sys_feat &= ~BIT(MM_FEAT_CMON_BIT);
+#endif
+        ecrnx_hw->mod_params->mesh = false;
+        ecrnx_hw->mod_params->tdls = false;
+    }
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    if (sys_feat & BIT(MM_FEAT_UMAC_BIT)) {
+        wiphy_err(wiphy, "Loading fullmac firmware with softmac driver\n");
+        res = -1;
+    }
+
+    if (sys_feat & BIT(MM_FEAT_AUTOBCN_BIT) &&
+        !ecrnx_hw->mod_params->autobcn) {
+        wiphy_err(wiphy,
+                  "Auto beacon enabled in firmware but disabled in driver\n");
+        res = -1;
+    }
+
+    if (!!(sys_feat & BIT(MM_FEAT_HWSCAN_BIT)) !=
+        !!ecrnx_hw->mod_params->hwscan) {
+        wiphy_err(wiphy,
+                  "hwscan %sabled in firmware but %sabled in driver\n",
+                  (sys_feat & BIT(MM_FEAT_HWSCAN_BIT)) ? "en" : "dis",
+                   ecrnx_hw->mod_params->hwscan ? "en" : "dis");
+        res = -1;
+    }
+
+    if (sys_feat & BIT(MM_FEAT_CMON_BIT)) {
+        ieee80211_hw_set(ecrnx_hw->hw, CONNECTION_MONITOR);
+    }
+
+    /* AMPDU (non)support implies different shared structure definition
+       so insure that fw and drv have consistent compilation option */
+    if (sys_feat & BIT(MM_FEAT_AMPDU_BIT)) {
+#ifndef CONFIG_ECRNX_AGG_TX
+        wiphy_err(wiphy,
+                  "AMPDU enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_ECRNX_AGG_TX */
+    } else {
+#ifdef CONFIG_ECRNX_AGG_TX
+        wiphy_err(wiphy,
+                  "AMPDU disabled in firmware but support compiled in driver\n");
+        res = -1;
+#else
+        ecrnx_hw->mod_params->agg_tx = false;
+#endif /* CONFIG_ECRNX_AGG_TX */
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_DPSM_BIT))) {
+        ecrnx_hw->mod_params->dpsm = false;
+    }
+
+#else /* check for FULLMAC */
+
+    if (!(sys_feat & BIT(MM_FEAT_UMAC_BIT))) {
+        wiphy_err(wiphy,
+                  "Loading softmac firmware with fullmac driver\n");
+        res = -1;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_ANT_DIV_BIT))) {
+        ecrnx_hw->mod_params->ant_div = false;
+    }
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    if (!(sys_feat & BIT(MM_FEAT_VHT_BIT))) {
+        ecrnx_hw->mod_params->vht_on = false;
+    }
+
+    // Check if HE is supported
+    if (!(sys_feat & BIT(MM_FEAT_HE_BIT))) {
+        ecrnx_hw->mod_params->he_on = false;
+        ecrnx_hw->mod_params->he_ul_on = false;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_PS_BIT))) {
+        ecrnx_hw->mod_params->ps_on = false;
+    }
+
+    /* AMSDU (non)support implies different shared structure definition
+       so insure that fw and drv have consistent compilation option */
+    if (sys_feat & BIT(MM_FEAT_AMSDU_BIT)) {
+#ifndef CONFIG_ECRNX_SPLIT_TX_BUF
+        wiphy_err(wiphy,
+                  "AMSDU enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#else
+        /* Adjust amsdu_maxnb so that it stays in allowed bounds */
+        ecrnx_adjust_amsdu_maxnb(ecrnx_hw);
+#endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
+    } else {
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+        wiphy_err(wiphy,
+                  "AMSDU disabled in firmware but support compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_ECRNX_SPLIT_TX_BUF */
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_UAPSD_BIT))) {
+        ecrnx_hw->mod_params->uapsd_timeout = 0;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_BFMEE_BIT))) {
+        ecrnx_hw->mod_params->bfmee = false;
+    }
+
+    if ((sys_feat & BIT(MM_FEAT_BFMER_BIT))) {
+#ifndef CONFIG_ECRNX_BFMER
+        wiphy_err(wiphy,
+                  "BFMER enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_ECRNX_BFMER */
+        // Check PHY and MAC HW BFMER support and update parameter accordingly
+        if (!(phy_feat & MDM_BFMER_BIT) || !(mac_feat & NXMAC_BFMER_BIT)) {
+            ecrnx_hw->mod_params->bfmer = false;
+            // Disable the feature in the bitfield so that it won't be displayed
+            sys_feat &= ~BIT(MM_FEAT_BFMER_BIT);
+        }
+    } else {
+#ifdef CONFIG_ECRNX_BFMER
+        wiphy_err(wiphy,
+                  "BFMER disabled in firmware but support compiled in driver\n");
+        res = -1;
+#else
+        ecrnx_hw->mod_params->bfmer = false;
+#endif /* CONFIG_ECRNX_BFMER */
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_MESH_BIT))) {
+        ecrnx_hw->mod_params->mesh = false;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_TDLS_BIT))) {
+        ecrnx_hw->mod_params->tdls = false;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_UF_BIT))) {
+        ecrnx_hw->mod_params->uf = false;
+    }
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    if ((sys_feat & BIT(MM_FEAT_MON_DATA_BIT))) {
+#ifndef CONFIG_ECRNX_MON_DATA
+        wiphy_err(wiphy,
+                  "Monitor+Data interface support (MON_DATA) is enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_ECRNX_MON_DATA */
+    } else {
+#ifdef CONFIG_ECRNX_MON_DATA
+        wiphy_err(wiphy,
+                  "Monitor+Data interface support (MON_DATA) disabled in firmware but support compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_ECRNX_MON_DATA */
+    }
+#endif
+
+    // Check supported AMSDU RX size
+    amsdu_rx = (sys_feat >> MM_AMSDU_MAX_SIZE_BIT0) & 0x03;
+    if (amsdu_rx < ecrnx_hw->mod_params->amsdu_rx_max) {
+        ecrnx_hw->mod_params->amsdu_rx_max = amsdu_rx;
+    }
+
+    // Check supported BW
+    bw = (phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB;
+    // Check if 80MHz BW is supported
+    if (bw < 2) {
+        ecrnx_hw->mod_params->use_80 = false;
+    }
+    // Check if 40MHz BW is supported
+    if (bw < 1)
+        ecrnx_hw->mod_params->use_2040 = false;
+
+    // 80MHz BW shall be disabled if 40MHz is not enabled
+    if (!ecrnx_hw->mod_params->use_2040)
+        ecrnx_hw->mod_params->use_80 = false;
+
+    // Check if HT is supposed to be supported. If not, disable VHT/HE too
+    if (!ecrnx_hw->mod_params->ht_on)
+    {
+        ecrnx_hw->mod_params->vht_on = false;
+        ecrnx_hw->mod_params->he_on = false;
+        ecrnx_hw->mod_params->he_ul_on = false;
+        ecrnx_hw->mod_params->use_80 = false;
+        ecrnx_hw->mod_params->use_2040 = false;
+    }
+
+    // LDPC is mandatory for HE40 and above, so if LDPC is not supported, then disable
+    // HE to use HT/VHT only
+    if (ecrnx_hw->mod_params->he_on && !ecrnx_hw->mod_params->ldpc_on)
+    {
+        ecrnx_hw->mod_params->use_80 = false;
+        /* ESWIN turns off 40M automotically when it is HE mode  */
+        //ecrnx_hw->mod_params->use_2040 = false;
+
+    }
+
+    // HT greenfield is not supported in modem >= 3.0
+    if (__MDM_MAJOR_VERSION(phy_vers) > 0) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+        ecrnx_hw->mod_params->gf_on = false;
+#endif
+        ecrnx_hw->mod_params->gf_rx_on = false;
+    }
+
+    if (!(sys_feat & BIT(MM_FEAT_MU_MIMO_RX_BIT)) ||
+        !ecrnx_hw->mod_params->bfmee) {
+        ecrnx_hw->mod_params->murx = false;
+    }
+
+    if ((sys_feat & BIT(MM_FEAT_MU_MIMO_TX_BIT))) {
+#ifndef CONFIG_ECRNX_MUMIMO_TX
+        wiphy_err(wiphy,
+                  "MU-MIMO TX enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+        if (!ecrnx_hw->mod_params->bfmer)
+            ecrnx_hw->mod_params->mutx = false;
+        // Check PHY and MAC HW MU-MIMO TX support and update parameter accordingly
+        else if (!(phy_feat & MDM_MUMIMOTX_BIT) || !(mac_feat & NXMAC_MU_MIMO_TX_BIT)) {
+                ecrnx_hw->mod_params->mutx = false;
+                // Disable the feature in the bitfield so that it won't be displayed
+                sys_feat &= ~BIT(MM_FEAT_MU_MIMO_TX_BIT);
+        }
+    } else {
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+        wiphy_err(wiphy,
+                  "MU-MIMO TX disabled in firmware but support compiled in driver\n");
+        res = -1;
+#else
+        ecrnx_hw->mod_params->mutx = false;
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+    }
+
+    if (sys_feat & BIT(MM_FEAT_WAPI_BIT)) {
+        ecrnx_enable_wapi(ecrnx_hw);
+    }
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    if (sys_feat & BIT(MM_FEAT_MFP_BIT)) {
+        ecrnx_enable_mfp(ecrnx_hw);
+    }
+    if (mac_feat & NXMAC_GCMP_BIT) {
+        ecrnx_enable_gcmp(ecrnx_hw);
+    }
+#endif
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define QUEUE_NAME "BEACON queue "
+#else
+#define QUEUE_NAME "Broadcast/Multicast queue "
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    if (sys_feat & BIT(MM_FEAT_BCN_BIT)) {
+#if NX_TXQ_CNT == 4
+        wiphy_err(wiphy, QUEUE_NAME
+                  "enabled in firmware but support not compiled in driver\n");
+        res = -1;
+#endif /* NX_TXQ_CNT == 4 */
+    } else {
+#if NX_TXQ_CNT == 5
+        wiphy_err(wiphy, QUEUE_NAME
+                  "disabled in firmware but support compiled in driver\n");
+        res = -1;
+#endif /* NX_TXQ_CNT == 5 */
+    }
+#undef QUEUE_NAME
+
+#ifdef CONFIG_ECRNX_RADAR
+    if (sys_feat & BIT(MM_FEAT_RADAR_BIT)) {
+        /* Enable combination with radar detection */
+        wiphy->n_iface_combinations++;
+    }
+#endif /* CONFIG_ECRNX_RADAR */
+
+#ifndef CONFIG_ECRNX_SDM
+    switch (__MDM_PHYCFG_FROM_VERS(phy_feat)) {
+        case MDM_PHY_CONFIG_TRIDENT:
+            ecrnx_hw->mod_params->nss = 1;
+            if ((ecrnx_hw->mod_params->phy_cfg < 0) || (ecrnx_hw->mod_params->phy_cfg > 2))
+                ecrnx_hw->mod_params->phy_cfg = 2;
+            break;
+        case MDM_PHY_CONFIG_KARST:
+        case MDM_PHY_CONFIG_CATAXIA:
+            {
+                int nss_supp = (phy_feat & MDM_NSS_MASK) >> MDM_NSS_LSB;
+                if (ecrnx_hw->mod_params->nss > nss_supp)
+                    ecrnx_hw->mod_params->nss = nss_supp;
+                if ((ecrnx_hw->mod_params->phy_cfg < 0) || (ecrnx_hw->mod_params->phy_cfg > 1))
+                    ecrnx_hw->mod_params->phy_cfg = 0;
+            }
+            break;
+        default:
+            WARN_ON(1);
+            break;
+    }
+#endif /* CONFIG_ECRNX_SDM */
+
+    if ((ecrnx_hw->mod_params->nss < 1) || (ecrnx_hw->mod_params->nss > 2))
+        ecrnx_hw->mod_params->nss = 1;
+
+
+    if ((ecrnx_hw->mod_params->mcs_map < 0) || (ecrnx_hw->mod_params->mcs_map > 2))
+        ecrnx_hw->mod_params->mcs_map = 0;
+
+#define PRINT_ECRNX_PHY_FEAT(feat)                                   \
+    (phy_feat & MDM_##feat##_BIT ? "["#feat"]" : "")
+    wiphy_info(wiphy, "PHY features: [NSS=%d][CHBW=%d]%s%s%s%s%s%s%s\n",
+               (phy_feat & MDM_NSS_MASK) >> MDM_NSS_LSB,
+               20 * (1 << ((phy_feat & MDM_CHBW_MASK) >> MDM_CHBW_LSB)),
+               (phy_feat & (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) ==
+                       (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT) ? "[LDPC]" : "",
+               PRINT_ECRNX_PHY_FEAT(VHT),
+               PRINT_ECRNX_PHY_FEAT(HE),
+               PRINT_ECRNX_PHY_FEAT(BFMER),
+               PRINT_ECRNX_PHY_FEAT(BFMEE),
+               PRINT_ECRNX_PHY_FEAT(MUMIMOTX),
+               PRINT_ECRNX_PHY_FEAT(MUMIMORX)
+               );
+
+#define PRINT_ECRNX_FEAT(feat)                                   \
+    (sys_feat & BIT(MM_FEAT_##feat##_BIT) ? "["#feat"]" : "")
+
+    wiphy_info(wiphy, "FW features: %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+               PRINT_ECRNX_FEAT(BCN),
+               PRINT_ECRNX_FEAT(AUTOBCN),
+               PRINT_ECRNX_FEAT(HWSCAN),
+               PRINT_ECRNX_FEAT(CMON),
+               PRINT_ECRNX_FEAT(MROLE),
+               PRINT_ECRNX_FEAT(RADAR),
+               PRINT_ECRNX_FEAT(PS),
+               PRINT_ECRNX_FEAT(UAPSD),
+               PRINT_ECRNX_FEAT(DPSM),
+               PRINT_ECRNX_FEAT(AMPDU),
+               PRINT_ECRNX_FEAT(AMSDU),
+               PRINT_ECRNX_FEAT(CHNL_CTXT),
+               PRINT_ECRNX_FEAT(REORD),
+               PRINT_ECRNX_FEAT(P2P),
+               PRINT_ECRNX_FEAT(P2P_GO),
+               PRINT_ECRNX_FEAT(UMAC),
+               PRINT_ECRNX_FEAT(VHT),
+               PRINT_ECRNX_FEAT(HE),
+               PRINT_ECRNX_FEAT(BFMEE),
+               PRINT_ECRNX_FEAT(BFMER),
+               PRINT_ECRNX_FEAT(WAPI),
+               PRINT_ECRNX_FEAT(MFP),
+               PRINT_ECRNX_FEAT(MU_MIMO_RX),
+               PRINT_ECRNX_FEAT(MU_MIMO_TX),
+               PRINT_ECRNX_FEAT(MESH),
+               PRINT_ECRNX_FEAT(TDLS),
+               PRINT_ECRNX_FEAT(ANT_DIV),
+               PRINT_ECRNX_FEAT(UF),
+               PRINT_ECRNX_FEAT(TWT));
+#undef PRINT_ECRNX_FEAT
+
+    if(max_sta_nb != NX_REMOTE_STA_MAX)
+    {
+        wiphy_err(wiphy, "Different number of supported stations between driver and FW (%d != %d)\n",
+                  NX_REMOTE_STA_MAX, max_sta_nb);
+        res = -1;
+    }
+
+    if(max_vif_nb != NX_VIRT_DEV_MAX)
+    {
+        wiphy_err(wiphy, "Different number of supported virtual interfaces between driver and FW (%d != %d)\n",
+                  NX_VIRT_DEV_MAX, max_vif_nb);
+        res = -1;
+    }
+
+    return res;
+}
+
+static void ecrnx_set_ppe_threshold(struct ecrnx_hw *ecrnx_hw,
+                                   struct ieee80211_sta_he_cap *he_cap)
+{
+    const u8_l PPE_THRES_INFO_OFT = 7;
+    const u8_l PPE_THRES_INFO_BIT_LEN = 6;
+    struct ppe_thres_info_tag
+    {
+        u8_l ppet16 : 3;
+        u8_l ppet8 : 3;
+    }__packed;
+    struct ppe_thres_field_tag
+    {
+        u8_l nsts : 3;
+        u8_l ru_idx_bmp : 4;
+    };
+    int nss = ecrnx_hw->mod_params->nss;
+    struct ppe_thres_field_tag* ppe_thres_field = (struct ppe_thres_field_tag*) he_cap->ppe_thres;
+    struct ppe_thres_info_tag ppe_thres_info = {.ppet16 = 0, //BSPK
+                                                .ppet8 = 7 //None
+                                               };
+    u8_l* ppe_thres_info_ptr = (u8_l*) &ppe_thres_info;
+    u16_l* ppe_thres_ptr = (u16_l*) he_cap->ppe_thres;
+    u8_l i, j, cnt, offset;
+    if (ecrnx_hw->mod_params->use_80)
+    {
+        ppe_thres_field->ru_idx_bmp = 7;
+        cnt = 3;
+    }
+    else
+    {
+        ppe_thres_field->ru_idx_bmp = 1;
+        cnt = 1;
+    }
+    ppe_thres_field->nsts = nss - 1;
+    for (i = 0; i < nss ; i++)
+    {
+        for (j = 0; j < cnt; j++){
+            offset = (i * cnt + j) * PPE_THRES_INFO_BIT_LEN + PPE_THRES_INFO_OFT;
+            ppe_thres_ptr = (u16_l*)&he_cap->ppe_thres[offset / 8];
+            *ppe_thres_ptr |= *ppe_thres_info_ptr << (offset % 8);
+        }
+    }
+}
+
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+static void ecrnx_set_softmac_flags(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ieee80211_hw *hw = ecrnx_hw->hw;
+    int nss;
+#ifdef CONFIG_MAC80211_AMSDUS_TX
+    ieee80211_hw_set(hw, TX_AMSDU);
+    ieee80211_hw_set(hw, TX_FRAG_LIST);
+    hw->max_tx_fragments = ecrnx_hw->mod_params->amsdu_maxnb;
+#endif
+
+    if (!ecrnx_hw->mod_params->autobcn)
+        ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
+
+    if (ecrnx_hw->mod_params->agg_tx)
+        ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+
+    if (ecrnx_hw->mod_params->cmon)
+        ieee80211_hw_set(hw, CONNECTION_MONITOR);
+
+    if (ecrnx_hw->mod_params->hwscan)
+        ieee80211_hw_set(hw, CHANCTX_STA_CSA);
+
+    if (ecrnx_hw->mod_params->ps_on) {
+        ieee80211_hw_set(hw, SUPPORTS_PS);
+    }
+    /* To disable the dynamic PS we say to the stack that we support it in
+     * HW. This will force mac80211 rely on us to handle this. */
+    ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+
+    if (ecrnx_hw->mod_params->mfp_on)
+        ieee80211_hw_set(hw, MFP_CAPABLE);
+
+    nss = ecrnx_hw->mod_params->nss;
+    ecrnx_hw->phy.ctrlinfo_1.value = 0;
+    ecrnx_hw->phy.ctrlinfo_2.value = 0;
+    if (nss == 1) {
+        ecrnx_hw->phy.ctrlinfo_2.antennaSet = 1;
+    } else {
+        ecrnx_hw->phy.ctrlinfo_1.fecCoding = 0;
+        ecrnx_hw->phy.ctrlinfo_1.nTx = 1;
+        ecrnx_hw->phy.ctrlinfo_2.antennaSet = 3;
+        ecrnx_hw->phy.ctrlinfo_2.smmIndex = 1;
+    }
+    ecrnx_hw->phy.stbc_nss = nss >> 1;
+}
+#endif
+
+#ifdef CONFIG_ECRNX_5G
+static void ecrnx_set_vht_capa(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+    struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+    int i;
+    int nss = ecrnx_hw->mod_params->nss;
+    int mcs_map;
+    int mcs_map_max;
+    int mcs_map_max_2ss_rx = IEEE80211_VHT_MCS_SUPPORT_0_9;
+    int mcs_map_max_2ss_tx = IEEE80211_VHT_MCS_SUPPORT_0_9;
+    int bw_max;
+
+    if (!ecrnx_hw->mod_params->vht_on)
+        return;
+
+    band_5GHz->vht_cap.vht_supported = true;
+    if (ecrnx_hw->mod_params->sgi80)
+        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+    if (ecrnx_hw->mod_params->stbc_on)
+        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+    if (ecrnx_hw->mod_params->ldpc_on)
+        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
+    if (ecrnx_hw->mod_params->bfmee) {
+        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+        band_5GHz->vht_cap.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+    }
+    if (nss > 1)
+        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+    // Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+    band_5GHz->vht_cap.cap |= ecrnx_hw->mod_params->amsdu_rx_max;
+
+    if (ecrnx_hw->mod_params->bfmer) {
+        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+        /* Set number of sounding dimensions */
+        band_5GHz->vht_cap.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+    }
+    if (ecrnx_hw->mod_params->murx)
+        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+    if (ecrnx_hw->mod_params->mutx)
+        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+    /*
+     * MCS map:
+     * This capabilities are filled according to the mcs_map module parameter.
+     * However currently we have some limitations due to FPGA clock constraints
+     * that prevent always using the range of MCS that is defined by the
+     * parameter:
+     *   - in RX, 2SS, we support up to MCS7
+     *   - in TX, 2SS, we support up to MCS8
+     */
+    // Get max supported BW
+    if (ecrnx_hw->mod_params->use_80) {
+        bw_max = PHY_CHNL_BW_80;
+        mcs_map_max_2ss_rx = IEEE80211_VHT_MCS_SUPPORT_0_7;
+        mcs_map_max_2ss_tx = IEEE80211_VHT_MCS_SUPPORT_0_8;
+    } else if (ecrnx_hw->mod_params->use_2040)
+        bw_max = PHY_CHNL_BW_40;
+    else
+        bw_max = PHY_CHNL_BW_20;
+
+    // Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+    // MCS9 is not supported in 1 and 2 SS
+    if (ecrnx_hw->mod_params->use_2040)
+        mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+    else
+        mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+    mcs_map = min_t(int, ecrnx_hw->mod_params->mcs_map, mcs_map_max);
+    band_5GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+    for (i = 0; i < nss; i++) {
+        band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+        band_5GHz->vht_cap.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+        mcs_map = min_t(int, mcs_map, mcs_map_max_2ss_rx);
+    }
+    for (; i < 8; i++) {
+        band_5GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
+            IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+    }
+
+    mcs_map = min_t(int, ecrnx_hw->mod_params->mcs_map, mcs_map_max);
+    band_5GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+    for (i = 0; i < nss; i++) {
+        band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+        band_5GHz->vht_cap.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+        mcs_map = min_t(int, mcs_map, mcs_map_max_2ss_tx);
+    }
+    for (; i < 8; i++) {
+        band_5GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(
+            IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+    }
+
+    if (!ecrnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_ECRNX_VHT_NO80
+        band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+        band_5GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+    }
+}
+#else
+static void ecrnx_set_vht_capa(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+    struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+    int i;
+    int nss = ecrnx_hw->mod_params->nss;
+    int mcs_map;
+    int mcs_map_max;
+    int mcs_map_max_2ss_rx = IEEE80211_VHT_MCS_SUPPORT_0_9;
+    int mcs_map_max_2ss_tx = IEEE80211_VHT_MCS_SUPPORT_0_9;
+    int bw_max;
+
+    if (!ecrnx_hw->mod_params->vht_on)
+        return;
+
+    band_2GHz->vht_cap.vht_supported = true;
+    if (ecrnx_hw->mod_params->sgi80)
+        band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+    if (ecrnx_hw->mod_params->stbc_on)
+        band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+    if (ecrnx_hw->mod_params->ldpc_on)
+        band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
+    if (ecrnx_hw->mod_params->bfmee) {
+        band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+        band_2GHz->vht_cap.cap |= 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT;
+#endif
+    }
+    if (nss > 1)
+        band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_TXSTBC;
+
+    // Update the AMSDU max RX size (not shifted as located at offset 0 of the VHT cap)
+    band_2GHz->vht_cap.cap |= ecrnx_hw->mod_params->amsdu_rx_max;
+
+    if (ecrnx_hw->mod_params->bfmer) {
+        band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+        /* Set number of sounding dimensions */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+        band_2GHz->vht_cap.cap |= (nss - 1) << IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT;
+#endif
+    }
+    if (ecrnx_hw->mod_params->murx)
+        band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+    if (ecrnx_hw->mod_params->mutx)
+        band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+    /*
+     * MCS map:
+     * This capabilities are filled according to the mcs_map module parameter.
+     * However currently we have some limitations due to FPGA clock constraints
+     * that prevent always using the range of MCS that is defined by the
+     * parameter:
+     *   - in RX, 2SS, we support up to MCS7
+     *   - in TX, 2SS, we support up to MCS8
+     */
+    // Get max supported BW
+    if (ecrnx_hw->mod_params->use_80) {
+        bw_max = PHY_CHNL_BW_80;
+        mcs_map_max_2ss_rx = IEEE80211_VHT_MCS_SUPPORT_0_7;
+        mcs_map_max_2ss_tx = IEEE80211_VHT_MCS_SUPPORT_0_8;
+    } else if (ecrnx_hw->mod_params->use_2040)
+        bw_max = PHY_CHNL_BW_40;
+    else
+        bw_max = PHY_CHNL_BW_20;
+
+    // Check if MCS map should be limited to MCS0_8 due to the standard. Indeed in BW20,
+    // MCS9 is not supported in 1 and 2 SS
+    if (ecrnx_hw->mod_params->use_2040)
+        mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_9;
+    else
+        mcs_map_max = IEEE80211_VHT_MCS_SUPPORT_0_8;
+
+    mcs_map = min_t(int, ecrnx_hw->mod_params->mcs_map, mcs_map_max);
+    band_2GHz->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0);
+    for (i = 0; i < nss; i++) {
+        band_2GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+        band_2GHz->vht_cap.vht_mcs.rx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+        mcs_map = min_t(int, mcs_map, mcs_map_max_2ss_rx);
+    }
+    for (; i < 8; i++) {
+        band_2GHz->vht_cap.vht_mcs.rx_mcs_map |= cpu_to_le16(
+            IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+    }
+
+    mcs_map = min_t(int, ecrnx_hw->mod_params->mcs_map, mcs_map_max);
+    band_2GHz->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0);
+    for (i = 0; i < nss; i++) {
+        band_2GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(mcs_map << (i*2));
+        band_2GHz->vht_cap.vht_mcs.tx_highest = MAX_VHT_RATE(mcs_map, nss, bw_max);
+        mcs_map = min_t(int, mcs_map, mcs_map_max_2ss_tx);
+    }
+    for (; i < 8; i++) {
+        band_2GHz->vht_cap.vht_mcs.tx_mcs_map |= cpu_to_le16(
+            IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2));
+    }
+
+    if (!ecrnx_hw->mod_params->use_80) {
+#ifdef CONFIG_VENDOR_ECRNX_VHT_NO80
+        band_2GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+        band_2GHz->vht_cap.cap &= ~IEEE80211_VHT_CAP_SHORT_GI_80;
+    }
+}
+#endif
+
+
+static void ecrnx_set_ht_capa(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+#ifdef CONFIG_ECRNX_5G
+    struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+#endif
+    struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+    int i;
+    int nss = ecrnx_hw->mod_params->nss;
+
+    if (!ecrnx_hw->mod_params->ht_on) {
+        band_2GHz->ht_cap.ht_supported = false;
+#ifdef CONFIG_ECRNX_5G
+        band_5GHz->ht_cap.ht_supported = false;
+#endif
+        return;
+    }
+       //JIRA438 begin by E0000550
+    //if (ecrnx_hw->mod_params->stbc_on)
+    //JIRA438 end
+        band_2GHz->ht_cap.cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT;
+    if (ecrnx_hw->mod_params->ldpc_on)
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+    if (ecrnx_hw->mod_params->use_2040) {
+        band_2GHz->ht_cap.mcs.rx_mask[4] = 0x1; /* MCS32 */
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+        band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(135 * nss);
+    } else {
+        band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(65 * nss);
+    }
+    if (nss > 1)
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC;
+
+    // Update the AMSDU max RX size
+    if (ecrnx_hw->mod_params->amsdu_rx_max)
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
+
+    if (ecrnx_hw->mod_params->sgi) {
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+        if (ecrnx_hw->mod_params->use_2040) {
+            band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+            band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(150 * nss);
+        } else
+            band_2GHz->ht_cap.mcs.rx_highest = cpu_to_le16(72 * nss);
+    }
+    if (ecrnx_hw->mod_params->gf_rx_on)
+        band_2GHz->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD;
+
+    for (i = 0; i < nss; i++) {
+        band_2GHz->ht_cap.mcs.rx_mask[i] = 0xFF;
+    }
+       
+#ifdef CONFIG_ECRNX_5G
+    band_5GHz->ht_cap = band_2GHz->ht_cap;
+#endif
+}
+
+static void ecrnx_set_he_capa(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+#ifdef CONFIG_ECRNX_5G
+    struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+#endif
+    struct ieee80211_supported_band *band_2GHz = wiphy->bands[NL80211_BAND_2GHZ];
+#endif
+
+    int i;
+    int nss = ecrnx_hw->mod_params->nss;
+    struct ieee80211_sta_he_cap *he_cap;
+    int mcs_map, mcs_map_max_2ss = IEEE80211_HE_MCS_SUPPORT_0_11;
+    u8 dcm_max_ru = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242;
+    u32_l phy_vers = ecrnx_hw->version_cfm.version_phy_2;
+
+    if (!ecrnx_hw->mod_params->he_on) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+        band_2GHz->iftype_data = NULL;
+        band_2GHz->n_iftype_data = 0;
+       #ifdef CONFIG_ECRNX_5G
+        band_5GHz->iftype_data = NULL;
+        band_5GHz->n_iftype_data = 0;
+       #endif
+#endif
+        return;
+    }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+    he_cap = (struct ieee80211_sta_he_cap *) &band_2GHz->iftype_data->he_cap;
+#else
+    he_cap = &ecrnx_he_cap;
+#endif
+    he_cap->has_he = true;
+    #ifdef CONFIG_ECRNX_FULLMAC
+    if (ecrnx_hw->version_cfm.features & BIT(MM_FEAT_TWT_BIT))
+    {
+        ecrnx_hw->ext_capa[9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT;
+        he_cap->he_cap_elem.mac_cap_info[0] |= IEEE80211_HE_MAC_CAP0_TWT_REQ;
+    }
+    #endif
+    he_cap->he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_ALL_ACK;
+    ecrnx_set_ppe_threshold(ecrnx_hw, he_cap);
+#if 0
+    /* ESWIN turns off 40M automotically when it is HE mode  */
+    if (ecrnx_hw->mod_params->use_2040) {
+        he_cap->he_cap_elem.phy_cap_info[0] |=
+                        IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G;
+        dcm_max_ru = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484;
+    }
+#endif
+    if (ecrnx_hw->mod_params->use_80) {
+        he_cap->he_cap_elem.phy_cap_info[0] |=
+                        IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
+        mcs_map_max_2ss = IEEE80211_HE_MCS_SUPPORT_0_7;
+        dcm_max_ru = IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996;
+    }
+    if (ecrnx_hw->mod_params->ldpc_on) {
+        he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD;
+    } else {
+        // If no LDPC is supported, we have to limit to MCS0_9, as LDPC is mandatory
+        // for MCS 10 and 11
+        ecrnx_hw->mod_params->he_mcs_map = min_t(int, ecrnx_hw->mod_params->he_mcs_map,
+                                                IEEE80211_HE_MCS_SUPPORT_0_9);
+    }
+#if 0
+    /* ESWIN: sync the he capa with 6600 standalone */
+    he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US |
+                                           IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS;
+    he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS |
+                                           IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+                                           IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+#else
+    he_cap->he_cap_elem.phy_cap_info[1] |= IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US;
+    he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+                                           IEEE80211_HE_PHY_CAP2_DOPPLER_RX;
+#endif
+    if (ecrnx_hw->mod_params->stbc_on)
+        he_cap->he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ;
+    he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM |
+                                           IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA;
+
+    /* ESWIN: sync the he capa with 6600 standalone */
+    if (nss > 1) {
+        he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_2;
+    } else {
+        he_cap->he_cap_elem.phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1;
+    }
+    if (ecrnx_hw->mod_params->bfmee) {
+        he_cap->he_cap_elem.phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE;
+        he_cap->he_cap_elem.phy_cap_info[4] |=
+                     IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4;
+    }
+    he_cap->he_cap_elem.phy_cap_info[5] |= IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK |
+                                           IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK;
+    he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU |
+                                           IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU |
+#if 0
+    /* ESWIN: sync the he capa with 6600 standalone */
+
+                                           IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB |
+                                           IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB |
+#endif
+                                           IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT |
+                                           IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO;
+    he_cap->he_cap_elem.phy_cap_info[7] |= IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI;
+    he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G |
+                                           dcm_max_ru;
+    he_cap->he_cap_elem.phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB |
+                                           IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB |
+                                           IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US;
+#if 0
+    if (__MDM_VERSION(phy_vers) > 30) {
+        he_cap->he_cap_elem.phy_cap_info[6] |= IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE;
+        he_cap->he_cap_elem.phy_cap_info[8] |= IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI |
+                                               IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI;
+    }
+#endif
+    mcs_map = ecrnx_hw->mod_params->he_mcs_map;
+    memset(&he_cap->he_mcs_nss_supp, 0, sizeof(he_cap->he_mcs_nss_supp));
+    for (i = 0; i < nss; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+        mcs_map = min_t(int, ecrnx_hw->mod_params->he_mcs_map,
+                        mcs_map_max_2ss);
+    }
+    for (; i < 8; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.rx_mcs_80 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.rx_mcs_80p80 |= unsup_for_ss;
+    }
+    mcs_map = ecrnx_hw->mod_params->he_mcs_map;
+    for (i = 0; i < nss; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_80 |= cpu_to_le16(mcs_map << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+        mcs_map = min_t(int, ecrnx_hw->mod_params->he_mcs_map,
+                        mcs_map_max_2ss);
+    }
+    for (; i < 8; i++) {
+        __le16 unsup_for_ss = cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << (i*2));
+        he_cap->he_mcs_nss_supp.tx_mcs_80 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_160 |= unsup_for_ss;
+        he_cap->he_mcs_nss_supp.tx_mcs_80p80 |= unsup_for_ss;
+    }
+}
+
+static void ecrnx_set_wiphy_params(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ieee80211_hw *hw = ecrnx_hw->hw;
+
+    /* SOFTMAC specific parameters */
+    if (ecrnx_hw->mod_params->hwscan) {
+        hw->wiphy->max_scan_ssids = SCAN_SSID_MAX;
+        hw->wiphy->max_scan_ie_len = SCAN_MAX_IE_LEN;
+    }
+#else
+    /* FULLMAC specific parameters */
+    wiphy->flags |= WIPHY_FLAG_REPORTS_OBSS;
+    wiphy->max_scan_ssids = SCAN_SSID_MAX;
+    wiphy->max_scan_ie_len = SCANU_MAX_IE_LEN;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
+    wiphy->support_mbssid = 1;
+#endif
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+    if (ecrnx_hw->mod_params->tdls) {
+        /* TDLS support */
+        wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
+#ifdef CONFIG_ECRNX_FULLMAC
+        /* TDLS external setup support */
+        wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
+#endif
+    }
+
+    if (ecrnx_hw->mod_params->ap_uapsd_on)
+        wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    if (ecrnx_hw->mod_params->ps_on)
+        wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
+    else
+        wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+#endif
+
+    if (ecrnx_hw->mod_params->custregd) {
+        // Apply custom regulatory. Note that for recent kernel versions we use instead the
+        // REGULATORY_WIPHY_SELF_MANAGED flag, along with the regulatory_set_wiphy_regd()
+        // function, that needs to be called after wiphy registration
+        // Check if custom channel set shall be enabled. In such case only monitor mode is
+        // supported
+        if (ecrnx_hw->mod_params->custchan) {
+            wiphy->interface_modes = BIT(NL80211_IFTYPE_MONITOR);
+
+            // Enable "extra" channels
+            wiphy->bands[NL80211_BAND_2GHZ]->n_channels += 13;
+               #ifdef CONFIG_ECRNX_5G
+            wiphy->bands[NL80211_BAND_5GHZ]->n_channels += 59;
+               #endif
+        }
+    }
+}
+
+static void ecrnx_set_rf_params(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+#ifndef CONFIG_ECRNX_SDM
+#ifdef CONFIG_ECRNX_5G
+    struct ieee80211_supported_band *band_5GHz = wiphy->bands[NL80211_BAND_5GHZ];
+#else
+#endif
+    u32 mdm_phy_cfg = __MDM_PHYCFG_FROM_VERS(ecrnx_hw->version_cfm.version_phy_1);
+
+    /*
+     * Get configuration file depending on the RF
+     */
+#if 0 /* baoyong: we use the custom rf, do not need this */
+    struct ecrnx_phy_conf_file phy_conf;
+    if (mdm_phy_cfg == MDM_PHY_CONFIG_TRIDENT) {
+        // Retrieve the Trident configuration
+        ecrnx_parse_phy_configfile(ecrnx_hw, ECRNX_PHY_CONFIG_TRD_NAME,
+                                  &phy_conf, ecrnx_hw->mod_params->phy_cfg);
+        memcpy(&ecrnx_hw->phy.cfg, &phy_conf.trd, sizeof(phy_conf.trd));
+    } else if (mdm_phy_cfg == MDM_PHY_CONFIG_CATAXIA) {
+        memset(&phy_conf.cataxia, 0, sizeof(phy_conf.cataxia));
+        phy_conf.cataxia.path_used = ecrnx_hw->mod_params->phy_cfg;
+        memcpy(&ecrnx_hw->phy.cfg, &phy_conf.cataxia, sizeof(phy_conf.cataxia));
+    } else if (mdm_phy_cfg == MDM_PHY_CONFIG_KARST) {
+        // We use the NSS parameter as is
+        // Retrieve the Karst configuration
+        ecrnx_parse_phy_configfile(ecrnx_hw, ECRNX_PHY_CONFIG_KARST_NAME,
+                                  &phy_conf, ecrnx_hw->mod_params->phy_cfg);
+
+        memcpy(&ecrnx_hw->phy.cfg, &phy_conf.karst, sizeof(phy_conf.karst));
+    } else {
+        WARN_ON(1);
+    }
+#endif
+
+    /*
+     * adjust caps depending on the RF
+     */
+    switch (mdm_phy_cfg) {
+        case MDM_PHY_CONFIG_TRIDENT:
+        {
+            wiphy_dbg(wiphy, "found Trident PHY .. limit BW to 40MHz\n");
+            ecrnx_hw->phy.limit_bw = true;
+#ifdef CONFIG_ECRNX_5G
+#ifdef CONFIG_VENDOR_ECRNX_VHT_NO80
+            band_5GHz->vht_cap.cap |= IEEE80211_VHT_CAP_NOT_SUP_WIDTH_80;
+#endif
+            band_5GHz->vht_cap.cap &= ~(IEEE80211_VHT_CAP_SHORT_GI_80 |
+                                        IEEE80211_VHT_CAP_RXSTBC_MASK);
+#endif
+            break;
+        }
+        case MDM_PHY_CONFIG_CATAXIA:
+        {
+            wiphy_dbg(wiphy, "found CATAXIA PHY\n");
+            break;
+        }
+        case MDM_PHY_CONFIG_KARST:
+        {
+            wiphy_dbg(wiphy, "found KARST PHY\n");
+            break;
+        }
+        default:
+            WARN_ON(1);
+            break;
+    }
+#endif /* CONFIG_ECRNX_SDM */
+}
+
+int ecrnx_handle_dynparams(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+    int ret;
+
+    /* Check compatibility between requested parameters and HW/SW features */
+    ret = ecrnx_check_fw_hw_feature(ecrnx_hw, wiphy);
+    if (ret)
+        return ret;
+#ifndef CONFIG_ECRNX_ESWIN
+
+    /* Allocate the RX buffers according to the maximum AMSDU RX size */
+    ret = ecrnx_ipc_rxbuf_init(ecrnx_hw,
+                              (4 * (ecrnx_hw->mod_params->amsdu_rx_max + 1) + 1) * 1024);
+    if (ret) {
+        wiphy_err(wiphy, "Cannot allocate the RX buffers\n");
+        return ret;
+    }
+#endif
+#ifdef CONFIG_ECRNX_SOFTMAC
+    /* SOFTMAC specific parameters*/
+    ecrnx_set_softmac_flags(ecrnx_hw);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    /* Set wiphy parameters */
+    ecrnx_set_wiphy_params(ecrnx_hw, wiphy);
+
+    /* Set VHT capabilities */
+    ecrnx_set_vht_capa(ecrnx_hw, wiphy);
+
+    /* Set HE capabilities */
+    ecrnx_set_he_capa(ecrnx_hw, wiphy);
+
+    /* Set HT capabilities */
+    ecrnx_set_ht_capa(ecrnx_hw, wiphy);
+
+    /* Set RF specific parameters (shall be done last as it might change some
+       capabilities previously set) */
+    ecrnx_set_rf_params(ecrnx_hw, wiphy);
+
+    return 0;
+}
+
+void ecrnx_custregd(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy)
+{
+// For older kernel version, the custom regulatory is applied before the wiphy
+// registration (in ecrnx_set_wiphy_params()), so nothing has to be done here
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+    if (!ecrnx_hw->mod_params->custregd)
+        return;
+
+    wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
+    wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
+
+    rtnl_lock();
+    if (regulatory_set_wiphy_regd_sync_rtnl(wiphy, &ecrnx_regdom))
+        wiphy_err(wiphy, "Failed to set custom regdomain\n");
+    else
+        wiphy_err(wiphy,"\n"
+                  "*******************************************************\n"
+                  "** CAUTION: USING PERMISSIVE CUSTOM REGULATORY RULES **\n"
+                  "*******************************************************\n");
+     rtnl_unlock();
+#endif
+}
+
+void ecrnx_adjust_amsdu_maxnb(struct ecrnx_hw *ecrnx_hw)
+{
+    if (ecrnx_hw->mod_params->amsdu_maxnb > NX_TX_PAYLOAD_MAX)
+        ecrnx_hw->mod_params->amsdu_maxnb = NX_TX_PAYLOAD_MAX;
+    else if (ecrnx_hw->mod_params->amsdu_maxnb == 0)
+        ecrnx_hw->mod_params->amsdu_maxnb = 1;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_mod_params.h b/drivers/net/wireless/eswin/ecrnx_mod_params.h
new file mode 100644 (file)
index 0000000..72c87db
--- /dev/null
@@ -0,0 +1,86 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_mod_params.h
+ *
+ * @brief Declaration of module parameters
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_MOD_PARAM_H_
+#define _ECRNX_MOD_PARAM_H_
+
+struct ecrnx_mod_params {
+    bool ht_on;
+    bool vht_on;
+    bool he_on;
+    int mcs_map;
+    int he_mcs_map;
+    bool he_ul_on;
+    bool ldpc_on;
+    bool stbc_on;
+    bool gf_rx_on;
+    int phy_cfg;
+    int uapsd_timeout;
+    bool ap_uapsd_on;
+    bool sgi;
+    bool sgi80;
+    bool use_2040;
+    bool use_80;
+    bool custregd;
+    bool custchan;
+    int nss;
+    int amsdu_rx_max;
+    bool bfmee;
+    bool bfmer;
+    bool mesh;
+    bool murx;
+    bool mutx;
+    bool mutx_on;
+    unsigned int roc_dur_max;
+    int listen_itv;
+    bool listen_bcmc;
+    int lp_clk_ppm;
+    bool ps_on;
+    int tx_lft;
+    int amsdu_maxnb;
+    int uapsd_queues;
+    bool tdls;
+    bool uf;
+    char *ftl;
+    bool dpsm;
+    int tx_to_bk;
+    int tx_to_be;
+    int tx_to_vi;
+    int tx_to_vo;
+#ifdef CONFIG_ECRNX_SOFTMAC
+    bool mfp_on;
+    bool gf_on;
+    bool bwsig_on;
+    bool dynbw_on;
+    bool agg_tx;
+    int  amsdu_force;
+    bool rc_probes_on;
+    bool cmon;
+    bool hwscan;
+    bool autobcn;
+#else
+    bool ant_div;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+};
+
+extern struct ecrnx_mod_params ecrnx_mod_params;
+
+struct ecrnx_hw;
+struct wiphy;
+int ecrnx_handle_dynparams(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy);
+void ecrnx_custregd(struct ecrnx_hw *ecrnx_hw, struct wiphy *wiphy);
+void ecrnx_enable_wapi(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_enable_mfp(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_enable_gcmp(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_adjust_amsdu_maxnb(struct ecrnx_hw *ecrnx_hw);
+
+#endif /* _ECRNX_MOD_PARAM_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_msg_rx.c b/drivers/net/wireless/eswin/ecrnx_msg_rx.c
new file mode 100644 (file)
index 0000000..5e51e2c
--- /dev/null
@@ -0,0 +1,1604 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_msg_rx.c
+ *
+ * @brief RX function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+#include "ecrnx_defs.h"
+#include "ecrnx_prof.h"
+#include "ecrnx_tx.h"
+#ifdef CONFIG_ECRNX_BFMER
+#include "ecrnx_bfmer.h"
+#endif //(CONFIG_ECRNX_BFMER)
+#ifdef CONFIG_ECRNX_FULLMAC
+#include "ecrnx_debugfs.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_tdls.h"
+#endif /* CONFIG_ECRNX_FULLMAC */
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+#include <linux/time.h>
+#endif
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+#include "ecrnx_debugfs_func.h"
+#endif
+static int ecrnx_freq_to_idx(struct ecrnx_hw *ecrnx_hw, int freq)
+{
+    struct ieee80211_supported_band *sband;
+    int band, ch, idx = 0;
+
+#ifdef CONFIG_ECRNX_5G
+    for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
+#else
+       for (band = NL80211_BAND_2GHZ; band <= NL80211_BAND_2GHZ; band++) {
+#endif
+#ifdef CONFIG_ECRNX_SOFTMAC
+        sband = ecrnx_hw->hw->wiphy->bands[band];
+#else
+        sband = ecrnx_hw->wiphy->bands[band];
+#endif /* CONFIG_ECRNX_SOFTMAC */
+        if (!sband) {
+            continue;
+        }
+
+        for (ch = 0; ch < sband->n_channels; ch++, idx++) {
+            if (sband->channels[ch].center_freq == freq) {
+                goto exit;
+            }
+        }
+    }
+
+       ECRNX_ERR("--!!!!!!!!error freq-----%d\n", freq);
+    //BUG_ON(1);
+
+exit:
+    // Channel has been found, return the index
+    return idx;
+}
+
+/***************************************************************************
+ * Messages from MM task
+ **************************************************************************/
+static inline int ecrnx_rx_chan_pre_switch_ind(struct ecrnx_hw *ecrnx_hw,
+                                              struct ecrnx_cmd *cmd,
+                                              struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ecrnx_chanctx *chan_ctxt;
+#endif
+    struct ecrnx_vif *ecrnx_vif;
+    int chan_idx = ((struct mm_channel_pre_switch_ind *)msg->param)->chan_index;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    REG_SW_SET_PROFILING_CHAN(ecrnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    list_for_each_entry(chan_ctxt, &ecrnx_hw->chan_ctxts, list) {
+        if (chan_ctxt->index == chan_idx) {
+            chan_ctxt->active = false;
+            break;
+        }
+    }
+
+    list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+        if (ecrnx_vif->chanctx && (ecrnx_vif->chanctx->index == chan_idx)) {
+            ecrnx_txq_vif_stop(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+        }
+    }
+#else
+    list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+        if (ecrnx_vif->up && ecrnx_vif->ch_index == chan_idx) {
+            ecrnx_txq_vif_stop(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+        }
+    }
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    REG_SW_CLEAR_PROFILING_CHAN(ecrnx_hw, SW_PROF_CHAN_CTXT_PSWTCH_BIT);
+
+    return 0;
+}
+
+static inline int ecrnx_rx_chan_switch_ind(struct ecrnx_hw *ecrnx_hw,
+                                          struct ecrnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ecrnx_chanctx *chan_ctxt;
+    struct ecrnx_sta *ecrnx_sta;
+#endif
+    struct ecrnx_vif *ecrnx_vif;
+    int chan_idx = ((struct mm_channel_switch_ind *)msg->param)->chan_index;
+    bool roc_req = ((struct mm_channel_switch_ind *)msg->param)->roc;
+    bool roc_tdls = ((struct mm_channel_switch_ind *)msg->param)->roc_tdls;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    REG_SW_SET_PROFILING_CHAN(ecrnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    if (roc_tdls) {
+        u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index;
+        // Enable traffic only for TDLS station
+        list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+            if (ecrnx_vif->vif_index == vif_index) {
+                list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
+                    if (ecrnx_sta->tdls.active) {
+                        ecrnx_vif->roc_tdls = true;
+                        ecrnx_txq_tdls_sta_start(ecrnx_sta, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+                        break;
+                    }
+                }
+                break;
+            }
+        }
+    } else if (!roc_req) {
+        list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+            if (ecrnx_vif->chanctx && (ecrnx_vif->chanctx->index == chan_idx)) {
+                ecrnx_txq_vif_start(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+            }
+        }
+    } else {
+        u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index;
+
+        // Inform the host that the offchannel period has been started
+        ieee80211_ready_on_channel(ecrnx_hw->hw);
+
+        // Enable traffic for associated VIF (roc may happen without chanctx)
+        list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+            if (ecrnx_vif->vif_index == vif_index) {
+                ecrnx_txq_vif_start(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+            }
+        }
+    }
+
+    /* keep cur_chan up to date */
+    list_for_each_entry(chan_ctxt, &ecrnx_hw->chan_ctxts, list) {
+        if (chan_ctxt->index == chan_idx) {
+            chan_ctxt->active = true;
+            ecrnx_hw->cur_freq = chan_ctxt->ctx->def.center_freq1;
+            ecrnx_hw->cur_band = chan_ctxt->ctx->def.chan->band;
+            if (chan_ctxt->ctx->def.chan->flags & IEEE80211_CHAN_RADAR) {
+                ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+                                            ECRNX_RADAR_DETECT_REPORT,
+                                            ECRNX_RADAR_RIU);
+            } else {
+                ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+                                            ECRNX_RADAR_DETECT_DISABLE,
+                                            ECRNX_RADAR_RIU);
+            }
+            break;
+        }
+    }
+
+#else
+    if (roc_tdls) {
+        u8 vif_index = ((struct mm_channel_switch_ind *)msg->param)->vif_index;
+        list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+            if (ecrnx_vif->vif_index == vif_index) {
+                ecrnx_vif->roc_tdls = true;
+                ecrnx_txq_tdls_sta_start(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+            }
+        }
+    } else if (!roc_req) {
+        list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+            if (ecrnx_vif->up && ecrnx_vif->ch_index == chan_idx) {
+                ecrnx_txq_vif_start(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+            }
+        }
+    } else {
+        /* Retrieve the allocated RoC element */
+        struct ecrnx_roc *roc = ecrnx_hw->roc;
+
+        if (roc && roc->vif) {
+            /* Get VIF on which RoC has been started */
+            ecrnx_vif = roc->vif;
+            /* For debug purpose (use ftrace kernel option) */
+            trace_switch_roc(ecrnx_vif->vif_index);
+
+            if (!roc->internal) {
+            /* If mgmt_roc is true, remain on channel has been started by ourself */
+                /* Inform the host that we have switch on the indicated off-channel */
+                cfg80211_ready_on_channel(&ecrnx_vif->wdev, (u64)(ecrnx_hw->roc_cookie),
+                                          roc->chan, roc->duration, GFP_ATOMIC);
+            }
+
+            /* Keep in mind that we have switched on the channel */
+            roc->on_chan = true;
+        }
+
+        // Enable traffic on OFF channel queue
+        ecrnx_txq_offchan_start(ecrnx_hw);
+#if defined(CONFIG_ECRNX_P2P)
+    if (roc && roc->internal) {
+        ecrnx_hw->p2p_listen.rxdatas = 1;
+        wake_up(&ecrnx_hw->p2p_listen.rxdataq);
+    }
+#endif
+    }
+
+    ecrnx_hw->cur_chanctx = chan_idx;
+    ecrnx_radar_detection_enable_on_cur_channel(ecrnx_hw);
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    REG_SW_CLEAR_PROFILING_CHAN(ecrnx_hw, SW_PROF_CHAN_CTXT_SWTCH_BIT);
+
+    return 0;
+}
+
+static inline int ecrnx_rx_tdls_chan_switch_cfm(struct ecrnx_hw *ecrnx_hw,
+                                                struct ecrnx_cmd *cmd,
+                                                struct ipc_e2a_msg *msg)
+{
+    return 0;
+}
+
+static inline int ecrnx_rx_tdls_chan_switch_ind(struct ecrnx_hw *ecrnx_hw,
+                                               struct ecrnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ecrnx_chanctx *chan_ctxt;
+    u8 chan_idx = ((struct tdls_chan_switch_ind *)msg->param)->chan_ctxt_index;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    // Enable channel context
+    list_for_each_entry(chan_ctxt, &ecrnx_hw->chan_ctxts, list) {
+        if (chan_ctxt->index == chan_idx) {
+            chan_ctxt->active = true;
+            ecrnx_hw->cur_freq = chan_ctxt->ctx->def.center_freq1;
+            ecrnx_hw->cur_band = chan_ctxt->ctx->def.chan->band;
+        }
+    }
+
+    return 0;
+#else
+    // Enable traffic on OFF channel queue
+    ecrnx_txq_offchan_start(ecrnx_hw);
+
+    return 0;
+#endif
+}
+
+static inline int ecrnx_rx_tdls_chan_switch_base_ind(struct ecrnx_hw *ecrnx_hw,
+                                                    struct ecrnx_cmd *cmd,
+                                                    struct ipc_e2a_msg *msg)
+{
+    struct ecrnx_vif *ecrnx_vif;
+    u8 vif_index = ((struct tdls_chan_switch_base_ind *)msg->param)->vif_index;
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ecrnx_sta *ecrnx_sta;
+#endif
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    // Disable traffic for associated VIF
+    list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+        if (ecrnx_vif->vif_index == vif_index) {
+            if (ecrnx_vif->chanctx)
+                ecrnx_vif->chanctx->active = false;
+            list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
+                if (ecrnx_sta->tdls.active) {
+                    ecrnx_vif->roc_tdls = false;
+                    ecrnx_txq_tdls_sta_stop(ecrnx_sta, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+                    break;
+                }
+            }
+            break;
+        }
+    }
+    return 0;
+#else
+    list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+        if (ecrnx_vif->vif_index == vif_index) {
+            ecrnx_vif->roc_tdls = false;
+            ecrnx_txq_tdls_sta_stop(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+        }
+    }
+    return 0;
+#endif
+}
+
+static inline int ecrnx_rx_tdls_peer_ps_ind(struct ecrnx_hw *ecrnx_hw,
+                                           struct ecrnx_cmd *cmd,
+                                           struct ipc_e2a_msg *msg)
+{
+    struct ecrnx_vif *ecrnx_vif;
+    u8 vif_index = ((struct tdls_peer_ps_ind *)msg->param)->vif_index;
+    bool ps_on = ((struct tdls_peer_ps_ind *)msg->param)->ps_on;
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    u8 sta_idx = ((struct tdls_peer_ps_ind *)msg->param)->sta_idx;
+    struct ecrnx_sta *ecrnx_sta;
+    list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+        if (ecrnx_vif->vif_index == vif_index) {
+            list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
+                if (ecrnx_sta->sta_idx == sta_idx) {
+                    ecrnx_sta->tdls.ps_on = ps_on;
+                    if (ps_on) {
+                        // disable TXQ for TDLS peer
+                        ecrnx_txq_tdls_sta_stop(ecrnx_sta, ECRNX_TXQ_STOP_STA_PS, ecrnx_hw);
+                    } else {
+                        // Enable TXQ for TDLS peer
+                        ecrnx_txq_tdls_sta_start(ecrnx_sta, ECRNX_TXQ_STOP_STA_PS, ecrnx_hw);
+                    }
+                    break;
+                }
+            }
+            break;
+        }
+    }
+    return 0;
+#else
+    list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+        if (ecrnx_vif->vif_index == vif_index) {
+            ecrnx_vif->sta.tdls_sta->tdls.ps_on = ps_on;
+            // Update PS status for the TDLS station
+            ecrnx_ps_bh_enable(ecrnx_hw, ecrnx_vif->sta.tdls_sta, ps_on);
+        }
+    }
+
+    return 0;
+#endif
+}
+
+static inline int ecrnx_rx_remain_on_channel_exp_ind(struct ecrnx_hw *ecrnx_hw,
+                                                    struct ecrnx_cmd *cmd,
+                                                    struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ecrnx_vif *ecrnx_vif;
+    u8 vif_index = ((struct mm_remain_on_channel_exp_ind *)msg->param)->vif_index;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    ieee80211_remain_on_channel_expired(ecrnx_hw->hw);
+
+    // Disable traffic for associated VIF
+    list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+        if (ecrnx_vif->vif_index == vif_index) {
+            if (ecrnx_vif->chanctx)
+                ecrnx_vif->chanctx->active = false;
+
+            ecrnx_txq_vif_stop(ecrnx_vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+            break;
+        }
+    }
+
+    return 0;
+
+#else
+    if(!ecrnx_hw->roc || !ecrnx_hw->roc->chan){
+        ECRNX_ERR("error!!!:ecrnx_hw->roc or !ecrnx_hw->roc->chan is null \n");
+        return 0;
+    }
+    /* Retrieve the allocated RoC element */
+    struct ecrnx_roc *roc = ecrnx_hw->roc;
+    /* Get VIF on which RoC has been started */
+    struct ecrnx_vif *ecrnx_vif = roc->vif;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* For debug purpose (use ftrace kernel option) */
+    trace_roc_exp(ecrnx_vif->vif_index);
+
+    /* If mgmt_roc is true, remain on channel has been started by ourself */
+    /* If RoC has been cancelled before we switched on channel, do not call cfg80211 */
+    if (!roc->internal && roc->on_chan) {
+        /* Inform the host that off-channel period has expired */
+        cfg80211_remain_on_channel_expired(&ecrnx_vif->wdev, (u64)(ecrnx_hw->roc_cookie),
+                                           roc->chan, GFP_ATOMIC);
+    }
+
+    /* De-init offchannel TX queue */
+    ecrnx_txq_offchan_deinit(ecrnx_vif);
+
+    /* Increase the cookie counter cannot be zero */
+    ecrnx_hw->roc_cookie++;
+
+    if (ecrnx_hw->roc_cookie == 0)
+        ecrnx_hw->roc_cookie = 1;
+
+#if CONFIG_ECRNX_P2P
+       ecrnx_hw->p2p_listen.listen_started = 0;
+#endif
+
+    /* Free the allocated RoC element */
+    kfree(roc);
+    ecrnx_hw->roc = NULL;
+    
+#if defined(CONFIG_ECRNX_P2P)
+       wake_up(&ecrnx_hw->p2p_listen.rxdataq);
+#endif
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+    return 0;
+}
+
+static inline int ecrnx_rx_p2p_vif_ps_change_ind(struct ecrnx_hw *ecrnx_hw,
+                                                struct ecrnx_cmd *cmd,
+                                                struct ipc_e2a_msg *msg)
+{
+    int vif_idx  = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->vif_index;
+    int ps_state = ((struct mm_p2p_vif_ps_change_ind *)msg->param)->ps_state;
+    struct ecrnx_vif *vif_entry;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    // Look for VIF entry
+    list_for_each_entry(vif_entry, &ecrnx_hw->vifs, list) {
+        if (vif_entry->vif_index == vif_idx) {
+            goto found_vif;
+        }
+    }
+#else
+    vif_entry = ecrnx_hw->vif_table[vif_idx];
+
+    if (vif_entry) {
+        goto found_vif;
+    }
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    goto exit;
+
+found_vif:
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    if (ps_state == MM_PS_MODE_OFF) {
+        ecrnx_txq_vif_start(vif_entry, ECRNX_TXQ_STOP_VIF_PS, ecrnx_hw);
+    }
+    else {
+        ecrnx_txq_vif_stop(vif_entry, ECRNX_TXQ_STOP_VIF_PS, ecrnx_hw);
+    }
+#else
+    if (ps_state == MM_PS_MODE_OFF) {
+        // Start TX queues for provided VIF
+        ecrnx_txq_vif_start(vif_entry, ECRNX_TXQ_STOP_VIF_PS, ecrnx_hw);
+    }
+    else {
+        // Stop TX queues for provided VIF
+        ecrnx_txq_vif_stop(vif_entry, ECRNX_TXQ_STOP_VIF_PS, ecrnx_hw);
+    }
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+exit:
+    return 0;
+}
+
+static inline int ecrnx_rx_channel_survey_ind(struct ecrnx_hw *ecrnx_hw,
+                                             struct ecrnx_cmd *cmd,
+                                             struct ipc_e2a_msg *msg)
+{
+    struct mm_channel_survey_ind *ind = (struct mm_channel_survey_ind *)msg->param;
+    // Get the channel index
+    int idx = ecrnx_freq_to_idx(ecrnx_hw, ind->freq);
+    // Get the survey
+    struct ecrnx_survey_info *ecrnx_survey;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if (idx >  ARRAY_SIZE(ecrnx_hw->survey))
+        return 0;
+
+    ecrnx_survey = &ecrnx_hw->survey[idx];
+
+    // Store the received parameters
+    ecrnx_survey->chan_time_ms = ind->chan_time_ms;
+    ecrnx_survey->chan_time_busy_ms = ind->chan_time_busy_ms;
+    ecrnx_survey->noise_dbm = ind->noise_dbm;
+    ecrnx_survey->filled = (SURVEY_INFO_TIME |
+                           SURVEY_INFO_TIME_BUSY);
+
+    if (ind->noise_dbm != 0) {
+        ecrnx_survey->filled |= SURVEY_INFO_NOISE_DBM;
+    }
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+       ecrnx_debugfs_noise_of_survey_info_update(ecrnx_hw, ecrnx_survey, idx);
+#endif
+
+    return 0;
+}
+
+static inline int ecrnx_rx_p2p_noa_upd_ind(struct ecrnx_hw *ecrnx_hw,
+                                          struct ecrnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    return 0;
+}
+
+static inline int ecrnx_rx_rssi_status_ind(struct ecrnx_hw *ecrnx_hw,
+                                          struct ecrnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    struct mm_rssi_status_ind *ind = (struct mm_rssi_status_ind *)msg->param;
+    int vif_idx  = ind->vif_index;
+    bool rssi_status = ind->rssi_status;
+
+    struct ecrnx_vif *vif_entry;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    list_for_each_entry(vif_entry, &ecrnx_hw->vifs, list) {
+        if (vif_entry->vif_index == vif_idx) {
+            ecrnx_ieee80211_cqm_rssi_notify(vif_entry->vif,
+                                      rssi_status ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+                                                    NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                                      ind->rssi, GFP_ATOMIC);
+        }
+    }
+#else
+    vif_entry = ecrnx_hw->vif_table[vif_idx];
+    if (vif_entry) {
+        ecrnx_cfg80211_cqm_rssi_notify(vif_entry->ndev,
+                                 rssi_status ? NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+                                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                                 ind->rssi, GFP_ATOMIC);
+    }
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    return 0;
+}
+
+static inline int ecrnx_rx_pktloss_notify_ind(struct ecrnx_hw *ecrnx_hw,
+                                             struct ecrnx_cmd *cmd,
+                                             struct ipc_e2a_msg *msg)
+{
+#ifdef CONFIG_ECRNX_FULLMAC
+    struct mm_pktloss_ind *ind = (struct mm_pktloss_ind *)msg->param;
+    struct ecrnx_vif *vif_entry;
+    int vif_idx  = ind->vif_index;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    vif_entry = ecrnx_hw->vif_table[vif_idx];
+    if (vif_entry) {
+        cfg80211_cqm_pktloss_notify(vif_entry->ndev, (const u8 *)ind->mac_addr.array,
+                                    ind->num_packets, GFP_ATOMIC);
+    }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+    return 0;
+}
+
+static inline int ecrnx_rx_csa_counter_ind(struct ecrnx_hw *ecrnx_hw,
+                                          struct ecrnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    struct mm_csa_counter_ind *ind = (struct mm_csa_counter_ind *)msg->param;
+    struct ecrnx_vif *vif;
+    bool found = false;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    // Look for VIF entry
+    list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+        if (vif->vif_index == ind->vif_index) {
+            found=true;
+            break;
+        }
+    }
+
+    if (found) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+        if (ind->csa_count == 1)
+            ieee80211_csa_finish(vif->vif);
+        else
+            ieee80211_csa_update_counter(vif->vif);
+#else
+        if (vif->ap.csa)
+            vif->ap.csa->count = ind->csa_count;
+        else
+            netdev_err(vif->ndev, "CSA counter update but no active CSA");
+
+#endif
+    }
+
+    return 0;
+}
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+static inline int ecrnx_rx_connection_loss_ind(struct ecrnx_hw *ecrnx_hw,
+                                              struct ecrnx_cmd *cmd,
+                                              struct ipc_e2a_msg *msg)
+{
+    struct ecrnx_vif *ecrnx_vif;
+    u8 inst_nbr;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    inst_nbr = ((struct mm_connection_loss_ind *)msg->param)->inst_nbr;
+
+    /* Search the VIF entry corresponding to the instance number */
+    list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+        if (ecrnx_vif->vif_index == inst_nbr) {
+            ieee80211_connection_loss(ecrnx_vif->vif);
+            break;
+        }
+    }
+
+    return 0;
+}
+
+
+#ifdef CONFIG_ECRNX_BCN
+static inline int ecrnx_rx_prm_tbtt_ind(struct ecrnx_hw *ecrnx_hw,
+                                       struct ecrnx_cmd *cmd,
+                                       struct ipc_e2a_msg *msg)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    ecrnx_tx_bcns(ecrnx_hw);
+
+    return 0;
+}
+#endif
+
+#else /* !CONFIG_ECRNX_SOFTMAC */
+static inline int ecrnx_rx_csa_finish_ind(struct ecrnx_hw *ecrnx_hw,
+                                         struct ecrnx_cmd *cmd,
+                                         struct ipc_e2a_msg *msg)
+{
+    struct mm_csa_finish_ind *ind = (struct mm_csa_finish_ind *)msg->param;
+    struct ecrnx_vif *vif;
+    bool found = false;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    // Look for VIF entry
+    list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+        if (vif->vif_index == ind->vif_index) {
+            found=true;
+            break;
+        }
+    }
+
+    if (found) {
+        if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP ||
+            ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO) {
+            if (vif->ap.csa) {
+                vif->ap.csa->status = ind->status;
+                vif->ap.csa->ch_idx = ind->chan_idx;
+                schedule_work(&vif->ap.csa->work);
+            } else
+                netdev_err(vif->ndev, "CSA finish indication but no active CSA");
+        } else {
+            if (ind->status == 0) {
+                ecrnx_chanctx_unlink(vif);
+                ecrnx_chanctx_link(vif, ind->chan_idx, NULL);
+                if (ecrnx_hw->cur_chanctx == ind->chan_idx) {
+                    ecrnx_radar_detection_enable_on_cur_channel(ecrnx_hw);
+                    ecrnx_txq_vif_start(vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+                } else
+                    ecrnx_txq_vif_stop(vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static inline int ecrnx_rx_csa_traffic_ind(struct ecrnx_hw *ecrnx_hw,
+                                          struct ecrnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    struct mm_csa_traffic_ind *ind = (struct mm_csa_traffic_ind *)msg->param;
+    struct ecrnx_vif *vif;
+    bool found = false;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    // Look for VIF entry
+    list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+        if (vif->vif_index == ind->vif_index) {
+            found=true;
+            break;
+        }
+    }
+
+    if (found) {
+        if (ind->enable)
+            ecrnx_txq_vif_start(vif, ECRNX_TXQ_STOP_CSA, ecrnx_hw);
+        else
+            ecrnx_txq_vif_stop(vif, ECRNX_TXQ_STOP_CSA, ecrnx_hw);
+    }
+
+    return 0;
+}
+
+static inline int ecrnx_rx_ps_change_ind(struct ecrnx_hw *ecrnx_hw,
+                                        struct ecrnx_cmd *cmd,
+                                        struct ipc_e2a_msg *msg)
+{
+    struct mm_ps_change_ind *ind = (struct mm_ps_change_ind *)msg->param;
+    struct ecrnx_sta *sta = &ecrnx_hw->sta_table[ind->sta_idx];
+
+    if (ind->sta_idx >= (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+        wiphy_err(ecrnx_hw->wiphy, "Invalid sta index reported by fw %d\n",
+                  ind->sta_idx);
+        return 1;
+    }
+
+    netdev_dbg(ecrnx_hw->vif_table[sta->vif_idx]->ndev,
+               "Sta %d, change PS mode to %s", sta->sta_idx,
+               ind->ps_state ? "ON" : "OFF");
+
+    ECRNX_DBG("Sta:0x%p, sta_idx:%d, sta->valid:%d, sta_mac:%pM, change PS mode to: %s \n",sta, sta->sta_idx, sta->valid, sta->mac_addr, ind->ps_state ? "ON" : "OFF");
+
+    if (sta->valid) {
+        ecrnx_ps_bh_enable(ecrnx_hw, sta, ind->ps_state);
+    } else if (test_bit(ECRNX_DEV_ADDING_STA, &ecrnx_hw->flags)) {
+        sta->ps.active = ind->ps_state ? true : false;
+    } else {
+        netdev_err(ecrnx_hw->vif_table[sta->vif_idx]->ndev,
+                   "Ignore PS mode change on invalid sta\n");
+    }
+
+    return 0;
+}
+
+
+static inline int ecrnx_rx_traffic_req_ind(struct ecrnx_hw *ecrnx_hw,
+                                          struct ecrnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    struct mm_traffic_req_ind *ind = (struct mm_traffic_req_ind *)msg->param;
+    struct ecrnx_sta *sta = &ecrnx_hw->sta_table[ind->sta_idx];
+
+    ECRNX_DBG("%s-%d:Sta:0x%p, sta_idx:%d, sta->valid:%d, sta_mac:%pM \n",__func__, __LINE__, sta, ind->sta_idx, sta->valid, sta->mac_addr);
+
+    netdev_dbg(ecrnx_hw->vif_table[sta->vif_idx]->ndev,
+               "Sta %d, asked for %d pkt", sta->sta_idx, ind->pkt_cnt);
+
+    ecrnx_ps_bh_traffic_req(ecrnx_hw, sta, ind->pkt_cnt,
+                           ind->uapsd ? UAPSD_ID : LEGACY_PS_ID);
+
+    return 0;
+}
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+/***************************************************************************
+ * Messages from SCAN task
+ **************************************************************************/
+#ifdef CONFIG_ECRNX_SOFTMAC
+static inline int ecrnx_rx_scan_done_ind(struct ecrnx_hw *ecrnx_hw,
+                                        struct ecrnx_cmd *cmd,
+                                        struct ipc_e2a_msg *msg)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+    struct cfg80211_scan_info info = {
+        .aborted = false,
+    };
+#endif
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &ecrnx_hw->scan_ie);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+    ieee80211_scan_completed(ecrnx_hw->hw, &info);
+#else
+    ieee80211_scan_completed(ecrnx_hw->hw, false);
+#endif
+
+    return 0;
+}
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+/***************************************************************************
+ * Messages from SCANU task
+ **************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+static inline int ecrnx_scanu_cancel_cfm(struct ecrnx_hw *ecrnx_hw,
+                                           struct ecrnx_cmd *cmd,
+                                           struct ipc_e2a_msg *msg)
+{
+    struct scanu_cancel_cfm *cfm = (struct scanu_cancel_cfm *)msg;
+    bool abort = false;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+    struct cfg80211_scan_info info = {
+        .aborted = abort,
+    };
+#endif
+
+    ECRNX_PRINT("%s: cfm status:%d, scan_request:0x%p \n", __func__, cfm->status, ecrnx_hw->scan_request);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+    cfg80211_scan_done(ecrnx_hw->scan_request, &info);
+#else
+    cfg80211_scan_done(ecrnx_hw->scan_request, false);
+#endif
+    ecrnx_hw->scan_request = NULL;
+
+    return 0;
+}
+
+static inline int ecrnx_rx_scanu_start_cfm(struct ecrnx_hw *ecrnx_hw,
+                                          struct ecrnx_cmd *cmd,
+                                          struct ipc_e2a_msg *msg)
+{
+    struct scanu_start_cfm* cfm = (struct scanu_start_cfm*)msg->param;
+    u8_l abort_status;
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    ECRNX_DBG("receive scanu cfm, status:%d \n", cfm->status);
+    abort_status = cfm->status?true:false;
+
+#ifndef CONFIG_ECRNX_ESWIN
+    ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &ecrnx_hw->scan_ie);
+#endif
+
+    spin_lock_bh(&ecrnx_hw->scan_req_lock);
+    if (ecrnx_hw->scan_request) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+        struct cfg80211_scan_info info = {
+            .aborted = abort_status,
+        };
+
+        ECRNX_PRINT("%s: cfm status:%d, scan_request:0x%p \n", __func__, cfm->status, ecrnx_hw->scan_request);
+        cfg80211_scan_done(ecrnx_hw->scan_request, &info);
+#else
+        cfg80211_scan_done(ecrnx_hw->scan_request, abort_status);
+#endif
+    }
+
+    ecrnx_hw->scan_request = NULL;
+    spin_unlock_bh(&ecrnx_hw->scan_req_lock);
+
+    return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+u64_l getBootTime(void)
+{
+       struct timespec64 ts;
+       u64_l bootTime = 0;
+
+       ts = ktime_to_timespec64(ktime_get_boottime());
+       bootTime = ts.tv_sec;
+       bootTime *= 1000000;
+       bootTime += ts.tv_nsec / 1000;
+       return bootTime;
+}
+#endif
+static inline int ecrnx_rx_scanu_result_ind(struct ecrnx_hw *ecrnx_hw,
+                                           struct ecrnx_cmd *cmd,
+                                           struct ipc_e2a_msg *msg)
+{
+    struct cfg80211_bss *bss = NULL;
+    struct ieee80211_channel *chan;
+    struct scanu_result_ind *ind = (struct scanu_result_ind *)msg->param;
+    struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)ind->payload;
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    chan = ieee80211_get_channel(ecrnx_hw->wiphy, ind->center_freq);
+
+    if (chan != NULL)
+    {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+        if(ieee80211_is_beacon(mgmt->frame_control))
+        {
+             mgmt->u.beacon.timestamp = getBootTime();
+        }
+        if(ieee80211_is_probe_resp(mgmt->frame_control))
+        {
+             mgmt->u.probe_resp.timestamp = getBootTime();
+        }
+#endif
+        bss = cfg80211_inform_bss_frame(ecrnx_hw->wiphy, chan,
+                                        mgmt,
+                                        ind->length, ind->rssi * 100, GFP_ATOMIC);
+    }
+
+    if (bss != NULL)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+        cfg80211_put_bss(ecrnx_hw->wiphy, bss);
+#else
+        cfg80211_put_bss(bss);
+#endif
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+       ecrnx_debugfs_survey_info_update(ecrnx_hw, bss);
+#endif
+
+    return 0;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from ME task
+ **************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+static inline int ecrnx_rx_me_tkip_mic_failure_ind(struct ecrnx_hw *ecrnx_hw,
+                                                  struct ecrnx_cmd *cmd,
+                                                  struct ipc_e2a_msg *msg)
+{
+    struct me_tkip_mic_failure_ind *ind = (struct me_tkip_mic_failure_ind *)msg->param;
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+    struct net_device *dev = ecrnx_vif->ndev;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    cfg80211_michael_mic_failure(dev, (u8 *)&ind->addr, (ind->ga?NL80211_KEYTYPE_GROUP:
+                                 NL80211_KEYTYPE_PAIRWISE), ind->keyid,
+                                 (u8 *)&ind->tsc, GFP_ATOMIC);
+
+    return 0;
+}
+
+static inline int ecrnx_rx_me_tx_credits_update_ind(struct ecrnx_hw *ecrnx_hw,
+                                                   struct ecrnx_cmd *cmd,
+                                                   struct ipc_e2a_msg *msg)
+{
+    struct me_tx_credits_update_ind *ind = (struct me_tx_credits_update_ind *)msg->param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    ecrnx_txq_credit_update(ecrnx_hw, ind->sta_idx, ind->tid, ind->credits);
+
+    return 0;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from SM task
+ **************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+static inline int ecrnx_rx_sm_connect_ind(struct ecrnx_hw *ecrnx_hw,
+                                         struct ecrnx_cmd *cmd,
+                                         struct ipc_e2a_msg *msg)
+{
+    struct sm_connect_ind *ind = (struct sm_connect_ind *)msg->param;
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+    struct net_device *dev = ecrnx_vif->ndev;
+    const u8 *req_ie, *rsp_ie;
+    const u8 *extcap_ie;
+    const struct ieee_types_extcap *extcap;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    spin_lock_bh(&ecrnx_hw->connect_req_lock);
+    /* Retrieve IE addresses and lengths */
+    req_ie = (const u8 *)ind->assoc_ie_buf;
+    rsp_ie = req_ie + ind->assoc_req_ie_len;
+
+    // Fill-in the AP information
+    if (ind->status_code == 0)
+    {
+        struct ecrnx_sta *sta = &ecrnx_hw->sta_table[ind->ap_idx];
+        u8 txq_status;
+        struct ieee80211_channel *chan;
+        struct cfg80211_chan_def chandef;
+
+        sta->valid = true;
+        memset(&sta->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+        memset(&ecrnx_vif->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+        sta->sta_idx = ind->ap_idx;
+        sta->ch_idx = ind->ch_idx;
+        sta->vif_idx = ind->vif_idx;
+        sta->vlan_idx = sta->vif_idx;
+        sta->qos = ind->qos;
+        sta->acm = ind->acm;
+        sta->ps.active = false;
+        sta->aid = ind->aid;
+        sta->band = ind->chan.band;
+        sta->width = ind->chan.type;
+        sta->center_freq = ind->chan.prim20_freq;
+        sta->center_freq1 = ind->chan.center1_freq;
+        sta->center_freq2 = ind->chan.center2_freq;
+        ecrnx_vif->sta.ap = sta;
+        ecrnx_vif->generation++;
+        chan = ieee80211_get_channel(ecrnx_hw->wiphy, ind->chan.prim20_freq);
+        cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+        if (!ecrnx_hw->mod_params->ht_on)
+            chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+        else
+            chandef.width = chnl2bw[ind->chan.type];
+        chandef.center_freq1 = ind->chan.center1_freq;
+        chandef.center_freq2 = ind->chan.center2_freq;
+        ecrnx_chanctx_link(ecrnx_vif, ind->ch_idx, &chandef);
+        memcpy(sta->mac_addr, ind->bssid.array, ETH_ALEN);
+        if (ind->ch_idx == ecrnx_hw->cur_chanctx) {
+            txq_status = 0;
+        } else {
+            txq_status = ECRNX_TXQ_STOP_CHAN;
+        }
+        memcpy(sta->ac_param, ind->ac_param, sizeof(sta->ac_param));
+        ecrnx_txq_sta_init(ecrnx_hw, sta, txq_status);
+        ecrnx_rx_reord_sta_init(ecrnx_hw, ecrnx_hw->vif_table[sta->vif_idx], sta->sta_idx);
+        ecrnx_dbgfs_register_sta(ecrnx_hw, sta);
+        ECRNX_PRINT("ecrnx_rx_sm_connect_ind, mac[%02x:%02x:%02x:%02x:%02x:%02x], status_code:%d \n", \
+                sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2], sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5], ind->status_code);
+        ecrnx_txq_tdls_vif_init(ecrnx_vif);
+        ecrnx_mu_group_sta_init(sta, NULL);
+        /* Look for TDLS Channel Switch Prohibited flag in the Extended Capability
+         * Information Element*/
+        extcap_ie = cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, rsp_ie, ind->assoc_rsp_ie_len);
+        if (extcap_ie && extcap_ie[1] >= 5) {
+            extcap = (void *)(extcap_ie);
+            ecrnx_vif->tdls_chsw_prohibited = extcap->ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED;
+        }
+
+#ifdef CONFIG_ECRNX_BFMER
+        /* If Beamformer feature is activated, check if features can be used
+         * with the new peer device
+         */
+        if (ecrnx_hw->mod_params->bfmer) {
+            const u8 *vht_capa_ie;
+            const struct ieee80211_vht_cap *vht_cap;
+
+            do {
+                /* Look for VHT Capability Information Element */
+                vht_capa_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, rsp_ie,
+                                               ind->assoc_rsp_ie_len);
+
+                /* Stop here if peer device does not support VHT */
+                if (!vht_capa_ie) {
+                    break;
+                }
+
+                vht_cap = (const struct ieee80211_vht_cap *)(vht_capa_ie + 2);
+
+                /* Send MM_BFMER_ENABLE_REQ message if needed */
+                ecrnx_send_bfmer_enable(ecrnx_hw, sta, vht_cap);
+            } while (0);
+        }
+#endif //(CONFIG_ECRNX_BFMER)
+
+#ifdef CONFIG_ECRNX_MON_DATA
+        // If there are 1 sta and 1 monitor interface active at the same time then
+        // monitor interface channel context is always the same as the STA interface.
+        // This doesn't work with 2 STA interfaces but we don't want to support it.
+        if (ecrnx_hw->monitor_vif != ECRNX_INVALID_VIF) {
+            struct ecrnx_vif *ecrnx_mon_vif = ecrnx_hw->vif_table[ecrnx_hw->monitor_vif];
+            ecrnx_chanctx_unlink(ecrnx_mon_vif);
+            ecrnx_chanctx_link(ecrnx_mon_vif, ind->ch_idx, NULL);
+        }
+#endif
+    }
+
+    if (ind->roamed) {
+        struct cfg80211_roam_info info;
+        memset(&info, 0, sizeof(info));
+        if (ecrnx_vif->ch_index < NX_CHAN_CTXT_CNT)
+            info.channel = ecrnx_hw->chanctx_table[ecrnx_vif->ch_index].chan_def.chan;
+        info.bssid = (const u8 *)ind->bssid.array;
+        info.req_ie = req_ie;
+        info.req_ie_len = ind->assoc_req_ie_len;
+        info.resp_ie = rsp_ie;
+        info.resp_ie_len = ind->assoc_rsp_ie_len;
+        cfg80211_roamed(dev, &info, GFP_ATOMIC);
+    } else {
+        cfg80211_connect_result(dev, (const u8 *)ind->bssid.array, req_ie,
+                                ind->assoc_req_ie_len, rsp_ie,
+                                ind->assoc_rsp_ie_len, ind->status_code,
+                                GFP_ATOMIC);
+    }
+
+    netif_tx_start_all_queues(dev);
+    netif_carrier_on(dev);
+    spin_unlock_bh(&ecrnx_hw->connect_req_lock);
+
+    return 0;
+}
+
+static inline int ecrnx_rx_sm_disconnect_ind(struct ecrnx_hw *ecrnx_hw,
+                                            struct ecrnx_cmd *cmd,
+                                            struct ipc_e2a_msg *msg)
+{
+    struct sm_disconnect_ind *ind = (struct sm_disconnect_ind *)msg->param;
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+    struct net_device *dev = ecrnx_vif->ndev;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    ECRNX_PRINT("%s:dev:%p, vif_up:%d, reason_code:%d \n", __func__, dev, ecrnx_vif->up, ind->reason_code);
+    /* if vif is not up, ecrnx_close has already been called */
+    if (ecrnx_vif->up) {
+        if (!ind->reassoc) {
+            cfg80211_disconnected(dev, ind->reason_code, NULL, 0,
+                                  (ind->reason_code <= 1), GFP_ATOMIC);
+            if (ecrnx_vif->sta.ft_assoc_ies) {
+                kfree(ecrnx_vif->sta.ft_assoc_ies);
+                ecrnx_vif->sta.ft_assoc_ies = NULL;
+                ecrnx_vif->sta.ft_assoc_ies_len = 0;
+            }
+        }
+        netif_tx_stop_all_queues(dev);
+        netif_carrier_off(dev);
+    }
+
+    if (ecrnx_vif->sta.ap && ecrnx_vif->sta.ap->valid)
+    {
+        ecrnx_dbgfs_unregister_sta(ecrnx_hw, ecrnx_vif->sta.ap);
+#ifdef CONFIG_ECRNX_BFMER
+        /* Disable Beamformer if supported */
+        ecrnx_bfmer_report_del(ecrnx_hw, ecrnx_vif->sta.ap);
+#endif //(CONFIG_ECRNX_BFMER)
+
+        ecrnx_txq_sta_deinit(ecrnx_hw, ecrnx_vif->sta.ap);
+        ecrnx_rx_reord_sta_deinit(ecrnx_hw, ecrnx_vif->sta.ap->sta_idx, true);
+        ecrnx_vif->sta.ap->valid = false;
+        ecrnx_vif->sta.ap = NULL;
+    }
+    ecrnx_txq_tdls_vif_deinit(ecrnx_vif);
+    //ecrnx_dbgfs_unregister_sta(ecrnx_hw, ecrnx_vif->sta.ap);
+    ecrnx_vif->generation++;
+    ecrnx_external_auth_disable(ecrnx_vif);
+    ecrnx_chanctx_unlink(ecrnx_vif);
+
+    return 0;
+}
+
+static inline int ecrnx_rx_sm_external_auth_required_ind(struct ecrnx_hw *ecrnx_hw,
+                                                        struct ecrnx_cmd *cmd,
+                                                        struct ipc_e2a_msg *msg)
+{
+    struct sm_external_auth_required_ind *ind =
+        (struct sm_external_auth_required_ind *)msg->param;
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+    struct net_device *dev = ecrnx_vif->ndev;
+    struct cfg80211_external_auth_params params;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    params.action = NL80211_EXTERNAL_AUTH_START;
+    memcpy(params.bssid, ind->bssid.array, ETH_ALEN);
+    params.ssid.ssid_len = ind->ssid.length;
+    memcpy(params.ssid.ssid, ind->ssid.array,
+           min_t(size_t, ind->ssid.length, sizeof(params.ssid.ssid)));
+    params.key_mgmt_suite = ind->akm;
+
+    if ((ind->vif_idx > NX_VIRT_DEV_MAX) || !ecrnx_vif->up ||
+        (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_STATION) ||
+        cfg80211_external_auth_request(dev, &params, GFP_ATOMIC)) {
+        wiphy_err(ecrnx_hw->wiphy, "Failed to start external auth on vif %d",
+                  ind->vif_idx);
+        ecrnx_send_sm_external_auth_required_rsp(ecrnx_hw, ecrnx_vif,
+                                                WLAN_STATUS_UNSPECIFIED_FAILURE);
+        return 0;
+    }
+
+    ecrnx_external_auth_enable(ecrnx_vif);
+#else
+    ecrnx_send_sm_external_auth_required_rsp(ecrnx_hw, ecrnx_vif,
+                                            WLAN_STATUS_UNSPECIFIED_FAILURE);
+#endif
+    return 0;
+}
+
+static inline int ecrnx_rx_sm_ft_auth_ind(struct ecrnx_hw *ecrnx_hw,
+                                         struct ecrnx_cmd *cmd,
+                                         struct ipc_e2a_msg *msg)
+{
+    struct sm_ft_auth_ind *ind = (struct sm_ft_auth_ind *)msg->param;
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+    struct sk_buff *skb;
+    size_t data_len = (offsetof(struct ieee80211_mgmt, u.auth.variable) +
+                       ind->ft_ie_len);
+    skb = dev_alloc_skb(data_len);
+    if (skb) {
+        struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb_put(skb, data_len);
+        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
+        memcpy(mgmt->u.auth.variable, ind->ft_ie_buf, ind->ft_ie_len);
+        ecrnx_rx_defer_skb(ecrnx_hw, ecrnx_vif, skb);
+        dev_kfree_skb(skb);
+    } else {
+        netdev_warn(ecrnx_vif->ndev, "Allocation failed for FT auth ind\n");
+    }
+    return 0;
+}
+static inline int ecrnx_rx_twt_setup_ind(struct ecrnx_hw *ecrnx_hw,
+                                        struct ecrnx_cmd *cmd,
+                                        struct ipc_e2a_msg *msg)
+{
+    struct twt_setup_ind *ind = (struct twt_setup_ind *)msg->param;
+    struct ecrnx_sta *ecrnx_sta = &ecrnx_hw->sta_table[ind->sta_idx];
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    memcpy(&ecrnx_sta->twt_ind, ind, sizeof(struct twt_setup_ind));
+    return 0;
+}
+
+static inline int ecrnx_rx_mesh_path_create_cfm(struct ecrnx_hw *ecrnx_hw,
+                                               struct ecrnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+    struct mesh_path_create_cfm *cfm = (struct mesh_path_create_cfm *)msg->param;
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[cfm->vif_idx];
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Check we well have a Mesh Point Interface */
+    if (ecrnx_vif && (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MESH_POINT))
+        ecrnx_vif->ap.flags &= ~ECRNX_AP_CREATE_MESH_PATH;
+
+    return 0;
+}
+
+static inline int ecrnx_rx_mesh_peer_update_ind(struct ecrnx_hw *ecrnx_hw,
+                                               struct ecrnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+    struct mesh_peer_update_ind *ind = (struct mesh_peer_update_ind *)msg->param;
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+    struct ecrnx_sta *ecrnx_sta = &ecrnx_hw->sta_table[ind->sta_idx];
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if ((ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)) ||
+        (ecrnx_vif && (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)) ||
+        (ind->sta_idx >= NX_REMOTE_STA_MAX))
+        return 1;
+
+    if (ecrnx_vif->ap.flags & ECRNX_AP_USER_MESH_PM)
+    {
+        if (!ind->estab && ecrnx_sta->valid) {
+            ecrnx_sta->ps.active = false;
+            ecrnx_sta->valid = false;
+            list_del_init(&ecrnx_sta->list);
+            ecrnx_txq_sta_deinit(ecrnx_hw, ecrnx_sta);
+            ecrnx_dbgfs_unregister_sta(ecrnx_hw, ecrnx_sta);
+        } else {
+            WARN_ON(0);
+        }
+    } else {
+        /* Check if peer link has been established or lost */
+        if (ind->estab) {
+            if (!ecrnx_sta->valid) {
+                u8 txq_status;
+
+                ecrnx_sta->valid = true;
+                ecrnx_sta->sta_idx = ind->sta_idx;
+                ecrnx_sta->ch_idx = ecrnx_vif->ch_index;
+                ecrnx_sta->vif_idx = ind->vif_idx;
+                ecrnx_sta->vlan_idx = ecrnx_sta->vif_idx;
+                ecrnx_sta->ps.active = false;
+                ecrnx_sta->qos = true;
+                ecrnx_sta->aid = ind->sta_idx + 1;
+                //ecrnx_sta->acm = ind->acm;
+                memcpy(ecrnx_sta->mac_addr, ind->peer_addr.array, ETH_ALEN);
+
+                ecrnx_chanctx_link(ecrnx_vif, ecrnx_sta->ch_idx, NULL);
+
+                /* Add the station in the list of VIF's stations */
+                INIT_LIST_HEAD(&ecrnx_sta->list);
+                list_add_tail(&ecrnx_sta->list, &ecrnx_vif->ap.sta_list);
+
+                /* Initialize the TX queues */
+                if (ecrnx_sta->ch_idx == ecrnx_hw->cur_chanctx) {
+                    txq_status = 0;
+                } else {
+                    txq_status = ECRNX_TXQ_STOP_CHAN;
+                }
+
+                ecrnx_txq_sta_init(ecrnx_hw, ecrnx_sta, txq_status);
+                ecrnx_dbgfs_register_sta(ecrnx_hw, ecrnx_sta);
+
+#ifdef CONFIG_ECRNX_BFMER
+                // TODO: update indication to contains vht capabilties
+                if (ecrnx_hw->mod_params->bfmer)
+                    ecrnx_send_bfmer_enable(ecrnx_hw, ecrnx_sta, NULL);
+
+                ecrnx_mu_group_sta_init(ecrnx_sta, NULL);
+#endif /* CONFIG_ECRNX_BFMER */
+
+            } else {
+                WARN_ON(0);
+            }
+        } else {
+            if (ecrnx_sta->valid) {
+                ecrnx_sta->ps.active = false;
+                ecrnx_sta->valid = false;
+
+                /* Remove the station from the list of VIF's station */
+                list_del_init(&ecrnx_sta->list);
+
+                ecrnx_txq_sta_deinit(ecrnx_hw, ecrnx_sta);
+                ecrnx_dbgfs_unregister_sta(ecrnx_hw, ecrnx_sta);
+            } else {
+                WARN_ON(0);
+            }
+            /* There is no way to inform upper layer for lost of peer, still
+               clean everything in the driver */
+
+            /* Remove the station from the list of VIF's station */
+
+        }
+    }
+
+    return 0;
+}
+
+static inline int ecrnx_rx_mesh_path_update_ind(struct ecrnx_hw *ecrnx_hw,
+                                               struct ecrnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+    struct mesh_path_update_ind *ind = (struct mesh_path_update_ind *)msg->param;
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+    struct ecrnx_mesh_path *mesh_path;
+    bool found = false;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX))
+        return 1;
+
+    if (!ecrnx_vif || (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT))
+        return 0;
+
+    /* Look for path with provided target address */
+    list_for_each_entry(mesh_path, &ecrnx_vif->ap.mpath_list, list) {
+        if (mesh_path->path_idx == ind->path_idx) {
+            found = true;
+            break;
+        }
+    }
+
+    /* Check if element has been deleted */
+    if (ind->delete) {
+        if (found) {
+            trace_mesh_delete_path(mesh_path);
+            /* Remove element from list */
+            list_del_init(&mesh_path->list);
+            /* Free the element */
+            kfree(mesh_path);
+        }
+    }
+    else {
+        if (found) {
+            // Update the Next Hop STA
+            mesh_path->nhop_sta = &ecrnx_hw->sta_table[ind->nhop_sta_idx];
+            trace_mesh_update_path(mesh_path);
+        } else {
+            // Allocate a Mesh Path structure
+            mesh_path = kmalloc(sizeof(struct ecrnx_mesh_path), GFP_ATOMIC);
+
+            if (mesh_path) {
+                INIT_LIST_HEAD(&mesh_path->list);
+
+                mesh_path->path_idx = ind->path_idx;
+                mesh_path->nhop_sta = &ecrnx_hw->sta_table[ind->nhop_sta_idx];
+                memcpy(&mesh_path->tgt_mac_addr, &ind->tgt_mac_addr, MAC_ADDR_LEN);
+
+                // Insert the path in the list of path
+                list_add_tail(&mesh_path->list, &ecrnx_vif->ap.mpath_list);
+
+                trace_mesh_create_path(mesh_path);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static inline int ecrnx_rx_mesh_proxy_update_ind(struct ecrnx_hw *ecrnx_hw,
+                                               struct ecrnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+    struct mesh_proxy_update_ind *ind = (struct mesh_proxy_update_ind *)msg->param;
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+    struct ecrnx_mesh_proxy *mesh_proxy;
+    bool found = false;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if (ind->vif_idx >= (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX))
+        return 1;
+
+    if (!ecrnx_vif || (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT))
+        return 0;
+
+    /* Look for path with provided external STA address */
+    list_for_each_entry(mesh_proxy, &ecrnx_vif->ap.proxy_list, list) {
+        if (!memcmp(&ind->ext_sta_addr, &mesh_proxy->ext_sta_addr, ETH_ALEN)) {
+            found = true;
+            break;
+        }
+    }
+
+    if (ind->delete && found) {
+        /* Delete mesh path */
+        list_del_init(&mesh_proxy->list);
+        kfree(mesh_proxy);
+    } else if (!ind->delete && !found) {
+        /* Allocate a Mesh Path structure */
+        mesh_proxy = (struct ecrnx_mesh_proxy *)kmalloc(sizeof(*mesh_proxy),
+                                                       GFP_ATOMIC);
+
+        if (mesh_proxy) {
+            INIT_LIST_HEAD(&mesh_proxy->list);
+
+            memcpy(&mesh_proxy->ext_sta_addr, &ind->ext_sta_addr, MAC_ADDR_LEN);
+            mesh_proxy->local = ind->local;
+
+            if (!ind->local) {
+                memcpy(&mesh_proxy->proxy_addr, &ind->proxy_mac_addr, MAC_ADDR_LEN);
+            }
+
+            /* Insert the path in the list of path */
+            list_add_tail(&mesh_proxy->list, &ecrnx_vif->ap.proxy_list);
+        }
+    }
+
+    return 0;
+}
+
+/***************************************************************************
+ * Messages from APM task
+ **************************************************************************/
+static inline int ecrnx_rx_apm_probe_client_ind(struct ecrnx_hw *ecrnx_hw,
+                                               struct ecrnx_cmd *cmd,
+                                               struct ipc_e2a_msg *msg)
+{
+    struct apm_probe_client_ind *ind = (struct apm_probe_client_ind *)msg->param;
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ind->vif_idx];
+    struct ecrnx_sta *ecrnx_sta = &ecrnx_hw->sta_table[ind->sta_idx];
+
+    ecrnx_sta->stats.last_act = jiffies;
+    cfg80211_probe_status(ecrnx_vif->ndev, ecrnx_sta->mac_addr, (u64)ind->probe_id,
+                          ind->client_present, 0, false, GFP_ATOMIC);
+    return 0;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/***************************************************************************
+ * Messages from DEBUG task
+ **************************************************************************/
+static inline int ecrnx_rx_dbg_error_ind(struct ecrnx_hw *ecrnx_hw,
+                                        struct ecrnx_cmd *cmd,
+                                        struct ipc_e2a_msg *msg)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    ecrnx_error_ind(ecrnx_hw);
+
+    return 0;
+}
+
+
+
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+
+static msg_cb_fct mm_hdlrs[MSG_I(MM_MAX)] = {
+    [MSG_I(MM_CONNECTION_LOSS_IND)]       = ecrnx_rx_connection_loss_ind,
+    [MSG_I(MM_CHANNEL_SWITCH_IND)]        = ecrnx_rx_chan_switch_ind,
+    [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)]    = ecrnx_rx_chan_pre_switch_ind,
+    [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = ecrnx_rx_remain_on_channel_exp_ind,
+#ifdef CONFIG_ECRNX_BCN
+    [MSG_I(MM_PRIMARY_TBTT_IND)]          = ecrnx_rx_prm_tbtt_ind,
+#endif
+    [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)]     = ecrnx_rx_p2p_vif_ps_change_ind,
+    [MSG_I(MM_CSA_COUNTER_IND)]           = ecrnx_rx_csa_counter_ind,
+    [MSG_I(MM_CHANNEL_SURVEY_IND)]        = ecrnx_rx_channel_survey_ind,
+    [MSG_I(MM_RSSI_STATUS_IND)]           = ecrnx_rx_rssi_status_ind,
+};
+
+static msg_cb_fct scan_hdlrs[MSG_I(SCAN_MAX)] = {
+    [MSG_I(SCAN_DONE_IND)]                = ecrnx_rx_scan_done_ind,
+};
+
+#else  /* CONFIG_ECRNX_FULLMAC */
+
+static msg_cb_fct mm_hdlrs[MSG_I(MM_MAX)] = {
+    [MSG_I(MM_CHANNEL_SWITCH_IND)]     = ecrnx_rx_chan_switch_ind,
+    [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)] = ecrnx_rx_chan_pre_switch_ind,
+    [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = ecrnx_rx_remain_on_channel_exp_ind,
+    [MSG_I(MM_PS_CHANGE_IND)]          = ecrnx_rx_ps_change_ind,
+    [MSG_I(MM_TRAFFIC_REQ_IND)]        = ecrnx_rx_traffic_req_ind,
+    [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)]  = ecrnx_rx_p2p_vif_ps_change_ind,
+    [MSG_I(MM_CSA_COUNTER_IND)]        = ecrnx_rx_csa_counter_ind,
+    [MSG_I(MM_CSA_FINISH_IND)]         = ecrnx_rx_csa_finish_ind,
+    [MSG_I(MM_CSA_TRAFFIC_IND)]        = ecrnx_rx_csa_traffic_ind,
+    [MSG_I(MM_CHANNEL_SURVEY_IND)]     = ecrnx_rx_channel_survey_ind,
+    [MSG_I(MM_P2P_NOA_UPD_IND)]        = ecrnx_rx_p2p_noa_upd_ind,
+    [MSG_I(MM_RSSI_STATUS_IND)]        = ecrnx_rx_rssi_status_ind,
+    [MSG_I(MM_PKTLOSS_IND)]            = ecrnx_rx_pktloss_notify_ind,
+};
+
+static msg_cb_fct scan_hdlrs[MSG_I(SCANU_MAX)] = {
+    [MSG_I(SCANU_START_CFM)]           = ecrnx_rx_scanu_start_cfm,
+    [MSG_I(SCANU_RESULT_IND)]          = ecrnx_rx_scanu_result_ind,
+    [MSG_I(SCANU_CANCEL_CFM)]          = ecrnx_scanu_cancel_cfm,
+};
+
+static msg_cb_fct me_hdlrs[MSG_I(ME_MAX)] = {
+    [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = ecrnx_rx_me_tkip_mic_failure_ind,
+    [MSG_I(ME_TX_CREDITS_UPDATE_IND)] = ecrnx_rx_me_tx_credits_update_ind,
+};
+
+static msg_cb_fct sm_hdlrs[MSG_I(SM_MAX)] = {
+    [MSG_I(SM_CONNECT_IND)]    = ecrnx_rx_sm_connect_ind,
+    [MSG_I(SM_DISCONNECT_IND)] = ecrnx_rx_sm_disconnect_ind,
+    [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = ecrnx_rx_sm_external_auth_required_ind,
+    [MSG_I(SM_FT_AUTH_IND)] = ecrnx_rx_sm_ft_auth_ind,
+};
+
+static msg_cb_fct apm_hdlrs[MSG_I(APM_MAX)] = {
+    [MSG_I(APM_PROBE_CLIENT_IND)] = ecrnx_rx_apm_probe_client_ind,
+};
+static msg_cb_fct twt_hdlrs[MSG_I(TWT_MAX)] = {
+    [MSG_I(TWT_SETUP_IND)]    = ecrnx_rx_twt_setup_ind,
+};
+
+static msg_cb_fct mesh_hdlrs[MSG_I(MESH_MAX)] = {
+    [MSG_I(MESH_PATH_CREATE_CFM)]  = ecrnx_rx_mesh_path_create_cfm,
+    [MSG_I(MESH_PEER_UPDATE_IND)]  = ecrnx_rx_mesh_peer_update_ind,
+    [MSG_I(MESH_PATH_UPDATE_IND)]  = ecrnx_rx_mesh_path_update_ind,
+    [MSG_I(MESH_PROXY_UPDATE_IND)] = ecrnx_rx_mesh_proxy_update_ind,
+};
+
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+static msg_cb_fct dbg_hdlrs[MSG_I(DBG_MAX)] = {
+    [MSG_I(DBG_ERROR_IND)]                = ecrnx_rx_dbg_error_ind,
+};
+
+static msg_cb_fct tdls_hdlrs[MSG_I(TDLS_MAX)] = {
+    [MSG_I(TDLS_CHAN_SWITCH_CFM)] = ecrnx_rx_tdls_chan_switch_cfm,
+    [MSG_I(TDLS_CHAN_SWITCH_IND)] = ecrnx_rx_tdls_chan_switch_ind,
+    [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)] = ecrnx_rx_tdls_chan_switch_base_ind,
+    [MSG_I(TDLS_PEER_PS_IND)] = ecrnx_rx_tdls_peer_ps_ind,
+};
+
+static msg_cb_fct *msg_hdlrs[] = {
+    [TASK_MM]    = mm_hdlrs,
+    [TASK_DBG]   = dbg_hdlrs,
+#ifdef CONFIG_ECRNX_SOFTMAC
+    [TASK_SCAN]  = scan_hdlrs,
+    [TASK_TDLS]  = tdls_hdlrs,
+#else
+    [TASK_TDLS]  = tdls_hdlrs,
+    [TASK_SCANU] = scan_hdlrs,
+    [TASK_ME]    = me_hdlrs,
+    [TASK_SM]    = sm_hdlrs,
+    [TASK_APM]   = apm_hdlrs,
+    [TASK_MESH]  = mesh_hdlrs,
+    [TASK_TWT]   = twt_hdlrs,
+#if 0//(CONFIG_ECRNX_P2P)
+    [TASK_P2P_LISTEN]  = p2p_listen_hdlrs,
+#endif
+#endif /* CONFIG_ECRNX_SOFTMAC */
+};
+
+/**
+ *
+ */
+void ecrnx_rx_handle_msg(struct ecrnx_hw *ecrnx_hw, struct ipc_e2a_msg *msg)
+{
+
+    if(!ecrnx_hw || !msg || !(&ecrnx_hw->cmd_mgr))
+    {
+        ECRNX_ERR("ecrnx_rx_handle_msg:receive msg info error \n");
+        return;
+    }
+
+#if defined(CONFIG_ECRNX_P2P)
+    if (MSG_T(msg->id) != TASK_P2P_LISTEN && (MSG_T(msg->id) < TASK_MM || MSG_T(msg->id) > TASK_MESH || MSG_I(msg->id) >= MSG_I(MM_MAX)))
+#else
+    if (MSG_T(msg->id) < TASK_MM || MSG_T(msg->id) > TASK_MESH || MSG_I(msg->id) >= MSG_I(MM_MAX))
+#endif
+    {
+        ECRNX_ERR("msg id 0x%x,%d,%d, max %d, dst:0x%x, src:0x%x\n", msg->id, MSG_T(msg->id), MSG_I(msg->id), MSG_I(MM_MAX), msg->dummy_dest_id, msg->dummy_src_id);
+       ECRNX_ERR("skip msg %p\n", msg);
+       return;
+    }
+
+    if(ecrnx_hw->wiphy != NULL)
+    {
+        ecrnx_hw->cmd_mgr.msgind(&ecrnx_hw->cmd_mgr, msg,
+                                msg_hdlrs[MSG_T(msg->id)][MSG_I(msg->id)]);
+    }
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_msg_rx.h b/drivers/net/wireless/eswin/ecrnx_msg_rx.h
new file mode 100644 (file)
index 0000000..66fad20
--- /dev/null
@@ -0,0 +1,18 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_msg_rx.h
+ *
+ * @brief RX function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_MSG_RX_H_
+#define _ECRNX_MSG_RX_H_
+
+void ecrnx_rx_handle_msg(struct ecrnx_hw *ecrnx_hw, struct ipc_e2a_msg *msg);
+
+#endif /* _ECRNX_MSG_RX_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_msg_tx.c b/drivers/net/wireless/eswin/ecrnx_msg_tx.c
new file mode 100644 (file)
index 0000000..bc51b9b
--- /dev/null
@@ -0,0 +1,3173 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_msg_tx.c
+ *
+ * @brief TX function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_mod_params.h"
+#include "reg_access.h"
+#ifdef CONFIG_ECRNX_BFMER
+#include "ecrnx_bfmer.h"
+#endif //(CONFIG_ECRNX_BFMER)
+#include "ecrnx_compat.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_calibration_data.h"
+#include "eswin_utils.h"
+#include "core.h"
+
+const struct mac_addr mac_addr_bcst = {{0xFFFF, 0xFFFF, 0xFFFF}};
+
+/* Default MAC Rx filters that can be changed by mac80211
+ * (via the configure_filter() callback) */
+#define ECRNX_MAC80211_CHANGEABLE        (                                       \
+                                         NXMAC_ACCEPT_BA_BIT                  | \
+                                         NXMAC_ACCEPT_BAR_BIT                 | \
+                                         NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT   | \
+                                         NXMAC_ACCEPT_PROBE_REQ_BIT           | \
+                                         NXMAC_ACCEPT_PS_POLL_BIT               \
+                                        )
+
+/* Default MAC Rx filters that cannot be changed by mac80211 */
+#define ECRNX_MAC80211_NOT_CHANGEABLE    (                                       \
+                                         NXMAC_ACCEPT_QO_S_NULL_BIT           | \
+                                         NXMAC_ACCEPT_Q_DATA_BIT              | \
+                                         NXMAC_ACCEPT_DATA_BIT                | \
+                                         NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT   | \
+                                         NXMAC_ACCEPT_MY_UNICAST_BIT          | \
+                                         NXMAC_ACCEPT_BROADCAST_BIT           | \
+                                         NXMAC_ACCEPT_BEACON_BIT              | \
+                                         NXMAC_ACCEPT_PROBE_RESP_BIT            \
+                                        )
+
+/* Default MAC Rx filter */
+#define ECRNX_DEFAULT_RX_FILTER  (ECRNX_MAC80211_CHANGEABLE | ECRNX_MAC80211_NOT_CHANGEABLE)
+
+const int bw2chnl[] = {
+    [NL80211_CHAN_WIDTH_20_NOHT] = PHY_CHNL_BW_20,
+    [NL80211_CHAN_WIDTH_20]      = PHY_CHNL_BW_20,
+    [NL80211_CHAN_WIDTH_40]      = PHY_CHNL_BW_40,
+    [NL80211_CHAN_WIDTH_80]      = PHY_CHNL_BW_80,
+    [NL80211_CHAN_WIDTH_160]     = PHY_CHNL_BW_160,
+    [NL80211_CHAN_WIDTH_80P80]   = PHY_CHNL_BW_80P80,
+};
+
+const int chnl2bw[] = {
+    [PHY_CHNL_BW_20]      = NL80211_CHAN_WIDTH_20,
+    [PHY_CHNL_BW_40]      = NL80211_CHAN_WIDTH_40,
+    [PHY_CHNL_BW_80]      = NL80211_CHAN_WIDTH_80,
+    [PHY_CHNL_BW_160]     = NL80211_CHAN_WIDTH_160,
+    [PHY_CHNL_BW_80P80]   = NL80211_CHAN_WIDTH_80P80,
+};
+
+/*****************************************************************************/
+/*
+ * Parse the ampdu density to retrieve the value in usec, according to the
+ * values defined in ieee80211.h
+ */
+static inline u8 ecrnx_ampdudensity2usec(u8 ampdudensity)
+{
+    switch (ampdudensity) {
+    case IEEE80211_HT_MPDU_DENSITY_NONE:
+        return 0;
+        /* 1 microsecond is our granularity */
+    case IEEE80211_HT_MPDU_DENSITY_0_25:
+    case IEEE80211_HT_MPDU_DENSITY_0_5:
+    case IEEE80211_HT_MPDU_DENSITY_1:
+        return 1;
+    case IEEE80211_HT_MPDU_DENSITY_2:
+        return 2;
+    case IEEE80211_HT_MPDU_DENSITY_4:
+        return 4;
+    case IEEE80211_HT_MPDU_DENSITY_8:
+        return 8;
+    case IEEE80211_HT_MPDU_DENSITY_16:
+        return 16;
+    default:
+        return 0;
+    }
+}
+
+static inline bool use_pairwise_key(struct cfg80211_crypto_settings *crypto)
+{
+    if ((crypto->cipher_group ==  WLAN_CIPHER_SUITE_WEP40) ||
+        (crypto->cipher_group ==  WLAN_CIPHER_SUITE_WEP104))
+        return false;
+
+    return true;
+}
+
+static inline bool is_non_blocking_msg(int id)
+{
+    return ((id == MM_TIM_UPDATE_REQ) || (id == ME_RC_SET_RATE_REQ) ||
+            (id == MM_BFMER_ENABLE_REQ) || (id == ME_TRAFFIC_IND_REQ) ||
+            (id == TDLS_PEER_TRAFFIC_IND_REQ) ||
+            (id == MESH_PATH_CREATE_REQ) || (id == MESH_PROXY_ADD_REQ) ||
+            (id == SM_EXTERNAL_AUTH_REQUIRED_RSP));
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+/**
+ * copy_connect_ies -- Copy Association Elements in the the request buffer
+ * send to the firmware
+ *
+ * @vif: Vif that received the connection request
+ * @req: Connection request to send to the firmware
+ * @sme: Connection info
+ *
+ * For driver that do not use userspace SME (like this one) the host connection
+ * request doesn't explicitly mentions that the connection can use FT over the
+ * air. if FT is possible, send the FT elements (as received in update_ft_ies callback)
+ * to the firmware
+ *
+ * In all other cases simply copy the list povided by the user space in the
+ * request buffer
+ */
+static void copy_connect_ies(struct ecrnx_vif *vif, struct sm_connect_req *req,
+                            struct cfg80211_connect_params *sme)
+{
+    if ((sme->auth_type == NL80211_AUTHTYPE_FT) && !(vif->sta.flags & ECRNX_STA_FT_OVER_DS))
+    {
+        const struct ecrnx_element *rsne, *fte, *mde;
+        uint8_t *pos;
+        rsne = cfg80211_find_ecrnx_elem(WLAN_EID_RSN, vif->sta.ft_assoc_ies,
+                                    vif->sta.ft_assoc_ies_len);
+        fte = cfg80211_find_ecrnx_elem(WLAN_EID_FAST_BSS_TRANSITION, vif->sta.ft_assoc_ies,
+                                    vif->sta.ft_assoc_ies_len);
+        mde = cfg80211_find_ecrnx_elem(WLAN_EID_MOBILITY_DOMAIN,
+                                         vif->sta.ft_assoc_ies, vif->sta.ft_assoc_ies_len);
+        pos = (uint8_t *)req->ie_buf;
+
+        // We can use FT over the air
+        memcpy(&vif->sta.ft_target_ap, sme->bssid, ETH_ALEN);
+
+        if (rsne) {
+            memcpy(pos, rsne, sizeof(struct ecrnx_element) + rsne->datalen);
+            pos += sizeof(struct ecrnx_element) + rsne->datalen;
+        }
+        memcpy(pos, mde, sizeof(struct ecrnx_element) + mde->datalen);
+        pos += sizeof(struct ecrnx_element) + mde->datalen;
+        if (fte) {
+            memcpy(pos, fte, sizeof(struct ecrnx_element) + fte->datalen);
+            pos += sizeof(struct ecrnx_element) + fte->datalen;
+        }
+
+        req->ie_len = pos - (uint8_t *)req->ie_buf;
+    }
+    else
+    {
+        memcpy(req->ie_buf, sme->ie, sme->ie_len);
+        req->ie_len = sme->ie_len;
+    }
+}
+
+/**
+ * update_connect_req -- Return the length of the association request IEs
+ *
+ * @vif: Vif that received the connection request
+ * @sme: Connection info
+ *
+ * Return the ft_ie_len in case of FT.
+ * FT over the air is possible if:
+ * - auth_type = AUTOMATIC (if already set to FT then it means FT over DS)
+ * - already associated to a FT BSS
+ * - Target Mobility domain is the same as the curent one
+ *
+ * If FT is not possible return ie length of the connection info
+ */
+static int update_connect_req(struct ecrnx_vif *vif, struct cfg80211_connect_params *sme)
+{
+    if ((vif->sta.ap) &&
+        (vif->sta.ft_assoc_ies) &&
+        (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC))
+    {
+        const struct ecrnx_element *rsne, *fte, *mde, *mde_req;
+        int ft_ie_len = 0;
+
+        mde_req = cfg80211_find_ecrnx_elem(WLAN_EID_MOBILITY_DOMAIN,
+                                     sme->ie, sme->ie_len);
+        mde = cfg80211_find_ecrnx_elem(WLAN_EID_MOBILITY_DOMAIN,
+                                 vif->sta.ft_assoc_ies, vif->sta.ft_assoc_ies_len);
+        if (!mde || !mde_req ||
+            memcmp(mde, mde_req, sizeof(struct ecrnx_element) + mde->datalen))
+        {
+            return sme->ie_len;
+        }
+
+        ft_ie_len += sizeof(struct ecrnx_element) + mde->datalen;
+
+        rsne = cfg80211_find_ecrnx_elem(WLAN_EID_RSN, vif->sta.ft_assoc_ies,
+                                    vif->sta.ft_assoc_ies_len);
+        fte = cfg80211_find_ecrnx_elem(WLAN_EID_FAST_BSS_TRANSITION, vif->sta.ft_assoc_ies,
+                                    vif->sta.ft_assoc_ies_len);
+
+        if (rsne && fte)
+        {
+            ft_ie_len += 2 * sizeof(struct ecrnx_element) + rsne->datalen + fte->datalen;
+            sme->auth_type = NL80211_AUTHTYPE_FT;
+            return ft_ie_len;
+        }
+        else if (rsne || fte)
+        {
+            netdev_warn(vif->ndev, "Missing RSNE or FTE element, skip FT over air");
+        }
+        else
+        {
+            sme->auth_type = NL80211_AUTHTYPE_FT;
+            return ft_ie_len;
+        }
+    }
+    return sme->ie_len;
+}
+#endif
+static inline u8_l get_chan_flags(uint32_t flags)
+{
+    u8_l chan_flags = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+    if (flags & IEEE80211_CHAN_PASSIVE_SCAN)
+#else
+    if (flags & IEEE80211_CHAN_NO_IR)
+        chan_flags |= CHAN_NO_IR;
+    if (flags & IEEE80211_CHAN_RADAR)
+        chan_flags |= CHAN_RADAR;
+#endif
+    return chan_flags;
+}
+
+static inline s8_l chan_to_fw_pwr(int power)
+{
+    return power > 127 ? 127 : (s8_l)power;
+}
+
+static void cfg80211_to_ecrnx_chan(const struct cfg80211_chan_def *chandef,
+                                  struct mac_chan_op *chan)
+{
+    chan->band = chandef->chan->band;
+    chan->type = bw2chnl[chandef->width];
+    chan->prim20_freq = chandef->chan->center_freq;
+    chan->center1_freq = chandef->center_freq1;
+    chan->center2_freq = chandef->center_freq2;
+    chan->flags = get_chan_flags(chandef->chan->flags);
+    chan->tx_power = chan_to_fw_pwr(chandef->chan->max_power);
+}
+
+static inline void limit_chan_bw(u8_l *bw, u16_l primary, u16_l *center1)
+{
+    int oft, new_oft = 10;
+
+    if (*bw <= PHY_CHNL_BW_40)
+        return;
+
+    oft = *center1 - primary;
+    *bw = PHY_CHNL_BW_40;
+
+    if (oft < 0)
+        new_oft = new_oft * -1;
+    if (abs(oft) == 10 || abs(oft) == 50)
+        new_oft = new_oft * -1;
+
+    *center1 = primary + new_oft;
+}
+
+/**
+ ******************************************************************************
+ * @brief Allocate memory for a message
+ *
+ * This primitive allocates memory for a message that has to be sent. The memory
+ * is allocated dynamically on the heap and the length of the variable parameter
+ * structure has to be provided in order to allocate the correct size.
+ *
+ * Several additional parameters are provided which will be preset in the message
+ * and which may be used internally to choose the kind of memory to allocate.
+ *
+ * The memory allocated will be automatically freed by the kernel, after the
+ * pointer has been sent to ke_msg_send(). If the message is not sent, it must
+ * be freed explicitly with ke_msg_free().
+ *
+ * Allocation failure is considered critical and should not happen.
+ *
+ * @param[in] id        Message identifier
+ * @param[in] dest_id   Destination Task Identifier
+ * @param[in] src_id    Source Task Identifier
+ * @param[in] param_len Size of the message parameters to be allocated
+ *
+ * @return Pointer to the parameter member of the ke_msg. If the parameter
+ *         structure is empty, the pointer will point to the end of the message
+ *         and should not be used (except to retrieve the message pointer or to
+ *         send the message)
+ ******************************************************************************
+ */
+static inline void *ecrnx_msg_zalloc(lmac_msg_id_t const id,
+                                    lmac_task_id_t const dest_id,
+                                    lmac_task_id_t const src_id,
+                                    uint16_t const param_len)
+{
+    struct lmac_msg *msg;
+    gfp_t flags;
+
+    if (is_non_blocking_msg(id) && in_softirq())
+        flags = GFP_ATOMIC;
+    else
+        flags = GFP_KERNEL;
+
+    msg = (struct lmac_msg *)kzalloc(sizeof(struct lmac_msg) + param_len,
+                                     flags);
+    if (msg == NULL) {
+        ECRNX_ERR(KERN_CRIT "%s: msg allocation failed\n", __func__);
+        return NULL;
+    }
+
+    msg->id = id;
+    msg->dest_id = dest_id;
+    msg->src_id = src_id;
+    msg->param_len = param_len;
+
+    return msg->param;
+}
+
+static void ecrnx_msg_free(struct ecrnx_hw *ecrnx_hw, const void *msg_params)
+{
+    struct lmac_msg *msg = container_of((void *)msg_params,
+                                        struct lmac_msg, param);
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Free the message */
+    kfree(msg);
+}
+
+static int ecrnx_send_msg(struct ecrnx_hw *ecrnx_hw, const void *msg_params,
+                         int reqcfm, lmac_msg_id_t reqid, void *cfm)
+{
+    struct lmac_msg *msg;
+    struct ecrnx_cmd *cmd;
+    bool nonblock;
+    int ret;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    msg = container_of((void *)msg_params, struct lmac_msg, param);
+
+    if (!test_bit(ECRNX_DEV_STARTED, &ecrnx_hw->flags) &&
+        reqid != MM_RESET_CFM && reqid != MM_VERSION_CFM &&
+        reqid != MM_START_CFM && reqid != MM_SET_IDLE_CFM &&
+        reqid != ME_CONFIG_CFM && reqid != MM_SET_PS_MODE_CFM &&
+        reqid != ME_CHAN_CONFIG_CFM && reqid != MM_SET_GAIN_DELTA_CFM &&
+        reqid != MM_GET_CAL_RESULT_CFM) {
+        ECRNX_ERR(KERN_CRIT "%s: bypassing (ECRNX_DEV_RESTARTING set) 0x%02x\n",
+               __func__, reqid);
+        kfree(msg);
+        return -EBUSY;
+    } else if (!ecrnx_hw->ipc_env) {
+        ECRNX_ERR(KERN_CRIT "%s: bypassing (restart must have failed)\n", __func__);
+        kfree(msg);
+        return -EBUSY;
+    }
+
+    nonblock = is_non_blocking_msg(msg->id);
+    
+#if defined(CONFIG_ECRNX_ESWIN_USB)
+    if(register_status == false){
+        kfree(msg);
+        return -ENODEV;
+    }
+#endif
+
+    cmd = kzalloc(sizeof(struct ecrnx_cmd), nonblock ? GFP_ATOMIC : GFP_KERNEL);
+    cmd->result  = -EINTR;
+    cmd->id      = msg->id;
+    cmd->reqid   = reqid;
+    cmd->a2e_msg = msg;
+    cmd->e2a_msg = cfm;
+    if (nonblock)
+        cmd->flags = ECRNX_CMD_FLAG_NONBLOCK;
+    if (reqcfm)
+        cmd->flags |= ECRNX_CMD_FLAG_REQ_CFM;
+    if(ecrnx_hw->wiphy != NULL)
+    {
+        ret = ecrnx_hw->cmd_mgr.queue(&ecrnx_hw->cmd_mgr, cmd);
+    }
+    if (!nonblock)
+        kfree(cmd);
+
+    if (!ret)
+        ret = cmd->result;
+
+    return ret;
+}
+
+/******************************************************************************
+ *    Control messages handling functions (SOFTMAC and  FULLMAC)
+ *****************************************************************************/
+int ecrnx_send_reset(struct ecrnx_hw *ecrnx_hw)
+{
+    void *void_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* RESET REQ has no parameter */
+    void_param = ecrnx_msg_zalloc(MM_RESET_REQ, TASK_MM, DRV_TASK_ID, 0);
+    if (!void_param)
+        return -ENOMEM;
+
+    return ecrnx_send_msg(ecrnx_hw, void_param, 1, MM_RESET_CFM, NULL);
+}
+
+int ecrnx_send_start(struct ecrnx_hw *ecrnx_hw)
+{
+    struct mm_start_req *start_req_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the START REQ message */
+    start_req_param = ecrnx_msg_zalloc(MM_START_REQ, TASK_MM, DRV_TASK_ID,
+                                      sizeof(struct mm_start_req));
+    if (!start_req_param)
+        return -ENOMEM;
+
+    /* Set parameters for the START message */
+    memcpy(&start_req_param->phy_cfg, &ecrnx_hw->phy.cfg, sizeof(ecrnx_hw->phy.cfg));
+    start_req_param->uapsd_timeout = (u32_l)ecrnx_hw->mod_params->uapsd_timeout;
+    start_req_param->lp_clk_accuracy = (u16_l)ecrnx_hw->mod_params->lp_clk_ppm;
+    start_req_param->tx_timeout[AC_BK] = (u16_l)ecrnx_hw->mod_params->tx_to_bk;
+    start_req_param->tx_timeout[AC_BE] = (u16_l)ecrnx_hw->mod_params->tx_to_be;
+    start_req_param->tx_timeout[AC_VI] = (u16_l)ecrnx_hw->mod_params->tx_to_vi;
+    start_req_param->tx_timeout[AC_VO] = (u16_l)ecrnx_hw->mod_params->tx_to_vo;
+
+    /* Send the START REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, start_req_param, 1, MM_START_CFM, NULL);
+}
+
+int ecrnx_send_version_req(struct ecrnx_hw *ecrnx_hw, struct mm_version_cfm *cfm)
+{
+    void *void_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    /* VERSION REQ has no parameter */
+    void_param = ecrnx_msg_zalloc(MM_VERSION_REQ, TASK_MM, DRV_TASK_ID, 0);
+    if (!void_param)
+        return -ENOMEM;
+
+    return ecrnx_send_msg(ecrnx_hw, void_param, 1, MM_VERSION_CFM, cfm);
+}
+
+int ecrnx_send_add_if(struct ecrnx_hw *ecrnx_hw, const unsigned char *mac,
+                     enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm)
+{
+    struct mm_add_if_req *add_if_req_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the ADD_IF_REQ message */
+    add_if_req_param = ecrnx_msg_zalloc(MM_ADD_IF_REQ, TASK_MM, DRV_TASK_ID,
+                                       sizeof(struct mm_add_if_req));
+    if (!add_if_req_param)
+        return -ENOMEM;
+
+    /* Set parameters for the ADD_IF_REQ message */
+    memcpy(&(add_if_req_param->addr.array[0]), mac, ETH_ALEN);
+    switch (iftype) {
+    #ifdef CONFIG_ECRNX_FULLMAC
+    case NL80211_IFTYPE_P2P_CLIENT:
+        add_if_req_param->p2p = true;
+        add_if_req_param->type = MM_STA;
+        break;
+    #endif /* CONFIG_ECRNX_FULLMAC */
+    case NL80211_IFTYPE_STATION:
+        add_if_req_param->type = MM_STA;
+        break;
+
+    case NL80211_IFTYPE_ADHOC:
+        add_if_req_param->type = MM_IBSS;
+        break;
+
+    #ifdef CONFIG_ECRNX_FULLMAC
+    case NL80211_IFTYPE_P2P_GO:
+        add_if_req_param->p2p = true;
+        add_if_req_param->type = MM_AP;
+        break;
+    #endif /* CONFIG_ECRNX_FULLMAC */
+    case NL80211_IFTYPE_AP:
+        add_if_req_param->type = MM_AP;
+        break;
+    case NL80211_IFTYPE_MESH_POINT:
+        add_if_req_param->type = MM_MESH_POINT;
+        break;
+    case NL80211_IFTYPE_AP_VLAN:
+        return -1;
+    case NL80211_IFTYPE_MONITOR:
+        add_if_req_param->type = MM_MONITOR;
+        break;
+    default:
+        add_if_req_param->type = MM_STA;
+        break;
+    }
+
+    #ifdef CONFIG_ECRNX_SOFTMAC
+    add_if_req_param->p2p = p2p;
+    #endif /* CONFIG_ECRNX_SOFTMAC */
+
+    /* Send the ADD_IF_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, add_if_req_param, 1, MM_ADD_IF_CFM, cfm);
+}
+
+int ecrnx_send_remove_if(struct ecrnx_hw *ecrnx_hw, u8 vif_index)
+{
+    struct mm_remove_if_req *remove_if_req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_REMOVE_IF_REQ message */
+    remove_if_req = ecrnx_msg_zalloc(MM_REMOVE_IF_REQ, TASK_MM, DRV_TASK_ID,
+                                    sizeof(struct mm_remove_if_req));
+    if (!remove_if_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_REMOVE_IF_REQ message */
+    remove_if_req->inst_nbr = vif_index;
+
+    /* Send the MM_REMOVE_IF_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, remove_if_req, 1, MM_REMOVE_IF_CFM, NULL);
+}
+
+int ecrnx_send_set_channel(struct ecrnx_hw *ecrnx_hw, int phy_idx,
+                          struct mm_set_channel_cfm *cfm)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct cfg80211_chan_def *chandef = &ecrnx_hw->hw->conf.chandef;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+    struct mm_set_channel_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if (phy_idx >= ecrnx_hw->phy.cnt)
+        return -ENOTSUPP;
+
+    req = ecrnx_msg_zalloc(MM_SET_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_set_channel_req));
+    if (!req)
+        return -ENOMEM;
+
+    if (phy_idx == 0) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+        cfg80211_to_ecrnx_chan(chandef, &req->chan);
+#else
+        /* On FULLMAC only setting channel of secondary chain */
+        wiphy_err(ecrnx_hw->wiphy, "Trying to set channel of primary chain");
+        return 0;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+    } else {
+        req->chan = ecrnx_hw->phy.sec_chan;
+    }
+
+    req->index = phy_idx;
+
+    if (ecrnx_hw->phy.limit_bw)
+        limit_chan_bw(&req->chan.type, req->chan.prim20_freq, &req->chan.center1_freq);
+
+    /*ECRNX_DBG("mac80211:   freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
+             "   hw(%d): prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
+             center_freq, center_freq1, center_freq2, width, band,
+             phy_idx, req->chan.prim20_freq, req->chan.center1_freq,
+             req->chan.center2_freq, req->chan.type, req->chan.band);*/
+
+    /* Send the MM_SET_CHANNEL_REQ REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_SET_CHANNEL_CFM, cfm);
+}
+
+
+int ecrnx_send_key_add(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 sta_idx, bool pairwise,
+                      u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
+                      struct mm_key_add_cfm *cfm)
+{
+    struct mm_key_add_req *key_add_req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_KEY_ADD_REQ message */
+    key_add_req = ecrnx_msg_zalloc(MM_KEY_ADD_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_key_add_req));
+    if (!key_add_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_KEY_ADD_REQ message */
+    if (sta_idx != 0xFF) {
+        /* Pairwise key */
+        key_add_req->sta_idx = sta_idx;
+    } else {
+        /* Default key */
+        key_add_req->sta_idx = sta_idx;
+        key_add_req->key_idx = (u8_l)key_idx; /* only useful for default keys */
+    }
+    key_add_req->pairwise = pairwise;
+    key_add_req->inst_nbr = vif_idx;
+    key_add_req->key.length = key_len;
+    memcpy(&(key_add_req->key.array[0]), key, key_len);
+
+    key_add_req->cipher_suite = cipher_suite;
+
+    ECRNX_DBG("%s: sta_idx:%d key_idx:%d inst_nbr:%d cipher:%d key_len:%d\n", __func__,
+             key_add_req->sta_idx, key_add_req->key_idx, key_add_req->inst_nbr,
+             key_add_req->cipher_suite, key_add_req->key.length);
+#if defined(CONFIG_ECRNX_DBG) || defined(CONFIG_DYNAMIC_DEBUG)
+    print_hex_dump_bytes("key: ", DUMP_PREFIX_OFFSET, key_add_req->key.array, key_add_req->key.length);
+#endif
+
+    /* Send the MM_KEY_ADD_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, key_add_req, 1, MM_KEY_ADD_CFM, cfm);
+}
+
+int ecrnx_send_key_del(struct ecrnx_hw *ecrnx_hw, uint8_t hw_key_idx)
+{
+    struct mm_key_del_req *key_del_req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_KEY_DEL_REQ message */
+    key_del_req = ecrnx_msg_zalloc(MM_KEY_DEL_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_key_del_req));
+    if (!key_del_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_KEY_DEL_REQ message */
+    key_del_req->hw_key_idx = hw_key_idx;
+
+    /* Send the MM_KEY_DEL_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, key_del_req, 1, MM_KEY_DEL_CFM, NULL);
+}
+
+int ecrnx_send_bcn_change(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, dma_addr_t bcn_addr,
+                         u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft)
+{
+    struct mm_bcn_change_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_BCN_CHANGE_REQ message */
+    req = ecrnx_msg_zalloc(MM_BCN_CHANGE_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_bcn_change_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_BCN_CHANGE_REQ message */
+    req->bcn_ptr = bcn_addr;
+    req->bcn_len = bcn_len;
+    req->tim_oft = tim_oft;
+    req->tim_len = tim_len;
+    req->inst_nbr = vif_idx;
+
+#if defined(CONFIG_ECRNX_SOFTMAC)
+    BUILD_BUG_ON_MSG(IEEE80211_MAX_CSA_COUNTERS_NUM != BCN_MAX_CSA_CPT,
+                     "BCN_MAX_CSA_CPT and IEEE80211_MAX_CSA_COUNTERS_NUM "
+                     "have different value");
+#endif /* CONFIG_ECRNX_SOFTMAC */
+    if (csa_oft) {
+        int i;
+        for (i = 0; i < BCN_MAX_CSA_CPT; i++) {
+            req->csa_oft[i] = csa_oft[i];
+        }
+    }
+
+    /* Send the MM_BCN_CHANGE_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_BCN_CHANGE_CFM, NULL);
+}
+
+int ecrnx_send_roc(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                  struct ieee80211_channel *chan, unsigned  int duration)
+{
+    struct mm_remain_on_channel_req *req;
+    struct cfg80211_chan_def chandef;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Create channel definition structure */
+    cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+
+    /* Build the MM_REMAIN_ON_CHANNEL_REQ message */
+    req = ecrnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_remain_on_channel_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
+    req->op_code      = MM_ROC_OP_START;
+    req->vif_index    = vif->vif_index;
+    req->duration_ms  = duration;
+    cfg80211_to_ecrnx_chan(&chandef, &req->chan);
+
+    /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, NULL);
+}
+
+int ecrnx_send_cancel_roc(struct ecrnx_hw *ecrnx_hw)
+{
+    struct mm_remain_on_channel_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_REMAIN_ON_CHANNEL_REQ message */
+    req = ecrnx_msg_zalloc(MM_REMAIN_ON_CHANNEL_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_remain_on_channel_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_REMAIN_ON_CHANNEL_REQ message */
+    req->op_code = MM_ROC_OP_CANCEL;
+
+    /* Send the MM_REMAIN_ON_CHANNEL_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_REMAIN_ON_CHANNEL_CFM, NULL);
+}
+
+int ecrnx_send_set_power(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, s8 pwr,
+                        struct mm_set_power_cfm *cfm)
+{
+    struct mm_set_power_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_POWER_REQ message */
+    req = ecrnx_msg_zalloc(MM_SET_POWER_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_set_power_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_POWER_REQ message */
+    req->inst_nbr = vif_idx;
+    req->power = pwr;
+
+    /* Send the MM_SET_POWER_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_SET_POWER_CFM, cfm);
+}
+
+int ecrnx_send_set_edca(struct ecrnx_hw *ecrnx_hw, u8 hw_queue, u32 param,
+                       bool uapsd, u8 inst_nbr)
+{
+    struct mm_set_edca_req *set_edca_req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_EDCA_REQ message */
+    set_edca_req = ecrnx_msg_zalloc(MM_SET_EDCA_REQ, TASK_MM, DRV_TASK_ID,
+                                   sizeof(struct mm_set_edca_req));
+    if (!set_edca_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_EDCA_REQ message */
+    set_edca_req->ac_param = param;
+    set_edca_req->uapsd = uapsd;
+    set_edca_req->hw_queue = hw_queue;
+    set_edca_req->inst_nbr = inst_nbr;
+
+    /* Send the MM_SET_EDCA_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, set_edca_req, 1, MM_SET_EDCA_CFM, NULL);
+}
+
+#ifdef CONFIG_ECRNX_P2P_DEBUGFS
+int ecrnx_send_p2p_oppps_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                            u8 ctw, struct mm_set_p2p_oppps_cfm *cfm)
+{
+    struct mm_set_p2p_oppps_req *p2p_oppps_req;
+    int error;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_P2P_OPPPS_REQ message */
+    p2p_oppps_req = ecrnx_msg_zalloc(MM_SET_P2P_OPPPS_REQ, TASK_MM, DRV_TASK_ID,
+                                    sizeof(struct mm_set_p2p_oppps_req));
+
+    if (!p2p_oppps_req) {
+        return -ENOMEM;
+    }
+
+    /* Fill the message parameters */
+    p2p_oppps_req->vif_index = ecrnx_vif->vif_index;
+    p2p_oppps_req->ctwindow = ctw;
+
+    /* Send the MM_P2P_OPPPS_REQ message to LMAC FW */
+    error = ecrnx_send_msg(ecrnx_hw, p2p_oppps_req, 1, MM_SET_P2P_OPPPS_CFM, cfm);
+
+    return (error);
+}
+
+int ecrnx_send_p2p_noa_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                          int count, int interval, int duration, bool dyn_noa,
+                          struct mm_set_p2p_noa_cfm *cfm)
+{
+    struct mm_set_p2p_noa_req *p2p_noa_req;
+    int error;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Param check */
+    if (count > 255)
+        count = 255;
+
+    if (duration >= interval) {
+        dev_err(ecrnx_hw->dev, "Invalid p2p NOA config: interval=%d <= duration=%d\n",
+                interval, duration);
+        return -EINVAL;
+    }
+
+    /* Build the MM_SET_P2P_NOA_REQ message */
+    p2p_noa_req = ecrnx_msg_zalloc(MM_SET_P2P_NOA_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_set_p2p_noa_req));
+
+    if (!p2p_noa_req) {
+        return -ENOMEM;
+    }
+
+    /* Fill the message parameters */
+    p2p_noa_req->vif_index = ecrnx_vif->vif_index;
+    p2p_noa_req->noa_inst_nb = 0;
+    p2p_noa_req->count = count;
+
+    if (count) {
+        p2p_noa_req->duration_us = duration * 1024;
+        p2p_noa_req->interval_us = interval * 1024;
+        p2p_noa_req->start_offset = (interval - duration - 10) * 1024;
+        p2p_noa_req->dyn_noa = dyn_noa;
+    }
+
+    /* Send the MM_SET_2P_NOA_REQ message to LMAC FW */
+    error = ecrnx_send_msg(ecrnx_hw, p2p_noa_req, 1, MM_SET_P2P_NOA_CFM, cfm);
+
+    return (error);
+}
+#endif /* CONFIG_ECRNX_P2P_DEBUGFS */
+
+/******************************************************************************
+ *    Control messages handling functions (SOFTMAC only)
+ *****************************************************************************/
+#ifdef CONFIG_ECRNX_SOFTMAC
+int ecrnx_send_sta_add(struct ecrnx_hw *ecrnx_hw, struct ieee80211_sta *sta,
+                      u8 inst_nbr, struct mm_sta_add_cfm *cfm)
+{
+    struct mm_sta_add_req *sta_add_req;
+    union ecrnx_thd_phy_ctrl_info *phy;
+    struct ecrnx_sta *ecrnx_sta;
+    int error;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_STA_ADD_REQ message */
+    sta_add_req = ecrnx_msg_zalloc(MM_STA_ADD_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_sta_add_req));
+    if (!sta_add_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_STA_ADD_REQ message */
+    memcpy(&(sta_add_req->mac_addr.array[0]), &(sta->addr[0]), ETH_ALEN);
+
+    if (sta->wme)
+        sta_add_req->capa_flags |= STA_QOS_CAPA;
+    /* TODO: check if a density of 0 microseconds is OK or not for LMAC */
+    if (sta->ht_cap.ht_supported) {
+        int ht_exp = sta->ht_cap.ampdu_factor;
+        int vht_exp = 0;
+
+        sta_add_req->capa_flags |= STA_HT_CAPA;
+        sta_add_req->ampdu_size_max_ht =
+            (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + ht_exp)) - 1;
+        sta_add_req->ampdu_spacing_min =
+            ecrnx_ampdudensity2usec(sta->ht_cap.ampdu_density);
+
+        if (sta->vht_cap.vht_supported) {
+            sta_add_req->capa_flags |= STA_VHT_CAPA;
+            vht_exp = (sta->vht_cap.cap &
+                IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
+                IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+            sta_add_req->ampdu_size_max_vht =
+                (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + vht_exp)) - 1;
+        }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) && defined(CONFIG_ECRNX_HE)
+        if (sta->he_cap.has_he) {
+            int he_exp_ext = (sta->he_cap.he_cap_elem.mac_cap_info[3] &
+                              IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK) >> 3;
+
+            sta_add_req->capa_flags |= STA_HE_CAPA;
+            if (sta->vht_cap.vht_supported) {
+                if ((vht_exp == 7) && he_exp_ext)
+                    sta_add_req->ampdu_size_max_he =
+                        (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + vht_exp + he_exp_ext)) - 1;
+                else
+                    sta_add_req->ampdu_size_max_he = sta_add_req->ampdu_size_max_vht;
+            } else {
+                if ((ht_exp == 3) && he_exp_ext)
+                    sta_add_req->ampdu_size_max_he =
+                        (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + ht_exp + he_exp_ext)) - 1;
+                else
+                    sta_add_req->ampdu_size_max_he = sta_add_req->ampdu_size_max_ht;
+            }
+        }
+#endif
+    }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
+    if (sta->mfp)
+        sta_add_req->capa_flags |= STA_MFP_CAPA;
+#endif
+
+    ecrnx_sta = (struct ecrnx_sta *)sta->drv_priv;
+    /* TODO Set the interface index from the vif structure */
+    sta_add_req->inst_nbr = inst_nbr;
+    sta_add_req->tdls_sta = sta->tdls;
+    sta_add_req->tdls_sta_initiator = STA_TDLS_INITIATOR(sta);
+    sta_add_req->bssid_index = 0;
+    sta_add_req->max_bssid_ind = 0;
+    phy = (union ecrnx_thd_phy_ctrl_info *)&sta_add_req->paid_gid;
+    phy->partialAIDTx = ecrnx_sta->paid;
+    phy->groupIDTx = ecrnx_sta->gid;
+
+    /* Send the MM_STA_ADD_REQ message to LMAC FW */
+    error = ecrnx_send_msg(ecrnx_hw, sta_add_req, 1, MM_STA_ADD_CFM, cfm);
+
+    return (error);
+}
+
+int ecrnx_send_sta_del(struct ecrnx_hw *ecrnx_hw, u8 sta_idx)
+{
+    struct mm_sta_del_req *sta_del_req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_STA_DEL_REQ message */
+    sta_del_req = ecrnx_msg_zalloc(MM_STA_DEL_REQ, TASK_MM, DRV_TASK_ID,
+                                  sizeof(struct mm_sta_del_req));
+    if (!sta_del_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_STA_DEL_REQ message */
+    sta_del_req->sta_idx = sta_idx;
+
+    /* Send the MM_STA_DEL_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, sta_del_req, 1, MM_STA_DEL_CFM, NULL);
+}
+
+int ecrnx_send_set_filter(struct ecrnx_hw *ecrnx_hw, uint32_t filter)
+{
+    struct mm_set_filter_req *set_filter_req_param;
+    uint32_t rx_filter = 0;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_FILTER_REQ message */
+    set_filter_req_param =
+        ecrnx_msg_zalloc(MM_SET_FILTER_REQ, TASK_MM, DRV_TASK_ID,
+                        sizeof(struct mm_set_filter_req));
+    if (!set_filter_req_param)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_FILTER_REQ message */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)
+    if (filter & FIF_PROMISC_IN_BSS)
+        rx_filter |= NXMAC_ACCEPT_UNICAST_BIT;
+#endif
+    if (filter & FIF_ALLMULTI)
+        rx_filter |= NXMAC_ACCEPT_MULTICAST_BIT;
+
+    if (filter & (FIF_FCSFAIL | FIF_PLCPFAIL))
+        rx_filter |= NXMAC_ACCEPT_ERROR_FRAMES_BIT;
+
+    if (filter & FIF_BCN_PRBRESP_PROMISC)
+        rx_filter |= NXMAC_ACCEPT_OTHER_BSSID_BIT;
+
+    if (filter & FIF_CONTROL)
+        rx_filter |= NXMAC_ACCEPT_OTHER_CNTRL_FRAMES_BIT |
+                     NXMAC_ACCEPT_CF_END_BIT |
+                     NXMAC_ACCEPT_ACK_BIT |
+                     NXMAC_ACCEPT_CTS_BIT |
+                     NXMAC_ACCEPT_RTS_BIT |
+                     NXMAC_ACCEPT_BA_BIT | NXMAC_ACCEPT_BAR_BIT;
+
+    if (filter & FIF_OTHER_BSS)
+        rx_filter |= NXMAC_ACCEPT_OTHER_BSSID_BIT;
+
+    if (filter & FIF_PSPOLL) {
+        /* TODO: check if the MAC filters apply to our BSSID or is general */
+        rx_filter |= NXMAC_ACCEPT_PS_POLL_BIT;
+    }
+
+    if (filter & FIF_PROBE_REQ) {
+        rx_filter |= NXMAC_ACCEPT_PROBE_REQ_BIT;
+        rx_filter |= NXMAC_ACCEPT_ALL_BEACON_BIT;
+    }
+
+    /* Add the filter flags that are set by default and cannot be changed here */
+    rx_filter |= ECRNX_MAC80211_NOT_CHANGEABLE;
+
+    /* XXX */
+    if (ieee80211_hw_check(ecrnx_hw->hw, AMPDU_AGGREGATION))
+        rx_filter |= NXMAC_ACCEPT_BA_BIT;
+
+    /* Now copy all the flags into the message parameter */
+    set_filter_req_param->filter = rx_filter;
+
+    ECRNX_DBG("new total_flags = 0x%08x\nrx filter set to  0x%08x\n",
+             filter, rx_filter);
+
+    /* Send the MM_SET_FILTER_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, set_filter_req_param, 1, MM_SET_FILTER_CFM, NULL);
+}
+
+
+int ecrnx_send_add_chanctx(struct ecrnx_hw *ecrnx_hw,
+                          struct ieee80211_chanctx_conf *ctx,
+                          struct mm_chan_ctxt_add_cfm *cfm)
+{
+    struct mm_chan_ctxt_add_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_CHAN_CTXT_ADD_REQ message */
+    req = ecrnx_msg_zalloc(MM_CHAN_CTXT_ADD_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_chan_ctxt_add_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the CHAN_CTXT_ADD_REQ message */
+    cfg80211_to_ecrnx_chan(&ctx->def, &req->chan);
+
+    if (ecrnx_hw->phy.limit_bw)
+        limit_chan_bw(&req->chan.type, req->chan.prim20_freq,
+                      &req->chan.center1_freq);
+
+    ECRNX_DBG("mac80211:   freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
+             "          prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
+             ctx->def.chan->center_freq, ctx->def.center_freq1,
+             ctx->def.center_freq2, ctx->def.width, ctx->def.chan->band,
+             req->chan.prim20_freq, req->chan.center1_freq,
+             req->chan.center2_freq, req->chan.type, req->chan.band);
+    /* Send the CHAN_CTXT_ADD_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_ADD_CFM, cfm);
+}
+
+int ecrnx_send_del_chanctx(struct ecrnx_hw *ecrnx_hw, u8 index)
+{
+    struct mm_chan_ctxt_del_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_CHAN_CTXT_DEL_REQ message */
+    req = ecrnx_msg_zalloc(MM_CHAN_CTXT_DEL_REQ, TASK_MM, DRV_TASK_ID,
+                                    sizeof(struct mm_chan_ctxt_del_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_REMOVE_IF_REQ message */
+    req->index = index;
+
+    /* Send the MM_CHAN_CTXT_DEL_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_DEL_CFM, NULL);
+}
+
+int ecrnx_send_link_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 chan_idx,
+                           u8 chan_switch)
+{
+    struct mm_chan_ctxt_link_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_CHAN_CTXT_LINK_REQ message */
+    req = ecrnx_msg_zalloc(MM_CHAN_CTXT_LINK_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_chan_ctxt_link_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_CHAN_CTXT_LINK_REQ message */
+    req->vif_index = vif_idx;
+    req->chan_index = chan_idx;
+    req->chan_switch = chan_switch;
+
+    /* Send the MM_CHAN_CTXT_LINK_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_LINK_CFM, NULL);
+}
+
+int ecrnx_send_unlink_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx)
+{
+    struct mm_chan_ctxt_unlink_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_CHAN_CTXT_UNLINK_REQ message */
+    req = ecrnx_msg_zalloc(MM_CHAN_CTXT_UNLINK_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_chan_ctxt_unlink_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_CHAN_CTXT_UNLINK_REQ message */
+    req->vif_index = vif_idx;
+
+    /* Send the MM_CHAN_CTXT_UNLINK_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_UNLINK_CFM, NULL);
+}
+
+int ecrnx_send_update_chanctx(struct ecrnx_hw *ecrnx_hw,
+                             struct ieee80211_chanctx_conf *ctx)
+{
+    struct mm_chan_ctxt_update_req *req;
+    struct ecrnx_chanctx *ecrnx_chanctx;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_CHAN_CTXT_UPDATE_REQ message */
+    req = ecrnx_msg_zalloc(MM_CHAN_CTXT_UPDATE_REQ, TASK_MM, DRV_TASK_ID,
+                                       sizeof(struct mm_chan_ctxt_update_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_CHAN_CTXT_UPDATE_REQ message */
+    ecrnx_chanctx = (struct ecrnx_chanctx *)ctx->drv_priv;
+    req->chan_index = ecrnx_chanctx->index;
+    cfg80211_to_ecrnx_chan(&ctx->def, &req->chan);
+
+    if (ecrnx_hw->phy.limit_bw)
+        limit_chan_bw(&req->chan.type, req->chan.prim20_freq, &req->chan.center1_freq);
+
+    ECRNX_DBG("mac80211:   freq=%d(c1:%d - c2:%d)/width=%d - band=%d\n"
+             "          prim20=%d(c1:%d - c2:%d)/ type=%d - band=%d\n",
+             ctx->def.chan->center_freq, ctx->def.center_freq1,
+             ctx->def.center_freq2, ctx->def.width, ctx->def.chan->band,
+             req->chan.prim20_freq, req->chan.center1_freq,
+             req->chan.center2_freq, req->chan.type, req->chan.band);
+    /* Send the MM_CHAN_CTXT_UPDATE_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_UPDATE_CFM, NULL);
+}
+
+int ecrnx_send_sched_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 chan_idx, u8 type)
+{
+    struct mm_chan_ctxt_sched_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_CHAN_CTXT_SCHED_REQ message */
+    req = ecrnx_msg_zalloc(MM_CHAN_CTXT_SCHED_REQ, TASK_MM, DRV_TASK_ID,
+                                    sizeof(struct mm_chan_ctxt_sched_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_CHAN_CTXT_SCHED_REQ message */
+    req->vif_index = vif_idx;
+    req->chan_index = chan_idx;
+    req->type = type;
+
+    /* Send the MM_CHAN_CTXT_SCHED_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_CHAN_CTXT_SCHED_CFM, NULL);
+}
+
+int ecrnx_send_dtim_req(struct ecrnx_hw *ecrnx_hw, u8 dtim_period)
+{
+    struct mm_set_dtim_req *set_dtim_req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_DTIM_REQ message */
+    set_dtim_req = ecrnx_msg_zalloc(MM_SET_DTIM_REQ, TASK_MM, DRV_TASK_ID,
+                                   sizeof(struct mm_set_dtim_req));
+    if (!set_dtim_req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_DTIM_REQ message */
+    set_dtim_req->dtim_period = dtim_period;
+
+    /* Send the MM_SET_DTIM_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, set_dtim_req, 1, MM_SET_DTIM_CFM, NULL);
+}
+
+int ecrnx_send_set_br(struct ecrnx_hw *ecrnx_hw, u32 basic_rates, u8 vif_idx, u8 band)
+{
+    struct mm_set_basic_rates_req *set_basic_rates_req_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_BASIC_RATES_REQ message */
+    set_basic_rates_req_param =
+        ecrnx_msg_zalloc(MM_SET_BASIC_RATES_REQ, TASK_MM, DRV_TASK_ID,
+                        sizeof(struct mm_set_basic_rates_req));
+    if (!set_basic_rates_req_param)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_BASIC_RATES_REQ message */
+    set_basic_rates_req_param->rates = basic_rates;
+    set_basic_rates_req_param->inst_nbr = vif_idx;
+    set_basic_rates_req_param->band = band;
+
+    /* Send the MM_SET_BASIC_RATES_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, set_basic_rates_req_param, 1, MM_SET_BASIC_RATES_CFM, NULL);
+}
+
+int ecrnx_send_set_beacon_int(struct ecrnx_hw *ecrnx_hw, u16 beacon_int, u8 vif_idx)
+{
+    struct mm_set_beacon_int_req *set_beacon_int_req_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_BEACON_INT_REQ message */
+    set_beacon_int_req_param =
+        ecrnx_msg_zalloc(MM_SET_BEACON_INT_REQ, TASK_MM, DRV_TASK_ID,
+                        sizeof(struct mm_set_beacon_int_req));
+    if (!set_beacon_int_req_param)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_BEACON_INT_REQ message */
+    set_beacon_int_req_param->beacon_int = beacon_int;
+    set_beacon_int_req_param->inst_nbr = vif_idx;
+
+    /* Send the MM_SET_BEACON_INT_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, set_beacon_int_req_param, 1,
+                         MM_SET_BEACON_INT_CFM, NULL);
+}
+
+int ecrnx_send_set_bssid(struct ecrnx_hw *ecrnx_hw, const u8 *bssid, u8 vif_idx)
+{
+    struct mm_set_bssid_req *set_bssid_req_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_BSSID_REQ message */
+    set_bssid_req_param = ecrnx_msg_zalloc(MM_SET_BSSID_REQ, TASK_MM, DRV_TASK_ID,
+                                          sizeof(struct mm_set_bssid_req));
+    if (!set_bssid_req_param)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_BSSID_REQ message */
+    memcpy(&(set_bssid_req_param->bssid.array[0]), bssid, ETH_ALEN);
+    set_bssid_req_param->inst_nbr = vif_idx;
+
+    /* Send the MM_SET_BSSID_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, set_bssid_req_param, 1, MM_SET_BSSID_CFM, NULL);
+}
+
+int ecrnx_send_set_vif_state(struct ecrnx_hw *ecrnx_hw, bool active,
+                            u16 aid, u8 vif_idx)
+{
+    struct mm_set_vif_state_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_VIF_STATE_REQ message */
+    req = ecrnx_msg_zalloc(MM_SET_VIF_STATE_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_set_vif_state_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_VIF_STATE_REQ message */
+    req->active = active;
+    req->aid = aid;
+    req->inst_nbr = vif_idx;
+
+    /* Send the MM_SET_VIF_STATE_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_SET_VIF_STATE_CFM, NULL);
+}
+
+int ecrnx_send_set_mode(struct ecrnx_hw *ecrnx_hw, u8 abgnmode)
+{
+    struct mm_set_mode_req *set_mode_req_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_MODE_REQ message */
+    set_mode_req_param = ecrnx_msg_zalloc(MM_SET_MODE_REQ, TASK_MM, DRV_TASK_ID,
+                                         sizeof(struct mm_set_mode_req));
+    if (!set_mode_req_param)
+        return -ENOMEM;
+
+    set_mode_req_param->abgnmode = abgnmode;
+
+    /* Send the MM_SET_MODE_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, set_mode_req_param, 1, MM_SET_MODE_CFM, NULL);
+}
+
+int ecrnx_send_set_idle(struct ecrnx_hw *ecrnx_hw, int idle)
+{
+    struct mm_set_idle_req *set_idle_req_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    set_idle_req_param = ecrnx_msg_zalloc(MM_SET_IDLE_REQ, TASK_MM, DRV_TASK_ID,
+                                         sizeof(struct mm_set_idle_req));
+    if (!set_idle_req_param)
+        return -ENOMEM;
+
+    set_idle_req_param->hw_idle = idle;
+
+    return ecrnx_send_msg(ecrnx_hw, set_idle_req_param, 1, MM_SET_IDLE_CFM, NULL);
+}
+
+int ecrnx_send_set_ps_mode(struct ecrnx_hw *ecrnx_hw, u8 ps_mode)
+{
+    struct mm_set_ps_mode_req *set_ps_mode_req_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    set_ps_mode_req_param =
+        ecrnx_msg_zalloc(MM_SET_PS_MODE_REQ, TASK_MM, DRV_TASK_ID,
+                        sizeof(struct mm_set_ps_mode_req));
+    if (!set_ps_mode_req_param)
+        return -ENOMEM;
+
+    set_ps_mode_req_param->new_state = ps_mode;
+
+    return ecrnx_send_msg(ecrnx_hw, set_ps_mode_req_param, 1, MM_SET_PS_MODE_CFM, NULL);
+}
+
+int ecrnx_send_set_ps_options(struct ecrnx_hw *ecrnx_hw, bool listen_bcmc,
+                             u16 listen_interval, u8 vif_idx)
+{
+    struct mm_set_ps_options_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_PS_OPTIONS_REQ message */
+    req = ecrnx_msg_zalloc(MM_SET_PS_OPTIONS_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_set_ps_options_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_VIF_STATE_REQ message */
+    req->listen_interval = listen_interval;
+    req->dont_listen_bc_mc = !listen_bcmc;
+    req->vif_index = vif_idx;
+
+    /* Send the MM_SET_PS_OPTIONS_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_SET_PS_OPTIONS_CFM, NULL);
+}
+
+int ecrnx_send_set_slottime(struct ecrnx_hw *ecrnx_hw, int use_short_slot)
+{
+    struct mm_set_slottime_req *set_slottime_req_param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_SLOTTIME_REQ message */
+    set_slottime_req_param =
+        ecrnx_msg_zalloc(MM_SET_SLOTTIME_REQ, TASK_MM, DRV_TASK_ID,
+                        sizeof(struct mm_set_slottime_req));
+    if (!set_slottime_req_param)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_SLOTTIME_REQ message */
+    set_slottime_req_param->slottime = use_short_slot ? 9 : 20;
+
+    /* Send the MM_SET_SLOTTIME_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, set_slottime_req_param, 1, MM_SET_SLOTTIME_CFM, NULL);
+}
+
+int ecrnx_send_ba_add(struct ecrnx_hw *ecrnx_hw, uint8_t type, uint8_t sta_idx,
+                     u16 tid, uint8_t bufsz, uint16_t ssn,
+                     struct mm_ba_add_cfm *cfm)
+{
+    struct mm_ba_add_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_BA_ADD_REQ message */
+    req = ecrnx_msg_zalloc(MM_BA_ADD_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_ba_add_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_BA_ADD_REQ message */
+    req->type = type;
+    req->sta_idx = sta_idx;
+    req->tid = (u8_l)tid;
+    req->bufsz = bufsz;
+    req->ssn = ssn;
+
+    /* Send the MM_BA_ADD_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_BA_ADD_CFM, cfm);
+}
+
+int ecrnx_send_ba_del(struct ecrnx_hw *ecrnx_hw, uint8_t sta_idx, u16 tid,
+                     struct mm_ba_del_cfm *cfm)
+{
+    struct mm_ba_del_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_BA_DEL_REQ message */
+    req = ecrnx_msg_zalloc(MM_BA_DEL_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_ba_del_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters */
+    req->type = 0;
+    req->sta_idx = sta_idx;
+    req->tid = (u8_l)tid;
+
+    /* Send the MM_BA_DEL_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_BA_DEL_CFM, cfm);
+}
+
+int ecrnx_send_scan_req(struct ecrnx_hw *ecrnx_hw, struct ieee80211_vif *vif,
+                       struct cfg80211_scan_request *param,
+                       struct scan_start_cfm *cfm)
+{
+    struct scan_start_req *req;
+    int i;
+    struct ecrnx_vif *ecrnx_vif;
+    uint8_t chan_flags = 0;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    ecrnx_vif = (struct ecrnx_vif *)vif->drv_priv;
+
+    /* Build the SCAN_START_REQ message */
+    req = ecrnx_msg_zalloc(SCAN_START_REQ, TASK_SCAN, DRV_TASK_ID,
+                          sizeof(struct scan_start_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters */
+    req->vif_idx = ecrnx_vif->vif_index;
+    req->chan_cnt = (u8)min_t(int, SCAN_CHANNEL_MAX, param->n_channels);
+    req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
+    req->bssid = mac_addr_bcst;
+    req->no_cck = param->no_cck;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+    if (param->duration_mandatory)
+        req->duration = ieee80211_tu_to_usec(param->duration);
+#endif
+
+    if (req->ssid_cnt == 0)
+        chan_flags |= CHAN_NO_IR;
+    for (i = 0; i < req->ssid_cnt; i++) {
+        int j;
+        for (j = 0; j < param->ssids[i].ssid_len; j++)
+            req->ssid[i].array[j] = param->ssids[i].ssid[j];
+        req->ssid[i].length = param->ssids[i].ssid_len;
+    }
+
+    if (param->ie) {
+        if (ecrnx_ipc_elem_var_allocs(ecrnx_hw, &ecrnx_hw->scan_ie,
+                                     param->ie_len, DMA_TO_DEVICE,
+                                     NULL, param->ie, NULL))
+            goto error;
+
+        req->add_ie_len = param->ie_len;
+        req->add_ies = ecrnx_hw->scan_ie.dma_addr;
+    } else {
+        req->add_ie_len = 0;
+        req->add_ies = 0;
+    }
+
+    for (i = 0; i < req->chan_cnt; i++) {
+        struct ieee80211_channel *chan = param->channels[i];
+
+        req->chan[i].band = chan->band;
+        req->chan[i].freq = chan->center_freq;
+        req->chan[i].flags = chan_flags | get_chan_flags(chan->flags);
+        req->chan[i].tx_power = chan_to_fw_pwr(chan->max_reg_power);
+    }
+
+    /* Send the SCAN_START_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, SCAN_START_CFM, cfm);
+error:
+    if (req != NULL)
+        ecrnx_msg_free(ecrnx_hw, req);
+    return -ENOMEM;
+}
+
+int ecrnx_send_scan_cancel_req(struct ecrnx_hw *ecrnx_hw,
+                              struct scan_cancel_cfm *cfm)
+{
+    struct scan_cancel_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the SCAN_CANCEL_REQ message */
+    req = ecrnx_msg_zalloc(SCAN_CANCEL_REQ, TASK_SCAN, DRV_TASK_ID,
+                          sizeof(struct scan_cancel_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Send the SCAN_CANCEL_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, SCAN_CANCEL_CFM, cfm);
+}
+
+void ecrnx_send_tdls_ps(struct ecrnx_hw *ecrnx_hw, bool ps_mode)
+{
+    if (!ecrnx_hw->mod_params->ps_on)
+        return;
+
+    ecrnx_send_set_ps_mode(ecrnx_hw, ps_mode);
+}
+
+int ecrnx_send_tim_update(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u16 aid,
+                         u8 tx_status)
+{
+    struct mm_tim_update_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_TIM_UPDATE_REQ message */
+    req = ecrnx_msg_zalloc(MM_TIM_UPDATE_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_tim_update_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_TIM_UPDATE_REQ message */
+    req->aid = aid;
+    req->tx_avail = tx_status;
+    req->inst_nbr = vif_idx;
+
+    /* Send the MM_TIM_UPDATE_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_TIM_UPDATE_CFM, NULL);
+}
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+/******************************************************************************
+ *    Control messages handling functions (FULLMAC only)
+ *****************************************************************************/
+#ifdef CONFIG_ECRNX_FULLMAC
+
+int ecrnx_send_me_config_req(struct ecrnx_hw *ecrnx_hw)
+{
+    struct me_config_req *req;
+    struct wiphy *wiphy = ecrnx_hw->wiphy;
+#ifdef CONFIG_ECRNX_5G
+    struct ieee80211_sta_ht_cap *ht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->ht_cap;
+    struct ieee80211_sta_vht_cap *vht_cap = &wiphy->bands[NL80211_BAND_5GHZ]->vht_cap;
+#else
+       struct ieee80211_sta_ht_cap *ht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap;
+       struct ieee80211_sta_vht_cap *vht_cap = &wiphy->bands[NL80211_BAND_2GHZ]->vht_cap;
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)  && defined(CONFIG_ECRNX_HE)
+    struct ieee80211_sta_he_cap const *he_cap;
+#endif
+    uint8_t *ht_mcs = (uint8_t *)&ht_cap->mcs;
+    int i;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the ME_CONFIG_REQ message */
+    req = ecrnx_msg_zalloc(ME_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
+                                   sizeof(struct me_config_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_CONFIG_REQ message */
+    req->ht_supp = ht_cap->ht_supported;
+    req->vht_supp = vht_cap->vht_supported;
+    req->ht_cap.ht_capa_info = cpu_to_le16(ht_cap->cap);
+    req->ht_cap.a_mpdu_param = ht_cap->ampdu_factor |
+                                     (ht_cap->ampdu_density <<
+                                         IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+    for (i = 0; i < sizeof(ht_cap->mcs); i++)
+        req->ht_cap.mcs_rate[i] = ht_mcs[i];
+    req->ht_cap.ht_extended_capa = 0;
+    req->ht_cap.tx_beamforming_capa = 0;
+    req->ht_cap.asel_capa = 0;
+
+    req->vht_cap.vht_capa_info = cpu_to_le32(vht_cap->cap);
+    req->vht_cap.rx_highest = cpu_to_le16(vht_cap->vht_mcs.rx_highest);
+    req->vht_cap.rx_mcs_map = cpu_to_le16(vht_cap->vht_mcs.rx_mcs_map);
+    req->vht_cap.tx_highest = cpu_to_le16(vht_cap->vht_mcs.tx_highest);
+    req->vht_cap.tx_mcs_map = cpu_to_le16(vht_cap->vht_mcs.tx_mcs_map);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && defined(CONFIG_ECRNX_HE)
+#ifdef CONFIG_ECRNX_5G
+    if (wiphy->bands[NL80211_BAND_5GHZ]->iftype_data != NULL) {
+        he_cap = &wiphy->bands[NL80211_BAND_5GHZ]->iftype_data->he_cap;
+#else
+       if (wiphy->bands[NL80211_BAND_2GHZ]->iftype_data != NULL) {
+               he_cap = &wiphy->bands[NL80211_BAND_2GHZ]->iftype_data->he_cap;
+#endif
+
+        req->he_supp = he_cap->has_he;
+        for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.mac_cap_info); i++) {
+            req->he_cap.mac_cap_info[i] = he_cap->he_cap_elem.mac_cap_info[i];
+        }
+        for (i = 0; i < ARRAY_SIZE(he_cap->he_cap_elem.phy_cap_info); i++) {
+            req->he_cap.phy_cap_info[i] = he_cap->he_cap_elem.phy_cap_info[i];
+        }
+        req->he_cap.mcs_supp.rx_mcs_80 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_80);
+        req->he_cap.mcs_supp.tx_mcs_80 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_80);
+        req->he_cap.mcs_supp.rx_mcs_160 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_160);
+        req->he_cap.mcs_supp.tx_mcs_160 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_160);
+        req->he_cap.mcs_supp.rx_mcs_80p80 = cpu_to_le16(he_cap->he_mcs_nss_supp.rx_mcs_80p80);
+        req->he_cap.mcs_supp.tx_mcs_80p80 = cpu_to_le16(he_cap->he_mcs_nss_supp.tx_mcs_80p80);
+        for (i = 0; i < MAC_HE_PPE_THRES_MAX_LEN; i++) {
+            req->he_cap.ppe_thres[i] = he_cap->ppe_thres[i];
+        }
+        req->he_ul_on = ecrnx_hw->mod_params->he_ul_on;
+    }
+#else
+    req->he_ul_on = false;
+
+    req->he_supp = ecrnx_he_cap.has_he;
+    for (i = 0; i < ARRAY_SIZE(ecrnx_he_cap.he_cap_elem.mac_cap_info); i++) {
+        req->he_cap.mac_cap_info[i] = ecrnx_he_cap.he_cap_elem.mac_cap_info[i];
+    }
+    for (i = 0; i < ARRAY_SIZE(ecrnx_he_cap.he_cap_elem.phy_cap_info); i++) {
+        req->he_cap.phy_cap_info[i] = ecrnx_he_cap.he_cap_elem.phy_cap_info[i];
+    }
+    req->he_cap.mcs_supp.rx_mcs_80 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_80);
+    req->he_cap.mcs_supp.tx_mcs_80 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_80);
+    req->he_cap.mcs_supp.rx_mcs_160 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_160);
+    req->he_cap.mcs_supp.tx_mcs_160 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_160);
+    req->he_cap.mcs_supp.rx_mcs_80p80 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_80p80);
+    req->he_cap.mcs_supp.tx_mcs_80p80 = cpu_to_le16(ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_80p80);
+    for (i = 0; i < MAC_HE_PPE_THRES_MAX_LEN; i++) {
+        req->he_cap.ppe_thres[i] = ecrnx_he_cap.ppe_thres[i];
+    }
+#endif
+
+    req->ps_on = ecrnx_hw->mod_params->ps_on;
+    req->dpsm = ecrnx_hw->mod_params->dpsm;
+    /**
+     * set sleep_flag for sdio slave.
+     * bit0: MODEM_SLEEP
+     * bit1: WFI_SLEEP
+     * bit2: LIGHT_SLEEP
+     * bit3: DEEP_SLEEP
+     */
+    req->sleep_flag = 0x5;
+    req->tx_lft = ecrnx_hw->mod_params->tx_lft;
+    req->ant_div_on = ecrnx_hw->mod_params->ant_div;
+    if (ecrnx_hw->mod_params->use_80)
+        req->phy_bw_max = PHY_CHNL_BW_80;
+    else if (ecrnx_hw->mod_params->use_2040)
+        req->phy_bw_max = PHY_CHNL_BW_40;
+    else
+        req->phy_bw_max = PHY_CHNL_BW_20;
+
+    wiphy_info(wiphy, "HT supp %d, VHT supp %d, HE supp %d\n", req->ht_supp,
+                                                               req->vht_supp,
+                                                               req->he_supp);
+
+    /* Send the ME_CONFIG_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, ME_CONFIG_CFM, NULL);
+}
+
+int ecrnx_send_me_chan_config_req(struct ecrnx_hw *ecrnx_hw)
+{
+    struct me_chan_config_req *req;
+    struct wiphy *wiphy = ecrnx_hw->wiphy;
+    int i;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the ME_CHAN_CONFIG_REQ message */
+    req = ecrnx_msg_zalloc(ME_CHAN_CONFIG_REQ, TASK_ME, DRV_TASK_ID,
+                                            sizeof(struct me_chan_config_req));
+    if (!req)
+        return -ENOMEM;
+
+    req->chan2G4_cnt=  0;
+    if (wiphy->bands[NL80211_BAND_2GHZ] != NULL) {
+        struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_2GHZ];
+        for (i = 0; i < b->n_channels; i++) {
+            req->chan2G4[req->chan2G4_cnt].flags = 0;
+            if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
+                req->chan2G4[req->chan2G4_cnt].flags |= CHAN_DISABLED;
+            req->chan2G4[req->chan2G4_cnt].flags |= get_chan_flags(b->channels[i].flags);
+            req->chan2G4[req->chan2G4_cnt].band = NL80211_BAND_2GHZ;
+            req->chan2G4[req->chan2G4_cnt].freq = b->channels[i].center_freq;
+            req->chan2G4[req->chan2G4_cnt].tx_power = chan_to_fw_pwr(b->channels[i].max_power);
+            req->chan2G4_cnt++;
+            if (req->chan2G4_cnt == MAC_DOMAINCHANNEL_24G_MAX)
+                break;
+        }
+    }
+
+    req->chan5G_cnt = 0;
+#ifdef CONFIG_ECRNX_5G
+    if (wiphy->bands[NL80211_BAND_5GHZ] != NULL) {
+        struct ieee80211_supported_band *b = wiphy->bands[NL80211_BAND_5GHZ];
+        for (i = 0; i < b->n_channels; i++) {
+            req->chan5G[req->chan5G_cnt].flags = 0;
+            if (b->channels[i].flags & IEEE80211_CHAN_DISABLED)
+                req->chan5G[req->chan5G_cnt].flags |= CHAN_DISABLED;
+            req->chan5G[req->chan5G_cnt].flags |= get_chan_flags(b->channels[i].flags);
+            req->chan5G[req->chan5G_cnt].band = NL80211_BAND_5GHZ;
+            req->chan5G[req->chan5G_cnt].freq = b->channels[i].center_freq;
+            req->chan5G[req->chan5G_cnt].tx_power = chan_to_fw_pwr(b->channels[i].max_power);
+            req->chan5G_cnt++;
+            if (req->chan5G_cnt == MAC_DOMAINCHANNEL_5G_MAX)
+                break;
+        }
+    }
+#endif
+    /* Send the ME_CHAN_CONFIG_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, ME_CHAN_CONFIG_CFM, NULL);
+}
+
+int ecrnx_send_me_set_control_port_req(struct ecrnx_hw *ecrnx_hw, bool opened, u8 sta_idx)
+{
+    struct me_set_control_port_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the ME_SET_CONTROL_PORT_REQ message */
+    req = ecrnx_msg_zalloc(ME_SET_CONTROL_PORT_REQ, TASK_ME, DRV_TASK_ID,
+                                   sizeof(struct me_set_control_port_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_SET_CONTROL_PORT_REQ message */
+    req->sta_idx = sta_idx;
+    req->control_port_open = opened;
+
+    /* Send the ME_SET_CONTROL_PORT_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, ME_SET_CONTROL_PORT_CFM, NULL);
+}
+
+int ecrnx_send_me_sta_add(struct ecrnx_hw *ecrnx_hw, struct station_parameters *params,
+                         const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm)
+{
+    struct me_sta_add_req *req;
+    u8 *ht_mcs = (u8 *)&params->ht_capa->mcs;
+    int i;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_STA_ADD_REQ message */
+    req = ecrnx_msg_zalloc(ME_STA_ADD_REQ, TASK_ME, DRV_TASK_ID,
+                                  sizeof(struct me_sta_add_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_STA_ADD_REQ message */
+    memcpy(&(req->mac_addr.array[0]), mac, ETH_ALEN);
+
+    req->rate_set.length = params->supported_rates_len;
+    for (i = 0; i < params->supported_rates_len; i++)
+        req->rate_set.array[i] = params->supported_rates[i];
+
+    req->flags = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    if (params->capability & WLAN_CAPABILITY_SHORT_PREAMBLE){
+        req->flags |= STA_SHORT_PREAMBLE_CAPA;
+    }
+#endif
+
+    if (params->ht_capa) {
+        const struct ieee80211_ht_cap *ht_capa = params->ht_capa;
+
+        req->flags |= STA_HT_CAPA;
+        req->ht_cap.ht_capa_info = cpu_to_le16(ht_capa->cap_info);
+        req->ht_cap.a_mpdu_param = ht_capa->ampdu_params_info;
+        for (i = 0; i < sizeof(ht_capa->mcs); i++)
+            req->ht_cap.mcs_rate[i] = ht_mcs[i];
+        req->ht_cap.ht_extended_capa = cpu_to_le16(ht_capa->extended_ht_cap_info);
+        req->ht_cap.tx_beamforming_capa = cpu_to_le32(ht_capa->tx_BF_cap_info);
+        req->ht_cap.asel_capa = ht_capa->antenna_selection_info;
+    }
+
+    if (params->vht_capa) {
+        const struct ieee80211_vht_cap *vht_capa = params->vht_capa;
+
+        req->flags |= STA_VHT_CAPA;
+        req->vht_cap.vht_capa_info = cpu_to_le32(vht_capa->vht_cap_info);
+        req->vht_cap.rx_highest = cpu_to_le16(vht_capa->supp_mcs.rx_highest);
+        req->vht_cap.rx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.rx_mcs_map);
+        req->vht_cap.tx_highest = cpu_to_le16(vht_capa->supp_mcs.tx_highest);
+        req->vht_cap.tx_mcs_map = cpu_to_le16(vht_capa->supp_mcs.tx_mcs_map);
+    }
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) && defined(CONFIG_ECRNX_HE)
+    if (params->he_capa) {
+        const struct ieee80211_he_cap_elem *he_capa = params->he_capa;
+        struct ieee80211_he_mcs_nss_supp *mcs_nss_supp =
+                                (struct ieee80211_he_mcs_nss_supp *)(he_capa + 1);
+
+        req->flags |= STA_HE_CAPA;
+        for (i = 0; i < ARRAY_SIZE(he_capa->mac_cap_info); i++) {
+            req->he_cap.mac_cap_info[i] = he_capa->mac_cap_info[i];
+        }
+        for (i = 0; i < ARRAY_SIZE(he_capa->phy_cap_info); i++) {
+            req->he_cap.phy_cap_info[i] = he_capa->phy_cap_info[i];
+        }
+        req->he_cap.mcs_supp.rx_mcs_80 = mcs_nss_supp->rx_mcs_80;
+        req->he_cap.mcs_supp.tx_mcs_80 = mcs_nss_supp->tx_mcs_80;
+        req->he_cap.mcs_supp.rx_mcs_160 = mcs_nss_supp->rx_mcs_160;
+        req->he_cap.mcs_supp.tx_mcs_160 = mcs_nss_supp->tx_mcs_160;
+        req->he_cap.mcs_supp.rx_mcs_80p80 = mcs_nss_supp->rx_mcs_80p80;
+        req->he_cap.mcs_supp.tx_mcs_80p80 = mcs_nss_supp->tx_mcs_80p80;
+    }
+
+#endif
+
+    if (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME))
+        req->flags |= STA_QOS_CAPA;
+
+    if (params->sta_flags_set & BIT(NL80211_STA_FLAG_MFP)) //  if (sme->mfp == NL80211_MFP_REQUIRED || sme->mfp ==NL80211_MFP_OPTIONAL) //wfa must used  NL80211_MFP_REQUIRED and NL80211_MFP_OPTIONAL
+        req->flags |= STA_MFP_CAPA;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+    if (params->opmode_notif_used) {
+        req->flags |= STA_OPMOD_NOTIF;
+        req->opmode = params->opmode_notif;
+    }
+#endif
+
+    req->aid = cpu_to_le16(params->aid);
+    req->uapsd_queues = params->uapsd_queues;
+    req->max_sp_len = params->max_sp * 2;
+    req->vif_idx = inst_nbr;
+
+    if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
+        struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[inst_nbr];
+        req->tdls_sta = true;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+        if ((params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) &&
+            !ecrnx_vif->tdls_chsw_prohibited)
+            req->tdls_chsw_allowed = true;
+#endif
+        if (ecrnx_vif->tdls_status == TDLS_SETUP_RSP_TX)
+            req->tdls_sta_initiator = true;
+    }
+
+    /* Send the ME_STA_ADD_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, ME_STA_ADD_CFM, cfm);
+}
+
+int ecrnx_send_me_sta_del(struct ecrnx_hw *ecrnx_hw, u8 sta_idx, bool tdls_sta)
+{
+    struct me_sta_del_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_STA_DEL_REQ message */
+    req = ecrnx_msg_zalloc(ME_STA_DEL_REQ, TASK_ME, DRV_TASK_ID,
+                          sizeof(struct me_sta_del_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_STA_DEL_REQ message */
+    req->sta_idx = sta_idx;
+    req->tdls_sta = tdls_sta;
+
+    /* Send the ME_STA_DEL_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, ME_STA_DEL_CFM, NULL);
+}
+
+int ecrnx_send_me_traffic_ind(struct ecrnx_hw *ecrnx_hw, u8 sta_idx, bool uapsd, u8 tx_status)
+{
+    struct me_traffic_ind_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the ME_UTRAFFIC_IND_REQ message */
+    req = ecrnx_msg_zalloc(ME_TRAFFIC_IND_REQ, TASK_ME, DRV_TASK_ID,
+                          sizeof(struct me_traffic_ind_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_TRAFFIC_IND_REQ message */
+    req->sta_idx = sta_idx;
+    req->tx_avail = tx_status;
+    req->uapsd = uapsd;
+
+    /* Send the ME_TRAFFIC_IND_REQ to UMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, ME_TRAFFIC_IND_CFM, NULL);
+}
+
+int ecrnx_send_twt_request(struct ecrnx_hw *ecrnx_hw,
+                          u8 setup_type, u8 vif_idx,
+                          struct twt_conf_tag *conf,
+                          struct twt_setup_cfm *cfm)
+{
+    struct twt_setup_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the TWT_SETUP_REQ message */
+    req = ecrnx_msg_zalloc(TWT_SETUP_REQ, TASK_TWT, DRV_TASK_ID,
+                          sizeof(struct twt_setup_req));
+    if (!req)
+        return -ENOMEM;
+
+    memcpy(&req->conf, conf, sizeof(req->conf));
+    req->setup_type = setup_type;
+    req->vif_idx = vif_idx;
+
+    /* Send the TWT_SETUP_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, TWT_SETUP_CFM, cfm);
+}
+
+int ecrnx_send_twt_teardown(struct ecrnx_hw *ecrnx_hw,
+                           struct twt_teardown_req *twt_teardown,
+                           struct twt_teardown_cfm *cfm)
+{
+    struct twt_teardown_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the TWT_TEARDOWN_REQ message */
+    req = ecrnx_msg_zalloc(TWT_TEARDOWN_REQ, TASK_TWT, DRV_TASK_ID,
+                          sizeof(struct twt_teardown_req));
+    if (!req)
+        return -ENOMEM;
+
+    memcpy(req, twt_teardown, sizeof(struct twt_teardown_req));
+
+    /* Send the TWT_TEARDOWN_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, TWT_TEARDOWN_CFM, cfm);
+}
+int ecrnx_send_me_rc_stats(struct ecrnx_hw *ecrnx_hw,
+                          u8 sta_idx,
+                          struct me_rc_stats_cfm *cfm)
+{
+    struct me_rc_stats_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the ME_RC_STATS_REQ message */
+    req = ecrnx_msg_zalloc(ME_RC_STATS_REQ, TASK_ME, DRV_TASK_ID,
+                                  sizeof(struct me_rc_stats_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_RC_STATS_REQ message */
+    req->sta_idx = sta_idx;
+
+    /* Send the ME_RC_STATS_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, ME_RC_STATS_CFM, cfm);
+}
+
+int ecrnx_send_me_rc_set_rate(struct ecrnx_hw *ecrnx_hw,
+                             u8 sta_idx,
+                             u16 rate_cfg)
+{
+    struct me_rc_set_rate_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the ME_RC_SET_RATE_REQ message */
+    req = ecrnx_msg_zalloc(ME_RC_SET_RATE_REQ, TASK_ME, DRV_TASK_ID,
+                          sizeof(struct me_rc_set_rate_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_RC_SET_RATE_REQ message */
+    req->sta_idx = sta_idx;
+    req->fixed_rate_cfg = rate_cfg;
+
+    /* Send the ME_RC_SET_RATE_REQ message to FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+}
+
+int ecrnx_send_me_set_ps_mode(struct ecrnx_hw *ecrnx_hw, u8 ps_mode)
+{
+    struct me_set_ps_mode_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the ME_SET_PS_MODE_REQ message */
+    req = ecrnx_msg_zalloc(ME_SET_PS_MODE_REQ, TASK_ME, DRV_TASK_ID,
+                          sizeof(struct me_set_ps_mode_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the ME_SET_PS_MODE_REQ message */
+    req->ps_state = ps_mode;
+
+    /* Send the ME_SET_PS_MODE_REQ message to FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, ME_SET_PS_MODE_CFM, NULL);
+}
+
+int ecrnx_send_sm_connect_req(struct ecrnx_hw *ecrnx_hw,
+                             struct ecrnx_vif *ecrnx_vif,
+                             struct cfg80211_connect_params *sme,
+                             struct sm_connect_cfm *cfm)
+{
+    struct sm_connect_req *req;
+    int i, ie_len;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    ie_len = update_connect_req(ecrnx_vif, sme);
+    /* Build the SM_CONNECT_REQ message */
+    req = ecrnx_msg_zalloc(SM_CONNECT_REQ, TASK_SM, DRV_TASK_ID,
+                     (sizeof(struct sm_connect_req) + ie_len));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the SM_CONNECT_REQ message */
+    if (sme->crypto.n_ciphers_pairwise &&
+        ((sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP40) ||
+         (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_TKIP) ||
+         (sme->crypto.ciphers_pairwise[0] == WLAN_CIPHER_SUITE_WEP104)))
+        req->flags |= DISABLE_HT;
+
+    if (sme->crypto.control_port)
+        req->flags |= CONTROL_PORT_HOST;
+
+    if (sme->crypto.control_port_no_encrypt)
+        req->flags |= CONTROL_PORT_NO_ENC;
+
+    if (use_pairwise_key(&sme->crypto))
+        req->flags |= WPA_WPA2_IN_USE;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    if (sme->mfp == NL80211_MFP_REQUIRED)
+        req->flags |= MFP_IN_USE;
+#endif
+
+    req->ctrl_port_ethertype = sme->crypto.control_port_ethertype;
+
+    if (sme->bssid)
+        memcpy(&req->bssid, sme->bssid, ETH_ALEN);
+    else
+        req->bssid = mac_addr_bcst;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
+    if (sme->prev_bssid)
+        req->flags |= REASSOCIATION;
+#else
+    if (ecrnx_vif->sta.ap)
+        req->flags |= REASSOCIATION;
+#endif
+    if ((sme->auth_type == NL80211_AUTHTYPE_FT) && (ecrnx_vif->sta.flags & ECRNX_STA_FT_OVER_DS))
+        req->flags |= (REASSOCIATION | FT_OVER_DS);
+    req->vif_idx = ecrnx_vif->vif_index;
+    if (sme->channel) {
+        req->chan.band = sme->channel->band;
+        req->chan.freq = sme->channel->center_freq;
+        req->chan.flags = get_chan_flags(sme->channel->flags);
+    } else {
+        req->chan.freq = (u16_l)-1;
+    }
+    for (i = 0; i < sme->ssid_len; i++)
+        req->ssid.array[i] = sme->ssid[i];
+    req->ssid.length = sme->ssid_len;
+
+    req->listen_interval = ecrnx_mod_params.listen_itv;
+    req->dont_wait_bcmc = !ecrnx_mod_params.listen_bcmc;
+
+    /* Set auth_type */
+    if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC)
+        req->auth_type = WLAN_AUTH_OPEN;
+    else if (sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
+        req->auth_type = WLAN_AUTH_OPEN;
+    else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY)
+        req->auth_type = WLAN_AUTH_SHARED_KEY;
+    else if (sme->auth_type == NL80211_AUTHTYPE_FT)
+        req->auth_type = WLAN_AUTH_FT;
+    else if (sme->auth_type == NL80211_AUTHTYPE_SAE)
+        req->auth_type = WLAN_AUTH_SAE;
+    else
+        goto invalid_param;
+    copy_connect_ies(ecrnx_vif, req, sme);
+
+    /* Set UAPSD queues */
+    req->uapsd_queues = ecrnx_mod_params.uapsd_queues;
+
+    /* Send the SM_CONNECT_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, SM_CONNECT_CFM, cfm);
+
+invalid_param:
+    ecrnx_msg_free(ecrnx_hw, req);
+    return -EINVAL;
+}
+
+int ecrnx_send_sm_disconnect_req(struct ecrnx_hw *ecrnx_hw,
+                                struct ecrnx_vif *ecrnx_vif,
+                                u16 reason)
+{
+    struct sm_disconnect_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the SM_DISCONNECT_REQ message */
+    req = ecrnx_msg_zalloc(SM_DISCONNECT_REQ, TASK_SM, DRV_TASK_ID,
+                                   sizeof(struct sm_disconnect_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the SM_DISCONNECT_REQ message */
+    req->reason_code = reason;
+    req->vif_idx = ecrnx_vif->vif_index;
+
+    /* Send the SM_DISCONNECT_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, SM_DISCONNECT_CFM, NULL);
+}
+
+int ecrnx_send_sm_external_auth_required_rsp(struct ecrnx_hw *ecrnx_hw,
+                                            struct ecrnx_vif *ecrnx_vif,
+                                            u16 status)
+{
+    struct sm_external_auth_required_rsp *rsp;
+
+    /* Build the SM_EXTERNAL_AUTH_CFM message */
+    rsp = ecrnx_msg_zalloc(SM_EXTERNAL_AUTH_REQUIRED_RSP, TASK_SM, DRV_TASK_ID,
+                          sizeof(struct sm_external_auth_required_rsp));
+    if (!rsp)
+        return -ENOMEM;
+
+    rsp->status = status;
+    rsp->vif_idx = ecrnx_vif->vif_index;
+
+    /* send the SM_EXTERNAL_AUTH_REQUIRED_RSP message UMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, rsp, 0, 0, NULL);
+}
+int ecrnx_send_sm_ft_auth_rsp(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                             uint8_t *ie, int ie_len)
+{
+    struct sm_connect_req *rsp;
+    rsp = ecrnx_msg_zalloc(SM_FT_AUTH_RSP, TASK_SM, DRV_TASK_ID,
+                         (sizeof(struct sm_connect_req) + ie_len));
+    if (!rsp)
+        return -ENOMEM;
+    rsp->vif_idx = ecrnx_vif->vif_index;
+    rsp->ie_len = ie_len;
+    memcpy(rsp->ie_buf, ie, rsp->ie_len);
+    return ecrnx_send_msg(ecrnx_hw, rsp, 0, 0, NULL);
+}
+
+int ecrnx_send_apm_start_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                            struct cfg80211_ap_settings *settings,
+                            struct apm_start_cfm *cfm,
+                            struct ecrnx_ipc_elem_var *elem)
+{
+    struct apm_start_req *req;
+    struct ecrnx_bcn *bcn = &vif->ap.bcn;
+    u8 *buf;
+    u32 flags = 0;
+    const u8 *rate_ie;
+    u8 rate_len = 0;
+    int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+    const u8 *var_pos;
+    int len, i, error;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the APM_START_REQ message */
+    req = ecrnx_msg_zalloc(APM_START_REQ, TASK_APM, DRV_TASK_ID,
+                                   sizeof(struct apm_start_req));
+    if (!req)
+        return -ENOMEM;
+
+    // Build the beacon
+    bcn->dtim = (u8)settings->dtim_period;
+    buf = ecrnx_build_bcn(bcn, &settings->beacon);
+    if (!buf) {
+        ecrnx_msg_free(ecrnx_hw, req);
+        return -ENOMEM;
+    }
+
+    // Retrieve the basic rate set from the beacon buffer
+    len = bcn->len - var_offset;
+    var_pos = buf + var_offset;
+
+// Assume that rate higher that 54 Mbps are BSS membership
+#define IS_BASIC_RATE(r) (r & 0x80) && ((r & ~0x80) <= (54 * 2))
+
+    rate_ie = cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
+    if (rate_ie) {
+        const u8 *rates = rate_ie + 2;
+        for (i = 0; (i < rate_ie[1]) && (rate_len < MAC_RATESET_LEN); i++) {
+            if (IS_BASIC_RATE(rates[i]))
+                req->basic_rates.array[rate_len++] = rates[i];
+        }
+    }
+    rate_ie = cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, var_pos, len);
+    if (rate_ie) {
+        const u8 *rates = rate_ie + 2;
+        for (i = 0; (i < rate_ie[1]) && (rate_len < MAC_RATESET_LEN); i++) {
+            if (IS_BASIC_RATE(rates[i]))
+                req->basic_rates.array[rate_len++] = rates[i];
+        }
+    }
+    req->basic_rates.length = rate_len;
+#undef IS_BASIC_RATE
+
+    // Sync buffer for FW
+    if ((error = ecrnx_ipc_elem_var_allocs(ecrnx_hw, elem, bcn->len,
+                                          DMA_TO_DEVICE, buf, NULL, NULL))) {
+        return error;
+    }
+
+    /* Set parameters for the APM_START_REQ message */
+    req->vif_idx = vif->vif_index;
+    req->bcn_addr = elem->dma_addr;
+    req->bcn_len = bcn->len;
+    req->tim_oft = bcn->head_len;
+    req->tim_len = bcn->tim_len;
+    cfg80211_to_ecrnx_chan(&settings->chandef, &req->chan);
+    req->bcn_int = settings->beacon_interval;
+    if (settings->crypto.control_port)
+        flags |= CONTROL_PORT_HOST;
+
+    if (settings->crypto.control_port_no_encrypt)
+        flags |= CONTROL_PORT_NO_ENC;
+
+    if (use_pairwise_key(&settings->crypto))
+        flags |= WPA_WPA2_IN_USE;
+
+    if (settings->crypto.control_port_ethertype)
+        req->ctrl_port_ethertype = settings->crypto.control_port_ethertype;
+    else
+        req->ctrl_port_ethertype = ETH_P_PAE;
+    req->flags = flags;
+
+    /* Send the APM_START_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, APM_START_CFM, cfm);
+}
+
+int ecrnx_send_apm_stop_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif)
+{
+    struct apm_stop_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the APM_STOP_REQ message */
+    req = ecrnx_msg_zalloc(APM_STOP_REQ, TASK_APM, DRV_TASK_ID,
+                                   sizeof(struct apm_stop_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the APM_STOP_REQ message */
+    req->vif_idx = vif->vif_index;
+
+    /* Send the APM_STOP_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, APM_STOP_CFM, NULL);
+}
+
+int ecrnx_send_apm_probe_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                            struct ecrnx_sta *sta, struct apm_probe_client_cfm *cfm)
+{
+    struct apm_probe_client_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    req = ecrnx_msg_zalloc(APM_PROBE_CLIENT_REQ, TASK_APM, DRV_TASK_ID,
+                          sizeof(struct apm_probe_client_req));
+    if (!req)
+        return -ENOMEM;
+
+    req->vif_idx = vif->vif_index;
+    req->sta_idx = sta->sta_idx;
+
+    /* Send the APM_PROBE_CLIENT_REQ message to UMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, APM_PROBE_CLIENT_CFM, cfm);
+}
+int ecrnx_send_scanu_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                        struct cfg80211_scan_request *param)
+{
+    struct scanu_start_req *req;
+    int i, chan_num = 0;
+    uint8_t chan_flags = 0;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the SCANU_START_REQ message */
+    req = ecrnx_msg_zalloc(SCANU_START_REQ, TASK_SCANU, DRV_TASK_ID,
+                          sizeof(struct scanu_start_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters */
+    req->vif_idx = ecrnx_vif->vif_index;
+    req->chan_cnt = (u8)min_t(int, SCAN_CHANNEL_MAX, param->n_channels);
+    req->ssid_cnt = (u8)min_t(int, SCAN_SSID_MAX, param->n_ssids);
+    req->bssid = mac_addr_bcst;
+    req->no_cck = param->no_cck;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+    if (param->duration_mandatory)
+        req->duration = ieee80211_tu_to_usec(param->duration);
+#endif
+
+    if(param->n_ssids > SCAN_SSID_MAX){
+        ECRNX_PRINT("%s:n_ssids: %d \n", __func__, param->n_ssids);
+        for (i = 0; i < param->n_ssids; i++){
+            print_hex_dump_bytes("[ecrnx]scan_req: ", DUMP_PREFIX_NONE, &param->ssids[i], param->ssids[i].ssid_len);
+            ECRNX_PRINT("i:%d, ssid_len:%d \n", i, param->ssids[i].ssid_len);
+        }
+    }
+
+    if (req->ssid_cnt == 0)
+        chan_flags |= CHAN_NO_IR;
+    for (i = 0; i < req->ssid_cnt; i++) {
+        int j;
+        for (j = 0; j < param->ssids[i].ssid_len; j++)
+            req->ssid[i].array[j] = param->ssids[i].ssid[j];
+        req->ssid[i].length = param->ssids[i].ssid_len;
+    }
+
+    if(req->ssid_cnt == 1 && param->ssids[0].ssid_len > 0)
+    {
+        if (strcmp(req->ssid[0].array, "DIRECT-"))
+        {
+            req->ssid_cnt = 2;
+            req->ssid[1].length = 0;
+        }
+    }
+
+    if (param->ie) {
+
+        if (ecrnx_ipc_elem_var_allocs(ecrnx_hw, &ecrnx_hw->scan_ie,
+                                     param->ie_len, DMA_TO_DEVICE,
+                                     NULL, param->ie, NULL))
+            goto error;
+        req->add_ie_len = param->ie_len;
+        req->add_ies = ecrnx_hw->scan_ie.dma_addr;
+    } else {
+        req->add_ie_len = 0;
+        req->add_ies = 0;
+    }
+
+    for (i = 0; i < req->chan_cnt; i++) {
+        struct ieee80211_channel *chan = param->channels[i];
+
+               if(chan->band){
+                       continue;
+               }
+        req->chan[chan_num].band = chan->band;
+        req->chan[chan_num].freq = chan->center_freq;
+        req->chan[chan_num].flags = chan_flags | get_chan_flags(chan->flags);
+        req->chan[chan_num].tx_power = chan_to_fw_pwr(chan->max_reg_power);
+               chan_num++;
+               //printk("--%d set ch, %d,%d,%d,%d\n", i, req->chan[i].band, req->chan[i].freq, req->chan[i].flags, req->chan[i].tx_power);
+    }
+       req->chan_cnt = chan_num;
+    /* Send the SCANU_START_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+error:
+    if (req != NULL)
+        ecrnx_msg_free(ecrnx_hw, req);
+    return -ENOMEM;
+}
+
+int ecrnx_send_scanu_cancel_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif)
+{
+    struct scanu_cancel_req *req = NULL;
+
+    /* Build the SCANU_START_REQ message */
+    req = ecrnx_msg_zalloc(SCANU_CANCEL_REQ, TASK_SCANU, DRV_TASK_ID,
+                          sizeof(struct scanu_cancel_req));
+    if (!req){
+        return -ENOMEM;
+    }
+
+    req->vif_idx = ecrnx_vif->vif_index;
+    ECRNX_PRINT("%s: vif_idx:%d; \n", __func__, req->vif_idx);
+    return ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+}
+
+int ecrnx_send_apm_start_cac_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                                struct cfg80211_chan_def *chandef,
+                                struct apm_start_cac_cfm *cfm)
+{
+    struct apm_start_cac_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the APM_START_CAC_REQ message */
+    req = ecrnx_msg_zalloc(APM_START_CAC_REQ, TASK_APM, DRV_TASK_ID,
+                          sizeof(struct apm_start_cac_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the APM_START_CAC_REQ message */
+    req->vif_idx = vif->vif_index;
+    cfg80211_to_ecrnx_chan(chandef, &req->chan);
+
+    /* Send the APM_START_CAC_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, APM_START_CAC_CFM, cfm);
+}
+
+int ecrnx_send_apm_stop_cac_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif)
+{
+    struct apm_stop_cac_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the APM_STOP_CAC_REQ message */
+    req = ecrnx_msg_zalloc(APM_STOP_CAC_REQ, TASK_APM, DRV_TASK_ID,
+                          sizeof(struct apm_stop_cac_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the APM_STOP_CAC_REQ message */
+    req->vif_idx = vif->vif_index;
+
+    /* Send the APM_STOP_CAC_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, APM_STOP_CAC_CFM, NULL);
+}
+
+int ecrnx_send_mesh_start_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                             const struct mesh_config *conf, const struct mesh_setup *setup,
+                             struct mesh_start_cfm *cfm)
+{
+    // Message to send
+    struct mesh_start_req *req;
+    // Supported basic rates
+    struct ieee80211_supported_band *band = ecrnx_hw->wiphy->bands[setup->chandef.chan->band];
+    /* Counter */
+    int i;
+    /* Return status */
+    int status;
+    /* DMA Address to be unmapped after confirmation reception */
+    u32 dma_addr = 0;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MESH_START_REQ message */
+    req = ecrnx_msg_zalloc(MESH_START_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_start_req));
+    if (!req) {
+        return -ENOMEM;
+    }
+
+    req->vif_index = vif->vif_index;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    req->bcn_int = setup->beacon_interval;
+    req->dtim_period = setup->dtim_period;
+#endif
+    req->mesh_id_len = setup->mesh_id_len;
+
+    for (i = 0; i < setup->mesh_id_len; i++) {
+        req->mesh_id[i] = *(setup->mesh_id + i);
+    }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    req->user_mpm = setup->user_mpm;
+#endif
+    req->is_auth = setup->is_authenticated;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+    req->auth_id = setup->auth_id;
+#endif
+    req->ie_len = setup->ie_len;
+
+    if (setup->ie_len) {
+        /*
+         * Need to provide a Virtual Address to the MAC so that it can download the
+         * additional information elements.
+         */
+        req->ie_addr = dma_map_single(ecrnx_hw->dev, (void *)setup->ie,
+                                      setup->ie_len, DMA_FROM_DEVICE);
+
+        /* Check DMA mapping result */
+        if (dma_mapping_error(ecrnx_hw->dev, req->ie_addr)) {
+            ECRNX_ERR(KERN_CRIT "%s - DMA Mapping error on additional IEs\n", __func__);
+
+            /* Consider there is no Additional IEs */
+            req->ie_len = 0;
+        } else {
+            /* Store DMA Address so that we can unmap the memory section once MESH_START_CFM is received */
+            dma_addr = req->ie_addr;
+        }
+    }
+
+    /* Provide rate information */
+    req->basic_rates.length = 0;
+    for (i = 0; i < band->n_bitrates; i++) {
+        u16 rate = band->bitrates[i].bitrate;
+
+        /* Read value is in in units of 100 Kbps, provided value is in units
+         * of 1Mbps, and multiplied by 2 so that 5.5 becomes 11 */
+        rate = (rate << 1) / 10;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
+        if (setup->basic_rates & CO_BIT(i)) {
+            rate |= 0x80;
+        }
+#endif
+
+        req->basic_rates.array[i] = (u8)rate;
+        req->basic_rates.length++;
+    }
+
+    /* Provide channel information */
+    cfg80211_to_ecrnx_chan(&setup->chandef, &req->chan);
+
+    /* Send the MESH_START_REQ message to UMAC FW */
+    status = ecrnx_send_msg(ecrnx_hw, req, 1, MESH_START_CFM, cfm);
+
+    /* Unmap DMA area */
+    if (setup->ie_len) {
+        dma_unmap_single(ecrnx_hw->dev, dma_addr, setup->ie_len, DMA_TO_DEVICE);
+    }
+
+    /* Return the status */
+    return (status);
+}
+
+int ecrnx_send_mesh_stop_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                            struct mesh_stop_cfm *cfm)
+{
+    // Message to send
+    struct mesh_stop_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MESH_STOP_REQ message */
+    req = ecrnx_msg_zalloc(MESH_STOP_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_stop_req));
+    if (!req) {
+        return -ENOMEM;
+    }
+
+    req->vif_idx = vif->vif_index;
+
+    /* Send the MESH_STOP_REQ message to UMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MESH_STOP_CFM, cfm);
+}
+
+int ecrnx_send_mesh_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                              u32 mask, const struct mesh_config *p_mconf, struct mesh_update_cfm *cfm)
+{
+    // Message to send
+    struct mesh_update_req *req;
+    // Keep only bit for fields which can be updated
+    u32 supp_mask = (mask << 1) & (CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS)
+                                   | CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE)
+                                   | CO_BIT(NL80211_MESHCONF_FORWARDING)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+                                   | CO_BIT(NL80211_MESHCONF_POWER_MODE)
+#endif
+                                   );
+
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if (!supp_mask) {
+        return -ENOENT;
+    }
+
+    /* Build the MESH_UPDATE_REQ message */
+    req = ecrnx_msg_zalloc(MESH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_update_req));
+
+    if (!req) {
+        return -ENOMEM;
+    }
+
+    req->vif_idx = vif->vif_index;
+
+    if (supp_mask & CO_BIT(NL80211_MESHCONF_GATE_ANNOUNCEMENTS))
+    {
+        req->flags |= CO_BIT(MESH_UPDATE_FLAGS_GATE_MODE_BIT);
+        req->gate_announ = p_mconf->dot11MeshGateAnnouncementProtocol;
+    }
+
+    if (supp_mask & CO_BIT(NL80211_MESHCONF_HWMP_ROOTMODE))
+    {
+        req->flags |= CO_BIT(MESH_UPDATE_FLAGS_ROOT_MODE_BIT);
+        req->root_mode = p_mconf->dot11MeshHWMPRootMode;
+    }
+
+    if (supp_mask & CO_BIT(NL80211_MESHCONF_FORWARDING))
+    {
+        req->flags |= CO_BIT(MESH_UPDATE_FLAGS_MESH_FWD_BIT);
+        req->mesh_forward = p_mconf->dot11MeshForwarding;
+    }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    if (supp_mask & CO_BIT(NL80211_MESHCONF_POWER_MODE))
+    {
+        req->flags |= CO_BIT(MESH_UPDATE_FLAGS_LOCAL_PSM_BIT);
+        req->local_ps_mode = p_mconf->power_mode;
+    }
+#endif
+    /* Send the MESH_UPDATE_REQ message to UMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MESH_UPDATE_CFM, cfm);
+}
+
+int ecrnx_send_mesh_peer_info_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                                 u8 sta_idx, struct mesh_peer_info_cfm *cfm)
+{
+    // Message to send
+    struct mesh_peer_info_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MESH_PEER_INFO_REQ message */
+    req = ecrnx_msg_zalloc(MESH_PEER_INFO_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_peer_info_req));
+    if (!req) {
+        return -ENOMEM;
+    }
+
+    req->sta_idx = sta_idx;
+
+    /* Send the MESH_PEER_INFO_REQ message to UMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MESH_PEER_INFO_CFM, cfm);
+}
+
+void ecrnx_send_mesh_peer_update_ntf(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                                    u8 sta_idx, u8 mlink_state)
+{
+    // Message to send
+    struct mesh_peer_update_ntf *ntf;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MESH_PEER_UPDATE_NTF message */
+    ntf = ecrnx_msg_zalloc(MESH_PEER_UPDATE_NTF, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_peer_update_ntf));
+
+    if (ntf) {
+        ntf->vif_idx = vif->vif_index;
+        ntf->sta_idx = sta_idx;
+        ntf->state = mlink_state;
+
+        /* Send the MESH_PEER_INFO_REQ message to UMAC FW */
+        ecrnx_send_msg(ecrnx_hw, ntf, 0, 0, NULL);
+    }
+}
+
+void ecrnx_send_mesh_path_create_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, u8 *tgt_addr)
+{
+    struct mesh_path_create_req *req;
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Check if we are already waiting for a confirmation */
+    if (vif->ap.flags & ECRNX_AP_CREATE_MESH_PATH)
+        return;
+
+        /* Build the MESH_PATH_CREATE_REQ message */
+    req = ecrnx_msg_zalloc(MESH_PATH_CREATE_REQ, TASK_MESH, DRV_TASK_ID,
+                              sizeof(struct mesh_path_create_req));
+    if (!req)
+        return;
+
+    req->vif_idx = vif->vif_index;
+    memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN);
+
+    vif->ap.flags |= ECRNX_AP_CREATE_MESH_PATH;
+
+    /* Send the MESH_PATH_CREATE_REQ message to UMAC FW */
+    ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+}
+
+int ecrnx_send_mesh_path_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, const u8 *tgt_addr,
+                                   const u8 *p_nhop_addr, struct mesh_path_update_cfm *cfm)
+{
+    // Message to send
+    struct mesh_path_update_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MESH_PATH_UPDATE_REQ message */
+    req = ecrnx_msg_zalloc(MESH_PATH_UPDATE_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_path_update_req));
+    if (!req) {
+        return -ENOMEM;
+    }
+
+    req->delete = (p_nhop_addr == NULL);
+    req->vif_idx = vif->vif_index;
+    memcpy(&req->tgt_mac_addr, tgt_addr, ETH_ALEN);
+
+    if (p_nhop_addr) {
+        memcpy(&req->nhop_mac_addr, p_nhop_addr, ETH_ALEN);
+    }
+
+    /* Send the MESH_PATH_UPDATE_REQ message to UMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MESH_PATH_UPDATE_CFM, cfm);
+}
+
+void ecrnx_send_mesh_proxy_add_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, u8 *ext_addr)
+{
+    // Message to send
+    struct mesh_proxy_add_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MESH_PROXY_ADD_REQ message */
+    req = ecrnx_msg_zalloc(MESH_PROXY_ADD_REQ, TASK_MESH, DRV_TASK_ID,
+                          sizeof(struct mesh_proxy_add_req));
+
+    if (req) {
+        req->vif_idx = vif->vif_index;
+        memcpy(&req->ext_sta_addr, ext_addr, ETH_ALEN);
+
+        /* Send the MESH_PROXY_ADD_REQ message to UMAC FW */
+        ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+    }
+}
+
+int ecrnx_send_tdls_peer_traffic_ind_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif)
+{
+    struct tdls_peer_traffic_ind_req *tdls_peer_traffic_ind_req;
+
+    if (!ecrnx_vif->sta.tdls_sta)
+        return -ENOLINK;
+
+    /* Build the TDLS_PEER_TRAFFIC_IND_REQ message */
+    tdls_peer_traffic_ind_req = ecrnx_msg_zalloc(TDLS_PEER_TRAFFIC_IND_REQ, TASK_TDLS, DRV_TASK_ID,
+                                           sizeof(struct tdls_peer_traffic_ind_req));
+
+    if (!tdls_peer_traffic_ind_req)
+        return -ENOMEM;
+
+    /* Set parameters for the TDLS_PEER_TRAFFIC_IND_REQ message */
+    tdls_peer_traffic_ind_req->vif_index = ecrnx_vif->vif_index;
+    tdls_peer_traffic_ind_req->sta_idx = ecrnx_vif->sta.tdls_sta->sta_idx;
+    memcpy(&(tdls_peer_traffic_ind_req->peer_mac_addr.array[0]),
+           ecrnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN);
+    tdls_peer_traffic_ind_req->dialog_token = 0; // check dialog token value
+    tdls_peer_traffic_ind_req->last_tid = ecrnx_vif->sta.tdls_sta->tdls.last_tid;
+    tdls_peer_traffic_ind_req->last_sn = ecrnx_vif->sta.tdls_sta->tdls.last_sn;
+
+    /* Send the TDLS_PEER_TRAFFIC_IND_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, tdls_peer_traffic_ind_req, 0, 0, NULL);
+}
+
+int ecrnx_send_config_monitor_req(struct ecrnx_hw *ecrnx_hw,
+                                 struct cfg80211_chan_def *chandef,
+                                 struct me_config_monitor_cfm *cfm)
+{
+    struct me_config_monitor_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the ME_CONFIG_MONITOR_REQ message */
+    req = ecrnx_msg_zalloc(ME_CONFIG_MONITOR_REQ, TASK_ME, DRV_TASK_ID,
+                                   sizeof(struct me_config_monitor_req));
+    if (!req)
+        return -ENOMEM;
+
+    if (chandef) {
+        req->chan_set = true;
+        cfg80211_to_ecrnx_chan(chandef, &req->chan);
+
+        if (ecrnx_hw->phy.limit_bw)
+            limit_chan_bw(&req->chan.type, req->chan.prim20_freq, &req->chan.center1_freq);
+    } else {
+         req->chan_set = false;
+    }
+
+    req->uf = ecrnx_hw->mod_params->uf;
+
+    /* Send the ME_CONFIG_MONITOR_REQ message to FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, ME_CONFIG_MONITOR_CFM, cfm);
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#ifdef CONFIG_ECRNX_P2P
+int ecrnx_send_p2p_start_listen_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, unsigned int duration)
+{
+       struct p2p_listen_start_req *req;
+       struct ecrnx_p2p_listen *p2p_listen = &ecrnx_hw->p2p_listen;
+       int rc;
+       
+       ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+       if(p2p_listen->listen_started)
+       {
+               ECRNX_ERR("P2P listen already ongoing\n");
+               return -EBUSY;
+       }
+
+       p2p_listen->ecrnx_vif = ecrnx_vif;
+       p2p_listen->listen_duration = duration;
+       
+       if(ecrnx_hw->scan_request)
+       {
+               ECRNX_ERR("Delaying p2p listen until scan done\n");
+               return 0;
+       }
+       
+       /* Build the P2P_LISTEN_START_REQ message */
+    req = ecrnx_msg_zalloc(P2P_LISTEN_START_REQ, TASK_P2P_LISTEN, DRV_TASK_ID,
+                                                       sizeof(struct p2p_listen_start_req));
+    if (!req)
+        return -ENOMEM;
+
+    req->vif_idx = ecrnx_vif->vif_index;
+
+    rc = ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+    if(rc)
+       return rc;
+
+    p2p_listen->listen_started = 1;
+
+    return rc;
+}
+
+int ecrnx_send_p2p_cancel_listen_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif)
+{
+    struct p2p_cancel_listen_req *req;
+    struct ecrnx_p2p_listen *p2p_listen = &ecrnx_hw->p2p_listen;
+       
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+       /* Build the P2P_CANCEL_LISTEN_REQ message */
+    req = ecrnx_msg_zalloc(P2P_CANCEL_LISTEN_REQ, TASK_P2P_LISTEN, DRV_TASK_ID,
+                                                       sizeof(struct p2p_cancel_listen_req));
+    if (!req)
+        return -ENOMEM;
+
+    req->vif_idx = ecrnx_vif->vif_index;
+    p2p_listen->listen_started = 0;
+    //return rwnx_send_msg(rwnx_hw, req, 1, P2P_CANCEL_LISTEN_CFM, NULL);
+    ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+
+    return 0;
+
+}
+#endif
+
+int ecrnx_send_tdls_chan_switch_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                                   struct ecrnx_sta *ecrnx_sta, bool sta_initiator,
+                                   u8 oper_class, struct cfg80211_chan_def *chandef,
+                                   struct tdls_chan_switch_cfm *cfm)
+{
+    struct tdls_chan_switch_req *tdls_chan_switch_req;
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    /* Check if channel switch already enabled on a TDLS peer */
+    if (ecrnx_hw->tdls_info.chsw_en) {
+        ECRNX_ERR("TDLS channel switch already enabled for another TDLS station\n");
+        return -ENOTSUPP;
+    }
+#endif
+
+    /* Build the TDLS_CHAN_SWITCH_REQ message */
+    tdls_chan_switch_req = ecrnx_msg_zalloc(TDLS_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID,
+                                           sizeof(struct tdls_chan_switch_req));
+
+    if (!tdls_chan_switch_req)
+        return -ENOMEM;
+
+    /* Set parameters for the TDLS_CHAN_SWITCH_REQ message */
+    tdls_chan_switch_req->vif_index = ecrnx_vif->vif_index;
+    tdls_chan_switch_req->sta_idx = ecrnx_sta->sta_idx;
+    memcpy(&(tdls_chan_switch_req->peer_mac_addr.array[0]),
+           ecrnx_sta_addr(ecrnx_sta), ETH_ALEN);
+    tdls_chan_switch_req->initiator = sta_initiator;
+    cfg80211_to_ecrnx_chan(chandef, &tdls_chan_switch_req->chan);
+    tdls_chan_switch_req->op_class = oper_class;
+
+    /* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, tdls_chan_switch_req, 1, TDLS_CHAN_SWITCH_CFM, cfm);
+}
+
+int ecrnx_send_tdls_cancel_chan_switch_req(struct ecrnx_hw *ecrnx_hw,
+                                          struct ecrnx_vif *ecrnx_vif,
+                                          struct ecrnx_sta *ecrnx_sta,
+                                          struct tdls_cancel_chan_switch_cfm *cfm)
+{
+    struct tdls_cancel_chan_switch_req *tdls_cancel_chan_switch_req;
+
+    /* Build the TDLS_CHAN_SWITCH_REQ message */
+    tdls_cancel_chan_switch_req = ecrnx_msg_zalloc(TDLS_CANCEL_CHAN_SWITCH_REQ, TASK_TDLS, DRV_TASK_ID,
+                                           sizeof(struct tdls_cancel_chan_switch_req));
+    if (!tdls_cancel_chan_switch_req)
+        return -ENOMEM;
+
+    /* Set parameters for the TDLS_CHAN_SWITCH_REQ message */
+    tdls_cancel_chan_switch_req->vif_index = ecrnx_vif->vif_index;
+    tdls_cancel_chan_switch_req->sta_idx = ecrnx_sta->sta_idx;
+    memcpy(&(tdls_cancel_chan_switch_req->peer_mac_addr.array[0]),
+           ecrnx_sta_addr(ecrnx_sta), ETH_ALEN);
+
+    /* Send the TDLS_CHAN_SWITCH_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, tdls_cancel_chan_switch_req, 1, TDLS_CANCEL_CHAN_SWITCH_CFM, cfm);
+}
+
+#ifdef CONFIG_ECRNX_BFMER
+#ifdef CONFIG_ECRNX_SOFTMAC
+void ecrnx_send_bfmer_enable(struct ecrnx_hw *ecrnx_hw, struct ieee80211_sta *sta)
+#else
+void ecrnx_send_bfmer_enable(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+                            const struct ieee80211_vht_cap *vht_cap)
+#endif /* CONFIG_ECRNX_SOFTMAC*/
+{
+    struct mm_bfmer_enable_req *bfmer_en_req;
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ecrnx_sta *ecrnx_sta = (struct ecrnx_sta *)&sta->drv_priv;
+    u32 vht_capability;
+#else
+    __le32 vht_capability;
+    u8 rx_nss = 0;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    if (!sta->vht_cap.vht_supported) {
+#else
+    if (!vht_cap) {
+#endif /* CONFIG_ECRNX_SOFTMAC */
+        goto end;
+    }
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    vht_capability = sta->vht_cap.cap;
+#else
+    vht_capability = vht_cap->vht_cap_info;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    if (!(vht_capability & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) {
+        goto end;
+    }
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    rx_nss = ecrnx_bfmer_get_rx_nss(vht_cap);
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+    /* Allocate a structure that will contain the beamforming report */
+    if (ecrnx_bfmer_report_add(ecrnx_hw, ecrnx_sta, ECRNX_BFMER_REPORT_SPACE_SIZE))
+    {
+        goto end;
+    }
+
+    /* Build the MM_BFMER_ENABLE_REQ message */
+    bfmer_en_req = ecrnx_msg_zalloc(MM_BFMER_ENABLE_REQ, TASK_MM, DRV_TASK_ID,
+                                   sizeof(struct mm_bfmer_enable_req));
+
+    /* Check message allocation */
+    if (!bfmer_en_req) {
+        /* Free memory allocated for the report */
+        ecrnx_bfmer_report_del(ecrnx_hw, ecrnx_sta);
+
+        /* Do not use beamforming */
+        goto end;
+    }
+
+    /* Provide DMA address to the MAC */
+    bfmer_en_req->host_bfr_addr = ecrnx_sta->bfm_report->dma_addr;
+    bfmer_en_req->host_bfr_size = ECRNX_BFMER_REPORT_SPACE_SIZE;
+    bfmer_en_req->sta_idx = ecrnx_sta->sta_idx;
+#ifdef CONFIG_ECRNX_SOFTMAC
+    bfmer_en_req->aid = sta->aid;
+    bfmer_en_req->rx_nss = sta->rx_nss;
+#else
+    bfmer_en_req->aid = ecrnx_sta->aid;
+    bfmer_en_req->rx_nss = rx_nss;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    if (vht_capability & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE) {
+        bfmer_en_req->vht_mu_bfmee = true;
+    } else {
+        bfmer_en_req->vht_mu_bfmee = false;
+    }
+
+    /* Send the MM_BFMER_EN_REQ message to LMAC FW */
+    ecrnx_send_msg(ecrnx_hw, bfmer_en_req, 0, 0, NULL);
+
+end:
+    return;
+}
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+int ecrnx_send_mu_group_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta)
+{
+    struct mm_mu_group_update_req *req;
+    int group_id, i = 0;
+    u64 map;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_MU_GROUP_UPDATE_REQ message */
+    req = ecrnx_msg_zalloc(MM_MU_GROUP_UPDATE_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_mu_group_update_req) +
+                          ecrnx_sta->group_info.cnt * sizeof(req->groups[0]));
+
+    /* Check message allocation */
+    if (!req)
+        return -ENOMEM;
+
+    /* Go through the groups the STA belongs to */
+    group_sta_for_each(ecrnx_sta, group_id, map) {
+        int user_pos = ecrnx_mu_group_sta_get_pos(ecrnx_hw, ecrnx_sta, group_id);
+
+        if (WARN((i >= ecrnx_sta->group_info.cnt),
+                 "STA%d: Too much group (%d)\n",
+                 ecrnx_sta->sta_idx, i + 1))
+            break;
+
+        req->groups[i].group_id = group_id;
+        req->groups[i].user_pos = user_pos;
+
+        i++;
+    }
+
+    req->group_cnt = ecrnx_sta->group_info.cnt;
+    req->sta_idx = ecrnx_sta->sta_idx;
+
+    /* Send the MM_MU_GROUP_UPDATE_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, MM_MU_GROUP_UPDATE_CFM, NULL);
+}
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+#endif /* CONFIG_ECRNX_BFMER */
+
+/**********************************************************************
+ *    Debug Messages
+ *********************************************************************/
+int ecrnx_send_dbg_trigger_req(struct ecrnx_hw *ecrnx_hw, char *msg)
+{
+    struct mm_dbg_trigger_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_DBG_TRIGGER_REQ message */
+    req = ecrnx_msg_zalloc(MM_DBG_TRIGGER_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_dbg_trigger_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_DBG_TRIGGER_REQ message */
+    strncpy(req->error, msg, sizeof(req->error));
+
+    /* Send the MM_DBG_TRIGGER_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 0, -1, NULL);
+}
+
+int ecrnx_send_dbg_mem_read_req(struct ecrnx_hw *ecrnx_hw, u32 mem_addr,
+                               struct dbg_mem_read_cfm *cfm)
+{
+    struct dbg_mem_read_req *mem_read_req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the DBG_MEM_READ_REQ message */
+    mem_read_req = ecrnx_msg_zalloc(DBG_MEM_READ_REQ, TASK_DBG, DRV_TASK_ID,
+                                   sizeof(struct dbg_mem_read_req));
+    if (!mem_read_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_MEM_READ_REQ message */
+    mem_read_req->memaddr = mem_addr;
+
+    /* Send the DBG_MEM_READ_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, mem_read_req, 1, DBG_MEM_READ_CFM, cfm);
+}
+
+int ecrnx_send_dbg_mem_write_req(struct ecrnx_hw *ecrnx_hw, u32 mem_addr,
+                                u32 mem_data)
+{
+    struct dbg_mem_write_req *mem_write_req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the DBG_MEM_WRITE_REQ message */
+    mem_write_req = ecrnx_msg_zalloc(DBG_MEM_WRITE_REQ, TASK_DBG, DRV_TASK_ID,
+                                    sizeof(struct dbg_mem_write_req));
+    if (!mem_write_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_MEM_WRITE_REQ message */
+    mem_write_req->memaddr = mem_addr;
+    mem_write_req->memdata = mem_data;
+
+    /* Send the DBG_MEM_WRITE_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, mem_write_req, 1, DBG_MEM_WRITE_CFM, NULL);
+}
+
+int ecrnx_send_dbg_set_mod_filter_req(struct ecrnx_hw *ecrnx_hw, u32 filter)
+{
+    struct dbg_set_mod_filter_req *set_mod_filter_req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the DBG_SET_MOD_FILTER_REQ message */
+    set_mod_filter_req =
+        ecrnx_msg_zalloc(DBG_SET_MOD_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
+                        sizeof(struct dbg_set_mod_filter_req));
+    if (!set_mod_filter_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_SET_MOD_FILTER_REQ message */
+    set_mod_filter_req->mod_filter = filter;
+
+    /* Send the DBG_SET_MOD_FILTER_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, set_mod_filter_req, 1, DBG_SET_MOD_FILTER_CFM, NULL);
+}
+
+int ecrnx_send_dbg_set_sev_filter_req(struct ecrnx_hw *ecrnx_hw, u32 filter)
+{
+    struct dbg_set_sev_filter_req *set_sev_filter_req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the DBG_SET_SEV_FILTER_REQ message */
+    set_sev_filter_req =
+        ecrnx_msg_zalloc(DBG_SET_SEV_FILTER_REQ, TASK_DBG, DRV_TASK_ID,
+                        sizeof(struct dbg_set_sev_filter_req));
+    if (!set_sev_filter_req)
+        return -ENOMEM;
+
+    /* Set parameters for the DBG_SET_SEV_FILTER_REQ message */
+    set_sev_filter_req->sev_filter = filter;
+
+    /* Send the DBG_SET_SEV_FILTER_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, set_sev_filter_req, 1, DBG_SET_SEV_FILTER_CFM, NULL);
+}
+
+int ecrnx_send_dbg_get_sys_stat_req(struct ecrnx_hw *ecrnx_hw,
+                                   struct dbg_get_sys_stat_cfm *cfm)
+{
+    void *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Allocate the message */
+    req = ecrnx_msg_zalloc(DBG_GET_SYS_STAT_REQ, TASK_DBG, DRV_TASK_ID, 0);
+    if (!req)
+        return -ENOMEM;
+
+    /* Send the DBG_MEM_READ_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 1, DBG_GET_SYS_STAT_CFM, cfm);
+}
+
+int ecrnx_send_cfg_rssi_req(struct ecrnx_hw *ecrnx_hw, u8 vif_index, int rssi_thold, u32 rssi_hyst)
+{
+    struct mm_cfg_rssi_req *req;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_CFG_RSSI_REQ message */
+    req = ecrnx_msg_zalloc(MM_CFG_RSSI_REQ, TASK_MM, DRV_TASK_ID,
+                          sizeof(struct mm_cfg_rssi_req));
+    if (!req)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_CFG_RSSI_REQ message */
+    req->vif_index = vif_index;
+    req->rssi_thold = (s8)rssi_thold;
+    req->rssi_hyst = (u8)rssi_hyst;
+
+    /* Send the MM_CFG_RSSI_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, req, 0, 0, NULL);
+}
+
+extern bool set_gain;
+int ecrnx_send_set_gain_delta_req(struct ecrnx_hw *ecrnx_hw)
+{
+       s8_l *delta;
+
+    if (set_gain != true)
+        return -ENOMEM;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Build the MM_SET_GAIN_DELTA_REQ message */
+    delta = ecrnx_msg_zalloc(MM_SET_GAIN_DELTA_REQ, TASK_MM, DRV_TASK_ID,
+                          GAIN_DELTA_CFG_BUF_SIZE);
+    if (!delta)
+        return -ENOMEM;
+
+    /* Set parameters for the MM_SET_GAIN_DELTA_REQ message */
+    memset(delta, 0, GAIN_DELTA_CFG_BUF_SIZE);
+    memcpy(delta, gain_delta, GAIN_DELTA_CFG_BUF_SIZE);
+
+    /* Send the MM_SET_GAIN_DELTA_REQ message to LMAC FW */
+    return ecrnx_send_msg(ecrnx_hw, delta, 0, MM_SET_GAIN_DELTA_CFM, NULL);
+}
+
+int ecrnx_send_cal_result_get_req(struct ecrnx_hw *ecrnx_hw, void *cfm)
+{
+    void *void_param;
+
+       ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    /* calibration result get REQ has no parameter */
+    void_param = ecrnx_msg_zalloc(MM_GET_CAL_RESULT_REQ, TASK_MM, DRV_TASK_ID, 0);
+    if (!void_param)
+        return -ENOMEM;
+
+    return ecrnx_send_msg(ecrnx_hw, void_param, 1, MM_GET_CAL_RESULT_CFM, cfm);
+}
+
+
diff --git a/drivers/net/wireless/eswin/ecrnx_msg_tx.h b/drivers/net/wireless/eswin/ecrnx_msg_tx.h
new file mode 100644 (file)
index 0000000..fc1946b
--- /dev/null
@@ -0,0 +1,198 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_msg_tx.h
+ *
+ * @brief TX function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_MSG_TX_H_
+#define _ECRNX_MSG_TX_H_
+
+#include "ecrnx_defs.h"
+
+int ecrnx_send_reset(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_start(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_version_req(struct ecrnx_hw *ecrnx_hw, struct mm_version_cfm *cfm);
+int ecrnx_send_add_if(struct ecrnx_hw *ecrnx_hw, const unsigned char *mac,
+                     enum nl80211_iftype iftype, bool p2p, struct mm_add_if_cfm *cfm);
+int ecrnx_send_remove_if(struct ecrnx_hw *ecrnx_hw, u8 vif_index);
+int ecrnx_send_set_channel(struct ecrnx_hw *ecrnx_hw, int phy_idx,
+                          struct mm_set_channel_cfm *cfm);
+int ecrnx_send_key_add(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 sta_idx, bool pairwise,
+                      u8 *key, u8 key_len, u8 key_idx, u8 cipher_suite,
+                      struct mm_key_add_cfm *cfm);
+int ecrnx_send_key_del(struct ecrnx_hw *ecrnx_hw, uint8_t hw_key_idx);
+int ecrnx_send_bcn_change(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, dma_addr_t bcn_addr,
+                         u16 bcn_len, u16 tim_oft, u16 tim_len, u16 *csa_oft);
+int ecrnx_send_tim_update(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u16 aid,
+                         u8 tx_status);
+int ecrnx_send_roc(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                  struct ieee80211_channel *chan, unsigned int duration);
+int ecrnx_send_cancel_roc(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_set_power(struct ecrnx_hw *ecrnx_hw,  u8 vif_idx, s8 pwr,
+                        struct mm_set_power_cfm *cfm);
+int ecrnx_send_set_edca(struct ecrnx_hw *ecrnx_hw, u8 hw_queue, u32 param,
+                       bool uapsd, u8 inst_nbr);
+int ecrnx_send_tdls_chan_switch_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                                   struct ecrnx_sta *ecrnx_sta, bool sta_initiator,
+                                   u8 oper_class, struct cfg80211_chan_def *chandef,
+                                   struct tdls_chan_switch_cfm *cfm);
+int ecrnx_send_tdls_cancel_chan_switch_req(struct ecrnx_hw *ecrnx_hw,
+                                          struct ecrnx_vif *ecrnx_vif,
+                                          struct ecrnx_sta *ecrnx_sta,
+                                          struct tdls_cancel_chan_switch_cfm *cfm);
+
+#ifdef CONFIG_ECRNX_P2P_DEBUGFS
+int ecrnx_send_p2p_oppps_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                            u8 ctw, struct mm_set_p2p_oppps_cfm *cfm);
+int ecrnx_send_p2p_noa_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                          int count, int interval, int duration,
+                          bool dyn_noa, struct mm_set_p2p_noa_cfm *cfm);
+#endif /* CONFIG_ECRNX_P2P_DEBUGFS */
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+int ecrnx_send_sta_add(struct ecrnx_hw *ecrnx_hw, struct ieee80211_sta *sta,
+                      u8 inst_nbr, struct mm_sta_add_cfm *cfm);
+int ecrnx_send_sta_del(struct ecrnx_hw *ecrnx_hw, u8 sta_idx);
+int ecrnx_send_set_filter(struct ecrnx_hw *ecrnx_hw, uint32_t filter);
+int ecrnx_send_add_chanctx(struct ecrnx_hw *ecrnx_hw,
+                          struct ieee80211_chanctx_conf *ctx,
+                          struct mm_chan_ctxt_add_cfm *cfm);
+int ecrnx_send_del_chanctx(struct ecrnx_hw *ecrnx_hw, u8 index);
+int ecrnx_send_link_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 chan_idx,
+                           u8 chan_switch);
+int ecrnx_send_unlink_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx);
+int ecrnx_send_update_chanctx(struct ecrnx_hw *ecrnx_hw,
+                             struct ieee80211_chanctx_conf *ctx);
+int ecrnx_send_sched_chanctx(struct ecrnx_hw *ecrnx_hw, u8 vif_idx, u8 chan_idx,
+                            u8 type);
+
+int ecrnx_send_dtim_req(struct ecrnx_hw *ecrnx_hw, u8 dtim_period);
+int ecrnx_send_set_br(struct ecrnx_hw *ecrnx_hw, u32 basic_rates, u8 vif_idx, u8 band);
+int ecrnx_send_set_beacon_int(struct ecrnx_hw *ecrnx_hw, u16 beacon_int, u8 vif_idx);
+int ecrnx_send_set_bssid(struct ecrnx_hw *ecrnx_hw, const u8 *bssid, u8 vif_idx);
+int ecrnx_send_set_vif_state(struct ecrnx_hw *ecrnx_hw, bool active,
+                            u16 aid, u8 vif_idx);
+int ecrnx_send_set_mode(struct ecrnx_hw *ecrnx_hw, u8 abgmode);
+int ecrnx_send_set_idle(struct ecrnx_hw *ecrnx_hw, int idle);
+int ecrnx_send_set_ps_mode(struct ecrnx_hw *ecrnx_hw, u8 ps_mode);
+int ecrnx_send_set_ps_options(struct ecrnx_hw *ecrnx_hw, bool listen_bcmc,
+                             u16 listen_interval, u8 vif_idx);
+int ecrnx_send_set_slottime(struct ecrnx_hw *ecrnx_hw, int use_short_slot);
+int ecrnx_send_ba_add(struct ecrnx_hw *ecrnx_hw, uint8_t type, uint8_t sta_idx,
+                     u16 tid, uint8_t bufsz, uint16_t ssn,
+                     struct mm_ba_add_cfm *cfm);
+int ecrnx_send_ba_del(struct ecrnx_hw *ecrnx_hw, uint8_t sta_idx, u16 tid,
+                     struct mm_ba_del_cfm *cfm);
+int ecrnx_send_scan_req(struct ecrnx_hw *ecrnx_hw, struct ieee80211_vif *vif,
+                       struct cfg80211_scan_request *param,
+                       struct scan_start_cfm *cfm);
+int ecrnx_send_scan_cancel_req(struct ecrnx_hw *ecrnx_hw,
+                              struct scan_cancel_cfm *cfm);
+void ecrnx_send_tdls_ps(struct ecrnx_hw *ecrnx_hw, bool ps_mode);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+int ecrnx_send_me_config_req(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_me_chan_config_req(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_me_set_control_port_req(struct ecrnx_hw *ecrnx_hw, bool opened,
+                                      u8 sta_idx);
+int ecrnx_send_me_sta_add(struct ecrnx_hw *ecrnx_hw, struct station_parameters *params,
+                         const u8 *mac, u8 inst_nbr, struct me_sta_add_cfm *cfm);
+int ecrnx_send_me_sta_del(struct ecrnx_hw *ecrnx_hw, u8 sta_idx, bool tdls_sta);
+int ecrnx_send_me_traffic_ind(struct ecrnx_hw *ecrnx_hw, u8 sta_idx, bool uapsd, u8 tx_status);
+int ecrnx_send_twt_request(struct ecrnx_hw *ecrnx_hw,
+                          u8 setup_type, u8 vif_idx,
+                          struct twt_conf_tag *conf,
+                          struct twt_setup_cfm *cfm);
+int ecrnx_send_twt_teardown(struct ecrnx_hw *ecrnx_hw,
+                           struct twt_teardown_req *twt_teardown,
+                           struct twt_teardown_cfm *cfm);
+int ecrnx_send_me_rc_stats(struct ecrnx_hw *ecrnx_hw, u8 sta_idx,
+                          struct me_rc_stats_cfm *cfm);
+int ecrnx_send_me_rc_set_rate(struct ecrnx_hw *ecrnx_hw,
+                             u8 sta_idx,
+                             u16 rate_idx);
+int ecrnx_send_me_set_ps_mode(struct ecrnx_hw *ecrnx_hw, u8 ps_mode);
+int ecrnx_send_sm_connect_req(struct ecrnx_hw *ecrnx_hw,
+                             struct ecrnx_vif *ecrnx_vif,
+                             struct cfg80211_connect_params *sme,
+                             struct sm_connect_cfm *cfm);
+int ecrnx_send_sm_disconnect_req(struct ecrnx_hw *ecrnx_hw,
+                                struct ecrnx_vif *ecrnx_vif,
+                                u16 reason);
+int ecrnx_send_sm_external_auth_required_rsp(struct ecrnx_hw *ecrnx_hw,
+                                            struct ecrnx_vif *ecrnx_vif,
+                                            u16 status);
+int ecrnx_send_sm_ft_auth_rsp(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                             uint8_t *ie, int ie_len);
+int ecrnx_send_apm_start_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                            struct cfg80211_ap_settings *settings,
+                            struct apm_start_cfm *cfm,
+                            struct ecrnx_ipc_elem_var *elem);
+int ecrnx_send_apm_stop_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif);
+int ecrnx_send_apm_probe_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                            struct ecrnx_sta *sta, struct apm_probe_client_cfm *cfm);
+int ecrnx_send_scanu_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                        struct cfg80211_scan_request *param);
+int ecrnx_send_scanu_cancel_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif);
+int ecrnx_send_apm_start_cac_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                                struct cfg80211_chan_def *chandef,
+                                struct apm_start_cac_cfm *cfm);
+int ecrnx_send_apm_stop_cac_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif);
+int ecrnx_send_tdls_peer_traffic_ind_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif);
+int ecrnx_send_config_monitor_req(struct ecrnx_hw *ecrnx_hw,
+                                 struct cfg80211_chan_def *chandef,
+                                 struct me_config_monitor_cfm *cfm);
+int ecrnx_send_mesh_start_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                             const struct mesh_config *conf, const struct mesh_setup *setup,
+                             struct mesh_start_cfm *cfm);
+int ecrnx_send_mesh_stop_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                            struct mesh_stop_cfm *cfm);
+int ecrnx_send_mesh_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                              u32 mask, const struct mesh_config *p_mconf, struct mesh_update_cfm *cfm);
+int ecrnx_send_mesh_peer_info_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                                 u8 sta_idx, struct mesh_peer_info_cfm *cfm);
+void ecrnx_send_mesh_peer_update_ntf(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif,
+                                    u8 sta_idx, u8 mlink_state);
+void ecrnx_send_mesh_path_create_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, u8 *tgt_addr);
+int ecrnx_send_mesh_path_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, const u8 *tgt_addr,
+                                   const u8 *p_nhop_addr, struct mesh_path_update_cfm *cfm);
+void ecrnx_send_mesh_proxy_add_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *vif, u8 *ext_addr);
+#if defined(CONFIG_ECRNX_P2P)
+int ecrnx_send_p2p_start_listen_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, unsigned int duration);
+int ecrnx_send_p2p_cancel_listen_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif);
+#endif
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+#ifdef CONFIG_ECRNX_BFMER
+#ifdef CONFIG_ECRNX_SOFTMAC
+void ecrnx_send_bfmer_enable(struct ecrnx_hw *ecrnx_hw, struct ieee80211_sta *sta);
+#else
+void ecrnx_send_bfmer_enable(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+                            const struct ieee80211_vht_cap *vht_cap);
+#endif /* CONFIG_ECRNX_SOFTMAC*/
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+int ecrnx_send_mu_group_update_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta);
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+#endif /* CONFIG_ECRNX_BFMER */
+
+/* Debug messages */
+int ecrnx_send_dbg_trigger_req(struct ecrnx_hw *ecrnx_hw, char *msg);
+int ecrnx_send_dbg_mem_read_req(struct ecrnx_hw *ecrnx_hw, u32 mem_addr,
+                               struct dbg_mem_read_cfm *cfm);
+int ecrnx_send_dbg_mem_write_req(struct ecrnx_hw *ecrnx_hw, u32 mem_addr,
+                                u32 mem_data);
+int ecrnx_send_dbg_set_mod_filter_req(struct ecrnx_hw *ecrnx_hw, u32 filter);
+int ecrnx_send_dbg_set_sev_filter_req(struct ecrnx_hw *ecrnx_hw, u32 filter);
+int ecrnx_send_dbg_get_sys_stat_req(struct ecrnx_hw *ecrnx_hw,
+                                   struct dbg_get_sys_stat_cfm *cfm);
+int ecrnx_send_cfg_rssi_req(struct ecrnx_hw *ecrnx_hw, u8 vif_index, int rssi_thold, u32 rssi_hyst);
+int ecrnx_send_set_gain_delta_req(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_send_cal_result_get_req(struct ecrnx_hw *ecrnx_hw, void *cfm);
+#endif /* _ECRNX_MSG_TX_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_mu_group.c b/drivers/net/wireless/eswin/ecrnx_mu_group.c
new file mode 100644 (file)
index 0000000..2725cbd
--- /dev/null
@@ -0,0 +1,659 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_mu_group.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_events.h"
+
+
+/**
+ * ecrnx_mu_group_sta_init - Initialize group information for a STA
+ *
+ * @sta: Sta to initialize
+ */
+void ecrnx_mu_group_sta_init(struct ecrnx_sta *sta,
+                            const struct ieee80211_vht_cap *vht_cap)
+{
+    sta->group_info.map = 0;
+    sta->group_info.cnt = 0;
+    sta->group_info.active.next = LIST_POISON1;
+    sta->group_info.update.next = LIST_POISON1;
+    sta->group_info.last_update = 0;
+    sta->group_info.traffic = 0;
+    sta->group_info.group = 0;
+
+    if (!vht_cap ||
+        !(vht_cap->vht_cap_info & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)) {
+            sta->group_info.map = ECRNX_SU_GROUP;
+    }
+}
+
+/**
+ * ecrnx_mu_group_sta_del - Remove a sta from all MU group
+ *
+ * @ecrnx_hw: main driver data
+ * @sta: STA to remove
+ *
+ * Remove one sta from all the MU groups it belongs to.
+ */
+void ecrnx_mu_group_sta_del(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta)
+{
+    struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+    int i, j, group_id;
+    bool lock_taken;
+    u64 map;
+
+    lock_taken = (down_interruptible(&mu->lock) == 0);
+
+    group_sta_for_each(sta, group_id, map) {
+        struct ecrnx_mu_group *group = ecrnx_mu_group_from_id(mu, group_id);
+
+        for (i = 0; i < CONFIG_USER_MAX; i++) {
+            if (group->users[i] == sta) {
+                group->users[i] = NULL;
+                group->user_cnt --;
+                /* Don't keep group with only one user */
+                if (group->user_cnt == 1) {
+                    for (j = 0; j < CONFIG_USER_MAX; j++) {
+                        if (group->users[j]) {
+                            group->users[j]->group_info.cnt--;
+                            group->users[j]->group_info.map &= ~BIT_ULL(group->group_id);
+                            if (group->users[j]->group_info.group == group_id)
+                                group->users[j]->group_info.group = 0;
+                            group->user_cnt --;
+                            break;
+                        }
+                    }
+                    mu->group_cnt--;
+                    trace_mu_group_delete(group->group_id);
+                } else {
+                    trace_mu_group_update(group);
+                }
+                break;
+            }
+        }
+
+        WARN((i == CONFIG_USER_MAX), "sta %d doesn't belongs to group %d",
+            sta->sta_idx, group_id);
+    }
+
+    sta->group_info.map = 0;
+    sta->group_info.cnt = 0;
+    sta->group_info.traffic = 0;
+
+    if (sta->group_info.active.next != LIST_POISON1)
+        list_del(&sta->group_info.active);
+
+    if (sta->group_info.update.next != LIST_POISON1)
+        list_del(&sta->group_info.update);
+
+    if (lock_taken)
+        up(&mu->lock);
+}
+
+/**
+ * ecrnx_mu_group_sta_get_map - Get the list of group a STA belongs to
+ *
+ * @sta: pointer to the sta
+ *
+ * @return the list of group a STA belongs to as a bitfield
+ */
+u64 ecrnx_mu_group_sta_get_map(struct ecrnx_sta *sta)
+{
+    if (sta)
+        return sta->group_info.map;
+    return 0;
+}
+
+/**
+ * ecrnx_mu_group_sta_get_pos - Get sta position in a group
+ *
+ * @ecrnx_hw: main driver data
+ * @sta: pointer to the sta
+ * @group_id: Group id
+ *
+ * @return the positon of @sta in group @group_id or -1 if the sta
+ * doesn't belongs to the group (or group id is invalid)
+ */
+int ecrnx_mu_group_sta_get_pos(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+                              int group_id)
+{
+    struct ecrnx_mu_group *group;
+    int i;
+
+    group = ecrnx_mu_group_from_id(&ecrnx_hw->mu, group_id);
+    if (!group)
+        return -1;
+
+    for (i = 0; i < CONFIG_USER_MAX; i++) {
+        if (group->users[i] == sta)
+            return i;
+    }
+
+    WARN(1, "sta %d doesn't belongs to group %d",
+         sta->sta_idx, group_id);
+    return -1;
+}
+
+/**
+ * ecrnx_mu_group_move_head - Move (or add) one element at the top of a list
+ *
+ * @list: list pointer
+ * @elem: element to move (or add) at the top of @list
+ *
+ */
+static inline
+void ecrnx_mu_group_move_head(struct list_head *list, struct list_head *elem)
+{
+    if (elem->next != LIST_POISON1) {
+        __list_del_entry(elem);
+    }
+    list_add(elem, list);
+}
+
+/**
+ * ecrnx_mu_group_remove_users - Remove all the users of a group
+ *
+ * @mu: pointer on MU info
+ * @group: pointer on group to remove users from
+ *
+ * Loop over all users one one group and remove this group from their
+ * map (and count).
+ * Each users is also added to the update_sta list, so that group info
+ * will be resent to fw for this user.
+ */
+static inline
+void ecrnx_mu_group_remove_users(struct ecrnx_mu_info *mu,
+                                struct ecrnx_mu_group *group)
+{
+    struct ecrnx_sta *sta;
+    int i, group_id = group->group_id;
+
+    for (i = 0; i < CONFIG_USER_MAX; i++) {
+        if (group->users[i]) {
+            sta = group->users[i];
+            group->users[i] = NULL;
+            sta->group_info.cnt--;
+            sta->group_info.map &= ~BIT_ULL(group_id);
+            ecrnx_mu_group_move_head(&mu->update_sta,
+                                    &sta->group_info.update);
+        }
+    }
+
+    if (group->user_cnt)
+        mu->group_cnt--;
+    group->user_cnt = 0;
+    trace_mu_group_delete(group_id);
+}
+
+/**
+ * ecrnx_mu_group_add_users - Add users to a group
+ *
+ * @mu: pointer on MU info
+ * @group: pointer on group to add users in
+ * @nb_user: number of users to ad
+ * @users: table of user to add
+ *
+ * Add @nb_users to @group (which may already have users)
+ * Each new users is added to the first free position.
+ * It is assume that @group has at least @nb_user free position. If it is not
+ * case it only add the number of users needed to complete the group.
+ * Each users (effectively added to @group) is also added to the update_sta
+ * list, so that group info will be resent to fw for this user.
+ */
+static inline
+void ecrnx_mu_group_add_users(struct ecrnx_mu_info *mu,
+                             struct ecrnx_mu_group *group,
+                             int nb_user, struct ecrnx_sta **users)
+{
+    int i, j, group_id = group->group_id;
+
+    if (!group->user_cnt)
+        mu->group_cnt++;
+
+    j = 0;
+    for (i = 0; i < nb_user ; i++) {
+        for (; j < CONFIG_USER_MAX ; j++) {
+            if (group->users[j] == NULL) {
+                group->users[j] = users[i];
+                users[i]->group_info.cnt ++;
+                users[i]->group_info.map |= BIT_ULL(group_id);
+
+                ecrnx_mu_group_move_head(&(mu->update_sta),
+                                        &(users[i]->group_info.update));
+                group->user_cnt ++;
+                j ++;
+                break;
+            }
+
+            WARN(j == (CONFIG_USER_MAX - 1),
+                 "Too many user for group %d (nb_user=%d)",
+                 group_id, group->user_cnt + nb_user - i);
+        }
+    }
+
+    trace_mu_group_update(group);
+}
+
+
+/**
+ * ecrnx_mu_group_create_one - create on group with a specific group of user
+ *
+ * @mu: pointer on MU info
+ * @nb_user: number of user to include in the group (<= CONFIG_USER_MAX)
+ * @users: table of users
+ *
+ * Try to create a new group with a specific group of users.
+ * 1- First it checks if a group containing all this users already exists.
+ *
+ * 2- Then it checks if it is possible to complete a group which already
+ *    contains at least one user.
+ *
+ * 3- Finally it create a new group. To do so, it take take the last group of
+ *    the active_groups list, remove all its current users and add the new ones
+ *
+ * In all cases, the group selected is moved at the top of the active_groups
+ * list
+ *
+ * @return 1 if a new group has been created and 0 otherwise
+ */
+static
+int ecrnx_mu_group_create_one(struct ecrnx_mu_info *mu, int nb_user,
+                             struct ecrnx_sta **users, int *nb_group_left)
+{
+    int i, group_id;
+    struct ecrnx_mu_group *group;
+    u64 group_match;
+    u64 group_avail;
+
+    group_match = users[0]->group_info.map;
+    group_avail = users[0]->group_info.map;
+    for (i = 1; i < nb_user ; i++) {
+        group_match &= users[i]->group_info.map;
+        group_avail |= users[i]->group_info.map;
+
+    }
+
+    if (group_match) {
+        /* a group (or more) with all the users already exist */
+        group_id = ECRNX_GET_FIRST_GROUP_ID(group_match);
+        group = ecrnx_mu_group_from_id(mu, group_id);
+        ecrnx_mu_group_move_head(&mu->active_groups, &group->list);
+        return 0;
+    }
+
+#if CONFIG_USER_MAX > 2
+    if (group_avail) {
+        /* check if we can complete a group */
+        struct ecrnx_sta *users2[CONFIG_USER_MAX];
+        int nb_user2;
+
+        group_for_each(group_id, group_avail) {
+            group = ecrnx_mu_group_from_id(mu, group_id);
+            if (group->user_cnt == CONFIG_USER_MAX)
+                continue;
+
+            nb_user2 = 0;
+            for (i = 0; i < nb_user ; i++) {
+                if (!(users[i]->group_info.map & BIT_ULL(group_id))) {
+                    users2[nb_user2] = users[i];
+                    nb_user2++;
+                }
+            }
+
+            if ((group->user_cnt + nb_user2) <= CONFIG_USER_MAX) {
+                ecrnx_mu_group_add_users(mu, group, nb_user2, users2);
+                ecrnx_mu_group_move_head(&mu->active_groups, &group->list);
+                return 0;
+            }
+        }
+    }
+#endif /* CONFIG_USER_MAX > 2*/
+
+    /* create a new group */
+    group = list_last_entry(&mu->active_groups, struct ecrnx_mu_group, list);
+    ecrnx_mu_group_remove_users(mu, group);
+    ecrnx_mu_group_add_users(mu, group, nb_user, users);
+    ecrnx_mu_group_move_head(&mu->active_groups, &group->list);
+    (*nb_group_left)--;
+
+    return 1;
+}
+
+/**
+ * ecrnx_mu_group_create - Create new groups containing one specific sta
+ *
+ * @mu: pointer on MU info
+ * @sta: sta to add in each group
+ * @nb_group_left: maximum number to new group allowed. (updated on exit)
+ *
+ * This will try to create "all the possible" group with a specific sta being
+ * a member of all these group.
+ * The function simply loops over the @active_sta list (starting from @sta).
+ * When it has (CONFIG_USER_MAX - 1) users it try to create a new group with
+ * these users (plus @sta).
+ * Loops end when there is no more users, or no more new group is allowed
+ *
+ */
+static
+void ecrnx_mu_group_create(struct ecrnx_mu_info *mu, struct ecrnx_sta *sta,
+                          int *nb_group_left)
+{
+    struct ecrnx_sta *user_sta = sta;
+    struct ecrnx_sta *users[CONFIG_USER_MAX];
+    int nb_user = 1;
+
+    users[0] = sta;
+    while (*nb_group_left) {
+
+        list_for_each_entry_continue(user_sta, &mu->active_sta, group_info.active) {
+            users[nb_user] = user_sta;
+            if (++nb_user == CONFIG_USER_MAX) {
+                break;
+            }
+        }
+
+        if (nb_user > 1) {
+            if (ecrnx_mu_group_create_one(mu, nb_user, users, nb_group_left))
+                (*nb_group_left)--;
+
+            if (nb_user < CONFIG_USER_MAX)
+                break;
+            else
+                nb_user = 1;
+        } else
+            break;
+    }
+}
+
+/**
+ * ecrnx_mu_group_work - process function of the "group_work"
+ *
+ * The work is scheduled when several sta (MU beamformee capable) are active.
+ * When called, the @active_sta contains the list of the active sta (starting
+ * from the most recent one), and @active_groups is the list of all possible
+ * groups ordered so that the first one is the most recently used.
+ *
+ * This function will create new groups, starting from group containing the
+ * most "active" sta.
+ * For example if the list of sta is :
+ * sta8 -> sta3 -> sta4 -> sta7 -> sta1
+ * and the number of user per group is 3, it will create grooups :
+ * - sta8 / sta3 / sta4
+ * - sta8 / sta7 / sta1
+ * - sta3 / sta4 / sta7
+ * - sta3 / sta1
+ * - sta4 / sta7 / sta1
+ * - sta7 / sta1
+ *
+ * To create new group, the least used group are first selected.
+ * It is only allowed to create NX_MU_GROUP_MAX per iteration.
+ *
+ * Once groups have been updated, mu group information is update to the fw.
+ * To do so it use the @update_sta list to know which sta has been affected.
+ * As it is necessary to wait for fw confirmation before using this new group
+ * MU is temporarily disabled during group update
+ *
+ * Work is then rescheduled.
+ *
+ * At the end of the function, both @active_sta and @update_sta list are empty.
+ *
+ * Note:
+ * - This is still a WIP, and will require more tuning
+ * - not all combinations are created, to avoid to much processing.
+ * - reschedule delay should be adaptative
+ */
+void ecrnx_mu_group_work(struct work_struct *ws)
+{
+    struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+    struct ecrnx_mu_info *mu = container_of(dw, struct ecrnx_mu_info, group_work);
+    struct ecrnx_hw *ecrnx_hw = container_of(mu, struct ecrnx_hw, mu);
+    struct ecrnx_sta *sta, *next;
+    int nb_group_left = NX_MU_GROUP_MAX;
+
+    if (WARN(!ecrnx_hw->mod_params->mutx,
+             "In group formation work, but mutx disabled"))
+        return;
+
+    if (down_interruptible(&mu->lock) != 0)
+        return;
+
+    mu->update_count++;
+    if (!mu->update_count)
+        mu->update_count++;
+
+    list_for_each_entry_safe(sta, next, &mu->active_sta, group_info.active) {
+        if (nb_group_left)
+            ecrnx_mu_group_create(mu, sta, &nb_group_left);
+
+        sta->group_info.last_update = mu->update_count;
+        list_del(&sta->group_info.active);
+    }
+
+    if (! list_empty(&mu->update_sta)) {
+        list_for_each_entry_safe(sta, next, &mu->update_sta, group_info.update) {
+            ecrnx_send_mu_group_update_req(ecrnx_hw, sta);
+            list_del(&sta->group_info.update);
+        }
+    }
+
+    mu->next_group_select = jiffies;
+    ecrnx_mu_group_sta_select(ecrnx_hw);
+    up(&mu->lock);
+
+    return;
+}
+
+/**
+ * ecrnx_mu_group_init - Initialize MU groups
+ *
+ * @ecrnx_hw: main driver data
+ *
+ * Initialize all MU group
+ */
+void ecrnx_mu_group_init(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+    int i;
+
+    INIT_LIST_HEAD(&mu->active_groups);
+    INIT_LIST_HEAD(&mu->active_sta);
+    INIT_LIST_HEAD(&mu->update_sta);
+
+    for (i = 0; i < NX_MU_GROUP_MAX; i++) {
+        int j;
+        mu->groups[i].user_cnt = 0;
+        mu->groups[i].group_id = i + 1;
+        for (j = 0; j < CONFIG_USER_MAX; j++) {
+            mu->groups[i].users[j] = NULL;
+        }
+        list_add(&mu->groups[i].list, &mu->active_groups);
+    }
+
+    mu->update_count = 1;
+    mu->group_cnt = 0;
+    mu->next_group_select = jiffies;
+    INIT_DELAYED_WORK(&mu->group_work, ecrnx_mu_group_work);
+    sema_init(&mu->lock, 1);
+}
+
+/**
+ * ecrnx_mu_set_active_sta - mark a STA as active
+ *
+ * @ecrnx_hw: main driver data
+ * @sta: pointer to the sta
+ * @traffic: Number of buffers to add in the sta's traffic counter
+ *
+ * If @sta is MU beamformee capable (and MU-MIMO tx is enabled) move the
+ * sta at the top of the @active_sta list.
+ * It also schedule the group_work if not already scheduled and the list
+ * contains more than one sta.
+ *
+ * If a STA was already in the list during the last group update
+ * (i.e. sta->group_info.last_update == mu->update_count) it is not added
+ * back to the list until a sta that wasn't active during the last update is
+ * added. This is to avoid scheduling group update with a list of sta that
+ * were all already in the list during previous update.
+ *
+ * It is called with mu->lock taken.
+ */
+void ecrnx_mu_set_active_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+                            int traffic)
+{
+    struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+
+    if (!sta || (sta->group_info.map & ECRNX_SU_GROUP))
+        return;
+
+    sta->group_info.traffic += traffic;
+
+    if ((sta->group_info.last_update != mu->update_count) ||
+        !list_empty(&mu->active_sta)) {
+
+        ecrnx_mu_group_move_head(&mu->active_sta, &sta->group_info.active);
+
+        if (!delayed_work_pending(&mu->group_work) &&
+            !list_is_singular(&mu->active_sta)) {
+            schedule_delayed_work(&mu->group_work,
+                                  msecs_to_jiffies(ECRNX_MU_GROUP_INTERVAL));
+        }
+    }
+}
+
+/**
+ * ecrnx_mu_set_active_group - mark a MU group as active
+ *
+ * @ecrnx_hw: main driver data
+ * @group_id: Group id
+ *
+ * move a group at the top of the @active_groups list
+ */
+void ecrnx_mu_set_active_group(struct ecrnx_hw *ecrnx_hw, int group_id)
+{
+    struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+    struct ecrnx_mu_group *group = ecrnx_mu_group_from_id(mu, group_id);
+
+    ecrnx_mu_group_move_head(&mu->active_groups, &group->list);
+}
+
+
+/**
+ * ecrnx_mu_group_sta_select - Select the best group for MU stas
+ *
+ * @ecrnx_hw: main driver data
+ *
+ * For each MU capable client of AP interfaces this function tries to select
+ * the best group to use.
+ *
+ * In first pass, gather information from all stations to form statistics
+ * for each group for the previous @ECRNX_MU_GROUP_SELECT_INTERVAL interval:
+ * - number of buffers transmitted
+ * - number of user
+ *
+ * Then groups with more than 2 active users, are assigned after being ordered
+ * by traffic :
+ * - group with highest traffic is selected: set this group for all its users
+ * - update nb_users for all others group (as one sta may be in several groups)
+ * - select the next group that have still mor than 2 users and assign it.
+ * - continue until all group are processed
+ *
+ */
+void ecrnx_mu_group_sta_select(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_mu_info *mu = &ecrnx_hw->mu;
+    int nb_users[NX_MU_GROUP_MAX + 1];
+    int traffic[NX_MU_GROUP_MAX + 1];
+    int order[NX_MU_GROUP_MAX + 1];
+    struct ecrnx_sta *sta;
+    struct ecrnx_vif *vif;
+    struct list_head *head;
+    u64 map;
+    int i, j, update, group_id, tmp, cnt = 0;
+
+    if (!mu->group_cnt || time_before(jiffies, mu->next_group_select))
+        return;
+
+    list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+
+        if (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP)
+            continue;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+        head = &vif->ap.sta_list;
+#else
+        head = &vif->stations;
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+        memset(nb_users, 0, sizeof(nb_users));
+        memset(traffic, 0, sizeof(traffic));
+        list_for_each_entry(sta, head, list) {
+            int sta_traffic = sta->group_info.traffic;
+
+            /* reset statistics for next selection */
+            sta->group_info.traffic = 0;
+            if (sta->group_info.group)
+                trace_mu_group_selection(sta, 0);
+            sta->group_info.group = 0;
+
+            if (sta->group_info.cnt == 0 ||
+                sta_traffic < ECRNX_MU_GROUP_MIN_TRAFFIC)
+                continue;
+
+            group_sta_for_each(sta, group_id, map) {
+                nb_users[group_id]++;
+                traffic[group_id] += sta_traffic;
+
+                /* list group with 2 users or more */
+                if (nb_users[group_id] == 2)
+                    order[cnt++] = group_id;
+            }
+        }
+
+        /* reorder list of group with more that 2 users */
+        update = 1;
+        while(update) {
+            update = 0;
+            for (i = 0; i < cnt - 1; i++) {
+                if (traffic[order[i]] < traffic[order[i + 1]]) {
+                    tmp = order[i];
+                    order[i] = order[i + 1];
+                    order[i + 1] = tmp;
+                    update = 1;
+                }
+            }
+        }
+
+        /* now assign group in traffic order */
+        for (i = 0; i < cnt ; i ++) {
+            struct ecrnx_mu_group *group;
+            group_id = order[i];
+
+            if (nb_users[group_id] < 2)
+                continue;
+
+            group = ecrnx_mu_group_from_id(mu, group_id);
+            for (j = 0; j < CONFIG_USER_MAX ; j++) {
+                if (group->users[j]) {
+                    trace_mu_group_selection(group->users[j], group_id);
+                    group->users[j]->group_info.group = group_id;
+
+                    group_sta_for_each(group->users[j], tmp, map) {
+                        if (group_id != tmp)
+                            nb_users[tmp]--;
+                    }
+                }
+            }
+        }
+    }
+
+    mu->next_group_select = jiffies +
+        msecs_to_jiffies(ECRNX_MU_GROUP_SELECT_INTERVAL);
+    mu->next_group_select |= 1;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_mu_group.h b/drivers/net/wireless/eswin/ecrnx_mu_group.h
new file mode 100644 (file)
index 0000000..1251986
--- /dev/null
@@ -0,0 +1,179 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_mu_group.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _ECRNX_MU_GROUP_H_
+#define _ECRNX_MU_GROUP_H_
+
+#include <linux/workqueue.h>
+#include <linux/semaphore.h>
+
+struct ecrnx_hw;
+struct ecrnx_sta;
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+
+/**
+ * struct ecrnx_sta_group_info - Group Information for a STA
+ *
+ * @active: node for @mu->active_sta list
+ * @update: node for @mu->update_sta list
+ * @cnt: Number of groups the STA belongs to
+ * @map: Bitfield of groups the sta belongs to
+ * @traffic: Number of buffers sent since previous group selection
+ * @group: Id of the group selected by previous group selection
+ *         (cf @ecrnx_mu_group_sta_select)
+ */
+struct ecrnx_sta_group_info {
+    struct list_head active;
+    struct list_head update;
+    u16 last_update;
+    int cnt;
+    u64 map;
+    int traffic;
+    u8  group;
+};
+
+/**
+ * struct mu_group_info - Information about the users of a group
+ *
+ * @list: node for mu->active_groups
+ * @group_id: Group identifier
+ * @user_cnt: Number of the users in the group
+ * @users: Pointer to the sta, ordered by user position
+ */
+struct ecrnx_mu_group {
+    struct list_head list;
+    int group_id;
+    int user_cnt;
+    struct ecrnx_sta *users[CONFIG_USER_MAX];
+};
+
+/**
+ * struct ecrnx_mu_info - Information about all MU group
+ *
+ * @active_groups: List of all possible groups. Ordered from the most recently
+ *                 used one to the least one (and possibly never used)
+ * @active_sta: List of MU beamformee sta that have been active (since previous
+ *              group update). Ordered from the most recently active.
+ * @update_sta: List of sta whose group information has changed and need to be
+ *              updated at fw level
+ * @groups: Table of all groups
+ * @group_work: Work item used to schedule group update
+ * @update_count: Counter used to identify the last group formation update.
+ *                (cf ecrnx_sta_group_info.last_update)
+ * @lock: Lock taken during group update. If tx happens lock is taken, then tx
+ *        will not used MU.
+ * @next_group_assign: Next time the group selection should be run
+ *                     (ref @ecrnx_mu_group_sta_select)
+ * @group_cnt: Number of group created
+ */
+struct ecrnx_mu_info {
+    struct list_head active_groups;
+    struct list_head active_sta;
+    struct list_head update_sta;
+    struct ecrnx_mu_group groups[NX_MU_GROUP_MAX];
+    struct delayed_work group_work;
+    u16 update_count;
+    struct semaphore lock;
+    unsigned long next_group_select;
+    u8 group_cnt;
+};
+
+#define ECRNX_SU_GROUP BIT_ULL(0)
+#define ECRNX_MU_GROUP_MASK 0x7ffffffffffffffeULL
+#define ECRNX_MU_GROUP_INTERVAL 200 /* in ms */
+#define ECRNX_MU_GROUP_SELECT_INTERVAL 100 /* in ms */
+// minimum traffic in a ECRNX_MU_GROUP_SELECT_INTERVAL to consider the sta
+#define ECRNX_MU_GROUP_MIN_TRAFFIC 50 /* in number of packet */
+
+
+#define ECRNX_GET_FIRST_GROUP_ID(map) (fls64(map) - 1)
+
+#define group_sta_for_each(sta, id, map)                                \
+    map = sta->group_info.map & ECRNX_MU_GROUP_MASK;                     \
+    for (id = (fls64(map) - 1) ; id > 0 ;                               \
+         map &= ~(u64)BIT_ULL(id), id = (fls64(map) - 1))
+
+#define group_for_each(id, map)                                         \
+    for (id = (fls64(map) - 1) ; id > 0 ;                               \
+         map &= ~(u64)BIT_ULL(id), id = (fls64(map) - 1))
+
+#define ECRNX_MUMIMO_INFO_POS_ID(info) (((info) >> 6) & 0x3)
+#define ECRNX_MUMIMO_INFO_GROUP_ID(info) ((info) & 0x3f)
+
+static inline
+struct ecrnx_mu_group *ecrnx_mu_group_from_id(struct ecrnx_mu_info *mu, int id)
+{
+    if (id > NX_MU_GROUP_MAX)
+        return NULL;
+
+    return &mu->groups[id - 1];
+}
+
+
+void ecrnx_mu_group_sta_init(struct ecrnx_sta *sta,
+                            const struct ieee80211_vht_cap *vht_cap);
+void ecrnx_mu_group_sta_del(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta);
+u64 ecrnx_mu_group_sta_get_map(struct ecrnx_sta *sta);
+int ecrnx_mu_group_sta_get_pos(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+                              int group_id);
+
+void ecrnx_mu_group_init(struct ecrnx_hw *ecrnx_hw);
+
+void ecrnx_mu_set_active_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+                            int traffic);
+void ecrnx_mu_set_active_group(struct ecrnx_hw *ecrnx_hw, int group_id);
+void ecrnx_mu_group_sta_select(struct ecrnx_hw *ecrnx_hw);
+
+
+#else /* ! CONFIG_ECRNX_MUMIMO_TX */
+
+static inline
+void ecrnx_mu_group_sta_init(struct ecrnx_sta *sta,
+                            const struct ieee80211_vht_cap *vht_cap)
+{}
+
+static inline
+void ecrnx_mu_group_sta_del(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta)
+{}
+
+static inline
+u64 ecrnx_mu_group_sta_get_map(struct ecrnx_sta *sta)
+{
+    return 0;
+}
+
+static inline
+int ecrnx_mu_group_sta_get_pos(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+                              int group_id)
+{
+    return 0;
+}
+
+static inline
+void ecrnx_mu_group_init(struct ecrnx_hw *ecrnx_hw)
+{}
+
+static inline
+void ecrnx_mu_set_active_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+                            int traffic)
+{}
+
+static inline
+void ecrnx_mu_set_active_group(struct ecrnx_hw *ecrnx_hw, int group_id)
+{}
+
+static inline
+void ecrnx_mu_group_sta_select(struct ecrnx_hw *ecrnx_hw)
+{}
+
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+#endif /* _ECRNX_MU_GROUP_H_ */
+
diff --git a/drivers/net/wireless/eswin/ecrnx_platform.c b/drivers/net/wireless/eswin/ecrnx_platform.c
new file mode 100644 (file)
index 0000000..76295c2
--- /dev/null
@@ -0,0 +1,1107 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_platform.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include "ecrnx_platform.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+#include "ecrnx_main.h"
+
+#ifndef CONFIG_ECRNX_ESWIN
+#include "ecrnx_pci.h"
+#ifndef CONFIG_ECRNX_FHOST
+#include "ipc_host.h"
+#endif /* !CONFIG_ECRNX_FHOST */
+#endif /* CONFIG_ECRNX_ESWIN */
+
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+#include "sdio.h"
+#include "ecrnx_sdio.h"
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+#include "usb.h"
+#include "ecrnx_usb.h"
+#endif
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "core.h"
+#include "ecrnx_amt.h"
+#endif
+
+#ifdef CONFIG_ECRNX_TL4
+/**
+ * ecrnx_plat_tl4_fw_upload() - Load the requested FW into embedded side.
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a hex file, into the specified address
+ */
+static int ecrnx_plat_tl4_fw_upload(struct ecrnx_plat *ecrnx_plat, u8* fw_addr,
+                                   char *filename)
+{
+    struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+    const struct firmware *fw;
+    int err = 0;
+    u32 *dst;
+    u8 const *file_data;
+    char typ0, typ1;
+    u32 addr0, addr1;
+    u32 dat0, dat1;
+    int remain;
+
+    err = request_firmware(&fw, filename, dev);
+    if (err) {
+        return err;
+    }
+    file_data = fw->data;
+    remain = fw->size;
+
+    /* Copy the file on the Embedded side */
+    dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+    /* Walk through all the lines of the configuration file */
+    while (remain >= 16) {
+        u32 data, offset;
+
+        if (sscanf(file_data, "%c:%08X %04X", &typ0, &addr0, &dat0) != 3)
+            break;
+        if ((addr0 & 0x01) != 0) {
+            addr0 = addr0 - 1;
+            dat0 = 0;
+        } else {
+            file_data += 16;
+            remain -= 16;
+        }
+        if ((remain < 16) ||
+            (sscanf(file_data, "%c:%08X %04X", &typ1, &addr1, &dat1) != 3) ||
+            (typ1 != typ0) || (addr1 != (addr0 + 1))) {
+            typ1 = typ0;
+            addr1 = addr0 + 1;
+            dat1 = 0;
+        } else {
+            file_data += 16;
+            remain -= 16;
+        }
+
+        if (typ0 == 'C') {
+            offset = 0x00200000;
+            if ((addr1 % 4) == 3)
+                offset += 2*(addr1 - 3);
+            else
+                offset += 2*(addr1 + 1);
+
+            data = dat1 | (dat0 << 16);
+        } else {
+            offset = 2*(addr1 - 1);
+            data = dat0 | (dat1 << 16);
+        }
+        dst = (u32 *)(fw_addr + offset);
+        *dst = data;
+    }
+
+    release_firmware(fw);
+
+    return err;
+}
+#endif
+
+/**
+ * ecrnx_plat_bin_fw_upload() - Load the requested binary FW into embedded side.
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a binary file, into the specified address
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_bin_fw_upload(struct ecrnx_plat *ecrnx_plat, u8* fw_addr,
+                               char *filename)
+{
+    const struct firmware *fw;
+    struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+    int err = 0;
+    unsigned int i, size;
+    u32 *src, *dst;
+
+    err = request_firmware(&fw, filename, dev);
+    if (err) {
+        return err;
+    }
+
+    /* Copy the file on the Embedded side */
+    dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+    src = (u32 *)fw->data;
+    dst = (u32 *)fw_addr;
+    size = (unsigned int)fw->size;
+
+    /* check potential platform bug on multiple stores vs memcpy */
+    for (i = 0; i < size; i += 4) {
+        *dst++ = *src++;
+    }
+
+    release_firmware(fw);
+
+    return err;
+}
+#endif
+
+#ifndef CONFIG_ECRNX_TL4
+#define IHEX_REC_DATA           0
+#define IHEX_REC_EOF            1
+#define IHEX_REC_EXT_SEG_ADD    2
+#define IHEX_REC_START_SEG_ADD  3
+#define IHEX_REC_EXT_LIN_ADD    4
+#define IHEX_REC_START_LIN_ADD  5
+
+/**
+ * ecrnx_plat_ihex_fw_upload() - Load the requested intel hex 8 FW into embedded side.
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @fw_addr: Virtual address where the fw must be loaded
+ * @filename: Name of the fw.
+ *
+ * Load a fw, stored as a ihex file, into the specified address.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_ihex_fw_upload(struct ecrnx_plat *ecrnx_plat, u8* fw_addr,
+                                    char *filename)
+{
+    const struct firmware *fw;
+    struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+    u8 const *src, *end;
+    u32 *dst;
+    u16 haddr, segaddr, addr;
+    u32 hwaddr;
+    u8 load_fw, byte_count, checksum, csum, rec_type;
+    int err, rec_idx;
+    char hex_buff[9];
+
+    err = request_firmware(&fw, filename, dev);
+    if (err) {
+        return err;
+    }
+
+    /* Copy the file on the Embedded side */
+    dev_dbg(dev, "\n### Now copy %s firmware, @ = %p\n", filename, fw_addr);
+
+    src = fw->data;
+    end = src + (unsigned int)fw->size;
+    haddr = 0;
+    segaddr = 0;
+    load_fw = 1;
+    err = -EINVAL;
+    rec_idx = 0;
+    hwaddr = 0;
+
+#define IHEX_READ8(_val, _cs) {                  \
+        hex_buff[2] = 0;                         \
+        strncpy(hex_buff, src, 2);               \
+        if (kstrtou8(hex_buff, 16, &_val))       \
+            goto end;                            \
+        src += 2;                                \
+        if (_cs)                                 \
+            csum += _val;                        \
+    }
+
+#define IHEX_READ16(_val) {                        \
+        hex_buff[4] = 0;                           \
+        strncpy(hex_buff, src, 4);                 \
+        if (kstrtou16(hex_buff, 16, &_val))        \
+            goto end;                              \
+        src += 4;                                  \
+        csum += (_val & 0xff) + (_val >> 8);       \
+    }
+
+#define IHEX_READ32(_val) {                              \
+        hex_buff[8] = 0;                                 \
+        strncpy(hex_buff, src, 8);                       \
+        if (kstrtouint(hex_buff, 16, &_val))             \
+            goto end;                                    \
+        src += 8;                                        \
+        csum += (_val & 0xff) + ((_val >> 8) & 0xff) +   \
+            ((_val >> 16) & 0xff) + (_val >> 24);        \
+    }
+
+#define IHEX_READ32_PAD(_val, _nb) {                    \
+        memset(hex_buff, '0', 8);                       \
+        hex_buff[8] = 0;                                \
+        strncpy(hex_buff, src, (2 * _nb));              \
+        if (kstrtouint(hex_buff, 16, &_val))            \
+            goto end;                                   \
+        src += (2 * _nb);                               \
+        csum += (_val & 0xff) + ((_val >> 8) & 0xff) +  \
+            ((_val >> 16) & 0xff) + (_val >> 24);       \
+}
+
+    /* loop until end of file is read*/
+    while (load_fw) {
+        rec_idx++;
+        csum = 0;
+
+        /* Find next colon start code */
+        while (*src != ':') {
+            src++;
+            if ((src + 3) >= end) /* 3 = : + rec_len */
+                goto end;
+        }
+        src++;
+
+        /* Read record len */
+        IHEX_READ8(byte_count, 1);
+        if ((src + (byte_count * 2) + 8) >= end) /* 8 = rec_addr + rec_type + chksum */
+            goto end;
+
+        /* Read record addr */
+        IHEX_READ16(addr);
+
+        /* Read record type */
+        IHEX_READ8(rec_type, 1);
+
+        switch(rec_type) {
+            case IHEX_REC_DATA:
+            {
+                /* Update destination address */
+                dst = (u32 *) (fw_addr + hwaddr + addr);
+
+                while (byte_count) {
+                    u32 val;
+                    if (byte_count >= 4) {
+                        IHEX_READ32(val);
+                        byte_count -= 4;
+                    } else {
+                        IHEX_READ32_PAD(val, byte_count);
+                        byte_count = 0;
+                    }
+                    *dst++ = __swab32(val);
+                }
+                break;
+            }
+            case IHEX_REC_EOF:
+            {
+                load_fw = 0;
+                err = 0;
+                break;
+            }
+            case IHEX_REC_EXT_SEG_ADD: /* Extended Segment Address */
+            {
+                IHEX_READ16(segaddr);
+                hwaddr = (haddr << 16) + (segaddr << 4);
+                break;
+            }
+            case IHEX_REC_EXT_LIN_ADD: /* Extended Linear Address */
+            {
+                IHEX_READ16(haddr);
+                hwaddr = (haddr << 16) + (segaddr << 4);
+                break;
+            }
+            case IHEX_REC_START_LIN_ADD: /* Start Linear Address */
+            {
+                u32 val;
+                IHEX_READ32(val); /* need to read for checksum */
+                break;
+            }
+            case IHEX_REC_START_SEG_ADD:
+            default:
+            {
+                dev_err(dev, "ihex: record type %d not supported\n", rec_type);
+                load_fw = 0;
+            }
+        }
+
+        /* Read and compare checksum */
+        IHEX_READ8(checksum, 0);
+        if (checksum != (u8)(~csum + 1))
+            goto end;
+    }
+
+#undef IHEX_READ8
+#undef IHEX_READ16
+#undef IHEX_READ32
+#undef IHEX_READ32_PAD
+
+  end:
+    release_firmware(fw);
+
+    if (err)
+        dev_err(dev, "%s: Invalid ihex record around line %d\n", filename, rec_idx);
+
+    return err;
+}
+#endif /* CONFIG_ECRNX_TL4 */
+#endif
+
+#ifndef CONFIG_ECRNX_ESWIN
+#ifndef CONFIG_ECRNX_SDM
+/**
+ * ecrnx_plat_get_rf() - Retrun the RF used in the platform
+ *
+ * @ecrnx_plat: pointer to platform structure
+ */
+static u32 ecrnx_plat_get_rf(struct ecrnx_plat *ecrnx_plat)
+{
+    u32 ver;
+    ver = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, MDM_HDMCONFIG_ADDR);
+
+    ver = __MDM_PHYCFG_FROM_VERS(ver);
+    WARN(((ver != MDM_PHY_CONFIG_TRIDENT) &&
+          (ver != MDM_PHY_CONFIG_CATAXIA) &&
+          (ver != MDM_PHY_CONFIG_KARST)),
+         "Unknown PHY version 0x%08x\n", ver);
+
+    return ver;
+}
+#endif
+
+/**
+ * ecrnx_plat_get_clkctrl_addr() - Return the clock control register address
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_SDM
+#ifndef CONFIG_ECRNX_ESWIN
+static u32 ecrnx_plat_get_clkctrl_addr(struct ecrnx_plat *ecrnx_plat)
+{
+    u32 regval;
+    if (ecrnx_plat_get_rf(ecrnx_plat) ==  MDM_PHY_CONFIG_TRIDENT)
+        return MDM_MEMCLKCTRL0_ADDR;
+    regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+    if (__FPGA_TYPE(regval) == 0xC0CA)
+        return CRM_CLKGATEFCTRL0_ADDR;
+    else
+        return MDM_CLKGATEFCTRL0_ADDR;
+}
+#endif /* CONFIG_ECRNX_SDM */
+#endif
+
+/**
+ * ecrnx_plat_stop_agcfsm() - Stop a AGC state machine
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @agg_reg: Address of the agccntl register (within ECRNX_ADDR_SYSTEM)
+ * @agcctl: Updated with value of the agccntl rgister before stop
+ * @memclk: Updated with value of the clock register before stop
+ * @agc_ver: Version of the AGC load procedure
+ * @clkctrladdr: Indicates which AGC clock register should be accessed
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_plat_stop_agcfsm(struct ecrnx_plat *ecrnx_plat, int agc_reg,
+                                  u32 *agcctl, u32 *memclk, u8 agc_ver,
+                                  u32 clkctrladdr)
+{
+    /* First read agcctnl and clock registers */
+    *memclk = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+
+    /* Stop state machine : xxAGCCNTL0[AGCFSMRESET]=1 */
+    *agcctl = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, agc_reg);
+    ECRNX_REG_WRITE((*agcctl) | BIT(12), ecrnx_plat, ECRNX_ADDR_SYSTEM, agc_reg);
+
+    /* Force clock */
+    if (agc_ver > 0) {
+        /* CLKGATEFCTRL0[AGCCLKFORCE]=1 */
+        ECRNX_REG_WRITE((*memclk) | BIT(29), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                       clkctrladdr);
+    } else {
+        /* MEMCLKCTRL0[AGCMEMCLKCTRL]=0 */
+        ECRNX_REG_WRITE((*memclk) & ~BIT(3), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                       clkctrladdr);
+    }
+}
+#endif
+
+/**
+ * ecrnx_plat_start_agcfsm() - Restart a AGC state machine
+ *
+ * @ecrnx_plat: pointer to platform structure
+ * @agg_reg: Address of the agccntl register (within ECRNX_ADDR_SYSTEM)
+ * @agcctl: value of the agccntl register to restore
+ * @memclk: value of the clock register to restore
+ * @agc_ver: Version of the AGC load procedure
+ * @clkctrladdr: Indicates which AGC clock register should be accessed
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_plat_start_agcfsm(struct ecrnx_plat *ecrnx_plat, int agc_reg,
+                                   u32 agcctl, u32 memclk, u8 agc_ver,
+                                   u32 clkctrladdr)
+{
+
+    /* Release clock */
+    if (agc_ver > 0)
+        /* CLKGATEFCTRL0[AGCCLKFORCE]=0 */
+        ECRNX_REG_WRITE(memclk & ~BIT(29), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                       clkctrladdr);
+    else
+        /* MEMCLKCTRL0[AGCMEMCLKCTRL]=1 */
+        ECRNX_REG_WRITE(memclk | BIT(3), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                       clkctrladdr);
+
+    /* Restart state machine: xxAGCCNTL0[AGCFSMRESET]=0 */
+    ECRNX_REG_WRITE(agcctl & ~BIT(12), ecrnx_plat, ECRNX_ADDR_SYSTEM, agc_reg);
+}
+#endif
+#endif
+
+/**
+ * ecrnx_plat_get_agc_load_version() - Return the agc load protocol version and the
+ * address of the clock control register
+ *
+ * @ecrnx_plat: platform data
+ * @rf: rf in used
+ * @clkctrladdr: returned clock control register address
+ *
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+#ifndef CONFIG_ECRNX_SDM
+static u8 ecrnx_plat_get_agc_load_version(struct ecrnx_plat *ecrnx_plat, u32 rf,
+                                         u32 *clkctrladdr)
+{
+    u8 agc_load_ver = 0;
+    u32 agc_ver;
+    u32 regval;
+
+    *clkctrladdr = ecrnx_plat_get_clkctrl_addr(ecrnx_plat);
+    /* Trident and Elma PHY use old method */
+    if (rf ==  MDM_PHY_CONFIG_TRIDENT)
+        return 0;
+
+    /* Get the FPGA signature */
+    regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+
+
+    /* Read RIU version register */
+    agc_ver = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, RIU_ECRNXVERSION_ADDR);
+    agc_load_ver = __RIU_AGCLOAD_FROM_VERS(agc_ver);
+
+    return agc_load_ver;
+}
+#endif /* CONFIG_ECRNX_SDM */
+#endif
+
+/**
+ * ecrnx_plat_agc_load() - Load AGC ucode
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_agc_load(struct ecrnx_plat *ecrnx_plat)
+{
+    int ret = 0;
+#ifndef CONFIG_ECRNX_SDM
+    u32 agc = 0, agcctl, memclk;
+    u32 clkctrladdr;
+    u32 rf = ecrnx_plat_get_rf(ecrnx_plat);
+    u8 agc_ver;
+
+    switch (rf) {
+        case MDM_PHY_CONFIG_TRIDENT:
+            agc = AGC_ECRNXAGCCNTL_ADDR;
+            break;
+        case MDM_PHY_CONFIG_CATAXIA:
+        case MDM_PHY_CONFIG_KARST:
+            agc = RIU_ECRNXAGCCNTL_ADDR;
+            break;
+        default:
+            return -1;
+    }
+
+    agc_ver = ecrnx_plat_get_agc_load_version(ecrnx_plat, rf, &clkctrladdr);
+
+    ecrnx_plat_stop_agcfsm(ecrnx_plat, agc, &agcctl, &memclk, agc_ver, clkctrladdr);
+
+    ret = ecrnx_plat_bin_fw_upload(ecrnx_plat,
+                              ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_SYSTEM, PHY_AGC_UCODE_ADDR),
+                              ECRNX_AGC_FW_NAME);
+
+    if (!ret && (agc_ver == 1)) {
+        /* Run BIST to ensure that the AGC RAM was correctly loaded */
+        ECRNX_REG_WRITE(BIT(28), ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                       RIU_ECRNXDYNAMICCONFIG_ADDR);
+        while (ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                             RIU_ECRNXDYNAMICCONFIG_ADDR) & BIT(28));
+
+        if (!(ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                            RIU_AGCMEMBISTSTAT_ADDR) & BIT(0))) {
+            dev_err(ecrnx_platform_get_dev(ecrnx_plat),
+                    "AGC RAM not loaded correctly 0x%08x\n",
+                    ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                                  RIU_AGCMEMSIGNATURESTAT_ADDR));
+            ret = -EIO;
+        }
+    }
+
+    ecrnx_plat_start_agcfsm(ecrnx_plat, agc, agcctl, memclk, agc_ver, clkctrladdr);
+
+#endif
+    return ret;
+}
+#endif
+
+/**
+ * ecrnx_ldpc_load() - Load LDPC RAM
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ldpc_load(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_SDM
+    struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+    u32 rf = ecrnx_plat_get_rf(ecrnx_plat);
+    u32 phy_feat = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, MDM_HDMCONFIG_ADDR);
+    u32 phy_vers = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, MDM_HDMVERSION_ADDR);
+
+    if (((rf !=  MDM_PHY_CONFIG_KARST) && (rf !=  MDM_PHY_CONFIG_CATAXIA)) ||
+        (phy_feat & (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) !=
+        (MDM_LDPCDEC_BIT | MDM_LDPCENC_BIT)) {
+        goto disable_ldpc;
+    }
+    if (__MDM_VERSION(phy_vers) > 30) {
+        return 0;
+    }
+
+    if (ecrnx_plat_bin_fw_upload(ecrnx_plat,
+                            ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_SYSTEM, PHY_LDPC_RAM_ADDR),
+                            ECRNX_LDPC_RAM_NAME)) {
+        goto disable_ldpc;
+    }
+
+    return 0;
+
+  disable_ldpc:
+    ecrnx_hw->mod_params->ldpc_on = false;
+
+#endif /* CONFIG_ECRNX_SDM */
+    return 0;
+}
+#endif
+/**
+ * ecrnx_plat_lmac_load() - Load FW code
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_lmac_load(struct ecrnx_plat *ecrnx_plat)
+{
+    int ret;
+
+    #ifdef CONFIG_ECRNX_TL4
+    ret = ecrnx_plat_tl4_fw_upload(ecrnx_plat,
+                                  ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+                                  ECRNX_MAC_FW_NAME);
+    #else
+    ret = ecrnx_plat_ihex_fw_upload(ecrnx_plat,
+                                   ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+                                   ECRNX_MAC_FW_NAME);
+    if (ret == -ENOENT)
+    {
+        ret = ecrnx_plat_bin_fw_upload(ecrnx_plat,
+                                      ECRNX_ADDR(ecrnx_plat, ECRNX_ADDR_CPU, RAM_LMAC_FW_ADDR),
+                                      ECRNX_MAC_FW_NAME2);
+    }
+    #endif
+
+    return ret;
+}
+#endif
+
+/**
+ * ecrnx_rf_fw_load() - Load RF FW if any
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_plat_rf_fw_load(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_SDM
+    struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+    u32 rf = ecrnx_plat_get_rf(ecrnx_plat);
+    struct device *dev = ecrnx_platform_get_dev(ecrnx_plat);
+    const struct firmware *fw;
+    int err = 0;
+    u8 const *file_data;
+    int remain;
+    u32 clkforce;
+    u32 clkctrladdr;
+
+    // Today only Cataxia has a FW to load
+    if (rf !=  MDM_PHY_CONFIG_CATAXIA)
+        return 0;
+
+    err = request_firmware(&fw, ECRNX_CATAXIA_FW_NAME, dev);
+    if (err)
+    {
+        dev_err(dev, "Make sure your board has up-to-date packages.");
+        dev_err(dev, "Run \"sudo smart update\" \"sudo smart upgrade\" commands.\n");
+        return err;
+    }
+
+    file_data = fw->data;
+    remain = fw->size;
+
+    // Get address of clock control register
+    clkctrladdr = ecrnx_plat_get_clkctrl_addr(ecrnx_plat);
+
+    // Force RC clock
+    clkforce = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+    ECRNX_REG_WRITE(clkforce | BIT(27), ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+    mdelay(1);
+
+    // Reset RC
+    ECRNX_REG_WRITE(0x00003100, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_SYSTEM_CONFIGURATION_ADDR);
+    mdelay(20);
+
+    // Reset RF
+    ECRNX_REG_WRITE(0x00133100, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_SYSTEM_CONFIGURATION_ADDR);
+    mdelay(20);
+
+    // Select trx 2 HB
+    ECRNX_REG_WRITE(0x00103100, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_SYSTEM_CONFIGURATION_ADDR);
+    mdelay(1);
+
+    // Set ASP freeze
+    ECRNX_REG_WRITE(0xC1010001, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_ACCES_TO_CATAXIA_REG_ADDR);
+    mdelay(1);
+
+    /* Walk through all the lines of the FW file */
+    while (remain >= 10) {
+        u32 data;
+
+        if (sscanf(file_data, "0x%08X", &data) != 1)
+        {
+            // Corrupted FW file
+            err = -1;
+            break;
+        }
+        file_data += 11;
+        remain -= 11;
+
+        ECRNX_REG_WRITE(data, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_ACCES_TO_CATAXIA_REG_ADDR);
+        udelay(50);
+    }
+
+    // Clear ASP freeze
+    ECRNX_REG_WRITE(0xE0010011, ecrnx_plat, ECRNX_ADDR_SYSTEM, RC_ACCES_TO_CATAXIA_REG_ADDR);
+    mdelay(1);
+
+    // Unforce RC clock
+    ECRNX_REG_WRITE(clkforce, ecrnx_plat, ECRNX_ADDR_SYSTEM, clkctrladdr);
+
+    release_firmware(fw);
+
+#endif /* CONFIG_ECRNX_SDM */
+    return err;
+}
+#endif
+
+/**
+ * ecrnx_plat_mpif_sel() - Select the MPIF according to the FPGA signature
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_plat_mpif_sel(struct ecrnx_plat *ecrnx_plat)
+{
+#ifndef CONFIG_ECRNX_SDM
+    u32 regval;
+    u32 type;
+
+    /* Get the FPGA signature */
+    regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_SIGNATURE_ADDR);
+    type = __FPGA_TYPE(regval);
+
+    /* Check if we need to switch to the old MPIF or not */
+    if ((type != 0xCAFE) && (type != 0XC0CA) && (regval & 0xF) < 0x3)
+    {
+        /* A old FPGA A is used, so configure the FPGA B to use the old MPIF */
+        ECRNX_REG_WRITE(0x3, ecrnx_plat, ECRNX_ADDR_SYSTEM, FPGAB_MPIF_SEL_ADDR);
+    }
+#endif
+}
+#endif
+
+/**
+ * ecrnx_platform_reset() - Reset the platform
+ *
+ * @ecrnx_plat: platform data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_platform_reset(struct ecrnx_plat *ecrnx_plat)
+{
+    u32 regval;
+
+    /* the doc states that SOFT implies FPGA_B_RESET
+     * adding FPGA_B_RESET is clearer */
+    ECRNX_REG_WRITE(SOFT_RESET | FPGA_B_RESET, ecrnx_plat,
+                   ECRNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+    msleep(100);
+
+    regval = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+
+    if (regval & SOFT_RESET) {
+        dev_err(ecrnx_platform_get_dev(ecrnx_plat), "reset: failed\n");
+        return -EIO;
+    }
+
+    ECRNX_REG_WRITE(regval & ~FPGA_B_RESET, ecrnx_plat,
+                   ECRNX_ADDR_SYSTEM, SYSCTRL_MISC_CNTL_ADDR);
+    msleep(100);
+    return 0;
+}
+#endif
+
+/**
+ * rwmx_platform_save_config() - Save hardware config before reload
+ *
+ * @ecrnx_plat: Pointer to platform data
+ *
+ * Return configuration registers values.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void* ecrnx_term_save_config(struct ecrnx_plat *ecrnx_plat)
+{
+    const u32 *reg_list;
+    u32 *reg_value, *res;
+    int i, size = 0;
+
+    if (ecrnx_plat->get_config_reg) {
+        size = ecrnx_plat->get_config_reg(ecrnx_plat, &reg_list);
+    }
+
+    if (size <= 0)
+        return NULL;
+
+    res = kmalloc(sizeof(u32) * size, GFP_KERNEL);
+    if (!res)
+        return NULL;
+
+    reg_value = res;
+    for (i = 0; i < size; i++) {
+        *reg_value++ = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                                     *reg_list++);
+    }
+
+    return res;
+}
+#endif
+
+/**
+ * rwmx_platform_restore_config() - Restore hardware config after reload
+ *
+ * @ecrnx_plat: Pointer to platform data
+ * @reg_value: Pointer of value to restore
+ * (obtained with rwmx_platform_save_config())
+ *
+ * Restore configuration registers value.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_term_restore_config(struct ecrnx_plat *ecrnx_plat,
+                                     u32 *reg_value)
+{
+    const u32 *reg_list;
+    int i, size = 0;
+
+    if (!reg_value || !ecrnx_plat->get_config_reg)
+        return;
+
+    size = ecrnx_plat->get_config_reg(ecrnx_plat, &reg_list);
+
+    for (i = 0; i < size; i++) {
+        ECRNX_REG_WRITE(*reg_value++, ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                       *reg_list++);
+    }
+}
+#endif
+
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_check_fw_compatibility(struct ecrnx_hw *ecrnx_hw)
+{
+    int res = 0;
+
+    struct ipc_shared_env_tag *shared = ecrnx_hw->ipc_env->shared;
+    #ifdef CONFIG_ECRNX_SOFTMAC
+    struct wiphy *wiphy = ecrnx_hw->hw->wiphy;
+    #else //CONFIG_ECRNX_SOFTMAC
+    struct wiphy *wiphy = ecrnx_hw->wiphy;
+    #endif //CONFIG_ECRNX_SOFTMAC
+    #ifdef CONFIG_ECRNX_OLD_IPC
+    int ipc_shared_version = 10;
+    #else //CONFIG_ECRNX_OLD_IPC
+    int ipc_shared_version = 11;
+    #endif //CONFIG_ECRNX_OLD_IPC
+
+    if(shared->comp_info.ipc_shared_version != ipc_shared_version)
+    {
+        wiphy_err(wiphy, "Different versions of IPC shared version between driver and FW (%d != %d)\n ",
+                  ipc_shared_version, shared->comp_info.ipc_shared_version);
+        res = -1;
+    }
+
+    if(shared->comp_info.radarbuf_cnt != IPC_RADARBUF_CNT)
+    {
+        wiphy_err(wiphy, "Different number of host buffers available for Radar events handling "\
+                  "between driver and FW (%d != %d)\n", IPC_RADARBUF_CNT,
+                  shared->comp_info.radarbuf_cnt);
+        res = -1;
+    }
+
+    if(shared->comp_info.unsuprxvecbuf_cnt != IPC_UNSUPRXVECBUF_CNT)
+    {
+        wiphy_err(wiphy, "Different number of host buffers available for unsupported Rx vectors "\
+                  "handling between driver and FW (%d != %d)\n", IPC_UNSUPRXVECBUF_CNT,
+                  shared->comp_info.unsuprxvecbuf_cnt);
+        res = -1;
+    }
+
+    #ifdef CONFIG_ECRNX_FULLMAC
+    if(shared->comp_info.rxdesc_cnt != IPC_RXDESC_CNT)
+    {
+        wiphy_err(wiphy, "Different number of shared descriptors available for Data RX handling "\
+                  "between driver and FW (%d != %d)\n", IPC_RXDESC_CNT,
+                  shared->comp_info.rxdesc_cnt);
+        res = -1;
+    }
+    #endif /* CONFIG_ECRNX_FULLMAC */
+
+    if(shared->comp_info.rxbuf_cnt != IPC_RXBUF_CNT)
+    {
+        wiphy_err(wiphy, "Different number of host buffers available for Data Rx handling "\
+                  "between driver and FW (%d != %d)\n", IPC_RXBUF_CNT,
+                  shared->comp_info.rxbuf_cnt);
+        res = -1;
+    }
+
+    if(shared->comp_info.msge2a_buf_cnt != IPC_MSGE2A_BUF_CNT)
+    {
+        wiphy_err(wiphy, "Different number of host buffers available for Emb->App MSGs "\
+                  "sending between driver and FW (%d != %d)\n", IPC_MSGE2A_BUF_CNT,
+                  shared->comp_info.msge2a_buf_cnt);
+        res = -1;
+    }
+
+    if(shared->comp_info.dbgbuf_cnt != IPC_DBGBUF_CNT)
+    {
+        wiphy_err(wiphy, "Different number of host buffers available for debug messages "\
+                  "sending between driver and FW (%d != %d)\n", IPC_DBGBUF_CNT,
+                  shared->comp_info.dbgbuf_cnt);
+        res = -1;
+    }
+
+    if(shared->comp_info.bk_txq != NX_TXDESC_CNT0)
+    {
+        wiphy_err(wiphy, "Driver and FW have different sizes of BK TX queue (%d != %d)\n",
+                  NX_TXDESC_CNT0, shared->comp_info.bk_txq);
+        res = -1;
+    }
+
+    if(shared->comp_info.be_txq != NX_TXDESC_CNT1)
+    {
+        wiphy_err(wiphy, "Driver and FW have different sizes of BE TX queue (%d != %d)\n",
+                  NX_TXDESC_CNT1, shared->comp_info.be_txq);
+        res = -1;
+    }
+
+    if(shared->comp_info.vi_txq != NX_TXDESC_CNT2)
+    {
+        wiphy_err(wiphy, "Driver and FW have different sizes of VI TX queue (%d != %d)\n",
+                  NX_TXDESC_CNT2, shared->comp_info.vi_txq);
+        res = -1;
+    }
+
+    if(shared->comp_info.vo_txq != NX_TXDESC_CNT3)
+    {
+        wiphy_err(wiphy, "Driver and FW have different sizes of VO TX queue (%d != %d)\n",
+                  NX_TXDESC_CNT3, shared->comp_info.vo_txq);
+        res = -1;
+    }
+
+    #if NX_TXQ_CNT == 5
+    if(shared->comp_info.bcn_txq != NX_TXDESC_CNT4)
+    {
+        wiphy_err(wiphy, "Driver and FW have different sizes of BCN TX queue (%d != %d)\n",
+                NX_TXDESC_CNT4, shared->comp_info.bcn_txq);
+        res = -1;
+    }
+    #else
+    if (shared->comp_info.bcn_txq > 0)
+    {
+        wiphy_err(wiphy, "BCMC enabled in firmware but disabled in driver\n");
+        res = -1;
+    }
+    #endif /* NX_TXQ_CNT == 5 */
+
+    if(shared->comp_info.ipc_shared_size != sizeof(ipc_shared_env))
+    {
+        wiphy_err(wiphy, "Different sizes of IPC shared between driver and FW (%zd != %d)\n",
+                  sizeof(ipc_shared_env), shared->comp_info.ipc_shared_size);
+        res = -1;
+    }
+
+    if(shared->comp_info.msg_api != MSG_API_VER)
+    {
+        wiphy_err(wiphy, "Different supported message API versions between "\
+                   "driver and FW (%d != %d)\n", MSG_API_VER, shared->comp_info.msg_api);
+        res = -1;
+    }
+
+    return res;
+}
+#endif /* !CONFIG_ECRNX_ESWIN */
+
+/**
+ * ecrnx_platform_on() - Start the platform
+ *
+ * @ecrnx_hw: Main driver data
+ * @config: Config to restore (NULL if nothing to restore)
+ *
+ * It starts the platform :
+ * - load fw and ucodes
+ * - initialize IPC
+ * - boot the fw
+ * - enable link communication/IRQ
+ *
+ * Called by 802.11 part
+ */
+int ecrnx_platform_on(struct ecrnx_hw *ecrnx_hw, void *config)
+{
+    u8 *shared_ram;
+    int ret;
+    
+    ECRNX_DBG("%s entry!!", __func__);
+    shared_ram = kzalloc(sizeof(struct ipc_shared_env_tag), GFP_KERNEL);
+    if (!shared_ram)
+        return -ENOMEM;
+
+    if ((ret = ecrnx_ipc_init(ecrnx_hw, shared_ram)))
+       return ret;
+
+    ECRNX_DBG("%s exit!!", __func__);
+    return 0;
+}
+
+/**
+ * ecrnx_platform_off() - Stop the platform
+ *
+ * @ecrnx_hw: Main driver data
+ * @config: Updated with pointer to config, to be able to restore it with
+ * ecrnx_platform_on(). It's up to the caller to free the config. Set to NULL
+ * if configuration is not needed.
+ *
+ * Called by 802.11 part
+ */
+void ecrnx_platform_off(struct ecrnx_hw *ecrnx_hw, void **config)
+{
+    ecrnx_ipc_deinit(ecrnx_hw);
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+     ecrnx_sdio_deinit(ecrnx_hw);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+    ecrnx_usb_deinit(ecrnx_hw);
+#else
+   #error "config error drv";
+#endif
+}
+
+/**
+ * ecrnx_platform_init() - Initialize the platform
+ *
+ * @ecrnx_plat: platform data (already updated by platform driver)
+ * @platform_data: Pointer to store the main driver data pointer (aka ecrnx_hw)
+ *                That will be set as driver data for the platform driver
+ * Return: 0 on success, < 0 otherwise
+ *
+ * Called by the platform driver after it has been probed
+ */
+int ecrnx_platform_init(void *ecrnx_plat, void **platform_data)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+#if defined CONFIG_ECRNX_SOFTMAC
+    return ecrnx_mac80211_init(ecrnx_plat, platform_data);
+#elif defined CONFIG_ECRNX_FULLMAC
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+       if (amt_mode == true) {
+               return ecrnx_amt_init();
+       }
+       else
+#endif
+    return ecrnx_cfg80211_init(ecrnx_plat, platform_data);
+#elif defined CONFIG_ECRNX_FHOST
+    return ecrnx_fhost_init(ecrnx_plat, platform_data);
+#endif
+}
+
+/**
+ * ecrnx_platform_deinit() - Deinitialize the platform
+ *
+ * @ecrnx_hw: main driver data
+ *
+ * Called by the platform driver after it is removed
+ */
+void ecrnx_platform_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#if defined CONFIG_ECRNX_SOFTMAC
+    ecrnx_mac80211_deinit(ecrnx_hw);
+#elif defined CONFIG_ECRNX_FULLMAC
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+       if (amt_mode == true) {
+               ecrnx_amt_deinit();
+       }
+       else
+#endif
+    ecrnx_cfg80211_deinit(ecrnx_hw);
+#elif defined CONFIG_ECRNX_FHOST
+    ecrnx_fhost_deinit(ecrnx_hw);
+#endif
+}
+
+
+/**
+ * ecrnx_platform_register_drv() - Register all possible platform drivers
+ */
+int ecrnx_platform_register_drv(void)
+{
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+    return ecrnx_sdio_register_drv();
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+    return ecrnx_usb_register_drv();
+#else
+    #error "config error drv"
+#endif
+}
+
+
+/**
+ * ecrnx_platform_unregister_drv() - Unegister all platform drivers
+ */
+void ecrnx_platform_unregister_drv(void)
+{
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+    return ecrnx_sdio_unregister_drv();
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+    return ecrnx_usb_unregister_drv();
+#else
+    #error "config error drv"
+#endif
+}
+
+
+#ifndef CONFIG_ECRNX_SDM
+MODULE_FIRMWARE(ECRNX_AGC_FW_NAME);
+MODULE_FIRMWARE(ECRNX_FCU_FW_NAME);
+MODULE_FIRMWARE(ECRNX_LDPC_RAM_NAME);
+#endif
+MODULE_FIRMWARE(ECRNX_MAC_FW_NAME);
+#ifndef CONFIG_ECRNX_TL4
+MODULE_FIRMWARE(ECRNX_MAC_FW_NAME2);
+#endif
diff --git a/drivers/net/wireless/eswin/ecrnx_platform.h b/drivers/net/wireless/eswin/ecrnx_platform.h
new file mode 100644 (file)
index 0000000..2c84c5a
--- /dev/null
@@ -0,0 +1,142 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_platorm.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_PLAT_H_
+#define _ECRNX_PLAT_H_
+
+#include <linux/pci.h>
+
+#define ECRNX_CONFIG_FW_NAME             "wifi_ecr6600u.cfg"
+#define ECRNX_PHY_CONFIG_TRD_NAME        "ecrnx_trident.ini"
+#define ECRNX_PHY_CONFIG_KARST_NAME      "ecrnx_karst.ini"
+#define ECRNX_AGC_FW_NAME                "agcram.bin"
+#define ECRNX_LDPC_RAM_NAME              "ldpcram.bin"
+#define ECRNX_CATAXIA_FW_NAME            "cataxia.fw"
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define ECRNX_MAC_FW_BASE_NAME           "lmacfw"
+#elif defined CONFIG_ECRNX_FULLMAC
+#define ECRNX_MAC_FW_BASE_NAME           "fmacfw"
+#elif defined CONFIG_ECRNX_FHOST
+#define ECRNX_MAC_FW_BASE_NAME           "fhostfw"
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+#ifdef CONFIG_ECRNX_TL4
+#define ECRNX_MAC_FW_NAME ECRNX_MAC_FW_BASE_NAME".hex"
+#else
+#define ECRNX_MAC_FW_NAME  ECRNX_MAC_FW_BASE_NAME".ihex"
+#define ECRNX_MAC_FW_NAME2 ECRNX_MAC_FW_BASE_NAME".bin"
+#endif
+
+#define ECRNX_FCU_FW_NAME                "fcuram.bin"
+
+/**
+ * Type of memory to access (cf ecrnx_plat.get_address)
+ *
+ * @ECRNX_ADDR_CPU To access memory of the embedded CPU
+ * @ECRNX_ADDR_SYSTEM To access memory/registers of one subsystem of the
+ * embedded system
+ *
+ */
+enum ecrnx_platform_addr {
+    ECRNX_ADDR_CPU,
+    ECRNX_ADDR_SYSTEM,
+    ECRNX_ADDR_MAX,
+};
+
+struct ecrnx_hw;
+
+/**
+ * struct ecrnx_plat - Operation pointers for ECRNX PCI platform
+ *
+ * @pci_dev: pointer to pci dev
+ * @enabled: Set if embedded platform has been enabled (i.e. fw loaded and
+ *          ipc started)
+ * @enable: Configure communication with the fw (i.e. configure the transfers
+ *         enable and register interrupt)
+ * @disable: Stop communication with the fw
+ * @deinit: Free all ressources allocated for the embedded platform
+ * @get_address: Return the virtual address to access the requested address on
+ *              the platform.
+ * @ack_irq: Acknowledge the irq at link level.
+ * @get_config_reg: Return the list (size + pointer) of registers to restore in
+ * order to reload the platform while keeping the current configuration.
+ *
+ * @priv Private data for the link driver
+ */
+struct ecrnx_plat {
+    struct pci_dev *pci_dev;
+    bool enabled;
+
+    int (*enable)(struct ecrnx_hw *ecrnx_hw);
+    int (*disable)(struct ecrnx_hw *ecrnx_hw);
+    void (*deinit)(struct ecrnx_plat *ecrnx_plat);
+    u8* (*get_address)(struct ecrnx_plat *ecrnx_plat, int addr_name,
+                       unsigned int offset);
+    void (*ack_irq)(struct ecrnx_plat *ecrnx_plat);
+    int (*get_config_reg)(struct ecrnx_plat *ecrnx_plat, const u32 **list);
+
+    u8 priv[0] __aligned(sizeof(void *));
+};
+
+#define ECRNX_ADDR(plat, base, offset)           \
+    plat->get_address(plat, base, offset)
+
+#define ECRNX_REG_READ(plat, base, offset)               \
+    readl(plat->get_address(plat, base, offset))
+
+#define ECRNX_REG_WRITE(val, plat, base, offset)         \
+    writel(val, plat->get_address(plat, base, offset))
+
+#ifdef CONFIG_ECRNX_ESWIN
+int ecrnx_platform_init(void *ecrnx_plat, void **platform_data);
+#else
+int ecrnx_platform_init(struct ecrnx_plat *ecrnx_plat, void **platform_data);
+#endif
+void ecrnx_platform_deinit(struct ecrnx_hw *ecrnx_hw);
+
+int ecrnx_platform_on(struct ecrnx_hw *ecrnx_hw, void *config);
+void ecrnx_platform_off(struct ecrnx_hw *ecrnx_hw, void **config);
+
+int ecrnx_platform_register_drv(void);
+void ecrnx_platform_unregister_drv(void);
+
+#ifndef CONFIG_ECRNX_ESWIN
+static inline struct device *ecrnx_platform_get_dev(struct ecrnx_plat *ecrnx_plat)
+{
+    return &(ecrnx_plat->pci_dev->dev);
+}
+
+static inline unsigned int ecrnx_platform_get_irq(struct ecrnx_plat *ecrnx_plat)
+{
+    return ecrnx_plat->pci_dev->irq;
+}
+#else
+
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+extern struct device *eswin_sdio_get_dev(void *plat);
+static inline struct device *ecrnx_platform_get_dev(void *ecrnx_plat)
+{
+    return eswin_sdio_get_dev(ecrnx_plat);
+}
+#endif
+
+
+#ifdef CONFIG_ECRNX_ESWIN_USB
+struct device *eswin_usb_get_dev(void *plat);
+static inline struct device *ecrnx_platform_get_dev(void *ecrnx_plat)
+{
+    return eswin_usb_get_dev(ecrnx_plat);
+}
+#endif
+
+
+#endif
+#endif /* _ECRNX_PLAT_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_prof.h b/drivers/net/wireless/eswin/ecrnx_prof.h
new file mode 100644 (file)
index 0000000..e828fd2
--- /dev/null
@@ -0,0 +1,136 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_prof.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_PROF_H_
+#define _ECRNX_PROF_H_
+
+#include "reg_access.h"
+#include "ecrnx_platform.h"
+
+static inline void ecrnx_prof_set(struct ecrnx_hw *ecrnx_hw, int val)
+{
+    struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+    ECRNX_REG_WRITE(val, ecrnx_plat, ECRNX_ADDR_SYSTEM, NXMAC_SW_SET_PROFILING_ADDR);
+}
+
+static inline void ecrnx_prof_clear(struct ecrnx_hw *ecrnx_hw, int val)
+{
+    struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+    ECRNX_REG_WRITE(val, ecrnx_plat, ECRNX_ADDR_SYSTEM, NXMAC_SW_CLEAR_PROFILING_ADDR);
+}
+
+#if 0
+/* Defines for SW Profiling registers values */
+enum {
+    TX_IPC_IRQ,
+    TX_IPC_EVT,
+    TX_PREP_EVT,
+    TX_DMA_IRQ,
+    TX_MAC_IRQ,
+    TX_PAYL_HDL,
+    TX_CFM_EVT,
+    TX_IPC_CFM,
+    RX_MAC_IRQ,                 // 8
+    RX_TRIGGER_EVT,
+    RX_DMA_IRQ,
+    RX_DMA_EVT,
+    RX_IPC_IND,
+    RX_MPDU_XFER,
+    DBG_PROF_MAX
+};
+#endif
+
+enum {
+    SW_PROF_HOSTBUF_IDX = 12,
+    /****** IPC IRQs related signals ******/
+    /* E2A direction */
+    SW_PROF_IRQ_E2A_RXDESC = 16,    // to make sure we let 16 bits available for LMAC FW
+    SW_PROF_IRQ_E2A_TXCFM,
+    SW_PROF_IRQ_E2A_DBG,
+    SW_PROF_IRQ_E2A_MSG,
+    SW_PROF_IPC_MSGPUSH,
+    SW_PROF_MSGALLOC,
+    SW_PROF_MSGIND,
+    SW_PROF_DBGIND,
+
+    /* A2E direction */
+    SW_PROF_IRQ_A2E_TXCFM_BACK,
+
+    /****** Driver functions related signals ******/
+    SW_PROF_WAIT_QUEUE_STOP,
+    SW_PROF_WAIT_QUEUE_WAKEUP,
+    SW_PROF_ECRNXDATAIND,
+    SW_PROF_ECRNX_IPC_IRQ_HDLR,
+    SW_PROF_ECRNX_IPC_THR_IRQ_HDLR,
+    SW_PROF_IEEE80211RX,
+    SW_PROF_ECRNX_PATTERN,
+    SW_PROF_MAX
+};
+
+// [LT]For debug purpose only
+#if (0)
+#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT       (21)
+#define SW_PROF_CHAN_CTXT_CFM_BIT           (22)
+#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT    (23)
+#define SW_PROF_CHAN_CTXT_PUSH_BIT          (24)
+#define SW_PROF_CHAN_CTXT_QUEUE_BIT         (25)
+#define SW_PROF_CHAN_CTXT_TX_BIT            (26)
+#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT      (27)
+#define SW_PROF_CHAN_CTXT_PSWTCH_BIT        (28)
+#define SW_PROF_CHAN_CTXT_SWTCH_BIT         (29)
+
+// TO DO: update this
+
+#define REG_SW_SET_PROFILING_CHAN(env, bit)             \
+    ecrnx_prof_set((struct ecrnx_hw*)env, BIT(bit))
+
+#define REG_SW_CLEAR_PROFILING_CHAN(env, bit) \
+    ecrnx_prof_clear((struct ecrnx_hw*)env, BIT(bit))
+
+#else
+#define SW_PROF_CHAN_CTXT_CFM_HDL_BIT       (0)
+#define SW_PROF_CHAN_CTXT_CFM_BIT           (0)
+#define SW_PROF_CHAN_CTXT_CFM_SWDONE_BIT    (0)
+#define SW_PROF_CHAN_CTXT_PUSH_BIT          (0)
+#define SW_PROF_CHAN_CTXT_QUEUE_BIT         (0)
+#define SW_PROF_CHAN_CTXT_TX_BIT            (0)
+#define SW_PROF_CHAN_CTXT_TX_PAUSE_BIT      (0)
+#define SW_PROF_CHAN_CTXT_PSWTCH_BIT        (0)
+#define SW_PROF_CHAN_CTXT_SWTCH_BIT         (0)
+
+#define REG_SW_SET_PROFILING_CHAN(env, bit)            do {} while (0)
+#define REG_SW_CLEAR_PROFILING_CHAN(env, bit)          do {} while (0)
+#endif
+
+#ifdef CONFIG_ECRNX_ESWIN
+#undef CONFIG_ECRNX_SW_PROFILING
+#endif
+#ifdef CONFIG_ECRNX_SW_PROFILING
+/* Macros for SW PRofiling registers access */
+#define REG_SW_SET_PROFILING(env, bit)                  \
+    ecrnx_prof_set((struct ecrnx_hw*)env, BIT(bit))
+
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val)      \
+    ecrnx_prof_set((struct ecrnx_hw*)env, val<<(SW_PROF_HOSTBUF_IDX))
+
+#define REG_SW_CLEAR_PROFILING(env, bit)                \
+    ecrnx_prof_clear((struct ecrnx_hw*)env, BIT(bit))
+
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env)                         \
+    ecrnx_prof_clear((struct ecrnx_hw*)env,0x0F<<(SW_PROF_HOSTBUF_IDX))
+
+#else
+#define REG_SW_SET_PROFILING(env, value)            do {} while (0)
+#define REG_SW_CLEAR_PROFILING(env, value)          do {} while (0)
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val)  do {} while (0)
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env)     do {} while (0)
+#endif
+
+#endif /* _ECRNX_PROF_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_radar.c b/drivers/net/wireless/eswin/ecrnx_radar.c
new file mode 100644 (file)
index 0000000..fca6ce8
--- /dev/null
@@ -0,0 +1,1647 @@
+/**
+******************************************************************************
+ *
+ * @file ecrnx_radar.c
+ *
+ * @brief Functions to handle radar detection
+ * Radar detection is copied (and adapted) from ath driver source code.
+ *
+ * Copyright (c) 2012 Neratec Solutions AG
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <net/mac80211.h>
+
+#include "ecrnx_radar.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+
+/*
+ * tolerated deviation of radar time stamp in usecs on both sides
+ * TODO: this might need to be HW-dependent
+ */
+#define PRI_TOLERANCE  16
+
+/**
+ * struct radar_types - contains array of patterns defined for one DFS domain
+ * @domain: DFS regulatory domain
+ * @num_radar_types: number of radar types to follow
+ * @radar_types: radar types array
+ */
+struct radar_types {
+    enum nl80211_dfs_regions region;
+    u32 num_radar_types;
+    const struct radar_detector_specs *spec_riu;
+    const struct radar_detector_specs *spec_fcu;
+};
+
+/**
+ * Type of radar waveform:
+ * RADAR_WAVEFORM_SHORT : waveform defined by
+ *  - pulse width
+ *  - pulse interval in a burst (pri)
+ *  - number of pulses in a burst (ppb)
+ *
+ * RADAR_WAVEFORM_WEATHER :
+ *   same than SHORT except that ppb is dependent of pri
+ *
+ * RADAR_WAVEFORM_INTERLEAVED :
+ *   same than SHORT except there are several value of pri (interleaved)
+ *
+ * RADAR_WAVEFORM_LONG :
+ *
+ */
+enum radar_waveform_type {
+    RADAR_WAVEFORM_SHORT,
+    RADAR_WAVEFORM_WEATHER,
+    RADAR_WAVEFORM_INTERLEAVED,
+    RADAR_WAVEFORM_LONG
+};
+
+/**
+ * struct radar_detector_specs - detector specs for a radar pattern type
+ * @type_id: pattern type, as defined by regulatory
+ * @width_min: minimum radar pulse width in [us]
+ * @width_max: maximum radar pulse width in [us]
+ * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
+ * @pri_max: minimum pri in [us] (including tolerance)
+ * @num_pri: maximum number of different pri for this type
+ * @ppb: pulses per bursts for this type
+ * @ppb_thresh: number of pulses required to trigger detection
+ * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
+ * @type: Type of radar waveform
+ */
+struct radar_detector_specs {
+    u8 type_id;
+    u8 width_min;
+    u8 width_max;
+    u16 pri_min;
+    u16 pri_max;
+    u8 num_pri;
+    u8 ppb;
+    u8 ppb_thresh;
+    u8 max_pri_tolerance;
+    enum radar_waveform_type type;
+};
+
+
+/* percentage on ppb threshold to trigger detection */
+#define MIN_PPB_THRESH  50
+#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
+#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)
+
+/* width tolerance */
+#define WIDTH_TOLERANCE 2
+#define WIDTH_LOWER(X) (X)
+#define WIDTH_UPPER(X) (X)
+
+#define ETSI_PATTERN_SHORT(ID, WMIN, WMAX, PMIN, PMAX, PPB)             \
+    {                                                                   \
+        ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),                       \
+            (PRF2PRI(PMAX) - PRI_TOLERANCE),                            \
+            (PRF2PRI(PMIN) + PRI_TOLERANCE), 1, PPB,                    \
+            PPB_THRESH(PPB), PRI_TOLERANCE,  RADAR_WAVEFORM_SHORT       \
+            }
+
+#define ETSI_PATTERN_INTERLEAVED(ID, WMIN, WMAX, PMIN, PMAX, PRFMIN, PRFMAX, PPB) \
+    {                                                                   \
+        ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),                       \
+            (PRF2PRI(PMAX) * PRFMIN- PRI_TOLERANCE),                    \
+            (PRF2PRI(PMIN) * PRFMAX + PRI_TOLERANCE),                   \
+            PRFMAX, PPB * PRFMAX,                                       \
+            PPB_THRESH(PPB), PRI_TOLERANCE, RADAR_WAVEFORM_INTERLEAVED  \
+            }
+
+/* radar types as defined by ETSI EN-301-893 v1.7.1 */
+static const struct radar_detector_specs etsi_radar_ref_types_v17_riu[] = {
+    ETSI_PATTERN_SHORT(0,  0,  8,  700,  700, 18),
+    ETSI_PATTERN_SHORT(1,  0, 10,  200, 1000, 10),
+    ETSI_PATTERN_SHORT(2,  0, 22,  200, 1600, 15),
+    ETSI_PATTERN_SHORT(3,  0, 22, 2300, 4000, 25),
+    ETSI_PATTERN_SHORT(4, 20, 38, 2000, 4000, 20),
+    ETSI_PATTERN_INTERLEAVED(5,  0,  8,  300,  400, 2, 3, 10),
+    ETSI_PATTERN_INTERLEAVED(6,  0,  8,  400, 1200, 2, 3, 15),
+};
+
+static const struct radar_detector_specs etsi_radar_ref_types_v17_fcu[] = {
+    ETSI_PATTERN_SHORT(0,  0,  8,  700,  700, 18),
+    ETSI_PATTERN_SHORT(1,  0,  8,  200, 1000, 10),
+    ETSI_PATTERN_SHORT(2,  0, 16,  200, 1600, 15),
+    ETSI_PATTERN_SHORT(3,  0, 16, 2300, 4000, 25),
+    ETSI_PATTERN_SHORT(4, 20, 34, 2000, 4000, 20),
+    ETSI_PATTERN_INTERLEAVED(5,  0,  8,  300,  400, 2, 3, 10),
+    ETSI_PATTERN_INTERLEAVED(6,  0,  8,  400, 1200, 2, 3, 15),
+};
+
+static const struct radar_types etsi_radar_types_v17 = {
+    .region          = NL80211_DFS_ETSI,
+    .num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v17_riu),
+    .spec_riu        = etsi_radar_ref_types_v17_riu,
+    .spec_fcu        = etsi_radar_ref_types_v17_fcu,
+};
+
+#define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, TYPE) \
+    {                                                           \
+        ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
+            PMIN - PRI_TOLERANCE,                               \
+            PMAX * PRF + PRI_TOLERANCE, PRF, PPB * PRF,         \
+            PPB_THRESH(PPB), PRI_TOLERANCE, TYPE                \
+            }
+
+static const struct radar_detector_specs fcc_radar_ref_types_riu[] = {
+    FCC_PATTERN(0,  0,   8, 1428, 1428, 1,  18, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(1,  0,   8,  518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
+    FCC_PATTERN(2,  0,   8,  150,  230, 1,  23, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(3,  6,  20,  200,  500, 1,  16, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(4, 10,  28,  200,  500, 1,  12, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(5, 50, 110, 1000, 2000, 1,   8, RADAR_WAVEFORM_LONG),
+    FCC_PATTERN(6,  0,   8,  333,  333, 1,   9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_detector_specs fcc_radar_ref_types_fcu[] = {
+    FCC_PATTERN(0,  0,   8, 1428, 1428, 1,  18, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(1,  0,   8,  518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
+    FCC_PATTERN(2,  0,   8,  150,  230, 1,  23, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(3,  6,  12,  200,  500, 1,  16, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(4, 10,  22,  200,  500, 1,  12, RADAR_WAVEFORM_SHORT),
+    FCC_PATTERN(5, 50, 104, 1000, 2000, 1,   8, RADAR_WAVEFORM_LONG),
+    FCC_PATTERN(6,  0,   8,  333,  333, 1,   9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_types fcc_radar_types = {
+    .region          = NL80211_DFS_FCC,
+    .num_radar_types = ARRAY_SIZE(fcc_radar_ref_types_riu),
+    .spec_riu        = fcc_radar_ref_types_riu,
+    .spec_fcu        = fcc_radar_ref_types_fcu,
+};
+
+#define JP_PATTERN FCC_PATTERN
+static const struct radar_detector_specs jp_radar_ref_types_riu[] = {
+    JP_PATTERN(0,  0,   8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(1,  2,   8, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(2,  0,   8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(3,  0,   8, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(4,  0,   8,  150,  230, 1, 23, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(5,  6,  20,  200,  500, 1, 16, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(6, 10,  28,  200,  500, 1, 12, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(7, 50, 110, 1000, 2000, 1,  8, RADAR_WAVEFORM_LONG),
+    JP_PATTERN(8,  0,   8,  333,  333, 1,  9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_detector_specs jp_radar_ref_types_fcu[] = {
+    JP_PATTERN(0,  0,   8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(1,  2,   6, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(2,  0,   8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(3,  2,   2, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(4,  0,   8,  150,  230, 1, 23, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(5,  6,  12,  200,  500, 1, 16, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(6, 10,  22,  200,  500, 1, 12, RADAR_WAVEFORM_SHORT),
+    JP_PATTERN(7, 50, 104, 1000, 2000, 1,  8, RADAR_WAVEFORM_LONG),
+    JP_PATTERN(8,  0,   8,  333,  333, 1,  9, RADAR_WAVEFORM_SHORT),
+};
+
+static const struct radar_types jp_radar_types = {
+    .region          = NL80211_DFS_JP,
+    .num_radar_types = ARRAY_SIZE(jp_radar_ref_types_riu),
+    .spec_riu        = jp_radar_ref_types_riu,
+    .spec_fcu        = jp_radar_ref_types_fcu,
+};
+
+static const struct radar_types *dfs_domains[] = {
+    &etsi_radar_types_v17,
+    &fcc_radar_types,
+    &jp_radar_types,
+};
+
+
+/**
+ * struct pri_sequence - sequence of pulses matching one PRI
+ * @head: list_head
+ * @pri: pulse repetition interval (PRI) in usecs
+ * @dur: duration of sequence in usecs
+ * @count: number of pulses in this sequence
+ * @count_falses: number of not matching pulses in this sequence
+ * @first_ts: time stamp of first pulse in usecs
+ * @last_ts: time stamp of last pulse in usecs
+ * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
+ * @ppb_thresh: Number of pulses to validate detection
+ *              (need for weather radar whose value depends of pri)
+ */
+struct pri_sequence {
+    struct list_head head;
+    u32 pri;
+    u32 dur;
+    u32 count;
+    u32 count_falses;
+    u64 first_ts;
+    u64 last_ts;
+    u64 deadline_ts;
+    u8 ppb_thresh;
+};
+
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct pulse_elem {
+    struct list_head head;
+    u64 ts;
+};
+
+/**
+ * struct pri_detector - PRI detector element for a dedicated radar type
+ * @head:
+ * @rs: detector specs for this detector element
+ * @last_ts: last pulse time stamp considered for this element in usecs
+ * @sequences: list_head holding potential pulse sequences
+ * @pulses: list connecting pulse_elem objects
+ * @count: number of pulses in queue
+ * @max_count: maximum number of pulses to be queued
+ * @window_size: window size back from newest pulse time stamp in usecs
+ * @freq:
+ */
+struct pri_detector {
+    struct list_head head;
+    const struct radar_detector_specs *rs;
+    u64 last_ts;
+    struct list_head sequences;
+    struct list_head pulses;
+    u32 count;
+    u32 max_count;
+    u32 window_size;
+    struct pri_detector_ops *ops;
+    u16 freq;
+};
+
+/**
+ * struct pri_detector_ops - PRI detector ops (dependent of waveform type)
+ * @init : Initialize pri_detector structure
+ * @add_pulse : Add a pulse to the pri-detector
+ * @reset_on_pri_overflow : Should the pri_detector be resetted when pri overflow
+ */
+struct pri_detector_ops {
+    void (*init)(struct pri_detector *pde);
+    struct pri_sequence * (*add_pulse)(struct pri_detector *pde, u16 len, u64 ts, u16 pri);
+    int reset_on_pri_overflow;
+};
+
+
+/******************************************************************************
+ * PRI (pulse repetition interval) sequence detection
+ *****************************************************************************/
+/**
+ * Singleton Pulse and Sequence Pools
+ *
+ * Instances of pri_sequence and pulse_elem are kept in singleton pools to
+ * reduce the number of dynamic allocations. They are shared between all
+ * instances and grow up to the peak number of simultaneously used objects.
+ *
+ * Memory is freed after all references to the pools are released.
+ */
+static u32 singleton_pool_references;
+static LIST_HEAD(pulse_pool);
+static LIST_HEAD(pseq_pool);
+static DEFINE_SPINLOCK(pool_lock);
+
+static void pool_register_ref(void)
+{
+    spin_lock_bh(&pool_lock);
+    singleton_pool_references++;
+    spin_unlock_bh(&pool_lock);
+}
+
+static void pool_deregister_ref(void)
+{
+    spin_lock_bh(&pool_lock);
+    singleton_pool_references--;
+    if (singleton_pool_references == 0) {
+        /* free singleton pools with no references left */
+        struct pri_sequence *ps, *ps0;
+        struct pulse_elem *p, *p0;
+
+        list_for_each_entry_safe(p, p0, &pulse_pool, head) {
+            list_del(&p->head);
+            kfree(p);
+        }
+        list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
+            list_del(&ps->head);
+            kfree(ps);
+        }
+    }
+    spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pulse_elem(struct pulse_elem *pe)
+{
+    spin_lock_bh(&pool_lock);
+    list_add(&pe->head, &pulse_pool);
+    spin_unlock_bh(&pool_lock);
+}
+
+static void pool_put_pseq_elem(struct pri_sequence *pse)
+{
+    spin_lock_bh(&pool_lock);
+    list_add(&pse->head, &pseq_pool);
+    spin_unlock_bh(&pool_lock);
+}
+
+static struct pri_sequence *pool_get_pseq_elem(void)
+{
+    struct pri_sequence *pse = NULL;
+    spin_lock_bh(&pool_lock);
+    if (!list_empty(&pseq_pool)) {
+        pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
+        list_del(&pse->head);
+    }
+    spin_unlock_bh(&pool_lock);
+
+    if (pse == NULL) {
+        pse = kmalloc(sizeof(*pse), GFP_ATOMIC);
+    }
+
+    return pse;
+}
+
+static struct pulse_elem *pool_get_pulse_elem(void)
+{
+    struct pulse_elem *pe = NULL;
+    spin_lock_bh(&pool_lock);
+    if (!list_empty(&pulse_pool)) {
+        pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
+        list_del(&pe->head);
+    }
+    spin_unlock_bh(&pool_lock);
+    return pe;
+}
+
+static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
+{
+    struct list_head *l = &pde->pulses;
+    if (list_empty(l))
+        return NULL;
+    return list_entry(l->prev, struct pulse_elem, head);
+}
+
+static bool pulse_queue_dequeue(struct pri_detector *pde)
+{
+    struct pulse_elem *p = pulse_queue_get_tail(pde);
+    if (p != NULL) {
+        list_del_init(&p->head);
+        pde->count--;
+        /* give it back to pool */
+        pool_put_pulse_elem(p);
+    }
+    return (pde->count > 0);
+}
+
+/**
+ * pulse_queue_check_window - remove pulses older than window
+ * @pde: pointer on pri_detector
+ *
+ *  dequeue pulse that are too old.
+ */
+static
+void pulse_queue_check_window(struct pri_detector *pde)
+{
+    u64 min_valid_ts;
+    struct pulse_elem *p;
+
+    /* there is no delta time with less than 2 pulses */
+    if (pde->count < 2)
+        return;
+
+    if (pde->last_ts <= pde->window_size)
+        return;
+
+    min_valid_ts = pde->last_ts - pde->window_size;
+    while ((p = pulse_queue_get_tail(pde)) != NULL) {
+        if (p->ts >= min_valid_ts)
+            return;
+        pulse_queue_dequeue(pde);
+    }
+}
+
+/**
+ * pulse_queue_enqueue - Queue one pulse
+ * @pde: pointer on pri_detector
+ *
+ * Add one pulse to the list. If the maximum number of pulses
+ * if reached, remove oldest one.
+ */
+static
+bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
+{
+    struct pulse_elem *p = pool_get_pulse_elem();
+    if (p == NULL) {
+        p = kmalloc(sizeof(*p), GFP_ATOMIC);
+        if (p == NULL) {
+             return false;
+        }
+    }
+    INIT_LIST_HEAD(&p->head);
+    p->ts = ts;
+    list_add(&p->head, &pde->pulses);
+    pde->count++;
+    pde->last_ts = ts;
+    pulse_queue_check_window(pde);
+    if (pde->count >= pde->max_count)
+        pulse_queue_dequeue(pde);
+
+    return true;
+}
+
+
+/***************************************************************************
+ * Short waveform
+ **************************************************************************/
+/**
+ * pde_get_multiple() - get number of multiples considering a given tolerance
+ * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
+ */
+static
+u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
+{
+    u32 remainder;
+    u32 factor;
+    u32 delta;
+
+    if (fraction == 0)
+        return 0;
+
+    delta = (val < fraction) ? (fraction - val) : (val - fraction);
+
+    if (delta <= tolerance)
+        /* val and fraction are within tolerance */
+        return 1;
+
+    factor = val / fraction;
+    remainder = val % fraction;
+    if (remainder > tolerance) {
+        /* no exact match */
+        if ((fraction - remainder) <= tolerance)
+            /* remainder is within tolerance */
+            factor++;
+        else
+            factor = 0;
+    }
+    return factor;
+}
+
+/**
+ * pde_short_create_sequences - create_sequences function for
+ *                              SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ * @ts: timestamp of the pulse
+ * @min_count: Minimum number of pulse to be present in the sequence.
+ *             (With this pulse there is already a sequence with @min_count
+ *              pulse, so if we can't create a sequence with more pulse don't
+ *              create it)
+ * @return: false if an error occured (memory allocation) true otherwise
+ *
+ * For each pulses queued check if we can create a sequence with
+ * pri = (ts - pulse_queued.ts) which contains more than @min_count pulses.
+ *
+ */
+static
+bool pde_short_create_sequences(struct pri_detector *pde,
+                                u64 ts, u32 min_count)
+{
+    struct pulse_elem *p;
+    u16 pulse_idx = 0;
+
+    list_for_each_entry(p, &pde->pulses, head) {
+        struct pri_sequence ps, *new_ps;
+        struct pulse_elem *p2;
+        u32 tmp_false_count;
+        u64 min_valid_ts;
+        u32 delta_ts = ts - p->ts;
+        pulse_idx++;
+
+        if (delta_ts < pde->rs->pri_min)
+            /* ignore too small pri */
+            continue;
+
+        if (delta_ts > pde->rs->pri_max)
+            /* stop on too large pri (sorted list) */
+            break;
+
+        /* build a new sequence with new potential pri */
+        ps.count = 2;
+        ps.count_falses = pulse_idx - 1;
+        ps.first_ts = p->ts;
+        ps.last_ts = ts;
+        ps.pri = ts - p->ts;
+        ps.dur = ps.pri * (pde->rs->ppb - 1)
+            + 2 * pde->rs->max_pri_tolerance;
+
+        p2 = p;
+        tmp_false_count = 0;
+        if (ps.dur > ts)
+            min_valid_ts = 0;
+        else
+            min_valid_ts = ts - ps.dur;
+        /* check which past pulses are candidates for new sequence */
+        list_for_each_entry_continue(p2, &pde->pulses, head) {
+            u32 factor;
+            if (p2->ts < min_valid_ts)
+                /* stop on crossing window border */
+                break;
+            /* check if pulse match (multi)PRI */
+            factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
+                                      pde->rs->max_pri_tolerance);
+            if (factor > 0) {
+                ps.count++;
+                ps.first_ts = p2->ts;
+                /*
+                 * on match, add the intermediate falses
+                 * and reset counter
+                 */
+                ps.count_falses += tmp_false_count;
+                tmp_false_count = 0;
+            } else {
+                /* this is a potential false one */
+                tmp_false_count++;
+            }
+        }
+        if (ps.count <= min_count) {
+            /* did not reach minimum count, drop sequence */
+            continue;
+        }
+        /* this is a valid one, add it */
+        ps.deadline_ts = ps.first_ts + ps.dur;
+        if (pde->rs->type == RADAR_WAVEFORM_WEATHER) {
+            ps.ppb_thresh = 19000000 / (360 * ps.pri);
+            ps.ppb_thresh = PPB_THRESH(ps.ppb_thresh);
+        } else {
+            ps.ppb_thresh = pde->rs->ppb_thresh;
+        }
+
+        new_ps = pool_get_pseq_elem();
+        if (new_ps == NULL) {
+            return false;
+        }
+        memcpy(new_ps, &ps, sizeof(ps));
+        INIT_LIST_HEAD(&new_ps->head);
+        list_add(&new_ps->head, &pde->sequences);
+    }
+    return true;
+}
+
+/**
+ * pde_short_add_to_existing_seqs - add_to_existing_seqs function for
+ *                                  SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ * @ts: timestamp of the pulse
+ *
+ * Check all sequemces created for this pde.
+ *  - If the sequence is too old delete it.
+ *  - Else if the delta with the previous pulse match the pri of the sequence
+ *    add the pulse to this sequence. If the pulse cannot be added it is added
+ *    to the false pulses for this sequence
+ *
+ * @return the length of the longest sequence in which the pulse has been added
+ */
+static
+u32 pde_short_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
+{
+    u32 max_count = 0;
+    struct pri_sequence *ps, *ps2;
+    list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
+        u32 delta_ts;
+        u32 factor;
+
+        /* first ensure that sequence is within window */
+        if (ts > ps->deadline_ts) {
+            list_del_init(&ps->head);
+            pool_put_pseq_elem(ps);
+            continue;
+        }
+
+        delta_ts = ts - ps->last_ts;
+        factor = pde_get_multiple(delta_ts, ps->pri,
+                                  pde->rs->max_pri_tolerance);
+
+        if (factor > 0) {
+            ps->last_ts = ts;
+            ps->count++;
+
+            if (max_count < ps->count)
+                max_count = ps->count;
+        } else {
+            ps->count_falses++;
+        }
+    }
+    return max_count;
+}
+
+
+/**
+ * pde_short_check_detection - check_detection function for
+ *                             SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Check all sequemces created for this pde.
+ *  - If a sequence contains more pulses than the threshold and more matching
+ *    that false pulses.
+ *
+ * @return The first complete sequence, and NULL if no sequence is complete.
+ */
+static
+struct pri_sequence * pde_short_check_detection(struct pri_detector *pde)
+{
+    struct pri_sequence *ps;
+
+    if (list_empty(&pde->sequences))
+        return NULL;
+
+    list_for_each_entry(ps, &pde->sequences, head) {
+        /*
+         * we assume to have enough matching confidence if we
+         * 1) have enough pulses
+         * 2) have more matching than false pulses
+         */
+        if ((ps->count >= ps->ppb_thresh) &&
+            (ps->count * pde->rs->num_pri > ps->count_falses)) {
+            return ps;
+        }
+    }
+    return NULL;
+}
+
+/**
+ * pde_short_init - init function for
+ *                  SHORT/WEATHER/INTERLEAVED radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Initialize pri_detector window size to the maximun size of one burst
+ * for the radar specification associated.
+ */
+static
+void pde_short_init(struct pri_detector *pde)
+{
+    pde->window_size = pde->rs->pri_max * pde->rs->ppb * pde->rs->num_pri;
+    pde->max_count = pde->rs->ppb * 2;
+}
+
+static void pri_detector_reset(struct pri_detector *pde, u64 ts);
+/**
+ *  pde_short_add_pulse - Add pulse to a pri_detector for
+ *                        SHORT/WEATHER/INTERLEAVED radar waveform
+ *
+ * @pde : pointer on pri_detector
+ * @len : width of the pulse
+ * @ts  : timestamp of the pulse received
+ * @pri : Delta in us with the previous pulse.
+ *        (0 means that delta in bigger than 65535 us)
+ *
+ * Process on pulse within this pri_detector
+ * - First try to add it to existing sequence
+ * - Then try to create a new and longest sequence
+ * - Check if this pulse complete a sequence
+ * - If not save this pulse in the list
+ */
+static
+struct pri_sequence *pde_short_add_pulse(struct pri_detector *pde,
+                                         u16 len, u64 ts, u16 pri)
+{
+    u32 max_updated_seq;
+    struct pri_sequence *ps;
+    const struct radar_detector_specs *rs = pde->rs;
+
+    if (pde->count == 0) {
+        /* This is the first pulse after reset, no need to check sequences */
+        pulse_queue_enqueue(pde, ts);
+        return NULL;
+    }
+
+    if ((ts - pde->last_ts) < rs->max_pri_tolerance) {
+        /* if delta to last pulse is too short, don't use this pulse */
+        return NULL;
+    }
+
+    max_updated_seq = pde_short_add_to_existing_seqs(pde, ts);
+
+    if (!pde_short_create_sequences(pde, ts, max_updated_seq)) {
+        pri_detector_reset(pde, ts);
+        return NULL;
+    }
+
+    ps = pde_short_check_detection(pde);
+
+    if (ps == NULL)
+        pulse_queue_enqueue(pde, ts);
+
+    return ps;
+}
+
+
+
+/**
+ * pri detector ops to detect short radar waveform
+ * A Short waveform is defined by :
+ *   The width of pulses.
+ *   The interval between two pulses inside a burst (called pri)
+ *   (some waveform may have or 2/3 interleaved pri)
+ *   The number of pulses per burst (ppb)
+ */
+static struct pri_detector_ops pri_detector_short = {
+    .init = pde_short_init,
+    .add_pulse = pde_short_add_pulse,
+    .reset_on_pri_overflow = 1,
+};
+
+
+/***************************************************************************
+ * Long waveform
+ **************************************************************************/
+#define LONG_RADAR_DURATION 12000000
+#define LONG_RADAR_BURST_MIN_DURATION (12000000 / 20)
+#define LONG_RADAR_MAX_BURST 20
+
+/**
+ * pde_long_init - init function for LONG radar waveform
+ * @pde: pointer on pri_detector
+ *
+ * Initialize pri_detector window size to the long waveform radar
+ * waveform (ie. 12s) and max_count
+ */
+static
+void pde_long_init(struct pri_detector *pde)
+{
+    pde->window_size = LONG_RADAR_DURATION;
+    pde->max_count = LONG_RADAR_MAX_BURST; /* only count burst not pulses */
+}
+
+
+/**
+ *  pde_long_add_pulse - Add pulse to a pri_detector for
+ *                       LONG radar waveform
+ *
+ * @pde : pointer on pri_detector
+ * @len : width of the pulse
+ * @ts  : timestamp of the pulse received
+ * @pri : Delta in us with the previous pulse.
+ *
+ *
+ * For long pulse we only handle one sequence. Since each burst
+ * have a different set of parameters (number of pulse, pri) than
+ * the previous one we only use pulse width to add the pulse in the
+ * sequence.
+ * We only queue one pulse per burst and valid the radar when enough burst
+ * has been detected.
+ */
+static
+struct pri_sequence *pde_long_add_pulse(struct pri_detector *pde,
+                                        u16 len, u64 ts, u16 pri)
+{
+    struct pri_sequence *ps;
+    const struct radar_detector_specs *rs = pde->rs;
+
+    if (list_empty(&pde->sequences)) {
+        /* First pulse, create a new sequence */
+        ps = pool_get_pseq_elem();
+        if (ps == NULL) {
+            return NULL;
+        }
+
+        /*For long waveform, "count" represents the number of burst detected */
+        ps->count = 1;
+        /*"count_false" represents the number of pulse in the current burst */
+        ps->count_falses = 1;
+        ps->first_ts = ts;
+        ps->last_ts = ts;
+        ps->deadline_ts = ts + pde->window_size;
+        ps->pri = 0;
+        INIT_LIST_HEAD(&ps->head);
+        list_add(&ps->head, &pde->sequences);
+        pulse_queue_enqueue(pde, ts);
+    } else {
+        u32 delta_ts;
+
+        ps = (struct pri_sequence *)pde->sequences.next;
+
+        delta_ts = ts - ps->last_ts;
+        ps->last_ts = ts;
+
+        if (delta_ts < rs->pri_max) {
+            /* ignore pulse too close from previous one */
+        } else if  ((delta_ts >= rs->pri_min) &&
+              (delta_ts <= rs->pri_max)) {
+            /* this is a new pulse in the current burst, ignore it
+               (i.e don't queue it) */
+            ps->count_falses++;
+        } else if ((ps->count > 2) &&
+                   (ps->dur + delta_ts) < LONG_RADAR_BURST_MIN_DURATION) {
+            /* not enough time between burst, ignore pulse */
+        } else {
+            /* a new burst */
+            ps->count++;
+            ps->count_falses = 1;
+
+            /* reset the start of the sequence if deadline reached */
+            if (ts > ps->deadline_ts) {
+                struct pulse_elem *p;
+                u64 min_valid_ts;
+
+                min_valid_ts = ts - pde->window_size;
+                while ((p = pulse_queue_get_tail(pde)) != NULL) {
+                    if (p->ts >= min_valid_ts) {
+                        ps->first_ts = p->ts;
+                        ps->deadline_ts = p->ts + pde->window_size;
+                        break;
+                    }
+                    pulse_queue_dequeue(pde);
+                    ps->count--;
+                }
+            }
+
+            /* valid radar if enough burst detected and delta with first burst
+               is at least duration/2 */
+            if (ps->count > pde->rs->ppb_thresh &&
+                (ts - ps->first_ts) > (pde->window_size / 2)) {
+                return ps;
+            } else {
+                pulse_queue_enqueue(pde, ts);
+                ps->dur = delta_ts;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * pri detector ops to detect long radar waveform
+ */
+static struct pri_detector_ops pri_detector_long = {
+    .init = pde_long_init,
+    .add_pulse = pde_long_add_pulse,
+    .reset_on_pri_overflow = 0,
+};
+
+
+/***************************************************************************
+ * PRI detector init/reset/exit/get
+ **************************************************************************/
+/**
+ * pri_detector_init- Create a new pri_detector
+ *
+ * @dpd: dfs_pattern_detector instance pointer
+ * @radar_type: index of radar pattern
+ * @freq: Frequency of the pri detector
+ */
+struct pri_detector *pri_detector_init(struct dfs_pattern_detector *dpd,
+                                       u16 radar_type, u16 freq)
+{
+    struct pri_detector *pde;
+
+    pde = kzalloc(sizeof(*pde), GFP_ATOMIC);
+    if (pde == NULL)
+        return NULL;
+
+    INIT_LIST_HEAD(&pde->sequences);
+    INIT_LIST_HEAD(&pde->pulses);
+    INIT_LIST_HEAD(&pde->head);
+    list_add(&pde->head, &dpd->detectors[radar_type]);
+
+    pde->rs = &dpd->radar_spec[radar_type];
+    pde->freq = freq;
+
+    if (pde->rs->type == RADAR_WAVEFORM_LONG) {
+        /* for LONG WAVEFORM */
+        pde->ops = &pri_detector_long;
+    } else {
+        /* for SHORT, WEATHER and INTERLEAVED */
+        pde->ops = &pri_detector_short;
+    }
+
+    /* Init dependent of specs */
+    pde->ops->init(pde);
+
+    pool_register_ref();
+    return pde;
+}
+
+/**
+ * pri_detector_reset - Reset pri_detector
+ *
+ * @pde: pointer on pri_detector
+ * @ts: New ts reference for the pri_detector
+ *
+ * free pulse queue and sequences list and give objects back to pools
+ */
+static
+void pri_detector_reset(struct pri_detector *pde, u64 ts)
+{
+    struct pri_sequence *ps, *ps0;
+    struct pulse_elem *p, *p0;
+    list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
+        list_del_init(&ps->head);
+        pool_put_pseq_elem(ps);
+    }
+    list_for_each_entry_safe(p, p0, &pde->pulses, head) {
+        list_del_init(&p->head);
+        pool_put_pulse_elem(p);
+    }
+    pde->count = 0;
+    pde->last_ts = ts;
+}
+
+/**
+ *  pri_detector_exit - Delete pri_detector
+ *
+ *  @pde: pointer on pri_detector
+ */
+static
+void pri_detector_exit(struct pri_detector *pde)
+{
+    pri_detector_reset(pde, 0);
+    pool_deregister_ref();
+    list_del(&pde->head);
+    kfree(pde);
+}
+
+/**
+ * pri_detector_get() - get pri detector for a given frequency and type
+ * @dpd: dfs_pattern_detector instance pointer
+ * @freq: frequency in MHz
+ * @radar_type: index of radar pattern
+ * @return pointer to pri detector on success, NULL otherwise
+ *
+ * Return existing pri detector for the given frequency or return a
+ * newly create one.
+ * Pri detector are "merged" by frequency so that if a pri detector for a freq
+ * of +/- 2Mhz already exists don't create a new one.
+ *
+ * Maybe will need to adapt frequency merge for pattern with chirp.
+ */
+static struct pri_detector *
+pri_detector_get(struct dfs_pattern_detector *dpd, u16 freq, u16 radar_type)
+{
+    struct pri_detector *pde, *cur = NULL;
+    list_for_each_entry(pde, &dpd->detectors[radar_type], head) {
+        if (pde->freq == freq) {
+            if (pde->count)
+                return pde;
+            else
+                cur = pde;
+        } else if (pde->freq - 2 == freq && pde->count) {
+            return pde;
+        } else if (pde->freq + 2 == freq && pde->count) {
+            return pde;
+        }
+    }
+
+    if (cur)
+        return cur;
+    else
+        return pri_detector_init(dpd, radar_type, freq);
+}
+
+
+/******************************************************************************
+ * DFS Pattern Detector
+ *****************************************************************************/
+/**
+ * dfs_pattern_detector_reset() - reset all channel detectors
+ *
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_reset(struct dfs_pattern_detector *dpd)
+{
+    struct pri_detector *pde;
+    int i;
+
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        if (!list_empty(&dpd->detectors[i]))
+            list_for_each_entry(pde, &dpd->detectors[i], head)
+                pri_detector_reset(pde, dpd->last_pulse_ts);
+    }
+
+    dpd->last_pulse_ts = 0;
+    dpd->prev_jiffies = jiffies;
+}
+
+/**
+ * dfs_pattern_detector_reset() - delete all channel detectors
+ *
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_exit(struct dfs_pattern_detector *dpd)
+{
+    struct pri_detector *pde, *pde0;
+    int i;
+
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        if (!list_empty(&dpd->detectors[i]))
+            list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
+                pri_detector_exit(pde);
+    }
+
+    kfree(dpd);
+}
+
+/**
+ * dfs_pattern_detector_pri_overflow - reset all channel detectors on pri
+ *                                     overflow
+ * @dpd: dfs_pattern_detector
+ */
+static void dfs_pattern_detector_pri_overflow(struct dfs_pattern_detector *dpd)
+{
+    struct pri_detector *pde;
+    int i;
+
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        if (!list_empty(&dpd->detectors[i]))
+            list_for_each_entry(pde, &dpd->detectors[i], head)
+                if (pde->ops->reset_on_pri_overflow)
+                    pri_detector_reset(pde, dpd->last_pulse_ts);
+    }
+}
+
+/**
+ * dfs_pattern_detector_add_pulse - Process one pulse
+ *
+ * @dpd: dfs_pattern_detector
+ * @chain: Chain that correspond to this pattern_detector (only for debug)
+ * @freq: frequency of the pulse
+ * @pri: Delta with previous pulse. (0 if delta is too big for u16)
+ * @len: width of the pulse
+ * @now: jiffies value when pulse was received
+ *
+ * Get (or create) the channel_detector for this frequency. Then add the pulse
+ * in each pri_detector created in this channel_detector.
+ *
+ *
+ * @return True is the pulse complete a radar pattern, false otherwise
+ */
+static bool dfs_pattern_detector_add_pulse(struct dfs_pattern_detector *dpd,
+                                           enum ecrnx_radar_chain chain,
+                                           u16 freq, u16 pri, u16 len, u32 now)
+{
+    u32 i;
+
+    /*
+     * pulses received for a non-supported or un-initialized
+     * domain are treated as detected radars for fail-safety
+     */
+    if (dpd->region == NL80211_DFS_UNSET)
+        return true;
+
+    /* Compute pulse time stamp */
+    if (pri == 0) {
+        u32 delta_jiffie;
+        if (unlikely(now < dpd->prev_jiffies)) {
+            delta_jiffie = 0xffffffff - dpd->prev_jiffies + now;
+        } else {
+            delta_jiffie = now - dpd->prev_jiffies;
+        }
+        dpd->last_pulse_ts += jiffies_to_usecs(delta_jiffie);
+        dpd->prev_jiffies = now;
+        dfs_pattern_detector_pri_overflow(dpd);
+    } else {
+        dpd->last_pulse_ts += pri;
+    }
+
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        struct pri_sequence *ps;
+        struct pri_detector *pde;
+        const struct radar_detector_specs *rs = &dpd->radar_spec[i];
+
+        /* no need to look up for pde if len is not within range */
+        if ((rs->width_min > len) ||
+            (rs->width_max < len)) {
+            continue;
+        }
+
+        pde = pri_detector_get(dpd, freq, i);
+        ps = pde->ops->add_pulse(pde, len, dpd->last_pulse_ts, pri);
+
+        if (ps != NULL) {
+            trace_radar_detected(chain, dpd->region, pde->freq, i, ps->pri);
+            // reset everything instead of just the channel detector
+            dfs_pattern_detector_reset(dpd);
+            return true;
+        }
+    }
+
+    return false;
+}
+
+/**
+ * get_dfs_domain_radar_types() - get radar types for a given DFS domain
+ * @param domain DFS domain
+ * @return radar_types ptr on success, NULL if DFS domain is not supported
+ */
+static const struct radar_types *
+get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
+{
+    u32 i;
+    for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+        if (dfs_domains[i]->region == region)
+            return dfs_domains[i];
+    }
+    return NULL;
+}
+
+/**
+ * get_dfs_max_radar_types() - get maximum radar types for all supported domain
+ * @return the maximum number of radar pattern supported by on region
+ */
+static u16 get_dfs_max_radar_types(void)
+{
+    u32 i;
+    u16 max = 0;
+    for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
+        if (dfs_domains[i]->num_radar_types > max)
+            max = dfs_domains[i]->num_radar_types;
+    }
+    return max;
+}
+
+/**
+ * dfs_pattern_detector_set_domain - set DFS domain
+ *
+ * @dpd: dfs_pattern_detector
+ * @region: DFS region
+ *
+ * set DFS domain, resets detector lines upon domain changes
+ */
+static
+bool dfs_pattern_detector_set_domain(struct dfs_pattern_detector *dpd,
+                                     enum nl80211_dfs_regions region, u8 chain)
+{
+    const struct radar_types *rt;
+    struct pri_detector *pde, *pde0;
+    int i;
+
+    if (dpd->region == region)
+        return true;
+
+    dpd->region = NL80211_DFS_UNSET;
+
+    rt = get_dfs_domain_radar_types(region);
+    if (rt == NULL)
+        return false;
+
+    /* delete all pri detectors for previous DFS domain */
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        if (!list_empty(&dpd->detectors[i]))
+            list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
+                pri_detector_exit(pde);
+    }
+
+    if (chain == ECRNX_RADAR_RIU)
+        dpd->radar_spec = rt->spec_riu;
+    else
+        dpd->radar_spec = rt->spec_fcu;
+    dpd->num_radar_types = rt->num_radar_types;
+
+    dpd->region = region;
+    return true;
+}
+
+/**
+ * dfs_pattern_detector_init - Initialize dfs_pattern_detector
+ *
+ * @region: DFS region
+ * @return: pointer on dfs_pattern_detector
+ *
+ */
+static struct dfs_pattern_detector *
+dfs_pattern_detector_init(enum nl80211_dfs_regions region, u8 chain)
+{
+    struct dfs_pattern_detector *dpd;
+    u16 i, max_radar_type = get_dfs_max_radar_types();
+
+    dpd = kmalloc(sizeof(*dpd) + max_radar_type * sizeof(dpd->detectors[0]),
+                  GFP_KERNEL);
+    if (dpd == NULL)
+        return NULL;
+
+    dpd->region = NL80211_DFS_UNSET;
+    dpd->enabled = ECRNX_RADAR_DETECT_DISABLE;
+    dpd->last_pulse_ts = 0;
+    dpd->prev_jiffies = jiffies;
+    dpd->num_radar_types = 0;
+    for (i = 0; i < max_radar_type; i++)
+        INIT_LIST_HEAD(&dpd->detectors[i]);
+
+    if (dfs_pattern_detector_set_domain(dpd, region, chain))
+        return dpd;
+
+    kfree(dpd);
+    return NULL;
+}
+
+
+/******************************************************************************
+ * driver interface
+ *****************************************************************************/
+static u16 ecrnx_radar_get_center_freq(struct ecrnx_hw *ecrnx_hw, u8 chain)
+{
+    if (chain == ECRNX_RADAR_FCU)
+        return ecrnx_hw->phy.sec_chan.center1_freq;
+
+    if (chain == ECRNX_RADAR_RIU) {
+#ifdef CONFIG_ECRNX_SOFTMAC
+        return ecrnx_hw->cur_freq;
+#else
+        if (!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_hw->cur_chanctx)) {
+            WARN(1, "Radar pulse without channel information");
+        } else
+            return ecrnx_hw->chanctx_table[ecrnx_hw->cur_chanctx].chan_def.center_freq1;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+    }
+
+    return 0;
+}
+
+static void ecrnx_radar_detected(struct ecrnx_hw *ecrnx_hw)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    ieee80211_radar_detected(ecrnx_hw->hw);
+#else
+    struct cfg80211_chan_def chan_def;
+
+    if (!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_hw->cur_chanctx)) {
+        WARN(1, "Radar detected without channel information");
+        return;
+    }
+
+    /*
+      recopy chan_def in local variable because ecrnx_radar_cancel_cac may
+      clean the variable (if in CAC and it's the only vif using this context)
+      and CAC should be aborted before reporting the radar.
+    */
+    chan_def = ecrnx_hw->chanctx_table[ecrnx_hw->cur_chanctx].chan_def;
+
+    ecrnx_radar_cancel_cac(&ecrnx_hw->radar);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    cfg80211_radar_event(ecrnx_hw->wiphy, &chan_def, GFP_KERNEL);
+#endif
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+static void ecrnx_radar_process_pulse(struct work_struct *ws)
+{
+    struct ecrnx_radar *radar = container_of(ws, struct ecrnx_radar,
+                                            detection_work);
+    struct ecrnx_hw *ecrnx_hw = container_of(radar, struct ecrnx_hw, radar);
+    int chain;
+    u32 pulses[ECRNX_RADAR_LAST][ECRNX_RADAR_PULSE_MAX];
+    u16 pulses_count[ECRNX_RADAR_LAST];
+    u32 now = jiffies; /* would be better to store jiffies value in IT handler */
+
+    /* recopy pulses locally to avoid too long spin_lock */
+    spin_lock_bh(&radar->lock);
+    for (chain = ECRNX_RADAR_RIU; chain < ECRNX_RADAR_LAST; chain++) {
+        int start, count;
+
+        count = radar->pulses[chain].count;
+        start = radar->pulses[chain].index - count;
+        if (start < 0)
+            start += ECRNX_RADAR_PULSE_MAX;
+
+        pulses_count[chain] = count;
+        if (count == 0)
+            continue;
+
+        if ((start + count) > ECRNX_RADAR_PULSE_MAX) {
+            u16 count1 = (ECRNX_RADAR_PULSE_MAX - start);
+            memcpy(&(pulses[chain][0]),
+                   &(radar->pulses[chain].buffer[start]),
+                   count1 * sizeof(struct radar_pulse));
+            memcpy(&(pulses[chain][count1]),
+                   &(radar->pulses[chain].buffer[0]),
+                   (count - count1) * sizeof(struct radar_pulse));
+        } else {
+            memcpy(&(pulses[chain][0]),
+                   &(radar->pulses[chain].buffer[start]),
+                   count * sizeof(struct radar_pulse));
+        }
+        radar->pulses[chain].count = 0;
+    }
+    spin_unlock_bh(&radar->lock);
+
+
+    /* now process pulses */
+    for (chain = ECRNX_RADAR_RIU; chain < ECRNX_RADAR_LAST; chain++) {
+        int i;
+        u16 freq;
+
+        if (pulses_count[chain] == 0)
+            continue;
+
+        freq = ecrnx_radar_get_center_freq(ecrnx_hw, chain);
+
+        for (i = 0; i < pulses_count[chain] ; i++) {
+            struct radar_pulse *p = (struct radar_pulse *)&pulses[chain][i];
+            trace_radar_pulse(chain, p);
+            if (dfs_pattern_detector_add_pulse(radar->dpd[chain], chain,
+                                               (s16)freq + (2 * p->freq),
+                                               p->rep, (p->len * 2), now)) {
+                u16 idx = radar->detected[chain].index;
+
+                if (chain == ECRNX_RADAR_RIU) {
+                    /* operating chain, inform upper layer to change channel */
+                    if (radar->dpd[chain]->enabled == ECRNX_RADAR_DETECT_REPORT) {
+                        ecrnx_radar_detected(ecrnx_hw);
+                        /* no need to report new radar until upper layer set a
+                           new channel. This prevent warning if a new radar is
+                           detected while mac80211 is changing channel */
+                        ecrnx_radar_detection_enable(radar,
+                                                    ECRNX_RADAR_DETECT_DISABLE,
+                                                    chain);
+                        /* purge any event received since the beginning of the
+                           function (we are sure not to interfer with tasklet
+                           as we disable detection just before) */
+                        radar->pulses[chain].count = 0;
+                    }
+                } else {
+                    /* secondary radar detection chain, simply report info in
+                       debugfs for now */
+                }
+
+                radar->detected[chain].freq[idx] = (s16)freq + (2 * p->freq);
+                radar->detected[chain].time[idx] = ktime_get_real_seconds();
+                radar->detected[chain].index = ((idx + 1 ) %
+                                                NX_NB_RADAR_DETECTED);
+                radar->detected[chain].count++;
+                /* no need to process next pulses for this chain */
+                break;
+             }
+        }
+    }
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+static void ecrnx_radar_cac_work(struct work_struct *ws)
+{
+    struct delayed_work *dw = container_of(ws, struct delayed_work, work);
+    struct ecrnx_radar *radar = container_of(dw, struct ecrnx_radar, cac_work);
+    struct ecrnx_hw *ecrnx_hw = container_of(radar, struct ecrnx_hw, radar);
+    struct ecrnx_chanctx *ctxt;
+
+    if (radar->cac_vif == NULL) {
+        WARN(1, "CAC finished but no vif set");
+        return;
+    }
+
+    ctxt = &ecrnx_hw->chanctx_table[radar->cac_vif->ch_index];
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    cfg80211_cac_event(radar->cac_vif->ndev, 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+                        &ctxt->chan_def,
+#endif
+                       NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+#endif
+    ecrnx_send_apm_stop_cac_req(ecrnx_hw, radar->cac_vif);
+    ecrnx_chanctx_unlink(radar->cac_vif);
+
+    radar->cac_vif = NULL;
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+bool ecrnx_radar_detection_init(struct ecrnx_radar *radar)
+{
+    spin_lock_init(&radar->lock);
+
+    radar->dpd[ECRNX_RADAR_RIU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
+                                                           ECRNX_RADAR_RIU);
+    if (radar->dpd[ECRNX_RADAR_RIU] == NULL)
+        return false;
+
+    radar->dpd[ECRNX_RADAR_FCU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
+                                                           ECRNX_RADAR_FCU);
+    if (radar->dpd[ECRNX_RADAR_FCU] == NULL) {
+        ecrnx_radar_detection_deinit(radar);
+        return false;
+    }
+
+    INIT_WORK(&radar->detection_work, ecrnx_radar_process_pulse);
+#ifdef CONFIG_ECRNX_FULLMAC
+    INIT_DELAYED_WORK(&radar->cac_work, ecrnx_radar_cac_work);
+    radar->cac_vif = NULL;
+#endif /* CONFIG_ECRNX_FULLMAC */
+    return true;
+}
+
+void ecrnx_radar_detection_deinit(struct ecrnx_radar *radar)
+{
+    if (radar->dpd[ECRNX_RADAR_RIU]) {
+        dfs_pattern_detector_exit(radar->dpd[ECRNX_RADAR_RIU]);
+        radar->dpd[ECRNX_RADAR_RIU] = NULL;
+    }
+    if (radar->dpd[ECRNX_RADAR_FCU]) {
+        dfs_pattern_detector_exit(radar->dpd[ECRNX_RADAR_FCU]);
+        radar->dpd[ECRNX_RADAR_FCU] = NULL;
+    }
+}
+
+bool ecrnx_radar_set_domain(struct ecrnx_radar *radar,
+                           enum nl80211_dfs_regions region)
+{
+    if (radar->dpd[0] == NULL)
+        return false;
+
+    trace_radar_set_region(region);
+
+    return (dfs_pattern_detector_set_domain(radar->dpd[ECRNX_RADAR_RIU],
+                                            region, ECRNX_RADAR_RIU) &&
+            dfs_pattern_detector_set_domain(radar->dpd[ECRNX_RADAR_FCU],
+                                            region, ECRNX_RADAR_FCU));
+}
+
+void ecrnx_radar_detection_enable(struct ecrnx_radar *radar, u8 enable, u8 chain)
+{
+    if (chain < ECRNX_RADAR_LAST ) {
+        trace_radar_enable_detection(radar->dpd[chain]->region, enable, chain);
+        spin_lock_bh(&radar->lock);
+        radar->dpd[chain]->enabled = enable;
+        spin_unlock_bh(&radar->lock);
+    }
+}
+
+bool ecrnx_radar_detection_is_enable(struct ecrnx_radar *radar, u8 chain)
+{
+    return radar->dpd[chain]->enabled != ECRNX_RADAR_DETECT_DISABLE;
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+void ecrnx_radar_start_cac(struct ecrnx_radar *radar, u32 cac_time_ms,
+                          struct ecrnx_vif *vif)
+{
+    WARN(radar->cac_vif != NULL, "CAC already in progress");
+    radar->cac_vif = vif;
+    schedule_delayed_work(&radar->cac_work, msecs_to_jiffies(cac_time_ms));
+}
+
+void ecrnx_radar_cancel_cac(struct ecrnx_radar *radar)
+{
+    struct ecrnx_hw *ecrnx_hw = container_of(radar, struct ecrnx_hw, radar);
+
+    if (radar->cac_vif == NULL) {
+        return;
+    }
+
+    if (cancel_delayed_work(&radar->cac_work)) {
+        struct ecrnx_chanctx *ctxt;
+        ctxt = &ecrnx_hw->chanctx_table[radar->cac_vif->ch_index];
+        ecrnx_send_apm_stop_cac_req(ecrnx_hw, radar->cac_vif);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+        cfg80211_cac_event(radar->cac_vif->ndev, 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
+                           &ctxt->chan_def,
+#endif
+                           NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+#endif
+        ecrnx_chanctx_unlink(radar->cac_vif);
+    }
+
+    radar->cac_vif = NULL;
+}
+
+void ecrnx_radar_detection_enable_on_cur_channel(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_chanctx *ctxt;
+
+    /* If no information on current channel do nothing */
+    if (!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_hw->cur_chanctx))
+        return;
+
+    ctxt = &ecrnx_hw->chanctx_table[ecrnx_hw->cur_chanctx];
+    if (ctxt->chan_def.chan->flags & IEEE80211_CHAN_RADAR) {
+        ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+                                    ECRNX_RADAR_DETECT_REPORT,
+                                    ECRNX_RADAR_RIU);
+    } else {
+        ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+                                    ECRNX_RADAR_DETECT_DISABLE,
+                                    ECRNX_RADAR_RIU);
+    }
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/*****************************************************************************
+ * Debug functions
+ *****************************************************************************/
+static
+int ecrnx_radar_dump_pri_detector(char *buf, size_t len,
+                                 struct pri_detector *pde)
+{
+    char freq_info[] = "Freq = %3.dMhz\n";
+    char seq_info[] = " pri    | count | false \n";
+    struct pri_sequence *seq;
+    int res, write = 0;
+
+    if (list_empty(&pde->sequences)) {
+        return 0;
+    }
+
+    if (buf == NULL) {
+        int nb_seq = 1;
+        list_for_each_entry(seq, &pde->sequences, head) {
+            nb_seq++;
+        }
+
+        return (sizeof(freq_info) + nb_seq * sizeof(seq_info));
+    }
+
+    res = scnprintf(buf, len, freq_info, pde->freq);
+    write += res;
+    len -= res;
+
+    res = scnprintf(&buf[write], len, "%s", seq_info);
+    write += res;
+    len -= res;
+
+    list_for_each_entry(seq, &pde->sequences, head) {
+        res = scnprintf(&buf[write], len, " %6.d |   %2.d  |    %.2d \n",
+                        seq->pri, seq->count, seq->count_falses);
+        write += res;
+        len -= res;
+    }
+
+    return write;
+}
+
+int ecrnx_radar_dump_pattern_detector(char *buf, size_t len,
+                                     struct ecrnx_radar *radar, u8 chain)
+{
+    struct dfs_pattern_detector *dpd = radar->dpd[chain];
+    char info[] = "Type = %3.d\n";
+    struct pri_detector *pde;
+    int i, res, write = 0;
+
+    /* if buf is NULL return size needed for dump */
+    if (buf == NULL) {
+        int size_needed = 0;
+
+        for (i = 0; i < dpd->num_radar_types; i++) {
+            list_for_each_entry(pde, &dpd->detectors[i], head) {
+                size_needed += ecrnx_radar_dump_pri_detector(NULL, 0, pde);
+            }
+            size_needed += sizeof(info);
+
+        return size_needed;
+        }
+    }
+
+    /* */
+    for (i = 0; i < dpd->num_radar_types; i++) {
+        res = scnprintf(&buf[write], len, info, i);
+
+        write += res;
+        len -= res;
+        list_for_each_entry(pde, &dpd->detectors[i], head) {
+            res = ecrnx_radar_dump_pri_detector(&buf[write], len, pde);
+            write += res;
+            len -= res;
+        }
+    }
+
+    return write;
+}
+
+
+int ecrnx_radar_dump_radar_detected(char *buf, size_t len,
+                                   struct ecrnx_radar *radar, u8 chain)
+{
+    struct ecrnx_radar_detected *detect = &(radar->detected[chain]);
+    char info[] = "2001/02/02 - 02:20 5126MHz\n";
+    int idx, i, res, write = 0;
+    int count = detect->count;
+
+    if (count > NX_NB_RADAR_DETECTED)
+        count = NX_NB_RADAR_DETECTED;
+
+    if (buf == NULL) {
+        return (count * sizeof(info)) + 1;
+     }
+
+    idx = (detect->index - detect->count) % NX_NB_RADAR_DETECTED;
+
+    for (i = 0; i < count; i++) {
+        struct tm tm;
+        time64_to_tm(detect->time[idx], 0, &tm);
+
+        res = scnprintf(&buf[write], len,
+                        "%.4d/%.2d/%.2d - %.2d:%.2d %4.4dMHz\n",
+                        (int)tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+                        tm.tm_hour, tm.tm_min, detect->freq[idx]);
+        write += res;
+        len -= res;
+
+        idx = (idx + 1 ) % NX_NB_RADAR_DETECTED;
+    }
+
+    return write;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_radar.h b/drivers/net/wireless/eswin/ecrnx_radar.h
new file mode 100644 (file)
index 0000000..78a9d1d
--- /dev/null
@@ -0,0 +1,160 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_radar.h
+ *
+ * @brief Functions to handle radar detection
+ *
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _ECRNX_RADAR_H_
+#define _ECRNX_RADAR_H_
+
+#include <linux/nl80211.h>
+
+struct ecrnx_vif;
+struct ecrnx_hw;
+
+enum ecrnx_radar_chain {
+    ECRNX_RADAR_RIU = 0,
+    ECRNX_RADAR_FCU,
+    ECRNX_RADAR_LAST
+};
+
+enum ecrnx_radar_detector {
+    ECRNX_RADAR_DETECT_DISABLE = 0, /* Ignore radar pulses */
+    ECRNX_RADAR_DETECT_ENABLE  = 1, /* Process pattern detection but do not
+                                      report radar to upper layer (for test) */
+    ECRNX_RADAR_DETECT_REPORT  = 2  /* Process pattern detection and report
+                                      radar to upper layer. */
+};
+
+#ifdef CONFIG_ECRNX_RADAR
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+
+#define ECRNX_RADAR_PULSE_MAX  32
+
+/**
+ * struct ecrnx_radar_pulses - List of pulses reported by HW
+ * @index: write index
+ * @count: number of valid pulses
+ * @buffer: buffer of pulses
+ */
+struct ecrnx_radar_pulses {
+    /* Last radar pulses received */
+    int index;
+    int count;
+    u32 buffer[ECRNX_RADAR_PULSE_MAX];
+};
+
+/**
+ * struct dfs_pattern_detector - DFS pattern detector
+ * @region: active DFS region, NL80211_DFS_UNSET until set
+ * @num_radar_types: number of different radar types
+ * @last_pulse_ts: time stamp of last valid pulse in usecs
+ * @prev_jiffies:
+ * @radar_detector_specs: array of radar detection specs
+ * @channel_detectors: list connecting channel_detector elements
+ */
+struct dfs_pattern_detector {
+    u8 enabled;
+    enum nl80211_dfs_regions region;
+    u8 num_radar_types;
+    u64 last_pulse_ts;
+    u32 prev_jiffies;
+    const struct radar_detector_specs *radar_spec;
+    struct list_head detectors[];
+};
+
+#define NX_NB_RADAR_DETECTED 4
+
+/**
+ * struct ecrnx_radar_detected - List of radar detected
+ */
+struct ecrnx_radar_detected {
+    u16 index;
+    u16 count;
+    s64 time[NX_NB_RADAR_DETECTED];
+    s16 freq[NX_NB_RADAR_DETECTED];
+};
+
+
+struct ecrnx_radar {
+    struct ecrnx_radar_pulses pulses[ECRNX_RADAR_LAST];
+    struct dfs_pattern_detector *dpd[ECRNX_RADAR_LAST];
+    struct ecrnx_radar_detected detected[ECRNX_RADAR_LAST];
+    struct work_struct detection_work;  /* Work used to process radar pulses */
+    spinlock_t lock;                    /* lock for pulses processing */
+
+    /* In softmac cac is handled by mac80211 */
+#ifdef CONFIG_ECRNX_FULLMAC
+    struct delayed_work cac_work;       /* Work used to handle CAC */
+    struct ecrnx_vif *cac_vif;           /* vif on which we started CAC */
+#endif
+};
+
+bool ecrnx_radar_detection_init(struct ecrnx_radar *radar);
+void ecrnx_radar_detection_deinit(struct ecrnx_radar *radar);
+bool ecrnx_radar_set_domain(struct ecrnx_radar *radar,
+                           enum nl80211_dfs_regions region);
+void ecrnx_radar_detection_enable(struct ecrnx_radar *radar, u8 enable, u8 chain);
+bool ecrnx_radar_detection_is_enable(struct ecrnx_radar *radar, u8 chain);
+void ecrnx_radar_start_cac(struct ecrnx_radar *radar, u32 cac_time_ms,
+                          struct ecrnx_vif *vif);
+void ecrnx_radar_cancel_cac(struct ecrnx_radar *radar);
+void ecrnx_radar_detection_enable_on_cur_channel(struct ecrnx_hw *ecrnx_hw);
+int  ecrnx_radar_dump_pattern_detector(char *buf, size_t len,
+                                      struct ecrnx_radar *radar, u8 chain);
+int  ecrnx_radar_dump_radar_detected(char *buf, size_t len,
+                                    struct ecrnx_radar *radar, u8 chain);
+
+#else
+
+struct ecrnx_radar {
+};
+
+static inline bool ecrnx_radar_detection_init(struct ecrnx_radar *radar)
+{return true;}
+
+static inline void ecrnx_radar_detection_deinit(struct ecrnx_radar *radar)
+{}
+
+static inline bool ecrnx_radar_set_domain(struct ecrnx_radar *radar,
+                                         enum nl80211_dfs_regions region)
+{return true;}
+
+static inline void ecrnx_radar_detection_enable(struct ecrnx_radar *radar,
+                                               u8 enable, u8 chain)
+{}
+
+static inline bool ecrnx_radar_detection_is_enable(struct ecrnx_radar *radar,
+                                                 u8 chain)
+{return false;}
+
+static inline void ecrnx_radar_start_cac(struct ecrnx_radar *radar,
+                                        u32 cac_time_ms, struct ecrnx_vif *vif)
+{}
+
+static inline void ecrnx_radar_cancel_cac(struct ecrnx_radar *radar)
+{}
+
+static inline void ecrnx_radar_detection_enable_on_cur_channel(struct ecrnx_hw *ecrnx_hw)
+{}
+
+static inline int ecrnx_radar_dump_pattern_detector(char *buf, size_t len,
+                                                   struct ecrnx_radar *radar,
+                                                   u8 chain)
+{return 0;}
+
+static inline int ecrnx_radar_dump_radar_detected(char *buf, size_t len,
+                                                 struct ecrnx_radar *radar,
+                                                 u8 chain)
+{return 0;}
+
+#endif /* CONFIG_ECRNX_RADAR */
+
+#endif // _ECRNX_RADAR_H_
diff --git a/drivers/net/wireless/eswin/ecrnx_strs.c b/drivers/net/wireless/eswin/ecrnx_strs.c
new file mode 100644 (file)
index 0000000..4352b9d
--- /dev/null
@@ -0,0 +1,252 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_strs.c
+ *
+ * @brief Miscellaneous debug strings
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include "lmac_msg.h"
+
+static const char *const ecrnx_mmid2str[MSG_I(MM_MAX)] = {
+    [MSG_I(MM_RESET_REQ)]                 = "MM_RESET_REQ",
+    [MSG_I(MM_RESET_CFM)]                 = "MM_RESET_CFM",
+    [MSG_I(MM_START_REQ)]                 = "MM_START_REQ",
+    [MSG_I(MM_START_CFM)]                 = "MM_START_CFM",
+    [MSG_I(MM_VERSION_REQ)]               = "MM_VERSION_REQ",
+    [MSG_I(MM_VERSION_CFM)]               = "MM_VERSION_CFM",
+    [MSG_I(MM_ADD_IF_REQ)]                = "MM_ADD_IF_REQ",
+    [MSG_I(MM_ADD_IF_CFM)]                = "MM_ADD_IF_CFM",
+    [MSG_I(MM_REMOVE_IF_REQ)]             = "MM_REMOVE_IF_REQ",
+    [MSG_I(MM_REMOVE_IF_CFM)]             = "MM_REMOVE_IF_CFM",
+    [MSG_I(MM_STA_ADD_REQ)]               = "MM_STA_ADD_REQ",
+    [MSG_I(MM_STA_ADD_CFM)]               = "MM_STA_ADD_CFM",
+    [MSG_I(MM_STA_DEL_REQ)]               = "MM_STA_DEL_REQ",
+    [MSG_I(MM_STA_DEL_CFM)]               = "MM_STA_DEL_CFM",
+    [MSG_I(MM_SET_FILTER_REQ)]            = "MM_SET_FILTER_REQ",
+    [MSG_I(MM_SET_FILTER_CFM)]            = "MM_SET_FILTER_CFM",
+    [MSG_I(MM_SET_CHANNEL_REQ)]           = "MM_SET_CHANNEL_REQ",
+    [MSG_I(MM_SET_CHANNEL_CFM)]           = "MM_SET_CHANNEL_CFM",
+    [MSG_I(MM_SET_DTIM_REQ)]              = "MM_SET_DTIM_REQ",
+    [MSG_I(MM_SET_DTIM_CFM)]              = "MM_SET_DTIM_CFM",
+    [MSG_I(MM_SET_BEACON_INT_REQ)]        = "MM_SET_BEACON_INT_REQ",
+    [MSG_I(MM_SET_BEACON_INT_CFM)]        = "MM_SET_BEACON_INT_CFM",
+    [MSG_I(MM_SET_BASIC_RATES_REQ)]       = "MM_SET_BASIC_RATES_REQ",
+    [MSG_I(MM_SET_BASIC_RATES_CFM)]       = "MM_SET_BASIC_RATES_CFM",
+    [MSG_I(MM_SET_BSSID_REQ)]             = "MM_SET_BSSID_REQ",
+    [MSG_I(MM_SET_BSSID_CFM)]             = "MM_SET_BSSID_CFM",
+    [MSG_I(MM_SET_EDCA_REQ)]              = "MM_SET_EDCA_REQ",
+    [MSG_I(MM_SET_EDCA_CFM)]              = "MM_SET_EDCA_CFM",
+    [MSG_I(MM_SET_MODE_REQ)]              = "MM_SET_MODE_REQ",
+    [MSG_I(MM_SET_MODE_CFM)]              = "MM_SET_MODE_CFM",
+    [MSG_I(MM_SET_VIF_STATE_REQ)]         = "MM_SET_VIF_STATE_REQ",
+    [MSG_I(MM_SET_VIF_STATE_CFM)]         = "MM_SET_VIF_STATE_CFM",
+    [MSG_I(MM_SET_SLOTTIME_REQ)]          = "MM_SET_SLOTTIME_REQ",
+    [MSG_I(MM_SET_SLOTTIME_CFM)]          = "MM_SET_SLOTTIME_CFM",
+    [MSG_I(MM_SET_IDLE_REQ)]              = "MM_SET_IDLE_REQ",
+    [MSG_I(MM_SET_IDLE_CFM)]              = "MM_SET_IDLE_CFM",
+    [MSG_I(MM_KEY_ADD_REQ)]               = "MM_KEY_ADD_REQ",
+    [MSG_I(MM_KEY_ADD_CFM)]               = "MM_KEY_ADD_CFM",
+    [MSG_I(MM_KEY_DEL_REQ)]               = "MM_KEY_DEL_REQ",
+    [MSG_I(MM_KEY_DEL_CFM)]               = "MM_KEY_DEL_CFM",
+    [MSG_I(MM_BA_ADD_REQ)]                = "MM_BA_ADD_REQ",
+    [MSG_I(MM_BA_ADD_CFM)]                = "MM_BA_ADD_CFM",
+    [MSG_I(MM_BA_DEL_REQ)]                = "MM_BA_DEL_REQ",
+    [MSG_I(MM_BA_DEL_CFM)]                = "MM_BA_DEL_CFM",
+    [MSG_I(MM_PRIMARY_TBTT_IND)]          = "MM_PRIMARY_TBTT_IND",
+    [MSG_I(MM_SECONDARY_TBTT_IND)]        = "MM_SECONDARY_TBTT_IND",
+    [MSG_I(MM_SET_POWER_REQ)]             = "MM_SET_POWER_REQ",
+    [MSG_I(MM_SET_POWER_CFM)]             = "MM_SET_POWER_CFM",
+    [MSG_I(MM_DBG_TRIGGER_REQ)]           = "MM_DBG_TRIGGER_REQ",
+    [MSG_I(MM_SET_PS_MODE_REQ)]           = "MM_SET_PS_MODE_REQ",
+    [MSG_I(MM_SET_PS_MODE_CFM)]           = "MM_SET_PS_MODE_CFM",
+    [MSG_I(MM_CHAN_CTXT_ADD_REQ)]         = "MM_CHAN_CTXT_ADD_REQ",
+    [MSG_I(MM_CHAN_CTXT_ADD_CFM)]         = "MM_CHAN_CTXT_ADD_CFM",
+    [MSG_I(MM_CHAN_CTXT_DEL_REQ)]         = "MM_CHAN_CTXT_DEL_REQ",
+    [MSG_I(MM_CHAN_CTXT_DEL_CFM)]         = "MM_CHAN_CTXT_DEL_CFM",
+    [MSG_I(MM_CHAN_CTXT_LINK_REQ)]        = "MM_CHAN_CTXT_LINK_REQ",
+    [MSG_I(MM_CHAN_CTXT_LINK_CFM)]        = "MM_CHAN_CTXT_LINK_CFM",
+    [MSG_I(MM_CHAN_CTXT_UNLINK_REQ)]      = "MM_CHAN_CTXT_UNLINK_REQ",
+    [MSG_I(MM_CHAN_CTXT_UNLINK_CFM)]      = "MM_CHAN_CTXT_UNLINK_CFM",
+    [MSG_I(MM_CHAN_CTXT_UPDATE_REQ)]      = "MM_CHAN_CTXT_UPDATE_REQ",
+    [MSG_I(MM_CHAN_CTXT_UPDATE_CFM)]      = "MM_CHAN_CTXT_UPDATE_CFM",
+    [MSG_I(MM_CHAN_CTXT_SCHED_REQ)]       = "MM_CHAN_CTXT_SCHED_REQ",
+    [MSG_I(MM_CHAN_CTXT_SCHED_CFM)]       = "MM_CHAN_CTXT_SCHED_CFM",
+    [MSG_I(MM_BCN_CHANGE_REQ)]            = "MM_BCN_CHANGE_REQ",
+    [MSG_I(MM_BCN_CHANGE_CFM)]            = "MM_BCN_CHANGE_CFM",
+    [MSG_I(MM_TIM_UPDATE_REQ)]            = "MM_TIM_UPDATE_REQ",
+    [MSG_I(MM_TIM_UPDATE_CFM)]            = "MM_TIM_UPDATE_CFM",
+    [MSG_I(MM_CONNECTION_LOSS_IND)]       = "MM_CONNECTION_LOSS_IND",
+    [MSG_I(MM_CHANNEL_SWITCH_IND)]        = "MM_CHANNEL_SWITCH_IND",
+    [MSG_I(MM_CHANNEL_PRE_SWITCH_IND)]    = "MM_CHANNEL_PRE_SWITCH_IND",
+    [MSG_I(MM_REMAIN_ON_CHANNEL_REQ)]     = "MM_REMAIN_ON_CHANNEL_REQ",
+    [MSG_I(MM_REMAIN_ON_CHANNEL_CFM)]     = "MM_REMAIN_ON_CHANNEL_CFM",
+    [MSG_I(MM_REMAIN_ON_CHANNEL_EXP_IND)] = "MM_REMAIN_ON_CHANNEL_EXP_IND",
+    [MSG_I(MM_PS_CHANGE_IND)]             = "MM_PS_CHANGE_IND",
+    [MSG_I(MM_TRAFFIC_REQ_IND)]           = "MM_TRAFFIC_REQ_IND",
+    [MSG_I(MM_SET_PS_OPTIONS_REQ)]        = "MM_SET_PS_OPTIONS_REQ",
+    [MSG_I(MM_SET_PS_OPTIONS_CFM)]        = "MM_SET_PS_OPTIONS_CFM",
+    [MSG_I(MM_P2P_VIF_PS_CHANGE_IND)]     = "MM_P2P_VIF_PS_CHANGE_IND",
+    [MSG_I(MM_CSA_COUNTER_IND)]           = "MM_CSA_COUNTER_IND",
+    [MSG_I(MM_CHANNEL_SURVEY_IND)]        = "MM_CHANNEL_SURVEY_IND",
+    [MSG_I(MM_SET_P2P_NOA_REQ)]           = "MM_SET_P2P_NOA_REQ",
+    [MSG_I(MM_SET_P2P_OPPPS_REQ)]         = "MM_SET_P2P_OPPPS_REQ",
+    [MSG_I(MM_SET_P2P_NOA_CFM)]           = "MM_SET_P2P_NOA_CFM",
+    [MSG_I(MM_SET_P2P_OPPPS_CFM)]         = "MM_SET_P2P_OPPPS_CFM",
+    [MSG_I(MM_CFG_RSSI_REQ)]              = "MM_CFG_RSSI_REQ",
+    [MSG_I(MM_RSSI_STATUS_IND)]           = "MM_RSSI_STATUS_IND",
+    [MSG_I(MM_CSA_FINISH_IND)]            = "MM_CSA_FINISH_IND",
+    [MSG_I(MM_CSA_TRAFFIC_IND)]           = "MM_CSA_TRAFFIC_IND",
+    [MSG_I(MM_MU_GROUP_UPDATE_REQ)]       = "MM_MU_GROUP_UPDATE_REQ",
+    [MSG_I(MM_MU_GROUP_UPDATE_CFM)]       = "MM_MU_GROUP_UPDATE_CFM",
+    [MSG_I(MM_GET_CAL_RESULT_REQ)]       = "MM_GET_CAL_RESULT_REQ",
+    [MSG_I(MM_GET_CAL_RESULT_CFM)]       = "MM_GET_CAL_RESULT_CFM",
+};
+
+static const char *const ecrnx_dbgid2str[MSG_I(DBG_MAX)] = {
+    [MSG_I(DBG_MEM_READ_REQ)]        = "DBG_MEM_READ_REQ",
+    [MSG_I(DBG_MEM_READ_CFM)]        = "DBG_MEM_READ_CFM",
+    [MSG_I(DBG_MEM_WRITE_REQ)]       = "DBG_MEM_WRITE_REQ",
+    [MSG_I(DBG_MEM_WRITE_CFM)]       = "DBG_MEM_WRITE_CFM",
+    [MSG_I(DBG_SET_MOD_FILTER_REQ)]  = "DBG_SET_MOD_FILTER_REQ",
+    [MSG_I(DBG_SET_MOD_FILTER_CFM)]  = "DBG_SET_MOD_FILTER_CFM",
+    [MSG_I(DBG_SET_SEV_FILTER_REQ)]  = "DBG_SET_SEV_FILTER_REQ",
+    [MSG_I(DBG_SET_SEV_FILTER_CFM)]  = "DBG_SET_SEV_FILTER_CFM",
+    [MSG_I(DBG_ERROR_IND)]           = "DBG_ERROR_IND",
+    [MSG_I(DBG_GET_SYS_STAT_REQ)]    = "DBG_GET_SYS_STAT_REQ",
+    [MSG_I(DBG_GET_SYS_STAT_CFM)]    = "DBG_GET_SYS_STAT_CFM",
+};
+
+static const char *const ecrnx_scanid2str[MSG_I(SCAN_MAX)] = {
+    [MSG_I(SCAN_START_REQ)]          = "SCAN_START_REQ",
+    [MSG_I(SCAN_START_CFM)]          = "SCAN_START_CFM",
+    [MSG_I(SCAN_DONE_IND)]           = "SCAN_DONE_IND",
+};
+
+static const char *const ecrnx_tdlsid2str[MSG_I(TDLS_MAX)] = {
+    [MSG_I(TDLS_CHAN_SWITCH_CFM)]        = "TDLS_CHAN_SWITCH_CFM",
+    [MSG_I(TDLS_CHAN_SWITCH_REQ)]        = "TDLS_CHAN_SWITCH_REQ",
+    [MSG_I(TDLS_CHAN_SWITCH_IND)]        = "TDLS_CHAN_SWITCH_IND",
+    [MSG_I(TDLS_CHAN_SWITCH_BASE_IND)]   = "TDLS_CHAN_SWITCH_BASE_IND",
+    [MSG_I(TDLS_CANCEL_CHAN_SWITCH_REQ)] = "TDLS_CANCEL_CHAN_SWITCH_REQ",
+    [MSG_I(TDLS_CANCEL_CHAN_SWITCH_CFM)] = "TDLS_CANCEL_CHAN_SWITCH_CFM",
+    [MSG_I(TDLS_PEER_PS_IND)]            = "TDLS_PEER_PS_IND",
+    [MSG_I(TDLS_PEER_TRAFFIC_IND_REQ)]   = "TDLS_PEER_TRAFFIC_IND_REQ",
+    [MSG_I(TDLS_PEER_TRAFFIC_IND_CFM)]   = "TDLS_PEER_TRAFFIC_IND_CFM",
+};
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+static const char *const ecrnx_scanuid2str[MSG_I(SCANU_MAX)] = {
+    [MSG_I(SCANU_START_REQ)]  = "SCANU_START_REQ",
+    [MSG_I(SCANU_START_CFM)]  = "SCANU_START_CFM",
+    [MSG_I(SCANU_JOIN_REQ)]   = "SCANU_JOIN_REQ",
+    [MSG_I(SCANU_JOIN_CFM)]   = "SCANU_JOIN_CFM",
+    [MSG_I(SCANU_RESULT_IND)] = "SCANU_RESULT_IND",
+    [MSG_I(SCANU_FAST_REQ)]   = "SCANU_FAST_REQ",
+    [MSG_I(SCANU_FAST_CFM)]   = "SCANU_FAST_CFM",
+    [MSG_I(SCANU_CANCEL_REQ)]   = "SCANU_CANCEL_REQ",
+    [MSG_I(SCANU_CANCEL_CFM)]   = "SCANU_CANCEL_CFM",
+};
+
+static const char *const ecrnx_meid2str[MSG_I(ME_MAX)] = {
+    [MSG_I(ME_CONFIG_REQ)]           = "ME_CONFIG_REQ",
+    [MSG_I(ME_CONFIG_CFM)]           = "ME_CONFIG_CFM",
+    [MSG_I(ME_CHAN_CONFIG_REQ)]      = "ME_CHAN_CONFIG_REQ",
+    [MSG_I(ME_CHAN_CONFIG_CFM)]      = "ME_CHAN_CONFIG_CFM",
+    [MSG_I(ME_SET_CONTROL_PORT_REQ)] = "ME_SET_CONTROL_PORT_REQ",
+    [MSG_I(ME_SET_CONTROL_PORT_CFM)] = "ME_SET_CONTROL_PORT_CFM",
+    [MSG_I(ME_TKIP_MIC_FAILURE_IND)] = "ME_TKIP_MIC_FAILURE_IND",
+    [MSG_I(ME_STA_ADD_REQ)]          = "ME_STA_ADD_REQ",
+    [MSG_I(ME_STA_ADD_CFM)]          = "ME_STA_ADD_CFM",
+    [MSG_I(ME_STA_DEL_REQ)]          = "ME_STA_DEL_REQ",
+    [MSG_I(ME_STA_DEL_CFM)]          = "ME_STA_DEL_CFM",
+    [MSG_I(ME_TX_CREDITS_UPDATE_IND)]= "ME_TX_CREDITS_UPDATE_IND",
+    [MSG_I(ME_RC_STATS_REQ)]         = "ME_RC_STATS_REQ",
+    [MSG_I(ME_RC_STATS_CFM)]         = "ME_RC_STATS_CFM",
+    [MSG_I(ME_RC_SET_RATE_REQ)]      = "ME_RC_SET_RATE_REQ",
+    [MSG_I(ME_TRAFFIC_IND_REQ)]      = "ME_TRAFFIC_IND_REQ",
+    [MSG_I(ME_TRAFFIC_IND_CFM)]      = "ME_TRAFFIC_IND_CFM",
+    [MSG_I(ME_SET_PS_MODE_REQ)]      = "ME_SET_PS_MODE_REQ",
+    [MSG_I(ME_SET_PS_MODE_CFM)]      = "ME_SET_PS_MODE_CFM",
+};
+
+static const char *const ecrnx_smid2str[MSG_I(SM_MAX)] = {
+    [MSG_I(SM_CONNECT_REQ)]       = "SM_CONNECT_REQ",
+    [MSG_I(SM_CONNECT_CFM)]       = "SM_CONNECT_CFM",
+    [MSG_I(SM_CONNECT_IND)]       = "SM_CONNECT_IND",
+    [MSG_I(SM_DISCONNECT_REQ)]    = "SM_DISCONNECT_REQ",
+    [MSG_I(SM_DISCONNECT_CFM)]    = "SM_DISCONNECT_CFM",
+    [MSG_I(SM_DISCONNECT_IND)]    = "SM_DISCONNECT_IND",
+    [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_IND)] = "SM_EXTERNAL_AUTH_REQUIRED_IND",
+    [MSG_I(SM_EXTERNAL_AUTH_REQUIRED_RSP)] = "SM_EXTERNAL_AUTH_REQUIRED_RSP",
+};
+
+static const char *const ecrnx_apmid2str[MSG_I(APM_MAX)] = {
+    [MSG_I(APM_START_REQ)]     = "APM_START_REQ",
+    [MSG_I(APM_START_CFM)]     = "APM_START_CFM",
+    [MSG_I(APM_STOP_REQ)]      = "APM_STOP_REQ",
+    [MSG_I(APM_STOP_CFM)]      = "APM_STOP_CFM",
+    [MSG_I(APM_START_CAC_REQ)] = "APM_START_CAC_REQ",
+    [MSG_I(APM_START_CAC_CFM)] = "APM_START_CAC_CFM",
+    [MSG_I(APM_STOP_CAC_REQ)]  = "APM_STOP_CAC_REQ",
+    [MSG_I(APM_STOP_CAC_CFM)]  = "APM_STOP_CAC_CFM",
+};
+
+static const char *const ecrnx_meshid2str[MSG_I(MESH_MAX)] = {
+    [MSG_I(MESH_START_REQ)]        = "MESH_START_REQ",
+    [MSG_I(MESH_START_CFM)]        = "MESH_START_CFM",
+    [MSG_I(MESH_STOP_REQ)]         = "MESH_STOP_REQ",
+    [MSG_I(MESH_STOP_CFM)]         = "MESH_STOP_CFM",
+    [MSG_I(MESH_UPDATE_REQ)]       = "MESH_UPDATE_REQ",
+    [MSG_I(MESH_UPDATE_CFM)]       = "MESH_UPDATE_CFM",
+    [MSG_I(MESH_PATH_CREATE_REQ)]  = "MESH_PATH_CREATE_REQ",
+    [MSG_I(MESH_PATH_CREATE_CFM)]  = "MESH_PATH_CREATE_CFM",
+    [MSG_I(MESH_PATH_UPDATE_REQ)]  = "MESH_PATH_UPDATE_REQ",
+    [MSG_I(MESH_PATH_UPDATE_CFM)]  = "MESH_PATH_UPDATE_CFM",
+    [MSG_I(MESH_PROXY_ADD_REQ)]    = "MESH_PROXY_ADD_REQ",
+    [MSG_I(MESH_PEER_UPDATE_IND)]  = "MESH_PEER_UPDATE_IND",
+    [MSG_I(MESH_PATH_UPDATE_IND)]  = "MESH_PATH_UPDATE_IND",
+    [MSG_I(MESH_PROXY_UPDATE_IND)] = "MESH_PROXY_UPDATE_IND",
+};
+
+static const char *const ecrnx_twtid2str[MSG_I(TWT_MAX)] = {
+    [MSG_I(TWT_SETUP_REQ)]         = "TWT_SETUP_REQ",
+    [MSG_I(TWT_SETUP_CFM)]         = "TWT_SETUP_CFM",
+    [MSG_I(TWT_SETUP_IND)]         = "TWT_SETUP_IND",
+    [MSG_I(TWT_TEARDOWN_REQ)]      = "TWT_TEARDOWN_REQ",
+    [MSG_I(TWT_TEARDOWN_CFM)]      = "TWT_TEARDOWN_CFM",
+};
+
+#if defined(CONFIG_ECRNX_P2P)
+static const char *const rwnx_p2plistenid2str[MSG_I(P2P_LISTEN_MAX)] = {
+    [MSG_I(P2P_LISTEN_START_REQ)]  = "P2P_LISTEN_START_REQ",
+    [MSG_I(P2P_LISTEN_START_CFM)]  = "P2P_LISTEN_START_CFM",
+    [MSG_I(P2P_CANCEL_LISTEN_REQ)]   = "P2P_CANCEL_LISTEN_REQ",
+    [MSG_I(P2P_CANCEL_LISTEN_CFM)]   = "P2P_CANCEL_LISTEN_CFM",
+};
+#endif
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+const char *const *ecrnx_id2str[TASK_LAST_EMB + 1] = {
+    [TASK_MM]    = ecrnx_mmid2str,
+    [TASK_DBG]   = ecrnx_dbgid2str,
+    [TASK_SCAN]  = ecrnx_scanid2str,
+    [TASK_TDLS]  = ecrnx_tdlsid2str,
+#ifdef CONFIG_ECRNX_FULLMAC
+    [TASK_SCANU] = ecrnx_scanuid2str,
+    [TASK_ME]    = ecrnx_meid2str,
+    [TASK_SM]    = ecrnx_smid2str,
+    [TASK_APM]   = ecrnx_apmid2str,
+    [TASK_MESH]  = ecrnx_meshid2str,
+#if defined(CONFIG_ECRNX_P2P)
+    [TASK_P2P_LISTEN]   = rwnx_p2plistenid2str,
+#endif
+    [TASK_TWT]   = ecrnx_twtid2str,
+#endif
+};
diff --git a/drivers/net/wireless/eswin/ecrnx_strs.h b/drivers/net/wireless/eswin/ecrnx_strs.h
new file mode 100644 (file)
index 0000000..aa0d129
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_strs.h
+ *
+ * @brief Miscellaneous debug strings
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_STRS_H_
+#define _ECRNX_STRS_H_
+
+#ifdef CONFIG_ECRNX_FHOST
+
+#define ECRNX_ID2STR(tag) "Cmd"
+
+#else
+#include "lmac_msg.h"
+
+#define ECRNX_ID2STR(tag) (((MSG_T(tag) < ARRAY_SIZE(ecrnx_id2str)) &&        \
+                           (ecrnx_id2str[MSG_T(tag)]) &&          \
+                           ((ecrnx_id2str[MSG_T(tag)])[MSG_I(tag)])) ?   \
+                          (ecrnx_id2str[MSG_T(tag)])[MSG_I(tag)] : "unknown")
+
+extern const char *const *ecrnx_id2str[TASK_LAST_EMB + 1];
+#endif /* CONFIG_ECRNX_FHOST */
+
+#endif /* _ECRNX_STRS_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_testmode.c b/drivers/net/wireless/eswin/ecrnx_testmode.c
new file mode 100644 (file)
index 0000000..7ce995e
--- /dev/null
@@ -0,0 +1,226 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_testmode.c
+ *
+ * @brief Test mode function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#include <net/mac80211.h>
+#include <net/netlink.h>
+
+#include "ecrnx_testmode.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_dini.h"
+#include "reg_access.h"
+
+/*
+ * This function handles the user application commands for register access.
+ *
+ * It retrieves command ID carried with ECRNX_TM_ATTR_COMMAND and calls to the
+ * handlers respectively.
+ *
+ * If it's an unknown commdn ID, -ENOSYS is returned; or -ENOMSG if the
+ * mandatory fields(ECRNX_TM_ATTR_REG_OFFSET,ECRNX_TM_ATTR_REG_VALUE32)
+ * are missing; Otherwise 0 is replied indicating the success of the command execution.
+ *
+ * If ECRNX_TM_ATTR_COMMAND is ECRNX_TM_CMD_APP2DEV_REG_READ, the register read
+ * value is returned with ECRNX_TM_ATTR_REG_VALUE32.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int ecrnx_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+    struct ecrnx_hw *ecrnx_hw = hw->priv;
+    u32 mem_addr, val32;
+    struct sk_buff *skb;
+    int status = 0;
+
+    /* First check if register address is there */
+    if (!tb[ECRNX_TM_ATTR_REG_OFFSET]) {
+        ECRNX_ERR("Error finding register offset\n");
+        return -ENOMSG;
+    }
+
+    mem_addr = nla_get_u32(tb[ECRNX_TM_ATTR_REG_OFFSET]);
+
+    switch (nla_get_u32(tb[ECRNX_TM_ATTR_COMMAND])) {
+    case ECRNX_TM_CMD_APP2DEV_REG_READ:
+        {
+            struct dbg_mem_read_cfm mem_read_cfm;
+
+            /*** Send the command to the LMAC ***/
+            if ((status = ecrnx_send_dbg_mem_read_req(ecrnx_hw, mem_addr, &mem_read_cfm)))
+                return status;
+
+            /* Allocate the answer message */
+            skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+            if (!skb) {
+                ECRNX_ERR("Error allocating memory\n");
+                return -ENOMEM;
+            }
+
+            val32 = mem_read_cfm.memdata;
+            if (nla_put_u32(skb, ECRNX_TM_ATTR_REG_VALUE32, val32))
+                goto nla_put_failure;
+
+            /* Send the answer to upper layer */
+            status = cfg80211_testmode_reply(skb);
+            if (status < 0)
+                ECRNX_ERR("Error sending msg : %d\n", status);
+        }
+        break;
+
+    case ECRNX_TM_CMD_APP2DEV_REG_WRITE:
+        {
+            if (!tb[ECRNX_TM_ATTR_REG_VALUE32]) {
+                ECRNX_ERR("Error finding value to write\n");
+                return -ENOMSG;
+            } else {
+                val32 = nla_get_u32(tb[ECRNX_TM_ATTR_REG_VALUE32]);
+                /* Send the command to the LMAC */
+                if ((status = ecrnx_send_dbg_mem_write_req(ecrnx_hw, mem_addr, val32)))
+                    return status;
+            }
+        }
+        break;
+
+    default:
+        ECRNX_ERR("Unknown testmode register command ID\n");
+        return -ENOSYS;
+    }
+
+    return status;
+
+nla_put_failure:
+    kfree_skb(skb);
+    return -EMSGSIZE;
+}
+
+/*
+ * This function handles the user application commands for Debug filter settings.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int ecrnx_testmode_dbg_filter(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+    struct ecrnx_hw *ecrnx_hw = hw->priv;
+    u32 filter;
+    int status = 0;
+
+    /* First check if the filter is there */
+    if (!tb[ECRNX_TM_ATTR_REG_FILTER]) {
+        ECRNX_ERR("Error finding filter value\n");
+        return -ENOMSG;
+    }
+
+    filter = nla_get_u32(tb[ECRNX_TM_ATTR_REG_FILTER]);
+    ECRNX_DBG("testmode debug filter, setting: 0x%x\n", filter);
+
+    switch (nla_get_u32(tb[ECRNX_TM_ATTR_COMMAND])) {
+    case ECRNX_TM_CMD_APP2DEV_SET_DBGMODFILTER:
+        {
+            /* Send the command to the LMAC */
+            if ((status = ecrnx_send_dbg_set_mod_filter_req(ecrnx_hw, filter)))
+                return status;
+        }
+        break;
+    case ECRNX_TM_CMD_APP2DEV_SET_DBGSEVFILTER:
+        {
+            /* Send the command to the LMAC */
+            if ((status = ecrnx_send_dbg_set_sev_filter_req(ecrnx_hw, filter)))
+                return status;
+        }
+        break;
+
+    default:
+        ECRNX_ERR("Unknown testmode register command ID\n");
+        return -ENOSYS;
+    }
+
+    return status;
+}
+
+/*
+ * This function handles the user application commands for register access without using
+ * the normal LMAC messaging way.
+ * This time register access will be done through direct PCI BAR windows. This can be used
+ * to access registers even when the :AMC FW is stuck.
+ *
+ * @hw: ieee80211_hw object that represents the device
+ * @tb: general message fields from the user space
+ */
+int ecrnx_testmode_reg_dbg(struct ieee80211_hw *hw, struct nlattr **tb)
+{
+    struct ecrnx_hw *ecrnx_hw = hw->priv;
+    struct ecrnx_plat *ecrnx_plat = ecrnx_hw->plat;
+    u32 mem_addr;
+    struct sk_buff *skb;
+    int status = 0;
+    volatile unsigned int reg_value = 0;
+    unsigned int offset;
+
+    /* First check if register address is there */
+    if (!tb[ECRNX_TM_ATTR_REG_OFFSET]) {
+        ECRNX_ERR("Error finding register offset\n");
+        return -ENOMSG;
+    }
+
+    mem_addr = nla_get_u32(tb[ECRNX_TM_ATTR_REG_OFFSET]);
+    offset = mem_addr & 0x00FFFFFF;
+
+    switch (nla_get_u32(tb[ECRNX_TM_ATTR_COMMAND])) {
+    case ECRNX_TM_CMD_APP2DEV_REG_READ_DBG:
+        {
+            /*** Send the command to the LMAC ***/
+            reg_value = ECRNX_REG_READ(ecrnx_plat, ECRNX_ADDR_SYSTEM, offset);
+
+            /* Allocate the answer message */
+            skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20);
+            if (!skb) {
+                ECRNX_ERR("Error allocating memory\n");
+                return -ENOMEM;
+            }
+
+            if (nla_put_u32(skb, ECRNX_TM_ATTR_REG_VALUE32, reg_value))
+                goto nla_put_failure;
+
+            /* Send the answer to upper layer */
+            status = cfg80211_testmode_reply(skb);
+            if (status < 0)
+                ECRNX_ERR("Error sending msg : %d\n", status);
+        }
+        break;
+
+    case ECRNX_TM_CMD_APP2DEV_REG_WRITE_DBG:
+        {
+            if (!tb[ECRNX_TM_ATTR_REG_VALUE32]) {
+                ECRNX_ERR("Error finding value to write\n");
+                return -ENOMSG;
+            } else {
+                reg_value = nla_get_u32(tb[ECRNX_TM_ATTR_REG_VALUE32]);
+
+                /* Send the command to the LMAC */
+                ECRNX_REG_WRITE(reg_value, ecrnx_plat, ECRNX_ADDR_SYSTEM,
+                               offset);
+            }
+        }
+        break;
+
+    default:
+        ECRNX_ERR("Unknown testmode register command ID\n");
+        return -ENOSYS;
+    }
+
+    return status;
+
+nla_put_failure:
+    kfree_skb(skb);
+    return -EMSGSIZE;
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_testmode.h b/drivers/net/wireless/eswin/ecrnx_testmode.h
new file mode 100644 (file)
index 0000000..36d8562
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_testmode.h
+ *
+ * @brief Test mode function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef ECRNX_TESTMODE_H_
+#define ECRNX_TESTMODE_H_
+
+#include <net/mac80211.h>
+#include <net/netlink.h>
+
+/* Commands from user space to kernel space(ECRNX_TM_CMD_APP2DEV_XX) and
+ * from and kernel space to user space(ECRNX_TM_CMD_DEV2APP_XX).
+ * The command ID is carried with ECRNX_TM_ATTR_COMMAND.
+ */
+enum ecrnx_tm_cmd_t {
+    /* commands from user application to access register */
+    ECRNX_TM_CMD_APP2DEV_REG_READ = 1,
+    ECRNX_TM_CMD_APP2DEV_REG_WRITE,
+
+    /* commands from user application to select the Debug levels */
+    ECRNX_TM_CMD_APP2DEV_SET_DBGMODFILTER,
+    ECRNX_TM_CMD_APP2DEV_SET_DBGSEVFILTER,
+
+    /* commands to access registers without sending messages to LMAC layer,
+     * this must be used when LMAC FW is stuck. */
+    ECRNX_TM_CMD_APP2DEV_REG_READ_DBG,
+    ECRNX_TM_CMD_APP2DEV_REG_WRITE_DBG,
+
+    ECRNX_TM_CMD_MAX,
+};
+
+enum ecrnx_tm_attr_t {
+    ECRNX_TM_ATTR_NOT_APPLICABLE = 0,
+
+    ECRNX_TM_ATTR_COMMAND,
+
+    /* When ECRNX_TM_ATTR_COMMAND is ECRNX_TM_CMD_APP2DEV_REG_XXX,
+     * The mandatory fields are:
+     * ECRNX_TM_ATTR_REG_OFFSET for the offset of the target register;
+     * ECRNX_TM_ATTR_REG_VALUE32 for value */
+    ECRNX_TM_ATTR_REG_OFFSET,
+    ECRNX_TM_ATTR_REG_VALUE32,
+
+    /* When ECRNX_TM_ATTR_COMMAND is ECRNX_TM_CMD_APP2DEV_SET_DBGXXXFILTER,
+     * The mandatory field is ECRNX_TM_ATTR_REG_FILTER. */
+    ECRNX_TM_ATTR_REG_FILTER,
+
+    ECRNX_TM_ATTR_MAX,
+};
+
+/***********************************************************************/
+int ecrnx_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb);
+int ecrnx_testmode_dbg_filter(struct ieee80211_hw *hw, struct nlattr **tb);
+int ecrnx_testmode_reg_dbg(struct ieee80211_hw *hw, struct nlattr **tb);
+
+#endif /* ECRNX_TESTMODE_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_txq.c b/drivers/net/wireless/eswin/ecrnx_txq.c
new file mode 100644 (file)
index 0000000..80536b9
--- /dev/null
@@ -0,0 +1,1772 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_txq.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include "ecrnx_defs.h"
+#include "ecrnx_tx.h"
+#include "ipc_host.h"
+#include "ecrnx_events.h"
+
+/******************************************************************************
+ * Utils functions
+ *****************************************************************************/
+#ifdef CONFIG_ECRNX_SOFTMAC
+const int nx_tid_prio[NX_NB_TID_PER_STA] = {15, 7, 14, 6, 13, 5, 12, 4,
+                                            11, 3, 8, 0, 10, 2, 9, 1};
+
+static inline int ecrnx_txq_sta_idx(struct ecrnx_sta *sta, u8 tid)
+{
+    return sta->sta_idx * NX_NB_TXQ_PER_STA;
+}
+
+static inline int ecrnx_txq_vif_idx(struct ecrnx_vif *vif, u8 ac)
+{
+    return vif->vif_index * NX_NB_TXQ_PER_VIF + ac + NX_FIRST_VIF_TXQ_IDX;
+}
+
+#ifdef CONFIG_MAC80211_TXQ
+struct ecrnx_txq *ecrnx_txq_sta_get(struct ecrnx_sta *ecrnx_sta, u8 tid)
+{
+    struct ieee80211_sta *sta = ecrnx_to_ieee80211_sta(ecrnx_sta);
+    struct ieee80211_txq *mac_txq = sta->txq[tid];
+
+    return (struct ecrnx_txq *)mac_txq->drv_priv;
+}
+
+struct ecrnx_txq *ecrnx_txq_vif_get(struct ecrnx_vif *ecrnx_vif, u8 ac)
+{
+    struct ieee80211_vif *vif = ecrnx_to_ieee80211_vif(ecrnx_vif);
+
+    /* mac80211 only allocate one txq per vif for Best Effort */
+    if (ac == ECRNX_HWQ_BE) {
+        struct ieee80211_txq *mac_txq = vif->txq;
+        if (!mac_txq)
+            return NULL;
+        return (struct ecrnx_txq *)mac_txq->drv_priv;
+    }
+
+    if (ac > NX_TXQ_CNT)
+        ac = ECRNX_HWQ_BK;
+
+    return &ecrnx_vif->txqs[ac];
+}
+
+#else /* ! CONFIG_MAC80211_TXQ */
+struct ecrnx_txq *ecrnx_txq_sta_get(struct ecrnx_sta *sta, u8 tid)
+{
+    if (tid >= NX_NB_TXQ_PER_STA)
+        tid = 0;
+
+    return &sta->txqs[tid];
+}
+
+struct ecrnx_txq *ecrnx_txq_vif_get(struct ecrnx_vif *vif, u8 ac)
+{
+    if (ac > NX_TXQ_CNT)
+        ac = ECRNX_HWQ_BK;
+
+    return &vif->txqs[ac];
+}
+
+#endif /* CONFIG_MAC80211_TXQ */
+
+static inline struct ecrnx_sta *ecrnx_txq_2_sta(struct ecrnx_txq *txq)
+{
+    if (txq->sta)
+        return (struct ecrnx_sta *)txq->sta->drv_priv;
+    return NULL;
+}
+
+#else /* CONFIG_ECRNX_FULLMAC */
+const int nx_tid_prio[NX_NB_TID_PER_STA] = {7, 6, 5, 4, 3, 0, 2, 1};
+
+static inline int ecrnx_txq_sta_idx(struct ecrnx_sta *sta, u8 tid)
+{
+    if (is_multicast_sta(sta->sta_idx))
+        return NX_FIRST_VIF_TXQ_IDX + sta->vif_idx;
+    else
+        return (sta->sta_idx * NX_NB_TXQ_PER_STA) + tid;
+}
+
+static inline int ecrnx_txq_vif_idx(struct ecrnx_vif *vif, u8 type)
+{
+    return NX_FIRST_VIF_TXQ_IDX + master_vif_idx(vif) + (type * NX_VIRT_DEV_MAX);
+}
+
+struct ecrnx_txq *ecrnx_txq_sta_get(struct ecrnx_sta *sta, u8 tid,
+                                  struct ecrnx_hw * ecrnx_hw)
+{
+    if (tid >= NX_NB_TXQ_PER_STA)
+        tid = 0;
+
+    return &ecrnx_hw->txq[ecrnx_txq_sta_idx(sta, tid)];
+}
+
+struct ecrnx_txq *ecrnx_txq_vif_get(struct ecrnx_vif *vif, u8 type)
+{
+    if (type > NX_UNK_TXQ_TYPE)
+        type = NX_BCMC_TXQ_TYPE;
+
+    return &vif->ecrnx_hw->txq[ecrnx_txq_vif_idx(vif, type)];
+}
+
+static inline struct ecrnx_sta *ecrnx_txq_2_sta(struct ecrnx_txq *txq)
+{
+    return txq->sta;
+}
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+
+/******************************************************************************
+ * Init/Deinit functions
+ *****************************************************************************/
+/**
+ * ecrnx_txq_init - Initialize a TX queue
+ *
+ * @txq: TX queue to be initialized
+ * @idx: TX queue index
+ * @status: TX queue initial status
+ * @hwq: Associated HW queue
+ * @ndev: Net device this queue belongs to
+ *        (may be null for non netdev txq)
+ *
+ * Each queue is initialized with the credit of @NX_TXQ_INITIAL_CREDITS.
+ */
+static void ecrnx_txq_init(struct ecrnx_txq *txq, int idx, u8 status,
+                          struct ecrnx_hwq *hwq, int tid,
+#ifdef CONFIG_ECRNX_SOFTMAC
+                          struct ieee80211_sta *sta
+#else
+                          struct ecrnx_sta *sta, struct net_device *ndev
+#endif
+                          )
+{
+    int i;
+
+    txq->idx = idx;
+    txq->status = status;
+    txq->credits = NX_TXQ_INITIAL_CREDITS;
+    txq->pkt_sent = 0;
+    skb_queue_head_init(&txq->sk_list);
+    txq->last_retry_skb = NULL;
+    txq->nb_retry = 0;
+    txq->hwq = hwq;
+    txq->sta = sta;
+    for (i = 0; i < CONFIG_USER_MAX ; i++)
+        txq->pkt_pushed[i] = 0;
+    txq->push_limit = 0;
+    txq->tid = tid;
+#ifdef CONFIG_MAC80211_TXQ
+    txq->nb_ready_mac80211 = 0;
+#endif
+#ifdef CONFIG_ECRNX_SOFTMAC
+    txq->baw.agg_on = false;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    txq->amsdu_anchor = NULL;
+    txq->amsdu_ht_len_cap = 0;
+    txq->amsdu_vht_len_cap = 0;
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+#else /* ! CONFIG_ECRNX_SOFTMAC */
+    txq->ps_id = LEGACY_PS_ID;
+    if (idx < NX_FIRST_VIF_TXQ_IDX) {
+        int sta_idx = sta->sta_idx;
+        int tid = idx - (sta_idx * NX_NB_TXQ_PER_STA);
+        if (tid < NX_NB_TID_PER_STA)
+            txq->ndev_idx = NX_STA_NDEV_IDX(tid, sta_idx);
+        else
+            txq->ndev_idx = NDEV_NO_TXQ;
+    } else if (idx < NX_FIRST_UNK_TXQ_IDX) {
+        txq->ndev_idx = NX_BCMC_TXQ_NDEV_IDX;
+    } else {
+        txq->ndev_idx = NDEV_NO_TXQ;
+    }
+    txq->ndev = ndev;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    txq->amsdu = NULL;
+    txq->amsdu_len = 0;
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+/**
+ * ecrnx_txq_flush - Flush all buffers queued for a TXQ
+ *
+ * @ecrnx_hw: main driver data
+ * @txq: txq to flush
+ */
+void ecrnx_txq_drop_skb(struct ecrnx_txq *txq, struct sk_buff *skb, struct ecrnx_hw *ecrnx_hw, bool retry_packet)
+{
+    struct ecrnx_sw_txhdr *sw_txhdr;
+    unsigned long queued_time = 0;
+
+    skb_unlink(skb, &txq->sk_list);
+
+    sw_txhdr = ((struct ecrnx_txhdr *)skb->data)->sw_hdr;
+    /* hwq->len doesn't count skb to retry */
+#ifdef CONFIG_ECRNX_FULLMAC
+    queued_time = jiffies - sw_txhdr->jiffies;
+#endif /* CONFIG_ECRNX_SOFTMAC*/
+    trace_txq_drop_skb(skb, txq, queued_time);
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+        if (sw_txhdr->desc.host.packet_cnt > 1) {
+            struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+            list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+#ifndef CONFIG_ECRNX_ESWIN             
+                dma_unmap_single(ecrnx_hw->dev, amsdu_txhdr->dma_addr,
+                                 amsdu_txhdr->map_len, DMA_TO_DEVICE);
+#endif
+                dev_kfree_skb_any(amsdu_txhdr->skb);
+            }
+#ifdef CONFIG_ECRNX_FULLMAC
+        if (txq->amsdu == sw_txhdr)
+            txq->amsdu = NULL;
+#endif
+        }
+#endif
+        kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+#ifndef CONFIG_ECRNX_ESWIN
+        dma_unmap_single(ecrnx_hw->dev, sw_txhdr->dma_addr, sw_txhdr->map_len,
+                         DMA_TO_DEVICE);
+#endif
+    if (retry_packet) {
+        txq->nb_retry--;
+        if (txq->nb_retry == 0) {
+            WARN(skb != txq->last_retry_skb,
+                 "last dropped retry buffer is not the expected one");
+            txq->last_retry_skb = NULL;
+        }
+    }
+#ifdef CONFIG_ECRNX_SOFTMAC
+    else
+        txq->hwq->len --; // hwq->len doesn't count skb to retry
+        ieee80211_free_txskb(ecrnx_hw->hw, skb);
+#else
+        dev_kfree_skb_any(skb);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+/**
+ * ecrnx_txq_flush - Flush all buffers queued for a TXQ
+ *
+ * @ecrnx_hw: main driver data
+ * @txq: txq to flush
+ */
+void ecrnx_txq_flush(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq)
+{
+    int i, pushed = 0;
+
+    while(!skb_queue_empty(&txq->sk_list)) {
+        ecrnx_txq_drop_skb(txq, skb_peek(&txq->sk_list), ecrnx_hw, txq->nb_retry);
+    }
+
+    for (i = 0; i < CONFIG_USER_MAX; i++) {
+        pushed += txq->pkt_pushed[i];
+    }
+
+    if (pushed)
+        dev_warn(ecrnx_hw->dev, "TXQ[%d]: %d skb still pushed to the FW",
+                 txq->idx, pushed);
+}
+
+/**
+ * ecrnx_txq_deinit - De-initialize a TX queue
+ *
+ * @ecrnx_hw: Driver main data
+ * @txq: TX queue to be de-initialized
+ * Any buffer stuck in a queue will be freed.
+ */
+static void ecrnx_txq_deinit(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq)
+{
+    if (txq->idx == TXQ_INACTIVE)
+        return;
+
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+    ecrnx_txq_del_from_hw_list(txq);
+    txq->idx = TXQ_INACTIVE;
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+
+    ecrnx_txq_flush(ecrnx_hw, txq);
+}
+
+/**
+ * ecrnx_txq_vif_init - Initialize all TXQ linked to a vif
+ *
+ * @ecrnx_hw: main driver data
+ * @ecrnx_vif: Pointer on VIF
+ * @status: Intial txq status
+ *
+ * Softmac : 1 VIF TXQ per HWQ
+ *
+ * Fullmac : 1 VIF TXQ for BC/MC
+ *           1 VIF TXQ for MGMT to unknown STA
+ */
+void ecrnx_txq_vif_init(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                       u8 status)
+{
+    struct ecrnx_txq *txq;
+    int idx;
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    int ac;
+
+    idx = ecrnx_txq_vif_idx(ecrnx_vif, 0);
+    foreach_vif_txq(ecrnx_vif, txq, ac) {
+        if (txq) {
+            ecrnx_txq_init(txq, idx, status, &ecrnx_hw->hwq[ac], 0, NULL);
+#ifdef CONFIG_MAC80211_TXQ
+            if (ac != ECRNX_HWQ_BE)
+                txq->nb_ready_mac80211 = NOT_MAC80211_TXQ;
+#endif
+        }
+        idx++;
+    }
+
+#else
+    txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+    idx = ecrnx_txq_vif_idx(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+    ecrnx_txq_init(txq, idx, status, &ecrnx_hw->hwq[ECRNX_HWQ_BE], 0,
+                  &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index], ecrnx_vif->ndev);
+
+    txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+    idx = ecrnx_txq_vif_idx(ecrnx_vif, NX_UNK_TXQ_TYPE);
+    ecrnx_txq_init(txq, idx, status, &ecrnx_hw->hwq[ECRNX_HWQ_VO], TID_MGT,
+                  NULL, ecrnx_vif->ndev);
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+/**
+ * ecrnx_txq_vif_deinit - Deinitialize all TXQ linked to a vif
+ *
+ * @ecrnx_hw: main driver data
+ * @ecrnx_vif: Pointer on VIF
+ */
+void ecrnx_txq_vif_deinit(struct ecrnx_hw * ecrnx_hw, struct ecrnx_vif *ecrnx_vif)
+{
+    struct ecrnx_txq *txq;
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    int ac;
+
+    foreach_vif_txq(ecrnx_vif, txq, ac) {
+        if (txq)
+            ecrnx_txq_deinit(ecrnx_hw, txq);
+    }
+
+#else
+    txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+    ecrnx_txq_deinit(ecrnx_hw, txq);
+
+    txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+    ecrnx_txq_deinit(ecrnx_hw, txq);
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+
+/**
+ * ecrnx_txq_sta_init - Initialize TX queues for a STA
+ *
+ * @ecrnx_hw: Main driver data
+ * @ecrnx_sta: STA for which tx queues need to be initialized
+ * @status: Intial txq status
+ *
+ * This function initialize all the TXQ associated to a STA.
+ * Softmac : 1 TXQ per TID
+ *
+ * Fullmac : 1 TXQ per TID (limited to 8)
+ *           1 TXQ for MGMT
+ */
+void ecrnx_txq_sta_init(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+                       u8 status)
+{
+    struct ecrnx_txq *txq;
+    int tid, idx;
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ieee80211_sta *sta = ecrnx_to_ieee80211_sta(ecrnx_sta);
+    idx = ecrnx_txq_sta_idx(ecrnx_sta, 0);
+
+    foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+        ecrnx_txq_init(txq, idx, status, &ecrnx_hw->hwq[ecrnx_tid2hwq[tid]], tid, sta);
+        idx++;
+    }
+
+#else
+    struct ecrnx_vif *ecrnx_vif = ecrnx_hw->vif_table[ecrnx_sta->vif_idx];
+    idx = ecrnx_txq_sta_idx(ecrnx_sta, 0);
+
+    foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+        ecrnx_txq_init(txq, idx, status, &ecrnx_hw->hwq[ecrnx_tid2hwq[tid]], tid,
+                      ecrnx_sta, ecrnx_vif->ndev);
+        txq->ps_id = ecrnx_sta->uapsd_tids & (1 << tid) ? UAPSD_ID : LEGACY_PS_ID;
+        idx++;
+    }
+
+#endif /* CONFIG_ECRNX_SOFTMAC*/
+#ifndef CONFIG_ECRNX_ESWIN
+    ecrnx_ipc_sta_buffer_init(ecrnx_hw, ecrnx_sta->sta_idx);
+#endif
+}
+
+/**
+ * ecrnx_txq_sta_deinit - Deinitialize TX queues for a STA
+ *
+ * @ecrnx_hw: Main driver data
+ * @ecrnx_sta: STA for which tx queues need to be deinitialized
+ */
+void ecrnx_txq_sta_deinit(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *ecrnx_sta)
+{
+    struct ecrnx_txq *txq;
+    int tid;
+
+    foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+        ecrnx_txq_deinit(ecrnx_hw, txq);
+    }
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+/**
+ * ecrnx_txq_unk_vif_init - Initialize TXQ for unknown STA linked to a vif
+ *
+ * @ecrnx_vif: Pointer on VIF
+ */
+void ecrnx_txq_unk_vif_init(struct ecrnx_vif *ecrnx_vif)
+{
+    struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+    struct ecrnx_txq *txq;
+    int idx;
+
+    txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+    idx = ecrnx_txq_vif_idx(ecrnx_vif, NX_UNK_TXQ_TYPE);
+    ecrnx_txq_init(txq, idx, 0, &ecrnx_hw->hwq[ECRNX_HWQ_VO], TID_MGT, NULL, ecrnx_vif->ndev);
+}
+
+/**
+ * ecrnx_txq_tdls_vif_deinit - Deinitialize TXQ for unknown STA linked to a vif
+ *
+ * @ecrnx_vif: Pointer on VIF
+ */
+void ecrnx_txq_unk_vif_deinit(struct ecrnx_vif *ecrnx_vif)
+{
+    struct ecrnx_txq *txq;
+
+    txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+    ecrnx_txq_deinit(ecrnx_vif->ecrnx_hw, txq);
+}
+
+/**
+ * ecrnx_init_unk_txq - Initialize TX queue for the transmission on a offchannel
+ *
+ * @vif: Interface for which the queue has to be initialized
+ *
+ * NOTE: Offchannel txq is only active for the duration of the ROC
+ */
+void ecrnx_txq_offchan_init(struct ecrnx_vif *ecrnx_vif)
+{
+    struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+    struct ecrnx_txq *txq;
+
+    txq = &ecrnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+    ecrnx_txq_init(txq, NX_OFF_CHAN_TXQ_IDX, ECRNX_TXQ_STOP_CHAN,
+                  &ecrnx_hw->hwq[ECRNX_HWQ_VO], TID_MGT, NULL, ecrnx_vif->ndev);
+}
+
+/**
+ * ecrnx_deinit_offchan_txq - Deinitialize TX queue for offchannel
+ *
+ * @vif: Interface that manages the STA
+ *
+ * This function deintialize txq for one STA.
+ * Any buffer stuck in a queue will be freed.
+ */
+void ecrnx_txq_offchan_deinit(struct ecrnx_vif *ecrnx_vif)
+{
+    struct ecrnx_txq *txq;
+
+    txq = &ecrnx_vif->ecrnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+    ecrnx_txq_deinit(ecrnx_vif->ecrnx_hw, txq);
+}
+
+
+/**
+ * ecrnx_txq_tdls_vif_init - Initialize TXQ vif for TDLS
+ *
+ * @ecrnx_vif: Pointer on VIF
+ */
+void ecrnx_txq_tdls_vif_init(struct ecrnx_vif *ecrnx_vif)
+{
+    struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+
+    if (!(ecrnx_hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+        return;
+
+    ecrnx_txq_unk_vif_init(ecrnx_vif);
+}
+
+/**
+ * ecrnx_txq_tdls_vif_deinit - Deinitialize TXQ vif for TDLS
+ *
+ * @ecrnx_vif: Pointer on VIF
+ */
+void ecrnx_txq_tdls_vif_deinit(struct ecrnx_vif *ecrnx_vif)
+{
+    struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+
+    if (!(ecrnx_hw->wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+        return;
+
+    ecrnx_txq_unk_vif_deinit(ecrnx_vif);
+}
+/**
+ * ecrnx_txq_drop_old_traffic - Drop pkt queued for too long in a TXQ
+ *
+ * @txq: TXQ to process
+ * @ecrnx_hw: Driver main data
+ * @skb_timeout: Max queue duration, in jiffies, for this queue
+ * @dropped: Updated to inidicate if at least one skb was dropped
+ *
+ * @return Whether there is still pkt queued in this queue.
+ */
+static bool ecrnx_txq_drop_old_traffic(struct ecrnx_txq *txq, struct ecrnx_hw *ecrnx_hw,
+                                      unsigned long skb_timeout, bool *dropped)
+{
+    struct sk_buff *skb, *skb_next;
+    bool pkt_queued = false;
+    int retry_packet = txq->nb_retry;
+
+    if (txq->idx == TXQ_INACTIVE)
+        return false;
+
+    spin_lock(&ecrnx_hw->tx_lock);
+
+    skb_queue_walk_safe(&txq->sk_list, skb, skb_next) {
+
+        struct ecrnx_sw_txhdr *sw_txhdr;
+
+        if (retry_packet) {
+            // Don't drop retry packets
+            retry_packet--;
+            continue;
+        }
+
+        sw_txhdr = ((struct ecrnx_txhdr *)skb->data)->sw_hdr;
+
+        if (!time_after(jiffies, sw_txhdr->jiffies + skb_timeout)) {
+            pkt_queued = true;
+            break;
+        }
+
+        *dropped = true;
+        ECRNX_WARN("%s:skb:0x%08x,txq:0x%p, txq_idx:%d, ps_active:%d, ps_id:%d \n", __func__, skb, txq, txq->idx, txq->sta->ps.active, txq->ps_id);
+        ecrnx_txq_drop_skb(txq, skb, ecrnx_hw, false);
+        if (txq->sta && txq->sta->ps.active) {
+            txq->sta->ps.pkt_ready[txq->ps_id]--;
+            if (txq->sta->ps.pkt_ready[txq->ps_id] == 0)
+                ecrnx_set_traffic_status(ecrnx_hw, txq->sta, false, txq->ps_id);
+
+            // drop packet during PS service period
+            if (txq->sta->ps.sp_cnt[txq->ps_id]) {
+                txq->sta->ps.sp_cnt[txq->ps_id] --;
+                if (txq->push_limit)
+                    txq->push_limit--;
+                if (WARN(((txq->ps_id == UAPSD_ID) &&
+                          (txq->sta->ps.sp_cnt[txq->ps_id] == 0)),
+                         "Drop last packet of UAPSD service period")) {
+                    // TODO: inform FW to end SP
+                }
+            }
+            trace_ps_drop(txq->sta);
+        }
+    }
+
+    if (skb_queue_empty(&txq->sk_list)) {
+        ecrnx_txq_del_from_hw_list(txq);
+        txq->pkt_sent = 0;
+    }
+
+    spin_unlock(&ecrnx_hw->tx_lock);
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    /* restart netdev queue if number no more queued buffer */
+    if (unlikely(txq->status & ECRNX_TXQ_NDEV_FLOW_CTRL) &&
+        skb_queue_empty(&txq->sk_list)) {
+        txq->status &= ~ECRNX_TXQ_NDEV_FLOW_CTRL;
+        netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+        trace_txq_flowctrl_restart(txq);
+    }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+    return pkt_queued;
+}
+
+/**
+ * ecrnx_txq_drop_ap_vif_old_traffic - Drop pkt queued for too long in TXQs
+ * linked to an "AP" vif (AP, MESH, P2P_GO)
+ *
+ * @vif: Vif to process
+ * @return Whether there is still pkt queued in any TXQ.
+ */
+static bool ecrnx_txq_drop_ap_vif_old_traffic(struct ecrnx_vif *vif)
+{
+    struct ecrnx_sta *sta;
+    unsigned long timeout = (vif->ap.bcn_interval * HZ * 3) >> 10;
+    bool pkt_queued = false;
+    bool pkt_dropped = false;
+
+    // Should never be needed but still check VIF queues
+    ecrnx_txq_drop_old_traffic(ecrnx_txq_vif_get(vif, NX_BCMC_TXQ_TYPE),
+                              vif->ecrnx_hw, ECRNX_TXQ_MAX_QUEUE_JIFFIES, &pkt_dropped);
+    ecrnx_txq_drop_old_traffic(ecrnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE),
+                              vif->ecrnx_hw, ECRNX_TXQ_MAX_QUEUE_JIFFIES, &pkt_dropped);
+    ECRNX_WARN("Dropped packet in BCMC/UNK queue. \n");
+
+    list_for_each_entry(sta, &vif->ap.sta_list, list) {
+        struct ecrnx_txq *txq;
+        int tid;
+        foreach_sta_txq(sta, txq, tid, vif->ecrnx_hw) {
+            pkt_queued |= ecrnx_txq_drop_old_traffic(txq, vif->ecrnx_hw,
+                                                    timeout * sta->listen_interval,
+                                                    &pkt_dropped);
+        }
+    }
+
+    return pkt_queued;
+}
+
+/**
+ * ecrnx_txq_drop_sta_vif_old_traffic - Drop pkt queued for too long in TXQs
+ * linked to a "STA" vif. In theory this should not be required as there is no
+ * case where traffic can accumulate in a STA interface.
+ *
+ * @vif: Vif to process
+ * @return Whether there is still pkt queued in any TXQ.
+ */
+static bool ecrnx_txq_drop_sta_vif_old_traffic(struct ecrnx_vif *vif)
+{
+    struct ecrnx_txq *txq;
+    bool pkt_queued = false, pkt_dropped = false;
+    int tid;
+
+    if (vif->tdls_status == TDLS_LINK_ACTIVE) {
+        txq = ecrnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE);
+        pkt_queued |= ecrnx_txq_drop_old_traffic(txq, vif->ecrnx_hw,
+                                                ECRNX_TXQ_MAX_QUEUE_JIFFIES,
+                                                &pkt_dropped);
+        foreach_sta_txq(vif->sta.tdls_sta, txq, tid, vif->ecrnx_hw) {
+            pkt_queued |= ecrnx_txq_drop_old_traffic(txq, vif->ecrnx_hw,
+                                                    ECRNX_TXQ_MAX_QUEUE_JIFFIES,
+                                                    &pkt_dropped);
+        }
+    }
+
+    if (vif->sta.ap) {
+        foreach_sta_txq(vif->sta.ap, txq, tid, vif->ecrnx_hw) {
+            pkt_queued |= ecrnx_txq_drop_old_traffic(txq, vif->ecrnx_hw,
+                                                    ECRNX_TXQ_MAX_QUEUE_JIFFIES,
+                                                    &pkt_dropped);
+        }
+    }
+
+    if (pkt_dropped) {
+        ECRNX_WARN("Dropped packet in STA interface TXQs. \n");
+    }
+    return pkt_queued;
+}
+
+/**
+ * ecrnx_txq_cleanup_timer_cb - callack for TXQ cleaup timer
+ * Used to prevent pkt to accumulate in TXQ. The main use case is for AP
+ * interface with client in Power Save mode but just in case all TXQs are
+ * checked.
+ *
+ * @t: timer structure
+ */
+static void ecrnx_txq_cleanup_timer_cb(struct timer_list *t)
+{
+    struct ecrnx_hw *ecrnx_hw = from_timer(ecrnx_hw, t, txq_cleanup);
+    struct ecrnx_vif *vif;
+    bool pkt_queue = false;
+
+    list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+        switch (ECRNX_VIF_TYPE(vif)) {
+            case NL80211_IFTYPE_AP:
+            case NL80211_IFTYPE_P2P_GO:
+            case NL80211_IFTYPE_MESH_POINT:
+                pkt_queue |= ecrnx_txq_drop_ap_vif_old_traffic(vif);
+                break;
+            case NL80211_IFTYPE_STATION:
+            case NL80211_IFTYPE_P2P_CLIENT:
+                 pkt_queue |= ecrnx_txq_drop_sta_vif_old_traffic(vif);
+                 break;
+            case NL80211_IFTYPE_AP_VLAN:
+            case NL80211_IFTYPE_MONITOR:
+            default:
+                continue;
+        }
+    }
+
+    if (pkt_queue)
+        mod_timer(t, jiffies + ECRNX_TXQ_CLEANUP_INTERVAL);
+}
+
+/**
+ * ecrnx_txq_start_cleanup_timer - Start 'cleanup' timer if not started
+ *
+ * @ecrnx_hw: Driver main data
+ */
+void ecrnx_txq_start_cleanup_timer(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta)
+{
+    if (sta && !is_multicast_sta(sta->sta_idx) &&
+        !timer_pending(&ecrnx_hw->txq_cleanup))
+        mod_timer(&ecrnx_hw->txq_cleanup, jiffies + ECRNX_TXQ_CLEANUP_INTERVAL);
+}
+
+/**
+ * ecrnx_txq_prepare - Global initialization of txq
+ *
+ * @ecrnx_hw: Driver main data
+ */
+void ecrnx_txq_prepare(struct ecrnx_hw *ecrnx_hw)
+{
+    int i;
+
+    for (i = 0; i < NX_NB_TXQ; i++) {
+        ecrnx_hw->txq[i].idx = TXQ_INACTIVE;
+    }
+
+    timer_setup(&ecrnx_hw->txq_cleanup, ecrnx_txq_cleanup_timer_cb, 0);
+}
+#endif
+
+/******************************************************************************
+ * Start/Stop functions
+ *****************************************************************************/
+/**
+ * ecrnx_txq_add_to_hw_list - Add TX queue to a HW queue schedule list.
+ *
+ * @txq: TX queue to add
+ *
+ * Add the TX queue if not already present in the HW queue list.
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_add_to_hw_list(struct ecrnx_txq *txq)
+{
+    if (!(txq->status & ECRNX_TXQ_IN_HWQ_LIST)) {
+        trace_txq_add_to_hw(txq);
+        txq->status |= ECRNX_TXQ_IN_HWQ_LIST;
+        list_add_tail(&txq->sched_list, &txq->hwq->list);
+        txq->hwq->need_processing = true;
+    }
+}
+
+/**
+ * ecrnx_txq_del_from_hw_list - Delete TX queue from a HW queue schedule list.
+ *
+ * @txq: TX queue to delete
+ *
+ * Remove the TX queue from the HW queue list if present.
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_del_from_hw_list(struct ecrnx_txq *txq)
+{
+    if (txq->status & ECRNX_TXQ_IN_HWQ_LIST) {
+        trace_txq_del_from_hw(txq);
+        txq->status &= ~ECRNX_TXQ_IN_HWQ_LIST;
+        list_del(&txq->sched_list);
+    }
+}
+
+/**
+ * ecrnx_txq_skb_ready - Check if skb are available for the txq
+ *
+ * @txq: Pointer on txq
+ * @return True if there are buffer ready to be pushed on this txq,
+ * false otherwise
+ */
+static inline bool ecrnx_txq_skb_ready(struct ecrnx_txq *txq)
+{
+#ifdef CONFIG_MAC80211_TXQ
+    if (txq->nb_ready_mac80211 != NOT_MAC80211_TXQ)
+        return ((txq->nb_ready_mac80211 > 0) || !skb_queue_empty(&txq->sk_list));
+    else
+#endif
+    return !skb_queue_empty(&txq->sk_list);
+}
+
+/**
+ * ecrnx_txq_start - Try to Start one TX queue
+ *
+ * @txq: TX queue to start
+ * @reason: reason why the TX queue is started (among ECRNX_TXQ_STOP_xxx)
+ *
+ * Re-start the TX queue for one reason.
+ * If after this the txq is no longer stopped and some buffers are ready,
+ * the TX queue is also added to HW queue list.
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_start(struct ecrnx_txq *txq, u16 reason)
+{
+    BUG_ON(txq==NULL);
+    if (txq->idx != TXQ_INACTIVE && (txq->status & reason))
+    {
+        trace_txq_start(txq, reason);
+        txq->status &= ~reason;
+        if (!ecrnx_txq_is_stopped(txq) && ecrnx_txq_skb_ready(txq))
+            ecrnx_txq_add_to_hw_list(txq);
+    }
+}
+
+/**
+ * ecrnx_txq_stop - Stop one TX queue
+ *
+ * @txq: TX queue to stop
+ * @reason: reason why the TX queue is stopped (among ECRNX_TXQ_STOP_xxx)
+ *
+ * Stop the TX queue. It will remove the TX queue from HW queue list
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_stop(struct ecrnx_txq *txq, u16 reason)
+{
+    BUG_ON(txq==NULL);
+    if (txq->idx != TXQ_INACTIVE)
+    {
+        trace_txq_stop(txq, reason);
+        txq->status |= reason;
+        ecrnx_txq_del_from_hw_list(txq);
+    }
+}
+
+
+/**
+ * ecrnx_txq_sta_start - Start all the TX queue linked to a STA
+ *
+ * @sta: STA whose TX queues must be re-started
+ * @reason: Reason why the TX queue are restarted (among ECRNX_TXQ_STOP_xxx)
+ * @ecrnx_hw: Driver main data
+ *
+ * This function will re-start all the TX queues of the STA for the reason
+ * specified. It can be :
+ * - ECRNX_TXQ_STOP_STA_PS: the STA is no longer in power save mode
+ * - ECRNX_TXQ_STOP_VIF_PS: the VIF is in power save mode (p2p absence)
+ * - ECRNX_TXQ_STOP_CHAN: the STA's VIF is now on the current active channel
+ *
+ * Any TX queue with buffer ready and not Stopped for other reasons, will be
+ * added to the HW queue list
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_sta_start(struct ecrnx_sta *ecrnx_sta, u16 reason
+#ifdef CONFIG_ECRNX_FULLMAC
+                        , struct ecrnx_hw *ecrnx_hw
+#endif
+                        )
+{
+    struct ecrnx_txq *txq;
+    int tid;
+
+    trace_txq_sta_start(ecrnx_sta->sta_idx);
+       ECRNX_DBG("%s-%d:ecrnx_txq_stop,reaosn:0x%x,sta:0x%08x,sta_index:0x%08x \n", __func__, __LINE__, reason, ecrnx_sta, ecrnx_sta->sta_idx);
+    foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+        ecrnx_txq_start(txq, reason);
+
+        if (txq->idx != TXQ_INACTIVE && !skb_queue_empty(&txq->sk_list))
+        {
+            ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+        }
+    }
+}
+
+
+/**
+ * ecrnx_stop_sta_txq - Stop all the TX queue linked to a STA
+ *
+ * @sta: STA whose TX queues must be stopped
+ * @reason: Reason why the TX queue are stopped (among ECRNX_TX_STOP_xxx)
+ * @ecrnx_hw: Driver main data
+ *
+ * This function will stop all the TX queues of the STA for the reason
+ * specified. It can be :
+ * - ECRNX_TXQ_STOP_STA_PS: the STA is in power save mode
+ * - ECRNX_TXQ_STOP_VIF_PS: the VIF is in power save mode (p2p absence)
+ * - ECRNX_TXQ_STOP_CHAN: the STA's VIF is not on the current active channel
+ *
+ * Any TX queue present in a HW queue list will be removed from this list.
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_sta_stop(struct ecrnx_sta *ecrnx_sta, u16 reason
+#ifdef CONFIG_ECRNX_FULLMAC
+                       , struct ecrnx_hw *ecrnx_hw
+#endif
+                       )
+{
+    struct ecrnx_txq *txq;
+    int tid;
+
+    if (!ecrnx_sta)
+        return;
+
+    trace_txq_sta_stop(ecrnx_sta->sta_idx);
+    foreach_sta_txq(ecrnx_sta, txq, tid, ecrnx_hw) {
+       ECRNX_DBG("%s-%d:stop_reaosn:0x%x,sta:0x%08x,sta_index:0x%08x ,txq:0x%p,tid:%d \n", __func__, __LINE__, reason, ecrnx_sta, ecrnx_sta->sta_idx, txq, tid);
+        ecrnx_txq_stop(txq, reason);
+    }
+}
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+void ecrnx_txq_tdls_sta_start(struct ecrnx_sta *ecrnx_sta, u16 reason,
+                             struct ecrnx_hw *ecrnx_hw)
+#else
+void ecrnx_txq_tdls_sta_start(struct ecrnx_vif *ecrnx_vif, u16 reason,
+                             struct ecrnx_hw *ecrnx_hw)
+#endif
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    trace_txq_vif_start(ecrnx_sta->vif_idx);
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+
+    if (ecrnx_sta->tdls.active) {
+        ecrnx_txq_sta_start(ecrnx_sta, reason);
+    }
+
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+#else
+    trace_txq_vif_start(ecrnx_vif->vif_index);
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+
+    if (ecrnx_vif->sta.tdls_sta)
+        ecrnx_txq_sta_start(ecrnx_vif->sta.tdls_sta, reason, ecrnx_hw);
+
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+#endif
+}
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+void ecrnx_txq_tdls_sta_stop(struct ecrnx_sta *ecrnx_sta, u16 reason,
+                            struct ecrnx_hw *ecrnx_hw)
+#else
+void ecrnx_txq_tdls_sta_stop(struct ecrnx_vif *ecrnx_vif, u16 reason,
+                            struct ecrnx_hw *ecrnx_hw)
+#endif
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    trace_txq_vif_stop(ecrnx_sta->vif_idx);
+
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+
+    if (ecrnx_sta->tdls.active) {
+        ecrnx_txq_sta_stop(ecrnx_sta, reason);
+    }
+
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+#else
+    trace_txq_vif_stop(ecrnx_vif->vif_index);
+
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+
+    if (ecrnx_vif->sta.tdls_sta)
+        ecrnx_txq_sta_stop(ecrnx_vif->sta.tdls_sta, reason, ecrnx_hw);
+
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+#endif
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+static inline
+void ecrnx_txq_vif_for_each_sta(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                               void (*f)(struct ecrnx_sta *, u16, struct ecrnx_hw *),
+                               u16 reason)
+{
+    switch (ECRNX_VIF_TYPE(ecrnx_vif)) {
+    case NL80211_IFTYPE_STATION:
+    case NL80211_IFTYPE_P2P_CLIENT:
+    {
+        if (ecrnx_vif->tdls_status == TDLS_LINK_ACTIVE)
+            f(ecrnx_vif->sta.tdls_sta, reason, ecrnx_hw);
+#ifdef CONFIG_ECRNX_P2P
+        if (!(ecrnx_vif->sta.ap == NULL))
+#else
+               if (!WARN_ON(ecrnx_vif->sta.ap == NULL))
+#endif
+            f(ecrnx_vif->sta.ap, reason, ecrnx_hw);
+        break;
+    }
+    case NL80211_IFTYPE_AP_VLAN:
+    {
+        ecrnx_vif = ecrnx_vif->ap_vlan.master;
+               struct ecrnx_sta *sta;
+        list_for_each_entry(sta, &ecrnx_vif->ap.sta_list, list) {
+            f(sta, reason, ecrnx_hw);
+        }
+        break;
+    }
+    case NL80211_IFTYPE_AP:
+    case NL80211_IFTYPE_MESH_POINT:
+    case NL80211_IFTYPE_P2P_GO:
+    {
+        struct ecrnx_sta *sta;
+        list_for_each_entry(sta, &ecrnx_vif->ap.sta_list, list) {
+            f(sta, reason, ecrnx_hw);
+        }
+        break;
+    }
+    default:
+        BUG();
+        break;
+    }
+}
+
+#endif
+
+/**
+ * ecrnx_txq_vif_start - START TX queues of all STA associated to the vif
+ *                      and vif's TXQ
+ *
+ * @vif: Interface to start
+ * @reason: Start reason (ECRNX_TXQ_STOP_CHAN or ECRNX_TXQ_STOP_VIF_PS)
+ * @ecrnx_hw: Driver main data
+ *
+ * Iterate over all the STA associated to the vif and re-start them for the
+ * reason @reason
+ * Take tx_lock
+ */
+void ecrnx_txq_vif_start(struct ecrnx_vif *ecrnx_vif, u16 reason,
+                        struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_txq *txq;
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ecrnx_sta *ecrnx_sta;
+    int ac;
+#endif
+
+    trace_txq_vif_start(ecrnx_vif->vif_index);
+
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    list_for_each_entry(ecrnx_sta, &ecrnx_vif->stations, list) {
+        if ((!ecrnx_vif->roc_tdls) ||
+            (ecrnx_sta->tdls.active && ecrnx_vif->roc_tdls && ecrnx_sta->tdls.chsw_en))
+            ecrnx_txq_sta_start(ecrnx_sta, reason);
+    }
+
+    foreach_vif_txq(ecrnx_vif, txq, ac) {
+        if (txq)
+            ecrnx_txq_start(txq, reason);
+    }
+#else
+    //Reject if monitor interface
+    if (ecrnx_vif->wdev.iftype == NL80211_IFTYPE_MONITOR)
+        goto end;
+
+    if (ecrnx_vif->roc_tdls && ecrnx_vif->sta.tdls_sta && ecrnx_vif->sta.tdls_sta->tdls.chsw_en) {
+        ecrnx_txq_sta_start(ecrnx_vif->sta.tdls_sta, reason, ecrnx_hw);
+    }
+    if (!ecrnx_vif->roc_tdls) {
+        ecrnx_txq_vif_for_each_sta(ecrnx_hw, ecrnx_vif, ecrnx_txq_sta_start, reason);
+    }
+
+    txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+    ecrnx_txq_start(txq, reason);
+    txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+    ecrnx_txq_start(txq, reason);
+
+end:
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+}
+
+
+/**
+ * ecrnx_txq_vif_stop - STOP TX queues of all STA associated to the vif
+ *
+ * @vif: Interface to stop
+ * @arg: Stop reason (ECRNX_TXQ_STOP_CHAN or ECRNX_TXQ_STOP_VIF_PS)
+ * @ecrnx_hw: Driver main data
+ *
+ * Iterate over all the STA associated to the vif and stop them for the
+ * reason ECRNX_TXQ_STOP_CHAN or ECRNX_TXQ_STOP_VIF_PS
+ * Take tx_lock
+ */
+void ecrnx_txq_vif_stop(struct ecrnx_vif *ecrnx_vif, u16 reason,
+                       struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_txq *txq;
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ecrnx_sta *sta;
+    int ac;
+#endif
+
+    trace_txq_vif_stop(ecrnx_vif->vif_index);
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    list_for_each_entry(sta, &ecrnx_vif->stations, list) {
+        ecrnx_txq_sta_stop(sta, reason);
+    }
+
+    foreach_vif_txq(ecrnx_vif, txq, ac) {
+        if (txq)
+        {
+            ecrnx_txq_stop(txq, reason);
+        }
+    }
+
+#else
+    //Reject if monitor interface
+    if (ecrnx_vif->wdev.iftype == NL80211_IFTYPE_MONITOR)
+        goto end;
+
+    ecrnx_txq_vif_for_each_sta(ecrnx_hw, ecrnx_vif, ecrnx_txq_sta_stop, reason);
+
+    txq = ecrnx_txq_vif_get(ecrnx_vif, NX_BCMC_TXQ_TYPE);
+    ecrnx_txq_stop(txq, reason);
+    txq = ecrnx_txq_vif_get(ecrnx_vif, NX_UNK_TXQ_TYPE);
+    ecrnx_txq_stop(txq, reason);
+
+end:
+#endif /* CONFIG_ECRNX_SOFTMAC*/
+
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+/**
+ * ecrnx_start_offchan_txq - START TX queue for offchannel frame
+ *
+ * @ecrnx_hw: Driver main data
+ */
+void ecrnx_txq_offchan_start(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_txq *txq;
+
+    txq = &ecrnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+    ecrnx_txq_start(txq, ECRNX_TXQ_STOP_CHAN);
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+    ECRNX_DBG("%s-%d:ecrnx_txq_start,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_CHAN);
+}
+
+/**
+ * ecrnx_switch_vif_sta_txq - Associate TXQ linked to a STA to a new vif
+ *
+ * @sta: STA whose txq must be switched
+ * @old_vif: Vif currently associated to the STA (may no longer be active)
+ * @new_vif: vif which should be associated to the STA for now on
+ *
+ * This function will switch the vif (i.e. the netdev) associated to all STA's
+ * TXQ. This is used when AP_VLAN interface are created.
+ * If one STA is associated to an AP_vlan vif, it will be moved from the master
+ * AP vif to the AP_vlan vif.
+ * If an AP_vlan vif is removed, then STA will be moved back to mastert AP vif.
+ *
+ */
+void ecrnx_txq_sta_switch_vif(struct ecrnx_sta *sta, struct ecrnx_vif *old_vif,
+                             struct ecrnx_vif *new_vif)
+{
+    struct ecrnx_hw *ecrnx_hw = new_vif->ecrnx_hw;
+    struct ecrnx_txq *txq;
+    int i;
+
+    /* start TXQ on the new interface, and update ndev field in txq */
+    if (!netif_carrier_ok(new_vif->ndev))
+        netif_carrier_on(new_vif->ndev);
+    txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);
+    for (i = 0; i < NX_NB_TID_PER_STA; i++, txq++) {
+        txq->ndev = new_vif->ndev;
+        netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+    }
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/******************************************************************************
+ * TXQ queue/schedule functions
+ *****************************************************************************/
+/**
+ * ecrnx_txq_queue_skb - Queue a buffer in a TX queue
+ *
+ * @skb: Buffer to queue
+ * @txq: TX Queue in which the buffer must be added
+ * @ecrnx_hw: Driver main data
+ * @retry: Should it be queued in the retry list
+ *
+ * @return: Retrun 1 if txq has been added to hwq list, 0 otherwise
+ *
+ * Add a buffer in the buffer list of the TX queue
+ * and add this TX queue in the HW queue list if the txq is not stopped.
+ * If this is a retry packet it is added after the last retry packet or at the
+ * beginning if there is no retry packet queued.
+ *
+ * If the STA is in PS mode and this is the first packet queued for this txq
+ * update TIM.
+ *
+ * To be called with tx_lock hold
+ */
+int ecrnx_txq_queue_skb(struct sk_buff *skb, struct ecrnx_txq *txq,
+                       struct ecrnx_hw *ecrnx_hw,  bool retry,
+                       struct sk_buff *skb_prev)
+{
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    if (unlikely(txq->sta && txq->sta->ps.active)) {
+        txq->sta->ps.pkt_ready[txq->ps_id]++;
+        trace_ps_queue(txq->sta);
+
+        if (txq->sta->ps.pkt_ready[txq->ps_id] == 1) {
+            ecrnx_set_traffic_status(ecrnx_hw, txq->sta, true, txq->ps_id);
+        }
+    }
+#endif
+
+    if (!retry) {
+        /* add buffer in the sk_list */
+        if (skb_prev)
+            skb_append(skb_prev, skb, &txq->sk_list);
+        else
+        skb_queue_tail(&txq->sk_list, skb);
+#ifdef CONFIG_ECRNX_FULLMAC
+        // to update for SOFTMAC
+        ecrnx_ipc_sta_buffer(ecrnx_hw, txq->sta, txq->tid,
+                            ((struct ecrnx_txhdr *)skb->data)->sw_hdr->frame_len);
+        ecrnx_txq_start_cleanup_timer(ecrnx_hw, txq->sta);
+#endif
+    } else {
+        if (txq->last_retry_skb)
+            skb_append(txq->last_retry_skb, skb, &txq->sk_list);
+        else
+            skb_queue_head(&txq->sk_list, skb);
+
+        txq->last_retry_skb = skb;
+        txq->nb_retry++;
+    }
+
+    trace_txq_queue_skb(skb, txq, retry);
+
+    /* Flowctrl corresponding netdev queue if needed */
+#ifdef CONFIG_ECRNX_FULLMAC
+    /* If too many buffer are queued for this TXQ stop netdev queue */
+    if ((txq->ndev_idx != NDEV_NO_TXQ) &&
+        (skb_queue_len(&txq->sk_list) > ECRNX_NDEV_FLOW_CTRL_STOP)) {
+        txq->status |= ECRNX_TXQ_NDEV_FLOW_CTRL;
+        netif_stop_subqueue(txq->ndev, txq->ndev_idx);
+        trace_txq_flowctrl_stop(txq);
+    }
+#else /* ! CONFIG_ECRNX_FULLMAC */
+
+    if (!retry && ++txq->hwq->len == txq->hwq->len_stop) {
+         trace_hwq_flowctrl_stop(txq->hwq->id);
+         ieee80211_stop_queue(ecrnx_hw->hw, txq->hwq->id);
+         ecrnx_hw->stats.queues_stops++;
+     }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+    //ECRNX_DBG("txq status: 0x%x \n", txq->status);
+    /* add it in the hwq list if not stopped and not yet present */
+    if (!ecrnx_txq_is_stopped(txq)) {
+        ecrnx_txq_add_to_hw_list(txq);
+        return 1;
+    }
+
+    return 0;
+}
+
+/**
+ * ecrnx_txq_confirm_any - Process buffer confirmed by fw
+ *
+ * @ecrnx_hw: Driver main data
+ * @txq: TX Queue
+ * @hwq: HW Queue
+ * @sw_txhdr: software descriptor of the confirmed packet
+ *
+ * Process a buffer returned by the fw. It doesn't check buffer status
+ * and only does systematic counter update:
+ * - hw credit
+ * - buffer pushed to fw
+ *
+ * To be called with tx_lock hold
+ */
+void ecrnx_txq_confirm_any(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq,
+                          struct ecrnx_hwq *hwq, struct ecrnx_sw_txhdr *sw_txhdr)
+{
+    int user = 0;
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    int group_id;
+
+    user = ECRNX_MUMIMO_INFO_POS_ID(sw_txhdr->desc.host.mumimo_info);
+    group_id = ECRNX_MUMIMO_INFO_GROUP_ID(sw_txhdr->desc.host.mumimo_info);
+
+    if ((txq->idx != TXQ_INACTIVE) &&
+        (txq->pkt_pushed[user] == 1) &&
+        (txq->status & ECRNX_TXQ_STOP_MU_POS)){
+            ecrnx_txq_start(txq, ECRNX_TXQ_STOP_MU_POS);
+            ECRNX_DBG("%s-%d:ecrnx_txq_start,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_MU_POS);
+        }
+
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+    if (txq->pkt_pushed[user])
+        txq->pkt_pushed[user]--;
+
+    hwq->credits[user]++;
+    hwq->need_processing = true;
+    ecrnx_hw->stats.cfm_balance[hwq->id]--;
+}
+
+/******************************************************************************
+ * HWQ processing
+ *****************************************************************************/
+static inline
+bool ecrnx_txq_take_mu_lock(struct ecrnx_hw *ecrnx_hw)
+{
+    bool res = false;
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    if (ecrnx_hw->mod_params->mutx)
+        res = (down_trylock(&ecrnx_hw->mu.lock) == 0);
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+    return res;
+}
+
+static inline
+void ecrnx_txq_release_mu_lock(struct ecrnx_hw *ecrnx_hw)
+{
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    up(&ecrnx_hw->mu.lock);
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+}
+
+static inline
+void ecrnx_txq_set_mu_info(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq,
+                          int group_id, int pos)
+{
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    trace_txq_select_mu_group(txq, group_id, pos);
+    if (group_id) {
+        txq->mumimo_info = group_id | (pos << 6);
+        ecrnx_mu_set_active_group(ecrnx_hw, group_id);
+    } else
+        txq->mumimo_info = 0;
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+}
+
+static inline
+s8 ecrnx_txq_get_credits(struct ecrnx_txq *txq)
+{
+    s8 cred = txq->credits;
+    /* if destination is in PS mode, push_limit indicates the maximum
+       number of packet that can be pushed on this txq. */
+    if (txq->push_limit && (cred > txq->push_limit)) {
+        cred = txq->push_limit;
+    }
+    return cred;
+}
+
+/**
+ * skb_queue_extract - Extract buffer from skb list
+ *
+ * @list: List of skb to extract from
+ * @head: List of skb to append to
+ * @nb_elt: Number of skb to extract
+ *
+ * extract the first @nb_elt of @list and append them to @head
+ * It is assume that:
+ * - @list contains more that @nb_elt
+ * - There is no need to take @list nor @head lock to modify them
+ */
+static inline void skb_queue_extract(struct sk_buff_head *list,
+                                     struct sk_buff_head *head, int nb_elt)
+{
+    int i;
+    struct sk_buff *first, *last, *ptr;
+
+    first = ptr = list->next;
+    for (i = 0; i < nb_elt; i++) {
+        ptr = ptr->next;
+    }
+    last = ptr->prev;
+
+    /* unlink nb_elt in list */
+    list->qlen -= nb_elt;
+    list->next = ptr;
+    ptr->prev = (struct sk_buff *)list;
+
+    /* append nb_elt at end of head */
+    head->qlen += nb_elt;
+    last->next = (struct sk_buff *)head;
+    head->prev->next = first;
+    first->prev = head->prev;
+    head->prev = last;
+}
+
+
+#ifdef CONFIG_MAC80211_TXQ
+/**
+ * ecrnx_txq_mac80211_dequeue - Dequeue buffer from mac80211 txq and
+ *                             add them to push list
+ *
+ * @ecrnx_hw: Main driver data
+ * @sk_list: List of buffer to push (initialized without lock)
+ * @txq: TXQ to dequeue buffers from
+ * @max: Max number of buffer to dequeue
+ *
+ * Dequeue buffer from mac80211 txq, prepare them for transmission and chain them
+ * to the list of buffer to push.
+ *
+ * @return true if no more buffer are queued in mac80211 txq and false otherwise.
+ */
+static bool ecrnx_txq_mac80211_dequeue(struct ecrnx_hw *ecrnx_hw,
+                                      struct sk_buff_head *sk_list,
+                                      struct ecrnx_txq *txq, int max)
+{
+    struct ieee80211_txq *mac_txq;
+    struct sk_buff *skb;
+    unsigned long mac_txq_len;
+
+    if (txq->nb_ready_mac80211 == NOT_MAC80211_TXQ)
+        return true;
+
+    mac_txq = container_of((void *)txq, struct ieee80211_txq, drv_priv);
+
+    for (; max > 0; max--) {
+        skb = ecrnx_tx_dequeue_prep(ecrnx_hw, mac_txq);
+        if (skb == NULL)
+            return true;
+
+        __skb_queue_tail(sk_list, skb);
+    }
+
+    /* re-read mac80211 txq current length.
+       It is mainly for debug purpose to trace dropped packet. There is no
+       problems to have nb_ready_mac80211 != actual mac80211 txq length */
+    ieee80211_txq_get_depth(mac_txq, &mac_txq_len, NULL);
+    if (txq->nb_ready_mac80211 > mac_txq_len)
+        trace_txq_drop(txq, txq->nb_ready_mac80211 - mac_txq_len);
+    txq->nb_ready_mac80211 = mac_txq_len;
+
+    return (txq->nb_ready_mac80211 == 0);
+}
+#endif
+
+/**
+ * ecrnx_txq_get_skb_to_push - Get list of buffer to push for one txq
+ *
+ * @ecrnx_hw: main driver data
+ * @hwq: HWQ on wich buffers will be pushed
+ * @txq: TXQ to get buffers from
+ * @user: user postion to use
+ * @sk_list_push: list to update
+ *
+ *
+ * This function will returned a list of buffer to push for one txq.
+ * It will take into account the number of credit of the HWQ for this user
+ * position and TXQ (and push_limit).
+ * This allow to get a list that can be pushed without having to test for
+ * hwq/txq status after each push
+ *
+ * If a MU group has been selected for this txq, it will also update the
+ * counter for the group
+ *
+ * @return true if txq no longer have buffer ready after the ones returned.
+ *         false otherwise
+ */
+static
+bool ecrnx_txq_get_skb_to_push(struct ecrnx_hw *ecrnx_hw, struct ecrnx_hwq *hwq,
+                              struct ecrnx_txq *txq, int user,
+                              struct sk_buff_head *sk_list_push)
+{
+    int nb_ready = skb_queue_len(&txq->sk_list);
+    int credits = min_t(int, ecrnx_txq_get_credits(txq), hwq->credits[user]);
+    bool res = false;
+
+    __skb_queue_head_init(sk_list_push);
+
+    if (credits >= nb_ready) {
+        skb_queue_splice_init(&txq->sk_list, sk_list_push);
+#ifdef CONFIG_MAC80211_TXQ
+        res = ecrnx_txq_mac80211_dequeue(ecrnx_hw, sk_list_push, txq, credits - nb_ready);
+        credits = skb_queue_len(sk_list_push);
+#else
+        res = true;
+        credits = nb_ready;
+#endif
+    } else {
+        skb_queue_extract(&txq->sk_list, sk_list_push, credits);
+
+        /* When processing PS service period (i.e. push_limit != 0), no longer
+           process this txq if the buffers extracted will complete the SP for
+           this txq */
+        if (txq->push_limit && (credits == txq->push_limit))
+            res = true;
+    }
+
+    ecrnx_mu_set_active_sta(ecrnx_hw, ecrnx_txq_2_sta(txq), credits);
+
+    return res;
+}
+
+/**
+ * ecrnx_txq_select_user - Select User queue for a txq
+ *
+ * @ecrnx_hw: main driver data
+ * @mu_lock: true is MU lock is taken
+ * @txq: TXQ to select MU group for
+ * @hwq: HWQ for the TXQ
+ * @user: Updated with user position selected
+ *
+ * @return false if it is no possible to process this txq.
+ *         true otherwise
+ *
+ * This function selects the MU group to use for a TXQ.
+ * The selection is done as follow:
+ *
+ * - return immediately for STA that don't belongs to any group and select
+ *   group 0 / user 0
+ *
+ * - If MU tx is disabled (by user mutx_on, or because mu group are being
+ *   updated !mu_lock), select group 0 / user 0
+ *
+ * - Use the best group selected by @ecrnx_mu_group_sta_select.
+ *
+ *   Each time a group is selected (except for the first case where sta
+ *   doesn't belongs to a MU group), the function checks that no buffer is
+ *   pending for this txq on another user position. If this is the case stop
+ *   the txq (ECRNX_TXQ_STOP_MU_POS) and return false.
+ *
+ */
+static
+bool ecrnx_txq_select_user(struct ecrnx_hw *ecrnx_hw, bool mu_lock,
+                          struct ecrnx_txq *txq, struct ecrnx_hwq *hwq, int *user)
+{
+    int pos = 0;
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    int id, group_id = 0;
+    struct ecrnx_sta *sta = ecrnx_txq_2_sta(txq);
+
+    /* for sta that belong to no group return immediately */
+    if (!sta || !sta->group_info.cnt)
+        goto end;
+
+    /* If MU is disabled, need to check user */
+    if (!ecrnx_hw->mod_params->mutx_on || !mu_lock)
+        goto check_user;
+
+    /* Use the "best" group selected */
+    group_id = sta->group_info.group;
+
+    if (group_id > 0)
+        pos = ecrnx_mu_group_sta_get_pos(ecrnx_hw, sta, group_id);
+
+  check_user:
+    /* check that we can push on this user position */
+#if CONFIG_USER_MAX == 2
+    id = (pos + 1) & 0x1;
+    if (txq->pkt_pushed[id]) {
+        ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_MU_POS);
+        ECRNX_DBG("%s-%d:ecrnx_txq_stop,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_MU_POS);
+        return false;
+    }
+
+#else
+    for (id = 0 ; id < CONFIG_USER_MAX ; id++) {
+        if (id != pos && txq->pkt_pushed[id]) {
+            ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_MU_POS);
+            ECRNX_DBG("%s-%d:ecrnx_txq_stop,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_MU_POS);
+            return false;
+        }
+    }
+#endif
+
+  end:
+    ecrnx_txq_set_mu_info(ecrnx_hw, txq, group_id, pos);
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+    *user = pos;
+    return true;
+}
+
+
+/**
+ * ecrnx_hwq_process - Process one HW queue list
+ *
+ * @ecrnx_hw: Driver main data
+ * @hw_queue: HW queue index to process
+ *
+ * The function will iterate over all the TX queues linked in this HW queue
+ * list. For each TX queue, push as many buffers as possible in the HW queue.
+ * (NB: TX queue have at least 1 buffer, otherwise it wouldn't be in the list)
+ * - If TX queue no longer have buffer, remove it from the list and check next
+ *   TX queue
+ * - If TX queue no longer have credits or has a push_limit (PS mode) and it
+ *   is reached , remove it from the list and check next TX queue
+ * - If HW queue is full, update list head to start with the next TX queue on
+ *   next call if current TX queue already pushed "too many" pkt in a row, and
+ *   return
+ *
+ * To be called when HW queue list is modified:
+ * - when a buffer is pushed on a TX queue
+ * - when new credits are received
+ * - when a STA returns from Power Save mode or receives traffic request.
+ * - when Channel context change
+ *
+ * To be called with tx_lock hold
+ */
+#define ALL_HWQ_MASK  ((1 << CONFIG_USER_MAX) - 1)
+
+void ecrnx_hwq_process(struct ecrnx_hw *ecrnx_hw, struct ecrnx_hwq *hwq)
+{
+    struct ecrnx_txq *txq, *next;
+    int user, credit_map = 0;
+    bool mu_enable;
+
+    trace_process_hw_queue(hwq);
+
+    hwq->need_processing = false;
+
+    mu_enable = ecrnx_txq_take_mu_lock(ecrnx_hw);
+    if (!mu_enable)
+        credit_map = ALL_HWQ_MASK - 1;
+
+    list_for_each_entry_safe(txq, next, &hwq->list, sched_list) {
+        struct ecrnx_txhdr *txhdr = NULL;
+        struct sk_buff_head sk_list_push;
+        struct sk_buff *skb;
+        bool txq_empty;
+
+        trace_process_txq(txq);
+
+        /* sanity check for debug */
+        BUG_ON(!(txq->status & ECRNX_TXQ_IN_HWQ_LIST));
+        BUG_ON(txq->idx == TXQ_INACTIVE);
+        BUG_ON(txq->credits <= 0);
+        BUG_ON(!ecrnx_txq_skb_ready(txq));
+
+        if (!ecrnx_txq_select_user(ecrnx_hw, mu_enable, txq, hwq, &user))
+            continue;
+
+        if (!hwq->credits[user]) {
+            credit_map |= BIT(user);
+            if (credit_map == ALL_HWQ_MASK)
+                break;
+            continue;
+        }
+
+        txq_empty = ecrnx_txq_get_skb_to_push(ecrnx_hw, hwq, txq, user,
+                                             &sk_list_push);
+
+        while ((skb = __skb_dequeue(&sk_list_push)) != NULL) {
+            txhdr = (struct ecrnx_txhdr *)skb->data;
+            ecrnx_tx_push(ecrnx_hw, txhdr, 0);
+        }
+
+        if (txq_empty) {
+            ecrnx_txq_del_from_hw_list(txq);
+            txq->pkt_sent = 0;
+#if defined CONFIG_ECRNX_SOFTMAC && defined CONFIG_ECRNX_AMSDUS_TX
+            if (txq->amsdu_ht_len_cap)
+                ieee80211_amsdu_ctl(ecrnx_hw->hw, txq->sta, txq->tid, NULL,
+                                    0, 0, false);
+#endif
+        } else if ((hwq->credits[user] == 0) &&
+                   ecrnx_txq_is_scheduled(txq)) {
+            /* txq not empty,
+               - To avoid starving need to process other txq in the list
+               - For better aggregation, need to send "as many consecutive
+               pkt as possible" for he same txq
+               ==> Add counter to trigger txq switch
+            */
+            if (txq->pkt_sent > hwq->size) {
+                txq->pkt_sent = 0;
+                list_rotate_left(&hwq->list);
+            }
+        }
+
+#ifdef CONFIG_ECRNX_FULLMAC
+        /* Unable to complete PS traffic request because of hwq credit */
+        if (txq->push_limit && txq->sta) {
+            if (txq->ps_id == LEGACY_PS_ID) {
+                /* for legacy PS abort SP and wait next ps-poll */
+                txq->sta->ps.sp_cnt[txq->ps_id] -= txq->push_limit;
+                txq->push_limit = 0;
+            }
+            /* for u-apsd need to complete the SP to send EOSP frame */
+        }
+
+        /* restart netdev queue if number of queued buffer is below threshold */
+        if (unlikely(txq->status & ECRNX_TXQ_NDEV_FLOW_CTRL) &&
+            skb_queue_len(&txq->sk_list) < ECRNX_NDEV_FLOW_CTRL_RESTART) {
+            txq->status &= ~ECRNX_TXQ_NDEV_FLOW_CTRL;
+            netif_wake_subqueue(txq->ndev, txq->ndev_idx);
+            trace_txq_flowctrl_restart(txq);
+        }
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+    }
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    if (hwq->len < hwq->len_start &&
+        ieee80211_queue_stopped(ecrnx_hw->hw, hwq->id)) {
+        trace_hwq_flowctrl_start(hwq->id);
+        ieee80211_wake_queue(ecrnx_hw->hw, hwq->id);
+    }
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+    if (mu_enable)
+        ecrnx_txq_release_mu_lock(ecrnx_hw);
+}
+
+/**
+ * ecrnx_hwq_process_all - Process all HW queue list
+ *
+ * @ecrnx_hw: Driver main data
+ *
+ * Loop over all HWQ, and process them if needed
+ * To be called with tx_lock hold
+ */
+void ecrnx_hwq_process_all(struct ecrnx_hw *ecrnx_hw)
+{
+    int id;
+
+    ecrnx_mu_group_sta_select(ecrnx_hw);
+
+    for (id = ARRAY_SIZE(ecrnx_hw->hwq) - 1; id >= 0 ; id--) {
+        if (ecrnx_hw->hwq[id].need_processing) {
+            ecrnx_hwq_process(ecrnx_hw, &ecrnx_hw->hwq[id]);
+        }
+    }
+}
+
+/**
+ * ecrnx_hwq_init - Initialize all hwq structures
+ *
+ * @ecrnx_hw: Driver main data
+ *
+ */
+void ecrnx_hwq_init(struct ecrnx_hw *ecrnx_hw)
+{
+    int i, j;
+
+    for (i = 0; i < ARRAY_SIZE(ecrnx_hw->hwq); i++) {
+        struct ecrnx_hwq *hwq = &ecrnx_hw->hwq[i];
+
+        for (j = 0 ; j < CONFIG_USER_MAX; j++)
+            hwq->credits[j] = nx_txdesc_cnt[i];
+        hwq->id = i;
+        hwq->size = nx_txdesc_cnt[i];
+        INIT_LIST_HEAD(&hwq->list);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+        hwq->len = 0;
+        hwq->len_stop = nx_txdesc_cnt[i] * 2;
+        hwq->len_start = hwq->len_stop / 4;
+#endif
+    }
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_txq.h b/drivers/net/wireless/eswin/ecrnx_txq.h
new file mode 100644 (file)
index 0000000..250e1d3
--- /dev/null
@@ -0,0 +1,506 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_txq.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+#ifndef _ECRNX_TXQ_H_
+#define _ECRNX_TXQ_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/ieee80211.h>
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#include <net/mac80211.h>
+#include "ecrnx_baws.h"
+
+/**
+ * Softmac TXQ configuration
+ *  - STA have one TXQ per TID
+ *  - VIF have one TXQ per HW queue
+ *
+ * Txq mapping looks like
+ * for NX_REMOTE_STA_MAX=10 and NX_VIRT_DEV_MAX=4
+ *
+ * | TXQ | VIF |   STA |  TID | HWQ |
+ * |-----+-----+-------+------+-----|-
+ * |   0 |     |     0 |    0 |   1 | 16 TXQ per STA
+ * |   1 |     |     0 |    1 |   0 |
+ * |   2 |     |     0 |    2 |   0 |
+ * |   3 |     |     0 |    3 |   1 |
+ * |   4 |     |     0 |    4 |   2 |
+ * |   5 |     |     0 |    5 |   2 |
+ * |   6 |     |     0 |    6 |   3 |
+ * |   7 |     |     0 |    7 |   3 |
+ * |   8 |     |     0 |    8 |   1 |
+ * |  ...|     |       |      |     |
+ * |  16 |     |     0 |   16 |   1 |
+ * |-----+-----+-------+------+-----|-
+ * | ... |     |       |      |     | same for all STAs
+ * |-----+-----+-------+------+-----|-
+ * | 160 |   0 |       |      |   0 | 5 TXQ per VIF
+ * | ... |     |       |      |     |
+ * | 164 |   0 |       |      |   4 |
+ * |-----+-----+-------+------+-----|-
+ * | ... |     |       |      |     | same for all VIFs
+ * |-----+-----+-------+------+-----|-
+ *
+ * NOTE: When using CONFIG_MAC80211_TXQ only one TXQ is allocated by mac80211
+ * for the VIF (associated to BE ac). To avoid too much differences with case
+ * where TXQ are allocated by the driver the "missing" VIF TXQs are allocated
+ * by the driver. Actually driver also allocates txq for BE (to avoid having
+ * modify ac parameter to access the TXQ) but this one is never used.
+ * Driver check if nb_ready_mac80211 field is equal to NOT_MAC80211_TXQ in
+ * order to distinguish non mac80211 txq.
+ * When the txq interface (.wake_tx_queue) is used only the TXQ
+ * allocated by mac80211 will be used and thus BE access category will always
+ * be used. When "VIF" frames needs to be pushed on different access category
+ * mac80211 will use the tx interface (.tx) and in this case driver will select
+ * the txq associated to the requested access category.
+ */
+#define NX_NB_TID_PER_STA IEEE80211_NUM_TIDS
+#define NX_NB_TXQ_PER_STA NX_NB_TID_PER_STA
+#define NX_NB_TXQ_PER_VIF NX_TXQ_CNT
+#define NX_NB_TXQ ((NX_NB_TXQ_PER_STA * NX_REMOTE_STA_MAX) +    \
+                   (NX_NB_TXQ_PER_VIF * NX_VIRT_DEV_MAX))
+
+#define NX_FIRST_VIF_TXQ_IDX (NX_REMOTE_STA_MAX * NX_NB_TXQ_PER_STA)
+
+#define NOT_MAC80211_TXQ ULONG_MAX
+
+#else /* i.e. #ifdef CONFIG_ECRNX_FULLMAC */
+/**
+ * Fullmac TXQ configuration:
+ *  - STA: 1 TXQ per TID (limited to 8)
+ *         1 TXQ for bufferable MGT frames
+ *  - VIF: 1 TXQ for Multi/Broadcast +
+ *         1 TXQ for MGT for unknown STAs or non-bufferable MGT frames
+ *  - 1 TXQ for offchannel transmissions
+ *
+ *
+ * Txq mapping looks like
+ * for NX_REMOTE_STA_MAX=10 and NX_VIRT_DEV_MAX=4
+ *
+ * | TXQ | NDEV_ID | VIF |   STA |  TID | HWQ |
+ * |-----+---------+-----+-------+------+-----|-
+ * |   0 |       0 |     |     0 |    0 |   1 | 9 TXQ per STA
+ * |   1 |       1 |     |     0 |    1 |   0 | (8 data + 1 mgmt)
+ * |   2 |       2 |     |     0 |    2 |   0 |
+ * |   3 |       3 |     |     0 |    3 |   1 |
+ * |   4 |       4 |     |     0 |    4 |   2 |
+ * |   5 |       5 |     |     0 |    5 |   2 |
+ * |   6 |       6 |     |     0 |    6 |   3 |
+ * |   7 |       7 |     |     0 |    7 |   3 |
+ * |   8 |     N/A |     |     0 | MGMT |   3 |
+ * |-----+---------+-----+-------+------+-----|-
+ * | ... |         |     |       |      |     | Same for all STAs
+ * |-----+---------+-----+-------+------+-----|-
+ * |  90 |      80 |   0 | BC/MC |    0 | 1/4 | 1 TXQ for BC/MC per VIF
+ * | ... |         |     |       |      |     |
+ * |  93 |      80 |   3 | BC/MC |    0 | 1/4 |
+ * |-----+---------+-----+-------+------+-----|-
+ * |  94 |     N/A |   0 |   N/A | MGMT |   3 | 1 TXQ for unknown STA per VIF
+ * | ... |         |     |       |      |     |
+ * |  97 |     N/A |   3 |   N/A | MGMT |   3 |
+ * |-----+---------+-----+-------+------+-----|-
+ * |  98 |     N/A |     |   N/A | MGMT |   3 | 1 TXQ for offchannel frame
+ */
+#define NX_NB_TID_PER_STA 8
+#define NX_NB_TXQ_PER_STA (NX_NB_TID_PER_STA + 1)
+#define NX_NB_TXQ_PER_VIF 2
+#define NX_NB_TXQ ((NX_NB_TXQ_PER_STA * NX_REMOTE_STA_MAX) +    \
+                   (NX_NB_TXQ_PER_VIF * NX_VIRT_DEV_MAX) + 1)
+
+#define NX_FIRST_VIF_TXQ_IDX (NX_REMOTE_STA_MAX * NX_NB_TXQ_PER_STA)
+#define NX_FIRST_BCMC_TXQ_IDX  NX_FIRST_VIF_TXQ_IDX
+#define NX_FIRST_UNK_TXQ_IDX  (NX_FIRST_BCMC_TXQ_IDX + NX_VIRT_DEV_MAX)
+
+#define NX_OFF_CHAN_TXQ_IDX (NX_FIRST_VIF_TXQ_IDX +                     \
+                             (NX_VIRT_DEV_MAX * NX_NB_TXQ_PER_VIF))
+#define NX_BCMC_TXQ_TYPE 0
+#define NX_UNK_TXQ_TYPE  1
+
+/**
+ * Each data TXQ is a netdev queue. TXQ to send MGT are not data TXQ as
+ * they did not recieved buffer from netdev interface.
+ * Need to allocate the maximum case.
+ * AP : all STAs + 1 BC/MC
+ */
+#define NX_NB_NDEV_TXQ ((NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX) + 1 )
+#define NX_BCMC_TXQ_NDEV_IDX (NX_NB_TID_PER_STA * NX_REMOTE_STA_MAX)
+#define NX_STA_NDEV_IDX(tid, sta_idx) ((tid) + (sta_idx) * NX_NB_TID_PER_STA)
+#define NDEV_NO_TXQ 0xffff
+#if (NX_NB_NDEV_TXQ >= NDEV_NO_TXQ)
+#error("Need to increase struct ecrnx_txq->ndev_idx size")
+#endif
+
+/* stop netdev queue when number of queued buffers if greater than this  */
+#define ECRNX_NDEV_FLOW_CTRL_STOP    200
+/* restart netdev queue when number of queued buffers is lower than this */
+#define ECRNX_NDEV_FLOW_CTRL_RESTART 100
+
+#endif /*  CONFIG_ECRNX_SOFTMAC */
+
+#define TXQ_INACTIVE 0xffff
+#if (NX_NB_TXQ >= TXQ_INACTIVE)
+#error("Need to increase struct ecrnx_txq->idx size")
+#endif
+
+#define NX_TXQ_INITIAL_CREDITS 20 //4
+
+#define ECRNX_TXQ_CLEANUP_INTERVAL (10 * HZ) //10s in jiffies
+#define ECRNX_TXQ_MAX_QUEUE_JIFFIES (20 * HZ)
+/**
+ * TXQ tid sorted by decreasing priority
+ */
+extern const int nx_tid_prio[NX_NB_TID_PER_STA];
+
+/**
+ * struct ecrnx_hwq - Structure used to save information relative to
+ *                   an AC TX queue (aka HW queue)
+ * @list: List of TXQ, that have buffers ready for this HWQ
+ * @credits: available credit for the queue (i.e. nb of buffers that
+ *           can be pushed to FW )
+ * @id Id of the HWQ among ECRNX_HWQ_....
+ * @size size of the queue
+ * @need_processing Indicate if hwq should be processed
+ * @len number of packet ready to be pushed to fw for this HW queue
+ * @len_stop threshold to stop mac80211(i.e. netdev) queues. Stop queue when
+ *           driver has more than @len_stop packets ready.
+ * @len_start threshold to wake mac8011 queues. Wake queue when driver has
+ *            less than @len_start packets ready.
+ */
+struct ecrnx_hwq {
+    struct list_head list;
+    u8 credits[CONFIG_USER_MAX];
+    u8 size;
+    u8 id;
+    bool need_processing;
+#ifdef CONFIG_ECRNX_SOFTMAC
+    u8 len;
+    u8 len_stop;
+    u8 len_start;
+#endif /* CONFIG_ECRNX_SOFTMAC */
+};
+
+/**
+ * enum ecrnx_push_flags - Flags of pushed buffer
+ *
+ * @ECRNX_PUSH_RETRY Pushing a buffer for retry
+ * @ECRNX_PUSH_IMMEDIATE Pushing a buffer without queuing it first
+ */
+enum ecrnx_push_flags {
+    ECRNX_PUSH_RETRY  = BIT(0),
+    ECRNX_PUSH_IMMEDIATE = BIT(1),
+};
+
+/**
+ * enum ecrnx_txq_flags - TXQ status flag
+ *
+ * @ECRNX_TXQ_IN_HWQ_LIST: The queue is scheduled for transmission
+ * @ECRNX_TXQ_STOP_FULL: No more credits for the queue
+ * @ECRNX_TXQ_STOP_CSA: CSA is in progress
+ * @ECRNX_TXQ_STOP_STA_PS: Destiniation sta is currently in power save mode
+ * @ECRNX_TXQ_STOP_VIF_PS: Vif owning this queue is currently in power save mode
+ * @ECRNX_TXQ_STOP_CHAN: Channel of this queue is not the current active channel
+ * @ECRNX_TXQ_STOP_MU_POS: TXQ is stopped waiting for all the buffers pushed to
+ *                       fw to be confirmed
+ * @ECRNX_TXQ_STOP: All possible reason to have a txq stopped
+ * @ECRNX_TXQ_NDEV_FLOW_CTRL: associated netdev queue is currently stopped.
+ *                          Note: when a TXQ is flowctrl it is NOT stopped
+ */
+enum ecrnx_txq_flags {
+    ECRNX_TXQ_IN_HWQ_LIST  = BIT(0),
+    ECRNX_TXQ_STOP_FULL    = BIT(1),
+    ECRNX_TXQ_STOP_CSA     = BIT(2),
+    ECRNX_TXQ_STOP_STA_PS  = BIT(3),
+    ECRNX_TXQ_STOP_VIF_PS  = BIT(4),
+    ECRNX_TXQ_STOP_CHAN    = BIT(5),
+    ECRNX_TXQ_STOP_MU_POS  = BIT(6),
+    ECRNX_TXQ_STOP         = (ECRNX_TXQ_STOP_FULL | ECRNX_TXQ_STOP_CSA |
+                             ECRNX_TXQ_STOP_STA_PS | ECRNX_TXQ_STOP_VIF_PS |
+                             ECRNX_TXQ_STOP_CHAN) ,
+    ECRNX_TXQ_NDEV_FLOW_CTRL = BIT(7),
+};
+
+
+/**
+ * struct ecrnx_txq - Structure used to save information relative to
+ *                   a RA/TID TX queue
+ *
+ * @idx: Unique txq idx. Set to TXQ_INACTIVE if txq is not used.
+ * @status: bitfield of @ecrnx_txq_flags.
+ * @credits: available credit for the queue (i.e. nb of buffers that
+ *           can be pushed to FW).
+ * @pkt_sent: number of consecutive pkt sent without leaving HW queue list
+ * @pkt_pushed: number of pkt currently pending for transmission confirmation
+ * @sched_list: list node for HW queue schedule list (ecrnx_hwq.list)
+ * @sk_list: list of buffers to push to fw
+ * @last_retry_skb: pointer on the last skb in @sk_list that is a retry.
+ *                  (retry skb are stored at the beginning of the list)
+ *                  NULL if no retry skb is queued in @sk_list
+ * @nb_retry: Number of retry packet queued.
+ * @hwq: Pointer on the associated HW queue.
+ * @push_limit: number of packet to push before removing the txq from hwq list.
+ *              (we always have push_limit < skb_queue_len(sk_list))
+ * @tid: TID
+ *
+ * SOFTMAC specific:
+ * @baw: Block Ack window information
+ * @amsdu_anchor: pointer to ecrnx_sw_txhdr of the first subframe of the A-MSDU.
+ *                NULL if no A-MSDU frame is in construction
+ * @amsdu_ht_len_cap:
+ * @amsdu_vht_len_cap:
+ * @nb_ready_mac80211: Number of buffer ready in mac80211 txq
+ *
+ * FULLMAC specific
+ * @ps_id: Index to use for Power save mode (LEGACY or UAPSD)
+ * @ndev_idx: txq idx from netdev point of view (0xFF for non netdev queue)
+ * @ndev: pointer to ndev of the corresponding vif
+ * @amsdu: pointer to ecrnx_sw_txhdr of the first subframe of the A-MSDU.
+ *         NULL if no A-MSDU frame is in construction
+ * @amsdu_len: Maximum size allowed for an A-MSDU. 0 means A-MSDU not allowed
+ */
+struct ecrnx_txq {
+    u16 idx;
+    u8 status;
+    s8 credits;
+    u8 pkt_sent;
+    u8 pkt_pushed[CONFIG_USER_MAX];
+    struct list_head sched_list;
+    struct sk_buff_head sk_list;
+    struct sk_buff *last_retry_skb;
+    struct ecrnx_hwq *hwq;
+    int nb_retry;
+    u8 push_limit;
+    u8 tid;
+#ifdef CONFIG_MAC80211_TXQ
+    unsigned long nb_ready_mac80211;
+#endif
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ecrnx_baw baw;
+    struct ieee80211_sta *sta;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    struct ecrnx_sw_txhdr *amsdu_anchor;
+    u16 amsdu_ht_len_cap;
+    u16 amsdu_vht_len_cap;
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+#else /* ! CONFIG_ECRNX_SOFTMAC */
+    struct ecrnx_sta *sta;
+    u8 ps_id;
+    u16 ndev_idx;
+    struct net_device *ndev;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    struct ecrnx_sw_txhdr *amsdu;
+    u16 amsdu_len;
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+#endif /* CONFIG_ECRNX_SOFTMAC */
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    u8 mumimo_info;
+#endif
+};
+
+struct ecrnx_sta;
+struct ecrnx_vif;
+struct ecrnx_hw;
+struct ecrnx_sw_txhdr;
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+#define ECRNX_TXQ_GROUP_ID(txq) ((txq)->mumimo_info & 0x3f)
+#define ECRNX_TXQ_POS_ID(txq)   (((txq)->mumimo_info >> 6) & 0x3)
+#else
+#define ECRNX_TXQ_GROUP_ID(txq) 0
+#define ECRNX_TXQ_POS_ID(txq)   0
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+static inline bool ecrnx_txq_is_stopped(struct ecrnx_txq *txq)
+{
+    return (txq->status & ECRNX_TXQ_STOP);
+}
+
+static inline bool ecrnx_txq_is_full(struct ecrnx_txq *txq)
+{
+    return (txq->status & ECRNX_TXQ_STOP_FULL);
+}
+
+static inline bool ecrnx_txq_is_scheduled(struct ecrnx_txq *txq)
+{
+    return (txq->status & ECRNX_TXQ_IN_HWQ_LIST);
+}
+
+/**
+ * ecrnx_txq_is_ready_for_push - Check if a TXQ is ready for push
+ *
+ * @txq: txq pointer
+ *
+ * if
+ * - txq is not stopped
+ * - and hwq has credits
+ * - and there is no buffer queued
+ * then a buffer can be immediately pushed without having to queue it first
+ * @return: true if the 3 conditions are met and false otherwise.
+ */
+static inline bool ecrnx_txq_is_ready_for_push(struct ecrnx_txq *txq)
+{
+    return (!ecrnx_txq_is_stopped(txq) &&
+            txq->hwq->credits[ECRNX_TXQ_POS_ID(txq)] > 0 &&
+            skb_queue_empty(&txq->sk_list));
+}
+
+/**
+ * foreach_sta_txq - Macro to iterate over all TXQ of a STA in increasing
+ *                   TID order
+ *
+ * @sta: pointer to ecrnx_sta
+ * @txq: pointer to ecrnx_txq updated with the next TXQ at each iteration
+ * @tid: int updated with the TXQ tid at each iteration
+ * @ecrnx_hw: main driver data
+ */
+#ifdef CONFIG_MAC80211_TXQ
+#define foreach_sta_txq(sta, txq, tid, ecrnx_hw)                         \
+    for (tid = 0, txq = ecrnx_txq_sta_get(sta, 0);                       \
+         tid < NX_NB_TXQ_PER_STA;                                       \
+         tid++, txq = ecrnx_txq_sta_get(sta, tid))
+
+#elif defined(CONFIG_ECRNX_SOFTMAC)
+#define foreach_sta_txq(sta, txq, tid, ecrnx_hw)                         \
+    for (tid = 0, txq = &sta->txqs[0];                                  \
+         tid < NX_NB_TXQ_PER_STA;                                       \
+         tid++, txq++)
+
+#else /* CONFIG_ECRNX_FULLMAC */
+#define foreach_sta_txq(sta, txq, tid, ecrnx_hw)                          \
+    for (tid = 0, txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);               \
+         tid < (is_multicast_sta(sta->sta_idx) ? 1 : NX_NB_TXQ_PER_STA); \
+         tid++, txq++)
+
+#endif
+
+/**
+ * foreach_sta_txq_prio - Macro to iterate over all TXQ of a STA in
+ *                        decreasing priority order
+ *
+ * @sta: pointer to ecrnx_sta
+ * @txq: pointer to ecrnx_txq updated with the next TXQ at each iteration
+ * @tid: int updated with the TXQ tid at each iteration
+ * @i: int updated with ieration count
+ * @ecrnx_hw: main driver data
+ *
+ * Note: For fullmac txq for mgmt frame is skipped
+ */
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define foreach_sta_txq_prio(sta, txq, tid, i, ecrnx_hw)                 \
+    for (i = 0, tid = nx_tid_prio[0], txq = ecrnx_txq_sta_get(sta, tid); \
+         i < NX_NB_TID_PER_STA;                                         \
+         i++, tid = nx_tid_prio[i], txq = ecrnx_txq_sta_get(sta, tid))
+#else /* CONFIG_ECRNX_FULLMAC */
+#define foreach_sta_txq_prio(sta, txq, tid, i, ecrnx_hw)                          \
+    for (i = 0, tid = nx_tid_prio[0], txq = ecrnx_txq_sta_get(sta, tid, ecrnx_hw); \
+         i < NX_NB_TID_PER_STA;                                                  \
+         i++, tid = nx_tid_prio[i], txq = ecrnx_txq_sta_get(sta, tid, ecrnx_hw))
+#endif
+
+/**
+ * foreach_vif_txq - Macro to iterate over all TXQ of a VIF (in AC order)
+ *
+ * @vif: pointer to ecrnx_vif
+ * @txq: pointer to ecrnx_txq updated with the next TXQ at each iteration
+ * @ac:  int updated with the TXQ ac at each iteration
+ */
+#ifdef CONFIG_MAC80211_TXQ
+#define foreach_vif_txq(vif, txq, ac)                                   \
+    for (ac = ECRNX_HWQ_BK, txq = ecrnx_txq_vif_get(vif, ac);             \
+         ac < NX_NB_TXQ_PER_VIF;                                        \
+         ac++, txq = ecrnx_txq_vif_get(vif, ac))
+
+#else
+#define foreach_vif_txq(vif, txq, ac)                                   \
+    for (ac = ECRNX_HWQ_BK, txq = &vif->txqs[0];                         \
+         ac < NX_NB_TXQ_PER_VIF;                                        \
+         ac++, txq++)
+#endif
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+struct ecrnx_txq *ecrnx_txq_sta_get(struct ecrnx_sta *sta, u8 tid);
+struct ecrnx_txq *ecrnx_txq_vif_get(struct ecrnx_vif *vif, u8 ac);
+#else
+struct ecrnx_txq *ecrnx_txq_sta_get(struct ecrnx_sta *sta, u8 tid,
+                                  struct ecrnx_hw * ecrnx_hw);
+struct ecrnx_txq *ecrnx_txq_vif_get(struct ecrnx_vif *vif, u8 type);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+/**
+ * ecrnx_txq_vif_get_status - return status bits related to the vif
+ *
+ * @ecrnx_vif: Pointer to vif structure
+ */
+static inline u8 ecrnx_txq_vif_get_status(struct ecrnx_vif *ecrnx_vif)
+{
+    struct ecrnx_txq *txq = ecrnx_txq_vif_get(ecrnx_vif, 0);
+    return (txq->status & (ECRNX_TXQ_STOP_CHAN | ECRNX_TXQ_STOP_VIF_PS));
+}
+
+void ecrnx_txq_vif_init(struct ecrnx_hw * ecrnx_hw, struct ecrnx_vif *vif,
+                       u8 status);
+void ecrnx_txq_vif_deinit(struct ecrnx_hw * ecrnx_hw, struct ecrnx_vif *vif);
+void ecrnx_txq_sta_init(struct ecrnx_hw * ecrnx_hw, struct ecrnx_sta *ecrnx_sta,
+                       u8 status);
+void ecrnx_txq_sta_deinit(struct ecrnx_hw * ecrnx_hw, struct ecrnx_sta *ecrnx_sta);
+#ifdef CONFIG_ECRNX_FULLMAC
+void ecrnx_txq_unk_vif_init(struct ecrnx_vif *ecrnx_vif);
+void ecrnx_txq_unk_vif_deinit(struct ecrnx_vif *vif);
+void ecrnx_txq_offchan_init(struct ecrnx_vif *ecrnx_vif);
+void ecrnx_txq_offchan_deinit(struct ecrnx_vif *ecrnx_vif);
+void ecrnx_txq_tdls_vif_init(struct ecrnx_vif *ecrnx_vif);
+void ecrnx_txq_tdls_vif_deinit(struct ecrnx_vif *vif);
+void ecrnx_txq_tdls_sta_start(struct ecrnx_vif *ecrnx_vif, u16 reason,
+                             struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_tdls_sta_stop(struct ecrnx_vif *ecrnx_vif, u16 reason,
+                            struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_prepare(struct ecrnx_hw *ecrnx_hw);
+#endif
+
+
+void ecrnx_txq_add_to_hw_list(struct ecrnx_txq *txq);
+void ecrnx_txq_del_from_hw_list(struct ecrnx_txq *txq);
+void ecrnx_txq_stop(struct ecrnx_txq *txq, u16 reason);
+void ecrnx_txq_start(struct ecrnx_txq *txq, u16 reason);
+void ecrnx_txq_vif_start(struct ecrnx_vif *vif, u16 reason,
+                        struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_vif_stop(struct ecrnx_vif *vif, u16 reason,
+                       struct ecrnx_hw *ecrnx_hw);
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+void ecrnx_txq_sta_start(struct ecrnx_sta *sta, u16 reason);
+void ecrnx_txq_sta_stop(struct ecrnx_sta *sta, u16 reason);
+void ecrnx_txq_tdls_sta_start(struct ecrnx_sta *ecrnx_sta, u16 reason,
+                             struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_tdls_sta_stop(struct ecrnx_sta *ecrnx_sta, u16 reason,
+                            struct ecrnx_hw *ecrnx_hw);
+#else
+void ecrnx_txq_sta_start(struct ecrnx_sta *sta, u16 reason,
+                        struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_sta_stop(struct ecrnx_sta *sta, u16 reason,
+                       struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_offchan_start(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_sta_switch_vif(struct ecrnx_sta *sta, struct ecrnx_vif *old_vif,
+                             struct ecrnx_vif *new_vif);
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+int ecrnx_txq_queue_skb(struct sk_buff *skb, struct ecrnx_txq *txq,
+                       struct ecrnx_hw *ecrnx_hw,  bool retry,
+                       struct sk_buff *skb_prev);
+void ecrnx_txq_confirm_any(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq,
+                          struct ecrnx_hwq *hwq, struct ecrnx_sw_txhdr *sw_txhdr);
+void ecrnx_txq_drop_skb(struct ecrnx_txq *txq,  struct sk_buff *skb, struct ecrnx_hw *ecrnx_hw, bool retry_packet);
+
+void ecrnx_hwq_init(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_hwq_process(struct ecrnx_hw *ecrnx_hw, struct ecrnx_hwq *hwq);
+void ecrnx_hwq_process_all(struct ecrnx_hw *ecrnx_hw);
+
+#endif /* _ECRNX_TXQ_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_utils.c b/drivers/net/wireless/eswin/ecrnx_utils.c
new file mode 100644 (file)
index 0000000..e60785c
--- /dev/null
@@ -0,0 +1,1333 @@
+/**
+ * ecrnx_utils.c
+ *
+ * IPC utility function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ */
+#include "ecrnx_utils.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_rx.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_msg_rx.h"
+#include "ecrnx_debugfs.h"
+#include "ecrnx_prof.h"
+#include "ipc_host.h"
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+#include "eswin_utils.h"
+#include "ecrnx_sdio.h"
+#include "sdio.h"
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+#include "eswin_utils.h"
+#include "ecrnx_usb.h"
+#include "usb.h"
+#endif
+
+
+/**
+ * ecrnx_ipc_elem_pool_allocs() - Allocate and push to fw a pool of buffer.
+ *
+ * @ecrnx_hw: Main driver structure
+ * @pool: Pool to allocate
+ * @nb: Size of the pool to allocate
+ * @elem_size: SIze of one pool element
+ * @pool_name: Name of the pool
+ * @push: Function to push one pool element to fw
+ *
+ * This function will allocate an array to store the list of element addresses,
+ * a dma pool and @nb element in the dma pool.
+ * Each element is set with '0' and then push to fw using the @push function.
+ * It assumes that pointer inside @ipc parameter are set to NULL at start.
+ *
+ * Return: 0 on success and <0 upon error. If error is returned any allocated
+ * memory is NOT freed and ecrnx_ipc_elem_pool_deallocs() must be called.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_elem_pool_allocs(struct ecrnx_hw *ecrnx_hw,
+                                     struct ecrnx_ipc_elem_pool *pool,
+                                     int nb, size_t elem_size, char *pool_name,
+                                     int (*push)(struct ipc_host_env_tag *,
+                                                 void *, uint32_t))
+{
+    struct ecrnx_ipc_elem *buf;
+    int i;
+
+    pool->nb = 0;
+
+    /* allocate buf array */
+    pool->buf = kmalloc(nb * sizeof(struct ecrnx_ipc_elem), GFP_KERNEL);
+    if (!pool->buf) {
+        dev_err(ecrnx_hw->dev, "Allocation of buffer array for %s failed\n",
+                pool_name);
+        return -ENOMEM;
+    }
+
+    /* allocate dma pool */
+    pool->pool = dma_pool_create(pool_name, ecrnx_hw->dev, elem_size,
+                                 cache_line_size(), 0);
+    if (!pool->pool) {
+        dev_err(ecrnx_hw->dev, "Allocation of dma pool %s failed\n",
+                pool_name);
+        return -ENOMEM;
+    }
+
+    for (i = 0, buf = pool->buf; i < nb; buf++, i++) {
+
+        /* allocate an elem */
+        buf->addr = dma_pool_alloc(pool->pool, GFP_KERNEL, &buf->dma_addr);
+        if (!buf->addr) {
+            dev_err(ecrnx_hw->dev, "Allocation of block %d/%d in %s failed\n",
+                    (i + 1), nb, pool_name);
+            return -ENOMEM;
+        }
+        pool->nb++;
+
+        /* reset the element */
+        memset(buf->addr, 0, elem_size);
+
+        /* push it to FW */
+        push(ecrnx_hw->ipc_env, buf, (uint32_t)buf->dma_addr);
+    }
+
+    return 0;
+}
+#endif
+/**
+ * ecrnx_ipc_elem_pool_deallocs() - Free all memory allocated for a pool
+ *
+ * @pool: Pool to free
+ *
+ * Must be call once after ecrnx_ipc_elem_pool_allocs(), even if it returned
+ * an error
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_elem_pool_deallocs(struct ecrnx_ipc_elem_pool *pool)
+{
+    struct ecrnx_ipc_elem *buf;
+    int i;
+
+    for (i = 0, buf = pool->buf; i < pool->nb ; buf++, i++) {
+        dma_pool_free(pool->pool, buf->addr, buf->dma_addr);
+    }
+    pool->nb = 0;
+
+    if (pool->pool)
+        dma_pool_destroy(pool->pool);
+    pool->pool = NULL;
+
+    if (pool->buf)
+        kfree(pool->buf);
+    pool->buf = NULL;
+}
+#endif
+/**
+ * ecrnx_ipc_elem_var_allocs - Alloc a single ipc buffer and push it to fw
+ *
+ * @ecrnx_hw: Main driver structure
+ * @elem: Element to allocate
+ * @elem_size: Size of the element to allcoate
+ * @dir: DMA direction
+ * @buf: If not NULL, used this buffer instead of allocating a new one. It must
+ * be @elem_size long and be allocated by kmalloc as kfree will be called.
+ * @init: Pointer to initial data to write in buffer before DMA sync. Needed
+ * only if direction is DMA_TO_DEVICE. If set it is assume that its size is
+ * @elem_size.
+ * @push: Function to push the element to fw. May be set to NULL.
+ *
+ * It allocates a buffer (or use the one provided with @buf), initializes it if
+ * @init is set, map buffer for DMA transfer, initializes @elem and push buffer
+ * to FW if @push is seet.
+ *
+ * Return: 0 on success and <0 upon error. If error is returned any allocated
+ * memory has been freed (including @buf if set).
+ */
+int ecrnx_ipc_elem_var_allocs(struct ecrnx_hw *ecrnx_hw,
+                             struct ecrnx_ipc_elem_var *elem, size_t elem_size,
+                             enum dma_data_direction dir,
+                             void *buf, const void *init,
+                             void (*push)(struct ipc_host_env_tag *, uint32_t))
+{
+    if (buf) {
+        elem->addr = buf;
+    } else {
+        elem->addr = kmalloc(elem_size, GFP_KERNEL);
+        if (!elem->addr) {
+            dev_err(ecrnx_hw->dev, "Allocation of ipc buffer failed\n");
+            return -ENOMEM;
+        }
+    }
+    elem->size = elem_size;
+
+    if ((dir == DMA_TO_DEVICE) && init) {
+        memcpy(elem->addr, init, elem_size);
+    }
+
+#ifdef CONFIG_ECRNX_ESWIN
+    elem->dma_addr = (ptr_addr)elem->addr;
+#else
+    elem->dma_addr = dma_map_single(ecrnx_hw->dev, elem->addr, elem_size, dir);
+    if (dma_mapping_error(ecrnx_hw->dev, elem->dma_addr)) {
+        dev_err(ecrnx_hw->dev, "DMA mapping failed\n");
+        kfree(elem->addr);
+        elem->addr = NULL;
+        return -EIO;
+    }
+
+    if (push)
+        push(ecrnx_hw->ipc_env, elem->dma_addr);
+#endif
+    return 0;
+}
+
+/**
+ * ecrnx_ipc_elem_var_deallocs() - Free memory allocated for a single ipc buffer
+ *
+ * @ecrnx_hw: Main driver structure
+ * @elem: Element to free
+ */
+void ecrnx_ipc_elem_var_deallocs(struct ecrnx_hw *ecrnx_hw,
+                                struct ecrnx_ipc_elem_var *elem)
+{
+    if (!elem->addr)
+        return;
+#ifndef CONFIG_ECRNX_ESWIN
+    dma_unmap_single(ecrnx_hw->dev, elem->dma_addr, elem->size, DMA_TO_DEVICE);
+#endif
+    kfree(elem->addr);
+    elem->addr = NULL;
+}
+
+/**
+ * ecrnx_ipc_skb_elem_allocs() - Allocate and push a skb buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that will contain the address of the buffer
+ */
+int ecrnx_ipc_skb_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+                                 struct ecrnx_ipc_skb_elem *elem, size_t skb_size,
+                                 enum dma_data_direction dir,
+                                 int (*push)(struct ipc_host_env_tag *,
+                                             void *, uint32_t))
+{
+    elem->skb = dev_alloc_skb(skb_size);
+    if (unlikely(!elem->skb)) {
+        dev_err(ecrnx_hw->dev, "Allocation of ipc skb failed\n");
+        return -ENOMEM;
+    }
+
+    elem->dma_addr = dma_map_single(ecrnx_hw->dev, elem->skb->data, skb_size, dir);
+    if (unlikely(dma_mapping_error(ecrnx_hw->dev, elem->dma_addr))) {
+        dev_err(ecrnx_hw->dev, "DMA mapping failed\n");
+        dev_kfree_skb(elem->skb);
+        elem->skb = NULL;
+        return -EIO;
+    }
+
+    if (push){
+        push(ecrnx_hw->ipc_env, elem, elem->dma_addr);
+    }
+    return 0;
+}
+
+/**
+ * ecrnx_ipc_skb_elem_deallocs() - Free a skb buffer allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that contains the address of the buffer
+ * @skb_size: size of the skb buffer data
+ * @dir: DMA direction
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_skb_elem_deallocs(struct ecrnx_hw *ecrnx_hw,
+                                       struct ecrnx_ipc_skb_elem *elem,
+                                       size_t skb_size, enum dma_data_direction dir) 
+{
+    if (elem->skb) {
+        dma_unmap_single(ecrnx_hw->dev, elem->dma_addr, skb_size, dir);
+        dev_kfree_skb(elem->skb);
+        elem->skb = NULL;
+    }
+}
+#endif
+/**
+ * ecrnx_ipc_unsup_rx_vec_elem_allocs() - Allocate and push an unsupported
+ *                                       RX vector buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that will contain the address of the buffer
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_unsup_rx_vec_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+                                      struct ecrnx_ipc_skb_elem *elem)
+{
+    struct rx_vector_desc *rxdesc;
+
+    if (ecrnx_ipc_skb_elem_allocs(ecrnx_hw, elem,
+            ecrnx_hw->ipc_env->unsuprxvec_bufsz, DMA_FROM_DEVICE, NULL))
+        return -ENOMEM;
+
+    rxdesc = (struct rx_vector_desc *) elem->skb->data;
+    rxdesc->pattern = 0;
+    dma_sync_single_for_device(ecrnx_hw->dev,
+                        elem->dma_addr + offsetof(struct rx_vector_desc, pattern),
+                        sizeof(rxdesc->pattern), DMA_BIDIRECTIONAL);
+
+    ipc_host_unsup_rx_vec_buf_push(ecrnx_hw->ipc_env, elem, (u32) elem->dma_addr);
+
+    return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_deallocs() - Free all unsupported rx vector buffer
+ *                                   allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_unsup_rx_vec_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_ipc_skb_elem *elem;
+    int i, nb = ecrnx_hw->ipc_env->unsuprxvec_bufnb;
+
+    if (!ecrnx_hw->e2aunsuprxvec_elems)
+        return;
+
+    for (i = 0, elem = ecrnx_hw->e2aunsuprxvec_elems; i < nb; i++, elem++) {
+        ecrnx_ipc_skb_elem_deallocs(ecrnx_hw, elem, ecrnx_hw->ipc_env->unsuprxvec_bufsz, DMA_FROM_DEVICE);
+    }
+
+    kfree(ecrnx_hw->e2aunsuprxvec_elems);
+    ecrnx_hw->e2aunsuprxvec_elems = NULL;
+}
+#endif
+
+/**
+* ecrnx_ipc_unsup_rx_vec_elems_allocs() - Allocate and push all unsupported RX
+*                                        vector buffer for the FW
+*
+* @ecrnx_hw: Main driver data
+*/
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_unsup_rx_vec_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+   struct ecrnx_ipc_skb_elem *elem;
+   int i, nb = ecrnx_hw->ipc_env->unsuprxvec_bufnb;
+
+   ecrnx_hw->e2aunsuprxvec_elems = kzalloc(nb * sizeof(struct ecrnx_ipc_skb_elem),
+                                   GFP_KERNEL);
+   if (!ecrnx_hw->e2aunsuprxvec_elems) {
+       dev_err(ecrnx_hw->dev, "Failed to allocate unsuprxvec_elems\n");
+       return -ENOMEM;
+   }
+
+   for (i = 0, elem = ecrnx_hw->e2aunsuprxvec_elems; i < nb; i++, elem++)
+   {
+       if (ecrnx_ipc_unsup_rx_vec_elem_allocs(ecrnx_hw, elem)) {
+           dev_err(ecrnx_hw->dev, "Failed to allocate unsuprxvec buf %d/%d\n",
+                   i + 1, nb);
+           return -ENOMEM;
+       }
+   }
+   return 0;
+}
+#endif
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+/**
+ * ecrnx_ipc_rxbuf_elem_allocs() - Allocate and push a rx buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that will contain the address of the buffer
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_rxbuf_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+                               struct ecrnx_ipc_skb_elem *elem)
+{
+    struct hw_rxhdr *hw_rxhdr;
+
+    if (ecrnx_ipc_skb_elem_allocs(ecrnx_hw, elem,
+            ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE, NULL))
+        return -ENOMEM;
+
+    hw_rxhdr = (struct hw_rxhdr *) elem->skb->data;
+    hw_rxhdr->pattern = 0;
+    dma_sync_single_for_device(ecrnx_hw->dev,
+            elem->dma_addr + offsetof(struct hw_rxhdr, pattern),
+            sizeof(hw_rxhdr->pattern), DMA_BIDIRECTIONAL);
+
+    ipc_host_rxbuf_push(ecrnx_hw->ipc_env, elem, (u32) elem->dma_addr);
+
+    return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_repush() - Reset and repush an already allocated RX buffer
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Pointer to the skb elem that contains the address of the buffer
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_repush(struct ecrnx_hw *ecrnx_hw,
+                                struct ecrnx_ipc_skb_elem *elem)
+{
+    struct sk_buff *skb = elem->skb;
+    int pattern_offset = sizeof(struct hw_rxhdr);
+
+    ((struct hw_rxhdr *)skb->data)->pattern = 0;
+    dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr,
+                               pattern_offset, DMA_BIDIRECTIONAL);
+    ipc_host_rxbuf_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_allocs() - Allocate and push all RX buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_rxbuf_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_ipc_skb_elem *elem;
+    int i, nb = ecrnx_hw->ipc_env->rx_bufnb;
+
+    ecrnx_hw->rxbuf_elems = kzalloc(nb * sizeof(struct ecrnx_ipc_skb_elem),
+                                GFP_KERNEL);
+    if (!ecrnx_hw->rxbuf_elems) {
+        dev_err(ecrnx_hw->dev, "Failed to allocate rx_elems\n");
+        return -ENOMEM;
+    }
+
+    for (i = 0, elem = ecrnx_hw->rxbuf_elems; i < nb; i++, elem++) {
+        if (ecrnx_ipc_rxbuf_elem_allocs(ecrnx_hw, elem)) {
+            dev_err(ecrnx_hw->dev, "Failed to allocate rx buf %d/%d\n",
+                    i + 1, nb);
+            return -ENOMEM;
+        }
+    }
+
+    return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_deallocs() - Free all RX buffer allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_rxbuf_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_ipc_skb_elem *elem;
+    int i, nb = ecrnx_hw->ipc_env->rx_bufnb;
+
+    if (!ecrnx_hw->rxbuf_elems)
+        return;
+
+    for (i = 0, elem = ecrnx_hw->rxbuf_elems; i < nb; i++, elem++) {
+        ecrnx_ipc_skb_elem_deallocs(ecrnx_hw, elem, ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE);
+    }
+
+    kfree(ecrnx_hw->rxbuf_elems);
+    ecrnx_hw->rxbuf_elems = NULL;   
+}
+#endif 
+
+#else /* ! CONFIG_ECRNX_SOFTMAC */
+
+/**
+ * ecrnx_ipc_rxdesc_elem_repush() - Repush a rxdesc to FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @elem: Rx desc to repush
+ *
+ * Once rx buffer has been received, the rxdesc used by FW to upload this
+ * buffer can be re-used for another rx buffer.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxdesc_elem_repush(struct ecrnx_hw *ecrnx_hw,
+                                 struct ecrnx_ipc_elem *elem)
+{
+    struct rxdesc_tag *rxdesc = elem->addr;
+    rxdesc->status = 0;
+    dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr,
+                               sizeof(struct rxdesc_tag), DMA_BIDIRECTIONAL);
+    ipc_host_rxdesc_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+}
+#endif
+/**
+ * ecrnx_ipc_rxbuf_elem_allocs() - Allocate and push a RX buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_rxbuf_elem_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+    struct sk_buff *skb;
+    struct hw_rxhdr *hw_rxhdr;
+    dma_addr_t dma_addr;
+    int size = ecrnx_hw->ipc_env->rx_bufsz;
+    int nb, idx;
+
+    skb = dev_alloc_skb(size);
+    if (unlikely(!skb)) {
+        dev_err(ecrnx_hw->dev, "Failed to allocate rx buffer\n");
+        return -ENOMEM;
+    }
+
+    dma_addr = dma_map_single(ecrnx_hw->dev, skb->data, size, DMA_FROM_DEVICE);
+
+    if (unlikely(dma_mapping_error(ecrnx_hw->dev, dma_addr))) {
+        dev_err(ecrnx_hw->dev, "Failed to map rx buffer\n");
+        goto err_skb;
+    }
+
+    hw_rxhdr = (struct hw_rxhdr *)skb->data;
+    hw_rxhdr->pattern = 0;
+    dma_sync_single_for_device(ecrnx_hw->dev,
+                               dma_addr + offsetof(struct hw_rxhdr, pattern),
+                               sizeof(hw_rxhdr->pattern), DMA_BIDIRECTIONAL);
+
+    /* Find first free slot */
+    nb = 0;
+    idx = ecrnx_hw->rxbuf_elems.idx;
+    while (ecrnx_hw->rxbuf_elems.skb[idx] && nb < ECRNX_RXBUFF_MAX) {
+        idx = ( idx + 1 ) % ECRNX_RXBUFF_MAX;
+        nb++;
+    }
+
+    if (WARN((nb == ECRNX_RXBUFF_MAX), "No more free space for rxbuff")) {
+        goto err_dma;
+    }
+
+    ecrnx_hw->rxbuf_elems.skb[idx] = skb;
+
+    /* Save info in skb control buffer  */
+    ECRNX_RXBUFF_DMA_ADDR_SET(skb, dma_addr);
+    ECRNX_RXBUFF_PATTERN_SET(skb, ecrnx_rxbuff_pattern);
+    ECRNX_RXBUFF_IDX_SET(skb, idx);
+
+    /* Push buffer to FW */
+    ipc_host_rxbuf_push(ecrnx_hw->ipc_env, ECRNX_RXBUFF_IDX_TO_HOSTID(idx),
+                        dma_addr);
+
+    /* Save idx so that on next push the free slot will be found quicker */
+    ecrnx_hw->rxbuf_elems.idx = ( idx + 1 ) % ECRNX_RXBUFF_MAX;
+
+    return 0;
+
+  err_dma:
+    dma_unmap_single(ecrnx_hw->dev, dma_addr, size, DMA_FROM_DEVICE);
+  err_skb:
+    dev_kfree_skb(skb);
+    return -ENOMEM;
+
+    return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_repush() - Repush a rxbuf to FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @skb: Skb to repush
+ *
+ * In case a skb is not forwarded to upper layer it can be re-used.
+ * It is assumed that @skb has been verified before calling this function and
+ * that it is a valid rx buffer
+ * (i.e. skb == ecrnx_hw->rxbuf_elems.skb[ECRNX_RXBUFF_IDX_GET(skb)])
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_repush(struct ecrnx_hw *ecrnx_hw,
+                                struct sk_buff *skb)
+{
+    dma_addr_t dma_addr;
+    struct hw_rxhdr *hw_rxhdr = (struct hw_rxhdr *)skb->data;
+    int idx;
+
+    /* reset pattern */
+    hw_rxhdr->pattern = 0;
+    dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+    dma_sync_single_for_device(ecrnx_hw->dev,
+                               dma_addr + offsetof(struct hw_rxhdr, pattern),
+                               sizeof(hw_rxhdr->pattern), DMA_BIDIRECTIONAL);
+
+    /* re-push buffer to FW */
+    idx = ECRNX_RXBUFF_IDX_GET(skb);
+   ipc_host_rxbuf_push(ecrnx_hw->ipc_env, ECRNX_RXBUFF_IDX_TO_HOSTID(idx),
+                       dma_addr);
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_allocs() - Allocate and push all RX buffer for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static int ecrnx_ipc_rxbuf_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+    //int i, nb = ecrnx_hw->ipc_env->rx_bufnb;
+    int i, nb = 0;
+
+    for (i = 0; i < ECRNX_RXBUFF_MAX; i++) {
+        ecrnx_hw->rxbuf_elems.skb[i] = NULL;
+    }
+    ecrnx_hw->rxbuf_elems.idx = 0;
+
+    for (i = 0; i < nb; i++) {
+        if (ecrnx_ipc_rxbuf_elem_allocs(ecrnx_hw)) {
+            dev_err(ecrnx_hw->dev, "Failed to allocate rx buf %d/%d\n",
+                    i + 1, nb);
+            return -ENOMEM;
+        }
+    }
+    return 0;
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elems_deallocs() - Free all RX buffer allocated for the FW
+ *
+ * @ecrnx_hw: Main driver data
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_ipc_rxbuf_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+    struct sk_buff *skb;
+    int i;
+
+    for (i = 0; i < ECRNX_RXBUFF_MAX; i++) {
+        if (ecrnx_hw->rxbuf_elems.skb[i]) {
+            skb = ecrnx_hw->rxbuf_elems.skb[i];
+            dma_unmap_single(ecrnx_hw->dev, ECRNX_RXBUFF_DMA_ADDR_GET(skb),
+                             ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE);
+            dev_kfree_skb(skb);
+            ecrnx_hw->rxbuf_elems.skb[i] = NULL;
+        }
+    }   
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_pull() - Extract a skb from local table
+ *
+ * @ecrnx_hw: Main driver data
+ * @skb: SKb to extract for table
+ *
+ * After checking that skb is actually a pointer of local table, extract it
+ * from the table.
+ * When buffer is removed, DMA mapping is remove which has the effect to
+ * synchronize the buffer for the cpu.
+ * To be called before passing skb to upper layer.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_pull(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+    unsigned int idx = ECRNX_RXBUFF_IDX_GET(skb);
+
+    if (ECRNX_RXBUFF_VALID_IDX(idx) && (ecrnx_hw->rxbuf_elems.skb[idx] == skb)) {
+        dma_addr_t dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+        ecrnx_hw->rxbuf_elems.skb[idx] = NULL;
+        dma_unmap_single(ecrnx_hw->dev, dma_addr,
+                         ecrnx_hw->ipc_env->rx_bufsz, DMA_FROM_DEVICE);
+    } else {
+        WARN(1, "Incorrect rxbuff idx skb=%p table[%u]=%p", skb, idx,
+             idx < ECRNX_RXBUFF_MAX ? ecrnx_hw->rxbuf_elems.skb[idx] : NULL);
+    }
+
+    /* Reset the pattern and idx */
+    ECRNX_RXBUFF_PATTERN_SET(skb, 0);
+    ECRNX_RXBUFF_IDX_SET(skb, ECRNX_RXBUFF_MAX);
+}
+#endif
+
+/**
+ * ecrnx_ipc_rxbuf_elem_sync() - Sync part of a RX buffer
+ *
+ * @ecrnx_hw: Main driver data
+ * @skb: SKb to sync
+ * @len: Len to sync
+ *
+ * After checking that skb is actually a pointer of local table, sync @p len
+ * bytes of the buffer for CPU. Buffer is not removed from the table
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+void ecrnx_ipc_rxbuf_elem_sync(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+                              int len)
+{
+    unsigned int idx = ECRNX_RXBUFF_IDX_GET(skb);
+
+    if (ECRNX_RXBUFF_VALID_IDX(idx) && (ecrnx_hw->rxbuf_elems.skb[idx] == skb)) {
+        dma_addr_t dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+        dma_sync_single_for_cpu(ecrnx_hw->dev, dma_addr, len, DMA_FROM_DEVICE);
+    } else {
+        WARN(1, "Incorrect rxbuff idx skb=%p table[%u]=%p", skb, idx,
+             idx < ECRNX_RXBUFF_MAX ? ecrnx_hw->rxbuf_elems.skb[idx] : NULL);
+    }
+}
+#endif
+#endif /* ! CONFIG_ECRNX_SOFTMAC */
+
+/**
+ * ecrnx_elems_deallocs() - Deallocate IPC storage elements.
+ * @ecrnx_hw: Main driver data
+ *
+ * This function deallocates all the elements required for communications with
+ * LMAC, such as Rx Data elements, MSGs elements, ...
+ * This function should be called in correspondence with the allocation function.
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+static void ecrnx_elems_deallocs(struct ecrnx_hw *ecrnx_hw)
+{
+    ecrnx_ipc_rxbuf_elems_deallocs(ecrnx_hw);
+    ecrnx_ipc_unsup_rx_vec_elems_deallocs(ecrnx_hw);
+#ifdef CONFIG_ECRNX_FULLMAC
+    ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->e2arxdesc_pool);
+#endif
+    ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->e2amsgs_pool);
+    ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->dbgmsgs_pool);
+    ecrnx_ipc_elem_pool_deallocs(&ecrnx_hw->e2aradars_pool);
+    ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &ecrnx_hw->pattern_elem);
+    ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &ecrnx_hw->dbgdump_elem.buf);   
+}
+#endif
+
+/**
+ * ecrnx_elems_allocs() - Allocate IPC storage elements.
+ * @ecrnx_hw: Main driver data
+ *
+ * This function allocates all the elements required for communications with
+ * LMAC, such as Rx Data elements, MSGs elements, ...
+ * This function should be called in correspondence with the deallocation function.
+ */
+static int ecrnx_elems_allocs(struct ecrnx_hw *ecrnx_hw)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#ifndef CONFIG_ECRNX_ESWIN
+    if (dma_set_coherent_mask(ecrnx_hw->dev, DMA_BIT_MASK(32)))
+        goto err_alloc;
+    if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->e2amsgs_pool,
+                                  ecrnx_hw->ipc_env->ipc_e2amsg_bufnb,
+                                  ecrnx_hw->ipc_env->ipc_e2amsg_bufsz,
+                                  "ecrnx_ipc_e2amsgs_pool",
+                                  ipc_host_msgbuf_push))
+        goto err_alloc;
+
+    if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->dbgmsgs_pool,
+                                  ecrnx_hw->ipc_env->ipc_dbg_bufnb,
+                                  ecrnx_hw->ipc_env->ipc_dbg_bufsz,
+                                  "ecrnx_ipc_dbgmsgs_pool",
+                                  ipc_host_dbgbuf_push))
+        goto err_alloc;
+
+    if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->e2aradars_pool,
+                                  ecrnx_hw->ipc_env->radar_bufnb,
+                                  ecrnx_hw->ipc_env->radar_bufsz,
+                                  "ecrnx_ipc_e2aradars_pool",
+                                  ipc_host_radarbuf_push))
+        goto err_alloc;
+
+    if (ecrnx_ipc_unsup_rx_vec_elems_allocs(ecrnx_hw))
+    #error 1111
+        goto err_alloc;
+
+    if (ecrnx_ipc_elem_var_allocs(ecrnx_hw, &ecrnx_hw->pattern_elem,
+                                 sizeof(u32), DMA_TO_DEVICE,
+                                 NULL, &ecrnx_rxbuff_pattern,
+                                 ipc_host_patt_addr_push))
+        goto err_alloc;
+
+    if (ecrnx_ipc_elem_var_allocs(ecrnx_hw, &ecrnx_hw->dbgdump_elem.buf,
+                                 sizeof(struct dbg_debug_dump_tag),
+                                 DMA_FROM_DEVICE, NULL, NULL,
+                                 ipc_host_dbginfobuf_push))
+        goto err_alloc;
+
+    /*
+     * Note that the RX buffers are no longer allocated here as their size depends on the
+     * FW configuration, which is not available at that time.
+     * They will be allocated when checking the parameter compatibility between the driver
+     * and the underlying components (i.e. during the ecrnx_handle_dynparams() execution)
+     */
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    if (ecrnx_ipc_elem_pool_allocs(ecrnx_hw, &ecrnx_hw->e2arxdesc_pool,
+                                  ecrnx_hw->ipc_env->rxdesc_nb,
+                                  sizeof(struct rxdesc_tag),
+                                  "ecrnx_ipc_e2arxdesc_pool",
+                                  ipc_host_rxdesc_push))
+        goto err_alloc;
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+    return 0;
+
+err_alloc:
+    ecrnx_elems_deallocs(ecrnx_hw);
+    return -ENOMEM;
+#else
+    return 0;
+#endif
+}
+
+/**
+ * ecrnx_ipc_msg_push() - Push a msg to IPC queue
+ *
+ * @ecrnx_hw: Main driver data
+ * @msg_buf: Pointer to message
+ * @len: Size, in bytes, of message
+ */
+void ecrnx_ipc_msg_push(struct ecrnx_hw *ecrnx_hw, void *msg_buf, uint16_t len)
+{
+    ecrnx_hw->msg_tx++;
+    ipc_host_msg_push(ecrnx_hw->ipc_env, msg_buf, len);
+}
+
+/**
+ * ecrnx_ipc_txdesc_push() - Push a txdesc to FW
+ *
+ * @ecrnx_hw: Main driver data
+ * @tx_desc: Pointer on &struct txdesc_api to push to FW
+ * @hostid: Pointer save in ipc env to retrieve tx buffer upon confirmation.
+ * @hw_queue: Hw queue to push txdesc to
+ * @user: User position to push the txdesc to. It must be set to 0 if  MU-MIMMO
+ * is not used.
+ */
+void ecrnx_ipc_txdesc_push(struct ecrnx_hw *ecrnx_hw, void *tx_desc,
+                          void *hostid, int hw_queue, int user)
+{
+
+#if !defined(CONFIG_ECRNX_ESWIN_SDIO) && !defined(CONFIG_ECRNX_ESWIN_USB)
+    volatile struct txdesc_host *txdesc_host;
+    u32 *src, *dst;
+    int i;
+
+    txdesc_host = ipc_host_txdesc_get(ecrnx_hw->ipc_env, hw_queue, user);
+    BUG_ON(!txdesc_host);
+
+    dst = (typeof(dst))&txdesc_host->api;
+    src = (typeof(src))tx_desc;
+    for (i = 0; i < sizeof(txdesc_host->api) / sizeof(*src); i++)
+        *dst++ = *src++;
+
+    wmb(); /* vs desc */
+
+       ipc_host_txdesc_push(ecrnx_hw->ipc_env, hw_queue, user, hostid);
+#else
+    ecrnx_frame_send(ecrnx_hw, tx_desc, hostid, hw_queue, user);
+#endif
+}
+
+/**
+ * ecrnx_ipc_fw_trace_desc_get() - Return pointer to the start of trace
+ * description in IPC environment
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void *ecrnx_ipc_fw_trace_desc_get(struct ecrnx_hw *ecrnx_hw)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+    return (void *)&(ecrnx_hw->ipc_env->shared->trace_pattern);
+#else
+    return NULL;
+#endif
+}
+
+#ifndef CONFIG_ECRNX_ESWIN
+/**
+ * ecrnx_ipc_sta_buffer_init - Initialize counter of bufferred data for a given sta
+ *
+ * @ecrnx_hw: Main driver data
+ * @sta_idx: Index of the station to initialize
+ */
+void ecrnx_ipc_sta_buffer_init(struct ecrnx_hw *ecrnx_hw, int sta_idx)
+{
+    int i;
+    volatile u32_l *buffered;
+
+    if (sta_idx >= NX_REMOTE_STA_MAX)
+        return;
+
+    buffered = ecrnx_hw->ipc_env->shared->buffered[sta_idx];
+
+    for (i = 0; i < TID_MAX; i++) {
+        *buffered++ = 0;
+    }
+}
+#endif
+/**
+ * ecrnx_ipc_sta_buffer - Update counter of bufferred data for a given sta
+ *
+ * @ecrnx_hw: Main driver data
+ * @sta: Managed station
+ * @tid: TID on which data has been added or removed
+ * @size: Size of data to add (or remove if < 0) to STA buffer.
+ */
+void ecrnx_ipc_sta_buffer(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta, int tid, int size)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+    u32_l *buffered;
+
+    if (!sta)
+        return;
+
+    if ((sta->sta_idx >= NX_REMOTE_STA_MAX) || (tid >= TID_MAX))
+        return;
+
+    buffered = &ecrnx_hw->ipc_env->shared->buffered[sta->sta_idx][tid];
+
+    if (size < 0) {
+        size = -size;
+        if (*buffered < size)
+            *buffered = 0;
+        else
+            *buffered -= size;
+    } else {
+        // no test on overflow
+        *buffered += size;
+    }
+#endif
+}
+
+/**
+ * ecrnx_msgind() - IRQ handler callback for %IPC_IRQ_E2A_MSG
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from e2amsgs_pool
+ */
+static u8 ecrnx_msgind(void *pthis, void *hostid)
+{
+    struct ecrnx_hw *ecrnx_hw = pthis;
+       u8 ret = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+    struct ecrnx_ipc_elem *elem = hostid;
+    struct ipc_e2a_msg *msg = elem->addr;
+
+    REG_SW_SET_PROFILING(ecrnx_hw, SW_PROF_MSGIND);
+
+    /* Look for pattern which means that this hostbuf has been used for a MSG */
+    if (msg->pattern != IPC_MSGE2A_VALID_PATTERN) {
+        ret = -1;
+        goto msg_no_push;
+    }
+#else
+    struct ipc_e2a_msg *msg = NULL;
+
+    ecrnx_hw->msg_rx++;
+    ECRNX_DBG("%s enter 0x%x, 0x%x!!\n", __func__, pthis, hostid);
+    if(!pthis || !hostid){
+        ECRNX_ERR(" %s input param error!! \n", __func__);
+        return ret;
+    }
+    msg = hostid;
+#endif
+    /* Relay further actions to the msg parser */
+    ecrnx_rx_handle_msg(ecrnx_hw, msg);
+    
+#ifndef CONFIG_ECRNX_ESWIN
+    /* Reset the msg element and re-use it */
+    msg->pattern = 0;
+    wmb();
+
+    /* Push back the buffer to the LMAC */
+    ipc_host_msgbuf_push(ecrnx_hw->ipc_env, elem, elem->dma_addr);
+
+msg_no_push:
+    REG_SW_CLEAR_PROFILING(ecrnx_hw, SW_PROF_MSGIND);
+#endif
+    ECRNX_DBG("%s exit!!", __func__);
+    return ret;
+}
+
+/**
+ * ecrnx_msgackind() - IRQ handler callback for %IPC_IRQ_E2A_MSG_ACK
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to command acknoledged
+ */
+static u8 ecrnx_msgackind(void *pthis, void *hostid)
+{
+    struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+
+    ecrnx_hw->msg_tx_done++;
+    ecrnx_hw->cmd_mgr.llind(&ecrnx_hw->cmd_mgr, (struct ecrnx_cmd *)hostid);
+    return -1;
+}
+
+/**
+ * ecrnx_radarind() - IRQ handler callback for %IPC_IRQ_E2A_RADAR
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from e2aradars_pool
+ */
+static u8 ecrnx_radarind(void *pthis, void *hostid)
+{
+#ifdef CONFIG_ECRNX_RADAR
+    struct ecrnx_hw *ecrnx_hw = pthis;
+    struct ecrnx_ipc_elem *elem = hostid;
+    struct radar_pulse_array_desc *pulses = elem->addr;
+    u8 ret = 0;
+    int i;
+
+    /* Look for pulse count meaning that this hostbuf contains RADAR pulses */
+    if (pulses->cnt == 0) {
+        ret = -1;
+        goto radar_no_push;
+    }
+
+    if (ecrnx_radar_detection_is_enable(&ecrnx_hw->radar, pulses->idx)) {
+        /* Save the received pulses only if radar detection is enabled */
+        for (i = 0; i < pulses->cnt; i++) {
+            struct ecrnx_radar_pulses *p = &ecrnx_hw->radar.pulses[pulses->idx];
+
+            p->buffer[p->index] = pulses->pulse[i];
+            p->index = (p->index + 1) % ECRNX_RADAR_PULSE_MAX;
+            if (p->count < ECRNX_RADAR_PULSE_MAX)
+                p->count++;
+        }
+
+        /* Defer pulse processing in separate work */
+        if (! work_pending(&ecrnx_hw->radar.detection_work))
+            schedule_work(&ecrnx_hw->radar.detection_work);
+    }
+
+    /* Reset the radar element and re-use it */
+    pulses->cnt = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+    wmb();
+
+    /* Push back the buffer to the LMAC */
+    ipc_host_radarbuf_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+#endif
+radar_no_push:
+    return ret;
+#else
+    return -1;
+#endif
+}
+
+/**
+ * ecrnx_prim_tbtt_ind() - IRQ handler callback for %IPC_IRQ_E2A_TBTT_PRIM
+ *
+ * @pthis: Pointer to main driver data
+ */
+static void ecrnx_prim_tbtt_ind(void *pthis)
+{
+#ifdef CONFIG_ECRNX_SOFTMAC
+    struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+    ecrnx_tx_bcns(ecrnx_hw);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+}
+
+/**
+ * ecrnx_sec_tbtt_ind() - IRQ handler callback for %IPC_IRQ_E2A_TBTT_SEC
+ *
+ * @pthis: Pointer to main driver data
+ */
+static void ecrnx_sec_tbtt_ind(void *pthis)
+{
+}
+
+/**
+ * ecrnx_dbgind() - IRQ handler callback for %IPC_IRQ_E2A_DBG
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from dbgmsgs_pool
+ */
+#ifdef CONFIG_ECRNX_ESWIN_USB
+ extern void usb_dbg_printf(void * data, int len);
+#endif
+static u8 ecrnx_dbgind(void *pthis, void *hostid)
+{
+    u8 ret = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+    struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+    struct ecrnx_ipc_elem *elem = hostid;
+    struct ipc_dbg_msg *dbg_msg = elem->addr;
+
+    REG_SW_SET_PROFILING(ecrnx_hw, SW_PROF_DBGIND);
+
+    /* Look for pattern which means that this hostbuf has been used for a MSG */
+    if (dbg_msg->pattern != IPC_DBG_VALID_PATTERN) {
+        ret = -1;
+        goto dbg_no_push;
+    }
+
+    /* Display the string */
+    //printk("%s %s", (char *)FW_STR, (char *)dbg_msg->string);
+
+    /* Reset the msg element and re-use it */
+    dbg_msg->pattern = 0;
+    wmb();
+
+    /* Push back the buffer to the LMAC */
+    ipc_host_dbgbuf_push(ecrnx_hw->ipc_env, elem, (u32)elem->dma_addr);
+
+dbg_no_push:
+    REG_SW_CLEAR_PROFILING(ecrnx_hw, SW_PROF_DBGIND);
+
+#else
+    struct sk_buff *skb = (struct sk_buff *)hostid;
+#ifdef CONFIG_ECRNX_ESWIN_USB
+    usb_dbg_printf(skb->data, skb->len);
+#else
+    uint8_t string[IPC_DBG_PARAM_SIZE] = {0}; 
+    if(skb->len < IPC_DBG_PARAM_SIZE)
+    {
+        memcpy(string, skb->data, skb->len);
+    }
+    else
+    {
+        printk("waring: string buff no enough \n");
+        memcpy(string, skb->data, IPC_DBG_PARAM_SIZE-1);
+    }
+    ECRNX_PRINT("%s %s", (char *)FW_STR, (char *)string);
+#endif
+#endif
+
+    return ret;
+}
+
+/**
+ * ecrnx_ipc_rxbuf_init() - Allocate and initialize RX buffers.
+ *
+ * @ecrnx_hw: Main driver data
+ * @rx_bufsz: Size of the buffer to be allocated
+ *
+ * This function updates the RX buffer size according to the parameter and allocates the
+ * RX buffers
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+int ecrnx_ipc_rxbuf_init(struct ecrnx_hw *ecrnx_hw, uint32_t rx_bufsz)
+{
+    ecrnx_hw->ipc_env->rx_bufsz = rx_bufsz;
+    return(ecrnx_ipc_rxbuf_elems_allocs(ecrnx_hw));
+}
+#endif
+/**
+ * ecrnx_ipc_init() - Initialize IPC interface.
+ *
+ * @ecrnx_hw: Main driver data
+ * @shared_ram: Pointer to shared memory that contains IPC shared struct
+ *
+ * This function initializes IPC interface by registering callbacks, setting
+ * shared memory area and calling IPC Init function.
+ * It should be called only once during driver's lifetime.
+ */
+int ecrnx_ipc_init(struct ecrnx_hw *ecrnx_hw, u8 *shared_ram)
+{
+    struct ipc_host_cb_tag cb;
+    int res;
+    ECRNX_DBG("%s entry!!", __func__);
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* initialize the API interface */
+    cb.recv_data_ind   = ecrnx_rxdataind;
+    cb.recv_radar_ind  = ecrnx_radarind;
+    cb.recv_msg_ind    = ecrnx_msgind;
+    cb.recv_msgack_ind = ecrnx_msgackind;
+    cb.recv_dbg_ind    = ecrnx_dbgind;
+    cb.send_data_cfm   = ecrnx_txdatacfm;
+    cb.handle_data_cfm   = ecrnx_handle_tx_datacfm;
+
+    cb.prim_tbtt_ind   = ecrnx_prim_tbtt_ind;
+    cb.sec_tbtt_ind    = ecrnx_sec_tbtt_ind;
+    cb.recv_unsup_rx_vec_ind = ecrnx_unsup_rx_vec_ind;
+
+    /* set the IPC environment */
+    ecrnx_hw->ipc_env = (struct ipc_host_env_tag *)
+                       kzalloc(sizeof(struct ipc_host_env_tag), GFP_KERNEL);
+
+    if (!ecrnx_hw->ipc_env)
+        return -ENOMEM;
+
+    /* call the initialization of the IPC */
+    ipc_host_init(ecrnx_hw->ipc_env, &cb,
+                  (struct ipc_shared_env_tag *)shared_ram, ecrnx_hw);
+
+    ecrnx_cmd_mgr_init(&ecrnx_hw->cmd_mgr);
+
+    ecrnx_rx_reord_init(ecrnx_hw);
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+    ecrnx_sdio_init(ecrnx_hw);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+    ecrnx_usb_init(ecrnx_hw);
+#endif
+
+    res = ecrnx_elems_allocs(ecrnx_hw);
+    if (res) {
+
+        kfree(ecrnx_hw->ipc_env);
+        ecrnx_hw->ipc_env = NULL;
+    }
+    ECRNX_DBG("%s exit!!", __func__);
+    return res;
+}
+
+/**
+ * ecrnx_ipc_deinit() - Release IPC interface
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void ecrnx_ipc_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    ecrnx_ipc_tx_drain(ecrnx_hw);
+    ecrnx_cmd_mgr_deinit(&ecrnx_hw->cmd_mgr);
+#ifndef CONFIG_ECRNX_ESWIN
+    ecrnx_elems_deallocs(ecrnx_hw);
+#endif
+
+    ecrnx_rx_reord_deinit(ecrnx_hw);
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+    if (ecrnx_hw->ipc_env->shared) {
+        kfree(ecrnx_hw->ipc_env->shared);
+        ecrnx_hw->ipc_env->shared = NULL;
+    }
+    ecrnx_sdio_deinit(ecrnx_hw);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+    ecrnx_usb_deinit(ecrnx_hw);
+#endif
+
+    if (ecrnx_hw->ipc_env) {
+        kfree(ecrnx_hw->ipc_env);
+        ecrnx_hw->ipc_env = NULL;
+    }
+}
+
+/**
+ * ecrnx_ipc_start() - Start IPC interface
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void ecrnx_ipc_start(struct ecrnx_hw *ecrnx_hw)
+{
+    ipc_host_enable_irq(ecrnx_hw->ipc_env, IPC_IRQ_E2A_ALL);
+}
+
+/**
+ * ecrnx_ipc_stop() - Stop IPC interface
+ *
+ * @ecrnx_hw: Main driver data
+ */
+void ecrnx_ipc_stop(struct ecrnx_hw *ecrnx_hw)
+{
+    ipc_host_disable_irq(ecrnx_hw->ipc_env, IPC_IRQ_E2A_ALL);
+}
+
+/**
+ * ecrnx_ipc_tx_drain() - Flush IPC TX buffers
+ *
+ * @ecrnx_hw: Main driver data
+ *
+ * This assumes LMAC is still (tx wise) and there's no TX race until LMAC is up
+ * tx wise.
+ * This also lets both IPC sides remain in sync before resetting the LMAC,
+ * e.g with ecrnx_send_reset.
+ */
+void ecrnx_ipc_tx_drain(struct ecrnx_hw *ecrnx_hw)
+{
+    int i, j;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if (!ecrnx_hw->ipc_env) {
+        printk(KERN_CRIT "%s: bypassing (restart must have failed)\n", __func__);
+        return;
+    }
+
+    for (i = 0; i < ECRNX_HWQ_NB; i++) {
+        for (j = 0; j < nx_txuser_cnt[i]; j++) {
+            struct sk_buff *skb;
+            while ((skb = (struct sk_buff *)ipc_host_tx_flush(ecrnx_hw->ipc_env, i, j))) {
+                struct ecrnx_sw_txhdr *sw_txhdr =
+                    ((struct ecrnx_txhdr *)skb->data)->sw_hdr;
+#ifndef CONFIG_ECRNX_ESWIN        
+       #ifdef CONFIG_ECRNX_AMSDUS_TX
+                if (sw_txhdr->desc.host.packet_cnt > 1) {
+                    struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+                    list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+                        dma_unmap_single(ecrnx_hw->dev, amsdu_txhdr->dma_addr,
+                                         amsdu_txhdr->map_len, DMA_TO_DEVICE);
+                        dev_kfree_skb_any(amsdu_txhdr->skb);
+                    }
+                }
+       #endif
+                kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+                dma_unmap_single(ecrnx_hw->dev, sw_txhdr->dma_addr,
+                                 sw_txhdr->map_len, DMA_TO_DEVICE);
+#endif
+                skb_pull(skb, sw_txhdr->headroom);
+#ifdef CONFIG_ECRNX_SOFTMAC
+                ieee80211_free_txskb(ecrnx_hw->hw, skb);
+#else
+                dev_kfree_skb_any(skb);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+            }
+        }
+    }
+}
+
+/**
+ * ecrnx_ipc_tx_pending() - Check if TX pframes are pending at FW level
+ *
+ * @ecrnx_hw: Main driver data
+ */
+bool ecrnx_ipc_tx_pending(struct ecrnx_hw *ecrnx_hw)
+{
+    return ipc_host_tx_frames_pending(ecrnx_hw->ipc_env);
+}
+
+/**
+ * ecrnx_error_ind() - %DBG_ERROR_IND message callback
+ *
+ * @ecrnx_hw: Main driver data
+ *
+ * This function triggers the UMH script call that will indicate to the user
+ * space the error that occurred and stored the debug dump. Once the UMH script
+ * is executed, the ecrnx_umh_done() function has to be called.
+ */
+void ecrnx_error_ind(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_ipc_elem_var *elem = &ecrnx_hw->dbgdump_elem.buf;
+    struct dbg_debug_dump_tag *dump = elem->addr;
+
+    dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr, elem->size,
+                               DMA_FROM_DEVICE);
+    dev_err(ecrnx_hw->dev, "(type %d): dump received\n",
+            dump->dbg_info.error_type);
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+    ecrnx_hw->debugfs.trace_prst = true;
+    ecrnx_trigger_um_helper(&ecrnx_hw->debugfs);
+#endif
+}
+
+/**
+ * ecrnx_umh_done() - Indicate User Mode helper finished
+ *
+ * @ecrnx_hw: Main driver data
+ *
+ */
+void ecrnx_umh_done(struct ecrnx_hw *ecrnx_hw)
+{
+    if (!test_bit(ECRNX_DEV_STARTED, &ecrnx_hw->flags))
+        return;
+
+    /* this assumes error_ind won't trigger before ipc_host_dbginfobuf_push
+       is called and so does not irq protect (TODO) against error_ind */
+#ifdef CONFIG_ECRNX_DEBUGFS
+    ecrnx_hw->debugfs.trace_prst = false;
+#ifndef CONFIG_ECRNX_ESWIN
+    ipc_host_dbginfobuf_push(ecrnx_hw->ipc_env, ecrnx_hw->dbgdump_elem.buf.dma_addr);
+#endif
+#endif
+}
diff --git a/drivers/net/wireless/eswin/ecrnx_utils.h b/drivers/net/wireless/eswin/ecrnx_utils.h
new file mode 100644 (file)
index 0000000..09315b2
--- /dev/null
@@ -0,0 +1,209 @@
+/**
+ * ecrnx_ipc_utils.h
+ *
+ * IPC utility function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ */
+#ifndef _ECRNX_IPC_UTILS_H_
+#define _ECRNX_IPC_UTILS_H_
+
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/skbuff.h>
+
+#include "lmac_msg.h"
+
+enum ecrnx_dev_flag {
+    ECRNX_DEV_RESTARTING,
+    ECRNX_DEV_STACK_RESTARTING,
+    ECRNX_DEV_STARTED,
+    ECRNX_DEV_ADDING_STA,
+};
+
+struct ecrnx_hw;
+struct ecrnx_sta;
+
+/**
+ * struct ecrnx_ipc_elem - Generic IPC buffer of fixed size
+ *
+ * @addr: Host address of the buffer.
+ * @dma_addr: DMA address of the buffer.
+ */
+struct ecrnx_ipc_elem {
+    void *addr;
+    dma_addr_t dma_addr;
+};
+
+/**
+ * struct ecrnx_ipc_elem_pool - Generic pool of IPC buffers of fixed size
+ *
+ * @nb: Number of buffers currenlty allocated in the pool
+ * @buf: Array of buffers (size of array is @nb)
+ * @pool: DMA pool in which buffers have been allocated
+ */
+struct ecrnx_ipc_elem_pool {
+    int nb;
+    struct ecrnx_ipc_elem *buf;
+    struct dma_pool *pool;
+};
+
+/**
+ * struct ecrnx_ipc_elem - Generic IPC buffer of variable size
+ *
+ * @addr: Host address of the buffer.
+ * @dma_addr: DMA address of the buffer.
+ * @size: Size, in bytes, of the buffer
+ */
+struct ecrnx_ipc_elem_var {
+    void *addr;
+    dma_addr_t dma_addr;
+    size_t size;
+};
+
+/**
+ * struct ecrnx_ipc_dbgdump_elem - IPC buffer for debug dump
+ *
+ * @mutex: Mutex to protect access to debug dump
+ * @buf: IPC buffer
+ */
+struct ecrnx_ipc_dbgdump_elem {
+    struct mutex mutex;
+    struct ecrnx_ipc_elem_var buf;
+};
+
+//static const u32 ecrnx_rxbuff_pattern = 0xCAFEFADE;
+static const u32 ecrnx_rxbuff_pattern = 0xAAAAAA00;
+
+
+/*
+ * Maximum Length of Radiotap header vendor specific data(in bytes)
+ */
+#define RADIOTAP_HDR_VEND_MAX_LEN   16
+
+/*
+ * Maximum Radiotap Header Length without vendor specific data (in bytes)
+ */
+#define RADIOTAP_HDR_MAX_LEN        80
+
+/*
+ * Unsupported HT Frame data length (in bytes)
+ */
+#define UNSUP_RX_VEC_DATA_LEN       2
+
+/**
+ * struct ecrnx_ipc_skb_elem - IPC buffer for SKB element
+ *
+ * @skb: Pointer to the skb buffer allocated
+ * @dma_addr: DMA address of the data buffer fo skb
+ *
+ */
+struct ecrnx_ipc_skb_elem {
+    struct sk_buff *skb;
+    dma_addr_t dma_addr;
+};
+
+#ifdef CONFIG_ECRNX_FULLMAC
+
+/* Maximum number of rx buffer the fw may use at the same time */
+#define ECRNX_RXBUFF_MAX (64 * NX_REMOTE_STA_MAX)
+
+/**
+ * struct ecrnx_ipc_rxbuf_elems - IPC buffers for RX
+ *
+ * @skb: Array of buffer push to FW.
+ * @idx: Index of the last pushed skb.(Use to find the next free entry quicker)
+ *
+ * Note: contrary to softmac version, dma_addr are stored inside skb->cb.
+ * (cf &struct ecrnx_skb_cb)
+ */
+struct ecrnx_ipc_rxbuf_elems {
+    struct sk_buff *skb[ECRNX_RXBUFF_MAX];
+    int idx;
+};
+
+/**
+ * struct ecrnx_skb_cb - Control Buffer structure for RX buffer
+ *
+ * @dma_addr: DMA address of the data buffer
+ * @pattern: Known pattern (used to check pointer on skb)
+ * @idx: Index in &struct ecrnx_hw.rxbuff_table that contains address of this
+ * buffer
+ */
+struct ecrnx_skb_cb {
+    dma_addr_t dma_addr;
+    uint32_t pattern;
+    uint32_t idx;
+};
+
+#define ECRNX_RXBUFF_DMA_ADDR_SET(skbuff, addr)          \
+    ((struct ecrnx_skb_cb *)(skbuff->cb))->dma_addr = addr
+#define ECRNX_RXBUFF_DMA_ADDR_GET(skbuff)                \
+    ((struct ecrnx_skb_cb *)(skbuff->cb))->dma_addr
+
+#define ECRNX_RXBUFF_PATTERN_SET(skbuff, pat)                \
+    ((struct ecrnx_skb_cb *)(skbuff->cb))->pattern = pat
+#define ECRNX_RXBUFF_PATTERN_GET(skbuff)         \
+    ((struct ecrnx_skb_cb *)(skbuff->cb))->pattern
+
+#define ECRNX_RXBUFF_IDX_SET(skbuff, val)                \
+    ((struct ecrnx_skb_cb *)(skbuff->cb))->idx = val
+#define ECRNX_RXBUFF_IDX_GET(skbuff)             \
+    ((struct ecrnx_skb_cb *)(skbuff->cb))->idx
+
+#define ECRNX_RXBUFF_VALID_IDX(idx) ((idx) < ECRNX_RXBUFF_MAX)
+
+/* Used to ensure that hostid set to fw is never 0 */
+#define ECRNX_RXBUFF_IDX_TO_HOSTID(idx) ((idx) + 1)
+#define ECRNX_RXBUFF_HOSTID_TO_IDX(hostid) ((hostid) - 1)
+
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+int ecrnx_ipc_rxbuf_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+                               struct ecrnx_ipc_skb_elem *elem);
+void ecrnx_ipc_rxbuf_elem_repush(struct ecrnx_hw *ecrnx_hw,
+                                struct ecrnx_ipc_skb_elem *elem);
+#else
+int ecrnx_ipc_rxbuf_elem_allocs(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_ipc_rxbuf_elem_pull(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb);
+void ecrnx_ipc_rxbuf_elem_sync(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+                              int len);
+void ecrnx_ipc_rxdesc_elem_repush(struct ecrnx_hw *ecrnx_hw,
+                                 struct ecrnx_ipc_elem *elem);
+void ecrnx_ipc_rxbuf_elem_repush(struct ecrnx_hw *ecrnx_hw,
+                                struct sk_buff *skb);
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+void ecrnx_printf(const char *fmt, ...);
+void ecrnx_ipc_msg_push(struct ecrnx_hw *ecrnx_hw, void *msg_buf, uint16_t len);
+void ecrnx_ipc_txdesc_push(struct ecrnx_hw *ecrnx_hw, void *tx_desc,
+                          void *hostid, int hw_queue, int user);
+void *ecrnx_ipc_fw_trace_desc_get(struct ecrnx_hw *ecrnx_hw);
+int ecrnx_ipc_rxbuf_init(struct ecrnx_hw *ecrnx_hw, uint32_t rx_bufsz);
+int ecrnx_ipc_init(struct ecrnx_hw *ecrnx_hw, u8 *shared_ram);
+void ecrnx_ipc_deinit(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_ipc_start(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_ipc_stop(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_ipc_tx_drain(struct ecrnx_hw *ecrnx_hw);
+bool ecrnx_ipc_tx_pending(struct ecrnx_hw *ecrnx_hw);
+
+struct ipc_host_env_tag;
+int ecrnx_ipc_elem_var_allocs(struct ecrnx_hw *ecrnx_hw,
+                             struct ecrnx_ipc_elem_var *elem, size_t elem_size,
+                             enum dma_data_direction dir,
+                             void *buf, const void *init,
+                             void (*push)(struct ipc_host_env_tag *, uint32_t));
+void ecrnx_ipc_elem_var_deallocs(struct ecrnx_hw *ecrnx_hw,
+                                struct ecrnx_ipc_elem_var *elem);
+int ecrnx_ipc_unsup_rx_vec_elem_allocs(struct ecrnx_hw *ecrnx_hw,
+                                      struct ecrnx_ipc_skb_elem *elem);
+
+void ecrnx_error_ind(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_umh_done(struct ecrnx_hw *ecrnx_hw);
+
+void ecrnx_ipc_sta_buffer_init(struct ecrnx_hw *ecrnx_hw, int sta_idx);
+void ecrnx_ipc_sta_buffer(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta, int tid, int size);
+
+#endif /* _ECRNX_IPC_UTILS_H_ */
diff --git a/drivers/net/wireless/eswin/ecrnx_version.h b/drivers/net/wireless/eswin/ecrnx_version.h
new file mode 100644 (file)
index 0000000..c56e942
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _ECRNX_VERSION_H_
+#define _ECRNX_VERSION_H_
+
+
+static inline void ecrnx_print_version(void)
+{
+    printk(ECRNX_VERS_BANNER"\n");
+}
+
+#endif /* _ECRNX_VERSION_H_ */
diff --git a/drivers/net/wireless/eswin/eswin_port/eswin_utils.c b/drivers/net/wireless/eswin/eswin_port/eswin_utils.c
new file mode 100644 (file)
index 0000000..bd75cc6
--- /dev/null
@@ -0,0 +1,194 @@
+/**
+ ******************************************************************************
+ *
+ * @file eswin_utils.c
+ *
+ *@brief msg and management frame send functions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_msg_tx.h"
+#ifdef CONFIG_ECRNX_FULLMAC
+#include "ecrnx_mesh.h"
+#endif
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+
+#include "eswin_utils.h"
+
+#if 0
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+#include "sdio_host_interface.h"
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+#include "usb_host_interface.h"
+#endif
+#endif
+
+typedef int (*fn_send_t)(void * buff, int len, int flag);
+
+
+fn_send_t   ecrnx_host_send;
+
+
+void ecrnx_send_handle_register(void * fn)
+{
+       ecrnx_host_send = (fn_send_t)fn;
+}
+
+
+int host_send(void *buff, int len, int flag)
+{
+       //ECRNX_DBG("%s:len:%d,flag %#x \n", __func__, len, flag);
+       return ecrnx_host_send(buff, len, flag);
+}
+
+void ecrnx_frame_send(struct ecrnx_hw *ecrnx_hw, struct txdesc_api *tx_desc,
+                          struct sk_buff *skb, int hw_queue, int user)
+{
+    u32_l tx_flag;
+    //struct ecrnx_txhdr *txhdr = NULL;
+    struct ecrnx_sw_txhdr *sw_txhdr = NULL;
+
+    ecrnx_hw->data_tx++;
+    //send tx desc
+    tx_flag = ((user << FLAG_TXDESC_USER_SHIFT) & FLAG_TXDESC_USER_MASK) | ((hw_queue << FLAG_TXDESC_QUEUE_SHIFT) & FLAG_TXDESC_QUEUE_MASK);
+    tx_flag |= TX_FLAG_TX_DESC & FLAG_MSG_TYPE_MASK;
+#if 0
+       ECRNX_DBG("%s, user %d, queue %d, flag %#x, hostid 0x%08x, skb 0x%08x\n", __func__,
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+               user, hw_queue, tx_flag, tx_desc->host.packet_addr[0], (u32_l)skb);
+       tx_desc->host.packet_addr[0] = (u32_l)skb;
+#else
+               user, hw_queue, tx_flag, tx_desc->host.packet_addr, (u32_l)skb);
+       tx_desc->host.packet_addr = (u32_l)skb;
+#endif
+#endif
+
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+    uint8_t *data = skb->data;
+    sw_txhdr = container_of(tx_desc, struct ecrnx_sw_txhdr, desc);
+    data += sw_txhdr->offset;
+    data -= ECRNX_TX_TXDESC_API_ALIGN;
+    memcpy(data, tx_desc, sizeof(struct txdesc_api));
+
+    host_send((void*)data, ECRNX_TX_TXDESC_API_ALIGN + sw_txhdr->frame_len,  tx_flag);
+
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+    uint8_t *data;
+    struct ecrnx_txhdr *txhdr = (struct ecrnx_txhdr *)skb->data;
+
+    sw_txhdr = txhdr->sw_hdr;
+    skb_pull(skb, sw_txhdr->offset - ECRNX_TX_TXDESC_API_ALIGN - sizeof(u32_l));
+    memcpy(skb->data, (char*)&tx_flag, sizeof(u32_l));
+    memcpy((uint8_t *)skb->data + sizeof(u32_l), (char*)tx_desc, sizeof(struct txdesc_api));
+
+    data = skb->data - sizeof(ptr_addr);
+    memcpy(data, (char*)&txhdr, sizeof(void *)); //store the ecrnx thhdr to the skb, cfm need use it
+    host_send((void *)skb, skb->len, tx_flag);
+#endif
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    //send amsdu sub frame
+    if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU) {
+        struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+        printk("...........amsdu tx\n");
+        list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+        printk("...........amsdu tx: %x, %u\n", amsdu_txhdr->send_pos, amsdu_txhdr->map_len);
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+            host_send((void*)amsdu_txhdr->send_pos, amsdu_txhdr->map_len, tx_flag);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+            struct sk_buff *amsdu_skb = dev_alloc_skb(amsdu_txhdr->map_len + sizeof(int));
+
+            memcpy(amsdu_skb->data, &tx_flag, sizeof(int));
+            memcpy((char*)amsdu_skb->data + sizeof(int), amsdu_txhdr->send_pos, amsdu_txhdr->map_len);
+            amsdu_skb->len = amsdu_txhdr->map_len + sizeof(int);
+            host_send((void *)amsdu_skb, amsdu_skb->len, tx_flag);
+#endif
+        }
+    }
+#endif
+    return;
+}
+
+static inline void ecrnx_bcn_param_send(struct mm_bcn_change_req *req)
+{
+    if (req->bcn_ptr && req->bcn_len) {
+        host_send((void*)req->bcn_ptr, req->bcn_len, TX_FLAG_MSG_BEACON_IE);
+    }
+}
+
+static inline void ecrnx_scanie_param_send(struct scan_start_req *req)
+{
+    if (req->add_ies && req->add_ie_len) {
+        host_send((void*)req->add_ies, req->add_ie_len, TX_FLAG_MSG_SCAN_IE);
+    }
+}
+
+static inline void ecrnx_scanuie_param_send(struct scanu_start_req *req)
+{
+    if (req->add_ies && req->add_ie_len) {
+        host_send((void*)req->add_ies, req->add_ie_len, TX_FLAG_MSG_SCANU_IE);
+    }
+}
+
+static inline void ecrnx_apm_param_send(struct apm_start_req *req)
+{
+    if (req->bcn_addr && req->bcn_len) {
+        host_send((void*)req->bcn_addr, req->bcn_len, TX_FLAG_MSG_SOFTAP_IE);
+    }
+}
+
+void ecrnx_msg_send(struct ecrnx_cmd *cmd, uint16_t len)
+{
+    void *param = cmd->a2e_msg->param;
+    int param_len = cmd->a2e_msg->param_len;
+
+    //cmd->a2e_msg->hostid = (u64_l)cmd;
+    memcpy(cmd->a2e_msg->hostid, &cmd, sizeof(struct ecrnx_cmd *));
+
+    switch (cmd->a2e_msg->id) {
+        case MM_BCN_CHANGE_REQ:
+            if (param_len >= sizeof(struct mm_bcn_change_req)) {
+                ecrnx_bcn_param_send((struct mm_bcn_change_req *)param);
+            }
+            break;
+        case SCAN_START_REQ:
+            if (param_len >= sizeof(struct scan_start_req)) {
+                ecrnx_scanie_param_send((struct scan_start_req *)param);
+            }
+            break;
+        case SCANU_START_REQ:
+            if (param_len >= sizeof(struct scanu_start_req)) {
+                ecrnx_scanuie_param_send((struct scanu_start_req *)param);
+            }
+            break;
+        case APM_START_REQ:
+            if (param_len >= sizeof(struct apm_start_req)) {
+                ecrnx_apm_param_send((struct apm_start_req *)param);
+            }
+            break;
+        default:
+            break;
+    }
+
+    host_send((void*)cmd->a2e_msg, len, TX_FLAG_MSG_E);
+}
+
+const struct ecrnx_element *cfg80211_find_ecrnx_elem(u8 eid, const u8 *ies, int len)
+{
+    const struct ecrnx_element *elem;
+    for_each_ecrnx_element(elem, ies, len) {
+        if (elem->id == (eid))
+            return elem;
+    }
+    return NULL;
+}
diff --git a/drivers/net/wireless/eswin/eswin_port/eswin_utils.h b/drivers/net/wireless/eswin/eswin_port/eswin_utils.h
new file mode 100644 (file)
index 0000000..1258c88
--- /dev/null
@@ -0,0 +1,123 @@
+/**
+ ******************************************************************************
+ *
+ * @file eswin_utils.h
+ *
+ * @brief File containing the definition of tx/rx info and debug descriptors.
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ESWIN_UTILS_H_
+#define _ESWIN_UTILS_H_
+
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+// for param in sdio irq flag
+#define FLAG_MSG_TYPE_MASK       0x0f
+
+// queue in sdio irq flag
+#define FLAG_TXDESC_QUEUE_SHIFT  5
+#define FLAG_TXDESC_QUEUE_MASK   0xe0
+#define FLAG_TXDESC_USER_SHIFT   4
+#define FLAG_TXDESC_USER_MASK    0x10
+
+// tag in tx payload param
+//#define FLAG_TXPAYLOAD_PARAM_SHIFT 6
+//#define FLAG_TXPAYLOAD_PARAM_MASK  0xffc0   //10 bits
+#define TX_DESC_TAG_LEN            492  //SLAVE fhost_tx_desc_tag_len
+
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+// for param in sdio irq flag
+#define FLAG_MSG_TYPE_MASK       0x00ff
+
+// queue in sdio irq flag
+#define FLAG_TXDESC_USER_SHIFT   8
+#define FLAG_TXDESC_USER_MASK    0x0f00
+#define FLAG_TXDESC_QUEUE_SHIFT  12
+#define FLAG_TXDESC_QUEUE_MASK   0xf000
+#endif
+
+typedef enum {
+    TX_FLAG_INVALID,
+    TX_FLAG_MSG_E,
+    TX_FLAG_MSG_SCAN_IE,
+    TX_FLAG_MSG_SCANU_IE,
+    TX_FLAG_MSG_BEACON_IE,
+    TX_FLAG_MSG_PROBE_IE,
+    TX_FLAG_MSG_SOFTAP_IE,
+    TX_FLAG_TX_DESC,
+    TX_FLAG_MSG_DEBUGFS_IE,
+    TX_FLAG_IWPRIV_IE,
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+       TX_FLAG_AMT_IWPRIV_IE,  //10
+#endif
+
+    TX_FLAG_MAX,
+}host_tx_flag_e;
+
+typedef enum {
+    DBG_TYPE_D,      //debug msg type
+    DBG_TYPE_I,      //info msg type
+    DBG_TYPE_W,      //warning msg type
+    DBG_TYPE_E,      //error msg type
+    DBG_TYPE_O,      //this type means always output
+    DBG_TYPE_MAX
+}dbg_print_type_e;
+
+typedef struct {
+    uint8_t direct; // slave log director, 0 is print by uart, 1 is send to host
+    dbg_print_type_e dbg_level; //slave debug level
+}dbg_req_t;
+
+typedef struct {
+    uint32_t  hostid;
+    uint32_t  tag;
+}data_req_msg_t;
+
+typedef struct {
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+    uint32_t resvd; /* for sdio */
+    uint32_t rlen;  /* for sdio */
+#endif
+    uint32_t frm_type;
+}dispatch_hdr_t;
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct agg_elem {
+    struct list_head head;
+    struct sk_buff* skb;
+    /// Traffic Index
+    uint8_t tid;
+    uint16_t real_offset; /* dma address align 4, real data - real_offset  */
+    u32 sn;
+    /*bitmaps of agg frame to upaold*/
+    uint64_t agg_upload_idx;
+    /*bitmaps of agg frame upaold status, 0 is forward, 1 is delete*/
+    uint64_t agg_upload_status;
+};
+
+/**
+ * struct pulse_elem - elements in pulse queue
+ * @ts: time stamp in usecs
+ */
+struct defrag_elem {
+    struct list_head head;
+    struct sk_buff* skb;
+    /// Traffic Index
+    uint8_t tid;
+    uint16_t real_offset; /* dma address align 4, real data - real_offset  */
+    u32 sn;
+};
+
+void ecrnx_frame_send(struct ecrnx_hw *ecrnx_hw, struct txdesc_api *tx_desc,
+                          struct sk_buff *skb, int hw_queue, int user);
+void ecrnx_msg_send(struct ecrnx_cmd *cmd, uint16_t len);
+int host_send(void *buff, int len, int flag);
+const struct ecrnx_element *cfg80211_find_ecrnx_elem(u8 eid, const u8 *ies, int len);
+
+#endif
diff --git a/drivers/net/wireless/eswin/feature_config/6600_config b/drivers/net/wireless/eswin/feature_config/6600_config
new file mode 100644 (file)
index 0000000..4d24d33
--- /dev/null
@@ -0,0 +1,87 @@
+export ECRNX_MODULE_NAME=wlan_ecr6600
+
+# Enable 6600 hal config
+export CONFIG_6600_HAL=y
+# Enable A-MSDU support (need FW support)
+## Select this if FW is compiled with AMSDU support
+export CONFIG_ECRNX_SPLIT_TX_BUF=n
+
+## Select this TO send AMSDU
+export CONFIG_ECRNX_AMSDUS_TX=n
+
+# Enable BFMER support (need FW support)
+export CONFIG_ECRNX_BFMER=n
+CONFIG_ECRNX_MUMIMO_TX=n
+
+# Enable handling of radar event
+export CONFIG_ECRNX_RADAR=y
+
+# Enable HW queue for Broadcast/Multicast traffic (need FW support)
+export CONFIG_ECRNX_BCMC=y
+
+# Enable Monitor+Data interface support (need FW support)
+export CONFIG_ECRNX_MON_DATA=n
+
+# extra DEBUG config
+export CONFIG_ECRNX_SW_PROFILING=n
+export CONFIG_ECRNX_DBG=y
+export CONFIG_ECRNX_DBG_LEVEL=3
+export CONFIG_DEBUG_FS=y
+export CONFIG_ECRNX_DEBUGFS_CUSTOM ?= y
+# CONFIG PLATFORM
+export CONFIG_ECRNX_ESWIN=y
+export CONFIG_ECRNX_ESWIN_SDIO=y
+
+export CONFIG_ECRNX_ESWIN_USB=n
+
+# CONFIG SYSTERM TEST
+export CONFIG_TEST_ESWIN_SDIO=n
+
+# CONFIG SYSTERM TEST
+export CONFIG_STANDALONE_WIFI=n
+
+# CONFIG SYSTERM TEST
+export CONFIG_STANDALONE_WIFI_BLE=y
+# Enable BFMER support (need FW support)
+export CONFIG_ECRNX_HE=y
+
+# Enable P2P Listen
+export CONFIG_ECRNX_P2P=y
+# Enable 5G
+export CONFIG_ECRNX_5G=n
+
+#CONFIG SDIO WIFI CALIBRATION
+export CONFIG_ECRNX_WIFO_CAIL=y
+
+#
+# WAITING FOR KCONFIG {
+#
+export CONFIG_ECRNX_SOFTMAC=n
+export CONFIG_ECRNX_FULLMAC=m
+export CONFIG_ECRNX_FHOST=n
+
+#
+# DEBUG OPTIONS
+export CONFIG_ECRNX_UM_HELPER_DFLT="/dini/dini_bin/ecrnx_umh.sh"
+
+#
+# FW ARCH:
+export CONFIG_ECRNX_SDM=n
+export CONFIG_ECRNX_TL4=n
+
+# IPC version
+export CONFIG_ECRNX_OLD_IPC=n
+
+# Support of P2P DebugFS for enabling/disabling NoA and OppPS
+export CONFIG_ECRNX_P2P_DEBUGFS=n
+
+# config_ceva_rtos = y use ceva rtos and add task_cli id
+# config_ceva_rtos = n use freertos and no task_cli id
+#export CONFIG_CEVA_RTOS=n
+
+export NX_VIRT_DEV_MAX=3
+export NX_REMOTE_STA_MAX=2
+export NX_MU_GROUP_MAX=62
+export NX_TXDESC_CNT=4
+export NX_TX_MAX_RATES=4
+export NX_CHAN_CTXT_CNT=3
diff --git a/drivers/net/wireless/eswin/feature_config/6600u_config b/drivers/net/wireless/eswin/feature_config/6600u_config
new file mode 100644 (file)
index 0000000..2ec3c83
--- /dev/null
@@ -0,0 +1,98 @@
+export ECRNX_MODULE_NAME=wlan_ecr6600u_usb
+
+# Enable A-MSDU support (need FW support)
+## Select this if FW is compiled with AMSDU support
+export CONFIG_ECRNX_SPLIT_TX_BUF=n
+
+## Select this TO send AMSDU
+export CONFIG_ECRNX_AMSDUS_TX=n
+
+# Enable BFMER support (need FW support)
+export CONFIG_ECRNX_BFMER=n
+CONFIG_ECRNX_MUMIMO_TX=n
+
+# Enable handling of radar event
+export CONFIG_ECRNX_RADAR=y
+
+# Enable HW queue for Broadcast/Multicast traffic (need FW support)
+export CONFIG_ECRNX_BCMC=y
+
+# Enable Monitor+Data interface support (need FW support)
+export CONFIG_ECRNX_MON_DATA=n
+
+#Tx and RX data processing method
+export CONFIG_ECRNX_WORKQUEUE=n
+export CONFIG_ECRNX_TASKLET=n
+export CONFIG_ECRNX_KTHREAD=y
+
+# extra DEBUG config
+export CONFIG_ECRNX_SW_PROFILING=n
+export CONFIG_ECRNX_DBG=y
+export CONFIG_ECRNX_DBG_LEVEL=3
+export CONFIG_DEBUG_FS=n
+export CONFIG_ECRNX_DEBUGFS_CUSTOM ?= n
+# Support of P2P DebugFS for enabling/disabling NoA and OppPS
+export CONFIG_ECRNX_P2P_DEBUGFS=n
+
+# CONFIG PLATFORM
+export CONFIG_ECRNX_ESWIN=y
+export CONFIG_ECRNX_ESWIN_SDIO=n
+
+export CONFIG_ECRNX_ESWIN_USB=y
+
+# CONFIG SYSTERM TEST
+export CONFIG_TEST_ESWIN_SDIO=n
+
+# CONFIG SYSTERM TEST
+export CONFIG_STANDALONE_WIFI=n
+
+# CONFIG SYSTERM TEST
+export CONFIG_STANDALONE_WIFI_BLE=y
+# Enable BFMER support (need FW support)
+export CONFIG_ECRNX_HE=y
+
+# Enable P2P Listen
+export CONFIG_ECRNX_P2P=y
+# Enable 5G
+export CONFIG_ECRNX_5G=n
+
+#CONFIG SDIO WIFI CALIBRATION
+export CONFIG_ECRNX_WIFO_CAIL=y
+
+#
+# WAITING FOR KCONFIG {
+#
+export CONFIG_ECRNX_SOFTMAC=n
+export CONFIG_ECRNX_FULLMAC=m
+export CONFIG_ECRNX_FHOST=n
+
+#
+#Build for Android Platform
+export CONFIG_ECRNX_ANDRIOD=y
+
+#
+# DEBUG OPTIONS
+export CONFIG_ECRNX_UM_HELPER_DFLT="/dini/dini_bin/ecrnx_umh.sh"
+
+#
+# FW ARCH:
+export CONFIG_ECRNX_SDM=n
+export CONFIG_ECRNX_TL4=n
+
+# IPC version
+export CONFIG_ECRNX_OLD_IPC=n
+
+# config_ceva_rtos = y use ceva rtos and add task_cli id
+# config_ceva_rtos = n use freertos and no task_cli id
+export CONFIG_CEVA_RTOS=y
+
+export NX_VIRT_DEV_MAX=3
+export NX_REMOTE_STA_MAX=4
+export NX_MU_GROUP_MAX=62
+export NX_TXDESC_CNT=4
+export NX_TX_MAX_RATES=4
+export NX_CHAN_CTXT_CNT=3
+
+# config gpio for 6600u power key
+#export CONFIG_POWERKEY_GPIO=4
+
diff --git a/drivers/net/wireless/eswin/fullmac/Makefile b/drivers/net/wireless/eswin/fullmac/Makefile
new file mode 100644 (file)
index 0000000..8479e35
--- /dev/null
@@ -0,0 +1,167 @@
+$(ECRNX_MODULE_NAME)-y := ecrnx_msg_tx.o          \
+               ecrnx_msg_rx.o          \
+               ecrnx_utils.o           \
+               ecrnx_cmds.o            \
+               ecrnx_cfgfile.o         \
+               ecrnx_strs.o            \
+               ecrnx_txq.o             \
+               ecrnx_mod_params.o      \
+               ecrnx_platform.o               \
+               ipc_host.o             \
+               hal_desc.o             \
+               ecrnx_iwpriv.o         \
+              fw_head_check.o         \
+               slave_log_buf.o         \
+              $(MAC_SRC)/ecrnx_tdls.o \
+              $(MAC_SRC)/ecrnx_mesh.o \
+              $(MAC_SRC)/ecrnx_main.o \
+              $(MAC_SRC)/ecrnx_rx.o   \
+               $(MAC_SRC)/ecrnx_tx.o \
+               $(MAC_SRC)/ecrnx_calibration_data.o
+
+ifeq ($(CONFIG_ECRNX_ESWIN_SDIO), y)
+$(ECRNX_MODULE_NAME)-y += sdio/sdio.o         \
+               sdio/ecrnx_sdio.o    \
+               sdio/core.o         \
+               sdio/fw.o           \
+              eswin_port/eswin_utils.o 
+ifeq ($(CONFIG_ECRNX_ESWIN_SDIO), y)
+#$(ECRNX_MODULE_NAME)-y += sdio/debug.o         
+endif
+endif
+
+ifeq ($(CONFIG_ECRNX_ESWIN_USB), y)
+$(ECRNX_MODULE_NAME)-y += usb/usb.o         \
+               usb/ecrnx_usb.o    \
+               usb/core.o         \
+               usb/fw.o           \
+              eswin_port/eswin_utils.o
+
+endif
+
+ifeq ($(CONFIG_ECRNX_WIFO_CAIL), y)
+$(ECRNX_MODULE_NAME)-y += $(MAC_SRC)/ecrnx_amt.o
+endif
+
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_RADAR)       += ecrnx_radar.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_DEBUG_FS)         += ecrnx_debugfs.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_DEBUG_FS)         += ecrnx_fw_dump.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_DEBUG_FS)         += ecrnx_fw_trace.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_NL80211_TESTMODE) += ecrnx_testmode.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_BFMER)       += ecrnx_bfmer.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_MUMIMO_TX)   += ecrnx_mu_group.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_DBG)         += ecrnx_debug.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_P2P)         += $(MAC_SRC)/ecrnx_p2p.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_DEBUGFS_CUSTOM) += $(MAC_SRC)/ecrnx_debugfs_func.o
+$(ECRNX_MODULE_NAME)-$(CONFIG_ECRNX_DEBUGFS_CUSTOM) += $(MAC_SRC)/ecrnx_debugfs_custom.o
+
+
+EXTRA_CFLAGS+=-Wno-date-time
+EXTRA_CFLAGS+=-Wno-declaration-after-statement
+EXTRA_CFLAGS+=-Wno-format
+EXTRA_CFLAGS+=-Wno-unused-variable
+EXTRA_CFLAGS+=-Wno-misleading-indentation
+EXTRA_CFLAGS+=-Wno-maybe-uninitialized
+#EXTRA_CFLAGS+=-Wno-int-conversion
+#EXTRA_CFLAGS+=-Wno-unused-function
+#EXTRA_CFLAGS+=-Wno-undef
+#EXTRA_CFLAGS+=-Wno-discarded-qualifiers
+#EXTRA_CFLAGS+=-Wno-unused-label
+
+
+ccflags-y := -DCONFIG_ECRNX_FULLMAC
+
+ccflags-y += -I$(DRIVER_PATH)
+ccflags-y += -I$(DRIVER_PATH)/$(MAC_SRC)
+ccflags-y += -I$(DRIVER_PATH)/eswin_port/
+
+ifeq ($(CONFIG_ECRNX_ESWIN_SDIO), y)
+ccflags-y += -I$(DRIVER_PATH)/sdio/
+else
+ccflags-y += -I$(DRIVER_PATH)/usb/
+endif
+
+ccflags-$(CONFIG_ECRNX_RADAR) += -DCONFIG_ECRNX_RADAR
+ccflags-$(CONFIG_ECRNX_MON_DATA) += -DCONFIG_ECRNX_MON_DATA
+ccflags-$(CONFIG_ECRNX_BFMER) += -DCONFIG_ECRNX_BFMER
+ccflags-$(CONFIG_ECRNX_SPLIT_TX_BUF) += -DCONFIG_ECRNX_SPLIT_TX_BUF
+ifeq ($(CONFIG_ECRNX_SPLIT_TX_BUF), y)
+ccflags-$(CONFIG_ECRNX_AMSDUS_TX) += -DCONFIG_ECRNX_AMSDUS_TX
+endif
+ccflags-$(CONFIG_ECRNX_SW_PROFILING) += -DCONFIG_ECRNX_SW_PROFILING
+ccflags-$(CONFIG_ECRNX_MUMIMO_TX) += -DCONFIG_ECRNX_MUMIMO_TX
+
+ifeq ($(CONFIG_ECRNX_MUMIMO_TX), y)
+ccflags-y += -DCONFIG_USER_MAX=2
+else
+ccflags-y += -DCONFIG_USER_MAX=1
+endif
+
+ifeq ($(CONFIG_ECRNX_BCMC), y)
+ccflags-y += -DNX_TXQ_CNT=5
+else
+ccflags-y += -DNX_TXQ_CNT=4
+endif
+
+# FW PLATFORM
+ifeq ($(CONFIG_ECRNX_DEBUGFS_CUSTOM), y)
+ccflags-$(CONFIG_ECRNX_DEBUGFS_CUSTOM) += -DCONFIG_ECRNX_DEBUGFS_CUSTOM
+endif
+
+ifeq ($(CONFIG_ECRNX_ESWIN), y)
+ccflags-$(CONFIG_ECRNX_ESWIN) += -DCONFIG_ECRNX_ESWIN
+endif
+
+ifeq ($(CONFIG_ECRNX_ESWIN_SDIO), y)
+ccflags-$(CONFIG_ECRNX_ESWIN_SDIO) += -DCONFIG_ECRNX_ESWIN_SDIO
+endif
+
+# SDIO TEST
+ifeq ($(CONFIG_TEST_ESWIN_SDIO), y)
+ccflags-$(CONFIG_TEST_ESWIN_SDIO) += -DCONFIG_TEST_ESWIN_SDIO
+endif
+
+ifeq ($(CONFIG_ECRNX_ESWIN_USB), y)
+ccflags-$(CONFIG_ECRNX_ESWIN_USB) += -DCONFIG_ECRNX_ESWIN_USB
+endif
+
+
+# standalone wifi
+ifeq ($(CONFIG_STANDALONE_WIFI), y)
+ccflags-$(CONFIG_STANDALONE_WIFI) += -DCONFIG_STANDALONE_WIFI
+endif
+
+# standalone wifi+ble
+ifeq ($(CONFIG_STANDALONE_WIFI_BLE), y)
+ccflags-$(CONFIG_STANDALONE_WIFI_BLE) += -DCONFIG_STANDALONE_WIFI_BLE
+endif
+
+# HE config
+ifeq ($(CONFIG_ECRNX_HE), y)
+ccflags-$(CONFIG_ECRNX_HE) += -DCONFIG_ECRNX_HE
+endif
+
+# P2P config
+ifeq ($(CONFIG_ECRNX_P2P), y)
+ccflags-$(CONFIG_ECRNX_P2P) += -DCONFIG_ECRNX_P2P
+endif
+
+# 5G config
+ifeq ($(CONFIG_ECRNX_5G), y)
+ccflags-$(CONFIG_ECRNX_5G) += -DCONFIG_ECRNX_5G
+endif
+
+# Android platform config
+ifeq ($(CONFIG_ECRNX_ANDRIOD), y)
+ccflags-$(CONFIG_ECRNX_ANDRIOD) += -DCONFIG_ECRNX_ANDRIOD
+endif
+
+# For old kernel (<=3.19)
+ifeq ($(shell test $(VERSION) -lt 4 -a "$(CONFIG_VENDOR_ECRNX)" = y ; echo $$?),0)
+ccflags-y += -DCONFIG_VENDOR_ECRNX_VHT_NO80
+endif
+
+#CONFIG SDIO WIFI CALIBRATION
+ifeq ($(CONFIG_ECRNX_WIFO_CAIL), y)
+ccflags-$(CONFIG_ECRNX_WIFO_CAIL) += -DCONFIG_ECRNX_WIFO_CAIL
+endif
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_amt.c b/drivers/net/wireless/eswin/fullmac/ecrnx_amt.c
new file mode 100644 (file)
index 0000000..5aa4384
--- /dev/null
@@ -0,0 +1,122 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_amt.c
+ *
+ * @brief Entry point of the ECRNX driver
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include "ecrnx_main.h"
+#include "eswin_utils.h"
+#include "ecrnx_calibration_data.h"
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#define AMT_GAIN_DELTA_MSG_FLAG ("gaindelta=")
+#define AMT_GAIN_DELTA_MSG_BUFF_LEN 60 //strlen(AMT_GAIN_DELTA_MSG_FLAG) + sizeof(gain_delta)
+static char gain_delta_msg_buf[AMT_GAIN_DELTA_MSG_BUFF_LEN];
+extern bool set_gain;
+
+struct ecrnx_iwpriv_amt_vif amt_vif;
+#if defined(CONFIG_WIRELESS_EXT) && defined(CONFIG_WEXT_PRIV)
+extern const struct iw_handler_def  ecrnx_wext_private_handler;
+#endif
+
+static int eswin_netdev_amt_open(struct net_device *ndev)
+{
+       ECRNX_DBG("amt netdev open\n");
+
+       return 0;
+}
+
+static int eswin_netdev_amt_stop(struct net_device *ndev)
+{
+       ECRNX_DBG("amt netdev stop\n");
+
+       return 0;
+}
+
+static netdev_tx_t eswin_netdev_amt_start_xmit(struct sk_buff *skb,
+                                           struct net_device *ndev)
+{
+       if (skb)
+               dev_kfree_skb_any(skb);
+
+       return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops eswin_netdev_ops_amt = {
+       .ndo_open = eswin_netdev_amt_open,
+       .ndo_stop = eswin_netdev_amt_stop,
+       .ndo_start_xmit = eswin_netdev_amt_start_xmit
+};
+
+static int ecrnx_amt_gain_delta_msg_send(void)
+{
+    if (set_gain != true)
+        return -1;
+
+    memcpy(gain_delta_msg_buf, AMT_GAIN_DELTA_MSG_FLAG, strlen(AMT_GAIN_DELTA_MSG_FLAG));
+    memcpy((gain_delta_msg_buf + strlen(AMT_GAIN_DELTA_MSG_FLAG)), gain_delta, GAIN_DELTA_CFG_BUF_SIZE);
+
+    host_send(gain_delta_msg_buf, sizeof(gain_delta_msg_buf), TX_FLAG_AMT_IWPRIV_IE);
+
+    return 0;
+}
+
+int ecrnx_amt_init(void)
+{
+       struct net_device *ndev;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
+    ndev = alloc_netdev(0, "amt0", ether_setup);
+#else
+       ndev = alloc_netdev(0, "amt0", NET_NAME_UNKNOWN, ether_setup);
+#endif
+       if (!ndev) {
+               ECRNX_DBG("alloc netdev fail !!");
+               return -1;
+       }
+
+       amt_vif.ndev = ndev;
+       amt_vif.rxlen = 0;
+       init_waitqueue_head(&amt_vif.rxdataq);
+
+       ndev->netdev_ops = &eswin_netdev_ops_amt;
+#if defined(CONFIG_WIRELESS_EXT) && defined(CONFIG_WEXT_PRIV)
+       ndev->wireless_handlers = &ecrnx_wext_private_handler;
+#endif
+       /* clear the mac address */
+       memset(ndev->dev_addr, 0, ETH_ALEN);
+
+       if (register_netdev(ndev) != 0)
+        goto err_dev;
+
+    ecrnx_amt_gain_delta_msg_send();
+
+       ECRNX_PRINT(" %s:register the amt net device success\n", __func__);
+       return 0;
+
+err_dev:
+       ECRNX_ERR(" %s couldn't register the amt net device!!",  __func__);
+       free_netdev(ndev);
+       amt_vif.ndev = NULL;
+    return -1;
+}
+
+void ecrnx_amt_deinit(void)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+       if (amt_vif.ndev)
+               unregister_netdev(amt_vif.ndev);
+
+       amt_vif.ndev = NULL;
+}
+#endif
+
+
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_amt.h b/drivers/net/wireless/eswin/fullmac/ecrnx_amt.h
new file mode 100644 (file)
index 0000000..dc06e78
--- /dev/null
@@ -0,0 +1,21 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_amt.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_AMT_H_
+#define _ECRNX_AMT_H_
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+int ecrnx_amt_init(void);
+void ecrnx_amt_deinit(void);
+
+extern struct ecrnx_iwpriv_amt_vif amt_vif;
+#endif
+
+#endif /* _ECRNX_AMT_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.c b/drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.c
new file mode 100644 (file)
index 0000000..1c845ee
--- /dev/null
@@ -0,0 +1,27 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_calibration_data.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+#include "ecrnx_calibration_data.h"
+
+/**
+ * TYPE DEFINITIONS
+ ****************************************************************************************
+ */
+wifi_cal_data_t cal_result;
+s8 gain_delta[GAIN_DELTA_CFG_BUF_SIZE] = { //base on 11b 11M, ht20/40 mcs7
+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,             /* 11b  */
+             1,  1,  1,  1,  1,  1,  1,  1,  0,  0,             /* 11g  */
+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,             /* 11n_20M mcs 0 - 7 */
+             0,  0,  0,  0,  0,  0,  0,  0,  0,  0,             /* 11n_40M mcs 0 - 7 */
+            -2, -2, -2, -2, -2, -2, -2, -2, -2, -2};            /* 11ax mcs 0 - 9 */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.h b/drivers/net/wireless/eswin/fullmac/ecrnx_calibration_data.h
new file mode 100644 (file)
index 0000000..ddf8301
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_calibration_data.h
+ *
+ * @brief Calibration Data function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_CALIBRATION_H_
+#define _ECRNX_CALIBRATION_H_
+#include "lmac_types.h"
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+/**
+ * DEFINES
+ ****************************************************************************************
+ */
+#define CAL_MAC_ADDR_LEN 6
+#define CAL_FORMAT_CLASS 3
+#define CAL_MCS_CNT      10
+#define CAL_CHAN_CNT     3
+#define IS_LOW_CHAN(ch)  ((ch) >= 1 && (ch) <= 4)
+#define IS_MID_CHAN(ch)  ((ch) >= 5 && (ch) <= 8)
+#define IS_HIG_CHAN(ch)  ((ch) >= 9 && (ch) <= 14)
+#define GAIN_DELTA_CFG_BUF_SIZE (CAL_FORMAT_MAX * CAL_MCS_CNT)
+
+
+typedef enum {
+    CHAN_LEVEL_LOW,
+    CHAN_LEVEL_MID,
+    CHAN_LEVEL_HIGH,
+    CHAN_LEVEL_MAX,
+} chan_level_e;
+
+typedef enum {
+    CAL_FORMAT_11B,
+    CAL_FORMAT_11G,
+    CAL_FORMAT_11N,
+    CAL_FORMAT_11N_40M,
+    CAL_FORMAT_11AX,
+    CAL_FORMAT_MAX
+} wifi_cal_format_e;
+
+typedef struct {
+    uint8_t gain[CHAN_LEVEL_MAX][CAL_FORMAT_MAX][CAL_MCS_CNT];
+}tx_gain_cal_t;
+
+typedef struct {
+    uint8_t fine;
+    uint8_t corase;
+    uint8_t swl;
+}cfo_cal_t;
+
+typedef struct {
+    tx_gain_cal_t tx_gain;
+    cfo_cal_t     cfo_cal;
+    uint8_t       mac_addr[CAL_MAC_ADDR_LEN];
+    uint32_t      lol;
+}wifi_cal_data_t;
+
+/**
+ * TYPE DEFINITIONS
+ ****************************************************************************************
+ */
+
+extern s8 gain_delta[];
+extern wifi_cal_data_t cal_result;
+/**
+ * FUNCTION DECLARATIONS
+ ****************************************************************************************
+ */
+
+#endif /* _ECRNX_CALIBRATION_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.c b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.c
new file mode 100644 (file)
index 0000000..d5ec98c
--- /dev/null
@@ -0,0 +1,1171 @@
+#include "ecrnx_debugfs_custom.h"
+#include "ecrnx_debugfs_func.h"
+#include "slave_log_buf.h"
+#include <linux/file.h>
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+#define DEFINE_SHOW_ATTRIBUTE(__name)                    \
+static int __name ## _open(struct inode *inode, struct file *file)    \
+{                                    \
+    return single_open(file, __name ## _show, inode->i_private);    \
+}                                    \
+                                    \
+static const struct file_operations __name ## _fops = {            \
+    .owner        = THIS_MODULE,                  \
+    .open        = __name ## _open,               \
+    .read        = seq_read,                      \
+    .llseek        = seq_lseek,                   \
+    .release    = single_release,                 \
+}
+
+#define DEFINE_SHOW_ATTRIBUTE_EX(__name)                    \
+static int __name ## _open(struct inode *inode, struct file *file)    \
+{                                    \
+    return single_open(file, __name ## _show, inode->i_private);    \
+}                                    \
+                                    \
+static const struct file_operations __name ## _fops = {            \
+    .owner        = THIS_MODULE,                  \
+    .open        = __name ## _open,               \
+    .read        = seq_read,                      \
+    .write        = __name ## _write,             \
+    .llseek        = seq_lseek,                   \
+    .release    = single_release,                 \
+}
+
+static long custom_sys_write(unsigned int fd, const char __user *buf, size_t count)
+{
+   long ret = -EBADF;
+   struct file *file = NULL;
+   mm_segment_t status;
+   loff_t offset;
+
+   if (!buf) {
+       printk("Write buffer was empty.\n");
+       return ret;
+   }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+   file = fget(fd);
+   if (file) {
+       offset = file->f_pos;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+        ret = kernel_write(file, buf, count, (loff_t *)&offset);
+#else
+        ret = kernel_write(file, buf, count, 0);
+#endif
+       file->f_pos = offset;
+       fput(file);
+   }
+#else
+   print_hex_dump(KERN_DEBUG, DBG_PREFIX, DUMP_PREFIX_NONE, 16, 1, buf, count, false);
+#endif
+   return ret;
+}
+
+
+void seq_reg_display_u32(struct seq_file *seq, u32 addr, u32 *reg, u32 len)
+{
+    u32 i;
+
+    for (i=0; i<len; i++)
+    {
+        if (i%4 == 0)
+            seq_printf(seq, "0x%08X  ", (addr+ 4*i));
+
+        seq_printf(seq, " 0x%08X", reg[i]);
+
+        if (i%4 == 3)
+            seq_printf(seq, "\n");
+    }
+
+    if (len % 4)
+        seq_printf(seq, "\n");
+}
+
+void seq_reg_display_u8(struct seq_file *seq, u32 addr, u8 *reg, u32 len)
+{
+    u32 i;
+
+    for (i=0; i<len; i++)
+    {
+        if (i%16 == 0)
+            seq_printf(seq, "0x%04X  ", (addr+i)&0xFFFF);
+
+        seq_printf(seq, " %02X", reg[i]);
+
+        if (i%16 == 7)
+            seq_printf(seq, "  ");
+
+        if (i%16 == 15)
+            seq_printf(seq, "\n");
+    }
+
+    if (len % 16)
+        seq_printf(seq, "\n");
+}
+void read_reg_display_u32(u32 addr, u32 *reg, u32 len)
+{
+    u32 i;
+    u32 size = len*11 + (len + 3)/4*16 + len/4;
+    u32 point = 0;
+    char * buf = NULL;
+
+    buf = kmalloc(size, GFP_ATOMIC);
+    if(buf == NULL)
+    {
+        return;
+    }
+    for (i=0; i<len; i++)
+    {
+        if (i%4 == 0)
+        {
+            sprintf(buf+point,"addr:0x%08X", (addr+ 4*i));
+            point += 15;
+            buf[point] = '\n';
+            point += 1;
+
+        }
+
+        sprintf(buf+point,"0x%08X", reg[i]);
+        point += 10;
+        buf[point] = '\n';
+        point += 1;
+
+
+        if (i%4 == 3)
+        {
+            buf[point] = '\n';
+            point += 1;
+        }
+    }
+    custom_sys_write(0,buf,size);
+    if(buf != NULL)
+    {
+        kfree(buf);
+        buf = NULL;
+    }
+
+}
+/*
+void reg_display_u32(u32 addr, u32 *reg, u32 len)
+{
+    u32 i;
+
+    for (i=0; i<len; i++)
+    {
+        if (i%4 == 0)
+            ECRNX_PRINT("0x%08X", (addr+ 4*i));
+
+        ECRNX_PRINT("0x%08X", reg[i]);
+
+        if (i%4 == 3)
+            printk("\n");
+    }
+
+    if (len % 4)
+        printk("\n");
+}
+*/
+void reg_display_u8(u32 addr, u8 *reg, u32 len)
+{
+    u32 i;
+
+    for (i=0; i<len; i++)
+    {
+        if (i%16 == 0)
+            ECRNX_PRINT("0x%04X  ", (addr+i)&0xFFFF);
+
+        ECRNX_PRINT(" %02X", reg[i]);
+
+        if (i%16 == 7)
+            printk("  ");
+
+        if (i%16 == 15)
+            printk("\n");
+    }
+
+    if (len % 16)
+        printk("\n");
+}
+
+static int drv_cfg_show(struct seq_file *seq, void *v)
+{
+//#define DRV_CFG_DETAILS
+
+    u8 host_ver[32];
+
+    ecrnx_host_ver_get(host_ver);
+
+    seq_printf(seq, "Kernel Version : %d.%d.%d\n", LINUX_VERSION_CODE >> 16, (LINUX_VERSION_CODE >> 8)&0xFF, LINUX_VERSION_CODE&0xFF);
+    seq_printf(seq, "Driver Version : %s\n", host_ver);
+    seq_printf(seq, "------------------------------------------------\n");
+
+#ifdef CONFIG_ECRNX_ESWIN
+    seq_printf(seq, "CONFIG_ECRNX_ESWIN=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+        seq_printf(seq, "CONFIG_ECRNX_ESWIN=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    seq_printf(seq, "CONFIG_ECRNX_FULLMAC=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+        seq_printf(seq, "CONFIG_ECRNX_FULLMAC=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+    seq_printf(seq, "CONFIG_ECRNX_SOFTMAC=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+        seq_printf(seq, "CONFIG_ECRNX_SOFTMAC=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_ESWIN_USB
+    seq_printf(seq, "CONFIG_ECRNX_ESWIN_USB=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+        seq_printf(seq, "CONFIG_ECRNX_ESWIN_USB=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+    seq_printf(seq, "CONFIG_ECRNX_ESWIN_SDIO=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+        seq_printf(seq, "CONFIG_ECRNX_ESWIN_SDIO=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_HE
+    seq_printf(seq, "CONFIG_ECRNX_HE=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+        seq_printf(seq, "CONFIG_ECRNX_HE=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_P2P
+    seq_printf(seq, "CONFIG_ECRNX_P2P=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+        seq_printf(seq, "CONFIG_ECRNX_P2P=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_FHOST
+        seq_printf(seq, "CONFIG_ECRNX_FHOST=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+            seq_printf(seq, "CONFIG_ECRNX_FHOST=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_SDM
+        seq_printf(seq, "CONFIG_ECRNX_SDM=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+            seq_printf(seq, "CONFIG_ECRNX_SDM=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_TL4
+        seq_printf(seq, "CONFIG_ECRNX_TL4=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+            seq_printf(seq, "CONFIG_ECRNX_TL4=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+        seq_printf(seq, "CONFIG_ECRNX_MUMIMO_TX=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+            seq_printf(seq, "CONFIG_ECRNX_MUMIMO_TX=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_RADAR
+        seq_printf(seq, "CONFIG_ECRNX_RADAR=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+            seq_printf(seq, "CONFIG_ECRNX_RADAR=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_BCMC
+        seq_printf(seq, "CONFIG_ECRNX_BCMC=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+            seq_printf(seq, "CONFIG_ECRNX_BCMC=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_MON_DATA
+        seq_printf(seq, "CONFIG_ECRNX_MON_DATA=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+            seq_printf(seq, "CONFIG_ECRNX_MON_DATA=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_SW_PROFILING
+        seq_printf(seq, "CONFIG_ECRNX_SW_PROFILING=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+            seq_printf(seq, "CONFIG_ECRNX_SW_PROFILING=0\n");
+    #endif
+#endif
+
+
+#ifdef CONFIG_ECRNX_DBG
+        seq_printf(seq, "CONFIG_ECRNX_DBG=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+            seq_printf(seq, "CONFIG_ECRNX_DBG=0\n");
+    #endif
+#endif
+
+
+#ifdef CFG_WOW_SUPPORT
+            seq_printf(seq, "CFG_WOW_SUPPORT=1\n");
+#else
+    #ifdef DRV_CFG_DETAILS
+                seq_printf(seq, "CFG_WOW_SUPPORT=0\n");
+    #endif
+#endif
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(drv_cfg);
+
+
+static int log_level_show(struct seq_file *seq, void *v)
+{
+    LOG_CTL_ST log;
+
+    if (0 == ecrnx_log_level_get(&log))
+        seq_printf(seq, "log_level:%d  dir:%d\n", log.level, log.dir);
+    else
+        seq_printf(seq, "\n");
+
+    return 0;
+}
+
+static ssize_t log_level_write(struct file *filp, const char __user *buffer,
+                            size_t count, loff_t *ppos)
+{
+    u8 kbuf[128]={0};
+    u32 level,dir;
+
+    if (count > 6)  // 255:0\n
+        return -1;
+
+    if (0 != copy_from_user(kbuf, buffer, count))
+        return -1;
+    sscanf(kbuf, "%d:%d\n", &level, &dir);
+
+    if (dir > 1)
+        return -1;
+
+    ecrnx_fw_log_level_set(level, dir);
+
+    return count;
+}
+DEFINE_SHOW_ATTRIBUTE_EX(log_level);
+
+static ssize_t mac_log_read(struct file *file , char __user *user_buf,
+                                   size_t count, loff_t *ppos)
+{
+    struct ring_buffer *buf_handle;
+    int len = 0;
+    char buf[512] = {0};
+    if (false == ecrnx_log_host_enable())
+        return 0;
+#ifdef CONFIG_ECRNX_ESWIN_USB
+    buf_handle = usb_dbg_buf_get();
+    ring_buffer_scrolling_display(buf_handle,true);
+    while(buf_handle->show)
+    {
+        len = ring_buffer_len(buf_handle);
+        len = min(len, 512);
+        //ECRNX_PRINT("mac_log_read len:%d %d %d", len,buf_handle->write_point, buf_handle->read_point);
+        if(len > 0)
+        {
+            ring_buffer_get(buf_handle,buf,len);
+            custom_sys_write(0,buf,len);
+            msleep(35);
+        }
+        else
+        {
+            msleep(500);
+        }
+        cond_resched();
+    }
+#endif
+
+    return count;
+}
+
+static ssize_t mac_log_write(struct file *filp, const char __user *buffer,
+                            size_t count, loff_t *ppos)
+{
+    u8 kbuf[128]={0};
+    u32 show;
+    struct ring_buffer *buf_handle;
+
+    if (count > 2)  // 255:0\n
+        return -1;
+
+    if (0 != copy_from_user(kbuf, buffer, count))
+        return -1;
+    sscanf(kbuf, "%d:%d\n", &show);
+#ifdef CONFIG_ECRNX_ESWIN_USB
+    if (show == 0)
+    {
+        buf_handle = usb_dbg_buf_get();
+        ring_buffer_scrolling_display(buf_handle, false);
+    }
+#endif
+
+    return count;
+}
+
+static const struct file_operations new_mac_log_fops = { \
+    .read   = mac_log_read,                         \
+    .write  = mac_log_write,                         \
+};
+
+static int ver_info_show(struct seq_file *seq, void *v)
+{
+    u8 host_ver[32];
+    u8 build_time[32];
+
+    ecrnx_host_ver_get(host_ver);
+    ecrnx_build_time_get(build_time);
+
+    seq_printf(seq, "%s\n", host_ver);
+    seq_printf(seq, "build time: %s\n", build_time);
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ver_info);
+
+extern bin_head_data head;
+static int fw_info_show(struct seq_file *seq, void *v)
+{
+    struct tm timenow = {0};
+
+    localtime(&timenow, head.UTC_time);
+    if(head.fw_Info != NULL)
+    {
+        seq_printf(seq, "sdk_verson:%s\n", head.fw_Info);
+    }
+    seq_printf(seq, "fw_crc:0x%8x\n", head.crc32);
+    seq_printf(seq, "fw_build_time: %04d-%02d-%02d %02d:%02d:%02d\n",(int)timenow.tm_year,timenow.tm_mon,timenow.tm_mday,timenow.tm_hour,timenow.tm_min,timenow.tm_sec);
+    return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(fw_info);
+
+struct dentry *ecr6600u_dir = NULL;
+struct dentry *drv_cfg, *log_level, *mac_log, *ver_info, *fw_info;
+
+int ecrnx_debugfs_info_init(void      *private_data)
+{
+    ecr6600u_dir = debugfs_create_dir("ecr6600u", NULL);
+    if (!ecr6600u_dir)
+        goto Fail;
+
+    drv_cfg = debugfs_create_file("drv_cfg", 0444, ecr6600u_dir, private_data, &drv_cfg_fops);
+    if (!drv_cfg)
+        goto Fail;
+
+    log_level = debugfs_create_file("log_level", 0666, ecr6600u_dir, private_data, &log_level_fops);
+    if (!log_level)
+        goto Fail;
+
+    mac_log = debugfs_create_file("maclog", 0444, ecr6600u_dir, private_data, &new_mac_log_fops);
+    if (!mac_log)
+        goto Fail;
+
+    ver_info = debugfs_create_file("ver_info", 0444, ecr6600u_dir, private_data, &ver_info_fops);
+    if (!ver_info)
+        goto Fail;
+
+    fw_info = debugfs_create_file("fw_info", 0444, ecr6600u_dir, private_data, &fw_info_fops);
+    if (!fw_info)
+        goto Fail;
+    return 0;
+
+Fail:
+    return -ENOENT;
+}
+
+
+static int rf_info_show(struct seq_file *seq, void *v)
+{
+    RF_INFO_ST cur, oper;
+
+    if (0 == ecrnx_rf_info_get(seq, IF_STA, &cur, &oper))
+    {
+        seq_printf(seq, "cur_ch=%d, cur_bw=%d, cur_ch_offset=%d\n", cur.ch, cur.bw, cur.ch_offset);
+        // seq_printf(seq, "oper_ch=%d, oper_bw=%d, oper_ch_offset=%d\n", oper.ch, oper.bw, oper.ch_offset);
+    }
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rf_info);
+
+static int mac_reg_dump_show(struct seq_file *seq, void *v)
+{
+    ecrnx_mac_reg_dump(seq);
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(mac_reg_dump);
+
+static int rf_reg_dump_show(struct seq_file *seq, void *v)
+{
+    ecrnx_rf_reg_dump(seq);
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rf_reg_dump);
+
+static int bb_reg_dump_show(struct seq_file *seq, void *v)
+{
+    ecrnx_bb_reg_dump(seq);
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(bb_reg_dump);
+
+static int country_code_show(struct seq_file *seq, void *v)
+{
+    char country_code[3];
+    if (0 == ecrnx_country_code_get(seq, country_code))
+    {
+        seq_printf(seq, "%s\n", country_code);
+    }
+    else
+    {
+        seq_printf(seq, "UNSPECIFIED\n");
+    }
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(country_code);
+
+static int mac_addr_show(struct seq_file *seq, void *v)
+{
+    u8 mac_addr_info[128];
+
+    if (0 == ecrnx_mac_addr_get_ex(seq, mac_addr_info))
+    {
+        seq_printf(seq, "%s", mac_addr_info);
+    }
+    else
+    {
+        seq_printf(seq, "--\n");
+    }
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(mac_addr);
+
+static int efuse_map_show(struct seq_file *seq, void *v)
+{
+    ecrnx_efuse_map_dump(seq);
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(efuse_map);
+
+static int read_reg_show(struct seq_file *seq, void *v)
+{
+    return 0;
+}
+static ssize_t read_reg_write(struct file *filp, const char __user *buffer,
+                            size_t count, loff_t *ppos)
+{
+    u8 kbuf[128]={0};
+    u32 addr, len;
+    int ret_code;
+    char * read_reg_buf = NULL;
+
+    if (count > sizeof(kbuf))
+        return -1;
+
+    if (0 != copy_from_user(kbuf, buffer, count))
+        return -1;
+
+    ret_code = sscanf(kbuf, "0x%x %d\n", &addr, &len);
+    if (ret_code != 2)
+    {
+        ECRNX_ERR("Parameter error.\n");
+        return -1;
+    }
+
+    if ( addr%4 )
+    {
+        ECRNX_ERR("Address is invalid.\n");
+        return -1;
+    }
+
+    if (0 == ecrnx_slave_reg_read(addr, reg_buf, len * sizeof(reg_buf[0])))
+        read_reg_display_u32(addr, reg_buf, len);
+    return count;
+}
+DEFINE_SHOW_ATTRIBUTE_EX(read_reg);
+static int write_reg_show(struct seq_file *seq, void *v)
+{
+    return 0;
+}
+
+static ssize_t write_reg_write(struct file *filp, const char __user *buffer,
+                            size_t count, loff_t *ppos)
+{
+    u8 kbuf[128]={0};
+    u32 addr, data;
+    int ret_code,ret = count;
+    char* write_reg_result = NULL;
+
+    if (count > sizeof(kbuf))
+        ret = -1;
+
+    if (0 != copy_from_user(kbuf, buffer, count))
+        ret = -1;
+
+    ret_code = sscanf(kbuf, "0x%x 0x%x\n", &addr, &data);
+    if (ret_code != 2)
+    {
+        write_reg_result = "Parameter error.\n";
+        ret = -1;
+    }
+
+    if ( addr%4 )
+    {
+        write_reg_result = "Address is invalid.\n";
+        ret = -1;
+    }
+
+    if (0 != ecrnx_slave_reg_write(addr, data, 1))
+    {
+        write_reg_result = "Write error.\n";
+        ret = -1;
+    }
+    else
+        write_reg_result = "Write success.\n";
+    if(write_reg_result != NULL)
+    {
+        custom_sys_write(0,write_reg_result,strlen(write_reg_result));
+    }
+    return ret;
+}
+DEFINE_SHOW_ATTRIBUTE_EX(write_reg);
+
+static int rf_tx_rx_info_show(struct seq_file *seq, void *v)
+{
+//    u8 *resp = NULL;
+//    u8 cli[]="rf_get_rssi 0 1 2";
+
+//    ecrnx_slave_cli_send(cli, resp);
+
+    return 0;
+}
+
+static ssize_t rf_tx_rx_info_write(struct file *filp, const char __user *buffer,
+                            size_t count, loff_t *ppos)
+{
+    u8 kbuf[128]={0};
+
+    if (count > sizeof(kbuf))
+        return -1;
+
+    if (0 != copy_from_user(kbuf, buffer, count))
+        return -1;
+
+    cli_cmd_parse(kbuf);
+
+    return count;
+}
+
+DEFINE_SHOW_ATTRIBUTE_EX(rf_tx_rx_info);
+
+struct dentry *hw_dir = NULL;
+struct dentry *hw_rf_info, *hw_mac_reg_dump, *hw_rf_reg_dump, *hw_bb_reg_dump, *hw_country_code, *hw_mac_addr;
+struct dentry *hw_efuse_map, *hw_read_reg, *hw_write_reg, *hw_rf_tx_rx_info;
+
+int ecrnx_debugfs_hw_init(void *private_data)
+{
+    if (!ecr6600u_dir)
+        return -ENOENT;
+
+    hw_dir = debugfs_create_dir("hw", ecr6600u_dir);
+    if (!hw_dir)
+        return -ENOENT;
+
+    hw_rf_info = debugfs_create_file("rf_info", 0444, hw_dir, private_data, &rf_info_fops);
+    if (!hw_rf_info)
+        goto Fail;
+
+    hw_mac_reg_dump = debugfs_create_file("mac_reg_dump", 0444, hw_dir, private_data, &mac_reg_dump_fops);
+    if (!hw_mac_reg_dump)
+        goto Fail;
+
+    hw_rf_reg_dump = debugfs_create_file("rf_reg_dump", 0444, hw_dir, private_data, &rf_reg_dump_fops);
+    if (!hw_rf_reg_dump)
+        goto Fail;
+
+    hw_bb_reg_dump = debugfs_create_file("bb_reg_dump", 0444, hw_dir, private_data, &bb_reg_dump_fops);
+    if (!hw_bb_reg_dump)
+        goto Fail;
+
+    hw_country_code = debugfs_create_file("country_code", 0444, hw_dir, private_data, &country_code_fops);
+    if (!hw_country_code)
+        goto Fail;
+
+    hw_mac_addr = debugfs_create_file("mac_addr", 0444, hw_dir, private_data, &mac_addr_fops);
+    if (!hw_mac_addr)
+        goto Fail;
+
+    hw_efuse_map = debugfs_create_file("efuse_map", 0444, hw_dir, private_data, &efuse_map_fops);
+    if (!hw_efuse_map)
+        goto Fail;
+
+    hw_read_reg = debugfs_create_file("read_reg", 0222, hw_dir, private_data, &read_reg_fops);
+    if (!hw_read_reg)
+        goto Fail;
+
+    hw_write_reg = debugfs_create_file("write_reg", 0222, hw_dir, private_data, &write_reg_fops);
+    if (!hw_write_reg)
+        goto Fail;
+
+    hw_rf_tx_rx_info = debugfs_create_file("rf_tx_rx_info", 0666, hw_dir, private_data, &rf_tx_rx_info_fops);
+    if (!hw_rf_tx_rx_info)
+        goto Fail;
+
+    return 0;
+
+Fail:
+    debugfs_remove_recursive(hw_dir);
+    hw_dir = NULL;
+    return -ENOENT;
+}
+
+#ifndef MAC_FMT
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#endif
+#ifndef MAC_ARG
+#define MAC_ARG(x) ((u8 *)(x))[0], ((u8 *)(x))[1], ((u8 *)(x))[2], ((u8 *)(x))[3], ((u8 *)(x))[4], ((u8 *)(x))[5]
+#endif
+
+static int survey_info_show(struct seq_file *seq, void *v)
+{
+  int index = 0;
+  struct ecrnx_hw *ecrnx_hw;
+  struct ecrnx_debugfs_survey_info_tbl *entry;
+
+  if (seq->private != NULL)
+      ecrnx_hw = seq->private;
+  else
+      return -1;
+  
+//seq_printf(seq, "index  bssid  ch  RSSI  SdBm  Noise    age          flag          ssid \n");
+  seq_printf(seq, "%5s  %-17s  %3s  %-3s  %-4s  %-4s  %5s  %32s  %32s\n", "index", "bssid", "ch", "RSSI", "SdBm", "Noise", "age", "flag", "ssid");
+  
+  list_for_each_entry(entry, &ecrnx_hw->debugfs_survey_info_tbl_ptr, list) {
+      seq_printf(seq, "%5d  "MAC_FMT"  %3d  %3d  %4d  %4d  %5d    %32s    %32s\n",
+          ++index,
+          MAC_ARG(entry->bssid),
+          entry->ch,
+          entry->rssi,
+          entry->sdbm,
+          entry->noise,
+          entry->age,
+          entry->flag,
+          entry->ssid);
+  }
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(survey_info);
+
+static int sta_info_show(struct seq_file *seq, void *v)
+{
+    s8 noise_dbm;
+    struct cfg80211_chan_def chandef;
+    u32 bw_array[]={20, 20, 40, 80, 80, 160, 5, 10};
+
+    if (0 == ecrnx_signal_level_get(seq, IF_STA, &noise_dbm))
+        seq_printf(seq, "signal level : %d dBm\n", noise_dbm);
+    else
+        seq_printf(seq, "signal level : \n");
+
+    if (0 == ecrnx_channel_get(seq, IF_STA, &chandef))
+    {
+        seq_printf(seq, "frequency    : %d MHz\n", chandef.center_freq1);
+        seq_printf(seq, "bandwidth    : %d MHz\n", bw_array[chandef.width]);
+    }
+    else
+    {
+        seq_printf(seq, "frequency    : \n");
+        seq_printf(seq, "bandwidth    : \n");
+    }
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(sta_info);
+
+static int ht_info_show(struct seq_file *seq, void *v)
+{
+    struct cfg80211_chan_def chandef;
+    u32 bw_array[]={20, 20, 40, 80, 80, 160, 5, 10};
+
+    if (0 == ecrnx_channel_get(seq, IF_STA, &chandef))
+        seq_printf(seq, "bandwidth    : %d MHz\n", bw_array[chandef.width]);
+    else
+        seq_printf(seq, "bandwidth    : \n");
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ht_info);
+
+
+struct dentry *wlan0_dir = NULL;
+struct dentry *wlan0_survey_info, *wlan0_sta_info, *wlan0_ht_info;;
+
+int ecrnx_debugfs_wlan0_init(void *private_data)
+{
+    if (!ecr6600u_dir)
+        return -ENOENT;
+
+    wlan0_dir = debugfs_create_dir("wlan0", ecr6600u_dir);
+    if (!wlan0_dir)
+        return -ENOENT;
+
+    wlan0_survey_info = debugfs_create_file("survey_info", 0444, wlan0_dir, private_data, &survey_info_fops);
+    if (!wlan0_survey_info)
+        goto Fail;
+
+    wlan0_sta_info = debugfs_create_file("sta_info", 0444, wlan0_dir, private_data, &sta_info_fops);
+    if (!wlan0_sta_info)
+        goto Fail;
+
+    wlan0_ht_info = debugfs_create_file("ht_info", 0444, wlan0_dir, private_data, &ht_info_fops);
+    if (!wlan0_ht_info)
+        goto Fail;
+
+    return 0;
+
+Fail:
+    debugfs_remove_recursive(wlan0_dir);
+    wlan0_dir = NULL;
+    return -ENOENT;
+}
+
+static int ap_info_show(struct seq_file *seq, void *v)
+{
+    struct cfg80211_chan_def chandef;
+    u32 bw_array[]={20, 20, 40, 80, 80, 160, 5, 10};
+    u8 ssid[IEEE80211_MAX_SSID_LEN];
+    u8 sta_mac[NX_REMOTE_STA_MAX][ETH_ALEN+1];
+    u32 i;
+
+    seq_printf(seq, "AP Vendor ESWIN\n");
+
+    seq_printf(seq, "ap info\n");
+    if (0 == ecrnx_ssid_get(seq, IF_AP, ssid))
+        seq_printf(seq, "ssid         : %s\n", ssid);
+    else
+        seq_printf(seq, "ssid         : \n");
+
+    if (0 == ecrnx_channel_get(seq, IF_AP, &chandef))
+    {
+        seq_printf(seq, "cur_channel=%d, cur_bwmode=%d(%dMHz), cur_ch_offset=0\n",
+            (chandef.center_freq1 - 2412)/5 + 1, chandef.width, bw_array[chandef.width]);
+    }
+    else
+    {
+        seq_printf(seq, "cur_channel=, cur_bwmode=(MHz), cur_ch_offset=\n");
+    }
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
+    if ((chandef.width <= NL80211_CHAN_WIDTH_10) && (chandef.width >= NL80211_CHAN_WIDTH_20_NOHT))
+    {
+        if (NL80211_CHAN_WIDTH_20_NOHT == chandef.width)
+        {
+            seq_printf(seq, "ht_en=0\n");
+        }
+        else
+        {
+            seq_printf(seq, "ht_en=1\n");
+        }
+    }
+    else
+    {
+        seq_printf(seq, "ht_en=0\n");
+    }
+#endif
+
+    seq_printf(seq, "wireless_mode=0xb(B/G/N), rtsen=0, cts2slef=0\n");
+    seq_printf(seq, "state=0x10, aid=0, macid=32, raid=0\n");
+    seq_printf(seq, "qos_en=1, init_rate=0\n");
+    seq_printf(seq, "bwmode=0, ch_offset=0, sgi_20m=1,sgi_40m=1\n");
+    seq_printf(seq, "ampdu_enable = 1\n");
+    seq_printf(seq, "agg_enable_bitmap=0, candidate_tid_bitmap=0\n");
+    seq_printf(seq, "ldpc_cap=0x0, stbc_cap=0x0, beamform_cap=0x0\n");
+
+
+    seq_printf(seq, "\n");
+    seq_printf(seq, "station info\n");
+    memset(sta_mac, 0x00, sizeof(sta_mac));
+    if (0 == ecrnx_sta_mac_get(seq, IF_AP, sta_mac))
+    {
+        for (i=0; i<NX_REMOTE_STA_MAX; i++)
+        {
+            if (sta_mac[i][0])
+                seq_printf(seq, "sta's macaddr:%02X:%02X:%02X:%02X:%02X:%02X\n", sta_mac[i][1], sta_mac[i][2],
+                                sta_mac[i][3], sta_mac[i][4], sta_mac[i][5], sta_mac[i][6]);
+        }
+    }
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ap_info);
+
+static int sta_info_in_ap_show(struct seq_file *seq, void *v)
+{
+       struct ecrnx_debugfs_sta *sta;
+       
+       if (seq->private == NULL)
+               ECRNX_ERR("error sta_info_in_ap_show\n");
+
+       sta = seq->private;
+       
+       seq_printf(seq, "cur_channel=%d, cur_bwmode=%d\n", sta->ch_idx, sta->width);
+       //seq_printf(m, "wireless_mode=0x%x(%s), rtsen=%d, cts2slef=%d\n", psta->wireless_mode, wl_mode, psta->rtsen, psta->cts2self);
+       seq_printf(seq, "aid=%d\n", sta->aid);
+
+       seq_printf(seq, "qos_en=%d, ht_en=%d\n", sta->qos, sta->ht);
+       seq_printf(seq, "sgi_20m=%d,sgi_40m=%d\n", sta->sgi_20m, sta->sgi_40m);
+       seq_printf(seq, "ampdu_enable = %d\n", sta->ampdu_enable);
+       seq_printf(seq, "agg_enable_bitmap=%x, candidate_tid_bitmap=%x\n", sta->agg_enable_bitmap, sta->candidate_tid_bitmap);
+       seq_printf(seq, "ldpc_cap=0x%x, stbc_cap=0x%x, beamform_cap=0x%x\n", sta->ldpc_cap, sta->stbc_cap, sta->beamform_cap);
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(sta_info_in_ap);
+
+struct dentry *ap0_dir = NULL;
+struct dentry *ap0_ap_info;
+struct dentry *stas[NX_REMOTE_STA_MAX];
+
+int ecrnx_debugfs_ap0_init(void *private_data)
+{
+    if (!ecr6600u_dir)
+        return -ENOENT;
+
+    ap0_dir = debugfs_create_dir("ap0", ecr6600u_dir);
+    if (!ap0_dir)
+        return -ENOENT;
+
+    ap0_ap_info = debugfs_create_file("ap_info", 0444, ap0_dir, private_data, &ap_info_fops);
+    if (!ap0_ap_info)
+        goto Fail;
+
+    return 0;
+
+Fail:
+    debugfs_remove_recursive(ap0_dir);
+    ap0_dir = NULL;
+    return -ENOENT;
+}
+
+void ecrnx_debugfs_sta_in_ap_init(void *private_data)
+{
+       int index;
+       char file_name[19];
+       struct ecrnx_debugfs_sta *sta;
+
+       sta = private_data;
+       index = sta->sta_idx;
+
+       sprintf(file_name, "%02x-%02x-%02x-%02x-%02x-%02x", MAC_ARG(sta->mac_addr));
+       stas[index] = debugfs_create_file(file_name, 0444, ap0_dir, private_data, &sta_info_in_ap_fops);
+       ECRNX_PRINT("%s:file_name: %s, sta idx:%d,stas:0x%p \n", __func__, file_name, sta->sta_idx, stas[index]);
+}
+
+void ecrnx_debugfs_sta_in_ap_del(u8 sta_idx)
+{
+    struct dentry *dentry_sta;
+    struct ecrnx_debugfs_sta *debugfs_sta;
+
+    ECRNX_PRINT("%s: sta_idx:%d, stas:0x%p \n", __func__, sta_idx, stas[sta_idx]);
+    dentry_sta = stas[sta_idx];
+    if(!dentry_sta)
+    {
+        ECRNX_ERR("error sta_idx!!!\n");
+        return;
+    }
+
+    if (dentry_sta->d_inode)
+    {
+        debugfs_sta = dentry_sta->d_inode->i_private;
+        ECRNX_DBG("%s: dentry_sta->d_inode:0x%p, debugfs_sta:0x%p \n", __func__, dentry_sta->d_inode, debugfs_sta);
+        if (debugfs_sta)
+        {
+            debugfs_remove(dentry_sta);
+            kfree(debugfs_sta);
+        }
+    }
+
+    stas[sta_idx] = NULL;
+}
+
+static int p2p0_survey_info_show(struct seq_file *seq, void *v)
+{
+       survey_info_show(seq, v);
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(p2p0_survey_info);
+
+static int p2p_info_show(struct seq_file *seq, void *v)
+{
+    s8 noise_dbm;
+    struct cfg80211_chan_def chandef;
+    u32 bw_array[]={20, 20, 40, 80, 80, 160, 5, 10};
+
+    if (0 == ecrnx_signal_level_get(seq, IF_P2P, &noise_dbm))
+        seq_printf(seq, "signal level : %d dBm\n", noise_dbm);
+    else
+        seq_printf(seq, "signal level : \n");
+
+    if (0 == ecrnx_channel_get(seq, IF_P2P, &chandef))
+    {
+        seq_printf(seq, "frequency    : %d MHz\n", chandef.center_freq1);
+        seq_printf(seq, "bandwidth    : %d MHz\n", bw_array[chandef.width]);
+    }
+    else
+    {
+        seq_printf(seq, "frequency    : \n");
+        seq_printf(seq, "bandwidth    : \n");
+    }
+
+    if (NL80211_IFTYPE_P2P_CLIENT == ecrnx_p2p_role_get(seq, IF_P2P))
+    {
+        seq_printf(seq, "type         : P2P-client\n");
+    }
+    else if(NL80211_IFTYPE_P2P_GO == ecrnx_p2p_role_get(seq, IF_P2P))
+    {
+        seq_printf(seq, "type         : P2P-GO\n");
+    }
+    else
+    {
+        seq_printf(seq, "type         : \n");
+    }
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(p2p_info);
+
+static int p2p0_ht_info_show(struct seq_file *seq, void *v)
+{
+    struct cfg80211_chan_def chandef;
+    u32 bw_array[]={20, 20, 40, 80, 80, 160, 5, 10};
+
+    if (0 == ecrnx_channel_get(seq, IF_P2P, &chandef))
+        seq_printf(seq, "bandwidth    : %d MHz\n", bw_array[chandef.width]);
+    else
+        seq_printf(seq, "bandwidth    : \n");
+
+    return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(p2p0_ht_info);
+
+struct dentry *p2p0_dir = NULL;
+struct dentry *p2p0_survey_info, *p2p0_p2p_info, *p2p0_ht_info;
+
+int ecrnx_debugfs_p2p0_init(void *private_data)
+{
+    if (!ecr6600u_dir)
+        return -ENOENT;
+
+    p2p0_dir = debugfs_create_dir("p2p0", ecr6600u_dir);
+    if (!p2p0_dir)
+        return -ENOENT;
+
+    p2p0_survey_info = debugfs_create_file("survey_info", 0444, p2p0_dir, private_data, &p2p0_survey_info_fops);
+    if (!p2p0_survey_info)
+        goto Fail;
+
+    p2p0_p2p_info = debugfs_create_file("p2p_info", 0444, p2p0_dir, private_data, &p2p_info_fops);
+    if (!p2p0_p2p_info)
+        goto Fail;
+
+    p2p0_ht_info = debugfs_create_file("ht_info", 0444, p2p0_dir, private_data, &p2p0_ht_info_fops);
+    if (!p2p0_ht_info)
+        goto Fail;
+
+    return 0;
+
+Fail:
+    debugfs_remove_recursive(p2p0_dir);
+    p2p0_dir = NULL;
+    return -ENOENT;
+}
+
+void exrnx_debugfs_sruvey_info_tbl_init(void *private_data)
+{
+       struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw*)private_data;
+       INIT_LIST_HEAD(&ecrnx_hw->debugfs_survey_info_tbl_ptr);
+}
+
+int ecrnx_debugfs_init(void     *private_data)
+{
+    ecrnx_debugfs_info_init(private_data);
+    ecrnx_debugfs_hw_init(private_data);
+    ecrnx_debugfs_wlan0_init(private_data);
+    ecrnx_debugfs_ap0_init(private_data);
+    ecrnx_debugfs_p2p0_init(private_data);
+    exrnx_debugfs_sruvey_info_tbl_init(private_data);
+
+    memset(&debugfs_info, 0, sizeof(debugfs_info_t));
+    init_waitqueue_head(&debugfs_resp.rxdataq);
+
+    return 0;
+}
+#ifdef CONFIG_ECRNX_ESWIN_USB
+extern struct ring_buffer buf_handle;
+#endif
+
+void ecrnx_debugfs_exit(void)  
+{
+    if (ecr6600u_dir != NULL)
+        debugfs_remove_recursive(ecr6600u_dir);
+#ifdef CONFIG_ECRNX_ESWIN_USB
+    ring_buffer_deinit(&buf_handle);
+#endif
+
+}
+#endif
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.h b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_custom.h
new file mode 100644 (file)
index 0000000..244464a
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __ECRNX_DEBUGFS_CUSTOM_H_
+#define __ECRNX_DEBUGFS_CUSTOM_H_
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+
+#include "lmac_types.h"
+#include "ecrnx_debugfs_func.h"
+#include "ecrnx_compat.h"
+
+
+int ecrnx_debugfs_init(void     *private_data);
+void ecrnx_debugfs_exit(void);
+void ecrnx_debugfs_sta_in_ap_init(void *private_data);
+void ecrnx_debugfs_sta_in_ap_del(u8 sta_idx);
+
+#endif
+
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.c b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.c
new file mode 100644 (file)
index 0000000..f9a27a3
--- /dev/null
@@ -0,0 +1,995 @@
+#include "ecrnx_debugfs_func.h"
+#include "ecrnx_debugfs_custom.h"
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+
+debugfs_info_t debugfs_info;
+debugfs_resp_t debugfs_resp;
+
+u32 reg_buf[512]={0};
+
+REG_MAP_ST mac_reg_table[]={
+    {"INTC_IRQ_STATUS_ADDR",              0x00510000},
+    {"INTC_IRQ_UNMASK_SET_ADDR",          0x00510010},
+    {"INTC_IRQ_INDEX_ADDR",               0x00510040},
+    {"NXMAC_MAC_ADDR_LOW_ADDR",           0x00610010},
+    {"NXMAC_MAC_ADDR_HI_ADDR",            0x00610014},
+    {"NXMAC_STATE_CNTRL_ADDR",            0x00610038},
+    {"NXMAC_MAC_CNTRL_1_ADDR",            0x0061004c},
+    {"NXMAC_RX_CNTRL_ADDR",               0x00610060},
+    {"NXMAC_MAX_POWER_LEVEL_ADDR",        0x006100a0},
+    {"NXMAC_TIMINGS_1_ADDR",              0x006100e4},
+    {"NXMAC_RX_CNTRL_2_ADDR",             0x0061010c},
+    {"NXMAC_MONOTONIC_COUNTER_2_LO_ADDR", 0x00610120},
+    {"NXMAC_MONOTONIC_COUNTER_2_HI_ADDR", 0x00610124},
+    {"NXMAC_MAX_RX_LENGTH_ADDR",          0x00610150},
+    {"NXMAC_GEN_INT_STATUS_ADDR",         0x0061806c},
+    {"NXMAC_GEN_INT_ENABLE_ADDR",         0x00618074},
+    {"NXMAC_TX_RX_INT_STATUS_ADDR",       0x00618078},
+    {"NXMAC_TX_RX_INT_ENABLE_ADDR",       0x00618080},
+    {"NXMAC_DMA_CNTRL_SET_ADDR",          0x00618180},
+    {"NXMAC_DMA_STATUS_1_ADDR",           0x00618188},
+    {"NXMAC_DMA_STATUS_2_ADDR",           0x0061818c},
+    {"NXMAC_DMA_STATUS_3_ADDR",           0x00618190},
+    {"NXMAC_DMA_STATUS_4_ADDR",           0x00618194},
+    {"NXMAC_TX_BCN_HEAD_PTR_ADDR",        0x00618198},
+    {"NXMAC_TX_AC_0_HEAD_PTR_ADDR",       0x0061819c},
+    {"NXMAC_TX_AC_1_HEAD_PTR_ADDR",       0x006181a0},
+    {"NXMAC_TX_AC_2_HEAD_PTR_ADDR",       0x006181a4},
+    {"NXMAC_TX_AC_3_HEAD_PTR_ADDR",       0x006181a8},
+    {"NXMAC_RX_BUF_1_START_PTR_ADDR",     0x006181c8},
+    {"NXMAC_RX_BUF_1_END_PTR_ADDR",       0x006181cc},
+    {"NXMAC_RX_BUF_1_RD_PTR_ADDR",        0x006181d0},
+    {"NXMAC_RX_BUF_1_WR_PTR_ADDR",        0x006181d4},
+    {"NXMAC_RX_BUF_CONFIG_ADDR",          0x006181e8},
+    {"MDM_HDMCONFIG_ADDR",                0x00700000},
+};
+
+REG_MAP_ST rf_reg_table[]={
+    {"RF_REG_EXAMPLE",                    0x00510000},
+};
+
+REG_MAP_ST bb_reg_table[]={
+    {"BB_REG_EXAMPLE",                    0x00510000},
+};
+
+REG_MAP_ST efuse_map_table[]={
+    {"EFUSE_REG_EXAMPLE",                 0x00510000},
+};
+
+int argc;
+char *argv[MAX_ARGV];
+char slave_cli_cmd[50];
+int arg_val[MAX_ARGV];
+
+
+ECRNX_CLI_TABLE_ST ecrnx_cli_tab[]={
+    {"macbyp start ", "macbyp_tx_start", 6, {{0,32},{0,32},{0,32},{0,32},{0,32},{0,32}},    cli_macbyp_start},
+    {"macbyp stop",   "macbyp_tx_stop",  0, {},                                             cli_macbyp_stop},
+    {"rf txgain ",    "rf_set_txgain",   1, {{0,32}},                                       cli_rf_txgain},
+    {"rf rxgain ",    "rf_set_rxgain",   4, {{0,32},{0,32},{0,32},{0,32}},                  cli_rf_rxgain},
+    {"rf chan ",      "rf_set_chan",     2, {{0,32},{0,32}},                                cli_rf_chan},
+};
+
+
+void ecrnx_debugfs_param_send(debugfs_info_t *req)
+{
+    if(debugfs_info.debugfs_type == SLAVE_LOG_LEVEL){
+        if (req->u.slave_log_level_t.debug_level < DBG_TYPE_D || req->u.slave_log_level_t.debug_level > DBG_TYPE_O) {
+            ECRNX_ERR("ecrnx debug param error!!!\n");
+        }
+    }
+
+    ECRNX_DBG("%s: fstype:%d, level:%d, dir:%d \n", __func__, req->debugfs_type, req->u.slave_log_level_t.debug_level, req->u.slave_log_level_t.debug_dir);
+    //print_hex_dump(KERN_INFO, "ecrnx_debugfs_send ", DUMP_PREFIX_ADDRESS, 32, 1,
+    //    (u8*)req, sizeof(debugfs_info_t), false);
+    host_send(req, sizeof(debugfs_info_t), TX_FLAG_MSG_DEBUGFS_IE);
+}
+
+int ecrnx_log_level_get(LOG_CTL_ST *log)
+{
+    if (log == NULL)
+        return -1;
+
+    *log = log_ctl;
+
+    return 0;
+}
+
+int ecrnx_fw_log_level_set(u32 level, u32 dir)
+{
+    log_ctl.level = level;
+    log_ctl.dir = dir;
+
+    debugfs_info.debugfs_type = SLAVE_LOG_LEVEL;
+    debugfs_info.u.slave_log_level_t.debug_level = level;
+    debugfs_info.u.slave_log_level_t.debug_dir = dir;
+
+    ecrnx_debugfs_param_send(&debugfs_info);
+
+    return 0;
+}
+
+bool ecrnx_log_host_enable(void)
+{
+    if (log_ctl.dir)
+        return true;
+
+    return false;
+}
+
+int ecrnx_host_ver_get(u8 *ver)
+{
+    if (ver == NULL)
+        return -1;
+
+    sprintf(ver, "v%s", HOST_DRIVER_VERSION);
+
+    return 0;
+}
+
+int ecrnx_fw_ver_get(u8 *ver)
+{
+    if (ver == NULL)
+        return -1;
+
+    debugfs_info.debugfs_type = SLAVE_FW_INFO;
+    ecrnx_debugfs_param_send(&debugfs_info);
+
+    //wait for confirm
+    debugfs_resp.rxdatas = 0;
+    wait_event_interruptible_timeout(debugfs_resp.rxdataq, debugfs_resp.rxdatas, 1*HZ);
+
+    if (debugfs_resp.rxdatas)
+        memcpy(ver, debugfs_resp.rxdata, debugfs_resp.rxlen);
+    else
+        return -1;
+
+    return 0;
+}
+
+int ecrnx_build_time_get(u8 *build_time)
+{
+    if (build_time == NULL)
+        return -1;
+
+    sprintf(build_time, "%s %s", __DATE__, __TIME__);
+
+    return 0;
+}
+
+static int ecrnx_wdevs_get(struct seq_file *seq, struct wireless_dev **wdev)
+{
+    struct ecrnx_hw *ecrnx_hw;
+    u32 i;
+
+    if (seq->private == NULL)
+        return -1;
+
+    ecrnx_hw = seq->private;
+
+    for (i=0; i<NX_VIRT_STA_MAX; i++)
+    {
+        if (ecrnx_hw->vif_table[i] != NULL)
+            wdev[i] = &ecrnx_hw->vif_table[i]->wdev;
+        else
+            wdev[i] = NULL;
+    }
+
+    return 0;
+}
+
+static int ecrnx_wdev_match(struct wireless_dev **wdev, IF_TYPE_EN iftype, u32 *index)
+{
+    struct ecrnx_vif *ecrnx_vif;
+    u32 i;
+
+    for (i=0; i<NX_VIRT_STA_MAX; i++)
+    {
+        // 1. valid
+        if (wdev[i] == NULL)
+            continue;
+
+        // 2. up
+        ecrnx_vif = netdev_priv(wdev[i]->netdev);
+        if (ecrnx_vif->up == false)
+            continue;
+
+        // 3. type
+        switch(wdev[i]->iftype)
+        {
+            case NL80211_IFTYPE_STATION:
+            {
+                if (iftype == IF_STA)
+                {
+                    *index = i;
+                    return 0;
+                }
+                break;
+            }
+
+            case NL80211_IFTYPE_AP:
+            case NL80211_IFTYPE_AP_VLAN:
+            {
+                if (iftype == IF_AP)
+                {
+                    *index = i;
+                    return 0;
+                }
+                break;
+            }
+
+            case NL80211_IFTYPE_P2P_CLIENT:
+            case NL80211_IFTYPE_P2P_GO:
+            case NL80211_IFTYPE_P2P_DEVICE:
+            {
+                if (iftype == IF_P2P)
+                {
+                    *index = i;
+                    return 0;
+                }
+                break;
+            }
+
+            default :
+                break;
+        }
+    }
+
+    return -1;
+}
+
+int ecrnx_rf_info_get(struct seq_file *seq, IF_TYPE_EN iftype,RF_INFO_ST *cur, RF_INFO_ST *oper)
+{
+    struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+    struct cfg80211_chan_def chandef;
+    u32 index;
+
+    if (0 != ecrnx_wdevs_get(seq, wdev))
+        return -1;
+
+    if ((cur == NULL) || (oper == NULL))
+        return -1;
+
+    if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+        return -1;
+
+    if (0 != ecrnx_cfg80211_get_channel(wdev[index]->wiphy, wdev[index], &chandef))
+        return -1;
+
+    cur->ch = (chandef.center_freq1 - 2412)/5 + 1;
+    cur->bw = chandef.width;
+    cur->ch_offset = 0;
+
+    oper->ch = (chandef.center_freq1 - 2412)/5 + 1;
+    oper->bw = chandef.width;
+    oper->ch_offset = 0;
+
+    return 0;
+}
+
+int ecrnx_country_code_get(struct seq_file *seq, char *alpha2)
+{
+    struct ecrnx_hw *ecrnx_hw;
+
+    if (seq->private != NULL)
+        ecrnx_hw = seq->private;
+    else
+        return -1;
+
+    if (alpha2 == NULL)
+        return -1;
+
+    if (ecrnx_hw->wiphy == NULL)
+        return -1;
+
+    if (ecrnx_hw->wiphy->regd == NULL)
+        return -1;
+
+    memcpy(alpha2, ecrnx_hw->wiphy->regd->alpha2, sizeof(ecrnx_hw->wiphy->regd->alpha2));
+    alpha2[2] = '\0';
+
+    return 0;
+}
+
+int ecrnx_mac_addr_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 *mac_addr)
+{
+    struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+    u32 index;
+
+    if (0 != ecrnx_wdevs_get(seq, wdev))
+        return -1;
+
+    if (mac_addr == NULL)
+        return -1;
+
+    if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+        return -1;
+
+    memcpy(mac_addr, wdev[index]->netdev->perm_addr, ETH_ALEN);
+
+    return 0;
+}
+
+int ecrnx_mac_addr_get_ex(struct seq_file *seq, u8 *mac_addr_info)
+{
+    struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+    u32 i;
+
+    if (0 != ecrnx_wdevs_get(seq, wdev))
+        return -1;
+
+    if (mac_addr_info == NULL)
+        return -1;
+
+    mac_addr_info[0] = '\0';
+    for (i=0; i<NX_VIRT_STA_MAX; i++)
+    {
+        if (wdev[i] != NULL)
+        {
+            sprintf(mac_addr_info+strlen(mac_addr_info), "%s hw_port(%d) mac_addr=%02X:%02X:%02X:%02X:%02X:%02X\n", wdev[i]->netdev->name, wdev[i]->netdev->ifindex,
+                wdev[i]->netdev->perm_addr[0], wdev[i]->netdev->perm_addr[1], wdev[i]->netdev->perm_addr[2],
+                wdev[i]->netdev->perm_addr[3], wdev[i]->netdev->perm_addr[4], wdev[i]->netdev->perm_addr[5]);
+        }
+    }
+
+    return 0;
+}
+
+int ecrnx_channel_get(struct seq_file *seq, IF_TYPE_EN iftype, struct cfg80211_chan_def *chandef)
+{
+    struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+    u32 index;
+
+    if (0 != ecrnx_wdevs_get(seq, wdev))
+        return -1;
+
+    if (chandef == NULL)
+        return -1;
+
+    if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+        return -1;
+
+    return ecrnx_cfg80211_get_channel(wdev[index]->wiphy, wdev[index], chandef);
+}
+
+int ecrnx_p2p_role_get(struct seq_file *seq, IF_TYPE_EN iftype)
+{
+    struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+    u32 index;
+
+    if (0 != ecrnx_wdevs_get(seq, wdev))
+        return -1;
+
+    if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+        return -1;
+
+    return wdev[index]->iftype;
+}
+
+int ecrnx_bssid_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 *bssid)
+{
+    struct station_info sinfo;
+    struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+    u32 index;
+
+    if (0 != ecrnx_wdevs_get(seq, wdev))
+        return -1;
+
+    if (bssid == NULL)
+        return -1;
+
+    if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+        return -1;
+
+    return ecrnx_cfg80211_dump_station(wdev[index]->wiphy, wdev[index]->netdev, 0, bssid, &sinfo);
+}
+
+int ecrnx_signal_level_get(struct seq_file *seq, IF_TYPE_EN iftype, s8 *noise_dbm)
+{
+    struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+    struct ecrnx_hw *ecrnx_hw;
+    u32 index;
+
+    if (0 != ecrnx_wdevs_get(seq, wdev))
+        return -1;
+
+    if (noise_dbm == NULL)
+        return -1;
+
+    if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+        return -1;
+
+    ecrnx_hw = wdev_priv(wdev[index]);
+    *noise_dbm = ecrnx_hw->survey[0].noise_dbm;
+
+    return 0;
+}
+
+int ecrnx_flags_get(struct seq_file *seq, IF_TYPE_EN iftype, u32 *flags)
+{
+    struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+    u32 index;
+
+    if (0 != ecrnx_wdevs_get(seq, wdev))
+        return -1;
+
+    if (flags == NULL)
+        return -1;
+
+    if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+        return -1;
+
+    *flags = wdev[index]->wiphy->flags;
+
+    return 0;
+}
+
+int ecrnx_ssid_get(struct seq_file *seq, IF_TYPE_EN iftype, char *ssid)
+{
+    struct wireless_dev *wdev[NX_VIRT_STA_MAX];
+    u32 index;
+
+    if (0 != ecrnx_wdevs_get(seq, wdev))
+        return -1;
+
+    if (ssid == NULL)
+        return -1;
+
+    if (0 != ecrnx_wdev_match(wdev, iftype, &index))
+        return -1;
+
+    memcpy(ssid, wdev[index]->ssid, IEEE80211_MAX_SSID_LEN);
+
+    return 0;
+}
+
+int ecrnx_sta_mac_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 sta_mac[][ETH_ALEN+1])
+{
+    struct ecrnx_hw *ecrnx_hw;
+    u32 i;
+    u8 mac[ETH_ALEN] = {0};
+
+    if (seq->private != NULL)
+        ecrnx_hw = seq->private;
+    else
+        return -1;
+
+    // station table
+    for (i=0; i<NX_REMOTE_STA_MAX; i++)
+    {
+        if (ecrnx_hw->sta_table[i].valid == true)
+        {
+            if (0 == memcmp(mac, ecrnx_hw->sta_table[i].mac_addr, ETH_ALEN))
+            {
+                sta_mac[i][0] = 0xFF;
+                continue;
+            }
+
+            sta_mac[i][0] = 0xFF;
+            memcpy(&sta_mac[i][1], ecrnx_hw->sta_table[i].mac_addr, ETH_ALEN);
+
+        }
+    }
+
+    return 0;
+
+}
+
+int ecrnx_mac_reg_dump(struct seq_file *seq)
+{
+    u32 i, data;
+
+    seq_printf(seq, "%-34s  %-11s %-10s\n", "name", "addr", "value");
+
+    for (i=0; i<(sizeof(mac_reg_table)/sizeof(mac_reg_table[0])); i++)
+    {
+        ecrnx_slave_reg_read(mac_reg_table[i].addr, &data, 1);
+        seq_printf(seq, "%-34s  0x%08X: 0x%08X\n", mac_reg_table[i].name, mac_reg_table[i].addr, data);
+    }
+
+    return 0;
+}
+
+int ecrnx_rf_reg_dump(struct seq_file *seq)
+{
+    u32 i, data;
+
+    seq_printf(seq, "%-34s  %-11s %-10s\n", "name", "addr", "value");
+
+    for (i=0; i<(sizeof(rf_reg_table)/sizeof(rf_reg_table[0])); i++)
+    {
+        ecrnx_slave_reg_read(rf_reg_table[i].addr, &data, 1);
+        seq_printf(seq, "%-34s  0x%08X: 0x%08X\n", rf_reg_table[i].name, rf_reg_table[i].addr, data);
+    }
+
+    return 0;
+}
+
+int ecrnx_bb_reg_dump(struct seq_file *seq)
+{
+    u32 i, data;
+
+    seq_printf(seq, "%-34s  %-11s %-10s\n", "name", "addr", "value");
+
+    for (i=0; i<(sizeof(bb_reg_table)/sizeof(bb_reg_table[0])); i++)
+    {
+        ecrnx_slave_reg_read(bb_reg_table[i].addr, &data, 1);
+        seq_printf(seq, "%-34s  0x%08X: 0x%08X\n", bb_reg_table[i].name, bb_reg_table[i].addr, data);
+    }
+
+    return 0;
+}
+
+int ecrnx_efuse_map_dump(struct seq_file *seq)
+{
+    u32 i, data;
+
+    seq_printf(seq, "%-34s  %-11s %-10s\n", "name", "addr", "value");
+
+    for (i=0; i<(sizeof(efuse_map_table)/sizeof(efuse_map_table[0])); i++)
+    {
+        ecrnx_slave_reg_read(efuse_map_table[i].addr, &data, 1);
+        seq_printf(seq, "%-34s  0x%08X: 0x%08X\n", efuse_map_table[i].name, efuse_map_table[i].addr, data);
+    }
+
+    return 0;
+}
+
+int ecrnx_slave_reg_read(u32 addr, u32 *data, u32 len)
+{
+    if (data == NULL)
+        return -1;
+
+    debugfs_info.debugfs_type = SLAVE_READ_REG;
+    debugfs_info.u.slave_read_reg_t.reg = addr;
+    debugfs_info.u.slave_read_reg_t.len = len;
+    ecrnx_debugfs_param_send(&debugfs_info);
+
+    //wait for confirm
+    debugfs_resp.rxdatas = 0;
+    wait_event_interruptible_timeout(debugfs_resp.rxdataq, debugfs_resp.rxdatas, 1*HZ);
+
+    if (debugfs_resp.rxdatas)
+        memcpy((u8 *)data, (u8 *)debugfs_resp.rxdata, debugfs_resp.rxlen);
+
+    return 0;
+}
+
+int ecrnx_slave_reg_write(u32 addr, u32 data, u32 len)
+{
+    debugfs_info.debugfs_type = SLAVE_WRITE_REG;
+    debugfs_info.u.slave_write_reg_t.reg = addr;
+    debugfs_info.u.slave_write_reg_t.value = data;
+    ecrnx_debugfs_param_send(&debugfs_info);
+
+    //wait for confirm
+    debugfs_resp.rxdatas = 0;
+    wait_event_interruptible_timeout(debugfs_resp.rxdataq, debugfs_resp.rxdatas, 1*HZ);
+
+    return 0;
+}
+
+int ecrnx_slave_cli_send(u8 *cli, u8 *resp)
+{
+    if (cli == NULL)
+        return -1;
+
+    debugfs_info.debugfs_type = SLAVE_FUNC_INFO;
+    strcpy(debugfs_info.u.slave_cli_func_info_t.func_and_param, cli);
+    ecrnx_debugfs_param_send(&debugfs_info);
+
+    //wait for confirm
+    debugfs_resp.rxdatas = 0;
+    wait_event_interruptible_timeout(debugfs_resp.rxdataq, debugfs_resp.rxdatas, 1*HZ);
+
+    if ((debugfs_resp.rxdatas) && (debugfs_resp.rxlen))
+    {
+        ECRNX_PRINT("%s\n", debugfs_resp.rxdata);
+    }
+
+    return 0;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
+/**
+ * ether_addr_copy - Copy an Ethernet address
+ * @dst: Pointer to a six-byte array Ethernet address destination
+ * @src: Pointer to a six-byte array Ethernet address source
+ *
+ * Please note: dst & src must both be aligned to u16.
+ */
+static inline void ether_addr_copy(u8 *dst, const u8 *src)
+{
+#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
+       *(u32 *)dst = *(const u32 *)src;
+       *(u16 *)(dst + 4) = *(const u16 *)(src + 4);
+#else
+       u16 *a = (u16 *)dst;
+       const u16 *b = (const u16 *)src;
+
+       a[0] = b[0];
+       a[1] = b[1];
+       a[2] = b[2];
+#endif
+}
+
+#endif
+void ecrnx_debugfs_survey_info_update(struct ecrnx_hw *ecrnx_hw, struct cfg80211_bss *bss)
+{
+       __le16 ie_cap = 0;
+       const u8 *ssid_elm;
+       const u8 *ie_wpa = NULL, *ie_wpa2 = NULL, *ie_wps = NULL;
+       const u8 *ie_p2p = NULL;
+
+       struct ecrnx_debugfs_survey_info_tbl *new_node, *entry, *tmp;
+       new_node = kzalloc(sizeof(struct ecrnx_debugfs_survey_info_tbl),
+                  GFP_ATOMIC);
+
+       if (bss) {
+               const struct cfg80211_bss_ies *ies;
+
+               new_node->ch = ieee80211_frequency_to_channel(bss->channel->center_freq);
+               ether_addr_copy(new_node->bssid, bss->bssid);
+               new_node->rssi = bss->signal/100;
+
+               rcu_read_lock();
+       
+               ie_wpa2 = ieee80211_bss_get_ie(bss, WLAN_EID_RSN);
+
+               ies = rcu_dereference(bss->ies);
+
+               ie_wpa = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                                               WLAN_OUI_TYPE_MICROSOFT_WPA,
+                                               ies->data,
+                                               ies->len);
+
+               ie_wps = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
+                                               WLAN_OUI_TYPE_MICROSOFT_WPS,
+                                               ies->data,
+                                               ies->len);
+
+               ie_p2p = cfg80211_find_vendor_ie(WLAN_OUI_WFA,
+                                               WLAN_OUI_TYPE_WFA_P2P,
+                                               ies->data,
+                                               ies->len);
+               
+               ie_cap = cpu_to_le16(bss->capability);
+
+               ssid_elm = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
+               if (ssid_elm) {
+                       if (ssid_elm[1] <= IEEE80211_MAX_SSID_LEN)
+                               memcpy(new_node->ssid, ssid_elm + 2, ssid_elm[1]);
+               }
+               
+               rcu_read_unlock();
+       }
+
+               sprintf(new_node->flag, "%s%s%s%s%s%s",
+                       (ie_wpa) ? "[WPA]" : "",
+                       (ie_wpa2) ? "[WPA2]" : "",
+                       (!ie_wpa && !ie_wpa && ie_cap & BIT(4)) ? "[WEP]" : "",
+                       (ie_wps) ? "[WPS]" : "",
+                       (ie_cap & BIT(0)) ? "[ESS]" : "",
+                       (ie_p2p) ? "[P2P]" : "");
+                               
+       INIT_LIST_HEAD(&new_node->list);
+
+       //if (!list_empty(&ecrnx_hw->debugfs_survey_info_tbl_ptr)) {
+               list_for_each_entry_safe(entry, tmp, &ecrnx_hw->debugfs_survey_info_tbl_ptr, list) {
+                       if(memcmp(entry->bssid, new_node->bssid, ETH_ALEN) == 0) {
+                               list_del(&entry->list);
+                               kfree(entry);
+                       }
+               }
+       //}
+       list_add_tail(&new_node->list, &ecrnx_hw->debugfs_survey_info_tbl_ptr);
+}
+
+void ecrnx_debugfs_noise_of_survey_info_update(struct ecrnx_hw *ecrnx_hw, struct ecrnx_survey_info *ecrnx_survey, int chan_no)
+{
+       struct ecrnx_debugfs_survey_info_tbl *entry;
+       
+       list_for_each_entry(entry, &ecrnx_hw->debugfs_survey_info_tbl_ptr, list) {
+               if(entry->ch == chan_no) {
+                       entry->noise = ecrnx_survey->noise_dbm;
+               }
+       }
+}
+
+void ecrnx_debugfs_add_station_in_ap_mode(struct ecrnx_hw *ecrnx_hw,
+                                            struct ecrnx_sta *sta, struct station_parameters *params)
+{
+    struct ecrnx_debugfs_sta *debugfs_sta;
+    //char file_name[18];
+
+    if(!sta || !params)
+    {
+        ECRNX_ERR("%s-%d:sta(%p) or params(%p) is null, return!\n", __func__, __LINE__, sta, params);
+        return;
+    }
+       
+    debugfs_sta = kzalloc(sizeof(struct ecrnx_debugfs_sta), GFP_KERNEL);
+
+    if (!debugfs_sta)
+        ECRNX_ERR("error debugfs_sta kzalloc!\n");
+
+
+    debugfs_sta->sta_idx = sta->sta_idx;
+    debugfs_sta->aid = sta->aid;
+    debugfs_sta->ch_idx = sta->ch_idx;
+    debugfs_sta->ht = sta->ht;
+    ether_addr_copy(debugfs_sta->mac_addr, sta->mac_addr);
+    debugfs_sta->qos = sta->qos;
+    debugfs_sta->vht = sta->vht;
+    debugfs_sta->width = sta->width;
+
+    if(sta->ht)
+    {
+        if (params->ht_capa->cap_info & IEEE80211_HT_CAP_TX_STBC)
+            debugfs_sta->stbc_cap = 1;
+        if (params->ht_capa->cap_info & IEEE80211_HT_CAP_LDPC_CODING)
+            debugfs_sta->ldpc_cap = 1;
+        if (params->ht_capa->cap_info & IEEE80211_HT_CAP_SGI_20)
+            debugfs_sta->sgi_20m = 1;
+        if (params->ht_capa->cap_info & IEEE80211_HT_CAP_SGI_40)
+            debugfs_sta->sgi_40m = 1;
+    }
+
+    ecrnx_debugfs_sta_in_ap_init(debugfs_sta);
+}
+
+int cli_parse_args(uint8_t* str, char* argv[])
+{
+    int i = 0;
+    char* ch = (char *)str;
+
+    while(*ch == ' ') ch++;
+    if (*ch == '\n')
+        return 0;
+
+    while(*ch != '\0') 
+    {
+        i++;
+
+        if (i > MAX_ARGV)
+            return 0;
+
+        argv[i-1] = ch;
+        while(*ch != '\0')
+        {
+            if(*ch == ' ')
+                break;
+            else
+                ch++;
+        }
+
+        *ch = '\0';
+        ch++;
+        while(*ch == ' ') {
+            ch++;
+        }
+    }
+
+    return i;
+}
+
+void argv_display(uint32_t argc, char* argv[])
+{
+    uint32_t i;
+
+    ECRNX_PRINT("argc is %d\n", argc);
+    for (i=0; i<argc; i++)
+    {
+        ECRNX_PRINT("param %d is %s\n", i, argv[i]);
+    }
+}
+
+int _atoi(char *pstr)
+{
+    int ret_integer = 0;
+    int integer_sign = 1;
+
+    if (pstr == NULL)
+    {
+        //printk("Pointer is NULL\n");
+        return 0;
+    }
+
+    while (*pstr == ' ')
+    {
+        pstr++;
+    }
+
+    if (*pstr == '-')
+    {
+        integer_sign = -1;
+    }
+    if (*pstr == '-' || *pstr == '+')
+    {
+        pstr++;
+    }
+
+    while (*pstr >= '0' && *pstr <= '9')
+    {
+        ret_integer = ret_integer * 10 + *pstr - '0';
+        pstr++;
+    }
+    ret_integer = integer_sign * ret_integer;
+
+    return ret_integer;
+}
+
+int cli_check_parm_num(uint32_t index, uint8_t *param)
+{
+    argc = cli_parse_args(param, argv);
+    //argv_display(argc, argv);
+
+    if (argc != ecrnx_cli_tab[index].param_num)
+        return -1;
+
+    return 0;
+}
+
+int cli_check_parm_range(uint32_t start_index, uint32_t index, uint32_t argc, char* argv[])
+{
+    uint32_t i;
+
+    for (i=start_index; i<argc; i++)
+    {
+        arg_val[i] = _atoi(argv[i]);
+        if ((arg_val[i] < ecrnx_cli_tab[index].param_range[i].range_min)
+            || (arg_val[i] > ecrnx_cli_tab[index].param_range[i].range_max))
+        {
+            ECRNX_PRINT("param %d is out of range! current %d, min %d, max %d\n",
+                i, arg_val[i], ecrnx_cli_tab[index].param_range[i].range_min, ecrnx_cli_tab[index].param_range[i].range_max);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+int cli_macbyp_start(uint32_t index, uint8_t *param)
+{
+    uint32_t i;
+
+    // parse parameters
+    if (0 != cli_check_parm_num(index, param))
+        return -1;
+
+    // check range value
+    if (0 != strcmp(argv[0], "11a")
+        && 0 != strcmp(argv[0], "11b")
+        && 0 != strcmp(argv[0], "11g")
+        && 0 != strcmp(argv[0], "11n")
+        && 0 != strcmp(argv[0], "11ac")
+        && 0 != strcmp(argv[0], "11ax"))
+        return -1;
+
+    if (0 != cli_check_parm_range(1, index, argc, argv))
+        return -1;
+
+    // package command
+    sprintf(slave_cli_cmd, "%s %s", ecrnx_cli_tab[index].map_cmd, argv[0]);
+    for (i=1; i<argc; i++)
+        sprintf(slave_cli_cmd+strlen(slave_cli_cmd), " %d", arg_val[i]);
+
+    //printk("--->The final command is: %s<-\n", slave_cli_cmd);
+    ecrnx_slave_cli_send(slave_cli_cmd, NULL);
+
+    return 0;
+}
+
+int cli_macbyp_stop(uint32_t index, uint8_t *param)
+{
+    // parse parameters
+    if (0 != cli_check_parm_num(index, param))
+        return -1;
+
+    // package command
+    sprintf(slave_cli_cmd, "%s", ecrnx_cli_tab[index].map_cmd);
+
+    //printk("---> The final command is: %s<-\n", slave_cli_cmd);
+    ecrnx_slave_cli_send(slave_cli_cmd, NULL);
+
+    return 0;
+}
+
+int cli_rf_txgain(uint32_t index, uint8_t *param)
+{
+    uint32_t i;
+
+    // parse parameters
+    if (0 != cli_check_parm_num(index, param))
+        return -1;
+
+    // check range value
+    if (0 != cli_check_parm_range(0, index, argc, argv))
+        return -1;
+
+    // package command
+    sprintf(slave_cli_cmd, "%s", ecrnx_cli_tab[index].map_cmd);
+    for (i=0; i<argc; i++)
+        sprintf(slave_cli_cmd+strlen(slave_cli_cmd), " %d", arg_val[i]);
+
+    //printk("---> The final command is: %s<-\n", slave_cli_cmd);
+    ecrnx_slave_cli_send(slave_cli_cmd, NULL);
+
+    return 0;
+}
+
+int cli_rf_rxgain(uint32_t index, uint8_t *param)
+{
+    uint32_t i;
+
+    // parse parameters
+    if (0 != cli_check_parm_num(index, param))
+        return -1;
+
+    // check range value
+    if (0 != cli_check_parm_range(0, index, argc, argv))
+        return -1;
+
+    // package command
+    sprintf(slave_cli_cmd, "%s", ecrnx_cli_tab[index].map_cmd);
+    for (i=0; i<argc; i++)
+        sprintf(slave_cli_cmd+strlen(slave_cli_cmd), " %d", arg_val[i]);
+
+    //printk("---> The final command is: %s<-\n", slave_cli_cmd);
+    ecrnx_slave_cli_send(slave_cli_cmd, NULL);
+
+    return 0;
+}
+
+int cli_rf_chan(uint32_t index, uint8_t *param)
+{
+    uint32_t i;
+
+    // parse parameters
+    if (0 != cli_check_parm_num(index, param))
+        return -1;
+
+    // check range value
+    if (0 != cli_check_parm_range(0, index, argc, argv))
+        return -1;
+
+    // package command
+    sprintf(slave_cli_cmd, "%s", ecrnx_cli_tab[index].map_cmd);
+    for (i=0; i<argc; i++)
+        sprintf(slave_cli_cmd+strlen(slave_cli_cmd), " %d", arg_val[i]);
+
+    //printk("---> The final command is: %s<-\n", slave_cli_cmd);
+    ecrnx_slave_cli_send(slave_cli_cmd, NULL);
+
+    return 0;
+}
+
+int cli_cmd_parse(uint8_t *cmd)
+{
+    uint32_t i;
+    int ret;
+
+    if (cmd == NULL)
+        return -1;
+
+    //printk("cmd is :%s<-\n", cmd);
+    for (i=0; i<sizeof(ecrnx_cli_tab)/sizeof(ecrnx_cli_tab[0]); i++)
+    {
+        if (0 == memcmp(cmd, ecrnx_cli_tab[i].cmd, strlen(ecrnx_cli_tab[i].cmd)))
+        {
+            //printk("index %d, cmd %s\n", i, ecrnx_cli_tab[i].cmd);
+            ret = ecrnx_cli_tab[i].cmd_func(i, cmd + strlen(ecrnx_cli_tab[i].cmd));
+            goto exit;
+        }
+    }
+
+    ECRNX_ERR("No matching commands!\n");
+
+    return -2;
+
+exit:
+    return ret;
+}
+#endif
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.h b/drivers/net/wireless/eswin/fullmac/ecrnx_debugfs_func.h
new file mode 100644 (file)
index 0000000..905d368
--- /dev/null
@@ -0,0 +1,211 @@
+#ifndef __ECRNX_DEBUGFS_FUNC_H_
+#define __ECRNX_DEBUGFS_FUNC_H_
+
+#include "ecrnx_defs.h"
+#include "ecrnx_utils.h"
+#include "eswin_utils.h"
+#include "fw_head_check.h"
+
+#include <linux/etherdevice.h>
+
+
+
+#define HOST_DRIVER_VERSION     "1.0.0"
+
+#define NX_VIRT_STA_MAX         (NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX)
+
+typedef enum {
+    IF_STA = 0,
+    IF_AP,
+    IF_P2P
+} IF_TYPE_EN;
+
+typedef struct {
+    u8 ch;
+    u8 bw;
+    u32 ch_offset;
+} RF_INFO_ST;
+
+typedef struct {
+    u8 name[48];
+    u32 addr;
+}REG_MAP_ST;
+
+struct ecrnx_debugfs_survey_info_tbl
+{
+       struct list_head list;
+       u8              ssid[IEEE80211_MAX_SSID_LEN];
+       u8              bssid[ETH_ALEN];
+       u32             ch;
+       int             rssi;
+       s32             sdbm;
+       s16             noise;
+       u32             age;
+       char    flag[64];
+};
+
+struct ecrnx_debugfs_sta
+{
+       u8 mac_addr[ETH_ALEN];
+    u16 aid;                /* association ID */
+    u8 sta_idx;             /* Identifier of the station */
+       enum nl80211_chan_width width; /* Channel width */
+       u8 ch_idx;              /* Identifier of the channel
+                               context the station belongs to */
+       bool qos;               /* Flag indicating if the station
+                               supports QoS */
+    bool ht;               /* Flag indicating if the station
+                               supports HT */
+    bool vht;               /* Flag indicating if the station
+                               supports VHT */
+
+       bool sgi_20m;
+       bool sgi_40m;
+
+       u8      ampdu_enable;/* for enable Tx A-MPDU */
+       
+       /* for processing Tx A-MPDU */
+       u8      agg_enable_bitmap;
+       /* u8   ADDBA_retry_count; */
+       u8      candidate_tid_bitmap;
+
+       u8      ldpc_cap;
+       u8      stbc_cap;
+       u8      beamform_cap;
+};
+
+#define MAX_ARGV                10
+
+typedef int (*_cmd_func)(uint32_t index, uint8_t *param);
+
+typedef struct{
+    uint32_t range_min;
+    uint32_t range_max;
+}ECRNX_CLI_PARAM_RANGE_ST;
+
+typedef struct {
+    char cmd[20];
+    char map_cmd[20];
+    uint32_t param_num;
+    ECRNX_CLI_PARAM_RANGE_ST param_range[MAX_ARGV];
+    _cmd_func cmd_func;
+}ECRNX_CLI_TABLE_ST;
+
+extern ECRNX_CLI_TABLE_ST ecrnx_cli_tab[];
+
+enum debugfs_type
+{
+    SLAVE_LOG_LEVEL = 1,
+    SLAVE_FW_INFO,
+    SLAVE_RF_INFO,
+    SLAVE_MAC_REG_DUMP,
+    SLAVE_RF_REG_DUMP,
+    SLAVE_BB_REG_DUMP,
+    SLAVE_COUNTRY_CODE,
+    SLAVE_EFUSE_MAP,
+    SLAVE_WOW_ENABLE,
+    SLAVE_WOW_WLAN_GOPI_INFO,
+    SLAVE_READ_REG,
+    SLAVE_WRITE_REG,
+    SLAVE_FUNC_INFO,
+    SLAVE_DEBUGFS_MAX,
+};
+
+typedef struct
+{
+    uint32_t debugfs_type;
+
+    union
+    {
+        struct 
+        {
+            uint32_t debug_level;
+            uint32_t debug_dir;
+        }slave_log_level_t;
+
+        struct
+        {
+            uint32_t info;
+            uint32_t info_len;
+        }slave_fw_info_t;
+
+        struct
+        {
+            uint32_t reg;
+            uint32_t len;
+        }slave_read_reg_t;
+
+        struct
+        {
+            uint32_t reg;
+            uint32_t value;
+        }slave_write_reg_t;
+
+        struct
+        {
+            uint8_t func_and_param[56];
+        }slave_cli_func_info_t;
+
+    }u;
+}debugfs_info_t;
+
+typedef struct
+{
+    uint32_t debugfs_type;
+    unsigned char   rxdata[ECRNX_RXSIZE];
+    int             rxlen;
+    wait_queue_head_t   rxdataq;
+    int                 rxdatas;
+}debugfs_resp_t;
+
+extern debugfs_info_t debugfs_info;
+extern debugfs_resp_t debugfs_resp;
+
+extern u32 reg_buf[512];
+
+void ecrnx_debug_param_send(dbg_req_t *req);
+
+struct ring_buffer *usb_dbg_buf_get(void);
+
+int ecrnx_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+                                      int idx, u8 *mac, struct station_info *sinfo);
+int ecrnx_cfg80211_get_channel(struct wiphy *wiphy,
+                                     struct wireless_dev *wdev,
+                                     struct cfg80211_chan_def *chandef);
+int ecrnx_log_level_get(LOG_CTL_ST *log);
+int ecrnx_fw_log_level_set(u32 level, u32 dir);
+bool ecrnx_log_host_enable(void);
+int ecrnx_host_ver_get(u8 *ver);
+int ecrnx_fw_ver_get(u8 *ver);
+int ecrnx_build_time_get(u8 *build_time);
+int ecrnx_rf_info_get(struct seq_file *seq, IF_TYPE_EN iftype,RF_INFO_ST *cur, RF_INFO_ST *oper);
+int ecrnx_country_code_get(struct seq_file *seq, char *alpha2);
+int ecrnx_mac_addr_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 *mac_addr);
+int ecrnx_mac_addr_get_ex(struct seq_file *seq, u8 *mac_addr_info);
+int ecrnx_channel_get(struct seq_file *seq, IF_TYPE_EN iftype, struct cfg80211_chan_def *chandef);
+int ecrnx_p2p_role_get(struct seq_file *seq, IF_TYPE_EN iftype);
+int ecrnx_bssid_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 *bssid);
+int ecrnx_signal_level_get(struct seq_file *seq, IF_TYPE_EN iftype, s8 *noise_dbm);
+int ecrnx_flags_get(struct seq_file *seq, IF_TYPE_EN iftype, u32 *flags);
+int ecrnx_ssid_get(struct seq_file *seq, IF_TYPE_EN iftype, char *ssid);
+int ecrnx_sta_mac_get(struct seq_file *seq, IF_TYPE_EN iftype, u8 sta_mac[][ETH_ALEN+1]);
+int ecrnx_mac_reg_dump(struct seq_file *seq);
+int ecrnx_rf_reg_dump(struct seq_file *seq);
+int ecrnx_bb_reg_dump(struct seq_file *seq);
+int ecrnx_efuse_map_dump(struct seq_file *seq);
+int ecrnx_slave_reg_read(u32 addr, u32 *data, u32 len);
+int ecrnx_slave_reg_write(u32 addr, u32 data, u32 len);
+int ecrnx_slave_cli_send(u8 *cli, u8 *resp);
+void ecrnx_debugfs_survey_info_update(struct ecrnx_hw *ecrnx_hw, struct cfg80211_bss *bss);
+void ecrnx_debugfs_noise_of_survey_info_update(struct ecrnx_hw *ecrnx_hw,
+                                                                               struct ecrnx_survey_info *ecrnx_survey, int chan_no);
+void ecrnx_debugfs_add_station_in_ap_mode(struct ecrnx_hw *ecrnx_hw,
+                                                                                       struct ecrnx_sta *sta, struct station_parameters *params);
+int cli_macbyp_start(uint32_t index, uint8_t *param);
+int cli_macbyp_stop(uint32_t index, uint8_t *param);
+int cli_rf_txgain(uint32_t index, uint8_t *param);
+int cli_rf_rxgain(uint32_t index, uint8_t *param);
+int cli_rf_chan(uint32_t index, uint8_t *param);
+int cli_cmd_parse(uint8_t *cmd);
+
+#endif
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_defs.h b/drivers/net/wireless/eswin/fullmac/ecrnx_defs.h
new file mode 100644 (file)
index 0000000..3c015b1
--- /dev/null
@@ -0,0 +1,1093 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_defs.h
+ *
+ * @brief Main driver structure declarations for fullmac driver
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_DEFS_H_
+#define _ECRNX_DEFS_H_
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/skbuff.h>
+#include <net/cfg80211.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
+#include <linux/vmalloc.h>
+#include <linux/module.h>
+#endif
+#include "ecrnx_mod_params.h"
+#include "ecrnx_debugfs.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_rx.h"
+#include "ecrnx_radar.h"
+#include "ecrnx_utils.h"
+#include "ecrnx_mu_group.h"
+#include "ecrnx_platform.h"
+#include "ecrnx_cmds.h"
+
+#include "ecrnx_p2p.h"
+#include "ecrnx_debug.h"
+#include "ecrnx_cfgfile.h"
+
+
+#define WPI_HDR_LEN    18
+#define WPI_PN_LEN     16
+#define WPI_PN_OFST     2
+#define WPI_MIC_LEN    16
+#define WPI_KEY_LEN    32
+#define WPI_SUBKEY_LEN 16 // WPI key is actually two 16bytes key
+
+#define LEGACY_PS_ID   0
+#define UAPSD_ID       1
+
+#define PS_SP_INTERRUPTED  255
+#define ECRNX_RXSIZE       1024
+
+#define CONFIG_ESWIN_RX_REORDER        1
+
+#if defined(CONFIG_ECRNX_HE)
+extern struct ieee80211_sta_he_cap ecrnx_he_cap;
+#endif
+
+/**
+ * struct ecrnx_bcn - Information of the beacon in used (AP mode)
+ *
+ * @head: head portion of beacon (before TIM IE)
+ * @tail: tail portion of beacon (after TIM IE)
+ * @ies: extra IEs (not used ?)
+ * @head_len: length of head data
+ * @tail_len: length of tail data
+ * @ies_len: length of extra IEs data
+ * @tim_len: length of TIM IE
+ * @len: Total beacon len (head + tim + tail + extra)
+ * @dtim: dtim period
+ */
+struct ecrnx_bcn {
+    u8 *head;
+    u8 *tail;
+    u8 *ies;
+    size_t head_len;
+    size_t tail_len;
+    size_t ies_len;
+    size_t tim_len;
+    size_t len;
+    u8 dtim;
+};
+
+/**
+ * struct ecrnx_key - Key information
+ *
+ * @hw_idx: Idx of the key from hardware point of view
+ */
+struct ecrnx_key {
+    u8 hw_idx;
+};
+
+/**
+ * Structure containing information about a Mesh Path
+ */
+struct ecrnx_mesh_path {
+    struct list_head list;          /* For ecrnx_vif.mesh_paths */
+    u8 path_idx;                    /* Path Index */
+    struct mac_addr tgt_mac_addr;   /* Target MAC Address */
+    struct ecrnx_sta *nhop_sta;      /* Pointer to the Next Hop STA */
+};
+
+struct ecrnx_mesh_proxy {
+    struct list_head list;          /* For ecrnx_vif.mesh_proxy */
+    struct mac_addr ext_sta_addr;   /* Address of the External STA */
+    struct mac_addr proxy_addr;     /* Proxy MAC Address */
+    bool local;                     /* Indicate if interface is a proxy for the device */
+};
+
+/**
+ * struct ecrnx_csa - Information for CSA (Channel Switch Announcement)
+ *
+ * @vif: Pointer to the vif doing the CSA
+ * @bcn: Beacon to use after CSA
+ * @elem: IPC buffer to send the new beacon to the fw
+ * @chandef: defines the channel to use after the switch
+ * @count: Current csa counter
+ * @status: Status of the CSA at fw level
+ * @ch_idx: Index of the new channel context
+ * @work: work scheduled at the end of CSA
+ */
+struct ecrnx_csa {
+    struct ecrnx_vif *vif;
+    struct ecrnx_bcn bcn;
+    struct ecrnx_ipc_elem_var elem;
+    struct cfg80211_chan_def chandef;
+    int count;
+    int status;
+    int ch_idx;
+    struct work_struct work;
+};
+
+/// Possible States of the TDLS link.
+enum tdls_status_tag {
+        /// TDLS link is not active (no TDLS peer connected)
+        TDLS_LINK_IDLE,
+        /// TDLS Setup Request transmitted
+        TDLS_SETUP_REQ_TX,
+        /// TDLS Setup Response transmitted
+        TDLS_SETUP_RSP_TX,
+        /// TDLS link is active (TDLS peer connected)
+        TDLS_LINK_ACTIVE,
+        /// TDLS Max Number of states.
+        TDLS_STATE_MAX
+};
+
+/*
+ * Structure used to save information relative to the TDLS peer.
+ * This is also linked within the ecrnx_hw vifs list.
+ *
+ */
+struct ecrnx_tdls {
+    bool active;                /* Indicate if TDLS link is active */
+    bool initiator;             /* Indicate if TDLS peer is the TDLS initiator */
+    bool chsw_en;               /* Indicate if channel switch is enabled */
+    u8 last_tid;                /* TID of the latest MPDU transmitted over the
+                                   TDLS direct link to the TDLS STA */
+    u16 last_sn;                /* Sequence number of the latest MPDU transmitted
+                                   over the TDLS direct link to the TDLS STA */
+    bool ps_on;                 /* Indicate if the power save is enabled on the
+                                   TDLS STA */
+    bool chsw_allowed;          /* Indicate if TDLS channel switch is allowed */
+};
+
+
+/**
+ * enum ecrnx_ap_flags - AP flags
+ *
+ * @ECRNX_AP_ISOLATE Isolate clients (i.e. Don't brige packets transmitted by
+ *                                   one client for another one)
+ */
+enum ecrnx_ap_flags {
+    ECRNX_AP_ISOLATE = BIT(0),
+    ECRNX_AP_USER_MESH_PM = BIT(1),
+    ECRNX_AP_CREATE_MESH_PATH = BIT(2),
+};
+
+/**
+ * enum ecrnx_sta_flags - STATION flags
+ *
+ * @ECRNX_STA_EXT_AUTH: External authentication is in progress
+ */
+enum ecrnx_sta_flags {
+    ECRNX_STA_EXT_AUTH = BIT(0),
+    ECRNX_STA_FT_OVER_DS = BIT(1),
+    ECRNX_STA_FT_OVER_AIR = BIT(2),
+};
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+/**
+ * struct ecrnx_iwpriv_amt_vif - iwpriv amt VIF information
+ *
+ * @ndev: Pointer to the associated net device
+ */
+struct ecrnx_iwpriv_amt_vif {
+    struct net_device *ndev;
+
+    unsigned char   rxdata[ECRNX_RXSIZE];
+    int             rxlen;
+    wait_queue_head_t   rxdataq;
+    int                 rxdatas;
+};
+#endif
+
+#define ECRNX_REORD_RX_MSDU_CNT      (256)
+#define ECRNX_REORD_TIMEOUT          (50)
+#define ECRNX_REORD_WINSIZE          (64)
+#define SN_LESS(a, b)           (((a-b)&0x800)!=0)
+#define SN_EQUAL(a, b)          (a == b)
+
+
+struct reord_cntrl {
+    bool active;
+    bool valid;
+    u16 win_size;
+    u16 win_start;
+    struct ecrnx_hw *ecrnx_hw;
+    struct ecrnx_vif *ecrnx_vif;
+    spinlock_t reord_list_lock;
+    struct list_head reord_list;
+    struct timer_list reord_timer;
+    struct work_struct reord_timer_work;
+};
+
+struct reord_msdu_info {
+    struct sk_buff *skb;
+    u8 tid;
+    u8 need_pn_check;
+    u8 is_ga;
+    u16 sn;
+    struct hw_rxhdr *hw_rxhdr;
+    struct list_head reord_pending_list;
+    struct list_head rx_msdu_list;
+    struct reord_cntrl *preorder_ctrl;
+};
+
+/**
+ * struct ecrnx_vif - VIF information
+ *
+ * @list: List element for ecrnx_hw->vifs
+ * @ecrnx_hw: Pointer to driver main data
+ * @wdev: Wireless device
+ * @ndev: Pointer to the associated net device
+ * @net_stats: Stats structure for the net device
+ * @key: Conversion table between protocol key index and MACHW key index
+ * @drv_vif_index: VIF index at driver level (only use to identify active
+ * vifs in ecrnx_hw->avail_idx_map)
+ * @vif_index: VIF index at fw level (used to index ecrnx_hw->vif_table, and
+ * ecrnx_sta->vif_idx)
+ * @ch_index: Channel context index (within ecrnx_hw->chanctx_table)
+ * @up: Indicate if associated netdev is up (i.e. Interface is created at fw level)
+ * @use_4addr: Whether 4address mode should be use or not
+ * @is_resending: Whether a frame is being resent on this interface
+ * @roc_tdls: Indicate if the ROC has been called by a TDLS station
+ * @tdls_status: Status of the TDLS link
+ * @tdls_chsw_prohibited: Whether TDLS Channel Switch is prohibited or not
+ * @generation: Generation ID. Increased each time a sta is added/removed
+ *
+ * STA / P2P_CLIENT interfaces
+ * @flags: see ecrnx_sta_flags
+ * @ap: Pointer to the peer STA entry allocated for the AP
+ * @tdls_sta: Pointer to the TDLS station
+ * @ft_assoc_ies: Association Request Elements (only allocated for FT connection)
+ * @ft_assoc_ies_len: Size, in bytes, of the Association request elements.
+ * @ft_target_ap: Target AP for a BSS transition for FT over DS
+ *
+ * AP/P2P GO/ MESH POINT interfaces
+ * @flags: see ecrnx_ap_flags
+ * @sta_list: List of station connected to the interface
+ * @bcn: Beacon data
+ * @bcn_interval: beacon interval in TU
+ * @bcmc_index: Index of the BroadCast/MultiCast station
+ * @csa: Information about current Channel Switch Announcement (NULL if no CSA)
+ * @mpath_list: List of Mesh Paths (MESH Point only)
+ * @proxy_list: List of Proxies Information (MESH Point only)
+ * @mesh_pm: Mesh power save mode currently set in firmware
+ * @next_mesh_pm: Mesh power save mode for next peer
+ *
+ * AP_VLAN interfaces
+ * @mater: Pointer to the master interface
+ * @sta_4a: When AP_VLAN interface are used for WDS (i.e. wireless connection
+ * between several APs) this is the 'gateway' sta to 'master' AP
+ */
+struct ecrnx_vif {
+    struct list_head list;
+    struct ecrnx_hw *ecrnx_hw;
+    struct wireless_dev wdev;
+    struct net_device *ndev;
+    struct net_device_stats net_stats;
+    struct ecrnx_key key[6];
+    u8 drv_vif_index;           /* Identifier of the VIF in driver */
+    u8 vif_index;               /* Identifier of the station in FW */
+    u8 ch_index;                /* Channel context identifier */
+    bool up;                    /* Indicate if associated netdev is up
+                                   (i.e. Interface is created at fw level) */
+    bool use_4addr;             /* Should we use 4addresses mode */
+    bool is_resending;          /* Indicate if a frame is being resent on this interface */
+    bool roc_tdls;              /* Indicate if the ROC has been called by a
+                                   TDLS station */
+    u8 tdls_status;             /* Status of the TDLS link */
+    bool tdls_chsw_prohibited;  /* Indicate if TDLS Channel Switch is prohibited */
+    int generation;
+
+    unsigned char   rxdata[ECRNX_RXSIZE];
+    int             rxlen;
+    wait_queue_head_t   rxdataq;
+    int                 rxdatas;
+
+       u32 mgmt_reg_stypes;    //GavinGao
+
+    union
+    {
+        struct
+        {
+            u32 flags;
+            struct ecrnx_sta *ap; /* Pointer to the peer STA entry allocated for
+                                    the AP */
+            struct ecrnx_sta *tdls_sta; /* Pointer to the TDLS station */
+            u8 *ft_assoc_ies;
+            int ft_assoc_ies_len;
+            u8 ft_target_ap[ETH_ALEN];
+        } sta;
+        struct
+        {
+            u32 flags;
+            struct list_head sta_list; /* List of STA connected to the AP */
+            struct ecrnx_bcn bcn;       /* beacon */
+            int bcn_interval;
+            u8 bcmc_index;             /* Index of the BCMC sta to use */
+            struct ecrnx_csa *csa;
+
+            struct list_head mpath_list; /* List of Mesh Paths used on this interface */
+            struct list_head proxy_list; /* List of Proxies Information used on this interface */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+            enum nl80211_mesh_power_mode mesh_pm; /* mesh power save mode currently set in firmware */
+            enum nl80211_mesh_power_mode next_mesh_pm; /* mesh power save mode for next peer */
+#endif
+        } ap;
+        struct
+        {
+            struct ecrnx_vif *master;   /* pointer on master interface */
+            struct ecrnx_sta *sta_4a;
+        } ap_vlan;
+    };
+    uint64_t rx_pn[TID_MAX];
+};
+
+#define ECRNX_INVALID_VIF 0xFF
+#define ECRNX_VIF_TYPE(vif) (vif->wdev.iftype)
+
+/**
+ * Structure used to store information relative to PS mode.
+ *
+ * @active: True when the sta is in PS mode.
+ *          If false, other values should be ignored
+ * @pkt_ready: Number of packets buffered for the sta in drv's txq
+ *             (1 counter for Legacy PS and 1 for U-APSD)
+ * @sp_cnt: Number of packets that remain to be pushed in the service period.
+ *          0 means that no service period is in progress
+ *          (1 counter for Legacy PS and 1 for U-APSD)
+ */
+struct ecrnx_sta_ps {
+    bool active;
+    u16 pkt_ready[2];
+    u16 sp_cnt[2];
+};
+
+/**
+ * struct ecrnx_rx_rate_stats - Store statistics for RX rates
+ *
+ * @table: Table indicating how many frame has been receive which each
+ * rate index. Rate index is the same as the one used by RC algo for TX
+ * @size: Size of the table array
+ * @cpt: number of frames received
+ */
+struct ecrnx_rx_rate_stats {
+    int *table;
+    int size;
+    int cpt;
+    int rate_cnt;
+};
+
+/**
+ * struct ecrnx_sta_stats - Structure Used to store statistics specific to a STA
+ *
+ * @last_rx: Hardware vector of the last received frame
+ * @rx_rate: Statistics of the received rates
+ */
+struct ecrnx_sta_stats {
+    u32 rx_pkts;
+    u32 tx_pkts;
+    u64 rx_bytes;
+    u64 tx_bytes;
+    unsigned long last_act;
+    struct hw_vect last_rx;
+#ifdef CONFIG_ECRNX_DEBUGFS
+    struct ecrnx_rx_rate_stats rx_rate;
+#endif
+};
+
+/*
+ * Structure used to save information relative to the managed stations.
+ */
+struct ecrnx_sta {
+    struct list_head list;
+    bool valid;
+    u8 mac_addr[ETH_ALEN];
+    u16 aid;                /* association ID */
+    u8 sta_idx;             /* Identifier of the station */
+    u8 vif_idx;             /* Identifier of the VIF (fw id) the station
+                               belongs to */
+    u8 vlan_idx;            /* Identifier of the VLAN VIF (fw id) the station
+                               belongs to (= vif_idx if no vlan in used) */
+    enum nl80211_band band; /* Band */
+    enum nl80211_chan_width width; /* Channel width */
+    u16 center_freq;        /* Center frequency */
+    u32 center_freq1;       /* Center frequency 1 */
+    u32 center_freq2;       /* Center frequency 2 */
+    u8 ch_idx;              /* Identifier of the channel
+                               context the station belongs to */
+    bool qos;               /* Flag indicating if the station
+                               supports QoS */
+    u8 acm;                 /* Bitfield indicating which queues
+                               have AC mandatory */
+    u16 uapsd_tids;         /* Bitfield indicating which tids are subject to
+                               UAPSD */
+    struct ecrnx_key key;
+    struct ecrnx_sta_ps ps;  /* Information when STA is in PS (AP only) */
+#ifdef CONFIG_ECRNX_BFMER
+    struct ecrnx_bfmer_report *bfm_report;     /* Beamforming report to be used for
+                                                 VHT TX Beamforming */
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    struct ecrnx_sta_group_info group_info; /* MU grouping information for the STA */
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+#endif /* CONFIG_ECRNX_BFMER */
+
+    bool ht;               /* Flag indicating if the station
+                               supports HT */
+    bool vht;               /* Flag indicating if the station
+                               supports VHT */
+    u32 ac_param[AC_MAX];  /* EDCA parameters */
+    struct ecrnx_tdls tdls; /* TDLS station information */
+    struct ecrnx_sta_stats stats;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    enum nl80211_mesh_power_mode mesh_pm; /*  link-specific mesh power save mode */
+#endif
+    int listen_interval;
+    struct twt_setup_ind twt_ind; /*TWT Setup indication*/
+    uint64_t rx_pn[TID_MAX];
+    struct reord_cntrl reord_cntrl[TID_MAX];
+};
+
+#define ECRNX_INVALID_STA 0xFF
+static inline const u8 *ecrnx_sta_addr(struct ecrnx_sta *sta) {
+    return sta->mac_addr;
+}
+
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+struct ecrnx_amsdu_stats {
+    int done;
+    int failed;
+};
+#endif
+
+struct ecrnx_stats {
+    int cfm_balance[NX_TXQ_CNT];
+    unsigned long last_rx, last_tx; /* jiffies */
+    int ampdus_tx[IEEE80211_MAX_AMPDU_BUF];
+    int ampdus_rx[IEEE80211_MAX_AMPDU_BUF];
+    int ampdus_rx_map[4];
+    int ampdus_rx_miss;
+    int ampdus_rx_last;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+    struct ecrnx_amsdu_stats amsdus[NX_TX_PAYLOAD_MAX];
+#endif
+    int amsdus_rx[64];
+};
+
+/**
+ * struct ecrnx_roc - Remain On Channel information
+ *
+
+ Structure that will contains all RoC information received from cfg80211 */
+struct ecrnx_roc {
+    struct ecrnx_vif *vif;
+    struct ieee80211_channel *chan;
+    unsigned int duration;
+    /* Used to avoid call of CFG80211 callback upon expiration of RoC */
+    bool internal;
+    /* Indicate if we have switch on the RoC channel */
+    bool on_chan;
+};
+
+/* Structure containing channel survey information received from MAC */
+struct ecrnx_survey_info {
+    // Filled
+    u32 filled;
+    // Amount of time in ms the radio spent on the channel
+    u32 chan_time_ms;
+    // Amount of time the primary channel was sensed busy
+    u32 chan_time_busy_ms;
+    // Noise in dbm
+    s8 noise_dbm;
+};
+
+
+/* Structure containing channel context information */
+struct ecrnx_chanctx {
+    struct cfg80211_chan_def chan_def; /* channel description */
+    u8 count;                          /* number of vif using this ctxt */
+};
+
+#define ECRNX_CH_NOT_SET 0xFF
+/**
+ * ecrnx_phy_info - Phy information
+ *
+ * @phy_cnt: Number of phy interface
+ * @cfg: Configuration send to firmware
+ * @sec_chan: Channel configuration of the second phy interface (if phy_cnt > 1)
+ * @limit_bw: Set to true to limit BW on requested channel. Only set to use
+ * VHT with old radio that don't support 80MHz (deprecated)
+ */
+struct ecrnx_phy_info {
+    u8 cnt;
+    struct phy_cfg_tag cfg;
+    struct mac_chan_op sec_chan;
+    bool limit_bw;
+};
+
+struct ecrnx_hw {
+    struct device *dev;
+        // Hardware info
+
+#ifdef CONFIG_ECRNX_ESWIN
+    void *plat;
+#else
+    struct ecrnx_plat *plat;
+#endif
+
+    struct ecrnx_phy_info phy;
+    struct mm_version_cfm version_cfm;
+    int machw_type;
+    struct ecrnx_mod_params *mod_params;
+    unsigned long flags;
+    struct wiphy *wiphy;
+    u8 ext_capa[10];
+    struct list_head vifs;
+    struct ecrnx_vif *vif_table[NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX]; /* indexed with fw id */
+    u8 vif_started;
+    u8 avail_idx_map;
+    u8 monitor_vif;
+    struct ecrnx_sta sta_table[NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX];
+
+    // Channels
+    struct ecrnx_chanctx chanctx_table[NX_CHAN_CTXT_CNT];
+    u8 cur_chanctx;
+    struct ecrnx_survey_info survey[SCAN_CHANNEL_MAX];
+
+    /* RoC Management */
+    struct ecrnx_roc *roc;
+    u32 roc_cookie;
+    struct cfg80211_scan_request *scan_request;
+    spinlock_t scan_req_lock;
+    spinlock_t connect_req_lock;
+    struct ecrnx_radar radar;
+       
+#ifdef CONFIG_ECRNX_P2P
+       struct ecrnx_p2p_listen p2p_listen;
+#endif
+
+    // TX path
+    spinlock_t tx_lock;
+    struct ecrnx_txq txq[NX_NB_TXQ];
+    struct ecrnx_hwq hwq[NX_TXQ_CNT];
+    struct timer_list txq_cleanup;
+    struct kmem_cache *sw_txhdr_cache;
+    u32 tcp_pacing_shift;
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    struct ecrnx_mu_info mu;
+#endif
+
+    // RX path
+    struct ecrnx_defer_rx defer_rx;
+    spinlock_t rx_lock;
+
+    struct tasklet_struct task;
+
+
+    /* IPC */
+    struct ipc_host_env_tag *ipc_env;
+    struct ecrnx_cmd_mgr cmd_mgr;
+    spinlock_t cb_lock;
+    struct ecrnx_ipc_elem_pool e2amsgs_pool;
+    struct ecrnx_ipc_elem_pool dbgmsgs_pool;
+    struct ecrnx_ipc_elem_pool e2aradars_pool;
+    struct ecrnx_ipc_elem_var pattern_elem;
+    struct ecrnx_ipc_dbgdump_elem dbgdump_elem;
+    struct ecrnx_ipc_elem_pool e2arxdesc_pool;
+    struct ecrnx_ipc_skb_elem *e2aunsuprxvec_elems;
+    struct ecrnx_ipc_rxbuf_elems rxbuf_elems;
+    struct ecrnx_ipc_elem_var scan_ie;
+    
+#ifdef CONFIG_ECRNX_DEBUGFS
+    struct ecrnx_debugfs     debugfs;
+#endif
+
+    struct ecrnx_stats       stats;
+    struct ecrnx_conf_file   conf_param;
+
+#ifdef CONFIG_ECRNX_ESWIN
+        struct list_head agg_rx_list;
+        struct list_head defrag_rx_list;
+#endif
+#ifdef CONFIG_ESWIN_RX_REORDER
+    spinlock_t rx_msdu_free_lock;
+    struct list_head rx_msdu_free_list;
+    struct list_head rx_reord_list;
+    spinlock_t rx_reord_lock;
+    struct reord_msdu_info * rx_reord_buf;
+#endif
+
+    /* extended capabilities supported */
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+       struct list_head debugfs_survey_info_tbl_ptr;
+#endif
+    u32 msg_tx;
+    u32 msg_tx_done;
+    u32 data_tx;
+    u32 data_tx_done;
+    u32 usb_rx;
+    u32 msg_rx;
+    u32 data_rx;
+};
+
+u8 *ecrnx_build_bcn(struct ecrnx_bcn *bcn, struct cfg80211_beacon_data *new);
+
+void ecrnx_chanctx_link(struct ecrnx_vif *vif, u8 idx,
+                        struct cfg80211_chan_def *chandef);
+void ecrnx_chanctx_unlink(struct ecrnx_vif *vif);
+int  ecrnx_chanctx_valid(struct ecrnx_hw *ecrnx_hw, u8 idx);
+
+static inline bool is_multicast_sta(int sta_idx)
+{
+    return (sta_idx >= NX_REMOTE_STA_MAX);
+}
+struct ecrnx_sta *ecrnx_get_sta(struct ecrnx_hw *ecrnx_hw, const u8 *mac_addr);
+
+static inline uint8_t master_vif_idx(struct ecrnx_vif *vif)
+{
+    if (unlikely(vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN)) {
+        return vif->ap_vlan.master->vif_index;
+    } else {
+        return vif->vif_index;
+    }
+}
+
+static inline void *ecrnx_get_shared_trace_buf(struct ecrnx_hw *ecrnx_hw)
+{
+#ifdef CONFIG_ECRNX_DEBUGFS
+    return (void *)&(ecrnx_hw->debugfs.fw_trace.buf);
+#else
+    return NULL;
+#endif
+}
+
+void ecrnx_external_auth_enable(struct ecrnx_vif *vif);
+void ecrnx_external_auth_disable(struct ecrnx_vif *vif);
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+/* 802.11ax HE MAC capabilities */
+#define IEEE80211_HE_MAC_CAP0_HTC_HE                           0x01
+#define IEEE80211_HE_MAC_CAP0_TWT_REQ                          0x02
+#define IEEE80211_HE_MAC_CAP0_TWT_RES                          0x04
+#define IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_NOT_SUPP            0x00
+#define IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_LEVEL_1             0x08
+#define IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_LEVEL_2             0x10
+#define IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_LEVEL_3             0x18
+#define IEEE80211_HE_MAC_CAP0_DYNAMIC_FRAG_MASK                        0x18
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_1              0x00
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_2              0x20
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_4              0x40
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_8              0x60
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_16             0x80
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_32             0xa0
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_64             0xc0
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_UNLIMITED      0xe0
+#define IEEE80211_HE_MAC_CAP0_MAX_NUM_FRAG_MSDU_MASK           0xe0
+
+#define IEEE80211_HE_MAC_CAP1_MIN_FRAG_SIZE_UNLIMITED          0x00
+#define IEEE80211_HE_MAC_CAP1_MIN_FRAG_SIZE_128                        0x01
+#define IEEE80211_HE_MAC_CAP1_MIN_FRAG_SIZE_256                        0x02
+#define IEEE80211_HE_MAC_CAP1_MIN_FRAG_SIZE_512                        0x03
+#define IEEE80211_HE_MAC_CAP1_MIN_FRAG_SIZE_MASK               0x03
+#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_0US               0x00
+#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_8US               0x04
+#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US              0x08
+#define IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_MASK              0x0c
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_1           0x00
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_2           0x10
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_3           0x20
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_4           0x30
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_5           0x40
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_6           0x50
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_7           0x60
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8           0x70
+#define IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_MASK                0x70
+
+/* Link adaptation is split between byte HE_MAC_CAP1 and
+ * HE_MAC_CAP2. It should be set only if IEEE80211_HE_MAC_CAP0_HTC_HE
+ * in which case the following values apply:
+ * 0 = No feedback.
+ * 1 = reserved.
+ * 2 = Unsolicited feedback.
+ * 3 = both
+ */
+#define IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION                  0x80
+
+#define IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION                  0x01
+#define IEEE80211_HE_MAC_CAP2_ALL_ACK                          0x02
+#define IEEE80211_HE_MAC_CAP2_TRS                              0x04
+#define IEEE80211_HE_MAC_CAP2_BSR                              0x08
+#define IEEE80211_HE_MAC_CAP2_BCAST_TWT                                0x10
+#define IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP                  0x20
+#define IEEE80211_HE_MAC_CAP2_MU_CASCADING                     0x40
+#define IEEE80211_HE_MAC_CAP2_ACK_EN                           0x80
+
+#define IEEE80211_HE_MAC_CAP3_OMI_CONTROL                      0x02
+#define IEEE80211_HE_MAC_CAP3_OFDMA_RA                         0x04
+
+/* The maximum length of an A-MDPU is defined by the combination of the Maximum
+ * A-MDPU Length Exponent field in the HT capabilities, VHT capabilities and the
+ * same field in the HE capabilities.
+ */
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_USE_VHT        0x00
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_1          0x08
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_VHT_2          0x10
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_RESERVED       0x18
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK           0x18
+#define IEEE80211_HE_MAC_CAP3_AMSDU_FRAG                       0x20
+#define IEEE80211_HE_MAC_CAP3_FLEX_TWT_SCHED                   0x40
+#define IEEE80211_HE_MAC_CAP3_RX_CTRL_FRAME_TO_MULTIBSS                0x80
+
+#define IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_SHIFT          3
+
+#define IEEE80211_HE_MAC_CAP4_BSRP_BQRP_A_MPDU_AGG             0x01
+#define IEEE80211_HE_MAC_CAP4_QTP                              0x02
+#define IEEE80211_HE_MAC_CAP4_BQR                              0x04
+#define IEEE80211_HE_MAC_CAP4_SRP_RESP                         0x08
+#define IEEE80211_HE_MAC_CAP4_NDP_FB_REP                       0x10
+#define IEEE80211_HE_MAC_CAP4_OPS                              0x20
+#define IEEE80211_HE_MAC_CAP4_AMDSU_IN_AMPDU                   0x40
+/* Multi TID agg TX is split between byte #4 and #5
+ * The value is a combination of B39,B40,B41
+ */
+#define IEEE80211_HE_MAC_CAP4_MULTI_TID_AGG_TX_QOS_B39         0x80
+
+#define IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B40         0x01
+#define IEEE80211_HE_MAC_CAP5_MULTI_TID_AGG_TX_QOS_B41         0x02
+#define IEEE80211_HE_MAC_CAP5_SUBCHAN_SELECVITE_TRANSMISSION   0x04
+#define IEEE80211_HE_MAC_CAP5_UL_2x996_TONE_RU                 0x08
+#define IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX                0x10
+#define IEEE80211_HE_MAC_CAP5_HE_DYNAMIC_SM_PS                 0x20
+#define IEEE80211_HE_MAC_CAP5_PUNCTURED_SOUNDING               0x40
+#define IEEE80211_HE_MAC_CAP5_HT_VHT_TRIG_FRAME_RX             0x80
+
+#define IEEE80211_HE_VHT_MAX_AMPDU_FACTOR      20
+#define IEEE80211_HE_HT_MAX_AMPDU_FACTOR       16
+
+/* 802.11ax HE PHY capabilities */
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G            0x02
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G      0x04
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G           0x08
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G     0x10
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G       0x20
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G       0x40
+#define IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK                   0xfe
+
+#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_80MHZ_ONLY_SECOND_20MHZ 0x01
+#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_80MHZ_ONLY_SECOND_40MHZ 0x02
+#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_160MHZ_ONLY_SECOND_20MHZ        0x04
+#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_160MHZ_ONLY_SECOND_40MHZ        0x08
+#define IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK                    0x0f
+#define IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A                           0x10
+#define IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD                   0x20
+#define IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US         0x40
+/* Midamble RX/TX Max NSTS is split between byte #2 and byte #3 */
+#define IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS                  0x80
+
+#define IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS                  0x01
+#define IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US                     0x02
+#define IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ                      0x04
+#define IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ                      0x08
+#define IEEE80211_HE_PHY_CAP2_DOPPLER_TX                               0x10
+#define IEEE80211_HE_PHY_CAP2_DOPPLER_RX                               0x20
+
+/* Note that the meaning of UL MU below is different between an AP and a non-AP
+ * sta, where in the AP case it indicates support for Rx and in the non-AP sta
+ * case it indicates support for Tx.
+ */
+#define IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO                       0x40
+#define IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO                    0x80
+
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_NO_DCM                  0x00
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_BPSK                    0x01
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK                    0x02
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_16_QAM                  0x03
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK                    0x03
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_1                         0x00
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_TX_NSS_2                         0x04
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_NO_DCM                  0x00
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_BPSK                    0x08
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK                    0x10
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_16_QAM                  0x18
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK                    0x18
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_1                         0x00
+#define IEEE80211_HE_PHY_CAP3_DCM_MAX_RX_NSS_2                         0x20
+#define IEEE80211_HE_PHY_CAP3_RX_HE_MU_PPDU_FROM_NON_AP_STA            0x40
+#define IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER                            0x80
+
+#define IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE                            0x01
+#define IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER                            0x02
+
+/* Minimal allowed value of Max STS under 80MHz is 3 */
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4         0x0c
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_5         0x10
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_6         0x14
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_7         0x18
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_8         0x1c
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK      0x1c
+
+/* Minimal allowed value of Max STS above 80MHz is 3 */
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4         0x60
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_5         0x80
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_6         0xa0
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_7         0xc0
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_8         0xe0
+#define IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK      0xe0
+
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_1     0x00
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2     0x01
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_3     0x02
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_4     0x03
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_5     0x04
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_6     0x05
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_7     0x06
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_8     0x07
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK  0x07
+
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_1     0x00
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_2     0x08
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_3     0x10
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_4     0x18
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_5     0x20
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_6     0x28
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_7     0x30
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_8     0x38
+#define IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK  0x38
+
+#define IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK                         0x40
+#define IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK                         0x80
+
+#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU                      0x01
+#define IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU                      0x02
+#define IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB                    0x04
+#define IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB                    0x08
+#define IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB                              0x10
+#define IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE                     0x20
+#define IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO              0x40
+#define IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT                    0x80
+
+#define IEEE80211_HE_PHY_CAP7_SRP_BASED_SR                             0x01
+#define IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_AR                    0x02
+#define IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI         0x04
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_1                                 0x08
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_2                                 0x10
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_3                                 0x18
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_4                                 0x20
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_5                                 0x28
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_6                                 0x30
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_7                                 0x38
+#define IEEE80211_HE_PHY_CAP7_MAX_NC_MASK                              0x38
+#define IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ                      0x40
+#define IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ                      0x80
+
+#define IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI         0x01
+#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G             0x02
+#define IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU                  0x04
+#define IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU                  0x08
+#define IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI              0x10
+#define IEEE80211_HE_PHY_CAP8_MIDAMBLE_RX_TX_2X_AND_1XLTF              0x20
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_242                           0x00
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484                           0x40
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_996                           0x80
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_2x996                         0xc0
+#define IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_MASK                          0xc0
+
+#define IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM             0x01
+#define IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK               0x02
+#define IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU                0x04
+#define IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU                0x08
+
+/* 802.11ax HE TX/RX MCS NSS Support  */
+#define IEEE80211_TX_RX_MCS_NSS_SUPP_HIGHEST_MCS_POS                   (3)
+#define IEEE80211_TX_RX_MCS_NSS_SUPP_TX_BITMAP_POS                     (6)
+#define IEEE80211_TX_RX_MCS_NSS_SUPP_RX_BITMAP_POS                     (11)
+#define IEEE80211_TX_RX_MCS_NSS_SUPP_TX_BITMAP_MASK                    0x07c0
+#define IEEE80211_TX_RX_MCS_NSS_SUPP_RX_BITMAP_MASK                    0xf800
+
+
+/* TX/RX HE MCS Support field Highest MCS subfield encoding */
+enum ieee80211_he_highest_mcs_supported_subfield_enc {
+       HIGHEST_MCS_SUPPORTED_MCS7 = 0,
+       HIGHEST_MCS_SUPPORTED_MCS8,
+       HIGHEST_MCS_SUPPORTED_MCS9,
+       HIGHEST_MCS_SUPPORTED_MCS10,
+       HIGHEST_MCS_SUPPORTED_MCS11,
+};
+
+#define IEEE80211_HE_PPE_THRES_MAX_LEN         25
+
+/**
+ * struct ieee80211_he_mcs_nss_supp - HE Tx/Rx HE MCS NSS Support Field
+ *
+ * This structure holds the data required for the Tx/Rx HE MCS NSS Support Field
+ * described in P802.11ax_D2.0 section 9.4.2.237.4
+ *
+ * @rx_mcs_80: Rx MCS map 2 bits for each stream, total 8 streams, for channel
+ *     widths less than 80MHz.
+ * @tx_mcs_80: Tx MCS map 2 bits for each stream, total 8 streams, for channel
+ *     widths less than 80MHz.
+ * @rx_mcs_160: Rx MCS map 2 bits for each stream, total 8 streams, for channel
+ *     width 160MHz.
+ * @tx_mcs_160: Tx MCS map 2 bits for each stream, total 8 streams, for channel
+ *     width 160MHz.
+ * @rx_mcs_80p80: Rx MCS map 2 bits for each stream, total 8 streams, for
+ *     channel width 80p80MHz.
+ * @tx_mcs_80p80: Tx MCS map 2 bits for each stream, total 8 streams, for
+ *     channel width 80p80MHz.
+ */
+struct ieee80211_he_mcs_nss_supp {
+       __le16 rx_mcs_80;
+       __le16 tx_mcs_80;
+       __le16 rx_mcs_160;
+       __le16 tx_mcs_160;
+       __le16 rx_mcs_80p80;
+       __le16 tx_mcs_80p80;
+} __packed;
+
+/**
+ * struct ieee80211_he_cap_elem - HE capabilities element
+ *
+ * This structure is the "HE capabilities element" fixed fields as
+ * described in P802.11ax_D2.0 section 9.4.2.237.2 and 9.4.2.237.3
+ */
+struct ieee80211_he_cap_elem {
+       u8 mac_cap_info[6];
+       u8 phy_cap_info[11];
+} __packed;
+
+/**
+ * struct ieee80211_sta_he_cap - STA's HE capabilities
+ *
+ * This structure describes most essential parameters needed
+ * to describe 802.11ax HE capabilities for a STA.
+ *
+ * @has_he: true iff HE data is valid.
+ * @he_cap_elem: Fixed portion of the HE capabilities element.
+ * @he_mcs_nss_supp: The supported NSS/MCS combinations.
+ * @ppe_thres: Holds the PPE Thresholds data.
+ */
+struct ieee80211_sta_he_cap {
+       bool has_he;
+       struct ieee80211_he_cap_elem he_cap_elem;
+       struct ieee80211_he_mcs_nss_supp he_mcs_nss_supp;
+       u8 ppe_thres[IEEE80211_HE_PPE_THRES_MAX_LEN];
+};
+
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB    0x10
+#define IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB        0x20
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_0US                  0x00
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_8US                  0x40
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_16US                 0x80
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_RESERVED             0xc0
+#define IEEE80211_HE_PHY_CAP9_NOMIMAL_PKT_PADDING_MASK                 0xc0
+#endif
+
+
+#if defined(CONFIG_ECRNX_HE)
+extern struct ieee80211_sta_he_cap ecrnx_he_cap;
+#endif
+
+#define RW_DRV_DESCRIPTION  "ESWIN 11nac driver for Linux cfg80211"
+#define RW_DRV_COPYRIGHT    "Copyright(c) 2015-2017 ESWIN"
+#define RW_DRV_AUTHOR       "ESWIN S.A.S"
+
+#define ECRNX_PRINT_CFM_ERR(req) \
+        printk(KERN_CRIT "%s: Status Error(%d)\n", #req, (&req##_cfm)->status)
+
+#define ECRNX_HT_CAPABILITIES                                    \
+{                                                               \
+    .ht_supported   = true,                                     \
+    .cap            = 0,                                        \
+    .ampdu_factor   = IEEE80211_HT_MAX_AMPDU_64K,               \
+    .ampdu_density  = IEEE80211_HT_MPDU_DENSITY_16,             \
+    .mcs        = {                                             \
+        .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, },        \
+        .rx_highest = cpu_to_le16(65),                          \
+        .tx_params = IEEE80211_HT_MCS_TX_DEFINED,               \
+    },                                                          \
+}
+
+#define ECRNX_VHT_CAPABILITIES                                   \
+{                                                               \
+    .vht_supported = false,                                     \
+    .cap       =                                                \
+      (7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT),\
+    .vht_mcs       = {                                          \
+        .rx_mcs_map = cpu_to_le16(                              \
+                      IEEE80211_VHT_MCS_SUPPORT_0_9    << 0  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 2  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 4  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 6  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 8  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 10 |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 12 |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 14),  \
+        .tx_mcs_map = cpu_to_le16(                              \
+                      IEEE80211_VHT_MCS_SUPPORT_0_9    << 0  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 2  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 4  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 6  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 8  |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 10 |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 12 |  \
+                      IEEE80211_VHT_MCS_NOT_SUPPORTED  << 14),  \
+    }                                                           \
+}
+
+#if CONFIG_ECRNX_HE
+#define ECRNX_HE_CAPABILITIES                                    \
+{                                                               \
+    .has_he = false,                                            \
+    .he_cap_elem = {                                            \
+        .mac_cap_info[0] = 0,                                   \
+        .mac_cap_info[1] = 0,                                   \
+        .mac_cap_info[2] = 0,                                   \
+        .mac_cap_info[3] = 0,                                   \
+        .mac_cap_info[4] = 0,                                   \
+        .mac_cap_info[5] = 0,                                   \
+        .phy_cap_info[0] = 0,                                   \
+        .phy_cap_info[1] = 0,                                   \
+        .phy_cap_info[2] = 0,                                   \
+        .phy_cap_info[3] = 0,                                   \
+        .phy_cap_info[4] = 0,                                   \
+        .phy_cap_info[5] = 0,                                   \
+        .phy_cap_info[6] = 0,                                   \
+        .phy_cap_info[7] = 0,                                   \
+        .phy_cap_info[8] = 0,                                   \
+        .phy_cap_info[9] = 0,                                   \
+        .phy_cap_info[10] = 0,                                  \
+    },                                                          \
+    .he_mcs_nss_supp = {                                        \
+        .rx_mcs_80 = cpu_to_le16(0xfffa),                       \
+        .tx_mcs_80 = cpu_to_le16(0xfffa),                       \
+        .rx_mcs_160 = cpu_to_le16(0xffff),                      \
+        .tx_mcs_160 = cpu_to_le16(0xffff),                      \
+        .rx_mcs_80p80 = cpu_to_le16(0xffff),                    \
+        .tx_mcs_80p80 = cpu_to_le16(0xffff),                    \
+    },                                                          \
+    .ppe_thres = {0x00},                                        \
+}
+#endif
+
+#define RATE(_bitrate, _hw_rate, _flags) {      \
+    .bitrate    = (_bitrate),                   \
+    .flags      = (_flags),                     \
+    .hw_value   = (_hw_rate),                   \
+}
+
+#define CHAN(_freq) {                           \
+    .center_freq    = (_freq),                  \
+    .max_power  = 30, /* FIXME */               \
+}
+
+#endif /* _ECRNX_DEFS_H_*/
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_main.c b/drivers/net/wireless/eswin/fullmac/ecrnx_main.c
new file mode 100644 (file)
index 0000000..f3a87f3
--- /dev/null
@@ -0,0 +1,4467 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_main.c
+ *
+ * @brief Entry point of the ECRNX driver
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/inetdevice.h>
+#include <net/cfg80211.h>
+#include <net/ip.h>
+#include <linux/etherdevice.h>
+
+#include "ecrnx_defs.h"
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
+#include <linux/if_arp.h>
+#include <linux/ieee80211.h>
+#endif
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_tx.h"
+#include "reg_access.h"
+#include "hal_desc.h"
+#include "ecrnx_debugfs.h"
+#include "ecrnx_cfgfile.h"
+#include "ecrnx_radar.h"
+#include "ecrnx_version.h"
+#ifdef CONFIG_ECRNX_BFMER
+#include "ecrnx_bfmer.h"
+#endif //(CONFIG_ECRNX_BFMER)
+#include "ecrnx_tdls.h"
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+#include "ecrnx_rx.h"
+
+#include "ecrnx_p2p.h"
+#include "ecrnx_debugfs_custom.h"
+#include "ecrnx_calibration_data.h"
+#include "eswin_utils.h"
+#include "ecrnx_debugfs_func.h"
+
+
+static struct ieee80211_rate ecrnx_ratetable[] = {
+    RATE(10,  0x00, 0),
+    RATE(20,  0x01, IEEE80211_RATE_SHORT_PREAMBLE),
+    RATE(55,  0x02, IEEE80211_RATE_SHORT_PREAMBLE),
+    RATE(110, 0x03, IEEE80211_RATE_SHORT_PREAMBLE),
+    RATE(60,  0x04, 0),
+    RATE(90,  0x05, 0),
+    RATE(120, 0x06, 0),
+    RATE(180, 0x07, 0),
+    RATE(240, 0x08, 0),
+    RATE(360, 0x09, 0),
+    RATE(480, 0x0A, 0),
+    RATE(540, 0x0B, 0),
+};
+
+/* The channels indexes here are not used anymore */
+static struct ieee80211_channel ecrnx_2ghz_channels[] = {
+    CHAN(2412),
+    CHAN(2417),
+    CHAN(2422),
+    CHAN(2427),
+    CHAN(2432),
+    CHAN(2437),
+    CHAN(2442),
+    CHAN(2447),
+    CHAN(2452),
+    CHAN(2457),
+    CHAN(2462),
+    CHAN(2467),
+    CHAN(2472),
+    CHAN(2484),
+    // Extra channels defined only to be used for PHY measures.
+    // Enabled only if custregd and custchan parameters are set
+    CHAN(2390),
+    CHAN(2400),
+    CHAN(2410),
+    CHAN(2420),
+    CHAN(2430),
+    CHAN(2440),
+    CHAN(2450),
+    CHAN(2460),
+    CHAN(2470),
+    CHAN(2480),
+    CHAN(2490),
+    CHAN(2500),
+    CHAN(2510),
+};
+
+#ifdef CONFIG_ECRNX_5G
+static struct ieee80211_channel ecrnx_5ghz_channels[] = {
+    CHAN(5180),             // 36 -   20MHz
+    CHAN(5200),             // 40 -   20MHz
+    CHAN(5220),             // 44 -   20MHz
+    CHAN(5240),             // 48 -   20MHz
+    CHAN(5260),             // 52 -   20MHz
+    CHAN(5280),             // 56 -   20MHz
+    CHAN(5300),             // 60 -   20MHz
+    CHAN(5320),             // 64 -   20MHz
+    CHAN(5500),             // 100 -  20MHz
+    CHAN(5520),             // 104 -  20MHz
+    CHAN(5540),             // 108 -  20MHz
+    CHAN(5560),             // 112 -  20MHz
+    CHAN(5580),             // 116 -  20MHz
+    CHAN(5600),             // 120 -  20MHz
+    CHAN(5620),             // 124 -  20MHz
+    CHAN(5640),             // 128 -  20MHz
+    CHAN(5660),             // 132 -  20MHz
+    CHAN(5680),             // 136 -  20MHz
+    CHAN(5700),             // 140 -  20MHz
+    CHAN(5720),             // 144 -  20MHz
+    CHAN(5745),             // 149 -  20MHz
+    CHAN(5765),             // 153 -  20MHz
+    CHAN(5785),             // 157 -  20MHz
+    CHAN(5805),             // 161 -  20MHz
+    CHAN(5825),             // 165 -  20MHz
+    // Extra channels defined only to be used for PHY measures.
+    // Enabled only if custregd and custchan parameters are set
+    CHAN(5190),
+    CHAN(5210),
+    CHAN(5230),
+    CHAN(5250),
+    CHAN(5270),
+    CHAN(5290),
+    CHAN(5310),
+    CHAN(5330),
+    CHAN(5340),
+    CHAN(5350),
+    CHAN(5360),
+    CHAN(5370),
+    CHAN(5380),
+    CHAN(5390),
+    CHAN(5400),
+    CHAN(5410),
+    CHAN(5420),
+    CHAN(5430),
+    CHAN(5440),
+    CHAN(5450),
+    CHAN(5460),
+    CHAN(5470),
+    CHAN(5480),
+    CHAN(5490),
+    CHAN(5510),
+    CHAN(5530),
+    CHAN(5550),
+    CHAN(5570),
+    CHAN(5590),
+    CHAN(5610),
+    CHAN(5630),
+    CHAN(5650),
+    CHAN(5670),
+    CHAN(5690),
+    CHAN(5710),
+    CHAN(5730),
+    CHAN(5750),
+    CHAN(5760),
+    CHAN(5770),
+    CHAN(5780),
+    CHAN(5790),
+    CHAN(5800),
+    CHAN(5810),
+    CHAN(5820),
+    CHAN(5830),
+    CHAN(5840),
+    CHAN(5850),
+    CHAN(5860),
+    CHAN(5870),
+    CHAN(5880),
+    CHAN(5890),
+    CHAN(5900),
+    CHAN(5910),
+    CHAN(5920),
+    CHAN(5930),
+    CHAN(5940),
+    CHAN(5950),
+    CHAN(5960),
+    CHAN(5970),
+};
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+#if CONFIG_ECRNX_HE
+static struct ieee80211_sband_iftype_data ecrnx_he_capa = {
+    .types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP),
+    .he_cap = ECRNX_HE_CAPABILITIES,
+};
+#endif
+#endif
+
+static struct ieee80211_supported_band ecrnx_band_2GHz = {
+    .channels   = ecrnx_2ghz_channels,
+    .n_channels = ARRAY_SIZE(ecrnx_2ghz_channels) - 13, // -13 to exclude extra channels
+    .bitrates   = ecrnx_ratetable,
+    .n_bitrates = ARRAY_SIZE(ecrnx_ratetable),
+    .ht_cap     = ECRNX_HT_CAPABILITIES,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+#if CONFIG_ECRNX_HE
+    .iftype_data = &ecrnx_he_capa,
+    .n_iftype_data = 1,
+#endif
+#endif
+};
+
+#ifdef CONFIG_ECRNX_5G
+static struct ieee80211_supported_band ecrnx_band_5GHz = {
+    .channels   = ecrnx_5ghz_channels,
+    .n_channels = ARRAY_SIZE(ecrnx_5ghz_channels) - 59, // -59 to exclude extra channels
+    .bitrates   = &ecrnx_ratetable[4],
+    .n_bitrates = ARRAY_SIZE(ecrnx_ratetable) - 4,
+    .ht_cap     = ECRNX_HT_CAPABILITIES,
+    .vht_cap    = ECRNX_VHT_CAPABILITIES,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
+#if CONFIG_ECRNX_HE
+    .iftype_data = &ecrnx_he_capa,
+    .n_iftype_data = 1,
+#endif
+#endif
+};
+#endif
+
+static struct ieee80211_iface_limit ecrnx_limits[] = {
+    { .max = NX_VIRT_DEV_MAX, .types = BIT(NL80211_IFTYPE_AP) |
+                                       BIT(NL80211_IFTYPE_STATION)}
+};
+
+static struct ieee80211_iface_limit ecrnx_limits_dfs[] = {
+    { .max = NX_VIRT_DEV_MAX, .types = BIT(NL80211_IFTYPE_AP)}
+};
+
+static const struct ieee80211_iface_combination ecrnx_combinations[] = {
+    {
+        .limits                 = ecrnx_limits,
+        .n_limits               = ARRAY_SIZE(ecrnx_limits),
+        .num_different_channels = NX_CHAN_CTXT_CNT,
+        .max_interfaces         = NX_VIRT_DEV_MAX,
+    },
+    /* Keep this combination as the last one */
+    {
+        .limits                 = ecrnx_limits_dfs,
+        .n_limits               = ARRAY_SIZE(ecrnx_limits_dfs),
+        .num_different_channels = 1,
+        .max_interfaces         = NX_VIRT_DEV_MAX,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+        .radar_detect_widths = (BIT(NL80211_CHAN_WIDTH_20_NOHT) |
+                                BIT(NL80211_CHAN_WIDTH_20) |
+                                BIT(NL80211_CHAN_WIDTH_40) |
+                                BIT(NL80211_CHAN_WIDTH_80)),
+#endif
+    }
+};
+
+/* There isn't a lot of sense in it, but you can transmit anything you like */
+static struct ieee80211_txrx_stypes
+ecrnx_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
+    [NL80211_IFTYPE_STATION] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+               BIT(IEEE80211_STYPE_AUTH >> 4)),
+    },
+    [NL80211_IFTYPE_AP] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+               BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+               BIT(IEEE80211_STYPE_AUTH >> 4) |
+               BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+               BIT(IEEE80211_STYPE_ACTION >> 4)),
+    },
+    [NL80211_IFTYPE_AP_VLAN] = {
+        /* copy AP */
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+               BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+               BIT(IEEE80211_STYPE_AUTH >> 4) |
+               BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+               BIT(IEEE80211_STYPE_ACTION >> 4)),
+    },
+    [NL80211_IFTYPE_P2P_CLIENT] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)),
+    },
+    [NL80211_IFTYPE_P2P_GO] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
+               BIT(IEEE80211_STYPE_DISASSOC >> 4) |
+               BIT(IEEE80211_STYPE_AUTH >> 4) |
+               BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+               BIT(IEEE80211_STYPE_ACTION >> 4)),
+    },
+    [NL80211_IFTYPE_P2P_DEVICE] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_PROBE_REQ >> 4)),
+    },
+    [NL80211_IFTYPE_MESH_POINT] = {
+        .tx = 0xffff,
+        .rx = (BIT(IEEE80211_STYPE_ACTION >> 4) |
+               BIT(IEEE80211_STYPE_AUTH >> 4) |
+               BIT(IEEE80211_STYPE_DEAUTH >> 4)),
+    },
+};
+
+
+static u32 cipher_suites[] = {
+    WLAN_CIPHER_SUITE_WEP40,
+    WLAN_CIPHER_SUITE_WEP104,
+    WLAN_CIPHER_SUITE_TKIP,
+    WLAN_CIPHER_SUITE_CCMP,
+    0, // reserved entries to enable AES-CMAC and/or SMS4
+    0,
+    0,
+    0,
+    0,
+};
+#define NB_RESERVED_CIPHER 5;
+
+static const int ecrnx_ac2hwq[1][NL80211_NUM_ACS] = {
+    {
+        [NL80211_TXQ_Q_VO] = ECRNX_HWQ_VO,
+        [NL80211_TXQ_Q_VI] = ECRNX_HWQ_VI,
+        [NL80211_TXQ_Q_BE] = ECRNX_HWQ_BE,
+        [NL80211_TXQ_Q_BK] = ECRNX_HWQ_BK
+    }
+};
+
+const int ecrnx_tid2hwq[IEEE80211_NUM_TIDS] = {
+    ECRNX_HWQ_BE,
+    ECRNX_HWQ_BK,
+    ECRNX_HWQ_BK,
+    ECRNX_HWQ_BE,
+    ECRNX_HWQ_VI,
+    ECRNX_HWQ_VI,
+    ECRNX_HWQ_VO,
+    ECRNX_HWQ_VO,
+    /* TID_8 is used for management frames */
+    ECRNX_HWQ_VO,
+    /* At the moment, all others TID are mapped to BE */
+    ECRNX_HWQ_BE,
+    ECRNX_HWQ_BE,
+    ECRNX_HWQ_BE,
+    ECRNX_HWQ_BE,
+    ECRNX_HWQ_BE,
+    ECRNX_HWQ_BE,
+    ECRNX_HWQ_BE,
+};
+
+static const int ecrnx_hwq2uapsd[NL80211_NUM_ACS] = {
+    [ECRNX_HWQ_VO] = IEEE80211_WMM_IE_STA_QOSINFO_AC_VO,
+    [ECRNX_HWQ_VI] = IEEE80211_WMM_IE_STA_QOSINFO_AC_VI,
+    [ECRNX_HWQ_BE] = IEEE80211_WMM_IE_STA_QOSINFO_AC_BE,
+    [ECRNX_HWQ_BK] = IEEE80211_WMM_IE_STA_QOSINFO_AC_BK,
+};
+
+/*********************************************************************
+ * helper
+ *********************************************************************/
+struct ecrnx_sta *ecrnx_get_sta(struct ecrnx_hw *ecrnx_hw, const u8 *mac_addr)
+{
+    int i;
+
+    for (i = 0; i < NX_REMOTE_STA_MAX; i++) {
+        struct ecrnx_sta *sta = &ecrnx_hw->sta_table[i];
+        if (sta->valid && (memcmp(mac_addr, &sta->mac_addr, 6) == 0))
+            return sta;
+    }
+
+    return NULL;
+}
+
+void ecrnx_enable_wapi(struct ecrnx_hw *ecrnx_hw)
+{
+    cipher_suites[ecrnx_hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_SMS4;
+    ecrnx_hw->wiphy->n_cipher_suites ++;
+    ecrnx_hw->wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
+}
+
+void ecrnx_enable_mfp(struct ecrnx_hw *ecrnx_hw)
+{
+    cipher_suites[ecrnx_hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_AES_CMAC;
+    ecrnx_hw->wiphy->n_cipher_suites ++;
+}
+
+void ecrnx_enable_gcmp(struct ecrnx_hw *ecrnx_hw)
+{
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
+    cipher_suites[ecrnx_hw->wiphy->n_cipher_suites++] = WLAN_CIPHER_SUITE_CCMP_256;
+    cipher_suites[ecrnx_hw->wiphy->n_cipher_suites++] = WLAN_CIPHER_SUITE_GCMP;
+    cipher_suites[ecrnx_hw->wiphy->n_cipher_suites++] = WLAN_CIPHER_SUITE_GCMP_256;
+#endif
+}
+u8 *ecrnx_build_bcn(struct ecrnx_bcn *bcn, struct cfg80211_beacon_data *new)
+{
+    u8 *buf, *pos;
+
+    if (new->head) {
+        u8 *head = kmalloc(new->head_len, GFP_KERNEL);
+
+        if (!head)
+            return NULL;
+
+        if (bcn->head)
+            kfree(bcn->head);
+
+        bcn->head = head;
+        bcn->head_len = new->head_len;
+        memcpy(bcn->head, new->head, new->head_len);
+    }
+    if (new->tail) {
+        u8 *tail = kmalloc(new->tail_len, GFP_KERNEL);
+
+        if (!tail)
+            return NULL;
+
+        if (bcn->tail)
+            kfree(bcn->tail);
+
+        bcn->tail = tail;
+        bcn->tail_len = new->tail_len;
+        memcpy(bcn->tail, new->tail, new->tail_len);
+    }
+
+    if (!bcn->head)
+        return NULL;
+
+    bcn->tim_len = 6;
+    bcn->len = bcn->head_len + bcn->tail_len + bcn->ies_len + bcn->tim_len;
+
+    buf = kmalloc(bcn->len, GFP_KERNEL);
+    if (!buf)
+        return NULL;
+
+    // Build the beacon buffer
+    pos = buf;
+    memcpy(pos, bcn->head, bcn->head_len);
+    pos += bcn->head_len;
+    *pos++ = WLAN_EID_TIM;
+    *pos++ = 4;
+    *pos++ = 0;
+    *pos++ = bcn->dtim;
+    *pos++ = 0;
+    *pos++ = 0;
+    if (bcn->tail) {
+        memcpy(pos, bcn->tail, bcn->tail_len);
+        pos += bcn->tail_len;
+    }
+    if (bcn->ies) {
+        memcpy(pos, bcn->ies, bcn->ies_len);
+    }
+
+    return buf;
+}
+
+
+static void ecrnx_del_bcn(struct ecrnx_bcn *bcn)
+{
+    if (bcn->head) {
+        kfree(bcn->head);
+        bcn->head = NULL;
+    }
+    bcn->head_len = 0;
+
+    if (bcn->tail) {
+        kfree(bcn->tail);
+        bcn->tail = NULL;
+    }
+    bcn->tail_len = 0;
+
+    if (bcn->ies) {
+        kfree(bcn->ies);
+        bcn->ies = NULL;
+    }
+    bcn->ies_len = 0;
+    bcn->tim_len = 0;
+    bcn->dtim = 0;
+    bcn->len = 0;
+}
+
+/**
+ * Link channel ctxt to a vif and thus increments count for this context.
+ */
+void ecrnx_chanctx_link(struct ecrnx_vif *vif, u8 ch_idx,
+                       struct cfg80211_chan_def *chandef)
+{
+    struct ecrnx_chanctx *ctxt;
+
+    if (ch_idx >= NX_CHAN_CTXT_CNT) {
+        WARN(1, "Invalid channel ctxt id %d", ch_idx);
+        return;
+    }
+
+    vif->ch_index = ch_idx;
+    ctxt = &vif->ecrnx_hw->chanctx_table[ch_idx];
+    ctxt->count++;
+
+    // For now chandef is NULL for STATION interface
+    if (chandef) {
+        if (!ctxt->chan_def.chan)
+            ctxt->chan_def = *chandef;
+        else {
+            // TODO. check that chandef is the same as the one already
+            // set for this ctxt
+        }
+    }
+}
+
+/**
+ * Unlink channel ctxt from a vif and thus decrements count for this context
+ */
+void ecrnx_chanctx_unlink(struct ecrnx_vif *vif)
+{
+    struct ecrnx_chanctx *ctxt;
+
+    if (vif->ch_index == ECRNX_CH_NOT_SET)
+        return;
+
+    ctxt = &vif->ecrnx_hw->chanctx_table[vif->ch_index];
+
+    if (ctxt->count == 0) {
+        WARN(1, "Chan ctxt ref count is already 0");
+    } else {
+        ctxt->count--;
+    }
+
+    if (ctxt->count == 0) {
+        if (vif->ch_index == vif->ecrnx_hw->cur_chanctx) {
+            /* If current chan ctxt is no longer linked to a vif
+               disable radar detection (no need to check if it was activated) */
+            ecrnx_radar_detection_enable(&vif->ecrnx_hw->radar,
+                                        ECRNX_RADAR_DETECT_DISABLE,
+                                        ECRNX_RADAR_RIU);
+        }
+        /* set chan to null, so that if this ctxt is relinked to a vif that
+           don't have channel information, don't use wrong information */
+        ctxt->chan_def.chan = NULL;
+    }
+    vif->ch_index = ECRNX_CH_NOT_SET;
+}
+
+int ecrnx_chanctx_valid(struct ecrnx_hw *ecrnx_hw, u8 ch_idx)
+{
+    if (ch_idx >= NX_CHAN_CTXT_CNT ||
+        ecrnx_hw->chanctx_table[ch_idx].chan_def.chan == NULL) {
+        return 0;
+    }
+
+    return 1;
+}
+
+static void ecrnx_del_csa(struct ecrnx_vif *vif)
+{
+    struct ecrnx_hw *ecrnx_hw = vif->ecrnx_hw;
+    struct ecrnx_csa *csa = vif->ap.csa;
+
+    if (!csa)
+        return;
+
+    ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &csa->elem);
+    ecrnx_del_bcn(&csa->bcn);
+    kfree(csa);
+    vif->ap.csa = NULL;
+}
+
+static void ecrnx_csa_finish(struct work_struct *ws)
+{
+    struct ecrnx_csa *csa = container_of(ws, struct ecrnx_csa, work);
+    struct ecrnx_vif *vif = csa->vif;
+    struct ecrnx_hw *ecrnx_hw = vif->ecrnx_hw;
+    int error = csa->status;
+
+    if (!error)
+        error = ecrnx_send_bcn_change(ecrnx_hw, vif->vif_index, csa->elem.dma_addr,
+                                     csa->bcn.len, csa->bcn.head_len,
+                                     csa->bcn.tim_len, NULL);
+
+    if (error){
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
+        cfg80211_stop_iface(ecrnx_hw->wiphy, &vif->wdev, GFP_KERNEL);
+#else
+        cfg80211_disconnected(vif->ndev, 0, NULL, 0, 0, GFP_KERNEL);
+#endif
+    }
+    else {
+        mutex_lock(&vif->wdev.mtx);
+        __acquire(&vif->wdev.mtx);
+        spin_lock_bh(&ecrnx_hw->cb_lock);
+        ecrnx_chanctx_unlink(vif);
+        ecrnx_chanctx_link(vif, csa->ch_idx, &csa->chandef);
+        if (ecrnx_hw->cur_chanctx == csa->ch_idx) {
+            ecrnx_radar_detection_enable_on_cur_channel(ecrnx_hw);
+            ecrnx_txq_vif_start(vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+        } else
+            ecrnx_txq_vif_stop(vif, ECRNX_TXQ_STOP_CHAN, ecrnx_hw);
+        spin_unlock_bh(&ecrnx_hw->cb_lock);
+        cfg80211_ch_switch_notify(vif->ndev, &csa->chandef);
+        mutex_unlock(&vif->wdev.mtx);
+        __release(&vif->wdev.mtx);
+    }
+    ecrnx_del_csa(vif);
+}
+
+/**
+ * ecrnx_external_auth_enable - Enable external authentication on a vif
+ *
+ * @vif: VIF on which external authentication must be enabled
+ *
+ * External authentication requires to start TXQ for unknown STA in
+ * order to send auth frame pusehd by user space.
+ * Note: It is assumed that fw is on the correct channel.
+ */
+void ecrnx_external_auth_enable(struct ecrnx_vif *vif)
+{
+    vif->sta.flags |= ECRNX_STA_EXT_AUTH;
+    ecrnx_txq_unk_vif_init(vif);
+    ecrnx_txq_start(ecrnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE), 0);
+}
+
+/**
+ * ecrnx_external_auth_disable - Disable external authentication on a vif
+ *
+ * @vif: VIF on which external authentication must be disabled
+ */
+void ecrnx_external_auth_disable(struct ecrnx_vif *vif)
+{
+    if (!(vif->sta.flags & ECRNX_STA_EXT_AUTH))
+        return;
+
+    vif->sta.flags &= ~ECRNX_STA_EXT_AUTH;
+    ecrnx_txq_unk_vif_deinit(vif);
+}
+
+/**
+ * ecrnx_update_mesh_power_mode -
+ *
+ * @vif: mesh VIF  for which power mode is updated
+ *
+ * Does nothing if vif is not a mesh point interface.
+ * Since firmware doesn't support one power save mode per link select the
+ * most "active" power mode among all mesh links.
+ * Indeed as soon as we have to be active on one link we might as well be
+ * active on all links.
+ *
+ * If there is no link then the power mode for next peer is used;
+ */
+void ecrnx_update_mesh_power_mode(struct ecrnx_vif *vif)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    enum nl80211_mesh_power_mode mesh_pm;
+    struct ecrnx_sta *sta;
+    struct mesh_config mesh_conf;
+    struct mesh_update_cfm cfm;
+    u32 mask;
+
+    if (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_MESH_POINT)
+        return;
+
+    if (list_empty(&vif->ap.sta_list)) {
+        mesh_pm = vif->ap.next_mesh_pm;
+    } else {
+        mesh_pm = NL80211_MESH_POWER_DEEP_SLEEP;
+        list_for_each_entry(sta, &vif->ap.sta_list, list) {
+            if (sta->valid && (sta->mesh_pm < mesh_pm)) {
+                mesh_pm = sta->mesh_pm;
+            }
+        }
+    }
+
+    if (mesh_pm == vif->ap.mesh_pm)
+        return;
+
+    mask = BIT(NL80211_MESHCONF_POWER_MODE - 1);
+    mesh_conf.power_mode = mesh_pm;
+    if (ecrnx_send_mesh_update_req(vif->ecrnx_hw, vif, mask, &mesh_conf, &cfm) ||
+        cfm.status)
+        return;
+
+    vif->ap.mesh_pm = mesh_pm;
+#endif
+}
+
+void ecrnx_save_assoc_info_for_ft(struct ecrnx_vif *vif,
+                                 struct cfg80211_connect_params *sme)
+{
+    int ies_len = sme->ie_len + sme->ssid_len + 2;
+    u8 *pos;
+    if (!vif->sta.ft_assoc_ies) {
+        if (!cfg80211_find_ie(WLAN_EID_MOBILITY_DOMAIN, sme->ie, sme->ie_len))
+            return;
+        vif->sta.ft_assoc_ies_len = ies_len;
+        vif->sta.ft_assoc_ies = kmalloc(ies_len, GFP_KERNEL);
+    } else if (vif->sta.ft_assoc_ies_len < ies_len) {
+        kfree(vif->sta.ft_assoc_ies);
+        vif->sta.ft_assoc_ies = kmalloc(ies_len, GFP_KERNEL);
+    }
+    if (!vif->sta.ft_assoc_ies)
+        return;
+    pos = vif->sta.ft_assoc_ies;
+    *pos++ = WLAN_EID_SSID;
+    *pos++ = sme->ssid_len;
+    memcpy(pos, sme->ssid, sme->ssid_len);
+    pos += sme->ssid_len;
+    memcpy(pos, sme->ie, sme->ie_len);
+    vif->sta.ft_assoc_ies_len = ies_len;
+}
+/**
+ * ecrnx_rsne_to_connect_params - Initialise cfg80211_connect_params from
+ * RSN element.
+ *
+ * @rsne: RSN element
+ * @sme: Structure cfg80211_connect_params to initialize
+ *
+ * The goal is only to initialize enough for ecrnx_send_sm_connect_req
+ */
+int ecrnx_rsne_to_connect_params(const struct ecrnx_element *rsne,
+                                struct cfg80211_connect_params *sme)
+{
+    int len = rsne->datalen;
+    int clen;
+    const u8 *pos = rsne->data ;
+    if (len < 8)
+        return 1;
+
+    sme->crypto.control_port_no_encrypt = false;
+    sme->crypto.control_port = true;
+    sme->crypto.control_port_ethertype = cpu_to_be16(ETH_P_PAE);
+
+    pos += 2;
+    sme->crypto.cipher_group = ntohl(*((u32 *)pos));
+    pos += 4;
+    clen = le16_to_cpu(*((u16 *)pos)) * 4;
+    pos += 2;
+    len -= 8;
+    if (len < clen + 2)
+        return 1;
+    // only need one cipher suite
+    sme->crypto.n_ciphers_pairwise = 1;
+    sme->crypto.ciphers_pairwise[0] = ntohl(*((u32 *)pos));
+    pos += clen;
+    len -= clen;
+
+    // no need for AKM
+    clen = le16_to_cpu(*((u16 *)pos)) * 4;
+    pos += 2;
+    len -= 2;
+    if (len < clen)
+        return 1;
+    pos += clen;
+    len -= clen;
+
+    if (len < 4)
+        return 0;
+
+    pos += 2;
+    clen = le16_to_cpu(*((u16 *)pos)) * 16;
+    len -= 4;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    if (len > clen)
+        sme->mfp = NL80211_MFP_REQUIRED;
+#endif
+
+    return 0;
+}
+
+/*********************************************************************
+ * netdev callbacks
+ ********************************************************************/
+/**
+ * int (*ndo_open)(struct net_device *dev);
+ *     This function is called when network device transistions to the up
+ *     state.
+ *
+ * - Start FW if this is the first interface opened
+ * - Add interface at fw level
+ */
+static int ecrnx_open(struct net_device *dev)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+    struct mm_add_if_cfm add_if_cfm;
+    int error = 0;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    // Check if it is the first opened VIF
+    if (ecrnx_hw->vif_started == 0)
+    {
+        // Start the FW
+       if ((error = ecrnx_send_start(ecrnx_hw)))
+           return error;
+
+       /* Device is now started */
+       set_bit(ECRNX_DEV_STARTED, &ecrnx_hw->flags);
+    }
+
+    if (ecrnx_vif->up) {
+        netdev_info(dev, "Started repeatedly");
+        return error;
+    }
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP_VLAN) {
+        /* For AP_vlan use same fw and drv indexes. We ensure that this index
+           will not be used by fw for another vif by taking index >= NX_VIRT_DEV_MAX */
+        add_if_cfm.inst_nbr = ecrnx_vif->drv_vif_index;
+        netif_tx_stop_all_queues(dev);
+    } else {
+        /* Forward the information to the LMAC,
+         *     p2p value not used in FMAC configuration, iftype is sufficient */
+        if ((error = ecrnx_send_add_if(ecrnx_hw, dev->dev_addr,
+                                      ECRNX_VIF_TYPE(ecrnx_vif), false, &add_if_cfm)))
+            return error;
+
+        if (add_if_cfm.status != 0) {
+            ECRNX_PRINT_CFM_ERR(add_if);
+            return -EIO;
+        }
+    }
+
+    /* Save the index retrieved from LMAC */
+    spin_lock_bh(&ecrnx_hw->cb_lock);
+    ecrnx_vif->vif_index = add_if_cfm.inst_nbr;
+    ecrnx_vif->up = true;
+    ecrnx_hw->vif_started++;
+    ecrnx_hw->vif_table[add_if_cfm.inst_nbr] = ecrnx_vif;
+    memset(ecrnx_hw->vif_table[add_if_cfm.inst_nbr]->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+    spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MONITOR) {
+        ecrnx_hw->monitor_vif = ecrnx_vif->vif_index;
+        if (ecrnx_vif->ch_index != ECRNX_CH_NOT_SET) {
+            //Configure the monitor channel
+            error = ecrnx_send_config_monitor_req(ecrnx_hw,
+                                                 &ecrnx_hw->chanctx_table[ecrnx_vif->ch_index].chan_def,
+                                                 NULL);
+        }
+    }
+
+    netif_carrier_off(dev);
+
+    return error;
+}
+
+/**
+ * int (*ndo_stop)(struct net_device *dev);
+ *     This function is called when network device transistions to the down
+ *     state.
+ *
+ * - Remove interface at fw level
+ * - Reset FW if this is the last interface opened
+ */
+static int ecrnx_close(struct net_device *dev)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    netdev_info(dev, "CLOSE");
+
+    ecrnx_radar_cancel_cac(&ecrnx_hw->radar);
+
+    spin_lock_bh(&ecrnx_hw->scan_req_lock);
+    /* Abort scan request on the vif */
+    if (ecrnx_hw->scan_request &&
+        ecrnx_hw->scan_request->wdev == &ecrnx_vif->wdev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
+        struct cfg80211_scan_info info = {
+            .aborted = true,
+        };
+
+        cfg80211_scan_done(ecrnx_hw->scan_request, &info);
+#else
+        cfg80211_scan_done(ecrnx_hw->scan_request, true);
+#endif
+        ecrnx_hw->scan_request = NULL;
+    }
+
+    spin_unlock_bh(&ecrnx_hw->scan_req_lock);
+    ecrnx_send_remove_if(ecrnx_hw, ecrnx_vif->vif_index);
+
+    if (ecrnx_hw->roc && (ecrnx_hw->roc->vif == ecrnx_vif)) {
+        kfree(ecrnx_hw->roc);
+        /* Initialize RoC element pointer to NULL, indicate that RoC can be started */
+        ecrnx_hw->roc = NULL;
+    }
+
+    /* Ensure that we won't process disconnect ind */
+    spin_lock_bh(&ecrnx_hw->cb_lock);
+
+    ecrnx_vif->up = false;
+    if (netif_carrier_ok(dev)) {
+        if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_STATION ||
+            ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_CLIENT) {
+            if (ecrnx_vif->sta.ft_assoc_ies) {
+                kfree(ecrnx_vif->sta.ft_assoc_ies);
+                ecrnx_vif->sta.ft_assoc_ies = NULL;
+                ecrnx_vif->sta.ft_assoc_ies_len = 0;
+            }
+            cfg80211_disconnected(dev, WLAN_REASON_DEAUTH_LEAVING,
+                                  NULL, 0, true, GFP_ATOMIC);
+            if (ecrnx_vif->sta.ap) {
+                ecrnx_txq_sta_deinit(ecrnx_hw, ecrnx_vif->sta.ap);
+                ecrnx_txq_tdls_vif_deinit(ecrnx_vif);
+            }
+            netif_tx_stop_all_queues(dev);
+            netif_carrier_off(dev);
+        } else if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP_VLAN) {
+            netif_carrier_off(dev);
+        } else {
+            netdev_warn(dev, "AP not stopped when disabling interface");
+        }
+    }
+
+    ecrnx_hw->vif_table[ecrnx_vif->vif_index] = NULL;
+    spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+    ecrnx_chanctx_unlink(ecrnx_vif);
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MONITOR)
+        ecrnx_hw->monitor_vif = ECRNX_INVALID_VIF;
+
+    ecrnx_hw->vif_started--;
+    if (ecrnx_hw->vif_started == 0) {
+#ifndef CONFIG_ECRNX_ESWIN
+        /* This also lets both ipc sides remain in sync before resetting */
+        ecrnx_ipc_tx_drain(ecrnx_hw);
+#endif
+        ecrnx_send_reset(ecrnx_hw);
+
+        // Set parameters to firmware
+        ecrnx_send_me_config_req(ecrnx_hw);
+
+        // Set channel parameters to firmware
+        ecrnx_send_me_chan_config_req(ecrnx_hw);
+
+        clear_bit(ECRNX_DEV_STARTED, &ecrnx_hw->flags);
+    }
+
+    return 0;
+}
+
+/**
+ * struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
+ *     Called when a user wants to get the network device usage
+ *     statistics. Drivers must do one of the following:
+ *     1. Define @ndo_get_stats64 to fill in a zero-initialised
+ *        rtnl_link_stats64 structure passed by the caller.
+ *     2. Define @ndo_get_stats to update a net_device_stats structure
+ *        (which should normally be dev->stats) and return a pointer to
+ *        it. The structure may be changed asynchronously only if each
+ *        field is written atomically.
+ *     3. Update dev->stats asynchronously and atomically, and define
+ *        neither operation.
+ */
+static struct net_device_stats *ecrnx_get_stats(struct net_device *dev)
+{
+    struct ecrnx_vif *vif = netdev_priv(dev);
+
+    return &vif->net_stats;
+}
+
+/**
+ * u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb,
+ *                         struct net_device *sb_dev);
+ *     Called to decide which queue to when device supports multiple
+ *     transmit queues.
+ */
+u16 ecrnx_select_queue(struct net_device *dev, struct sk_buff *skb,
+                      struct net_device *sb_dev)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    return ecrnx_select_txq(ecrnx_vif, skb);
+}
+
+/**
+ * int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
+ *     This function  is called when the Media Access Control address
+ *     needs to be changed. If this interface is not defined, the
+ *     mac address can not be changed.
+ */
+static int ecrnx_set_mac_address(struct net_device *dev, void *addr)
+{
+    struct sockaddr *sa = addr;
+    int ret;
+
+    ret = eth_mac_addr(dev, sa);
+
+    return ret;
+}
+
+int ecrnx_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+    //struct iwreq *wrq = (struct iwreq *)rq;
+    int ret = 0;
+
+    switch (cmd) {
+     case (SIOCDEVPRIVATE+1):
+      //ret = ecrnx_android_priv_cmd(dev, rq, cmd);
+        break;
+     default:
+        ret = -EOPNOTSUPP;
+        break;
+    }
+
+ return ret;
+}
+
+static const struct net_device_ops ecrnx_netdev_ops = {
+    .ndo_open               = ecrnx_open,
+    .ndo_stop               = ecrnx_close,
+    .ndo_start_xmit         = ecrnx_start_xmit,
+    .ndo_get_stats          = ecrnx_get_stats,
+    .ndo_select_queue       = ecrnx_select_queue,
+    .ndo_set_mac_address    = ecrnx_set_mac_address,
+    .ndo_do_ioctl           = ecrnx_ioctl,
+//    .ndo_set_features       = ecrnx_set_features,
+//    .ndo_set_rx_mode        = ecrnx_set_multicast_list,
+};
+
+static const struct net_device_ops ecrnx_netdev_monitor_ops = {
+    .ndo_open               = ecrnx_open,
+    .ndo_stop               = ecrnx_close,
+    .ndo_get_stats          = ecrnx_get_stats,
+    .ndo_set_mac_address    = ecrnx_set_mac_address,
+};
+
+#ifdef CONFIG_WIRELESS_EXT
+extern const struct iw_handler_def  ecrnx_wext_handler_def;
+#endif
+
+static void ecrnx_netdev_setup(struct net_device *dev)
+{
+    ether_setup(dev);
+    dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+    dev->netdev_ops = &ecrnx_netdev_ops;
+#ifdef CONFIG_WIRELESS_EXT
+    dev->wireless_handlers = &ecrnx_wext_handler_def;
+#endif
+#if LINUX_VERSION_CODE <  KERNEL_VERSION(4, 12, 0)
+    dev->destructor = free_netdev;
+#else
+    dev->needs_free_netdev = true;
+#endif
+    dev->watchdog_timeo = ECRNX_TX_LIFETIME_MS;
+    dev->needed_headroom = ECRNX_TX_MAX_HEADROOM;
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    dev->needed_headroom = max(dev->needed_headroom,
+                               (unsigned short)(sizeof(struct ecrnx_amsdu_txhdr)
+                                                + sizeof(struct ethhdr) + 4
+                                                + sizeof(rfc1042_header) + 2));
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+    dev->hw_features = 0;
+}
+
+/*********************************************************************
+ * Cfg80211 callbacks (and helper)
+ *********************************************************************/
+static struct wireless_dev *ecrnx_interface_add(struct ecrnx_hw *ecrnx_hw,
+                                               const char *name,
+                                               unsigned char name_assign_type,
+                                               enum nl80211_iftype type,
+                                               struct vif_params *params)
+{
+    struct net_device *ndev;
+    struct ecrnx_vif *vif;
+    int min_idx, max_idx;
+    int vif_idx = -1;
+    int i;
+
+    // Look for an available VIF
+    if (type == NL80211_IFTYPE_AP_VLAN) {
+        min_idx = NX_VIRT_DEV_MAX;
+        max_idx = NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX;
+    } else {
+        min_idx = 0;
+        max_idx = NX_VIRT_DEV_MAX;
+    }
+
+    for (i = min_idx; i < max_idx; i++) {
+        if ((ecrnx_hw->avail_idx_map) & BIT(i)) {
+            vif_idx = i;
+            break;
+        }
+    }
+    if (vif_idx < 0)
+        return NULL;
+
+    #ifndef CONFIG_ECRNX_MON_DATA
+    list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+        // Check if monitor interface already exists or type is monitor
+        if ((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR) ||
+           (type == NL80211_IFTYPE_MONITOR)) {
+            wiphy_err(ecrnx_hw->wiphy,
+                    "Monitor+Data interface support (MON_DATA) disabled\n");
+            return NULL;
+        }
+    }
+    #endif
+
+    ndev = alloc_netdev_mqs(sizeof(*vif), name, name_assign_type,
+                            ecrnx_netdev_setup, NX_NB_NDEV_TXQ, 1);
+    if (!ndev)
+        return NULL;
+
+    vif = netdev_priv(ndev);
+    ndev->ieee80211_ptr = &vif->wdev;
+    vif->wdev.wiphy = ecrnx_hw->wiphy;
+    vif->ecrnx_hw = ecrnx_hw;
+    vif->ndev = ndev;
+    vif->drv_vif_index = vif_idx;
+    SET_NETDEV_DEV(ndev, wiphy_dev(vif->wdev.wiphy));
+    vif->wdev.netdev = ndev;
+    vif->wdev.iftype = type;
+    vif->up = false;
+    vif->ch_index = ECRNX_CH_NOT_SET;
+    vif->generation = 0;
+    memset(&vif->net_stats, 0, sizeof(vif->net_stats));
+    memset(vif->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+
+    switch (type) {
+    case NL80211_IFTYPE_STATION:
+    case NL80211_IFTYPE_P2P_CLIENT:
+        vif->sta.flags = 0;
+        vif->sta.ap = NULL;
+        vif->sta.tdls_sta = NULL;
+        vif->sta.ft_assoc_ies = NULL;
+        vif->sta.ft_assoc_ies_len = 0;
+        break;
+    case NL80211_IFTYPE_MESH_POINT:
+        INIT_LIST_HEAD(&vif->ap.mpath_list);
+        INIT_LIST_HEAD(&vif->ap.proxy_list);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+        vif->ap.mesh_pm = NL80211_MESH_POWER_ACTIVE;
+        vif->ap.next_mesh_pm = NL80211_MESH_POWER_ACTIVE;
+#endif
+        break;
+    case NL80211_IFTYPE_AP:
+    case NL80211_IFTYPE_P2P_GO:
+        INIT_LIST_HEAD(&vif->ap.sta_list);
+        memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
+        vif->ap.flags = 0;
+        break;
+    case NL80211_IFTYPE_AP_VLAN:
+    {
+        struct ecrnx_vif *master_vif;
+        bool found = false;
+        list_for_each_entry(master_vif, &ecrnx_hw->vifs, list) {
+            if ((ECRNX_VIF_TYPE(master_vif) == NL80211_IFTYPE_AP)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+                && !(!memcmp(master_vif->ndev->dev_addr, params->macaddr,
+                           ETH_ALEN))
+#endif
+                 ) {
+                 found=true;
+                 break;
+            }
+        }
+
+        if (!found)
+            goto err;
+
+         vif->ap_vlan.master = master_vif;
+         vif->ap_vlan.sta_4a = NULL;
+         break;
+    }
+    case NL80211_IFTYPE_MONITOR:
+        ndev->type = ARPHRD_IEEE80211_RADIOTAP;
+        ndev->netdev_ops = &ecrnx_netdev_monitor_ops;
+        break;
+    default:
+        break;
+    }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    if (type == NL80211_IFTYPE_AP_VLAN)
+        memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
+    else 
+#endif
+    {
+        memcpy(ndev->dev_addr, ecrnx_hw->wiphy->perm_addr, ETH_ALEN);
+        ndev->dev_addr[5] ^= vif_idx;
+    }
+
+    if (params) {
+        vif->use_4addr = params->use_4addr;
+        ndev->ieee80211_ptr->use_4addr = params->use_4addr;
+    } else
+        vif->use_4addr = false;
+
+
+    if (register_netdevice(ndev))
+        goto err;
+
+    spin_lock_bh(&ecrnx_hw->cb_lock);
+    list_add_tail(&vif->list, &ecrnx_hw->vifs);
+    spin_unlock_bh(&ecrnx_hw->cb_lock);
+    ecrnx_hw->avail_idx_map &= ~BIT(vif_idx);
+
+//#if defined(CONFIG_ECRNX_ESWIN_SDIO) || defined(CONFIG_ECRNX_ESWIN_USB)
+    init_waitqueue_head(&vif->rxdataq);
+//#endif
+
+    return &vif->wdev;
+
+err:
+    free_netdev(ndev);
+    return NULL;
+}
+
+
+/*
+ * @brief Retrieve the ecrnx_sta object allocated for a given MAC address
+ * and a given role.
+ */
+static struct ecrnx_sta *ecrnx_retrieve_sta(struct ecrnx_hw *ecrnx_hw,
+                                          struct ecrnx_vif *ecrnx_vif, u8 *addr,
+                                          __le16 fc, bool ap)
+{
+    if (ap) {
+        /* only deauth, disassoc and action are bufferable MMPDUs */
+        bool bufferable = ieee80211_is_deauth(fc) ||
+                          ieee80211_is_disassoc(fc) ||
+                          ieee80211_is_action(fc);
+
+        /* Check if the packet is bufferable or not */
+        if (bufferable)
+        {
+            /* Check if address is a broadcast or a multicast address */
+            if (is_broadcast_ether_addr(addr) || is_multicast_ether_addr(addr)) {
+                /* Returned STA pointer */
+                struct ecrnx_sta *ecrnx_sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+
+                if (ecrnx_sta->valid)
+                    return ecrnx_sta;
+            } else {
+                /* Returned STA pointer */
+                struct ecrnx_sta *ecrnx_sta;
+
+                /* Go through list of STAs linked with the provided VIF */
+                list_for_each_entry(ecrnx_sta, &ecrnx_vif->ap.sta_list, list) {
+                    if (ecrnx_sta->valid &&
+                        ether_addr_equal(ecrnx_sta->mac_addr, addr)) {
+                        /* Return the found STA */
+                        return ecrnx_sta;
+                    }
+                }
+            }
+        }
+    } else {
+        return ecrnx_vif->sta.ap;
+    }
+
+    return NULL;
+}
+
+/**
+ * @add_virtual_intf: create a new virtual interface with the given name,
+ *     must set the struct wireless_dev's iftype. Beware: You must create
+ *     the new netdev in the wiphy's network namespace! Returns the struct
+ *     wireless_dev, or an ERR_PTR. For P2P device wdevs, the driver must
+ *     also set the address member in the wdev.
+ */
+static struct wireless_dev *ecrnx_cfg80211_add_iface(struct wiphy *wiphy,
+                                                    const char *name,
+                                                    unsigned char name_assign_type,
+                                                    enum nl80211_iftype type,
+                                                    struct vif_params *params)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct wireless_dev *wdev;
+
+    wdev = ecrnx_interface_add(ecrnx_hw, name, name_assign_type, type, params);
+
+    if (!wdev)
+        return ERR_PTR(-EINVAL);
+
+    return wdev;
+}
+
+/**
+ * @del_virtual_intf: remove the virtual interface
+ */
+static int ecrnx_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+    struct net_device *dev = wdev->netdev;
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+
+    netdev_info(dev, "Remove Interface");
+
+    if (dev->reg_state == NETREG_REGISTERED) {
+      ECRNX_DBG("%s-%d:unregister_netdevice \n", __func__, __LINE__);
+        /* Will call ecrnx_close if interface is UP */
+        unregister_netdevice(dev);
+    }
+
+    spin_lock_bh(&ecrnx_hw->cb_lock);
+    list_del(&ecrnx_vif->list);
+    spin_unlock_bh(&ecrnx_hw->cb_lock);
+    ecrnx_hw->avail_idx_map |= BIT(ecrnx_vif->drv_vif_index);
+    ecrnx_vif->ndev = NULL;
+
+    /* Clear the priv in adapter */
+    dev->ieee80211_ptr = NULL;
+
+    return 0;
+}
+
+static int ecrnx_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev);
+static int ecrnx_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+                                    u16 reason_code);
+
+/**
+ * @change_virtual_intf: change type/configuration of virtual interface,
+ *     keep the struct wireless_dev's iftype updated.
+ */
+static int ecrnx_cfg80211_change_iface(struct wiphy *wiphy,
+                                      struct net_device *dev,
+                                      enum nl80211_iftype type,
+                                      struct vif_params *params)
+{
+#ifndef CONFIG_ECRNX_MON_DATA
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+#endif
+    struct ecrnx_vif *vif = netdev_priv(dev);
+
+
+    ECRNX_PRINT("%s:dev:0x%p, type:%d, vif->up:%d \n", __func__, dev, type, vif->up);
+
+    if (vif->up)
+    {
+        if((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP) || (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_GO))
+        {
+            ecrnx_cfg80211_stop_ap(wiphy, dev);
+        }
+        else if((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_STATION) || (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_CLIENT))
+        {
+            ecrnx_cfg80211_disconnect(wiphy, dev, WLAN_REASON_DEAUTH_LEAVING);
+        }
+        ECRNX_ERR("ecrnx_cfg80211_change_iface: -EBUSY \n");
+        ecrnx_close(dev);
+        //return (-EBUSY);
+    }
+
+#ifndef CONFIG_ECRNX_MON_DATA
+    if ((type == NL80211_IFTYPE_MONITOR) &&
+       (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_MONITOR)) {
+        struct ecrnx_vif *vif_el;
+        list_for_each_entry(vif_el, &ecrnx_hw->vifs, list) {
+            // Check if data interface already exists
+            if ((vif_el != vif) &&
+               (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_MONITOR)) {
+                wiphy_err(ecrnx_hw->wiphy,
+                        "Monitor+Data interface support (MON_DATA) disabled\n");
+                return -EIO;
+            }
+        }
+    }
+#endif
+
+    // Reset to default case (i.e. not monitor)
+    dev->type = ARPHRD_ETHER;
+    dev->netdev_ops = &ecrnx_netdev_ops;
+
+    switch (type) {
+    case NL80211_IFTYPE_STATION:
+    case NL80211_IFTYPE_P2P_CLIENT:
+        vif->sta.flags = 0;
+        vif->sta.ap = NULL;
+        vif->sta.tdls_sta = NULL;
+        vif->sta.ft_assoc_ies = NULL;
+        vif->sta.ft_assoc_ies_len = 0;
+        break;
+    case NL80211_IFTYPE_MESH_POINT:
+        INIT_LIST_HEAD(&vif->ap.mpath_list);
+        INIT_LIST_HEAD(&vif->ap.proxy_list);
+        break;
+    case NL80211_IFTYPE_AP:
+    case NL80211_IFTYPE_P2P_GO:
+        INIT_LIST_HEAD(&vif->ap.sta_list);
+        memset(&vif->ap.bcn, 0, sizeof(vif->ap.bcn));
+        vif->ap.flags = 0;
+        break;
+    case NL80211_IFTYPE_AP_VLAN:
+        return -EPERM;
+    case NL80211_IFTYPE_MONITOR:
+        dev->type = ARPHRD_IEEE80211_RADIOTAP;
+        dev->netdev_ops = &ecrnx_netdev_monitor_ops;
+        break;
+    default:
+        break;
+    }
+
+    vif->generation = 0;
+    vif->wdev.iftype = type;
+    if (params->use_4addr != -1)
+        vif->use_4addr = params->use_4addr;
+
+    if (!vif->up)
+    {
+        ecrnx_open(dev);
+    }
+
+    return 0;
+}
+
+/*
+ * GavinGao
+ * Used as P2P_DEVICE mode
+ */
+static int ecrnx_cfg80211_start_p2p_device(struct wiphy *wiphy,
+                                     struct wireless_dev *wdev)
+{
+       ECRNX_PRINT("rwnx_cfg80211_start_p2p_device\n");
+       return 0;
+}
+
+static void ecrnx_cfg80211_stop_p2p_device(struct wiphy *wiphy,
+                                     struct wireless_dev *wdev)
+{
+       //TODO
+}
+/* Used as P2P_DEVICE mode*/
+                                         
+
+/**
+ * @scan: Request to do a scan. If returning zero, the scan request is given
+ *     the driver, and will be valid until passed to cfg80211_scan_done().
+ *     For scan results, call cfg80211_inform_bss(); you can call this outside
+ *     the scan/scan_done bracket too.
+ */
+static int ecrnx_cfg80211_scan(struct wiphy *wiphy,
+                              struct cfg80211_scan_request *request)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = container_of(request->wdev, struct ecrnx_vif,
+                                             wdev);
+    int error;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if(!ecrnx_hw->scan_request){
+        ecrnx_hw->scan_request = request;
+        ECRNX_PRINT("%s:scan_request:0x%p \n", __func__, request);
+        if ((error = ecrnx_send_scanu_req(ecrnx_hw, ecrnx_vif, request))){
+            ECRNX_PRINT("scan message send error!!\n");
+            ecrnx_hw->scan_request = NULL;
+            return error;      
+       }
+    }else{
+        ECRNX_PRINT("scan is already running!!\n");
+    }
+    ECRNX_DBG("send finish:ecrnx_cfg80211_scan \n");
+
+    return 0;
+}
+
+static void ecrnx_cfg80211_abort_scan(struct wiphy *wiphy,
+                              struct wireless_dev *wdev)
+{
+    u8_l ret = 0;
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = container_of(wdev, struct ecrnx_vif,
+                                             wdev);
+
+    //mutex_lock(&ecrnx_hw->mutex);
+    ECRNX_PRINT("%s:ecrnx_hw->scan_request:0x%p \n", __func__, ecrnx_hw->scan_request);
+
+    if(!ecrnx_hw->scan_request){
+        ECRNX_ERR("no scan is running, don't need abort! \n");
+        goto out;
+    }
+
+    if(wdev->iftype != NL80211_IFTYPE_STATION){
+        ECRNX_ERR("abort scan ignored, iftype(%d)\n", wdev->iftype);
+        goto out;
+    }
+
+    if(wdev != ecrnx_hw->scan_request->wdev){
+        ECRNX_ERR("abort scan was called on the wrong iface\n");
+        goto out;
+    }
+
+    ret = ecrnx_send_scanu_cancel_req(ecrnx_hw, ecrnx_vif);
+
+out:
+    //mutex_unlock(&ecrnx_hw->mutex);
+    return;
+}
+
+/**
+ * @add_key: add a key with the given parameters. @mac_addr will be %NULL
+ *     when adding a group key.
+ */
+static int ecrnx_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev,
+                                 u8 key_index, bool pairwise, const u8 *mac_addr,
+                                 struct key_params *params)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *vif = netdev_priv(netdev);
+    int i, error = 0;
+    struct mm_key_add_cfm key_add_cfm;
+    u8_l cipher = 0;
+    struct ecrnx_sta *sta = NULL;
+    struct ecrnx_key *ecrnx_key;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if (mac_addr) {
+        sta = ecrnx_get_sta(ecrnx_hw, mac_addr);
+        if (!sta)
+            return -EINVAL;
+        ecrnx_key = &sta->key;
+    }
+    else
+        ecrnx_key = &vif->key[key_index];
+
+    /* Retrieve the cipher suite selector */
+    switch (params->cipher) {
+    case WLAN_CIPHER_SUITE_WEP40:
+        cipher = MAC_CIPHER_WEP40;
+        break;
+    case WLAN_CIPHER_SUITE_WEP104:
+        cipher = MAC_CIPHER_WEP104;
+        break;
+    case WLAN_CIPHER_SUITE_TKIP:
+        cipher = MAC_CIPHER_TKIP;
+        break;
+    case WLAN_CIPHER_SUITE_CCMP:
+        cipher = MAC_CIPHER_CCMP;
+        break;
+    case WLAN_CIPHER_SUITE_AES_CMAC:
+        cipher = MAC_CIPHER_BIP_CMAC_128;
+        break;
+    case WLAN_CIPHER_SUITE_SMS4:
+    {
+        // Need to reverse key order
+        u8 tmp, *key = (u8 *)params->key;
+        cipher = MAC_CIPHER_WPI_SMS4;
+        for (i = 0; i < WPI_SUBKEY_LEN/2; i++) {
+            tmp = key[i];
+            key[i] = key[WPI_SUBKEY_LEN - 1 - i];
+            key[WPI_SUBKEY_LEN - 1 - i] = tmp;
+        }
+        for (i = 0; i < WPI_SUBKEY_LEN/2; i++) {
+            tmp = key[i + WPI_SUBKEY_LEN];
+            key[i + WPI_SUBKEY_LEN] = key[WPI_KEY_LEN - 1 - i];
+            key[WPI_KEY_LEN - 1 - i] = tmp;
+        }
+        break;
+    }
+    case WLAN_CIPHER_SUITE_GCMP:
+        cipher = MAC_CIPHER_GCMP_128;
+        break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+    case WLAN_CIPHER_SUITE_GCMP_256:
+        cipher = MAC_CIPHER_GCMP_256;
+        break;
+    case WLAN_CIPHER_SUITE_CCMP_256:
+        cipher = MAC_CIPHER_CCMP_256;
+        break;
+#endif
+    default:
+        return -EINVAL;
+    }
+
+    if ((error = ecrnx_send_key_add(ecrnx_hw, vif->vif_index,
+                                   (sta ? sta->sta_idx : 0xFF), pairwise,
+                                   (u8 *)params->key, params->key_len,
+                                   key_index, cipher, &key_add_cfm)))
+        return error;
+
+    if (key_add_cfm.status != 0) {
+        ECRNX_PRINT_CFM_ERR(key_add);
+        return -EIO;
+    }
+
+    /* Save the index retrieved from LMAC */
+    ecrnx_key->hw_idx = key_add_cfm.hw_key_idx;
+
+    return 0;
+}
+
+/**
+ * @get_key: get information about the key with the given parameters.
+ *     @mac_addr will be %NULL when requesting information for a group
+ *     key. All pointers given to the @callback function need not be valid
+ *     after it returns. This function should return an error if it is
+ *     not possible to retrieve the key, -ENOENT if it doesn't exist.
+ *
+ */
+static int ecrnx_cfg80211_get_key(struct wiphy *wiphy, struct net_device *netdev,
+                                 u8 key_index, bool pairwise, const u8 *mac_addr,
+                                 void *cookie,
+                                 void (*callback)(void *cookie, struct key_params*))
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    return -1;
+}
+
+
+/**
+ * @del_key: remove a key given the @mac_addr (%NULL for a group key)
+ *     and @key_index, return -ENOENT if the key doesn't exist.
+ */
+static int ecrnx_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev,
+                                 u8 key_index, bool pairwise, const u8 *mac_addr)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *vif = netdev_priv(netdev);
+    int error;
+    struct ecrnx_sta *sta = NULL;
+    struct ecrnx_key *ecrnx_key;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    if (mac_addr) {
+        sta = ecrnx_get_sta(ecrnx_hw, mac_addr);
+        if (!sta)
+            return -EINVAL;
+        ecrnx_key = &sta->key;
+    }
+    else
+        ecrnx_key = &vif->key[key_index];
+
+    error = ecrnx_send_key_del(ecrnx_hw, ecrnx_key->hw_idx);
+
+    return error;
+}
+
+/**
+ * @set_default_key: set the default key on an interface
+ */
+static int ecrnx_cfg80211_set_default_key(struct wiphy *wiphy,
+                                         struct net_device *netdev,
+                                         u8 key_index, bool unicast, bool multicast)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    return 0;
+}
+
+/**
+ * @set_default_mgmt_key: set the default management frame key on an interface
+ */
+static int ecrnx_cfg80211_set_default_mgmt_key(struct wiphy *wiphy,
+                                              struct net_device *netdev,
+                                              u8 key_index)
+{
+    return 0;
+}
+
+/**
+ * @connect: Connect to the ESS with the specified parameters. When connected,
+ *     call cfg80211_connect_result() with status code %WLAN_STATUS_SUCCESS.
+ *     If the connection fails for some reason, call cfg80211_connect_result()
+ *     with the status from the AP.
+ *     (invoked with the wireless_dev mutex held)
+ */
+static int ecrnx_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+                                 struct cfg80211_connect_params *sme)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct sm_connect_cfm sm_connect_cfm;
+    int error = 0;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* For SHARED-KEY authentication, must install key first */
+    if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY && sme->key)
+    {
+        struct key_params key_params;
+        key_params.key = sme->key;
+        key_params.seq = NULL;
+        key_params.key_len = sme->key_len;
+        key_params.seq_len = 0;
+        key_params.cipher = sme->crypto.cipher_group;
+        ecrnx_cfg80211_add_key(wiphy, dev, sme->key_idx, false, NULL, &key_params);
+    }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+    else if ((sme->auth_type == NL80211_AUTHTYPE_SAE) &&
+             !(sme->flags & CONNECT_REQ_EXTERNAL_AUTH_SUPPORT)) {
+        netdev_err(dev, "Doesn't support SAE without external authentication\n");
+        return -EINVAL;
+    }
+#endif
+
+    /* Forward the information to the LMAC */
+    if ((error = ecrnx_send_sm_connect_req(ecrnx_hw, ecrnx_vif, sme, &sm_connect_cfm)))
+        return error;
+    ECRNX_PRINT("%s:bssid:%pM, send status:%d\n", __func__, sme->bssid, sm_connect_cfm.status);
+
+    // Check the status
+    switch (sm_connect_cfm.status)
+    {
+        case CO_OK:
+            ecrnx_save_assoc_info_for_ft(ecrnx_vif, sme);
+            error = 0;
+            break;
+        case CO_BUSY:
+            error = -EINPROGRESS;
+            break;
+        case CO_BAD_PARAM:
+            error = -EINVAL;
+            break;
+        case CO_OP_IN_PROGRESS:
+            error = -EALREADY;
+            break;
+        default:
+            error = -EIO;
+            break;
+    }
+
+    return error;
+}
+
+/**
+ * @disconnect: Disconnect from the BSS/ESS.
+ *     (invoked with the wireless_dev mutex held)
+ */
+static int ecrnx_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+                                    u16 reason_code)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+
+    ECRNX_PRINT("%s:dev:0x%p, vif_index:%d, reason_code:%d \n", __func__, dev, ecrnx_vif->vif_index, reason_code);
+
+    return(ecrnx_send_sm_disconnect_req(ecrnx_hw, ecrnx_vif, reason_code));
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+/**
+ * @external_auth: indicates result of offloaded authentication processing from
+ *     user space
+ */
+static int ecrnx_cfg80211_external_auth(struct wiphy *wiphy, struct net_device *dev,
+                                       struct cfg80211_external_auth_params *params)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+
+    if (!(ecrnx_vif->sta.flags & ECRNX_STA_EXT_AUTH))
+        return -EINVAL;
+
+    ecrnx_external_auth_disable(ecrnx_vif);
+    return ecrnx_send_sm_external_auth_required_rsp(ecrnx_hw, ecrnx_vif,
+                                                   params->status);
+}
+#endif
+
+/**
+ * @add_station: Add a new station.
+ */
+static int ecrnx_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
+                                     const u8 *mac, struct station_parameters *params)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct me_sta_add_cfm me_sta_add_cfm;
+    int error = 0;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    WARN_ON(ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP_VLAN);
+
+    /* Do not add TDLS station */
+    if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+        return 0;
+
+    /* Indicate we are in a STA addition process - This will allow handling
+     * potential PS mode change indications correctly
+     */
+    set_bit(ECRNX_DEV_ADDING_STA, &ecrnx_hw->flags);
+
+    /* Forward the information to the LMAC */
+    if ((error = ecrnx_send_me_sta_add(ecrnx_hw, params, mac, ecrnx_vif->vif_index,
+                                      &me_sta_add_cfm)))
+        return error;
+
+    // Check the status
+    switch (me_sta_add_cfm.status)
+    {
+        case CO_OK:
+        {
+            struct ecrnx_sta *sta = &ecrnx_hw->sta_table[me_sta_add_cfm.sta_idx];
+
+            ECRNX_PRINT("%s-%d:sta:0x%p, sta_idx:%d \n", __func__, __LINE__, sta, me_sta_add_cfm.sta_idx);
+            int tid;
+            sta->aid = params->aid;
+            memset(sta->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+            sta->sta_idx = me_sta_add_cfm.sta_idx;
+            sta->ch_idx = ecrnx_vif->ch_index;
+            sta->vif_idx = ecrnx_vif->vif_index;
+            sta->vlan_idx = sta->vif_idx;
+            sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0;
+            sta->ht = params->ht_capa ? 1 : 0;
+            sta->vht = params->vht_capa ? 1 : 0;
+            sta->acm = 0;
+            sta->listen_interval = params->listen_interval;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+            if (params->local_pm != NL80211_MESH_POWER_UNKNOWN)
+                sta->mesh_pm = params->local_pm;
+            else
+                sta->mesh_pm = ecrnx_vif->ap.next_mesh_pm;
+#endif
+            ecrnx_update_mesh_power_mode(ecrnx_vif);
+
+            for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++) {
+                int uapsd_bit = ecrnx_hwq2uapsd[ecrnx_tid2hwq[tid]];
+                if (params->uapsd_queues & uapsd_bit)
+                    sta->uapsd_tids |= 1 << tid;
+                else
+                    sta->uapsd_tids &= ~(1 << tid);
+            }
+            memcpy(sta->mac_addr, mac, ETH_ALEN);
+            ecrnx_dbgfs_register_sta(ecrnx_hw, sta);
+
+            /* Ensure that we won't process PS change or channel switch ind*/
+            spin_lock_bh(&ecrnx_hw->cb_lock);
+            ecrnx_txq_sta_init(ecrnx_hw, sta, ecrnx_txq_vif_get_status(ecrnx_vif));
+            ecrnx_rx_reord_sta_init(ecrnx_hw, ecrnx_vif, sta->sta_idx);
+            list_add_tail(&sta->list, &ecrnx_vif->ap.sta_list);
+            ecrnx_vif->generation++;
+            sta->valid = true;
+            ecrnx_ps_bh_enable(ecrnx_hw, sta, sta->ps.active || me_sta_add_cfm.pm_state);
+            spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+#ifdef CONFIG_ECRNX_ANDRIOD
+            if((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP) || (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_GO)) {
+                struct station_info sinfo;
+                u8 ie_offset;
+
+                if((!is_multicast_sta(sta->sta_idx)) && (sta->mac_addr)) {
+                    memset(&sinfo, 0, sizeof(sinfo));
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+                    sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
+#endif
+                    sinfo.assoc_req_ies = NULL;
+                    sinfo.assoc_req_ies_len = 0;
+                    ECRNX_PRINT("%s-%d:sta:0x%x,sta->mac_addr:%pM \n", __func__, __LINE__, sta, sta->mac_addr);
+                    cfg80211_new_sta(ecrnx_vif->ndev, sta->mac_addr, &sinfo, GFP_ATOMIC);
+                }
+            }
+#endif
+
+            error = 0;
+
+#ifdef CONFIG_ECRNX_BFMER
+            if (ecrnx_hw->mod_params->bfmer)
+                ecrnx_send_bfmer_enable(ecrnx_hw, sta, params->vht_capa);
+
+            ecrnx_mu_group_sta_init(sta, params->vht_capa);
+#endif /* CONFIG_ECRNX_BFMER */
+
+            #define PRINT_STA_FLAG(f)                               \
+                (params->sta_flags_set & BIT(NL80211_STA_FLAG_##f) ? "["#f"]" : "")
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+            netdev_info(dev, "Add sta %d (%pM) flags=%s%s%s%s%s%s%s",
+                        sta->sta_idx, mac,
+                        PRINT_STA_FLAG(AUTHORIZED),
+                        PRINT_STA_FLAG(SHORT_PREAMBLE),
+                        PRINT_STA_FLAG(WME),
+                        PRINT_STA_FLAG(MFP),
+                        PRINT_STA_FLAG(AUTHENTICATED),
+                        PRINT_STA_FLAG(TDLS_PEER),
+                        PRINT_STA_FLAG(ASSOCIATED));
+#else
+            netdev_info(dev, "Add sta %d (%pM) flags=%s%s%s%s%s%s",
+                        sta->sta_idx, mac,
+                        PRINT_STA_FLAG(AUTHORIZED),
+                        PRINT_STA_FLAG(SHORT_PREAMBLE),
+                        PRINT_STA_FLAG(WME),
+                        PRINT_STA_FLAG(MFP),
+                        PRINT_STA_FLAG(AUTHENTICATED),
+                        PRINT_STA_FLAG(TDLS_PEER));
+#endif
+
+            #undef PRINT_STA_FLAG
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+            ecrnx_debugfs_add_station_in_ap_mode(ecrnx_hw, sta, params);
+#endif
+            break;
+        }
+        default:
+            error = -EBUSY;
+            break;
+    }
+
+    clear_bit(ECRNX_DEV_ADDING_STA, &ecrnx_hw->flags);
+
+    return error;
+}
+
+/**
+ * @del_station: Remove a station
+ */
+static int ecrnx_cfg80211_del_station(struct wiphy *wiphy,
+                                        struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+                                        u8 *mac
+#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0))
+                                        const u8 *mac
+#else
+                                        struct station_del_parameters *params
+#endif
+)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_sta *cur, *tmp;
+    int error = 0, found = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+    const u8 *mac = NULL;
+    if (params)
+        mac = params->mac;
+#endif
+
+    if(list_empty(&ecrnx_vif->ap.sta_list)) {
+        goto end;
+    }
+
+    list_for_each_entry_safe(cur, tmp, &ecrnx_vif->ap.sta_list, list) {
+        if ((!mac) || (!memcmp(cur->mac_addr, mac, ETH_ALEN))) {
+            netdev_info(dev, "Del sta %d (%pM)", cur->sta_idx, cur->mac_addr);
+            ECRNX_PRINT("%s-%d:cur_list:0x%p, vif_list:0x%p, mac:%pM\n", __func__, __LINE__, &cur->list, &ecrnx_vif->ap.sta_list, mac);
+            /* Ensure that we won't process PS change ind */
+            spin_lock_bh(&ecrnx_hw->cb_lock);
+            cur->ps.active = false;
+            cur->valid = false;
+            spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+            if (cur->vif_idx != cur->vlan_idx) {
+                struct ecrnx_vif *vlan_vif;
+                vlan_vif = ecrnx_hw->vif_table[cur->vlan_idx];
+                if (vlan_vif->up) {
+                    if ((ECRNX_VIF_TYPE(vlan_vif) == NL80211_IFTYPE_AP_VLAN) &&
+                        (vlan_vif->use_4addr)) {
+                        vlan_vif->ap_vlan.sta_4a = NULL;
+                    } else {
+                        WARN(1, "Deleting sta belonging to VLAN other than AP_VLAN 4A");
+                    }
+                }
+            }
+
+            ecrnx_txq_sta_deinit(ecrnx_hw, cur);
+            ecrnx_rx_reord_sta_deinit(ecrnx_hw, cur->sta_idx, true);
+
+            error = ecrnx_send_me_sta_del(ecrnx_hw, cur->sta_idx, false);
+            if ((error != 0) && (error != -EPIPE)){
+                ECRNX_WARN("del sta msg send fail, error code:%d \n", error);
+            }
+
+#ifdef CONFIG_ECRNX_ANDRIOD
+            if((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP) || (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_GO)) {
+                if((!is_multicast_sta(cur->sta_idx)) && params) {
+                    if(params->mac){
+                        ECRNX_PRINT("%s-%d:vif:%d, mac:%pM \n", __func__, __LINE__, ECRNX_VIF_TYPE(ecrnx_vif), params->mac);
+                        cfg80211_del_sta(ecrnx_vif->ndev, params->mac, GFP_ATOMIC);
+                    }
+                }
+            }
+#endif
+
+
+#ifdef CONFIG_ECRNX_BFMER
+            // Disable Beamformer if supported
+            ecrnx_bfmer_report_del(ecrnx_hw, cur);
+            ecrnx_mu_group_sta_del(ecrnx_hw, cur);
+#endif /* CONFIG_ECRNX_BFMER */
+
+            list_del(&cur->list);
+            ecrnx_vif->generation++;
+            ecrnx_dbgfs_unregister_sta(ecrnx_hw, cur);
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+            ecrnx_debugfs_sta_in_ap_del(cur->sta_idx);
+#endif
+            found ++;
+            break;
+        }
+    }
+
+end:
+    if ((!found) && (mac))
+        return -ENOENT;
+
+    ecrnx_update_mesh_power_mode(ecrnx_vif);
+
+    return 0;
+}
+
+/**
+ * @change_station: Modify a given station. Note that flags changes are not much
+ *     validated in cfg80211, in particular the auth/assoc/authorized flags
+ *     might come to the driver in invalid combinations -- make sure to check
+ *     them, also against the existing state! Drivers must call
+ *     cfg80211_check_station_change() to validate the information.
+ */
+static int ecrnx_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
+                                        const u8 *mac, struct station_parameters *params)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *vif = netdev_priv(dev);
+    struct ecrnx_sta *sta;
+
+    sta = ecrnx_get_sta(ecrnx_hw, mac);
+    if (!sta)
+    {
+        /* Add the TDLS station */
+        if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+        {
+            struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+            struct me_sta_add_cfm me_sta_add_cfm;
+            int error = 0;
+
+            /* Indicate we are in a STA addition process - This will allow handling
+             * potential PS mode change indications correctly
+             */
+            set_bit(ECRNX_DEV_ADDING_STA, &ecrnx_hw->flags);
+
+            /* Forward the information to the LMAC */
+            if ((error = ecrnx_send_me_sta_add(ecrnx_hw, params, mac, ecrnx_vif->vif_index,
+                                              &me_sta_add_cfm)))
+                return error;
+
+            // Check the status
+            switch (me_sta_add_cfm.status)
+            {
+                case CO_OK:
+                {
+                    int tid;
+                    sta = &ecrnx_hw->sta_table[me_sta_add_cfm.sta_idx];
+                    memset(&sta->rx_pn, 0, TID_MAX * sizeof(uint64_t));
+                    sta->aid = params->aid;
+                    sta->sta_idx = me_sta_add_cfm.sta_idx;
+                    sta->ch_idx = ecrnx_vif->ch_index;
+                    sta->vif_idx = ecrnx_vif->vif_index;
+                    sta->vlan_idx = sta->vif_idx;
+                    sta->qos = (params->sta_flags_set & BIT(NL80211_STA_FLAG_WME)) != 0;
+                    sta->ht = params->ht_capa ? 1 : 0;
+                    sta->vht = params->vht_capa ? 1 : 0;
+                    sta->acm = 0;
+                    for (tid = 0; tid < NX_NB_TXQ_PER_STA; tid++) {
+                        int uapsd_bit = ecrnx_hwq2uapsd[ecrnx_tid2hwq[tid]];
+                        if (params->uapsd_queues & uapsd_bit)
+                            sta->uapsd_tids |= 1 << tid;
+                        else
+                            sta->uapsd_tids &= ~(1 << tid);
+                    }
+                    memcpy(sta->mac_addr, mac, ETH_ALEN);
+                    ecrnx_dbgfs_register_sta(ecrnx_hw, sta);
+
+                    /* Ensure that we won't process PS change or channel switch ind*/
+                    spin_lock_bh(&ecrnx_hw->cb_lock);
+                    ecrnx_txq_sta_init(ecrnx_hw, sta, ecrnx_txq_vif_get_status(ecrnx_vif));
+                    ecrnx_rx_reord_sta_init(ecrnx_hw, ecrnx_vif, sta->sta_idx);
+                    if (ecrnx_vif->tdls_status == TDLS_SETUP_RSP_TX) {
+                        ecrnx_vif->tdls_status = TDLS_LINK_ACTIVE;
+                        sta->tdls.initiator = true;
+                        sta->tdls.active = true;
+                    }
+                    /* Set TDLS channel switch capability */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+                    if ((params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) &&
+                        !ecrnx_vif->tdls_chsw_prohibited)
+#else
+                    if (!ecrnx_vif->tdls_chsw_prohibited)
+#endif
+                        sta->tdls.chsw_allowed = true;
+                    ecrnx_vif->sta.tdls_sta = sta;
+                    sta->valid = true;
+                    spin_unlock_bh(&ecrnx_hw->cb_lock);
+#ifdef CONFIG_ECRNX_BFMER
+                    if (ecrnx_hw->mod_params->bfmer)
+                        ecrnx_send_bfmer_enable(ecrnx_hw, sta, params->vht_capa);
+
+                    ecrnx_mu_group_sta_init(sta, NULL);
+#endif /* CONFIG_ECRNX_BFMER */
+
+                    #define PRINT_STA_FLAG(f)                               \
+                        (params->sta_flags_set & BIT(NL80211_STA_FLAG_##f) ? "["#f"]" : "")
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+                    netdev_info(dev, "Add %s TDLS sta %d (%pM) flags=%s%s%s%s%s%s%s",
+                                sta->tdls.initiator ? "initiator" : "responder",
+                                sta->sta_idx, mac,
+                                PRINT_STA_FLAG(AUTHORIZED),
+                                PRINT_STA_FLAG(SHORT_PREAMBLE),
+                                PRINT_STA_FLAG(WME),
+                                PRINT_STA_FLAG(MFP),
+                                PRINT_STA_FLAG(AUTHENTICATED),
+                                PRINT_STA_FLAG(TDLS_PEER),
+                                PRINT_STA_FLAG(ASSOCIATED));
+#else
+                    netdev_info(dev, "Add %s TDLS sta %d (%pM) flags=%s%s%s%s%s%s",
+                            sta->tdls.initiator ? "initiator" : "responder",
+                            sta->sta_idx, mac,
+                            PRINT_STA_FLAG(AUTHORIZED),
+                            PRINT_STA_FLAG(SHORT_PREAMBLE),
+                            PRINT_STA_FLAG(WME),
+                            PRINT_STA_FLAG(MFP),
+                            PRINT_STA_FLAG(AUTHENTICATED),
+                            PRINT_STA_FLAG(TDLS_PEER));
+#endif
+                    #undef PRINT_STA_FLAG
+
+                    break;
+                }
+                default:
+                    error = -EBUSY;
+                    break;
+            }
+
+            clear_bit(ECRNX_DEV_ADDING_STA, &ecrnx_hw->flags);
+        } else  {
+            return -EINVAL;
+        }
+    }
+
+    if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))
+        ecrnx_send_me_set_control_port_req(ecrnx_hw,
+                (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) != 0,
+                sta->sta_idx);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MESH_POINT) {
+        if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) {
+            if (params->plink_state < NUM_NL80211_PLINK_STATES) {
+                ecrnx_send_mesh_peer_update_ntf(ecrnx_hw, vif, sta->sta_idx, params->plink_state);
+            }
+        }
+
+        if (params->local_pm != NL80211_MESH_POWER_UNKNOWN) {
+            sta->mesh_pm = params->local_pm;
+            ecrnx_update_mesh_power_mode(vif);
+        }
+    }
+#endif
+
+    if (params->vlan) {
+        uint8_t vlan_idx;
+
+        vif = netdev_priv(params->vlan);
+        vlan_idx = vif->vif_index;
+
+        if (sta->vlan_idx != vlan_idx) {
+            struct ecrnx_vif *old_vif;
+            old_vif = ecrnx_hw->vif_table[sta->vlan_idx];
+            ecrnx_txq_sta_switch_vif(sta, old_vif, vif);
+            sta->vlan_idx = vlan_idx;
+
+            if ((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_AP_VLAN) &&
+                (vif->use_4addr)) {
+                WARN((vif->ap_vlan.sta_4a),
+                     "4A AP_VLAN interface with more than one sta");
+                vif->ap_vlan.sta_4a = sta;
+            }
+
+            if ((ECRNX_VIF_TYPE(old_vif) == NL80211_IFTYPE_AP_VLAN) &&
+                (old_vif->use_4addr)) {
+                old_vif->ap_vlan.sta_4a = NULL;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/**
+ * @start_ap: Start acting in AP mode defined by the parameters.
+ */
+static int ecrnx_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
+                                  struct cfg80211_ap_settings *settings)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct apm_start_cfm apm_start_cfm;
+    struct ecrnx_ipc_elem_var elem;
+    struct ecrnx_sta *sta;
+    int error = 0;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* Forward the information to the LMAC */
+    if ((error = ecrnx_send_apm_start_req(ecrnx_hw, ecrnx_vif, settings,
+                                         &apm_start_cfm, &elem)))
+        goto end;
+
+    // Check the status
+    switch (apm_start_cfm.status)
+    {
+        case CO_OK:
+        {
+            u8 txq_status = 0;
+            ecrnx_vif->ap.bcmc_index = apm_start_cfm.bcmc_idx;
+            ecrnx_vif->ap.flags = 0;
+            ecrnx_vif->ap.bcn_interval = settings->beacon_interval;
+            sta = &ecrnx_hw->sta_table[apm_start_cfm.bcmc_idx];
+            sta->valid = true;
+            sta->aid = 0;
+            sta->sta_idx = apm_start_cfm.bcmc_idx;
+            sta->ch_idx = apm_start_cfm.ch_idx;
+            sta->vif_idx = ecrnx_vif->vif_index;
+            sta->qos = false;
+            sta->acm = 0;
+            sta->ps.active = false;
+            sta->listen_interval = 5;
+            ecrnx_mu_group_sta_init(sta, NULL);
+            spin_lock_bh(&ecrnx_hw->cb_lock);
+            ecrnx_chanctx_link(ecrnx_vif, apm_start_cfm.ch_idx,
+                              &settings->chandef);
+            if (ecrnx_hw->cur_chanctx != apm_start_cfm.ch_idx) {
+                txq_status = ECRNX_TXQ_STOP_CHAN;
+            }
+            ecrnx_txq_vif_init(ecrnx_hw, ecrnx_vif, txq_status);
+            spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+            netif_tx_start_all_queues(dev);
+            netif_carrier_on(dev);
+            error = 0;
+            /* If the AP channel is already the active, we probably skip radar
+               activation on MM_CHANNEL_SWITCH_IND (unless another vif use this
+               ctxt). In anycase retest if radar detection must be activated
+             */
+            if (txq_status == 0) {
+                ecrnx_radar_detection_enable_on_cur_channel(ecrnx_hw);
+            }
+            break;
+        }
+        case CO_BUSY:
+            error = -EINPROGRESS;
+            break;
+        case CO_OP_IN_PROGRESS:
+            error = -EALREADY;
+            break;
+        default:
+            error = -EIO;
+            break;
+    }
+
+    if (error) {
+        netdev_info(dev, "Failed to start AP (%d)", error);
+    } else {
+        netdev_info(dev, "AP started: ch=%d, bcmc_idx=%d",
+                    ecrnx_vif->ch_index, ecrnx_vif->ap.bcmc_index);
+    }
+
+  end:
+    ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &elem);
+
+    return error;
+}
+
+
+/**
+ * @change_beacon: Change the beacon parameters for an access point mode
+ *     interface. This should reject the call when AP mode wasn't started.
+ */
+static int ecrnx_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
+                                       struct cfg80211_beacon_data *info)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *vif = netdev_priv(dev);
+    struct ecrnx_bcn *bcn = &vif->ap.bcn;
+    struct ecrnx_ipc_elem_var elem;
+    u8 *buf;
+    int error = 0;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    // Build the beacon
+    buf = ecrnx_build_bcn(bcn, info);
+    if (!buf)
+        return -ENOMEM;
+
+    // Sync buffer for FW
+    if ((error = ecrnx_ipc_elem_var_allocs(ecrnx_hw, &elem, bcn->len, DMA_TO_DEVICE,
+                                          buf, NULL, NULL)))
+        return error;
+
+    // Forward the information to the LMAC
+    error = ecrnx_send_bcn_change(ecrnx_hw, vif->vif_index, elem.dma_addr,
+                                 bcn->len, bcn->head_len, bcn->tim_len, NULL);
+
+    ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &elem);
+
+    return error;
+}
+
+/**
+ * * @stop_ap: Stop being an AP, including stopping beaconing.
+ */
+static int ecrnx_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_sta *sta;
+
+    ecrnx_radar_cancel_cac(&ecrnx_hw->radar);
+    ecrnx_send_apm_stop_req(ecrnx_hw, ecrnx_vif);
+    spin_lock_bh(&ecrnx_hw->cb_lock);
+    ecrnx_chanctx_unlink(ecrnx_vif);
+    spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+    /* delete any remaining STA*/
+    while (!list_empty(&ecrnx_vif->ap.sta_list)) {
+        ecrnx_cfg80211_del_station(wiphy, dev, NULL);
+    }
+
+    /* delete BC/MC STA */
+    sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+    ecrnx_txq_vif_deinit(ecrnx_hw, ecrnx_vif);
+    ecrnx_del_bcn(&ecrnx_vif->ap.bcn);
+    ecrnx_del_csa(ecrnx_vif);
+
+    netif_tx_stop_all_queues(dev);
+    netif_carrier_off(dev);
+
+    netdev_info(dev, "AP Stopped");
+
+    return 0;
+}
+
+/**
+ * @set_monitor_channel: Set the monitor mode channel for the device. If other
+ *     interfaces are active this callback should reject the configuration.
+ *     If no interfaces are active or the device is down, the channel should
+ *     be stored for when a monitor interface becomes active.
+ *
+ * Also called internaly with chandef set to NULL simply to retrieve the channel
+ * configured at firmware level.
+ */
+static int ecrnx_cfg80211_set_monitor_channel(struct wiphy *wiphy,
+                                             struct cfg80211_chan_def *chandef)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif;
+    struct me_config_monitor_cfm cfm;
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if (ecrnx_hw->monitor_vif == ECRNX_INVALID_VIF)
+        return -EINVAL;
+
+    ecrnx_vif = ecrnx_hw->vif_table[ecrnx_hw->monitor_vif];
+
+    // Do nothing if monitor interface is already configured with the requested channel
+    if (ecrnx_chanctx_valid(ecrnx_hw, ecrnx_vif->ch_index)) {
+        struct ecrnx_chanctx *ctxt;
+        ctxt = &ecrnx_vif->ecrnx_hw->chanctx_table[ecrnx_vif->ch_index];
+        if (chandef && cfg80211_chandef_identical(&ctxt->chan_def, chandef))
+            return 0;
+    }
+
+    // Always send command to firmware. It allows to retrieve channel context index
+    // and its configuration.
+    if (ecrnx_send_config_monitor_req(ecrnx_hw, chandef, &cfm))
+        return -EIO;
+
+    // Always re-set channel context info
+    ecrnx_chanctx_unlink(ecrnx_vif);
+
+
+
+    // If there is also a STA interface not yet connected then monitor interface
+    // will only have a channel context after the connection of the STA interface.
+    if (cfm.chan_index != ECRNX_CH_NOT_SET)
+    {
+        struct cfg80211_chan_def mon_chandef;
+
+        if (ecrnx_hw->vif_started > 1) {
+            // In this case we just want to update the channel context index not
+            // the channel configuration
+            ecrnx_chanctx_link(ecrnx_vif, cfm.chan_index, NULL);
+            return -EBUSY;
+        }
+
+        mon_chandef.chan = ieee80211_get_channel(wiphy, cfm.chan.prim20_freq);
+        mon_chandef.center_freq1 = cfm.chan.center1_freq;
+        mon_chandef.center_freq2 = cfm.chan.center2_freq;
+        mon_chandef.width =  chnl2bw[cfm.chan.type];
+        ecrnx_chanctx_link(ecrnx_vif, cfm.chan_index, &mon_chandef);
+    }
+
+    return 0;
+}
+
+/**
+ * @probe_client: probe an associated client, must return a cookie that it
+ *     later passes to cfg80211_probe_status().
+ */
+int ecrnx_cfg80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
+            const u8 *peer, u64 *cookie)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *vif = netdev_priv(dev);
+    struct ecrnx_sta *sta = NULL;
+    struct apm_probe_client_cfm cfm;
+    if ((ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP) &&
+        (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_AP_VLAN) &&
+        (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_P2P_GO) &&
+        (ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_MESH_POINT))
+        return -EINVAL;
+    list_for_each_entry(sta, &vif->ap.sta_list, list) {
+        if (sta->valid && ether_addr_equal(sta->mac_addr, peer))
+            break;
+}
+
+    if (!sta)
+        return -EINVAL;
+
+    ecrnx_send_apm_probe_req(ecrnx_hw, vif, sta, &cfm);
+
+    if (cfm.status != CO_OK)
+        return -EINVAL;
+
+    *cookie = (u64)cfm.probe_id;
+    return 0;
+}
+
+/**
+ * @set_wiphy_params: Notify that wiphy parameters have changed;
+ *     @changed bitfield (see &enum wiphy_params_flags) describes which values
+ *     have changed. The actual parameter values are available in
+ *     struct wiphy. If returning an error, no value should be changed.
+ */
+static int ecrnx_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+    return 0;
+}
+
+
+/**
+ * @set_tx_power: set the transmit power according to the parameters,
+ *     the power passed is in mBm, to get dBm use MBM_TO_DBM(). The
+ *     wdev may be %NULL if power was set for the wiphy, and will
+ *     always be %NULL unless the driver supports per-vif TX power
+ *     (as advertised by the nl80211 feature flag.)
+ */
+static int ecrnx_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
+                                      enum nl80211_tx_power_setting type, int mbm)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *vif;
+    s8 pwr;
+    int res = 0;
+
+    if (type == NL80211_TX_POWER_AUTOMATIC) {
+        pwr = 0x7f;
+    } else {
+        pwr = MBM_TO_DBM(mbm);
+    }
+
+    if (wdev) {
+        vif = container_of(wdev, struct ecrnx_vif, wdev);
+        res = ecrnx_send_set_power(ecrnx_hw, vif->vif_index, pwr, NULL);
+    } else {
+        list_for_each_entry(vif, &ecrnx_hw->vifs, list) {
+            res = ecrnx_send_set_power(ecrnx_hw, vif->vif_index, pwr, NULL);
+            if (res)
+                break;
+        }
+    }
+
+    return res;
+}
+
+/**
+ * @set_power_mgmt: set the power save to one of those two modes:
+ *  Power-save off
+ *  Power-save on - Dynamic mode
+ */
+static int ecrnx_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+                                        struct net_device *dev,
+                                        bool enabled, int timeout)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    u8 ps_mode;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    if (timeout >= 0)
+        netdev_info(dev, "Ignore timeout value %d", timeout);
+
+    if (!(ecrnx_hw->version_cfm.features & BIT(MM_FEAT_PS_BIT)))
+        enabled = false;
+
+    if (enabled) {
+        /* Switch to Dynamic Power Save */
+        ps_mode = MM_PS_MODE_ON_DYN;
+    } else {
+        /* Exit Power Save */
+        ps_mode = MM_PS_MODE_OFF;
+    }
+
+    return ecrnx_send_me_set_ps_mode(ecrnx_hw, ps_mode);
+}
+
+static int ecrnx_cfg80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev,
+                                        struct ieee80211_txq_params *params)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    u8 hw_queue, aifs, cwmin, cwmax;
+    u32 param;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    hw_queue = ecrnx_ac2hwq[0][params->ac];
+
+    aifs  = params->aifs;
+    cwmin = fls(params->cwmin);
+    cwmax = fls(params->cwmax);
+
+    /* Store queue information in general structure */
+    param  = (u32) (aifs << 0);
+    param |= (u32) (cwmin << 4);
+    param |= (u32) (cwmax << 8);
+    param |= (u32) (params->txop) << 12;
+
+    /* Send the MM_SET_EDCA_REQ message to the FW */
+    return ecrnx_send_set_edca(ecrnx_hw, hw_queue, param, false, ecrnx_vif->vif_index);
+}
+
+
+/**
+ * @remain_on_channel: Request the driver to remain awake on the specified
+ *     channel for the specified duration to complete an off-channel
+ *     operation (e.g., public action frame exchange). When the driver is
+ *     ready on the requested channel, it must indicate this with an event
+ *     notification by calling cfg80211_ready_on_channel().
+ */
+static int
+ecrnx_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
+                                struct ieee80211_channel *chan,
+                                unsigned int duration, u64 *cookie)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(wdev->netdev);
+    struct ecrnx_roc *roc;
+    int error;
+    unsigned long timer = 0;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* For debug purpose (use ftrace kernel option) */
+    trace_roc(ecrnx_vif->vif_index, chan->center_freq, duration);
+
+    /* Check that no other RoC procedure has been launched */
+    if (ecrnx_hw->roc)
+    {
+        ECRNX_ERR("%s-%d,statu error!!!, duration:%d \n", __func__, __LINE__, duration);
+        //exp_report(ecrnx_hw);
+            //wait for slave confirm
+        //ecrnx_hw->p2p_listen.rxdatas = 0;
+#ifdef CONFIG_ECRNX_P2P
+               timer = wait_event_interruptible_timeout(ecrnx_hw->p2p_listen.rxdataq, (ecrnx_hw->roc == NULL), HZ/2);
+               if (timer)
+                       ECRNX_PRINT("wait_event: wake up!!! timer:%ld \n", timer);
+               else
+                       ECRNX_PRINT("wait_event: timout!!!\n");
+#endif
+        //return -EBUSY;
+    }
+
+    /* Allocate a temporary RoC element */
+    roc = kmalloc(sizeof(struct ecrnx_roc), GFP_KERNEL);
+
+    /* Verify that element has well been allocated */
+    if (!roc)
+        return -ENOMEM;
+
+    /* Initialize the RoC information element */
+    roc->vif = ecrnx_vif;
+    roc->chan = chan;
+    roc->duration = duration;
+    roc->internal = false;
+    roc->on_chan = false;
+
+    /* Initialize the OFFCHAN TX queue to allow off-channel transmissions */
+    ecrnx_txq_offchan_init(ecrnx_vif);
+
+    /* Forward the information to the FMAC */
+    ecrnx_hw->roc = roc;
+    error = ecrnx_send_roc(ecrnx_hw, ecrnx_vif, chan, duration);
+
+    /* If no error, keep all the information for handling of end of procedure */
+    if (error == 0) {
+
+        /* Set the cookie value */
+        *cookie = (u64)(ecrnx_hw->roc_cookie);
+
+#ifdef CONFIG_ECRNX_P2P
+               if(ecrnx_vif->mgmt_reg_stypes & BIT(IEEE80211_STYPE_PROBE_REQ >> 4))
+               {
+                       if(ecrnx_send_p2p_start_listen_req(ecrnx_hw, ecrnx_vif, duration))
+                               ECRNX_ERR("P2P: start_listen failed\n");
+               }
+#endif
+    } else {
+        kfree(roc);
+        ecrnx_hw->roc = NULL;
+        ecrnx_txq_offchan_deinit(ecrnx_vif);
+    }
+
+    return error;
+}
+
+/**
+ * @cancel_remain_on_channel: Cancel an on-going remain-on-channel operation.
+ *     This allows the operation to be terminated prior to timeout based on
+ *     the duration value.
+ */
+static int ecrnx_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
+                                                  struct wireless_dev *wdev,
+                                                  u64 cookie)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(wdev->netdev);
+       int error;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    /* For debug purpose (use ftrace kernel option) */
+    trace_cancel_roc(ecrnx_vif->vif_index);
+
+    /* Check if a RoC procedure is pending */
+    if (!ecrnx_hw->roc)
+        return 0;
+#ifdef CONFIG_ECRNX_P2P
+       //if(ecrnx_vif->mgmt_reg_stypes & BIT(IEEE80211_STYPE_PROBE_REQ >> 4))
+       {
+               error = ecrnx_send_p2p_cancel_listen_req(ecrnx_hw, ecrnx_vif);
+               if(error == 0)
+                       ECRNX_PRINT("P2P: cancel_listen OK!!!\n");
+               else
+                       ECRNX_ERR("P2P: cancel_listen failed, error=%d\n", error);
+       }
+#endif
+    /* Forward the information to the FMAC */
+    return ecrnx_send_cancel_roc(ecrnx_hw);
+}
+
+/**
+ * @dump_survey: get site survey information.
+ */
+static int ecrnx_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *netdev,
+                                     int idx, struct survey_info *info)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ieee80211_supported_band *sband;
+    struct ecrnx_survey_info *ecrnx_survey;
+
+    //ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if (idx >= ARRAY_SIZE(ecrnx_hw->survey))
+        return -ENOENT;
+
+    ecrnx_survey = &ecrnx_hw->survey[idx];
+
+    // Check if provided index matches with a supported 2.4GHz channel
+    sband = wiphy->bands[NL80211_BAND_2GHZ];
+    if (sband && idx >= sband->n_channels) {
+        idx -= sband->n_channels;
+        sband = NULL;
+    }
+
+    if (!sband) {
+#ifdef CONFIG_ECRNX_5G
+        // Check if provided index matches with a supported 5GHz channel
+        sband = wiphy->bands[NL80211_BAND_5GHZ];
+#endif
+        if (!sband || idx >= sband->n_channels)
+            return -ENOENT;
+    }
+
+    // Fill the survey
+    info->channel = &sband->channels[idx];
+    info->filled = ecrnx_survey->filled;
+
+    if (ecrnx_survey->filled != 0) {
+        SURVEY_TIME(info) = (u64)ecrnx_survey->chan_time_ms;
+        SURVEY_TIME(info) = (u64)ecrnx_survey->chan_time_busy_ms;
+        info->noise = ecrnx_survey->noise_dbm;
+
+        // Set the survey report as not used
+        ecrnx_survey->filled = 0;
+    }
+
+    return 0;
+}
+
+/**
+ * @get_channel: Get the current operating channel for the virtual interface.
+ *     For monitor interfaces, it should return %NULL unless there's a single
+ *     current monitoring channel.
+ */
+int ecrnx_cfg80211_get_channel(struct wiphy *wiphy,
+                                     struct wireless_dev *wdev,
+                                     struct cfg80211_chan_def *chandef) {
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = container_of(wdev, struct ecrnx_vif, wdev);
+    struct ecrnx_chanctx *ctxt;
+
+    if (!ecrnx_vif->up) {
+        return -ENODATA;
+    }
+
+    if (ecrnx_vif->vif_index == ecrnx_hw->monitor_vif)
+    {
+        //retrieve channel from firmware
+        ecrnx_cfg80211_set_monitor_channel(wiphy, NULL);
+    }
+
+    //Check if channel context is valid
+    if(!ecrnx_chanctx_valid(ecrnx_hw, ecrnx_vif->ch_index)){
+        return -ENODATA;
+    }
+
+    ctxt = &ecrnx_hw->chanctx_table[ecrnx_vif->ch_index];
+    *chandef = ctxt->chan_def;
+
+    return 0;
+}
+
+/**
+ * @mgmt_tx: Transmit a management frame.
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+static int ecrnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+                                 struct cfg80211_mgmt_tx_params *params,
+                                 u64 *cookie)
+#else
+static int ecrnx_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+                                 struct ieee80211_channel *channel, bool offchan,
+                                 unsigned int wait, const u8* buf, size_t len,
+                            #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+                                 bool no_cck,
+                            #endif
+                            #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+                                 bool dont_wait_for_ack,
+                            #endif
+                                 u64 *cookie)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(wdev->netdev);
+    struct ecrnx_sta *ecrnx_sta;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+    struct ieee80211_channel *channel = params->chan;
+    const u8 *buf = params->buf;
+    bool offchan = false;
+#endif
+    struct ieee80211_mgmt *mgmt = (void *)buf;
+    bool ap = false;
+
+    /* Check if provided VIF is an AP or a STA one */
+    switch (ECRNX_VIF_TYPE(ecrnx_vif)) {
+        case NL80211_IFTYPE_AP_VLAN:
+            ecrnx_vif = ecrnx_vif->ap_vlan.master;
+            break;
+        case NL80211_IFTYPE_AP:
+        case NL80211_IFTYPE_P2P_GO:
+        case NL80211_IFTYPE_MESH_POINT:
+            ap = true;
+            break;
+        case NL80211_IFTYPE_STATION:
+        case NL80211_IFTYPE_P2P_CLIENT:
+        default:
+            break;
+    }
+
+    /* Get STA on which management frame has to be sent */
+    ecrnx_sta = ecrnx_retrieve_sta(ecrnx_hw, ecrnx_vif, mgmt->da,
+                                 mgmt->frame_control, ap);
+
+    trace_mgmt_tx((channel) ? channel->center_freq : 0,
+                  ecrnx_vif->vif_index, (ecrnx_sta) ? ecrnx_sta->sta_idx : 0xFF,
+                  mgmt);
+
+    if (ap || ecrnx_sta)
+        goto send_frame;
+
+    /* Not an AP interface sending frame to unknown STA:
+     * This is allowed for external authetication */
+    if ((ecrnx_vif->sta.flags & ECRNX_STA_EXT_AUTH) && ieee80211_is_auth(mgmt->frame_control))
+        goto send_frame;
+
+       if(ieee80211_is_probe_resp(mgmt->frame_control))
+               goto p2p_send_frame;
+
+    /* Otherwise ROC is needed */
+    if (!channel)
+        return -EINVAL;
+
+    /* Check that a RoC is already pending */
+    if (ecrnx_hw->roc) {
+        /* Get VIF used for current ROC */
+
+        /* Check if RoC channel is the same than the required one */
+        if ((ecrnx_hw->roc->vif != ecrnx_vif) ||
+            (ecrnx_hw->roc->chan->center_freq != channel->center_freq))
+            return -EINVAL;
+
+    } else {
+        u64 cookie;
+        int error;
+
+        /* Start a ROC procedure for 30ms */
+        error = ecrnx_cfg80211_remain_on_channel(wiphy, wdev, channel,
+                                                30, &cookie);
+        if (error)
+            return error;
+
+        /* Need to keep in mind that RoC has been launched internally in order to
+         * avoid to call the cfg80211 callback once expired */
+        ecrnx_hw->roc->internal = true;
+#ifdef CONFIG_ECRNX_P2P
+               ecrnx_hw->p2p_listen.rxdatas = 0;
+               wait_event_interruptible_timeout(ecrnx_hw->p2p_listen.rxdataq, ecrnx_hw->p2p_listen.rxdatas, HZ);
+#endif
+    }
+
+p2p_send_frame:
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+    offchan = true;
+#endif
+
+send_frame:
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+    return ecrnx_start_mgmt_xmit(ecrnx_vif, ecrnx_sta, params, offchan, cookie);
+#else
+    return ecrnx_start_mgmt_xmit(ecrnx_vif, ecrnx_sta, channel, offchan, wait, buf, len, no_cck, dont_wait_for_ack, cookie);
+#endif
+}
+
+/**
+ * @start_radar_detection: Start radar detection in the driver.
+ */
+static int ecrnx_cfg80211_start_radar_detection(struct wiphy *wiphy,
+                                        struct net_device *dev,
+                                        struct cfg80211_chan_def *chandef,
+                                        u32 cac_time_ms)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct apm_start_cac_cfm cfm;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
+    ecrnx_radar_start_cac(&ecrnx_hw->radar, cac_time_ms, ecrnx_vif);
+#endif
+    ecrnx_send_apm_start_cac_req(ecrnx_hw, ecrnx_vif, chandef, &cfm);
+
+    if (cfm.status == CO_OK) {
+        spin_lock_bh(&ecrnx_hw->cb_lock);
+        ecrnx_chanctx_link(ecrnx_vif, cfm.ch_idx, chandef);
+        if (ecrnx_hw->cur_chanctx == ecrnx_vif->ch_index)
+            ecrnx_radar_detection_enable(&ecrnx_hw->radar,
+                                        ECRNX_RADAR_DETECT_REPORT,
+                                        ECRNX_RADAR_RIU);
+        spin_unlock_bh(&ecrnx_hw->cb_lock);
+    } else {
+        return -EIO;
+    }
+
+    return 0;
+}
+
+/**
+ * @update_ft_ies: Provide updated Fast BSS Transition information to the
+ *     driver. If the SME is in the driver/firmware, this information can be
+ *     used in building Authentication and Reassociation Request frames.
+ */
+static int ecrnx_cfg80211_update_ft_ies(struct wiphy *wiphy,
+                            struct net_device *dev,
+                            struct cfg80211_update_ft_ies_params *ftie)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *vif = netdev_priv(dev);
+    const struct ecrnx_element *rsne = NULL, *mde = NULL, *fte = NULL, *elem;
+    bool ft_in_non_rsn = false;
+    int fties_len = 0;
+    u8 *ft_assoc_ies, *pos;
+    if ((ECRNX_VIF_TYPE(vif) != NL80211_IFTYPE_STATION) ||
+        (vif->sta.ft_assoc_ies == NULL))
+        return 0;
+    for_each_ecrnx_element(elem, ftie->ie, ftie->ie_len) {
+        if (elem->id == WLAN_EID_RSN)
+            rsne = elem;
+        else if (elem->id == WLAN_EID_MOBILITY_DOMAIN)
+            mde = elem;
+        else if (elem->id == WLAN_EID_FAST_BSS_TRANSITION)
+            fte = elem;
+        else
+            netdev_warn(dev, "Unexpected FT element %d\n", elem->id);
+    }
+    if (!mde) {
+        netdev_warn(dev, "Didn't find Mobility_Domain Element\n");
+        return 0;
+    } else if (!rsne && !fte) {
+        ft_in_non_rsn = true;
+    } else if (!rsne || !fte) {
+        netdev_warn(dev, "Didn't find RSN or Fast Transition Element\n");
+        return 0;
+    }
+    for_each_ecrnx_element(elem, vif->sta.ft_assoc_ies, vif->sta.ft_assoc_ies_len) {
+        if ((elem->id == WLAN_EID_RSN) ||
+            (elem->id == WLAN_EID_MOBILITY_DOMAIN) ||
+            (elem->id == WLAN_EID_FAST_BSS_TRANSITION))
+            fties_len += elem->datalen + sizeof(struct ecrnx_element);
+    }
+    ft_assoc_ies = kmalloc(vif->sta.ft_assoc_ies_len - fties_len + ftie->ie_len,
+                        GFP_KERNEL);
+    if (!ft_assoc_ies) {
+        netdev_warn(dev, "Fail to allocate buffer for association elements");
+    }
+    pos = ft_assoc_ies;
+    for_each_ecrnx_element(elem, vif->sta.ft_assoc_ies, vif->sta.ft_assoc_ies_len) {
+        if (elem->id == WLAN_EID_RSN) {
+            if (ft_in_non_rsn) {
+                netdev_warn(dev, "Found RSN element in non RSN FT");
+                goto abort;
+            } else if (!rsne) {
+                netdev_warn(dev, "Found several RSN element");
+                goto abort;
+            } else {
+                memcpy(pos, rsne, sizeof(*rsne) + rsne->datalen);
+                pos += sizeof(*rsne) + rsne->datalen;
+                rsne = NULL;
+            }
+        } else if (elem->id == WLAN_EID_MOBILITY_DOMAIN) {
+            if (!mde) {
+                netdev_warn(dev, "Found several Mobility Domain element");
+                goto abort;
+            } else {
+                memcpy(pos, mde, sizeof(*mde) + mde->datalen);
+                pos += sizeof(*mde) + mde->datalen;
+                mde = NULL;
+            }
+        }
+        else if (elem->id == WLAN_EID_FAST_BSS_TRANSITION) {
+            if (ft_in_non_rsn) {
+                netdev_warn(dev, "Found Fast Transition element in non RSN FT");
+                goto abort;
+            } else if (!fte) {
+                netdev_warn(dev, "found several Fast Transition element");
+                goto abort;
+            } else {
+                memcpy(pos, fte, sizeof(*fte) + fte->datalen);
+                pos += sizeof(*fte) + fte->datalen;
+                fte = NULL;
+            }
+        }
+        else {
+            if (fte && !mde) {
+                memcpy(pos, fte, sizeof(*fte) + fte->datalen);
+                pos += sizeof(*fte) + fte->datalen;
+                fte = NULL;
+            }
+            memcpy(pos, elem, sizeof(*elem) + elem->datalen);
+            pos += sizeof(*elem) + elem->datalen;
+        }
+    }
+    if (fte) {
+        memcpy(pos, fte, sizeof(*fte) + fte->datalen);
+        pos += sizeof(*fte) + fte->datalen;
+        fte = NULL;
+    }
+    kfree(vif->sta.ft_assoc_ies);
+    vif->sta.ft_assoc_ies = ft_assoc_ies;
+    vif->sta.ft_assoc_ies_len = pos - ft_assoc_ies;
+    if (vif->sta.flags & ECRNX_STA_FT_OVER_DS) {
+        struct sm_connect_cfm sm_connect_cfm;
+        struct cfg80211_connect_params sme;
+        memset(&sme, 0, sizeof(sme));
+        rsne = cfg80211_find_ecrnx_elem(WLAN_EID_RSN, vif->sta.ft_assoc_ies,
+                                  vif->sta.ft_assoc_ies_len);
+        if (rsne && ecrnx_rsne_to_connect_params(rsne, &sme)) {
+            netdev_warn(dev, "FT RSN parsing failed\n");
+            return 0;
+        }
+        sme.ssid_len = vif->sta.ft_assoc_ies[1];
+        sme.ssid = &vif->sta.ft_assoc_ies[2];
+        sme.bssid = vif->sta.ft_target_ap;
+        sme.ie = &vif->sta.ft_assoc_ies[2 + sme.ssid_len];
+        sme.ie_len = vif->sta.ft_assoc_ies_len - (2 + sme.ssid_len);
+        sme.auth_type = NL80211_AUTHTYPE_FT;
+        ecrnx_send_sm_connect_req(ecrnx_hw, vif, &sme, &sm_connect_cfm);
+        vif->sta.flags &= ~ECRNX_STA_FT_OVER_DS;
+    } else if (vif->sta.flags & ECRNX_STA_FT_OVER_AIR) {
+        uint8_t ssid_len;
+        vif->sta.flags &= ~ECRNX_STA_FT_OVER_AIR;
+        ssid_len = vif->sta.ft_assoc_ies[1] + 2;
+        if (ecrnx_send_sm_ft_auth_rsp(ecrnx_hw, vif, &vif->sta.ft_assoc_ies[ssid_len],
+                                     vif->sta.ft_assoc_ies_len - ssid_len))
+            netdev_err(dev, "FT Over Air: Failed to send updated assoc elem\n");
+    }
+    return 0;
+abort:
+    kfree(ft_assoc_ies);
+    return 0;
+}
+
+/**
+ * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
+ */
+static int ecrnx_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
+                                  struct net_device *dev,
+                                  int32_t rssi_thold, uint32_t rssi_hyst)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+
+    return ecrnx_send_cfg_rssi_req(ecrnx_hw, ecrnx_vif->vif_index, rssi_thold, rssi_hyst);
+}
+
+/**
+ *
+ * @channel_switch: initiate channel-switch procedure (with CSA). Driver is
+ *     responsible for veryfing if the switch is possible. Since this is
+ *     inherently tricky driver may decide to disconnect an interface later
+ *     with cfg80211_stop_iface(). This doesn't mean driver can accept
+ *     everything. It should do it's best to verify requests and reject them
+ *     as soon as possible.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+static int ecrnx_cfg80211_channel_switch(struct wiphy *wiphy,
+                                 struct net_device *dev,
+                                 struct cfg80211_csa_settings *params)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *vif = netdev_priv(dev);
+    struct ecrnx_ipc_elem_var elem;
+    struct ecrnx_bcn *bcn, *bcn_after;
+    struct ecrnx_csa *csa;
+    u16 csa_oft[BCN_MAX_CSA_CPT];
+    u8 *buf;
+    int i, error = 0;
+
+
+    if (vif->ap.csa)
+        return -EBUSY;
+
+    if (params->n_counter_offsets_beacon > BCN_MAX_CSA_CPT)
+        return -EINVAL;
+
+    /* Build the new beacon with CSA IE */
+    bcn = &vif->ap.bcn;
+    buf = ecrnx_build_bcn(bcn, &params->beacon_csa);
+    if (!buf)
+        return -ENOMEM;
+
+    memset(csa_oft, 0, sizeof(csa_oft));
+    for (i = 0; i < params->n_counter_offsets_beacon; i++)
+    {
+        csa_oft[i] = params->counter_offsets_beacon[i] + bcn->head_len +
+            bcn->tim_len;
+    }
+
+    /* If count is set to 0 (i.e anytime after this beacon) force it to 2 */
+    if (params->count == 0) {
+        params->count = 2;
+        for (i = 0; i < params->n_counter_offsets_beacon; i++)
+        {
+            buf[csa_oft[i]] = 2;
+        }
+    }
+
+    if ((error = ecrnx_ipc_elem_var_allocs(ecrnx_hw, &elem, bcn->len,
+                                          DMA_TO_DEVICE, buf, NULL, NULL))) {
+        goto end;
+    }
+
+    /* Build the beacon to use after CSA. It will only be sent to fw once
+       CSA is over, but do it before sending the beacon as it must be ready
+       when CSA is finished. */
+    csa = kzalloc(sizeof(struct ecrnx_csa), GFP_KERNEL);
+    if (!csa) {
+        error = -ENOMEM;
+        goto end;
+    }
+
+    bcn_after = &csa->bcn;
+    buf = ecrnx_build_bcn(bcn_after, &params->beacon_after);
+    if (!buf) {
+        error = -ENOMEM;
+        ecrnx_del_csa(vif);
+        goto end;
+    }
+
+    if ((error = ecrnx_ipc_elem_var_allocs(ecrnx_hw, &csa->elem, bcn_after->len,
+                                          DMA_TO_DEVICE, buf, NULL, NULL))) {
+        goto end;
+    }
+
+    vif->ap.csa = csa;
+    csa->vif = vif;
+    csa->chandef = params->chandef;
+
+    /* Send new Beacon. FW will extract channel and count from the beacon */
+    error = ecrnx_send_bcn_change(ecrnx_hw, vif->vif_index, elem.dma_addr,
+                                 bcn->len, bcn->head_len, bcn->tim_len, csa_oft);
+
+    if (error) {
+        ecrnx_del_csa(vif);
+        goto end;
+    } else {
+        INIT_WORK(&csa->work, ecrnx_csa_finish);
+        cfg80211_ch_switch_started_notify(dev, &csa->chandef, params->count);
+    }
+
+  end:
+    ecrnx_ipc_elem_var_deallocs(ecrnx_hw, &elem);
+    return error;
+}
+#endif
+
+/*
+ * @tdls_mgmt: prepare TDLS action frame packets and forward them to FW
+ */
+static int ecrnx_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
+                        const u8 *peer, u8 action_code,  u8 dialog_token,
+                        u16 status_code, u32 peer_capability,
+                        bool initiator, const u8 *buf, size_t len)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    int ret = 0;
+
+    /* make sure we support TDLS */
+    if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
+        return -ENOTSUPP;
+
+    /* make sure we are in station mode (and connected) */
+    if ((ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_STATION) ||
+        (!ecrnx_vif->up) || (!ecrnx_vif->sta.ap))
+        return -ENOTSUPP;
+
+    /* only one TDLS link is supported */
+    if ((action_code == WLAN_TDLS_SETUP_REQUEST) &&
+        (ecrnx_vif->sta.tdls_sta) &&
+        (ecrnx_vif->tdls_status == TDLS_LINK_ACTIVE)) {
+        ECRNX_ERR("%s: only one TDLS link is supported!\n", __func__);
+        return -ENOTSUPP;
+    }
+
+    if ((action_code == WLAN_TDLS_DISCOVERY_REQUEST) &&
+        (ecrnx_hw->mod_params->ps_on)) {
+        ECRNX_ERR("%s: discovery request is not supported when "
+                "power-save is enabled!\n", __func__);
+        return -ENOTSUPP;
+    }
+
+    switch (action_code) {
+    case WLAN_TDLS_SETUP_RESPONSE:
+        /* only one TDLS link is supported */
+        if ((status_code == 0) &&
+            (ecrnx_vif->sta.tdls_sta) &&
+            (ecrnx_vif->tdls_status == TDLS_LINK_ACTIVE)) {
+            ECRNX_ERR("%s: only one TDLS link is supported!\n", __func__);
+            status_code = WLAN_STATUS_REQUEST_DECLINED;
+        }
+        /* fall-through */
+    case WLAN_TDLS_SETUP_REQUEST:
+    case WLAN_TDLS_TEARDOWN:
+    case WLAN_TDLS_DISCOVERY_REQUEST:
+    case WLAN_TDLS_SETUP_CONFIRM:
+    case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+        ret = ecrnx_tdls_send_mgmt_packet_data(ecrnx_hw, ecrnx_vif, peer, action_code,
+                dialog_token, status_code, peer_capability, initiator, buf, len, 0, NULL);
+        break;
+
+    default:
+        ECRNX_ERR("%s: Unknown TDLS mgmt/action frame %pM\n",
+                __func__, peer);
+        ret = -EOPNOTSUPP;
+        break;
+    }
+
+    if (action_code == WLAN_TDLS_SETUP_REQUEST) {
+        ecrnx_vif->tdls_status = TDLS_SETUP_REQ_TX;
+    } else if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
+        ecrnx_vif->tdls_status = TDLS_SETUP_RSP_TX;
+    } else if ((action_code == WLAN_TDLS_SETUP_CONFIRM) && (ret == CO_OK)) {
+        ecrnx_vif->tdls_status = TDLS_LINK_ACTIVE;
+        /* Set TDLS active */
+        ecrnx_vif->sta.tdls_sta->tdls.active = true;
+    }
+
+    return ret;
+}
+
+/*
+ * @tdls_oper: execute TDLS operation
+ */
+static int ecrnx_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
+        const u8 *peer, enum nl80211_tdls_operation oper)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    int error;
+
+    if (oper != NL80211_TDLS_DISABLE_LINK)
+        return 0;
+
+    if (!ecrnx_vif->sta.tdls_sta) {
+        ECRNX_ERR("%s: TDLS station %pM does not exist\n", __func__, peer);
+        return -ENOLINK;
+    }
+
+    if (memcmp(ecrnx_vif->sta.tdls_sta->mac_addr, peer, ETH_ALEN) == 0) {
+        /* Disable Channel Switch */
+        if (!ecrnx_send_tdls_cancel_chan_switch_req(ecrnx_hw, ecrnx_vif,
+                                                   ecrnx_vif->sta.tdls_sta,
+                                                   NULL))
+            ecrnx_vif->sta.tdls_sta->tdls.chsw_en = false;
+
+        netdev_info(dev, "Del TDLS sta %d (%pM)",
+                ecrnx_vif->sta.tdls_sta->sta_idx,
+                ecrnx_vif->sta.tdls_sta->mac_addr);
+        /* Ensure that we won't process PS change ind */
+        spin_lock_bh(&ecrnx_hw->cb_lock);
+        ecrnx_vif->sta.tdls_sta->ps.active = false;
+        ecrnx_vif->sta.tdls_sta->valid = false;
+        spin_unlock_bh(&ecrnx_hw->cb_lock);
+        ecrnx_txq_sta_deinit(ecrnx_hw, ecrnx_vif->sta.tdls_sta);
+        error = ecrnx_send_me_sta_del(ecrnx_hw, ecrnx_vif->sta.tdls_sta->sta_idx, true);
+        if ((error != 0) && (error != -EPIPE))
+            return error;
+
+#ifdef CONFIG_ECRNX_BFMER
+            // Disable Beamformer if supported
+            ecrnx_bfmer_report_del(ecrnx_hw, ecrnx_vif->sta.tdls_sta);
+            ecrnx_mu_group_sta_del(ecrnx_hw, ecrnx_vif->sta.tdls_sta);
+#endif /* CONFIG_ECRNX_BFMER */
+
+        /* Set TDLS not active */
+        ecrnx_vif->sta.tdls_sta->tdls.active = false;
+        ecrnx_dbgfs_unregister_sta(ecrnx_hw, ecrnx_vif->sta.tdls_sta);
+        // Remove TDLS station
+        ecrnx_vif->tdls_status = TDLS_LINK_IDLE;
+        ecrnx_vif->sta.tdls_sta = NULL;
+    }
+
+    return 0;
+}
+
+/*
+ * @tdls_channel_switch: enable TDLS channel switch
+ */
+static int ecrnx_cfg80211_tdls_channel_switch(struct wiphy *wiphy,
+                                      struct net_device *dev,
+                                      const u8 *addr, u8 oper_class,
+                                      struct cfg80211_chan_def *chandef)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_sta *ecrnx_sta = ecrnx_vif->sta.tdls_sta;
+    struct tdls_chan_switch_cfm cfm;
+    int error;
+
+    if ((!ecrnx_sta) || (memcmp(addr, ecrnx_sta->mac_addr, ETH_ALEN))) {
+        ECRNX_ERR("%s: TDLS station %pM doesn't exist\n", __func__, addr);
+        return -ENOLINK;
+    }
+
+    if (!ecrnx_sta->tdls.chsw_allowed) {
+        ECRNX_ERR("%s: TDLS station %pM does not support TDLS channel switch\n", __func__, addr);
+        return -ENOTSUPP;
+    }
+
+    error = ecrnx_send_tdls_chan_switch_req(ecrnx_hw, ecrnx_vif, ecrnx_sta,
+                                           ecrnx_sta->tdls.initiator,
+                                           oper_class, chandef, &cfm);
+    if (error)
+        return error;
+
+    if (!cfm.status) {
+        ecrnx_sta->tdls.chsw_en = true;
+        return 0;
+    } else {
+        ECRNX_ERR("%s: TDLS channel switch already enabled and only one is supported\n", __func__);
+        return -EALREADY;
+    }
+}
+
+/*
+ * @tdls_cancel_channel_switch: disable TDLS channel switch
+ */
+static void ecrnx_cfg80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
+                                              struct net_device *dev,
+                                              const u8 *addr)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_sta *ecrnx_sta = ecrnx_vif->sta.tdls_sta;
+    struct tdls_cancel_chan_switch_cfm cfm;
+
+    if (!ecrnx_sta)
+        return;
+
+    if (!ecrnx_send_tdls_cancel_chan_switch_req(ecrnx_hw, ecrnx_vif,
+                                               ecrnx_sta, &cfm))
+        ecrnx_sta->tdls.chsw_en = false;
+}
+
+/**
+ * @change_bss: Modify parameters for a given BSS (mainly for AP mode).
+ */
+static int ecrnx_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev,
+                             struct bss_parameters *params)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    int res =  -EOPNOTSUPP;
+
+    if (((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP) ||
+         (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_GO)) &&
+        (params->ap_isolate > -1)) {
+
+        if (params->ap_isolate)
+            ecrnx_vif->ap.flags |= ECRNX_AP_ISOLATE;
+        else
+            ecrnx_vif->ap.flags &= ~ECRNX_AP_ISOLATE;
+
+        res = 0;
+    }
+
+    return res;
+}
+
+
+/**
+ * @get_station: get station information for the station identified by @mac
+ */
+static int ecrnx_fill_station_info(struct ecrnx_sta *sta, struct ecrnx_vif *vif,
+                                  struct station_info *sinfo)
+{
+    struct ecrnx_sta_stats *stats = &sta->stats;
+    struct rx_vector_1 *rx_vect1 = &stats->last_rx.rx_vect1;
+
+    // Generic info
+    sinfo->generation = vif->generation;
+
+    //sinfo->inactive_time = jiffies_to_msecs(jiffies - stats->last_act);
+    sinfo->rx_bytes = stats->rx_bytes;
+    sinfo->tx_bytes = stats->tx_bytes;
+    sinfo->tx_packets = stats->tx_pkts;
+    sinfo->rx_packets = stats->rx_pkts;
+    sinfo->signal = rx_vect1->rssi1;
+    sinfo->tx_failed = 1;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
+    switch (rx_vect1->ch_bw) {
+        case PHY_CHNL_BW_20:
+            sinfo->rxrate.bw = RATE_INFO_BW_20;
+            break;
+        case PHY_CHNL_BW_40:
+            sinfo->rxrate.bw = RATE_INFO_BW_40;
+            break;
+        case PHY_CHNL_BW_80:
+            sinfo->rxrate.bw = RATE_INFO_BW_80;
+            break;
+        case PHY_CHNL_BW_160:
+            sinfo->rxrate.bw = RATE_INFO_BW_160;
+            break;
+        default:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+            sinfo->rxrate.bw = RATE_INFO_BW_HE_RU;
+#else
+            sinfo->rxrate.bw = RATE_INFO_BW_160;
+#endif
+            break;
+    }
+#endif
+
+    switch (rx_vect1->format_mod) {
+        case FORMATMOD_NON_HT:
+        case FORMATMOD_NON_HT_DUP_OFDM:
+            sinfo->rxrate.flags = 0;
+            sinfo->rxrate.legacy = legrates_lut[rx_vect1->leg_rate].rate;
+            break;
+        case FORMATMOD_HT_MF:
+        case FORMATMOD_HT_GF:
+            sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS;
+            if (rx_vect1->ht.short_gi)
+                sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+            sinfo->rxrate.mcs = rx_vect1->ht.mcs;
+            break;
+        case FORMATMOD_VHT:
+            sinfo->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS;
+            if (rx_vect1->vht.short_gi)
+                sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+            sinfo->rxrate.mcs = rx_vect1->vht.mcs;
+            sinfo->rxrate.nss = rx_vect1->vht.nss;
+            break;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+#if CONFIG_ECRNX_HE
+        case FORMATMOD_HE_MU:
+            sinfo->rxrate.he_ru_alloc = rx_vect1->he.ru_size;
+            break;
+        case FORMATMOD_HE_SU:
+        case FORMATMOD_HE_ER:
+        case FORMATMOD_HE_TB:
+            sinfo->rxrate.flags = RATE_INFO_FLAGS_HE_MCS;
+            sinfo->rxrate.mcs = rx_vect1->he.mcs;
+            sinfo->rxrate.nss = rx_vect1->he.nss;
+            sinfo->rxrate.he_gi = rx_vect1->he.gi_type;
+            sinfo->rxrate.he_dcm = rx_vect1->he.dcm;
+            break;
+#endif
+#endif
+        default :
+            return -EINVAL;
+    }
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+    sinfo->filled |= (STATION_INFO_INACTIVE_TIME |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+                     STATION_INFO_RX_BYTES64 |
+                     STATION_INFO_TX_BYTES64 |
+#endif
+                     STATION_INFO_RX_PACKETS |
+                     STATION_INFO_TX_PACKETS |
+                     STATION_INFO_SIGNAL |
+                     STATION_INFO_RX_BITRATE);
+#else
+    sinfo->filled = (BIT(NL80211_STA_INFO_RX_BYTES64)    |
+                     BIT(NL80211_STA_INFO_TX_BYTES64)    |
+                     BIT(NL80211_STA_INFO_RX_PACKETS)    |
+                     BIT(NL80211_STA_INFO_TX_PACKETS)    |
+                     BIT(NL80211_STA_INFO_SIGNAL)        |
+                     BIT(NL80211_STA_INFO_TX_BITRATE)    |
+                     BIT(NL80211_STA_INFO_TX_FAILED)     |
+                     BIT(NL80211_STA_INFO_RX_BITRATE));
+#endif
+
+    // Mesh specific info
+    if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MESH_POINT)
+    {
+        struct mesh_peer_info_cfm peer_info_cfm;
+        if (ecrnx_send_mesh_peer_info_req(vif->ecrnx_hw, vif, sta->sta_idx,
+                                         &peer_info_cfm))
+            return -ENOMEM;
+
+        peer_info_cfm.last_bcn_age = peer_info_cfm.last_bcn_age / 1000;
+        if (peer_info_cfm.last_bcn_age < sinfo->inactive_time)
+            sinfo->inactive_time = peer_info_cfm.last_bcn_age;
+
+        sinfo->llid = peer_info_cfm.local_link_id;
+        sinfo->plid = peer_info_cfm.peer_link_id;
+        sinfo->plink_state = peer_info_cfm.link_state;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+        sinfo->local_pm = peer_info_cfm.local_ps_mode;
+        sinfo->peer_pm = peer_info_cfm.peer_ps_mode;
+        sinfo->nonpeer_pm = peer_info_cfm.non_peer_ps_mode;
+#endif
+        sinfo->filled |= (BIT(NL80211_STA_INFO_LLID) |
+                          BIT(NL80211_STA_INFO_PLID) |
+                          BIT(NL80211_STA_INFO_PLINK_STATE) |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+                          BIT(NL80211_STA_INFO_LOCAL_PM) |
+                          BIT(NL80211_STA_INFO_PEER_PM) |
+                          BIT(NL80211_STA_INFO_NONPEER_PM)|
+#endif
+                          0);
+    }
+
+    sinfo->txrate.legacy = 0x6818;
+
+    return 0;
+}
+
+static int ecrnx_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
+                    u8 *mac,
+#else
+                    const u8 *mac,
+#endif
+                     struct station_info *sinfo)
+{
+    struct ecrnx_vif *vif = netdev_priv(dev);
+    struct ecrnx_sta *sta = NULL;
+
+    if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR)
+        return -EINVAL;
+    else if ((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_STATION) ||
+             (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_CLIENT)) {
+        if (vif->sta.ap && ether_addr_equal(vif->sta.ap->mac_addr, mac))
+            sta = vif->sta.ap;
+    }
+    else
+    {
+        struct ecrnx_sta *sta_iter;
+        list_for_each_entry(sta_iter, &vif->ap.sta_list, list) {
+            if (sta_iter->valid && ether_addr_equal(sta_iter->mac_addr, mac)) {
+                sta = sta_iter;
+                break;
+            }
+        }
+    }
+
+    if (sta)
+        return ecrnx_fill_station_info(sta, vif, sinfo);
+
+    return -EINVAL;
+}
+
+int ecrnx_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
+                                      int idx, u8 *mac, struct station_info *sinfo)
+{
+    struct ecrnx_vif *vif = netdev_priv(dev);
+    struct ecrnx_sta *sta = NULL;
+
+    if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR)
+        return -EINVAL;
+    else if ((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_STATION) ||
+             (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_CLIENT)) {
+        if ((idx == 0) && vif->sta.ap && vif->sta.ap->valid)
+            sta = vif->sta.ap;
+    } else {
+        struct ecrnx_sta *sta_iter;
+        int i = 0;
+        list_for_each_entry(sta_iter, &vif->ap.sta_list, list) {
+            if (i == idx) {
+                sta = sta_iter;
+                break;
+            }
+            i++;
+        }
+    }
+
+    if (sta == NULL)
+        return -ENOENT;
+
+
+    memcpy(mac, &sta->mac_addr, ETH_ALEN);
+
+    return ecrnx_fill_station_info(sta, vif, sinfo);
+}
+
+/**
+ * @add_mpath: add a fixed mesh path
+ */
+static int ecrnx_cfg80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                   const u8 *dst, const u8 *next_hop)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct mesh_path_update_cfm cfm;
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    return ecrnx_send_mesh_path_update_req(ecrnx_hw, ecrnx_vif, dst, next_hop, &cfm);
+}
+
+/**
+ * @del_mpath: delete a given mesh path
+ */
+static int ecrnx_cfg80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                   const u8 *dst)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct mesh_path_update_cfm cfm;
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    return ecrnx_send_mesh_path_update_req(ecrnx_hw, ecrnx_vif, dst, NULL, &cfm);
+}
+
+/**
+ * @change_mpath: change a given mesh path
+ */
+static int ecrnx_cfg80211_change_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                      const u8 *dst, const u8 *next_hop)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct mesh_path_update_cfm cfm;
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    return ecrnx_send_mesh_path_update_req(ecrnx_hw, ecrnx_vif, dst, next_hop, &cfm);
+}
+
+/**
+ * @get_mpath: get a mesh path for the given parameters
+ */
+static int ecrnx_cfg80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                   u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_mesh_path *mesh_path = NULL;
+    struct ecrnx_mesh_path *cur;
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    list_for_each_entry(cur, &ecrnx_vif->ap.mpath_list, list) {
+        /* Compare the path target address and the provided destination address */
+        if (memcmp(dst, &cur->tgt_mac_addr, ETH_ALEN)) {
+            continue;
+        }
+
+        mesh_path = cur;
+        break;
+    }
+
+    if (mesh_path == NULL)
+        return -ENOENT;
+
+    /* Copy next HOP MAC address */
+    if (mesh_path->nhop_sta)
+        memcpy(next_hop, &mesh_path->nhop_sta->mac_addr, ETH_ALEN);
+
+    /* Fill path information */
+    pinfo->filled = 0;
+    pinfo->generation = ecrnx_vif->generation;
+
+    return 0;
+}
+
+/**
+ * @dump_mpath: dump mesh path callback -- resume dump at index @idx
+ */
+static int ecrnx_cfg80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
+                                    int idx, u8 *dst, u8 *next_hop,
+                                    struct mpath_info *pinfo)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_mesh_path *mesh_path = NULL;
+    struct ecrnx_mesh_path *cur;
+    int i = 0;
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    list_for_each_entry(cur, &ecrnx_vif->ap.mpath_list, list) {
+        if (i < idx) {
+            i++;
+            continue;
+        }
+
+        mesh_path = cur;
+        break;
+    }
+
+    if (mesh_path == NULL)
+        return -ENOENT;
+
+    /* Copy target and next hop MAC address */
+    memcpy(dst, &mesh_path->tgt_mac_addr, ETH_ALEN);
+    if (mesh_path->nhop_sta)
+        memcpy(next_hop, &mesh_path->nhop_sta->mac_addr, ETH_ALEN);
+
+    /* Fill path information */
+    pinfo->filled = 0;
+    pinfo->generation = ecrnx_vif->generation;
+
+    return 0;
+}
+
+/**
+ * @get_mpp: get a mesh proxy path for the given parameters
+ */
+static int ecrnx_cfg80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
+                                 u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_mesh_proxy *mesh_proxy = NULL;
+    struct ecrnx_mesh_proxy *cur;
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    list_for_each_entry(cur, &ecrnx_vif->ap.proxy_list, list) {
+        if (cur->local) {
+            continue;
+        }
+
+        /* Compare the path target address and the provided destination address */
+        if (memcmp(dst, &cur->ext_sta_addr, ETH_ALEN)) {
+            continue;
+        }
+
+        mesh_proxy = cur;
+        break;
+    }
+
+    if (mesh_proxy == NULL)
+        return -ENOENT;
+
+    memcpy(mpp, &mesh_proxy->proxy_addr, ETH_ALEN);
+
+    /* Fill path information */
+    pinfo->filled = 0;
+    pinfo->generation = ecrnx_vif->generation;
+
+    return 0;
+}
+
+/**
+ * @dump_mpp: dump mesh proxy path callback -- resume dump at index @idx
+ */
+static int ecrnx_cfg80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
+                                  int idx, u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_mesh_proxy *mesh_proxy = NULL;
+    struct ecrnx_mesh_proxy *cur;
+    int i = 0;
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    list_for_each_entry(cur, &ecrnx_vif->ap.proxy_list, list) {
+        if (cur->local) {
+            continue;
+        }
+
+        if (i < idx) {
+            i++;
+            continue;
+        }
+
+        mesh_proxy = cur;
+        break;
+    }
+
+    if (mesh_proxy == NULL)
+        return -ENOENT;
+
+    /* Copy target MAC address */
+    memcpy(dst, &mesh_proxy->ext_sta_addr, ETH_ALEN);
+    memcpy(mpp, &mesh_proxy->proxy_addr, ETH_ALEN);
+
+    /* Fill path information */
+    pinfo->filled = 0;
+    pinfo->generation = ecrnx_vif->generation;
+
+    return 0;
+}
+
+/**
+ * @get_mesh_config: Get the current mesh configuration
+ */
+static int ecrnx_cfg80211_get_mesh_config(struct wiphy *wiphy, struct net_device *dev,
+                                         struct mesh_config *conf)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    return 0;
+}
+
+/**
+ * @update_mesh_config: Update mesh parameters on a running mesh.
+ */
+static int ecrnx_cfg80211_update_mesh_config(struct wiphy *wiphy, struct net_device *dev,
+                                            u32 mask, const struct mesh_config *nconf)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct mesh_update_cfm cfm;
+    int status;
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    if (mask & CO_BIT(NL80211_MESHCONF_POWER_MODE - 1)) {
+        ecrnx_vif->ap.next_mesh_pm = nconf->power_mode;
+
+        if (!list_empty(&ecrnx_vif->ap.sta_list)) {
+            // If there are mesh links we don't want to update the power mode
+            // It will be updated with ecrnx_update_mesh_power_mode() when the
+            // ps mode of a link is updated or when a new link is added/removed
+            mask &= ~BIT(NL80211_MESHCONF_POWER_MODE - 1);
+
+            if (!mask)
+                return 0;
+        }
+    }
+#endif
+
+    status = ecrnx_send_mesh_update_req(ecrnx_hw, ecrnx_vif, mask, nconf, &cfm);
+
+    if (!status && (cfm.status != 0))
+        status = -EINVAL;
+
+    return status;
+}
+
+/**
+ * @join_mesh: join the mesh network with the specified parameters
+ * (invoked with the wireless_dev mutex held)
+ */
+static int ecrnx_cfg80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
+                                   const struct mesh_config *conf, const struct mesh_setup *setup)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct mesh_start_cfm mesh_start_cfm;
+    int error = 0;
+    u8 txq_status = 0;
+    /* STA for BC/MC traffic */
+    struct ecrnx_sta *sta;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    if (ECRNX_VIF_TYPE(ecrnx_vif) != NL80211_IFTYPE_MESH_POINT)
+        return -ENOTSUPP;
+
+    /* Forward the information to the UMAC */
+    if ((error = ecrnx_send_mesh_start_req(ecrnx_hw, ecrnx_vif, conf, setup, &mesh_start_cfm))) {
+        return error;
+    }
+
+    /* Check the status */
+    switch (mesh_start_cfm.status) {
+        case CO_OK:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+            ecrnx_vif->ap.bcmc_index = mesh_start_cfm.bcmc_idx;
+            ecrnx_vif->ap.bcn_interval = setup->beacon_interval;
+#endif
+            ecrnx_vif->ap.flags = 0;
+            ecrnx_vif->use_4addr = true;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+            if (setup->user_mpm)
+                ecrnx_vif->ap.flags |= ECRNX_AP_USER_MESH_PM;
+#endif
+
+            sta = &ecrnx_hw->sta_table[mesh_start_cfm.bcmc_idx];
+            sta->valid = true;
+            sta->aid = 0;
+            sta->sta_idx = mesh_start_cfm.bcmc_idx;
+            sta->ch_idx = mesh_start_cfm.ch_idx;
+            sta->vif_idx = ecrnx_vif->vif_index;
+            sta->qos = true;
+            sta->acm = 0;
+            sta->ps.active = false;
+            sta->listen_interval = 5;
+            ecrnx_mu_group_sta_init(sta, NULL);
+            spin_lock_bh(&ecrnx_hw->cb_lock);
+            ecrnx_chanctx_link(ecrnx_vif, mesh_start_cfm.ch_idx,
+                              (struct cfg80211_chan_def *)(&setup->chandef));
+            if (ecrnx_hw->cur_chanctx != mesh_start_cfm.ch_idx) {
+                txq_status = ECRNX_TXQ_STOP_CHAN;
+            }
+            ecrnx_txq_vif_init(ecrnx_hw, ecrnx_vif, txq_status);
+            spin_unlock_bh(&ecrnx_hw->cb_lock);
+
+            netif_tx_start_all_queues(dev);
+            netif_carrier_on(dev);
+
+            /* If the AP channel is already the active, we probably skip radar
+               activation on MM_CHANNEL_SWITCH_IND (unless another vif use this
+               ctxt). In anycase retest if radar detection must be activated
+             */
+            if (ecrnx_hw->cur_chanctx == mesh_start_cfm.ch_idx) {
+                ecrnx_radar_detection_enable_on_cur_channel(ecrnx_hw);
+            }
+            break;
+
+        case CO_BUSY:
+            error = -EINPROGRESS;
+            break;
+
+        default:
+            error = -EIO;
+            break;
+    }
+
+    /* Print information about the operation */
+    if (error) {
+        netdev_info(dev, "Failed to start MP (%d)", error);
+    } else {
+        netdev_info(dev, "MP started: ch=%d, bcmc_idx=%d",
+                    ecrnx_vif->ch_index, ecrnx_vif->ap.bcmc_index);
+    }
+
+    return error;
+}
+
+/**
+ * @leave_mesh: leave the current mesh network
+ * (invoked with the wireless_dev mutex held)
+ */
+static int ecrnx_cfg80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct mesh_stop_cfm mesh_stop_cfm;
+    int error = 0;
+
+    error = ecrnx_send_mesh_stop_req(ecrnx_hw, ecrnx_vif, &mesh_stop_cfm);
+
+    if (error == 0) {
+        /* Check the status */
+        switch (mesh_stop_cfm.status) {
+            case CO_OK:
+                spin_lock_bh(&ecrnx_hw->cb_lock);
+                ecrnx_chanctx_unlink(ecrnx_vif);
+                ecrnx_radar_cancel_cac(&ecrnx_hw->radar);
+                spin_unlock_bh(&ecrnx_hw->cb_lock);
+                /* delete BC/MC STA */
+                ecrnx_txq_vif_deinit(ecrnx_hw, ecrnx_vif);
+                ecrnx_del_bcn(&ecrnx_vif->ap.bcn);
+
+                netif_tx_stop_all_queues(dev);
+                netif_carrier_off(dev);
+
+                break;
+
+            default:
+                error = -EIO;
+                break;
+        }
+    }
+
+    if (error) {
+        netdev_info(dev, "Failed to stop MP");
+    } else {
+        netdev_info(dev, "MP Stopped");
+    }
+
+    return 0;
+}
+
+#ifdef CONFIG_ECRNX_P2P
+#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 8, 0)
+static void ecrnx_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
+                                         struct wireless_dev *wdev,
+                                         struct mgmt_frame_regs *upd)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(wdev->netdev);
+
+       ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+       
+       if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_STATION)
+       {               
+               ecrnx_vif->mgmt_reg_stypes = upd->interface_stypes & BIT(IEEE80211_STYPE_PROBE_REQ >> 4);
+    }
+}
+#else
+static void ecrnx_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
+                                  struct wireless_dev *wdev,
+                                  u16 frame_type, bool reg)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(wdev->netdev);
+
+       u16 mgmt_type;
+
+       mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
+
+       if (reg)
+               ecrnx_vif->mgmt_reg_stypes |= BIT(mgmt_type);
+       else
+               ecrnx_vif->mgmt_reg_stypes &= ~BIT(mgmt_type);
+}                         
+#endif
+#endif
+
+static struct cfg80211_ops ecrnx_cfg80211_ops = {
+    .add_virtual_intf = ecrnx_cfg80211_add_iface,
+    .del_virtual_intf = ecrnx_cfg80211_del_iface,
+    .change_virtual_intf = ecrnx_cfg80211_change_iface,
+    .start_p2p_device = ecrnx_cfg80211_start_p2p_device,
+    .stop_p2p_device = ecrnx_cfg80211_stop_p2p_device,
+    .scan = ecrnx_cfg80211_scan,
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0))
+    .abort_scan = ecrnx_cfg80211_abort_scan,
+#endif
+    .connect = ecrnx_cfg80211_connect,
+    .disconnect = ecrnx_cfg80211_disconnect,
+    .add_key = ecrnx_cfg80211_add_key,
+    .get_key = ecrnx_cfg80211_get_key,
+    .del_key = ecrnx_cfg80211_del_key,
+    .set_default_key = ecrnx_cfg80211_set_default_key,
+    .set_default_mgmt_key = ecrnx_cfg80211_set_default_mgmt_key,
+    .add_station = ecrnx_cfg80211_add_station,
+    .del_station = ecrnx_cfg80211_del_station,
+    .change_station = ecrnx_cfg80211_change_station,
+    .mgmt_tx = ecrnx_cfg80211_mgmt_tx,
+    .start_ap = ecrnx_cfg80211_start_ap,
+    .change_beacon = ecrnx_cfg80211_change_beacon,
+    .stop_ap = ecrnx_cfg80211_stop_ap,
+    .set_monitor_channel = ecrnx_cfg80211_set_monitor_channel,
+    .probe_client = ecrnx_cfg80211_probe_client,
+    .set_wiphy_params = ecrnx_cfg80211_set_wiphy_params,
+    .set_txq_params = ecrnx_cfg80211_set_txq_params,
+    .set_tx_power = ecrnx_cfg80211_set_tx_power,
+//    .get_tx_power = ecrnx_cfg80211_get_tx_power,
+    .set_power_mgmt = ecrnx_cfg80211_set_power_mgmt,
+    .get_station = ecrnx_cfg80211_get_station,
+    .remain_on_channel = ecrnx_cfg80211_remain_on_channel,
+    .cancel_remain_on_channel = ecrnx_cfg80211_cancel_remain_on_channel,
+    .dump_survey = ecrnx_cfg80211_dump_survey,
+    .get_channel = ecrnx_cfg80211_get_channel,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    .start_radar_detection = ecrnx_cfg80211_start_radar_detection,
+    .update_ft_ies = ecrnx_cfg80211_update_ft_ies,
+#endif
+    .set_cqm_rssi_config = ecrnx_cfg80211_set_cqm_rssi_config,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+    .channel_switch = ecrnx_cfg80211_channel_switch,
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+    .tdls_channel_switch = ecrnx_cfg80211_tdls_channel_switch,
+    .tdls_cancel_channel_switch = ecrnx_cfg80211_tdls_cancel_channel_switch,
+#endif
+    .tdls_mgmt = ecrnx_cfg80211_tdls_mgmt,
+    .tdls_oper = ecrnx_cfg80211_tdls_oper,
+    .change_bss = ecrnx_cfg80211_change_bss,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+    .external_auth = ecrnx_cfg80211_external_auth,
+#endif
+
+#ifdef CONFIG_ECRNX_P2P
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
+       .update_mgmt_frame_registrations =
+               ecrnx_cfg80211_update_mgmt_frame_registrations,
+#else
+    .mgmt_frame_register = ecrnx_cfg80211_mgmt_frame_register,
+#endif
+#endif
+
+};
+
+
+/*********************************************************************
+ * Init/Exit functions
+ *********************************************************************/
+static void ecrnx_wdev_unregister(struct ecrnx_hw *ecrnx_hw)
+{
+    struct ecrnx_vif *ecrnx_vif, *tmp;
+
+    rtnl_lock();
+    list_for_each_entry_safe(ecrnx_vif, tmp, &ecrnx_hw->vifs, list) {
+        ecrnx_cfg80211_del_iface(ecrnx_hw->wiphy, &ecrnx_vif->wdev);
+    }
+    rtnl_unlock();
+}
+
+static void ecrnx_set_vers(struct ecrnx_hw *ecrnx_hw)
+{
+    u32 vers = ecrnx_hw->version_cfm.version_lmac;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    snprintf(ecrnx_hw->wiphy->fw_version,
+             sizeof(ecrnx_hw->wiphy->fw_version), "%d.%d.%d.%d",
+             (vers & (0xff << 24)) >> 24, (vers & (0xff << 16)) >> 16,
+             (vers & (0xff <<  8)) >>  8, (vers & (0xff <<  0)) >>  0);
+    ecrnx_hw->machw_type = ecrnx_machw_type(ecrnx_hw->version_cfm.version_machw_2);
+}
+
+static void ecrnx_reg_notifier(struct wiphy *wiphy,
+                              struct regulatory_request *request)
+{
+    struct ecrnx_hw *ecrnx_hw = wiphy_priv(wiphy);
+
+    // For now trust all initiator
+    ecrnx_radar_set_domain(&ecrnx_hw->radar, request->dfs_region);
+    ecrnx_send_me_chan_config_req(ecrnx_hw);
+}
+
+static void ecrnx_enable_mesh(struct ecrnx_hw *ecrnx_hw)
+{
+    struct wiphy *wiphy = ecrnx_hw->wiphy;
+
+    if (!ecrnx_mod_params.mesh)
+        return;
+
+    ecrnx_cfg80211_ops.add_mpath = ecrnx_cfg80211_add_mpath;
+    ecrnx_cfg80211_ops.del_mpath = ecrnx_cfg80211_del_mpath;
+    ecrnx_cfg80211_ops.change_mpath = ecrnx_cfg80211_change_mpath;
+    ecrnx_cfg80211_ops.get_mpath = ecrnx_cfg80211_get_mpath;
+    ecrnx_cfg80211_ops.dump_mpath = ecrnx_cfg80211_dump_mpath;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
+    ecrnx_cfg80211_ops.get_mpp = ecrnx_cfg80211_get_mpp;
+    ecrnx_cfg80211_ops.dump_mpp = ecrnx_cfg80211_dump_mpp;
+#endif
+    ecrnx_cfg80211_ops.get_mesh_config = ecrnx_cfg80211_get_mesh_config;
+    ecrnx_cfg80211_ops.update_mesh_config = ecrnx_cfg80211_update_mesh_config;
+    ecrnx_cfg80211_ops.join_mesh = ecrnx_cfg80211_join_mesh;
+    ecrnx_cfg80211_ops.leave_mesh = ecrnx_cfg80211_leave_mesh;
+
+    wiphy->flags |= (WIPHY_FLAG_MESH_AUTH | WIPHY_FLAG_IBSS_RSN);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    wiphy->features |= NL80211_FEATURE_USERSPACE_MPM;
+#endif
+    wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
+
+    ecrnx_limits[0].types |= BIT(NL80211_IFTYPE_MESH_POINT);
+    ecrnx_limits_dfs[0].types |= BIT(NL80211_IFTYPE_MESH_POINT);
+}
+
+int ecrnx_get_cal_result(struct ecrnx_hw *ecrnx_hw)
+{
+       int ret;
+       wifi_cal_data_t *result = &cal_result;
+
+       ret = ecrnx_send_cal_result_get_req(ecrnx_hw, result);
+
+       return ret;
+}
+
+void ecrnx_he_init(void)
+{
+    ecrnx_he_cap.has_he = true;
+    memset(&ecrnx_he_cap.he_cap_elem, 0, sizeof(struct ieee80211_he_cap_elem));
+
+    ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_80 = cpu_to_le16(0xfffa);
+    ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_80 = cpu_to_le16(0xfffa);
+    ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_160 = cpu_to_le16(0xffff);
+    ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_160 = cpu_to_le16(0xffff);
+    ecrnx_he_cap.he_mcs_nss_supp.rx_mcs_80p80 = cpu_to_le16(0xffff);
+    ecrnx_he_cap.he_mcs_nss_supp.tx_mcs_80p80 = cpu_to_le16(0xffff);
+    memset(ecrnx_he_cap.ppe_thres, 0, sizeof(u8)*IEEE80211_HE_PPE_THRES_MAX_LEN);
+}
+
+/**
+ *
+ */
+bool register_drv_done = false;
+int ecrnx_cfg80211_init(void *ecrnx_plat, void **platform_data)
+{
+    struct ecrnx_hw *ecrnx_hw;
+    int ret = 0;
+    struct wiphy *wiphy;
+    struct wireless_dev *wdev;
+    int i;
+
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    /* create a new wiphy for use with cfg80211 */
+    wiphy = wiphy_new(&ecrnx_cfg80211_ops, sizeof(struct ecrnx_hw));
+
+    if (!wiphy) {
+        dev_err(ecrnx_platform_get_dev(ecrnx_plat), "Failed to create new wiphy\n");
+        ret = -ENOMEM;
+        goto err_out;
+    }
+
+    ecrnx_hw = wiphy_priv(wiphy);
+    ecrnx_hw->wiphy = wiphy;
+    ecrnx_hw->plat = ecrnx_plat;
+    ecrnx_hw->dev = ecrnx_platform_get_dev(ecrnx_plat);
+    ecrnx_hw->mod_params = &ecrnx_mod_params;
+    ecrnx_hw->tcp_pacing_shift = 7;
+    *platform_data = ecrnx_hw;
+
+    /* set device pointer for wiphy */
+    set_wiphy_dev(wiphy, ecrnx_hw->dev);
+    /* Create cache to allocate sw_txhdr */
+    ecrnx_hw->sw_txhdr_cache = KMEM_CACHE(ecrnx_sw_txhdr, 0);
+    if (!ecrnx_hw->sw_txhdr_cache) {
+        wiphy_err(wiphy, "Cannot allocate cache for sw TX header\n");
+        ret = -ENOMEM;
+        goto err_cache;
+    }
+
+    if ((ret = ecrnx_parse_configfile(ecrnx_hw, ECRNX_CONFIG_FW_NAME))) {
+        wiphy_err(wiphy, "ecrnx_parse_configfile failed\n");
+        goto err_config;
+    }
+
+    ecrnx_hw->vif_started = 0;
+    ecrnx_hw->monitor_vif = ECRNX_INVALID_VIF;
+
+    ecrnx_hw->scan_ie.addr = NULL;
+
+    for (i = 0; i < NX_VIRT_DEV_MAX + NX_REMOTE_STA_MAX; i++)
+        ecrnx_hw->avail_idx_map |= BIT(i);
+
+    ecrnx_hwq_init(ecrnx_hw);
+    ecrnx_txq_prepare(ecrnx_hw);
+
+    ecrnx_mu_group_init(ecrnx_hw);
+
+    /* Initialize RoC element pointer to NULL, indicate that RoC can be started */
+    ecrnx_hw->roc = NULL;
+    /* Cookie can not be 0 */
+    ecrnx_hw->roc_cookie = 1;
+
+    wiphy->mgmt_stypes = ecrnx_default_mgmt_stypes;
+
+    wiphy->bands[NL80211_BAND_2GHZ] = &ecrnx_band_2GHz;
+#ifdef CONFIG_ECRNX_5G
+    wiphy->bands[NL80211_BAND_5GHZ] = &ecrnx_band_5GHz;
+#endif
+    wiphy->interface_modes =
+        BIT(NL80211_IFTYPE_STATION)     |
+        BIT(NL80211_IFTYPE_AP)          |
+        BIT(NL80211_IFTYPE_AP_VLAN)     |
+        BIT(NL80211_IFTYPE_P2P_CLIENT)  |
+        BIT(NL80211_IFTYPE_P2P_GO)      |
+        BIT(NL80211_IFTYPE_MONITOR);
+    wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
+        WIPHY_FLAG_HAS_CHANNEL_SWITCH |
+#endif
+        WIPHY_FLAG_4ADDR_STATION |
+        WIPHY_FLAG_4ADDR_AP;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+    wiphy->max_num_csa_counters = BCN_MAX_CSA_CPT;
+#endif
+
+    wiphy->max_remain_on_channel_duration = ecrnx_hw->mod_params->roc_dur_max;
+
+#if 0 /* eswin:rm the feature of OBSS_SCAN which can cause the uplink stream shutdown */
+    wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN |
+        NL80211_FEATURE_SK_TX_STATUS |
+#else
+    wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
+#endif
+        NL80211_FEATURE_VIF_TXPOWER |
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+        NL80211_FEATURE_ACTIVE_MONITOR |
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
+        NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
+#endif
+        0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+    wiphy->features |= NL80211_FEATURE_SAE;
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
+    ecrnx_he_init();
+#endif
+
+    if (ecrnx_mod_params.tdls)
+        /* TDLS support */
+        wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+
+    wiphy->iface_combinations   = ecrnx_combinations;
+    /* -1 not to include combination with radar detection, will be re-added in
+       ecrnx_handle_dynparams if supported */
+    wiphy->n_iface_combinations = ARRAY_SIZE(ecrnx_combinations) - 1;
+    wiphy->reg_notifier = ecrnx_reg_notifier;
+
+    wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+
+    wiphy->cipher_suites = cipher_suites;
+    wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites) - NB_RESERVED_CIPHER;
+
+    ecrnx_hw->ext_capa[0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
+    ecrnx_hw->ext_capa[2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT;
+    ecrnx_hw->ext_capa[4] = WLAN_EXT_CAPA5_QOS_MAP_SUPPORT;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+    ecrnx_hw->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF;
+    wiphy->extended_capabilities = ecrnx_hw->ext_capa;
+    wiphy->extended_capabilities_mask = ecrnx_hw->ext_capa;
+    wiphy->extended_capabilities_len = ARRAY_SIZE(ecrnx_hw->ext_capa);
+#endif
+
+#ifndef CONFIG_ECRNX_ESWIN
+    tasklet_init(&ecrnx_hw->task, ecrnx_task, (unsigned long)ecrnx_hw);
+#endif
+
+    INIT_LIST_HEAD(&ecrnx_hw->vifs);
+#ifdef CONFIG_ECRNX_ESWIN
+    INIT_LIST_HEAD(&ecrnx_hw->agg_rx_list);
+    INIT_LIST_HEAD(&ecrnx_hw->defrag_rx_list);
+#endif
+
+    mutex_init(&ecrnx_hw->dbgdump_elem.mutex);
+    spin_lock_init(&ecrnx_hw->tx_lock);
+    spin_lock_init(&ecrnx_hw->cb_lock);
+    spin_lock_init(&ecrnx_hw->rx_lock);
+    spin_lock_init(&ecrnx_hw->scan_req_lock);
+    spin_lock_init(&ecrnx_hw->connect_req_lock);
+
+    if ((ret = ecrnx_platform_on(ecrnx_hw, NULL)))
+        goto err_platon;
+
+       if ((ret = ecrnx_get_cal_result(ecrnx_hw))) {
+        wiphy_err(wiphy, "get cal result failed\n");
+        goto err_lmac_reqs;
+       } else {
+               if ((0 == (cal_result.mac_addr[0] & 0x1)) && (cal_result.mac_addr[0] || cal_result.mac_addr[1] 
+                       || cal_result.mac_addr[2] || cal_result.mac_addr[3] || cal_result.mac_addr[4] 
+                       || cal_result.mac_addr[5])) {
+                       memcpy(ecrnx_hw->conf_param.mac_addr, cal_result.mac_addr, ETH_ALEN);
+               }
+       }
+       memcpy(wiphy->perm_addr, ecrnx_hw->conf_param.mac_addr, ETH_ALEN);
+
+    /* Reset FW */
+    if ((ret = ecrnx_send_reset(ecrnx_hw)))
+        goto err_lmac_reqs;
+    if ((ret = ecrnx_send_version_req(ecrnx_hw, &ecrnx_hw->version_cfm)))
+        goto err_lmac_reqs;
+    ecrnx_set_vers(ecrnx_hw);
+
+    if ((ret = ecrnx_handle_dynparams(ecrnx_hw, ecrnx_hw->wiphy)))
+        goto err_lmac_reqs;
+
+    ecrnx_enable_mesh(ecrnx_hw);
+    ecrnx_radar_detection_init(&ecrnx_hw->radar);
+
+#ifdef CONFIG_ECRNX_P2P
+       ecrnx_p2p_listen_init(&ecrnx_hw->p2p_listen);
+#endif
+
+    /* Set parameters to firmware */
+    ecrnx_send_me_config_req(ecrnx_hw);
+
+    /* Only monitor mode supported when custom channels are enabled */
+    if (ecrnx_mod_params.custchan) {
+        ecrnx_limits[0].types = BIT(NL80211_IFTYPE_MONITOR);
+        ecrnx_limits_dfs[0].types = BIT(NL80211_IFTYPE_MONITOR);
+    }
+
+    if ((ret = wiphy_register(wiphy))) {
+        wiphy_err(wiphy, "Could not register wiphy device\n");
+        goto err_register_wiphy;
+    }
+
+    INIT_WORK(&ecrnx_hw->defer_rx.work, ecrnx_rx_deferred);
+    skb_queue_head_init(&ecrnx_hw->defer_rx.sk_list);
+    /* Update regulatory (if needed) and set channel parameters to firmware
+       (must be done after WiPHY registration) */
+    ecrnx_fw_log_level_set((u32)ecrnx_hw->conf_param.fw_log_level, (u32)ecrnx_hw->conf_param.fw_log_type);
+    ecrnx_custregd(ecrnx_hw, wiphy);
+    ecrnx_send_me_chan_config_req(ecrnx_hw);
+
+    /* config gain delta */
+    ecrnx_send_set_gain_delta_req(ecrnx_hw);
+
+#ifdef CONFIG_ECRNX_DEBUGFS
+    if ((ret = ecrnx_dbgfs_register(ecrnx_hw, "ecrnx"))) {
+               ECRNX_DBG(" ecrnx_dbgfs_register error \n");
+        wiphy_err(wiphy, "Failed to register debugfs entries");
+        goto err_debugfs;
+    }
+#endif
+
+    rtnl_lock();
+
+    /* Add an initial interface */
+    wdev = ecrnx_interface_add(ecrnx_hw, "wlan%d", NET_NAME_UNKNOWN,
+               ecrnx_mod_params.custchan ? NL80211_IFTYPE_MONITOR : NL80211_IFTYPE_STATION,
+               NULL);
+#if defined(CONFIG_ECRNX_P2P)
+    wdev = ecrnx_interface_add(ecrnx_hw, "p2p%d", NET_NAME_UNKNOWN, NL80211_IFTYPE_STATION,
+               NULL);
+#endif
+    rtnl_unlock();
+
+    if (!wdev) {
+        wiphy_err(wiphy, "Failed to instantiate a network device\n");
+        ret = -ENOMEM;
+        goto err_add_interface;
+    }
+
+    wiphy_info(wiphy, "New interface create %s", wdev->netdev->name);
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+    ecrnx_debugfs_init(ecrnx_hw);
+#endif
+    register_drv_done = true;
+    return 0;
+
+err_add_interface:
+#ifdef CONFIG_ECRNX_DEBUGFS
+err_debugfs:
+#endif
+    wiphy_unregister(ecrnx_hw->wiphy);
+err_register_wiphy:
+err_lmac_reqs:
+    ecrnx_fw_trace_dump(ecrnx_hw);
+    ecrnx_platform_off(ecrnx_hw, NULL);
+err_platon:
+err_config:
+    kmem_cache_destroy(ecrnx_hw->sw_txhdr_cache);
+err_cache:
+    wiphy_free(wiphy);
+err_out:
+    ECRNX_DBG(" %s cfg80211 init failed %d!!", __func__, ret);
+    return ret;
+}
+
+/**
+ *
+ */
+void ecrnx_cfg80211_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    if(!register_drv_done)
+    {
+        return;
+    }
+#if 0
+    ecrnx_dbgfs_unregister(ecrnx_hw);
+#endif
+    register_drv_done = false;
+
+    del_timer_sync(&ecrnx_hw->txq_cleanup);
+    ecrnx_wdev_unregister(ecrnx_hw);
+    if(ecrnx_hw->wiphy)
+    {
+        ECRNX_DBG("%s wiphy_unregister \n", __func__);
+        wiphy_unregister(ecrnx_hw->wiphy);
+        wiphy_free(ecrnx_hw->wiphy);
+        ecrnx_hw->wiphy = NULL;
+    }
+    ecrnx_radar_detection_deinit(&ecrnx_hw->radar);
+    ecrnx_platform_off(ecrnx_hw, NULL);
+    kmem_cache_destroy(ecrnx_hw->sw_txhdr_cache);
+}
+
+/**
+ *
+ */
+static int __init ecrnx_mod_init(void)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+    ecrnx_print_version();
+    return ecrnx_platform_register_drv();
+}
+
+/**
+ *
+ */
+static void __exit ecrnx_mod_exit(void)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+    ecrnx_debugfs_exit();
+#endif
+
+    ecrnx_platform_unregister_drv();
+}
+
+module_init(ecrnx_mod_init);
+module_exit(ecrnx_mod_exit);
+
+MODULE_FIRMWARE(ECRNX_CONFIG_FW_NAME);
+
+MODULE_DESCRIPTION(RW_DRV_DESCRIPTION);
+MODULE_VERSION(ECRNX_VERS_MOD);
+MODULE_AUTHOR(RW_DRV_COPYRIGHT " " RW_DRV_AUTHOR);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_main.h b/drivers/net/wireless/eswin/fullmac/ecrnx_main.h
new file mode 100644 (file)
index 0000000..b1c81e9
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_main.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_MAIN_H_
+#define _ECRNX_MAIN_H_
+
+#include "ecrnx_defs.h"
+
+int ecrnx_cfg80211_init(struct ecrnx_plat *ecrnx_plat, void **platform_data);
+void ecrnx_cfg80211_deinit(struct ecrnx_hw *ecrnx_hw);
+
+#endif /* _ECRNX_MAIN_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_mesh.c b/drivers/net/wireless/eswin/fullmac/ecrnx_mesh.c
new file mode 100644 (file)
index 0000000..c027d67
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_mesh.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+#include "ecrnx_mesh.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ****************************************************************************************
+ */
+
+struct ecrnx_mesh_proxy *ecrnx_get_mesh_proxy_info(struct ecrnx_vif *p_ecrnx_vif, u8 *p_sta_addr, bool local)
+{
+    struct ecrnx_mesh_proxy *p_mesh_proxy = NULL;
+    struct ecrnx_mesh_proxy *p_cur_proxy;
+
+    /* Look for proxied devices with provided address */
+    list_for_each_entry(p_cur_proxy, &p_ecrnx_vif->ap.proxy_list, list) {
+        if (p_cur_proxy->local != local) {
+            continue;
+        }
+
+        if (!memcmp(&p_cur_proxy->ext_sta_addr, p_sta_addr, ETH_ALEN)) {
+            p_mesh_proxy = p_cur_proxy;
+            break;
+        }
+    }
+
+    /* Return the found information */
+    return p_mesh_proxy;
+}
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_mesh.h b/drivers/net/wireless/eswin/fullmac/ecrnx_mesh.h
new file mode 100644 (file)
index 0000000..e27ec9b
--- /dev/null
@@ -0,0 +1,45 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_mesh.h
+ *
+ * @brief VHT Beamformer function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_MESH_H_
+#define _ECRNX_MESH_H_
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+#include "ecrnx_defs.h"
+
+/**
+ * DEFINES
+ ****************************************************************************************
+ */
+
+/**
+ * TYPE DEFINITIONS
+ ****************************************************************************************
+ */
+
+/**
+ * FUNCTION DECLARATIONS
+ ****************************************************************************************
+ */
+
+/**
+ ****************************************************************************************
+ * @brief TODO [LT]
+ ****************************************************************************************
+ */
+struct ecrnx_mesh_proxy *ecrnx_get_mesh_proxy_info(struct ecrnx_vif *p_ecrnx_vif, u8 *p_sta_addr, bool local);
+
+#endif /* _ECRNX_MESH_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_p2p.c b/drivers/net/wireless/eswin/fullmac/ecrnx_p2p.c
new file mode 100644 (file)
index 0000000..bab577c
--- /dev/null
@@ -0,0 +1,72 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_p2p.c
+ *
+ * 
+ *
+ ****************************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+
+#include "ecrnx_p2p.h"
+#include "ecrnx_msg_tx.h"
+
+
+/**
+ * FUNCTION DEFINITIONS
+ ****************************************************************************************
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+static void ecrnx_p2p_discovery_timer_fn(struct timer_list *t)
+#else
+static void ecrnx_p2p_discovery_timer_fn(ulong x)
+#endif
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+       struct ecrnx_hw *ecrnx_hw = from_timer(ecrnx_hw, t, p2p_listen.listen_timer);
+#else
+       struct ecrnx_hw *ecrnx_hw = (void *)x;
+#endif
+       ECRNX_PRINT("ecrnx_p2p_discovery_timer_fn\n");
+
+       schedule_work(&ecrnx_hw->p2p_listen.listen_expired_work);
+
+}
+
+void ecrnx_p2p_listen_init(struct ecrnx_p2p_listen *p2p_listen)
+{
+       struct ecrnx_hw *ecrnx_hw = container_of(p2p_listen, struct ecrnx_hw, p2p_listen);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+       timer_setup(&p2p_listen->listen_timer, ecrnx_p2p_discovery_timer_fn, 0);
+#else
+       setup_timer(&p2p_listen->listen_timer, ecrnx_p2p_discovery_timer_fn, (ulong)ecrnx_hw);
+#endif
+       INIT_WORK(&p2p_listen->listen_expired_work, ecrnx_p2p_listen_expired);
+
+    init_waitqueue_head(&p2p_listen->rxdataq);
+}
+
+void ecrnx_p2p_listen_expired(struct work_struct *work)
+{
+       struct ecrnx_p2p_listen *p2p_listen = container_of(work, struct ecrnx_p2p_listen, listen_expired_work);
+       struct ecrnx_hw *ecrnx_hw = container_of(p2p_listen, struct ecrnx_hw, p2p_listen);
+
+       ECRNX_PRINT("p2p_listen_expired\n");
+
+       if (p2p_listen->listen_started)
+       {
+               del_timer_sync(&p2p_listen->listen_timer);
+               ecrnx_send_p2p_cancel_listen_req(ecrnx_hw, p2p_listen->ecrnx_vif);
+       }
+       else
+       {
+               return;
+       }
+}
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_p2p.h b/drivers/net/wireless/eswin/fullmac/ecrnx_p2p.h
new file mode 100644 (file)
index 0000000..390b950
--- /dev/null
@@ -0,0 +1,60 @@
+/**
+ ****************************************************************************************
+ *
+ * @file rwnx_p2p.h
+ *
+ * @brief P2P Listen function declarations
+ *
+ * 
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _ECRNX_P2P_H_
+#define _ECRNX_P2P_H_
+
+/**
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+#include <net/cfg80211.h>
+
+/**
+ * DEFINES
+ ****************************************************************************************
+ */
+
+/**
+ * TYPE DEFINITIONS
+ ****************************************************************************************
+ */
+struct ecrnx_p2p_listen {
+       struct ecrnx_vif *ecrnx_vif;
+       struct ieee80211_channel listen_chan;
+       bool listen_started;
+       bool pending_req;
+       u64 cookie;
+       struct wireless_dev *pending_listen_wdev;
+       unsigned int listen_duration;
+       struct timer_list listen_timer; /* listen duration */
+       struct work_struct listen_expired_work; /* listen expire */
+
+    wait_queue_head_t   rxdataq;
+    int                 rxdatas;
+};
+
+/**
+ * FUNCTION DECLARATIONS
+ ****************************************************************************************
+ */
+void ecrnx_p2p_listen_init(struct ecrnx_p2p_listen *p2p_listen);
+void ecrnx_p2p_listen_expired(struct work_struct *work);
+
+/**
+ ****************************************************************************************
+ * @brief TODO [LT]
+ ****************************************************************************************
+ */
+
+
+#endif /* _RWNX_P2P_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_rx.c b/drivers/net/wireless/eswin/fullmac/ecrnx_rx.c
new file mode 100644 (file)
index 0000000..b6cf0d5
--- /dev/null
@@ -0,0 +1,2541 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_rx.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/dma-mapping.h>
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
+#include <net/ieee80211_radiotap.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_rx.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_prof.h"
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+#include "ipc_host.h"
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+#include "ecrnx_sdio.h"
+#include "sdio.h"
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+#include "ecrnx_usb.h"
+#include "usb.h"
+#endif
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "core.h"
+#endif
+
+#ifndef IEEE80211_MAX_CHAINS
+#define IEEE80211_MAX_CHAINS 4
+#endif
+
+struct vendor_radiotap_hdr {
+    u8 oui[3];
+    u8 subns;
+    u16 len;
+    u8 data[];
+};
+
+/**
+ * ecrnx_rx_get_vif - Return pointer to the destination vif
+ *
+ * @ecrnx_hw: main driver data
+ * @vif_idx: vif index present in rx descriptor
+ *
+ * Select the vif that should receive this frame. Returns NULL if the destination
+ * vif is not active or vif is not specified in the descriptor.
+ */
+static inline
+struct ecrnx_vif *ecrnx_rx_get_vif(struct ecrnx_hw *ecrnx_hw, int vif_idx)
+{
+    struct ecrnx_vif *ecrnx_vif = NULL;
+
+    if (vif_idx < NX_VIRT_DEV_MAX) {
+        ecrnx_vif = ecrnx_hw->vif_table[vif_idx];
+        //ECRNX_DBG("%s, index : %d, up: %d \n", __func__, vif_idx, ecrnx_vif->up);
+        if (!ecrnx_vif || !ecrnx_vif->up)
+            return NULL;
+    }
+
+    return ecrnx_vif;
+}
+
+/**
+ * ecrnx_rx_statistic - save some statistics about received frames
+ *
+ * @ecrnx_hw: main driver data.
+ * @hw_rxhdr: Rx Hardware descriptor of the received frame.
+ * @sta: STA that sent the frame.
+ */
+static void ecrnx_rx_statistic(struct ecrnx_hw *ecrnx_hw, struct hw_rxhdr *hw_rxhdr,
+                              struct ecrnx_sta *sta)
+{
+    struct ecrnx_stats *stats = &ecrnx_hw->stats;
+#ifdef CONFIG_ECRNX_DEBUGFS
+    struct ecrnx_rx_rate_stats *rate_stats = &sta->stats.rx_rate;
+    struct rx_vector_1 *rxvect = &hw_rxhdr->hwvect.rx_vect1;
+    int mpdu, ampdu, mpdu_prev, rate_idx;
+
+    /* save complete hwvect */
+
+    /* update ampdu rx stats */
+    mpdu = hw_rxhdr->hwvect.mpdu_cnt;
+    ampdu = hw_rxhdr->hwvect.ampdu_cnt;
+    mpdu_prev = stats->ampdus_rx_map[ampdu];
+
+    if (mpdu == 63) {
+        if (ampdu == stats->ampdus_rx_last)
+            mpdu = mpdu_prev + 1;
+        else
+            mpdu = 0;
+    }
+    if (ampdu != stats->ampdus_rx_last) {
+        stats->ampdus_rx[mpdu_prev]++;
+        stats->ampdus_rx_miss += mpdu;
+    } else {
+        if (mpdu <= mpdu_prev) {
+            stats->ampdus_rx_miss += mpdu;
+    } else {
+            stats->ampdus_rx_miss += mpdu - mpdu_prev - 1;
+        }
+    }
+    stats->ampdus_rx_map[ampdu] = mpdu;
+    stats->ampdus_rx_last = ampdu;
+
+    /* update rx rate statistic */
+    if (!rate_stats->size)
+        return;
+
+    if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
+        int mcs;
+        int bw = rxvect->ch_bw;
+        int sgi;
+        int nss;
+        switch (rxvect->format_mod) {
+            case FORMATMOD_HT_MF:
+            case FORMATMOD_HT_GF:
+                mcs = rxvect->ht.mcs % 8;
+                nss = rxvect->ht.mcs / 8;
+                sgi = rxvect->ht.short_gi;
+                rate_idx = N_CCK + N_OFDM + nss * 32 + mcs * 4 +  bw * 2 + sgi;
+                break;
+            case FORMATMOD_VHT:
+                mcs = rxvect->vht.mcs;
+                nss = rxvect->vht.nss;
+                sgi = rxvect->vht.short_gi;
+                rate_idx = N_CCK + N_OFDM + N_HT + nss * 80 + mcs * 8 + bw * 2 + sgi;
+                break;
+            case FORMATMOD_HE_SU:
+                mcs = rxvect->he.mcs;
+                nss = rxvect->he.nss;
+                sgi = rxvect->he.gi_type;
+                rate_idx = N_CCK + N_OFDM + N_HT + N_VHT + nss * 144 + mcs * 12 + bw * 3 + sgi;
+                break;
+            default:
+                mcs = rxvect->he.mcs;
+                nss = rxvect->he.nss;
+                sgi = rxvect->he.gi_type;
+                rate_idx = N_CCK + N_OFDM + N_HT + N_VHT + N_HE_SU
+                                                + nss * 216 + mcs * 18 + rxvect->he.ru_size * 3 + sgi;
+                break;
+        }
+    } else {
+        int idx = legrates_lut[rxvect->leg_rate].idx;
+        if (idx < 4) {
+            rate_idx = idx * 2 + rxvect->pre_type;
+        } else {
+            rate_idx = N_CCK + idx - 4;
+        }
+    }
+    if (rate_idx < rate_stats->size) {
+        if (!rate_stats->table[rate_idx])
+            rate_stats->rate_cnt++;
+        rate_stats->table[rate_idx]++;
+        rate_stats->cpt++;
+    } else {
+        wiphy_err(ecrnx_hw->wiphy, "RX: Invalid index conversion => %d/%d\n",
+                  rate_idx, rate_stats->size);
+    }
+#endif
+    sta->stats.last_rx = hw_rxhdr->hwvect;
+    sta->stats.rx_pkts ++;
+    sta->stats.rx_bytes += hw_rxhdr->hwvect.len;
+    sta->stats.last_act = stats->last_rx;
+}
+void ecrnx_rx_defer_skb(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                       struct sk_buff *skb)
+{
+    struct ecrnx_defer_rx_cb *rx_cb = (struct ecrnx_defer_rx_cb *)skb->cb;
+    if (skb_shared(skb))
+        return;
+    skb_get(skb);
+    rx_cb->vif = ecrnx_vif;
+    skb_queue_tail(&ecrnx_hw->defer_rx.sk_list, skb);
+    schedule_work(&ecrnx_hw->defer_rx.work);
+}
+
+/**
+ * ecrnx_rx_data_skb - Process one data frame
+ *
+ * @ecrnx_hw: main driver data
+ * @ecrnx_vif: vif that received the buffer
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ * @return: true if buffer has been forwarded to upper layer
+ *
+ * If buffer is amsdu , it is first split into a list of skb.
+ * Then each skb may be:
+ * - forwarded to upper layer
+ * - resent on wireless interface
+ *
+ * When vif is a STA interface, every skb is only forwarded to upper layer.
+ * When vif is an AP interface, multicast skb are forwarded and resent, whereas
+ * skb for other BSS's STA are only resent.
+ */
+static bool ecrnx_rx_data_skb(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                             struct sk_buff *skb,  struct hw_rxhdr *rxhdr)
+{
+    struct sk_buff_head list;
+    struct sk_buff *rx_skb;
+    bool amsdu = rxhdr->flags_is_amsdu;
+    bool resend = false, forward = true;
+    int skip_after_eth_hdr = 0;
+
+    skb->dev = ecrnx_vif->ndev;
+
+    __skb_queue_head_init(&list);
+
+    if (amsdu) {
+        int count;
+        ieee80211_amsdu_to_8023s(skb, &list, ecrnx_vif->ndev->dev_addr,
+                                 ECRNX_VIF_TYPE(ecrnx_vif), 0, NULL, NULL);
+
+        count = skb_queue_len(&list);
+        if (count == 0)
+        {
+            ECRNX_PRINT("amsdu decode fail!!\n");
+            //return false;
+            print_hex_dump(KERN_INFO, "amsdu:", DUMP_PREFIX_ADDRESS, 16, 1,
+                        skb->data, skb->len>64 ? 64 : skb->len, false);
+        }
+
+        if (count > ARRAY_SIZE(ecrnx_hw->stats.amsdus_rx))
+            count = ARRAY_SIZE(ecrnx_hw->stats.amsdus_rx);
+        if(count > 0)
+            ecrnx_hw->stats.amsdus_rx[count - 1]++;
+    } else {
+        ecrnx_hw->stats.amsdus_rx[0]++;
+        __skb_queue_head(&list, skb);
+    }
+
+    if (((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP) ||
+         (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_AP_VLAN) ||
+         (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_P2P_GO)) &&
+        !(ecrnx_vif->ap.flags & ECRNX_AP_ISOLATE)) {
+        const struct ethhdr *eth;
+        rx_skb = skb_peek(&list);
+        skb_reset_mac_header(rx_skb);
+        eth = eth_hdr(rx_skb);
+
+        if (unlikely(is_multicast_ether_addr(eth->h_dest))) {
+            /* broadcast pkt need to be forwared to upper layer and resent
+               on wireless interface */
+            resend = true;
+        } else {
+            /* unicast pkt for STA inside the BSS, no need to forward to upper
+               layer simply resend on wireless interface */
+            if (rxhdr->flags_dst_idx != ECRNX_INVALID_STA)
+            {
+                struct ecrnx_sta *sta = &ecrnx_hw->sta_table[rxhdr->flags_dst_idx];
+                if (sta->valid && (sta->vlan_idx == ecrnx_vif->vif_index))
+                {
+                    forward = false;
+                    resend = true;
+                }
+            }
+        }
+    } else if (ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MESH_POINT) {
+        const struct ethhdr *eth;
+        rx_skb = skb_peek(&list);
+        skb_reset_mac_header(rx_skb);
+        eth = eth_hdr(rx_skb);
+
+            /* unicast pkt for STA inside the BSS, no need to forward to upper
+               layer simply resend on wireless interface */
+            if (rxhdr->flags_dst_idx != ECRNX_INVALID_STA)
+            {
+            resend = true;
+            if (is_multicast_ether_addr(eth->h_dest)) {
+                uint8_t *mesh_ctrl = (uint8_t *)(eth + 1);
+                skip_after_eth_hdr = 8 + 6;
+                if ((*mesh_ctrl & MESH_FLAGS_AE) == MESH_FLAGS_AE_A4)
+                    skip_after_eth_hdr += ETH_ALEN;
+                else if ((*mesh_ctrl & MESH_FLAGS_AE) == MESH_FLAGS_AE_A5_A6)
+                    skip_after_eth_hdr += 2 * ETH_ALEN;
+            } else {
+                forward = false;
+            }
+        }
+    }
+
+    while (!skb_queue_empty(&list)) {
+        rx_skb = __skb_dequeue(&list);
+
+        /* resend pkt on wireless interface */
+        if (resend) {
+            struct sk_buff *skb_copy;
+            /* always need to copy buffer when forward=0 to get enough headrom for tsdesc */
+            skb_copy = skb_copy_expand(rx_skb, ECRNX_TX_MAX_HEADROOM, 0, GFP_ATOMIC);
+
+            if (skb_copy) {
+                int res;
+                skb_copy->protocol = htons(ETH_P_802_3);
+                skb_reset_network_header(skb_copy);
+                skb_reset_mac_header(skb_copy);
+
+                ecrnx_vif->is_resending = true;
+                res = dev_queue_xmit(skb_copy);
+                ecrnx_vif->is_resending = false;
+                /* note: buffer is always consummed by dev_queue_xmit */
+                if (res == NET_XMIT_DROP) {
+                    ecrnx_vif->net_stats.rx_dropped++;
+                    ecrnx_vif->net_stats.tx_dropped++;
+                } else if (res != NET_XMIT_SUCCESS) {
+                    netdev_err(ecrnx_vif->ndev,
+                               "Failed to re-send buffer to driver (res=%d)",
+                               res);
+                    ecrnx_vif->net_stats.tx_errors++;
+                }
+            } else {
+                netdev_err(ecrnx_vif->ndev, "Failed to copy skb");
+            }
+        }
+
+        /* forward pkt to upper layer */
+        if (forward) {
+            rx_skb->protocol = eth_type_trans(rx_skb, ecrnx_vif->ndev);
+            memset(rx_skb->cb, 0, sizeof(rx_skb->cb));
+
+            if (unlikely(skip_after_eth_hdr)) {
+                memmove(skb_mac_header(rx_skb) + skip_after_eth_hdr,
+                        skb_mac_header(rx_skb), sizeof(struct ethhdr));
+                __skb_pull(rx_skb, skip_after_eth_hdr);
+                skb_reset_mac_header(rx_skb);
+                skip_after_eth_hdr = 0;
+            }
+            REG_SW_SET_PROFILING(ecrnx_hw, SW_PROF_IEEE80211RX);
+            ECRNX_DBG("netif_receive_skb enter!! \n");
+            /* netif_receive_skb only be called from softirq context, replace it with netif_rx */
+            netif_rx(rx_skb);
+            REG_SW_CLEAR_PROFILING(ecrnx_hw, SW_PROF_IEEE80211RX);
+
+            /* Update statistics */
+            ecrnx_vif->net_stats.rx_packets++;
+            ecrnx_vif->net_stats.rx_bytes += rx_skb->len;
+        }
+    }
+
+    return forward;
+}
+
+/**
+ * ecrnx_rx_mgmt - Process one 802.11 management frame
+ *
+ * @ecrnx_hw: main driver data
+ * @ecrnx_vif: vif to upload the buffer to
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ *
+ * Forward the management frame to a given interface.
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+extern u64_l getBootTime(void);
+#endif
+static void ecrnx_rx_mgmt(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                         struct sk_buff *skb,  struct hw_rxhdr *hw_rxhdr)
+{
+    struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+    struct rx_vector_1 *rxvect = &hw_rxhdr->hwvect.rx_vect1;
+
+    if (ieee80211_is_beacon(mgmt->frame_control)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+        mgmt->u.beacon.timestamp = getBootTime();
+#endif
+        if ((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_MESH_POINT) &&
+            hw_rxhdr->flags_new_peer) {
+            cfg80211_notify_new_peer_candidate(ecrnx_vif->ndev, mgmt->sa,
+                                               mgmt->u.beacon.variable,
+                                               skb->len - offsetof(struct ieee80211_mgmt,
+                                                                   u.beacon.variable),
+                                               rxvect->rssi1, GFP_ATOMIC);
+        } else {
+                       //ECRNX_DBG("%s:%d beacon\n", __func__, __LINE__);
+            cfg80211_report_obss_beacon(ecrnx_hw->wiphy, skb->data, skb->len,
+                                        hw_rxhdr->phy_info.phy_prim20_freq,
+                                        rxvect->rssi1);
+        }
+    } else if ((ieee80211_is_deauth(mgmt->frame_control) ||
+                ieee80211_is_disassoc(mgmt->frame_control)) &&
+               (mgmt->u.deauth.reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
+                mgmt->u.deauth.reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)  // TODO: process unprot mgmt
+        cfg80211_rx_unprot_mlme_mgmt(ecrnx_vif->ndev, skb->data, skb->len);
+#endif
+    } else if ((ECRNX_VIF_TYPE(ecrnx_vif) == NL80211_IFTYPE_STATION) &&
+               (ieee80211_is_action(mgmt->frame_control) &&
+                (mgmt->u.action.category == 6))) {
+        // Wpa_supplicant will ignore the FT action frame if reported via cfg80211_rx_mgmt
+        // and cannot call cfg80211_ft_event from atomic context so defer message processing
+        ecrnx_rx_defer_skb(ecrnx_hw, ecrnx_vif, skb);
+    } else {
+        cfg80211_rx_mgmt(&ecrnx_vif->wdev, hw_rxhdr->phy_info.phy_prim20_freq,
+                 rxvect->rssi1, skb->data, skb->len, 0);
+    }
+}
+
+#if 0
+static void dump_mgmt_rx(struct hw_rxhdr *hw_rxhdr)
+{
+       ECRNX_DBG("%s, amsdu:%d, 80211_mpdu:%d, 4addr:%d, vif_idx:%d, sta_idx:%d, dst_idx:%d \n", \
+               __func__, \
+               hw_rxhdr->flags_is_amsdu, \
+               hw_rxhdr->flags_is_80211_mpdu, \
+               hw_rxhdr->flags_is_4addr, \
+               hw_rxhdr->flags_vif_idx, \
+               hw_rxhdr->flags_sta_idx, \
+               hw_rxhdr->flags_dst_idx);
+       
+       ECRNX_DBG("phy_prim20_freq: 0x%x \n", hw_rxhdr->phy_info.phy_prim20_freq);
+}
+#endif
+
+/**
+ * ecrnx_rx_mgmt_any - Process one 802.11 management frame
+ *
+ * @ecrnx_hw: main driver data
+ * @skb: skb received
+ * @rxhdr: HW rx descriptor
+ *
+ * Process the management frame and free the corresponding skb.
+ * If vif is not specified in the rx descriptor, the the frame is uploaded
+ * on all active vifs.
+ */
+static void ecrnx_rx_mgmt_any(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+                             struct hw_rxhdr *hw_rxhdr)
+{
+    struct ecrnx_vif *ecrnx_vif;
+    int vif_idx = hw_rxhdr->flags_vif_idx;
+
+       //ECRNX_DBG("%s:%d \n", __func__, __LINE__);
+    trace_mgmt_rx(hw_rxhdr->phy_info.phy_prim20_freq, vif_idx,
+                  hw_rxhdr->flags_sta_idx, (struct ieee80211_mgmt *)skb->data);
+
+    if (vif_idx == ECRNX_INVALID_VIF) {
+               //ECRNX_DBG("search the list \n");
+        list_for_each_entry(ecrnx_vif, &ecrnx_hw->vifs, list) {
+                       //ECRNX_DBG("find a item, vif_up:%d \n", ecrnx_vif->up);
+            if (! ecrnx_vif->up)
+                continue;
+                       //ECRNX_DBG("wpa up \n");
+            ecrnx_rx_mgmt(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr);
+        }
+    } else {
+        ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, vif_idx);
+        if (ecrnx_vif)
+            ecrnx_rx_mgmt(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr);
+    }
+
+    dev_kfree_skb(skb);
+}
+
+/**
+ * ecrnx_rx_rtap_hdrlen - Return radiotap header length
+ *
+ * @rxvect: Rx vector used to fill the radiotap header
+ * @has_vend_rtap: boolean indicating if vendor specific data is present
+ *
+ * Compute the length of the radiotap header based on @rxvect and vendor
+ * specific data (if any).
+ */
+static u8 ecrnx_rx_rtap_hdrlen(struct rx_vector_1 *rxvect,
+                              bool has_vend_rtap)
+{
+    u8 rtap_len;
+
+    /* Compute radiotap header length */
+    rtap_len = sizeof(struct ieee80211_radiotap_header) + 8;
+
+    // Check for multiple antennas
+    if (hweight32(rxvect->antenna_set) > 1)
+        // antenna and antenna signal fields
+        rtap_len += 4 * hweight8(rxvect->antenna_set);
+
+    // TSFT
+    if (!has_vend_rtap) {
+        rtap_len = ALIGN(rtap_len, 8);
+        rtap_len += 8;
+    }
+
+    // IEEE80211_HW_SIGNAL_DBM
+    rtap_len++;
+
+    // Check if single antenna
+    if (hweight32(rxvect->antenna_set) == 1)
+        rtap_len++; //Single antenna
+
+    // padding for RX FLAGS
+    rtap_len = ALIGN(rtap_len, 2);
+
+    // Check for HT frames
+    if ((rxvect->format_mod == FORMATMOD_HT_MF) ||
+        (rxvect->format_mod == FORMATMOD_HT_GF))
+        rtap_len += 3;
+
+    // Check for AMPDU
+    if (!(has_vend_rtap) && ((rxvect->format_mod >= FORMATMOD_VHT) ||
+                             ((rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) &&
+                                                     (rxvect->ht.aggregation)))) {
+        rtap_len = ALIGN(rtap_len, 4);
+        rtap_len += 8;
+    }
+
+    // Check for VHT frames
+    if (rxvect->format_mod == FORMATMOD_VHT) {
+        rtap_len = ALIGN(rtap_len, 2);
+        rtap_len += 12;
+    }
+
+    // Check for HE frames
+    if (rxvect->format_mod == FORMATMOD_HE_SU) {
+        rtap_len = ALIGN(rtap_len, 2);
+        rtap_len += sizeof(struct ieee80211_radiotap_he);
+    }
+
+    // Check for multiple antennas
+    if (hweight32(rxvect->antenna_set) > 1) {
+        // antenna and antenna signal fields
+        rtap_len += 2 * hweight8(rxvect->antenna_set);
+    }
+
+    // Check for vendor specific data
+    if (has_vend_rtap) {
+        /* vendor presence bitmap */
+        rtap_len += 4;
+        /* alignment for fixed 6-byte vendor data header */
+        rtap_len = ALIGN(rtap_len, 2);
+    }
+
+    return rtap_len;
+}
+
+/**
+ * ecrnx_rx_add_rtap_hdr - Add radiotap header to sk_buff
+ *
+ * @ecrnx_hw: main driver data
+ * @skb: skb received (will include the radiotap header)
+ * @rxvect: Rx vector
+ * @phy_info: Information regarding the phy
+ * @hwvect: HW Info (NULL if vendor specific data is available)
+ * @rtap_len: Length of the radiotap header
+ * @vend_rtap_len: radiotap vendor length (0 if not present)
+ * @vend_it_present: radiotap vendor present
+ *
+ * Builds a radiotap header and add it to @skb.
+ */
+static void ecrnx_rx_add_rtap_hdr(struct ecrnx_hw* ecrnx_hw,
+                                 struct sk_buff *skb,
+                                 struct rx_vector_1 *rxvect,
+                                 struct phy_channel_info_desc *phy_info,
+                                 struct hw_vect *hwvect,
+                                 int rtap_len,
+                                 u8 vend_rtap_len,
+                                 u32 vend_it_present)
+{
+    struct ieee80211_radiotap_header *rtap;
+    u8 *pos, rate_idx;
+    __le32 *it_present;
+    u32 it_present_val = 0;
+    bool fec_coding = false;
+    bool short_gi = false;
+    bool stbc = false;
+    bool aggregation = false;
+
+    rtap = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
+    memset((u8*) rtap, 0, rtap_len);
+
+    rtap->it_version = 0;
+    rtap->it_pad = 0;
+    rtap->it_len = cpu_to_le16(rtap_len + vend_rtap_len);
+
+    it_present = (__le32*)&rtap->it_present;
+
+    // Check for multiple antennas
+    if (hweight32(rxvect->antenna_set) > 1) {
+        int chain;
+        unsigned long chains = rxvect->antenna_set;
+
+        for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+            it_present_val |=
+                BIT(IEEE80211_RADIOTAP_EXT) |
+                BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
+            put_unaligned_le32(it_present_val, it_present);
+            it_present++;
+            it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
+                             BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+        }
+    }
+
+    // Check if vendor specific data is present
+    if (vend_rtap_len) {
+        it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+                          BIT(IEEE80211_RADIOTAP_EXT);
+        put_unaligned_le32(it_present_val, it_present);
+        it_present++;
+        it_present_val = vend_it_present;
+    }
+
+    put_unaligned_le32(it_present_val, it_present);
+    pos = (void *)(it_present + 1);
+
+    // IEEE80211_RADIOTAP_TSFT
+    if (hwvect) {
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+        // padding
+        while ((pos - (u8 *)rtap) & 7)
+            *pos++ = 0;
+        put_unaligned_le64((((u64)le32_to_cpu(hwvect->tsf_hi) << 32) +
+                            (u64)le32_to_cpu(hwvect->tsf_lo)), pos);
+        pos += 8;
+    }
+
+    // IEEE80211_RADIOTAP_FLAGS
+    rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_FLAGS);
+    if (hwvect && (!hwvect->status.frm_successful_rx))
+        *pos |= IEEE80211_RADIOTAP_F_BADFCS;
+    if (!rxvect->pre_type
+            && (rxvect->format_mod <= FORMATMOD_NON_HT_DUP_OFDM))
+        *pos |= IEEE80211_RADIOTAP_F_SHORTPRE;
+    pos++;
+
+    // IEEE80211_RADIOTAP_RATE
+    // check for HT, VHT or HE frames
+    if (rxvect->format_mod >= FORMATMOD_HE_SU) {
+        rate_idx = rxvect->he.mcs;
+        fec_coding = rxvect->he.fec;
+        stbc = rxvect->he.stbc;
+        aggregation = true;
+        *pos = 0;
+    } else if (rxvect->format_mod == FORMATMOD_VHT) {
+        rate_idx = rxvect->vht.mcs;
+        fec_coding = rxvect->vht.fec;
+        short_gi = rxvect->vht.short_gi;
+        stbc = rxvect->vht.stbc;
+        aggregation = true;
+        *pos = 0;
+    } else if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM) {
+        rate_idx = rxvect->ht.mcs;
+        fec_coding = rxvect->ht.fec;
+        short_gi = rxvect->ht.short_gi;
+        stbc = rxvect->ht.stbc;
+        aggregation = rxvect->ht.aggregation;
+        *pos = 0;
+    } else {
+        struct ieee80211_supported_band* band =
+                ecrnx_hw->wiphy->bands[phy_info->phy_band];
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
+        BUG_ON((rate_idx = legrates_lut[rxvect->leg_rate].idx) == -1);
+#ifdef CONFIG_ECRNX_5G
+        if (phy_info->phy_band == NL80211_BAND_5GHZ)
+            rate_idx -= 4;  /* ecrnx_ratetable_5ghz[0].hw_value == 4 */
+#endif
+        *pos = DIV_ROUND_UP(band->bitrates[rate_idx].bitrate, 5);
+    }
+    pos++;
+
+    // IEEE80211_RADIOTAP_CHANNEL
+    rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_CHANNEL);
+    put_unaligned_le16(phy_info->phy_prim20_freq, pos);
+    pos += 2;
+
+#ifdef CONFIG_ECRNX_5G
+    if (phy_info->phy_band == NL80211_BAND_5GHZ)
+        put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, pos);
+    else if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM)
+#else
+       if (rxvect->format_mod > FORMATMOD_NON_HT_DUP_OFDM)
+#endif
+        put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ, pos);
+    else
+        put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ, pos);
+    pos += 2;
+
+    if (hweight32(rxvect->antenna_set) == 1) {
+        // IEEE80211_RADIOTAP_DBM_ANTSIGNAL
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+        *pos++ = rxvect->rssi1;
+
+        // IEEE80211_RADIOTAP_ANTENNA
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_ANTENNA);
+        *pos++ = rxvect->antenna_set;
+    }
+
+    // IEEE80211_RADIOTAP_LOCK_QUALITY is missing
+    // IEEE80211_RADIOTAP_DB_ANTNOISE is missing
+
+    // IEEE80211_RADIOTAP_RX_FLAGS
+    rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RX_FLAGS);
+    // 2 byte alignment
+    if ((pos - (u8 *)rtap) & 1)
+        *pos++ = 0;
+    put_unaligned_le16(0, pos);
+    //Right now, we only support fcs error (no RX_FLAG_FAILED_PLCP_CRC)
+    pos += 2;
+
+    // Check if HT
+    if ((rxvect->format_mod == FORMATMOD_HT_MF) ||
+        (rxvect->format_mod == FORMATMOD_HT_GF)) {
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
+        *pos++ = (IEEE80211_RADIOTAP_MCS_HAVE_MCS |
+                 IEEE80211_RADIOTAP_MCS_HAVE_GI |
+                  IEEE80211_RADIOTAP_MCS_HAVE_BW |
+                  IEEE80211_RADIOTAP_MCS_HAVE_FMT |
+                  IEEE80211_RADIOTAP_MCS_HAVE_FEC |
+#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 17, 0)
+                  IEEE80211_RADIOTAP_MCS_HAVE_STBC |
+#endif
+                  0);
+        pos++;
+        *pos = 0;
+        if (short_gi)
+            *pos |= IEEE80211_RADIOTAP_MCS_SGI;
+        if (rxvect->ch_bw  == PHY_CHNL_BW_40)
+            *pos |= IEEE80211_RADIOTAP_MCS_BW_40;
+        if (rxvect->format_mod == FORMATMOD_HT_GF)
+            *pos |= IEEE80211_RADIOTAP_MCS_FMT_GF;
+        if (fec_coding)
+            *pos |= IEEE80211_RADIOTAP_MCS_FEC_LDPC;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
+        *pos++ |= stbc << 5;
+#else
+        *pos++ |= stbc << IEEE80211_RADIOTAP_MCS_STBC_SHIFT;
+#endif
+        *pos++ = rate_idx;
+    }
+
+    // check for HT or VHT frames
+    if (aggregation && hwvect) {
+        // 4 byte alignment
+        while ((pos - (u8 *)rtap) & 3)
+            pos++;
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_AMPDU_STATUS);
+        put_unaligned_le32(hwvect->ampdu_cnt, pos);
+        pos += 4;
+        put_unaligned_le32(0, pos);
+        pos += 4;
+    }
+
+    // Check for VHT frames
+    if (rxvect->format_mod == FORMATMOD_VHT) {
+        u16 vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+                          IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+        u8 vht_nss = rxvect->vht.nss + 1;
+
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+
+        if ((rxvect->ch_bw == PHY_CHNL_BW_160)
+                && phy_info->phy_center2_freq)
+            vht_details &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+        put_unaligned_le16(vht_details, pos);
+        pos += 2;
+
+        // flags
+        if (short_gi)
+            *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+        if (stbc)
+            *pos |= IEEE80211_RADIOTAP_VHT_FLAG_STBC;
+        pos++;
+
+        // bandwidth
+        if (rxvect->ch_bw == PHY_CHNL_BW_40)
+            *pos++ = 1;
+        if (rxvect->ch_bw == PHY_CHNL_BW_80)
+            *pos++ = 4;
+        else if ((rxvect->ch_bw == PHY_CHNL_BW_160)
+                && phy_info->phy_center2_freq)
+            *pos++ = 0; //80P80
+        else if  (rxvect->ch_bw == PHY_CHNL_BW_160)
+            *pos++ = 11;
+        else // 20 MHz
+            *pos++ = 0;
+
+        // MCS/NSS
+        *pos++ = (rate_idx << 4) | vht_nss;
+        *pos++ = 0;
+        *pos++ = 0;
+        *pos++ = 0;
+        if (fec_coding){
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0)
+            *pos |= 0x01;
+#else
+            *pos |= IEEE80211_RADIOTAP_CODING_LDPC_USER0;
+#endif
+        }
+        pos++;
+        // group ID
+        pos++;
+        // partial_aid
+        pos += 2;
+    }
+
+    // Check for HE frames
+    if (rxvect->format_mod >= FORMATMOD_HE_SU) {
+        struct ieee80211_radiotap_he he;
+        memset(&he, 0, sizeof(struct ieee80211_radiotap_he));
+        #define HE_PREP(f, val) cpu_to_le16(FIELD_PREP(IEEE80211_RADIOTAP_HE_##f, val))
+        #define D1_KNOWN(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_##f##_KNOWN)
+        #define D2_KNOWN(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_##f##_KNOWN)
+
+        he.data1 = D1_KNOWN(BSS_COLOR) | D1_KNOWN(BEAM_CHANGE) |
+                   D1_KNOWN(UL_DL) | D1_KNOWN(STBC) |
+                   D1_KNOWN(DOPPLER) | D1_KNOWN(DATA_DCM);
+        he.data2 = D2_KNOWN(GI) | D2_KNOWN(TXBF) | D2_KNOWN(TXOP);
+
+        he.data3 |= HE_PREP(DATA3_BSS_COLOR, rxvect->he.bss_color);
+        he.data3 |= HE_PREP(DATA3_BEAM_CHANGE, rxvect->he.beam_change);
+        he.data3 |= HE_PREP(DATA3_UL_DL, rxvect->he.uplink_flag);
+        he.data3 |= HE_PREP(DATA3_BSS_COLOR, rxvect->he.bss_color);
+        he.data3 |= HE_PREP(DATA3_DATA_DCM, rxvect->he.dcm);
+        he.data5 |= HE_PREP(DATA5_GI, rxvect->he.gi_type);
+        he.data5 |= HE_PREP(DATA5_TXBF, rxvect->he.beamformed);
+        he.data5 |= HE_PREP(DATA5_LTF_SIZE, rxvect->he.he_ltf_type + 1);
+        he.data6 |= HE_PREP(DATA6_DOPPLER, rxvect->he.doppler);
+        he.data6 |= HE_PREP(DATA6_TXOP, rxvect->he.txop_duration);
+        if (rxvect->format_mod != FORMATMOD_HE_TB) {
+            he.data1 |= (D1_KNOWN(DATA_MCS) | D1_KNOWN(CODING) |
+                         D1_KNOWN(SPTL_REUSE) | D1_KNOWN(BW_RU_ALLOC));
+        if (stbc) {
+            he.data6 |= HE_PREP(DATA6_NSTS, 2);
+            he.data3 |= HE_PREP(DATA3_STBC, 1);
+        } else {
+            he.data6 |= HE_PREP(DATA6_NSTS, rxvect->he.nss);
+        }
+
+        he.data3 |= HE_PREP(DATA3_DATA_MCS, rxvect->he.mcs);
+        he.data3 |= HE_PREP(DATA3_CODING, rxvect->he.fec);
+
+            he.data4 = HE_PREP(DATA4_SU_MU_SPTL_REUSE, rxvect->he.spatial_reuse);
+
+            if (rxvect->format_mod == FORMATMOD_HE_MU) {
+                he.data1 |= IEEE80211_RADIOTAP_HE_DATA1_FORMAT_MU;
+                he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+                                    rxvect->he.ru_size +
+                                    IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_26T);
+            } else {
+                if (rxvect->format_mod == FORMATMOD_HE_SU)
+                    he.data1 |= IEEE80211_RADIOTAP_HE_DATA1_FORMAT_SU;
+                else
+                    he.data1 |= IEEE80211_RADIOTAP_HE_DATA1_FORMAT_EXT_SU;
+
+        switch (rxvect->ch_bw) {
+        case PHY_CHNL_BW_20:
+            he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+                        IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_20MHZ);
+            break;
+        case PHY_CHNL_BW_40:
+            he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+                        IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_40MHZ);
+            break;
+        case PHY_CHNL_BW_80:
+            he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+                        IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_80MHZ);
+            break;
+        case PHY_CHNL_BW_160:
+            he.data5 |= HE_PREP(DATA5_DATA_BW_RU_ALLOC,
+                        IEEE80211_RADIOTAP_HE_DATA5_DATA_BW_RU_ALLOC_160MHZ);
+            break;
+        default:
+            WARN_ONCE(1, "Invalid SU BW %d\n", rxvect->ch_bw);
+        }
+            }
+        } else {
+            he.data1 |= IEEE80211_RADIOTAP_HE_DATA1_FORMAT_TRIG;
+        }
+
+        /* ensure 2 byte alignment */
+        while ((pos - (u8 *)rtap) & 1)
+            pos++;
+        rtap->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_HE);
+        memcpy(pos, &he, sizeof(he));
+        pos += sizeof(he);
+    }
+
+    // Rx Chains
+    if (hweight32(rxvect->antenna_set) > 1) {
+        int chain;
+        unsigned long chains = rxvect->antenna_set;
+        u8 rssis[4] = {rxvect->rssi1, rxvect->rssi1, rxvect->rssi1, rxvect->rssi1};
+
+        for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+            *pos++ = rssis[chain];
+            *pos++ = chain;
+        }
+    }
+}
+
+/**
+ * ecrnx_rx_monitor - Build radiotap header for skb an send it to netdev
+ *
+ * @ecrnx_hw: main driver data
+ * @ecrnx_vif: vif that received the buffer
+ * @skb: sk_buff received
+ * @hw_rxhdr_ptr: Pointer to HW RX header
+ * @rtap_len: Radiotap Header length
+ *
+ * Add radiotap header to the receved skb and send it to netdev
+ */
+static int ecrnx_rx_monitor(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                           struct sk_buff *skb,  struct hw_rxhdr *hw_rxhdr_ptr,
+                           u8 rtap_len)
+{
+    skb->dev = ecrnx_vif->ndev;
+
+    ECRNX_DBG("%s enter!!", __func__);
+    if (ecrnx_vif->wdev.iftype != NL80211_IFTYPE_MONITOR) {
+        netdev_err(ecrnx_vif->ndev, "not a monitor vif\n");
+        return -1;
+    }
+
+    /* Add RadioTap Header */
+    ecrnx_rx_add_rtap_hdr(ecrnx_hw, skb, &hw_rxhdr_ptr->hwvect.rx_vect1,
+                         &hw_rxhdr_ptr->phy_info, &hw_rxhdr_ptr->hwvect,
+                         rtap_len, 0, 0);
+
+    skb_reset_mac_header(skb);
+    skb->ip_summed = CHECKSUM_UNNECESSARY;
+    skb->pkt_type = PACKET_OTHERHOST;
+    skb->protocol = htons(ETH_P_802_2);
+
+    netif_receive_skb(skb);
+    ECRNX_DBG("%s exit!!", __func__);
+    return 0;
+}
+
+/**
+* ecrnx_rx_deferred - Work function to defer processing of buffer that cannot be
+* done in ecrnx_rxdataind (that is called in atomic context)
+*
+* @ws: work field within struct ecrnx_defer_rx
+*/
+void ecrnx_rx_deferred(struct work_struct *ws)
+{
+   struct ecrnx_defer_rx *rx = container_of(ws, struct ecrnx_defer_rx, work);
+   struct sk_buff *skb;
+
+   while ((skb = skb_dequeue(&rx->sk_list)) != NULL) {
+       // Currently only management frame can be deferred
+       struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+       struct ecrnx_defer_rx_cb *rx_cb = (struct ecrnx_defer_rx_cb *)skb->cb;
+
+       if (ieee80211_is_action(mgmt->frame_control) &&
+           (mgmt->u.action.category == 6)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+           struct cfg80211_ft_event_params ft_event;
+           struct ecrnx_vif *vif = rx_cb->vif;
+           u8 *action_frame = (u8 *)&mgmt->u.action;
+           u8 action_code = action_frame[1];
+           u16 status_code = *((u16 *)&action_frame[2 + 2 * ETH_ALEN]);
+
+           if ((action_code == 2) && (status_code == 0)) {
+               ft_event.target_ap = action_frame + 2 + ETH_ALEN;
+               ft_event.ies = action_frame + 2 + 2 * ETH_ALEN + 2;
+               ft_event.ies_len = skb->len - (ft_event.ies - (u8 *)mgmt);
+               ft_event.ric_ies = NULL;
+               ft_event.ric_ies_len = 0;
+               cfg80211_ft_event(rx_cb->vif->ndev, &ft_event);
+               vif->sta.flags |= ECRNX_STA_FT_OVER_DS;
+               memcpy(vif->sta.ft_target_ap, ft_event.target_ap, ETH_ALEN);
+
+           }
+#endif
+       } else if (ieee80211_is_auth(mgmt->frame_control)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
+           struct cfg80211_ft_event_params ft_event;
+           struct ecrnx_vif *vif = rx_cb->vif;
+           ft_event.target_ap = vif->sta.ft_target_ap;
+           ft_event.ies = mgmt->u.auth.variable;
+           ft_event.ies_len = (skb->len -
+                               offsetof(struct ieee80211_mgmt, u.auth.variable));
+           ft_event.ric_ies = NULL;
+           ft_event.ric_ies_len = 0;
+           cfg80211_ft_event(rx_cb->vif->ndev, &ft_event);
+           vif->sta.flags |= ECRNX_STA_FT_OVER_AIR;
+#endif
+       } else {
+           netdev_warn(rx_cb->vif->ndev, "Unexpected deferred frame fctl=0x%04x",
+                       mgmt->frame_control);
+       }
+
+       dev_kfree_skb(skb);
+   }
+}
+
+/**
+ * ecrnx_unsup_rx_vec_ind() - IRQ handler callback for %IPC_IRQ_E2A_UNSUP_RX_VEC
+ *
+ * LMAC has triggered an IT saying that a rx vector of an unsupported frame has been
+ * captured and sent to upper layer. Then we need to fill the rx status, create a vendor
+ * specific header and fill it with the HT packet length. Finally, we need to specify at
+ * least 2 bytes of data and send the sk_buff to mac80211.
+ *
+ * @pthis: Pointer to main driver data
+ * @hostid: Pointer to IPC elem from e2aradars_pool
+ */
+u8 ecrnx_unsup_rx_vec_ind(void *pthis, void *hostid) {
+    struct ecrnx_hw *ecrnx_hw = pthis;
+    struct ecrnx_ipc_skb_elem *elem = hostid;
+    struct rx_vector_desc *rx_desc;
+    struct sk_buff *skb;
+    struct rx_vector_1 *rx_vect1;
+    struct phy_channel_info_desc *phy_info;
+    struct vendor_radiotap_hdr *rtap;
+    u16 ht_length;
+    struct ecrnx_vif *ecrnx_vif;
+    struct rx_vector_desc rx_vect_desc;
+    u8 rtap_len, vend_rtap_len = sizeof(*rtap);
+
+    dma_sync_single_for_cpu(ecrnx_hw->dev, elem->dma_addr,
+                            sizeof(struct rx_vector_desc), DMA_FROM_DEVICE);
+
+    skb = elem->skb;
+    if (((struct rx_vector_desc *) (skb->data))->pattern == 0) {
+        /*sync is needed even if the driver did not modify the memory*/
+        dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr,
+                                     sizeof(struct rx_vector_desc), DMA_FROM_DEVICE);
+        return -1;
+    }
+
+    if (ecrnx_hw->monitor_vif == ECRNX_INVALID_VIF) {
+        /* Unmap will synchronize buffer for CPU */
+#ifndef CONFIG_ECRNX_ESWIN
+       dma_unmap_single(ecrnx_hw->dev, elem->dma_addr, ecrnx_hw->ipc_env->unsuprxvec_bufsz,
+                         DMA_FROM_DEVICE);
+#endif
+        elem->skb = NULL;
+
+        /* Free skb */
+        dev_kfree_skb(skb);
+
+#ifndef CONFIG_ECRNX_ESWIN
+        /* Allocate and push a new buffer to fw to replace this one */
+        if (ecrnx_ipc_unsup_rx_vec_elem_allocs(ecrnx_hw, elem))
+            dev_err(ecrnx_hw->dev, "Failed to alloc new unsupported rx vector buf\n");
+        return -1;
+#endif
+    }
+
+    ecrnx_vif = ecrnx_hw->vif_table[ecrnx_hw->monitor_vif];
+    skb->dev = ecrnx_vif->ndev;
+    memcpy(&rx_vect_desc, skb->data, sizeof(rx_vect_desc));
+    rx_desc = &rx_vect_desc;
+
+    rx_vect1 = (struct rx_vector_1 *) (rx_desc->rx_vect1);
+    ecrnx_rx_vector_convert(ecrnx_hw->machw_type, rx_vect1, NULL);
+    phy_info = (struct phy_channel_info_desc *) (&rx_desc->phy_info);
+    if (rx_vect1->format_mod >= FORMATMOD_VHT)
+        ht_length = 0;
+    else
+        ht_length = (u16) le32_to_cpu(rx_vect1->ht.length);
+
+    // Reserve space for radiotap
+    skb_reserve(skb, RADIOTAP_HDR_MAX_LEN);
+
+    /* Fill vendor specific header with fake values */
+    rtap = (struct vendor_radiotap_hdr *) skb->data;
+    rtap->oui[0] = 0x00;
+    rtap->oui[1] = 0x25;
+    rtap->oui[2] = 0x3A;
+    rtap->subns  = 0;
+    rtap->len = sizeof(ht_length);
+    put_unaligned_le16(ht_length, rtap->data);
+    vend_rtap_len += rtap->len;
+    skb_put(skb, vend_rtap_len);
+
+    /* Copy fake data */
+    put_unaligned_le16(0, skb->data + vend_rtap_len);
+    skb_put(skb, UNSUP_RX_VEC_DATA_LEN);
+
+    /* Get RadioTap Header length */
+    rtap_len = ecrnx_rx_rtap_hdrlen(rx_vect1, true);
+
+    /* Check headroom space */
+    if (skb_headroom(skb) < rtap_len) {
+        netdev_err(ecrnx_vif->ndev, "not enough headroom %d need %d\n", skb_headroom(skb), rtap_len);
+        return -1;
+    }
+
+    /* Add RadioTap Header */
+    ecrnx_rx_add_rtap_hdr(ecrnx_hw, skb, rx_vect1, phy_info, NULL,
+                         rtap_len, vend_rtap_len, BIT(0));
+
+    skb_reset_mac_header(skb);
+    skb->ip_summed = CHECKSUM_UNNECESSARY;
+    skb->pkt_type = PACKET_OTHERHOST;
+    skb->protocol = htons(ETH_P_802_2);
+
+    /* Unmap will synchronize buffer for CPU */
+#ifndef CONFIG_ECRNX_ESWIN
+    dma_unmap_single(ecrnx_hw->dev, elem->dma_addr, ecrnx_hw->ipc_env->unsuprxvec_bufsz,
+                     DMA_FROM_DEVICE);
+#endif
+    elem->skb = NULL;
+
+    netif_receive_skb(skb);
+
+#ifndef CONFIG_ECRNX_ESWIN
+    /* Allocate and push a new buffer to fw to replace this one */
+    if (ecrnx_ipc_unsup_rx_vec_elem_allocs(ecrnx_hw, elem))
+        netdev_err(ecrnx_vif->ndev, "Failed to alloc new unsupported rx vector buf\n");
+#endif
+    return 0;
+}
+
+/**
+ * ecrnx_rxdataind - Process rx buffer
+ *
+ * @pthis: Pointer to the object attached to the IPC structure
+ *         (points to struct ecrnx_hw is this case)
+ * @hostid: Address of the RX descriptor
+ *
+ * This function is called for each buffer received by the fw
+ *
+ */
+#ifndef CONFIG_ECRNX_ESWIN
+u8 ecrnx_rxdataind(void *pthis, void *hostid)
+{
+    struct ecrnx_hw *ecrnx_hw = pthis;
+    struct ecrnx_ipc_elem *elem = hostid;
+    struct hw_rxhdr *hw_rxhdr;
+    struct rxdesc_tag *rxdesc;
+    struct ecrnx_vif *ecrnx_vif;
+    struct sk_buff *skb = NULL;
+    int rx_buff_idx;
+    int msdu_offset = sizeof(struct hw_rxhdr) + 2;
+
+    int peek_len    = msdu_offset + sizeof(struct ethhdr);
+    u16_l status;
+
+    REG_SW_SET_PROFILING(ecrnx_hw, SW_PROF_ECRNXDATAIND);
+
+    if(!pthis || !hostid)
+    {
+        return -1;
+    }
+
+    /* Get the ownership of the descriptor */
+    dma_sync_single_for_cpu(ecrnx_hw->dev, elem->dma_addr,
+                            sizeof(struct rxdesc_tag), DMA_FROM_DEVICE);
+
+    rxdesc = elem->addr;
+    status = rxdesc->status;
+
+    /* check that frame is completely uploaded */
+    if (!status){
+        /* Get the ownership of the descriptor */
+        dma_sync_single_for_device(ecrnx_hw->dev, elem->dma_addr,
+                                   sizeof(struct rxdesc_tag), DMA_FROM_DEVICE);
+        return -1;
+    }
+
+    /* Get the buffer linked with the received descriptor */
+    rx_buff_idx = ECRNX_RXBUFF_HOSTID_TO_IDX(rxdesc->host_id);
+    if (ECRNX_RXBUFF_VALID_IDX(rx_buff_idx))
+        skb = ecrnx_hw->rxbuf_elems.skb[rx_buff_idx];
+
+    if (!skb){
+        dev_err(ecrnx_hw->dev, "RX Buff invalid idx [%d]\n", rx_buff_idx);
+        return -1;
+    }
+
+    /* Check the pattern */
+    if (ECRNX_RXBUFF_PATTERN_GET(skb) != ecrnx_rxbuff_pattern) {
+        dev_err(ecrnx_hw->dev, "RX Buff Pattern not correct\n");
+        BUG();
+    }
+
+    /* Check if we need to delete the buffer */
+    if (status & RX_STAT_DELETE) {
+        /* Remove the SK buffer from the rxbuf_elems table */
+        ecrnx_ipc_rxbuf_elem_pull(ecrnx_hw, skb);
+        /* Free the buffer */
+        dev_kfree_skb(skb);
+        goto end;
+    }
+
+    /* Check if we need to forward the buffer coming from a monitor interface */
+    if (status & RX_STAT_MONITOR) {
+        struct sk_buff *skb_monitor;
+        struct hw_rxhdr hw_rxhdr_copy;
+        u8 rtap_len;
+        u16 frm_len;
+
+        //Check if monitor interface exists and is open
+        ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, ecrnx_hw->monitor_vif);
+        if (!ecrnx_vif) {
+            dev_err(ecrnx_hw->dev, "Received monitor frame but there is no monitor interface open\n");
+            goto check_len_update;
+        }
+
+        hw_rxhdr = (struct hw_rxhdr *)skb->data;
+        ecrnx_rx_vector_convert(ecrnx_hw->machw_type,
+                               &hw_rxhdr->hwvect.rx_vect1,
+                               &hw_rxhdr->hwvect.rx_vect2);
+        rtap_len = ecrnx_rx_rtap_hdrlen(&hw_rxhdr->hwvect.rx_vect1, false);
+
+        // Move skb->data pointer to MAC Header or Ethernet header
+        skb->data += msdu_offset;
+
+        //Save frame length
+        frm_len = le32_to_cpu(hw_rxhdr->hwvect.len);
+
+        // Reserve space for frame
+        skb->len = frm_len;
+
+        if (status == RX_STAT_MONITOR) {
+            /* Remove the SK buffer from the rxbuf_elems table. It will also
+               unmap the buffer and then sync the buffer for the cpu */
+            ecrnx_ipc_rxbuf_elem_pull(ecrnx_hw, skb);
+
+            //Check if there is enough space to add the radiotap header
+            if (skb_headroom(skb) > rtap_len) {
+
+                skb_monitor = skb;
+
+                //Duplicate the HW Rx Header to override with the radiotap header
+                memcpy(&hw_rxhdr_copy, hw_rxhdr, sizeof(hw_rxhdr_copy));
+
+                hw_rxhdr = &hw_rxhdr_copy;
+            } else {
+                //Duplicate the skb and extend the headroom
+                skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+
+                //Reset original skb->data pointer
+                skb->data = (void*) hw_rxhdr;
+            }
+        }
+        else
+        {
+        #ifdef CONFIG_ECRNX_MON_DATA
+            // Check if MSDU
+            if (!hw_rxhdr->flags_is_80211_mpdu) {
+                // MSDU
+                //Extract MAC header
+                u16 machdr_len = hw_rxhdr->mac_hdr_backup.buf_len;
+                u8* machdr_ptr = hw_rxhdr->mac_hdr_backup.buffer;
+
+                //Pull Ethernet header from skb
+                skb_pull(skb, sizeof(struct ethhdr));
+
+                // Copy skb and extend for adding the radiotap header and the MAC header
+                skb_monitor = skb_copy_expand(skb,
+                                              rtap_len + machdr_len,
+                                              0, GFP_ATOMIC);
+
+                //Reserve space for the MAC Header
+                skb_push(skb_monitor, machdr_len);
+
+                //Copy MAC Header
+                memcpy(skb_monitor->data, machdr_ptr, machdr_len);
+
+                //Update frame length
+                frm_len += machdr_len - sizeof(struct ethhdr);
+            } else {
+                // MPDU
+                skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+            }
+
+            //Reset original skb->data pointer
+            skb->data = (void*) hw_rxhdr;
+        #else
+            //Reset original skb->data pointer
+            skb->data = (void*) hw_rxhdr;
+
+            wiphy_err(ecrnx_hw->wiphy, "RX status %d is invalid when MON_DATA is disabled\n", status);
+            goto check_len_update;
+        #endif
+        }
+
+        skb_reset_tail_pointer(skb);
+        skb->len = 0;
+        skb_reset_tail_pointer(skb_monitor);
+        skb_monitor->len = 0;
+
+        skb_put(skb_monitor, frm_len);
+        if (ecrnx_rx_monitor(ecrnx_hw, ecrnx_vif, skb_monitor, hw_rxhdr, rtap_len))
+            dev_kfree_skb(skb_monitor);
+
+        if (status == RX_STAT_MONITOR) {
+            status |= RX_STAT_ALLOC;
+            if (skb_monitor != skb) {
+                dev_kfree_skb(skb);
+            }
+        }
+    }
+
+check_len_update:
+    /* Check if we need to update the length */
+    if (status & RX_STAT_LEN_UPDATE) {
+        dma_addr_t dma_addr = ECRNX_RXBUFF_DMA_ADDR_GET(skb);
+        dma_sync_single_for_cpu(ecrnx_hw->dev, dma_addr,
+                                peek_len, DMA_FROM_DEVICE);
+
+        hw_rxhdr = (struct hw_rxhdr *)skb->data;
+
+        hw_rxhdr->hwvect.len = rxdesc->frame_len;
+
+        if (status & RX_STAT_ETH_LEN_UPDATE) {
+            /* Update Length Field inside the Ethernet Header */
+            struct ethhdr *hdr = (struct ethhdr *)((u8 *)hw_rxhdr + msdu_offset);
+
+            hdr->h_proto = htons(rxdesc->frame_len - sizeof(struct ethhdr));
+        }
+
+        dma_sync_single_for_device(ecrnx_hw->dev, dma_addr,
+                                   peek_len, DMA_BIDIRECTIONAL);
+        goto end;
+    }
+
+    /* Check if it must be discarded after informing upper layer */
+    if (status & RX_STAT_SPURIOUS) {
+        struct ieee80211_hdr *hdr;
+
+        /* Read mac header to obtain Transmitter Address */
+        ecrnx_ipc_rxbuf_elem_sync(ecrnx_hw, skb, msdu_offset + sizeof(*hdr));
+
+        hw_rxhdr = (struct hw_rxhdr *)skb->data;
+        hdr = (struct ieee80211_hdr *)(skb->data + msdu_offset);
+        ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, hw_rxhdr->flags_vif_idx);
+        if (ecrnx_vif) {
+            cfg80211_rx_spurious_frame(ecrnx_vif->ndev, hdr->addr2, GFP_ATOMIC);
+        }
+        ecrnx_ipc_rxbuf_elem_repush(ecrnx_hw, skb);
+        goto end;
+    }
+
+    /* Check if we need to forward the buffer */
+    if (status & RX_STAT_FORWARD) {
+        struct ecrnx_sta *sta = NULL;
+
+        /* Remove the SK buffer from the rxbuf_elems table. It will also
+           unmap the buffer and then sync the buffer for the cpu */
+        ecrnx_ipc_rxbuf_elem_pull(ecrnx_hw, skb);
+
+        hw_rxhdr = (struct hw_rxhdr *)skb->data;
+        ecrnx_rx_vector_convert(ecrnx_hw->machw_type,
+                               &hw_rxhdr->hwvect.rx_vect1,
+                               &hw_rxhdr->hwvect.rx_vect2);
+        skb_reserve(skb, msdu_offset);
+        skb_put(skb, le32_to_cpu(hw_rxhdr->hwvect.len));
+        if (hw_rxhdr->flags_sta_idx != ECRNX_INVALID_STA) {
+            sta = &ecrnx_hw->sta_table[hw_rxhdr->flags_sta_idx];
+            ecrnx_rx_statistic(ecrnx_hw, hw_rxhdr, sta);
+        }
+
+        if (hw_rxhdr->flags_is_80211_mpdu) {
+            ecrnx_rx_mgmt_any(ecrnx_hw, skb, hw_rxhdr);
+        } else {
+            ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, hw_rxhdr->flags_vif_idx);
+
+            if (!ecrnx_vif) {
+                dev_err(ecrnx_hw->dev, "Frame received but no active vif (%d)",
+                        hw_rxhdr->flags_vif_idx);
+                dev_kfree_skb(skb);
+                goto check_alloc;
+            }
+
+            if (sta) {
+
+                if (sta->vlan_idx != ecrnx_vif->vif_index) {
+                    ecrnx_vif = ecrnx_hw->vif_table[sta->vlan_idx];
+                    if (!ecrnx_vif) {
+                        dev_kfree_skb(skb);
+                        goto check_alloc;
+                    }
+                }
+
+                if (hw_rxhdr->flags_is_4addr && !ecrnx_vif->use_4addr) {
+                    cfg80211_rx_unexpected_4addr_frame(ecrnx_vif->ndev,
+                                                       sta->mac_addr, GFP_ATOMIC);
+                }
+            }
+
+            skb->priority = 256 + hw_rxhdr->flags_user_prio;
+            if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr))
+                dev_kfree_skb(skb);
+        }
+    }
+
+check_alloc:
+    /* Check if we need to allocate a new buffer */
+    if ((status & RX_STAT_ALLOC) &&
+        ecrnx_ipc_rxbuf_elem_allocs(ecrnx_hw)) {
+        dev_err(ecrnx_hw->dev, "Failed to alloc new RX buf\n");
+    }
+
+end:
+    REG_SW_CLEAR_PROFILING(ecrnx_hw, SW_PROF_ECRNXDATAIND);
+
+    /* Reset and repush descriptor to FW */
+    ecrnx_ipc_rxdesc_elem_repush(ecrnx_hw, elem);
+
+    return 0;
+}
+
+#else 
+
+#ifdef CONFIG_ESWIN_RX_REORDER
+
+static bool ecrnx_rx_data_pn_check(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb, u8_l vif_idx, u8_l sta_idx, u8_l tid, u8_l is_ga)
+{
+    struct ecrnx_sta *sta;
+    struct ecrnx_vif *vif;
+    u64_l *last_pn;
+    u64_l pn;
+
+    memcpy(&pn, skb->data, sizeof(u64_l));
+
+    if (is_ga) {
+        last_pn = &ecrnx_hw->vif_table[vif_idx]->rx_pn[tid];
+    } else if(ECRNX_INVALID_STA != sta_idx) {
+        last_pn = &ecrnx_hw->sta_table[sta_idx].rx_pn[tid];
+    } else
+    {
+        return true;
+    }
+
+    //printk("sta_idx:%d tid:%d pn:%llu last:%llu\n ", sta_idx, tid, pn, *last_pn);
+    if (pn > (*last_pn)){
+        *last_pn = pn;
+        return true;
+    }
+
+    return false;
+}
+
+void ecrnx_rx_reord_msdu_free(spinlock_t *lock, struct list_head *q, struct list_head *list)
+{
+    spin_lock_bh(lock);
+    list_add(list, q);
+    spin_unlock_bh(lock);
+}
+
+struct reord_msdu_info *ecrnx_rx_reord_msdu_alloc(spinlock_t *lock, struct list_head *q)
+{
+    struct reord_msdu_info *pmsdu;
+
+    spin_lock_bh(lock);
+    if (list_empty(q)) {
+        spin_unlock_bh(lock);
+        return NULL;
+    }
+
+    pmsdu = list_entry(q->next, struct reord_msdu_info, rx_msdu_list);
+    list_del_init(q->next);
+    spin_unlock_bh(lock);
+
+    return pmsdu;
+}
+
+int ecrnx_rx_reord_single_msdu(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, struct reord_msdu_info *pmsdu)
+{
+    struct list_head *rx_msdu_free_list = NULL;
+    struct sk_buff *skb = NULL;
+    struct hw_rxhdr* hw_rxhdr;
+    //static u16_l last_sn[TID_MAX] = {0};
+
+    rx_msdu_free_list = &ecrnx_hw->rx_msdu_free_list;
+    skb = pmsdu->skb;
+    if (skb == NULL) {
+        ECRNX_ERR("skb is NULL\n");
+        return -1;
+    }
+
+    if (pmsdu->need_pn_check) {
+        if (ecrnx_rx_data_pn_check(ecrnx_hw, skb,  pmsdu->hw_rxhdr->flags_vif_idx, pmsdu->hw_rxhdr->flags_sta_idx, pmsdu->tid, pmsdu->is_ga)){
+            skb_pull(skb, 8);
+        } else { 
+            ECRNX_DBG("rx_data_check_pn error\n");
+            dev_kfree_skb(skb);
+            goto end;
+        }
+    }
+#if 0
+    if ((last_sn[pmsdu->tid] != pmsdu->sn) && ((last_sn[pmsdu->tid] + 1) % 4096 != pmsdu->sn))
+    {
+        ECRNX_PRINT("miss[%d] last:%d sn=%d\n",pmsdu->tid,last_sn[pmsdu->tid], pmsdu->sn);
+    }
+
+    last_sn[pmsdu->tid] = pmsdu->sn;
+#endif
+    if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, pmsdu->hw_rxhdr))
+        dev_kfree_skb(skb);
+
+end:
+    pmsdu->skb = NULL;
+    ecrnx_rx_reord_msdu_free(&ecrnx_hw->rx_msdu_free_lock, rx_msdu_free_list, &pmsdu->rx_msdu_list);
+
+    return 0;
+}
+
+
+void ecrnx_rx_reord_timer_update(struct ecrnx_hw *ecrnx_hw, struct reord_cntrl *reord_cntrl, int force)
+{
+    struct list_head *phead, *plist;
+    struct reord_msdu_info *pmsdu;
+    bool update = false;
+
+    if (force == true) {
+        phead = &reord_cntrl->reord_list;
+        if (list_empty(phead)) {
+            goto end;
+        }
+
+        plist = phead->next;
+        pmsdu = list_entry(plist, struct reord_msdu_info, reord_pending_list);
+        reord_cntrl->win_start = pmsdu->sn;
+    }
+
+    phead = &reord_cntrl->reord_list;
+    if (list_empty(phead)) {
+        goto end;
+    }
+
+    list_for_each_entry(pmsdu, phead, reord_pending_list) {
+        if (!SN_LESS(reord_cntrl->win_start, pmsdu->sn)) {
+            if (SN_EQUAL(reord_cntrl->win_start, pmsdu->sn)) {
+                reord_cntrl->win_start = (reord_cntrl->win_start + 1) & 0xFFF;
+            }
+        } else {
+            update = true;
+            break;
+        }
+    }
+
+end:
+    if (update == true) {
+        if (!timer_pending(&reord_cntrl->reord_timer)) {
+            mod_timer(&reord_cntrl->reord_timer, jiffies + msecs_to_jiffies(ECRNX_REORD_TIMEOUT));
+        }
+    } else {
+        if(timer_pending(&reord_cntrl->reord_timer)) {
+            del_timer(&reord_cntrl->reord_timer);
+        }
+    }
+
+}
+
+void ecrnx_rx_reord_list_flush(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, struct reord_cntrl *reord_cntrl)
+{
+    struct list_head *phead, *plist;
+    struct reord_msdu_info *pmsdu;
+
+    phead = &reord_cntrl->reord_list;
+    while (1) {
+        if (list_empty(phead)) {
+            break;
+        }
+
+        plist = phead->next;
+        pmsdu = list_entry(plist, struct reord_msdu_info, reord_pending_list);
+
+        if (!SN_LESS(reord_cntrl->win_start, pmsdu->sn)) {
+            list_del_init(&(pmsdu->reord_pending_list));
+            ecrnx_rx_reord_single_msdu(ecrnx_hw, ecrnx_vif, pmsdu);
+        } else {
+            break;
+        }
+    }
+}
+
+
+void ecrnx_rx_reord_timeout_handler(struct timer_list *t)
+{
+    struct reord_cntrl *reord_cntrl = from_timer(reord_cntrl, t, reord_timer);
+
+    if(!reord_cntrl->valid || !reord_cntrl->active)
+        return ;
+
+    if(!work_pending(&reord_cntrl->reord_timer_work))
+        schedule_work(&reord_cntrl->reord_timer_work);
+
+}
+
+void ecrnx_rx_reord_timeout_worker(struct work_struct *work)
+{
+    struct reord_cntrl *reord_cntrl = container_of(work, struct reord_cntrl, reord_timer_work);
+    struct ecrnx_hw *ecrnx_hw = reord_cntrl->ecrnx_hw;
+    struct ecrnx_vif *ecrnx_vif = reord_cntrl->ecrnx_vif;
+
+    if(!reord_cntrl->valid || !reord_cntrl->active)
+        return ;
+
+    spin_lock_bh(&reord_cntrl->reord_list_lock);
+
+    ecrnx_rx_reord_timer_update(ecrnx_hw, reord_cntrl, true);
+
+    ecrnx_rx_reord_list_flush(ecrnx_hw, ecrnx_vif, reord_cntrl);
+    spin_unlock_bh(&reord_cntrl->reord_list_lock);
+
+    return ;
+}
+
+void ecrnx_rx_reord_sta_init(struct ecrnx_hw* ecrnx_hw, struct ecrnx_vif *ecrnx_vif, u8 sta_idx)
+{
+    struct reord_cntrl *reord_cntrl = NULL;
+    u32_l i = 0;
+
+    ECRNX_DBG("%s sta_idx:%d\n", __func__, sta_idx);
+    for (i = 0; i < TID_MAX; i++) {
+        reord_cntrl = &ecrnx_hw->sta_table[sta_idx].reord_cntrl[i];
+        if (!reord_cntrl->valid) {
+            reord_cntrl->active = true;
+            reord_cntrl->win_start = 0xffff;
+            reord_cntrl->win_size = ECRNX_REORD_WINSIZE;
+            reord_cntrl->ecrnx_hw = ecrnx_hw;
+            reord_cntrl->ecrnx_vif = ecrnx_vif;
+            INIT_LIST_HEAD(&reord_cntrl->reord_list);
+            spin_lock_init(&reord_cntrl->reord_list_lock);
+            timer_setup(&reord_cntrl->reord_timer, ecrnx_rx_reord_timeout_handler, 0);
+            INIT_WORK(&reord_cntrl->reord_timer_work, ecrnx_rx_reord_timeout_worker);
+            reord_cntrl->valid = true;
+        }
+    }
+
+}
+
+int ecrnx_rx_reord_tid_flush(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, struct sk_buff *skb, u8 sta_idx, u8 tid)
+{
+    struct reord_cntrl *reord_cntrl;
+    struct list_head *phead, *plist;
+    struct reord_msdu_info *pmsdu;
+
+    if (sta_idx == ECRNX_INVALID_STA)
+        return -1;
+
+    reord_cntrl = &ecrnx_hw->sta_table[sta_idx].reord_cntrl[tid];
+
+    if(!reord_cntrl->valid || !reord_cntrl->active)
+        return -1;
+
+    spin_lock(&reord_cntrl->reord_list_lock);
+    phead = &reord_cntrl->reord_list;
+    while (1) {
+        if (list_empty(phead)) {
+            break;
+        }
+        plist = phead->next;
+        pmsdu = list_entry(plist, struct reord_msdu_info, reord_pending_list);
+        ecrnx_rx_reord_single_msdu(ecrnx_hw, ecrnx_vif, pmsdu);
+        list_del_init(&(pmsdu->reord_pending_list));
+    }
+
+    //printk("flush:sta_idx:%d tid=%d \n", sta_idx,tid);
+    reord_cntrl->active = false;
+    spin_unlock(&reord_cntrl->reord_list_lock);
+    if (timer_pending(&reord_cntrl->reord_timer))
+        del_timer_sync(&reord_cntrl->reord_timer);
+    //cancel_work_sync(&reord_cntrl->reord_timer_work);
+
+    return 0;
+}
+
+void ecrnx_rx_reord_sta_deinit(struct ecrnx_hw* ecrnx_hw, u8 sta_idx, bool is_del)
+{
+    struct reord_cntrl *reord_cntrl = NULL;
+    u32_l i = 0;
+
+    if (ecrnx_hw == NULL || sta_idx >= (NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX)) {
+        return;
+    }
+    ECRNX_DBG("%s sta_idx:%d\n", __func__, sta_idx);
+
+    for (i=0; i < TID_MAX; i++) {
+        struct reord_msdu_info *req, *next;
+        reord_cntrl = &ecrnx_hw->sta_table[sta_idx].reord_cntrl[i];
+        if (reord_cntrl->valid) {
+            reord_cntrl->valid = false;
+            if(reord_cntrl->active){
+                reord_cntrl->active = false;
+                if (timer_pending(&reord_cntrl->reord_timer)) {
+                    del_timer_sync(&reord_cntrl->reord_timer);
+                }
+
+                if (!is_del) {
+                    cancel_work_sync(&reord_cntrl->reord_timer_work);
+                }
+            }
+
+            spin_lock(&reord_cntrl->reord_list_lock);
+            list_for_each_entry_safe(req, next, &reord_cntrl->reord_list, reord_pending_list) {
+                list_del_init(&req->reord_pending_list);
+                if(req->skb != NULL)
+                    dev_kfree_skb(req->skb);
+                req->skb = NULL;
+                ecrnx_rx_reord_msdu_free(&ecrnx_hw->rx_msdu_free_lock, &ecrnx_hw->rx_msdu_free_list, &req->rx_msdu_list);
+            }
+            spin_unlock(&reord_cntrl->reord_list_lock);
+        }
+    }
+
+}
+
+static struct reord_msdu_info *ecrnx_rx_reord_queue_init(struct list_head *q, int qsize)
+{
+    int i;
+    struct reord_msdu_info *req, *reqs;
+
+    reqs = vmalloc(qsize*sizeof(struct reord_msdu_info));
+    if (reqs == NULL)
+        return NULL;
+
+    req = reqs;
+    for (i = 0; i < qsize; i++)
+    {
+        INIT_LIST_HEAD(&req->rx_msdu_list);
+        list_add(&req->rx_msdu_list, q);
+        req++;
+    }
+
+    return reqs;
+}
+
+void ecrnx_rx_reord_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+    struct reord_msdu_info *req, *next;
+    u32_l sta_idx;
+
+    ECRNX_DBG("%s\n", __func__);
+    for (sta_idx = 0; sta_idx < NX_REMOTE_STA_MAX + NX_VIRT_DEV_MAX; sta_idx++)
+    {
+        if (ecrnx_hw->sta_table[sta_idx].valid)
+        {
+             ecrnx_rx_reord_sta_deinit(ecrnx_hw, sta_idx, false);
+        }
+    }
+    list_for_each_entry_safe(req, next, &ecrnx_hw->rx_msdu_free_list, rx_msdu_list) {
+        list_del_init(&req->rx_msdu_list);
+    }
+
+    if (ecrnx_hw->rx_reord_buf)
+        vfree(ecrnx_hw->rx_reord_buf);
+}
+
+void ecrnx_rx_reord_init(struct ecrnx_hw *ecrnx_hw)
+{
+    ECRNX_DBG("%s\n", __func__);
+    INIT_LIST_HEAD(&ecrnx_hw->rx_msdu_free_list);
+    spin_lock_init(&ecrnx_hw->rx_msdu_free_lock);
+    ecrnx_hw->rx_reord_buf = ecrnx_rx_reord_queue_init(&ecrnx_hw->rx_msdu_free_list, ECRNX_REORD_RX_MSDU_CNT);
+    spin_lock_init(&ecrnx_hw->rx_reord_lock);
+    INIT_LIST_HEAD(&ecrnx_hw->rx_reord_list);
+
+}
+
+int ecrnx_rx_reord_sn_check(struct reord_cntrl *reord_cntrl, u16 sn)
+{
+    u16 win_size = reord_cntrl->win_size;
+    u16 win_end = (reord_cntrl->win_start + win_size -1) & 0xFFF;
+
+    if (reord_cntrl->win_start == 0xFFFF) {
+        reord_cntrl->win_start = sn;
+    }
+
+    if (SN_LESS(sn, reord_cntrl->win_start)) {
+        return -1;
+    }
+
+    if (SN_EQUAL(sn, reord_cntrl->win_start)){
+        reord_cntrl->win_start = (reord_cntrl->win_start + 1) & 0xFFF;
+    } else if (SN_LESS(win_end, sn)) {
+        if (sn >= (win_size-1))
+            reord_cntrl->win_start = sn-(win_size-1);
+        else
+            reord_cntrl->win_start = 0xFFF - (win_size - (sn + 1)) + 1;
+    }
+
+    return 0;
+}
+
+int ecrnx_rx_reord_msdu_insert(struct reord_cntrl *reord_cntrl, struct reord_msdu_info *pmsdu)
+{
+    struct list_head *preord_list = &reord_cntrl->reord_list;
+    struct list_head *phead, *plist;
+    struct reord_msdu_info *nextmsdu;
+
+//first time:not any prframe in preord_list, so phead = phead->next
+    phead = preord_list;
+    plist = phead->next;
+
+    while(phead != plist) {
+        nextmsdu = list_entry(plist, struct reord_msdu_info, reord_pending_list);
+        if (SN_LESS(nextmsdu->sn, pmsdu->sn)) {
+            plist = plist->next;
+        } else if (SN_EQUAL(nextmsdu->sn, pmsdu->sn)){
+            return -1;
+        } else {
+            break;
+        }
+    }
+
+    list_add_tail(&(pmsdu->reord_pending_list), plist);
+
+    return 0;
+}
+
+void ecrnx_rx_reord_check(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif, struct hw_rxhdr *hw_rxhdr, struct sk_buff *skb, struct rxu_stat_mm* orignal_rxu_state)
+{
+    int ret=0;
+    struct reord_msdu_info *pmsdu;
+    struct reord_cntrl *reord_cntrl;
+
+    if (ecrnx_vif == NULL || skb->len <= 14) {
+        dev_kfree_skb(skb);
+        return ;
+    }
+
+    pmsdu = ecrnx_rx_reord_msdu_alloc(&ecrnx_hw->rx_msdu_free_lock, &ecrnx_hw->rx_msdu_free_list);
+    if (!pmsdu) {
+        dev_err(ecrnx_hw->dev, "ecrnx_rx_reord_msdu_alloc fail\n");
+        dev_kfree_skb(skb);
+        return ;
+    }
+
+    INIT_LIST_HEAD(&pmsdu->reord_pending_list);
+    pmsdu->hw_rxhdr = hw_rxhdr;
+    pmsdu->sn = orignal_rxu_state->sn;
+    pmsdu->tid = orignal_rxu_state->tid;
+    pmsdu->is_ga = orignal_rxu_state->is_ga;
+    pmsdu->skb = skb;
+    pmsdu->need_pn_check = orignal_rxu_state->need_pn_check;
+
+    if (hw_rxhdr->flags_sta_idx != ECRNX_INVALID_STA) {
+        reord_cntrl = &ecrnx_hw->sta_table[hw_rxhdr->flags_sta_idx].reord_cntrl[orignal_rxu_state->tid];
+        pmsdu->preorder_ctrl = reord_cntrl;
+        if(reord_cntrl->valid) {
+            if (!reord_cntrl->active) {
+                reord_cntrl->active = true;
+                reord_cntrl->win_start = 0xffff;
+                //reord_cntrl->win_size = ECRNX_REORD_WINSIZE;
+                //reord_cntrl->ecrnx_hw = ecrnx_hw;
+                //reord_cntrl->ecrnx_vif = ecrnx_vif;
+            }
+        } else {
+            ECRNX_PRINT("reord_cntrl invalid sta:%d sn:%d start:%d \n", hw_rxhdr->flags_sta_idx , pmsdu->sn, reord_cntrl->win_start);
+            ecrnx_rx_reord_single_msdu(ecrnx_hw, ecrnx_vif, pmsdu);
+            return ;
+        }
+    } else {
+        ECRNX_PRINT("sta_idx invalid sta:%d sn:%d start:%d  \n", hw_rxhdr->flags_sta_idx , pmsdu->sn, reord_cntrl->win_start);
+        ecrnx_rx_reord_single_msdu(ecrnx_hw, ecrnx_vif, pmsdu);
+        return ;
+    }
+
+    spin_lock(&reord_cntrl->reord_list_lock);
+    if (ecrnx_rx_reord_sn_check(reord_cntrl, pmsdu->sn)) {
+        //printk("%s discard sn:%d s:%d \n", __func__, pmsdu->sn, reord_cntrl->win_start);
+        //ecrnx_rx_reord_single_msdu(ecrnx_hw, ecrnx_vif, pmsdu);
+        spin_unlock(&reord_cntrl->reord_list_lock);
+        goto discard;
+    }
+    //printk("start:%d %d\n",reord_cntrl->win_start, pmsdu->sn);
+    if (ecrnx_rx_reord_msdu_insert(reord_cntrl, pmsdu)) {
+        spin_unlock(&reord_cntrl->reord_list_lock);
+        goto discard;
+    }
+
+    ecrnx_rx_reord_timer_update(ecrnx_hw, reord_cntrl, false);
+
+    ecrnx_rx_reord_list_flush(ecrnx_hw, ecrnx_vif, reord_cntrl);
+
+    spin_unlock(&reord_cntrl->reord_list_lock);
+
+    return ;
+
+discard:
+    if (pmsdu->skb) {
+        dev_kfree_skb(pmsdu->skb);
+        pmsdu->skb = NULL;
+    }
+
+    ecrnx_rx_reord_msdu_free(&ecrnx_hw->rx_msdu_free_lock, &ecrnx_hw->rx_msdu_free_list, &pmsdu->rx_msdu_list);
+}
+#endif
+
+
+u8 ecrnx_rx_agg_data_ind(struct ecrnx_hw *ecrnx_hw, u16_l status, struct sk_buff* skb, int msdu_offset)
+{
+    struct hw_rxhdr *hw_rxhdr = NULL;
+    struct ecrnx_vif *ecrnx_vif  = NULL;
+    hw_rxhdr = (struct hw_rxhdr*)skb->data;
+
+#if 0
+       ECRNX_DBG("[eswin_agg] hw_vect_len: %d , rxu_stat_mm: %d \n", sizeof(struct hw_rxhdr), sizeof(struct rxu_stat_mm));
+    ECRNX_DBG("[eswin_agg] rcv_frm_len: %d \n", hw_rxhdr->hwvect.len);
+    ECRNX_DBG("%s, parttern:0x%2x, status:%d !!", __func__, hw_rxhdr->pattern, status);
+#endif
+    /* Check the pattern */
+    if (hw_rxhdr->pattern != ecrnx_rxbuff_pattern) {
+        dev_err(ecrnx_hw->dev, "RX Buff Pattern not correct, pattern (%x), status %d\n", hw_rxhdr->pattern, status);
+            print_hex_dump(KERN_DEBUG, DBG_PREFIX_PAT, DUMP_PREFIX_NONE, 16, 1, hw_rxhdr, skb->len, false);
+        BUG();
+    }
+
+    /* Check if we need to delete the buffer */
+    if (status & RX_STAT_DELETE) {
+        dev_kfree_skb(skb);
+        goto end;
+    }
+    /* Check if we need to forward the buffer */
+    else if (status & RX_STAT_FORWARD) {
+
+        /* Remove the SK buffer from the rxbuf_elems table. It will also
+           unmap the buffer and then sync the buffer for the cpu */
+        //ecrnx_ipc_rxbuf_elem_pull(ecrnx_hw, skb);
+
+        hw_rxhdr = (struct hw_rxhdr *)skb->data;
+        ecrnx_rx_vector_convert(ecrnx_hw->machw_type,
+                               &hw_rxhdr->hwvect.rx_vect1,
+                               &hw_rxhdr->hwvect.rx_vect2);
+
+        //skb_reserve(skb, msdu_offset);
+        //ECRNX_DBG("[eswin_agg]before pull skb len: %d \n", skb->len);
+        skb_pull(skb, msdu_offset);
+        //ECRNX_DBG("[eswin_agg]after pull skb len: %d \n", skb->len);
+
+        if (hw_rxhdr->flags_is_80211_mpdu) {
+                       //ECRNX_DBG("[eswin_agg]recv mgmt\n");
+            ecrnx_rx_mgmt_any(ecrnx_hw, skb, hw_rxhdr);
+        } else {
+            ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, hw_rxhdr->flags_vif_idx);
+            //ECRNX_DBG("[eswin_agg] flags vif idx1:%d, vif: 0x%x \n", hw_rxhdr->flags_vif_idx, ecrnx_vif);
+            if (!ecrnx_vif) {
+                dev_err(ecrnx_hw->dev, "Frame received but no active vif (%d)",
+                        hw_rxhdr->flags_vif_idx);
+                dev_kfree_skb(skb);
+                               ECRNX_ERR("[agg] check_alloc, %d !!", __LINE__);
+                goto exit_no_free;
+            }
+
+            if (hw_rxhdr->flags_sta_idx != ECRNX_INVALID_STA) {
+                struct ecrnx_sta *sta;
+
+                sta = &ecrnx_hw->sta_table[hw_rxhdr->flags_sta_idx];
+                ecrnx_rx_statistic(ecrnx_hw, hw_rxhdr, sta);
+                //ECRNX_DBG("[eswin_agg] sta idx:%d, vif idx: %d \n", sta->vlan_idx, ecrnx_vif->vif_index);
+                if (sta->vlan_idx != ecrnx_vif->vif_index) {
+                    ecrnx_vif = ecrnx_hw->vif_table[sta->vlan_idx];
+                    if (!ecrnx_vif) {
+                        dev_kfree_skb(skb);
+                                               ECRNX_ERR("[agg] check_alloc, %d !!", __LINE__);
+                        goto exit_no_free;
+                    }
+                }
+
+                if (hw_rxhdr->flags_is_4addr && !ecrnx_vif->use_4addr) {
+                    cfg80211_rx_unexpected_4addr_frame(ecrnx_vif->ndev,
+                                                       sta->mac_addr, GFP_ATOMIC);
+                }
+            }
+
+            skb->priority = 256 + hw_rxhdr->flags_user_prio;
+            if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr)){
+                dev_kfree_skb(skb);
+                dev_err(ecrnx_hw->dev, "ecrnx_rx_data_skb_sdio error \n");
+            }
+        }
+    }
+
+exit_no_free:
+    /* Check if we need to allocate a new buffer */
+    //dev_err(ecrnx_hw->dev, "Failed to alloc new RX buf\n");
+
+end:
+    /* Reset and repush descriptor to FW */
+    //sdio_rx_buf_repush(&orignal_rxu_state, &orignal_rx_hd, skb);
+
+    return 0;
+}
+
+static int ecrnx_set_station_info(struct ecrnx_vif *vif, const u8 *mac, struct rx_vector_1 *rx_vect1)
+{
+    struct ecrnx_sta *sta = NULL;
+    static u32 rx_pkts = 0x1ff;
+    static u32 tx_pkts = 0x2ff;
+    static u64 rx_bytes = 0x3ff;
+    static u64 tx_bytes = 0x4ff;
+
+    if (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_MONITOR)
+        return -EINVAL;
+    else if ((ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_STATION) ||
+             (ECRNX_VIF_TYPE(vif) == NL80211_IFTYPE_P2P_CLIENT)) {
+        if (vif->sta.ap && ether_addr_equal(vif->sta.ap->mac_addr, mac))
+            sta = vif->sta.ap;
+    }
+    else
+    {
+        struct ecrnx_sta *sta_iter;
+        list_for_each_entry(sta_iter, &vif->ap.sta_list, list) {
+            if (sta_iter->valid && ether_addr_equal(sta_iter->mac_addr, mac)) {
+                sta = sta_iter;
+                break;
+            }
+        }
+    }
+
+    if (sta)
+    {
+        struct ecrnx_sta_stats *stats = &sta->stats;
+
+        stats->rx_bytes = rx_bytes++;
+        stats->tx_bytes = tx_bytes++;
+        stats->rx_pkts = rx_pkts++;
+        stats->tx_pkts = tx_pkts++;
+        memcpy(&stats->last_rx.rx_vect1, rx_vect1, sizeof(struct rx_vector_1));
+    }
+
+    return 0;
+}
+
+u8 ecrnx_rxdataind(void *pthis, void *hostid)
+{
+    struct ecrnx_hw *ecrnx_hw = pthis;
+    struct hw_rxhdr *hw_rxhdr = NULL;
+    struct ecrnx_vif *ecrnx_vif  = NULL;
+    int msdu_offset = sizeof(struct hw_rxhdr);
+    u16_l status;
+
+    struct rxu_stat_mm* orignal_rxu_state = NULL;
+    struct sk_buff *skb = NULL, *frg_ctrl_skb = NULL;
+
+    if(!pthis || !hostid){
+        ECRNX_ERR("ecrnx_rxdataind error! \n");
+        return 0;
+    }
+
+    spin_lock_bh(&ecrnx_hw->rx_lock);
+    ecrnx_hw->data_rx++;
+    skb = (struct sk_buff *)hostid;
+    orignal_rxu_state = (struct rxu_stat_mm*)skb->data;
+    skb_pull(skb, sizeof(struct rxu_stat_mm));
+
+#ifdef CONFIG_ECRNX_ESWIN
+    if (orignal_rxu_state->real_offset)
+    {
+        msdu_offset += orignal_rxu_state->real_offset;
+        //ECRNX_DBG("orignal_rxu_state->real_offset = %hu, msdu_offset:%d \n", orignal_rxu_state->real_offset, msdu_offset);
+    }
+
+    if(orignal_rxu_state->fragment_flag && ((orignal_rxu_state->status & RX_STAT_FORWARD) || (orignal_rxu_state->status & RX_STAT_DELETE))) { //defrag frame
+        struct defrag_elem *defrag_elem_rx = NULL, *next = NULL;
+
+        if(list_empty(&ecrnx_hw->defrag_rx_list)){
+            ECRNX_ERR("error defrag_rx_list is epmty!!! \n");
+            dev_kfree_skb(skb);
+            goto end;
+        }
+
+        ECRNX_DBG("[eswin_agg]%s enter, tid:0x%x, fragment_flag:0x%llx, status:0x%x \n", __func__, orignal_rxu_state->tid, orignal_rxu_state->fragment_flag, orignal_rxu_state->status);
+        if(orignal_rxu_state->status & RX_STAT_DELETE){
+            /*delete all the same tid frame*/
+            list_for_each_entry_safe(defrag_elem_rx, next, &ecrnx_hw->defrag_rx_list, head) {
+                ECRNX_DBG("[eswin_agg]delete: sn1:%d, sn2:%d, tid1:%d, skb:0x%08x \n",defrag_elem_rx->sn, orignal_rxu_state->sn, defrag_elem_rx->tid, defrag_elem_rx->skb);
+                if((defrag_elem_rx->tid == orignal_rxu_state->tid) && (defrag_elem_rx->sn == orignal_rxu_state->sn)){ ///==sn
+                    dev_kfree_skb(defrag_elem_rx->skb);
+                    list_del(&defrag_elem_rx->head);
+                    kfree(defrag_elem_rx);
+                }
+            }
+            dev_kfree_skb(skb);
+            goto end;
+        }
+        else if(orignal_rxu_state->status & RX_STAT_FORWARD){
+            struct defrag_elem *defrag_elem_rx = NULL, *next = NULL;
+            u32_l recv_len = 0, offset_len = 0, hw_rxhdr_len = sizeof(*hw_rxhdr);
+            unsigned char* skb_ptr = NULL;
+            int fg_first = 1;
+            int fg_total_len = 0;
+
+            /*caculate the defrag frames total lens*/
+            list_for_each_entry_safe(defrag_elem_rx, next, &ecrnx_hw->defrag_rx_list, head) {
+                    if((defrag_elem_rx->tid == orignal_rxu_state->tid) && (defrag_elem_rx->sn == orignal_rxu_state->sn)){
+                        recv_len += defrag_elem_rx->skb->len;
+                    }
+                }
+
+            frg_ctrl_skb = skb;
+            /*alloc a new skb, and put the same tid frame to the new skb*/
+            skb = dev_alloc_skb(recv_len);
+            
+            list_for_each_entry_safe(defrag_elem_rx, next, &ecrnx_hw->defrag_rx_list, head) {
+                ECRNX_DBG("[eswin_agg]forward: sn1:%d, sn2:%d, tid1:%d, skb:0x%p \n",defrag_elem_rx->sn, orignal_rxu_state->sn, defrag_elem_rx->tid, defrag_elem_rx->skb);
+                if((defrag_elem_rx->tid == orignal_rxu_state->tid) && (defrag_elem_rx->sn == orignal_rxu_state->sn)){
+                    if (fg_first) {
+                                           offset_len = 0;
+                    } else {
+                        /*first skb should include the hw_rxhdr, other's not need*/
+                        offset_len = hw_rxhdr_len;
+                        offset_len += sizeof(struct ethhdr);
+                    }
+                    offset_len += defrag_elem_rx->real_offset;
+                    skb_ptr = skb_put(skb, defrag_elem_rx->skb->len - offset_len);
+                    if (fg_first) {
+                        memcpy(skb_ptr, defrag_elem_rx->skb->data, hw_rxhdr_len);
+                        memcpy(skb_ptr + hw_rxhdr_len, defrag_elem_rx->skb->data + hw_rxhdr_len + offset_len, defrag_elem_rx->skb->len - hw_rxhdr_len - offset_len);
+                        fg_total_len = defrag_elem_rx->skb->len - hw_rxhdr_len - offset_len;
+                        hw_rxhdr = (struct hw_rxhdr*)skb_ptr;
+                    } else {
+                        memcpy(skb_ptr, defrag_elem_rx->skb->data + offset_len, defrag_elem_rx->skb->len - offset_len);
+                        fg_total_len += defrag_elem_rx->skb->len - offset_len;
+                    }
+                    dev_kfree_skb(defrag_elem_rx->skb);
+                    list_del(&defrag_elem_rx->head);
+                    kfree(defrag_elem_rx);
+                    fg_first = 0;
+                }
+            }
+
+            if (!fg_first) {
+                hw_rxhdr->hwvect.len = fg_total_len;//update len
+                msdu_offset = hw_rxhdr_len;
+                //printk("[llm] sn %d: total %d, skb %x (%d-%x-%x)\n", orignal_rxu_state->sn, fg_total_len, skb, skb->len, skb->data, skb->tail);
+            }
+        }
+    }
+#endif
+
+    hw_rxhdr = (struct hw_rxhdr*)skb->data;
+
+#if 0
+       ECRNX_DBG(" hw_vect_len: %d , rxu_stat_mm: %d \n", sizeof(struct hw_rxhdr), sizeof(struct rxu_stat_mm));
+    ECRNX_DBG(" rcv_frm_len: %d \n", hw_rxhdr->hwvect.len);
+#endif
+
+    status = orignal_rxu_state->status;
+    //ECRNX_DBG("%s, parttern:0x%2x, status:%d !!", __func__, hw_rxhdr->pattern, status);
+    /* Check the pattern */
+    if (hw_rxhdr->pattern != ecrnx_rxbuff_pattern) {
+        dev_err(ecrnx_hw->dev, "RX Buff Pattern not correct, pattern (%x), status %d\n", hw_rxhdr->pattern, status);
+        ECRNX_ERR("RX Buff Pattern not correct, pattern (%x), status %d, skb (%p), skb_len %d\n", hw_rxhdr->pattern, status, skb->data, skb->len);
+        print_hex_dump(KERN_DEBUG, DBG_PREFIX_PAT, DUMP_PREFIX_NONE, 16, 1, hw_rxhdr, skb->len, false);
+        BUG();
+    }
+
+    /* Check if we need to delete the buffer */
+    if (status & RX_STAT_DELETE) {
+        dev_kfree_skb(skb);
+        goto end;
+    }
+
+    /* Check if we need to forward the buffer coming from a monitor interface */
+    if (status & RX_STAT_MONITOR) {
+        struct sk_buff *skb_monitor;
+        struct hw_rxhdr hw_rxhdr_copy;
+        u8 rtap_len;
+        u16 frm_len;
+
+        //Check if monitor interface exists and is open
+        ECRNX_DBG("monitor_vif: %d \n", ecrnx_hw->monitor_vif);
+        ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, ecrnx_hw->monitor_vif);
+        if (!ecrnx_vif) {
+            dev_err(ecrnx_hw->dev, "Received monitor frame but there is no monitor interface open\n");
+            dev_kfree_skb(skb);
+            goto check_len_update;
+        }
+
+        ecrnx_rx_vector_convert(ecrnx_hw->machw_type,
+                               &hw_rxhdr->hwvect.rx_vect1,
+                               &hw_rxhdr->hwvect.rx_vect2);
+        rtap_len = ecrnx_rx_rtap_hdrlen(&hw_rxhdr->hwvect.rx_vect1, false);
+
+        // Move skb->data pointer to MAC Header or Ethernet header
+        skb->data += msdu_offset;
+
+        //Save frame length
+        frm_len = le32_to_cpu(hw_rxhdr->hwvect.len) - msdu_offset;
+
+        // Reserve space for frame
+        skb->len = frm_len;
+
+        if (status == RX_STAT_MONITOR) {
+            //Check if there is enough space to add the radiotap header
+            if (skb_headroom(skb) > rtap_len) {
+
+                skb_monitor = skb;
+
+                //Duplicate the HW Rx Header to override with the radiotap header
+                memcpy(&hw_rxhdr_copy, hw_rxhdr, sizeof(hw_rxhdr_copy));
+
+            } else {
+                //Duplicate the skb and extend the headroom
+                skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+
+                //Reset original skb->data pointer
+                skb->data = (void*) hw_rxhdr;
+            }
+        }
+        else
+        {
+#ifdef CONFIG_ECRNX_MON_DATA
+            // Check if MSDU
+            if (!hw_rxhdr->flags_is_80211_mpdu) {
+                // MSDU
+                //Extract MAC header
+                u16 machdr_len = hw_rxhdr->mac_hdr_backup.buf_len;
+                u8* machdr_ptr = hw_rxhdr->mac_hdr_backup.buffer;
+        
+                //Pull Ethernet header from skb
+                skb_pull(skb, sizeof(struct ethhdr));
+        
+                // Copy skb and extend for adding the radiotap header and the MAC header
+                skb_monitor = skb_copy_expand(skb,
+                                              rtap_len + machdr_len,
+                                              0, GFP_ATOMIC);
+        
+                //Reserve space for the MAC Header
+                skb_push(skb_monitor, machdr_len);
+        
+                //Copy MAC Header
+                memcpy(skb_monitor->data, machdr_ptr, machdr_len);
+        
+                //Update frame length
+                frm_len += machdr_len - sizeof(struct ethhdr);
+            } else {
+                // MPDU
+                skb_monitor = skb_copy_expand(skb, rtap_len, 0, GFP_ATOMIC);
+            }
+        
+            //Reset original skb->data pointer
+            skb->data = (void*) hw_rxhdr;
+#else
+            //Reset original skb->data pointer
+            skb->data = (void*) hw_rxhdr;
+        
+            wiphy_err(ecrnx_hw->wiphy, "RX status %d is invalid when MON_DATA is disabled\n", status);
+            dev_kfree_skb(skb);
+            goto check_len_update;
+#endif
+        }
+
+        skb_reset_tail_pointer(skb);
+        skb->len = 0;
+        skb_reset_tail_pointer(skb_monitor);
+        skb_monitor->len = 0;
+
+        skb_put(skb_monitor, frm_len);
+        if (ecrnx_rx_monitor(ecrnx_hw, ecrnx_vif, skb_monitor, hw_rxhdr, rtap_len)){
+                dev_kfree_skb(skb);
+                dev_err(ecrnx_hw->dev, "skb monitor handle error \n");
+            }
+
+        if (status == RX_STAT_MONITOR) {
+            status |= RX_STAT_ALLOC;
+            if (skb_monitor != skb) {
+                dev_kfree_skb(skb);
+                dev_err(ecrnx_hw->dev, "skb  handle status error \n");
+            }
+        }
+    }
+
+check_len_update:
+    /* Check if we need to update the length */
+    if (status & RX_STAT_LEN_UPDATE) {
+
+        hw_rxhdr = (struct hw_rxhdr *)skb->data;
+
+        hw_rxhdr->hwvect.len = orignal_rxu_state->frame_len - msdu_offset;
+
+        if (status & RX_STAT_ETH_LEN_UPDATE) {
+            /* Update Length Field inside the Ethernet Header */
+            struct ethhdr *hdr = (struct ethhdr *)((u8 *)hw_rxhdr + msdu_offset);
+
+            hdr->h_proto = htons(orignal_rxu_state->frame_len - sizeof(struct ethhdr));
+        }
+        dev_kfree_skb(skb);
+        goto end;
+    }
+
+    /* Check if it must be discarded after informing upper layer */
+    if (status & RX_STAT_SPURIOUS) {
+        struct ieee80211_hdr *hdr;
+
+        /* Read mac header to obtain Transmitter Address */
+        //ecrnx_ipc_rxbuf_elem_sync(ecrnx_hw, skb, msdu_offset + sizeof(*hdr));
+
+        hw_rxhdr = (struct hw_rxhdr *)skb->data;
+        hdr = (struct ieee80211_hdr *)(skb->data + msdu_offset);
+        ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, hw_rxhdr->flags_vif_idx);
+        ECRNX_DBG(" flags vif idx:%d, vif: 0x%x \n", hw_rxhdr->flags_vif_idx, ecrnx_vif);
+        if (ecrnx_vif) {
+            cfg80211_rx_spurious_frame(ecrnx_vif->ndev, hdr->addr2, GFP_ATOMIC);
+        }else{
+            dev_kfree_skb(skb);
+        }
+
+        goto end;
+    }
+
+    /* Check if we need to forward the buffer */
+    if (status & RX_STAT_FORWARD) {
+
+        /* Remove the SK buffer from the rxbuf_elems table. It will also
+           unmap the buffer and then sync the buffer for the cpu */
+        //ecrnx_ipc_rxbuf_elem_pull(ecrnx_hw, skb);
+
+        hw_rxhdr = (struct hw_rxhdr *)skb->data;
+        ecrnx_rx_vector_convert(ecrnx_hw->machw_type,
+                               &hw_rxhdr->hwvect.rx_vect1,
+                               &hw_rxhdr->hwvect.rx_vect2);
+
+        //skb_reserve(skb, msdu_offset);
+        //ECRNX_DBG("before pull skb len: %d, msdu_offset:%d \n", skb->len, msdu_offset);
+        skb_pull(skb, msdu_offset);
+        //ECRNX_DBG("after pull skb len: %d \n", skb->len);
+
+        if (hw_rxhdr->flags_is_80211_mpdu) {
+                       //ECRNX_DBG("recv mgmt\n");
+            ecrnx_rx_mgmt_any(ecrnx_hw, skb, hw_rxhdr);
+        } else {
+            ecrnx_vif = ecrnx_rx_get_vif(ecrnx_hw, hw_rxhdr->flags_vif_idx);
+            //ECRNX_DBG(" flags vif idx1:%d, vif: 0x%x \n", hw_rxhdr->flags_vif_idx, ecrnx_vif);
+            if (!ecrnx_vif) {
+                dev_err(ecrnx_hw->dev, "Frame received but no active vif (%d)",
+                        hw_rxhdr->flags_vif_idx);
+                dev_kfree_skb(skb);
+                               ECRNX_ERR(" check_alloc, %d !!", __LINE__);
+                goto end;
+            }
+
+            if (hw_rxhdr->flags_sta_idx != ECRNX_INVALID_STA) {
+                struct ecrnx_sta *sta;
+
+                sta = &ecrnx_hw->sta_table[hw_rxhdr->flags_sta_idx];
+                ecrnx_rx_statistic(ecrnx_hw, hw_rxhdr, sta);
+                //ECRNX_DBG(" sta idx:%d, vif idx: %d \n", sta->vlan_idx, ecrnx_vif->vif_index);
+                if (sta->vlan_idx != ecrnx_vif->vif_index) {
+                    ecrnx_vif = ecrnx_hw->vif_table[sta->vlan_idx];
+                    if (!ecrnx_vif) {
+                        dev_kfree_skb(skb);
+                                               ECRNX_ERR(" check_alloc, %d !!", __LINE__);
+                        goto end;
+                    }
+                }
+
+                ecrnx_set_station_info(ecrnx_vif, (const u8*)sta->mac_addr, &hw_rxhdr->hwvect.rx_vect1);
+                if (hw_rxhdr->flags_is_4addr && !ecrnx_vif->use_4addr) {
+                    cfg80211_rx_unexpected_4addr_frame(ecrnx_vif->ndev,
+                                                       sta->mac_addr, GFP_ATOMIC);
+                }
+            }
+
+            skb->priority = 256 + hw_rxhdr->flags_user_prio;
+#ifdef CONFIG_ESWIN_RX_REORDER
+                //printk("sn:%d %d %d %d l:%d\n",orignal_rxu_state->is_qos, orignal_rxu_state->need_reord, orignal_rxu_state->need_pn_check,orignal_rxu_state->sn, skb->len -8);
+                if(orignal_rxu_state->is_qos && orignal_rxu_state->need_reord) {
+                    ecrnx_rx_reord_check(ecrnx_hw, ecrnx_vif, hw_rxhdr, skb, orignal_rxu_state);
+                }else if(orignal_rxu_state->is_qos  && !orignal_rxu_state->need_reord) {
+                    ecrnx_rx_reord_tid_flush(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr->flags_sta_idx,orignal_rxu_state->tid);
+                    if (orignal_rxu_state->need_pn_check)
+                    {
+                        if (!ecrnx_rx_data_pn_check(ecrnx_hw, skb, hw_rxhdr->flags_vif_idx, hw_rxhdr->flags_sta_idx, orignal_rxu_state->tid, orignal_rxu_state->is_ga))
+                        {
+                            ECRNX_DBG("rx_data_check_pn error\n");
+                            dev_kfree_skb(skb);
+                            goto end;
+                        }
+                        skb_pull(skb, 8);
+                    }
+                    if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr))
+                        dev_kfree_skb(skb);
+                }else {
+                    if (orignal_rxu_state->need_pn_check)
+                    {
+                        if (!ecrnx_rx_data_pn_check(ecrnx_hw, skb, hw_rxhdr->flags_vif_idx, hw_rxhdr->flags_sta_idx, orignal_rxu_state->tid, orignal_rxu_state->is_ga))
+                        {
+                            ECRNX_DBG("rx_data_check_pn error\n");
+                            dev_kfree_skb(skb);
+                            goto end;
+                        }
+                        skb_pull(skb, 8);
+                    }
+                    if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr))
+                        dev_kfree_skb(skb);
+                }
+#else
+            if (!ecrnx_rx_data_skb(ecrnx_hw, ecrnx_vif, skb, hw_rxhdr)){
+                dev_kfree_skb(skb);
+                dev_err(ecrnx_hw->dev, "ecrnx_rx_data_skb_sdio error \n");
+            }
+#endif
+        }
+        goto end;
+    }
+
+    else if (status & RX_STAT_ALLOC) {
+        /*agg frame*/
+            if (hw_rxhdr->flags_sta_idx != ECRNX_INVALID_STA) {
+                if (orignal_rxu_state->need_pn_check)
+                {
+                    if (!ecrnx_rx_data_pn_check(ecrnx_hw, skb, hw_rxhdr->flags_vif_idx, hw_rxhdr->flags_sta_idx, orignal_rxu_state->tid, orignal_rxu_state->is_ga))
+                    {
+                        dev_err(ecrnx_hw->dev, "rx_data_check_pn error \n");
+                        dev_kfree_skb(skb);
+                        goto end;
+                    }
+                    skb_pull(skb, 8);
+                }
+            }
+            
+           if(orignal_rxu_state->fragment_flag){
+            struct defrag_elem* defrag_elem_rx = (struct defrag_elem*)kzalloc(sizeof(struct defrag_elem), GFP_ATOMIC);
+
+            if(defrag_elem_rx){
+                defrag_elem_rx->tid = orignal_rxu_state->tid;
+                defrag_elem_rx->sn = orignal_rxu_state->sn;
+                defrag_elem_rx->real_offset = orignal_rxu_state->real_offset;
+                defrag_elem_rx->skb = skb;
+                ECRNX_DBG("ecrnx_rxdataind:insert_skb:0x%08x, sn:%d, tid:%d, sn:%d \n", skb, orignal_rxu_state->tid, orignal_rxu_state->sn);
+                list_add_tail(&defrag_elem_rx->head, &ecrnx_hw->defrag_rx_list);
+            }else{
+                ECRNX_ERR("no buffer !!!! \n");
+            }
+        }
+        //ECRNX_DBG("status set alloc\n");
+    }
+
+end:
+    if(frg_ctrl_skb){
+        dev_kfree_skb(frg_ctrl_skb);
+    }
+    spin_unlock_bh(&ecrnx_hw->rx_lock);
+    return 0;
+}
+#endif
+
+#if 0
+static void sdio_rx_debug(struct sk_buff *skb)
+{
+    int i;
+  
+    if(!skb || !skb->len){
+        ECRNX_DBG("skb error \n");
+    }
+
+    ECRNX_DBG("%s, len: 0x%x \n", __func__, skb->len);
+    for(i = 0; i< skb->len; i++){
+        printk("0x%02x ", skb->data[i]);
+               if (i && (i % 16) == 0) {
+                       printk("\n");
+               }
+    }
+}
+#endif
+
+int ecrnx_data_cfm_callback(void *priv, void *host_id)
+{
+    struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw*)priv;
+    struct ipc_host_env_tag *env;
+    env = ecrnx_hw->ipc_env;
+
+    ecrnx_hw->data_tx_done++;
+    if(!env || !env->pthis || host_id == 0)
+    {
+        ECRNX_ERR("ecrnx_data_cfm_callback input param error!! \n!");
+        return -1;
+    }
+    spin_lock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+    env->cb.handle_data_cfm(env->pthis, host_id);
+    spin_unlock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+    return 0;
+}
+
+int ecrnx_msg_cfm_callback(void *priv, void *host_id)
+{
+    struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw*)priv;
+    struct ipc_host_env_tag *env;
+    env = ecrnx_hw->ipc_env;
+
+    if(!env || !env->pthis || host_id == 0)
+    {
+        ECRNX_ERR("ecrnx_msg_cfm_callback input param error!! \n!");
+        return -1;
+    }
+
+    env->msga2e_hostid = NULL;
+    ecrnx_hw->cmd_mgr.llind(&ecrnx_hw->cmd_mgr, (struct ecrnx_cmd *)host_id);
+    return 0;
+}
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+int ecrnx_rx_callback(void *priv, struct sk_buff *skb)
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+extern void usb_skb_debug(struct sk_buff *skb);
+int ecrnx_rx_callback(void *priv, struct sk_buff *skb, uint8_t endpoint)
+#endif
+{
+    //struct sk_buff *ret_skb = NULL;
+    struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw*)priv;
+    uint32_t frm_type = 0;
+    //ECRNX_DBG("%s enter, skb_len: %d, skb: 0x%x !!", __func__, skb->len, skb);
+
+       //print_hex_dump(KERN_DEBUG, "rx skb: ", DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+
+    if(!skb || !skb->data)
+    {
+        ECRNX_PRINT("sdio rx err error \n");
+    }
+
+     //usb_rx_debug(skb);
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+        frm_type = (skb->data[11] << 24) | (skb->data[10] << 16) | (skb->data[9] << 8) | skb->data[8];
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+               frm_type = (skb->data[3] << 24) | (skb->data[2] << 16) | (skb->data[1] << 8) | skb->data[0];
+#endif
+
+       //ECRNX_DBG(" frame_type: 0x%x, frame_len: %d, ecrnx_hw: 0x%x", frm_type, skb->len, ecrnx_hw);
+
+#if defined(CONFIG_ECRNX_ESWIN_SDIO)
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+       if (amt_mode == true) {
+               sdio_host_amt_rx_handler(frm_type, skb);
+       }
+       else
+#endif
+       sdio_host_rx_handler( frm_type, ecrnx_hw->ipc_env, skb);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+    if(MSG_TYPE_DATA == endpoint)
+    {
+        //msg is USB_FRM_TYPE_RXDESC
+        usb_host_rx_handler( USB_FRM_TYPE_RXDESC, ecrnx_hw->ipc_env, skb); //current just used a point
+    }
+    else if(MSG_TYPE_CTRL == endpoint)
+    {
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+               if (amt_mode == true) {
+                       usb_host_amt_rx_handler(frm_type, skb);
+               }
+               else
+#endif
+        usb_host_rx_handler( frm_type, ecrnx_hw->ipc_env, skb);
+    }
+    else
+    {
+        ECRNX_ERR("endopint error \n");
+    }
+#endif
+    //ECRNX_DBG("%s exit!!", __func__);
+    return 0;
+}
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_rx.h b/drivers/net/wireless/eswin/fullmac/ecrnx_rx.h
new file mode 100644 (file)
index 0000000..c84cd8b
--- /dev/null
@@ -0,0 +1,125 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_rx.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _ECRNX_RX_H_
+#define _ECRNX_RX_H_
+
+#include <linux/workqueue.h>
+#include "hal_desc.h"
+enum rx_status_bits
+{
+    /// The buffer can be forwarded to the networking stack
+    RX_STAT_FORWARD = 1 << 0,
+    /// A new buffer has to be allocated
+    RX_STAT_ALLOC = 1 << 1,
+    /// The buffer has to be deleted
+    RX_STAT_DELETE = 1 << 2,
+    /// The length of the buffer has to be updated
+    RX_STAT_LEN_UPDATE = 1 << 3,
+    /// The length in the Ethernet header has to be updated
+    RX_STAT_ETH_LEN_UPDATE = 1 << 4,
+    /// Simple copy
+    RX_STAT_COPY = 1 << 5,
+    /// Spurious frame (inform upper layer and discard)
+    RX_STAT_SPURIOUS = 1 << 6,
+    /// packet for monitor interface
+    RX_STAT_MONITOR = 1 << 7,
+};
+
+#if defined(CONFIG_ECRNX_ESWIN_USB)
+typedef enum
+{
+    MSG_TYPE_DATA = 1,
+    MSG_TYPE_CTRL
+}MSG_TYPE_E;
+#endif
+
+/*
+ * Decryption status subfields.
+ * {
+ */
+// @}
+
+#define _ASOCREQ_IE_OFFSET_     4   /* excluding wlan_hdr */
+#define _REASOCREQ_IE_OFFSET_   10
+#define STATION_INFO_ASSOC_REQ_IES 0
+#define WLAN_HDR_A3_LEN            24
+#define get_addr2_ptr(pbuf)    ((unsigned char *)((unsigned int)(pbuf) + 10))
+
+/* keep it same with the FW */
+#define RX_CNTRL_REORD_WIN_SIZE 42
+#ifdef CONFIG_ECRNX_MON_DATA
+#define RX_MACHDR_BACKUP_LEN    64
+/// MAC header backup descriptor
+struct mon_machdrdesc
+{
+    /// Length of the buffer
+    u32 buf_len;
+    /// Buffer containing mac header, LLC and SNAP
+    u8 buffer[RX_MACHDR_BACKUP_LEN];
+};
+#endif
+
+struct hw_rxhdr {
+    /** RX vector */
+    struct hw_vect hwvect;
+
+    /** PHY channel information */
+    struct phy_channel_info_desc phy_info;
+
+    /** RX flags */
+    u32    flags_is_amsdu     : 1;
+    u32    flags_is_80211_mpdu: 1;
+    u32    flags_is_4addr     : 1;
+    u32    flags_new_peer     : 1;
+    u32    flags_user_prio    : 3;
+    u32    flags_rsvd0        : 1;
+    u32    flags_vif_idx      : 8;    // 0xFF if invalid VIF index
+    u32    flags_sta_idx      : 8;    // 0xFF if invalid STA index
+    u32    flags_dst_idx      : 8;    // 0xFF if unknown destination STA
+#ifdef CONFIG_ECRNX_MON_DATA
+    /// MAC header backup descriptor (used only for MSDU when there is a monitor and a data interface)
+    struct mon_machdrdesc mac_hdr_backup;
+#endif
+    /** Pattern indicating if the buffer is available for the driver */
+    u32    pattern;
+};
+
+struct ecrnx_defer_rx {
+    struct sk_buff_head sk_list;
+    struct work_struct work;
+};
+
+/**
+ * struct ecrnx_defer_rx_cb - Control buffer for deferred buffers
+ *
+ * @vif: VIF that received the buffer
+ */
+struct ecrnx_defer_rx_cb {
+    struct ecrnx_vif *vif;
+};
+
+u8 ecrnx_unsup_rx_vec_ind(void *pthis, void *hostid);
+u8 ecrnx_rxdataind(void *pthis, void *hostid);
+void ecrnx_rx_deferred(struct work_struct *ws);
+void ecrnx_rx_defer_skb(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                       struct sk_buff *skb);
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+int ecrnx_rx_callback(void *priv, struct sk_buff *skb);
+#elif defined(CONFIG_ECRNX_ESWIN_USB)
+int ecrnx_rx_callback(void *priv, struct sk_buff *skb, uint8_t endpoint);
+#endif
+
+void ecrnx_rx_reord_deinit(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_rx_reord_init(struct ecrnx_hw *ecrnx_hw);
+void ecrnx_rx_reord_sta_init(struct ecrnx_hw* ecrnx_hw, struct ecrnx_vif *ecrnx_vif, u8 sta_idx);
+void ecrnx_rx_reord_sta_deinit(struct ecrnx_hw* ecrnx_hw, u8 sta_idx, bool is_del);
+
+
+#endif /* _ECRNX_RX_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_tdls.c b/drivers/net/wireless/eswin/fullmac/ecrnx_tdls.c
new file mode 100644 (file)
index 0000000..7fe1ff2
--- /dev/null
@@ -0,0 +1,788 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_tx.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+/**
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+
+#include "ecrnx_tdls.h"
+#include "ecrnx_compat.h"
+
+/**
+ * FUNCTION DEFINITIONS
+ ******************************************************************************
+ */
+
+static u16
+ecrnx_get_tdls_sta_capab(struct ecrnx_vif *ecrnx_vif, u16 status_code)
+{
+    u16 capab = 0;
+
+    /* The capability will be 0 when sending a failure code */
+    if (status_code != 0)
+        return capab;
+
+    if (ecrnx_vif->sta.ap->band != NL80211_BAND_2GHZ)
+        return capab;
+
+    capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+    capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+    return capab;
+}
+
+static int
+ecrnx_tdls_prepare_encap_data(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                                 const u8 *peer, u8 action_code, u8 dialog_token,
+                                 u16 status_code, struct sk_buff *skb)
+{
+    struct ieee80211_tdls_data *tf;
+    tf = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_data) - sizeof(tf->u));
+
+    // set eth header
+    memcpy(tf->da, peer, ETH_ALEN);
+    memcpy(tf->sa, ecrnx_hw->wiphy->perm_addr, ETH_ALEN);
+    tf->ether_type = cpu_to_be16(ETH_P_TDLS);
+
+    // set common TDLS info
+    tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
+    tf->category = WLAN_CATEGORY_TDLS;
+    tf->action_code = action_code;
+
+    // set action specific TDLS info
+    switch (action_code) {
+    case WLAN_TDLS_SETUP_REQUEST:
+        skb_put(skb, sizeof(tf->u.setup_req));
+        tf->u.setup_req.dialog_token = dialog_token;
+        tf->u.setup_req.capability =
+                cpu_to_le16(ecrnx_get_tdls_sta_capab(ecrnx_vif, status_code));
+        break;
+
+    case WLAN_TDLS_SETUP_RESPONSE:
+        skb_put(skb, sizeof(tf->u.setup_resp));
+        tf->u.setup_resp.status_code = cpu_to_le16(status_code);
+        tf->u.setup_resp.dialog_token = dialog_token;
+        tf->u.setup_resp.capability =
+                cpu_to_le16(ecrnx_get_tdls_sta_capab(ecrnx_vif, status_code));
+        break;
+
+    case WLAN_TDLS_SETUP_CONFIRM:
+        skb_put(skb, sizeof(tf->u.setup_cfm));
+        tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
+        tf->u.setup_cfm.dialog_token = dialog_token;
+        break;
+
+    case WLAN_TDLS_TEARDOWN:
+        skb_put(skb, sizeof(tf->u.teardown));
+        tf->u.teardown.reason_code = cpu_to_le16(status_code);
+        break;
+
+    case WLAN_TDLS_DISCOVERY_REQUEST:
+        skb_put(skb, sizeof(tf->u.discover_req));
+        tf->u.discover_req.dialog_token = dialog_token;
+        break;
+
+    default:
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int
+ecrnx_prep_tdls_direct(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                      const u8 *peer, u8 action_code, u8 dialog_token,
+                      u16 status_code, struct sk_buff *skb)
+{
+    struct ieee80211_mgmt *mgmt;
+
+    mgmt = (void *)skb_put(skb, 24);
+    memset(mgmt, 0, 24);
+    memcpy(mgmt->da, peer, ETH_ALEN);
+    memcpy(mgmt->sa, ecrnx_hw->wiphy->perm_addr, ETH_ALEN);
+    memcpy(mgmt->bssid, ecrnx_vif->sta.ap->mac_addr, ETH_ALEN);
+
+    mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                      IEEE80211_STYPE_ACTION);
+
+    switch (action_code) {
+    case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+        skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
+        mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
+        mgmt->u.action.u.tdls_discover_resp.action_code = WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
+        mgmt->u.action.u.tdls_discover_resp.dialog_token = dialog_token;
+        mgmt->u.action.u.tdls_discover_resp.capability =
+            cpu_to_le16(ecrnx_get_tdls_sta_capab(ecrnx_vif, status_code));
+        break;
+    default:
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int
+ecrnx_add_srates_ie(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+    u8 i, rates, *pos;
+    int rate;
+    struct ieee80211_supported_band *ecrnx_band_2GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+    rates = 8;
+
+    if (skb_tailroom(skb) < rates + 2)
+        return -ENOMEM;
+
+    pos = skb_put(skb, rates + 2);
+    *pos++ = WLAN_EID_SUPP_RATES;
+    *pos++ = rates;
+    for (i = 0; i < rates; i++) {
+        rate = ecrnx_band_2GHz->bitrates[i].bitrate;
+        rate = DIV_ROUND_UP(rate, 5);
+        *pos++ = (u8)rate;
+    }
+
+    return 0;
+}
+
+static int
+ecrnx_add_ext_srates_ie(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+    u8 i, exrates, *pos;
+    int rate;
+    struct ieee80211_supported_band *ecrnx_band_2GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+
+    exrates = ecrnx_band_2GHz->n_bitrates - 8;
+
+    if (skb_tailroom(skb) < exrates + 2)
+        return -ENOMEM;
+
+    pos = skb_put(skb, exrates + 2);
+    *pos++ = WLAN_EID_EXT_SUPP_RATES;
+    *pos++ = exrates;
+    for (i = 8; i < (8+exrates); i++) {
+        rate = ecrnx_band_2GHz->bitrates[i].bitrate;
+        rate = DIV_ROUND_UP(rate, 5);
+        *pos++ = (u8)rate;
+    }
+
+    return 0;
+}
+
+static void
+ecrnx_tdls_add_supp_channels(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+    /*
+     * Add possible channels for TDLS. These are channels that are allowed
+     * to be active.
+     */
+    u8 subband_cnt = 0;
+    u8 *pos_subband;
+    u8 *pos = skb_put(skb, 2);
+    struct ieee80211_supported_band *ecrnx_band_2GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+#ifdef CONFIG_ECRNX_5G
+    struct ieee80211_supported_band *ecrnx_band_5GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_5GHZ];
+#endif
+
+    *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
+
+    /*
+     * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as
+     * this doesn't happen in real world scenarios.
+     */
+
+    /* 2GHz, with 5MHz spacing */
+    pos_subband = skb_put(skb, 2);
+    if (ecrnx_band_2GHz->n_channels > 0)
+    {
+        *pos_subband++ = ieee80211_frequency_to_channel(ecrnx_band_2GHz->channels[0].center_freq);
+        *pos_subband++ = ecrnx_band_2GHz->n_channels;
+        subband_cnt++;
+    }
+
+#ifdef CONFIG_ECRNX_5G
+    /* 5GHz, with 20MHz spacing */
+    pos_subband = skb_put(skb, 2);
+    if (ecrnx_band_5GHz->n_channels > 0)
+    {
+        *pos_subband++ = ieee80211_frequency_to_channel(ecrnx_band_5GHz->channels[0].center_freq);
+        *pos_subband++ = ecrnx_band_5GHz->n_channels;
+        subband_cnt++;
+    }
+#endif
+
+    /* length */
+    *pos = 2 * subband_cnt;
+}
+
+static void
+ecrnx_tdls_add_ext_capab(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb)
+{
+    u8 *pos = (void *)skb_put(skb, 7);
+    bool chan_switch = ecrnx_hw->wiphy->features &
+               NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
+
+    *pos++ = WLAN_EID_EXT_CAPABILITY;
+    *pos++ = 5; /* len */
+    *pos++ = 0x0;
+    *pos++ = 0x0;
+    *pos++ = 0x0;
+    *pos++ = WLAN_EXT_CAPA4_TDLS_BUFFER_STA |
+             (chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0);
+    *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
+}
+
+static void
+ecrnx_add_wmm_info_ie(struct sk_buff *skb, u8 qosinfo)
+{
+    u8 *pos = (void *)skb_put(skb, 9);
+
+    *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+    *pos++ = 7; /* len */
+    *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+    *pos++ = 0x50;
+    *pos++ = 0xf2;
+    *pos++ = 2; /* WME */
+    *pos++ = 0; /* WME info */
+    *pos++ = 1; /* WME ver */
+    *pos++ = qosinfo; /* U-APSD no in use */
+}
+
+/* translate numbering in the WMM parameter IE to the mac80211 notation */
+static u8 ecrnx_ac_from_wmm(int ac)
+{
+       switch (ac) {
+       case 0:
+               return AC_BE;
+       case 1:
+               return AC_BK;
+       case 2:
+               return AC_VI;
+       case 3:
+               return AC_VO;
+    default:
+        WARN_ON_ONCE(1);
+       }
+       return -1;
+}
+
+static void
+ecrnx_add_wmm_param_ie(struct sk_buff *skb, u8 acm_bits, u32 *ac_params)
+{
+    struct ieee80211_wmm_param_ie *wmm;
+    int i, j;
+    u8 cw_min, cw_max;
+    bool acm;
+
+    wmm = (void *)skb_put(skb, sizeof(struct ieee80211_wmm_param_ie));
+    memset(wmm, 0, sizeof(*wmm));
+
+    wmm->element_id = WLAN_EID_VENDOR_SPECIFIC;
+    wmm->len = sizeof(*wmm) - 2;
+
+    wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */
+    wmm->oui[1] = 0x50;
+    wmm->oui[2] = 0xf2;
+    wmm->oui_type = 2; /* WME */
+    wmm->oui_subtype = 1; /* WME param */
+    wmm->version = 1; /* WME ver */
+    wmm->qos_info = 0; /* U-APSD not in use */
+
+    /*
+     * Use the EDCA parameters defined for the BSS, or default if the AP
+     * doesn't support it, as mandated by 802.11-2012 section 10.22.4
+     */
+    for (i = 0; i < AC_MAX; i++) {
+        j = ecrnx_ac_from_wmm(i);
+        cw_min = (ac_params[j] & 0xF0 ) >> 4;
+        cw_max = (ac_params[j] & 0xF00 ) >> 8;
+        acm = (acm_bits & (1 << j)) != 0;
+
+        wmm->ac[i].aci_aifsn = (i << 5) | (acm << 4) | (ac_params[j] & 0xF);
+        wmm->ac[i].cw = (cw_max << 4) | cw_min;
+        wmm->ac[i].txop_limit = (ac_params[j] & 0x0FFFF000 ) >> 12;
+    }
+}
+
+static void
+ecrnx_tdls_add_oper_classes(struct ecrnx_vif *ecrnx_vif, struct sk_buff *skb)
+{
+    u8 *pos;
+    u8 op_class;
+    struct cfg80211_chan_def chan_def;
+    struct ieee80211_channel chan;
+
+    chan.band = ecrnx_vif->sta.ap->band;
+    chan.center_freq = ecrnx_vif->sta.ap->center_freq;
+    chan_def.chan = &chan;
+    chan_def.width = ecrnx_vif->sta.ap->width;
+    chan_def.center_freq1 = ecrnx_vif->sta.ap->center_freq1;
+    chan_def.center_freq2 = ecrnx_vif->sta.ap->center_freq2;
+
+    if (!ieee80211_chandef_to_operating_class(&chan_def, &op_class))
+        return;
+
+    pos = skb_put(skb, 4);
+    *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES;
+    *pos++ = 2; /* len */
+
+    // current op class
+    *pos++ = op_class;
+    *pos++ = op_class; /* give current operating class as alternate too */
+
+    // need to add 5GHz classes?
+}
+
+static void
+ecrnx_ie_build_ht_cap(struct sk_buff *skb, struct ieee80211_sta_ht_cap *ht_cap,
+                  u16 cap)
+{
+    u8 *pos;
+    __le16 tmp;
+
+    pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
+    *pos++ = WLAN_EID_HT_CAPABILITY;
+    *pos++ = sizeof(struct ieee80211_ht_cap);
+    memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+
+    /* capability flags */
+    tmp = cpu_to_le16(cap);
+    memcpy(pos, &tmp, sizeof(u16));
+    pos += sizeof(u16);
+
+    /* AMPDU parameters */
+    *pos++ = ht_cap->ampdu_factor |
+         (ht_cap->ampdu_density <<
+            IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT);
+
+    /* MCS set */
+    memcpy(pos, &ht_cap->mcs, sizeof(ht_cap->mcs));
+    pos += sizeof(ht_cap->mcs);
+
+    /* extended capabilities */
+    pos += sizeof(__le16);
+
+    /* BF capabilities */
+    pos += sizeof(__le32);
+
+    /* antenna selection */
+    pos += sizeof(u8);
+}
+
+static void
+ecrnx_ie_build_vht_cap(struct sk_buff *skb, struct ieee80211_sta_vht_cap *vht_cap,
+                   u32 cap)
+{
+    u8 *pos;
+    __le32 tmp;
+
+    pos = skb_put(skb, 14);
+
+    *pos++ = WLAN_EID_VHT_CAPABILITY;
+    *pos++ = sizeof(struct ieee80211_vht_cap);
+    memset(pos, 0, sizeof(struct ieee80211_vht_cap));
+
+    /* capability flags */
+    tmp = cpu_to_le32(cap);
+    memcpy(pos, &tmp, sizeof(u32));
+    pos += sizeof(u32);
+
+    /* VHT MCS set */
+    memcpy(pos, &vht_cap->vht_mcs, sizeof(vht_cap->vht_mcs));
+    pos += sizeof(vht_cap->vht_mcs);
+}
+
+static void
+ecrnx_tdls_add_bss_coex_ie(struct sk_buff *skb)
+{
+    u8 *pos = (void *)skb_put(skb, 3);
+
+    *pos++ = WLAN_EID_BSS_COEX_2040;
+    *pos++ = 1; /* len */
+
+    *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
+}
+
+static void
+ecrnx_tdls_add_link_ie(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                       struct sk_buff *skb, const u8 *peer,
+                       bool initiator)
+{
+    struct ieee80211_tdls_lnkie *lnkid;
+    const u8 *init_addr, *rsp_addr;
+
+    if (initiator) {
+        init_addr = ecrnx_hw->wiphy->perm_addr;
+        rsp_addr = peer;
+    } else {
+        init_addr = peer;
+        rsp_addr = ecrnx_hw->wiphy->perm_addr;
+    }
+
+    lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
+
+    lnkid->ie_type = WLAN_EID_LINK_ID;
+    lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
+
+    memcpy(lnkid->bssid, ecrnx_vif->sta.ap->mac_addr, ETH_ALEN);
+    memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
+    memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
+}
+
+static void
+ecrnx_tdls_add_aid_ie(struct ecrnx_vif *ecrnx_vif, struct sk_buff *skb)
+{
+    u8 *pos = (void *)skb_put(skb, 4);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+    *pos++ = WLAN_EID_AID;
+#else
+    *pos++ = 197;
+#endif
+    *pos++ = 2; /* len */
+    *pos++ = ecrnx_vif->sta.ap->aid;
+}
+
+static u8 *
+ecrnx_ie_build_ht_oper(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                          u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+                          u16 prot_mode)
+{
+    struct ieee80211_ht_operation *ht_oper;
+    /* Build HT Information */
+    *pos++ = WLAN_EID_HT_OPERATION;
+    *pos++ = sizeof(struct ieee80211_ht_operation);
+    ht_oper = (struct ieee80211_ht_operation *)pos;
+    ht_oper->primary_chan = ieee80211_frequency_to_channel(
+                    ecrnx_vif->sta.ap->center_freq);
+    switch (ecrnx_vif->sta.ap->width) {
+    case NL80211_CHAN_WIDTH_160:
+    case NL80211_CHAN_WIDTH_80P80:
+    case NL80211_CHAN_WIDTH_80:
+    case NL80211_CHAN_WIDTH_40:
+        if (ecrnx_vif->sta.ap->center_freq1 > ecrnx_vif->sta.ap->center_freq)
+            ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+        else
+            ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+        break;
+    default:
+        ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
+        break;
+    }
+    if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
+        ecrnx_vif->sta.ap->width != NL80211_CHAN_WIDTH_20_NOHT &&
+        ecrnx_vif->sta.ap->width != NL80211_CHAN_WIDTH_20)
+        ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
+
+    ht_oper->operation_mode = cpu_to_le16(prot_mode);
+    ht_oper->stbc_param = 0x0000;
+
+    /* It seems that Basic MCS set and Supported MCS set
+       are identical for the first 10 bytes */
+    memset(&ht_oper->basic_set, 0, 16);
+    memcpy(&ht_oper->basic_set, &ht_cap->mcs, 10);
+
+    return pos + sizeof(struct ieee80211_ht_operation);
+}
+
+static u8 *
+ecrnx_ie_build_vht_oper(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                          u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
+                          u16 prot_mode)
+{
+    struct ieee80211_vht_operation *vht_oper;
+    /* Build HT Information */
+    *pos++ = WLAN_EID_VHT_OPERATION;
+    *pos++ = sizeof(struct ieee80211_vht_operation);
+    vht_oper = (struct ieee80211_vht_operation *)pos;
+
+    switch (ecrnx_vif->sta.ap->width) {
+    case NL80211_CHAN_WIDTH_80:
+        vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; // Channel Width
+        CCFS0(vht_oper) =
+                ieee80211_frequency_to_channel(ecrnx_vif->sta.ap->center_freq); // Channel Center Frequency Segment 0
+        CCFS1(vht_oper) = 0; // Channel Center Frequency Segment 1 (N.A.)
+        break;
+    case NL80211_CHAN_WIDTH_160:
+        vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; // Channel Width
+        CCFS0(vht_oper) =
+                ieee80211_frequency_to_channel(ecrnx_vif->sta.ap->center_freq); // Channel Center Frequency Segment 0
+        CCFS1(vht_oper) = 0; // Channel Center Frequency Segment 1 (N.A.)
+        break;
+    case NL80211_CHAN_WIDTH_80P80:
+        vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; // Channel Width
+        CCFS0(vht_oper) =
+                ieee80211_frequency_to_channel(ecrnx_vif->sta.ap->center_freq1); // Channel Center Frequency Segment 0
+        CCFS1(vht_oper) =
+                ieee80211_frequency_to_channel(ecrnx_vif->sta.ap->center_freq2); // Channel Center Frequency Segment 1
+        break;
+    default:
+        vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT;
+        CCFS0(vht_oper) = 0;
+        CCFS1(vht_oper) = 0;
+        break;
+    }
+
+    vht_oper->basic_mcs_set = cpu_to_le16(ecrnx_hw->mod_params->mcs_map);
+
+    return pos + sizeof(struct ieee80211_vht_operation);
+
+}
+
+static void
+ecrnx_tdls_add_setup_start_ies(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                              struct sk_buff *skb, const u8 *peer,
+                              u8 action_code, bool initiator,
+                              const u8 *extra_ies, size_t extra_ies_len)
+{
+    enum nl80211_band band = ecrnx_vif->sta.ap->band;
+    struct ieee80211_supported_band *sband;
+    struct ieee80211_sta_ht_cap ht_cap;
+    struct ieee80211_sta_vht_cap vht_cap;
+    size_t offset = 0, noffset;
+    u8 *pos;
+
+    rcu_read_lock();
+
+    ecrnx_add_srates_ie(ecrnx_hw, skb);
+    ecrnx_add_ext_srates_ie(ecrnx_hw, skb);
+    ecrnx_tdls_add_supp_channels(ecrnx_hw, skb);
+    ecrnx_tdls_add_ext_capab(ecrnx_hw, skb);
+
+    /* add the QoS element if we support it */
+    if (/*local->hw.queues >= IEEE80211_NUM_ACS &&*/
+        action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES)
+        ecrnx_add_wmm_info_ie(skb, 0); /* no U-APSD */
+
+    ecrnx_tdls_add_oper_classes(ecrnx_vif, skb);
+
+    /*
+     * with TDLS we can switch channels, and HT-caps are not necessarily
+     * the same on all bands. The specification limits the setup to a
+     * single HT-cap, so use the current band for now.
+     */
+    sband = ecrnx_hw->wiphy->bands[band];
+    memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+    if (((action_code == WLAN_TDLS_SETUP_REQUEST) ||
+         (action_code == WLAN_TDLS_SETUP_RESPONSE) ||
+         (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES)) &&
+         ht_cap.ht_supported /* (!sta || sta->sta.ht_cap.ht_supported)*/) {
+        ecrnx_ie_build_ht_cap(skb, &ht_cap, ht_cap.cap);
+    }
+
+    if (ht_cap.ht_supported &&
+        (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+        ecrnx_tdls_add_bss_coex_ie(skb);
+
+    ecrnx_tdls_add_link_ie(ecrnx_hw, ecrnx_vif, skb, peer, initiator);
+
+    memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+    if (vht_cap.vht_supported) {
+        ecrnx_tdls_add_aid_ie(ecrnx_vif, skb);
+        ecrnx_ie_build_vht_cap(skb, &vht_cap, vht_cap.cap);
+        // Operating mode Notification (optional)
+    }
+
+    /* add any remaining IEs */
+    if (extra_ies_len) {
+        noffset = extra_ies_len;
+        pos = skb_put(skb, noffset - offset);
+        memcpy(pos, extra_ies + offset, noffset - offset);
+    }
+
+    rcu_read_unlock();
+}
+
+
+static void
+ecrnx_tdls_add_setup_cfm_ies(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                              struct sk_buff *skb, const u8 *peer, bool initiator,
+                              const u8 *extra_ies, size_t extra_ies_len)
+{
+    struct ieee80211_supported_band *sband;
+    enum nl80211_band band = ecrnx_vif->sta.ap->band;
+    struct ieee80211_sta_ht_cap ht_cap;
+    struct ieee80211_sta_vht_cap vht_cap;
+
+    size_t offset = 0, noffset;
+    struct ecrnx_sta *sta, *ap_sta;
+    u8 *pos;
+
+    rcu_read_lock();
+
+    sta = ecrnx_get_sta(ecrnx_hw, peer);
+    ap_sta = ecrnx_vif->sta.ap;
+    if (WARN_ON_ONCE(!sta || !ap_sta)) {
+        rcu_read_unlock();
+        return;
+    }
+
+    /* add the QoS param IE if both the peer and we support it */
+    if (sta->qos)
+       ecrnx_add_wmm_param_ie(skb, ap_sta->acm, ap_sta->ac_param);
+
+    /* if HT support is only added in TDLS, we need an HT-operation IE */
+    sband = ecrnx_hw->wiphy->bands[band];
+    memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+    if (ht_cap.ht_supported && !ap_sta->ht && sta->ht) {
+        pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
+        /* send an empty HT operation IE */
+        ecrnx_ie_build_ht_oper(ecrnx_hw, ecrnx_vif, pos, &ht_cap, 0);
+    }
+
+    ecrnx_tdls_add_link_ie(ecrnx_hw, ecrnx_vif, skb, peer, initiator);
+
+    memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap));
+    if (vht_cap.vht_supported && !ap_sta->vht && sta->vht) {
+        pos = skb_put(skb, 2 + sizeof(struct ieee80211_vht_operation));
+        ecrnx_ie_build_vht_oper(ecrnx_hw, ecrnx_vif, pos, &ht_cap, 0);
+        // Operating mode Notification (optional)
+    }
+
+    /* add any remaining IEs */
+    if (extra_ies_len) {
+        noffset = extra_ies_len;
+        pos = skb_put(skb, noffset - offset);
+        memcpy(pos, extra_ies + offset, noffset - offset);
+    }
+
+    rcu_read_unlock();
+}
+
+static void
+ecrnx_tdls_add_ies(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                                   struct sk_buff *skb, const u8 *peer,
+                                   u8 action_code, u16 status_code,
+                                   bool initiator, const u8 *extra_ies,
+                                   size_t extra_ies_len, u8 oper_class,
+                                   struct cfg80211_chan_def *chandef)
+{
+    switch (action_code) {
+    case WLAN_TDLS_SETUP_REQUEST:
+    case WLAN_TDLS_SETUP_RESPONSE:
+    case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+        if (status_code == 0)
+            ecrnx_tdls_add_setup_start_ies(ecrnx_hw, ecrnx_vif, skb, peer, action_code,
+                                          initiator, extra_ies, extra_ies_len);
+        break;
+    case WLAN_TDLS_SETUP_CONFIRM:
+        if (status_code == 0)
+            ecrnx_tdls_add_setup_cfm_ies(ecrnx_hw, ecrnx_vif, skb, peer, initiator,
+                                        extra_ies, extra_ies_len);
+        break;
+
+    case WLAN_TDLS_TEARDOWN:
+    case WLAN_TDLS_DISCOVERY_REQUEST:
+        if (extra_ies_len)
+            memcpy(skb_put(skb, extra_ies_len), extra_ies,
+                   extra_ies_len);
+        if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
+            ecrnx_tdls_add_link_ie(ecrnx_hw, ecrnx_vif, skb, peer, initiator);
+        break;
+    }
+}
+
+int
+ecrnx_tdls_send_mgmt_packet_data(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                         const u8 *peer, u8 action_code, u8 dialog_token,
+                         u16 status_code, u32 peer_capability, bool initiator,
+                         const u8 *extra_ies, size_t extra_ies_len, u8 oper_class,
+                         struct cfg80211_chan_def *chandef)
+{
+    struct sk_buff *skb;
+    int ret = 0;
+    struct ieee80211_supported_band *ecrnx_band_2GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_2GHZ];
+#ifdef CONFIG_ECRNX_5G
+    struct ieee80211_supported_band *ecrnx_band_5GHz = ecrnx_hw->wiphy->bands[NL80211_BAND_5GHZ];
+#endif
+    skb = netdev_alloc_skb(ecrnx_vif->ndev,
+              sizeof(struct ieee80211_tdls_data) + // ethhdr + TDLS info
+              10 +  /* supported rates */
+              6 +  /* extended supported rates */
+            #ifdef CONFIG_ECRNX_5G
+              (2 + ecrnx_band_2GHz->n_channels + ecrnx_band_5GHz->n_channels) + /* supported channels */
+            #else
+                         (2 + ecrnx_band_2GHz->n_channels) +
+                       #endif
+              sizeof(struct ieee_types_extcap) +
+              sizeof(struct ieee80211_wmm_param_ie) +
+              4 + /* oper classes */
+              28 + //sizeof(struct ieee80211_ht_cap) +
+              sizeof(struct ieee_types_bss_co_2040) +
+              sizeof(struct ieee80211_tdls_lnkie) +
+              (2 + sizeof(struct ieee80211_vht_cap)) +
+              4 + /*AID*/
+              (2 + sizeof(struct ieee80211_ht_operation)) +
+              extra_ies_len);
+
+    if (!skb)
+        return 0;
+
+    switch (action_code) {
+    case WLAN_TDLS_SETUP_REQUEST:
+    case WLAN_TDLS_SETUP_RESPONSE:
+    case WLAN_TDLS_SETUP_CONFIRM:
+    case WLAN_TDLS_TEARDOWN:
+    case WLAN_TDLS_DISCOVERY_REQUEST:
+        ret = ecrnx_tdls_prepare_encap_data(ecrnx_hw, ecrnx_vif, peer, action_code,
+                                           dialog_token, status_code, skb);
+        break;
+
+    case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
+        ret = ecrnx_prep_tdls_direct(ecrnx_hw, ecrnx_vif, peer, action_code,
+                                    dialog_token, status_code, skb);
+        break;
+
+    default:
+        ret = -ENOTSUPP;
+        break;
+    }
+
+    if (ret < 0)
+        goto fail;
+
+    ecrnx_tdls_add_ies(ecrnx_hw, ecrnx_vif, skb, peer, action_code, status_code,
+                      initiator, extra_ies, extra_ies_len, oper_class, chandef);
+
+    if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
+        u64 cookie;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+        struct cfg80211_mgmt_tx_params params;
+
+        params.len = skb->len;
+        params.buf = skb->data;
+        ret = ecrnx_start_mgmt_xmit(ecrnx_vif, NULL, &params, false, &cookie);
+#else
+        ret = ecrnx_start_mgmt_xmit(ecrnx_vif, NULL, NULL, false, 0, skb->data, skb->len, false, false, &cookie);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+        return ret;
+    }
+
+    switch (action_code) {
+    case WLAN_TDLS_SETUP_REQUEST:
+    case WLAN_TDLS_SETUP_RESPONSE:
+    case WLAN_TDLS_SETUP_CONFIRM:
+        skb->priority = 2;
+        break;
+    default:
+        skb->priority = 5;
+        break;
+    }
+
+    ret = ecrnx_select_txq(ecrnx_vif, skb);
+    ret = ecrnx_start_xmit(skb, ecrnx_vif->ndev);
+
+   return ret;
+
+fail:
+    dev_kfree_skb(skb);
+    return ret;
+}
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_tdls.h b/drivers/net/wireless/eswin/fullmac/ecrnx_tdls.h
new file mode 100644 (file)
index 0000000..4bec85d
--- /dev/null
@@ -0,0 +1,54 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_tdls.h
+ *
+ * @brief TDLS function declarations
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef ECRNX_TDLS_H_
+#define ECRNX_TDLS_H_
+
+#include "ecrnx_defs.h"
+
+struct ieee_types_header {
+    u8 element_id;
+    u8 len;
+} __packed;
+
+struct ieee_types_bss_co_2040 {
+    struct ieee_types_header ieee_hdr;
+    u8 bss_2040co;
+} __packed;
+
+struct ieee_types_extcap {
+    struct ieee_types_header ieee_hdr;
+    u8 ext_capab[8];
+} __packed;
+
+struct ieee_types_vht_cap {
+    struct ieee_types_header ieee_hdr;
+    struct ieee80211_vht_cap vhtcap;
+} __packed;
+
+struct ieee_types_vht_oper {
+    struct ieee_types_header ieee_hdr;
+    struct ieee80211_vht_operation vhtoper;
+} __packed;
+
+struct ieee_types_aid {
+    struct ieee_types_header ieee_hdr;
+    u16 aid;
+} __packed;
+
+int ecrnx_tdls_send_mgmt_packet_data(struct ecrnx_hw *ecrnx_hw, struct ecrnx_vif *ecrnx_vif,
+                         const u8 *peer, u8 action_code, u8 dialog_token,
+                         u16 status_code, u32 peer_capability, bool initiator,
+                         const u8 *extra_ies, size_t extra_ies_len, u8 oper_class,
+                         struct cfg80211_chan_def *chandef);
+
+#endif /* ECRNX_TDLS_H_ */
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_tx.c b/drivers/net/wireless/eswin/fullmac/ecrnx_tx.c
new file mode 100644 (file)
index 0000000..459e818
--- /dev/null
@@ -0,0 +1,1877 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_tx.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+
+#include "ecrnx_defs.h"
+#include "ecrnx_tx.h"
+#include "ecrnx_msg_tx.h"
+#include "ecrnx_mesh.h"
+#include "ecrnx_events.h"
+#include "ecrnx_compat.h"
+
+#ifdef CONFIG_ECRNX_ESWIN
+#include "eswin_utils.h"
+#endif
+/******************************************************************************
+ * Power Save functions
+ *****************************************************************************/
+/**
+ * ecrnx_set_traffic_status - Inform FW if traffic is available for STA in PS
+ *
+ * @ecrnx_hw: Driver main data
+ * @sta: Sta in PS mode
+ * @available: whether traffic is buffered for the STA
+ * @ps_id: type of PS data requested (@LEGACY_PS_ID or @UAPSD_ID)
+  */
+void ecrnx_set_traffic_status(struct ecrnx_hw *ecrnx_hw,
+                             struct ecrnx_sta *sta,
+                             bool available,
+                             u8 ps_id)
+{
+    if (sta->tdls.active) {
+        ecrnx_send_tdls_peer_traffic_ind_req(ecrnx_hw,
+                                            ecrnx_hw->vif_table[sta->vif_idx]);
+    } else {
+        bool uapsd = (ps_id != LEGACY_PS_ID);
+        ecrnx_send_me_traffic_ind(ecrnx_hw, sta->sta_idx, uapsd, available);
+        trace_ps_traffic_update(sta->sta_idx, available, uapsd);
+    }
+}
+
+/**
+ * ecrnx_ps_bh_enable - Enable/disable PS mode for one STA
+ *
+ * @ecrnx_hw: Driver main data
+ * @sta: Sta which enters/leaves PS mode
+ * @enable: PS mode status
+ *
+ * This function will enable/disable PS mode for one STA.
+ * When enabling PS mode:
+ *  - Stop all STA's txq for ECRNX_TXQ_STOP_STA_PS reason
+ *  - Count how many buffers are already ready for this STA
+ *  - For BC/MC sta, update all queued SKB to use hw_queue BCMC
+ *  - Update TIM if some packet are ready
+ *
+ * When disabling PS mode:
+ *  - Start all STA's txq for ECRNX_TXQ_STOP_STA_PS reason
+ *  - For BC/MC sta, update all queued SKB to use hw_queue AC_BE
+ *  - Update TIM if some packet are ready (otherwise fw will not update TIM
+ *    in beacon for this STA)
+ *
+ * All counter/skb updates are protected from TX path by taking tx_lock
+ *
+ * NOTE: _bh_ in function name indicates that this function is called
+ * from a bottom_half tasklet.
+ */
+void ecrnx_ps_bh_enable(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+                       bool enable)
+{
+    struct ecrnx_txq *txq;
+
+    if (enable) {
+        trace_ps_enable(sta);
+
+        spin_lock(&ecrnx_hw->tx_lock);
+        sta->ps.active = true;
+        sta->ps.sp_cnt[LEGACY_PS_ID] = 0;
+        sta->ps.sp_cnt[UAPSD_ID] = 0;
+        ecrnx_txq_sta_stop(sta, ECRNX_TXQ_STOP_STA_PS, ecrnx_hw);
+
+        if (is_multicast_sta(sta->sta_idx)) {
+            txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);
+            sta->ps.pkt_ready[LEGACY_PS_ID] = skb_queue_len(&txq->sk_list);
+            sta->ps.pkt_ready[UAPSD_ID] = 0;
+            txq->hwq = &ecrnx_hw->hwq[ECRNX_HWQ_BCMC];
+        } else {
+            int i;
+            sta->ps.pkt_ready[LEGACY_PS_ID] = 0;
+            sta->ps.pkt_ready[UAPSD_ID] = 0;
+            foreach_sta_txq(sta, txq, i, ecrnx_hw) {
+                sta->ps.pkt_ready[txq->ps_id] += skb_queue_len(&txq->sk_list);
+            }
+        }
+
+        spin_unlock(&ecrnx_hw->tx_lock);
+
+        if (sta->ps.pkt_ready[LEGACY_PS_ID])
+            ecrnx_set_traffic_status(ecrnx_hw, sta, true, LEGACY_PS_ID);
+
+        if (sta->ps.pkt_ready[UAPSD_ID])
+            ecrnx_set_traffic_status(ecrnx_hw, sta, true, UAPSD_ID);
+    } else {
+        trace_ps_disable(sta->sta_idx);
+
+        spin_lock(&ecrnx_hw->tx_lock);
+        sta->ps.active = false;
+
+        if (is_multicast_sta(sta->sta_idx)) {
+            txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);
+            txq->hwq = &ecrnx_hw->hwq[ECRNX_HWQ_BE];
+            txq->push_limit = 0;
+        } else {
+            int i;
+            foreach_sta_txq(sta, txq, i, ecrnx_hw) {
+                txq->push_limit = 0;
+            }
+        }
+
+        ecrnx_txq_sta_start(sta, ECRNX_TXQ_STOP_STA_PS, ecrnx_hw);
+        spin_unlock(&ecrnx_hw->tx_lock);
+
+        if (sta->ps.pkt_ready[LEGACY_PS_ID])
+            ecrnx_set_traffic_status(ecrnx_hw, sta, false, LEGACY_PS_ID);
+
+        if (sta->ps.pkt_ready[UAPSD_ID])
+            ecrnx_set_traffic_status(ecrnx_hw, sta, false, UAPSD_ID);
+    }
+}
+
+/**
+ * ecrnx_ps_bh_traffic_req - Handle traffic request for STA in PS mode
+ *
+ * @ecrnx_hw: Driver main data
+ * @sta: Sta which enters/leaves PS mode
+ * @pkt_req: number of pkt to push
+ * @ps_id: type of PS data requested (@LEGACY_PS_ID or @UAPSD_ID)
+ *
+ * This function will make sure that @pkt_req are pushed to fw
+ * whereas the STA is in PS mode.
+ * If request is 0, send all traffic
+ * If request is greater than available pkt, reduce request
+ * Note: request will also be reduce if txq credits are not available
+ *
+ * All counter updates are protected from TX path by taking tx_lock
+ *
+ * NOTE: _bh_ in function name indicates that this function is called
+ * from the bottom_half tasklet.
+ */
+void ecrnx_ps_bh_traffic_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+                            u16 pkt_req, u8 ps_id)
+{
+    int pkt_ready_all;
+       u16 txq_len;
+    struct ecrnx_txq *txq;
+
+#ifndef CONFIG_ECRNX_ESWIN
+    if (WARN(!sta->ps.active, "sta %pM is not in Power Save mode",
+             sta->mac_addr))
+        return;
+#else
+    if (!sta->ps.active)
+    {
+        ECRNX_DBG(" sta is not in Power Save mode %02x:%02x:%02x:%02x:%02x:%02x %d %d \n", sta->mac_addr[0], sta->mac_addr[1], sta->mac_addr[2], \
+                sta->mac_addr[3], sta->mac_addr[4], sta->mac_addr[5],pkt_req, ps_id);
+        return;
+    }
+#endif
+
+    trace_ps_traffic_req(sta, pkt_req, ps_id);
+
+    spin_lock(&ecrnx_hw->tx_lock);
+
+    /* Fw may ask to stop a service period with PS_SP_INTERRUPTED. This only
+       happens for p2p-go interface if NOA starts during a service period */
+    if ((pkt_req == PS_SP_INTERRUPTED) && (ps_id == UAPSD_ID)) {
+        int tid;
+        sta->ps.sp_cnt[ps_id] = 0;
+        foreach_sta_txq(sta, txq, tid, ecrnx_hw) {
+            txq->push_limit = 0;
+        }
+        goto done;
+    }
+
+    pkt_ready_all = (sta->ps.pkt_ready[ps_id] - sta->ps.sp_cnt[ps_id]);
+
+    /* Don't start SP until previous one is finished or we don't have
+       packet ready (which must not happen for U-APSD) */
+    if (sta->ps.sp_cnt[ps_id] || pkt_ready_all <= 0) {
+        goto done;
+    }
+
+    /* Adapt request to what is available. */
+    if (pkt_req == 0 || pkt_req > pkt_ready_all) {
+        pkt_req = pkt_ready_all;
+    }
+
+    /* Reset the SP counter */
+    sta->ps.sp_cnt[ps_id] = 0;
+
+    /* "dispatch" the request between txq */
+    if (is_multicast_sta(sta->sta_idx)) {
+        txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);
+        if (txq->credits <= 0)
+            goto done;
+        if (pkt_req > txq->credits)
+            pkt_req = txq->credits;
+        txq->push_limit = pkt_req;
+        sta->ps.sp_cnt[ps_id] = pkt_req;
+        ECRNX_DBG("%s-%d:sta:0x%p, sta_idx:%d, txq:0x%p, txq status:%d \n", __func__, __LINE__, sta, sta->sta_idx, txq, txq->status);
+        ecrnx_txq_add_to_hw_list(txq);
+        ecrnx_txq_sta_start(sta, ECRNX_TXQ_STOP_STA_PS, ecrnx_hw);
+    } else {
+        int i, tid;
+
+        for (i = 0; i < NX_NB_TID_PER_STA; i++) {
+                       tid = nx_tid_prio[i];
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+                       txq = ecrnx_txq_sta_get(sta, tid);
+#else
+                       txq = ecrnx_txq_sta_get(sta, tid, ecrnx_hw);
+#endif
+                       
+            txq_len = skb_queue_len(&txq->sk_list);
+
+            if (txq->ps_id != ps_id)
+                continue;
+
+            if (txq_len > txq->credits)
+                txq_len = txq->credits;
+
+            if (txq_len == 0)
+                continue;
+
+            if (txq_len < pkt_req) {
+                /* Not enough pkt queued in this txq, add this
+                   txq to hwq list and process next txq */
+                pkt_req -= txq_len;
+                txq->push_limit = txq_len;
+                sta->ps.sp_cnt[ps_id] += txq_len;
+                ecrnx_txq_add_to_hw_list(txq);
+            } else {
+                /* Enough pkt in this txq to comlete the request
+                   add this txq to hwq list and stop processing txq */
+                txq->push_limit = pkt_req;
+                sta->ps.sp_cnt[ps_id] += pkt_req;
+                ecrnx_txq_add_to_hw_list(txq);
+                break;
+            }
+        }
+    }
+
+  done:
+    spin_unlock(&ecrnx_hw->tx_lock);
+}
+
+/******************************************************************************
+ * TX functions
+ *****************************************************************************/
+#define PRIO_STA_NULL 0xAA
+
+static const int ecrnx_down_hwq2tid[3] = {
+    [ECRNX_HWQ_BK] = 2,
+    [ECRNX_HWQ_BE] = 3,
+    [ECRNX_HWQ_VI] = 5,
+};
+
+static void ecrnx_downgrade_ac(struct ecrnx_sta *sta, struct sk_buff *skb)
+{
+    int8_t ac = ecrnx_tid2hwq[skb->priority];
+
+    if (WARN((ac > ECRNX_HWQ_VO),
+             "Unexepcted ac %d for skb before downgrade", ac))
+        ac = ECRNX_HWQ_VO;
+
+    while (sta->acm & BIT(ac)) {
+        if (ac == ECRNX_HWQ_BK) {
+            skb->priority = 1;
+            return;
+        }
+        ac--;
+        skb->priority = ecrnx_down_hwq2tid[ac];
+    }
+}
+
+static void ecrnx_tx_statistic(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq,
+                              union ecrnx_hw_txstatus ecrnx_txst, unsigned int data_len)
+{
+    struct ecrnx_sta *sta = txq->sta;
+    if (!sta || !ecrnx_txst.acknowledged)
+        return;
+    sta->stats.tx_pkts ++;
+    sta->stats.tx_bytes += data_len;
+    sta->stats.last_act = ecrnx_hw->stats.last_tx;
+}
+u16 ecrnx_select_txq(struct ecrnx_vif *ecrnx_vif, struct sk_buff *skb)
+{
+    struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+    struct wireless_dev *wdev = &ecrnx_vif->wdev;
+    struct ecrnx_sta *sta = NULL;
+    struct ecrnx_txq *txq;
+    u16 netdev_queue;
+    bool tdls_mgmgt_frame = false;
+
+    switch (wdev->iftype) {
+    case NL80211_IFTYPE_STATION:
+    case NL80211_IFTYPE_P2P_CLIENT:
+    {
+        struct ethhdr *eth;
+        eth = (struct ethhdr *)skb->data;
+        if (eth->h_proto == cpu_to_be16(ETH_P_TDLS)) {
+            tdls_mgmgt_frame = true;
+        }
+        if ((ecrnx_vif->tdls_status == TDLS_LINK_ACTIVE) &&
+            (ecrnx_vif->sta.tdls_sta != NULL) &&
+            (memcmp(eth->h_dest, ecrnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN) == 0))
+            sta = ecrnx_vif->sta.tdls_sta;
+        else
+            sta = ecrnx_vif->sta.ap;
+        break;
+    }
+    case NL80211_IFTYPE_AP_VLAN:
+    {
+        struct ecrnx_sta *cur;
+        struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+        if (ecrnx_vif->ap_vlan.sta_4a) {
+            sta = ecrnx_vif->ap_vlan.sta_4a;
+            break;
+        }
+
+        /* AP_VLAN interface is not used for a 4A STA,
+           fallback searching sta amongs all AP's clients */
+        ecrnx_vif = ecrnx_vif->ap_vlan.master;
+        
+        if (is_multicast_ether_addr(eth->h_dest)) {
+            sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+        } else {
+            list_for_each_entry(cur, &ecrnx_vif->ap.sta_list, list) {
+                if (!memcmp(cur->mac_addr, eth->h_dest, ETH_ALEN)) {
+                    sta = cur;
+                    break;
+                }
+            }
+        }
+
+        break;
+    }
+    case NL80211_IFTYPE_AP:
+    case NL80211_IFTYPE_P2P_GO:
+    {
+        struct ecrnx_sta *cur;
+        struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+        if (is_multicast_ether_addr(eth->h_dest)) {
+            sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+        } else {
+            list_for_each_entry(cur, &ecrnx_vif->ap.sta_list, list) {
+                if (!memcmp(cur->mac_addr, eth->h_dest, ETH_ALEN)) {
+                    sta = cur;
+                    break;
+                }
+            }
+        }
+
+        break;
+    }
+    case NL80211_IFTYPE_MESH_POINT:
+    {
+        struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+        if (!ecrnx_vif->is_resending) {
+            /*
+             * If ethernet source address is not the address of a mesh wireless interface, we are proxy for
+             * this address and have to inform the HW
+             */
+            if (memcmp(&eth->h_source[0], &ecrnx_vif->ndev->perm_addr[0], ETH_ALEN)) {
+                /* Check if LMAC is already informed */
+                if (!ecrnx_get_mesh_proxy_info(ecrnx_vif, (u8 *)&eth->h_source, true)) {
+                    ecrnx_send_mesh_proxy_add_req(ecrnx_hw, ecrnx_vif, (u8 *)&eth->h_source);
+                }
+            }
+        }
+
+        if (is_multicast_ether_addr(eth->h_dest)) {
+            sta = &ecrnx_hw->sta_table[ecrnx_vif->ap.bcmc_index];
+        } else {
+            /* Path to be used */
+            struct ecrnx_mesh_path *p_mesh_path = NULL;
+            struct ecrnx_mesh_path *p_cur_path;
+            /* Check if destination is proxied by a peer Mesh STA */
+            struct ecrnx_mesh_proxy *p_mesh_proxy = ecrnx_get_mesh_proxy_info(ecrnx_vif, (u8 *)&eth->h_dest, false);
+            /* Mesh Target address */
+            struct mac_addr *p_tgt_mac_addr;
+
+            if (p_mesh_proxy) {
+                p_tgt_mac_addr = &p_mesh_proxy->proxy_addr;
+            } else {
+                p_tgt_mac_addr = (struct mac_addr *)&eth->h_dest;
+            }
+
+            /* Look for path with provided target address */
+            list_for_each_entry(p_cur_path, &ecrnx_vif->ap.mpath_list, list) {
+                if (!memcmp(&p_cur_path->tgt_mac_addr, p_tgt_mac_addr, ETH_ALEN)) {
+                    p_mesh_path = p_cur_path;
+                    break;
+                }
+            }
+
+            if (p_mesh_path) {
+                sta = p_mesh_path->nhop_sta;
+            } else {
+                ecrnx_send_mesh_path_create_req(ecrnx_hw, ecrnx_vif, (u8 *)p_tgt_mac_addr);
+            }
+        }
+
+        break;
+    }
+    default:
+        break;
+    }
+
+    if (sta && sta->qos)
+    {
+        if (tdls_mgmgt_frame) {
+            skb_set_queue_mapping(skb, NX_STA_NDEV_IDX(skb->priority, sta->sta_idx));
+        } else {
+            /* use the data classifier to determine what 802.1d tag the
+             * data frame has */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
+            skb->priority = cfg80211_classify8021d(skb) & IEEE80211_QOS_CTL_TAG1D_MASK;
+#else
+            skb->priority = cfg80211_classify8021d(skb, NULL) & IEEE80211_QOS_CTL_TAG1D_MASK;
+#endif
+        }
+        if (sta->acm)
+            ecrnx_downgrade_ac(sta, skb);
+
+        txq = ecrnx_txq_sta_get(sta, skb->priority, ecrnx_hw);
+        netdev_queue = txq->ndev_idx;
+    }
+    else if (sta)
+    {
+        skb->priority = 0xFF;
+        txq = ecrnx_txq_sta_get(sta, 0, ecrnx_hw);
+        netdev_queue = txq->ndev_idx;
+    }
+    else
+    {
+        /* This packet will be dropped in xmit function, still need to select
+           an active queue for xmit to be called. As it most likely to happen
+           for AP interface, select BCMC queue
+           (TODO: select another queue if BCMC queue is stopped) */
+        skb->priority = PRIO_STA_NULL;
+        netdev_queue = NX_BCMC_TXQ_NDEV_IDX;
+    }
+
+    BUG_ON(netdev_queue >= NX_NB_NDEV_TXQ);
+
+    return netdev_queue;
+}
+
+/**
+ * ecrnx_set_more_data_flag - Update MORE_DATA flag in tx sw desc
+ *
+ * @ecrnx_hw: Driver main data
+ * @sw_txhdr: Header for pkt to be pushed
+ *
+ * If STA is in PS mode
+ *  - Set EOSP in case the packet is the last of the UAPSD service period
+ *  - Set MORE_DATA flag if more pkt are ready for this sta
+ *  - Update TIM if this is the last pkt buffered for this sta
+ *
+ * note: tx_lock already taken.
+ */
+static inline void ecrnx_set_more_data_flag(struct ecrnx_hw *ecrnx_hw,
+                                           struct ecrnx_sw_txhdr *sw_txhdr)
+{
+    struct ecrnx_sta *sta = sw_txhdr->ecrnx_sta;
+    struct ecrnx_vif *vif = sw_txhdr->ecrnx_vif;
+    struct ecrnx_txq *txq = sw_txhdr->txq;
+
+    if (unlikely(sta->ps.active)) {
+        sta->ps.pkt_ready[txq->ps_id]--;
+        sta->ps.sp_cnt[txq->ps_id]--;
+
+        trace_ps_push(sta);
+
+        if (((txq->ps_id == UAPSD_ID) || (vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) || (sta->tdls.active))
+                && !sta->ps.sp_cnt[txq->ps_id]) {
+            sw_txhdr->desc.host.flags |= TXU_CNTRL_EOSP;
+        }
+
+        if (sta->ps.pkt_ready[txq->ps_id]) {
+            sw_txhdr->desc.host.flags |= TXU_CNTRL_MORE_DATA;
+        } else {
+            ecrnx_set_traffic_status(ecrnx_hw, sta, false, txq->ps_id);
+        }
+    }
+}
+
+/**
+ * ecrnx_get_tx_info - Get STA and tid for one skb
+ *
+ * @ecrnx_vif: vif ptr
+ * @skb: skb
+ * @tid: pointer updated with the tid to use for this skb
+ *
+ * @return: pointer on the destination STA (may be NULL)
+ *
+ * skb has already been parsed in ecrnx_select_queue function
+ * simply re-read information form skb.
+ */
+static struct ecrnx_sta *ecrnx_get_tx_info(struct ecrnx_vif *ecrnx_vif,
+                                         struct sk_buff *skb,
+                                         u8 *tid)
+{
+    struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+    struct ecrnx_sta *sta;
+    int sta_idx;
+
+    *tid = skb->priority;
+    if (unlikely(skb->priority == PRIO_STA_NULL)) {
+        return NULL;
+    } else {
+        int ndev_idx = skb_get_queue_mapping(skb);
+
+        if (ndev_idx == NX_BCMC_TXQ_NDEV_IDX)
+            sta_idx = NX_REMOTE_STA_MAX + master_vif_idx(ecrnx_vif);
+        else
+            sta_idx = ndev_idx / NX_NB_TID_PER_STA;
+
+        sta = &ecrnx_hw->sta_table[sta_idx];
+    }
+
+    return sta;
+}
+
+#ifndef CONFIG_ECRNX_ESWIN
+/**
+ * ecrnx_prep_tx - Prepare buffer for DMA transmission
+ *
+ * @ecrnx_hw: Driver main data
+ * @txhdr: Tx descriptor
+ *
+ * Maps hw_txhdr and buffer data for transmission via DMA.
+ * - Data buffer with be downloaded by embebded side.
+ * - hw_txhdr will be uploaded by embedded side when buffer has been
+ *   transmitted over the air.
+ */
+static int ecrnx_prep_dma_tx(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txhdr *txhdr, bool eth_hdr)
+{
+    struct ecrnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+    struct ecrnx_hw_txhdr *hw_txhdr = &txhdr->hw_hdr;
+    struct txdesc_api *desc = &sw_txhdr->desc;
+    dma_addr_t dma_addr;
+
+    txhdr->hw_hdr.cfm.status.value = 0;
+    /* MAP (and sync) memory for DMA */
+    dma_addr = dma_map_single(ecrnx_hw->dev, hw_txhdr,
+                              sw_txhdr->map_len, DMA_BIDIRECTIONAL);
+    if (WARN_ON(dma_mapping_error(ecrnx_hw->dev, dma_addr)))
+        return -1;
+
+    sw_txhdr->dma_addr = dma_addr;
+
+    desc->host.status_desc_addr = dma_addr;
+    dma_addr += ECRNX_TX_DATA_OFT(sw_txhdr);
+    if (eth_hdr)
+        dma_addr += sizeof(struct ethhdr);
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+    desc->host.packet_len[0] = sw_txhdr->frame_len;
+    desc->host.packet_addr[0] = dma_addr;
+    desc->host.packet_cnt = 1;
+#else
+    desc->host.packet_len = sw_txhdr->frame_len;
+    desc->host.packet_addr = dma_addr;
+#endif
+    return 0;
+}
+#endif
+
+/**
+ *  ecrnx_tx_push - Push one packet to fw
+ *
+ * @ecrnx_hw: Driver main data
+ * @txhdr: tx desc of the buffer to push
+ * @flags: push flags (see @ecrnx_push_flags)
+ *
+ * Push one packet to fw. Sw desc of the packet has already been updated.
+ * Only MORE_DATA flag will be set if needed.
+ */
+void ecrnx_tx_push(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txhdr *txhdr, int flags)
+{
+    struct ecrnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+    struct sk_buff *skb = sw_txhdr->skb;
+    struct ecrnx_txq *txq = sw_txhdr->txq;
+    u16 hw_queue = txq->hwq->id;
+    int user = 0;
+
+    lockdep_assert_held(&ecrnx_hw->tx_lock);
+
+    /* RETRY flag is not always set so retest here */
+    if (txq->nb_retry) {
+        flags |= ECRNX_PUSH_RETRY;
+        txq->nb_retry--;
+        if (txq->nb_retry == 0) {
+            WARN(skb != txq->last_retry_skb,
+                 "last retry buffer is not the expected one");
+            txq->last_retry_skb = NULL;
+        }
+    } else if (!(flags & ECRNX_PUSH_RETRY)) {
+        txq->pkt_sent++;
+    }
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    if (txq->amsdu == sw_txhdr) {
+        WARN((flags & ECRNX_PUSH_RETRY), "End A-MSDU on a retry");
+        ecrnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+        txq->amsdu = NULL;
+    } else if (!(flags & ECRNX_PUSH_RETRY) &&
+               !(sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU)) {
+        ecrnx_hw->stats.amsdus[0].done++;
+    }
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+    /* Wait here to update hw_queue, as for multicast STA hwq may change
+       between queue and push (because of PS) */
+    sw_txhdr->hw_queue = hw_queue;
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    /* MU group is only selected during hwq processing */
+    sw_txhdr->desc.host.mumimo_info = txq->mumimo_info;
+    user = ECRNX_TXQ_POS_ID(txq);
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+    if (sw_txhdr->ecrnx_sta) {
+        /* only for AP mode */
+        ecrnx_set_more_data_flag(ecrnx_hw, sw_txhdr);
+    }
+
+    trace_push_desc(skb, sw_txhdr, flags);
+    txq->credits--;
+    txq->pkt_pushed[user]++;
+    if (txq->credits <= 0){
+        ECRNX_DBG("%s-%d:ecrnx_txq_stop,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_FULL);
+        ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_FULL);
+    }
+
+    if (txq->push_limit)
+        txq->push_limit--;
+
+    ecrnx_ipc_txdesc_push(ecrnx_hw, &sw_txhdr->desc, skb, hw_queue, user);
+    txq->hwq->credits[user]--;
+    ecrnx_hw->stats.cfm_balance[hw_queue]++;
+}
+
+
+
+/**
+ * ecrnx_tx_retry - Push an AMPDU pkt that need to be retried
+ *
+ * @ecrnx_hw: Driver main data
+ * @skb: pkt to re-push
+ * @txhdr: tx desc of the pkt to re-push
+ * @sw_retry: Indicates if fw decide to retry this buffer
+ *            (i.e. it has never been transmitted over the air)
+ *
+ * Called when a packet needs to be repushed to the firmware.
+ * First update sw descriptor and then queue it in the retry list.
+ */
+static void ecrnx_tx_retry(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+                           struct ecrnx_txhdr *txhdr, bool sw_retry)
+{
+    struct ecrnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+    struct tx_cfm_tag *cfm = &txhdr->hw_hdr.cfm;
+    struct ecrnx_txq *txq = sw_txhdr->txq;
+#ifndef CONFIG_ECRNX_ESWIN
+    dma_addr_t cfm_dma_addr;
+#endif
+
+    if (!sw_retry) {
+        /* update sw desc */
+        sw_txhdr->desc.host.sn = cfm->sn;
+        sw_txhdr->desc.host.pn[0] = cfm->pn[0];
+        sw_txhdr->desc.host.pn[1] = cfm->pn[1];
+        sw_txhdr->desc.host.pn[2] = cfm->pn[2];
+        sw_txhdr->desc.host.pn[3] = cfm->pn[3];
+        sw_txhdr->desc.host.timestamp = cfm->timestamp;
+        sw_txhdr->desc.host.flags |= TXU_CNTRL_RETRY;
+
+        #ifdef CONFIG_ECRNX_AMSDUS_TX
+        if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU)
+            ecrnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].failed++;
+        #endif
+    }
+
+    /* MORE_DATA will be re-set if needed when pkt will be repushed */
+    sw_txhdr->desc.host.flags &= ~TXU_CNTRL_MORE_DATA;
+
+    cfm->status.value = 0;
+//TODO:need to check here. 
+#ifndef CONFIG_ECRNX_ESWIN
+       cfm_dma_addr = (ptr_addr)sw_txhdr->desc.host.status_desc_addr;
+    dma_sync_single_for_device(ecrnx_hw->dev, cfm_dma_addr, sizeof(cfm), DMA_BIDIRECTIONAL);
+#endif
+    txq->credits++;
+    if (txq->credits > 0){
+        ecrnx_txq_start(txq, ECRNX_TXQ_STOP_FULL);
+        ECRNX_DBG("%s-%d:ecrnx_txq_start,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_FULL);
+    }
+    /* Queue the buffer */
+    if (ecrnx_txq_queue_skb(skb, txq, ecrnx_hw, true, NULL))
+    {
+        /* baoyong:we need to send this AMPDU retry pkt asap, so process it now */
+        ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+    }
+
+    return;
+}
+
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+/* return size of subframe (including header) */
+static inline int ecrnx_amsdu_subframe_length(struct ethhdr *eth, int eth_len)
+{
+    /* ethernet header is replaced with amdsu header that have the same size
+       Only need to check if LLC/SNAP header will be added */
+    int len = eth_len;
+
+    if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+        len += sizeof(rfc1042_header) + 2;
+    }
+
+    return len;
+}
+
+static inline bool ecrnx_amsdu_is_aggregable(struct sk_buff *skb)
+{
+    /* need to add some check on buffer to see if it can be aggregated ? */
+    return true;
+}
+
+
+/**
+ * ecrnx_amsdu_del_subframe_header - remove AMSDU header
+ *
+ * amsdu_txhdr: amsdu tx descriptor
+ *
+ * Move back the ethernet header at the "beginning" of the data buffer.
+ * (which has been moved in @ecrnx_amsdu_add_subframe_header)
+ */
+static void ecrnx_amsdu_del_subframe_header(struct ecrnx_amsdu_txhdr *amsdu_txhdr)
+{
+    struct sk_buff *skb = amsdu_txhdr->skb;
+    struct ethhdr *eth;
+    u8 *pos;
+
+    pos = skb->data;
+    pos += sizeof(struct ecrnx_amsdu_txhdr);
+    eth = (struct ethhdr*)pos;
+    pos += amsdu_txhdr->pad + sizeof(struct ethhdr);
+
+    if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+        pos += sizeof(rfc1042_header) + 2;
+    }
+
+    memmove(pos, eth, sizeof(*eth));
+    skb_pull(skb, (pos - skb->data));
+}
+
+/**
+ * ecrnx_amsdu_add_subframe_header - Add AMSDU header and link subframe
+ *
+ * @ecrnx_hw Driver main data
+ * @skb Buffer to aggregate
+ * @sw_txhdr Tx descriptor for the first A-MSDU subframe
+ *
+ * return 0 on sucess, -1 otherwise
+ *
+ * This functions Add A-MSDU header and LLC/SNAP header in the buffer
+ * and update sw_txhdr of the first subframe to link this buffer.
+ * If an error happens, the buffer will be queued as a normal buffer.
+ *
+ *
+ *            Before           After
+ *         +-------------+  +-------------+
+ *         | HEADROOM    |  | HEADROOM    |
+ *         |             |  +-------------+ <- data
+ *         |             |  | amsdu_txhdr |
+ *         |             |  | * pad size  |
+ *         |             |  +-------------+
+ *         |             |  | ETH hdr     | keep original eth hdr
+ *         |             |  |             | to restore it once transmitted
+ *         |             |  +-------------+ <- packet_addr[x]
+ *         |             |  | Pad         |
+ *         |             |  +-------------+
+ * data -> +-------------+  | AMSDU HDR   |
+ *         | ETH hdr     |  +-------------+
+ *         |             |  | LLC/SNAP    |
+ *         +-------------+  +-------------+
+ *         | DATA        |  | DATA        |
+ *         |             |  |             |
+ *         +-------------+  +-------------+
+ *
+ * Called with tx_lock hold
+ */
+static int ecrnx_amsdu_add_subframe_header(struct ecrnx_hw *ecrnx_hw,
+                                          struct sk_buff *skb,
+                                          struct ecrnx_sw_txhdr *sw_txhdr)
+{
+    struct ecrnx_amsdu *amsdu = &sw_txhdr->amsdu;
+    struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+    struct ethhdr *amsdu_hdr, *eth = (struct ethhdr *)skb->data;
+    int headroom_need, map_len, msdu_len, amsdu_len, map_oft = 0;
+#ifndef CONFIG_ECRNX_ESWIN
+    dma_addr_t dma_addr;
+#endif
+    u8 *pos, *map_start;
+
+    map_len = ECRNX_TX_DMA_MAP_LEN(skb);
+    msdu_len = skb->len - sizeof(*eth);
+    headroom_need = sizeof(*amsdu_txhdr) + amsdu->pad +
+        sizeof(*amsdu_hdr);
+    if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+        headroom_need += sizeof(rfc1042_header) + 2;
+        msdu_len += sizeof(rfc1042_header) + 2;
+    }
+    amsdu_len = msdu_len + sizeof(*amsdu_hdr) + amsdu->pad;
+
+    /* we should have enough headroom (checked in xmit) */
+    if (WARN_ON(skb_headroom(skb) < headroom_need)) {
+        return -1;
+    }
+
+    /* allocate headroom */
+    pos = skb_push(skb, headroom_need);
+    amsdu_txhdr = (struct ecrnx_amsdu_txhdr *)pos;
+    pos += sizeof(*amsdu_txhdr);
+
+    /* move eth header */
+    memmove(pos, eth, sizeof(*eth));
+    eth = (struct ethhdr *)pos;
+    pos += sizeof(*eth);
+
+    /* Add padding from previous subframe */
+    map_start = pos;
+    memset(pos, 0, amsdu->pad);
+    pos += amsdu->pad;
+
+    /* Add AMSDU hdr */
+    amsdu_hdr = (struct ethhdr *)pos;
+    memcpy(amsdu_hdr->h_dest, eth->h_dest, ETH_ALEN);
+    memcpy(amsdu_hdr->h_source, eth->h_source, ETH_ALEN);
+    amsdu_hdr->h_proto = htons(msdu_len);
+    pos += sizeof(*amsdu_hdr);
+
+    if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) {
+        memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+        pos += sizeof(rfc1042_header) + 2;
+    }
+
+    if (amsdu_len < map_len) {
+        map_oft = map_len - amsdu_len;
+        map_start -= map_oft;
+    }
+    /* MAP (and sync) memory for DMA */
+#ifndef CONFIG_ECRNX_ESWIN  
+    dma_addr = dma_map_single(ecrnx_hw->dev, map_start, map_len, DMA_BIDIRECTIONAL);
+    if (WARN_ON(dma_mapping_error(ecrnx_hw->dev, dma_addr))) {
+        pos -= sizeof(*eth);
+        memmove(pos, eth, sizeof(*eth));
+        skb_pull(skb, headroom_need);
+        return -1;
+    }
+#endif
+
+    /* update amdsu_txhdr */
+    amsdu_txhdr->map_len = map_len;
+#ifdef CONFIG_ECRNX_ESWIN
+    amsdu_txhdr->send_pos = map_start;
+#else
+    amsdu_txhdr->dma_addr = dma_addr;
+#endif
+    amsdu_txhdr->skb = skb;
+    amsdu_txhdr->pad = amsdu->pad;
+    amsdu_txhdr->msdu_len = msdu_len;
+
+    /* update ecrnx_sw_txhdr (of the first subframe) */
+    BUG_ON(amsdu->nb != sw_txhdr->desc.host.packet_cnt);
+#ifdef CONFIG_ECRNX_ESWIN
+    sw_txhdr->desc.host.packet_addr[amsdu->nb] = skb;
+#else
+    sw_txhdr->desc.host.packet_addr[amsdu->nb] = dma_addr + map_oft;
+#endif
+    sw_txhdr->desc.host.packet_len[amsdu->nb] = amsdu_len;
+    sw_txhdr->desc.host.packet_cnt++;
+    amsdu->nb++;
+
+    amsdu->pad = AMSDU_PADDING(amsdu_len - amsdu->pad);
+    list_add_tail(&amsdu_txhdr->list, &amsdu->hdrs);
+    amsdu->len += amsdu_len;
+
+    ecrnx_ipc_sta_buffer(ecrnx_hw, sw_txhdr->txq->sta,
+                        sw_txhdr->txq->tid, msdu_len);
+
+    trace_amsdu_subframe(sw_txhdr);
+    return 0;
+}
+
+/**
+ * ecrnx_amsdu_add_subframe - Add this buffer as an A-MSDU subframe if possible
+ *
+ * @ecrnx_hw Driver main data
+ * @skb Buffer to aggregate if possible
+ * @sta Destination STA
+ * @txq sta's txq used for this buffer
+ *
+ * Tyr to aggregate the buffer in an A-MSDU. If it succeed then the
+ * buffer is added as a new A-MSDU subframe with AMSDU and LLC/SNAP
+ * headers added (so FW won't have to modify this subframe).
+ *
+ * To be added as subframe :
+ * - sta must allow amsdu
+ * - buffer must be aggregable (to be defined)
+ * - at least one other aggregable buffer is pending in the queue
+ *  or an a-msdu (with enough free space) is currently in progress
+ *
+ * returns true if buffer has been added as A-MDSP subframe, false otherwise
+ *
+ */
+static bool ecrnx_amsdu_add_subframe(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+                                    struct ecrnx_sta *sta, struct ecrnx_txq *txq)
+{
+    bool res = false;
+    struct ethhdr *eth;
+    ecrnx_adjust_amsdu_maxnb(ecrnx_hw);
+
+    /* immediately return if amsdu are not allowed for this sta */
+    if (!txq->amsdu_len || ecrnx_hw->mod_params->amsdu_maxnb < 2 ||
+        !ecrnx_amsdu_is_aggregable(skb)
+       )
+        return false;
+
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+    if (txq->amsdu) {
+        /* aggreagation already in progress, add this buffer if enough space
+           available, otherwise end the current amsdu */
+        struct ecrnx_sw_txhdr *sw_txhdr = txq->amsdu;
+        eth = (struct ethhdr *)(skb->data);
+
+        if (((sw_txhdr->amsdu.len + sw_txhdr->amsdu.pad +
+              ecrnx_amsdu_subframe_length(eth, skb->len)) > txq->amsdu_len) ||
+            ecrnx_amsdu_add_subframe_header(ecrnx_hw, skb, sw_txhdr)) {
+            txq->amsdu = NULL;
+            goto end;
+        }
+
+        if (sw_txhdr->amsdu.nb >= ecrnx_hw->mod_params->amsdu_maxnb) {
+            ecrnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+            /* max number of subframes reached */
+            txq->amsdu = NULL;
+        }
+    } else {
+        /* Check if a new amsdu can be started with the previous buffer
+           (if any) and this one */
+        struct sk_buff *skb_prev = skb_peek_tail(&txq->sk_list);
+        struct ecrnx_txhdr *txhdr;
+        struct ecrnx_sw_txhdr *sw_txhdr;
+        int len1, len2;
+
+        if (!skb_prev || !ecrnx_amsdu_is_aggregable(skb_prev))
+            goto end;
+
+        txhdr = (struct ecrnx_txhdr *)skb_prev->data;
+        sw_txhdr = txhdr->sw_hdr;
+        if ((sw_txhdr->amsdu.len) ||
+            (sw_txhdr->desc.host.flags & TXU_CNTRL_RETRY))
+            /* previous buffer is already a complete amsdu or a retry */
+            goto end;
+
+        eth = (struct ethhdr *)(skb_prev->data + sw_txhdr->headroom);
+        len1 = ecrnx_amsdu_subframe_length(eth, (sw_txhdr->frame_len +
+                                                sizeof(struct ethhdr)));
+
+        eth = (struct ethhdr *)(skb->data);
+        len2 = ecrnx_amsdu_subframe_length(eth, skb->len);
+
+        if (len1 + AMSDU_PADDING(len1) + len2 > txq->amsdu_len)
+            /* not enough space to aggregate those two buffers */
+            goto end;
+
+        /* Add subframe header.
+           Note: Fw will take care of adding AMDSU header for the first
+           subframe while generating 802.11 MAC header */
+        INIT_LIST_HEAD(&sw_txhdr->amsdu.hdrs);
+        sw_txhdr->amsdu.len = len1;
+        sw_txhdr->amsdu.nb = 1;
+        sw_txhdr->amsdu.pad = AMSDU_PADDING(len1);
+        if (ecrnx_amsdu_add_subframe_header(ecrnx_hw, skb, sw_txhdr))
+            goto end;
+
+        sw_txhdr->desc.host.flags |= TXU_CNTRL_AMSDU;
+
+        if (sw_txhdr->amsdu.nb < ecrnx_hw->mod_params->amsdu_maxnb)
+            txq->amsdu = sw_txhdr;
+        else
+            ecrnx_hw->stats.amsdus[sw_txhdr->amsdu.nb - 1].done++;
+    }
+
+    res = true;
+
+  end:
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+    return res;
+}
+/**
+ * ecrnx_amsdu_dismantle - Dismantle an already formatted A-MSDU
+ *
+ * @ecrnx_hw Driver main data
+ * @sw_txhdr_main Software descriptor of the A-MSDU to dismantle.
+ *
+ * The a-mdsu is always fully dismantled (i.e don't try to reduce it's size to
+ * fit the new limit).
+ * The DMA mapping can be re-used as ecrnx_amsdu_add_subframe_header ensure that
+ * enough data in the skb bufer are 'DMA mapped'.
+ * It would have been slightly simple to unmap/re-map but it is a little faster like this
+ * and not that much more complicated to read.
+ */
+static void ecrnx_amsdu_dismantle(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sw_txhdr *sw_txhdr_main)
+{
+    struct ecrnx_amsdu_txhdr *amsdu_txhdr, *next;
+    struct sk_buff *skb_prev = sw_txhdr_main->skb;
+    struct ecrnx_txq *txq =  sw_txhdr_main->txq;
+    trace_amsdu_dismantle(sw_txhdr_main);
+    ecrnx_hw->stats.amsdus[sw_txhdr_main->amsdu.nb - 1].done--;
+    sw_txhdr_main->amsdu.len = 0;
+    sw_txhdr_main->amsdu.nb = 0;
+    sw_txhdr_main->desc.host.flags &= ~TXU_CNTRL_AMSDU;
+    sw_txhdr_main->desc.host.packet_cnt = 1;
+    list_for_each_entry_safe(amsdu_txhdr, next, &sw_txhdr_main->amsdu.hdrs, list) {
+        struct ecrnx_txhdr *txhdr;
+        struct ecrnx_sw_txhdr *sw_txhdr;
+        dma_addr_t dma_addr = amsdu_txhdr->dma_addr;
+        size_t map_len = amsdu_txhdr->map_len;
+        size_t tx_map_len;
+        size_t data_oft, cfm_oft = 0;
+        struct sk_buff *skb = amsdu_txhdr->skb;
+        int headroom;
+        list_del(&amsdu_txhdr->list);
+        ecrnx_ipc_sta_buffer(ecrnx_hw, txq->sta, txq->tid, -amsdu_txhdr->msdu_len);
+        ecrnx_amsdu_del_subframe_header(amsdu_txhdr);
+        headroom = ECRNX_TX_HEADROOM(skb);
+        tx_map_len = ECRNX_TX_DMA_MAP_LEN(skb);
+        sw_txhdr = kmem_cache_alloc(ecrnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+        if (unlikely((skb_headroom(skb) < headroom) ||
+                     (sw_txhdr == NULL) || (tx_map_len > map_len))) {
+            if (sw_txhdr)
+                kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+            dma_unmap_single(ecrnx_hw->dev, dma_addr, map_len, DMA_TO_DEVICE);
+            dev_kfree_skb_any(skb);
+            continue;
+        }
+        sw_txhdr->headroom = headroom;
+        cfm_oft = map_len - tx_map_len;
+        data_oft = sizeof(struct ethhdr) + ECRNX_TX_DATA_OFT(sw_txhdr) + cfm_oft;
+        txhdr = skb_push(skb, headroom);
+        txhdr->sw_hdr = sw_txhdr;
+        memcpy(sw_txhdr, sw_txhdr_main, sizeof(*sw_txhdr));
+        sw_txhdr->frame_len = map_len - data_oft;
+        sw_txhdr->skb = skb;
+        sw_txhdr->headroom = headroom;
+        txhdr->hw_hdr.cfm.status.value = 0;
+        sw_txhdr->map_len = map_len;
+        sw_txhdr->dma_addr = dma_addr;
+        sw_txhdr->desc.host.packet_addr[0] = dma_addr + data_oft;
+        sw_txhdr->desc.host.status_desc_addr = dma_addr + cfm_oft;
+        sw_txhdr->desc.host.packet_len[0] = sw_txhdr->frame_len;
+        sw_txhdr->desc.host.packet_cnt = 1;
+        ecrnx_txq_queue_skb(skb, sw_txhdr->txq, ecrnx_hw, false, skb_prev);
+        skb_prev = skb;
+    }
+}
+/**
+ * ecrnx_amsdu_update_len - Update length allowed for A-MSDU on a TXQ
+ *
+ * @ecrnx_hw Driver main data.
+ * @txq The TXQ.
+ * @amsdu_len New length allowed ofr A-MSDU.
+ *
+ * If this is a TXQ linked to a STA and the allowed A-MSDU size is reduced it is
+ * then necessary to disassemble all A-MSDU currently queued on all STA' txq that
+ * are larger than this new limit.
+ * Does nothing if the A-MSDU limit increase or stay the same.
+ */
+static void ecrnx_amsdu_update_len(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txq *txq,
+                                  u16 amsdu_len)
+{
+    struct ecrnx_sta *sta = txq->sta;
+    int tid;
+
+    if (amsdu_len != txq->amsdu_len)
+        trace_amsdu_len_update(txq->sta, amsdu_len);
+
+    if (amsdu_len >= txq->amsdu_len) {
+        txq->amsdu_len = amsdu_len;
+        return;
+    }
+
+    if (!sta) {
+        netdev_err(txq->ndev, "Non STA txq(%d) with a-amsdu len %d\n",
+                   txq->idx, amsdu_len);
+        txq->amsdu_len = 0;
+        return;
+    }
+
+    /* A-MSDU size has been reduced by the firmware, need to dismantle all
+       queued a-msdu that are too large. Need to do this for all txq of the STA. */
+    foreach_sta_txq(sta, txq, tid, ecrnx_hw) {
+        struct sk_buff *skb, *skb_next;
+
+        if (txq->amsdu_len <= amsdu_len)
+            continue;
+
+        if (txq->last_retry_skb)
+            skb = txq->last_retry_skb->next;
+        else
+            skb = txq->sk_list.next;
+
+        skb_queue_walk_from_safe(&txq->sk_list, skb, skb_next) {
+            struct ecrnx_txhdr *txhdr = (struct ecrnx_txhdr *)skb->data;
+            struct ecrnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+            if ((sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU) &&
+                (sw_txhdr->amsdu.len > amsdu_len))
+                ecrnx_amsdu_dismantle(ecrnx_hw, sw_txhdr);
+
+            if (txq->amsdu == sw_txhdr)
+                txq->amsdu = NULL;
+        }
+
+        txq->amsdu_len = amsdu_len;
+    }
+}
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+/**
+ * netdev_tx_t (*ndo_start_xmit)(struct sk_buff *skb,
+ *                               struct net_device *dev);
+ *     Called when a packet needs to be transmitted.
+ *     Must return NETDEV_TX_OK , NETDEV_TX_BUSY.
+ *        (can also return NETDEV_TX_LOCKED if NETIF_F_LLTX)
+ *
+ *  - Initialize the desciptor for this pkt (stored in skb before data)
+ *  - Push the pkt in the corresponding Txq
+ *  - If possible (i.e. credit available and not in PS) the pkt is pushed
+ *    to fw
+ */
+netdev_tx_t ecrnx_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+    struct ecrnx_vif *ecrnx_vif = netdev_priv(dev);
+    struct ecrnx_hw *ecrnx_hw = ecrnx_vif->ecrnx_hw;
+    struct ecrnx_txhdr *txhdr;
+    struct ecrnx_sw_txhdr *sw_txhdr = NULL;
+    struct ethhdr *eth;
+    struct txdesc_api *desc;
+    struct ecrnx_sta *sta;
+    struct ecrnx_txq *txq;
+    int headroom = 0, hdr_pads = 0;
+       u16 frame_len;
+    u64_l skb_addr;
+
+    u8 tid;
+
+    sk_pacing_shift_update(skb->sk, ecrnx_hw->tcp_pacing_shift);
+
+    /* check whether the current skb can be used */
+    if (skb_shared(skb) || (skb_headroom(skb) < ECRNX_TX_MAX_HEADROOM) ||
+        (skb_cloned(skb) && (dev->priv_flags & IFF_BRIDGE_PORT))) {
+        struct sk_buff *newskb = skb_copy_expand(skb, ECRNX_TX_MAX_HEADROOM, 0, GFP_ATOMIC);
+        if (unlikely(newskb == NULL))
+            goto free;
+
+        dev_kfree_skb_any(skb);
+
+        skb = newskb;
+    }
+
+    /* Get the STA id and TID information */
+    sta = ecrnx_get_tx_info(ecrnx_vif, skb, &tid);
+    if (!sta)
+        goto free;
+
+    txq = ecrnx_txq_sta_get(sta, tid, ecrnx_hw);
+    ECRNX_DBG("%s-%d:sta:0x%p,sta_idx:%d, sta_mac:%pM, tid:%d, ecrnx_hw:0x%p, txq:0x%p \n", __func__, __LINE__, sta, sta->sta_idx, sta->mac_addr, tid, ecrnx_hw, txq);
+    if (txq->idx == TXQ_INACTIVE)
+        goto free;
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    if (ecrnx_amsdu_add_subframe(ecrnx_hw, skb, sta, txq))
+        return NETDEV_TX_OK;
+#endif
+
+    sw_txhdr = kmem_cache_alloc(ecrnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+    if (unlikely(sw_txhdr == NULL))
+        goto free;
+
+    /* Retrieve the pointer to the Ethernet data */
+    eth = (struct ethhdr *)skb->data;
+
+       
+#if 0
+    if ((skb->data[0] & 0x1) && (skb->data[0] != 0xff)) {
+        printk("drop mc pkt 0x%x\n", skb->data[0]);
+        goto free;
+    }
+#endif
+
+#if 1
+    if (0xDD86 == eth->h_proto) { //ipv6
+               //printk("%s-%d: eapol\n", __func__, __LINE__);
+               goto free;
+               //dump_xxx_buf(skb->data, skb->len);
+       }
+#endif
+#if 0
+    if (0x8e88 == eth->h_proto) { //icmp
+               printk("%s-%d: eapol\n", __func__, __LINE__);
+               //dump_xxx_buf(skb->data, skb->len);
+       }
+#endif
+
+#if 0
+
+       if (8 == eth->h_proto && 0x1 == skb->data[23]) { //icmp
+               memset(skb->data + 14, 0xff, skb->len - 14);
+       }
+       if (8 == eth->h_proto && 0x11 == skb->data[23]) {
+               printk("---drop udp pkt\n");
+               goto free;
+       }
+#endif 
+    //no_encrypt = check_eapol_dont_encrypt(skb);
+
+    hdr_pads  = ECRNX_SWTXHDR_ALIGN_PADS((long)eth);
+    /* Use headroom to store struct ecrnx_txhdr */
+    headroom = ECRNX_TX_HEADROOM(skb);
+
+    txhdr = (struct ecrnx_txhdr *)skb_push(skb, headroom);
+    txhdr->sw_hdr = sw_txhdr;
+    frame_len = (u16)skb->len - headroom - sizeof(*eth);
+
+    sw_txhdr->txq       = txq;
+    sw_txhdr->frame_len = frame_len;
+    sw_txhdr->ecrnx_sta  = sta;
+    sw_txhdr->ecrnx_vif  = ecrnx_vif;
+    sw_txhdr->skb       = skb;
+    sw_txhdr->headroom  = headroom;
+#ifdef CONFIG_ECRNX_ESWIN
+    sw_txhdr->offset = headroom + sizeof(*eth);
+#else
+    sw_txhdr->map_len   = skb->len - offsetof(struct ecrnx_txhdr, hw_hdr);
+#endif
+    sw_txhdr->jiffies   = jiffies;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    sw_txhdr->amsdu.len = 0;
+    sw_txhdr->amsdu.nb = 0;
+#endif
+    // Fill-in the descriptor
+    desc = &sw_txhdr->desc;
+    memcpy(&desc->host.eth_dest_addr, eth->h_dest, ETH_ALEN);
+    memcpy(&desc->host.eth_src_addr, eth->h_source, ETH_ALEN);
+    desc->host.ethertype = eth->h_proto;
+    desc->host.staid = sta->sta_idx;
+    desc->host.tid = tid;
+    if (unlikely(ecrnx_vif->wdev.iftype == NL80211_IFTYPE_AP_VLAN))
+        desc->host.vif_idx = ecrnx_vif->ap_vlan.master->vif_index;
+    else
+        desc->host.vif_idx = ecrnx_vif->vif_index;
+    desc->host.flags = 0;
+
+    if (ecrnx_vif->use_4addr && (sta->sta_idx < NX_REMOTE_STA_MAX))
+        desc->host.flags |= TXU_CNTRL_USE_4ADDR;
+
+    if ((ecrnx_vif->tdls_status == TDLS_LINK_ACTIVE) &&
+        ecrnx_vif->sta.tdls_sta &&
+        (memcmp(desc->host.eth_dest_addr.array, ecrnx_vif->sta.tdls_sta->mac_addr, ETH_ALEN) == 0)) {
+        desc->host.flags |= TXU_CNTRL_TDLS;
+        ecrnx_vif->sta.tdls_sta->tdls.last_tid = desc->host.tid;
+        ecrnx_vif->sta.tdls_sta->tdls.last_sn = desc->host.sn;
+    }
+
+    if ((ecrnx_vif->wdev.iftype == NL80211_IFTYPE_MESH_POINT) &&
+        (ecrnx_vif->is_resending))
+            desc->host.flags |= TXU_CNTRL_MESH_FWD;
+
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+    desc->host.packet_len[0] = frame_len;
+#else
+    desc->host.packet_len = frame_len;
+#endif
+
+    txhdr->hw_hdr.cfm.status.value = 0;
+
+#ifdef CONFIG_ECRNX_ESWIN
+    skb_addr = (ptr_addr)skb;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+    desc->host.packet_addr[0] = (u64_l)skb;
+    desc->host.packet_cnt = 1;
+#else
+    //desc->host.packet_addr = (u64_l)skb;
+    desc->host.packet_addr[0] = (u32_l)skb_addr;
+    desc->host.packet_addr[1] = (u32_l)(skb_addr >> 32);
+#endif
+    //desc->host.status_desc_addr = (u64_l)skb;
+    desc->host.status_desc_addr[0] = (u32_l)skb_addr;
+    desc->host.status_desc_addr[1] = (u32_l)(skb_addr >> 32);
+
+    /*if (no_encrypt) {
+        desc->host.flags |= TXU_CNTRL_NO_ENCRYPT;
+    }*/
+#else //CONFIG_ECRNX_ESWIN_SDIO
+    if (unlikely(ecrnx_prep_dma_tx(ecrnx_hw, txhdr, true)))
+        goto free;
+#endif //CONFIG_ECRNX_ESWIN_SDIO
+    //ECRNX_DBG("%s:desc:0x%08x, vif_idx:%d, skb:0x%08x, headroom:%d !!! \n", __func__, desc, desc->host.vif_idx, skb, headroom);
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+    if (ecrnx_txq_queue_skb(skb, txq, ecrnx_hw, false, NULL))
+    {
+        ECRNX_DBG("%s-%d:txdesc:0x%x, skb:0x%08x, skb->len:%d \n", __func__, __LINE__, desc, skb, skb->len);
+        ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+    }
+    else
+    {
+        ECRNX_DBG("%s-%d: delay send(put txq), txq:0x%p, queue status 0x%x, skb:0x%08x, skb->len:%d !!! \n", __func__, __LINE__, txq, txq->status, skb, skb->len);
+    }
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+
+    return NETDEV_TX_OK;
+
+free:
+    if (sw_txhdr)
+        kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+    if (headroom)
+        skb_pull(skb, headroom);
+    dev_kfree_skb_any(skb);
+
+    return NETDEV_TX_OK;
+}
+
+/**
+ * ecrnx_start_mgmt_xmit - Transmit a management frame
+ *
+ * @vif: Vif that send the frame
+ * @sta: Destination of the frame. May be NULL if the destiantion is unknown
+ *       to the AP.
+ * @params: Mgmt frame parameters
+ * @offchan: Indicate whether the frame must be send via the offchan TXQ.
+ *           (is is redundant with params->offchan ?)
+ * @cookie: updated with a unique value to identify the frame with upper layer
+ *
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, struct ecrnx_sta *sta,
+                         struct cfg80211_mgmt_tx_params *params, bool offchan,
+                         u64 *cookie)
+#else
+int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, struct ecrnx_sta *sta,
+                         struct ieee80211_channel *channel, bool offchan,
+                         unsigned int wait, const u8* buf, size_t len,
+                    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+                         bool no_cck,
+                    #endif
+                    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+                         bool dont_wait_for_ack,
+                    #endif
+                         u64 *cookie)
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+{
+    struct ecrnx_hw *ecrnx_hw = vif->ecrnx_hw;
+    struct ecrnx_txhdr *txhdr;
+    struct ecrnx_sw_txhdr *sw_txhdr;
+    struct txdesc_api *desc;
+    struct sk_buff *skb;
+    u16 frame_len, headroom;
+    u8 *data;
+    struct ecrnx_txq *txq;
+    bool robust;
+    u64_l skb_addr;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+    const u8 *buf = params->buf;
+    size_t len = params->len;
+    bool no_cck = params->no_cck;
+#endif
+
+
+#ifdef CONFIG_ECRNX_ESWIN
+    headroom = sizeof(struct ecrnx_txhdr) + ECRNX_TX_TXDESC_API_ALIGN;
+#else
+    headroom = sizeof(struct ecrnx_txhdr);
+#endif
+    frame_len = len;
+
+    //----------------------------------------------------------------------
+
+    /* Set TID and Queues indexes */
+    if (sta) {
+        txq = ecrnx_txq_sta_get(sta, 8, ecrnx_hw);
+    } else {
+        if (offchan)
+            txq = &ecrnx_hw->txq[NX_OFF_CHAN_TXQ_IDX];
+        else
+            txq = ecrnx_txq_vif_get(vif, NX_UNK_TXQ_TYPE);
+    }
+
+    /* Ensure that TXQ is active */
+    if (txq->idx == TXQ_INACTIVE) {
+        if(sta){
+            netdev_dbg(vif->ndev, "TXQ inactive\n");
+            return -EBUSY;
+        }else{
+            return 0;
+        }
+    }
+
+    /*
+     * Create a SK Buff object that will contain the provided data
+     */
+    skb = dev_alloc_skb(headroom + frame_len);
+
+    if (!skb) {
+        return -ENOMEM;
+    }
+
+    *cookie = (unsigned long)skb;
+
+    sw_txhdr = kmem_cache_alloc(ecrnx_hw->sw_txhdr_cache, GFP_ATOMIC);
+    if (unlikely(sw_txhdr == NULL)) {
+        dev_kfree_skb(skb);
+        return -ENOMEM;
+    }
+    /*
+     * Move skb->data pointer in order to reserve room for ecrnx_txhdr
+     * headroom value will be equal to sizeof(struct ecrnx_txhdr)
+     */
+    skb_reserve(skb, headroom);
+
+    /*
+     * Extend the buffer data area in order to contain the provided packet
+     * len value (for skb) will be equal to param->len
+     */
+    data = skb_put(skb, frame_len);
+    /* Copy the provided data */
+    memcpy(data, buf, frame_len);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
+    robust = ieee80211_is_robust_mgmt_frame(skb);
+#else
+    if (skb->len < 25){
+        robust = false;
+    }
+    robust = ieee80211_is_robust_mgmt_frame((void *)skb->data);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) */
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
+    /* Update CSA counter if present */
+    if (unlikely(params->n_csa_offsets) &&
+        vif->wdev.iftype == NL80211_IFTYPE_AP &&
+        vif->ap.csa) {
+        int i;
+
+        data = skb->data;
+        for (i = 0; i < params->n_csa_offsets ; i++) {
+            data[params->csa_offsets[i]] = vif->ap.csa->count;
+        }
+    }
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) */
+
+    /*
+     * Go back to the beginning of the allocated data area
+     * skb->data pointer will move backward
+     */
+    txhdr = (struct ecrnx_txhdr *)skb_push(skb, headroom);
+
+    //----------------------------------------------------------------------
+
+    /* Fill the TX Header */
+
+
+    //----------------------------------------------------------------------
+
+    /* Fill the SW TX Header */
+    txhdr->sw_hdr = sw_txhdr;
+
+    sw_txhdr->txq = txq;
+    sw_txhdr->frame_len = frame_len;
+    sw_txhdr->ecrnx_sta = sta;
+    sw_txhdr->ecrnx_vif = vif;
+    sw_txhdr->skb = skb;
+    sw_txhdr->headroom = headroom;
+#ifdef CONFIG_ECRNX_ESWIN
+    sw_txhdr->offset = headroom; //sizeof(struct ecrnx_txhdr) + sizeof(struct txdesc_api);
+#else
+    sw_txhdr->map_len = skb->len - offsetof(struct ecrnx_txhdr, hw_hdr);
+#endif
+       sw_txhdr->jiffies = jiffies;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    sw_txhdr->amsdu.len = 0;
+    sw_txhdr->amsdu.nb = 0;
+#endif
+    //----------------------------------------------------------------------
+
+    /* Fill the Descriptor to be provided to the MAC SW */
+    desc = &sw_txhdr->desc;
+
+    desc->host.staid = (sta) ? sta->sta_idx : 0xFF;
+    desc->host.vif_idx = vif->vif_index;
+    desc->host.tid = 0xFF;
+    desc->host.flags = TXU_CNTRL_MGMT;
+    if (robust)
+        desc->host.flags |= TXU_CNTRL_MGMT_ROBUST;
+
+
+    if (no_cck) {
+        desc->host.flags |= TXU_CNTRL_MGMT_NO_CCK;
+    }
+    /* baoyong */
+#ifdef CONFIG_RWNX_SPLIT_TX_BUF
+    desc->host.packet_len[0] = frame_len;
+#else
+    desc->host.packet_len = frame_len;
+#endif
+
+    txhdr->hw_hdr.cfm.status.value = 0;
+#ifdef CONFIG_ECRNX_ESWIN
+    skb_addr = (ptr_addr)skb;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+    desc->host.packet_addr[0] = skb;
+    desc->host.packet_cnt = 1;
+#else
+    //desc->host.packet_addr = (u64_l)skb;
+    desc->host.packet_addr[0] = (u32_l)skb_addr;
+    desc->host.packet_addr[1] = (u32_l)(skb_addr >> 32);
+#endif
+    //desc->host.status_desc_addr = (u64_l)skb;
+    desc->host.packet_addr[0] = (u32_l)skb_addr;
+    desc->host.packet_addr[1] = (u32_l)(skb_addr >> 32);
+#else //CONFIG_ECRNX_ESWIN_SDIO
+
+    /* Get DMA Address */
+    if (unlikely(ecrnx_prep_dma_tx(ecrnx_hw, txhdr, false))) {
+        kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+        dev_kfree_skb(skb);
+        return -EBUSY;
+    }
+#endif
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+#endif
+
+    //----------------------------------------------------------------------
+
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+    if (ecrnx_txq_queue_skb(skb, txq, ecrnx_hw, false, NULL)) {
+        ECRNX_DBG("%s-%d:txdesc:0x%x, skb:0x%08x, skb->len:%d \n", __func__, __LINE__, desc, skb, skb->len);
+        ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+    } else {
+        ECRNX_DBG("%s-%d: delay send(put txq), queue status 0x%x, skb:0x%08x, skb->len:%d !!! \n", __func__, __LINE__, txq->status, skb, skb->len);
+    }
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+
+    return 0;
+}
+
+int ecrnx_handle_tx_datacfm(void *priv, void *host_id)
+{
+    struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)priv;
+    struct sk_buff *skb = host_id;
+    struct ecrnx_hwq *hwq;
+    struct ecrnx_txq *txq;
+    struct ecrnx_sta *sta;
+    struct ecrnx_txhdr *txhdr;
+
+#if defined(CONFIG_ECRNX_ESWIN_USB)
+        txhdr = (struct ecrnx_txhdr *)(*((ptr_addr*)skb->data - 1));
+#else
+        txhdr = (struct ecrnx_txhdr *)skb->data;
+#endif
+
+    struct ecrnx_sw_txhdr *sw_txhdr = txhdr->sw_hdr;
+    /* Check status in the header. If status is null, it means that the buffer
+    * was not transmitted and we have to return immediately */
+    ECRNX_DBG("%s:hostid(tx_skb):0x%08x\n", __func__, skb);
+
+    txq = sw_txhdr->txq;
+    /* don't use txq->hwq as it may have changed between push and confirm */
+    hwq = &ecrnx_hw->hwq[sw_txhdr->hw_queue];
+    ecrnx_txq_confirm_any(ecrnx_hw, txq, hwq, sw_txhdr);
+
+    if (txq->idx != TXQ_INACTIVE) {
+
+        txq->credits += 1;
+        //printk("finish_cfm: txq->credits %d 0x%08x\n", txq->credits,skb);
+        if (txq->credits <= 0){
+            ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_FULL);
+        }
+        else if (txq->credits > 0)
+        {
+            ecrnx_txq_start(txq, ECRNX_TXQ_STOP_FULL);
+            /* baoyong:handle the pkts in sk_list right now */
+            if (txq->idx != TXQ_INACTIVE && !skb_queue_empty(&txq->sk_list))
+            {
+                ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+            }
+        }
+
+        /* continue service period */
+        if (unlikely(txq->push_limit && !ecrnx_txq_is_full(txq))) {
+            ecrnx_txq_add_to_hw_list(txq);
+        }
+    }
+
+    /* Update statistics */
+    sw_txhdr->ecrnx_vif->net_stats.tx_packets++;
+    sw_txhdr->ecrnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
+
+    sta = txq->sta;
+    if(sta)
+    {
+        sta = txq->sta;
+        sta->stats.tx_pkts ++;
+        sta->stats.tx_bytes += sw_txhdr->frame_len;
+        sta->stats.last_act = ecrnx_hw->stats.last_tx;
+    }
+    //printk("sta->stats.tx_pkts=%d sta->stats.tx_bytes =%d\n", sta->stats.tx_pkts, sta->stats.tx_bytes);
+
+    kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+    skb_pull(skb, sw_txhdr->headroom);
+    consume_skb(skb);
+    return 0;
+}
+
+/**
+ * ecrnx_txdatacfm - FW callback for TX confirmation
+ *
+ * called with tx_lock hold
+ */
+int ecrnx_txdatacfm(void *pthis, void *host_id)
+{
+    struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)pthis;
+    struct sk_buff *skb = host_id;
+    struct ecrnx_txhdr *txhdr;
+    union ecrnx_hw_txstatus ecrnx_txst;
+    struct ecrnx_sw_txhdr *sw_txhdr;
+    struct ecrnx_hwq *hwq;
+    struct ecrnx_txq *txq;
+#ifndef CONFIG_ECRNX_ESWIN
+    dma_addr_t cfm_dma_addr;
+#endif
+    size_t cfm_len;
+
+#if defined(CONFIG_ECRNX_ESWIN_USB)
+    txhdr = (struct ecrnx_txhdr *)host_id;
+    skb = txhdr->sw_hdr->skb;
+    skb_push(skb, sizeof(struct ecrnx_txhdr) - sizeof(u32_l));
+#else
+    txhdr = (struct ecrnx_txhdr *)skb->data;
+#endif
+    sw_txhdr = txhdr->sw_hdr;
+    cfm_len = sizeof(txhdr->hw_hdr.cfm);
+
+       //ECRNX_DBG("%s-%d: skb:0x%08x, skb->len:%d \n", __func__, __LINE__, skb, skb->len);
+#ifndef CONFIG_ECRNX_ESWIN
+       cfm_dma_addr = (ptr_addr)sw_txhdr->desc.host.status_desc_addr;
+    dma_sync_single_for_cpu(ecrnx_hw->dev, cfm_dma_addr, cfm_len, DMA_FROM_DEVICE);
+#endif
+    /* Read status in the TX control header */
+    ecrnx_txst = txhdr->hw_hdr.cfm.status;
+
+    /* Check status in the header. If status is null, it means that the buffer
+     * was not transmitted and we have to return immediately */
+    if (ecrnx_txst.value == 0) {
+#ifndef CONFIG_ECRNX_ESWIN
+        dma_sync_single_for_device(ecrnx_hw->dev, cfm_dma_addr, cfm_len, DMA_FROM_DEVICE);
+#endif
+        return -1;
+    }
+
+    txq = sw_txhdr->txq;
+    /* don't use txq->hwq as it may have changed between push and confirm */
+    hwq = &ecrnx_hw->hwq[sw_txhdr->hw_queue];
+    ecrnx_txq_confirm_any(ecrnx_hw, txq, hwq, sw_txhdr);
+
+    /* Update txq and HW queue credits */
+    if (sw_txhdr->desc.host.flags & TXU_CNTRL_MGMT) {
+        trace_mgmt_cfm(sw_txhdr->ecrnx_vif->vif_index,
+                       (sw_txhdr->ecrnx_sta) ? sw_txhdr->ecrnx_sta->sta_idx : 0xFF,
+                       ecrnx_txst.acknowledged);
+
+        /* Confirm transmission to CFG80211 */
+        cfg80211_mgmt_tx_status(&sw_txhdr->ecrnx_vif->wdev,
+                                (unsigned long)skb,
+                                (skb->data + sw_txhdr->headroom),
+                                sw_txhdr->frame_len,
+                                ecrnx_txst.acknowledged,
+                                GFP_ATOMIC);
+    } else if ((txq->idx != TXQ_INACTIVE) &&
+               (ecrnx_txst.retry_required || ecrnx_txst.sw_retry_required)) {
+        bool sw_retry = (ecrnx_txst.sw_retry_required) ? true : false;
+
+        /* Reset the status */
+        txhdr->hw_hdr.cfm.status.value = 0;
+
+        /* The confirmed packet was part of an AMPDU and not acked
+         * correctly, so reinject it in the TX path to be retried */
+        ecrnx_tx_retry(ecrnx_hw, skb, txhdr, sw_retry);
+        return 0;
+    }
+
+    trace_skb_confirm(skb, txq, hwq, &txhdr->hw_hdr.cfm);
+
+    /* STA may have disconnect (and txq stopped) when buffers were stored
+       in fw. In this case do nothing when they're returned */
+    if (txq->idx != TXQ_INACTIVE) {
+        if (txhdr->hw_hdr.cfm.credits) {
+            txq->credits += txhdr->hw_hdr.cfm.credits;
+            if (txq->credits <= 0)
+                ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_FULL);
+            else if (txq->credits > 0)
+            {
+                ecrnx_txq_start(txq, ECRNX_TXQ_STOP_FULL);
+                /* baoyong:handle the pkts in sk_list right now */
+                if (txq->idx != TXQ_INACTIVE && !skb_queue_empty(&txq->sk_list))
+                {
+                    ecrnx_hwq_process(ecrnx_hw, txq->hwq);
+                }
+            
+            }
+        }
+
+        /* continue service period */
+        if (unlikely(txq->push_limit && !ecrnx_txq_is_full(txq))) {
+            ecrnx_txq_add_to_hw_list(txq);
+        }
+    }
+
+    if (txhdr->hw_hdr.cfm.ampdu_size &&
+        txhdr->hw_hdr.cfm.ampdu_size < IEEE80211_MAX_AMPDU_BUF)
+        ecrnx_hw->stats.ampdus_tx[txhdr->hw_hdr.cfm.ampdu_size - 1]++;
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    ecrnx_amsdu_update_len(ecrnx_hw, txq, txhdr->hw_hdr.cfm.amsdu_size);
+#endif
+
+    /* Update statistics */
+    sw_txhdr->ecrnx_vif->net_stats.tx_packets++;
+    sw_txhdr->ecrnx_vif->net_stats.tx_bytes += sw_txhdr->frame_len;
+
+    /* Release SKBs */
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    if (sw_txhdr->desc.host.flags & TXU_CNTRL_AMSDU) {
+        struct ecrnx_amsdu_txhdr *amsdu_txhdr;
+        list_for_each_entry(amsdu_txhdr, &sw_txhdr->amsdu.hdrs, list) {
+            ecrnx_amsdu_del_subframe_header(amsdu_txhdr);
+#ifndef CONFIG_ECRNX_ESWIN
+            dma_unmap_single(ecrnx_hw->dev, amsdu_txhdr->dma_addr,
+                             amsdu_txhdr->map_len, DMA_TO_DEVICE);
+#endif
+             ecrnx_ipc_sta_buffer(ecrnx_hw, txq->sta, txq->tid,
+                                 -amsdu_txhdr->msdu_len);
+            ecrnx_tx_statistic(ecrnx_hw, txq, ecrnx_txst, amsdu_txhdr->msdu_len);
+            consume_skb(amsdu_txhdr->skb);
+        }
+    }
+#endif /* CONFIG_ECRNX_AMSDUS_TX */
+
+#ifndef CONFIG_ECRNX_ESWIN
+    /* unmap with the least costly DMA_TO_DEVICE since we don't need to inval */
+    dma_unmap_single(ecrnx_hw->dev, sw_txhdr->dma_addr, sw_txhdr->map_len,
+                     DMA_TO_DEVICE);
+#endif
+    ecrnx_ipc_sta_buffer(ecrnx_hw, txq->sta, txq->tid, -sw_txhdr->frame_len);
+    ecrnx_tx_statistic(ecrnx_hw, txq, ecrnx_txst, sw_txhdr->frame_len);
+
+    kmem_cache_free(ecrnx_hw->sw_txhdr_cache, sw_txhdr);
+    skb_pull(skb, sw_txhdr->headroom);
+    consume_skb(skb);
+
+    return 0;
+}
+
+/**
+ * ecrnx_txq_credit_update - Update credit for one txq
+ *
+ * @ecrnx_hw: Driver main data
+ * @sta_idx: STA idx
+ * @tid: TID
+ * @update: offset to apply in txq credits
+ *
+ * Called when fw send ME_TX_CREDITS_UPDATE_IND message.
+ * Apply @update to txq credits, and stop/start the txq if needed
+ */
+void ecrnx_txq_credit_update(struct ecrnx_hw *ecrnx_hw, int sta_idx, u8 tid, s8 update)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+    struct ecrnx_sta *sta = &ecrnx_hw->sta_table[sta_idx];
+    struct ecrnx_txq *txq;
+
+    txq = ecrnx_txq_sta_get(sta, tid, ecrnx_hw);
+
+    spin_lock_bh(&ecrnx_hw->tx_lock);
+
+    if (txq->idx != TXQ_INACTIVE) {
+        txq->credits += update;
+        trace_credit_update(txq, update);
+        if (txq->credits <= 0){
+            ECRNX_DBG("%s-%d:ecrnx_txq_stop,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_FULL);
+            ecrnx_txq_stop(txq, ECRNX_TXQ_STOP_FULL);
+        }
+        else{
+            ecrnx_txq_start(txq, ECRNX_TXQ_STOP_FULL);
+            ECRNX_DBG("%s-%d:ecrnx_txq_start,reaosn:0x%x \n", __func__, __LINE__, ECRNX_TXQ_STOP_FULL);
+        }
+    }
+
+// Drop all the retry packets of a BA that was deleted
+    if (update < NX_TXQ_INITIAL_CREDITS) {
+        int packet;
+
+        for (packet = 0; packet < txq->nb_retry; packet++) {
+            ecrnx_txq_drop_skb(txq, skb_peek(&txq->sk_list), ecrnx_hw, true);
+        }
+    }
+
+    spin_unlock_bh(&ecrnx_hw->tx_lock);
+#endif
+}
+
+
+#ifdef CONFIG_ECRNX_ESWIN_SDIO
+void ecrnx_tx_retry_sdio(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+                           struct ecrnx_txhdr *txhdr, bool sw_retry)
+{
+    ecrnx_tx_retry(ecrnx_hw, skb, txhdr, sw_retry);
+}
+
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+void ecrnx_amsdu_del_subframe_header_sdio(struct ecrnx_amsdu_txhdr *amsdu_txhdr)
+{
+    ecrnx_amsdu_del_subframe_header(amsdu_txhdr);
+}
+#endif
+#endif
+
+#ifdef CONFIG_ECRNX_ESWIN_USB
+void ecrnx_tx_retry_usb(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb,
+                           struct ecrnx_txhdr *txhdr, bool sw_retry)
+{
+    ecrnx_tx_retry(ecrnx_hw, skb, txhdr, sw_retry);
+}
+
+
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+void ecrnx_amsdu_del_subframe_header_sdio(struct ecrnx_amsdu_txhdr *amsdu_txhdr)
+{
+    ecrnx_amsdu_del_subframe_header(amsdu_txhdr);
+}
+#endif
+#endif
+
diff --git a/drivers/net/wireless/eswin/fullmac/ecrnx_tx.h b/drivers/net/wireless/eswin/fullmac/ecrnx_tx.h
new file mode 100644 (file)
index 0000000..03d1177
--- /dev/null
@@ -0,0 +1,285 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_tx.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _ECRNX_TX_H_
+#define _ECRNX_TX_H_
+
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <linux/netdevice.h>
+#include "lmac_types.h"
+#include "ipc_shared.h"
+#include "ecrnx_txq.h"
+#include "hal_desc.h"
+
+#define ECRNX_HWQ_BK                     0
+#define ECRNX_HWQ_BE                     1
+#define ECRNX_HWQ_VI                     2
+#define ECRNX_HWQ_VO                     3
+#define ECRNX_HWQ_BCMC                   4
+#define ECRNX_HWQ_NB                     NX_TXQ_CNT
+#define ECRNX_HWQ_ALL_ACS (ECRNX_HWQ_BK | ECRNX_HWQ_BE | ECRNX_HWQ_VI | ECRNX_HWQ_VO)
+#define ECRNX_HWQ_ALL_ACS_BIT ( BIT(ECRNX_HWQ_BK) | BIT(ECRNX_HWQ_BE) |    \
+                               BIT(ECRNX_HWQ_VI) | BIT(ECRNX_HWQ_VO) )
+
+#define ECRNX_TX_LIFETIME_MS             100
+#define ECRNX_TX_MAX_RATES               NX_TX_MAX_RATES
+
+
+#define AMSDU_PADDING(x) ((4 - ((x) & 0x3)) & 0x3)
+
+#define TXU_CNTRL_RETRY        BIT(0)
+#define TXU_CNTRL_MORE_DATA    BIT(2)
+#define TXU_CNTRL_MGMT         BIT(3)
+#define TXU_CNTRL_MGMT_NO_CCK  BIT(4)
+#define TXU_CNTRL_AMSDU        BIT(6)
+#define TXU_CNTRL_MGMT_ROBUST  BIT(7)
+#define TXU_CNTRL_USE_4ADDR    BIT(8)
+#define TXU_CNTRL_EOSP         BIT(9)
+#define TXU_CNTRL_MESH_FWD     BIT(10)
+#define TXU_CNTRL_TDLS         BIT(11)
+#define TXU_CNTRL_NO_ENCRYPT   BIT(15)
+
+extern const int ecrnx_tid2hwq[IEEE80211_NUM_TIDS];
+
+/**
+ * struct ecrnx_amsdu_txhdr - Structure added in skb headroom (instead of
+ * ecrnx_txhdr) for amsdu subframe buffer (except for the first subframe
+ * that has a normal ecrnx_txhdr)
+ *
+ * @list     List of other amsdu subframe (ecrnx_sw_txhdr.amsdu.hdrs)
+ * @map_len  Length to be downloaded for this subframe
+ * @dma_addr Buffer address form embedded point of view
+ * @skb      skb
+ * @pad      padding added before this subframe
+ *           (only use when amsdu must be dismantled)
+ * @msdu_len Size, in bytes, of the MSDU (without padding nor amsdu header)
+ */
+struct ecrnx_amsdu_txhdr {
+    struct list_head list;
+    size_t map_len;
+#ifdef CONFIG_ECRNX_ESWIN
+    u8 *send_pos; // offset from skb->data for send to slave.
+#else    
+    dma_addr_t dma_addr;
+#endif
+    struct sk_buff *skb;
+    u16 pad;
+    u16 msdu_len;
+};
+
+/**
+ * struct ecrnx_amsdu - Structure to manage creation of an A-MSDU, updated
+ * only In the first subframe of an A-MSDU
+ *
+ * @hdrs List of subframe of ecrnx_amsdu_txhdr
+ * @len  Current size for this A-MDSU (doesn't take padding into account)
+ *       0 means that no amsdu is in progress
+ * @nb   Number of subframe in the amsdu
+ * @pad  Padding to add before adding a new subframe
+ */
+struct ecrnx_amsdu {
+    struct list_head hdrs;
+    u16 len;
+    u8 nb;
+    u8 pad;
+};
+
+/**
+ * struct ecrnx_sw_txhdr - Software part of tx header
+ *
+ * @ecrnx_sta sta to which this buffer is addressed
+ * @ecrnx_vif vif that send the buffer
+ * @txq pointer to TXQ used to send the buffer
+ * @hw_queue Index of the HWQ used to push the buffer.
+ *           May be different than txq->hwq->id on confirmation.
+ * @frame_len Size of the frame (doesn't not include mac header)
+ *            (Only used to update stat, can't we use skb->len instead ?)
+ * @headroom Headroom added in skb to add ecrnx_txhdr
+ *           (Only used to remove it before freeing skb, is it needed ?)
+ * @amsdu Description of amsdu whose first subframe is this buffer
+ *        (amsdu.nb = 0 means this buffer is not part of amsdu)
+ * @skb skb received from transmission
+ * @map_len  Length mapped for DMA (only ecrnx_hw_txhdr and data are mapped)
+ * @dma_addr DMA address after mapping
+ * @desc Buffer description that will be copied in shared mem for FW
+ */
+struct ecrnx_sw_txhdr {
+    struct ecrnx_sta *ecrnx_sta;
+    struct ecrnx_vif *ecrnx_vif;
+    struct ecrnx_txq *txq;
+    u8 hw_queue;
+    u16 frame_len;
+    u16 headroom;
+#ifdef CONFIG_ECRNX_AMSDUS_TX
+    struct ecrnx_amsdu amsdu;
+#endif
+    struct sk_buff *skb;
+
+#ifdef CONFIG_ECRNX_ESWIN
+    u32 offset; // offset from skb->data for send to slave.
+#else
+    size_t map_len;
+    dma_addr_t dma_addr;
+#endif
+    struct txdesc_api desc;
+    unsigned long jiffies;
+};
+
+/**
+ * struct ecrnx_txhdr - Stucture to control transimission of packet
+ * (Added in skb headroom)
+ *
+ * @sw_hdr: Information from driver
+ * @cache_guard:
+ * @hw_hdr: Information for/from hardware
+ */
+struct ecrnx_txhdr {
+    struct ecrnx_sw_txhdr *sw_hdr;
+    char cache_guard[L1_CACHE_BYTES];
+    struct ecrnx_hw_txhdr hw_hdr;
+};
+#define ECRNX_TX_ALIGN_SIZE 4
+#define ECRNX_TX_ALIGN_MASK (ECRNX_TX_ALIGN_SIZE - 1)
+#define ECRNX_SWTXHDR_ALIGN_PADS(x) \
+                    ((ECRNX_TX_ALIGN_SIZE - ((x) & ECRNX_TX_ALIGN_MASK)) \
+                     & ECRNX_TX_ALIGN_MASK)
+
+#define ECRNX_TX_TXDESC_API_ALIGN  ((sizeof(struct txdesc_api) + 3) & (~ECRNX_TX_ALIGN_MASK))
+/**
+ * ECRNX_TX_MAX_HEADROOM - Maximum size needed in skb headroom to prepare a buffer
+ * for transmission
+ * The headroom is used to store the 'struct ecrnx_txhdr' and moreover the part that is used
+ * by the firmware to provide tx status (i.e. struct ecrnx_hw_txhdr) must be aligned on
+ * 32bits because firmware used DMA to update it.
+ */
+#ifdef CONFIG_ECRNX_ESWIN
+#define ECRNX_TX_MAX_HEADROOM (ECRNX_TX_TXDESC_API_ALIGN + sizeof(struct ecrnx_txhdr) + ECRNX_TX_ALIGN_SIZE)
+#else
+#define ECRNX_TX_MAX_HEADROOM (sizeof(struct ecrnx_txhdr) + ECRNX_TX_ALIGN_SIZE)
+#endif
+
+/**
+ * ECRNX_TX_HEADROOM - Headroom to use to store struct ecrnx_txhdr
+ *
+ * Takes into account current aligment of data buffer to ensure that struct ecrnx_txhdr
+ * (and as a consequence its field hw_hdr) will be aligned on 32bits boundary.
+ */
+#ifdef CONFIG_ECRNX_ESWIN
+#define ECRNX_TX_HEADROOM(skb) (ECRNX_TX_TXDESC_API_ALIGN + sizeof(struct ecrnx_txhdr) + ((long)skb->data & ECRNX_TX_ALIGN_MASK))
+#else
+#define ECRNX_TX_HEADROOM(skb) (sizeof(struct ecrnx_txhdr) + ((long)skb->data & ECRNX_TX_ALIGN_MASK))
+#endif
+
+/**
+ * ECRNX_TX_DMA_MAP_LEN - Length, in bytes, to map for DMA transfer
+ * To be called with skb BEFORE reserving headroom to store struct ecrnx_txhdr.
+ */
+#define ECRNX_TX_DMA_MAP_LEN(skb) (                                      \
+        (sizeof(struct ecrnx_txhdr) - offsetof(struct ecrnx_txhdr, hw_hdr)) + \
+        ((long)skb->data & ECRNX_TX_ALIGN_MASK) + skb->len)
+
+/**
+ * ECRNX_TX_DATA_OFT - Offset, in bytes, between the location of the 'struct ecrnx_hw_txhdr'
+ * and the beginning of frame (i.e. ethernet of 802.11 header). Cannot simply use
+ * sizeof(struct ecrnx_hw_txhdr) because of padding added by compiler to align fields
+ * in structure.
+ */
+#define ECRNX_TX_DATA_OFT(sw_txhdr) (                                    \
+        (sizeof(struct ecrnx_txhdr) - offsetof(struct ecrnx_txhdr, hw_hdr)) \
+        + (sw_txhdr->headroom & ECRNX_TX_ALIGN_MASK))
+
+/**
+ * SKB buffer format before it is pushed to MACSW
+ *
+ * For DATA frame
+ *                    |--------------------|
+ *                    | headroom           |
+ *    skb->data ----> |--------------------| <------ skb->data
+ *                    | struct ecrnx_txhdr  |
+ *                    | * ecrnx_sw_txhdr *  |
+ *                    | * [L1 guard]       |
+ *               +--> | * ecrnx_hw_txhdr    | <---- desc.host.status_desc_addr
+ *               :    |                    |
+ *     memory    :    |--------------------|
+ *     mapped    :    | padding (optional) |
+ *     for DMA   :    |--------------------|
+ *               :    | Ethernet Header    |
+ *               :    |--------------------| <---- desc.host.packet_addr[0]
+ *               :    | Data               |
+ *               :    |                    |
+ *               :    |                    |
+ *               :    |                    |
+ *               +--> |--------------------|
+ *                    | tailroom           |
+ *                    |--------------------|
+ *
+ *
+ * For MGMT frame (skb is created by the driver so buffer is always aligned
+ *                 with no headroom/tailroom)
+ *
+ *    skb->data ----> |--------------------| <------ skb->data
+ *                    | struct ecrnx_txhdr  |
+ *                    | * ecrnx_sw_txhdr *  |
+ *                    | * [L1 guard]       |
+ *               +--> | * ecrnx_hw_txhdr    | <---- desc.host.status_desc_addr
+ *     memory    :    |                    |
+ *     mapped    :    |--------------------| <---- desc.host.packet_addr[0]
+ *     for DMA   :    | 802.11 HDR         |
+ *               :    |--------------------|
+ *               :    | Data               |
+ *               :    |                    |
+ *               +--> |--------------------|
+ *
+ */
+
+u16 ecrnx_select_txq(struct ecrnx_vif *ecrnx_vif, struct sk_buff *skb);
+int ecrnx_start_xmit(struct sk_buff *skb, struct net_device *dev);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, struct ecrnx_sta *sta,
+                         struct cfg80211_mgmt_tx_params *params, bool offchan,
+                         u64 *cookie);
+#else
+int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, struct ecrnx_sta *sta,
+                         struct ieee80211_channel *channel, bool offchan,
+                         unsigned int wait, const u8* buf, size_t len,
+                    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
+                         bool no_cck,
+                    #endif
+                    #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
+                         bool dont_wait_for_ack,
+                    #endif
+                         u64 *cookie);
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) */
+
+int ecrnx_txdatacfm(void *pthis, void *host_id);
+int ecrnx_handle_tx_datacfm(void *priv, void *host_id);
+
+struct ecrnx_hw;
+struct ecrnx_sta;
+void ecrnx_set_traffic_status(struct ecrnx_hw *ecrnx_hw,
+                             struct ecrnx_sta *sta,
+                             bool available,
+                             u8 ps_id);
+void ecrnx_ps_bh_enable(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+                       bool enable);
+void ecrnx_ps_bh_traffic_req(struct ecrnx_hw *ecrnx_hw, struct ecrnx_sta *sta,
+                            u16 pkt_req, u8 ps_id);
+
+void ecrnx_switch_vif_sta_txq(struct ecrnx_sta *sta, struct ecrnx_vif *old_vif,
+                             struct ecrnx_vif *new_vif);
+
+int ecrnx_dbgfs_print_sta(char *buf, size_t size, struct ecrnx_sta *sta,
+                         struct ecrnx_hw *ecrnx_hw);
+void ecrnx_txq_credit_update(struct ecrnx_hw *ecrnx_hw, int sta_idx, u8 tid,
+                            s8 update);
+void ecrnx_tx_push(struct ecrnx_hw *ecrnx_hw, struct ecrnx_txhdr *txhdr, int flags);
+
+#endif /* _ECRNX_TX_H_ */
diff --git a/drivers/net/wireless/eswin/fw_head_check.c b/drivers/net/wireless/eswin/fw_head_check.c
new file mode 100644 (file)
index 0000000..739b0a1
--- /dev/null
@@ -0,0 +1,258 @@
+/**
+******************************************************************************
+*
+* @file fw_head_check.c
+*
+* @brief ecrnx usb firmware validity check functions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#include <linux/firmware.h>
+#include <linux/version.h>
+#include "core.h"
+#include "fw_head_check.h"
+#include "ecrnx_defs.h"
+
+extern char *fw_name;
+
+#define MSB_MODE  1
+#define LSB_MODE  0
+#define DSE_FIRST       (2039)
+#define SECONDS_PER_DAY (86400)
+
+bin_head_data head = {0};
+unsigned int offset = 0;
+
+static const crc32_Table_TypeDef CRC32_Table[]={
+    {0xFFFFFFFF, 0x04C11DB7, 0xFFFFFFFF, LSB_MODE, "CRC32"},
+    {0xFFFFFFFF, 0x04C11DB7, 0x00000000, MSB_MODE, "CRC32_MPEG-2"}
+};
+
+static const u_int16_t days_since_year[] = {
+    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+};
+
+static const u_int16_t days_since_leapyear[] = {
+    0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
+};
+
+static const u_int16_t days_since_epoch[] = {
+    /* 2039 - 2030 */
+    25202, 24837, 24472, 24106, 23741, 23376, 23011, 22645, 22280, 21915,
+    /* 2029 - 2020 */
+    21550, 21184, 20819, 20454, 20089, 19723, 19358, 18993, 18628, 18262,
+    /* 2019 - 2010 */
+    17897, 17532, 17167, 16801, 16436, 16071, 15706, 15340, 14975, 14610,
+    /* 2009 - 2000 */
+    14245, 13879, 13514, 13149, 12784, 12418, 12053, 11688, 11323, 10957,
+    /* 1999 - 1990 */
+    10592, 10227, 9862, 9496, 9131, 8766, 8401, 8035, 7670, 7305,
+    /* 1989 - 1980 */
+    6940, 6574, 6209, 5844, 5479, 5113, 4748, 4383, 4018, 3652,
+    /* 1979 - 1970 */
+    3287, 2922, 2557, 2191, 1826, 1461, 1096, 730, 365, 0,
+};
+
+static inline bool is_leap(unsigned int y)
+{
+    return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
+}
+
+void localtime(struct tm *stTm, unsigned int time)
+{
+    unsigned int year, transition_value, i, days = time / SECONDS_PER_DAY,secs = time % SECONDS_PER_DAY;
+
+    stTm->tm_wday = (4 + days - 1) % 7 + 1;
+    stTm->tm_sec = secs % 60;
+    transition_value = secs / 60;
+    stTm->tm_min = transition_value % 60;
+    stTm->tm_hour   = transition_value / 60;
+
+    for (i = 0, year = DSE_FIRST; days_since_epoch[i] > days; ++i, --year);
+
+    days -= days_since_epoch[i];
+    stTm->tm_year = DSE_FIRST - i;
+    stTm->tm_yday = days;
+    if (is_leap(year))
+    {
+        for (i = ARRAY_SIZE(days_since_leapyear) - 1;i > 0 && days_since_leapyear[i] > days; --i);
+        stTm->tm_mday = days - days_since_leapyear[i] + 1;
+    }
+    else
+    {
+        for (i = ARRAY_SIZE(days_since_year) - 1;i > 0 && days_since_year[i] > days; --i);
+        stTm->tm_mday = days - days_since_year[i] + 1;
+    }
+
+    stTm->tm_mon = i + 1;
+}
+
+static void _InvertU8(uint8_t *dBuf, uint8_t *srcBuf)
+{
+    uint8_t tmp[4] = {0};
+    uint8_t i=0;
+    for(i=0;i<8;i++)
+    {
+        if(srcBuf[0] & (1<<i))
+        {
+            tmp[0] |= 1<<(7-i);
+        }
+    }
+    dBuf[0] = tmp[0];
+}
+
+static void _InvertU32(uint32_t *dBuf, uint32_t *srcBuf)
+{
+    uint32_t tmp[4] = {0};
+    uint8_t i=0;
+    for(i=0;i<32;i++)
+    {
+        if(srcBuf[0] & (1<<i))
+        {
+            tmp[0] |= 1<<(31-i);
+        }
+    }
+    dBuf[0] = tmp[0];
+}
+
+uint32_t calc_crc32(uint8_t Mode, uint8_t *pMsg, uint32_t Len)
+{
+    if(Mode > sizeof(CRC32_Table)/sizeof(crc32_Table_TypeDef))
+        return 0;
+
+    uint32_t CRCin = CRC32_Table[Mode].initVal;
+    uint32_t tmp = 0;
+    uint32_t i=0;
+    uint8_t j=0;
+
+       for(i=0;i<Len;i++)
+    {
+        tmp = *(pMsg++);
+        if(CRC32_Table[Mode].bits == LSB_MODE)
+        {
+            _InvertU8((uint8_t*)&tmp, (uint8_t*)&tmp);
+        }
+        CRCin ^= (tmp <<24);
+        for(j=0;j<8;j++)
+        {
+            if(CRCin & 0x80000000)
+            {
+                CRCin = (CRCin << 1) ^ CRC32_Table[Mode].POLY;
+            }
+            else
+            {
+                CRCin <<= 1;
+            }
+        }
+    }
+       if(CRC32_Table[Mode].bits == LSB_MODE)
+    {
+       _InvertU32(&CRCin, &CRCin);
+    }
+       return (CRCin ^ CRC32_Table[Mode].sub);
+}
+
+
+unsigned long long parse_data(struct eswin *tr,unsigned char size)
+{
+    unsigned long long value = 0;
+    if(size == 1)
+    {
+        value = * (unsigned char*)(tr->fw->data + offset);
+    }
+    else if(size == 2)
+    {
+        value = * (unsigned short*)(tr->fw->data + offset);
+    }
+    else if(size == 4)
+    {
+        value = * (unsigned int*)(tr->fw->data + offset);
+    }
+    else if(size == 8)
+    {
+        value = * (unsigned long long*)(tr->fw->data + offset);
+    }
+    else
+    {
+        return value;
+    }
+    offset += size;
+    return value;
+}
+
+uint8_t parse_fw_info(struct eswin *tr, bin_head_data *phead)
+{
+    unsigned int len = INFO_SIZE;
+
+    phead->fw_Info = vmalloc(len+1);
+    memcpy(phead->fw_Info,(unsigned char*)(tr->fw->data + offset),len);
+
+    offset += len;
+    return len;
+}
+
+bool fw_crc_check(struct eswin *tr,bin_head_data head)
+{
+    unsigned int now_crc32 = 0;
+    unsigned char crc_shift = ((unsigned char)((void *)(&head.head_crc32) - (void *)(&head))) + sizeof(unsigned int);
+    now_crc32 = calc_crc32(LSB_MODE, (unsigned char *)(tr->fw->data + crc_shift), HEAD_SIZE - crc_shift);
+    if(now_crc32 == head.head_crc32)
+    {
+        return true;
+    }
+    else
+    {
+        ECRNX_PRINT("%s,firmware CRC check error , (%s) ,crc total size(%d)\n", __func__, fw_name,HEAD_SIZE - crc_shift);
+        print_hex_dump(KERN_ERR, DBG_PREFIX_CRC_CHECK, DUMP_PREFIX_ADDRESS, 32, 1, &(head.crc32), 4, false);
+        print_hex_dump(KERN_ERR, DBG_PREFIX_CRC_CHECK, DUMP_PREFIX_ADDRESS, 32, 1, &now_crc32, 4, false);
+        return false;
+    }
+}
+bool fw_magic_check(struct eswin *tr,bin_head_data head)
+{
+    if(head.magic == 0xEF40)
+    {
+        return true;
+    }
+    else if(head.magic == 0xCE56)
+    {
+        return true;
+    }
+    else
+    {
+        ECRNX_PRINT("%s,firmware magic check error , (%s) ,magic(%x)\n", __func__, fw_name,head.magic);
+        return false;
+    }
+}
+
+bool fw_check_head(struct eswin *tr)
+{
+    struct tm timenow = {0};
+
+    head.head_crc32 = parse_data(tr, sizeof(unsigned int));
+    head.crc32 = parse_data(tr, sizeof(unsigned int));
+    head.magic = parse_data(tr, sizeof(unsigned int));
+    head.UTC_time = parse_data(tr, sizeof(unsigned int));
+
+    if(fw_magic_check(tr,head) == false)
+    {
+        return false;
+    }
+    if(fw_crc_check(tr,head) == false)
+    {
+        return false;
+    }
+
+    parse_fw_info(tr, &head);
+
+    localtime(&timenow, head.UTC_time);
+    ECRNX_PRINT("%s,firmware build time: %04d-%02d-%02d %02d:%02d:%02d\n", __func__,(int)timenow.tm_year,timenow.tm_mon,timenow.tm_mday,timenow.tm_hour,timenow.tm_min,timenow.tm_sec);
+    if(head.fw_Info != NULL)
+    {
+        ECRNX_PRINT("%s,firmware information: (%s)\n", __func__, head.fw_Info);
+    }
+    return true;
+}
diff --git a/drivers/net/wireless/eswin/fw_head_check.h b/drivers/net/wireless/eswin/fw_head_check.h
new file mode 100644 (file)
index 0000000..65a60c7
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+******************************************************************************
+*
+* @file fw_head_check.h
+*
+* @brief ecrnx usb firmware validity check functions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#ifndef _FW_HEAD_CHECK_H_
+#define _FW_HEAD_CHECK_H_
+
+#include "core.h"
+
+#define HEAD_SIZE  (64)
+#define INFO_SIZE  (48)
+
+typedef struct _bin_head_data {
+    unsigned int head_crc32;
+    unsigned int crc32;
+    unsigned int magic;
+    unsigned int UTC_time;
+    unsigned char *fw_Info;
+}bin_head_data;
+
+typedef struct{
+       uint32_t initVal;
+       uint32_t POLY;
+       uint32_t sub;
+       uint8_t  bits;
+       char     *funcName;
+}crc32_Table_TypeDef;
+
+
+extern unsigned int offset;
+
+void localtime(struct tm *stTm, unsigned int time);
+bool fw_check_head(struct eswin *tr);
+
+#endif
diff --git a/drivers/net/wireless/eswin/hal_desc.c b/drivers/net/wireless/eswin/hal_desc.c
new file mode 100644 (file)
index 0000000..6f040fa
--- /dev/null
@@ -0,0 +1,189 @@
+/**
+ *****************************************************************************
+ *
+ * @file hal_desc.c
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ *****************************************************************************
+ */
+
+#include <linux/string.h>
+#include "hal_desc.h"
+
+const struct ecrnx_legrate legrates_lut[] = {
+    [0]  = { .idx = 0,  .rate = 10 },
+    [1]  = { .idx = 1,  .rate = 20 },
+    [2]  = { .idx = 2,  .rate = 55 },
+    [3]  = { .idx = 3,  .rate = 110 },
+    [4]  = { .idx = -1, .rate = 0 },
+    [5]  = { .idx = -1, .rate = 0 },
+    [6]  = { .idx = -1, .rate = 0 },
+    [7]  = { .idx = -1, .rate = 0 },
+    [8]  = { .idx = 10, .rate = 480 },
+    [9]  = { .idx = 8,  .rate = 240 },
+    [10] = { .idx = 6,  .rate = 120 },
+    [11] = { .idx = 4,  .rate = 60 },
+    [12] = { .idx = 11, .rate = 540 },
+    [13] = { .idx = 9,  .rate = 360 },
+    [14] = { .idx = 7,  .rate = 180 },
+    [15] = { .idx = 5,  .rate = 90 },
+};
+
+/**
+ * ecrnx_machw_type - Return type (NX or HE) MAC HW is used
+ *
+ */
+int ecrnx_machw_type(uint32_t machw_version_2)
+{
+    uint32_t machw_um_ver_maj = (machw_version_2 >> 4) & 0x7;
+
+    if (machw_um_ver_maj >= 4)
+        return ECRNX_MACHW_HE;
+    else
+        return ECRNX_MACHW_NX;
+}
+
+/**
+ * ecrnx_rx_vector_convert - Convert in place a RX vector from NX hardware into
+ * a RX vector formatted by HE hardware.
+ *
+ * @machw_type: Type of MACHW in use.
+ * @rx_vect1: Rx vector 1 descriptor of the received frame.
+ * @rx_vect2: Rx vector 2 descriptor of the received frame.
+ */
+void ecrnx_rx_vector_convert(int machw_type,
+                            struct rx_vector_1 *rx_vect1,
+                            struct rx_vector_2 *rx_vect2)
+{
+    struct rx_vector_1_nx rx_vect1_nx;
+    struct rx_vector_2_nx rx_vect2_nx;
+
+    // Check if we need to do the conversion. Only if old modem is used
+    if (machw_type == ECRNX_MACHW_HE) {
+        rx_vect1->rssi1 = rx_vect1->rssi_leg;
+        return;
+    }
+
+    // Copy the received vector locally
+    memcpy(&rx_vect1_nx, rx_vect1, sizeof(struct rx_vector_1_nx));
+
+    // Reset it
+    memset(rx_vect1, 0, sizeof(struct rx_vector_1));
+
+    // Perform the conversion
+    rx_vect1->format_mod = rx_vect1_nx.format_mod;
+    rx_vect1->ch_bw = rx_vect1_nx.ch_bw;
+    rx_vect1->pre_type = rx_vect1_nx.pre_type;
+    rx_vect1->antenna_set = rx_vect1_nx.antenna_set;
+    rx_vect1->leg_length = rx_vect1_nx.leg_length;
+    rx_vect1->leg_rate = rx_vect1_nx.leg_rate;
+    rx_vect1->rssi1 = rx_vect1_nx.rssi1;
+
+    switch (rx_vect1->format_mod) {
+        case FORMATMOD_NON_HT:
+        case FORMATMOD_NON_HT_DUP_OFDM:
+            rx_vect1->leg.dyn_bw_in_non_ht = rx_vect1_nx.dyn_bw;
+            rx_vect1->leg.chn_bw_in_non_ht = rx_vect1_nx.ch_bw;
+            rx_vect1->leg.lsig_valid = rx_vect1_nx.lsig_valid;
+            break;
+        case FORMATMOD_HT_MF:
+        case FORMATMOD_HT_GF:
+            rx_vect1->ht.sounding = rx_vect1_nx.sounding;
+            rx_vect1->ht.smoothing = rx_vect1_nx.smoothing;
+            rx_vect1->ht.short_gi = rx_vect1_nx.short_gi;
+            rx_vect1->ht.aggregation = rx_vect1_nx.aggregation;
+            rx_vect1->ht.stbc = rx_vect1_nx.stbc;
+            rx_vect1->ht.num_extn_ss = rx_vect1_nx.num_extn_ss;
+            rx_vect1->ht.lsig_valid = rx_vect1_nx.lsig_valid;
+            rx_vect1->ht.mcs = rx_vect1_nx.mcs;
+            rx_vect1->ht.fec = rx_vect1_nx.fec_coding;
+            rx_vect1->ht.length = rx_vect1_nx.ht_length;
+            break;
+        case FORMATMOD_VHT:
+            rx_vect1->vht.sounding = rx_vect1_nx.sounding;
+            rx_vect1->vht.beamformed = !rx_vect1_nx.smoothing;
+            rx_vect1->vht.short_gi = rx_vect1_nx.short_gi;
+            rx_vect1->vht.stbc = rx_vect1_nx.stbc;
+            rx_vect1->vht.doze_not_allowed = rx_vect1_nx.doze_not_allowed;
+            rx_vect1->vht.first_user = rx_vect1_nx.first_user;
+            rx_vect1->vht.partial_aid = rx_vect1_nx.partial_aid;
+            rx_vect1->vht.group_id = rx_vect1_nx.group_id;
+            rx_vect1->vht.mcs = rx_vect1_nx.mcs;
+            rx_vect1->vht.nss = rx_vect1_nx.stbc ? rx_vect1_nx.n_sts/2 : rx_vect1_nx.n_sts;
+            rx_vect1->vht.fec = rx_vect1_nx.fec_coding;
+            rx_vect1->vht.length = (rx_vect1_nx._ht_length << 16) | rx_vect1_nx.ht_length;
+            break;
+    }
+
+    if (!rx_vect2)
+        return;
+
+    // Copy the received vector 2 locally
+    memcpy(&rx_vect2_nx, rx_vect2, sizeof(struct rx_vector_2_nx));
+
+    // Reset it
+    memset(rx_vect2, 0, sizeof(struct rx_vector_2));
+
+    rx_vect2->rcpi1 = rx_vect2_nx.rcpi;
+    rx_vect2->rcpi2 = rx_vect2_nx.rcpi;
+    rx_vect2->rcpi3 = rx_vect2_nx.rcpi;
+    rx_vect2->rcpi4 = rx_vect2_nx.rcpi;
+
+    rx_vect2->evm1 = rx_vect2_nx.evm1;
+    rx_vect2->evm2 = rx_vect2_nx.evm2;
+    rx_vect2->evm3 = rx_vect2_nx.evm3;
+    rx_vect2->evm4 = rx_vect2_nx.evm4;
+}
+
+
+/**
+ * ecrnx_rx_status_convert - Convert in place a legacy MPDU status from NX hardware
+ * into a MPDU status formatted by HE hardware.
+ *
+ * @machw_type: Type of MACHW in use.
+ * @status: Rx MPDU status of the received frame.
+ */
+void ecrnx_rx_status_convert(int machw_type, struct mpdu_status *status)
+{
+    struct mpdu_status_nx *status_nx;
+
+    if (machw_type == ECRNX_MACHW_HE)
+        return;
+
+    status_nx = (struct mpdu_status_nx *)status;
+    status->undef_err = status_nx->undef_err;
+
+    switch (status_nx->decr_status) {
+        case ECRNX_RX_HD_NX_DECR_UNENC:
+            status->decr_type = ECRNX_RX_HD_DECR_UNENC;
+            status->decr_err = 0;
+            break;
+        case ECRNX_RX_HD_NX_DECR_ICVFAIL:
+            status->decr_type = ECRNX_RX_HD_DECR_WEP;
+            status->decr_err = 1;
+            break;
+        case ECRNX_RX_HD_NX_DECR_CCMPFAIL:
+        case ECRNX_RX_HD_NX_DECR_AMSDUDISCARD:
+            status->decr_type = ECRNX_RX_HD_DECR_CCMP128;
+            status->decr_err = 1;
+            break;
+        case ECRNX_RX_HD_NX_DECR_NULLKEY:
+            status->decr_type = ECRNX_RX_HD_DECR_NULLKEY;
+            status->decr_err = 1;
+            break;
+        case ECRNX_RX_HD_NX_DECR_WEPSUCCESS:
+            status->decr_type = ECRNX_RX_HD_DECR_WEP;
+            status->decr_err = 0;
+            break;
+        case ECRNX_RX_HD_NX_DECR_TKIPSUCCESS:
+            status->decr_type = ECRNX_RX_HD_DECR_TKIP;
+            status->decr_err = 0;
+            break;
+        case ECRNX_RX_HD_NX_DECR_CCMPSUCCESS:
+            status->decr_type = ECRNX_RX_HD_DECR_CCMP128;
+            status->decr_err = 0;
+            break;
+    }
+}
+
diff --git a/drivers/net/wireless/eswin/hal_desc.h b/drivers/net/wireless/eswin/hal_desc.h
new file mode 100644 (file)
index 0000000..647961a
--- /dev/null
@@ -0,0 +1,670 @@
+/**
+ ******************************************************************************
+ *
+ * @file hal_desc.h
+ *
+ * @brief File containing the definition of HW descriptors.
+ *
+ * Contains the definition and structures used by HW
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _HAL_DESC_H_
+#define _HAL_DESC_H_
+
+#include "lmac_types.h"
+
+#define ECRNX_MACHW_NX 1
+#define ECRNX_MACHW_HE 2
+/* Rate and policy table */
+
+#define N_CCK  8
+#define N_OFDM 8
+#define N_HT   (8 * 2 * 2 * 4)
+#define N_VHT  (10 * 4 * 2 * 8)
+#define N_HE_SU (12 * 4 * 3 * 8)
+#define N_HE_MU (12 * 6 * 3 * 8)
+
+/* conversion table from NL80211 to MACHW enum */
+extern const int chnl2bw[];
+
+/* conversion table from MACHW to NL80211 enum */
+extern const int bw2chnl[];
+
+struct ecrnx_legrate {
+    s16 idx;
+    u16 rate;  // in 100Kbps
+};
+extern const struct ecrnx_legrate legrates_lut[];
+/* Values for formatModTx */
+#define FORMATMOD_NON_HT          0
+#define FORMATMOD_NON_HT_DUP_OFDM 1
+#define FORMATMOD_HT_MF           2
+#define FORMATMOD_HT_GF           3
+#define FORMATMOD_VHT             4
+#define FORMATMOD_HE_SU           5
+#define FORMATMOD_HE_MU           6
+#define FORMATMOD_HE_ER           7
+#define FORMATMOD_HE_TB           8
+
+/* Values for navProtFrmEx */
+#define NAV_PROT_NO_PROT_BIT                 0
+#define NAV_PROT_SELF_CTS_BIT                1
+#define NAV_PROT_RTS_CTS_BIT                 2
+#define NAV_PROT_RTS_CTS_WITH_QAP_BIT        3
+#define NAV_PROT_STBC_BIT                    4
+
+/* THD MACCTRLINFO2 fields, used in  struct umacdesc umac.flags */
+/// WhichDescriptor definition - contains aMPDU bit and position value
+/// Offset of WhichDescriptor field in the MAC CONTROL INFO 2 word
+#define WHICHDESC_OFT                     19
+/// Mask of the WhichDescriptor field
+#define WHICHDESC_MSK                     (0x07 << WHICHDESC_OFT)
+/// Only 1 THD possible, describing an unfragmented MSDU
+#define WHICHDESC_UNFRAGMENTED_MSDU       (0x00 << WHICHDESC_OFT)
+/// THD describing the first MPDU of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_FIRST   (0x01 << WHICHDESC_OFT)
+/// THD describing intermediate MPDUs of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_INT     (0x02 << WHICHDESC_OFT)
+/// THD describing the last MPDU of a fragmented MSDU
+#define WHICHDESC_FRAGMENTED_MSDU_LAST    (0x03 << WHICHDESC_OFT)
+/// THD for extra descriptor starting an AMPDU
+#define WHICHDESC_AMPDU_EXTRA             (0x04 << WHICHDESC_OFT)
+/// THD describing the first MPDU of an A-MPDU
+#define WHICHDESC_AMPDU_FIRST             (0x05 << WHICHDESC_OFT)
+/// THD describing intermediate MPDUs of an A-MPDU
+#define WHICHDESC_AMPDU_INT               (0x06 << WHICHDESC_OFT)
+/// THD describing the last MPDU of an A-MPDU
+#define WHICHDESC_AMPDU_LAST              (0x07 << WHICHDESC_OFT)
+
+/// aMPDU bit offset
+#define AMPDU_OFT                         21
+/// aMPDU bit
+#define AMPDU_BIT                         CO_BIT(AMPDU_OFT)
+
+union ecrnx_mcs_index {
+    struct {
+        u32 mcs : 3;
+        u32 nss : 2;
+    } ht;
+    struct {
+        u32 mcs : 4;
+        u32 nss : 3;
+    } vht;
+    struct {
+        u32 mcs : 4;
+        u32 nss : 3;
+    } he;
+    u32 legacy : 7;
+};
+
+union ecrnx_rate_ctrl_info {
+    struct {
+        u32 mcsIndexTx      : 7;
+        u32 bwTx            : 2;
+        u32 giAndPreTypeTx  : 2;
+        u32 formatModTx     : 3;
+        #ifdef CONFIG_ECRNX_FULLMAC
+        u32 dcmTx           : 1;
+        #else
+        u32 navProtFrmEx    : 3;
+        u32 mcsIndexProtTx  : 7;
+        u32 bwProtTx        : 2;
+        u32 formatModProtTx : 3;
+        u32 nRetry          : 3;
+        #endif
+    };
+    u32 value;
+};
+
+struct ecrnx_power_ctrl_info {
+    u32 txPwrLevelPT          : 8;
+    u32 txPwrLevelProtPT      : 8;
+    u32 reserved              :16;
+};
+
+union ecrnx_pol_phy_ctrl_info_1 {
+    struct {
+        u32 rsvd1     : 3;
+        u32 bfFrmEx   : 1;
+        u32 numExtnSS : 2;
+        u32 fecCoding : 1;
+        u32 stbc      : 2;
+        u32 rsvd2     : 5;
+        u32 nTx       : 3;
+        u32 nTxProt   : 3;
+    };
+    u32 value;
+};
+
+union ecrnx_pol_phy_ctrl_info_2 {
+    struct {
+        u32 antennaSet : 8;
+        u32 smmIndex   : 8;
+        u32 beamFormed : 1;
+    };
+    u32 value;
+};
+
+union ecrnx_pol_mac_ctrl_info_1 {
+    struct {
+        u32 keySRamIndex   : 10;
+        u32 keySRamIndexRA : 10;
+    };
+    u32 value;
+};
+
+union ecrnx_pol_mac_ctrl_info_2 {
+    struct {
+        u32 longRetryLimit  : 8;
+        u32 shortRetryLimit : 8;
+        u32 rtsThreshold    : 12;
+    };
+    u32 value;
+};
+
+#define POLICY_TABLE_PATTERN    0xBADCAB1E
+
+struct tx_policy_tbl {
+    /* Unique Pattern at the start of Policy Table */
+    u32 upatterntx;
+    /* PHY Control 1 Information used by MAC HW */
+    union ecrnx_pol_phy_ctrl_info_1 phyctrlinfo_1;
+    /* PHY Control 2 Information used by MAC HW */
+    union ecrnx_pol_phy_ctrl_info_2 phyctrlinfo_2;
+    /* MAC Control 1 Information used by MAC HW */
+    union ecrnx_pol_mac_ctrl_info_1 macctrlinfo_1;
+    /* MAC Control 2 Information used by MAC HW */
+    union ecrnx_pol_mac_ctrl_info_2 macctrlinfo_2;
+
+    union ecrnx_rate_ctrl_info  ratectrlinfos[NX_TX_MAX_RATES];
+    struct ecrnx_power_ctrl_info powerctrlinfos[NX_TX_MAX_RATES];
+};
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+
+union ecrnx_hw_txstatus {
+    struct {
+        u32 num_rts_retries      : 8;
+        u32 num_mpdu_retries     : 8;
+        u32 retry_limit_reached  : 1;
+        u32 lifetime_expired     : 1;
+        u32 baFrameReceived      : 1;
+        u32 reserved2            : 4;
+        u32 frm_successful_tx    : 1;
+        u32 transmission_bw      : 2;
+        u32 which_descriptor_sw  : 4;
+        u32 descriptor_done_swtx : 1;
+        u32 descriptor_done_hwtx : 1;
+    };
+    u32 value;
+};
+
+// WhichDescriptor for AMPDUs (_under BA Policy_)
+#define __WD_AMPDU_BAPOL                     0xC
+#define __WD_AMPDU_EXTRA                     0xC
+#define __WD_AMPDU_FIRST                     0xD
+#define __WD_AMPDU_INT                       0xE
+#define __WD_AMPDU_LAST                      0xF
+
+#define ECRNX_WD_IS_AMPDU(whichdesc) \
+    (((whichdesc) & __WD_AMPDU_BAPOL) == __WD_AMPDU_BAPOL)
+#define ECRNX_WD_IS_FIRST(whichdesc) \
+    ((whichdesc) == __WD_AMPDU_FIRST)
+#define ECRNX_WD_IS_LAST(whichdesc) \
+    ((whichdesc) == __WD_AMPDU_LAST)
+
+union ecrnx_thd_phy_ctrl_info {
+    struct {
+        u32 soundingTx       : 1;
+        u32 smoothingTx      : 1;
+        u32 smoothingProtTx  : 1;
+        u32 useBWSignalingTx : 1;
+        u32 dynBWTx          : 1;
+        u32 dozeNotAllowedTx : 1;
+        u32 continuousTx     : 1;
+        u32 rsvd             : 1;
+        u32 PTITx            : 4;
+        u32 userPositionTx   : 2;
+        u32 useRIFSTx        : 1;
+        u32 muMIMOTx         : 1;
+        u32 groupIDTx        : 6;
+        u32 partialAIDTx     : 9;
+    };
+    u32 value;
+};
+
+#define EXPECTED_ACK_NO_ACK               0
+#define EXPECTED_ACK_NORMAL_ACK           1
+#define EXPECTED_ACK_BLOCK_ACK            2
+#define EXPECTED_ACK_COMPRESSED_BLOCK_ACK 3
+
+union ecrnx_thd_mac_ctrl_info_1 {
+    struct {
+        u32 rsvd1        : 9;
+        u32 expectedAck  : 2;
+        u32 lstp         : 1;
+        u32 lstpProt     : 1;
+        u32 lowRateRetry : 1;
+        u32 writeACK     : 1;
+        u32 rsvd2        : 1;
+        u32 protFrmDur   : 16;
+    };
+    u32 value;
+};
+
+/**
+ * struct ecrnx_hw_txhdr - Hardware part of tx header
+ *
+ * @policy: Policy table to use for transmission
+ * @mac_ctrl_info: MAC configuration to use for transmission
+ * @phy_ctrl_info: PHY configuration to use for transmission
+ *
+ * @status: Status updated by fw/hardware after transmission
+ */
+struct ecrnx_hw_txhdr {
+    struct tx_policy_tbl policy;
+    union ecrnx_thd_mac_ctrl_info_1 mac_ctrl_info;
+    union ecrnx_thd_phy_ctrl_info phy_ctrl_info;
+    union ecrnx_hw_txstatus status;
+};
+
+#else /* !CONFIG_ECRNX_SOFTMAC */
+
+/**
+ * struct ecrnx_hw_txstatus - Bitfield of confirmation status
+ *
+ * @tx_done: packet has been processed by the firmware.
+ * @retry_required: packet has been transmitted but not acknoledged.
+ * Driver must repush it.
+ * @sw_retry_required: packet has not been transmitted (FW wasn't able to push
+ * it when it received it: not active channel ...). Driver must repush it.
+ * @acknowledged: packet has been acknowledged by peer
+ */
+union ecrnx_hw_txstatus {
+    struct {
+        u32 tx_done            : 1;
+        u32 retry_required     : 1;
+        u32 sw_retry_required  : 1;
+        u32 acknowledged       : 1;
+        u32 reserved           :28;
+    };
+    u32 value;
+};
+
+/**
+ * struct tx_cfm_tag - Structure indicating the status and other
+ * information about the transmission
+ *
+ * @pn: PN that was used for the transmission
+ * @sn: Sequence number of the packet
+ * @timestamp: Timestamp of first transmission of this MPDU
+ * @credits: Number of credits to be reallocated for the txq that push this
+ * buffer (can be 0 or 1)
+ * @ampdu_size: Size of the ampdu in which the frame has been transmitted if
+ * this was the last frame of the a-mpdu, and 0 if the frame is not the last
+ * frame on a a-mdpu.
+ * 1 means that the frame has been transmitted as a singleton.
+ * @amsdu_size: Size, in bytes, allowed to create a-msdu.
+ * @status: transmission status
+ */
+struct tx_cfm_tag
+{
+    u16_l pn[4];
+    u16_l sn;
+    u16_l timestamp;
+    s8_l credits;
+    u8_l ampdu_size;
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+    u16_l amsdu_size;
+#endif
+
+#ifdef CONFIG_ECRNX_ESWIN
+    uint32_t  hostid[2];
+#endif
+
+    union ecrnx_hw_txstatus status;
+};
+
+/**
+ * struct ecrnx_hw_txhdr - Hardware part of tx header
+ *
+ * @cfm: Information updated by fw/hardware after sending a frame
+ */
+struct ecrnx_hw_txhdr {
+    struct tx_cfm_tag cfm;
+};
+
+#endif /* CONFIG_ECRNX_SOFTMAC */
+
+#define ECRNX_RX_HD_NX_DECR_UNENC           0 // Frame unencrypted
+#define ECRNX_RX_HD_NX_DECR_ICVFAIL         1 // WEP/TKIP ICV failure
+#define ECRNX_RX_HD_NX_DECR_CCMPFAIL        2 // CCMP failure
+#define ECRNX_RX_HD_NX_DECR_AMSDUDISCARD    3 // A-MSDU discarded at HW
+#define ECRNX_RX_HD_NX_DECR_NULLKEY         4 // NULL key found
+#define ECRNX_RX_HD_NX_DECR_WEPSUCCESS      5 // Security type WEP
+#define ECRNX_RX_HD_NX_DECR_TKIPSUCCESS     6 // Security type TKIP
+#define ECRNX_RX_HD_NX_DECR_CCMPSUCCESS     7 // Security type CCMP (or WPI)
+#define ECRNX_RX_HD_DECR_UNENC     0 // Frame unencrypted
+#define ECRNX_RX_HD_DECR_WEP       1 // Security type WEP
+#define ECRNX_RX_HD_DECR_TKIP      2 // Security type TKIP
+#define ECRNX_RX_HD_DECR_CCMP128   3 // Security type CCMP (128 bits)
+#define ECRNX_RX_HD_DECR_CCMP256   4 // Security type CCMP (256 bits)
+#define ECRNX_RX_HD_DECR_GCMP128   5 // Security type GCMP (128 bits)
+#define ECRNX_RX_HD_DECR_GCMP256   6 // Security type GCMP (256 bits)
+#define ECRNX_RX_HD_DECR_WAPI      7 // Security type WAPI
+#define ECRNX_RX_HD_DECR_NULLKEY  15 // NULL key found
+struct rx_vector_1_nx {
+    u32    leg_length         :12;
+    u32    leg_rate           : 4;
+    u32    ht_length          :16;
+    u32    _ht_length         : 4; // FIXME
+    u32    short_gi           : 1;
+    u32    stbc               : 2;
+    u32    smoothing          : 1;
+    u32    mcs                : 7;
+    u32    pre_type           : 1;
+    u32    format_mod         : 3;
+    u32    ch_bw              : 2;
+    u32    n_sts              : 3;
+    u32    lsig_valid         : 1;
+    u32    sounding           : 1;
+    u32    num_extn_ss        : 2;
+    u32    aggregation        : 1;
+    u32    fec_coding         : 1;
+    u32    dyn_bw             : 1;
+    u32    doze_not_allowed   : 1;
+    u32    antenna_set        : 8;
+    u32    partial_aid        : 9;
+    u32    group_id           : 6;
+    u32    first_user         : 1;
+    s32    rssi1              : 8;
+    s32    rssi2              : 8;
+    s32    rssi3              : 8;
+    s32    rssi4              : 8;
+    u32    reserved_1d        : 8;
+};
+struct rx_vector_2_nx {
+    u32    rcpi               : 8;
+    u32    evm1               : 8;
+    u32    evm2               : 8;
+    u32    evm3               : 8;
+    u32    evm4               : 8;
+    u32    reserved2b_1       : 8;
+    u32    reserved2b_2       : 8;
+    u32    reserved2b_3       : 8;
+};
+struct mpdu_status_nx {
+    u32    rx_vect2_valid     : 1;
+    u32    resp_frame         : 1;
+    u32    decr_status        : 3;
+    u32    rx_fifo_oflow      : 1;
+    u32    undef_err          : 1;
+    u32    phy_err            : 1;
+    u32    fcs_err            : 1;
+    u32    addr_mismatch      : 1;
+    u32    ga_frame           : 1;
+    u32    current_ac         : 2;
+    u32    frm_successful_rx  : 1;
+    u32    desc_done_rx       : 1;
+    u32    key_sram_index     : 10;
+    u32    key_sram_valid     : 1;
+    u32    type               : 2;
+    u32    subtype            : 4;
+};
+struct rx_leg_vect
+{
+    u8    dyn_bw_in_non_ht     : 1;
+    u8    chn_bw_in_non_ht     : 2;
+    u8    rsvd_nht             : 4;
+    u8    lsig_valid           : 1;
+} __packed;
+struct rx_ht_vect
+{
+    u16   sounding             : 1;
+    u16   smoothing            : 1;
+    u16   short_gi             : 1;
+    u16   aggregation          : 1;
+    u16   stbc                 : 1;
+    u16   num_extn_ss          : 2;
+    u16   lsig_valid           : 1;
+    u16   mcs                  : 7;
+    u16   fec                  : 1;
+    u16   length               :16;
+} __packed;
+struct rx_vht_vect
+{
+    u8   sounding              : 1;
+    u8   beamformed            : 1;
+    u8   short_gi              : 1;
+    u8   rsvd_vht1             : 1;
+    u8   stbc                  : 1;
+    u8   doze_not_allowed      : 1;
+    u8   first_user            : 1;
+    u8   rsvd_vht2             : 1;
+    u16  partial_aid           : 9;
+    u16  group_id              : 6;
+    u16  rsvd_vht3             : 1;
+    u32  mcs                   : 4;
+    u32  nss                   : 3;
+    u32  fec                   : 1;
+    u32  length                :20;
+    u32  rsvd_vht4             : 4;
+} __packed;
+struct rx_he_vect
+{
+    u8   sounding              : 1;
+    u8   beamformed            : 1;
+    u8   gi_type               : 2;
+    u8   stbc                  : 1;
+    u8   rsvd_he1              : 3;
+    u8   uplink_flag           : 1;
+    u8   beam_change           : 1;
+    u8   dcm                   : 1;
+    u8   he_ltf_type           : 2;
+    u8   doppler               : 1;
+    u8   rsvd_he2              : 2;
+    u8   bss_color             : 6;
+    u8   rsvd_he3              : 2;
+    u8   txop_duration         : 7;
+    u8   rsvd_he4              : 1;
+    u8   pe_duration           : 4;
+    u8   spatial_reuse         : 4;
+    u8   sig_b_comp_mode       : 1;
+    u8   dcm_sig_b             : 1;
+    u8   mcs_sig_b             : 3;
+    u8   ru_size               : 3;
+    u32  mcs                   : 4;
+    u32  nss                   : 3;
+    u32  fec                   : 1;
+    u32  length                :20;
+    u32  rsvd_he6              : 4;
+} __packed;
+struct rx_vector_1 {
+    u8     format_mod         : 4;
+    u8     ch_bw              : 3;
+    u8     pre_type           : 1;
+    u8     antenna_set        : 8;
+    s32    rssi_leg           : 8;
+    u32    leg_length         :12;
+    u32    leg_rate           : 4;
+    s32    rssi1              : 8;
+    union
+    {
+        struct rx_leg_vect leg;
+        struct rx_ht_vect ht;
+        struct rx_vht_vect vht;
+        struct rx_he_vect he;
+    };
+} __packed;
+struct rx_vector_2 {
+    u32    rcpi1              : 8;
+    u32    rcpi2              : 8;
+    u32    rcpi3              : 8;
+    u32    rcpi4              : 8;
+    u32    evm1               : 8;
+    u32    evm2               : 8;
+    u32    evm3               : 8;
+    u32    evm4               : 8;
+};
+struct mpdu_status {
+    u32    rx_vect2_valid     : 1;
+    u32    resp_frame         : 1;
+    u32    decr_type          : 4;
+    u32    decr_err           : 1;
+    u32    undef_err          : 1;
+    u32    fcs_err            : 1;
+    u32    addr_mismatch      : 1;
+    u32    ga_frame           : 1;
+    u32    current_ac         : 2;
+    u32    frm_successful_rx  : 1;
+    u32    desc_done_rx       : 1;
+    u32    key_sram_index     : 10;
+    u32    key_sram_v         : 1;
+    u32    type               : 2;
+    u32    subtype            : 4;
+};
+struct hw_vect {
+    u32 len                   :16;
+    u32 reserved              : 8;
+    u32 mpdu_cnt              : 6;
+    u32 ampdu_cnt             : 2;
+    __le32 tsf_lo;
+    /** TSF High */
+    __le32 tsf_hi;
+
+    /** Receive Vector 1 */
+    struct rx_vector_1 rx_vect1;
+    /** Receive Vector 2 */
+    struct rx_vector_2 rx_vect2;
+
+    /** MPDU status information */
+    struct mpdu_status status;
+};
+
+struct phy_channel_info_desc {
+    /** PHY channel information 1 */
+    u32    phy_band           : 8;
+    u32    phy_channel_type   : 8;
+    u32    phy_prim20_freq    : 16;
+
+    /** PHY channel information 2 */
+    u32    phy_center1_freq   : 16;
+    u32    phy_center2_freq   : 16;
+};
+
+int ecrnx_machw_type(uint32_t machw_version_2);
+void ecrnx_rx_vector_convert(int machw_type, struct rx_vector_1 *rx_vect1,
+                            struct rx_vector_2 *rx_vect2);
+void ecrnx_rx_status_convert(int machw_type, struct mpdu_status *status);
+
+/******************************************************************************
+ * Modem
+ ******************************************************************************/
+#define MDM_PHY_CONFIG_TRIDENT     0
+#define MDM_PHY_CONFIG_CATAXIA     1
+#define MDM_PHY_CONFIG_KARST       2
+
+// MODEM features (from reg_mdm_stat.h)
+/// MUMIMOTX field bit
+#define MDM_MUMIMOTX_BIT    ((u32)0x80000000)
+/// MUMIMOTX field position
+#define MDM_MUMIMOTX_POS    31
+/// MUMIMORX field bit
+#define MDM_MUMIMORX_BIT    ((u32)0x40000000)
+/// MUMIMORX field position
+#define MDM_MUMIMORX_POS    30
+/// BFMER field bit
+#define MDM_BFMER_BIT       ((u32)0x20000000)
+/// BFMER field position
+#define MDM_BFMER_POS       29
+/// BFMEE field bit
+#define MDM_BFMEE_BIT       ((u32)0x10000000)
+/// BFMEE field position
+#define MDM_BFMEE_POS       28
+/// LDPCDEC field bit
+#define MDM_LDPCDEC_BIT     ((u32)0x08000000)
+/// LDPCDEC field position
+#define MDM_LDPCDEC_POS     27
+/// LDPCENC field bit
+#define MDM_LDPCENC_BIT     ((u32)0x04000000)
+/// LDPCENC field position
+#define MDM_LDPCENC_POS     26
+/// CHBW field mask
+#define MDM_CHBW_MASK       ((u32)0x03000000)
+/// CHBW field LSB position
+#define MDM_CHBW_LSB        24
+/// CHBW field width
+#define MDM_CHBW_WIDTH      ((u32)0x00000002)
+/// DSSSCCK field bit
+#define MDM_DSSSCCK_BIT     ((u32)0x00800000)
+/// DSSSCCK field position
+#define MDM_DSSSCCK_POS     23
+/// VHT field bit
+#define MDM_VHT_BIT         ((u32)0x00400000)
+/// VHT field position
+#define MDM_VHT_POS         22
+/// HE field bit
+#define MDM_HE_BIT          ((u32)0x00200000)
+/// HE field position
+#define MDM_HE_POS          21
+/// ESS field bit
+#define MDM_ESS_BIT         ((u32)0x00100000)
+/// ESS field position
+#define MDM_ESS_POS         20
+/// RFMODE field mask
+#define MDM_RFMODE_MASK     ((u32)0x000F0000)
+/// RFMODE field LSB position
+#define MDM_RFMODE_LSB      16
+/// RFMODE field width
+#define MDM_RFMODE_WIDTH    ((u32)0x00000004)
+/// NSTS field mask
+#define MDM_NSTS_MASK       ((u32)0x0000F000)
+/// NSTS field LSB position
+#define MDM_NSTS_LSB        12
+/// NSTS field width
+#define MDM_NSTS_WIDTH      ((u32)0x00000004)
+/// NSS field mask
+#define MDM_NSS_MASK        ((u32)0x00000F00)
+/// NSS field LSB position
+#define MDM_NSS_LSB         8
+/// NSS field width
+#define MDM_NSS_WIDTH       ((u32)0x00000004)
+/// NTX field mask
+#define MDM_NTX_MASK        ((u32)0x000000F0)
+/// NTX field LSB position
+#define MDM_NTX_LSB         4
+/// NTX field width
+#define MDM_NTX_WIDTH       ((u32)0x00000004)
+/// NRX field mask
+#define MDM_NRX_MASK        ((u32)0x0000000F)
+/// NRX field LSB position
+#define MDM_NRX_LSB         0
+/// NRX field width
+#define MDM_NRX_WIDTH       ((u32)0x00000004)
+
+#define __MDM_PHYCFG_FROM_VERS(v)  (((v) & MDM_RFMODE_MASK) >> MDM_RFMODE_LSB)
+
+#define RIU_FCU_PRESENT_MASK       ((u32)0xFF000000)
+#define RIU_FCU_PRESENT_LSB        24
+
+#define __RIU_FCU_PRESENT(v)  (((v) & RIU_FCU_PRESENT_MASK) >> RIU_FCU_PRESENT_LSB == 5)
+
+/// AGC load version field mask
+#define RIU_AGC_LOAD_MASK          ((u32)0x00C00000)
+/// AGC load version field LSB position
+#define RIU_AGC_LOAD_LSB           22
+
+#define __RIU_AGCLOAD_FROM_VERS(v) (((v) & RIU_AGC_LOAD_MASK) >> RIU_AGC_LOAD_LSB)
+
+#define __FPGA_TYPE(v)             (((v) & 0xFFFF0000) >> 16)
+
+#define __MDM_MAJOR_VERSION(v)     (((v) & 0xFF000000) >> 24)
+#define __MDM_MINOR_VERSION(v)     (((v) & 0x00FF0000) >> 16)
+#define __MDM_VERSION(v)       ((__MDM_MAJOR_VERSION(v) + 2) * 10 + __MDM_MINOR_VERSION(v))
+
+
+#endif // _HAL_DESC_H_
diff --git a/drivers/net/wireless/eswin/ipc_compat.h b/drivers/net/wireless/eswin/ipc_compat.h
new file mode 100644 (file)
index 0000000..3d0e71a
--- /dev/null
@@ -0,0 +1,25 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ipc_compat.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _IPC_H_
+#define _IPC_H_
+
+#define __INLINE static __attribute__((__always_inline__)) inline
+
+#define __ALIGN4 __aligned(4)
+
+#define ASSERT_ERR(condition)                                                           \
+    do {                                                                                \
+        if (unlikely(!(condition))) {                                                   \
+            printk(DBG_PREFIX KERN_ERR "%s:%d:ASSERT_ERR(" #condition ")\n", __FILE__,  __LINE__); \
+        }                                                                               \
+    } while(0)
+
+#endif /* _IPC_H_ */
diff --git a/drivers/net/wireless/eswin/ipc_host.c b/drivers/net/wireless/eswin/ipc_host.c
new file mode 100644 (file)
index 0000000..17ef5c1
--- /dev/null
@@ -0,0 +1,784 @@
+/**
+ ******************************************************************************
+ *
+ * @file ipc_host.c
+ *
+ * @brief IPC module.
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+/*
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#ifndef __KERNEL__
+#include <stdio.h>
+#define REG_SW_SET_PROFILING(env, value)   do{  }while(0)
+#define REG_SW_CLEAR_PROFILING(env, value)   do{  }while(0)
+#define REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env)   do{  }while(0)
+#define REG_SW_SET_HOSTBUF_IDX_PROFILING(env, val)   do{  }while(0)
+#else
+#include <linux/spinlock.h>
+#include "ecrnx_defs.h"
+#include "ecrnx_prof.h"
+#endif
+
+#include "reg_ipc_app.h"
+#include "ipc_host.h"
+
+#ifdef CONFIG_ECRNX_ESWIN
+#include "eswin_utils.h"
+#endif
+
+/*
+ * TYPES DEFINITION
+ ******************************************************************************
+ */
+
+const int nx_txdesc_cnt[] =
+{
+    NX_TXDESC_CNT0,
+    NX_TXDESC_CNT1,
+    NX_TXDESC_CNT2,
+    NX_TXDESC_CNT3,
+    #if NX_TXQ_CNT == 5
+    NX_TXDESC_CNT4,
+    #endif
+};
+
+const int nx_txdesc_cnt_msk[] =
+{
+    NX_TXDESC_CNT0 - 1,
+    NX_TXDESC_CNT1 - 1,
+    NX_TXDESC_CNT2 - 1,
+    NX_TXDESC_CNT3 - 1,
+    #if NX_TXQ_CNT == 5
+    NX_TXDESC_CNT4 - 1,
+    #endif
+};
+
+const int nx_txuser_cnt[] =
+{
+    CONFIG_USER_MAX,
+    CONFIG_USER_MAX,
+    CONFIG_USER_MAX,
+    CONFIG_USER_MAX,
+    #if NX_TXQ_CNT == 5
+    1,
+    #endif
+};
+
+
+/*
+ * FUNCTIONS DEFINITIONS
+ ******************************************************************************
+ */
+/**
+ * ipc_host_rxdesc_handler() - Handle the reception of a Rx Descriptor
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_RXDESC is set
+ */
+static void ipc_host_rxdesc_handler(struct ipc_host_env_tag *env)
+{
+    // For profiling
+    REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_RXDESC);
+
+    // LMAC has triggered an IT saying that a reception has occurred.
+    // Then we first need to check the validity of the current hostbuf, and the validity
+    // of the next hostbufs too, because it is likely that several hostbufs have been
+    // filled within the time needed for this irq handling
+    do {
+        #ifdef CONFIG_ECRNX_FULLMAC
+        // call the external function to indicate that a RX descriptor is received
+        if (env->cb.recv_data_ind(env->pthis,
+                                  env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].hostid) != 0)
+        #else
+        // call the external function to indicate that a RX packet is received
+        if (env->cb.recv_data_ind(env->pthis,
+                                  env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].hostid) != 0)
+        #endif //(CONFIG_ECRNX_FULLMAC)
+            break;
+
+    }while(1);
+
+    // For profiling
+    REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_RXDESC);
+}
+
+/**
+ * ipc_host_radar_handler() - Handle the reception of radar events
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_RADAR is set
+ */
+static void ipc_host_radar_handler(struct ipc_host_env_tag *env)
+{
+#ifdef CONFIG_ECRNX_RADAR
+    // LMAC has triggered an IT saying that a radar event has been sent to upper layer.
+    // Then we first need to check the validity of the current msg buf, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    spin_lock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+    while (env->cb.recv_radar_ind(env->pthis,
+              env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].hostid) == 0)
+        ;
+    spin_unlock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+#endif /* CONFIG_ECRNX_RADAR */
+}
+
+/**
+ * ipc_host_unsup_rx_vec_handler() - Handle the reception of unsupported rx vector
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_UNSUP_RX_VEC is set
+ */
+static void ipc_host_unsup_rx_vec_handler(struct ipc_host_env_tag *env)
+{
+    while (env->cb.recv_unsup_rx_vec_ind(env->pthis,
+              env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].hostid) == 0)
+        ;
+}
+
+/**
+ * ipc_host_msg_handler() - Handler for firmware message
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_MSG is set
+ */
+static void ipc_host_msg_handler(struct ipc_host_env_tag *env)
+{
+    // For profiling
+    REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_MSG);
+
+    // LMAC has triggered an IT saying that a message has been sent to upper layer.
+    // Then we first need to check the validity of the current msg buf, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    while (env->cb.recv_msg_ind(env->pthis,
+                    env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].hostid) == 0)
+        ;
+
+
+    // For profiling
+    REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_MSG);
+
+}
+
+/**
+ * ipc_host_msgack_handler() - Handle the reception of message acknowledgement
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_MSG_ACK is set
+ */
+static void ipc_host_msgack_handler(struct ipc_host_env_tag *env)
+{
+    void *hostid = env->msga2e_hostid;
+
+    ASSERT_ERR(hostid);
+    ASSERT_ERR(env->msga2e_cnt == (((struct lmac_msg *)(&env->shared->msg_a2e_buf.msg))->src_id & 0xFF));
+
+    env->msga2e_hostid = NULL;
+    env->msga2e_cnt++;
+    env->cb.recv_msgack_ind(env->pthis, hostid);
+}
+
+/**
+ * ipc_host_dbg_handler() - Handle the reception of Debug event
+ *
+ * @env: pointer to the IPC Host environment
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_DBG is set
+ */
+static void ipc_host_dbg_handler(struct ipc_host_env_tag *env)
+{
+    // For profiling
+    REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_DBG);
+
+    // LMAC has triggered an IT saying that a DBG message has been sent to upper layer.
+    // Then we first need to check the validity of the current buffer, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    while(env->cb.recv_dbg_ind(env->pthis,
+            env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].hostid) == 0)
+        ;
+
+    // For profiling
+    REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_DBG);
+}
+
+/**
+ * ipc_host_tx_cfm_handler() - Handle the reception of TX confirmation
+ *
+ * @env: pointer to the IPC Host environment
+ * @queue_idx: index of the hardware on which the confirmation has been received
+ * @user_pos: index of the user position
+ *
+ * Called from general IRQ handler when status %IPC_IRQ_E2A_TXCFM is set
+ */
+static void ipc_host_tx_cfm_handler(struct ipc_host_env_tag *env,
+                                    const int queue_idx, const int user_pos)
+{
+    // TX confirmation descriptors have been received
+    REG_SW_SET_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+    while (1)
+    {
+        // Get the used index and increase it. We do the increase before knowing if the
+        // current buffer is confirmed because the callback function may call the
+        // ipc_host_txdesc_get() in case flow control was enabled and the index has to be
+        // already at the good value to ensure that the test of FIFO full is correct
+        uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos]++;
+        uint32_t used_idx_mod = used_idx & nx_txdesc_cnt_msk[queue_idx];
+        void *host_id = env->tx_host_id[queue_idx][user_pos][used_idx_mod];
+
+        // Reset the host id in the array
+        env->tx_host_id[queue_idx][user_pos][used_idx_mod] = 0;
+
+        // call the external function to indicate that a TX packet is freed
+        if (host_id == 0)
+        {
+            // No more confirmations, so put back the used index at its initial value
+            env->txdesc_used_idx[queue_idx][user_pos] = used_idx;
+            break;
+        }
+
+        if (env->cb.send_data_cfm(env->pthis, host_id) != 0)
+        {
+            // No more confirmations, so put back the used index at its initial value
+            env->txdesc_used_idx[queue_idx][user_pos] = used_idx;
+            env->tx_host_id[queue_idx][user_pos][used_idx_mod] = host_id;
+            // and exit the loop
+            break;
+        }
+
+        REG_SW_SET_PROFILING_CHAN(env->pthis, SW_PROF_CHAN_CTXT_CFM_HDL_BIT);
+        REG_SW_CLEAR_PROFILING_CHAN(env->pthis, SW_PROF_CHAN_CTXT_CFM_HDL_BIT);
+    }
+
+    REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IRQ_E2A_TXCFM);
+}
+
+/**
+ ******************************************************************************
+ */
+bool ipc_host_tx_frames_pending(struct ipc_host_env_tag *env)
+{
+    int i, j;
+    bool tx_frames_pending = false;
+
+    for (i = 0; (i < IPC_TXQUEUE_CNT) && !tx_frames_pending; i++)
+    {
+        for (j = 0; j < nx_txuser_cnt[i]; j++)
+        {
+            uint32_t used_idx = env->txdesc_used_idx[i][j];
+            uint32_t free_idx = env->txdesc_free_idx[i][j];
+
+            // Check if this queue is empty or not
+            if (used_idx != free_idx)
+            {
+                // The queue is not empty, update the flag and exit
+                tx_frames_pending = true;
+                break;
+            }
+        }
+    }
+
+    return (tx_frames_pending);
+}
+
+/**
+ ******************************************************************************
+ */
+void *ipc_host_tx_flush(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos)
+{
+    uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos];
+    void *host_id = env->tx_host_id[queue_idx][user_pos][used_idx & nx_txdesc_cnt_msk[queue_idx]];
+
+    // call the external function to indicate that a TX packet is freed
+    if (host_id != 0)
+    {
+        // Reset the host id in the array
+        env->tx_host_id[queue_idx][user_pos][used_idx & nx_txdesc_cnt_msk[queue_idx]] = 0;
+
+        // Increment the used index
+        env->txdesc_used_idx[queue_idx][user_pos]++;
+    }
+
+    return (host_id);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_init(struct ipc_host_env_tag *env,
+                  struct ipc_host_cb_tag *cb,
+                  struct ipc_shared_env_tag *shared_env_ptr,
+                  void *pthis)
+{
+    unsigned int i;
+    unsigned int size;
+    unsigned int * dst;
+
+    // Reset the environments
+    // Reset the IPC Shared memory
+#if 0
+    /* check potential platform bug on multiple stores */
+    memset(shared_env_ptr, 0, sizeof(struct ipc_shared_env_tag));
+#else
+    dst = (unsigned int *)shared_env_ptr;
+    size = (unsigned int)sizeof(struct ipc_shared_env_tag);
+    for (i=0; i < size; i+=4)
+    {
+        *dst++ = 0;
+    }
+#endif
+    // Reset the IPC Host environment
+    memset(env, 0, sizeof(struct ipc_host_env_tag));
+
+    // Initialize the shared environment pointer
+    env->shared = shared_env_ptr;
+
+    // Save the callbacks in our own environment
+    env->cb = *cb;
+
+    // Save the pointer to the register base
+    env->pthis = pthis;
+
+    // Initialize buffers numbers and buffers sizes needed for DMA Receptions
+    env->rx_bufnb = IPC_RXBUF_CNT;
+    #ifdef CONFIG_ECRNX_FULLMAC
+    env->rxdesc_nb = IPC_RXDESC_CNT;
+    #endif //(CONFIG_ECRNX_FULLMAC)
+    env->radar_bufnb = IPC_RADARBUF_CNT;
+    env->radar_bufsz = sizeof(struct radar_pulse_array_desc);
+    env->unsuprxvec_bufnb = IPC_UNSUPRXVECBUF_CNT;
+    env->unsuprxvec_bufsz = max(sizeof(struct rx_vector_desc), (size_t) RADIOTAP_HDR_MAX_LEN) +
+                            RADIOTAP_HDR_VEND_MAX_LEN +  UNSUP_RX_VEC_DATA_LEN;
+    env->ipc_e2amsg_bufnb = IPC_MSGE2A_BUF_CNT;
+    env->ipc_e2amsg_bufsz = sizeof(struct ipc_e2a_msg);
+    env->ipc_dbg_bufnb = IPC_DBGBUF_CNT;
+    env->ipc_dbg_bufsz = sizeof(struct ipc_dbg_msg);
+
+    for (i = 0; i < CONFIG_USER_MAX; i++)
+    {
+        // Initialize the pointers to the hostid arrays
+        env->tx_host_id[0][i] = env->tx_host_id0[i];
+        env->tx_host_id[1][i] = env->tx_host_id1[i];
+        env->tx_host_id[2][i] = env->tx_host_id2[i];
+        env->tx_host_id[3][i] = env->tx_host_id3[i];
+        #if NX_TXQ_CNT == 5
+        env->tx_host_id[4][i] = NULL;
+        #endif
+
+        // Initialize the pointers to the TX descriptor arrays
+        env->txdesc[0][i] = shared_env_ptr->txdesc0[i];
+        env->txdesc[1][i] = shared_env_ptr->txdesc1[i];
+        env->txdesc[2][i] = shared_env_ptr->txdesc2[i];
+        env->txdesc[3][i] = shared_env_ptr->txdesc3[i];
+        #if NX_TXQ_CNT == 5
+        env->txdesc[4][i] = NULL;
+        #endif
+    }
+
+    #if NX_TXQ_CNT == 5
+    env->tx_host_id[4][0] = env->tx_host_id4[0];
+    env->txdesc[4][0] = shared_env_ptr->txdesc4[0];
+    #endif
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_patt_addr_push(struct ipc_host_env_tag *env, uint32_t addr)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Copy the address
+    shared_env_ptr->pattern_addr = addr;
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_rxbuf_push(struct ipc_host_env_tag *env,
+#ifdef CONFIG_ECRNX_SOFTMAC
+                        void *hostid,
+#else
+                        uint32_t hostid,
+#endif
+                        uint32_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    REG_SW_CLEAR_HOSTBUF_IDX_PROFILING(env->pthis);
+    REG_SW_SET_HOSTBUF_IDX_PROFILING(env->pthis, env->ipc_host_rxbuf_idx);
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx].hostid   = hostid;
+    shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx].dma_addr = hostbuf;
+#else
+    // Save the hostid and the hostbuf in global array
+    env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].hostid = hostid;
+    env->ipc_host_rxbuf_array[env->ipc_host_rxbuf_idx].dma_addr = hostbuf;
+
+    shared_env_ptr->host_rxbuf[env->ipc_host_rxbuf_idx] = hostbuf;
+#endif //(CONFIG_ECRNX_FULLMAC)
+
+    // Signal to the embedded CPU that at least one buffer is available
+    ipc_app2emb_trigger_set(shared_env_ptr, IPC_IRQ_A2E_RXBUF_BACK);
+
+    // Increment the array index
+    env->ipc_host_rxbuf_idx = (env->ipc_host_rxbuf_idx +1)%IPC_RXBUF_CNT;
+
+    return (0);
+}
+
+#ifdef CONFIG_ECRNX_FULLMAC
+/**
+ ******************************************************************************
+ */
+int ipc_host_rxdesc_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint32_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Reset the RX Descriptor DMA Address and increment the counter
+    env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].dma_addr = hostbuf;
+    env->ipc_host_rxdesc_array[env->ipc_host_rxdesc_idx].hostid = hostid;
+
+    shared_env_ptr->host_rxdesc[env->ipc_host_rxdesc_idx].dma_addr = hostbuf;
+
+    // Signal to the embedded CPU that at least one descriptor is available
+    ipc_app2emb_trigger_set(shared_env_ptr, IPC_IRQ_A2E_RXDESC_BACK);
+
+    env->ipc_host_rxdesc_idx = (env->ipc_host_rxdesc_idx + 1) % IPC_RXDESC_CNT;
+
+    return (0);
+}
+#endif /* CONFIG_ECRNX_FULLMAC */
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_radarbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                           uint32_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Save the hostid and the hostbuf in global array
+    env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].hostid = hostid;
+    env->ipc_host_radarbuf_array[env->ipc_host_radarbuf_idx].dma_addr = hostbuf;
+
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->radarbuf_hostbuf[env->ipc_host_radarbuf_idx] = hostbuf;
+
+    // Increment the array index
+    env->ipc_host_radarbuf_idx = (env->ipc_host_radarbuf_idx +1)%IPC_RADARBUF_CNT;
+
+    return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+
+int ipc_host_unsup_rx_vec_buf_push(struct ipc_host_env_tag *env,
+                                   void *hostid,
+                                   uint32_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].hostid = hostid;
+    env->ipc_host_unsuprxvecbuf_array[env->ipc_host_unsuprxvecbuf_idx].dma_addr = hostbuf;
+
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->unsuprxvecbuf_hostbuf[env->ipc_host_unsuprxvecbuf_idx] = hostbuf;
+
+    // Increment the array index
+    env->ipc_host_unsuprxvecbuf_idx = (env->ipc_host_unsuprxvecbuf_idx + 1)%IPC_UNSUPRXVECBUF_CNT;
+
+    return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_msgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint64_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Save the hostid and the hostbuf in global array
+    env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].hostid = hostid;
+    env->ipc_host_msgbuf_array[env->ipc_host_msge2a_idx].dma_addr = hostbuf;
+
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->msg_e2a_hostbuf_addr[env->ipc_host_msge2a_idx] = hostbuf;
+
+    // Increment the array index
+    env->ipc_host_msge2a_idx = (env->ipc_host_msge2a_idx +1)%IPC_MSGE2A_BUF_CNT;
+
+    return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_dbgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint64_t hostbuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Save the hostid and the hostbuf in global array
+    env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].hostid = hostid;
+    env->ipc_host_dbgbuf_array[env->ipc_host_dbg_idx].dma_addr = hostbuf;
+
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->dbg_hostbuf_addr[env->ipc_host_dbg_idx] = hostbuf;
+
+    // Increment the array index
+    env->ipc_host_dbg_idx = (env->ipc_host_dbg_idx +1)%IPC_DBGBUF_CNT;
+
+    return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_dbginfobuf_push(struct ipc_host_env_tag *env, uint32_t infobuf)
+{
+    struct ipc_shared_env_tag *shared_env_ptr = env->shared;
+
+    // Copy the hostbuf (DMA address) in the ipc shared memory
+    shared_env_ptr->la_dbginfo_addr = infobuf;
+}
+
+/**
+ ******************************************************************************
+ */
+volatile struct txdesc_host *ipc_host_txdesc_get(struct ipc_host_env_tag *env, const int queue_idx, const int user_pos)
+{
+    volatile struct txdesc_host *txdesc_free;
+    uint32_t used_idx = env->txdesc_used_idx[queue_idx][user_pos];
+    uint32_t free_idx = env->txdesc_free_idx[queue_idx][user_pos];
+
+    ASSERT_ERR(queue_idx < IPC_TXQUEUE_CNT);
+    ASSERT_ERR((free_idx - used_idx) <= nx_txdesc_cnt[queue_idx]);
+
+    // Check if a free descriptor is available
+    if (free_idx != (used_idx + nx_txdesc_cnt[queue_idx]))
+    {
+        // Get the pointer to the first free descriptor
+        txdesc_free = env->txdesc[queue_idx][user_pos] + (free_idx & nx_txdesc_cnt_msk[queue_idx]);
+    }
+    else
+    {
+        txdesc_free = NULL;
+    }
+
+    return txdesc_free;
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_txdesc_push(struct ipc_host_env_tag *env, const int queue_idx,
+                          const int user_pos, void *host_id)
+{
+    uint32_t free_idx = env->txdesc_free_idx[queue_idx][user_pos] & nx_txdesc_cnt_msk[queue_idx];
+    volatile struct txdesc_host *txdesc_pushed = env->txdesc[queue_idx][user_pos] + free_idx;
+
+
+    // Descriptor is now ready
+    txdesc_pushed->ready = 0xFFFFFFFF;
+
+    // Save the host id in the environment
+    env->tx_host_id[queue_idx][user_pos][free_idx] = host_id;
+
+    // Increment the index
+    env->txdesc_free_idx[queue_idx][user_pos]++;
+
+    // trigger interrupt!!!
+    //REG_SW_SET_PROFILING(env->pthis, CO_BIT(queue_idx+SW_PROF_IRQ_A2E_TXDESC_FIRSTBIT));
+    ipc_app2emb_trigger_setf(env->shared, CO_BIT(user_pos + queue_idx * CONFIG_USER_MAX +
+                                                 IPC_IRQ_A2E_TXDESC_FIRSTBIT));
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_irq(struct ipc_host_env_tag *env, uint32_t status)
+{
+    // Acknowledge the pending interrupts
+    ipc_emb2app_ack_clear(env->shared, status);
+    // And re-read the status, just to be sure that the acknowledgment is
+    // effective when we start the interrupt handling
+    ipc_emb2app_status_get(env->shared);
+
+    // Optimized for only one IRQ at a time
+    if (status & IPC_IRQ_E2A_RXDESC)
+    {
+        // handle the RX descriptor reception
+        ipc_host_rxdesc_handler(env);
+    }
+    if (status & IPC_IRQ_E2A_MSG_ACK)
+    {
+        ipc_host_msgack_handler(env);
+    }
+    if (status & IPC_IRQ_E2A_MSG)
+    {
+        ipc_host_msg_handler(env);
+    }
+    if (status & IPC_IRQ_E2A_TXCFM)
+    {
+        int i;
+
+#ifdef __KERNEL__
+        spin_lock(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+#endif
+        // handle the TX confirmation reception
+        for (i = 0; i < IPC_TXQUEUE_CNT; i++)
+        {
+            int j = 0;
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+            for (; j < nx_txuser_cnt[i]; j++)
+#endif
+            {
+                uint32_t q_bit = CO_BIT(j + i * CONFIG_USER_MAX + IPC_IRQ_E2A_TXCFM_POS);
+                if (status & q_bit)
+                {
+                    // handle the confirmation
+                    ipc_host_tx_cfm_handler(env, i, j);
+                }
+            }
+        }
+#ifdef __KERNEL__
+        spin_unlock(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+#endif
+    }
+    if (status & IPC_IRQ_E2A_RADAR)
+    {
+        // handle the radar event reception
+        ipc_host_radar_handler(env);
+    }
+
+    if (status & IPC_IRQ_E2A_UNSUP_RX_VEC)
+    {
+        // handle the unsupported rx vector reception
+        ipc_host_unsup_rx_vec_handler(env);
+    }
+
+    if (status & IPC_IRQ_E2A_DBG)
+    {
+        ipc_host_dbg_handler(env);
+    }
+
+    if (status & IPC_IRQ_E2A_TBTT_PRIM)
+    {
+        env->cb.prim_tbtt_ind(env->pthis);
+    }
+
+    if (status & IPC_IRQ_E2A_TBTT_SEC)
+    {
+        env->cb.sec_tbtt_ind(env->pthis);
+    }
+}
+
+/**
+ ******************************************************************************
+ */
+int ipc_host_msg_push(struct ipc_host_env_tag *env, void *msg_buf, uint16_t len)
+{
+#ifndef CONFIG_ECRNX_ESWIN
+    int i;
+    uint32_t *src, *dst;
+#endif
+
+    REG_SW_SET_PROFILING(env->pthis, SW_PROF_IPC_MSGPUSH);
+
+    ASSERT_ERR(!env->msga2e_hostid);
+    ASSERT_ERR(round_up(len, 4) <= sizeof(env->shared->msg_a2e_buf.msg));
+
+#ifndef CONFIG_ECRNX_ESWIN
+    // Copy the message into the IPC MSG buffer
+#ifdef __KERNEL__
+    src = (uint32_t*)((struct ecrnx_cmd *)msg_buf)->a2e_msg;
+#else
+    src = (uint32_t*) msg_buf;
+#endif
+    dst = (uint32_t*)&(env->shared->msg_a2e_buf.msg);
+
+    // Copy the message in the IPC queue
+    for (i=0; i<len; i+=4)
+    {
+        *dst++ = *src++;
+    }
+
+    env->msga2e_hostid = msg_buf;
+
+    // Trigger the irq to send the message to EMB
+    ipc_app2emb_trigger_set(env->shared, IPC_IRQ_A2E_MSG);
+#else
+    env->msga2e_hostid = msg_buf;
+    ecrnx_msg_send(msg_buf, len);
+#endif
+
+    REG_SW_CLEAR_PROFILING(env->pthis, SW_PROF_IPC_MSGPUSH);
+
+    return (0);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_enable_irq(struct ipc_host_env_tag *env, uint32_t value)
+{
+    // Enable the handled interrupts
+    ipc_emb2app_unmask_set(env->shared, value);
+}
+
+/**
+ ******************************************************************************
+ */
+void ipc_host_disable_irq(struct ipc_host_env_tag *env, uint32_t value)
+{
+    // Enable the handled interrupts
+    ipc_emb2app_unmask_clear(env->shared, value);
+}
+
+/**
+ ******************************************************************************
+ */
+uint32_t ipc_host_get_status(struct ipc_host_env_tag *env)
+{
+    volatile uint32_t status;
+
+    status = ipc_emb2app_status_get(env->shared);
+
+    return status;
+}
+
+/**
+ ******************************************************************************
+ */
+uint32_t ipc_host_get_rawstatus(struct ipc_host_env_tag *env)
+{
+    volatile uint32_t rawstatus;
+
+    rawstatus = ipc_emb2app_rawstatus_get(env->shared);
+
+    return rawstatus;
+}
+
diff --git a/drivers/net/wireless/eswin/ipc_host.h b/drivers/net/wireless/eswin/ipc_host.h
new file mode 100644 (file)
index 0000000..127be2a
--- /dev/null
@@ -0,0 +1,517 @@
+/**
+ ******************************************************************************
+ *
+ * @file ipc_host.h
+ *
+ * @brief IPC module.
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _IPC_HOST_H_
+#define _IPC_HOST_H_
+
+/*
+ * INCLUDE FILES
+ ******************************************************************************
+ */
+#include "ipc_shared.h"
+#ifndef __KERNEL__
+#include "arch.h"
+#else
+#include "ipc_compat.h"
+#endif
+
+/*
+ * ENUMERATION
+ ******************************************************************************
+ */
+
+enum ipc_host_desc_status
+{
+    /// Descriptor is IDLE
+    IPC_HOST_DESC_IDLE      = 0,
+    /// Data can be forwarded
+    IPC_HOST_DESC_FORWARD,
+    /// Data has to be kept in UMAC memory
+    IPC_HOST_DESC_KEEP,
+    /// Delete stored packet
+    IPC_HOST_DESC_DELETE,
+    /// Update Frame Length status
+    IPC_HOST_DESC_LEN_UPDATE,
+};
+
+/**
+ ******************************************************************************
+ * @brief This structure is used to initialize the MAC SW
+ *
+ * The WLAN device driver provides functions call-back with this structure
+ ******************************************************************************
+ */
+struct ipc_host_cb_tag
+{
+    /// WLAN driver call-back function: send_data_cfm
+    int (*send_data_cfm)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: handle_data_cfm
+    int (*handle_data_cfm)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_data_ind
+    uint8_t (*recv_data_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_radar_ind
+    uint8_t (*recv_radar_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_unsup_rx_vec_ind
+    uint8_t (*recv_unsup_rx_vec_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_msg_ind
+    uint8_t (*recv_msg_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_msgack_ind
+    uint8_t (*recv_msgack_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: recv_dbg_ind
+    uint8_t (*recv_dbg_ind)(void *pthis, void *host_id);
+
+    /// WLAN driver call-back function: prim_tbtt_ind
+    void (*prim_tbtt_ind)(void *pthis);
+
+    /// WLAN driver call-back function: sec_tbtt_ind
+    void (*sec_tbtt_ind)(void *pthis);
+
+};
+
+/*
+ * Struct used to store information about host buffers (DMA Address and local pointer)
+ */
+struct ipc_hostbuf
+{
+    void    *hostid;     ///< ptr to hostbuf client (ipc_host client) structure
+    uint64_t dma_addr;   ///< ptr to real hostbuf dma address
+};
+
+/// Definition of the IPC Host environment structure.
+struct ipc_host_env_tag
+{
+    /// Structure containing the callback pointers
+    struct ipc_host_cb_tag cb;
+
+    /// Pointer to the shared environment
+    struct ipc_shared_env_tag *shared;
+
+    #ifdef CONFIG_ECRNX_FULLMAC
+    // Array used to store the descriptor addresses
+    struct ipc_hostbuf ipc_host_rxdesc_array[IPC_RXDESC_CNT];
+    // Index of the host RX descriptor array (ipc_shared environment)
+    uint8_t ipc_host_rxdesc_idx;
+    /// Store the number of RX Descriptors
+    uint8_t rxdesc_nb;
+    #endif //(CONFIG_ECRNX_FULLMAC)
+
+    /// Fields for Data Rx handling
+    #ifdef CONFIG_ECRNX_SOFTMAC
+    // Global array used to store the hostid and hostbuf addresses
+    struct ipc_hostbuf ipc_host_rxbuf_array[IPC_RXBUF_CNT];
+    #endif // CONFIG_ECRNX_SOFTMAC
+    // Index used for ipc_host_rxbuf_array to point to current buffer
+    uint8_t ipc_host_rxbuf_idx;
+    // Store the number of Rx Data buffers
+    uint32_t rx_bufnb;
+    // Store the size of the Rx Data buffers
+    uint32_t rx_bufsz;
+
+    /// Fields for Radar events handling
+    // Global array used to store the hostid and hostbuf addresses
+    struct ipc_hostbuf ipc_host_radarbuf_array[IPC_RADARBUF_CNT];
+    // Index used for ipc_host_rxbuf_array to point to current buffer
+    uint8_t ipc_host_radarbuf_idx;
+    // Store the number of radar event buffers
+    uint32_t radar_bufnb;
+    // Store the size of the radar event buffers
+    uint32_t radar_bufsz;
+
+    ///Fields for Unsupported frame handling
+    // Global array used to store the hostid and hostbuf addresses
+    struct ipc_hostbuf ipc_host_unsuprxvecbuf_array[IPC_UNSUPRXVECBUF_CNT];
+    // Index used for ipc_host_unsuprxvecbuf_array to point to current buffer
+    uint8_t ipc_host_unsuprxvecbuf_idx;
+    // Store the number of unsupported rx vector buffers
+    uint32_t unsuprxvec_bufnb;
+    // Store the size of unsupported rx vector buffers
+    uint32_t unsuprxvec_bufsz;
+
+    // Index used that points to the first free TX desc
+    uint32_t txdesc_free_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+    // Index used that points to the first used TX desc
+    uint32_t txdesc_used_idx[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+    // Array storing the currently pushed host ids for the BK queue
+    void *tx_host_id0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
+    // Array storing the currently pushed host ids for the BE queue
+    void *tx_host_id1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
+    // Array storing the currently pushed host ids for the VI queue
+    void *tx_host_id2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
+    // Array storing the currently pushed host ids for the VO queue
+    void *tx_host_id3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
+    #if NX_TXQ_CNT == 5
+    // Array storing the currently pushed host ids for the BCN queue
+    void *tx_host_id4[1][NX_TXDESC_CNT4];
+    #endif
+    // Pointer to the different host ids arrays, per IPC queue
+    void **tx_host_id[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+    // Pointer to the different TX descriptor arrays, per IPC queue
+    volatile struct txdesc_host *txdesc[IPC_TXQUEUE_CNT][CONFIG_USER_MAX];
+
+    /// Fields for Emb->App MSGs handling
+    // Global array used to store the hostid and hostbuf addresses for msg/ind
+    struct ipc_hostbuf ipc_host_msgbuf_array[IPC_MSGE2A_BUF_CNT];
+    // Index of the MSG E2A buffers array to point to current buffer
+    uint8_t ipc_host_msge2a_idx;
+    // Store the number of E2A MSG buffers
+    uint32_t ipc_e2amsg_bufnb;
+    // Store the size of the E2A MSG buffers
+    uint32_t ipc_e2amsg_bufsz;
+
+    /// E2A ACKs of A2E MSGs
+    uint8_t msga2e_cnt;
+    void *msga2e_hostid;
+
+    /// Fields for Debug MSGs handling
+    // Global array used to store the hostid and hostbuf addresses for Debug messages
+    struct ipc_hostbuf ipc_host_dbgbuf_array[IPC_DBGBUF_CNT];
+    // Index of the Debug messages buffers array to point to current buffer
+    uint8_t ipc_host_dbg_idx;
+    // Store the number of Debug messages buffers
+    uint32_t ipc_dbg_bufnb;
+    // Store the size of the Debug messages buffers
+    uint32_t ipc_dbg_bufsz;
+
+    /// Pointer to the attached object (used in callbacks and register accesses)
+    void *pthis;
+};
+
+extern const int nx_txdesc_cnt[];
+extern const int nx_txuser_cnt[];
+
+/**
+ ******************************************************************************
+ * @brief Returns the full/not full status of the queue the index of which is
+ * passed as parameter.
+ *
+ * @param[in]   env       Pointer to the IPC host environment
+ * @param[in]   queue_idx Index of the queue to be checked
+ *
+ * @return true if the queue is full, false otherwise
+ *
+ ******************************************************************************
+ */
+__INLINE bool ipc_host_queue_full(struct ipc_host_env_tag *env,
+                                  const int queue_idx)
+{
+    return (env->txdesc_free_idx[queue_idx] ==
+                      (env->txdesc_used_idx[queue_idx] + nx_txdesc_cnt[queue_idx]));
+}
+
+/**
+ ******************************************************************************
+ * @brief Initialize the IPC running on the Application CPU.
+ *
+ * This function:
+ *   - initializes the IPC software environments
+ *   - enables the interrupts in the IPC block
+ *
+ * @param[in]   env   Pointer to the IPC host environment
+ *
+ * @warning Since this function resets the IPC Shared memory, it must be called
+ * before the LMAC FW is launched because LMAC sets some init values in IPC
+ * Shared memory at boot.
+ *
+ ******************************************************************************
+ */
+void ipc_host_init(struct ipc_host_env_tag *env,
+                  struct ipc_host_cb_tag *cb,
+                  struct ipc_shared_env_tag *shared_env_ptr,
+                  void *pthis);
+
+/** @addtogroup IPC_TX
+ *  @{
+ */
+
+/**
+ ******************************************************************************
+ * @brief Retrieve a new free Tx descriptor (host side).
+ *
+ * This function returns a pointer to the next Tx descriptor available from the
+ * queue queue_idx to the host driver. The driver will have to fill it with the
+ * appropriate endianness and to send it to the
+ * emb side with ipc_host_txdesc_push().
+ *
+ * This function should only be called once until ipc_host_txdesc_push() is called.
+ *
+ * This function will return NULL if the queue is full.
+ *
+ * @param[in]   env   Pointer to the IPC host environment
+ * @param[in]   queue_idx   Queue index. The index can be inferred from the
+ *                          user priority of the incoming packet.
+ * @param[in]   user_pos    User position. If MU-MIMO is not used, this value
+ *                          shall be 0.
+ * @return                  Pointer to the next Tx descriptor free. This can
+ *                          point to the host memory or to shared memory,
+ *                          depending on IPC implementation.
+ *
+ ******************************************************************************
+ */
+volatile struct txdesc_host *ipc_host_txdesc_get(struct ipc_host_env_tag *env,
+                                                 const int queue_idx,
+                                                 const int user_pos);
+
+
+/**
+ ******************************************************************************
+ * @brief Push a filled Tx descriptor (host side).
+ *
+ * This function sets the next Tx descriptor available by the host side:
+ * - as used for the host side
+ * - as available for the emb side.
+ * The Tx descriptor must be correctly filled before calling this function.
+ *
+ * This function may trigger an IRQ to the emb CPU depending on the interrupt
+ * mitigation policy and on the push count.
+ *
+ * @param[in]   env   Pointer to the IPC host environment
+ * @param[in]   queue_idx   Queue index. Same value than ipc_host_txdesc_get()
+ * @param[in]   user_pos    User position. If MU-MIMO is not used, this value
+ *                          shall be 0.
+ * @param[in]   host_id     Parameter indicated by the IPC at TX confirmation,
+ *                          that allows the driver finding the buffer
+ *
+ ******************************************************************************
+ */
+void ipc_host_txdesc_push(struct ipc_host_env_tag *env, const int queue_idx,
+                          const int user_pos, void *host_id);
+
+
+/**
+ ******************************************************************************
+ * @brief Check if there are TX frames pending in the TX queues.
+ *
+ * @param[in]   env   Pointer to the IPC host environment
+ *
+ * @return true if there are frames pending, false otherwise.
+ *
+ ******************************************************************************
+ */
+bool ipc_host_tx_frames_pending(struct ipc_host_env_tag *env);
+
+/**
+ ******************************************************************************
+ * @brief Get and flush a packet from the IPC queue passed as parameter.
+ *
+ * @param[in]   env        Pointer to the IPC host environment
+ * @param[in]   queue_idx  Index of the queue to flush
+ * @param[in]   user_pos   User position to flush
+ *
+ * @return The flushed hostid if there is one, 0 otherwise.
+ *
+ ******************************************************************************
+ */
+void *ipc_host_tx_flush(struct ipc_host_env_tag *env, const int queue_idx,
+                        const int user_pos);
+
+/// @} IPC_TX
+
+/** @addtogroup IPC_RX
+ *  @{
+ */
+void ipc_host_patt_addr_push(struct ipc_host_env_tag *env, uint32_t addr);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for Rx packet (host side)
+ *
+ * This function should be called by the host IRQ handler to supply the
+ * embedded side with new empty buffer.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Packet ID used by the host (skbuff pointer on Linux)
+ * @param[in]   hostbuf     Pointer to the start of the buffer payload in the
+ *                          host memory (this may be inferred from the skbuff?)
+ *                          The length of this buffer should be predefined
+ *                          between host and emb statically (constant needed?).
+ *
+ ******************************************************************************
+ */
+int ipc_host_rxbuf_push(struct ipc_host_env_tag *env,
+#ifdef CONFIG_ECRNX_SOFTMAC
+                        void *hostid,
+#else
+                        uint32_t hostid,
+#endif
+                        uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated Descriptor
+ *
+ * This function should be called by the host IRQ handler to supply the
+ * embedded side with new empty buffer.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Address of packet for host
+ * @param[in]   hostbuf     Pointer to the start of the buffer payload in the
+ *                          host memory. The length of this buffer should be
+ *                          predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_rxdesc_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated radar event buffer descriptor
+ *
+ * This function is called at Init time to initialize all radar event buffers.
+ * Then each time embedded send a radar event, this function is used to push
+ * back the same buffer once it has been handled.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Address of packet for host
+ * @param[in]   hostbuf     Pointer to the start of the buffer payload in the
+ *                          host memory. The length of this buffer should be
+ *                          predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_radarbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                           uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated unsupported rx vector buffer descriptor
+ *
+ * This function is called at Init time to initialize all unsupported rx vector
+ * buffers. Then each time the embedded sends a unsupported rx vector, this
+ * function is used to push a new unsupported rx vector buffer.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Address of packet for host
+ * @param[in]   hostbuf     Pointer to the start of the buffer payload in the
+ *                          host memory. The length of this buffer should be
+ *                          predefined between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_unsup_rx_vec_buf_push(struct ipc_host_env_tag *env, void *hostid,
+                                    uint32_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for IPC MSGs (host side)
+ *
+ * This function is called at Init time to initialize all Emb2App messages
+ * buffers. Then each time embedded send a IPC message, this function is used
+ * to push back the same buffer once it has been handled.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Address of buffer for host
+ * @param[in]   hostbuf     Address of buffer for embedded
+ *                          The length of this buffer should be predefined
+ *                          between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_msgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint64_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push a pre-allocated buffer descriptor for Debug messages (host side)
+ *
+ * This function is called at Init time to initialize all debug messages.
+ * Then each time embedded send a debug message, this function is used to push
+ * back the same buffer once it has been handled.
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   hostid      Address of buffer for host
+ * @param[in]   hostbuf     Address of buffer for embedded
+ *                          The length of this buffer should be predefined
+ *                          between host and emb statically.
+ *
+ ******************************************************************************
+ */
+int ipc_host_dbgbuf_push(struct ipc_host_env_tag *env, void *hostid,
+                         uint64_t hostbuf);
+
+/**
+ ******************************************************************************
+ * @brief Push the pre-allocated logic analyzer and debug information buffer
+ *
+ * @param[in]   env         Pointer to the IPC host environment
+ * @param[in]   infobuf     Address of buffer for embedded
+ *                          The length of this buffer should be predefined
+ *                          between host and emb statically.
+ *
+ ******************************************************************************
+ */
+void ipc_host_dbginfobuf_push(struct ipc_host_env_tag *env, uint32_t infobuf);
+
+/// @} IPC_RX
+
+
+
+/** @addtogroup IPC_MISC
+ *  @{
+ */
+
+/**
+ ******************************************************************************
+ * @brief Handle all IPC interrupts on the host side.
+ *
+ * The following interrupts should be handled:
+ * Tx confirmation, Rx buffer requests, Rx packet ready and kernel messages
+ *
+ * @param[in]   env   Pointer to the IPC host environment
+ *
+ ******************************************************************************
+ */
+void ipc_host_irq(struct ipc_host_env_tag *env, uint32_t status);
+
+/**
+ ******************************************************************************
+ * @brief Send a message to the embedded side
+ *
+ * @param[in]   env      Pointer to the IPC host environment
+ * @param[in]   msg_buf  Pointer to the message buffer
+ * @param[in]   msg_len  Length of the message to be transmitted
+ *
+ * @return      Non-null value on failure
+ *
+ ******************************************************************************
+ */
+int ipc_host_msg_push(struct ipc_host_env_tag *env, void *msg_buf, uint16_t len);
+
+/**
+ ******************************************************************************
+ * @brief Enable IPC interrupts
+ *
+ * @param[in]   env  Global ipc_host environment pointer
+ * @param[in]   value  Bitfield of the interrupts to enable
+ *
+ * @warning After calling this function, IPC interrupts can be triggered at any
+ * time. Potentially, an interrupt could happen even before returning from the
+ * function if there is a request pending from the embedded side.
+ *
+ ******************************************************************************
+ */
+void ipc_host_enable_irq(struct ipc_host_env_tag *env, uint32_t value);
+void ipc_host_disable_irq(struct ipc_host_env_tag *env, uint32_t value);
+
+uint32_t ipc_host_get_status(struct ipc_host_env_tag *env);
+uint32_t ipc_host_get_rawstatus(struct ipc_host_env_tag *env);
+
+/// @} IPC_MISC
+
+
+#endif // _IPC_HOST_H_
diff --git a/drivers/net/wireless/eswin/ipc_shared.h b/drivers/net/wireless/eswin/ipc_shared.h
new file mode 100644 (file)
index 0000000..e6d7ebf
--- /dev/null
@@ -0,0 +1,836 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ipc_shared.h
+ *
+ * @brief Shared data between both IPC modules.
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _IPC_SHARED_H_
+#define _IPC_SHARED_H_
+
+/*
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+#include "ipc_compat.h"
+#include "lmac_mac.h"
+
+/*
+ * DEFINES AND MACROS
+ ****************************************************************************************
+ */
+#define CO_BIT(pos) (1U<<(pos))
+
+#define IPC_TXQUEUE_CNT     NX_TXQ_CNT
+#define NX_TXDESC_CNT0      8
+#define NX_TXDESC_CNT1      64
+#define NX_TXDESC_CNT2      64
+#define NX_TXDESC_CNT3      32
+#if NX_TXQ_CNT == 5
+#define NX_TXDESC_CNT4      8
+#endif
+
+/*
+ * Number of Host buffers available for Data Rx handling (through DMA)
+ */
+#define IPC_RXBUF_CNT       128
+
+/*
+ * Number of shared descriptors available for Data RX handling
+ */
+#define IPC_RXDESC_CNT      128
+
+/*
+ * Number of Host buffers available for Radar events handling (through DMA)
+ */
+#define IPC_RADARBUF_CNT       16
+
+/*
+ * Number of Host buffers available for unsupported Rx vectors handling (through DMA)
+ */
+#define IPC_UNSUPRXVECBUF_CNT       8
+
+/*
+ *  Size of RxVector
+ */
+#define IPC_RXVEC_SIZE      16
+
+/*
+ * Number of Host buffers available for Emb->App MSGs sending (through DMA)
+ */
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define IPC_MSGE2A_BUF_CNT       4
+#else
+#define IPC_MSGE2A_BUF_CNT       64
+#endif
+/*
+ * Number of Host buffers available for Debug Messages sending (through DMA)
+ */
+#define IPC_DBGBUF_CNT       32
+
+/*
+ * Length used in MSGs structures
+ */
+#define IPC_A2E_MSG_BUF_SIZE    127 // size in 4-byte words
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define IPC_E2A_MSG_SIZE_BASE   15 // size in 4-byte words
+#else
+#define IPC_E2A_MSG_SIZE_BASE   256 // size in 4-byte words
+#endif
+
+#ifdef CONFIG_ECRNX_TL4
+#define IPC_E2A_MSG_PARAM_SIZE  (IPC_E2A_MSG_SIZE_BASE + (IPC_E2A_MSG_SIZE_BASE / 2))
+#else
+#define IPC_E2A_MSG_PARAM_SIZE  IPC_E2A_MSG_SIZE_BASE
+#endif
+
+/*
+ * Debug messages buffers size (in bytes)
+ */
+#define IPC_DBG_PARAM_SIZE       256
+
+/*
+ * Define used for Rx hostbuf validity.
+ * This value should appear only when hostbuf was used for a Reception.
+ */
+#define RX_DMA_OVER_PATTERN 0xAAAAAA00
+
+/*
+ * Define used for MSG buffers validity.
+ * This value will be written only when a MSG buffer is used for sending from Emb to App.
+ */
+#define IPC_MSGE2A_VALID_PATTERN 0xADDEDE2A
+
+/*
+ * Define used for Debug messages buffers validity.
+ * This value will be written only when a DBG buffer is used for sending from Emb to App.
+ */
+#define IPC_DBG_VALID_PATTERN 0x000CACA0
+
+/*
+ *  Length of the receive vectors, in bytes
+ */
+#define DMA_HDR_PHYVECT_LEN    36
+
+/*
+ * Maximum number of payload addresses and lengths present in the descriptor
+ */
+#define NX_TX_PAYLOAD_MAX      6
+
+/*
+ * Message struct/ID API version
+ */
+#define MSG_API_VER  25
+
+/*
+ ****************************************************************************************
+ */
+// c.f LMAC/src/tx/tx_swdesc.h
+/// Descriptor filled by the Host
+struct hostdesc
+{
+#ifdef CONFIG_ECRNX_SPLIT_TX_BUF
+    /// Pointers to packet payloads
+    u32_l packet_addr[NX_TX_PAYLOAD_MAX];
+    /// Sizes of the MPDU/MSDU payloads
+    u16_l packet_len[NX_TX_PAYLOAD_MAX];
+    /// Number of payloads forming the MPDU
+    u8_l packet_cnt;
+#else
+    /// Pointer to packet payload
+    u32_l packet_addr[2];
+    /// Size of the payload
+    u16_l packet_len;
+#endif //(NX_AMSDU_TX)
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    /// Address of the status descriptor in host memory (used for confirmation upload)
+    u32_l status_desc_addr[2];
+    /// Destination Address
+    struct mac_addr eth_dest_addr;
+    /// Source Address
+    struct mac_addr eth_src_addr;
+    /// Ethernet Type
+    u16_l ethertype;
+    /// Buffer containing the PN to be used for this packet
+    u16_l pn[4];
+    /// Sequence Number used for transmission of this MPDU
+    u16_l sn;
+    /// Timestamp of first transmission of this MPDU
+    u16_l timestamp;
+    /// TX flags
+    u16_l flags;
+#else /* ! CONFIG_ECRNX_FULLMAC */
+#ifdef CONFIG_ECRNX_AGG_TX
+    ///Sequence Number for AMPDU MPDUs - for quick check if it's allowed within window
+    u16_l sn;
+#endif /* CONFIG_ECRNX_AGG_TX */
+    /// Padding between the buffer control structure and the MPDU in host memory
+    u8_l padding;
+#endif /* CONFIG_ECRNX_FULLMAC */
+    /// Packet TID (0xFF if not a QoS frame)
+    u8_l tid;
+    /// Interface Id
+    u8_l vif_idx;
+    /// Station Id (0xFF if station is unknown)
+    u8_l staid;
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+    /// MU-MIMO information (GroupId and User Position in the group) - The GroupId
+    /// is located on bits 0-5 and the User Position on bits 6-7. The GroupId value is set
+    /// to 63 if MU-MIMO shall not be used
+    u8_l mumimo_info;
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+};
+
+/// Descriptor filled by the UMAC
+struct umacdesc
+{
+#if defined CONFIG_ECRNX_SOFTMAC && defined CONFIG_ECRNX_BFMER
+    /**
+     * Flags from UMAC which do not fit with tx_hd.macctrlinfo2 format
+     *      Bit 0 - Indicate if frame can be beamformed when sent as a singleton
+     */
+    u16_l tx_flags;
+#endif // defined CONFIG_ECRNX_SOFTMAC && defined CONFIG_ECRNX_BFMER
+#ifdef CONFIG_ECRNX_AGG_TX
+    ///First Sequence Number of the BlockAck window
+    u16_l sn_win;
+    /// Flags from UMAC (match tx_hd.macctrlinfo2 format)
+    u32_l flags;
+    /// PHY related flags field - rate, GI type, BW type - filled by driver
+    u32_l phy_flags;
+#endif //(CONFIG_ECRNX_AGG_TX)
+};
+
+struct txdesc_api
+{
+    /// Information provided by Host
+    struct hostdesc host;
+#ifdef CONFIG_ECRNX_SOFTMAC
+    /// Information provided by UMAC
+    struct umacdesc umac;
+#endif
+};
+
+
+struct txdesc_host
+{
+    u32_l ready;
+
+    /// API of the embedded part
+    struct txdesc_api api;
+};
+
+/// Comes from ipc_dma.h
+/// Element in the pool of TX DMA bridge descriptors.
+struct dma_desc
+{
+#ifdef  CONFIG_ECRNX_ESWIN
+    uint8_t          split;
+#endif
+
+    /** Application subsystem address which is used as source address for DMA payload
+      * transfer*/
+    u32_l            src;
+    /** Points to the start of the embedded data buffer associated with this descriptor.
+     *  This address acts as the destination address for the DMA payload transfer*/
+    u32_l            dest;
+    /// Complete length of the buffer in memory
+    u16_l            length;
+    /// Control word for the DMA engine (e.g. for interrupt generation)
+    u16_l            ctrl;
+    /// Pointer to the next element of the chained list
+    u32_l            next;
+};
+
+// Comes from la.h
+/// Length of the configuration data of a logic analyzer
+#define LA_CONF_LEN          10
+
+/// Structure containing the configuration data of a logic analyzer
+struct la_conf_tag
+{
+    u32_l conf[LA_CONF_LEN];
+    u32_l trace_len;
+    u32_l diag_conf;
+};
+
+/// Size of a logic analyzer memory
+#define LA_MEM_LEN       (1024 * 1024)
+
+/// Type of errors
+enum
+{
+    /// Recoverable error, not requiring any action from Upper MAC
+    DBG_ERROR_RECOVERABLE = 0,
+    /// Fatal error, requiring Upper MAC to reset Lower MAC and HW and restart operation
+    DBG_ERROR_FATAL
+};
+
+/// Maximum length of the SW diag trace
+#define DBG_SW_DIAG_MAX_LEN   1024
+
+/// Maximum length of the error trace
+#define DBG_ERROR_TRACE_SIZE  256
+
+/// Number of MAC diagnostic port banks
+#define DBG_DIAGS_MAC_MAX     48
+
+/// Number of PHY diagnostic port banks
+#define DBG_DIAGS_PHY_MAX     32
+
+/// Maximum size of the RX header descriptor information in the debug dump
+#define DBG_RHD_MEM_LEN      (5 * 1024)
+
+/// Maximum size of the RX buffer descriptor information in the debug dump
+#define DBG_RBD_MEM_LEN      (5 * 1024)
+
+/// Maximum size of the TX header descriptor information in the debug dump
+#define DBG_THD_MEM_LEN      (10 * 1024)
+
+/// Structure containing the information about the PHY channel that is used
+struct phy_channel_info
+{
+    /// PHY channel information 1
+    u32_l info1;
+    /// PHY channel information 2
+    u32_l info2;
+};
+
+/// Debug information forwarded to host when an error occurs
+struct dbg_debug_info_tag
+{
+    /// Type of error (0: recoverable, 1: fatal)
+    u32_l error_type;
+    /// Pointer to the first RX Header Descriptor chained to the MAC HW
+    u32_l rhd;
+    /// Size of the RX header descriptor buffer
+    u32_l rhd_len;
+    /// Pointer to the first RX Buffer Descriptor chained to the MAC HW
+    u32_l rbd;
+    /// Size of the RX buffer descriptor buffer
+    u32_l rbd_len;
+    /// Pointer to the first TX Header Descriptors chained to the MAC HW
+    u32_l thd[NX_TXQ_CNT];
+    /// Size of the TX header descriptor buffer
+    u32_l thd_len[NX_TXQ_CNT];
+    /// MAC HW diag configuration
+    u32_l hw_diag;
+    /// Error message
+    u32_l error[DBG_ERROR_TRACE_SIZE/4];
+    /// SW diag configuration length
+    u32_l sw_diag_len;
+    /// SW diag configuration
+    u32_l sw_diag[DBG_SW_DIAG_MAX_LEN/4];
+    /// PHY channel information
+    struct phy_channel_info chan_info;
+    /// Embedded LA configuration
+    struct la_conf_tag la_conf;
+    /// MAC diagnostic port state
+    u16_l diags_mac[DBG_DIAGS_MAC_MAX];
+    /// PHY diagnostic port state
+    u16_l diags_phy[DBG_DIAGS_PHY_MAX];
+    /// MAC HW RX Header descriptor pointer
+    u32_l rhd_hw_ptr;
+    /// MAC HW RX Buffer descriptor pointer
+    u32_l rbd_hw_ptr;
+};
+
+/// Full debug dump that is forwarded to host in case of error
+struct dbg_debug_dump_tag
+{
+    /// Debug information
+    struct dbg_debug_info_tag dbg_info;
+
+    /// RX header descriptor memory
+    u32_l rhd_mem[DBG_RHD_MEM_LEN/4];
+
+    /// RX buffer descriptor memory
+    u32_l rbd_mem[DBG_RBD_MEM_LEN/4];
+
+    /// TX header descriptor memory
+    u32_l thd_mem[NX_TXQ_CNT][DBG_THD_MEM_LEN/4];
+
+    /// Logic analyzer memory
+    u32_l la_mem[LA_MEM_LEN/4];
+};
+
+
+/// Number of pulses in a radar event structure
+#define RADAR_PULSE_MAX   4
+
+/// Definition of an array of radar pulses
+struct radar_pulse_array_desc
+{
+    /// Buffer containing the radar pulses
+    u32_l pulse[RADAR_PULSE_MAX];
+    /// Index of the radar detection chain that detected those pulses
+    u32_l idx;
+    /// Number of valid pulses in the buffer
+    u32_l cnt;
+};
+
+/// Bit mapping inside a radar pulse element
+struct radar_pulse {
+    s32_l freq:6; /** Freq (resolution is 2Mhz range is [-Fadc/4 .. Fadc/4]) */
+    u32_l fom:4;  /** Figure of Merit */
+    u32_l len:6;  /** Length of the current radar pulse (resolution is 2us) */
+    u32_l rep:16; /** Time interval between the previous radar event
+                      and the current one (in us) */
+};
+
+/// Definition of a RX vector descriptor
+struct rx_vector_desc
+{
+    /// PHY channel information
+    struct phy_channel_info phy_info;
+
+    /// RX vector 1
+    u32_l rx_vect1[IPC_RXVEC_SIZE/4];
+
+    /// Used to print a valid rx vector
+    u32_l pattern;
+};
+
+///
+struct rxdesc_tag
+{
+    /// Host Buffer Address
+    u32_l host_id;
+    /// Length
+    u32_l frame_len;
+    /// Status
+    u16_l status;
+};
+
+/**
+ ****************************************************************************************
+ *  @defgroup IPC IPC
+ *  @ingroup NXMAC
+ *  @brief Inter Processor Communication module.
+ *
+ * The IPC module implements the protocol to communicate between the Host CPU
+ * and the Embedded CPU.
+ *
+ * @see http://en.wikipedia.org/wiki/Circular_buffer
+ * For more information about the ring buffer typical use and difficulties.
+ ****************************************************************************************
+ */
+
+
+/**
+ ****************************************************************************************
+ * @addtogroup IPC_TX IPC Tx path
+ *  @ingroup IPC
+ *  @brief IPC Tx path structures and functions
+ *
+ * A typical use case of the IPC Tx path API:
+ * @msc
+ * hscale = "2";
+ *
+ * a [label=Driver],
+ * b [label="IPC host"],
+ * c [label="IPC emb"],
+ * d [label=Firmware];
+ *
+ * ---   [label="Tx descriptor queue example"];
+ * a=>a  [label="Driver receives a Tx packet from OS"];
+ * a=>b  [label="ipc_host_txdesc_get()"];
+ * a<<b  [label="struct txdesc_host *"];
+ * a=>a  [label="Driver fill the descriptor"];
+ * a=>b  [label="ipc_host_txdesc_push()"];
+ * ...   [label="(several Tx desc can be pushed)"];
+ * b:>c  [label="Tx desc queue filled IRQ"];
+ * c=>>d [label="EDCA sub-scheduler callback"];
+ * c<<d  [label="Tx desc queue to pop"];
+ * c=>>d [label="UMAC Tx desc callback"];
+ * ...   [label="(several Tx desc can be popped)"];
+ * d=>d  [label="Packets are sent or discarded"];
+ * ---   [label="Tx confirm queue example"];
+ * c<=d  [label="ipc_emb_txcfm_push()"];
+ * c>>d  [label="Request accepted"];
+ * ...   [label="(several Tx cfm can be pushed)"];
+ * b<:c  [label="Tx cfm queue filled IRQ"];
+ * a<<=b [label="Driver's Tx Confirm callback"];
+ * a=>b  [label="ipc_host_txcfm_pop()"];
+ * a<<b  [label="struct ipc_txcfm"];
+ * a<=a  [label="Packets are freed by the driver"];
+ * @endmsc
+ *
+ * @{
+ ****************************************************************************************
+ */
+
+/// @} IPC_TX
+
+/**
+ ****************************************************************************************
+ *  @defgroup IPC_RX IPC Rx path
+ *  @ingroup IPC
+ *  @brief IPC Rx path functions and structures
+ *
+ * A typical use case of the IPC Rx path API:
+ * @msc
+ * hscale = "2";
+ *
+ * a [label=Firmware],
+ * b [label="IPC emb"],
+ * c [label="IPC host"],
+ * d [label=Driver];
+ *
+ * ---   [label="Rx buffer and desc queues usage example"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * ...   [label="(several Rx buffer are pushed)"];
+ * a=>a  [label=" Frame is received\n from the medium"];
+ * a<<b  [label="struct ipc_rxbuf"];
+ * a=>a  [label=" Firmware fill the buffer\n with received frame"];
+ * a<<b  [label="Push accepted"];
+ * ...   [label="(several Rx desc can be pushed)"];
+ * b:>c  [label="Rx desc queue filled IRQ"];
+ * c=>>d [label="Driver Rx packet callback"];
+ * c<=d  [label="ipc_host_rxdesc_pop()"];
+ * d=>d  [label="Rx packet is handed \nover to the OS "];
+ * ...   [label="(several Rx desc can be poped)"];
+ * ---   [label="Rx buffer request exemple"];
+ * b:>c  [label="Low Rx buffer count IRQ"];
+ * a<<b  [label="struct ipc_rxbuf"];
+ * c=>>d [label="Driver Rx buffer callback"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * d=>c  [label="ipc_host_rxbuf_push()"];
+ * ...   [label="(several Rx buffer are pushed)"];
+ * @endmsc
+ *
+ * @addtogroup IPC_RX
+ * @{
+ ****************************************************************************************
+ */
+
+/// @} IPC_RX
+
+
+
+/**
+ ****************************************************************************************
+ *  @defgroup IPC_MISC IPC Misc
+ *  @ingroup IPC
+ *  @brief IPC miscellaneous functions
+ ****************************************************************************************
+ */
+/** IPC header structure.  This structure is stored at the beginning of every IPC message.
+ * @warning This structure's size must NOT exceed 4 bytes in length.
+ */
+struct ipc_header
+{
+    /// IPC message type.
+    u16_l type;
+    /// IPC message size in number of bytes.
+    u16_l size;
+};
+
+struct ipc_msg_elt
+{
+    /// Message header (alignment forced on word size, see allocation in shared env).
+    struct ipc_header header __ALIGN4;
+};
+
+/// Message structure for MSGs from Emb to App
+struct ipc_e2a_msg
+{
+    u16_l id;                ///< Message id.
+    u16_l dummy_dest_id;
+    u16_l dummy_src_id;
+#ifdef CONFIG_ECRNX_ESWIN
+    u8_l hostid[8];
+#endif
+
+    u16_l param_len;         ///< Parameter embedded struct length.
+    u32_l param[IPC_E2A_MSG_PARAM_SIZE];  ///< Parameter embedded struct. Must be word-aligned.
+    u32_l pattern;           ///< Used to stamp a valid MSG buffer
+};
+
+/// Message structure for Debug messages from Emb to App
+struct ipc_dbg_msg
+{
+    u32_l string[IPC_DBG_PARAM_SIZE/4]; ///< Debug string
+    u32_l pattern;                    ///< Used to stamp a valid buffer
+};
+
+/// Message structure for MSGs from App to Emb.
+/// Actually a sub-structure will be used when filling the messages.
+struct ipc_a2e_msg
+{
+    u32_l dummy_word;                // used to cope with kernel message structure
+    u32_l msg[IPC_A2E_MSG_BUF_SIZE]; // body of the msg
+};
+
+struct ipc_shared_rx_buf
+{
+    /// < ptr to hostbuf client (ipc_host client) structure
+    u64_l hostid;
+    /// < ptr to real hostbuf dma address
+    u64_l dma_addr;
+};
+
+struct ipc_shared_rx_desc
+{
+    /// DMA Address
+    u64_l dma_addr;
+};
+
+/// Structure containing FW characteristics for compatibility checking
+struct compatibility_tag {
+    /// Size of IPC shared memory
+    u16_l ipc_shared_size;
+    /// Message struct/ID API version
+    u16_l msg_api;
+    /// Version of IPC shared
+    u8_l ipc_shared_version;
+    /// Number of host buffers available for Emb->App MSGs sending
+    u8_l msge2a_buf_cnt;
+    /// Number of host buffers available for Debug Messages sending
+    u8_l dbgbuf_cnt;
+    /// Number of host buffers available for Radar events handling
+    u8_l radarbuf_cnt;
+    /// Number of host buffers available for unsupported Rx vectors handling
+    u8_l unsuprxvecbuf_cnt;
+    /// Number of shared descriptors available for Data RX handling
+    u8_l rxdesc_cnt;
+    /// Number of host buffers available for Data Rx handling
+    u8_l rxbuf_cnt;
+    /// Number of descriptors in BK TX queue (power of 2, min 4, max 64)
+    u8_l bk_txq;
+    /// Number of descriptors in BE TX queue (power of 2, min 4, max 64)
+    u8_l be_txq;
+    /// Number of descriptors in VI TX queue (power of 2, min 4, max 64)
+    u8_l vi_txq;
+    /// Number of descriptors in VO TX queue (power of 2, min 4, max 64)
+    u8_l vo_txq;
+    /// Number of descriptors in BCN TX queue (power of 2, min 4, max 64)
+    u8_l bcn_txq;
+};
+
+/*
+ * TYPE and STRUCT DEFINITIONS
+ ****************************************************************************************
+ */
+
+
+// Indexes are defined in the MIB shared structure
+struct ipc_shared_env_tag
+{
+    volatile struct compatibility_tag comp_info; //FW characteristics
+
+    volatile struct ipc_a2e_msg msg_a2e_buf; // room for MSG to be sent from App to Emb
+
+    // Fields for MSGs sending from Emb to App
+    volatile struct    ipc_e2a_msg msg_e2a_buf; // room to build the MSG to be DMA Xferred
+    volatile struct    dma_desc msg_dma_desc;   // DMA descriptor for Emb->App MSGs Xfers
+    volatile u32_l  msg_e2a_hostbuf_addr [IPC_MSGE2A_BUF_CNT]; // buffers @ for DMA Xfers
+
+    // Fields for Debug MSGs sending from Emb to App
+    volatile struct    ipc_dbg_msg dbg_buf; // room to build the MSG to be DMA Xferred
+    volatile struct    dma_desc dbg_dma_desc;   // DMA descriptor for Emb->App MSGs Xfers
+    volatile u32_l  dbg_hostbuf_addr [IPC_DBGBUF_CNT]; // buffers @ for MSGs DMA Xfers
+    volatile u32_l  la_dbginfo_addr; // Host buffer address for the debug information
+    volatile u32_l  pattern_addr;
+    volatile u32_l  radarbuf_hostbuf [IPC_RADARBUF_CNT]; // buffers @ for Radar Events
+    volatile u32_l  unsuprxvecbuf_hostbuf [IPC_UNSUPRXVECBUF_CNT]; // buffers @ for unsupported Rx vectors
+    volatile struct txdesc_host txdesc0[CONFIG_USER_MAX][NX_TXDESC_CNT0];
+    volatile struct txdesc_host txdesc1[CONFIG_USER_MAX][NX_TXDESC_CNT1];
+    volatile struct txdesc_host txdesc2[CONFIG_USER_MAX][NX_TXDESC_CNT2];
+    volatile struct txdesc_host txdesc3[CONFIG_USER_MAX][NX_TXDESC_CNT3];
+    #if NX_TXQ_CNT == 5
+    volatile struct txdesc_host txdesc4[1][NX_TXDESC_CNT4];
+    #endif
+    #ifdef CONFIG_ECRNX_FULLMAC
+    // RX Descriptors Array
+    volatile struct ipc_shared_rx_desc host_rxdesc[IPC_RXDESC_CNT];
+    // RX Buffers Array
+    volatile struct ipc_shared_rx_buf  host_rxbuf[IPC_RXBUF_CNT];
+    #else
+    // buffers @ for Data Rx
+    volatile u32_l host_rxbuf[IPC_RXBUF_CNT];
+    #endif /* CONFIG_ECRNX_FULLMAC */
+
+    u32_l buffered[NX_REMOTE_STA_MAX][TID_MAX];
+
+    volatile uint16_t trace_pattern;
+    volatile uint32_t trace_start;
+    volatile uint32_t trace_end;
+    volatile uint32_t trace_size;
+    volatile uint32_t trace_offset;
+    volatile uint32_t trace_nb_compo;
+    volatile uint32_t trace_offset_compo;
+};
+
+extern struct ipc_shared_env_tag ipc_shared_env;
+
+
+/*
+ * TYPE and STRUCT DEFINITIONS
+ ****************************************************************************************
+ */
+
+// IRQs from app to emb
+/// Interrupts bits used for the TX descriptors of the AC queues
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+#ifdef CONFIG_ECRNX_OLD_IPC
+#error "MU-MIMO cannot be compiled for old IPC"
+#endif
+/// Interrupts bits used
+#if CONFIG_USER_MAX > 3
+#define IPC_IRQ_A2E_USER_MSK       0xF
+#elif CONFIG_USER_MAX > 2
+#define IPC_IRQ_A2E_USER_MSK       0x7
+#else
+#define IPC_IRQ_A2E_USER_MSK       0x3
+#endif
+
+/// Offset of the interrupts for AC0
+#define IPC_IRQ_A2E_AC0_OFT        8
+/// Mask of the interrupts for AC0
+#define IPC_IRQ_A2E_AC0_MSK       (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC0_OFT)
+/// Offset of the interrupts for AC1
+#define IPC_IRQ_A2E_AC1_OFT       (IPC_IRQ_A2E_AC0_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC1
+#define IPC_IRQ_A2E_AC1_MSK       (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC1_OFT)
+/// Offset of the interrupts for AC2
+#define IPC_IRQ_A2E_AC2_OFT       (IPC_IRQ_A2E_AC1_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC2
+#define IPC_IRQ_A2E_AC2_MSK       (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC2_OFT)
+/// Offset of the interrupts for AC3
+#define IPC_IRQ_A2E_AC3_OFT       (IPC_IRQ_A2E_AC2_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC3
+#define IPC_IRQ_A2E_AC3_MSK       (IPC_IRQ_A2E_USER_MSK << IPC_IRQ_A2E_AC3_OFT)
+/// Offset of the interrupts for BCN
+#define IPC_IRQ_A2E_BCN_OFT       (IPC_IRQ_A2E_AC3_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for BCN
+#define IPC_IRQ_A2E_BCN_MSK       CO_BIT(IPC_IRQ_A2E_BCN_OFT)
+
+#define IPC_IRQ_A2E_AC_TXDESC     (IPC_IRQ_A2E_AC0_MSK | IPC_IRQ_A2E_AC1_MSK | \
+                                   IPC_IRQ_A2E_AC2_MSK | IPC_IRQ_A2E_AC3_MSK)
+
+/// Interrupts bits used for the TX descriptors of the BCN queue
+#if NX_TXQ_CNT < 5
+#define IPC_IRQ_A2E_BCN_TXDESC      0
+#else
+#define IPC_IRQ_A2E_BCN_TXDESC      (0x01 << IPC_IRQ_A2E_BCN_OFT)
+#endif
+
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_A2E_TXDESC          (IPC_IRQ_A2E_AC_TXDESC | IPC_IRQ_A2E_BCN_TXDESC)
+#else
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_A2E_TXDESC          0xFF00
+#endif
+
+#define IPC_IRQ_A2E_TXDESC_FIRSTBIT (8)
+#define IPC_IRQ_A2E_RXBUF_BACK      CO_BIT(5)
+#define IPC_IRQ_A2E_RXDESC_BACK     CO_BIT(4)
+
+#define IPC_IRQ_A2E_MSG             CO_BIT(1)
+#define IPC_IRQ_A2E_DBG             CO_BIT(0)
+
+#define IPC_IRQ_A2E_ALL             (IPC_IRQ_A2E_TXDESC|IPC_IRQ_A2E_MSG|IPC_IRQ_A2E_DBG)
+
+// IRQs from emb to app
+#define IPC_IRQ_E2A_TXCFM_POS   7
+
+#ifdef CONFIG_ECRNX_MUMIMO_TX
+#ifdef CONFIG_ECRNX_OLD_IPC
+#error "MU-MIMO cannot be compiled for old IPC"
+#endif
+/// Interrupts bits used
+#if CONFIG_USER_MAX > 3
+#define IPC_IRQ_E2A_USER_MSK       0xF
+#elif CONFIG_USER_MAX > 2
+#define IPC_IRQ_E2A_USER_MSK       0x7
+#else
+#define IPC_IRQ_E2A_USER_MSK       0x3
+#endif
+
+/// Offset of the interrupts for AC0
+#define IPC_IRQ_E2A_AC0_OFT        IPC_IRQ_E2A_TXCFM_POS
+/// Mask of the interrupts for AC0
+#define IPC_IRQ_E2A_AC0_MSK       (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC0_OFT)
+/// Offset of the interrupts for AC1
+#define IPC_IRQ_E2A_AC1_OFT       (IPC_IRQ_E2A_AC0_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC1
+#define IPC_IRQ_E2A_AC1_MSK       (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC1_OFT)
+/// Offset of the interrupts for AC2
+#define IPC_IRQ_E2A_AC2_OFT       (IPC_IRQ_E2A_AC1_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC2
+#define IPC_IRQ_E2A_AC2_MSK       (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC2_OFT)
+/// Offset of the interrupts for AC3
+#define IPC_IRQ_E2A_AC3_OFT       (IPC_IRQ_E2A_AC2_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for AC3
+#define IPC_IRQ_E2A_AC3_MSK       (IPC_IRQ_E2A_USER_MSK << IPC_IRQ_E2A_AC3_OFT)
+/// Offset of the interrupts for BCN
+#define IPC_IRQ_E2A_BCN_OFT       (IPC_IRQ_E2A_AC3_OFT + CONFIG_USER_MAX)
+/// Mask of the interrupts for BCN
+#define IPC_IRQ_E2A_BCN_MSK       CO_BIT(IPC_IRQ_E2A_BCN_OFT)
+
+#define IPC_IRQ_E2A_AC_TXCFM     (IPC_IRQ_E2A_AC0_MSK | IPC_IRQ_E2A_AC1_MSK | \
+                                   IPC_IRQ_E2A_AC2_MSK | IPC_IRQ_E2A_AC3_MSK)
+
+/// Interrupts bits used for the TX descriptors of the BCN queue
+#if NX_TXQ_CNT < 5
+#define IPC_IRQ_E2A_BCN_TXCFM      0
+#else
+#define IPC_IRQ_E2A_BCN_TXCFM      (0x01 << IPC_IRQ_E2A_BCN_OFT)
+#endif
+
+/// IPC TX descriptor interrupt mask
+#define IPC_IRQ_E2A_TXCFM          (IPC_IRQ_E2A_AC_TXCFM | IPC_IRQ_E2A_BCN_TXCFM)
+
+#else
+
+#define IPC_IRQ_E2A_TXCFM       ((1 << NX_TXQ_CNT) - 1 ) << IPC_IRQ_E2A_TXCFM_POS
+
+#endif /* CONFIG_ECRNX_MUMIMO_TX */
+
+#define IPC_IRQ_E2A_UNSUP_RX_VEC    CO_BIT(7)
+#define IPC_IRQ_E2A_RADAR           CO_BIT(6)
+#define IPC_IRQ_E2A_TBTT_SEC        CO_BIT(5)
+#define IPC_IRQ_E2A_TBTT_PRIM       CO_BIT(4)
+#define IPC_IRQ_E2A_RXDESC          CO_BIT(3)
+#define IPC_IRQ_E2A_MSG_ACK         CO_BIT(2)
+#define IPC_IRQ_E2A_MSG             CO_BIT(1)
+#define IPC_IRQ_E2A_DBG             CO_BIT(0)
+
+#define IPC_IRQ_E2A_ALL         ( IPC_IRQ_E2A_TXCFM         \
+                                | IPC_IRQ_E2A_RXDESC        \
+                                | IPC_IRQ_E2A_MSG_ACK       \
+                                | IPC_IRQ_E2A_MSG           \
+                                | IPC_IRQ_E2A_DBG           \
+                                | IPC_IRQ_E2A_TBTT_PRIM     \
+                                | IPC_IRQ_E2A_TBTT_SEC      \
+                                | IPC_IRQ_E2A_RADAR         \
+                                | IPC_IRQ_E2A_UNSUP_RX_VEC)
+
+// FLAGS for RX desc
+#define IPC_RX_FORWARD          CO_BIT(1)
+#define IPC_RX_INTRABSS         CO_BIT(0)
+
+
+// IPC message TYPE
+enum
+{
+    IPC_MSG_NONE = 0,
+    IPC_MSG_WRAP,
+    IPC_MSG_KMSG,
+
+    IPC_DBG_STRING,
+
+};
+
+#endif // _IPC_SHARED_H_
+
diff --git a/drivers/net/wireless/eswin/lmac_mac.h b/drivers/net/wireless/eswin/lmac_mac.h
new file mode 100644 (file)
index 0000000..bb31e8b
--- /dev/null
@@ -0,0 +1,467 @@
+/**
+ ****************************************************************************************
+ *
+ * @file lmac_mac_types.h
+ *
+ * @brief MAC related definitions.
+ *
+ * Adapted from mac_types.h to used lmac_types.h instead of standard types
+ * eg: perl -pi -e '$_ =~ s/uint(\d{1,2})_t/u$1_l/g; \
+ *                  $_ =~ s/int(\d{1,2})_t/s$1_l/g; \
+ *                  $_ =~ s/CO_BIT/BIT/g;' lmac_mac.h
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef LMAC_MAC_H_
+#define LMAC_MAC_H_
+
+#include "lmac_types.h"
+
+/// Interface types
+enum mac_vif_type
+{
+    /// ESS STA interface
+    VIF_STA,
+    /// IBSS STA interface
+    VIF_IBSS,
+    /// AP interface
+    VIF_AP,
+    /// Mesh Point interface
+    VIF_MESH_POINT,
+    /// Monitor interface
+    VIF_MONITOR,
+    /// Unknown type
+    VIF_UNKNOWN
+};
+
+/// MAC address length in bytes.
+#define MAC_ADDR_LEN 6
+
+/// MAC address structure.
+struct mac_addr
+{
+    /// Array of 16-bit words that make up the MAC address.
+    u16_l array[MAC_ADDR_LEN/2];
+};
+
+/// SSID maximum length.
+#define MAC_SSID_LEN 32
+
+/// SSID.
+struct mac_ssid
+{
+    /// Actual length of the SSID.
+    u8_l length;
+    /// Array containing the SSID name.
+    u8_l array[MAC_SSID_LEN];
+};
+
+/// BSS type
+enum mac_bss_type
+{
+    INFRASTRUCTURE_MODE = 1,
+    INDEPENDENT_BSS_MODE,
+    ANY_BSS_MODE
+};
+
+/// Channel Band
+enum mac_chan_band
+{
+    /// 2.4GHz Band
+    PHY_BAND_2G4,
+    /// 5GHz band
+    PHY_BAND_5G,
+    /// Number of bands
+    PHY_BAND_MAX,
+};
+
+/// Operating Channel Bandwidth
+enum mac_chan_bandwidth
+{
+    /// 20MHz BW
+    PHY_CHNL_BW_20,
+    /// 40MHz BW
+    PHY_CHNL_BW_40,
+    /// 80MHz BW
+    PHY_CHNL_BW_80,
+    /// 160MHz BW
+    PHY_CHNL_BW_160,
+    /// 80+80MHz BW
+    PHY_CHNL_BW_80P80,
+    /// Reserved BW
+    PHY_CHNL_BW_OTHER,
+};
+
+/// max number of channels in the 2.4 GHZ band
+#define MAC_DOMAINCHANNEL_24G_MAX 14
+
+/// max number of channels in the 5 GHZ band
+#define MAC_DOMAINCHANNEL_5G_MAX 28
+
+/// Channel Flag
+enum mac_chan_flags
+{
+    /// Cannot initiate radiation on this channel
+    CHAN_NO_IR = BIT(0),
+    /// Channel is not allowed
+    CHAN_DISABLED = BIT(1),
+    /// Radar detection required on this channel
+    CHAN_RADAR = BIT(2),
+};
+
+/// Primary Channel definition
+struct mac_chan_def
+{
+    /// Frequency of the channel (in MHz)
+    u16_l freq;
+    /// RF band (@ref mac_chan_band)
+    u8_l band;
+    /// Additional information (@ref mac_chan_flags)
+    u8_l flags;
+    /// Max transmit power allowed on this channel (dBm)
+    s8_l tx_power;
+};
+
+/// Operating Channel
+struct mac_chan_op
+{
+    /// Band (@ref mac_chan_band)
+    u8_l band;
+    /// Channel type (@ref mac_chan_bandwidth)
+    u8_l type;
+    /// Frequency for Primary 20MHz channel (in MHz)
+    u16_l prim20_freq;
+    /// Frequency center of the contiguous channel or center of Primary 80+80 (in MHz)
+    u16_l center1_freq;
+    /// Frequency center of the non-contiguous secondary 80+80 (in MHz)
+    u16_l center2_freq;
+    /// Max transmit power allowed on this channel (dBm)
+    s8_l tx_power;
+    /// Additional information (@ref mac_chan_flags)
+    u8_l flags;
+};
+
+/// Cipher suites (order is important as it is used by MACHW)
+enum mac_cipher_suite
+{
+    /// 00-0F-AC 1
+    MAC_CIPHER_WEP40 = 0,
+    /// 00-0F-AC 2
+    MAC_CIPHER_TKIP = 1,
+    /// 00-0F-AC 4
+    MAC_CIPHER_CCMP = 2,
+    /// 00-0F-AC 5
+    MAC_CIPHER_WEP104 = 3,
+    /// 00-14-72 1
+    MAC_CIPHER_WPI_SMS4 = 4,
+    /// 00-0F-AC 6  (aka AES_CMAC)
+    MAC_CIPHER_BIP_CMAC_128 = 5,
+
+    // following cipher are not supported by MACHW
+    /// 00-0F-AC 08
+    MAC_CIPHER_GCMP_128,
+    /// 00-0F-AC 09
+    MAC_CIPHER_GCMP_256,
+    /// 00-0F-AC 10
+    MAC_CIPHER_CCMP_256,
+    /// 00-0F-AC 11
+    MAC_CIPHER_BIP_GMAC_128,
+    /// 00-0F-AC 12
+    MAC_CIPHER_BIP_GMAC_256,
+    /// 00-0F-AC 13
+    MAC_CIPHER_BIP_CMAC_256,
+
+    MAC_CIPHER_INVALID = 0xFF
+};
+
+/// Authentication and Key Management suite
+enum mac_akm_suite
+{
+    /// No security
+    MAC_AKM_NONE,
+    /// Pre RSN (WEP or WPA)
+    MAC_AKM_PRE_RSN,
+    /// 00-0F-AC 1
+    MAC_AKM_8021X,
+    /// 00-0F-AC 2
+    MAC_AKM_PSK,
+    /// 00-0F-AC 3
+    MAC_AKM_FT_8021X,
+    /// 00-0F-AC 4
+    MAC_AKM_FT_PSK,
+    /// 00-0F-AC 5
+    MAC_AKM_8021X_SHA256,
+    /// 00-0F-AC 6
+    MAC_AKM_PSK_SHA256,
+    /// 00-0F-AC 7
+    MAC_AKM_TDLS,
+    /// 00-0F-AC 8
+    MAC_AKM_SAE,
+    /// 00-0F-AC 9
+    MAC_AKM_FT_OVER_SAE,
+    /// 00-0F-AC 11
+    MAC_AKM_8021X_SUITE_B,
+    /// 00-0F-AC 12
+    MAC_AKM_8021X_SUITE_B_192,
+    /// 00-0F-AC 14
+    MAC_AKM_FILS_SHA256,
+    /// 00-0F-AC 15
+    MAC_AKM_FILS_SHA384,
+    /// 00-0F-AC 16
+    MAC_AKM_FT_FILS_SHA256,
+    /// 00-0F-AC 17
+    MAC_AKM_FT_FILS_SHA384,
+    /// 00-0F-AC 18
+    MAC_AKM_OWE,
+
+    /// 00-14-72 1
+    MAC_AKM_WAPI_CERT,
+    /// 00-14-72 2
+    MAC_AKM_WAPI_PSK,
+};
+
+/// Scan result element, parsed from beacon or probe response frames.
+struct mac_scan_result
+{
+    /// Scan result is valid
+    bool valid_flag;
+    /// Network BSSID.
+    struct mac_addr bssid;
+    /// Network name.
+    struct mac_ssid ssid;
+    /// Network type (@ref mac_bss_type).
+    u16_l bsstype;
+    /// Network channel.
+    struct mac_chan_def *chan;
+    /// Network beacon period (in TU).
+    u16_l beacon_period;
+    /// Capability information
+    u16_l cap_info;
+    /// Supported AKM (bit-field of @ref mac_akm_suite)
+    u32_l akm;
+    /// Group cipher (bit-field of @ref mac_cipher_suite)
+    u16_l group_cipher;
+    /// Group cipher (bit-field of @ref mac_cipher_suite)
+    u16_l pairwise_cipher;
+    /// RSSI of the scanned BSS (in dBm)
+    s8_l rssi;
+    /// Multi-BSSID index (0 if this is the reference (i.e. transmitted) BSSID)
+    u8_l multi_bssid_index;
+    /// Maximum BSSID indicator
+    u8_l max_bssid_indicator;
+};
+
+/// Legacy rate 802.11 definitions
+enum mac_legacy_rates
+{
+    /// DSSS/CCK 1Mbps
+    MAC_RATE_1MBPS   =   2,
+    /// DSSS/CCK 2Mbps
+    MAC_RATE_2MBPS   =   4,
+    /// DSSS/CCK 5.5Mbps
+    MAC_RATE_5_5MBPS =  11,
+    /// OFDM 6Mbps
+    MAC_RATE_6MBPS   =  12,
+    /// OFDM 9Mbps
+    MAC_RATE_9MBPS   =  18,
+    /// DSSS/CCK 11Mbps
+    MAC_RATE_11MBPS  =  22,
+    /// OFDM 12Mbps
+    MAC_RATE_12MBPS  =  24,
+    /// OFDM 18Mbps
+    MAC_RATE_18MBPS  =  36,
+    /// OFDM 24Mbps
+    MAC_RATE_24MBPS  =  48,
+    /// OFDM 36Mbps
+    MAC_RATE_36MBPS  =  72,
+    /// OFDM 48Mbps
+    MAC_RATE_48MBPS  =  96,
+    /// OFDM 54Mbps
+    MAC_RATE_54MBPS  = 108
+};
+
+/// BSS Membership Selector definitions
+enum mac_bss_membership
+{
+    /// HT PHY
+    MAC_BSS_MEMBERSHIP_HT_PHY = 127,
+    /// VHT PHY
+    MAC_BSS_MEMBERSHIP_VHT_PHY = 126,
+};
+
+/// MAC rateset maximum length
+#define MAC_RATESET_LEN 12
+
+/// Structure containing the legacy rateset of a station
+struct mac_rateset
+{
+    /// Number of legacy rates supported
+    u8_l length;
+    /// Array of legacy rates
+    u8_l array[MAC_RATESET_LEN];
+};
+
+/// MAC Security Key maximum length
+#define MAC_SEC_KEY_LEN 32  // TKIP keys 256 bits (max length) with MIC keys
+
+/// Structure defining a security key
+struct mac_sec_key
+{
+    /// Key material length
+    u8_l length;
+    /// Key material
+    u32_l array[MAC_SEC_KEY_LEN/4];
+};
+
+/// Access Category enumeration
+enum mac_ac
+{
+    /// Background
+    AC_BK = 0,
+    /// Best-effort
+    AC_BE,
+    /// Video
+    AC_VI,
+    /// Voice
+    AC_VO,
+    /// Number of access categories
+    AC_MAX
+};
+
+/// Traffic ID enumeration
+enum mac_tid
+{
+    /// TID_0. Mapped to @ref AC_BE as per 802.11 standard.
+    TID_0,
+    /// TID_1. Mapped to @ref AC_BK as per 802.11 standard.
+    TID_1,
+    /// TID_2. Mapped to @ref AC_BK as per 802.11 standard.
+    TID_2,
+    /// TID_3. Mapped to @ref AC_BE as per 802.11 standard.
+    TID_3,
+    /// TID_4. Mapped to @ref AC_VI as per 802.11 standard.
+    TID_4,
+    /// TID_5. Mapped to @ref AC_VI as per 802.11 standard.
+    TID_5,
+    /// TID_6. Mapped to @ref AC_VO as per 802.11 standard.
+    TID_6,
+    /// TID_7. Mapped to @ref AC_VO as per 802.11 standard.
+    TID_7,
+    /// Non standard Management TID used internally
+    TID_MGT,
+    /// Number of TID supported
+    TID_MAX
+};
+
+/// MCS bitfield maximum size (in bytes)
+#define MAX_MCS_LEN 16 // 16 * 8 = 128
+
+/// MAC HT capability information element
+struct mac_htcapability
+{
+    /// HT capability information
+    u16_l ht_capa_info;
+    /// A-MPDU parameters
+    u8_l a_mpdu_param;
+    /// Supported MCS
+    u8_l mcs_rate[MAX_MCS_LEN];
+    /// HT extended capability information
+    u16_l ht_extended_capa;
+    /// Beamforming capability information
+    u32_l tx_beamforming_capa;
+    /// Antenna selection capability information
+    u8_l asel_capa;
+};
+
+/// MAC VHT capability information element
+struct mac_vhtcapability
+{
+    /// VHT capability information
+    u32_l vht_capa_info;
+    /// RX MCS map
+    u16_l rx_mcs_map;
+    /// RX highest data rate
+    u16_l rx_highest;
+    /// TX MCS map
+    u16_l tx_mcs_map;
+    /// TX highest data rate
+    u16_l tx_highest;
+};
+
+/// Length (in bytes) of the MAC HE capability field
+#define MAC_HE_MAC_CAPA_LEN 6
+/// Length (in bytes) of the PHY HE capability field
+#define MAC_HE_PHY_CAPA_LEN 11
+/// Maximum length (in bytes) of the PPE threshold data
+#define MAC_HE_PPE_THRES_MAX_LEN 25
+
+/// Structure listing the per-NSS, per-BW supported MCS combinations
+struct mac_he_mcs_nss_supp
+{
+    /// per-NSS supported MCS in RX, for BW <= 80MHz
+    u16_l rx_mcs_80;
+    /// per-NSS supported MCS in TX, for BW <= 80MHz
+    u16_l tx_mcs_80;
+    /// per-NSS supported MCS in RX, for BW = 160MHz
+    u16_l rx_mcs_160;
+    /// per-NSS supported MCS in TX, for BW = 160MHz
+    u16_l tx_mcs_160;
+    /// per-NSS supported MCS in RX, for BW = 80+80MHz
+    u16_l rx_mcs_80p80;
+    /// per-NSS supported MCS in TX, for BW = 80+80MHz
+    u16_l tx_mcs_80p80;
+};
+
+/// MAC HE capability information element
+struct mac_hecapability
+{
+    /// MAC HE capabilities
+    u8_l mac_cap_info[MAC_HE_MAC_CAPA_LEN];
+    /// PHY HE capabilities
+    u8_l phy_cap_info[MAC_HE_PHY_CAPA_LEN];
+    /// Supported MCS combinations
+    struct mac_he_mcs_nss_supp mcs_supp;
+    /// PPE Thresholds data
+    u8_l ppe_thres[MAC_HE_PPE_THRES_MAX_LEN];
+};
+
+/// Station flags
+enum mac_sta_flags
+{
+    /// Bit indicating that a STA has QoS (WMM) capability
+    STA_QOS_CAPA = BIT(0),
+    /// Bit indicating that a STA has HT capability
+    STA_HT_CAPA = BIT(1),
+    /// Bit indicating that a STA has VHT capability
+    STA_VHT_CAPA = BIT(2),
+    /// Bit indicating that a STA has MFP capability
+    STA_MFP_CAPA = BIT(3),
+    /// Bit indicating that the STA included the Operation Notification IE
+    STA_OPMOD_NOTIF = BIT(4),
+    /// Bit indicating that a STA has HE capability
+    STA_HE_CAPA = BIT(5),
+    /// Bit Inidcating supprot for short Preamble in ERP
+    STA_SHORT_PREAMBLE_CAPA = BIT(6),
+};
+
+/// Connection flags
+enum mac_connection_flags
+{
+    /// Flag indicating whether the control port is controlled by host or not
+    CONTROL_PORT_HOST = BIT(0),
+    /// Flag indicating whether the control port frame shall be sent unencrypted
+    CONTROL_PORT_NO_ENC = BIT(1),
+    /// Flag indicating whether HT and VHT shall be disabled or not
+    DISABLE_HT = BIT(2),
+    /// Flag indicating whether WPA or WPA2 authentication is in use
+    WPA_WPA2_IN_USE = BIT(3),
+    /// Flag indicating whether MFP is in use
+    MFP_IN_USE = BIT(4),
+    REASSOCIATION = BIT(5),
+    FT_OVER_DS = BIT(6),
+};
+
+#endif // LMAC_MAC_H_
diff --git a/drivers/net/wireless/eswin/lmac_msg.h b/drivers/net/wireless/eswin/lmac_msg.h
new file mode 100644 (file)
index 0000000..1e1fe7f
--- /dev/null
@@ -0,0 +1,2767 @@
+/**
+ ****************************************************************************************
+ *
+ * @file lmac_msg.h
+ *
+ * @brief Main definitions for message exchanges with LMAC
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef LMAC_MSG_H_
+#define LMAC_MSG_H_
+
+/*
+ * INCLUDE FILES
+ ****************************************************************************************
+ */
+// for MAC related elements (mac_addr, mac_ssid...)
+#include "lmac_mac.h"
+
+
+#if defined(CONFIG_STANDALONE_WIFI) && defined(CONFIG_STANDALONE_WIFI_BLE)
+#error biuld mode error, wifi or wifi_ble must choice one!
+#endif
+
+/*
+ ****************************************************************************************
+ */
+/////////////////////////////////////////////////////////////////////////////////
+// COMMUNICATION WITH LMAC LAYER
+/////////////////////////////////////////////////////////////////////////////////
+/* Task identifiers for communication between LMAC and DRIVER */
+enum
+{
+    TASK_NONE = (u8_l) -1,
+#if defined(CONFIG_STANDALONE_WIFI)
+    // MAC Management task.
+    TASK_MM = 0,
+#elif defined(CONFIG_STANDALONE_WIFI_BLE)
+    TASK_MM = 32,
+#else
+#error #error not defined biuld mode!
+#endif
+    // DEBUG task
+    TASK_DBG,
+    /// SCAN task
+    TASK_SCAN,
+#ifdef CONFIG_CEVA_RTOS
+    TASK_CLI,
+#endif
+    /// TDLS task
+    TASK_TDLS,
+    /// SCANU task
+    TASK_SCANU,
+    /// ME task
+    TASK_ME,
+    /// SM task
+    TASK_SM,
+    /// APM task
+    TASK_APM,
+    /// BAM task
+    TASK_BAM,
+    /// MESH task
+    TASK_MESH,
+    /// RXU task
+    TASK_RXU,
+    TASK_RM,
+#if defined(CONFIG_ECRNX_P2P)
+    TASK_P2P_LISTEN,
+#endif
+    TASK_TWT,
+#if defined CONFIG_ECRNX_SOFTMAC
+    // This is used to define the last task that is running on the EMB processor
+    TASK_LAST_EMB = TASK_TDLS,
+#elif defined CONFIG_ECRNX_FULLMAC || defined CONFIG_ECRNX_FHOST
+    // This is used to define the last task that is running on the EMB processor
+    TASK_LAST_EMB = TASK_TWT,
+#else
+#error "Need to define SOFTMAC or FULLMAC"
+#endif
+    // nX API task
+    TASK_API,
+    TASK_MAX,
+};
+
+
+/// For MAC HW States copied from "hal_machw.h"
+enum
+{
+    /// MAC HW IDLE State.
+    HW_IDLE = 0,
+    /// MAC HW RESERVED State.
+    HW_RESERVED,
+    /// MAC HW DOZE State.
+    HW_DOZE,
+    /// MAC HW ACTIVE State.
+    HW_ACTIVE
+};
+
+/// Power Save mode setting
+enum mm_ps_mode_state
+{
+    MM_PS_MODE_OFF,
+    MM_PS_MODE_ON,
+    MM_PS_MODE_ON_DYN,
+};
+
+/// Status/error codes used in the MAC software.
+enum
+{
+    CO_OK,
+    CO_FAIL,
+    CO_EMPTY,
+    CO_FULL,
+    CO_BAD_PARAM,
+    CO_NOT_FOUND,
+    CO_NO_MORE_ELT_AVAILABLE,
+    CO_NO_ELT_IN_USE,
+    CO_BUSY,
+    CO_OP_IN_PROGRESS,
+};
+
+/// Remain on channel operation codes
+enum mm_remain_on_channel_op
+{
+    MM_ROC_OP_START = 0,
+    MM_ROC_OP_CANCEL,
+};
+
+#define DRV_TASK_ID 100
+
+/// Message Identifier. The number of messages is limited to 0xFFFF.
+/// The message ID is divided in two parts:
+/// - bits[15..10] : task index (no more than 64 tasks supported).
+/// - bits[9..0] : message index (no more that 1024 messages per task).
+typedef u16 lmac_msg_id_t;
+
+typedef u16 lmac_task_id_t;
+
+/// Build the first message ID of a task.
+#if defined(CONFIG_STANDALONE_WIFI)
+#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 10))
+
+#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 10))
+#define MSG_I(msg) ((msg) & ((1<<10)-1))
+
+#elif defined(CONFIG_STANDALONE_WIFI_BLE)
+#define LMAC_FIRST_MSG(task) ((lmac_msg_id_t)((task) << 8))
+
+#define MSG_T(msg) ((lmac_task_id_t)((msg) >> 8))
+#define MSG_I(msg) ((msg) & ((1<<8)-1))
+#else
+#error not defined biuld mode!
+#endif
+
+/// Message structure.
+struct lmac_msg
+{
+    lmac_msg_id_t     id;         ///< Message id.
+    lmac_task_id_t    dest_id;    ///< Destination kernel identifier.
+    lmac_task_id_t    src_id;     ///< Source kernel identifier.
+#ifdef CONFIG_ECRNX_ESWIN
+    uint8_t          hostid[8];
+#endif
+
+    u16        param_len;  ///< Parameter embedded struct length.
+    u32        param[];   ///< Parameter embedded struct. Must be word-aligned.
+};
+
+/// List of messages related to the task.
+enum mm_msg_tag
+{
+    /// RESET Request.
+    MM_RESET_REQ = LMAC_FIRST_MSG(TASK_MM),
+    /// RESET Confirmation.
+    MM_RESET_CFM,
+    /// START Request.
+    MM_START_REQ,
+    /// START Confirmation.
+    MM_START_CFM,
+    /// Read Version Request.
+    MM_VERSION_REQ,
+    /// Read Version Confirmation.
+    MM_VERSION_CFM,
+    /// ADD INTERFACE Request.
+    MM_ADD_IF_REQ,
+    /// ADD INTERFACE Confirmation.
+    MM_ADD_IF_CFM,
+    /// REMOVE INTERFACE Request.
+    MM_REMOVE_IF_REQ,
+    /// REMOVE INTERFACE Confirmation.
+    MM_REMOVE_IF_CFM,
+    /// STA ADD Request.
+    MM_STA_ADD_REQ,
+    /// STA ADD Confirm.
+    MM_STA_ADD_CFM,
+    /// STA DEL Request.
+    MM_STA_DEL_REQ,
+    /// STA DEL Confirm.
+    MM_STA_DEL_CFM,
+    /// RX FILTER CONFIGURATION Request.
+    MM_SET_FILTER_REQ,
+    /// RX FILTER CONFIGURATION Confirmation.
+    MM_SET_FILTER_CFM,
+    /// CHANNEL CONFIGURATION Request.
+    MM_SET_CHANNEL_REQ,
+    /// CHANNEL CONFIGURATION Confirmation.
+    MM_SET_CHANNEL_CFM,
+    /// DTIM PERIOD CONFIGURATION Request.
+    MM_SET_DTIM_REQ,
+    /// DTIM PERIOD CONFIGURATION Confirmation.
+    MM_SET_DTIM_CFM,
+    /// BEACON INTERVAL CONFIGURATION Request.
+    MM_SET_BEACON_INT_REQ,
+    /// BEACON INTERVAL CONFIGURATION Confirmation.
+    MM_SET_BEACON_INT_CFM,
+    /// BASIC RATES CONFIGURATION Request.
+    MM_SET_BASIC_RATES_REQ,
+    /// BASIC RATES CONFIGURATION Confirmation.
+    MM_SET_BASIC_RATES_CFM,
+    /// BSSID CONFIGURATION Request.
+    MM_SET_BSSID_REQ,
+    /// BSSID CONFIGURATION Confirmation.
+    MM_SET_BSSID_CFM,
+    /// EDCA PARAMETERS CONFIGURATION Request.
+    MM_SET_EDCA_REQ,
+    /// EDCA PARAMETERS CONFIGURATION Confirmation.
+    MM_SET_EDCA_CFM,
+    /// ABGN MODE CONFIGURATION Request.
+    MM_SET_MODE_REQ,
+    /// ABGN MODE CONFIGURATION Confirmation.
+    MM_SET_MODE_CFM,
+    /// Request setting the VIF active state (i.e associated or AP started)
+    MM_SET_VIF_STATE_REQ,
+    /// Confirmation of the @ref MM_SET_VIF_STATE_REQ message.
+    MM_SET_VIF_STATE_CFM,
+    /// SLOT TIME PARAMETERS CONFIGURATION Request.
+    MM_SET_SLOTTIME_REQ,
+    /// SLOT TIME PARAMETERS CONFIGURATION Confirmation.
+    MM_SET_SLOTTIME_CFM,
+    /// Power Mode Change Request.
+    MM_SET_IDLE_REQ,
+    /// Power Mode Change Confirm.
+    MM_SET_IDLE_CFM,
+    /// KEY ADD Request.
+    MM_KEY_ADD_REQ,
+    /// KEY ADD Confirm.
+    MM_KEY_ADD_CFM,
+    /// KEY DEL Request.
+    MM_KEY_DEL_REQ,
+    /// KEY DEL Confirm.
+    MM_KEY_DEL_CFM,
+    /// Block Ack agreement info addition
+    MM_BA_ADD_REQ,
+    /// Block Ack agreement info addition confirmation
+    MM_BA_ADD_CFM,
+    /// Block Ack agreement info deletion
+    MM_BA_DEL_REQ,
+    /// Block Ack agreement info deletion confirmation
+    MM_BA_DEL_CFM,
+    /// Indication of the primary TBTT to the upper MAC. Upon the reception of this
+    // message the upper MAC has to push the beacon(s) to the beacon transmission queue.
+    MM_PRIMARY_TBTT_IND,
+    /// Indication of the secondary TBTT to the upper MAC. Upon the reception of this
+    // message the upper MAC has to push the beacon(s) to the beacon transmission queue.
+    MM_SECONDARY_TBTT_IND,
+    /// Request for changing the TX power
+    MM_SET_POWER_REQ,
+    /// Confirmation of the TX power change
+    MM_SET_POWER_CFM,
+    /// Request to the LMAC to trigger the embedded logic analyzer and forward the debug
+    /// dump.
+    MM_DBG_TRIGGER_REQ,
+    /// Set Power Save mode
+    MM_SET_PS_MODE_REQ,
+    /// Set Power Save mode confirmation
+    MM_SET_PS_MODE_CFM,
+    /// Request to add a channel context
+    MM_CHAN_CTXT_ADD_REQ,
+    /// Confirmation of the channel context addition
+    MM_CHAN_CTXT_ADD_CFM,
+    /// Request to delete a channel context
+    MM_CHAN_CTXT_DEL_REQ,
+    /// Confirmation of the channel context deletion
+    MM_CHAN_CTXT_DEL_CFM,
+    /// Request to link a channel context to a VIF
+    MM_CHAN_CTXT_LINK_REQ,
+    /// Confirmation of the channel context link
+    MM_CHAN_CTXT_LINK_CFM,
+    /// Request to unlink a channel context from a VIF
+    MM_CHAN_CTXT_UNLINK_REQ,
+    /// Confirmation of the channel context unlink
+    MM_CHAN_CTXT_UNLINK_CFM,
+    /// Request to update a channel context
+    MM_CHAN_CTXT_UPDATE_REQ,
+    /// Confirmation of the channel context update
+    MM_CHAN_CTXT_UPDATE_CFM,
+    /// Request to schedule a channel context
+    MM_CHAN_CTXT_SCHED_REQ,
+    /// Confirmation of the channel context scheduling
+    MM_CHAN_CTXT_SCHED_CFM,
+    /// Request to change the beacon template in LMAC
+    MM_BCN_CHANGE_REQ,
+    /// Confirmation of the beacon change
+    MM_BCN_CHANGE_CFM,
+    /// Request to update the TIM in the beacon (i.e to indicate traffic bufferized at AP)
+    MM_TIM_UPDATE_REQ,
+    /// Confirmation of the TIM update
+    MM_TIM_UPDATE_CFM,
+    /// Connection loss indication
+    MM_CONNECTION_LOSS_IND,
+    /// Channel context switch indication to the upper layers
+    MM_CHANNEL_SWITCH_IND,
+    /// Channel context pre-switch indication to the upper layers
+    MM_CHANNEL_PRE_SWITCH_IND,
+    /// Request to remain on channel or cancel remain on channel
+    MM_REMAIN_ON_CHANNEL_REQ,
+    /// Confirmation of the (cancel) remain on channel request
+    MM_REMAIN_ON_CHANNEL_CFM,
+    /// Remain on channel expired indication
+    MM_REMAIN_ON_CHANNEL_EXP_IND,
+    /// Indication of a PS state change of a peer device
+    MM_PS_CHANGE_IND,
+    /// Indication that some buffered traffic should be sent to the peer device
+    MM_TRAFFIC_REQ_IND,
+    /// Request to modify the STA Power-save mode options
+    MM_SET_PS_OPTIONS_REQ,
+    /// Confirmation of the PS options setting
+    MM_SET_PS_OPTIONS_CFM,
+    /// Indication of PS state change for a P2P VIF
+    MM_P2P_VIF_PS_CHANGE_IND,
+    /// Indication that CSA counter has been updated
+    MM_CSA_COUNTER_IND,
+    /// Channel occupation report indication
+    MM_CHANNEL_SURVEY_IND,
+    /// Message containing Beamformer Information
+    MM_BFMER_ENABLE_REQ,
+    /// Request to Start/Stop/Update NOA - GO Only
+    MM_SET_P2P_NOA_REQ,
+    /// Request to Start/Stop/Update Opportunistic PS - GO Only
+    MM_SET_P2P_OPPPS_REQ,
+    /// Start/Stop/Update NOA Confirmation
+    MM_SET_P2P_NOA_CFM,
+    /// Start/Stop/Update Opportunistic PS Confirmation
+    MM_SET_P2P_OPPPS_CFM,
+    /// P2P NoA Update Indication - GO Only
+    MM_P2P_NOA_UPD_IND,
+    /// Request to set RSSI threshold and RSSI hysteresis
+    MM_CFG_RSSI_REQ,
+    /// Indication that RSSI level is below or above the threshold
+    MM_RSSI_STATUS_IND,
+    /// Indication that CSA is done
+    MM_CSA_FINISH_IND,
+    /// Indication that CSA is in prorgess (resp. done) and traffic must be stopped (resp. restarted)
+    MM_CSA_TRAFFIC_IND,
+    /// Request to update the group information of a station
+    MM_MU_GROUP_UPDATE_REQ,
+    /// Confirmation of the @ref MM_MU_GROUP_UPDATE_REQ message
+    MM_MU_GROUP_UPDATE_CFM,
+    /// Request to initialize the antenna diversity algorithm
+    MM_ANT_DIV_INIT_REQ,
+    /// Request to stop the antenna diversity algorithm
+    MM_ANT_DIV_STOP_REQ,
+    /// Request to update the antenna switch status
+    MM_ANT_DIV_UPDATE_REQ,
+    /// Request to switch the antenna connected to path_0
+    MM_SWITCH_ANTENNA_REQ,
+    /// Indication that a packet loss has occurred
+    MM_PKTLOSS_IND,
+    /// MU EDCA PARAMETERS Configuration Request.
+    MM_SET_MU_EDCA_REQ,
+    /// MU EDCA PARAMETERS Configuration Confirmation.
+    MM_SET_MU_EDCA_CFM,
+    /// UORA PARAMETERS Configuration Request.
+    MM_SET_UORA_REQ,
+    /// UORA PARAMETERS Configuration Confirmation.
+    MM_SET_UORA_CFM,
+    /// TXOP RTS THRESHOLD Configuration Request.
+    MM_SET_TXOP_RTS_THRES_REQ,
+    /// TXOP RTS THRESHOLD Configuration Confirmation.
+    MM_SET_TXOP_RTS_THRES_CFM,
+    /// HE BSS Color Configuration Request.
+    MM_SET_BSS_COLOR_REQ,
+    /// HE BSS Color Configuration Confirmation.
+    MM_SET_BSS_COLOR_CFM,
+    /// Calibration Gain Delta Configuration Request.
+    MM_SET_GAIN_DELTA_REQ,
+    /// Calibration Gain Delta Configuration Confirmation.
+    MM_SET_GAIN_DELTA_CFM,
+    /// Calibration result get Request.
+    MM_GET_CAL_RESULT_REQ,
+    /// Calibration result get Confirmation.
+    MM_GET_CAL_RESULT_CFM,
+
+    /*
+     * Section of internal MM messages. No MM API messages should be defined below this point
+     */
+    /// MAX number of messages
+    MM_MAX,
+};
+
+/// Interface types
+enum
+{
+    /// ESS STA interface
+    MM_STA,
+    /// IBSS STA interface
+    MM_IBSS,
+    /// AP interface
+    MM_AP,
+    // Mesh Point interface
+    MM_MESH_POINT,
+    // Monitor interface
+    MM_MONITOR,
+};
+
+///BA agreement types
+enum
+{
+    ///BlockAck agreement for TX
+    BA_AGMT_TX,
+    ///BlockAck agreement for RX
+    BA_AGMT_RX,
+};
+
+///BA agreement related status
+enum
+{
+    ///Correct BA agreement establishment
+    BA_AGMT_ESTABLISHED,
+    ///BA agreement already exists for STA+TID requested, cannot override it (should have been deleted first)
+    BA_AGMT_ALREADY_EXISTS,
+    ///Correct BA agreement deletion
+    BA_AGMT_DELETED,
+    ///BA agreement for the (STA, TID) doesn't exist so nothing to delete
+    BA_AGMT_DOESNT_EXIST,
+};
+
+/// Features supported by LMAC - Positions
+enum mm_features
+{
+    /// Beaconing
+    MM_FEAT_BCN_BIT         = 0,
+    /// Autonomous Beacon Transmission
+    MM_FEAT_AUTOBCN_BIT,
+    /// Scan in LMAC
+    MM_FEAT_HWSCAN_BIT,
+    /// Connection Monitoring
+    MM_FEAT_CMON_BIT,
+    /// Multi Role
+    MM_FEAT_MROLE_BIT,
+    /// Radar Detection
+    MM_FEAT_RADAR_BIT,
+    /// Power Save
+    MM_FEAT_PS_BIT,
+    /// UAPSD
+    MM_FEAT_UAPSD_BIT,
+    /// DPSM
+    MM_FEAT_DPSM_BIT,
+    /// A-MPDU
+    MM_FEAT_AMPDU_BIT,
+    /// A-MSDU
+    MM_FEAT_AMSDU_BIT,
+    /// Channel Context
+    MM_FEAT_CHNL_CTXT_BIT,
+    /// Packet reordering
+    MM_FEAT_REORD_BIT,
+    /// P2P
+    MM_FEAT_P2P_BIT,
+    /// P2P Go
+    MM_FEAT_P2P_GO_BIT,
+    /// UMAC Present
+    MM_FEAT_UMAC_BIT,
+    /// VHT support
+    MM_FEAT_VHT_BIT,
+    /// Beamformee
+    MM_FEAT_BFMEE_BIT,
+    /// Beamformer
+    MM_FEAT_BFMER_BIT,
+    /// WAPI
+    MM_FEAT_WAPI_BIT,
+    /// MFP
+    MM_FEAT_MFP_BIT,
+    /// Mu-MIMO RX support
+    MM_FEAT_MU_MIMO_RX_BIT,
+    /// Mu-MIMO TX support
+    MM_FEAT_MU_MIMO_TX_BIT,
+    /// Wireless Mesh Networking
+    MM_FEAT_MESH_BIT,
+    /// TDLS support
+    MM_FEAT_TDLS_BIT,
+    /// Antenna Diversity support
+    MM_FEAT_ANT_DIV_BIT,
+    /// UF support
+    MM_FEAT_UF_BIT,
+    /// A-MSDU maximum size (bit0)
+    MM_AMSDU_MAX_SIZE_BIT0,
+    /// A-MSDU maximum size (bit1)
+    MM_AMSDU_MAX_SIZE_BIT1,
+    /// MON_DATA support
+    MM_FEAT_MON_DATA_BIT,
+    /// HE (802.11ax) support
+    MM_FEAT_HE_BIT,
+    MM_FEAT_TWT_BIT,
+};
+
+/// Maximum number of words in the configuration buffer
+#define PHY_CFG_BUF_SIZE     16
+
+/// Structure containing the parameters of the PHY configuration
+struct phy_cfg_tag
+{
+    /// Buffer containing the parameters specific for the PHY used
+    u32_l parameters[PHY_CFG_BUF_SIZE];
+};
+
+/// Structure containing the parameters of the Trident PHY configuration
+struct phy_trd_cfg_tag
+{
+    /// MDM type(nxm)(upper nibble) and MDM2RF path mapping(lower nibble)
+    u8_l path_mapping;
+    /// TX DC offset compensation
+    u32_l tx_dc_off_comp;
+};
+
+/// Structure containing the parameters of the Karst PHY configuration
+struct phy_karst_cfg_tag
+{
+    /// TX IQ mismatch compensation in 2.4GHz
+    u32_l tx_iq_comp_2_4G[2];
+    /// RX IQ mismatch compensation in 2.4GHz
+    u32_l rx_iq_comp_2_4G[2];
+    /// TX IQ mismatch compensation in 5GHz
+    u32_l tx_iq_comp_5G[2];
+    /// RX IQ mismatch compensation in 5GHz
+    u32_l rx_iq_comp_5G[2];
+    /// RF path used by default (0 or 1)
+    u8_l path_used;
+};
+
+struct phy_cataxia_cfg_tag
+{
+    u8_l path_used;
+};
+/// Structure containing the parameters of the @ref MM_START_REQ message
+struct mm_start_req
+{
+    /// PHY configuration
+    struct phy_cfg_tag phy_cfg;
+    /// UAPSD timeout
+    u32_l uapsd_timeout;
+    /// Local LP clock accuracy (in ppm)
+    u16_l lp_clk_accuracy;
+    u16_l tx_timeout[AC_MAX];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_REQ message
+struct mm_set_channel_req
+{
+    /// Channel information
+    struct mac_chan_op chan;
+    /// Index of the RF for which the channel has to be set (0: operating (primary), 1: secondary
+    /// RF (used for additional radar detection). This parameter is reserved if no secondary RF
+    /// is available in the system
+    u8_l index;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_CHANNEL_CFM message
+struct mm_set_channel_cfm
+{
+    /// Radio index to be used in policy table
+    u8_l radio_idx;
+    /// TX power configured (in dBm)
+    s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_DTIM_REQ message
+struct mm_set_dtim_req
+{
+    /// DTIM period
+    u8_l dtim_period;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_POWER_REQ message
+struct mm_set_power_req
+{
+    /// Index of the interface for which the parameter is configured
+    u8_l inst_nbr;
+    /// TX power (in dBm)
+    s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_POWER_CFM message
+struct mm_set_power_cfm
+{
+    /// Radio index to be used in policy table
+    u8_l radio_idx;
+    /// TX power configured (in dBm)
+    s8_l power;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BEACON_INT_REQ message
+struct mm_set_beacon_int_req
+{
+    /// Beacon interval
+    u16_l beacon_int;
+    /// Index of the interface for which the parameter is configured
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BASIC_RATES_REQ message
+struct mm_set_basic_rates_req
+{
+    /// Basic rate set (as expected by bssBasicRateSet field of Rates MAC HW register)
+    u32_l rates;
+    /// Index of the interface for which the parameter is configured
+    u8_l inst_nbr;
+    /// Band on which the interface will operate
+    u8_l band;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BSSID_REQ message
+struct mm_set_bssid_req
+{
+    /// BSSID to be configured in HW
+    struct mac_addr bssid;
+    /// Index of the interface for which the parameter is configured
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_FILTER_REQ message
+struct mm_set_filter_req
+{
+    /// RX filter to be put into rxCntrlReg HW register
+    u32_l filter;
+};
+
+/// Structure containing the parameters of the @ref MM_ADD_IF_REQ message.
+struct mm_add_if_req
+{
+    /// Type of the interface (AP, STA, ADHOC, ...)
+    u8_l type;
+    /// MAC ADDR of the interface to start
+    struct mac_addr addr;
+    /// P2P Interface
+    bool_l p2p;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_EDCA_REQ message
+struct mm_set_edca_req
+{
+    /// EDCA parameters of the queue (as expected by edcaACxReg HW register)
+    u32_l ac_param;
+    /// Flag indicating if UAPSD can be used on this queue
+    bool_l uapsd;
+    /// HW queue for which the parameters are configured
+    u8_l hw_queue;
+    /// Index of the interface for which the parameters are configured
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_MU_EDCA_REQ message
+struct mm_set_mu_edca_req
+{
+    /// MU EDCA parameters of the different HE queues
+    u32_l param[AC_MAX];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UORA_REQ message
+struct mm_set_uora_req
+{
+    /// Minimum exponent of OFDMA Contention Window.
+    u8_l eocw_min;
+    /// Maximum exponent of OFDMA Contention Window.
+    u8_l eocw_max;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_TXOP_RTS_THRES_REQ message
+struct mm_set_txop_rts_thres_req
+{
+    /// TXOP RTS threshold
+    u16_l txop_dur_rts_thres;
+    /// Index of the interface for which the parameter is configured
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_BSS_COLOR_REQ message
+struct mm_set_bss_color_req
+{
+    /// HE BSS color, formatted as per BSS_COLOR MAC HW register
+    u32_l bss_color;
+};
+
+struct mm_set_idle_req
+{
+    u8_l hw_idle;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_SLOTTIME_REQ message
+struct mm_set_slottime_req
+{
+    /// Slot time expressed in us
+    u8_l slottime;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_MODE_REQ message
+struct mm_set_mode_req
+{
+    /// abgnMode field of macCntrl1Reg register
+    u8_l abgnmode;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_VIF_STATE_REQ message
+struct mm_set_vif_state_req
+{
+    /// Association Id received from the AP (valid only if the VIF is of STA type)
+    u16_l aid;
+    /// Flag indicating if the VIF is active or not
+    bool_l active;
+    /// Interface index
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_ADD_IF_CFM message.
+struct mm_add_if_cfm
+{
+    /// Status of operation (different from 0 if unsuccessful)
+    u8_l status;
+    /// Interface index assigned by the LMAC
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_REMOVE_IF_REQ message.
+struct mm_remove_if_req
+{
+    /// Interface index assigned by the LMAC
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_VERSION_CFM message.
+struct mm_version_cfm
+{
+    /// Version of the LMAC FW
+    u32_l version_lmac;
+    /// Version1 of the MAC HW (as encoded in version1Reg MAC HW register)
+    u32_l version_machw_1;
+    /// Version2 of the MAC HW (as encoded in version2Reg MAC HW register)
+    u32_l version_machw_2;
+    /// Version1 of the PHY (depends on actual PHY)
+    u32_l version_phy_1;
+    /// Version2 of the PHY (depends on actual PHY)
+    u32_l version_phy_2;
+    /// Supported Features
+    u32_l features;
+    /// Maximum number of supported stations
+    u16_l max_sta_nb;
+    /// Maximum number of supported virtual interfaces
+    u8_l max_vif_nb;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_ADD_REQ message.
+struct mm_sta_add_req
+{
+    u32_l capa_flags;
+    /// Maximum A-MPDU size, in bytes, for HE frames
+    u32_l ampdu_size_max_he;
+    /// Maximum A-MPDU size, in bytes, for VHT frames
+    u32_l ampdu_size_max_vht;
+    /// PAID/GID
+    u32_l paid_gid;
+    /// Maximum A-MPDU size, in bytes, for HT frames
+    u16_l ampdu_size_max_ht;
+    /// MAC address of the station to be added
+    struct mac_addr mac_addr;
+    /// A-MPDU spacing, in us
+    u8_l ampdu_spacing_min;
+    /// Interface index
+    u8_l inst_nbr;
+    /// TDLS station
+    bool_l tdls_sta;
+    /// Indicate if the station is TDLS link initiator station
+    bool_l tdls_sta_initiator;
+    /// Indicate if the TDLS Channel Switch is allowed
+    bool_l tdls_chsw_allowed;
+    /// nonTransmitted BSSID index, set to the BSSID index in case the STA added is an AP
+    /// that is a nonTransmitted BSSID. Should be set to 0 otherwise
+    u8_l bssid_index;
+    /// Maximum BSSID indicator, valid if the STA added is an AP that is a nonTransmitted
+    /// BSSID
+    u8_l max_bssid_ind;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_ADD_CFM message.
+struct mm_sta_add_cfm
+{
+    /// Status of the operation (different from 0 if unsuccessful)
+    u8_l status;
+    /// Index assigned by the LMAC to the newly added station
+    u8_l sta_idx;
+    /// MAC HW index of the newly added station
+    u8_l hw_sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_DEL_REQ message.
+struct mm_sta_del_req
+{
+    /// Index of the station to be deleted
+    u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_STA_DEL_CFM message.
+struct mm_sta_del_cfm
+{
+    /// Status of the operation (different from 0 if unsuccessful)
+    u8_l     status;
+};
+
+/// Structure containing the parameters of the SET_POWER_MODE REQ message.
+struct mm_setpowermode_req
+{
+    u8_l mode;
+    u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the SET_POWER_MODE CFM message.
+struct mm_setpowermode_cfm
+{
+    u8_l     status;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_ADD REQ message.
+struct mm_key_add_req
+{
+    /// Key index (valid only for default keys)
+    u8_l key_idx;
+    /// STA index (valid only for pairwise or mesh group keys)
+    u8_l sta_idx;
+    /// Key material
+    struct mac_sec_key key;
+    /// Cipher suite (WEP64, WEP128, TKIP, CCMP)
+    u8_l cipher_suite;
+    /// Index of the interface for which the key is set (valid only for default keys or mesh group keys)
+    u8_l inst_nbr;
+    /// A-MSDU SPP parameter
+    u8_l spp;
+    /// Indicate if provided key is a pairwise key or not
+    bool_l pairwise;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_ADD_CFM message.
+struct mm_key_add_cfm
+{
+    /// Status of the operation (different from 0 if unsuccessful)
+    u8_l status;
+    /// HW index of the key just added
+    u8_l hw_key_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_KEY_DEL_REQ message.
+struct mm_key_del_req
+{
+    /// HW index of the key to be deleted
+    u8_l hw_key_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_ADD_REQ message.
+struct mm_ba_add_req
+{
+    ///Type of agreement (0: TX, 1: RX)
+    u8_l  type;
+    ///Index of peer station with which the agreement is made
+    u8_l  sta_idx;
+    ///TID for which the agreement is made with peer station
+    u8_l  tid;
+    ///Buffer size - number of MPDUs that can be held in its buffer per TID
+    u8_l  bufsz;
+    /// Start sequence number negotiated during BA setup - the one in first aggregated MPDU counts more
+    u16_l ssn;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_ADD_CFM message.
+struct mm_ba_add_cfm
+{
+    ///Index of peer station for which the agreement is being confirmed
+    u8_l sta_idx;
+    ///TID for which the agreement is being confirmed
+    u8_l tid;
+    /// Status of ba establishment
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_DEL_REQ message.
+struct mm_ba_del_req
+{
+    ///Type of agreement (0: TX, 1: RX)
+    u8_l type;
+    ///Index of peer station for which the agreement is being deleted
+    u8_l sta_idx;
+    ///TID for which the agreement is being deleted
+    u8_l tid;
+};
+
+/// Structure containing the parameters of the @ref MM_BA_DEL_CFM message.
+struct mm_ba_del_cfm
+{
+    ///Index of peer station for which the agreement deletion is being confirmed
+    u8_l sta_idx;
+    ///TID for which the agreement deletion is being confirmed
+    u8_l tid;
+    /// Status of ba deletion
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
+struct mm_chan_ctxt_add_req
+{
+    /// Operating channel
+    struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_ADD_REQ message
+struct mm_chan_ctxt_add_cfm
+{
+    /// Status of the addition
+    u8_l status;
+    /// Index of the new channel context
+    u8_l index;
+};
+
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_DEL_REQ message
+struct mm_chan_ctxt_del_req
+{
+    /// Index of the new channel context to be deleted
+    u8_l index;
+};
+
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_LINK_REQ message
+struct mm_chan_ctxt_link_req
+{
+    /// VIF index
+    u8_l vif_index;
+    /// Channel context index
+    u8_l chan_index;
+    /// Indicate if this is a channel switch (unlink current ctx first if true)
+    u8_l chan_switch;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UNLINK_REQ message
+struct mm_chan_ctxt_unlink_req
+{
+    /// VIF index
+    u8_l vif_index;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_UPDATE_REQ message
+struct mm_chan_ctxt_update_req
+{
+    /// Channel context index
+    u8_l chan_index;
+    /// New channel information
+    struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref MM_CHAN_CTXT_SCHED_REQ message
+struct mm_chan_ctxt_sched_req
+{
+    /// VIF index
+    u8_l vif_index;
+    /// Channel context index
+    u8_l chan_index;
+    /// Type of the scheduling request (0: normal scheduling, 1: derogatory
+    /// scheduling)
+    u8_l type;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_SWITCH_IND message
+struct mm_channel_switch_ind
+{
+    /// Index of the channel context we will switch to
+    u8_l chan_index;
+    /// Indicate if the switch has been triggered by a Remain on channel request
+    bool_l roc;
+    /// VIF on which remain on channel operation has been started (if roc == 1)
+    u8_l vif_index;
+    /// Indicate if the switch has been triggered by a TDLS Remain on channel request
+    bool_l roc_tdls;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_PRE_SWITCH_IND message
+struct mm_channel_pre_switch_ind
+{
+    /// Index of the channel context we will switch to
+    u8_l chan_index;
+};
+
+/// Structure containing the parameters of the @ref MM_CONNECTION_LOSS_IND message.
+struct mm_connection_loss_ind
+{
+    /// VIF instance number
+    u8_l inst_nbr;
+};
+
+
+/// Structure containing the parameters of the @ref MM_DBG_TRIGGER_REQ message.
+struct mm_dbg_trigger_req
+{
+    /// Error trace to be reported by the LMAC
+    char error[64];
+};
+
+/// Structure containing the parameters of the @ref MM_SET_PS_MODE_REQ message.
+struct mm_set_ps_mode_req
+{
+    /// Power Save is activated or deactivated
+    u8_l  new_state;
+};
+
+/// Structure containing the parameters of the @ref MM_BCN_CHANGE_REQ message.
+#define BCN_MAX_CSA_CPT 2
+struct mm_bcn_change_req
+{
+    /// Offset of the TIM IE in the beacon
+    u16_l tim_oft;
+    /// Length of the TIM IE
+    u8_l tim_len;
+    /// Index of the VIF for which the beacon is updated
+    u8_l inst_nbr;
+    /// Offset of CSA (channel switch announcement) counters (0 means no counter)
+    u8_l csa_oft[BCN_MAX_CSA_CPT];
+    /// Length of the beacon template
+    u16_l bcn_len;
+    /// Pointer, in host memory, to the new beacon template
+    ptr_addr bcn_ptr;
+};
+
+
+/// Structure containing the parameters of the @ref MM_TIM_UPDATE_REQ message.
+struct mm_tim_update_req
+{
+    /// Association ID of the STA the bit of which has to be updated (0 for BC/MC traffic)
+    u16_l aid;
+    /// Flag indicating the availability of data packets for the given STA
+    u8_l tx_avail;
+    /// Index of the VIF for which the TIM is updated
+    u8_l inst_nbr;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_REQ message.
+struct mm_remain_on_channel_req
+{
+    /// Operation Code
+    u8_l op_code;
+    /// VIF Index
+    u8_l vif_index;
+    /// Channel parameter
+    struct mac_chan_op chan;
+    /// Duration (in ms)
+    u32_l duration_ms;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_CFM message
+struct mm_remain_on_channel_cfm
+{
+    /// Operation Code
+    u8_l op_code;
+    /// Status of the operation
+    u8_l status;
+    /// Channel Context index
+    u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref MM_REMAIN_ON_CHANNEL_EXP_IND message
+struct mm_remain_on_channel_exp_ind
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// Channel Context index
+    u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_REQ message.
+struct mm_set_uapsd_tmr_req
+{
+    /// action: Start or Stop the timer
+    u8_l  action;
+    /// timeout value, in milliseconds
+    u32_l  timeout;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_UAPSD_TMR_CFM message.
+struct mm_set_uapsd_tmr_cfm
+{
+    /// Status of the operation (different from 0 if unsuccessful)
+    u8_l     status;
+};
+
+
+/// Structure containing the parameters of the @ref MM_PS_CHANGE_IND message
+struct mm_ps_change_ind
+{
+    /// Index of the peer device that is switching its PS state
+    u8_l sta_idx;
+    /// New PS state of the peer device (0: active, 1: sleeping)
+    u8_l ps_state;
+};
+
+/// Structure containing the parameters of the @ref MM_P2P_VIF_PS_CHANGE_IND message
+struct mm_p2p_vif_ps_change_ind
+{
+    /// Index of the P2P VIF that is switching its PS state
+    u8_l vif_index;
+    /// New PS state of the P2P VIF interface (0: active, 1: sleeping)
+    u8_l ps_state;
+};
+
+/// Structure containing the parameters of the @ref MM_TRAFFIC_REQ_IND message
+struct mm_traffic_req_ind
+{
+    /// Index of the peer device that needs traffic
+    u8_l sta_idx;
+    /// Number of packets that need to be sent (if 0, all buffered traffic shall be sent and
+    /// if set to @ref PS_SP_INTERRUPTED, it means that current service period has been interrupted)
+    u8_l pkt_cnt;
+    /// Flag indicating if the traffic request concerns U-APSD queues or not
+    bool_l uapsd;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_PS_OPTIONS_REQ message.
+struct mm_set_ps_options_req
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// Listen interval (0 if wake up shall be based on DTIM period)
+    u16_l listen_interval;
+    /// Flag indicating if we shall listen the BC/MC traffic or not
+    bool_l dont_listen_bc_mc;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_COUNTER_IND message
+struct mm_csa_counter_ind
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// Updated CSA counter value
+    u8_l csa_count;
+};
+
+/// Structure containing the parameters of the @ref MM_CHANNEL_SURVEY_IND message
+struct mm_channel_survey_ind
+{
+    /// Frequency of the channel
+    u16_l freq;
+    /// Noise in dbm
+    s8_l noise_dbm;
+    /// Amount of time spent of the channel (in ms)
+    u32_l chan_time_ms;
+    /// Amount of time the primary channel was sensed busy
+    u32_l chan_time_busy_ms;
+};
+
+/// Structure containing the parameters of the @ref MM_BFMER_ENABLE_REQ message.
+struct mm_bfmer_enable_req
+{
+    /**
+     * Address of the beamforming report space allocated in host memory
+     * (Valid only if vht_su_bfmee is true)
+     */
+    u32_l host_bfr_addr;
+    /**
+     * Size of the beamforming report space allocated in host memory. This space should
+     * be twice the maximum size of the expected beamforming reports as the FW will
+     * divide it in two in order to be able to upload a new report while another one is
+     * used in transmission
+     */
+    u16_l host_bfr_size;
+    /// AID
+    u16_l aid;
+    /// Station Index
+    u8_l sta_idx;
+    /// Maximum number of spatial streams the station can receive
+    u8_l rx_nss;
+    /**
+     * Indicate if peer STA is MU Beamformee (VHT) capable
+     * (Valid only if vht_su_bfmee is true)
+     */
+    bool_l vht_mu_bfmee;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_REQ message.
+struct mm_set_p2p_noa_req
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// Allocated NOA Instance Number - Valid only if count = 0
+    u8_l noa_inst_nb;
+    /// Count
+    u8_l count;
+    /// Indicate if NoA can be paused for traffic reason
+    bool_l dyn_noa;
+    /// Duration (in us)
+    u32_l duration_us;
+    /// Interval (in us)
+    u32_l interval_us;
+    /// Start Time offset from next TBTT (in us)
+    u32_l start_offset;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_REQ message.
+struct mm_set_p2p_oppps_req
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// CTWindow
+    u8_l ctwindow;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_NOA_CFM message.
+struct mm_set_p2p_noa_cfm
+{
+    /// Request status
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_SET_P2P_OPPPS_CFM message.
+struct mm_set_p2p_oppps_cfm
+{
+    /// Request status
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MM_P2P_NOA_UPD_IND message.
+struct mm_p2p_noa_upd_ind
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// NOA Instance Number
+    u8_l noa_inst_nb;
+    /// NoA Type
+    u8_l noa_type;
+    /// Count
+    u8_l count;
+    /// Duration (in us)
+    u32_l duration_us;
+    /// Interval (in us)
+    u32_l interval_us;
+    /// Start Time
+    u32_l start_time;
+};
+
+/// Structure containing the parameters of the @ref MM_CFG_RSSI_REQ message
+struct mm_cfg_rssi_req
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// RSSI threshold
+    s8_l rssi_thold;
+    /// RSSI hysteresis
+    u8_l rssi_hyst;
+};
+
+/// Structure containing the parameters of the @ref MM_RSSI_STATUS_IND message
+struct mm_rssi_status_ind
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// Status of the RSSI
+    bool_l rssi_status;
+    /// Current RSSI
+    s8_l rssi;
+};
+
+/// Structure containing the parameters of the @ref MM_PKTLOSS_IND message
+struct mm_pktloss_ind
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// Address of the STA for which there is a packet loss
+    struct mac_addr mac_addr;
+    /// Number of packets lost
+    u32 num_packets;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_FINISH_IND message
+struct mm_csa_finish_ind
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// Status of the operation
+    u8_l status;
+    /// New channel ctx index
+    u8_l chan_idx;
+};
+
+/// Structure containing the parameters of the @ref MM_CSA_TRAFFIC_IND message
+struct mm_csa_traffic_ind
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// Is tx traffic enable or disable
+    bool_l enable;
+};
+
+/// Structure containing the parameters of the @ref MM_MU_GROUP_UPDATE_REQ message.
+/// Size allocated for the structure depends of the number of group
+struct mm_mu_group_update_req
+{
+    /// Station index
+    u8_l sta_idx;
+    /// Number of groups the STA belongs to
+    u8_l group_cnt;
+    /// Group information
+    struct
+    {
+        /// Group Id
+        u8_l group_id;
+        /// User position
+        u8_l user_pos;
+    } groups[0];
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Scan messages
+///////////////////////////////////////////////////////////////////////////////
+enum scan_msg_tag
+{
+    /// Scanning start Request.
+    SCAN_START_REQ = LMAC_FIRST_MSG(TASK_SCAN),
+    /// Scanning start Confirmation.
+    SCAN_START_CFM,
+    /// End of scanning indication.
+    SCAN_DONE_IND,
+    /// Cancel scan request
+    SCAN_CANCEL_REQ,
+    /// Cancel scan confirmation
+    SCAN_CANCEL_CFM,
+
+    /// MAX number of messages
+    SCAN_MAX,
+};
+
+/// Maximum number of SSIDs in a scan request
+#define SCAN_SSID_MAX   4
+
+/// Maximum number of channels in a scan request
+#define SCAN_CHANNEL_MAX (MAC_DOMAINCHANNEL_24G_MAX + MAC_DOMAINCHANNEL_5G_MAX)
+
+/// Maximum length of the ProbeReq IEs (SoftMAC mode)
+#define SCAN_MAX_IE_LEN 300
+
+/// Maximum number of PHY bands supported
+#define SCAN_BAND_MAX 2
+
+/// Structure containing the parameters of the @ref SCAN_START_REQ message
+struct scan_start_req
+{
+    /// List of channel to be scanned
+    struct mac_chan_def chan[SCAN_CHANNEL_MAX];
+    /// List of SSIDs to be scanned
+    struct mac_ssid ssid[SCAN_SSID_MAX];
+    /// BSSID to be scanned
+    struct mac_addr bssid;
+    /// Index of the VIF that is scanning
+    u8_l vif_idx;
+    /// Number of channels to scan
+    u8_l chan_cnt;
+    /// Number of SSIDs to scan for
+    u8_l ssid_cnt;
+    /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
+    bool no_cck;
+    u32_l duration;
+    /// Length of the additional IEs
+    u16_l add_ie_len;
+    /// Pointer (in host memory) to the additional IEs that need to be added to the ProbeReq
+    /// (following the SSID element)
+    ptr_addr add_ies;
+};
+
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
+struct scan_start_cfm
+{
+    /// Status of the request
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref SCAN_CANCEL_REQ message
+struct scan_cancel_req
+{
+};
+
+/// Structure containing the parameters of the @ref SCAN_START_CFM message
+struct scan_cancel_cfm
+{
+    /// Status of the request
+    u8_l status;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Scanu messages
+///////////////////////////////////////////////////////////////////////////////
+/// Messages that are logically related to the task.
+enum
+{
+    /// Scan request from host.
+    SCANU_START_REQ = LMAC_FIRST_MSG(TASK_SCANU),
+    /// Scanning start Confirmation.
+    SCANU_START_CFM,
+    /// Join request
+    SCANU_JOIN_REQ,
+    /// Join confirmation.
+    SCANU_JOIN_CFM,
+    /// Scan result indication.
+    SCANU_RESULT_IND,
+    /// Fast scan request from any other module.
+    SCANU_FAST_REQ,
+    /// Confirmation of fast scan request.
+    SCANU_FAST_CFM,
+    ///Scan cancel from host.
+    SCANU_CANCEL_REQ,
+    ///Scan cancel confirmation
+    SCANU_CANCEL_CFM,
+
+    /// MAX number of messages
+    SCANU_MAX,
+};
+
+/// Maximum length of the additional ProbeReq IEs (FullMAC mode)
+#define SCANU_MAX_IE_LEN  200
+
+/// Structure containing the parameters of the @ref SCANU_START_REQ message
+struct scanu_start_req
+{
+    /// List of channel to be scanned
+    struct mac_chan_def chan[SCAN_CHANNEL_MAX];
+    /// List of SSIDs to be scanned
+    struct mac_ssid ssid[SCAN_SSID_MAX];
+    /// BSSID to be scanned (or WILDCARD BSSID if no BSSID is searched in particular)
+    struct mac_addr bssid;
+    /// Index of the VIF that is scanning
+    u8_l vif_idx;
+    /// Number of channels to scan
+    u8_l chan_cnt;
+    /// Number of SSIDs to scan for
+    u8_l ssid_cnt;
+    /// no CCK - For P2P frames not being sent at CCK rate in 2GHz band.
+    bool no_cck;
+    u32_l duration;
+    /// Length of the additional IEs
+    u16_l add_ie_len;
+    /// Address (in host memory) of the additional IEs that need to be added to the ProbeReq
+    /// (following the SSID element)
+    ptr_addr add_ies;
+};
+
+/// Structure containing the parameters of the @ref SCANU_START_CFM message
+struct scanu_start_cfm
+{
+    /// Index of the VIF that was scanning
+    u8_l vif_idx;
+    /// Status of the request
+    u8_l status;
+    /// Number of scan results available
+    u8_l result_cnt;
+};
+
+/// Parameters of the @SCANU_RESULT_IND message
+struct scanu_result_ind
+{
+    /// Length of the frame
+    u16_l length;
+    /// Frame control field of the frame.
+    u16_l framectrl;
+    /// Center frequency on which we received the packet
+    u16_l center_freq;
+    /// PHY band
+    u8_l band;
+    /// Index of the station that sent the frame. 0xFF if unknown.
+    u8_l sta_idx;
+    /// Index of the VIF that received the frame. 0xFF if unknown.
+    u8_l inst_nbr;
+    /// RSSI of the received frame.
+    s8_l rssi;
+    /// Frame payload.
+    u32_l payload[];
+};
+
+/// Structure containing the parameters of the message.
+struct scanu_fast_req
+{
+    /// The SSID to scan in the channel.
+    struct mac_ssid ssid;
+    /// BSSID.
+    struct mac_addr bssid;
+    /// Probe delay.
+    u16_l probe_delay;
+    /// Minimum channel time.
+    u16_l minch_time;
+    /// Maximum channel time.
+    u16_l maxch_time;
+    /// The channel number to scan.
+    u16_l ch_nbr;
+};
+/// Structure containing the parameters of the @ref SCANU_CANCEL_REQ message
+struct scanu_cancel_req
+{
+    /// index of the scan element
+    uint8_t vif_idx;
+};
+
+/// Structure containing the parameters of the @ref SCANU_CANCEL_CFM and
+/// @ref SCANU_CANCEL_CFM messages
+struct scanu_cancel_cfm
+{
+    /// Index of the VIF that was scanning
+    uint8_t vif_idx;
+    /// Status of the request
+    uint8_t status;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For ME messages
+///////////////////////////////////////////////////////////////////////////////
+/// Messages that are logically related to the task.
+enum
+{
+    /// Configuration request from host.
+    ME_CONFIG_REQ = LMAC_FIRST_MSG(TASK_ME),
+    /// Configuration confirmation.
+    ME_CONFIG_CFM,
+    /// Configuration request from host.
+    ME_CHAN_CONFIG_REQ,
+    /// Configuration confirmation.
+    ME_CHAN_CONFIG_CFM,
+    /// Set control port state for a station.
+    ME_SET_CONTROL_PORT_REQ,
+    /// Control port setting confirmation.
+    ME_SET_CONTROL_PORT_CFM,
+    /// TKIP MIC failure indication.
+    ME_TKIP_MIC_FAILURE_IND,
+    /// Add a station to the FW (AP mode)
+    ME_STA_ADD_REQ,
+    /// Confirmation of the STA addition
+    ME_STA_ADD_CFM,
+    /// Delete a station from the FW (AP mode)
+    ME_STA_DEL_REQ,
+    /// Confirmation of the STA deletion
+    ME_STA_DEL_CFM,
+    /// Indication of a TX RA/TID queue credit update
+    ME_TX_CREDITS_UPDATE_IND,
+    /// Request indicating to the FW that there is traffic buffered on host
+    ME_TRAFFIC_IND_REQ,
+    /// Confirmation that the @ref ME_TRAFFIC_IND_REQ has been executed
+    ME_TRAFFIC_IND_CFM,
+    /// Request of RC statistics to a station
+    ME_RC_STATS_REQ,
+    /// RC statistics confirmation
+    ME_RC_STATS_CFM,
+    /// RC fixed rate request
+    ME_RC_SET_RATE_REQ,
+    /// Configure monitor interface
+    ME_CONFIG_MONITOR_REQ,
+    /// Configure monitor interface response
+    ME_CONFIG_MONITOR_CFM,
+    /// Setting power Save mode request from host
+    ME_SET_PS_MODE_REQ,
+    /// Set power Save mode confirmation
+    ME_SET_PS_MODE_CFM,
+    /// MAX number of messages
+    ME_MAX,
+};
+
+/// Structure containing the parameters of the @ref ME_START_REQ message
+struct me_config_req
+{
+    /// HT Capabilities
+    struct mac_htcapability ht_cap;
+    /// VHT Capabilities
+    struct mac_vhtcapability vht_cap;
+    /// HE capabilities
+    struct mac_hecapability he_cap;
+    /// Lifetime of packets sent under a BlockAck agreement (expressed in TUs)
+    u16_l tx_lft;
+    /// Maximum supported BW
+    u8_l phy_bw_max;
+    /// Boolean indicating if HT is supported or not
+    bool_l ht_supp;
+    /// Boolean indicating if VHT is supported or not
+    bool_l vht_supp;
+    /// Boolean indicating if HE is supported or not
+    bool_l he_supp;
+    /// Boolean indicating if HE OFDMA UL is enabled or not
+    bool_l he_ul_on;
+    /// Boolean indicating if PS mode shall be enabled or not
+    bool_l ps_on;
+    /// Boolean indicating if Antenna Diversity shall be enabled or not
+    bool_l ant_div_on;
+    /// Boolean indicating if Dynamic PS mode shall be used or not
+    bool_l dpsm;
+    unsigned char sleep_flag;
+};
+
+/// Structure containing the parameters of the @ref ME_CHAN_CONFIG_REQ message
+struct me_chan_config_req
+{
+    /// List of 2.4GHz supported channels
+    struct mac_chan_def chan2G4[MAC_DOMAINCHANNEL_24G_MAX];
+    /// List of 5GHz supported channels
+    struct mac_chan_def chan5G[MAC_DOMAINCHANNEL_5G_MAX];
+    /// Number of 2.4GHz channels in the list
+    u8_l chan2G4_cnt;
+    /// Number of 5GHz channels in the list
+    u8_l chan5G_cnt;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_CONTROL_PORT_REQ message
+struct me_set_control_port_req
+{
+    /// Index of the station for which the control port is opened
+    u8_l sta_idx;
+    /// Control port state
+    bool_l control_port_open;
+};
+
+/// Structure containing the parameters of the @ref ME_TKIP_MIC_FAILURE_IND message
+struct me_tkip_mic_failure_ind
+{
+    /// Address of the sending STA
+    struct mac_addr addr;
+    /// TSC value
+    u64_l tsc;
+    /// Boolean indicating if the packet was a group or unicast one (true if group)
+    bool_l ga;
+    /// Key Id
+    u8_l keyid;
+    /// VIF index
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_ADD_REQ message
+struct me_sta_add_req
+{
+    /// MAC address of the station to be added
+    struct mac_addr mac_addr;
+    /// Supported legacy rates
+    struct mac_rateset rate_set;
+    /// HT Capabilities
+    struct mac_htcapability ht_cap;
+    /// VHT Capabilities
+    struct mac_vhtcapability vht_cap;
+    /// HE capabilities
+    struct mac_hecapability he_cap;
+    /// Flags giving additional information about the station (@ref mac_sta_flags)
+    u32_l flags;
+    /// Association ID of the station
+    u16_l aid;
+    /// Bit field indicating which queues have U-APSD enabled
+    u8_l uapsd_queues;
+    /// Maximum size, in frames, of a APSD service period
+    u8_l max_sp_len;
+    /// Operation mode information (valid if bit @ref STA_OPMOD_NOTIF is
+    /// set in the flags)
+    u8_l opmode;
+    /// Index of the VIF the station is attached to
+    u8_l vif_idx;
+    /// Whether the the station is TDLS station
+    bool_l tdls_sta;
+    /// Indicate if the station is TDLS link initiator station
+    bool_l tdls_sta_initiator;
+    /// Indicate if the TDLS Channel Switch is allowed
+    bool_l tdls_chsw_allowed;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_ADD_CFM message
+struct me_sta_add_cfm
+{
+    /// Station index
+    u8_l sta_idx;
+    /// Status of the station addition
+    u8_l status;
+    /// PM state of the station
+    u8_l pm_state;
+};
+
+/// Structure containing the parameters of the @ref ME_STA_DEL_REQ message.
+struct me_sta_del_req
+{
+    /// Index of the station to be deleted
+    u8_l sta_idx;
+    /// Whether the the station is TDLS station
+    bool_l tdls_sta;
+};
+
+/// Structure containing the parameters of the @ref ME_TX_CREDITS_UPDATE_IND message.
+struct me_tx_credits_update_ind
+{
+    /// Index of the station for which the credits are updated
+    u8_l sta_idx;
+    /// TID for which the credits are updated
+    u8_l tid;
+    /// Offset to be applied on the credit count
+    s8_l credits;
+};
+
+/// Structure containing the parameters of the @ref ME_TRAFFIC_IND_REQ message.
+struct me_traffic_ind_req
+{
+    /// Index of the station for which UAPSD traffic is available on host
+    u8_l sta_idx;
+    /// Flag indicating the availability of UAPSD packets for the given STA
+    u8_l tx_avail;
+    /// Indicate if traffic is on uapsd-enabled queues
+    bool_l uapsd;
+};
+
+/// Structure containing the parameters of the @ref ME_RC_STATS_REQ message.
+struct me_rc_stats_req
+{
+    /// Index of the station for which the RC statistics are requested
+    u8_l sta_idx;
+};
+
+/// Structure containing the rate control statistics
+struct rc_rate_stats
+{
+    /// Number of attempts (per sampling interval)
+    u16_l attempts;
+    /// Number of success (per sampling interval)
+    u16_l success;
+    /// Estimated probability of success (EWMA)
+    u16_l probability;
+    /// Rate configuration of the sample
+    u16_l rate_config;
+    union
+    {
+        struct {
+            /// Number of times the sample has been skipped (per sampling interval)
+            u8_l  sample_skipped;
+            /// Whether the old probability is available
+            bool_l  old_prob_available;
+            /// Whether the rate can be used in the retry chain
+            bool_l rate_allowed;
+        };
+        struct {
+            /// RU size and UL length received in the latest HE trigger frame
+            u16_l ru_and_length;
+        };
+    };
+};
+
+/// Number of RC samples
+#define RC_MAX_N_SAMPLE 10
+/// Index of the HE statistics element in the table
+#define RC_HE_STATS_IDX RC_MAX_N_SAMPLE
+
+/// Structure containing the parameters of the @ref ME_RC_STATS_CFM message.
+struct me_rc_stats_cfm
+{
+    /// Index of the station for which the RC statistics are provided
+    u8_l sta_idx;
+    /// Number of samples used in the RC algorithm
+    u16_l no_samples;
+    /// Number of MPDUs transmitted (per sampling interval)
+    u16_l ampdu_len;
+    /// Number of AMPDUs transmitted (per sampling interval)
+    u16_l ampdu_packets;
+    /// Average number of MPDUs in each AMPDU frame (EWMA)
+    u32_l avg_ampdu_len;
+    // Current step 0 of the retry chain
+    u8_l sw_retry_step;
+    /// Trial transmission period
+    u8_l sample_wait;
+    /// Retry chain steps
+    u16_l retry_step_idx[4];
+    /// RC statistics - Max number of RC samples, plus one for the HE TB statistics
+    struct rc_rate_stats rate_stats[RC_MAX_N_SAMPLE + 1];
+    /// Throughput - Max number of RC samples, plus one for the HE TB statistics
+    u32_l tp[RC_MAX_N_SAMPLE + 1];
+};
+
+/// Structure containing the parameters of the @ref ME_RC_SET_RATE_REQ message.
+struct me_rc_set_rate_req
+{
+    /// Index of the station for which the fixed rate is set
+    u8_l sta_idx;
+    /// Rate configuration to be set
+    u16_l fixed_rate_cfg;
+};
+
+/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_REQ message.
+struct me_config_monitor_req
+{
+    /// Channel to configure
+    struct mac_chan_op chan;
+    /// Is channel data valid
+    bool_l chan_set;
+    /// Enable report of unsupported HT frames
+    bool_l uf;
+};
+
+/// Structure containing the parameters of the @ref ME_CONFIG_MONITOR_CFM message.
+struct me_config_monitor_cfm
+{
+    /// Channel context index
+    u8_l chan_index;
+    /// Channel parameters
+    struct mac_chan_op chan;
+};
+
+/// Structure containing the parameters of the @ref ME_SET_PS_MODE_REQ message.
+struct me_set_ps_mode_req
+{
+    /// Power Save is activated or deactivated
+    u8_l  ps_state;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For TWT messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the TWT task
+enum
+{
+    /// Request Individual TWT Establishment
+    TWT_SETUP_REQ = LMAC_FIRST_MSG(TASK_TWT),
+    /// Confirm Individual TWT Establishment
+    TWT_SETUP_CFM,
+    /// Indicate TWT Setup response from peer
+    TWT_SETUP_IND,
+    /// Request to destroy a TWT Establishment or all of them
+    TWT_TEARDOWN_REQ,
+    /// Confirm to destroy a TWT Establishment or all of them
+    TWT_TEARDOWN_CFM,
+
+    /// MAX number of messages
+    TWT_MAX,
+};
+
+///TWT Group assignment
+struct twt_grp_assignment_tag
+{
+    /// TWT Group assignment byte array
+    u8_l grp_assignment[9];
+};
+
+///TWT Flow configuration
+struct twt_conf_tag
+{
+    /// Flow Type (0: Announced, 1: Unannounced)
+    u8_l flow_type;
+    /// Wake interval Exponent
+    u8_l wake_int_exp;
+    /// Unit of measurement of TWT Minimum Wake Duration (0:256us, 1:tu)
+    bool_l wake_dur_unit;
+    /// Nominal Minimum TWT Wake Duration
+    u8_l min_twt_wake_dur;
+    /// TWT Wake Interval Mantissa
+    u16_l wake_int_mantissa;
+};
+
+///TWT Setup request message structure
+struct twt_setup_req
+{
+    /// VIF Index
+    u8_l vif_idx;
+    /// Setup request type
+    u8_l setup_type;
+    /// TWT Setup configuration
+    struct twt_conf_tag conf;
+};
+
+///TWT Setup confirmation message structure
+struct twt_setup_cfm
+{
+    /// Status (0 = TWT Setup Request has been transmitted to peer)
+    u8_l status;
+};
+
+/// TWT Setup command
+enum twt_setup_types
+{
+    MAC_TWT_SETUP_REQ = 0,
+    MAC_TWT_SETUP_SUGGEST,
+    MAC_TWT_SETUP_DEMAND,
+    MAC_TWT_SETUP_GROUPING,
+    MAC_TWT_SETUP_ACCEPT,
+    MAC_TWT_SETUP_ALTERNATE,
+    MAC_TWT_SETUP_DICTATE,
+    MAC_TWT_SETUP_REJECT,
+};
+
+///TWT Setup indication message structure
+struct twt_setup_ind
+{
+    /// Response type
+    u8_l resp_type;
+    /// STA Index
+    u8_l sta_idx;
+    /// TWT Setup configuration
+    struct twt_conf_tag conf;
+};
+
+/// TWT Teardown request message structure
+struct twt_teardown_req
+{
+    /// TWT Negotiation type
+    u8_l neg_type;
+    /// All TWT
+    u8_l all_twt;
+    /// TWT flow ID
+    u8_l id;
+    /// VIF Index
+    u8_l vif_idx;
+};
+
+///TWT Teardown confirmation message structure
+struct twt_teardown_cfm
+{
+    /// Status (0 = TWT Teardown Request has been transmitted to peer)
+    u8_l status;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For SM messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the SM task
+enum sm_msg_tag
+{
+    /// Request to connect to an AP
+    SM_CONNECT_REQ = LMAC_FIRST_MSG(TASK_SM),
+    /// Confirmation of connection
+    SM_CONNECT_CFM,
+    /// Indicates that the SM associated to the AP
+    SM_CONNECT_IND,
+    /// Request to disconnect
+    SM_DISCONNECT_REQ,
+    /// Confirmation of disconnection
+    SM_DISCONNECT_CFM,
+    /// Indicates that the SM disassociated the AP
+    SM_DISCONNECT_IND,
+    /// Request to start external authentication
+    SM_EXTERNAL_AUTH_REQUIRED_IND,
+    /// Response to external authentication request
+    SM_EXTERNAL_AUTH_REQUIRED_RSP,
+    SM_FT_AUTH_IND,
+    SM_FT_AUTH_RSP,
+
+    /// MAX number of messages
+    SM_MAX,
+};
+
+/// Structure containing the parameters of @ref SM_CONNECT_REQ message.
+struct sm_connect_req
+{
+    /// SSID to connect to
+    struct mac_ssid ssid;
+    /// BSSID to connect to (if not specified, set this field to WILDCARD BSSID)
+    struct mac_addr bssid;
+    /// Channel on which we have to connect (if not specified, set -1 in the chan.freq field)
+    struct mac_chan_def chan;
+    /// Connection flags (see @ref mac_connection_flags)
+    u32_l flags;
+    /// Control port Ethertype (in network endianness)
+    u16_l ctrl_port_ethertype;
+    /// Length of the association request IEs
+    /// Listen interval to be used for this connection
+    u16_l listen_interval;
+    /// Flag indicating if the we have to wait for the BC/MC traffic after beacon or not
+    bool_l dont_wait_bcmc;
+    /// Authentication type
+    u8_l auth_type;
+    /// UAPSD queues (bit0: VO, bit1: VI, bit2: BE, bit3: BK)
+    u8_l uapsd_queues;
+    /// VIF index
+    u8_l vif_idx;
+    u16_l ie_len;
+    /// Buffer containing the additional information elements to be put in the
+    /// association request
+    u32_l ie_buf[0];
+};
+
+/// Structure containing the parameters of the @ref SM_CONNECT_CFM message.
+struct sm_connect_cfm
+{
+    /// Status. If 0, it means that the connection procedure will be performed and that
+    /// a subsequent @ref SM_CONNECT_IND message will be forwarded once the procedure is
+    /// completed
+    u8_l status;
+};
+
+#define SM_ASSOC_IE_LEN   800
+/// Structure containing the parameters of the @ref SM_CONNECT_IND message.
+struct sm_connect_ind
+{
+    /// Status code of the connection procedure
+    u16_l status_code;
+    /// BSSID
+    struct mac_addr bssid;
+    /// Flag indicating if the indication refers to an internal roaming or from a host request
+    bool_l roamed;
+    /// Index of the VIF for which the association process is complete
+    u8_l vif_idx;
+    /// Index of the STA entry allocated for the AP
+    u8_l ap_idx;
+    /// Index of the LMAC channel context the connection is attached to
+    u8_l ch_idx;
+    /// Flag indicating if the AP is supporting QoS
+    bool_l qos;
+    /// ACM bits set in the AP WMM parameter element
+    u8_l acm;
+    /// Length of the AssocReq IEs
+    u16_l assoc_req_ie_len;
+    /// Length of the AssocRsp IEs
+    u16_l assoc_rsp_ie_len;
+    /// IE buffer
+    /// Association Id allocated by the AP for this connection
+    u16_l aid;
+    /// AP operating channel
+    struct mac_chan_op chan;
+    /// EDCA parameters
+    u32_l ac_param[AC_MAX];
+    u32_l assoc_ie_buf[0];
+};
+
+/// Structure containing the parameters of the @ref SM_DISCONNECT_REQ message.
+struct sm_disconnect_req
+{
+    /// Reason of the deauthentication.
+    u16_l reason_code;
+    /// Index of the VIF.
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of SM_ASSOCIATION_IND the message
+struct sm_association_ind
+{
+    // MAC ADDR of the STA
+    struct mac_addr     me_mac_addr;
+};
+
+
+/// Structure containing the parameters of the @ref SM_DISCONNECT_IND message.
+struct sm_disconnect_ind
+{
+    /// Reason of the disconnection.
+    u16_l reason_code;
+    /// Index of the VIF.
+    u8_l vif_idx;
+    /// FT over DS is ongoing
+    bool_l reassoc;
+};
+
+/// Structure containing the parameters of the @ref SM_EXTERNAL_AUTH_REQUIRED_IND
+struct sm_external_auth_required_ind
+{
+    /// Index of the VIF.
+    u8_l vif_idx;
+    /// SSID to authenticate to
+    struct mac_ssid ssid;
+    /// BSSID to authenticate to
+    struct mac_addr bssid;
+    /// AKM suite of the respective authentication
+    u32_l akm;
+};
+
+/// Structure containing the parameters of the @ref SM_EXTERNAL_AUTH_REQUIRED_RSP
+struct sm_external_auth_required_rsp
+{
+    /// Index of the VIF.
+    u8_l vif_idx;
+    /// Authentication status
+    u16_l status;
+};
+
+/// Structure containing the parameters of the @ref SM_FT_AUTH_IND
+struct sm_ft_auth_ind
+{
+    /// Index of the VIF.
+    u8_l vif_idx;
+    /// Size of the FT elements
+    u16_l ft_ie_len;
+    /// Fast Transition elements in the authentication
+    u32_l ft_ie_buf[0];
+};
+///////////////////////////////////////////////////////////////////////////////
+/////////// For APM messages
+///////////////////////////////////////////////////////////////////////////////
+/// Message API of the APM task
+enum apm_msg_tag
+{
+    /// Request to start the AP.
+    APM_START_REQ = LMAC_FIRST_MSG(TASK_APM),
+    /// Confirmation of the AP start.
+    APM_START_CFM,
+    /// Request to stop the AP.
+    APM_STOP_REQ,
+    /// Confirmation of the AP stop.
+    APM_STOP_CFM,
+    /// Request to start CAC
+    APM_START_CAC_REQ,
+    /// Confirmation of the CAC start
+    APM_START_CAC_CFM,
+    /// Request to stop CAC
+    APM_STOP_CAC_REQ,
+    /// Confirmation of the CAC stop
+    APM_STOP_CAC_CFM,
+    APM_PROBE_CLIENT_REQ,
+    APM_PROBE_CLIENT_CFM,
+    APM_PROBE_CLIENT_IND,
+
+    /// MAX number of messages
+    APM_MAX,
+};
+
+/// Structure containing the parameters of the @ref APM_START_REQ message.
+struct apm_start_req
+{
+    /// Basic rate set
+    struct mac_rateset basic_rates;
+    /// Operating channel on which we have to enable the AP
+    struct mac_chan_op chan;
+    /// Offset of the TIM IE in the beacon
+    u16_l tim_oft;
+    /// Beacon interval
+    u16_l bcn_int;
+    /// Flags (@ref mac_connection_flags)
+    u32_l flags;
+    /// Control port Ethertype
+    u16_l ctrl_port_ethertype;
+    /// Length of the TIM IE
+    u8_l tim_len;
+    /// Index of the VIF for which the AP is started
+    u8_l vif_idx;
+    /// Length of the beacon template
+    u16_l bcn_len;
+    /// Address, in host memory, to the beacon template
+    ptr_addr bcn_addr;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CFM message.
+struct apm_start_cfm
+{
+    /// Status of the AP starting procedure
+    u8_l status;
+    /// Index of the VIF for which the AP is started
+    u8_l vif_idx;
+    /// Index of the channel context attached to the VIF
+    u8_l ch_idx;
+    /// Index of the STA used for BC/MC traffic
+    u8_l bcmc_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_STOP_REQ message.
+struct apm_stop_req
+{
+    /// Index of the VIF for which the AP has to be stopped
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CAC_REQ message.
+struct apm_start_cac_req
+{
+    /// Channel configuration
+    struct mac_chan_op chan;
+    /// Index of the VIF for which the CAC is started
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_START_CAC_CFM message.
+struct apm_start_cac_cfm
+{
+    /// Status of the CAC starting procedure
+    u8_l status;
+    /// Index of the channel context attached to the VIF for CAC
+    u8_l ch_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_STOP_CAC_REQ message.
+struct apm_stop_cac_req
+{
+    /// Index of the VIF for which the CAC has to be stopped
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_PROBE_CLIENT_REQ message.
+struct apm_probe_client_req
+{
+    /// Index of the VIF
+    u8_l vif_idx;
+    /// Index of the Station to probe
+    u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the @ref APM_PROBE_CLIENT_CFM message.
+struct apm_probe_client_cfm
+{
+    /// Status of the probe request
+    u8_l status;
+    /// Unique ID to distinguish @ref APM_PROBE_CLIENT_IND message
+    u32_l probe_id;
+};
+
+/// Structure containing the parameters of the @ref APM_PROBE_CLIENT_CFM message.
+struct apm_probe_client_ind
+{
+    /// Index of the VIF
+    u8_l vif_idx;
+    /// Index of the Station to probe
+    u8_l sta_idx;
+    /// Whether client is still present or not
+    bool_l client_present;
+    /// Unique ID as returned in @ref APM_PROBE_CLIENT_CFM
+    u32_l probe_id;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For MESH messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// Maximum length of the Mesh ID
+#define MESH_MESHID_MAX_LEN     (32)
+
+/// Message API of the MESH task
+enum mesh_msg_tag
+{
+    /// Request to start the MP
+    MESH_START_REQ = LMAC_FIRST_MSG(TASK_MESH),
+    /// Confirmation of the MP start.
+    MESH_START_CFM,
+
+    /// Request to stop the MP.
+    MESH_STOP_REQ,
+    /// Confirmation of the MP stop.
+    MESH_STOP_CFM,
+
+    // Request to update the MP
+    MESH_UPDATE_REQ,
+    /// Confirmation of the MP update
+    MESH_UPDATE_CFM,
+
+    /// Request information about a given link
+    MESH_PEER_INFO_REQ,
+    /// Response to the MESH_PEER_INFO_REQ message
+    MESH_PEER_INFO_CFM,
+
+    /// Request automatic establishment of a path with a given mesh STA
+    MESH_PATH_CREATE_REQ,
+    /// Confirmation to the MESH_PATH_CREATE_REQ message
+    MESH_PATH_CREATE_CFM,
+
+    /// Request a path update (delete path, modify next hop mesh STA)
+    MESH_PATH_UPDATE_REQ,
+    /// Confirmation to the MESH_PATH_UPDATE_REQ message
+    MESH_PATH_UPDATE_CFM,
+
+    /// Indication from Host that the indicated Mesh Interface is a proxy for an external STA
+    MESH_PROXY_ADD_REQ,
+
+    /// Indicate that a connection has been established or lost
+    MESH_PEER_UPDATE_IND,
+    /// Notification that a connection has been established or lost (when MPM handled by userspace)
+    MESH_PEER_UPDATE_NTF = MESH_PEER_UPDATE_IND,
+
+    /// Indicate that a path is now active or inactive
+    MESH_PATH_UPDATE_IND,
+    /// Indicate that proxy information have been updated
+    MESH_PROXY_UPDATE_IND,
+
+    /// MAX number of messages
+    MESH_MAX,
+};
+
+/// Structure containing the parameters of the @ref MESH_START_REQ message.
+struct mesh_start_req
+{
+    /// Basic rate set
+    struct mac_rateset basic_rates;
+    /// Operating channel on which we have to enable the AP
+    struct mac_chan_op chan;
+    /// DTIM Period
+    u8_l dtim_period;
+    /// Beacon Interval
+    u16_l bcn_int;
+    /// Index of the VIF for which the MP is started
+    u8_l vif_index;
+    /// Length of the Mesh ID
+    u8_l mesh_id_len;
+    /// Mesh ID
+    u8_l mesh_id[MESH_MESHID_MAX_LEN];
+    /// Address of the IEs to download
+    u32_l ie_addr;
+    /// Length of the provided IEs
+    u8_l ie_len;
+    /// Indicate if Mesh Peering Management (MPM) protocol is handled in userspace
+    bool_l user_mpm;
+    /// Indicate if Mesh Point is using authentication
+    bool_l is_auth;
+    /// Indicate which authentication method is used
+    u8_l auth_id;
+};
+
+/// Structure containing the parameters of the @ref MESH_START_CFM message.
+struct mesh_start_cfm
+{
+    /// Status of the MP starting procedure
+    u8_l status;
+    /// Index of the VIF for which the MP is started
+    u8_l vif_idx;
+    /// Index of the channel context attached to the VIF
+    u8_l ch_idx;
+    /// Index of the STA used for BC/MC traffic
+    u8_l bcmc_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_STOP_REQ message.
+struct mesh_stop_req
+{
+    /// Index of the VIF for which the MP has to be stopped
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_STOP_CFM message.
+struct mesh_stop_cfm
+{
+    /// Index of the VIF for which the MP has to be stopped
+    u8_l vif_idx;
+   /// Status
+    u8_l status;
+};
+
+/// Bit fields for mesh_update_req message's flags value
+enum mesh_update_flags_bit
+{
+    /// Root Mode
+    MESH_UPDATE_FLAGS_ROOT_MODE_BIT = 0,
+    /// Gate Mode
+    MESH_UPDATE_FLAGS_GATE_MODE_BIT,
+    /// Mesh Forwarding
+    MESH_UPDATE_FLAGS_MESH_FWD_BIT,
+    /// Local Power Save Mode
+    MESH_UPDATE_FLAGS_LOCAL_PSM_BIT,
+};
+
+/// Structure containing the parameters of the @ref MESH_UPDATE_REQ message.
+struct mesh_update_req
+{
+    /// Flags, indicate fields which have been updated
+    u8_l flags;
+    /// VIF Index
+    u8_l vif_idx;
+    /// Root Mode
+    u8_l root_mode;
+    /// Gate Announcement
+    bool_l gate_announ;
+    /// Mesh Forwarding
+    bool_l mesh_forward;
+    /// Local PS Mode
+    u8_l local_ps_mode;
+};
+
+/// Structure containing the parameters of the @ref MESH_UPDATE_CFM message.
+struct mesh_update_cfm
+{
+    /// Status
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_INFO_REQ message.
+struct mesh_peer_info_req
+{
+    ///Index of the station allocated for the peer
+    u8_l sta_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_INFO_CFM message.
+struct mesh_peer_info_cfm
+{
+    /// Response status
+    u8_l status;
+    /// Index of the station allocated for the peer
+    u8_l sta_idx;
+    /// Local Link ID
+    u16_l local_link_id;
+    /// Peer Link ID
+    u16_l peer_link_id;
+    /// Local PS Mode
+    u8_l local_ps_mode;
+    /// Peer PS Mode
+    u8_l peer_ps_mode;
+    /// Non-peer PS Mode
+    u8_l non_peer_ps_mode;
+    /// Link State
+    u8_l link_state;
+    u32_l last_bcn_age;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_CREATE_REQ message.
+struct mesh_path_create_req
+{
+    /// Index of the interface on which path has to be created
+    u8_l vif_idx;
+    /// Indicate if originator MAC Address is provided
+    bool_l has_orig_addr;
+    /// Path Target MAC Address
+    struct mac_addr tgt_mac_addr;
+    /// Originator MAC Address
+    struct mac_addr orig_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_CREATE_CFM message.
+struct mesh_path_create_cfm
+{
+    /// Confirmation status
+    u8_l status;
+    /// VIF Index
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_REQ message.
+struct mesh_path_update_req
+{
+    /// Indicate if path must be deleted
+    bool_l delete;
+    /// Index of the interface on which path has to be created
+    u8_l vif_idx;
+    /// Path Target MAC Address
+    struct mac_addr tgt_mac_addr;
+    /// Next Hop MAC Address
+    struct mac_addr nhop_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_CFM message.
+struct mesh_path_update_cfm
+{
+    /// Confirmation status
+    u8_l status;
+    /// VIF Index
+    u8_l vif_idx;
+};
+
+/// Structure containing the parameters of the @ref MESH_PROXY_ADD_REQ message.
+struct mesh_proxy_add_req
+{
+    /// VIF Index
+    u8_l vif_idx;
+    /// MAC Address of the External STA
+    struct mac_addr ext_sta_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PROXY_UPDATE_IND
+struct mesh_proxy_update_ind
+{
+    /// Indicate if proxy information has been added or deleted
+    bool_l delete;
+    /// Indicate if we are a proxy for the external STA
+    bool_l local;
+    /// VIF Index
+    u8_l vif_idx;
+    /// MAC Address of the External STA
+    struct mac_addr ext_sta_addr;
+    /// MAC Address of the proxy (only valid if local is false)
+    struct mac_addr proxy_mac_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_IND message.
+struct mesh_peer_update_ind
+{
+    /// Indicate if connection has been established or lost
+    bool_l estab;
+    /// VIF Index
+    u8_l vif_idx;
+    /// STA Index
+    u8_l sta_idx;
+    /// Peer MAC Address
+    struct mac_addr peer_addr;
+};
+
+/// Structure containing the parameters of the @ref MESH_PEER_UPDATE_NTF message.
+struct mesh_peer_update_ntf
+{
+    /// VIF Index
+    u8_l vif_idx;
+    /// STA Index
+    u8_l sta_idx;
+    /// Mesh Link State
+    u8_l state;
+};
+
+/// Structure containing the parameters of the @ref MESH_PATH_UPDATE_IND message.
+struct mesh_path_update_ind
+{
+    /// Indicate if path is deleted or not
+    bool_l delete;
+    /// Indicate if path is towards an external STA (not part of MBSS)
+    bool_l ext_sta;
+    /// VIF Index
+    u8_l vif_idx;
+    /// Path Index
+    u8_l path_idx;
+    /// Target MAC Address
+    struct mac_addr tgt_mac_addr;
+    /// External STA MAC Address (only if ext_sta is true)
+    struct mac_addr ext_sta_mac_addr;
+    /// Next Hop STA Index
+    u8_l nhop_sta_idx;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For Debug messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// Messages related to Debug Task
+enum dbg_msg_tag
+{
+    /// Memory read request
+    DBG_MEM_READ_REQ = LMAC_FIRST_MSG(TASK_DBG),
+    /// Memory read confirm
+    DBG_MEM_READ_CFM,
+    /// Memory write request
+    DBG_MEM_WRITE_REQ,
+    /// Memory write confirm
+    DBG_MEM_WRITE_CFM,
+    /// Module filter request
+    DBG_SET_MOD_FILTER_REQ,
+    /// Module filter confirm
+    DBG_SET_MOD_FILTER_CFM,
+    /// Severity filter request
+    DBG_SET_SEV_FILTER_REQ,
+    /// Severity filter confirm
+    DBG_SET_SEV_FILTER_CFM,
+    /// LMAC/MAC HW fatal error indication
+    DBG_ERROR_IND,
+    /// Request to get system statistics
+    DBG_GET_SYS_STAT_REQ,
+    /// COnfirmation of system statistics
+    DBG_GET_SYS_STAT_CFM,
+    /// Max number of Debug messages
+    DBG_MAX,
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_REQ message.
+struct dbg_mem_read_req
+{
+    u32_l memaddr;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_READ_CFM message.
+struct dbg_mem_read_cfm
+{
+    u32_l memaddr;
+    u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_REQ message.
+struct dbg_mem_write_req
+{
+    u32_l memaddr;
+    u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_MEM_WRITE_CFM message.
+struct dbg_mem_write_cfm
+{
+    u32_l memaddr;
+    u32_l memdata;
+};
+
+/// Structure containing the parameters of the @ref DBG_SET_MOD_FILTER_REQ message.
+struct dbg_set_mod_filter_req
+{
+    /// Bit field indicating for each module if the traces are enabled or not
+    u32_l mod_filter;
+};
+
+/// Structure containing the parameters of the @ref DBG_SEV_MOD_FILTER_REQ message.
+struct dbg_set_sev_filter_req
+{
+    /// Bit field indicating the severity threshold for the traces
+    u32_l sev_filter;
+};
+
+/// Structure containing the parameters of the @ref DBG_GET_SYS_STAT_CFM message.
+struct dbg_get_sys_stat_cfm
+{
+    /// Time spent in CPU sleep since last reset of the system statistics
+    u32_l cpu_sleep_time;
+    /// Time spent in DOZE since last reset of the system statistics
+    u32_l doze_time;
+    /// Total time spent since last reset of the system statistics
+    u32_l stats_time;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+/////////// For TDLS messages
+///////////////////////////////////////////////////////////////////////////////
+
+/// List of messages related to the task.
+enum tdls_msg_tag
+{
+    /// TDLS channel Switch Request.
+    TDLS_CHAN_SWITCH_REQ = LMAC_FIRST_MSG(TASK_TDLS),
+    /// TDLS channel switch confirmation.
+    TDLS_CHAN_SWITCH_CFM,
+    /// TDLS channel switch indication.
+    TDLS_CHAN_SWITCH_IND,
+    /// TDLS channel switch to base channel indication.
+    TDLS_CHAN_SWITCH_BASE_IND,
+    /// TDLS cancel channel switch request.
+    TDLS_CANCEL_CHAN_SWITCH_REQ,
+    /// TDLS cancel channel switch confirmation.
+    TDLS_CANCEL_CHAN_SWITCH_CFM,
+    /// TDLS peer power save indication.
+    TDLS_PEER_PS_IND,
+    /// TDLS peer traffic indication request.
+    TDLS_PEER_TRAFFIC_IND_REQ,
+    /// TDLS peer traffic indication confirmation.
+    TDLS_PEER_TRAFFIC_IND_CFM,
+    /// MAX number of messages
+    TDLS_MAX
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_REQ message
+struct tdls_chan_switch_req
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// STA Index
+    u8_l sta_idx;
+    /// MAC address of the TDLS station
+    struct mac_addr peer_mac_addr;
+    /// Flag to indicate if the TDLS peer is the TDLS link initiator
+    bool_l initiator;
+    /// Channel configuration
+    struct mac_chan_op chan;
+    /// Operating class
+    u8_l op_class;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_REQ message
+struct tdls_cancel_chan_switch_req
+{
+    /// Index of the VIF
+    u8_l vif_index;
+    /// STA Index
+    u8_l sta_idx;
+    /// MAC address of the TDLS station
+    struct mac_addr peer_mac_addr;
+};
+
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_CFM message
+struct tdls_chan_switch_cfm
+{
+    /// Status of the operation
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CANCEL_CHAN_SWITCH_CFM message
+struct tdls_cancel_chan_switch_cfm
+{
+    /// Status of the operation
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_IND message
+struct tdls_chan_switch_ind
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// Channel Context Index
+    u8_l chan_ctxt_index;
+    /// Status of the operation
+    u8_l status;
+};
+
+/// Structure containing the parameters of the @ref TDLS_CHAN_SWITCH_BASE_IND message
+struct tdls_chan_switch_base_ind
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// Channel Context index
+    u8_l chan_ctxt_index;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_PS_IND message
+struct tdls_peer_ps_ind
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// STA Index
+    u8_l sta_idx;
+    /// MAC ADDR of the TDLS STA
+    struct mac_addr peer_mac_addr;
+    /// Flag to indicate if the TDLS peer is going to sleep
+    bool ps_on;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_REQ message
+struct tdls_peer_traffic_ind_req
+{
+    /// VIF Index
+    u8_l vif_index;
+    /// STA Index
+    u8_l sta_idx;
+    // MAC ADDR of the TDLS STA
+    struct mac_addr peer_mac_addr;
+    /// Dialog token
+    u8_l dialog_token;
+    /// TID of the latest MPDU transmitted over the TDLS direct link to the TDLS STA
+    u8_l last_tid;
+    /// Sequence number of the latest MPDU transmitted over the TDLS direct link
+    /// to the TDLS STA
+    u16_l last_sn;
+};
+
+/// Structure containing the parameters of the @ref TDLS_PEER_TRAFFIC_IND_CFM message
+struct tdls_peer_traffic_ind_cfm
+{
+    /// Status of the operation
+    u8_l status;
+};
+
+#if defined(CONFIG_ECRNX_P2P)
+enum p2p_listen_msg_tag
+{
+    /// Scan request from host.
+    P2P_LISTEN_START_REQ = LMAC_FIRST_MSG(TASK_P2P_LISTEN),
+    P2P_LISTEN_START_CFM,
+    P2P_CANCEL_LISTEN_REQ,
+    P2P_CANCEL_LISTEN_CFM,
+
+       P2P_LISTEN_MAX,
+};
+
+struct p2p_listen_start_req
+{
+       /// Index of the interface on which path has to be created
+    u8_l vif_idx;
+};
+
+struct p2p_listen_start_cfm
+{
+    /// p2p listen task status
+    bool_l status;
+    /// VIF Index
+    u8_l vif_idx;
+};
+
+
+struct p2p_cancel_listen_req
+{
+       /// Index of the interface on which path has to be created
+    u8_l vif_idx;
+};
+
+struct p2p_cancel_listen_cfm
+{
+    /// p2p listen task status
+    bool_l status;
+    /// VIF Index
+    u8_l vif_idx;
+};
+#endif
+
+#endif // LMAC_MSG_H_
diff --git a/drivers/net/wireless/eswin/lmac_types.h b/drivers/net/wireless/eswin/lmac_types.h
new file mode 100644 (file)
index 0000000..b8dcd2b
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ ****************************************************************************************
+ *
+ * @file co_types.h
+ *
+ * @brief This file replaces the need to include stdint or stdbool typical headers,
+ *        which may not be available in all toolchains, and adds new types
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ * $Rev: $
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _LMAC_INT_H_
+#define _LMAC_INT_H_
+
+
+/**
+ ****************************************************************************************
+ * @addtogroup CO_INT
+ * @ingroup COMMON
+ * @brief Common integer standard types (removes use of stdint)
+ *
+ * @{
+ ****************************************************************************************
+ */
+
+
+/*
+ * DEFINES
+ ****************************************************************************************
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
+#include <linux/bits.h>
+#else
+#include <linux/bitops.h>
+#endif
+
+#ifdef CONFIG_ECRNX_TL4
+typedef uint16_t u8_l;
+typedef int16_t s8_l;
+typedef uint16_t bool_l;
+#else
+typedef uint8_t u8_l;
+typedef int8_t s8_l;
+typedef bool bool_l;
+#endif
+typedef uint16_t u16_l;
+typedef int16_t s16_l;
+typedef uint32_t u32_l;
+typedef int32_t s32_l;
+typedef uint64_t u64_l;
+
+#define ALIGNED(n)  __attribute__((aligned (n)))
+
+#if (__SIZEOF_POINTER__ == 8)
+#define  ptr_addr    u64_l
+#else
+#define  ptr_addr    u32_l
+#endif
+
+
+/// @} CO_INT
+#endif // _LMAC_INT_H_
diff --git a/drivers/net/wireless/eswin/reg_access.h b/drivers/net/wireless/eswin/reg_access.h
new file mode 100644 (file)
index 0000000..16a02eb
--- /dev/null
@@ -0,0 +1,156 @@
+/**
+ ******************************************************************************
+ *
+ * @file reg_access.h
+ *
+ * @brief Definitions and macros for MAC HW and platform register accesses
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef REG_ACCESS_H_
+#define REG_ACCESS_H_
+
+/*****************************************************************************
+ * Addresses within ECRNX_ADDR_CPU
+ *****************************************************************************/
+#define RAM_LMAC_FW_ADDR               0x00000000
+
+/*****************************************************************************
+ * Addresses within ECRNX_ADDR_SYSTEM
+ *****************************************************************************/
+/* Shard RAM */
+#define SHARED_RAM_START_ADDR          0x00000000
+
+/* IPC registers */
+#define IPC_REG_BASE_ADDR              0x00800000
+
+/* System Controller Registers */
+#define SYSCTRL_SIGNATURE_ADDR         0x00900000
+// old diag register name
+#define SYSCTRL_DIAG_CONF_ADDR         0x00900068
+#define SYSCTRL_PHYDIAG_CONF_ADDR      0x00900074
+#define SYSCTRL_RIUDIAG_CONF_ADDR      0x00900078
+// new diag register name
+#define SYSCTRL_DIAG_CONF0             0x00900064
+#define SYSCTRL_DIAG_CONF1             0x00900068
+#define SYSCTRL_DIAG_CONF2             0x00900074
+#define SYSCTRL_DIAG_CONF3             0x00900078
+#define SYSCTRL_MISC_CNTL_ADDR         0x009000E0
+#define   BOOTROM_ENABLE               BIT(4)
+#define   FPGA_B_RESET                 BIT(1)
+#define   SOFT_RESET                   BIT(0)
+
+/* MAC platform */
+#define NXMAC_VERSION_1_ADDR           0x00B00004
+#define   NXMAC_MU_MIMO_TX_BIT         BIT(19)
+#define   NXMAC_BFMER_BIT              BIT(18)
+#define   NXMAC_BFMEE_BIT              BIT(17)
+#define   NXMAC_MAC_80211MH_FORMAT_BIT BIT(16)
+#define   NXMAC_COEX_BIT               BIT(14)
+#define   NXMAC_WAPI_BIT               BIT(13)
+#define   NXMAC_TPC_BIT                BIT(12)
+#define   NXMAC_VHT_BIT                BIT(11)
+#define   NXMAC_HT_BIT                 BIT(10)
+#define   NXMAC_GCMP_BIT               BIT(9)
+#define   NXMAC_RCE_BIT                BIT(8)
+#define   NXMAC_CCMP_BIT               BIT(7)
+#define   NXMAC_TKIP_BIT               BIT(6)
+#define   NXMAC_WEP_BIT                BIT(5)
+#define   NXMAC_SECURITY_BIT           BIT(4)
+#define   NXMAC_SME_BIT                BIT(3)
+#define   NXMAC_HCCA_BIT               BIT(2)
+#define   NXMAC_EDCA_BIT               BIT(1)
+#define   NXMAC_QOS_BIT                BIT(0)
+
+#define NXMAC_RX_CNTRL_ADDR                     0x00B00060
+#define   NXMAC_EN_DUPLICATE_DETECTION_BIT      BIT(31)
+#define   NXMAC_ACCEPT_UNKNOWN_BIT              BIT(30)
+#define   NXMAC_ACCEPT_OTHER_DATA_FRAMES_BIT    BIT(29)
+#define   NXMAC_ACCEPT_QO_S_NULL_BIT            BIT(28)
+#define   NXMAC_ACCEPT_QCFWO_DATA_BIT           BIT(27)
+#define   NXMAC_ACCEPT_Q_DATA_BIT               BIT(26)
+#define   NXMAC_ACCEPT_CFWO_DATA_BIT            BIT(25)
+#define   NXMAC_ACCEPT_DATA_BIT                 BIT(24)
+#define   NXMAC_ACCEPT_OTHER_CNTRL_FRAMES_BIT   BIT(23)
+#define   NXMAC_ACCEPT_CF_END_BIT               BIT(22)
+#define   NXMAC_ACCEPT_ACK_BIT                  BIT(21)
+#define   NXMAC_ACCEPT_CTS_BIT                  BIT(20)
+#define   NXMAC_ACCEPT_RTS_BIT                  BIT(19)
+#define   NXMAC_ACCEPT_PS_POLL_BIT              BIT(18)
+#define   NXMAC_ACCEPT_BA_BIT                   BIT(17)
+#define   NXMAC_ACCEPT_BAR_BIT                  BIT(16)
+#define   NXMAC_ACCEPT_OTHER_MGMT_FRAMES_BIT    BIT(15)
+#define   NXMAC_ACCEPT_BFMEE_FRAMES_BIT         BIT(14)
+#define   NXMAC_ACCEPT_ALL_BEACON_BIT           BIT(13)
+#define   NXMAC_ACCEPT_NOT_EXPECTED_BA_BIT      BIT(12)
+#define   NXMAC_ACCEPT_DECRYPT_ERROR_FRAMES_BIT BIT(11)
+#define   NXMAC_ACCEPT_BEACON_BIT               BIT(10)
+#define   NXMAC_ACCEPT_PROBE_RESP_BIT           BIT(9)
+#define   NXMAC_ACCEPT_PROBE_REQ_BIT            BIT(8)
+#define   NXMAC_ACCEPT_MY_UNICAST_BIT           BIT(7)
+#define   NXMAC_ACCEPT_UNICAST_BIT              BIT(6)
+#define   NXMAC_ACCEPT_ERROR_FRAMES_BIT         BIT(5)
+#define   NXMAC_ACCEPT_OTHER_BSSID_BIT          BIT(4)
+#define   NXMAC_ACCEPT_BROADCAST_BIT            BIT(3)
+#define   NXMAC_ACCEPT_MULTICAST_BIT            BIT(2)
+#define   NXMAC_DONT_DECRYPT_BIT                BIT(1)
+#define   NXMAC_EXC_UNENCRYPTED_BIT             BIT(0)
+
+#define NXMAC_DEBUG_PORT_SEL_ADDR      0x00B00510
+#define NXMAC_SW_SET_PROFILING_ADDR    0x00B08564
+#define NXMAC_SW_CLEAR_PROFILING_ADDR  0x00B08568
+
+/* Modem Status */
+#define MDM_HDMCONFIG_ADDR             0x00C00000
+#define MDM_HDMVERSION_ADDR            0x00C0003C
+
+/* Clock gating configuration */
+#define MDM_MEMCLKCTRL0_ADDR           0x00C00848
+#define MDM_CLKGATEFCTRL0_ADDR         0x00C00874
+#define CRM_CLKGATEFCTRL0_ADDR         0x00940010
+
+/* AGC (trident) */
+#define AGC_ECRNXAGCCNTL_ADDR           0x00C02060
+
+/* LDPC RAM*/
+#define PHY_LDPC_RAM_ADDR              0x00C09000
+
+/* FCU (elma )*/
+#define FCU_ECRNXFCAGCCNTL_ADDR         0x00C09034
+
+/* AGC RAM */
+#define PHY_AGC_UCODE_ADDR             0x00C0A000
+
+/* RIU */
+#define RIU_ECRNXVERSION_ADDR           0x00C0B000
+#define RIU_ECRNXDYNAMICCONFIG_ADDR     0x00C0B008
+#define RIU_AGCMEMBISTSTAT_ADDR        0x00C0B238
+#define RIU_AGCMEMSIGNATURESTAT_ADDR   0x00C0B23C
+#define RIU_ECRNXAGCCNTL_ADDR           0x00C0B390
+
+/* FCU RAM */
+#define RC_SYSTEM_CONFIGURATION_ADDR   0x00C0C000
+#define RC_ACCES_TO_CATAXIA_REG_ADDR   0x00C0C004
+
+/* RF ITF */
+#define FPGAB_MPIF_SEL_ADDR            0x00C10030
+#define RF_V6_DIAGPORT_CONF1_ADDR      0x00C10010
+#define RF_v6_PHYDIAG_CONF1_ADDR       0x00C10018
+
+#define RF_V7_DIAGPORT_CONF1_ADDR      0x00F10010
+#define RF_v7_PHYDIAG_CONF1_ADDR       0x00F10018
+
+/*****************************************************************************
+ * Macros for generated register files
+ *****************************************************************************/
+/* Macros for IPC registers access (used in reg_ipc_app.h) */
+#define REG_IPC_APP_RD(env, INDEX)                                      \
+    (*(volatile u32*)((u8*)env + IPC_REG_BASE_ADDR + 4*(INDEX)))
+
+#define REG_IPC_APP_WR(env, INDEX, value)                               \
+    (*(volatile u32*)((u8*)env + IPC_REG_BASE_ADDR + 4*(INDEX)) = value)
+
+#endif /* REG_ACCESS_H_ */
diff --git a/drivers/net/wireless/eswin/reg_ipc_app.h b/drivers/net/wireless/eswin/reg_ipc_app.h
new file mode 100644 (file)
index 0000000..9be4151
--- /dev/null
@@ -0,0 +1,299 @@
+/**
+ ****************************************************************************************
+ *
+ * @file ecrnx_ipc_app.h
+ *
+ * @brief IPC module register definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ****************************************************************************************
+ */
+
+#ifndef _REG_IPC_APP_H_
+#define _REG_IPC_APP_H_
+
+#ifndef __KERNEL__
+#include <stdint.h>
+#include "arch.h"
+#else
+#include "ipc_compat.h"
+#endif
+#include "reg_access.h"
+
+#define REG_IPC_APP_DECODING_MASK 0x0000007F
+
+/**
+ * @brief APP2EMB_TRIGGER register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00      APP2EMB_TRIGGER   0x0
+ * </pre>
+ */
+#define IPC_APP2EMB_TRIGGER_ADDR   0x12000000
+#define IPC_APP2EMB_TRIGGER_OFFSET 0x00000000
+#define IPC_APP2EMB_TRIGGER_INDEX  0x00000000
+#define IPC_APP2EMB_TRIGGER_RESET  0x00000000
+
+__INLINE u32 ipc_app2emb_trigger_get(void *env)
+{
+    return REG_IPC_APP_RD(env, IPC_APP2EMB_TRIGGER_INDEX);
+}
+
+__INLINE void ipc_app2emb_trigger_set(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_APP2EMB_TRIGGER_INDEX, value);
+}
+
+// field definitions
+#define IPC_APP2EMB_TRIGGER_MASK   ((u32)0xFFFFFFFF)
+#define IPC_APP2EMB_TRIGGER_LSB    0
+#define IPC_APP2EMB_TRIGGER_WIDTH  ((u32)0x00000020)
+
+#define IPC_APP2EMB_TRIGGER_RST    0x0
+
+__INLINE u32 ipc_app2emb_trigger_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_APP2EMB_TRIGGER_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+__INLINE void ipc_app2emb_trigger_setf(void *env, u32 app2embtrigger)
+{
+    ASSERT_ERR((((u32)app2embtrigger << 0) & ~((u32)0xFFFFFFFF)) == 0);
+    REG_IPC_APP_WR(env, IPC_APP2EMB_TRIGGER_INDEX, (u32)app2embtrigger << 0);
+}
+
+/**
+ * @brief EMB2APP_RAWSTATUS register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00    EMB2APP_RAWSTATUS   0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_RAWSTATUS_ADDR   0x12000004
+#define IPC_EMB2APP_RAWSTATUS_OFFSET 0x00000004
+#define IPC_EMB2APP_RAWSTATUS_INDEX  0x00000001
+#define IPC_EMB2APP_RAWSTATUS_RESET  0x00000000
+
+__INLINE u32 ipc_emb2app_rawstatus_get(void *env)
+{
+    return REG_IPC_APP_RD(env, IPC_EMB2APP_RAWSTATUS_INDEX);
+}
+
+__INLINE void ipc_emb2app_rawstatus_set(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_EMB2APP_RAWSTATUS_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_RAWSTATUS_MASK   ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_RAWSTATUS_LSB    0
+#define IPC_EMB2APP_RAWSTATUS_WIDTH  ((u32)0x00000020)
+
+#define IPC_EMB2APP_RAWSTATUS_RST    0x0
+
+__INLINE u32 ipc_emb2app_rawstatus_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_RAWSTATUS_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+/**
+ * @brief EMB2APP_ACK register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00          EMB2APP_ACK   0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_ACK_ADDR   0x12000008
+#define IPC_EMB2APP_ACK_OFFSET 0x00000008
+#define IPC_EMB2APP_ACK_INDEX  0x00000002
+#define IPC_EMB2APP_ACK_RESET  0x00000000
+
+__INLINE u32 ipc_emb2app_ack_get(void *env)
+{
+    return REG_IPC_APP_RD(env, IPC_EMB2APP_ACK_INDEX);
+}
+
+__INLINE void ipc_emb2app_ack_clear(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_EMB2APP_ACK_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_ACK_MASK   ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_ACK_LSB    0
+#define IPC_EMB2APP_ACK_WIDTH  ((u32)0x00000020)
+
+#define IPC_EMB2APP_ACK_RST    0x0
+
+__INLINE u32 ipc_emb2app_ack_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_ACK_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+__INLINE void ipc_emb2app_ack_clearf(void *env, u32 emb2appack)
+{
+    ASSERT_ERR((((u32)emb2appack << 0) & ~((u32)0xFFFFFFFF)) == 0);
+    REG_IPC_APP_WR(env, IPC_EMB2APP_ACK_INDEX, (u32)emb2appack << 0);
+}
+
+/**
+ * @brief EMB2APP_UNMASK_SET register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00       EMB2APP_UNMASK   0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_UNMASK_SET_ADDR   0x1200000C
+#define IPC_EMB2APP_UNMASK_SET_OFFSET 0x0000000C
+#define IPC_EMB2APP_UNMASK_SET_INDEX  0x00000003
+#define IPC_EMB2APP_UNMASK_SET_RESET  0x00000000
+
+__INLINE u32 ipc_emb2app_unmask_get(void *env)
+{
+    return REG_IPC_APP_RD(env, IPC_EMB2APP_UNMASK_SET_INDEX);
+}
+
+__INLINE void ipc_emb2app_unmask_set(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_SET_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_UNMASK_MASK   ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_UNMASK_LSB    0
+#define IPC_EMB2APP_UNMASK_WIDTH  ((u32)0x00000020)
+
+#define IPC_EMB2APP_UNMASK_RST    0x0
+
+__INLINE u32 ipc_emb2app_unmask_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_UNMASK_SET_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+__INLINE void ipc_emb2app_unmask_setf(void *env, u32 emb2appunmask)
+{
+    ASSERT_ERR((((u32)emb2appunmask << 0) & ~((u32)0xFFFFFFFF)) == 0);
+    REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_SET_INDEX, (u32)emb2appunmask << 0);
+}
+
+/**
+ * @brief EMB2APP_UNMASK_CLEAR register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00       EMB2APP_UNMASK   0x0
+ * </pre>
+ */
+#define IPC_EMB2APP_UNMASK_CLEAR_ADDR   0x12000010
+#define IPC_EMB2APP_UNMASK_CLEAR_OFFSET 0x00000010
+#define IPC_EMB2APP_UNMASK_CLEAR_INDEX  0x00000004
+#define IPC_EMB2APP_UNMASK_CLEAR_RESET  0x00000000
+
+__INLINE void ipc_emb2app_unmask_clear(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_CLEAR_INDEX, value);
+}
+
+// fields defined in symmetrical set/clear register
+__INLINE void ipc_emb2app_unmask_clearf(void *env, u32 emb2appunmask)
+{
+    ASSERT_ERR((((u32)emb2appunmask << 0) & ~((u32)0xFFFFFFFF)) == 0);
+    REG_IPC_APP_WR(env, IPC_EMB2APP_UNMASK_CLEAR_INDEX, (u32)emb2appunmask << 0);
+}
+
+/**
+ * @brief EMB2APP_STATUS register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   -----------
+ *  31:00       EMB2APP_STATUS   0x0
+ * </pre>
+ */
+#ifdef CONFIG_ECRNX_OLD_IPC
+#define IPC_EMB2APP_STATUS_ADDR   0x12000014
+#define IPC_EMB2APP_STATUS_OFFSET 0x00000014
+#define IPC_EMB2APP_STATUS_INDEX  0x00000005
+#else
+#define IPC_EMB2APP_STATUS_ADDR   0x1200001C
+#define IPC_EMB2APP_STATUS_OFFSET 0x0000001C
+#define IPC_EMB2APP_STATUS_INDEX  0x00000007
+#endif
+#define IPC_EMB2APP_STATUS_RESET  0x00000000
+
+__INLINE u32 ipc_emb2app_status_get(void *env)
+{
+    return REG_IPC_APP_RD(env, IPC_EMB2APP_STATUS_INDEX);
+}
+
+__INLINE void ipc_emb2app_status_set(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_EMB2APP_STATUS_INDEX, value);
+}
+
+// field definitions
+#define IPC_EMB2APP_STATUS_MASK   ((u32)0xFFFFFFFF)
+#define IPC_EMB2APP_STATUS_LSB    0
+#define IPC_EMB2APP_STATUS_WIDTH  ((u32)0x00000020)
+
+#define IPC_EMB2APP_STATUS_RST    0x0
+
+__INLINE u32 ipc_emb2app_status_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_EMB2APP_STATUS_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+/**
+ * @brief APP_SIGNATURE register definition
+ * <pre>
+ *   Bits           Field Name   Reset Value
+ *  -----   ------------------   ----------
+ *  31:00        APP_SIGNATURE   0x0
+ * </pre>
+ */
+#define IPC_APP_SIGNATURE_ADDR   0x12000040
+#define IPC_APP_SIGNATURE_OFFSET 0x00000040
+#define IPC_APP_SIGNATURE_INDEX  0x00000010
+#define IPC_APP_SIGNATURE_RESET  0x00000000
+
+__INLINE u32 ipc_app_signature_get(void *env)
+{
+      return REG_IPC_APP_RD(env, IPC_APP_SIGNATURE_INDEX);
+}
+
+__INLINE void ipc_app_signature_set(void *env, u32 value)
+{
+    REG_IPC_APP_WR(env, IPC_APP_SIGNATURE_INDEX, value);
+}
+
+// field definitions
+#define IPC_APP_SIGNATURE_MASK   ((u32)0xFFFFFFFF)
+#define IPC_APP_SIGNATURE_LSB    0
+#define IPC_APP_SIGNATURE_WIDTH  ((u32)0x00000020)
+
+#define IPC_APP_SIGNATURE_RST    0x0
+
+__INLINE u32 ipc_app_signature_getf(void *env)
+{
+    u32 localVal = REG_IPC_APP_RD(env, IPC_APP_SIGNATURE_INDEX);
+    ASSERT_ERR((localVal & ~((u32)0xFFFFFFFF)) == 0);
+    return (localVal >> 0);
+}
+
+
+#endif // _REG_IPC_APP_H_
+
diff --git a/drivers/net/wireless/eswin/sdio/core.c b/drivers/net/wireless/eswin/sdio/core.c
new file mode 100644 (file)
index 0000000..a7da369
--- /dev/null
@@ -0,0 +1,774 @@
+/**\r
+ ******************************************************************************\r
+ *\r
+ * @file core.c\r
+ *\r
+ * @brief sdio core function definitions\r
+ *\r
+ * Copyright (C) ESWIN 2015-2020\r
+ *\r
+ ******************************************************************************\r
+ */\r
+#include <linux/firmware.h>\r
+#include <linux/kthread.h>\r
+#include "core.h"\r
+#include "fw.h"\r
+#include <uapi/linux/sched/types.h>\r
+//#include "debug.h"\r
+#include "ecrnx_platform.h"\r
+#include "sdio.h"\r
+#include "ecrnx_rx.h"\r
+#include "sdio_host_interface.h"\r
+#include "eswin_utils.h"\r
+\r
+bool loopback;\r
+module_param(loopback, bool, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(loopback, "HIF loopback");\r
+\r
+int power_save;\r
+module_param(power_save, int, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(power_save, "Power Save(0: disable, 1:enable)");\r
+\r
+int disable_cqm = 0;\r
+module_param(disable_cqm, int, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(disable_cqm, "Disable CQM (0: disable, 1:enable)");\r
+\r
+\r
+int listen_interval = 0;\r
+module_param(listen_interval, int, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(listen_interval, "Listen Interval");\r
+\r
+int bss_max_idle = 0;\r
+module_param(bss_max_idle, int, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(bss_max_idle, "BSS Max Idle");\r
+\r
+\r
+bool dl_fw;\r
+module_param(dl_fw, bool, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(dl_fw, "download firmware");\r
+\r
+\r
+#ifdef CONFIG_ECRNX_WIFO_CAIL\r
+bool amt_mode;\r
+module_param(amt_mode, bool, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(amt_mode, "calibrate mode");\r
+#endif\r
+\r
+bool set_gain;\r
+module_param(set_gain, bool, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(set_gain, "set gain delta");\r
+\r
+char *fw_name;\r
+struct eswin *pEswin;\r
+\r
+module_param(fw_name, charp, S_IRUGO);\r
+MODULE_PARM_DESC(fw_name, "Firmware file name");\r
+\r
+#if 0\r
+static void eswin_fw_ready(struct sk_buff *skb, struct eswin * tr)\r
+{\r
+       struct ieee80211_hw *hw = tr->hw;\r
+       struct wim_ready *ready;\r
+       struct wim *wim = (struct wim *)skb->data;\r
+\r
+       ECRNX_PRINT(" %s entry!!", __func__);\r
+       ready = (struct wim_ready *) (wim + 1);\r
+\r
+       ECRNX_PRINT(" %s  -- version: 0x%x", __func__, ready->v.version);\r
+       ECRNX_PRINT(" %s  -- rx_head_size: %d", __func__, ready->v.rx_head_size);\r
+       ECRNX_PRINT(" %s  -- tx_head_size: %d", __func__, ready->v.tx_head_size);\r
+       ECRNX_PRINT(" %s  -- buffer_size: %d", __func__, ready->v.buffer_size);\r
+\r
+       tr->fwinfo.ready = 2;\r
+       tr->fwinfo.version = ready->v.version;\r
+       tr->fwinfo.rx_head_size = ready->v.rx_head_size;\r
+       tr->fwinfo.tx_head_size = ready->v.tx_head_size;\r
+       tr->fwinfo.payload_align = ready->v.payload_align;\r
+       tr->fwinfo.buffer_size = ready->v.buffer_size;\r
+\r
+       ECRNX_PRINT(" %s  -- cap_mask: 0x%llx", __func__, ready->v.cap.cap);\r
+       ECRNX_PRINT(" %s  -- cap_li: %d, %d", __func__, ready->v.cap.listen_interval, listen_interval);\r
+       ECRNX_PRINT(" %s  -- cap_idle: %d, %d", __func__, ready->v.cap.bss_max_idle, bss_max_idle);\r
+\r
+       tr->cap.cap_mask = ready->v.cap.cap;\r
+       tr->cap.listen_interval = ready->v.cap.listen_interval;\r
+       tr->cap.bss_max_idle = ready->v.cap.bss_max_idle;\r
+\r
+       if (listen_interval) {\r
+               hw->max_listen_interval = listen_interval;\r
+               tr->cap.listen_interval = listen_interval;\r
+       } \r
+\r
+       if (bss_max_idle) {\r
+               tr->cap.bss_max_idle = bss_max_idle;\r
+       } \r
+\r
+       dev_kfree_skb(skb);\r
+       ECRNX_PRINT(" %s exit!!", __func__);\r
+}\r
+#endif\r
+static unsigned int sdio_tx_packets = 0;
+static struct timer_list sdio_tx_timer = {0};
+\r
+#define SDIO_TX_TIMER_TIMEOUT_US          (200)
+\r
+void sdio_tx_queue_init(struct tx_buff_queue * queue)\r
+{\r
+       queue->head = NULL;\r
+       queue->tail = NULL;\r
+       queue->count = 0;\r
+       spin_lock_init(&queue->lock);\r
+}\r
+\r
+void sdio_tx_queue_push(struct tx_buff_queue * queue, struct tx_buff_node *node)\r
+{\r
+       unsigned long flags;\r
+\r
+       spin_lock_irqsave(&queue->lock, flags);\r
+       if (queue->head) {\r
+               queue->tail->next = node;\r
+       } else {\r
+               queue->head = node;\r
+       }\r
+\r
+       queue->tail = node;;\r
+       queue->tail->next = NULL;\r
+       queue->count++;\r
+\r
+       spin_unlock_irqrestore(&queue->lock, flags);\r
+\r
+       //ECRNX_PRINT(" queue push count: %d\n", queue->count);\r
+       //ECRNX_PRINT(" queue push head: %#x\n", queue->head);\r
+}\r
+\r
+struct tx_buff_node *sdio_tx_queue_pop(struct tx_buff_queue *queue)\r
+{\r
+       unsigned long flags;\r
+       struct tx_buff_node *res = NULL;\r
+\r
+       //ECRNX_PRINT(" queue pop count: %d\n", queue->count);\r
+       //ECRNX_PRINT(" queue pop head: %#x\n", queue->head);\r
+\r
+       spin_lock_irqsave(&queue->lock, flags);\r
+\r
+       if (queue->count) {\r
+               res = queue->head;\r
+               queue->head = res->next;\r
+               res->next = NULL;\r
+               queue->count--;\r
+       }\r
+\r
+       spin_unlock_irqrestore(&queue->lock, flags);\r
+       return res;\r
+}\r
+\r
+struct tx_buff_node *sdio_tx_queue_peek(struct tx_buff_queue *queue)\r
+{\r
+       unsigned long flags;\r
+       struct tx_buff_node *res = NULL;\r
+\r
+       spin_lock_irqsave(&queue->lock, flags);\r
+\r
+       if (queue->count) {\r
+               res = queue->head;\r
+       }\r
+\r
+       spin_unlock_irqrestore(&queue->lock, flags);\r
+       return res;\r
+}\r
+\r
+struct tx_buff_node * sdio_tx_node_alloc(struct eswin *tr)\r
+{\r
+       struct tx_buff_node * res;\r
+       unsigned long flags;\r
+\r
+       spin_lock_irqsave(&tr->tx_lock,flags);\r
+       res = tr->tx_node_head;\r
+       if(res == NULL)\r
+       {\r
+               spin_unlock_irqrestore(&tr->tx_lock, flags);\r
+               return NULL;\r
+       }\r
+       tr->tx_node_head = tr->tx_node_head->next;\r
+       res->next = NULL;\r
+       tr->tx_node_num--;\r
+       spin_unlock_irqrestore(&tr->tx_lock, flags);\r
+\r
+       return res;\r
+}\r
+\r
+void sdio_tx_node_free(struct eswin *tr, struct tx_buff_node * node)\r
+{\r
+       unsigned long flags;\r
+       spin_lock_irqsave(&tr->tx_lock,flags);\r
+       kfree(node->buff);\r
+    node->buff = NULL;\r
+       node->next = tr->tx_node_head;\r
+       tr->tx_node_head = node;\r
+       tr->tx_node_num++;\r
+       spin_unlock_irqrestore(&tr->tx_lock, flags);\r
+}\r
+\r
+void sdio_tx_pkg_queue_init(struct tx_buff_pkg_queue * queue)\r
+{\r
+       queue->head = NULL;\r
+       queue->tail = NULL;\r
+       queue->count = 0;\r
+       spin_lock_init(&queue->lock);\r
+}\r
+\r
+void sdio_tx_pkg_queue_push(struct tx_buff_pkg_queue * queue, struct tx_buff_pkg_node *node)\r
+{\r
+       unsigned long flags;\r
+\r
+       spin_lock_irqsave(&queue->lock, flags);\r
+       if (queue->head) {\r
+               queue->tail->next = node;\r
+       } else {\r
+               queue->head = node;\r
+       }\r
+\r
+       queue->tail = node;;\r
+       queue->tail->next = NULL;\r
+       queue->count++;\r
+\r
+       spin_unlock_irqrestore(&queue->lock, flags);\r
+}\r
+\r
+struct tx_buff_pkg_node *sdio_tx_pkg_queue_pop(struct tx_buff_pkg_queue *queue)\r
+{\r
+       unsigned long flags;\r
+       struct tx_buff_pkg_node *res = NULL;\r
+\r
+       spin_lock_irqsave(&queue->lock, flags);\r
+\r
+       if (queue->count) {\r
+               res = queue->head;\r
+               queue->head = res->next;\r
+               res->next = NULL;\r
+               queue->count--;\r
+       }\r
+\r
+       spin_unlock_irqrestore(&queue->lock, flags);\r
+       return res;\r
+}\r
+\r
+struct tx_buff_pkg_node * sdio_tx_pkg_node_alloc(struct eswin *tr)\r
+{\r
+       struct tx_buff_pkg_node * res;\r
+       unsigned long flags;\r
+\r
+       spin_lock_irqsave(&tr->tx_pkg_lock,flags);\r
+       res = tr->tx_pkg_node_head;\r
+       if(res == NULL)\r
+       {\r
+               spin_unlock_irqrestore(&tr->tx_pkg_lock, flags);\r
+               return NULL;\r
+       }\r
+       tr->tx_pkg_node_head = tr->tx_pkg_node_head->next;\r
+       res->next = NULL;\r
+       tr->tx_pkg_node_num--;\r
+       spin_unlock_irqrestore(&tr->tx_pkg_lock, flags);\r
+\r
+       return res;\r
+}\r
+\r
+void sdio_tx_pkg_node_free(struct eswin *tr, struct tx_buff_pkg_node * node)\r
+{\r
+       unsigned long flags;\r
+       int i;\r
+       spin_lock_irqsave(&tr->tx_pkg_lock,flags);\r
+       if((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)\r
+       {\r
+               kfree(node->buff);\r
+       }\r
+       node->buff = NULL;\r
+       node->next = tr->tx_pkg_node_head;\r
+       tr->tx_pkg_node_head = node;\r
+       tr->tx_pkg_node_num++;\r
+       /*\r
+       if (node->node_cnt > 1)\r
+       {\r
+               printk("%s,count :%d\n",__func__,node->node_cnt);\r
+       }\r
+       */\r
+\r
+       for (i = 0; i < node->node_cnt; ++i)\r
+       {\r
+               sdio_tx_node_free(tr, node->tx_node[i]);\r
+       }\r
+       spin_unlock_irqrestore(&tr->tx_pkg_lock, flags);\r
+}\r
+\r
+\r
+void eswin_sdio_register_rx_cb(struct eswin *tr, sdio_rx_cb_t cb)\r
+{\r
+    tr->rx_callback = cb;\r
+}\r
+\r
+extern int ecrnx_data_cfm_callback(void *priv, void *host_id);\r
+extern int ecrnx_msg_cfm_callback(void *priv, void *host_id);\r
+static void eswin_core_register_work(struct work_struct *work)\r
+{\r
+       //struct sk_buff *skb_resp;\r
+       int ret;\r
+       struct eswin *tr = container_of(work, struct eswin, register_work.work);\r
+\r
+       ECRNX_PRINT(" %s entry, dl_fw = %d!!", __func__, dl_fw);\r
+\r
+       if (dl_fw  && eswin_fw_file_chech(tr)) {\r
+               eswin_fw_file_download(tr);\r
+        release_firmware(tr->fw);\r
+               dl_fw = false;\r
+               schedule_delayed_work(&tr->register_work, msecs_to_jiffies(1000));\r
+               return;\r
+       }\r
+\r
+#ifdef CONFIG_ECRNX_WIFO_CAIL\r
+       ECRNX_PRINT(" %s entry, amt_mode = %d!!", __func__, amt_mode);\r
+#endif\r
+\r
+       tr->rx_callback = ecrnx_rx_callback;\r
+       tr->data_cfm_callback = ecrnx_data_cfm_callback;\r
+       tr->msg_cfm_callback = ecrnx_msg_cfm_callback;\r
+\r
+       ret = ecrnx_platform_init(tr, &tr->umac_priv);\r
+       set_bit(ESWIN_FLAG_CORE_REGISTERED, &tr->dev_flags);\r
+    ECRNX_DBG("%s exit!!", __func__);\r
+\r
+    return;\r
+}\r
+\r
+int eswin_core_register(struct eswin *tr)\r
+{\r
+       ECRNX_PRINT("%s entry!!", __func__);\r
+       tr->ops->start(tr);\r
+\r
+       //schedule_delayed_work(&tr->register_work, msecs_to_jiffies(10));\r
+       schedule_delayed_work(&tr->register_work, msecs_to_jiffies(1));\r
+       ECRNX_PRINT("%s exit!!", __func__);\r
+       return 0;\r
+}\r
+\r
+void eswin_core_unregister(struct eswin *tr)\r
+{\r
+       struct eswin_sdio *tr_sdio   = (struct eswin_sdio *)tr->drv_priv;\r
+       ECRNX_PRINT("%s entry!!", __func__);\r
+\r
+       cancel_delayed_work(&tr->register_work);\r
+\r
+       if (!test_bit(ESWIN_FLAG_CORE_REGISTERED, &tr->dev_flags))\r
+               return;\r
+\r
+    ecrnx_platform_deinit(tr->umac_priv);\r
+}\r
+\r
+static int eswin_sdio_tx_thread(void *data)
+{
+       struct eswin *tr = (struct eswin *)data;
+       struct tx_buff_pkg_node *node;\r
+       int i, ret = 0, cb_per = 0;\r
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+               struct sched_param param = { .sched_priority = 1 };
+               param.sched_priority = 56;
+               sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+               sched_set_fifo(get_current());
+#endif
+               ECRNX_PRINT("sdio pkg thread entry\n");\r
+
+       while (!kthread_should_stop())
+       {
+               ret = wait_event_interruptible(tr->wait_tx, tr->tx_pkg_queue.count != 0 || kthread_should_stop());
+               if (ret < 0)
+               {
+                       ECRNX_ERR("sdio pkg thread error!\n");\r
+                       return 0;\r
+               }
+               if(kthread_should_stop())\r
+               {\r
+                       continue;\r
+               }\r
+               while (tr->tx_pkg_queue.count != 0)
+               {
+                       node = sdio_tx_pkg_queue_pop(&tr->tx_pkg_queue);\r
+                       if (!node) {\r
+                               cb_per = 0;\r
+                               wake_up_interruptible(&tr->wait_cb);\r
+                           break;\r
+                       }\r
+                       \r
+                       if (tr->ops->xmit) {\r
+                               ret = tr->ops->xmit(tr, node);\r
+                               WARN_ON(ret < 0);\r
+                               if((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC || (node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_MSG_E)\r
+                               {\r
+                                       sdio_tx_pkg_queue_push(&tr->tx_c_queue,node);\r
+                               }\r
+                               else\r
+                               {\r
+                                       sdio_tx_pkg_node_free(tr, node);\r
+                               }\r
+                               cb_per++;\r
+                               //if (cb_per % 4 == 0)\r
+                               {\r
+                                       cb_per = 0;\r
+                                       wake_up_interruptible(&tr->wait_cb);\r
+                               }\r
+                       } else {\r
+                               ECRNX_ERR(" eswin_sdio_work, ops->xmit is null\n");\r
+                       }\r
+               }
+       }
+       ECRNX_PRINT("sdio tx thread exit\n");\r
+       return 0;
+}
+\r
+static int eswin_sdio_callback_thread(void *data)
+{
+       struct eswin *tr = (struct eswin *)data;
+       struct tx_buff_pkg_node *node;\r
+       int i, ret = 0;\r
+       struct txdesc_api *tx_desc;\r
+       ptr_addr host_id;\r
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+               struct sched_param param = { .sched_priority = 1 };
+               param.sched_priority = 56;
+               sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+               sched_set_fifo(get_current());
+#endif
+               ECRNX_PRINT("sdio callback thread entry\n");\r
+
+       while (!kthread_should_stop())
+       {
+               ret = wait_event_interruptible(tr->wait_cb, tr->tx_c_queue.count != 0 || kthread_should_stop());
+               if (ret < 0)
+               {
+                       ECRNX_ERR("sdio callback thread error!\n");\r
+                       return 0;\r
+               }
+               if(kthread_should_stop())\r
+               {\r
+                       continue;\r
+               }\r
+               while (tr->tx_c_queue.count != 0)
+               {
+                       node = sdio_tx_pkg_queue_pop(&tr->tx_c_queue);\r
+                       if((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC && (tr->data_cfm_callback))\r
+                       {\r
+                               for (i = 0; i < node->node_cnt; ++i)\r
+                               {\r
+                                       tx_desc = (struct txdesc_api *)node->tx_node[i]->buff;\r
+                                       if (tx_desc->host.flags & TXU_CNTRL_MGMT)\r
+                                       {\r
+                                               continue;\r
+                                       }\r
+                                       memcpy(&host_id, tx_desc->host.packet_addr, sizeof(ptr_addr));\r
+                                       tr->data_cfm_callback(tr->umac_priv, (void*)host_id);\r
+                               }\r
+                       }\r
+                       //else if((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_MSG_E && (tr->msg_cfm_callback))\r
+                       //{\r
+                       //      for (i = 0; i < node->node_cnt; ++i)\r
+                       //      {\r
+                       //              struct ecrnx_cmd_a2emsg *msg = (struct ecrnx_cmd_a2emsg *)node->tx_node[i]->buff;\r
+                       //              tr->msg_cfm_callback(tr->umac_priv, msg->hostid);\r
+                       //      }\r
+                       //}\r
+                       sdio_tx_pkg_node_free(tr, node);\r
+               }
+       }
+       ECRNX_PRINT("rx callback thread exit\n");\r
+       return 0;
+}
+\r
+static int eswin_sdio_tx_pkg_thread(void *data)
+{
+       struct eswin *tr = (struct eswin *)data;
+       struct tx_buff_node *node;\r
+       struct txdesc_api *tx_desc;\r
+       struct tx_buff_pkg_node * pkg_node = NULL;\r
+       struct tx_buff_pkg_head tx_pkg_head;\r
+       unsigned int offset = 0;\r
+       int i, ret, pkg_cnt = 0;\r
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+               struct sched_param param = { .sched_priority = 1 };
+               param.sched_priority = 56;
+               sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+               sched_set_fifo(get_current());
+#endif
+               ECRNX_PRINT("sdio tx pkg thread entry\n");\r
+
+       while (!kthread_should_stop())
+       {
+               ret = wait_event_interruptible(tr->wait_pkg, tr->tx_queue.count != 0 || kthread_should_stop());
+               if (ret < 0)
+               {
+                       ECRNX_ERR("sdio tx pkg thread error!\n");\r
+                       return 0;\r
+               }
+               if(kthread_should_stop())\r
+               {\r
+                       continue;\r
+               }\r
+               while (tr->tx_queue.count != 0)
+               {
+                       pkg_cnt = 0;\r
+                       offset = 0;\r
+                       memset(&tx_pkg_head,0,sizeof(tx_pkg_head));\r
+                       pkg_node = sdio_tx_pkg_node_alloc(tr);\r
+                       memset(pkg_node,0,sizeof(struct tx_buff_pkg_node));\r
+                       if (!pkg_node) {\r
+                           ECRNX_PRINT(" sdio pkg failed, no node!!\n");\r
+                           break;\r
+                       }\r
+                       node = sdio_tx_queue_peek(&tr->tx_queue);\r
+                       pkg_node->flag = node->flag;\r
+\r
+                       if((node->flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)\r
+                       {\r
+                               node = sdio_tx_queue_pop(&tr->tx_queue);\r
+                               pkg_node->buff = node->buff;\r
+                               pkg_node->len = node->len;\r
+                               pkg_node->tx_node[pkg_cnt] = node;\r
+                               pkg_cnt++;\r
+                       }\r
+                       else\r
+                       {\r
+                               pkg_node->buff = (void *)kzalloc(ALIGN(SDIO_PKG_MAX_DATA*SDIO_PKG_MAX_CNT + sizeof(tx_pkg_head), 512), GFP_ATOMIC);\r
+                               if(!pkg_node->buff){\r
+                               ECRNX_PRINT("pkg_node buff malloc error! \n");\r
+                               }\r
+                               pkg_node->len = sizeof(tx_pkg_head);\r
+\r
+                               while (tr->tx_queue.count)\r
+                               {\r
+                                       offset = pkg_node->len;\r
+                                       node = sdio_tx_queue_peek(&tr->tx_queue);\r
+\r
+                                       if (((node->flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC) || (pkg_cnt > (SDIO_PKG_MAX_CNT-1)))\r
+                                       {\r
+                                               break;\r
+                                       }\r
+                                       //ECRNX_DBG("tx count 2 %d,node %x",tr->tx_queue.count,node);\r
+                                       node = sdio_tx_queue_pop(&tr->tx_queue);\r
+                                       if(ALIGN(node->len, SDIO_PKG_PAD_GRN) < (SDIO_PKG_DIV_MSZ+1))\r
+                                       {\r
+                                               pkg_node->len += ALIGN(node->len, SDIO_PKG_PAD_GRN);\r
+                                               pkg_node->flag |= (ALIGN(node->len, SDIO_PKG_PAD_GRN)/SDIO_PKG_PAD_GRN) << (8+SDIO_PKG_BIT_SHIFT*pkg_cnt);\r
+                                       }\r
+                                       else\r
+                                       {\r
+                                               pkg_node->len += SDIO_PKG_MAX_DATA;\r
+                                               pkg_node->flag |= ((1 << SDIO_PKG_BIT_SHIFT) - 1) << (8+SDIO_PKG_BIT_SHIFT*pkg_cnt);\r
+                                       }\r
+                                       memcpy(pkg_node->buff + offset, node->buff, node->len);\r
+                                       pkg_node->tx_node[pkg_cnt] = node;\r
+                                       tx_pkg_head.len[pkg_cnt] = node->len;\r
+                                       pkg_cnt++;\r
+                               }\r
+                               pkg_node->len = ALIGN(pkg_node->len, 512);\r
+                               memcpy(pkg_node->buff, &tx_pkg_head, sizeof(tx_pkg_head));\r
+                       }\r
+                       pkg_node->node_cnt = pkg_cnt;\r
+                       sdio_tx_pkg_queue_push(&tr->tx_pkg_queue, pkg_node);\r
+                       wake_up_interruptible(&tr->wait_tx);\r
+               }
+       }
+       ECRNX_PRINT("tx pkg thread exit\n");\r
+       return 0;
+}\r
+\r
+void eswin_sdio_ops_init(struct eswin * tr, const struct sdio_ops * ops)\r
+{\r
+       int i;\r
+\r
+       tr->ops = ops;\r
+       \r
+       sdio_tx_queue_init(&tr->tx_queue);\r
+       sdio_tx_pkg_queue_init(&tr->tx_c_queue);\r
+       sdio_tx_pkg_queue_init(&tr->tx_pkg_queue);\r
+\r
+       for (i=1; i<ESWIN_TX_NODE_CNT; i++) {\r
+               tr->tx_node[i-1].next = &tr->tx_node[i];\r
+       }\r
+\r
+       tr->tx_node[i-1].next = NULL;\r
+       tr->tx_node_head = &tr->tx_node[0];\r
+       tr->tx_node_num = ESWIN_TX_NODE_CNT;\r
+       spin_lock_init(&tr->tx_lock);\r
+\r
+       for (i=1; i<ESWIN_TX_NODE_CNT; i++) {\r
+               tr->tx_pkg_node[i-1].next = &tr->tx_pkg_node[i];\r
+       }\r
+\r
+       tr->tx_pkg_node[i-1].next = NULL;\r
+       tr->tx_pkg_node_head = &tr->tx_pkg_node[0];\r
+       tr->tx_pkg_node_num = ESWIN_TX_NODE_CNT;\r
+       spin_lock_init(&tr->tx_pkg_lock);\r
+}\r
+\r
+int tx_desc_count = 0;\r
+int sdio_host_send(void *buff, int len, int flag)\r
+{\r
+    struct eswin * tr= pEswin;\r
+    struct tx_buff_node * node = sdio_tx_node_alloc(tr);\r
+\r
+    ECRNX_DBG("%s enter, data len :%d ", __func__, len);\r
+\r
+    if (!node) {\r
+        ECRNX_PRINT(" sdio send failed, no node!!\n");\r
+        return -1;\r
+    }\r
+\r
+    node->buff = (struct lmac_msg *)kzalloc(len, GFP_ATOMIC);\r
+    if(!node->buff){\r
+        ECRNX_PRINT("buff malloc error! \n");\r
+    }\r
+\r
+    memcpy(node->buff, buff, len);\r
+    node->len  = len;\r
+    node->flag = flag & 0xFF;\r
+\r
+    if ((len > 512) && (len%512) && ((node->flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)) {\r
+        node->flag |= (len%512)<<8;\r
+    }\r
+       else\r
+       {\r
+       }\r
+\r
+    sdio_tx_queue_push(&tr->tx_queue, node);\r
+\r
+       if((node->flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)\r
+       {\r
+               tx_desc_count = 0;\r
+               //queue_work(tr->workqueue_pkg,&tr->work_pkg);\r
+               wake_up_interruptible(&tr->wait_pkg);\r
+       }\r
+       else\r
+       {\r
+               tx_desc_count++;\r
+               if(tx_desc_count%SDIO_PKG_MAX_CNT == 0)\r
+               {\r
+                       //queue_work(tr->workqueue_pkg,&tr->work_pkg);\r
+                       wake_up_interruptible(&tr->wait_pkg);\r
+               }\r
+               else\r
+               {\r
+                       mod_timer(&sdio_tx_timer, jiffies + usecs_to_jiffies(SDIO_TX_TIMER_TIMEOUT_US));\r
+               }\r
+       }\r
+    return 0;\r
+}\r
+\r
+void sdio_tx_timer_handle(struct timer_list *time)
+{
+       struct eswin * tr = pEswin;\r
+       if (tx_desc_count)\r
+       {\r
+               tx_desc_count = 0;\r
+               wake_up_interruptible(&tr->wait_pkg);\r
+       }\r
+}
+\r
+extern void ecrnx_send_handle_register(void * fn);\r
+\r
+struct eswin * eswin_core_create(size_t priv_size, struct device *dev,\r
+                               const struct sdio_ops * ops)\r
+{\r
+       struct eswin * tr;\r
+\r
+       tr = (struct eswin *)kzalloc(sizeof(struct eswin) + priv_size, GFP_KERNEL);\r
+       if(!tr) {\r
+               return NULL;\r
+       }\r
+\r
+       pEswin = tr;\r
+\r
+       tr->dev = dev;\r
+       tr->loopback = loopback;\r
+       //tr->loopback = 1;\r
+       eswin_sdio_ops_init(tr, ops);\r
+       ecrnx_send_handle_register(sdio_host_send);\r
+\r
+       //init_completion(&tr->wim_responded);\r
+       init_waitqueue_head(&tr->wait_pkg);\r
+       init_waitqueue_head(&tr->wait_tx);
+       init_waitqueue_head(&tr->wait_cb);\r
+       \r
+       tr->kthread_pkg = kthread_run(eswin_sdio_tx_pkg_thread, tr, "sdio-tx-pkg");
+       tr->kthread_tx = kthread_run(eswin_sdio_tx_thread, tr, "sdio-tx");\r
+       tr->kthread_cb = kthread_run(eswin_sdio_callback_thread, tr, "sdio-tx-callback");\r
+\r
+       INIT_DELAYED_WORK(&tr->register_work, eswin_core_register_work);\r
+       timer_setup(&sdio_tx_timer, sdio_tx_timer_handle, 0);\r
+\r
+       tr->state = ESWIN_STATE_INIT;\r
+\r
+       //eswin_init_debugfs(tr);\r
+\r
+       ECRNX_PRINT(" %s exit!!", __func__);\r
+       return tr;\r
+\r
+err_free_mac:\r
+       eswin_core_destroy(tr);\r
+       return NULL;\r
+}\r
+\r
+void eswin_core_destroy(struct eswin *tr)\r
+{\r
+    unsigned long flags;\r
+    int i;\r
+\r
+    ECRNX_PRINT("%s entry!!", __func__);\r
+    tr->state = ESWIN_STATE_CLOSEED;\r
+\r
+\r
+    //flush_workqueue(tr->workqueue);\r
+    //destroy_workqueue(tr->workqueue);\r
+    //tr->workqueue = NULL;\r
+\r
+    ECRNX_PRINT("%s node_num %d\n", __func__, tr->tx_node_num);\r
+    spin_lock_irqsave(&tr->tx_lock,flags);\r
+    for (i=0; i<64; i++) \r
+    {\r
+        if (tr->tx_node[i].buff)\r
+        {\r
+            kfree(tr->tx_node[i].buff);\r
+        }\r
+    }\r
+    spin_unlock_irqrestore(&tr->tx_lock, flags);\r
+\r
+    spin_lock_irqsave(&tr->tx_pkg_lock,flags);\r
+    for (i=0; i<64; i++)\r
+    {\r
+        if (tr->tx_pkg_node[i].buff)\r
+        {\r
+            kfree(tr->tx_pkg_node[i].buff);\r
+        }\r
+    }\r
+    spin_unlock_irqrestore(&tr->tx_pkg_lock, flags);\r
+\r
+       kthread_stop(tr->kthread_pkg);\r
+       wake_up_interruptible(&tr->wait_pkg);\r
+       kthread_stop(tr->kthread_cb);\r
+       wake_up_interruptible(&tr->wait_cb);\r
+       kthread_stop(tr->kthread_tx);\r
+       wake_up_interruptible(&tr->wait_tx);\r
+\r
+    kfree(tr);\r
+    tr = NULL;\r
+    //TODO:\r
+    //eswin_mac_destroy(tr);\r
+    ECRNX_PRINT("%s exit!!", __func__);\r
+}\r
+\r
+\r
+//MODULE_AUTHOR("Transa-Semi");\r
+//MODULE_LICENSE("Dual BSD/GPL");\r
+//MODULE_DESCRIPTION("Core module for Transa-Semi 802.11 WLAN SDIO driver");\r
diff --git a/drivers/net/wireless/eswin/sdio/core.h b/drivers/net/wireless/eswin/sdio/core.h
new file mode 100644 (file)
index 0000000..58aa812
--- /dev/null
@@ -0,0 +1,364 @@
+/**
+ ******************************************************************************
+ *
+ * @file core.h
+ *
+ * @brief sdio core definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _CORE_H_
+#define _CORE_H_
+
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+
+#include <net/mac80211.h>
+#include "ecrnx_compat.h"
+
+
+#define ESWIN_FLAG_CORE_REGISTERED     1
+
+#define ESWIN_NR_VIF           2
+#define ESWIN_NR_VIF_HW_QUEUE  4
+
+
+#define ESWIN_STATE_BOOT               1
+#define ESWIN_STATE_INIT               2
+#define ESWIN_STATE_STOP               3
+#define ESWIN_STATE_CLOSING    4
+#define ESWIN_STATE_CLOSEED    5
+#define ESWIN_STATE_START      6
+#define ESWIN_STATE_RUNNING    7
+
+#define SDIO_PKG_MAX_BIT       9
+#define SDIO_PKG_MAX_CNT       4//4/3
+#define SDIO_PKG_BIT_SHIFT     (SDIO_PKG_MAX_BIT/SDIO_PKG_MAX_CNT)
+#define SDIO_PKG_PAD_GRN       (512)//512/256
+#define SDIO_PKG_DIV_MSZ       (1024)//1024/1536
+#define SDIO_DATA_MTU       (1500)
+#define SDIO_PKG_MAX_DATA   (SDIO_DATA_MTU + ECRNX_TX_TXDESC_API_ALIGN)
+
+
+#define WIM_RESP_TIMEOUT    (msecs_to_jiffies(100))
+
+enum ESWIN_SCAN_MODE {
+       ESWIN_SCAN_MODE_IDLE = 0,
+       ESWIN_SCAN_MODE_SCANNING,
+       ESWIN_SCAN_MODE_ABORTING,
+};
+
+
+
+struct fwinfo_t {
+       uint32_t ready;
+       uint32_t version;
+       uint32_t tx_head_size;
+       uint32_t rx_head_size;
+       uint32_t payload_align;
+       uint32_t buffer_size;
+};
+
+struct eswin_capabilities {
+       uint64_t cap_mask;
+       uint16_t listen_interval;
+       uint16_t bss_max_idle;
+       uint8_t bss_max_idle_options;
+};
+
+struct eswin_max_idle {
+       bool enable;
+       u16 period;
+       u16 scale_factor;
+       u8 options;
+       struct timer_list keep_alive_timer;
+
+       unsigned long idle_period; /* jiffies */
+       struct timer_list timer;
+};
+
+
+/* Private txq driver data structure */
+struct eswin_txq {
+       u16 hw_queue; /* 0: AC_BK, 1: AC_BE, 2: AC_VI, 3: AC_VO */
+       struct list_head list;
+       struct sk_buff_head queue; /* own queue */
+       unsigned long nr_fw_queueud;
+       unsigned long nr_push_allowed;
+       struct ieee80211_vif vif;
+       struct ieee80211_sta sta;
+};
+
+struct tx_buff_node {
+       struct tx_buff_node * next;
+       void * buff;
+       int    len;
+       int    flag;
+};
+
+struct tx_buff_queue {
+       struct tx_buff_node * head;
+       struct tx_buff_node * tail;
+       int    count;
+       spinlock_t lock;
+};
+
+struct tx_buff_pkg_node {
+       struct tx_buff_pkg_node * next;
+       void * buff;
+       int    len;
+       int    flag;
+       struct tx_buff_node * tx_node[SDIO_PKG_MAX_CNT];
+       int    node_cnt;
+};
+
+struct tx_buff_pkg_queue {
+       struct tx_buff_pkg_node * head;
+       struct tx_buff_pkg_node * tail;
+       int    count;
+       spinlock_t lock;
+};
+
+struct tx_buff_pkg_head {
+       unsigned int len[SDIO_PKG_MAX_CNT];
+};
+
+#define ESWIN_QUEUE_MAX         (ESWIN_NR_VIF_HW_QUEUE*ESWIN_NR_VIF + 3)
+#define ESWIN_TX_NODE_CNT       64
+
+typedef int (*sdio_rx_cb_t)(void *priv, struct sk_buff *skb);
+typedef int (*sdio_data_cfm_cb_t)(void *priv, void *host_id);
+struct eswin {
+
+    void *umac_priv; //mac drv data.
+       struct ieee80211_hw *hw;
+       struct ieee80211_vif *vif[ESWIN_NR_VIF];
+       struct device *dev;
+       int nr_active_vif;
+       int state;
+       bool promisc;
+
+       bool loopback;
+       bool ampdu_supported;
+       int lb_count;
+       bool amsdu_supported;
+       bool block_frame;
+       bool ampdu_reject;
+       
+       char alpha2[2];
+       u64 tsf_offset;
+
+       const struct sdio_ops *ops;
+       sdio_rx_cb_t rx_callback;
+       sdio_data_cfm_cb_t data_cfm_callback;
+       sdio_data_cfm_cb_t msg_cfm_callback;
+
+       //struct sx_buff_queue queue[ESWIN_NR_VIF]; /* 0: frame, 1: wim */
+       struct tx_buff_queue tx_queue;
+       struct tx_buff_pkg_queue tx_c_queue;
+       struct tx_buff_pkg_queue tx_pkg_queue;
+       struct tx_buff_node tx_node[ESWIN_TX_NODE_CNT];
+       struct tx_buff_node * tx_node_head;
+       spinlock_t tx_lock;
+       int tx_node_num;
+
+       struct tx_buff_pkg_node tx_pkg_node[ESWIN_TX_NODE_CNT];
+       struct tx_buff_pkg_node * tx_pkg_node_head;
+       spinlock_t tx_pkg_lock;
+       int tx_pkg_node_num;
+
+       struct work_struct work;
+       struct work_struct work_c;
+       struct work_struct work_pkg;
+
+       //struct task_struct *kthread_pkg;
+       //wait_queue_head_t wait_pkg; /* wait queue */
+
+       struct eswin_txq ntxq[ESWIN_QUEUE_MAX];
+
+       struct mutex state_mtx;
+       enum ESWIN_SCAN_MODE scan_mode;
+       
+       struct task_struct *kthread_pkg;
+       struct task_struct *kthread_tx;
+       struct task_struct *kthread_cb;
+
+       wait_queue_head_t wait_pkg;
+       wait_queue_head_t wait_tx;
+       wait_queue_head_t wait_cb;
+
+       struct delayed_work scan_timeout;
+
+       //struct work_struct register_work;
+       struct delayed_work register_work;
+
+
+       unsigned long dev_flags;
+       struct mac_address mac_addr[ESWIN_NR_VIF];
+       struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
+
+
+
+       /* Move to vif or sta driver data */
+       u8 frame_seqno;
+       u8 wim_seqno;
+       u8 band;
+       u16 center_freq;
+       u16 aid;
+       u32 cipher_pairwise;
+       u32 cipher_group;
+       
+       struct fwinfo_t fwinfo;
+       struct eswin_capabilities cap;
+
+       /* power management */
+       enum ps_mode {
+               PS_DISABLED,
+               PS_ENABLED,
+               PS_AUTO_POLL,
+               PS_MANUAL_POLL
+       } ps;
+       bool ps_poll_pending;
+       bool ps_enabled;
+
+
+
+       /* tx */
+       spinlock_t txq_lock;
+       struct list_head txq;
+       /* 0: AC_BK, 1: AC_BE, 2: AC_VI, 3: AC_VO */
+       atomic_t tx_credit[IEEE80211_NUM_ACS*3];
+       atomic_t tx_pend[IEEE80211_NUM_ACS*3];
+
+//     struct completion wim_responded;
+//     struct sk_buff *last_wim_responded;
+
+
+       struct delayed_work roc_finish;
+
+       struct firmware *fw;
+
+       struct dentry *debugfs;
+
+       /* must be last */
+       u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+
+/* vif driver data structure */
+struct eswin_vif {
+       struct eswin *tr;
+       int index;
+       struct net_device *dev;
+
+       /* scan */
+       struct delayed_work scan_timeout;
+
+       /* power save */
+       bool ps_polling;
+
+       /* MLME */
+       spinlock_t preassoc_sta_lock;
+       struct list_head preassoc_sta_list;
+
+       /* inactivity */
+       u16 max_idle_period;
+};
+
+#define to_ieee80211_vif(v) \
+       container_of((void *)v, struct ieee80211_vif, drv_priv)
+
+#define to_i_vif(v) ((struct eswin_vif *) (v)->drv_priv)
+
+static inline int hw_vifindex(struct ieee80211_vif *vif)
+{
+       struct eswin_vif *i_vif;
+
+       if (vif == NULL)
+               return 0;
+
+       i_vif = to_i_vif(vif);
+       return i_vif->index;
+}
+
+/* sta driver data structure */
+struct eswin_sta {
+       struct eswin *tr;
+       struct ieee80211_vif *vif;
+       /*struct ieee80211_sta *sta;*/
+
+       enum ieee80211_sta_state state;
+       struct list_head list;
+
+       /* keys */
+       struct ieee80211_key_conf *ptk;
+       struct ieee80211_key_conf *gtk;
+
+       /* BSS max idle period */
+       struct eswin_capabilities cap;
+       struct eswin_max_idle max_idle;
+};
+
+#define to_ieee80211_sta(s) \
+       container_of((void *)s, struct ieee80211_sta, drv_priv)
+
+#define to_i_sta(s) ((struct eswin_sta *) (s)->drv_priv)
+
+
+
+struct eswin_sta_handler {
+       int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        struct ieee80211_sta *sta,
+                        enum ieee80211_sta_state old_state,
+                        enum ieee80211_sta_state new_state);
+
+};
+
+#if 0
+#define STAH(fn)                                       \
+       static struct eswin_sta_handler __stah_ ## fn   \
+       __attribute((__used__))                         \
+       __attribute((__section__("nrc.sta"))) = {       \
+               .sta_state = fn,                        \
+       }
+
+extern struct eswin_sta_handler __sta_h_start, __sta_h_end;
+#endif
+
+
+/* trx */
+
+struct eswin_trx_data {
+       struct eswin *tr;
+       struct ieee80211_vif *vif;
+       struct ieee80211_sta *sta;
+       struct sk_buff *skb;
+       int result;
+};
+
+
+#define NL80211_IFTYPE_ALL (BIT(NUM_NL80211_IFTYPES)-1)
+
+
+
+int eswin_core_register(struct eswin *tr);
+void eswin_core_unregister(struct eswin *tr);
+
+struct eswin * eswin_core_create(size_t priv_size, struct device *dev,
+                               const struct sdio_ops * ops);
+
+void eswin_core_destroy(struct eswin *tr);
+
+
+extern int power_save;
+extern int disable_cqm;
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+extern bool amt_mode;
+#endif
+extern bool set_gain;
+
+#endif
diff --git a/drivers/net/wireless/eswin/sdio/debug.c b/drivers/net/wireless/eswin/sdio/debug.c
new file mode 100644 (file)
index 0000000..83420fa
--- /dev/null
@@ -0,0 +1,202 @@
+/**
+ ******************************************************************************
+ *
+ * @file debug.c
+ *
+ * @brief sdio driver debug function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/timer.h>
+#include "ecrnx_utils.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_rx.h"
+#include "ecrnx_sdio.h"
+#include "core.h"
+#include "debug.h"
+#include "sdio.h"
+
+
+extern int ecrnx_rx_callback(void *priv, struct sk_buff *skb);
+
+//kernal timer param
+static struct timer_list tm;
+static int counter = 0;
+
+//rx param
+struct sk_buff *skb = NULL;
+struct ecrnx_hw *g_ecrnx_hw = NULL;
+
+//tx param
+struct ecrnx_vif vif;
+struct ecrnx_sta sta;
+struct cfg80211_mgmt_tx_params params;
+
+static void test_timer_handler(struct timer_list * lt);
+
+void ecrnx_hw_set(void* init_ecrnx_hw)
+{
+    g_ecrnx_hw = (struct ecrnx_hw *)init_ecrnx_hw;
+}
+
+static int sdio_rx_param_init(void)
+{
+    struct rxu_stat_mm rxu_state;
+    struct rx_hd  rx_head;
+    struct ethhdr eth_hd;
+    int res = 0, index = 0;
+    uint8_t *ptr = NULL;
+    uint16_t head_len = sizeof(struct ethhdr);
+    ECRNX_DBG("%s entry!!", __func__);
+    memset(&rxu_state, 0, sizeof(struct rxu_stat_mm));
+    memset(&rx_head, 0, sizeof(struct rx_hd));
+    memset(&eth_hd, 0, sizeof(struct ethhdr));
+
+    rxu_state.comm_hd.frm_type = SDIO_FRM_TYPE_RXDESC;
+    //rxu_state.comm_hd.frm_type = SDIO_FRM_TYPE_MSG;
+    if(rxu_state.comm_hd.frm_type == SDIO_FRM_TYPE_RXDESC)
+    {
+        head_len += sizeof(struct rxu_stat_mm) + sizeof(struct rx_hd);
+    }
+    else
+    {
+        head_len += sizeof(dispatch_hdr_t);
+    }
+
+    skb = dev_alloc_skb(FRAME_SIZE + head_len);
+    skb_reserve(skb, head_len);
+    ptr = skb_put(skb, FRAME_SIZE); //ptr is skb tail
+    memset(skb->data, 0x0f, FRAME_SIZE); //payload
+    skb_push(skb, sizeof(struct ethhdr));
+
+    for( index = 0; index < ETH_ALEN; index++)
+    {
+        eth_hd.h_dest[index] = index;
+        eth_hd.h_source[index] = index;
+    }
+
+    eth_hd.h_proto = ETH_P_80221; //ETHERTYPE_IP;
+    memcpy(skb->data, &eth_hd, sizeof(struct ethhdr));
+
+    if(rxu_state.comm_hd.frm_type == SDIO_FRM_TYPE_RXDESC)
+    {
+        //data frame, need header, rxu state
+        //rx head
+        skb_push(skb, sizeof(struct rx_hd));
+        rx_head.frmlen = FRAME_SIZE + head_len;
+        rx_head.ampdu_stat_info = 0; 
+        //...
+        memcpy(skb->data , &rx_head, sizeof(struct rx_hd));
+
+        //rxu state
+        skb_push(skb, sizeof(struct rxu_stat_mm));
+        rxu_state.msdu_mode = 0x01;
+        rxu_state.host_id = 0x0001;
+        rxu_state.frame_len  = rx_head.frmlen;
+        rxu_state.status = RX_STAT_MONITOR;
+        //rxu_state.phy_info.info1 = 1 | (1 << 8) | (2450 << 16);
+        //rxu_state.phy_info.info2 = 2450 | (2450 << 16);
+#ifdef CONFIG_ECRNX_FULLMAC
+        //rxu_state.flags = 0;
+#endif
+        //rxu_state.pattern = ecrnx_rxbuff_pattern;
+        memcpy(skb->data, &rxu_state, sizeof(struct rxu_stat_mm));
+    }
+    else
+    {
+        //message frame, don't need header
+        skb_push(skb, sizeof(dispatch_hdr_t)); //rxu state
+        memcpy(skb->data, &rxu_state.comm_hd, sizeof(dispatch_hdr_t));
+    }
+
+    for(index = 0; index < head_len; index++)
+    {
+        ECRNX_DBG("0x%x ", skb->data[index]);
+    }
+
+    ECRNX_DBG("%s exit, skb_len:%d, type: %d!!", __func__, skb->len, (skb->data[1] << 8) | skb->data[0]);
+
+    return res;
+}
+
+extern int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, \
+                        struct ecrnx_sta *sta, \
+                        struct cfg80211_mgmt_tx_params *params, \
+                        bool offchan, \
+                        u64 *cookie);
+
+/*
+struct cfg80211_mgmt_tx_params {
+       struct ieee80211_channel *chan;
+       bool offchan;
+       unsigned int wait;
+       const u8 *buf;
+       size_t len;
+       bool no_cck;
+       bool dont_wait_for_ack;
+       int n_csa_offsets;
+       const u16 *csa_offsets;
+};
+*/
+static void sdio_tx_param_init(void)
+{
+    u8 send_buf[FRAME_SIZE] = {0x00, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
+
+    params.len = FRAME_SIZE;
+    params.buf = (const u8 *)send_buf;
+    params.n_csa_offsets = 10;
+    params.csa_offsets = (const u16 *)send_buf;
+    params.no_cck = 0;
+
+    vif.ecrnx_hw = g_ecrnx_hw;
+}
+
+void sdio_rx_tx_test_schedule(void)
+{
+    ECRNX_DBG("%s entry!!", __func__);
+
+    //wangc add
+    //tm.function = test_timer_handler;
+    tm.expires = jiffies + HZ * 10;
+    add_timer(&tm);
+    
+    sdio_rx_param_init();
+    sdio_tx_param_init();
+    ECRNX_DBG("%s exit!!", __func__);
+}
+
+static void test_timer_handler(struct timer_list * lt)
+{
+    ECRNX_DBG("%s, counter:%d\n", __FUNCTION__, counter);
+
+    if(counter%2)
+    {
+        u64 cookie;
+        //ecrnx_start_mgmt_xmit(&vif, NULL, &params, false, &cookie);
+    }
+    else
+    {
+        ecrnx_rx_callback(g_ecrnx_hw, skb);
+    }
+
+    if(lt)
+    {
+        counter++;
+    }
+
+    if(counter < 5)
+    {
+        //tm.expires = jiffies +1 * HZ / 1000/10;  //100us
+        tm.expires = jiffies +1 * HZ;
+        add_timer(&tm);
+    }
+    else
+    {
+        counter = 0;
+        del_timer(&tm);
+    }
+}
+
diff --git a/drivers/net/wireless/eswin/sdio/debug.h b/drivers/net/wireless/eswin/sdio/debug.h
new file mode 100644 (file)
index 0000000..ec53824
--- /dev/null
@@ -0,0 +1,31 @@
+/**
+ ******************************************************************************
+ *
+ * @file debug.h
+ *
+ * @brief sdio driver debug definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _ECRNX_DBG_
+#define _ECRNX_DBG_
+#include <net/mac80211.h>
+
+//#define CONFIG_HIF_PRINT_TX_DATA
+#define CONFIG_USE_TXQ
+
+//#define CONFIG_NRC_HIF_PRINT_FLOW_CONTROL
+//#define CONFIG_SHOW_TX_SPEED
+//#define CONFIG_SHOW_RX_SPEED
+
+#define FRAME_SIZE         512
+
+void eswin_dump_wim(struct sk_buff *skb);
+void eswin_init_debugfs(struct eswin *tr);
+void sdio_rx_tx_test_schedule(void);
+void ecrnx_hw_set(void* init_ecrnx_hw);
+
+#endif
diff --git a/drivers/net/wireless/eswin/sdio/ecrnx_sdio.c b/drivers/net/wireless/eswin/sdio/ecrnx_sdio.c
new file mode 100644 (file)
index 0000000..fc8cd1f
--- /dev/null
@@ -0,0 +1,559 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_sdio.c
+ *
+ * @brief ECRNX sdio init and management function
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include "core.h"
+//#include "debug.h"
+#include "ecrnx_utils.h"
+#include "ecrnx_cmds.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_rx.h"
+#include "ipc_host.h"
+#include "ipc_shared.h"
+#include "ecrnx_events.h"
+#include "ecrnx_sdio.h"
+#ifdef CONFIG_TEST_ESWIN_SDIO
+#include "debug.h"
+#endif
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "ecrnx_amt.h"
+#endif
+
+
+#define SDIO_ADDR_DATA                 (unsigned int)(0x200)
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define FW_STR  "lmac"
+#elif defined CONFIG_ECRNX_FULLMAC
+#define FW_STR  "fmac"
+#endif
+
+sdio_rx_buf_t sdio_rx_buff;
+
+extern const int nx_txdesc_cnt_msk[];
+
+struct vendor_radiotap_hdr {
+    u8 oui[3];
+    u8 subns;
+    u16 len;
+    u8 data[];
+};
+
+/**
+ * @brief: sdio_rx_buf_init: Initialization the sdio rx buffer
+ * @param {void} void 
+ * @return: none
+ */
+static void sdio_rx_buf_init(void)
+{
+    uint8_t i = 0;
+    ECRNX_DBG("%s entry!!", __func__);
+    memset(&sdio_rx_buff, 0, sizeof(sdio_rx_buf_t));
+
+    for(i = 0; i < SDIO_RXBUF_CNT; i++)
+    {
+        sdio_rx_buff.ecrnx_sdio_rx_skb[i].skb = dev_alloc_skb(SDIO_RXBUF_SIZE);
+        skb_put(sdio_rx_buff.ecrnx_sdio_rx_skb[i].skb, SDIO_RXBUF_SIZE);
+    }
+#ifdef CONFIG_TEST_ESWIN_SDIO
+    sdio_rx_tx_test_schedule();
+#endif
+    ECRNX_DBG("%s exit!!", __func__);
+}
+
+/**
+ * @brief: sdio_rx_buf_deinit: Deinitialization the sdio rx buffer
+ * @param {void} void 
+ * @return: none
+ */
+static void sdio_rx_buf_deinit(void)
+{
+    uint8_t i = 0;
+
+    memset(&sdio_rx_buff, 0, sizeof(sdio_rx_buf_t));
+
+    for(i = 0; i < SDIO_RXBUF_CNT; i++)
+    {
+        if(sdio_rx_buff.ecrnx_sdio_rx_skb[i].skb)
+        {
+            dev_kfree_skb(sdio_rx_buff.ecrnx_sdio_rx_skb[i].skb);
+        }
+    }
+}
+
+/**
+ * @brief: sdio_rx_buf_push: push a skb element to sdio rx buffer
+ * @param {skb}  skb data need to push
+ * @param {recv_len}  skb data length
+ * @return: sk_buff
+ */
+struct sk_buff * sdio_rx_buf_push(struct sk_buff *skb, uint16_t recv_len)
+{
+    uint8_t index = 0;
+
+    ECRNX_DBG("%s enter, skb: %d, skb_data:%d, skb_len:%d", __func__, skb, skb->data, recv_len);
+    if(atomic_read(&sdio_rx_buff.suspend))
+    {
+        return NULL;
+    }
+
+    if(sdio_rx_buff.sdio_host_rx_buff_used >= SDIO_RXBUF_CNT)
+    {
+        ECRNX_PRINT("no enough space \n");
+        return NULL;
+    }
+
+    if((!skb) || (!recv_len) || (recv_len > SDIO_RXBUF_SIZE))
+    {
+        ECRNX_PRINT("rx data is error \n");
+        return NULL;
+    }
+
+    atomic_set(&sdio_rx_buff.suspend, 1);
+
+    do
+    {
+        if(!sdio_rx_buff.ecrnx_sdio_rx_skb[index].flag)  //find a index to store the data
+        {
+            break;
+        }
+        index++;
+    }while(index < SDIO_RXBUF_CNT);
+
+    sdio_rx_buff.sdio_host_rx_buff_used++;
+    sdio_rx_buff.ecrnx_sdio_rx_skb[index].flag = true;
+    sdio_rx_buff.ecrnx_sdio_rx_skb[index].data_len = recv_len;
+    memcpy(sdio_rx_buff.ecrnx_sdio_rx_skb[index].skb->data, skb->data, recv_len);
+    atomic_set(&sdio_rx_buff.suspend, 0);
+
+    return sdio_rx_buff.ecrnx_sdio_rx_skb[index].skb;
+}
+
+/**
+ * @brief: sdio_rx_buf_pop: pop a skb element from sdio rx buffer
+ * @param {void} 
+ * @return: sk_buff
+ */
+struct sk_buff* sdio_rx_buf_pop(void)
+{
+    uint8_t index = 0;
+    struct sk_buff *skb= NULL;
+
+    if(atomic_read(&sdio_rx_buff.suspend))
+    {
+       ECRNX_PRINT("sdio suspend! \n");
+        return NULL;
+    }
+
+    if(sdio_rx_buff.sdio_host_rx_buff_used <= 0)
+    {
+        ECRNX_PRINT("no data in memory \n");
+        return NULL;
+    }
+
+    atomic_set(&sdio_rx_buff.suspend, 1);
+
+    do
+    {
+        if(sdio_rx_buff.ecrnx_sdio_rx_skb[index].flag)  //find a index to pop the data
+        {
+            break;
+        }
+        index++;
+    }while(index < SDIO_RXBUF_CNT);
+
+    if(index != SDIO_RXBUF_CNT)
+    {
+        skb = sdio_rx_buff.ecrnx_sdio_rx_skb[index].skb;
+        sdio_rx_buff.ecrnx_sdio_rx_skb[index].flag = false;
+        sdio_rx_buff.sdio_host_rx_buff_used = (sdio_rx_buff.sdio_host_rx_buff_used > 0)? sdio_rx_buff.sdio_host_rx_buff_used-1: 0;
+    }
+    atomic_set(&sdio_rx_buff.suspend, 0);
+
+    return skb;
+}
+
+/**
+ * @brief: ipc_host_rxdesc_handler: Handle the reception of a Rx Descriptor
+ *  Called from general IRQ handler when status %IPC_IRQ_E2A_RXDESC is set
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_rxdesc_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+       u8 ret = 0;
+    // LMAC has triggered an IT saying that a reception has occurred.
+    // Then we first need to check the validity of the current hostbuf, and the validity
+    // of the next hostbufs too, because it is likely that several hostbufs have been
+    // filled within the time needed for this irq handling
+#ifdef CONFIG_ECRNX_FULLMAC
+    // call the external function to indicate that a RX descriptor is received
+    ret = env->cb.recv_data_ind(env->pthis, skb);
+#else
+    // call the external function to indicate that a RX packet is received
+    ret = env->cb.recv_data_ind(env->pthis, skb);
+#endif //(CONFIG_ECRNX_FULLMAC)
+    ECRNX_DBG("%s exit!!", __func__);
+       return ret;
+}
+
+/**
+ * @brief: sdio_host_radar_handler  Handle the reception of radar events
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_radar_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    int ret = 0;
+    
+#ifdef CONFIG_ECRNX_RADAR
+    // LMAC has triggered an IT saying that a radar event has been sent to upper layer.
+    // Then we first need to check the validity of the current msg buf, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    spin_lock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+    ret = env->cb.recv_radar_ind(env->pthis, skb);
+    spin_unlock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+#endif /* CONFIG_ECRNX_RADAR */
+    return ret;
+}
+
+/**
+ * @brief: sdio_host_unsup_rx_vec_handler  Handle the reception of unsupported rx vector
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_unsup_rx_vec_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    return env->cb.recv_unsup_rx_vec_ind(env->pthis, skb);
+}
+
+/**
+ * @brief: sdio_host_msg_handler  Handler for firmware message
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_msg_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    // LMAC has triggered an IT saying that a message has been sent to upper layer.
+    // Then we first need to check the validity of the current msg buf, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    return env->cb.recv_msg_ind(env->pthis, skb->data);
+}
+
+/**
+ * @brief: sdio_host_msgack_handler  Handle the reception of message acknowledgement
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_msgack_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    uint64_t hostid = *(uint64_t *)skb->data;
+
+    ASSERT_ERR(hostid);
+
+    env->msga2e_hostid = NULL;
+    env->cb.recv_msgack_ind(env->pthis, hostid);
+
+    return 0;
+}
+
+/**
+ * @brief: sdio_host_dbg_handler  Handle the reception of Debug event
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int sdio_host_dbg_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    // LMAC has triggered an IT saying that a DBG message has been sent to upper layer.
+    // Then we first need to check the validity of the current buffer, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    return env->cb.recv_dbg_ind(env->pthis, skb);
+}
+
+/**
+ * @brief: sdio_host_tx_cfm_handler  Handle the reception of TX confirmation
+ * @param {env} pointer to the sdio Host environment
+ * @param {queue_idx} index of the hardware on which the confirmation has been received
+ * @param {user_pos} index of the user position
+ * @return: none
+ */
+static int sdio_host_tx_cfm_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    ptr_addr host_id;
+    struct sk_buff *skb_cfm;
+    struct ecrnx_txhdr *txhdr;
+
+    ECRNX_DBG("%s, skb: 0x%08x \n", __func__, skb);
+#ifdef CONFIG_ECRNX_FULLMAC
+    struct tx_cfm_tag *cfm;
+
+    cfm = (struct tx_cfm_tag *)skb->data;
+    memcpy((uint8_t *)&host_id, (uint8_t *)cfm->hostid, sizeof(ptr_addr));
+    ECRNX_DBG("--%s--hostid 0x%08x, skb: 0x%x \n", __func__, host_id, skb);
+    if (host_id == 0) {
+        return 0;
+    }
+
+    skb_cfm = (struct sk_buff *)host_id;
+    txhdr = (struct ecrnx_txhdr *)skb_cfm->data;
+    memcpy(&txhdr->hw_hdr.cfm, cfm, sizeof(*cfm));
+#elif defined CONFIG_ECRNX_SOFTMAC
+    //TODO:
+#endif
+
+    return env->cb.send_data_cfm(env->pthis, host_id);
+    //return 0;
+}
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+/**
+ * @brief: sdio_host_rx_handler  Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {skb} received skb data
+ * @return: none
+**/
+void sdio_host_amt_rx_handler(uint32_t frm_type, struct sk_buff *skb)
+{
+    int need_free = 0;
+
+    ECRNX_PRINT("%s enter, frame type: %d, len %d.!!", __func__, frm_type, skb->len);
+    if (frm_type != SDIO_FRM_TYPE_RXDESC)
+    {
+        skb_pull(skb, SKB_DATA_COM_HD_OFFSET); //delete the frame common header
+    }
+
+    switch (frm_type)
+    {
+               case SDIO_FRM_TYPE_IWPRIV:
+        {
+            /*print_hex_dump(KERN_INFO, "iwpriv-cfm:", DUMP_PREFIX_ADDRESS, 32, 1,
+                       skb->data, skb->len, false);*/
+            amt_vif.rxlen = skb->len;
+                       memset(amt_vif.rxdata, 0, ECRNX_RXSIZE);
+            memcpy(amt_vif.rxdata, skb->data, skb->len > ECRNX_RXSIZE ? ECRNX_RXSIZE : skb->len);
+            amt_vif.rxdatas = 1;
+            wake_up(&amt_vif.rxdataq);
+                       need_free = 1;
+            break;
+        }
+
+        default:
+                       need_free = 1;
+            break;
+    }
+
+    if (need_free && skb) { // free the skb
+               ECRNX_DBG("skb free: 0x%x, \n", skb);
+        dev_kfree_skb(skb);
+    }
+    ECRNX_DBG("%s exit!!", __func__);
+    return;
+}
+#endif
+
+/**
+ * @brief: sdio_host_rx_handler  Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+void sdio_host_rx_handler(uint32_t frm_type, struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    int ret = 1;
+
+    ECRNX_DBG("%s enter, frame type: %d!!", __func__, frm_type);
+    if (frm_type != SDIO_FRM_TYPE_RXDESC)
+    {
+        skb_pull(skb, SKB_DATA_COM_HD_OFFSET); //delete the frame common header
+    }
+
+    switch (frm_type)
+    {
+        case SDIO_FRM_TYPE_RXDESC:
+        {
+            sdio_host_rxdesc_handler(env, skb);
+            break;
+        }
+
+        case SDIO_FRM_TYPE_MSG_ACK:
+        {
+            ret = sdio_host_msgack_handler(env, skb);
+            break;
+        }
+
+        case SDIO_FRM_TYPE_MSG:
+        {
+            ret = sdio_host_msg_handler(env, skb);
+            break;
+        }
+
+        case SDIO_FRM_TYPE_TXCFM:
+        {
+            /* add the spinlock which was missed during porting. */
+            spin_lock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+            while(skb->len > sizeof(struct tx_cfm_tag))
+            {
+                ret = sdio_host_tx_cfm_handler(env, skb);
+                skb_pull(skb, sizeof(struct tx_cfm_tag));
+            }
+            ret = sdio_host_tx_cfm_handler(env, skb);
+            spin_unlock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+            break;
+        }
+
+        case SDIO_FRM_TYPE_UNSUP_RX_VEC:
+        {
+            // handle the unsupported rx vector reception
+            ret = sdio_host_unsup_rx_vec_handler(env, skb);
+            break;
+        }
+
+        case SDIO_FRM_TYPE_RADAR:
+        {
+            // handle the radar event reception
+            ret = sdio_host_radar_handler(env, skb);
+            break;
+        }
+
+        case SDIO_FRM_TYPE_TBTT_SEC:
+        {
+            env->cb.sec_tbtt_ind(env->pthis);
+            break;
+        }
+
+        case SDIO_FRM_TYPE_TBTT_PRIM:
+        {
+            env->cb.prim_tbtt_ind(env->pthis);
+            break;
+        }
+
+        case SDIO_FRM_TYPE_DBG:
+        {
+            ret = sdio_host_dbg_handler(env, skb);
+            break;
+        }
+
+        case SDIO_FRM_TYPE_UPDATE:
+        {
+            ret = 0;
+            break;
+        }
+        default:
+               ret = 0;
+            break;
+    }
+
+    if (!ret && skb) { // free the skb
+       ECRNX_DBG("skb free: 0x%x, ret: %d!! \n", skb, ret);
+        dev_kfree_skb(skb);
+    }
+    ECRNX_DBG("%s exit!!", __func__);
+    return;
+}
+
+/**
+ * @brief: rcv_skb_convert  convert the sdio received skb to the ecrnx handled skb.
+ * @param {src_rxu_state} received rxu state in skb
+ * @param {src_rxu_state} received rx header in skb
+ * @param {src_rxu_state} handled hw rxhdr in skb
+ * @param {src_rxu_state} handled rx desc tag  in skb
+ * @return: u8
+ */
+u8 rcv_skb_convert(struct rxu_stat_mm* src_rxu_state, \
+                struct rx_hd* src_rx_hd, \
+                struct sk_buff *skb, \
+                struct hw_rxhdr* dst_hw_rxhdr,\
+                struct rxdesc_tag* dst_rxdesc_tag)
+{
+    ECRNX_DBG("%s enter!!", __func__);
+#if 0
+    uint16_t index = 0;
+
+    if (!skb || !skb->data){
+        ECRNX_PRINT("RX data invalid \n");
+        return -1;
+    }
+
+    //copy the rxu state and rx header, repush process need to used them
+    memcpy(src_rxu_state, skb->data, sizeof(struct rxu_stat_mm));
+    memcpy(src_rx_hd, skb->data + sizeof(struct rxu_stat_mm), sizeof(struct rx_hd));
+
+    /*copy the hw vector and rxu state */
+    memcpy(dst_hw_rxhdr, skb->data + sizeof(struct rxu_stat_mm), sizeof(struct rx_hd));
+    memcpy(&dst_hw_rxhdr->phy_info, \
+        skb->data + SKB_DATA_TAG_OFFSET, \
+        sizeof(struct rxu_stat_mm) - SKB_DATA_TAG_OFFSET);
+
+    memcpy(dst_rxdesc_tag, skb->data + SKB_DATA_COM_HD_OFFSET + 2, sizeof(struct rxdesc_tag)); //two byte msdu_mode
+    /*recove the hdr to skb data*/
+    skb_pull(skb, SKB_DATA_HD_OFFSET); //delete the frame type and amsdu flag
+    skb_push(skb, sizeof(struct hw_rxhdr));
+    memcpy(skb->data, dst_hw_rxhdr, sizeof(struct hw_rxhdr)); //put hw header to skb
+    skb_push(skb, sizeof(struct rxdesc_tag));
+    memcpy(skb->data, dst_rxdesc_tag, sizeof(struct rxdesc_tag)); //put rx desc tag to skb
+
+    for(index = 0; index < sizeof(struct hw_rxhdr) + sizeof(struct rxdesc_tag); index++)
+    {
+        ECRNX_DBG("0x%x ", skb->data[index]);
+    }
+#endif
+    ECRNX_DBG("%s exit!!", __func__);
+    return 0;
+}
+
+/**
+ * @brief: ecrnx_sdio_init  Initialize sdio interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: u8
+ */
+u8 ecrnx_sdio_init(struct ecrnx_hw *ecrnx_hw)
+{
+    ECRNX_DBG("%s entry!!", __func__);
+    // Save the pointer to the register base
+    ecrnx_hw->ipc_env->pthis = (void*)ecrnx_hw;
+    
+#ifdef CONFIG_TEST_ESWIN_SDIO
+    ecrnx_hw_set((void*)ecrnx_hw);
+#endif
+    ECRNX_DBG("%s exit!!", __func__);
+    return 0;
+}
+
+/**
+ * @brief: ecrnx_sdio_deinit  DeInitialize sdio interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: none
+ */
+void ecrnx_sdio_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    sdio_rx_buf_deinit();
+    memset(ecrnx_hw, 0, sizeof(struct ecrnx_hw));
+    memset(&sdio_rx_buff, 0, sizeof(sdio_rx_buf_t));
+}
diff --git a/drivers/net/wireless/eswin/sdio/ecrnx_sdio.h b/drivers/net/wireless/eswin/sdio/ecrnx_sdio.h
new file mode 100644 (file)
index 0000000..d2b210b
--- /dev/null
@@ -0,0 +1,173 @@
+ /**
+ ******************************************************************************
+ *
+ * @file ecrnx_sdio.h
+ *
+ * @brief ecrnx sdio header file
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _ECRNX_SDIO_H_
+#define _ECRNX_SDIO_H_
+
+#include "ecrnx_rx.h"
+#include "eswin_utils.h"
+#include "ecrnx_defs.h"
+/*
+ * Number of Host buffers available for Data Rx handling
+ */
+#define SDIO_RXBUF_CNT       32
+#define SDIO_RXBUF_SIZE      12288
+
+enum{
+    SDIO_FRM_TYPE_RXDESC =1,
+    SDIO_FRM_TYPE_MSG,
+    SDIO_FRM_TYPE_DBG,
+    SDIO_FRM_TYPE_UNSUP_RX_VEC,
+    SDIO_FRM_TYPE_RADAR,
+    SDIO_FRM_TYPE_TBTT_SEC,
+    SDIO_FRM_TYPE_TBTT_PRIM,
+    SDIO_FRM_TYPE_MSG_ACK,
+    SDIO_FRM_TYPE_TXCFM,
+    SDIO_FRM_TYPE_IWPRIV,
+    SDIO_FRM_TYPE_UPDATE
+};
+
+
+#define SKB_DATA_COM_HD_OFFSET      sizeof(dispatch_hdr_t)
+//#define SKB_DATA_TAG_OFFSET         offsetof(struct rxu_stat_mm, phy_info) //two byte msdu flag
+//#define SKB_DATA_HD_OFFSET          sizeof(struct rxu_stat_mm) + sizeof(struct rx_hd)
+
+
+struct sdio_rx_skb{
+    bool flag;
+    u16 data_len;
+    struct sk_buff *skb;
+};
+
+typedef struct{
+    atomic_t suspend;
+    uint8_t sdio_host_rx_buff_used;
+    struct  sdio_rx_skb ecrnx_sdio_rx_skb[SDIO_RXBUF_CNT];
+}sdio_rx_buf_t;
+
+/// Structure containing the information about the PHY channel that is used
+// typedef uint32_t u32_l;
+struct phy_channel_info_sdio
+{
+    /// PHY channel information 1
+    uint32_t info1;
+    /// PHY channel information 2
+    uint32_t info2;
+};
+
+/*调整phy info、 pattern,因为这些信息必须要放到另外一个buff中传输*/
+/// Element in the pool of RX header descriptor.
+struct rx_hd
+{
+    /// Total length of the received MPDU
+    uint16_t            frmlen;
+    /// AMPDU status information
+    uint16_t            ampdu_stat_info;
+    /// TSF Low
+    uint32_t            tsflo;
+    /// TSF High
+    uint32_t            tsfhi;
+    /// Rx Vector 1
+    struct rx_vector_1  rx_vec_1;
+    /// Rx Vector 2
+    struct rx_vector_2  rx_vec_2;
+    /// MPDU status information
+    uint32_t            statinfo;
+};
+
+/*调整phy info、 pattern,因为这些信息必须要放到另外一个buff中传输*/
+struct rxu_stat_mm
+{
+       /*type of msg/dbg/frm etc.*/
+       dispatch_hdr_t  frm_type;
+    uint32_t fragment_flag     : 1;
+    uint32_t is_qos            : 1;
+    uint32_t need_reord        : 1;
+    uint32_t need_pn_check     : 1;
+    uint32_t is_ga             : 1;
+    uint32_t flags_rsvd0       : 3;
+    uint32_t tid               : 8;
+    uint16_t sn;
+    /// Length
+    uint16_t frame_len;
+    /// Status (@ref rx_status_bits)
+    uint16_t status;
+    uint16_t real_offset; /* dma address align 4, real data - real_offset  */
+};
+
+
+extern sdio_rx_buf_t sdio_rx_buff;
+
+/**
+ * @brief: ecrnx_sdio_init  Initialize sdio interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: u8
+ */
+u8 ecrnx_sdio_init(struct ecrnx_hw *ecrnx_hw);
+
+/**
+ * @brief: ecrnx_sdio_deinit  DeInitialize sdio interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: none
+ */
+void ecrnx_sdio_deinit(struct ecrnx_hw *ecrnx_hw);
+
+/**
+ * @brief: sdio_rx_buf_push: push a skb element to sdio rx buffer
+ * @param {skb}  skb data need to push
+ * @param {recv_len}  skb data length
+ * @return: sk_buff
+ */
+struct sk_buff * sdio_rx_buf_push(struct sk_buff *skb, uint16_t recv_len);
+
+
+/**
+ * @brief: sdio_rx_buf_pop: pop a skb element from sdio rx buffer
+ * @param {void} 
+ * @return: sk_buff
+ */
+struct sk_buff* sdio_rx_buf_pop(void);
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+/**
+ * @brief: sdio_host_rx_handler  Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {skb} received skb data
+ * @return: none
+ */
+void sdio_host_amt_rx_handler(uint32_t frm_type, struct sk_buff *skb);
+#endif
+
+/**
+ * @brief: sdio_host_rx_handler  Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {env} pointer to the sdio Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+void sdio_host_rx_handler(uint32_t frm_type, struct ipc_host_env_tag *env, struct sk_buff *skb);
+
+/**
+ * @brief: rcv_skb_convert  convert the sdio received skb to the ecrnx handled skb.
+ * @param {src_rxu_state} received rxu state in skb
+ * @param {src_rxu_state} received rx header in skb
+ * @param {src_rxu_state} handled hw rxhdr in skb
+ * @param {src_rxu_state} handled rx desc tag  in skb
+ * @return: u8
+ */
+u8 rcv_skb_convert(struct rxu_stat_mm* src_rxu_state, \
+                struct rx_hd* src_rx_hd, \
+                struct sk_buff *skb, \
+                struct hw_rxhdr* dst_hw_rxhdr,\
+                struct rxdesc_tag* dst_rxdesc_tag);
+
+#endif /* __ECRNX_SDIO_H */
diff --git a/drivers/net/wireless/eswin/sdio/fw.c b/drivers/net/wireless/eswin/sdio/fw.c
new file mode 100644 (file)
index 0000000..9d6bab2
--- /dev/null
@@ -0,0 +1,158 @@
+/**
+******************************************************************************
+*
+* @file fw.c
+*
+* @brief ecrnx sdio firmware download functions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#include <linux/firmware.h>
+#include "core.h"
+#include "sdio.h"
+#include "fw_head_check.h"
+
+extern char *fw_name;
+
+
+void eswin_fw_file_download(struct eswin *tr)
+{
+       int ret;
+       unsigned int length_all;
+       unsigned char length_str[9]={0};
+       unsigned int lengthLeft, lengthSend, offset = HEAD_SIZE;
+       const u8 * dataAddr;
+       struct sk_buff *skb;
+       int file_num = 0;
+       unsigned int file_load_addr[3] = {0x10000U, 0x60800U, 0x80000U};  // ilm addr; dlm addr offset 0x800 for bootrom log; iram0 addr
+
+       char str_sync[4] = {0x63, 0x6E, 0x79, 0x73};            
+       char str_cfg[] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00};  // default for sync
+
+       skb = dev_alloc_skb(1024);
+
+       ECRNX_PRINT("%s entry!!", __func__);
+
+
+       /* 1 sync */
+       memcpy(skb->data, str_sync, 4);
+       tr->ops->write(tr, skb->data, 4);
+       ret = tr->ops->wait_ack(tr);
+       ECRNX_PRINT("dl-fw >> sync, ret: %d\n", ret);
+
+       
+       dataAddr = tr->fw->data;
+       length_all = tr->fw->size - offset;
+       
+       while(length_all)
+       {
+               memcpy(length_str, dataAddr + offset, 8);
+               ECRNX_PRINT("-------------------------------------%s\n", length_str);
+               offset+=8; 
+               length_all-=8;
+               ret = kstrtol(length_str, 10, (long*)&lengthLeft);
+               //ECRNX_PRINT("dl-fw >> file len, ret: %d  len:%d\n", ret, lengthLeft);
+               if(ret==0 && lengthLeft)
+               {
+                       length_all-=lengthLeft;
+
+                       /* 2 cfg addr and length */
+                       str_cfg[4] = (char)((file_load_addr[file_num]) & 0xFF);
+                       str_cfg[5] = (char)(((file_load_addr[file_num])>>8) & 0xFF);
+                       str_cfg[6] = (char)(((file_load_addr[file_num])>>16) & 0xFF);
+                       str_cfg[7] = (char)(((file_load_addr[file_num])>>24) & 0xFF);
+                       file_num++;
+                       str_cfg[8] = (char)((lengthLeft) & 0xFF);
+                       str_cfg[9] = (char)(((lengthLeft)>>8) & 0xFF);
+                       str_cfg[10] = (char)(((lengthLeft)>>16) & 0xFF);
+                       str_cfg[11] = (char)(((lengthLeft)>>24) & 0xFF);
+
+
+                       memcpy(skb->data, &str_cfg[0], 12);
+                       tr->ops->write(tr, skb->data, 12);
+                       ret = tr->ops->wait_ack(tr);
+                       //ECRNX_PRINT("dl-fw >> cfg, ret: %d\n", ret);
+
+
+                       /* 3 load fw */
+                       do {
+                               lengthSend = (lengthLeft >= 1024) ? 1024 : lengthLeft; //ECO3 supprot 64K buff
+                               if(lengthLeft%512==0)
+                               {
+                                       memcpy(skb->data, dataAddr + offset, lengthSend);
+                                       tr->ops->write(tr, skb->data, lengthSend);
+
+                                       //ECRNX_PRINT("dl-fw >> ld(%d), ret: %d\n", offset/1024, ret);
+
+                                       ret = tr->ops->wait_ack(tr);
+                               }
+                               else
+                               {       
+                                       memcpy(skb->data, dataAddr + offset, lengthSend&0xFFFFFE00U);
+                                       tr->ops->write(tr, skb->data, lengthSend&0xFFFFFE00U);
+                                       //ECRNX_PRINT("dl-fw >> ld(%d), ret: %d\n", offset/1024, ret);
+                                       ret = tr->ops->wait_ack(tr);
+                                       
+                                       memcpy(skb->data, dataAddr + offset + (int)(lengthLeft&0xFFFFFE00U), lengthSend&0x1FFU);
+                                       tr->ops->write(tr, skb->data, lengthSend&0x1FFU);
+                                       //ECRNX_PRINT("dl-fw >> ld(%d), ret: %d\n", offset/1024, ret);
+                                       ret = tr->ops->wait_ack(tr);
+                               }
+
+                               //ECRNX_PRINT("dl-fw >> ld-ack(%d), ret: %d\n", offset/1024, ret);
+                               offset += lengthSend;   
+                               lengthLeft -= lengthSend;
+                       } while(lengthLeft);    
+                       //ECRNX_PRINT("dl-fw >> ld, ret: %d\n", ret);
+               }
+       }
+
+       /* 4 start up */
+       memset(skb->data, 0, 12);
+       tr->ops->write(tr, skb->data, 12);
+       tr->ops->wait_ack(tr);
+
+       dev_kfree_skb(skb);
+}
+
+
+
+
+
+bool eswin_fw_file_chech(struct eswin *tr)
+{
+       int status;
+
+       if (fw_name == NULL)
+               goto err_fw;
+
+       if (tr->fw)
+               return true;
+
+       ECRNX_PRINT("%s, Checking firmware... (%s)\n",  __func__, fw_name);
+
+       status = request_firmware((const struct firmware **)&tr->fw, fw_name, tr->dev);
+       if (status != 0) {
+               ECRNX_PRINT("%s, error status = %d\n",  __func__, status);
+               goto err_fw;
+       }
+
+       ECRNX_PRINT("%s, request fw OK and size is %d\n",
+                       __func__, tr->fw->size);
+
+    if(fw_check_head(tr) == false)
+    {
+        goto err_fw;
+    }
+       return true;
+
+
+err_fw:
+       tr->fw = NULL;
+       return false;
+}
+
+
diff --git a/drivers/net/wireless/eswin/sdio/fw.h b/drivers/net/wireless/eswin/sdio/fw.h
new file mode 100644 (file)
index 0000000..f3af4b9
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+******************************************************************************
+*
+* @file fw.h
+*
+* @brief ecrnx sdio firmware download definitions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#ifndef _FW_H_
+#define _FW_H_
+
+void eswin_fw_file_download(struct eswin *tr);
+bool eswin_fw_file_chech(struct eswin *tr);
+
+#endif
diff --git a/drivers/net/wireless/eswin/sdio/sdio.c b/drivers/net/wireless/eswin/sdio/sdio.c
new file mode 100644 (file)
index 0000000..94a8b26
--- /dev/null
@@ -0,0 +1,925 @@
+/**
+ ******************************************************************************
+ *
+ * @file sdio.c
+ *
+ * @brief sdio driver function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include "core.h"
+#include <uapi/linux/sched/types.h>
+//#include "debug.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_sdio.h"
+#include "sdio.h"
+
+#include "sdio_host_interface.h"
+
+#define SDIO_DEBUG     1
+
+
+#define SDIO_ADDR_INFO                 (unsigned int)(0x081)
+#define SDIO_ADDR_INFO_ASYNC           (unsigned int)(0x082)
+#define SDIO_ADDR_DATA                 (unsigned int)(0x080)
+#define NEXT_BUF_SZ_OFFSET                     (0)
+#define SLAVE_BUF_SZ_OFFSET         (4)
+#define SDIO_AVL_NOTIFY_FLAG    (0x5A5A5A5A)
+
+static atomic_t suspend;
+int stop_tx= 0;
+
+#ifdef  CONFIG_SHOW_TX_SPEED
+unsigned long sdio_tx_last_jiffies;
+unsigned long sdio_tx_len_totol;
+unsigned long sdio_tx_error_cnt;
+#endif
+
+#ifdef  CONFIG_SHOW_RX_SPEED
+unsigned long sdio_rx_last_jiffies;
+unsigned long sdio_rx_len_totol;
+unsigned long sdio_rx_error_cnt;
+#endif
+
+
+struct eswin_sdio * g_sdio;
+
+#if SDIO_DEBUG
+static struct eswin_sdio *trS;
+static struct dentry *p_debugfs_sdio;
+
+static int debugfs_sdio_read(void *data, u64 *val)
+{
+       struct eswin_sdio *tr_sdio = trS;
+
+       int pdata[10];
+
+       ECRNX_PRINT("%s\n", __func__);
+       ECRNX_PRINT("%s, func: 0x%x!!", __func__, tr_sdio->func);
+       ECRNX_PRINT("%s, func: 0x%x!!", __func__, tr_sdio->slave_avl_buf);
+       ECRNX_PRINT("%s, data addr: 0x%x, sdio_info: 0x%x, 0x%x !!", __func__, pdata, &tr_sdio->sdio_info, &(tr_sdio->sdio_info));
+#if 0
+       sdio_claim_host(tr_sdio->func);
+    /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+       ret = sdio_readsb(tr_sdio->func, pdata/* &tr_sdio->sdio_info */, SDIO_ADDR_INFO, 4);
+       //ret = 0;
+       if (ret < 0) {
+               ECRNX_PRINT(" debugfs_sdio_read failde, ret: %d\n", ret);
+               //print_hex_dump(KERN_DEBUG, "status - 1 ", DUMP_PREFIX_NONE, 16, 1, &priv->sdio_info, 32, false);
+               sdio_release_host(tr_sdio->func);
+               return ret;
+       }
+       print_hex_dump(KERN_DEBUG, "status - 1 ", DUMP_PREFIX_NONE, 16, 1, pdata, 16, false);
+       sdio_release_host(tr_sdio->func);
+#endif
+       return 0;
+}
+
+static int debugfs_sdio_write(void *data1, u64 val)
+{
+       int ret = 0;
+       //int cmd = (int)val;
+       char cmd[16] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7};
+       struct sk_buff * skb;
+       char * p;
+
+#if 1  
+       struct eswin_sdio *tr_sdio = trS;
+
+       ECRNX_PRINT(" %s, tr_sdio->func: %x\n", __func__, g_sdio->func);
+
+       sdio_claim_host(tr_sdio->func);
+
+    /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+       ret = sdio_writesb(tr_sdio->func, 0x100, &cmd, 16);
+       if (ret < 0) {
+               ECRNX_PRINT(" debugfs_sdio_write failde, ret: %d\n", ret);
+       }
+
+       sdio_release_host(tr_sdio->func);
+#else
+
+       skb = dev_alloc_skb(4096);
+       memset(skb->data, 0x34, 4096);
+
+       p = (char *)(skb->data);
+       for (ret=0; ret<4096; ret++) {
+               *p++ = (char)ret;
+       }
+       
+       sdio_host_send(skb->data, 4096, 0x100);
+#endif
+       return ret;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_sdio,
+                       debugfs_sdio_read,
+                       debugfs_sdio_write,
+                       "%llu\n");
+
+void debugfs_sdio_init(void)
+{
+       ECRNX_PRINT("%s\n", __func__);
+       p_debugfs_sdio = debugfs_create_file("sdiod", 0777, NULL, NULL, &debugfs_sdio);
+}
+
+void debug_sdio_rx_callback(struct sk_buff *skb)
+{
+       ECRNX_PRINT("rx-callback: %d\n", skb->len);
+       print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+       dev_kfree_skb(skb);
+}
+#endif
+
+
+static inline u16 sdio_num_slots(struct eswin_sdio *tr_sdio, int dir)
+{
+       return (tr_sdio->slot[dir].head - tr_sdio->slot[dir].tail);
+}
+
+#if 0
+static void sdio_credit_skb(struct eswin_sdio *tr_sdio)
+{
+       struct sk_buff *skb;
+       struct hif *hif;
+       struct wim *wim;
+       struct wim_credit_report *cr;
+       u8 *p;
+       int i;
+       int size = sizeof(*hif) + sizeof(struct wim) + sizeof(*cr);
+
+       if (!once) {
+               once = true;
+               return;
+       }
+
+       skb = dev_alloc_skb(size);
+
+       p = skb->data;
+       hif = (void *)p;
+       hif->type = HIF_TYPE_WIM;
+       hif->subtype = HIF_WIM_SUB_EVENT;
+       hif->vifindex = 0;
+       hif->len = sizeof(*wim) + sizeof(*cr);
+
+       p += sizeof(*hif);
+       wim = (void *)p;
+       wim->event = WIM_EVENT_CREDIT_REPORT;
+
+       p += sizeof(*wim);
+       cr = (void *)p;
+       cr->h.type = WIM_TLV_AC_CREDIT_REPORT;
+       cr->h.len = sizeof(struct wim_credit_report_param);
+
+       cr->v.change_index = 0;
+
+       for (i = 0; i < CREDIT_QUEUE_MAX; i++) {
+               u8 room = tr_sdio->front[i] - tr_sdio->rear[i];
+
+               room = min(tr_sdio->credit_max[i], room);
+               cr->v.ac[i] = tr_sdio->credit_max[i] - room;
+
+#ifdef CONFIG_NRC_HIF_PRINT_FLOW_CONTROL
+               ECRNX_PRINT(" credit[%d] %d f:%d, r:%d\n",
+                               i, cr->v.ac[i], tr_sdio->front[i],
+                               tr_sdio->rear[i]);
+#endif
+       }
+
+       skb_put(skb, hif->len+sizeof(*hif));
+
+    if (tr_sdio->tr->rx_callback) {
+           tr_sdio->tr->rx_callback(tr_sdio->tr->umac_priv, skb);
+    } else {
+        dev_kfree_skb(skb);
+    }
+}
+#endif
+
+static int sdio_update_status(struct eswin_sdio *tr_sdio)
+{
+       u32 rear;
+       int ac, ret;
+    u8 * buf = kmalloc(4,GFP_ATOMIC);
+
+       //ECRNX_PRINT("%s\n", __func__);
+       sdio_claim_host(tr_sdio->func);
+       trS = tr_sdio;
+    /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+       ret = sdio_readsb(tr_sdio->func, buf, SDIO_ADDR_INFO_ASYNC, 4);
+       if (ret < 0) {
+               ECRNX_PRINT(" sdio_update_status, ret: %d\n", ret);
+        kfree(buf);
+               //print_hex_dump(KERN_DEBUG, "status - 1 ", DUMP_PREFIX_NONE, 16, 1, &priv->sdio_info, 32, false);
+               sdio_release_host(tr_sdio->func);
+               return ret;
+       }
+
+    /*
+    if (slave_avl_buf < tr_sdio->curr_tx_size)
+    {
+        sdio_release_host(tr_sdio->func);
+        return -1;
+    }
+    */
+    spin_lock(&tr_sdio->lock);
+    tr_sdio->slave_avl_buf = *(unsigned int*)buf;
+    spin_unlock(&tr_sdio->lock);
+    kfree(buf);
+       sdio_release_host(tr_sdio->func);
+               
+       return 0;
+}
+
+static void sdio_poll_status(struct work_struct *work)
+{
+       struct eswin_sdio *tr_sdio = container_of(to_delayed_work(work), struct eswin_sdio, work);
+
+       if( sdio_update_status(tr_sdio)) {
+               schedule_delayed_work(&tr_sdio->work, msecs_to_jiffies(1000));
+       } else {
+               wake_up_interruptible(&tr_sdio->wait);
+       }
+}
+
+int continue_transfer = 0;
+
+static struct sk_buff *sdio_rx_skb(struct eswin_sdio *tr_sdio)
+{
+       struct sk_buff *skb = NULL;
+       int ret;
+       int recv_len;
+    unsigned int slave_avl_buf;
+    //unsigned int slave_avl_buf_last;
+       //unsigned int real_length;
+
+       if(tr_sdio->next_rx_size)
+       {
+       //clear next rx buffer size
+       tr_sdio->next_rx_size = 0;
+       }
+    else
+       {
+       /* Wait until at least one rx slot is non-empty */
+       ret = wait_event_interruptible(tr_sdio->wait, (tr_sdio->recv_len > 1 || kthread_should_stop()));
+       if (ret < 0)
+                   goto fail;
+       }
+
+
+
+       if (kthread_should_stop())
+               goto fail;
+
+       recv_len = tr_sdio->recv_len;
+       skb = dev_alloc_skb(recv_len);
+
+       sdio_claim_host(tr_sdio->func);
+
+    /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+       ret = sdio_readsb(tr_sdio->func, skb->data, SDIO_ADDR_DATA, recv_len);
+       if (ret){
+               print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, recv_len, false);
+               ECRNX_PRINT("[eswin-err] rx-len: %d, ret %d\n", recv_len, ret);
+               stop_tx = 1;
+               sdio_release_host(tr_sdio->func);
+               goto fail;
+       }
+
+    //get next rx size
+       tr_sdio->next_rx_size=*(unsigned int *)&skb->data[NEXT_BUF_SZ_OFFSET];
+    if(tr_sdio->next_rx_size > 1)
+    {
+        tr_sdio->recv_len = tr_sdio->next_rx_size;
+    }
+    else
+    {
+        tr_sdio->recv_len = 1;
+    }
+       //get slave avl buf cnt
+    slave_avl_buf = *(unsigned int *)&skb->data[SLAVE_BUF_SZ_OFFSET] >> 16;
+    spin_lock(&tr_sdio->lock);
+    tr_sdio->slave_avl_buf = slave_avl_buf;
+    spin_unlock(&tr_sdio->lock);
+
+    skb_put(skb, recv_len);
+    skb_queue_tail(&tr_sdio->skb_rx_list,skb);
+
+       sdio_release_host(tr_sdio->func);
+
+    if (slave_avl_buf > (SDIO_PKG_MAX_CNT-1))
+    {
+        if (atomic_read(&tr_sdio->slave_buf_suspend))
+        {
+            wake_up_interruptible(&tr_sdio->wait);
+        }
+    }
+
+       //ECRNX_PRINT("rx-len: %d, skb: 0x%x \n", skb->len, skb);
+       return skb;
+
+fail:
+       if (skb)
+               dev_kfree_skb(skb);
+       return NULL;
+}
+
+
+#include <linux/sched.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
+#include <uapi/linux/sched/types.h>
+#endif
+//extern int sched_setscheduler(struct task_struct *, int, const struct sched_param *);
+
+static int sdio_rx_unpack_thread(void *data)
+{
+       struct eswin_sdio *tr_sdio = (struct eswin_sdio *)data;
+       struct eswin *tr = tr_sdio->tr;
+       struct sk_buff *skb;
+       struct sk_buff *skb_frag;
+       struct sdio_rx_head_t * rx_head;
+       int ret;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+               struct sched_param param = { .sched_priority = 1 };
+               param.sched_priority = 56;
+               sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+               sched_set_fifo(get_current());
+#endif
+               ECRNX_PRINT("rx unpack thread entry\n");
+
+       while (!kthread_should_stop())
+       {
+               ret = wait_event_interruptible(tr_sdio->wait_unpack, skb_peek(&tr_sdio->skb_rx_list) 
+                       || kthread_should_stop() );
+               if(kthread_should_stop())
+                       continue;
+               if (ret < 0)
+               {
+                       ECRNX_ERR("rx unpack thread error!\n");
+                       return 0;
+               }
+               while(NULL != (skb = skb_dequeue(&tr_sdio->skb_rx_list)))
+               {
+                       while (skb->len > 8)//valid data must contain 8 byte head
+                       {
+                               rx_head = (struct sdio_rx_head_t *)skb->data;
+                               if(rx_head->data_len == 0)
+                                       break;
+                               if (*(unsigned int *)&skb->data[8] == SDIO_AVL_NOTIFY_FLAG && rx_head->data_len == 12)
+                               {
+                                       skb_pull(skb, ALIGN(rx_head->data_len, 4));
+                                       continue;
+                               }
+                               skb_frag = dev_alloc_skb(rx_head->data_len);
+                               memcpy(skb_frag->data,skb->data,rx_head->data_len);
+                               skb_put(skb_frag,rx_head->data_len);
+
+                               if (skb_frag->len > skb->len)
+                               {
+                                       ECRNX_ERR("skb len error!!!  frag len:%d skb->len:%d\n",skb_frag->len,skb->len);
+                                       print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+                                       BUG_ON(1);
+                               }
+                               tr->rx_callback(tr->umac_priv, skb_frag);
+                               skb_pull(skb,ALIGN(rx_head->data_len, 4));
+                       }
+                       dev_kfree_skb(skb);
+               }
+       }
+       ECRNX_PRINT("rx unpack thread exit\n");
+       return 0;
+}
+
+static int sdio_rx_thread(void *data)
+{
+       struct eswin_sdio *tr_sdio = (struct eswin_sdio *)data;
+       struct eswin *tr = tr_sdio->tr;
+       struct sk_buff *skb;
+       struct sk_buff *skb_frag;
+       struct sdio_rx_head_t * rx_head;
+       int is_suspend;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+       struct sched_param param = { .sched_priority = 1 };
+       param.sched_priority = 56;
+       sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+       sched_set_fifo(get_current());
+#endif
+       ECRNX_PRINT("rx thread entry, loopbakc: %d\n", tr->loopback);
+
+       while (!kthread_should_stop())
+       {
+               skb = sdio_rx_skb(tr_sdio);
+               is_suspend = atomic_read(&suspend);
+               ECRNX_DBG("rx_cb: 0x%x, skb: 0x%x, is_suspend: 0x%x \n", tr->rx_callback, skb, is_suspend);
+               if ((!tr->rx_callback) || (skb && is_suspend)){
+                       dev_kfree_skb(skb);
+               }
+               else if (skb){
+                       wake_up_interruptible(&tr_sdio->wait_unpack);
+#if 0
+                       while (skb->len > 8)//valid data must contain 8 byte head
+                               {
+                               //printk("skb len 0:%d",skb->len);
+                               rx_head = (struct sdio_rx_head_t *)skb->data;
+                               if(rx_head->data_len == 0)
+                                       break;
+                               skb_frag = dev_alloc_skb(rx_head->data_len);
+                               memcpy(skb_frag->data,skb->data,rx_head->data_len);
+                               skb_put(skb_frag,rx_head->data_len);
+                               //printk("skb_frag len 1:%d",skb_frag->len);
+
+                               if (skb_frag->len > skb->len)
+                                       {
+                                       printk("skb len error!!!  frag len:%d skb->len:%d\n",skb_frag->len,skb->len);
+                                       print_hex_dump(KERN_DEBUG, "eswin-rx: ", DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+                                       BUG_ON(1);
+                                       }
+
+                               tr->rx_callback(tr->umac_priv, skb_frag);
+                               skb_pull(skb,ALIGN(rx_head->data_len, 4));
+                               //printk("skb len 1:%d",skb->len);
+                               //break;
+
+                               }
+                       dev_kfree_skb(skb);
+                       //dev_alloc_skb
+                       //tr->rx_callback(tr->umac_priv, skb);
+                       //debug_sdio_rx_callback(skb);
+#endif
+               }
+               else {
+                       ECRNX_ERR("rx-head: %d, rx-tail: %d, rx-cnt: %d\n",
+                                       tr_sdio->slot[RX_SLOT].head, tr_sdio->slot[RX_SLOT].tail, tr_sdio->sdio_info.info_rd);
+                       break;
+               }
+       }
+       ECRNX_PRINT("rx thread exit\n");
+
+       return 0;
+}
+
+#if 0
+static int sdio_noop(struct eswin *tr, struct sk_buff *skb)
+{
+       ECRNX_PRINT("%s exit!!", __func__);
+       return 0;
+}
+#endif
+
+void dump_sdio_buf(struct tx_buff_node *node)
+{
+       int i = 0;
+
+       ECRNX_PRINT("sdio tx len %d\n", node?node->len:-1);
+       /*for (i = 0; i < node->len; ++i) {
+               if (i % 16 == 0 && i) {
+                       printk("\n");
+               }
+               printk("%02x ", ((unsigned char *)(node->buff))[i]);
+       }*/
+
+}
+
+static int sdio_xmit(struct eswin *tr, struct tx_buff_pkg_node *node)
+{
+       struct eswin_sdio *tr_sdio   = (struct eswin_sdio *)tr->drv_priv;
+       struct eswin * tr1 = tr_sdio->tr;
+       int ret,flag;
+       unsigned int slave_avl_buf = 0;
+
+       //dump_sdio_buf(node);
+    spin_lock(&tr_sdio->lock);
+    slave_avl_buf = tr_sdio->slave_avl_buf;
+    spin_unlock(&tr_sdio->lock);
+    if(slave_avl_buf < node->node_cnt && (node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+    {
+        atomic_set(&tr_sdio->slave_buf_suspend, 1);
+        ret = wait_event_interruptible(tr_sdio->wait, node->node_cnt < tr_sdio->slave_avl_buf);
+        if (ret < 0) 
+        {
+            ECRNX_PRINT("[transa] sdio_xmit, wait_event_interruptible fail, ret = %d slave_avl_buf=%d\n", ret, tr_sdio->slave_avl_buf);
+            return ret;
+        }
+        cancel_delayed_work_sync(&tr_sdio->work);      
+        atomic_set(&tr_sdio->slave_buf_suspend, 0);
+    }
+       sdio_claim_host(tr_sdio->func);
+    /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+       ret = sdio_writesb(tr_sdio->func, node->flag, node->buff, node->len);
+       if(ret) {
+               //stop_tx = 1;
+               ECRNX_ERR("sdio_xmit error, len = %d, ret:%d slave_avl_buf=%d ,node->node_cnt :%d\n",\
+                       node->len, ret, tr_sdio->slave_avl_buf,node->node_cnt);
+               print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_TX, DUMP_PREFIX_NONE, 32, 1,
+                              node->buff, 64, false);
+       }
+
+    spin_lock(&tr_sdio->lock);
+    if ((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+    {
+        tr_sdio->slave_avl_buf -= node->node_cnt;
+    }
+    spin_unlock(&tr_sdio->lock);
+
+    sdio_release_host(tr_sdio->func);
+    ECRNX_DBG(" sdio_xmit ok, len = %d, flag: 0x%02x!\n", node->len, node->flag);
+    return ret;
+}
+
+static int sdio_start(struct eswin *tr)
+{
+       int ret=0;
+
+       struct eswin_sdio *tr_sdio   = (struct eswin_sdio *)tr->drv_priv;
+
+       ECRNX_PRINT("%s\n", __func__);
+
+       /* Start rx thread */
+       //if(tr->loopback == 1)
+       //      return 0;
+       tr_sdio->curr_tx_size = 0;
+       ret = sdio_update_status(tr_sdio);
+       INIT_DELAYED_WORK(&tr_sdio->work, sdio_poll_status);
+
+       tr_sdio->kthread = kthread_run(sdio_rx_thread, tr_sdio, "sdio-rx");
+       tr_sdio->kthread_unpack = kthread_run(sdio_rx_unpack_thread, tr_sdio, "sdio-rx-unpack");
+
+       atomic_set(&suspend, 0);
+       atomic_set(&tr_sdio->slave_buf_suspend, 0);
+    spin_lock_init(&tr_sdio->lock);
+
+       return ret;
+}
+
+
+static int sdio_suspend(struct eswin *tr)
+{
+       atomic_set(&suspend, 1);
+       return 0;
+}
+
+static int sdio_resume(struct eswin *tr)
+{
+       atomic_set(&suspend, 0);
+       return 0;
+}
+
+
+static int sdio_raw_write(struct eswin *tr,    const void *data, const u32 len)
+{
+       int ret;
+       struct eswin_sdio *tr_sdio   = (struct eswin_sdio *)tr->drv_priv;
+
+       ECRNX_PRINT(" %s, entry~", __func__);
+
+       sdio_claim_host(tr_sdio->func);
+    /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+       ret = sdio_writesb(tr_sdio->func, SDIO_ADDR_DATA, data,  len);
+       sdio_release_host(tr_sdio->func);
+
+       return ret;
+}
+
+               
+static int sdio_wait_ack(struct eswin *tr)
+{
+       int data = 0;
+    u8 * buf = kmalloc(4,GFP_ATOMIC);
+       struct eswin_sdio *tr_sdio   = (struct eswin_sdio *)tr->drv_priv;
+
+       ECRNX_PRINT(" %s, entry~", __func__);
+
+       sdio_claim_host(tr_sdio->func);
+    /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+       sdio_readsb(tr_sdio->func, buf, SDIO_ADDR_DATA, 1);
+       sdio_release_host(tr_sdio->func);
+    data = *(int *)buf;
+    kfree(buf);
+
+       return data;
+}
+
+
+
+static struct sdio_ops eswin_sdio_ops = {
+       .start      = sdio_start,
+       .xmit       = sdio_xmit,
+       .suspend    = sdio_suspend,
+       .resume     = sdio_resume,
+       .write      = sdio_raw_write,
+       .wait_ack       = sdio_wait_ack,
+};
+
+
+
+static void eswin_sdio_irq_handler(struct sdio_func *func)
+{
+       struct eswin_sdio *tr_sdio = sdio_get_drvdata(func);
+       u32 rear;       
+       unsigned char lowbyte, highbyte;
+       int ret, ac;
+
+       //printk(" %s, entry~\n", __func__);
+
+       sdio_claim_host(tr_sdio->func);
+       lowbyte  = sdio_readb(tr_sdio->func, 0x00, &ret);
+       highbyte = sdio_readb(tr_sdio->func, 0x01, &ret);
+       tr_sdio->recv_len  = (highbyte << 8) | lowbyte;
+       //printk("%s %u, %hhu, %hhu!", __func__, tr_sdio->recv_len, highbyte, lowbyte);
+
+#if 0
+       //_info(" eswin_sdio_irq_handler, len: %d\n", tr_sdio->recv_len);
+       if(tr_sdio->recv_len == 1) {
+       
+               ret = sdio_memcpy_fromio(tr_sdio->func, &tr_sdio->sdio_info, SDIO_ADDR_INFO, 0x10 /*priv->recv_len*/);
+               if (ret < 0) {
+                       ECRNX_PRINT(" eswin_sdio_irq_handler, info-ret: %d\n", ret);
+                       //print_hex_dump(KERN_DEBUG, "status - 2 ", DUMP_PREFIX_NONE, 16, 1, &priv->sdio_info, 32, false);
+                       sdio_release_host(tr_sdio->func);
+                       return;
+               }
+               //ECRNX_PRINT(" get info\n");
+               //ECRNX_PRINT(" eswin_sdio_irq_handler, info-wr: %#x, info-rd: %#x\n", priv->sdio_info.info_wr, priv->sdio_info.info_rd);
+
+               tr_sdio->slot[TX_SLOT].head = tr_sdio->sdio_info.info_wr;
+               tr_sdio->slot[RX_SLOT].head = tr_sdio->sdio_info.info_rd;
+
+
+               //if (hdev->nw->loopback)
+               //      return;
+
+               if((tr_sdio->sdio_info.credit_vif0 != tr_sdio->credit_vif0) 
+                       ||(tr_sdio->sdio_info.credit_vif1 != tr_sdio->credit_vif1)){
+                       /* Update VIF0 credit */
+                       rear = tr_sdio->sdio_info.credit_vif0;
+                       tr_sdio->credit_vif0 = rear;
+                       for (ac = 0; ac < 4; ac++)
+                               tr_sdio->rear[ac] = (rear >> 8*ac) & 0xff;
+
+
+                       /* Update VIF1 credit */
+                       rear = tr_sdio->sdio_info.credit_vif1;
+                       tr_sdio->credit_vif1 = rear;
+                       for (ac = 0; ac < 4; ac++)
+                               tr_sdio->rear[6+ac] = (rear >> 8*ac) & 0xff;
+
+                       //need_credit = 1;
+                       //sdio_release_host(tr_sdio->func);
+                       //sdio_credit_skb(tr_sdio);
+                       //sdio_claim_host(func);
+               }
+#if 0
+       printk("irq: wr: %d, rd: %d, credit:%d/%d, %d/%d, %d/%d, %d/%d\n", 
+                       priv->sdio_info.info_wr, priv->sdio_info.info_rd,
+                       priv->rear[3], priv->front[3], 
+                       priv->rear[2], priv->front[2], 
+                       priv->rear[1], priv->front[1], 
+                       priv->rear[0], priv->front[0]);
+#endif
+
+#ifdef CONFIG_NRC_HIF_PRINT_FLOW_CONTROL
+               //nrc_dbg(NRC_DBG_HIF, "-%s\n", __func__);
+#endif
+
+       }
+       else
+       {
+               //ECRNX_PRINT(" get data len: %d\n", priv->recv_len);
+       }
+#endif
+       sdio_release_host(tr_sdio->func);
+       wake_up_interruptible(&tr_sdio->wait);
+}
+
+static void eswin_sdio_irq2_handler(struct sdio_func *func)
+{
+       struct eswin_sdio *tr_sdio = g_sdio;
+       unsigned char lowbyte, highbyte;
+       int ret, recv_len;
+       struct sk_buff * skb;
+
+       ECRNX_PRINT(" %s, entry~\n", __func__);
+       
+       sdio_claim_host(func);
+       lowbyte  = sdio_readb(func, 0x00, &ret);
+       highbyte = sdio_readb(func, 0x01, &ret);
+       sdio_release_host(func);
+
+       recv_len  = (highbyte << 8) | lowbyte;
+       skb = dev_alloc_skb(recv_len);
+       skb_put(skb, recv_len);
+
+       sdio_claim_host(func);
+    /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+       ret = sdio_readsb(func, skb->data, SDIO_ADDR_DATA, recv_len);
+       if (ret){
+               ECRNX_ERR("[eswin-err] rx-len: %d\n", skb->len);
+       }
+
+       sdio_release_host(func);
+
+       print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+       dev_kfree_skb(skb);
+}
+
+struct device *eswin_sdio_get_dev(void *plat)
+{
+    struct eswin* tr = (struct eswin*)plat;
+    
+    return tr->dev;
+}
+
+
+static int eswin_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
+{
+       int ret;
+       struct eswin_sdio * tr_sdio;
+       struct eswin* tr;
+
+       ECRNX_PRINT("%s entry, func: %d!!", __func__, func->num);
+
+       if (func->num == 1) {
+               tr = eswin_core_create(sizeof(* tr_sdio), &func->dev, &eswin_sdio_ops);
+               if(!tr) {
+                       dev_err(&func->dev, "failed to allocate core\n");
+                       return -ENOMEM;
+               }
+
+               tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+               g_sdio = tr_sdio;
+               tr_sdio->tr = tr;
+               tr_sdio->func = func;
+       } else {
+               g_sdio->func2 = func;
+               
+               sdio_claim_host(func);
+               sdio_enable_func(func);
+               func->max_blksize = ESWIN_SDIO_BLK_SIZE;
+               sdio_set_block_size(func, func->max_blksize);
+               sdio_claim_irq(func, eswin_sdio_irq2_handler);
+               sdio_release_host(func);
+               return 0;
+       }
+
+       tr_sdio->slot[TX_SLOT].size = 456;
+       tr_sdio->slot[RX_SLOT].size = 492;
+
+       tr_sdio->credit_max[0] = CREDIT_AC0;
+       tr_sdio->credit_max[1] = CREDIT_AC1;
+       tr_sdio->credit_max[2] = CREDIT_AC2;
+       tr_sdio->credit_max[3] = CREDIT_AC3;
+
+       tr_sdio->credit_max[6] = CREDIT_AC0;
+       tr_sdio->credit_max[7] = CREDIT_AC1;
+       tr_sdio->credit_max[8] = CREDIT_AC2;
+       tr_sdio->credit_max[9] = CREDIT_AC3;
+
+       init_waitqueue_head(&tr_sdio->wait);
+       init_waitqueue_head(&tr_sdio->wait_unpack);
+
+       func->max_blksize = ESWIN_SDIO_BLK_SIZE;
+
+       skb_queue_head_init(&tr_sdio->skb_rx_list);
+       //skb_queue_head_init(tr_sdio->skb_rx_unpack_list);
+
+       sdio_claim_host(func);
+
+       ret = sdio_enable_func(func);
+       if(ret) {
+               ECRNX_PRINT("failed to enable sdio func\n");
+               goto release;
+       }
+       
+       ret = sdio_set_block_size(func, func->max_blksize);
+       if(ret) {
+               ECRNX_PRINT("failed to set sdio func block size\n");
+               goto release;
+       }
+
+       ret = sdio_claim_irq(func, eswin_sdio_irq_handler);
+       if(ret) {
+               ECRNX_PRINT("failed to claim sdio irq\n");
+               goto release;
+       }
+
+       sdio_release_host(func);
+       sdio_set_drvdata(func, tr_sdio);
+
+#ifdef CONFIG_SHOW_TX_SPEED
+       sdio_tx_last_jiffies = jiffies;
+       sdio_tx_len_totol = 0;
+       sdio_tx_error_cnt = 0;
+#endif
+
+#ifdef CONFIG_SHOW_RX_SPEED
+       sdio_rx_last_jiffies = jiffies;
+       sdio_rx_len_totol = 0;
+       sdio_rx_error_cnt = 0;
+#endif
+
+       
+       ret = eswin_core_register(tr);
+       if(ret) {
+               ECRNX_PRINT("failed to register core\n");
+
+       }
+
+       debugfs_sdio_init();
+
+       ECRNX_PRINT("%s exit!!", __func__);
+       return 0;
+
+release:
+       sdio_release_host(func);
+       return ret;
+}
+
+static void eswin_sdio_remove(struct sdio_func *func)
+{
+    struct eswin_sdio *tr_sdio = sdio_get_drvdata(func);
+    struct eswin *tr = tr_sdio->tr;
+
+
+    ECRNX_PRINT(" %s entry!!\n", __func__);
+    debugfs_remove_recursive(p_debugfs_sdio);
+
+    eswin_core_unregister(tr);
+
+    sdio_claim_host(func);
+    sdio_release_irq(func);
+    sdio_disable_func(func);
+    sdio_release_host(func);
+
+       kthread_stop(tr_sdio->kthread);
+       wake_up_interruptible(&tr_sdio->wait);
+       kthread_stop(tr_sdio->kthread_unpack);
+       wake_up_interruptible(&tr_sdio->wait_unpack);
+
+    eswin_core_destroy(tr); 
+    ECRNX_PRINT(" %s exit!!\n", __func__);
+}
+
+
+static const struct sdio_device_id eswin_sdio_dev[] =
+{
+       { SDIO_DEVICE(ESWIN_SDIO_VENDER, ESWIN_SDIO_DEVICE) },
+       {},
+};
+
+static struct sdio_driver eswin_sdio_driver =
+{
+       .name     = "eswin_sdio",
+       .id_table = eswin_sdio_dev,
+       .probe    = eswin_sdio_probe,
+       .remove   = eswin_sdio_remove,
+};
+
+static int __init eswin_sdio_init(void)
+{
+       int ret;
+       ECRNX_PRINT(" %s entry!!", __func__);
+
+       ret = sdio_register_driver(&eswin_sdio_driver);
+       if (ret)
+               ECRNX_PRINT("sdio driver registration failed: %d\n", ret);
+
+       ECRNX_PRINT(" %s exit!!", __func__);
+       return ret;
+}
+
+static void __exit eswin_sdio_exit(void)
+{
+       ECRNX_PRINT(" %s entry!!", __func__);
+       sdio_unregister_driver(&eswin_sdio_driver);
+       ECRNX_PRINT(" %s exit!!", __func__);
+}
+
+int ecrnx_sdio_register_drv(void)
+{
+    return eswin_sdio_init();
+}
+
+void ecrnx_sdio_unregister_drv(void)
+{
+    return eswin_sdio_exit();
+}
+
+//module_init(eswin_sdio_init);
+//module_exit(eswin_sdio_exit);
+
+//MODULE_AUTHOR("Transa-Semi");
+//MODULE_LICENSE("Dual BSD/GPL");
+//MODULE_DESCRIPTION("Driver support for Transa-Semi 802.11 WLAN SDIO driver");
+//MODULE_SUPPORTED_DEVICE("Transa-Semi 802.11 devices");
+
diff --git a/drivers/net/wireless/eswin/sdio/sdio.h b/drivers/net/wireless/eswin/sdio/sdio.h
new file mode 100644 (file)
index 0000000..6dde7c1
--- /dev/null
@@ -0,0 +1,156 @@
+/**
+ ******************************************************************************
+ *
+ * @file sdio.h
+ *
+ * @brief sdio driver definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef __SDIO_H
+#define __SDIO_H
+
+#include "ecrnx_defs.h"
+#include "core.h"
+
+#define        ESWIN_SDIO_VENDER       0x0296
+#define        ESWIN_SDIO_DEVICE       0x5347
+
+#define        ESWIN_SDIO_BLK_SIZE     512
+
+#define TX_SLOT 0
+#define RX_SLOT 1
+
+#define CREDIT_QUEUE_MAX (12)
+
+
+#define TCN  (3*2)
+#define TCNE (0)
+
+#define CREDIT_AC0     4//(TCN*4+TCNE) /* BK */
+#define CREDIT_AC1     30//(TCN*3+TCNE)        /* BE */
+#define CREDIT_AC2     4//(TCN*2+TCNE) /* VI */
+#define CREDIT_AC3     4//(TCN*1+TCNE) /* VO */
+
+struct sdio_sys_reg {
+       u8 wakeup;      /* 0x0 */
+       u8 status;      /* 0x1 */
+       u16 chip_id;    /* 0x2-0x3 */
+       u32 modem_id;   /* 0x4-0x7 */
+       u32 sw_id;      /* 0x8-0xb */
+       u32 board_id;   /* 0xc-0xf */
+} __packed;
+
+struct sdio_status_reg {
+       struct {
+               u8 mode;
+               u8 enable;
+               u8 latched_status;
+               u8 status;
+       } eirq;
+       u8 txq_status[6];
+       u8 rxq_status[6];
+       u32 msg[4];
+
+#define EIRQ_IO_ENABLE (1<<2)
+#define EIRQ_EDGE      (1<<1)
+#define EIRQ_ACTIVE_LO (1<<0)
+
+#define EIRQ_DEV_SLEEP (1<<3)
+#define EIRQ_DEV_READY (1<<2)
+#define EIRQ_RXQ       (1<<1)
+#define EIRQ_TXQ       (1<<0)
+
+#define TXQ_ERROR      (1<<7)
+#define TXQ_SLOT_COUNT (0x7F)
+#define RXQ_SLOT_COUNT (0x7F)
+
+} __packed;
+
+struct sdio_rx_head_t {
+       unsigned int      next_rx_len;
+       unsigned short    data_len;
+       unsigned short    avl_len;
+};
+
+struct sdio_data_t {
+       unsigned int      credit_vif0;
+       unsigned int      credit_vif1;
+       unsigned int      info_wr;
+       unsigned int      info_rd;
+};
+
+struct eswin_sdio {
+       struct eswin * tr;
+       struct sdio_func   *func;
+       struct sdio_func   *func2;
+
+       /* work, kthread, ... */
+       struct delayed_work work;
+       struct task_struct *kthread;
+       wait_queue_head_t wait; /* wait queue */
+
+       struct task_struct *kthread_unpack;
+       wait_queue_head_t wait_unpack;
+
+       struct {
+               struct sdio_sys_reg    sys;
+               struct sdio_status_reg status;
+       } hw;
+
+       spinlock_t lock;
+       struct {
+               unsigned int head;
+               unsigned int tail;
+               unsigned int size;
+               unsigned int count;
+       } slot[2];
+       /* VIF0(AC0~AC3), BCN, CONC, VIF1(AC0~AC3), padding*/
+       u8 front[CREDIT_QUEUE_MAX];
+       u8 rear[CREDIT_QUEUE_MAX];
+       u8 credit_max[CREDIT_QUEUE_MAX];
+
+/*
+       unsigned long loopback_prev_cnt;
+       unsigned long loopback_total_cnt;
+       unsigned long loopback_last_jiffies;
+       unsigned long loopback_read_usec;
+       unsigned long loopback_write_usec;
+       unsigned long loopback_measure_cnt;
+*/
+       //struct eswin_sdio_ops_t *ops;
+       unsigned int              recv_len;
+       unsigned int              recv_num;
+//     struct dentry *debugfs;
+
+       unsigned int      credit_vif0;
+       unsigned int      credit_vif1;
+       
+       struct sdio_data_t sdio_info;
+       unsigned int      slave_avl_buf;
+    atomic_t          slave_buf_suspend;
+       unsigned int      curr_tx_size;
+       unsigned int      next_rx_size;
+//     struct sk_buff  *skb_tx_last;
+
+       struct sk_buff_head skb_rx_list;
+       //struct sk_buff_head *skb_rx_unpack_list;
+
+};
+
+struct sdio_ops {
+       int (*start)(struct eswin *tr);
+       int (*xmit)(struct eswin *tr, struct tx_buff_pkg_node * node);
+       int (*suspend)(struct eswin *tr);
+       int (*resume)(struct eswin *tr);
+       int (*write)(struct eswin *tr, const void* data, const u32 len);
+       int (*wait_ack)(struct eswin *tr);
+};
+
+extern int ecrnx_sdio_register_drv(void);
+extern void ecrnx_sdio_unregister_drv(void);
+
+#endif /* __SDIO_H */
diff --git a/drivers/net/wireless/eswin/sdio/sdio_host_interface.h b/drivers/net/wireless/eswin/sdio/sdio_host_interface.h
new file mode 100644 (file)
index 0000000..1460479
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ ******************************************************************************
+ *
+ * @file sdio_host_interface.h
+ *
+ * @brief sdio host interface definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#ifndef _SDIO_HOST_INTERFACE_H
+#define _SDIO_HOST_INTERFACE_H
+/*******************************************************************************
+ * Function: sdio_xmit
+ * Description:send buff from host to slave
+ * Parameters: 
+ *   Input: void *buff, int len, int flag
+ *
+ *   Output:
+ *
+ * Returns: 0
+ *
+ *
+ * Others: 
+ ********************************************************************************/
+int sdio_host_send(void *buff, int len, int flag);
+#endif
\ No newline at end of file
diff --git a/drivers/net/wireless/eswin/slave_log_buf.c b/drivers/net/wireless/eswin/slave_log_buf.c
new file mode 100644 (file)
index 0000000..99009e4
--- /dev/null
@@ -0,0 +1,149 @@
+#include "slave_log_buf.h"
+
+spinlock_t lock;
+
+uint32_t ring_buffer_init(struct ring_buffer* ring_buf, uint32_t size)
+{
+    if(ring_buf== NULL) 
+        return false;
+
+    if (!is_power_of_2(size))
+    {
+        printk("size must be power of 2.\n");
+        return false;
+    }
+
+    memset(ring_buf, 0, sizeof(struct ring_buffer));
+    spin_lock_init(&lock);
+    ring_buf->buffer = kmalloc(size,GFP_KERNEL);
+    ring_buf->size = size;
+    ring_buf->write_point = 0;
+    ring_buf->read_point = 0;
+    ring_buf->f_lock = &lock;
+    ring_buf->init = true;
+    ring_buf->cover = false;
+    ring_buf->show = false;
+    return true;
+}
+
+void ring_buffer_deinit(struct ring_buffer *ring_buf)
+{
+    memset(ring_buf, 0, sizeof(struct ring_buffer));
+    if(ring_buf->buffer != NULL)
+    {
+        kfree(ring_buf->buffer);
+        ring_buf->buffer = NULL;
+    }
+
+}
+
+uint32_t __ring_buffer_len(const struct ring_buffer *ring_buf)
+{
+    if(ring_buf->cover == false)
+    {
+        return ring_buf->write_point;
+    }
+    if(ring_buf->show == true)
+    {
+        if(ring_buf->write_point < ring_buf->read_point)
+            return (ring_buf->write_point + ring_buf->size - ring_buf->read_point);
+        else
+            return (ring_buf->write_point - ring_buf->read_point);
+    }
+    
+    return  ring_buf->size;
+}
+
+uint32_t __ring_buffer_get(struct ring_buffer *ring_buf, void * buffer, uint32_t size)
+{
+    if((ring_buf== NULL) || (buffer== NULL))
+        return 0;
+
+    uint32_t copy_len = 0;
+    uint32_t read_len = 0;
+    if(ring_buf->write_point < ring_buf->read_point)
+        read_len = (ring_buf->write_point + ring_buf->size - ring_buf->read_point);
+    else
+        read_len = (ring_buf->write_point - ring_buf->read_point);
+
+    size  = min(size, read_len);        
+    /* first get the data from fifo->read_point until the end of the buffer */
+    copy_len = min(size, ring_buf->size - ring_buf->read_point);
+    memcpy(buffer, ring_buf->buffer + ring_buf->read_point, copy_len);
+    /* then get the rest (if any) from the beginning of the buffer */
+    if(size - copy_len > 0)
+    {
+        memcpy(buffer + copy_len, ring_buf->buffer, size - copy_len);
+    }
+
+    ring_buf->read_point += size;
+    ring_buf->read_point = (ring_buf->read_point & (ring_buf->size - 1));
+
+    return size;
+}
+//Ïò»º³åÇøÖдæ·ÅÊý¾Ý
+uint32_t __ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
+{
+    if((ring_buf == NULL) || (buffer == NULL))
+    {
+        return 0;
+    }
+    uint32_t copy_len = 0;
+
+    /* first put the data starting from fifo->write_point to buffer end */
+    copy_len  = min(size, ring_buf->size - ring_buf->write_point);
+
+    memcpy(ring_buf->buffer + ring_buf->write_point, buffer, copy_len);
+    /* then put the rest (if any) at the beginning of the buffer */
+    if(size - copy_len > 0)
+    {
+        memcpy(ring_buf->buffer, buffer + copy_len, size - copy_len);
+        ring_buf->cover = true;
+    }
+
+    ring_buf->write_point += size;
+    ring_buf->write_point = (ring_buf->write_point & (ring_buf->size - 1));
+    return size;
+}
+uint32_t ring_buffer_len(const struct ring_buffer *ring_buf)
+{
+    uint32_t len = 0;
+    spin_lock_irq(ring_buf->f_lock);
+    len = __ring_buffer_len(ring_buf);
+    spin_unlock_irq(ring_buf->f_lock);
+    return len;
+}
+uint32_t ring_buffer_get(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
+{
+    uint32_t ret;
+    spin_lock_irq(ring_buf->f_lock);
+    ret = __ring_buffer_get(ring_buf, buffer, size);
+    spin_unlock_irq(ring_buf->f_lock);
+    return ret;
+}
+uint32_t ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size)
+{
+    uint32_t ret;
+    spin_lock_irq(ring_buf->f_lock);
+    ret = __ring_buffer_put(ring_buf, buffer, size);
+    spin_unlock_irq(ring_buf->f_lock);
+    return ret;
+}
+uint32_t ring_buffer_scrolling_display(struct ring_buffer *ring_buf, char show)
+{
+    uint32_t ret = true;
+    spin_lock_irq(ring_buf->f_lock);
+    ring_buf->show = show;
+    if((ring_buf->cover == true)&&(show == true))
+    {
+        ring_buf->read_point = (ring_buf->write_point & (ring_buf->size - 1)) + 1;
+    }
+    spin_unlock_irq(ring_buf->f_lock);
+    return ret;
+}
+
diff --git a/drivers/net/wireless/eswin/slave_log_buf.h b/drivers/net/wireless/eswin/slave_log_buf.h
new file mode 100644 (file)
index 0000000..bd756e7
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef _SLAVE_LOG_BUF_H_
+#define _SLAVE_LOG_BUF_H_
+#include <linux/firmware.h>
+#include "core.h"
+#include "ecrnx_debug.h"
+
+
+#ifndef uint32_t
+#define uint32_t unsigned int
+#endif
+
+
+#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
+
+//#define min(a, b) (((a) < (b)) ? (a) : (b))
+struct ring_buffer
+{
+    void            *buffer;
+    uint32_t        size;
+    uint32_t        write_point;
+    uint32_t        read_point;
+    char            cover; 
+    char            show;
+    char            init; 
+    spinlock_t     *f_lock;
+};
+
+uint32_t ring_buffer_get(struct ring_buffer *ring_buf, void *buffer, uint32_t size);
+uint32_t ring_buffer_put(struct ring_buffer *ring_buf, void *buffer, uint32_t size);
+uint32_t ring_buffer_init(struct ring_buffer* ring_buf, uint32_t size);
+void ring_buffer_deinit(struct ring_buffer *ring_buf);
+uint32_t ring_buffer_scrolling_display(struct ring_buffer *ring_buf, char show);
+uint32_t ring_buffer_len(const struct ring_buffer *ring_buf);
+
+
+#endif
diff --git a/drivers/net/wireless/eswin/usb/core.c b/drivers/net/wireless/eswin/usb/core.c
new file mode 100644 (file)
index 0000000..a778105
--- /dev/null
@@ -0,0 +1,212 @@
+#include <linux/firmware.h>\r
+#include "core.h"\r
+#include "fw.h"\r
+//#include "debug.h"\r
+#include "ecrnx_platform.h"\r
+#include "usb.h"\r
+#include "ecrnx_rx.h"\r
+#include "eswin_utils.h"\r
+#include "ecrnx_defs.h"\r
+#include "usb_host_interface.h"\r
+\r
+bool loopback;\r
+module_param(loopback, bool, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(loopback, "HIF loopback");\r
+\r
+int power_save;\r
+module_param(power_save, int, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(power_save, "Power Save(0: disable, 1:enable)");\r
+\r
+int disable_cqm = 0;\r
+module_param(disable_cqm, int, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(disable_cqm, "Disable CQM (0: disable, 1:enable)");\r
+\r
+\r
+int listen_interval = 0;\r
+module_param(listen_interval, int, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(listen_interval, "Listen Interval");\r
+\r
+int bss_max_idle = 0;\r
+module_param(bss_max_idle, int, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(bss_max_idle, "BSS Max Idle");\r
+\r
+\r
+bool dl_fw = true;\r
+module_param(dl_fw, bool, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(dl_fw, "download firmware");\r
+\r
+\r
+#ifdef CONFIG_ECRNX_WIFO_CAIL\r
+bool amt_mode;\r
+module_param(amt_mode, bool, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(amt_mode, "calibrate mode");\r
+#endif\r
+\r
+bool set_gain;\r
+module_param(set_gain, bool, S_IRUSR | S_IWUSR);\r
+MODULE_PARM_DESC(set_gain, "set gain delta");\r
+\r
+char *fw_name = NULL;\r
+struct eswin *pEswin = NULL;\r
+bool usb_status = false;\r
+\r
+module_param(fw_name, charp, S_IRUGO);\r
+MODULE_PARM_DESC(fw_name, "Firmware file name");\r
+\r
+extern int ecrnx_data_cfm_callback(void *priv, void *host_id);\r
+extern int ecrnx_msg_cfm_callback(void *priv, void *host_id);\r
+static void eswin_core_register_work(struct work_struct *work)\r
+{\r
+       int ret;\r
+       struct eswin *tr = container_of(work, struct eswin, register_work.work);\r
+\r
+       ECRNX_DBG("%s entry, dl_fw = %d!!", __func__, dl_fw);\r
+\r
+    if(dl_fw){\r
+       if (fw_name) {\r
+               ECRNX_PRINT("fw file name: %s\n",fw_name);\r
+       }\r
+       else {\r
+               fw_name = "ECR6600U_transport.bin";\r
+       }\r
+    }\r
+       \r
+       if (dl_fw  && eswin_fw_file_chech(tr)) {\r
+           ECRNX_DBG("%s entry, start fw download!!", __func__);\r
+        if( eswin_fw_file_download(tr) < 0)\r
+        {\r
+            release_firmware(tr->fw);\r
+            return;\r
+        } \r
+        release_firmware(tr->fw);\r
+               dl_fw = false;\r
+               ECRNX_DBG("%s entry, finish and stop fw download!!", __func__);\r
+               schedule_delayed_work(&tr->register_work, msecs_to_jiffies(1000));\r
+               return;\r
+       }\r
+\r
+#ifdef CONFIG_ECRNX_WIFO_CAIL\r
+       ECRNX_DBG("%s entry, amt_mode = %d!!", __func__, amt_mode);\r
+#endif\r
+\r
+       tr->rx_callback = ecrnx_rx_callback;\r
+       tr->data_cfm_callback = ecrnx_data_cfm_callback;\r
+       tr->msg_cfm_callback = ecrnx_msg_cfm_callback;\r
+       tr->ops->start(tr);\r
+       ret = ecrnx_platform_init(tr, &tr->umac_priv);\r
+       set_bit(ESWIN_FLAG_CORE_REGISTERED, &tr->dev_flags);\r
+\r
+    ECRNX_DBG("%s exit!!", __func__);\r
+\r
+    return;\r
+}\r
+bool register_status = false;\r
+\r
+int eswin_core_register(struct eswin *tr)\r
+{\r
+    if(register_status == true)\r
+    {\r
+        return 0;\r
+    }\r
+    register_status = true;\r
+       ECRNX_DBG("%s entry!!", __func__);\r
+       schedule_delayed_work(&tr->register_work, msecs_to_jiffies(1));\r
+       return 0;\r
+}\r
+\r
+void eswin_core_unregister(struct eswin *tr)\r
+{\r
+       ECRNX_DBG("%s entry!!", __func__);\r
+    if(register_status == false)\r
+    {\r
+        return;\r
+    }\r
+    register_status = false;\r
+    msleep(20);\r
+       cancel_delayed_work_sync(&tr->register_work);\r
+\r
+       if (!test_bit(ESWIN_FLAG_CORE_REGISTERED, &tr->dev_flags))\r
+               return;\r
+    tr->rx_callback = NULL;\r
+    ecrnx_platform_deinit(tr->umac_priv);\r
+    eswin_core_destroy(tr);\r
+}\r
+\r
+int usb_host_send(void *buff, int len, int flag)\r
+{\r
+       int ret = -1;\r
+       struct eswin * tr= pEswin;\r
+       struct sk_buff *skb = NULL;\r
+\r
+    //ECRNX_DBG("%s-%d: flag:0x%08x, mask:0x%x, desc:0x%x, type:0x%x \n", __func__, __LINE__, flag, FLAG_MSG_TYPE_MASK, TX_FLAG_TX_DESC, (flag & FLAG_MSG_TYPE_MASK));\r
+       if((u8_l)(flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)\r
+       {\r
+           skb = (struct sk_buff*)buff;\r
+       }\r
+       else\r
+       {\r
+        skb = dev_alloc_skb(len + sizeof(int)); //add the flag length (len + 4)\r
+\r
+        memcpy(skb->data, (char*)&flag, sizeof(int));\r
+        memcpy((char*)skb->data + sizeof(int), buff, len); //put rx desc tag to skb\r
+        skb->len = len + sizeof(int);\r
+       }\r
+\r
+       ECRNX_DBG("usb_host_send, skb:0x%08x, skb_len:%d, frame_len:%d, flag:0x%08x \n", skb, skb->len, len, flag);\r
+\r
+       if (tr->ops->xmit) {\r
+               ret = tr->ops->xmit(tr, skb);\r
+       } else {\r
+               ECRNX_ERR("eswin_sdio_work error, ops->xmit is null\n");\r
+       }\r
+\r
+       return ret;\r
+}\r
+\r
+extern void ecrnx_send_handle_register(void * fn);\r
+struct eswin * eswin_core_create(size_t priv_size, struct device *dev, struct usb_ops * ops)\r
+{\r
+       struct eswin * tr;\r
+\r
+       tr = (struct eswin *)kzalloc(sizeof(struct eswin) + priv_size, GFP_KERNEL);\r
+       if(!tr) {\r
+               return NULL;\r
+       }\r
+\r
+       pEswin = tr;\r
+\r
+       tr->dev = dev;\r
+       tr->ops = ops;\r
+\r
+       ecrnx_send_handle_register(usb_host_send);\r
+\r
+    if(usb_status == false)\r
+    {\r
+        INIT_DELAYED_WORK(&tr->register_work, eswin_core_register_work);\r
+        usb_status = true;\r
+    }\r
+\r
+       tr->state = ESWIN_STATE_INIT;\r
+\r
+       ECRNX_DBG(" %s exit!!", __func__);\r
+       return tr;\r
+\r
+}\r
+\r
+void eswin_core_destroy(struct eswin *tr)\r
+{\r
+       tr->state = ESWIN_STATE_CLOSEED;\r
+\r
+       ECRNX_DBG("%s entry!!", __func__);\r
+    usb_status = false;\r
+\r
+       //flush_workqueue(tr->workqueue);\r
+       //destroy_workqueue(tr->workqueue);\r
+    //TODO:\r
+       //eswin_mac_destroy(tr);\r
+}\r
+\r
+\r
+//MODULE_AUTHOR("Transa-Semi");\r
+//MODULE_LICENSE("Dual BSD/GPL");\r
+//MODULE_DESCRIPTION("Core module for Transa-Semi 802.11 WLAN SDIO driver");\r
diff --git a/drivers/net/wireless/eswin/usb/core.h b/drivers/net/wireless/eswin/usb/core.h
new file mode 100644 (file)
index 0000000..e99074f
--- /dev/null
@@ -0,0 +1,297 @@
+#ifndef _CORE_H_
+#define _CORE_H_
+
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+
+#include <net/mac80211.h>
+#include "ecrnx_compat.h"
+
+
+#define ESWIN_FLAG_CORE_REGISTERED     1
+
+#define ESWIN_NR_VIF           2
+#define ESWIN_NR_VIF_HW_QUEUE  4
+
+
+#define ESWIN_STATE_BOOT               1
+#define ESWIN_STATE_INIT               2
+#define ESWIN_STATE_STOP               3
+#define ESWIN_STATE_CLOSING    4
+#define ESWIN_STATE_CLOSEED    5
+#define ESWIN_STATE_START      6
+#define ESWIN_STATE_RUNNING    7
+
+
+#define WIM_RESP_TIMEOUT    (msecs_to_jiffies(100))
+
+enum ESWIN_SCAN_MODE {
+       ESWIN_SCAN_MODE_IDLE = 0,
+       ESWIN_SCAN_MODE_SCANNING,
+       ESWIN_SCAN_MODE_ABORTING,
+};
+
+
+
+struct fwinfo_t {
+       uint32_t ready;
+       uint32_t version;
+       uint32_t tx_head_size;
+       uint32_t rx_head_size;
+       uint32_t payload_align;
+       uint32_t buffer_size;
+};
+
+struct eswin_capabilities {
+       uint64_t cap_mask;
+       uint16_t listen_interval;
+       uint16_t bss_max_idle;
+       uint8_t bss_max_idle_options;
+};
+
+struct eswin_max_idle {
+       bool enable;
+       u16 period;
+       u16 scale_factor;
+       u8 options;
+       struct timer_list keep_alive_timer;
+
+       unsigned long idle_period; /* jiffies */
+       struct timer_list timer;
+};
+
+
+/* Private txq driver data structure */
+struct eswin_txq {
+       u16 hw_queue; /* 0: AC_BK, 1: AC_BE, 2: AC_VI, 3: AC_VO */
+       struct list_head list;
+       struct sk_buff_head queue; /* own queue */
+       unsigned long nr_fw_queueud;
+       unsigned long nr_push_allowed;
+       struct ieee80211_vif vif;
+       struct ieee80211_sta sta;
+};
+
+struct tx_buff_node {
+       struct tx_buff_node * next;
+       void * buff;
+       int    len;
+       int    flag;
+};
+struct tx_buff_queue {
+       struct tx_buff_node * head;
+       struct tx_buff_node * tail;
+       int    count;
+       spinlock_t lock;
+};
+
+#define ESWIN_QUEUE_MAX         (ESWIN_NR_VIF_HW_QUEUE*ESWIN_NR_VIF + 3)
+
+typedef int (*usb_rx_cb_t)(void *priv, struct sk_buff *skb, unsigned char ep);
+typedef int (*usb_data_cfm_cb_t)(void *priv, void *host_id);
+struct eswin {
+
+    void *umac_priv; //mac drv data.
+       struct ieee80211_hw *hw;
+       struct ieee80211_vif *vif[ESWIN_NR_VIF];
+       struct device *dev;
+       int nr_active_vif;
+       int state;
+       bool promisc;
+
+       bool loopback;
+       bool ampdu_supported;
+       int lb_count;
+       bool amsdu_supported;
+       bool block_frame;
+       bool ampdu_reject;
+       
+       char alpha2[2];
+       u64 tsf_offset;
+
+       struct usb_ops *ops;
+       usb_rx_cb_t rx_callback;
+       usb_data_cfm_cb_t data_cfm_callback;
+       usb_data_cfm_cb_t msg_cfm_callback;
+
+       //struct sx_buff_queue queue[ESWIN_NR_VIF]; /* 0: frame, 1: wim */
+       struct tx_buff_queue tx_queue;
+       struct tx_buff_node tx_node[64];
+       struct tx_buff_node * tx_node_head;
+       spinlock_t tx_lock;
+       int tx_node_num;
+       struct work_struct work;
+
+       struct eswin_txq ntxq[ESWIN_QUEUE_MAX];
+
+
+       struct mutex state_mtx;
+       enum ESWIN_SCAN_MODE scan_mode;
+       struct workqueue_struct *workqueue;
+       struct delayed_work scan_timeout;
+
+       //struct work_struct register_work;
+       struct delayed_work register_work;
+
+
+       unsigned long dev_flags;
+       struct mac_address mac_addr[ESWIN_NR_VIF];
+       struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
+
+
+
+       /* Move to vif or sta driver data */
+       u8 frame_seqno;
+       u8 wim_seqno;
+       u8 band;
+       u16 center_freq;
+       u16 aid;
+       u32 cipher_pairwise;
+       u32 cipher_group;
+       
+       struct fwinfo_t fwinfo;
+       struct eswin_capabilities cap;
+
+       /* power management */
+       enum ps_mode {
+               PS_DISABLED,
+               PS_ENABLED,
+               PS_AUTO_POLL,
+               PS_MANUAL_POLL
+       } ps;
+       bool ps_poll_pending;
+       bool ps_enabled;
+
+
+
+       /* tx */
+       spinlock_t txq_lock;
+       struct list_head txq;
+       /* 0: AC_BK, 1: AC_BE, 2: AC_VI, 3: AC_VO */
+       atomic_t tx_credit[IEEE80211_NUM_ACS*3];
+       atomic_t tx_pend[IEEE80211_NUM_ACS*3];
+
+//     struct completion wim_responded;
+//     struct sk_buff *last_wim_responded;
+
+
+       struct delayed_work roc_finish;
+
+       struct firmware *fw;
+
+       struct dentry *debugfs;
+
+       /* must be last */
+       u8 drv_priv[0] __aligned(sizeof(void *));
+};
+
+
+/* vif driver data structure */
+struct eswin_vif {
+       struct eswin *tr;
+       int index;
+       struct net_device *dev;
+
+       /* scan */
+       struct delayed_work scan_timeout;
+
+       /* power save */
+       bool ps_polling;
+
+       /* MLME */
+       spinlock_t preassoc_sta_lock;
+       struct list_head preassoc_sta_list;
+
+       /* inactivity */
+       u16 max_idle_period;
+};
+
+#define to_ieee80211_vif(v) \
+       container_of((void *)v, struct ieee80211_vif, drv_priv)
+
+#define to_i_vif(v) ((struct eswin_vif *) (v)->drv_priv)
+
+static inline int hw_vifindex(struct ieee80211_vif *vif)
+{
+       struct eswin_vif *i_vif;
+
+       if (vif == NULL)
+               return 0;
+
+       i_vif = to_i_vif(vif);
+       return i_vif->index;
+}
+
+/* sta driver data structure */
+struct eswin_sta {
+       struct eswin *tr;
+       struct ieee80211_vif *vif;
+       /*struct ieee80211_sta *sta;*/
+
+       enum ieee80211_sta_state state;
+       struct list_head list;
+
+       /* keys */
+       struct ieee80211_key_conf *ptk;
+       struct ieee80211_key_conf *gtk;
+
+       /* BSS max idle period */
+       struct eswin_capabilities cap;
+       struct eswin_max_idle max_idle;
+};
+
+#define to_ieee80211_sta(s) \
+       container_of((void *)s, struct ieee80211_sta, drv_priv)
+
+#define to_i_sta(s) ((struct eswin_sta *) (s)->drv_priv)
+
+
+
+struct eswin_sta_handler {
+       int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+                        struct ieee80211_sta *sta,
+                        enum ieee80211_sta_state old_state,
+                        enum ieee80211_sta_state new_state);
+
+};
+
+#if 0
+#define STAH(fn)                                       \
+       static struct eswin_sta_handler __stah_ ## fn   \
+       __attribute((__used__))                         \
+       __attribute((__section__("nrc.sta"))) = {       \
+               .sta_state = fn,                        \
+       }
+
+extern struct eswin_sta_handler __sta_h_start, __sta_h_end;
+#endif
+
+
+/* trx */
+
+struct eswin_trx_data {
+       struct eswin *tr;
+       struct ieee80211_vif *vif;
+       struct ieee80211_sta *sta;
+       struct sk_buff *skb;
+       int result;
+};
+
+
+#define NL80211_IFTYPE_ALL (BIT(NUM_NL80211_IFTYPES)-1)
+int eswin_core_register(struct eswin *tr);
+void eswin_core_unregister(struct eswin *tr);
+struct eswin * eswin_core_create(size_t priv_size, struct device *dev, struct usb_ops * ops);
+void eswin_core_destroy(struct eswin *tr);
+
+extern int power_save;
+extern int disable_cqm;
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+extern bool amt_mode;
+#endif
+extern bool set_gain;
+extern bool dl_fw;
+extern bool register_status;
+
+#endif
diff --git a/drivers/net/wireless/eswin/usb/debug.c b/drivers/net/wireless/eswin/usb/debug.c
new file mode 100644 (file)
index 0000000..f5a3469
--- /dev/null
@@ -0,0 +1,187 @@
+#include <linux/timer.h>
+#include "ecrnx_utils.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_rx.h"
+#include "ecrnx_usb.h"
+#include "core.h"
+#include "debug.h"
+#include "usb.h"
+#include "ecrnx_rx.h"
+
+
+//kernal timer param
+static struct timer_list tm;
+static int counter = 0;
+
+//rx param
+struct sk_buff *skb = NULL;
+struct ecrnx_hw *g_ecrnx_hw = NULL;
+
+//tx param
+struct ecrnx_vif vif;
+struct ecrnx_sta sta;
+struct cfg80211_mgmt_tx_params params;
+
+static void test_timer_handler(struct timer_list * lt);
+
+void ecrnx_hw_set(void* init_ecrnx_hw)
+{
+    g_ecrnx_hw = (struct ecrnx_hw *)init_ecrnx_hw;
+}
+
+static int sdio_rx_param_init(void)
+{
+    struct rxu_stat_mm rxu_state;
+    struct rx_hd  rx_head;
+    struct ethhdr eth_hd;
+    int res, index = 0;
+    uint8_t *ptr = NULL;
+    uint16_t head_len = sizeof(struct ethhdr);
+    ECRNX_DBG("%s entry!!", __func__);
+    memset(&rxu_state, 0, sizeof(struct rxu_stat_mm));
+    memset(&rx_head, 0, sizeof(struct rx_hd));
+    memset(&eth_hd, 0, sizeof(struct ethhdr));
+
+    rxu_state.comm_hd.frm_type = USB_FRM_TYPE_RXDESC;
+    //rxu_state.comm_hd.frm_type = USB_FRM_TYPE_MSG;
+    if(rxu_state.comm_hd.frm_type == USB_FRM_TYPE_RXDESC)
+    {
+        head_len += sizeof(struct rxu_stat_mm) + sizeof(struct rx_hd);
+    }
+    else
+    {
+        head_len += sizeof(dispatch_hdr_t);
+    }
+
+    skb = dev_alloc_skb(FRAME_SIZE + head_len);
+    skb_reserve(skb, head_len);
+    ptr = skb_put(skb, FRAME_SIZE); //ptr is skb tail
+    memset(skb->data, 0x0f, FRAME_SIZE); //payload
+    skb_push(skb, sizeof(struct ethhdr));
+
+    for( index = 0; index < ETH_ALEN; index++)
+    {
+        eth_hd.h_dest[index] = index;
+        eth_hd.h_source[index] = index;
+    }
+
+    eth_hd.h_proto = ETH_P_80221; //ETHERTYPE_IP;
+    memcpy(skb->data, &eth_hd, sizeof(struct ethhdr));
+
+    if(rxu_state.comm_hd.frm_type == USB_FRM_TYPE_RXDESC)
+    {
+        //data frame, need header, rxu state
+        //rx head
+        skb_push(skb, sizeof(struct rx_hd));
+        rx_head.frmlen = FRAME_SIZE + head_len;
+        rx_head.ampdu_stat_info = 0; 
+        //...
+        memcpy(skb->data , &rx_head, sizeof(struct rx_hd));
+
+        //rxu state
+        skb_push(skb, sizeof(struct rxu_stat_mm));
+        rxu_state.msdu_mode = 0x01;
+        rxu_state.host_id = 0x0001;
+        rxu_state.frame_len  = rx_head.frmlen;
+        rxu_state.status = RX_STAT_MONITOR;
+        //rxu_state.phy_info.info1 = 1 | (1 << 8) | (2450 << 16);
+        //rxu_state.phy_info.info2 = 2450 | (2450 << 16);
+#ifdef CONFIG_ECRNX_FULLMAC
+        //rxu_state.flags = 0;
+#endif
+        //rxu_state.pattern = ecrnx_rxbuff_pattern;
+        memcpy(skb->data, &rxu_state, sizeof(struct rxu_stat_mm));
+    }
+    else
+    {
+        //message frame, don't need header
+        skb_push(skb, sizeof(dispatch_hdr_t)); //rxu state
+        memcpy(skb->data, &rxu_state.comm_hd, sizeof(dispatch_hdr_t));
+    }
+
+    for(index = 0; index < head_len; index++)
+    {
+        ECRNX_DBG("0x%x ", skb->data[index]);
+    }
+
+    ECRNX_DBG("%s exit, skb_len:%d, type: %d!!", __func__, skb->len, (skb->data[1] << 8) | skb->data[0]);
+
+    return res;
+}
+
+extern int ecrnx_start_mgmt_xmit(struct ecrnx_vif *vif, \
+                        struct ecrnx_sta *sta, \
+                        struct cfg80211_mgmt_tx_params *params, \
+                        bool offchan, \
+                        u64 *cookie);
+
+/*
+struct cfg80211_mgmt_tx_params {
+       struct ieee80211_channel *chan;
+       bool offchan;
+       unsigned int wait;
+       const u8 *buf;
+       size_t len;
+       bool no_cck;
+       bool dont_wait_for_ack;
+       int n_csa_offsets;
+       const u16 *csa_offsets;
+};
+*/
+static void sdio_tx_param_init(void)
+{
+    u8 send_buf[FRAME_SIZE] = {0x00, 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09};
+
+    params.len = FRAME_SIZE;
+    params.buf = (const u8 *)send_buf;
+    params.n_csa_offsets = 10;
+    params.csa_offsets = (const u16 *)send_buf;
+    params.no_cck = 0;
+
+    vif.ecrnx_hw = g_ecrnx_hw;
+}
+
+void sdio_rx_tx_test_schedule(void)
+{
+    ECRNX_DBG("%s entry!!", __func__);
+    tm.function = test_timer_handler;
+    tm.expires = jiffies + HZ * 10;
+    add_timer(&tm);
+    
+    sdio_rx_param_init();
+    sdio_tx_param_init();
+    ECRNX_DBG("%s exit!!", __func__);
+}
+
+static void test_timer_handler(struct timer_list * lt)
+{
+    ECRNX_DBG("%s, counter:%d\n", __FUNCTION__, counter);
+
+    if(counter%2)
+    {
+        u64 cookie;
+        //ecrnx_start_mgmt_xmit(&vif, NULL, &params, false, &cookie);
+    }
+    else
+    {
+        ecrnx_rx_callback(g_ecrnx_hw, skb, 1);
+    }
+
+    if(lt)
+    {
+        counter++;
+    }
+
+    if(counter < 5)
+    {
+        //tm.expires = jiffies +1 * HZ / 1000/10;  //100us
+        tm.expires = jiffies +1 * HZ;
+        add_timer(&tm);
+    }
+    else
+    {
+        counter = 0;
+        del_timer(&tm);
+    }
+}
+
diff --git a/drivers/net/wireless/eswin/usb/debug.h b/drivers/net/wireless/eswin/usb/debug.h
new file mode 100644 (file)
index 0000000..1f50258
--- /dev/null
@@ -0,0 +1,21 @@
+
+
+#ifndef _ECRNX_DBG
+#define _ECRNX_DBG
+#include <net/mac80211.h>
+
+//#define CONFIG_HIF_PRINT_TX_DATA
+#define CONFIG_USE_TXQ
+
+//#define CONFIG_NRC_HIF_PRINT_FLOW_CONTROL
+//#define CONFIG_SHOW_TX_SPEED
+//#define CONFIG_SHOW_RX_SPEED
+
+#define FRAME_SIZE         512
+
+void eswin_dump_wim(struct sk_buff *skb);
+void eswin_init_debugfs(struct eswin *tr);
+void sdio_rx_tx_test_schedule(void);
+void ecrnx_hw_set(void* init_ecrnx_hw);
+
+#endif
diff --git a/drivers/net/wireless/eswin/usb/ecrnx_usb.c b/drivers/net/wireless/eswin/usb/ecrnx_usb.c
new file mode 100644 (file)
index 0000000..4bfe655
--- /dev/null
@@ -0,0 +1,447 @@
+/**
+ ******************************************************************************
+ *
+ * @file ecrnx_usb.c
+ *
+ * @brief ECRNX usb init and management function
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "core.h"
+//#include "debug.h"
+#include "ecrnx_utils.h"
+#include "ecrnx_cmds.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_msg_rx.h"
+#include "ipc_host.h"
+#include "ipc_shared.h"
+#include "ecrnx_events.h"
+#include "ecrnx_usb.h"
+#ifdef CONFIG_TEST_ESWIN_USB
+#include "debug.h"
+#endif
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+#include "ecrnx_amt.h"
+#endif
+
+
+#define USB_ADDR_DATA                  (unsigned int)(0x200)
+
+#ifdef CONFIG_ECRNX_SOFTMAC
+#define FW_STR  "lmac"
+#elif defined CONFIG_ECRNX_FULLMAC
+#define FW_STR  "fmac"
+#endif
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+#include "ecrnx_debugfs_func.h"
+#endif
+
+extern const int nx_txdesc_cnt_msk[];
+
+struct vendor_radiotap_hdr {
+    u8 oui[3];
+    u8 subns;
+    u16 len;
+    u8 data[];
+};
+
+#ifdef CONFIG_WEXT_PRIV
+extern void priv_copy_data_wakeup(struct ecrnx_hw *ecrnx_hw, struct sk_buff *skb);
+#endif
+
+/**
+ * @brief: ipc_host_rxdesc_handler: Handle the reception of a Rx Descriptor
+ *  Called from general IRQ handler when status %IPC_IRQ_E2A_RXDESC is set
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_rxdesc_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+       u8 ret = 0;
+    // LMAC has triggered an IT saying that a reception has occurred.
+    // Then we first need to check the validity of the current hostbuf, and the validity
+    // of the next hostbufs too, because it is likely that several hostbufs have been
+    // filled within the time needed for this irq handling
+#ifdef CONFIG_ECRNX_FULLMAC
+    // call the external function to indicate that a RX descriptor is received
+    ret = env->cb.recv_data_ind(env->pthis, skb);
+#else
+    // call the external function to indicate that a RX packet is received
+    ret = env->cb.recv_data_ind(env->pthis, skb);
+#endif //(CONFIG_ECRNX_FULLMAC)
+    //ECRNX_DBG("%s exit!!", __func__);
+       return ret;
+}
+
+/**
+ * @brief: usb_host_radar_handler  Handle the reception of radar events
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_radar_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    int ret = 0;
+    
+#ifdef CONFIG_ECRNX_RADAR
+    // LMAC has triggered an IT saying that a radar event has been sent to upper layer.
+    // Then we first need to check the validity of the current msg buf, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    spin_lock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+    ret = env->cb.recv_radar_ind(env->pthis, skb);
+    spin_unlock(&((struct ecrnx_hw *)env->pthis)->radar.lock);
+#endif /* CONFIG_ECRNX_RADAR */
+    return ret;
+}
+
+/**
+ * @brief: usb_host_unsup_rx_vec_handler  Handle the reception of unsupported rx vector
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_unsup_rx_vec_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    return env->cb.recv_unsup_rx_vec_ind(env->pthis, skb);
+}
+
+/**
+ * @brief: usb_host_msg_handler  Handler for firmware message
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_msg_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    // LMAC has triggered an IT saying that a message has been sent to upper layer.
+    // Then we first need to check the validity of the current msg buf, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    return env->cb.recv_msg_ind(env->pthis, skb->data);
+}
+
+/**
+ * @brief: usb_host_msgack_handler  Handle the reception of message acknowledgement
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_msgack_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    ptr_addr hostid = *(ptr_addr *)skb->data;
+
+    ASSERT_ERR(hostid);
+
+    env->msga2e_hostid = NULL;
+    env->cb.recv_msgack_ind(env->pthis, (void*)hostid);
+
+    return 0;
+}
+
+/**
+ * @brief: usb_host_dbg_handler  Handle the reception of Debug event
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+static int usb_host_dbg_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    // LMAC has triggered an IT saying that a DBG message has been sent to upper layer.
+    // Then we first need to check the validity of the current buffer, and the validity
+    // of the next buffers too, because it is likely that several buffers have been
+    // filled within the time needed for this irq handling
+    // call the external function to indicate that a RX packet is received
+    return env->cb.recv_dbg_ind(env->pthis, skb);
+}
+
+/**
+ * @brief: usb_host_tx_cfm_handler  Handle the reception of TX confirmation
+ * @param {env} pointer to the usb Host environment
+ * @param {queue_idx} index of the hardware on which the confirmation has been received
+ * @param {user_pos} index of the user position
+ * @return: none
+ */
+static int usb_host_tx_cfm_handler(struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    ptr_addr host_id;
+    struct sk_buff *skb_cfm;
+    struct ecrnx_txhdr *txhdr;
+
+#ifdef CONFIG_ECRNX_FULLMAC
+    struct tx_cfm_tag *cfm;
+
+    cfm = (struct tx_cfm_tag *)skb->data;
+    //host_id = cfm->hostid;
+    memcpy((uint8_t *)&host_id, (uint8_t *)cfm->hostid, sizeof(ptr_addr));
+    if (host_id == 0) {
+        return 0;
+    }
+
+    ECRNX_DBG("%s:hostid(tx_skb):0x%08x, rx_skb: 0x%x \n", __func__, host_id, skb);
+    skb_cfm = (struct sk_buff *)host_id;
+    txhdr = (struct ecrnx_txhdr *)(*((ptr_addr*)skb_cfm->data - 1));
+    memcpy(&txhdr->hw_hdr.cfm, cfm, sizeof(*cfm));
+#elif defined CONFIG_ECRNX_SOFTMAC
+    //TODO:
+#endif
+
+    return env->cb.send_data_cfm(env->pthis, (void*)txhdr);
+}
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+/**
+ * @brief: usb_host_amt_rx_handler  Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {skb} received skb data
+ * @return: none
+ */
+void usb_host_amt_rx_handler(uint32_t frm_type, struct sk_buff *skb)
+{
+    int need_free = 0;
+
+    ECRNX_DBG("%s enter, frame type: %d!!", __func__, frm_type);
+    if(!skb)
+    {
+        ECRNX_ERR("usb_host_amt_rx_handler input param error!! \n!");
+        return;
+    }
+
+    if (frm_type != USB_FRM_TYPE_RXDESC)
+    {
+        skb_pull(skb, SKB_DATA_COM_HD_OFFSET); //delete the frame common header
+    }
+
+    ECRNX_DBG("skb:0x%08x, skb_len:%d, frame type: %d!!", skb->data,skb->len, frm_type);
+
+    switch (frm_type)
+    {
+        case USB_FRM_TYPE_IWPRIV:
+        {
+               /*printk("vif_start:%d, vif_monitor:%d \n", ecrnx_hw->vif_started, ecrnx_hw->monitor_vif);
+                       print_hex_dump(KERN_INFO, "iwpriv-cfm:", DUMP_PREFIX_ADDRESS, 32, 1,
+                       skb->data, skb->len, false);*/
+            amt_vif.rxlen = skb->len;
+                       memset(amt_vif.rxdata, 0, ECRNX_RXSIZE);
+            memcpy(amt_vif.rxdata, skb->data, skb->len);
+            amt_vif.rxdatas = 1;
+            wake_up(&amt_vif.rxdataq);
+                       need_free = 1;
+            break;
+        }
+        default:
+               need_free = 1;
+            break;
+    }
+
+    if (need_free && skb) { // free the skb
+       ECRNX_DBG("skb free: 0x%x !! \n", skb);
+        dev_kfree_skb(skb);
+    }
+    ECRNX_DBG("%s exit!!", __func__);
+    return;
+}
+#endif
+
+
+/**
+ * @brief: usb_host_rx_handler  Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+void usb_host_rx_handler(uint32_t frm_type, struct ipc_host_env_tag *env, struct sk_buff *skb)
+{
+    int ret = 1;
+
+    //ECRNX_DBG("%s enter, frame type: %d!!", __func__, frm_type);
+    if(!env || !skb)
+    {
+        ECRNX_ERR("usb_host_rx_handler input param error!! \n!");
+        return;
+    }
+
+    ((struct ecrnx_hw *)env->pthis)->usb_rx++;
+
+    if (frm_type != USB_FRM_TYPE_RXDESC)
+    {
+        skb_pull(skb, SKB_DATA_COM_HD_OFFSET); //delete the frame common header
+    }
+
+    //ECRNX_DBG("skb:0x%08x, skb_len:%d, frame type: %d!!", skb->data,skb->len, frm_type);
+
+    switch (frm_type)
+    {
+        case USB_FRM_TYPE_RXDESC:
+        {
+            // handle the RX descriptor reception
+                       usb_host_rxdesc_handler(env, skb); //just for current only one endpoint test
+            break;
+        }
+
+        case USB_FRM_TYPE_MSG_ACK:
+        {
+            if(1 == skb->len)
+            {
+                ECRNX_PRINT("MSG_ACK len: 1");
+                break;
+            }
+            ret = usb_host_msgack_handler(env, skb);
+            break;
+        }
+
+        case USB_FRM_TYPE_MSG:
+        {
+            ret = usb_host_msg_handler(env, skb);
+            break;
+        }
+
+        case USB_FRM_TYPE_TXCFM:
+        {
+            if(!env->pthis)
+            {
+                ECRNX_ERR("env->pthis ptr error!! \n!");
+                break;
+            }
+
+            /* add the spinlock which was missed during porting.
+               when skb->len more than 24 and skb contans more than one data cfm.
+               data cfm structure length is 24 byte.
+            */
+            spin_lock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+            while(skb->len > sizeof(struct tx_cfm_tag))
+            {
+                ret = usb_host_tx_cfm_handler(env, skb);
+                skb_pull(skb, sizeof(struct tx_cfm_tag));
+            }
+            ret = usb_host_tx_cfm_handler(env, skb);
+            spin_unlock_bh(&((struct ecrnx_hw *)env->pthis)->tx_lock);
+            break;
+        }
+
+        case USB_FRM_TYPE_UNSUP_RX_VEC:
+        {
+            // handle the unsupported rx vector reception
+            ret = usb_host_unsup_rx_vec_handler(env, skb);
+            break;
+        }
+
+        case USB_FRM_TYPE_RADAR:
+        {
+            // handle the radar event reception
+            ret = usb_host_radar_handler(env, skb);
+            break;
+        }
+
+        case USB_FRM_TYPE_TBTT_SEC:
+        {
+            env->cb.sec_tbtt_ind(env->pthis);
+            break;
+        }
+
+        case USB_FRM_TYPE_TBTT_PRIM:
+        {
+            env->cb.prim_tbtt_ind(env->pthis);
+            break;
+        }
+
+        case USB_FRM_TYPE_DBG:
+        {
+            ECRNX_DBG("--%s:USB_FRM_TYPE_DBG, len:%d, slave:%s \n", __func__, skb->len, skb->data);
+            ret = usb_host_dbg_handler(env, skb);
+            break;
+        }
+        case USB_FRM_TYPE_IWPRIV:
+        {
+#ifdef CONFIG_WEXT_PRIV
+#if 0
+            struct ecrnx_hw *ecrnx_hw = (struct ecrnx_hw *)env->pthis;
+            struct ecrnx_vif* ecrnx_vif = ecrnx_hw->vif_table[0];
+
+            printk("vif_start:%d, vif_monitor:%d \n", ecrnx_hw->vif_started, ecrnx_hw->monitor_vif);
+            print_hex_dump(KERN_INFO, "iwpriv-cfm:", DUMP_PREFIX_ADDRESS, 32, 1,
+                       skb->data, skb->len, false);
+            ecrnx_vif->rxlen = skb->len;
+            memcpy(ecrnx_vif->rxdata, skb->data, skb->len);
+            ecrnx_vif->rxdatas = 1;
+            wake_up(&ecrnx_vif->rxdataq);
+#else
+            priv_copy_data_wakeup((struct ecrnx_hw *)env->pthis, skb);
+#endif
+
+#endif
+                       ret = 0;
+            break;
+        }
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+        case USB_FRM_DEBUG_FS:
+        {
+            uint32_t debugfs_type = ((uint32_t*)skb->data)[0];
+            debugfs_resp.debugfs_type = debugfs_type;
+
+
+            if((debugfs_type != SLAVE_LOG_LEVEL) && \
+            (debugfs_type < SLAVE_DEBUGFS_MAX)){
+
+                debugfs_resp.rxlen = skb->len-4;
+                memcpy(debugfs_resp.rxdata, skb->data+4, debugfs_resp.rxlen);
+
+                ECRNX_DBG("%s - wake_up()\n", __func__);
+                debugfs_resp.rxdatas = 1;
+                wake_up(&debugfs_resp.rxdataq);
+            }
+
+            break;
+        }
+#endif
+        default:
+               ret = 0;
+            break;
+    }
+
+    if (!ret && skb) { // free the skb
+       ECRNX_DBG("skb free: 0x%x, ret: %d!! \n", skb, ret);
+        dev_kfree_skb(skb);
+    }
+    //ECRNX_DBG("%s exit!!", __func__);
+    return;
+}
+
+/**
+ * @brief: ecrnx_usb_init  Initialize usb interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: u8
+ */
+u8 ecrnx_usb_init(struct ecrnx_hw *ecrnx_hw)
+{
+    ECRNX_DBG("%s entry!!", __func__);
+    // Save the pointer to the register base
+    ecrnx_hw->ipc_env->pthis = (void*)ecrnx_hw;
+
+    ECRNX_DBG("%s exit!!", __func__);
+    return 0;
+}
+
+/**
+ * @brief: ecrnx_usb_deinit  DeInitialize usb interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: none
+ */
+void ecrnx_usb_deinit(struct ecrnx_hw *ecrnx_hw)
+{
+    ECRNX_DBG(ECRNX_FN_ENTRY_STR);
+
+    memset(ecrnx_hw, 0, sizeof(struct ecrnx_hw));
+}
diff --git a/drivers/net/wireless/eswin/usb/ecrnx_usb.h b/drivers/net/wireless/eswin/usb/ecrnx_usb.h
new file mode 100644 (file)
index 0000000..98178a7
--- /dev/null
@@ -0,0 +1,110 @@
+/**
+******************************************************************************
+*
+* @file ecrnx_usb.h
+*
+* @brief ecrnx usb header file
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#ifndef _ECRNX_USB_H_
+#define _ECRNX_USB_H_
+
+#include "ecrnx_rx.h"
+#include "eswin_utils.h"
+
+enum{
+    USB_FRM_TYPE_RXDESC =1,
+    USB_FRM_TYPE_MSG,
+    USB_FRM_TYPE_DBG,
+    USB_FRM_TYPE_UNSUP_RX_VEC,
+    USB_FRM_TYPE_RADAR,
+    USB_FRM_TYPE_TBTT_SEC,
+    USB_FRM_TYPE_TBTT_PRIM,
+    USB_FRM_TYPE_MSG_ACK,
+    USB_FRM_TYPE_TXCFM,
+    USB_FRM_TYPE_IWPRIV,
+    USB_FRM_DEBUG_FS,
+};
+
+
+#define SKB_DATA_COM_HD_OFFSET      sizeof(dispatch_hdr_t)
+
+/// Element in the pool of RX header descriptor.
+struct rx_hd
+{
+    /// Total length of the received MPDU
+    uint16_t            frmlen;
+    /// AMPDU status information
+    uint16_t            ampdu_stat_info;
+    /// TSF Low
+    uint32_t            tsflo;
+    /// TSF High
+    uint32_t            tsfhi;
+    /// Rx Vector 1
+    struct rx_vector_1  rx_vec_1;
+    /// Rx Vector 2
+    struct rx_vector_2  rx_vec_2;
+    /// MPDU status information
+    uint32_t            statinfo;
+};
+
+/*adjust phy info,pattern, these infomation should be put to another buffer to trnasfer*/
+struct rxu_stat_mm
+{
+       /*type of msg/dbg/frm etc.*/
+       dispatch_hdr_t  frm_type;
+    uint32_t fragment_flag     : 1;
+    uint32_t is_qos            : 1;
+    uint32_t need_reord        : 1;
+    uint32_t need_pn_check     : 1;
+    uint32_t is_ga             : 1;
+    uint32_t flags_rsvd0       : 3;
+    uint32_t tid               : 8;
+    uint16_t sn;
+    /// Length
+    uint16_t frame_len;
+    /// Status (@ref rx_status_bits)
+    uint16_t status;
+    uint16_t real_offset; /* dma address align 4, real data - real_offset  */
+};
+
+
+
+/**
+ * @brief: ecrnx_usb_init  Initialize usb interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: u8
+ */
+u8 ecrnx_usb_init(struct ecrnx_hw *ecrnx_hw);
+
+/**
+ * @brief: ecrnx_usb_deinit  DeInitialize usb interface.
+ * @param {ecrnx_hw} Main driver data
+ * @return: none
+ */
+void ecrnx_usb_deinit(struct ecrnx_hw *ecrnx_hw);
+
+#ifdef CONFIG_ECRNX_WIFO_CAIL
+/**
+ * @brief: usb_host_rx_handler  Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {skb} received skb data
+ * @return: none
+ */
+void usb_host_amt_rx_handler(uint32_t frm_type, struct sk_buff *skb);
+#endif /* CONFIG_ECRNX_WIFO_CAIL */
+
+/**
+ * @brief: usb_host_rx_handler  Handle the reception of rx frame
+ * @param {frm_type} received frame type
+ * @param {env} pointer to the usb Host environment
+ * @param {skb} received skb data
+ * @return: none
+ */
+void usb_host_rx_handler(uint32_t frm_type, struct ipc_host_env_tag *env, struct sk_buff *skb);
+
+#endif /* __ECRNX_USB_H */
diff --git a/drivers/net/wireless/eswin/usb/fw.c b/drivers/net/wireless/eswin/usb/fw.c
new file mode 100644 (file)
index 0000000..66254d8
--- /dev/null
@@ -0,0 +1,266 @@
+/**
+******************************************************************************
+*
+* @file fw.c
+*
+* @brief ecrnx usb firmware download functions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#include <linux/firmware.h>
+#include "core.h"
+#include "usb.h"
+#include "fw_head_check.h"
+
+extern char *fw_name;
+
+#define MAX_PACKET_SIZE 512
+#define DATA_ADDR_O    0x20000
+
+#define DATA_ADDR_O_1  0x10000
+
+struct cfg_sync
+{
+       char magic;
+       char type;
+       short length;
+       char crc8;
+};
+
+struct cfg_msg
+{
+       char magic;
+       char type;
+       short length;
+       unsigned int data_addr;
+       unsigned int data_len;
+       char crc8;
+};
+
+typedef struct _ack_msg
+{
+       unsigned char  magic;
+       unsigned char  type;
+       unsigned short length;
+       char               data[0];
+} ack_msg;
+
+typedef enum
+{
+       STATU_SUCCESS = 0,
+       STATU_ERR
+} ACK_STATUS;
+
+unsigned char eswin_crc8(unsigned char * buf, unsigned short length)
+{
+       unsigned char crc = 0, i = 0;
+       while(length--)
+       {
+               crc ^= *buf++;
+               for(i = 8; i > 0; i--)
+               {
+                       if(crc & 0x80)
+                       {
+                               crc = (crc << 1) ^ 0x31;
+                       }
+                       else
+                       {
+                               crc <<= 1;
+                       }
+               }
+       }
+       return crc;
+}
+
+char eswin_fw_ack_check(u8 * buff)
+{
+    ack_msg* msg = (ack_msg*)buff;
+    if(msg->magic != 0x5a)
+    {
+        ECRNX_PRINT("dl-fw ack fail, magic: %d\n", msg->magic);
+        return -1;
+    }
+    if(msg->data[0] != STATU_SUCCESS)
+    {
+        ECRNX_PRINT("dl-fw ack fail, status: %d\n", msg->data[0]);
+        return -1;
+    }
+    return 0;
+}
+
+char eswin_fw_file_download(struct eswin *tr)
+{
+       int ret,i;
+       unsigned int lengthLeft, lengthSend, offset = HEAD_SIZE, total_send_len = 0;
+       unsigned char length_str[9]={0};
+       const u8 * dataAddr;
+       u8 * buff;
+       struct sk_buff *skb;
+       //char str_msg[16];
+    char str_sync[4] = {0x63,0x6e,0x79,0x73};
+       unsigned int file_load_addr[3] = {0x20000U, 0x40000U, 0x50000U};  // ilm addr(start addr); dlm addr; iram0 addr
+
+       struct cfg_msg * p_msg;
+       struct cfg_sync * p_sync;
+       u8 prev_dl_pst = 0;
+
+       skb = dev_alloc_skb(MAX_PACKET_SIZE + 16);
+       buff = (u8 *)skb->data;
+
+       p_msg = (struct cfg_msg *)buff;
+       p_sync = (struct cfg_sync *)buff;
+
+       lengthLeft = tr->fw->size;
+       dataAddr = tr->fw->data;
+
+       /* 0 download sync*/
+       memcpy(buff,str_sync,4);
+    tr->ops->write(tr,buff,4);
+    ret = tr->ops->wait_ack(tr,buff,1);
+       ECRNX_PRINT("dl-fw >> sync, ret: %d\n", ret);
+    if((buff[0] != '3')||(ret < 0))
+    {
+        ECRNX_PRINT("dl-fw >> download sync fail, ack: %d\n", buff[0]);
+        dev_kfree_skb(skb);
+        return -1;
+    }
+       /* 1 usb sync */
+       p_sync->magic = 0xA5;
+       p_sync->type = 0x00;
+       p_sync->length = 0x00;
+       p_sync->crc8 = eswin_crc8(buff,sizeof(struct cfg_msg)-1);
+
+       tr->ops->write(tr, buff, sizeof(struct cfg_sync));
+       ret = tr->ops->wait_ack(tr, buff, 6);
+       ECRNX_PRINT("dl-fw >> sync, ret: %d\n", ret);
+    if((eswin_fw_ack_check(buff) < 0)||(ret < 0))
+    {
+        dev_kfree_skb(skb);
+        ECRNX_PRINT("fimeware download fail! \n");
+        return -1;
+    }
+
+       for(i=0;i<3;i++)
+       {
+               memcpy(length_str, dataAddr + offset, 8);
+               ret = kstrtoint(length_str, 10, (int*)&lengthLeft);
+               offset+=8; 
+               /* 2 cfg */
+               p_msg->magic = 0xA5;
+               p_msg->type = 0x01;
+               p_msg->length = 0x08;
+               p_msg->data_addr = file_load_addr[i];
+               p_msg->data_len = lengthLeft;
+               p_msg->crc8 = 0x00;
+
+               tr->ops->write(tr, buff, sizeof(struct cfg_msg));
+               ret = tr->ops->wait_ack(tr, buff, 6);
+        if((eswin_fw_ack_check(buff) < 0)||(ret < 0))
+        {
+            dev_kfree_skb(skb);
+            ECRNX_PRINT("fimeware download fail! \n");
+            return -1;
+        }
+
+               buff[0] = 0xA5;
+               buff[1] = 0x01;
+
+               /* 3 load fw */
+               do {
+                       lengthSend = (lengthLeft >= MAX_PACKET_SIZE) ? MAX_PACKET_SIZE : lengthLeft;
+                       total_send_len += lengthSend;
+                       
+                       buff[0] = 0xA5;
+                       buff[1] = 0x01;
+                       buff[2] = lengthSend & 0xFF;
+                       buff[3] = (lengthSend>>8) & 0xFF;
+
+                       memcpy(&buff[4], dataAddr + offset, lengthSend);
+                       tr->ops->write(tr, buff, lengthSend + 5);
+
+                       if(lengthSend >= MAX_PACKET_SIZE)
+                       {
+                           if(prev_dl_pst < total_send_len/(tr->fw->size/100))
+                           {
+                               prev_dl_pst = total_send_len/(tr->fw->size/100);
+                           ECRNX_PRINT("firmware downloading %d%% \n", prev_dl_pst);
+                           }
+                       }
+                       else if(i == 2)
+                       {
+                           ECRNX_PRINT("firmware downloading 100%% \n");
+                       }
+
+                       ret = tr->ops->wait_ack(tr, buff, 6);
+            if((eswin_fw_ack_check(buff) < 0)||(ret < 0))
+            {
+                dev_kfree_skb(skb);
+                ECRNX_PRINT("fimeware download fail! \n");
+                return -1;
+            }
+                       offset += lengthSend;
+                       lengthLeft -= lengthSend;
+               } while(lengthLeft);
+       }
+
+       /* 4 start up */
+       buff[0] = 0xA5;
+       buff[1] = 0x06;
+       buff[2] = 0x02;
+       buff[3] = 0x00;
+
+       buff[4] = (char)((file_load_addr[0]) & 0xFF);
+       buff[5] = (char)(((file_load_addr[0])>>8) & 0xFF);
+       buff[6] = (char)(((file_load_addr[0])>>16) & 0xFF);
+       buff[7] = (char)(((file_load_addr[0])>>24) & 0xFF);
+
+       tr->ops->write(tr, skb->data, 9);
+       tr->ops->wait_ack(tr, buff, 6);
+    if((eswin_fw_ack_check(buff) < 0)||(ret < 0))
+    {
+        dev_kfree_skb(skb);
+        ECRNX_PRINT("fimeware download fail! \n");
+        return -1;
+    }
+
+       ECRNX_PRINT("fimeware download successfully! \n");
+
+       dev_kfree_skb(skb);
+    return 0;
+}
+
+bool eswin_fw_file_chech(struct eswin *tr)
+{
+       int status;
+
+       if (fw_name == NULL)
+               goto err_fw;
+
+
+       //if (tr->fw)
+       //      return true;
+
+       ECRNX_PRINT("%s, Checking firmware... (%s)\n",  __func__, fw_name);
+
+       status = request_firmware((const struct firmware **)&tr->fw, fw_name, tr->dev);
+       if (status != 0) {
+               ECRNX_PRINT("%s, error status = %d\n",  __func__, status);
+               goto err_fw;
+       }
+
+       ECRNX_PRINT("%s, request fw OK and size is %d\n", __func__, tr->fw->size);
+
+    if(fw_check_head(tr) == false)
+    {
+        goto err_fw;
+    }
+    return true;
+
+err_fw:
+       tr->fw = NULL;
+       return false;
+}
+
diff --git a/drivers/net/wireless/eswin/usb/fw.h b/drivers/net/wireless/eswin/usb/fw.h
new file mode 100644 (file)
index 0000000..ab329e8
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+******************************************************************************
+*
+* @file fw.h
+*
+* @brief ecrnx usb firmware download definitions
+*
+* Copyright (C) ESWIN 2015-2020
+*
+******************************************************************************
+*/
+
+#ifndef _FW_H_
+#define _FW_H_
+
+char eswin_fw_file_download(struct eswin *tr);
+bool eswin_fw_file_chech(struct eswin *tr);
+
+#endif
diff --git a/drivers/net/wireless/eswin/usb/usb.c b/drivers/net/wireless/eswin/usb/usb.c
new file mode 100644 (file)
index 0000000..ddfda8e
--- /dev/null
@@ -0,0 +1,1041 @@
+/**
+ ******************************************************************************
+ *
+ * @file usb.c
+ *
+ * @brief usb driver function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/gpio.h>
+#include <linux/timer.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
+#include <uapi/linux/sched/types.h>
+#endif
+//#include <linux/usb.h>
+#include "usb.h"
+
+#include "core.h"
+//#include "debug.h"
+#include "ecrnx_usb.h"
+
+#include "usb_host_interface.h"
+#include "ecrnx_compat.h"
+#include "fw_head_check.h"
+#include "slave_log_buf.h"
+
+#define USB_RX_TIMER_TIMEOUT_US          (200)
+//TCP PKG MAX LEN:1594; UDP PKG MAX LEN:1582
+#define USB_RX_LENGTH_THD                (1594)
+#define USB_RX_UPLOAD_THD                (12)
+
+static struct eswin_usb * g_usb;
+static unsigned int rx_packets =0;
+static struct timer_list usb_rx_timer = {0};
+
+static void usb_refill_recv_transfer(struct usb_infac_pipe * pipe);
+
+
+#define USB_LOG_MEM_SIZE       (512*8)//(512*16)
+#define USB_LOG_MAX_SIZE       512
+
+struct ring_buffer buf_handle = {0};
+
+#if defined(CONFIG_ECRNX_DEBUGFS_CUSTOM)
+struct ring_buffer *usb_dbg_buf_get(void)
+{
+    if(buf_handle.init == false)
+    {
+        ring_buffer_init(&buf_handle, USB_LOG_MEM_SIZE);
+    }
+    return &buf_handle;
+}
+#endif
+
+void usb_dbg_printf(void * data, int len)
+{
+    if(buf_handle.init == false)
+    {
+        ring_buffer_init(&buf_handle, USB_LOG_MEM_SIZE);
+    }
+    ring_buffer_put(&buf_handle, data, len);
+}
+
+
+static struct usb_urb_context *
+usb_alloc_urb_from_pipe(struct usb_infac_pipe *pipe)
+{
+       struct usb_urb_context *urb_context = NULL;
+       unsigned long flags;
+
+       spin_lock_irqsave(&g_usb->cs_lock, flags);
+       if (!list_empty(&pipe->urb_list_head)) {
+               urb_context = list_first_entry(&pipe->urb_list_head,
+                                              struct usb_urb_context, link);
+               list_del(&urb_context->link);
+               pipe->urb_cnt--;
+       }
+       spin_unlock_irqrestore(&g_usb->cs_lock, flags);
+
+       return urb_context;
+}
+
+
+
+static void usb_free_urb_to_infac(struct usb_infac_pipe * pipe,
+                                       struct usb_urb_context *urb_context)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&g_usb->cs_lock, flags);
+       pipe->urb_cnt++;
+       list_add(&urb_context->link, &pipe->urb_list_head);
+    if(urb_context->urb)
+    {
+        usb_unanchor_urb(urb_context->urb);
+        usb_free_urb(urb_context->urb);
+        urb_context->urb = NULL;
+    }
+       spin_unlock_irqrestore(&g_usb->cs_lock, flags);
+}
+
+static void usb_cleanup_recv_urb(struct usb_urb_context *urb_context)
+{
+       dev_kfree_skb(urb_context->skb);
+       urb_context->skb = NULL;
+
+       usb_free_urb_to_infac(urb_context->pipe, urb_context);
+}
+
+static void usb_free_pipe_resources(struct usb_infac_pipe *pipe)
+{
+       struct usb_urb_context *urb_context;
+
+       for (;;) {
+               urb_context = usb_alloc_urb_from_pipe(pipe);
+
+               if (!urb_context)
+                       break;
+        if(urb_context->urb)
+        {
+            usb_unanchor_urb(urb_context->urb);
+            usb_free_urb(urb_context->urb);
+            urb_context->urb = NULL;
+        }
+               kfree(urb_context);
+       }
+}
+
+static void usb_transmit_complete(struct urb *urb)
+{
+       struct usb_urb_context *urb_context = urb->context;
+       struct usb_infac_pipe *pipe = urb_context->pipe;
+       struct sk_buff *skb;
+       struct txdesc_api *tx_desc;
+       u32_l flag = 0;
+
+       //ECRNX_DBG(" %s entry, dir: %d!!", __func__, pipe->dir);
+
+       if (urb->status != 0) {
+        pipe->err_count++;
+        if(pipe->err_count%100 == 1)
+        {
+            ECRNX_PRINT( "pipe-dir: %d, failed:%d\n",
+                       pipe->dir, urb->status);
+        }
+        if((pipe->err_status != urb->status)||(pipe->err_count == 10000))
+        {
+            pipe->err_status = urb->status;
+            pipe->err_count = 0;
+        }
+       }
+
+       skb = urb_context->skb;
+       urb_context->skb = NULL;
+       usb_free_urb_to_infac(urb_context->pipe, urb_context);
+
+       flag = *(u32_l *)skb->data;
+       if((u8_l)(flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+       {
+               tx_desc = (struct txdesc_api *)((u32_l *)skb->data + 1);
+               if (tx_desc->host.flags & TXU_CNTRL_MGMT)
+               {
+                       return;
+               }
+       }
+
+       skb_queue_tail(&pipe->io_comp_queue, skb);
+#ifdef CONFIG_ECRNX_KTHREAD
+       wake_up_interruptible(&g_usb->wait_tx_comp);
+#endif
+#ifdef CONFIG_ECRNX_WORKQUEUE
+       schedule_work(&pipe->io_complete_work);
+#endif
+#ifdef CONFIG_ECRNX_TASKLET
+       tasklet_schedule(&pipe->tx_tasklet);
+#endif
+}
+
+
+void usb_rx_timer_handle(struct timer_list *time)
+{
+    if (rx_packets)
+    {
+        rx_packets = 0;
+        #ifdef CONFIG_ECRNX_KTHREAD
+           wake_up_interruptible(&g_usb->wait_rx_comp);
+        #endif
+        #ifdef CONFIG_ECRNX_WORKQUEUE
+           schedule_work(&g_usb->infac_data.pipe_rx.io_complete_work);
+        #endif
+        #ifdef CONFIG_ECRNX_TASKLET
+           tasklet_schedule(&g_usb->infac_data.pipe_rx.rx_tasklet);
+        #endif
+    }
+}
+
+static void usb_recv_complete(struct urb *urb)
+{
+       struct usb_urb_context *urb_context = urb->context;
+       struct usb_infac_pipe *pipe = urb_context->pipe;
+       struct sk_buff *skb;
+       int status = 0;
+
+       //ECRNX_PRINT(" %s entry, dir: %d!!", __func__, pipe->dir);
+
+
+       //ECRNX_PRINT( " usb recv pipe-dir %d stat %d len %d urb 0x%pK\n",
+       //         pipe->dir, 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:
+            pipe->err_count++;
+            if(pipe->err_count%100 == 1)
+            {
+                           ECRNX_PRINT("usb redcv pipe-dir %d  failed: %d\n",
+                                  pipe->dir, urb->status);
+            }
+            if((pipe->err_status != status)||(pipe->err_count == 10000))
+            {
+                pipe->err_status = status;
+                pipe->err_count = 0;
+            }
+                       break;
+               }
+               goto cleanup_recv_urb;
+       }
+
+       if (urb->actual_length == 0)
+               goto cleanup_recv_urb;
+
+       skb = urb_context->skb;
+
+       /* we are going to pass it up */
+       urb_context->skb = NULL;
+       skb_put(skb, urb->actual_length);
+
+       /* note: queue implements a lock */
+       skb_queue_tail(&pipe->io_comp_queue, skb);
+       usb_free_urb_to_infac(pipe, urb_context);
+
+#if 0
+    usb_rx_cnt++;
+    //printk("skb->len:%d %d %d\n", skb->len, usb_rx_cnt, urb->actual_length);
+    if (skb->len < 1500 ||usb_rx_cnt > 12)
+       {
+           usb_rx_cnt = 0;
+           schedule_work(&pipe->io_complete_work);
+    }
+#else
+
+    rx_packets++;
+    if (skb->len < USB_RX_LENGTH_THD || rx_packets > USB_RX_UPLOAD_THD)
+    {
+        rx_packets = 0;
+        #ifdef CONFIG_ECRNX_KTHREAD
+        wake_up_interruptible(&g_usb->wait_rx_comp);
+        #endif
+        #ifdef CONFIG_ECRNX_WORKQUEUE
+        schedule_work(&pipe->io_complete_work);
+        #endif
+        #ifdef CONFIG_ECRNX_TASKLET
+        tasklet_schedule(&pipe->rx_tasklet);
+        #endif
+    }
+    else
+    {
+        mod_timer(&usb_rx_timer, jiffies + usecs_to_jiffies(USB_RX_TIMER_TIMEOUT_US));
+    }
+#endif
+
+       //ECRNX_DBG(" entry %s, len: %d\n", __func__, urb->actual_length);      
+       //print_hex_dump(KERN_INFO, "READ:", DUMP_PREFIX_ADDRESS, 32, 1, skb->data, urb->actual_length, false);
+
+       usb_refill_recv_transfer(pipe);
+       return;
+
+cleanup_recv_urb:
+       usb_cleanup_recv_urb(urb_context);
+}
+
+#ifdef CONFIG_ECRNX_TASKLET
+static void usb_tx_comp_tasklet(unsigned long data)
+{
+       struct usb_infac_pipe *pipe = (struct usb_infac_pipe *)data;
+       struct sk_buff *skb;
+       struct txdesc_api *tx_desc;
+       u32_l flag = 0;
+       ptr_addr host_id;
+
+       while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+               //ECRNX_DBG(" %s skb dequeue, skb:0x%08x, skb len: %d!!", __func__, skb, skb->len);
+
+               flag = *(u32_l *)skb->data;
+               if((u8_l)(flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)
+               {
+                       dev_kfree_skb(skb);
+                       continue;
+               }
+               tx_desc = (struct txdesc_api *)((u32_l *)skb->data + 1);
+               if (g_usb->p_eswin->data_cfm_callback && ((u8_l)(flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC) && !(tx_desc->host.flags & TXU_CNTRL_MGMT))
+               {
+                       memcpy(&host_id, tx_desc->host.packet_addr, sizeof(ptr_addr));
+                       g_usb->p_eswin->data_cfm_callback(g_usb->p_eswin->umac_priv, (void*)host_id);
+               }
+       }
+}
+#endif
+
+#ifdef CONFIG_ECRNX_WORKQUEUE
+static void usb_tx_comp_work(struct work_struct *work)
+{
+    struct usb_infac_pipe *pipe = container_of(work,
+                                               struct usb_infac_pipe,
+                                               io_complete_work);
+    struct sk_buff *skb;
+    struct txdesc_api *tx_desc;
+    u32_l flag = 0;
+    ptr_addr host_id;
+
+    while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+        //ECRNX_DBG(" %s skb dequeue, skb:0x%08x, skb len: %d!!", __func__, skb, skb->len);
+
+        flag = *(u32_l *)skb->data;
+        if((u8_l)(flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)
+        {
+            dev_kfree_skb(skb);
+            continue;
+        }
+        tx_desc = (struct txdesc_api *)((u32_l *)skb->data + 1);
+        if (g_usb->p_eswin->data_cfm_callback && ((u8_l)(flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC) && !(tx_desc->host.flags & TXU_CNTRL_MGMT))
+        {
+            memcpy(&host_id, tx_desc->host.packet_addr, sizeof(ptr_addr));
+            g_usb->p_eswin->data_cfm_callback(g_usb->p_eswin->umac_priv, (void*)host_id);
+        }
+    }
+}
+#endif
+
+#ifdef CONFIG_ECRNX_KTHREAD
+void usb_tx_comp_cb(struct sk_buff_head *queue)
+{
+    struct sk_buff *skb;
+    struct txdesc_api *tx_desc;
+    u32_l flag = 0;
+    ptr_addr host_id;
+
+    while ((skb = skb_dequeue(queue))) {
+        //ECRNX_DBG(" %s skb dequeue, skb:0x%08x, skb len: %d!!", __func__, skb, skb->len);
+
+        flag = *(u32_l *)skb->data;
+        if((u8_l)(flag & FLAG_MSG_TYPE_MASK) != TX_FLAG_TX_DESC)
+        {
+            dev_kfree_skb(skb);
+            continue;
+        }
+        tx_desc = (struct txdesc_api *)((u32_l *)skb->data + 1);
+        if (g_usb->p_eswin->data_cfm_callback && ((u8_l)(flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC) && !(tx_desc->host.flags & TXU_CNTRL_MGMT))
+        {
+            memcpy(&host_id, tx_desc->host.packet_addr, sizeof(ptr_addr));
+            g_usb->p_eswin->data_cfm_callback(g_usb->p_eswin->umac_priv, (void*)host_id);
+        }
+    }
+}
+
+void usb_rx_comp_cb(struct usb_infac_pipe *pipe)
+{
+    struct sk_buff *skb;
+    while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+        if (g_usb->p_eswin->rx_callback)
+               {
+                       g_usb->p_eswin->rx_callback(g_usb->p_eswin->umac_priv, skb, usb_pipeendpoint(pipe->usb_pipe_handle));
+               }
+        else
+        {
+            if (skb)
+                       { // free the skb
+                ECRNX_DBG("%s, skb free: 0x%x !! \n",__func__);
+                dev_kfree_skb(skb);
+            }
+
+        }
+    }
+}
+
+static int eswin_tx_comp_thread(void *data)
+{
+    struct eswin_usb * p_usb = (struct eswin_usb *)data;
+    int ret;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+    struct sched_param param = { .sched_priority = 1 };
+    param.sched_priority = 56;
+    sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+    sched_set_fifo(get_current());
+#endif
+    ECRNX_PRINT("eswin_tx_comp_thread entry\n");
+
+    while (!kthread_should_stop())
+    {
+        ret = wait_event_interruptible(p_usb->wait_tx_comp, skb_queue_len(&g_usb->infac_data.pipe_tx.io_comp_queue) != 0 ||
+                skb_queue_len(&g_usb->infac_msg.pipe_tx.io_comp_queue) != 0 || kthread_should_stop());
+        if (ret < 0)
+        {
+            ECRNX_ERR("usb tx pkg thread error!\n");
+            return 0;
+        }
+        if(kthread_should_stop())
+        {
+            continue;
+        }
+        usb_tx_comp_cb(&g_usb->infac_msg.pipe_tx.io_comp_queue);
+        usb_tx_comp_cb(&g_usb->infac_data.pipe_tx.io_comp_queue);
+    }
+    ECRNX_PRINT("tx pkg thread exit\n");
+    return 0;
+}
+
+static int eswin_rx_comp_thread(void *data)
+{
+    struct eswin_usb * p_usb = (struct eswin_usb *)data;
+    int ret;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+    struct sched_param param = { .sched_priority = 1 };
+    param.sched_priority = 56;
+    sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+    sched_set_fifo(get_current());
+#endif
+    ECRNX_PRINT("eswin_rx_comp_thread entry\n");
+
+    //ECRNX_DBG(" %s entry, dir: %d!!", __func__, pipe->dir);
+    while (!kthread_should_stop())
+    {
+        ret = wait_event_interruptible(p_usb->wait_rx_comp, skb_queue_len(&g_usb->infac_data.pipe_rx.io_comp_queue) != 0 ||
+                skb_queue_len(&g_usb->infac_msg.pipe_rx.io_comp_queue) != 0|| kthread_should_stop());
+
+        if (ret < 0)
+        {
+            ECRNX_ERR("usb tx pkg thread error!\n");
+            return 0;
+        }
+        if(kthread_should_stop())
+        {
+            continue;
+        }
+        usb_rx_comp_cb(&g_usb->infac_msg.pipe_rx);
+        usb_rx_comp_cb(&g_usb->infac_data.pipe_rx);
+    }
+    ECRNX_PRINT("rx pkg thread exit\n");
+    return 0;
+}
+#endif
+
+#ifdef CONFIG_ECRNX_TASKLET
+static void usb_rx_comp_tasklet(unsigned long data)
+{
+       struct usb_infac_pipe *pipe = (struct usb_infac_pipe *)data;
+       struct sk_buff *skb;
+
+       //ECRNX_DBG(" %s entry, dir: %d!!", __func__, pipe->dir);
+
+       while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+               if (g_usb->p_eswin->rx_callback)
+               {
+                       g_usb->p_eswin->rx_callback(g_usb->p_eswin->umac_priv, skb, usb_pipeendpoint(pipe->usb_pipe_handle));
+               }
+        else
+        {
+            if (skb)
+                       { // free the skb
+                ECRNX_DBG("%s, skb free: 0x%x !! \n",__func__);
+                dev_kfree_skb(skb);
+            }
+
+        }
+
+       }
+}
+
+#endif
+
+#ifdef CONFIG_ECRNX_WORKQUEUE
+static void usb_rx_comp_work(struct work_struct *work)
+{
+    struct usb_infac_pipe *pipe = container_of(work,
+                                struct usb_infac_pipe,
+                                io_complete_work);
+    struct sk_buff *skb;
+    //ECRNX_DBG(" %s entry, dir: %d!!", __func__, pipe->dir);
+
+    while ((skb = skb_dequeue(&pipe->io_comp_queue))) {
+        if (g_usb->p_eswin->rx_callback)
+               {
+                       g_usb->p_eswin->rx_callback(g_usb->p_eswin->umac_priv, skb, usb_pipeendpoint(pipe->usb_pipe_handle));
+               }
+        else
+        {
+            if (skb)
+                       { // free the skb
+                ECRNX_DBG("%s, skb free: 0x%x !! \n",__func__);
+                dev_kfree_skb(skb);
+            }
+
+        }
+    }
+}
+#endif
+
+static void usb_flush_pipe(struct usb_infac_pipe * pipe)
+{
+       usb_kill_anchored_urbs(&pipe->urb_submitted);
+    if (pipe->dir == USB_DIR_TX) {
+        #ifdef CONFIG_ECRNX_KTHREAD
+        if(g_usb->kthread_tx_comp)
+        {
+            kthread_stop(g_usb->kthread_tx_comp);
+            wake_up_interruptible(&g_usb->wait_tx_comp);
+            g_usb->kthread_tx_comp = NULL;
+        }
+        #endif
+        #ifdef CONFIG_ECRNX_TASKLET
+        tasklet_kill(&pipe->tx_tasklet);
+        #endif
+        #ifdef CONFIG_ECRNX_WORKQUEUE
+        cancel_work_sync(&pipe->io_complete_work);
+        #endif
+    } else {
+        #ifdef CONFIG_ECRNX_KTHREAD
+        if(g_usb->kthread_rx_comp)
+        {
+            kthread_stop(g_usb->kthread_rx_comp);
+            wake_up_interruptible(&g_usb->wait_rx_comp);
+            g_usb->kthread_rx_comp = NULL;
+        }
+        #endif
+        #ifdef CONFIG_ECRNX_TASKLET
+        tasklet_kill(&pipe->rx_tasklet);
+        #endif
+        #ifdef CONFIG_ECRNX_WORKQUEUE
+        cancel_work_sync(&pipe->io_complete_work);
+        #endif
+    }
+}
+
+static void usb_flush_all(struct eswin_usb * p_usb)
+{
+       usb_flush_pipe(&p_usb->infac_data.pipe_rx);
+       usb_flush_pipe(&p_usb->infac_data.pipe_tx);
+       usb_flush_pipe(&p_usb->infac_msg.pipe_rx);
+       usb_flush_pipe(&p_usb->infac_msg.pipe_tx);
+}
+
+
+static void usb_refill_recv_transfer(struct usb_infac_pipe * pipe)
+{
+       struct usb_urb_context *urb_context;
+       struct urb *urb;
+       int usb_status;
+
+       for ( ;; )  {
+               urb_context = usb_alloc_urb_from_pipe(pipe);
+               if (!urb_context)
+                       break;
+               
+               urb_context->skb = dev_alloc_skb(USB_RX_MAX_BUF_SIZE);
+               if (!urb_context->skb)
+                       goto err;
+
+               urb = usb_alloc_urb(0, GFP_ATOMIC);
+               if (!urb)
+                       goto err;
+        urb_context->urb = urb;
+
+               usb_fill_bulk_urb(urb,
+                                 pipe->infac->udev,
+                                 pipe->usb_pipe_handle,
+                                 urb_context->skb->data,
+                                 USB_RX_MAX_BUF_SIZE,
+                                 usb_recv_complete, urb_context);
+
+               usb_anchor_urb(urb, &pipe->urb_submitted);
+               usb_status = usb_submit_urb(urb, GFP_ATOMIC);
+
+               if (usb_status) {
+                       ECRNX_PRINT("usb_refill_recv_transfer, usb bulk recv failed: %d\n", 
+                               usb_status);
+                       //usb_unanchor_urb(urb);
+                       //usb_free_urb(urb);
+                       goto err;
+               }
+               //usb_free_urb(urb);
+       }
+               
+       return;
+
+err:
+       usb_cleanup_recv_urb(urb_context);
+}
+
+
+
+static int usb_hif_start(struct eswin *tr)
+{
+       struct eswin_usb * p_usb =  (struct eswin_usb *)tr->drv_priv;
+
+       ECRNX_DBG("%s entry!!", __func__);
+
+       usb_refill_recv_transfer(&p_usb->infac_data.pipe_rx);
+       usb_refill_recv_transfer(&p_usb->infac_msg.pipe_rx);
+       timer_setup(&usb_rx_timer, usb_rx_timer_handle, 0);
+
+       return 0;
+}
+
+
+int usb_hif_xmit(struct eswin *tr, struct sk_buff *skb)
+{
+
+    unsigned int req_type = TX_FLAG_MAX;
+       struct eswin_usb * p_usb =  (struct eswin_usb *)tr->drv_priv;
+       struct usb_infac_data_t * infac_data = NULL;
+       struct usb_infac_pipe * pipe = NULL;
+       struct usb_urb_context *urb_context;
+       struct urb *urb;
+    int ret = -1;
+
+    req_type = *((unsigned int*)(skb->data));
+    if((req_type & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+    {
+        infac_data = &p_usb->infac_data;
+        pipe = &infac_data->pipe_tx;
+    }
+    else
+    {
+        infac_data = &p_usb->infac_msg;
+        pipe = &infac_data->pipe_tx;
+    }
+
+       urb_context = usb_alloc_urb_from_pipe(pipe);
+       if (!urb_context) {
+               ret = -ENOMEM;
+               goto err;
+       }
+       
+       urb_context->skb = skb;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb) {
+               ret = -ENOMEM;
+               goto err_free_urb_to_pipe;
+       }
+
+    urb_context->urb = urb;
+       usb_fill_bulk_urb(urb,
+                         infac_data->udev,
+                         pipe->usb_pipe_handle,
+                         skb->data,
+                         skb->len,
+                         usb_transmit_complete, urb_context);
+
+       if (!(skb->len % infac_data->max_packet_size)) {
+               /* hit a max packet boundary on this pipe */
+               urb->transfer_flags |= URB_ZERO_PACKET;
+       }
+
+       usb_anchor_urb(urb, &pipe->urb_submitted);
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret) {
+               ECRNX_PRINT("usb_hif_xmit, usb bulk transmit failed: %d\n", ret);
+               //usb_unanchor_urb(urb);
+               ret = -EINVAL;
+               goto err_free_urb_to_pipe;
+       }
+
+       //usb_free_urb(urb);
+
+       return 0;
+
+err_free_urb_to_pipe:
+       usb_free_urb_to_infac(urb_context->pipe, urb_context);
+err:
+       ECRNX_PRINT("usb_hif_xmit, pkg miss due to urb.\n");
+       skb_queue_tail(&pipe->io_comp_queue, skb);
+       #ifdef CONFIG_ECRNX_KTHREAD
+       wake_up_interruptible(&g_usb->wait_tx_comp);
+       #endif
+       #ifdef CONFIG_ECRNX_WORKQUEUE
+       schedule_work(&pipe->io_complete_work);
+       #endif
+       #ifdef CONFIG_ECRNX_TASKLET
+       tasklet_schedule(&pipe->tx_tasklet);
+       #endif
+       return ret;
+}
+
+static int usb_hif_write_raw(struct eswin *tr, const void* data, const u32 len)
+{
+       struct eswin_usb * p_usb =  (struct eswin_usb *)tr->drv_priv;
+       struct usb_infac_data_t * infac_data = &p_usb->infac_data;
+       struct usb_infac_pipe * pipe = &infac_data->pipe_tx;
+
+       return usb_bulk_msg(infac_data->udev, pipe->usb_pipe_handle, (void*)data, len, NULL, 20000);
+}
+
+static int usb_hif_wait_ack(struct eswin *tr, void* data, const u32 len)
+{
+       struct eswin_usb * p_usb =  (struct eswin_usb *)tr->drv_priv;
+       struct usb_infac_data_t * infac_data = &p_usb->infac_data;
+       struct usb_infac_pipe * pipe = &infac_data->pipe_rx;
+
+       return usb_bulk_msg(infac_data->udev, pipe->usb_pipe_handle, data, len, NULL, 20000);
+}
+
+#ifdef CONFIG_PM
+static int usb_hif_suspend(struct eswin *tr)
+{
+       return -EOPNOTSUPP;
+}
+
+static int usb_hif_resume(struct eswin *tr)
+{
+       return -EOPNOTSUPP;
+}
+#endif
+
+static struct usb_ops eswin_usb_hif_ops = {
+       .xmit                   = usb_hif_xmit,
+       .start                  = usb_hif_start,
+       .write                  = usb_hif_write_raw,
+       .wait_ack                       = usb_hif_wait_ack,
+       .suspend                = usb_hif_suspend,
+       .resume         = usb_hif_resume,
+};
+
+static int usb_create_pipe(struct usb_infac_pipe * pipe, int dir, bool flag)
+{
+       int i;
+       struct usb_urb_context *urb_context;
+       ECRNX_DBG("%s entry, dir: %d!!", __func__, dir);
+
+       pipe->dir = dir;
+    pipe->err_count = 0;
+       init_usb_anchor(&pipe->urb_submitted);
+       INIT_LIST_HEAD(&pipe->urb_list_head);
+
+       for (i = 0; i < (flag ? USB_MSG_URB_NUM : USB_DATA_URB_NUM); i++) {
+               urb_context = kzalloc(sizeof(*urb_context), GFP_KERNEL);
+               if (!urb_context)
+                       return -ENOMEM;
+
+               urb_context->pipe = pipe;
+               //pipe->urb_cnt++;
+               
+               usb_free_urb_to_infac(pipe, urb_context);
+       }
+
+       if (dir) {
+               #ifdef CONFIG_ECRNX_WORKQUEUE
+               INIT_WORK(&pipe->io_complete_work,  usb_tx_comp_work);
+               #endif
+               #ifdef CONFIG_ECRNX_TASKLET
+               tasklet_init(&pipe->tx_tasklet, usb_tx_comp_tasklet, (unsigned long) pipe);
+               #endif
+       } else {
+               #ifdef CONFIG_ECRNX_WORKQUEUE
+               INIT_WORK(&pipe->io_complete_work,  usb_rx_comp_work);
+               #endif
+               #ifdef CONFIG_ECRNX_TASKLET
+               tasklet_init(&pipe->rx_tasklet, usb_rx_comp_tasklet, (unsigned long) pipe);
+               #endif
+       }
+
+       skb_queue_head_init(&pipe->io_comp_queue);
+       return 0;
+}
+
+static int usb_create_infac(struct usb_interface *interface, struct usb_infac_data_t  * p_infac, bool flag)
+{
+       struct usb_device *dev = interface_to_usbdev(interface);
+       struct usb_host_interface *iface_desc = interface->cur_altsetting;
+       struct usb_endpoint_descriptor *endpoint = NULL;
+
+       int i;
+       ECRNX_DBG("%s entry %d!!", __func__, iface_desc->desc.bNumEndpoints);
+
+       usb_get_dev(dev);
+       usb_set_intfdata(interface, p_infac);
+
+       p_infac->udev = dev;
+       p_infac->interface = interface;
+
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+               if (endpoint->bEndpointAddress & USB_DIR_MASK) {
+                       p_infac->pipe_rx.usb_pipe_handle = usb_rcvbulkpipe(dev, endpoint->bEndpointAddress);
+                       p_infac->pipe_rx.infac = p_infac;
+                       usb_create_pipe(&p_infac->pipe_rx, USB_DIR_RX, flag);
+               } else {
+                       p_infac->pipe_tx.usb_pipe_handle = usb_sndbulkpipe(dev, endpoint->bEndpointAddress);
+                       p_infac->pipe_tx.infac = p_infac;
+                       usb_create_pipe(&p_infac->pipe_tx, USB_DIR_TX, flag);
+               }
+       }
+       p_infac->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize);;
+
+       return 0;
+}
+
+extern bool usb_status;
+extern struct eswin *pEswin;
+static int eswin_usb_probe(struct usb_interface *interface,
+                           const struct usb_device_id *id)
+{
+       int ret;
+       struct eswin_usb * p_usb;
+       struct eswin* p_eswin;
+       struct usb_host_interface *iface_desc = interface->cur_altsetting;
+    struct usb_device *dev = interface_to_usbdev(interface);
+
+     if((usb_status == false) && dl_fw)
+     {
+        ECRNX_PRINT("%s entry, reset slave !!", __func__);
+        usb_control_msg(dev,
+            usb_sndctrlpipe(dev, 0),
+            0x2,
+            USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+            0,
+            0,
+            NULL, 0,10);
+        msleep(200);
+     }
+
+       ECRNX_PRINT("%s entry, func: %d, g_usb: %p !!", __func__, iface_desc->desc.bInterfaceNumber, g_usb);
+
+       if (iface_desc->desc.bInterfaceNumber == USB_INFAC_DATA) {
+               if (g_usb) {
+                       p_usb = g_usb;
+            pEswin->dev = &interface->dev;
+               } else {
+                       p_eswin = eswin_core_create(sizeof(struct eswin_usb), &interface->dev, &eswin_usb_hif_ops);
+                       if(!p_eswin) {
+                               dev_err(&interface->dev, "failed to allocate core\n");
+                               return -ENOMEM;
+                       }
+
+                       p_usb = (struct eswin_usb *)p_eswin->drv_priv;
+                       p_usb->p_eswin = p_eswin;
+                       g_usb = p_usb;
+                       spin_lock_init(&p_usb->cs_lock);
+               }
+
+               p_usb->dev = &interface->dev;
+
+               usb_create_infac(interface, &p_usb->infac_data, 0);
+       } else {
+               usb_create_infac(interface, &g_usb->infac_msg, 1);
+               //usb_hif_start(g_usb->p_eswin);
+
+               if (!g_usb->usb_enum_already) {
+                       ret = eswin_core_register(g_usb->p_eswin);
+                       if(ret) {
+                               ECRNX_PRINT("failed to register core\n");
+                               return ret;
+                       }
+               } else {
+                       g_usb->usb_enum_already = 1;
+               }
+               return 0;
+       }
+
+       #ifdef CONFIG_ECRNX_KTHREAD
+       if(iface_desc->desc.bInterfaceNumber == USB_INFAC_DATA)
+       {
+               init_waitqueue_head(&g_usb->wait_tx_comp);
+               init_waitqueue_head(&g_usb->wait_rx_comp);
+
+               g_usb->kthread_tx_comp = kthread_run(eswin_tx_comp_thread, g_usb, "tx_comp");
+               g_usb->kthread_rx_comp = kthread_run(eswin_rx_comp_thread, g_usb, "rx_comp");
+
+               if (IS_ERR(g_usb->kthread_tx_comp)) {
+                       g_usb->kthread_tx_comp = NULL;
+                       ECRNX_PRINT("kthread_tx_comp run fail\n");
+               }
+
+               if (IS_ERR(g_usb->kthread_rx_comp)) {
+                       g_usb->kthread_rx_comp = NULL;
+                       ECRNX_PRINT("kthread_rx_comp run fail\n");
+               }
+               ECRNX_PRINT("%s kthread_run success.", __func__);
+       }
+       #endif
+       ECRNX_PRINT("%s exit!!", __func__);
+       return 0;
+}
+
+static void eswin_usb_remove(struct usb_interface *interface)
+{
+       struct usb_infac_data_t  * p_infac;
+       struct usb_host_interface *iface_desc = interface->cur_altsetting;
+
+       ECRNX_PRINT("%s entry!!", __func__);
+
+       p_infac = usb_get_intfdata(interface);
+
+       if (!p_infac) {
+               ECRNX_PRINT("%s, p_infa is null!!", __func__);
+               return;
+       }
+
+       if (iface_desc->desc.bInterfaceNumber == USB_INFAC_DATA) {
+               eswin_core_unregister(g_usb->p_eswin);
+       } 
+
+       del_timer(&usb_rx_timer);
+
+       usb_flush_pipe(&p_infac->pipe_rx);
+       usb_flush_pipe(&p_infac->pipe_tx);
+       usb_free_pipe_resources(&p_infac->pipe_rx);
+       usb_free_pipe_resources(&p_infac->pipe_tx);
+
+       usb_set_intfdata(interface, NULL);
+       usb_put_dev(interface_to_usbdev(interface));
+       dl_fw = true;
+       offset = 0;
+}
+
+
+#ifdef CONFIG_PM
+static int eswin_usb_pm_suspend(struct usb_interface *interface,       pm_message_t message)
+{
+       ECRNX_PRINT("entry %s\n", __func__);
+       usb_flush_all(g_usb);
+       return 0;
+}
+
+static int eswin_usb_pm_resume(struct usb_interface *interface)
+{
+       ECRNX_PRINT("entry %s\n", __func__);
+       usb_refill_recv_transfer(&g_usb->infac_data.pipe_rx);
+       usb_refill_recv_transfer(&g_usb->infac_msg.pipe_rx);
+       return 0;
+}
+
+#else
+#define eswin_usb_pm_suspend NULL
+#define eswin_usb_pm_resume NULL
+#endif
+
+/* table of devices that work with this driver */
+static struct usb_device_id eswin_usb_ids[] = {
+       {USB_DEVICE(0x3452, 0x6600)},
+       { /* Terminating entry */ },
+};
+
+MODULE_DEVICE_TABLE(usb, eswin_usb_ids);
+static struct usb_driver eswin_usb_driver = {
+       .name = "eswin_usb",
+       .probe = eswin_usb_probe,
+       .suspend = eswin_usb_pm_suspend,
+       .resume = eswin_usb_pm_resume,
+       .disconnect = eswin_usb_remove,
+       .id_table = eswin_usb_ids,
+       .supports_autosuspend = true,
+};
+
+static int __init eswin_usb_init(void)
+{
+       int ret;
+
+       #ifdef CONFIG_POWERKEY_GPIO
+       int pk_gpio = 4;
+       pk_gpio = CONFIG_POWERKEY_GPIO;
+       gpio_direction_output(pk_gpio,0);
+       msleep(1000);
+       gpio_direction_output(pk_gpio,1);
+       #endif
+
+       ECRNX_PRINT("%s entry !!\n", __func__);
+       ret = usb_register(&eswin_usb_driver);
+       if (ret)
+               ECRNX_PRINT("sdio driver registration failed: %d\n", ret);
+
+       ECRNX_PRINT("%s exit !!\n", __func__);
+       return ret;
+}
+
+static void __exit eswin_usb_exit(void)
+{
+       ECRNX_PRINT("%s entry !!\n", __func__);
+       usb_deregister(&eswin_usb_driver);
+       ECRNX_PRINT("%s exit !!\n", __func__);
+}
+
+void ecrnx_usb_reset_sync_fw(void)
+{
+    usb_control_msg(g_usb->infac_msg.udev,
+        usb_sndctrlpipe(g_usb->infac_msg.udev, 0),
+        0x2,
+        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+        0,
+        0,
+        NULL, 0,10);
+}
+
+int ecrnx_usb_register_drv(void)
+{
+    return eswin_usb_init();
+}
+
+void ecrnx_usb_unregister_drv(void)
+{
+    eswin_core_unregister(g_usb->p_eswin);
+    ecrnx_usb_reset_sync_fw();
+    return eswin_usb_exit();
+}
+
+struct device *eswin_usb_get_dev(void *plat)
+{
+    struct eswin* tr = (struct eswin*)plat;
+    
+    return tr->dev;
+}
+
diff --git a/drivers/net/wireless/eswin/usb/usb.h b/drivers/net/wireless/eswin/usb/usb.h
new file mode 100644 (file)
index 0000000..27e9592
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ ******************************************************************************
+ *
+ * @file usb.h
+ *
+ * @brief usb driver definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef __USB_H
+#define __USB_H
+
+#include "ecrnx_defs.h"
+#include <linux/usb.h>
+
+#define USB_INFAC_DATA         0
+#define USB_INFAC_MSG          1
+
+#define USB_DIR_MASK   0x80
+#define USB_NUM_MASK    0x7F
+
+#define USB_DATA_URB_NUM 64
+#define USB_MSG_URB_NUM        16
+
+#define USB_DIR_RX             0
+#define USB_DIR_TX             1
+
+#define USB_RX_MAX_BUF_SIZE    4096
+
+struct usb_infac_pipe {
+       int dir;
+       u32 urb_cnt;
+       struct usb_infac_data_t *infac;
+
+       struct usb_anchor urb_submitted;
+       unsigned int usb_pipe_handle;
+       struct list_head urb_list_head;
+       #ifdef CONFIG_ECRNX_WORKQUEUE
+       struct work_struct io_complete_work;
+       #endif
+       #ifdef CONFIG_ECRNX_TASKLET
+       struct tasklet_struct tx_tasklet;
+       struct tasklet_struct rx_tasklet;
+       #endif
+       struct sk_buff_head io_comp_queue;
+    unsigned int err_count;
+    int err_status;
+};
+
+
+struct usb_infac_data_t {
+       struct usb_interface *interface;
+       struct usb_device *udev;
+       u16 max_packet_size;
+
+       struct usb_infac_pipe pipe_rx;
+       struct usb_infac_pipe pipe_tx;
+};
+
+struct eswin_usb {
+       struct eswin * p_eswin;
+       struct device * dev;
+       struct usb_infac_data_t infac_data;
+       struct usb_infac_data_t infac_msg;
+       #ifdef CONFIG_ECRNX_KTHREAD
+       struct task_struct *kthread_tx_comp;
+       struct task_struct *kthread_rx_comp;
+
+       wait_queue_head_t wait_tx_comp;
+       wait_queue_head_t wait_rx_comp;
+       #endif
+       spinlock_t cs_lock;
+       u8 usb_enum_already;
+};
+
+struct usb_urb_context {
+       struct list_head link;
+       struct sk_buff *skb;
+    struct urb *urb;
+       struct usb_infac_pipe * pipe;
+};
+
+
+struct usb_ops {
+       int (*start)(struct eswin *tr);
+       int (*xmit)(struct eswin *tr, struct sk_buff *skb);
+       int (*suspend)(struct eswin *tr);
+       int (*resume)(struct eswin *tr);
+       int (*write)(struct eswin *tr, const void* data, const u32 len);
+       int (*wait_ack)(struct eswin *tr, void* data, const u32 len);
+};
+
+
+
+extern int ecrnx_usb_register_drv(void);
+extern void ecrnx_usb_unregister_drv(void);
+
+#endif /* __SDIO_H */
diff --git a/drivers/net/wireless/eswin/usb/usb_host_interface.h b/drivers/net/wireless/eswin/usb/usb_host_interface.h
new file mode 100644 (file)
index 0000000..195753e
--- /dev/null
@@ -0,0 +1,29 @@
+/**
+ ******************************************************************************
+ *
+ * @file usb_host_interface.h
+ *
+ * @brief usb host interface definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#ifndef _USB_HOST_INTERFACE_H
+#define _USB_HOST_INTERFACE_H
+/*******************************************************************************
+ * Function: usb_host_send
+ * Description:send buff from host to slave
+ * Parameters: 
+ *   Input: void *buff, int len, int flag
+ *
+ *   Output:
+ *
+ * Returns: 0
+ *
+ *
+ * Others: 
+ ********************************************************************************/
+int usb_host_send(void *buff, int len, int flag);
+#endif
diff --git a/drivers/net/wireless/eswin/wifi_ecr6600u.cfg b/drivers/net/wireless/eswin/wifi_ecr6600u.cfg
new file mode 100644 (file)
index 0000000..df5912e
--- /dev/null
@@ -0,0 +1,3 @@
+DRIVER_LOG_LEVEL=3
+FW_LOG_LEVEL=2
+FW_LOG_TYPE=0